@neowhale/storefront 0.2.53 → 0.2.55

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.
@@ -469,8 +469,57 @@ var WhaleStorefront = (function (exports) {
469
469
  }
470
470
 
471
471
  // src/react/components/sections/content-sections.tsx
472
- function HeroSection({ section, theme, tracking, onEvent }) {
473
- const { title, subtitle, background_image, cta_text, cta_url, review_line } = section.content;
472
+ var GOOGLE_G = '<svg width="18" height="18" viewBox="0 0 48 48"><path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/><path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/><path fill="#FBBC05" d="M10.53 28.59a14.5 14.5 0 010-9.18l-7.98-6.19a24.03 24.03 0 000 21.56l7.98-6.19z"/><path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/></svg>';
473
+ function HeroSection({ section, theme, tracking, onEvent, data }) {
474
+ const c = section.content;
475
+ const [formOpen, setFormOpen] = useState(false);
476
+ const [firstName, setFirstName] = useState("");
477
+ const [email, setEmail] = useState("");
478
+ const [status, setStatus] = useState("idle");
479
+ const [errorMsg, setErrorMsg] = useState("");
480
+ const gatewayUrl = data?.gatewayUrl || "https://whale-gateway.fly.dev";
481
+ const storeId = data?.store?.id;
482
+ const slug = data?.landing_page?.slug;
483
+ async function handleSubmit(e) {
484
+ e.preventDefault();
485
+ if (!email || !storeId) return;
486
+ setStatus("loading");
487
+ setErrorMsg("");
488
+ const urlParams = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
489
+ try {
490
+ const res = await fetch(`${gatewayUrl}/v1/stores/${storeId}/storefront/leads`, {
491
+ method: "POST",
492
+ headers: { "Content-Type": "application/json" },
493
+ body: JSON.stringify({
494
+ email,
495
+ first_name: firstName || void 0,
496
+ source: c.inline_form?.source || "landing_page",
497
+ landing_page_slug: slug || void 0,
498
+ tags: c.inline_form?.tags || void 0,
499
+ segment_id: c.inline_form?.segment_id || void 0,
500
+ campaign_id: c.inline_form?.campaign_id || void 0,
501
+ preferred_location_id: c.inline_form?.preferred_location_id || void 0,
502
+ template_slug: c.inline_form?.template_slug || void 0,
503
+ acquisition_campaign: c.inline_form?.acquisition_campaign || void 0,
504
+ visitor_id: data?.analyticsContext?.visitorId || void 0,
505
+ session_id: data?.analyticsContext?.sessionId || void 0,
506
+ utm_source: urlParams?.get("utm_source") || void 0,
507
+ utm_medium: urlParams?.get("utm_medium") || void 0,
508
+ utm_campaign: urlParams?.get("utm_campaign") || void 0
509
+ })
510
+ });
511
+ if (!res.ok) {
512
+ const body = await res.json().catch(() => ({}));
513
+ throw new Error(body?.error?.message || "something went wrong.");
514
+ }
515
+ setStatus("success");
516
+ onEvent?.("lead", { email, first_name: firstName || void 0, source: c.inline_form?.source || "landing_page" });
517
+ } catch (err) {
518
+ setErrorMsg(err instanceof Error ? err.message : "something went wrong.");
519
+ setStatus("error");
520
+ }
521
+ }
522
+ const hasInlineForm = !!c.inline_form;
474
523
  return /* @__PURE__ */ jsx("div", { style: {
475
524
  position: "relative",
476
525
  minHeight: "60vh",
@@ -480,36 +529,56 @@ var WhaleStorefront = (function (exports) {
480
529
  alignItems: "center",
481
530
  textAlign: "center",
482
531
  padding: "3rem 1.5rem",
483
- backgroundImage: background_image ? `url(${background_image})` : void 0,
532
+ backgroundImage: c.background_image ? `url(${c.background_image})` : void 0,
484
533
  backgroundSize: "cover",
485
534
  backgroundPosition: "center"
486
535
  }, children: [
487
- background_image && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" } }),
488
- /* @__PURE__ */ jsx("div", { style: { position: "relative", zIndex: 1, maxWidth: 640 }, children: [
489
- title && /* @__PURE__ */ jsx("h1", { style: {
490
- fontSize: "clamp(2rem, 8vw, 3rem)",
536
+ c.background_image && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" } }),
537
+ /* @__PURE__ */ jsx("div", { style: { position: "relative", zIndex: 1, maxWidth: 640, width: "100%" }, children: [
538
+ c.title && /* @__PURE__ */ jsx("h1", { style: {
539
+ fontSize: "clamp(2rem, 7vw, 3.25rem)",
491
540
  fontWeight: 300,
492
541
  fontFamily: theme.fontDisplay || "inherit",
493
- margin: "0 0 1rem",
494
- lineHeight: 1.15,
495
- letterSpacing: "-0.02em",
542
+ margin: "0 0 1.25rem",
543
+ lineHeight: 1.08,
544
+ letterSpacing: "-0.03em",
496
545
  color: theme.fg
497
- }, children: /* @__PURE__ */ jsx(AnimatedText, { text: title }) }),
498
- subtitle && /* @__PURE__ */ jsx("p", { style: {
499
- fontSize: "0.85rem",
546
+ }, children: /* @__PURE__ */ jsx(AnimatedText, { text: c.title }) }),
547
+ c.subtitle && /* @__PURE__ */ jsx("p", { style: {
548
+ fontSize: "clamp(0.9rem, 2.5vw, 1.1rem)",
500
549
  color: theme.accent,
501
550
  margin: "0 0 2rem",
502
551
  lineHeight: 1.6,
503
552
  textTransform: "uppercase",
504
- letterSpacing: "0.15em"
505
- }, children: subtitle }),
506
- cta_text && cta_url && /* @__PURE__ */ jsx(
553
+ letterSpacing: "0.1em"
554
+ }, children: c.subtitle }),
555
+ hasInlineForm ? /* @__PURE__ */ jsx(
556
+ HeroInlineForm,
557
+ {
558
+ ctaText: c.cta_text || "claim it",
559
+ formOpen,
560
+ setFormOpen,
561
+ firstName,
562
+ setFirstName,
563
+ email,
564
+ setEmail,
565
+ status,
566
+ errorMsg,
567
+ onSubmit: handleSubmit,
568
+ successHeading: c.inline_form.success_heading || "you're in.",
569
+ successMessage: c.inline_form.success_message || "check your inbox.",
570
+ submitText: c.inline_form.button_text || "send my code",
571
+ theme,
572
+ onEvent,
573
+ tracking
574
+ }
575
+ ) : c.cta_text && c.cta_url ? /* @__PURE__ */ jsx(
507
576
  "a",
508
577
  {
509
- href: cta_url,
578
+ href: c.cta_url,
510
579
  onClick: () => {
511
- trackClick(tracking, cta_text, cta_url);
512
- onEvent?.("cta_click", { label: cta_text, url: cta_url });
580
+ trackClick(tracking, c.cta_text, c.cta_url);
581
+ onEvent?.("cta_click", { label: c.cta_text, url: c.cta_url });
513
582
  },
514
583
  style: {
515
584
  display: "inline-block",
@@ -522,13 +591,114 @@ var WhaleStorefront = (function (exports) {
522
591
  letterSpacing: "0.08em",
523
592
  textTransform: "uppercase"
524
593
  },
525
- children: cta_text
594
+ children: c.cta_text
526
595
  }
527
- ),
528
- review_line && review_line.count > 0 && /* @__PURE__ */ jsx(ReviewLine, { rating: review_line.rating, count: review_line.count, source: review_line.source, url: review_line.url, accent: theme.accent, fg: theme.fg })
596
+ ) : null,
597
+ c.review_line && c.review_line.count > 0 && /* @__PURE__ */ jsx(ReviewLine, { rating: c.review_line.rating, count: c.review_line.count, source: c.review_line.source, url: c.review_line.url, accent: theme.accent, fg: theme.fg })
529
598
  ] })
530
599
  ] });
531
600
  }
601
+ function HeroInlineForm({ ctaText, formOpen, setFormOpen, firstName, setFirstName, email, setEmail, status, errorMsg, onSubmit, successHeading, successMessage, submitText, theme, onEvent, tracking }) {
602
+ const formMaxW = "min(480px, 90vw)";
603
+ if (status === "success") {
604
+ return /* @__PURE__ */ jsx("div", { style: { maxWidth: formMaxW, margin: "0 auto", padding: "1.5rem 2rem", background: `${theme.fg}06`, border: `1px solid ${theme.fg}10` }, children: [
605
+ /* @__PURE__ */ jsx("p", { style: { fontSize: "clamp(1rem, 3vw, 1.25rem)", fontWeight: 300, color: theme.fg, margin: "0 0 0.375rem", fontFamily: theme.fontDisplay || "inherit" }, children: successHeading }),
606
+ /* @__PURE__ */ jsx("p", { style: { fontSize: "0.85rem", color: `${theme.fg}70`, margin: 0, lineHeight: 1.5 }, children: successMessage })
607
+ ] });
608
+ }
609
+ if (!formOpen) {
610
+ return /* @__PURE__ */ jsx(
611
+ "button",
612
+ {
613
+ onClick: () => {
614
+ setFormOpen(true);
615
+ trackClick(tracking, ctaText, "#form");
616
+ onEvent?.("cta_click", { label: ctaText });
617
+ },
618
+ style: {
619
+ display: "inline-block",
620
+ padding: "clamp(0.875rem, 2.5vw, 1.125rem) clamp(2rem, 5vw, 3rem)",
621
+ background: theme.fg,
622
+ color: theme.bg,
623
+ border: "none",
624
+ cursor: "pointer",
625
+ fontSize: "clamp(0.85rem, 2vw, 1rem)",
626
+ fontWeight: 500,
627
+ letterSpacing: "0.06em",
628
+ textTransform: "uppercase",
629
+ fontFamily: "inherit"
630
+ },
631
+ children: ctaText
632
+ }
633
+ );
634
+ }
635
+ const inputBase = {
636
+ padding: "clamp(0.75rem, 2vw, 0.875rem) clamp(0.75rem, 2vw, 1rem)",
637
+ background: theme.surface,
638
+ border: `1px solid ${theme.fg}20`,
639
+ color: theme.fg,
640
+ fontSize: "clamp(0.8rem, 2vw, 0.9rem)",
641
+ fontWeight: 300,
642
+ outline: "none",
643
+ fontFamily: "inherit",
644
+ boxSizing: "border-box",
645
+ width: "100%"
646
+ };
647
+ return /* @__PURE__ */ jsx("form", { onSubmit, style: { maxWidth: formMaxW, margin: "0 auto", width: "100%" }, children: [
648
+ /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: `
649
+ .hero-form-row{display:flex;gap:0}
650
+ @media(max-width:480px){.hero-form-row{flex-direction:column;gap:0.5rem}}
651
+ ` } }),
652
+ /* @__PURE__ */ jsx("div", { className: "hero-form-row", children: [
653
+ /* @__PURE__ */ jsx(
654
+ "input",
655
+ {
656
+ type: "text",
657
+ placeholder: "first name",
658
+ value: firstName,
659
+ autoFocus: true,
660
+ onChange: (e) => setFirstName(e.target.value),
661
+ style: { ...inputBase, flex: 1, minWidth: 0, borderRight: "none" }
662
+ }
663
+ ),
664
+ /* @__PURE__ */ jsx(
665
+ "input",
666
+ {
667
+ type: "email",
668
+ placeholder: "email",
669
+ value: email,
670
+ required: true,
671
+ onChange: (e) => setEmail(e.target.value),
672
+ style: { ...inputBase, flex: 1.2, minWidth: 0 }
673
+ }
674
+ )
675
+ ] }),
676
+ /* @__PURE__ */ jsx(
677
+ "button",
678
+ {
679
+ type: "submit",
680
+ disabled: status === "loading",
681
+ style: {
682
+ width: "100%",
683
+ padding: "clamp(0.75rem, 2vw, 0.875rem)",
684
+ background: theme.fg,
685
+ color: theme.bg,
686
+ border: "none",
687
+ fontSize: "clamp(0.8rem, 2vw, 0.85rem)",
688
+ fontWeight: 600,
689
+ letterSpacing: "0.08em",
690
+ textTransform: "uppercase",
691
+ cursor: status === "loading" ? "wait" : "pointer",
692
+ fontFamily: "inherit",
693
+ marginTop: "0.5rem",
694
+ opacity: status === "loading" ? 0.7 : 1
695
+ },
696
+ children: status === "loading" ? "..." : submitText
697
+ }
698
+ ),
699
+ status === "error" && errorMsg && /* @__PURE__ */ jsx("p", { style: { fontSize: "0.75rem", color: "#e55", margin: "0.5rem 0 0", textAlign: "left" }, children: errorMsg })
700
+ ] });
701
+ }
532
702
  function TextSection({ section, theme }) {
533
703
  const { heading, body } = section.content;
534
704
  const align = section.config?.align || "left";
@@ -630,29 +800,100 @@ var WhaleStorefront = (function (exports) {
630
800
  ] })
631
801
  ] });
632
802
  }
633
- function GallerySection({ section, theme }) {
634
- const { images } = section.content;
803
+ function GallerySection({ section, theme, onEvent }) {
804
+ const c = section.content;
805
+ const images = c.images || [];
635
806
  const columns = section.config?.columns || 3;
636
807
  const layout = section.config?.layout || "grid";
808
+ const loc = c.location_card;
637
809
  if (!images || images.length === 0) return null;
638
810
  if (layout === "collage") {
639
- return /* @__PURE__ */ jsx("div", { style: { padding: "0.375rem", maxWidth: 900, margin: "0 auto" }, children: /* @__PURE__ */ jsx("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "0.375rem" }, children: images.map((img, i) => /* @__PURE__ */ jsx("div", { style: {
640
- gridColumn: i === 0 ? "1 / -1" : void 0,
641
- aspectRatio: i === 0 ? "16/9" : i % 3 === 0 ? "3/4" : "1",
642
- overflow: "hidden",
643
- background: theme.surface
644
- }, children: /* @__PURE__ */ jsx(
645
- "img",
646
- {
647
- src: img.url,
648
- alt: img.alt || "",
649
- loading: i < 2 ? "eager" : "lazy",
650
- style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
651
- }
652
- ) }, i)) }) });
811
+ return /* @__PURE__ */ jsx("div", { style: { padding: "0.375rem", maxWidth: 900, margin: "0 auto" }, children: [
812
+ loc && /* @__PURE__ */ jsx("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "0.375rem", marginBottom: "0.375rem" }, children: [
813
+ /* @__PURE__ */ jsx(GalleryLocationCard, { loc, theme, onEvent }),
814
+ /* @__PURE__ */ jsx("div", { style: { aspectRatio: "4/5", overflow: "hidden", background: theme.surface }, children: /* @__PURE__ */ jsx(
815
+ "img",
816
+ {
817
+ src: images[0].url,
818
+ alt: images[0].alt || "",
819
+ loading: "eager",
820
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
821
+ }
822
+ ) })
823
+ ] }),
824
+ /* @__PURE__ */ jsx("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "0.375rem" }, children: images.slice(loc ? 1 : 0).map((img, i) => /* @__PURE__ */ jsx("div", { style: {
825
+ gridColumn: !loc && i === 0 ? "1 / -1" : void 0,
826
+ aspectRatio: !loc && i === 0 ? "16/9" : i % 3 === 0 ? "3/4" : "1",
827
+ overflow: "hidden",
828
+ background: theme.surface
829
+ }, children: /* @__PURE__ */ jsx(
830
+ "img",
831
+ {
832
+ src: img.url,
833
+ alt: img.alt || "",
834
+ loading: i < 2 ? "eager" : "lazy",
835
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
836
+ }
837
+ ) }, i)) })
838
+ ] });
653
839
  }
