@hook-sdk/template 0.26.0 → 0.28.0

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.
package/dist/index.cjs CHANGED
@@ -46,10 +46,26 @@ __export(index_exports, {
46
46
  OnboardingFlow: () => OnboardingFlow,
47
47
  PaymentReturnHandler: () => PaymentReturnHandler,
48
48
  Paywall: () => Paywall,
49
+ PaywallAnchorPrice: () => PaywallAnchorPrice,
50
+ PaywallContext: () => PaywallContext,
51
+ PaywallCountdown: () => PaywallCountdown,
49
52
  PaywallCta: () => PaywallCta,
50
53
  PaywallCyclePicker: () => PaywallCyclePicker,
54
+ PaywallEyebrow: () => PaywallEyebrow,
55
+ PaywallFeatures: () => PaywallFeatures,
56
+ PaywallFeaturesCard: () => PaywallFeaturesCard,
57
+ PaywallFinePrint: () => PaywallFinePrint,
58
+ PaywallHeadline: () => PaywallHeadline,
59
+ PaywallHero: () => PaywallHero,
51
60
  PaywallMethodContent: () => PaywallMethodContent,
52
61
  PaywallMethodTabs: () => PaywallMethodTabs,
62
+ PaywallPriceHeadline: () => PaywallPriceHeadline,
63
+ PaywallProvider: () => PaywallProvider,
64
+ PaywallStatsRow: () => PaywallStatsRow,
65
+ PaywallStickyFooter: () => PaywallStickyFooter,
66
+ PaywallTestimonials: () => PaywallTestimonials,
67
+ PaywallTrophyBadge: () => PaywallTrophyBadge,
68
+ PaywallTrustLine: () => PaywallTrustLine,
53
69
  PersistenceRegistry: () => PersistenceRegistry,
54
70
  PixWaitingPageDefault: () => PixWaitingPageDefault,
55
71
  PreAuthShell: () => PreAuthShell,
@@ -80,6 +96,7 @@ __export(index_exports, {
80
96
  useInstallPrompt: () => useInstallPrompt,
81
97
  useLoginForm: () => useLoginForm,
82
98
  useOnboardingStep: () => useOnboardingStep,
99
+ usePaywallContext: () => usePaywallContext,
83
100
  usePaywallState: () => usePaywallState,
84
101
  usePlan: () => usePlan,
85
102
  usePush: () => usePush,
@@ -726,8 +743,8 @@ function usePaywallState() {
726
743
  opening: submitting,
727
744
  availableMethods: methods,
728
745
  monthlyEquivalent,
729
- dismissPix: () => {
730
- },
746
+ // G154 fix (template 0.24.0 + SDK 0.26.0): wired to SDK action.
747
+ dismissPix: subscription.clearPixPending,
731
748
  refreshPlan: () => {
732
749
  }
733
750
  };
@@ -2942,7 +2959,7 @@ function useCheckoutForm(args) {
2942
2959
  const emailConfirmError = touchedEmailConfirm || formSubmitAttempted ? validateEmailConfirm : null;
2943
2960
  const phoneError = touchedPhone || formSubmitAttempted ? validatePhone : null;
2944
2961
  const cpfError = touchedCpf || formSubmitAttempted ? validateCpf : null;
2945
- const canSubmit = name.trim().length >= 2 && EMAIL_RE.test(email) && emailConfirm === email && PHONE_RE.test(phone) && validateCpf === null && cpf.replace(/\D/g, "").length === 11 && emailStatus !== "exists" && !submitting && (method !== "card" || card.number.length >= 12 && card.ccv.length >= 3 && card.expiryMonth.length >= 1 && card.expiryYear.length >= 2 && card.holderName.length >= 1);
2962
+ const canSubmit = name.trim().length >= 2 && EMAIL_RE.test(email) && (emailConfirm === "" || emailConfirm === email) && (phone === "" || PHONE_RE.test(phone)) && validateCpf === null && cpf.replace(/\D/g, "").length === 11 && emailStatus !== "exists" && !submitting && (method !== "card" || card.number.length >= 12 && card.ccv.length >= 3 && card.expiryMonth.length >= 1 && card.expiryYear.length >= 2 && card.holderName.length >= 1);
2946
2963
  const submit = (0, import_react17.useCallback)(async () => {
2947
2964
  setFormSubmitAttempted(true);
2948
2965
  setError(null);
@@ -2957,7 +2974,7 @@ function useCheckoutForm(args) {
2957
2974
  method: "card",
2958
2975
  name: name.trim(),
2959
2976
  email,
2960
- emailConfirm,
2977
+ emailConfirm: emailConfirm || email,
2961
2978
  phone,
2962
2979
  cpf: cpf.replace(/\D/g, ""),
2963
2980
  cycle,
@@ -2984,7 +3001,7 @@ function useCheckoutForm(args) {
2984
3001
  method: "pix-auto",
2985
3002
  name: name.trim(),
2986
3003
  email,
2987
- emailConfirm,
3004
+ emailConfirm: emailConfirm || email,
2988
3005
  phone,
2989
3006
  cpf: cpf.replace(/\D/g, ""),
2990
3007
  cycle
@@ -3073,13 +3090,49 @@ function readIntent() {
3073
3090
  function formatBrl(cents) {
3074
3091
  return new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(cents / 100);
3075
3092
  }
3093
+ function formatCardNumber(v) {
3094
+ const digits = v.replace(/\D/g, "").slice(0, 16);
3095
+ return digits.replace(/(.{4})/g, "$1 ").trim();
3096
+ }
3097
+ function formatExpiryMmAa(v) {
3098
+ const d = v.replace(/\D/g, "").slice(0, 4);
3099
+ if (d.length < 3) return d;
3100
+ return d.slice(0, 2) + "/" + d.slice(2);
3101
+ }
3102
+ function parseExpiryMmAa(v) {
3103
+ const d = v.replace(/\D/g, "");
3104
+ return { month: d.slice(0, 2), year: d.slice(2, 4) };
3105
+ }
3106
+ function formatCpf(v) {
3107
+ const d = v.replace(/\D/g, "").slice(0, 11);
3108
+ if (d.length <= 3) return d;
3109
+ if (d.length <= 6) return d.slice(0, 3) + "." + d.slice(3);
3110
+ if (d.length <= 9) return d.slice(0, 3) + "." + d.slice(3, 6) + "." + d.slice(6);
3111
+ return d.slice(0, 3) + "." + d.slice(3, 6) + "." + d.slice(6, 9) + "-" + d.slice(9);
3112
+ }
3113
+ function detectCardBrand(num) {
3114
+ const n = num.replace(/\s/g, "");
3115
+ if (/^4/.test(n)) return "VISA";
3116
+ if (/^(5[1-5]|2[2-7])/.test(n)) return "MASTER";
3117
+ if (/^3[47]/.test(n)) return "AMEX";
3118
+ if (/^(4011|4312|4389|4514|6011|6362|6363)/.test(n)) return "ELO";
3119
+ if (/^(606282|3841)/.test(n)) return "HIPER";
3120
+ return "";
3121
+ }
3076
3122
  function CheckoutPageDefault() {
3077
3123
  const navigate = (0, import_react_router_dom3.useNavigate)();
3078
3124
  const plan = usePlan();
3079
3125
  const intent = (0, import_react18.useMemo)(readIntent, []);
3080
3126
  const defaultMethod = intent.method === "pix-auto" ? "pix-auto" : "card";
3081
- const defaultCycle = intent.cycle === "YEARLY" ? "YEARLY" : "MONTHLY";
3127
+ const defaultCycle = intent.cycle === "MONTHLY" ? "MONTHLY" : "YEARLY";
3082
3128
  const form = useCheckoutForm({ defaultMethod, defaultCycle });
3129
+ const [expiryMmAa, setExpiryMmAa] = (0, import_react18.useState)("");
3130
+ (0, import_react18.useEffect)(() => {
3131
+ const { month, year } = parseExpiryMmAa(expiryMmAa);
3132
+ if (month !== form.card.expiryMonth || year !== form.card.expiryYear) {
3133
+ form.setCard({ expiryMonth: month, expiryYear: year });
3134
+ }
3135
+ }, [expiryMmAa]);
3083
3136
  (0, import_react18.useEffect)(() => {
3084
3137
  if (form.emailTaken && form.loginUrl) {
3085
3138
  const t = setTimeout(() => navigate(form.loginUrl), 1200);
@@ -3091,21 +3144,30 @@ function CheckoutPageDefault() {
3091
3144
  yearlyPriceCents: plan.data.yearlyPriceCents,
3092
3145
  trialDays: plan.data.trialDays
3093
3146
  } : null;
3147
+ const annual = form.cycle === "YEARLY";
3094
3148
  const cyclePrice = (0, import_react18.useMemo)(() => {
3095
3149
  if (!planInfo) return null;
3096
- return form.cycle === "YEARLY" ? planInfo.yearlyPriceCents ?? planInfo.priceCents * 12 : planInfo.priceCents;
3097
- }, [planInfo, form.cycle]);
3098
- const submitLabel = (0, import_react18.useMemo)(() => {
3099
- if (form.submitting) return "Processando\u2026";
3100
- if (form.method === "card") {
3101
- const trial = planInfo?.trialDays ?? 0;
3102
- if (trial > 0 && cyclePrice) {
3103
- return `Come\xE7ar ${trial} dias gr\xE1tis \xB7 ${formatBrl(cyclePrice)} depois`;
3104
- }
3105
- return cyclePrice ? `Pagar ${formatBrl(cyclePrice)}` : "Continuar";
3106
- }
3107
- return cyclePrice ? `Pagar ${formatBrl(cyclePrice)} via PIX` : "Continuar via PIX";
3108
- }, [form.method, form.submitting, planInfo, cyclePrice]);
3150
+ return annual ? planInfo.yearlyPriceCents ?? planInfo.priceCents * 12 : planInfo.priceCents;
3151
+ }, [planInfo, annual]);
3152
+ const monthlyText = (0, import_react18.useMemo)(() => {
3153
+ if (!planInfo) return "";
3154
+ const monthly = annual && planInfo.yearlyPriceCents ? Math.round(planInfo.yearlyPriceCents / 12) : planInfo.priceCents;
3155
+ return formatBrl(monthly);
3156
+ }, [planInfo, annual]);
3157
+ const todayCents = (0, import_react18.useMemo)(() => {
3158
+ if (form.method === "pix-auto") return cyclePrice ?? 0;
3159
+ const trialDays2 = planInfo?.trialDays ?? 0;
3160
+ if (trialDays2 > 0) return 0;
3161
+ return cyclePrice ?? 0;
3162
+ }, [form.method, cyclePrice, planInfo]);
3163
+ const todayAmount = formatBrl(todayCents);
3164
+ const cyclePriceText = cyclePrice !== null ? formatBrl(cyclePrice) : "";
3165
+ const annualSavingsCents = (0, import_react18.useMemo)(() => {
3166
+ if (!planInfo || !planInfo.yearlyPriceCents) return 0;
3167
+ return planInfo.priceCents * 12 - planInfo.yearlyPriceCents;
3168
+ }, [planInfo]);
3169
+ const trialDays = planInfo?.trialDays ?? 7;
3170
+ const cardBrand = detectCardBrand(form.card.number);
3109
3171
  async function onSubmit(e) {
3110
3172
  e.preventDefault();
3111
3173
  const result = await form.submit();
@@ -3128,201 +3190,367 @@ function CheckoutPageDefault() {
3128
3190
  }
3129
3191
  navigate(result.redirect.replace(/^.*\/app\/[^/]+/, "") || "/");
3130
3192
  }
3131
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "flex-1 flex flex-col bg-background min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("form", { onSubmit, className: "flex-1 overflow-y-auto px-5 py-6 space-y-6", children: [
3132
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("header", { className: "space-y-2", children: [
3133
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("h1", { className: "font-display text-2xl text-foreground", children: "Finalizar assinatura" }),
3134
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm text-muted-foreground", children: "Preencha seus dados pra liberar o app. Voc\xEA j\xE1 entra com acesso na hora." })
3135
- ] }),
3136
- form.emailTaken ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { role: "alert", className: "rounded-xl bg-destructive/10 p-4 text-sm text-destructive border border-destructive/20", children: [
3193
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "flex-1 flex flex-col bg-background min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("form", { onSubmit, className: "flex-1 overflow-y-auto", children: [
3194
+ form.emailTaken ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "px-5 pt-4", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { role: "alert", className: "rounded-2xl bg-destructive/10 p-4 text-sm text-destructive border border-destructive/20", children: [
3137
3195
  "Esse e-mail j\xE1 tem conta nesse app.",
3138
3196
  " ",
3139
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("a", { href: form.loginUrl ?? "/signin", className: "underline font-medium", children: "Entrar agora" })
3140
- ] }) : null,
3141
- form.error ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { role: "alert", className: "rounded-xl bg-destructive/10 p-4 text-sm text-destructive border border-destructive/20", children: form.error.message || "N\xE3o foi poss\xEDvel concluir o pagamento. Tente novamente." }) : null,
3142
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("section", { className: "space-y-3", children: [
3143
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("h2", { className: "text-sm font-semibold text-foreground uppercase tracking-wide", children: "Voc\xEA" }),
3144
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3145
- FieldRow,
3146
- {
3147
- label: "Nome completo",
3148
- value: form.name,
3149
- onChange: form.setName,
3150
- onBlur: form.markNameTouched,
3151
- error: form.nameError,
3152
- autoComplete: "name"
3153
- }
3154
- ),
3197
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("a", { href: form.loginUrl ?? "/signin", className: "underline font-semibold", children: "Entrar agora" })
3198
+ ] }) }) : null,
3199
+ form.error ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "px-5 pt-4", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { role: "alert", className: "rounded-2xl bg-destructive/10 p-4 text-sm text-destructive border border-destructive/20", children: form.error.message || "N\xE3o foi poss\xEDvel concluir o pagamento. Tente novamente." }) }) : null,
3200
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "px-5 pt-4", children: form.method === "card" ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "rounded-2xl bg-card border-[1.5px] border-foreground p-3.5", children: [
3201
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [
3202
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ShieldIcon, { className: "w-4 h-4" }),
3203
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "text-sm font-bold", children: "Voc\xEA N\xC3O ser\xE1 cobrada hoje" })
3204
+ ] }),
3205
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex justify-between items-baseline text-sm text-muted-foreground", children: [
3206
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: "R$ 0,00 agora" }),
3207
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "opacity-50", children: "\xB7" }),
3208
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { children: [
3209
+ monthlyText,
3210
+ "/m\xEAs ap\xF3s ",
3211
+ trialDays,
3212
+ " dias"
3213
+ ] })
3214
+ ] }),
3215
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "mt-2.5 text-[11px] text-muted-foreground flex items-center gap-1.5", children: [
3216
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(BellIcon, { className: "w-2.5 h-2.5" }),
3217
+ "Avisamos por email 2 dias antes da primeira cobran\xE7a"
3218
+ ] })
3219
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "rounded-2xl p-3.5 bg-emerald-50 border-[1.5px] border-emerald-600/60", children: [
3220
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center gap-2 mb-2 text-emerald-900", children: [
3221
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ShieldIcon, { className: "w-4 h-4" }),
3222
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-sm font-bold", children: [
3223
+ "Garantia incondicional de ",
3224
+ trialDays,
3225
+ " dias"
3226
+ ] })
3227
+ ] }),
3228
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-sm text-emerald-900 leading-snug", children: [
3229
+ "Voc\xEA paga ",
3230
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("b", { children: todayAmount }),
3231
+ " agora via Pix.",
3232
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("br", {}),
3233
+ "N\xE3o gostou em ",
3234
+ trialDays,
3235
+ " dias? Devolvemos ",
3236
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("b", { children: "100%" }),
3237
+ " sem perguntas \u2014 direto pelo app."
3238
+ ] })
3239
+ ] }) }),
3240
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("section", { className: "px-5 pt-5", children: [
3241
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("h2", { className: "font-display text-2xl mb-3.5 leading-tight text-foreground", children: "Quase l\xE1." }),
3242
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldLabel, { children: "Email" }),
3155
3243
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3156
- FieldRow,
3244
+ FieldInput,
3157
3245
  {
3158
- label: "E-mail",
3159
3246
  type: "email",
3160
- value: form.email,
3161
- onChange: form.setEmail,
3162
- onBlur: form.markEmailTouched,
3163
- error: form.emailError,
3247
+ inputMode: "email",
3164
3248
  autoComplete: "email",
3165
3249
  autoCapitalize: "none",
3166
3250
  autoCorrect: "off",
3167
3251
  spellCheck: false,
3168
- hint: form.emailStatus === "checking" ? "Verificando\u2026" : form.emailStatus === "available" ? "\u2713 Dispon\xEDvel" : form.emailStatus === "exists" ? null : null
3169
- }
3170
- ),
3171
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3172
- FieldRow,
3173
- {
3174
- label: "Confirme o e-mail",
3175
- type: "email",
3176
- value: form.emailConfirm,
3177
- onChange: form.setEmailConfirm,
3178
- onBlur: form.markEmailConfirmTouched,
3179
- error: form.emailConfirmError,
3180
- autoComplete: "email",
3181
- autoCapitalize: "none",
3182
- autoCorrect: "off",
3183
- spellCheck: false
3252
+ placeholder: "seu@email.com",
3253
+ value: form.email,
3254
+ onChange: form.setEmail,
3255
+ onBlur: form.markEmailTouched,
3256
+ error: form.emailError,
3257
+ valid: form.emailStatus === "available"
3184
3258
  }
3185
3259
  ),
