@btndprojects/loam-ui-native 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (365) hide show
  1. package/dist/accordion/index.js +4 -2
  2. package/dist/adaptive/index.js +12 -2
  3. package/dist/address-block/index.js +23 -0
  4. package/dist/alert/index.js +5 -2
  5. package/dist/analyzer/index.js +13 -5
  6. package/dist/announcement/index.js +15 -5
  7. package/dist/appearance-picker/index.js +15 -0
  8. package/dist/audio-player/index.js +26 -2
  9. package/dist/avatar/index.js +2 -2
  10. package/dist/avatar-group/index.js +5 -3
  11. package/dist/badge/index.js +6 -3
  12. package/dist/banner/index.js +3 -2
  13. package/dist/board/index.js +13 -6
  14. package/dist/button/index.js +6 -3
  15. package/dist/camera-permission/index.js +19 -11
  16. package/dist/capture-result/index.js +26 -16
  17. package/dist/card/index.js +7 -3
  18. package/dist/card-form/index.js +15 -9
  19. package/dist/carousel/index.js +3 -1
  20. package/dist/chart/index.js +15 -5
  21. package/dist/charts/index.js +18 -5
  22. package/dist/chat/index.js +35 -10
  23. package/dist/checkbox/index.js +6 -4
  24. package/dist/checklist/index.js +16 -7
  25. package/dist/checkout/index.js +25 -12
  26. package/dist/chip/index.js +7 -4
  27. package/dist/chunk-2BZD3NAP.js +1110 -0
  28. package/dist/chunk-2YKVID2V.js +35 -0
  29. package/dist/{chunk-H7DWSL4O.js → chunk-36DA3IPO.js} +115 -27
  30. package/dist/chunk-3A56MIAN.js +599 -0
  31. package/dist/{chunk-AEB7KCHQ.js → chunk-3SCKCWF5.js} +39 -28
  32. package/dist/chunk-3TZNDHKO.js +187 -0
  33. package/dist/chunk-43K3BLXV.js +9 -0
  34. package/dist/chunk-46ZJIQ2Z.js +208 -0
  35. package/dist/{chunk-PMMTGBBS.js → chunk-4CZMQDQL.js} +75 -22
  36. package/dist/chunk-4QCR3K2J.js +1 -0
  37. package/dist/{chunk-E2E3I5W2.js → chunk-5DTKORLI.js} +54 -45
  38. package/dist/{chunk-5VFZRGVD.js → chunk-5J4WFXFA.js} +32 -15
  39. package/dist/{chunk-JBMLWFSJ.js → chunk-5KJ7TEQ6.js} +83 -48
  40. package/dist/chunk-5NU33TXK.js +529 -0
  41. package/dist/chunk-6KSUS7DF.js +690 -0
  42. package/dist/{chunk-3WEZCGHX.js → chunk-6RV6UW2J.js} +140 -56
  43. package/dist/chunk-6RZM7RP2.js +520 -0
  44. package/dist/{chunk-JJLPBRVZ.js → chunk-75BDCC7K.js} +2 -0
  45. package/dist/{chunk-U5NLHYV6.js → chunk-7CGJBQUA.js} +333 -22
  46. package/dist/{chunk-I4FHVKEC.js → chunk-7IVF6JLJ.js} +16 -5
  47. package/dist/chunk-7IWCGXYA.js +143 -0
  48. package/dist/chunk-7LM4ASFH.js +172 -0
  49. package/dist/chunk-7MCEJFWV.js +341 -0
  50. package/dist/{chunk-2SY63FCH.js → chunk-7NGMUVNU.js} +66 -30
  51. package/dist/chunk-7PBSIXT7.js +355 -0
  52. package/dist/chunk-7QOYGENL.js +443 -0
  53. package/dist/{chunk-TDMG26JI.js → chunk-A7SWYDE5.js} +40 -9
  54. package/dist/chunk-ADHPJALR.js +3051 -0
  55. package/dist/{chunk-ZLTO5VQI.js → chunk-ALJYZLNQ.js} +131 -61
  56. package/dist/{chunk-GDKVKNR2.js → chunk-AQ5FIG74.js} +110 -30
  57. package/dist/chunk-B2MMZZQB.js +426 -0
  58. package/dist/{chunk-QYPE5JX3.js → chunk-B7JZ3N5G.js} +142 -105
  59. package/dist/{chunk-QHTVYA65.js → chunk-BL37SW6G.js} +388 -102
  60. package/dist/chunk-BTRMA7GC.js +21 -0
  61. package/dist/{chunk-FUMHAAF3.js → chunk-C7IUWIUZ.js} +243 -92
  62. package/dist/chunk-CA3DUVYD.js +10 -0
  63. package/dist/{chunk-FYWNRX4F.js → chunk-CBCJSO2B.js} +143 -13
  64. package/dist/chunk-CHPX4MFI.js +239 -0
  65. package/dist/chunk-CLGAMIOT.js +648 -0
  66. package/dist/chunk-CUE7VML6.js +1141 -0
  67. package/dist/{chunk-D5N5QWI7.js → chunk-CVU6S2E5.js} +497 -235
  68. package/dist/{chunk-U23FYWME.js → chunk-CXPQMA6Z.js} +101 -34
  69. package/dist/{chunk-36EC2T6P.js → chunk-D2LN4I7I.js} +1 -1
  70. package/dist/{chunk-TLCIR6RG.js → chunk-D34LNBO5.js} +411 -155
  71. package/dist/{chunk-FV6HMP7W.js → chunk-D4BCQICM.js} +207 -65
  72. package/dist/chunk-EABKIIA4.js +1 -0
  73. package/dist/chunk-EICSYRAG.js +341 -0
  74. package/dist/chunk-EMWAJMAS.js +83 -0
  75. package/dist/chunk-EOLZWRV6.js +149 -0
  76. package/dist/chunk-EOQVTGB6.js +350 -0
  77. package/dist/{chunk-ZVATDZOM.js → chunk-ESGUHNB2.js} +106 -28
  78. package/dist/{chunk-46MMSDGL.js → chunk-F2KNBPKY.js} +104 -73
  79. package/dist/{chunk-KQZ3GLHR.js → chunk-G7H3NX3G.js} +40 -17
  80. package/dist/{chunk-3QMEOFW5.js → chunk-GM7YIPB7.js} +421 -207
  81. package/dist/{chunk-QHAITQIF.js → chunk-GVQQG435.js} +256 -86
  82. package/dist/chunk-HE43OMEL.js +1056 -0
  83. package/dist/{chunk-XIDTBCBA.js → chunk-HHOMFQ7R.js} +1049 -207
  84. package/dist/chunk-HNAKS4Z3.js +207 -0
  85. package/dist/chunk-HP6ST2OW.js +352 -0
  86. package/dist/chunk-HW3OWDTU.js +1 -0
  87. package/dist/{chunk-QP3KMOVF.js → chunk-HZDB44K4.js} +24 -14
  88. package/dist/chunk-I6CUP2LW.js +1155 -0
  89. package/dist/chunk-ICV4Y2WY.js +27 -0
  90. package/dist/chunk-IHSIENOE.js +3950 -0
  91. package/dist/{chunk-T5LTPKX6.js → chunk-IMMALLRU.js} +53 -25
  92. package/dist/{chunk-C4GTDLRC.js → chunk-JA6TK7H2.js} +154 -67
  93. package/dist/{chunk-EVOIGUBI.js → chunk-JAVZ5JPV.js} +43 -13
  94. package/dist/{chunk-CPTOADB2.js → chunk-JCMTP3E4.js} +527 -201
  95. package/dist/{chunk-OJQUBQWA.js → chunk-JCXFFK64.js} +73 -16
  96. package/dist/{chunk-V2OEB75Z.js → chunk-JWCVBFDU.js} +75 -35
  97. package/dist/chunk-JXRTD5RI.js +414 -0
  98. package/dist/chunk-JYIUZUIQ.js +1567 -0
  99. package/dist/chunk-K3TK3GIV.js +219 -0
  100. package/dist/chunk-KEVZIHM2.js +1511 -0
  101. package/dist/{chunk-VAOAKWUK.js → chunk-KHZDNQQD.js} +42 -8
  102. package/dist/chunk-KL4GDV4V.js +131 -0
  103. package/dist/{chunk-Q6NKEPRE.js → chunk-KNILMM4H.js} +73 -28
  104. package/dist/chunk-KRBUZV45.js +25 -0
  105. package/dist/{chunk-7BUIKDD6.js → chunk-KXLDK6HB.js} +91 -30
  106. package/dist/chunk-KZ3X7TSL.js +196 -0
  107. package/dist/{chunk-5UTRIXQ6.js → chunk-L2WMOA2V.js} +187 -43
  108. package/dist/chunk-L4D5LHCH.js +109 -0
  109. package/dist/{chunk-W63AJTM7.js → chunk-LAUJPLDB.js} +236 -71
  110. package/dist/{chunk-TN2ZNJUI.js → chunk-LGNTP5XA.js} +47 -13
  111. package/dist/chunk-LUZFCCSD.js +271 -0
  112. package/dist/{chunk-XTORZWRF.js → chunk-LXJCP4PL.js} +111 -30
  113. package/dist/chunk-MCOZJWPU.js +731 -0
  114. package/dist/{chunk-SATMR6SG.js → chunk-MG4SKWLL.js} +780 -142
  115. package/dist/{chunk-JUYWSSKW.js → chunk-MPEPTSER.js} +95 -16
  116. package/dist/chunk-MWPJ4YGP.js +1 -0
  117. package/dist/{chunk-LUMVGO2K.js → chunk-N7AX4OA4.js} +80 -29
  118. package/dist/chunk-NHSLM6SE.js +523 -0
  119. package/dist/{chunk-IT6FUXXG.js → chunk-NNJLVA7M.js} +43 -11
  120. package/dist/{chunk-TQVAIKFD.js → chunk-NZG75DGA.js} +102 -62
  121. package/dist/chunk-O76QXRBV.js +431 -0
  122. package/dist/chunk-OANH27AK.js +223 -0
  123. package/dist/chunk-OCHPK5YV.js +609 -0
  124. package/dist/chunk-OMHVEVX4.js +436 -0
  125. package/dist/{chunk-SWS5JQIF.js → chunk-ONKQUJWD.js} +187 -60
  126. package/dist/chunk-OVTK7A6T.js +615 -0
  127. package/dist/{chunk-UXGIW7OK.js → chunk-PCEFCOLN.js} +5 -1
  128. package/dist/{chunk-QIKT7LIB.js → chunk-PFLNFIVB.js} +91 -23
  129. package/dist/chunk-PHSZY37H.js +300 -0
  130. package/dist/chunk-PJLTKDVY.js +18 -0
  131. package/dist/{chunk-SHHY5OMK.js → chunk-PK7BN3MI.js} +83 -15
  132. package/dist/{chunk-YXOAPXMU.js → chunk-PWFRDRGS.js} +101 -14
  133. package/dist/{chunk-TY4MTKQJ.js → chunk-PYPMD46W.js} +332 -66
  134. package/dist/chunk-Q6CCHDI7.js +331 -0
  135. package/dist/chunk-QFA5WS2A.js +750 -0
  136. package/dist/chunk-QJMMM4RC.js +124 -0
  137. package/dist/chunk-QYWEVAM6.js +407 -0
  138. package/dist/chunk-R5T4VZ7Z.js +126 -0
  139. package/dist/chunk-R77HFLZD.js +1 -0
  140. package/dist/{chunk-LMPHCVJJ.js → chunk-RAESDNU5.js} +162 -68
  141. package/dist/chunk-RBYQUXNL.js +1 -0
  142. package/dist/chunk-RCM3DT5E.js +1 -0
  143. package/dist/chunk-RGNY56L7.js +282 -0
  144. package/dist/chunk-RIK6UX2K.js +344 -0
  145. package/dist/{chunk-5LAAAXFP.js → chunk-RJA3RXO3.js} +63 -54
  146. package/dist/chunk-RJYRJ3LD.js +1 -0
  147. package/dist/chunk-RMA3NOY4.js +599 -0
  148. package/dist/{chunk-VDPK5CY4.js → chunk-RSWKKAHK.js} +143 -61
  149. package/dist/{chunk-B7N4ASYE.js → chunk-RVOIIX3V.js} +17 -45
  150. package/dist/chunk-SA2RRBGT.js +1077 -0
  151. package/dist/{chunk-7OVVSVEZ.js → chunk-SG3J4R45.js} +440 -169
  152. package/dist/chunk-SPUK7JD4.js +238 -0
  153. package/dist/chunk-SWXV2K5N.js +434 -0
  154. package/dist/chunk-SYJEPXP6.js +1303 -0
  155. package/dist/{chunk-LKFC4CS7.js → chunk-SZLI34TA.js} +18 -9
  156. package/dist/{chunk-NBEF4EPJ.js → chunk-T77WJA74.js} +131 -16
  157. package/dist/chunk-TDWWVJA4.js +630 -0
  158. package/dist/chunk-TFHI5XDR.js +610 -0
  159. package/dist/chunk-TH4PW2WU.js +17 -0
  160. package/dist/chunk-TTQ24HQX.js +12 -0
  161. package/dist/chunk-TUACVJNG.js +12 -0
  162. package/dist/{chunk-F5B3UINI.js → chunk-TUL5RUIA.js} +171 -62
  163. package/dist/{chunk-C6APNDVA.js → chunk-TYOSA7JG.js} +63 -34
  164. package/dist/chunk-TYQTMMEV.js +1 -0
  165. package/dist/{chunk-DW3L54DQ.js → chunk-TZO6YDS7.js} +53 -12
  166. package/dist/{chunk-67IZZ2MJ.js → chunk-U3ETF22F.js} +75 -30
  167. package/dist/{chunk-EDCBJV7J.js → chunk-U6BYHD2N.js} +87 -31
  168. package/dist/{chunk-ENP3BMZU.js → chunk-UF6EJXPQ.js} +16 -1
  169. package/dist/{chunk-IPUNJMAQ.js → chunk-UNNQIPGK.js} +304 -68
  170. package/dist/chunk-UQVO6AJV.js +171 -0
  171. package/dist/{chunk-FZGAAOKS.js → chunk-VFDQAAIW.js} +138 -32
  172. package/dist/chunk-VIUE3VQT.js +1 -0
  173. package/dist/{chunk-5RENVKBQ.js → chunk-VJLSSSGC.js} +126 -30
  174. package/dist/chunk-VLMT6KYT.js +350 -0
  175. package/dist/chunk-VMHGBUGS.js +35 -0
  176. package/dist/{chunk-4IZXHVFJ.js → chunk-VQKPBYW6.js} +228 -172
  177. package/dist/{chunk-G6UESEVN.js → chunk-VSSTWLC5.js} +49 -11
  178. package/dist/{chunk-TRGFFU6A.js → chunk-VZYYZRPN.js} +866 -330
  179. package/dist/{chunk-JLC6PM5S.js → chunk-WAIJCD2V.js} +128 -54
  180. package/dist/chunk-WFMQ3WKM.js +378 -0
  181. package/dist/chunk-WJ2KDRMY.js +66 -0
  182. package/dist/chunk-WKFZJO54.js +437 -0
  183. package/dist/{chunk-QEYFDRHR.js → chunk-WVVD3XOV.js} +146 -84
  184. package/dist/chunk-XAIWMSLB.js +1 -0
  185. package/dist/chunk-XBXUBBJZ.js +98 -0
  186. package/dist/chunk-XCU7MYBJ.js +1 -0
  187. package/dist/chunk-XHE4PGCI.js +717 -0
  188. package/dist/chunk-XRSJ32M3.js +331 -0
  189. package/dist/chunk-XTCVOEAW.js +1 -0
  190. package/dist/chunk-XWGXVQF6.js +58 -0
  191. package/dist/{chunk-MKSNFG7H.js → chunk-Y3XKKO5K.js} +191 -109
  192. package/dist/chunk-Y4QNMBLO.js +533 -0
  193. package/dist/chunk-Y5H4CTKO.js +1 -0
  194. package/dist/chunk-YBQIN75A.js +403 -0
  195. package/dist/{chunk-PNF3NKB5.js → chunk-YJ5PXOX3.js} +28 -11
  196. package/dist/{chunk-M656FYNH.js → chunk-YLRSRUPS.js} +63 -49
  197. package/dist/chunk-YM6MPJ4A.js +1 -0
  198. package/dist/chunk-YV7KZENV.js +200 -0
  199. package/dist/chunk-Z7I4ZSO6.js +316 -0
  200. package/dist/{chunk-EY4DD67D.js → chunk-ZIRMWD2B.js} +199 -126
  201. package/dist/chunk-ZPMG3FWU.js +75 -0
  202. package/dist/{chunk-QRUMA22E.js → chunk-ZTXMK2G7.js} +92 -43
  203. package/dist/{chunk-2OLKPG4E.js → chunk-ZVJCWXAL.js} +189 -87
  204. package/dist/{chunk-YAAHPATV.js → chunk-ZYQ23DPT.js} +49 -34
  205. package/dist/coachmark/index.js +7 -3
  206. package/dist/code-block/index.js +4 -3
  207. package/dist/color-field/index.js +12 -4
  208. package/dist/color-picker/index.js +23 -0
  209. package/dist/commerce/index.js +27 -16
  210. package/dist/confirm/index.js +31 -0
  211. package/dist/connection-card/index.js +34 -0
  212. package/dist/connectivity/index.js +3 -2
  213. package/dist/context-menu/index.js +10 -2
  214. package/dist/copy-button/index.js +3 -2
  215. package/dist/data-grid/index.js +30 -17
  216. package/dist/date-range-picker/index.js +19 -9
  217. package/dist/description-list/index.js +4 -1
  218. package/dist/device-frame/index.js +30 -0
  219. package/dist/dialog/index.js +8 -2
  220. package/dist/divider/index.js +2 -0
  221. package/dist/empty-state/index.js +13 -2
  222. package/dist/error-summary/index.js +7 -2
  223. package/dist/fab/index.js +4 -2
  224. package/dist/file-manager/index.js +33 -17
  225. package/dist/file-upload/index.js +12 -6
  226. package/dist/filter-bar/index.js +10 -6
  227. package/dist/flow-run/index.js +19 -12
  228. package/dist/form/index.js +30 -3
  229. package/dist/gallery/index.js +5 -2
  230. package/dist/hold-to-confirm/index.js +4 -2
  231. package/dist/hover-card/index.js +18 -0
  232. package/dist/icon-tile/index.js +10 -0
  233. package/dist/image/index.js +9 -0
  234. package/dist/index.d.ts +29826 -17941
  235. package/dist/index.js +197 -141
  236. package/dist/infinite-list/index.js +4 -2
  237. package/dist/inline-edit/index.js +5 -4
  238. package/dist/input/index.js +4 -3
  239. package/dist/interactions/index.js +4 -1
  240. package/dist/invoice/index.js +18 -9
  241. package/dist/job-row/index.js +22 -0
  242. package/dist/json-inspector/index.js +20 -0
  243. package/dist/kbd/index.js +2 -2
  244. package/dist/keyboard/index.js +7 -2
  245. package/dist/level-meter/index.js +9 -0
  246. package/dist/list-row/index.js +11 -3
  247. package/dist/log-viewer/index.js +9 -2
  248. package/dist/map-view/index.js +13 -2
  249. package/dist/markdown/index.js +8 -5
  250. package/dist/metric/index.js +6 -3
  251. package/dist/metric-strip/index.js +12 -0
  252. package/dist/money/index.js +11 -4
  253. package/dist/nav-drawer/index.js +6 -2
  254. package/dist/nav-header/index.js +10 -4
  255. package/dist/navigation/index.js +29 -2
  256. package/dist/notification-center/index.js +24 -10
  257. package/dist/number-roll/index.js +3 -1
  258. package/dist/onboarding-pager/index.js +22 -0
  259. package/dist/one-handed/index.js +2 -2
  260. package/dist/os-surfaces/index.js +6 -3
  261. package/dist/otp/index.js +5 -4
  262. package/dist/page-header/index.js +2 -1
  263. package/dist/pagination/index.js +4 -2
  264. package/dist/payment-method/index.js +17 -11
  265. package/dist/payment-status/index.js +22 -14
  266. package/dist/permission/index.js +18 -2
  267. package/dist/picker/index.js +17 -6
  268. package/dist/progress/index.js +7 -2
  269. package/dist/provenance/index.js +16 -0
  270. package/dist/pull-to-refresh/index.js +2 -2
  271. package/dist/qr-code/index.js +16 -0
  272. package/dist/query-builder/index.js +30 -0
  273. package/dist/radio-group/index.js +6 -4
  274. package/dist/rating/index.js +7 -2
  275. package/dist/receivables/index.js +31 -18
  276. package/dist/recording-indicator/index.js +21 -0
  277. package/dist/refund/index.js +24 -15
  278. package/dist/repeatable-rows/index.js +19 -0
  279. package/dist/save-status/index.js +5 -3
  280. package/dist/screen/index.js +3 -0
  281. package/dist/scroll-area/index.js +1 -1
  282. package/dist/search-field/index.js +8 -3
  283. package/dist/secret-field/index.js +19 -0
  284. package/dist/section-header/index.js +4 -0
  285. package/dist/segmented-control/index.js +2 -2
  286. package/dist/sheet/index.js +9 -3
  287. package/dist/shortcuts-sheet/index.js +2 -0
  288. package/dist/sign-in-screen/index.js +25 -0
  289. package/dist/skeleton/index.js +3 -1
  290. package/dist/slider/index.js +2 -1
  291. package/dist/sortable/index.js +4 -3
  292. package/dist/spinner/index.js +2 -1
  293. package/dist/split-button/index.js +17 -5
  294. package/dist/spotlight/index.js +7 -1
  295. package/dist/stack/index.js +2 -0
  296. package/dist/stage-timeline/index.js +6 -3
  297. package/dist/status-dot/index.js +6 -2
  298. package/dist/stepper/index.js +7 -3
  299. package/dist/streaming-text/index.js +2 -1
  300. package/dist/survey/index.js +24 -14
  301. package/dist/switch/index.js +6 -2
  302. package/dist/system-state/index.js +12 -4
  303. package/dist/tab-bar/index.js +10 -2
  304. package/dist/tabs/index.js +10 -2
  305. package/dist/tags-input/index.js +8 -5
  306. package/dist/text/index.js +4 -0
  307. package/dist/textarea/index.js +5 -4
  308. package/dist/thinking/index.js +4 -1
  309. package/dist/tile-rail/index.js +10 -0
  310. package/dist/time-field/index.js +15 -7
  311. package/dist/time-range-control/index.js +26 -12
  312. package/dist/timeline/index.js +2 -2
  313. package/dist/toast/index.js +4 -2
  314. package/dist/toolbar/index.js +3 -2
  315. package/dist/tour/index.js +12 -10
  316. package/dist/transcript/index.js +28 -0
  317. package/dist/truncation-notice/index.js +2 -2
  318. package/dist/update-notice/index.js +15 -8
  319. package/dist/video-player/index.js +26 -2
  320. package/dist/viewfinder/index.js +12 -4
  321. package/dist/widget/index.js +11 -4
  322. package/dist/wizard/index.js +7 -4
  323. package/package.json +13 -13
  324. package/dist/chunk-2PKYGLQ6.js +0 -268
  325. package/dist/chunk-2SWKL3AJ.js +0 -466
  326. package/dist/chunk-2TXERVVA.js +0 -1081
  327. package/dist/chunk-3DUWIYV5.js +0 -437
  328. package/dist/chunk-3NMD74ZU.js +0 -4
  329. package/dist/chunk-4DS5ZWVC.js +0 -83
  330. package/dist/chunk-6YAQBXB3.js +0 -93
  331. package/dist/chunk-74FLKD24.js +0 -237
  332. package/dist/chunk-CMTVNF3D.js +0 -104
  333. package/dist/chunk-DN7MNQIP.js +0 -799
  334. package/dist/chunk-DVGBZ3TX.js +0 -455
  335. package/dist/chunk-F3H33DQX.js +0 -300
  336. package/dist/chunk-FAJI7BSC.js +0 -443
  337. package/dist/chunk-G3AFC24V.js +0 -60
  338. package/dist/chunk-HJLPZA4X.js +0 -369
  339. package/dist/chunk-HYEOQWT7.js +0 -647
  340. package/dist/chunk-IDM7JJMO.js +0 -110
  341. package/dist/chunk-IVKMZ7BB.js +0 -326
  342. package/dist/chunk-IXKWIFWZ.js +0 -334
  343. package/dist/chunk-JV5YOTOB.js +0 -164
  344. package/dist/chunk-KFJGFLMW.js +0 -158
  345. package/dist/chunk-LQP4KEXZ.js +0 -214
  346. package/dist/chunk-NTQ2F3AU.js +0 -257
  347. package/dist/chunk-O5V4TG3V.js +0 -329
  348. package/dist/chunk-OWAMM2DK.js +0 -280
  349. package/dist/chunk-PID3NJB7.js +0 -405
  350. package/dist/chunk-QDZGFXQD.js +0 -176
  351. package/dist/chunk-QPWDDJJ4.js +0 -385
  352. package/dist/chunk-ROCUG24E.js +0 -416
  353. package/dist/chunk-RU3ZPNL6.js +0 -1986
  354. package/dist/chunk-RXSCIO6W.js +0 -153
  355. package/dist/chunk-SKWO3SZF.js +0 -2284
  356. package/dist/chunk-UQGBTPXH.js +0 -218
  357. package/dist/chunk-UYGDJWMN.js +0 -273
  358. package/dist/chunk-W74TXYFB.js +0 -107
  359. package/dist/chunk-WRDSPUE7.js +0 -417
  360. package/dist/chunk-XBZGDDMR.js +0 -105
  361. package/dist/chunk-XN4KG3P4.js +0 -1040
  362. package/dist/chunk-XQXMLMH3.js +0 -211
  363. package/dist/chunk-ZECZOOSB.js +0 -245
  364. /package/dist/{chunk-VFCUIDRT.js → chunk-3BKLBYPK.js} +0 -0
  365. /package/dist/{chunk-GEIGMWNF.js → chunk-QNZII37O.js} +0 -0
