@hook-sdk/template 0.23.3 → 0.25.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
@@ -1,7 +1,7 @@
1
1
  // src/AppRoot.tsx
2
2
  import { useMemo as useMemo3 } from "react";
3
3
  import { BrowserRouter, MemoryRouter, Navigate, Route, Routes } from "react-router-dom";
4
- import { useHook as useHook7 } from "@hook-sdk/sdk";
4
+ import { useHook as useHook8 } from "@hook-sdk/sdk";
5
5
 
6
6
  // src/config/AppConfigContext.tsx
7
7
  import { createContext, useContext } from "react";
@@ -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
  };
@@ -2031,9 +2031,53 @@ function SessionExpiredBanner() {
2031
2031
  ] });
2032
2032
  }
2033
2033
 
2034
+ // src/internal/EmailVerifyBanner.tsx
2035
+ import { useState as useState5 } from "react";
2036
+ import { useHook as useHook5 } from "@hook-sdk/sdk";
2037
+ import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
2038
+ function EmailVerifyBanner() {
2039
+ const { user, auth } = useHook5();
2040
+ const [sending, setSending] = useState5(false);
2041
+ const [sent, setSent] = useState5(false);
2042
+ if (!user || user.emailVerified) return null;
2043
+ async function handleResend() {
2044
+ if (sending || sent) return;
2045
+ setSending(true);
2046
+ try {
2047
+ await auth.resendVerify();
2048
+ setSent(true);
2049
+ } catch {
2050
+ } finally {
2051
+ setSending(false);
2052
+ }
2053
+ }
2054
+ const label = sent ? "Enviado!" : sending ? "Enviando..." : "Reenviar link";
2055
+ return /* @__PURE__ */ jsxs12(
2056
+ "div",
2057
+ {
2058
+ role: "status",
2059
+ "data-testid": "email-verify-banner",
2060
+ className: "sticky top-0 inset-x-0 z-50 bg-yellow-100 text-yellow-900 border-b border-yellow-200 px-4 py-2 flex items-center justify-between gap-3 text-sm",
2061
+ children: [
2062
+ /* @__PURE__ */ jsx18("span", { children: "Confirma teu e-mail pra liberar tudo." }),
2063
+ /* @__PURE__ */ jsx18(
2064
+ "button",
2065
+ {
2066
+ type: "button",
2067
+ onClick: handleResend,
2068
+ disabled: sending || sent,
2069
+ className: "px-3 py-1 rounded text-xs font-medium bg-yellow-900 text-yellow-50 hover:bg-yellow-800 disabled:opacity-60 disabled:cursor-not-allowed",
2070
+ children: label
2071
+ }
2072
+ )
2073
+ ]
2074
+ }
2075
+ );
2076
+ }
2077
+
2034
2078
  // src/defaults/ErrorBoundary.tsx
2035
2079
  import { Component } from "react";
