@neowhale/storefront 0.2.7 → 0.2.9

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.
@@ -366,34 +366,14 @@ declare function useCoupons(): {
366
366
  };
367
367
 
368
368
  interface QRLandingPageProps {
369
- /** QR code string (from URL param) */
370
369
  code: string;
371
- /** Gateway URL — defaults to https://whale-gateway.fly.dev */
372
370
  gatewayUrl?: string;
373
- /** Custom component for product display */
374
371
  renderProduct?: (data: QRLandingData) => React.ReactNode;
375
- /** Custom component for COA/lab results */
376
372
  renderCOA?: (data: QRLandingData) => React.ReactNode;
377
- /** Custom component for the full page (overrides default layout) */
378
373
  renderPage?: (data: QRLandingData) => React.ReactNode;
379
- /** Called when data is loaded */
380
374
  onDataLoaded?: (data: QRLandingData) => void;
381
- /** Called on error */
382
375
  onError?: (error: Error) => void;
383
376
  }
384
- /**
385
- * QR Landing Page — drop-in component that renders a branded landing page
386
- * for any QR code. Fetches data from the gateway's public endpoint.
387
- *
388
- * Usage:
389
- * ```tsx
390
- * // In app/qr/[code]/page.tsx:
391
- * import { QRLandingPage } from '@neowhale/storefront/react'
392
- * export default function Page({ params }: { params: { code: string } }) {
393
- * return <QRLandingPage code={params.code} />
394
- * }
395
- * ```
396
- */
397
377
  declare function QRLandingPage({ code, gatewayUrl, renderProduct, renderCOA, renderPage, onDataLoaded, onError, }: QRLandingPageProps): react_jsx_runtime.JSX.Element | null;
398
378
 
399
379
  interface LandingPageProps {
@@ -366,34 +366,14 @@ declare function useCoupons(): {
366
366
  };
367
367
 
368
368
  interface QRLandingPageProps {
369
- /** QR code string (from URL param) */
370
369
  code: string;
371
- /** Gateway URL — defaults to https://whale-gateway.fly.dev */
372
370
  gatewayUrl?: string;
373
- /** Custom component for product display */
374
371
  renderProduct?: (data: QRLandingData) => React.ReactNode;
375
- /** Custom component for COA/lab results */
376
372
  renderCOA?: (data: QRLandingData) => React.ReactNode;
377
- /** Custom component for the full page (overrides default layout) */
378
373
  renderPage?: (data: QRLandingData) => React.ReactNode;
379
- /** Called when data is loaded */
380
374
  onDataLoaded?: (data: QRLandingData) => void;
381
- /** Called on error */
382
375
  onError?: (error: Error) => void;
383
376
  }
384
- /**
385
- * QR Landing Page — drop-in component that renders a branded landing page
386
- * for any QR code. Fetches data from the gateway's public endpoint.
387
- *
388
- * Usage:
389
- * ```tsx
390
- * // In app/qr/[code]/page.tsx:
391
- * import { QRLandingPage } from '@neowhale/storefront/react'
392
- * export default function Page({ params }: { params: { code: string } }) {
393
- * return <QRLandingPage code={params.code} />
394
- * }
395
- * ```
396
- */
397
377
  declare function QRLandingPage({ code, gatewayUrl, renderProduct, renderCOA, renderPage, onDataLoaded, onError, }: QRLandingPageProps): react_jsx_runtime.JSX.Element | null;
398
378
 
399
379
  interface LandingPageProps {
@@ -1262,174 +1262,414 @@ 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 || "#111";
1279
- const muted = store?.theme?.muted || "#888";
1280
1293
  const logoUrl = qr.logo_url || store?.logo_url;
1281
- const productImage = lp.image_url || product?.featured_image || product?.image_gallery?.[0] || null;
1294
+ const productImage = lp.image_url || product?.featured_image || null;
1282
1295
  const productName = lp.title || product?.name || qr.name;
1283
1296
  const description = lp.description || product?.description || "";
1284
- const ctaText = lp.cta_text || (coa ? "View Lab Results" : "Learn More");
1285
1297
  const ctaUrl = lp.cta_url || qr.destination_url;
1298
+ const categoryName = product?.category_name ?? null;
1286
1299
  const cf = product?.custom_fields;
1287
1300
  const thca = cf?.thca_percentage ?? null;
1288
1301
  const thc = cf?.d9_percentage ?? null;
1289
1302
  const cbd = cf?.cbd_total ?? null;
1290
1303
  const strainType = cf?.strain_type ?? null;
1291
- const categoryName = product?.category_name ?? null;
1304
+ const batchNumber = cf?.batch_number ?? null;
1305
+ const dateTested = cf?.date_tested ?? null;
1306
+ const terpenes = cf?.terpenes ?? null;
1307
+ const effects = cf?.effects ?? null;
1308
+ const genetics = cf?.genetics ?? null;
1309
+ const tagline = cf?.tagline ?? null;
1310
+ const pricingData = product?.pricing_data;
1311
+ const tiers = pricingData?.tiers?.sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0)) ?? [];
1312
+ const lowestPrice = tiers.length > 0 ? Math.min(...tiers.map((t) => t.default_price)) : null;
1292
1313
  const handleCOAClick = useCallback(() => {
1293
1314
  if (coa) setShowCOA(true);
1294
1315
  }, [coa]);