3260
+ !form.emailError && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldHint, { children: form.emailStatus === "checking" ? "Verificando\u2026" : form.emailStatus === "available" ? "\u2713 Dispon\xEDvel" : "Voc\xEA vai usar este email para entrar no app" }),
3261
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-3" }),
3262
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldLabel, { children: "Nome completo" }),
3186
3263
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3187
- FieldRow,
3264
+ FieldInput,
3188
3265
  {
3189
- label: "Telefone",
3190
- type: "tel",
3191
- value: form.phone,
3192
- onChange: form.setPhone,
3193
- onBlur: form.markPhoneTouched,
3194
- error: form.phoneError,
3195
- autoComplete: "tel",
3196
- placeholder: "(11) 99999-9999"
3266
+ type: "text",
3267
+ autoComplete: "name",
3268
+ placeholder: "como est\xE1 no documento",
3269
+ value: form.name,
3270
+ onChange: form.setName,
3271
+ onBlur: form.markNameTouched,
3272
+ error: form.nameError,
3273
+ valid: !!form.name && !form.nameError
3197
3274
  }
3198
3275
  ),
3276
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-3" }),
3277
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldLabel, { children: "CPF" }),
3199
3278
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3200
- FieldRow,
3279
+ FieldInput,
3201
3280
  {
3202
- label: "CPF",
3203
3281
  type: "text",
3204
3282
  inputMode: "numeric",
3283
+ placeholder: "000.000.000-00",
3205
3284
  value: form.cpf,
3206
- onChange: form.setCpf,
3285
+ onChange: (v) => form.setCpf(formatCpf(v)),
3207
3286
  onBlur: form.markCpfTouched,
3208
3287
  error: form.cpfError,
3209
- autoComplete: "off",
3210
- placeholder: "000.000.000-00"
3288
+ valid: !!form.cpf && !form.cpfError
3211
3289
  }
