@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.
@@ -2208,8 +2208,57 @@ function AnimatedText({ text }) {
2208
2208
  (part, i) => NUM_TEST.test(part) ? /* @__PURE__ */ jsxRuntime.jsx(AnimatedNumber, { raw: part }, i) : part
2209
2209
  ) });
2210
2210
  }
2211
- function HeroSection({ section, theme, tracking, onEvent }) {
2212
- const { title, subtitle, background_image, cta_text, cta_url, review_line } = section.content;
2211
+ 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>';
2212
+ function HeroSection({ section, theme, tracking, onEvent, data }) {
2213
+ const c = section.content;
2214
+ const [formOpen, setFormOpen] = react.useState(false);
2215
+ const [firstName, setFirstName] = react.useState("");
2216
+ const [email, setEmail] = react.useState("");
2217
+ const [status, setStatus] = react.useState("idle");
2218
+ const [errorMsg, setErrorMsg] = react.useState("");
2219
+ const gatewayUrl = data?.gatewayUrl || "https://whale-gateway.fly.dev";
2220
+ const storeId = data?.store?.id;
2221
+ const slug = data?.landing_page?.slug;
2222
+ async function handleSubmit(e) {
2223
+ e.preventDefault();
2224
+ if (!email || !storeId) return;
2225
+ setStatus("loading");
2226
+ setErrorMsg("");
2227
+ const urlParams = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
2228
+ try {
2229
+ const res = await fetch(`${gatewayUrl}/v1/stores/${storeId}/storefront/leads`, {
2230
+ method: "POST",
2231
+ headers: { "Content-Type": "application/json" },
2232
+ body: JSON.stringify({
2233
+ email,
2234
+ first_name: firstName || void 0,
2235
+ source: c.inline_form?.source || "landing_page",
2236
+ landing_page_slug: slug || void 0,
2237
+ tags: c.inline_form?.tags || void 0,
2238
+ segment_id: c.inline_form?.segment_id || void 0,
2239
+ campaign_id: c.inline_form?.campaign_id || void 0,
2240
+ preferred_location_id: c.inline_form?.preferred_location_id || void 0,
2241
+ template_slug: c.inline_form?.template_slug || void 0,
2242
+ acquisition_campaign: c.inline_form?.acquisition_campaign || void 0,
2243
+ visitor_id: data?.analyticsContext?.visitorId || void 0,
2244
+ session_id: data?.analyticsContext?.sessionId || void 0,
2245
+ utm_source: urlParams?.get("utm_source") || void 0,
2246
+ utm_medium: urlParams?.get("utm_medium") || void 0,
2247
+ utm_campaign: urlParams?.get("utm_campaign") || void 0
2248
+ })
2249
+ });
2250
+ if (!res.ok) {
2251
+ const body = await res.json().catch(() => ({}));
2252
+ throw new Error(body?.error?.message || "something went wrong.");
2253
+ }
2254
+ setStatus("success");
2255
+ onEvent?.("lead", { email, first_name: firstName || void 0, source: c.inline_form?.source || "landing_page" });
2256
+ } catch (err) {
2257
+ setErrorMsg(err instanceof Error ? err.message : "something went wrong.");
2258
+ setStatus("error");
2259
+ }
2260
+ }
2261
+ const hasInlineForm = !!c.inline_form;
2213
2262
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
2214
2263
  position: "relative",
2215
2264
  minHeight: "60vh",
@@ -2219,36 +2268,56 @@ function HeroSection({ section, theme, tracking, onEvent }) {
2219
2268
  alignItems: "center",
2220
2269
  textAlign: "center",
2221
2270
  padding: "3rem 1.5rem",
2222
- backgroundImage: background_image ? `url(${background_image})` : void 0,
2271
+ backgroundImage: c.background_image ? `url(${c.background_image})` : void 0,
2223
2272
  backgroundSize: "cover",
2224
2273
  backgroundPosition: "center"
2225
2274
  }, children: [
2226
- background_image && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" } }),
2227
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", zIndex: 1, maxWidth: 640 }, children: [
2228
- title && /* @__PURE__ */ jsxRuntime.jsx("h1", { style: {
2229
- fontSize: "clamp(2rem, 8vw, 3rem)",
2275
+ c.background_image && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" } }),
2276
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", zIndex: 1, maxWidth: 640, width: "100%" }, children: [
2277
+ c.title && /* @__PURE__ */ jsxRuntime.jsx("h1", { style: {
2278
+ fontSize: "clamp(2rem, 7vw, 3.25rem)",
2230
2279
  fontWeight: 300,
2231
2280
  fontFamily: theme.fontDisplay || "inherit",
2232
- margin: "0 0 1rem",
2233
- lineHeight: 1.15,
2234
- letterSpacing: "-0.02em",
2281
+ margin: "0 0 1.25rem",
2282
+ lineHeight: 1.08,
2283
+ letterSpacing: "-0.03em",
2235
2284
  color: theme.fg
2236
- }, children: /* @__PURE__ */ jsxRuntime.jsx(AnimatedText, { text: title }) }),
2237
- subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { style: {
2238
- fontSize: "0.85rem",
2285
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(AnimatedText, { text: c.title }) }),
2286
+ c.subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { style: {
2287
+ fontSize: "clamp(0.9rem, 2.5vw, 1.1rem)",
2239
2288
  color: theme.accent,
2240
2289
  margin: "0 0 2rem",
2241
2290
  lineHeight: 1.6,
2242
2291
  textTransform: "uppercase",
2243
- letterSpacing: "0.15em"
2244
- }, children: subtitle }),
2245
- cta_text && cta_url && /* @__PURE__ */ jsxRuntime.jsx(
2292
+ letterSpacing: "0.1em"
2293
+ }, children: c.subtitle }),
2294
+ hasInlineForm ? /* @__PURE__ */ jsxRuntime.jsx(
2295
+ HeroInlineForm,
2296
+ {
2297
+ ctaText: c.cta_text || "claim it",
2298
+ formOpen,
2299
+ setFormOpen,
2300
+ firstName,
2301
+ setFirstName,
2302
+ email,
2303
+ setEmail,
2304
+ status,
2305
+ errorMsg,
2306
+ onSubmit: handleSubmit,
2307
+ successHeading: c.inline_form.success_heading || "you're in.",
2308
+ successMessage: c.inline_form.success_message || "check your inbox.",
2309
+ submitText: c.inline_form.button_text || "send my code",
2310
+ theme,
2311
+ onEvent,
2312
+ tracking
2313
+ }
2314
+ ) : c.cta_text && c.cta_url ? /* @__PURE__ */ jsxRuntime.jsx(
2246
2315
  "a",
2247
2316
  {
2248
- href: cta_url,
2317
+ href: c.cta_url,
2249
2318
  onClick: () => {
2250
- trackClick(tracking, cta_text, cta_url);
2251
- onEvent?.("cta_click", { label: cta_text, url: cta_url });
2319
+ trackClick(tracking, c.cta_text, c.cta_url);
2320
+ onEvent?.("cta_click", { label: c.cta_text, url: c.cta_url });
2252
2321
  },
2253
2322
  style: {
2254
2323
  display: "inline-block",
@@ -2261,13 +2330,114 @@ function HeroSection({ section, theme, tracking, onEvent }) {
2261
2330
  letterSpacing: "0.08em",
2262
2331
  textTransform: "uppercase"
2263
2332
  },
2264
- children: cta_text
2333
+ children: c.cta_text
2265
2334
  }
2266
- ),
2267
- review_line && review_line.count > 0 && /* @__PURE__ */ jsxRuntime.jsx(ReviewLine, { rating: review_line.rating, count: review_line.count, source: review_line.source, url: review_line.url, accent: theme.accent, fg: theme.fg })
2335
+ ) : null,
2336
+ c.review_line && c.review_line.count > 0 && /* @__PURE__ */ jsxRuntime.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 })
2268
2337
  ] })