1295
- return /* @__PURE__ */ jsxs("div", { style: { minHeight: "100dvh", background: bg, color: fg, fontFamily: "system-ui, -apple-system, sans-serif" }, children: [
1296
- /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", display: "flex", justifyContent: "center" }, children: logoUrl && /* @__PURE__ */ jsx(
1297
- "img",
1298
- {
1299
- src: logoUrl,
1300
- alt: store?.name || "Store",
1301
- style: { height: 40, objectFit: "contain" }
1302
- }
1303
- ) }),
1304
- productImage && /* @__PURE__ */ jsx("div", { style: { width: "100%", aspectRatio: "1", overflow: "hidden", background: surface }, children: /* @__PURE__ */ jsx(
1305
- "img",
1306
- {
1307
- src: productImage,
1308
- alt: productName,
1309
- style: { width: "100%", height: "100%", objectFit: "cover" }
1310
- }
1311
- ) }),
1312
- /* @__PURE__ */ jsxs("div", { style: { padding: "1.5rem", maxWidth: 480, margin: "0 auto" }, children: [
1313
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.5rem", marginBottom: "0.75rem", flexWrap: "wrap" }, children: [
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",
1338
+ display: "flex",
1339
+ alignItems: "center",
1340
+ justifyContent: "space-between",
1341
+ maxWidth: 1280,
1342
+ margin: "0 auto"
1343
+ }, children: [
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
+ )
1363
+ ] }),
1364
+ /* @__PURE__ */ jsxs("main", { style: { maxWidth: 560, margin: "0 auto", padding: "0 1.5rem 3rem" }, children: [
1365
+ productImage && /* @__PURE__ */ jsx("div", { style: {
1366
+ width: "100%",
1367
+ aspectRatio: "1",
1368
+ overflow: "hidden",
1369
+ background: theme.surfaceLight,
1370
+ display: "flex",
1371
+ alignItems: "center",
1372
+ justifyContent: "center",
1373
+ padding: "2rem"
1374
+ }, children: /* @__PURE__ */ jsx(
1375
+ "img",
1376
+ {
1377
+ src: productImage,
1378
+ alt: productName,
1379
+ style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" }
1380
+ }
1381
+ ) }),
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,
1385
+ fontWeight: 500,
1386
+ color: theme.accent,
1387
+ textTransform: "uppercase",
1388
+ letterSpacing: "0.15em"
1389
+ }, children: categoryName }),
1314
1390
  strainType && /* @__PURE__ */ jsx("span", { style: {
1315
- padding: "0.25rem 0.75rem",
1316
- borderRadius: 999,
1317
- fontSize: "0.75rem",
1391
+ fontSize: 10,
1318
1392
  fontWeight: 600,
1319
1393
  textTransform: "uppercase",
1320
- letterSpacing: "0.05em",
1394
+ letterSpacing: "0.08em",
1395
+ padding: "0.2rem 0.5rem",
1321
1396
  background: strainBadgeColor(strainType),
1322
1397
  color: "#fff"
1323
- }, children: strainType }),
1324
- categoryName && /* @__PURE__ */ jsx("span", { style: {
1325
- padding: "0.25rem 0.75rem",
1326
- borderRadius: 999,
1327
- fontSize: "0.75rem",
1328
- background: surface,
1329
- color: muted
1330
- }, children: categoryName })
1398
+ }, children: strainType })
1399
+ ] }),
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"
1420
+ }, children: [
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" })
1331
1424
  ] }),