3212
3290
  )
3213
3291
  ] }),
3214
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("section", { className: "space-y-3", children: [
3215
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("h2", { className: "text-sm font-semibold text-foreground uppercase tracking-wide", children: "Pagamento" }),
3216
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "grid grid-cols-2 gap-2", role: "tablist", children: [
3217
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(MethodTab, { active: form.method === "card", onClick: () => form.setMethod("card"), children: "Cart\xE3o" }),
3218
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(MethodTab, { active: form.method === "pix-auto", onClick: () => form.setMethod("pix-auto"), children: "PIX" })
3219
- ] }),
3220
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "grid grid-cols-2 gap-2", role: "tablist", children: [
3221
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(MethodTab, { active: form.cycle === "MONTHLY", onClick: () => form.setCycle("MONTHLY"), children: "Mensal" }),
3222
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(MethodTab, { active: form.cycle === "YEARLY", onClick: () => form.setCycle("YEARLY"), children: "Anual" })
3223
- ] }),
3224
- form.method === "card" ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "space-y-3 rounded-xl border border-border p-4", children: [
3292
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("section", { className: "px-5 pt-5", children: [
3293
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldLabel, { children: "Forma de pagamento" }),
3294
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { role: "tablist", className: "flex gap-1.5 bg-muted p-1 rounded-xl", children: [
3225
3295
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3226
- FieldRow,
3296
+ TabButton,
3227
3297
  {
3228
- label: "Nome no cart\xE3o",
3229
- value: form.card.holderName,
3230
- onChange: (v) => form.setCard({ holderName: v }),
3231
- autoComplete: "cc-name"
3298
+ active: form.method === "card",
3299
+ onClick: () => form.setMethod("card"),
3300
+ icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(CardIcon, { className: "w-3.5 h-3.5" }),
3301
+ label: "Cart\xE3o",
3302
+ subtitle: trialDays > 0 ? `${trialDays} dias gr\xE1tis` : "pague hoje",
3303
+ subtitleActiveClass: "text-emerald-700"
3232
3304
  }
3233
3305
  ),
3234
3306
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3235
- FieldRow,
3307
+ TabButton,
3236
3308
  {
3237
- label: "N\xFAmero",
3238
- value: form.card.number,
3239
- onChange: (v) => form.setCard({ number: v }),
3240
- autoComplete: "cc-number",
3309
+ active: form.method === "pix-auto",
3310
+ onClick: () => form.setMethod("pix-auto"),
3311
+ icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(PixIcon, { className: "w-3.5 h-3.5" }),
3312
+ label: "Pix",
3313
+ subtitle: `pague hoje \xB7 garantia ${trialDays}d`,
3314
+ subtitleActiveClass: "text-foreground/70"
3315
+ }
3316
+ )
3317
+ ] })
3318
+ ] }),
3319
+ form.method === "card" ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("section", { className: "px-5 pt-3.5", children: [
3320
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldLabel, { children: "N\xFAmero do cart\xE3o" }),
3321
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "relative", children: [
3322
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3323
+ FieldInput,
3324
+ {
3325
+ type: "text",
3241
3326
  inputMode: "numeric",
3242
- placeholder: "0000 0000 0000 0000"
3327
+ autoComplete: "cc-number",
3328
+ placeholder: "0000 0000 0000 0000",
3329
+ value: form.card.number,
3330
+ onChange: (v) => form.setCard({ number: formatCardNumber(v) }),
3331
+ style: cardBrand ? { paddingRight: "4.5rem" } : void 0
3243
3332
  }
3244
3333
  ),
3245
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
3334
+ cardBrand && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "absolute right-3 top-1/2 -translate-y-1/2 text-[10px] font-bold tracking-wide px-1.5 py-0.5 rounded bg-muted text-muted-foreground", children: cardBrand })
3335
+ ] }),
3336
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-3" }),
3337
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex gap-2.5", children: [
3338
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex-1", children: [
3339
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldLabel, { children: "Validade" }),
3246
3340
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3247
- FieldRow,
3341
+ FieldInput,
3248
3342
  {
3249
- label: "M\xEAs",
3250
- value: form.card.expiryMonth,
3251
- onChange: (v) => form.setCard({ expiryMonth: v }),
3252
- autoComplete: "cc-exp-month",
3343
+ type: "text",
3253
3344
  inputMode: "numeric",
3254
- placeholder: "MM"
3345
+ autoComplete: "cc-exp",
3346
+ placeholder: "MM/AA",
3347
+ value: expiryMmAa,
3348
+ onChange: (v) => setExpiryMmAa(formatExpiryMmAa(v))
3255
3349
  }
3256
- ),
3350
+ )
3351
+ ] }),
3352
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex-1", children: [
3353
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldLabel, { children: "CVV" }),
3257
3354
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3258
- FieldRow,
3355
+ FieldInput,
3259
3356
  {
3260
- label: "Ano",
3261
- value: form.card.expiryYear,
3262
- onChange: (v) => form.setCard({ expiryYear: v }),
3263
- autoComplete: "cc-exp-year",
3357
+ type: "text",
3264
3358
  inputMode: "numeric",
3265
- placeholder: "AA"
3266
- }
3267
- ),
3268
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3269
- FieldRow,
3270
- {
3271
- label: "CVV",
3272
- value: form.card.ccv,
3273
- onChange: (v) => form.setCard({ ccv: v }),
3274
3359
  autoComplete: "cc-csc",
3275
- inputMode: "numeric",
3276
- placeholder: "123"
3360
+ placeholder: "3 d\xEDgitos",
3361
+ value: form.card.ccv,
3362
+ onChange: (v) => form.setCard({ ccv: v.replace(/\D/g, "").slice(0, 4) })
3277
3363
  }
3278
3364
  )
3279
3365
  ] })
3280
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "rounded-xl border border-border p-4 text-sm text-muted-foreground", children: "Ap\xF3s confirmar, mostraremos o QR Code PIX. Pagamento \xE0 vista." })
3281
- ] }),
3282
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3283
- "button",
3284
- {
3285
- type: "submit",
3286
- disabled: !form.canSubmit,
3287
- className: "w-full rounded-xl bg-primary py-4 text-base font-semibold text-primary-foreground disabled:opacity-50 disabled:cursor-not-allowed",
3288
- children: submitLabel
3289
- }
3290
- ),
3291
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-center text-xs text-muted-foreground", children: "\u{1F512} SSL \xB7 via Asaas \xB7 Garantia 7 dias" })
3366
+ ] }),
3367
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-3" }),
3368
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FieldLabel, { children: "Nome no cart\xE3o" }),
3369
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3370
+ FieldInput,
3371
+ {
3372
+ type: "text",
3373
+ autoComplete: "cc-name",
3374
+ placeholder: "como est\xE1 no cart\xE3o",
3375
+ value: form.card.holderName,
3376
+ onChange: (v) => form.setCard({ holderName: v })
3377
+ }
3378
+ )
3379
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("section", { className: "px-5 pt-3.5", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "rounded-2xl bg-card border border-border p-3.5 flex gap-3.5 items-center", children: [
3380
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "w-[72px] h-[72px] rounded-xl shrink-0 border-2 border-foreground relative overflow-hidden bg-muted", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[22px] h-[22px] bg-background flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(PixIcon, { className: "w-3.5 h-3.5 text-foreground" }) }) }),
3381
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex-1", children: [
3382
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "text-xs font-bold uppercase tracking-wider text-muted-foreground", children: "pagamento em segundos" }),
3383
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-sm text-foreground mt-1 leading-snug", children: [
3384
+ "Geramos seu ",
3385
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("b", { children: "QR Pix" }),
3386
+ " no pr\xF3ximo passo. Pague pelo app do banco e seu acesso libera ",
3387
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("b", { children: "imediatamente" }),
3388
+ "."
3389
+ ] })
3390
+ ] })
3391
+ ] }) }),
3392
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("section", { className: "px-5 pt-5", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "bg-muted rounded-2xl p-4", children: [
3393
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex justify-between mb-2.5", children: [
3394
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { children: [
3395
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "text-sm font-semibold text-foreground", children: annual ? "Plano Anual" : "Plano Mensal" }),
3396
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "text-[11px] text-muted-foreground", children: "Coach" })
3397
+ ] }),
3398
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-right", children: [
3399
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-sm font-bold text-foreground", children: [
3400
+ cyclePriceText,
3401
+ "/",
3402
+ annual ? "ano" : "m\xEAs"
3403
+ ] }),
3404
+ annual && annualSavingsCents > 0 && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-[11px] text-emerald-700 font-semibold", children: [
3405
+ "economia ",
3406
+ formatBrl(annualSavingsCents)
3407
+ ] })
3408
+ ] })
3409
+ ] }),
3410
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-px bg-border my-3" }),
3411
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex justify-between items-baseline", children: [
3412
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { children: [
3413
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "text-sm font-bold text-foreground", children: "Voc\xEA paga hoje" }),
3414
+ form.method === "card" && trialDays > 0 && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-[11px] text-muted-foreground mt-0.5", children: [
3415
+ "cobran\xE7a inicia no dia ",
3416
+ trialDays
3417
+ ] }),
3418
+ form.method === "pix-auto" && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-[11px] text-emerald-700 mt-0.5 font-semibold", children: [
3419
+ "reembolso garantido at\xE9 o dia ",
3420
+ trialDays
3421
+ ] })
3422
+ ] }),
3423
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "text-2xl font-bold font-display tracking-tight text-foreground", children: todayAmount })
3424
+ ] })
3425
+ ] }) }),
3426
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-4" }),
3427
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "sticky bottom-0 px-5 pt-3.5 pb-6 bg-gradient-to-b from-transparent to-background", children: [
3428
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3429
+ "button",
3430
+ {
3431
+ type: "submit",
3432
+ disabled: !form.canSubmit,
3433
+ className: "w-full rounded-full bg-primary text-primary-foreground min-h-14 px-5 text-base font-bold inline-flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed shadow-lg",
3434
+ children: form.submitting ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
3435
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Spinner2, {}),
3436
+ " ",
3437
+ form.method === "pix-auto" ? "Gerando QR\u2026" : "Confirmando\u2026"
3438
+ ] }) : form.method === "card" ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
3439
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(LockIcon, { className: "w-3.5 h-3.5" }),
3440
+ " Confirmar e come\xE7ar gr\xE1tis"
3441
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
3442
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(PixIcon, { className: "w-3.5 h-3.5" }),
3443
+ " Gerar QR \xB7 pagar ",
3444
+ todayAmount
3445
+ ] })
3446
+ }
3447
+ ),
3448
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "text-center mt-2.5 text-xs text-muted-foreground", children: [
3449
+ "Ao continuar, voc\xEA concorda com nossos ",
3450
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("u", { children: "Termos" }),
3451
+ ". Pagamento seguro Asaas."
3452
+ ] }),
3453
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "mt-3 flex items-center justify-center gap-3.5 text-[11px] text-muted-foreground", children: [
3454
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
3455
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(LockIcon, { className: "w-2.5 h-2.5 opacity-60" }),
3456
+ " SSL 256-bit"
3457
+ ] }),
3458
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "w-px h-2.5 bg-border" }),
3459
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: "Pagamento via Asaas" }),
3460
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "w-px h-2.5 bg-border" }),
3461
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { children: [
3462
+ "Garantia ",
3463
+ trialDays,
3464
+ " dias"
3465
+ ] })
3466
+ ] })
3467
+ ] })
3292
3468
  ] }) });
