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