654
840
  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)) }) });
655
841
  }
842
+ function GalleryLocationCard({ loc, theme, onEvent }) {
843
+ const dirUrl = loc.directions_url || `https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent(loc.address + ", " + loc.city + ", " + loc.state)}`;
844
+ const brandFont = loc.brand_font || "inherit";
845
+ return /* @__PURE__ */ jsx("div", { style: { background: "#0C0C0C", border: "1px solid rgba(255,255,255,0.06)", overflow: "hidden", display: "flex", flexDirection: "column" }, children: [
846
+ /* @__PURE__ */ jsx("div", { style: { position: "relative", flex: 1, minHeight: 0, overflow: "hidden" }, children: [
847
+ /* @__PURE__ */ jsx("img", { src: loc.image, alt: loc.name, style: { width: "100%", height: "100%", objectFit: "cover", display: "block" } }),
848
+ /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: "linear-gradient(to top, rgba(0,0,0,0.6) 0%, transparent 50%)" } }),
849
+ /* @__PURE__ */ jsx("div", { style: { position: "absolute", bottom: 0, left: 0, padding: "clamp(0.75rem, 3vw, 1.25rem)" }, children: [
850
+ /* @__PURE__ */ jsx("p", { className: "loc-card-name", style: { fontFamily: brandFont, fontSize: "clamp(1.25rem, 5vw, 1.75rem)", color: "#fff", margin: 0, lineHeight: 1 }, children: loc.name }),
851
+ /* @__PURE__ */ jsx("p", { style: { fontSize: "0.6rem", letterSpacing: "0.2em", color: "rgba(255,255,255,0.6)", margin: "0.25rem 0 0", fontWeight: 600, textTransform: "uppercase" }, children: [
852
+ loc.city,
853
+ ", ",
854
+ loc.state
855
+ ] })
856
+ ] })
857
+ ] }),
858
+ /* @__PURE__ */ jsx("div", { style: { padding: "clamp(0.625rem, 2vw, 1rem)", display: "flex", flexDirection: "column", gap: "0.4rem" }, children: [
859
+ loc.tagline && /* @__PURE__ */ jsx("p", { style: { fontSize: "0.75rem", color: "rgba(255,255,255,0.5)", fontWeight: 500, fontStyle: "italic", margin: 0 }, children: loc.tagline }),
860
+ loc.rating != null && loc.review_count != null && loc.review_count > 0 && /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", gap: "0.375rem" }, children: [
861
+ /* @__PURE__ */ jsx("span", { style: { display: "inline-flex", gap: "2px" }, children: [1, 2, 3, 4, 5].map((s) => /* @__PURE__ */ jsx("svg", { width: 12, height: 12, viewBox: "0 0 20 20", fill: s <= Math.round(loc.rating) ? theme.accent : "rgba(255,255,255,0.1)", children: /* @__PURE__ */ jsx("path", { d: "M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" }) }, s)) }),
862
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "0.7rem", color: "rgba(255,255,255,0.5)", fontWeight: 500 }, children: `${loc.rating.toFixed(1)} \xB7 ${loc.review_count} reviews` })
863
+ ] }),
864
+ loc.address && /* @__PURE__ */ jsx("a", { href: dirUrl, target: "_blank", rel: "noopener noreferrer", style: { fontSize: "0.7rem", color: "rgba(255,255,255,0.35)", fontWeight: 500, textDecoration: "none" }, children: [
865
+ loc.address,
866
+ ", ",
867
+ loc.city,
868
+ ", ",
869
+ loc.state
870
+ ] }),
871
+ loc.phone && /* @__PURE__ */ jsx("a", { href: loc.phone_href || `tel:${loc.phone}`, style: { fontSize: "0.7rem", color: "rgba(255,255,255,0.35)", fontWeight: 500, textDecoration: "none" }, children: loc.phone }),
872
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "0.375rem", marginTop: "auto", paddingTop: "0.125rem" }, children: [
873
+ /* @__PURE__ */ jsx(
874
+ "a",
875
+ {
876
+ href: dirUrl,
877
+ target: "_blank",
878
+ rel: "noopener noreferrer",
879
+ onClick: () => onEvent?.("cta_click", { label: "directions", location: loc.name }),
880
+ style: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", gap: "0.25rem", padding: "0.5rem", background: "#fff", color: "#000", fontSize: "0.65rem", fontWeight: 700, letterSpacing: "0.1em", textTransform: "uppercase", textDecoration: "none" },
881
+ children: "directions"
882
+ }
883
+ ),
884
+ loc.shop_url && /* @__PURE__ */ jsx(
885
+ "a",
886
+ {
887
+ href: loc.shop_url,
888
+ onClick: () => onEvent?.("cta_click", { label: "shop", location: loc.name }),
889
+ style: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", gap: "0.25rem", padding: "0.5rem", border: "1px solid rgba(255,255,255,0.15)", color: "rgba(255,255,255,0.8)", fontSize: "0.65rem", fontWeight: 700, letterSpacing: "0.1em", textTransform: "uppercase", textDecoration: "none" },
890
+ children: "shop"
891
+ }
892
+ )
893
+ ] })
894
+ ] })
895
+ ] });
896
+ }
656
897
  function SocialLinksSection({ section, theme }) {
657
898
  const { links } = section.content;
658
899
  if (!links || links.length === 0) return null;
@@ -680,25 +921,21 @@ var WhaleStorefront = (function (exports) {
680
921
  }
681
922
  function ReviewLine({ rating, count, source, url, accent, fg }) {
682
923
  const label = `${rating.toFixed(1)} \xB7 ${count} ${source || "reviews"}`;
683
- const stars = /* @__PURE__ */ jsx("span", { style: { display: "inline-flex", gap: "1px" }, children: [1, 2, 3, 4, 5].map((s) => /* @__PURE__ */ jsx("svg", { width: 14, height: 14, viewBox: "0 0 20 20", fill: s <= Math.round(rating) ? accent : `${fg}20`, children: /* @__PURE__ */ jsx("path", { d: "M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" }) }, s)) });
684
- const text = /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8rem", color: `${fg}80`, fontWeight: 500 }, children: label });
685
- const inner = /* @__PURE__ */ jsx("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.5rem" }, children: [
686
- stars,
687
- text
924
+ const content = /* @__PURE__ */ jsx("div", { style: {
925
+ marginTop: "1.5rem",
926
+ display: "flex",
927
+ alignItems: "center",
928
+ justifyContent: "center",
929
+ gap: "0.625rem"
930
+ }, children: [
931
+ /* @__PURE__ */ jsx("span", { dangerouslySetInnerHTML: { __html: GOOGLE_G } }),
932
+ /* @__PURE__ */ jsx("span", { style: { display: "inline-flex", gap: "2px" }, children: [1, 2, 3, 4, 5].map((s) => /* @__PURE__ */ jsx("svg", { width: 16, height: 16, viewBox: "0 0 20 20", fill: s <= Math.round(rating) ? "#FBBC05" : `${fg}20`, children: /* @__PURE__ */ jsx("path", { d: "M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" }) }, s)) }),
933
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "0.9rem", color: `${fg}AA`, fontWeight: 500 }, children: label })
688
934
  ] });
689
935
  if (url) {
690
- return /* @__PURE__ */ jsx("div", { style: { marginTop: "1.25rem", textAlign: "center" }, children: /* @__PURE__ */ jsx(
691
- "a",
692
- {
693
- href: url,
694
- target: "_blank",
695
- rel: "noopener noreferrer",
696
- style: { textDecoration: "none", display: "inline-flex", alignItems: "center", gap: "0.5rem" },
697
- children: inner
698
- }
699
- ) });
936
+ return /* @__PURE__ */ jsx("a", { href: url, target: "_blank", rel: "noopener noreferrer", style: { textDecoration: "none", display: "block" }, children: content });
700
937
  }
701
- return /* @__PURE__ */ jsx("div", { style: { marginTop: "1.25rem", display: "flex", justifyContent: "center" }, children: inner });
938
+ return content;
702
939
  }