@@ -0,0 +1,3051 @@
1
+ import { List, ListRow } from './chunk-JA6TK7H2.js';
2
+ import { Amount, PriceRow } from './chunk-GVQQG435.js';
3
+ import { usePayLabels } from './chunk-QNZII37O.js';
4
+ import { Progress } from './chunk-L2WMOA2V.js';
5
+ import { Checkbox } from './chunk-KNILMM4H.js';
6
+ import { Dialog } from './chunk-AQ5FIG74.js';
7
+ import { Button } from './chunk-5DTKORLI.js';
8
+ import { resolveTextRole } from './chunk-L4D5LHCH.js';
9
+ import { Badge } from './chunk-LGNTP5XA.js';
10
+ import { Skeleton } from './chunk-YJ5PXOX3.js';
11
+ import { Switch } from './chunk-VFDQAAIW.js';
12
+ import { SegmentedControl } from './chunk-NZG75DGA.js';
13
+ import { RadioGroup, RadioGroupItem } from './chunk-PK7BN3MI.js';
14
+ import { FieldMessage } from './chunk-BTRMA7GC.js';
15
+ import { useInputPlatform, TextField } from './chunk-KXLDK6HB.js';
16
+ import { isAndroidIdiom } from './chunk-TH4PW2WU.js';
17
+ import { useLabels, useLoamLocale } from './chunk-APCWFOZW.js';
18
+ import { Icon } from './chunk-PCEFCOLN.js';
19
+ import * as React7 from 'react';
20
+ import { Platform, ScrollView, Text, View, Pressable } from 'react-native';
21
+ import { createStyleSheet, useStyles, useLoamTheme } from '@btndprojects/loam-theme-native';
22
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
23
+ import { yearlySaving, planDelta, planCta, yearlyMonthly, money } from '@btndprojects/loam-pay';
24
+
25
+ // src/commerce/commerce-labels.ts
26
+ var TOGGLE_ROW_LABELS = {
27
+ withDescription: (label, description) => `${label}, ${description}`
28
+ };
29
+ var SETTINGS_SCREEN_LABELS = {
30
+ cancel: "Cancel",
31
+ close: "Close",
32
+ save: "Save",
33
+ saving: "Saving\u2026",
34
+ selectRow: (label, value) => `${label}, ${value}`
35
+ };
36
+ var NOTIFICATION_PREFERENCES_LABELS = {
37
+ systemOn: "System notifications are on. Turn categories off here rather than muting the whole app.",
38
+ systemOff: "Notifications are turned off for this app in system settings. Your choices below are kept, and take effect as soon as you turn them back on.",
39
+ openSettings: "Open Settings"
40
+ };
41
+ var DELETE_ACCOUNT_LABELS = {
42
+ title: "Delete account",
43
+ whatIsDeleted: "What is deleted",
44
+ whatIsKept: "What is kept, and why",
45
+ deletionCompletes: (duration) => `Deletion completes ${duration}.`,
46
+ typeToConfirm: (phrase) => `Type ${phrase} to confirm`,
47
+ confirm: "Delete my account",
48
+ busy: "Deleting\u2026",
49
+ keep: "Keep my account"
50
+ };
51
+ var MANAGE_SUBSCRIPTION_LABELS = {
52
+ statusActive: "Active",
53
+ statusTrial: "Trial",
54
+ statusCanceled: "Canceled",
55
+ statusPastDue: "Past due",
56
+ renews: "renews",
57
+ renewalLine: (price, renews, date) => `${price} \xB7 ${renews} ${date}`,
58
+ payment: "Payment",
59
+ paymentLine: (payment, method) => `${payment} \xB7 ${method}`,
60
+ cancel: "Cancel subscription"
61
+ };
62
+ function DeleteAccountScreen({
63
+ title: titleProp,
64
+ deletes,
65
+ retains,
66
+ duration,
67
+ confirmationPhrase,
68
+ confirmationLabel,
69
+ confirmationValue,
70
+ onConfirmationChange,
71
+ confirmLabel: confirmLabelProp,
72
+ onConfirm,
73
+ loading = false,
74
+ busyLabel: busyLabelProp,
75
+ cancelLabel: cancelLabelProp,
76
+ onCancel,
77
+ labels: labelsProp,
78
+ platform,
79
+ density = "standard",
80
+ size,
81
+ error,
82
+ disabled = false
83
+ }) {
84
+ const os = useInputPlatform(platform);
85
+ const { styles } = useStyles(sheet, { os });
86
+ const labels = useLabels("deleteAccount", DELETE_ACCOUNT_LABELS, labelsProp);
87
+ const title = titleProp ?? labels.title;
88
+ const confirmLabel = confirmLabelProp ?? labels.confirm;
89
+ const busyLabel = busyLabelProp ?? labels.busy;
90
+ const cancelLabel = cancelLabelProp ?? labels.keep;
91
+ const confirmationName = confirmationLabel ?? labels.typeToConfirm(confirmationPhrase);
92
+ const theme = useLoamTheme("commerce");
93
+ const android = isAndroidIdiom(theme, os);
94
+ const isControlled = confirmationValue !== void 0;
95
+ const [internal, setInternal] = React7.useState("");
96
+ const typed = isControlled ? confirmationValue : internal;
97
+ const armed = typed.trim() === confirmationPhrase;
98
+ const change = (next) => {
99
+ if (!isControlled) setInternal(next);
100
+ onConfirmationChange?.(next);
101
+ };
102
+ return /* @__PURE__ */ jsxs(
103
+ ScrollView,
104
+ {
105
+ style: styles.screen,
106
+ contentContainerStyle: [
107
+ styles.scroll,
108
+ density === "compact" ? styles.scrollCompact : null,
109
+ density === "comfortable" ? styles.scrollComfortable : null
110
+ ],
111
+ children: [
112
+ /* @__PURE__ */ jsx(
113
+ Text,
114
+ {
115
+ accessibilityRole: "header",
116
+ style: [styles.title, resolveTextRole(theme, "surfaceTitle", android)],
117
+ children: title
118
+ }
119
+ ),
120
+ /* @__PURE__ */ jsxs(View, { style: styles.group, children: [
121
+ /* @__PURE__ */ jsx(Text, { style: styles.caption, accessibilityRole: "header", children: labels.whatIsDeleted }),
122
+ deletes.map((d) => /* @__PURE__ */ jsxs(View, { style: styles.row, children: [
123
+ /* @__PURE__ */ jsx(Icon, { name: "trash", size: theme.icon.sm, color: theme.color.danger }),
124
+ /* @__PURE__ */ jsx(Text, { style: styles.rowLabel, children: d })
125
+ ] }, d))
126
+ ] }),
127
+ /* @__PURE__ */ jsxs(View, { style: styles.group, children: [
128
+ /* @__PURE__ */ jsx(Text, { style: styles.caption, accessibilityRole: "header", children: labels.whatIsKept }),
129
+ retains.map((r) => /* @__PURE__ */ jsxs(View, { style: styles.row, children: [
130
+ /* @__PURE__ */ jsx(Icon, { name: "lock", size: theme.icon.sm, color: theme.color.text3 }),
131
+ /* @__PURE__ */ jsx(Text, { style: styles.rowLabel, children: r })
132
+ ] }, r))
133
+ ] }),
134
+ /* @__PURE__ */ jsx(View, { style: styles.duration, children: /* @__PURE__ */ jsx(Text, { style: styles.durationText, children: labels.deletionCompletes(duration) }) }),
135
+ /* @__PURE__ */ jsx(
136
+ TextField,
137
+ {
138
+ label: confirmationName,
139
+ value: typed,
140
+ onChangeText: change,
141
+ autoCapitalize: "characters",
142
+ autoCorrect: false,
143
+ accessibilityLabel: confirmationName,
144
+ size,
145
+ error,
146
+ editable: !disabled
147
+ }
148
+ ),
149
+ /* @__PURE__ */ jsx(
150
+ Button,
151
+ {
152
+ variant: "danger",
153
+ size: size ?? "lg",
154
+ fullWidth: true,
155
+ disabled: !armed || disabled,
156
+ loading,
157
+ accessibilityLabel: loading ? busyLabel : confirmLabel,
158
+ onPress: armed && !loading ? onConfirm : void 0,
159
+ children: loading ? busyLabel : confirmLabel
160
+ }
161
+ ),
162
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: size ?? "lg", fullWidth: true, disabled, onPress: onCancel, children: cancelLabel })
163
+ ]
164
+ }
165
+ );
166
+ }
167
+ var sheet = createStyleSheet((theme) => ({
168
+ screen: { flex: 1, backgroundColor: theme.color.bg },
169
+ scroll: { padding: theme.layout.screenPad, gap: theme.space.s4 },
170
+ scrollCompact: { gap: theme.space.s2 },
171
+ scrollComfortable: { gap: theme.space.s6 },
172
+ // F117 / Refinement 38: the surface title in the native face.
173
+ title: { fontFamily: theme.font.sans, color: theme.color.text },
174
+ group: { gap: theme.space.s2 },
175
+ // The OS section header: iOS footnote caps in `text2`, Android 14 / 500 in
176
+ // `accentStrong` (the same header SettingsScreen draws).
177
+ caption: {
178
+ fontFamily: theme.font.sans,
179
+ variants: {
180
+ os: {
181
+ ios: {
182
+ fontSize: theme.type.footnote.fontSize,
183
+ lineHeight: theme.type.footnote.lineHeight,
184
+ letterSpacing: theme.type.footnote.letterSpacing,
185
+ textTransform: "uppercase",
186
+ color: theme.color.text2
187
+ },
188
+ android: {
189
+ fontWeight: theme.fontWeight.medium,
190
+ fontSize: theme.fontSize[14],
191
+ lineHeight: theme.space[20],
192
+ color: theme.color.accentStrong
193
+ }
194
+ }
195
+ }
196
+ },
197
+ row: { flexDirection: "row", alignItems: "flex-start", gap: theme.space.s2 },
198
+ rowLabel: {
199
+ flex: 1,
200
+ fontFamily: theme.font.sans,
201
+ fontSize: theme.type.footnote.fontSize,
202
+ lineHeight: theme.type.footnote.fontSize * theme.lineHeight.normal,
203
+ color: theme.color.text
204
+ },
205
+ duration: {
206
+ padding: theme.space.s3,
207
+ borderRadius: theme.radius.md,
208
+ backgroundColor: theme.color.warnSoft,
209
+ borderWidth: theme.borderWidth.base,
210
+ borderColor: theme.edge.warn
211
+ },
212
+ // Text on a `-soft` tint takes the tone itself, never the on-ink.
213
+ durationText: {
214
+ fontFamily: theme.font.sans,
215
+ fontSize: theme.type.footnote.fontSize,
216
+ lineHeight: theme.type.footnote.lineHeight,
217
+ color: theme.color.warnOnSoft
218
+ }
219
+ }));
220
+ var DEFAULT_LABELS = {
221
+ platformNote: "Billed by the app store on your account. Prices come from its catalogue.",
222
+ restorePurchase: "Restore purchase",
223
+ close: "Close",
224
+ bestValue: "Best value",
225
+ planLine: (name, price, period) => `${name} \xB7 ${price} ${period}`,
226
+ planPrice: (price, period) => `${price} ${period}`,
227
+ planSpoken: (name, price, period) => `${name} ${price} ${period}`,
228
+ notNow: "Not now",
229
+ restorePurchases: "Restore purchases",
230
+ meterReadout: (value, max, unit) => `${value} of ${max}${unit ? ` ${unit}` : ""}`,
231
+ save: "Save",
232
+ perYear: "a year",
233
+ samePrice: "Same price",
234
+ billedYearlyTail: "billed yearly",
235
+ yourPlan: "Your plan",
236
+ included: "Included",
237
+ notIncluded: "Not included",
238
+ featureState: (state) => `${state}: `,
239
+ comparisonCell: (label, included) => `${label}: ${included ? "included" : "not included"}`,
240
+ billingPeriod: "Billing period",
241
+ plansCompared: "Plans compared",
242
+ feature: "Feature",
243
+ sortBy: (column) => `Sort by ${column}`,
244
+ selectRow: (row) => `Select ${row}`,
245
+ itemColumn: "Item",
246
+ amountColumn: "Amount",
247
+ choosePlanRow: "Choose a plan",
248
+ creditedNextInvoice: "credited to your next invoice",
249
+ today: "today",
250
+ then: "then",
251
+ cadenceFrom: (cadence, date) => `${cadence} from ${date}`,
252
+ monthly: "monthly",
253
+ midCycleChange: (currency) => `Mid-cycle change \xB7 ${currency}`,
254
+ unusedTime: (plan, daysLeft, cycleDays) => `Unused ${plan}, ${daysLeft} of ${cycleDays} days`,
255
+ sameDays: (plan, daysLeft) => `${plan} for the same ${daysLeft} days`,
256
+ creditOnNextInvoice: "Credit on your next invoice",
257
+ chargedToday: "Charged today",
258
+ fromDate: (date, cadence) => `From ${date}, ${cadence}`,
259
+ moveTo: (plan) => `Move to ${plan}`,
260
+ upgrade: "upgrade",
261
+ downgrade: "downgrade",
262
+ planArrow: (from, to) => `${from} \u2192 ${to}`,
263
+ perCycle: "per cycle",
264
+ confirmChange: (way) => `Confirm ${way}`,
265
+ keepCurrentPlan: "Keep current plan",
266
+ subscription: "Subscription",
267
+ plan: "Plan",
268
+ seats: (count) => `${count} seats`,
269
+ billedYearly: "Billed yearly",
270
+ billedMonthly: "Billed monthly",
271
+ renews: "Renews",
272
+ method: "Method",
273
+ trialDaysLeft: (days) => `${days} ${days === 1 ? "day" : "days"} left in your trial`,
274
+ trialUsed: "Trial used",
275
+ days: "days",
276
+ graceTitle: "We could not charge your card",
277
+ updatePaymentMethod: "Update payment method",
278
+ graceSchedule: (day) => `due \xB7 day ${day} of the retry schedule`,
279
+ accessEnds: (date) => `Access ends ${date}.`,
280
+ cancelPlan: (plan) => `Cancel ${plan}`,
281
+ staysActiveUntil: (plan, date) => React7.createElement(React7.Fragment, null, `${plan} stays active until `, date, "."),
282
+ whatYouLose: (date) => `What you lose on ${date}`,
283
+ whyCancelling: "Why are you cancelling?",
284
+ keepMyPlan: "Keep my plan"
285
+ };
286
+ var INDICATOR = { ring: 20, dot: 9 };
287
+ function Paywall({
288
+ title,
289
+ eyebrow,
290
+ description,
291
+ features = [],
292
+ meter,
293
+ plans,
294
+ selectedPlanId,
295
+ onSelectPlan,
296
+ ctaLabel,
297
+ onPurchase,
298
+ busy = false,
299
+ legalText,
300
+ restoreLabel: restoreLabelProp,
301
+ onRestore,
302
+ footNote,
303
+ links = [],
304
+ onLinkPress,
305
+ onClose,
306
+ closeLabel: closeLabelProp,
307
+ labels: labelsProp,
308
+ platform,
309
+ error,
310
+ tone = "good"
311
+ }) {
312
+ const os = useInputPlatform(platform);
313
+ const android = os === "android";
314
+ const { styles } = useStyles(sheet2, { os });
315
+ const labels = useLabels("paywall", DEFAULT_LABELS, labelsProp);
316
+ const restoreLabel = restoreLabelProp ?? labels.restorePurchase;
317
+ const closeLabel = closeLabelProp ?? labels.close;
318
+ const theme = useLoamTheme("commerce");
319
+ const toneInk = tone === "good" ? theme.color.good : tone === "accent" ? theme.color.accentStrong : tone === "neutral" ? theme.color.text2 : theme.color[tone];
320
+ const fallback = plans.find((p) => p.recommended)?.id ?? plans[0]?.id;
321
+ const isControlled = selectedPlanId !== void 0;
322
+ const [internal, setInternal] = React7.useState(fallback);
323
+ const selected = isControlled ? selectedPlanId : internal;
324
+ const select = (id) => {
325
+ if (!isControlled) setInternal(id);
326
+ onSelectPlan?.(id);
327
+ };
328
+ return /* @__PURE__ */ jsxs(View, { style: styles.screen, children: [
329
+ /* @__PURE__ */ jsxs(ScrollView, { contentContainerStyle: styles.scroll, children: [
330
+ onClose ? /* @__PURE__ */ jsx(View, { style: styles.closeRow, children: /* @__PURE__ */ jsx(
331
+ Pressable,
332
+ {
333
+ accessibilityRole: "button",
334
+ accessibilityLabel: closeLabel,
335
+ onPress: onClose,
336
+ style: styles.close,
337
+ children: /* @__PURE__ */ jsx(Icon, { name: "x", size: theme.icon.md, color: theme.color.text3 })
338
+ }
339
+ ) }) : null,
340
+ /* @__PURE__ */ jsxs(View, { style: styles.intro, children: [
341
+ eyebrow ? /* @__PURE__ */ jsx(
342
+ Text,
343
+ {
344
+ style: [
345
+ styles.eyebrow,
346
+ resolveTextRole(theme, "overline", android),
347
+ tone === "good" ? null : { color: toneInk }
348
+ ],
349
+ children: eyebrow
350
+ }
351
+ ) : null,
352
+ /* @__PURE__ */ jsx(
353
+ Text,
354
+ {
355
+ accessibilityRole: "header",
356
+ style: [
357
+ styles.title,
358
+ // Android's large title is headline-medium 28, not headline-large.
359
+ resolveTextRole(theme, android ? "headlineMedium" : "largeTitle", android),
360
+ android ? null : styles.titleIos
361
+ ],
362
+ children: title
363
+ }
364
+ ),
365
+ description ? /* @__PURE__ */ jsx(Text, { style: styles.description, children: description }) : null
366
+ ] }),
367
+ features.length > 0 ? /* @__PURE__ */ jsx(View, { style: styles.features, children: features.map((f) => (
368
+ // The glyph carries the same meaning as the tint, so the list still
369
+ // reads under forced colors / greyscale.
370
+ /* @__PURE__ */ jsxs(View, { style: styles.featureRow, children: [
371
+ /* @__PURE__ */ jsx(Icon, { name: "check", size: theme.icon.sm, color: toneInk }),
372
+ /* @__PURE__ */ jsx(Text, { style: styles.featureLabel, children: f })
373
+ ] }, f)
374
+ )) }) : null,
375
+ meter,
376
+ /* @__PURE__ */ jsx(View, { accessibilityRole: "radiogroup", style: styles.plans, children: plans.map((plan) => {
377
+ const on = plan.id === selected;
378
+ return /* @__PURE__ */ jsxs(
379
+ Pressable,
380
+ {
381
+ accessibilityRole: "radio",
382
+ accessibilityLabel: labels.planSpoken(plan.name, plan.price, plan.period),
383
+ accessibilityState: { checked: on, selected: on },
384
+ "aria-checked": on,
385
+ onPress: () => select(plan.id),
386
+ style: ({ pressed }) => [
387
+ styles.plan,
388
+ // M1: iOS keeps the row flat (check only); Android's chosen row
389
+ // takes the selected fill and accent edge.
390
+ on && android ? styles.planOn : null,
391
+ pressed ? styles.planPressed : null
392
+ ],
393
+ children: [
394
+ android ? /* @__PURE__ */ jsx(View, { style: [styles.indicator, on ? styles.indicatorOn : null], children: on ? /* @__PURE__ */ jsx(View, { style: styles.indicatorDot }) : null }) : null,
395
+ /* @__PURE__ */ jsxs(View, { style: styles.planMain, children: [
396
+ /* @__PURE__ */ jsxs(View, { style: styles.planHead, children: [
397
+ /* @__PURE__ */ jsx(Text, { style: styles.planTitle, numberOfLines: 1, children: plan.name }),
398
+ plan.recommended ? /* @__PURE__ */ jsx(Badge, { variant: "accent", size: "sm", children: plan.recommendedLabel ?? labels.bestValue }) : null
399
+ ] }),
400
+ /* @__PURE__ */ jsxs(Text, { style: styles.planNote, children: [
401
+ labels.planPrice(plan.price, plan.period),
402
+ plan.note ? ` \xB7 ${plan.note}` : null
403
+ ] })
404
+ ] }),
405
+ android ? null : (
406
+ // The slot keeps its width unchecked so the text never shifts.
407
+ /* @__PURE__ */ jsx(View, { "aria-hidden": true, style: styles.check, children: on ? /* @__PURE__ */ jsx(Icon, { name: "check", size: theme.icon.md, color: theme.color.accentStrong }) : null })
408
+ )
409
+ ]
410
+ },
411
+ plan.id
412
+ );
413
+ }) })
414
+ ] }),
415
+ /* @__PURE__ */ jsxs(View, { style: styles.footer, children: [
416
+ error ? /* @__PURE__ */ jsx(Text, { role: "alert", accessibilityRole: "alert", style: styles.error, children: error }) : null,
417
+ /* @__PURE__ */ jsx(
418
+ Button,
419
+ {
420
+ fullWidth: true,
421
+ size: "lg",
422
+ loading: busy,
423
+ onPress: selected ? () => onPurchase?.(selected) : void 0,
424
+ children: ctaLabel
425
+ }
426
+ ),
427
+ /* @__PURE__ */ jsx(Text, { style: styles.legal, children: legalText }),
428
+ footNote,
429
+ /* @__PURE__ */ jsxs(View, { style: styles.links, children: [
430
+ onRestore ? /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onPress: onRestore, children: restoreLabel }) : null,
431
+ links.map((l) => /* @__PURE__ */ jsx(
432
+ Pressable,
433
+ {
434
+ accessibilityRole: "link",
435
+ accessibilityLabel: l.label,
436
+ onPress: () => onLinkPress?.(l.id),
437
+ children: /* @__PURE__ */ jsx(Text, { style: styles.link, children: l.label })
438
+ },
439
+ l.id
440
+ ))
441
+ ] })
442
+ ] })
443
+ ] });
444
+ }
445
+ var sheet2 = createStyleSheet((theme) => ({
446
+ screen: { flex: 1, backgroundColor: theme.color.bg },
447
+ scroll: { padding: theme.layout.screenPad, gap: theme.space.s4 },
448
+ closeRow: { flexDirection: "row", justifyContent: "flex-end" },
449
+ close: {
450
+ minWidth: theme.tapMin,
451
+ minHeight: theme.tapMin,
452
+ alignItems: "flex-end",
453
+ justifyContent: "center"
454
+ },
455
+ intro: { gap: theme.space.s2 },
456
+ // The OS large title in the native face (R14): iOS 34 / 700, Android 28 / 400.
457
+ title: { fontFamily: theme.font.sans, color: theme.color.text },
458
+ titleIos: { fontWeight: theme.fontWeight.bold },
459
+ eyebrow: { fontFamily: theme.font.sans, color: theme.color.accentStrong },
460
+ description: {
461
+ fontFamily: theme.font.sans,
462
+ fontSize: theme.type.subheadline.fontSize,
463
+ lineHeight: theme.type.subheadline.fontSize * theme.lineHeight.relaxed,
464
+ color: theme.color.text2
465
+ },
466
+ features: { gap: theme.space.s2 },
467
+ featureRow: { flexDirection: "row", alignItems: "center", gap: theme.space.s2 },
468
+ featureLabel: {
469
+ flex: 1,
470
+ fontFamily: theme.font.sans,
471
+ fontSize: theme.type.subheadline.fontSize,
472
+ lineHeight: theme.type.subheadline.lineHeight,
473
+ color: theme.color.text
474
+ },
475
+ plans: { gap: theme.space.s2 },
476
+ // Refinement 38: a two-line row, 60 iOS / 72 Android, r 12. The chosen plan
477
+ // is the selected fill under an accent edge; the rest keep the strong edge.
478
+ plan: {
479
+ flexDirection: "row",
480
+ alignItems: "center",
481
+ gap: theme.space.s3,
482
+ paddingHorizontal: theme.space.s4,
483
+ paddingVertical: theme.space.s2,
484
+ borderRadius: theme.radius.md,
485
+ // `borderWidth.thick` is the static scale: the plan outline is part of the
486
+ // drawing (it is what "selected" looks like), not resizable chrome.
487
+ borderWidth: theme.borderWidth.thick,
488
+ borderColor: theme.color.borderStrong,
489
+ backgroundColor: "transparent",
490
+ variants: {
491
+ os: {
492
+ ios: { minHeight: theme.space[60] },
493
+ android: { minHeight: theme.space[72], gap: theme.space.s4 }
494
+ }
495
+ }
496
+ },
497
+ // `focus.ringColor`, not the raw accent: this border is what marks the chosen
498
+ // plan, and it is the only thing left doing that once high contrast flattens
499
+ // the fill. It derives the accent at the 3:1 hard-edge strength any
500
+ // meaning-carrying accent edge needs.
501
+ planOn: {
502
+ borderColor: theme.focus.ringColor,
503
+ backgroundColor: theme.state.selected
504
+ },
505
+ planPressed: { backgroundColor: theme.state.pressed },
506
+ indicator: {
507
+ width: INDICATOR.ring,
508
+ height: INDICATOR.ring,
509
+ flexShrink: 0,
510
+ alignItems: "center",
511
+ justifyContent: "center",
512
+ borderRadius: theme.radius.full,
513
+ borderWidth: theme.borderWidth.thick,
514
+ borderColor: theme.color.borderStrong
515
+ },
516
+ // The radio ring's selected edge, on the same footing as `planOn` above.
517
+ indicatorOn: { borderColor: theme.focus.ringColor },
518
+ indicatorDot: {
519
+ width: INDICATOR.dot,
520
+ height: INDICATOR.dot,
521
+ borderRadius: theme.radius.full,
522
+ backgroundColor: theme.color.accent
523
+ },
524
+ check: {
525
+ alignItems: "center",
526
+ justifyContent: "center",
527
+ width: theme.icon.md,
528
+ height: theme.icon.md
529
+ },
530
+ planMain: { flex: 1, minWidth: 0, gap: theme.space[2] },
531
+ planHead: { flexDirection: "row", alignItems: "center", gap: theme.space.s2 },
532
+ // Title: iOS body 17 / 400; Android body-large 16 / .5.
533
+ planTitle: {
534
+ flexShrink: 1,
535
+ fontFamily: theme.font.sans,
536
+ fontWeight: theme.fontWeight.regular,
537
+ color: theme.color.text,
538
+ variants: {
539
+ os: {
540
+ ios: {
541
+ fontSize: theme.type.body.fontSize,
542
+ lineHeight: theme.type.body.lineHeight,
543
+ letterSpacing: theme.type.body.letterSpacing
544
+ },
545
+ android: {
546
+ fontSize: theme.fontSize[16],
547
+ lineHeight: theme.space[24],
548
+ letterSpacing: theme.fontSize[16] * 0.03125
549
+ }
550
+ }
551
+ }
552
+ },
553
+ // Price as the subtitle: iOS subheadline 15, Android body-medium 14, `text2`.
554
+ planNote: {
555
+ fontFamily: theme.font.sans,
556
+ color: theme.color.text2,
557
+ fontVariant: ["tabular-nums"],
558
+ variants: {
559
+ os: {
560
+ ios: {
561
+ fontSize: theme.type.subheadline.fontSize,
562
+ lineHeight: theme.type.subheadline.lineHeight,
563
+ letterSpacing: theme.type.subheadline.letterSpacing
564
+ },
565
+ android: {
566
+ fontSize: theme.fontSize[14],
567
+ lineHeight: theme.space[20],
568
+ letterSpacing: theme.fontSize[14] * 0.0179
569
+ }
570
+ }
571
+ }
572
+ },
573
+ footer: {
574
+ padding: theme.layout.screenPad,
575
+ gap: theme.space.s2,
576
+ borderTopWidth: theme.borderWidth.base,
577
+ borderTopColor: theme.color.border,
578
+ backgroundColor: theme.color.bg
579
+ },
580
+ // Legal copy is footnote 13 in `text2` (F60), never caption 11.
581
+ error: {
582
+ fontFamily: theme.font.sans,
583
+ fontSize: theme.type.footnote.fontSize,
584
+ lineHeight: theme.type.footnote.lineHeight,
585
+ color: theme.color.danger,
586
+ textAlign: "center"
587
+ },
588
+ legal: {
589
+ fontFamily: theme.font.sans,
590
+ fontSize: theme.type.footnote.fontSize,
591
+ lineHeight: theme.type.footnote.lineHeight,
592
+ color: theme.color.text2,
593
+ textAlign: "center"
594
+ },
595
+ links: {
596
+ flexDirection: "row",
597
+ justifyContent: "center",
598
+ flexWrap: "wrap",
599
+ gap: theme.space.s4
600
+ },
601
+ link: {
602
+ fontFamily: theme.font.sans,
603
+ fontSize: theme.type.footnote.fontSize,
604
+ lineHeight: theme.type.footnote.lineHeight,
605
+ color: theme.color.text2
606
+ }
607
+ }));
608
+ var WEB_BLUR = Platform.OS === "web";
609
+ var FADE_STEPS = [0.25, 0.5, 0.75, 0.92];
610
+ function PaywallGate({
611
+ children,
612
+ locked = true,
613
+ treatment = "blur",
614
+ title,
615
+ description,
616
+ planLabel,
617
+ actionLabel,
618
+ onAction,
619
+ secondaryLabel,
620
+ onSecondary,
621
+ previewHeight,
622
+ size = "md",
623
+ BlurComponent,
624
+ icon = "lock",
625
+ headingLevel = 3,
626
+ style,
627
+ tone = "accent",
628
+ variant = "card",
629
+ loading = false,
630
+ error
631
+ }) {
632
+ const { styles, theme } = useStyles(sheet3);
633
+ const android = isAndroidIdiom(theme);
634
+ if (!locked) return /* @__PURE__ */ jsx(View, { style, children });
635
+ const compact = size === "compact";
636
+ const fade = treatment === "fade";
637
+ const preview = previewHeight ?? theme.panel.xs;
638
+ const inline = variant === "inline";
639
+ const ink = {
640
+ accent: {
641
+ ink: theme.color.accentOnSoft,
642
+ strong: theme.color.accentStrong,
643
+ fill: theme.color.accentSoft,
644
+ edge: theme.edge.accent
645
+ },
646
+ good: {
647
+ ink: theme.color.goodOnSoft,
648
+ strong: theme.color.goodOnSoft,
649
+ fill: theme.color.goodSoft,
650
+ edge: theme.edge.good
651
+ },
652
+ warn: {
653
+ ink: theme.color.warnOnSoft,
654
+ strong: theme.color.warnOnSoft,
655
+ fill: theme.color.warnSoft,
656
+ edge: theme.edge.warn
657
+ },
658
+ danger: {
659
+ ink: theme.color.dangerOnSoft,
660
+ strong: theme.color.dangerOnSoft,
661
+ fill: theme.color.dangerSoft,
662
+ edge: theme.edge.danger
663
+ },
664
+ info: {
665
+ ink: theme.color.infoOnSoft,
666
+ strong: theme.color.infoOnSoft,
667
+ fill: theme.color.infoSoft,
668
+ edge: theme.edge.info
669
+ },
670
+ neutral: {
671
+ ink: theme.color.text2,
672
+ strong: theme.color.text2,
673
+ fill: theme.color.surface2,
674
+ edge: theme.edge.neutral
675
+ }
676
+ }[tone];
677
+ const card = /* @__PURE__ */ jsxs(
678
+ View,
679
+ {
680
+ accessibilityRole: "summary",
681
+ accessibilityLabel: title,
682
+ accessibilityState: { busy: loading },
683
+ style: [
684
+ styles.card,
685
+ compact ? styles.cardCompact : styles.cardRegular,
686
+ inline ? styles.cardInline : null
687
+ ],
688
+ children: [
689
+ /* @__PURE__ */ jsx(
690
+ View,
691
+ {
692
+ style: [
693
+ styles.lock,
694
+ compact ? styles.lockCompact : styles.lockRegular,
695
+ { backgroundColor: ink.fill, borderColor: ink.edge }
696
+ ],
697
+ children: /* @__PURE__ */ jsx(Icon, { name: icon, size: compact ? theme.icon.sm : theme.icon.md, color: ink.ink })
698
+ }
699
+ ),
700
+ /* @__PURE__ */ jsxs(View, { style: [styles.copy, inline ? styles.copyInline : null], children: [
701
+ planLabel ? /* @__PURE__ */ jsx(
702
+ Text,
703
+ {
704
+ style: [
705
+ styles.plan,
706
+ resolveTextRole(theme, "overline", android),
707
+ { color: ink.strong }
708
+ ],
709
+ children: planLabel
710
+ }
711
+ ) : null,
712
+ /* @__PURE__ */ jsx(Text, { accessibilityRole: "header", "aria-level": headingLevel, style: styles.title, children: title }),
713
+ description ? /* @__PURE__ */ jsx(Text, { style: styles.description, children: description }) : null,
714
+ error ? /* @__PURE__ */ jsx(Text, { role: "alert", accessibilityRole: "alert", style: styles.error, children: error }) : null
715
+ ] }),
716
+ /* @__PURE__ */ jsxs(View, { style: styles.actions, children: [
717
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "primary", loading, onPress: onAction, children: actionLabel }),
718
+ secondaryLabel && onSecondary ? /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onPress: onSecondary, children: secondaryLabel }) : null
719
+ ] })
720
+ ]
721
+ }
722
+ );
723
+ return /* @__PURE__ */ jsxs(View, { style: [styles.root, style], children: [
724
+ /* @__PURE__ */ jsxs(
725
+ View,
726
+ {
727
+ pointerEvents: "none",
728
+ accessibilityElementsHidden: true,
729
+ importantForAccessibility: "no-hide-descendants",
730
+ "aria-hidden": true,
731
+ style: [
732
+ treatment === "dim" ? styles.dimmed : null,
733
+ treatment === "blur" && WEB_BLUR ? (
734
+ // react-native-web forwards `filter` as CSS; cast because RN's own
735
+ // `filter` type is the new-architecture array form. `blur.md`
736
+ // matches the web component's `--blur-md`.
737
+ {
738
+ filter: `blur(${theme.blur.md}px) saturate(0.6)`,
739
+ opacity: theme.opacity[72]
740
+ }
741
+ ) : null,
742
+ fade ? { maxHeight: preview + theme.space.s8, overflow: "hidden" } : null
743
+ ],
744
+ children: [
745
+ children,
746
+ treatment === "blur" ? BlurComponent ? /* @__PURE__ */ jsx(BlurComponent, { intensity: 40, style: styles.fill }) : WEB_BLUR ? null : /* @__PURE__ */ jsx(View, { style: [styles.fill, styles.veil] }) : null,
747
+ fade ? /* @__PURE__ */ jsx(View, { style: styles.fadeBands, children: FADE_STEPS.map((opacity) => /* @__PURE__ */ jsx(View, { style: [styles.fadeBand, { opacity }] }, opacity)) }) : null
748
+ ]
749
+ }
750
+ ),
751
+ /* @__PURE__ */ jsx(View, { style: fade ? styles.slotAfter : styles.slotOver, children: card })
752
+ ] });
753
+ }
754
+ var sheet3 = createStyleSheet((theme) => ({
755
+ root: { position: "relative", overflow: "hidden" },
756
+ fill: { position: "absolute", top: 0, right: 0, bottom: 0, left: 0 },
757
+ // The frosted fallback: opaque enough that no row of sample text is
758
+ // legible through it, translucent enough that the shape still shows.
759
+ veil: { backgroundColor: theme.color.surface, opacity: theme.opacity[72] },
760
+ dimmed: { opacity: theme.state.disabledOpacity },
761
+ fadeBands: { position: "absolute", left: 0, right: 0, bottom: 0 },
762
+ fadeBand: { height: theme.space.s2, backgroundColor: theme.color.bg },
763
+ slotOver: {
764
+ position: "absolute",
765
+ top: 0,
766
+ right: 0,
767
+ bottom: 0,
768
+ left: 0,
769
+ alignItems: "center",
770
+ justifyContent: "center",
771
+ padding: theme.space.s4
772
+ },
773
+ slotAfter: { alignItems: "center", marginTop: -theme.space.s6, padding: theme.space.s4 },
774
+ card: {
775
+ width: "100%",
776
+ alignItems: "center",
777
+ gap: theme.space.s3,
778
+ borderRadius: theme.radius.lg,
779
+ borderWidth: theme.borderWidth.base,
780
+ borderColor: theme.color.border,
781
+ backgroundColor: theme.color.surface,
782
+ ...theme.shadow.md
783
+ },
784
+ cardRegular: { maxWidth: theme.panel.xl, padding: theme.space.s5 },
785
+ cardCompact: { maxWidth: theme.panel.lg, padding: theme.space.s4 },
786
+ lock: {
787
+ alignItems: "center",
788
+ justifyContent: "center",
789
+ borderRadius: theme.radius.full,
790
+ backgroundColor: theme.color.accentSoft,
791
+ borderWidth: theme.borderWidth.base,
792
+ borderColor: theme.edge.accent
793
+ },
794
+ cardInline: { maxWidth: "100%", flexDirection: "row", alignItems: "center", gap: theme.space.s3 },
795
+ copyInline: { flex: 1, alignItems: "flex-start" },
796
+ error: {
797
+ fontFamily: theme.font.sans,
798
+ fontSize: theme.type.footnote.fontSize,
799
+ lineHeight: theme.type.footnote.lineHeight,
800
+ color: theme.color.danger,
801
+ textAlign: "center"
802
+ },
803
+ lockRegular: { width: theme.ctl.md, height: theme.ctl.md },
804
+ lockCompact: { width: theme.ctl.sm, height: theme.ctl.sm },
805
+ copy: { alignItems: "center", gap: theme.space.s1 },
806
+ // The sans overline role (Refinement 36), not bold caption 2 caps.
807
+ plan: {
808
+ fontFamily: theme.font.sans,
809
+ color: theme.color.accentStrong
810
+ },
811
+ title: {
812
+ fontFamily: theme.font.sans,
813
+ fontWeight: theme.fontWeight.bold,
814
+ fontSize: theme.fontSize[14],
815
+ color: theme.color.text,
816
+ textAlign: "center"
817
+ },
818
+ description: {
819
+ fontFamily: theme.font.sans,
820
+ fontSize: theme.type.footnote.fontSize,
821
+ lineHeight: theme.type.footnote.fontSize * theme.lineHeight.normal,
822
+ color: theme.color.text2,
823
+ textAlign: "center"
824
+ },
825
+ actions: {
826
+ flexDirection: "row",
827
+ flexWrap: "wrap",
828
+ justifyContent: "center",
829
+ gap: theme.space.s2
830
+ }
831
+ }));
832
+ var METER_SEGMENTS = 10;
833
+ function MeterBar({
834
+ value,
835
+ max,
836
+ unit,
837
+ tone = "accent",
838
+ label,
839
+ caption,
840
+ size = "md",
841
+ animate = false,
842
+ ariaLabel,
843
+ locale: localeProp,
844
+ labels: labelsProp,
845
+ style,
846
+ variant = "bar"
847
+ }) {
848
+ const { styles } = useStyles(sheet4);
849
+ const theme = useLoamTheme("progress");
850
+ const fill = tone === "good" ? theme.color.good : tone === "warn" ? theme.color.warn : tone === "danger" ? theme.color.danger : tone === "neutral" ? theme.color.text3 : theme.color.accentStrong;
851
+ const filled = Math.max(
852
+ 0,
853
+ Math.min(METER_SEGMENTS, Math.round(value / (max || 1) * METER_SEGMENTS))
854
+ );
855
+ const labels = useLabels("paywall", DEFAULT_LABELS, labelsProp);
856
+ const providerLocale = useLoamLocale();
857
+ const locale = localeProp ?? providerLocale;
858
+ const readout = labels.meterReadout(
859
+ value.toLocaleString(locale),
860
+ max.toLocaleString(locale),
861
+ unit
862
+ );
863
+ const name = ariaLabel ?? (label ? `${label}: ${readout}` : readout);
864
+ return /* @__PURE__ */ jsxs(View, { style: [styles.block, style], children: [
865
+ label == null && unit == null ? null : /* @__PURE__ */ jsxs(View, { style: styles.readoutRow, children: [
866
+ label == null ? null : /* @__PURE__ */ jsx(Text, { style: styles.label, children: label }),
867
+ /* @__PURE__ */ jsx(View, { style: styles.pushEnd, children: /* @__PURE__ */ jsx(Text, { style: styles.readout, children: readout }) })
868
+ ] }),
869
+ variant === "segments" ? /* @__PURE__ */ jsx(
870
+ View,
871
+ {
872
+ testID: "meter-segments",
873
+ accessibilityRole: "progressbar",
874
+ accessibilityLabel: name,
875
+ accessibilityValue: { min: 0, max, now: value },
876
+ style: styles.segments,
877
+ children: Array.from({ length: METER_SEGMENTS }, (_, i) => /* @__PURE__ */ jsx(
878
+ View,
879
+ {
880
+ style: [
881
+ styles.segment,
882
+ size === "sm" ? styles.segmentSm : styles.segmentMd,
883
+ { backgroundColor: i < filled ? fill : theme.color.surface3 }
884
+ ]
885
+ },
886
+ i
887
+ ))
888
+ }
889
+ ) : /* @__PURE__ */ jsx(
890
+ Progress,
891
+ {
892
+ value,
893
+ max,
894
+ size,
895
+ tone,
896
+ animateValue: animate,
897
+ accessibilityLabel: name
898
+ }
899
+ ),
900
+ caption ? /* @__PURE__ */ jsx(Text, { style: styles.caption, children: caption }) : null
901
+ ] });
902
+ }
903
+ var sheet4 = createStyleSheet((theme) => ({
904
+ block: { alignSelf: "stretch", gap: theme.space.s1 },
905
+ readoutRow: {
906
+ flexDirection: "row",
907
+ flexWrap: "wrap",
908
+ alignItems: "baseline",
909
+ gap: theme.space.s2
910
+ },
911
+ label: {
912
+ flexShrink: 1,
913
+ fontFamily: theme.font.sans,
914
+ fontSize: theme.type.footnote.fontSize,
915
+ lineHeight: theme.type.footnote.lineHeight,
916
+ color: theme.color.text2
917
+ },
918
+ segments: { flexDirection: "row", gap: theme.space[2] },
919
+ segment: { flex: 1, borderRadius: theme.radius.full },
920
+ segmentSm: { height: theme.space[4] },
921
+ segmentMd: { height: theme.space[8] },
922
+ pushEnd: { marginStart: "auto" },
923
+ readout: {
924
+ fontFamily: theme.font.sans,
925
+ fontWeight: theme.fontWeight.bold,
926
+ fontVariant: ["tabular-nums"],
927
+ fontSize: theme.type.footnote.fontSize,
928
+ lineHeight: theme.type.footnote.lineHeight,
929
+ color: theme.color.text
930
+ },
931
+ caption: {
932
+ fontFamily: theme.font.sans,
933
+ fontSize: theme.type.caption1.fontSize,
934
+ lineHeight: theme.type.caption1.fontSize * theme.lineHeight.normal,
935
+ color: theme.color.text3
936
+ }
937
+ }));
938
+ function usePlanTone(tone) {
939
+ const theme = useLoamTheme("commerce");
940
+ switch (tone) {
941
+ case "accent":
942
+ return {
943
+ ink: theme.color.accentOnSoft,
944
+ fill: theme.color.accentSoft,
945
+ edge: theme.edge.accent
946
+ };
947
+ case "warn":
948
+ return { ink: theme.color.warnOnSoft, fill: theme.color.warnSoft, edge: theme.edge.warn };
949
+ case "danger":
950
+ return {
951
+ ink: theme.color.dangerOnSoft,
952
+ fill: theme.color.dangerSoft,
953
+ edge: theme.edge.danger
954
+ };
955
+ case "info":
956
+ return { ink: theme.color.infoOnSoft, fill: theme.color.infoSoft, edge: theme.edge.info };
957
+ case "neutral":
958
+ return { ink: theme.color.text2, fill: theme.color.surface2, edge: theme.edge.neutral };
959
+ case "good":
960
+ default:
961
+ return { ink: theme.color.goodOnSoft, fill: theme.color.goodSoft, edge: theme.edge.good };
962
+ }
963
+ }
964
+ var ALIGN_SELF = { start: "flex-start", center: "center", end: "flex-end" };
965
+ function SavingsChip({
966
+ savingMinor,
967
+ plan,
968
+ currency,
969
+ label,
970
+ per,
971
+ locale,
972
+ labels: labelsProp,
973
+ style,
974
+ tone = "good",
975
+ size = "md",
976
+ variant = "soft",
977
+ motion,
978
+ loading = false,
979
+ error,
980
+ count
981
+ }) {
982
+ const labels = useLabels("paywall", DEFAULT_LABELS, labelsProp);
983
+ const theme = useLoamTheme("commerce");
984
+ const { styles } = useStyles(sheet5);
985
+ const toned = usePlanTone(error ? "danger" : tone);
986
+ const saving = savingMinor ?? (plan ? yearlySaving(plan) : null);
987
+ if (saving == null) return null;
988
+ const outline = variant === "outline";
989
+ const wordStyle = [
990
+ styles.savingWord,
991
+ { color: toned.ink },
992
+ size === "sm" ? styles.savingWordSm : null
993
+ ];
994
+ return /* @__PURE__ */ jsxs(
995
+ View,
996
+ {
997
+ testID: "savings-chip",
998
+ style: [
999
+ styles.savingChip,
1000
+ { backgroundColor: toned.fill, borderColor: toned.edge },
1001
+ size === "sm" ? styles.savingChipSm : null,
1002
+ outline ? {
1003
+ backgroundColor: "transparent",
1004
+ borderWidth: theme.borderWidth.thin,
1005
+ borderColor: theme.color.borderStrong
1006
+ } : null,
1007
+ style
1008
+ ],
1009
+ children: [
1010
+ /* @__PURE__ */ jsx(Text, { style: wordStyle, children: label ?? labels.save }),
1011
+ /* @__PURE__ */ jsx(
1012
+ Amount,
1013
+ {
1014
+ minor: saving,
1015
+ currency,
1016
+ size: "sm",
1017
+ weight: "bold",
1018
+ locale,
1019
+ loading,
1020
+ roll: motion != null,
1021
+ motion,
1022
+ style: { color: toned.ink }
1023
+ }
1024
+ ),
1025
+ /* @__PURE__ */ jsx(Text, { style: wordStyle, children: per ?? labels.perYear }),
1026
+ count === void 0 ? null : /* @__PURE__ */ jsx(Badge, { count, variant: error ? "danger" : tone, size: "sm", motion }),
1027
+ error ? /* @__PURE__ */ jsx(Text, { role: "alert", accessibilityRole: "alert", style: wordStyle, children: error }) : null
1028
+ ]
1029
+ }
1030
+ );
1031
+ }
1032
+ function PlanPriceDelta({
1033
+ fromMinor,
1034
+ toMinor,
1035
+ currency,
1036
+ unit,
1037
+ sameLabel,
1038
+ locale,
1039
+ labels: labelsProp,
1040
+ style,
1041
+ size = "sm",
1042
+ tone,
1043
+ loading = false,
1044
+ align = "start",
1045
+ error
1046
+ }) {
1047
+ const { styles } = useStyles(sheet5);
1048
+ const labels = useLabels("paywall", DEFAULT_LABELS, labelsProp);
1049
+ const pay = usePayLabels(labelsProp);
1050
+ const delta = planDelta(fromMinor, toMinor);
1051
+ const place = { alignSelf: ALIGN_SELF[align] };
1052
+ if (delta === 0) {
1053
+ return /* @__PURE__ */ jsx(Text, { style: [styles.deltaUnit, place, error ? styles.cardError : null, style], children: error ?? sameLabel ?? labels.samePrice });
1054
+ }
1055
+ return /* @__PURE__ */ jsxs(View, { style: [styles.deltaRow, place, style], children: [
1056
+ /* @__PURE__ */ jsx(
1057
+ Amount,
1058
+ {
1059
+ minor: delta,
1060
+ currency,
1061
+ size,
1062
+ weight: "bold",
1063
+ signed: true,
1064
+ tone: error ? "danger" : tone ?? (delta > 0 ? "warn" : "good"),
1065
+ loading,
1066
+ locale,
1067
+ error
1068
+ }
1069
+ ),
1070
+ /* @__PURE__ */ jsx(Text, { style: styles.deltaUnit, children: unit ?? pay.perSeat })
1071
+ ] });
1072
+ }
1073
+ function PlanFeatureRow({
1074
+ label,
1075
+ included = true,
1076
+ detail,
1077
+ stateLabel,
1078
+ labels: labelsProp,
1079
+ style,
1080
+ tone = "good",
1081
+ loading = false,
1082
+ error
1083
+ }) {
1084
+ const labels = useLabels("paywall", DEFAULT_LABELS, labelsProp);
1085
+ const theme = useLoamTheme("commerce");
1086
+ const { styles } = useStyles(sheet5);
1087
+ const state = stateLabel ?? (included ? labels.included : labels.notIncluded);
1088
+ const glyph = error ? theme.color.danger : tone === "good" ? theme.color.good : tone === "neutral" ? theme.color.text2 : theme.color[tone];
1089
+ return /* @__PURE__ */ jsxs(
1090
+ View,
1091
+ {
1092
+ role: "listitem",
1093
+ accessibilityLabel: `${labels.featureState(state)}${label}${detail ? `. ${detail}` : ""}${error ? `. ${error}` : ""}`,
1094
+ accessibilityState: { busy: loading },
1095
+ "aria-busy": loading || void 0,
1096
+ style: [styles.featureRow, style],
1097
+ children: [
1098
+ /* @__PURE__ */ jsx(
1099
+ Icon,
1100
+ {
1101
+ name: included ? "check" : "minus",
1102
+ size: theme.icon.sm,
1103
+ color: included || error ? glyph : theme.color.text3
1104
+ }
1105
+ ),
1106
+ /* @__PURE__ */ jsxs(View, { style: styles.featureBody, children: [
1107
+ loading ? /* @__PURE__ */ jsx(Skeleton, { decorative: true, width: theme.space[96] }) : /* @__PURE__ */ jsx(Text, { style: included ? styles.featureLabel : styles.featureLabelOut, children: label }),
1108
+ detail && !loading ? /* @__PURE__ */ jsx(Text, { style: styles.featureDetail, children: detail }) : null,
1109
+ error ? /* @__PURE__ */ jsx(Text, { role: "alert", accessibilityRole: "alert", style: styles.cardError, children: error }) : null
1110
+ ] })
1111
+ ]
1112
+ }
1113
+ );
1114
+ }
1115
+ function PlanPriceCell({
1116
+ plan,
1117
+ currency,
1118
+ cadence = "mo",
1119
+ size = "lg",
1120
+ savingMinor,
1121
+ locale,
1122
+ labels: labelsProp,
1123
+ style,
1124
+ roll = true,
1125
+ motion = "full",
1126
+ tone,
1127
+ loading = false,
1128
+ delta,
1129
+ caption,
1130
+ align = "start",
1131
+ error
1132
+ }) {
1133
+ const { styles } = useStyles(sheet5);
1134
+ const labels = useLabels("paywall", DEFAULT_LABELS, labelsProp);
1135
+ const pay = usePayLabels(labelsProp);
1136
+ const words = planCta(plan, pay).price;
1137
+ const perMonth = yearlyMonthly(plan);
1138
+ const yearly = cadence === "yr" && plan.yearly != null ? plan.yearly : null;
1139
+ const headline = yearly != null && perMonth != null ? perMonth : plan.price;
1140
+ return /* @__PURE__ */ jsxs(
1141
+ View,
1142
+ {
1143
+ style: [
1144
+ styles.priceCell,
1145
+ align === "start" ? null : { alignItems: ALIGN_SELF[align] },
1146
+ style
1147
+ ],
1148
+ children: [
1149
+ /* @__PURE__ */ jsx(
1150
+ View,
1151
+ {
1152
+ style: [
1153
+ styles.priceHeadline,
1154
+ align === "start" ? null : { justifyContent: ALIGN_SELF[align] }
1155
+ ],
1156
+ children: words ? /* @__PURE__ */ jsx(Text, { style: size === "hero" ? styles.priceWordsHero : styles.priceWords, children: words }) : headline == null ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
1157
+ /* @__PURE__ */ jsx(
1158
+ Amount,
1159
+ {
1160
+ minor: headline,
1161
+ currency,
1162
+ size,
1163
+ weight: "bold",
1164
+ tone: error ? "danger" : tone,
1165
+ loading,
1166
+ locale,
1167
+ roll,
1168
+ motion
1169
+ }
1170
+ ),
1171
+ /* @__PURE__ */ jsx(Text, { style: styles.priceUnit, children: pay.perSeat })
1172
+ ] })
1173
+ }
1174
+ ),
1175
+ yearly == null ? null : /* @__PURE__ */ jsxs(View, { style: styles.yearlyRow, children: [
1176
+ /* @__PURE__ */ jsx(
1177
+ Amount,
1178
+ {
1179
+ minor: yearly,
1180
+ currency,
1181
+ size: "sm",
1182
+ weight: "bold",
1183
+ loading,
1184
+ locale
1185
+ }
1186
+ ),
1187
+ /* @__PURE__ */ jsx(Text, { style: styles.priceUnit, children: labels.billedYearlyTail }),
1188
+ /* @__PURE__ */ jsx(
1189
+ SavingsChip,
1190
+ {
1191
+ savingMinor,
1192
+ plan,
1193
+ currency,
1194
+ locale,
1195
+ labels: labelsProp
1196
+ }
1197
+ )
1198
+ ] }),
1199
+ delta ? /* @__PURE__ */ jsx(
1200
+ PlanPriceDelta,
1201
+ {
1202
+ fromMinor: delta.fromMinor,
1203
+ toMinor: delta.toMinor,
1204
+ currency,
1205
+ unit: delta.unit,
1206
+ locale,
1207
+ loading,
1208
+ align,
1209
+ labels: labelsProp
1210
+ }
1211
+ ) : null,
1212
+ caption ? /* @__PURE__ */ jsx(Text, { style: styles.priceUnit, children: caption }) : null,
1213
+ error ? /* @__PURE__ */ jsx(Text, { role: "alert", accessibilityRole: "alert", style: styles.cardError, children: error }) : null
1214
+ ]
1215
+ }
1216
+ );
1217
+ }
1218
+ function PlanCard({
1219
+ plan,
1220
+ currency,
1221
+ cadence = "mo",
1222
+ highlighted,
1223
+ isCurrent = false,
1224
+ savingMinor,
1225
+ ctaLabel,
1226
+ currentLabel: currentLabelProp,
1227
+ onSelect,
1228
+ loading = false,
1229
+ locked = false,
1230
+ locale,
1231
+ labels: labelsProp,
1232
+ style,
1233
+ elevation = "default",
1234
+ padding = "md",
1235
+ error,
1236
+ tone
1237
+ }) {
1238
+ const theme = useLoamTheme("commerce");
1239
+ const { styles } = useStyles(sheet5);
1240
+ const labels = useLabels("paywall", DEFAULT_LABELS, labelsProp);
1241
+ const pay = usePayLabels(labelsProp);
1242
+ const currentLabel = currentLabelProp ?? labels.yourPlan;
1243
+ const recommended = highlighted ?? plan.popular ?? false;
1244
+ return /* @__PURE__ */ jsxs(
1245
+ View,
1246
+ {
1247
+ testID: `plan-card-${plan.id}`,
1248
+ style: [
1249
+ styles.card,
1250
+ recommended ? styles.cardRecommended : styles.cardPlain,
1251
+ // `current` adds the accent tint so "this one is yours" survives next to
1252
+ // a highlighted neighbour.
1253
+ isCurrent ? styles.cardCurrent : null,
1254
+ elevation === "flat" ? styles.cardFlat : null,
1255
+ elevation === "raised" ? styles.cardRaised : null,
1256
+ padding === "sm" ? styles.cardPadSm : null,
1257
+ padding === "lg" ? styles.cardPadLg : null,
1258
+ error ? { borderColor: theme.color.danger } : null,
1259
+ style
1260
+ ],
1261
+ children: [
1262
+ /* @__PURE__ */ jsxs(View, { style: styles.cardHead, children: [
1263
+ /* @__PURE__ */ jsx(Text, { accessibilityRole: "header", style: styles.planName, children: plan.name }),
1264
+ recommended ? /* @__PURE__ */ jsx(Badge, { style: styles.rowBadge, variant: "accent", size: "sm", children: pay.popularBadge }) : null,
1265
+ isCurrent ? /* @__PURE__ */ jsx(Badge, { variant: "neutral", size: "sm", outline: true, children: currentLabel }) : null
1266
+ ] }),
1267
+ /* @__PURE__ */ jsx(
1268
+ PlanPriceCell,
1269
+ {
1270
+ plan,
1271
+ currency,
1272
+ cadence,
1273
+ savingMinor,
1274
+ locale,
1275
+ tone,
1276
+ labels: labelsProp
1277
+ }
1278
+ ),
1279
+ /* @__PURE__ */ jsx(Text, { style: styles.blurb, children: plan.blurb }),
1280
+ /* @__PURE__ */ jsx(View, { style: styles.rule }),
1281
+ /* @__PURE__ */ jsx(View, { role: "list", accessibilityRole: "list", style: styles.featureList, children: plan.features.map((feature) => /* @__PURE__ */ jsx(PlanFeatureRow, { label: feature, labels: labelsProp }, feature)) }),
1282
+ isCurrent ? (
1283
+ // Not a disabled button: a dead control invites the press that teaches
1284
+ // nothing. The marker states the fact and takes no tab stop.
1285
+ /* @__PURE__ */ jsxs(View, { style: styles.currentMarker, children: [
1286
+ /* @__PURE__ */ jsx(Icon, { name: "check-circle", size: theme.icon.sm, color: theme.color.good }),
1287
+ /* @__PURE__ */ jsx(Text, { style: styles.currentMarkerLabel, children: currentLabel })
1288
+ ] })
1289
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
1290
+ error ? /* @__PURE__ */ jsx(Text, { role: "alert", accessibilityRole: "alert", style: styles.cardError, children: error }) : null,
1291
+ /* @__PURE__ */ jsx(
1292
+ Button,
1293
+ {
1294
+ fullWidth: true,
1295
+ variant: recommended ? "primary" : "outline",
1296
+ loading,
1297
+ onPress: locked ? void 0 : () => onSelect?.(plan.id),
1298
+ children: ctaLabel ?? planCta(plan, pay).cta
1299
+ }
1300
+ )
1301
+ ] })
1302
+ ]
1303
+ }
1304
+ );
1305
+ }
1306
+ function BillingPeriodToggle({
1307
+ value,
1308
+ onValueChange,
1309
+ monthlyLabel,
1310
+ yearlyLabel,
1311
+ size = "md",
1312
+ disabled = false,
1313
+ fullWidth = false,
1314
+ savingMinor,
1315
+ currency,
1316
+ locale,
1317
+ labels: labelsProp,
1318
+ style,
1319
+ loading = false,
1320
+ error,
1321
+ tone = "good",
1322
+ orientation = "horizontal",
1323
+ density = "standard",
1324
+ variant = "pill"
1325
+ }) {
1326
+ const { styles } = useStyles(sheet5);
1327
+ const labels = useLabels("paywall", DEFAULT_LABELS, labelsProp);
1328
+ const pay = usePayLabels(labelsProp);
1329
+ const control = /* @__PURE__ */ jsx(
1330
+ SegmentedControl,
1331
+ {
1332
+ accessibilityLabel: labels.billingPeriod,
1333
+ size,
1334
+ shape: variant,
1335
+ disabled: disabled || loading,
1336
+ fullWidth,
1337
+ value,
1338
+ onValueChange: (next) => onValueChange(next),
1339
+ items: [
1340
+ { value: "mo", label: monthlyLabel ?? pay.cadenceMonthly },
1341
+ { value: "yr", label: yearlyLabel ?? pay.cadenceYearly }
1342
+ ]
1343
+ }
1344
+ );
1345
+ const vertical = orientation === "vertical";
1346
+ return /* @__PURE__ */ jsxs(
1347
+ View,
1348
+ {
1349
+ accessibilityState: { busy: loading },
1350
+ "aria-busy": loading || void 0,
1351
+ style: [
1352
+ styles.toggleRow,
1353
+ fullWidth ? styles.toggleRowFull : null,
1354
+ vertical ? styles.toggleRowVertical : null,
1355
+ density === "compact" ? styles.toggleDense : null,
1356
+ density === "comfortable" ? styles.toggleLoose : null,
1357
+ style
1358
+ ],
1359
+ children: [
1360
+ fullWidth && !vertical ? /* @__PURE__ */ jsx(View, { style: styles.toggleGrow, children: control }) : control,
1361
+ savingMinor != null && currency ? /* @__PURE__ */ jsx(
1362
+ SavingsChip,
1363
+ {
1364
+ savingMinor,
1365
+ currency,
1366
+ locale,
1367
+ tone,
1368
+ labels: labelsProp
1369
+ }
1370
+ ) : null,
1371
+ error ? /* @__PURE__ */ jsx(Text, { role: "alert", accessibilityRole: "alert", style: styles.toggleError, children: error }) : null
1372
+ ]
1373
+ }
1374
+ );
1375
+ }
1376
+ function PlanGrid({
1377
+ plans,
1378
+ currency,
1379
+ cadence,
1380
+ onCadenceChange,
1381
+ onSelect,
1382
+ currentPlanId,
1383
+ pendingPlanId,
1384
+ savings,
1385
+ hideIds,
1386
+ loading = false,
1387
+ locale,
1388
+ labels: labelsProp,
1389
+ style,
1390
+ error,
1391
+ tone
1392
+ }) {
1393
+ const { styles } = useStyles(sheet5);
1394
+ const shown = hideIds?.length ? plans.filter((plan) => !hideIds.includes(plan.id)) : plans;
1395
+ return /* @__PURE__ */ jsxs(View, { style: [styles.grid, style], children: [
1396
+ onCadenceChange ? /* @__PURE__ */ jsx(
1397
+ BillingPeriodToggle,
1398
+ {
1399
+ value: cadence,
1400
+ onValueChange: onCadenceChange,
1401
+ currency,
1402
+ locale,
1403
+ labels: labelsProp
1404
+ }
1405
+ ) : null,
1406
+ error ? /* @__PURE__ */ jsx(Text, { role: "alert", accessibilityRole: "alert", style: styles.toggleError, children: error }) : null,
1407
+ /* @__PURE__ */ jsx(View, { style: styles.gridStack, children: shown.map((plan) => /* @__PURE__ */ jsx(
1408
+ PlanCard,
1409
+ {
1410
+ plan,
1411
+ currency,
1412
+ cadence,
1413
+ isCurrent: currentPlanId === plan.id,
1414
+ savingMinor: savings?.[plan.id],
1415
+ onSelect,
1416
+ loading: loading || pendingPlanId === plan.id,
1417
+ locked: pendingPlanId != null,
1418
+ tone,
1419
+ locale,
1420
+ labels: labelsProp
1421
+ },
1422
+ plan.id
1423
+ )) })
1424
+ ] });
1425
+ }
1426
+ var sheet5 = createStyleSheet((theme) => ({
1427
+ savingChip: {
1428
+ flexDirection: "row",
1429
+ alignItems: "baseline",
1430
+ alignSelf: "flex-start",
1431
+ gap: theme.space.s1,
1432
+ paddingHorizontal: theme.space.s2,
1433
+ paddingVertical: theme.space[2],
1434
+ borderRadius: theme.radius.full,
1435
+ // A soft tint needs its tone edge: the fill collapses to the plain surface
1436
+ // under high contrast, where the edge is the only thing drawing it.
1437
+ borderWidth: theme.borderWidth.base,
1438
+ borderColor: theme.edge.good,
1439
+ backgroundColor: theme.color.goodSoft
1440
+ },
1441
+ savingChipSm: { paddingHorizontal: theme.space[6], paddingVertical: 0 },
1442
+ savingWordSm: {
1443
+ fontSize: theme.type.caption2.fontSize,
1444
+ lineHeight: theme.type.caption2.lineHeight
1445
+ },
1446
+ savingWord: {
1447
+ fontFamily: theme.font.sans,
1448
+ fontSize: theme.type.caption1.fontSize,
1449
+ lineHeight: theme.type.caption1.lineHeight,
1450
+ color: theme.color.goodOnSoft
1451
+ },
1452
+ deltaRow: {
1453
+ flexDirection: "row",
1454
+ alignItems: "baseline",
1455
+ alignSelf: "flex-start",
1456
+ gap: theme.space.s1
1457
+ },
1458
+ deltaUnit: {
1459
+ fontFamily: theme.font.sans,
1460
+ fontSize: theme.type.footnote.fontSize,
1461
+ lineHeight: theme.type.footnote.lineHeight,
1462
+ color: theme.color.text3
1463
+ },
1464
+ featureRow: { flexDirection: "row", alignItems: "flex-start", gap: theme.space.s2 },
1465
+ featureBody: { flex: 1, gap: theme.space[2] },
1466
+ featureLabel: {
1467
+ fontFamily: theme.font.sans,
1468
+ fontSize: theme.type.footnote.fontSize,
1469
+ lineHeight: theme.type.footnote.fontSize * theme.lineHeight.normal,
1470
+ color: theme.color.text2
1471
+ },
1472
+ featureLabelOut: {
1473
+ fontFamily: theme.font.sans,
1474
+ fontSize: theme.type.footnote.fontSize,
1475
+ lineHeight: theme.type.footnote.fontSize * theme.lineHeight.normal,
1476
+ color: theme.color.text3
1477
+ },
1478
+ featureDetail: {
1479
+ fontFamily: theme.font.sans,
1480
+ fontSize: theme.type.caption1.fontSize,
1481
+ lineHeight: theme.type.caption1.lineHeight,
1482
+ color: theme.color.text3
1483
+ },
1484
+ priceCell: { gap: theme.space.s1 },
1485
+ priceHeadline: {
1486
+ flexDirection: "row",
1487
+ flexWrap: "wrap",
1488
+ alignItems: "baseline",
1489
+ columnGap: theme.space.s2,
1490
+ rowGap: theme.space.s1
1491
+ },
1492
+ priceWords: {
1493
+ fontFamily: theme.font.sans,
1494
+ fontWeight: theme.fontWeight.bold,
1495
+ fontSize: theme.type.title3.fontSize,
1496
+ lineHeight: theme.type.title3.lineHeight,
1497
+ color: theme.color.text
1498
+ },
1499
+ priceWordsHero: {
1500
+ fontFamily: theme.font.sans,
1501
+ fontWeight: theme.fontWeight.bold,
1502
+ fontSize: theme.fontSize[32],
1503
+ lineHeight: theme.fontSize[32] * theme.lineHeight.tight,
1504
+ color: theme.color.text
1505
+ },
1506
+ priceUnit: {
1507
+ fontFamily: theme.font.sans,
1508
+ fontSize: theme.type.footnote.fontSize,
1509
+ lineHeight: theme.type.footnote.lineHeight,
1510
+ color: theme.color.text3
1511
+ },
1512
+ yearlyRow: {
1513
+ flexDirection: "row",
1514
+ flexWrap: "wrap",
1515
+ alignItems: "baseline",
1516
+ gap: theme.space.s2
1517
+ },
1518
+ card: {
1519
+ alignSelf: "stretch",
1520
+ gap: theme.space.s4,
1521
+ padding: theme.space.s5,
1522
+ borderRadius: theme.radius.lg,
1523
+ backgroundColor: theme.color.surface
1524
+ },
1525
+ cardPlain: { borderWidth: theme.borderWidth.base, borderColor: theme.color.border },
1526
+ // `focus.ringColor`, not the raw accent: this border is what marks the
1527
+ // recommended plan, and it is a graphical object under SC 1.4.11 either way.
1528
+ // Identical to the raw accent in 9 of the 14 palette × mode combos, so it
1529
+ // moves only the four that failed. Same call `paywall.tsx` makes for `planOn`.
1530
+ cardRecommended: {
1531
+ borderWidth: theme.borderWidth.heavy,
1532
+ borderColor: theme.focus.ringColor,
1533
+ ...theme.shadow.md
1534
+ },
1535
+ cardFlat: { shadowOpacity: 0, elevation: 0 },
1536
+ cardRaised: { ...theme.shadow.lg },
1537
+ cardPadSm: { padding: theme.space.s3, gap: theme.space.s3 },
1538
+ cardPadLg: { padding: theme.space.s6 },
1539
+ cardError: {
1540
+ fontFamily: theme.font.sans,
1541
+ fontSize: theme.type.footnote.fontSize,
1542
+ lineHeight: theme.type.footnote.lineHeight,
1543
+ color: theme.color.danger
1544
+ },
1545
+ cardCurrent: { backgroundColor: theme.color.accentSoft, borderColor: theme.edge.accent },
1546
+ cardHead: {
1547
+ flexDirection: "row",
1548
+ flexWrap: "wrap",
1549
+ alignItems: "center",
1550
+ gap: theme.space.s2
1551
+ },
1552
+ // The plan name is the card's heading, so it never sits below the price words
1553
+ // ("Free", "Talk to us") it introduces: same title 3 step, same weight.
1554
+ planName: {
1555
+ fontFamily: theme.font.sans,
1556
+ fontWeight: theme.fontWeight.bold,
1557
+ fontSize: theme.type.title3.fontSize,
1558
+ lineHeight: theme.type.title3.lineHeight,
1559
+ color: theme.color.text
1560
+ },
1561
+ blurb: {
1562
+ fontFamily: theme.font.sans,
1563
+ fontSize: theme.type.footnote.fontSize,
1564
+ lineHeight: theme.type.footnote.fontSize * theme.lineHeight.normal,
1565
+ color: theme.color.text2
1566
+ },
1567
+ rule: { height: theme.borderWidth.base, backgroundColor: theme.color.border },
1568
+ featureList: { gap: theme.space.s2 },
1569
+ currentMarker: {
1570
+ flexDirection: "row",
1571
+ alignItems: "center",
1572
+ justifyContent: "center",
1573
+ gap: theme.space.s1,
1574
+ paddingVertical: theme.space.s2,
1575
+ borderRadius: theme.radius.md,
1576
+ borderWidth: theme.borderWidth.base,
1577
+ borderColor: theme.color.border
1578
+ },
1579
+ currentMarkerLabel: {
1580
+ fontFamily: theme.font.sans,
1581
+ fontWeight: theme.fontWeight.semibold,
1582
+ fontSize: theme.type.footnote.fontSize,
1583
+ lineHeight: theme.type.footnote.lineHeight,
1584
+ color: theme.color.text2
1585
+ },
1586
+ toggleRow: {
1587
+ flexDirection: "row",
1588
+ flexWrap: "wrap",
1589
+ alignItems: "center",
1590
+ justifyContent: "center",
1591
+ gap: theme.space.s3
1592
+ },
1593
+ toggleRowFull: { alignSelf: "stretch" },
1594
+ toggleRowVertical: { flexDirection: "column", flexWrap: "nowrap" },
1595
+ toggleDense: { gap: theme.space.s1 },
1596
+ toggleLoose: { gap: theme.space.s5 },
1597
+ toggleError: {
1598
+ fontFamily: theme.font.sans,
1599
+ fontSize: theme.type.footnote.fontSize,
1600
+ lineHeight: theme.type.footnote.lineHeight,
1601
+ color: theme.color.danger
1602
+ },
1603
+ toggleGrow: { flexGrow: 1, flexShrink: 1 },
1604
+ grid: { alignSelf: "stretch", gap: theme.space.s5 },
1605
+ gridStack: { gap: theme.space.s4 },
1606
+ // Badge hugs with `flex-start`, which pins it to the top of this centred row.
1607
+ rowBadge: { alignSelf: "center" }
1608
+ }));
1609
+ function prorationWay(proration) {
1610
+ return proration.dueNow === 0 && proration.refund > 0 ? "downgrade" : "upgrade";
1611
+ }
1612
+ function ProrationLine({
1613
+ proration,
1614
+ currency,
1615
+ effectiveLabel,
1616
+ nextMinor,
1617
+ nextCadenceLabel,
1618
+ locale,
1619
+ labels: labelsProp,
1620
+ style,
1621
+ size = "sm",
1622
+ tone,
1623
+ unit,
1624
+ loading = false,
1625
+ align = "start",
1626
+ density = "standard",
1627
+ orientation = "horizontal",
1628
+ error
1629
+ }) {
1630
+ const { styles } = useStyles(sheet6);
1631
+ const labels = useLabels("paywall", DEFAULT_LABELS, labelsProp);
1632
+ const downgrade = prorationWay(proration) === "downgrade";
1633
+ const vertical = orientation === "vertical";
1634
+ const next = nextMinor == null ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
1635
+ vertical ? null : /* @__PURE__ */ jsx(Text, { style: styles.prorationSep, children: "\xB7" }),
1636
+ /* @__PURE__ */ jsx(Text, { style: styles.prorationWord, children: labels.then }),
1637
+ /* @__PURE__ */ jsx(
1638
+ Amount,
1639
+ {
1640
+ minor: nextMinor,
1641
+ currency,
1642
+ size,
1643
+ weight: "bold",
1644
+ unit,
1645
+ loading,
1646
+ locale
1647
+ }
1648
+ ),
1649
+ /* @__PURE__ */ jsx(Text, { style: styles.prorationWord, children: labels.cadenceFrom(nextCadenceLabel ?? labels.monthly, effectiveLabel) })
1650
+ ] });
1651
+ const today = /* @__PURE__ */ jsxs(Fragment, { children: [
1652
+ /* @__PURE__ */ jsx(
1653
+ Amount,
1654
+ {
1655
+ minor: downgrade ? proration.refund : proration.dueNow,
1656
+ currency,
1657
+ size,
1658
+ weight: "bold",
1659
+ tone: error ? "danger" : tone ?? (downgrade ? "good" : "default"),
1660
+ unit,
1661
+ loading,
1662
+ locale
1663
+ }
1664
+ ),
1665
+ /* @__PURE__ */ jsx(Text, { style: styles.prorationWord, children: downgrade ? labels.creditedNextInvoice : labels.today })
1666
+ ] });
1667
+ return /* @__PURE__ */ jsxs(
1668
+ View,
1669
+ {
1670
+ accessibilityState: { busy: loading },
1671
+ style: [
1672
+ styles.prorationLine,
1673
+ align === "start" ? null : { justifyContent: align === "end" ? "flex-end" : "center" },
1674
+ density === "compact" ? styles.prorationLineCompact : null,
1675
+ vertical ? styles.prorationLineStack : null,
1676
+ style
1677
+ ],
1678
+ children: [
1679
+ vertical ? /* @__PURE__ */ jsx(View, { style: styles.prorationLineRow, children: today }) : today,
1680
+ vertical && next ? /* @__PURE__ */ jsx(View, { style: styles.prorationLineRow, children: next }) : next,
1681
+ error ? /* @__PURE__ */ jsx(Text, { role: "alert", accessibilityRole: "alert", style: styles.prorationError, children: error }) : null
1682
+ ]
1683
+ }
1684
+ );
1685
+ }
1686
+ function ProrationTable({
1687
+ proration,
1688
+ currency,
1689
+ fromName,
1690
+ toName,
1691
+ daysLeft = proration.daysLeft,
1692
+ cycleDays = proration.cycleDays,
1693
+ effectiveLabel,
1694
+ nextMinor,
1695
+ nextCadenceLabel,
1696
+ caption,
1697
+ note,
1698
+ locale,
1699
+ labels: labelsProp,
1700
+ style,
1701
+ size,
1702
+ unit,
1703
+ tone,
1704
+ loading = false,
1705
+ align = "end",
1706
+ density = "standard",
1707
+ empty,
1708
+ error,
1709
+ sortable = false,
1710
+ sort,
1711
+ onSortChange,
1712
+ selectable = false,
1713
+ selectedIds,
1714
+ onSelectedIdsChange
1715
+ }) {
1716
+ const { styles, theme } = useStyles(sheet6);
1717
+ const [innerSort, setInnerSort] = React7.useState(null);
1718
+ const [innerSel, setInnerSel] = React7.useState([]);
1719
+ const active = sort !== void 0 ? sort : innerSort;
1720
+ const selected = selectedIds ?? innerSel;
1721
+ const toggleSort = (key) => {
1722
+ const next = {
1723
+ key,
1724
+ direction: active?.key === key && active.direction === "asc" ? "desc" : "asc"
1725
+ };
1726
+ if (sort === void 0) setInnerSort(next);
1727
+ onSortChange?.(next);
1728
+ };
1729
+ const setSelected = (ids) => {
1730
+ if (selectedIds === void 0) setInnerSel(ids);
1731
+ onSelectedIdsChange?.(ids);
1732
+ };
1733
+ const android = isAndroidIdiom(theme);
1734
+ const labels = useLabels("paywall", DEFAULT_LABELS, labelsProp);
1735
+ const pay = usePayLabels(labelsProp);
1736
+ const downgrade = prorationWay(proration) === "downgrade";
1737
+ const row = {
1738
+ size,
1739
+ unit,
1740
+ loading,
1741
+ density: density === "compact" ? "compact" : "comfortable",
1742
+ orientation: align === "start" ? "vertical" : "horizontal",
1743
+ align: "start"
1744
+ };
1745
+ const rows = [
1746
+ { id: "unused", label: labels.unusedTime(fromName, daysLeft, cycleDays), minor: -proration.credit, tone: "good" },
1747
+ { id: "same", label: labels.sameDays(toName, daysLeft), minor: proration.charge },
1748
+ {
1749
+ id: "total",
1750
+ label: downgrade ? labels.creditOnNextInvoice : labels.chargedToday,
1751
+ minor: downgrade ? proration.refund : proration.dueNow,
1752
+ tone: error ? "danger" : tone ?? (downgrade ? "good" : void 0),
1753
+ strong: true,
1754
+ rule: true
1755
+ },
1756
+ ...nextMinor == null ? [] : [{ id: "next", label: labels.fromDate(effectiveLabel, nextCadenceLabel ?? labels.monthly), minor: nextMinor }]
1757
+ ];
1758
+ const shown = active ? [...rows].sort(
1759
+ (x, y) => (active.direction === "asc" ? 1 : -1) * (active.key === "amount" ? x.minor - y.minor : x.label.localeCompare(y.label))
1760
+ ) : rows;
1761
+ const nothing = empty != null && proration.credit === 0 && proration.charge === 0 && proration.dueNow === 0 && proration.refund === 0;
1762
+ return /* @__PURE__ */ jsxs(
1763
+ View,
1764
+ {
1765
+ accessibilityState: { busy: loading },
1766
+ style: [
1767
+ styles.prorationTable,
1768
+ density === "compact" ? styles.prorationTableCompact : null,
1769
+ style
1770
+ ],
1771
+ children: [
1772
+ /* @__PURE__ */ jsx(Text, { style: [styles.overline, resolveTextRole(theme, "overline", android)], children: caption ?? labels.midCycleChange(currency) }),
1773
+ nothing ? /* @__PURE__ */ jsx(Text, { style: styles.prorationNote, children: empty }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1774
+ sortable ? /* @__PURE__ */ jsx(View, { style: styles.prorationSortRow, children: ["label", "amount"].map((key) => {
1775
+ const name = key === "label" ? labels.itemColumn : labels.amountColumn;
1776
+ return /* @__PURE__ */ jsx(
1777
+ Pressable,
1778
+ {
1779
+ accessibilityRole: "button",
1780
+ accessibilityLabel: labels.sortBy(name),
1781
+ accessibilityState: { selected: active?.key === key },
1782
+ onPress: () => toggleSort(key),
1783
+ style: styles.prorationSortButton,
1784
+ children: /* @__PURE__ */ jsxs(Text, { style: [styles.overline, resolveTextRole(theme, "overline", android)], children: [
1785
+ name,
1786
+ active?.key === key ? active.direction === "asc" ? " \u25B2" : " \u25BC" : ""
1787
+ ] })
1788
+ },
1789
+ key
1790
+ );
1791
+ }) }) : null,
1792
+ shown.map((r) => /* @__PURE__ */ jsxs(
1793
+ View,
1794
+ {
1795
+ testID: `proration-row-${r.id}`,
1796
+ style: selectable ? styles.prorationSelectRow : null,
1797
+ children: [
1798
+ selectable ? /* @__PURE__ */ jsx(
1799
+ Checkbox,
1800
+ {
1801
+ size: "sm",
1802
+ checked: selected.includes(r.id),
1803
+ onCheckedChange: (on) => setSelected(on ? [...selected, r.id] : selected.filter((x) => x !== r.id)),
1804
+ accessibilityLabel: labels.selectRow(r.label)
1805
+ }
1806
+ ) : null,
1807
+ /* @__PURE__ */ jsx(View, { style: selectable ? styles.prorationSelectBody : null, children: /* @__PURE__ */ jsx(
1808
+ PriceRow,
1809
+ {
1810
+ label: r.label,
1811
+ minor: r.minor,
1812
+ currency,
1813
+ tone: r.tone,
1814
+ strong: r.strong,
1815
+ rule: r.rule,
1816
+ noSymbol: true,
1817
+ locale,
1818
+ ...row,
1819
+ size: r.strong ? void 0 : row.size
1820
+ }
1821
+ ) })
1822
+ ]
1823
+ },
1824
+ r.id
1825
+ ))
1826
+ ] }),
1827
+ error ? /* @__PURE__ */ jsx(Text, { role: "alert", accessibilityRole: "alert", style: styles.prorationError, children: error }) : null,
1828
+ note === false || error ? null : /* @__PURE__ */ jsx(Text, { style: styles.prorationNote, children: note === void 0 ? pay.prorationNote : note })
1829
+ ]
1830
+ }
1831
+ );
1832
+ }
1833
+ function toPaywallPlan(plan, currency, cadence, savingMinor, isCurrent, currentLabel, locale, labels, pay) {
1834
+ const words = planCta(plan, pay).price;
1835
+ const perMonth = yearlyMonthly(plan);
1836
+ const yearly = cadence === "yr" && plan.yearly != null ? plan.yearly : null;
1837
+ const headline = yearly != null && perMonth != null ? perMonth : plan.price;
1838
+ const saving = savingMinor ?? yearlySaving(plan);
1839
+ const notes = [];
1840
+ if (yearly != null)
1841
+ notes.push(`${money(yearly, currency, { locale })} ${labels.billedYearlyTail}`);
1842
+ if (yearly != null && saving != null) {
1843
+ notes.push(`${labels.save} ${money(saving, currency, { locale })} ${labels.perYear}`);
1844
+ }
1845
+ notes.push(plan.blurb);
1846
+ if (isCurrent) notes.push(currentLabel);
1847
+ return {
1848
+ id: plan.id,
1849
+ name: plan.name,
1850
+ price: words || (headline == null ? "" : money(headline, currency, { locale })),
1851
+ period: words ? "" : pay.perSeat,
1852
+ note: notes.join(" \xB7 "),
1853
+ recommended: plan.popular ?? false,
1854
+ recommendedLabel: pay.popularBadge
1855
+ };
1856
+ }
1857
+ function PaywallSurface({
1858
+ variant = "hard",
1859
+ title,
1860
+ subtitle,
1861
+ meter,
1862
+ plans,
1863
+ currency,
1864
+ cadence,
1865
+ onCadenceChange,
1866
+ onPickPlan,
1867
+ currentPlanId,
1868
+ pendingPlanId,
1869
+ currentLabel: currentLabelProp,
1870
+ savings,
1871
+ hideIds,
1872
+ featureOffer,
1873
+ billing = "ours",
1874
+ platformNote: platformNoteProp,
1875
+ legalText,
1876
+ footNote,
1877
+ dismissLabel: dismissLabelProp,
1878
+ onDismiss,
1879
+ restoreLabel: restoreLabelProp,
1880
+ onRestore,
1881
+ upgradeLabel,
1882
+ onUpgrade,
1883
+ locale: localeProp,
1884
+ labels: labelsProp,
1885
+ style,
1886
+ loading = false,
1887
+ error
1888
+ }) {
1889
+ const { styles, theme } = useStyles(sheet6);
1890
+ const android = isAndroidIdiom(theme);
1891
+ const labels = useLabels("paywall", DEFAULT_LABELS, labelsProp);
1892
+ const pay = usePayLabels(labelsProp);
1893
+ const providerLocale = useLoamLocale();
1894
+ const locale = localeProp ?? providerLocale;
1895
+ const currentLabel = currentLabelProp ?? labels.yourPlan;
1896
+ const platformNote = platformNoteProp ?? labels.platformNote;
1897
+ const dismissLabel = dismissLabelProp ?? labels.notNow;
1898
+ const restoreLabel = restoreLabelProp ?? labels.restorePurchases;
1899
+ if (process.env.NODE_ENV !== "production" && billing === "platform" && !onRestore) {
1900
+ console.warn(
1901
+ '[loam] <PaywallSurface billing="platform"> has no `onRestore`. Restore purchases is a required, always-visible action on every platform-billed paywall \u2014 without it a user who has already paid cannot get back in.'
1902
+ );
1903
+ }
1904
+ const canDismiss = variant !== "hard" && Boolean(onDismiss);
1905
+ const showMeter = (variant === "metered" || variant === "trial") && meter != null;
1906
+ const offer = variant === "feature" ? featureOffer : void 0;
1907
+ const shown = React7.useMemo(() => {
1908
+ const list = plans ?? [];
1909
+ return hideIds?.length ? list.filter((p) => !hideIds.includes(p.id)) : list;
1910
+ }, [plans, hideIds]);
1911
+ const [selected, setSelected] = React7.useState(
1912
+ () => shown.find((p) => p.popular)?.id ?? shown[0]?.id
1913
+ );
1914
+ const selectedPlan = shown.find((p) => p.id === selected);
1915
+ const footer = /* @__PURE__ */ jsx(View, { style: styles.footerNotes, children: footNote ? /* @__PURE__ */ jsx(Text, { style: styles.footNote, children: footNote }) : null });
1916
+ const slot = /* @__PURE__ */ jsxs(View, { style: styles.slot, children: [
1917
+ billing === "platform" ? /* @__PURE__ */ jsx(Text, { style: styles.platformNote, children: platformNote }) : null,
1918
+ showMeter && meter ? /* @__PURE__ */ jsx(MeterBar, { labels: labelsProp, ...meter }) : null,
1919
+ onCadenceChange ? /* @__PURE__ */ jsx(
1920
+ BillingPeriodToggle,
1921
+ {
1922
+ value: cadence,
1923
+ onValueChange: onCadenceChange,
1924
+ loading,
1925
+ locale,
1926
+ labels: labelsProp
1927
+ }
1928
+ ) : null
1929
+ ] });
1930
+ if (offer) {
1931
+ return /* @__PURE__ */ jsxs(
1932
+ View,
1933
+ {
1934
+ role: "region",
1935
+ accessibilityLabel: title,
1936
+ accessibilityState: { busy: loading },
1937
+ style: [styles.featureSurface, style],
1938
+ children: [
1939
+ /* @__PURE__ */ jsxs(View, { style: styles.featureHead, children: [
1940
+ /* @__PURE__ */ jsx(
1941
+ Text,
1942
+ {
1943
+ accessibilityRole: "header",
1944
+ style: [styles.featureTitle, resolveTextRole(theme, "surfaceTitle", android)],
1945
+ children: title
1946
+ }
1947
+ ),
1948
+ subtitle ? /* @__PURE__ */ jsx(Text, { style: styles.featureSubtitle, children: subtitle }) : null
1949
+ ] }),
1950
+ billing === "platform" ? /* @__PURE__ */ jsx(Text, { style: styles.platformNote, children: platformNote }) : null,
1951
+ /* @__PURE__ */ jsx(
1952
+ PlanCard,
1953
+ {
1954
+ plan: offer.plan,
1955
+ currency,
1956
+ cadence,
1957
+ savingMinor: offer.savingMinor,
1958
+ highlighted: true,
1959
+ ctaLabel: upgradeLabel,
1960
+ loading: loading || pendingPlanId === offer.plan.id,
1961
+ error,
1962
+ locked: loading || pendingPlanId != null,
1963
+ onSelect: () => {
1964
+ onUpgrade?.();
1965
+ onPickPlan?.(offer.plan.id);
1966
+ },
1967
+ locale,
1968
+ labels: labelsProp,
1969
+ currentLabel: currentLabelProp
1970
+ }
1971
+ ),
1972
+ offer.proration && offer.effectiveLabel ? /* @__PURE__ */ jsx(
1973
+ ProrationLine,
1974
+ {
1975
+ proration: offer.proration,
1976
+ currency,
1977
+ effectiveLabel: offer.effectiveLabel,
1978
+ nextMinor: offer.nextMinor,
1979
+ locale,
1980
+ labels: labelsProp
1981
+ }
1982
+ ) : null,
1983
+ /* @__PURE__ */ jsx(Text, { style: styles.legal, children: legalText }),
1984
+ footNote ? /* @__PURE__ */ jsx(Text, { style: styles.footNote, children: footNote }) : null,
1985
+ /* @__PURE__ */ jsxs(View, { style: styles.featureActions, children: [
1986
+ onRestore ? /* @__PURE__ */ jsx(
1987
+ Pressable,
1988
+ {
1989
+ accessibilityRole: "button",
1990
+ accessibilityLabel: restoreLabel,
1991
+ onPress: onRestore,
1992
+ children: /* @__PURE__ */ jsx(Text, { style: styles.link, children: restoreLabel })
1993
+ }
1994
+ ) : null,
1995
+ canDismiss ? /* @__PURE__ */ jsx(
1996
+ Pressable,
1997
+ {
1998
+ accessibilityRole: "button",
1999
+ accessibilityLabel: dismissLabel,
2000
+ onPress: onDismiss,
2001
+ children: /* @__PURE__ */ jsx(Text, { style: styles.link, children: dismissLabel })
2002
+ }
2003
+ ) : null
2004
+ ] })
2005
+ ]
2006
+ }
2007
+ );
2008
+ }
2009
+ return /* @__PURE__ */ jsx(View, { accessibilityState: { busy: loading }, style: [styles.surface, style], children: /* @__PURE__ */ jsx(
2010
+ Paywall,
2011
+ {
2012
+ error,
2013
+ title,
2014
+ description: subtitle,
2015
+ meter: slot,
2016
+ plans: shown.map(
2017
+ (plan) => toPaywallPlan(
2018
+ plan,
2019
+ currency,
2020
+ cadence,
2021
+ savings?.[plan.id],
2022
+ currentPlanId === plan.id,
2023
+ currentLabel,
2024
+ locale,
2025
+ labels,
2026
+ pay
2027
+ )
2028
+ ),
2029
+ selectedPlanId: selected,
2030
+ onSelectPlan: setSelected,
2031
+ ctaLabel: selectedPlan ? planCta(selectedPlan, pay).cta : restoreLabel,
2032
+ busy: loading || pendingPlanId != null,
2033
+ onPurchase: (id) => onPickPlan?.(id),
2034
+ legalText,
2035
+ footNote: footer,
2036
+ restoreLabel,
2037
+ onRestore,
2038
+ onClose: canDismiss ? onDismiss : void 0,
2039
+ closeLabel: dismissLabel,
2040
+ labels: labelsProp
2041
+ }
2042
+ ) });
2043
+ }
2044
+ var sheet6 = createStyleSheet((theme) => ({
2045
+ surface: { flex: 1 },
2046
+ slot: { gap: theme.space.s3 },
2047
+ platformNote: {
2048
+ fontFamily: theme.font.sans,
2049
+ fontSize: theme.type.footnote.fontSize,
2050
+ lineHeight: theme.type.footnote.fontSize * theme.lineHeight.normal,
2051
+ color: theme.color.text2,
2052
+ textAlign: "center"
2053
+ },
2054
+ footerNotes: { gap: theme.space.s1 },
2055
+ // Legal copy is footnote 13 in `text2` (F60), never caption 11.
2056
+ footNote: {
2057
+ fontFamily: theme.font.sans,
2058
+ fontSize: theme.type.footnote.fontSize,
2059
+ lineHeight: theme.type.footnote.lineHeight,
2060
+ color: theme.color.text2,
2061
+ textAlign: "center"
2062
+ },
2063
+ legal: {
2064
+ fontFamily: theme.font.sans,
2065
+ fontSize: theme.type.footnote.fontSize,
2066
+ lineHeight: theme.type.footnote.lineHeight,
2067
+ color: theme.color.text2,
2068
+ textAlign: "center"
2069
+ },
2070
+ link: {
2071
+ fontFamily: theme.font.sans,
2072
+ fontSize: theme.type.footnote.fontSize,
2073
+ lineHeight: theme.type.footnote.lineHeight,
2074
+ color: theme.color.text2
2075
+ },
2076
+ featureSurface: {
2077
+ alignSelf: "stretch",
2078
+ gap: theme.space.s4,
2079
+ padding: theme.space.s5,
2080
+ borderRadius: theme.radius.lg,
2081
+ borderWidth: theme.borderWidth.base,
2082
+ borderColor: theme.color.border,
2083
+ backgroundColor: theme.color.surface
2084
+ },
2085
+ featureHead: { alignItems: "center", gap: theme.space.s2 },
2086
+ // F117 / Refinement 38: the surface title in the native face.
2087
+ featureTitle: {
2088
+ fontFamily: theme.font.sans,
2089
+ color: theme.color.text,
2090
+ textAlign: "center"
2091
+ },
2092
+ featureSubtitle: {
2093
+ fontFamily: theme.font.sans,
2094
+ fontSize: theme.type.body.fontSize,
2095
+ lineHeight: theme.type.body.fontSize * theme.lineHeight.normal,
2096
+ color: theme.color.text2,
2097
+ textAlign: "center"
2098
+ },
2099
+ featureActions: {
2100
+ flexDirection: "row",
2101
+ flexWrap: "wrap",
2102
+ alignItems: "center",
2103
+ justifyContent: "center",
2104
+ gap: theme.space.s4
2105
+ },
2106
+ prorationLine: {
2107
+ flexDirection: "row",
2108
+ flexWrap: "wrap",
2109
+ alignItems: "baseline",
2110
+ columnGap: theme.space.s1,
2111
+ rowGap: theme.space[2]
2112
+ },
2113
+ prorationLineCompact: { columnGap: theme.space[2], rowGap: 0 },
2114
+ prorationLineStack: { flexDirection: "column", flexWrap: "nowrap", alignItems: "flex-start" },
2115
+ prorationLineRow: {
2116
+ flexDirection: "row",
2117
+ flexWrap: "wrap",
2118
+ alignItems: "baseline",
2119
+ columnGap: theme.space.s1
2120
+ },
2121
+ prorationWord: {
2122
+ fontFamily: theme.font.sans,
2123
+ fontSize: theme.type.footnote.fontSize,
2124
+ lineHeight: theme.type.footnote.lineHeight,
2125
+ color: theme.color.text2
2126
+ },
2127
+ prorationSep: {
2128
+ fontFamily: theme.font.sans,
2129
+ fontSize: theme.type.footnote.fontSize,
2130
+ lineHeight: theme.type.footnote.lineHeight,
2131
+ color: theme.color.text3
2132
+ },
2133
+ prorationTable: {
2134
+ alignSelf: "stretch",
2135
+ gap: theme.space.s2,
2136
+ padding: theme.space.s4,
2137
+ borderRadius: theme.radius.md,
2138
+ borderWidth: theme.borderWidth.base,
2139
+ borderColor: theme.color.border,
2140
+ backgroundColor: theme.color.surface2
2141
+ },
2142
+ prorationTableCompact: { gap: theme.space.s1, padding: theme.space.s3 },
2143
+ // The sans overline role (Refinement 36), not bold caption 2 caps.
2144
+ overline: {
2145
+ fontFamily: theme.font.sans,
2146
+ color: theme.color.text2
2147
+ },
2148
+ prorationSortRow: {
2149
+ flexDirection: "row",
2150
+ justifyContent: "space-between"
2151
+ },
2152
+ prorationSortButton: {
2153
+ minHeight: theme.tapMin,
2154
+ justifyContent: "center"
2155
+ },
2156
+ prorationSelectRow: {
2157
+ flexDirection: "row",
2158
+ alignItems: "center",
2159
+ gap: theme.space.s2
2160
+ },
2161
+ prorationSelectBody: { flex: 1 },
2162
+ prorationNote: {
2163
+ fontFamily: theme.font.sans,
2164
+ fontSize: theme.type.caption1.fontSize,
2165
+ lineHeight: theme.type.caption1.fontSize * theme.lineHeight.normal,
2166
+ color: theme.color.text3
2167
+ },
2168
+ prorationError: {
2169
+ fontFamily: theme.font.sans,
2170
+ fontSize: theme.type.caption1.fontSize,
2171
+ lineHeight: theme.type.caption1.fontSize * theme.lineHeight.normal,
2172
+ color: theme.color.danger
2173
+ }
2174
+ }));
2175
+ var STATUS = {
2176
+ active: { variant: "good", label: "statusActive" },
2177
+ trial: { variant: "info", label: "statusTrial" },
2178
+ canceled: { variant: "neutral", label: "statusCanceled" },
2179
+ "past-due": { variant: "warn", label: "statusPastDue" }
2180
+ };
2181
+ function ManageSubscription({
2182
+ planName,
2183
+ status = "active",
2184
+ statusLabel,
2185
+ price,
2186
+ renewalDate,
2187
+ renewalLabel: renewalLabelProp,
2188
+ paymentMethod,
2189
+ paymentLabel: paymentLabelProp,
2190
+ actions = [],
2191
+ onAction,
2192
+ cancelLabel: cancelLabelProp,
2193
+ onCancel,
2194
+ cancelNote,
2195
+ cancelBusy = false,
2196
+ labels: labelsProp,
2197
+ platform,
2198
+ density = "standard",
2199
+ size = "lg",
2200
+ error,
2201
+ disabled = false
2202
+ }) {
2203
+ const os = useInputPlatform(platform);
2204
+ const { styles } = useStyles(sheet7, { os });
2205
+ const labels = useLabels("manageSubscription", MANAGE_SUBSCRIPTION_LABELS, labelsProp);
2206
+ const renewalLabel = renewalLabelProp ?? labels.renews;
2207
+ const paymentLabel = paymentLabelProp ?? labels.payment;
2208
+ const cancelLabel = cancelLabelProp ?? labels.cancel;
2209
+ const theme = useLoamTheme("commerce");
2210
+ const badge = STATUS[status];
2211
+ return /* @__PURE__ */ jsxs(
2212
+ ScrollView,
2213
+ {
2214
+ style: styles.screen,
2215
+ contentContainerStyle: [
2216
+ styles.scroll,
2217
+ density === "compact" ? styles.scrollCompact : null,
2218
+ density === "comfortable" ? styles.scrollComfortable : null
2219
+ ],
2220
+ children: [
2221
+ /* @__PURE__ */ jsxs(View, { style: styles.card, children: [
2222
+ /* @__PURE__ */ jsxs(View, { style: styles.cardHead, children: [
2223
+ /* @__PURE__ */ jsx(Text, { style: styles.planName, children: planName }),
2224
+ /* @__PURE__ */ jsx(Badge, { style: styles.rowBadge, variant: badge.variant, size: "sm", children: statusLabel ?? labels[badge.label] })
2225
+ ] }),
2226
+ /* @__PURE__ */ jsx(Text, { style: styles.meta, children: labels.renewalLine(price, renewalLabel, renewalDate) }),
2227
+ paymentMethod ? /* @__PURE__ */ jsx(Text, { style: styles.meta, children: labels.paymentLine(paymentLabel, paymentMethod) }) : null
2228
+ ] }),
2229
+ actions.length > 0 ? /* @__PURE__ */ jsx(List, { children: actions.map((a) => /* @__PURE__ */ jsx(
2230
+ ListRow,
2231
+ {
2232
+ platform: os,
2233
+ title: a.label,
2234
+ subtitle: a.value,
2235
+ leading: a.icon ? /* @__PURE__ */ jsx(Icon, { name: a.icon, size: theme.icon.md, color: theme.color.text2 }) : null,
2236
+ trailing: /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: theme.icon.sm, color: theme.color.text3 }),
2237
+ disabled,
2238
+ onPress: () => onAction?.(a.id)
2239
+ },
2240
+ a.id
2241
+ )) }) : null,
2242
+ onCancel ? /* @__PURE__ */ jsxs(Fragment, { children: [
2243
+ error ? /* @__PURE__ */ jsx(Text, { role: "alert", accessibilityRole: "alert", style: styles.error, children: error }) : null,
2244
+ /* @__PURE__ */ jsx(
2245
+ Pressable,
2246
+ {
2247
+ accessibilityRole: "button",
2248
+ accessibilityLabel: cancelLabel,
2249
+ accessibilityState: { busy: cancelBusy, disabled },
2250
+ disabled,
2251
+ onPress: cancelBusy ? void 0 : onCancel,
2252
+ style: ({ pressed }) => [
2253
+ styles.cancel,
2254
+ size === "sm" ? styles.cancelSm : null,
2255
+ size === "md" ? styles.cancelMd : null,
2256
+ disabled ? styles.cancelDisabled : null,
2257
+ pressed ? styles.cancelPressed : null
2258
+ ],
2259
+ children: /* @__PURE__ */ jsx(Text, { style: styles.cancelLabel, children: cancelLabel })
2260
+ }
2261
+ ),
2262
+ cancelNote ? /* @__PURE__ */ jsx(Text, { style: styles.cancelNote, children: cancelNote }) : null
2263
+ ] }) : null
2264
+ ]
2265
+ }
2266
+ );
2267
+ }
2268
+ var sheet7 = createStyleSheet((theme) => ({
2269
+ screen: { flex: 1, backgroundColor: theme.color.bg },
2270
+ scroll: { padding: theme.layout.screenPad, gap: theme.space.s3 },
2271
+ card: {
2272
+ padding: theme.space.s4,
2273
+ borderRadius: theme.radius.lg,
2274
+ backgroundColor: theme.color.surface,
2275
+ borderWidth: theme.borderWidth.base,
2276
+ borderColor: theme.color.border,
2277
+ gap: theme.space.s2
2278
+ },
2279
+ cardHead: { flexDirection: "row", alignItems: "center", gap: theme.space.s2 },
2280
+ planName: {
2281
+ flexShrink: 1,
2282
+ fontFamily: theme.font.sans,
2283
+ fontWeight: theme.type.headline.fontWeight,
2284
+ fontSize: theme.type.headline.fontSize,
2285
+ lineHeight: theme.type.headline.lineHeight,
2286
+ letterSpacing: theme.type.headline.letterSpacing,
2287
+ color: theme.color.text
2288
+ },
2289
+ meta: {
2290
+ fontFamily: theme.font.sans,
2291
+ fontSize: theme.type.footnote.fontSize,
2292
+ lineHeight: theme.type.footnote.lineHeight,
2293
+ color: theme.color.text2
2294
+ },
2295
+ // The lg tier (44), r 12, the outline in the tone (Refinement 38).
2296
+ cancel: {
2297
+ minHeight: theme.ctl.lg,
2298
+ alignItems: "center",
2299
+ justifyContent: "center",
2300
+ borderRadius: theme.radius.md,
2301
+ // Static `w2`: the destructive outline is the drawing, not chrome.
2302
+ borderWidth: theme.borderWidth.thick,
2303
+ borderColor: theme.color.danger,
2304
+ backgroundColor: "transparent"
2305
+ },
2306
+ cancelSm: { minHeight: theme.ctl.sm },
2307
+ cancelMd: { minHeight: theme.ctl.md },
2308
+ cancelDisabled: { opacity: theme.state.disabledOpacity },
2309
+ scrollCompact: { gap: theme.space.s2 },
2310
+ scrollComfortable: { gap: theme.space.s5 },
2311
+ error: {
2312
+ fontFamily: theme.font.sans,
2313
+ fontSize: theme.type.footnote.fontSize,
2314
+ lineHeight: theme.type.footnote.lineHeight,
2315
+ color: theme.color.danger
2316
+ },
2317
+ cancelPressed: { backgroundColor: theme.state.pressed },
2318
+ // Text on a transparent surface, so the tone itself — not `dangerInk`, which
2319
+ // belongs on a solid danger fill.
2320
+ // The Button lg label: iOS 17 / 600, Android 14 / 500.
2321
+ cancelLabel: {
2322
+ fontFamily: theme.font.sans,
2323
+ color: theme.color.danger,
2324
+ variants: {
2325
+ os: {
2326
+ ios: { fontWeight: theme.fontWeight.semibold, fontSize: theme.fontSize[17] },
2327
+ android: { fontWeight: theme.fontWeight.medium, fontSize: theme.fontSize[14] }
2328
+ }
2329
+ }
2330
+ },
2331
+ // Legal-weight copy: footnote 13 in `text2` (F60).
2332
+ cancelNote: {
2333
+ fontFamily: theme.font.sans,
2334
+ fontSize: theme.type.footnote.fontSize,
2335
+ lineHeight: theme.type.footnote.lineHeight,
2336
+ color: theme.color.text2
2337
+ },
2338
+ // Badge hugs with `flex-start`, which pins it to the top of this centred row.
2339
+ rowBadge: { alignSelf: "center" }
2340
+ }));
2341
+ function ToggleRow({
2342
+ label,
2343
+ description,
2344
+ value,
2345
+ onValueChange,
2346
+ leading,
2347
+ disabled = false,
2348
+ error = false,
2349
+ size,
2350
+ tone = "default",
2351
+ density = "standard",
2352
+ trailing,
2353
+ variant = "plain",
2354
+ style,
2355
+ accessibilityLabel,
2356
+ labels: labelsProp,
2357
+ platform
2358
+ }) {
2359
+ const os = useInputPlatform(platform);
2360
+ const { styles } = useStyles(sheet8, { os });
2361
+ const height = styles[`${os}${description ? "Two" : "One"}`];
2362
+ const labels = useLabels("toggleRow", TOGGLE_ROW_LABELS, labelsProp);
2363
+ const announced = accessibilityLabel ?? (description ? labels.withDescription(label, description) : label);
2364
+ return /* @__PURE__ */ jsxs(
2365
+ Pressable,
2366
+ {
2367
+ accessibilityRole: "switch",
2368
+ accessibilityLabel: announced,
2369
+ accessibilityState: { checked: value, disabled },
2370
+ "aria-checked": value,
2371
+ "aria-invalid": error ? true : void 0,
2372
+ disabled,
2373
+ onPress: disabled ? void 0 : () => onValueChange?.(!value),
2374
+ style: ({ pressed }) => [
2375
+ styles.row,
2376
+ height,
2377
+ density === "compact" ? styles.compact : density === "comfortable" ? styles.comfortable : null,
2378
+ variant === "card" ? styles.card : null,
2379
+ disabled ? styles.disabled : null,
2380
+ pressed && !disabled ? styles.pressed : null,
2381
+ style
2382
+ ],
2383
+ children: [
2384
+ leading != null ? /* @__PURE__ */ jsx(View, { "aria-hidden": true, style: styles.leading, children: leading }) : null,
2385
+ /* @__PURE__ */ jsxs(View, { style: styles.main, children: [
2386
+ /* @__PURE__ */ jsx(Text, { style: [styles.label, tone !== "default" ? styles[`tone_${tone}`] : null], children: label }),
2387
+ description ? /* @__PURE__ */ jsx(Text, { style: styles.description, children: description }) : null,
2388
+ typeof error === "string" && error ? /* @__PURE__ */ jsx(FieldMessage, { invalid: true, children: error }) : null
2389
+ ] }),
2390
+ trailing != null ? /* @__PURE__ */ jsx(View, { "aria-hidden": true, style: styles.trailing, children: trailing }) : null,
2391
+ /* @__PURE__ */ jsx(View, { "aria-hidden": true, style: styles.glyph, children: /* @__PURE__ */ jsx(
2392
+ Switch,
2393
+ {
2394
+ value,
2395
+ disabled,
2396
+ decorative: true,
2397
+ platform: os,
2398
+ ...size ? { size } : null,
2399
+ ...error ? { failed: true } : null
2400
+ }
2401
+ ) })
2402
+ ]
2403
+ }
2404
+ );
2405
+ }
2406
+ var sheet8 = createStyleSheet((theme) => ({
2407
+ // Refinement 38: the OS list row. One line 44 / 56, two lines 60 / 72; the
2408
+ // label is the body role at 400, never a bold headline (F59).
2409
+ row: {
2410
+ flexDirection: "row",
2411
+ alignItems: "center",
2412
+ paddingHorizontal: theme.space.s4,
2413
+ paddingVertical: theme.space.s2,
2414
+ backgroundColor: "transparent",
2415
+ variants: {
2416
+ os: { ios: { gap: theme.space.s3 }, android: { gap: theme.space.s4 } }
2417
+ }
2418
+ },
2419
+ iosOne: { minHeight: theme.ctl.lg },
2420
+ iosTwo: { minHeight: theme.space[60] },
2421
+ androidOne: { minHeight: theme.space[56] },
2422
+ androidTwo: { minHeight: theme.space[72] },
2423
+ pressed: { backgroundColor: theme.state.pressed },
2424
+ disabled: { opacity: theme.state.disabledOpacity },
2425
+ main: { flex: 1, minWidth: 0, gap: theme.space[2] },
2426
+ label: {
2427
+ fontFamily: theme.font.sans,
2428
+ color: theme.color.text,
2429
+ variants: {
2430
+ os: {
2431
+ ios: {
2432
+ fontWeight: theme.type.body.fontWeight,
2433
+ fontSize: theme.type.body.fontSize,
2434
+ lineHeight: theme.type.body.lineHeight,
2435
+ letterSpacing: theme.type.body.letterSpacing
2436
+ },
2437
+ android: {
2438
+ fontWeight: theme.fontWeight.regular,
2439
+ fontSize: theme.fontSize[16],
2440
+ lineHeight: theme.space[24],
2441
+ letterSpacing: theme.fontSize[16] * 0.03125
2442
+ }
2443
+ }
2444
+ }
2445
+ },
2446
+ // The second line: iOS subheadline 15, Android body-medium 14, in `text2`.
2447
+ description: {
2448
+ fontFamily: theme.font.sans,
2449
+ color: theme.color.text2,
2450
+ variants: {
2451
+ os: {
2452
+ ios: {
2453
+ fontSize: theme.type.subheadline.fontSize,
2454
+ lineHeight: theme.type.subheadline.lineHeight,
2455
+ letterSpacing: theme.type.subheadline.letterSpacing
2456
+ },
2457
+ android: {
2458
+ fontSize: theme.fontSize[14],
2459
+ lineHeight: theme.space[20],
2460
+ letterSpacing: theme.fontSize[14] * 0.0179
2461
+ }
2462
+ }
2463
+ }
2464
+ },
2465
+ // `pointerEvents` in the style object, not as a prop: the prop form is
2466
+ // deprecated in RN 0.76.
2467
+ leading: { flexShrink: 0, alignItems: "center", justifyContent: "center" },
2468
+ glyph: { flexShrink: 0, pointerEvents: "none" },
2469
+ trailing: { flexShrink: 0, alignItems: "center", justifyContent: "center" },
2470
+ compact: { paddingVertical: theme.space.s1 },
2471
+ comfortable: { paddingVertical: theme.space.s4 },
2472
+ card: {
2473
+ backgroundColor: theme.color.surface,
2474
+ borderRadius: theme.radius.md,
2475
+ borderWidth: theme.borderWidth.base,
2476
+ borderColor: theme.color.border
2477
+ },
2478
+ tone_accent: { color: theme.color.accentStrong },
2479
+ tone_good: { color: theme.color.goodOnSoft },
2480
+ tone_warn: { color: theme.color.warnOnSoft },
2481
+ tone_danger: { color: theme.color.danger }
2482
+ }));
2483
+ function SettingsScreen({
2484
+ groups,
2485
+ onRowPress,
2486
+ onToggle,
2487
+ onSelect,
2488
+ save = "immediate",
2489
+ title,
2490
+ dirty = false,
2491
+ saving = false,
2492
+ onSave,
2493
+ onCancel,
2494
+ platform: platformProp,
2495
+ labels: labelsProp,
2496
+ density = "standard",
2497
+ size,
2498
+ error,
2499
+ disabled: disabledAll = false
2500
+ }) {
2501
+ const os = useInputPlatform(platformProp);
2502
+ const { styles } = useStyles(sheet9, { os });
2503
+ const theme = useLoamTheme("commerce");
2504
+ const labels = useLabels("settingsScreen", SETTINGS_SCREEN_LABELS, labelsProp);
2505
+ const [picking, setPicking] = React7.useState(
2506
+ null
2507
+ );
2508
+ const canSave = dirty && !saving;
2509
+ const choose = (row, next) => {
2510
+ setPicking(null);
2511
+ if (next === row.value) return;
2512
+ row.onValueChange?.(next);
2513
+ onSelect?.(row.id, next);
2514
+ };
2515
+ const renderRow = (row) => {
2516
+ if (row.kind === "switch") {
2517
+ return /* @__PURE__ */ jsx(
2518
+ ToggleRow,
2519
+ {
2520
+ label: row.label,
2521
+ description: row.description,
2522
+ value: row.value,
2523
+ disabled: row.disabled || disabledAll,
2524
+ density,
2525
+ ...size ? { size } : null,
2526
+ platform: os,
2527
+ onValueChange: (next) => {
2528
+ row.onValueChange?.(next);
2529
+ onToggle?.(row.id, next);
2530
+ }
2531
+ },
2532
+ row.id
2533
+ );
2534
+ }
2535
+ if (row.kind === "value") {
2536
+ return /* @__PURE__ */ jsxs(
2537
+ View,
2538
+ {
2539
+ style: [styles.row, os === "android" ? styles.rowTwoLine : null],
2540
+ accessible: true,
2541
+ accessibilityLabel: `${row.label}, ${row.value}`,
2542
+ children: [
2543
+ /* @__PURE__ */ jsxs(View, { style: styles.main, children: [
2544
+ /* @__PURE__ */ jsx(Text, { style: styles.label, children: row.label }),
2545
+ os === "android" ? /* @__PURE__ */ jsx(Text, { style: styles.second, children: row.value }) : null
2546
+ ] }),
2547
+ os === "ios" ? /* @__PURE__ */ jsx(Text, { style: styles.value, children: row.value }) : null
2548
+ ]
2549
+ },
2550
+ row.id
2551
+ );
2552
+ }
2553
+ const isSelect = row.kind === "select" || row.kind === "segmented";
2554
+ const display = isSelect ? row.options.find((o) => o.value === row.value)?.label ?? row.value : row.kind === "nav" ? row.value : void 0;
2555
+ const disabled = (isSelect ? Boolean(row.disabled) : false) || disabledAll;
2556
+ let onPress;
2557
+ if (row.kind === "select" || row.kind === "segmented") {
2558
+ const picked = row;
2559
+ onPress = () => setPicking(picked);
2560
+ } else {
2561
+ const pressed = row;
2562
+ onPress = () => {
2563
+ pressed.onPress?.();
2564
+ onRowPress?.(pressed.id);
2565
+ };
2566
+ }
2567
+ return /* @__PURE__ */ jsxs(
2568
+ Pressable,
2569
+ {
2570
+ accessibilityRole: "button",
2571
+ accessibilityLabel: isSelect && display ? labels.selectRow(row.label, display) : row.label,
2572
+ accessibilityState: { disabled },
2573
+ disabled,
2574
+ onPress: disabled ? void 0 : onPress,
2575
+ style: ({ pressed }) => [
2576
+ styles.row,
2577
+ os === "android" && display ? styles.rowTwoLine : null,
2578
+ pressed && !disabled ? styles.pressed : null,
2579
+ disabled ? styles.disabled : null
2580
+ ],
2581
+ children: [
2582
+ row.kind === "nav" && row.icon ? /* @__PURE__ */ jsx(Icon, { name: row.icon, size: theme.icon.md, color: theme.color.text2 }) : null,
2583
+ /* @__PURE__ */ jsxs(View, { style: styles.main, children: [
2584
+ /* @__PURE__ */ jsx(Text, { style: row.kind === "danger" ? styles.dangerLabel : styles.label, children: row.label }),
2585
+ os === "android" && display ? /* @__PURE__ */ jsx(Text, { style: styles.second, children: display }) : null
2586
+ ] }),
2587
+ os === "ios" && row.kind !== "danger" ? /* @__PURE__ */ jsxs(View, { style: styles.trailing, children: [
2588
+ display ? /* @__PURE__ */ jsx(Text, { style: styles.value, children: display }) : null,
2589
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-right", size: theme.icon.sm, color: theme.color.text3 })
2590
+ ] }) : null
2591
+ ]
2592
+ },
2593
+ row.id
2594
+ );
2595
+ };
2596
+ const saveBar = save === "explicit" ? /* @__PURE__ */ jsxs(View, { style: styles.bar, accessibilityRole: "header", children: [
2597
+ os === "ios" ? /* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", onPress: onCancel, style: styles.barSide, children: /* @__PURE__ */ jsx(Text, { style: styles.barAction, children: labels.cancel }) }) : /* @__PURE__ */ jsx(
2598
+ Pressable,
2599
+ {
2600
+ accessibilityRole: "button",
2601
+ accessibilityLabel: labels.close,
2602
+ onPress: onCancel,
2603
+ style: styles.barIcon,
2604
+ children: /* @__PURE__ */ jsx(Icon, { name: "x", size: theme.icon.md, color: theme.color.text })
2605
+ }
2606
+ ),
2607
+ /* @__PURE__ */ jsx(Text, { style: styles.barTitle, numberOfLines: 1, children: title }),
2608
+ /* @__PURE__ */ jsx(
2609
+ Pressable,
2610
+ {
2611
+ accessibilityRole: "button",
2612
+ accessibilityState: { disabled: !canSave, busy: saving },
2613
+ "aria-disabled": !canSave,
2614
+ disabled: !canSave,
2615
+ onPress: canSave ? onSave : void 0,
2616
+ style: [styles.barSide, styles.barEnd],
2617
+ children: /* @__PURE__ */ jsx(Text, { style: [styles.barAction, styles.barSave, !canSave ? styles.barOff : null], children: saving ? labels.saving : labels.save })
2618
+ }
2619
+ )
2620
+ ] }) : null;
2621
+ return /* @__PURE__ */ jsxs(View, { style: styles.screen, children: [
2622
+ saveBar,
2623
+ /* @__PURE__ */ jsxs(
2624
+ ScrollView,
2625
+ {
2626
+ style: styles.screen,
2627
+ contentContainerStyle: [
2628
+ styles.scroll,
2629
+ density === "compact" ? styles.scrollCompact : density === "comfortable" ? styles.scrollComfortable : null
2630
+ ],
2631
+ children: [
2632
+ error ? /* @__PURE__ */ jsx(Text, { accessibilityRole: "alert", style: styles.error, children: error }) : null,
2633
+ groups.map((group) => /* @__PURE__ */ jsxs(View, { style: styles.group, children: [
2634
+ group.title ? /* @__PURE__ */ jsx(Text, { style: styles.caption, accessibilityRole: "header", children: group.title }) : null,
2635
+ /* @__PURE__ */ jsx(View, { style: styles.card, children: group.rows.map((row, i) => /* @__PURE__ */ jsxs(React7.Fragment, { children: [
2636
+ i > 0 && os === "ios" ? /* @__PURE__ */ jsx(
2637
+ View,
2638
+ {
2639
+ "aria-hidden": true,
2640
+ accessibilityElementsHidden: true,
2641
+ importantForAccessibility: "no-hide-descendants",
2642
+ style: styles.divider
2643
+ }
2644
+ ) : null,
2645
+ renderRow(row)
2646
+ ] }, row.id)) }),
2647
+ group.footnote ? /* @__PURE__ */ jsx(Text, { style: styles.footnote, children: group.footnote }) : null
2648
+ ] }, group.id))
2649
+ ]
2650
+ }
2651
+ ),
2652
+ picking ? /* @__PURE__ */ jsx(
2653
+ Dialog,
2654
+ {
2655
+ open: true,
2656
+ platform: os,
2657
+ title: picking.label,
2658
+ onOpenChange: (open) => {
2659
+ if (!open) setPicking(null);
2660
+ },
2661
+ children: /* @__PURE__ */ jsx(
2662
+ RadioGroup,
2663
+ {
2664
+ platform: os,
2665
+ accessibilityLabel: picking.label,
2666
+ value: picking.value,
2667
+ onValueChange: (next) => choose(picking, next),
2668
+ children: picking.options.map((o) => /* @__PURE__ */ jsx(RadioGroupItem, { value: o.value, label: o.label }, o.value))
2669
+ }
2670
+ )
2671
+ }
2672
+ ) : null
2673
+ ] });
2674
+ }
2675
+ var sheet9 = createStyleSheet((theme) => ({
2676
+ screen: { flex: 1, backgroundColor: theme.color.bg },
2677
+ scroll: {
2678
+ paddingBottom: theme.space.s6,
2679
+ variants: {
2680
+ os: {
2681
+ ios: { paddingHorizontal: theme.space.s4, paddingTop: theme.space.s5, gap: theme.space.s6 },
2682
+ android: { gap: theme.space.s2 }
2683
+ }
2684
+ }
2685
+ },
2686
+ scrollCompact: {
2687
+ variants: { os: { ios: { gap: theme.space.s3 }, android: { gap: theme.space.s1 } } }
2688
+ },
2689
+ scrollComfortable: {
2690
+ variants: { os: { ios: { gap: theme.space.s8 }, android: { gap: theme.space.s5 } } }
2691
+ },
2692
+ error: {
2693
+ paddingHorizontal: theme.space.s4,
2694
+ fontFamily: theme.font.sans,
2695
+ fontSize: theme.type.footnote.fontSize,
2696
+ color: theme.color.danger
2697
+ },
2698
+ group: { variants: { os: { ios: { gap: theme.space.s2 }, android: {} } } },
2699
+ // Group header: iOS 13 uppercase `text2`, Android 14 / 500 `accentStrong`.
2700
+ caption: {
2701
+ fontFamily: theme.font.sans,
2702
+ variants: {
2703
+ os: {
2704
+ ios: {
2705
+ paddingHorizontal: theme.space.s4,
2706
+ fontSize: theme.type.footnote.fontSize,
2707
+ lineHeight: theme.type.footnote.lineHeight,
2708
+ letterSpacing: theme.type.footnote.letterSpacing,
2709
+ textTransform: "uppercase",
2710
+ color: theme.color.text2
2711
+ },
2712
+ android: {
2713
+ paddingHorizontal: theme.space.s4,
2714
+ paddingTop: theme.space.s4,
2715
+ paddingBottom: theme.space.s2,
2716
+ fontWeight: theme.fontWeight.medium,
2717
+ fontSize: theme.fontSize[14],
2718
+ lineHeight: theme.space[20],
2719
+ color: theme.color.accentStrong
2720
+ }
2721
+ }
2722
+ }
2723
+ },
2724
+ // iOS: an inset r 12 card on `surface`. Android: the list sits on the page.
2725
+ card: {
2726
+ variants: {
2727
+ os: {
2728
+ ios: {
2729
+ backgroundColor: theme.color.surface,
2730
+ borderRadius: theme.radius.md,
2731
+ overflow: "hidden"
2732
+ },
2733
+ android: {}
2734
+ }
2735
+ }
2736
+ },
2737
+ divider: {
2738
+ height: theme.borderWidth.hair,
2739
+ marginStart: theme.space.s4,
2740
+ backgroundColor: theme.color.border
2741
+ },
2742
+ row: {
2743
+ flexDirection: "row",
2744
+ alignItems: "center",
2745
+ paddingHorizontal: theme.space.s4,
2746
+ backgroundColor: "transparent",
2747
+ variants: {
2748
+ os: {
2749
+ ios: { minHeight: theme.ctl.lg, gap: theme.space.s3, paddingVertical: theme.space.s2 },
2750
+ android: {
2751
+ minHeight: theme.space[56],
2752
+ gap: theme.space.s4,
2753
+ paddingVertical: theme.space.s2
2754
+ }
2755
+ }
2756
+ }
2757
+ },
2758
+ // Android: a row with the value as a second line takes the two-line tier (72).
2759
+ rowTwoLine: { minHeight: theme.space[72] },
2760
+ pressed: { backgroundColor: theme.state.pressed },
2761
+ disabled: { opacity: theme.state.disabledOpacity },
2762
+ main: { flex: 1, minWidth: 0, gap: theme.space[2] },
2763
+ // Row title: iOS body 17 / 400; Android body-large 16 / .5.
2764
+ label: {
2765
+ fontFamily: theme.font.sans,
2766
+ color: theme.color.text,
2767
+ variants: {
2768
+ os: {
2769
+ ios: {
2770
+ fontWeight: theme.type.body.fontWeight,
2771
+ fontSize: theme.type.body.fontSize,
2772
+ lineHeight: theme.type.body.lineHeight,
2773
+ letterSpacing: theme.type.body.letterSpacing
2774
+ },
2775
+ android: {
2776
+ fontWeight: theme.fontWeight.regular,
2777
+ fontSize: theme.fontSize[16],
2778
+ lineHeight: theme.space[24],
2779
+ letterSpacing: theme.fontSize[16] * 0.03125
2780
+ }
2781
+ }
2782
+ }
2783
+ },
2784
+ // Android's second line: the value, 14 `text2`.
2785
+ second: {
2786
+ fontFamily: theme.font.sans,
2787
+ fontSize: theme.fontSize[14],
2788
+ lineHeight: theme.space[20],
2789
+ letterSpacing: theme.fontSize[14] * 0.0179,
2790
+ color: theme.color.text2
2791
+ },
2792
+ trailing: { flexDirection: "row", alignItems: "center", gap: theme.space.s1, flexShrink: 1 },
2793
+ // iOS trailing value: body 17 in `text2`.
2794
+ value: {
2795
+ fontFamily: theme.font.sans,
2796
+ fontSize: theme.type.body.fontSize,
2797
+ lineHeight: theme.type.body.lineHeight,
2798
+ letterSpacing: theme.type.body.letterSpacing,
2799
+ color: theme.color.text2
2800
+ },
2801
+ // Destructive rows take `dangerOnSoft` (Refinement 38) — `dangerInk` belongs
2802
+ // on a solid danger fill, not on a list row.
2803
+ dangerLabel: {
2804
+ fontFamily: theme.font.sans,
2805
+ color: theme.color.dangerOnSoft,
2806
+ variants: {
2807
+ os: {
2808
+ ios: {
2809
+ fontSize: theme.type.body.fontSize,
2810
+ lineHeight: theme.type.body.lineHeight,
2811
+ letterSpacing: theme.type.body.letterSpacing
2812
+ },
2813
+ android: {
2814
+ fontSize: theme.fontSize[16],
2815
+ lineHeight: theme.space[24],
2816
+ letterSpacing: theme.fontSize[16] * 0.03125
2817
+ }
2818
+ }
2819
+ }
2820
+ },
2821
+ footnote: {
2822
+ paddingHorizontal: theme.space.s4,
2823
+ fontFamily: theme.font.sans,
2824
+ fontSize: theme.type.footnote.fontSize,
2825
+ lineHeight: theme.type.footnote.lineHeight,
2826
+ color: theme.color.text2,
2827
+ variants: { os: { ios: {}, android: { paddingBottom: theme.space.s2 } } }
2828
+ },
2829
+ /* ---- explicit save bar ---- */
2830
+ bar: {
2831
+ flexDirection: "row",
2832
+ alignItems: "center",
2833
+ backgroundColor: theme.color.bg,
2834
+ variants: {
2835
+ os: {
2836
+ ios: {
2837
+ minHeight: theme.ctl.lg,
2838
+ paddingHorizontal: theme.space.s4,
2839
+ borderBottomWidth: theme.borderWidth.hair,
2840
+ borderBottomColor: theme.color.border
2841
+ },
2842
+ android: {
2843
+ minHeight: theme.space[56],
2844
+ paddingHorizontal: theme.space[4],
2845
+ gap: theme.space[4]
2846
+ }
2847
+ }
2848
+ }
2849
+ },
2850
+ barSide: { minHeight: theme.ctl.lg, justifyContent: "center" },
2851
+ barEnd: { alignItems: "flex-end" },
2852
+ barIcon: {
2853
+ width: theme.space[48],
2854
+ height: theme.space[48],
2855
+ alignItems: "center",
2856
+ justifyContent: "center",
2857
+ borderRadius: theme.radius.full
2858
+ },
2859
+ barTitle: {
2860
+ flex: 1,
2861
+ fontFamily: theme.font.sans,
2862
+ color: theme.color.text,
2863
+ variants: {
2864
+ os: {
2865
+ ios: {
2866
+ textAlign: "center",
2867
+ fontWeight: theme.type.headline.fontWeight,
2868
+ fontSize: theme.type.headline.fontSize,
2869
+ lineHeight: theme.type.headline.lineHeight,
2870
+ letterSpacing: theme.type.headline.letterSpacing
2871
+ },
2872
+ android: {
2873
+ fontSize: theme.fontSize[22],
2874
+ lineHeight: theme.space[28]
2875
+ }
2876
+ }
2877
+ }
2878
+ },
2879
+ // Text actions: iOS body 17 accent (Save 600); Android label-large 14 / 500.
2880
+ barAction: {
2881
+ fontFamily: theme.font.sans,
2882
+ color: theme.color.accentStrong,
2883
+ variants: {
2884
+ os: {
2885
+ ios: {
2886
+ fontSize: theme.type.body.fontSize,
2887
+ lineHeight: theme.type.body.lineHeight,
2888
+ letterSpacing: theme.type.body.letterSpacing
2889
+ },
2890
+ android: {
2891
+ paddingHorizontal: theme.space.s3,
2892
+ fontWeight: theme.fontWeight.medium,
2893
+ fontSize: theme.fontSize[14],
2894
+ lineHeight: theme.space[20],
2895
+ letterSpacing: theme.fontSize[14] * 714e-5
2896
+ }
2897
+ }
2898
+ }
2899
+ },
2900
+ barSave: { variants: { os: { ios: { fontWeight: theme.fontWeight.semibold }, android: {} } } },
2901
+ barOff: { color: theme.color.text3 }
2902
+ }));
2903
+ function NotificationPreferences({
2904
+ systemEnabled = true,
2905
+ systemNote,
2906
+ systemActionLabel: systemActionLabelProp,
2907
+ onOpenSystemSettings,
2908
+ categories,
2909
+ onChange,
2910
+ labels: labelsProp,
2911
+ density = "standard",
2912
+ size,
2913
+ loading = false,
2914
+ error,
2915
+ disabled = false,
2916
+ platform
2917
+ }) {
2918
+ const os = useInputPlatform(platform);
2919
+ const { styles } = useStyles(sheet10, { os });
2920
+ const labels = useLabels("notificationPreferences", NOTIFICATION_PREFERENCES_LABELS, labelsProp);
2921
+ const systemActionLabel = systemActionLabelProp ?? labels.openSettings;
2922
+ const note = systemNote ?? (systemEnabled ? labels.systemOn : labels.systemOff);
2923
+ return /* @__PURE__ */ jsxs(
2924
+ ScrollView,
2925
+ {
2926
+ style: styles.screen,
2927
+ contentContainerStyle: [
2928
+ styles.scroll,
2929
+ density === "compact" ? styles.scrollCompact : density === "comfortable" ? styles.scrollComfortable : null
2930
+ ],
2931
+ "aria-busy": loading || void 0,
2932
+ children: [
2933
+ /* @__PURE__ */ jsxs(
2934
+ View,
2935
+ {
2936
+ accessibilityRole: systemEnabled ? "text" : "alert",
2937
+ style: [styles.banner, systemEnabled ? styles.bannerOn : styles.bannerOff],
2938
+ children: [
2939
+ /* @__PURE__ */ jsx(
2940
+ Text,
2941
+ {
2942
+ style: [styles.bannerText, systemEnabled ? styles.bannerTextOn : styles.bannerTextOff],
2943
+ children: note
2944
+ }
2945
+ ),
2946
+ !systemEnabled && onOpenSystemSettings ? /* @__PURE__ */ jsx(
2947
+ Pressable,
2948
+ {
2949
+ accessibilityRole: "button",
2950
+ accessibilityLabel: systemActionLabel,
2951
+ onPress: onOpenSystemSettings,
2952
+ style: ({ pressed }) => [styles.bannerAction, pressed ? styles.pressed : null],
2953
+ children: /* @__PURE__ */ jsx(Text, { style: styles.bannerActionLabel, children: systemActionLabel })
2954
+ }
2955
+ ) : null
2956
+ ]
2957
+ }
2958
+ ),
2959
+ error ? /* @__PURE__ */ jsx(Text, { accessibilityRole: "alert", style: styles.error, children: error }) : null,
2960
+ loading ? /* @__PURE__ */ jsx(View, { style: styles.card, testID: "notification-loading", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(View, { style: styles.loadingRow, children: /* @__PURE__ */ jsx(Skeleton, { variant: "text" }) }, i)) }) : null,
2961
+ !loading && categories.length > 0 ? /* @__PURE__ */ jsx(View, { style: styles.card, children: categories.map((c, i) => /* @__PURE__ */ jsxs(React7.Fragment, { children: [
2962
+ i > 0 && os === "ios" ? /* @__PURE__ */ jsx(
2963
+ View,
2964
+ {
2965
+ "aria-hidden": true,
2966
+ accessibilityElementsHidden: true,
2967
+ importantForAccessibility: "no-hide-descendants",
2968
+ style: styles.divider
2969
+ }
2970
+ ) : null,
2971
+ /* @__PURE__ */ jsx(
2972
+ ToggleRow,
2973
+ {
2974
+ platform: os,
2975
+ label: c.label,
2976
+ description: c.description,
2977
+ value: c.value,
2978
+ disabled: c.disabled === true || !systemEnabled || disabled,
2979
+ density,
2980
+ ...size ? { size } : null,
2981
+ onValueChange: (next) => onChange?.(c.id, next)
2982
+ }
2983
+ )
2984
+ ] }, c.id)) }) : null
2985
+ ]
2986
+ }
2987
+ );
2988
+ }
2989
+ var sheet10 = createStyleSheet((theme) => ({
2990
+ screen: { flex: 1, backgroundColor: theme.color.bg },
2991
+ scroll: { padding: theme.layout.screenPad, gap: theme.space.s3 },
2992
+ banner: {
2993
+ padding: theme.space.s4,
2994
+ borderRadius: theme.radius.lg,
2995
+ borderWidth: theme.borderWidth.base,
2996
+ gap: theme.space.s3,
2997
+ alignItems: "flex-start"
2998
+ },
2999
+ bannerOn: { backgroundColor: theme.color.accentSoft, borderColor: theme.edge.accent },
3000
+ bannerOff: { backgroundColor: theme.color.warnSoft, borderColor: theme.edge.warn },
3001
+ bannerText: {
3002
+ fontFamily: theme.font.sans,
3003
+ fontSize: theme.type.footnote.fontSize,
3004
+ lineHeight: theme.type.footnote.fontSize * theme.lineHeight.normal
3005
+ },
3006
+ // Text on a `-soft` tint is a surface case: the tone itself, never the on-ink.
3007
+ bannerTextOn: { color: theme.color.accentOnSoft },
3008
+ bannerTextOff: { color: theme.color.warnOnSoft },
3009
+ bannerAction: {
3010
+ minHeight: theme.tapMin,
3011
+ justifyContent: "center",
3012
+ paddingHorizontal: theme.space.s3,
3013
+ borderRadius: theme.radius.md,
3014
+ backgroundColor: theme.color.warn
3015
+ },
3016
+ // Solid `warn` fill, so the measured ink for that fill.
3017
+ bannerActionLabel: {
3018
+ fontFamily: theme.font.sans,
3019
+ fontWeight: theme.fontWeight.bold,
3020
+ fontSize: theme.type.footnote.fontSize,
3021
+ color: theme.color.warnInk
3022
+ },
3023
+ scrollCompact: { gap: theme.space.s1 },
3024
+ scrollComfortable: { gap: theme.space.s5 },
3025
+ error: {
3026
+ fontFamily: theme.font.sans,
3027
+ fontSize: theme.type.footnote.fontSize,
3028
+ color: theme.color.danger
3029
+ },
3030
+ loadingRow: { padding: theme.space.s4 },
3031
+ pressed: { opacity: theme.state.pressedOpacity },
3032
+ card: {
3033
+ variants: {
3034
+ os: {
3035
+ ios: {
3036
+ backgroundColor: theme.color.surface,
3037
+ borderRadius: theme.radius.md,
3038
+ overflow: "hidden"
3039
+ },
3040
+ android: {}
3041
+ }
3042
+ }
3043
+ },
3044
+ divider: {
3045
+ height: theme.borderWidth.hair,
3046
+ marginStart: theme.space.s4,
3047
+ backgroundColor: theme.color.border
3048
+ }
3049
+ }));
3050
+
3051
+ export { BillingPeriodToggle, DeleteAccountScreen, ManageSubscription, MeterBar, NotificationPreferences, Paywall, PaywallGate, PaywallSurface, PlanCard, PlanFeatureRow, PlanGrid, PlanPriceCell, PlanPriceDelta, ProrationLine, ProrationTable, SavingsChip, SettingsScreen, ToggleRow };