3293
3469
  }
3294
- function FieldRow(props) {
3295
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("label", { className: "block space-y-1", children: [
3296
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "block text-sm font-medium text-foreground", children: props.label }),
3470
+ function FieldLabel({ children }) {
3471
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground mb-1.5", children });
3472
+ }
3473
+ function FieldInput(props) {
3474
+ const baseClass = "w-full px-4 rounded-xl bg-card text-base text-foreground outline-none border-[1.5px] transition-colors";
3475
+ const stateClass = props.error ? "border-destructive focus:border-destructive" : props.valid ? "border-emerald-600 focus:border-emerald-700" : "border-border focus:border-foreground";
3476
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
3297
3477
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3298
3478
  "input",
3299
3479
  {
3300
3480
  type: props.type ?? "text",
3301
- value: props.value,
3302
- onChange: (e) => props.onChange(e.target.value),
3303
- onBlur: props.onBlur,
3481
+ inputMode: props.inputMode,
3304
3482
  autoComplete: props.autoComplete,
3305
3483
  autoCapitalize: props.autoCapitalize,
3306
3484
  autoCorrect: props.autoCorrect,
3307
3485
  spellCheck: props.spellCheck,
3308
- inputMode: props.inputMode,
3309
3486
  placeholder: props.placeholder,
3310
- className: `w-full rounded-xl border bg-card px-3 py-2 text-base text-foreground outline-none focus:ring-2 focus:ring-primary ${props.error ? "border-destructive" : "border-border"}`
3487
+ value: props.value,
3488
+ onChange: (e) => props.onChange(e.target.value),
3489
+ onBlur: props.onBlur,
3490
+ style: { height: "52px", ...props.style },
3491
+ className: `${baseClass} ${stateClass}`
3311
3492
  }
3312
3493
  ),
3313
- props.error ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "block text-xs text-destructive", children: props.error }) : props.hint ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "block text-xs text-muted-foreground", children: props.hint }) : null
3494
+ props.error ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "mt-1.5 text-xs text-destructive font-medium", children: props.error }) : null
3314
3495
  ] });
3315
3496
  }
3316
- function MethodTab({ active, onClick, children }) {
3317
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3497
+ function FieldHint({ children }) {
3498
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "mt-1.5 text-xs text-muted-foreground", children });
3499
+ }
3500
+ function TabButton({ active, onClick, icon, label, subtitle, subtitleActiveClass }) {
3501
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
3318
3502
  "button",
3319
3503
  {
3320
3504
  type: "button",
3321
3505
  role: "tab",
3322
3506
  "aria-selected": active,
3323
3507
  onClick,
3324
- className: `rounded-xl border px-4 py-2 text-sm font-medium transition ${active ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-foreground"}`,
3325
- children
3508
+ className: `flex-1 flex flex-col items-center justify-center gap-0.5 py-2.5 px-1.5 rounded-lg text-sm font-semibold transition-colors ${active ? "bg-card text-foreground shadow-sm" : "bg-transparent text-muted-foreground"}`,
3509
+ style: { minHeight: 56 },
3510
+ children: [
3511
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "inline-flex items-center gap-1.5", children: [
3512
+ icon,
3513
+ " ",
3514
+ label
3515
+ ] }),
3516
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: `text-[10px] font-medium ${active ? subtitleActiveClass : "text-muted-foreground/70"}`, children: subtitle })
3517
+ ]
3518
+ }
3519
+ );
3520
+ }
3521
+ function CardIcon({ className }) {
3522
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinejoin: "round", "aria-hidden": "true", children: [
3523
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("rect", { x: "2.5", y: "5.5", width: "19", height: "13", rx: "2.5" }),
3524
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { d: "M2.5 10h19" })
3525
+ ] });
3526
+ }
3527
+ function PixIcon({ className }) {
3528
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("svg", { className, viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { d: "M12 2L2 12l10 10 10-10L12 2zm0 4.83L17.17 12 12 17.17 6.83 12 12 6.83z" }) });
3529
+ }
3530
+ function LockIcon({ className }) {
3531
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
3532
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("rect", { x: "4", y: "10", width: "16", height: "11", rx: "2.5" }),
3533
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { d: "M7.5 10V7a4.5 4.5 0 119 0v3" })
3534
+ ] });
3535
+ }
3536
+ function ShieldIcon({ className }) {
3537
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
3538
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { d: "M12 2.5l8 3v6c0 5-3.5 8.5-8 10-4.5-1.5-8-5-8-10v-6l8-3z" }),
3539
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { d: "M9 12l2 2 4-4" })
3540
+ ] });
3541
+ }
3542
+ function BellIcon({ className }) {
3543
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
3544
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { d: "M6 17V11a6 6 0 1112 0v6l1.5 2H4.5L6 17z" }),
3545
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { d: "M10 21a2 2 0 004 0" })
3546
+ ] });
3547
+ }
3548
+ function Spinner2() {
3549
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3550
+ "span",
3551
+ {
3552
+ className: "w-4 h-4 rounded-full border-2 border-white/40 border-t-white",
3553
+ style: { animation: "spin 0.7s linear infinite" }
3326
3554
  }
3327
3555
  );
3328
3556
  }
@@ -3959,25 +4187,42 @@ function useFeature(name) {
3959
4187
  }
3960
4188
 
3961
4189
  // src/components/paywall/Paywall.tsx
3962
- var import_react29 = require("react");
4190
+ var import_react31 = require("react");
3963
4191
  var import_sdk24 = require("@hook-sdk/sdk");
3964
4192
 
3965
- // src/components/paywall/PaywallMethodTabs.tsx
4193
+ // src/components/paywall/PaywallProvider.tsx
4194
+ var import_react29 = require("react");
3966
4195
  var import_jsx_runtime32 = require("react/jsx-runtime");
