create-ailk 0.21.0 → 0.23.0

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 (342) hide show
  1. package/component-catalog.json +186 -8
  2. package/component-catalog.md +9 -7
  3. package/dist/add-module.js +20 -14
  4. package/dist/add-route.d.ts +55 -1
  5. package/dist/add-route.js +294 -12
  6. package/dist/cli.js +27 -10
  7. package/dist/derive-architecture.d.ts +12 -3
  8. package/dist/derive-architecture.js +20 -8
  9. package/dist/design-package.js +7 -2
  10. package/dist/lib/design-checks.js +4 -2
  11. package/dist/lib/env-catalog.js +5 -5
  12. package/dist/lib/npm-auth.d.ts +9 -0
  13. package/dist/lib/npm-auth.js +48 -0
  14. package/dist/lib/scaffold-render-sweep.d.ts +3 -2
  15. package/dist/lib/scaffold-render-sweep.js +5 -8
  16. package/dist/module-architecture.js +99 -9
  17. package/dist/paid-sections.d.ts +21 -2
  18. package/dist/paid-sections.js +69 -7
  19. package/dist/programmatic.js +19 -5
  20. package/dist/seed-flows.d.ts +4 -1
  21. package/dist/seed-flows.js +50 -4
  22. package/dist/site-chrome.d.ts +8 -0
  23. package/dist/site-chrome.js +17 -1
  24. package/dist/spoke-checkout.d.ts +65 -41
  25. package/dist/spoke-checkout.js +208 -430
  26. package/dist/starter-grid.d.ts +0 -3
  27. package/dist/starter-grid.js +24 -14
  28. package/dist/surfaces.js +52 -26
  29. package/dist/sync-routes.d.ts +38 -1
  30. package/dist/sync-routes.js +187 -13
  31. package/dist/template-record.d.ts +4 -3
  32. package/dist/template-record.js +8 -4
  33. package/dist/topology.js +25 -1
  34. package/dist/update.js +15 -0
  35. package/package.json +3 -3
  36. package/site-templates/_base/content/en/NewsletterSignup/newsletter.mdx +4 -49
  37. package/site-templates/_base/content/en/Pricing/pricing.mdx +3 -21
  38. package/site-templates/designs/minimalism/DESIGN.md +1 -1
  39. package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +23 -20
  40. package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +28 -14
  41. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +15 -39
  42. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +38 -34
  43. package/site-templates/marketing-site-free/content/en/NewsletterSignup/newsletter.mdx +6 -43
  44. package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.json +14 -31
  45. package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +26 -25
  46. package/site-templates/shapes/local-business-service/content/en/NewsletterSignup/newsletter.mdx +6 -43
  47. package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +14 -31
  48. package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +26 -25
  49. package/site-templates/shapes/organization-service/content/en/NewsletterSignup/newsletter.mdx +6 -43
  50. package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.json +14 -31
  51. package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.md +26 -25
  52. package/site-templates/shapes/person-service/content/en/NewsletterSignup/newsletter.mdx +6 -43
  53. package/templates/.env.example +9 -7
  54. package/templates/.github/workflows/ci.yml +13 -0
  55. package/templates/README.md +1 -0
  56. package/templates/_gitignore +4 -0
  57. package/templates/apps/api/.env.example +27 -0
  58. package/templates/apps/api/CLAUDE.md +87 -17
  59. package/templates/apps/api/package.json +1 -0
  60. package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +2 -0
  61. package/templates/apps/api/src/bin/stripe-connections-rekey.ts +45 -0
  62. package/templates/apps/api/src/config/__tests__/stripe-connection-key.test.ts +185 -0
  63. package/templates/apps/api/src/config/index.ts +109 -0
  64. package/templates/apps/api/src/lib/__mocks__/prisma.ts +3 -0
  65. package/templates/apps/api/src/lib/__tests__/consent-store.test.ts +19 -0
  66. package/templates/apps/api/src/lib/__tests__/flow-completion-store.test.ts +149 -0
  67. package/templates/apps/api/src/lib/__tests__/flow-rename-race.db.test.ts +116 -0
  68. package/templates/apps/api/src/lib/consent-store.ts +28 -0
  69. package/templates/apps/api/src/lib/error-serializer.ts +74 -7
  70. package/templates/apps/api/src/lib/flow-completion-store.ts +136 -0
  71. package/templates/apps/api/src/lib/hub-sign-in-store.ts +38 -2
  72. package/templates/apps/api/src/lib/proposal-paid-store.ts +21 -4
  73. package/templates/apps/api/src/lib/stripe.ts +74 -4
  74. package/templates/apps/api/src/lib/webhook-idempotency.ts +66 -2
  75. package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +14 -3
  76. package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +218 -0
  77. package/templates/apps/api/src/middleware/__tests__/write-budget.test.ts +174 -0
  78. package/templates/apps/api/src/middleware/auth.ts +13 -6
  79. package/templates/apps/api/src/middleware/rate-limit.ts +226 -4
  80. package/templates/apps/api/src/openapi/spec.ts +33 -3
  81. package/templates/apps/api/src/routes/auth/__tests__/hub-sign-in.test.ts +49 -0
  82. package/templates/apps/api/src/routes/auth/hub-sign-in.ts +41 -0
  83. package/templates/apps/api/src/routes/billing/plan-checkout.ts +325 -0
  84. package/templates/apps/api/src/routes/checkout/__tests__/live-key.test.ts +11 -0
  85. package/templates/apps/api/src/routes/checkout/__tests__/offerings-authorization.test.ts +1300 -0
  86. package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +11 -0
  87. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +49 -0
  88. package/templates/apps/api/src/routes/checkout/session.ts +57 -25
  89. package/templates/apps/api/src/routes/checkout/sessions.ts +232 -82
  90. package/templates/apps/api/src/routes/consent/__tests__/account.test.ts +386 -0
  91. package/templates/apps/api/src/routes/consent/__tests__/sign-up-rate-limit.test.ts +160 -0
  92. package/templates/apps/api/src/routes/consent/account.ts +268 -0
  93. package/templates/apps/api/src/routes/consent/index.ts +15 -7
  94. package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +36 -0
  95. package/templates/apps/api/src/routes/content/__tests__/offerings.test.ts +731 -0
  96. package/templates/apps/api/src/routes/content/__tests__/pricing.test.ts +753 -0
  97. package/templates/apps/api/src/routes/content/__tests__/shown-offerings.test.ts +762 -0
  98. package/templates/apps/api/src/routes/content/get-page.ts +4 -0
  99. package/templates/apps/api/src/routes/content/index.ts +16 -2
  100. package/templates/apps/api/src/routes/content/offerings.ts +184 -0
  101. package/templates/apps/api/src/routes/content/pricing.ts +176 -0
  102. package/templates/apps/api/src/routes/content/shown-offerings.ts +229 -0
  103. package/templates/apps/api/src/routes/leads/__tests__/magnet-completion.test.ts +515 -0
  104. package/templates/apps/api/src/routes/leads/index.ts +164 -7
  105. package/templates/apps/api/src/routes/webhooks/README.md +66 -0
  106. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-checkout-consent.test.ts +649 -0
  107. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-offerings.test.ts +814 -0
  108. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +80 -68
  109. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +5 -5
  110. package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +8 -2
  111. package/templates/apps/api/src/routes/webhooks/index.ts +23 -0
  112. package/templates/apps/api/src/routes/webhooks/stripe-offerings.ts +384 -0
  113. package/templates/apps/api/src/routes/webhooks/stripe.ts +245 -36
  114. package/templates/apps/api/src/server.ts +163 -46
  115. package/templates/apps/api/src/services/__tests__/checkout-plan.test.ts +313 -0
  116. package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +51 -2
  117. package/templates/apps/api/src/services/__tests__/keyless-workspace-predicate.test.ts +234 -0
  118. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +1383 -133
  119. package/templates/apps/api/src/services/__tests__/magnet-completion.test.ts +389 -0
  120. package/templates/apps/api/src/services/__tests__/person.test.ts +232 -1
  121. package/templates/apps/api/src/services/__tests__/plan-offerings.test.ts +444 -0
  122. package/templates/apps/api/src/services/checkout-buyer.ts +100 -4
  123. package/templates/apps/api/src/services/consent.ts +95 -0
  124. package/templates/apps/api/src/services/deliverable-fulfillment.ts +13 -5
  125. package/templates/apps/api/src/services/entitlements.ts +157 -1
  126. package/templates/apps/api/src/services/lead-routing.ts +431 -47
  127. package/templates/apps/api/src/services/magnet-completion.ts +242 -0
  128. package/templates/apps/api/src/services/offerings/__tests__/catalog.test.ts +295 -0
  129. package/templates/apps/api/src/services/offerings/__tests__/checkout-session-record.test.ts +268 -0
  130. package/templates/apps/api/src/services/offerings/__tests__/connection.test.ts +868 -0
  131. package/templates/apps/api/src/services/offerings/__tests__/fixtures.ts +416 -0
  132. package/templates/apps/api/src/services/offerings/__tests__/org-products.test.ts +396 -0
  133. package/templates/apps/api/src/services/offerings/__tests__/readers.test.ts +681 -0
  134. package/templates/apps/api/src/services/offerings/__tests__/source.test.ts +231 -0
  135. package/templates/apps/api/src/services/offerings/catalog.ts +308 -0
  136. package/templates/apps/api/src/services/offerings/checkout-authorization.ts +385 -0
  137. package/templates/apps/api/src/services/offerings/checkout-session-record.ts +192 -0
  138. package/templates/apps/api/src/services/offerings/connection.ts +1003 -0
  139. package/templates/apps/api/src/services/offerings/index.ts +614 -0
  140. package/templates/apps/api/src/services/offerings/source.ts +140 -0
  141. package/templates/apps/api/src/services/offerings/store.ts +150 -0
  142. package/templates/apps/api/src/services/person.ts +164 -17
  143. package/templates/apps/api/src/services/plan-offerings.ts +125 -0
  144. package/templates/apps/api/src/services/public-site-resolver.ts +27 -15
  145. package/templates/apps/mcp/CLAUDE.md +22 -14
  146. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +9 -4
  147. package/templates/apps/mcp/__tests__/get_offerings.test.ts +358 -0
  148. package/templates/apps/mcp/__tests__/get_pricing.test.ts +364 -0
  149. package/templates/apps/mcp/__tests__/http-transport.test.ts +4 -4
  150. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +2 -2
  151. package/templates/apps/mcp/__tests__/server.test.ts +13 -11
  152. package/templates/apps/mcp/__tests__/start_purchase.test.ts +208 -12
  153. package/templates/apps/mcp/__tests__/tools/purchase.test.ts +11 -11
  154. package/templates/apps/mcp/src/lib/shown-offerings.ts +105 -0
  155. package/templates/apps/mcp/src/server.ts +5 -0
  156. package/templates/apps/mcp/src/tools/get_offerings.ts +47 -30
  157. package/templates/apps/mcp/src/tools/get_pricing.ts +40 -40
  158. package/templates/apps/mcp/src/tools/purchase.ts +30 -2
  159. package/templates/apps/mcp/src/workspace-server.ts +15 -0
  160. package/templates/apps/web/.env.example +24 -31
  161. package/templates/apps/web/CLAUDE.md +393 -9
  162. package/templates/apps/web/__tests__/__mocks__/next-intl-server.ts +6 -2
  163. package/templates/apps/web/app/[locale]/(auth)/__tests__/auth-pages.test.tsx +5 -4
  164. package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +5 -1
  165. package/templates/apps/web/app/[locale]/(auth)/forgot-password/page.tsx +2 -0
  166. package/templates/apps/web/app/[locale]/(auth)/layout.tsx +3 -14
  167. package/templates/apps/web/app/[locale]/(auth)/reset-password/page.tsx +2 -0
  168. package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +5 -1
  169. package/templates/apps/web/app/[locale]/(auth)/sign-in/__tests__/page.test.tsx +160 -8
  170. package/templates/apps/web/app/[locale]/(auth)/sign-in/__tests__/sign-in-card.test.tsx +12 -9
  171. package/templates/apps/web/app/[locale]/(auth)/sign-in/page.tsx +105 -2
  172. package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +36 -3
  173. package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-text.ts +47 -1
  174. package/templates/apps/web/app/[locale]/(auth)/sign-up/__tests__/sign-up-card.test.tsx +60 -22
  175. package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +16 -8
  176. package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +85 -31
  177. package/templates/apps/web/app/[locale]/(auth)/use-auth-card-flow.ts +18 -4
  178. package/templates/apps/web/app/[locale]/(auth)/verify-email/page.tsx +3 -1
  179. package/templates/apps/web/app/[locale]/(authed)/__tests__/gate.test.tsx +7 -4
  180. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/agreements-prompt.test.tsx +227 -207
  181. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout-sign-out.test.tsx +17 -1
  182. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout.test.tsx +143 -1
  183. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/new-menu.test.tsx +4 -0
  184. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +385 -89
  185. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/popups.test.tsx +5 -0
  186. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/privacy.test.tsx +44 -5
  187. package/templates/apps/web/app/[locale]/(authed)/account/account-frame.tsx +68 -34
  188. package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +0 -8
  189. package/templates/apps/web/app/[locale]/(authed)/account/agreements-actions.ts +39 -151
  190. package/templates/apps/web/app/[locale]/(authed)/account/agreements-gate.ts +61 -0
  191. package/templates/apps/web/app/[locale]/(authed)/account/agreements-prompt.tsx +143 -140
  192. package/templates/apps/web/app/[locale]/(authed)/account/layout.tsx +40 -6
  193. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +68 -54
  194. package/templates/apps/web/app/[locale]/(authed)/account/privacy/page.tsx +5 -1
  195. package/templates/apps/web/app/[locale]/careers/[slug]/[action]/page.tsx +189 -0
  196. package/templates/apps/web/app/[locale]/careers/[slug]/apply.actions.ts +295 -0
  197. package/templates/apps/web/app/[locale]/careers/[slug]/job-application-form.ts +120 -0
  198. package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +23 -0
  199. package/templates/apps/web/app/[locale]/careers/__tests__/apply.actions.test.ts +319 -0
  200. package/templates/apps/web/app/[locale]/careers/__tests__/careers-hub.test.tsx +172 -0
  201. package/templates/apps/web/app/[locale]/careers/__tests__/careers-set.test.tsx +149 -0
  202. package/templates/apps/web/app/[locale]/careers/__tests__/job-application-form.test.ts +141 -0
  203. package/templates/apps/web/app/[locale]/careers/__tests__/job-apply-page.test.tsx +424 -0
  204. package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +72 -11
  205. package/templates/apps/web/app/[locale]/careers/page.tsx +129 -5
  206. package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +2 -2
  207. package/templates/apps/web/app/[locale]/checkout/__tests__/pages.test.tsx +139 -0
  208. package/templates/apps/web/app/[locale]/checkout/__tests__/thank-you-account.test.tsx +136 -0
  209. package/templates/apps/web/app/[locale]/checkout/thank-you/page.tsx +114 -0
  210. package/templates/apps/web/app/[locale]/checkout/unavailable/page.tsx +51 -0
  211. package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +1 -1
  212. package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +17 -1
  213. package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +79 -4
  214. package/templates/apps/web/app/[locale]/contact/__tests__/contact-magnet-page.test.tsx +160 -13
  215. package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +466 -36
  216. package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +7 -0
  217. package/templates/apps/web/app/[locale]/contact/contact.actions.ts +172 -57
  218. package/templates/apps/web/app/[locale]/contact/intake-script.ts +26 -0
  219. package/templates/apps/web/app/[locale]/contact/page.tsx +99 -60
  220. package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +2 -2
  221. package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +2 -2
  222. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +341 -4
  223. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.count.test.tsx +43 -0
  224. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.deliverable.test.tsx +200 -0
  225. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.ending.test.tsx +197 -0
  226. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.invite.test.tsx +257 -0
  227. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/account-invite.actions.test.ts +69 -0
  228. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/magnet-page.test.tsx +430 -0
  229. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +21 -9
  230. package/templates/apps/web/app/[locale]/flows/[slug]/account-invite.actions.ts +35 -0
  231. package/templates/apps/web/app/[locale]/flows/[slug]/magnet-page.tsx +352 -0
  232. package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +12 -73
  233. package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +10 -31
  234. package/templates/apps/web/app/[locale]/for/__tests__/page-renderer-migration.test.tsx +1 -1
  235. package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
  236. package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +2 -2
  237. package/templates/apps/web/app/[locale]/layout.tsx +55 -4
  238. package/templates/apps/web/app/[locale]/newsletter/__tests__/page.test.tsx +174 -104
  239. package/templates/apps/web/app/[locale]/newsletter/page.tsx +95 -58
  240. package/templates/apps/web/app/[locale]/page.tsx +13 -1
  241. package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +2 -2
  242. package/templates/apps/web/app/[locale]/pricing/__tests__/__snapshots__/unconnected.test.tsx.snap +7 -0
  243. package/templates/apps/web/app/[locale]/pricing/__tests__/offerings.test.tsx +599 -0
  244. package/templates/apps/web/app/[locale]/pricing/__tests__/page.test.tsx +216 -0
  245. package/templates/apps/web/app/[locale]/pricing/__tests__/unconnected.test.tsx +236 -0
  246. package/templates/apps/web/app/[locale]/pricing/page.tsx +367 -6
  247. package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +2 -2
  248. package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +2 -2
  249. package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +2 -2
  250. package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
  251. package/templates/apps/web/app/[locale]/team/__tests__/team-routes.test.tsx +4 -1
  252. package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
  253. package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +1 -1
  254. package/templates/apps/web/app/[locale]/thanks/[slug]/AccountInviteLink.tsx +89 -0
  255. package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/AccountInviteLink.test.tsx +104 -0
  256. package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/page.test.tsx +38 -0
  257. package/templates/apps/web/app/[locale]/thanks/[slug]/page.tsx +9 -0
  258. package/templates/apps/web/app/[locale]/unsubscribe/__tests__/unsubscribe-rsc-boundary.test.tsx +17 -0
  259. package/templates/apps/web/app/[locale]/unsubscribe/__tests__/unsubscribe.test.tsx +62 -3
  260. package/templates/apps/web/app/[locale]/unsubscribe/page.tsx +70 -18
  261. package/templates/apps/web/app/[locale]/unsubscribe/unsubscribe-button.tsx +30 -9
  262. package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
  263. package/templates/apps/web/app/api/auth/hub/authorize/__tests__/route.test.ts +114 -0
  264. package/templates/apps/web/app/api/auth/hub/authorize/route.ts +20 -3
  265. package/templates/apps/web/app/api/hub-session/__tests__/routes.test.ts +44 -1
  266. package/templates/apps/web/app/api/hub-session/sign-in/route.ts +22 -3
  267. package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/route.ts +316 -0
  268. package/templates/apps/web/app/api/offerings/checkout/__tests__/route.test.ts +810 -0
  269. package/templates/apps/web/auth.ts +28 -2
  270. package/templates/apps/web/lib/__tests__/account-invite.test.ts +63 -0
  271. package/templates/apps/web/lib/__tests__/auth-routes.test.ts +27 -1
  272. package/templates/apps/web/lib/__tests__/consent-policies.test.ts +23 -0
  273. package/templates/apps/web/lib/__tests__/deck-page.test.ts +7 -1
  274. package/templates/apps/web/lib/__tests__/in-page-magnet.test.ts +265 -0
  275. package/templates/apps/web/lib/__tests__/magnet-routes.test.ts +57 -0
  276. package/templates/apps/web/lib/__tests__/newsletter.actions.test.ts +293 -0
  277. package/templates/apps/web/lib/__tests__/offerings-pricing.test.ts +601 -0
  278. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +101 -25
  279. package/templates/apps/web/lib/account-invite.ts +37 -0
  280. package/templates/apps/web/lib/auth-routes.ts +36 -2
  281. package/templates/apps/web/lib/checkout-config.ts +16 -0
  282. package/templates/apps/web/lib/consent-policies.ts +22 -0
  283. package/templates/apps/web/lib/deck-page.ts +7 -0
  284. package/templates/apps/web/lib/deliverable-grant.ts +18 -0
  285. package/templates/apps/web/lib/hub-session/__tests__/sign-in-gate.test.tsx +111 -0
  286. package/templates/apps/web/lib/hub-session/sign-in-gate.tsx +88 -32
  287. package/templates/apps/web/lib/in-page-magnet.ts +416 -0
  288. package/templates/apps/web/lib/magnet-routes.ts +42 -0
  289. package/templates/apps/web/lib/modules.generated.ts +9 -0
  290. package/templates/apps/web/lib/newsletter.actions.ts +202 -0
  291. package/templates/apps/web/lib/offerings-pricing.ts +326 -0
  292. package/templates/apps/web/lib/posting-title.ts +17 -0
  293. package/templates/apps/web/lib/prisma-for-auth.ts +8 -0
  294. package/templates/apps/web/lib/sign-up-agreements.ts +449 -0
  295. package/templates/apps/web/lib/site-chrome.ts +2 -0
  296. package/templates/apps/web/lib/telemetry-scrub.ts +5 -1
  297. package/templates/apps/web/messages/en.json +71 -20
  298. package/templates/apps/web/middleware.ts +12 -2
  299. package/templates/apps/web/package.json +1 -0
  300. package/templates/apps/web/security-headers.config.ts +17 -0
  301. package/templates/apps/web/src/lib/__tests__/lead-write.test.ts +187 -0
  302. package/templates/apps/web/src/lib/__tests__/rate-limit.test.ts +67 -0
  303. package/templates/apps/web/src/lib/lead-write.ts +90 -0
  304. package/templates/apps/web/src/lib/rate-limit.ts +79 -0
  305. package/templates/content/en/BlogList/blog.mdx +4 -71
  306. package/templates/content/en/BookCall/book-a-call.mdx +1 -41
  307. package/templates/content/en/BookCall/onsite-estimate.mdx +1 -17
  308. package/templates/content/en/BookCall/onsite-service.mdx +1 -17
  309. package/templates/content/en/BookCall/start-project.mdx +1 -17
  310. package/templates/content/en/ContactPage/contact.mdx +6 -63
  311. package/templates/content/en/HomePage/home.mdx +3 -21
  312. package/templates/database/CHANGELOG.md +69 -0
  313. package/templates/database/__tests__/consent-source-checkout-migration.test.ts +79 -0
  314. package/templates/database/__tests__/flow-completion-migration.test.ts +115 -0
  315. package/templates/database/__tests__/flow-completion.db.test.ts +136 -0
  316. package/templates/database/__tests__/in-page-magnets-migration.test.ts +161 -0
  317. package/templates/database/__tests__/offerings-checkout-session.db.test.ts +270 -0
  318. package/templates/database/__tests__/offerings-migration.test.ts +197 -0
  319. package/templates/database/__tests__/offerings-shown-price-ids-migration.test.ts +86 -0
  320. package/templates/database/__tests__/offerings.db.test.ts +303 -0
  321. package/templates/database/__tests__/seed.test.ts +64 -1
  322. package/templates/database/auth/schema.prisma +4 -0
  323. package/templates/database/config/schema.prisma +91 -0
  324. package/templates/database/inbox/schema.prisma +17 -2
  325. package/templates/database/migrations/20261018120000_flow_completion/migration.sql +52 -0
  326. package/templates/database/migrations/20261019120000_consent_source_checkout/migration.sql +27 -0
  327. package/templates/database/migrations/20261020120000_offerings_data_model/migration.sql +146 -0
  328. package/templates/database/migrations/20261021120000_offerings_checkout_session/migration.sql +77 -0
  329. package/templates/database/migrations/20261022120000_in_page_magnets/migration.sql +45 -0
  330. package/templates/database/migrations/20261023120000_offerings_shown_price_ids/migration.sql +47 -0
  331. package/templates/database/migrations/20261024120000_organization_plan_checkout_session/migration.sql +16 -0
  332. package/templates/database/package.json +1 -1
  333. package/templates/database/seed.ts +62 -3
  334. package/versions.json +35 -35
  335. package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +0 -199
  336. package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up.actions.ts +0 -125
  337. package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +0 -93
  338. package/templates/apps/web/app/[locale]/downloads/page.tsx +0 -37
  339. package/templates/apps/web/app/[locale]/newsletter/__tests__/newsletter.actions.test.ts +0 -139
  340. package/templates/apps/web/app/[locale]/newsletter/newsletter.actions.ts +0 -93
  341. package/templates/apps/web/lib/__tests__/meeting-slugs.test.ts +0 -120
  342. package/templates/apps/web/lib/meeting-slugs.ts +0 -63
