@neowhale/storefront 0.2.35 → 0.2.37

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.
@@ -2133,7 +2133,18 @@ function useReferral() {
2133
2133
  referredBy: status?.referred_by ?? null
2134
2134
  };
2135
2135
  }
2136
- var NUM_PATTERN = /(\$?[\d,]+\.?\d*[+★%]?)/g;
2136
+ function trackClick(tracking, label, url, position) {
2137
+ if (!tracking?.gatewayUrl || !tracking?.code) return;
2138
+ const body = JSON.stringify({ label, url, position });
2139
+ if (typeof navigator !== "undefined" && navigator.sendBeacon) {
2140
+ navigator.sendBeacon(
2141
+ `${tracking.gatewayUrl}/q/${encodeURIComponent(tracking.code)}/click`,
2142
+ new Blob([body], { type: "application/json" })
2143
+ );
2144
+ }
2145
+ }
2146
+ var NUM_SPLIT = /(\$?[\d,]+\.?\d*[+★%]?)/g;
2147
+ var NUM_TEST = /^\$?[\d,]+\.?\d*[+★%]?$/;
2137
2148
  function easeOutQuart(t) {
2138
2149
  return 1 - Math.pow(1 - t, 4);
2139
2150
  }
@@ -2190,147 +2201,69 @@ function AnimatedNumber({ raw }) {
2190
2201
  ] });
2191
2202
  }
2192
2203
  function AnimatedText({ text }) {
2193
- const parts = text.split(NUM_PATTERN);
2204
+ const parts = text.split(NUM_SPLIT);
2194
2205
  return /* @__PURE__ */ jsx(Fragment, { children: parts.map(
2195
- (part, i) => NUM_PATTERN.test(part) ? /* @__PURE__ */ jsx(AnimatedNumber, { raw: part }, i) : part
2206
+ (part, i) => NUM_TEST.test(part) ? /* @__PURE__ */ jsx(AnimatedNumber, { raw: part }, i) : part
2196
2207
  ) });
2197
2208
  }
2198
- function trackClick(tracking, label, url, position) {
2199
- if (!tracking?.gatewayUrl || !tracking?.code) return;
2200
- const body = JSON.stringify({ label, url, position });
2201
- if (typeof navigator !== "undefined" && navigator.sendBeacon) {
2202
- navigator.sendBeacon(
2203
- `${tracking.gatewayUrl}/q/${encodeURIComponent(tracking.code)}/click`,
2204
- new Blob([body], { type: "application/json" })
2205
- );
2206
- }
2207
- }
2208
- function SectionRenderer({
2209
- section,
2210
- data,
2211
- theme,
2212
- tracking,
2213
- onEvent
2214
- }) {
2215
- const [showCOA, setShowCOA] = useState(false);
2216
- const el = (() => {
2217
- switch (section.type) {
2218
- case "hero":
2219
- return /* @__PURE__ */ jsx(HeroSection, { section, theme, tracking, onEvent });
2220
- case "text":
2221
- return /* @__PURE__ */ jsx(TextSection, { section, theme });
2222
- case "image":
2223
- return /* @__PURE__ */ jsx(ImageSection, { section, theme });
2224
- case "video":
2225
- return /* @__PURE__ */ jsx(VideoSection, { section, theme });
2226
- case "gallery":
2227
- return /* @__PURE__ */ jsx(GallerySection, { section, theme });
2228
- case "cta":
2229
- return /* @__PURE__ */ jsx(CTASection, { section, theme, tracking, onEvent });
2230
- case "stats":
2231
- return /* @__PURE__ */ jsx(StatsSection, { section, theme });
2232
- case "product_card":
2233
- return /* @__PURE__ */ jsx(ProductCardSection, { section, data, theme, tracking });
2234
- case "coa_viewer":
2235
- return /* @__PURE__ */ jsx(COAViewerSection, { section, data, theme, onShowCOA: () => setShowCOA(true), tracking });
2236
- case "social_links":
2237
- return /* @__PURE__ */ jsx(SocialLinksSection, { section, theme });
2238
- case "lead_capture":
2239
- return /* @__PURE__ */ jsx(LeadCaptureSection, { section, data, theme, onEvent });
2240
- case "divider":
2241
- return /* @__PURE__ */ jsx(DividerSection, { theme });
2242
- default:
2243
- return null;
2244
- }
2245
- })();
2246
- const sectionRef = useRef(null);
2247
- useEffect(() => {
2248
- const el2 = sectionRef.current;
2249
- if (!el2 || typeof IntersectionObserver === "undefined") return;
2250
- const obs = new IntersectionObserver(
2251
- ([entry]) => {
2252
- if (entry.isIntersecting) {
2253
- onEvent?.("section_view", {
2254
- section_id: section.id,
2255
- section_type: section.type
2256
- });
2257
- obs.disconnect();
2258
- }
2259
- },
2260
- { threshold: 0.5 }
2261
- );
2262
- obs.observe(el2);
2263
- return () => obs.disconnect();
2264
- }, [section.id, section.type, onEvent]);
2265
- return /* @__PURE__ */ jsxs("div", { ref: sectionRef, "data-section-id": section.id, "data-section-type": section.type, children: [
2266
- el,
2267
- showCOA && data?.coa && /* @__PURE__ */ jsx(COAModal, { coa: data.coa, theme, onClose: () => setShowCOA(false) })
2268
- ] });
2269
- }
2270
2209
  function HeroSection({ section, theme, tracking, onEvent }) {
2271
2210
  const { title, subtitle, background_image, cta_text, cta_url } = section.content;
2272
- return /* @__PURE__ */ jsxs(
2273
- "div",
2274
- {
2275
- style: {
2276
- position: "relative",
2277
- minHeight: "60vh",
2278
- display: "flex",
2279
- flexDirection: "column",
2280
- justifyContent: "center",
2281
- alignItems: "center",
2282
- textAlign: "center",
2283
- padding: "3rem 1.5rem",
2284
- backgroundImage: background_image ? `url(${background_image})` : void 0,
2285
- backgroundSize: "cover",
2286
- backgroundPosition: "center"
2287
- },
2288
- children: [
2289
- background_image && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" } }),
2290
- /* @__PURE__ */ jsxs("div", { style: { position: "relative", zIndex: 1, maxWidth: 640 }, children: [
2291
- title && /* @__PURE__ */ jsx("h1", { style: {
2292
- fontSize: "clamp(2rem, 8vw, 3rem)",
2293
- fontWeight: 300,
2294
- fontFamily: theme.fontDisplay || "inherit",
2295
- margin: "0 0 1rem",
2296
- lineHeight: 1.15,
2297
- letterSpacing: "-0.02em",
2298
- color: theme.fg
2299
- }, children: /* @__PURE__ */ jsx(AnimatedText, { text: title }) }),
2300
- subtitle && /* @__PURE__ */ jsx("p", { style: {
2211
+ return /* @__PURE__ */ jsxs("div", { style: {
2212
+ position: "relative",
2213
+ minHeight: "60vh",
2214
+ display: "flex",
2215
+ flexDirection: "column",
2216
+ justifyContent: "center",
2217
+ alignItems: "center",
2218
+ textAlign: "center",
2219
+ padding: "3rem 1.5rem",
2220
+ backgroundImage: background_image ? `url(${background_image})` : void 0,
2221
+ backgroundSize: "cover",
2222
+ backgroundPosition: "center"
2223
+ }, children: [
2224
+ background_image && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" } }),
2225
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative", zIndex: 1, maxWidth: 640 }, children: [
2226
+ title && /* @__PURE__ */ jsx("h1", { style: {
2227
+ fontSize: "clamp(2rem, 8vw, 3rem)",
2228
+ fontWeight: 300,
2229
+ fontFamily: theme.fontDisplay || "inherit",
2230
+ margin: "0 0 1rem",
2231
+ lineHeight: 1.15,
2232
+ letterSpacing: "-0.02em",
2233
+ color: theme.fg
2234
+ }, children: /* @__PURE__ */ jsx(AnimatedText, { text: title }) }),
2235
+ subtitle && /* @__PURE__ */ jsx("p", { style: {
2236
+ fontSize: "0.85rem",
2237
+ color: theme.accent,
2238
+ margin: "0 0 2rem",
2239
+ lineHeight: 1.6,
2240
+ textTransform: "uppercase",
2241
+ letterSpacing: "0.15em"
2242
+ }, children: subtitle }),
2243
+ cta_text && cta_url && /* @__PURE__ */ jsx(
2244
+ "a",
2245
+ {
2246
+ href: cta_url,
2247
+ onClick: () => {
2248
+ trackClick(tracking, cta_text, cta_url);
2249
+ onEvent?.("cta_click", { label: cta_text, url: cta_url });
2250
+ },
2251
+ style: {
2252
+ display: "inline-block",
2253
+ padding: "0.875rem 2rem",
2254
+ background: theme.fg,
2255
+ color: theme.bg,
2256
+ textDecoration: "none",
2301
2257
  fontSize: "0.85rem",
2302
- color: theme.accent,
2303
- margin: "0 0 2rem",
2304
- lineHeight: 1.6,
2305
- textTransform: "uppercase",
2306
- letterSpacing: "0.15em"
2307
- }, children: subtitle }),
2308
- cta_text && cta_url && /* @__PURE__ */ jsx(
2309
- "a",
2310
- {
2311
- href: cta_url,
2312
- onClick: () => {
2313
- trackClick(tracking, cta_text, cta_url);
2314
- onEvent?.("cta_click", { label: cta_text, url: cta_url });
2315
- },
2316
- style: {
2317
- display: "inline-block",
2318
- padding: "0.875rem 2rem",
2319
- background: theme.fg,
2320
- color: theme.bg,
2321
- textDecoration: "none",
2322
- fontSize: "0.85rem",
2323
- fontWeight: 500,
2324
- letterSpacing: "0.08em",
2325
- textTransform: "uppercase"
2326
- },
2327
- children: cta_text
2328
- }
2329
- )
2330
- ] })
2331
- ]
2332
- }
2333
- );
2258
+ fontWeight: 500,
2259
+ letterSpacing: "0.08em",
2260
+ textTransform: "uppercase"
2261
+ },
2262
+ children: cta_text
2263
+ }
2264
+ )
2265
+ ] })
2266
+ ] });
2334
2267
  }