4196
+ var PaywallContext = (0, import_react29.createContext)(null);
4197
+ function PaywallProvider({ children }) {
4198
+ const state = usePaywallState();
4199
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(PaywallContext.Provider, { value: state, children });
4200
+ }
4201
+
4202
+ // src/components/paywall/usePaywallContext.ts
4203
+ var import_react30 = require("react");
4204
+ function usePaywallContext() {
4205
+ const ctx = (0, import_react30.useContext)(PaywallContext);
4206
+ if (!ctx) {
4207
+ throw new Error("usePaywallContext must be used within <PaywallProvider>");
4208
+ }
4209
+ return ctx;
4210
+ }
4211
+
4212
+ // src/components/paywall/PaywallMethodTabs.tsx
4213
+ var import_jsx_runtime33 = require("react/jsx-runtime");
3967
4214
  function PaywallMethodTabs({
3968
- methods,
3969
- selected,
3970
- onSelect,
3971
4215
  labels,
3972
4216
  className,
3973
4217
  tabClassName,
3974
4218
  tabActiveClassName
3975
4219
  }) {
4220
+ const { methods, selectedMethod, selectMethod } = usePaywallContext();
3976
4221
  if (methods.length < 2) return null;
3977
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { role: "tablist", "aria-label": "M\xE9todo de pagamento", className, children: methods.map((m) => {
3978
- const active = m === selected;
4222
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { role: "tablist", "aria-label": "M\xE9todo de pagamento", className, children: methods.map((m) => {
4223
+ const active = m === selectedMethod;
3979
4224
  const label = labels[m] ?? m;
3980
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4225
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3981
4226
  "button",
3982
4227
  {
3983
4228
  type: "button",
@@ -3985,7 +4230,7 @@ function PaywallMethodTabs({
3985
4230
  "aria-selected": active,
3986
4231
  "aria-controls": `paywall-tab-${m}`,
3987
4232
  tabIndex: active ? 0 : -1,
3988
- onClick: () => onSelect(m),
4233
+ onClick: () => selectMethod(m),
3989
4234
  className: [tabClassName, active ? tabActiveClassName : ""].filter(Boolean).join(" "),
3990
4235
  children: label
3991
4236
  },
@@ -3995,36 +4240,51 @@ function PaywallMethodTabs({
3995
4240
  }
3996
4241
 
3997
4242
  // src/components/paywall/PaywallMethodContent.tsx
3998
- var import_jsx_runtime33 = require("react/jsx-runtime");
3999
- function PaywallMethodContent({
4000
- method,
4001
- copy,
4002
- hasConsumedTrial = false,
4003
- className,
4004
- rowClassName
4005
- }) {
4006
- const useCardConsumed = method === "card" && hasConsumedTrial && copy.cardConsumedTrial;
4007
- const rows = useCardConsumed ? copy.cardConsumedTrial.bodyRows : method === "pix-auto" || method === "pix-once" ? copy.pix.bodyRows : copy.card.bodyRows;
4008
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { role: "tabpanel", id: `paywall-tab-${method}`, className, children: rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: rowClassName, children: row }, i)) });
4243
+ var import_jsx_runtime34 = require("react/jsx-runtime");
4244
+ function PaywallMethodContent({ copy, className, rowClassName }) {
4245
+ const { selectedMethod, hasConsumedTrial } = usePaywallContext();
4246
+ const useCardConsumed = selectedMethod === "card" && hasConsumedTrial && copy.cardConsumedTrial;
4247
+ const rows = useCardConsumed ? copy.cardConsumedTrial.bodyRows : selectedMethod === "pix-auto" || selectedMethod === "pix-once" ? copy.pix.bodyRows : copy.card.bodyRows;
4248
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { role: "tabpanel", id: `paywall-tab-${selectedMethod}`, className, children: rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: rowClassName, children: row }, i)) });
4009
4249
  }
4010
4250
 
4011
4251
  // src/components/paywall/PaywallCyclePicker.tsx
4012
- var import_jsx_runtime34 = require("react/jsx-runtime");
4252
+ var import_jsx_runtime35 = require("react/jsx-runtime");
4253
+ var VARIANT_CLASSES = {
4254
+ default: { card: "", cardSelected: "" },
4255
+ "premium-gold": {
4256
+ card: "",
4257
+ cardSelected: "border-2 border-yellow-400/80 ring-2 ring-yellow-400/20"
4258
+ },
4259
+ "pink-pill": {
4260
+ card: "rounded-2xl",
4261
+ cardSelected: "border-2 border-pink-500"
4262
+ }
4263
+ };
4013
4264
  function PaywallCyclePicker({
4014
- cycles,
4015
- selected,
4016
- onSelect,
4017
- priceCentsByCycle,
4018
- anchorCentsByCycle,
4019
- monthlyEquivByCycle,
4020
4265
  labels,
4021
4266
  className,
4022
4267
  cardClassName,
4023
4268
  cardSelectedClassName,
4024
- anchorClassName
4269
+ anchorClassName,
4270
+ variant = "default",
4271
+ render
4025
4272
  }) {
4273
+ const ctx = usePaywallContext();
4274
+ const { cycle: selected, setCycle, plan, anchorPriceCents } = ctx;
4275
+ const cycles = ["MONTHLY", "YEARLY"];
4276
+ if (render) {
4277
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className, children: render({ cycles, selected, setCycle, plan, anchorPriceCents }) });
4278
+ }
4026
4279
  if (cycles.length < 2) return null;
4027
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4280
+ const v = VARIANT_CLASSES[variant];
4281
+ const composedCardClassName = [v.card, cardClassName].filter(Boolean).join(" ");
4282
+ const composedCardSelectedClassName = [v.cardSelected, cardSelectedClassName].filter(Boolean).join(" ");
4283
+ const monthlyCents = plan?.monthlyCents ?? 0;
4284
+ const yearlyCents = plan?.yearlyCents ?? 0;
4285
+ const anchorMonthly = plan?.anchorMonthlyCents ?? null;
4286
+ const anchorYearly = plan?.anchorYearlyCents ?? null;
4287
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4028
4288
  "div",
4029
4289
  {
4030
4290
  role: "radiogroup",
@@ -4034,21 +4294,25 @@ function PaywallCyclePicker({
4034
4294
  const active = c === selected;
4035
4295
  const label = c === "YEARLY" ? labels.annualLabel : labels.monthlyLabel;
4036
4296
  const suffix = c === "YEARLY" ? labels.annualSuffix : labels.monthlySuffix;
4037
- const mainCents = c === "YEARLY" ? monthlyEquivByCycle[c] : priceCentsByCycle[c];
4038
- const anchorCents = anchorCentsByCycle[c];
4039
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
4297
+ const mainCents = c === "YEARLY" ? Math.round(yearlyCents / 12) : monthlyCents;
4298
+ const anchorCents = c === "YEARLY" ? anchorYearly : anchorMonthly;
4299
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
4040
4300
  "button",
4041
4301
  {
4042
4302
  type: "button",
4043
4303
  role: "radio",
4044
4304
  "aria-checked": active,
4045
- onClick: () => onSelect(c),
4046
- className: ["flex flex-col items-center gap-0.5", cardClassName, active ? cardSelectedClassName : ""].filter(Boolean).join(" "),
4305
+ onClick: () => setCycle(c),
4306
+ className: [
4307
+ "flex flex-col items-center gap-0.5",
4308
+ composedCardClassName,
4309
+ active ? composedCardSelectedClassName : ""
4310
+ ].filter(Boolean).join(" "),
4047
4311
  children: [
4048
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "font-bold text-base leading-tight", children: formatBRL(mainCents) }),
4049
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "text-xs opacity-70 leading-tight", children: suffix }),
4050
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "text-xs opacity-60 leading-tight", children: label }),
4051
- anchorCents != null && anchorCents > mainCents ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: anchorClassName ?? "text-xs opacity-50", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("s", { children: formatBRL(anchorCents) }) }) : null
4312
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "font-bold text-base leading-tight", children: formatBRL(mainCents) }),
4313
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "text-xs opacity-70 leading-tight", children: suffix }),
4314
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "text-xs opacity-60 leading-tight", children: label }),
4315
+ anchorCents != null && anchorCents > mainCents ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: anchorClassName ?? "text-xs opacity-50", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("s", { children: formatBRL(anchorCents) }) }) : null
4052
4316
  ]
4053
4317
  },
4054
4318
  c
@@ -4058,38 +4322,6 @@ function PaywallCyclePicker({
4058
4322
  );
4059
4323
  }
4060
4324
 
4061
- // src/components/paywall/PaywallCta.tsx
4062
- var import_jsx_runtime35 = require("react/jsx-runtime");
4063
- function PaywallCta({
4064
- ctaLabel,
4065
- loadingLabel,
4066
- switchHint,
4067
- trustLine,
4068
- onClick,
4069
- disabled = false,
4070
- loading = false,
4071
- className,
4072
- buttonClassName,
4073
- switchHintClassName,
4074
- trustClassName
4075
- }) {
4076
- const label = loading && loadingLabel ? loadingLabel : ctaLabel;
4077
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className, children: [
4078
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4079
- "button",
4080
- {
4081
- type: "button",
4082
- onClick,
4083
- disabled: disabled || loading,
4084
- className: buttonClassName,
4085
- children: label
4086
- }
4087
- ),
4088
- switchHint ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: switchHintClassName, children: switchHint }) : null,
4089
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: trustClassName, children: trustLine })
4090
- ] });
4091
- }
4092
-
4093
4325
  // src/components/paywall/Paywall.tsx
4094
4326
  var import_jsx_runtime36 = require("react/jsx-runtime");
4095
4327
  var NBSP = "\xA0";