2036
- import { Fragment as Fragment4, jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
2080
+ import { Fragment as Fragment4, jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
2037
2081
  var ErrorBoundary = class extends Component {
2038
2082
  state = { error: null };
2039
2083
  static getDerivedStateFromError(error) {
@@ -2051,12 +2095,12 @@ var ErrorBoundary = class extends Component {
2051
2095
  }
2052
2096
  render() {
2053
2097
  if (this.state.error) {
2054
- return this.props.fallback ?? /* @__PURE__ */ jsxs12("div", { role: "alert", style: { padding: 24, textAlign: "center" }, children: [
2055
- /* @__PURE__ */ jsx18("h2", { children: "Algo deu errado" }),
2056
- /* @__PURE__ */ jsx18("p", { style: { opacity: 0.7 }, children: "Recarregue a p\xE1gina pra tentar de novo." })
2098
+ return this.props.fallback ?? /* @__PURE__ */ jsxs13("div", { role: "alert", style: { padding: 24, textAlign: "center" }, children: [
2099
+ /* @__PURE__ */ jsx19("h2", { children: "Algo deu errado" }),
2100
+ /* @__PURE__ */ jsx19("p", { style: { opacity: 0.7 }, children: "Recarregue a p\xE1gina pra tentar de novo." })
2057
2101
  ] });
2058
2102
  }
2059
- return /* @__PURE__ */ jsx18(Fragment4, { children: this.props.children });
2103
+ return /* @__PURE__ */ jsx19(Fragment4, { children: this.props.children });
2060
2104
  }
2061
2105
  };
2062
2106
 
@@ -2065,7 +2109,7 @@ import { useEffect as useEffect7 } from "react";
2065
2109
  import i18n from "i18next";
2066
2110
  import { I18nextProvider, initReactI18next } from "react-i18next";
2067
2111
  import { usePersistedState } from "@hook-sdk/sdk";
2068
- import { jsx as jsx19 } from "react/jsx-runtime";
2112
+ import { jsx as jsx20 } from "react/jsx-runtime";
2069
2113
  function ensureInitialized(defaultLocale, supportedLocales, resources, initialLocale) {
2070
2114
  if (i18n.isInitialized) return;
2071
2115
  i18n.use(initReactI18next).init({
@@ -2094,7 +2138,7 @@ function I18nProvider({
2094
2138
  i18n.changeLanguage(userLocale);
2095
2139
  }
2096
2140
  }, [userLocale]);
2097
- return /* @__PURE__ */ jsx19(I18nextProvider, { i18n, children });
2141
+ return /* @__PURE__ */ jsx20(I18nextProvider, { i18n, children });
2098
2142
  }
2099
2143
 
2100
2144
  // src/dev/env.ts
@@ -2105,9 +2149,9 @@ function isDevToolsEnabled() {
2105
2149
  }
2106
2150
 
2107
2151
  // src/dev/DevSkipOnboardingFab.tsx
2108
- import { useCallback as useCallback3, useRef as useRef4, useState as useState5 } from "react";
2109
- import { useHook as useHook5 } from "@hook-sdk/sdk";
2110
- import { jsx as jsx20 } from "react/jsx-runtime";
2152
+ import { useCallback as useCallback3, useRef as useRef4, useState as useState6 } from "react";
2153
+ import { useHook as useHook6 } from "@hook-sdk/sdk";
2154
+ import { jsx as jsx21 } from "react/jsx-runtime";
2111
2155
  var STORAGE_KEY = "hook_dev_skip_email";
2112
2156
  var TEST_EMAIL_DOMAIN = "@hook.test";
2113
2157
  var TEST_PASSWORD = "SkipTest!2026";
@@ -2174,10 +2218,10 @@ var STYLES = {
2174
2218
  };
2175
2219
  var CONFIRM_TIMEOUT_MS = 3e3;
2176
2220
  function DevSkipOnboardingFab({ defaults }) {
2177
- const hook = useHook5();
2221
+ const hook = useHook6();
2178
2222
  const { slug } = useAppConfig();
2179
- const [state, setState] = useState5("idle");
2180
- const [errorMsg, setErrorMsg] = useState5(null);
2223
+ const [state, setState] = useState6("idle");
2224
+ const [errorMsg, setErrorMsg] = useState6(null);
2181
2225
  const timerRef = useRef4(null);
2182
2226
  const clearTimer = useCallback3(() => {
2183
2227
  if (timerRef.current) {
@@ -2214,7 +2258,7 @@ function DevSkipOnboardingFab({ defaults }) {
2214
2258
  ...state === "confirm" || state === "error" ? STYLES.confirm : {},
2215
2259
  ...state === "busy" ? STYLES.busy : {}
2216
2260
  };
2217
- return /* @__PURE__ */ jsx20(
2261
+ return /* @__PURE__ */ jsx21(
2218
2262
  "button",
2219
2263
  {
2220
2264
  type: "button",
@@ -2229,20 +2273,20 @@ function DevSkipOnboardingFab({ defaults }) {
2229
2273
  }
2230
2274
 
2231
2275
  // src/internal/PaymentReturnHandler.tsx
2232
- import { useCallback as useCallback4, useEffect as useEffect8, useRef as useRef5, useState as useState6 } from "react";
2233
- import { useHook as useHook6 } from "@hook-sdk/sdk";
2234
- import { Fragment as Fragment5, jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
2276
+ import { useCallback as useCallback4, useEffect as useEffect8, useRef as useRef5, useState as useState7 } from "react";
2277
+ import { useHook as useHook7 } from "@hook-sdk/sdk";
2278
+ import { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
2235
2279
  var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
2236
2280
  var MAX_CYCLES = 3;
2237
2281
  var SUPPORT_MAILTO = "mailto:suporte@usehook.net?subject=Pagamento%20pendente";
2238
2282
  function PaymentReturnHandler({ children }) {
2239
- const { subscription, track: track2 } = useHook6();
2283
+ const { subscription, track: track2 } = useHook7();
2240
2284
  const subRef = useRef5(subscription);
2241
2285
  subRef.current = subscription;
2242
2286
  const runIdRef = useRef5(0);
2243
2287
  const cyclesRef = useRef5(0);
2244
2288
  const startMsRef = useRef5(0);
2245
- const [state, setState] = useState6("idle");
2289
+ const [state, setState] = useState7("idle");
2246
2290
  const runPoll = useCallback4(() => {
2247
2291
  const runId = ++runIdRef.current;
2248
2292
  const isFirstRun = cyclesRef.current === 0;
@@ -2308,19 +2352,19 @@ function PaymentReturnHandler({ children }) {
2308
2352
  window.location.href = cleanUrl.toString();
2309
2353
  }, []);
2310
2354
  if (state === "confirming") {
2311
- return /* @__PURE__ */ jsx21("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: "Confirmando pagamento\u2026" });
2355
+ return /* @__PURE__ */ jsx22("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: "Confirmando pagamento\u2026" });
2312
2356
  }
2313
2357
  if (state === "waiting") {
2314
- return /* @__PURE__ */ jsx21("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ jsxs13("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
2315
- /* @__PURE__ */ jsx21("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
2316
- /* @__PURE__ */ jsx21("button", { type: "button", onClick: runPoll, style: buttonStyle, children: "Atualizar" })
2358
+ return /* @__PURE__ */ jsx22("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ jsxs14("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
2359
+ /* @__PURE__ */ jsx22("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
2360
+ /* @__PURE__ */ jsx22("button", { type: "button", onClick: runPoll, style: buttonStyle, children: "Atualizar" })
2317
2361
  ] }) });
2318
2362
  }
2319
2363
  if (state === "timeout") {
2320
- return /* @__PURE__ */ jsx21("div", { role: "alert", "aria-live": "assertive", style: overlayStyle2, children: /* @__PURE__ */ jsxs13("div", { style: { maxWidth: 360, textAlign: "center", lineHeight: 1.5 }, children: [
2321
- /* @__PURE__ */ jsx21("div", { style: { marginBottom: 16 }, children: "Ainda n\xE3o conseguimos confirmar seu pagamento com o banco. Voc\xEA pode tentar de novo, voltar pro app, ou falar com a gente." }),
2322
- /* @__PURE__ */ jsxs13("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
2323
- /* @__PURE__ */ jsx21(
2364
+ return /* @__PURE__ */ jsx22("div", { role: "alert", "aria-live": "assertive", style: overlayStyle2, children: /* @__PURE__ */ jsxs14("div", { style: { maxWidth: 360, textAlign: "center", lineHeight: 1.5 }, children: [
2365
+ /* @__PURE__ */ jsx22("div", { style: { marginBottom: 16 }, children: "Ainda n\xE3o conseguimos confirmar seu pagamento com o banco. Voc\xEA pode tentar de novo, voltar pro app, ou falar com a gente." }),
2366
+ /* @__PURE__ */ jsxs14("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
2367
+ /* @__PURE__ */ jsx22(
2324
2368
  "button",
2325
2369
  {
2326
2370
  type: "button",
@@ -2333,7 +2377,7 @@ function PaymentReturnHandler({ children }) {
2333
2377
  children: "Tentar de novo"
2334
2378
  }
2335
2379
  ),
2336
- /* @__PURE__ */ jsx21(
2380
+ /* @__PURE__ */ jsx22(
2337
2381
  "button",
2338
2382
  {
2339
2383
  type: "button",
@@ -2343,7 +2387,7 @@ function PaymentReturnHandler({ children }) {
2343
2387
  children: "Voltar pro app"
2344
2388
  }
2345
2389
  ),
2346
- /* @__PURE__ */ jsx21(
2390
+ /* @__PURE__ */ jsx22(
2347
2391
  "a",
2348
2392
  {
2349
2393
  href: SUPPORT_MAILTO,
@@ -2355,7 +2399,7 @@ function PaymentReturnHandler({ children }) {
2355
2399
  ] })
2356
2400
  ] }) });
2357
2401
  }
2358
- return /* @__PURE__ */ jsx21(Fragment5, { children });
2402
+ return /* @__PURE__ */ jsx22(Fragment5, { children });
2359
2403
  }
2360
2404
  var overlayStyle2 = {
2361
2405
  position: "fixed",
@@ -2394,7 +2438,7 @@ var linkStyle = {
2394
2438
  };
2395
2439
 
2396
2440
  // src/AppRoot.tsx
2397
- import { Fragment as Fragment6, jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
2441
+ import { Fragment as Fragment6, jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
2398
2442
  function buildLegacyConfigShim(config) {
2399
2443
  const paywall = config.paywall;
2400
2444
  const isFree = paywall.mode === "free";
@@ -2473,14 +2517,17 @@ function AppRoot(props) {
2473
2517
  const basename = `/app/${config.slug}`;
2474
2518
  const routerProps = testRouter === "memory" ? { basename, initialEntries: testInitialEntries } : { basename };
2475
2519
  const position = config.install_prompt?.position ?? "post-paywall";
2476
- const subscriptionGated = /* @__PURE__ */ jsx22(SubscriptionGate, { Paywall: Paywall2 ?? FallbackPaywall, children: position === "post-paywall" ? /* @__PURE__ */ jsxs14(InstallGate, { position: "post-paywall", children: [
2477
- children,
2478
- /* @__PURE__ */ jsx22(PushPrompt, {})
2479
- ] }) : /* @__PURE__ */ jsxs14(Fragment6, { children: [
2480
- children,
2481
- /* @__PURE__ */ jsx22(PushPrompt, {})
2482
- ] }) });
2483
- const authGated = /* @__PURE__ */ jsx22(
2520
+ const subscriptionGated = /* @__PURE__ */ jsxs15(SubscriptionGate, { Paywall: Paywall2 ?? FallbackPaywall, children: [
2521
+ /* @__PURE__ */ jsx23(EmailVerifyBanner, {}),
2522
+ position === "post-paywall" ? /* @__PURE__ */ jsxs15(InstallGate, { position: "post-paywall", children: [
2523
+ children,
2524
+ /* @__PURE__ */ jsx23(PushPrompt, {})
2525
+ ] }) : /* @__PURE__ */ jsxs15(Fragment6, { children: [
2526
+ children,
2527
+ /* @__PURE__ */ jsx23(PushPrompt, {})
2528
+ ] })
2529
+ ] });
2530
+ const authGated = /* @__PURE__ */ jsx23(
2484
2531
  AuthGated,
2485
2532
  {
2486
2533
  config,
@@ -2495,13 +2542,13 @@ function AppRoot(props) {
2495
2542
  children: subscriptionGated
2496
2543
  }
2497
2544
  );
2498
- const routedTree = /* @__PURE__ */ jsxs14(Router, { ...routerProps, children: [
2499
- /* @__PURE__ */ jsx22(DeepLinkHandler, { deepLinks: config.deepLinks }),
2500
- /* @__PURE__ */ jsx22(SessionExpiredBanner, {}),
2501
- position === "pre-auth" ? /* @__PURE__ */ jsx22(InstallGate, { position: "pre-auth", children: authGated }) : authGated,
2502
- isDevToolsEnabled() && devSkipOnboarding ? /* @__PURE__ */ jsx22(DevSkipOnboardingFab, { defaults: devSkipOnboarding.defaults }) : null
2545
+ const routedTree = /* @__PURE__ */ jsxs15(Router, { ...routerProps, children: [
2546
+ /* @__PURE__ */ jsx23(DeepLinkHandler, { deepLinks: config.deepLinks }),
2547
+ /* @__PURE__ */ jsx23(SessionExpiredBanner, {}),
2548
+ position === "pre-auth" ? /* @__PURE__ */ jsx23(InstallGate, { position: "pre-auth", children: authGated }) : authGated,
2549
+ isDevToolsEnabled() && devSkipOnboarding ? /* @__PURE__ */ jsx23(DevSkipOnboardingFab, { defaults: devSkipOnboarding.defaults }) : null
2503
2550
  ] });
2504
- return /* @__PURE__ */ jsx22(ErrorBoundary, { children: /* @__PURE__ */ jsx22(AppConfigProvider, { config, children: /* @__PURE__ */ jsx22(TemplateConfigProvider, { config: legacyShim, children: /* @__PURE__ */ jsx22(ThemeProvider, { children: /* @__PURE__ */ jsx22(PersistenceRegistry, { config: config.persistedKeys, children: config.i18n ? /* @__PURE__ */ jsx22(
2551
+ return /* @__PURE__ */ jsx23(ErrorBoundary, { children: /* @__PURE__ */ jsx23(AppConfigProvider, { config, children: /* @__PURE__ */ jsx23(TemplateConfigProvider, { config: legacyShim, children: /* @__PURE__ */ jsx23(ThemeProvider, { children: /* @__PURE__ */ jsx23(PersistenceRegistry, { config: config.persistedKeys, children: config.i18n ? /* @__PURE__ */ jsx23(
2505
2552
  I18nProvider,
2506
2553
  {
2507
2554
  defaultLocale: config.i18n.defaultLocale,
@@ -2521,37 +2568,37 @@ function AuthGated({
2521
2568
  EmailVerify,
2522
2569
  PreAuthFlow
2523
2570
  }) {
2524
- const { authStatus } = useHook7();
2571
+ const { authStatus } = useHook8();
2525
2572
  if (authStatus === "loading") return null;
2526
2573
  if (authStatus !== "authenticated") {
2527
2574
  if (config.onboarding?.trigger === "pre_signup_custom" && PreAuthFlow) {
2528
- return /* @__PURE__ */ jsxs14(Routes, { children: [
2529
- /* @__PURE__ */ jsx22(Route, { path: "/signin", element: /* @__PURE__ */ jsx22(Login, {}) }),
2530
- /* @__PURE__ */ jsx22(Route, { path: "/signup", element: /* @__PURE__ */ jsx22(Signup, {}) }),
2531
- /* @__PURE__ */ jsx22(Route, { path: "/forgot", element: /* @__PURE__ */ jsx22(Forgot, {}) }),
2532
- /* @__PURE__ */ jsx22(Route, { path: "/reset", element: /* @__PURE__ */ jsx22(Reset, {}) }),
2533
- EmailVerify ? /* @__PURE__ */ jsx22(Route, { path: "/verify", element: /* @__PURE__ */ jsx22(EmailVerify, {}) }) : null,
2534
- /* @__PURE__ */ jsx22(Route, { path: "/*", element: /* @__PURE__ */ jsx22(PreAuthFlow, {}) })
2575
+ return /* @__PURE__ */ jsxs15(Routes, { children: [
2576
+ /* @__PURE__ */ jsx23(Route, { path: "/signin", element: /* @__PURE__ */ jsx23(Login, {}) }),
2577
+ /* @__PURE__ */ jsx23(Route, { path: "/signup", element: /* @__PURE__ */ jsx23(Signup, {}) }),
2578
+ /* @__PURE__ */ jsx23(Route, { path: "/forgot", element: /* @__PURE__ */ jsx23(Forgot, {}) }),
2579
+ /* @__PURE__ */ jsx23(Route, { path: "/reset", element: /* @__PURE__ */ jsx23(Reset, {}) }),
2580
+ EmailVerify ? /* @__PURE__ */ jsx23(Route, { path: "/verify", element: /* @__PURE__ */ jsx23(EmailVerify, {}) }) : null,
2581
+ /* @__PURE__ */ jsx23(Route, { path: "/*", element: /* @__PURE__ */ jsx23(PreAuthFlow, {}) })
2535
2582
  ] });
2536
2583
  }
2537
- return /* @__PURE__ */ jsxs14(Routes, { children: [
2538
- /* @__PURE__ */ jsx22(Route, { path: "/", element: /* @__PURE__ */ jsx22(Login, {}) }),
2539
- /* @__PURE__ */ jsx22(Route, { path: "/signup", element: /* @__PURE__ */ jsx22(Signup, {}) }),
2540
- /* @__PURE__ */ jsx22(Route, { path: "/forgot", element: /* @__PURE__ */ jsx22(Forgot, {}) }),
2541
- /* @__PURE__ */ jsx22(Route, { path: "/reset", element: /* @__PURE__ */ jsx22(Reset, {}) }),
2542
- EmailVerify ? /* @__PURE__ */ jsx22(Route, { path: "/verify", element: /* @__PURE__ */ jsx22(EmailVerify, {}) }) : null,
2543
- /* @__PURE__ */ jsx22(Route, { path: "*", element: /* @__PURE__ */ jsx22(Navigate, { to: "/", replace: true }) })
2584
+ return /* @__PURE__ */ jsxs15(Routes, { children: [
2585
+ /* @__PURE__ */ jsx23(Route, { path: "/", element: /* @__PURE__ */ jsx23(Login, {}) }),
2586
+ /* @__PURE__ */ jsx23(Route, { path: "/signup", element: /* @__PURE__ */ jsx23(Signup, {}) }),
2587
+ /* @__PURE__ */ jsx23(Route, { path: "/forgot", element: /* @__PURE__ */ jsx23(Forgot, {}) }),
2588
+ /* @__PURE__ */ jsx23(Route, { path: "/reset", element: /* @__PURE__ */ jsx23(Reset, {}) }),
2589
+ EmailVerify ? /* @__PURE__ */ jsx23(Route, { path: "/verify", element: /* @__PURE__ */ jsx23(EmailVerify, {}) }) : null,
2590
+ /* @__PURE__ */ jsx23(Route, { path: "*", element: /* @__PURE__ */ jsx23(Navigate, { to: "/", replace: true }) })
2544
2591
  ] });
2545
2592
  }
2546
- return /* @__PURE__ */ jsx22(Fragment6, { children });
2593
+ return /* @__PURE__ */ jsx23(Fragment6, { children });
2547
2594
  }
2548
2595
  function FallbackPaywall() {
2549
2596
  return null;
2550
2597
  }
2551
2598
 
2552
2599
  // src/hooks/usePush.ts
2553
- import { useCallback as useCallback5, useEffect as useEffect9, useState as useState7 } from "react";
2554
- import { useHook as useHook8 } from "@hook-sdk/sdk";
2600
+ import { useCallback as useCallback5, useEffect as useEffect9, useState as useState8 } from "react";
2601
+ import { useHook as useHook9 } from "@hook-sdk/sdk";
2555
2602
  var DISMISS_STORAGE_KEY = "push:dismissed-until";
2556
2603
  var DISMISS_TTL_MS2 = 7 * 24 * 60 * 60 * 1e3;
2557
2604
  function detectIosNeedsInstall() {
@@ -2595,8 +2642,8 @@ function deriveState(push) {
2595
2642
  return { kind: "prompt" };
2596
2643
  }
2597
2644
  function usePush() {
2598
- const { push } = useHook8();
2599
- const [state, setState] = useState7(() => deriveState(push));
2645
+ const { push } = useHook9();
2646
+ const [state, setState] = useState8(() => deriveState(push));
2600
2647
  useEffect9(() => {
2601
2648
  setState(deriveState(push));
2602
2649
  }, [push]);
@@ -2634,27 +2681,27 @@ function usePush() {
2634
2681
  }
2635
2682
 
2636
2683
  // src/components/PushPrompt.tsx
2637
- import { jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
2684
+ import { jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
2638
2685
  function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, className }) {
2639
2686
  const { state, subscribe } = usePush();
2640
2687
  if (state.kind === "denied" || state.kind === "dismissed" || state.kind === "subscribed") {
2641
2688
  return null;
2642
2689
  }
2643
2690
  if (state.kind === "ios_needs_install") {
2644
- return /* @__PURE__ */ jsxs15("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
2645
- /* @__PURE__ */ jsx23("h3", { children: texts.iosInstallTitle }),
2646
- /* @__PURE__ */ jsx23("p", { children: texts.iosInstallBody }),
2647
- onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx23("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
2691
+ return /* @__PURE__ */ jsxs16("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
2692
+ /* @__PURE__ */ jsx24("h3", { children: texts.iosInstallTitle }),
2693
+ /* @__PURE__ */ jsx24("p", { children: texts.iosInstallBody }),
2694
+ onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx24("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
2648
2695
  ] });
2649
2696
  }
2650
2697
  if (state.kind === "unsupported") {
2651
- return /* @__PURE__ */ jsx23("div", { className, role: "region", children: /* @__PURE__ */ jsx23("p", { children: texts.unsupportedBody }) });
2698
+ return /* @__PURE__ */ jsx24("div", { className, role: "region", children: /* @__PURE__ */ jsx24("p", { children: texts.unsupportedBody }) });
2652
2699
  }
2653
2700
  if (state.kind === "error") {
2654
- return /* @__PURE__ */ jsx23("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx23("p", { children: state.message }) });
2701
+ return /* @__PURE__ */ jsx24("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx24("p", { children: state.message }) });
2655
2702
  }
2656
- return /* @__PURE__ */ jsxs15("div", { className, role: "region", children: [
2657
- /* @__PURE__ */ jsx23(
2703
+ return /* @__PURE__ */ jsxs16("div", { className, role: "region", children: [
2704
+ /* @__PURE__ */ jsx24(
2658
2705
  "button",
2659
2706
  {
2660
2707
  type: "button",
@@ -2668,13 +2715,13 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
2668
2715
  children: texts.cta
2669
2716
  }
2670
2717
  ),
2671
- onDeclined && /* @__PURE__ */ jsx23("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
2718
+ onDeclined && /* @__PURE__ */ jsx24("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
2672
2719
  ] });
2673
2720
  }
2674
2721
 
2675
2722
  // src/components/LanguageSwitcher.tsx
2676
2723
  import { usePersistedState as usePersistedState2 } from "@hook-sdk/sdk";
2677
- import { jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
2724
+ import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
2678
2725
  function LanguageSwitcher({ id, className, label = "Language" }) {
2679
2726
  const config = useAppConfig();
2680
2727
  const i18nConfig = config.i18n;
@@ -2683,43 +2730,43 @@ function LanguageSwitcher({ id, className, label = "Language" }) {
2683
2730
  i18nConfig?.defaultLocale ?? "en-US"
2684
2731
  );
2685
2732
  if (!i18nConfig) return null;
2686
- return /* @__PURE__ */ jsxs16("label", { className, children: [
2687
- label ? /* @__PURE__ */ jsx24("span", { children: label }) : null,
2688
- /* @__PURE__ */ jsx24(
2733
+ return /* @__PURE__ */ jsxs17("label", { className, children: [
2734
+ label ? /* @__PURE__ */ jsx25("span", { children: label }) : null,
2735
+ /* @__PURE__ */ jsx25(
2689
2736
  "select",
2690
2737
  {
2691
2738
  id,
2692
2739
  value: userLocale,
2693
2740
  onChange: (e) => setUserLocale(e.target.value),
2694
2741
  "data-testid": "language-switcher",
2695
- children: i18nConfig.supportedLocales.map((loc) => /* @__PURE__ */ jsx24("option", { value: loc, children: loc }, loc))
2742
+ children: i18nConfig.supportedLocales.map((loc) => /* @__PURE__ */ jsx25("option", { value: loc, children: loc }, loc))
2696
2743
  }
2697
2744
  )
2698
2745
  ] });
2699
2746
  }
2700
2747
 
2701
2748
  // src/defaults/LoadingState.tsx
2702
- import { jsx as jsx25 } from "react/jsx-runtime";
2749
+ import { jsx as jsx26 } from "react/jsx-runtime";
2703
2750
  function LoadingState({ message }) {
2704
- return /* @__PURE__ */ jsx25("div", { role: "status", "aria-live": "polite", style: { padding: 24, textAlign: "center" }, children: /* @__PURE__ */ jsx25("span", { children: message ?? "Carregando..." }) });
2751
+ return /* @__PURE__ */ jsx26("div", { role: "status", "aria-live": "polite", style: { padding: 24, textAlign: "center" }, children: /* @__PURE__ */ jsx26("span", { children: message ?? "Carregando..." }) });
2705
2752
  }
2706
2753
 
2707
2754
  // src/defaults/EmptyState.tsx
2708
- import { jsx as jsx26, jsxs as jsxs17 } from "react/jsx-runtime";
2755
+ import { jsx as jsx27, jsxs as jsxs18 } from "react/jsx-runtime";
2709
2756
  function EmptyState({ title, description, action }) {
2710
- return /* @__PURE__ */ jsxs17("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
2711
- /* @__PURE__ */ jsx26("h2", { style: { marginBottom: 8 }, children: title }),
2712
- description && /* @__PURE__ */ jsx26("p", { style: { opacity: 0.7 }, children: description }),
2713
- action && /* @__PURE__ */ jsx26("div", { style: { marginTop: 16 }, children: action })
2757
+ return /* @__PURE__ */ jsxs18("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
2758
+ /* @__PURE__ */ jsx27("h2", { style: { marginBottom: 8 }, children: title }),
2759
+ description && /* @__PURE__ */ jsx27("p", { style: { opacity: 0.7 }, children: description }),
2760
+ action && /* @__PURE__ */ jsx27("div", { style: { marginTop: 16 }, children: action })
2714
2761
  ] });
2715
2762
  }
2716
2763
 
2717
2764
  // src/hooks/useLoginForm.ts
2718
- import { useCallback as useCallback6, useMemo as useMemo4, useState as useState8 } from "react";
2719
- import { useHook as useHook9 } from "@hook-sdk/sdk";
2765
+ import { useCallback as useCallback6, useMemo as useMemo4, useState as useState9 } from "react";
2766
+ import { useHook as useHook10 } from "@hook-sdk/sdk";
2720
2767
 
2721
2768
  // src/errors.ts
2722
- import { SdkError, SdkAuthError, SdkRateLimitError } from "@hook-sdk/sdk";
2769
+ import { SdkError, SdkAuthError, SdkRateLimitError, SdkValidationError } from "@hook-sdk/sdk";
2723
2770
  function mapSdkError(err) {
2724
2771
  if (err instanceof SdkRateLimitError) {
2725
2772
  return {
@@ -2738,6 +2785,9 @@ function mapSdkError(err) {
2738
2785
  }
2739
2786
  return { code: "invalid_credentials", message: "E-mail ou senha inv\xE1lidos." };
2740
2787
  }
2788
+ if (err instanceof SdkValidationError && err.code === "auth.email_taken") {
2789
+ return { code: "email_taken", message: "Esse e-mail j\xE1 tem conta." };
2790
+ }
2741
2791
  if (err instanceof SdkError && err.httpStatus === 0) {
2742
2792
  return { code: "network", message: "Sem conex\xE3o com o servidor. Verifique sua internet." };
2743
2793
  }
@@ -2751,14 +2801,14 @@ function mapSdkError(err) {
2751
2801
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2752
2802
  var MIN_PASSWORD = 8;
2753
2803
  function useLoginForm() {
2754
- const { auth } = useHook9();
2755
- const [email, setEmail] = useState8("");
2756
- const [password, setPassword] = useState8("");
2757
- const [submitting, setSubmitting] = useState8(false);
2758
- const [error, setError] = useState8(null);
2759
- const [touchedEmail, setTouchedEmail] = useState8(false);
2760
- const [touchedPassword, setTouchedPassword] = useState8(false);
2761
- const [formSubmitAttempted, setFormSubmitAttempted] = useState8(false);
2804
+ const { auth } = useHook10();
2805
+ const [email, setEmail] = useState9("");
2806
+ const [password, setPassword] = useState9("");
2807
+ const [submitting, setSubmitting] = useState9(false);
2808
+ const [error, setError] = useState9(null);
2809
+ const [touchedEmail, setTouchedEmail] = useState9(false);
2810
+ const [touchedPassword, setTouchedPassword] = useState9(false);
2811
+ const [formSubmitAttempted, setFormSubmitAttempted] = useState9(false);
2762
2812
  const validateEmail = useMemo4(() => {
2763
2813
  if (email.length === 0) return null;
2764
2814
  if (!EMAIL_RE.test(email)) return "Formato de e-mail inv\xE1lido.";
@@ -2806,21 +2856,21 @@ function useLoginForm() {
2806
2856
  }
2807
2857
 
2808
2858
  // src/hooks/useSignupForm.ts
2809
- import { useCallback as useCallback7, useMemo as useMemo5, useState as useState9 } from "react";
2810
- import { useHook as useHook10 } from "@hook-sdk/sdk";
2859
+ import { useCallback as useCallback7, useMemo as useMemo5, useState as useState10 } from "react";
2860
+ import { useHook as useHook11 } from "@hook-sdk/sdk";
2811
2861
  var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2812
2862
  var MIN_PASSWORD2 = 8;
2813
2863
  function useSignupForm() {
2814
- const { auth } = useHook10();
2815
- const [name, setName] = useState9("");
2816
- const [email, setEmail] = useState9("");
2817
- const [password, setPassword] = useState9("");
2818
- const [submitting, setSubmitting] = useState9(false);
2819
- const [error, setError] = useState9(null);
2820
- const [touchedName, setTouchedName] = useState9(false);
2821
- const [touchedEmail, setTouchedEmail] = useState9(false);
2822
- const [touchedPassword, setTouchedPassword] = useState9(false);
2823
- const [formSubmitAttempted, setFormSubmitAttempted] = useState9(false);
2864
+ const { auth } = useHook11();
2865
+ const [name, setName] = useState10("");
2866
+ const [email, setEmail] = useState10("");
2867
+ const [password, setPassword] = useState10("");
2868
+ const [submitting, setSubmitting] = useState10(false);
2869
+ const [error, setError] = useState10(null);
2870
+ const [touchedName, setTouchedName] = useState10(false);
2871
+ const [touchedEmail, setTouchedEmail] = useState10(false);
2872
+ const [touchedPassword, setTouchedPassword] = useState10(false);
2873
+ const [formSubmitAttempted, setFormSubmitAttempted] = useState10(false);
2824
2874
  const validateName = useMemo5(() => {
2825
2875
  if (name.length === 0) return null;
2826
2876
  if (name.trim().length < 2) return "Nome muito curto.";
@@ -2878,17 +2928,17 @@ function useSignupForm() {
2878
2928
  }
2879
2929
 
2880
2930
  // src/hooks/useForgotForm.ts
2881
- import { useCallback as useCallback8, useMemo as useMemo6, useState as useState10 } from "react";
2882
- import { useHook as useHook11 } from "@hook-sdk/sdk";
2931
+ import { useCallback as useCallback8, useMemo as useMemo6, useState as useState11 } from "react";
2932
+ import { useHook as useHook12 } from "@hook-sdk/sdk";
2883
2933
  var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2884
2934
  function useForgotForm() {
2885
- const { auth } = useHook11();
2886
- const [email, setEmail] = useState10("");
2887
- const [submitting, setSubmitting] = useState10(false);
2888
- const [sent, setSent] = useState10(false);
2889
- const [error, setError] = useState10(null);
2890
- const [touchedEmail, setTouchedEmail] = useState10(false);
2891
- const [formSubmitAttempted, setFormSubmitAttempted] = useState10(false);
2935
+ const { auth } = useHook12();
2936
+ const [email, setEmail] = useState11("");
2937
+ const [submitting, setSubmitting] = useState11(false);
2938
+ const [sent, setSent] = useState11(false);
2939
+ const [error, setError] = useState11(null);
2940
+ const [touchedEmail, setTouchedEmail] = useState11(false);
2941
+ const [formSubmitAttempted, setFormSubmitAttempted] = useState11(false);
2892
2942
  const validateEmail = useMemo6(() => {
2893
2943
  if (email.length === 0) return null;
2894
2944
  if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
@@ -2927,20 +2977,20 @@ function useForgotForm() {
2927
2977
  }
2928
2978
 
2929
2979
  // src/hooks/useResetForm.ts
2930
- import { useCallback as useCallback9, useEffect as useEffect10, useMemo as useMemo7, useState as useState11 } from "react";
2931
- import { useHook as useHook12 } from "@hook-sdk/sdk";
2980
+ import { useCallback as useCallback9, useEffect as useEffect10, useMemo as useMemo7, useState as useState12 } from "react";
2981
+ import { useHook as useHook13 } from "@hook-sdk/sdk";
2932
2982
  var MIN_PASSWORD3 = 12;
2933
2983
  function useResetForm() {
2934
- const { auth } = useHook12();
2935
- const [token, setToken] = useState11(null);
2936
- const [password, setPassword] = useState11("");
2937
- const [confirm, setConfirm] = useState11("");
2938
- const [submitting, setSubmitting] = useState11(false);
2939
- const [done, setDone] = useState11(false);
2940
- const [error, setError] = useState11(null);
2941
- const [touchedPassword, setTouchedPassword] = useState11(false);
2942
- const [touchedConfirm, setTouchedConfirm] = useState11(false);
2943
- const [formSubmitAttempted, setFormSubmitAttempted] = useState11(false);
2984
+ const { auth } = useHook13();
2985
+ const [token, setToken] = useState12(null);
2986
+ const [password, setPassword] = useState12("");
2987
+ const [confirm, setConfirm] = useState12("");
2988
+ const [submitting, setSubmitting] = useState12(false);
2989
+ const [done, setDone] = useState12(false);
2990
+ const [error, setError] = useState12(null);
2991
+ const [touchedPassword, setTouchedPassword] = useState12(false);
2992
+ const [touchedConfirm, setTouchedConfirm] = useState12(false);
2993
+ const [formSubmitAttempted, setFormSubmitAttempted] = useState12(false);
2944
2994
  useEffect10(() => {
2945
2995
  if (typeof window === "undefined") return;
2946
2996
  const params = new URLSearchParams(window.location.search);
@@ -3000,9 +3050,9 @@ function useResetForm() {
3000
3050
  }
3001
3051
 
3002
3052
  // src/hooks/usePlan.ts
3003
- import { useHook as useHook13 } from "@hook-sdk/sdk";
3053
+ import { useHook as useHook14 } from "@hook-sdk/sdk";
3004
3054
  function usePlan() {
3005
- const { plan } = useHook13();
3055
+ const { plan } = useHook14();
3006
3056
  return plan;
3007
3057
  }
3008
3058
 
@@ -3036,10 +3086,10 @@ function discountPercent(anchorCents, realCents) {
3036
3086
 
3037
3087
  // src/hooks/useAuthPrimitives.ts
3038
3088
  import { useEffect as useEffect11 } from "react";
3039
- import { useHook as useHook14 } from "@hook-sdk/sdk";
3089
+ import { useHook as useHook15 } from "@hook-sdk/sdk";
3040
3090
  var warned = false;
3041
3091
  function useAuthPrimitives() {
3042
- const { auth } = useHook14();
3092
+ const { auth } = useHook15();
3043
3093
  useEffect11(() => {
3044
3094
  if (!warned && process.env.NODE_ENV !== "production") {
3045
3095
  warned = true;
@@ -3062,9 +3112,9 @@ function useAuthPrimitives() {
3062
3112
  }
3063
3113
 
3064
3114
  // src/hooks/useAuth.ts
3065
- import { useHook as useHook15 } from "@hook-sdk/sdk";
3115
+ import { useHook as useHook16 } from "@hook-sdk/sdk";
3066
3116
  function useAuth() {
3067
- const { user, authStatus, auth } = useHook15();
3117
+ const { user, authStatus, auth } = useHook16();
3068
3118
  return {
3069
3119
  user,
3070
3120
  authStatus,
@@ -3076,22 +3126,22 @@ function useAuth() {
3076
3126
  import { useTrackOnboardingStep } from "@hook-sdk/sdk";
3077
3127
 
3078
3128
  // src/hooks/useSubscription.ts
3079
- import { useHook as useHook16 } from "@hook-sdk/sdk";
3129
+ import { useHook as useHook17 } from "@hook-sdk/sdk";
3080
3130
  function useSubscription() {
3081
- const { subscription } = useHook16();
3131
+ const { subscription } = useHook17();
3082
3132
  return {
3083
3133
  status: subscription.status()
3084
3134
  };
3085
3135
  }
3086
3136
 
3087
3137
  // src/hooks/useReminders.ts
3088
- import { useCallback as useCallback10, useEffect as useEffect12, useState as useState12 } from "react";
3089
- import { useHook as useHook17 } from "@hook-sdk/sdk";
3138
+ import { useCallback as useCallback10, useEffect as useEffect12, useState as useState13 } from "react";
3139
+ import { useHook as useHook18 } from "@hook-sdk/sdk";
3090
3140
  function useReminders() {
3091
- const { push } = useHook17();
3141
+ const { push } = useHook18();
3092
3142
  const r = push.reminders;
3093
- const [reminders, setReminders] = useState12([]);
3094
- const [loading, setLoading] = useState12(true);
3143
+ const [reminders, setReminders] = useState13([]);
3144
+ const [loading, setLoading] = useState13(true);
3095
3145
  const reload = useCallback10(async () => {
3096
3146
  setLoading(true);
3097
3147
  try {
@@ -3122,9 +3172,9 @@ function useReminders() {
3122
3172
  }
3123
3173
 
3124
3174
  // src/hooks/useToast.ts
3125
- import { useCallback as useCallback11, useState as useState13 } from "react";
3175
+ import { useCallback as useCallback11, useState as useState14 } from "react";
3126
3176
  function useToast() {
3127
- const [items, setItems] = useState13([]);
3177
+ const [items, setItems] = useState14([]);
3128
3178
  const show = useCallback11((message, kind = "info") => {
3129
3179
  const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
3130
3180
  setItems((prev) => [...prev, { id, message, kind }]);
@@ -3140,20 +3190,20 @@ function useToast() {
3140
3190
 
3141
3191
  // src/RouteBoundary.tsx
3142
3192
  import { Routes as Routes2, Route as Route2 } from "react-router-dom";
3143
- import { jsx as jsx27, jsxs as jsxs18 } from "react/jsx-runtime";
3193
+ import { jsx as jsx28, jsxs as jsxs19 } from "react/jsx-runtime";
3144
3194
  function RouteBoundary({ children }) {
3145
- return /* @__PURE__ */ jsxs18(Routes2, { children: [
3195
+ return /* @__PURE__ */ jsxs19(Routes2, { children: [
3146
3196
  children,
3147
- /* @__PURE__ */ jsx27(Route2, { path: "*", element: /* @__PURE__ */ jsx27(DefaultNotFound, {}) })
3197
+ /* @__PURE__ */ jsx28(Route2, { path: "*", element: /* @__PURE__ */ jsx28(DefaultNotFound, {}) })
3148
3198
  ] });
3149
3199
  }
3150
3200
  function DefaultNotFound() {
3151
- return /* @__PURE__ */ jsx27("div", { role: "alert", children: "P\xE1gina n\xE3o encontrada" });
3201
+ return /* @__PURE__ */ jsx28("div", { role: "alert", children: "P\xE1gina n\xE3o encontrada" });
3152
3202
  }
3153
3203
 
3154
3204
  // src/PreAuthShell.tsx
3155
3205
  import { BrowserRouter as BrowserRouter2, MemoryRouter as MemoryRouter2, Routes as Routes3 } from "react-router-dom";
3156
- import { jsx as jsx28 } from "react/jsx-runtime";
3206
+ import { jsx as jsx29 } from "react/jsx-runtime";
3157
3207
  function PreAuthShell({
3158
3208
  basename,
3159
3209
  testRouter,
@@ -3161,14 +3211,14 @@ function PreAuthShell({
3161
3211
  children
3162
3212
  }) {
3163
3213
  if (testRouter === "memory") {
3164
- return /* @__PURE__ */ jsx28(MemoryRouter2, { basename, initialEntries: testInitialEntries, children: /* @__PURE__ */ jsx28(Routes3, { children }) });
3214
+ return /* @__PURE__ */ jsx29(MemoryRouter2, { basename, initialEntries: testInitialEntries, children: /* @__PURE__ */ jsx29(Routes3, { children }) });
3165
3215
  }
3166
- return /* @__PURE__ */ jsx28(BrowserRouter2, { basename, children: /* @__PURE__ */ jsx28(Routes3, { children }) });
3216
+ return /* @__PURE__ */ jsx29(BrowserRouter2, { basename, children: /* @__PURE__ */ jsx29(Routes3, { children }) });
3167
3217
  }
3168
3218
 
3169
3219
  // src/OnboardingFlow.tsx
3170
3220
  import { useCallback as useCallback12, useEffect as useEffect13, useMemo as useMemo8, useRef as useRef6 } from "react";
3171
- import { usePersistedState as usePersistedState3, useHook as useHook18 } from "@hook-sdk/sdk";
3221
+ import { usePersistedState as usePersistedState3, useHook as useHook19 } from "@hook-sdk/sdk";
3172
3222
 
3173
3223
  // src/hooks/useOnboardingStep.ts
3174
3224
  import { createContext as createContext3, useContext as useContext4 } from "react";
@@ -3184,7 +3234,7 @@ function useOnboardingStep() {
3184
3234
  }
3185
3235
 
3186
3236
  // src/OnboardingFlow.tsx
3187
- import { jsx as jsx29 } from "react/jsx-runtime";
3237
+ import { jsx as jsx30 } from "react/jsx-runtime";
3188
3238
  var isFilled = (v) => v != null && v !== "";
3189
3239
  var CURRENT_STEP_FIELD = "currentStep";
3190
3240
  function readPersistedStepIdx(draft) {
@@ -3220,7 +3270,7 @@ function OnboardingFlow({
3220
3270
  [setDraft]
3221
3271
  );
3222
3272
  const step = steps[clampedIdx];
3223
- const hookCtx = useHook18();
3273
+ const hookCtx = useHook19();
3224
3274
  const track2 = typeof hookCtx.track === "function" ? hookCtx.track : void 0;
3225
3275
  useEffect13(() => {
3226
3276
  if (status.loading) return;
@@ -3274,7 +3324,7 @@ function OnboardingFlow({
3274
3324
  `[hook-template] OnboardingFlow: missing screen component for step '${step.id}' (expected key '${step.screen}' in screens prop)`
3275
3325
  );
3276
3326
  }
3277
- return /* @__PURE__ */ jsx29(OnboardingStepContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx29(Screen, {}) });
3327
+ return /* @__PURE__ */ jsx30(OnboardingStepContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx30(Screen, {}) });
3278
3328
  }
3279
3329
 
3280
3330
  // src/hooks/useFeature.ts
@@ -3285,24 +3335,41 @@ function useFeature(name) {
3285
3335
 
3286
3336
  // src/components/paywall/Paywall.tsx
3287
3337
  import { useEffect as useEffect14, useMemo as useMemo9 } from "react";
3288
- import { useHook as useHook19 } from "@hook-sdk/sdk";
3338
+ import { useHook as useHook20 } from "@hook-sdk/sdk";
3339
+
3340
+ // src/components/paywall/PaywallProvider.tsx
3341
+ import { createContext as createContext4 } from "react";
3342
+ import { jsx as jsx31 } from "react/jsx-runtime";
3343
+ var PaywallContext = createContext4(null);
3344
+ function PaywallProvider({ children }) {
3345
+ const state = usePaywallState();
3346
+ return /* @__PURE__ */ jsx31(PaywallContext.Provider, { value: state, children });
3347
+ }
3348
+
3349
+ // src/components/paywall/usePaywallContext.ts
3350
+ import { useContext as useContext5 } from "react";
3351
+ function usePaywallContext() {
3352
+ const ctx = useContext5(PaywallContext);
3353
+ if (!ctx) {
3354
+ throw new Error("usePaywallContext must be used within <PaywallProvider>");
3355
+ }
3356
+ return ctx;
3357
+ }
3289
3358
 
3290
3359
  // src/components/paywall/PaywallMethodTabs.tsx
3291
- import { jsx as jsx30 } from "react/jsx-runtime";
3360
+ import { jsx as jsx32 } from "react/jsx-runtime";
3292
3361
  function PaywallMethodTabs({
3293
- methods,
3294
- selected,
3295
- onSelect,
3296
3362
  labels,
3297
3363
  className,
3298
3364
  tabClassName,
3299
3365
  tabActiveClassName
3300
3366
  }) {
3367
+ const { methods, selectedMethod, selectMethod } = usePaywallContext();
3301
3368
  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;
3369
+ return /* @__PURE__ */ jsx32("div", { role: "tablist", "aria-label": "M\xE9todo de pagamento", className, children: methods.map((m) => {
3370
+ const active = m === selectedMethod;
3304
3371
  const label = labels[m] ?? m;
3305
- return /* @__PURE__ */ jsx30(
3372
+ return /* @__PURE__ */ jsx32(
3306
3373
  "button",
3307
3374
  {
3308
3375
  type: "button",
@@ -3310,7 +3377,7 @@ function PaywallMethodTabs({
3310
3377
  "aria-selected": active,
3311
3378
  "aria-controls": `paywall-tab-${m}`,
3312
3379
  tabIndex: active ? 0 : -1,
3313
- onClick: () => onSelect(m),
3380
+ onClick: () => selectMethod(m),
3314
3381
  className: [tabClassName, active ? tabActiveClassName : ""].filter(Boolean).join(" "),
3315
3382
  children: label
3316
3383
  },
@@ -3320,36 +3387,51 @@ function PaywallMethodTabs({
3320
3387
  }
3321
3388
 
3322
3389
  // 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)) });
3390
+ import { jsx as jsx33 } from "react/jsx-runtime";
3391
+ function PaywallMethodContent({ copy, className, rowClassName }) {
3392
+ const { selectedMethod, hasConsumedTrial } = usePaywallContext();
3393
+ const useCardConsumed = selectedMethod === "card" && hasConsumedTrial && copy.cardConsumedTrial;
3394
+ const rows = useCardConsumed ? copy.cardConsumedTrial.bodyRows : selectedMethod === "pix-auto" || selectedMethod === "pix-once" ? copy.pix.bodyRows : copy.card.bodyRows;
3395
+ return /* @__PURE__ */ jsx33("div", { role: "tabpanel", id: `paywall-tab-${selectedMethod}`, className, children: rows.map((row, i) => /* @__PURE__ */ jsx33("div", { className: rowClassName, children: row }, i)) });
3334
3396
  }
3335
3397
 
3336
3398
  // src/components/paywall/PaywallCyclePicker.tsx
3337
- import { jsx as jsx32, jsxs as jsxs19 } from "react/jsx-runtime";
3399
+ import { jsx as jsx34, jsxs as jsxs20 } from "react/jsx-runtime";
3400
+ var VARIANT_CLASSES = {
3401
+ default: { card: "", cardSelected: "" },
3402
+ "premium-gold": {
3403
+ card: "",
3404
+ cardSelected: "border-2 border-yellow-400/80 ring-2 ring-yellow-400/20"
3405
+ },
3406
+ "pink-pill": {
3407
+ card: "rounded-2xl",
3408
+ cardSelected: "border-2 border-pink-500"
3409
+ }
3410
+ };
3338
3411
  function PaywallCyclePicker({
3339
- cycles,
3340
- selected,
3341
- onSelect,
3342
- priceCentsByCycle,
3343
- anchorCentsByCycle,
3344
- monthlyEquivByCycle,
3345
3412
  labels,
3346
3413
  className,
3347
3414
  cardClassName,
3348
3415
  cardSelectedClassName,
3349
- anchorClassName
3416
+ anchorClassName,
3417
+ variant = "default",
3418
+ render
3350
3419
  }) {
3420
+ const ctx = usePaywallContext();
3421
+ const { cycle: selected, setCycle, plan, anchorPriceCents } = ctx;
3422
+ const cycles = ["MONTHLY", "YEARLY"];
3423
+ if (render) {
3424
+ return /* @__PURE__ */ jsx34("div", { className, children: render({ cycles, selected, setCycle, plan, anchorPriceCents }) });
3425
+ }
3351
3426
  if (cycles.length < 2) return null;
3352
- return /* @__PURE__ */ jsx32(
3427
+ const v = VARIANT_CLASSES[variant];
3428
+ const composedCardClassName = [v.card, cardClassName].filter(Boolean).join(" ");
3429
+ const composedCardSelectedClassName = [v.cardSelected, cardSelectedClassName].filter(Boolean).join(" ");
3430
+ const monthlyCents = plan?.monthlyCents ?? 0;
3431
+ const yearlyCents = plan?.yearlyCents ?? 0;
3432
+ const anchorMonthly = plan?.anchorMonthlyCents ?? null;
3433
+ const anchorYearly = plan?.anchorYearlyCents ?? null;
3434
+ return /* @__PURE__ */ jsx34(
3353
3435
  "div",
3354
3436
  {
3355
3437
  role: "radiogroup",
@@ -3359,21 +3441,25 @@ function PaywallCyclePicker({
3359
3441
  const active = c === selected;
3360
3442
  const label = c === "YEARLY" ? labels.annualLabel : labels.monthlyLabel;
3361
3443
  const suffix = c === "YEARLY" ? labels.annualSuffix : labels.monthlySuffix;
3362
- const mainCents = c === "YEARLY" ? monthlyEquivByCycle[c] : priceCentsByCycle[c];
3363
- const anchorCents = anchorCentsByCycle[c];
3364
- return /* @__PURE__ */ jsxs19(
3444
+ const mainCents = c === "YEARLY" ? Math.round(yearlyCents / 12) : monthlyCents;
3445
+ const anchorCents = c === "YEARLY" ? anchorYearly : anchorMonthly;
3446
+ return /* @__PURE__ */ jsxs20(
3365
3447
  "button",
3366
3448
  {
3367
3449
  type: "button",
3368
3450
  role: "radio",
3369
3451
  "aria-checked": active,
3370
- onClick: () => onSelect(c),
3371
- className: ["flex flex-col items-center gap-0.5", cardClassName, active ? cardSelectedClassName : ""].filter(Boolean).join(" "),
3452
+ onClick: () => setCycle(c),
3453
+ className: [
3454
+ "flex flex-col items-center gap-0.5",
3455
+ composedCardClassName,
3456
+ active ? composedCardSelectedClassName : ""
3457
+ ].filter(Boolean).join(" "),
3372
3458
  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
3459
+ /* @__PURE__ */ jsx34("span", { className: "font-bold text-base leading-tight", children: formatBRL(mainCents) }),
3460
+ /* @__PURE__ */ jsx34("span", { className: "text-xs opacity-70 leading-tight", children: suffix }),
3461
+ /* @__PURE__ */ jsx34("span", { className: "text-xs opacity-60 leading-tight", children: label }),
3462
+ anchorCents != null && anchorCents > mainCents ? /* @__PURE__ */ jsx34("span", { className: anchorClassName ?? "text-xs opacity-50", children: /* @__PURE__ */ jsx34("s", { children: formatBRL(anchorCents) }) }) : null
3377
3463
  ]
3378
3464
  },
3379
3465
  c
@@ -3383,40 +3469,8 @@ function PaywallCyclePicker({
3383
3469
  );
3384
3470
  }
3385
3471
 
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
3472
  // src/components/paywall/Paywall.tsx
3419
- import { jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
3473
+ import { jsx as jsx35, jsxs as jsxs21 } from "react/jsx-runtime";
3420
3474
  var NBSP = "\xA0";
3421
3475
  function Paywall({
3422
3476
  copy,
@@ -3424,21 +3478,42 @@ function Paywall({
3424
3478
  slots = {},
3425
3479
  onBeforeCheckout
3426
3480
  }) {
3427
- const { track: track2 } = useHook19();
3428
- const s = usePaywallState();
3481
+ return /* @__PURE__ */ jsx35(PaywallProvider, { children: /* @__PURE__ */ jsx35(
3482
+ PaywallInner,
3483
+ {
3484
+ copy,
3485
+ themeClasses,
3486
+ slots,
3487
+ onBeforeCheckout
3488
+ }
3489
+ ) });
3490
+ }
3491
+ function PaywallInner({
3492
+ copy,
3493
+ themeClasses = {},
3494
+ slots = {},
3495
+ onBeforeCheckout
3496
+ }) {
3497
+ const { track: track2 } = useHook20();
3498
+ const s = usePaywallContext();
3429
3499
  const priceLabel = formatBRL(s.currentPriceCents).replace(new RegExp(NBSP, "g"), " ");
3430
- const monthlyEquivLabel = formatBRL(s.currentMonthlyEquivCents).replace(new RegExp(NBSP, "g"), " ");
3431
3500
  const trialDaysCardLabel = String(s.trialDaysCard);
3432
3501
  const ctaLabel = useMemo9(() => {
3433
3502
  if (s.isFree) return copy.freeCta ?? "Come\xE7ar agora";
3434
3503
  if (s.selectedMethod === "card") {
3435
3504
  if (s.hasConsumedTrial && copy.cardConsumedTrial) {
3436
- return interp(copy.cardConsumedTrial.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
3505
+ return interp(copy.cardConsumedTrial.ctaTemplate, {
3506
+ price: priceLabel,
3507
+ days: trialDaysCardLabel
3508
+ });
3437
3509
  }
3438
3510
  if (s.trialDaysCard > 0) {
3439
3511
  return interp(copy.card.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
3440
3512
  }
3441
- return copy.cardConsumedTrial ? interp(copy.cardConsumedTrial.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel }) : `Assinar por ${priceLabel}`;
3513
+ return copy.cardConsumedTrial ? interp(copy.cardConsumedTrial.ctaTemplate, {
3514
+ price: priceLabel,
3515
+ days: trialDaysCardLabel
3516
+ }) : `Assinar por ${priceLabel}`;
3442
3517
  }
3443
3518
  return interp(copy.pix.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
3444
3519
  }, [
@@ -3478,52 +3553,33 @@ function Paywall({
3478
3553
  const ctaTheme = s.selectedMethod === "card" ? themeClasses.ctaCard : themeClasses.ctaPix;
3479
3554
  return /* @__PURE__ */ jsxs21("div", { className: themeClasses.container, children: [
3480
3555
  slots.heroSlot,
3481
- /* @__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: [
3556
+ /* @__PURE__ */ jsx35("h1", { className: themeClasses.headline, children: copy.headline }),
3557
+ /* @__PURE__ */ jsx35("ul", { children: copy.features.map((f) => /* @__PURE__ */ jsxs21("li", { className: themeClasses.feature, children: [
3483
3558
  "\u2713 ",
3484
- /* @__PURE__ */ jsx34("span", { children: f })
3559
+ /* @__PURE__ */ jsx35("span", { children: f })
3485
3560
  ] }, f)) }),
3486
- copy.socialProof ? /* @__PURE__ */ jsx34("p", { className: themeClasses.socialProof, children: copy.socialProof }) : null,
3487
- slots.cyclePickerSlot ?? /* @__PURE__ */ jsx34(
3561
+ copy.socialProof ? /* @__PURE__ */ jsx35("p", { className: themeClasses.socialProof, children: copy.socialProof }) : null,
3562
+ slots.cyclePickerSlot ?? /* @__PURE__ */ jsx35(
3488
3563
  PaywallCyclePicker,
3489
3564
  {
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
3565
  labels: copy.cycle,
3506
3566
  cardClassName: themeClasses.cycleCard,
3507
3567
  cardSelectedClassName: themeClasses.cycleCardSelected,
3508
3568
  anchorClassName: themeClasses.anchorPrice
3509
3569
  }
3510
3570
  ),
3511
- /* @__PURE__ */ jsx34(
3571
+ /* @__PURE__ */ jsx35(
3512
3572
  PaywallMethodTabs,
3513
3573
  {
3514
- methods: s.methods,
3515
- selected: s.selectedMethod,
3516
- onSelect: s.selectMethod,
3517
3574
  labels: { "pix-auto": copy.pix.tabLabel, card: copy.card.tabLabel },
3518
3575
  className: themeClasses.tabs,
3519
3576
  tabClassName: themeClasses.tab,
3520
3577
  tabActiveClassName: themeClasses.tabActive
3521
3578
  }
3522
3579
  ),
3523
- /* @__PURE__ */ jsx34(
3580
+ /* @__PURE__ */ jsx35(
3524
3581
  PaywallMethodContent,
3525
3582
  {
3526
- method: s.selectedMethod,
3527
3583
  copy: {
3528
3584
  pix: interpolateCopy(copy.pix, priceLabel, trialDaysCardLabel),
3529
3585
  card: interpolateCopy(copy.card, priceLabel, trialDaysCardLabel),
@@ -3534,27 +3590,27 @@ function Paywall({
3534
3590
  ctaTemplate: copy.cardConsumedTrial.ctaTemplate
3535
3591
  } : void 0
3536
3592
  },
3537
- hasConsumedTrial: s.hasConsumedTrial,
3538
3593
  className: themeClasses.tabContent,
3539
3594
  rowClassName: themeClasses.tabContentRow
3540
3595
  }
3541
3596
  ),
3542
3597
  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
- )
3598
+ /* @__PURE__ */ jsxs21("div", { children: [
3599
+ /* @__PURE__ */ jsx35(
3600
+ "button",
3601
+ {
3602
+ type: "button",
3603
+ onClick: () => {
3604
+ void handleCta();
3605
+ },
3606
+ disabled: s.submitting,
3607
+ className: ctaTheme,
3608
+ children: s.submitting ? "Abrindo checkout\u2026" : ctaLabel
3609
+ }
3610
+ ),
3611
+ switchHint ? /* @__PURE__ */ jsx35("p", { className: themeClasses.switchHint, children: switchHint }) : null,
3612
+ /* @__PURE__ */ jsx35("p", { className: themeClasses.trustLine, children: copy.trustLine })
3613
+ ] })
3558
3614
  ] });
3559
3615
  }
3560
3616
  function interp(tpl, vars) {
@@ -3568,13 +3624,438 @@ function interpolateCopy(m, price, days) {
3568
3624
  switchHint: m.switchHint
3569
3625
  };
3570
3626
  }
3571
- function priceCentsForCycle(s, c) {
3572
- return s.plan ? c === "YEARLY" ? s.plan.yearlyCents ?? 0 : s.plan.monthlyCents : 0;
3627
+
3628
+ // src/components/paywall/PaywallCta.tsx
3629
+ import { jsx as jsx36, jsxs as jsxs22 } from "react/jsx-runtime";
3630
+ function PaywallCta({
3631
+ ctaLabel,
3632
+ loadingLabel,
3633
+ switchHint,
3634
+ trustLine,
3635
+ className,
3636
+ buttonClassName,
3637
+ switchHintClassName,
3638
+ trustClassName
3639
+ }) {
3640
+ const { submit, submitting } = usePaywallContext();
3641
+ const label = submitting && loadingLabel ? loadingLabel : ctaLabel;
3642
+ return /* @__PURE__ */ jsxs22("div", { className, children: [
3643
+ /* @__PURE__ */ jsx36(
3644
+ "button",
3645
+ {
3646
+ type: "button",
3647
+ onClick: () => {
3648
+ void submit();
3649
+ },
3650
+ disabled: submitting,
3651
+ className: buttonClassName,
3652
+ children: label
3653
+ }
3654
+ ),
3655
+ switchHint ? /* @__PURE__ */ jsx36("p", { className: switchHintClassName, children: switchHint }) : null,
3656
+ /* @__PURE__ */ jsx36("p", { className: trustClassName, children: trustLine })
3657
+ ] });
3658
+ }
3659
+
3660
+ // src/components/paywall/blocks/PaywallEyebrow.tsx
3661
+ import { jsx as jsx37 } from "react/jsx-runtime";
3662
+ var DEFAULT_EYEBROW_CLASSES = "text-xs uppercase tracking-widest font-semibold opacity-70";
3663
+ function PaywallEyebrow({ text, className }) {
3664
+ return /* @__PURE__ */ jsx37("div", { className: [DEFAULT_EYEBROW_CLASSES, className].filter(Boolean).join(" "), children: text });
3665
+ }
3666
+
3667
+ // src/components/paywall/blocks/PaywallHero.tsx
3668
+ import { jsx as jsx38, jsxs as jsxs23 } from "react/jsx-runtime";
3669
+ var DEFAULT_GRADIENT = "absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent";
3670
+ function PaywallHero({
3671
+ src,
3672
+ alt = "",
3673
+ headline,
3674
+ aspectRatio = "16/9",
3675
+ gradientClassName,
3676
+ className,
3677
+ headlineClassName,
3678
+ imgClassName,
3679
+ render
3680
+ }) {
3681
+ if (render) {
3682
+ return /* @__PURE__ */ jsx38("div", { className, children: render({ src, headline }) });
3683
+ }
3684
+ return /* @__PURE__ */ jsxs23(
3685
+ "div",
3686
+ {
3687
+ className: ["relative overflow-hidden", className].filter(Boolean).join(" "),
3688
+ style: { aspectRatio },
3689
+ children: [
3690
+ /* @__PURE__ */ jsx38(
3691
+ "img",
3692
+ {
3693
+ src,
3694
+ alt,
3695
+ className: ["absolute inset-0 w-full h-full object-cover", imgClassName].filter(Boolean).join(" ")
3696
+ }
3697
+ ),
3698
+ /* @__PURE__ */ jsx38("div", { className: gradientClassName ?? DEFAULT_GRADIENT, "aria-hidden": "true" }),
3699
+ headline ? /* @__PURE__ */ jsx38(
3700
+ "h1",
3701
+ {
3702
+ className: ["absolute bottom-0 left-0 right-0 p-4 text-white font-bold text-2xl", headlineClassName].filter(Boolean).join(" "),
3703
+ children: headline
3704
+ }
3705
+ ) : null
3706
+ ]
3707
+ }
3708
+ );
3709
+ }
3710
+
3711
+ // src/components/paywall/blocks/PaywallHeadline.tsx
3712
+ import { jsx as jsx39 } from "react/jsx-runtime";
3713
+ var DEFAULT_HEADLINE_CLASSES = "text-2xl font-bold leading-tight";
3714
+ function PaywallHeadline({ text, className, as = "h1" }) {
3715
+ const Tag = as;
3716
+ return /* @__PURE__ */ jsx39(Tag, { className: [DEFAULT_HEADLINE_CLASSES, className].filter(Boolean).join(" "), children: text });
3717
+ }
3718
+
3719
+ // src/components/paywall/blocks/PaywallPriceHeadline.tsx
3720
+ import { jsx as jsx40 } from "react/jsx-runtime";
3721
+ var DEFAULT_CLASS = "text-2xl font-bold leading-tight";
3722
+ var CYCLE_LABEL = {
3723
+ MONTHLY: "mensal",
3724
+ YEARLY: "anual"
3725
+ };
3726
+ function PaywallPriceHeadline({
3727
+ template,
3728
+ className,
3729
+ as = "h1",
3730
+ render
3731
+ }) {
3732
+ const { cycle, currentMonthlyEquivCents, plan } = usePaywallContext();
3733
+ const yearlyCents = plan?.yearlyCents ?? null;
3734
+ const pricePerDay = formatBRL(dailyFromYearly(yearlyCents));
3735
+ const monthlyEquiv = currentMonthlyEquivCents ?? 0;
3736
+ const cycleLabel = CYCLE_LABEL[cycle] ?? cycle.toLowerCase();
3737
+ const rootClasses = [DEFAULT_CLASS, className].filter(Boolean).join(" ");
3738
+ if (render) {
3739
+ const RootTag2 = as;
3740
+ return /* @__PURE__ */ jsx40(RootTag2, { className: [className].filter(Boolean).join(" ") || void 0, children: render({ pricePerDay, currentMonthlyEquivCents: monthlyEquiv, cycle }) });
3741
+ }
3742
+ const text = template.replaceAll("{pricePerDay}", pricePerDay).replaceAll("{currentMonthlyEquiv}", formatBRL(monthlyEquiv)).replaceAll("{cycle}", cycleLabel);
3743
+ const RootTag = as;
3744
+ return /* @__PURE__ */ jsx40(RootTag, { className: rootClasses, children: text });
3573
3745
  }
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;
3746
+
3747
+ // src/components/paywall/blocks/PaywallCountdown.tsx
3748
+ import { useEffect as useEffect15, useRef as useRef7, useState as useState15 } from "react";
3749
+ import { jsx as jsx41 } from "react/jsx-runtime";
3750
+ var DEFAULT_COUNTDOWN_CLASSES = "font-mono tabular-nums";
3751
+ function resolveDeadlineMs(deadline) {
3752
+ if (deadline instanceof Date) return deadline.getTime();
3753
+ if (typeof deadline === "string") return new Date(deadline).getTime();
3754
+ const { sessionStorageKey, durationMs } = deadline;
3755
+ if (typeof window === "undefined" || typeof window.sessionStorage === "undefined") {
3756
+ return Date.now() + durationMs;
3757
+ }
3758
+ const stored = window.sessionStorage.getItem(sessionStorageKey);
3759
+ const parsed = stored ? Number.parseInt(stored, 10) : NaN;
3760
+ const now = Date.now();
3761
+ if (!Number.isFinite(parsed) || parsed < now) {
3762
+ const target = now + durationMs;
3763
+ window.sessionStorage.setItem(sessionStorageKey, String(target));
3764
+ return target;
3765
+ }
3766
+ return parsed;
3767
+ }
3768
+ function computeRemaining(deadlineMs) {
3769
+ const diff = Math.max(0, deadlineMs - Date.now());
3770
+ const totalSeconds = Math.floor(diff / 1e3);
3771
+ const h = Math.floor(totalSeconds / 3600);
3772
+ const m = Math.floor(totalSeconds % 3600 / 60);
3773
+ const s = totalSeconds % 60;
3774
+ return { h, m, s, expired: diff === 0 };
3775
+ }
3776
+ function pad(n) {
3777
+ return String(n).padStart(2, "0");
3778
+ }
3779
+ function PaywallCountdown({
3780
+ deadline,
3781
+ format = "h:m:s",
3782
+ onExpire,
3783
+ className,
3784
+ render
3785
+ }) {
3786
+ const deadlineMsRef = useRef7(null);
3787
+ if (deadlineMsRef.current === null) {
3788
+ deadlineMsRef.current = resolveDeadlineMs(deadline);
3789
+ }
3790
+ const [state, setState] = useState15(() => computeRemaining(deadlineMsRef.current));
3791
+ const expiredCalledRef = useRef7(false);
3792
+ useEffect15(() => {
3793
+ if (state.expired) {
3794
+ if (!expiredCalledRef.current) {
3795
+ expiredCalledRef.current = true;
3796
+ onExpire?.();
3797
+ }
3798
+ return;
3799
+ }
3800
+ const tick = () => {
3801
+ const next = computeRemaining(deadlineMsRef.current);
3802
+ setState(next);
3803
+ if (next.expired && !expiredCalledRef.current) {
3804
+ expiredCalledRef.current = true;
3805
+ onExpire?.();
3806
+ }
3807
+ };
3808
+ const id = setInterval(tick, 1e3);
3809
+ return () => clearInterval(id);
3810
+ }, [state.expired]);
3811
+ if (render) {
3812
+ return /* @__PURE__ */ jsx41("div", { className, children: render(state) });
3813
+ }
3814
+ const formatted = format === "h:m:s" ? `${pad(state.h)}:${pad(state.m)}:${pad(state.s)}` : `${pad(state.h * 60 + state.m)}:${pad(state.s)}`;
3815
+ return /* @__PURE__ */ jsx41("div", { className: [DEFAULT_COUNTDOWN_CLASSES, className].filter(Boolean).join(" "), children: formatted });
3816
+ }
3817
+
3818
+ // src/components/paywall/blocks/PaywallFeatures.tsx
3819
+ import { jsx as jsx42, jsxs as jsxs24 } from "react/jsx-runtime";
3820
+ function PaywallFeatures({
3821
+ items,
3822
+ IconComponent,
3823
+ className,
3824
+ itemClassName,
3825
+ iconClassName,
3826
+ render,
3827
+ renderItem
3828
+ }) {
3829
+ if (render) {
3830
+ return /* @__PURE__ */ jsx42("div", { className, children: render({ items }) });
3831
+ }
3832
+ if (renderItem) {
3833
+ return /* @__PURE__ */ jsx42("ul", { className, children: items.map((item, idx) => /* @__PURE__ */ jsx42("li", { children: renderItem(item, idx) }, idx)) });
3834
+ }
3835
+ return /* @__PURE__ */ jsx42("ul", { className, children: items.map((item, idx) => /* @__PURE__ */ jsxs24("li", { className: itemClassName, children: [
3836
+ IconComponent ? /* @__PURE__ */ jsx42(IconComponent, { className: iconClassName }) : /* @__PURE__ */ jsx42("span", { className: iconClassName, "aria-hidden": "true", children: "\u2713" }),
3837
+ /* @__PURE__ */ jsx42("span", { children: item })
3838
+ ] }, idx)) });
3839
+ }
3840
+
3841
+ // src/components/paywall/blocks/PaywallFeaturesCard.tsx
3842
+ import { jsx as jsx43, jsxs as jsxs25 } from "react/jsx-runtime";
3843
+ var DEFAULT_CARD_CLASSES = "rounded-xl border p-4";
3844
+ function PaywallFeaturesCard({
3845
+ title,
3846
+ items,
3847
+ className,
3848
+ cardClassName,
3849
+ titleClassName,
3850
+ itemClassName,
3851
+ renderItem
3852
+ }) {
3853
+ return /* @__PURE__ */ jsx43("div", { className, children: /* @__PURE__ */ jsxs25("div", { className: [DEFAULT_CARD_CLASSES, cardClassName].filter(Boolean).join(" "), children: [
3854
+ title ? /* @__PURE__ */ jsx43("div", { className: ["font-semibold mb-2", titleClassName].filter(Boolean).join(" "), children: title }) : null,
3855
+ /* @__PURE__ */ jsx43("ul", { children: items.map(
3856
+ (item, idx) => renderItem ? /* @__PURE__ */ jsx43("li", { children: renderItem(item, idx) }, idx) : /* @__PURE__ */ jsxs25("li", { className: itemClassName, children: [
3857
+ /* @__PURE__ */ jsx43("span", { "aria-hidden": "true", children: "\u2022" }),
3858
+ " ",
3859
+ /* @__PURE__ */ jsx43("span", { children: item })
3860
+ ] }, idx)
3861
+ ) })
3862
+ ] }) });
3863
+ }
3864
+
3865
+ // src/components/paywall/blocks/PaywallTrophyBadge.tsx
3866
+ import { jsx as jsx44, jsxs as jsxs26 } from "react/jsx-runtime";
3867
+ 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";
3868
+ var FLOATING_CLASSES = "absolute top-2 right-2 z-10 shadow-md";
3869
+ function PaywallTrophyBadge({
3870
+ text,
3871
+ className,
3872
+ iconClassName,
3873
+ floating = false,
3874
+ render
3875
+ }) {
3876
+ if (render) {
3877
+ return /* @__PURE__ */ jsx44("div", { className, children: render({ text }) });
3878
+ }
3879
+ const rootClasses = [
3880
+ DEFAULT_CHIP_CLASSES,
3881
+ floating ? FLOATING_CLASSES : "",
3882
+ className
3883
+ ].filter(Boolean).join(" ");
3884
+ return /* @__PURE__ */ jsxs26("div", { className: rootClasses, children: [
3885
+ /* @__PURE__ */ jsx44("span", { className: iconClassName, "aria-hidden": "true", children: "\u{1F3C6}" }),
3886
+ /* @__PURE__ */ jsx44("span", { children: text })
3887
+ ] });
3888
+ }
3889
+
3890
+ // src/components/paywall/blocks/PaywallAnchorPrice.tsx
3891
+ import { jsx as jsx45 } from "react/jsx-runtime";
3892
+ var DEFAULT_CLASS2 = "text-sm opacity-60 line-through";
3893
+ function PaywallAnchorPrice({
3894
+ className,
3895
+ render
3896
+ }) {
3897
+ const { anchorPriceCents, cycle } = usePaywallContext();
3898
+ if (anchorPriceCents === null || anchorPriceCents === void 0 || anchorPriceCents <= 0) {
3899
+ return null;
3900
+ }
3901
+ void cycle;
3902
+ const formatted = formatBRL(anchorPriceCents);
3903
+ const rootClasses = [DEFAULT_CLASS2, className].filter(Boolean).join(" ");
3904
+ if (render) {
3905
+ return /* @__PURE__ */ jsx45("span", { className: className || void 0, children: render({ anchorCents: anchorPriceCents, formatted }) });
3906
+ }
3907
+ return /* @__PURE__ */ jsx45("span", { className: rootClasses, children: formatted });
3908
+ }
3909
+
3910
+ // src/components/paywall/blocks/PaywallTestimonials.tsx
3911
+ import { jsx as jsx46, jsxs as jsxs27 } from "react/jsx-runtime";
3912
+ var DEFAULT_ROOT = "flex gap-3 overflow-x-auto snap-x snap-mandatory pb-2";
3913
+ var DEFAULT_CARD = "snap-start shrink-0 w-72 rounded-2xl border p-4 flex flex-col gap-2";
3914
+ var DEFAULT_AVATAR = "w-10 h-10 rounded-full object-cover";
3915
+ var DEFAULT_QUOTE = "text-sm leading-snug";
3916
+ var DEFAULT_NAME = "text-xs font-semibold opacity-80";
3917
+ var DEFAULT_STARS = "text-yellow-500 text-sm";
3918
+ function clampStars(n) {
3919
+ return Math.max(0, Math.min(5, Math.round(n)));
3920
+ }
3921
+ function PaywallTestimonials({
3922
+ items,
3923
+ className,
3924
+ cardClassName,
3925
+ avatarClassName,
3926
+ quoteClassName,
3927
+ nameClassName,
3928
+ starsClassName,
3929
+ renderItem
3930
+ }) {
3931
+ const rootClasses = [DEFAULT_ROOT, className].filter(Boolean).join(" ");
3932
+ const cardClasses = [DEFAULT_CARD, cardClassName].filter(Boolean).join(" ");
3933
+ const avatarClasses = [DEFAULT_AVATAR, avatarClassName].filter(Boolean).join(" ");
3934
+ const quoteClasses = [DEFAULT_QUOTE, quoteClassName].filter(Boolean).join(" ");
3935
+ const nameClasses = [DEFAULT_NAME, nameClassName].filter(Boolean).join(" ");
3936
+ const starsClasses = [DEFAULT_STARS, starsClassName].filter(Boolean).join(" ");
3937
+ return /* @__PURE__ */ jsx46("div", { className: rootClasses, children: items.map((item, idx) => {
3938
+ if (renderItem) return renderItem(item, idx);
3939
+ const filled = clampStars(item.stars);
3940
+ const empty = 5 - filled;
3941
+ return /* @__PURE__ */ jsxs27("div", { className: cardClasses, children: [
3942
+ /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-2", children: [
3943
+ item.avatar ? /* @__PURE__ */ jsx46(
3944
+ "img",
3945
+ {
3946
+ src: item.avatar,
3947
+ alt: "",
3948
+ loading: "lazy",
3949
+ className: avatarClasses,
3950
+ "aria-hidden": "true"
3951
+ }
3952
+ ) : null,
3953
+ /* @__PURE__ */ jsx46("div", { className: nameClasses, children: item.name })
3954
+ ] }),
3955
+ /* @__PURE__ */ jsxs27("div", { className: starsClasses, "aria-label": `${filled} de 5 estrelas`, children: [
3956
+ "\u2605".repeat(filled),
3957
+ "\u2606".repeat(empty)
3958
+ ] }),
3959
+ /* @__PURE__ */ jsx46("p", { className: quoteClasses, children: item.quote })
3960
+ ] }, idx);
3961
+ }) });
3962
+ }
3963
+
3964
+ // src/components/paywall/blocks/PaywallStatsRow.tsx
3965
+ import { jsx as jsx47, jsxs as jsxs28 } from "react/jsx-runtime";
3966
+ var DEFAULT_ROOT2 = "grid grid-cols-3 gap-4";
3967
+ var DEFAULT_CELL = "flex flex-col items-center text-center";
3968
+ var DEFAULT_VALUE = "text-2xl font-bold";
3969
+ var DEFAULT_LABEL = "text-xs opacity-70";
3970
+ function PaywallStatsRow({
3971
+ stats,
3972
+ className,
3973
+ cellClassName,
3974
+ valueClassName,
3975
+ labelClassName,
3976
+ renderCell
3977
+ }) {
3978
+ const rootClasses = [DEFAULT_ROOT2, className].filter(Boolean).join(" ");
3979
+ const cellClasses = [DEFAULT_CELL, cellClassName].filter(Boolean).join(" ");
3980
+ const valueClasses = [DEFAULT_VALUE, valueClassName].filter(Boolean).join(" ");
3981
+ const labelClasses = [DEFAULT_LABEL, labelClassName].filter(Boolean).join(" ");
3982
+ return /* @__PURE__ */ jsx47("div", { className: rootClasses, children: stats.map((stat, idx) => {
3983
+ if (renderCell) return renderCell(stat, idx);
3984
+ return /* @__PURE__ */ jsxs28("div", { className: cellClasses, children: [
3985
+ stat.icon ? /* @__PURE__ */ jsx47("div", { "aria-hidden": "true", children: stat.icon }) : null,
3986
+ /* @__PURE__ */ jsx47("div", { className: valueClasses, children: stat.value }),
3987
+ /* @__PURE__ */ jsx47("div", { className: labelClasses, children: stat.label })
3988
+ ] }, idx);
3989
+ }) });
3990
+ }
3991
+
3992
+ // src/components/paywall/blocks/PaywallFinePrint.tsx
3993
+ import { jsx as jsx48 } from "react/jsx-runtime";
3994
+ var DEFAULT_CLASS3 = "text-xs opacity-60 leading-snug";
3995
+ var CYCLE_LABEL2 = {
3996
+ MONTHLY: "mensal",
3997
+ YEARLY: "anual"
3998
+ };
3999
+ function PaywallFinePrint({
4000
+ template,
4001
+ className,
4002
+ render
4003
+ }) {
4004
+ const {
4005
+ currentPriceCents,
4006
+ cycle,
4007
+ trialDaysCard,
4008
+ trialDaysPix,
4009
+ selectedMethod
4010
+ } = usePaywallContext();
4011
+ const trialDays = selectedMethod === "card" ? trialDaysCard : trialDaysPix;
4012
+ const cycleLabel = CYCLE_LABEL2[cycle] ?? cycle.toLowerCase();
4013
+ const priceFormatted = formatBRL(currentPriceCents ?? 0);
4014
+ const rootClasses = [DEFAULT_CLASS3, className].filter(Boolean).join(" ");
4015
+ if (render) {
4016
+ return /* @__PURE__ */ jsx48("p", { className: className || void 0, children: render({
4017
+ currentPriceCents: currentPriceCents ?? 0,
4018
+ cycle,
4019
+ trialDays: trialDays ?? 0,
4020
+ selectedMethod
4021
+ }) });
4022
+ }
4023
+ const text = template.replaceAll("{price}", priceFormatted).replaceAll("{trialDays}", String(trialDays ?? 0)).replaceAll("{cycle}", cycleLabel);
4024
+ return /* @__PURE__ */ jsx48("p", { className: rootClasses, children: text });
4025
+ }
4026
+
4027
+ // src/components/paywall/blocks/PaywallTrustLine.tsx
4028
+ import { jsx as jsx49, jsxs as jsxs29 } from "react/jsx-runtime";
4029
+ var DEFAULT_ROOT3 = "flex items-center gap-3";
4030
+ var DEFAULT_ITEM = "flex items-center gap-1.5 text-xs";
4031
+ function PaywallTrustLine({
4032
+ items,
4033
+ className,
4034
+ itemClassName,
4035
+ renderItem
4036
+ }) {
4037
+ const rootClasses = [DEFAULT_ROOT3, className].filter(Boolean).join(" ");
4038
+ const itemClasses = [DEFAULT_ITEM, itemClassName].filter(Boolean).join(" ");
4039
+ return /* @__PURE__ */ jsx49("div", { className: rootClasses, children: items.map((item, idx) => {
4040
+ if (renderItem) return renderItem(item, idx);
4041
+ return /* @__PURE__ */ jsxs29("span", { className: itemClasses, children: [
4042
+ /* @__PURE__ */ jsx49("span", { "aria-hidden": "true", children: item.icon }),
4043
+ /* @__PURE__ */ jsx49("span", { children: item.text })
4044
+ ] }, idx);
4045
+ }) });
4046
+ }
4047
+
4048
+ // src/components/paywall/blocks/PaywallStickyFooter.tsx
4049
+ import { jsx as jsx50 } from "react/jsx-runtime";
4050
+ var DEFAULT_CLASSES = "sticky bottom-0 left-0 right-0 bg-background";
4051
+ var SAFE_AREA_CLASS = "pb-[env(safe-area-inset-bottom)]";
4052
+ function PaywallStickyFooter({
4053
+ children,
4054
+ className,
4055
+ safeAreaInsets = true
4056
+ }) {
4057
+ const classes = [DEFAULT_CLASSES, safeAreaInsets ? SAFE_AREA_CLASS : null, className].filter(Boolean).join(" ");
4058
+ return /* @__PURE__ */ jsx50("div", { className: classes, children });
3578
4059
  }
3579
4060
  export {
3580
4061
  AppConfigProvider,
@@ -3592,10 +4073,26 @@ export {
3592
4073
  OnboardingFlow,
3593
4074
  PaymentReturnHandler,
3594
4075
  Paywall,
4076
+ PaywallAnchorPrice,
4077
+ PaywallContext,
4078
+ PaywallCountdown,
3595
4079
  PaywallCta,
3596
4080
  PaywallCyclePicker,
4081
+ PaywallEyebrow,
4082
+ PaywallFeatures,
4083
+ PaywallFeaturesCard,
4084
+ PaywallFinePrint,
4085
+ PaywallHeadline,
4086
+ PaywallHero,
3597
4087
  PaywallMethodContent,
3598
4088
  PaywallMethodTabs,
4089
+ PaywallPriceHeadline,
4090
+ PaywallProvider,
4091
+ PaywallStatsRow,
4092
+ PaywallStickyFooter,
4093
+ PaywallTestimonials,
4094
+ PaywallTrophyBadge,
4095
+ PaywallTrustLine,
3599
4096
  PersistenceRegistry,
3600
4097
  PreAuthShell,
3601
4098
  PushPrompt2 as PushPrompt,
@@ -3624,6 +4121,7 @@ export {
3624
4121
  useInstallPrompt,
3625
4122
  useLoginForm,
3626
4123
  useOnboardingStep,
4124
+ usePaywallContext,
3627
4125
  usePaywallState,
3628
4126
  usePlan,
3629
4127
  usePush,