2269
2338
  ] });
2270
2339
  }
2340
+ function HeroInlineForm({ ctaText, formOpen, setFormOpen, firstName, setFirstName, email, setEmail, status, errorMsg, onSubmit, successHeading, successMessage, submitText, theme, onEvent, tracking }) {
2341
+ const formMaxW = "min(480px, 90vw)";
2342
+ if (status === "success") {
2343
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { maxWidth: formMaxW, margin: "0 auto", padding: "1.5rem 2rem", background: `${theme.fg}06`, border: `1px solid ${theme.fg}10` }, children: [
2344
+ /* @__PURE__ */ jsxRuntime.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 }),
2345
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.85rem", color: `${theme.fg}70`, margin: 0, lineHeight: 1.5 }, children: successMessage })
2346
+ ] });
2347
+ }
2348
+ if (!formOpen) {
2349
+ return /* @__PURE__ */ jsxRuntime.jsx(
2350
+ "button",
2351
+ {
2352
+ onClick: () => {
2353
+ setFormOpen(true);
2354
+ trackClick(tracking, ctaText, "#form");
2355
+ onEvent?.("cta_click", { label: ctaText });
2356
+ },
2357
+ style: {
2358
+ display: "inline-block",
2359
+ padding: "clamp(0.875rem, 2.5vw, 1.125rem) clamp(2rem, 5vw, 3rem)",
2360
+ background: theme.fg,
2361
+ color: theme.bg,
2362
+ border: "none",
2363
+ cursor: "pointer",
2364
+ fontSize: "clamp(0.85rem, 2vw, 1rem)",
2365
+ fontWeight: 500,
2366
+ letterSpacing: "0.06em",
2367
+ textTransform: "uppercase",
2368
+ fontFamily: "inherit"
2369
+ },
2370
+ children: ctaText
2371
+ }
2372
+ );
2373
+ }
2374
+ const inputBase = {
2375
+ padding: "clamp(0.75rem, 2vw, 0.875rem) clamp(0.75rem, 2vw, 1rem)",
2376
+ background: theme.surface,
2377
+ border: `1px solid ${theme.fg}20`,
2378
+ color: theme.fg,
2379
+ fontSize: "clamp(0.8rem, 2vw, 0.9rem)",
2380
+ fontWeight: 300,
2381
+ outline: "none",
2382
+ fontFamily: "inherit",
2383
+ boxSizing: "border-box",
2384
+ width: "100%"
2385
+ };
2386
+ return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit, style: { maxWidth: formMaxW, margin: "0 auto", width: "100%" }, children: [
2387
+ /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: `
2388
+ .hero-form-row{display:flex;gap:0}
2389
+ @media(max-width:480px){.hero-form-row{flex-direction:column;gap:0.5rem}}
2390
+ ` } }),
2391
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "hero-form-row", children: [
2392
+ /* @__PURE__ */ jsxRuntime.jsx(
2393
+ "input",
2394
+ {
2395
+ type: "text",
2396
+ placeholder: "first name",
2397
+ value: firstName,
2398
+ autoFocus: true,
2399
+ onChange: (e) => setFirstName(e.target.value),
2400
+ style: { ...inputBase, flex: 1, minWidth: 0, borderRight: "none" }
2401
+ }
2402
+ ),
2403
+ /* @__PURE__ */ jsxRuntime.jsx(
2404
+ "input",
2405
+ {
2406
+ type: "email",
2407
+ placeholder: "email",
2408
+ value: email,
2409
+ required: true,
2410
+ onChange: (e) => setEmail(e.target.value),
2411
+ style: { ...inputBase, flex: 1.2, minWidth: 0 }
2412
+ }
2413
+ )
2414
+ ] }),
2415
+ /* @__PURE__ */ jsxRuntime.jsx(
2416
+ "button",
2417
+ {
2418
+ type: "submit",
2419
+ disabled: status === "loading",
2420
+ style: {
2421
+ width: "100%",
2422
+ padding: "clamp(0.75rem, 2vw, 0.875rem)",
2423
+ background: theme.fg,
2424
+ color: theme.bg,
2425
+ border: "none",
2426
+ fontSize: "clamp(0.8rem, 2vw, 0.85rem)",
2427
+ fontWeight: 600,
2428
+ letterSpacing: "0.08em",
2429
+ textTransform: "uppercase",
2430
+ cursor: status === "loading" ? "wait" : "pointer",
2431
+ fontFamily: "inherit",
2432
+ marginTop: "0.5rem",
2433
+ opacity: status === "loading" ? 0.7 : 1
2434
+ },
2435
+ children: status === "loading" ? "..." : submitText
2436
+ }
2437
+ ),
2438
+ status === "error" && errorMsg && /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.75rem", color: "#e55", margin: "0.5rem 0 0", textAlign: "left" }, children: errorMsg })
2439
+ ] });
2440
+ }
2271
2441
  function TextSection({ section, theme }) {
2272
2442
  const { heading, body } = section.content;
2273
2443
  const align = section.config?.align || "left";
@@ -2369,29 +2539,100 @@ function CollageHeroSection({ section, theme, tracking, onEvent }) {
2369
2539
  ] })
