@neowhale/storefront 0.2.35 → 0.2.36

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.
@@ -384,8 +384,19 @@ var WhaleStorefront = (function (exports) {
384
384
  R2.createElement;
385
385
  var Fragment2 = R2.Fragment;
386
386
 
387
- // src/react/components/section-renderer.tsx
388
- var NUM_PATTERN = /(\$?[\d,]+\.?\d*[+★%]?)/g;
387
+ // src/react/components/sections/shared.tsx
388
+ function trackClick(tracking, label, url, position) {
389
+ if (!tracking?.gatewayUrl || !tracking?.code) return;
390
+ const body = JSON.stringify({ label, url, position });
391
+ if (typeof navigator !== "undefined" && navigator.sendBeacon) {
392
+ navigator.sendBeacon(
393
+ `${tracking.gatewayUrl}/q/${encodeURIComponent(tracking.code)}/click`,
394
+ new Blob([body], { type: "application/json" })
395
+ );
396
+ }
397
+ }
398
+ var NUM_SPLIT = /(\$?[\d,]+\.?\d*[+★%]?)/g;
399
+ var NUM_TEST = /^\$?[\d,]+\.?\d*[+★%]?$/;
389
400
  function easeOutQuart(t) {
390
401
  return 1 - Math.pow(1 - t, 4);
391
402
  }
@@ -442,147 +453,71 @@ var WhaleStorefront = (function (exports) {
442
453
  ] });
443
454
  }
444
455
  function AnimatedText({ text }) {
445
- const parts = text.split(NUM_PATTERN);
456
+ const parts = text.split(NUM_SPLIT);
446
457
  return /* @__PURE__ */ jsx(Fragment2, { children: parts.map(
447
- (part, i) => NUM_PATTERN.test(part) ? /* @__PURE__ */ jsx(AnimatedNumber, { raw: part }, i) : part
458
+ (part, i) => NUM_TEST.test(part) ? /* @__PURE__ */ jsx(AnimatedNumber, { raw: part }, i) : part
448
459
  ) });
449
460
  }
450
- function trackClick(tracking, label, url, position) {
451
- if (!tracking?.gatewayUrl || !tracking?.code) return;
452
- const body = JSON.stringify({ label, url, position });
453
- if (typeof navigator !== "undefined" && navigator.sendBeacon) {
454
- navigator.sendBeacon(
455
- `${tracking.gatewayUrl}/q/${encodeURIComponent(tracking.code)}/click`,
456
- new Blob([body], { type: "application/json" })
457
- );
458
- }
459
- }
460
- function SectionRenderer({
461
- section,
462
- data,
463
- theme,
464
- tracking,
465
- onEvent
466
- }) {
467
- const [showCOA, setShowCOA] = useState(false);
468
- const el = (() => {
469
- switch (section.type) {
470
- case "hero":
471
- return /* @__PURE__ */ jsx(HeroSection, { section, theme, tracking, onEvent });
472
- case "text":
473
- return /* @__PURE__ */ jsx(TextSection, { section, theme });
474
- case "image":
475
- return /* @__PURE__ */ jsx(ImageSection, { section, theme });
476
- case "video":
477
- return /* @__PURE__ */ jsx(VideoSection, { section, theme });
478
- case "gallery":
479
- return /* @__PURE__ */ jsx(GallerySection, { section, theme });
480
- case "cta":
481
- return /* @__PURE__ */ jsx(CTASection, { section, theme, tracking, onEvent });
482
- case "stats":
483
- return /* @__PURE__ */ jsx(StatsSection, { section, theme });
484
- case "product_card":
485
- return /* @__PURE__ */ jsx(ProductCardSection, { section, data, theme, tracking });
486
- case "coa_viewer":
487
- return /* @__PURE__ */ jsx(COAViewerSection, { section, data, theme, onShowCOA: () => setShowCOA(true), tracking });
488
- case "social_links":
489
- return /* @__PURE__ */ jsx(SocialLinksSection, { section, theme });
490
- case "lead_capture":
491
- return /* @__PURE__ */ jsx(LeadCaptureSection, { section, data, theme, onEvent });
492
- case "divider":
493
- return /* @__PURE__ */ jsx(DividerSection, { theme });
494
- default:
495
- return null;
496
- }
497
- })();
498
- const sectionRef = useRef(null);
499
- useEffect(() => {
500
- const el2 = sectionRef.current;
501
- if (!el2 || typeof IntersectionObserver === "undefined") return;
502
- const obs = new IntersectionObserver(
503
- ([entry]) => {
504
- if (entry.isIntersecting) {
505
- onEvent?.("section_view", {
506
- section_id: section.id,
507
- section_type: section.type
508
- });
509
- obs.disconnect();
510
- }
511
- },
512
- { threshold: 0.5 }
513
- );
514
- obs.observe(el2);
515
- return () => obs.disconnect();
516
- }, [section.id, section.type, onEvent]);
517
- return /* @__PURE__ */ jsxs("div", { ref: sectionRef, "data-section-id": section.id, "data-section-type": section.type, children: [
518
- el,
519
- showCOA && data?.coa && /* @__PURE__ */ jsx(COAModal, { coa: data.coa, theme, onClose: () => setShowCOA(false) })
520
- ] });
521
- }
461
+
462
+ // src/react/components/sections/content-sections.tsx
522
463
  function HeroSection({ section, theme, tracking, onEvent }) {
523
464
  const { title, subtitle, background_image, cta_text, cta_url } = section.content;
524
- return /* @__PURE__ */ jsxs(
525
- "div",
526
- {
527
- style: {
528
- position: "relative",
529
- minHeight: "60vh",
530
- display: "flex",
531
- flexDirection: "column",
532
- justifyContent: "center",
533
- alignItems: "center",
534
- textAlign: "center",
535
- padding: "3rem 1.5rem",
536
- backgroundImage: background_image ? `url(${background_image})` : void 0,
537
- backgroundSize: "cover",
538
- backgroundPosition: "center"
539
- },
540
- children: [
541
- background_image && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" } }),
542
- /* @__PURE__ */ jsxs("div", { style: { position: "relative", zIndex: 1, maxWidth: 640 }, children: [
543
- title && /* @__PURE__ */ jsx("h1", { style: {
544
- fontSize: "clamp(2rem, 8vw, 3rem)",
545
- fontWeight: 300,
546
- fontFamily: theme.fontDisplay || "inherit",
547
- margin: "0 0 1rem",
548
- lineHeight: 1.15,
549
- letterSpacing: "-0.02em",
550
- color: theme.fg
551
- }, children: /* @__PURE__ */ jsx(AnimatedText, { text: title }) }),
552
- subtitle && /* @__PURE__ */ jsx("p", { style: {
465
+ return /* @__PURE__ */ jsxs("div", { style: {
466
+ position: "relative",
467
+ minHeight: "60vh",
468
+ display: "flex",
469
+ flexDirection: "column",
470
+ justifyContent: "center",
471
+ alignItems: "center",
472
+ textAlign: "center",
473
+ padding: "3rem 1.5rem",
474
+ backgroundImage: background_image ? `url(${background_image})` : void 0,
475
+ backgroundSize: "cover",
476
+ backgroundPosition: "center"
477
+ }, children: [
478
+ background_image && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" } }),
479
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative", zIndex: 1, maxWidth: 640 }, children: [
480
+ title && /* @__PURE__ */ jsx("h1", { style: {
481
+ fontSize: "clamp(2rem, 8vw, 3rem)",
482
+ fontWeight: 300,
483
+ fontFamily: theme.fontDisplay || "inherit",
484
+ margin: "0 0 1rem",
485
+ lineHeight: 1.15,
486
+ letterSpacing: "-0.02em",
487
+ color: theme.fg
488
+ }, children: /* @__PURE__ */ jsx(AnimatedText, { text: title }) }),
489
+ subtitle && /* @__PURE__ */ jsx("p", { style: {
490
+ fontSize: "0.85rem",
491
+ color: theme.accent,
492
+ margin: "0 0 2rem",
493
+ lineHeight: 1.6,
494
+ textTransform: "uppercase",
495
+ letterSpacing: "0.15em"
496
+ }, children: subtitle }),
497
+ cta_text && cta_url && /* @__PURE__ */ jsx(
498
+ "a",
499
+ {
500
+ href: cta_url,
501
+ onClick: () => {
502
+ trackClick(tracking, cta_text, cta_url);
503
+ onEvent?.("cta_click", { label: cta_text, url: cta_url });
504
+ },
505
+ style: {
506
+ display: "inline-block",
507
+ padding: "0.875rem 2rem",
508
+ background: theme.fg,
509
+ color: theme.bg,
510
+ textDecoration: "none",
553
511
  fontSize: "0.85rem",
554
- color: theme.accent,
555
- margin: "0 0 2rem",
556
- lineHeight: 1.6,
557
- textTransform: "uppercase",
558
- letterSpacing: "0.15em"
559
- }, children: subtitle }),
560
- cta_text && cta_url && /* @__PURE__ */ jsx(
561
- "a",
562
- {
563
- href: cta_url,
564
- onClick: () => {
565
- trackClick(tracking, cta_text, cta_url);
566
- onEvent?.("cta_click", { label: cta_text, url: cta_url });
567
- },
568
- style: {
569
- display: "inline-block",
570
- padding: "0.875rem 2rem",
571
- background: theme.fg,
572
- color: theme.bg,
573
- textDecoration: "none",
574
- fontSize: "0.85rem",
575
- fontWeight: 500,
576
- letterSpacing: "0.08em",
577
- textTransform: "uppercase"
578
- },
579
- children: cta_text
580
- }
581
- )
582
- ] })
583
- ]
584
- }
585
- );
512
+ fontWeight: 500,
513
+ letterSpacing: "0.08em",
514
+ textTransform: "uppercase"
515
+ },
516
+ children: cta_text
517
+ }
518
+ )
519
+ ] })
520
+ ] });
586
521
  }