703
940
 
704
941
  // src/react/components/sections/interactive-sections.tsx
@@ -1081,15 +1318,26 @@ var WhaleStorefront = (function (exports) {
1081
1318
  }
1082
1319
 
1083
1320
  // src/react/components/sections/conversion-sections.tsx
1321
+ var GOOGLE_G_LG = '<svg width="28" height="28" viewBox="0 0 48 48"><path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/><path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/><path fill="#FBBC05" d="M10.53 28.59a14.5 14.5 0 010-9.18l-7.98-6.19a24.03 24.03 0 000 21.56l7.98-6.19z"/><path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/></svg>';
1322
+ var GOOGLE_G_SM = '<svg width="14" height="14" viewBox="0 0 48 48"><path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/><path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/><path fill="#FBBC05" d="M10.53 28.59a14.5 14.5 0 010-9.18l-7.98-6.19a24.03 24.03 0 000 21.56l7.98-6.19z"/><path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/></svg>';
1084
1323
  function TestimonialsSection({ section, theme }) {
1085
1324
  const c = section.content;
1086
- const layout = section.config?.layout || "grid";
1325
+ section.config?.layout || "grid";
1087
1326
  const reviews = c.reviews || [];
1088
1327
  if (reviews.length === 0) return null;
1089
1328
  const overallRating = c.rating ?? 5;
1090
1329
  const reviewCount = c.review_count ?? reviews.length;
1091
- return /* @__PURE__ */ jsx("div", { style: { padding: "3rem 1.5rem", maxWidth: 640, margin: "0 auto" }, children: [
1092
- /* @__PURE__ */ jsx("div", { style: { textAlign: "center", marginBottom: "2rem" }, children: [
1330
+ const googleUrl = section.config?.google_url || void 0;
1331
+ const directionsUrl = section.config?.directions_url || void 0;
1332
+ `reviews-${section.id}`;
1333
+ return /* @__PURE__ */ jsx("div", { style: { padding: "3rem 0", overflow: "hidden" }, children: [
1334
+ /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: `
1335
+ .rv-scroll{display:flex;gap:clamp(0.5rem,2vw,0.75rem);overflow-x:auto;scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;scrollbar-width:none;padding:0 clamp(1rem,4vw,3rem)}
1336
+ .rv-scroll::-webkit-scrollbar{display:none}
1337
+ .rv-card{flex:0 0 clamp(260px,40vw,320px);scroll-snap-align:start}
1338
+ ` } }),
1339
+ /* @__PURE__ */ jsx("div", { style: { textAlign: "center", marginBottom: "2rem", padding: "0 1.5rem" }, children: [
1340
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", justifyContent: "center", marginBottom: "0.75rem" }, children: /* @__PURE__ */ jsx("span", { dangerouslySetInnerHTML: { __html: GOOGLE_G_LG } }) }),
1093
1341
  c.heading && /* @__PURE__ */ jsx("h2", { style: {
1094
1342
  fontSize: "clamp(1.25rem, 4vw, 1.75rem)",
1095
1343
  fontWeight: 300,
@@ -1100,42 +1348,121 @@ var WhaleStorefront = (function (exports) {
1100
1348
  color: theme.fg
1101
1349
  }, children: /* @__PURE__ */ jsx(AnimatedText, { text: c.heading }) }),
1102
1350
  /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: "0.5rem", marginBottom: "0.25rem" }, children: [
1103
- /* @__PURE__ */ jsx(Stars, { rating: overallRating, color: theme.accent, size: 18 }),
1351
+ /* @__PURE__ */ jsx(Stars, { rating: overallRating, color: "#FBBC05", size: 18 }),
1104
1352
  /* @__PURE__ */ jsx("span", { style: { fontSize: "1.1rem", fontWeight: 500, color: theme.fg }, children: /* @__PURE__ */ jsx(AnimatedText, { text: overallRating.toFixed(1) }) })
1105
1353
  ] }),
1106
1354
  c.subtitle ? /* @__PURE__ */ jsx("p", { style: { fontSize: "0.8rem", color: theme.muted, margin: 0, letterSpacing: "0.1em", textTransform: "uppercase" }, children: c.subtitle }) : /* @__PURE__ */ jsx("p", { style: { fontSize: "0.8rem", color: theme.muted, margin: 0, letterSpacing: "0.1em", textTransform: "uppercase" }, children: [
1107
1355
  "from ",
1108
1356
  /* @__PURE__ */ jsx(AnimatedText, { text: String(reviewCount) }),
1109
1357
  " reviews"
1358
+ ] }),
1359
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "0.625rem", justifyContent: "center", marginTop: "1.5rem", flexWrap: "wrap" }, children: [
1360
+ googleUrl && /* @__PURE__ */ jsx("a", { href: googleUrl, target: "_blank", rel: "noopener noreferrer", style: {
1361
+ display: "inline-flex",
1362
+ alignItems: "center",
1363
+ gap: "0.5rem",
1364
+ padding: "clamp(0.75rem, 2vw, 0.875rem) clamp(1.25rem, 3vw, 2rem)",
1365
+ background: theme.fg,
1366
+ color: theme.bg,
1367
+ textDecoration: "none",
1368
+ fontSize: "clamp(0.8rem, 2vw, 0.9rem)",
1369
+ fontWeight: 600,
1370
+ letterSpacing: "0.06em",
1371
+ textTransform: "uppercase"
1372
+ }, children: [
1373
+ /* @__PURE__ */ jsx("span", { dangerouslySetInnerHTML: { __html: GOOGLE_G_SM } }),
1374
+ `see all ${reviewCount} reviews`
1375
+ ] }),
1376
+ directionsUrl && /* @__PURE__ */ jsx("a", { href: directionsUrl, target: "_blank", rel: "noopener noreferrer", style: {
1377
+ display: "inline-flex",
1378
+ alignItems: "center",
1379
+ gap: "0.5rem",
1380
+ padding: "clamp(0.75rem, 2vw, 0.875rem) clamp(1.25rem, 3vw, 2rem)",
1381
+ border: `1px solid ${theme.fg}25`,
1382
+ color: theme.fg,
1383
+ textDecoration: "none",
1384
+ fontSize: "clamp(0.8rem, 2vw, 0.9rem)",
1385
+ fontWeight: 600,
1386
+ letterSpacing: "0.06em",
1387
+ textTransform: "uppercase"
1388
+ }, children: "get directions" })
1110
1389
  ] })
1111
1390
  ] }),
1112
- /* @__PURE__ */ jsx("div", { style: {
1113
- display: "grid",
1114
- gridTemplateColumns: layout === "list" ? "1fr" : `repeat(${Math.min(reviews.length, 2)}, 1fr)`,
1115
- gap: "0.75rem"
1116
- }, children: reviews.map((review, i) => /* @__PURE__ */ jsx("div", { style: {
1117
- background: theme.surface,
1118
- border: `1px solid ${theme.fg}08`,
1119
- padding: "1.25rem"
1120
- }, children: [
1121
- /* @__PURE__ */ jsx(Stars, { rating: review.rating ?? 5, color: theme.accent, size: 14 }),
1122
- /* @__PURE__ */ jsx("p", { style: {
1123
- fontSize: "0.88rem",
1124
- color: `${theme.fg}CC`,
1125
- margin: "0.75rem 0",
1126
- lineHeight: 1.6,
1127
- fontWeight: 300,
1128
- fontStyle: "italic"
1391
+ /* @__PURE__ */ jsx("div", { className: "rv-scroll", children: [
1392
+ reviews.map((review, i) => /* @__PURE__ */ jsx("div", { className: "rv-card", children: /* @__PURE__ */ jsx("div", { style: {
1393
+ background: theme.surface,
1394
+ border: `1px solid ${theme.fg}08`,
1395
+ padding: "clamp(1rem, 3vw, 1.5rem)",
1396
+ height: "100%",
1397
+ display: "flex",
1398
+ flexDirection: "column"
1129
1399
  }, children: [
1130
- '"',
1131
- review.text,
1132
- '"'
1133
- ] }),
1134
- /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "baseline", gap: "0.375rem" }, children: [
1135
- /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8rem", fontWeight: 500, color: theme.fg }, children: review.name }),
1136
- review.location && /* @__PURE__ */ jsx("span", { style: { fontSize: "0.7rem", color: theme.muted }, children: review.location })
1137
- ] })
1138
- ] }, i)) })
1400
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "0.75rem" }, children: [
1401
+ /* @__PURE__ */ jsx(Stars, { rating: review.rating ?? 5, color: "#FBBC05", size: 13 }),
1402
+ review.location && /* @__PURE__ */ jsx("span", { style: { fontSize: "0.6rem", letterSpacing: "0.15em", textTransform: "uppercase", color: theme.muted, fontWeight: 500 }, children: review.location })
1403
+ ] }),
1404
+ /* @__PURE__ */ jsx("p", { style: {
1405
+ fontSize: "0.9rem",
1406
+ color: `${theme.fg}B0`,
1407
+ margin: "0 0 auto",
1408
+ lineHeight: 1.65,
1409
+ fontWeight: 400,
1410
+ paddingBottom: "1rem"
1411
+ }, children: `\u201C${review.text}\u201D` }),
1412
+ /* @__PURE__ */ jsx("div", { style: { borderTop: `1px solid ${theme.fg}08`, paddingTop: "0.75rem", display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
1413
+ /* @__PURE__ */ jsx("div", { style: {
1414
+ width: 28,
1415
+ height: 28,
1416
+ borderRadius: "50%",
1417
+ background: `${theme.accent}18`,
1418
+ border: `1px solid ${theme.accent}15`,
1419
+ display: "flex",
1420
+ alignItems: "center",
1421
+ justifyContent: "center",
1422
+ flexShrink: 0
1423
+ }, children: /* @__PURE__ */ jsx("span", { style: { fontSize: "0.7rem", fontWeight: 600, color: theme.accent, textTransform: "uppercase" }, children: review.name.charAt(0) }) }),
1424
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8rem", fontWeight: 500, color: `${theme.fg}AA` }, children: review.name })
1425
+ ] })
1426
+ ] }) }, i)),
1427
+ googleUrl && /* @__PURE__ */ jsx("div", { className: "rv-card", children: /* @__PURE__ */ jsx("a", { href: googleUrl, target: "_blank", rel: "noopener noreferrer", style: {
1428
+ height: "100%",
1429
+ display: "flex",
1430
+ flexDirection: "column",
1431
+ alignItems: "center",
1432
+ justifyContent: "center",
1433
+ gap: "0.75rem",
1434
+ background: theme.surface,
1435
+ border: `1px solid ${theme.fg}08`,
1436
+ padding: "clamp(1rem, 3vw, 1.5rem)",
1437
+ textDecoration: "none"
1438
+ }, children: [
1439
+ /* @__PURE__ */ jsx("span", { dangerouslySetInnerHTML: { __html: GOOGLE_G_LG } }),
1440
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "0.85rem", color: theme.fg, fontWeight: 500 }, children: `read all ${reviewCount}` }),
1441
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "0.75rem", color: theme.muted, letterSpacing: "0.08em" }, children: "reviews on google" })
1442
+ ] }) })
1443
+ ] }),
1444
+ googleUrl && /* @__PURE__ */ jsx("div", { style: { textAlign: "center", marginTop: "1.5rem", padding: "0 1.5rem" }, children: /* @__PURE__ */ jsx(
1445
+ "a",
1446
+ {
1447
+ href: googleUrl,
1448
+ target: "_blank",
1449
+ rel: "noopener noreferrer",
1450
+ style: {
1451
+ display: "inline-flex",
1452
+ alignItems: "center",
1453
+ gap: "0.5rem",
1454
+ fontSize: "0.8rem",
1455
+ color: theme.muted,
1456
+ textDecoration: "none",
1457
+ letterSpacing: "0.08em",
1458
+ fontWeight: 500
1459
+ },
1460
+ children: [
1461
+ /* @__PURE__ */ jsx("span", { dangerouslySetInnerHTML: { __html: GOOGLE_G_SM } }),
1462
+ `read all ${reviewCount} reviews on google`
1463
+ ]
1464
+ }
1465
+ ) })
1139
1466
  ] });