2335
2268
  function TextSection({ section, theme }) {
2336
2269
  const { heading, body } = section.content;
@@ -2352,14 +2285,7 @@ function ImageSection({ section, theme }) {
2352
2285
  const contained = section.config?.contained !== false;
2353
2286
  if (!url) return null;
2354
2287
  return /* @__PURE__ */ jsxs("div", { style: { padding: contained ? "1.5rem" : 0, maxWidth: contained ? 640 : void 0, margin: contained ? "0 auto" : void 0 }, children: [
2355
- /* @__PURE__ */ jsx(
2356
- "img",
2357
- {
2358
- src: url,
2359
- alt: alt || "",
2360
- style: { width: "100%", display: "block", objectFit: "cover" }
2361
- }
2362
- ),
2288
+ /* @__PURE__ */ jsx("img", { src: url, alt: alt || "", style: { width: "100%", display: "block", objectFit: "cover" } }),
2363
2289
  caption && /* @__PURE__ */ jsx("p", { style: { fontSize: "0.8rem", color: theme.muted, textAlign: "center", marginTop: "0.75rem" }, children: caption })
2364
2290
  ] });
2365
2291
  }
@@ -2367,36 +2293,38 @@ function VideoSection({ section, theme }) {
2367
2293
  const { url, poster } = section.content;
2368
2294
  if (!url) return null;
2369
2295
  const isEmbed = url.includes("youtube") || url.includes("youtu.be") || url.includes("vimeo");
2370
- return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 640, margin: "0 auto" }, children: isEmbed ? /* @__PURE__ */ jsx("div", { style: { position: "relative", paddingBottom: "56.25%", height: 0 }, children: /* @__PURE__ */ jsx(
2371
- "iframe",
2372
- {
2373
- src: toEmbedUrl(url),
2374
- style: { position: "absolute", top: 0, left: 0, width: "100%", height: "100%", border: "none" },
2375
- allow: "autoplay; fullscreen",
2376
- title: "Video"
2377
- }
2378
- ) }) : /* @__PURE__ */ jsx(
2379
- "video",
2380
- {
2381
- src: url,
2382
- poster,
2383
- controls: true,
2384
- style: { width: "100%", display: "block", background: theme.surface }
2385
- }
2386
- ) });
2296
+ return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 640, margin: "0 auto" }, children: isEmbed ? /* @__PURE__ */ jsx("div", { style: { position: "relative", paddingBottom: "56.25%", height: 0 }, children: /* @__PURE__ */ jsx("iframe", { src: toEmbedUrl(url), style: { position: "absolute", top: 0, left: 0, width: "100%", height: "100%", border: "none" }, allow: "autoplay; fullscreen", title: "Video" }) }) : /* @__PURE__ */ jsx("video", { src: url, poster, controls: true, style: { width: "100%", display: "block", background: theme.surface } }) });
2387
2297
  }
