@hook-sdk/template 0.19.0 → 0.21.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 useHook6 } from "@hook-sdk/sdk";
4
+ import { useHook as useHook7 } from "@hook-sdk/sdk";
5
5
 
6
6
  // src/config/AppConfigContext.tsx
7
7
  import { createContext, useContext } from "react";
@@ -35,7 +35,16 @@ var AuthFlowSchema = z.object({
35
35
  });
36
36
  var PaywallNonFreeSchema = z.object({
37
37
  mode: z.enum(["trial", "pay_first"]),
38
+ // Legacy flat trial — fallback when per-method fields aren't set.
38
39
  trialDays: z.number().int().nonnegative().optional(),
40
+ // Per-method trial (ADR-022 Amendment 2026-05-12 + G154). PIX Auto can't
41
+ // offer trial on Asaas today (Jornada 2 unavailable), so apps that mix
42
+ // methods must split the value to avoid bait-and-switch on PIX shoppers.
43
+ trialDaysCard: z.number().int().nonnegative().optional(),
44
+ trialDaysPix: z.number().int().nonnegative().optional(),
45
+ // Which method the paywall preselects. null/undefined = no preselection
46
+ // (user picks). Per-app override reflects audience, not Hook bias.
47
+ defaultMethod: z.enum(["card", "pix-auto", "pix-once"]).nullable().optional(),
39
48
  cycles: z.array(z.enum(["MONTHLY", "YEARLY"])).min(1),
40
49
  prices: z.object({
41
50
  monthlyCents: z.number().int().nonnegative(),
@@ -269,7 +278,7 @@ var FALLBACK_PAYWALL = {
269
278
  };
270
279
  var isMethodAvailable = (availability, method) => availability[method] !== false;
271
280
  function usePaywallState() {
272
- const { subscription, plan } = useHook3();
281
+ const { subscription, plan, authStatus, track: track2 } = useHook3();
273
282
  const configFromCtx = useContext3(AppConfigContext);
274
283
  const paywall = configFromCtx?.paywall ?? FALLBACK_PAYWALL;
275
284
  const isFree = paywall.mode === "free";
@@ -286,7 +295,8 @@ function usePaywallState() {
286
295
  () => declaredMethods.filter((m) => isMethodAvailable(availability, m)),
287
296
  [declaredMethods, availability]
288
297
  );
289
- const defaultMethod = methods[0] ?? declaredMethods[0] ?? "card";
298
+ const configDefault = !isFree && "defaultMethod" in paywall ? paywall.defaultMethod ?? null : null;
299
+ const defaultMethod = (configDefault && methods.includes(configDefault) ? configDefault : null) ?? methods[0] ?? declaredMethods[0] ?? "card";
290
300
  const [selectedMethodRaw, setSelectedMethod] = useState(defaultMethod);
291
301
  const selectedMethod = methods.includes(selectedMethodRaw) ? selectedMethodRaw : methods[0] ?? selectedMethodRaw;
292
302
  const initialCycle = isFree ? "MONTHLY" : paywall.cycles[0] ?? "MONTHLY";
@@ -307,6 +317,22 @@ function usePaywallState() {
307
317
  const [submitting, setSubmitting] = useState(false);
308
318
  const status = subscription.status();
309
319
  const daysLeftInTrial = subscription.daysLeftInTrial();
320
+ const trialDaysForMethod = useCallback(
321
+ (method) => {
322
+ if (isFree) return 0;
323
+ if (method === "card") {
324
+ if (typeof paywall.trialDaysCard === "number") return paywall.trialDaysCard;
325
+ if (typeof paywall.trialDays === "number") return paywall.trialDays;
326
+ return 7;
327
+ }
328
+ if (typeof paywall.trialDaysPix === "number") return paywall.trialDaysPix;
329
+ if (typeof paywall.trialDays === "number") return paywall.trialDays;
330
+ return 0;
331
+ },
332
+ [isFree, paywall]
333
+ );
334
+ const trialDaysCard = trialDaysForMethod("card");
335
+ const trialDaysPix = trialDaysForMethod("pix-auto");
310
336
  const initialLoadComplete = subscription.initialLoadComplete;
311
337
  const hasAccess = subscription.hasAccess;
312
338
  const pixPending = useMemo2(() => {
@@ -346,6 +372,40 @@ function usePaywallState() {
346
372
  discountPercent: discount
347
373
  };
348
374
  }, [paywall, cycle, isFree]);
375
+ const hasConsumedTrial = useMemo2(() => {
376
+ return ["active", "trialing", "past_due", "canceled", "expired"].includes(status);
377
+ }, [status]);
378
+ const currentPriceCents = useMemo2(() => {
379
+ if (isFree) return 0;
380
+ return cycle === "YEARLY" ? paywall.prices.yearlyCents : paywall.prices.monthlyCents;
381
+ }, [paywall, cycle, isFree]);
382
+ const currentMonthlyEquivCents = useMemo2(() => {
383
+ if (isFree) return 0;
384
+ if (cycle === "YEARLY") return Math.round(paywall.prices.yearlyCents / 12);
385
+ return paywall.prices.monthlyCents;
386
+ }, [paywall, cycle, isFree]);
387
+ const anchorPriceCents = useMemo2(() => {
388
+ if (isFree) return null;
389
+ const a = paywall.anchorPrices;
390
+ if (!a) return null;
391
+ return cycle === "YEARLY" ? a.yearlyCents : a.monthlyCents;
392
+ }, [paywall, cycle, isFree]);
393
+ const selectMethod = useCallback(
394
+ (next) => {
395
+ if (next === selectedMethod) return;
396
+ track2("paywall_method_tab_clicked", { method: next, from_method: selectedMethod });
397
+ setSelectedMethod(next);
398
+ },
399
+ [selectedMethod, track2]
400
+ );
401
+ const selectCycle = useCallback(
402
+ (next) => {
403
+ if (next === cycle) return;
404
+ track2("paywall_cycle_clicked", { cycle: next, from_cycle: cycle });
405
+ setCycle(next);
406
+ },
407
+ [cycle, track2]
408
+ );
349
409
  const useDefaultMessages = paywall.mode !== "free" && paywall.errorMessages === "default";
350
410
  const buildError = useCallback(
351
411
  (code, fallbackMessage) => ({
@@ -358,6 +418,21 @@ function usePaywallState() {
358
418
  [useDefaultMessages]
359
419
  );
360
420
  const submit = useCallback(async () => {
421
+ if (authStatus === "loading") return void 0;
422
+ if (authStatus !== "authenticated") {
423
+ track2("unauthenticated_submit_attempted", {
424
+ method: selectedMethod,
425
+ cycle,
426
+ cpf_valid: cpfValid
427
+ });
428
+ return void 0;
429
+ }
430
+ track2("payment_attempted", {
431
+ method: selectedMethod,
432
+ cycle,
433
+ cpf_valid: cpfValid,
434
+ selected_amount_cents: cycle === "YEARLY" ? plan.data?.yearlyPriceCents ?? (isFree ? 0 : paywall.prices.yearlyCents) : plan.data?.priceCents ?? (isFree ? 0 : paywall.prices.monthlyCents)
435
+ });
361
436
  setSubmitting(true);
362
437
  setError(null);
363
438
  const methodToUse = selectedMethod;
@@ -418,7 +493,7 @@ function usePaywallState() {
418
493
  setSubmitting(false);
419
494
  return void 0;
420
495
  }
421
- }, [selectedMethod, availability, subscription, cycle, cpf, card, buildError]);
496
+ }, [authStatus, track2, selectedMethod, availability, subscription, cycle, cpf, cpfValid, card, buildError, plan, paywall]);
422
497
  const checkout = useCallback(
423
498
  async (args) => {
424
499
  setSubmitting(true);
@@ -525,6 +600,20 @@ function usePaywallState() {
525
600
  methods,
526
601
  selectedMethod,
527
602
  setSelectedMethod,
603
+ // Conversion-max derivations (template 0.21)
604
+ hasConsumedTrial,
605
+ currentPriceCents,
606
+ currentMonthlyEquivCents,
607
+ anchorPriceCents,
608
+ selectMethod,
609
+ selectCycle,
610
+ isFree,
611
+ // Per-method trial (ADR-022 Amendment 2026-05-12). Use these to render
612
+ // asymmetric copy per method card on the paywall — never a global
613
+ // "free trial!" headline when both methods are visible.
614
+ trialDaysForMethod,
615
+ trialDaysCard,
616
+ trialDaysPix,
528
617
  // Form state
529
618
  cpfState,
530
619
  cardState,
@@ -556,13 +645,13 @@ var BLOCKING = /* @__PURE__ */ new Set([
556
645
  "canceled",
557
646
  "none"
558
647
  ]);
559
- function SubscriptionGate({ Paywall, children }) {
648
+ function SubscriptionGate({ Paywall: Paywall2, children }) {
560
649
  const { mode } = useTemplateConfig();
561
650
  const { status, hasAccess, initialLoadComplete } = usePaywallState();
562
651
  if (mode === "free") return /* @__PURE__ */ jsx5(Fragment2, { children });
563
652
  if (!initialLoadComplete && status === "none") return null;
564
653
  if (hasAccess === true && status !== "none") return /* @__PURE__ */ jsx5(Fragment2, { children });
565
- if (BLOCKING.has(status)) return /* @__PURE__ */ jsx5(Paywall, {});
654
+ if (BLOCKING.has(status)) return /* @__PURE__ */ jsx5(Paywall2, {});
566
655
  return /* @__PURE__ */ jsx5(Fragment2, { children });
567
656
  }
568
657
 
@@ -2008,23 +2097,162 @@ function I18nProvider({
2008
2097
  return /* @__PURE__ */ jsx19(I18nextProvider, { i18n, children });
2009
2098
  }
2010
2099
 
2011
- // src/internal/PaymentReturnHandler.tsx
2012
- import { useCallback as useCallback3, useEffect as useEffect8, useRef as useRef4, useState as useState5 } from "react";
2100
+ // src/dev/env.ts
2101
+ function isDevToolsEnabled() {
2102
+ const meta = import.meta;
2103
+ if (meta.env?.VITE_HOOK_DEV_TOOLS !== "1") return false;
2104
+ if (typeof window === "undefined") return false;
2105
+ const host = window.location.hostname;
2106
+ return host.includes(".staging.") || host === "localhost" || host === "127.0.0.1";
2107
+ }
2108
+
2109
+ // src/dev/DevSkipOnboardingFab.tsx
2110
+ import { useCallback as useCallback3, useRef as useRef4, useState as useState5 } from "react";
2013
2111
  import { useHook as useHook5 } from "@hook-sdk/sdk";
2014
- import { Fragment as Fragment5, jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
2112
+ import { jsx as jsx20 } from "react/jsx-runtime";
2113
+ var STORAGE_KEY = "hook_dev_skip_email";
2114
+ var TEST_EMAIL_DOMAIN = "@hook.test";
2115
+ var TEST_PASSWORD = "SkipTest!2026";
2116
+ function makeEmail() {
2117
+ return `ryan+skip-${Date.now()}${TEST_EMAIL_DOMAIN}`;
2118
+ }
2119
+ async function ensureSignedIn(hook, maxAttempts = 3) {
2120
+ if (hook.authStatus === "authenticated") return null;
2121
+ let lastErr;
2122
+ for (let i = 0; i < maxAttempts; i += 1) {
2123
+ const email = makeEmail();
2124
+ try {
2125
+ await hook.auth.signup({ email, password: TEST_PASSWORD, name: "Ryan Test" });
2126
+ try {
2127
+ window.sessionStorage.setItem(STORAGE_KEY, email);
2128
+ } catch {
2129
+ }
2130
+ return email;
2131
+ } catch (err) {
2132
+ lastErr = err;
2133
+ await new Promise((r) => setTimeout(r, 30));
2134
+ }
2135
+ }
2136
+ throw lastErr ?? new Error("signup failed after retries");
2137
+ }
2138
+ async function skipOnboarding(hook, defaults, appSlug) {
2139
+ const { __seed, ...rest } = defaults;
2140
+ await ensureSignedIn(hook);
2141
+ await hook.appData.set("onboarding_data", {
2142
+ ...rest,
2143
+ onboarding_completed: true
2144
+ });
2145
+ if (__seed) {
2146
+ await __seed(hook);
2147
+ }
2148
+ console.info("[hook-template] dev_skip_onboarding fired", {
2149
+ app_slug: appSlug,
2150
+ hostname: window.location.hostname
2151
+ });
2152
+ window.location.assign(`/app/${appSlug}/`);
2153
+ }
2154
+ var STYLES = {
2155
+ base: {
2156
+ position: "fixed",
2157
+ bottom: "16px",
2158
+ right: "16px",
2159
+ zIndex: 2147483647,
2160
+ padding: "10px 14px",
2161
+ borderRadius: "999px",
2162
+ border: "none",
2163
+ background: "#F59E0B",
2164
+ color: "#111827",
2165
+ fontWeight: 600,
2166
+ fontSize: "13px",
2167
+ fontFamily: "system-ui, -apple-system, sans-serif",
2168
+ boxShadow: "0 4px 14px rgba(0, 0, 0, 0.25)",
2169
+ cursor: "pointer",
2170
+ display: "flex",
2171
+ alignItems: "center",
2172
+ gap: "6px"
2173
+ },
2174
+ confirm: { background: "#DC2626", color: "#FFFFFF" },
2175
+ busy: { opacity: 0.6, cursor: "wait" }
2176
+ };
2177
+ var CONFIRM_TIMEOUT_MS = 3e3;
2178
+ function DevSkipOnboardingFab({ defaults }) {
2179
+ const hook = useHook5();
2180
+ const { slug } = useAppConfig();
2181
+ const [state, setState] = useState5("idle");
2182
+ const [errorMsg, setErrorMsg] = useState5(null);
2183
+ const timerRef = useRef4(null);
2184
+ const clearTimer = useCallback3(() => {
2185
+ if (timerRef.current) {
2186
+ clearTimeout(timerRef.current);
2187
+ timerRef.current = null;
2188
+ }
2189
+ }, []);
2190
+ const onClick = useCallback3(async () => {
2191
+ if (state === "busy") return;
2192
+ if (state === "idle" || state === "error") {
2193
+ setState("confirm");
2194
+ setErrorMsg(null);
2195
+ clearTimer();
2196
+ timerRef.current = setTimeout(() => setState("idle"), CONFIRM_TIMEOUT_MS);
2197
+ return;
2198
+ }
2199
+ clearTimer();
2200
+ setState("busy");
2201
+ try {
2202
+ await skipOnboarding(hook, defaults, slug);
2203
+ } catch (err) {
2204
+ setState("error");
2205
+ setErrorMsg(err instanceof Error ? err.message : String(err));
2206
+ }
2207
+ }, [state, hook, defaults, slug, clearTimer]);
2208
+ const label = (() => {
2209
+ if (state === "busy") return "skipping\u2026";
2210
+ if (state === "confirm") return "tap again to confirm";
2211
+ if (state === "error") return `failed \u2014 tap to retry`;
2212
+ return hook.authStatus === "authenticated" ? "\u26A1 skip onboarding" : "\u26A1 skip + signup";
2213
+ })();
2214
+ const style = {
2215
+ ...STYLES.base,
2216
+ ...state === "confirm" || state === "error" ? STYLES.confirm : {},
2217
+ ...state === "busy" ? STYLES.busy : {}
2218
+ };
2219
+ return /* @__PURE__ */ jsx20(
2220
+ "button",
2221
+ {
2222
+ type: "button",
2223
+ "data-testid": "dev-skip-onboarding-fab",
2224
+ "aria-label": "Skip onboarding (staging dev only)",
2225
+ style,
2226
+ onClick,
2227
+ title: errorMsg ?? void 0,
2228
+ children: label
2229
+ }
2230
+ );
2231
+ }
2232
+
2233
+ // src/internal/PaymentReturnHandler.tsx
2234
+ import { useCallback as useCallback4, useEffect as useEffect8, useRef as useRef5, useState as useState6 } from "react";
2235
+ import { useHook as useHook6 } from "@hook-sdk/sdk";
2236
+ import { Fragment as Fragment5, jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
2015
2237
  var BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 4e4];
2016
2238
  var MAX_CYCLES = 3;
2017
2239
  var SUPPORT_MAILTO = "mailto:suporte@usehook.net?subject=Pagamento%20pendente";
2018
2240
  function PaymentReturnHandler({ children }) {
2019
- const { subscription } = useHook5();
2020
- const subRef = useRef4(subscription);
2241
+ const { subscription, track: track2 } = useHook6();
2242
+ const subRef = useRef5(subscription);
2021
2243
  subRef.current = subscription;
2022
- const runIdRef = useRef4(0);
2023
- const cyclesRef = useRef4(0);
2024
- const [state, setState] = useState5("idle");
2025
- const runPoll = useCallback3(() => {
2244
+ const runIdRef = useRef5(0);
2245
+ const cyclesRef = useRef5(0);
2246
+ const startMsRef = useRef5(0);
2247
+ const [state, setState] = useState6("idle");
2248
+ const runPoll = useCallback4(() => {
2026
2249
  const runId = ++runIdRef.current;
2250
+ const isFirstRun = cyclesRef.current === 0;
2027
2251
  cyclesRef.current += 1;
2252
+ if (isFirstRun) {
2253
+ startMsRef.current = Date.now();
2254
+ track2("payment_confirmation_started", {});
2255
+ }
2028
2256
  setState("confirming");
2029
2257
  let attempts = 0;
2030
2258
  const tick = async () => {
@@ -2037,6 +2265,11 @@ function PaymentReturnHandler({ children }) {
2037
2265
  if (runIdRef.current !== runId) return;
2038
2266
  const status = subRef.current.status();
2039
2267
  if (status === "active" || status === "trialing") {
2268
+ track2("payment_confirmation_succeeded", {
2269
+ cycle_count: cyclesRef.current,
2270
+ attempt_count: attempts,
2271
+ duration_ms: Date.now() - startMsRef.current
2272
+ });
2040
2273
  const cleanUrl = new URL(window.location.href);
2041
2274
  cleanUrl.searchParams.delete("paymentReturn");
2042
2275
  window.history.replaceState({}, "", cleanUrl.toString());
@@ -2047,6 +2280,9 @@ function PaymentReturnHandler({ children }) {
2047
2280
  const delay = BACKOFF_MS[attempts - 1];
2048
2281
  if (delay === void 0) {
2049
2282
  if (cyclesRef.current >= MAX_CYCLES) {
2283
+ track2("payment_confirmation_timed_out", {
2284
+ total_duration_ms: Date.now() - startMsRef.current
2285
+ });
2050
2286
  setState("timeout");
2051
2287
  } else {
2052
2288
  setState("waiting");
@@ -2056,7 +2292,7 @@ function PaymentReturnHandler({ children }) {
2056
2292
  setTimeout(tick, delay);
2057
2293
  };
2058
2294
  void tick();
2059
- }, []);
2295
+ }, [track2]);
2060
2296
  useEffect8(() => {
2061
2297
  if (typeof window === "undefined") return;
2062
2298
  const url = new URL(window.location.href);
@@ -2067,26 +2303,26 @@ function PaymentReturnHandler({ children }) {
2067
2303
  runIdRef.current++;
2068
2304
  };
2069
2305
  }, [runPoll]);
2070
- const goHome = useCallback3(() => {
2306
+ const goHome = useCallback4(() => {
2071
2307
  const cleanUrl = new URL(window.location.href);
2072
2308
  cleanUrl.searchParams.delete("paymentReturn");
2073
2309
  cleanUrl.pathname = "/app/home";
2074
2310
  window.location.href = cleanUrl.toString();
2075
2311
  }, []);
2076
2312
  if (state === "confirming") {
2077
- return /* @__PURE__ */ jsx20("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: "Confirmando pagamento\u2026" });
2313
+ return /* @__PURE__ */ jsx21("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: "Confirmando pagamento\u2026" });
2078
2314
  }
2079
2315
  if (state === "waiting") {
2080
- return /* @__PURE__ */ jsx20("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ jsxs13("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
2081
- /* @__PURE__ */ jsx20("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
2082
- /* @__PURE__ */ jsx20("button", { type: "button", onClick: runPoll, style: buttonStyle, children: "Atualizar" })
2316
+ return /* @__PURE__ */ jsx21("div", { role: "status", "aria-live": "polite", style: overlayStyle2, children: /* @__PURE__ */ jsxs13("div", { style: { maxWidth: 320, textAlign: "center", lineHeight: 1.5 }, children: [
2317
+ /* @__PURE__ */ jsx21("div", { style: { marginBottom: 16 }, children: "Pagamento aceito. Estamos confirmando com o banco \u2014 pode levar alguns minutos." }),
2318
+ /* @__PURE__ */ jsx21("button", { type: "button", onClick: runPoll, style: buttonStyle, children: "Atualizar" })
2083
2319
  ] }) });
2084
2320
  }
2085
2321
  if (state === "timeout") {
2086
- return /* @__PURE__ */ jsx20("div", { role: "alert", "aria-live": "assertive", style: overlayStyle2, children: /* @__PURE__ */ jsxs13("div", { style: { maxWidth: 360, textAlign: "center", lineHeight: 1.5 }, children: [
2087
- /* @__PURE__ */ jsx20("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
+ return /* @__PURE__ */ jsx21("div", { role: "alert", "aria-live": "assertive", style: overlayStyle2, children: /* @__PURE__ */ jsxs13("div", { style: { maxWidth: 360, textAlign: "center", lineHeight: 1.5 }, children: [
2323
+ /* @__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." }),
2088
2324
  /* @__PURE__ */ jsxs13("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
2089
- /* @__PURE__ */ jsx20(
2325
+ /* @__PURE__ */ jsx21(
2090
2326
  "button",
2091
2327
  {
2092
2328
  type: "button",
@@ -2099,7 +2335,7 @@ function PaymentReturnHandler({ children }) {
2099
2335
  children: "Tentar de novo"
2100
2336
  }
2101
2337
  ),
2102
- /* @__PURE__ */ jsx20(
2338
+ /* @__PURE__ */ jsx21(
2103
2339
  "button",
2104
2340
  {
2105
2341
  type: "button",
@@ -2109,7 +2345,7 @@ function PaymentReturnHandler({ children }) {
2109
2345
  children: "Voltar pro app"
2110
2346
  }
2111
2347
  ),
2112
- /* @__PURE__ */ jsx20(
2348
+ /* @__PURE__ */ jsx21(
2113
2349
  "a",
2114
2350
  {
2115
2351
  href: SUPPORT_MAILTO,
@@ -2121,7 +2357,7 @@ function PaymentReturnHandler({ children }) {
2121
2357
  ] })
2122
2358
  ] }) });
2123
2359
  }
2124
- return /* @__PURE__ */ jsx20(Fragment5, { children });
2360
+ return /* @__PURE__ */ jsx21(Fragment5, { children });
2125
2361
  }
2126
2362
  var overlayStyle2 = {
2127
2363
  position: "fixed",
@@ -2160,7 +2396,7 @@ var linkStyle = {
2160
2396
  };
2161
2397
 
2162
2398
  // src/AppRoot.tsx
2163
- import { Fragment as Fragment6, jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
2399
+ import { Fragment as Fragment6, jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
2164
2400
  function buildLegacyConfigShim(config) {
2165
2401
  const paywall = config.paywall;
2166
2402
  const isFree = paywall.mode === "free";
@@ -2208,9 +2444,10 @@ function AppRoot(props) {
2208
2444
  Forgot,
2209
2445
  Reset,
2210
2446
  EmailVerify,
2211
- Paywall,
2447
+ Paywall: Paywall2,
2212
2448
  Onboarding,
2213
- PreAuthFlow
2449
+ PreAuthFlow,
2450
+ devSkipOnboarding
2214
2451
  } = props;
2215
2452
  if (!Login || !Signup || !Forgot || !Reset) {
2216
2453
  throw new Error(
@@ -2218,7 +2455,7 @@ function AppRoot(props) {
2218
2455
  );
2219
2456
  }
2220
2457
  const config = parseAppConfig(rawConfig);
2221
- if (config.paywall.mode !== "free" && !Paywall) {
2458
+ if (config.paywall.mode !== "free" && !Paywall2) {
2222
2459
  throw new Error(
2223
2460
  "[hook-template] <AppRoot>: Paywall slot prop is required when config.paywall.mode != 'free'."
2224
2461
  );
@@ -2238,14 +2475,14 @@ function AppRoot(props) {
2238
2475
  const basename = `/app/${config.slug}`;
2239
2476
  const routerProps = testRouter === "memory" ? { basename, initialEntries: testInitialEntries } : { basename };
2240
2477
  const position = config.install_prompt?.position ?? "post-paywall";
2241
- const subscriptionGated = /* @__PURE__ */ jsx21(SubscriptionGate, { Paywall: Paywall ?? FallbackPaywall, children: position === "post-paywall" ? /* @__PURE__ */ jsxs14(InstallGate, { position: "post-paywall", children: [
2478
+ const subscriptionGated = /* @__PURE__ */ jsx22(SubscriptionGate, { Paywall: Paywall2 ?? FallbackPaywall, children: position === "post-paywall" ? /* @__PURE__ */ jsxs14(InstallGate, { position: "post-paywall", children: [
2242
2479
  children,
2243
- /* @__PURE__ */ jsx21(PushPrompt, {})
2480
+ /* @__PURE__ */ jsx22(PushPrompt, {})
2244
2481
  ] }) : /* @__PURE__ */ jsxs14(Fragment6, { children: [
2245
2482
  children,
2246
- /* @__PURE__ */ jsx21(PushPrompt, {})
2483
+ /* @__PURE__ */ jsx22(PushPrompt, {})
2247
2484
  ] }) });
2248
- const authGated = /* @__PURE__ */ jsx21(
2485
+ const authGated = /* @__PURE__ */ jsx22(
2249
2486
  AuthGated,
2250
2487
  {
2251
2488
  config,
@@ -2254,18 +2491,19 @@ function AppRoot(props) {
2254
2491
  Forgot,
2255
2492
  Reset,
2256
2493
  EmailVerify,
2257
- Paywall,
2494
+ Paywall: Paywall2,
2258
2495
  Onboarding,
2259
2496
  PreAuthFlow,
2260
2497
  children: subscriptionGated
2261
2498
  }
2262
2499
  );
2263
2500
  const routedTree = /* @__PURE__ */ jsxs14(Router, { ...routerProps, children: [
2264
- /* @__PURE__ */ jsx21(DeepLinkHandler, { deepLinks: config.deepLinks }),
2265
- /* @__PURE__ */ jsx21(SessionExpiredBanner, {}),
2266
- position === "pre-auth" ? /* @__PURE__ */ jsx21(InstallGate, { position: "pre-auth", children: authGated }) : authGated
2501
+ /* @__PURE__ */ jsx22(DeepLinkHandler, { deepLinks: config.deepLinks }),
2502
+ /* @__PURE__ */ jsx22(SessionExpiredBanner, {}),
2503
+ position === "pre-auth" ? /* @__PURE__ */ jsx22(InstallGate, { position: "pre-auth", children: authGated }) : authGated,
2504
+ isDevToolsEnabled() && devSkipOnboarding ? /* @__PURE__ */ jsx22(DevSkipOnboardingFab, { defaults: devSkipOnboarding.defaults }) : null
2267
2505
  ] });
2268
- return /* @__PURE__ */ jsx21(ErrorBoundary, { children: /* @__PURE__ */ jsx21(AppConfigProvider, { config, children: /* @__PURE__ */ jsx21(TemplateConfigProvider, { config: legacyShim, children: /* @__PURE__ */ jsx21(ThemeProvider, { children: /* @__PURE__ */ jsx21(PersistenceRegistry, { config: config.persistedKeys, children: config.i18n ? /* @__PURE__ */ jsx21(
2506
+ 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(
2269
2507
  I18nProvider,
2270
2508
  {
2271
2509
  defaultLocale: config.i18n.defaultLocale,
@@ -2285,37 +2523,37 @@ function AuthGated({
2285
2523
  EmailVerify,
2286
2524
  PreAuthFlow
2287
2525
  }) {
2288
- const { authStatus } = useHook6();
2526
+ const { authStatus } = useHook7();
2289
2527
  if (authStatus === "loading") return null;
2290
2528
  if (authStatus !== "authenticated") {
2291
2529
  if (config.onboarding?.trigger === "pre_signup_custom" && PreAuthFlow) {
2292
2530
  return /* @__PURE__ */ jsxs14(Routes, { children: [
2293
- /* @__PURE__ */ jsx21(Route, { path: "/signin", element: /* @__PURE__ */ jsx21(Login, {}) }),
2294
- /* @__PURE__ */ jsx21(Route, { path: "/signup", element: /* @__PURE__ */ jsx21(Signup, {}) }),
2295
- /* @__PURE__ */ jsx21(Route, { path: "/forgot", element: /* @__PURE__ */ jsx21(Forgot, {}) }),
2296
- /* @__PURE__ */ jsx21(Route, { path: "/reset", element: /* @__PURE__ */ jsx21(Reset, {}) }),
2297
- EmailVerify ? /* @__PURE__ */ jsx21(Route, { path: "/verify", element: /* @__PURE__ */ jsx21(EmailVerify, {}) }) : null,
2298
- /* @__PURE__ */ jsx21(Route, { path: "/*", element: /* @__PURE__ */ jsx21(PreAuthFlow, {}) })
2531
+ /* @__PURE__ */ jsx22(Route, { path: "/signin", element: /* @__PURE__ */ jsx22(Login, {}) }),
2532
+ /* @__PURE__ */ jsx22(Route, { path: "/signup", element: /* @__PURE__ */ jsx22(Signup, {}) }),
2533
+ /* @__PURE__ */ jsx22(Route, { path: "/forgot", element: /* @__PURE__ */ jsx22(Forgot, {}) }),
2534
+ /* @__PURE__ */ jsx22(Route, { path: "/reset", element: /* @__PURE__ */ jsx22(Reset, {}) }),
2535
+ EmailVerify ? /* @__PURE__ */ jsx22(Route, { path: "/verify", element: /* @__PURE__ */ jsx22(EmailVerify, {}) }) : null,
2536
+ /* @__PURE__ */ jsx22(Route, { path: "/*", element: /* @__PURE__ */ jsx22(PreAuthFlow, {}) })
2299
2537
  ] });
2300
2538
  }
2301
2539
  return /* @__PURE__ */ jsxs14(Routes, { children: [
2302
- /* @__PURE__ */ jsx21(Route, { path: "/", element: /* @__PURE__ */ jsx21(Login, {}) }),
2303
- /* @__PURE__ */ jsx21(Route, { path: "/signup", element: /* @__PURE__ */ jsx21(Signup, {}) }),
2304
- /* @__PURE__ */ jsx21(Route, { path: "/forgot", element: /* @__PURE__ */ jsx21(Forgot, {}) }),
2305
- /* @__PURE__ */ jsx21(Route, { path: "/reset", element: /* @__PURE__ */ jsx21(Reset, {}) }),
2306
- EmailVerify ? /* @__PURE__ */ jsx21(Route, { path: "/verify", element: /* @__PURE__ */ jsx21(EmailVerify, {}) }) : null,
2307
- /* @__PURE__ */ jsx21(Route, { path: "*", element: /* @__PURE__ */ jsx21(Navigate, { to: "/", replace: true }) })
2540
+ /* @__PURE__ */ jsx22(Route, { path: "/", element: /* @__PURE__ */ jsx22(Login, {}) }),
2541
+ /* @__PURE__ */ jsx22(Route, { path: "/signup", element: /* @__PURE__ */ jsx22(Signup, {}) }),
2542
+ /* @__PURE__ */ jsx22(Route, { path: "/forgot", element: /* @__PURE__ */ jsx22(Forgot, {}) }),
2543
+ /* @__PURE__ */ jsx22(Route, { path: "/reset", element: /* @__PURE__ */ jsx22(Reset, {}) }),
2544
+ EmailVerify ? /* @__PURE__ */ jsx22(Route, { path: "/verify", element: /* @__PURE__ */ jsx22(EmailVerify, {}) }) : null,
2545
+ /* @__PURE__ */ jsx22(Route, { path: "*", element: /* @__PURE__ */ jsx22(Navigate, { to: "/", replace: true }) })
2308
2546
  ] });
2309
2547
  }
2310
- return /* @__PURE__ */ jsx21(Fragment6, { children });
2548
+ return /* @__PURE__ */ jsx22(Fragment6, { children });
2311
2549
  }
2312
2550
  function FallbackPaywall() {
2313
2551
  return null;
2314
2552
  }
2315
2553
 
2316
2554
  // src/hooks/usePush.ts
2317
- import { useCallback as useCallback4, useEffect as useEffect9, useState as useState6 } from "react";
2318
- import { useHook as useHook7 } from "@hook-sdk/sdk";
2555
+ import { useCallback as useCallback5, useEffect as useEffect9, useState as useState7 } from "react";
2556
+ import { useHook as useHook8 } from "@hook-sdk/sdk";
2319
2557
  var DISMISS_STORAGE_KEY = "push:dismissed-until";
2320
2558
  var DISMISS_TTL_MS2 = 7 * 24 * 60 * 60 * 1e3;
2321
2559
  function detectIosNeedsInstall() {
@@ -2359,12 +2597,12 @@ function deriveState(push) {
2359
2597
  return { kind: "prompt" };
2360
2598
  }
2361
2599
  function usePush() {
2362
- const { push } = useHook7();
2363
- const [state, setState] = useState6(() => deriveState(push));
2600
+ const { push } = useHook8();
2601
+ const [state, setState] = useState7(() => deriveState(push));
2364
2602
  useEffect9(() => {
2365
2603
  setState(deriveState(push));
2366
2604
  }, [push]);
2367
- const subscribe = useCallback4(async () => {
2605
+ const subscribe = useCallback5(async () => {
2368
2606
  try {
2369
2607
  await push.subscribe();
2370
2608
  setState({ kind: "subscribed" });
@@ -2376,7 +2614,7 @@ function usePush() {
2376
2614
  throw e;
2377
2615
  }
2378
2616
  }, [push]);
2379
- const unsubscribe = useCallback4(async () => {
2617
+ const unsubscribe = useCallback5(async () => {
2380
2618
  try {
2381
2619
  await push.unsubscribe();
2382
2620
  setState({ kind: "prompt" });
@@ -2385,7 +2623,7 @@ function usePush() {
2385
2623
  throw e;
2386
2624
  }
2387
2625
  }, [push]);
2388
- const dismiss = useCallback4(() => {
2626
+ const dismiss = useCallback5(() => {
2389
2627
  if (typeof localStorage !== "undefined") {
2390
2628
  try {
2391
2629
  localStorage.setItem(DISMISS_STORAGE_KEY, String(Date.now() + DISMISS_TTL_MS2));
@@ -2398,51 +2636,27 @@ function usePush() {
2398
2636
  }
2399
2637
 
2400
2638
  // src/components/PushPrompt.tsx
2401
- import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
2402
- function platformRecoveryCopy(texts) {
2403
- if (typeof navigator === "undefined") return null;
2404
- const ua = navigator.userAgent || "";
2405
- const platform = detectPlatform(ua);
2406
- switch (platform) {
2407
- case "ios-safari":
2408
- case "ios-other":
2409
- return texts.deniedRecoveryIos ?? null;
2410
- case "android":
2411
- return texts.deniedRecoveryAndroid ?? null;
2412
- case "desktop":
2413
- return texts.deniedRecoveryDesktop ?? null;
2414
- case "in-app":
2415
- return texts.deniedRecoveryInApp ?? null;
2416
- default:
2417
- return null;
2418
- }
2419
- }
2639
+ import { jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
2420
2640
  function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, className }) {
2421
2641
  const { state, subscribe } = usePush();
2422
- if (state.kind === "subscribed" || state.kind === "dismissed") return null;
2642
+ if (state.kind === "denied" || state.kind === "dismissed" || state.kind === "subscribed") {
2643
+ return null;
2644
+ }
2423
2645
  if (state.kind === "ios_needs_install") {
2424
2646
  return /* @__PURE__ */ jsxs15("div", { className, role: "region", "aria-label": texts.iosInstallTitle, children: [
2425
- /* @__PURE__ */ jsx22("h3", { children: texts.iosInstallTitle }),
2426
- /* @__PURE__ */ jsx22("p", { children: texts.iosInstallBody }),
2427
- onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx22("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
2428
- ] });
2429
- }
2430
- if (state.kind === "denied") {
2431
- const recovery = platformRecoveryCopy(texts);
2432
- return /* @__PURE__ */ jsxs15("div", { className, role: "region", "aria-label": texts.deniedTitle, children: [
2433
- /* @__PURE__ */ jsx22("h3", { children: texts.deniedTitle }),
2434
- /* @__PURE__ */ jsx22("p", { children: texts.deniedBody }),
2435
- recovery && /* @__PURE__ */ jsx22("p", { "data-testid": "denied-recovery", children: recovery })
2647
+ /* @__PURE__ */ jsx23("h3", { children: texts.iosInstallTitle }),
2648
+ /* @__PURE__ */ jsx23("p", { children: texts.iosInstallBody }),
2649
+ onInstallRequested && texts.iosInstallCta && /* @__PURE__ */ jsx23("button", { onClick: onInstallRequested, children: texts.iosInstallCta })
2436
2650
  ] });
2437
2651
  }
2438
2652
  if (state.kind === "unsupported") {
2439
- return /* @__PURE__ */ jsx22("div", { className, role: "region", children: /* @__PURE__ */ jsx22("p", { children: texts.unsupportedBody }) });
2653
+ return /* @__PURE__ */ jsx23("div", { className, role: "region", children: /* @__PURE__ */ jsx23("p", { children: texts.unsupportedBody }) });
2440
2654
  }
2441
2655
  if (state.kind === "error") {
2442
- return /* @__PURE__ */ jsx22("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx22("p", { children: state.message }) });
2656
+ return /* @__PURE__ */ jsx23("div", { className, role: "region", "aria-label": "error", children: /* @__PURE__ */ jsx23("p", { children: state.message }) });
2443
2657
  }
2444
2658
  return /* @__PURE__ */ jsxs15("div", { className, role: "region", children: [
2445
- /* @__PURE__ */ jsx22(
2659
+ /* @__PURE__ */ jsx23(
2446
2660
  "button",
2447
2661
  {
2448
2662
  type: "button",
@@ -2456,13 +2670,13 @@ function PushPrompt2({ texts, onSubscribed, onDeclined, onInstallRequested, clas
2456
2670
  children: texts.cta
2457
2671
  }
2458
2672
  ),
2459
- onDeclined && /* @__PURE__ */ jsx22("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
2673
+ onDeclined && /* @__PURE__ */ jsx23("button", { type: "button", onClick: onDeclined, children: texts.declineCta })
2460
2674
  ] });
2461
2675
  }
2462
2676
 
2463
2677
  // src/components/LanguageSwitcher.tsx
2464
2678
  import { usePersistedState as usePersistedState2 } from "@hook-sdk/sdk";
2465
- import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
2679
+ import { jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
2466
2680
  function LanguageSwitcher({ id, className, label = "Language" }) {
2467
2681
  const config = useAppConfig();
2468
2682
  const i18nConfig = config.i18n;
@@ -2472,39 +2686,39 @@ function LanguageSwitcher({ id, className, label = "Language" }) {
2472
2686
  );
2473
2687
  if (!i18nConfig) return null;
2474
2688
  return /* @__PURE__ */ jsxs16("label", { className, children: [
2475
- label ? /* @__PURE__ */ jsx23("span", { children: label }) : null,
2476
- /* @__PURE__ */ jsx23(
2689
+ label ? /* @__PURE__ */ jsx24("span", { children: label }) : null,
2690
+ /* @__PURE__ */ jsx24(
2477
2691
  "select",
2478
2692
  {
2479
2693
  id,
2480
2694
  value: userLocale,
2481
2695
  onChange: (e) => setUserLocale(e.target.value),
2482
2696
  "data-testid": "language-switcher",
2483
- children: i18nConfig.supportedLocales.map((loc) => /* @__PURE__ */ jsx23("option", { value: loc, children: loc }, loc))
2697
+ children: i18nConfig.supportedLocales.map((loc) => /* @__PURE__ */ jsx24("option", { value: loc, children: loc }, loc))
2484
2698
  }
2485
2699
  )
2486
2700
  ] });
2487
2701
  }
2488
2702
 
2489
2703
  // src/defaults/LoadingState.tsx
2490
- import { jsx as jsx24 } from "react/jsx-runtime";
2704
+ import { jsx as jsx25 } from "react/jsx-runtime";
2491
2705
  function LoadingState({ message }) {
2492
- return /* @__PURE__ */ jsx24("div", { role: "status", "aria-live": "polite", style: { padding: 24, textAlign: "center" }, children: /* @__PURE__ */ jsx24("span", { children: message ?? "Carregando..." }) });
2706
+ return /* @__PURE__ */ jsx25("div", { role: "status", "aria-live": "polite", style: { padding: 24, textAlign: "center" }, children: /* @__PURE__ */ jsx25("span", { children: message ?? "Carregando..." }) });
2493
2707
  }
2494
2708
 
2495
2709
  // src/defaults/EmptyState.tsx
2496
- import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
2710
+ import { jsx as jsx26, jsxs as jsxs17 } from "react/jsx-runtime";
2497
2711
  function EmptyState({ title, description, action }) {
2498
2712
  return /* @__PURE__ */ jsxs17("div", { role: "status", style: { padding: 32, textAlign: "center" }, children: [
2499
- /* @__PURE__ */ jsx25("h2", { style: { marginBottom: 8 }, children: title }),
2500
- description && /* @__PURE__ */ jsx25("p", { style: { opacity: 0.7 }, children: description }),
2501
- action && /* @__PURE__ */ jsx25("div", { style: { marginTop: 16 }, children: action })
2713
+ /* @__PURE__ */ jsx26("h2", { style: { marginBottom: 8 }, children: title }),
2714
+ description && /* @__PURE__ */ jsx26("p", { style: { opacity: 0.7 }, children: description }),
2715
+ action && /* @__PURE__ */ jsx26("div", { style: { marginTop: 16 }, children: action })
2502
2716
  ] });
2503
2717
  }
2504
2718
 
2505
2719
  // src/hooks/useLoginForm.ts
2506
- import { useCallback as useCallback5, useMemo as useMemo4, useState as useState7 } from "react";
2507
- import { useHook as useHook8 } from "@hook-sdk/sdk";
2720
+ import { useCallback as useCallback6, useMemo as useMemo4, useState as useState8 } from "react";
2721
+ import { useHook as useHook9 } from "@hook-sdk/sdk";
2508
2722
 
2509
2723
  // src/errors.ts
2510
2724
  import { SdkError, SdkAuthError, SdkRateLimitError } from "@hook-sdk/sdk";
@@ -2539,14 +2753,14 @@ function mapSdkError(err) {
2539
2753
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2540
2754
  var MIN_PASSWORD = 8;
2541
2755
  function useLoginForm() {
2542
- const { auth } = useHook8();
2543
- const [email, setEmail] = useState7("");
2544
- const [password, setPassword] = useState7("");
2545
- const [submitting, setSubmitting] = useState7(false);
2546
- const [error, setError] = useState7(null);
2547
- const [touchedEmail, setTouchedEmail] = useState7(false);
2548
- const [touchedPassword, setTouchedPassword] = useState7(false);
2549
- const [formSubmitAttempted, setFormSubmitAttempted] = useState7(false);
2756
+ const { auth } = useHook9();
2757
+ const [email, setEmail] = useState8("");
2758
+ const [password, setPassword] = useState8("");
2759
+ const [submitting, setSubmitting] = useState8(false);
2760
+ const [error, setError] = useState8(null);
2761
+ const [touchedEmail, setTouchedEmail] = useState8(false);
2762
+ const [touchedPassword, setTouchedPassword] = useState8(false);
2763
+ const [formSubmitAttempted, setFormSubmitAttempted] = useState8(false);
2550
2764
  const validateEmail = useMemo4(() => {
2551
2765
  if (email.length === 0) return null;
2552
2766
  if (!EMAIL_RE.test(email)) return "Formato de e-mail inv\xE1lido.";
@@ -2560,7 +2774,7 @@ function useLoginForm() {
2560
2774
  const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
2561
2775
  const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
2562
2776
  const canSubmit = email.length > 0 && password.length >= MIN_PASSWORD && validateEmail === null && validatePassword === null && !submitting;
2563
- const submit = useCallback5(async () => {
2777
+ const submit = useCallback6(async () => {
2564
2778
  setFormSubmitAttempted(true);
2565
2779
  if (!canSubmit) return false;
2566
2780
  setSubmitting(true);
@@ -2594,21 +2808,21 @@ function useLoginForm() {
2594
2808
  }
2595
2809
 
2596
2810
  // src/hooks/useSignupForm.ts
2597
- import { useCallback as useCallback6, useMemo as useMemo5, useState as useState8 } from "react";
2598
- import { useHook as useHook9 } from "@hook-sdk/sdk";
2811
+ import { useCallback as useCallback7, useMemo as useMemo5, useState as useState9 } from "react";
2812
+ import { useHook as useHook10 } from "@hook-sdk/sdk";
2599
2813
  var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2600
2814
  var MIN_PASSWORD2 = 8;
2601
2815
  function useSignupForm() {
2602
- const { auth } = useHook9();
2603
- const [name, setName] = useState8("");
2604
- const [email, setEmail] = useState8("");
2605
- const [password, setPassword] = useState8("");
2606
- const [submitting, setSubmitting] = useState8(false);
2607
- const [error, setError] = useState8(null);
2608
- const [touchedName, setTouchedName] = useState8(false);
2609
- const [touchedEmail, setTouchedEmail] = useState8(false);
2610
- const [touchedPassword, setTouchedPassword] = useState8(false);
2611
- const [formSubmitAttempted, setFormSubmitAttempted] = useState8(false);
2816
+ const { auth } = useHook10();
2817
+ const [name, setName] = useState9("");
2818
+ const [email, setEmail] = useState9("");
2819
+ const [password, setPassword] = useState9("");
2820
+ const [submitting, setSubmitting] = useState9(false);
2821
+ const [error, setError] = useState9(null);
2822
+ const [touchedName, setTouchedName] = useState9(false);
2823
+ const [touchedEmail, setTouchedEmail] = useState9(false);
2824
+ const [touchedPassword, setTouchedPassword] = useState9(false);
2825
+ const [formSubmitAttempted, setFormSubmitAttempted] = useState9(false);
2612
2826
  const validateName = useMemo5(() => {
2613
2827
  if (name.length === 0) return null;
2614
2828
  if (name.trim().length < 2) return "Nome muito curto.";
@@ -2628,7 +2842,7 @@ function useSignupForm() {
2628
2842
  const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
2629
2843
  const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
2630
2844
  const canSubmit = name.trim().length >= 2 && email.length > 0 && password.length >= MIN_PASSWORD2 && validateName === null && validateEmail === null && validatePassword === null && !submitting;
2631
- const submit = useCallback6(async () => {
2845
+ const submit = useCallback7(async () => {
2632
2846
  setFormSubmitAttempted(true);
2633
2847
  if (!canSubmit) return false;
2634
2848
  setSubmitting(true);
@@ -2666,17 +2880,17 @@ function useSignupForm() {
2666
2880
  }
2667
2881
 
2668
2882
  // src/hooks/useForgotForm.ts
2669
- import { useCallback as useCallback7, useMemo as useMemo6, useState as useState9 } from "react";
2670
- import { useHook as useHook10 } from "@hook-sdk/sdk";
2883
+ import { useCallback as useCallback8, useMemo as useMemo6, useState as useState10 } from "react";
2884
+ import { useHook as useHook11 } from "@hook-sdk/sdk";
2671
2885
  var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2672
2886
  function useForgotForm() {
2673
- const { auth } = useHook10();
2674
- const [email, setEmail] = useState9("");
2675
- const [submitting, setSubmitting] = useState9(false);
2676
- const [sent, setSent] = useState9(false);
2677
- const [error, setError] = useState9(null);
2678
- const [touchedEmail, setTouchedEmail] = useState9(false);
2679
- const [formSubmitAttempted, setFormSubmitAttempted] = useState9(false);
2887
+ const { auth } = useHook11();
2888
+ const [email, setEmail] = useState10("");
2889
+ const [submitting, setSubmitting] = useState10(false);
2890
+ const [sent, setSent] = useState10(false);
2891
+ const [error, setError] = useState10(null);
2892
+ const [touchedEmail, setTouchedEmail] = useState10(false);
2893
+ const [formSubmitAttempted, setFormSubmitAttempted] = useState10(false);
2680
2894
  const validateEmail = useMemo6(() => {
2681
2895
  if (email.length === 0) return null;
2682
2896
  if (!EMAIL_RE3.test(email)) return "Formato de e-mail inv\xE1lido.";
@@ -2684,7 +2898,7 @@ function useForgotForm() {
2684
2898
  }, [email]);
2685
2899
  const emailError = touchedEmail || formSubmitAttempted ? validateEmail : null;
2686
2900
  const canSubmit = email.length > 0 && validateEmail === null && !submitting;
2687
- const submit = useCallback7(async () => {
2901
+ const submit = useCallback8(async () => {
2688
2902
  setFormSubmitAttempted(true);
2689
2903
  if (!canSubmit) return false;
2690
2904
  setSubmitting(true);
@@ -2715,20 +2929,20 @@ function useForgotForm() {
2715
2929
  }
2716
2930
 
2717
2931
  // src/hooks/useResetForm.ts
2718
- import { useCallback as useCallback8, useEffect as useEffect10, useMemo as useMemo7, useState as useState10 } from "react";
2719
- import { useHook as useHook11 } from "@hook-sdk/sdk";
2932
+ import { useCallback as useCallback9, useEffect as useEffect10, useMemo as useMemo7, useState as useState11 } from "react";
2933
+ import { useHook as useHook12 } from "@hook-sdk/sdk";
2720
2934
  var MIN_PASSWORD3 = 12;
2721
2935
  function useResetForm() {
2722
- const { auth } = useHook11();
2723
- const [token, setToken] = useState10(null);
2724
- const [password, setPassword] = useState10("");
2725
- const [confirm, setConfirm] = useState10("");
2726
- const [submitting, setSubmitting] = useState10(false);
2727
- const [done, setDone] = useState10(false);
2728
- const [error, setError] = useState10(null);
2729
- const [touchedPassword, setTouchedPassword] = useState10(false);
2730
- const [touchedConfirm, setTouchedConfirm] = useState10(false);
2731
- const [formSubmitAttempted, setFormSubmitAttempted] = useState10(false);
2936
+ const { auth } = useHook12();
2937
+ const [token, setToken] = useState11(null);
2938
+ const [password, setPassword] = useState11("");
2939
+ const [confirm, setConfirm] = useState11("");
2940
+ const [submitting, setSubmitting] = useState11(false);
2941
+ const [done, setDone] = useState11(false);
2942
+ const [error, setError] = useState11(null);
2943
+ const [touchedPassword, setTouchedPassword] = useState11(false);
2944
+ const [touchedConfirm, setTouchedConfirm] = useState11(false);
2945
+ const [formSubmitAttempted, setFormSubmitAttempted] = useState11(false);
2732
2946
  useEffect10(() => {
2733
2947
  if (typeof window === "undefined") return;
2734
2948
  const params = new URLSearchParams(window.location.search);
@@ -2748,7 +2962,7 @@ function useResetForm() {
2748
2962
  const passwordError = touchedPassword || formSubmitAttempted ? validatePassword : null;
2749
2963
  const confirmError = touchedConfirm || formSubmitAttempted ? validateConfirm : null;
2750
2964
  const canSubmit = token !== null && password.length >= MIN_PASSWORD3 && confirm === password && validatePassword === null && validateConfirm === null && !submitting && !done;
2751
- const submit = useCallback8(async () => {
2965
+ const submit = useCallback9(async () => {
2752
2966
  setFormSubmitAttempted(true);
2753
2967
  if (!canSubmit || token === null) return;
2754
2968
  setSubmitting(true);
@@ -2788,9 +3002,9 @@ function useResetForm() {
2788
3002
  }
2789
3003
 
2790
3004
  // src/hooks/usePlan.ts
2791
- import { useHook as useHook12 } from "@hook-sdk/sdk";
3005
+ import { useHook as useHook13 } from "@hook-sdk/sdk";
2792
3006
  function usePlan() {
2793
- const { plan } = useHook12();
3007
+ const { plan } = useHook13();
2794
3008
  return plan;
2795
3009
  }
2796
3010
 
@@ -2824,10 +3038,10 @@ function discountPercent(anchorCents, realCents) {
2824
3038
 
2825
3039
  // src/hooks/useAuthPrimitives.ts
2826
3040
  import { useEffect as useEffect11 } from "react";
2827
- import { useHook as useHook13 } from "@hook-sdk/sdk";
3041
+ import { useHook as useHook14 } from "@hook-sdk/sdk";
2828
3042
  var warned = false;
2829
3043
  function useAuthPrimitives() {
2830
- const { auth } = useHook13();
3044
+ const { auth } = useHook14();
2831
3045
  useEffect11(() => {
2832
3046
  if (!warned && process.env.NODE_ENV !== "production") {
2833
3047
  warned = true;
@@ -2850,9 +3064,9 @@ function useAuthPrimitives() {
2850
3064
  }
2851
3065
 
2852
3066
  // src/hooks/useAuth.ts
2853
- import { useHook as useHook14 } from "@hook-sdk/sdk";
3067
+ import { useHook as useHook15 } from "@hook-sdk/sdk";
2854
3068
  function useAuth() {
2855
- const { user, authStatus, auth } = useHook14();
3069
+ const { user, authStatus, auth } = useHook15();
2856
3070
  return {
2857
3071
  user,
2858
3072
  authStatus,
@@ -2864,23 +3078,23 @@ function useAuth() {
2864
3078
  import { useTrackOnboardingStep } from "@hook-sdk/sdk";
2865
3079
 
2866
3080
  // src/hooks/useSubscription.ts
2867
- import { useHook as useHook15 } from "@hook-sdk/sdk";
3081
+ import { useHook as useHook16 } from "@hook-sdk/sdk";
2868
3082
  function useSubscription() {
2869
- const { subscription } = useHook15();
3083
+ const { subscription } = useHook16();
2870
3084
  return {
2871
3085
  status: subscription.status()
2872
3086
  };
2873
3087
  }
2874
3088
 
2875
3089
  // src/hooks/useReminders.ts
2876
- import { useCallback as useCallback9, useEffect as useEffect12, useState as useState11 } from "react";
2877
- import { useHook as useHook16 } from "@hook-sdk/sdk";
3090
+ import { useCallback as useCallback10, useEffect as useEffect12, useState as useState12 } from "react";
3091
+ import { useHook as useHook17 } from "@hook-sdk/sdk";
2878
3092
  function useReminders() {
2879
- const { push } = useHook16();
3093
+ const { push } = useHook17();
2880
3094
  const r = push.reminders;
2881
- const [reminders, setReminders] = useState11([]);
2882
- const [loading, setLoading] = useState11(true);
2883
- const reload = useCallback9(async () => {
3095
+ const [reminders, setReminders] = useState12([]);
3096
+ const [loading, setLoading] = useState12(true);
3097
+ const reload = useCallback10(async () => {
2884
3098
  setLoading(true);
2885
3099
  try {
2886
3100
  const next = await r.list();
@@ -2892,35 +3106,35 @@ function useReminders() {
2892
3106
  useEffect12(() => {
2893
3107
  void reload();
2894
3108
  }, [reload]);
2895
- const setReminder = useCallback9(async (input) => {
3109
+ const setReminder = useCallback10(async (input) => {
2896
3110
  await r.set(input);
2897
3111
  await reload();
2898
3112
  }, [r, reload]);
2899
- const deleteReminder = useCallback9(async (slot) => {
3113
+ const deleteReminder = useCallback10(async (slot) => {
2900
3114
  await r.delete(slot);
2901
3115
  await reload();
2902
3116
  }, [r, reload]);
2903
- const schedule = useCallback9(async (items) => {
3117
+ const schedule = useCallback10(async (items) => {
2904
3118
  return r.schedule(items);
2905
3119
  }, [r]);
2906
- const setFallbacks = useCallback9(async (items) => {
3120
+ const setFallbacks = useCallback10(async (items) => {
2907
3121
  return r.setFallbacks(items);
2908
3122
  }, [r]);
2909
3123
  return { reminders, loading, setReminder, deleteReminder, schedule, setFallbacks };
2910
3124
  }
2911
3125
 
2912
3126
  // src/hooks/useToast.ts
2913
- import { useCallback as useCallback10, useState as useState12 } from "react";
3127
+ import { useCallback as useCallback11, useState as useState13 } from "react";
2914
3128
  function useToast() {
2915
- const [items, setItems] = useState12([]);
2916
- const show = useCallback10((message, kind = "info") => {
3129
+ const [items, setItems] = useState13([]);
3130
+ const show = useCallback11((message, kind = "info") => {
2917
3131
  const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
2918
3132
  setItems((prev) => [...prev, { id, message, kind }]);
2919
3133
  setTimeout(() => {
2920
3134
  setItems((prev) => prev.filter((t) => t.id !== id));
2921
3135
  }, 4e3);
2922
3136
  }, []);
2923
- const dismiss = useCallback10((id) => {
3137
+ const dismiss = useCallback11((id) => {
2924
3138
  setItems((prev) => prev.filter((t) => t.id !== id));
2925
3139
  }, []);
2926
3140
  return { items, show, dismiss };
@@ -2928,20 +3142,20 @@ function useToast() {
2928
3142
 
2929
3143
  // src/RouteBoundary.tsx
2930
3144
  import { Routes as Routes2, Route as Route2 } from "react-router-dom";
2931
- import { jsx as jsx26, jsxs as jsxs18 } from "react/jsx-runtime";
3145
+ import { jsx as jsx27, jsxs as jsxs18 } from "react/jsx-runtime";
2932
3146
  function RouteBoundary({ children }) {
2933
3147
  return /* @__PURE__ */ jsxs18(Routes2, { children: [
2934
3148
  children,
2935
- /* @__PURE__ */ jsx26(Route2, { path: "*", element: /* @__PURE__ */ jsx26(DefaultNotFound, {}) })
3149
+ /* @__PURE__ */ jsx27(Route2, { path: "*", element: /* @__PURE__ */ jsx27(DefaultNotFound, {}) })
2936
3150
  ] });
2937
3151
  }
2938
3152
  function DefaultNotFound() {
2939
- return /* @__PURE__ */ jsx26("div", { role: "alert", children: "P\xE1gina n\xE3o encontrada" });
3153
+ return /* @__PURE__ */ jsx27("div", { role: "alert", children: "P\xE1gina n\xE3o encontrada" });
2940
3154
  }
2941
3155
 
2942
3156
  // src/PreAuthShell.tsx
2943
3157
  import { BrowserRouter as BrowserRouter2, MemoryRouter as MemoryRouter2, Routes as Routes3 } from "react-router-dom";
2944
- import { jsx as jsx27 } from "react/jsx-runtime";
3158
+ import { jsx as jsx28 } from "react/jsx-runtime";
2945
3159
  function PreAuthShell({
2946
3160
  basename,
2947
3161
  testRouter,
@@ -2949,14 +3163,14 @@ function PreAuthShell({
2949
3163
  children
2950
3164
  }) {
2951
3165
  if (testRouter === "memory") {
2952
- return /* @__PURE__ */ jsx27(MemoryRouter2, { basename, initialEntries: testInitialEntries, children: /* @__PURE__ */ jsx27(Routes3, { children }) });
3166
+ return /* @__PURE__ */ jsx28(MemoryRouter2, { basename, initialEntries: testInitialEntries, children: /* @__PURE__ */ jsx28(Routes3, { children }) });
2953
3167
  }
2954
- return /* @__PURE__ */ jsx27(BrowserRouter2, { basename, children: /* @__PURE__ */ jsx27(Routes3, { children }) });
3168
+ return /* @__PURE__ */ jsx28(BrowserRouter2, { basename, children: /* @__PURE__ */ jsx28(Routes3, { children }) });
2955
3169
  }
2956
3170
 
2957
3171
  // src/OnboardingFlow.tsx
2958
- import { useCallback as useCallback11, useEffect as useEffect13, useMemo as useMemo8, useRef as useRef5 } from "react";
2959
- import { usePersistedState as usePersistedState3, useHook as useHook17 } from "@hook-sdk/sdk";
3172
+ import { useCallback as useCallback12, useEffect as useEffect13, useMemo as useMemo8, useRef as useRef6 } from "react";
3173
+ import { usePersistedState as usePersistedState3, useHook as useHook18 } from "@hook-sdk/sdk";
2960
3174
 
2961
3175
  // src/hooks/useOnboardingStep.ts
2962
3176
  import { createContext as createContext3, useContext as useContext4 } from "react";
@@ -2972,7 +3186,7 @@ function useOnboardingStep() {
2972
3186
  }
2973
3187
 
2974
3188
  // src/OnboardingFlow.tsx
2975
- import { jsx as jsx28 } from "react/jsx-runtime";
3189
+ import { jsx as jsx29 } from "react/jsx-runtime";
2976
3190
  var isFilled = (v) => v != null && v !== "";
2977
3191
  var CURRENT_STEP_FIELD = "currentStep";
2978
3192
  function readPersistedStepIdx(draft) {
@@ -2986,11 +3200,11 @@ function OnboardingFlow({
2986
3200
  persistKey
2987
3201
  }) {
2988
3202
  const [draft, setDraft, status] = usePersistedState3(persistKey, {});
2989
- const draftRef = useRef5(draft);
3203
+ const draftRef = useRef6(draft);
2990
3204
  draftRef.current = draft;
2991
3205
  const idx = readPersistedStepIdx(draft);
2992
3206
  const clampedIdx = Math.min(Math.max(idx, 0), Math.max(steps.length - 1, 0));
2993
- const setIdx = useCallback11(
3207
+ const setIdx = useCallback12(
2994
3208
  (n) => {
2995
3209
  setDraft((prev) => {
2996
3210
  const prevIdx = readPersistedStepIdx(prev);
@@ -3000,7 +3214,7 @@ function OnboardingFlow({
3000
3214
  },
3001
3215
  [setDraft]
3002
3216
  );
3003
- const setValue = useCallback11(
3217
+ const setValue = useCallback12(
3004
3218
  (patch) => {
3005
3219
  draftRef.current = { ...draftRef.current, ...patch };
3006
3220
  setDraft((prev) => ({ ...prev, ...patch }));
@@ -3008,7 +3222,7 @@ function OnboardingFlow({
3008
3222
  [setDraft]
3009
3223
  );
3010
3224
  const step = steps[clampedIdx];
3011
- const hookCtx = useHook17();
3225
+ const hookCtx = useHook18();
3012
3226
  const track2 = typeof hookCtx.track === "function" ? hookCtx.track : void 0;
3013
3227
  useEffect13(() => {
3014
3228
  if (status.loading) return;
@@ -3024,7 +3238,7 @@ function OnboardingFlow({
3024
3238
  () => step ? (step.validates ?? []).every((field) => isFilled(draft[field])) : false,
3025
3239
  [draft, step]
3026
3240
  );
3027
- const next = useCallback11(() => {
3241
+ const next = useCallback12(() => {
3028
3242
  if (!step) return;
3029
3243
  const current = draftRef.current;
3030
3244
  const validNow = (step.validates ?? []).every((field) => isFilled(current[field]));
@@ -3035,7 +3249,7 @@ function OnboardingFlow({
3035
3249
  setIdx(clampedIdx + 1);
3036
3250
  }
3037
3251
  }, [clampedIdx, onComplete, step, steps.length, setIdx]);
3038
- const prevStep = useCallback11(() => setIdx((i) => Math.max(0, i - 1)), [setIdx]);
3252
+ const prevStep = useCallback12(() => setIdx((i) => Math.max(0, i - 1)), [setIdx]);
3039
3253
  const ctx = useMemo8(
3040
3254
  () => ({
3041
3255
  stepIndex: clampedIdx,
@@ -3062,7 +3276,7 @@ function OnboardingFlow({
3062
3276
  `[hook-template] OnboardingFlow: missing screen component for step '${step.id}' (expected key '${step.screen}' in screens prop)`
3063
3277
  );
3064
3278
  }
3065
- return /* @__PURE__ */ jsx28(OnboardingStepContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx28(Screen, {}) });
3279
+ return /* @__PURE__ */ jsx29(OnboardingStepContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx29(Screen, {}) });
3066
3280
  }
3067
3281
 
3068
3282
  // src/hooks/useFeature.ts
@@ -3070,11 +3284,298 @@ function useFeature(name) {
3070
3284
  const config = useAppConfig();
3071
3285
  return Array.isArray(config.features_enabled) && config.features_enabled.includes(name);
3072
3286
  }
3287
+
3288
+ // src/components/paywall/Paywall.tsx
3289
+ import { useEffect as useEffect14, useMemo as useMemo9 } from "react";
3290
+ import { useHook as useHook19 } from "@hook-sdk/sdk";
3291
+
3292
+ // src/components/paywall/PaywallMethodTabs.tsx
3293
+ import { jsx as jsx30 } from "react/jsx-runtime";
3294
+ function PaywallMethodTabs({
3295
+ methods,
3296
+ selected,
3297
+ onSelect,
3298
+ labels,
3299
+ className,
3300
+ tabClassName,
3301
+ tabActiveClassName
3302
+ }) {
3303
+ if (methods.length < 2) return null;
3304
+ return /* @__PURE__ */ jsx30("div", { role: "tablist", "aria-label": "M\xE9todo de pagamento", className, children: methods.map((m) => {
3305
+ const active = m === selected;
3306
+ const label = labels[m] ?? m;
3307
+ return /* @__PURE__ */ jsx30(
3308
+ "button",
3309
+ {
3310
+ type: "button",
3311
+ role: "tab",
3312
+ "aria-selected": active,
3313
+ "aria-controls": `paywall-tab-${m}`,
3314
+ tabIndex: active ? 0 : -1,
3315
+ onClick: () => onSelect(m),
3316
+ className: [tabClassName, active ? tabActiveClassName : ""].filter(Boolean).join(" "),
3317
+ children: label
3318
+ },
3319
+ m
3320
+ );
3321
+ }) });
3322
+ }
3323
+
3324
+ // src/components/paywall/PaywallMethodContent.tsx
3325
+ import { jsx as jsx31 } from "react/jsx-runtime";
3326
+ function PaywallMethodContent({
3327
+ method,
3328
+ copy,
3329
+ hasConsumedTrial = false,
3330
+ className,
3331
+ rowClassName
3332
+ }) {
3333
+ const useCardConsumed = method === "card" && hasConsumedTrial && copy.cardConsumedTrial;
3334
+ const rows = useCardConsumed ? copy.cardConsumedTrial.bodyRows : method === "pix-auto" || method === "pix-once" ? copy.pix.bodyRows : copy.card.bodyRows;
3335
+ return /* @__PURE__ */ jsx31("div", { role: "tabpanel", id: `paywall-tab-${method}`, className, children: rows.map((row, i) => /* @__PURE__ */ jsx31("div", { className: rowClassName, children: row }, i)) });
3336
+ }
3337
+
3338
+ // src/components/paywall/PaywallCyclePicker.tsx
3339
+ import { jsx as jsx32, jsxs as jsxs19 } from "react/jsx-runtime";
3340
+ function PaywallCyclePicker({
3341
+ cycles,
3342
+ selected,
3343
+ onSelect,
3344
+ priceCentsByCycle,
3345
+ anchorCentsByCycle,
3346
+ monthlyEquivByCycle,
3347
+ labels,
3348
+ className,
3349
+ cardClassName,
3350
+ cardSelectedClassName,
3351
+ anchorClassName
3352
+ }) {
3353
+ if (cycles.length < 2) return null;
3354
+ return /* @__PURE__ */ jsx32("div", { role: "radiogroup", "aria-label": "Ciclo de cobran\xE7a", className, children: cycles.map((c) => {
3355
+ const active = c === selected;
3356
+ const label = c === "YEARLY" ? labels.annualLabel : labels.monthlyLabel;
3357
+ const suffix = c === "YEARLY" ? labels.annualSuffix : labels.monthlySuffix;
3358
+ const mainCents = c === "YEARLY" ? monthlyEquivByCycle[c] : priceCentsByCycle[c];
3359
+ const anchorCents = anchorCentsByCycle[c];
3360
+ return /* @__PURE__ */ jsxs19(
3361
+ "button",
3362
+ {
3363
+ type: "button",
3364
+ role: "radio",
3365
+ "aria-checked": active,
3366
+ onClick: () => onSelect(c),
3367
+ className: [cardClassName, active ? cardSelectedClassName : ""].filter(Boolean).join(" "),
3368
+ children: [
3369
+ /* @__PURE__ */ jsx32("strong", { children: formatBRL(mainCents) }),
3370
+ /* @__PURE__ */ jsx32("span", { children: suffix }),
3371
+ /* @__PURE__ */ jsx32("span", { children: label }),
3372
+ anchorCents != null && anchorCents > mainCents ? /* @__PURE__ */ jsx32("span", { className: anchorClassName, children: /* @__PURE__ */ jsx32("s", { children: formatBRL(anchorCents) }) }) : null
3373
+ ]
3374
+ },
3375
+ c
3376
+ );
3377
+ }) });
3378
+ }
3379
+
3380
+ // src/components/paywall/PaywallCta.tsx
3381
+ import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
3382
+ function PaywallCta({
3383
+ ctaLabel,
3384
+ loadingLabel,
3385
+ switchHint,
3386
+ trustLine,
3387
+ onClick,
3388
+ disabled = false,
3389
+ loading = false,
3390
+ className,
3391
+ buttonClassName,
3392
+ switchHintClassName,
3393
+ trustClassName
3394
+ }) {
3395
+ const label = loading && loadingLabel ? loadingLabel : ctaLabel;
3396
+ return /* @__PURE__ */ jsxs20("div", { className, children: [
3397
+ /* @__PURE__ */ jsx33(
3398
+ "button",
3399
+ {
3400
+ type: "button",
3401
+ onClick,
3402
+ disabled: disabled || loading,
3403
+ className: buttonClassName,
3404
+ children: label
3405
+ }
3406
+ ),
3407
+ switchHint ? /* @__PURE__ */ jsx33("p", { className: switchHintClassName, children: switchHint }) : null,
3408
+ /* @__PURE__ */ jsx33("p", { className: trustClassName, children: trustLine })
3409
+ ] });
3410
+ }
3411
+
3412
+ // src/components/paywall/Paywall.tsx
3413
+ import { jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
3414
+ var NBSP = "\xA0";
3415
+ function Paywall({
3416
+ copy,
3417
+ themeClasses = {},
3418
+ slots = {},
3419
+ onBeforeCheckout
3420
+ }) {
3421
+ const { track: track2 } = useHook19();
3422
+ const s = usePaywallState();
3423
+ const priceLabel = formatBRL(s.currentPriceCents).replace(new RegExp(NBSP, "g"), " ");
3424
+ const monthlyEquivLabel = formatBRL(s.currentMonthlyEquivCents).replace(new RegExp(NBSP, "g"), " ");
3425
+ const trialDaysCardLabel = String(s.trialDaysCard);
3426
+ const ctaLabel = useMemo9(() => {
3427
+ if (s.isFree) return copy.freeCta ?? "Come\xE7ar agora";
3428
+ if (s.selectedMethod === "card") {
3429
+ if (s.hasConsumedTrial && copy.cardConsumedTrial) {
3430
+ return interp(copy.cardConsumedTrial.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
3431
+ }
3432
+ if (s.trialDaysCard > 0) {
3433
+ return interp(copy.card.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
3434
+ }
3435
+ return copy.cardConsumedTrial ? interp(copy.cardConsumedTrial.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel }) : `Assinar por ${priceLabel}`;
3436
+ }
3437
+ return interp(copy.pix.ctaTemplate, { price: priceLabel, days: trialDaysCardLabel });
3438
+ }, [
3439
+ s.isFree,
3440
+ s.selectedMethod,
3441
+ s.hasConsumedTrial,
3442
+ s.trialDaysCard,
3443
+ copy,
3444
+ priceLabel,
3445
+ trialDaysCardLabel
3446
+ ]);
3447
+ const switchHint = useMemo9(() => {
3448
+ if (s.methods.length < 2) return void 0;
3449
+ return s.selectedMethod === "card" ? copy.card.switchHint : copy.pix.switchHint;
3450
+ }, [s.methods.length, s.selectedMethod, copy]);
3451
+ useEffect14(() => {
3452
+ if (!s.initialLoadComplete) return;
3453
+ track2("paywall_view", {
3454
+ default_method: s.selectedMethod,
3455
+ default_cycle: s.cycle,
3456
+ available_methods: s.methods
3457
+ });
3458
+ }, [s.initialLoadComplete]);
3459
+ const handleCta = async () => {
3460
+ track2("paywall_cta_clicked", {
3461
+ method: s.selectedMethod,
3462
+ cycle: s.cycle,
3463
+ price_cents: s.currentPriceCents,
3464
+ had_consumed_trial: s.hasConsumedTrial
3465
+ });
3466
+ if (onBeforeCheckout) {
3467
+ await onBeforeCheckout(s.selectedMethod, s.cycle);
3468
+ return;
3469
+ }
3470
+ await s.submit();
3471
+ };
3472
+ const ctaTheme = s.selectedMethod === "card" ? themeClasses.ctaCard : themeClasses.ctaPix;
3473
+ return /* @__PURE__ */ jsxs21("div", { className: themeClasses.container, children: [
3474
+ slots.heroSlot,
3475
+ /* @__PURE__ */ jsx34("h1", { className: themeClasses.headline, children: copy.headline }),
3476
+ /* @__PURE__ */ jsx34("ul", { children: copy.features.map((f) => /* @__PURE__ */ jsxs21("li", { className: themeClasses.feature, children: [
3477
+ "\u2713 ",
3478
+ /* @__PURE__ */ jsx34("span", { children: f })
3479
+ ] }, f)) }),
3480
+ copy.socialProof ? /* @__PURE__ */ jsx34("p", { className: themeClasses.socialProof, children: copy.socialProof }) : null,
3481
+ slots.cyclePickerSlot ?? /* @__PURE__ */ jsx34(
3482
+ PaywallCyclePicker,
3483
+ {
3484
+ cycles: ["MONTHLY", "YEARLY"],
3485
+ selected: s.cycle,
3486
+ onSelect: s.selectCycle,
3487
+ priceCentsByCycle: {
3488
+ MONTHLY: priceCentsForCycle(s, "MONTHLY"),
3489
+ YEARLY: priceCentsForCycle(s, "YEARLY")
3490
+ },
3491
+ anchorCentsByCycle: {
3492
+ MONTHLY: anchorForCycle(s, "MONTHLY"),
3493
+ YEARLY: anchorForCycle(s, "YEARLY")
3494
+ },
3495
+ monthlyEquivByCycle: {
3496
+ MONTHLY: priceCentsForCycle(s, "MONTHLY"),
3497
+ YEARLY: Math.round(priceCentsForCycle(s, "YEARLY") / 12)
3498
+ },
3499
+ labels: copy.cycle,
3500
+ cardClassName: themeClasses.cycleCard,
3501
+ cardSelectedClassName: themeClasses.cycleCardSelected,
3502
+ anchorClassName: themeClasses.anchorPrice
3503
+ }
3504
+ ),
3505
+ /* @__PURE__ */ jsx34(
3506
+ PaywallMethodTabs,
3507
+ {
3508
+ methods: s.methods,
3509
+ selected: s.selectedMethod,
3510
+ onSelect: s.selectMethod,
3511
+ labels: { "pix-auto": copy.pix.tabLabel, card: copy.card.tabLabel },
3512
+ className: themeClasses.tabs,
3513
+ tabClassName: themeClasses.tab,
3514
+ tabActiveClassName: themeClasses.tabActive
3515
+ }
3516
+ ),
3517
+ /* @__PURE__ */ jsx34(
3518
+ PaywallMethodContent,
3519
+ {
3520
+ method: s.selectedMethod,
3521
+ copy: {
3522
+ pix: interpolateCopy(copy.pix, priceLabel, trialDaysCardLabel),
3523
+ card: interpolateCopy(copy.card, priceLabel, trialDaysCardLabel),
3524
+ cardConsumedTrial: copy.cardConsumedTrial ? {
3525
+ bodyRows: copy.cardConsumedTrial.bodyRows.map(
3526
+ (r) => interp(r, { price: priceLabel, days: trialDaysCardLabel })
3527
+ ),
3528
+ ctaTemplate: copy.cardConsumedTrial.ctaTemplate
3529
+ } : void 0
3530
+ },
3531
+ hasConsumedTrial: s.hasConsumedTrial,
3532
+ className: themeClasses.tabContent,
3533
+ rowClassName: themeClasses.tabContentRow
3534
+ }
3535
+ ),
3536
+ slots.beforeCtaSlot,
3537
+ /* @__PURE__ */ jsx34(
3538
+ PaywallCta,
3539
+ {
3540
+ ctaLabel,
3541
+ loadingLabel: "Abrindo checkout\u2026",
3542
+ switchHint,
3543
+ trustLine: copy.trustLine,
3544
+ onClick: handleCta,
3545
+ disabled: !s.initialLoadComplete,
3546
+ loading: s.submitting,
3547
+ buttonClassName: ctaTheme,
3548
+ switchHintClassName: themeClasses.switchHint,
3549
+ trustClassName: themeClasses.trustLine
3550
+ }
3551
+ )
3552
+ ] });
3553
+ }
3554
+ function interp(tpl, vars) {
3555
+ return tpl.replace(/\{(\w+)\}/g, (_m, k) => vars[k] ?? "");
3556
+ }
3557
+ function interpolateCopy(m, price, days) {
3558
+ return {
3559
+ tabLabel: m.tabLabel,
3560
+ bodyRows: m.bodyRows.map((r) => interp(r, { price, days })),
3561
+ ctaTemplate: m.ctaTemplate,
3562
+ switchHint: m.switchHint
3563
+ };
3564
+ }
3565
+ function priceCentsForCycle(s, c) {
3566
+ return s.plan ? c === "YEARLY" ? s.plan.yearlyCents ?? 0 : s.plan.monthlyCents : 0;
3567
+ }
3568
+ function anchorForCycle(s, c) {
3569
+ if (!s.plan) return null;
3570
+ if (c === "YEARLY") return s.plan.anchorYearlyCents ?? null;
3571
+ return s.plan.anchorMonthlyCents ?? null;
3572
+ }
3073
3573
  export {
3074
3574
  AppConfigProvider,
3075
3575
  AppConfigSchema,
3076
3576
  AppRoot,
3077
3577
  DeepLinkHandler,
3578
+ DevSkipOnboardingFab,
3078
3579
  EmptyState,
3079
3580
  ErrorBoundary,
3080
3581
  I18nProvider,
@@ -3084,6 +3585,11 @@ export {
3084
3585
  LoadingState,
3085
3586
  OnboardingFlow,
3086
3587
  PaymentReturnHandler,
3588
+ Paywall,
3589
+ PaywallCta,
3590
+ PaywallCyclePicker,
3591
+ PaywallMethodContent,
3592
+ PaywallMethodTabs,
3087
3593
  PersistenceRegistry,
3088
3594
  PreAuthShell,
3089
3595
  PushPrompt2 as PushPrompt,
@@ -3098,10 +3604,12 @@ export {
3098
3604
  detectStandalone,
3099
3605
  discountPercent,
3100
3606
  formatBRL,
3607
+ isDevToolsEnabled,
3101
3608
  monthlyFromYearly,
3102
3609
  parseAppConfig,
3103
3610
  shouldBlockInstall,
3104
3611
  shouldShowPermanentOption,
3612
+ skipOnboarding,
3105
3613
  useAppConfig,
3106
3614
  useAuth,
3107
3615
  useAuthPrimitives,