2370
2540
  ] });
2371
2541
  }
2372
- function GallerySection({ section, theme }) {
2373
- const { images } = section.content;
2542
+ function GallerySection({ section, theme, onEvent }) {
2543
+ const c = section.content;
2544
+ const images = c.images || [];
2374
2545
  const columns = section.config?.columns || 3;
2375
2546
  const layout = section.config?.layout || "grid";
2547
+ const loc = c.location_card;
2376
2548
  if (!images || images.length === 0) return null;
2377
2549
  if (layout === "collage") {
2378
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "0.375rem", maxWidth: 900, margin: "0 auto" }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "0.375rem" }, children: images.map((img, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
2379
- gridColumn: i === 0 ? "1 / -1" : void 0,
2380
- aspectRatio: i === 0 ? "16/9" : i % 3 === 0 ? "3/4" : "1",
2381
- overflow: "hidden",
2382
- background: theme.surface
2383
- }, children: /* @__PURE__ */ jsxRuntime.jsx(
2384
- "img",
2385
- {
2386
- src: img.url,
2387
- alt: img.alt || "",
2388
- loading: i < 2 ? "eager" : "lazy",
2389
- style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
2390
- }
2391
- ) }, i)) }) });
2550
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "0.375rem", maxWidth: 900, margin: "0 auto" }, children: [
2551
+ loc && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "0.375rem", marginBottom: "0.375rem" }, children: [
2552
+ /* @__PURE__ */ jsxRuntime.jsx(GalleryLocationCard, { loc, theme, onEvent }),
2553
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { aspectRatio: "4/5", overflow: "hidden", background: theme.surface }, children: /* @__PURE__ */ jsxRuntime.jsx(
2554
+ "img",
2555
+ {
2556
+ src: images[0].url,
2557
+ alt: images[0].alt || "",
2558
+ loading: "eager",
2559
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
2560
+ }
2561
+ ) })
2562
+ ] }),
2563
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "0.375rem" }, children: images.slice(loc ? 1 : 0).map((img, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
2564
+ gridColumn: !loc && i === 0 ? "1 / -1" : void 0,
2565
+ aspectRatio: !loc && i === 0 ? "16/9" : i % 3 === 0 ? "3/4" : "1",
2566
+ overflow: "hidden",
2567
+ background: theme.surface
2568
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(
2569
+ "img",
2570
+ {
2571
+ src: img.url,
2572
+ alt: img.alt || "",
2573
+ loading: i < 2 ? "eager" : "lazy",
2574
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
2575
+ }
2576
+ ) }, i)) })
2577
+ ] });
2392
2578
  }