2388
2298
  function GallerySection({ section, theme }) {
2389
2299
  const { images } = section.content;
2390
2300
  const columns = section.config?.columns || 3;
2391
2301
  if (!images || images.length === 0) return null;
2392
- return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 800, margin: "0 auto" }, children: /* @__PURE__ */ jsx("div", { style: { display: "grid", gridTemplateColumns: `repeat(${columns}, 1fr)`, gap: "0.5rem" }, children: images.map((img, i) => /* @__PURE__ */ jsx("div", { style: { aspectRatio: "1", overflow: "hidden", background: theme.surface }, children: /* @__PURE__ */ jsx(
2393
- "img",
2302
+ return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 800, margin: "0 auto" }, children: /* @__PURE__ */ jsx("div", { style: { display: "grid", gridTemplateColumns: `repeat(${columns}, 1fr)`, gap: "0.5rem" }, children: images.map((img, i) => /* @__PURE__ */ jsx("div", { style: { aspectRatio: "1", overflow: "hidden", background: theme.surface }, children: /* @__PURE__ */ jsx("img", { src: img.url, alt: img.alt || "", style: { width: "100%", height: "100%", objectFit: "cover", display: "block" } }) }, i)) }) });
2303
+ }
2304
+ function SocialLinksSection({ section, theme }) {
2305
+ const { links } = section.content;
2306
+ if (!links || links.length === 0) return null;
2307
+ return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", display: "flex", justifyContent: "center", gap: "1.5rem", flexWrap: "wrap" }, children: links.map((link, i) => /* @__PURE__ */ jsx(
2308
+ "a",
2394
2309
  {
2395
- src: img.url,
2396
- alt: img.alt || "",
2397
- style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
2398
- }
2399
- ) }, i)) }) });
2310
+ href: link.url,
2311
+ target: "_blank",
2312
+ rel: "noopener noreferrer",
2313
+ style: { color: theme.muted, textDecoration: "none", fontSize: "0.85rem", fontWeight: 500, textTransform: "capitalize", letterSpacing: "0.03em" },
2314
+ children: link.platform
2315
+ },
2316
+ i
2317
+ )) });
2318
+ }
2319
+ function DividerSection({ theme }) {
2320
+ return /* @__PURE__ */ jsx("div", { style: { padding: "1rem 1.5rem", maxWidth: 640, margin: "0 auto" }, children: /* @__PURE__ */ jsx("hr", { style: { border: "none", borderTop: `1px solid ${theme.fg}0A`, margin: 0 } }) });
2321
+ }
2322
+ function toEmbedUrl(url) {
2323
+ const ytMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)/);
2324
+ if (ytMatch) return `https://www.youtube.com/embed/${ytMatch[1]}`;
2325
+ const vimeoMatch = url.match(/vimeo\.com\/(\d+)/);
2326
+ if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}`;
2327
+ return url;
2400
2328
  }
2401
2329
  function CTASection({ section, theme, tracking, onEvent }) {
2402
2330
  const { title, subtitle, buttons } = section.content;
@@ -2459,29 +2387,15 @@ function StatsSection({ section, theme }) {
2459
2387
  if (!stats || stats.length === 0) return null;
2460
2388
  if (layout === "list") {
2461
2389
  return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 640, margin: "0 auto" }, children: stats.map((stat, i) => /* @__PURE__ */ jsxs("div", { children: [
2462
- /* @__PURE__ */ jsxs("div", { style: {
2463
- display: "flex",
2464
- justifyContent: "space-between",
2465
- alignItems: "baseline",
2466
- padding: "0.625rem 0"
2467
- }, children: [
2468
- /* @__PURE__ */ jsx("span", { style: {
2469
- fontSize: 12,
2470
- textTransform: "uppercase",
2471
- letterSpacing: "0.15em",
2472
- color: `${theme.fg}66`
2473
- }, children: stat.label }),
2390
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "baseline", padding: "0.625rem 0" }, children: [
2391
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 12, textTransform: "uppercase", letterSpacing: "0.15em", color: `${theme.fg}66` }, children: stat.label }),
2474
2392
  /* @__PURE__ */ jsx("span", { style: { fontSize: 14, fontWeight: 300, color: `${theme.fg}CC` }, children: /* @__PURE__ */ jsx(AnimatedText, { text: stat.value }) })
2475
2393
  ] }),
2476
2394
  i < stats.length - 1 && /* @__PURE__ */ jsx("hr", { style: { border: "none", borderTop: `1px solid ${theme.fg}0A`, margin: 0 } })
2477
2395
  ] }, i)) });
2478
2396
  }
2479
2397
  const columns = Math.min(stats.length, 4);
2480
- return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 640, margin: "0 auto" }, children: /* @__PURE__ */ jsx("div", { style: {
2481
- display: "grid",
2482
- gridTemplateColumns: `repeat(${columns}, 1fr)`,
2483
- border: `1px solid ${theme.fg}0F`
2484
- }, children: stats.map((stat, i) => /* @__PURE__ */ jsxs("div", { style: {
2398
+ return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 640, margin: "0 auto" }, children: /* @__PURE__ */ jsx("div", { style: { display: "grid", gridTemplateColumns: `repeat(${columns}, 1fr)`, border: `1px solid ${theme.fg}0F` }, children: stats.map((stat, i) => /* @__PURE__ */ jsxs("div", { style: {
2485
2399
  padding: "1.25rem 0.5rem",
2486
2400
  textAlign: "center",
2487
2401
  borderRight: i < stats.length - 1 ? `1px solid ${theme.fg}0F` : void 0
@@ -2540,13 +2454,7 @@ function ProductCardSection({ section, data, theme, tracking }) {
2540
2454
  ] })
2541
2455
  ] }) });
2542
2456
  }
2543
- function COAViewerSection({
2544
- section,
2545
- data,
2546
- theme,
2547
- onShowCOA,
2548
- tracking
2549
- }) {
2457
+ function COAViewerSection({ section, data, theme, onShowCOA, tracking }) {
2550
2458
  const coa = data?.coa;
2551
2459
  const c = section.content;
2552
2460
  if (!coa) return null;
@@ -2585,6 +2493,28 @@ function COAViewerSection({
2585
2493
  onShowCOA();
2586
2494
  }, style: buttonStyle, children: buttonLabel }) });
2587
2495
  }
2496
+ function COAModal({ coa, theme, onClose }) {
2497
+ return /* @__PURE__ */ jsxs("div", { style: { position: "fixed", inset: 0, zIndex: 9999, background: "rgba(0,0,0,0.95)", display: "flex", flexDirection: "column" }, children: [
2498
+ /* @__PURE__ */ jsxs("div", { style: {
2499
+ display: "flex",
2500
+ justifyContent: "space-between",
2501
+ alignItems: "center",
2502
+ padding: "0.75rem 1rem",
2503
+ borderBottom: `1px solid ${theme.fg}10`
2504
+ }, children: [
2505
+ /* @__PURE__ */ jsx("span", { style: { color: "#fff", fontWeight: 500, fontSize: "0.85rem" }, children: coa.document_name || "Lab Results" }),
2506
+ /* @__PURE__ */ jsx("button", { onClick: onClose, style: {
2507
+ background: `${theme.fg}10`,
2508
+ border: "none",
2509
+ color: "#fff",
2510
+ fontSize: "0.85rem",
2511
+ cursor: "pointer",
2512
+ padding: "0.375rem 0.75rem"
2513
+ }, children: "Close" })
2514
+ ] }),
2515
+ /* @__PURE__ */ jsx("iframe", { src: coa.url, style: { flex: 1, border: "none", background: "#fff" }, title: "Lab Results" })
2516
+ ] });
2517
+ }
2588
2518
  function LeadCaptureSection({ section, data, theme, onEvent }) {
2589
2519
  const c = section.content;
2590
2520
  const [firstName, setFirstName] = useState("");
@@ -2595,13 +2525,17 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
2595
2525
  const gatewayUrl = c.gateway_url || data.gatewayUrl || "https://whale-gateway.fly.dev";
2596
2526
  const storeId = c.store_id || data.store?.id;
2597
2527
  const slug = c.landing_page_slug || data.landing_page?.slug;
2528
+ const heading = c.heading || "get 10% off your first visit.";
2529
+ const subtitle = c.subtitle || "drop your email and we will send you the code.";
2530
+ const buttonText = c.button_text || "Claim My Discount";
2531
+ const successHeading = c.success_heading || "You\u2019re in!";
2532
+ const successMessage = c.success_message || "Check your inbox for the discount code.";
2598
2533
  async function handleSubmit(e) {
2599
2534
  e.preventDefault();
2600
2535
  if (!email || !storeId) return;
2601
2536
  setStatus("loading");
2602
2537
  setErrorMsg("");
2603
2538
  const urlParams = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
2604
- const analyticsData = data.analyticsContext;
2605
2539
  try {
2606
2540
  const res = await fetch(`${gatewayUrl}/v1/stores/${storeId}/storefront/leads`, {
2607
2541
  method: "POST",
@@ -2617,8 +2551,8 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
2617
2551
  if (newsletterOptIn) t.push(c.newsletter_tag || "newsletter-subscriber");
2618
2552
  return t.length > 0 ? t : void 0;
2619
2553
  })(),
2620
- visitor_id: analyticsData?.visitorId || void 0,
2621
- session_id: analyticsData?.sessionId || void 0,
2554
+ visitor_id: data.analyticsContext?.visitorId || void 0,
2555
+ session_id: data.analyticsContext?.sessionId || void 0,
2622
2556
  utm_source: urlParams?.get("utm_source") || void 0,
2623
2557
  utm_medium: urlParams?.get("utm_medium") || void 0,
2624
2558
  utm_campaign: urlParams?.get("utm_campaign") || void 0,
@@ -2636,11 +2570,6 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
2636
2570
  setStatus("error");
2637
2571
  }
2638
2572
  }
2639
- const heading = c.heading || "get 10% off your first visit.";
2640
- const subtitle = c.subtitle || "drop your email and we will send you the code.";
2641
- const buttonText = c.button_text || "Claim My Discount";
2642
- const successHeading = c.success_heading || "You\u2019re in!";
2643
- const successMessage = c.success_message || "Check your inbox for the discount code.";
2644
2573
  const inputStyle = {
2645
2574
  flex: 1,
2646
2575
  minWidth: 0,
@@ -2655,41 +2584,10 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
2655
2584
  fontFamily: "inherit",
2656
2585
  transition: "border-color 0.2s"
2657
2586
  };
2587
+ if (status === "success") return /* @__PURE__ */ jsx(SuccessState, { theme, heading: successHeading, message: successMessage, couponCode: c.coupon_code });
2658
2588
  return /* @__PURE__ */ jsxs("div", { style: { padding: "3.5rem 1.5rem", maxWidth: 560, margin: "0 auto" }, children: [
2659
2589
  /* @__PURE__ */ jsx("style", { children: `@keyframes lc-spin { to { transform: rotate(360deg) } }` }),
2660
- /* @__PURE__ */ jsx("div", { style: {
2661
- background: theme.surface,
2662
- border: `1px solid ${theme.fg}12`,
2663
- padding: "clamp(2rem, 6vw, 3rem)"
2664
- }, children: status === "success" ? /* @__PURE__ */ jsxs("div", { style: { textAlign: "center" }, children: [
2665
- /* @__PURE__ */ jsx("h2", { style: {
2666
- fontSize: "clamp(1.5rem, 5vw, 2rem)",
2667
- fontWeight: 300,
2668
- fontFamily: theme.fontDisplay || "inherit",
2669
- margin: "0 0 0.75rem",
2670
- lineHeight: 1.2,
2671
- letterSpacing: "-0.02em",
2672
- color: theme.fg
2673
- }, children: successHeading }),
2674
- /* @__PURE__ */ jsx("p", { style: {
2675
- fontSize: "0.9rem",
2676
- color: `${theme.fg}99`,
2677
- margin: "0 0 1.5rem",
2678
- lineHeight: 1.6,
2679
- fontWeight: 300
2680
- }, children: successMessage }),
2681
- c.coupon_code && /* @__PURE__ */ jsx("div", { style: {
2682
- display: "inline-block",
2683
- padding: "0.75rem 2rem",
2684
- background: `${theme.fg}08`,
2685
- border: `1px dashed ${theme.fg}30`,
2686
- fontSize: "clamp(1.25rem, 4vw, 1.75rem)",
2687
- fontWeight: 500,
2688
- fontFamily: "monospace",
2689
- letterSpacing: "0.12em",
2690
- color: theme.accent
2691
- }, children: c.coupon_code })
2692
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2590
+ /* @__PURE__ */ jsxs("div", { style: { background: theme.surface, border: `1px solid ${theme.fg}12`, padding: "clamp(2rem, 6vw, 3rem)" }, children: [
2693
2591
  /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", marginBottom: "clamp(1.5rem, 4vw, 2rem)" }, children: [
2694
2592
  /* @__PURE__ */ jsx("h2", { style: {
2695
2593
  fontSize: "clamp(1.5rem, 5vw, 2.25rem)",
@@ -2711,27 +2609,8 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
2711
2609
  ] }),
2712
2610
  /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
2713
2611
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.75rem", flexWrap: "wrap" }, children: [
2714
- /* @__PURE__ */ jsx(
2715
- "input",
2716
- {
2717
- type: "text",
2718
- placeholder: "First name",
2719
- value: firstName,
2720
- onChange: (e) => setFirstName(e.target.value),
2721
- style: inputStyle
2722
- }
2723
- ),
2724
- /* @__PURE__ */ jsx(
2725
- "input",
2726
- {
2727
- type: "email",
2728
- placeholder: "Email address",
2729
- value: email,
2730
- onChange: (e) => setEmail(e.target.value),
2731
- required: true,
2732
- style: inputStyle
2733
- }
2734
- )
2612
+ /* @__PURE__ */ jsx("input", { type: "text", placeholder: "First name", value: firstName, onChange: (e) => setFirstName(e.target.value), style: inputStyle }),
2613
+ /* @__PURE__ */ jsx("input", { type: "email", placeholder: "Email address", value: email, onChange: (e) => setEmail(e.target.value), required: true, style: inputStyle })
2735
2614
  ] }),
2736
2615
  c.show_newsletter_opt_in !== false && /* @__PURE__ */ jsxs("label", { style: {
2737
2616
  display: "flex",
@@ -2749,121 +2628,130 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
2749
2628
  type: "checkbox",
2750
2629
  checked: newsletterOptIn,
2751
2630
  onChange: (e) => setNewsletterOptIn(e.target.checked),
2752
- style: {
2753
- width: 16,
2754
- height: 16,
2755
- accentColor: theme.accent,
2756
- cursor: "pointer",
2757
- flexShrink: 0
2758
- }
2631
+ style: { width: 16, height: 16, accentColor: theme.accent, cursor: "pointer", flexShrink: 0 }
2759
2632
  }
2760
2633
  ),
2761
2634
  c.newsletter_label || "Also sign me up for the newsletter \u2014 new drops, deals, and company news."
2762
2635
  ] }),
2763
2636
  status === "error" && errorMsg && /* @__PURE__ */ jsx("p", { style: { fontSize: "0.8rem", color: "#e55", margin: 0, fontWeight: 400 }, children: errorMsg }),
2764
- /* @__PURE__ */ jsxs(
2765
- "button",
2766
- {
2767
- type: "submit",
2768
- disabled: status === "loading",
2769
- style: {
2770
- width: "100%",
2771
- padding: "0.875rem",
2772
- background: theme.fg,
2773
- color: theme.bg,
2774
- border: "none",
2775
- fontSize: "0.85rem",
2776
- fontWeight: 500,
2777
- cursor: status === "loading" ? "wait" : "pointer",
2778
- letterSpacing: "0.08em",
2779
- textTransform: "uppercase",
2780
- fontFamily: "inherit",
2781
- display: "flex",
2782
- alignItems: "center",
2783
- justifyContent: "center",
2784
- gap: "0.5rem",
2785
- opacity: status === "loading" ? 0.7 : 1,
2786
- transition: "opacity 0.2s"
2787
- },
2788
- children: [
2789
- status === "loading" && /* @__PURE__ */ jsx("span", { style: {
2790
- display: "inline-block",
2791
- width: 16,
2792
- height: 16,
2793
- border: `2px solid ${theme.bg}40`,
2794
- borderTopColor: theme.bg,
2795
- borderRadius: "50%",
2796
- animation: "lc-spin 0.8s linear infinite"
2797
- } }),
2798
- buttonText
2799
- ]
2800
- }
2801
- )
2637
+ /* @__PURE__ */ jsxs("button", { type: "submit", disabled: status === "loading", style: {
2638
+ width: "100%",
2639
+ padding: "0.875rem",
2640
+ background: theme.fg,
2641
+ color: theme.bg,
2642
+ border: "none",
2643
+ fontSize: "0.85rem",
2644
+ fontWeight: 500,
2645
+ cursor: status === "loading" ? "wait" : "pointer",
2646
+ letterSpacing: "0.08em",
2647
+ textTransform: "uppercase",
2648
+ fontFamily: "inherit",
2649
+ display: "flex",
2650
+ alignItems: "center",
2651
+ justifyContent: "center",
2652
+ gap: "0.5rem",
2653
+ opacity: status === "loading" ? 0.7 : 1,
2654
+ transition: "opacity 0.2s"
2655
+ }, children: [
2656
+ status === "loading" && /* @__PURE__ */ jsx("span", { style: {
2657
+ display: "inline-block",
2658
+ width: 16,
2659
+ height: 16,
2660
+ border: `2px solid ${theme.bg}40`,
2661
+ borderTopColor: theme.bg,
2662
+ borderRadius: "50%",
2663
+ animation: "lc-spin 0.8s linear infinite"
2664
+ } }),
2665
+ buttonText
2666
+ ] })
2802
2667
  ] })
2803
- ] }) })
2668
+ ] })
2804
2669
  ] });
2805
2670
  }
2806
- function SocialLinksSection({ section, theme }) {
2807
- const { links } = section.content;
2808
- if (!links || links.length === 0) return null;
2809
- return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", display: "flex", justifyContent: "center", gap: "1.5rem", flexWrap: "wrap" }, children: links.map((link, i) => /* @__PURE__ */ jsx(
2810
- "a",
2811
- {
2812
- href: link.url,
2813
- target: "_blank",
2814
- rel: "noopener noreferrer",
2815
- style: {
2816
- color: theme.muted,
2817
- textDecoration: "none",
2818
- fontSize: "0.85rem",
2819
- fontWeight: 500,
2820
- textTransform: "capitalize",
2821
- letterSpacing: "0.03em"
2822
- },
2823
- children: link.platform
2824
- },
2825
- i
2826
- )) });
2827
- }
2828
- function DividerSection({ theme }) {
2829
- return /* @__PURE__ */ jsx("div", { style: { padding: "1rem 1.5rem", maxWidth: 640, margin: "0 auto" }, children: /* @__PURE__ */ jsx("hr", { style: { border: "none", borderTop: `1px solid ${theme.fg}0A`, margin: 0 } }) });
2671
+ function SuccessState({ theme, heading, message, couponCode }) {
2672
+ return /* @__PURE__ */ jsx("div", { style: { padding: "3.5rem 1.5rem", maxWidth: 560, margin: "0 auto" }, children: /* @__PURE__ */ jsxs("div", { style: { background: theme.surface, border: `1px solid ${theme.fg}12`, padding: "clamp(2rem, 6vw, 3rem)", textAlign: "center" }, children: [
2673
+ /* @__PURE__ */ jsx("h2", { style: {
2674
+ fontSize: "clamp(1.5rem, 5vw, 2rem)",
2675
+ fontWeight: 300,
2676
+ fontFamily: theme.fontDisplay || "inherit",
2677
+ margin: "0 0 0.75rem",
2678
+ lineHeight: 1.2,
2679
+ letterSpacing: "-0.02em",
2680
+ color: theme.fg
2681
+ }, children: heading }),
2682
+ /* @__PURE__ */ jsx("p", { style: { fontSize: "0.9rem", color: `${theme.fg}99`, margin: "0 0 1.5rem", lineHeight: 1.6, fontWeight: 300 }, children: message }),
2683
+ couponCode && /* @__PURE__ */ jsx("div", { style: {
2684
+ display: "inline-block",
2685
+ padding: "0.75rem 2rem",
2686
+ background: `${theme.fg}08`,
2687
+ border: `1px dashed ${theme.fg}30`,
2688
+ fontSize: "clamp(1.25rem, 4vw, 1.75rem)",
2689
+ fontWeight: 500,
2690
+ fontFamily: "monospace",
2691
+ letterSpacing: "0.12em",
2692
+ color: theme.accent
2693
+ }, children: couponCode })
2694
+ ] }) });
2830
2695
  }
2831
- function COAModal({ coa, theme, onClose }) {
2832
- return /* @__PURE__ */ jsxs("div", { style: { position: "fixed", inset: 0, zIndex: 9999, background: "rgba(0,0,0,0.95)", display: "flex", flexDirection: "column" }, children: [
2833
- /* @__PURE__ */ jsxs("div", { style: {
2834
- display: "flex",
2835
- justifyContent: "space-between",
2836
- alignItems: "center",
2837
- padding: "0.75rem 1rem",
2838
- borderBottom: `1px solid ${theme.fg}10`
2839
- }, children: [
2840
- /* @__PURE__ */ jsx("span", { style: { color: "#fff", fontWeight: 500, fontSize: "0.85rem" }, children: coa.document_name || "Lab Results" }),
2841
- /* @__PURE__ */ jsx(
2842
- "button",
2843
- {
2844
- onClick: onClose,
2845
- style: {
2846
- background: `${theme.fg}10`,
2847
- border: "none",
2848
- color: "#fff",
2849
- fontSize: "0.85rem",
2850
- cursor: "pointer",
2851
- padding: "0.375rem 0.75rem"
2852
- },
2853
- children: "Close"
2696
+ function SectionRenderer({
2697
+ section,
2698
+ data,
2699
+ theme,
2700
+ tracking,
2701
+ onEvent
2702
+ }) {
2703
+ const [showCOA, setShowCOA] = useState(false);
2704
+ const el = (() => {
2705
+ switch (section.type) {
2706
+ case "hero":
2707
+ return /* @__PURE__ */ jsx(HeroSection, { section, theme, tracking, onEvent });
2708
+ case "text":
2709
+ return /* @__PURE__ */ jsx(TextSection, { section, theme });
2710
+ case "image":
2711
+ return /* @__PURE__ */ jsx(ImageSection, { section, theme });
2712
+ case "video":
2713
+ return /* @__PURE__ */ jsx(VideoSection, { section, theme });
2714
+ case "gallery":
2715
+ return /* @__PURE__ */ jsx(GallerySection, { section, theme });
2716
+ case "cta":
2717
+ return /* @__PURE__ */ jsx(CTASection, { section, theme, tracking, onEvent });
2718
+ case "stats":
2719
+ return /* @__PURE__ */ jsx(StatsSection, { section, theme });
2720
+ case "product_card":
2721
+ return /* @__PURE__ */ jsx(ProductCardSection, { section, data, theme, tracking });
2722
+ case "coa_viewer":
2723
+ return /* @__PURE__ */ jsx(COAViewerSection, { section, data, theme, onShowCOA: () => setShowCOA(true), tracking });
2724
+ case "social_links":
2725
+ return /* @__PURE__ */ jsx(SocialLinksSection, { section, theme });
2726
+ case "lead_capture":
2727
+ return /* @__PURE__ */ jsx(LeadCaptureSection, { section, data, theme, onEvent });
2728
+ case "divider":
2729
+ return /* @__PURE__ */ jsx(DividerSection, { theme });
2730
+ default:
2731
+ return null;
2732
+ }
2733
+ })();
2734
+ const sectionRef = useRef(null);
2735
+ useEffect(() => {
2736
+ const el2 = sectionRef.current;
2737
+ if (!el2 || typeof IntersectionObserver === "undefined") return;
2738
+ const obs = new IntersectionObserver(
2739
+ ([entry]) => {
2740
+ if (entry.isIntersecting) {
2741
+ onEvent?.("section_view", { section_id: section.id, section_type: section.type });
2742
+ obs.disconnect();
2854
2743
  }
2855
- )
2856
- ] }),
2857
- /* @__PURE__ */ jsx("iframe", { src: coa.url, style: { flex: 1, border: "none", background: "#fff" }, title: "Lab Results" })
2744
+ },
2745
+ { threshold: 0.5 }
2746
+ );
2747
+ obs.observe(el2);
2748
+ return () => obs.disconnect();
2749
+ }, [section.id, section.type, onEvent]);
2750
+ return /* @__PURE__ */ jsxs("div", { ref: sectionRef, "data-section-id": section.id, "data-section-type": section.type, children: [
2751
+ el,
2752
+ showCOA && data?.coa && /* @__PURE__ */ jsx(COAModal, { coa: data.coa, theme, onClose: () => setShowCOA(false) })
2858
2753
  ] });
2859
2754
  }
2860
- function toEmbedUrl(url) {
2861
- const ytMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)/);
2862
- if (ytMatch) return `https://www.youtube.com/embed/${ytMatch[1]}`;
2863
- const vimeoMatch = url.match(/vimeo\.com\/(\d+)/);
2864
- if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}`;
2865
- return url;
2866
- }
2867
2755
  function QRLandingPage({
2868
2756
  code,
2869
2757
  gatewayUrl = "https://whale-gateway.fly.dev",
@@ -3162,6 +3050,12 @@ function DefaultError({ message }) {
3162
3050
  /* @__PURE__ */ jsx("p", { style: { color: "#888" }, children: message || "Please try scanning again." })
3163
3051
  ] }) });
3164
3052
  }
3053
+ function getInlinedData() {
3054
+ if (typeof window !== "undefined" && window.__LANDING_DATA__) {
3055
+ return window.__LANDING_DATA__;
3056
+ }
3057
+ return null;
3058
+ }
3165
3059
  function LandingPage({
3166
3060
  slug,
3167
3061
  gatewayUrl = "https://whale-gateway.fly.dev",
@@ -3172,47 +3066,43 @@ function LandingPage({
3172
3066
  analyticsContext,
3173
3067
  enableAnalytics = true
3174
3068
  }) {
3175
- const [state, setState] = useState("loading");
3176
- const [data, setData] = useState(null);
3069
+ const inlined = useRef(getInlinedData()).current;
3070
+ const [state, setState] = useState(inlined ? "ready" : "loading");
3071
+ const [data, setData] = useState(inlined);
3177
3072
  const [errorMsg, setErrorMsg] = useState("");
3178
3073
  useEffect(() => {
3179
3074
  if (!slug) return;
3180
- let cancelled = false;
3181
- if (typeof window !== "undefined" && window.__LANDING_DATA__) {
3182
- const json = window.__LANDING_DATA__;
3183
- setData(json);
3184
- setState("ready");
3185
- onDataLoaded?.(json);
3075
+ if (data) {
3076
+ onDataLoaded?.(data);
3186
3077
  return;
3187
3078
  }
3079
+ let cancelled = false;
3188
3080
  async function load() {
3189
3081
  try {
3190
3082
  const res = await fetch(`${gatewayUrl}/l/${encodeURIComponent(slug)}`);
3191
- if (!cancelled) {
3192
- if (res.status === 404) {
3193
- setState("not_found");
3194
- return;
3195
- }
3196
- if (res.status === 410) {
3197
- setState("expired");
3198
- return;
3199
- }
3200
- if (!res.ok) {
3201
- const body = await res.json().catch(() => ({}));
3202
- throw new Error(body?.error?.message ?? `Failed to load: ${res.status}`);
3203
- }
3204
- const json = await res.json();
3205
- setData(json);
3206
- setState("ready");
3207
- onDataLoaded?.(json);
3083
+ if (cancelled) return;
3084
+ if (res.status === 404) {
3085
+ setState("not_found");
3086
+ return;
3208
3087
  }
3209
- } catch (err) {
3210
- if (!cancelled) {
3211
- const e = err instanceof Error ? err : new Error(String(err));
3212
- setErrorMsg(e.message);
3213
- setState("error");
3214
- onError?.(e);
3088
+ if (res.status === 410) {
3089
+ setState("expired");
3090
+ return;
3091
+ }
3092
+ if (!res.ok) {
3093
+ const body = await res.json().catch(() => ({}));
3094
+ throw new Error(body?.error?.message ?? `Failed to load: ${res.status}`);
3215
3095
  }
3096
+ const json = await res.json();
3097
+ setData(json);
3098
+ setState("ready");
3099
+ onDataLoaded?.(json);
3100
+ } catch (err) {
3101
+ if (cancelled) return;
3102
+ const e = err instanceof Error ? err : new Error(String(err));
3103
+ setErrorMsg(e.message);
3104
+ setState("error");
3105
+ onError?.(e);
3216
3106
  }
3217
3107
  }
3218
3108
  load();
@@ -3220,12 +3110,22 @@ function LandingPage({
3220
3110
  cancelled = true;
3221
3111
  };
3222
3112
  }, [slug, gatewayUrl]);
3223
- if (state === "loading") return /* @__PURE__ */ jsx(DefaultLoading2, {});
3224
- if (state === "not_found") return /* @__PURE__ */ jsx(DefaultNotFound2, {});
3225
- if (state === "expired") return /* @__PURE__ */ jsx(DefaultExpired2, {});
3226
- if (state === "error") return /* @__PURE__ */ jsx(DefaultError2, { message: errorMsg });
3113
+ if (state === "loading") return /* @__PURE__ */ jsx(StateScreen, { title: "", loading: true });
3114
+ if (state === "not_found") return /* @__PURE__ */ jsx(StateScreen, { title: "Page Not Found", subtitle: "This page does not exist or has been removed." });
3115
+ if (state === "expired") return /* @__PURE__ */ jsx(StateScreen, { title: "Page Expired", subtitle: "This page is no longer active." });
3116
+ if (state === "error") return /* @__PURE__ */ jsx(StateScreen, { title: "Something Went Wrong", subtitle: errorMsg || "Please try again later." });
3227
3117
  if (!data) return null;
3228
- return /* @__PURE__ */ jsx(PageLayout, { data, gatewayUrl, renderSection, onEvent, analyticsContext, enableAnalytics });
3118
+ return /* @__PURE__ */ jsx(
3119
+ PageLayout,
3120
+ {
3121
+ data,
3122
+ gatewayUrl,
3123
+ renderSection,
3124
+ onEvent,
3125
+ analyticsContext,
3126
+ enableAnalytics
3127
+ }
3128
+ );
3229
3129
  }
3230
3130
  function isSectionVisible(section, urlParams) {
3231
3131
  const vis = section.config?.visibility;
@@ -3248,8 +3148,8 @@ function PageLayout({
3248
3148
  const trackerRef = useRef(null);
3249
3149
  useEffect(() => {
3250
3150
  if (!enableAnalytics || typeof window === "undefined") return;
3251
- const analyticsConfig = window.__LANDING_ANALYTICS__;
3252
- if (!analyticsConfig?.slug) return;
3151
+ const config = window.__LANDING_ANALYTICS__;
3152
+ if (!config?.slug) return;
3253
3153
  let visitorId = localStorage.getItem("wt_vid") || "";
3254
3154
  if (!visitorId) {
3255
3155
  visitorId = crypto.randomUUID();
@@ -3261,8 +3161,8 @@ function PageLayout({
3261
3161
  sessionStorage.setItem("wt_sid", sessionId);
3262
3162
  }
3263
3163
  import('../tracker-34GTXU54.js').then(({ BehavioralTracker: BehavioralTracker2 }) => {
3264
- const gwUrl = analyticsConfig.gatewayUrl || gatewayUrl;
3265
- const slug = analyticsConfig.slug;
3164
+ const gwUrl = config.gatewayUrl || gatewayUrl;
3165
+ const slug = config.slug;
3266
3166
  const utmParams = new URLSearchParams(window.location.search);
3267
3167
  const tracker = new BehavioralTracker2({
3268
3168
  sessionId,
@@ -3273,65 +3173,44 @@ function PageLayout({
3273
3173
  event_data: e.data,
3274
3174
  session_id: batch.session_id,
3275
3175
  visitor_id: batch.visitor_id,
3276
- campaign_id: analyticsConfig.campaignId || utmParams.get("utm_campaign_id") || void 0,
3176
+ campaign_id: config.campaignId || utmParams.get("utm_campaign_id") || void 0,
3277
3177
  utm_source: utmParams.get("utm_source") || void 0,
3278
3178
  utm_medium: utmParams.get("utm_medium") || void 0,
3279
3179
  utm_campaign: utmParams.get("utm_campaign") || void 0
3280
3180
  }));
3281
- const body = JSON.stringify({ events });
3282
- if (typeof navigator !== "undefined" && navigator.sendBeacon) {
3283
- navigator.sendBeacon(
3284
- `${gwUrl}/l/${encodeURIComponent(slug)}/events`,
3285
- new Blob([body], { type: "application/json" })
3286
- );
3287
- } else {
3288
- await fetch(`${gwUrl}/l/${encodeURIComponent(slug)}/events`, {
3289
- method: "POST",
3290
- headers: { "Content-Type": "application/json" },
3291
- body,
3292
- keepalive: true
3293
- });
3294
- }
3181
+ sendEvents(gwUrl, slug, events);
3295
3182
  }
3296
3183
  });
3297
3184
  tracker.setPageContext(window.location.href, window.location.pathname);
3298
3185
  tracker.start();
3299
3186
  trackerRef.current = tracker;
3300
- const pageViewBody = JSON.stringify({
3301
- events: [{
3302
- event_type: "page_view",
3303
- event_data: { referrer: document.referrer, url: window.location.href },
3304
- session_id: sessionId,
3305
- visitor_id: visitorId,
3306
- campaign_id: analyticsConfig.campaignId || void 0,
3307
- utm_source: utmParams.get("utm_source") || void 0,
3308
- utm_medium: utmParams.get("utm_medium") || void 0,
3309
- utm_campaign: utmParams.get("utm_campaign") || void 0
3310
- }]
3311
- });
3312
- if (navigator.sendBeacon) {
3313
- navigator.sendBeacon(
3314
- `${gwUrl}/l/${encodeURIComponent(slug)}/events`,
3315
- new Blob([pageViewBody], { type: "application/json" })
3316
- );
3317
- } else {
3318
- fetch(`${gwUrl}/l/${encodeURIComponent(slug)}/events`, {
3319
- method: "POST",
3320
- headers: { "Content-Type": "application/json" },
3321
- body: pageViewBody,
3322
- keepalive: true
3323
- }).catch(() => {
3324
- });
3325
- }
3187
+ sendEvents(gwUrl, slug, [{
3188
+ event_type: "page_view",
3189
+ event_data: { referrer: document.referrer, url: window.location.href },
3190
+ session_id: sessionId,
3191
+ visitor_id: visitorId,
3192
+ campaign_id: config.campaignId || void 0
3193
+ }]);
3326
3194
  }).catch(() => {
3327
3195
  });
3328
3196
  return () => {
3329
- if (trackerRef.current) {
3330
- trackerRef.current.stop();
3331
- trackerRef.current = null;
3332
- }
3197
+ trackerRef.current?.stop();
3198
+ trackerRef.current = null;
3333
3199
  };
3334
3200
  }, [enableAnalytics, gatewayUrl]);
3201
+ const handleEvent = useCallback((event, eventData) => {
3202
+ onEvent?.(event, eventData);
3203
+ if (!enableAnalytics || typeof window === "undefined") return;
3204
+ const config = window.__LANDING_ANALYTICS__;
3205
+ if (!config?.slug) return;
3206
+ sendEvents(config.gatewayUrl || gatewayUrl, config.slug, [{
3207
+ event_type: event,
3208
+ event_data: eventData,
3209
+ session_id: sessionStorage.getItem("wt_sid") || void 0,
3210
+ visitor_id: localStorage.getItem("wt_vid") || void 0,
3211
+ campaign_id: config.campaignId || void 0
3212
+ }]);
3213
+ }, [onEvent, enableAnalytics, gatewayUrl]);
3335
3214
  const theme = {
3336
3215
  bg: lp.background_color || store?.theme?.background || "#050505",
3337
3216
  fg: lp.text_color || store?.theme?.foreground || "#fafafa",
@@ -3350,11 +3229,9 @@ function PageLayout({
3350
3229
  lp.custom_css && /* @__PURE__ */ jsx("style", { children: lp.custom_css }),
3351
3230
  logoUrl && /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ jsx("img", { src: logoUrl, alt: store?.name || "Store", style: { height: 40, objectFit: "contain" } }) }),
3352
3231
  sorted.map((section) => {
3353
- const defaultRenderer = () => /* @__PURE__ */ jsx(SectionRenderer, { section, data: sectionData, theme, onEvent }, section.id);
3354
- if (renderSection) {
3355
- return /* @__PURE__ */ jsx("div", { children: renderSection(section, defaultRenderer) }, section.id);
3356
- }
3357
- return /* @__PURE__ */ jsx(SectionRenderer, { section, data: sectionData, theme, onEvent }, section.id);
3232
+ const defaultRenderer = () => /* @__PURE__ */ jsx(SectionRenderer, { section, data: sectionData, theme, onEvent: handleEvent }, section.id);
3233
+ if (renderSection) return /* @__PURE__ */ jsx("div", { children: renderSection(section, defaultRenderer) }, section.id);
3234
+ return /* @__PURE__ */ jsx(SectionRenderer, { section, data: sectionData, theme, onEvent: handleEvent }, section.id);
3358
3235
  }),
3359
3236
  store?.name && /* @__PURE__ */ jsx("div", { style: { padding: "2rem 1.5rem", borderTop: `1px solid ${theme.surface}`, textAlign: "center" }, children: /* @__PURE__ */ jsxs("p", { style: { fontSize: "0.75rem", color: theme.muted, margin: 0 }, children: [
3360
3237
  "Powered by ",
@@ -3362,7 +3239,17 @@ function PageLayout({
3362
3239
  ] }) })
3363
3240
  ] });
3364
3241
  }
3365
- var containerStyle2 = {
3242
+ function sendEvents(gwUrl, slug, events) {
3243
+ const body = JSON.stringify({ events });
3244
+ const url = `${gwUrl}/l/${encodeURIComponent(slug)}/events`;
3245
+ if (typeof navigator !== "undefined" && navigator.sendBeacon) {
3246
+ navigator.sendBeacon(url, new Blob([body], { type: "application/json" }));
3247
+ } else {
3248
+ fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body, keepalive: true }).catch(() => {
3249
+ });
3250
+ }
3251
+ }
3252
+ var screenStyle = {
3366
3253
  minHeight: "100dvh",
3367
3254
  display: "flex",
3368
3255
  justifyContent: "center",
@@ -3373,28 +3260,14 @@ var containerStyle2 = {
3373
3260
  textAlign: "center",
3374
3261
  padding: "2rem"
3375
3262
  };
3376
- function DefaultLoading2() {
3377
- return /* @__PURE__ */ jsx("div", { style: containerStyle2, children: /* @__PURE__ */ jsxs("div", { children: [
3378
- /* @__PURE__ */ jsx("div", { style: { width: 32, height: 32, border: "2px solid #333", borderTopColor: "#fafafa", borderRadius: "50%", animation: "spin 0.8s linear infinite", margin: "0 auto 1rem" } }),
3379
- /* @__PURE__ */ jsx("style", { children: `@keyframes spin { to { transform: rotate(360deg) } }` })
3380
- ] }) });
3381
- }
3382
- function DefaultNotFound2() {
3383
- return /* @__PURE__ */ jsx("div", { style: containerStyle2, children: /* @__PURE__ */ jsxs("div", { children: [
3384
- /* @__PURE__ */ jsx("h1", { style: { fontSize: "1.5rem", marginBottom: "0.5rem" }, children: "Page Not Found" }),
3385
- /* @__PURE__ */ jsx("p", { style: { color: "#888" }, children: "This page does not exist or has been removed." })
3386
- ] }) });
3387
- }
3388
- function DefaultExpired2() {
3389
- return /* @__PURE__ */ jsx("div", { style: containerStyle2, children: /* @__PURE__ */ jsxs("div", { children: [
3390
- /* @__PURE__ */ jsx("h1", { style: { fontSize: "1.5rem", marginBottom: "0.5rem" }, children: "Page Expired" }),
3391
- /* @__PURE__ */ jsx("p", { style: { color: "#888" }, children: "This page is no longer active." })
3392
- ] }) });
3393
- }
3394
- function DefaultError2({ message }) {
3395
- return /* @__PURE__ */ jsx("div", { style: containerStyle2, children: /* @__PURE__ */ jsxs("div", { children: [
3396
- /* @__PURE__ */ jsx("h1", { style: { fontSize: "1.5rem", marginBottom: "0.5rem" }, children: "Something Went Wrong" }),
3397
- /* @__PURE__ */ jsx("p", { style: { color: "#888" }, children: message || "Please try again later." })
3263
+ function StateScreen({ title, subtitle, loading }) {
3264
+ return /* @__PURE__ */ jsx("div", { style: screenStyle, children: /* @__PURE__ */ jsxs("div", { children: [
3265
+ loading && /* @__PURE__ */ jsxs(Fragment, { children: [
3266
+ /* @__PURE__ */ jsx("div", { style: { width: 32, height: 32, border: "2px solid #333", borderTopColor: "#fafafa", borderRadius: "50%", animation: "spin 0.8s linear infinite", margin: "0 auto 1rem" } }),
3267
+ /* @__PURE__ */ jsx("style", { children: `@keyframes spin { to { transform: rotate(360deg) } }` })
3268
+ ] }),
3269
+ title && /* @__PURE__ */ jsx("h1", { style: { fontSize: "1.5rem", marginBottom: "0.5rem" }, children: title }),
3270
+ subtitle && /* @__PURE__ */ jsx("p", { style: { color: "#888" }, children: subtitle })
3398
3271
  ] }) });
3399
3272
  }
3400
3273