@@ -4098,22 +4330,43 @@ function Paywall({
4098
4330
  themeClasses = {},
4099
4331
  slots = {},
4100
4332
  onBeforeCheckout
4333
+ }) {
4334
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(PaywallProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4335
+ PaywallInner,
4336
+ {
4337
+ copy,
4338
+ themeClasses,
4339
+ slots,
4340
+ onBeforeCheckout
4341
+ }
4342
+ ) });
4343
+ }
4344
+ function PaywallInner({
4345
+ copy,
4346
+ themeClasses = {},
4347
+ slots = {},
4348
+ onBeforeCheckout
4101
4349
  }) {
4102
4350
  const { track: track2 } = (0, import_sdk24.useHook)();
4103
- const s = usePaywallState();
4351
+ const s = usePaywallContext();
4104
4352
  const priceLabel = formatBRL(s.currentPriceCents).replace(new RegExp(NBSP, "g"), " ");
4105
- const monthlyEquivLabel = formatBRL(s.currentMonthlyEquivCents).replace(new RegExp(NBSP, "g"), " ");
4106
4353
  const trialDaysCardLabel = String(s.trialDaysCard);
4107
- const ctaLabel = (0, import_react29.useMemo)(() => {
4354
+ const ctaLabel = (0, import_react31.useMemo)(() => {
4108
4355
  if (s.isFree) return copy.freeCta ?? "Come\xE7ar agora";
4109
4356
  if (s.selectedMethod === "card") {
4110
4357
  if (s.hasConsumedTrial && copy.cardConsumedTrial) {
4111
- return interp(copy.cardConsumedTrial.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
4358
+ return interp(copy.cardConsumedTrial.ctaTemplate, {
4359
+ price: priceLabel,
4360
+ days: trialDaysCardLabel
4361
+ });
4112
4362
  }
4113
4363
  if (s.trialDaysCard > 0) {
4114
4364
  return interp(copy.card.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
4115
4365
  }
4116
- return copy.cardConsumedTrial ? interp(copy.cardConsumedTrial.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel }) : `Assinar por ${priceLabel}`;
4366
+ return copy.cardConsumedTrial ? interp(copy.cardConsumedTrial.ctaTemplate, {
4367
+ price: priceLabel,
4368
+ days: trialDaysCardLabel
4369
+ }) : `Assinar por ${priceLabel}`;
4117
4370
  }
4118
4371
  return interp(copy.pix.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
4119
4372
  }, [
@@ -4125,11 +4378,11 @@ function Paywall({
4125
4378
  priceLabel,
4126
4379
  trialDaysCardLabel
4127
4380
  ]);
4128
- const switchHint = (0, import_react29.useMemo)(() => {
4381
+ const switchHint = (0, import_react31.useMemo)(() => {
4129
4382
  if (s.methods.length < 2) return void 0;
4130
4383
  return s.selectedMethod === "card" ? copy.card.switchHint : copy.pix.switchHint;
4131
4384
  }, [s.methods.length, s.selectedMethod, copy]);
4132
- (0, import_react29.useEffect)(() => {
4385
+ (0, import_react31.useEffect)(() => {
4133
4386
  if (!s.initialLoadComplete) return;
4134
4387
  track2("paywall_view", {
4135
4388
  default_method: s.selectedMethod,
@@ -4162,21 +4415,6 @@ function Paywall({
4162
4415
  slots.cyclePickerSlot ?? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4163
4416
  PaywallCyclePicker,
4164
4417
  {
4165
- cycles: ["MONTHLY", "YEARLY"],
4166
- selected: s.cycle,
4167
- onSelect: s.selectCycle,
4168
- priceCentsByCycle: {
4169
- MONTHLY: priceCentsForCycle(s, "MONTHLY"),
4170
- YEARLY: priceCentsForCycle(s, "YEARLY")
4171
- },
4172
- anchorCentsByCycle: {
4173
- MONTHLY: anchorForCycle(s, "MONTHLY"),
4174
- YEARLY: anchorForCycle(s, "YEARLY")
4175
- },
4176
- monthlyEquivByCycle: {
4177
- MONTHLY: priceCentsForCycle(s, "MONTHLY"),
4178
- YEARLY: Math.round(priceCentsForCycle(s, "YEARLY") / 12)
4179
- },
4180
4418
  labels: copy.cycle,
4181
4419
  cardClassName: themeClasses.cycleCard,
4182
4420
  cardSelectedClassName: themeClasses.cycleCardSelected,
@@ -4186,9 +4424,6 @@ function Paywall({
4186
4424
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4187
4425
  PaywallMethodTabs,
4188
4426
  {
4189
- methods: s.methods,
4190
- selected: s.selectedMethod,
4191
- onSelect: s.selectMethod,
4192
4427
  labels: { "pix-auto": copy.pix.tabLabel, card: copy.card.tabLabel },
4193
4428
  className: themeClasses.tabs,
4194
4429
  tabClassName: themeClasses.tab,
@@ -4198,7 +4433,6 @@ function Paywall({
4198
4433
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4199
4434
  PaywallMethodContent,
4200
4435
  {
4201
- method: s.selectedMethod,
4202
4436
  copy: {
4203
4437
  pix: interpolateCopy(copy.pix, priceLabel, trialDaysCardLabel),
4204
4438
  card: interpolateCopy(copy.card, priceLabel, trialDaysCardLabel),
@@ -4209,27 +4443,27 @@ function Paywall({
4209
4443
  ctaTemplate: copy.cardConsumedTrial.ctaTemplate
4210
4444
  } : void 0
4211
4445
  },
4212
- hasConsumedTrial: s.hasConsumedTrial,
4213
4446
  className: themeClasses.tabContent,
4214
4447
  rowClassName: themeClasses.tabContentRow
4215
4448
  }
4216
4449
  ),
4217
4450
  slots.beforeCtaSlot,
4218
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4219
- PaywallCta,
4220
- {
4221
- ctaLabel,
4222
- loadingLabel: "Abrindo checkout\u2026",
4223
- switchHint,
4224
- trustLine: copy.trustLine,
4225
- onClick: handleCta,
4226
- disabled: s.submitting,
4227
- loading: s.submitting,
4228
- buttonClassName: ctaTheme,
4229
- switchHintClassName: themeClasses.switchHint,
4230
- trustClassName: themeClasses.trustLine
4231
- }
4232
- )
4451
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { children: [
4452
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4453
+ "button",
4454
+ {
4455
+ type: "button",
4456
+ onClick: () => {
4457
+ void handleCta();
4458
+ },
4459
+ disabled: s.submitting,
4460
+ className: ctaTheme,
4461
+ children: s.submitting ? "Abrindo checkout\u2026" : ctaLabel
4462
+ }
4463
+ ),
4464
+ switchHint ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: themeClasses.switchHint, children: switchHint }) : null,
4465
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: themeClasses.trustLine, children: copy.trustLine })
4466
+ ] })
4233
4467
  ] });
4234
4468
  }
4235
4469
  function interp(tpl, vars) {
@@ -4243,13 +4477,438 @@ function interpolateCopy(m, price, days) {
4243
4477
  switchHint: m.switchHint
4244
4478
  };
4245
4479
  }
4246
- function priceCentsForCycle(s, c) {
4247
- return s.plan ? c === "YEARLY" ? s.plan.yearlyCents ?? 0 : s.plan.monthlyCents : 0;
4480
+
4481
+ // src/components/paywall/PaywallCta.tsx
4482
+ var import_jsx_runtime37 = require("react/jsx-runtime");
4483
+ function PaywallCta({
4484
+ ctaLabel,
4485
+ loadingLabel,
4486
+ switchHint,
4487
+ trustLine,
4488
+ className,
4489
+ buttonClassName,
4490
+ switchHintClassName,
4491
+ trustClassName
4492
+ }) {
4493
+ const { submit, submitting } = usePaywallContext();
4494
+ const label = submitting && loadingLabel ? loadingLabel : ctaLabel;
4495
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className, children: [
4496
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
4497
+ "button",
4498
+ {
4499
+ type: "button",
4500
+ onClick: () => {
4501
+ void submit();
4502
+ },
4503
+ disabled: submitting,
4504
+ className: buttonClassName,
4505
+ children: label
4506
+ }
4507
+ ),
4508
+ switchHint ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: switchHintClassName, children: switchHint }) : null,
4509
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: trustClassName, children: trustLine })
4510
+ ] });
4511
+ }
4512
+
4513
+ // src/components/paywall/blocks/PaywallEyebrow.tsx
4514
+ var import_jsx_runtime38 = require("react/jsx-runtime");
4515
+ var DEFAULT_EYEBROW_CLASSES = "text-xs uppercase tracking-widest font-semibold opacity-70";
4516
+ function PaywallEyebrow({ text, className }) {
4517
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: [DEFAULT_EYEBROW_CLASSES, className].filter(Boolean).join(" "), children: text });
4518
+ }
4519
+
4520
+ // src/components/paywall/blocks/PaywallHero.tsx
4521
+ var import_jsx_runtime39 = require("react/jsx-runtime");
4522
+ var DEFAULT_GRADIENT = "absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent";
4523
+ function PaywallHero({
4524
+ src,
4525
+ alt = "",
4526
+ headline,
4527
+ aspectRatio = "16/9",
4528
+ gradientClassName,
4529
+ className,
4530
+ headlineClassName,
4531
+ imgClassName,
4532
+ render
4533
+ }) {
4534
+ if (render) {
4535
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className, children: render({ src, headline }) });
4536
+ }
4537
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
4538
+ "div",
4539
+ {
4540
+ className: ["relative overflow-hidden", className].filter(Boolean).join(" "),
4541
+ style: { aspectRatio },
4542
+ children: [
4543
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
4544
+ "img",
4545
+ {
4546
+ src,
4547
+ alt,
4548
+ className: ["absolute inset-0 w-full h-full object-cover", imgClassName].filter(Boolean).join(" ")
4549
+ }
4550
+ ),
4551
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: gradientClassName ?? DEFAULT_GRADIENT, "aria-hidden": "true" }),
4552
+ headline ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
4553
+ "h1",
4554
+ {
4555
+ className: ["absolute bottom-0 left-0 right-0 p-4 text-white font-bold text-2xl", headlineClassName].filter(Boolean).join(" "),
4556
+ children: headline
4557
+ }
4558
+ ) : null
4559
+ ]
4560
+ }
4561
+ );
4562
+ }
4563
+
4564
+ // src/components/paywall/blocks/PaywallHeadline.tsx
4565
+ var import_jsx_runtime40 = require("react/jsx-runtime");
4566
+ var DEFAULT_HEADLINE_CLASSES = "text-2xl font-bold leading-tight";
4567
+ function PaywallHeadline({ text, className, as = "h1" }) {
4568
+ const Tag = as;
4569
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Tag, { className: [DEFAULT_HEADLINE_CLASSES, className].filter(Boolean).join(" "), children: text });
4570
+ }
4571
+
4572
+ // src/components/paywall/blocks/PaywallPriceHeadline.tsx
4573
+ var import_jsx_runtime41 = require("react/jsx-runtime");
4574
+ var DEFAULT_CLASS = "text-2xl font-bold leading-tight";
4575
+ var CYCLE_LABEL = {
4576
+ MONTHLY: "mensal",
4577
+ YEARLY: "anual"
4578
+ };
4579
+ function PaywallPriceHeadline({
4580
+ template,
4581
+ className,
4582
+ as = "h1",
4583
+ render
4584
+ }) {
4585
+ const { cycle, currentMonthlyEquivCents, plan } = usePaywallContext();
4586
+ const yearlyCents = plan?.yearlyCents ?? null;
4587
+ const pricePerDay = formatBRL(dailyFromYearly(yearlyCents));
4588
+ const monthlyEquiv = currentMonthlyEquivCents ?? 0;
4589
+ const cycleLabel = CYCLE_LABEL[cycle] ?? cycle.toLowerCase();
4590
+ const rootClasses = [DEFAULT_CLASS, className].filter(Boolean).join(" ");
4591
+ if (render) {
4592
+ const RootTag2 = as;
4593
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(RootTag2, { className: [className].filter(Boolean).join(" ") || void 0, children: render({ pricePerDay, currentMonthlyEquivCents: monthlyEquiv, cycle }) });
4594
+ }
4595
+ const text = template.replaceAll("{pricePerDay}", pricePerDay).replaceAll("{currentMonthlyEquiv}", formatBRL(monthlyEquiv)).replaceAll("{cycle}", cycleLabel);
4596
+ const RootTag = as;
4597
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(RootTag, { className: rootClasses, children: text });
4598
+ }
4599
+
4600
+ // src/components/paywall/blocks/PaywallCountdown.tsx
4601
+ var import_react32 = require("react");
4602
+ var import_jsx_runtime42 = require("react/jsx-runtime");
4603
+ var DEFAULT_COUNTDOWN_CLASSES = "font-mono tabular-nums";
4604
+ function resolveDeadlineMs(deadline) {
4605
+ if (deadline instanceof Date) return deadline.getTime();
4606
+ if (typeof deadline === "string") return new Date(deadline).getTime();
4607
+ const { sessionStorageKey, durationMs } = deadline;
4608
+ if (typeof window === "undefined" || typeof window.sessionStorage === "undefined") {
4609
+ return Date.now() + durationMs;
4610
+ }
4611
+ const stored = window.sessionStorage.getItem(sessionStorageKey);
4612
+ const parsed = stored ? Number.parseInt(stored, 10) : NaN;
4613
+ const now = Date.now();
4614
+ if (!Number.isFinite(parsed) || parsed < now) {
4615
+ const target = now + durationMs;
4616
+ window.sessionStorage.setItem(sessionStorageKey, String(target));
4617
+ return target;
4618
+ }
4619
+ return parsed;
4620
+ }
4621
+ function computeRemaining(deadlineMs) {
4622
+ const diff = Math.max(0, deadlineMs - Date.now());
4623
+ const totalSeconds = Math.floor(diff / 1e3);
4624
+ const h = Math.floor(totalSeconds / 3600);
4625
+ const m = Math.floor(totalSeconds % 3600 / 60);
4626
+ const s = totalSeconds % 60;
4627
+ return { h, m, s, expired: diff === 0 };
4628
+ }
4629
+ function pad(n) {
4630
+ return String(n).padStart(2, "0");
4248
4631
  }