587
522
  function TextSection({ section, theme }) {
588
523
  const { heading, body } = section.content;
@@ -604,14 +539,7 @@ var WhaleStorefront = (function (exports) {
604
539
  const contained = section.config?.contained !== false;
605
540
  if (!url) return null;
606
541
  return /* @__PURE__ */ jsxs("div", { style: { padding: contained ? "1.5rem" : 0, maxWidth: contained ? 640 : void 0, margin: contained ? "0 auto" : void 0 }, children: [
607
- /* @__PURE__ */ jsx(
608
- "img",
609
- {
610
- src: url,
611
- alt: alt || "",
612
- style: { width: "100%", display: "block", objectFit: "cover" }
613
- }
614
- ),
542
+ /* @__PURE__ */ jsx("img", { src: url, alt: alt || "", style: { width: "100%", display: "block", objectFit: "cover" } }),
615
543
  caption && /* @__PURE__ */ jsx("p", { style: { fontSize: "0.8rem", color: theme.muted, textAlign: "center", marginTop: "0.75rem" }, children: caption })
616
544
  ] });
617
545
  }
@@ -619,37 +547,41 @@ var WhaleStorefront = (function (exports) {
619
547
  const { url, poster } = section.content;
620
548
  if (!url) return null;
621
549
  const isEmbed = url.includes("youtube") || url.includes("youtu.be") || url.includes("vimeo");
622
- 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(
623
- "iframe",
624
- {
625
- src: toEmbedUrl(url),
626
- style: { position: "absolute", top: 0, left: 0, width: "100%", height: "100%", border: "none" },
627
- allow: "autoplay; fullscreen",
628
- title: "Video"
629
- }
630
- ) }) : /* @__PURE__ */ jsx(
631
- "video",
632
- {
633
- src: url,
634
- poster,
635
- controls: true,
636
- style: { width: "100%", display: "block", background: theme.surface }
637
- }
638
- ) });
550
+ 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 } }) });
639
551
  }
