@tokenoftrust/storefront-runner 1.3.2

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.
Files changed (310) hide show
  1. package/LICENSE +58 -0
  2. package/README.md +42 -0
  3. package/apps/storefront/astro.config.mjs +66 -0
  4. package/apps/storefront/dev-plugins/dev-loop-state.mjs +147 -0
  5. package/apps/storefront/dev-plugins/tenant-hot-reload.mjs +238 -0
  6. package/apps/storefront/env.d.ts +162 -0
  7. package/apps/storefront/migrations/0000_baseline.sql +54 -0
  8. package/apps/storefront/migrations/0001_products_fts.sql +9 -0
  9. package/apps/storefront/migrations/README.md +54 -0
  10. package/apps/storefront/migrations/meta/0000_snapshot.json +347 -0
  11. package/apps/storefront/migrations/meta/0001_snapshot.json +347 -0
  12. package/apps/storefront/migrations/meta/_journal.json +20 -0
  13. package/apps/storefront/package.json +42 -0
  14. package/apps/storefront/public/brand/northwind-logo-dark.svg +4 -0
  15. package/apps/storefront/public/brand/northwind-logo-light.svg +4 -0
  16. package/apps/storefront/public/brand/northwind-og.svg +13 -0
  17. package/apps/storefront/public/favicon.svg +5 -0
  18. package/apps/storefront/public/js/dashboard-team.js +147 -0
  19. package/apps/storefront/public/js/dashboard-vendor-search.js +115 -0
  20. package/apps/storefront/public/shared/commerce-chrome.css +369 -0
  21. package/apps/storefront/src/components/Badge.astro +34 -0
  22. package/apps/storefront/src/components/Breadcrumbs.astro +45 -0
  23. package/apps/storefront/src/components/CollectionCard.astro +56 -0
  24. package/apps/storefront/src/components/EmptyState.astro +55 -0
  25. package/apps/storefront/src/components/Img.astro +68 -0
  26. package/apps/storefront/src/components/ProductCard.astro +228 -0
  27. package/apps/storefront/src/components/Section.astro +39 -0
  28. package/apps/storefront/src/components/Seo.astro +70 -0
  29. package/apps/storefront/src/components/WidgetFrame.astro +34 -0
  30. package/apps/storefront/src/components/admin/AdminAIWorkflowsTab.astro +531 -0
  31. package/apps/storefront/src/components/admin/AdminCustomersTab.astro +597 -0
  32. package/apps/storefront/src/components/admin/AdminFoxyCartSettingsPanel.astro +227 -0
  33. package/apps/storefront/src/components/admin/AdminFulfillmentTab.astro +465 -0
  34. package/apps/storefront/src/components/admin/AdminProductsTab.astro +901 -0
  35. package/apps/storefront/src/components/admin/AdminReportingTab.astro +435 -0
  36. package/apps/storefront/src/components/admin/AdminSettingsTab.astro +782 -0
  37. package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +1223 -0
  38. package/apps/storefront/src/components/auth/AuthBadge.astro +44 -0
  39. package/apps/storefront/src/components/chrome/AnnouncementBar.astro +108 -0
  40. package/apps/storefront/src/components/chrome/MegaMenu.astro +26 -0
  41. package/apps/storefront/src/components/chrome/NavDropdown.astro +201 -0
  42. package/apps/storefront/src/components/chrome/SiteFooter.astro +201 -0
  43. package/apps/storefront/src/components/chrome/SiteHeader.astro +234 -0
  44. package/apps/storefront/src/components/chrome/UtilityNav.astro +79 -0
  45. package/apps/storefront/src/components/commerce/ArticleCard.astro +242 -0
  46. package/apps/storefront/src/components/commerce/BrandGrid.astro +175 -0
  47. package/apps/storefront/src/components/commerce/FoxyLoader.astro +23 -0
  48. package/apps/storefront/src/components/commerce/FoxySidecartStyles.astro +61 -0
  49. package/apps/storefront/src/components/commerce/PriceDisplay.astro +91 -0
  50. package/apps/storefront/src/components/commerce/ProductCarousel.astro +197 -0
  51. package/apps/storefront/src/components/commerce/ProductGrid.astro +75 -0
  52. package/apps/storefront/src/components/commerce/RatingStars.astro +58 -0
  53. package/apps/storefront/src/components/commerce/ReviewList.astro +54 -0
  54. package/apps/storefront/src/components/commerce/ReviewSummary.astro +44 -0
  55. package/apps/storefront/src/components/commerce/TagFilterCloud.astro +149 -0
  56. package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +37 -0
  57. package/apps/storefront/src/components/compliance/ComplianceNotice.astro +99 -0
  58. package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +45 -0
  59. package/apps/storefront/src/components/compliance/NicotineWarning.astro +33 -0
  60. package/apps/storefront/src/components/compliance/PactActNotice.astro +35 -0
  61. package/apps/storefront/src/components/compliance/Prop65Warning.astro +80 -0
  62. package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +38 -0
  63. package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +35 -0
  64. package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +53 -0
  65. package/apps/storefront/src/components/home/Hero.astro +164 -0
  66. package/apps/storefront/src/components/islands/AgeGate.tsx +109 -0
  67. package/apps/storefront/src/components/islands/ImageGallery.tsx +237 -0
  68. package/apps/storefront/src/components/islands/IsolatedAgeGate.tsx +73 -0
  69. package/apps/storefront/src/components/islands/MobileNav.tsx +239 -0
  70. package/apps/storefront/src/components/islands/QuickView.tsx +138 -0
  71. package/apps/storefront/src/components/islands/RecentlyViewed.tsx +122 -0
  72. package/apps/storefront/src/components/islands/SavedList.tsx +99 -0
  73. package/apps/storefront/src/components/islands/SearchTypeahead.tsx +248 -0
  74. package/apps/storefront/src/components/islands/VariantSelector.tsx +375 -0
  75. package/apps/storefront/src/components/marketing/CardGrid.astro +72 -0
  76. package/apps/storefront/src/components/marketing/CodeSample.astro +20 -0
  77. package/apps/storefront/src/components/marketing/CtaBand.astro +24 -0
  78. package/apps/storefront/src/components/marketing/Faq.astro +37 -0
  79. package/apps/storefront/src/components/marketing/Integrations.astro +63 -0
  80. package/apps/storefront/src/components/marketing/MarketingBlocks.astro +53 -0
  81. package/apps/storefront/src/components/marketing/MarketingCtas.astro +48 -0
  82. package/apps/storefront/src/components/marketing/MarketingHero.astro +72 -0
  83. package/apps/storefront/src/components/marketing/ProofStrip.astro +37 -0
  84. package/apps/storefront/src/components/marketing/SplitCompare.astro +53 -0
  85. package/apps/storefront/src/components/marketing/Steps.astro +36 -0
  86. package/apps/storefront/src/components/marketing/Testimonials.astro +38 -0
  87. package/apps/storefront/src/components/marketing/TrustBar.astro +22 -0
  88. package/apps/storefront/src/components/membership/IllustrativeLabel.astro +56 -0
  89. package/apps/storefront/src/components/membership/TierCards.astro +164 -0
  90. package/apps/storefront/src/components/membership/TierComparisonTable.astro +148 -0
  91. package/apps/storefront/src/components/plp/ActiveFilters.astro +79 -0
  92. package/apps/storefront/src/components/plp/FacetSidebar.astro +199 -0
  93. package/apps/storefront/src/components/plp/Pagination.astro +99 -0
  94. package/apps/storefront/src/components/plp/SortSelect.astro +64 -0
  95. package/apps/storefront/src/components/style-guide/StyleGuideNav.astro +95 -0
  96. package/apps/storefront/src/components/subscription/HowItWorksSteps.astro +67 -0
  97. package/apps/storefront/src/components/subscription/ManageSubscriptionEntry.astro +67 -0
  98. package/apps/storefront/src/components/subscription/SavingsExplainer.astro +101 -0
  99. package/apps/storefront/src/config/adminTargetTenant.ts +28 -0
  100. package/apps/storefront/src/config/devTenantSeed.ts +78 -0
  101. package/apps/storefront/src/config/devTenants.ts +16 -0
  102. package/apps/storefront/src/config/kvTenantResolver.ts +87 -0
  103. package/apps/storefront/src/config/resolver.ts +102 -0
  104. package/apps/storefront/src/config/tenants.ts +87 -0
  105. package/apps/storefront/src/layouts/Layout.astro +368 -0
  106. package/apps/storefront/src/lib/admin/README.md +122 -0
  107. package/apps/storefront/src/lib/admin/ai/approvalMode.ts +21 -0
  108. package/apps/storefront/src/lib/admin/ai/askEnvelope.ts +48 -0
  109. package/apps/storefront/src/lib/admin/ai/audit.ts +14 -0
  110. package/apps/storefront/src/lib/admin/ai/index.ts +6 -0
  111. package/apps/storefront/src/lib/admin/ai/operationalRuns.ts +155 -0
  112. package/apps/storefront/src/lib/admin/ai/recommendation.ts +20 -0
  113. package/apps/storefront/src/lib/admin/checkoutStyleInput.ts +172 -0
  114. package/apps/storefront/src/lib/admin/checkoutStyleTarget.ts +65 -0
  115. package/apps/storefront/src/lib/admin/envelope.ts +10 -0
  116. package/apps/storefront/src/lib/admin/evidence/evidenceReference.ts +32 -0
  117. package/apps/storefront/src/lib/admin/memoizeByEnvelope.ts +27 -0
  118. package/apps/storefront/src/lib/admin/orderClient.ts +146 -0
  119. package/apps/storefront/src/lib/admin/seed.ts +54 -0
  120. package/apps/storefront/src/lib/admin/services/mock/aiWorkflows.ts +59 -0
  121. package/apps/storefront/src/lib/admin/services/mock/customers.ts +86 -0
  122. package/apps/storefront/src/lib/admin/services/mock/evidence.ts +99 -0
  123. package/apps/storefront/src/lib/admin/services/mock/foxyCartSettings.ts +78 -0
  124. package/apps/storefront/src/lib/admin/services/mock/fulfillment.ts +62 -0
  125. package/apps/storefront/src/lib/admin/services/mock/index.ts +12 -0
  126. package/apps/storefront/src/lib/admin/services/mock/orders.ts +184 -0
  127. package/apps/storefront/src/lib/admin/services/mock/products.ts +105 -0
  128. package/apps/storefront/src/lib/admin/services/mock/reporting.ts +51 -0
  129. package/apps/storefront/src/lib/admin/services/mock/settings.ts +70 -0
  130. package/apps/storefront/src/lib/admin/services/mock/tenants.ts +29 -0
  131. package/apps/storefront/src/lib/admin/services/orders.ts +300 -0
  132. package/apps/storefront/src/lib/admin/viewmodels/aiWorkflows.ts +67 -0
  133. package/apps/storefront/src/lib/admin/viewmodels/customers.ts +56 -0
  134. package/apps/storefront/src/lib/admin/viewmodels/fulfillment.ts +49 -0
  135. package/apps/storefront/src/lib/admin/viewmodels/orders.ts +92 -0
  136. package/apps/storefront/src/lib/admin/viewmodels/products.ts +74 -0
  137. package/apps/storefront/src/lib/admin/viewmodels/reporting.ts +34 -0
  138. package/apps/storefront/src/lib/admin/viewmodels/settings.ts +64 -0
  139. package/apps/storefront/src/lib/adminTenantEnvelope.ts +90 -0
  140. package/apps/storefront/src/lib/analytics/index.ts +47 -0
  141. package/apps/storefront/src/lib/auth/brokerAssertion.ts +136 -0
  142. package/apps/storefront/src/lib/auth/devAuth.ts +90 -0
  143. package/apps/storefront/src/lib/auth/gatePage.ts +81 -0
  144. package/apps/storefront/src/lib/auth/identityToken.ts +341 -0
  145. package/apps/storefront/src/lib/auth/loginGate.ts +119 -0
  146. package/apps/storefront/src/lib/auth/route.ts +146 -0
  147. package/apps/storefront/src/lib/auth/session.ts +203 -0
  148. package/apps/storefront/src/lib/auth/totAccessClient.ts +503 -0
  149. package/apps/storefront/src/lib/basePath.ts +41 -0
  150. package/apps/storefront/src/lib/cfImage.ts +55 -0
  151. package/apps/storefront/src/lib/chrome/model.ts +244 -0
  152. package/apps/storefront/src/lib/chrome/parseConfig.ts +34 -0
  153. package/apps/storefront/src/lib/colors.ts +97 -0
  154. package/apps/storefront/src/lib/commerce/foxyCartSettings.ts +253 -0
  155. package/apps/storefront/src/lib/content-edit/applyDiff.ts +91 -0
  156. package/apps/storefront/src/lib/content-edit/client.ts +230 -0
  157. package/apps/storefront/src/lib/content-edit/index.ts +14 -0
  158. package/apps/storefront/src/lib/content-edit/route.ts +25 -0
  159. package/apps/storefront/src/lib/content-edit/types.ts +19 -0
  160. package/apps/storefront/src/lib/cspStaticHashes.ts +24 -0
  161. package/apps/storefront/src/lib/d1/catalog.ts +366 -0
  162. package/apps/storefront/src/lib/dashboard/staffAdmission.ts +43 -0
  163. package/apps/storefront/src/lib/dashboard/staffVendorAccess.ts +449 -0
  164. package/apps/storefront/src/lib/dashboard/tenantSelection.ts +136 -0
  165. package/apps/storefront/src/lib/dashboard/vendorQuery.ts +61 -0
  166. package/apps/storefront/src/lib/demoGate.ts +113 -0
  167. package/apps/storefront/src/lib/dev/activityIngestToken.ts +78 -0
  168. package/apps/storefront/src/lib/dev/activityStore.ts +72 -0
  169. package/apps/storefront/src/lib/dev/ai-widget-client.js +308 -0
  170. package/apps/storefront/src/lib/dev/apiBase.ts +14 -0
  171. package/apps/storefront/src/lib/dev/cliSignInCode.ts +155 -0
  172. package/apps/storefront/src/lib/dev/devDraft.ts +133 -0
  173. package/apps/storefront/src/lib/dev/hostedCockpit.ts +169 -0
  174. package/apps/storefront/src/lib/dev/livePreview.ts +77 -0
  175. package/apps/storefront/src/lib/dev/runtimeStore.ts +120 -0
  176. package/apps/storefront/src/lib/dev/storeStatus.ts +142 -0
  177. package/apps/storefront/src/lib/diag/redact.ts +20 -0
  178. package/apps/storefront/src/lib/edgeCache.ts +128 -0
  179. package/apps/storefront/src/lib/email/magicLinkInviteEmail.ts +62 -0
  180. package/apps/storefront/src/lib/env.ts +60 -0
  181. package/apps/storefront/src/lib/fixtures/sampleProducts.ts +130 -0
  182. package/apps/storefront/src/lib/format.ts +38 -0
  183. package/apps/storefront/src/lib/foxyCommerce.ts +49 -0
  184. package/apps/storefront/src/lib/imageAttrs.ts +55 -0
  185. package/apps/storefront/src/lib/jsonld.ts +166 -0
  186. package/apps/storefront/src/lib/maintenance.ts +71 -0
  187. package/apps/storefront/src/lib/membership/model.ts +97 -0
  188. package/apps/storefront/src/lib/memoryKv.ts +32 -0
  189. package/apps/storefront/src/lib/messaging/messagesClient.ts +143 -0
  190. package/apps/storefront/src/lib/placeholder.ts +99 -0
  191. package/apps/storefront/src/lib/previewReload.ts +125 -0
  192. package/apps/storefront/src/lib/priceBuckets.ts +33 -0
  193. package/apps/storefront/src/lib/rawChrome.ts +505 -0
  194. package/apps/storefront/src/lib/rawMarketingHtml.ts +136 -0
  195. package/apps/storefront/src/lib/search/index.ts +74 -0
  196. package/apps/storefront/src/lib/sections.ts +129 -0
  197. package/apps/storefront/src/lib/securityHeaders.ts +31 -0
  198. package/apps/storefront/src/lib/storyblok/content-model.ts +289 -0
  199. package/apps/storefront/src/lib/storyblok/provider.ts +607 -0
  200. package/apps/storefront/src/lib/subscription/model.ts +68 -0
  201. package/apps/storefront/src/lib/tenantRouting.ts +91 -0
  202. package/apps/storefront/src/lib/tot/ToTClient.ts +177 -0
  203. package/apps/storefront/src/lib/tot/d1Client.ts +62 -0
  204. package/apps/storefront/src/lib/tot/query.ts +249 -0
  205. package/apps/storefront/src/lib/tot/totClientInterface.ts +30 -0
  206. package/apps/storefront/src/lib/url.ts +142 -0
  207. package/apps/storefront/src/lib/wishlist.ts +63 -0
  208. package/apps/storefront/src/middleware/index.ts +455 -0
  209. package/apps/storefront/src/pages/404.astro +61 -0
  210. package/apps/storefront/src/pages/[...slug].astro +90 -0
  211. package/apps/storefront/src/pages/admin.astro +1675 -0
  212. package/apps/storefront/src/pages/api/admin/orders/[id].ts +53 -0
  213. package/apps/storefront/src/pages/api/admin/orders.ts +71 -0
  214. package/apps/storefront/src/pages/api/auth/logout.ts +21 -0
  215. package/apps/storefront/src/pages/api/auth/magic-exchange.ts +107 -0
  216. package/apps/storefront/src/pages/api/auth/send.ts +85 -0
  217. package/apps/storefront/src/pages/api/auth/verify.ts +135 -0
  218. package/apps/storefront/src/pages/api/cache-purge.ts +44 -0
  219. package/apps/storefront/src/pages/api/checkout-style.css.ts +43 -0
  220. package/apps/storefront/src/pages/api/cli/policy.ts +70 -0
  221. package/apps/storefront/src/pages/api/content-edit/ai-edit.ts +164 -0
  222. package/apps/storefront/src/pages/api/csp-report.ts +25 -0
  223. package/apps/storefront/src/pages/api/dashboard/enter-vendor.ts +124 -0
  224. package/apps/storefront/src/pages/api/dashboard/vendor-search.ts +120 -0
  225. package/apps/storefront/src/pages/api/dev/activity.ts +124 -0
  226. package/apps/storefront/src/pages/api/dev/cli-signin-code.ts +73 -0
  227. package/apps/storefront/src/pages/api/dev/diff.ts +68 -0
  228. package/apps/storefront/src/pages/api/dev/file.ts +57 -0
  229. package/apps/storefront/src/pages/api/dev/locate.ts +125 -0
  230. package/apps/storefront/src/pages/api/dev/publish.ts +128 -0
  231. package/apps/storefront/src/pages/api/dev/tree.ts +70 -0
  232. package/apps/storefront/src/pages/api/dev/write.ts +116 -0
  233. package/apps/storefront/src/pages/api/newsletter.ts +104 -0
  234. package/apps/storefront/src/pages/api/request-access.ts +172 -0
  235. package/apps/storefront/src/pages/auth/login.astro +149 -0
  236. package/apps/storefront/src/pages/auth/magic.astro +105 -0
  237. package/apps/storefront/src/pages/capabilities.astro +292 -0
  238. package/apps/storefront/src/pages/collections/[handle].astro +177 -0
  239. package/apps/storefront/src/pages/collections/index.astro +59 -0
  240. package/apps/storefront/src/pages/dashboard/[appDomain]/index.astro +100 -0
  241. package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +154 -0
  242. package/apps/storefront/src/pages/dashboard/index.astro +160 -0
  243. package/apps/storefront/src/pages/index.astro +290 -0
  244. package/apps/storefront/src/pages/llms.txt.ts +92 -0
  245. package/apps/storefront/src/pages/products/[handle].astro +402 -0
  246. package/apps/storefront/src/pages/robots.txt.ts +24 -0
  247. package/apps/storefront/src/pages/saved.astro +35 -0
  248. package/apps/storefront/src/pages/search.astro +150 -0
  249. package/apps/storefront/src/pages/sitemap.xml.ts +78 -0
  250. package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +1280 -0
  251. package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +229 -0
  252. package/apps/storefront/src/pages/style-guide/[tenant]/guide/[theme].astro +144 -0
  253. package/apps/storefront/src/pages/style-guide/[tenant]/index.astro +116 -0
  254. package/apps/storefront/src/pages/style-guide/index.astro +98 -0
  255. package/apps/storefront/src/pages/tenants/[id]/[...path].ts +57 -0
  256. package/apps/storefront/src/styles/global.css +292 -0
  257. package/apps/storefront/src/themes/reference.ts +68 -0
  258. package/apps/storefront/src/themes/schema.ts +111 -0
  259. package/apps/storefront/src/themes/tenants/index.ts +51 -0
  260. package/apps/storefront/tsconfig.json +16 -0
  261. package/apps/storefront/vitest.config.ts +22 -0
  262. package/package.json +26 -0
  263. package/packages/public-runtime/LICENSE +58 -0
  264. package/packages/public-runtime/package.json +22 -0
  265. package/packages/public-runtime/src/binary-path.ts +68 -0
  266. package/packages/public-runtime/src/capabilities.ts +89 -0
  267. package/packages/public-runtime/src/catalog-api.ts +81 -0
  268. package/packages/public-runtime/src/catalog-d1.ts +192 -0
  269. package/packages/public-runtime/src/checkout-style-publish.ts +200 -0
  270. package/packages/public-runtime/src/checkout-style.ts +375 -0
  271. package/packages/public-runtime/src/csp.ts +109 -0
  272. package/packages/public-runtime/src/customization-preview.ts +197 -0
  273. package/packages/public-runtime/src/customization-reconcile.ts +63 -0
  274. package/packages/public-runtime/src/customization-runtime.ts +157 -0
  275. package/packages/public-runtime/src/customization-scripts.ts +64 -0
  276. package/packages/public-runtime/src/customization-versioning.ts +95 -0
  277. package/packages/public-runtime/src/foxy-orders.ts +136 -0
  278. package/packages/public-runtime/src/foxy.ts +196 -0
  279. package/packages/public-runtime/src/hot-standby-readiness.ts +140 -0
  280. package/packages/public-runtime/src/index.ts +34 -0
  281. package/packages/public-runtime/src/product.ts +159 -0
  282. package/packages/public-runtime/src/search.ts +45 -0
  283. package/packages/public-runtime/src/tenant-assets.ts +444 -0
  284. package/packages/public-runtime/src/tenant.ts +384 -0
  285. package/packages/public-runtime/tsconfig.json +8 -0
  286. package/packages/public-runtime/vitest.config.ts +8 -0
  287. package/pnpm-workspace.yaml +7 -0
  288. package/scripts/build/copy-tenant-assets.mjs +69 -0
  289. package/scripts/dev/ai-edit.mjs +183 -0
  290. package/scripts/dev/checkout-watch.mjs +116 -0
  291. package/scripts/dev/file-browser.mjs +159 -0
  292. package/scripts/dev/file-writer.mjs +141 -0
  293. package/scripts/dev/git-status.mjs +171 -0
  294. package/scripts/dev/locate-handler.mjs +163 -0
  295. package/scripts/dev/locate.mjs +112 -0
  296. package/scripts/dev/port-check.mjs +48 -0
  297. package/scripts/dev/safe-path.mjs +109 -0
  298. package/scripts/dev/shot.mjs +46 -0
  299. package/scripts/dev/tenant-source-root.mjs +76 -0
  300. package/scripts/dev/unified-diff.mjs +81 -0
  301. package/scripts/tot-dev.mjs +376 -0
  302. package/tenants/README.md +14 -0
  303. package/tenants/home/content/chrome.json +7 -0
  304. package/tenants/home/content/home.html +58 -0
  305. package/tenants/home/content/home.json +11 -0
  306. package/tenants/home/public/js/storefront.js +210 -0
  307. package/tenants/home/public/logo-wordmark.png +0 -0
  308. package/tenants/home/public/pages/storefront.css +212 -0
  309. package/tenants/home/theme.json +6 -0
  310. package/tsconfig.base.json +20 -0