2393
2579
  return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "1.5rem", maxWidth: 800, margin: "0 auto" }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "grid", gridTemplateColumns: `repeat(${columns}, 1fr)`, gap: "0.5rem" }, children: images.map((img, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: { aspectRatio: "1", overflow: "hidden", background: theme.surface }, children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: img.url, alt: img.alt || "", style: { width: "100%", height: "100%", objectFit: "cover", display: "block" } }) }, i)) }) });
2394
2580
  }
2581
+ function GalleryLocationCard({ loc, theme, onEvent }) {
2582
+ const dirUrl = loc.directions_url || `https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent(loc.address + ", " + loc.city + ", " + loc.state)}`;
2583
+ const brandFont = loc.brand_font || "inherit";
2584
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { background: "#0C0C0C", border: "1px solid rgba(255,255,255,0.06)", overflow: "hidden", display: "flex", flexDirection: "column" }, children: [
2585
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", flex: 1, minHeight: 0, overflow: "hidden" }, children: [
2586
+ /* @__PURE__ */ jsxRuntime.jsx("img", { src: loc.image, alt: loc.name, style: { width: "100%", height: "100%", objectFit: "cover", display: "block" } }),
2587
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, background: "linear-gradient(to top, rgba(0,0,0,0.6) 0%, transparent 50%)" } }),
2588
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "absolute", bottom: 0, left: 0, padding: "clamp(0.75rem, 3vw, 1.25rem)" }, children: [
2589
+ /* @__PURE__ */ jsxRuntime.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 }),
2590
+ /* @__PURE__ */ jsxRuntime.jsxs("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: [
2591
+ loc.city,
2592
+ ", ",
2593
+ loc.state
2594
+ ] })
2595
+ ] })
2596
+ ] }),
2597
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "clamp(0.625rem, 2vw, 1rem)", display: "flex", flexDirection: "column", gap: "0.4rem" }, children: [
2598
+ loc.tagline && /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.75rem", color: "rgba(255,255,255,0.5)", fontWeight: 500, fontStyle: "italic", margin: 0 }, children: loc.tagline }),
2599
+ loc.rating != null && loc.review_count != null && loc.review_count > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.375rem" }, children: [
2600
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "inline-flex", gap: "2px" }, children: [1, 2, 3, 4, 5].map((s) => /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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)) }),
2601
+ /* @__PURE__ */ jsxRuntime.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` })
2602
+ ] }),
2603
+ loc.address && /* @__PURE__ */ jsxRuntime.jsxs("a", { href: dirUrl, target: "_blank", rel: "noopener noreferrer", style: { fontSize: "0.7rem", color: "rgba(255,255,255,0.35)", fontWeight: 500, textDecoration: "none" }, children: [
2604
+ loc.address,
2605
+ ", ",
2606
+ loc.city,
2607
+ ", ",
2608
+ loc.state
2609
+ ] }),
2610
+ loc.phone && /* @__PURE__ */ jsxRuntime.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 }),
2611
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "0.375rem", marginTop: "auto", paddingTop: "0.125rem" }, children: [
2612
+ /* @__PURE__ */ jsxRuntime.jsx(
2613
+ "a",
2614
+ {
2615
+ href: dirUrl,
2616
+ target: "_blank",
2617
+ rel: "noopener noreferrer",
2618
+ onClick: () => onEvent?.("cta_click", { label: "directions", location: loc.name }),
2619
+ 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" },
2620
+ children: "directions"
2621
+ }
2622
+ ),
2623
+ loc.shop_url && /* @__PURE__ */ jsxRuntime.jsx(
2624
+ "a",
2625
+ {
2626
+ href: loc.shop_url,
2627
+ onClick: () => onEvent?.("cta_click", { label: "shop", location: loc.name }),
2628
+ 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" },
2629
+ children: "shop"
2630
+ }
2631
+ )
2632
+ ] })
2633
+ ] })
2634
+ ] });
2635
+ }
2395
2636
  function SocialLinksSection({ section, theme }) {
2396
2637
  const { links } = section.content;
2397
2638
  if (!links || links.length === 0) return null;
@@ -2419,25 +2660,21 @@ function toEmbedUrl(url) {
2419
2660
  }
2420
2661
  function ReviewLine({ rating, count, source, url, accent, fg }) {
2421
2662
  const label = `${rating.toFixed(1)} \xB7 ${count} ${source || "reviews"}`;
2422
- const stars = /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "inline-flex", gap: "1px" }, children: [1, 2, 3, 4, 5].map((s) => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: 14, height: 14, viewBox: "0 0 20 20", fill: s <= Math.round(rating) ? accent : `${fg}20`, children: /* @__PURE__ */ jsxRuntime.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)) });
2423
- const text = /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.8rem", color: `${fg}80`, fontWeight: 500 }, children: label });
2424
- const inner = /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.5rem" }, children: [
2425
- stars,
2426
- text
2663
+ const content = /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
2664
+ marginTop: "1.5rem",
2665
+ display: "flex",
2666
+ alignItems: "center",
2667
+ justifyContent: "center",
2668
+ gap: "0.625rem"
2669
+ }, children: [
2670
+ /* @__PURE__ */ jsxRuntime.jsx("span", { dangerouslySetInnerHTML: { __html: GOOGLE_G } }),
2671
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "inline-flex", gap: "2px" }, children: [1, 2, 3, 4, 5].map((s) => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: 16, height: 16, viewBox: "0 0 20 20", fill: s <= Math.round(rating) ? "#FBBC05" : `${fg}20`, children: /* @__PURE__ */ jsxRuntime.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)) }),
2672
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.9rem", color: `${fg}AA`, fontWeight: 500 }, children: label })
2427
2673
  ] });
2428
2674
  if (url) {
2429
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: "1.25rem", textAlign: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(
2430
- "a",
2431
- {
2432
- href: url,
2433
- target: "_blank",
2434
- rel: "noopener noreferrer",
2435
- style: { textDecoration: "none", display: "inline-flex", alignItems: "center", gap: "0.5rem" },
2436
- children: inner
2437
- }
2438
- ) });
2675
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { href: url, target: "_blank", rel: "noopener noreferrer", style: { textDecoration: "none", display: "block" }, children: content });
2439
2676
  }
2440
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: "1.25rem", display: "flex", justifyContent: "center" }, children: inner });
2677
+ return content;
2441
2678
  }
2442
2679
  function CTASection({ section, theme, tracking, onEvent }) {
2443
2680
  const { title, subtitle, buttons } = section.content;
@@ -2814,15 +3051,26 @@ function SuccessState({ theme, heading, message, couponCode }) {
2814
3051
  }, children: couponCode })
2815
3052
  ] }) });
2816
3053
  }
3054
+ 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>';
3055
+ 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>';
2817
3056
  function TestimonialsSection({ section, theme }) {
2818
3057
  const c = section.content;
2819
- const layout = section.config?.layout || "grid";
3058
+ section.config?.layout || "grid";
2820
3059
  const reviews = c.reviews || [];
2821
3060
  if (reviews.length === 0) return null;
2822
3061
  const overallRating = c.rating ?? 5;
2823
3062
  const reviewCount = c.review_count ?? reviews.length;
2824
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "3rem 1.5rem", maxWidth: 640, margin: "0 auto" }, children: [
2825
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center", marginBottom: "2rem" }, children: [
3063
+ const googleUrl = section.config?.google_url || void 0;
3064
+ const directionsUrl = section.config?.directions_url || void 0;
3065
+ `reviews-${section.id}`;
3066
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "3rem 0", overflow: "hidden" }, children: [
3067
+ /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: `
3068
+ .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)}
3069
+ .rv-scroll::-webkit-scrollbar{display:none}
3070
+ .rv-card{flex:0 0 clamp(260px,40vw,320px);scroll-snap-align:start}
3071
+ ` } }),
3072
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center", marginBottom: "2rem", padding: "0 1.5rem" }, children: [
3073
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", justifyContent: "center", marginBottom: "0.75rem" }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { dangerouslySetInnerHTML: { __html: GOOGLE_G_LG } }) }),
2826
3074
  c.heading && /* @__PURE__ */ jsxRuntime.jsx("h2", { style: {
2827
3075
  fontSize: "clamp(1.25rem, 4vw, 1.75rem)",
2828
3076
  fontWeight: 300,
@@ -2833,42 +3081,121 @@ function TestimonialsSection({ section, theme }) {
2833
3081
  color: theme.fg
2834
3082
  }, children: /* @__PURE__ */ jsxRuntime.jsx(AnimatedText, { text: c.heading }) }),
2835
3083
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: "0.5rem", marginBottom: "0.25rem" }, children: [
2836
- /* @__PURE__ */ jsxRuntime.jsx(Stars, { rating: overallRating, color: theme.accent, size: 18 }),
3084
+ /* @__PURE__ */ jsxRuntime.jsx(Stars, { rating: overallRating, color: "#FBBC05", size: 18 }),
2837
3085
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "1.1rem", fontWeight: 500, color: theme.fg }, children: /* @__PURE__ */ jsxRuntime.jsx(AnimatedText, { text: overallRating.toFixed(1) }) })
2838
3086
  ] }),
2839
3087
  c.subtitle ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.8rem", color: theme.muted, margin: 0, letterSpacing: "0.1em", textTransform: "uppercase" }, children: c.subtitle }) : /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { fontSize: "0.8rem", color: theme.muted, margin: 0, letterSpacing: "0.1em", textTransform: "uppercase" }, children: [
2840
3088
  "from ",
2841
3089
  /* @__PURE__ */ jsxRuntime.jsx(AnimatedText, { text: String(reviewCount) }),
2842
3090
  " reviews"
3091
+ ] }),
3092
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "0.625rem", justifyContent: "center", marginTop: "1.5rem", flexWrap: "wrap" }, children: [
3093
+ googleUrl && /* @__PURE__ */ jsxRuntime.jsxs("a", { href: googleUrl, target: "_blank", rel: "noopener noreferrer", style: {
3094
+ display: "inline-flex",
3095
+ alignItems: "center",
3096
+ gap: "0.5rem",
3097
+ padding: "clamp(0.75rem, 2vw, 0.875rem) clamp(1.25rem, 3vw, 2rem)",
3098
+ background: theme.fg,
3099
+ color: theme.bg,
3100
+ textDecoration: "none",
3101
+ fontSize: "clamp(0.8rem, 2vw, 0.9rem)",
3102
+ fontWeight: 600,
3103
+ letterSpacing: "0.06em",
3104
+ textTransform: "uppercase"
3105
+ }, children: [
3106
+ /* @__PURE__ */ jsxRuntime.jsx("span", { dangerouslySetInnerHTML: { __html: GOOGLE_G_SM } }),
3107
+ `see all ${reviewCount} reviews`
3108
+ ] }),
3109
+ directionsUrl && /* @__PURE__ */ jsxRuntime.jsx("a", { href: directionsUrl, target: "_blank", rel: "noopener noreferrer", style: {
3110
+ display: "inline-flex",
3111
+ alignItems: "center",
3112
+ gap: "0.5rem",
3113
+ padding: "clamp(0.75rem, 2vw, 0.875rem) clamp(1.25rem, 3vw, 2rem)",
3114
+ border: `1px solid ${theme.fg}25`,
3115
+ color: theme.fg,
3116
+ textDecoration: "none",
3117
+ fontSize: "clamp(0.8rem, 2vw, 0.9rem)",
3118
+ fontWeight: 600,
3119
+ letterSpacing: "0.06em",
3120
+ textTransform: "uppercase"
3121
+ }, children: "get directions" })
2843
3122
  ] })
2844
3123
  ] }),
2845
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
2846
- display: "grid",
2847
- gridTemplateColumns: layout === "list" ? "1fr" : `repeat(${Math.min(reviews.length, 2)}, 1fr)`,
2848
- gap: "0.75rem"
2849
- }, children: reviews.map((review, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
2850
- background: theme.surface,
2851
- border: `1px solid ${theme.fg}08`,
2852
- padding: "1.25rem"
2853
- }, children: [
2854
- /* @__PURE__ */ jsxRuntime.jsx(Stars, { rating: review.rating ?? 5, color: theme.accent, size: 14 }),
2855
- /* @__PURE__ */ jsxRuntime.jsxs("p", { style: {
2856
- fontSize: "0.88rem",
2857
- color: `${theme.fg}CC`,
2858
- margin: "0.75rem 0",
2859
- lineHeight: 1.6,
2860
- fontWeight: 300,
2861
- fontStyle: "italic"
3124
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rv-scroll", children: [
3125
+ reviews.map((review, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rv-card", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
3126
+ background: theme.surface,
3127
+ border: `1px solid ${theme.fg}08`,
3128
+ padding: "clamp(1rem, 3vw, 1.5rem)",
3129
+ height: "100%",
3130
+ display: "flex",
3131
+ flexDirection: "column"
2862
3132
  }, children: [
2863
- '"',
2864
- review.text,
2865
- '"'
2866
- ] }),
2867
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "baseline", gap: "0.375rem" }, children: [
2868
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.8rem", fontWeight: 500, color: theme.fg }, children: review.name }),
2869
- review.location && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.7rem", color: theme.muted }, children: review.location })
2870
- ] })
2871
- ] }, i)) })
3133
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "0.75rem" }, children: [
3134
+ /* @__PURE__ */ jsxRuntime.jsx(Stars, { rating: review.rating ?? 5, color: "#FBBC05", size: 13 }),
3135
+ review.location && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.6rem", letterSpacing: "0.15em", textTransform: "uppercase", color: theme.muted, fontWeight: 500 }, children: review.location })
3136
+ ] }),
3137
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: {
3138
+ fontSize: "0.9rem",
3139
+ color: `${theme.fg}B0`,
3140
+ margin: "0 0 auto",
3141
+ lineHeight: 1.65,
3142
+ fontWeight: 400,
3143
+ paddingBottom: "1rem"
3144
+ }, children: `\u201C${review.text}\u201D` }),
3145
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { borderTop: `1px solid ${theme.fg}08`, paddingTop: "0.75rem", display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
3146
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
3147
+ width: 28,
3148
+ height: 28,
3149
+ borderRadius: "50%",
3150
+ background: `${theme.accent}18`,
3151
+ border: `1px solid ${theme.accent}15`,
3152
+ display: "flex",
3153
+ alignItems: "center",
3154
+ justifyContent: "center",
3155
+ flexShrink: 0
3156
+ }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.7rem", fontWeight: 600, color: theme.accent, textTransform: "uppercase" }, children: review.name.charAt(0) }) }),
3157
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.8rem", fontWeight: 500, color: `${theme.fg}AA` }, children: review.name })
3158
+ ] })
3159
+ ] }) }, i)),
3160
+ googleUrl && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rv-card", children: /* @__PURE__ */ jsxRuntime.jsxs("a", { href: googleUrl, target: "_blank", rel: "noopener noreferrer", style: {
3161
+ height: "100%",
3162
+ display: "flex",
3163
+ flexDirection: "column",
3164
+ alignItems: "center",
3165
+ justifyContent: "center",
3166
+ gap: "0.75rem",
3167
+ background: theme.surface,
3168
+ border: `1px solid ${theme.fg}08`,
3169
+ padding: "clamp(1rem, 3vw, 1.5rem)",
3170
+ textDecoration: "none"
3171
+ }, children: [
3172
+ /* @__PURE__ */ jsxRuntime.jsx("span", { dangerouslySetInnerHTML: { __html: GOOGLE_G_LG } }),
3173
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.85rem", color: theme.fg, fontWeight: 500 }, children: `read all ${reviewCount}` }),
3174
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.75rem", color: theme.muted, letterSpacing: "0.08em" }, children: "reviews on google" })
3175
+ ] }) })
3176
+ ] }),
3177
+ googleUrl && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { textAlign: "center", marginTop: "1.5rem", padding: "0 1.5rem" }, children: /* @__PURE__ */ jsxRuntime.jsxs(
3178
+ "a",
3179
+ {
3180
+ href: googleUrl,
3181
+ target: "_blank",
3182
+ rel: "noopener noreferrer",
3183
+ style: {
3184
+ display: "inline-flex",
3185
+ alignItems: "center",
3186
+ gap: "0.5rem",
3187
+ fontSize: "0.8rem",
3188
+ color: theme.muted,
3189
+ textDecoration: "none",
3190
+ letterSpacing: "0.08em",
3191
+ fontWeight: 500
3192
+ },
3193
+ children: [
3194
+ /* @__PURE__ */ jsxRuntime.jsx("span", { dangerouslySetInnerHTML: { __html: GOOGLE_G_SM } }),
3195
+ `read all ${reviewCount} reviews on google`
3196
+ ]
3197
+ }
3198
+ ) })
2872
3199
  ] });
2873
3200
  }
2874
3201
  function Stars({ rating, color, size }) {
@@ -3292,7 +3619,7 @@ function SectionRenderer({
3292
3619
  const el = (() => {
3293
3620
  switch (section.type) {
3294
3621
  case "hero":
3295
- return /* @__PURE__ */ jsxRuntime.jsx(HeroSection, { section, theme, tracking, onEvent });
3622
+ return /* @__PURE__ */ jsxRuntime.jsx(HeroSection, { section, theme, tracking, onEvent, data });
3296
3623
  case "collage_hero":
3297
3624
  return /* @__PURE__ */ jsxRuntime.jsx(CollageHeroSection, { section, theme, tracking, onEvent });
3298
3625
  case "text":
@@ -3302,7 +3629,7 @@ function SectionRenderer({
3302
3629
  case "video":
3303
3630
  return /* @__PURE__ */ jsxRuntime.jsx(VideoSection, { section, theme });
3304
3631
  case "gallery":
3305
- return /* @__PURE__ */ jsxRuntime.jsx(GallerySection, { section, theme });
3632
+ return /* @__PURE__ */ jsxRuntime.jsx(GallerySection, { section, theme, onEvent });
3306
3633
  case "cta":
3307
3634
  return /* @__PURE__ */ jsxRuntime.jsx(CTASection, { section, theme, tracking, onEvent });
3308
3635
  case "stats":