1332
- /* @__PURE__ */ jsx("h1", { style: { fontSize: "1.75rem", fontWeight: 600, margin: "0 0 0.5rem", lineHeight: 1.2 }, children: productName }),
1333
- description && /* @__PURE__ */ jsx("p", { style: { color: muted, lineHeight: 1.6, margin: "0 0 1.5rem", fontSize: "0.95rem" }, children: description }),
1334
1425
  renderProduct ? renderProduct(data) : null,
1335
- (thca || thc || cbd) && /* @__PURE__ */ jsxs("div", { style: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: "0.75rem", margin: "1.5rem 0" }, children: [
1336
- thca != null && /* @__PURE__ */ jsx(StatCard, { label: "THCa", value: `${thca.toFixed(1)}%`, bg: surface, fg, accent }),
1337
- thc != null && /* @__PURE__ */ jsx(StatCard, { label: "D9 THC", value: `${thc.toFixed(1)}%`, bg: surface, fg, accent }),
1338
- cbd != null && /* @__PURE__ */ jsx(StatCard, { label: "CBD", value: `${cbd.toFixed(1)}%`, bg: surface, fg, accent })
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)`
1431
+ }, children: [
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,
1443
+ textAlign: "center"
1444
+ }, children: [
1445
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: "1rem", fontWeight: 300, fontFamily: theme.fontDisplay }, children: [
1446
+ "$",
1447
+ tier.default_price.toFixed(2)
1448
+ ] }),
1449
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 10, color: `${theme.fg}60`, letterSpacing: "0.1em", textTransform: "uppercase", marginTop: 2 }, children: tier.label })
1450
+ ] }, tier.id)) })
1339
1451
  ] }),
1340
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.75rem", marginTop: "1.5rem" }, children: [
1341
- 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(
1342
1492
  "button",
1343
1493
  {
1344
1494
  onClick: handleCOAClick,
1345
1495
  style: {
1346
1496
  width: "100%",
1347
- padding: "0.875rem",
1348
- background: accent,
1349
- color: bg,
1350
- border: "none",
1351
- fontSize: "0.95rem",
1352
- fontWeight: 600,
1497
+ position: "relative",
1498
+ height: 180,
1499
+ border: `1px solid ${theme.fg}0F`,
1500
+ background: theme.surface,
1353
1501
  cursor: "pointer",
1354
- borderRadius: 0
1502
+ overflow: "hidden",
1503
+ padding: 0,
1504
+ display: "block"
1355
1505
  },
1356
- children: "View Lab Results"
1357
- }
1358
- ),
1359
- renderCOA ? renderCOA(data) : null,
1360
- /* @__PURE__ */ jsx(
1361
- "a",
1362
- {
1363
- href: ctaUrl,
1364
- style: {
1365
- display: "block",
1366
- width: "100%",
1367
- padding: "0.875rem",
1368
- background: "transparent",
1369
- color: fg,
1370
- border: `1px solid ${muted}`,
1371
- fontSize: "0.95rem",
1372
- fontWeight: 500,
1373
- textAlign: "center",
1374
- textDecoration: "none",
1375
- boxSizing: "border-box",
1376
- borderRadius: 0
1377
- },
1378
- children: ctaText === "View Lab Results" ? "Shop Online" : ctaText
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
+ ]
1379
1539
  }
1380
1540
  )
1381
1541
  ] }),
1382
- /* @__PURE__ */ jsxs("div", { style: { marginTop: "2rem", paddingTop: "1.5rem", borderTop: `1px solid ${surface}`, textAlign: "center" }, children: [
1383
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "center", gap: "1.5rem", marginBottom: "0.75rem" }, children: [
1384
- /* @__PURE__ */ jsx("span", { style: { fontSize: "0.75rem", color: muted, textTransform: "uppercase", letterSpacing: "0.05em" }, children: "Lab Tested" }),
1385
- /* @__PURE__ */ jsx("span", { style: { fontSize: "0.75rem", color: muted, textTransform: "uppercase", letterSpacing: "0.05em" }, children: "Authentic" })
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
+ ),
1566
+ /* @__PURE__ */ jsxs("footer", { style: {
1567
+ marginTop: "3rem",
1568
+ paddingTop: "1.5rem",
1569
+ borderTop: `1px solid ${theme.fg}0A`,
1570
+ textAlign: "center"
1571
+ }, children: [
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" })
1386
1575
  ] }),
1387
- store?.name && /* @__PURE__ */ jsxs("p", { style: { fontSize: "0.75rem", color: muted, margin: 0 }, children: [
1388
- "Verified by ",
1389
- store.name
1576
+ store?.name && /* @__PURE__ */ jsxs("p", { style: { fontSize: 11, color: `${theme.fg}40`, margin: "0.5rem 0 0", letterSpacing: "0.1em" }, children: [
1577
+ store.name,
1578
+ store?.tagline ? ` \u2014 ${store.tagline}` : ""
1390
1579
  ] })
1391
1580
  ] })
1392
1581
  ] }),
1393
- showCOA && coa && /* @__PURE__ */ jsxs(
1394
- "div",
1395
- {
1396
- style: {
1397
- position: "fixed",
1398
- inset: 0,
1399
- zIndex: 9999,
1400
- background: "rgba(0,0,0,0.9)",
1401
- display: "flex",
1402
- flexDirection: "column"
1403
- },
1404
- children: [
1405
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "1rem" }, children: [
1406
- /* @__PURE__ */ jsx("span", { style: { color: "#fff", fontWeight: 600 }, children: coa.document_name || "Lab Results" }),
1407
- /* @__PURE__ */ jsx(
1408
- "button",
1409
- {
1410
- onClick: () => setShowCOA(false),
1411
- style: { background: "none", border: "none", color: "#fff", fontSize: "1.5rem", cursor: "pointer", padding: "0.5rem" },
1412
- children: "\u2715"
1413
- }
1414
- )
1415
- ] }),
1416
- /* @__PURE__ */ jsx(
1417
- "iframe",
1418
- {
1419
- src: coa.url,
1420
- style: { flex: 1, border: "none", background: "#fff" },
1421
- title: "Lab Results"
1422
- }
1423
- )
1424
- ]
1425
- }
1426
- )
1582
+ showCOA && coa && /* @__PURE__ */ jsx(COAModal, { coa, theme, onClose: () => setShowCOA(false) })
1583
+ ] });
1584
+ }
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: [
1598
+ value.toFixed(value >= 1 ? 1 : 2),
1599
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "0.6em", marginLeft: 1 }, children: "%" })
1600
+ ] }),
1601
+ /* @__PURE__ */ jsx("div", { style: {
1602
+ fontSize: 11,
1603
+ fontWeight: 500,
1604
+ textTransform: "uppercase",
1605
+ letterSpacing: "0.25em",
1606
+ color: theme.accent,
1607
+ marginTop: "0.5rem"
1608
+ }, children: label })
1427
1609
  ] });
1428
1610
  }
1429
- function StatCard({ label, value, bg, fg, accent }) {
1430
- return /* @__PURE__ */ jsxs("div", { style: { background: bg, padding: "1rem", textAlign: "center" }, children: [
1431
- /* @__PURE__ */ jsx("div", { style: { fontSize: "1.25rem", fontWeight: 700, color: fg }, children: value }),
1432
- /* @__PURE__ */ jsx("div", { style: { fontSize: "0.7rem", color: accent, textTransform: "uppercase", letterSpacing: "0.05em", marginTop: "0.25rem" }, children: label })
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 })
1629
+ ] });
1630
+ }
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" })
1433
1673
  ] });
1434
1674
  }
1435
1675
  function strainBadgeColor(strain) {
@@ -1439,6 +1679,14 @@ function strainBadgeColor(strain) {
1439
1679
  if (s === "hybrid") return "#f59e0b";
1440
1680
  return "#6b7280";
1441
1681
  }
1682
+ function formatDate(dateStr) {
1683
+ try {
1684
+ const d = /* @__PURE__ */ new Date(dateStr + "T00:00:00");
1685
+ return d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
1686
+ } catch {
1687
+ return dateStr;
1688
+ }
1689
+ }
1442
1690
  var containerStyle = {
1443
1691
  minHeight: "100dvh",
1444
1692
  display: "flex",