@@ -0,0 +1,54 @@
1
+ ---
2
+ /**
3
+ * <ReviewList> — molecule. A grid of individual review cards (author, star
4
+ * rating, optional title, body, verified-buyer badge, date).
5
+ *
6
+ * Presentation only — it renders exactly the `Review[]` you pass through, in
7
+ * order, up to `max`. It never fabricates a review, a rating, or a "verified"
8
+ * badge: every field comes from the caller's sourced/imported review data
9
+ * (claims-ledger discipline; see docs/architecture/review-markup-policy.md).
10
+ * Renders nothing when `reviews` is empty — callers decide whether an empty
11
+ * state or the section itself should show.
12
+ *
13
+ * data-widget="review-list" is the audit/test hook.
14
+ */
15
+ import type { Review } from "@tot/public-runtime";
16
+ import RatingStars from "./RatingStars.astro";
17
+ import { formatDate } from "@/lib/format";
18
+
19
+ interface Props {
20
+ reviews: Review[];
21
+ /** Cap how many render (source list may be longer). */
22
+ max?: number;
23
+ locale?: string;
24
+ class?: string;
25
+ }
26
+
27
+ const { reviews, max = 10, locale = "en-US", class: className } = Astro.props;
28
+ const shown = reviews.slice(0, max);
29
+ ---
30
+
31
+ {
32
+ shown.length > 0 && (
33
+ <ul class:list={["grid gap-5 md:grid-cols-2", className]} data-widget="review-list">
34
+ {shown.map((r) => (
35
+ <li class="rounded-[var(--radius-md)] border border-[var(--color-border)] bg-[var(--color-surface)] p-5">
36
+ <div class="flex items-center justify-between gap-3">
37
+ <RatingStars rating={r.rating} showCount={false} />
38
+ {r.verifiedBuyer && (
39
+ <span class="font-mono text-[0.6rem] uppercase tracking-[0.12em] text-[var(--color-success)]">
40
+ Verified buyer
41
+ </span>
42
+ )}
43
+ </div>
44
+ {r.title && <p class="mt-2.5 font-display font-semibold leading-snug">{r.title}</p>}
45
+ <p class="mt-1.5 text-sm leading-relaxed text-[var(--color-text)]">{r.body}</p>
46
+ <p class="mt-3 text-xs text-[var(--color-muted)]">
47
+ {r.author}
48
+ {formatDate(r.date, locale) ? ` · ${formatDate(r.date, locale)}` : ""}
49
+ </p>
50
+ </li>
51
+ ))}
52
+ </ul>
53
+ )
54
+ }
@@ -0,0 +1,44 @@
1
+ ---
2
+ /**
3
+ * <ReviewSummary> — atom. The aggregate rating badge (stars + "N reviews") shown
4
+ * near a product title. Wraps <RatingStars>; adds an optional link down to the
5
+ * full review list.
6
+ *
7
+ * Presentation only — it renders whatever aggregate you pass. No review data is
8
+ * invented here (claims-ledger discipline: source the average/count before
9
+ * shipping them; see docs/architecture/review-markup-policy.md). Callers that
10
+ * have no real aggregate yet should not render this component at all.
11
+ *
12
+ * data-widget="review-summary" is the audit/test hook.
13
+ */
14
+ import RatingStars from "./RatingStars.astro";
15
+
16
+ interface Props {
17
+ /** 0–5, real average from the source reviews app. */
18
+ average: number;
19
+ /** Real review count backing the average. */
20
+ count: number;
21
+ /** Anchor to the full review list section, e.g. "#reviews". */
22
+ href?: string;
23
+ size?: "sm" | "md";
24
+ class?: string;
25
+ }
26
+
27
+ const { average, count, href, size = "sm", class: className } = Astro.props;
28
+ ---
29
+
30
+ {
31
+ href ? (
32
+ <a
33
+ href={href}
34
+ class:list={["inline-flex items-center gap-2 hover:opacity-80", className]}
35
+ data-widget="review-summary"
36
+ >
37
+ <RatingStars rating={average} count={count} size={size} />
38
+ </a>
39
+ ) : (
40
+ <div class:list={["inline-flex items-center gap-2", className]} data-widget="review-summary">
41
+ <RatingStars rating={average} count={count} size={size} />
42
+ </div>
43
+ )
44
+ }
@@ -0,0 +1,149 @@
1
+ ---
2
+ /**
3
+ * <TagFilterCloud> — a cloud of tag/category chips for a blog or article index,
4
+ * each linking to its filtered view. The active tag is marked (aria-current) and
5
+ * visually filled; an optional leading "All" chip clears the filter. Tenant-
6
+ * agnostic — the `tags` are content, theme tokens are the skin.
7
+ *
8
+ * URL-addressable + zero-JS: each chip is a plain link, so filtering works
9
+ * without client script (the server renders the filtered index). Mirrors the
10
+ * raw-chrome `.tag-cloud` / `.tag-chip` block in
11
+ * public/shared/commerce-chrome.css — same token vocabulary, same look.
12
+ */
13
+ import { withBase } from "@/lib/basePath";
14
+
15
+ export interface TagItem {
16
+ /** Display label. */
17
+ label: string;
18
+ /** Filtered-index URL. Base-prefixed here. */
19
+ href: string;
20
+ /** Optional article count shown as a superscript-ish suffix. */
21
+ count?: number;
22
+ /** True for the currently-selected tag. */
23
+ active?: boolean;
24
+ }
25
+
26
+ interface Props {
27
+ tags: TagItem[];
28
+ /** Optional heading above the cloud. */
29
+ title?: string;
30
+ /** Render a leading "All" chip that clears the filter. */
31
+ allHref?: string;
32
+ allLabel?: string;
33
+ /** True when no tag filter is applied (the "All" chip is active). */
34
+ allActive?: boolean;
35
+ /** Accessible label for the nav region (default "Filter by tag"). */
36
+ ariaLabel?: string;
37
+ class?: string;
38
+ }
39
+
40
+ const {
41
+ tags,
42
+ title,
43
+ allHref,
44
+ allLabel = "All",
45
+ allActive = false,
46
+ ariaLabel = "Filter by tag",
47
+ class: className,
48
+ } = Astro.props;
49
+ const basePath = Astro.locals.basePath ?? "";
50
+
51
+ const chip = (t: TagItem) => ({
52
+ href: withBase(basePath, t.href),
53
+ active: !!t.active,
54
+ });
55
+ ---
56
+
57
+ <nav class:list={["tag-cloud-wrap", className]} aria-label={ariaLabel}>
58
+ {title && <p class="tag-cloud__title">{title}</p>}
59
+ <ul class="tag-cloud" role="list">
60
+ {
61
+ allHref && (
62
+ <li>
63
+ <a
64
+ class:list={["tag-chip", allActive && "is-active"]}
65
+ href={withBase(basePath, allHref)}
66
+ aria-current={allActive ? "page" : undefined}
67
+ >
68
+ {allLabel}
69
+ </a>
70
+ </li>
71
+ )
72
+ }
73
+ {
74
+ tags.map((t) => (
75
+ <li>
76
+ <a
77
+ class:list={["tag-chip", chip(t).active && "is-active"]}
78
+ href={chip(t).href}
79
+ aria-current={chip(t).active ? "page" : undefined}
80
+ >
81
+ {t.label}
82
+ {typeof t.count === "number" && <span class="tag-chip__count">{t.count}</span>}
83
+ </a>
84
+ </li>
85
+ ))
86
+ }
87
+ </ul>
88
+ </nav>
89
+
90
+ <style>
91
+ /* Mirrors the raw-chrome `.tag-cloud` / `.tag-chip` block in
92
+ public/shared/commerce-chrome.css — same token vocabulary, same look. */
93
+ .tag-cloud__title {
94
+ font-family: var(--font-display);
95
+ font-weight: 700;
96
+ font-size: 0.72rem;
97
+ letter-spacing: 0.1em;
98
+ text-transform: uppercase;
99
+ color: var(--color-muted);
100
+ margin-bottom: 0.6rem;
101
+ }
102
+ .tag-cloud {
103
+ display: flex;
104
+ flex-wrap: wrap;
105
+ gap: 8px;
106
+ }
107
+ .tag-chip {
108
+ display: inline-flex;
109
+ align-items: center;
110
+ gap: 6px;
111
+ padding: 6px 14px;
112
+ border: 1px solid var(--color-border);
113
+ border-radius: var(--radius-full);
114
+ background: var(--color-surface);
115
+ color: var(--color-text);
116
+ font-size: 0.85rem;
117
+ font-weight: 600;
118
+ transition:
119
+ background 0.15s ease,
120
+ color 0.15s ease,
121
+ border-color 0.15s ease;
122
+ }
123
+ .tag-chip:hover {
124
+ border-color: var(--color-primary);
125
+ color: var(--color-primary);
126
+ }
127
+ .tag-chip.is-active {
128
+ background: var(--color-primary);
129
+ border-color: var(--color-primary);
130
+ color: var(--color-primary-contrast);
131
+ }
132
+ .tag-chip__count {
133
+ font-size: 0.72rem;
134
+ font-weight: 700;
135
+ color: var(--color-muted);
136
+ }
137
+ .tag-chip.is-active .tag-chip__count {
138
+ color: color-mix(in srgb, var(--color-primary-contrast) 78%, transparent);
139
+ }
140
+ .tag-chip:focus-visible {
141
+ outline: 2px solid var(--color-primary);
142
+ outline-offset: 2px;
143
+ }
144
+ @media (prefers-reduced-motion: reduce) {
145
+ .tag-chip {
146
+ transition: none;
147
+ }
148
+ }
149
+ </style>
@@ -0,0 +1,37 @@
1
+ ---
2
+ /**
3
+ * Adult-signature-on-delivery notice. Regulated nicotine/vapor deliveries
4
+ * require an of-age adult to sign; the wording is fixed here and the age
5
+ * reflects tenant.compliance.minAge (defaults to 21). Renders NOTHING when
6
+ * `enabled` is false. Driven by tenant.compliance.adultSignature — never hand-
7
+ * authored on a marketing page (decision compliance-widgets-platform-owned).
8
+ */
9
+ import ComplianceNotice from "./ComplianceNotice.astro";
10
+
11
+ interface Props {
12
+ /** From tenant.compliance.adultSignature. */
13
+ enabled?: boolean;
14
+ /** From tenant.compliance.minAge (defaults to 21). */
15
+ minAge?: number;
16
+ variant?: "bar" | "block" | "inline" | "footer";
17
+ }
18
+
19
+ const { enabled = false, minAge = 21, variant = "inline" } = Astro.props;
20
+
21
+ const TEXT =
22
+ `Adult signature required: someone ${minAge} or older must be present to sign ` +
23
+ `for this delivery.`;
24
+ ---
25
+
26
+ {
27
+ enabled && (
28
+ <ComplianceNotice
29
+ variant={variant}
30
+ tone="neutral"
31
+ hook="adult-signature"
32
+ label="Adult signature on delivery notice"
33
+ >
34
+ {TEXT}
35
+ </ComplianceNotice>
36
+ )
37
+ }
@@ -0,0 +1,99 @@
1
+ ---
2
+ /**
3
+ * Shared presentational chrome for the platform compliance-notice widgets
4
+ * (PACT Act, adult signature, Prop 65, purchase limits — and the pattern the
5
+ * FDA NicotineWarning follows). It owns ONLY the chrome: the fixed legal text
6
+ * is supplied by each sibling widget via <slot />, and every sibling renders it
7
+ * ONLY when its tenant.compliance data is present. This component adds no copy
8
+ * of its own — it never fabricates legal wording (decision
9
+ * compliance-widgets-platform-owned).
10
+ *
11
+ * Variants — one visual treatment per placement, token-styled so they re-skin
12
+ * with the theme and never carry a hardcoded brand color:
13
+ * - "bar" persistent full-width strip (top-of-page compliance band)
14
+ * - "block" reinforced bordered box (PDP / cart / dedicated notice)
15
+ * - "inline" compact inline note (beside a control or price)
16
+ * - "footer" footer-hub row (matches the shipping-restriction footer band)
17
+ *
18
+ * Tone drives the emphasis color from the contract token vocab only. Every
19
+ * arbitrary-value class is written as a STATIC literal in the lookup maps below
20
+ * so the Tailwind compiler emits it (dynamic interpolation would be dropped) —
21
+ * same pattern as Badge.astro.
22
+ */
23
+ type Variant = "bar" | "block" | "inline" | "footer";
24
+ type Tone = "warning" | "info" | "neutral";
25
+
26
+ interface Props {
27
+ variant?: Variant;
28
+ tone?: Tone;
29
+ /** data-compliance hook value, e.g. "pact-act". */
30
+ hook: string;
31
+ /** Accessible label for the note landmark. */
32
+ label: string;
33
+ /** Optional leading glyph (decorative), e.g. the ⚠ Prop 65 symbol. */
34
+ symbol?: string;
35
+ }
36
+
37
+ const { variant = "block", tone = "neutral", hook, label, symbol } = Astro.props;
38
+
39
+ // Static class literals — see the note above on Tailwind JIT.
40
+ const TONE_BORDER: Record<Tone, string> = {
41
+ warning: "border-[var(--color-warning)]",
42
+ info: "border-[var(--color-info)]",
43
+ neutral: "border-[var(--color-text)]",
44
+ };
45
+ const TONE_TEXT: Record<Tone, string> = {
46
+ warning: "text-[var(--color-warning)]",
47
+ info: "text-[var(--color-info)]",
48
+ neutral: "text-[var(--color-text)]",
49
+ };
50
+ ---
51
+
52
+ {
53
+ variant === "bar" ? (
54
+ <div
55
+ class="border-b border-[var(--color-border)] bg-[var(--color-surface-2)]"
56
+ role="note"
57
+ aria-label={label}
58
+ data-compliance={hook}
59
+ >
60
+ <p class={`container-prose py-2 text-center text-[0.72rem] font-semibold uppercase tracking-[0.05em] sm:text-xs ${TONE_TEXT[tone]}`}>
61
+ {symbol && <span aria-hidden="true" class="mr-1">{symbol}</span>}
62
+ <slot />
63
+ </p>
64
+ </div>
65
+ ) : variant === "footer" ? (
66
+ <div class="border-t border-[var(--color-border)] bg-[var(--color-bg)]" data-compliance={hook}>
67
+ <p
68
+ class="container-prose py-3 text-center text-xs text-[var(--color-muted)]"
69
+ role="note"
70
+ aria-label={label}
71
+ >
72
+ {symbol && <span aria-hidden="true" class="mr-1">{symbol}</span>}
73
+ <slot />
74
+ </p>
75
+ </div>
76
+ ) : variant === "inline" ? (
77
+ <span
78
+ class="inline-flex items-center gap-1.5 text-xs font-medium text-[var(--color-muted)]"
79
+ role="note"
80
+ aria-label={label}
81
+ data-compliance={hook}
82
+ >
83
+ {symbol && <span aria-hidden="true">{symbol}</span>}
84
+ <slot />
85
+ </span>
86
+ ) : (
87
+ <div
88
+ class={`rounded-[var(--radius-md)] border-2 px-4 py-3 ${TONE_BORDER[tone]}`}
89
+ role="note"
90
+ aria-label={label}
91
+ data-compliance={hook}
92
+ >
93
+ <p class="text-sm font-semibold text-[var(--color-text)]">
94
+ {symbol && <span aria-hidden="true" class="mr-1.5">{symbol}</span>}
95
+ <slot />
96
+ </p>
97
+ </div>
98
+ )
99
+ }
@@ -0,0 +1,45 @@
1
+ ---
2
+ /**
3
+ * Excise-tax DISPLAY cue. Presentational only — it surfaces that applicable
4
+ * excise taxes apply (and, optionally, a per-jurisdiction breakdown for shopper
5
+ * transparency); it NEVER computes tax. The actual excise-tax CALCULATION is
6
+ * product/commerce-owned (decision compliance-widgets-platform-owned). Renders
7
+ * NOTHING when no note is supplied.
8
+ *
9
+ * The optional jurisdiction breakdown is rendered as inline "label detail"
10
+ * spans (valid inside the primitive's paragraph) so it re-skins with the theme
11
+ * and never fabricates a figure the tenant didn't provide.
12
+ */
13
+ import type { ExciseTaxConfig } from "@tot/public-runtime";
14
+ import ComplianceNotice from "./ComplianceNotice.astro";
15
+
16
+ interface Props {
17
+ /** From tenant.compliance.exciseTax. */
18
+ config?: ExciseTaxConfig;
19
+ variant?: "bar" | "block" | "inline" | "footer";
20
+ }
21
+
22
+ const { config, variant = "inline" } = Astro.props;
23
+
24
+ const note = config?.note?.trim();
25
+ const jurisdictions = (config?.jurisdictions ?? []).filter((j) => j && j.label && j.detail);
26
+ ---
27
+
28
+ {
29
+ note && (
30
+ <ComplianceNotice
31
+ variant={variant}
32
+ tone="info"
33
+ hook="excise-tax"
34
+ label="Excise tax notice"
35
+ >
36
+ {note}
37
+ {jurisdictions.length > 0 &&
38
+ jurisdictions.map((j) => (
39
+ <span class="ml-1.5 whitespace-nowrap text-[var(--color-muted)]">
40
+ · <span class="font-semibold">{j.label}</span> {j.detail}
41
+ </span>
42
+ ))}
43
+ </ComplianceNotice>
44
+ )
45
+ }
@@ -0,0 +1,33 @@
1
+ ---
2
+ /**
3
+ * FDA-mandated nicotine warning (21 CFR 1143.3). The wording is fixed by law and
4
+ * must be conspicuous — do not paraphrase. Two presentations:
5
+ * - "bar": a persistent high-contrast strip at the very top of every page.
6
+ * - "block": a reinforced box on the PDP (regulation also requires it on the
7
+ * product's own page/ads).
8
+ * Rendered only when the tenant's compliance.nicotineWarning is set.
9
+ */
10
+ interface Props {
11
+ variant?: "bar" | "block";
12
+ }
13
+ const { variant = "bar" } = Astro.props;
14
+
15
+ const TEXT =
16
+ "WARNING: This product contains nicotine. Nicotine is an addictive chemical.";
17
+ ---
18
+
19
+ {
20
+ variant === "bar" ? (
21
+ <div class="border-b-2 border-[var(--color-text)] bg-[var(--color-text)] text-[var(--color-bg)]" role="note" aria-label="Health warning" data-compliance="nicotine-warning">
22
+ <p class="container-prose py-2 text-center text-[0.72rem] font-semibold uppercase tracking-[0.06em] sm:text-xs">
23
+ {TEXT}
24
+ </p>
25
+ </div>
26
+ ) : (
27
+ <div class="rounded-[var(--radius-md)] border-2 border-[var(--color-text)] px-4 py-3" role="note" aria-label="Health warning" data-compliance="nicotine-warning">
28
+ <p class="text-sm font-semibold uppercase tracking-[0.04em] text-[var(--color-text)]">
29
+ {TEXT}
30
+ </p>
31
+ </div>
32
+ )
33
+ }
@@ -0,0 +1,35 @@
1
+ ---
2
+ /**
3
+ * PACT Act (Prevent All Cigarette Trafficking Act, extended to ENDS/vapor in
4
+ * 2021) compliance notice. The federal requirements it summarizes are fixed and
5
+ * authored here — age verification, adult signature on delivery, and no U.S.
6
+ * Postal Service carriage; only whether it renders (and the age) comes from
7
+ * tenant.compliance.pactAct / .minAge. Renders NOTHING when `enabled` is false,
8
+ * so absent data never shows placeholder legal text. Never hand-author this on a
9
+ * marketing page (decision compliance-widgets-platform-owned).
10
+ */
11
+ import ComplianceNotice from "./ComplianceNotice.astro";
12
+
13
+ interface Props {
14
+ /** From tenant.compliance.pactAct. */
15
+ enabled?: boolean;
16
+ /** From tenant.compliance.minAge (defaults to the PACT Act floor, 21). */
17
+ minAge?: number;
18
+ variant?: "bar" | "block" | "inline" | "footer";
19
+ }
20
+
21
+ const { enabled = false, minAge = 21, variant = "block" } = Astro.props;
22
+
23
+ const TEXT =
24
+ `In compliance with the federal PACT Act, all vapor and nicotine orders require ` +
25
+ `age verification (${minAge}+) and an adult signature on delivery. These products ` +
26
+ `cannot be shipped through the U.S. Postal Service.`;
27
+ ---
28
+
29
+ {
30
+ enabled && (
31
+ <ComplianceNotice variant={variant} tone="info" hook="pact-act" label="PACT Act shipping compliance notice">
32
+ {TEXT}
33
+ </ComplianceNotice>
34
+ )
35
+ }
@@ -0,0 +1,80 @@
1
+ ---
2
+ /**
3
+ * California Prop 65 warning. The safe-harbor warning FORMAT is mandated (Cal.
4
+ * Code Regs. tit. 27 § 25601+): this component owns the fixed skeleton
5
+ * ("WARNING: … known to the State of California to cause …"), the ⚠ symbol, and
6
+ * the official warnings URL. Only the variable slots — harm endpoint(s) and the
7
+ * chemical name(s) named in the long form — come from tenant.compliance.prop65.
8
+ * Renders NOTHING when the config is absent, so it never fabricates a warning
9
+ * (decision compliance-widgets-platform-owned).
10
+ *
11
+ * `true` → short cancer + reproductive-harm warning. A Prop65Config selects the
12
+ * harm endpoint(s); providing `chemicals` switches to the long form.
13
+ */
14
+ import type { Prop65Config } from "@tot/public-runtime";
15
+ import ComplianceNotice from "./ComplianceNotice.astro";
16
+
17
+ interface Props {
18
+ /** From tenant.compliance.prop65. */
19
+ config?: Prop65Config | boolean;
20
+ variant?: "bar" | "block" | "inline" | "footer";
21
+ }
22
+
23
+ const { config, variant = "block" } = Astro.props;
24
+
25
+ // Normalize: `true` is the common cancer + reproductive short warning.
26
+ const cfg: Prop65Config | null =
27
+ config === true ? { harm: "cancer-and-reproductive" } : config || null;
28
+
29
+ const DEFAULT_URL = "www.P65Warnings.ca.gov";
30
+
31
+ // Mandated harm clauses (long form) and short-form labels — fixed wording.
32
+ const HARM_CLAUSE: Record<Prop65Config["harm"], string> = {
33
+ cancer: "cancer",
34
+ reproductive: "birth defects or other reproductive harm",
35
+ "cancer-and-reproductive": "cancer and birth defects or other reproductive harm",
36
+ };
37
+ const HARM_SHORT: Record<Prop65Config["harm"], string> = {
38
+ cancer: "Cancer",
39
+ reproductive: "Reproductive Harm",
40
+ "cancer-and-reproductive": "Cancer and Reproductive Harm",
41
+ };
42
+
43
+ const url = cfg?.url ?? DEFAULT_URL;
44
+ const href = /^https?:\/\//i.test(url) ? url : `https://${url}`;
45
+
46
+ const chemicals = cfg?.chemicals?.filter(Boolean) ?? [];
47
+ const isLongForm = chemicals.length > 0;
48
+ const chemicalList =
49
+ chemicals.length <= 1
50
+ ? chemicals[0] ?? ""
51
+ : `${chemicals.slice(0, -1).join(", ")} and ${chemicals[chemicals.length - 1]}`;
52
+ const verb = chemicals.length > 1 ? "are" : "is";
53
+
54
+ // Text up to the URL; the URL renders as a link in the slot.
55
+ const lead =
56
+ cfg && isLongForm
57
+ ? `WARNING: This product can expose you to chemicals including ${chemicalList}, ` +
58
+ `which ${verb} known to the State of California to cause ${HARM_CLAUSE[cfg.harm]}. ` +
59
+ `For more information, go to `
60
+ : cfg
61
+ ? `WARNING: ${HARM_SHORT[cfg.harm]} — `
62
+ : "";
63
+ ---
64
+
65
+ {
66
+ cfg && (
67
+ <ComplianceNotice
68
+ variant={variant}
69
+ tone="warning"
70
+ hook="prop65"
71
+ label="California Proposition 65 warning"
72
+ symbol="⚠"
73
+ >
74
+ {lead}
75
+ <a href={href} rel="noopener" class="underline underline-offset-2">
76
+ {url}
77
+ </a>
78
+ </ComplianceNotice>
79
+ )
80
+ }
@@ -0,0 +1,38 @@
1
+ ---
2
+ /**
3
+ * Purchase-quantity-limit DISPLAY. Surfaces a shopper-facing limit (e.g. state
4
+ * or PACT-Act order caps). Display surface ONLY — the limit text comes from
5
+ * tenant.compliance.purchaseLimit and the actual enforcement stays in the
6
+ * product/commerce layer, never hand-built here (decision
7
+ * compliance-widgets-platform-owned). Renders NOTHING when the config (or its
8
+ * text) is absent.
9
+ */
10
+ import type { PurchaseLimitConfig } from "@tot/public-runtime";
11
+ import ComplianceNotice from "./ComplianceNotice.astro";
12
+
13
+ interface Props {
14
+ /** From tenant.compliance.purchaseLimit. */
15
+ config?: PurchaseLimitConfig;
16
+ variant?: "bar" | "block" | "inline" | "footer";
17
+ }
18
+
19
+ const { config, variant = "inline" } = Astro.props;
20
+ const text = config?.text?.trim();
21
+ const reason = config?.reason?.trim();
22
+
23
+ const label = `Purchase limit: ${text ?? ""}`;
24
+ ---
25
+
26
+ {
27
+ text && (
28
+ <ComplianceNotice
29
+ variant={variant}
30
+ tone="neutral"
31
+ hook="purchase-limit"
32
+ label={label}
33
+ >
34
+ Purchase limit: {text}
35
+ {reason && <span> ({reason})</span>}
36
+ </ComplianceNotice>
37
+ )
38
+ }
@@ -0,0 +1,35 @@
1
+ ---
2
+ /**
3
+ * Shipping-restriction DISPLAY notice (e.g. PACT Act no-ship states, adult
4
+ * signature, carrier limits). Presentational only — it renders the tenant's
5
+ * configured `compliance.shippingRestriction` text; the actual ship/no-ship
6
+ * DECISION for an address stays in the product/commerce layer (decision
7
+ * compliance-widgets-platform-owned). Renders NOTHING when `text` is absent.
8
+ *
9
+ * This is the platform component behind the footer shipping band the app Layout
10
+ * previously inlined — same data, same footer placement, now token-styled via
11
+ * the shared ComplianceNotice primitive so it re-skins with the theme.
12
+ */
13
+ import ComplianceNotice from "./ComplianceNotice.astro";
14
+
15
+ interface Props {
16
+ /** From tenant.compliance.shippingRestriction. Display text only. */
17
+ text?: string;
18
+ variant?: "bar" | "block" | "inline" | "footer";
19
+ }
20
+
21
+ const { text, variant = "footer" } = Astro.props;
22
+ ---
23
+
24
+ {
25
+ text && (
26
+ <ComplianceNotice
27
+ variant={variant}
28
+ tone="neutral"
29
+ hook="shipping-restriction"
30
+ label="Shipping restriction notice"
31
+ >
32
+ {text}
33
+ </ComplianceNotice>
34
+ )
35
+ }