@neowhale/storefront 0.2.8 → 0.2.10

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.
@@ -1262,25 +1262,34 @@ function QRLandingPage({
1262
1262
  if (state === "error") return /* @__PURE__ */ jsx(DefaultError, { message: errorMsg });
1263
1263
  if (!data) return null;
1264
1264
  if (renderPage) return /* @__PURE__ */ jsx(Fragment, { children: renderPage(data) });
1265
- return /* @__PURE__ */ jsx(DefaultLayout, { data, renderProduct, renderCOA });
1265
+ return /* @__PURE__ */ jsx(ProductLanding, { data, renderProduct, renderCOA });
1266
1266
  }
1267
- function DefaultLayout({
1267
+ function extractTheme(data) {
1268
+ const lp = data.qr_code.landing_page;
1269
+ const t = data.store?.theme;
1270
+ 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",
1279
+ 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({
1268
1285
  data,
1269
1286
  renderProduct,
1270
1287
  renderCOA
1271
1288
  }) {
1272
1289
  const { qr_code: qr, store, product, coa } = data;
1273
1290
  const lp = qr.landing_page;
1291
+ const theme = extractTheme(data);
1274
1292
  const [showCOA, setShowCOA] = useState(false);
1275
- const bg = lp.background_color || store?.theme?.background || "#050505";
1276
- const fg = lp.text_color || store?.theme?.foreground || "#fafafa";
1277
- const accent = store?.theme?.accent || qr.brand_color || "#E8E2D9";
1278
- const surface = store?.theme?.surface || "#0C0C0C";
1279
- const surfaceLight = store?.theme?.surfaceLight || "#141414";
1280
- const muted = store?.theme?.muted || "#888";
1281
- const border = store?.theme?.border || "#1C1C1C";
1282
- const fontDisplay = store?.theme?.fontDisplay || "system-ui, -apple-system, sans-serif";
1283
- const fontBody = store?.theme?.fontBody || "system-ui, -apple-system, sans-serif";
1284
1293
  const logoUrl = qr.logo_url || store?.logo_url;
1285
1294
  const productImage = lp.image_url || product?.featured_image || null;
1286
1295
  const productName = lp.title || product?.name || qr.name;
@@ -1288,310 +1297,390 @@ function DefaultLayout({
1288
1297
  const ctaUrl = lp.cta_url || qr.destination_url;
1289
1298
  const categoryName = product?.category_name ?? null;
1290
1299
  const cf = product?.custom_fields;
1291
- const thca = cf?.thca_percentage ?? null;
1292
- const thc = cf?.d9_percentage ?? null;
1293
- const cbd = cf?.cbd_total ?? null;
1294
- const strainType = cf?.strain_type ?? null;
1295
- const batchNumber = cf?.batch_number ?? null;
1296
- const dateTested = cf?.date_tested ?? null;
1297
- const terpenes = cf?.terpenes ?? null;
1298
- const effects = cf?.effects ?? null;
1299
- const genetics = cf?.genetics ?? null;
1300
- const tagline = cf?.tagline ?? null;
1301
- const pricingData = product?.pricing_data;
1302
- const tiers = pricingData?.tiers?.sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0)) ?? [];
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 rawPricing = product?.pricing_data;
1311
+ const tiers = (Array.isArray(rawPricing) ? rawPricing : rawPricing?.tiers ?? []).sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0));
1303
1312
  const lowestPrice = tiers.length > 0 ? Math.min(...tiers.map((t) => t.default_price)) : null;
1304
1313
  const handleCOAClick = useCallback(() => {
1305
1314
  if (coa) setShowCOA(true);
1306
1315
  }, [coa]);
1307
- return /* @__PURE__ */ jsxs("div", { style: { minHeight: "100dvh", background: bg, color: fg, fontFamily: fontBody }, children: [
1308
- /* @__PURE__ */ jsxs("header", { style: {
1309
- padding: "1rem 1.5rem",
1316
+ const labelStyle = {
1317
+ fontSize: 11,
1318
+ fontWeight: 500,
1319
+ textTransform: "uppercase",
1320
+ letterSpacing: "0.25em",
1321
+ color: `${theme.fg}40`
1322
+ // 40 = 25% opacity
1323
+ };
1324
+ const dividerStyle = {
1325
+ border: "none",
1326
+ borderTop: `1px solid ${theme.fg}0A`,
1327
+ // 0A = 4% opacity
1328
+ margin: 0
1329
+ };
1330
+ return /* @__PURE__ */ jsxs("div", { style: {
1331
+ minHeight: "100dvh",
1332
+ background: theme.bg,
1333
+ color: theme.fg,
1334
+ fontFamily: theme.fontBody
1335
+ }, children: [
1336
+ /* @__PURE__ */ jsxs("nav", { style: {
1337
+ padding: "1.25rem 1.5rem",
1310
1338
  display: "flex",
1311
1339
  alignItems: "center",
1312
1340
  justifyContent: "space-between",
1313
- borderBottom: `1px solid ${border}`
1341
+ maxWidth: 1280,
1342
+ margin: "0 auto"
1314
1343
  }, children: [
1315
- logoUrl ? /* @__PURE__ */ jsx("img", { src: logoUrl, alt: store?.name || "Store", style: { height: 32, objectFit: "contain" } }) : /* @__PURE__ */ jsx("span", { style: { fontFamily: fontDisplay, fontWeight: 600, fontSize: "1.1rem" }, children: store?.name || "" }),
1316
- store?.name && logoUrl && /* @__PURE__ */ jsx("span", { style: { fontSize: "0.75rem", color: muted, fontWeight: 500 }, children: store.name })
1344
+ 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", {}),
1345
+ /* @__PURE__ */ jsx(
1346
+ "a",
1347
+ {
1348
+ href: ctaUrl,
1349
+ style: {
1350
+ fontSize: 11,
1351
+ fontWeight: 500,
1352
+ textTransform: "uppercase",
1353
+ letterSpacing: "0.15em",
1354
+ color: theme.fg,
1355
+ textDecoration: "none",
1356
+ padding: "0.5rem 1.25rem",
1357
+ border: `1px solid ${theme.fg}10`,
1358
+ transition: "border-color 0.3s"
1359
+ },
1360
+ children: "Shop"
1361
+ }
1362
+ )
1317
1363
  ] }),
1318
- /* @__PURE__ */ jsxs("div", { style: {
1319
- display: "flex",
1320
- flexDirection: "column",
1321
- maxWidth: 640,
1322
- margin: "0 auto",
1323
- padding: "0 1.5rem"
1324
- }, children: [
1364
+ /* @__PURE__ */ jsxs("main", { style: { maxWidth: 560, margin: "0 auto", padding: "0 1.5rem 3rem" }, children: [
1325
1365
  productImage && /* @__PURE__ */ jsx("div", { style: {
1326
- marginTop: "1.5rem",
1327
- borderRadius: 8,
1366
+ width: "100%",
1367
+ aspectRatio: "1",
1328
1368
  overflow: "hidden",
1329
- background: surface,
1330
- aspectRatio: "4/3",
1331
- maxHeight: 320,
1369
+ background: theme.surfaceLight,
1332
1370
  display: "flex",
1333
1371
  alignItems: "center",
1334
- justifyContent: "center"
1372
+ justifyContent: "center",
1373
+ padding: "2rem"
1335
1374
  }, children: /* @__PURE__ */ jsx(
1336
1375
  "img",
1337
1376
  {
1338
1377
  src: productImage,
1339
1378
  alt: productName,
1340
- style: { width: "100%", height: "100%", objectFit: "cover" }
1379
+ style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" }
1341
1380
  }
1342
1381
  ) }),
1343
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.5rem", marginTop: "1.25rem", flexWrap: "wrap", alignItems: "center" }, children: [
1344
- categoryName && /* @__PURE__ */ jsx(Badge, { text: categoryName, bg: surfaceLight, color: accent }),
1345
- strainType && /* @__PURE__ */ jsx(Badge, { text: strainType, bg: strainBadgeColor(strainType), color: "#fff", bold: true }),
1346
- coa && /* @__PURE__ */ jsx(Badge, { text: "Lab Tested", bg: "rgba(34,197,94,0.15)", color: "#22c55e" })
1347
- ] }),
1348
- /* @__PURE__ */ jsxs("div", { style: { marginTop: "0.75rem" }, children: [
1349
- /* @__PURE__ */ jsx("h1", { style: {
1350
- fontFamily: fontDisplay,
1351
- fontSize: "1.75rem",
1352
- fontWeight: 600,
1353
- margin: 0,
1354
- lineHeight: 1.2,
1355
- letterSpacing: "-0.02em"
1356
- }, children: productName }),
1357
- lowestPrice != null && /* @__PURE__ */ jsxs("p", { style: {
1358
- fontSize: "1.25rem",
1382
+ /* @__PURE__ */ jsxs("div", { style: { marginTop: "1.5rem", display: "flex", gap: "0.75rem", alignItems: "center" }, children: [
1383
+ categoryName && /* @__PURE__ */ jsx("span", { style: {
1384
+ fontSize: 11,
1359
1385
  fontWeight: 500,
1360
- color: accent,
1361
- margin: "0.375rem 0 0"
1362
- }, children: [
1363
- "From $",
1364
- lowestPrice.toFixed(2)
1365
- ] }),
1366
- tagline && /* @__PURE__ */ jsx("p", { style: { color: muted, fontSize: "0.9rem", margin: "0.5rem 0 0", fontStyle: "italic" }, children: tagline })
1367
- ] }),
1368
- description && /* @__PURE__ */ jsx("p", { style: { color: muted, lineHeight: 1.6, margin: "1rem 0 0", fontSize: "0.9rem" }, children: description }),
1369
- renderProduct ? renderProduct(data) : null,
1370
- (thca != null || thc != null || cbd != null) && /* @__PURE__ */ jsxs("div", { style: {
1371
- marginTop: "1.25rem",
1372
- background: surface,
1373
- border: `1px solid ${border}`,
1374
- borderRadius: 8,
1375
- padding: "1rem"
1376
- }, children: [
1377
- /* @__PURE__ */ jsx("div", { style: {
1378
- fontSize: "0.65rem",
1386
+ color: theme.accent,
1387
+ textTransform: "uppercase",
1388
+ letterSpacing: "0.15em"
1389
+ }, children: categoryName }),
1390
+ strainType && /* @__PURE__ */ jsx("span", { style: {
1391
+ fontSize: 10,
1379
1392
  fontWeight: 600,
1380
1393
  textTransform: "uppercase",
1381
1394
  letterSpacing: "0.08em",
1382
- color: muted,
1383
- marginBottom: "0.75rem"
1384
- }, children: "Potency" }),
1385
- /* @__PURE__ */ jsxs("div", { style: { display: "grid", gridTemplateColumns: `repeat(${[thca, thc, cbd].filter((v) => v != null).length}, 1fr)`, gap: "0.5rem" }, children: [
1386
- thca != null && /* @__PURE__ */ jsx(PotencyStat, { label: "THCa", value: thca, accent, fg }),
1387
- thc != null && /* @__PURE__ */ jsx(PotencyStat, { label: "\u03949 THC", value: thc, accent, fg }),
1388
- cbd != null && /* @__PURE__ */ jsx(PotencyStat, { label: "CBD", value: cbd, accent, fg })
1389
- ] })
1395
+ padding: "0.2rem 0.5rem",
1396
+ background: strainBadgeColor(strainType),
1397
+ color: "#fff"
1398
+ }, children: strainType })
1390
1399
  ] }),
1391
- (batchNumber || dateTested || genetics || terpenes || effects) && /* @__PURE__ */ jsxs("div", { style: {
1392
- marginTop: "0.75rem",
1393
- background: surface,
1394
- border: `1px solid ${border}`,
1395
- borderRadius: 8,
1396
- padding: "1rem"
1400
+ /* @__PURE__ */ jsx("h1", { style: {
1401
+ fontFamily: theme.fontDisplay,
1402
+ fontSize: "clamp(2rem, 8vw, 3rem)",
1403
+ fontWeight: 300,
1404
+ margin: "0.5rem 0 0",
1405
+ lineHeight: 1.1,
1406
+ letterSpacing: "-0.02em"
1407
+ }, children: productName }),
1408
+ tagline && /* @__PURE__ */ jsx("p", { style: {
1409
+ fontFamily: theme.fontDisplay,
1410
+ fontSize: "1.1rem",
1411
+ fontWeight: 300,
1412
+ color: `${theme.fg}80`,
1413
+ margin: "0.5rem 0 0"
1414
+ }, children: tagline }),
1415
+ lowestPrice != null && /* @__PURE__ */ jsxs("p", { style: {
1416
+ fontFamily: theme.fontDisplay,
1417
+ fontSize: "1.5rem",
1418
+ fontWeight: 300,
1419
+ margin: "0.75rem 0 0"
1397
1420
  }, children: [
1398
- /* @__PURE__ */ jsx("div", { style: {
1399
- fontSize: "0.65rem",
1400
- fontWeight: 600,
1401
- textTransform: "uppercase",
1402
- letterSpacing: "0.08em",
1403
- color: muted,
1404
- marginBottom: "0.75rem"
1405
- }, children: "Details" }),
1406
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
1407
- genetics && /* @__PURE__ */ jsx(DetailRow, { label: "Genetics", value: genetics, fg, muted }),
1408
- terpenes && /* @__PURE__ */ jsx(DetailRow, { label: "Terpenes", value: terpenes, fg, muted }),
1409
- effects && /* @__PURE__ */ jsx(DetailRow, { label: "Effects", value: effects, fg, muted }),
1410
- batchNumber && /* @__PURE__ */ jsx(DetailRow, { label: "Batch", value: batchNumber, fg, muted }),
1411
- dateTested && /* @__PURE__ */ jsx(DetailRow, { label: "Tested", value: formatDate(dateTested), fg, muted })
1412
- ] })
1421
+ "$",
1422
+ lowestPrice.toFixed(2),
1423
+ tiers.length > 1 && /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8rem", color: `${theme.fg}50`, marginLeft: "0.5rem", fontWeight: 400 }, children: "and up" })
1413
1424
  ] }),
1414
- tiers.length > 1 && /* @__PURE__ */ jsxs("div", { style: {
1415
- marginTop: "0.75rem",
1416
- background: surface,
1417
- border: `1px solid ${border}`,
1418
- borderRadius: 8,
1419
- padding: "1rem"
1425
+ renderProduct ? renderProduct(data) : null,
1426
+ (thca != null || thc != null || cbd != null) && /* @__PURE__ */ jsxs("div", { style: {
1427
+ marginTop: "1.75rem",
1428
+ border: `1px solid ${theme.fg}0F`,
1429
+ display: "grid",
1430
+ gridTemplateColumns: `repeat(${[thca, thc, cbd].filter((v) => v != null).length}, 1fr)`
1420
1431
  }, children: [
1421
- /* @__PURE__ */ jsx("div", { style: {
1422
- fontSize: "0.65rem",
1423
- fontWeight: 600,
1424
- textTransform: "uppercase",
1425
- letterSpacing: "0.08em",
1426
- color: muted,
1427
- marginBottom: "0.75rem"
1428
- }, children: "Pricing" }),
1429
- /* @__PURE__ */ jsx("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(100px, 1fr))", gap: "0.5rem" }, children: tiers.map((tier) => /* @__PURE__ */ jsxs("div", { style: {
1430
- background: surfaceLight,
1431
- borderRadius: 6,
1432
- padding: "0.625rem",
1432
+ thca != null && /* @__PURE__ */ jsx(CannabinoidStat, { label: "THCa", value: thca, theme, showBorder: thc != null || cbd != null }),
1433
+ thc != null && /* @__PURE__ */ jsx(CannabinoidStat, { label: "\u03949 THC", value: thc, theme, showBorder: cbd != null }),
1434
+ cbd != null && /* @__PURE__ */ jsx(CannabinoidStat, { label: "CBD", value: cbd, theme, showBorder: false })
1435
+ ] }),
1436
+ tiers.length > 1 && /* @__PURE__ */ jsxs("div", { style: { marginTop: "1.5rem" }, children: [
1437
+ /* @__PURE__ */ jsx("p", { style: { ...labelStyle, marginBottom: "0.75rem" }, children: "Pricing" }),
1438
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 1 }, children: tiers.map((tier) => /* @__PURE__ */ jsxs("div", { style: {
1439
+ flex: "1 1 0",
1440
+ minWidth: 90,
1441
+ padding: "0.75rem 0.5rem",
1442
+ background: theme.surfaceLight,
1433
1443
  textAlign: "center"
1434
1444
  }, children: [
1435
- /* @__PURE__ */ jsxs("div", { style: { fontSize: "1rem", fontWeight: 600, color: fg }, children: [
1445
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: "1rem", fontWeight: 300, fontFamily: theme.fontDisplay }, children: [
1436
1446
  "$",
1437
1447
  tier.default_price.toFixed(2)
1438
1448
  ] }),
1439
- /* @__PURE__ */ jsx("div", { style: { fontSize: "0.7rem", color: muted, marginTop: "0.125rem" }, children: tier.label })
1449
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 10, color: `${theme.fg}60`, letterSpacing: "0.1em", textTransform: "uppercase", marginTop: 2 }, children: tier.label })
1440
1450
  ] }, tier.id)) })
1441
1451
  ] }),
1442
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.625rem", marginTop: "1.5rem" }, children: [
1443
- coa && /* @__PURE__ */ jsx(
1452
+ (genetics || terpenes || effects || batchNumber || dateTested) && /* @__PURE__ */ jsxs("div", { style: { marginTop: "1.75rem" }, children: [
1453
+ /* @__PURE__ */ jsx("p", { style: { ...labelStyle, marginBottom: "0.75rem" }, children: "Details" }),
1454
+ /* @__PURE__ */ jsxs("div", { children: [
1455
+ genetics && /* @__PURE__ */ jsxs(Fragment, { children: [
1456
+ /* @__PURE__ */ jsx(DetailRow, { label: "Genetics", value: genetics, theme }),
1457
+ /* @__PURE__ */ jsx("hr", { style: dividerStyle })
1458
+ ] }),
1459
+ terpenes && /* @__PURE__ */ jsxs(Fragment, { children: [
1460
+ /* @__PURE__ */ jsx(DetailRow, { label: "Terpenes", value: terpenes, theme }),
1461
+ /* @__PURE__ */ jsx("hr", { style: dividerStyle })
1462
+ ] }),
1463
+ effects && /* @__PURE__ */ jsxs(Fragment, { children: [
1464
+ /* @__PURE__ */ jsx(DetailRow, { label: "Effects", value: effects, theme }),
1465
+ /* @__PURE__ */ jsx("hr", { style: dividerStyle })
1466
+ ] }),
1467
+ batchNumber && /* @__PURE__ */ jsxs(Fragment, { children: [
1468
+ /* @__PURE__ */ jsx(DetailRow, { label: "Batch", value: batchNumber, theme }),
1469
+ /* @__PURE__ */ jsx("hr", { style: dividerStyle })
1470
+ ] }),
1471
+ dateTested && /* @__PURE__ */ jsx(DetailRow, { label: "Tested", value: formatDate(dateTested), theme })
1472
+ ] })
1473
+ ] }),
1474
+ description && /* @__PURE__ */ jsxs("div", { style: { marginTop: "1.75rem" }, children: [
1475
+ /* @__PURE__ */ jsx("p", { style: { ...labelStyle, marginBottom: "0.75rem" }, children: "About" }),
1476
+ /* @__PURE__ */ jsx("p", { style: {
1477
+ fontSize: "0.9rem",
1478
+ fontWeight: 300,
1479
+ color: `${theme.fg}99`,
1480
+ lineHeight: 1.7,
1481
+ margin: 0
1482
+ }, children: description })
1483
+ ] }),
1484
+ coa && /* @__PURE__ */ jsxs("div", { style: { marginTop: "1.75rem" }, children: [
1485
+ /* @__PURE__ */ jsx("p", { style: { ...labelStyle, marginBottom: "0.75rem" }, children: "Lab Results" }),
1486
+ batchNumber && /* @__PURE__ */ jsxs("p", { style: { fontSize: 12, color: `${theme.fg}60`, margin: "0 0 0.5rem", letterSpacing: "0.05em" }, children: [
1487
+ "Batch ",
1488
+ batchNumber,
1489
+ dateTested ? ` \xB7 ${formatDate(dateTested)}` : ""
1490
+ ] }),
1491
+ /* @__PURE__ */ jsxs(
1444
1492
  "button",
1445
1493
  {
1446
1494
  onClick: handleCOAClick,
1447
1495
  style: {
1448
1496
  width: "100%",
1449
- padding: "0.875rem",
1450
- background: accent,
1451
- color: bg,
1452
- border: "none",
1453
- fontSize: "0.9rem",
1454
- fontWeight: 600,
1455
- fontFamily: fontDisplay,
1497
+ position: "relative",
1498
+ height: 180,
1499
+ border: `1px solid ${theme.fg}0F`,
1500
+ background: theme.surface,
1456
1501
  cursor: "pointer",
1457
- borderRadius: 6,
1458
- letterSpacing: "-0.01em"
1459
- },
1460
- children: "View Lab Results"
1461
- }
1462
- ),
1463
- renderCOA ? renderCOA(data) : null,
1464
- /* @__PURE__ */ jsx(
1465
- "a",
1466
- {
1467
- href: ctaUrl,
1468
- style: {
1469
- display: "block",
1470
- width: "100%",
1471
- padding: "0.875rem",
1472
- background: "transparent",
1473
- color: fg,
1474
- border: `1px solid ${border}`,
1475
- fontSize: "0.9rem",
1476
- fontWeight: 500,
1477
- fontFamily: fontDisplay,
1478
- textAlign: "center",
1479
- textDecoration: "none",
1480
- boxSizing: "border-box",
1481
- borderRadius: 6
1502
+ overflow: "hidden",
1503
+ padding: 0,
1504
+ display: "block"
1482
1505
  },
1483
- children: "Shop Online"
1506
+ children: [
1507
+ /* @__PURE__ */ jsx(
1508
+ "iframe",
1509
+ {
1510
+ src: coa.url,
1511
+ style: {
1512
+ width: "200%",
1513
+ height: "200%",
1514
+ border: "none",
1515
+ transform: "scale(0.5)",
1516
+ transformOrigin: "top left",
1517
+ pointerEvents: "none"
1518
+ },
1519
+ title: "COA Preview",
1520
+ tabIndex: -1
1521
+ }
1522
+ ),
1523
+ /* @__PURE__ */ jsx("div", { style: {
1524
+ position: "absolute",
1525
+ inset: 0,
1526
+ background: `linear-gradient(to top, ${theme.bg}E6 0%, transparent 60%)`,
1527
+ display: "flex",
1528
+ alignItems: "flex-end",
1529
+ justifyContent: "center",
1530
+ paddingBottom: "1.25rem"
1531
+ }, children: /* @__PURE__ */ jsx("span", { style: {
1532
+ fontSize: 11,
1533
+ fontWeight: 500,
1534
+ textTransform: "uppercase",
1535
+ letterSpacing: "0.2em",
1536
+ color: theme.accent
1537
+ }, children: "View Certificate of Analysis" }) })
1538
+ ]
1484
1539
  }
1485
1540
  )
1486
1541
  ] }),
1542
+ renderCOA ? renderCOA(data) : null,
1543
+ /* @__PURE__ */ jsx(
1544
+ "a",
1545
+ {
1546
+ href: ctaUrl,
1547
+ style: {
1548
+ display: "block",
1549
+ width: "100%",
1550
+ marginTop: "2rem",
1551
+ padding: "1rem",
1552
+ background: theme.fg,
1553
+ color: theme.bg,
1554
+ fontFamily: theme.fontDisplay,
1555
+ fontSize: "0.85rem",
1556
+ fontWeight: 500,
1557
+ textAlign: "center",
1558
+ textDecoration: "none",
1559
+ letterSpacing: "0.08em",
1560
+ textTransform: "uppercase",
1561
+ boxSizing: "border-box"
1562
+ },
1563
+ children: "Shop Online"
1564
+ }
1565
+ ),
1487
1566
  /* @__PURE__ */ jsxs("footer", { style: {
1488
- marginTop: "2rem",
1489
- paddingTop: "1.25rem",
1490
- paddingBottom: "2rem",
1491
- borderTop: `1px solid ${border}`,
1567
+ marginTop: "3rem",
1568
+ paddingTop: "1.5rem",
1569
+ borderTop: `1px solid ${theme.fg}0A`,
1492
1570
  textAlign: "center"
1493
1571
  }, children: [
1494
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "center", gap: "1.5rem", marginBottom: "0.5rem" }, children: [
1495
- coa && /* @__PURE__ */ jsx(FooterBadge, { icon: "\u2713", text: "Lab Verified", muted }),
1496
- /* @__PURE__ */ jsx(FooterBadge, { icon: "\u2726", text: "Authentic Product", muted })
1572
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "center", gap: "2rem", marginBottom: "0.75rem" }, children: [
1573
+ coa && /* @__PURE__ */ jsx(FooterLabel, { text: "Lab Verified" }),
1574
+ /* @__PURE__ */ jsx(FooterLabel, { text: "Authentic Product" })
1497
1575
  ] }),
1498
- store?.name && /* @__PURE__ */ jsxs("p", { style: { fontSize: "0.7rem", color: muted, margin: "0.5rem 0 0" }, children: [
1576
+ store?.name && /* @__PURE__ */ jsxs("p", { style: { fontSize: 11, color: `${theme.fg}40`, margin: "0.5rem 0 0", letterSpacing: "0.1em" }, children: [
1499
1577
  store.name,
1500
1578
  store?.tagline ? ` \u2014 ${store.tagline}` : ""
1501
1579
  ] })
1502
1580
  ] })
1503
1581
  ] }),
1504
- showCOA && coa && /* @__PURE__ */ jsxs(
1505
- "div",
1506
- {
1507
- style: {
1508
- position: "fixed",
1509
- inset: 0,
1510
- zIndex: 9999,
1511
- background: "rgba(0,0,0,0.92)",
1512
- display: "flex",
1513
- flexDirection: "column"
1514
- },
1515
- children: [
1516
- /* @__PURE__ */ jsxs("div", { style: {
1517
- display: "flex",
1518
- justifyContent: "space-between",
1519
- alignItems: "center",
1520
- padding: "0.75rem 1rem",
1521
- borderBottom: "1px solid #222"
1522
- }, children: [
1523
- /* @__PURE__ */ jsx("span", { style: { color: "#fff", fontWeight: 600, fontSize: "0.9rem", fontFamily: fontDisplay }, children: coa.document_name || "Lab Results" }),
1524
- /* @__PURE__ */ jsx(
1525
- "button",
1526
- {
1527
- onClick: () => setShowCOA(false),
1528
- style: {
1529
- background: "rgba(255,255,255,0.1)",
1530
- border: "none",
1531
- color: "#fff",
1532
- fontSize: "1rem",
1533
- cursor: "pointer",
1534
- padding: "0.375rem 0.75rem",
1535
- borderRadius: 4
1536
- },
1537
- children: "\u2715"
1538
- }
1539
- )
1540
- ] }),
1541
- /* @__PURE__ */ jsx(
1542
- "iframe",
1543
- {
1544
- src: coa.url,
1545
- style: { flex: 1, border: "none", background: "#fff" },
1546
- title: "Lab Results"
1547
- }
1548
- )
1549
- ]
1550
- }
1551
- )
1582
+ showCOA && coa && /* @__PURE__ */ jsx(COAModal, { coa, theme, onClose: () => setShowCOA(false) })
1552
1583
  ] });
1553
1584
  }
1554
- function Badge({ text, bg, color, bold }) {
1555
- return /* @__PURE__ */ jsx("span", { style: {
1556
- padding: "0.25rem 0.625rem",
1557
- borderRadius: 4,
1558
- fontSize: "0.7rem",
1559
- fontWeight: bold ? 700 : 500,
1560
- textTransform: "uppercase",
1561
- letterSpacing: "0.04em",
1562
- background: bg,
1563
- color
1564
- }, children: text });
1565
- }
1566
- function PotencyStat({ label, value, accent, fg }) {
1567
- return /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", padding: "0.25rem 0" }, children: [
1568
- /* @__PURE__ */ jsxs("div", { style: { fontSize: "1.5rem", fontWeight: 700, color: fg, lineHeight: 1.1 }, children: [
1585
+ function CannabinoidStat({ label, value, theme, showBorder }) {
1586
+ return /* @__PURE__ */ jsxs("div", { style: {
1587
+ padding: "1.25rem 0.5rem",
1588
+ textAlign: "center",
1589
+ borderRight: showBorder ? `1px solid ${theme.fg}0F` : void 0
1590
+ }, children: [
1591
+ /* @__PURE__ */ jsxs("div", { style: {
1592
+ fontFamily: theme.fontDisplay,
1593
+ fontSize: "clamp(1.5rem, 5vw, 2rem)",
1594
+ fontWeight: 300,
1595
+ lineHeight: 1,
1596
+ color: theme.fg
1597
+ }, children: [
1569
1598
  value.toFixed(value >= 1 ? 1 : 2),
1570
- "%"
1599
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "0.6em", marginLeft: 1 }, children: "%" })
1571
1600
  ] }),
1572
1601
  /* @__PURE__ */ jsx("div", { style: {
1573
- fontSize: "0.65rem",
1574
- color: accent,
1602
+ fontSize: 11,
1603
+ fontWeight: 500,
1575
1604
  textTransform: "uppercase",
1576
- letterSpacing: "0.06em",
1577
- marginTop: "0.25rem",
1578
- fontWeight: 600
1605
+ letterSpacing: "0.25em",
1606
+ color: theme.accent,
1607
+ marginTop: "0.5rem"
1579
1608
  }, children: label })
1580
1609
  ] });
1581
1610
  }
1582
- function DetailRow({ label, value, fg, muted }) {
1583
- return /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "baseline" }, children: [
1584
- /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8rem", color: muted }, children: label }),
1585
- /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8rem", color: fg, fontWeight: 500 }, children: value })
1611
+ function DetailRow({ label, value, theme }) {
1612
+ return /* @__PURE__ */ jsxs("div", { style: {
1613
+ display: "flex",
1614
+ justifyContent: "space-between",
1615
+ alignItems: "baseline",
1616
+ padding: "0.625rem 0"
1617
+ }, children: [
1618
+ /* @__PURE__ */ jsx("span", { style: {
1619
+ fontSize: 12,
1620
+ textTransform: "uppercase",
1621
+ letterSpacing: "0.15em",
1622
+ color: `${theme.fg}66`
1623
+ }, children: label }),
1624
+ /* @__PURE__ */ jsx("span", { style: {
1625
+ fontSize: 14,
1626
+ fontWeight: 300,
1627
+ color: `${theme.fg}CC`
1628
+ }, children: value })
1586
1629
  ] });
1587
1630
  }
1588
- function FooterBadge({ icon, text, muted }) {
1589
- return /* @__PURE__ */ jsxs("span", { style: { fontSize: "0.7rem", color: muted, display: "flex", alignItems: "center", gap: "0.25rem" }, children: [
1590
- /* @__PURE__ */ jsx("span", { style: { fontSize: "0.6rem" }, children: icon }),
1591
- " ",
1592
- text
1631
+ function FooterLabel({ text }) {
1632
+ return /* @__PURE__ */ jsx("span", { style: {
1633
+ fontSize: 10,
1634
+ textTransform: "uppercase",
1635
+ letterSpacing: "0.15em",
1636
+ color: "rgba(255,255,255,0.25)"
1637
+ }, children: text });
1638
+ }
1639
+ function COAModal({ coa, theme, onClose }) {
1640
+ return /* @__PURE__ */ jsxs("div", { style: { position: "fixed", inset: 0, zIndex: 9999, background: "rgba(0,0,0,0.95)", display: "flex", flexDirection: "column" }, children: [
1641
+ /* @__PURE__ */ jsxs("div", { style: {
1642
+ display: "flex",
1643
+ justifyContent: "space-between",
1644
+ alignItems: "center",
1645
+ padding: "0.75rem 1rem",
1646
+ borderBottom: `1px solid ${theme.fg}10`
1647
+ }, children: [
1648
+ /* @__PURE__ */ jsx("span", { style: {
1649
+ color: "#fff",
1650
+ fontFamily: theme.fontDisplay,
1651
+ fontWeight: 500,
1652
+ fontSize: "0.85rem",
1653
+ letterSpacing: "-0.01em"
1654
+ }, children: coa.document_name || "Lab Results" }),
1655
+ /* @__PURE__ */ jsx(
1656
+ "button",
1657
+ {
1658
+ onClick: onClose,
1659
+ style: {
1660
+ background: `${theme.fg}10`,
1661
+ border: "none",
1662
+ color: "#fff",
1663
+ fontSize: "0.85rem",
1664
+ cursor: "pointer",
1665
+ padding: "0.375rem 0.75rem",
1666
+ letterSpacing: "0.05em"
1667
+ },
1668
+ children: "Close"
1669
+ }
1670
+ )
1671
+ ] }),
1672
+ /* @__PURE__ */ jsx("iframe", { src: coa.url, style: { flex: 1, border: "none", background: "#fff" }, title: "Lab Results" })
1593
1673
  ] });
1594
1674
  }
1675
+ function toNum(v) {
1676
+ if (v === "" || v == null) return null;
1677
+ const n = Number(v);
1678
+ return Number.isFinite(n) ? n : null;
1679
+ }
1680
+ function toStr(v) {
1681
+ if (v == null || v === "") return null;
1682
+ return String(v);
1683
+ }
1595
1684
  function strainBadgeColor(strain) {
1596
1685
  const s = strain.toLowerCase();
1597
1686
  if (s === "sativa") return "#22c55e";