@funnelsgrove/cli 0.1.14 → 0.1.18

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 (293) hide show
  1. package/contracts/step-contract-v2.json +733 -0
  2. package/dist/analyticsOutput.d.ts +30 -15
  3. package/dist/analyticsOutput.js +45 -28
  4. package/dist/apiClient.d.ts +85 -0
  5. package/dist/apiClient.js +225 -8
  6. package/dist/cli.d.ts +149 -1
  7. package/dist/cli.js +1232 -71
  8. package/dist/diagnosticOutput.d.ts +42 -0
  9. package/dist/diagnosticOutput.js +83 -0
  10. package/dist/docsOutput.d.ts +19 -0
  11. package/dist/docsOutput.js +73 -0
  12. package/dist/docsTransaction.d.ts +81 -0
  13. package/dist/docsTransaction.js +1755 -0
  14. package/dist/funnelValidation.d.ts +14 -0
  15. package/dist/funnelValidation.js +297 -0
  16. package/dist/localSync.d.ts +79 -0
  17. package/dist/localSync.js +428 -53
  18. package/dist/projectValidator.d.ts +27 -0
  19. package/dist/projectValidator.js +208 -0
  20. package/dist/reskin.js +27 -1
  21. package/dist/stepContractMigration.d.ts +71 -0
  22. package/dist/stepContractMigration.js +1323 -0
  23. package/dist/templateDocs.js +8 -25
  24. package/docs-release-history.json +66 -0
  25. package/legacy-docs-catalog.json +1012 -0
  26. package/package.json +13 -4
  27. package/template_docs/.funnelsgrove-docs.json +144 -0
  28. package/template_docs/AGENTS.md +30 -58
  29. package/template_docs/CLAUDE.md +0 -2
  30. package/template_docs/docs/funnelsgrove/START-HERE.md +73 -0
  31. package/template_docs/docs/funnelsgrove/contracts/analytics-events.md +59 -0
  32. package/template_docs/docs/funnelsgrove/contracts/content-answers.md +44 -0
  33. package/template_docs/docs/funnelsgrove/contracts/flow-routing.md +41 -0
  34. package/template_docs/docs/funnelsgrove/contracts/payments.md +42 -0
  35. package/template_docs/docs/funnelsgrove/contracts/step-metadata.md +57 -0
  36. package/template_docs/docs/funnelsgrove/migrations/step-contract-v3.md +39 -0
  37. package/template_docs/docs/funnelsgrove/qa/analytics.md +43 -0
  38. package/template_docs/docs/funnelsgrove/qa/local.md +42 -0
  39. package/template_docs/docs/funnelsgrove/qa/paywall-checkout.md +55 -0
  40. package/template_docs/docs/funnelsgrove/qa/publish.md +32 -0
  41. package/template_docs/docs/funnelsgrove/recipes/add-experiment.md +34 -0
  42. package/template_docs/docs/funnelsgrove/recipes/add-step.md +33 -0
  43. package/template_docs/docs/funnelsgrove/recipes/edit-step.md +31 -0
  44. package/template_docs/docs/funnelsgrove/steps/INDEX.md +26 -0
  45. package/template_docs/docs/funnelsgrove/steps/cancellation_offer.md +112 -0
  46. package/template_docs/docs/funnelsgrove/steps/checkout.md +120 -0
  47. package/template_docs/docs/funnelsgrove/steps/form_input.md +115 -0
  48. package/template_docs/docs/funnelsgrove/steps/intro_hero.md +110 -0
  49. package/template_docs/docs/funnelsgrove/steps/multi_select_choice.md +123 -0
  50. package/template_docs/docs/funnelsgrove/steps/paywall_offer.md +115 -0
  51. package/template_docs/docs/funnelsgrove/steps/progress_interstitial.md +111 -0
  52. package/template_docs/docs/funnelsgrove/steps/purchase_completed.md +117 -0
  53. package/template_docs/docs/funnelsgrove/steps/single_step_choice.md +122 -0
  54. package/template_docs/docs/funnelsgrove/steps/single_step_choice_emoji.md +122 -0
  55. package/template_docs/docs/funnelsgrove/steps/social_proof.md +110 -0
  56. package/template_docs/docs/funnelsgrove/steps/subscription_handoff.md +113 -0
  57. package/template_docs/docs/funnelsgrove/steps/subscription_management.md +112 -0
  58. package/template_docs/docs/funnelsgrove/steps/summary_confirmation.md +110 -0
  59. package/template_docs/docs/funnelsgrove/steps/upsell_offer.md +112 -0
  60. package/template_docs/docs/funnelsgrove/steps/value_prop_story.md +110 -0
  61. package/template_docs/funnel-docs.config.json +43 -0
  62. package/template_scaffold/.env.example +41 -0
  63. package/template_scaffold/.funnelsgrove-contract-tools.json +19 -0
  64. package/template_scaffold/.funnelsgrove-docs.json +144 -0
  65. package/template_scaffold/.funnelsgrove-scaffold.json +1078 -0
  66. package/template_scaffold/AGENTS.md +42 -0
  67. package/template_scaffold/CLAUDE.md +1 -0
  68. package/template_scaffold/PLAN.md +81 -0
  69. package/template_scaffold/PRODUCT_SENSE.md +28 -0
  70. package/template_scaffold/context/build-context.md +34 -0
  71. package/template_scaffold/docs/AGENT_FUNNEL_IMPLEMENTATION_SPEC.md +5 -0
  72. package/template_scaffold/docs/ARCHITECTURE.md +5 -0
  73. package/template_scaffold/docs/FLOW_CONFIG_AND_ROUTING.md +5 -0
  74. package/template_scaffold/docs/PAYWALL_AND_PAYMENTS.md +5 -0
  75. package/template_scaffold/docs/README.md +5 -0
  76. package/template_scaffold/docs/funnelsgrove/START-HERE.md +73 -0
  77. package/template_scaffold/docs/funnelsgrove/contracts/analytics-events.md +59 -0
  78. package/template_scaffold/docs/funnelsgrove/contracts/content-answers.md +44 -0
  79. package/template_scaffold/docs/funnelsgrove/contracts/flow-routing.md +41 -0
  80. package/template_scaffold/docs/funnelsgrove/contracts/payments.md +42 -0
  81. package/template_scaffold/docs/funnelsgrove/contracts/step-metadata.md +57 -0
  82. package/template_scaffold/docs/funnelsgrove/migrations/step-contract-v3.md +39 -0
  83. package/template_scaffold/docs/funnelsgrove/qa/analytics.md +43 -0
  84. package/template_scaffold/docs/funnelsgrove/qa/local.md +42 -0
  85. package/template_scaffold/docs/funnelsgrove/qa/paywall-checkout.md +55 -0
  86. package/template_scaffold/docs/funnelsgrove/qa/publish.md +32 -0
  87. package/template_scaffold/docs/funnelsgrove/recipes/add-experiment.md +34 -0
  88. package/template_scaffold/docs/funnelsgrove/recipes/add-step.md +33 -0
  89. package/template_scaffold/docs/funnelsgrove/recipes/edit-step.md +31 -0
  90. package/template_scaffold/docs/funnelsgrove/steps/INDEX.md +26 -0
  91. package/template_scaffold/docs/funnelsgrove/steps/cancellation_offer.md +112 -0
  92. package/template_scaffold/docs/funnelsgrove/steps/checkout.md +120 -0
  93. package/template_scaffold/docs/funnelsgrove/steps/form_input.md +115 -0
  94. package/template_scaffold/docs/funnelsgrove/steps/intro_hero.md +110 -0
  95. package/template_scaffold/docs/funnelsgrove/steps/multi_select_choice.md +123 -0
  96. package/template_scaffold/docs/funnelsgrove/steps/paywall_offer.md +115 -0
  97. package/template_scaffold/docs/funnelsgrove/steps/progress_interstitial.md +111 -0
  98. package/template_scaffold/docs/funnelsgrove/steps/purchase_completed.md +117 -0
  99. package/template_scaffold/docs/funnelsgrove/steps/single_step_choice.md +122 -0
  100. package/template_scaffold/docs/funnelsgrove/steps/single_step_choice_emoji.md +122 -0
  101. package/template_scaffold/docs/funnelsgrove/steps/social_proof.md +110 -0
  102. package/template_scaffold/docs/funnelsgrove/steps/subscription_handoff.md +113 -0
  103. package/template_scaffold/docs/funnelsgrove/steps/subscription_management.md +112 -0
  104. package/template_scaffold/docs/funnelsgrove/steps/summary_confirmation.md +110 -0
  105. package/template_scaffold/docs/funnelsgrove/steps/upsell_offer.md +112 -0
  106. package/template_scaffold/docs/funnelsgrove/steps/value_prop_story.md +110 -0
  107. package/template_scaffold/docs/references/funnel-sdk-user-answers.md +5 -0
  108. package/template_scaffold/eslint.config.mjs +16 -0
  109. package/template_scaffold/funnel-agent-docs.test.ts +699 -0
  110. package/template_scaffold/funnel-docs.config.json +43 -0
  111. package/template_scaffold/funnel.config.json +5 -0
  112. package/template_scaffold/gitignore.template +5 -0
  113. package/template_scaffold/next-env.d.ts +6 -0
  114. package/template_scaffold/next.config.ts +53 -0
  115. package/template_scaffold/package-lock.json +7737 -0
  116. package/template_scaffold/package.json +37 -0
  117. package/template_scaffold/package.test.ts +23 -0
  118. package/template_scaffold/public/claimbee/bee.png +0 -0
  119. package/template_scaffold/public/claimbee/device-iphone.png +0 -0
  120. package/template_scaffold/public/claimbee/device-macbook.png +0 -0
  121. package/template_scaffold/public/claimbee/device-none.png +0 -0
  122. package/template_scaffold/public/claimbee/device-watch.png +0 -0
  123. package/template_scaffold/public/claimbee/glow.svg +3 -0
  124. package/template_scaffold/public/claimbee/steps-sprite-alt.png +0 -0
  125. package/template_scaffold/public/claimbee/steps-sprite-default.png +0 -0
  126. package/template_scaffold/public/claimbee/testimonial-avatar.png +0 -0
  127. package/template_scaffold/public/claimbee/testimonial-star.svg +3 -0
  128. package/template_scaffold/public/paywall/ai-claim-assistant.png +0 -0
  129. package/template_scaffold/public/paywall/claim-history.png +0 -0
  130. package/template_scaffold/public/paywall/clear-progress.png +0 -0
  131. package/template_scaffold/public/paywall/hero.png +0 -0
  132. package/template_scaffold/public/paywall/money-alerts.png +0 -0
  133. package/template_scaffold/public/paywall/money-back-badge.png +0 -0
  134. package/template_scaffold/public/paywall/paywall-image.png +0 -0
  135. package/template_scaffold/public/paywall/r1.png +0 -0
  136. package/template_scaffold/public/paywall/r2.png +0 -0
  137. package/template_scaffold/public/paywall/r3.png +0 -0
  138. package/template_scaffold/public/paywall/r4.png +0 -0
  139. package/template_scaffold/public/paywall/r5.png +0 -0
  140. package/template_scaffold/public/paywall/r6.png +0 -0
  141. package/template_scaffold/public/paywall/special-offer-gift.png +0 -0
  142. package/template_scaffold/public/please/bee.png +0 -0
  143. package/template_scaffold/public/please/device-iphone.png +0 -0
  144. package/template_scaffold/public/please/device-macbook.png +0 -0
  145. package/template_scaffold/public/please/device-none.png +0 -0
  146. package/template_scaffold/public/please/device-watch.png +0 -0
  147. package/template_scaffold/public/please/glow.svg +3 -0
  148. package/template_scaffold/public/please/steps-sprite-alt.png +0 -0
  149. package/template_scaffold/public/please/steps-sprite-default.png +0 -0
  150. package/template_scaffold/public/please/testimonial-avatar.png +0 -0
  151. package/template_scaffold/public/please/testimonial-star.svg +3 -0
  152. package/template_scaffold/public/upsell/upgrade-hero.png +0 -0
  153. package/template_scaffold/src/app/[stepId]/page.test.tsx +59 -0
  154. package/template_scaffold/src/app/[stepId]/page.tsx +62 -0
  155. package/template_scaffold/src/app/globals.css +49 -0
  156. package/template_scaffold/src/app/layout.test.ts +26 -0
  157. package/template_scaffold/src/app/layout.tsx +61 -0
  158. package/template_scaffold/src/app/page.tsx +8 -0
  159. package/template_scaffold/src/app/sdk/page.tsx +673 -0
  160. package/template_scaffold/src/app/subscription-dashboard/page.tsx +274 -0
  161. package/template_scaffold/src/assets/claimbee/bee.png +0 -0
  162. package/template_scaffold/src/assets/claimbee/device-iphone.png +0 -0
  163. package/template_scaffold/src/assets/claimbee/device-macbook.png +0 -0
  164. package/template_scaffold/src/assets/claimbee/device-none.png +0 -0
  165. package/template_scaffold/src/assets/claimbee/device-watch.png +0 -0
  166. package/template_scaffold/src/assets/claimbee/glow.svg +3 -0
  167. package/template_scaffold/src/assets/claimbee/steps-sprite-alt.png +0 -0
  168. package/template_scaffold/src/assets/claimbee/steps-sprite-default.png +0 -0
  169. package/template_scaffold/src/assets/claimbee/testimonial-avatar.png +0 -0
  170. package/template_scaffold/src/assets/claimbee/testimonial-star.svg +3 -0
  171. package/template_scaffold/src/components/FunnelEditorPanel.tsx +194 -0
  172. package/template_scaffold/src/components/FunnelFlow.test.tsx +17 -0
  173. package/template_scaffold/src/components/FunnelFlow.tsx +114 -0
  174. package/template_scaffold/src/components/FunnelStepImage.test.ts +15 -0
  175. package/template_scaffold/src/components/FunnelStepImage.tsx +28 -0
  176. package/template_scaffold/src/components/step/FunnelStepHeader.tsx +35 -0
  177. package/template_scaffold/src/config/billing.plans.test.ts +143 -0
  178. package/template_scaffold/src/config/billing.plans.ts +69 -0
  179. package/template_scaffold/src/config/billing.test.plans.ts +32 -0
  180. package/template_scaffold/src/config/experiments.generated.ts +4 -0
  181. package/template_scaffold/src/config/experiments.test.ts +12 -0
  182. package/template_scaffold/src/config/experiments.ts +9 -0
  183. package/template_scaffold/src/config/funnel.config.ts +12 -0
  184. package/template_scaffold/src/config/funnel.manifest.test.ts +166 -0
  185. package/template_scaffold/src/config/funnel.manifest.ts +139 -0
  186. package/template_scaffold/src/config/offer-sets.generated.ts +4 -0
  187. package/template_scaffold/src/contract/funnel-project.validation.test.ts +80 -0
  188. package/template_scaffold/src/contract/funnel-project.validation.ts +44 -0
  189. package/template_scaffold/src/contract/funnel-validator.vite.config.ts +11 -0
  190. package/template_scaffold/src/contract/golden/cancellation_offer.fixture.ts +29 -0
  191. package/template_scaffold/src/contract/golden/checkout.fixture.ts +37 -0
  192. package/template_scaffold/src/contract/golden/form_input.fixture.ts +28 -0
  193. package/template_scaffold/src/contract/golden/golden-fixtures.test.ts +190 -0
  194. package/template_scaffold/src/contract/golden/intro_hero.fixture.ts +27 -0
  195. package/template_scaffold/src/contract/golden/multi_select_choice.fixture.ts +37 -0
  196. package/template_scaffold/src/contract/golden/paywall_offer.fixture.ts +30 -0
  197. package/template_scaffold/src/contract/golden/progress_interstitial.fixture.ts +28 -0
  198. package/template_scaffold/src/contract/golden/purchase_completed.fixture.ts +33 -0
  199. package/template_scaffold/src/contract/golden/single_step_choice.fixture.ts +36 -0
  200. package/template_scaffold/src/contract/golden/single_step_choice_emoji.fixture.ts +36 -0
  201. package/template_scaffold/src/contract/golden/social_proof.fixture.ts +27 -0
  202. package/template_scaffold/src/contract/golden/subscription_handoff.fixture.ts +30 -0
  203. package/template_scaffold/src/contract/golden/subscription_management.fixture.ts +29 -0
  204. package/template_scaffold/src/contract/golden/summary_confirmation.fixture.ts +27 -0
  205. package/template_scaffold/src/contract/golden/upsell_offer.fixture.ts +29 -0
  206. package/template_scaffold/src/contract/golden/value_prop_story.fixture.ts +27 -0
  207. package/template_scaffold/src/contract/validate-funnel.cli.test.ts +180 -0
  208. package/template_scaffold/src/contract/validate-funnel.cli.ts +199 -0
  209. package/template_scaffold/src/runtime/checkout-runtime-config.test.ts +60 -0
  210. package/template_scaffold/src/runtime/checkout-runtime-config.ts +26 -0
  211. package/template_scaffold/src/runtime/funnel-runtime.test.ts +25 -0
  212. package/template_scaffold/src/runtime/funnel-runtime.ts +118 -0
  213. package/template_scaffold/src/runtime/step-content-context.test.ts +38 -0
  214. package/template_scaffold/src/runtime/step-content-context.ts +65 -0
  215. package/template_scaffold/src/runtime/step-registry.ts +66 -0
  216. package/template_scaffold/src/runtime/step-variable-values.ts +31 -0
  217. package/template_scaffold/src/runtime/use-funnel-flow-controller.integration.test.ts +203 -0
  218. package/template_scaffold/src/runtime/use-funnel-flow-controller.test.ts +76 -0
  219. package/template_scaffold/src/runtime/use-funnel-flow-controller.ts +53 -0
  220. package/template_scaffold/src/steps/content/email-capture.content.ts +29 -0
  221. package/template_scaffold/src/steps/content/manage-subscription.content.ts +62 -0
  222. package/template_scaffold/src/steps/content/paywall.content.ts +191 -0
  223. package/template_scaffold/src/steps/content/step-01.content.ts +23 -0
  224. package/template_scaffold/src/steps/content/step-02.content.ts +77 -0
  225. package/template_scaffold/src/steps/content/step-03.content.ts +40 -0
  226. package/template_scaffold/src/steps/content/step-04.content.ts +49 -0
  227. package/template_scaffold/src/steps/content/step-32-paywall.content.ts +352 -0
  228. package/template_scaffold/src/steps/content/subscription-started.content.ts +68 -0
  229. package/template_scaffold/src/steps/editor/email-capture.editor.ts +58 -0
  230. package/template_scaffold/src/steps/editor/manage-subscription.editor.ts +179 -0
  231. package/template_scaffold/src/steps/editor/paywall.editor.ts +232 -0
  232. package/template_scaffold/src/steps/editor/step-01.editor.ts +22 -0
  233. package/template_scaffold/src/steps/editor/step-02.editor.ts +41 -0
  234. package/template_scaffold/src/steps/editor/step-03.editor.ts +58 -0
  235. package/template_scaffold/src/steps/editor/step-04.editor.ts +98 -0
  236. package/template_scaffold/src/steps/editor/step-32-paywall.editor.ts +478 -0
  237. package/template_scaffold/src/steps/editor/subscription-started.editor.ts +131 -0
  238. package/template_scaffold/src/steps/index.ts +25 -0
  239. package/template_scaffold/src/steps/paywall-checkout-options.test.ts +104 -0
  240. package/template_scaffold/src/steps/paywall-checkout-options.ts +130 -0
  241. package/template_scaffold/src/steps/pricing/paywall-b.pricing.ts +12 -0
  242. package/template_scaffold/src/steps/pricing/paywall.pricing.ts +12 -0
  243. package/template_scaffold/src/steps/pricing/shared-plan-order.ts +17 -0
  244. package/template_scaffold/src/steps/pricing/upsell-form.pricing.ts +16 -0
  245. package/template_scaffold/src/steps/shared-header-contract.test.ts +27 -0
  246. package/template_scaffold/src/steps/step-01.test.ts +38 -0
  247. package/template_scaffold/src/steps/step-01.tsx +108 -0
  248. package/template_scaffold/src/steps/step-02.test.ts +43 -0
  249. package/template_scaffold/src/steps/step-02.tsx +140 -0
  250. package/template_scaffold/src/steps/step-03.test.ts +15 -0
  251. package/template_scaffold/src/steps/step-03.tsx +262 -0
  252. package/template_scaffold/src/steps/step-04.test.ts +32 -0
  253. package/template_scaffold/src/steps/step-04.tsx +673 -0
  254. package/template_scaffold/src/steps/step-31-email-capture.test.ts +22 -0
  255. package/template_scaffold/src/steps/step-31-email-capture.tsx +195 -0
  256. package/template_scaffold/src/steps/step-32-paywall.test.ts +770 -0
  257. package/template_scaffold/src/steps/step-32-paywall.tsx +2253 -0
  258. package/template_scaffold/src/steps/step-32b-paywall-b.tsx +35 -0
  259. package/template_scaffold/src/steps/step-33-subscription-started.test.ts +26 -0
  260. package/template_scaffold/src/steps/step-33-subscription-started.tsx +86 -0
  261. package/template_scaffold/src/steps/step-34-upsell-form.test.ts +27 -0
  262. package/template_scaffold/src/steps/step-35-manage-subscription.tsx +46 -0
  263. package/template_scaffold/src/steps/step-content.registry.ts +45 -0
  264. package/template_scaffold/src/steps/step-image-migration.test.ts +20 -0
  265. package/template_scaffold/src/steps/styles/index.css +2 -0
  266. package/template_scaffold/src/steps/styles/shared/final-flow-shared.css +55 -0
  267. package/template_scaffold/src/steps/styles/shared/funnel-base.css +543 -0
  268. package/template_scaffold/src/steps/styles/shared/info-shared.css +139 -0
  269. package/template_scaffold/src/steps/styles/shared/progress-shared.css +117 -0
  270. package/template_scaffold/src/steps/styles/shared/question-shared.css +76 -0
  271. package/template_scaffold/src/steps/styles/shared/responsive-container.css +176 -0
  272. package/template_scaffold/src/steps/types.ts +7 -0
  273. package/template_scaffold/src/steps/use-claimbee-step-content.ts +15 -0
  274. package/template_scaffold/src/theme/theme.ts +81 -0
  275. package/template_scaffold/tsconfig.json +38 -0
  276. package/template_scaffold/vitest.config.ts +32 -0
  277. package/template_validation/.funnelsgrove-contract-tools.json +19 -0
  278. package/template_validation/src/contract/funnel-project.validation.ts +44 -0
  279. package/template_validation/src/contract/funnel-validator.vite.config.ts +11 -0
  280. package/template_validation/src/contract/validate-funnel.cli.ts +199 -0
  281. package/template_docs/docs/ab-experiments.md +0 -55
  282. package/template_docs/docs/analytics.md +0 -65
  283. package/template_docs/docs/editing-flow.md +0 -125
  284. package/template_docs/docs/editing-step.md +0 -125
  285. package/template_docs/docs/editor-and-content.md +0 -58
  286. package/template_docs/docs/funnel-runtime-architecture.md +0 -125
  287. package/template_docs/docs/meta-pixel-conversions-api.md +0 -50
  288. package/template_docs/docs/payment-plans-and-discounts.md +0 -106
  289. package/template_docs/docs/publishing-and-versioning.md +0 -117
  290. package/template_docs/docs/qa-checklist.md +0 -80
  291. package/template_docs/docs/sdk-api-endpoints.md +0 -78
  292. package/template_docs/docs/step-ui-guidelines.md +0 -110
  293. package/template_docs/docs/theme.md +0 -35
