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