@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,229 @@
1
+ ---
2
+ /**
3
+ * Preview/dev-only SHARED RAW-CHROME STYLE GUIDE —
4
+ * `/_style-guide/<tenant>/chrome/<theme-id>/` (unit F4).
5
+ *
6
+ * Renders the platform-shipped raw chrome set — the `/shared/commerce-chrome.css`
7
+ * class vocabulary (announcement + utility bar, header + search + actions, primary
8
+ * nav + mega panels, brand/category tiles, compliance strip, age gate, footer) —
9
+ * re-skinned under ONE tenant theme's component tokens. Swap the theme in the URL
10
+ * and the whole chrome re-skins; the CSS never forks. This is the visual counterpart
11
+ * to the style-concept-audit `chromeConcept` gate: the guide shows the shared chrome,
12
+ * the gate proves no tenant re-authors it. Per decision raw-chrome-single-stylesheet.
13
+ *
14
+ * Deliberately links ONLY the theme token sheet + the shared chrome sheet — NOT the
15
+ * tenant's own mkt.css — so what renders is exactly the platform chrome, isolated
16
+ * from any per-tenant CSS. The token re-skin uses the same component-var mechanism
17
+ * as the component guide ([theme].astro): reference tokens overridden by the
18
+ * descriptor's preview.componentVars, emitted on a scoping wrapper.
19
+ * noindex, never in the sitemap, never production publishing.
20
+ */
21
+ const { tenant, theme } = Astro.params;
22
+ const cspNonce = Astro.locals.cspNonce;
23
+
24
+ // Tenant-local theme descriptors. From src/pages/style-guide/[tenant]/chrome/ the
25
+ // repo root is seven levels up.
26
+ const themeModules = import.meta.glob<Record<string, any>>(
27
+ "../../../../../../../tenants/*/content/themes/*.json",
28
+ { eager: true, import: "default" },
29
+ );
30
+ const seg = `/tenants/${tenant}/content/themes/`;
31
+ const t = Object.entries(themeModules)
32
+ .filter(([path]) => path.includes(seg))
33
+ .map(([, mod]) => mod)
34
+ .find((m) => m.id === theme);
35
+
36
+ if (!t) return new Response(null, { status: 404 });
37
+
38
+ // Reference component tokens (the "Northwind" pilot identity), overridden per theme
39
+ // by the descriptor's preview.componentVars — the same reskin mechanism the component
40
+ // guide uses. Fonts are intentionally omitted so the theme stylesheet's --font-display
41
+ // (inherited from :root) drives type. Emitted on the .sg-page wrapper so the shared
42
+ // chrome inherits them.
43
+ const REFERENCE_COMPONENT_VARS: Record<string, string> = {
44
+ "--color-bg": "#f4f1ea", "--color-surface": "#fcfaf5", "--color-surface-2": "#efe9dc",
45
+ "--color-text": "#1c2624", "--color-muted": "#5c6663",
46
+ "--color-primary": "#1f3a34", "--color-primary-hover": "#16302b", "--color-primary-contrast": "#f4f1ea",
47
+ // --color-accent darkened from #b5612f (L 0.174) to #9a5228 (L 0.130) — the lighter
48
+ // value read below 4.5:1 against both --color-primary-contrast (as an accent-fill
49
+ // background) and --color-surface (as accent-colored text), which axe caught on any
50
+ // tenant relying on this fallback instead of its own theme override.
51
+ "--color-accent": "#9a5228", "--color-accent-hover": "#804421", "--color-border": "#dcd6c9",
52
+ "--color-success": "#3f6b4f", "--color-sale": "#9e3b2e", "--color-error": "#9e3b2e",
53
+ "--radius-sm": "3px", "--radius-md": "6px", "--radius-lg": "12px", "--radius-full": "999px",
54
+ "--shadow-sm": "0 1px 2px rgba(28,38,36,.06)", "--shadow-md": "0 6px 24px -8px rgba(28,38,36,.14)",
55
+ "--shadow-lg": "0 24px 60px -20px rgba(28,38,36,.22)", "--container-max": "1240px",
56
+ };
57
+ const componentVars: Record<string, string> = {
58
+ ...REFERENCE_COMPONENT_VARS,
59
+ ...((t.preview?.componentVars ?? {}) as Record<string, string>),
60
+ };
61
+ const componentVarsInline = Object.entries(componentVars)
62
+ .map(([k, v]) => `${k}:${v}`)
63
+ .join(";");
64
+ ---
65
+
66
+ <!doctype html>
67
+ <html lang="en">
68
+ <head>
69
+ <meta charset="utf-8" />
70
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
71
+ <meta name="robots" content="noindex, nofollow" />
72
+ <link rel="icon" type="image/png" href={`/tenants/${tenant}/favicon.png`} />
73
+ <title>{t.name} — shared chrome (preview)</title>
74
+ {/* The theme token sheet (fonts + any :root tokens) + the shared chrome sheet.
75
+ NO tenant mkt.css — the chrome renders purely from the platform stylesheet. */}
76
+ <link rel="stylesheet" href={t.stylesheet} />
77
+ <link rel="stylesheet" href="/shared/commerce-chrome.css" />
78
+ {/* Guide-only chrome + preview affordances (distinct .sg-* names). The mega panel
79
+ + age gate are shown OPEN in normal flow so reviewers see them without hover or
80
+ the fixed overlay covering the page. Every styled selector is authored in this
81
+ component, so component-scoped styles apply. */}
82
+ <style nonce={cspNonce}>
83
+ .sg-bar { position: sticky; top: 0; z-index: 500; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; background: #0f1729; color: #e6edf6; padding: 12px 20px; font: 600 .95rem -apple-system, sans-serif; }
84
+ .sg-bar .flag { font-size: .78rem; letter-spacing: .1em; text-transform: uppercase; background: #f59e0b; color: #221600; font-weight: 800; padding: 4px 8px; border-radius: 5px; }
85
+ .sg-bar .role { font-size: .78rem; text-transform: uppercase; letter-spacing: .08em; background: #0e9f8e; color: #00201b; font-weight: 800; padding: 4px 8px; border-radius: 999px; }
86
+ .sg-bar a { color: #bfe0ff; }
87
+ .sg-bar a.right { margin-left: auto; }
88
+ .sg-page { background: var(--color-bg); color: var(--color-text); min-height: 100vh; padding-bottom: 40px; }
89
+ .sg-lbl { max-width: var(--container-max, 1240px); margin: 26px auto 6px; padding: 0 20px; font: 800 .78rem/1.3 -apple-system, sans-serif; letter-spacing: .12em; text-transform: uppercase; color: var(--color-primary); }
90
+ .sg-frame { max-width: var(--container-max, 1240px); margin: 0 auto 6px; padding: 0 20px; }
91
+ .sg-frame > * { border: 1px dashed var(--color-border); border-radius: 8px; }
92
+ /* Show the demoed mega panel + age gate open, in normal flow, for review. */
93
+ .sg-open .mega { position: static; opacity: 1; visibility: visible; transform: none; box-shadow: var(--shadow-md); margin-top: 6px; }
94
+ .sg-agegate-frame .age-gate { position: static; display: flex; inset: auto; background: color-mix(in srgb, var(--color-text) 10%, transparent); border-radius: 8px; }
95
+ .sg-note { max-width: var(--container-max, 1240px); margin: 8px auto 0; padding: 16px 20px 0; border-top: 1px solid var(--color-border); font-size: .82rem; color: var(--color-muted); }
96
+ </style>
97
+ </head>
98
+ <body>
99
+ <!-- A toolbar, not page content — a real <header> here would duplicate
100
+ the demoed .site-header's own banner landmark (axe
101
+ landmark-no-duplicate-banner/landmark-unique). -->
102
+ <div class="sg-bar">
103
+ <span class="flag">Preview · shared chrome · noindex</span>
104
+ <span class="role">{t.role}</span>
105
+ <b>{t.name}</b>
106
+ <a class="right" href={`/_style-guide/${tenant}/guide/${t.id}/`}>widget library guide →</a>
107
+ <a href={`/_style-guide/${tenant}/${t.id}/`}>← component guide</a>
108
+ </div>
109
+
110
+ <main class="sg-page" style={componentVarsInline}>
111
+ <h1 class="sg-lbl">Shared chrome preview — {t.name}</h1>
112
+ <p class="sg-lbl">Announcement + top utility bar</p>
113
+ <div class="sg-frame">
114
+ <div>
115
+ <div class="topbar-warning"><div class="container"><p>WARNING: This product contains nicotine. Nicotine is an addictive chemical.</p></div></div>
116
+ <div class="announce"><div class="container">Free US shipping on orders over $100</div></div>
117
+ <div class="announce-marquee"><div class="announce-marquee__track"><span>Clearance event — up to 40% off</span><span>Clearance event — up to 40% off</span></div></div>
118
+ <div class="announce-bar"><div class="container"><span class="announce-bar__msg">Members save 10% every day — <a href="#">join free</a></span><button class="announce-bar__close" aria-label="Dismiss">×</button></div></div>
119
+ <nav class="utility-nav" aria-label="Utility"><div class="container">
120
+ <ul><li><a href="#">News</a></li><li><a href="#">About</a></li><li><a href="#">Contact</a></li></ul>
121
+ <div class="utility-nav__right"><a class="member-cue" href="#">Member pricing</a><span class="sel">USD $</span></div>
122
+ </div></nav>
123
+ </div>
124
+ </div>
125
+
126
+ <p class="sg-lbl">Header — brand · search · actions · primary nav + mega panel</p>
127
+ <div class="sg-frame">
128
+ <header class="site-header sg-open">
129
+ <div class="container header-row">
130
+ <a class="brand" href="#"><span class="b-prime">Prime</span><span class="b-vapor">Vapor</span><span class="b-club">Club</span></a>
131
+ <div class="header-search">
132
+ <input type="search" placeholder="Search products…" aria-label="Search" />
133
+ <button aria-label="Search"><svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"></circle><path d="M21 21l-4-4"></path></svg></button>
134
+ </div>
135
+ <div class="header-actions">
136
+ <a href="#" aria-label="Account"><svg viewBox="0 0 24 24"><circle cx="12" cy="8" r="4"></circle><path d="M4 21c0-4 4-6 8-6s8 2 8 6"></path></svg></a>
137
+ <a class="cart" href="#" aria-label="Cart"><svg viewBox="0 0 24 24"><path d="M3 3h2l2 13h11l2-8H7"></path></svg><span class="count">2</span></a>
138
+ </div>
139
+ <button class="hamburger" aria-label="Menu"><span></span><span></span><span></span></button>
140
+ </div>
141
+ <div class="primary-nav-bar"><div class="container">
142
+ <nav class="primary-nav" aria-label="Primary"><ul>
143
+ <li><a class="is-flag" href="#">New<span class="new-badge">NEW</span></a></li>
144
+ <li class="nav-item">
145
+ <a href="#">Shop <span class="caret">▾</span></a>
146
+ <div class="mega mega--wide"><div class="mega__cols">
147
+ <div class="mega__col"><h4>Category</h4><a href="#">Disposables</a><a href="#">E-Liquid</a><a href="#">Devices</a></div>
148
+ <div class="mega__col"><h4>Featured</h4><a href="#">Best Sellers</a><a href="#">On Sale</a></div>
149
+ </div></div>
150
+ </li>
151
+ <li><a href="#">Coils &amp; Parts</a></li>
152
+ <li><a href="#">On Sale</a></li>
153
+ </ul></nav>
154
+ </div></div>
155
+ </header>
156
+ </div>
157
+
158
+ <p class="sg-lbl">Brand tiles + category cards</p>
159
+ <div class="sg-frame">
160
+ <section class="section container">
161
+ <div class="sec-head"><span class="eyebrow">Shop by</span><h2>Top brands</h2></div>
162
+ <div class="tile-grid">
163
+ <a class="tile" href="#"><span>Northwind</span></a>
164
+ <a class="tile" href="#"><span>Summit Co.</span></a>
165
+ <a class="tile" href="#"><span>Cascade</span></a>
166
+ <a class="tile" href="#"><span>Harbor</span></a>
167
+ </div>
168
+ <div class="cat-grid" style="margin-top:20px">
169
+ <a class="cat-card" href="#"><h3>Disposables</h3></a>
170
+ <a class="cat-card" href="#"><h3>E-Liquid</h3></a>
171
+ <a class="cat-card" href="#"><h3>Devices</h3></a>
172
+ </div>
173
+ </section>
174
+ </div>
175
+
176
+ <p class="sg-lbl">Compliance strip</p>
177
+ <div class="sg-frame">
178
+ <div class="compliance-strip"><div class="container"><p>Must be 21+ to purchase. Products intended for adults of legal smoking age only.</p></div></div>
179
+ </div>
180
+
181
+ <p class="sg-lbl">Age gate (21+ interstitial) — shown open for review</p>
182
+ <div class="sg-frame sg-agegate-frame">
183
+ <div class="age-gate is-open">
184
+ <div class="age-gate__panel">
185
+ <p class="age-gate__mark"><span class="is-red">Prime</span>VaporClub</p>
186
+ <h2>Are you 21 or older?</h2>
187
+ <p class="age-gate__lede">You must be of legal smoking age to enter this store.</p>
188
+ <div class="age-gate__actions">
189
+ <a class="btn btn-primary age-gate__enter" href="#">Yes, I'm 21+</a>
190
+ <a class="btn btn-outline" href="#">No</a>
191
+ </div>
192
+ <p class="age-gate__fine">By entering you agree you are of legal age to purchase these products.</p>
193
+ </div>
194
+ </div>
195
+ </div>
196
+
197
+ <p class="sg-lbl">Footer — columns · newsletter · payments · trust marks</p>
198
+ <div class="sg-frame">
199
+ <footer class="site-footer">
200
+ <div class="container">
201
+ <div class="footer-grid">
202
+ <div class="footer-brand">
203
+ <a class="brand" href="#"><span class="b-prime">Prime</span><span class="b-vapor">Vapor</span><span class="b-club">Club</span></a>
204
+ <p>Regulated-commerce chrome, one shared stylesheet, re-skinned per tenant theme.</p>
205
+ <div class="footer-news"><div class="footer-news__row"><input type="email" placeholder="Email address" aria-label="Email" /><button>Join</button></div>
206
+ <label class="footer-attest"><input type="checkbox" /> <span>I confirm I am 21 or older.</span></label>
207
+ </div>
208
+ <div class="pay-badges"><span>Visa</span><span>Mastercard</span><span>Amex</span><span>PayPal</span></div>
209
+ </div>
210
+ <div class="footer-col"><h4>Shop</h4><ul><li><a href="#">Disposables</a></li><li><a href="#">E-Liquid</a></li><li><a href="#">Devices</a></li></ul></div>
211
+ <div class="footer-col"><h4>Support</h4><ul><li><a href="#">Contact</a></li><li><a href="#">Shipping</a></li><li><a href="#">Returns</a></li></ul></div>
212
+ <div class="footer-col"><h4>Company</h4><ul><li><a href="#">About</a></li><li><a href="#">Blog</a></li></ul></div>
213
+ </div>
214
+ <div class="trust-marks"><span>🔒 Secure checkout</span><span>✓ Age-verified</span><span>★ 4.9 / 5</span></div>
215
+ <div class="footer-warning"><strong>WARNING:</strong><p>This product contains nicotine. Nicotine is an addictive chemical.</p></div>
216
+ <div class="footer-bottom"><p>© Sample Store. All rights reserved.</p><p><a href="#">Privacy</a> · <a href="#">Terms</a></p></div>
217
+ </div>
218
+ </footer>
219
+ </div>
220
+
221
+ <p class="sg-note">
222
+ Every element above is styled solely by <code>/shared/commerce-chrome.css</code> under the
223
+ <b>{t.name}</b> theme's tokens — no tenant chrome CSS is linked here. The
224
+ <code>chromeConcept</code> gate (style-concept-audit + verify-tenant.sh) fails any tenant
225
+ that re-authors or inlines these selectors instead of linking the shared sheet.
226
+ </p>
227
+ </main>
228
+ </body>
229
+ </html>
@@ -0,0 +1,144 @@
1
+ ---
2
+ /**
3
+ * Preview/dev-only WIDGET LIBRARY GUIDE —
4
+ * `/_style-guide/<tenant>/guide/<theme-id>/`.
5
+ *
6
+ * Renders `docs/widget-library-guide.md` (the source of truth) as HTML via
7
+ * Astro's native markdown pipeline, inside the tenant's theme so the prose reads
8
+ * under real tokens. The .md file is imported (never duplicated here) so the doc
9
+ * has ONE maintained home. A persistent left sidebar (StyleGuideNav) makes the
10
+ * guide reachable in one click from the tenant hub and links across the other
11
+ * style-guide surfaces. noindex, never in the sitemap, never production publishing.
12
+ */
13
+ import "@/styles/global.css";
14
+ import type { MarkdownInstance } from "astro";
15
+ import StyleGuideNav from "@/components/style-guide/StyleGuideNav.astro";
16
+
17
+ const { tenant, theme } = Astro.params;
18
+ const cspNonce = Astro.locals.cspNonce;
19
+
20
+ // Tenant-local theme descriptors. From src/pages/style-guide/[tenant]/guide/ the
21
+ // repo root is seven levels up (same depth as the chrome guide).
22
+ const themeModules = import.meta.glob<Record<string, any>>(
23
+ "../../../../../../../tenants/*/content/themes/*.json",
24
+ { eager: true, import: "default" },
25
+ );
26
+ const seg = `/tenants/${tenant}/content/themes/`;
27
+ const t = Object.entries(themeModules)
28
+ .filter(([path]) => path.includes(seg))
29
+ .map(([, mod]) => mod)
30
+ .find((m) => m.id === theme);
31
+
32
+ if (!t) return new Response(null, { status: 404 });
33
+
34
+ // The guide markdown — the single maintained source of truth (repo-root docs/).
35
+ // Imported through Astro's markdown pipeline so we render its <Content /> and
36
+ // pull its heading outline for the sidebar TOC; the prose is never copied here.
37
+ const guideModules = import.meta.glob<MarkdownInstance<Record<string, unknown>>>(
38
+ "../../../../../../../docs/widget-library-guide.md",
39
+ { eager: true },
40
+ );
41
+ const guide = Object.values(guideModules)[0];
42
+ if (!guide) return new Response(null, { status: 404 });
43
+ const { Content } = guide;
44
+ const headings = guide.getHeadings();
45
+
46
+ // Same reskin mechanism as the component/chrome guides: reference tokens (the
47
+ // Northwind pilot identity) overridden by the descriptor's preview.componentVars,
48
+ // emitted on the themed content wrapper so the prose reads under THIS theme.
49
+ const REFERENCE_COMPONENT_VARS: Record<string, string> = {
50
+ "--color-bg": "#f4f1ea", "--color-surface": "#fcfaf5", "--color-surface-2": "#ece7db",
51
+ "--color-text": "#1c2624", "--color-muted": "#5c6663",
52
+ "--color-primary": "#1f3a34", "--color-primary-contrast": "#f4f1ea",
53
+ "--color-accent": "#b5612f", "--color-accent-ink": "#8a481f", "--color-border": "#dcd6c9",
54
+ "--color-success": "#3f6b4f", "--color-sale": "#9e3b2e",
55
+ "--radius-sm": "3px", "--radius-md": "6px", "--radius-lg": "12px", "--radius-full": "999px",
56
+ "--shadow-sm": "0 1px 2px rgba(28,38,36,.06)", "--shadow-md": "0 6px 24px -8px rgba(28,38,36,.14)",
57
+ "--shadow-lg": "0 24px 60px -20px rgba(28,38,36,.22)", "--container-max": "1240px",
58
+ };
59
+ const componentVars: Record<string, string> = {
60
+ ...REFERENCE_COMPONENT_VARS,
61
+ ...((t.preview?.componentVars ?? {}) as Record<string, string>),
62
+ };
63
+ const componentVarsInline = Object.entries(componentVars)
64
+ .map(([k, v]) => `${k}:${v}`)
65
+ .join(";");
66
+ ---
67
+
68
+ <!doctype html>
69
+ <html lang="en">
70
+ <head>
71
+ <meta charset="utf-8" />
72
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
73
+ <meta name="robots" content="noindex, nofollow" />
74
+ <link rel="icon" type="image/png" href={`/tenants/${tenant}/favicon.png`} />
75
+ <title>Widget library guide — {t.name} (preview)</title>
76
+ {/* The tenant's theme sheet so the prose reads under real brand tokens. */}
77
+ <link rel="stylesheet" href={t.stylesheet} />
78
+ <style nonce={cspNonce}>
79
+ body { margin: 0; }
80
+ .sg-bar { position: sticky; top: 0; z-index: 200; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; background: #0f1729; color: #e6edf6; padding: 12px 20px; font: 600 .95rem -apple-system, sans-serif; }
81
+ .sg-bar .flag { font-size: .78rem; letter-spacing: .1em; text-transform: uppercase; background: #f59e0b; color: #221600; font-weight: 800; padding: 4px 8px; border-radius: 5px; }
82
+ .sg-bar .role { font-size: .78rem; text-transform: uppercase; letter-spacing: .08em; background: #0e9f8e; color: #00201b; font-weight: 800; padding: 4px 8px; border-radius: 999px; }
83
+ .sg-bar a { color: #bfe0ff; display: inline-flex; align-items: center; min-height: 44px; text-decoration: none; }
84
+ .sg-bar a.right { margin-left: auto; }
85
+
86
+ /* Two-column docs layout: persistent left rail + themed prose. */
87
+ .sg-shell { display: grid; grid-template-columns: 264px minmax(0, 1fr); align-items: start; }
88
+ .sg-side { position: sticky; top: 0; align-self: start; max-height: 100vh; overflow-y: auto; background: #0f1729; color: #e6edf6; padding: 22px 14px 40px; }
89
+
90
+ /* Left-nav (StyleGuideNav markup) — styled here so the component stays CSP-clean. */
91
+ .sg-nav__eyebrow { font: 800 .72rem/1.2 -apple-system, sans-serif; letter-spacing: .14em; text-transform: uppercase; color: #7fe3d4; margin: 0 0 12px; padding: 0 10px; }
92
+ .sg-nav__list { list-style: none; margin: 0; padding: 0; }
93
+ .sg-nav__list li { margin: 0; }
94
+ .sg-nav__link { display: block; padding: 9px 10px; border-radius: 8px; color: #bfe0ff; text-decoration: none; font-size: .92rem; min-height: 40px; }
95
+ .sg-nav__link:hover { background: #16223c; }
96
+ .sg-nav__link.is-active { background: #16223c; color: #fff; font-weight: 700; }
97
+ .sg-nav__link--sub { padding-left: 20px; font-size: .88rem; }
98
+ .sg-nav__toc { list-style: none; margin: 2px 0 8px; padding: 0; border-left: 1px solid #26365a; margin-left: 16px; }
99
+ .sg-nav__toc-link { display: block; padding: 5px 10px 5px 14px; color: #9fb0c8; font-size: .82rem; text-decoration: none; line-height: 1.3; }
100
+ .sg-nav__toc-link:hover { color: #e6edf6; }
101
+
102
+ /* Themed prose column. .rte styles come from global.css; token vars are
103
+ supplied by componentVarsInline on this wrapper so they always resolve. */
104
+ .sg-content { background: var(--color-bg); color: var(--color-text); min-height: 100vh; padding: 34px clamp(20px, 4vw, 56px) 96px; }
105
+ .sg-content .rte { max-width: 900px; }
106
+ .sg-content h1 { font-size: clamp(1.9rem, 4vw, 2.7rem); line-height: 1.1; margin: 0 0 10px; color: var(--color-text); }
107
+ .sg-content .rte h2 { border-top: 1px solid var(--color-border); padding-top: 1.4em; scroll-margin-top: 68px; }
108
+ .sg-content .rte h3 { scroll-margin-top: 68px; }
109
+ .sg-content blockquote { border-left: 3px solid var(--color-accent); margin: 1em 0; padding: 2px 0 2px 16px; color: var(--color-muted); }
110
+ .sg-content pre { background: var(--color-surface-2); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 14px 16px; overflow-x: auto; font-family: var(--font-mono, ui-monospace, Menlo, monospace); font-size: .84rem; line-height: 1.5; }
111
+ .sg-content pre code { background: none; padding: 0; }
112
+ .sg-content code { font-family: var(--font-mono, ui-monospace, Menlo, monospace); font-size: .88em; }
113
+ .sg-content :not(pre) > code { background: var(--color-surface-2); padding: 1px 5px; border-radius: 4px; }
114
+ .sg-content table { display: block; width: 100%; overflow-x: auto; border-collapse: collapse; }
115
+ .sg-content th, .sg-content td { border: 1px solid var(--color-border); padding: 6px 10px; text-align: left; font-size: .9rem; }
116
+
117
+ @media (max-width: 900px) {
118
+ .sg-shell { grid-template-columns: 1fr; }
119
+ .sg-side { position: static; max-height: none; }
120
+ }
121
+ </style>
122
+ </head>
123
+ <body>
124
+ <header class="sg-bar">
125
+ <span class="flag">Preview · widget library · noindex</span>
126
+ <span class="role">{t.role}</span>
127
+ <b>{t.name}</b>
128
+ <a class="right" href={`/_style-guide/${tenant}/${t.id}/`}>← component guide</a>
129
+ <a href={`/_style-guide/${tenant}/`}>tenant hub →</a>
130
+ </header>
131
+
132
+ <div class="sg-shell">
133
+ <aside class="sg-side">
134
+ <StyleGuideNav tenant={tenant!} theme={t.id} active="guide" headings={headings} />
135
+ </aside>
136
+
137
+ <main class="sg-content" style={componentVarsInline}>
138
+ <article class="rte">
139
+ <Content />
140
+ </article>
141
+ </main>
142
+ </div>
143
+ </body>
144
+ </html>
@@ -0,0 +1,116 @@
1
+ ---
2
+ /**
3
+ * Preview/dev-only STYLE-GUIDE HUB for a tenant — `/_style-guide/<tenant>/`.
4
+ *
5
+ * Lists every theme guide (current / candidate / legacy) so reviewers can open
6
+ * them side-by-side WITHOUT reskinning the live marketing site. A persistent
7
+ * left sidebar (StyleGuideNav) puts the Widget Library Guide one click away.
8
+ * This is a review surface only: noindex, never in the sitemap, never part of
9
+ * production marketing publishing. Tenant-agnostic route; theme data is
10
+ * tenant-local (tenants/<tenant>/content/themes/*.json).
11
+ */
12
+ import StyleGuideNav from "@/components/style-guide/StyleGuideNav.astro";
13
+
14
+ const { tenant } = Astro.params;
15
+ const cspNonce = Astro.locals.cspNonce;
16
+
17
+ // Tenant-local theme descriptors. Colocated repo-root layout: from
18
+ // src/pages/style-guide/[tenant]/ the repo root is six levels up.
19
+ const themeModules = import.meta.glob<Record<string, any>>(
20
+ "../../../../../../tenants/*/content/themes/*.json",
21
+ { eager: true, import: "default" },
22
+ );
23
+ const seg = `/tenants/${tenant}/content/themes/`;
24
+ const themes = Object.entries(themeModules)
25
+ .filter(([path]) => path.includes(seg))
26
+ .map(([, mod]) => mod);
27
+
28
+ if (themes.length === 0) return new Response(null, { status: 404 });
29
+
30
+ const ROLE_ORDER: Record<string, number> = { current: 0, candidate: 1, legacy: 2 };
31
+ themes.sort(
32
+ (a, b) => (ROLE_ORDER[a.role] ?? 9) - (ROLE_ORDER[b.role] ?? 9),
33
+ );
34
+ // The current (or first) theme skins the persistent sidebar's per-theme links.
35
+ const navTheme = themes[0]?.id;
36
+ ---
37
+
38
+ <!doctype html>
39
+ <html lang="en">
40
+ <head>
41
+ <meta charset="utf-8" />
42
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
43
+ <meta name="robots" content="noindex, nofollow" />
44
+ <link rel="icon" type="image/png" href={`/tenants/${tenant}/favicon.png`} />
45
+ <title>Style guides — {tenant} (preview)</title>
46
+ <style nonce={cspNonce}>
47
+ :root { color-scheme: light; }
48
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; margin: 0; background: #0f1729; color: #e6edf6; line-height: 1.55; }
49
+ .sg-shell { display: grid; grid-template-columns: 264px minmax(0, 1fr); align-items: start; }
50
+ .sg-side { position: sticky; top: 0; align-self: start; max-height: 100vh; overflow-y: auto; padding: 22px 14px 40px; border-right: 1px solid #1c2a44; }
51
+ .sg-nav__eyebrow { font: 800 .72rem/1.2 -apple-system, sans-serif; letter-spacing: .14em; text-transform: uppercase; color: #7fe3d4; margin: 0 0 12px; padding: 0 10px; }
52
+ .sg-nav__list { list-style: none; margin: 0; padding: 0; }
53
+ .sg-nav__list li { margin: 0; }
54
+ .sg-nav__link { display: block; padding: 9px 10px; border-radius: 8px; color: #bfe0ff; text-decoration: none; font-size: .92rem; min-height: 40px; }
55
+ .sg-nav__link:hover { background: #16223c; }
56
+ .sg-nav__link.is-active { background: #16223c; color: #fff; font-weight: 700; }
57
+ .sg-nav__link--sub { padding-left: 20px; font-size: .88rem; }
58
+ .sg-nav__toc { list-style: none; margin: 2px 0 8px; padding: 0; }
59
+ .sg-nav__toc-link { display: block; padding: 4px 10px 4px 26px; color: #9fb0c8; font-size: .82rem; text-decoration: none; }
60
+ .wrap { max-width: 860px; margin: 0 auto; padding: 40px 24px 80px; }
61
+ .flag { display: inline-block; font-size: .72rem; letter-spacing: .1em; text-transform: uppercase; background: #f59e0b; color: #221600; font-weight: 800; padding: 4px 10px; border-radius: 6px; }
62
+ h1 { font-size: 1.9rem; margin: 18px 0 6px; }
63
+ .sub { color: #9fb0c8; margin: 0 0 32px; }
64
+ .card { display: block; text-decoration: none; color: inherit; background: #16223c; border: 1px solid #24324f; border-radius: 14px; padding: 20px 22px; margin-bottom: 6px; transition: border-color .15s, transform .15s; }
65
+ .card:hover { border-color: #3f5680; transform: translateY(-2px); }
66
+ .card .row { display: flex; align-items: center; gap: 12px; }
67
+ .badge { font-size: .68rem; letter-spacing: .08em; text-transform: uppercase; font-weight: 800; padding: 3px 9px; border-radius: 999px; }
68
+ .badge.current { background: #0e9f8e; color: #001f1a; }
69
+ .badge.candidate { background: #6f7dff; color: #060a26; }
70
+ .badge.legacy { background: #47536e; color: #dfe6f2; }
71
+ .status { margin-left: auto; font-size: .78rem; color: #9fb0c8; font-family: ui-monospace, Menlo, monospace; }
72
+ .name { font-weight: 700; font-size: 1.05rem; }
73
+ .desc { color: #9fb0c8; font-size: .9rem; margin-top: 8px; }
74
+ .sub-links { display: flex; gap: 16px; flex-wrap: wrap; margin: 8px 0 16px 4px; }
75
+ .sub-links a { color: #bfe0ff; font-size: .82rem; text-decoration: none; display: inline-flex; align-items: center; min-height: 32px; }
76
+ .sub-links a:hover { text-decoration: underline; }
77
+ .note { margin-top: 28px; font-size: .82rem; color: #7f8fab; border-top: 1px solid #24324f; padding-top: 16px; }
78
+ @media (max-width: 900px) { .sg-shell { grid-template-columns: 1fr; } .sg-side { position: static; max-height: none; border-right: 0; border-bottom: 1px solid #1c2a44; } }
79
+ </style>
80
+ </head>
81
+ <body>
82
+ <div class="sg-shell">
83
+ <aside class="sg-side">
84
+ <StyleGuideNav tenant={tenant!} theme={navTheme} active="tenant" />
85
+ </aside>
86
+ <main class="wrap">
87
+ <span class="flag">Preview · dev only · noindex</span>
88
+ <p style="margin:14px 0 0"><a href="/_style-guide/" style="color:#bfe0ff;font-size:.85rem;text-decoration:none">← all clients</a></p>
89
+ <h1>{tenant} — theme style guides</h1>
90
+ <p class="sub">Open any guide independently to compare looks side-by-side. Nothing here reskins the live site. The <b>Widget Library Guide</b> (left nav) documents every shared component.</p>
91
+
92
+ {themes.map((t) => (
93
+ <div>
94
+ <a class="card" href={`/_style-guide/${tenant}/${t.id}/`}>
95
+ <div class="row">
96
+ <span class:list={["badge", t.role]}>{t.role}</span>
97
+ <span class="name">{t.name}</span>
98
+ <span class="status">{t.status}</span>
99
+ </div>
100
+ {t.description && <p class="desc">{t.description}</p>}
101
+ </a>
102
+ <div class="sub-links">
103
+ <a href={`/_style-guide/${tenant}/chrome/${t.id}/`}>shared chrome preview →</a>
104
+ <a href={`/_style-guide/${tenant}/guide/${t.id}/`}>widget library guide →</a>
105
+ </div>
106
+ </div>
107
+ ))}
108
+
109
+ <p class="note">
110
+ These are preview/dev review surfaces — excluded from the sitemap and production marketing
111
+ publishing. Approve the <b>current</b> guide before propagating its style concept across the site.
112
+ </p>
113
+ </main>
114
+ </div>
115
+ </body>
116
+ </html>
@@ -0,0 +1,98 @@
1
+ ---
2
+ /**
3
+ * Preview/dev-only STYLE-GUIDE ROOT INDEX — `/_style-guide/`.
4
+ *
5
+ * Lists EVERY tenant and each of its theme guides, so all clients' guides are
6
+ * discoverable from one place. Review surface only: noindex, never in the
7
+ * sitemap, never part of production marketing publishing. Tenant-agnostic —
8
+ * theme data is tenant-local (tenants/<tenant>/content/themes/*.json).
9
+ */
10
+ const cspNonce = Astro.locals.cspNonce;
11
+
12
+ // All tenant-local theme descriptors, grouped by tenant. Colocated repo-root
13
+ // layout: from src/pages/style-guide/ the repo root is five levels up.
14
+ const themeModules = import.meta.glob<Record<string, any>>(
15
+ "../../../../../tenants/*/content/themes/*.json",
16
+ { eager: true, import: "default" },
17
+ );
18
+
19
+ const ROLE_ORDER: Record<string, number> = { current: 0, candidate: 1, legacy: 2 };
20
+ const byTenant = new Map<string, any[]>();
21
+ for (const [path, mod] of Object.entries(themeModules)) {
22
+ const m = /\/tenants\/([^/]+)\/content\/themes\//.exec(path);
23
+ const tenant = m?.[1];
24
+ if (!tenant) continue;
25
+ if (!byTenant.has(tenant)) byTenant.set(tenant, []);
26
+ byTenant.get(tenant)!.push(mod);
27
+ }
28
+ const tenants = [...byTenant.entries()]
29
+ .map(([tenant, themes]) => ({
30
+ tenant,
31
+ themes: themes.sort((a, b) => (ROLE_ORDER[a.role] ?? 9) - (ROLE_ORDER[b.role] ?? 9)),
32
+ }))
33
+ .sort((a, b) => a.tenant.localeCompare(b.tenant));
34
+ ---
35
+
36
+ <!doctype html>
37
+ <html lang="en">
38
+ <head>
39
+ <meta charset="utf-8" />
40
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
41
+ <meta name="robots" content="noindex, nofollow" />
42
+ <title>Style guides — all clients (preview)</title>
43
+ <style nonce={cspNonce}>
44
+ :root { color-scheme: light; }
45
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; margin: 0; background: #0f1729; color: #e6edf6; line-height: 1.55; }
46
+ .wrap { max-width: 920px; margin: 0 auto; padding: 48px 24px 80px; }
47
+ .flag { display: inline-block; font-size: .72rem; letter-spacing: .1em; text-transform: uppercase; background: #f59e0b; color: #221600; font-weight: 800; padding: 4px 10px; border-radius: 6px; }
48
+ h1 { font-size: 1.9rem; margin: 18px 0 6px; }
49
+ .sub { color: #9fb0c8; margin: 0 0 32px; }
50
+ .tenant { margin-bottom: 30px; }
51
+ .tenant > h2 { font-size: 1.05rem; margin: 0 0 12px; display: flex; align-items: baseline; gap: 10px; }
52
+ .tenant > h2 a { color: #bfe0ff; font-size: .82rem; font-weight: 600; text-decoration: none; }
53
+ .card { display: block; text-decoration: none; color: inherit; background: #16223c; border: 1px solid #24324f; border-radius: 14px; padding: 16px 20px; margin-bottom: 12px; transition: border-color .15s, transform .15s; }
54
+ .card:hover { border-color: #3f5680; transform: translateY(-2px); }
55
+ .row { display: flex; align-items: center; gap: 12px; }
56
+ .badge { font-size: .68rem; letter-spacing: .08em; text-transform: uppercase; font-weight: 800; padding: 3px 9px; border-radius: 999px; }
57
+ .badge.current { background: #0e9f8e; color: #001f1a; }
58
+ .badge.candidate { background: #6f7dff; color: #060a26; }
59
+ .badge.legacy { background: #47536e; color: #dfe6f2; }
60
+ .status { margin-left: auto; font-size: .78rem; color: #9fb0c8; font-family: ui-monospace, Menlo, monospace; }
61
+ .name { font-weight: 700; font-size: 1.02rem; }
62
+ .desc { color: #9fb0c8; font-size: .88rem; margin-top: 6px; }
63
+ .note { margin-top: 28px; font-size: .82rem; color: #7f8fab; border-top: 1px solid #24324f; padding-top: 16px; }
64
+ </style>
65
+ </head>
66
+ <body>
67
+ <div class="wrap">
68
+ <span class="flag">Preview · dev only · noindex</span>
69
+ <h1>Client style guides</h1>
70
+ <p class="sub">Every tenant and its theme guides. Open a guide to preview the standard components and page sections re-skinned under that theme. Nothing here re-skins a live site.</p>
71
+
72
+ {tenants.map(({ tenant, themes }) => (
73
+ <section class="tenant">
74
+ <h2>
75
+ {tenant}
76
+ <a href={`/_style-guide/${tenant}/`}>tenant hub →</a>
77
+ {themes[0]?.id && <a href={`/_style-guide/${tenant}/guide/${themes[0].id}/`}>widget library guide →</a>}
78
+ </h2>
79
+ {themes.map((t) => (
80
+ <a class="card" href={`/_style-guide/${tenant}/${t.id}/`}>
81
+ <div class="row">
82
+ <span class:list={["badge", t.role]}>{t.role}</span>
83
+ <span class="name">{t.name}</span>
84
+ <span class="status">{t.status}</span>
85
+ </div>
86
+ {t.description && <p class="desc">{t.description}</p>}
87
+ </a>
88
+ ))}
89
+ </section>
90
+ ))}
91
+
92
+ <p class="note">
93
+ These are preview/dev review surfaces — excluded from the sitemap and production marketing
94
+ publishing. Approve a tenant's <b>current</b> guide before propagating its style concept across the site.
95
+ </p>
96
+ </div>
97
+ </body>
98
+ </html>