4249
- function anchorForCycle(s, c) {
4250
- if (!s.plan) return null;
4251
- if (c === "YEARLY") return s.plan.anchorYearlyCents ?? null;
4252
- return s.plan.anchorMonthlyCents ?? null;
4632
+ function PaywallCountdown({
4633
+ deadline,
4634
+ format = "h:m:s",
4635
+ onExpire,
4636
+ className,
4637
+ render
4638
+ }) {
4639
+ const deadlineMsRef = (0, import_react32.useRef)(null);
4640
+ if (deadlineMsRef.current === null) {
4641
+ deadlineMsRef.current = resolveDeadlineMs(deadline);
4642
+ }
4643
+ const [state, setState] = (0, import_react32.useState)(() => computeRemaining(deadlineMsRef.current));
4644
+ const expiredCalledRef = (0, import_react32.useRef)(false);
4645
+ (0, import_react32.useEffect)(() => {
4646
+ if (state.expired) {
4647
+ if (!expiredCalledRef.current) {
4648
+ expiredCalledRef.current = true;
4649
+ onExpire?.();
4650
+ }
4651
+ return;
4652
+ }
4653
+ const tick = () => {
4654
+ const next = computeRemaining(deadlineMsRef.current);
4655
+ setState(next);
4656
+ if (next.expired && !expiredCalledRef.current) {
4657
+ expiredCalledRef.current = true;
4658
+ onExpire?.();
4659
+ }
4660
+ };
4661
+ const id = setInterval(tick, 1e3);
4662
+ return () => clearInterval(id);
4663
+ }, [state.expired]);
4664
+ if (render) {
4665
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className, children: render(state) });
4666
+ }
4667
+ const formatted = format === "h:m:s" ? `${pad(state.h)}:${pad(state.m)}:${pad(state.s)}` : `${pad(state.h * 60 + state.m)}:${pad(state.s)}`;
4668
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: [DEFAULT_COUNTDOWN_CLASSES, className].filter(Boolean).join(" "), children: formatted });
4669
+ }
4670
+
4671
+ // src/components/paywall/blocks/PaywallFeatures.tsx
4672
+ var import_jsx_runtime43 = require("react/jsx-runtime");
4673
+ function PaywallFeatures({
4674
+ items,
4675
+ IconComponent,
4676
+ className,
4677
+ itemClassName,
4678
+ iconClassName,
4679
+ render,
4680
+ renderItem
4681
+ }) {
4682
+ if (render) {
4683
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className, children: render({ items }) });
4684
+ }
4685
+ if (renderItem) {
4686
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { className, children: items.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { children: renderItem(item, idx) }, idx)) });
4687
+ }
4688
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { className, children: items.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("li", { className: itemClassName, children: [
4689
+ IconComponent ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(IconComponent, { className: iconClassName }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: iconClassName, "aria-hidden": "true", children: "\u2713" }),
4690
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { children: item })
4691
+ ] }, idx)) });
4692
+ }
4693
+
4694
+ // src/components/paywall/blocks/PaywallFeaturesCard.tsx
4695
+ var import_jsx_runtime44 = require("react/jsx-runtime");
4696
+ var DEFAULT_CARD_CLASSES = "rounded-xl border p-4";
4697
+ function PaywallFeaturesCard({
4698
+ title,
4699
+ items,
4700
+ className,
4701
+ cardClassName,
4702
+ titleClassName,
4703
+ itemClassName,
4704
+ renderItem
4705
+ }) {
4706
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: [DEFAULT_CARD_CLASSES, cardClassName].filter(Boolean).join(" "), children: [
4707
+ title ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: ["font-semibold mb-2", titleClassName].filter(Boolean).join(" "), children: title }) : null,
4708
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { children: items.map(
4709
+ (item, idx) => renderItem ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { children: renderItem(item, idx) }, idx) : /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("li", { className: itemClassName, children: [
4710
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { "aria-hidden": "true", children: "\u2022" }),
4711
+ " ",
4712
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { children: item })
4713
+ ] }, idx)
4714
+ ) })
4715
+ ] }) });
4716
+ }
4717
+
4718
+ // src/components/paywall/blocks/PaywallTrophyBadge.tsx
4719
+ var import_jsx_runtime45 = require("react/jsx-runtime");
4720
+ var DEFAULT_CHIP_CLASSES = "inline-flex items-center gap-1 px-3 py-1 rounded-full bg-yellow-100 text-yellow-900 text-sm font-medium";
4721
+ var FLOATING_CLASSES = "absolute top-2 right-2 z-10 shadow-md";
4722
+ function PaywallTrophyBadge({
4723
+ text,
4724
+ className,
4725
+ iconClassName,
4726
+ floating = false,
4727
+ render
4728
+ }) {
4729
+ if (render) {
4730
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className, children: render({ text }) });
4731
+ }
4732
+ const rootClasses = [
4733
+ DEFAULT_CHIP_CLASSES,
4734
+ floating ? FLOATING_CLASSES : "",
4735
+ className
4736
+ ].filter(Boolean).join(" ");
4737
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: rootClasses, children: [
4738
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: iconClassName, "aria-hidden": "true", children: "\u{1F3C6}" }),
4739
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { children: text })
4740
+ ] });
4741
+ }
4742
+
4743
+ // src/components/paywall/blocks/PaywallAnchorPrice.tsx
4744
+ var import_jsx_runtime46 = require("react/jsx-runtime");
4745
+ var DEFAULT_CLASS2 = "text-sm opacity-60 line-through";
4746
+ function PaywallAnchorPrice({
4747
+ className,
4748
+ render
4749
+ }) {
4750
+ const { anchorPriceCents, cycle } = usePaywallContext();
4751
+ if (anchorPriceCents === null || anchorPriceCents === void 0 || anchorPriceCents <= 0) {
4752
+ return null;
4753
+ }
4754
+ void cycle;
4755
+ const formatted = formatBRL(anchorPriceCents);
4756
+ const rootClasses = [DEFAULT_CLASS2, className].filter(Boolean).join(" ");
4757
+ if (render) {
4758
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: className || void 0, children: render({ anchorCents: anchorPriceCents, formatted }) });
4759
+ }
4760
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: rootClasses, children: formatted });
4761
+ }
4762
+
4763
+ // src/components/paywall/blocks/PaywallTestimonials.tsx
4764
+ var import_jsx_runtime47 = require("react/jsx-runtime");
4765
+ var DEFAULT_ROOT = "flex gap-3 overflow-x-auto snap-x snap-mandatory pb-2";
4766
+ var DEFAULT_CARD = "snap-start shrink-0 w-72 rounded-2xl border p-4 flex flex-col gap-2";
4767
+ var DEFAULT_AVATAR = "w-10 h-10 rounded-full object-cover";
4768
+ var DEFAULT_QUOTE = "text-sm leading-snug";
4769
+ var DEFAULT_NAME = "text-xs font-semibold opacity-80";
4770
+ var DEFAULT_STARS = "text-yellow-500 text-sm";
4771
+ function clampStars(n) {
4772
+ return Math.max(0, Math.min(5, Math.round(n)));
4773
+ }
4774
+ function PaywallTestimonials({
4775
+ items,
4776
+ className,
4777
+ cardClassName,
4778
+ avatarClassName,
4779
+ quoteClassName,
4780
+ nameClassName,
4781
+ starsClassName,
4782
+ renderItem
4783
+ }) {
4784
+ const rootClasses = [DEFAULT_ROOT, className].filter(Boolean).join(" ");
4785
+ const cardClasses = [DEFAULT_CARD, cardClassName].filter(Boolean).join(" ");
4786
+ const avatarClasses = [DEFAULT_AVATAR, avatarClassName].filter(Boolean).join(" ");
4787
+ const quoteClasses = [DEFAULT_QUOTE, quoteClassName].filter(Boolean).join(" ");
4788
+ const nameClasses = [DEFAULT_NAME, nameClassName].filter(Boolean).join(" ");
4789
+ const starsClasses = [DEFAULT_STARS, starsClassName].filter(Boolean).join(" ");
4790
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: rootClasses, children: items.map((item, idx) => {
4791
+ if (renderItem) return renderItem(item, idx);
4792
+ const filled = clampStars(item.stars);
4793
+ const empty = 5 - filled;
4794
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: cardClasses, children: [
4795
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex items-center gap-2", children: [
4796
+ item.avatar ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
4797
+ "img",
4798
+ {
4799
+ src: item.avatar,
4800
+ alt: "",
4801
+ loading: "lazy",
4802
+ className: avatarClasses,
4803
+ "aria-hidden": "true"
4804
+ }
4805
+ ) : null,
4806
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: nameClasses, children: item.name })
4807
+ ] }),
4808
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: starsClasses, "aria-label": `${filled} de 5 estrelas`, children: [
4809
+ "\u2605".repeat(filled),
4810
+ "\u2606".repeat(empty)
4811
+ ] }),
4812
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: quoteClasses, children: item.quote })
4813
+ ] }, idx);
4814
+ }) });
4815
+ }
4816
+
4817
+ // src/components/paywall/blocks/PaywallStatsRow.tsx
4818
+ var import_jsx_runtime48 = require("react/jsx-runtime");
4819
+ var DEFAULT_ROOT2 = "grid grid-cols-3 gap-4";
4820
+ var DEFAULT_CELL = "flex flex-col items-center text-center";
4821
+ var DEFAULT_VALUE = "text-2xl font-bold";
4822
+ var DEFAULT_LABEL = "text-xs opacity-70";
4823
+ function PaywallStatsRow({
4824
+ stats,
4825
+ className,
4826
+ cellClassName,
4827
+ valueClassName,
4828
+ labelClassName,
4829
+ renderCell
4830
+ }) {
4831
+ const rootClasses = [DEFAULT_ROOT2, className].filter(Boolean).join(" ");
4832
+ const cellClasses = [DEFAULT_CELL, cellClassName].filter(Boolean).join(" ");
4833
+ const valueClasses = [DEFAULT_VALUE, valueClassName].filter(Boolean).join(" ");
4834
+ const labelClasses = [DEFAULT_LABEL, labelClassName].filter(Boolean).join(" ");
4835
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: rootClasses, children: stats.map((stat, idx) => {
4836
+ if (renderCell) return renderCell(stat, idx);
4837
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: cellClasses, children: [
4838
+ stat.icon ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { "aria-hidden": "true", children: stat.icon }) : null,
4839
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: valueClasses, children: stat.value }),
4840
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: labelClasses, children: stat.label })
4841
+ ] }, idx);
4842
+ }) });
4843
+ }
4844
+
4845
+ // src/components/paywall/blocks/PaywallFinePrint.tsx
4846
+ var import_jsx_runtime49 = require("react/jsx-runtime");
4847
+ var DEFAULT_CLASS3 = "text-xs opacity-60 leading-snug";
4848
+ var CYCLE_LABEL2 = {
4849
+ MONTHLY: "mensal",
4850
+ YEARLY: "anual"
4851
+ };
4852
+ function PaywallFinePrint({
4853
+ template,
4854
+ className,
4855
+ render
4856
+ }) {
4857
+ const {
4858
+ currentPriceCents,
4859
+ cycle,
4860
+ trialDaysCard,
4861
+ trialDaysPix,
4862
+ selectedMethod
4863
+ } = usePaywallContext();
4864
+ const trialDays = selectedMethod === "card" ? trialDaysCard : trialDaysPix;
4865
+ const cycleLabel = CYCLE_LABEL2[cycle] ?? cycle.toLowerCase();
4866
+ const priceFormatted = formatBRL(currentPriceCents ?? 0);
4867
+ const rootClasses = [DEFAULT_CLASS3, className].filter(Boolean).join(" ");
4868
+ if (render) {
4869
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: className || void 0, children: render({
4870
+ currentPriceCents: currentPriceCents ?? 0,
4871
+ cycle,
4872
+ trialDays: trialDays ?? 0,
4873
+ selectedMethod
4874
+ }) });
4875
+ }
4876
+ const text = template.replaceAll("{price}", priceFormatted).replaceAll("{trialDays}", String(trialDays ?? 0)).replaceAll("{cycle}", cycleLabel);
4877
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: rootClasses, children: text });
4878
+ }
4879
+
4880
+ // src/components/paywall/blocks/PaywallTrustLine.tsx
4881
+ var import_jsx_runtime50 = require("react/jsx-runtime");
4882
+ var DEFAULT_ROOT3 = "flex items-center gap-3";
4883
+ var DEFAULT_ITEM = "flex items-center gap-1.5 text-xs";
4884
+ function PaywallTrustLine({
4885
+ items,
4886
+ className,
4887
+ itemClassName,
4888
+ renderItem
4889
+ }) {
4890
+ const rootClasses = [DEFAULT_ROOT3, className].filter(Boolean).join(" ");
4891
+ const itemClasses = [DEFAULT_ITEM, itemClassName].filter(Boolean).join(" ");
4892
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: rootClasses, children: items.map((item, idx) => {
4893
+ if (renderItem) return renderItem(item, idx);
4894
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("span", { className: itemClasses, children: [
4895
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { "aria-hidden": "true", children: item.icon }),
4896
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { children: item.text })
4897
+ ] }, idx);
4898
+ }) });
4899
+ }
4900
+
4901
+ // src/components/paywall/blocks/PaywallStickyFooter.tsx
4902
+ var import_jsx_runtime51 = require("react/jsx-runtime");
4903
+ var DEFAULT_CLASSES = "sticky bottom-0 left-0 right-0 bg-background";
4904
+ var SAFE_AREA_CLASS = "pb-[env(safe-area-inset-bottom)]";
4905
+ function PaywallStickyFooter({
4906
+ children,
4907
+ className,
4908
+ safeAreaInsets = true
4909
+ }) {
4910
+ const classes = [DEFAULT_CLASSES, safeAreaInsets ? SAFE_AREA_CLASS : null, className].filter(Boolean).join(" ");
4911
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: classes, children });
4253
4912
  }
