@neowhale/storefront 0.2.11 → 0.2.13

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.
@@ -1206,12 +1206,383 @@ function useCoupons() {
1206
1206
  }, []);
1207
1207
  return { validation, loading, error, validate, apply, remove, clear };
1208
1208
  }
1209
+ function SectionRenderer({
1210
+ section,
1211
+ data,
1212
+ theme
1213
+ }) {
1214
+ const [showCOA, setShowCOA] = useState(false);
1215
+ const el = (() => {
1216
+ switch (section.type) {
1217
+ case "hero":
1218
+ return /* @__PURE__ */ jsx(HeroSection, { section, theme });
1219
+ case "text":
1220
+ return /* @__PURE__ */ jsx(TextSection, { section, theme });
1221
+ case "image":
1222
+ return /* @__PURE__ */ jsx(ImageSection, { section, theme });
1223
+ case "video":
1224
+ return /* @__PURE__ */ jsx(VideoSection, { section, theme });
1225
+ case "gallery":
1226
+ return /* @__PURE__ */ jsx(GallerySection, { section, theme });
1227
+ case "cta":
1228
+ return /* @__PURE__ */ jsx(CTASection, { section, theme });
1229
+ case "stats":
1230
+ return /* @__PURE__ */ jsx(StatsSection, { section, theme });
1231
+ case "product_card":
1232
+ return /* @__PURE__ */ jsx(ProductCardSection, { section, data, theme });
1233
+ case "coa_viewer":
1234
+ return /* @__PURE__ */ jsx(COAViewerSection, { section, data, theme, onShowCOA: () => setShowCOA(true) });
1235
+ case "social_links":
1236
+ return /* @__PURE__ */ jsx(SocialLinksSection, { section, theme });
1237
+ case "divider":
1238
+ return /* @__PURE__ */ jsx(DividerSection, { theme });
1239
+ default:
1240
+ return null;
1241
+ }
1242
+ })();
1243
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1244
+ el,
1245
+ showCOA && data?.coa && /* @__PURE__ */ jsx(COAModal, { coa: data.coa, theme, onClose: () => setShowCOA(false) })
1246
+ ] });
1247
+ }
1248
+ function HeroSection({ section, theme }) {
1249
+ const { title, subtitle, background_image, cta_text, cta_url } = section.content;
1250
+ return /* @__PURE__ */ jsxs(
1251
+ "div",
1252
+ {
1253
+ style: {
1254
+ position: "relative",
1255
+ minHeight: "60vh",
1256
+ display: "flex",
1257
+ flexDirection: "column",
1258
+ justifyContent: "center",
1259
+ alignItems: "center",
1260
+ textAlign: "center",
1261
+ padding: "3rem 1.5rem",
1262
+ backgroundImage: background_image ? `url(${background_image})` : void 0,
1263
+ backgroundSize: "cover",
1264
+ backgroundPosition: "center"
1265
+ },
1266
+ children: [
1267
+ background_image && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" } }),
1268
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative", zIndex: 1, maxWidth: 640 }, children: [
1269
+ title && /* @__PURE__ */ jsx("h1", { style: {
1270
+ fontSize: "clamp(2rem, 8vw, 3rem)",
1271
+ fontWeight: 300,
1272
+ fontFamily: theme.fontDisplay || "inherit",
1273
+ margin: "0 0 1rem",
1274
+ lineHeight: 1.15,
1275
+ letterSpacing: "-0.02em",
1276
+ color: theme.fg
1277
+ }, children: title }),
1278
+ subtitle && /* @__PURE__ */ jsx("p", { style: {
1279
+ fontSize: "0.85rem",
1280
+ color: theme.accent,
1281
+ margin: "0 0 2rem",
1282
+ lineHeight: 1.6,
1283
+ textTransform: "uppercase",
1284
+ letterSpacing: "0.15em"
1285
+ }, children: subtitle }),
1286
+ cta_text && cta_url && /* @__PURE__ */ jsx(
1287
+ "a",
1288
+ {
1289
+ href: cta_url,
1290
+ style: {
1291
+ display: "inline-block",
1292
+ padding: "0.875rem 2rem",
1293
+ background: theme.fg,
1294
+ color: theme.bg,
1295
+ textDecoration: "none",
1296
+ fontSize: "0.85rem",
1297
+ fontWeight: 500,
1298
+ letterSpacing: "0.08em",
1299
+ textTransform: "uppercase"
1300
+ },
1301
+ children: cta_text
1302
+ }
1303
+ )
1304
+ ] })
1305
+ ]
1306
+ }
1307
+ );
1308
+ }
1309
+ function TextSection({ section, theme }) {
1310
+ const { heading, body } = section.content;
1311
+ const align = section.config?.align || "left";
1312
+ return /* @__PURE__ */ jsxs("div", { style: { padding: "2rem 1.5rem", maxWidth: 640, margin: "0 auto", textAlign: align }, children: [
1313
+ heading && /* @__PURE__ */ jsx("h2", { style: {
1314
+ fontSize: 11,
1315
+ fontWeight: 500,
1316
+ textTransform: "uppercase",
1317
+ letterSpacing: "0.25em",
1318
+ color: `${theme.fg}40`,
1319
+ margin: "0 0 0.75rem"
1320
+ }, children: heading }),
1321
+ body && /* @__PURE__ */ jsx("div", { style: { color: `${theme.fg}99`, lineHeight: 1.7, fontSize: "0.9rem", fontWeight: 300, whiteSpace: "pre-wrap" }, children: body })
1322
+ ] });
1323
+ }
1324
+ function ImageSection({ section, theme }) {
1325
+ const { url, alt, caption } = section.content;
1326
+ const contained = section.config?.contained !== false;
1327
+ if (!url) return null;
1328
+ return /* @__PURE__ */ jsxs("div", { style: { padding: contained ? "1.5rem" : 0, maxWidth: contained ? 640 : void 0, margin: contained ? "0 auto" : void 0 }, children: [
1329
+ /* @__PURE__ */ jsx(
1330
+ "img",
1331
+ {
1332
+ src: url,
1333
+ alt: alt || "",
1334
+ style: { width: "100%", display: "block", objectFit: "cover" }
1335
+ }
1336
+ ),
1337
+ caption && /* @__PURE__ */ jsx("p", { style: { fontSize: "0.8rem", color: theme.muted, textAlign: "center", marginTop: "0.75rem" }, children: caption })
1338
+ ] });
1339
+ }
1340
+ function VideoSection({ section, theme }) {
1341
+ const { url, poster } = section.content;
1342
+ if (!url) return null;
1343
+ const isEmbed = url.includes("youtube") || url.includes("youtu.be") || url.includes("vimeo");
1344
+ 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(
1345
+ "iframe",
1346
+ {
1347
+ src: toEmbedUrl(url),
1348
+ style: { position: "absolute", top: 0, left: 0, width: "100%", height: "100%", border: "none" },
1349
+ allow: "autoplay; fullscreen",
1350
+ title: "Video"
1351
+ }
1352
+ ) }) : /* @__PURE__ */ jsx(
1353
+ "video",
1354
+ {
1355
+ src: url,
1356
+ poster,
1357
+ controls: true,
1358
+ style: { width: "100%", display: "block", background: theme.surface }
1359
+ }
1360
+ ) });
1361
+ }
1362
+ function GallerySection({ section, theme }) {
1363
+ const { images } = section.content;
1364
+ const columns = section.config?.columns || 3;
1365
+ if (!images || images.length === 0) return null;
1366
+ 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(
1367
+ "img",
1368
+ {
1369
+ src: img.url,
1370
+ alt: img.alt || "",
1371
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
1372
+ }
1373
+ ) }, i)) }) });
1374
+ }
1375
+ function CTASection({ section, theme }) {
1376
+ const { buttons } = section.content;
1377
+ if (!buttons || buttons.length === 0) return null;
1378
+ return /* @__PURE__ */ jsx("div", { style: { padding: "2rem 1.5rem", maxWidth: 480, margin: "0 auto", display: "flex", flexDirection: "column", gap: "0.75rem" }, children: buttons.map((btn, i) => {
1379
+ const isPrimary = btn.style !== "outline";
1380
+ return /* @__PURE__ */ jsx(
1381
+ "a",
1382
+ {
1383
+ href: btn.url,
1384
+ style: {
1385
+ display: "block",
1386
+ width: "100%",
1387
+ padding: "0.875rem",
1388
+ background: isPrimary ? theme.fg : "transparent",
1389
+ color: isPrimary ? theme.bg : theme.fg,
1390
+ border: isPrimary ? "none" : `1px solid ${theme.fg}20`,
1391
+ fontSize: "0.85rem",
1392
+ fontWeight: 500,
1393
+ textAlign: "center",
1394
+ textDecoration: "none",
1395
+ boxSizing: "border-box",
1396
+ letterSpacing: "0.08em",
1397
+ textTransform: "uppercase"
1398
+ },
1399
+ children: btn.text
1400
+ },
1401
+ i
1402
+ );
1403
+ }) });
1404
+ }
1405
+ function StatsSection({ section, theme }) {
1406
+ const { stats } = section.content;
1407
+ const layout = section.config?.layout;
1408
+ if (!stats || stats.length === 0) return null;
1409
+ if (layout === "list") {
1410
+ return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 640, margin: "0 auto" }, children: stats.map((stat, i) => /* @__PURE__ */ jsxs("div", { children: [
1411
+ /* @__PURE__ */ jsxs("div", { style: {
1412
+ display: "flex",
1413
+ justifyContent: "space-between",
1414
+ alignItems: "baseline",
1415
+ padding: "0.625rem 0"
1416
+ }, children: [
1417
+ /* @__PURE__ */ jsx("span", { style: {
1418
+ fontSize: 12,
1419
+ textTransform: "uppercase",
1420
+ letterSpacing: "0.15em",
1421
+ color: `${theme.fg}66`
1422
+ }, children: stat.label }),
1423
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 14, fontWeight: 300, color: `${theme.fg}CC` }, children: stat.value })
1424
+ ] }),
1425
+ i < stats.length - 1 && /* @__PURE__ */ jsx("hr", { style: { border: "none", borderTop: `1px solid ${theme.fg}0A`, margin: 0 } })
1426
+ ] }, i)) });
1427
+ }
1428
+ const columns = Math.min(stats.length, 4);
1429
+ return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 640, margin: "0 auto" }, children: /* @__PURE__ */ jsx("div", { style: {
1430
+ display: "grid",
1431
+ gridTemplateColumns: `repeat(${columns}, 1fr)`,
1432
+ border: `1px solid ${theme.fg}0F`
1433
+ }, children: stats.map((stat, i) => /* @__PURE__ */ jsxs("div", { style: {
1434
+ padding: "1.25rem 0.5rem",
1435
+ textAlign: "center",
1436
+ borderRight: i < stats.length - 1 ? `1px solid ${theme.fg}0F` : void 0
1437
+ }, children: [
1438
+ /* @__PURE__ */ jsx("div", { style: {
1439
+ fontFamily: theme.fontDisplay || "inherit",
1440
+ fontSize: "clamp(1.5rem, 5vw, 2rem)",
1441
+ fontWeight: 300,
1442
+ lineHeight: 1,
1443
+ color: theme.fg
1444
+ }, children: stat.value }),
1445
+ /* @__PURE__ */ jsx("div", { style: {
1446
+ fontSize: 11,
1447
+ fontWeight: 500,
1448
+ textTransform: "uppercase",
1449
+ letterSpacing: "0.25em",
1450
+ color: theme.accent,
1451
+ marginTop: "0.5rem"
1452
+ }, children: stat.label })
1453
+ ] }, i)) }) });
1454
+ }
1455
+ function ProductCardSection({ section, data, theme }) {
1456
+ const product = data?.product;
1457
+ const c = section.content;
1458
+ const name = c.name || product?.name || "";
1459
+ const description = c.description || product?.description || "";
1460
+ const imageUrl = c.image_url || product?.featured_image || null;
1461
+ const url = c.url || null;
1462
+ return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 480, margin: "0 auto" }, children: /* @__PURE__ */ jsxs("div", { style: { background: theme.surface, overflow: "hidden" }, children: [
1463
+ 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" } }) }),
1464
+ /* @__PURE__ */ jsxs("div", { style: { padding: "1.25rem" }, children: [
1465
+ /* @__PURE__ */ jsx("h3", { style: { fontSize: "1.25rem", fontWeight: 600, margin: "0 0 0.5rem", color: theme.fg }, children: name }),
1466
+ description && /* @__PURE__ */ jsx("p", { style: { fontSize: "0.9rem", color: theme.muted, margin: "0 0 1rem", lineHeight: 1.5 }, children: description }),
1467
+ url && /* @__PURE__ */ jsx(
1468
+ "a",
1469
+ {
1470
+ href: url,
1471
+ style: {
1472
+ display: "block",
1473
+ width: "100%",
1474
+ padding: "0.75rem",
1475
+ background: theme.fg,
1476
+ color: theme.bg,
1477
+ textAlign: "center",
1478
+ textDecoration: "none",
1479
+ fontSize: "0.85rem",
1480
+ fontWeight: 500,
1481
+ boxSizing: "border-box",
1482
+ letterSpacing: "0.08em",
1483
+ textTransform: "uppercase"
1484
+ },
1485
+ children: "View Product"
1486
+ }
1487
+ )
1488
+ ] })
1489
+ ] }) });
1490
+ }
1491
+ function COAViewerSection({
1492
+ section,
1493
+ data,
1494
+ theme,
1495
+ onShowCOA
1496
+ }) {
1497
+ const coa = data?.coa;
1498
+ const c = section.content;
1499
+ if (!coa) return null;
1500
+ const buttonStyle = {
1501
+ width: "100%",
1502
+ padding: "0.875rem",
1503
+ background: theme.accent,
1504
+ color: theme.bg,
1505
+ border: "none",
1506
+ fontSize: "0.85rem",
1507
+ fontWeight: 500,
1508
+ cursor: "pointer",
1509
+ letterSpacing: "0.08em",
1510
+ textTransform: "uppercase",
1511
+ textAlign: "center",
1512
+ textDecoration: "none",
1513
+ display: "block",
1514
+ boxSizing: "border-box"
1515
+ };
1516
+ if (coa.viewer_url) {
1517
+ return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 480, margin: "0 auto" }, children: /* @__PURE__ */ jsx("a", { href: coa.viewer_url, target: "_blank", rel: "noopener noreferrer", style: buttonStyle, children: c.button_text || "View Lab Results" }) });
1518
+ }
1519
+ return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 480, margin: "0 auto" }, children: /* @__PURE__ */ jsx("button", { onClick: onShowCOA, style: buttonStyle, children: c.button_text || "View Lab Results" }) });
1520
+ }
1521
+ function SocialLinksSection({ section, theme }) {
1522
+ const { links } = section.content;
1523
+ if (!links || links.length === 0) return null;
1524
+ return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", display: "flex", justifyContent: "center", gap: "1.5rem", flexWrap: "wrap" }, children: links.map((link, i) => /* @__PURE__ */ jsx(
1525
+ "a",
1526
+ {
1527
+ href: link.url,
1528
+ target: "_blank",
1529
+ rel: "noopener noreferrer",
1530
+ style: {
1531
+ color: theme.muted,
1532
+ textDecoration: "none",
1533
+ fontSize: "0.85rem",
1534
+ fontWeight: 500,
1535
+ textTransform: "capitalize",
1536
+ letterSpacing: "0.03em"
1537
+ },
1538
+ children: link.platform
1539
+ },
1540
+ i
1541
+ )) });
1542
+ }
1543
+ function DividerSection({ theme }) {
1544
+ 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 } }) });
1545
+ }
1546
+ function COAModal({ coa, theme, onClose }) {
1547
+ return /* @__PURE__ */ jsxs("div", { style: { position: "fixed", inset: 0, zIndex: 9999, background: "rgba(0,0,0,0.95)", display: "flex", flexDirection: "column" }, children: [
1548
+ /* @__PURE__ */ jsxs("div", { style: {
1549
+ display: "flex",
1550
+ justifyContent: "space-between",
1551
+ alignItems: "center",
1552
+ padding: "0.75rem 1rem",
1553
+ borderBottom: `1px solid ${theme.fg}10`
1554
+ }, children: [
1555
+ /* @__PURE__ */ jsx("span", { style: { color: "#fff", fontWeight: 500, fontSize: "0.85rem" }, children: coa.document_name || "Lab Results" }),
1556
+ /* @__PURE__ */ jsx(
1557
+ "button",
1558
+ {
1559
+ onClick: onClose,
1560
+ style: {
1561
+ background: `${theme.fg}10`,
1562
+ border: "none",
1563
+ color: "#fff",
1564
+ fontSize: "0.85rem",
1565
+ cursor: "pointer",
1566
+ padding: "0.375rem 0.75rem"
1567
+ },
1568
+ children: "Close"
1569
+ }
1570
+ )
1571
+ ] }),
1572
+ /* @__PURE__ */ jsx("iframe", { src: coa.url, style: { flex: 1, border: "none", background: "#fff" }, title: "Lab Results" })
1573
+ ] });
1574
+ }
1575
+ function toEmbedUrl(url) {
1576
+ const ytMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)/);
1577
+ if (ytMatch) return `https://www.youtube.com/embed/${ytMatch[1]}`;
1578
+ const vimeoMatch = url.match(/vimeo\.com\/(\d+)/);
1579
+ if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}`;
1580
+ return url;
1581
+ }
1209
1582
  function QRLandingPage({
1210
1583
  code,
1211
1584
  gatewayUrl = "https://whale-gateway.fly.dev",
1212
- renderProduct,
1213
- renderCOA,
1214
- renderPage,
1585
+ renderSection,
1215
1586
  onDataLoaded,
1216
1587
  onError
1217
1588
  }) {
@@ -1261,387 +1632,140 @@ function QRLandingPage({
1261
1632
  if (state === "expired") return /* @__PURE__ */ jsx(DefaultExpired, {});
1262
1633
  if (state === "error") return /* @__PURE__ */ jsx(DefaultError, { message: errorMsg });
1263
1634
  if (!data) return null;
1264
- if (renderPage) return /* @__PURE__ */ jsx(Fragment, { children: renderPage(data) });
1265
- return /* @__PURE__ */ jsx(ProductLanding, { data, renderProduct, renderCOA });
1635
+ const lp = data.landing_page;
1636
+ const sections = lp?.sections ?? buildDefaultSections(data);
1637
+ const theme = extractTheme(data, lp);
1638
+ const fontFamily = lp?.font_family || data.store?.theme?.fontDisplay || "system-ui, -apple-system, sans-serif";
1639
+ const logoUrl = data.qr_code.logo_url || data.store?.logo_url;
1640
+ const storeName = data.store?.name;
1641
+ const sorted = [...sections].sort((a, b) => a.order - b.order);
1642
+ return /* @__PURE__ */ jsxs("div", { style: { minHeight: "100dvh", background: theme.bg, color: theme.fg, fontFamily }, children: [
1643
+ lp?.custom_css && /* @__PURE__ */ jsx("style", { children: lp.custom_css }),
1644
+ logoUrl && /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ jsx("img", { src: logoUrl, alt: storeName || "Store", style: { height: 40, objectFit: "contain" } }) }),
1645
+ sorted.map((section) => {
1646
+ const defaultRenderer = () => /* @__PURE__ */ jsx(SectionRenderer, { section, data, theme }, section.id);
1647
+ if (renderSection) {
1648
+ return /* @__PURE__ */ jsx("div", { children: renderSection(section, defaultRenderer) }, section.id);
1649
+ }
1650
+ return /* @__PURE__ */ jsx(SectionRenderer, { section, data, theme }, section.id);
1651
+ }),
1652
+ storeName && /* @__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: [
1653
+ storeName,
1654
+ data.store?.tagline ? ` \u2014 ${data.store.tagline}` : ""
1655
+ ] }) })
1656
+ ] });
1266
1657
  }
1267
- function extractTheme(data) {
1268
- const lp = data.qr_code.landing_page;
1658
+ function extractTheme(data, lp) {
1269
1659
  const t = data.store?.theme;
1660
+ const qrLp = data.qr_code.landing_page;
1270
1661
  return {
1271
- bg: lp.background_color || t?.background || "#050505",
1272
- fg: lp.text_color || t?.foreground || "#fafafa",
1273
- accent: t?.accent || "#E8E2D9",
1274
- accentDark: t?.accentDark || "#C8BFB2",
1275
- surface: t?.surface || "#0C0C0C",
1276
- surfaceLight: t?.surfaceLight || "#141414",
1277
- muted: t?.muted || "#8A8A8A",
1278
- border: t?.border || "#1C1C1C",
1662
+ bg: lp?.background_color || qrLp.background_color || t?.background || "#050505",
1663
+ fg: lp?.text_color || qrLp.text_color || t?.foreground || "#fafafa",
1664
+ accent: lp?.accent_color || t?.accent || "#E8E2D9",
1665
+ surface: t?.surface || "#111",
1666
+ muted: t?.muted || "#888",
1279
1667
  fontDisplay: t?.fontDisplay || "system-ui, -apple-system, sans-serif",
1280
- fontBody: t?.fontBody || "system-ui, -apple-system, sans-serif",
1281
- radius: t?.radius || "0px"
1282
- };
1283
- }
1284
- function ProductLanding({
1285
- data,
1286
- renderProduct,
1287
- renderCOA
1288
- }) {
1289
- const { qr_code: qr, store, product, coa } = data;
1290
- const lp = qr.landing_page;
1291
- const theme = extractTheme(data);
1292
- const [showCOA, setShowCOA] = useState(false);
1293
- const logoUrl = qr.logo_url || store?.logo_url;
1294
- const productImage = lp.image_url || product?.featured_image || null;
1295
- const productName = lp.title || product?.name || qr.name;
1296
- const description = lp.description || product?.description || "";
1297
- const ctaUrl = lp.cta_url || qr.destination_url;
1298
- const categoryName = product?.category_name ?? null;
1299
- const cf = product?.custom_fields;
1300
- const thca = toNum(cf?.thca_percentage);
1301
- const thc = toNum(cf?.d9_percentage);
1302
- const cbd = toNum(cf?.cbd_total);
1303
- const strainType = toStr(cf?.strain_type);
1304
- const batchNumber = toStr(cf?.batch_number);
1305
- const dateTested = toStr(cf?.date_tested);
1306
- const terpenes = toStr(cf?.terpenes);
1307
- const effects = toStr(cf?.effects);
1308
- const genetics = toStr(cf?.genetics);
1309
- const tagline = toStr(cf?.tagline);
1310
- const handleCOAClick = useCallback(() => {
1311
- if (coa) setShowCOA(true);
1312
- }, [coa]);
1313
- const labelStyle = {
1314
- fontSize: 11,
1315
- fontWeight: 500,
1316
- textTransform: "uppercase",
1317
- letterSpacing: "0.25em",
1318
- color: `${theme.fg}40`
1319
- // 40 = 25% opacity
1320
- };
1321
- const dividerStyle = {
1322
- border: "none",
1323
- borderTop: `1px solid ${theme.fg}0A`,
1324
- // 0A = 4% opacity
1325
- margin: 0
1326
- };
1327
- return /* @__PURE__ */ jsxs("div", { style: {
1328
- minHeight: "100dvh",
1329
- background: theme.bg,
1330
- color: theme.fg,
1331
- fontFamily: theme.fontBody
1332
- }, children: [
1333
- /* @__PURE__ */ jsxs("nav", { style: {
1334
- padding: "1.25rem 1.5rem",
1335
- display: "flex",
1336
- alignItems: "center",
1337
- justifyContent: "space-between",
1338
- maxWidth: 1280,
1339
- margin: "0 auto"
1340
- }, children: [
1341
- logoUrl ? /* @__PURE__ */ jsx("img", { src: logoUrl, alt: store?.name || "", style: { height: 28, objectFit: "contain" } }) : store?.name ? /* @__PURE__ */ jsx("span", { style: { fontFamily: theme.fontDisplay, fontWeight: 600, fontSize: "1rem", letterSpacing: "-0.02em" }, children: store.name }) : /* @__PURE__ */ jsx("span", {}),
1342
- /* @__PURE__ */ jsx(
1343
- "a",
1344
- {
1345
- href: ctaUrl,
1346
- style: {
1347
- fontSize: 11,
1348
- fontWeight: 500,
1349
- textTransform: "uppercase",
1350
- letterSpacing: "0.15em",
1351
- color: theme.fg,
1352
- textDecoration: "none",
1353
- padding: "0.5rem 1.25rem",
1354
- border: `1px solid ${theme.fg}10`,
1355
- transition: "border-color 0.3s"
1356
- },
1357
- children: "Shop"
1358
- }
1359
- )
1360
- ] }),
1361
- /* @__PURE__ */ jsxs("main", { style: { maxWidth: 560, margin: "0 auto", padding: "0 1.5rem 3rem" }, children: [
1362
- productImage && /* @__PURE__ */ jsx("div", { style: {
1363
- width: "100%",
1364
- aspectRatio: "1",
1365
- overflow: "hidden",
1366
- background: theme.surfaceLight,
1367
- display: "flex",
1368
- alignItems: "center",
1369
- justifyContent: "center",
1370
- padding: "2rem"
1371
- }, children: /* @__PURE__ */ jsx(
1372
- "img",
1373
- {
1374
- src: productImage,
1375
- alt: productName,
1376
- style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" }
1377
- }
1378
- ) }),
1379
- /* @__PURE__ */ jsxs("div", { style: { marginTop: "1.5rem", display: "flex", gap: "0.75rem", alignItems: "center" }, children: [
1380
- categoryName && /* @__PURE__ */ jsx("span", { style: {
1381
- fontSize: 11,
1382
- fontWeight: 500,
1383
- color: theme.accent,
1384
- textTransform: "uppercase",
1385
- letterSpacing: "0.15em"
1386
- }, children: categoryName }),
1387
- strainType && /* @__PURE__ */ jsx("span", { style: {
1388
- fontSize: 10,
1389
- fontWeight: 600,
1390
- textTransform: "uppercase",
1391
- letterSpacing: "0.08em",
1392
- padding: "0.2rem 0.5rem",
1393
- background: strainBadgeColor(strainType),
1394
- color: "#fff"
1395
- }, children: strainType })
1396
- ] }),
1397
- /* @__PURE__ */ jsx("h1", { style: {
1398
- fontFamily: theme.fontDisplay,
1399
- fontSize: "clamp(2rem, 8vw, 3rem)",
1400
- fontWeight: 300,
1401
- margin: "0.5rem 0 0",
1402
- lineHeight: 1.1,
1403
- letterSpacing: "-0.02em"
1404
- }, children: productName }),
1405
- tagline && /* @__PURE__ */ jsx("p", { style: {
1406
- fontFamily: theme.fontDisplay,
1407
- fontSize: "1.1rem",
1408
- fontWeight: 300,
1409
- color: `${theme.fg}80`,
1410
- margin: "0.5rem 0 0"
1411
- }, children: tagline }),
1412
- renderProduct ? renderProduct(data) : null,
1413
- (thca != null || thc != null || cbd != null) && /* @__PURE__ */ jsxs("div", { style: {
1414
- marginTop: "1.75rem",
1415
- border: `1px solid ${theme.fg}0F`,
1416
- display: "grid",
1417
- gridTemplateColumns: `repeat(${[thca, thc, cbd].filter((v) => v != null).length}, 1fr)`
1418
- }, children: [
1419
- thca != null && /* @__PURE__ */ jsx(CannabinoidStat, { label: "THCa", value: thca, theme, showBorder: thc != null || cbd != null }),
1420
- thc != null && /* @__PURE__ */ jsx(CannabinoidStat, { label: "\u03949 THC", value: thc, theme, showBorder: cbd != null }),
1421
- cbd != null && /* @__PURE__ */ jsx(CannabinoidStat, { label: "CBD", value: cbd, theme, showBorder: false })
1422
- ] }),
1423
- (genetics || terpenes || effects || batchNumber || dateTested) && /* @__PURE__ */ jsxs("div", { style: { marginTop: "1.75rem" }, children: [
1424
- /* @__PURE__ */ jsx("p", { style: { ...labelStyle, marginBottom: "0.75rem" }, children: "Details" }),
1425
- /* @__PURE__ */ jsxs("div", { children: [
1426
- genetics && /* @__PURE__ */ jsxs(Fragment, { children: [
1427
- /* @__PURE__ */ jsx(DetailRow, { label: "Genetics", value: genetics, theme }),
1428
- /* @__PURE__ */ jsx("hr", { style: dividerStyle })
1429
- ] }),
1430
- terpenes && /* @__PURE__ */ jsxs(Fragment, { children: [
1431
- /* @__PURE__ */ jsx(DetailRow, { label: "Terpenes", value: terpenes, theme }),
1432
- /* @__PURE__ */ jsx("hr", { style: dividerStyle })
1433
- ] }),
1434
- effects && /* @__PURE__ */ jsxs(Fragment, { children: [
1435
- /* @__PURE__ */ jsx(DetailRow, { label: "Effects", value: effects, theme }),
1436
- /* @__PURE__ */ jsx("hr", { style: dividerStyle })
1437
- ] }),
1438
- batchNumber && /* @__PURE__ */ jsxs(Fragment, { children: [
1439
- /* @__PURE__ */ jsx(DetailRow, { label: "Batch", value: batchNumber, theme }),
1440
- /* @__PURE__ */ jsx("hr", { style: dividerStyle })
1441
- ] }),
1442
- dateTested && /* @__PURE__ */ jsx(DetailRow, { label: "Tested", value: formatDate(dateTested), theme })
1443
- ] })
1444
- ] }),
1445
- description && /* @__PURE__ */ jsxs("div", { style: { marginTop: "1.75rem" }, children: [
1446
- /* @__PURE__ */ jsx("p", { style: { ...labelStyle, marginBottom: "0.75rem" }, children: "About" }),
1447
- /* @__PURE__ */ jsx("p", { style: {
1448
- fontSize: "0.9rem",
1449
- fontWeight: 300,
1450
- color: `${theme.fg}99`,
1451
- lineHeight: 1.7,
1452
- margin: 0
1453
- }, children: description })
1454
- ] }),
1455
- coa && /* @__PURE__ */ jsxs("div", { style: { marginTop: "1.75rem" }, children: [
1456
- /* @__PURE__ */ jsx("p", { style: { ...labelStyle, marginBottom: "0.75rem" }, children: "Lab Results" }),
1457
- batchNumber && /* @__PURE__ */ jsxs("p", { style: { fontSize: 12, color: `${theme.fg}60`, margin: "0 0 0.5rem", letterSpacing: "0.05em" }, children: [
1458
- "Batch ",
1459
- batchNumber,
1460
- dateTested ? ` \xB7 ${formatDate(dateTested)}` : ""
1461
- ] }),
1462
- /* @__PURE__ */ jsxs(
1463
- "button",
1464
- {
1465
- onClick: handleCOAClick,
1466
- style: {
1467
- width: "100%",
1468
- position: "relative",
1469
- height: 180,
1470
- border: `1px solid ${theme.fg}0F`,
1471
- background: theme.surface,
1472
- cursor: "pointer",
1473
- overflow: "hidden",
1474
- padding: 0,
1475
- display: "block"
1476
- },
1477
- children: [
1478
- /* @__PURE__ */ jsx(
1479
- "iframe",
1480
- {
1481
- src: coa.url,
1482
- style: {
1483
- width: "200%",
1484
- height: "200%",
1485
- border: "none",
1486
- transform: "scale(0.5)",
1487
- transformOrigin: "top left",
1488
- pointerEvents: "none"
1489
- },
1490
- title: "COA Preview",
1491
- tabIndex: -1
1492
- }
1493
- ),
1494
- /* @__PURE__ */ jsx("div", { style: {
1495
- position: "absolute",
1496
- inset: 0,
1497
- background: `linear-gradient(to top, ${theme.bg}E6 0%, transparent 60%)`,
1498
- display: "flex",
1499
- alignItems: "flex-end",
1500
- justifyContent: "center",
1501
- paddingBottom: "1.25rem"
1502
- }, children: /* @__PURE__ */ jsx("span", { style: {
1503
- fontSize: 11,
1504
- fontWeight: 500,
1505
- textTransform: "uppercase",
1506
- letterSpacing: "0.2em",
1507
- color: theme.accent
1508
- }, children: "View Certificate of Analysis" }) })
1509
- ]
1510
- }
1511
- )
1512
- ] }),
1513
- renderCOA ? renderCOA(data) : null,
1514
- /* @__PURE__ */ jsx(
1515
- "a",
1516
- {
1517
- href: ctaUrl,
1518
- style: {
1519
- display: "block",
1520
- width: "100%",
1521
- marginTop: "2rem",
1522
- padding: "1rem",
1523
- background: theme.fg,
1524
- color: theme.bg,
1525
- fontFamily: theme.fontDisplay,
1526
- fontSize: "0.85rem",
1527
- fontWeight: 500,
1528
- textAlign: "center",
1529
- textDecoration: "none",
1530
- letterSpacing: "0.08em",
1531
- textTransform: "uppercase",
1532
- boxSizing: "border-box"
1533
- },
1534
- children: "Shop Online"
1535
- }
1536
- ),
1537
- /* @__PURE__ */ jsxs("footer", { style: {
1538
- marginTop: "3rem",
1539
- paddingTop: "1.5rem",
1540
- borderTop: `1px solid ${theme.fg}0A`,
1541
- textAlign: "center"
1542
- }, children: [
1543
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "center", gap: "2rem", marginBottom: "0.75rem" }, children: [
1544
- coa && /* @__PURE__ */ jsx(FooterLabel, { text: "Lab Verified" }),
1545
- /* @__PURE__ */ jsx(FooterLabel, { text: "Authentic Product" })
1546
- ] }),
1547
- store?.name && /* @__PURE__ */ jsxs("p", { style: { fontSize: 11, color: `${theme.fg}40`, margin: "0.5rem 0 0", letterSpacing: "0.1em" }, children: [
1548
- store.name,
1549
- store?.tagline ? ` \u2014 ${store.tagline}` : ""
1550
- ] })
1551
- ] })
1552
- ] }),
1553
- showCOA && coa && /* @__PURE__ */ jsx(COAModal, { coa, theme, onClose: () => setShowCOA(false) })
1554
- ] });
1555
- }
1556
- function CannabinoidStat({ label, value, theme, showBorder }) {
1557
- return /* @__PURE__ */ jsxs("div", { style: {
1558
- padding: "1.25rem 0.5rem",
1559
- textAlign: "center",
1560
- borderRight: showBorder ? `1px solid ${theme.fg}0F` : void 0
1561
- }, children: [
1562
- /* @__PURE__ */ jsxs("div", { style: {
1563
- fontFamily: theme.fontDisplay,
1564
- fontSize: "clamp(1.5rem, 5vw, 2rem)",
1565
- fontWeight: 300,
1566
- lineHeight: 1,
1567
- color: theme.fg
1568
- }, children: [
1569
- value.toFixed(value >= 1 ? 1 : 2),
1570
- /* @__PURE__ */ jsx("span", { style: { fontSize: "0.6em", marginLeft: 1 }, children: "%" })
1571
- ] }),
1572
- /* @__PURE__ */ jsx("div", { style: {
1573
- fontSize: 11,
1574
- fontWeight: 500,
1575
- textTransform: "uppercase",
1576
- letterSpacing: "0.25em",
1577
- color: theme.accent,
1578
- marginTop: "0.5rem"
1579
- }, children: label })
1580
- ] });
1581
- }
1582
- function DetailRow({ label, value, theme }) {
1583
- return /* @__PURE__ */ jsxs("div", { style: {
1584
- display: "flex",
1585
- justifyContent: "space-between",
1586
- alignItems: "baseline",
1587
- padding: "0.625rem 0"
1588
- }, children: [
1589
- /* @__PURE__ */ jsx("span", { style: {
1590
- fontSize: 12,
1591
- textTransform: "uppercase",
1592
- letterSpacing: "0.15em",
1593
- color: `${theme.fg}66`
1594
- }, children: label }),
1595
- /* @__PURE__ */ jsx("span", { style: {
1596
- fontSize: 14,
1597
- fontWeight: 300,
1598
- color: `${theme.fg}CC`
1599
- }, children: value })
1600
- ] });
1601
- }
1602
- function FooterLabel({ text }) {
1603
- return /* @__PURE__ */ jsx("span", { style: {
1604
- fontSize: 10,
1605
- textTransform: "uppercase",
1606
- letterSpacing: "0.15em",
1607
- color: "rgba(255,255,255,0.25)"
1608
- }, children: text });
1609
- }
1610
- function COAModal({ coa, theme, onClose }) {
1611
- return /* @__PURE__ */ jsxs("div", { style: { position: "fixed", inset: 0, zIndex: 9999, background: "rgba(0,0,0,0.95)", display: "flex", flexDirection: "column" }, children: [
1612
- /* @__PURE__ */ jsxs("div", { style: {
1613
- display: "flex",
1614
- justifyContent: "space-between",
1615
- alignItems: "center",
1616
- padding: "0.75rem 1rem",
1617
- borderBottom: `1px solid ${theme.fg}10`
1618
- }, children: [
1619
- /* @__PURE__ */ jsx("span", { style: {
1620
- color: "#fff",
1621
- fontFamily: theme.fontDisplay,
1622
- fontWeight: 500,
1623
- fontSize: "0.85rem",
1624
- letterSpacing: "-0.01em"
1625
- }, children: coa.document_name || "Lab Results" }),
1626
- /* @__PURE__ */ jsx(
1627
- "button",
1628
- {
1629
- onClick: onClose,
1630
- style: {
1631
- background: `${theme.fg}10`,
1632
- border: "none",
1633
- color: "#fff",
1634
- fontSize: "0.85rem",
1635
- cursor: "pointer",
1636
- padding: "0.375rem 0.75rem",
1637
- letterSpacing: "0.05em"
1638
- },
1639
- children: "Close"
1640
- }
1641
- )
1642
- ] }),
1643
- /* @__PURE__ */ jsx("iframe", { src: coa.url, style: { flex: 1, border: "none", background: "#fff" }, title: "Lab Results" })
1644
- ] });
1668
+ fontBody: t?.fontBody || "system-ui, -apple-system, sans-serif"
1669
+ };
1670
+ }
1671
+ function buildDefaultSections(data) {
1672
+ const { product, coa, qr_code: qr } = data;
1673
+ const cf = product?.custom_fields;
1674
+ const sections = [];
1675
+ let order = 0;
1676
+ const productName = qr.landing_page.title || product?.name || qr.name;
1677
+ const productImage = qr.landing_page.image_url || product?.featured_image || null;
1678
+ const description = qr.landing_page.description || product?.description || "";
1679
+ const ctaUrl = qr.landing_page.cta_url || qr.destination_url;
1680
+ const categoryName = product?.category_name ?? null;
1681
+ const strainType = toStr(cf?.strain_type);
1682
+ if (productImage) {
1683
+ sections.push({
1684
+ id: "auto-hero",
1685
+ type: "hero",
1686
+ order: order++,
1687
+ content: {
1688
+ title: productName,
1689
+ subtitle: [categoryName, strainType].filter(Boolean).join(" \xB7 "),
1690
+ background_image: productImage,
1691
+ cta_text: qr.landing_page.cta_text || null,
1692
+ cta_url: ctaUrl
1693
+ }
1694
+ });
1695
+ } else {
1696
+ sections.push({
1697
+ id: "auto-header",
1698
+ type: "text",
1699
+ order: order++,
1700
+ content: {
1701
+ heading: productName,
1702
+ body: [categoryName, strainType].filter(Boolean).join(" \xB7 ") || void 0
1703
+ },
1704
+ config: { align: "center" }
1705
+ });
1706
+ }
1707
+ const thca = toNum(cf?.thca_percentage);
1708
+ const thc = toNum(cf?.d9_percentage);
1709
+ const cbd = toNum(cf?.cbd_total);
1710
+ const stats = [];
1711
+ if (thca != null) stats.push({ label: "THCa", value: `${thca.toFixed(thca >= 1 ? 1 : 2)}%` });
1712
+ if (thc != null) stats.push({ label: "\u03949 THC", value: `${thc.toFixed(thc >= 1 ? 1 : 2)}%` });
1713
+ if (cbd != null) stats.push({ label: "CBD", value: `${cbd.toFixed(cbd >= 1 ? 1 : 2)}%` });
1714
+ if (stats.length > 0) {
1715
+ sections.push({
1716
+ id: "auto-stats",
1717
+ type: "stats",
1718
+ order: order++,
1719
+ content: { stats }
1720
+ });
1721
+ }
1722
+ const details = [];
1723
+ const genetics = toStr(cf?.genetics);
1724
+ const terpenes = toStr(cf?.terpenes);
1725
+ const effects = toStr(cf?.effects);
1726
+ const batchNumber = toStr(cf?.batch_number);
1727
+ const dateTested = toStr(cf?.date_tested);
1728
+ if (genetics) details.push({ label: "Genetics", value: genetics });
1729
+ if (terpenes) details.push({ label: "Terpenes", value: terpenes });
1730
+ if (effects) details.push({ label: "Effects", value: effects });
1731
+ if (batchNumber) details.push({ label: "Batch", value: batchNumber });
1732
+ if (dateTested) details.push({ label: "Tested", value: formatDate(dateTested) });
1733
+ if (details.length > 0) {
1734
+ sections.push({
1735
+ id: "auto-details",
1736
+ type: "stats",
1737
+ order: order++,
1738
+ content: { stats: details },
1739
+ config: { layout: "list" }
1740
+ });
1741
+ }
1742
+ if (description) {
1743
+ sections.push({
1744
+ id: "auto-description",
1745
+ type: "text",
1746
+ order: order++,
1747
+ content: { heading: "About", body: description }
1748
+ });
1749
+ }
1750
+ if (coa) {
1751
+ sections.push({
1752
+ id: "auto-coa",
1753
+ type: "coa_viewer",
1754
+ order: order++,
1755
+ content: { button_text: "View Lab Results" }
1756
+ });
1757
+ }
1758
+ if (ctaUrl) {
1759
+ sections.push({
1760
+ id: "auto-cta",
1761
+ type: "cta",
1762
+ order: order++,
1763
+ content: {
1764
+ buttons: [{ text: qr.landing_page.cta_text || "Shop Online", url: ctaUrl, style: "primary" }]
1765
+ }
1766
+ });
1767
+ }
1768
+ return sections;
1645
1769
  }
1646
1770
  function toNum(v) {
1647
1771
  if (v === "" || v == null) return null;
@@ -1652,13 +1776,6 @@ function toStr(v) {
1652
1776
  if (v == null || v === "") return null;
1653
1777
  return String(v);
1654
1778
  }
1655
- function strainBadgeColor(strain) {
1656
- const s = strain.toLowerCase();
1657
- if (s === "sativa") return "#22c55e";
1658
- if (s === "indica") return "#8b5cf6";
1659
- if (s === "hybrid") return "#f59e0b";
1660
- return "#6b7280";
1661
- }
1662
1779
  function formatDate(dateStr) {
1663
1780
  try {
1664
1781
  const d = /* @__PURE__ */ new Date(dateStr + "T00:00:00");
@@ -1762,365 +1879,34 @@ function PageLayout({
1762
1879
  renderSection
1763
1880
  }) {
1764
1881
  const { landing_page: lp, store } = data;
1765
- const [showCOA, setShowCOA] = useState(false);
1766
1882
  const theme = {
1767
1883
  bg: lp.background_color || store?.theme?.background || "#050505",
1768
1884
  fg: lp.text_color || store?.theme?.foreground || "#fafafa",
1769
1885
  accent: lp.accent_color || store?.theme?.accent || "#E8E2D9",
1770
1886
  surface: store?.theme?.surface || "#111",
1771
- muted: store?.theme?.muted || "#888"
1887
+ muted: store?.theme?.muted || "#888",
1888
+ fontDisplay: store?.theme?.fontDisplay || void 0,
1889
+ fontBody: store?.theme?.fontBody || void 0
1772
1890
  };
1773
- const fontFamily = lp.font_family || "system-ui, -apple-system, sans-serif";
1891
+ const fontFamily = lp.font_family || theme.fontDisplay || "system-ui, -apple-system, sans-serif";
1774
1892
  const logoUrl = store?.logo_url;
1775
1893
  const sorted = [...lp.sections].sort((a, b) => a.order - b.order);
1776
1894
  return /* @__PURE__ */ jsxs("div", { style: { minHeight: "100dvh", background: theme.bg, color: theme.fg, fontFamily }, children: [
1777
1895
  lp.custom_css && /* @__PURE__ */ jsx("style", { children: lp.custom_css }),
1778
- logoUrl && /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ jsx(
1779
- "img",
1780
- {
1781
- src: logoUrl,
1782
- alt: store?.name || "Store",
1783
- style: { height: 40, objectFit: "contain" }
1784
- }
1785
- ) }),
1896
+ 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" } }) }),
1786
1897
  sorted.map((section) => {
1787
- const defaultRenderer = () => /* @__PURE__ */ jsx(
1788
- DefaultSectionRenderer,
1789
- {
1790
- section,
1791
- data,
1792
- theme,
1793
- onShowCOA: () => setShowCOA(true)
1794
- },
1795
- section.id
1796
- );
1898
+ const defaultRenderer = () => /* @__PURE__ */ jsx(SectionRenderer, { section, data, theme }, section.id);
1797
1899
  if (renderSection) {
1798
1900
  return /* @__PURE__ */ jsx("div", { children: renderSection(section, defaultRenderer) }, section.id);
1799
1901
  }
1800
- return /* @__PURE__ */ jsx(
1801
- DefaultSectionRenderer,
1802
- {
1803
- section,
1804
- data,
1805
- theme,
1806
- onShowCOA: () => setShowCOA(true)
1807
- },
1808
- section.id
1809
- );
1902
+ return /* @__PURE__ */ jsx(SectionRenderer, { section, data, theme }, section.id);
1810
1903
  }),
1811
1904
  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: [
1812
1905
  "Powered by ",
1813
1906
  store.name
1814
- ] }) }),
1815
- showCOA && data.coa && /* @__PURE__ */ jsxs(
1816
- "div",
1817
- {
1818
- style: {
1819
- position: "fixed",
1820
- inset: 0,
1821
- zIndex: 9999,
1822
- background: "rgba(0,0,0,0.9)",
1823
- display: "flex",
1824
- flexDirection: "column"
1825
- },
1826
- children: [
1827
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "1rem" }, children: [
1828
- /* @__PURE__ */ jsx("span", { style: { color: "#fff", fontWeight: 600 }, children: data.coa.document_name || "Lab Results" }),
1829
- /* @__PURE__ */ jsx(
1830
- "button",
1831
- {
1832
- onClick: () => setShowCOA(false),
1833
- style: { background: "none", border: "none", color: "#fff", fontSize: "1.5rem", cursor: "pointer", padding: "0.5rem" },
1834
- children: "\u2715"
1835
- }
1836
- )
1837
- ] }),
1838
- /* @__PURE__ */ jsx(
1839
- "iframe",
1840
- {
1841
- src: data.coa.url,
1842
- style: { flex: 1, border: "none", background: "#fff" },
1843
- title: "Lab Results"
1844
- }
1845
- )
1846
- ]
1847
- }
1848
- )
1849
- ] });
1850
- }
1851
- function DefaultSectionRenderer({
1852
- section,
1853
- data,
1854
- theme,
1855
- onShowCOA
1856
- }) {
1857
- switch (section.type) {
1858
- case "hero":
1859
- return /* @__PURE__ */ jsx(HeroSection, { section, theme });
1860
- case "text":
1861
- return /* @__PURE__ */ jsx(TextSection, { section, theme });
1862
- case "image":
1863
- return /* @__PURE__ */ jsx(ImageSection, { section, theme });
1864
- case "video":
1865
- return /* @__PURE__ */ jsx(VideoSection, { section, theme });
1866
- case "gallery":
1867
- return /* @__PURE__ */ jsx(GallerySection, { section, theme });
1868
- case "cta":
1869
- return /* @__PURE__ */ jsx(CTASection, { section, theme });
1870
- case "stats":
1871
- return /* @__PURE__ */ jsx(StatsSection, { section, theme });
1872
- case "product_card":
1873
- return /* @__PURE__ */ jsx(ProductCardSection, { section, data, theme });
1874
- case "coa_viewer":
1875
- return /* @__PURE__ */ jsx(COAViewerSection, { section, data, theme, onShowCOA });
1876
- case "social_links":
1877
- return /* @__PURE__ */ jsx(SocialLinksSection, { section, theme });
1878
- case "divider":
1879
- return /* @__PURE__ */ jsx(DividerSection, { theme });
1880
- default:
1881
- return null;
1882
- }
1883
- }
1884
- function HeroSection({ section, theme }) {
1885
- const { title, subtitle, background_image, cta_text, cta_url } = section.content;
1886
- return /* @__PURE__ */ jsxs(
1887
- "div",
1888
- {
1889
- style: {
1890
- position: "relative",
1891
- minHeight: "60vh",
1892
- display: "flex",
1893
- flexDirection: "column",
1894
- justifyContent: "center",
1895
- alignItems: "center",
1896
- textAlign: "center",
1897
- padding: "3rem 1.5rem",
1898
- backgroundImage: background_image ? `url(${background_image})` : void 0,
1899
- backgroundSize: "cover",
1900
- backgroundPosition: "center"
1901
- },
1902
- children: [
1903
- background_image && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" } }),
1904
- /* @__PURE__ */ jsxs("div", { style: { position: "relative", zIndex: 1, maxWidth: 640 }, children: [
1905
- title && /* @__PURE__ */ jsx("h1", { style: { fontSize: "2.5rem", fontWeight: 700, margin: "0 0 1rem", lineHeight: 1.15, color: theme.fg }, children: title }),
1906
- subtitle && /* @__PURE__ */ jsx("p", { style: { fontSize: "1.125rem", color: theme.muted, margin: "0 0 2rem", lineHeight: 1.6 }, children: subtitle }),
1907
- cta_text && cta_url && /* @__PURE__ */ jsx(
1908
- "a",
1909
- {
1910
- href: cta_url,
1911
- style: {
1912
- display: "inline-block",
1913
- padding: "0.875rem 2rem",
1914
- background: theme.accent,
1915
- color: theme.bg,
1916
- textDecoration: "none",
1917
- fontSize: "0.95rem",
1918
- fontWeight: 600,
1919
- borderRadius: 0
1920
- },
1921
- children: cta_text
1922
- }
1923
- )
1924
- ] })
1925
- ]
1926
- }
1927
- );
1928
- }
1929
- function TextSection({ section, theme }) {
1930
- const { heading, body } = section.content;
1931
- const align = section.config?.align || "left";
1932
- return /* @__PURE__ */ jsxs("div", { style: { padding: "2rem 1.5rem", maxWidth: 640, margin: "0 auto", textAlign: align }, children: [
1933
- heading && /* @__PURE__ */ jsx("h2", { style: { fontSize: "1.5rem", fontWeight: 600, margin: "0 0 1rem", color: theme.fg }, children: heading }),
1934
- body && /* @__PURE__ */ jsx("div", { style: { color: theme.muted, lineHeight: 1.7, fontSize: "0.95rem", whiteSpace: "pre-wrap" }, children: body })
1935
- ] });
1936
- }
1937
- function ImageSection({ section, theme }) {
1938
- const { url, alt, caption } = section.content;
1939
- const contained = section.config?.contained !== false;
1940
- if (!url) return null;
1941
- return /* @__PURE__ */ jsxs("div", { style: { padding: contained ? "1.5rem" : 0, maxWidth: contained ? 640 : void 0, margin: contained ? "0 auto" : void 0 }, children: [
1942
- /* @__PURE__ */ jsx(
1943
- "img",
1944
- {
1945
- src: url,
1946
- alt: alt || "",
1947
- style: { width: "100%", display: "block", objectFit: "cover" }
1948
- }
1949
- ),
1950
- caption && /* @__PURE__ */ jsx("p", { style: { fontSize: "0.8rem", color: theme.muted, textAlign: "center", marginTop: "0.75rem" }, children: caption })
1907
+ ] }) })
1951
1908
  ] });
1952
1909
  }
1953
- function VideoSection({ section, theme }) {
1954
- const { url, poster } = section.content;
1955
- if (!url) return null;
1956
- const isEmbed = url.includes("youtube") || url.includes("youtu.be") || url.includes("vimeo");
1957
- 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(
1958
- "iframe",
1959
- {
1960
- src: toEmbedUrl(url),
1961
- style: { position: "absolute", top: 0, left: 0, width: "100%", height: "100%", border: "none" },
1962
- allow: "autoplay; fullscreen",
1963
- title: "Video"
1964
- }
1965
- ) }) : /* @__PURE__ */ jsx(
1966
- "video",
1967
- {
1968
- src: url,
1969
- poster,
1970
- controls: true,
1971
- style: { width: "100%", display: "block", background: theme.surface }
1972
- }
1973
- ) });
1974
- }
1975
- function GallerySection({ section, theme }) {
1976
- const { images } = section.content;
1977
- const columns = section.config?.columns || 3;
1978
- if (!images || images.length === 0) return null;
1979
- 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(
1980
- "img",
1981
- {
1982
- src: img.url,
1983
- alt: img.alt || "",
1984
- style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
1985
- }
1986
- ) }, i)) }) });
1987
- }
1988
- function CTASection({ section, theme }) {
1989
- const { buttons } = section.content;
1990
- if (!buttons || buttons.length === 0) return null;
1991
- return /* @__PURE__ */ jsx("div", { style: { padding: "2rem 1.5rem", maxWidth: 480, margin: "0 auto", display: "flex", flexDirection: "column", gap: "0.75rem" }, children: buttons.map((btn, i) => {
1992
- const isPrimary = btn.style !== "outline";
1993
- return /* @__PURE__ */ jsx(
1994
- "a",
1995
- {
1996
- href: btn.url,
1997
- style: {
1998
- display: "block",
1999
- width: "100%",
2000
- padding: "0.875rem",
2001
- background: isPrimary ? theme.accent : "transparent",
2002
- color: isPrimary ? theme.bg : theme.fg,
2003
- border: isPrimary ? "none" : `1px solid ${theme.muted}`,
2004
- fontSize: "0.95rem",
2005
- fontWeight: 600,
2006
- textAlign: "center",
2007
- textDecoration: "none",
2008
- boxSizing: "border-box",
2009
- borderRadius: 0
2010
- },
2011
- children: btn.text
2012
- },
2013
- i
2014
- );
2015
- }) });
2016
- }
2017
- function StatsSection({ section, theme }) {
2018
- const { stats } = section.content;
2019
- if (!stats || stats.length === 0) return null;
2020
- const columns = Math.min(stats.length, 4);
2021
- return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 640, margin: "0 auto" }, children: /* @__PURE__ */ jsx("div", { style: { display: "grid", gridTemplateColumns: `repeat(${columns}, 1fr)`, gap: "0.75rem" }, children: stats.map((stat, i) => /* @__PURE__ */ jsxs("div", { style: { background: theme.surface, padding: "1rem", textAlign: "center" }, children: [
2022
- /* @__PURE__ */ jsx("div", { style: { fontSize: "1.25rem", fontWeight: 700, color: theme.fg }, children: stat.value }),
2023
- /* @__PURE__ */ jsx("div", { style: { fontSize: "0.7rem", color: theme.accent, textTransform: "uppercase", letterSpacing: "0.05em", marginTop: "0.25rem" }, children: stat.label })
2024
- ] }, i)) }) });
2025
- }
2026
- function ProductCardSection({ section, data, theme }) {
2027
- const product = data.product;
2028
- const c = section.content;
2029
- const name = c.name || product?.name || "";
2030
- const description = c.description || product?.description || "";
2031
- const price = c.price || null;
2032
- const imageUrl = c.image_url || product?.featured_image || null;
2033
- const url = c.url || null;
2034
- return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 480, margin: "0 auto" }, children: /* @__PURE__ */ jsxs("div", { style: { background: theme.surface, overflow: "hidden" }, children: [
2035
- 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" } }) }),
2036
- /* @__PURE__ */ jsxs("div", { style: { padding: "1.25rem" }, children: [
2037
- /* @__PURE__ */ jsx("h3", { style: { fontSize: "1.25rem", fontWeight: 600, margin: "0 0 0.5rem", color: theme.fg }, children: name }),
2038
- description && /* @__PURE__ */ jsx("p", { style: { fontSize: "0.9rem", color: theme.muted, margin: "0 0 1rem", lineHeight: 1.5 }, children: description }),
2039
- price && /* @__PURE__ */ jsx("p", { style: { fontSize: "1.125rem", fontWeight: 700, color: theme.accent, margin: "0 0 1rem" }, children: price }),
2040
- url && /* @__PURE__ */ jsx(
2041
- "a",
2042
- {
2043
- href: url,
2044
- style: {
2045
- display: "block",
2046
- width: "100%",
2047
- padding: "0.75rem",
2048
- background: theme.accent,
2049
- color: theme.bg,
2050
- textAlign: "center",
2051
- textDecoration: "none",
2052
- fontSize: "0.9rem",
2053
- fontWeight: 600,
2054
- boxSizing: "border-box",
2055
- borderRadius: 0
2056
- },
2057
- children: "View Product"
2058
- }
2059
- )
2060
- ] })
2061
- ] }) });
2062
- }
2063
- function COAViewerSection({
2064
- section,
2065
- data,
2066
- theme,
2067
- onShowCOA
2068
- }) {
2069
- const coa = data.coa;
2070
- const c = section.content;
2071
- if (!coa) return null;
2072
- return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", maxWidth: 480, margin: "0 auto" }, children: /* @__PURE__ */ jsx(
2073
- "button",
2074
- {
2075
- onClick: onShowCOA,
2076
- style: {
2077
- width: "100%",
2078
- padding: "0.875rem",
2079
- background: theme.accent,
2080
- color: theme.bg,
2081
- border: "none",
2082
- fontSize: "0.95rem",
2083
- fontWeight: 600,
2084
- cursor: "pointer",
2085
- borderRadius: 0
2086
- },
2087
- children: c.button_text || "View Lab Results"
2088
- }
2089
- ) });
2090
- }
2091
- function SocialLinksSection({ section, theme }) {
2092
- const { links } = section.content;
2093
- if (!links || links.length === 0) return null;
2094
- return /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", display: "flex", justifyContent: "center", gap: "1.5rem", flexWrap: "wrap" }, children: links.map((link, i) => /* @__PURE__ */ jsx(
2095
- "a",
2096
- {
2097
- href: link.url,
2098
- target: "_blank",
2099
- rel: "noopener noreferrer",
2100
- style: {
2101
- color: theme.muted,
2102
- textDecoration: "none",
2103
- fontSize: "0.85rem",
2104
- fontWeight: 500,
2105
- textTransform: "capitalize",
2106
- letterSpacing: "0.03em",
2107
- transition: "color 0.15s"
2108
- },
2109
- children: link.platform
2110
- },
2111
- i
2112
- )) });
2113
- }
2114
- function DividerSection({ theme }) {
2115
- 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.surface}`, margin: 0 } }) });
2116
- }
2117
- function toEmbedUrl(url) {
2118
- const ytMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)/);
2119
- if (ytMatch) return `https://www.youtube.com/embed/${ytMatch[1]}`;
2120
- const vimeoMatch = url.match(/vimeo\.com\/(\d+)/);
2121
- if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}`;
2122
- return url;
2123
- }
2124
1910
  var containerStyle2 = {
2125
1911
  minHeight: "100dvh",
2126
1912
  display: "flex",
@@ -2157,6 +1943,6 @@ function DefaultError2({ message }) {
2157
1943
  ] }) });
2158
1944
  }
2159
1945
 
2160
- export { AnalyticsTracker, AuthInitializer, CartInitializer, LandingPage, PixelInitializer, QRLandingPage, WhaleContext, WhaleProvider, useAnalytics, useAuth, useCart, useCartItemCount, useCartTotal, useCategories, useCheckout, useCoupons, useCustomerAnalytics, useCustomerOrders, useLocations, useLoyalty, useProduct, useProducts, useRecommendations, useReviews, useSearch, useShipping, useWhaleClient, useWishlist };
1946
+ export { AnalyticsTracker, AuthInitializer, CartInitializer, LandingPage, PixelInitializer, QRLandingPage, SectionRenderer, WhaleContext, WhaleProvider, useAnalytics, useAuth, useCart, useCartItemCount, useCartTotal, useCategories, useCheckout, useCoupons, useCustomerAnalytics, useCustomerOrders, useLocations, useLoyalty, useProduct, useProducts, useRecommendations, useReviews, useSearch, useShipping, useWhaleClient, useWishlist };
2161
1947
  //# sourceMappingURL=index.js.map
2162
1948
  //# sourceMappingURL=index.js.map