@@ -0,0 +1,2253 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useMemo, useRef, useState } from 'react';
4
+ import Image from 'next/image';
5
+ import Link from 'next/link';
6
+ import {
7
+ checkoutRuntimeConfig,
8
+ resolveClaimbeeCheckoutMode,
9
+ } from '@/runtime/checkout-runtime-config';
10
+ import {
11
+ billingDiscountStorageKeys,
12
+ billingDiscounts,
13
+ claimbeePlansByMode,
14
+ } from '@/config/billing.plans';
15
+ import { stepPaywallContent } from '@/steps/content/step-32-paywall.content';
16
+ import { useClaimbeeStepContent } from '@/steps/use-claimbee-step-content';
17
+ import { publicAnalyticsSdk } from '@funnelsgrove/analytics';
18
+ import {
19
+ apiService,
20
+ collectCurrentFunnelAttribution,
21
+ readPaywallStateValue,
22
+ runtimePublicConfig,
23
+ updatePaywallStateValue,
24
+ useFunnel,
25
+ useRuntimeMode,
26
+ type FunnelStepMeta,
27
+ } from '@funnelsgrove/runtime';
28
+ import { getPathForStep } from '@/runtime/funnel-runtime';
29
+ import {
30
+ ApplePaySubscriptionCheckoutSlot,
31
+ GooglePaySubscriptionCheckoutSlot,
32
+ SharedCheckoutSpecialOfferDialog,
33
+ SharedStripeCheckoutV2Dialog,
34
+ activateSecondPaywallDiscount,
35
+ advancePaywallDiscountState,
36
+ buildConfigPaywallPlans,
37
+ buildDiscountedPaywallPlans,
38
+ buildPaywallRenewalDisclaimer,
39
+ getDefaultPlanId,
40
+ getPaywallPromoDisplayName,
41
+ resolvePaywallDiscountState,
42
+ serializePaywallDiscountState,
43
+ usePlatformWalletPaymentMethods,
44
+ useStripeSubscriptionCheckoutSession,
45
+ type PaywallDiscountState,
46
+ type PlatformWalletPaymentMethod,
47
+ } from '@funnelsgrove/payments';
48
+ import { stepSubscriptionStartedId } from '@/steps/step-33-subscription-started';
49
+ import { funnelManifest } from '@/config/funnel.manifest';
50
+
51
+ export const stepPaywallId = 'paywall';
52
+
53
+ export const stepPaywall = {
54
+ id: stepPaywallId,
55
+ name: 'paywall',
56
+ type: 'paywall_offer',
57
+ kind: 'paywall',
58
+ figmaNodeId: '6992-6210',
59
+ title: 'Paywall',
60
+ description: 'ClaimBee paywall rebuilt from Figma node 6992:6210 with a local three-plan catalog and FAQ accordion.',
61
+ actionBar: {
62
+ hidden: true,
63
+ },
64
+ } as const satisfies FunnelStepMeta;
65
+
66
+ type StoryOption = {
67
+ quote: string;
68
+ author: string;
69
+ backgroundColor: string;
70
+ };
71
+
72
+ type PaywallWalletAvailabilityState = {
73
+ intentKey: string;
74
+ methods: Partial<Record<PlatformWalletPaymentMethod, boolean>>;
75
+ };
76
+
77
+ const claimbeeStoryBackgroundColors = ['var(--color-secondary-text)', 'var(--color-primary)', 'var(--color-secondary)'] as const;
78
+ const paywallFeatureTileColorClassNames = [
79
+ 'paywall-v2-feature-title--orange',
80
+ 'paywall-v2-feature-title--orange',
81
+ 'paywall-v2-feature-title--green',
82
+ 'paywall-v2-feature-title--green',
83
+ ] as const;
84
+ const paywallDetailImageDimensions = {
85
+ tracking: {
86
+ width: 172,
87
+ height: 70,
88
+ },
89
+ alerts: {
90
+ width: 127,
91
+ height: 108,
92
+ },
93
+ } as const;
94
+ const defaultSelectedPlan = 'secondary';
95
+ const CLAIMBEE_PAYWALL_SECOND_COUPON_ID = billingDiscounts.second.couponId;
96
+ export const CLAIMBEE_PAYWALL_DISCOUNT_STORAGE_KEYS = billingDiscountStorageKeys;
97
+ const deferPaywallStateSync = (syncState: () => void): (() => void) => {
98
+ const timeoutId = window.setTimeout(syncState, 0);
99
+ return () => window.clearTimeout(timeoutId);
100
+ };
101
+ const checkoutCurrencyFormatter = new Intl.NumberFormat('en-US', {
102
+ style: 'currency',
103
+ currency: 'USD',
104
+ minimumFractionDigits: 2,
105
+ maximumFractionDigits: 2,
106
+ });
107
+
108
+ function formatCheckoutAmount(amountCents: number): string {
109
+ return checkoutCurrencyFormatter.format(amountCents / 100);
110
+ }
111
+
112
+ type StepPaywallProps = {
113
+ analyticsStepId?: string;
114
+ analyticsStepName?: string;
115
+ };
116
+
117
+ export function StepPaywall({
118
+ analyticsStepId = stepPaywallId,
119
+ analyticsStepName = stepPaywall.name || stepPaywall.title,
120
+ }: StepPaywallProps = {}) {
121
+ const [runtimeMode] = useRuntimeMode();
122
+ const checkoutMode = useMemo(
123
+ () => resolveClaimbeeCheckoutMode(runtimeMode),
124
+ [runtimeMode],
125
+ );
126
+ const { user, setUser, attributes, featureFlags, completeStep } = useFunnel();
127
+ const content = useClaimbeeStepContent(stepPaywallId, stepPaywallContent);
128
+ const supportEmail = runtimePublicConfig.supportEmail;
129
+ const walletPaymentMethods = usePlatformWalletPaymentMethods();
130
+ const availablePlans = useMemo(
131
+ () =>
132
+ buildConfigPaywallPlans(
133
+ checkoutMode === 'test' ? claimbeePlansByMode.test : claimbeePlansByMode.live,
134
+ ['primary', 'secondary', 'tertiary'],
135
+ ),
136
+ [checkoutMode],
137
+ );
138
+ const initialPlanId = useMemo(
139
+ () =>
140
+ availablePlans.find((plan) => plan.id === defaultSelectedPlan)?.id ??
141
+ getDefaultPlanId(availablePlans) ??
142
+ availablePlans[0]?.id ??
143
+ null,
144
+ [availablePlans],
145
+ );
146
+ const [selectedPlanId, setSelectedPlanId] = useState<string | null>(
147
+ initialPlanId,
148
+ );
149
+ const [checkoutDialogOpen, setCheckoutDialogOpen] = useState(false);
150
+ const [specialOfferDialogOpen, setSpecialOfferDialogOpen] = useState(() => {
151
+ if (typeof window === 'undefined') {
152
+ return false;
153
+ }
154
+
155
+ const searchParams = new URLSearchParams(window.location.search);
156
+ return searchParams.get('checkout_offer') === 'first';
157
+ });
158
+ const [error, setError] = useState<string | null>(null);
159
+ const [discountStateReady, setDiscountStateReady] = useState(false);
160
+ const [discountState, setDiscountState] = useState<PaywallDiscountState>(() =>
161
+ resolvePaywallDiscountState({
162
+ discounts: billingDiscounts,
163
+ nowMs: Date.now(),
164
+ storedValue: null,
165
+ }),
166
+ );
167
+ const [activeStoryIndex, setActiveStoryIndex] = useState(0);
168
+ const [openFaqIds, setOpenFaqIds] = useState<string[]>([]);
169
+ const [checkoutEmail, setCheckoutEmail] = useState(user.email);
170
+ const [paywallWalletAvailability, setPaywallWalletAvailability] = useState<PaywallWalletAvailabilityState>({
171
+ intentKey: '',
172
+ methods: {},
173
+ });
174
+ const paywallStepRef = useRef<HTMLDivElement | null>(null);
175
+
176
+ const activeCouponId = discountState.couponId;
177
+ const displayPlans = useMemo(
178
+ () =>
179
+ buildDiscountedPaywallPlans({
180
+ plans: availablePlans,
181
+ couponId: activeCouponId,
182
+ discountPercent: discountState.discountPercent,
183
+ }),
184
+ [activeCouponId, availablePlans, discountState.discountPercent],
185
+ );
186
+ const selectedPlan = useMemo(() => {
187
+ if (availablePlans.length === 0) {
188
+ return null;
189
+ }
190
+
191
+ if (!selectedPlanId) {
192
+ return availablePlans.find((plan) => plan.id === initialPlanId) ?? availablePlans[0];
193
+ }
194
+
195
+ return availablePlans.find((plan) => plan.id === selectedPlanId) ?? availablePlans[0];
196
+ }, [availablePlans, initialPlanId, selectedPlanId]);
197
+ const selectedDisplayPlan = useMemo(() => {
198
+ if (displayPlans.length === 0) {
199
+ return null;
200
+ }
201
+
202
+ if (!selectedPlanId) {
203
+ return displayPlans.find((plan) => plan.id === initialPlanId) ?? displayPlans[0] ?? null;
204
+ }
205
+
206
+ return displayPlans.find((plan) => plan.id === selectedPlanId) ?? displayPlans[0];
207
+ }, [displayPlans, initialPlanId, selectedPlanId]);
208
+ const checkoutSummaryLabel =
209
+ selectedDisplayPlan?.checkoutSummaryLabel?.trim() || selectedDisplayPlan?.title || 'Selected plan';
210
+ const storyOptions = useMemo<readonly StoryOption[]>(
211
+ () =>
212
+ content.stories.items.map((item, index) => ({
213
+ quote: item.quote,
214
+ author: item.author,
215
+ backgroundColor:
216
+ claimbeeStoryBackgroundColors[index % claimbeeStoryBackgroundColors.length],
217
+ })),
218
+ [content.stories.items],
219
+ );
220
+ const activeStory = storyOptions[activeStoryIndex] ?? storyOptions[0] ?? null;
221
+ const minutes = Math.floor(discountState.remainingSeconds / 60);
222
+ const seconds = discountState.remainingSeconds % 60;
223
+ const upgradedDiscountActive = activeCouponId === CLAIMBEE_PAYWALL_SECOND_COUPON_ID;
224
+ const activeDiscountStartedAtMs =
225
+ discountState.stage === 'second'
226
+ ? discountState.secondStartedAtMs
227
+ : discountState.firstStartedAtMs;
228
+ const serializedDiscountSnapshot = useMemo(
229
+ () => serializePaywallDiscountState(discountState),
230
+ [discountState],
231
+ );
232
+ const promoDisplayName = useMemo(() => {
233
+ if (
234
+ discountState.status !== 'active' ||
235
+ typeof activeDiscountStartedAtMs !== 'number'
236
+ ) {
237
+ return '';
238
+ }
239
+
240
+ return getPaywallPromoDisplayName(activeDiscountStartedAtMs);
241
+ }, [activeDiscountStartedAtMs, discountState.status]);
242
+ const paymentDisclaimer = useMemo(() => {
243
+ if (!selectedPlan) {
244
+ return '';
245
+ }
246
+
247
+ return buildPaywallRenewalDisclaimer({
248
+ plan: selectedPlan,
249
+ hasActiveDiscount: Boolean(activeCouponId),
250
+ productName: 'ClaimBee',
251
+ });
252
+ }, [activeCouponId, selectedPlan]);
253
+ const appliedDiscountAmountCents = useMemo(() => {
254
+ if (!selectedDisplayPlan) {
255
+ return 0;
256
+ }
257
+
258
+ return Math.max(0, selectedDisplayPlan.baseAmountCents - selectedDisplayPlan.amountCents);
259
+ }, [selectedDisplayPlan]);
260
+ const checkoutDiscountPercent = discountState.discountPercent ?? 0;
261
+ const checkoutPromoActive =
262
+ Boolean(selectedDisplayPlan?.hasActiveDiscount) &&
263
+ discountState.status === 'active' &&
264
+ checkoutDiscountPercent > 0 &&
265
+ appliedDiscountAmountCents > 0;
266
+ const checkoutAnalyticsMetadata = useMemo(() => {
267
+ if (!selectedDisplayPlan) {
268
+ return null;
269
+ }
270
+
271
+ return {
272
+ planId: selectedDisplayPlan.id,
273
+ providerPlanId: selectedDisplayPlan.providerPlanId || null,
274
+ couponId: checkoutPromoActive ? selectedDisplayPlan.couponId : null,
275
+ summaryLabel: checkoutSummaryLabel,
276
+ amountCents: selectedDisplayPlan.amountCents,
277
+ currency: 'USD',
278
+ ...(typeof selectedDisplayPlan.analyticsPurchaseValue === 'number'
279
+ ? { value: selectedDisplayPlan.analyticsPurchaseValue }
280
+ : {}),
281
+ environment: checkoutMode,
282
+ };
283
+ }, [checkoutMode, checkoutPromoActive, checkoutSummaryLabel, selectedDisplayPlan]);
284
+ const checkoutAnalyticsContext = useMemo(() => {
285
+ if (!checkoutAnalyticsMetadata) {
286
+ return null;
287
+ }
288
+
289
+ return {
290
+ environment: checkoutMode,
291
+ featureFlags,
292
+ metadata: checkoutAnalyticsMetadata,
293
+ runtimeConfig: checkoutRuntimeConfig,
294
+ stepId: analyticsStepId,
295
+ stepName: analyticsStepName,
296
+ stepType: stepPaywall.type,
297
+ stepContractVersion: funnelManifest.stepContractVersion,
298
+ };
299
+ }, [analyticsStepId, analyticsStepName, checkoutAnalyticsMetadata, checkoutMode, featureFlags]);
300
+ const successReturnUrl = useMemo(() => {
301
+ if (typeof window === 'undefined') {
302
+ return '';
303
+ }
304
+
305
+ const successUrl = new URL(`${window.location.origin}${getPathForStep(stepSubscriptionStartedId)}`);
306
+ if (user.id) {
307
+ successUrl.searchParams.set('user_id', user.id);
308
+ }
309
+ successUrl.searchParams.set('source', 'stripe-elements');
310
+
311
+ return successUrl.toString();
312
+ }, [user.id]);
313
+ const checkoutSession = useStripeSubscriptionCheckoutSession({
314
+ analyticsMetadata: checkoutAnalyticsMetadata,
315
+ checkoutMode,
316
+ couponId: checkoutPromoActive ? selectedDisplayPlan?.couponId ?? null : null,
317
+ customerEmail: checkoutEmail || user.email,
318
+ displayPlan: selectedDisplayPlan,
319
+ onError: setError,
320
+ plan: selectedPlan,
321
+ returnUrl: successReturnUrl,
322
+ runtimeConfig: checkoutRuntimeConfig,
323
+ userId: user.id,
324
+ });
325
+ const checkoutDisabled = !checkoutSession.configured || !selectedPlan || !selectedDisplayPlan;
326
+ const checkoutDialogReady =
327
+ checkoutDialogOpen &&
328
+ Boolean(checkoutSession.activeClientSecret) &&
329
+ Boolean(checkoutSession.stripePromise) &&
330
+ Boolean(selectedDisplayPlan);
331
+ const currentPaywallWalletAvailability: PaywallWalletAvailabilityState['methods'] =
332
+ paywallWalletAvailability.intentKey === checkoutSession.intentKey
333
+ ? paywallWalletAvailability.methods
334
+ : {};
335
+ const checkoutInitialWalletAvailable = walletPaymentMethods.some(
336
+ (method) => currentPaywallWalletAvailability[method] === true,
337
+ )
338
+ ? true
339
+ : null;
340
+ const checkoutTotalValue = useMemo(() => {
341
+ if (!selectedDisplayPlan) {
342
+ return '';
343
+ }
344
+
345
+ return selectedDisplayPlan.priceLabel.split('/')[0]?.trim() || selectedDisplayPlan.priceLabel;
346
+ }, [selectedDisplayPlan]);
347
+ const checkoutDiscountLabel = checkoutPromoActive
348
+ ? content.checkout.discountRowLabelTemplate.replace(
349
+ '{percent}',
350
+ String(checkoutDiscountPercent),
351
+ )
352
+ : '';
353
+ const checkoutDiscountAmountLabel = checkoutPromoActive
354
+ ? `-${formatCheckoutAmount(appliedDiscountAmountCents)}`
355
+ : '';
356
+ const checkoutSavedLabel = checkoutPromoActive
357
+ ? content.checkout.savedLabelTemplate
358
+ .replace('{amount}', formatCheckoutAmount(appliedDiscountAmountCents))
359
+ .replace('{percent}', String(checkoutDiscountPercent))
360
+ : '';
361
+ useEffect(() => {
362
+ return deferPaywallStateSync(() => {
363
+ setDiscountState(
364
+ resolvePaywallDiscountState({
365
+ discounts: billingDiscounts,
366
+ nowMs: Date.now(),
367
+ storedValue: readPaywallStateValue({
368
+ legacyKeys: CLAIMBEE_PAYWALL_DISCOUNT_STORAGE_KEYS,
369
+ }),
370
+ }),
371
+ );
372
+ setDiscountStateReady(true);
373
+ });
374
+ }, []);
375
+
376
+ useEffect(() => {
377
+ return deferPaywallStateSync(() => {
378
+ setCheckoutEmail(user.email);
379
+ });
380
+ }, [user.email]);
381
+
382
+ useEffect(() => {
383
+ if (!discountStateReady) {
384
+ return;
385
+ }
386
+
387
+ const timer = window.setInterval(() => {
388
+ setDiscountState((current) => {
389
+ const nextState = advancePaywallDiscountState({
390
+ discounts: billingDiscounts,
391
+ state: current,
392
+ nowMs: Date.now(),
393
+ });
394
+ return nextState.status === current.status &&
395
+ nextState.remainingSeconds === current.remainingSeconds &&
396
+ nextState.expiresAtMs === current.expiresAtMs
397
+ ? current
398
+ : nextState;
399
+ });
400
+ }, 1000);
401
+
402
+ return () => window.clearInterval(timer);
403
+ }, [discountStateReady]);
404
+
405
+ useEffect(() => {
406
+ if (storyOptions.length < 2) {
407
+ return;
408
+ }
409
+
410
+ const storyTimer = window.setInterval(() => {
411
+ setActiveStoryIndex((current) => (current + 1) % storyOptions.length);
412
+ }, 2500);
413
+
414
+ return () => window.clearInterval(storyTimer);
415
+ }, [storyOptions.length]);
416
+
417
+ useEffect(() => {
418
+ if (!discountStateReady) {
419
+ return;
420
+ }
421
+
422
+ updatePaywallStateValue(
423
+ () => serializedDiscountSnapshot,
424
+ {
425
+ legacyKeys: CLAIMBEE_PAYWALL_DISCOUNT_STORAGE_KEYS,
426
+ },
427
+ );
428
+ }, [discountStateReady, serializedDiscountSnapshot]);
429
+
430
+ const updatePaywallWalletAvailability = (
431
+ method: PlatformWalletPaymentMethod,
432
+ available: boolean,
433
+ ) => {
434
+ setPaywallWalletAvailability((current) => {
435
+ const currentMethods =
436
+ current.intentKey === checkoutSession.intentKey
437
+ ? current.methods
438
+ : {};
439
+
440
+ return {
441
+ intentKey: checkoutSession.intentKey,
442
+ methods: {
443
+ ...currentMethods,
444
+ [method]: available,
445
+ },
446
+ };
447
+ });
448
+ };
449
+
450
+ const handleApplePayAvailabilityChange = (available: boolean) =>
451
+ updatePaywallWalletAvailability('applePay', available);
452
+
453
+ const handleGooglePayAvailabilityChange = (available: boolean) =>
454
+ updatePaywallWalletAvailability('googlePay', available);
455
+
456
+ const startCheckout = async () => {
457
+ if (checkoutDisabled || checkoutSession.loading.card) {
458
+ return;
459
+ }
460
+
461
+ if (checkoutSession.activeClientSecret) {
462
+ setCheckoutDialogOpen(true);
463
+ return;
464
+ }
465
+
466
+ const readyClientSecret = await checkoutSession.prepareCardCheckout();
467
+ if (readyClientSecret) {
468
+ setCheckoutDialogOpen(true);
469
+ }
470
+ };
471
+
472
+ const trackPaymentInfoSubmitted = () => {
473
+ if (!selectedDisplayPlan || !checkoutAnalyticsMetadata) {
474
+ return;
475
+ }
476
+
477
+ const checkoutSessionId = checkoutSession.checkoutSessionId?.trim();
478
+ if (!checkoutSessionId) {
479
+ return;
480
+ }
481
+
482
+ completeStep(analyticsStepId, {
483
+ planId: selectedDisplayPlan.id,
484
+ });
485
+ publicAnalyticsSdk.trackPaymentInfoSubmitted({
486
+ eventId: `add_payment_info:${checkoutSessionId}`,
487
+ stepId: analyticsStepId,
488
+ stepName: analyticsStepName,
489
+ stepType: stepPaywall.type,
490
+ stepContractVersion: funnelManifest.stepContractVersion,
491
+ featureFlags,
492
+ metadata: checkoutAnalyticsMetadata,
493
+ });
494
+ void publicAnalyticsSdk.flush().catch(() => 0);
495
+ };
496
+
497
+ const preparePaywallWalletCheckout = async (): Promise<string | null> => {
498
+ const normalizedEmail = checkoutEmail.trim() || user.email.trim();
499
+ if (!normalizedEmail) {
500
+ setCheckoutDialogOpen(true);
501
+ return null;
502
+ }
503
+
504
+ return normalizedEmail;
505
+ };
506
+
507
+ const openCardCheckoutFromUnavailableWallet = async () => {
508
+ if (checkoutDisabled || checkoutSession.loading.card) {
509
+ return;
510
+ }
511
+
512
+ const readyClientSecret =
513
+ checkoutSession.activeClientSecret ?? await checkoutSession.prepareCardCheckout();
514
+ if (readyClientSecret) {
515
+ setCheckoutDialogOpen(true);
516
+ }
517
+ };
518
+
519
+ const commitCheckoutEmail = async (draftEmail: string): Promise<string> => {
520
+ const normalizedEmail = draftEmail.trim();
521
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalizedEmail)) {
522
+ throw new Error(content.checkout.customerEmailInvalidMessage);
523
+ }
524
+
525
+ if (normalizedEmail === user.email.trim()) {
526
+ setCheckoutEmail(normalizedEmail);
527
+ return normalizedEmail;
528
+ }
529
+
530
+ const updatedUser = await apiService.updateUser({
531
+ ...user,
532
+ email: normalizedEmail,
533
+ document: user.document ?? {},
534
+ attributes: {
535
+ ...attributes,
536
+ ...user.attributes,
537
+ },
538
+ }, {
539
+ attribution: collectCurrentFunnelAttribution(),
540
+ });
541
+
542
+ const syncedEmail = updatedUser.email.trim() || normalizedEmail;
543
+ setUser(updatedUser);
544
+ setCheckoutEmail(syncedEmail);
545
+ return syncedEmail;
546
+ };
547
+
548
+ const closeCheckoutDialog = () => {
549
+ setCheckoutDialogOpen(false);
550
+ setError(null);
551
+ if (discountState.stage === 'first' && discountState.status === 'active') {
552
+ setSpecialOfferDialogOpen(true);
553
+ }
554
+ };
555
+
556
+ const handleSpecialOfferAccept = () => {
557
+ setSpecialOfferDialogOpen(false);
558
+ paywallStepRef.current?.scrollTo({ top: 0, behavior: 'smooth' });
559
+ setDiscountState((current) =>
560
+ activateSecondPaywallDiscount({
561
+ discounts: billingDiscounts,
562
+ state: current,
563
+ nowMs: Date.now(),
564
+ }),
565
+ );
566
+ };
567
+
568
+ const toggleFaqId = (faqId: string) => {
569
+ setOpenFaqIds((current) =>
570
+ current.includes(faqId)
571
+ ? current.filter((currentId) => currentId !== faqId)
572
+ : [...current, faqId],
573
+ );
574
+ };
575
+
576
+ if (!discountStateReady) {
577
+ return (
578
+ <>
579
+ <div className='paywall-v2-loading-screen' aria-live='polite'>
580
+ <div className='paywall-v2-loading-content'>
581
+ <div className='paywall-v2-loading-spinner' aria-hidden='true' />
582
+ <p className='paywall-v2-loading-label'>{content.loadingLabel}</p>
583
+ </div>
584
+ </div>
585
+
586
+ <style>{stepPaywallStyles}</style>
587
+ </>
588
+ );
589
+ }
590
+
591
+ if (checkoutSession.loading.card) {
592
+ return (
593
+ <>
594
+ <div
595
+ className='paywall-v2-loading-screen'
596
+ aria-label='Loading checkout'
597
+ aria-live='polite'
598
+ >
599
+ <div className='paywall-v2-loading-content'>
600
+ <div className='paywall-v2-loading-spinner' aria-hidden='true' />
601
+ </div>
602
+ </div>
603
+
604
+ <style>{stepPaywallStyles}</style>
605
+ </>
606
+ );
607
+ }
608
+
609
+ return (
610
+ <>
611
+ {checkoutDialogOpen &&
612
+ checkoutSession.activeClientSecret &&
613
+ checkoutSession.stripePromise &&
614
+ selectedDisplayPlan ? (
615
+ <SharedStripeCheckoutV2Dialog
616
+ amountCents={selectedDisplayPlan.amountCents}
617
+ buttonColor='var(--color-secondary-text)'
618
+ cardSubmitLabel={content.checkout.cardSubmitLabel}
619
+ checkoutAnalytics={checkoutAnalyticsContext}
620
+ checkoutSessionId={checkoutSession.checkoutSessionId}
621
+ clientSecret={checkoutSession.activeClientSecret}
622
+ closeAriaLabel={content.checkout.closeAriaLabel}
623
+ countdownLabel={content.checkout.countdownLabel}
624
+ customerEmail={checkoutEmail}
625
+ customerEmailEditable={!user.email.trim()}
626
+ customerEmailLabel={content.checkout.customerEmailLabel}
627
+ customerEmailPlaceholder={content.checkout.customerEmailPlaceholder}
628
+ customerEmailInvalidMessage={content.checkout.customerEmailInvalidMessage}
629
+ customerName={user.name}
630
+ discountAmountLabel={checkoutDiscountAmountLabel}
631
+ discountLabel={checkoutDiscountLabel}
632
+ discountPercent={checkoutDiscountPercent}
633
+ initialWalletAvailable={checkoutInitialWalletAvailable}
634
+ onClose={closeCheckoutDialog}
635
+ onCustomerEmailChange={setCheckoutEmail}
636
+ onCustomerEmailCommit={commitCheckoutEmail}
637
+ onError={checkoutSession.setError}
638
+ onPaymentInfoSubmitted={trackPaymentInfoSubmitted}
639
+ paymentMethodLabels={content.checkout.paymentMethodLabels}
640
+ paypalButtonLabel={content.checkout.paypalButtonLabel}
641
+ paypalLabel={content.checkout.paypalLabel}
642
+ promoActive={checkoutPromoActive}
643
+ promoCode={promoDisplayName}
644
+ promoCodeLabel={content.checkout.promoCodeLabel}
645
+ processingLabel={content.checkout.processingLabel}
646
+ remainingSeconds={discountState.remainingSeconds}
647
+ returnUrl={successReturnUrl}
648
+ savedLabel={checkoutSavedLabel}
649
+ secureLabel={content.checkout.secureLabel}
650
+ satisfactionLabel={content.checkout.satisfactionLabel}
651
+ satisfactionPercent={content.checkout.satisfactionPercent}
652
+ stripePromise={checkoutSession.stripePromise}
653
+ summaryLabel={checkoutSummaryLabel}
654
+ themeColor='var(--color-secondary-text)'
655
+ title={content.checkout.title}
656
+ totalLabel={content.checkout.totalLabel}
657
+ totalValue={checkoutTotalValue}
658
+ walletButtonLabel={content.checkout.walletButtonLabel}
659
+ originalPriceLabel={content.checkout.originalPriceLabel}
660
+ originalPriceValue={selectedDisplayPlan.basePriceLabel}
661
+ />
662
+ ) : null}
663
+
664
+ {specialOfferDialogOpen ? (
665
+ <SharedCheckoutSpecialOfferDialog
666
+ buttonColor='var(--color-secondary-text)'
667
+ buttonLabel={content.checkout.specialOffer.buttonLabel}
668
+ description={content.checkout.specialOffer.description}
669
+ discountLabel={content.checkout.specialOffer.discountLabel}
670
+ imageAlt={content.checkout.specialOffer.image.alt}
671
+ imageSrc={content.checkout.specialOffer.image.src}
672
+ onAccept={handleSpecialOfferAccept}
673
+ themeColor='var(--color-secondary-text)'
674
+ title={content.checkout.specialOffer.title}
675
+ />
676
+ ) : null}
677
+
678
+ <div ref={paywallStepRef} className='paywall-v2-step' data-node-id='6992:6210'>
679
+ <section className='paywall-v2-top-row'>
680
+ <p className='paywall-v2-top-label'>{content.topBarDiscountLabel}</p>
681
+ <div className='paywall-v2-top-controls'>
682
+ <div
683
+ className='paywall-v2-countdown'
684
+ aria-label={`Time left ${String(minutes).padStart(2, '0')} minutes ${String(seconds).padStart(2, '0')} seconds`}
685
+ >
686
+ <p className='paywall-v2-countdown-value'>
687
+ <span className='paywall-v2-countdown-segment'>
688
+ {String(minutes).padStart(2, '0')}
689
+ </span>
690
+ <span className='paywall-v2-countdown-separator'>:</span>
691
+ <span className='paywall-v2-countdown-segment'>
692
+ {String(seconds).padStart(2, '0')}
693
+ </span>
694
+ </p>
695
+ <p className='paywall-v2-countdown-units' aria-hidden='true'>
696
+ <span className='paywall-v2-countdown-unit'>{content.countdownUnits[0]}</span>
697
+ <span className='paywall-v2-countdown-unit-spacer' />
698
+ <span className='paywall-v2-countdown-unit'>{content.countdownUnits[1]}</span>
699
+ </p>
700
+ </div>
701
+ <button
702
+ type='button'
703
+ className='paywall-v2-scroll-cta paywall-v2-vibe-cta'
704
+ onClick={() => {
705
+ const currentPlanId = selectedPlan?.id ?? selectedPlanId;
706
+ if (!currentPlanId) {
707
+ return;
708
+ }
709
+
710
+ const selectedPlanElement = document.getElementById(`paywall-v2-plan-${currentPlanId}`);
711
+ selectedPlanElement?.scrollIntoView({ behavior: 'smooth', block: 'center' });
712
+ }}
713
+ >
714
+ {content.topBarCtaLabel}
715
+ </button>
716
+ </div>
717
+ </section>
718
+
719
+ <section className='paywall-v2-head'>
720
+ {content.hero.pills.map((line, index) => (
721
+ <p
722
+ key={line}
723
+ className={index === 1 ? 'paywall-v2-pill-line is-green' : 'paywall-v2-pill-line'}
724
+ >
725
+ {line}
726
+ </p>
727
+ ))}
728
+ </section>
729
+
730
+ <Image
731
+ src={content.hero.image.src}
732
+ alt={content.hero.image.alt || ''}
733
+ width={390}
734
+ height={288}
735
+ className='paywall-v2-hero'
736
+ priority
737
+ />
738
+
739
+ {activeCouponId ? (
740
+ <section
741
+ className={[
742
+ 'paywall-v2-promo-banner',
743
+ upgradedDiscountActive ? 'is-upgraded' : '',
744
+ ]
745
+ .filter(Boolean)
746
+ .join(' ')}
747
+ aria-label={content.promo.ariaLabel}
748
+ >
749
+ {upgradedDiscountActive ? (
750
+ <>
751
+ <div className='paywall-v2-promo-upgrade-row'>
752
+ <div className='paywall-v2-promo-upgrade-badge'>
753
+ {discountState.previousDiscountPercent ? (
754
+ <span className='paywall-v2-promo-upgrade-badge-old'>
755
+ {discountState.previousDiscountPercent}%
756
+ </span>
757
+ ) : null}
758
+ <span className='paywall-v2-promo-upgrade-badge-main'>
759
+ {discountState.discountPercent}% OFF
760
+ </span>
761
+ </div>
762
+ <p className='paywall-v2-promo-upgrade-label'>{content.promo.upgradedLabel}</p>
763
+ </div>
764
+ <div className='paywall-v2-promo-divider' aria-hidden='true' />
765
+ <p className='paywall-v2-promo-code'>{promoDisplayName}</p>
766
+ </>
767
+ ) : (
768
+ <>
769
+ <p className='paywall-v2-promo-label'>{content.promo.initialLabel}</p>
770
+ <div className='paywall-v2-promo-divider' aria-hidden='true' />
771
+ <p className='paywall-v2-promo-code'>{promoDisplayName}</p>
772
+ </>
773
+ )}
774
+ </section>
775
+ ) : null}
776
+
777
+ <section className='paywall-v2-highlight'>
778
+ <p className='paywall-v2-highlight-title'>
779
+ {content.highlight.title.map((line) => (
780
+ <span key={line}>
781
+ {line}
782
+ <br />
783
+ </span>
784
+ ))}
785
+ </p>
786
+ <p className='paywall-v2-highlight-copy'>{content.highlight.description}</p>
787
+ </section>
788
+
789
+ <section id='paywall-v2-plans' className='paywall-v2-plan-list' aria-label='Plans'>
790
+ <h2 className='paywall-v2-title'>{content.plans.title}</h2>
791
+
792
+ {displayPlans.map((plan) => (
793
+ <button
794
+ id={`paywall-v2-plan-${plan.id}`}
795
+ key={plan.id}
796
+ type='button'
797
+ className={[
798
+ 'paywall-v2-plan',
799
+ plan.featuredTag ? 'paywall-v2-plan--featured' : '',
800
+ plan.id === selectedPlanId ? 'is-selected' : '',
801
+ ]
802
+ .filter(Boolean)
803
+ .join(' ')}
804
+ onClick={() => setSelectedPlanId(plan.id)}
805
+ >
806
+ {plan.featuredTag ? <span className='paywall-v2-plan-tag'>{plan.featuredTag}</span> : null}
807
+ <div className='paywall-v2-plan-row'>
808
+ <div
809
+ className={[
810
+ 'paywall-v2-plan-indicator',
811
+ plan.id === selectedPlanId ? 'is-selected' : '',
812
+ ]
813
+ .filter(Boolean)
814
+ .join(' ')}
815
+ aria-hidden='true'
816
+ >
817
+ {plan.id === selectedPlanId ? <span className='paywall-v2-plan-indicator-check' /> : null}
818
+ </div>
819
+ <div className='paywall-v2-plan-body'>
820
+ <div className='paywall-v2-plan-left'>
821
+ <p className='paywall-v2-plan-title'>{plan.title}</p>
822
+ <p className='paywall-v2-plan-price'>
823
+ {plan.oldPriceLabel ? (
824
+ <span className='paywall-v2-plan-price-old'>{plan.oldPriceLabel}</span>
825
+ ) : null}
826
+ <span className='paywall-v2-plan-price-main'>{plan.priceLabel}</span>
827
+ </p>
828
+ </div>
829
+ <div className='paywall-v2-plan-divider' aria-hidden='true' />
830
+ <div className='paywall-v2-plan-right'>
831
+ <p className='paywall-v2-plan-per-day-price'>
832
+ {plan.oldPerDayAmount ? (
833
+ <span className='paywall-v2-plan-per-day-old'>{plan.oldPerDayAmount}</span>
834
+ ) : null}
835
+ <span className='paywall-v2-plan-per-day-amount'>{plan.perDayAmount}</span>
836
+ </p>
837
+ <p className='paywall-v2-plan-per-day-label'>{plan.perDayLabel}</p>
838
+ </div>
839
+ </div>
840
+ </div>
841
+ </button>
842
+ ))}
843
+ </section>
844
+
845
+ <section className='paywall-v2-payment-stack'>
846
+ {walletPaymentMethods.includes('applePay') ? (
847
+ <div className='paywall-v2-apple-pay-shell'>
848
+ {selectedDisplayPlan ? (
849
+ <ApplePaySubscriptionCheckoutSlot
850
+ amountCents={selectedDisplayPlan.amountCents}
851
+ beforeConfirm={preparePaywallWalletCheckout}
852
+ checkoutAnalytics={checkoutAnalyticsContext}
853
+ className='paywall-v2-apple-pay-button'
854
+ customerEmail={checkoutEmail || user.email}
855
+ customerName={user.name}
856
+ disabled={checkoutDisabled}
857
+ onAvailabilityChange={handleApplePayAvailabilityChange}
858
+ onPaymentInfoSubmitted={trackPaymentInfoSubmitted}
859
+ onUnavailableClick={openCardCheckoutFromUnavailableWallet}
860
+ placeholderLabel={content.checkout.walletPlaceholderLabel}
861
+ returnUrl={successReturnUrl}
862
+ session={checkoutSession}
863
+ summaryLabel={checkoutSummaryLabel}
864
+ suspended={checkoutDialogReady || specialOfferDialogOpen}
865
+ />
866
+ ) : null}
867
+ </div>
868
+ ) : null}
869
+ {walletPaymentMethods.includes('googlePay') ? (
870
+ <div className='paywall-v2-google-pay-shell'>
871
+ {selectedDisplayPlan ? (
872
+ <GooglePaySubscriptionCheckoutSlot
873
+ amountCents={selectedDisplayPlan.amountCents}
874
+ beforeConfirm={preparePaywallWalletCheckout}
875
+ checkoutAnalytics={checkoutAnalyticsContext}
876
+ className='paywall-v2-google-pay-button'
877
+ customerEmail={checkoutEmail || user.email}
878
+ customerName={user.name}
879
+ disabled={checkoutDisabled}
880
+ onAvailabilityChange={handleGooglePayAvailabilityChange}
881
+ onPaymentInfoSubmitted={trackPaymentInfoSubmitted}
882
+ onUnavailableClick={openCardCheckoutFromUnavailableWallet}
883
+ placeholderLabel={content.checkout.googlePayPlaceholderLabel}
884
+ returnUrl={successReturnUrl}
885
+ session={checkoutSession}
886
+ summaryLabel={checkoutSummaryLabel}
887
+ suspended={checkoutDialogReady || specialOfferDialogOpen}
888
+ />
889
+ ) : null}
890
+ </div>
891
+ ) : null}
892
+ <button
893
+ type='button'
894
+ className='paywall-v2-card-button paywall-v2-vibe-cta'
895
+ onClick={() => void startCheckout()}
896
+ disabled={checkoutDisabled || checkoutSession.loading.card}
897
+ >
898
+ {content.plans.cardButtonLabel}
899
+ </button>
900
+ <p className='paywall-v2-alt-meta'>{content.plans.metaLabels.join(' · ')}</p>
901
+ <p className='paywall-v2-disclaimer'>{paymentDisclaimer}</p>
902
+ <p className='paywall-v2-support'>
903
+ {content.checkout.supportText}{' '}
904
+ <a href={`mailto:${supportEmail}`}>{supportEmail}</a>
905
+ </p>
906
+ {!checkoutSession.configured ? (
907
+ <p className='paywall-v2-note'>{content.plans.stripeUnavailableNote}</p>
908
+ ) : null}
909
+ {error ? <p className='paywall-v2-error'>{error}</p> : null}
910
+ </section>
911
+
912
+ <section className='paywall-v2-feature-grid' data-node-id='6043:6154'>
913
+ <div className='paywall-v2-feature-grid-head'>
914
+ <h3 className='paywall-v2-feature-grid-title'>
915
+ {content.featureGrid.title.map((line) => (
916
+ <span key={line}>
917
+ {line}
918
+ <br />
919
+ </span>
920
+ ))}
921
+ </h3>
922
+ <p className='paywall-v2-feature-grid-subtitle'>
923
+ {content.featureGrid.subtitle}
924
+ </p>
925
+ </div>
926
+
927
+ <article className='paywall-v2-feature-card paywall-v2-feature-card--large'>
928
+ <Image
929
+ src={content.featureGrid.assistant.image?.src || '/paywall/ai-claim-assistant.png'}
930
+ alt={content.featureGrid.assistant.image?.alt || ''}
931
+ width={289}
932
+ height={105}
933
+ className='paywall-v2-feature-hero-image'
934
+ />
935
+ <div className='paywall-v2-feature-copy'>
936
+ <p className='paywall-v2-feature-name'>{content.featureGrid.assistant.title}</p>
937
+ <p className='paywall-v2-feature-description'>
938
+ {content.featureGrid.assistant.description}
939
+ </p>
940
+ </div>
941
+ </article>
942
+
943
+ <div className='paywall-v2-feature-row'>
944
+ {content.featureGrid.quickFeatures.slice(0, 2).map((feature, index) => (
945
+ <article key={feature.id} className='paywall-v2-feature-card paywall-v2-feature-card--small'>
946
+ <p
947
+ className={[
948
+ 'paywall-v2-feature-tile-title',
949
+ paywallFeatureTileColorClassNames[index],
950
+ ].join(' ')}
951
+ >
952
+ {(feature.title || '').split('\n').map((line) => (
953
+ <span key={line}>{line}</span>
954
+ ))}
955
+ </p>
956
+ </article>
957
+ ))}
958
+ </div>
959
+
960
+ <article className='paywall-v2-feature-card paywall-v2-feature-card--mid'>
961
+ <Image
962
+ src={content.featureGrid.history.image?.src || '/paywall/claim-history.png'}
963
+ alt={content.featureGrid.history.image?.alt || ''}
964
+ width={160}
965
+ height={129}
966
+ className='paywall-v2-feature-mid-image'
967
+ />
968
+ <p className='paywall-v2-feature-mid-title'>
969
+ {(content.featureGrid.history.title || '').split('\n').map((line) => (
970
+ <span key={line}>
971
+ {line}
972
+ <br />
973
+ </span>
974
+ ))}
975
+ </p>
976
+ </article>
977
+
978
+ <div className='paywall-v2-feature-row'>
979
+ {content.featureGrid.quickFeatures.slice(2).map((feature, index) => (
980
+ <article key={feature.id} className='paywall-v2-feature-card paywall-v2-feature-card--small'>
981
+ <p
982
+ className={[
983
+ 'paywall-v2-feature-tile-title',
984
+ paywallFeatureTileColorClassNames[index + 2],
985
+ ].join(' ')}
986
+ >
987
+ {(feature.title || '').split('\n').map((line) => (
988
+ <span key={line}>{line}</span>
989
+ ))}
990
+ </p>
991
+ </article>
992
+ ))}
993
+ </div>
994
+
995
+ <div className='paywall-v2-feature-row paywall-v2-feature-row--tall'>
996
+ {content.featureGrid.detailedFeatures.map((feature) => {
997
+ const imageDimensions =
998
+ paywallDetailImageDimensions[
999
+ feature.id as keyof typeof paywallDetailImageDimensions
1000
+ ] || {
1001
+ width: 160,
1002
+ height: 90,
1003
+ };
1004
+
1005
+ return (
1006
+ <article key={feature.id} className='paywall-v2-feature-card paywall-v2-feature-card--tall'>
1007
+ <Image
1008
+ src={feature.image?.src || ''}
1009
+ alt={feature.image?.alt || ''}
1010
+ width={imageDimensions.width}
1011
+ height={imageDimensions.height}
1012
+ className='paywall-v2-feature-detail-image'
1013
+ />
1014
+ <div className='paywall-v2-feature-detail-copy'>
1015
+ <p className='paywall-v2-feature-detail-title'>
1016
+ {(feature.title || '').split('\n').map((line) => (
1017
+ <span key={line}>{line}</span>
1018
+ ))}
1019
+ </p>
1020
+ <p className='paywall-v2-feature-detail-description'>
1021
+ {(feature.description || '').split('\n').map((line) => (
1022
+ <span key={line}>{line}</span>
1023
+ ))}
1024
+ </p>
1025
+ </div>
1026
+ </article>
1027
+ );
1028
+ })}
1029
+ </div>
1030
+ </section>
1031
+
1032
+ <section className='paywall-v2-feedback' aria-live='polite'>
1033
+ <h3 className='paywall-v2-feedback-title'>{content.stories.title}</h3>
1034
+ <article
1035
+ className='paywall-v2-feedback-card'
1036
+ style={{ backgroundColor: activeStory?.backgroundColor || claimbeeStoryBackgroundColors[0] }}
1037
+ >
1038
+ <p className='paywall-v2-feedback-quote'>{activeStory?.quote}</p>
1039
+ <p className='paywall-v2-feedback-author'>- {activeStory?.author}</p>
1040
+ </article>
1041
+ </section>
1042
+
1043
+ <section className='paywall-v2-faq'>
1044
+ <h3 className='paywall-v2-faq-title'>{content.faq.title}</h3>
1045
+ <div className='paywall-v2-faq-list'>
1046
+ {content.faq.items.map((item) => {
1047
+ const isOpen = openFaqIds.includes(item.id);
1048
+
1049
+ return (
1050
+ <article
1051
+ key={item.id}
1052
+ className={['paywall-v2-faq-item', isOpen ? 'is-open' : ''].filter(Boolean).join(' ')}
1053
+ >
1054
+ <button
1055
+ type='button'
1056
+ className='paywall-v2-faq-toggle'
1057
+ onClick={() => toggleFaqId(item.id)}
1058
+ aria-expanded={isOpen}
1059
+ >
1060
+ <span className='paywall-v2-faq-question'>{item.question}</span>
1061
+ <span
1062
+ className={['paywall-v2-faq-icon', isOpen ? 'is-open' : ''].filter(Boolean).join(' ')}
1063
+ aria-hidden='true'
1064
+ />
1065
+ </button>
1066
+ {item.answer && isOpen ? (
1067
+ <p className='paywall-v2-faq-answer'>{item.answer}</p>
1068
+ ) : null}
1069
+ </article>
1070
+ );
1071
+ })}
1072
+ </div>
1073
+ </section>
1074
+
1075
+ <section className='paywall-v2-guarantee-card'>
1076
+ <Image
1077
+ src={content.guarantee.image.src}
1078
+ alt={content.guarantee.image.alt || ''}
1079
+ width={184}
1080
+ height={184}
1081
+ className='paywall-v2-badge'
1082
+ />
1083
+ <div className='paywall-v2-guarantee-copy'>
1084
+ <p className='paywall-v2-guarantee-title'>{content.guarantee.title}</p>
1085
+ <p className='paywall-v2-guarantee-text'>
1086
+ {content.guarantee.description}{' '}
1087
+ <Link href={content.guarantee.links[0]?.href || '/refund-policy'}>
1088
+ {content.guarantee.links[0]?.label || 'refund policy'}
1089
+ </Link>{' '}
1090
+ for details and limitations.
1091
+ </p>
1092
+ </div>
1093
+ </section>
1094
+ </div>
1095
+
1096
+ <style>{stepPaywallStyles}</style>
1097
+ </>
1098
+ );
1099
+ }
1100
+
1101
+ const stepPaywallStyles = `
1102
+ .paywall-v2-loading-screen {
1103
+ position: absolute;
1104
+ inset: 0;
1105
+ display: flex;
1106
+ align-items: center;
1107
+ justify-content: center;
1108
+ background: #fff;
1109
+ }
1110
+
1111
+ .paywall-v2-loading-content {
1112
+ display: flex;
1113
+ flex-direction: column;
1114
+ align-items: center;
1115
+ gap: 12px;
1116
+ }
1117
+
1118
+ .paywall-v2-loading-spinner {
1119
+ width: 26px;
1120
+ height: 26px;
1121
+ border: 3px solid rgb(63 81 181 / 14%);
1122
+ border-top-color: var(--color-secondary-text);
1123
+ border-radius: 999px;
1124
+ animation: paywall-v2-loading-spin 720ms linear infinite;
1125
+ }
1126
+
1127
+ .paywall-v2-loading-label {
1128
+ margin: 0;
1129
+ color: #5f6790;
1130
+ font-size: 14px;
1131
+ font-weight: 600;
1132
+ line-height: 1.25;
1133
+ text-align: center;
1134
+ }
1135
+
1136
+ .paywall-v2-step {
1137
+ position: absolute;
1138
+ inset: 0;
1139
+ box-sizing: border-box;
1140
+ max-width: 100%;
1141
+ overflow-y: auto;
1142
+ overflow-x: hidden;
1143
+ background: #ecf7ff;
1144
+ padding: 0 24px 64px;
1145
+ container-type: inline-size;
1146
+ }
1147
+
1148
+ .paywall-v2-top-row {
1149
+ position: sticky;
1150
+ top: 0;
1151
+ z-index: 20;
1152
+ min-height: 92px;
1153
+ box-sizing: border-box;
1154
+ display: flex;
1155
+ flex-direction: column;
1156
+ justify-content: center;
1157
+ gap: 6px;
1158
+ margin: 0 -24px 0;
1159
+ padding: 8px 24px 10px;
1160
+ background: #ecf7ff;
1161
+ border-bottom: 1px solid rgb(63 81 181 / 10%);
1162
+ }
1163
+
1164
+ .paywall-v2-top-label {
1165
+ margin: 0;
1166
+ color: var(--color-secondary-text);
1167
+ font-size: 13px;
1168
+ font-weight: 700;
1169
+ line-height: 1.15;
1170
+ letter-spacing: 0;
1171
+ }
1172
+
1173
+ .paywall-v2-top-controls {
1174
+ display: grid;
1175
+ grid-template-columns: minmax(128px, 164px) minmax(132px, 1fr);
1176
+ align-items: center;
1177
+ gap: 12px;
1178
+ }
1179
+
1180
+ .paywall-v2-countdown {
1181
+ width: 100%;
1182
+ min-width: 0;
1183
+ display: flex;
1184
+ flex-direction: column;
1185
+ justify-content: center;
1186
+ gap: 4px;
1187
+ }
1188
+
1189
+ .paywall-v2-countdown-value {
1190
+ margin: 0;
1191
+ display: grid;
1192
+ grid-template-columns: minmax(0, 1fr) 14px minmax(0, 1fr);
1193
+ align-items: end;
1194
+ color: var(--color-secondary-text);
1195
+ font-size: 40px;
1196
+ font-weight: 700;
1197
+ line-height: 1;
1198
+ letter-spacing: -0.08em;
1199
+ text-align: left;
1200
+ }
1201
+
1202
+ .paywall-v2-countdown-segment {
1203
+ color: var(--color-secondary-text);
1204
+ }
1205
+
1206
+ .paywall-v2-countdown-separator {
1207
+ color: #b8b0a5;
1208
+ text-align: center;
1209
+ }
1210
+
1211
+ .paywall-v2-countdown-units {
1212
+ margin: 0;
1213
+ display: grid;
1214
+ grid-template-columns: minmax(0, 1fr) 14px minmax(0, 1fr);
1215
+ align-items: start;
1216
+ color: #2f230f;
1217
+ font-size: 11px;
1218
+ font-weight: 500;
1219
+ line-height: 1.15;
1220
+ }
1221
+
1222
+ .paywall-v2-countdown-unit {
1223
+ white-space: nowrap;
1224
+ text-align: center;
1225
+ }
1226
+
1227
+ .paywall-v2-scroll-cta {
1228
+ width: 100%;
1229
+ max-width: 188px;
1230
+ min-height: 46px;
1231
+ padding: 0 14px;
1232
+ border: 0;
1233
+ border-radius: 16px;
1234
+ background: var(--color-secondary-text);
1235
+ display: flex;
1236
+ align-items: center;
1237
+ justify-content: center;
1238
+ color: #fff;
1239
+ font-size: 16px;
1240
+ font-weight: 800;
1241
+ line-height: 1;
1242
+ letter-spacing: -0.03em;
1243
+ cursor: pointer;
1244
+ justify-self: end;
1245
+ }
1246
+
1247
+ .paywall-v2-head {
1248
+ margin-top: 16px;
1249
+ display: flex;
1250
+ flex-direction: column;
1251
+ align-items: center;
1252
+ gap: 4px;
1253
+ }
1254
+
1255
+ .paywall-v2-pill-line {
1256
+ margin: 0;
1257
+ max-width: 100%;
1258
+ box-sizing: border-box;
1259
+ display: inline-flex;
1260
+ align-items: center;
1261
+ justify-content: center;
1262
+ border-radius: 999px;
1263
+ background: var(--color-secondary-text);
1264
+ padding: 4px 16px;
1265
+ color: #fff;
1266
+ font-size: 28px;
1267
+ font-weight: 600;
1268
+ line-height: 1.25;
1269
+ text-align: center;
1270
+ }
1271
+
1272
+ .paywall-v2-pill-line.is-green {
1273
+ color: #bcfbb4;
1274
+ }
1275
+
1276
+ .paywall-v2-hero {
1277
+ display: block;
1278
+ width: min(100%, 390px);
1279
+ height: auto;
1280
+ margin: -12px auto 0;
1281
+ }
1282
+
1283
+ .paywall-v2-promo-banner {
1284
+ --paywall-v2-promo-code-height: 41px;
1285
+ position: relative;
1286
+ display: flex;
1287
+ flex-direction: column;
1288
+ width: 100%;
1289
+ max-width: 382px;
1290
+ min-height: 86px;
1291
+ margin: -20px auto 0;
1292
+ box-sizing: border-box;
1293
+ overflow: hidden;
1294
+ border-radius: 16px;
1295
+ background: #f4c249;
1296
+ box-shadow:
1297
+ 0 2px 5px rgb(40 58 128 / 10%),
1298
+ 0 9px 9px rgb(40 58 128 / 9%),
1299
+ 0 20px 12px rgb(40 58 128 / 5%),
1300
+ 0 36px 14px rgb(40 58 128 / 1%);
1301
+ text-align: center;
1302
+ }
1303
+
1304
+ .paywall-v2-promo-banner::before,
1305
+ .paywall-v2-promo-banner::after {
1306
+ content: '';
1307
+ position: absolute;
1308
+ top: calc(100% - var(--paywall-v2-promo-code-height) - 1px);
1309
+ width: 24px;
1310
+ height: 24px;
1311
+ border-radius: 999px;
1312
+ background: #ecf7ff;
1313
+ transform: translateY(-50%);
1314
+ }
1315
+
1316
+ .paywall-v2-promo-banner::before {
1317
+ left: -12px;
1318
+ }
1319
+
1320
+ .paywall-v2-promo-banner::after {
1321
+ right: -12px;
1322
+ }
1323
+
1324
+ .paywall-v2-promo-label {
1325
+ margin: 0;
1326
+ display: flex;
1327
+ min-height: 44px;
1328
+ align-items: center;
1329
+ justify-content: center;
1330
+ padding: 0 16px;
1331
+ color: #56330c;
1332
+ font-family: var(--font-family-base, Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
1333
+ font-size: 16px;
1334
+ font-weight: 600;
1335
+ line-height: 1.35;
1336
+ text-align: center;
1337
+ }
1338
+
1339
+ .paywall-v2-promo-divider {
1340
+ height: 1px;
1341
+ margin: 0 13px;
1342
+ background-image: repeating-linear-gradient(
1343
+ to right,
1344
+ rgb(255 255 255 / 92%) 0 10px,
1345
+ transparent 10px 16px
1346
+ );
1347
+ }
1348
+
1349
+ .paywall-v2-promo-code {
1350
+ margin: 0;
1351
+ display: flex;
1352
+ min-height: var(--paywall-v2-promo-code-height);
1353
+ align-items: center;
1354
+ justify-content: center;
1355
+ width: 100%;
1356
+ box-sizing: border-box;
1357
+ background: #dca63a;
1358
+ color: #fff;
1359
+ font-family: var(--font-family-base, Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
1360
+ font-size: 14px;
1361
+ font-weight: 700;
1362
+ line-height: 1.35;
1363
+ text-align: center;
1364
+ padding: 0 16px;
1365
+ text-transform: uppercase;
1366
+ }
1367
+
1368
+ .paywall-v2-promo-banner.is-upgraded {
1369
+ min-height: 126px;
1370
+ background: #edf4d4;
1371
+ }
1372
+
1373
+ .paywall-v2-promo-banner.is-upgraded .paywall-v2-promo-code {
1374
+ background: #dbe8b1;
1375
+ color: #5f780d;
1376
+ }
1377
+
1378
+ .paywall-v2-promo-upgrade-row {
1379
+ display: flex;
1380
+ align-items: center;
1381
+ gap: 16px;
1382
+ padding: 12px 16px 14px 12px;
1383
+ }
1384
+
1385
+ .paywall-v2-promo-upgrade-badge {
1386
+ position: relative;
1387
+ display: flex;
1388
+ min-width: 110px;
1389
+ min-height: 80px;
1390
+ flex-direction: column;
1391
+ align-items: center;
1392
+ justify-content: center;
1393
+ gap: 6px;
1394
+ border-radius: 24px;
1395
+ background: #76aa10;
1396
+ color: #fff;
1397
+ flex-shrink: 0;
1398
+ }
1399
+
1400
+ .paywall-v2-promo-upgrade-badge::after {
1401
+ content: '';
1402
+ position: absolute;
1403
+ left: 50%;
1404
+ bottom: -12px;
1405
+ width: 0;
1406
+ height: 0;
1407
+ border-left: 20px solid transparent;
1408
+ border-right: 20px solid transparent;
1409
+ border-top: 12px solid #76aa10;
1410
+ transform: translateX(-50%);
1411
+ }
1412
+
1413
+ .paywall-v2-promo-upgrade-badge-old {
1414
+ color: rgb(255 255 255 / 78%);
1415
+ font-size: 17px;
1416
+ font-weight: 600;
1417
+ line-height: 1;
1418
+ text-decoration: line-through;
1419
+ }
1420
+
1421
+ .paywall-v2-promo-upgrade-badge-main {
1422
+ font-size: 18px;
1423
+ font-weight: 800;
1424
+ line-height: 1.05;
1425
+ text-align: center;
1426
+ }
1427
+
1428
+ .paywall-v2-promo-upgrade-label {
1429
+ margin: 0;
1430
+ color: #2f230f;
1431
+ font-family: var(--font-family-base, Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
1432
+ font-size: 18px;
1433
+ font-weight: 700;
1434
+ line-height: 1.05;
1435
+ letter-spacing: -0.03em;
1436
+ text-align: left;
1437
+ }
1438
+
1439
+ .paywall-v2-highlight {
1440
+ margin-top: 16px;
1441
+ border-radius: 16px;
1442
+ background: #fff;
1443
+ padding: 16px;
1444
+ text-align: center;
1445
+ }
1446
+
1447
+ .paywall-v2-highlight-title {
1448
+ margin: 0;
1449
+ color: var(--color-primary);
1450
+ font-size: 28px;
1451
+ font-weight: 600;
1452
+ line-height: 1.25;
1453
+ letter-spacing: -0.036em;
1454
+ }
1455
+
1456
+ .paywall-v2-highlight-copy {
1457
+ margin: 12px 0 0;
1458
+ color: #1b286f;
1459
+ font-size: 14px;
1460
+ font-weight: 500;
1461
+ line-height: 1.25;
1462
+ }
1463
+
1464
+ .paywall-v2-plan-list {
1465
+ margin-top: 24px;
1466
+ display: flex;
1467
+ flex-direction: column;
1468
+ gap: 16px;
1469
+ }
1470
+
1471
+ .paywall-v2-title {
1472
+ margin: 0;
1473
+ color: var(--color-secondary-text);
1474
+ font-size: 28px;
1475
+ font-weight: 600;
1476
+ line-height: 1.25;
1477
+ text-align: center;
1478
+ }
1479
+
1480
+ .paywall-v2-plan {
1481
+ box-sizing: border-box;
1482
+ display: flex;
1483
+ flex-direction: column;
1484
+ width: 100%;
1485
+ min-height: 92px;
1486
+ border: 1px solid #dce2ff;
1487
+ border-radius: 16px;
1488
+ background: #fff;
1489
+ padding: 0;
1490
+ text-align: left;
1491
+ cursor: pointer;
1492
+ }
1493
+
1494
+ .paywall-v2-plan--featured {
1495
+ overflow: hidden;
1496
+ border-color: var(--color-primary);
1497
+ }
1498
+
1499
+ .paywall-v2-plan.is-selected {
1500
+ border-color: var(--color-primary);
1501
+ box-shadow: inset 0 0 0 1px var(--color-primary);
1502
+ }
1503
+
1504
+ .paywall-v2-plan-tag {
1505
+ display: flex;
1506
+ min-height: 32px;
1507
+ align-items: center;
1508
+ justify-content: center;
1509
+ background: var(--color-primary);
1510
+ color: #fff;
1511
+ font-size: 14px;
1512
+ font-weight: 600;
1513
+ line-height: 1.25;
1514
+ text-align: center;
1515
+ }
1516
+
1517
+ .paywall-v2-plan-row {
1518
+ display: flex;
1519
+ min-height: 92px;
1520
+ align-items: center;
1521
+ gap: 12px;
1522
+ padding: 16px 14px;
1523
+ box-sizing: border-box;
1524
+ }
1525
+
1526
+ .paywall-v2-plan-indicator {
1527
+ width: 22px;
1528
+ height: 22px;
1529
+ border: 1.5px solid rgb(31 16 0 / 24%);
1530
+ border-radius: 999px;
1531
+ display: flex;
1532
+ align-items: center;
1533
+ justify-content: center;
1534
+ flex-shrink: 0;
1535
+ box-sizing: border-box;
1536
+ }
1537
+
1538
+ .paywall-v2-plan-indicator.is-selected {
1539
+ border-color: var(--color-primary);
1540
+ background: var(--color-primary);
1541
+ }
1542
+
1543
+ .paywall-v2-plan-indicator-check {
1544
+ width: 7px;
1545
+ height: 14px;
1546
+ border-right: 2px solid #fff;
1547
+ border-bottom: 2px solid #fff;
1548
+ transform: rotate(45deg) translate(-1px, -2px);
1549
+ box-sizing: border-box;
1550
+ }
1551
+
1552
+ .paywall-v2-plan-body {
1553
+ display: flex;
1554
+ align-items: center;
1555
+ justify-content: flex-start;
1556
+ gap: 12px;
1557
+ width: 100%;
1558
+ min-width: 0;
1559
+ }
1560
+
1561
+ .paywall-v2-plan-left,
1562
+ .paywall-v2-plan-right {
1563
+ display: flex;
1564
+ flex-direction: column;
1565
+ justify-content: center;
1566
+ gap: 6px;
1567
+ }
1568
+
1569
+ .paywall-v2-plan-left {
1570
+ flex: 1 1 0;
1571
+ width: auto;
1572
+ min-width: 0;
1573
+ }
1574
+
1575
+ .paywall-v2-plan-right {
1576
+ width: min(34%, 118px);
1577
+ min-width: 86px;
1578
+ align-items: center;
1579
+ text-align: center;
1580
+ flex-shrink: 0;
1581
+ }
1582
+
1583
+ .paywall-v2-plan-divider {
1584
+ width: 1px;
1585
+ align-self: stretch;
1586
+ background: rgb(31 16 0 / 8%);
1587
+ border-left: 1px solid rgb(31 16 0 / 8%);
1588
+ flex-shrink: 0;
1589
+ }
1590
+
1591
+ .paywall-v2-plan-title {
1592
+ margin: 0;
1593
+ color: rgba(31, 16, 0, 0.44);
1594
+ font-size: 22px;
1595
+ font-weight: 700;
1596
+ line-height: 1.25;
1597
+ overflow-wrap: anywhere;
1598
+ }
1599
+
1600
+ .paywall-v2-plan-price {
1601
+ margin: 0;
1602
+ display: flex;
1603
+ flex-wrap: wrap;
1604
+ align-items: center;
1605
+ gap: 4px;
1606
+ color: rgba(31, 16, 0, 0.72);
1607
+ font-size: 16px;
1608
+ font-weight: 500;
1609
+ line-height: 1.25;
1610
+ }
1611
+
1612
+ .paywall-v2-plan-price-old {
1613
+ text-decoration: line-through;
1614
+ }
1615
+
1616
+ .paywall-v2-plan-price-main {
1617
+ color: rgba(31, 16, 0, 0.72);
1618
+ font-size: 16px;
1619
+ font-weight: 500;
1620
+ line-height: 1.25;
1621
+ min-width: 0;
1622
+ }
1623
+
1624
+ .paywall-v2-plan-per-day-price {
1625
+ margin: 0;
1626
+ display: flex;
1627
+ flex-direction: column;
1628
+ align-items: center;
1629
+ width: 100%;
1630
+ gap: 2px;
1631
+ }
1632
+
1633
+ .paywall-v2-plan-per-day-old {
1634
+ margin: 0;
1635
+ color: rgba(31, 16, 0, 0.72);
1636
+ font-size: 15px;
1637
+ font-weight: 500;
1638
+ line-height: 1.25;
1639
+ text-decoration: line-through;
1640
+ }
1641
+
1642
+ .paywall-v2-plan-per-day-amount {
1643
+ margin: 0;
1644
+ color: rgba(31, 16, 0, 0.44);
1645
+ font-size: 30px;
1646
+ font-weight: 700;
1647
+ line-height: 1.05;
1648
+ }
1649
+
1650
+ .paywall-v2-plan-per-day-label {
1651
+ margin: 0;
1652
+ color: rgba(31, 16, 0, 0.72);
1653
+ font-size: 16px;
1654
+ font-weight: 500;
1655
+ line-height: 1.25;
1656
+ overflow-wrap: anywhere;
1657
+ }
1658
+
1659
+ .paywall-v2-plan.is-selected .paywall-v2-plan-title,
1660
+ .paywall-v2-plan.is-selected .paywall-v2-plan-per-day-amount {
1661
+ color: var(--color-secondary-text);
1662
+ }
1663
+
1664
+ .paywall-v2-payment-stack {
1665
+ margin-top: 24px;
1666
+ display: flex;
1667
+ flex-direction: column;
1668
+ align-items: stretch;
1669
+ gap: 12px;
1670
+ }
1671
+
1672
+ .paywall-v2-apple-pay-button,
1673
+ .paywall-v2-google-pay-button {
1674
+ min-height: 64px;
1675
+ border-radius: 16px;
1676
+ }
1677
+
1678
+ .paywall-v2-apple-pay-shell,
1679
+ .paywall-v2-google-pay-shell {
1680
+ width: 100%;
1681
+ }
1682
+
1683
+ .paywall-v2-card-button {
1684
+ width: 100%;
1685
+ min-height: 64px;
1686
+ border: 0;
1687
+ border-radius: 16px;
1688
+ cursor: pointer;
1689
+ }
1690
+
1691
+ .paywall-v2-card-button,
1692
+ .paywall-v2-scroll-cta {
1693
+ transition:
1694
+ opacity 160ms ease,
1695
+ transform 180ms ease,
1696
+ box-shadow 180ms ease,
1697
+ background-color 180ms ease;
1698
+ }
1699
+
1700
+ .paywall-v2-card-button:disabled,
1701
+ .paywall-v2-scroll-cta:disabled {
1702
+ cursor: default;
1703
+ opacity: 0.65;
1704
+ }
1705
+
1706
+ .paywall-v2-vibe-cta {
1707
+ position: relative;
1708
+ isolation: isolate;
1709
+ overflow: visible;
1710
+ transform-origin: center;
1711
+ }
1712
+
1713
+ .paywall-v2-vibe-cta::after {
1714
+ content: '';
1715
+ position: absolute;
1716
+ inset: -7px;
1717
+ border: 2px solid rgb(63 81 181 / 0%);
1718
+ border-radius: 22px;
1719
+ opacity: 0;
1720
+ pointer-events: none;
1721
+ }
1722
+
1723
+ .paywall-v2-vibe-cta:not(:disabled) {
1724
+ animation: paywall-v2-cta-beat 2400ms ease-in-out infinite;
1725
+ }
1726
+
1727
+ .paywall-v2-vibe-cta:not(:disabled)::after {
1728
+ animation: paywall-v2-cta-wave 2400ms ease-out infinite;
1729
+ }
1730
+
1731
+ @keyframes paywall-v2-cta-beat {
1732
+ 0%,
1733
+ 100% {
1734
+ transform: scale(1);
1735
+ }
1736
+
1737
+ 10% {
1738
+ transform: scale(1.035);
1739
+ }
1740
+
1741
+ 18% {
1742
+ transform: scale(1);
1743
+ }
1744
+
1745
+ 28% {
1746
+ transform: scale(1.018);
1747
+ }
1748
+
1749
+ 38% {
1750
+ transform: scale(1);
1751
+ }
1752
+ }
1753
+
1754
+ @keyframes paywall-v2-cta-wave {
1755
+ 0%,
1756
+ 17% {
1757
+ opacity: 0;
1758
+ transform: scale(0.98);
1759
+ border-color: rgb(63 81 181 / 0%);
1760
+ }
1761
+
1762
+ 24% {
1763
+ opacity: 0.42;
1764
+ transform: scale(1.02);
1765
+ border-color: rgb(63 81 181 / 28%);
1766
+ }
1767
+
1768
+ 48% {
1769
+ opacity: 0;
1770
+ transform: scale(1.14);
1771
+ border-color: rgb(63 81 181 / 0%);
1772
+ }
1773
+
1774
+ 100% {
1775
+ opacity: 0;
1776
+ transform: scale(1.14);
1777
+ border-color: rgb(63 81 181 / 0%);
1778
+ }
1779
+ }
1780
+
1781
+ .paywall-v2-card-button {
1782
+ background: var(--color-secondary-text);
1783
+ color: #fff;
1784
+ font-size: 18px;
1785
+ font-weight: 700;
1786
+ line-height: 1.25;
1787
+ }
1788
+
1789
+ .paywall-v2-alt-meta {
1790
+ margin: 0;
1791
+ color: var(--color-secondary-text);
1792
+ font-size: 14px;
1793
+ font-weight: 500;
1794
+ line-height: 1.25;
1795
+ text-align: center;
1796
+ }
1797
+
1798
+ .paywall-v2-disclaimer {
1799
+ margin: 0;
1800
+ border: 1px solid rgb(63 81 181 / 12%);
1801
+ border-radius: 24px;
1802
+ background: rgb(255 255 255 / 72%);
1803
+ padding: 20px 18px;
1804
+ color: #766f66;
1805
+ font-size: 15px;
1806
+ font-weight: 500;
1807
+ line-height: 1.45;
1808
+ text-align: center;
1809
+ }
1810
+
1811
+ .paywall-v2-support {
1812
+ margin: 0;
1813
+ color: #4d5470;
1814
+ font-size: 14px;
1815
+ font-weight: 500;
1816
+ line-height: 1.35;
1817
+ text-align: center;
1818
+ }
1819
+
1820
+ .paywall-v2-support a {
1821
+ color: var(--color-secondary-text);
1822
+ font-weight: 600;
1823
+ text-decoration: none;
1824
+ }
1825
+
1826
+ .paywall-v2-note,
1827
+ .paywall-v2-error {
1828
+ margin: 0;
1829
+ font-size: 14px;
1830
+ font-weight: 600;
1831
+ line-height: 1.35;
1832
+ text-align: center;
1833
+ }
1834
+
1835
+ .paywall-v2-note {
1836
+ color: #4d5470;
1837
+ }
1838
+
1839
+ .paywall-v2-error {
1840
+ color: #d33;
1841
+ }
1842
+
1843
+ @media (prefers-reduced-motion: reduce) {
1844
+ .paywall-v2-vibe-cta:not(:disabled),
1845
+ .paywall-v2-vibe-cta:not(:disabled)::after {
1846
+ animation: none;
1847
+ }
1848
+ }
1849
+
1850
+ .paywall-v2-feedback {
1851
+ margin: 32px -24px 0;
1852
+ }
1853
+
1854
+ .paywall-v2-feedback-title {
1855
+ margin: 0;
1856
+ color: var(--color-secondary-text);
1857
+ font-size: 28px;
1858
+ font-weight: 600;
1859
+ line-height: 1.25;
1860
+ text-align: center;
1861
+ }
1862
+
1863
+ .paywall-v2-feedback-card {
1864
+ margin-top: 24px;
1865
+ box-sizing: border-box;
1866
+ height: 372px;
1867
+ padding: 48px 48px 40px;
1868
+ display: flex;
1869
+ flex-direction: column;
1870
+ justify-content: center;
1871
+ gap: 24px;
1872
+ text-align: center;
1873
+ transition: background-color 240ms ease;
1874
+ }
1875
+
1876
+ .paywall-v2-feedback-quote {
1877
+ margin: 0;
1878
+ color: #fff;
1879
+ font-size: 24px;
1880
+ font-weight: 600;
1881
+ line-height: 1.25;
1882
+ }
1883
+
1884
+ .paywall-v2-feedback-author {
1885
+ margin: 0;
1886
+ color: #fff;
1887
+ font-size: 14px;
1888
+ font-weight: 600;
1889
+ line-height: 1.25;
1890
+ }
1891
+
1892
+ .paywall-v2-faq {
1893
+ margin-top: 32px;
1894
+ }
1895
+
1896
+ .paywall-v2-faq-title {
1897
+ margin: 0;
1898
+ color: var(--color-secondary-text);
1899
+ font-size: 28px;
1900
+ font-weight: 600;
1901
+ line-height: 1.25;
1902
+ text-align: center;
1903
+ }
1904
+
1905
+ .paywall-v2-faq-list {
1906
+ margin-top: 24px;
1907
+ display: flex;
1908
+ flex-direction: column;
1909
+ gap: 16px;
1910
+ }
1911
+
1912
+ .paywall-v2-faq-item {
1913
+ border-radius: 16px;
1914
+ background: #fff;
1915
+ padding: 24px 16px;
1916
+ }
1917
+
1918
+ .paywall-v2-faq-toggle {
1919
+ width: 100%;
1920
+ border: 0;
1921
+ background: transparent;
1922
+ padding: 0;
1923
+ display: flex;
1924
+ align-items: flex-start;
1925
+ justify-content: space-between;
1926
+ gap: 10px;
1927
+ text-align: left;
1928
+ cursor: pointer;
1929
+ }
1930
+
1931
+ .paywall-v2-faq-question,
1932
+ .paywall-v2-faq-answer {
1933
+ margin: 0;
1934
+ }
1935
+
1936
+ .paywall-v2-faq-question {
1937
+ color: var(--color-secondary-text);
1938
+ font-size: 18px;
1939
+ font-weight: 600;
1940
+ line-height: 1.25;
1941
+ }
1942
+
1943
+ .paywall-v2-faq-answer {
1944
+ margin-top: 18px;
1945
+ color: #585a5e;
1946
+ font-size: 16px;
1947
+ font-weight: 500;
1948
+ line-height: 1.25;
1949
+ white-space: pre-line;
1950
+ }
1951
+
1952
+ .paywall-v2-faq-icon {
1953
+ position: relative;
1954
+ flex-shrink: 0;
1955
+ width: 28px;
1956
+ height: 28px;
1957
+ border-radius: 999px;
1958
+ background: #ecf7ff;
1959
+ }
1960
+
1961
+ .paywall-v2-faq-icon::before,
1962
+ .paywall-v2-faq-icon::after {
1963
+ content: '';
1964
+ position: absolute;
1965
+ top: 50%;
1966
+ left: 50%;
1967
+ width: 10px;
1968
+ height: 2px;
1969
+ border-radius: 999px;
1970
+ background: var(--color-secondary-text);
1971
+ transform: translate(-50%, -50%);
1972
+ transition: opacity 160ms ease;
1973
+ }
1974
+
1975
+ .paywall-v2-faq-icon::after {
1976
+ transform: translate(-50%, -50%) rotate(90deg);
1977
+ }
1978
+
1979
+ .paywall-v2-faq-icon.is-open::after {
1980
+ opacity: 0;
1981
+ }
1982
+
1983
+ .paywall-v2-guarantee-card {
1984
+ margin-top: 32px;
1985
+ border-radius: 16px;
1986
+ background: #fff;
1987
+ padding: 11px 16px 16px;
1988
+ display: flex;
1989
+ flex-direction: column;
1990
+ align-items: center;
1991
+ gap: 12px;
1992
+ }
1993
+
1994
+ .paywall-v2-badge {
1995
+ width: 184px;
1996
+ height: 184px;
1997
+ object-fit: cover;
1998
+ object-position: center 54%;
1999
+ }
2000
+
2001
+ .paywall-v2-guarantee-copy {
2002
+ width: 100%;
2003
+ display: flex;
2004
+ flex-direction: column;
2005
+ gap: 8px;
2006
+ text-align: center;
2007
+ }
2008
+
2009
+ .paywall-v2-guarantee-title {
2010
+ margin: 0;
2011
+ color: var(--color-secondary-text);
2012
+ font-size: 22px;
2013
+ font-weight: 600;
2014
+ line-height: 1.2;
2015
+ white-space: nowrap;
2016
+ }
2017
+
2018
+ .paywall-v2-guarantee-text {
2019
+ margin: 0;
2020
+ color: #6074e2;
2021
+ font-size: 14px;
2022
+ font-weight: 500;
2023
+ line-height: 1.25;
2024
+ }
2025
+
2026
+ .paywall-v2-guarantee-text a {
2027
+ color: #3d59f8;
2028
+ font-weight: 600;
2029
+ text-decoration: none;
2030
+ }
2031
+
2032
+ .paywall-v2-feature-grid {
2033
+ margin-top: 48px;
2034
+ }
2035
+
2036
+ .paywall-v2-feature-grid-head {
2037
+ text-align: center;
2038
+ }
2039
+
2040
+ .paywall-v2-feature-grid-title {
2041
+ margin: 0;
2042
+ color: var(--color-secondary-text);
2043
+ font-size: 28px;
2044
+ font-weight: 600;
2045
+ line-height: 1.25;
2046
+ letter-spacing: -0.036em;
2047
+ }
2048
+
2049
+ .paywall-v2-feature-grid-subtitle {
2050
+ margin: 16px 0 0;
2051
+ color: var(--color-secondary-text);
2052
+ font-size: 16px;
2053
+ font-weight: 500;
2054
+ line-height: 1.25;
2055
+ }
2056
+
2057
+ .paywall-v2-feature-card {
2058
+ border-radius: 16px;
2059
+ background: #fff;
2060
+ }
2061
+
2062
+ .paywall-v2-feature-card--large {
2063
+ margin-top: 24px;
2064
+ min-height: 220px;
2065
+ padding: 16px;
2066
+ display: flex;
2067
+ flex-direction: column;
2068
+ align-items: center;
2069
+ gap: 16px;
2070
+ }
2071
+
2072
+ .paywall-v2-feature-hero-image {
2073
+ width: 289px;
2074
+ height: 105px;
2075
+ object-fit: contain;
2076
+ flex-shrink: 0;
2077
+ }
2078
+
2079
+ .paywall-v2-feature-copy {
2080
+ width: 100%;
2081
+ display: flex;
2082
+ flex-direction: column;
2083
+ gap: 8px;
2084
+ text-align: center;
2085
+ }
2086
+
2087
+ .paywall-v2-feature-name {
2088
+ margin: 0;
2089
+ color: var(--color-secondary-text);
2090
+ font-size: 18px;
2091
+ font-weight: 600;
2092
+ line-height: 1.25;
2093
+ }
2094
+
2095
+ .paywall-v2-feature-description {
2096
+ margin: 0;
2097
+ color: #6074e2;
2098
+ font-size: 14px;
2099
+ font-weight: 500;
2100
+ line-height: 1.25;
2101
+ }
2102
+
2103
+ .paywall-v2-feature-row {
2104
+ margin-top: 24px;
2105
+ display: grid;
2106
+ grid-template-columns: repeat(2, minmax(0, 1fr));
2107
+ gap: 14px;
2108
+ }
2109
+
2110
+ .paywall-v2-feature-card--small {
2111
+ min-height: 122px;
2112
+ display: flex;
2113
+ align-items: center;
2114
+ justify-content: center;
2115
+ padding: 10px;
2116
+ }
2117
+
2118
+ .paywall-v2-feature-tile-title,
2119
+ .paywall-v2-feature-detail-title,
2120
+ .paywall-v2-feature-detail-description {
2121
+ margin: 0;
2122
+ display: flex;
2123
+ flex-direction: column;
2124
+ }
2125
+
2126
+ .paywall-v2-feature-tile-title {
2127
+ font-size: 18px;
2128
+ font-weight: 600;
2129
+ line-height: 1.25;
2130
+ text-align: center;
2131
+ }
2132
+
2133
+ .paywall-v2-feature-title--orange {
2134
+ color: var(--color-secondary);
2135
+ }
2136
+
2137
+ .paywall-v2-feature-title--green {
2138
+ color: var(--color-primary);
2139
+ }
2140
+
2141
+ .paywall-v2-feature-card--mid {
2142
+ margin-top: 24px;
2143
+ min-height: 196px;
2144
+ padding: 5px 16px 16px;
2145
+ display: flex;
2146
+ flex-direction: column;
2147
+ align-items: center;
2148
+ gap: 12px;
2149
+ }
2150
+
2151
+ .paywall-v2-feature-mid-image {
2152
+ width: 160px;
2153
+ height: 129px;
2154
+ object-fit: contain;
2155
+ flex-shrink: 0;
2156
+ }
2157
+
2158
+ .paywall-v2-feature-mid-title {
2159
+ margin: 0;
2160
+ color: var(--color-secondary-text);
2161
+ font-size: 18px;
2162
+ font-weight: 600;
2163
+ line-height: 1.25;
2164
+ text-align: center;
2165
+ }
2166
+
2167
+ .paywall-v2-feature-row--tall {
2168
+ align-items: stretch;
2169
+ }
2170
+
2171
+ .paywall-v2-feature-card--tall {
2172
+ min-height: 255px;
2173
+ padding: 16px 12px 24px;
2174
+ display: flex;
2175
+ flex-direction: column;
2176
+ align-items: center;
2177
+ gap: 14px;
2178
+ }
2179
+
2180
+ .paywall-v2-feature-detail-image {
2181
+ height: auto;
2182
+ object-fit: contain;
2183
+ flex-shrink: 0;
2184
+ }
2185
+
2186
+ .paywall-v2-feature-detail-copy {
2187
+ display: flex;
2188
+ flex-direction: column;
2189
+ gap: 8px;
2190
+ text-align: center;
2191
+ }
2192
+
2193
+ .paywall-v2-feature-detail-title {
2194
+ color: var(--color-secondary-text);
2195
+ font-size: 16px;
2196
+ font-weight: 600;
2197
+ line-height: 1.25;
2198
+ }
2199
+
2200
+ .paywall-v2-feature-detail-description {
2201
+ color: #6074e2;
2202
+ font-size: 12px;
2203
+ font-weight: 500;
2204
+ line-height: 1.25;
2205
+ }
2206
+
2207
+ @media (max-width: 429px) {
2208
+ .paywall-v2-step {
2209
+ padding: 0 16px 48px;
2210
+ }
2211
+ }
2212
+
2213
+ @container (max-width: 429px) {
2214
+ .paywall-v2-top-row {
2215
+ min-height: 92px;
2216
+ margin: 0 -16px 0;
2217
+ padding: 8px 16px 10px;
2218
+ }
2219
+
2220
+ .paywall-v2-top-controls {
2221
+ grid-template-columns: minmax(120px, 150px) minmax(128px, 1fr);
2222
+ gap: 10px;
2223
+ }
2224
+
2225
+ .paywall-v2-countdown-value {
2226
+ font-size: 40px;
2227
+ }
2228
+
2229
+ .paywall-v2-scroll-cta {
2230
+ min-height: 46px;
2231
+ padding: 0 10px;
2232
+ font-size: 15px;
2233
+ }
2234
+
2235
+ .paywall-v2-plan-row {
2236
+ gap: 10px;
2237
+ padding: 14px 12px;
2238
+ }
2239
+
2240
+ .paywall-v2-plan-body {
2241
+ gap: 10px;
2242
+ }
2243
+
2244
+ .paywall-v2-plan-right {
2245
+ min-width: 80px;
2246
+ }
2247
+
2248
+ .paywall-v2-feedback {
2249
+ margin-left: -16px;
2250
+ margin-right: -16px;
2251
+ }
2252
+ }
2253
+ `;