640
552
  function GallerySection({ section, theme }) {
641
553
  const { images } = section.content;
642
554
  const columns = section.config?.columns || 3;
643
555
  if (!images || images.length === 0) return null;
644
- 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(
645
- "img",
556
+ 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)) }) });
557
+ }
558
+ function SocialLinksSection({ section, theme }) {
559
+ const { links } = section.content;
560
+ if (!links || links.length === 0) return null;
561
+ return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", display: "flex", justifyContent: "center", gap: "1.5rem", flexWrap: "wrap" }, children: links.map((link, i) => /* @__PURE__ */ jsx(
562
+ "a",
646
563
  {
647
- src: img.url,
648
- alt: img.alt || "",
649
- style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
650
- }
651
- ) }, i)) }) });
564
+ href: link.url,
565
+ target: "_blank",
566
+ rel: "noopener noreferrer",
567
+ style: { color: theme.muted, textDecoration: "none", fontSize: "0.85rem", fontWeight: 500, textTransform: "capitalize", letterSpacing: "0.03em" },
568
+ children: link.platform
569
+ },
570
+ i
571
+ )) });
572
+ }
573
+ function DividerSection({ theme }) {
574
+ 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 } }) });
575
+ }
576
+ function toEmbedUrl(url) {
577
+ const ytMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)/);
578
+ if (ytMatch) return `https://www.youtube.com/embed/${ytMatch[1]}`;
579
+ const vimeoMatch = url.match(/vimeo\.com\/(\d+)/);
580
+ if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}`;
581
+ return url;
652
582
  }
583
+
584
+ // src/react/components/sections/interactive-sections.tsx
653
585
  function CTASection({ section, theme, tracking, onEvent }) {
654
586
  const { title, subtitle, buttons } = section.content;
655
587
  if (!buttons || buttons.length === 0) return null;
@@ -711,29 +643,15 @@ var WhaleStorefront = (function (exports) {
711
643
  if (!stats || stats.length === 0) return null;
712
644
  if (layout === "list") {
713
645
  return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 640, margin: "0 auto" }, children: stats.map((stat, i) => /* @__PURE__ */ jsxs("div", { children: [
714
- /* @__PURE__ */ jsxs("div", { style: {
715
- display: "flex",
716
- justifyContent: "space-between",
717
- alignItems: "baseline",
718
- padding: "0.625rem 0"
719
- }, children: [
720
- /* @__PURE__ */ jsx("span", { style: {
721
- fontSize: 12,
722
- textTransform: "uppercase",
723
- letterSpacing: "0.15em",
724
- color: `${theme.fg}66`
725
- }, children: stat.label }),
646
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "baseline", padding: "0.625rem 0" }, children: [
647
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 12, textTransform: "uppercase", letterSpacing: "0.15em", color: `${theme.fg}66` }, children: stat.label }),
726
648
  /* @__PURE__ */ jsx("span", { style: { fontSize: 14, fontWeight: 300, color: `${theme.fg}CC` }, children: /* @__PURE__ */ jsx(AnimatedText, { text: stat.value }) })
727
649
  ] }),
728
650
  i < stats.length - 1 && /* @__PURE__ */ jsx("hr", { style: { border: "none", borderTop: `1px solid ${theme.fg}0A`, margin: 0 } })
729
651
  ] }, i)) });
730
652
  }
731
653
  const columns = Math.min(stats.length, 4);
732
- return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 640, margin: "0 auto" }, children: /* @__PURE__ */ jsx("div", { style: {
733
- display: "grid",
734
- gridTemplateColumns: `repeat(${columns}, 1fr)`,
735
- border: `1px solid ${theme.fg}0F`
736
- }, children: stats.map((stat, i) => /* @__PURE__ */ jsxs("div", { style: {
654
+ 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: {
737
655
  padding: "1.25rem 0.5rem",
738
656
  textAlign: "center",
739
657
  borderRight: i < stats.length - 1 ? `1px solid ${theme.fg}0F` : void 0
@@ -792,13 +710,7 @@ var WhaleStorefront = (function (exports) {
792
710
  ] })
793
711
  ] }) });
794
712
  }
795
- function COAViewerSection({
796
- section,
797
- data,
798
- theme,
799
- onShowCOA,
800
- tracking
801
- }) {
713
+ function COAViewerSection({ section, data, theme, onShowCOA, tracking }) {
802
714
  const coa = data?.coa;
803
715
  const c = section.content;
804
716
  if (!coa) return null;
@@ -837,6 +749,30 @@ var WhaleStorefront = (function (exports) {
837
749
  onShowCOA();
838
750
  }, style: buttonStyle, children: buttonLabel }) });
839
751
  }
752
+ function COAModal({ coa, theme, onClose }) {
753
+ return /* @__PURE__ */ jsxs("div", { style: { position: "fixed", inset: 0, zIndex: 9999, background: "rgba(0,0,0,0.95)", display: "flex", flexDirection: "column" }, children: [
754
+ /* @__PURE__ */ jsxs("div", { style: {
755
+ display: "flex",
756
+ justifyContent: "space-between",
757
+ alignItems: "center",
758
+ padding: "0.75rem 1rem",
759
+ borderBottom: `1px solid ${theme.fg}10`
760
+ }, children: [
761
+ /* @__PURE__ */ jsx("span", { style: { color: "#fff", fontWeight: 500, fontSize: "0.85rem" }, children: coa.document_name || "Lab Results" }),
762
+ /* @__PURE__ */ jsx("button", { onClick: onClose, style: {
763
+ background: `${theme.fg}10`,
764
+ border: "none",
765
+ color: "#fff",
766
+ fontSize: "0.85rem",
767
+ cursor: "pointer",
768
+ padding: "0.375rem 0.75rem"
769
+ }, children: "Close" })
770
+ ] }),
771
+ /* @__PURE__ */ jsx("iframe", { src: coa.url, style: { flex: 1, border: "none", background: "#fff" }, title: "Lab Results" })
772
+ ] });
773
+ }
774
+
775
+ // src/react/components/sections/lead-capture-section.tsx
840
776
  function LeadCaptureSection({ section, data, theme, onEvent }) {
841
777
  const c = section.content;
842
778
  const [firstName, setFirstName] = useState("");
@@ -847,13 +783,17 @@ var WhaleStorefront = (function (exports) {
847
783
  const gatewayUrl = c.gateway_url || data.gatewayUrl || "https://whale-gateway.fly.dev";
848
784
  const storeId = c.store_id || data.store?.id;
849
785
  const slug = c.landing_page_slug || data.landing_page?.slug;
786
+ const heading = c.heading || "get 10% off your first visit.";
787
+ const subtitle = c.subtitle || "drop your email and we will send you the code.";
788
+ const buttonText = c.button_text || "Claim My Discount";
789
+ const successHeading = c.success_heading || "You\u2019re in!";
790
+ const successMessage = c.success_message || "Check your inbox for the discount code.";
850
791
  async function handleSubmit(e) {
851
792
  e.preventDefault();
852
793
  if (!email || !storeId) return;
853
794
  setStatus("loading");
854
795
  setErrorMsg("");
855
796
  const urlParams = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
856
- const analyticsData = data.analyticsContext;
857
797
  try {
858
798
  const res = await fetch(`${gatewayUrl}/v1/stores/${storeId}/storefront/leads`, {
859
799
  method: "POST",
@@ -869,8 +809,8 @@ var WhaleStorefront = (function (exports) {
869
809
  if (newsletterOptIn) t.push(c.newsletter_tag || "newsletter-subscriber");
870
810
  return t.length > 0 ? t : void 0;
871
811
  })(),
872
- visitor_id: analyticsData?.visitorId || void 0,
873
- session_id: analyticsData?.sessionId || void 0,
812
+ visitor_id: data.analyticsContext?.visitorId || void 0,
813
+ session_id: data.analyticsContext?.sessionId || void 0,
874
814
  utm_source: urlParams?.get("utm_source") || void 0,
875
815
  utm_medium: urlParams?.get("utm_medium") || void 0,
876
816
  utm_campaign: urlParams?.get("utm_campaign") || void 0,
@@ -888,11 +828,6 @@ var WhaleStorefront = (function (exports) {
888
828
  setStatus("error");
889
829
  }
890
830
  }
891
- const heading = c.heading || "get 10% off your first visit.";
892
- const subtitle = c.subtitle || "drop your email and we will send you the code.";
893
- const buttonText = c.button_text || "Claim My Discount";
894
- const successHeading = c.success_heading || "You\u2019re in!";
895
- const successMessage = c.success_message || "Check your inbox for the discount code.";
896
831
  const inputStyle = {
897
832
  flex: 1,
898
833
  minWidth: 0,
@@ -907,41 +842,10 @@ var WhaleStorefront = (function (exports) {
907
842
  fontFamily: "inherit",
908
843
  transition: "border-color 0.2s"
909
844
  };
845
+ if (status === "success") return /* @__PURE__ */ jsx(SuccessState, { theme, heading: successHeading, message: successMessage, couponCode: c.coupon_code });
910
846
  return /* @__PURE__ */ jsxs("div", { style: { padding: "3.5rem 1.5rem", maxWidth: 560, margin: "0 auto" }, children: [
911
847
  /* @__PURE__ */ jsx("style", { children: `@keyframes lc-spin { to { transform: rotate(360deg) } }` }),
912
- /* @__PURE__ */ jsx("div", { style: {
913
- background: theme.surface,
914
- border: `1px solid ${theme.fg}12`,
915
- padding: "clamp(2rem, 6vw, 3rem)"
916
- }, children: status === "success" ? /* @__PURE__ */ jsxs("div", { style: { textAlign: "center" }, children: [
917
- /* @__PURE__ */ jsx("h2", { style: {
918
- fontSize: "clamp(1.5rem, 5vw, 2rem)",
919
- fontWeight: 300,
920
- fontFamily: theme.fontDisplay || "inherit",
921
- margin: "0 0 0.75rem",
922
- lineHeight: 1.2,
923
- letterSpacing: "-0.02em",
924
- color: theme.fg
925
- }, children: successHeading }),
926
- /* @__PURE__ */ jsx("p", { style: {
927
- fontSize: "0.9rem",
928
- color: `${theme.fg}99`,
929
- margin: "0 0 1.5rem",
930
- lineHeight: 1.6,
931
- fontWeight: 300
932
- }, children: successMessage }),
933
- c.coupon_code && /* @__PURE__ */ jsx("div", { style: {
934
- display: "inline-block",
935
- padding: "0.75rem 2rem",
936
- background: `${theme.fg}08`,
937
- border: `1px dashed ${theme.fg}30`,
938
- fontSize: "clamp(1.25rem, 4vw, 1.75rem)",
939
- fontWeight: 500,
940
- fontFamily: "monospace",
941
- letterSpacing: "0.12em",
942
- color: theme.accent
943
- }, children: c.coupon_code })
944
- ] }) : /* @__PURE__ */ jsxs(Fragment2, { children: [
848
+ /* @__PURE__ */ jsxs("div", { style: { background: theme.surface, border: `1px solid ${theme.fg}12`, padding: "clamp(2rem, 6vw, 3rem)" }, children: [
945
849
  /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", marginBottom: "clamp(1.5rem, 4vw, 2rem)" }, children: [
946
850
  /* @__PURE__ */ jsx("h2", { style: {
947
851
  fontSize: "clamp(1.5rem, 5vw, 2.25rem)",
@@ -963,27 +867,8 @@ var WhaleStorefront = (function (exports) {
963
867
  ] }),
964
868
  /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
965
869
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.75rem", flexWrap: "wrap" }, children: [
966
- /* @__PURE__ */ jsx(
967
- "input",
968
- {
969
- type: "text",
970
- placeholder: "First name",
971
- value: firstName,
972
- onChange: (e) => setFirstName(e.target.value),
973
- style: inputStyle
974
- }
975
- ),
976
- /* @__PURE__ */ jsx(
977
- "input",
978
- {
979
- type: "email",
980
- placeholder: "Email address",
981
- value: email,
982
- onChange: (e) => setEmail(e.target.value),
983
- required: true,
984
- style: inputStyle
985
- }
986
- )
870
+ /* @__PURE__ */ jsx("input", { type: "text", placeholder: "First name", value: firstName, onChange: (e) => setFirstName(e.target.value), style: inputStyle }),
871
+ /* @__PURE__ */ jsx("input", { type: "email", placeholder: "Email address", value: email, onChange: (e) => setEmail(e.target.value), required: true, style: inputStyle })
987
872
  ] }),
988
873
  c.show_newsletter_opt_in !== false && /* @__PURE__ */ jsxs("label", { style: {
989
874
  display: "flex",
@@ -1001,123 +886,140 @@ var WhaleStorefront = (function (exports) {
1001
886
  type: "checkbox",
1002
887
  checked: newsletterOptIn,
1003
888
  onChange: (e) => setNewsletterOptIn(e.target.checked),
1004
- style: {
1005
- width: 16,
1006
- height: 16,
1007
- accentColor: theme.accent,
1008
- cursor: "pointer",
1009
- flexShrink: 0
1010
- }
889
+ style: { width: 16, height: 16, accentColor: theme.accent, cursor: "pointer", flexShrink: 0 }
1011
890
  }
1012
891
  ),
1013
892
  c.newsletter_label || "Also sign me up for the newsletter \u2014 new drops, deals, and company news."
1014
893
  ] }),
1015
894
  status === "error" && errorMsg && /* @__PURE__ */ jsx("p", { style: { fontSize: "0.8rem", color: "#e55", margin: 0, fontWeight: 400 }, children: errorMsg }),
1016
- /* @__PURE__ */ jsxs(
1017
- "button",
1018
- {
1019
- type: "submit",
1020
- disabled: status === "loading",
1021
- style: {
1022
- width: "100%",
1023
- padding: "0.875rem",
1024
- background: theme.fg,
1025
- color: theme.bg,
1026
- border: "none",
1027
- fontSize: "0.85rem",
1028
- fontWeight: 500,
1029
- cursor: status === "loading" ? "wait" : "pointer",
1030
- letterSpacing: "0.08em",
1031
- textTransform: "uppercase",
1032
- fontFamily: "inherit",
1033
- display: "flex",
1034
- alignItems: "center",
1035
- justifyContent: "center",
1036
- gap: "0.5rem",
1037
- opacity: status === "loading" ? 0.7 : 1,
1038
- transition: "opacity 0.2s"
1039
- },
1040
- children: [
1041
- status === "loading" && /* @__PURE__ */ jsx("span", { style: {
1042
- display: "inline-block",
1043
- width: 16,
1044
- height: 16,
1045
- border: `2px solid ${theme.bg}40`,
1046
- borderTopColor: theme.bg,
1047
- borderRadius: "50%",
1048
- animation: "lc-spin 0.8s linear infinite"
1049
- } }),
1050
- buttonText
1051
- ]
1052
- }
1053
- )
895
+ /* @__PURE__ */ jsxs("button", { type: "submit", disabled: status === "loading", style: {
896
+ width: "100%",
897
+ padding: "0.875rem",
898
+ background: theme.fg,
899
+ color: theme.bg,
900
+ border: "none",
901
+ fontSize: "0.85rem",
902
+ fontWeight: 500,
903
+ cursor: status === "loading" ? "wait" : "pointer",
904
+ letterSpacing: "0.08em",
905
+ textTransform: "uppercase",
906
+ fontFamily: "inherit",
907
+ display: "flex",
908
+ alignItems: "center",
909
+ justifyContent: "center",
910
+ gap: "0.5rem",
911
+ opacity: status === "loading" ? 0.7 : 1,
912
+ transition: "opacity 0.2s"
913
+ }, children: [
914
+ status === "loading" && /* @__PURE__ */ jsx("span", { style: {
915
+ display: "inline-block",
916
+ width: 16,
917
+ height: 16,
918
+ border: `2px solid ${theme.bg}40`,
919
+ borderTopColor: theme.bg,
920
+ borderRadius: "50%",
921
+ animation: "lc-spin 0.8s linear infinite"
922
+ } }),
923
+ buttonText
924
+ ] })
1054
925
  ] })
1055
- ] }) })
926
+ ] })
1056
927
  ] });
1057
928
  }
1058
- function SocialLinksSection({ section, theme }) {
1059
- const { links } = section.content;
1060
- if (!links || links.length === 0) return null;
1061
- return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", display: "flex", justifyContent: "center", gap: "1.5rem", flexWrap: "wrap" }, children: links.map((link, i) => /* @__PURE__ */ jsx(
1062
- "a",
1063
- {
1064
- href: link.url,
1065
- target: "_blank",
1066
- rel: "noopener noreferrer",
1067
- style: {
1068
- color: theme.muted,
1069
- textDecoration: "none",
1070
- fontSize: "0.85rem",
1071
- fontWeight: 500,
1072
- textTransform: "capitalize",
1073
- letterSpacing: "0.03em"
1074
- },
1075
- children: link.platform
1076
- },
1077
- i
1078
- )) });
1079
- }
1080
- function DividerSection({ theme }) {
1081
- 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 } }) });
929
+ function SuccessState({ theme, heading, message, couponCode }) {
930
+ 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: [
931
+ /* @__PURE__ */ jsx("h2", { style: {
932
+ fontSize: "clamp(1.5rem, 5vw, 2rem)",
933
+ fontWeight: 300,
934
+ fontFamily: theme.fontDisplay || "inherit",
935
+ margin: "0 0 0.75rem",
936
+ lineHeight: 1.2,
937
+ letterSpacing: "-0.02em",
938
+ color: theme.fg
939
+ }, children: heading }),
940
+ /* @__PURE__ */ jsx("p", { style: { fontSize: "0.9rem", color: `${theme.fg}99`, margin: "0 0 1.5rem", lineHeight: 1.6, fontWeight: 300 }, children: message }),
941
+ couponCode && /* @__PURE__ */ jsx("div", { style: {
942
+ display: "inline-block",
943
+ padding: "0.75rem 2rem",
944
+ background: `${theme.fg}08`,
945
+ border: `1px dashed ${theme.fg}30`,
946
+ fontSize: "clamp(1.25rem, 4vw, 1.75rem)",
947
+ fontWeight: 500,
948
+ fontFamily: "monospace",
949
+ letterSpacing: "0.12em",
950
+ color: theme.accent
951
+ }, children: couponCode })
952
+ ] }) });
1082
953
  }
1083
- function COAModal({ coa, theme, onClose }) {
1084
- return /* @__PURE__ */ jsxs("div", { style: { position: "fixed", inset: 0, zIndex: 9999, background: "rgba(0,0,0,0.95)", display: "flex", flexDirection: "column" }, children: [
1085
- /* @__PURE__ */ jsxs("div", { style: {
1086
- display: "flex",
1087
- justifyContent: "space-between",
1088
- alignItems: "center",
1089
- padding: "0.75rem 1rem",
1090
- borderBottom: `1px solid ${theme.fg}10`
1091
- }, children: [
1092
- /* @__PURE__ */ jsx("span", { style: { color: "#fff", fontWeight: 500, fontSize: "0.85rem" }, children: coa.document_name || "Lab Results" }),
1093
- /* @__PURE__ */ jsx(
1094
- "button",
1095
- {
1096
- onClick: onClose,
1097
- style: {
1098
- background: `${theme.fg}10`,
1099
- border: "none",
1100
- color: "#fff",
1101
- fontSize: "0.85rem",
1102
- cursor: "pointer",
1103
- padding: "0.375rem 0.75rem"
1104
- },
1105
- children: "Close"
954
+
955
+ // src/react/components/section-renderer.tsx
956
+ function SectionRenderer({
957
+ section,
958
+ data,
959
+ theme,
960
+ tracking,
961
+ onEvent
962
+ }) {
963
+ const [showCOA, setShowCOA] = useState(false);
964
+ const el = (() => {
965
+ switch (section.type) {
966
+ case "hero":
967
+ return /* @__PURE__ */ jsx(HeroSection, { section, theme, tracking, onEvent });
968
+ case "text":
969
+ return /* @__PURE__ */ jsx(TextSection, { section, theme });
970
+ case "image":
971
+ return /* @__PURE__ */ jsx(ImageSection, { section, theme });
972
+ case "video":
973
+ return /* @__PURE__ */ jsx(VideoSection, { section, theme });
974
+ case "gallery":
975
+ return /* @__PURE__ */ jsx(GallerySection, { section, theme });
976
+ case "cta":
977
+ return /* @__PURE__ */ jsx(CTASection, { section, theme, tracking, onEvent });
978
+ case "stats":
979
+ return /* @__PURE__ */ jsx(StatsSection, { section, theme });
980
+ case "product_card":
981
+ return /* @__PURE__ */ jsx(ProductCardSection, { section, data, theme, tracking });
982
+ case "coa_viewer":
983
+ return /* @__PURE__ */ jsx(COAViewerSection, { section, data, theme, onShowCOA: () => setShowCOA(true), tracking });
984
+ case "social_links":
985
+ return /* @__PURE__ */ jsx(SocialLinksSection, { section, theme });
986
+ case "lead_capture":
987
+ return /* @__PURE__ */ jsx(LeadCaptureSection, { section, data, theme, onEvent });
988
+ case "divider":
989
+ return /* @__PURE__ */ jsx(DividerSection, { theme });
990
+ default:
991
+ return null;
992
+ }
993
+ })();
994
+ const sectionRef = useRef(null);
995
+ useEffect(() => {
996
+ const el2 = sectionRef.current;
997
+ if (!el2 || typeof IntersectionObserver === "undefined") return;
998
+ const obs = new IntersectionObserver(
999
+ ([entry]) => {
1000
+ if (entry.isIntersecting) {
1001
+ onEvent?.("section_view", { section_id: section.id, section_type: section.type });
1002
+ obs.disconnect();
1106
1003
  }
1107
- )
1108
- ] }),
1109
- /* @__PURE__ */ jsx("iframe", { src: coa.url, style: { flex: 1, border: "none", background: "#fff" }, title: "Lab Results" })
1004
+ },
1005
+ { threshold: 0.5 }
1006
+ );
1007
+ obs.observe(el2);
1008
+ return () => obs.disconnect();
1009
+ }, [section.id, section.type, onEvent]);
1010
+ return /* @__PURE__ */ jsxs("div", { ref: sectionRef, "data-section-id": section.id, "data-section-type": section.type, children: [
1011
+ el,
1012
+ showCOA && data?.coa && /* @__PURE__ */ jsx(COAModal, { coa: data.coa, theme, onClose: () => setShowCOA(false) })
1110
1013
  ] });
1111
1014
  }
1112
- function toEmbedUrl(url) {
1113
- const ytMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)/);
1114
- if (ytMatch) return `https://www.youtube.com/embed/${ytMatch[1]}`;
1115
- const vimeoMatch = url.match(/vimeo\.com\/(\d+)/);
1116
- if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}`;
1117
- return url;
1118
- }
1119
1015
 
1120
1016
  // src/react/components/landing-page.tsx
1017
+ function getInlinedData() {
1018
+ if (typeof window !== "undefined" && window.__LANDING_DATA__) {
1019
+ return window.__LANDING_DATA__;
1020
+ }
1021
+ return null;
1022
+ }
1121
1023
  function LandingPage({
1122
1024
  slug,
1123
1025
  gatewayUrl = "https://whale-gateway.fly.dev",
@@ -1128,47 +1030,43 @@ var WhaleStorefront = (function (exports) {
1128
1030
  analyticsContext,
1129
1031
  enableAnalytics = true
1130
1032
  }) {
1131
- const [state, setState] = useState("loading");
1132
- const [data, setData] = useState(null);
1033
+ const inlined = useRef(getInlinedData()).current;
1034
+ const [state, setState] = useState(inlined ? "ready" : "loading");
1035
+ const [data, setData] = useState(inlined);
1133
1036
  const [errorMsg, setErrorMsg] = useState("");
1134
1037
  useEffect(() => {
1135
1038
  if (!slug) return;
1136
- let cancelled = false;
1137
- if (typeof window !== "undefined" && window.__LANDING_DATA__) {
1138
- const json = window.__LANDING_DATA__;
1139
- setData(json);
1140
- setState("ready");
1141
- onDataLoaded?.(json);
1039
+ if (data) {
1040
+ onDataLoaded?.(data);
1142
1041
  return;
1143
1042
  }
1043
+ let cancelled = false;
1144
1044
  async function load() {
1145
1045
  try {
1146
1046
  const res = await fetch(`${gatewayUrl}/l/${encodeURIComponent(slug)}`);
1147
- if (!cancelled) {
1148
- if (res.status === 404) {
1149
- setState("not_found");
1150
- return;
1151
- }
1152
- if (res.status === 410) {
1153
- setState("expired");
1154
- return;
1155
- }
1156
- if (!res.ok) {
1157
- const body = await res.json().catch(() => ({}));
1158
- throw new Error(body?.error?.message ?? `Failed to load: ${res.status}`);
1159
- }
1160
- const json = await res.json();
1161
- setData(json);
1162
- setState("ready");
1163
- onDataLoaded?.(json);
1047
+ if (cancelled) return;
1048
+ if (res.status === 404) {
1049
+ setState("not_found");
1050
+ return;
1164
1051
  }
1165
- } catch (err) {
1166
- if (!cancelled) {
1167
- const e = err instanceof Error ? err : new Error(String(err));
1168
- setErrorMsg(e.message);
1169
- setState("error");
1170
- onError?.(e);
1052
+ if (res.status === 410) {
1053
+ setState("expired");
1054
+ return;
1055
+ }
1056
+ if (!res.ok) {
1057
+ const body = await res.json().catch(() => ({}));
1058
+ throw new Error(body?.error?.message ?? `Failed to load: ${res.status}`);
1171
1059
  }
1060
+ const json = await res.json();
1061
+ setData(json);
1062
+ setState("ready");
1063
+ onDataLoaded?.(json);
1064
+ } catch (err) {
1065
+ if (cancelled) return;
1066
+ const e = err instanceof Error ? err : new Error(String(err));
1067
+ setErrorMsg(e.message);
1068
+ setState("error");
1069
+ onError?.(e);
1172
1070
  }
1173
1071
  }
1174
1072
  load();
@@ -1176,12 +1074,22 @@ var WhaleStorefront = (function (exports) {
1176
1074
  cancelled = true;
1177
1075
  };
1178
1076
  }, [slug, gatewayUrl]);
1179
- if (state === "loading") return /* @__PURE__ */ jsx(DefaultLoading, {});
1180
- if (state === "not_found") return /* @__PURE__ */ jsx(DefaultNotFound, {});
1181
- if (state === "expired") return /* @__PURE__ */ jsx(DefaultExpired, {});
1182
- if (state === "error") return /* @__PURE__ */ jsx(DefaultError, { message: errorMsg });
1077
+ if (state === "loading") return /* @__PURE__ */ jsx(StateScreen, { title: "", loading: true });
1078
+ if (state === "not_found") return /* @__PURE__ */ jsx(StateScreen, { title: "Page Not Found", subtitle: "This page does not exist or has been removed." });
1079
+ if (state === "expired") return /* @__PURE__ */ jsx(StateScreen, { title: "Page Expired", subtitle: "This page is no longer active." });
1080
+ if (state === "error") return /* @__PURE__ */ jsx(StateScreen, { title: "Something Went Wrong", subtitle: errorMsg || "Please try again later." });
1183
1081
  if (!data) return null;
1184
- return /* @__PURE__ */ jsx(PageLayout, { data, gatewayUrl, renderSection, onEvent, analyticsContext, enableAnalytics });
1082
+ return /* @__PURE__ */ jsx(
1083
+ PageLayout,
1084
+ {
1085
+ data,
1086
+ gatewayUrl,
1087
+ renderSection,
1088
+ onEvent,
1089
+ analyticsContext,
1090
+ enableAnalytics
1091
+ }
1092
+ );
1185
1093
  }
1186
1094
  function isSectionVisible(section, urlParams) {
1187
1095
  const vis = section.config?.visibility;
@@ -1204,8 +1112,8 @@ var WhaleStorefront = (function (exports) {
1204
1112
  const trackerRef = useRef(null);
1205
1113
  useEffect(() => {
1206
1114
  if (!enableAnalytics || typeof window === "undefined") return;
1207
- const analyticsConfig = window.__LANDING_ANALYTICS__;
1208
- if (!analyticsConfig?.slug) return;
1115
+ const config = window.__LANDING_ANALYTICS__;
1116
+ if (!config?.slug) return;
1209
1117
  let visitorId = localStorage.getItem("wt_vid") || "";
1210
1118
  if (!visitorId) {
1211
1119
  visitorId = crypto.randomUUID();
@@ -1217,8 +1125,8 @@ var WhaleStorefront = (function (exports) {
1217
1125
  sessionStorage.setItem("wt_sid", sessionId);
1218
1126
  }
1219
1127
  Promise.resolve().then(() => (init_tracker(), tracker_exports)).then(({ BehavioralTracker: BehavioralTracker2 }) => {
1220
- const gwUrl = analyticsConfig.gatewayUrl || gatewayUrl;
1221
- const slug = analyticsConfig.slug;
1128
+ const gwUrl = config.gatewayUrl || gatewayUrl;
1129
+ const slug = config.slug;
1222
1130
  const utmParams = new URLSearchParams(window.location.search);
1223
1131
  const tracker = new BehavioralTracker2({
1224
1132
  sessionId,
@@ -1229,65 +1137,44 @@ var WhaleStorefront = (function (exports) {
1229
1137
  event_data: e.data,
1230
1138
  session_id: batch.session_id,
1231
1139
  visitor_id: batch.visitor_id,
1232
- campaign_id: analyticsConfig.campaignId || utmParams.get("utm_campaign_id") || void 0,
1140
+ campaign_id: config.campaignId || utmParams.get("utm_campaign_id") || void 0,
1233
1141
  utm_source: utmParams.get("utm_source") || void 0,
1234
1142
  utm_medium: utmParams.get("utm_medium") || void 0,
1235
1143
  utm_campaign: utmParams.get("utm_campaign") || void 0
1236
1144
  }));
1237
- const body = JSON.stringify({ events });
1238
- if (typeof navigator !== "undefined" && navigator.sendBeacon) {
1239
- navigator.sendBeacon(
1240
- `${gwUrl}/l/${encodeURIComponent(slug)}/events`,
1241
- new Blob([body], { type: "application/json" })
1242
- );
1243
- } else {
1244
- await fetch(`${gwUrl}/l/${encodeURIComponent(slug)}/events`, {
1245
- method: "POST",
1246
- headers: { "Content-Type": "application/json" },
1247
- body,
1248
- keepalive: true
1249
- });
1250
- }
1145
+ sendEvents(gwUrl, slug, events);
1251
1146
  }
1252
1147
  });
1253
1148
  tracker.setPageContext(window.location.href, window.location.pathname);
1254
1149
  tracker.start();
1255
1150
  trackerRef.current = tracker;
1256
- const pageViewBody = JSON.stringify({
1257
- events: [{
1258
- event_type: "page_view",
1259
- event_data: { referrer: document.referrer, url: window.location.href },
1260
- session_id: sessionId,
1261
- visitor_id: visitorId,
1262
- campaign_id: analyticsConfig.campaignId || void 0,
1263
- utm_source: utmParams.get("utm_source") || void 0,
1264
- utm_medium: utmParams.get("utm_medium") || void 0,
1265
- utm_campaign: utmParams.get("utm_campaign") || void 0
1266
- }]
1267
- });
1268
- if (navigator.sendBeacon) {
1269
- navigator.sendBeacon(
1270
- `${gwUrl}/l/${encodeURIComponent(slug)}/events`,
1271
- new Blob([pageViewBody], { type: "application/json" })
1272
- );
1273
- } else {
1274
- fetch(`${gwUrl}/l/${encodeURIComponent(slug)}/events`, {
1275
- method: "POST",
1276
- headers: { "Content-Type": "application/json" },
1277
- body: pageViewBody,
1278
- keepalive: true
1279
- }).catch(() => {
1280
- });
1281
- }
1151
+ sendEvents(gwUrl, slug, [{
1152
+ event_type: "page_view",
1153
+ event_data: { referrer: document.referrer, url: window.location.href },
1154
+ session_id: sessionId,
1155
+ visitor_id: visitorId,
1156
+ campaign_id: config.campaignId || void 0
1157
+ }]);
1282
1158
  }).catch(() => {
1283
1159
  });
1284
1160
  return () => {
1285
- if (trackerRef.current) {
1286
- trackerRef.current.stop();
1287
- trackerRef.current = null;
1288
- }
1161
+ trackerRef.current?.stop();
1162
+ trackerRef.current = null;
1289
1163
  };
1290
1164
  }, [enableAnalytics, gatewayUrl]);
1165
+ const handleEvent = useCallback((event, eventData) => {
1166
+ onEvent?.(event, eventData);
1167
+ if (!enableAnalytics || typeof window === "undefined") return;
1168
+ const config = window.__LANDING_ANALYTICS__;
1169
+ if (!config?.slug) return;
1170
+ sendEvents(config.gatewayUrl || gatewayUrl, config.slug, [{
1171
+ event_type: event,
1172
+ event_data: eventData,
1173
+ session_id: sessionStorage.getItem("wt_sid") || void 0,
1174
+ visitor_id: localStorage.getItem("wt_vid") || void 0,
1175
+ campaign_id: config.campaignId || void 0
1176
+ }]);
1177
+ }, [onEvent, enableAnalytics, gatewayUrl]);
1291
1178
  const theme = {
1292
1179
  bg: lp.background_color || store?.theme?.background || "#050505",
1293
1180
  fg: lp.text_color || store?.theme?.foreground || "#fafafa",
@@ -1306,11 +1193,9 @@ var WhaleStorefront = (function (exports) {
1306
1193
  lp.custom_css && /* @__PURE__ */ jsx("style", { children: lp.custom_css }),
1307
1194
  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" } }) }),
1308
1195
  sorted.map((section) => {
1309
- const defaultRenderer = () => /* @__PURE__ */ jsx(SectionRenderer, { section, data: sectionData, theme, onEvent }, section.id);
1310
- if (renderSection) {
1311
- return /* @__PURE__ */ jsx("div", { children: renderSection(section, defaultRenderer) }, section.id);
1312
- }
1313
- return /* @__PURE__ */ jsx(SectionRenderer, { section, data: sectionData, theme, onEvent }, section.id);
1196
+ const defaultRenderer = () => /* @__PURE__ */ jsx(SectionRenderer, { section, data: sectionData, theme, onEvent: handleEvent }, section.id);
1197
+ if (renderSection) return /* @__PURE__ */ jsx("div", { children: renderSection(section, defaultRenderer) }, section.id);
1198
+ return /* @__PURE__ */ jsx(SectionRenderer, { section, data: sectionData, theme, onEvent: handleEvent }, section.id);
1314
1199
  }),
1315
1200
  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: [
1316
1201
  "Powered by ",
@@ -1318,7 +1203,17 @@ var WhaleStorefront = (function (exports) {
1318
1203
  ] }) })
1319
1204
  ] });
1320
1205
  }
1321
- var containerStyle = {
1206
+ function sendEvents(gwUrl, slug, events) {
1207
+ const body = JSON.stringify({ events });
1208
+ const url = `${gwUrl}/l/${encodeURIComponent(slug)}/events`;
1209
+ if (typeof navigator !== "undefined" && navigator.sendBeacon) {
1210
+ navigator.sendBeacon(url, new Blob([body], { type: "application/json" }));
1211
+ } else {
1212
+ fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body, keepalive: true }).catch(() => {
1213
+ });
1214
+ }
1215
+ }
1216
+ var screenStyle = {
1322
1217
  minHeight: "100dvh",
1323
1218
  display: "flex",
1324
1219
  justifyContent: "center",
@@ -1329,28 +1224,14 @@ var WhaleStorefront = (function (exports) {
1329
1224
  textAlign: "center",
1330
1225
  padding: "2rem"
1331
1226
  };
1332
- function DefaultLoading() {
1333
- return /* @__PURE__ */ jsx("div", { style: containerStyle, children: /* @__PURE__ */ jsxs("div", { children: [
1334
- /* @__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" } }),
1335
- /* @__PURE__ */ jsx("style", { children: `@keyframes spin { to { transform: rotate(360deg) } }` })
1336
- ] }) });
1337
- }
1338
- function DefaultNotFound() {
1339
- return /* @__PURE__ */ jsx("div", { style: containerStyle, children: /* @__PURE__ */ jsxs("div", { children: [
1340
- /* @__PURE__ */ jsx("h1", { style: { fontSize: "1.5rem", marginBottom: "0.5rem" }, children: "Page Not Found" }),
1341
- /* @__PURE__ */ jsx("p", { style: { color: "#888" }, children: "This page does not exist or has been removed." })
1342
- ] }) });
1343
- }
1344
- function DefaultExpired() {
1345
- return /* @__PURE__ */ jsx("div", { style: containerStyle, children: /* @__PURE__ */ jsxs("div", { children: [
1346
- /* @__PURE__ */ jsx("h1", { style: { fontSize: "1.5rem", marginBottom: "0.5rem" }, children: "Page Expired" }),
1347
- /* @__PURE__ */ jsx("p", { style: { color: "#888" }, children: "This page is no longer active." })
1348
- ] }) });
1349
- }
1350
- function DefaultError({ message }) {
1351
- return /* @__PURE__ */ jsx("div", { style: containerStyle, children: /* @__PURE__ */ jsxs("div", { children: [
1352
- /* @__PURE__ */ jsx("h1", { style: { fontSize: "1.5rem", marginBottom: "0.5rem" }, children: "Something Went Wrong" }),
1353
- /* @__PURE__ */ jsx("p", { style: { color: "#888" }, children: message || "Please try again later." })
1227
+ function StateScreen({ title, subtitle, loading }) {
1228
+ return /* @__PURE__ */ jsx("div", { style: screenStyle, children: /* @__PURE__ */ jsxs("div", { children: [
1229
+ loading && /* @__PURE__ */ jsxs(Fragment2, { children: [
1230
+ /* @__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" } }),
1231
+ /* @__PURE__ */ jsx("style", { children: `@keyframes spin { to { transform: rotate(360deg) } }` })
1232
+ ] }),
1233
+ title && /* @__PURE__ */ jsx("h1", { style: { fontSize: "1.5rem", marginBottom: "0.5rem" }, children: title }),
1234
+ subtitle && /* @__PURE__ */ jsx("p", { style: { color: "#888" }, children: subtitle })
1354
1235
  ] }) });
1355
1236
  }
1356
1237