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
@@ -3,21 +3,26 @@
3
3
  // content the route 404s. Mirrors for/page.tsx: a sections-composed
4
4
  // page renders its sections, a prose-only page its MDX body.
5
5
  //
6
- // A signup only when there is somewhere to keep it (#6730 D9). The API says
7
- // whether it accepts a subscription (`GET /v1/leads/capture` — a database is
8
- // set up):
6
+ // The newsletter magnet is the only source (#8847 D11): the page reads the
7
+ // workspace's `newsletter` magnet (`flows.get("newsletter")`, shared for 15
8
+ // seconds per instance like /contact's read), else the seeded
9
+ // `newsletterFlowConfig` when the hub cannot be read. Its step title is the
10
+ // page's H1 and `<title>`, its body the line under it (`framedSources`), and
11
+ // the standard `NewsletterFormInline` — label, consent, help line, button and
12
+ // ending all the magnet's — goes into the page's first `contact-form`
13
+ // section, bound to `subscribeNewsletterAction`. Rendered on every request.
9
14
  //
10
- // - accepting: a content `newsletter` section renders as authored (its own
11
- // form posts to `/v1/leads/subscribe`); a page with no such section gets
12
- // the shared `LeadCaptureForm` (one email field) appended, bound to
13
- // `subscribeNewsletterAction`.
14
- // - otherwise — including an unreachable API — every `newsletter` section
15
- // is dropped before render, and a leading hero's CTA that pointed at a
16
- // dropped section's anchor goes with it, so no signup form of any kind
17
- // renders and no button points at a section that is gone. The API
18
- // refuses a subscribe in that state anyway, so a cached page can never
19
- // record a false success.
20
- import { leads } from "@working-theory/api-client";
15
+ // A signup only when there is somewhere to keep it (#6730 D9): when the API
16
+ // does not accept a subscription (`GET /v1/leads/capture` — a database is set
17
+ // up), the slot holds `ContactFallback` with the site's contact email.
18
+ //
19
+ // An authored `newsletter` section never renders beside the slot. A page with
20
+ // no `contact-form` section keeps the composition it had before
21
+ // #8847: a content `newsletter` section renders as authored when accepting
22
+ // (its own form posts to `/v1/leads/subscribe`), a page with no such section
23
+ // gets the signup appended, and when not accepting every `newsletter` section
24
+ // is dropped before render, with any leading-hero CTA that pointed at a
25
+ // dropped section's anchor.
21
26
  import type {
22
27
  getMdxSource as defaultGetMdxSource,
23
28
  getPageSections as defaultGetPageSections,
@@ -28,14 +33,25 @@ import {
28
33
  renderPage,
29
34
  renderPageMetadata,
30
35
  } from "@working-theory/page-renderer";
31
- import { LeadCaptureForm, localeHref } from "@working-theory/ui";
36
+ import { ContactFallback } from "@working-theory/ui";
37
+ import { NEWSLETTER_FLOW_SLUG, newsletterFlowConfig } from "@working-theory/validation";
32
38
  import type { Metadata } from "next";
33
39
  import { getTranslations } from "next-intl/server";
34
40
  import type { ReactElement } from "react";
35
41
 
36
- import { subscribeNewsletterAction } from "./newsletter.actions";
42
+ import {
43
+ framedSources,
44
+ inPageView,
45
+ type InPageView,
46
+ newsletterSignup,
47
+ type NewsletterFormView,
48
+ readCaptureStatus,
49
+ readServedMagnet,
50
+ } from "../../../lib/in-page-magnet";
51
+ import { readSiteContactEmail } from "../contact/site-contact-email";
37
52
 
38
- export const revalidate = 60;
53
+ // #8847 D11 — rendered on every request; the magnet read behind it is shared for 15 seconds.
54
+ export const dynamic = "force-dynamic";
39
55
 
40
56
  interface Props {
41
57
  params: Promise<{ locale: string }>;
@@ -44,15 +60,16 @@ interface Props {
44
60
  }
45
61
 
46
62
  type Section = Awaited<ReturnType<typeof defaultGetPageSections>>[number];
63
+ type NewsletterView = InPageView & { form: NewsletterFormView };
47
64
 
48
- /** Whether the API accepts a subscription; `false` for any failed read. */
49
- async function acceptsSubscription(): Promise<boolean> {
50
- try {
51
- const res = await leads.captureStatus();
52
- return res.ok && res.body.ok === true && res.body.data.newsletter;
53
- } catch {
54
- return false;
55
- }
65
+ /** The served newsletter magnet's view when it maps, else the seeded config's. */
66
+ async function newsletterView(locale: string): Promise<NewsletterView> {
67
+ const magnet = await readServedMagnet(NEWSLETTER_FLOW_SLUG);
68
+ const view =
69
+ (magnet ? inPageView(magnet, locale, "newsletter") : null) ??
70
+ // The seeded config always maps: one step, its email and consent fields.
71
+ inPageView(newsletterFlowConfig, locale, "newsletter")!;
72
+ return view as NewsletterView;
56
73
  }
57
74
 
58
75
  /**
@@ -77,12 +94,22 @@ function withoutSignup(sections: Section[]): Section[] {
77
94
 
78
95
  export async function generateMetadata({
79
96
  params,
80
- }: Pick<Props, "params">): Promise<Metadata> {
97
+ getMdxSource,
98
+ getPageSections,
99
+ }: Props): Promise<Metadata> {
81
100
  const { locale } = await params;
101
+ const view = await newsletterView(locale);
102
+ // #8847 D11 — the `<title>` is the magnet's step title, like the H1.
103
+ const framed = framedSources({
104
+ getMdxSource: getMdxSource ?? loadMdxSource,
105
+ getPageSections: getPageSections ?? loadPageSections,
106
+ view,
107
+ });
82
108
  return renderPageMetadata({
83
109
  type: "NewsletterSignup",
84
110
  slug: "newsletter",
85
111
  locale,
112
+ getMdxSource: framed.getMdxSource,
86
113
  });
87
114
  }
88
115
 
@@ -92,51 +119,61 @@ export default async function Page({
92
119
  getPageSections,
93
120
  }: Props): Promise<ReactElement> {
94
121
  const { locale } = await params;
95
- const load = getMdxSource ?? loadMdxSource;
96
- const loadSections = getPageSections ?? loadPageSections;
97
- const [source, accepting] = await Promise.all([
98
- load("NewsletterSignup", "newsletter", locale),
99
- acceptsSubscription(),
100
- ]);
101
- const declared = source ? await loadSections(source.frontmatter) : [];
122
+ const [status, view] = await Promise.all([readCaptureStatus(), newsletterView(locale)]);
123
+ const accepting = status?.newsletter === true;
124
+ const framed = framedSources({
125
+ getMdxSource: getMdxSource ?? loadMdxSource,
126
+ getPageSections: getPageSections ?? loadPageSections,
127
+ view,
128
+ });
129
+ const source = await framed.getMdxSource("NewsletterSignup", "newsletter", locale);
130
+ const declared = source ? await framed.getPageSections(source.frontmatter) : [];
102
131
  const composed = declared.length > 0;
132
+ const slot = declared.find((s) => s.type === "contact-form")?.id;
103
133
  const hasSignupSection = declared.some((s) => s.type === "newsletter");
134
+ const contactEmail = readSiteContactEmail();
135
+
136
+ let surface: ReactElement;
137
+ if (accepting) {
138
+ surface = newsletterSignup(view, {
139
+ locale,
140
+ className: "mx-auto",
141
+ ...(contactEmail !== undefined && { fallbackEmail: contactEmail }),
142
+ });
143
+ } else {
144
+ const tc = await getTranslations({ locale, namespace: "contact" });
145
+ surface = (
146
+ <ContactFallback
147
+ email={contactEmail}
148
+ text={{
149
+ heading: tc("unavailableHeading"),
150
+ body: tc("unavailableBody", { email: "{email}" }),
151
+ noEmail: tc("unavailableNoEmail"),
152
+ }}
153
+ />
154
+ );
155
+ }
104
156
 
105
157
  const rendered = await renderPage({
106
158
  type: "NewsletterSignup",
107
159
  slug: "newsletter",
108
160
  locale,
109
161
  ...(composed ? {} : { bodyMode: "mdx-body" as const }),
110
- ...(getMdxSource !== undefined && { getMdxSource }),
111
- getPageSections: accepting
112
- ? loadSections
113
- : async (frontmatter: unknown) => withoutSignup(await loadSections(frontmatter)),
162
+ getMdxSource: framed.getMdxSource,
163
+ // With a slot the magnet's form is the page's one signup, so an authored
164
+ // `newsletter` section (posting around the magnet) never renders beside it.
165
+ getPageSections:
166
+ accepting && slot === undefined
167
+ ? framed.getPageSections
168
+ : async (frontmatter: unknown) => withoutSignup(await framed.getPageSections(frontmatter)),
169
+ ...(slot !== undefined && { sectionSlots: { [slot]: surface } }),
114
170
  });
115
171
 
116
- if (!accepting || hasSignupSection || source === null) return rendered;
117
-
118
- const [t, tc] = await Promise.all([
119
- getTranslations({ locale, namespace: "newsletter" }),
120
- getTranslations({ locale, namespace: "contact" }),
121
- ]);
172
+ if (slot !== undefined || !accepting || hasSignupSection || source === null) return rendered;
122
173
  return (
123
174
  <>
124
175
  {rendered}
125
- <LeadCaptureForm
126
- action={subscribeNewsletterAction}
127
- fields={[{ name: "email", label: t("emailLabel"), type: "email", required: true }]}
128
- termsHref={localeHref(locale, "/terms")}
129
- text={{
130
- submit: t("submit"),
131
- sending: t("sending"),
132
- success: t("success"),
133
- consentStatement: t("consentStatement"),
134
- genericError: tc("genericError"),
135
- unavailableHeading: tc("unavailableHeading"),
136
- unavailableBody: tc("unavailableBody", { email: "{email}" }),
137
- unavailableNoEmail: tc("unavailableNoEmail"),
138
- }}
139
- />
176
+ {surface}
140
177
  </>
141
178
  );
142
179
  }
@@ -25,7 +25,11 @@ type DeckGrounds = NonNullable<Parameters<typeof renderPage>[0]["deckGrounds"]>;
25
25
  * carries `deckGrounds` only when the site's design sets one (#7719 D3), so
26
26
  * this compiles against both shapes of the generated file.
27
27
  */
28
- const chrome: SiteChrome & { deckGrounds?: DeckGrounds; deckPresentation?: true } = siteChrome;
28
+ const chrome: SiteChrome & {
29
+ deckGrounds?: DeckGrounds;
30
+ deckPresentation?: true;
31
+ deckAnchors?: "bare";
32
+ } = siteChrome;
29
33
 
30
34
  interface Props {
31
35
  params: Promise<{ locale: string }>;
@@ -41,6 +45,8 @@ interface Props {
41
45
  deckGrounds?: DeckGrounds;
42
46
  /** The design's `layout.deckPresentation` (#7740); defaults to the generated `siteChrome`'s. */
43
47
  deckPresentation?: boolean;
48
+ /** The design's `layout.deckAnchors` (#8773): `bare` names the slides `#<id>`; defaults to the generated `siteChrome`'s. */
49
+ deckAnchors?: "bare";
44
50
  }
45
51
 
46
52
  export async function generateMetadata({
@@ -57,6 +63,7 @@ export default async function Page({
57
63
  home = chrome.home,
58
64
  deckGrounds = chrome.deckGrounds,
59
65
  deckPresentation = chrome.deckPresentation,
66
+ deckAnchors = chrome.deckAnchors,
60
67
  }: Props): Promise<ReactElement> {
61
68
  const { locale } = await params;
62
69
  return renderPage({
@@ -83,5 +90,10 @@ export default async function Page({
83
90
  // alignment field centred, card grids one column on phones, and slide
84
91
  // anchors clear of the page's JSON-LD @id fragments.
85
92
  ...(home === "deck" && deckPresentation === true && { deckPresentation: true }),
93
+ // #8773 — and, when the design chooses it, names those slides `#<id>`
94
+ // (the `slide-` name kept only where the bare id would collide).
95
+ ...(home === "deck" &&
96
+ deckPresentation === true &&
97
+ deckAnchors === "bare" && { deckAnchorStyle: "bare" as const }),
86
98
  });
87
99
  }
@@ -68,7 +68,7 @@ describe("partners/page — snapshot regression", () => {
68
68
  });
69
69
  const { container } = render(element);
70
70
  expect(container.innerHTML).toMatchInlineSnapshot(
71
- `"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@type":"CollectionPage","inLanguage":"en","name":"Partners","description":"Organisations partnering with AI Launch Kit.","mainEntity":{"@type":"ItemList","itemListElement":[{"@type":"Organization"}]}}</script><main id="main"><div class="mx-auto w-full max-w-container-xl px-4 md:px-6 lg:px-8 py-section"><h1 class="text-heading-1 lg:text-display-sm font-display text-fg hero-type-display">Partners</h1><p data-testid="partners-description" class="mt-block-tight text-lead">Organisations partnering with AI Launch Kit.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle mt-block-loose"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div class="ailk-prose max-w-none text-(--color-text-muted) text-body-sm leading-relaxed [&amp;>*:first-child]:mt-0 [&amp;_h2]:mt-block-loose [&amp;_h2]:scroll-mt-24 [&amp;_h2]:text-heading-3 [&amp;_h2]:font-display [&amp;_h2]:text-(--color-fg) [&amp;_h3]:mt-block-loose [&amp;_h3]:scroll-mt-24 [&amp;_h3]:text-heading-4 [&amp;_h3]:font-display [&amp;_h3]:text-(--color-fg) [&amp;_h4]:mt-block-tight [&amp;_h4]:scroll-mt-24 [&amp;_h4]:text-heading-4 [&amp;_h4]:font-display [&amp;_h4]:text-(--color-fg) [&amp;_h5]:mt-block-tight [&amp;_h5]:scroll-mt-24 [&amp;_h5]:text-heading-4 [&amp;_h5]:font-display [&amp;_h5]:text-(--color-fg) [&amp;_h6]:mt-block-tight [&amp;_h6]:scroll-mt-24 [&amp;_h6]:text-heading-4 [&amp;_h6]:font-display [&amp;_h6]:text-(--color-fg) [&amp;_p]:mt-paragraph [&amp;_strong]:font-semibold [&amp;_strong]:text-(--color-fg) [&amp;_em]:italic [&amp;_ul]:mt-paragraph [&amp;_ul]:list-disc [&amp;_ul]:ps-6 [&amp;_ol]:mt-paragraph [&amp;_ol]:list-decimal [&amp;_ol]:ps-6 [&amp;_li]:mt-1 [&amp;_li]:ps-1 [&amp;_li]:marker:text-(--color-text-muted) [&amp;_li>ul]:mt-1 [&amp;_li>ol]:mt-1 [&amp;_blockquote]:mt-paragraph [&amp;_blockquote]:border-s-4 [&amp;_blockquote]:border-(--color-border-strong) [&amp;_blockquote]:ps-4 [&amp;_blockquote]:italic [&amp;_blockquote]:text-(--color-text-muted) [&amp;_hr]:my-block-loose [&amp;_hr]:border-(--color-border) [&amp;_a]:text-(--color-accent) [&amp;_a]:underline [&amp;_a]:underline-offset-4 [&amp;_:not(pre)>code]:rounded [&amp;_:not(pre)>code]:bg-(--color-surface-secondary) [&amp;_:not(pre)>code]:px-1.5 [&amp;_:not(pre)>code]:py-0.5 [&amp;_:not(pre)>code]:text-code [&amp;_:not(pre)>code]:font-mono [&amp;_:not(pre)>code]:text-(--color-fg) [&amp;_pre]:mt-paragraph [&amp;_pre]:overflow-auto [&amp;_pre]:rounded-md [&amp;_pre]:border-hairline [&amp;_pre]:border-(--color-border) [&amp;_pre]:bg-(--color-bg) [&amp;_pre]:p-4 [&amp;_pre]:font-mono [&amp;_pre]:text-body-sm [&amp;_img]:mt-paragraph [&amp;_img]:h-auto [&amp;_img]:max-w-full [&amp;_img]:rounded-lg [&amp;_table]:mt-paragraph [&amp;_table]:w-full [&amp;_table]:border-collapse [&amp;_table]:text-body-sm [&amp;_th]:border-b [&amp;_th]:border-(--color-border) [&amp;_th]:bg-(--color-surface-secondary) [&amp;_th]:px-3 [&amp;_th]:py-2 [&amp;_th]:text-start [&amp;_th]:font-semibold [&amp;_th]:text-(--color-fg) [&amp;_td]:border-b [&amp;_td]:border-(--color-border) [&amp;_td]:px-3 [&amp;_td]:py-2 [&amp;_kbd]:rounded [&amp;_kbd]:border-hairline [&amp;_kbd]:border-(--color-border) [&amp;_kbd]:bg-(--color-surface-secondary) [&amp;_kbd]:px-1.5 [&amp;_kbd]:py-0.5 [&amp;_kbd]:text-caption [&amp;_kbd]:font-mono" data-testid="mdx-content"><div data-testid="mdx-rendered">mdx-body-content</div></div></article></div></div></div></main>"`,
71
+ `"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@type":"CollectionPage","inLanguage":"en","name":"Partners","description":"Organisations partnering with AI Launch Kit.","mainEntity":{"@type":"ItemList","itemListElement":[{"@type":"Organization"}]}}</script><main id="main"><div class="mx-auto w-full max-w-container-xl px-4 md:px-6 lg:px-8 py-section"><h1 class="text-heading-1 lg:text-display-sm font-display text-fg hero-type-display">Partners</h1><p data-testid="partners-description" class="mt-block-tight text-lead">Organisations partnering with AI Launch Kit.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle mt-block-loose"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium motion-safe:transition motion-safe:duration-(--button-hover-duration) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 text-(--color-fg) not-disabled:hover:bg-(--button-hover-quiet-bg) not-disabled:hover:text-(--button-hover-quiet-fg) not-disabled:hover:opacity-(--button-hover-opacity) not-disabled:hover:transform-(--button-hover-transform) not-disabled:hover:shadow-(--button-hover-shadow) not-disabled:active:bg-gray-200 not-disabled:active:text-(--color-fg) rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium motion-safe:transition motion-safe:duration-(--button-hover-duration) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 text-(--color-fg) not-disabled:hover:bg-(--button-hover-quiet-bg) not-disabled:hover:text-(--button-hover-quiet-fg) not-disabled:hover:opacity-(--button-hover-opacity) not-disabled:hover:transform-(--button-hover-transform) not-disabled:hover:shadow-(--button-hover-shadow) not-disabled:active:bg-gray-200 not-disabled:active:text-(--color-fg) rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div class="ailk-prose max-w-none text-(--color-text-muted) text-body-sm leading-relaxed [&amp;>*:first-child]:mt-0 [&amp;_h2]:mt-block-loose [&amp;_h2]:scroll-mt-24 [&amp;_h2]:text-heading-3 [&amp;_h2]:font-display [&amp;_h2]:text-(--color-fg) [&amp;_h3]:mt-block-loose [&amp;_h3]:scroll-mt-24 [&amp;_h3]:text-heading-4 [&amp;_h3]:font-display [&amp;_h3]:text-(--color-fg) [&amp;_h4]:mt-block-tight [&amp;_h4]:scroll-mt-24 [&amp;_h4]:text-heading-4 [&amp;_h4]:font-display [&amp;_h4]:text-(--color-fg) [&amp;_h5]:mt-block-tight [&amp;_h5]:scroll-mt-24 [&amp;_h5]:text-heading-4 [&amp;_h5]:font-display [&amp;_h5]:text-(--color-fg) [&amp;_h6]:mt-block-tight [&amp;_h6]:scroll-mt-24 [&amp;_h6]:text-heading-4 [&amp;_h6]:font-display [&amp;_h6]:text-(--color-fg) [&amp;_p]:mt-paragraph [&amp;_strong]:font-semibold [&amp;_strong]:text-(--color-fg) [&amp;_em]:italic [&amp;_ul]:mt-paragraph [&amp;_ul]:list-disc [&amp;_ul]:ps-6 [&amp;_ol]:mt-paragraph [&amp;_ol]:list-decimal [&amp;_ol]:ps-6 [&amp;_li]:mt-1 [&amp;_li]:ps-1 [&amp;_li]:marker:text-(--color-text-muted) [&amp;_li>ul]:mt-1 [&amp;_li>ol]:mt-1 [&amp;_blockquote]:mt-paragraph [&amp;_blockquote]:border-s-4 [&amp;_blockquote]:border-(--color-border-strong) [&amp;_blockquote]:ps-4 [&amp;_blockquote]:italic [&amp;_blockquote]:text-(--color-text-muted) [&amp;_hr]:my-block-loose [&amp;_hr]:border-(--color-border) [&amp;_a]:text-(--color-accent) [&amp;_a]:underline [&amp;_a]:underline-offset-4 [&amp;_:not(pre)>code]:rounded [&amp;_:not(pre)>code]:bg-(--color-surface-secondary) [&amp;_:not(pre)>code]:px-1.5 [&amp;_:not(pre)>code]:py-0.5 [&amp;_:not(pre)>code]:text-code [&amp;_:not(pre)>code]:font-mono [&amp;_:not(pre)>code]:text-(--color-fg) [&amp;_pre]:mt-paragraph [&amp;_pre]:overflow-auto [&amp;_pre]:rounded-md [&amp;_pre]:border-hairline [&amp;_pre]:border-(--color-border) [&amp;_pre]:bg-(--color-bg) [&amp;_pre]:p-4 [&amp;_pre]:font-mono [&amp;_pre]:text-body-sm [&amp;_img]:mt-paragraph [&amp;_img]:h-auto [&amp;_img]:max-w-full [&amp;_img]:rounded-lg [&amp;_table]:mt-paragraph [&amp;_table]:w-full [&amp;_table]:border-collapse [&amp;_table]:text-body-sm [&amp;_th]:border-b [&amp;_th]:border-(--color-border) [&amp;_th]:bg-(--color-surface-secondary) [&amp;_th]:px-3 [&amp;_th]:py-2 [&amp;_th]:text-start [&amp;_th]:font-semibold [&amp;_th]:text-(--color-fg) [&amp;_td]:border-b [&amp;_td]:border-(--color-border) [&amp;_td]:px-3 [&amp;_td]:py-2 [&amp;_kbd]:rounded [&amp;_kbd]:border-hairline [&amp;_kbd]:border-(--color-border) [&amp;_kbd]:bg-(--color-surface-secondary) [&amp;_kbd]:px-1.5 [&amp;_kbd]:py-0.5 [&amp;_kbd]:text-caption [&amp;_kbd]:font-mono" data-testid="mdx-content"><div data-testid="mdx-rendered">mdx-body-content</div></div></article></div></div></div></main>"`,
72
72
  );
73
73
  });
74
74
  });
@@ -93,7 +93,7 @@ describe("partners/[slug]/page — snapshot regression", () => {
93
93
  });
94
94
  const { container } = render(element);
95
95
  expect(container.innerHTML).toMatchInlineSnapshot(
96
- `"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@graph":[{"@context":"https://schema.org","@type":"WebPage","inLanguage":"en","name":"Acme Corp","description":"Acme Corp is a strategic partner.","mainEntity":{"@context":"https://schema.org","@type":"Organization","name":"Acme Corp","description":"Acme Corp is a strategic partner."}},{"@context":"https://schema.org","@type":"BreadcrumbList","inLanguage":"en","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http://localhost:3000/en/"},{"@type":"ListItem","position":2,"name":"Partners","item":"http://localhost:3000/en/partners"},{"@type":"ListItem","position":3,"name":"Acme Corp","item":"http://localhost:3000/en/partners/acme-corp"}]}]}</script><main id="main"><div class="mx-auto w-full max-w-container-xl px-4 md:px-6 lg:px-8 py-section"><h1 class="text-heading-1 lg:text-display-sm font-display text-fg hero-type-display">Acme Corp</h1><p data-testid="acme-corp-description" class="mt-block-tight text-lead">Acme Corp is a strategic partner.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle mt-block-loose"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div class="ailk-prose max-w-none text-(--color-text-muted) text-body-sm leading-relaxed [&amp;>*:first-child]:mt-0 [&amp;_h2]:mt-block-loose [&amp;_h2]:scroll-mt-24 [&amp;_h2]:text-heading-3 [&amp;_h2]:font-display [&amp;_h2]:text-(--color-fg) [&amp;_h3]:mt-block-loose [&amp;_h3]:scroll-mt-24 [&amp;_h3]:text-heading-4 [&amp;_h3]:font-display [&amp;_h3]:text-(--color-fg) [&amp;_h4]:mt-block-tight [&amp;_h4]:scroll-mt-24 [&amp;_h4]:text-heading-4 [&amp;_h4]:font-display [&amp;_h4]:text-(--color-fg) [&amp;_h5]:mt-block-tight [&amp;_h5]:scroll-mt-24 [&amp;_h5]:text-heading-4 [&amp;_h5]:font-display [&amp;_h5]:text-(--color-fg) [&amp;_h6]:mt-block-tight [&amp;_h6]:scroll-mt-24 [&amp;_h6]:text-heading-4 [&amp;_h6]:font-display [&amp;_h6]:text-(--color-fg) [&amp;_p]:mt-paragraph [&amp;_strong]:font-semibold [&amp;_strong]:text-(--color-fg) [&amp;_em]:italic [&amp;_ul]:mt-paragraph [&amp;_ul]:list-disc [&amp;_ul]:ps-6 [&amp;_ol]:mt-paragraph [&amp;_ol]:list-decimal [&amp;_ol]:ps-6 [&amp;_li]:mt-1 [&amp;_li]:ps-1 [&amp;_li]:marker:text-(--color-text-muted) [&amp;_li>ul]:mt-1 [&amp;_li>ol]:mt-1 [&amp;_blockquote]:mt-paragraph [&amp;_blockquote]:border-s-4 [&amp;_blockquote]:border-(--color-border-strong) [&amp;_blockquote]:ps-4 [&amp;_blockquote]:italic [&amp;_blockquote]:text-(--color-text-muted) [&amp;_hr]:my-block-loose [&amp;_hr]:border-(--color-border) [&amp;_a]:text-(--color-accent) [&amp;_a]:underline [&amp;_a]:underline-offset-4 [&amp;_:not(pre)>code]:rounded [&amp;_:not(pre)>code]:bg-(--color-surface-secondary) [&amp;_:not(pre)>code]:px-1.5 [&amp;_:not(pre)>code]:py-0.5 [&amp;_:not(pre)>code]:text-code [&amp;_:not(pre)>code]:font-mono [&amp;_:not(pre)>code]:text-(--color-fg) [&amp;_pre]:mt-paragraph [&amp;_pre]:overflow-auto [&amp;_pre]:rounded-md [&amp;_pre]:border-hairline [&amp;_pre]:border-(--color-border) [&amp;_pre]:bg-(--color-bg) [&amp;_pre]:p-4 [&amp;_pre]:font-mono [&amp;_pre]:text-body-sm [&amp;_img]:mt-paragraph [&amp;_img]:h-auto [&amp;_img]:max-w-full [&amp;_img]:rounded-lg [&amp;_table]:mt-paragraph [&amp;_table]:w-full [&amp;_table]:border-collapse [&amp;_table]:text-body-sm [&amp;_th]:border-b [&amp;_th]:border-(--color-border) [&amp;_th]:bg-(--color-surface-secondary) [&amp;_th]:px-3 [&amp;_th]:py-2 [&amp;_th]:text-start [&amp;_th]:font-semibold [&amp;_th]:text-(--color-fg) [&amp;_td]:border-b [&amp;_td]:border-(--color-border) [&amp;_td]:px-3 [&amp;_td]:py-2 [&amp;_kbd]:rounded [&amp;_kbd]:border-hairline [&amp;_kbd]:border-(--color-border) [&amp;_kbd]:bg-(--color-surface-secondary) [&amp;_kbd]:px-1.5 [&amp;_kbd]:py-0.5 [&amp;_kbd]:text-caption [&amp;_kbd]:font-mono" data-testid="mdx-content"><div data-testid="mdx-rendered">mdx-body-content</div></div></article></div></div></div></main>"`,
96
+ `"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@graph":[{"@context":"https://schema.org","@type":"WebPage","inLanguage":"en","name":"Acme Corp","description":"Acme Corp is a strategic partner.","mainEntity":{"@context":"https://schema.org","@type":"Organization","name":"Acme Corp","description":"Acme Corp is a strategic partner."}},{"@context":"https://schema.org","@type":"BreadcrumbList","inLanguage":"en","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http://localhost:3000/en/"},{"@type":"ListItem","position":2,"name":"Partners","item":"http://localhost:3000/en/partners"},{"@type":"ListItem","position":3,"name":"Acme Corp","item":"http://localhost:3000/en/partners/acme-corp"}]}]}</script><main id="main"><div class="mx-auto w-full max-w-container-xl px-4 md:px-6 lg:px-8 py-section"><h1 class="text-heading-1 lg:text-display-sm font-display text-fg hero-type-display">Acme Corp</h1><p data-testid="acme-corp-description" class="mt-block-tight text-lead">Acme Corp is a strategic partner.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle mt-block-loose"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium motion-safe:transition motion-safe:duration-(--button-hover-duration) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 text-(--color-fg) not-disabled:hover:bg-(--button-hover-quiet-bg) not-disabled:hover:text-(--button-hover-quiet-fg) not-disabled:hover:opacity-(--button-hover-opacity) not-disabled:hover:transform-(--button-hover-transform) not-disabled:hover:shadow-(--button-hover-shadow) not-disabled:active:bg-gray-200 not-disabled:active:text-(--color-fg) rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium motion-safe:transition motion-safe:duration-(--button-hover-duration) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 text-(--color-fg) not-disabled:hover:bg-(--button-hover-quiet-bg) not-disabled:hover:text-(--button-hover-quiet-fg) not-disabled:hover:opacity-(--button-hover-opacity) not-disabled:hover:transform-(--button-hover-transform) not-disabled:hover:shadow-(--button-hover-shadow) not-disabled:active:bg-gray-200 not-disabled:active:text-(--color-fg) rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div class="ailk-prose max-w-none text-(--color-text-muted) text-body-sm leading-relaxed [&amp;>*:first-child]:mt-0 [&amp;_h2]:mt-block-loose [&amp;_h2]:scroll-mt-24 [&amp;_h2]:text-heading-3 [&amp;_h2]:font-display [&amp;_h2]:text-(--color-fg) [&amp;_h3]:mt-block-loose [&amp;_h3]:scroll-mt-24 [&amp;_h3]:text-heading-4 [&amp;_h3]:font-display [&amp;_h3]:text-(--color-fg) [&amp;_h4]:mt-block-tight [&amp;_h4]:scroll-mt-24 [&amp;_h4]:text-heading-4 [&amp;_h4]:font-display [&amp;_h4]:text-(--color-fg) [&amp;_h5]:mt-block-tight [&amp;_h5]:scroll-mt-24 [&amp;_h5]:text-heading-4 [&amp;_h5]:font-display [&amp;_h5]:text-(--color-fg) [&amp;_h6]:mt-block-tight [&amp;_h6]:scroll-mt-24 [&amp;_h6]:text-heading-4 [&amp;_h6]:font-display [&amp;_h6]:text-(--color-fg) [&amp;_p]:mt-paragraph [&amp;_strong]:font-semibold [&amp;_strong]:text-(--color-fg) [&amp;_em]:italic [&amp;_ul]:mt-paragraph [&amp;_ul]:list-disc [&amp;_ul]:ps-6 [&amp;_ol]:mt-paragraph [&amp;_ol]:list-decimal [&amp;_ol]:ps-6 [&amp;_li]:mt-1 [&amp;_li]:ps-1 [&amp;_li]:marker:text-(--color-text-muted) [&amp;_li>ul]:mt-1 [&amp;_li>ol]:mt-1 [&amp;_blockquote]:mt-paragraph [&amp;_blockquote]:border-s-4 [&amp;_blockquote]:border-(--color-border-strong) [&amp;_blockquote]:ps-4 [&amp;_blockquote]:italic [&amp;_blockquote]:text-(--color-text-muted) [&amp;_hr]:my-block-loose [&amp;_hr]:border-(--color-border) [&amp;_a]:text-(--color-accent) [&amp;_a]:underline [&amp;_a]:underline-offset-4 [&amp;_:not(pre)>code]:rounded [&amp;_:not(pre)>code]:bg-(--color-surface-secondary) [&amp;_:not(pre)>code]:px-1.5 [&amp;_:not(pre)>code]:py-0.5 [&amp;_:not(pre)>code]:text-code [&amp;_:not(pre)>code]:font-mono [&amp;_:not(pre)>code]:text-(--color-fg) [&amp;_pre]:mt-paragraph [&amp;_pre]:overflow-auto [&amp;_pre]:rounded-md [&amp;_pre]:border-hairline [&amp;_pre]:border-(--color-border) [&amp;_pre]:bg-(--color-bg) [&amp;_pre]:p-4 [&amp;_pre]:font-mono [&amp;_pre]:text-body-sm [&amp;_img]:mt-paragraph [&amp;_img]:h-auto [&amp;_img]:max-w-full [&amp;_img]:rounded-lg [&amp;_table]:mt-paragraph [&amp;_table]:w-full [&amp;_table]:border-collapse [&amp;_table]:text-body-sm [&amp;_th]:border-b [&amp;_th]:border-(--color-border) [&amp;_th]:bg-(--color-surface-secondary) [&amp;_th]:px-3 [&amp;_th]:py-2 [&amp;_th]:text-start [&amp;_th]:font-semibold [&amp;_th]:text-(--color-fg) [&amp;_td]:border-b [&amp;_td]:border-(--color-border) [&amp;_td]:px-3 [&amp;_td]:py-2 [&amp;_kbd]:rounded [&amp;_kbd]:border-hairline [&amp;_kbd]:border-(--color-border) [&amp;_kbd]:bg-(--color-surface-secondary) [&amp;_kbd]:px-1.5 [&amp;_kbd]:py-0.5 [&amp;_kbd]:text-caption [&amp;_kbd]:font-mono" data-testid="mdx-content"><div data-testid="mdx-rendered">mdx-body-content</div></div></article></div></div></div></main>"`,
97
97
  );
98
98
  });
99
99
 
@@ -0,0 +1,7 @@
1
+ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2
+
3
+ exports[`/pricing on an unconnected site is today's page (#8927 AC-4) renders today's DI page for ?plan=BASIC 1`] = `"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@graph":[{"@context":"https://schema.org","@type":"WebPage","inLanguage":"en","name":"Pricing","description":"Plans for every stage."},{"@context":"https://schema.org","@type":"Offer","name":"Basic","price":"29","priceCurrency":"USD"},{"@context":"https://schema.org","@type":"Offer","name":"Business","price":"79","priceCurrency":"USD"},{"@context":"https://schema.org","@type":"Offer","name":"Agency","price":"199","priceCurrency":"USD"},{"@context":"https://schema.org","@type":"AggregateOffer","priceCurrency":"USD","lowPrice":"29","highPrice":"199","offerCount":3}]}</script><main id="main"><div class="mx-auto w-full max-w-container-xl px-4 md:px-6 lg:px-8 py-section"><h1 class="text-heading-1 lg:text-display-sm font-display text-fg hero-type-display">Pricing</h1><p data-testid="pricing-description" class="mt-block-tight text-lead">Plans for every stage.</p></div><div class="ailk-section-renderer" data-testid="section-renderer"><span id="section-plans" class="ailk-section-anchor" aria-hidden="true" data-testid="section-anchor"></span><section class="py-section bg-(--color-bg)" data-testid="pricing" data-section="pricing"><div class="mx-auto w-full max-w-container-xl px-4 md:px-6 lg:px-8"><span hidden="" data-testid="pricing-recommended-anchor"></span><div class="grid gap-grid md:grid-cols-2 lg:grid-cols-3 mt-10 items-stretch pricing-card-row-grid" data-testid="pricing-grid" data-separation="gapped"><div class="ailk-pricing-tier-card pricing-card-subgrid" data-testid="pricing-tier-card" aria-current="true"><div class="pricing-card-subgrid-body"><div class="h-full rounded-card p-card pricing-card-subgrid border-thin border-(--color-brand) bg-(--color-surface-raised)" data-testid="card"><div class="row-start-1 flex flex-wrap items-center gap-2 justify-between"><h3 class="text-heading-3 font-display text-fg text-2xl font-normal">Basic</h3><div class="group inline-flex items-center border-hairline rounded-full transition-colors focus-visible:outline-none bg-(--color-brand) border-transparent text-(--color-fg-on-brand) hover:bg-(--color-brand)/90 focus-visible:bg-(--color-brand)/90 ps-2 pe-2 py-0.5 text-body-sm" data-testid="tier-badge">Recommended for you</div></div><div class="row-start-3 mt-6"><h4 class="text-heading-4 font-display text-fg text-4xl font-medium tracking-tight">$29</h4><span class="text-body font-body leading-normal text-fg mt-1 text-body-sm text-(--color-text-muted)">/month</span></div><div class="row-start-5 mt-6"><a class="inline-flex items-center justify-center gap-2 font-medium motion-safe:transition motion-safe:duration-(--button-hover-duration) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 bg-(--color-brand) text-(--color-fg-on-brand) not-disabled:hover:bg-(--button-hover-bg) not-disabled:hover:text-(--button-hover-fg) not-disabled:hover:outline not-disabled:hover:outline-1 not-disabled:hover:-outline-offset-1 not-disabled:hover:outline-(--button-hover-border) not-disabled:hover:opacity-(--button-hover-opacity) not-disabled:hover:transform-(--button-hover-transform) not-disabled:hover:shadow-(--button-hover-shadow) not-disabled:active:opacity-80 rounded-control px-control-x min-h-(--control-height-big) w-full" href="/checkout/basic" data-testid="tier-cta">Choose Basic</a></div><ul class="row-start-6 mt-block-loose space-y-3" role="list"><li class="flex items-start gap-2"><span data-testid="feature-check-circle" class="mt-0.5 inline-flex size-5 shrink-0 items-center justify-center rounded-full check-badge"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check size-[var(--icon-size-xs)] check-glyph" aria-label="Included" role="img"><path d="M20 6 9 17l-5-5"></path></svg></span><span class="text-body font-body leading-normal text-fg text-body-sm text-(--color-fg)">One site</span></li></ul></div></div></div><div class="ailk-pricing-tier-card pricing-card-subgrid" data-testid="pricing-tier-card"><div class="pricing-card-subgrid-body"><div class="h-full rounded-card p-card pricing-card-subgrid border-hairline border-(--color-border) bg-(--color-surface-raised)" data-testid="card"><div class="row-start-1 flex flex-wrap items-center gap-2 justify-between"><h3 class="text-heading-3 font-display text-fg text-2xl font-normal">Business</h3></div><div class="row-start-3 mt-6"><h4 class="text-heading-4 font-display text-fg text-4xl font-medium tracking-tight">$79</h4><span class="text-body font-body leading-normal text-fg mt-1 text-body-sm text-(--color-text-muted)">/month</span></div><div class="row-start-5 mt-6"><a class="inline-flex items-center justify-center gap-2 font-medium motion-safe:transition motion-safe:duration-(--button-hover-duration) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 border-hairline border-(--color-border) text-(--color-fg) bg-transparent not-disabled:hover:bg-(--button-hover-quiet-bg) not-disabled:hover:text-(--button-hover-quiet-fg) not-disabled:hover:opacity-(--button-hover-opacity) not-disabled:hover:transform-(--button-hover-transform) not-disabled:hover:shadow-(--button-hover-shadow) not-disabled:active:bg-gray-200 not-disabled:active:text-(--color-fg) rounded-control px-control-x min-h-(--control-height-big) w-full" href="/checkout/business" data-testid="tier-cta">Choose Business</a></div><ul class="row-start-6 mt-block-loose space-y-3" role="list"><li class="flex items-start gap-2"><span data-testid="feature-check-circle" class="mt-0.5 inline-flex size-5 shrink-0 items-center justify-center rounded-full check-badge"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check size-[var(--icon-size-xs)] check-glyph" aria-label="Included" role="img"><path d="M20 6 9 17l-5-5"></path></svg></span><span class="text-body font-body leading-normal text-fg text-body-sm text-(--color-fg)">Three sites</span></li></ul></div></div></div><div class="ailk-pricing-tier-card pricing-card-subgrid" data-testid="pricing-tier-card"><div class="pricing-card-subgrid-body"><div class="h-full rounded-card p-card pricing-card-subgrid border-hairline border-(--color-border) bg-(--color-surface-raised)" data-testid="card"><div class="row-start-1 flex flex-wrap items-center gap-2 justify-between"><h3 class="text-heading-3 font-display text-fg text-2xl font-normal">Agency</h3></div><div class="row-start-3 mt-6"><h4 class="text-heading-4 font-display text-fg text-4xl font-medium tracking-tight">$199</h4><span class="text-body font-body leading-normal text-fg mt-1 text-body-sm text-(--color-text-muted)">/month</span></div><div class="row-start-5 mt-6"><a class="inline-flex items-center justify-center gap-2 font-medium motion-safe:transition motion-safe:duration-(--button-hover-duration) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 border-hairline border-(--color-border) text-(--color-fg) bg-transparent not-disabled:hover:bg-(--button-hover-quiet-bg) not-disabled:hover:text-(--button-hover-quiet-fg) not-disabled:hover:opacity-(--button-hover-opacity) not-disabled:hover:transform-(--button-hover-transform) not-disabled:hover:shadow-(--button-hover-shadow) not-disabled:active:bg-gray-200 not-disabled:active:text-(--color-fg) rounded-control px-control-x min-h-(--control-height-big) w-full" href="/checkout/agency" data-testid="tier-cta">Choose Agency</a></div><ul class="row-start-6 mt-block-loose space-y-3" role="list"><li class="flex items-start gap-2"><span data-testid="feature-check-circle" class="mt-0.5 inline-flex size-5 shrink-0 items-center justify-center rounded-full check-badge"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check size-[var(--icon-size-xs)] check-glyph" aria-label="Included" role="img"><path d="M20 6 9 17l-5-5"></path></svg></span><span class="text-body font-body leading-normal text-fg text-body-sm text-(--color-fg)">Unlimited sites</span></li></ul></div></div></div></div></div></section></div></main>"`;
4
+
5
+ exports[`/pricing on an unconnected site is today's page (#8927 AC-4) renders today's DI page for ?plan=agency 1`] = `"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@graph":[{"@context":"https://schema.org","@type":"WebPage","inLanguage":"en","name":"Pricing","description":"Plans for every stage."},{"@context":"https://schema.org","@type":"Offer","name":"Basic","price":"29","priceCurrency":"USD"},{"@context":"https://schema.org","@type":"Offer","name":"Business","price":"79","priceCurrency":"USD"},{"@context":"https://schema.org","@type":"Offer","name":"Agency","price":"199","priceCurrency":"USD"},{"@context":"https://schema.org","@type":"AggregateOffer","priceCurrency":"USD","lowPrice":"29","highPrice":"199","offerCount":3}]}</script><main id="main"><div class="mx-auto w-full max-w-container-xl px-4 md:px-6 lg:px-8 py-section"><h1 class="text-heading-1 lg:text-display-sm font-display text-fg hero-type-display">Pricing</h1><p data-testid="pricing-description" class="mt-block-tight text-lead">Plans for every stage.</p></div><div class="ailk-section-renderer" data-testid="section-renderer"><span id="section-plans" class="ailk-section-anchor" aria-hidden="true" data-testid="section-anchor"></span><section class="py-section bg-(--color-bg)" data-testid="pricing" data-section="pricing"><div class="mx-auto w-full max-w-container-xl px-4 md:px-6 lg:px-8"><span hidden="" data-testid="pricing-recommended-anchor"></span><div class="grid gap-grid md:grid-cols-2 lg:grid-cols-3 mt-10 items-stretch pricing-card-row-grid" data-testid="pricing-grid" data-separation="gapped"><div class="ailk-pricing-tier-card pricing-card-subgrid" data-testid="pricing-tier-card"><div class="pricing-card-subgrid-body"><div class="h-full rounded-card p-card pricing-card-subgrid border-hairline border-(--color-border) bg-(--color-surface-raised)" data-testid="card"><div class="row-start-1 flex flex-wrap items-center gap-2 justify-between"><h3 class="text-heading-3 font-display text-fg text-2xl font-normal">Basic</h3></div><div class="row-start-3 mt-6"><h4 class="text-heading-4 font-display text-fg text-4xl font-medium tracking-tight">$29</h4><span class="text-body font-body leading-normal text-fg mt-1 text-body-sm text-(--color-text-muted)">/month</span></div><div class="row-start-5 mt-6"><a class="inline-flex items-center justify-center gap-2 font-medium motion-safe:transition motion-safe:duration-(--button-hover-duration) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 border-hairline border-(--color-border) text-(--color-fg) bg-transparent not-disabled:hover:bg-(--button-hover-quiet-bg) not-disabled:hover:text-(--button-hover-quiet-fg) not-disabled:hover:opacity-(--button-hover-opacity) not-disabled:hover:transform-(--button-hover-transform) not-disabled:hover:shadow-(--button-hover-shadow) not-disabled:active:bg-gray-200 not-disabled:active:text-(--color-fg) rounded-control px-control-x min-h-(--control-height-big) w-full" href="/checkout/basic" data-testid="tier-cta">Choose Basic</a></div><ul class="row-start-6 mt-block-loose space-y-3" role="list"><li class="flex items-start gap-2"><span data-testid="feature-check-circle" class="mt-0.5 inline-flex size-5 shrink-0 items-center justify-center rounded-full check-badge"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check size-[var(--icon-size-xs)] check-glyph" aria-label="Included" role="img"><path d="M20 6 9 17l-5-5"></path></svg></span><span class="text-body font-body leading-normal text-fg text-body-sm text-(--color-fg)">One site</span></li></ul></div></div></div><div class="ailk-pricing-tier-card pricing-card-subgrid" data-testid="pricing-tier-card"><div class="pricing-card-subgrid-body"><div class="h-full rounded-card p-card pricing-card-subgrid border-hairline border-(--color-border) bg-(--color-surface-raised)" data-testid="card"><div class="row-start-1 flex flex-wrap items-center gap-2 justify-between"><h3 class="text-heading-3 font-display text-fg text-2xl font-normal">Business</h3></div><div class="row-start-3 mt-6"><h4 class="text-heading-4 font-display text-fg text-4xl font-medium tracking-tight">$79</h4><span class="text-body font-body leading-normal text-fg mt-1 text-body-sm text-(--color-text-muted)">/month</span></div><div class="row-start-5 mt-6"><a class="inline-flex items-center justify-center gap-2 font-medium motion-safe:transition motion-safe:duration-(--button-hover-duration) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 border-hairline border-(--color-border) text-(--color-fg) bg-transparent not-disabled:hover:bg-(--button-hover-quiet-bg) not-disabled:hover:text-(--button-hover-quiet-fg) not-disabled:hover:opacity-(--button-hover-opacity) not-disabled:hover:transform-(--button-hover-transform) not-disabled:hover:shadow-(--button-hover-shadow) not-disabled:active:bg-gray-200 not-disabled:active:text-(--color-fg) rounded-control px-control-x min-h-(--control-height-big) w-full" href="/checkout/business" data-testid="tier-cta">Choose Business</a></div><ul class="row-start-6 mt-block-loose space-y-3" role="list"><li class="flex items-start gap-2"><span data-testid="feature-check-circle" class="mt-0.5 inline-flex size-5 shrink-0 items-center justify-center rounded-full check-badge"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check size-[var(--icon-size-xs)] check-glyph" aria-label="Included" role="img"><path d="M20 6 9 17l-5-5"></path></svg></span><span class="text-body font-body leading-normal text-fg text-body-sm text-(--color-fg)">Three sites</span></li></ul></div></div></div><div class="ailk-pricing-tier-card pricing-card-subgrid" data-testid="pricing-tier-card" aria-current="true"><div class="pricing-card-subgrid-body"><div class="h-full rounded-card p-card pricing-card-subgrid border-thin border-(--color-brand) bg-(--color-surface-raised)" data-testid="card"><div class="row-start-1 flex flex-wrap items-center gap-2 justify-between"><h3 class="text-heading-3 font-display text-fg text-2xl font-normal">Agency</h3><div class="group inline-flex items-center border-hairline rounded-full transition-colors focus-visible:outline-none bg-(--color-brand) border-transparent text-(--color-fg-on-brand) hover:bg-(--color-brand)/90 focus-visible:bg-(--color-brand)/90 ps-2 pe-2 py-0.5 text-body-sm" data-testid="tier-badge">Recommended for you</div></div><div class="row-start-3 mt-6"><h4 class="text-heading-4 font-display text-fg text-4xl font-medium tracking-tight">$199</h4><span class="text-body font-body leading-normal text-fg mt-1 text-body-sm text-(--color-text-muted)">/month</span></div><div class="row-start-5 mt-6"><a class="inline-flex items-center justify-center gap-2 font-medium motion-safe:transition motion-safe:duration-(--button-hover-duration) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 bg-(--color-brand) text-(--color-fg-on-brand) not-disabled:hover:bg-(--button-hover-bg) not-disabled:hover:text-(--button-hover-fg) not-disabled:hover:outline not-disabled:hover:outline-1 not-disabled:hover:-outline-offset-1 not-disabled:hover:outline-(--button-hover-border) not-disabled:hover:opacity-(--button-hover-opacity) not-disabled:hover:transform-(--button-hover-transform) not-disabled:hover:shadow-(--button-hover-shadow) not-disabled:active:opacity-80 rounded-control px-control-x min-h-(--control-height-big) w-full" href="/checkout/agency" data-testid="tier-cta">Choose Agency</a></div><ul class="row-start-6 mt-block-loose space-y-3" role="list"><li class="flex items-start gap-2"><span data-testid="feature-check-circle" class="mt-0.5 inline-flex size-5 shrink-0 items-center justify-center rounded-full check-badge"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check size-[var(--icon-size-xs)] check-glyph" aria-label="Included" role="img"><path d="M20 6 9 17l-5-5"></path></svg></span><span class="text-body font-body leading-normal text-fg text-body-sm text-(--color-fg)">Unlimited sites</span></li></ul></div></div></div></div></div></section></div></main>"`;
6
+
7
+ exports[`/pricing on an unconnected site is today's page (#8927 AC-4) renders today's DI page for no plan 1`] = `"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@graph":[{"@context":"https://schema.org","@type":"WebPage","inLanguage":"en","name":"Pricing","description":"Plans for every stage."},{"@context":"https://schema.org","@type":"Offer","name":"Basic","price":"29","priceCurrency":"USD"},{"@context":"https://schema.org","@type":"Offer","name":"Business","price":"79","priceCurrency":"USD"},{"@context":"https://schema.org","@type":"Offer","name":"Agency","price":"199","priceCurrency":"USD"},{"@context":"https://schema.org","@type":"AggregateOffer","priceCurrency":"USD","lowPrice":"29","highPrice":"199","offerCount":3}]}</script><main id="main"><div class="mx-auto w-full max-w-container-xl px-4 md:px-6 lg:px-8 py-section"><h1 class="text-heading-1 lg:text-display-sm font-display text-fg hero-type-display">Pricing</h1><p data-testid="pricing-description" class="mt-block-tight text-lead">Plans for every stage.</p></div><div class="ailk-section-renderer" data-testid="section-renderer"><span id="section-plans" class="ailk-section-anchor" aria-hidden="true" data-testid="section-anchor"></span><section class="py-section bg-(--color-bg)" data-testid="pricing" data-section="pricing"><div class="mx-auto w-full max-w-container-xl px-4 md:px-6 lg:px-8"><div class="grid gap-grid md:grid-cols-2 lg:grid-cols-3 mt-10 items-stretch pricing-card-row-grid" data-testid="pricing-grid" data-separation="gapped"><div class="ailk-pricing-tier-card pricing-card-subgrid" data-testid="pricing-tier-card"><div class="pricing-card-subgrid-body"><div class="h-full rounded-card p-card pricing-card-subgrid border-hairline border-(--color-border) bg-(--color-surface-raised)" data-testid="card"><div class="row-start-1 flex flex-wrap items-center gap-2 justify-between"><h3 class="text-heading-3 font-display text-fg text-2xl font-normal">Basic</h3></div><div class="row-start-3 mt-6"><h4 class="text-heading-4 font-display text-fg text-4xl font-medium tracking-tight">$29</h4><span class="text-body font-body leading-normal text-fg mt-1 text-body-sm text-(--color-text-muted)">/month</span></div><div class="row-start-5 mt-6"><a class="inline-flex items-center justify-center gap-2 font-medium motion-safe:transition motion-safe:duration-(--button-hover-duration) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 border-hairline border-(--color-border) text-(--color-fg) bg-transparent not-disabled:hover:bg-(--button-hover-quiet-bg) not-disabled:hover:text-(--button-hover-quiet-fg) not-disabled:hover:opacity-(--button-hover-opacity) not-disabled:hover:transform-(--button-hover-transform) not-disabled:hover:shadow-(--button-hover-shadow) not-disabled:active:bg-gray-200 not-disabled:active:text-(--color-fg) rounded-control px-control-x min-h-(--control-height-big) w-full" href="/checkout/basic" data-testid="tier-cta">Choose Basic</a></div><ul class="row-start-6 mt-block-loose space-y-3" role="list"><li class="flex items-start gap-2"><span data-testid="feature-check-circle" class="mt-0.5 inline-flex size-5 shrink-0 items-center justify-center rounded-full check-badge"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check size-[var(--icon-size-xs)] check-glyph" aria-label="Included" role="img"><path d="M20 6 9 17l-5-5"></path></svg></span><span class="text-body font-body leading-normal text-fg text-body-sm text-(--color-fg)">One site</span></li></ul></div></div></div><div class="ailk-pricing-tier-card pricing-card-subgrid" data-testid="pricing-tier-card" aria-current="true"><div class="pricing-card-subgrid-body"><div class="h-full rounded-card p-card pricing-card-subgrid border-thin border-(--color-brand) bg-(--color-surface-raised)" data-testid="card"><div class="row-start-1 flex flex-wrap items-center gap-2 justify-between"><h3 class="text-heading-3 font-display text-fg text-2xl font-normal">Business</h3><div class="group inline-flex items-center border-hairline rounded-full transition-colors focus-visible:outline-none bg-(--color-brand) border-transparent text-(--color-fg-on-brand) hover:bg-(--color-brand)/90 focus-visible:bg-(--color-brand)/90 ps-2 pe-2 py-0.5 text-body-sm" data-testid="tier-badge">Recommended</div></div><div class="row-start-3 mt-6"><h4 class="text-heading-4 font-display text-fg text-4xl font-medium tracking-tight">$79</h4><span class="text-body font-body leading-normal text-fg mt-1 text-body-sm text-(--color-text-muted)">/month</span></div><div class="row-start-5 mt-6"><a class="inline-flex items-center justify-center gap-2 font-medium motion-safe:transition motion-safe:duration-(--button-hover-duration) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 bg-(--color-brand) text-(--color-fg-on-brand) not-disabled:hover:bg-(--button-hover-bg) not-disabled:hover:text-(--button-hover-fg) not-disabled:hover:outline not-disabled:hover:outline-1 not-disabled:hover:-outline-offset-1 not-disabled:hover:outline-(--button-hover-border) not-disabled:hover:opacity-(--button-hover-opacity) not-disabled:hover:transform-(--button-hover-transform) not-disabled:hover:shadow-(--button-hover-shadow) not-disabled:active:opacity-80 rounded-control px-control-x min-h-(--control-height-big) w-full" href="/checkout/business" data-testid="tier-cta">Choose Business</a></div><ul class="row-start-6 mt-block-loose space-y-3" role="list"><li class="flex items-start gap-2"><span data-testid="feature-check-circle" class="mt-0.5 inline-flex size-5 shrink-0 items-center justify-center rounded-full check-badge"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check size-[var(--icon-size-xs)] check-glyph" aria-label="Included" role="img"><path d="M20 6 9 17l-5-5"></path></svg></span><span class="text-body font-body leading-normal text-fg text-body-sm text-(--color-fg)">Three sites</span></li></ul></div></div></div><div class="ailk-pricing-tier-card pricing-card-subgrid" data-testid="pricing-tier-card"><div class="pricing-card-subgrid-body"><div class="h-full rounded-card p-card pricing-card-subgrid border-hairline border-(--color-border) bg-(--color-surface-raised)" data-testid="card"><div class="row-start-1 flex flex-wrap items-center gap-2 justify-between"><h3 class="text-heading-3 font-display text-fg text-2xl font-normal">Agency</h3></div><div class="row-start-3 mt-6"><h4 class="text-heading-4 font-display text-fg text-4xl font-medium tracking-tight">$199</h4><span class="text-body font-body leading-normal text-fg mt-1 text-body-sm text-(--color-text-muted)">/month</span></div><div class="row-start-5 mt-6"><a class="inline-flex items-center justify-center gap-2 font-medium motion-safe:transition motion-safe:duration-(--button-hover-duration) focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&amp;>svg]:shrink-0 border-hairline border-(--color-border) text-(--color-fg) bg-transparent not-disabled:hover:bg-(--button-hover-quiet-bg) not-disabled:hover:text-(--button-hover-quiet-fg) not-disabled:hover:opacity-(--button-hover-opacity) not-disabled:hover:transform-(--button-hover-transform) not-disabled:hover:shadow-(--button-hover-shadow) not-disabled:active:bg-gray-200 not-disabled:active:text-(--color-fg) rounded-control px-control-x min-h-(--control-height-big) w-full" href="/checkout/agency" data-testid="tier-cta">Choose Agency</a></div><ul class="row-start-6 mt-block-loose space-y-3" role="list"><li class="flex items-start gap-2"><span data-testid="feature-check-circle" class="mt-0.5 inline-flex size-5 shrink-0 items-center justify-center rounded-full check-badge"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check size-[var(--icon-size-xs)] check-glyph" aria-label="Included" role="img"><path d="M20 6 9 17l-5-5"></path></svg></span><span class="text-body font-body leading-normal text-fg text-body-sm text-(--color-fg)">Unlimited sites</span></li></ul></div></div></div></div></div></section></div></main>"`;