@hook-sdk/template 0.23.2 → 0.24.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.js CHANGED
@@ -630,8 +630,8 @@ function usePaywallState() {
630
630
  opening: submitting,
631
631
  availableMethods: methods,
632
632
  monthlyEquivalent,
633
- dismissPix: () => {
634
- },
633
+ // G154 fix (template 0.24.0 + SDK 0.26.0): wired to SDK action.
634
+ dismissPix: subscription.clearPixPending,
635
635
  refreshPlan: () => {
636
636
  }
637
637
  };
@@ -2101,7 +2101,7 @@ function I18nProvider({
2101
2101
  function isDevToolsEnabled() {
2102
2102
  if (typeof window === "undefined") return false;
2103
2103
  const host = window.location.hostname;
2104
- return host.includes(".staging.") || host === "localhost" || host === "127.0.0.1";
2104
+ return host.includes("staging.usehook.net") || host === "localhost" || host === "127.0.0.1";
2105
2105
  }
2106
2106
 
2107
2107
  // src/dev/DevSkipOnboardingFab.tsx
@@ -3287,22 +3287,39 @@ function useFeature(name) {
3287
3287
  import { useEffect as useEffect14, useMemo as useMemo9 } from "react";
3288
3288
  import { useHook as useHook19 } from "@hook-sdk/sdk";
3289
3289
 
3290
- // src/components/paywall/PaywallMethodTabs.tsx
3290
+ // src/components/paywall/PaywallProvider.tsx
3291
+ import { createContext as createContext4 } from "react";
3291
3292
  import { jsx as jsx30 } from "react/jsx-runtime";
3293
+ var PaywallContext = createContext4(null);
3294
+ function PaywallProvider({ children }) {
3295
+ const state = usePaywallState();
3296
+ return /* @__PURE__ */ jsx30(PaywallContext.Provider, { value: state, children });
3297
+ }
3298
+
3299
+ // src/components/paywall/usePaywallContext.ts
3300
+ import { useContext as useContext5 } from "react";
3301
+ function usePaywallContext() {
3302
+ const ctx = useContext5(PaywallContext);
3303
+ if (!ctx) {
3304
+ throw new Error("usePaywallContext must be used within <PaywallProvider>");
3305
+ }
3306
+ return ctx;
3307
+ }
3308
+
3309
+ // src/components/paywall/PaywallMethodTabs.tsx
3310
+ import { jsx as jsx31 } from "react/jsx-runtime";
3292
3311
  function PaywallMethodTabs({
3293
- methods,
3294
- selected,
3295
- onSelect,
3296
3312
  labels,
3297
3313
  className,
3298
3314
  tabClassName,
3299
3315
  tabActiveClassName
3300
3316
  }) {
3317
+ const { methods, selectedMethod, selectMethod } = usePaywallContext();
3301
3318
  if (methods.length < 2) return null;
3302
- return /* @__PURE__ */ jsx30("div", { role: "tablist", "aria-label": "M\xE9todo de pagamento", className, children: methods.map((m) => {
3303
- const active = m === selected;
3319
+ return /* @__PURE__ */ jsx31("div", { role: "tablist", "aria-label": "M\xE9todo de pagamento", className, children: methods.map((m) => {
3320
+ const active = m === selectedMethod;
3304
3321
  const label = labels[m] ?? m;
3305
- return /* @__PURE__ */ jsx30(
3322
+ return /* @__PURE__ */ jsx31(
3306
3323
  "button",
3307
3324
  {
3308
3325
  type: "button",
@@ -3310,7 +3327,7 @@ function PaywallMethodTabs({
3310
3327
  "aria-selected": active,
3311
3328
  "aria-controls": `paywall-tab-${m}`,
3312
3329
  tabIndex: active ? 0 : -1,
3313
- onClick: () => onSelect(m),
3330
+ onClick: () => selectMethod(m),
3314
3331
  className: [tabClassName, active ? tabActiveClassName : ""].filter(Boolean).join(" "),
3315
3332
  children: label
3316
3333
  },
@@ -3320,36 +3337,51 @@ function PaywallMethodTabs({
3320
3337
  }
3321
3338
 
3322
3339
  // src/components/paywall/PaywallMethodContent.tsx
3323
- import { jsx as jsx31 } from "react/jsx-runtime";
3324
- function PaywallMethodContent({
3325
- method,
3326
- copy,
3327
- hasConsumedTrial = false,
3328
- className,
3329
- rowClassName
3330
- }) {
3331
- const useCardConsumed = method === "card" && hasConsumedTrial && copy.cardConsumedTrial;
3332
- const rows = useCardConsumed ? copy.cardConsumedTrial.bodyRows : method === "pix-auto" || method === "pix-once" ? copy.pix.bodyRows : copy.card.bodyRows;
3333
- return /* @__PURE__ */ jsx31("div", { role: "tabpanel", id: `paywall-tab-${method}`, className, children: rows.map((row, i) => /* @__PURE__ */ jsx31("div", { className: rowClassName, children: row }, i)) });
3340
+ import { jsx as jsx32 } from "react/jsx-runtime";
3341
+ function PaywallMethodContent({ copy, className, rowClassName }) {
3342
+ const { selectedMethod, hasConsumedTrial } = usePaywallContext();
3343
+ const useCardConsumed = selectedMethod === "card" && hasConsumedTrial && copy.cardConsumedTrial;
3344
+ const rows = useCardConsumed ? copy.cardConsumedTrial.bodyRows : selectedMethod === "pix-auto" || selectedMethod === "pix-once" ? copy.pix.bodyRows : copy.card.bodyRows;
3345
+ return /* @__PURE__ */ jsx32("div", { role: "tabpanel", id: `paywall-tab-${selectedMethod}`, className, children: rows.map((row, i) => /* @__PURE__ */ jsx32("div", { className: rowClassName, children: row }, i)) });
3334
3346
  }
3335
3347
 
3336
3348
  // src/components/paywall/PaywallCyclePicker.tsx
3337
- import { jsx as jsx32, jsxs as jsxs19 } from "react/jsx-runtime";
3349
+ import { jsx as jsx33, jsxs as jsxs19 } from "react/jsx-runtime";
3350
+ var VARIANT_CLASSES = {
3351
+ default: { card: "", cardSelected: "" },
3352
+ "premium-gold": {
3353
+ card: "",
3354
+ cardSelected: "border-2 border-yellow-400/80 ring-2 ring-yellow-400/20"
3355
+ },
3356
+ "pink-pill": {
3357
+ card: "rounded-2xl",
3358
+ cardSelected: "border-2 border-pink-500"
3359
+ }
3360
+ };
3338
3361
  function PaywallCyclePicker({
3339
- cycles,
3340
- selected,
3341
- onSelect,
3342
- priceCentsByCycle,
3343
- anchorCentsByCycle,
3344
- monthlyEquivByCycle,
3345
3362
  labels,
3346
3363
  className,
3347
3364
  cardClassName,
3348
3365
  cardSelectedClassName,
3349
- anchorClassName
3366
+ anchorClassName,
3367
+ variant = "default",
3368
+ render
3350
3369
  }) {
3370
+ const ctx = usePaywallContext();
3371
+ const { cycle: selected, setCycle, plan, anchorPriceCents } = ctx;
3372
+ const cycles = ["MONTHLY", "YEARLY"];
3373
+ if (render) {
3374
+ return /* @__PURE__ */ jsx33("div", { className, children: render({ cycles, selected, setCycle, plan, anchorPriceCents }) });
3375
+ }
3351
3376
  if (cycles.length < 2) return null;
3352
- return /* @__PURE__ */ jsx32(
3377
+ const v = VARIANT_CLASSES[variant];
3378
+ const composedCardClassName = [v.card, cardClassName].filter(Boolean).join(" ");
3379
+ const composedCardSelectedClassName = [v.cardSelected, cardSelectedClassName].filter(Boolean).join(" ");
3380
+ const monthlyCents = plan?.monthlyCents ?? 0;
3381
+ const yearlyCents = plan?.yearlyCents ?? 0;
3382
+ const anchorMonthly = plan?.anchorMonthlyCents ?? null;
3383
+ const anchorYearly = plan?.anchorYearlyCents ?? null;
3384
+ return /* @__PURE__ */ jsx33(
3353
3385
  "div",
3354
3386
  {
3355
3387
  role: "radiogroup",
@@ -3359,21 +3391,25 @@ function PaywallCyclePicker({
3359
3391
  const active = c === selected;
3360
3392
  const label = c === "YEARLY" ? labels.annualLabel : labels.monthlyLabel;
3361
3393
  const suffix = c === "YEARLY" ? labels.annualSuffix : labels.monthlySuffix;
3362
- const mainCents = c === "YEARLY" ? monthlyEquivByCycle[c] : priceCentsByCycle[c];
3363
- const anchorCents = anchorCentsByCycle[c];
3394
+ const mainCents = c === "YEARLY" ? Math.round(yearlyCents / 12) : monthlyCents;
3395
+ const anchorCents = c === "YEARLY" ? anchorYearly : anchorMonthly;
3364
3396
  return /* @__PURE__ */ jsxs19(
3365
3397
  "button",
3366
3398
  {
3367
3399
  type: "button",
3368
3400
  role: "radio",
3369
3401
  "aria-checked": active,
3370
- onClick: () => onSelect(c),
3371
- className: ["flex flex-col items-center gap-0.5", cardClassName, active ? cardSelectedClassName : ""].filter(Boolean).join(" "),
3402
+ onClick: () => setCycle(c),
3403
+ className: [
3404
+ "flex flex-col items-center gap-0.5",
3405
+ composedCardClassName,
3406
+ active ? composedCardSelectedClassName : ""
3407
+ ].filter(Boolean).join(" "),
3372
3408
  children: [
3373
- /* @__PURE__ */ jsx32("span", { className: "font-bold text-base leading-tight", children: formatBRL(mainCents) }),
3374
- /* @__PURE__ */ jsx32("span", { className: "text-xs opacity-70 leading-tight", children: suffix }),
3375
- /* @__PURE__ */ jsx32("span", { className: "text-xs opacity-60 leading-tight", children: label }),
3376
- anchorCents != null && anchorCents > mainCents ? /* @__PURE__ */ jsx32("span", { className: anchorClassName ?? "text-xs opacity-50", children: /* @__PURE__ */ jsx32("s", { children: formatBRL(anchorCents) }) }) : null
3409
+ /* @__PURE__ */ jsx33("span", { className: "font-bold text-base leading-tight", children: formatBRL(mainCents) }),
3410
+ /* @__PURE__ */ jsx33("span", { className: "text-xs opacity-70 leading-tight", children: suffix }),
3411
+ /* @__PURE__ */ jsx33("span", { className: "text-xs opacity-60 leading-tight", children: label }),
3412
+ anchorCents != null && anchorCents > mainCents ? /* @__PURE__ */ jsx33("span", { className: anchorClassName ?? "text-xs opacity-50", children: /* @__PURE__ */ jsx33("s", { children: formatBRL(anchorCents) }) }) : null
3377
3413
  ]
3378
3414
  },
3379
3415
  c
@@ -3383,62 +3419,51 @@ function PaywallCyclePicker({
3383
3419
  );
3384
3420
  }
3385
3421
 
3386
- // src/components/paywall/PaywallCta.tsx
3387
- import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
3388
- function PaywallCta({
3389
- ctaLabel,
3390
- loadingLabel,
3391
- switchHint,
3392
- trustLine,
3393
- onClick,
3394
- disabled = false,
3395
- loading = false,
3396
- className,
3397
- buttonClassName,
3398
- switchHintClassName,
3399
- trustClassName
3400
- }) {
3401
- const label = loading && loadingLabel ? loadingLabel : ctaLabel;
3402
- return /* @__PURE__ */ jsxs20("div", { className, children: [
3403
- /* @__PURE__ */ jsx33(
3404
- "button",
3405
- {
3406
- type: "button",
3407
- onClick,
3408
- disabled: disabled || loading,
3409
- className: buttonClassName,
3410
- children: label
3411
- }
3412
- ),
3413
- switchHint ? /* @__PURE__ */ jsx33("p", { className: switchHintClassName, children: switchHint }) : null,
3414
- /* @__PURE__ */ jsx33("p", { className: trustClassName, children: trustLine })
3415
- ] });
3416
- }
3417
-
3418
3422
  // src/components/paywall/Paywall.tsx
3419
- import { jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
3423
+ import { jsx as jsx34, jsxs as jsxs20 } from "react/jsx-runtime";
3420
3424
  var NBSP = "\xA0";
3421
3425
  function Paywall({
3422
3426
  copy,
3423
3427
  themeClasses = {},
3424
3428
  slots = {},
3425
3429
  onBeforeCheckout
3430
+ }) {
3431
+ return /* @__PURE__ */ jsx34(PaywallProvider, { children: /* @__PURE__ */ jsx34(
3432
+ PaywallInner,
3433
+ {
3434
+ copy,
3435
+ themeClasses,
3436
+ slots,
3437
+ onBeforeCheckout
3438
+ }
3439
+ ) });
3440
+ }
3441
+ function PaywallInner({
3442
+ copy,
3443
+ themeClasses = {},
3444
+ slots = {},
3445
+ onBeforeCheckout
3426
3446
  }) {
3427
3447
  const { track: track2 } = useHook19();
3428
- const s = usePaywallState();
3448
+ const s = usePaywallContext();
3429
3449
  const priceLabel = formatBRL(s.currentPriceCents).replace(new RegExp(NBSP, "g"), " ");
3430
- const monthlyEquivLabel = formatBRL(s.currentMonthlyEquivCents).replace(new RegExp(NBSP, "g"), " ");
3431
3450
  const trialDaysCardLabel = String(s.trialDaysCard);
3432
3451
  const ctaLabel = useMemo9(() => {
3433
3452
  if (s.isFree) return copy.freeCta ?? "Come\xE7ar agora";
3434
3453
  if (s.selectedMethod === "card") {
3435
3454
  if (s.hasConsumedTrial && copy.cardConsumedTrial) {
3436
- return interp(copy.cardConsumedTrial.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
3455
+ return interp(copy.cardConsumedTrial.ctaTemplate, {
3456
+ price: priceLabel,
3457
+ days: trialDaysCardLabel
3458
+ });
3437
3459
  }
3438
3460
  if (s.trialDaysCard > 0) {
3439
3461
  return interp(copy.card.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
3440
3462
  }
3441
- return copy.cardConsumedTrial ? interp(copy.cardConsumedTrial.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel }) : `Assinar por ${priceLabel}`;
3463
+ return copy.cardConsumedTrial ? interp(copy.cardConsumedTrial.ctaTemplate, {
3464
+ price: priceLabel,
3465
+ days: trialDaysCardLabel
3466
+ }) : `Assinar por ${priceLabel}`;
3442
3467
  }
3443
3468
  return interp(copy.pix.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
3444
3469
  }, [
@@ -3476,10 +3501,10 @@ function Paywall({
3476
3501
  await s.submit();
3477
3502
  };
3478
3503
  const ctaTheme = s.selectedMethod === "card" ? themeClasses.ctaCard : themeClasses.ctaPix;
3479
- return /* @__PURE__ */ jsxs21("div", { className: themeClasses.container, children: [
3504
+ return /* @__PURE__ */ jsxs20("div", { className: themeClasses.container, children: [
3480
3505
  slots.heroSlot,
3481
3506
  /* @__PURE__ */ jsx34("h1", { className: themeClasses.headline, children: copy.headline }),
3482
- /* @__PURE__ */ jsx34("ul", { children: copy.features.map((f) => /* @__PURE__ */ jsxs21("li", { className: themeClasses.feature, children: [
3507
+ /* @__PURE__ */ jsx34("ul", { children: copy.features.map((f) => /* @__PURE__ */ jsxs20("li", { className: themeClasses.feature, children: [
3483
3508
  "\u2713 ",
3484
3509
  /* @__PURE__ */ jsx34("span", { children: f })
3485
3510
  ] }, f)) }),
@@ -3487,21 +3512,6 @@ function Paywall({
3487
3512
  slots.cyclePickerSlot ?? /* @__PURE__ */ jsx34(
3488
3513
  PaywallCyclePicker,
3489
3514
  {
3490
- cycles: ["MONTHLY", "YEARLY"],
3491
- selected: s.cycle,
3492
- onSelect: s.selectCycle,
3493
- priceCentsByCycle: {
3494
- MONTHLY: priceCentsForCycle(s, "MONTHLY"),
3495
- YEARLY: priceCentsForCycle(s, "YEARLY")
3496
- },
3497
- anchorCentsByCycle: {
3498
- MONTHLY: anchorForCycle(s, "MONTHLY"),
3499
- YEARLY: anchorForCycle(s, "YEARLY")
3500
- },
3501
- monthlyEquivByCycle: {
3502
- MONTHLY: priceCentsForCycle(s, "MONTHLY"),
3503
- YEARLY: Math.round(priceCentsForCycle(s, "YEARLY") / 12)
3504
- },
3505
3515
  labels: copy.cycle,
3506
3516
  cardClassName: themeClasses.cycleCard,
3507
3517
  cardSelectedClassName: themeClasses.cycleCardSelected,
@@ -3511,9 +3521,6 @@ function Paywall({
3511
3521
  /* @__PURE__ */ jsx34(
3512
3522
  PaywallMethodTabs,
3513
3523
  {
3514
- methods: s.methods,
3515
- selected: s.selectedMethod,
3516
- onSelect: s.selectMethod,
3517
3524
  labels: { "pix-auto": copy.pix.tabLabel, card: copy.card.tabLabel },
3518
3525
  className: themeClasses.tabs,
3519
3526
  tabClassName: themeClasses.tab,
@@ -3523,7 +3530,6 @@ function Paywall({
3523
3530
  /* @__PURE__ */ jsx34(
3524
3531
  PaywallMethodContent,
3525
3532
  {
3526
- method: s.selectedMethod,
3527
3533
  copy: {
3528
3534
  pix: interpolateCopy(copy.pix, priceLabel, trialDaysCardLabel),
3529
3535
  card: interpolateCopy(copy.card, priceLabel, trialDaysCardLabel),
@@ -3534,27 +3540,27 @@ function Paywall({
3534
3540
  ctaTemplate: copy.cardConsumedTrial.ctaTemplate
3535
3541
  } : void 0
3536
3542
  },
3537
- hasConsumedTrial: s.hasConsumedTrial,
3538
3543
  className: themeClasses.tabContent,
3539
3544
  rowClassName: themeClasses.tabContentRow
3540
3545
  }
3541
3546
  ),
3542
3547
  slots.beforeCtaSlot,
3543
- /* @__PURE__ */ jsx34(
3544
- PaywallCta,
3545
- {
3546
- ctaLabel,
3547
- loadingLabel: "Abrindo checkout\u2026",
3548
- switchHint,
3549
- trustLine: copy.trustLine,
3550
- onClick: handleCta,
3551
- disabled: s.submitting,
3552
- loading: s.submitting,
3553
- buttonClassName: ctaTheme,
3554
- switchHintClassName: themeClasses.switchHint,
3555
- trustClassName: themeClasses.trustLine
3556
- }
3557
- )
3548
+ /* @__PURE__ */ jsxs20("div", { children: [
3549
+ /* @__PURE__ */ jsx34(
3550
+ "button",
3551
+ {
3552
+ type: "button",
3553
+ onClick: () => {
3554
+ void handleCta();
3555
+ },
3556
+ disabled: s.submitting,
3557
+ className: ctaTheme,
3558
+ children: s.submitting ? "Abrindo checkout\u2026" : ctaLabel
3559
+ }
3560
+ ),
3561
+ switchHint ? /* @__PURE__ */ jsx34("p", { className: themeClasses.switchHint, children: switchHint }) : null,
3562
+ /* @__PURE__ */ jsx34("p", { className: themeClasses.trustLine, children: copy.trustLine })
3563
+ ] })
3558
3564
  ] });
3559
3565
  }
3560
3566
  function interp(tpl, vars) {
@@ -3568,13 +3574,438 @@ function interpolateCopy(m, price, days) {
3568
3574
  switchHint: m.switchHint
3569
3575
  };
3570
3576
  }
3571
- function priceCentsForCycle(s, c) {
3572
- return s.plan ? c === "YEARLY" ? s.plan.yearlyCents ?? 0 : s.plan.monthlyCents : 0;
3577
+
3578
+ // src/components/paywall/PaywallCta.tsx
3579
+ import { jsx as jsx35, jsxs as jsxs21 } from "react/jsx-runtime";
3580
+ function PaywallCta({
3581
+ ctaLabel,
3582
+ loadingLabel,
3583
+ switchHint,
3584
+ trustLine,
3585
+ className,
3586
+ buttonClassName,
3587
+ switchHintClassName,
3588
+ trustClassName
3589
+ }) {
3590
+ const { submit, submitting } = usePaywallContext();
3591
+ const label = submitting && loadingLabel ? loadingLabel : ctaLabel;
3592
+ return /* @__PURE__ */ jsxs21("div", { className, children: [
3593
+ /* @__PURE__ */ jsx35(
3594
+ "button",
3595
+ {
3596
+ type: "button",
3597
+ onClick: () => {
3598
+ void submit();
3599
+ },
3600
+ disabled: submitting,
3601
+ className: buttonClassName,
3602
+ children: label
3603
+ }
3604
+ ),
3605
+ switchHint ? /* @__PURE__ */ jsx35("p", { className: switchHintClassName, children: switchHint }) : null,
3606
+ /* @__PURE__ */ jsx35("p", { className: trustClassName, children: trustLine })
3607
+ ] });
3608
+ }
3609
+
3610
+ // src/components/paywall/blocks/PaywallEyebrow.tsx
3611
+ import { jsx as jsx36 } from "react/jsx-runtime";
3612
+ var DEFAULT_EYEBROW_CLASSES = "text-xs uppercase tracking-widest font-semibold opacity-70";
3613
+ function PaywallEyebrow({ text, className }) {
3614
+ return /* @__PURE__ */ jsx36("div", { className: [DEFAULT_EYEBROW_CLASSES, className].filter(Boolean).join(" "), children: text });
3573
3615
  }
3574
- function anchorForCycle(s, c) {
3575
- if (!s.plan) return null;
3576
- if (c === "YEARLY") return s.plan.anchorYearlyCents ?? null;
3577
- return s.plan.anchorMonthlyCents ?? null;
3616
+
3617
+ // src/components/paywall/blocks/PaywallHero.tsx
3618
+ import { jsx as jsx37, jsxs as jsxs22 } from "react/jsx-runtime";
3619
+ var DEFAULT_GRADIENT = "absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent";
3620
+ function PaywallHero({
3621
+ src,
3622
+ alt = "",
3623
+ headline,
3624
+ aspectRatio = "16/9",
3625
+ gradientClassName,
3626
+ className,
3627
+ headlineClassName,
3628
+ imgClassName,
3629
+ render
3630
+ }) {
3631
+ if (render) {
3632
+ return /* @__PURE__ */ jsx37("div", { className, children: render({ src, headline }) });
3633
+ }
3634
+ return /* @__PURE__ */ jsxs22(
3635
+ "div",
3636
+ {
3637
+ className: ["relative overflow-hidden", className].filter(Boolean).join(" "),
3638
+ style: { aspectRatio },
3639
+ children: [
3640
+ /* @__PURE__ */ jsx37(
3641
+ "img",
3642
+ {
3643
+ src,
3644
+ alt,
3645
+ className: ["absolute inset-0 w-full h-full object-cover", imgClassName].filter(Boolean).join(" ")
3646
+ }
3647
+ ),
3648
+ /* @__PURE__ */ jsx37("div", { className: gradientClassName ?? DEFAULT_GRADIENT, "aria-hidden": "true" }),
3649
+ headline ? /* @__PURE__ */ jsx37(
3650
+ "h1",
3651
+ {
3652
+ className: ["absolute bottom-0 left-0 right-0 p-4 text-white font-bold text-2xl", headlineClassName].filter(Boolean).join(" "),
3653
+ children: headline
3654
+ }
3655
+ ) : null
3656
+ ]
3657
+ }
3658
+ );
3659
+ }
3660
+
3661
+ // src/components/paywall/blocks/PaywallHeadline.tsx
3662
+ import { jsx as jsx38 } from "react/jsx-runtime";
3663
+ var DEFAULT_HEADLINE_CLASSES = "text-2xl font-bold leading-tight";
3664
+ function PaywallHeadline({ text, className, as = "h1" }) {
3665
+ const Tag = as;
3666
+ return /* @__PURE__ */ jsx38(Tag, { className: [DEFAULT_HEADLINE_CLASSES, className].filter(Boolean).join(" "), children: text });
3667
+ }
3668
+
3669
+ // src/components/paywall/blocks/PaywallPriceHeadline.tsx
3670
+ import { jsx as jsx39 } from "react/jsx-runtime";
3671
+ var DEFAULT_CLASS = "text-2xl font-bold leading-tight";
3672
+ var CYCLE_LABEL = {
3673
+ MONTHLY: "mensal",
3674
+ YEARLY: "anual"
3675
+ };
3676
+ function PaywallPriceHeadline({
3677
+ template,
3678
+ className,
3679
+ as = "h1",
3680
+ render
3681
+ }) {
3682
+ const { cycle, currentMonthlyEquivCents, plan } = usePaywallContext();
3683
+ const yearlyCents = plan?.yearlyCents ?? null;
3684
+ const pricePerDay = formatBRL(dailyFromYearly(yearlyCents));
3685
+ const monthlyEquiv = currentMonthlyEquivCents ?? 0;
3686
+ const cycleLabel = CYCLE_LABEL[cycle] ?? cycle.toLowerCase();
3687
+ const rootClasses = [DEFAULT_CLASS, className].filter(Boolean).join(" ");
3688
+ if (render) {
3689
+ const RootTag2 = as;
3690
+ return /* @__PURE__ */ jsx39(RootTag2, { className: [className].filter(Boolean).join(" ") || void 0, children: render({ pricePerDay, currentMonthlyEquivCents: monthlyEquiv, cycle }) });
3691
+ }
3692
+ const text = template.replaceAll("{pricePerDay}", pricePerDay).replaceAll("{currentMonthlyEquiv}", formatBRL(monthlyEquiv)).replaceAll("{cycle}", cycleLabel);
3693
+ const RootTag = as;
3694
+ return /* @__PURE__ */ jsx39(RootTag, { className: rootClasses, children: text });
3695
+ }
3696
+
3697
+ // src/components/paywall/blocks/PaywallCountdown.tsx
3698
+ import { useEffect as useEffect15, useRef as useRef7, useState as useState14 } from "react";
3699
+ import { jsx as jsx40 } from "react/jsx-runtime";
3700
+ var DEFAULT_COUNTDOWN_CLASSES = "font-mono tabular-nums";
3701
+ function resolveDeadlineMs(deadline) {
3702
+ if (deadline instanceof Date) return deadline.getTime();
3703
+ if (typeof deadline === "string") return new Date(deadline).getTime();
3704
+ const { sessionStorageKey, durationMs } = deadline;
3705
+ if (typeof window === "undefined" || typeof window.sessionStorage === "undefined") {
3706
+ return Date.now() + durationMs;
3707
+ }
3708
+ const stored = window.sessionStorage.getItem(sessionStorageKey);
3709
+ const parsed = stored ? Number.parseInt(stored, 10) : NaN;
3710
+ const now = Date.now();
3711
+ if (!Number.isFinite(parsed) || parsed < now) {
3712
+ const target = now + durationMs;
3713
+ window.sessionStorage.setItem(sessionStorageKey, String(target));
3714
+ return target;
3715
+ }
3716
+ return parsed;
3717
+ }
3718
+ function computeRemaining(deadlineMs) {
3719
+ const diff = Math.max(0, deadlineMs - Date.now());
3720
+ const totalSeconds = Math.floor(diff / 1e3);
3721
+ const h = Math.floor(totalSeconds / 3600);
3722
+ const m = Math.floor(totalSeconds % 3600 / 60);
3723
+ const s = totalSeconds % 60;
3724
+ return { h, m, s, expired: diff === 0 };
3725
+ }
3726
+ function pad(n) {
3727
+ return String(n).padStart(2, "0");
3728
+ }
3729
+ function PaywallCountdown({
3730
+ deadline,
3731
+ format = "h:m:s",
3732
+ onExpire,
3733
+ className,
3734
+ render
3735
+ }) {
3736
+ const deadlineMsRef = useRef7(null);
3737
+ if (deadlineMsRef.current === null) {
3738
+ deadlineMsRef.current = resolveDeadlineMs(deadline);
3739
+ }
3740
+ const [state, setState] = useState14(() => computeRemaining(deadlineMsRef.current));
3741
+ const expiredCalledRef = useRef7(false);
3742
+ useEffect15(() => {
3743
+ if (state.expired) {
3744
+ if (!expiredCalledRef.current) {
3745
+ expiredCalledRef.current = true;
3746
+ onExpire?.();
3747
+ }
3748
+ return;
3749
+ }
3750
+ const tick = () => {
3751
+ const next = computeRemaining(deadlineMsRef.current);
3752
+ setState(next);
3753
+ if (next.expired && !expiredCalledRef.current) {
3754
+ expiredCalledRef.current = true;
3755
+ onExpire?.();
3756
+ }
3757
+ };
3758
+ const id = setInterval(tick, 1e3);
3759
+ return () => clearInterval(id);
3760
+ }, [state.expired]);
3761
+ if (render) {
3762
+ return /* @__PURE__ */ jsx40("div", { className, children: render(state) });
3763
+ }
3764
+ const formatted = format === "h:m:s" ? `${pad(state.h)}:${pad(state.m)}:${pad(state.s)}` : `${pad(state.h * 60 + state.m)}:${pad(state.s)}`;
3765
+ return /* @__PURE__ */ jsx40("div", { className: [DEFAULT_COUNTDOWN_CLASSES, className].filter(Boolean).join(" "), children: formatted });
3766
+ }
3767
+
3768
+ // src/components/paywall/blocks/PaywallFeatures.tsx
3769
+ import { jsx as jsx41, jsxs as jsxs23 } from "react/jsx-runtime";
3770
+ function PaywallFeatures({
3771
+ items,
3772
+ IconComponent,
3773
+ className,
3774
+ itemClassName,
3775
+ iconClassName,
3776
+ render,
3777
+ renderItem
3778
+ }) {
3779
+ if (render) {
3780
+ return /* @__PURE__ */ jsx41("div", { className, children: render({ items }) });
3781
+ }
3782
+ if (renderItem) {
3783
+ return /* @__PURE__ */ jsx41("ul", { className, children: items.map((item, idx) => /* @__PURE__ */ jsx41("li", { children: renderItem(item, idx) }, idx)) });
3784
+ }
3785
+ return /* @__PURE__ */ jsx41("ul", { className, children: items.map((item, idx) => /* @__PURE__ */ jsxs23("li", { className: itemClassName, children: [
3786
+ IconComponent ? /* @__PURE__ */ jsx41(IconComponent, { className: iconClassName }) : /* @__PURE__ */ jsx41("span", { className: iconClassName, "aria-hidden": "true", children: "\u2713" }),
3787
+ /* @__PURE__ */ jsx41("span", { children: item })
3788
+ ] }, idx)) });
3789
+ }
3790
+
3791
+ // src/components/paywall/blocks/PaywallFeaturesCard.tsx
3792
+ import { jsx as jsx42, jsxs as jsxs24 } from "react/jsx-runtime";
3793
+ var DEFAULT_CARD_CLASSES = "rounded-xl border p-4";
3794
+ function PaywallFeaturesCard({
3795
+ title,
3796
+ items,
3797
+ className,
3798
+ cardClassName,
3799
+ titleClassName,
3800
+ itemClassName,
3801
+ renderItem
3802
+ }) {
3803
+ return /* @__PURE__ */ jsx42("div", { className, children: /* @__PURE__ */ jsxs24("div", { className: [DEFAULT_CARD_CLASSES, cardClassName].filter(Boolean).join(" "), children: [
3804
+ title ? /* @__PURE__ */ jsx42("div", { className: ["font-semibold mb-2", titleClassName].filter(Boolean).join(" "), children: title }) : null,
3805
+ /* @__PURE__ */ jsx42("ul", { children: items.map(
3806
+ (item, idx) => renderItem ? /* @__PURE__ */ jsx42("li", { children: renderItem(item, idx) }, idx) : /* @__PURE__ */ jsxs24("li", { className: itemClassName, children: [
3807
+ /* @__PURE__ */ jsx42("span", { "aria-hidden": "true", children: "\u2022" }),
3808
+ " ",
3809
+ /* @__PURE__ */ jsx42("span", { children: item })
3810
+ ] }, idx)
3811
+ ) })
3812
+ ] }) });
3813
+ }
3814
+
3815
+ // src/components/paywall/blocks/PaywallTrophyBadge.tsx
3816
+ import { jsx as jsx43, jsxs as jsxs25 } from "react/jsx-runtime";
3817
+ 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";
3818
+ var FLOATING_CLASSES = "absolute top-2 right-2 z-10 shadow-md";
3819
+ function PaywallTrophyBadge({
3820
+ text,
3821
+ className,
3822
+ iconClassName,
3823
+ floating = false,
3824
+ render
3825
+ }) {
3826
+ if (render) {
3827
+ return /* @__PURE__ */ jsx43("div", { className, children: render({ text }) });
3828
+ }
3829
+ const rootClasses = [
3830
+ DEFAULT_CHIP_CLASSES,
3831
+ floating ? FLOATING_CLASSES : "",
3832
+ className
3833
+ ].filter(Boolean).join(" ");
3834
+ return /* @__PURE__ */ jsxs25("div", { className: rootClasses, children: [
3835
+ /* @__PURE__ */ jsx43("span", { className: iconClassName, "aria-hidden": "true", children: "\u{1F3C6}" }),
3836
+ /* @__PURE__ */ jsx43("span", { children: text })
3837
+ ] });
3838
+ }
3839
+
3840
+ // src/components/paywall/blocks/PaywallAnchorPrice.tsx
3841
+ import { jsx as jsx44 } from "react/jsx-runtime";
3842
+ var DEFAULT_CLASS2 = "text-sm opacity-60 line-through";
3843
+ function PaywallAnchorPrice({
3844
+ className,
3845
+ render
3846
+ }) {
3847
+ const { anchorPriceCents, cycle } = usePaywallContext();
3848
+ if (anchorPriceCents === null || anchorPriceCents === void 0 || anchorPriceCents <= 0) {
3849
+ return null;
3850
+ }
3851
+ void cycle;
3852
+ const formatted = formatBRL(anchorPriceCents);
3853
+ const rootClasses = [DEFAULT_CLASS2, className].filter(Boolean).join(" ");
3854
+ if (render) {
3855
+ return /* @__PURE__ */ jsx44("span", { className: className || void 0, children: render({ anchorCents: anchorPriceCents, formatted }) });
3856
+ }
3857
+ return /* @__PURE__ */ jsx44("span", { className: rootClasses, children: formatted });
3858
+ }
3859
+
3860
+ // src/components/paywall/blocks/PaywallTestimonials.tsx
3861
+ import { jsx as jsx45, jsxs as jsxs26 } from "react/jsx-runtime";
3862
+ var DEFAULT_ROOT = "flex gap-3 overflow-x-auto snap-x snap-mandatory pb-2";
3863
+ var DEFAULT_CARD = "snap-start shrink-0 w-72 rounded-2xl border p-4 flex flex-col gap-2";
3864
+ var DEFAULT_AVATAR = "w-10 h-10 rounded-full object-cover";
3865
+ var DEFAULT_QUOTE = "text-sm leading-snug";
3866
+ var DEFAULT_NAME = "text-xs font-semibold opacity-80";
3867
+ var DEFAULT_STARS = "text-yellow-500 text-sm";
3868
+ function clampStars(n) {
3869
+ return Math.max(0, Math.min(5, Math.round(n)));
3870
+ }
3871
+ function PaywallTestimonials({
3872
+ items,
3873
+ className,
3874
+ cardClassName,
3875
+ avatarClassName,
3876
+ quoteClassName,
3877
+ nameClassName,
3878
+ starsClassName,
3879
+ renderItem
3880
+ }) {
3881
+ const rootClasses = [DEFAULT_ROOT, className].filter(Boolean).join(" ");
3882
+ const cardClasses = [DEFAULT_CARD, cardClassName].filter(Boolean).join(" ");
3883
+ const avatarClasses = [DEFAULT_AVATAR, avatarClassName].filter(Boolean).join(" ");
3884
+ const quoteClasses = [DEFAULT_QUOTE, quoteClassName].filter(Boolean).join(" ");
3885
+ const nameClasses = [DEFAULT_NAME, nameClassName].filter(Boolean).join(" ");
3886
+ const starsClasses = [DEFAULT_STARS, starsClassName].filter(Boolean).join(" ");
3887
+ return /* @__PURE__ */ jsx45("div", { className: rootClasses, children: items.map((item, idx) => {
3888
+ if (renderItem) return renderItem(item, idx);
3889
+ const filled = clampStars(item.stars);
3890
+ const empty = 5 - filled;
3891
+ return /* @__PURE__ */ jsxs26("div", { className: cardClasses, children: [
3892
+ /* @__PURE__ */ jsxs26("div", { className: "flex items-center gap-2", children: [
3893
+ item.avatar ? /* @__PURE__ */ jsx45(
3894
+ "img",
3895
+ {
3896
+ src: item.avatar,
3897
+ alt: "",
3898
+ loading: "lazy",
3899
+ className: avatarClasses,
3900
+ "aria-hidden": "true"
3901
+ }
3902
+ ) : null,
3903
+ /* @__PURE__ */ jsx45("div", { className: nameClasses, children: item.name })
3904
+ ] }),
3905
+ /* @__PURE__ */ jsxs26("div", { className: starsClasses, "aria-label": `${filled} de 5 estrelas`, children: [
3906
+ "\u2605".repeat(filled),
3907
+ "\u2606".repeat(empty)
3908
+ ] }),
3909
+ /* @__PURE__ */ jsx45("p", { className: quoteClasses, children: item.quote })
3910
+ ] }, idx);
3911
+ }) });
3912
+ }
3913
+
3914
+ // src/components/paywall/blocks/PaywallStatsRow.tsx
3915
+ import { jsx as jsx46, jsxs as jsxs27 } from "react/jsx-runtime";
3916
+ var DEFAULT_ROOT2 = "grid grid-cols-3 gap-4";
3917
+ var DEFAULT_CELL = "flex flex-col items-center text-center";
3918
+ var DEFAULT_VALUE = "text-2xl font-bold";
3919
+ var DEFAULT_LABEL = "text-xs opacity-70";
3920
+ function PaywallStatsRow({
3921
+ stats,
3922
+ className,
3923
+ cellClassName,
3924
+ valueClassName,
3925
+ labelClassName,
3926
+ renderCell
3927
+ }) {
3928
+ const rootClasses = [DEFAULT_ROOT2, className].filter(Boolean).join(" ");
3929
+ const cellClasses = [DEFAULT_CELL, cellClassName].filter(Boolean).join(" ");
3930
+ const valueClasses = [DEFAULT_VALUE, valueClassName].filter(Boolean).join(" ");
3931
+ const labelClasses = [DEFAULT_LABEL, labelClassName].filter(Boolean).join(" ");
3932
+ return /* @__PURE__ */ jsx46("div", { className: rootClasses, children: stats.map((stat, idx) => {
3933
+ if (renderCell) return renderCell(stat, idx);
3934
+ return /* @__PURE__ */ jsxs27("div", { className: cellClasses, children: [
3935
+ stat.icon ? /* @__PURE__ */ jsx46("div", { "aria-hidden": "true", children: stat.icon }) : null,
3936
+ /* @__PURE__ */ jsx46("div", { className: valueClasses, children: stat.value }),
3937
+ /* @__PURE__ */ jsx46("div", { className: labelClasses, children: stat.label })
3938
+ ] }, idx);
3939
+ }) });
3940
+ }
3941
+
3942
+ // src/components/paywall/blocks/PaywallFinePrint.tsx
3943
+ import { jsx as jsx47 } from "react/jsx-runtime";
3944
+ var DEFAULT_CLASS3 = "text-xs opacity-60 leading-snug";
3945
+ var CYCLE_LABEL2 = {
3946
+ MONTHLY: "mensal",
3947
+ YEARLY: "anual"
3948
+ };
3949
+ function PaywallFinePrint({
3950
+ template,
3951
+ className,
3952
+ render
3953
+ }) {
3954
+ const {
3955
+ currentPriceCents,
3956
+ cycle,
3957
+ trialDaysCard,
3958
+ trialDaysPix,
3959
+ selectedMethod
3960
+ } = usePaywallContext();
3961
+ const trialDays = selectedMethod === "card" ? trialDaysCard : trialDaysPix;
3962
+ const cycleLabel = CYCLE_LABEL2[cycle] ?? cycle.toLowerCase();
3963
+ const priceFormatted = formatBRL(currentPriceCents ?? 0);
3964
+ const rootClasses = [DEFAULT_CLASS3, className].filter(Boolean).join(" ");
3965
+ if (render) {
3966
+ return /* @__PURE__ */ jsx47("p", { className: className || void 0, children: render({
3967
+ currentPriceCents: currentPriceCents ?? 0,
3968
+ cycle,
3969
+ trialDays: trialDays ?? 0,
3970
+ selectedMethod
3971
+ }) });
3972
+ }
3973
+ const text = template.replaceAll("{price}", priceFormatted).replaceAll("{trialDays}", String(trialDays ?? 0)).replaceAll("{cycle}", cycleLabel);
3974
+ return /* @__PURE__ */ jsx47("p", { className: rootClasses, children: text });
3975
+ }
3976
+
3977
+ // src/components/paywall/blocks/PaywallTrustLine.tsx
3978
+ import { jsx as jsx48, jsxs as jsxs28 } from "react/jsx-runtime";
3979
+ var DEFAULT_ROOT3 = "flex items-center gap-3";
3980
+ var DEFAULT_ITEM = "flex items-center gap-1.5 text-xs";
3981
+ function PaywallTrustLine({
3982
+ items,
3983
+ className,
3984
+ itemClassName,
3985
+ renderItem
3986
+ }) {
3987
+ const rootClasses = [DEFAULT_ROOT3, className].filter(Boolean).join(" ");
3988
+ const itemClasses = [DEFAULT_ITEM, itemClassName].filter(Boolean).join(" ");
3989
+ return /* @__PURE__ */ jsx48("div", { className: rootClasses, children: items.map((item, idx) => {
3990
+ if (renderItem) return renderItem(item, idx);
3991
+ return /* @__PURE__ */ jsxs28("span", { className: itemClasses, children: [
3992
+ /* @__PURE__ */ jsx48("span", { "aria-hidden": "true", children: item.icon }),
3993
+ /* @__PURE__ */ jsx48("span", { children: item.text })
3994
+ ] }, idx);
3995
+ }) });
3996
+ }
3997
+
3998
+ // src/components/paywall/blocks/PaywallStickyFooter.tsx
3999
+ import { jsx as jsx49 } from "react/jsx-runtime";
4000
+ var DEFAULT_CLASSES = "sticky bottom-0 left-0 right-0 bg-background";
4001
+ var SAFE_AREA_CLASS = "pb-[env(safe-area-inset-bottom)]";
4002
+ function PaywallStickyFooter({
4003
+ children,
4004
+ className,
4005
+ safeAreaInsets = true
4006
+ }) {
4007
+ const classes = [DEFAULT_CLASSES, safeAreaInsets ? SAFE_AREA_CLASS : null, className].filter(Boolean).join(" ");
4008
+ return /* @__PURE__ */ jsx49("div", { className: classes, children });
3578
4009
  }
3579
4010
  export {
3580
4011
  AppConfigProvider,
@@ -3592,10 +4023,26 @@ export {
3592
4023
  OnboardingFlow,
3593
4024
  PaymentReturnHandler,
3594
4025
  Paywall,
4026
+ PaywallAnchorPrice,
4027
+ PaywallContext,
4028
+ PaywallCountdown,
3595
4029
  PaywallCta,
3596
4030
  PaywallCyclePicker,
4031
+ PaywallEyebrow,
4032
+ PaywallFeatures,
4033
+ PaywallFeaturesCard,
4034
+ PaywallFinePrint,
4035
+ PaywallHeadline,
4036
+ PaywallHero,
3597
4037
  PaywallMethodContent,
3598
4038
  PaywallMethodTabs,
4039
+ PaywallPriceHeadline,
4040
+ PaywallProvider,
4041
+ PaywallStatsRow,
4042
+ PaywallStickyFooter,
4043
+ PaywallTestimonials,
4044
+ PaywallTrophyBadge,
4045
+ PaywallTrustLine,
3599
4046
  PersistenceRegistry,
3600
4047
  PreAuthShell,
3601
4048
  PushPrompt2 as PushPrompt,
@@ -3624,6 +4071,7 @@ export {
3624
4071
  useInstallPrompt,
3625
4072
  useLoginForm,
3626
4073
  useOnboardingStep,
4074
+ usePaywallContext,
3627
4075
  usePaywallState,
3628
4076
  usePlan,
3629
4077
  usePush,