4254
4913
  // Annotate the CommonJS export names for ESM import in node:
4255
4914
  0 && (module.exports = {
@@ -4269,10 +4928,26 @@ function anchorForCycle(s, c) {
4269
4928
  OnboardingFlow,
4270
4929
  PaymentReturnHandler,
4271
4930
  Paywall,
4931
+ PaywallAnchorPrice,
4932
+ PaywallContext,
4933
+ PaywallCountdown,
4272
4934
  PaywallCta,
4273
4935
  PaywallCyclePicker,
4936
+ PaywallEyebrow,
4937
+ PaywallFeatures,
4938
+ PaywallFeaturesCard,
4939
+ PaywallFinePrint,
4940
+ PaywallHeadline,
4941
+ PaywallHero,
4274
4942
  PaywallMethodContent,
4275
4943
  PaywallMethodTabs,
4944
+ PaywallPriceHeadline,
4945
+ PaywallProvider,
4946
+ PaywallStatsRow,
4947
+ PaywallStickyFooter,
4948
+ PaywallTestimonials,
4949
+ PaywallTrophyBadge,
4950
+ PaywallTrustLine,
4276
4951
  PersistenceRegistry,
4277
4952
  PixWaitingPageDefault,
4278
4953
  PreAuthShell,
@@ -4303,6 +4978,7 @@ function anchorForCycle(s, c) {
4303
4978
  useInstallPrompt,
4304
4979
  useLoginForm,
4305
4980
  useOnboardingStep,
4981
+ usePaywallContext,
4306
4982
  usePaywallState,
4307
4983
  usePlan,
4308
4984
  usePush,