package/dist/topology.js CHANGED
@@ -174,6 +174,29 @@ export function isRemovableClosureMember(path, opts = {}) {
174
174
  return (p.split("/").includes("__tests__") ||
175
175
  prefixes.some((prefix) => underPrefix(p, prefix)));
176
176
  }
177
+ /**
178
+ * The relative module specifiers a file reaches that are NOT static imports:
179
+ * a jest module reference (`jest.mock("../auth")`, `doMock`, `unmock`,
180
+ * `setMock`, `requireActual`, `requireMock`, `createMockFromModule`) or a
181
+ * `require(...)`. A suite that mocks a seed by path ships a dangling
182
+ * reference on a spoke exactly as one that imports it would (#8758), yet the
183
+ * shared import walk (`relativeImportsOf`) sees only `import`/`export ... from`
184
+ * and `import()` — it is shared with the module-slice lints, so the extra
185
+ * forms are read here rather than widening it for them.
186
+ */
187
+ function referencedModulesOf(absPath) {
188
+ const specs = relativeImportsOf(absPath);
189
+ let src;
190
+ try {
191
+ src = readFileSync(absPath, "utf8");
192
+ }
193
+ catch {
194
+ return specs;
195
+ }
196
+ for (const m of src.matchAll(/\b(?:jest\s*\.\s*(?:mock|doMock|unmock|setMock|requireActual|requireMock|createMockFromModule)|require)\s*\(\s*["'`](\.[^"'`]*)["'`]/g))
197
+ specs.push(m[1]);
198
+ return specs;
199
+ }
177
200
  /**
178
201
  * Every path a spoke does not carry, computed over the built `templates/`
179
202
  * tree: the removed roots, the seeds that apply, and every source file under
@@ -209,7 +232,7 @@ export function spokeExclusions(templatesDir, opts) {
209
232
  for (const file of candidates) {
210
233
  if (closed.has(file))
211
234
  continue;
212
- for (const spec of relativeImportsOf(join(templatesDir, file))) {
235
+ for (const spec of referencedModulesOf(join(templatesDir, file))) {
213
236
  const target = resolveRelativeImport(file, spec, candidates);
214
237
  if (target !== undefined && closed.has(target)) {
215
238
  closed.add(file);
@@ -899,6 +922,7 @@ export function renderSpokeHardFloors(opts) {
899
922
  " reason: Stripe webhook route — payment state requires human review",
900
923
  " - paths:",
901
924
  ' - "apps/web/app/api/checkout/**"',
925
+ ' - "apps/web/lib/checkout.ts"',
902
926
  ' - "apps/web/lib/checkout-config.ts"',
903
927
  ' - ".github/workflows/checkout-e2e.yml"',
904
928
  " reason: Spoke checkout — the buy route, the price map and the checkout e2e workflow require human review",
package/dist/update.js CHANGED
@@ -26,8 +26,10 @@ import { AddModuleRefusedError, readArchitectureHeader } from "./add-module-core
26
26
  import { buildScaffoldMarkers, isExcluded, isTextFile, renderTemplateFile, templateDestination } from "./copy.js";
27
27
  import { fetchTemplateSnapshot } from "./lib/fetch-template-snapshot.js";
28
28
  import { mergeThreeWay } from "./lib/merge-three-way.js";
29
+ import { MODULE_FLAGS_PATH, writeModuleFlags } from "./paid-sections.js";
29
30
  import { ParseError } from "./parse-args.js";
30
31
  import { SITE_ROSTER_PATH } from "./site-roster.js";
32
+ import { SPOKE_CHECKOUT_TEMPLATE_PATHS } from "./spoke-checkout.js";
31
33
  import { excludedRoutePaths } from "./surfaces.js";
32
34
  import { TEMPLATE_RECORD_PATH, isRecordable, parseTemplateRecord, serializeTemplateRecord, sha256Hex, } from "./template-record.js";
33
35
  import { TopologyError, isSpokeExcluded, readSpokeShape, spokeExclusions, } from "./topology.js";
@@ -248,9 +250,13 @@ export async function updateSite(opts) {
248
250
  }
249
251
  const excludedRoutes = excludedRoutePaths(header.surfaces);
250
252
  const spokeExclusionList = spoke !== undefined ? spokeExclusions(opts.templatesDir, spoke) : undefined;
253
+ // #8748 D3 — the standard spoke checkout is a commerce spoke's alone.
254
+ const sellsAsSpoke = spoke !== undefined && header.modules.includes("commerce");
251
255
  const outOfScope = (destRel) => {
252
256
  if (isExcluded(destRel, excludedRoutes))
253
257
  return true;
258
+ if (!sellsAsSpoke && isExcluded(destRel, SPOKE_CHECKOUT_TEMPLATE_PATHS))
259
+ return true;
254
260
  if (spoke !== undefined && spokeExclusionList !== undefined) {
255
261
  return isSpokeExcluded(destRel, { ...spoke, exclusions: spokeExclusionList });
256
262
  }
@@ -509,6 +515,15 @@ export async function updateSite(opts) {
509
515
  if (recordChanged) {
510
516
  writeFileSync(recordPath, serializeTemplateRecord({ schema: 1, projectName: record.projectName, files: nextFiles }));
511
517
  }
518
+ // #8843 D10 — the module-presence flags are generated, never copied
519
+ // (REGENERATED_APP_PATHS): render them from this site's own
520
+ // apps/web/package.json on every run, so a site updated onto a layout
521
+ // that imports them has them before it builds. Not recorded.
522
+ const flags = writeModuleFlags(targetDir);
523
+ if (flags === "added")
524
+ moved.added.push(MODULE_FLAGS_PATH);
525
+ else if (flags === "updated")
526
+ moved.updated.push(MODULE_FLAGS_PATH);
512
527
  return {
513
528
  targetDir,
514
529
  version,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ailk",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
4
4
  "description": "Scaffolder for AI Launch Kit — `pnpm create ailk@latest <dir>` produces a starter repo consuming @working-theory/* from npm (per ADR 0007).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -28,8 +28,8 @@
28
28
  "tar": "^7.5.21"
29
29
  },
30
30
  "devDependencies": {
31
- "@working-theory/schema": "0.42.0",
32
- "@working-theory/templates": "0.42.0"
31
+ "@working-theory/schema": "0.44.0",
32
+ "@working-theory/templates": "0.44.0"
33
33
  },
34
34
  "repository": {
35
35
  "type": "git",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  type: "NewsletterSignup"
3
3
  slug: "newsletter"
4
- title: "{Newsletter: what subscribers get and how often}"
4
+ title: "Get the newsletter"
5
5
  description: "{One sentence on what this newsletter covers and who it is for}"
6
6
  schema:
7
7
  mainEntity:
@@ -11,57 +11,12 @@ schema:
11
11
  priceCurrency: "USD"
12
12
  sections:
13
13
  [
14
- {
15
- "id": "hero",
16
- "type": "hero",
17
- "data":
18
- {
19
- "eyebrow": "Newsletter",
20
- "headline": "{Newsletter: what subscribers get and how often}",
21
- "subheadline": "{One sentence on what this newsletter covers and who it is for}",
22
- "primaryCta": { "label": "Subscribe", "href": "#signup" },
23
- },
24
- },
25
14
  {
26
15
  "id": "signup",
27
- "type": "newsletter",
28
- "data":
29
- {
30
- "headline": "Subscribe",
31
- "description": "{One sentence on what lands in the inbox}",
32
- "action": "/v1/leads/subscribe",
33
- "source": "newsletter",
34
- "placeholder": "you@example.com",
35
- "submitLabel": "Subscribe",
36
- "consentText": "{One line on how you use the address, and that readers can unsubscribe any time}",
37
- "pattern": "none",
38
- },
39
- },
40
- {
41
- "id": "faq",
42
- "type": "faq",
43
- "data":
44
- {
45
- "headline": "FAQs",
46
- "presentation": "expanded",
47
- "items":
48
- [
49
- {
50
- "question": "{How often will I hear from you?}",
51
- "answer": "{Answer in one or two plain sentences}",
52
- },
53
- {
54
- "question": "{What will I get?}",
55
- "answer": "{Answer in one or two plain sentences}",
56
- },
57
- {
58
- "question": "{Can I unsubscribe?}",
59
- "answer": "{Answer in one or two plain sentences}",
60
- },
61
- ],
62
- },
16
+ "type": "contact-form",
17
+ "data": { "variant": "column", "fields": [] },
63
18
  },
64
19
  ]
65
20
  ---
66
21
 
67
- {/* Rendered from frontmatter `sections[]` by the /newsletter route. The form posts the address to lead capture (`/v1/leads/subscribe`), which records a subscriber and sends no verification email. The `schema:` Offer is the page's JSON-LD main entity — a free subscription. Placeholder copy — replace every {brace} with your own words before you launch. */}
22
+ {/* Rendered from frontmatter `sections[]` by the /newsletter route. The page's one signup surface is the workspace's Newsletter magnet: its heading, sentence, field, consent, button and ending all come from the magnet, mounted by the route in this section's slot. To change the signup, change the magnet in Admin → Magnets. The `schema:` Offer is the page's JSON-LD main entity — a free subscription. Replace every {brace} with your own words before you launch. */}
@@ -67,27 +67,9 @@ sections:
67
67
  "headlineStyle": "label",
68
68
  "logos":
69
69
  [
70
- {
71
- "name": "SalesSmyth",
72
- "src": "/logos/salessmyth.svg",
73
- "darkSrc": "/logos/salessmyth-light.svg",
74
- "width": 1118,
75
- "height": 147,
76
- },
77
- {
78
- "name": "AI Launch Kit",
79
- "src": "/logos/ai-launch-kit.svg",
80
- "darkSrc": "/logos/ai-launch-kit-light.svg",
81
- "width": 1248,
82
- "height": 144,
83
- },
84
- {
85
- "name": "Atomic Agent",
86
- "src": "/logos/atomic-agent.svg",
87
- "darkSrc": "/logos/atomic-agent-light.svg",
88
- "width": 1320,
89
- "height": 148,
90
- },
70
+ { "name": "Placeholder logo", "src": "/logomark.svg" },
71
+ { "name": "Placeholder logo", "src": "/logomark.svg" },
72
+ { "name": "Placeholder logo", "src": "/logomark.svg" },
91
73
  ],
92
74
  "layout": "inline",
93
75
  "treatment": "monochrome",
@@ -183,7 +183,7 @@ spacing:
183
183
 
184
184
  The minimalism design is the standard foundation — the same black-on-white wireframe, the same type and the same components — presented as a deck. The home page is a deck: each home section is one slide, snapping one slide per scroll, and the landing hero carries the brand, so the site reads like a presentation before it reads like a website; the rest of the site is quiet and document-like. Its character comes from structure, not a palette: it carries no theme of its own.
185
185
 
186
- The deck presents slide by slide (`layout.deckPresentation: true`). Each slide fits the screen, and a slide with more to say scrolls inside itself, with its down chevron after its last line. The pinned mark takes the ground of the slide under it, so it shows its dark variant over a dark slide. Each slide has its own link, `#slide-<name>`, which the address bar follows as the reader moves without adding a history entry. Slides fade in as they arrive, unless the reader has asked for reduced motion. Every slide's header is centred, and feature cards stack in one column on phones.
186
+ The deck presents slide by slide (`layout.deckPresentation: true`). Each slide fits the screen, and a slide with more to say scrolls inside itself, with its down chevron after its last line. The pinned mark takes the ground of the slide under it, so it shows its dark variant over a dark slide. Each slide has its own link, `#slide-<name>`, which the address bar follows as the reader moves without adding a history entry. A site may choose bare links instead, `#<name>`, by setting `layout.deckAnchors: bare` (it applies only with `deckPresentation: true`; minimalism leaves it unset, so its links stay `#slide-<name>`): a slide keeps the `slide-` name only where its bare name would collide, with another slide, a JSON-LD `@id` fragment on the page or an element id on the page (the page renderer checks `main` and the ids it is given; the deck checks every bare name against the live page when it loads, and the closing slide is `contact` unless an element or a JSON-LD node already has that id, else `slide-contact`), so a link never lands on a different element than its slide. Slides fade in as they arrive, unless the reader has asked for reduced motion. Every slide's header is centred, and feature cards stack in one column on phones. From 1024px up the slides after the landing share three lines, so a reader moving from slide to slide sees nothing jump: a **label heading** (the heading of a logo wall, a list or the portraits, each set in the section-label role above, smaller than the statement size) hangs from the **top line**, `--spacing-deck-label-line` below the top of the slide (7rem); the slide's content centres on the **middle line**, midway between the label zone and the action zone, whatever its height; and a slide's **action** (a statement slide's button, the logo wall's and the portraits' action row, the closing slide's buttons) ends on the **button line**, `--spacing-deck-action-line` above the foot of the slide (7.5rem, plus the consent bar's height while it shows), which is the landing's own button line. The landing sets that line; the other slides read it. Below 1024px a slide is one stacked flow and the lines do not apply. The action is seated on the line while its slide's content fits the zone; a slide that outgrows the zone scrolls inside itself, and the action, which is part of the scrolled content, no longer sits on the line (keep a slide's content within the zone, or put the action in the content). A list taller than its slide's content zone (five display-size titles at 1440×727) scrolls inside the slide, and its down chevron comes after its last line.
187
187
 
188
188
  Every page is composed from the AI Launch Kit component library (`@working-theory/ui`). Nothing is hand-built: a page is a list of registered sections, each with a bounded set of variants.
189
189
 
@@ -17,7 +17,7 @@
17
17
  ],
18
18
  "shape": "organization-product",
19
19
  "design": "standard",
20
- "generatedHash": "20f84bf7cdd2f51960084e6a91f2cbdc35d86fe87230619572124ca977609f5b",
20
+ "generatedHash": "f4a8f11aba42a071794585c4563015bd1c99cbe6917a36ee0629d37c6f5f476c",
21
21
  "layout": {
22
22
  "nav": "full",
23
23
  "footer": "columns",
@@ -333,15 +333,8 @@
333
333
  "chrome": "on",
334
334
  "sections": [
335
335
  {
336
- "type": "hero",
337
- "component": "Hero"
338
- },
339
- {
340
- "type": "items",
341
- "component": "Items",
342
- "options": {
343
- "columns": 2
344
- }
336
+ "type": "contact-form",
337
+ "component": "ContactForm"
345
338
  }
346
339
  ]
347
340
  },
@@ -372,19 +365,20 @@
372
365
  "chrome": "on",
373
366
  "sections": [
374
367
  {
375
- "type": "hero",
376
- "component": "Hero"
377
- },
378
- {
379
- "type": "newsletter",
380
- "component": "Newsletter"
381
- },
382
- {
383
- "type": "faq",
384
- "component": "FAQ"
368
+ "type": "contact-form",
369
+ "component": "ContactForm"
385
370
  }
386
371
  ]
387
372
  },
373
+ {
374
+ "route": "/unsubscribe",
375
+ "kind": "route",
376
+ "pageType": null,
377
+ "schema": null,
378
+ "shell": "MarketingShell",
379
+ "chrome": "on",
380
+ "sections": []
381
+ },
388
382
  {
389
383
  "route": "/sign-in",
390
384
  "kind": "route",
@@ -697,6 +691,15 @@
697
691
  "chrome": "on",
698
692
  "sections": []
699
693
  },
694
+ {
695
+ "route": "/private",
696
+ "kind": "route",
697
+ "pageType": null,
698
+ "schema": null,
699
+ "shell": "MarketingShell",
700
+ "chrome": "on",
701
+ "sections": []
702
+ },
700
703
  {
701
704
  "route": "/appointments",
702
705
  "kind": "route",
@@ -177,10 +177,9 @@ _Detail route over `content/en/Feature/` — 3 seed pages: `feature-1.mdx`, `fea
177
177
 
178
178
  <!-- generated:begin page=/contact -->
179
179
 
180
- | # | Section | Component | Copy (first line) |
181
- | --- | --------------------------------------------------------- | --------- | ----------------------------------------- |
182
- | 1 | `hero` (split · eyebrow "Contact" · checklist ×3 · 1 CTA) | `Hero` | Get every message in your email, free |
183
- | 2 | `items` (tiles · columns=2 · items ×4) | `Items` | {Add appointment scheduling to your site} |
180
+ | # | Section | Component | Copy (first line) |
181
+ | --- | ----------------------------------- | ------------- | ----------------- |
182
+ | 1 | `contact-form` (column · fields ×0) | `ContactForm` | |
184
183
 
185
184
  <!-- generated:end -->
186
185
 
@@ -197,14 +196,17 @@ _Detail route over `content/en/Feature/` — 3 seed pages: `feature-1.mdx`, `fea
197
196
 
198
197
  <!-- generated:begin page=/newsletter -->
199
198
 
200
- | # | Section | Component | Copy (first line) |
201
- | --- | ------------------------------------- | ------------ | ------------------------------------------------ |
202
- | 1 | `hero` (eyebrow "Newsletter" · 1 CTA) | `Hero` | {Newsletter: what subscribers get and how often} |
203
- | 2 | `newsletter` | `Newsletter` | Subscribe |
204
- | 3 | `faq` (expanded · items ×3) | `FAQ` | FAQs |
199
+ | # | Section | Component | Copy (first line) |
200
+ | --- | ----------------------------------- | ------------- | ----------------- |
201
+ | 1 | `contact-form` (column · fields ×0) | `ContactForm` | |
205
202
 
206
203
  <!-- generated:end -->
207
204
 
205
+ <!-- generated:begin page=/unsubscribe -->
206
+
207
+ _Code route — no content, no sections._
208
+ <!-- generated:end -->
209
+
208
210
  `/sign-in`:
209
211
 
210
212
  <!-- generated:begin page=/sign-in -->
@@ -342,6 +344,16 @@ Target composition (deferred to **#5670**, same as `/meet` above):
342
344
  _Code route — no content, no sections._
343
345
  <!-- generated:end -->
344
346
 
347
+ ### `/private` — the private meeting index · scheduling
348
+
349
+ The same booking frame as `/meet`, listing the workspace's private meetings
350
+ (#8843 D8); `noindex`, unlinked, no seed.
351
+
352
+ <!-- generated:begin page=/private -->
353
+
354
+ _Code route — no content, no sections._
355
+ <!-- generated:end -->
356
+
345
357
  `/appointments`:
346
358
 
347
359
  <!-- generated:begin page=/appointments -->
@@ -517,6 +529,7 @@ marketing-site:
517
529
  - { route: "/contact", kind: page, content: ["content/en/ContactPage/contact.mdx"] }
518
530
  - { route: "/glossary", kind: page, content: ["content/en/GlossaryList/glossary.mdx"] }
519
531
  - { route: "/newsletter", kind: page, content: ["content/en/NewsletterSignup/newsletter.mdx"] }
532
+ - { route: "/unsubscribe", kind: route }
520
533
  - { route: "/sign-in", kind: route }
521
534
  - { route: "/sign-up", kind: route }
522
535
  - { route: "/privacy", kind: page, content: ["content/en/PrivacyPage/privacy.mdx"] }
@@ -537,6 +550,7 @@ marketing-site:
537
550
  - { route: "/meet", kind: route }
538
551
  - { route: "/meet/[slug]", kind: route }
539
552
  - { route: "/meet/confirmed", kind: route }
553
+ - { route: "/private", kind: route }
540
554
  - { route: "/appointments", kind: route }
541
555
  - { route: "/flows/[slug]", kind: route }
542
556
  - { route: "/flows/[slug]/start", kind: route }
@@ -563,11 +577,11 @@ Every component the roster rows' seed content composes today (generated):
563
577
 
564
578
  <!-- generated:begin inventory -->
565
579
 
566
- | Tier | Used by this template |
567
- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
568
- | Shell | `MarketingShell` |
569
- | Chrome | `Nav`, `Footer`, `Banner` (+ `BannerConsentIsland`, `BannerDismissIsland`) |
570
- | Sections | `ContentBlock`, `CTA`, `DecisionSplit`, `DocumentChassis`, `FAQ`, `Feature`, `GlossaryIndex`, `Hero`, `Items`, `LogoCloud`, `MediaBand`, `Newsletter`, `Pricing`, `StairSteps`, `Team`, `Testimonials` |
580
+ | Tier | Used by this template |
581
+ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
582
+ | Shell | `MarketingShell` |
583
+ | Chrome | `Nav`, `Footer`, `Banner` (+ `BannerConsentIsland`, `BannerDismissIsland`) |
584
+ | Sections | `ContactForm`, `ContentBlock`, `CTA`, `DecisionSplit`, `DocumentChassis`, `FAQ`, `Feature`, `GlossaryIndex`, `Hero`, `Items`, `LogoCloud`, `MediaBand`, `Pricing`, `StairSteps`, `Team`, `Testimonials` |
571
585
 
572
586
  <!-- generated:end -->
573
587
 
@@ -4,11 +4,11 @@
4
4
  "categories": ["accounting", "legal", "technology"],
5
5
  "shape": "organization-product",
6
6
  "design": "standard",
7
- "generatedHash": "08391280820bcc6edd5c7062b75c2f33852b02c37ab518d1302bb7cb43c44714",
7
+ "generatedHash": "69b9e9d31dba7bc9d556deb74bf88969c0be778e81cc96fa6966387d728317fa",
8
8
  "layout": {
9
9
  "nav": "full",
10
10
  "footer": "columns",
11
- "announcementBanner": true,
11
+ "announcementBanner": false,
12
12
  "home": "default"
13
13
  },
14
14
  "theme": {
@@ -222,13 +222,6 @@
222
222
  "shell": "MarketingShell",
223
223
  "chrome": "on",
224
224
  "sections": [
225
- {
226
- "type": "hero",
227
- "component": "Hero",
228
- "options": {
229
- "gapBelow": "tight"
230
- }
231
- },
232
225
  {
233
226
  "type": "pricing",
234
227
  "component": "Pricing",
@@ -327,15 +320,8 @@
327
320
  "chrome": "on",
328
321
  "sections": [
329
322
  {
330
- "type": "hero",
331
- "component": "Hero"
332
- },
333
- {
334
- "type": "items",
335
- "component": "Items",
336
- "options": {
337
- "columns": 2
338
- }
323
+ "type": "contact-form",
324
+ "component": "ContactForm"
339
325
  }
340
326
  ]
341
327
  },
@@ -366,15 +352,20 @@
366
352
  "chrome": "on",
367
353
  "sections": [
368
354
  {
369
- "type": "hero",
370
- "component": "Hero"
371
- },
372
- {
373
- "type": "faq",
374
- "component": "FAQ"
355
+ "type": "contact-form",
356
+ "component": "ContactForm"
375
357
  }
376
358
  ]
377
359
  },
360
+ {
361
+ "route": "/unsubscribe",
362
+ "kind": "route",
363
+ "pageType": null,
364
+ "schema": null,
365
+ "shell": "MarketingShell",
366
+ "chrome": "on",
367
+ "sections": []
368
+ },
378
369
  {
379
370
  "route": "/sign-in",
380
371
  "kind": "route",
@@ -476,10 +467,6 @@
476
467
  "gapBelow": "tight"
477
468
  }
478
469
  },
479
- {
480
- "type": "blog-list",
481
- "component": "BlogList"
482
- },
483
470
  {
484
471
  "type": "logoCloud",
485
472
  "component": "LogoCloud",
@@ -526,17 +513,6 @@
526
513
  "type": "hero",
527
514
  "component": "Hero"
528
515
  },
529
- {
530
- "type": "cta",
531
- "component": "CTA"
532
- },
533
- {
534
- "type": "items",
535
- "component": "Items",
536
- "options": {
537
- "columns": 3
538
- }
539
- },
540
516
  {
541
517
  "type": "content-block",
542
518
  "component": "ContentBlock"
@@ -34,7 +34,7 @@ The template's `layout` block — resolved with `resolveSiteChrome`, defaults fi
34
34
  | -------------------- | --------- |
35
35
  | `nav` | `full` |
36
36
  | `footer` | `columns` |
37
- | `announcementBanner` | `true` |
37
+ | `announcementBanner` | `false` |
38
38
  | `home` | `default` |
39
39
 
40
40
  <!-- generated:end -->
@@ -154,14 +154,13 @@ Reference: `screenshots/pricing.png`, `copy/pricing.txt`. The reference opens on
154
154
 
155
155
  <!-- generated:begin page=/pricing -->
156
156
 
157
- | # | Section | Component | Copy (first line) |
158
- | --- | ------------------------------------------------------------------------------------ | -------------- | ----------------------------------------------------------------- |
159
- | 1 | `hero` (centered · gapBelow=tight · eyebrow "Pricing" · 2 CTAs) | `Hero` | {Upgrade to Pro with Commerce} |
160
- | 2 | `pricing` (three-tiers-with-toggle · cardLayout=centered · plans ×3 · billingToggle) | `Pricing` | Basic plan |
161
- | 3 | `logoCloud` (layout=inline · logos ×3) | `LogoCloud` | {Don’t just take our word for it} |
162
- | 4 | `testimonials` (quote · items ×1) | `Testimonials` | {A customer quote, in their own words, about the result they got} |
163
- | 5 | `cta` (box · checklist ×3 · 2 CTAs · closingLine) | `CTA` | {Switch to your product in time amount or less} |
164
- | 6 | `faq` (accordion · items ×5 · 1 CTA · initialOpen none) | `FAQ` | {FAQs} |
157
+ | # | Section | Component | Copy (first line) |
158
+ | --- | -------------------------------------------------------------------------------------------------------- | -------------- | ----------------------------------------------------------------- |
159
+ | 1 | `pricing` (three-tiers-with-toggle · cardLayout=centered · eyebrow "Pricing" · plans ×3 · billingToggle) | `Pricing` | {Pays for itself in tangible value or outcome} |
160
+ | 2 | `logoCloud` (layout=inline · logos ×3) | `LogoCloud` | Don’t just take our word for it |
161
+ | 3 | `testimonials` (quote · items ×1) | `Testimonials` | {A customer quote, in their own words, about the result they got} |
162
+ | 4 | `cta` (box · checklist ×3 · 2 CTAs · closingLine) | `CTA` | {Switch to your product in time amount or less} |
163
+ | 5 | `faq` (accordion · items ×5 · 1 CTA · initialOpen none) | `FAQ` | FAQs |
165
164
 
166
165
  <!-- generated:end -->
167
166
 
@@ -201,10 +200,9 @@ Reference: `screenshots/contact.png`, `copy/contact.txt`. The reference is a spl
201
200
 
202
201
  <!-- generated:begin page=/contact -->
203
202
 
204
- | # | Section | Component | Copy (first line) |
205
- | --- | --------------------------------------------------------- | --------- | ----------------------------------------- |
206
- | 1 | `hero` (split · eyebrow "Contact" · checklist ×3 · 1 CTA) | `Hero` | Get every message in your email, free |
207
- | 2 | `items` (tiles · columns=2 · items ×4) | `Items` | {Add appointment scheduling to your site} |
203
+ | # | Section | Component | Copy (first line) |
204
+ | --- | ----------------------------------- | ------------- | ----------------- |
205
+ | 1 | `contact-form` (column · fields ×0) | `ContactForm` | |
208
206
 
209
207
  <!-- generated:end -->
210
208
 
@@ -227,13 +225,21 @@ A base-layer seed every starter carries (#6658 D5). The form posts to lead captu
227
225
 
228
226
  <!-- generated:begin page=/newsletter -->
229
227
 
230
- | # | Section | Component | Copy (first line) |
231
- | --- | -------------------------------------- | --------- | --------------------------------------- |
232
- | 1 | `hero` (eyebrow "Newsletter" · 2 CTAs) | `Hero` | {Start a free newsletter with Substack} |
233
- | 2 | `faq` (expanded · items ×3) | `FAQ` | FAQs |
228
+ | # | Section | Component | Copy (first line) |
229
+ | --- | ----------------------------------- | ------------- | ----------------- |
230
+ | 1 | `contact-form` (column · fields ×0) | `ContactForm` | |
234
231
 
235
232
  <!-- generated:end -->
236
233
 
234
+ ### `/unsubscribe` — code route (the unsubscribe landing) · chrome on
235
+
236
+ The landing every signed unsubscribe link calls (#8438 D17) — a code route like `/sign-in`, copied into every scaffold and never gated, listed so the manifest is a complete install record (#8850 D1). No reference page.
237
+
238
+ <!-- generated:begin page=/unsubscribe -->
239
+
240
+ _Code route — no content, no sections._
241
+ <!-- generated:end -->
242
+
237
243
  ### `/meet` — LandingPage · schema `WebPage` · chrome on
238
244
 
239
245
  An always-copied overlay seed (#6897 D3): a meeting-types hub, served by the generic `[slug]` route, listing the four `/meet/<slug>` tiles below through the same `items` `tiles` variant Contact uses. A template carrying the scheduling module serves its own `meet/` route directory instead, by Next.js static-segment precedence.
@@ -253,12 +259,10 @@ Four always-copied overlay seeds (#6897 D4/D5), served by the new `[slug]/[detai
253
259
 
254
260
  <!-- generated:begin page=/meet/[slug] -->
255
261
 
256
- | # | Section | Component | Copy (first line) |
257
- | --- | -------------------------------------- | -------------- | ------------------------------------------------ |
258
- | 1 | `hero` (eyebrow "Book a Call" · 1 CTA) | `Hero` | Book a Call |
259
- | 2 | `cta` (box · 1 CTA) | `CTA` | {What booking a call would do for your visitors} |
260
- | 3 | `items` (columns=3 · items ×3) | `Items` | What happens on the call? |
261
- | 4 | `content-block` (1 CTA) | `ContentBlock` | |
262
+ | # | Section | Component | Copy (first line) |
263
+ | --- | -------------------------------------- | -------------- | ----------------- |
264
+ | 1 | `hero` (eyebrow "Book a Call" · 1 CTA) | `Hero` | Book a Call |
265
+ | 2 | `content-block` (1 CTA) | `ContentBlock` | |
262
266
 
263
267
  <!-- generated:end -->
264
268
 
@@ -268,12 +272,11 @@ Reference: `screenshots/blog.png`, `copy/blog.txt` for the opener and the newsle
268
272
 
269
273
  <!-- generated:begin page=/blog -->
270
274
 
271
- | # | Section | Component | Copy (first line) |
272
- | --- | ------------------------------------------------------------ | -------------- | ----------------------------------------------------------------- |
273
- | 1 | `hero` (centered · gapBelow=tight · eyebrow "Blog" · 2 CTAs) | `Hero` | Get free blog/docs from Fumadocs |
274
- | 2 | `blog-list` (grid) | `BlogList` | |
275
- | 3 | `logoCloud` (layout=inline · logos ×6) | `LogoCloud` | Don’t just take our word for it |
276
- | 4 | `testimonials` (quote · items ×1) | `Testimonials` | {A customer quote, in their own words, about the result they got} |
275
+ | # | Section | Component | Copy (first line) |
276
+ | --- | --------------------------------------------------- | -------------- | ----------------------------------------------------------------- |
277
+ | 1 | `hero` (centered · gapBelow=tight · eyebrow "Blog") | `Hero` | {Blog} |
278
+ | 2 | `logoCloud` (layout=inline · logos ×6) | `LogoCloud` | Don’t just take our word for it |
279
+ | 3 | `testimonials` (quote · items ×1) | `Testimonials` | {A customer quote, in their own words, about the result they got} |
277
280
 
278
281
  <!-- generated:end -->
279
282
 
@@ -442,6 +445,7 @@ marketing-site-free:
442
445
  - { route: "/contact", kind: page, content: ["content/en/ContactPage/contact.mdx"] }
443
446
  - { route: "/glossary", kind: page, content: ["content/en/GlossaryList/glossary.mdx"] }
444
447
  - { route: "/newsletter", kind: page, content: ["content/en/NewsletterSignup/newsletter.mdx"] }
448
+ - { route: "/unsubscribe", kind: route }
445
449
  - { route: "/sign-in", kind: route }
446
450
  - { route: "/sign-up", kind: route }
447
451
  - { route: "/privacy", kind: page, content: ["content/en/PrivacyPage/privacy.mdx"] }
@@ -505,11 +509,11 @@ reversed (`docs/decisions/starter-roster-page-set.md` D4, D7).
505
509
 
506
510
  <!-- generated:begin inventory -->
507
511
 
508
- | Tier | Used by this template |
509
- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
510
- | Shell | `MarketingShell` |
511
- | Chrome | `Nav`, `Footer`, `Banner` (+ `BannerConsentIsland`, `BannerDismissIsland`) |
512
- | Sections | `BlogList`, `ContentBlock`, `CTA`, `DecisionSplit`, `DocumentChassis`, `FAQ`, `Feature`, `GlossaryIndex`, `Hero`, `Items`, `LogoCloud`, `MediaBand`, `Pricing`, `Team`, `Testimonials` |
512
+ | Tier | Used by this template |
513
+ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
514
+ | Shell | `MarketingShell` |
515
+ | Chrome | `Nav`, `Footer`, `Banner` (+ `BannerConsentIsland`, `BannerDismissIsland`) |
516
+ | Sections | `ContactForm`, `ContentBlock`, `CTA`, `DecisionSplit`, `DocumentChassis`, `FAQ`, `Feature`, `GlossaryIndex`, `Hero`, `Items`, `LogoCloud`, `MediaBand`, `Pricing`, `Team`, `Testimonials` |
513
517
 
514
518
  <!-- generated:end -->
515
519