1140
1467
  }
1141
1468
  function Stars({ rating, color, size }) {
@@ -1561,7 +1888,7 @@ var WhaleStorefront = (function (exports) {
1561
1888
  const el = (() => {
1562
1889
  switch (section.type) {
1563
1890
  case "hero":
1564
- return /* @__PURE__ */ jsx(HeroSection, { section, theme, tracking, onEvent });
1891
+ return /* @__PURE__ */ jsx(HeroSection, { section, theme, tracking, onEvent, data });
1565
1892
  case "collage_hero":
1566
1893
  return /* @__PURE__ */ jsx(CollageHeroSection, { section, theme, tracking, onEvent });
1567
1894
  case "text":
@@ -1571,7 +1898,7 @@ var WhaleStorefront = (function (exports) {
1571
1898
  case "video":
1572
1899
  return /* @__PURE__ */ jsx(VideoSection, { section, theme });
1573
1900
  case "gallery":
1574
- return /* @__PURE__ */ jsx(GallerySection, { section, theme });
1901
+ return /* @__PURE__ */ jsx(GallerySection, { section, theme, onEvent });
1575
1902
  case "cta":
1576
1903
  return /* @__PURE__ */ jsx(CTASection, { section, theme, tracking, onEvent });
1577
1904
  case "stats":