@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,234 @@
1
+ ---
2
+ /**
3
+ * <SiteHeader> — logo/wordmark + search island + account/auth + cart-count
4
+ * affordance, with the primary <MegaMenu> nav below and a mobile drawer (the
5
+ * <MobileNav> island). Tenant-agnostic; theme-token skinned. This is the
6
+ * CANONICAL site header (unit I4) — the former `components/nav/Header.astro`
7
+ * app-Layout implementation is retired; every feature it had (search typeahead,
8
+ * mobile drawer with focus-trap/scroll-lock, the session-aware auth entry point,
9
+ * the saved-items/wishlist cue, marketing header CTAs, a dark header variant)
10
+ * lives here now, as explicit props — the caller (Layout.astro) resolves
11
+ * session/feature/theme state and passes it down; this component stays pure.
12
+ *
13
+ * COMMERCE BOUNDARY: the cart is a LINK only (cart/checkout is FoxyCart, Phase 2)
14
+ * — no cart state, no add-to-cart. The count is a static affordance the FoxyCart
15
+ * layer hydrates later.
16
+ */
17
+ import type { ChromeNavItem, ChromeLogo, ChromeCta } from "@/lib/chrome/model";
18
+ import MegaMenu from "@/components/chrome/MegaMenu.astro";
19
+ import SearchTypeahead from "@/components/islands/SearchTypeahead.tsx";
20
+ import MobileNav from "@/components/islands/MobileNav.tsx";
21
+ import { withBase } from "@/lib/basePath";
22
+
23
+ interface Props {
24
+ logo?: ChromeLogo;
25
+ /** Fallback wordmark text when no logo image. */
26
+ wordmark?: string;
27
+ nav?: ChromeNavItem[];
28
+ /** Search form action (GET). Omit to hide search. Mounts the live SearchTypeahead island. */
29
+ searchAction?: string;
30
+ /** Announcement text surfaced inside the mobile drawer (the desktop banner is a Layout-level sibling, e.g. <AnnouncementBar>). */
31
+ announcement?: string;
32
+ /** Generic account-icon link (no session awareness). Prefer `authLink` for a real session-aware entry point — passing both renders two affordances. */
33
+ accountHref?: string;
34
+ /** Session-aware auth entry point ("Sign in" / "Dashboard"), resolved by the caller. */
35
+ authLink?: { label: string; href: string };
36
+ /** Saved-items (wishlist) affordance. Renders a heart icon with a `[data-wishlist-count]` badge a page-level script hydrates. Omit to hide. */
37
+ savedItemsHref?: string;
38
+ /** Marketing conversion CTAs (non-commerce tenants), shown alongside search/saved. */
39
+ headerCtas?: ChromeCta[];
40
+ /** Cart link target (FoxyCart, Phase 2). Omit to hide the cart affordance. */
41
+ cartHref?: string;
42
+ cartCount?: number;
43
+ /** A brand-fixed "dark" header bar (e.g. a navy bar with a light logo) vs. the default light bar. */
44
+ variant?: "light" | "dark";
45
+ sticky?: boolean;
46
+ class?: string;
47
+ }
48
+
49
+ const {
50
+ logo,
51
+ wordmark,
52
+ nav = [],
53
+ searchAction,
54
+ announcement,
55
+ accountHref,
56
+ authLink,
57
+ savedItemsHref,
58
+ headerCtas = [],
59
+ cartHref,
60
+ cartCount = 0,
61
+ variant = "light",
62
+ sticky = true,
63
+ class: className,
64
+ } = Astro.props;
65
+
66
+ const { basePath } = Astro.locals;
67
+ const homeHref = withBase(basePath, "/");
68
+ const showLogo = !!logo && /^(https?:|\/)/.test(logo.src);
69
+ const dark = variant === "dark";
70
+
71
+ const ctaClass = (v?: string) =>
72
+ v === "secondary" || v === "ghost"
73
+ ? "inline-flex items-center rounded-full border border-current px-4 py-2 font-display text-sm font-medium transition-opacity hover:opacity-80"
74
+ : "inline-flex items-center rounded-full bg-accent px-4 py-2 font-display text-sm font-medium text-[var(--color-primary-contrast)] transition-transform hover:-translate-y-0.5";
75
+
76
+ // Nav handed to the (client-rendered) MobileNav island with hrefs pre-prefixed,
77
+ // so it doesn't depend on the runtime base path inside the island. MobileNav's
78
+ // item shape is flat (label/href/children) — a mega item's columns are
79
+ // flattened into one children list (no group headers) since no real content
80
+ // source produces `columns` today; NavDropdown's accordion mode is the richer
81
+ // zero-JS alternative if per-column grouping is ever needed on mobile.
82
+ const mobileNav = nav.map((item) => ({
83
+ label: item.label,
84
+ href: withBase(basePath, item.href),
85
+ children: (item.children ?? item.columns?.flatMap((c) => c.links))?.map((child) => ({
86
+ label: child.label,
87
+ href: withBase(basePath, child.href),
88
+ })),
89
+ }));
90
+ ---
91
+
92
+ <header class:list={[sticky && "sticky top-0 z-40", className]}>
93
+ <div
94
+ class:list={[
95
+ "border-b backdrop-blur",
96
+ dark
97
+ ? "border-[color-mix(in_srgb,var(--color-primary-contrast)_18%,var(--color-primary))] bg-primary text-primary-contrast"
98
+ : "border-border bg-surface/95 text-text",
99
+ ]}
100
+ >
101
+ <div class="container-prose flex h-16 items-center gap-4">
102
+ <!-- Logo / wordmark -->
103
+ <a
104
+ href={homeHref}
105
+ class="flex shrink-0 items-center transition-opacity hover:opacity-80"
106
+ aria-label={logo?.alt ?? wordmark ?? "Home"}
107
+ >
108
+ {
109
+ showLogo ? (
110
+ <img
111
+ src={logo!.src}
112
+ alt={logo!.alt}
113
+ width={logo!.width ?? 160}
114
+ height={logo!.height ?? 48}
115
+ class="h-9 w-auto max-w-[180px] object-contain"
116
+ />
117
+ ) : (
118
+ <span class="font-display text-lg font-bold tracking-tight">
119
+ {wordmark ?? logo?.alt ?? "Home"}
120
+ </span>
121
+ )
122
+ }
123
+ </a>
124
+
125
+ <!-- Search (live typeahead island) -->
126
+ {
127
+ searchAction && (
128
+ <div class="hidden flex-1 md:flex">
129
+ <SearchTypeahead client:idle action={searchAction} />
130
+ </div>
131
+ )
132
+ }
133
+
134
+ <!-- Actions: saved items + marketing CTAs + auth + account + cart (link only) + mobile menu -->
135
+ <div class="ml-auto flex items-center gap-1">
136
+ {
137
+ savedItemsHref && (
138
+ <a
139
+ href={withBase(basePath, savedItemsHref)}
140
+ aria-label="Saved items"
141
+ class="relative grid h-9 w-9 shrink-0 place-items-center rounded-full text-text transition-colors hover:text-accent"
142
+ >
143
+ <svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" aria-hidden="true">
144
+ <path d="M12 21s-7.5-4.6-10-9.2C.4 8.6 2 5 5.5 5 8 5 9.4 6.6 12 9c2.6-2.4 4-4 6.5-4C22 5 23.6 8.6 22 11.8 19.5 16.4 12 21 12 21z" />
145
+ </svg>
146
+ <span
147
+ data-wishlist-count
148
+ hidden
149
+ class="absolute -right-0.5 -top-0.5 grid h-4 min-w-4 place-items-center rounded-full bg-[var(--color-sale)] px-1 font-mono text-[0.6rem] font-medium text-[var(--color-primary-contrast)]"
150
+ />
151
+ </a>
152
+ )
153
+ }
154
+ {
155
+ headerCtas.length > 0 && (
156
+ <div class="hidden items-center gap-2 sm:flex">
157
+ {headerCtas.map((cta) => (
158
+ <a href={withBase(basePath, cta.href)} class={ctaClass(cta.variant)}>
159
+ {cta.label}
160
+ </a>
161
+ ))}
162
+ </div>
163
+ )
164
+ }
165
+ {
166
+ authLink && (
167
+ <a
168
+ href={authLink.href}
169
+ class="inline-flex shrink-0 items-center rounded-full border border-current px-4 py-2 font-display text-sm font-medium transition-opacity hover:opacity-80"
170
+ >
171
+ {authLink.label}
172
+ </a>
173
+ )
174
+ }
175
+ {
176
+ accountHref && (
177
+ <a
178
+ href={withBase(basePath, accountHref)}
179
+ aria-label="Account"
180
+ class="grid h-9 w-9 place-items-center rounded-full text-text transition-colors hover:text-accent"
181
+ >
182
+ <svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" aria-hidden="true">
183
+ <circle cx="12" cy="8" r="4" />
184
+ <path d="M4 21c0-4.4 3.6-7 8-7s8 2.6 8 7" />
185
+ </svg>
186
+ </a>
187
+ )
188
+ }
189
+ {
190
+ cartHref && (
191
+ <a
192
+ href={withBase(basePath, cartHref)}
193
+ aria-label="Cart"
194
+ class="relative grid h-9 w-9 place-items-center rounded-full text-text transition-colors hover:text-accent"
195
+ >
196
+ <svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" aria-hidden="true">
197
+ <path d="M6 6h15l-1.5 9h-12z" />
198
+ <circle cx="9" cy="20" r="1.4" />
199
+ <circle cx="18" cy="20" r="1.4" />
200
+ <path d="M6 6L5 3H2" />
201
+ </svg>
202
+ <span
203
+ data-cart-count
204
+ class="absolute -right-0.5 -top-0.5 grid h-4 min-w-4 place-items-center rounded-full bg-[var(--color-sale)] px-1 font-mono text-[0.6rem] font-medium text-white"
205
+ >
206
+ {cartCount}
207
+ </span>
208
+ </a>
209
+ )
210
+ }
211
+
212
+ <!-- Mobile menu: the MobileNav island (focus-trap, scroll-lock, Escape-to-close) -->
213
+ {
214
+ nav.length > 0 && (
215
+ <div class="md:hidden">
216
+ <MobileNav client:idle nav={mobileNav} announcement={announcement} />
217
+ </div>
218
+ )
219
+ }
220
+ </div>
221
+ </div>
222
+ </div>
223
+
224
+ <!-- Primary nav bar -->
225
+ {
226
+ nav.length > 0 && (
227
+ <div class="border-b border-border bg-surface">
228
+ <div class="container-prose">
229
+ <MegaMenu items={nav} />
230
+ </div>
231
+ </div>
232
+ )
233
+ }
234
+ </header>
@@ -0,0 +1,79 @@
1
+ ---
2
+ /**
3
+ * <UtilityNav> — the thin secondary bar above the header: minor links (News,
4
+ * About, Contact…), a locale/currency indicator, and social icons. Tenant-
5
+ * agnostic; theme-token skinned. Hidden on small screens (it's a desktop
6
+ * convenience — the mobile menu carries these links).
7
+ */
8
+ import type { ChromeLink, SocialLink } from "@/lib/chrome/model";
9
+ import { withBase } from "@/lib/basePath";
10
+
11
+ interface Props {
12
+ links?: ChromeLink[];
13
+ locale?: string;
14
+ currency?: string;
15
+ social?: SocialLink[];
16
+ class?: string;
17
+ }
18
+
19
+ const { links = [], locale, currency, social = [], class: className } = Astro.props;
20
+ const { basePath } = Astro.locals;
21
+ const hrefOf = (l: ChromeLink) => (l.external ? l.href : withBase(basePath, l.href));
22
+ ---
23
+
24
+ <div
25
+ class:list={[
26
+ "hidden border-b border-border bg-surface text-muted md:block",
27
+ className,
28
+ ]}
29
+ >
30
+ <div class="container-prose flex h-9 items-center justify-between gap-4 text-[0.72rem]">
31
+ <ul class="flex items-center gap-5">
32
+ {
33
+ links.map((l) => (
34
+ <li>
35
+ <a
36
+ href={hrefOf(l)}
37
+ class="transition-colors hover:text-primary"
38
+ {...(l.external ? { rel: "noopener noreferrer" } : {})}
39
+ >
40
+ {l.label}
41
+ </a>
42
+ </li>
43
+ ))
44
+ }
45
+ </ul>
46
+
47
+ <div class="flex items-center gap-4">
48
+ {locale && <span class="font-mono uppercase tracking-[0.08em]">{locale}</span>}
49
+ {currency && <span class="font-mono">{currency}</span>}
50
+ {
51
+ social.length > 0 && (
52
+ <span class="flex items-center gap-3">
53
+ {social.map((s) => (
54
+ <a
55
+ href={s.href}
56
+ aria-label={s.label}
57
+ rel="noopener noreferrer"
58
+ class="transition-colors hover:text-primary"
59
+ >
60
+ {s.iconPath ? (
61
+ <svg
62
+ class="h-4 w-4"
63
+ viewBox="0 0 24 24"
64
+ fill="currentColor"
65
+ aria-hidden="true"
66
+ >
67
+ <path d={s.iconPath} />
68
+ </svg>
69
+ ) : (
70
+ <span>{s.label}</span>
71
+ )}
72
+ </a>
73
+ ))}
74
+ </span>
75
+ )
76
+ }
77
+ </div>
78
+ </div>
79
+ </div>
@@ -0,0 +1,242 @@
1
+ ---
2
+ /**
3
+ * <ArticleCard> — one blog/article card: featured image + category tag +
4
+ * title + excerpt + meta (author · date · read-time). Tenant-agnostic — the
5
+ * `article` is content, theme tokens are the skin. Replaces per-tenant
6
+ * hand-authored article tiles with one shared widget.
7
+ *
8
+ * Two delivery paths, one look: this component styles itself from the token
9
+ * contract via a scoped <style> (so it renders under the app Layout AND in the
10
+ * standalone style guide, neither of which links the raw sheet). The class-only
11
+ * raw-chrome parity lives in the `.article-grid` / `.article-card` / `.tag-cloud`
12
+ * block of public/shared/commerce-chrome.css, for tenants that ship hand-authored
13
+ * HTML through the rawMarketingHtml splice pipeline. Keep the two visually equal.
14
+ *
15
+ * Presentational only: renders whatever article metadata a tenant supplies (from
16
+ * imported/source-copied blog data) and NEVER fabricates authors, dates, or
17
+ * excerpts — omit a field and its row simply doesn't render.
18
+ */
19
+ import Img from "@/components/Img.astro";
20
+ import { withBase } from "@/lib/basePath";
21
+
22
+ export interface ArticleImage {
23
+ src: string;
24
+ alt?: string;
25
+ /** True when src is a rehosted asset (enables responsive srcset). */
26
+ rehosted?: boolean;
27
+ width?: number;
28
+ height?: number;
29
+ }
30
+
31
+ export interface Article {
32
+ /** Headline — the card link label. */
33
+ title: string;
34
+ /** Article URL. Base-prefixed here. */
35
+ href: string;
36
+ /** Short teaser/dek. Omit to render title-only. */
37
+ excerpt?: string;
38
+ /** Featured image. Omit to render the token-tinted placeholder band. */
39
+ image?: ArticleImage;
40
+ /** Category/tag label shown as an eyebrow chip. */
41
+ tag?: string;
42
+ /** Optional link for the tag chip (e.g. /blog/tag/<slug>). */
43
+ tagHref?: string;
44
+ /** Byline. */
45
+ author?: string;
46
+ /** Human-readable publish date, e.g. "May 12, 2026". */
47
+ date?: string;
48
+ /** Machine date for <time datetime> (ISO). */
49
+ dateTime?: string;
50
+ /** Estimated read time in minutes. */
51
+ readMinutes?: number;
52
+ }
53
+
54
+ interface Props {
55
+ article: Article;
56
+ /** Heading level for the title (default 3). */
57
+ as?: "h2" | "h3" | "h4";
58
+ class?: string;
59
+ }
60
+
61
+ const { article, as: Title = "h3", class: className } = Astro.props;
62
+ const basePath = Astro.locals.basePath ?? "";
63
+
64
+ const href = withBase(basePath, article.href);
65
+ const img = article.image;
66
+ const hasMeta = !!(article.author || article.date || article.readMinutes);
67
+ ---
68
+
69
+ <article class:list={["article-card", className]} data-reveal>
70
+ <div class="article-card__media">
71
+ {
72
+ img ? (
73
+ <Img
74
+ src={img.src}
75
+ rehosted={img.rehosted}
76
+ seed={article.href}
77
+ label={article.title}
78
+ alt={img.alt ?? ""}
79
+ width={img.width ?? 640}
80
+ height={img.height ?? 400}
81
+ sizes="(min-width: 900px) 360px, (min-width: 600px) 45vw, 92vw"
82
+ />
83
+ ) : (
84
+ <span class="article-card__ph" />
85
+ )
86
+ }
87
+ </div>
88
+ <div class="article-card__body">
89
+ {
90
+ article.tag &&
91
+ (article.tagHref ? (
92
+ <a class="article-card__tag" href={withBase(basePath, article.tagHref)}>
93
+ {article.tag}
94
+ </a>
95
+ ) : (
96
+ <span class="article-card__tag">{article.tag}</span>
97
+ ))
98
+ }
99
+ <Title class="article-card__title">
100
+ <a href={href}>{article.title}</a>
101
+ </Title>
102
+ {article.excerpt && <p class="article-card__excerpt">{article.excerpt}</p>}
103
+ {
104
+ hasMeta && (
105
+ <p class="article-card__meta">
106
+ {article.author && <span class="article-card__author">{article.author}</span>}
107
+ {article.date && (
108
+ <time datetime={article.dateTime ?? undefined}>{article.date}</time>
109
+ )}
110
+ {article.readMinutes && <span>{article.readMinutes} min read</span>}
111
+ </p>
112
+ )
113
+ }
114
+ </div>
115
+ </article>
116
+
117
+ <style>
118
+ /* Mirrors the raw-chrome `.article-card` block in
119
+ public/shared/commerce-chrome.css — same token vocabulary, same look. */
120
+ .article-card {
121
+ display: flex;
122
+ flex-direction: column;
123
+ overflow: hidden;
124
+ background: var(--color-surface);
125
+ border: 1px solid var(--color-border);
126
+ border-radius: var(--radius-lg);
127
+ transition:
128
+ transform 0.14s ease,
129
+ box-shadow 0.14s ease,
130
+ border-color 0.14s ease;
131
+ }
132
+ .article-card:hover {
133
+ transform: translateY(-3px);
134
+ box-shadow: var(--shadow-md);
135
+ border-color: var(--color-surface-2);
136
+ }
137
+ .article-card__media {
138
+ display: block;
139
+ aspect-ratio: 16 / 10;
140
+ overflow: hidden;
141
+ background: var(--color-surface-2);
142
+ }
143
+ .article-card__media :global(img) {
144
+ width: 100%;
145
+ height: 100%;
146
+ object-fit: cover;
147
+ transition: transform 0.3s ease;
148
+ }
149
+ .article-card:hover .article-card__media :global(img) {
150
+ transform: scale(1.04);
151
+ }
152
+ .article-card__ph {
153
+ display: block;
154
+ width: 100%;
155
+ height: 100%;
156
+ background: linear-gradient(
157
+ 135deg,
158
+ var(--color-surface-2),
159
+ color-mix(in srgb, var(--color-primary) 12%, var(--color-surface-2))
160
+ );
161
+ }
162
+ .article-card__body {
163
+ display: flex;
164
+ flex-direction: column;
165
+ gap: 0.5rem;
166
+ padding: clamp(16px, 2vw, 22px);
167
+ }
168
+ .article-card__tag {
169
+ align-self: flex-start;
170
+ font-family: var(--font-display);
171
+ font-weight: 700;
172
+ font-size: 0.68rem;
173
+ letter-spacing: 0.08em;
174
+ text-transform: uppercase;
175
+ color: var(--color-primary);
176
+ }
177
+ a.article-card__tag:hover {
178
+ text-decoration: underline;
179
+ }
180
+ .article-card__title {
181
+ font-family: var(--font-display);
182
+ font-weight: 700;
183
+ font-size: clamp(1.05rem, 1.6vw, 1.2rem);
184
+ line-height: 1.25;
185
+ color: var(--color-text);
186
+ }
187
+ .article-card__title a {
188
+ color: inherit;
189
+ }
190
+ /* Whole-card click target: the title link overlays the card. */
191
+ .article-card__title a::after {
192
+ content: "";
193
+ position: absolute;
194
+ inset: 0;
195
+ }
196
+ .article-card {
197
+ position: relative;
198
+ }
199
+ .article-card__title a:hover {
200
+ color: var(--color-primary);
201
+ }
202
+ .article-card__excerpt {
203
+ font-size: 0.92rem;
204
+ line-height: 1.5;
205
+ color: var(--color-muted);
206
+ display: -webkit-box;
207
+ -webkit-line-clamp: 3;
208
+ line-clamp: 3;
209
+ -webkit-box-orient: vertical;
210
+ overflow: hidden;
211
+ }
212
+ .article-card__meta {
213
+ display: flex;
214
+ flex-wrap: wrap;
215
+ align-items: center;
216
+ gap: 0.35rem 0.75rem;
217
+ margin-top: 0.15rem;
218
+ font-size: 0.78rem;
219
+ color: var(--color-muted);
220
+ }
221
+ /* Interpuncts between meta items. */
222
+ .article-card__meta > * + *::before {
223
+ content: "·";
224
+ margin-right: 0.75rem;
225
+ color: var(--color-border);
226
+ }
227
+ .article-card__author {
228
+ font-weight: 600;
229
+ color: var(--color-text);
230
+ }
231
+ /* The tag chip sits above the overlay title link so it stays clickable. */
232
+ a.article-card__tag {
233
+ position: relative;
234
+ z-index: 1;
235
+ }
236
+ @media (prefers-reduced-motion: reduce) {
237
+ .article-card,
238
+ .article-card__media :global(img) {
239
+ transition: none;
240
+ }
241
+ }
242
+ </style>