@aglyn/aglyn 1.0.0-beta.143

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 (963) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +11 -0
  3. package/package.json +66 -0
  4. package/src/index.d.ts +25 -0
  5. package/src/index.js +26 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/aglyn.d.ts +31 -0
  8. package/src/lib/aglyn.js +114 -0
  9. package/src/lib/aglyn.js.map +1 -0
  10. package/src/lib/app-utils/abuse-report.d.ts +193 -0
  11. package/src/lib/app-utils/abuse-report.js +321 -0
  12. package/src/lib/app-utils/abuse-report.js.map +1 -0
  13. package/src/lib/app-utils/accept-negotiation.d.ts +129 -0
  14. package/src/lib/app-utils/accept-negotiation.js +266 -0
  15. package/src/lib/app-utils/accept-negotiation.js.map +1 -0
  16. package/src/lib/app-utils/account-emails.d.ts +263 -0
  17. package/src/lib/app-utils/account-emails.js +264 -0
  18. package/src/lib/app-utils/account-emails.js.map +1 -0
  19. package/src/lib/app-utils/actions.d.ts +717 -0
  20. package/src/lib/app-utils/actions.js +1031 -0
  21. package/src/lib/app-utils/actions.js.map +1 -0
  22. package/src/lib/app-utils/activity-presenter.d.ts +152 -0
  23. package/src/lib/app-utils/activity-presenter.js +326 -0
  24. package/src/lib/app-utils/activity-presenter.js.map +1 -0
  25. package/src/lib/app-utils/advertising-tag-mounts.d.ts +133 -0
  26. package/src/lib/app-utils/advertising-tag-mounts.js +118 -0
  27. package/src/lib/app-utils/advertising-tag-mounts.js.map +1 -0
  28. package/src/lib/app-utils/advertising-tags.d.ts +307 -0
  29. package/src/lib/app-utils/advertising-tags.js +525 -0
  30. package/src/lib/app-utils/advertising-tags.js.map +1 -0
  31. package/src/lib/app-utils/agent-api-catalog.d.ts +94 -0
  32. package/src/lib/app-utils/agent-api-catalog.js +106 -0
  33. package/src/lib/app-utils/agent-api-catalog.js.map +1 -0
  34. package/src/lib/app-utils/agent-openapi.d.ts +103 -0
  35. package/src/lib/app-utils/agent-openapi.js +1128 -0
  36. package/src/lib/app-utils/agent-openapi.js.map +1 -0
  37. package/src/lib/app-utils/analytics-beacon.d.ts +154 -0
  38. package/src/lib/app-utils/analytics-beacon.js +152 -0
  39. package/src/lib/app-utils/analytics-beacon.js.map +1 -0
  40. package/src/lib/app-utils/analytics-environment.d.ts +133 -0
  41. package/src/lib/app-utils/analytics-environment.js +152 -0
  42. package/src/lib/app-utils/analytics-environment.js.map +1 -0
  43. package/src/lib/app-utils/analytics-events.d.ts +744 -0
  44. package/src/lib/app-utils/analytics-events.js +650 -0
  45. package/src/lib/app-utils/analytics-events.js.map +1 -0
  46. package/src/lib/app-utils/analytics-link-clicks.d.ts +102 -0
  47. package/src/lib/app-utils/analytics-link-clicks.js +267 -0
  48. package/src/lib/app-utils/analytics-link-clicks.js.map +1 -0
  49. package/src/lib/app-utils/analytics-path-key.d.ts +46 -0
  50. package/src/lib/app-utils/analytics-path-key.js +46 -0
  51. package/src/lib/app-utils/analytics-path-key.js.map +1 -0
  52. package/src/lib/app-utils/api-adapter.d.ts +47 -0
  53. package/src/lib/app-utils/api-adapter.js +367 -0
  54. package/src/lib/app-utils/api-adapter.js.map +1 -0
  55. package/src/lib/app-utils/api-idempotency.d.ts +169 -0
  56. package/src/lib/app-utils/api-idempotency.js +213 -0
  57. package/src/lib/app-utils/api-idempotency.js.map +1 -0
  58. package/src/lib/app-utils/api-plugins.d.ts +210 -0
  59. package/src/lib/app-utils/api-plugins.js +221 -0
  60. package/src/lib/app-utils/api-plugins.js.map +1 -0
  61. package/src/lib/app-utils/assist-credits.d.ts +482 -0
  62. package/src/lib/app-utils/assist-credits.js +509 -0
  63. package/src/lib/app-utils/assist-credits.js.map +1 -0
  64. package/src/lib/app-utils/attribution-attribute.d.ts +34 -0
  65. package/src/lib/app-utils/attribution-attribute.js +34 -0
  66. package/src/lib/app-utils/attribution-attribute.js.map +1 -0
  67. package/src/lib/app-utils/attribution-guard.d.ts +130 -0
  68. package/src/lib/app-utils/attribution-guard.js +410 -0
  69. package/src/lib/app-utils/attribution-guard.js.map +1 -0
  70. package/src/lib/app-utils/author-css.d.ts +123 -0
  71. package/src/lib/app-utils/author-css.js +275 -0
  72. package/src/lib/app-utils/author-css.js.map +1 -0
  73. package/src/lib/app-utils/author-html-markdown.d.ts +31 -0
  74. package/src/lib/app-utils/author-html-markdown.js +497 -0
  75. package/src/lib/app-utils/author-html-markdown.js.map +1 -0
  76. package/src/lib/app-utils/author-html.d.ts +135 -0
  77. package/src/lib/app-utils/author-html.js +1037 -0
  78. package/src/lib/app-utils/author-html.js.map +1 -0
  79. package/src/lib/app-utils/automation-placeholders.d.ts +72 -0
  80. package/src/lib/app-utils/automation-placeholders.js +207 -0
  81. package/src/lib/app-utils/automation-placeholders.js.map +1 -0
  82. package/src/lib/app-utils/bandwidth-cap.d.ts +183 -0
  83. package/src/lib/app-utils/bandwidth-cap.js +193 -0
  84. package/src/lib/app-utils/bandwidth-cap.js.map +1 -0
  85. package/src/lib/app-utils/binding-token-catalog.d.ts +82 -0
  86. package/src/lib/app-utils/binding-token-catalog.js +293 -0
  87. package/src/lib/app-utils/binding-token-catalog.js.map +1 -0
  88. package/src/lib/app-utils/binding-tokens.d.ts +157 -0
  89. package/src/lib/app-utils/binding-tokens.js +251 -0
  90. package/src/lib/app-utils/binding-tokens.js.map +1 -0
  91. package/src/lib/app-utils/breadcrumb-json-ld.d.ts +57 -0
  92. package/src/lib/app-utils/breadcrumb-json-ld.js +67 -0
  93. package/src/lib/app-utils/breadcrumb-json-ld.js.map +1 -0
  94. package/src/lib/app-utils/campaign-attribution.d.ts +117 -0
  95. package/src/lib/app-utils/campaign-attribution.js +162 -0
  96. package/src/lib/app-utils/campaign-attribution.js.map +1 -0
  97. package/src/lib/app-utils/campaign-forwarding.d.ts +220 -0
  98. package/src/lib/app-utils/campaign-forwarding.js +332 -0
  99. package/src/lib/app-utils/campaign-forwarding.js.map +1 -0
  100. package/src/lib/app-utils/campaign-membership.d.ts +109 -0
  101. package/src/lib/app-utils/campaign-membership.js +176 -0
  102. package/src/lib/app-utils/campaign-membership.js.map +1 -0
  103. package/src/lib/app-utils/campaign-touch.d.ts +206 -0
  104. package/src/lib/app-utils/campaign-touch.js +326 -0
  105. package/src/lib/app-utils/campaign-touch.js.map +1 -0
  106. package/src/lib/app-utils/child-contract-compose.d.ts +28 -0
  107. package/src/lib/app-utils/child-contract-compose.js +140 -0
  108. package/src/lib/app-utils/child-contract-compose.js.map +1 -0
  109. package/src/lib/app-utils/child-contract.d.ts +129 -0
  110. package/src/lib/app-utils/child-contract.js +153 -0
  111. package/src/lib/app-utils/child-contract.js.map +1 -0
  112. package/src/lib/app-utils/collection-delete.d.ts +113 -0
  113. package/src/lib/app-utils/collection-delete.js +120 -0
  114. package/src/lib/app-utils/collection-delete.js.map +1 -0
  115. package/src/lib/app-utils/collection-entries.d.ts +833 -0
  116. package/src/lib/app-utils/collection-entries.js +1285 -0
  117. package/src/lib/app-utils/collection-entries.js.map +1 -0
  118. package/src/lib/app-utils/collection-entry-date.d.ts +129 -0
  119. package/src/lib/app-utils/collection-entry-date.js +183 -0
  120. package/src/lib/app-utils/collection-entry-date.js.map +1 -0
  121. package/src/lib/app-utils/collection-kind.d.ts +55 -0
  122. package/src/lib/app-utils/collection-kind.js +69 -0
  123. package/src/lib/app-utils/collection-kind.js.map +1 -0
  124. package/src/lib/app-utils/collection-slug.d.ts +79 -0
  125. package/src/lib/app-utils/collection-slug.js +70 -0
  126. package/src/lib/app-utils/collection-slug.js.map +1 -0
  127. package/src/lib/app-utils/compose-layout-nodes.d.ts +147 -0
  128. package/src/lib/app-utils/compose-layout-nodes.js +261 -0
  129. package/src/lib/app-utils/compose-layout-nodes.js.map +1 -0
  130. package/src/lib/app-utils/compose-layout-props.d.ts +71 -0
  131. package/src/lib/app-utils/compose-layout-props.js +61 -0
  132. package/src/lib/app-utils/compose-layout-props.js.map +1 -0
  133. package/src/lib/app-utils/compose-reusable-components.d.ts +504 -0
  134. package/src/lib/app-utils/compose-reusable-components.js +1366 -0
  135. package/src/lib/app-utils/compose-reusable-components.js.map +1 -0
  136. package/src/lib/app-utils/compress.d.ts +35 -0
  137. package/src/lib/app-utils/compress.js +38 -0
  138. package/src/lib/app-utils/compress.js.map +1 -0
  139. package/src/lib/app-utils/consent-banner-ui.d.ts +226 -0
  140. package/src/lib/app-utils/consent-banner-ui.js +583 -0
  141. package/src/lib/app-utils/consent-banner-ui.js.map +1 -0
  142. package/src/lib/app-utils/consent-groups.d.ts +189 -0
  143. package/src/lib/app-utils/consent-groups.js +219 -0
  144. package/src/lib/app-utils/consent-groups.js.map +1 -0
  145. package/src/lib/app-utils/consent-preferences-dialog.d.ts +58 -0
  146. package/src/lib/app-utils/consent-preferences-dialog.js +106 -0
  147. package/src/lib/app-utils/consent-preferences-dialog.js.map +1 -0
  148. package/src/lib/app-utils/console-record-links.d.ts +163 -0
  149. package/src/lib/app-utils/console-record-links.js +178 -0
  150. package/src/lib/app-utils/console-record-links.js.map +1 -0
  151. package/src/lib/app-utils/console-routes.d.ts +658 -0
  152. package/src/lib/app-utils/console-routes.js +489 -0
  153. package/src/lib/app-utils/console-routes.js.map +1 -0
  154. package/src/lib/app-utils/console-widget-slot-context.d.ts +46 -0
  155. package/src/lib/app-utils/console-widget-slot-context.js +23 -0
  156. package/src/lib/app-utils/console-widget-slot-context.js.map +1 -0
  157. package/src/lib/app-utils/contact-custom-fields.d.ts +191 -0
  158. package/src/lib/app-utils/contact-custom-fields.js +349 -0
  159. package/src/lib/app-utils/contact-custom-fields.js.map +1 -0
  160. package/src/lib/app-utils/contact-holder.d.ts +52 -0
  161. package/src/lib/app-utils/contact-holder.js +98 -0
  162. package/src/lib/app-utils/contact-holder.js.map +1 -0
  163. package/src/lib/app-utils/contact-merge.d.ts +136 -0
  164. package/src/lib/app-utils/contact-merge.js +379 -0
  165. package/src/lib/app-utils/contact-merge.js.map +1 -0
  166. package/src/lib/app-utils/contacts.d.ts +554 -0
  167. package/src/lib/app-utils/contacts.js +401 -0
  168. package/src/lib/app-utils/contacts.js.map +1 -0
  169. package/src/lib/app-utils/content-author-profile.d.ts +68 -0
  170. package/src/lib/app-utils/content-author-profile.js +118 -0
  171. package/src/lib/app-utils/content-author-profile.js.map +1 -0
  172. package/src/lib/app-utils/content-authors.d.ts +662 -0
  173. package/src/lib/app-utils/content-authors.js +771 -0
  174. package/src/lib/app-utils/content-authors.js.map +1 -0
  175. package/src/lib/app-utils/content-schema-type.d.ts +81 -0
  176. package/src/lib/app-utils/content-schema-type.js +87 -0
  177. package/src/lib/app-utils/content-schema-type.js.map +1 -0
  178. package/src/lib/app-utils/contexts.d.ts +35 -0
  179. package/src/lib/app-utils/contexts.js +35 -0
  180. package/src/lib/app-utils/contexts.js.map +1 -0
  181. package/src/lib/app-utils/create-resource-uid.d.ts +18 -0
  182. package/src/lib/app-utils/create-resource-uid.js +23 -0
  183. package/src/lib/app-utils/create-resource-uid.js.map +1 -0
  184. package/src/lib/app-utils/crm-booking.d.ts +75 -0
  185. package/src/lib/app-utils/crm-booking.js +133 -0
  186. package/src/lib/app-utils/crm-booking.js.map +1 -0
  187. package/src/lib/app-utils/crm-csv.d.ts +232 -0
  188. package/src/lib/app-utils/crm-csv.js +432 -0
  189. package/src/lib/app-utils/crm-csv.js.map +1 -0
  190. package/src/lib/app-utils/crm-digest.d.ts +179 -0
  191. package/src/lib/app-utils/crm-digest.js +271 -0
  192. package/src/lib/app-utils/crm-digest.js.map +1 -0
  193. package/src/lib/app-utils/crm-email-templates.d.ts +172 -0
  194. package/src/lib/app-utils/crm-email-templates.js +382 -0
  195. package/src/lib/app-utils/crm-email-templates.js.map +1 -0
  196. package/src/lib/app-utils/crm-inbound.d.ts +245 -0
  197. package/src/lib/app-utils/crm-inbound.js +407 -0
  198. package/src/lib/app-utils/crm-inbound.js.map +1 -0
  199. package/src/lib/app-utils/crm-kinds.d.ts +73 -0
  200. package/src/lib/app-utils/crm-kinds.js +92 -0
  201. package/src/lib/app-utils/crm-kinds.js.map +1 -0
  202. package/src/lib/app-utils/crm-next-activity.d.ts +107 -0
  203. package/src/lib/app-utils/crm-next-activity.js +140 -0
  204. package/src/lib/app-utils/crm-next-activity.js.map +1 -0
  205. package/src/lib/app-utils/crm-reports.d.ts +479 -0
  206. package/src/lib/app-utils/crm-reports.js +680 -0
  207. package/src/lib/app-utils/crm-reports.js.map +1 -0
  208. package/src/lib/app-utils/crm-task-reminders.d.ts +129 -0
  209. package/src/lib/app-utils/crm-task-reminders.js +139 -0
  210. package/src/lib/app-utils/crm-task-reminders.js.map +1 -0
  211. package/src/lib/app-utils/crm.d.ts +1546 -0
  212. package/src/lib/app-utils/crm.js +1814 -0
  213. package/src/lib/app-utils/crm.js.map +1 -0
  214. package/src/lib/app-utils/csp-report.d.ts +84 -0
  215. package/src/lib/app-utils/csp-report.js +177 -0
  216. package/src/lib/app-utils/csp-report.js.map +1 -0
  217. package/src/lib/app-utils/csv-import.d.ts +192 -0
  218. package/src/lib/app-utils/csv-import.js +279 -0
  219. package/src/lib/app-utils/csv-import.js.map +1 -0
  220. package/src/lib/app-utils/dataset-csv.d.ts +109 -0
  221. package/src/lib/app-utils/dataset-csv.js +235 -0
  222. package/src/lib/app-utils/dataset-csv.js.map +1 -0
  223. package/src/lib/app-utils/dataset-models.d.ts +194 -0
  224. package/src/lib/app-utils/dataset-models.js +409 -0
  225. package/src/lib/app-utils/dataset-models.js.map +1 -0
  226. package/src/lib/app-utils/dataset-query.d.ts +57 -0
  227. package/src/lib/app-utils/dataset-query.js +126 -0
  228. package/src/lib/app-utils/dataset-query.js.map +1 -0
  229. package/src/lib/app-utils/datasets.d.ts +136 -0
  230. package/src/lib/app-utils/datasets.js +177 -0
  231. package/src/lib/app-utils/datasets.js.map +1 -0
  232. package/src/lib/app-utils/decompress.d.ts +36 -0
  233. package/src/lib/app-utils/decompress.js +32 -0
  234. package/src/lib/app-utils/decompress.js.map +1 -0
  235. package/src/lib/app-utils/dedicated-sending-domain.d.ts +47 -0
  236. package/src/lib/app-utils/dedicated-sending-domain.js +114 -0
  237. package/src/lib/app-utils/dedicated-sending-domain.js.map +1 -0
  238. package/src/lib/app-utils/deferred-image.d.ts +86 -0
  239. package/src/lib/app-utils/deferred-image.js +83 -0
  240. package/src/lib/app-utils/deferred-image.js.map +1 -0
  241. package/src/lib/app-utils/definition-canvas-tree.d.ts +61 -0
  242. package/src/lib/app-utils/definition-canvas-tree.js +126 -0
  243. package/src/lib/app-utils/definition-canvas-tree.js.map +1 -0
  244. package/src/lib/app-utils/deployment-shape.d.ts +167 -0
  245. package/src/lib/app-utils/deployment-shape.js +238 -0
  246. package/src/lib/app-utils/deployment-shape.js.map +1 -0
  247. package/src/lib/app-utils/detect-template-placeholders.d.ts +30 -0
  248. package/src/lib/app-utils/detect-template-placeholders.js +53 -0
  249. package/src/lib/app-utils/detect-template-placeholders.js.map +1 -0
  250. package/src/lib/app-utils/dmca-counter-notice.d.ts +185 -0
  251. package/src/lib/app-utils/dmca-counter-notice.js +277 -0
  252. package/src/lib/app-utils/dmca-counter-notice.js.map +1 -0
  253. package/src/lib/app-utils/docs-deflection.d.ts +156 -0
  254. package/src/lib/app-utils/docs-deflection.js +547 -0
  255. package/src/lib/app-utils/docs-deflection.js.map +1 -0
  256. package/src/lib/app-utils/docs-help.d.ts +57 -0
  257. package/src/lib/app-utils/docs-help.generated.d.ts +303 -0
  258. package/src/lib/app-utils/docs-help.generated.js +867 -0
  259. package/src/lib/app-utils/docs-help.generated.js.map +1 -0
  260. package/src/lib/app-utils/docs-help.js +46 -0
  261. package/src/lib/app-utils/docs-help.js.map +1 -0
  262. package/src/lib/app-utils/docs-index.generated.d.ts +29 -0
  263. package/src/lib/app-utils/docs-index.generated.js +8388 -0
  264. package/src/lib/app-utils/docs-index.generated.js.map +1 -0
  265. package/src/lib/app-utils/docs-retrieval.d.ts +67 -0
  266. package/src/lib/app-utils/docs-retrieval.js +188 -0
  267. package/src/lib/app-utils/docs-retrieval.js.map +1 -0
  268. package/src/lib/app-utils/document-landmark.d.ts +50 -0
  269. package/src/lib/app-utils/document-landmark.js +100 -0
  270. package/src/lib/app-utils/document-landmark.js.map +1 -0
  271. package/src/lib/app-utils/duplicate-resource.d.ts +101 -0
  272. package/src/lib/app-utils/duplicate-resource.js +206 -0
  273. package/src/lib/app-utils/duplicate-resource.js.map +1 -0
  274. package/src/lib/app-utils/dynamic-list-rule.d.ts +471 -0
  275. package/src/lib/app-utils/dynamic-list-rule.js +914 -0
  276. package/src/lib/app-utils/dynamic-list-rule.js.map +1 -0
  277. package/src/lib/app-utils/element-animation.d.ts +200 -0
  278. package/src/lib/app-utils/element-animation.js +233 -0
  279. package/src/lib/app-utils/element-animation.js.map +1 -0
  280. package/src/lib/app-utils/element-hidden-style.d.ts +46 -0
  281. package/src/lib/app-utils/element-hidden-style.js +48 -0
  282. package/src/lib/app-utils/element-hidden-style.js.map +1 -0
  283. package/src/lib/app-utils/element-ui.d.ts +243 -0
  284. package/src/lib/app-utils/element-ui.js +459 -0
  285. package/src/lib/app-utils/element-ui.js.map +1 -0
  286. package/src/lib/app-utils/email-starter-policy.d.ts +160 -0
  287. package/src/lib/app-utils/email-starter-policy.js +327 -0
  288. package/src/lib/app-utils/email-starter-policy.js.map +1 -0
  289. package/src/lib/app-utils/email-topics.d.ts +310 -0
  290. package/src/lib/app-utils/email-topics.js +336 -0
  291. package/src/lib/app-utils/email-topics.js.map +1 -0
  292. package/src/lib/app-utils/enabled-plugins-context.d.ts +100 -0
  293. package/src/lib/app-utils/enabled-plugins-context.js +113 -0
  294. package/src/lib/app-utils/enabled-plugins-context.js.map +1 -0
  295. package/src/lib/app-utils/ensure-canvas-root.d.ts +38 -0
  296. package/src/lib/app-utils/ensure-canvas-root.js +61 -0
  297. package/src/lib/app-utils/ensure-canvas-root.js.map +1 -0
  298. package/src/lib/app-utils/entity-picker-context.d.ts +199 -0
  299. package/src/lib/app-utils/entity-picker-context.js +93 -0
  300. package/src/lib/app-utils/entity-picker-context.js.map +1 -0
  301. package/src/lib/app-utils/error-beacon.d.ts +107 -0
  302. package/src/lib/app-utils/error-beacon.js +296 -0
  303. package/src/lib/app-utils/error-beacon.js.map +1 -0
  304. package/src/lib/app-utils/expand-repeatables.d.ts +172 -0
  305. package/src/lib/app-utils/expand-repeatables.js +299 -0
  306. package/src/lib/app-utils/expand-repeatables.js.map +1 -0
  307. package/src/lib/app-utils/foreign-script.d.ts +82 -0
  308. package/src/lib/app-utils/foreign-script.js +102 -0
  309. package/src/lib/app-utils/foreign-script.js.map +1 -0
  310. package/src/lib/app-utils/form-abuse-ceiling.d.ts +146 -0
  311. package/src/lib/app-utils/form-abuse-ceiling.js +158 -0
  312. package/src/lib/app-utils/form-abuse-ceiling.js.map +1 -0
  313. package/src/lib/app-utils/form-contract.d.ts +198 -0
  314. package/src/lib/app-utils/form-contract.js +282 -0
  315. package/src/lib/app-utils/form-contract.js.map +1 -0
  316. package/src/lib/app-utils/form-dataset-binding.d.ts +50 -0
  317. package/src/lib/app-utils/form-dataset-binding.js +74 -0
  318. package/src/lib/app-utils/form-dataset-binding.js.map +1 -0
  319. package/src/lib/app-utils/forms.d.ts +727 -0
  320. package/src/lib/app-utils/forms.js +777 -0
  321. package/src/lib/app-utils/forms.js.map +1 -0
  322. package/src/lib/app-utils/functions.d.ts +188 -0
  323. package/src/lib/app-utils/functions.js +529 -0
  324. package/src/lib/app-utils/functions.js.map +1 -0
  325. package/src/lib/app-utils/guarded-version-save.d.ts +91 -0
  326. package/src/lib/app-utils/guarded-version-save.js +131 -0
  327. package/src/lib/app-utils/guarded-version-save.js.map +1 -0
  328. package/src/lib/app-utils/health-report.d.ts +1766 -0
  329. package/src/lib/app-utils/health-report.js +2072 -0
  330. package/src/lib/app-utils/health-report.js.map +1 -0
  331. package/src/lib/app-utils/host-naming.d.ts +135 -0
  332. package/src/lib/app-utils/host-naming.js +188 -0
  333. package/src/lib/app-utils/host-naming.js.map +1 -0
  334. package/src/lib/app-utils/host-permissions.d.ts +76 -0
  335. package/src/lib/app-utils/host-permissions.js +119 -0
  336. package/src/lib/app-utils/host-permissions.js.map +1 -0
  337. package/src/lib/app-utils/host-tokens.d.ts +228 -0
  338. package/src/lib/app-utils/host-tokens.js +304 -0
  339. package/src/lib/app-utils/host-tokens.js.map +1 -0
  340. package/src/lib/app-utils/idp-profile.d.ts +83 -0
  341. package/src/lib/app-utils/idp-profile.js +268 -0
  342. package/src/lib/app-utils/idp-profile.js.map +1 -0
  343. package/src/lib/app-utils/image-upload-types.d.ts +49 -0
  344. package/src/lib/app-utils/image-upload-types.js +86 -0
  345. package/src/lib/app-utils/image-upload-types.js.map +1 -0
  346. package/src/lib/app-utils/index.d.ts +18 -0
  347. package/src/lib/app-utils/index.js +23 -0
  348. package/src/lib/app-utils/index.js.map +1 -0
  349. package/src/lib/app-utils/internal-traffic.d.ts +197 -0
  350. package/src/lib/app-utils/internal-traffic.js +288 -0
  351. package/src/lib/app-utils/internal-traffic.js.map +1 -0
  352. package/src/lib/app-utils/lineal-order.d.ts +62 -0
  353. package/src/lib/app-utils/lineal-order.js +65 -0
  354. package/src/lib/app-utils/lineal-order.js.map +1 -0
  355. package/src/lib/app-utils/link-references.d.ts +36 -0
  356. package/src/lib/app-utils/link-references.js +89 -0
  357. package/src/lib/app-utils/link-references.js.map +1 -0
  358. package/src/lib/app-utils/link-target-search-context.d.ts +123 -0
  359. package/src/lib/app-utils/link-target-search-context.js +221 -0
  360. package/src/lib/app-utils/link-target-search-context.js.map +1 -0
  361. package/src/lib/app-utils/list-assignment-policy.d.ts +169 -0
  362. package/src/lib/app-utils/list-assignment-policy.js +170 -0
  363. package/src/lib/app-utils/list-assignment-policy.js.map +1 -0
  364. package/src/lib/app-utils/list-import.d.ts +158 -0
  365. package/src/lib/app-utils/list-import.js +332 -0
  366. package/src/lib/app-utils/list-import.js.map +1 -0
  367. package/src/lib/app-utils/llms-txt.d.ts +104 -0
  368. package/src/lib/app-utils/llms-txt.js +144 -0
  369. package/src/lib/app-utils/llms-txt.js.map +1 -0
  370. package/src/lib/app-utils/local-storage-budget.d.ts +75 -0
  371. package/src/lib/app-utils/local-storage-budget.js +153 -0
  372. package/src/lib/app-utils/local-storage-budget.js.map +1 -0
  373. package/src/lib/app-utils/lockdown.d.ts +698 -0
  374. package/src/lib/app-utils/lockdown.js +929 -0
  375. package/src/lib/app-utils/lockdown.js.map +1 -0
  376. package/src/lib/app-utils/margin-utilization.d.ts +267 -0
  377. package/src/lib/app-utils/margin-utilization.js +351 -0
  378. package/src/lib/app-utils/margin-utilization.js.map +1 -0
  379. package/src/lib/app-utils/markdown-lite.d.ts +297 -0
  380. package/src/lib/app-utils/markdown-lite.js +705 -0
  381. package/src/lib/app-utils/markdown-lite.js.map +1 -0
  382. package/src/lib/app-utils/marketing-consent.d.ts +704 -0
  383. package/src/lib/app-utils/marketing-consent.js +783 -0
  384. package/src/lib/app-utils/marketing-consent.js.map +1 -0
  385. package/src/lib/app-utils/marketplace-listing-visibility.d.ts +120 -0
  386. package/src/lib/app-utils/marketplace-listing-visibility.js +139 -0
  387. package/src/lib/app-utils/marketplace-listing-visibility.js.map +1 -0
  388. package/src/lib/app-utils/marketplace-overrides.d.ts +271 -0
  389. package/src/lib/app-utils/marketplace-overrides.js +462 -0
  390. package/src/lib/app-utils/marketplace-overrides.js.map +1 -0
  391. package/src/lib/app-utils/marketplace-provenance.d.ts +150 -0
  392. package/src/lib/app-utils/marketplace-provenance.js +136 -0
  393. package/src/lib/app-utils/marketplace-provenance.js.map +1 -0
  394. package/src/lib/app-utils/marketplace-theme.d.ts +237 -0
  395. package/src/lib/app-utils/marketplace-theme.js +525 -0
  396. package/src/lib/app-utils/marketplace-theme.js.map +1 -0
  397. package/src/lib/app-utils/marketplace-update-state.d.ts +119 -0
  398. package/src/lib/app-utils/marketplace-update-state.js +132 -0
  399. package/src/lib/app-utils/marketplace-update-state.js.map +1 -0
  400. package/src/lib/app-utils/marketplace-verification.d.ts +95 -0
  401. package/src/lib/app-utils/marketplace-verification.js +81 -0
  402. package/src/lib/app-utils/marketplace-verification.js.map +1 -0
  403. package/src/lib/app-utils/measure-node-map.d.ts +76 -0
  404. package/src/lib/app-utils/measure-node-map.js +111 -0
  405. package/src/lib/app-utils/measure-node-map.js.map +1 -0
  406. package/src/lib/app-utils/media-alt.d.ts +55 -0
  407. package/src/lib/app-utils/media-alt.js +58 -0
  408. package/src/lib/app-utils/media-alt.js.map +1 -0
  409. package/src/lib/app-utils/media-asset-facts.d.ts +125 -0
  410. package/src/lib/app-utils/media-asset-facts.js +467 -0
  411. package/src/lib/app-utils/media-asset-facts.js.map +1 -0
  412. package/src/lib/app-utils/media-cdn-scope.d.ts +65 -0
  413. package/src/lib/app-utils/media-cdn-scope.js +75 -0
  414. package/src/lib/app-utils/media-cdn-scope.js.map +1 -0
  415. package/src/lib/app-utils/media-folders.d.ts +161 -0
  416. package/src/lib/app-utils/media-folders.js +230 -0
  417. package/src/lib/app-utils/media-folders.js.map +1 -0
  418. package/src/lib/app-utils/media-metadata.d.ts +277 -0
  419. package/src/lib/app-utils/media-metadata.js +388 -0
  420. package/src/lib/app-utils/media-metadata.js.map +1 -0
  421. package/src/lib/app-utils/media-picker-context.d.ts +93 -0
  422. package/src/lib/app-utils/media-picker-context.js +26 -0
  423. package/src/lib/app-utils/media-picker-context.js.map +1 -0
  424. package/src/lib/app-utils/media-quarantine.d.ts +247 -0
  425. package/src/lib/app-utils/media-quarantine.js +323 -0
  426. package/src/lib/app-utils/media-quarantine.js.map +1 -0
  427. package/src/lib/app-utils/media-ref.d.ts +661 -0
  428. package/src/lib/app-utils/media-ref.js +1055 -0
  429. package/src/lib/app-utils/media-ref.js.map +1 -0
  430. package/src/lib/app-utils/media-takedown-reach.d.ts +126 -0
  431. package/src/lib/app-utils/media-takedown-reach.js +143 -0
  432. package/src/lib/app-utils/media-takedown-reach.js.map +1 -0
  433. package/src/lib/app-utils/member-email-aliases.d.ts +180 -0
  434. package/src/lib/app-utils/member-email-aliases.js +217 -0
  435. package/src/lib/app-utils/member-email-aliases.js.map +1 -0
  436. package/src/lib/app-utils/merge-node-sx.d.ts +47 -0
  437. package/src/lib/app-utils/merge-node-sx.js +123 -0
  438. package/src/lib/app-utils/merge-node-sx.js.map +1 -0
  439. package/src/lib/app-utils/name-match.d.ts +155 -0
  440. package/src/lib/app-utils/name-match.js +175 -0
  441. package/src/lib/app-utils/name-match.js.map +1 -0
  442. package/src/lib/app-utils/name-search.d.ts +117 -0
  443. package/src/lib/app-utils/name-search.js +145 -0
  444. package/src/lib/app-utils/name-search.js.map +1 -0
  445. package/src/lib/app-utils/node-capabilities.d.ts +85 -0
  446. package/src/lib/app-utils/node-capabilities.js +88 -0
  447. package/src/lib/app-utils/node-capabilities.js.map +1 -0
  448. package/src/lib/app-utils/node-definition-sanitizer.d.ts +164 -0
  449. package/src/lib/app-utils/node-definition-sanitizer.js +402 -0
  450. package/src/lib/app-utils/node-definition-sanitizer.js.map +1 -0
  451. package/src/lib/app-utils/node-identity.d.ts +47 -0
  452. package/src/lib/app-utils/node-identity.js +50 -0
  453. package/src/lib/app-utils/node-identity.js.map +1 -0
  454. package/src/lib/app-utils/node-interactions.d.ts +102 -0
  455. package/src/lib/app-utils/node-interactions.js +192 -0
  456. package/src/lib/app-utils/node-interactions.js.map +1 -0
  457. package/src/lib/app-utils/node-url-policy.d.ts +54 -0
  458. package/src/lib/app-utils/node-url-policy.js +72 -0
  459. package/src/lib/app-utils/node-url-policy.js.map +1 -0
  460. package/src/lib/app-utils/notifications.d.ts +71 -0
  461. package/src/lib/app-utils/notifications.js +101 -0
  462. package/src/lib/app-utils/notifications.js.map +1 -0
  463. package/src/lib/app-utils/onboarding-deep-link.d.ts +120 -0
  464. package/src/lib/app-utils/onboarding-deep-link.js +142 -0
  465. package/src/lib/app-utils/onboarding-deep-link.js.map +1 -0
  466. package/src/lib/app-utils/operator-identity.d.ts +207 -0
  467. package/src/lib/app-utils/operator-identity.js +186 -0
  468. package/src/lib/app-utils/operator-identity.js.map +1 -0
  469. package/src/lib/app-utils/org-billing-doc.d.ts +231 -0
  470. package/src/lib/app-utils/org-billing-doc.js +238 -0
  471. package/src/lib/app-utils/org-billing-doc.js.map +1 -0
  472. package/src/lib/app-utils/org-override-reason.d.ts +100 -0
  473. package/src/lib/app-utils/org-override-reason.js +116 -0
  474. package/src/lib/app-utils/org-override-reason.js.map +1 -0
  475. package/src/lib/app-utils/org-permissions.d.ts +194 -0
  476. package/src/lib/app-utils/org-permissions.js +357 -0
  477. package/src/lib/app-utils/org-permissions.js.map +1 -0
  478. package/src/lib/app-utils/org-roles.d.ts +72 -0
  479. package/src/lib/app-utils/org-roles.js +131 -0
  480. package/src/lib/app-utils/org-roles.js.map +1 -0
  481. package/src/lib/app-utils/organizations.d.ts +407 -0
  482. package/src/lib/app-utils/organizations.js +511 -0
  483. package/src/lib/app-utils/organizations.js.map +1 -0
  484. package/src/lib/app-utils/page-header-actions-context.d.ts +83 -0
  485. package/src/lib/app-utils/page-header-actions-context.js +58 -0
  486. package/src/lib/app-utils/page-header-actions-context.js.map +1 -0
  487. package/src/lib/app-utils/page-header-record-context.d.ts +80 -0
  488. package/src/lib/app-utils/page-header-record-context.js +64 -0
  489. package/src/lib/app-utils/page-header-record-context.js.map +1 -0
  490. package/src/lib/app-utils/page-markdown.d.ts +103 -0
  491. package/src/lib/app-utils/page-markdown.js +385 -0
  492. package/src/lib/app-utils/page-markdown.js.map +1 -0
  493. package/src/lib/app-utils/paid-media-source.d.ts +90 -0
  494. package/src/lib/app-utils/paid-media-source.js +184 -0
  495. package/src/lib/app-utils/paid-media-source.js.map +1 -0
  496. package/src/lib/app-utils/palette-sx.d.ts +34 -0
  497. package/src/lib/app-utils/palette-sx.js +150 -0
  498. package/src/lib/app-utils/palette-sx.js.map +1 -0
  499. package/src/lib/app-utils/password-policy.d.ts +47 -0
  500. package/src/lib/app-utils/password-policy.js +46 -0
  501. package/src/lib/app-utils/password-policy.js.map +1 -0
  502. package/src/lib/app-utils/payments-configured.d.ts +84 -0
  503. package/src/lib/app-utils/payments-configured.js +88 -0
  504. package/src/lib/app-utils/payments-configured.js.map +1 -0
  505. package/src/lib/app-utils/person-erasure.d.ts +126 -0
  506. package/src/lib/app-utils/person-erasure.js +121 -0
  507. package/src/lib/app-utils/person-erasure.js.map +1 -0
  508. package/src/lib/app-utils/person-key.d.ts +32 -0
  509. package/src/lib/app-utils/person-key.js +62 -0
  510. package/src/lib/app-utils/person-key.js.map +1 -0
  511. package/src/lib/app-utils/plan-entitlements.d.ts +2756 -0
  512. package/src/lib/app-utils/plan-entitlements.js +4085 -0
  513. package/src/lib/app-utils/plan-entitlements.js.map +1 -0
  514. package/src/lib/app-utils/platform-ad-conversions.d.ts +122 -0
  515. package/src/lib/app-utils/platform-ad-conversions.js +174 -0
  516. package/src/lib/app-utils/platform-ad-conversions.js.map +1 -0
  517. package/src/lib/app-utils/platform-advertising-tags.d.ts +139 -0
  518. package/src/lib/app-utils/platform-advertising-tags.js +203 -0
  519. package/src/lib/app-utils/platform-advertising-tags.js.map +1 -0
  520. package/src/lib/app-utils/platform-brand.d.ts +151 -0
  521. package/src/lib/app-utils/platform-brand.js +175 -0
  522. package/src/lib/app-utils/platform-brand.js.map +1 -0
  523. package/src/lib/app-utils/platform-consent-default.d.ts +142 -0
  524. package/src/lib/app-utils/platform-consent-default.js +295 -0
  525. package/src/lib/app-utils/platform-consent-default.js.map +1 -0
  526. package/src/lib/app-utils/platform-marketing-consent.d.ts +151 -0
  527. package/src/lib/app-utils/platform-marketing-consent.js +182 -0
  528. package/src/lib/app-utils/platform-marketing-consent.js.map +1 -0
  529. package/src/lib/app-utils/platform-marketing-host.d.ts +33 -0
  530. package/src/lib/app-utils/platform-marketing-host.js +97 -0
  531. package/src/lib/app-utils/platform-marketing-host.js.map +1 -0
  532. package/src/lib/app-utils/platform-visitor-consent.d.ts +252 -0
  533. package/src/lib/app-utils/platform-visitor-consent.js +533 -0
  534. package/src/lib/app-utils/platform-visitor-consent.js.map +1 -0
  535. package/src/lib/app-utils/plugin-api-cross-origin.d.ts +61 -0
  536. package/src/lib/app-utils/plugin-api-cross-origin.js +202 -0
  537. package/src/lib/app-utils/plugin-api-cross-origin.js.map +1 -0
  538. package/src/lib/app-utils/plugin-api-rate-limit.d.ts +192 -0
  539. package/src/lib/app-utils/plugin-api-rate-limit.js +270 -0
  540. package/src/lib/app-utils/plugin-api-rate-limit.js.map +1 -0
  541. package/src/lib/app-utils/plugin-artifact-path.d.ts +32 -0
  542. package/src/lib/app-utils/plugin-artifact-path.js +33 -0
  543. package/src/lib/app-utils/plugin-artifact-path.js.map +1 -0
  544. package/src/lib/app-utils/plugin-bundle-checks.d.ts +148 -0
  545. package/src/lib/app-utils/plugin-bundle-checks.js +989 -0
  546. package/src/lib/app-utils/plugin-bundle-checks.js.map +1 -0
  547. package/src/lib/app-utils/plugin-manifest.d.ts +516 -0
  548. package/src/lib/app-utils/plugin-manifest.js +687 -0
  549. package/src/lib/app-utils/plugin-manifest.js.map +1 -0
  550. package/src/lib/app-utils/published-legal-pages.d.ts +71 -0
  551. package/src/lib/app-utils/published-legal-pages.js +90 -0
  552. package/src/lib/app-utils/published-legal-pages.js.map +1 -0
  553. package/src/lib/app-utils/publisher-agreement.d.ts +254 -0
  554. package/src/lib/app-utils/publisher-agreement.js +274 -0
  555. package/src/lib/app-utils/publisher-agreement.js.map +1 -0
  556. package/src/lib/app-utils/publisher-attestation.d.ts +99 -0
  557. package/src/lib/app-utils/publisher-attestation.js +158 -0
  558. package/src/lib/app-utils/publisher-attestation.js.map +1 -0
  559. package/src/lib/app-utils/recipient-email-render.d.ts +216 -0
  560. package/src/lib/app-utils/recipient-email-render.js +221 -0
  561. package/src/lib/app-utils/recipient-email-render.js.map +1 -0
  562. package/src/lib/app-utils/redispatch-caught-error.d.ts +38 -0
  563. package/src/lib/app-utils/redispatch-caught-error.js +46 -0
  564. package/src/lib/app-utils/redispatch-caught-error.js.map +1 -0
  565. package/src/lib/app-utils/registering-plugin.d.ts +19 -0
  566. package/src/lib/app-utils/registering-plugin.js +38 -0
  567. package/src/lib/app-utils/registering-plugin.js.map +1 -0
  568. package/src/lib/app-utils/release-flags.d.ts +283 -0
  569. package/src/lib/app-utils/release-flags.js +518 -0
  570. package/src/lib/app-utils/release-flags.js.map +1 -0
  571. package/src/lib/app-utils/repair-canvas-nodes.d.ts +130 -0
  572. package/src/lib/app-utils/repair-canvas-nodes.js +298 -0
  573. package/src/lib/app-utils/repair-canvas-nodes.js.map +1 -0
  574. package/src/lib/app-utils/repeat-infringer.d.ts +166 -0
  575. package/src/lib/app-utils/repeat-infringer.js +188 -0
  576. package/src/lib/app-utils/repeat-infringer.js.map +1 -0
  577. package/src/lib/app-utils/repeat-sources.d.ts +115 -0
  578. package/src/lib/app-utils/repeat-sources.js +86 -0
  579. package/src/lib/app-utils/repeat-sources.js.map +1 -0
  580. package/src/lib/app-utils/request-geo.d.ts +114 -0
  581. package/src/lib/app-utils/request-geo.js +247 -0
  582. package/src/lib/app-utils/request-geo.js.map +1 -0
  583. package/src/lib/app-utils/request-ip.d.ts +213 -0
  584. package/src/lib/app-utils/request-ip.js +344 -0
  585. package/src/lib/app-utils/request-ip.js.map +1 -0
  586. package/src/lib/app-utils/resolve-named-tokens.d.ts +26 -0
  587. package/src/lib/app-utils/resolve-named-tokens.js +59 -0
  588. package/src/lib/app-utils/resolve-named-tokens.js.map +1 -0
  589. package/src/lib/app-utils/responsive-typography-sx.d.ts +77 -0
  590. package/src/lib/app-utils/responsive-typography-sx.js +122 -0
  591. package/src/lib/app-utils/responsive-typography-sx.js.map +1 -0
  592. package/src/lib/app-utils/reusable-component-keys.d.ts +135 -0
  593. package/src/lib/app-utils/reusable-component-keys.js +140 -0
  594. package/src/lib/app-utils/reusable-component-keys.js.map +1 -0
  595. package/src/lib/app-utils/reusable-prop-values.d.ts +199 -0
  596. package/src/lib/app-utils/reusable-prop-values.js +494 -0
  597. package/src/lib/app-utils/reusable-prop-values.js.map +1 -0
  598. package/src/lib/app-utils/safe-json-ld.d.ts +29 -0
  599. package/src/lib/app-utils/safe-json-ld.js +37 -0
  600. package/src/lib/app-utils/safe-json-ld.js.map +1 -0
  601. package/src/lib/app-utils/sanitize-svg.d.ts +55 -0
  602. package/src/lib/app-utils/sanitize-svg.js +460 -0
  603. package/src/lib/app-utils/sanitize-svg.js.map +1 -0
  604. package/src/lib/app-utils/scheme-sx.d.ts +84 -0
  605. package/src/lib/app-utils/scheme-sx.js +148 -0
  606. package/src/lib/app-utils/scheme-sx.js.map +1 -0
  607. package/src/lib/app-utils/scope-tokens.d.ts +249 -0
  608. package/src/lib/app-utils/scope-tokens.js +331 -0
  609. package/src/lib/app-utils/scope-tokens.js.map +1 -0
  610. package/src/lib/app-utils/screen-link-context-value.d.ts +57 -0
  611. package/src/lib/app-utils/screen-link-context-value.js +43 -0
  612. package/src/lib/app-utils/screen-link-context-value.js.map +1 -0
  613. package/src/lib/app-utils/screen-link-context.d.ts +185 -0
  614. package/src/lib/app-utils/screen-link-context.js +304 -0
  615. package/src/lib/app-utils/screen-link-context.js.map +1 -0
  616. package/src/lib/app-utils/screen-link-value.d.ts +269 -0
  617. package/src/lib/app-utils/screen-link-value.js +384 -0
  618. package/src/lib/app-utils/screen-link-value.js.map +1 -0
  619. package/src/lib/app-utils/screen-route.d.ts +669 -0
  620. package/src/lib/app-utils/screen-route.js +751 -0
  621. package/src/lib/app-utils/screen-route.js.map +1 -0
  622. package/src/lib/app-utils/screen-seo-fields.d.ts +37 -0
  623. package/src/lib/app-utils/screen-seo-fields.js +42 -0
  624. package/src/lib/app-utils/screen-seo-fields.js.map +1 -0
  625. package/src/lib/app-utils/search-indexing.d.ts +162 -0
  626. package/src/lib/app-utils/search-indexing.js +207 -0
  627. package/src/lib/app-utils/search-indexing.js.map +1 -0
  628. package/src/lib/app-utils/seo-listing-fields.d.ts +70 -0
  629. package/src/lib/app-utils/seo-listing-fields.js +75 -0
  630. package/src/lib/app-utils/seo-listing-fields.js.map +1 -0
  631. package/src/lib/app-utils/seo-locale.d.ts +119 -0
  632. package/src/lib/app-utils/seo-locale.js +133 -0
  633. package/src/lib/app-utils/seo-locale.js.map +1 -0
  634. package/src/lib/app-utils/seo-title-variables.d.ts +149 -0
  635. package/src/lib/app-utils/seo-title-variables.js +190 -0
  636. package/src/lib/app-utils/seo-title-variables.js.map +1 -0
  637. package/src/lib/app-utils/seo-title.d.ts +70 -0
  638. package/src/lib/app-utils/seo-title.js +117 -0
  639. package/src/lib/app-utils/seo-title.js.map +1 -0
  640. package/src/lib/app-utils/server.d.ts +174 -0
  641. package/src/lib/app-utils/server.js +314 -0
  642. package/src/lib/app-utils/server.js.map +1 -0
  643. package/src/lib/app-utils/site-context.d.ts +106 -0
  644. package/src/lib/app-utils/site-context.js +100 -0
  645. package/src/lib/app-utils/site-context.js.map +1 -0
  646. package/src/lib/app-utils/sitemap.d.ts +117 -0
  647. package/src/lib/app-utils/sitemap.js +183 -0
  648. package/src/lib/app-utils/sitemap.js.map +1 -0
  649. package/src/lib/app-utils/social-image.d.ts +112 -0
  650. package/src/lib/app-utils/social-image.js +191 -0
  651. package/src/lib/app-utils/social-image.js.map +1 -0
  652. package/src/lib/app-utils/starter-templates.d.ts +99 -0
  653. package/src/lib/app-utils/starter-templates.js +579 -0
  654. package/src/lib/app-utils/starter-templates.js.map +1 -0
  655. package/src/lib/app-utils/stored-nodes.d.ts +120 -0
  656. package/src/lib/app-utils/stored-nodes.js +174 -0
  657. package/src/lib/app-utils/stored-nodes.js.map +1 -0
  658. package/src/lib/app-utils/strip-undefined.d.ts +59 -0
  659. package/src/lib/app-utils/strip-undefined.js +95 -0
  660. package/src/lib/app-utils/strip-undefined.js.map +1 -0
  661. package/src/lib/app-utils/stripe-deployment-mode.d.ts +47 -0
  662. package/src/lib/app-utils/stripe-deployment-mode.js +70 -0
  663. package/src/lib/app-utils/stripe-deployment-mode.js.map +1 -0
  664. package/src/lib/app-utils/support-tiers.d.ts +108 -0
  665. package/src/lib/app-utils/support-tiers.js +201 -0
  666. package/src/lib/app-utils/support-tiers.js.map +1 -0
  667. package/src/lib/app-utils/timezone-geo-hint.d.ts +36 -0
  668. package/src/lib/app-utils/timezone-geo-hint.js +180 -0
  669. package/src/lib/app-utils/timezone-geo-hint.js.map +1 -0
  670. package/src/lib/app-utils/upload-cors.d.ts +155 -0
  671. package/src/lib/app-utils/upload-cors.js +254 -0
  672. package/src/lib/app-utils/upload-cors.js.map +1 -0
  673. package/src/lib/app-utils/upload-inspection.d.ts +159 -0
  674. package/src/lib/app-utils/upload-inspection.js +539 -0
  675. package/src/lib/app-utils/upload-inspection.js.map +1 -0
  676. package/src/lib/app-utils/url-slug.d.ts +40 -0
  677. package/src/lib/app-utils/url-slug.js +42 -0
  678. package/src/lib/app-utils/url-slug.js.map +1 -0
  679. package/src/lib/app-utils/usage-budget.d.ts +429 -0
  680. package/src/lib/app-utils/usage-budget.js +421 -0
  681. package/src/lib/app-utils/usage-budget.js.map +1 -0
  682. package/src/lib/app-utils/variables.d.ts +96 -0
  683. package/src/lib/app-utils/variables.js +233 -0
  684. package/src/lib/app-utils/variables.js.map +1 -0
  685. package/src/lib/app-utils/video-asset-facts.d.ts +61 -0
  686. package/src/lib/app-utils/video-asset-facts.js +70 -0
  687. package/src/lib/app-utils/video-asset-facts.js.map +1 -0
  688. package/src/lib/app-utils/video-object.d.ts +89 -0
  689. package/src/lib/app-utils/video-object.js +245 -0
  690. package/src/lib/app-utils/video-object.js.map +1 -0
  691. package/src/lib/app-utils/visitor-consent.d.ts +757 -0
  692. package/src/lib/app-utils/visitor-consent.js +981 -0
  693. package/src/lib/app-utils/visitor-consent.js.map +1 -0
  694. package/src/lib/app-utils/visitor-record-ceiling.d.ts +223 -0
  695. package/src/lib/app-utils/visitor-record-ceiling.js +215 -0
  696. package/src/lib/app-utils/visitor-record-ceiling.js.map +1 -0
  697. package/src/lib/app-utils/web-vitals-rum.d.ts +53 -0
  698. package/src/lib/app-utils/web-vitals-rum.js +278 -0
  699. package/src/lib/app-utils/web-vitals-rum.js.map +1 -0
  700. package/src/lib/app-utils/webhook-delivery.d.ts +324 -0
  701. package/src/lib/app-utils/webhook-delivery.js +413 -0
  702. package/src/lib/app-utils/webhook-delivery.js.map +1 -0
  703. package/src/lib/app-utils/where-used.d.ts +57 -0
  704. package/src/lib/app-utils/where-used.js +59 -0
  705. package/src/lib/app-utils/where-used.js.map +1 -0
  706. package/src/lib/app-utils/wistia-embed.d.ts +86 -0
  707. package/src/lib/app-utils/wistia-embed.js +119 -0
  708. package/src/lib/app-utils/wistia-embed.js.map +1 -0
  709. package/src/lib/app-utils/workflows.d.ts +136 -0
  710. package/src/lib/app-utils/workflows.js +346 -0
  711. package/src/lib/app-utils/workflows.js.map +1 -0
  712. package/src/lib/canvas-manager/canvas-manager-tree.d.ts +109 -0
  713. package/src/lib/canvas-manager/canvas-manager-tree.js +139 -0
  714. package/src/lib/canvas-manager/canvas-manager-tree.js.map +1 -0
  715. package/src/lib/canvas-manager/canvas-manager.d.ts +549 -0
  716. package/src/lib/canvas-manager/canvas-manager.js +1311 -0
  717. package/src/lib/canvas-manager/canvas-manager.js.map +1 -0
  718. package/src/lib/canvas-manager/index.d.ts +20 -0
  719. package/src/lib/canvas-manager/index.js +21 -0
  720. package/src/lib/canvas-manager/index.js.map +1 -0
  721. package/src/lib/canvas-manager/stores/index.d.ts +17 -0
  722. package/src/lib/canvas-manager/stores/index.js +18 -0
  723. package/src/lib/canvas-manager/stores/index.js.map +1 -0
  724. package/src/lib/canvas-manager/stores/node.store.d.ts +30 -0
  725. package/src/lib/canvas-manager/stores/node.store.js +45 -0
  726. package/src/lib/canvas-manager/stores/node.store.js.map +1 -0
  727. package/src/lib/components-manager/components-manager.d.ts +56 -0
  728. package/src/lib/components-manager/components-manager.js +191 -0
  729. package/src/lib/components-manager/components-manager.js.map +1 -0
  730. package/src/lib/components-manager/index.d.ts +18 -0
  731. package/src/lib/components-manager/index.js +19 -0
  732. package/src/lib/components-manager/index.js.map +1 -0
  733. package/src/lib/emit-manager/emit-manager.d.ts +56 -0
  734. package/src/lib/emit-manager/emit-manager.js +60 -0
  735. package/src/lib/emit-manager/emit-manager.js.map +1 -0
  736. package/src/lib/emit-manager/index.d.ts +18 -0
  737. package/src/lib/emit-manager/index.js +19 -0
  738. package/src/lib/emit-manager/index.js.map +1 -0
  739. package/src/lib/foundation/constants/_internal.d.ts +18 -0
  740. package/src/lib/foundation/constants/_internal.js +62 -0
  741. package/src/lib/foundation/constants/_internal.js.map +1 -0
  742. package/src/lib/foundation/constants/app.d.ts +23 -0
  743. package/src/lib/foundation/constants/app.js +25 -0
  744. package/src/lib/foundation/constants/app.js.map +1 -0
  745. package/src/lib/foundation/constants/canvas.d.ts +18 -0
  746. package/src/lib/foundation/constants/canvas.js +18 -0
  747. package/src/lib/foundation/constants/canvas.js.map +1 -0
  748. package/src/lib/foundation/constants/components.d.ts +54 -0
  749. package/src/lib/foundation/constants/components.js +61 -0
  750. package/src/lib/foundation/constants/components.js.map +1 -0
  751. package/src/lib/foundation/constants/shared.d.ts +50 -0
  752. package/src/lib/foundation/constants/shared.js +49 -0
  753. package/src/lib/foundation/constants/shared.js.map +1 -0
  754. package/src/lib/foundation/constants/symbol.d.ts +63 -0
  755. package/src/lib/foundation/constants/symbol.js +75 -0
  756. package/src/lib/foundation/constants/symbol.js.map +1 -0
  757. package/src/lib/foundation/definitions/components.types.d.ts +372 -0
  758. package/src/lib/foundation/definitions/components.types.js +180 -0
  759. package/src/lib/foundation/definitions/components.types.js.map +1 -0
  760. package/src/lib/foundation/definitions/contact.types.d.ts +73 -0
  761. package/src/lib/foundation/definitions/contact.types.js +107 -0
  762. package/src/lib/foundation/definitions/contact.types.js.map +1 -0
  763. package/src/lib/foundation/definitions/host-content-collections.d.ts +137 -0
  764. package/src/lib/foundation/definitions/host-content-collections.js +219 -0
  765. package/src/lib/foundation/definitions/host-content-collections.js.map +1 -0
  766. package/src/lib/foundation/definitions/org-billing.types.d.ts +1361 -0
  767. package/src/lib/foundation/definitions/org-billing.types.js +77 -0
  768. package/src/lib/foundation/definitions/org-billing.types.js.map +1 -0
  769. package/src/lib/foundation/definitions/organization.types.d.ts +307 -0
  770. package/src/lib/foundation/definitions/organization.types.js +42 -0
  771. package/src/lib/foundation/definitions/organization.types.js.map +1 -0
  772. package/src/lib/foundation/definitions/platform.types.d.ts +1326 -0
  773. package/src/lib/foundation/definitions/platform.types.js +124 -0
  774. package/src/lib/foundation/definitions/platform.types.js.map +1 -0
  775. package/src/lib/foundation/definitions/property-kinds.d.ts +189 -0
  776. package/src/lib/foundation/definitions/property-kinds.js +565 -0
  777. package/src/lib/foundation/definitions/property-kinds.js.map +1 -0
  778. package/src/lib/foundation/definitions/shared.d.ts +23 -0
  779. package/src/lib/foundation/definitions/shared.js +18 -0
  780. package/src/lib/foundation/definitions/shared.js.map +1 -0
  781. package/src/lib/foundation/definitions/write-deny-coverage.util.d.ts +249 -0
  782. package/src/lib/foundation/definitions/write-deny-coverage.util.js +499 -0
  783. package/src/lib/foundation/definitions/write-deny-coverage.util.js.map +1 -0
  784. package/src/lib/foundation/index.d.ts +32 -0
  785. package/src/lib/foundation/index.js +67 -0
  786. package/src/lib/foundation/index.js.map +1 -0
  787. package/src/lib/lifecycle.d.ts +24 -0
  788. package/src/lib/lifecycle.js +39 -0
  789. package/src/lib/lifecycle.js.map +1 -0
  790. package/src/lib/mst/common.d.ts +17 -0
  791. package/src/lib/mst/common.js +23 -0
  792. package/src/lib/mst/common.js.map +1 -0
  793. package/src/lib/mst/index.d.ts +19 -0
  794. package/src/lib/mst/index.js +20 -0
  795. package/src/lib/mst/index.js.map +1 -0
  796. package/src/lib/mst/json.d.ts +22 -0
  797. package/src/lib/mst/json.js +23 -0
  798. package/src/lib/mst/json.js.map +1 -0
  799. package/src/lib/mst/types.d.ts +20 -0
  800. package/src/lib/mst/types.js +18 -0
  801. package/src/lib/mst/types.js.map +1 -0
  802. package/src/lib/plugin-manager/billing-webhook-hooks.d.ts +77 -0
  803. package/src/lib/plugin-manager/billing-webhook-hooks.js +55 -0
  804. package/src/lib/plugin-manager/billing-webhook-hooks.js.map +1 -0
  805. package/src/lib/plugin-manager/commerce-zone-props.d.ts +172 -0
  806. package/src/lib/plugin-manager/commerce-zone-props.js +31 -0
  807. package/src/lib/plugin-manager/commerce-zone-props.js.map +1 -0
  808. package/src/lib/plugin-manager/custom-fields.d.ts +60 -0
  809. package/src/lib/plugin-manager/custom-fields.js +39 -0
  810. package/src/lib/plugin-manager/custom-fields.js.map +1 -0
  811. package/src/lib/plugin-manager/editor-sessions.d.ts +109 -0
  812. package/src/lib/plugin-manager/editor-sessions.js +137 -0
  813. package/src/lib/plugin-manager/editor-sessions.js.map +1 -0
  814. package/src/lib/plugin-manager/enabled-plugins.d.ts +438 -0
  815. package/src/lib/plugin-manager/enabled-plugins.js +403 -0
  816. package/src/lib/plugin-manager/enabled-plugins.js.map +1 -0
  817. package/src/lib/plugin-manager/feature-plugins.d.ts +1314 -0
  818. package/src/lib/plugin-manager/feature-plugins.js +609 -0
  819. package/src/lib/plugin-manager/feature-plugins.js.map +1 -0
  820. package/src/lib/plugin-manager/first-party-plugins.generated.d.ts +11 -0
  821. package/src/lib/plugin-manager/first-party-plugins.generated.js +149 -0
  822. package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -0
  823. package/src/lib/plugin-manager/index.d.ts +42 -0
  824. package/src/lib/plugin-manager/index.js +46 -0
  825. package/src/lib/plugin-manager/index.js.map +1 -0
  826. package/src/lib/plugin-manager/install-presets.d.ts +93 -0
  827. package/src/lib/plugin-manager/install-presets.js +86 -0
  828. package/src/lib/plugin-manager/install-presets.js.map +1 -0
  829. package/src/lib/plugin-manager/media-delivery-provider.d.ts +124 -0
  830. package/src/lib/plugin-manager/media-delivery-provider.js +53 -0
  831. package/src/lib/plugin-manager/media-delivery-provider.js.map +1 -0
  832. package/src/lib/plugin-manager/order-fulfilment.d.ts +142 -0
  833. package/src/lib/plugin-manager/order-fulfilment.js +104 -0
  834. package/src/lib/plugin-manager/order-fulfilment.js.map +1 -0
  835. package/src/lib/plugin-manager/plugin-activity-actions.d.ts +122 -0
  836. package/src/lib/plugin-manager/plugin-activity-actions.js +140 -0
  837. package/src/lib/plugin-manager/plugin-activity-actions.js.map +1 -0
  838. package/src/lib/plugin-manager/plugin-bridge.d.ts +98 -0
  839. package/src/lib/plugin-manager/plugin-bridge.js +154 -0
  840. package/src/lib/plugin-manager/plugin-bridge.js.map +1 -0
  841. package/src/lib/plugin-manager/plugin-config.d.ts +148 -0
  842. package/src/lib/plugin-manager/plugin-config.js +228 -0
  843. package/src/lib/plugin-manager/plugin-config.js.map +1 -0
  844. package/src/lib/plugin-manager/plugin-console-crons.d.ts +139 -0
  845. package/src/lib/plugin-manager/plugin-console-crons.js +177 -0
  846. package/src/lib/plugin-manager/plugin-console-crons.js.map +1 -0
  847. package/src/lib/plugin-manager/plugin-contact-capture.d.ts +220 -0
  848. package/src/lib/plugin-manager/plugin-contact-capture.js +89 -0
  849. package/src/lib/plugin-manager/plugin-contact-capture.js.map +1 -0
  850. package/src/lib/plugin-manager/plugin-contributions.d.ts +244 -0
  851. package/src/lib/plugin-manager/plugin-contributions.js +283 -0
  852. package/src/lib/plugin-manager/plugin-contributions.js.map +1 -0
  853. package/src/lib/plugin-manager/plugin-entitlement-keys.d.ts +169 -0
  854. package/src/lib/plugin-manager/plugin-entitlement-keys.js +84 -0
  855. package/src/lib/plugin-manager/plugin-entitlement-keys.js.map +1 -0
  856. package/src/lib/plugin-manager/plugin-entitlements.d.ts +181 -0
  857. package/src/lib/plugin-manager/plugin-entitlements.js +188 -0
  858. package/src/lib/plugin-manager/plugin-entitlements.js.map +1 -0
  859. package/src/lib/plugin-manager/plugin-events.d.ts +145 -0
  860. package/src/lib/plugin-manager/plugin-events.js +79 -0
  861. package/src/lib/plugin-manager/plugin-events.js.map +1 -0
  862. package/src/lib/plugin-manager/plugin-figures.d.ts +251 -0
  863. package/src/lib/plugin-manager/plugin-figures.js +252 -0
  864. package/src/lib/plugin-manager/plugin-figures.js.map +1 -0
  865. package/src/lib/plugin-manager/plugin-host-collections.d.ts +146 -0
  866. package/src/lib/plugin-manager/plugin-host-collections.js +116 -0
  867. package/src/lib/plugin-manager/plugin-host-collections.js.map +1 -0
  868. package/src/lib/plugin-manager/plugin-jobs.d.ts +130 -0
  869. package/src/lib/plugin-manager/plugin-jobs.js +182 -0
  870. package/src/lib/plugin-manager/plugin-jobs.js.map +1 -0
  871. package/src/lib/plugin-manager/plugin-loader.d.ts +86 -0
  872. package/src/lib/plugin-manager/plugin-loader.js +223 -0
  873. package/src/lib/plugin-manager/plugin-loader.js.map +1 -0
  874. package/src/lib/plugin-manager/plugin-manager.d.ts +95 -0
  875. package/src/lib/plugin-manager/plugin-manager.js +343 -0
  876. package/src/lib/plugin-manager/plugin-manager.js.map +1 -0
  877. package/src/lib/plugin-manager/plugin-metered-lines.d.ts +97 -0
  878. package/src/lib/plugin-manager/plugin-metered-lines.js +123 -0
  879. package/src/lib/plugin-manager/plugin-metered-lines.js.map +1 -0
  880. package/src/lib/plugin-manager/plugin-org-erasure.d.ts +52 -0
  881. package/src/lib/plugin-manager/plugin-org-erasure.js +100 -0
  882. package/src/lib/plugin-manager/plugin-org-erasure.js.map +1 -0
  883. package/src/lib/plugin-manager/plugin-permissions.d.ts +69 -0
  884. package/src/lib/plugin-manager/plugin-permissions.js +77 -0
  885. package/src/lib/plugin-manager/plugin-permissions.js.map +1 -0
  886. package/src/lib/plugin-manager/plugin-person-erasure.d.ts +58 -0
  887. package/src/lib/plugin-manager/plugin-person-erasure.js +109 -0
  888. package/src/lib/plugin-manager/plugin-person-erasure.js.map +1 -0
  889. package/src/lib/plugin-manager/plugin-record-cards.d.ts +99 -0
  890. package/src/lib/plugin-manager/plugin-record-cards.js +59 -0
  891. package/src/lib/plugin-manager/plugin-record-cards.js.map +1 -0
  892. package/src/lib/plugin-manager/plugin-record-facts.d.ts +111 -0
  893. package/src/lib/plugin-manager/plugin-record-facts.js +51 -0
  894. package/src/lib/plugin-manager/plugin-record-facts.js.map +1 -0
  895. package/src/lib/plugin-manager/plugin-record-routes.d.ts +129 -0
  896. package/src/lib/plugin-manager/plugin-record-routes.js +93 -0
  897. package/src/lib/plugin-manager/plugin-record-routes.js.map +1 -0
  898. package/src/lib/plugin-manager/plugin-record-timeline.d.ts +147 -0
  899. package/src/lib/plugin-manager/plugin-record-timeline.js +39 -0
  900. package/src/lib/plugin-manager/plugin-record-timeline.js.map +1 -0
  901. package/src/lib/plugin-manager/plugin-resource-drafts.d.ts +137 -0
  902. package/src/lib/plugin-manager/plugin-resource-drafts.js +51 -0
  903. package/src/lib/plugin-manager/plugin-resource-drafts.js.map +1 -0
  904. package/src/lib/plugin-manager/plugin-services.d.ts +94 -0
  905. package/src/lib/plugin-manager/plugin-services.js +153 -0
  906. package/src/lib/plugin-manager/plugin-services.js.map +1 -0
  907. package/src/lib/plugin-manager/plugin-styles-ui.d.ts +71 -0
  908. package/src/lib/plugin-manager/plugin-styles-ui.js +80 -0
  909. package/src/lib/plugin-manager/plugin-styles-ui.js.map +1 -0
  910. package/src/lib/plugin-manager/plugin-styles.d.ts +176 -0
  911. package/src/lib/plugin-manager/plugin-styles.js +186 -0
  912. package/src/lib/plugin-manager/plugin-styles.js.map +1 -0
  913. package/src/lib/plugin-manager/plugin-subprocessors.d.ts +135 -0
  914. package/src/lib/plugin-manager/plugin-subprocessors.js +129 -0
  915. package/src/lib/plugin-manager/plugin-subprocessors.js.map +1 -0
  916. package/src/lib/plugin-manager/plugin-tax-profile.d.ts +84 -0
  917. package/src/lib/plugin-manager/plugin-tax-profile.js +39 -0
  918. package/src/lib/plugin-manager/plugin-tax-profile.js.map +1 -0
  919. package/src/lib/plugin-manager/plugin-user-erasure.d.ts +53 -0
  920. package/src/lib/plugin-manager/plugin-user-erasure.js +84 -0
  921. package/src/lib/plugin-manager/plugin-user-erasure.js.map +1 -0
  922. package/src/lib/plugin-manager/plugin-zones.d.ts +121 -0
  923. package/src/lib/plugin-manager/plugin-zones.js +74 -0
  924. package/src/lib/plugin-manager/plugin-zones.js.map +1 -0
  925. package/src/lib/plugin-manager/realm-plugins.d.ts +112 -0
  926. package/src/lib/plugin-manager/realm-plugins.js +196 -0
  927. package/src/lib/plugin-manager/realm-plugins.js.map +1 -0
  928. package/src/lib/plugin-manager/realm-server.d.ts +54 -0
  929. package/src/lib/plugin-manager/realm-server.js +88 -0
  930. package/src/lib/plugin-manager/realm-server.js.map +1 -0
  931. package/src/lib/plugin-manager/record-zone-props.d.ts +126 -0
  932. package/src/lib/plugin-manager/record-zone-props.js +31 -0
  933. package/src/lib/plugin-manager/record-zone-props.js.map +1 -0
  934. package/src/lib/plugin-manager/site-page-hooks.d.ts +102 -0
  935. package/src/lib/plugin-manager/site-page-hooks.js +114 -0
  936. package/src/lib/plugin-manager/site-page-hooks.js.map +1 -0
  937. package/src/lib/plugin-manager/site-runtime.d.ts +57 -0
  938. package/src/lib/plugin-manager/site-runtime.js +25 -0
  939. package/src/lib/plugin-manager/site-runtime.js.map +1 -0
  940. package/src/lib/plugin-manager/usage-alert-contributors.d.ts +115 -0
  941. package/src/lib/plugin-manager/usage-alert-contributors.js +106 -0
  942. package/src/lib/plugin-manager/usage-alert-contributors.js.map +1 -0
  943. package/src/lib/types/ast.d.ts +199 -0
  944. package/src/lib/types/ast.js +20 -0
  945. package/src/lib/types/ast.js.map +1 -0
  946. package/src/lib/types/index.d.ts +20 -0
  947. package/src/lib/types/index.js +21 -0
  948. package/src/lib/types/index.js.map +1 -0
  949. package/src/lib/types/nodes.d.ts +608 -0
  950. package/src/lib/types/nodes.js +28 -0
  951. package/src/lib/types/nodes.js.map +1 -0
  952. package/src/lib/types/routing.d.ts +17 -0
  953. package/src/lib/types/routing.js +18 -0
  954. package/src/lib/types/routing.js.map +1 -0
  955. package/src/lib/types/screen.d.ts +23 -0
  956. package/src/lib/types/screen.js +18 -0
  957. package/src/lib/types/screen.js.map +1 -0
  958. package/src/lib/utils.d.ts +25 -0
  959. package/src/lib/utils.js +42 -0
  960. package/src/lib/utils.js.map +1 -0
  961. package/src/server.d.ts +37 -0
  962. package/src/server.js +55 -0
  963. package/src/server.js.map +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/docs-index.generated.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n// GENERATED FILE — do not edit. Regenerate with:\n// node tools/scripts/generate-assist-docs-index.mjs\n// Source of truth: apps/docs/docs content sections (AGL-1860).\n// SERVER-ONLY: imported by the assist API route; never ship client-side.\n\nexport interface AssistDocsSection {\n /** Docs-site path, e.g. `/building-sites/besigner/overview`. */\n path: string\n /** Docs page title. */\n title: string\n /** H2 section heading ('' for the page intro before the first H2). */\n heading: string\n /** Heading anchor including '#', or '' for the page intro. */\n anchor: string\n /** Plain-text section content, capped for retrieval context. */\n text: string\n}\n\nexport const ASSIST_DOCS_INDEX: readonly AssistDocsSection[] = [\n { path: '/ai/ab-tests-with-ai', title: 'A/B tests by AI: write variants, read the result', heading: '', anchor: '', text: 'A/B tests by AI\\nRolling out A/B tests by AI is a release-flagged feature, currently being rolled out — it is not available in your workspace yet. This page says what it does, and grows with the feature.\\nThe test itself is yours. The experiment, its variants, the traffic split, the conversion goal and the counters all belong to the Experiments card on your site\\'s Marketing page. Two AI cards sit inside that card: one writes variants while you are setting a test up, and one reads a finished test\\'s figures back in plain words.\\nNeither of them starts a test, pauses one, finishes one or moves a visitor.' },\n { path: '/ai/ab-tests-with-ai', title: 'A/B tests by AI: write variants, read the result', heading: 'It proposes; you write', anchor: '#it-proposes-you-write', text: 'Write variants with AI fills the experiment editor\\'s own fields, unsaved. The dialog\\'s Save is the only write — the same one a variant you typed yourself takes, and Cancel leaves the test exactly as it was.\\nExplain this result with AI has nothing to apply at all. It is words about figures that were already counted.\\nNothing either card returns sets a traffic weight, a goal or a schedule, and nothing it returns reaches a visitor.' },\n { path: '/ai/ab-tests-with-ai', title: 'A/B tests by AI: write variants, read the result', heading: 'Write variants', anchor: '#write-variants', text: 'The card sits in the experiment editor, beneath the list of variants it writes for.\\n1. Give it the copy under test — the headline and copy as they stand on the page, or the subject line and message as they stand. For an email the control\\'s subject and body are filled in from the first variant, so usually there is nothing to paste.\\n2. Write variants. It writes between two and four: a test needs a first variant and at least one to compare against it, and the card stores no more than four. The first one it writes is your copy unchanged, so the test has something to measure against.\\n3. Each variant comes back with a name, its copy, and one sentence saying what it changes and what that is testing.\\nWhat a variant varies depends on what the test varies. An email variant varies its subject line, its preheader and its body; a screen or a section variant varies the copy on it. A variant that fills the other kind\\'s fields has them dropped.\\nPutting them in {#putting-them-in}\\nPut into the variants places the proposal in the editor\\'s own fields, unsaved:\\n- For an email, each variant takes its name, subject and body.\\n- For a screen or a section, each variant takes its name only. The copy itself is a screen version, so you make one per variant in the editor and pin it above — read the proposed copy on the card and build from it.\\nThen review them and Save the experiment, or Cancel.\\nThe editor\\'s list is the test\\'s shape, and the card does not change it: a proposal is mapped onto the variants you already have, in order, and anything past them is ignored. Add a variant row first if you want a fourth.\\nTwo things are refused rather than pasted. The fields are plain text, so a variant carrying markup is dropped; and a variant whose copy repeats another\\'s is dropped, since two identical arms a' },\n { path: '/ai/ab-tests-with-ai', title: 'A/B tests by AI: write variants, read the result', heading: 'Read a result', anchor: '#read-a-result', text: 'The second card sits in a test\\'s results dialog, below the figures. Explain this result with AI reads the same figures you are looking at.\\nThe verdict is decided before the model is asked {#the-verdict}\\nThis is the part worth understanding, because it is the opposite of how it looks. The product does not ask a model what your test showed and print the answer. It works out what the figures support from the counts alone, and only then asks for the words.\\nThree things have to hold before a comparison is read at all:\\n- Enough visitors. Every variant needs at least 200 exposures. Below that the test is early rather than inconclusive, and those are different instructions: one says wait, the other says stop.\\n- Enough conversions. At the pooled rate across the test, each variant must expect at least five conversions and five non-conversions — the condition the comparison behind the confidence figure rests on. This is what stops a test with a hundred thousand visitors and four conversions reading as a decided one.\\n- A bar that rises with the number of challengers. One comparison is called at 95%, the same confidence the card\\'s own auto-winner uses. But testing three challengers at 95% each would call a winner by chance about one time in seven, so the bar is corrected upward for every extra chance taken. Four variants are held to a stricter bar than two.\\nWhat comes out is one of five readings:\\n| What the figures support | What the card says |\\n| A challenger is ahead by more than chance explains | One variant is ahead |\\n| Every challenger is behind by more than chance explains | The first variant is ahead |\\n| A variant has not been shown to enough visitors, or too little has converted | No result yet |\\n| Enough has happened, and nothing separates the variants | No winner |\\n| These' },\n { path: '/ai/ab-tests-with-ai', title: 'A/B tests by AI: write variants, read the result', heading: 'What is sent', anchor: '#what-is-sent', text: '- For variants — the copy you gave it, up to 4,000 characters, along with the test\\'s name, what it varies and the conversion goal it is trying to move.\\n- For an explanation — the test\\'s name and each variant\\'s counts: exposures, conversions, rate, lift and confidence, as the results already report them.\\nNo contact, lead, deal, form submission or list member is read for either.' },\n { path: '/ai/ab-tests-with-ai', title: 'A/B tests by AI: write variants, read the result', heading: 'Who can use it', anchor: '#who-can-use-it', text: 'A/B testing is a plan feature of its own: it starts at the Business plan and is not included below it. Where a workspace does not have it, the Experiments card offers an upgrade note in place of the editor and the list — so there is no experiment to open, and neither AI card has anywhere to appear.\\nAbove that, both cards need what every generative surface needs: AI generation open on your workspace and the Generate with AI permission on your role — see Custom roles & permissions. Where either is missing the card is simply absent rather than shown and disabled, so an experiment editor looks as it always did.\\nBoth cards draw on your workspace\\'s AI credits like any other AI request, and the same allotments and caps apply.' },\n { path: '/ai/ab-tests-with-ai', title: 'A/B tests by AI: write variants, read the result', heading: 'Related', anchor: '#related', text: '- How Aglyn AI builds\\n- Marketing overlays\\n- Email campaigns\\n- Rewrite and write copy with AI\\n- AI allotments, usage and model choice\\n- Aglyn AI' },\n { path: '/ai/agency-sites', title: 'An AI website builder for agencies', heading: '', anchor: '', text: 'An AI website builder for agencies\\nAn agency\\'s problem with a website builder is rarely the first site. It is the twentieth: twenty logins, twenty billing relationships, twenty slightly different stacks, and a first draft that costs the same on the twentieth site as it did on the first.\\nAglyn AI runs from the console your team already signs in to, against the sites your organization already owns. One brief can start a build on many client sites at once — and each one stays a separate job you review.\\nRolling out Aglyn AI is a release-flagged feature, currently being rolled out — it is not available in every workspace yet. This page says how a batch behaves, and grows with the feature.' },\n { path: '/ai/agency-sites', title: 'An AI website builder for agencies', heading: 'One brief, many client sites', anchor: '#one-brief', text: 'A batch takes the brief once and starts a site build job per site you picked. It is not one job writing twenty sites: each site gets its own plan, its own confirmation and its own drafts, so a plan you dislike on one client costs you that client\\'s credits and nothing else.\\nThe batch also asks once where form submissions should go, and binds that answer to every job it starts — the Inbox, or the Inbox and a CRM lead for each message that carries an email address. You can change it on any individual form afterwards.' },\n { path: '/ai/agency-sites', title: 'An AI website builder for agencies', heading: 'What is shared, and what is not', anchor: '#shared-and-not', text: 'The brief is shared. Nothing else is.\\nEach job reads the site it is building for — that site\\'s theme, its layout, its components and its forms — and builds from those, following the building rules. Two clients with the same brief and different themes get drafts that look like their own sites, because the generator has each site\\'s own material to build from rather than a prompt and a blank page.' },\n { path: '/ai/agency-sites', title: 'An AI website builder for agencies', heading: 'The organization model it assumes', anchor: '#organization-model', text: 'A batch operates on the sites inside one organization. If your agency runs every client as a site in a single workspace, a batch can reach all of them; if each client has its own separate organization, a batch reaches one client at a time.\\nWhich of those to run is a decision that predates AI and outlives it — running an agency workspace covers the trade-offs, and AI does not change them.' },\n { path: '/ai/agency-sites', title: 'An AI website builder for agencies', heading: 'Keeping spend separate per client', anchor: '#per-client-spend', text: 'Credits are a workspace pool, so without a limit one busy client can draw down what another\\'s work needs. An allotment is how you divide it: a monthly number of credits for a whole site, a member, or a site collaborator, counted from the first of the month.\\nA hard allotment stops AI requests for that site once it is used; a soft one records the same number and lets work continue. Either way an allotment is a share of the pool and never an addition to it — the workspace\\'s own limits still apply first. AI allotments, usage and model choice has the full behavior, including limiting which models a client\\'s site may use.' },\n { path: '/ai/agency-sites', title: 'An AI website builder for agencies', heading: 'Turning AI off for one client', anchor: '#off-for-one-site', text: 'A client who does not want generative work on their site can be opted out without affecting anyone else. Switching AI off for a site hides the assistant, Describe it, the AI cards and the editor\\'s AI controls on that site, refuses every AI request made for it, and stops its queued jobs without spending credits.\\nIt does not stop the workspace\\'s AI add-on, its credits, its allotments or its billing, and AI keeps working on the workspace\\'s other sites.' },\n { path: '/ai/agency-sites', title: 'An AI website builder for agencies', heading: 'White-label is a separate setting', anchor: '#white-label', text: 'Generating a client\\'s site and removing Aglyn\\'s name from it are two different features. White-label replaces the Aglyn brand across the console, your published sites and transactional email; it is its own setting with its own entitlement, and having the AI add-on does not include it.' },\n { path: '/ai/agency-sites', title: 'An AI website builder for agencies', heading: 'Who publishes', anchor: '#who-publishes', text: 'Nobody\\'s client site goes live because a job finished. Every generation writes an unpublished draft, or a new unpublished version of something that already exists — it never flips a published version, registers an address on a live site, changes navigation or sends anything.\\nAn account manager opens each draft in the editor, changes what they want, and publishes it. On twenty sites that is twenty deliberate acts by a person, which is the point: the batch removes the typing, not the review.' },\n { path: '/ai/agency-sites', title: 'An AI website builder for agencies', heading: 'Related', anchor: '#related', text: '- Generate a website from a prompt — what a single site job plans and builds\\n- How Aglyn AI builds — the rules every job is held to\\n- AI allotments, usage and model choice — dividing the pool per site or member\\n- Running an agency workspace — one workspace or many\\n- White-label — replacing the Aglyn brand' },\n { path: '/ai/ai-allotments', title: 'AI allotments, usage and model choice', heading: '', anchor: '', text: 'AI allotments, usage and model choice\\nA workspace\\'s AI credits are one pool. Allotments decide how much of that pool one person or one site may draw each month, so one client site, one contractor or one teammate cannot spend what everyone else was counting on. Everyone sees their own usage while they work, and can choose which model answers — or leave it on Auto.' },\n { path: '/ai/ai-allotments', title: 'AI allotments, usage and model choice', heading: 'Allotments', anchor: '#allotments', text: 'An allotment is a number of credits a month, counted from the first of the month (UTC). It can be set on:\\n- A team member — it counts what they draw on every site.\\n- A site collaborator, on one site — it counts what they draw on that site.\\n- A whole site — it counts what everyone draws on that site, together. An agency can give each client site its own share, so one site running out never touches another.\\nWhen more than one applies to a request — a collaborator\\'s own allotment and their site\\'s — each one is checked, and the tightest is the one you see.\\nHard or soft {#hard-or-soft}\\n- Hard stops AI requests once the allotment is used for the month. The message says who can raise it.\\n- Soft keeps requests working, and notifies the person and the workspace\\'s owners and admins — in the console and by email — when the month passes 80% and again at 100%, once per threshold per month.\\nAn allotment is checked against what was drawn before a request, the same way the workspace\\'s own credits are, so a request that starts under the line can finish a little past it. The next request is the one a hard allotment stops.\\nThe workspace\\'s credits still come first {#the-pool-comes-first}\\nAn allotment is a share of the pool, never an addition to it. The workspace\\'s own limits — its included credits, a stop at the included band, an overage ceiling — are checked first. An allotment larger than the pool is allowed; the pool simply stops first.\\nLimiting models {#limiting-models}\\nAn allotment can also carry a list of models. The person or site then runs only on those models, including when Auto chooses. On Agency and Enterprise, the organization can restrict the models everyone uses the same way.' },\n { path: '/ai/ai-allotments', title: 'AI allotments, usage and model choice', heading: 'Who can set them', anchor: '#who-can-set-them', text: '- Billing → Usage → AI allotments lists team members and sites with this month\\'s credits and their allotments. Select several to set one allotment for all of them, or use Same for every client site to give every site the same share at once. Seeing the section requires View billing; changing it requires Manage billing.\\n- A member\\'s page under Team shows their allotment — one across every site for a team member, one per site for a site collaborator.\\n- A site\\'s Users card has an AI allotment column for its collaborators and a Site AI allotment card for the site. A collaborator who is the site\\'s Admin can set the other collaborators\\' allotments on that site. Nobody can raise their own.\\nEvery change is recorded in the organization\\'s activity feed.' },\n { path: '/ai/ai-allotments', title: 'AI allotments, usage and model choice', heading: 'Your usage while you work', anchor: '#usage-strip', text: 'The assistant panel and the AI dialogs in the Besigner show a compact usage line once you have made a request:\\n- You — your credits this month, against your allotment when one applies.\\n- Workspace — the pool\\'s credits this month.\\n- Last request — what the request you just made cost.\\n- The model that answered.\\nFrom 80% of an allotment or of the pool it warns, and at a hard allotment it says who can raise it. The line updates from the answer to each request, so it never costs a request of its own; after a reload it shows where you stood at your last request.' },\n { path: '/ai/ai-allotments', title: 'AI allotments, usage and model choice', heading: 'Choosing a model', anchor: '#choosing-a-model', text: 'The Model switch beside the assistant\\'s message box and in each AI dialog lists:\\n- Auto, the default. The platform picks a model for each kind of request, and keeps to any model list your allotment or your organization set.\\n- The models your plan offers, each with the credits a typical request costs on it and how that compares with Auto. Once your workspace has made a few requests of that kind, the typical request is measured from them.\\nYour pick is remembered for you on each surface — the assistant, a rewrite, a generated section. If your plan or an allotment no longer allows it, the request runs on Auto. Free workspaces always run on Auto.' },\n { path: '/ai/ai-allotments', title: 'AI allotments, usage and model choice', heading: 'Related', anchor: '#related', text: '- AI Assist\\n- Billing & Plans: AI allotments\\n- Invite teammates: AI usage per member' },\n { path: '/ai/automations-with-ai', title: 'Explain automations with AI', heading: '', anchor: '', text: 'Explain automations with AI\\nOn Automation → Actions, Aglyn AI can explain an automation you already have, and tell you why one of its runs failed. Neither of them changes or runs an automation.\\nRolling out Aglyn AI is a release-flagged feature, currently being rolled out — it is not available in every workspace yet. This page says what it does with automations, and grows with the feature.' },\n { path: '/ai/automations-with-ai', title: 'Explain automations with AI', heading: 'Drafting an automation is not available yet', anchor: '#draft', text: 'Describing an automation and having Aglyn AI build it — \"when someone submits the newsletter form, add them to the newsletter list, make them a lead and send them a welcome email\" — is not open yet, on any plan.\\nThe work is done up to a point a draft is not yet worth handing you: a description asking for several steps can come back as a draft holding one, because the answer is cut short at its size ceiling rather than re-asked. A one-step draft of a three-step description is worse than no draft, so the door stays shut until that is fixed.\\nWhat follows on this page — Explain it and Why did this fail? — does not depend on it and is unaffected. When drafting opens, this section is replaced by how to use it.' },\n { path: '/ai/automations-with-ai', title: 'Explain automations with AI', heading: 'Explain an automation', anchor: '#explain', text: 'Open a saved action or workflow and choose Explain it at the top of the editor. In a few minutes you get a plain-words account of what it does, step by step, and anything worth checking, such as a list your site no longer has or a placeholder nobody has filled in. It reads the automation as it is saved, so save your changes first.' },\n { path: '/ai/automations-with-ai', title: 'Explain automations with AI', heading: 'Find out why a run failed', anchor: '#why-a-run-failed', text: 'In an automation\\'s Runs log, a failed run has Why did this fail?. It reads what the run history recorded about that run, together with the automation\\'s settings, and answers with the most likely cause and how to fix it. Opening it again shows the same answer.' },\n { path: '/ai/automations-with-ai', title: 'Explain automations with AI', heading: 'What Aglyn AI is sent', anchor: '#what-is-sent', text: '- To explain an automation: how it is set up, including the text of its emails and messages and the names of the records it uses. Email addresses are removed first, a teammate is described only as a teammate, and on-page code is left out.\\n- To explain a failed run: also when the run happened, what it did and the errors it recorded, with email addresses removed. What a visitor submitted, such as what they entered in a form, is never read.\\nNo contact, lead, deal, form submission or list member is read for any of them.' },\n { path: '/ai/automations-with-ai', title: 'Explain automations with AI', heading: 'Who can use it', anchor: '#who-can-use-it', text: 'These jobs need the Generate with AI permission, on a site whose Automation plugin is on. See who can use Aglyn AI. A site that has switched AI off shows none of these controls. Each job spends AI credits; an automation\\'s own runs count against your site\\'s action runs, never your AI credits.' },\n { path: '/ai/automations-with-ai', title: 'Explain automations with AI', heading: 'Related', anchor: '#related', text: '- Actions builder\\n- Aglyn AI\\n- How Aglyn AI builds' },\n { path: '/ai/copy-assist', title: 'Rewrite and write copy with AI', heading: '', anchor: '', text: 'Rewrite and write copy with AI\\nRewrite with AI drafts and rewrites text without leaving the Besigner. It works on any text an element carries — a headline, body copy, a button label, a caption — and on a blog entry.\\nRolling out Writing copy with AI is part of Aglyn AI, a release-flagged feature currently being rolled out — it is not available in every workspace yet. This page says what it does, and grows with the feature.' },\n { path: '/ai/copy-assist', title: 'Rewrite and write copy with AI', heading: 'Use it', anchor: '#use-it', text: '1. Select a text-capable element on the canvas.\\n2. Open the element\\'s fields in the Attributes panel and choose Rewrite with AI, under the text you want changed.\\n3. Describe the change, or leave it empty to have the text rewritten as it stands.\\n4. Read what comes back, then apply it.\\nWhat you apply is an unsaved change on the version you have open, exactly like typing the words yourself. One undo takes it back, and nothing reaches your live site until you publish. On the version your live site shows, the Besigner asks you to make a new version first.' },\n { path: '/ai/copy-assist', title: 'Rewrite and write copy with AI', heading: 'What you get back', anchor: '#what-you-get-back', text: '- Your voice, not a house style. The rewrite follows the tone of the copy already on the page and the brief you gave.\\n- Finished sentences. No lorem ipsum, and no heading or paragraph cut off partway.\\n- Square brackets rather than invention. Where your brief leaves out a fact — a phone number, an address, a price — the copy marks the gap in square brackets instead of making one up. Fill those in before you publish.' },\n { path: '/ai/copy-assist', title: 'Rewrite and write copy with AI', heading: 'Tips', anchor: '#tips', text: '- Say who it is for and what it should do. \"Shorter, for a tradesperson in a hurry, ending in a call to book\" beats \"make it better\".\\n- Treat what comes back as a first draft and edit it inline afterward with inline text editing.\\n- For structure rather than words, use Generate a section.' },\n { path: '/ai/copy-assist', title: 'Rewrite and write copy with AI', heading: 'Who can use it', anchor: '#who-can-use-it', text: 'Rewriting needs AI generation on your plan and the Generate with AI permission — see who can use Aglyn AI. A site that has switched AI off shows none of these controls. Each rewrite spends AI credits like any other AI request.' },\n { path: '/ai/copy-assist', title: 'Rewrite and write copy with AI', heading: 'Related', anchor: '#related', text: '- Generate a section\\n- Aglyn AI\\n- How Aglyn AI builds' },\n { path: '/ai/crm-by-ai', title: 'The AI CRM built into Aglyn', heading: '', anchor: '', text: 'The AI CRM built into Aglyn\\nAglyn AI works in three places in the CRM. In each one it makes a suggestion and you decide what happens next: nothing is saved, sent or imported until you do it yourself.\\n- On a record\\'s page, a short summary of where things stand with a contact, company, deal or lead, with a suggested next step.\\n- In the email composer, a draft of the one-to-one email you describe.\\n- In an import, a suggested match between the columns of your file and the fields they fill.\\nRolling out Aglyn AI is a release-flagged feature, currently being rolled out — it is not available in every workspace yet. This page says what it does in the CRM, and grows with the feature.' },\n { path: '/ai/crm-by-ai', title: 'The AI CRM built into Aglyn', heading: 'Summarize a record', anchor: '#summarize-a-record', text: 'On a contact\\'s, company\\'s, deal\\'s or lead\\'s page, Summarize this contact (or company, deal or lead) writes at most two sentences from the record\\'s timeline: when it was last in touch and how, and what is still open. The date under the summary is the day it was written.\\nWhat comes with the summary depends on the record:\\n- Contacts, companies and deals get a suggested next step when one is worth doing: a task with a title, a kind (call, email, meeting or to-do), a priority, a due date within the next 30 days, and the reason for it. Create task opens the CRM\\'s task form with the suggestion filled in and linked to the record. Change anything you like; the task is saved only when you press Save. No step is suggested when an open task already covers it.\\n- Deals also get a suggested stage when the timeline shows the deal has moved on, such as a proposal accepted on a call. Move to asks you to confirm, and the deal then moves exactly as it does from its stage card, including any automation that runs on a stage change. AI only suggests open stages: winning or losing a deal is always your call.\\n- Leads get a sentence on why the lead stands where it does: its status, how it came in, and how often and how recently it was active. The CRM keeps no lead score, and AI does not make one up.\\nSummaries are reused until the record changes\\nA summary is written from the record as it is at that moment. Summarize again on a record that has not changed shows the same summary again without writing a new one, and it does not use your AI allowance. A new timeline entry, a new or finished task, or an edited note makes the next summary a new one.\\nA summary is kept for two weeks. Anyone in your workspace who can open the record can read its summary; other members cannot.' },\n { path: '/ai/crm-by-ai', title: 'The AI CRM built into Aglyn', heading: 'Draft a one-to-one email', anchor: '#draft-an-email', text: 'In the email composer on a contact, a deal or a lead, describe what the email should say (for example, \"follow up on the quote and offer a call next week\") and press Draft the message. The subject and message are filled in from what you asked and the record\\'s timeline, greeted and signed with merge fields such as {{contact.firstName}}, which are filled in when the email is sent. If you have already written a message, you are asked before it is replaced.\\nAI never sends an email. Read the draft, edit it, and press Send yourself. A draft that includes an email address or a phone number is not offered, and AI is told never to make up a price, a date or a promise that neither your request nor the record mentions, so check any it states before you send.\\nOther members of your workspace cannot open a draft you asked for. What you type into the request is kept with the AI request, as every AI request is, and members of your workspace can see it in the list of AI jobs, so describe the email rather than pasting private details into the request.' },\n { path: '/ai/crm-by-ai', title: 'The AI CRM built into Aglyn', heading: 'Match an import\\'s columns', anchor: '#match-columns', text: 'In a contacts, companies, deals or leads import, choose your file, then press Match columns. The import\\'s matching table fills in with a suggested field for each column that has one, and the preview shows what the suggestion produces. Change any match, then press Import when the preview looks right.\\nYour file\\'s rows are never sent. For each column, only its header and the kind of values under it are: email addresses, phone numbers, dates, numbers, yes-or-no values, web addresses, text, or an empty column. That kind is worked out in your browser. Matching works on files of up to 60 columns.\\nOther members of your workspace cannot see the matches suggested for your file.' },\n { path: '/ai/crm-by-ai', title: 'The AI CRM built into Aglyn', heading: 'What AI reads from a record', anchor: '#what-is-sent', text: 'The CRM decides what AI may read, and it reads the same record you would see. For a summary or an email draft, AI is given:\\n- the record\\'s name, and for a contact their job title, company, lifecycle stage and tags;\\n- how the person came in (for example a form, a booking or an order), a count of their orders and the day of the last one, when they were added, and when they last opened or clicked an email;\\n- for a company, its domain, industry and how many people it has; for a deal, its pipeline and stages, stage, status, amount, expected close date, lost reason, the names of the person and company it is with, and how many products it has; for a lead, its status, how it was captured and how many times, when it was first and last seen, whether it is assigned or converted, and why it was unqualified;\\n- the record\\'s notes (their first 600 characters);\\n- its 12 newest timeline entries: the day, the kind of activity, an email\\'s subject and delivery state, and the first 280 characters of what was logged;\\n- up to 8 open tasks and, for a contact or company, up to 5 deals.\\nAI is never given an email address, phone number or postal address field, anyone\\'s marketing consent, a custom field\\'s value, which team member owns a record or a task, or a record\\'s id. When an email address or a phone number is typed into any text that is sent, such as a name, a note, a logged activity, or a task\\'s or deal\\'s title, it is replaced before the text is sent. A street address typed into a note is not recognized, so it is sent as typed.' },\n { path: '/ai/crm-by-ai', title: 'The AI CRM built into Aglyn', heading: 'Who can use it', anchor: '#who-can-use-it', text: 'CRM by AI needs the Generate with AI permission (see the AI permissions) and the CRM\\'s own access to the record: the Manage data permission and access to the record\\'s site. At the organization level, it works for members who can reach every site. On a site that has switched AI off, none of it appears on that site\\'s pages.' },\n { path: '/ai/crm-by-ai', title: 'The AI CRM built into Aglyn', heading: 'Related', anchor: '#related', text: '- The contact record\\n- Deals\\n- Leads\\n- Import contacts from CSV\\n- How Aglyn AI builds' },\n { path: '/ai/generate-a-form', title: 'Generate a form from a description', heading: '', anchor: '', text: 'Generate a form from a description\\nAn AI build job can make a form from a brief, such as \"a quote request form for a roofing company\". The job proposes a plan first, and builds once you confirm it: the fields a visitor fills in, the answers the form needs, a marketing consent box when the form asks for an email address, and where each submission goes. The form follows the building rules, and one more of its own: what the form\\'s design draws and what the form declares always agree, so every field has its own name and every submission arrives with all of its answers.\\nThe form arrives as a new draft on the Forms page, and Open draft on the job opens the form\\'s page. No screen places it, so nothing on the live site changes. It counts against your forms allowance like a form you create yourself, and when your plan has no room for another form the job says so before it starts, not after it has spent anything.\\nRolling out Aglyn AI is a release-flagged feature, currently being rolled out — it is not available in every workspace yet. This page says what a form job does, and grows with the feature.' },\n { path: '/ai/generate-a-form', title: 'Generate a form from a description', heading: 'Describe the form', anchor: '#describe-the-form', text: 'Open Forms for the site and choose Describe it, beside Create Form. Write what the form is for in one box: who fills it in, what you need to know from them and what should happen with each submission. Then choose Plan the form.\\nThe job proposes its plan before it builds anything. Open AI jobs in the Assist panel to read it, then choose Confirm plan to build the form, or cancel.' },\n { path: '/ai/generate-a-form', title: 'Generate a form from a description', heading: 'What the form gets', anchor: '#what-the-form-gets', text: '- Fields of the types a form supports: text, email, multiline, dropdown, radio choice, checkboxes and star rating. Each has a label, and is marked Required only when the form cannot do its job without the answer.\\n- Marketing consent. When the form asks for an email address, it gets the Marketing consent field, the same one the Besigner\\'s element picker adds: a Marketing emails checkbox with the single option Email me news and offers, left unticked and named as the form\\'s Marketing consent field. A visitor who ticks it is recorded as consenting; submitting the form is never consent on its own. You can reword the label and the option on the form.\\n- Routing. Every submission lands in the Inbox. When your brief describes each submission as a sales lead, the form also files a lead in CRM → Leads, which you can switch off on the form\\'s page. When your brief asks to add people to an email list, the job tells you how: add an action on form submissions that enrolls the people who tick the consent box. The job never picks a list for you, and it never reads your lists, contacts or submissions.' },\n { path: '/ai/generate-a-form', title: 'Generate a form from a description', heading: 'What a form cannot collect', anchor: '#what-a-form-cannot-collect', text: 'A form collects text, choices and ratings. When your brief asks for something else, such as a file or photo upload, a signature or a payment, the form leaves it out and the job says so beside Open draft. For a roofing quote request that asks for photos of the damage, for example, the form asks for a written description of the damage instead. The job also lists any fact the brief did not give that the form marks in square brackets, such as a label naming your studio, for you to fill in before the form goes on a page.' },\n { path: '/ai/generate-a-form', title: 'Generate a form from a description', heading: 'Nothing is live until you place it', anchor: '#nothing-is-live-until-you-place-it', text: 'The job creates the form and nothing else. Review it on its page and in the Besigner, then place it on a screen with a Form element when you are ready. A form placed on a published screen starts collecting straight away, so check its fields and its consent box first.' },\n { path: '/ai/generate-a-form', title: 'Generate a form from a description', heading: 'Who can use it', anchor: '#who-can-use-it', text: 'Build jobs need the Generate with AI permission, and your plan must include forms. See who can use AI assist.' },\n { path: '/ai/generate-a-form', title: 'Generate a form from a description', heading: 'Related', anchor: '#related', text: '- How Aglyn AI builds\\n- Forms & Lead Capture' },\n { path: '/ai/generate-a-site', title: 'Generate a website from a prompt', heading: '', anchor: '', text: 'Generate a website from a prompt\\nAn AI build job can scaffold a whole site from a brief, such as \"a neighborhood dog groomer that takes bookings\". The job proposes a plan first — the pages it will build with their addresses and navigation, the layout they share, a contact form, and a palette suggestion made from your theme — and builds nothing until you confirm it.\\nEverything it then builds follows the building rules, because each piece is built by the same job that builds it on its own: the pages by the page job, the layout by the layout job, the form by the form job. A scaffold is those jobs run in order, not a different way of building.\\nRolling out Aglyn AI is a release-flagged feature, currently being rolled out — it is not available in every workspace yet. This page says what a site job does, and grows with the feature.' },\n { path: '/ai/generate-a-site', title: 'Generate a website from a prompt', heading: 'Starting a new site from a few questions', anchor: '#starting-a-new-site-from-a-few-questions', text: 'A site you have just created opens on its Setup page, and Start this site with AI opens over it. It asks what kind of site you are creating, who it is for, which of the starter examples you like, and where your contact form\\'s submissions should go — then writes the brief for you and plans the site from it.\\nEach answer changes what gets built. Who the site is for is who every page\\'s search listing is written for, and who the contact form asks its questions of. Where submissions go is the form\\'s routing: the Inbox, or the Inbox and a lead in CRM for every message that carries an email address. You can change that on the form itself afterwards.\\nSkip and start blank is in the bar at the top, from the moment the questions appear, and it is the same offer whether you have answered them or not. So are the close control beside it and the Escape key: all three do the same thing, and none of them stops working while the site is being planned. Taking any of them creates nothing at all: no job, no pages, no half-built site behind you. You keep the empty site you already have, on the page you were already on, and that site does not ask again.' },\n { path: '/ai/generate-a-site', title: 'Generate a website from a prompt', heading: 'What a scaffold builds', anchor: '#what-a-scaffold-builds', text: '- Four to eight pages, each with its sections, its address and its search title and description. A brief that asks for a bigger site is several jobs.\\n- A shared layout for the header, navigation and footer every page sits in.\\n- A contact form, created on the Forms page and placed on the page that needs it.\\n- A palette suggestion built from your theme\\'s own colors, which you apply in the Theme section — or do not.\\n- A welcome email draft, when you ask for one, written to the people the site is for and saying what became of the message it answers.\\n- The site\\'s own search title and description, from what you said the site is and who it is for. They are the fallback every page without its own publishes, and they wait on the site\\'s SEO section under The listing your answers describe — Put in the form stages them there, and the form\\'s Update saves them.\\n- A navigation entry per page, proposed for you to add. The live navigation is not touched.' },\n { path: '/ai/generate-a-site', title: 'Generate a website from a prompt', heading: 'Nothing is published', anchor: '#nothing-is-published', text: 'Every page arrives as an unpublished draft, and so does everything else the scaffold makes. No address on your live site resolves to a new page, the navigation is unchanged, the palette waits in the Theme section until you save it, and the welcome email is not sent. You publish what you want, when you want, through the same buttons you always use.' },\n { path: '/ai/generate-a-site', title: 'Generate a website from a prompt', heading: 'What it costs, before it starts', anchor: '#what-it-costs-before-it-starts', text: 'A scaffold is far more work than a single page, so the plan shows an estimated cost in credits beside the button that confirms it. It is an estimate: what the job really costs is what each of its steps spends, and you can watch that add up on the job while it runs.\\nIf your workspace runs out of credits partway through, the job stops and waits rather than failing. What it had already built stays, and it carries on from there once there are credits again. You can also cancel it at any point and keep what it made.' },\n { path: '/ai/generate-a-site', title: 'Generate a website from a prompt', heading: 'Watching it build', anchor: '#watching-it-build', text: 'A site takes a while — each page is built a section at a time — so the job works in the background. Open AI jobs in the Assist panel to see which step it is on, what it has made so far, and to open any draft it has finished.' },\n { path: '/ai/generate-a-site', title: 'Generate a website from a prompt', heading: 'Generate for several sites at once', anchor: '#generate-for-several-sites-at-once', text: 'On the Sites page of your organization, Generate for several sites runs one brief across many of your sites, changing the business name, the city and the brand for each. It is available on the plans that hold enough sites for it to be useful.\\nPick the sites, fill in each one\\'s details, and the card shows what one site is estimated to cost and what the whole run is. Each site then becomes its own job: each proposes its own plan, each waits for you to confirm it, and each builds only drafts. The card\\'s table shows where every site in the run has got to, with a link into each.\\nWhere do form submissions go? is asked once for the whole run, in the same two answers the guided start offers, and it routes the contact form on every site the run builds. You can change it on any one of those forms afterwards.\\nYou only see the sites you may build on, and a site you cannot is named rather than skipped quietly.' },\n { path: '/ai/generate-section', title: 'Generate a section on the canvas', heading: '', anchor: '', text: 'Generate a section on the canvas\\nWhere rewriting copy changes words, Generate a section with AI builds structure — a whole section of real components — directly on the page you have open.\\nRolling out Generating a section is part of Aglyn AI, a release-flagged feature currently being rolled out — it is not available in every workspace yet. This page says what it does, and grows with the feature.' },\n { path: '/ai/generate-section', title: 'Generate a section on the canvas', heading: 'Use it', anchor: '#use-it', text: '1. Open a page, layout or reusable component in the Besigner and select where the section should go.\\n2. Choose Generate a section with AI on the editor\\'s toolbar.\\n3. Describe the section — \"a three-column features row with icons and short blurbs\", \"a quote from a customer above a button that books a call\".\\n4. Read what comes back, then apply it.\\nThe section arrives as an unsaved change on the version you have open. One undo takes it back, nothing is published, and on the version your live site shows the Besigner asks you to make a new version first.' },\n { path: '/ai/generate-section', title: 'Generate a section on the canvas', heading: 'What it builds', anchor: '#what-it-builds', text: 'The section is built from your site\\'s real components and follows the building rules, so it drops into your design rather than sitting on top of it:\\n- Your theme\\'s colors, spacing and text styles, never a fixed color value.\\n- A grid that breaks down, showing one column on a phone and stepping up on larger screens, at your theme\\'s own breakpoints.\\n- Headings in order, so the page still reads well to screen readers and search engines.\\n- Images from your media library, each with alt text, or an empty slot for you to fill. Never an image linked from another site.\\n- A repeat built once. A block that appears three or more times becomes one reusable component placed as instances — or, on a plan without reusable components, is built into the page each time.' },\n { path: '/ai/generate-section', title: 'Generate a section on the canvas', heading: 'Tips', anchor: '#tips', text: '- Generate a rough section, then adjust it with drag-and-drop and inline text editing.\\n- Mix generated sections with the section & block library.\\n- For a whole page rather than one section, start from Generate a page, which plans the page first.' },\n { path: '/ai/generate-section', title: 'Generate a section on the canvas', heading: 'Who can use it', anchor: '#who-can-use-it', text: 'Generating a section needs AI generation on your plan and the Generate with AI permission — see who can use Aglyn AI. A site that has switched AI off shows none of these controls. Each generation spends AI credits like any other AI request.' },\n { path: '/ai/generate-section', title: 'Generate a section on the canvas', heading: 'Related', anchor: '#related', text: '- Rewrite and write copy with AI\\n- How Aglyn AI builds\\n- The Besigner' },\n { path: '/ai/how-aglyn-ai-builds', title: 'How Aglyn AI builds', heading: '', anchor: '', text: 'How Aglyn AI builds\\nWhen Aglyn AI builds for your site (a page, a component, a layout, a page template, a form or an email), it builds the way a careful designer on your team would: from what the site already has, with your theme and brand, as a draft you review. The rules below are not a suggestion in a prompt. Every plan and every generated document is checked against them before you see it.\\nRolling out Aglyn AI is a release-flagged feature, currently being rolled out — it is not available in every workspace yet. The rules on this page apply to every build as it reaches you.' },\n { path: '/ai/how-aglyn-ai-builds', title: 'How Aglyn AI builds', heading: 'Where to describe a build', anchor: '#where-to-describe-a-build', text: 'Open the site you are building for, go to the page that lists what you want built, and choose Describe it beside its create button:\\n- a page on Screens, or Describe a page in AI jobs in the Assist panel; see Generate a page;\\n- a page template on Templates; see Generate a page template;\\n- a layout on Layouts; see Generate a layout;\\n- a form on Forms; see Generate a form;\\n- a reusable component on Components; see Generate a reusable component.\\nDescribe it is there when your workspace has AI build jobs, you have the Generate with AI permission, and AI is on for the site.' },\n { path: '/ai/how-aglyn-ai-builds', title: 'How Aglyn AI builds', heading: 'The plan comes first', anchor: '#the-plan-comes-first', text: 'Before it generates anything, a build job proposes a plan:\\n- what it will reuse from your site: components, layouts, templates, forms, datasets and collections;\\n- what it will create, and why nothing you already have will do;\\n- the screens it will build, each with its layout, address, search title and sections.\\nA plan only proposes what your workspace can create: what your plan includes, and what your site still has room for. A plan the job could not build is stopped before you are asked to confirm it, with the reason. What a page\\'s plan creates, such as its layout, a card that repeats or its form, is built first when you confirm, in the same job.\\nThe job then waits for you. Open AI jobs in the Assist panel, read the plan, and choose Confirm plan to build it, or Cancel to stop. Nothing is generated until you confirm.' },\n { path: '/ai/how-aglyn-ai-builds', title: 'How Aglyn AI builds', heading: 'The building rules', anchor: '#the-building-rules', text: '1. Repeats become one reusable component. A block that appears three or more times on a page, such as the same card with different words, is built once as a reusable component and placed as instances. The AI looks for a component you already have first, and never makes a second copy of one. On a plan without reusable components, such as Free, the block is built into the page each time it repeats instead. Either way, a list of such blocks is one section of the page, never a section for each block.\\n2. Site-wide regions live in the layout. Headers, navigation, footers, announcement bars and cookie notices belong to the site\\'s layout. A generated page sits in your layout and never carries its own copy of them.\\n3. Forms are built on the Forms page, then placed. A form is created with its fields, validation, consent and routing on the Forms page, and a page places it by reference, so you edit it in one place. On a plan without saved forms, such as Free, the page carries the form itself, with its fields, and its submissions reach your inbox like any form\\'s. A search is never built as a form, because a form collects submissions: the AI places a Search Box for your site search, or a Collection Search for the entries of one collection.\\n4. Similar pages share one template. Pages that share a structure and differ by content, such as products, locations, team members or services, are built from one template or bound to a collection.\\n5. Colors, spacing and type come from your theme. Generated content uses your theme\\'s colors, spacing scale and text styles, never a fixed color value. A color your theme lacks is proposed as a theme change for you to review. A link or button on a colored band, such as a footer in your primary color, uses the band\\'s text color, so it stays readable.\\n6. E' },\n { path: '/ai/how-aglyn-ai-builds', title: 'How Aglyn AI builds', heading: 'When an answer breaks a rule', anchor: '#when-an-answer-breaks-a-rule', text: 'A plan or document that breaks a rule is not used. The AI is asked once more and told which rules it broke. If the second answer still breaks one, the job stops and shows you which rules. Choose Try again to ask once more, or cancel the job. Every answer counts toward your AI usage, including the ones that were not used.' },\n { path: '/ai/how-aglyn-ai-builds', title: 'How Aglyn AI builds', heading: 'Who can use it', anchor: '#who-can-use-it', text: 'Build jobs need the Generate with AI permission. See who can use AI assist.' },\n { path: '/ai/how-aglyn-ai-builds', title: 'How Aglyn AI builds', heading: 'Related', anchor: '#related', text: '- AI Assist\\n- AI Generate Section\\n- Generate a layout\\n- Generate a page template\\n- Generate a reusable component\\n- Generate a page from a brief\\n- Generate an email with AI' },\n { path: '/ai/overview', title: 'Aglyn AI: the AI website builder', heading: '', anchor: '', text: 'Aglyn AI: the AI website builder\\nAglyn AI builds for your site. Describe what you want — a page, a reusable component, a layout, a page template, a form, an email, a campaign, product copy, a theme, your search titles and descriptions — and it plans the work, builds it from what your site already has, and hands you a draft to review.\\nRolling out Aglyn AI is a release-flagged feature, currently being rolled out — the doors described here do not all appear in every workspace yet. This section says what each one does, and grows as each reaches you.\\nIt is the same assistant as Aglyn Assist, which answers questions about the product from this documentation. Aglyn AI is what that assistant does when it stops explaining and starts building.' },\n { path: '/ai/overview', title: 'Aglyn AI: the AI website builder', heading: 'The one rule worth knowing first', anchor: '#drafts-only', text: 'Every generation writes a draft. Nothing goes live until a person publishes it.\\nThat is not a setting and there is nothing to switch on. A build job writes a new draft, or a new unpublished version of something that already exists. It never flips a published version, never registers an address on your live site, never changes your navigation and never sends anything. A generated page has no live URL until you open its draft and publish it through the same button you always use; a generated email sits unsent; a generated theme waits in the theme editor as unsaved changes. A drafted automation arrives switched off.\\nBecause of that, a job is safe to run and safe to abandon. The worst a bad answer costs you is the credits it spent and a draft you delete.' },\n { path: '/ai/overview', title: 'Aglyn AI: the AI website builder', heading: 'What it can build', anchor: '#what-it-can-build', text: 'Each capability has its own page, next to the thing it builds:\\n| You want | Where you start | The page |\\n| A page from a brief | Screens → Describe it | Generate a page |\\n| A whole small site | Screens, or Sites for several at once | Generate a site |\\n| Many client sites, run as an agency | Sites | An AI website builder for agencies |\\n| A layout — header, navigation, footer | Layouts → Describe it | Generate a layout |\\n| A page template | Templates → Describe it | Generate a page template |\\n| A reusable component | Components → Describe it | Generate a component |\\n| A form | Forms → Describe it | Generate a form |\\n| A section on the canvas | The Besigner | Generate a section |\\n| Copy, rewritten or fresh | Any text in the Besigner | Rewrite and write copy |\\n| A change to your theme | Setup → Theme | Change your theme with AI |\\n| Search titles and descriptions | SEO | SEO by AI |\\n| Product copy, catalog and discount ideas | Commerce | Products with AI |\\n| Help working the CRM | A record, the composer, an import | CRM by AI |\\n| A designed email | Email → Describe it | Generate an email |\\n| A campaign | Campaigns | Generate an email |\\n| Variants for an A/B test, and its result in words | Marketing → Experiments | A/B tests by AI |\\n| What your analytics mean | Analytics → Insights | Insights |\\n| A change to the page you have open | The Assist panel, in the Besigner | Edits in the Besigner |\\nEverything in that table follows the same building rules — reuse before creating, your theme\\'s colors and spacing, one reusable component for a repeat, images from your media library with alt text, and a measured size. They are written out on How Aglyn AI builds, and every plan and every generated document is checked against them before you see it.\\nNot available yet {#not-yet}\\n- Drafting ' },\n { path: '/ai/overview', title: 'Aglyn AI: the AI website builder', heading: 'The Aglyn AI add-on', anchor: '#the-add-on', text: 'Aglyn AI is an add-on, bought once for the workspace on any paid plan, not per seat and not per site. With it on, every member who has the permission to generate can use every door above, on every site in the workspace.\\n- It adds a band of AI credits to the band your plan already includes.\\n- Every paid plan already includes some credits, Starter included, so the add-on widens a band you have rather than opening your first one. On Starter it also opens the guided assist that Pro and up already include.\\n- Enterprise workspaces carry generative building in their agreement rather than buying the add-on.\\n- Free workspaces do not buy it. A Free workspace gets a small monthly allowance of credits instead, enough to try generating, and it stops at that allowance rather than billing you.\\nWhat the add-on costs, and the size of the band it adds, are on aglyn.com/pricing and in Billing before you confirm — see Add-ons.' },\n { path: '/ai/overview', title: 'Aglyn AI: the AI website builder', heading: 'Credits and caps', anchor: '#credits-and-caps', text: 'Everything AI does — an answer from the assistant, a rewrite, a generated section, a whole site — is metered in AI credits from one workspace pool. There is no second meter per feature.\\nWhat a request costs is not fixed: a rewrite of one headline is small, a site scaffold is large, and a job tells you its estimate before you confirm it. The shape of the limits is the part worth knowing:\\n- An included band each month, from your plan plus the add-on, reset monthly.\\n- Past the band, a paid workspace keeps working and the extra credits bill on the monthly invoice at your plan\\'s rate. A switch under Billing → Usage stops AI at the included band instead; it is off unless you choose it.\\n- A ceiling you set, so overage can never pass a figure you are comfortable with.\\n- Alerts on the way there, to the workspace\\'s owners and admins.\\n- Allotments, which give one member, one site collaborator or one whole site its own monthly share of the pool, hard or soft — see AI allotments, usage and model choice. An agency can give each client site its own share so one site running out never touches another.\\n- A short-term rate limit per person, which smooths bursts rather than capping your month. If you see \"Too many AI requests\", wait a moment and try again.\\nA request that fails before the model answers does not spend credits. An answer that was generated and then refused for breaking a building rule does — the rules page says which, and why.\\nThe figures behind all of that are on Billing & Plans, and your own usage is on Billing → Usage.' },\n { path: '/ai/overview', title: 'Aglyn AI: the AI website builder', heading: 'Who can use it', anchor: '#who-can-use-it', text: 'Aglyn AI is a permission as well as a plan feature. Organization members hold it through the Use AI assistance and Generate with AI keys on their role (owners, admins and editors by default; viewers not), and site collaborators through the Assist and Generate boxes on the site\\'s Users card. A member whose role lacks the key is refused with a message naming it — see Custom roles & permissions.\\nNobody can raise their own allotment.' },\n { path: '/ai/overview', title: 'Aglyn AI: the AI website builder', heading: 'Switch AI off for one site', anchor: '#switch-ai-off-for-one-site', text: 'AI is on for every site in a workspace. A site admin can switch it off for one site on that site\\'s Admin → Plugins → AI page, and the page says, beside the switch, what switching it off stops and what it leaves running.\\nWith AI off for a site:\\n- the assistant, Describe it on the Screens, Templates, Layouts, Forms and Components pages, the SEO and theme cards, the editor\\'s Rewrite with AI, Generate a section with AI and Make a reusable component with AI controls, and the AI columns on the site\\'s collaborators card are gone from that site;\\n- every AI request made for the site is refused with AI is switched off for this site., including one sent from a tab that was open before the switch;\\n- an AI job already queued for the site stops with the same sentence, and spends no credits. A job that stopped does not restart when AI is switched back on.\\nSwitching AI off for a site does not stop the workspace\\'s AI add-on, credits, allotments or overage billing. AI keeps working on the workspace\\'s other sites and on workspace pages such as Billing, and an agency batch still builds the sites in it that have AI on.' },\n { path: '/ai/overview', title: 'Aglyn AI: the AI website builder', heading: 'What happens to what you send', anchor: '#what-is-sent', text: 'A build job is sent your brief and what it needs to build against it — the names of the components, layouts, forms and datasets your site already has, your theme\\'s values, and the copy of anything it is starting from. Each capability\\'s page says exactly what its own job is sent, and the CRM and automation pages say what is removed first.\\nYour brief is customer text and is kept with the job for 180 days, then deleted. No contact, lead, deal, form submission or list member is read to build a page.' },\n { path: '/ai/overview', title: 'Aglyn AI: the AI website builder', heading: 'Related', anchor: '#related', text: '- How Aglyn AI builds\\n- Aglyn Assist\\n- AI allotments, usage and model choice\\n- Add-ons\\n- The Besigner' },\n { path: '/ai/products-with-ai', title: 'Product copy and catalogs with AI', heading: '', anchor: '', text: 'Product copy and catalogs with AI\\nAglyn AI can write the copy for your products and help you set up a store. Everything it does is a proposal: you review it, and nothing is saved to your catalog until you apply it. It never sets a price, and it never publishes a product.\\nRolling out Aglyn AI is a release-flagged feature, currently being rolled out — it is not available in every workspace yet. This page says what it does for a catalog, and grows with the feature.' },\n { path: '/ai/products-with-ai', title: 'Product copy and catalogs with AI', heading: 'Write a product\\'s copy', anchor: '#write-a-products-copy', text: 'In the product editor, under the description, tags and categories, Write with AI writes a proposal from what the product already says and shows:\\n1. Give the product a name. Its current text, tags, options and first photo help, but none of them is required.\\n2. Press Write copy.\\n3. Review the proposal: a description, a search title and search description with their lengths, tags, the categories it fits from the ones your store has, and clearer option names where yours are unclear.\\n4. Press Put in the fields, check the fields, and press Save product.\\nRenaming an option this way keeps every variant with its price, SKU and stock.\\nThe copy is written only from your product. Where a shopper would need a fact the product does not give, such as a material or a size, the description marks it in square brackets, like [material], and the proposal lists what to fill in before you save.' },\n { path: '/ai/products-with-ai', title: 'Product copy and catalogs with AI', heading: 'Write copy for many products', anchor: '#write-copy-for-many-products', text: 'On the products page, Build your catalog with AI has Write copy for products. Pick up to 50 products and press Write copy. The copy is written one product at a time, and each row appears in the review table as it is ready.\\n- Apply saves one product\\'s copy. Apply to all saves every row still waiting.\\n- Applying changes only the description, search listing, tags, categories and option names. A price, stock count or anything else changed since the table loaded is kept.\\n- A product that was deleted, or whose copy could not be held to the rules below, is marked Skipped and the rest carry on.\\n- If your AI credits run out partway, the job waits and carries on from the product where it stopped once credits are available.\\nWhen you import products\\nIn the Import products (CSV) dialog, tick Write descriptions, search listings and tags with AI as they land. Once the import has created your products, their copy is written as above and waits in the review table on the products page. Copy is written for the first 50 products of a larger import.' },\n { path: '/ai/products-with-ai', title: 'Product copy and catalogs with AI', heading: 'Propose a first catalog', anchor: '#propose-a-first-catalog', text: 'Propose products takes a brief, such as \"a candle studio: hand-poured soy candles in 8 oz and 16 oz jars, and wax melts\", and proposes up to twelve products, usually six or more unless your brief says how many: each with a name, type, description, tags, options, a search listing and a sentence saying what its photo should show.\\nReview the table, untick any you do not want, and press Create drafts. Each product is created as a draft:\\n- Its price is left empty, and the table marks it Set a price. The product editor will not save the product until every variant has a price, and a product with an empty price is never sold.\\n- It has no photo, and the table marks it Needs a photo with the suggested shot. Nothing is uploaded or linked for you.\\n- Nothing is on your storefront until you price it and set it to Active.\\nA proposed product with the same name as one already in your catalog is left out.' },\n { path: '/ai/products-with-ai', title: 'Product copy and catalogs with AI', heading: 'Propose categories and discounts', anchor: '#propose-categories-and-discounts', text: 'Propose categories and discounts takes a brief and proposes store categories and up to five discounts, each with a sentence saying why it suits your store. A discount is a percentage off, an amount off or free shipping, with an optional minimum order and a code or no code.\\nUntick what you do not want and press Create selected. Categories appear in your catalog straight away. Discounts are created switched off: a shopper sees none until you switch it on under Promotions. A category or discount your store already has is left as it is.' },\n { path: '/ai/products-with-ai', title: 'Product copy and catalogs with AI', heading: 'What the copy never says', anchor: '#what-the-copy-never-says', text: 'Every proposal is checked before you see it, and a proposal that breaks a rule is written again or not shown:\\n- No health claims, such as a product that cures, treats or prevents a condition, or is clinically proven or doctor recommended.\\n- No financial or legal promises, such as guaranteed returns, income, or legal compliance.\\n- No certification, award or endorsement your own product text does not state. If your text says certified organic, the copy may say so too.\\n- No prices you did not give, and no links, emoji or formatting marks.\\nThe rules help, but you are still the publisher: read the copy before you apply it.' },\n { path: '/ai/products-with-ai', title: 'Product copy and catalogs with AI', heading: 'What is sent to the AI provider', anchor: '#what-is-sent-to-the-ai-provider', text: 'To write a product\\'s copy, the product\\'s name, type, current text, tags, options and current search listing are sent, with your store\\'s name and the names of your store\\'s categories. For a proposal from a brief, your brief and your store\\'s name are sent, and for categories, the names of the categories you already have.\\nA product\\'s first photo is sent only when it is in your site\\'s or organization\\'s media library, and only as a smaller copy: at most 768 pixels on its longer side, saved fresh as a JPEG, without the original file\\'s details such as the camera or the location it was taken. Nothing else from your media library is sent: no other photo, file name, folder or description. A photo that is a link to another website is not sent at all. No price, stock count, order or customer is ever sent.' },\n { path: '/ai/products-with-ai', title: 'Product copy and catalogs with AI', heading: 'Who can use it', anchor: '#who-can-use-it', text: 'Products with AI needs the Generate with AI permission, a plan that includes commerce, and Commerce switched on for the site. Each request uses AI credits. See who can use AI assist.' },\n { path: '/ai/products-with-ai', title: 'Product copy and catalogs with AI', heading: 'Related', anchor: '#related', text: '- Product catalog\\n- SEO by AI\\n- How Aglyn AI builds' },\n { path: '/ai/theme-assist', title: 'Change your site\\'s theme with AI', heading: '', anchor: '', text: 'Change your site\\'s theme with AI\\nRolling out The Theme assistant is a release-flagged feature, currently being rolled out — it is not available in your workspace yet. This page says what it does, and grows with the feature.\\nThe Theme assistant sits on your site\\'s Setup → Theme section, above the theme editor. Describe what you want and it proposes values for the editor\\'s own controls — the same colors, font, corner radius, spacing, navigation heights, dark scheme and component overrides you can set by hand. Nothing changes on your site until you save.' },\n { path: '/ai/theme-assist', title: 'Change your site\\'s theme with AI', heading: 'Change what you describe, or design a new theme', anchor: '#change-what-you-describe-or-design-a-new-theme', text: '- Change what I describe makes a targeted change. \"Make it feel warmer\" proposes new colors and leaves your font, corners and spacing alone; \"bigger headings on mobile\" changes heading sizes on phones and nothing else. Anything the proposal does not name keeps its current value.\\n- Design a new theme proposes a complete look — accents, backgrounds, text and their dark values — from a brief like \"warm and editorial, deep green primary, serif headings\".' },\n { path: '/ai/theme-assist', title: 'Change your site\\'s theme with AI', heading: 'Match your brand', anchor: '#match-your-brand', text: '- A hex color in your description, such as #1a73e8, is used exactly as written.\\n- The assistant reads the colors of your site logo when the logo comes from your media library.\\n- Paste a link to a public page and the assistant borrows the colors that page uses.\\n- In a white-label workspace, the workspace\\'s brand color is offered as a starting point.' },\n { path: '/ai/theme-assist', title: 'Change your site\\'s theme with AI', heading: 'Review the proposal', anchor: '#review-the-proposal', text: 'Each proposal shows:\\n- What changes — every control it sets, with its current value and the proposed one.\\n- Before and after — the editor\\'s own preview, in light and in dark.\\n- Adjusted for readability — body text needs 4.5:1 against the page and the paper, and the primary color 3:1 against the page. A pair the proposal would take below that is moved to the nearest shade that clears it, and the change is named.\\n- Dark values — visitors get your dark scheme unless you switched it off, so an accent changed for light also gets a dark value that keeps its hue.\\nIf your theme has changed since a proposal was made, the preview applies the proposal to the theme as it is now, and says so.' },\n { path: '/ai/theme-assist', title: 'Change your site\\'s theme with AI', heading: 'Save it, or don\\'t', anchor: '#save-it-or-dont', text: 'Put in the editor places the proposal in the theme editor as unsaved changes. Review or adjust them there, then Save — or Discard changes to go back. The save is stored the way any edit to your theme is:\\n- An installed theme keeps the publisher\\'s version and stores your changes on top of it, so it can still take updates.\\n- The default theme stores only the values you save.\\n- Your own theme changes in place.\\nYour site\\'s recent proposals stay listed on the card, so one that finished while you were elsewhere is still there to preview.' },\n { path: '/ai/theme-assist', title: 'Change your site\\'s theme with AI', heading: 'Who can use it', anchor: '#who-can-use-it', text: 'The assistant needs AI generation on your plan and the Generate with AI permission — see Custom roles & permissions. Each proposal draws on your workspace\\'s AI credits like any other AI request.' },\n { path: '/ai/theme-assist', title: 'Change your site\\'s theme with AI', heading: 'Related', anchor: '#related', text: '- Edit your theme\\n- AI Assist' },\n { path: '/building-sites/besigner/animations', title: 'Element animations', heading: '', anchor: '', text: 'Element animations\\nAny element on a screen can animate. Pick a preset, choose when it plays, and adjust a few numbers — there is no CSS to write.' },\n { path: '/building-sites/besigner/animations', title: 'Element animations', heading: 'Add an animation', anchor: '#add-an-animation', text: '1. Select the element on the canvas.\\n2. Open the Attributes tab in the right-hand panel.\\n3. Scroll to Animation and pick a preset.\\nThe rest of the animation fields appear once a preset is chosen.\\nPresets\\n| Preset | Entrance | On hover |\\n| Fade in | Fades up from invisible | Dims slightly |\\n| Slide up | Rises into place while fading in | Lifts |\\n| Slide down | Drops into place while fading in | Sinks |\\n| Slide left | Enters from the right | Nudges left |\\n| Slide right | Enters from the left | Nudges right |\\n| Zoom in | Grows from slightly small | Grows |\\n| Zoom out | Shrinks from slightly large | Shrinks |\\nPlays\\n- On scroll into view (default) — the animation runs the first time the element reaches the viewport. This is the right choice for anything below the top of the page.\\n- On page load — runs immediately. Best for a hero or a headline that is visible the moment the page opens.\\n- On hover — the element eases to its emphasis state while the pointer is over it, and eases back when the pointer leaves.\\nDuration and delay\\nBoth are in milliseconds. Duration is how long the motion takes; 600 is a natural default and anything over 3000 is capped. Delay is how long to wait before starting.\\nEasing\\nEasing is the shape of the motion — whether it starts fast and settles, or moves at one steady speed. It changes how an animation feels far more than its duration does.\\n| Easing | Feels like |\\n| Smooth (default) | Starts quickly and settles gently. The safe choice. |\\n| Steady | One constant speed from start to finish. Mechanical, good for subtle moves. |\\n| Gentle start | Eases in slowly, then arrives quickly. |\\n| Gentle end | Starts quickly, then slows into place. |\\n| Gentle start and end | Slow at both ends, quickest in the middle. |\\n| Settles into place | Travels a little past its ' },\n { path: '/building-sites/besigner/animations', title: 'Element animations', heading: 'Animations do not play on the canvas', anchor: '#animations-do-not-play-on-the-canvas', text: 'The besigner shows every element in its finished, un-animated state so you can always see and select it. Use Preview or the published site to watch an animation run.' },\n { path: '/building-sites/besigner/animations', title: 'Element animations', heading: 'Accessibility', anchor: '#accessibility', text: 'Some people get motion sickness, migraines or vertigo from movement on screen, and both macOS and Windows have a system setting to ask for less of it.\\nAglyn honors that setting. A visitor who has turned on Reduce motion sees your page with every element in its final position, fully visible, with no movement at all — including elements set to animate on scroll. You do not have to do anything, and there is no way to override it.\\nThis is also why the presets are what they are. Every one of them only fades, slides a short distance or changes size slightly; none of them spin or parallax, and the most emphatic thing on offer is the Settles into place easing — which travels a little past its resting place before coming back to it, and which you have to choose deliberately.' },\n { path: '/building-sites/besigner/animations', title: 'Element animations', heading: 'Speed and layout', anchor: '#speed-and-layout', text: 'Animations are pure CSS. A published page loads no animation library, and a page with no animations on it ships nothing extra at all — no stylesheet, no script. A page that uses the scroll trigger adds a few hundred bytes of inline code to watch for elements entering the viewport.\\nStaggering a row costs nothing extra. The page watches the row, not each card in it, so twelve cards arriving one after another is one thing to keep track of rather than twelve — the spacing between them is plain CSS.\\nThe presets only ever change an element\\'s opacity and its position on the screen, never the space it takes up. Nothing on the page moves to make room for an animation, so animations cannot cause the layout shift that search engines penalise.' },\n { path: '/building-sites/besigner/animations', title: 'Element animations', heading: 'If a visitor has JavaScript turned off', anchor: '#if-a-visitor-has-javascript-turned-off', text: 'Everything stays visible. Scroll-triggered elements simply appear in their final state rather than animating in, and load and hover animations still work normally. Search engines see the full text of the page either way.' },\n { path: '/building-sites/besigner/copy-paste', title: 'Copy & paste elements', heading: '', anchor: '', text: 'Copy & paste elements\\nDuplicate makes another copy right where you are. Copy and paste goes further: it carries an element, with everything inside it, to somewhere else entirely — another screen, a layout, a reusable component, an email template.' },\n { path: '/building-sites/besigner/copy-paste', title: 'Copy & paste elements', heading: 'Copy', anchor: '#copy', text: 'Select an element and either:\\n- press Cmd / Ctrl + C , or\\n- open the element\\'s ⋮ menu (on the canvas overlay or in the hierarchy) and choose Copy.\\nThe whole subtree comes along — a column you built copies with every link inside it.\\nWith several elements selected the menu reads Copy selection and takes them all, in document order. If your selection includes both a container and something already inside it, the container wins: you get one copy, not two.' },\n { path: '/building-sites/besigner/copy-paste', title: 'Copy & paste elements', heading: 'Paste', anchor: '#paste', text: 'Select where it should go and press Cmd / Ctrl + V , or choose Paste from the same ⋮ menu. The menu item names what\\'s waiting — Paste Stack, Paste 3 elements — and is grayed out when nothing has been copied.\\nWhere it lands follows the same rule as Add element:\\n- Select a container (a Stack, Section, Container) and the copy goes inside it, at the end.\\n- Select a leaf — a Screen Link, button, icon, image — and the copy goes beside it as the next sibling, because a leaf has no slot to render children in.\\nEverything pasted is selected afterwards, so you can retext or restyle it straight away. Paste is a single undo step.' },\n { path: '/building-sites/besigner/copy-paste', title: 'Copy & paste elements', heading: 'Between documents', anchor: '#between-documents', text: 'What you copy stays copied after you navigate. Open a different screen, layout, component or template and paste — the clipboard is still there. This is the fastest way to reuse a structure you have already built and refined.\\nTwo things to know:\\n- The copy is a snapshot, not a link. Editing the original later does not change what you pasted. When you want one source of truth across many places, promote it to a reusable component instead.\\n- Elements have to exist where you\\'re pasting. A block copied from an email template uses email elements, and pasting it into a site screen is refused with a message naming the element that isn\\'t available there. Nothing is half-pasted — the paste either lands completely or not at all.' },\n { path: '/building-sites/besigner/copy-paste', title: 'Copy & paste elements', heading: 'Copy & paste vs. Duplicate vs. reusable components', anchor: '#copy--paste-vs-duplicate-vs-reusable-components', text: '| You want | Use |\\n| -- | -- |\\n| Another one right here | Duplicate |\\n| The same structure somewhere else, edited separately from then on | Copy & paste |\\n| One thing that updates everywhere it appears | Reusable component |' },\n { path: '/building-sites/besigner/copy-paste', title: 'Copy & paste elements', heading: 'Shortcuts', anchor: '#shortcuts', text: '| Action | Shortcut |\\n| -- | -- |\\n| Copy selection | Cmd / Ctrl + C |\\n| Paste | Cmd / Ctrl + V |\\n| Select every element at the same depth | Cmd / Ctrl + A |\\nThese act on elements, not text. While you\\'re editing text — in the attributes panel, in a dialog, or directly on the canvas — the shortcuts do the ordinary thing and copy or paste the text instead.' },\n { path: '/building-sites/besigner/copy-paste', title: 'Copy & paste elements', heading: 'Related', anchor: '#related', text: '- Multi-select & multi-drag\\n- Reusable components\\n- The Besigner' },\n { path: '/building-sites/besigner/drag-drop-hierarchy', title: 'Drag-and-drop hierarchy', heading: '', anchor: '', text: 'Drag-and-drop hierarchy\\nEvery element on the canvas is a node in a tree. You rearrange that tree by dragging — and you add new elements by dragging them in from the drawer. This page covers all the ins and outs: where you can drag, how drop zones decide placement, why some elements accept children and others don\\'t, and what happens when a drop isn\\'t allowed.' },\n { path: '/building-sites/besigner/drag-drop-hierarchy', title: 'Drag-and-drop hierarchy', heading: 'Where you can drag', anchor: '#where-you-can-drag', text: 'Drag-and-drop works in two places, and they stay in sync:\\n- The hierarchy panel — drag a node up, down, or onto another node to reorder or reparent it. Good for precise moves and for deeply nested trees.\\n- The canvas — grab an element on the live page and drop it where you want. Good for visual, in-context placement.\\nA move made in one place is immediately reflected in the other.' },\n { path: '/building-sites/besigner/drag-drop-hierarchy', title: 'Drag-and-drop hierarchy', heading: 'What a drag does', anchor: '#what-a-drag-does', text: '- Reorder — drag a node between two siblings to change its position in the same parent.\\n- Reparent — drag a node onto a different container to move it into that container.\\n- Add — drag a component from the elements drawer onto the canvas to create a new element at the drop point.\\nEvery drag is a single undoable step — press Undo (⌘/Ctrl+Z) to put it back.' },\n { path: '/building-sites/besigner/drag-drop-hierarchy', title: 'Drag-and-drop hierarchy', heading: 'Drop zones: edges vs. center', anchor: '#drop-zones-edges-vs-center', text: 'As you drag over an element, the Besigner splits it into drop zones and shows a placement marker for the zone you\\'re pointing at. The zone decides where your element lands:\\n- Edges — the thin bands along an element\\'s top, bottom, left, and right. Dropping on an edge places your element as a sibling, before (top/left) or after (right/bottom) the element you\\'re over — in the same parent.\\n- Center — the large area in the middle. Dropping in the center places your element inside, as a child of that element.\\nThe placement marker is the source of truth. A line between elements means \"reorder/insert as a sibling here\"; a marker within an element means \"drop inside as a child.\" Always glance at the marker before you release.' },\n { path: '/building-sites/besigner/drag-drop-hierarchy', title: 'Drag-and-drop hierarchy', heading: 'Containers vs. leaf elements', anchor: '#containers-vs-leaf-elements', text: 'Whether the center zone means \"drop inside\" depends on the kind of element you\\'re over.\\nContainers accept children\\nContainers are built to hold other elements: Stack, Box, Section, Card, App Bar, Toolbar, and similar. Dropping onto a container\\'s center nests your element inside it. This is how you build structure — a Stack of buttons, a Section full of blocks, and so on.\\nLeaf elements don\\'t — dropping on one makes a sibling\\nLeaf elements have no slot for child elements. There are three kinds:\\n- Text elements render their content as inline text — a Screen Link, a Button, or a Text element. Their words are their content; there\\'s nowhere to nest a child.\\n- Self-closing elements draw themselves and take no children at all — an Image, an Icon, or a Video.\\n- Elements whose content is an attribute look like containers but draw only what their attributes say — a Markdown block (its document is the Content attribute), a List Item Text row (its words are Primary/Secondary), a Reusable Component instance (its content lives in the definition), and a Layout Slot (filled by the screen bound to the layout). Anything nested inside one of these has nowhere to appear on the published page.\\nDropping onto a leaf places your element as a sibling right after it, inside the leaf\\'s parent — never nested inside the leaf. Over a leaf you\\'ll only ever see a before/after placement marker, never an \"inside\" one, so the marker always matches where the element actually lands.\\nWhy this matters Aiming at the middle of a small element like a Screen Link used to tuck the new element inside it, where it wouldn\\'t render as expected. Now a leaf\\'s center reads as \"place next to me,\" which is almost always what you want — and you don\\'t have to aim precisely at its edges.\\nThe third kind is the one that used to' },\n { path: '/building-sites/besigner/drag-drop-hierarchy', title: 'Drag-and-drop hierarchy', heading: 'Adding a new element', anchor: '#adding-a-new-element', text: 'Insert → New Element (and the toolbar +) adds the component you pick relative to your current selection, following the same container-vs-leaf logic as a drop:\\n- Select a container — a stack, section, or the document itself — and the new element is added inside it.\\n- Select a leaf — anything without a child slot, such as a screen link, button, or icon — and the new element lands as its next sibling in the same container, rather than nested inside it.\\n- With nothing selected, the element is added at the end of the document.\\nYou can also drag straight from the elements drawer, which groups components into curated categories (Sections & Blocks first, then Layout, Navigation, Text, and so on). A dragged-in component lands using the same drop-zone and placement rules described above.' },\n { path: '/building-sites/besigner/drag-drop-hierarchy', title: 'Drag-and-drop hierarchy', heading: 'When a drop is rejected', anchor: '#when-a-drop-is-rejected', text: 'Not every element can go everywhere. Two kinds of guardrails apply:\\n- Placement (lineal) rules. Components can declare what they accept as children or require as a parent — a Toolbar belongs in an App Bar, a layout slot accepts only certain content, and so on. If a drop breaks a rule, the Besigner rejects it and tells you why, naming the specific rule, so you can adjust instead of guessing. The same rules run for Insert → New Element, so you can\\'t create an arrangement that drag-and-drop would refuse to make.\\n- You can\\'t move an element inside itself (or into one of its own descendants). That move is blocked with a clear message.\\nLayout-only components are additionally gated by the view type you\\'re editing (a screen vs. a shared layout).' },\n { path: '/building-sites/besigner/drag-drop-hierarchy', title: 'Drag-and-drop hierarchy', heading: 'Moving an element without dragging', anchor: '#moving-an-element-without-dragging', text: 'Dragging isn\\'t the only way to move an element between containers. Every element\\'s ⋮ menu — on the canvas overlay, and on the hierarchy row — carries four move actions that work by clicking:\\n- Shift up / Shift down — reorder the element among its siblings, without changing its container.\\n- Move out of container — lift the element out of the container it\\'s in, landing it directly after that container, one level up.\\n- Move into element above — tuck the element into the sibling directly above it.\\nUsed together, these reach anywhere in the tree: move out, shift until the element is sitting next to the container you want, then move into. This is the reliable route in a deeply nested tree, on a long page where the source and destination aren\\'t on screen at the same time, or any time a drag is fiddly.\\nEach action is a single undoable step, and each obeys exactly the same rules a drag does — including the leaf rule. An element whose content is an attribute (a Markdown block, a Reusable Component instance, a Layout Slot, a List Item Text) can\\'t hold other elements, so Move into element above is grayed out over one, and Move out of container refuses to drop an element into one. When an action is unavailable the Besigner says why rather than moving the element somewhere it would never render.' },\n { path: '/building-sites/besigner/drag-drop-hierarchy', title: 'Drag-and-drop hierarchy', heading: 'Multi-drag', anchor: '#multi-drag', text: 'Select several nodes first, then drag any one of them to move the whole selection at once. The group keeps its document order at the destination, and each node is still checked against the placement rules — anything that can\\'t legally move is skipped rather than blocking the rest. See Multi-select & multi-drag.' },\n { path: '/building-sites/besigner/drag-drop-hierarchy', title: 'Drag-and-drop hierarchy', heading: 'Tips', anchor: '#tips', text: '- Watch the placement marker — it\\'s the definitive preview of where the drop lands.\\n- Aim at an edge to place beside an element; aim at the center of a container to place inside it.\\n- Dropping on a leaf always makes a sibling — you don\\'t have to be precise about its edges.\\n- Undo any drag you didn\\'t mean.\\n- Use the hierarchy panel for precise moves in busy or deeply nested trees.' },\n { path: '/building-sites/besigner/drag-drop-hierarchy', title: 'Drag-and-drop hierarchy', heading: 'Related', anchor: '#related', text: '- Multi-select & multi-drag\\n- The Besigner overview\\n- Inline & rich text editing\\n- Screens & layouts' },\n { path: '/building-sites/besigner/edit-from-the-live-site', title: 'Edit from the live site', heading: '', anchor: '', text: 'Edit from the live site\\nBrowsing your published site and spot something to fix? If you can edit the site, the admin bar runs across the top of the live page. It names the screen serving that page and opens it in the besigner in one click.\\nPlan availability Every plan, Free included. The count of today\\'s views of the page you\\'re on is Pro+, like per-screen traffic.' },\n { path: '/building-sites/besigner/edit-from-the-live-site', title: 'Edit from the live site', heading: 'Who sees it', anchor: '#who-sees-it', text: 'The bar only appears for someone whose role on the site can edit content: Admin, Editor or Author. Workspace owners and admins are admins on every site. A Viewer doesn\\'t get a read-only bar; they get no bar at all.\\nNobody else gets the bar. A visitor who isn\\'t signed in, or who is signed in without edit access to this site, gets the page exactly as it is.' },\n { path: '/building-sites/besigner/edit-from-the-live-site', title: 'Edit from the live site', heading: 'On your aglyn.app address', anchor: '#on-your-aglynapp-address', text: 'On your site\\'s aglyn.app address, the bar appears by itself. Open any page of the site in the browser you use the console in, and the bar is there with nothing to click.\\nThe console makes that possible by leaving a hint for aglyn.app sites in your browser. While you\\'re signed in, it refreshes the hint at most once a day as the console loads, so you may notice a quick redirect through console.aglyn.app. The hint lasts a week. It isn\\'t a sign-in: each time the bar connects, the site\\'s server checks that your account still exists and can still edit this site.' },\n { path: '/building-sites/besigner/edit-from-the-live-site', title: 'Edit from the live site', heading: 'On your own domain', anchor: '#on-your-own-domain', text: 'A custom domain like www.example.com can\\'t see that hint, so there the bar waits until you ask for it. On any page of the site, either:\\n- add ?aglyn-edit to the address, as in https://www.example.com/about?aglyn-edit, or\\n- press Cmd/Ctrl + Shift + E.\\nVisit on the console\\'s Sites list opens your site with ?aglyn-edit already added.\\nAn Edit this site button appears in the bottom-right corner. Click it and a small console window opens. It checks your console sign-in and your role on this site, then connects the site; the window closes and the bar appears. If you aren\\'t signed in to the console, the window says so and links to sign in. Sign in, then click the button on your site again. An account that can\\'t edit the site sees No edit access instead.\\nThe connection lasts 30 minutes and is kept in your browser for that site, so other pages and tabs of the site show the bar without asking again. When it runs out, connect the same way again.\\n?aglyn-edit and the shortcut work on the aglyn.app address too. There they bring the bar straight up, with no button, whenever the hint is in your browser. Visit site on a site\\'s dashboard opens that address with ?aglyn-edit added.' },\n { path: '/building-sites/besigner/edit-from-the-live-site', title: 'Edit from the live site', heading: 'The bar', anchor: '#the-bar', text: 'From left to right:\\n- the site\\'s name, beside the Aglyn mark and the site\\'s favicon, if it has one. It opens the site\\'s dashboard in the console.\\n- the screen serving the page. A blog post or another collection page shows the collection and the template screen that renders it, such as Blog entry · Post. A page no screen serves shows Unrouted page.\\n- Draft changes, when the screen has a version newer than the one that\\'s live.\\n- Edit this page, which opens the besigner on that screen, at the version the page is serving.\\n- today\\'s page views across the site, and on Pro+ how many of them were of this page, as in 120 views today · 18 on this page. Days are counted in UTC. It opens the site\\'s analytics.\\n- Screens, plus Inbox and Orders where the inbox and commerce plugins are enabled on the site.\\n- your email, which opens a menu with Account settings, Site dashboard and Disconnect.\\n- ×.\\nEvery link opens the console in a new tab, where your usual permissions apply. As you browse, the bar follows: go to another page and it names that page\\'s screen.\\nThe bar sits above the page rather than over it. The page moves down by the bar\\'s height, and so does a site header pinned to the top of the window. On a phone, the bar keeps the site name, Edit this page and ×, and a ⋯ menu holds the links, your email and Disconnect.' },\n { path: '/building-sites/besigner/edit-from-the-live-site', title: 'Edit from the live site', heading: 'Hide it', anchor: '#hide-it', text: '- × hides the bar until the next full page load.\\n- Disconnect forgets the connection for this site in this browser, and the bar stops appearing by itself there. ?aglyn-edit or the shortcut brings it back.' },\n { path: '/building-sites/besigner/edit-from-the-live-site', title: 'Edit from the live site', heading: 'What it never does', anchor: '#what-it-never-does', text: '- Appear on its own for anyone who can\\'t edit. A visitor sees nothing unless they add ?aglyn-edit or press the shortcut themselves. Even then, all they get is the Edit this site button, and the console window answers it with Sign in first or No edit access.\\n- Change your site. The bar reads which screen serves the page and today\\'s counts, and links to the console. Every edit still happens in the console, with your normal permissions, and what visitors see is untouched.\\n- Give the site your console sign-in. The site gets a pass that works only on that site and expires in 30 minutes, plus your email to show in the bar.\\n- Set cookies on your own domain. The hint lives on aglyn.app, and only in the browsers of people who use the console.' },\n { path: '/building-sites/besigner/edit-from-the-live-site', title: 'Edit from the live site', heading: 'Good to know', anchor: '#good-to-know', text: '- Signing out of the console doesn\\'t remove the hint. It lapses within a week, and the site\\'s server re-checks your access every time the bar connects. On a shared computer, use Disconnect on each site you opened, or clear the browser\\'s cookies and site data for aglyn.app.\\n- Taking away someone\\'s edit access stops new connections straight away. A connection their browser already holds keeps showing the bar until its 30 minutes run out, and every link in it leads to the console, which refuses them.' },\n { path: '/building-sites/besigner/edit-from-the-live-site', title: 'Edit from the live site', heading: 'Related', anchor: '#related', text: '- The Besigner\\n- Versions & scheduled publishing\\n- Publish your first screen\\n- Teams, roles & membership' },\n { path: '/building-sites/besigner/element-catalog', title: 'Element catalog', heading: '', anchor: '', text: 'Element catalog\\nThese are the built-in elements in the ELEMENTS drawer, grouped by the categories the drawer uses. Every one of them ships with a preset — dropping it gives you an element that already has content in it, not an empty box you have to fill before you can see it.\\nElements from installed plugins appear under their own groups (Marketplace, Your components) and are not listed here.\\nAnything on this page can be styled from the Styles panel, targeted by interactions, and made responsive per breakpoint. The attributes below are the element-specific settings in the Attributes panel.' },\n { path: '/building-sites/besigner/element-catalog', title: 'Element catalog', heading: 'Finding the one you want', anchor: '#finding-an-element', text: 'You do not have to recognize an element by name. Two places offer them — the Elements tab beside Hierarchy, and the Choose element dialog — and both search and describe them the same way.\\nSearch {#element-search}\\nType in Search elements and the categories collapse into a single Best matches list. Searching looks at more than the name: an element\\'s description, its category and its keywords all count, so \"space\" finds Stack and \"sign in\" finds the member elements even though neither says so in its title.\\nMatches on the name always come first — an exact name, then a name starting with what you typed, then a name containing it — and only after those, elements matched on their description or keywords. So typing grid puts Grid at the top rather than burying it under everything whose description happens to mention a grid. Clearing the box brings the curated categories back.\\nWhat an element says about itself {#element-detail}\\nPoint at an element and a detail view explains it before you commit to it:\\n- A live preview — the element genuinely rendered, with its preset content and your site\\'s theme, so the colors and type are the ones you will actually get. It is a picture, not a playground: nothing in it is clickable.\\n- What it is for, in a sentence — written per element rather than generated, so it tells you when to reach for this one instead of the similar one next to it.\\n- What it will and won\\'t do — whether it holds other elements, which children it accepts, whether it must sit inside something else, whether its text is edited on the canvas, and which plugin provides it.\\n- Its attributes, and a Learn more link into this page at the right category.\\nSome elements have nothing to show on their own — a table row, a tab panel — and say so, naming the parent they belong inside rath' },\n { path: '/building-sites/besigner/element-catalog', title: 'Element catalog', heading: 'Layout', anchor: '#layout', text: '| Element | What it\\'s for |\\n| Box | The plain container. No styling of its own — you give it padding, background and borders from the Styles panel. Renders as div, span, p, figure, figcaption, blockquote or pre. For a landmark that needs an accessible name, use Section. |\\n| Section | Grouping inside a semantic HTML element (section, article, aside, nav, header, footer) with an accessible label, so the page keeps a meaningful document outline. main is not on the list — see The page\\'s main landmark. |\\n| Container | Centers content and caps its maximum width. |\\n| Stack | One-dimensional row or column with a gap. A natural frame for a repeat, though every element can repeat. |\\n| Grid | Responsive 12-column layout. See Grid below. |\\n| Layout slot | Where a bound layout injects the screen\\'s own content. Its Component is main unless you change it — see below. |\\nThe page\\'s main landmark\\nEvery published page carries exactly one main element: the landmark assistive tech uses to skip past the nav straight to the content, and the target a \"skip to content\" link needs. You don\\'t have to place it, and you can\\'t accidentally end up with two.\\nIt goes on the page\\'s content region, which the platform works out for you:\\n- On a screen framed by a shared layout, the layout\\'s Layout slot — everything between the chrome. The site nav and the site footer stay outside it, which is the whole point of the landmark.\\n- On a screen with no layout, the Document layer at the top of the hierarchy.\\nBoth of those have a Component attribute if you want to say otherwise. Set the Document layer to main and the slot steps aside; set the slot to section (for a layout whose slot genuinely isn\\'t the page\\'s main content) and the Document layer takes it instead. Choose something other than main in both places and' },\n { path: '/building-sites/besigner/element-catalog', title: 'Element catalog', heading: 'Surface', anchor: '#surface', text: '| Element | What it\\'s for |\\n| Paper | A themed surface. Elevation 0–24 raises it with a shadow; the Outlined variant swaps the shadow for a border (and hides the elevation control, which does nothing there). |\\n| Card | A surface for one subject, composed from Card Header (title + subheader), Card Content (the padded body) and Card Actions (the button row). Each piece is its own element, so you can select, style and reorder them. |\\n| Accordion | A header that expands to reveal its details, built from Accordion Summary and Accordion Details. See Accordion below. |\\nAccordion\\nStart expanded controls whether visitors arrive with the panel open. On the canvas the panel is always shown open regardless — collapsed details can\\'t be selected or styled. Preview and the published site both behave the way you configured it, so what ships is never in doubt.\\nAccordion Summary takes an optional Header links to. Leave it empty and the whole header row toggles the panel, which is what every accordion does by default. Point it at a screen and the row splits in two: the header text becomes a link to that screen, and the chevron beside it becomes the toggle. That split is the only way a header can do both — a link placed inside the toggle button is invalid markup and unreachable by keyboard. It is what lets a mobile drawer offer Product both as a group to open and as a page to visit.\\nThe FAQ preset drops three complete panels at once.' },\n { path: '/building-sites/besigner/element-catalog', title: 'Element catalog', heading: 'Navigation', anchor: '#navigation', text: '| Element | What it\\'s for |\\n| Screen Link | A link that targets a screen, a content collection\\'s listing page, one of its entries, or its RSS feed — by id, so it survives slug renames. Renders as a button or as a text link. See Linking to a listing, an entry, or a feed. |\\n| Link Container | A box that is itself one link, so a whole card is the click target. See Link Container below. |\\n| App bar / Toolbar Content | The site header frame. The app bar is the band; Toolbar Content is the row inside it that holds the brand, the links and the actions, and it may only be dropped into an app bar. |\\n| Nav menu / Mega menu | Dropdown and full-width navigation menus. |\\n| Drawer / Menu Button | A panel that slides in from the left, right, top or bottom. Open it with a Menu Button or an interaction. Width applies to left/right drawers; top and bottom sheets span the viewport, and the control is hidden for them. The Mobile Nav preset wires a hamburger, a drawer and a desktop link row in one insert. |\\n| Tabs / Tab Panel | A tab strip and its panels. See Tabs below. |\\n| Breadcrumbs | The trail showing where a page sits. Fill it with Screen Links so it survives renames, and leave the current page as plain Typography — linking a page to itself is the classic breadcrumb mistake. Set Collapse above to fold a long trail into an ellipsis. |\\n| Pagination | A page picker. See Pagination below. |\\n| Language switcher / Theme mode switcher | Locale and light/dark controls. The theme mode switcher is hidden on published pages while the theme\\'s Dark scheme is off. |\\n| Table of Contents | An \"On this page\" list built from the headings of a Markdown element on the same screen. |\\nWhere a link opens\\nScreen Link and Button both carry an Open link in dropdown once they point at a screen or a URL:\\n- Same ' },\n { path: '/building-sites/besigner/element-catalog', title: 'Element catalog', heading: 'Text', anchor: '#text', text: '| Element | What it\\'s for |\\n| Typography | One run of text — a headline, a label, a paragraph. The element you edit inline on the canvas. |\\n| Markdown | A whole document, held as markdown source in one element. For policies, terms and anything whose real source is a .md file. See Long documents in markdown. |\\n| Entry Body | A content entry\\'s markdown body, on an entry-template screen. |' },\n { path: '/building-sites/besigner/element-catalog', title: 'Element catalog', heading: 'Data Display', anchor: '#data-display', text: '| Element | What it\\'s for |\\n| List | A vertical list. Optional sticky Heading, plus Dense and Disable padding. |\\n| List Item | One row, with Divider, Align items, Dense, Disable gutters and Disable padding. |\\n| List Item Text | Primary and secondary text, plus Inset to line the text up with rows that have an icon or avatar. |\\n| Collection entries and friends | Content collections — entry lists, bodies, related posts, share bars. |\\n| Custom HTML | Sanitized markup you supply. |' },\n { path: '/building-sites/besigner/element-catalog', title: 'Element catalog', heading: 'Media', anchor: '#media', text: '| Element | What it\\'s for |\\n| Image | An image with fit, size and radius controls, an optional link, and an automatic responsive srcSet for media-library URLs. |\\n| Video | A video file from your media library, poster first. See Video. |\\n| Icon | Any icon from the icon picker. |\\n| Image List / Image List Item | A dense gallery. See Image List below. |\\nImage List\\nPick a Variant:\\n- Standard — a plain grid.\\n- Quilted — tiles can span several columns and rows. Set Column span / Row span on the tiles; only this variant reads them.\\n- Masonry — every image keeps its own aspect ratio. Row height is ignored here (and hidden), because a fixed height would crop exactly what masonry exists to preserve.\\n- Woven — alternating tile sizes.\\nEach tile holds an ordinary Image element, so galleries get the same responsive srcSet and lazy loading as an image anywhere else. Fill in Caption for a caption bar, or leave it blank for no bar at all.' },\n { path: '/building-sites/besigner/element-catalog', title: 'Element catalog', heading: 'Forms, Input, Commerce, Members', anchor: '#forms-input-commerce-members', text: 'Forms and fields, buttons and widgets, product grids and cart companions, and the member sign-in/sign-up elements. These are covered with the features they belong to — see Forms, Commerce and Member accounts.\\nOne element in this group belongs to neither:\\n| Element | What it\\'s for |\\n| Function Widget | Runs one of your no-code functions on the live site and shows what it returns — a quote calculator, a shipping estimate, a score. Function name is the function from the site\\'s Functions card; the widget draws an input per parameter — a number box, a switch or a list, under the label and starting value the function sets — a run button you can relabel, and the result under a Result label prefix. Results to show lists several named results instead of one, one per line as name | Label, and Update as the visitor types recomputes on every change, so there is no button. The function runs in the visitor\\'s browser: its definition, and the site variables it names, are part of the page, so keep anything private out of both. |\\n| Calculator | A container bound to one of your functions. Everything inside it reads that function: place Calculator Inputs and Calculator Results anywhere in it, between whatever stacks, grids and text the design calls for. Function name is the function from the site\\'s Functions card. The results are always current; there is no run button. |\\n| Calculator Input | Asks one parameter of the Calculator it sits in. Ask as picks the control: Automatic follows the parameter (a list when it has choices, a number box for a number, a switch for a true/false), and Quick picks draws buttons that set the value, so 10, 25, 50, 100 can sit beside a number box on the same parameter. Choices overrides the function\\'s own, as value: Label, value: Label. |\\n| Calculator Result | S' },\n { path: '/building-sites/besigner/element-catalog', title: 'Element catalog', heading: 'Related', anchor: '#related', text: '- The Besigner\\n- Video — posters, preload, captions and the lightbox\\n- Drag-and-drop hierarchy — which elements accept which children\\n- Long documents in markdown — the Markdown and Table of contents elements\\n- Responsive styling\\n- Interactions & custom HTML' },\n { path: '/building-sites/besigner/interactions-and-custom-html', title: 'Interactions & custom HTML', heading: '', anchor: '', text: 'Interactions & custom HTML' },\n { path: '/building-sites/besigner/interactions-and-custom-html', title: 'Interactions & custom HTML', heading: 'Fluent interactions', anchor: '#fluent-interactions', text: 'Select any element, open the Interactions tab in the right-hand panel — beside Attributes and Styles — and use Add interaction. Everything configures in one dialog without leaving the besigner:\\n- Trigger: when the element is clicked, hovered, the hover ends, or it scrolls into view.\\n- Frequency: every time (the default — repeatable, for menu and drawer toggles), once per page view, once per session, once per visitor, or with a cooldown.\\n- Actions (stack as many as you need). The basic actions below are pure in-page choreography — available on every plan (see Plan availability):\\n- Show / hide / show-hide an element — pick any other canvas element from a list; no selectors or classes needed. Use the target\\'s ⋮ → Start hidden until an interaction shows it to make it start hidden on the live site and on the canvas; selecting it opens it again while you design. (Not the eye on the Hierarchy row — that hides an element outright, and nothing reveals it.) Each of these takes an optional Delay (a later show/hide on the same target cancels a pending one — the classic hover grace period) and, for steps that can show, Close on Esc / Close on outside click so a revealed panel dismisses itself like a real menu.\\n- Open / close / open-close a menu — drives a Dropdown or Mega Menu element. The target defaults to the element itself when it is a menu; leave it empty to address the page\\'s first menu. A menu opened by When hovered closes itself when the pointer leaves the nav item and its panel.\\n- Open / close / open-close a drawer — drives a Drawer element; leave the target empty to address the page\\'s first drawer.\\n- Toggle / append / remove a class — targets this element by default, or any CSS selector. Pair with your theme\\'s utility classes for menus, reveals, and state changes.\\n- Scroll' },\n { path: '/building-sites/besigner/interactions-and-custom-html', title: 'Interactions & custom HTML', heading: 'Interactions belong to the page they are on', anchor: '#interactions-belong-to-the-page-they-are-on', text: 'An interaction is stored on the element, inside the screen, layout, component or template that holds it. That is what makes the obvious things true:\\n- it publishes and rolls back with the page — restoring an earlier version restores the interactions that version had;\\n- it travels with the element — duplicate a section, copy a component to another site, or package a template, and the choreography arrives with it, bound to the copy rather than the original;\\n- it is removed with the element — deleting a button takes its click behavior with it, leaving nothing pointing at something that is gone;\\n- it is unsaved work until you save, like any other edit, rather than going live the moment the dialog closes.\\nAutomation → Actions is a different list. Actions are things the site does — an order was placed, a form was submitted — so they are site-wide and live outside any one page. Interactions are things an element does, and they are edited only here, on the element.\\nInteractions you set up earlier Interactions created before this change are still listed on their element and still run. They are being moved onto their elements; nothing you need to do.' },\n { path: '/building-sites/besigner/interactions-and-custom-html', title: 'Interactions & custom HTML', heading: 'Plan availability', anchor: '#plan-availability', text: 'Interactions come in two tiers, and the field editor labels the steps that need a higher plan:\\n- Basic interactions — every plan, including Free. Opening and closing menus and drawers, showing and hiding elements, toggling classes, scrolling to an element, playing a video, sticky nav, navigation, and site alerts are pure client-side DOM behavior with no server cost. They run everywhere and are never metered — a hover-to-open menu is not a paid feature.\\n- The automations engine — Pro+, metered. Steps that reach the server or a data pipeline — overlays, analytics events, custom JS (Business), and the server steps (workflows, email, datasets, campaigns) — require the actions entitlement and count against your monthly action runs.\\nOn a plan without the automations entitlement, an interaction that mixes tiers still runs its basic steps live; the Pro+ steps are simply skipped until you upgrade. See workflows & actions for the full step catalog and metering.' },\n { path: '/building-sites/besigner/interactions-and-custom-html', title: 'Interactions & custom HTML', heading: 'Pick the target by clicking', anchor: '#pick-the-target-by-clicking', text: 'Any action that points at an element — open a menu, show / hide, open a drawer, scroll to element, play a video — lets you choose the target visually: click Pick element, click the element on the canvas, and confirm. No CSS selectors to write, and the picker resolves the stable data-aglyn id under the hood so the target survives edits. A custom selector field is there as a secondary option for advanced cases (a class or attribute selector you maintain yourself).\\nInteraction cookbook\\n- Hamburger menu: on any button, When clicked → Open/close a drawer. (The Menu Button element does this without any interaction at all.)\\n- Hover menu: on a Dropdown or Mega Menu element, When hovered → Open a menu at frequency every time. Clicking already toggles every menu with zero configuration; this adds the hover-open, and the menu closes itself when the pointer leaves.\\n- Hover reveal: When hovered → Show an element plus When hover ends → Hide an element, both at frequency every time.\\n- Mega menu from scratch (no Mega Menu element): wrap a trigger button and a panel in one Stack (wrapper: position relative; panel: ⋮ → Start hidden until an interaction shows it, position absolute below the trigger). On the wrapper: When hovered → Show the panel, When hover ends → Hide it with a small Delay (say 250ms) so the pointer can travel; add Close on Esc to the show step for keyboard dismissal. The Dropdown Panel preset (Navigation group) inserts exactly this pattern with both interactions pre-wired — see menus & navigation.\\n- Scroll reveal: on a section, When scrolled into view → Append class visible with a CSS transition, frequency once per visitor.\\n- Announcement click-through: When clicked → Track analytics event plus Go to a URL.\\n- \"Watch the demo\" button: When clicked → Scroll to element and' },\n { path: '/building-sites/besigner/interactions-and-custom-html', title: 'Interactions & custom HTML', heading: 'Scroll to element', anchor: '#scroll-to-element-step', text: 'When clicked → Scroll to element brings the element you pick to the top of the window — no id to invent, and it keeps working when the page around it changes.\\n| Option | What it does |\\n| Scroll | Smoothly (the default) or Instantly. A visitor whose device asks for reduced motion is always scrolled instantly. |\\n| Offset (px) | Room left above the element, 0–1000, so a sticky header does not cover it. |\\nKeyboard focus moves to the element too, so a keyboard or screen-reader visitor carries on from there. If the element is hidden or has been deleted, the step does nothing and the interaction\\'s other actions still run.\\nA #link is not a scroll A Button or Link whose External URL is only a fragment, such as #watch, goes nowhere on the published page, because the elements you place carry no id for it to find. The field warns while it holds one: clear it and use this action instead.' },\n { path: '/building-sites/besigner/interactions-and-custom-html', title: 'Interactions & custom HTML', heading: 'Play a video', anchor: '#play-video-step', text: 'When clicked → Play a video does exactly what pressing a Video element\\'s poster does, from a button anywhere on the page. Pick the Video as the target.\\n- With Open in a lightbox on, the lightbox opens and the film plays.\\n- With it off, the film plays in place of the poster.\\nNothing is loaded from the video\\'s host until the action runs, and a Wistia video follows the same consent rules as a press on its poster. To be sure the film is in view as it starts, put Scroll to element, picking the same Video, before it.' },\n { path: '/building-sites/besigner/interactions-and-custom-html', title: 'Interactions & custom HTML', heading: 'Track an analytics event', anchor: '#analytics-event-step', text: 'When clicked → Track an analytics event sends an event of your own naming to your site\\'s Google Analytics property, alongside the events Aglyn already sends. It is a Pro+ step, and it is the only way to measure something the built-in events do not already cover — a click on one particular button, a tab someone opened, a video they started.\\nThe step takes an event name and up to 10 parameters, each a name and a value. See Google Analytics events for everything your property receives without you doing anything.\\nNaming the event {#analytics-event-name}\\nType a readable name; Aglyn normalizes it into the shape GA4 accepts. CTA Click! is stored and sent as ctaclick — lowercased, anything that is not a letter, digit or underscore becomes an underscore, runs of underscores collapse, a trailing one is dropped, and the result is cut to 40 characters.\\nTwo names are refused outright rather than rewritten, with the reason shown as you save:\\n- A name GA4 reserves for itself, or one starting firebase, google or ga. GA4 would silently discard the event.\\n- A name Aglyn already uses, such as purchase or generatelead. Reusing one would mix your events into a report built on a different shape.\\nA name that does not start with a letter is refused too.\\nParameters {#analytics-event-parameters}\\n| Limit | Value |\\n| Parameters per event | 10 |\\n| Parameter name length | 40 characters — GA4 drops anything longer |\\n| Parameter value length | 100 characters — longer values are truncated |\\nEvery parameter needs both a name and a value; a row with either half blank is not saved.\\nParameters carry labels, never a visitor\\'s details A parameter must not carry anything about the person — no email address, name, phone number, postal address or IP. Those parameter names are refused, and so is any value that l' },\n { path: '/building-sites/besigner/interactions-and-custom-html', title: 'Interactions & custom HTML', heading: 'Custom HTML block', anchor: '#custom-html-block', text: 'The Custom HTML block accepts markup and CSS with a strict safety model:\\n- Sanitized by default — scripts, iframes, objects, forms, and inline event handlers (onclick etc.) are stripped on every render. What you paste can style and structure, never execute.\\n- Embed mode for third-party widgets: the raw snippet runs inside a sandboxed iframe with scripts allowed but no access to your site, cookies, or visitor data. Set the iframe height to fit the widget.\\n- A url(...) still reaches out, and that is on you. Sanitizing stops your CSS from executing; it does not stop it from fetching. Only https:, data: and blob: addresses are accepted — anything else, http: included, is rewritten to url(about:invalid), which loads nothing and leaves the rest of the rule intact. Which https host you name is your choice, and we do not proxy it: your visitor\\'s browser contacts that host directly, so it learns their IP address, user-agent and the page they are on. The same rule covers a style attribute inside your markup and the Styles panel\\'s Custom CSS. Hosts you reference this way are yours to name in your own privacy notice — see Trust & security.\\nPrefer first-party blocks where one exists — embeds can\\'t participate in theming or interactions.' },\n { path: '/building-sites/besigner/interactions-and-custom-html', title: 'Interactions & custom HTML', heading: 'Related', anchor: '#related', text: '- Walkthrough: build a mega menu with hover interactions\\n- Menus & navigation\\n- Besigner overview\\n- Workflows & actions' },\n { path: '/building-sites/besigner/live-co-editing', title: 'Live co-editing & unsaved work', heading: '', anchor: '', text: 'Live co-editing & unsaved work\\nThe besigner is safe to share. When teammates open the same document you see each other — avatars, cursors, and selections — and edits flow between you live. And if a tab crashes or closes with unsaved work, the besigner keeps a local draft and offers it back.\\nThis works in every besigner editor: screens, layouts, reusable components, templates, and designed emails.\\nPlan availability Free. Presence, co-editing, conflict protection, and draft recovery are on for every plan — how many teammates can edit is a question of collaborator seats, not of this feature.' },\n { path: '/building-sites/besigner/live-co-editing', title: 'Live co-editing & unsaved work', heading: 'Who\\'s here', anchor: '#whos-here', text: 'When someone else has the document open, their avatar appears in the besigner toolbar (photo or initial, on their assigned color, ringed in that same color). Hovering says what to expect of working alongside them: \"«Name» has this open too. Edits merge live, element by element, and either of you can save at any time — if you both change the same element, the last change wins.\"\\nUp to six avatars are shown side by side; beyond that the rest collapse into a\\n+N disc that says how many more are here. When you are the only one in the document, the toolbar shows nothing at all rather than an empty slot.\\nIf your own account has the document open somewhere else — another tab, or your account signed in on another machine — that window gets its own avatar too, drawn with a dashed ring and a small screen badge so you can tell at a glance it is yours. It is treated as a separate session in every respect: edits merge between your windows the same way, and either window can save. The conflict guard has never keyed on who wrote, so two windows of one account are protected exactly as two people are. Your current window never draws an avatar for itself.\\nOn the canvas, each collaborator shows up as:\\n- a cursor in their color, with their name on a small pill — shown while they are moving around, and dropped once they have an element selected, where the selection already carries their name;\\n- a selection outline around the element they have selected, with their name in a tab above it. Your own other tab is labeled \"You, in another tab\" rather than by name, and its outline is dashed.\\nA collaborator\\'s cursor is only broadcast while their pointer is genuinely over the canvas, so someone typing in a side panel stops trailing a cursor across your screen.\\nThe overlays never intercept your clicks.' },\n { path: '/building-sites/besigner/live-co-editing', title: 'Live co-editing & unsaved work', heading: 'One version, one room', anchor: '#per-version-rooms', text: 'Presence and live editing are scoped to one version of one document — not to the document as a whole.\\nThat matters in practice: if a teammate is editing version 3 of a screen and you open version 4, you will not see each other. No avatar, no cursor, no selection box, and none of their typing arrives on your canvas.\\nThis is not a gap — it is the honest answer. Live edits flow through a shared working copy that is itself per version, so two people on different versions were never going to reach each other. Showing an avatar would promise a connection that does not exist. If you mean to work together, make sure you are both on the same version.\\nTemplates are the exception, because templates do not have versions: everyone editing one template shares a single room.' },\n { path: '/building-sites/besigner/live-co-editing', title: 'Live co-editing & unsaved work', heading: 'Who\\'s in a document, before you open it', anchor: '#presence-in-lists', text: 'You don\\'t have to open a document to find out someone is already in it. Small avatars appear on the row itself in:\\n- the Screens list,\\n- the Layouts list,\\n- the Components card,\\n- the Templates card, and\\n- the Site emails card.\\nUp to three faces are shown per row, then +N. A row with nobody in it shows nothing, so a quiet list stays quiet.\\nTwo things are worth knowing about these row avatars, because they answer a slightly different question from the ones in the editor:\\n- They mean \"in this document\", not \"in this version.\" They are rolled up across every version, so a face on the row may be someone editing a version other than the one your Open button goes to. The tooltip says so.\\n- They refresh about every 30 seconds, and only while the tab is in front — this is a periodic check, not the live connection the editor has. Treat a row avatar as \"someone was here a moment ago\", and the editor\\'s toolbar as the live truth.\\nOne person shows as one face here however many tabs they have open, and you will see yourself on a row if you have that document open in another window.' },\n { path: '/building-sites/besigner/live-co-editing', title: 'Live co-editing & unsaved work', heading: 'Presence is not a lock', anchor: '#not-a-lock', text: 'Seeing nobody does not reserve the document, and seeing somebody does not shut you out. Nothing is ever locked — anyone with edit access can open anything at any time. What actually protects your work is the save guard described in When a save is refused, not the avatars.\\nSo an empty avatar row is a good sign, not a guarantee. If presence cannot reach the server the toolbar says so — an amber warning disc replaces the avatars, and clicking it explains what went wrong, what to do, and warns you explicitly that an empty stack is not proof you are alone.\\nOn a self-hosted Aglyn without a Realtime Database configured, presence and live co-editing are simply not available; the toolbar shows a neutral \"not set up\" badge rather than an error, and everything else on this page — the save guard and draft recovery — carries on working.' },\n { path: '/building-sites/besigner/live-co-editing', title: 'Live co-editing & unsaved work', heading: 'Editing together', anchor: '#editing-together', text: 'Edits are shared live, per element: change a heading while a teammate restyles the footer and you both keep your work. What lands in the document:\\n- Different elements — both sets of edits apply. This is the normal case, and it just works.\\n- The same element — last write wins. Co-editing shares changes; it does not merge two versions of one element.\\n- A teammate\\'s changes never enter your undo stack — undo rewinds only your own edits, including edits that came back to you from another of your own tabs.\\nOne rough edge in undo If you try to undo a change to an element a teammate has since edited themselves, the undo will step back without visibly changing anything — Aglyn will not roll back their work on your behalf. Press undo again to keep going back through your own edits. This is uncommon, and only happens on an element two of you touched.\\nAnyone with edit access to the site (a site admin or editor, or a workspace owner / admin / editor) can co-edit. Viewers see presence but their changes are refused at the door.\\nLive edits are working state, not saves — the document still saves when someone presses Save, and a successful save is what makes the shared state durable.' },\n { path: '/building-sites/besigner/live-co-editing', title: 'Live co-editing & unsaved work', heading: 'Saving together', anchor: '#saving-together', text: 'Everyone can save, in any order, as often as they like. One of you saves, the other carries on and saves a minute later — that is the normal rhythm and nothing interrupts it. It works because co-editing has already done the merging: your teammate\\'s changes reached your canvas element by element before they pressed Save, so what you write next contains their work rather than overwriting it.\\nThe besigner checks that rather than assuming it. Before letting a save through it confirms your document actually incorporates what is stored — every element the other save changed is on your canvas with the stored value, deletions included. Two people in step both pass, always.' },\n { path: '/building-sites/besigner/live-co-editing', title: 'Live co-editing & unsaved work', heading: 'When a save is refused', anchor: '#when-a-save-is-refused', text: 'Two situations do not pass that check, and both are real:\\n- Your session has fallen behind. A tab that lost its connection never received those elements, so it would write over work it has never seen.\\n- You have both changed the same element. That is the one case co-editing cannot merge, and letting it through is exactly how the last writer silently wins.\\nEither way the besigner tells you immediately — not twenty minutes later when you press Save:\\n> Someone else saved this screen while you were editing. Saving is paused so their\\n> work is not overwritten — reload to pick up their changes. Nothing you have done\\n> here is lost until you do.\\nThe banner has one action — Reload — and that\\'s deliberate: there is no \"save anyway\" that could silently destroy a teammate\\'s work. Your canvas keeps everything you\\'ve done until you reload, so the recovery is calm: note (or copy) what you need, reload to pick up their version, and reapply.\\nPressing Save while the banner is up answers with the same message rather than saving. The guard also runs server-side inside a transaction, so even a save racing the conflict by milliseconds is refused, never silently applied.\\nThe guard identifies the other writer as a different editing session, not as a different person — so it applies to a second window of your own account just as it does to a colleague.' },\n { path: '/building-sites/besigner/live-co-editing', title: 'Live co-editing & unsaved work', heading: 'Local draft recovery', anchor: '#local-draft-recovery', text: 'The besigner continuously keeps a local draft of your unsaved work in the browser — written about a second after you pause, and flushed when the tab is hidden or closed. If the tab crashes, the machine sleeps at the wrong moment, or you just close without saving, the next time you open that document you\\'re offered the work back:\\n> Unsaved changes to this screen from 3 minutes ago were recovered from this\\n> browser. Restoring puts them back on the canvas without saving; you can undo it.\\n- Restore puts the draft back on the canvas — unsaved and undoable, so you can inspect before committing to it.\\n- Discard deletes the draft from this browser and keeps the saved document. It changes nothing on the canvas and nothing for anyone else.\\n- Nothing is ever restored automatically; the besigner always asks.\\n- If someone saved while your draft was stranded, restoring it would roll their work back, so it is not offered — the banner says so and points you at Reload.\\nThe offer only appears when you are alone in the document. Recovery is for a crash: a lost connection, a browser quit, a tab closed on unsaved work. If anyone else has the document open — including another of your own windows — live co-editing has already put the unsaved work back on the canvas, so there is nothing to recover and no prompt is shown. Your draft is still kept; it simply is not offered over the top of work other people are in the middle of.\\nDrafts live in this browser only (they don\\'t follow you across machines), are kept for up to 7 days, and are deleted the moment a save succeeds. They\\'re a crash net, not version history — for named, durable snapshots use versions.' },\n { path: '/building-sites/besigner/live-co-editing', title: 'Live co-editing & unsaved work', heading: 'The Save button always answers', anchor: '#the-save-button-always-answers', text: 'The toolbar\\'s save control reads Save when there\\'s work to save and Up to date when there isn\\'t — including after a teammate saves work you already have, where the document is now stored exactly as your canvas holds it and there is nothing left to write — and clicking always produces an answer (saving, \"Already saved\", or the conflict warning), never silence.' },\n { path: '/building-sites/besigner/live-co-editing', title: 'Live co-editing & unsaved work', heading: 'Related', anchor: '#related', text: '- The Besigner\\n- Screens & layouts — versions\\n- Teams, roles & membership' },\n { path: '/building-sites/besigner/long-form-markdown', title: 'Long documents in markdown', heading: '', anchor: '', text: 'Long documents in markdown\\nSome pages aren\\'t designed — they\\'re written. A privacy policy, terms of service, an acceptable-use page or a staff handbook is a document, and its real source is usually a markdown file somewhere else: a repo, a Drive folder, whatever your lawyer sends back.\\nThe Markdown element puts that document on the canvas as one element, holding the source text. Updating the page is one paste. The Table of contents element reads that same text and builds the \"On this page\" list from its headings, so the list can never fall out of step with the document above it.\\nBuilding a page out of one Typography element per paragraph is the alternative, and it costs you the thing that matters most here: you can\\'t copy the page back out to compare it with the source file, and every heading you add means hand-editing the contents list too.' },\n { path: '/building-sites/besigner/long-form-markdown', title: 'Long documents in markdown', heading: 'The Markdown element', anchor: '#the-markdown-element', text: 'Drop Markdown from the Text group of the ELEMENTS drawer, then write or paste your document into the Content attribute.\\nContent is a visual editor, not a text box. Headings, bold, italic, links, lists, quotes, images, code blocks and tables all have toolbar buttons, and the shorthands below still work as you type — start a line with ## and it becomes a heading. Pasting from a word processor or a Google Doc keeps its formatting rather than arriving as one flat paragraph.\\nThe toolbar\\'s Markdown switch shows the raw source instead, for when you want to paste a whole file in one go or check exactly what\\'s stored. Both views edit the same text — the element stores markdown either way.\\nDouble-click the element on the canvas to get the same editor over the document itself, without going to the attributes panel — the same gesture that edits any other text element in place. What you type is kept as you type it, so Done, pressing Esc, and clicking another element all leave the document saved; use undo (⌘Z) to take an edit back.\\nText is set for reading rather than for a layout: 17px on a generous line height, in the theme\\'s primary text color, with headings that step up in size on wider screens. You don\\'t need to style anything for it to look right.\\nThe element draws its Content attribute and nothing else, so it takes no child elements — there\\'s no position in a parsed document an element could occupy. Dropping onto it places your element next to the Markdown block rather than inside it, the same way every other leaf element behaves. To put a picture in the document, write it as markdown — — and to put one beside the document, drop an Image as a sibling.\\nWhat the markdown supports\\n| You write | You get |\\n| ## A heading | A top-level heading — and an anchor a contents list can link' },\n { path: '/building-sites/besigner/long-form-markdown', title: 'Long documents in markdown', heading: 'The Table of contents element', anchor: '#the-table-of-contents-element', text: 'Drop Table of contents from the Navigation group. On a page with one Markdown element, that\\'s the whole setup — it finds it and lists its headings.\\n| Attribute | What it does |\\n| Markdown element | Which document to read. Leave it empty unless the screen has more than one. |\\n| Heading | The label above the list. Defaults to On this page; clear it for no label. |\\n| Levels | List ## and ### headings, or ## only. |\\nSub-headings are indented under their parents. Each entry is a real link, so it can be copied, opened in a new tab and followed by a crawler — clicking one scrolls the page to the matching heading.\\nHow it finds the markdown\\nBy walking the screen, in reading order — not by looking for anything in the page\\'s HTML. That\\'s what makes it work everywhere: on the canvas, in Preview and on the published site.\\n- Left empty, it takes the first document on the screen.\\n- Set to a specific element, it takes that one.\\n- Set to an element that\\'s since been deleted, it falls back to the first one. Deleting and re-adding a Markdown element is exactly what re-pasting a document looks like, and a published page with an empty aside is the worse outcome.\\nTwo elements count as a document: the Markdown element, and the Collection entry body on a blog article template. So an article template can carry an \"On this page\" aside beside the post, with no extra wiring — drop the Table of contents element on the screen and it reads the entry body.\\nWhile you\\'re authoring, the entry body still shows its {{entry.body}} token rather than a real post, so the contents list skips it and reads the next document on the screen instead. It fills in on Preview and on the published page, where the post is actually there.\\nPut the contents list wherever you like — most often in an aside column beside the do' },\n { path: '/building-sites/besigner/long-form-markdown', title: 'Long documents in markdown', heading: 'Heading links', anchor: '#heading-links', text: 'Every ## and ### heading gets an anchor id built from its own words — in the Markdown element, in a collection entry body, and in the console\\'s own markdown preview, all from the same rule:\\n- lower-cased, with accents folded to plain letters (Résumé → resume)\\n- anything that isn\\'t a letter or a digit becomes a hyphen, and runs of hyphens collapse\\n- leading and trailing hyphens are trimmed\\nSo ## Your Rights & Choices is #your-rights-choices, and ### Section 4.2 — Retention is #section-4-2-retention.\\nThe rule is deterministic: the same words always produce the same link. Re-pasting an updated document doesn\\'t break links people have already shared or bookmarked, as long as the heading still says the same thing.\\nIf a document repeats a heading, the second one gets -2, the third -3, and so on in document order — and the numbering skips anything already taken, so a document containing Notice twice and ## Notice 2 still ends up with three distinct links.\\nAnchors come from the document\\'s own words, so two documents on one screen with the same headings will produce the same ids twice. On a page like that, give the second document distinct headings — or split it onto its own screen, which is usually what it wanted anyway.' },\n { path: '/building-sites/besigner/long-form-markdown', title: 'Long documents in markdown', heading: 'Related', anchor: '#related', text: '- Element catalog\\n- Text editing — for headlines and short copy, where Typography is the right element\\n- Screens and layouts' },\n { path: '/building-sites/besigner/multi-select', title: 'Multi-select & multi-drag', heading: '', anchor: '', text: 'Multi-select & multi-drag\\nWorking element-by-element is slow. The Besigner lets you select multiple nodes — in the hierarchy or on the canvas — and act on them together.' },\n { path: '/building-sites/besigner/multi-select', title: 'Multi-select & multi-drag', heading: 'Select multiple', anchor: '#select-multiple', text: '- On the canvas, add elements to the selection as you click.\\n- In the hierarchy, select multiple nodes the same way.\\n- Selection spans both surfaces — what you pick in the hierarchy is reflected on the canvas and vice versa.' },\n { path: '/building-sites/besigner/multi-select', title: 'Multi-select & multi-drag', heading: 'Move the whole selection', anchor: '#move-the-whole-selection', text: 'Once several nodes are selected, dragging moves the entire selection at once, preserving their arrangement. This is the fast way to reposition a group without grouping them permanently.' },\n { path: '/building-sites/besigner/multi-select', title: 'Multi-select & multi-drag', heading: 'What the inspector shows', anchor: '#what-the-inspector-shows', text: 'The inspector\\'s header names one element, and with several selected that element is the last one you added to the selection — not the first, and not \"several\". So a header reading Hero · Section while four things are highlighted is telling you about the fourth click, and the panel\\'s Attributes and Styles tabs act on that element.\\nPick the element you actually want to edit last, or drop back to a single selection before reaching for the inspector. Dragging, by contrast, still moves everything selected. See the inspector.' },\n { path: '/building-sites/besigner/multi-select', title: 'Multi-select & multi-drag', heading: 'Tips', anchor: '#tips', text: '- Selection overlays render inside the viewport\\'s stacking context, so they highlight the right elements without disturbing page scroll.\\n- Combine with drag-and-drop reparenting to move a group into a new parent.' },\n { path: '/building-sites/besigner/multi-select', title: 'Multi-select & multi-drag', heading: 'Related', anchor: '#related', text: '- Drag-and-drop hierarchy\\n- The Besigner\\n- The inspector' },\n { path: '/building-sites/besigner/overview', title: 'The Besigner', heading: '', anchor: '', text: 'The Besigner\\nThe Besigner is Aglyn\\'s visual editor. You build a screen by placing components on a canvas, arranging them in a hierarchy, and editing content directly on the page. It renders your screen under the real site theme, so what you see matches what publishes.\\n1. Primary bar — the document switcher, File/Edit/Insert menus, the Publish/Unpublish button, the ƒx functions panel, the version you\\'re editing, and notifications.\\n2. Toolbar — add elements, undo/redo, scheme and device preview, panel toggles, and the Live/Preview/save state on the right.\\n3. Hierarchy & elements — the node tree of the screen and the drawer of components you can add.\\n4. Canvas — your screen rendering live under the real site theme; select, drag, and edit text inline.\\n5. Inspector — info, attributes, and styles for the selected element.' },\n { path: '/building-sites/besigner/overview', title: 'The Besigner', heading: 'Preview vs. canvas', anchor: '#preview-vs-canvas', text: 'The canvas is an editing surface: hidden panels expand when you select them, so you can style what you can\\'t otherwise reach, and nothing responds to hover the way a visitor would experience it.\\nPreview (in the toolbar) renders the same draft the way the published site will: author-hidden panels start closed, and hover and click interactions actually run — so a mega menu, dropdown, or popup behaves exactly as it will live.\\nTwo deliberate differences from the real site:\\n- Links don\\'t navigate. You stay on the screen you\\'re previewing.\\n- Server-side steps don\\'t fire — no analytics events, webhooks, or automations. A preview never writes data or spends quota.\\nUse Preview to check interaction behavior before publishing, and the canvas to edit the parts a visitor would never see open.\\nPlan availability Free. The Besigner is core to building; some components and actions it exposes are plan-gated (noted where relevant).' },\n { path: '/building-sites/besigner/overview', title: 'The Besigner', heading: 'What you can do', anchor: '#what-you-can-do', text: '- Drag-and-drop components from the drawer onto the canvas, and reparent them by dragging in the hierarchy or on the canvas. The drawer and the New Element picker group components into curated categories, with the Sections & Blocks library first — composed, ready-made sections ahead of the primitives. The groups, in order: Sections & Blocks, Layout (boxes, containers, stacks, grids, sections, layout slots), Navigation (app bar, toolbar, screen links, tabs, breadcrumbs, pagination, drawers, language switcher, social links), Text, Forms (forms, fields, search), Input (buttons, switches, widgets), Media (images, video, icons, image lists), Data Display (lists, custom HTML, feeds), Commerce (product grids, cart, checkout companions), Members (sign-in, sign-up, password recovery), Surface (paper, cards, accordions), then any plugin-provided groups such as Marketplace and Your components. Every built-in element is listed in the element catalog.\\n- Multi-select across the hierarchy and canvas, then move the whole selection at once.\\n- Edit text inline — double-click a text-capable element to type directly; opt-in elements support basic rich text.\\n- Bind a layout so the screen renders inside a shared header/footer frame.\\n- Preview color schemes with the artboard light/dark toggle, matching the live site\\'s system-driven scheme.' },\n { path: '/building-sites/besigner/overview', title: 'The Besigner', heading: 'The canvas', anchor: '#the-canvas', text: 'The canvas shows your screen composed with its theme and, if bound, its layout. Selection overlays highlight the active element without affecting page scroll.\\nWhen a drop isn\\'t allowed, the Besigner explains why — it surfaces the specific placement (lineal) rule that rejected the move, so you\\'re never guessing.' },\n { path: '/building-sites/besigner/overview', title: 'The Besigner', heading: 'Hierarchy panel', anchor: '#hierarchy-panel', text: 'Every element on the canvas is a node in a tree. The hierarchy panel lets you:\\n- Reorder and reparent nodes by dragging (with a placement marker showing the target slot).\\n- Select multiple nodes and act on them together.\\n- See which nodes are layout-only vs. screen content.\\n- Read how deeply a node is nested by counting the guide lines to the left of its name — one per level, the same weight at every level. The branch holding the current selection draws its guides in the accent color.\\n- Copy a node and its children and paste them elsewhere — including into a different screen, layout or component. See Copy & paste elements.' },\n { path: '/building-sites/besigner/overview', title: 'The Besigner', heading: 'The inspector', anchor: '#the-inspector', text: 'The right-hand panel has three tabs — Attributes, Styles and Info — and one header above all three naming what they act on, with a SELECTED chip opposite. It reads like Hero · Section: the element\\'s name as the hierarchy shows it, then the component it actually is.\\nRead it before you edit. On a canvas, clicking a child moves the selection under you — click a heading inside a hero and the panel silently switches from the section to the heading, with three tabs of controls that look identical either way. The header is what tells you which of the two your next change lands on.\\nThe second half is dropped when it would only repeat the first, so a plain box reads Box, not Box · Box. Because the header is shared, Attributes, Styles and Info cannot disagree about what is selected. With nothing selected the panel shows its empty state instead.' },\n { path: '/building-sites/besigner/overview', title: 'The Besigner', heading: 'Inline and rich text', anchor: '#inline-and-rich-text', text: '- Double-click any text-capable component to edit its text on the canvas.\\n- Components that opt in support basic rich text (bold, links, and similar).\\n- You can also set a component\\'s text from the Text attribute field in the inspector.' },\n { path: '/building-sites/besigner/overview', title: 'The Besigner', heading: 'Reusable components', anchor: '#reusable-components', text: 'Promote any subtree into a reusable component, then insert instances of it across screens. Editing the source updates every instance at render time. You can rename, demote, or delete reusable components from the site dashboard. See Reusable components.' },\n { path: '/building-sites/besigner/overview', title: 'The Besigner', heading: 'Editing together', anchor: '#editing-together', text: 'Two people can open the same document and edit it live: you see each other\\'s avatars in the toolbar, cursors and selections on the canvas, and element-level changes as they happen. Saves are protected against overwriting each other, and unsaved work survives a crash as a local draft. See Live co-editing & unsaved work.' },\n { path: '/building-sites/besigner/overview', title: 'The Besigner', heading: 'AI in the canvas', anchor: '#ai-in-the-canvas', text: '- AI copy assist rewrites or generates text for any canvas text prop.\\n- AI Generate Section produces a constrained subtree straight onto the canvas.\\nSee AI Assist.' },\n { path: '/building-sites/besigner/overview', title: 'The Besigner', heading: 'Related', anchor: '#related', text: '- Element catalog\\n- Copy & paste elements\\n- Screens & layouts\\n- Bindings & variables\\n- Section & block library' },\n { path: '/building-sites/besigner/repeat', title: 'Repeat over data', heading: '', anchor: '', text: 'Repeat over data\\nAny element on a screen can repeat. Point it at a dataset and it renders once per record on the published page, with each copy filled in from that record. A row of cards, a gallery, a team list, a price table — one design, as many copies as you have rows.' },\n { path: '/building-sites/besigner/repeat', title: 'Repeat over data', heading: 'Turn it on', anchor: '#turn-it-on', text: '1. Select the element on the canvas.\\n2. Open the Attributes tab in the right-hand panel.\\n3. Scroll to Repeat over dataset and pick a dataset.\\nThe rest of the repeat fields appear once a dataset is chosen. The canvas starts drawing the real copies straight away.' },\n { path: '/building-sites/besigner/repeat', title: 'Repeat over data', heading: 'What repeats: the element, or what\\'s inside it', anchor: '#what-repeats-the-element-or-whats-inside-it', text: 'Two things can sensibly repeat, and which one you want depends on the element:\\n- What is inside this element — the element stays as one frame and its contents are the item template. This is what you want for a Stack, a grid or a gallery: the element is the list, and each record fills it with one more item.\\n- This element — the element itself is copied, with everything inside it. This is what you want for a card, an image, a row or a placed component: the element is the item, and each record gets its own.\\nThe Repeat field chooses between them. It only appears on an element that has something inside it, because an element with nothing inside it has only one possible reading — it repeats itself. So a leaf element, an image, and a placed component need no choice: they are copied, one per record.\\nIf your copies come out nested inside one frame when you wanted them side by side, you picked the wrong scope. Switch Repeat to This element.' },\n { path: '/building-sites/besigner/repeat', title: 'Repeat over data', heading: 'Fill each copy in', anchor: '#fill-each-copy-in', text: 'Inside the repeating element, write {{item.field}} wherever a record\\'s value belongs — {{item.name}}, {{item.price}}, {{item.photo}}. Anything you can type into a field can hold one, and the Insert binding button lists the dataset\\'s fields for you.\\nA reference field goes one hop further: {{item.author.name}} follows the reference and reads the field on the record it points at. One hop only.\\nA token naming a field the dataset doesn\\'t have stays on the page as written, which is how you spot a typo rather than shipping a blank.' },\n { path: '/building-sites/besigner/repeat', title: 'Repeat over data', heading: 'See the copies while you design', anchor: '#see-the-copies-while-you-design', text: 'The canvas draws the copies from your real records, the same ones the published page will render, in the same order. A repeat badge in the corner of the element names what it repeats over and how many records it found.\\nThe first copy is the element you edit. Change a color, retype a heading, drag something in — every copy changes with it, because there is only ever one design. The copies beside it are pictures: they can\\'t be selected, dragged or edited, and nothing you do to them is saved, because there is nothing there to save.' },\n { path: '/building-sites/besigner/repeat', title: 'Repeat over data', heading: 'Bound what renders', anchor: '#bound-what-renders', text: 'Three optional fields decide which records appear, and they apply on the canvas exactly as they apply on the published page:\\n| Field | What it does | Example |\\n| Repeat filter | Keeps only matching records | price <= 20, tier == plus, tags contains red |\\n| Repeat sort | Orders them | price desc, name |\\n| Repeat limit | Caps how many render | 6 |\\nFilter operators are ==, !=, >, >=, <, <= and contains. A filter or sort that can\\'t be read is ignored rather than emptying the page.\\nThe hundred-record ceiling\\nA single repeat renders at most 100 records, whatever the limit says. It is a page, not a database export: a screen that tried to render ten thousand rows would be slower than any visitor would wait for. Records with an explicit order come first, in that order; the rest follow by creation.\\nFor a bigger collection, give it a screen per record instead of a hundred rows on one.' },\n { path: '/building-sites/besigner/repeat', title: 'Repeat over data', heading: 'What you can\\'t do', anchor: '#what-you-cant-do', text: '- Repeats don\\'t nest. A repeat inside another repeat\\'s template renders once inside each copy, using its own record — it does not multiply out.\\n- A copy is not a page. Copies share one design by definition. If one record needs to look different, that\\'s a condition on the design, not a second design.' },\n { path: '/building-sites/besigner/repeat', title: 'Repeat over data', heading: 'When a dataset goes away', anchor: '#when-a-dataset-goes-away', text: 'A repeat stores the dataset\\'s id, so renaming the dataset never breaks it.\\nIf the dataset is deleted, or stops being shared with this site, the element renders once, as you designed it — tokens and all. A published page never goes down because its data went away. See who a dataset is shared with.' },\n { path: '/building-sites/besigner/repeat', title: 'Repeat over data', heading: 'Related', anchor: '#related', text: '- Datasets overview\\n- Model builder\\n- Relations\\n- Reusable components\\n- Bindings' },\n { path: '/building-sites/besigner/responsive-styling', title: 'Responsive styling & custom CSS', heading: '', anchor: '', text: 'Responsive styling & custom CSS' },\n { path: '/building-sites/besigner/responsive-styling', title: 'Responsive styling & custom CSS', heading: 'Style per breakpoint', anchor: '#style-per-breakpoint', text: 'The artboard preview mode now doubles as your styling scope:\\n- Fluid Responsive (default) — style edits apply at every screen size.\\n- XS / SM / MD / LG / XL — edits in the styles panel apply from that breakpoint up, following the mobile-first cascade. Switch to SM – Tablet, change a padding, and phones keep the base value while tablets and up take the new one.\\nA chip at the top of the styles panel always shows the active scope (\"Styling: all screen sizes\" or \"Styling breakpoint: SM\"). Values you don\\'t touch keep inheriting — opening a panel at a breakpoint never pins anything by itself.\\nSelecting a device also re-renders the canvas at that width: responsive values ({ xs, md }), visibility bands, and breakpoint-driven component layouts all resolve as they will on a real device of that size — the published site is untouched by preview mode.' },\n { path: '/building-sites/besigner/responsive-styling', title: 'Responsive styling & custom CSS', heading: 'Switch one style off to compare', anchor: '#mute-a-style', text: 'Every field that has a value carries an eye in its top corner. Click it and that one declaration stops applying on the canvas — the value stays in the box, struck through and dimmed, and clicking again puts it straight back. It is the way to answer \"what does this section look like without the max width?\" without deleting a value you want to compare against.\\nThe same eye is on the rows under Classes & custom CSS: each declaration in the Builder list, and each class chip. A switched-off class stays on the element — the chip\\'s ✕ is still the only thing that removes one — so the chips are always the class list your site ships. The one exception is aglyn-hidden — the class behind ⋮ → Start hidden until an interaction shows it. Its chip\\'s eye shows the element on the canvas while you design it, because \"stop applying the hidden class here\" and \"show this element while I design it\" are one decision. Its ✕ still removes the class, which is the same as choosing Do not start hidden.\\nThe eye on the Hierarchy row is a different control: it hides the element outright, in the besigner and on the published site. It writes no class, and it never touches the element\\'s own styles — hide a Stack laid out with display: flex and you get that flex back when you show it again.\\nSwitching a style off is a canvas setting, like the device preview: it is never saved, it never reaches Preview or your published site, and reloading the editor brings every declaration back. It also follows the scopes below — switching off a color in the Hover state leaves the default color applying, and one switched off while styling SM is back on at all screen sizes.' },\n { path: '/building-sites/besigner/responsive-styling', title: 'Responsive styling & custom CSS', heading: 'Style hover, focus and other states', anchor: '#interaction-states', text: 'Under the breakpoint chip is a second row of chips: Default, Hover, Active, Focus and Disabled. They answer the same question the breakpoint chip does — which version of this element am I editing?\\nPick Hover and every field in the panel switches to that element\\'s hover styles. Change the background, and you have said \"this button is darker when you point at it\". Pick Default again to go back to the resting styles.\\n- Hover — while the pointer is over the element.\\n- Active — while it is being pressed or clicked.\\n- Focus — while it has keyboard focus (see the note below).\\n- Disabled — while a form control is disabled.\\nA chip with a • already has styles for that state. Selecting a state gives its chip an × that clears the whole state at once.\\nYou can see the state while you style it\\nYou cannot hover an element while working in a side panel, so picking a state also holds it on the canvas: the element renders as though you were hovering it (or pressing it, or focusing it) while you work, and a banner says so.\\nThis is a preview. It is not saved, it does not appear in Preview, and it never reaches your published page — going back to Default ends it, and so does selecting a different element.\\nFields you don\\'t touch keep inheriting\\nA state only overrides what you actually change. Set a hover background and the text color, padding and everything else keep coming from the default styles — which is exactly how the browser treats it.\\nThat means setting a value back to what the default says removes it from the state rather than pinning a duplicate, and clearing a field lets the state inherit again.\\nStates and breakpoints combine\\nThe two chips work together. Choose MD – Laptop and Hover and you are editing \"the hover style, on laptops and up\" — phones keep whatever the smaller breakpoi' },\n { path: '/building-sites/besigner/responsive-styling', title: 'Responsive styling & custom CSS', heading: 'Box stylers', anchor: '#box-stylers', text: 'The spacing styler is one diagram of the element\\'s box, drawn the way a browser actually builds it: margin on the outside, then the border, then padding, and the content in the middle. Each region is shaded and labeled, and every side shows the value it is currently set to.\\n- Pick the fan-out with Side / Axis / All: one side at a time, the vertical or horizontal pair together, or all four sides at once.\\n- Click any side of the diagram to open its editor. Clicking the same side again closes it.\\n- Every side takes either a spacing step from your theme or a custom amount with a unit you choose.\\n- The border ring is shown so the diagram matches what the browser draws. Border width, style and color are edited under Borders & Shadows, not here — there is one place for each.\\n- Everything respects the active breakpoint scope.\\nThe sides are named, not abbreviated {#spacing-side-names}\\nYou do not have to know that mt means margin-top. Each side announces itself in words — Space outside — top on the margin, Space inside — top on the padding — in its tooltip, in its heading when you open it, and to a screen reader. A side with nothing set on it simply reads Top, Right, Bottom or Left; once it has a value, the side shows the value instead (24px, 0px, auto), so the diagram doubles as the readout.\\nThe legend beneath the diagram says what each region does, in the terms that actually matter when you are deciding which one to reach for:\\n| Region | What it is |\\n| Margin | Space outside the element, pushing its neighbors away |\\n| Border | The line drawn around the element — set under Borders & Shadows |\\n| Padding | Space inside the element, between its border and its content |\\n| Contents | The element\\'s own text or children |\\nThe whole diagram is keyboard-reachable: Tab moves between the s' },\n { path: '/building-sites/besigner/responsive-styling', title: 'Responsive styling & custom CSS', heading: 'Spacing steps & units', anchor: '#spacing-units', text: 'Space can be set two ways, and the difference matters more than it looks.\\nSpacing steps (recommended) {#spacing-steps}\\nA step is a rung on your site theme\\'s own spacing ladder. The styler shows what each rung comes to on your theme today, but it stores the step, not the pixels.\\n| Step | On a default theme |\\n| None | 0px |\\n| Hairline | 4px |\\n| Extra small | 8px |\\n| Small | 16px |\\n| Medium | 24px |\\n| Large | 32px |\\n| Extra large | 48px |\\n| Huge | 64px |\\n| Giant | 96px |\\nThose pixel figures are what a default theme resolves to, and they are shown beside each rung in the menu so you never have to guess. A site whose theme uses a different spacing unit gets a different column of numbers against the same names — which is the point.\\nThat is the whole point. If you later retune your theme\\'s spacing, or a different theme is applied, everything set to Medium moves with it. An element set to a fixed 24px stays at 24px forever and slowly falls out of step with the rest of the site.\\nTwo entries in the menu are easy to confuse and mean opposite things:\\n- Not set removes the property altogether, so the element goes back to inheriting whatever it would have had.\\n- None is a real value of zero — an instruction to have no space here, which will override an inherited one.\\nIf an element already carries a step that is not on the ladder — set in the JSS tab, or by a template — the menu keeps it rather than silently rounding it, shown as \"10× the spacing unit\" with its resolved size.\\nReach for a custom amount when you need an exact figure — a hairline offset, a value that has to match a specific image — and for everything else use a step.\\nUnder the hood A step is stored as the number MUI\\'s theme.spacing() takes, so marginTop: 3 is what lands in the document and theme.spacing(3) resolves it at' },\n { path: '/building-sites/besigner/responsive-styling', title: 'Responsive styling & custom CSS', heading: 'Style groups', anchor: '#style-groups', text: 'The styles panel organizes every control into accordions, and every field has exactly one home — no custom CSS needed for the common properties:\\n- Flexbox & Grid — everything about laying an element out as a flex or grid container, and about where it sits inside its own parent: alignment and direction toggles, wrapping, the gap / row-gap / column-gap fields, the grid column and row track lists with auto-flow, and the per-item controls — grid column/row placement, flex grow, flex shrink, flex basis, and order.\\n- Layout — display variant and float.\\n- Colors — text color, background color, and Background Fill (see gradient backgrounds). Both pickers open on theme color references first (see scheme-scoped colors); a Custom color step reveals the full picker. The two color fields hold a color — a hex, an rgb()/hsl() value, or a theme reference. Paste anything else (a gradient, a url(…)) and the field says so and refuses it, rather than saving a value the browser would silently drop.\\n- Sizing — width, height, and the min/max bounds for both.\\n- Typography — Font Family first (a menu, see picking a font), then font size, weight, line height, letter spacing, text transform, and text decoration.\\n- Borders & Shadows — a Border editor (thickness box + line-style menu) with its color beside it, the same editor per edge (top, right, bottom, left) for dividers and accent rails, a Corner Radius preset menu, an Outline, and a Shadow preset menu. The shorthand draws all four edges; use the per-edge fields when you want a rule under a header or a line between columns. See borders without CSS.\\n- Position & Overflow — position scheme with top/right/bottom/left offsets, z-index, overflow, opacity, and cursor.\\nEvery field also carries a ✕ at its top-right once it holds a value. That is how a ' },\n { path: '/building-sites/besigner/responsive-styling', title: 'Responsive styling & custom CSS', heading: 'Gradient backgrounds', anchor: '#gradient-backgrounds', text: 'Background Fill in the Colors group builds a gradient by clicking — no custom CSS.\\n1. Set Background Fill to Linear gradient (or Radial gradient) — the field opens as that one select. It starts from your theme\\'s primary and secondary colors so you see a gradient immediately; Solid color paints no image and hands the element back to the Background Color field above.\\n2. Set the Angle in degrees for a linear fill — 180 runs top to bottom, 90 left to right.\\n3. Edit the color stops. Each stop is a color button plus a position in percent. The color button opens the same two-stage picker as every other color field, so a stop can be a theme color (it then follows your palette, in both light and dark) or a literal hex. One gradient can mix the two — bind the ends to Primary and Secondary and drop a literal mid-tone between them. Add stop adds one near the end of the ramp, ready to be positioned; a gradient always keeps at least two, so the remove button greys out at two.\\nBackground Fill writes background-image, which paints over the Background Color — so a solid color set there still shows through anywhere the gradient is transparent, and stays as the fallback if you switch the fill back to solid.\\nThe first choice in the menu — Default, or Inherited on a component instance — is not the same as Solid color. Default leaves the fill unset; Solid color is a positive \"paint no image\" (background-image: none). On an ordinary element they look identical, and on a component instance they are the difference between keeping the component\\'s gradient and replacing it with your own background color.\\nLike the other color fields, Background Fill is scheme-scoped: theme-color stops adapt on their own, and while the artboard previews dark you can give dark a different gradient outright.\\nA backgr' },\n { path: '/building-sites/besigner/responsive-styling', title: 'Responsive styling & custom CSS', heading: 'Visibility per device band', anchor: '#visibility-per-device-band', text: 'The Visibility accordion hides the selected element on whole device bands — mobile (under 600px), tablet (600–899px), and desktop (900px and up). Bands are range-scoped rather than mobile-first, so hiding one band never changes the element\\'s display on the others — the classic \"hide the link cluster on mobile, show a menu button instead\" swap is two toggles (menus & navigation).\\nOn the canvas, bands follow the artboard: select a device in the preview switcher and the matching band applies at that device\\'s width — XS shows the mobile band, SM the tablet band, MD and up the desktop band. In Fluid Responsive mode the canvas follows the real browser window instead, so resize the window (or open the published site) to see bands flip.' },\n { path: '/building-sites/besigner/responsive-styling', title: 'Responsive styling & custom CSS', heading: 'Scheme-scoped colors', anchor: '#scheme-scoped-colors', text: 'Published sites follow each visitor\\'s light/dark scheme (system setting, or their own choice via the theme mode switcher component) unless the theme has its Dark scheme switched off, so a hardcoded light-mode hex can be unreadable in dark mode. Two tools keep colors correct in both schemes:\\nTheme color references (preferred). Every color picker — text, background, and border color, plus color attributes on components — opens on your site theme\\'s palette references first: Primary, Secondary, Background, Surface, Text, Divider, and friends. Each swatch is split to preview its light and dark resolutions, and selecting one stores the reference (e.g. background.paper), not a fixed color — the element automatically re-colors when the site switches schemes. Pick Custom color to reveal the full picker when you really want a fixed value.\\nPer-scheme custom colors. The artboard\\'s scheme toggle (the sun/moon button in the toolbar) doubles as a styling scope, exactly like the device preview does for breakpoints:\\n- Light preview (default) — color edits set the element\\'s base colors, which both schemes share until dark overrides exist.\\n- Dark preview — the styles panel shows a \"Styling: dark scheme\" chip, and edits to text, background, and border color become dark-only overrides. Light mode keeps the base values; the canvas shows the dark result as you edit.\\nOnly color fields scope to the scheme — spacing, sizing, typography, and layout always apply to both schemes no matter which one you preview. Clearing a color while previewing dark removes the override and falls back to the base color. Scheme overrides compose with breakpoint scoping: previewing dark on the MD – Laptop artboard writes a dark override that applies from MD up.' },\n { path: '/building-sites/besigner/responsive-styling', title: 'Responsive styling & custom CSS', heading: 'Custom classes', anchor: '#custom-classes', text: 'Every element accepts Classes (chips input under Classes & custom CSS). They merge into the rendered element, so you can target them from theme styles and from interaction class actions.' },\n { path: '/building-sites/besigner/responsive-styling', title: 'Responsive styling & custom CSS', heading: 'Custom CSS (sx)', anchor: '#custom-css-sx', text: 'The Classes & custom CSS section edits the element\\'s sx in three modes:\\n- Builder — property + value rows with grouped suggestions (layout, spacing, typography, background, border, effects).\\n- CSS — paste plain declarations (border-radius: 8px;); they parse into the element\\'s styles at the active breakpoint scope.\\n- JSS (sx) — the full document as JSON, including responsive objects ({ xs, md }) and nested selectors (\"&:hover\"), for full control.' },\n { path: '/building-sites/besigner/responsive-styling', title: 'Responsive styling & custom CSS', heading: 'Semantic sections & theme mode', anchor: '#semantic-sections--theme-mode', text: '- The Section component groups children inside a real HTML element — section, article, aside, nav, header, footer, or div — keeping your page outline meaningful for SEO and assistive tech.\\n- The Theme mode switcher component gives visitors a light/dark/ device-default override that persists across visits. It is hidden on published pages while the theme\\'s Dark scheme is off.' },\n { path: '/building-sites/besigner/responsive-styling', title: 'Responsive styling & custom CSS', heading: 'Edit JSON for one element', anchor: '#edit-json-for-one-element', text: 'Right-click any element → Edit JSON to edit just that element and its children as JSON (the rest of the screen is untouched). Apply validates component ids and node ids, and the change is undoable.' },\n { path: '/building-sites/besigner/reusable-components', title: 'Reusable components', heading: '', anchor: '', text: 'Reusable components\\nBuild something once — a card, a call-to-action, a footer block — and reuse it everywhere as a reusable component.\\nAn instance is not a copy. It grafts the source at render time, so editing the component updates every page that places it. Change a font size once and the whole site follows.\\nA component is content you repeat inside a page. If what you want is the frame around the page — header, navigation, footer — that is a layout, not a component. And if you want a starting point you copy once, with no live link back to the source, that is a template.\\nPlan availability Starter and above. On Free, promoting an element answers \"Reusable components require a Starter plan — see Billing to upgrade.\" There is no cap on how many components a site can have.' },\n { path: '/building-sites/besigner/reusable-components', title: 'Reusable components', heading: 'Promote', anchor: '#promote', text: '1. Select the element you want to reuse. The whole subtree comes with it.\\n2. In the Attributes panel, choose Save as reusable component.\\n3. Give it a name and an optional description, then Save component.\\nThe element you promoted becomes the first instance of the new component, in place. It keeps its position in the page and its element id, so the parent\\'s child list, your undo history and the current selection all stay valid.\\nThat in-place swap is the point. If promoting left the original behind as ordinary elements, the document that defined the component would be the one place that never tracked it — you would edit the component later and this page alone would silently keep the old copy.\\nSave as reusable component appears only on an element that is not already an instance and is not locked by a shared layout. Elements a layout frames are locked on the screens that use it — open the layout to promote from there.' },\n { path: '/building-sites/besigner/reusable-components', title: 'Reusable components', heading: 'Insert instances', anchor: '#insert-instances', text: 'Insert instances from Your components in the element drawer, on any screen, layout, template or other component.\\nOn the canvas an instance renders its actual content, not a placeholder — what you see is what the page will render, with properties already resolved. The rendered elements are not canvas elements, though: clicking anywhere on an instance selects the instance, which is the only thing there you can select, move or delete. To change what is inside it, open the component.' },\n { path: '/building-sites/besigner/reusable-components', title: 'Reusable components', heading: 'Properties', anchor: '#properties', text: 'A component doesn\\'t have to look identical everywhere. Give it properties and each place you use it supplies its own text, image, link, color or choice — while the layout and the styling still come from the one component.\\nThis is what stops a hero from being rebuilt on every page. Only the differences vary.\\nDeclare them\\n1. Open the component and choose File ▸ Properties…\\n2. Add property. Give it a name — headline, say — a type, an optional label for the Attributes panel, optional help, and a default.\\n3. The dialog shows each property\\'s token under its name.\\nEvery field a built-in element offers in the Attributes panel is a property type, listed in the Type picker by group:\\n| Group | Types |\\n| -- | -- |\\n| Text | Text · Long text · Formatted document · Table |\\n| Media and links | Image · Link · Icon |\\n| Numbers | Number · Slider |\\n| Choices | Yes / no · Checkbox · Choice · Radio buttons · Toggle buttons · Pick list |\\n| Style | Color · Size · Border · Background fill · Column span · Theme preset · Theme scale |\\n| Date and time | Date · Time |\\n| Site content | Element on the page · Product · Collection · Category · Dataset · Dataset field · Form · Plugin · Plugin settings |\\nA property is edited with the control that field has on a built-in element, in the dialog\\'s Default and on every page that places the component: Yes / no is a switch, Icon is the icon picker, Color is the color and theme-token picker, Size is a number with a unit, Product lists the site\\'s products, and so on.\\nSome types need a setting before they can be drawn, shown under the property\\'s row:\\n- Slider — the lowest value, the highest value and the step.\\n- Choice — whether a page can pick several answers.\\n- Theme scale — which of the theme\\'s scales it offers: font sizes, font weights or stacking layers. The' },\n { path: '/building-sites/besigner/reusable-components', title: 'Reusable components', heading: 'Retrofit duplicated sections', anchor: '#retrofit-duplicated-sections', text: 'If the same section has already been copied onto several pages, converting it is safe and takes one pass:\\n1. On the page whose wording is correct, promote the section. It becomes an instance and the definition is created from it.\\n2. Open the component, declare a property for each part that differs between pages, and replace those texts with their tokens. Use the first page\\'s wording as each default.\\n3. Save properties, then publish.\\n4. On every other page, insert an instance, check it renders, and only then delete the old section. Appending before deleting keeps the page\\'s section order intact when the section is the last one.\\n5. Fill in that page\\'s wording on the instance — or leave the fields empty where the copy was already identical.\\nDeleting last is what makes this reversible: at every point the page still has exactly one copy of the section.' },\n { path: '/building-sites/besigner/reusable-components', title: 'Reusable components', heading: 'Detach', anchor: '#detach', text: 'Detach from component, on an instance, turns it back into ordinary elements with fresh ids — the confirmation reads \"Detached — this copy no longer follows the component.\" Use it when one page needs a variation the shared source shouldn\\'t carry.\\nWhat you get is what the page was showing. The property values set on that instance — and every per-instance style and attribute override applied to it, on its outer element and on each element inside it — are baked into the copy as ordinary text, images, styles and attributes, so the section looks identical before and after; it is simply editable now. Nothing in the copy still points at a property.\\nDetach copies the component\\'s published tree — unsaved or unpublished edits sitting in the component\\'s working version are not what you get.' },\n { path: '/building-sites/besigner/reusable-components', title: 'Reusable components', heading: 'Nesting', anchor: '#nesting', text: 'A component can place instances of other components. Expansion runs to a depth of 5, which also bounds a component that accidentally references itself.\\nNested components expand on the canvas too, not only in Preview and on the live site — so a shared button inside a shared nav is drawn where you are editing, and publishing that button updates every open canvas that shows it, however deeply it is nested.' },\n { path: '/building-sites/besigner/reusable-components', title: 'Reusable components', heading: 'Used by', anchor: '#used-by', text: 'A component\\'s detail page has a Used by card listing everything that places an instance of it, so deleting one is not a guess.\\nThree places are searched, which is everywhere the renderer expands an instance:\\n- the published version of every screen,\\n- the published version of every layout,\\n- and other reusable components — a component can be placed inside another one, so one used nowhere else can still be very much in use.\\nUnpublished drafts and templates in your library are not searched. If the check fails — a dropped connection, say — the card says so and shows a Try again button. It never reports \"nothing uses this\" when it could not actually look.\\nIf a component is deleted while instances remain, those instances are left untouched rather than emptied: a missing definition never takes a published page down.' },\n { path: '/building-sites/besigner/reusable-components', title: 'Reusable components', heading: 'Manage', anchor: '#manage', text: 'From the site\\'s Components page you can rename, edit the description, open the besigner, or delete a reusable component. The component\\'s ID is persisted inside every screen that places it and never changes.\\nYou can also give a component its own icon, from the same picker the besigner uses for icon elements — either in the Edit component dialog on the Components page, or on the component\\'s detail page. Every instance is then marked with it: in the hierarchy, on the canvas badge, and in the element drawer under Your components. A page assembled from promoted sections becomes readable at a glance. Components without an icon keep the generic package glyph.' },\n { path: '/building-sites/besigner/reusable-components', title: 'Reusable components', heading: 'Duplicate', anchor: '#duplicate', text: 'To start a new component from an existing one, choose Duplicate… in its row menu on the Components page, or More → Duplicate on its detail page. The copy carries the definition, its properties and the latest saved version, under the name you give it. It has no instances: every screen keeps pointing at the original, and you place the copy where you want it. Duplicating needs the same plan as creating a component.' },\n { path: '/building-sites/besigner/reusable-components', title: 'Reusable components', heading: 'Copy & paste vs. reusable components', anchor: '#copy--paste-vs-reusable-components', text: '| You want | Use |\\n| -- | -- |\\n| Another one right here | Duplicate |\\n| The same structure somewhere else, edited separately from then on | Copy & paste |\\n| One thing that updates everywhere it appears | Reusable component |' },\n { path: '/building-sites/besigner/reusable-components', title: 'Reusable components', heading: 'Tips', anchor: '#tips', text: '- Reusable components are perfect for anything that repeats across pages — headers, CTAs, contact blocks.\\n- Give a property a default that reads well on its own. A page that sets nothing should still look finished.\\n- Detach when you need a one-off variation that shouldn\\'t affect the shared source.' },\n { path: '/building-sites/besigner/reusable-components', title: 'Reusable components', heading: 'Related', anchor: '#related', text: '- Copy & paste elements\\n- The Besigner\\n- Screens & layouts\\n- Generate a reusable component with Aglyn AI' },\n { path: '/building-sites/besigner/text-editing', title: 'Inline & rich text editing', heading: '', anchor: '', text: 'Inline & rich text editing\\nText is edited where it lives. Double-click a heading on the canvas and you are typing into that heading — the real one, at its real size, weight, color and position. Nothing pops open, nothing moves, and the page reflows around you as you type, exactly as visitors will see it.' },\n { path: '/building-sites/besigner/text-editing', title: 'Inline & rich text editing', heading: 'Edit on the canvas', anchor: '#edit-inline', text: '- Double-click the text. A single click just selects the element.\\n- The cursor lands at the end of the existing text — never selected-all, so a stray keystroke cannot wipe a heading you meant to add a word to.\\n- Type. The surrounding layout moves in real time: a heading that grows to two lines pushes the paragraph below it down as it happens.\\n- The selection outline and its shading step aside while you type, so you are looking at your text on the page rather than at editor chrome. It comes back when you finish.\\nA Markdown element is different — double-clicking one opens the full document editor instead. See Long documents in markdown.\\nFinishing, and changing your mind {#committing}\\n| You press | Plain text element | Rich text element |\\n| Enter | Commits the edit | Starts a new line |\\n| Shift + Enter | Inserts a line break, keeps editing | Starts a new line |\\n| Cmd/Ctrl + Enter | Commits the edit | Commits the edit |\\n| Escape | Cancels — everything you typed is discarded | Cancels |\\n| Click anywhere else | Commits, and selects whatever you clicked | Commits |\\n| Done on the toolbar | Commits | Commits |\\nTwo things worth knowing:\\n- One edit is one undo step. Undo puts back the previous text, and for formatted text the previous formatting with it.\\n- Opening and closing without typing is not an edit. No undo step is recorded and nothing is sent to anyone editing alongside you.\\nWhile you are editing text, Cmd/Ctrl + C, V and A act on the text — not on canvas elements.\\nThe toolbar {#inline-toolbar}\\nA small toolbar floats above the text you are editing: Bold, Italic, Underline, Bulleted list, Numbered list, Insert link, the {} Insert data button, and Done. Insert link asks for the address (https://…).' },\n { path: '/building-sites/besigner/text-editing', title: 'Inline & rich text editing', heading: 'Rich text', anchor: '#rich-text', text: 'Rich text is opt-in per element — today that means Typography. Everywhere else, text is plain, which is deliberate: a button label or a link caption has no business carrying paragraphs.\\nWhere it is available you can use bold, italic, underline, links, and bulleted or numbered lists. That is the whole list, and it is enforced rather than merely suggested: anything else — styling pasted in from another app, stray attributes, scripts — is stripped when the edit is committed. Links keep their address and nothing else.\\nFormatting is not styling Rich text says a word is emphasized; it does not say what emphasis looks like. Color, size, weight and spacing come from your theme and the Styles panel, which is what keeps a site looking like one site. If you find yourself wanting to color a single word, that is the JSS (sx) tab, not this.\\nIf you strip all the formatting back out on the canvas, the element quietly becomes a plain-text element again — there is no leftover empty markup to trip over later.' },\n { path: '/building-sites/besigner/text-editing', title: 'Inline & rich text editing', heading: 'The Text attribute', anchor: '#the-text-attribute', text: 'The Text field in the right-hand panel and double-clicking on the canvas edit the same value. Use whichever suits: the panel is often quicker for a short label you are setting alongside other attributes.\\nWhen the Text field is read-only {#text-field-read-only}\\nOnce an element carries formatted text, the Text field goes read-only and says so:\\n> This text is formatted — double-click the element on the canvas to edit it. Remove\\n> formatting to edit it here.\\nThis is not the field being awkward. A plain text box can only show you the words, so anything you typed into it would have to throw the bold, the links and the lists away silently to save. Rather than do that behind your back, the field shows you the text, stays out of the way, and points you at the two things you can actually do.\\nRemove formatting {#remove-formatting}\\nAbove the field is a small Remove formatting button. It does exactly what it says and nothing more:\\n- Kept — every word, and every line break.\\n- Lost — bold, italic, underline, links, and list structure.\\nThe element goes on saying the same thing in the same shape, and the Text field becomes editable again. It is one undo step, so if you press it by mistake, undo brings the formatting straight back.' },\n { path: '/building-sites/besigner/text-editing', title: 'Inline & rich text editing', heading: 'Line breaks', anchor: '#line-breaks', text: 'Shift + Enter puts a real line break in plain text; on a rich text element, Enter does. Breaks survive into the Text field, which is a multi-line box that shows them as breaks.\\nText wrapping because a container is narrow is not a line break, and correctly does not appear in the Text field — it is a fact about the width, not about the text, and it changes on a phone.\\nAn element edited before this shipped Line breaks only started being carried into the Text field recently, and nothing was rewritten retroactively. An older element may show its text in the panel without the breaks until the next time you edit it on the canvas — after which it will look right there too.' },\n { path: '/building-sites/besigner/text-editing', title: 'Inline & rich text editing', heading: 'Bindings in text', anchor: '#bindings-in-text', text: 'Text props accept bindings — {{variable}}, {{fn:name(args)}}, and dataset fields.\\nOn the canvas, a binding normally shows you its resolved value. The moment you start editing, it turns into a single named pill instead. That is on purpose: a binding is one thing, and if it were spelled out as ordinary letters, a backspace in the middle of it would quietly destroy it. As a pill it can only be kept or removed whole — click it to Replace or Remove it, and use {} on the toolbar to insert a new one where the cursor is. The pill label is only ever a display; what gets saved is the {{…}} you wrote.' },\n { path: '/building-sites/besigner/text-editing', title: 'Inline & rich text editing', heading: 'Where you cannot edit in place', anchor: '#limits', text: '- Elements that do not declare text. In-place editing is offered by the components built for it — Typography, inline text, buttons, screen links, accordion summaries and the email text blocks. Elsewhere, use the attribute panel.\\n- Locked elements, such as the layout chrome shown around a screen. Double-click does nothing; edit it in the layout itself.\\n- While the interaction builder is choosing a target — there, two clicks are two choices, not an edit.\\n- Inside a reusable component instance, you can only edit a piece of text the component exposed as a prop. Text the component owns stays as the component defines it, which is the point of a component. Clearing an override you have set returns that text to the component\\'s own copy.' },\n { path: '/building-sites/besigner/text-editing', title: 'Inline & rich text editing', heading: 'Multi-line elements and the selection outline', anchor: '#wrapped-outlines', text: 'An inline run of text that wraps onto several lines is outlined line by line, with a single name label — the same way a browser highlights text you select with the mouse. It is one element, drawn as the several line boxes it genuinely occupies, rather than one big rectangle that would cover the empty space beside a short last line and the neighboring text beside the first.\\nBlocks are unaffected: one element, one box, as before.' },\n { path: '/building-sites/besigner/text-editing', title: 'Inline & rich text editing', heading: 'Related', anchor: '#related', text: '- The Besigner\\n- Responsive styling & custom CSS\\n- Reusable components\\n- Long documents in markdown\\n- Bindings, variables & functions\\n- Live co-editing & unsaved work' },\n { path: '/building-sites/besigner/theme-styles', title: 'Text styles & theme tokens', heading: '', anchor: '', text: 'Text styles & theme tokens\\nEvery site has a theme — a named set of type sizes, weights, colors, spacing steps and shadows. The styles panel offers those names wherever one exists, and picking a name is almost always the better answer than typing a number.' },\n { path: '/building-sites/besigner/theme-styles', title: 'Text styles & theme tokens', heading: 'Why a name beats a number', anchor: '#why-a-name-beats-a-number', text: 'Type 13px into Font Size and that element is now pinned to 13 pixels forever. Pick Body compact and the element follows your theme: if the brand later decides small text should be 14px, every element that asked for Body compact moves, and the ones that were told 13px stay behind.\\nThe same applies to weight, spacing, corner radius and shadow. Each control that can follow the theme shows you what the name resolves to today — Bold (theme) sits next to 700, Small next to 16px — so you can see the value without hard-coding it.' },\n { path: '/building-sites/besigner/theme-styles', title: 'Text styles & theme tokens', heading: 'Text Style sets everything at once', anchor: '#text-style-sets-everything-at-once', text: 'Text Style is the first control in the Typography group, and it is the one pick that can be right on its own. It applies the whole style — face, size, weight, line height and letter spacing — in a single choice.\\nThe fields under it (Font Family, Font Size, Font Weight, and so on) are adjustments on top of a text style, not the way to build one. Reach for them when you need a deliberate exception, not to reconstruct a heading by hand.\\nStart with Text Style If you find yourself setting size and weight and line height to match a heading elsewhere on the site, stop — that is a Text Style, and matching it by hand will drift the moment either place changes.' },\n { path: '/building-sites/besigner/theme-styles', title: 'Text styles & theme tokens', heading: 'What your theme offers', anchor: '#what-your-theme-offers', text: 'Aglyn\\'s default theme carries the standard set plus a few rungs of its own. Your own theme may differ; the panel always lists what your site defines.\\n| Text style | Typical use |\\n| Heading 1 – Heading 6 | Page and section headings |\\n| Lede | The intro paragraph under a heading |\\n| Body / Body small | Running text |\\n| Body compact | Dense labels and card text |\\n| Caption | Small print under an element |\\n| Micro | Metadata lines, fine detail |\\n| Overline | The small label above a heading |\\n| Button | Button text |\\nFont weights are named too — Light, Regular, Medium, Semi bold, Bold, Extra bold and Black — and each row shows the number it resolves to.' },\n { path: '/building-sites/besigner/theme-styles', title: 'Text styles & theme tokens', heading: 'Colors, spacing and shadows', anchor: '#colors-spacing-and-shadows', text: 'The same rule runs through the rest of the panel:\\n- Colors — pick from your theme\\'s palette rather than pasting a hex code. A palette color follows a rebrand; #00B0FF does not.\\n- Spacing, Gap, Margin and Padding — the ladder (None, Extra small, Small, Medium, Large…) are multiples of your theme\\'s spacing unit. Small stays correct if that unit changes.\\n- Corner Radius — Square through Very rounded are multiples of your theme\\'s shape scale. Pill and Circle are shapes rather than theme values, so those two are deliberately fixed.\\n- Shadow — the rungs are your theme\\'s elevation ladder, and each row previews the shadow it will draw.\\nEvery one of these controls keeps a Custom… entry. The escape hatch is always there when a design genuinely needs a one-off — it is simply not the default.' },\n { path: '/building-sites/besigner/theme-styles', title: 'Text styles & theme tokens', heading: 'Checking a page you already built', anchor: '#checking-a-page-you-already-built', text: 'Open the styles panel on an element and look at the Typography group. If Font Size and Font Weight are filled in on a heading, that heading is describing itself in numbers rather than asking for a Text Style. Clearing those two and picking the matching Text Style leaves it looking the same today and keeps it correct tomorrow.\\nChanging the theme itself Editing the theme changes every site that uses it, so it is a separate, deliberate step rather than something you do while styling one page. If a value you need has no name yet, that is a sign the theme is missing a rung — worth adding once, rather than typing the same number on twenty pages.' },\n { path: '/building-sites/besigner/video', title: 'Video', heading: '', anchor: '', text: 'Video\\nThe Video element plays a film from your media library, poster first. Almost everything else on this page follows from that poster: with one, a visitor downloads a single image and fetches the film only when they press play.' },\n { path: '/building-sites/besigner/video', title: 'Video', heading: 'Choosing the film', anchor: '#video-source', text: 'Set a Video source with Browse media. A video from your media library arrives with a poster frame, its running time and its dimensions already filled in — you only need Poster image to override the frame the library picked.\\nVideo uploads are paused New video files cannot be uploaded to the media library for now. The Video element can still use any video that is already in your library, or a video hosted on Wistia.\\nPicking a video from the media library copies its dimensions onto the element, which reserves the right-shaped box before anything loads and stops the rest of the page shifting when it does. It fills in the duration too, so the only SEO fields left to write are the title, the description and the date.' },\n { path: '/building-sites/besigner/video', title: 'Video', heading: 'What a visitor downloads before pressing play', anchor: '#video-preload', text: 'The poster is what makes the element cheap. With one, Preload defaults to fetching nothing — a visitor downloads the poster and not one byte of the film until they press play. Without a poster the element falls back to fetching the video\\'s length and dimensions, because a video with neither a poster nor metadata paints a black rectangle. If you want the old behavior on a postered video, set Preload to Length and dimensions only.' },\n { path: '/building-sites/besigner/video', title: 'Video', heading: 'Captions', anchor: '#video-captions', text: 'Captions file takes a WebVTT (.vtt) file — pick it with Browse media. Fill in Captions label and Captions language so the player can name the track.' },\n { path: '/building-sites/besigner/video', title: 'Video', heading: 'Opening in a lightbox', anchor: '#video-lightbox', text: 'Turn on Open in a lightbox and the poster becomes a play button that opens the film full size over the page, with a close button and Esc to dismiss. It needs a poster; without one the player stays in the page as usual.\\nThe lightbox has its own player controls: play, a seek bar with the time, mute and volume, captions when the video has a captions file, and full screen. Each one works from the keyboard. Space or K plays and pauses, the left and right arrows skip five seconds, M mutes, F goes full screen, and Esc closes the lightbox from any control, leaving full screen first if it is on.' },\n { path: '/building-sites/besigner/video', title: 'Video', heading: 'A video hosted on Wistia', anchor: '#video-wistia', text: 'Paste a Wistia media link into Video source: the address of the video\\'s page in your Wistia account, or its embed link. Then pick a Poster image from your media library. The poster becomes a play button, and Wistia\\'s player loads only when a visitor presses it, so someone who never presses play never contacts Wistia, unless you turn on Load the player with the page (below). The player opens in the lightbox when Open in a lightbox is on, and in place of the poster when it is off. Without a poster the element asks for one instead of loading the player.\\nWistia\\'s player brings its own controls. In the lightbox, Esc closes it until you move into the player; from inside the player, Shift+Tab reaches the close button. Wistia records a viewing only for a visitor whose analytics consent your site has on record, and plays with its tracking off for everyone else. Type the running time into Duration (seconds), because a Wistia link does not carry it.\\nTurn on Load the player with the page for a page whose main content is this one video, such as the entry page of a video collection. Search engines list a video only from a page like that, and they only find a player that is on the page when it loads. With the switch on, Wistia\\'s player replaces the poster as soon as the page loads, without starting the video, for each visitor whose analytics consent your site has on record. Everyone else still gets the poster to press, with Wistia\\'s tracking off. The player stays in the page even when Open in a lightbox is on. Keep the switch off on your home page, and on any page where the video is secondary.\\nWistia\\'s player keeps data in the visitor\\'s browser from the moment it loads, which is why the switch waits for consent. In the geo-conditional consent mode, a visitor outside the EU, the UK and' },\n { path: '/building-sites/besigner/video', title: 'Video', heading: 'Starting the film from a button', anchor: '#video-play-from-a-button', text: 'A button elsewhere on the page can start the film: give the button the interaction When clicked → Play a video and pick this Video. It does what pressing the poster does.' },\n { path: '/building-sites/besigner/video', title: 'Video', heading: 'The fields a search result reads', anchor: '#video-seo', text: 'Video title and Publication date are what let search engines list the video in results. They are not decoration: a video is only eligible for a video result when both are filled in, alongside the poster. Video description is recommended rather than required, so a video without one can still be listed; write one anyway, different for every video. The title is also the name a screen reader announces for the player, and the tooltip on hover. None of the three is ever displayed as text on the page.\\nTo give each video a page of its own, build a video collection: its entry template fills these fields from each entry, with Publication date set to {{entry.publishedAt}}.' },\n { path: '/building-sites/besigner/video', title: 'Video', heading: 'Related', anchor: '#related', text: '- Element catalog — every other element in the drawer\\n- SEO overview — how a page describes itself to search engines\\n- The Besigner' },\n { path: '/building-sites/bindings/overview', title: 'Bindings, Variables & Functions', heading: '', anchor: '', text: 'Bindings, Variables & Functions\\nBindings put live values into your content. Instead of hard-coding text, you reference a variable, a function result, or a dataset field, and Aglyn resolves it when the page renders.\\nPlan availability Free to start, with plan caps on the number of variables, functions, and workflows.' },\n { path: '/building-sites/bindings/overview', title: 'Bindings, Variables & Functions', heading: 'Binding tokens', anchor: '#binding-tokens', text: 'Bindings appear inside text props as tokens:\\n| Token | Resolves to |\\n| {{name}} | A site variable. |\\n| {{fn:name(args)}} | The result of a function. |\\n| Dataset field bindings | A value from a dataset record. |\\nThe Besigner resolves bindings WYSIWYG on the canvas and marks bound content so you can see what\\'s dynamic. The { } button in the canvas toolbar switches to the raw tokens and back.' },\n { path: '/building-sites/bindings/overview', title: 'Bindings, Variables & Functions', heading: 'Rename-safe id tokens', anchor: '#rename-safe-id-tokens', text: 'Bindings reference variables and functions by id, not by name. That means renaming a variable doesn\\'t break anything that uses it. A typed {{name}} is converted to its id form when you save or publish, and imports are converted automatically. A published page resolves only the id form, so a name token that was never converted — because no variable had that name — shows exactly as typed.' },\n { path: '/building-sites/bindings/overview', title: 'Bindings, Variables & Functions', heading: 'Insert a variable', anchor: '#insert-a-variable', text: 'You never have to hand-type token syntax. Every text-capable attribute field in the Besigner (Text, Link URL, Image URL, …) has a small {x} insert button at its end. Click it to open the data picker — a searchable menu grouped by source:\\n| Group | Tokens | When it appears |\\n| Variables | Your site variables (inserted as rename-safe id tokens) | When the site has variables |\\n| Functions | Your functions, with parameter placeholders | When the site has functions |\\n| Site | The site\\'s own details — see Site details | Always |\\n| Entry | Collection-entry fields — Title, Excerpt, Link URL, Published date, … | Always (hint shows where they resolve) |\\n| Collection | Collection name and slug | Always (resolves on collection pages) |\\n| Dataset item | The fields of the repeated dataset, by display name | Only inside a repeating container |\\nPicking an option inserts the token at your cursor, keeping the text around it — so you can compose values like Read “{{entry.title}}” → or https://example.com/blog/{{entry.slug}} by mixing typed text and inserted placeholders. Pick again to keep concatenating; the cursor lands right after each inserted token. Each variable option shows a live value preview so you can confirm what the binding resolves to before inserting it.\\nSelected text is replaced by the inserted token, and the element-level Insert binding button on text elements opens the same picker (appending to the element text).\\nToken pills\\nPlaceholders never show up as raw {{...}} syntax while you edit — each one renders as a small colored pill carrying the binding\\'s current display name. Variables, functions, entry fields, collection fields, and dataset items each get their own stable color, so a glance tells you what kind of data a pill resolves to. Under the hood the stored value sti' },\n { path: '/building-sites/bindings/overview', title: 'Bindings, Variables & Functions', heading: 'Site details', anchor: '#site-details', text: '{{host.}} tokens name the site itself instead of hard-coding it, so copy stays right when the site is renamed, and a template installed on another site fills in that site\\'s details.\\n| Token | Fills in |\\n| {{host.businessName}} | Setup → SEO → Entity → Name, or the site name when that is empty |\\n| {{host.url}} | The site\\'s address — its custom domain when one is connected |\\n| {{host.supportEmail}} | Support email, from Setup → Details → Business details |\\n| {{host.address}} | Postal address, from the same card |\\n| {{host.socialLinks}} | The labels of the card\\'s Social links |\\nA detail the site hasn\\'t set renders as nothing, never as the token. The canvas and Preview fill these in from the site you are editing, exactly as the published page does. In a field, the token shows as a pill named after the detail, such as Business name.' },\n { path: '/building-sites/bindings/overview', title: 'Bindings, Variables & Functions', heading: 'Typed variables', anchor: '#typed-variables', text: 'Create typed site variables and reference them as {{name}} in any prop. Types keep values consistent across the site.' },\n { path: '/building-sites/bindings/overview', title: 'Bindings, Variables & Functions', heading: 'No-code functions', anchor: '#no-code-functions', text: 'Build functions in the in-editor function builder with a safe evaluator — no arbitrary code execution. Compose variables and other functions, and call them inline with {{fn:name(args)}}.\\nA function is parameters in, numbered if / then / otherwise steps, and one value out. Inside a step, an expression can use:\\n- + - / and parentheses, on numbers; + also joins text.\\n- Any site variable, by its name. A function that prices a plan can read extrasiteprice directly, so changing the variable changes every page and every calculator that uses it. A function can read a variable and never set one, and a parameter or local variable with the same name wins.\\n- A dictionary variable\\'s members, as name.member. Keep one dictionary per plan — {\"name\": \"Pro\", \"annual\": 39, \"sites\": 3} — and read planpro.annual. A text variable inside the function can hold a copy of one (chosen = planpro), so the function can pick a plan and then read chosen.sites.\\n- These built-ins: min(a, b, …), max(a, b, …), round(value, places), floor(value), ceil(value), abs(value), and format(value, places), which writes a number with thousands separators — \\'$\\' + format(1396) is $1,396.\\nThere are no loops and no way to add a built-in, which is what keeps the evaluator safe to run on a page.\\nParameters a visitor can answer\\nWhen a function is placed on a page in a Function Widget, each parameter becomes an input. Under What visitors see in the function builder, give a parameter a Label (instead of its identifier), a value it Starts as, and Choices written as value: Label, value: Label. A parameter with choices is asked as a list, a number as a number box, and a true/false as a switch.\\nA widget runs in the visitor\\'s browser, so the function\\'s definition is part of the page, along with the site variables that function na' },\n { path: '/building-sites/bindings/overview', title: 'Bindings, Variables & Functions', heading: 'Where-used & safety', anchor: '#where-used--safety', text: 'Before you rename or delete a variable or function, run the where-used scan to see every screen and prop that references it, so changes are safe.' },\n { path: '/building-sites/bindings/overview', title: 'Bindings, Variables & Functions', heading: 'Workflows', anchor: '#workflows', text: 'Variables and functions compose into workflows — multi-step logic triggered by site events. See Workflows & actions.' },\n { path: '/building-sites/bindings/overview', title: 'Bindings, Variables & Functions', heading: 'Related', anchor: '#related', text: '- Datasets\\n- The Besigner' },\n { path: '/building-sites/components/generate-a-component-with-aglyn-ai', title: 'Generate a reusable component with Aglyn AI', heading: '', anchor: '', text: 'Generate a reusable component with Aglyn AI\\nAglyn AI makes a reusable component two ways: from a brief, such as \"a testimonial card with the customer\\'s name, their role, their quote and a photo,\" or from a section you already have on a page. Either way you get one component built the way you would build it yourself: the elements, and a property for each value that changes from one page to the next, already bound to the element that shows it.\\nAvailability Aglyn AI build jobs are released gradually, and need the Generate with AI permission. Reusable components come with the Starter plan and above: on a plan without them, the job says so before it starts, and spends nothing.' },\n { path: '/building-sites/components/generate-a-component-with-aglyn-ai', title: 'Generate a reusable component with Aglyn AI', heading: 'From a brief', anchor: '#from-a-brief', text: 'Open Components for the site and choose Describe it, beside Templates and Create Component. Write what the component should show and which of its values change from one page to the next, then choose Plan the component, and open AI jobs in the Assist panel to read the plan and confirm it.\\nWhat the job builds\\nThe job proposes a plan first and builds once you confirm it, following the building rules. The plan lists the properties it intends, such as quote:richText or photo:image, and the component declares every one of them.\\nEach property is one of the kinds the File ▸ Properties… dialog offers:\\n| Kind | What it fills |\\n| -- | -- |\\n| Text, Long text | Copy: a heading, a paragraph, a button\\'s label, an image\\'s alt text. Copy can mix a property with words, such as Portrait of {{prop.name}}. |\\n| Image | A picture\\'s source. |\\n| Link | A screen picker, or an address. |\\n| Icon | An Icon element, or a button\\'s icon. |\\n| Number | A number setting, or copy. |\\n| Yes / no | A switch, such as a button\\'s Full width, or whether an optional part shows. |\\n| Choice | A dropdown, such as a button\\'s Variant, with answers that dropdown lists. |\\nA property is bound only where its value can show: an Image never lands in a heading, an icon is never shown as words, and a Choice never offers an answer its dropdown does not list.\\nA component\\'s headings start one level below the heading of the section that places it, such as a card\\'s title as a heading 3 under the section\\'s heading 2, so the page\\'s outline stays in order wherever the component goes. These are the pairings the {} beside a field offers when you bind one yourself.\\nOptional parts\\nA part not every page needs, such as a photo or a second button, gets a Yes / no property labeled Hide …, such as Hide photo. It starts as No, so the part show' },\n { path: '/building-sites/components/generate-a-component-with-aglyn-ai', title: 'Generate a reusable component with Aglyn AI', heading: 'From a section on your page', anchor: '#from-a-section-on-your-page', text: 'A section you have already built and want to reuse does not need describing: select it in the Besigner and use Make a reusable component with AI, under the element\\'s own settings in the Attributes panel. Name the component, and Aglyn AI reads the section and suggests which of its values each page should be able to change.\\nThe suggestion is shown before anything happens, with what every page will be able to set. The kinds, the pairings and the Hide … rule above are the same ones, except that a section\\'s icon stays part of the section rather than becoming an Icon property.\\nApply then does what Save as reusable component does, with the properties already in place:\\n1. the component is saved to your library, with each suggested value replaced by its property;\\n2. the section on your page is swapped for one that follows the component.\\nEach property\\'s default is the value that was on the page, so the page looks exactly as it did a moment before — the section now follows the component instead of being a copy of it. A Hide … property starts as No, because the part it hides is on the page.\\nThe swap is an unsaved change on the version you have open, and one undo takes it back. Nothing is published, and on the version your live site shows, the Besigner asks you to make a new version first. If the component cannot be saved — a plan without reusable components, for instance — nothing on your page changes at all.\\nPick the section, not the page Save the part you want to repeat. Aglyn AI declines a selection that is the whole page, one carrying the page\\'s main region, or one with more than one top-level heading, because a component is placed inside pages rather than being one.' },\n { path: '/building-sites/components/generate-a-component-with-aglyn-ai', title: 'Generate a reusable component with Aglyn AI', heading: 'Related', anchor: '#related', text: '- Reusable components\\n- How Aglyn AI builds' },\n { path: '/building-sites/custom-domains/connect-a-domain', title: 'Connect a domain', heading: '', anchor: '', text: 'Connect a domain\\nMove your site from its Aglyn subdomain to a domain you own. It\\'s one card, one DNS record, and one button — there\\'s no multi-step wizard.\\nPlan availability Starter and above. On Free, the card explains: \"Custom domains are included from the Starter plan — see Billing to upgrade.\"' },\n { path: '/building-sites/custom-domains/connect-a-domain', title: 'Connect a domain', heading: 'Steps', anchor: '#steps', text: '1. In the site\\'s Admin page, open the Custom Domain tab. The card explains: \"Point your domain at Aglyn, then verify. A subdomain like www uses a CNAME. A bare apex cannot carry a CNAME, so point it at the same hostname with an ALIAS — or use the A record if your registrar has no ALIAS. Any one of these verifies\", and lists the records with your domain filled in, each with a line saying when it\\'s the right one. Type a bare apex and the ALIAS line comes first; type a subdomain and the CNAME line does.\\n2. At your registrar\\'s DNS settings, add one of those records — see the quick reference below.\\n3. Back in Aglyn, type your domain into the Domain field and press Verify & connect. Aglyn checks DNS from public resolvers and, if the record checks out, connects the domain in the same click.\\n4. On success the card confirms \"your-domain.com\" connected and shows the domain as a green chip. An apex — connected by ALIAS or by A record, since both are checked the same way — gets an extra note: \"Apex verified by the addresses it resolves to.\"\\nIf DNS hasn\\'t propagated yet, Verify & connect tells you what it found instead — either the record it saw and what it expected, or that no record exists yet — and asks you to try again in a while. Nothing is saved on a failed verify, so re-running it is always safe.' },\n { path: '/building-sites/custom-domains/connect-a-domain', title: 'Connect a domain', heading: 'After it connects', anchor: '#after-it-connects', text: 'The chip next to your domain says what it is actually doing. SSL provisions automatically — there is nothing to upload or renew — but it takes a few minutes, and the chip is how you tell \"still working on it\" from \"stuck\".\\n| Chip | What it means | What to do |\\n| your-domain.com — live | Attached, DNS resolves here, certificate issued. | Nothing. |\\n| your-domain.com — issuing certificate | Everything checks out; the certificate is still being issued. | Wait a few minutes and press Check status. Until it finishes the domain may show a security warning. |\\n| your-domain.com — ownership check needed | Your domain is registered to another account on our hosting platform, so it has to be proven yours first. | Add the TXT record the card prints, then press Re-attach. |\\n| your-domain.com — DNS not pointing here | Attached, but the domain no longer resolves to us. | Check the record at your registrar — see troubleshooting. |\\n| your-domain.com — not attached | Saved here, but not attached to the serving platform, so it serves nothing. | Press Retry attachment. If it keeps failing the domain is likely held by another account — contact support with the domain name. |\\n| your-domain.com — attachment pending | Our attach call never landed, and the platform could not be asked just now. | Press Retry attachment. |\\nThree buttons sit next to the chip:\\n- Check status re-reads the live state. A certificate arrives on its own a few minutes later, and this is how you see that it has.\\n- Retry attachment (labeled Re-attach once the domain is healthy) re-runs the platform attachment. Always safe to press.\\n- Disconnect releases the domain — see below.\\nIf other DNS records are answering for the same name, the card says so and names them, even when the domain is live. That is worth acting on: a left' },\n { path: '/building-sites/custom-domains/connect-a-domain', title: 'Connect a domain', heading: 'Your Aglyn subdomain afterwards', anchor: '#your-aglyn-subdomain-afterwards', text: 'Once the domain is connected and serving, your-site.aglyn.app redirects to it — same page, same path, so an old link to your-site.aglyn.app/pricing lands on https://your-domain.com/pricing.\\nThat is deliberate. Serving the same pages at two addresses splits your search ranking between them, and lets a search engine pick which one to show. One address keeps the credit on the domain you own.\\nDisconnect the domain and the Aglyn subdomain starts serving again on its own, within a minute or so. The redirect is temporary by design so that reversal always works.' },\n { path: '/building-sites/custom-domains/connect-a-domain', title: 'Connect a domain', heading: 'Registrar quick reference', anchor: '#registrar-quick-reference', text: 'The exact screen differs by registrar, but you only ever add one record:\\nA subdomain (www.example.com, shop.example.com, …):\\n| Field | Value |\\n| Type | CNAME |\\n| Name / Host | www (or the subdomain you\\'re connecting) |\\n| Value / Target | sites.aglyn.app |\\n| TTL | default is fine |\\nA bare apex (example.com) — first choice:\\n| Field | Value |\\n| Type | ALIAS (also called ANAME, or \"CNAME flattening\") |\\n| Name / Host | @ |\\n| Value / Target | sites.aglyn.app |\\n| TTL | default is fine |\\nA bare apex, if your registrar has no ALIAS — fallback:\\n| Field | Value |\\n| Type | A |\\n| Name / Host | @ |\\n| Value | 216.198.79.1 |\\n| TTL | default is fine |\\nAn apex can\\'t carry a CNAME (that\\'s a DNS rule, not an Aglyn one), which is why it needs one of these two instead. Prefer the ALIAS: it names sites.aglyn.app rather than an address, so your registrar resolves it to wherever Aglyn is serving from at the time, and nothing in your zone has to change if those addresses ever do.\\nThe A record is there because plenty of registrars offer no ALIAS/ANAME and no flattening — for those, it is the only way to point an apex, and it verifies exactly the same. Its one cost is that the address is pinned in your zone by hand: keep it in mind if you ever see the domain stop resolving long after it was working.\\nCloudflare and other proxies CNAME flattening at the apex works, but leave the record DNS only (gray cloud) until verification succeeds — a proxied record resolves to the proxy\\'s addresses, not Aglyn\\'s.\\nWant both www and the apex? Aglyn serves one connected domain per site. Connect the one you want as your canonical address, then have your registrar redirect the other to it — most registrars offer domain forwarding for exactly this.' },\n { path: '/building-sites/custom-domains/connect-a-domain', title: 'Connect a domain', heading: 'One domain per site', anchor: '#one-domain-per-site', text: 'Each site carries a single custom domain, and a domain can only be connected to one site across all of Aglyn — connecting it elsewhere answers \"That domain is already connected to another site\". Disconnect it from the old site first.\\nSome names cannot be connected at all, and the wizard answers \"That domain is reserved by the platform and cannot be connected to a site\". These are Aglyn\\'s own hostnames — anything ending in aglyn.com, aglyn.app or aglyn.io, including another site\\'s your-site.aglyn.app address — and names on shared app-hosting suffixes such as vercel.app, pages.dev or github.io, which nobody can prove they own. Retrying will not help; connect a domain registered to you instead.' },\n { path: '/building-sites/custom-domains/connect-a-domain', title: 'Connect a domain', heading: 'Disconnect', anchor: '#disconnect', text: 'Disconnect, next to the connected domain\\'s chip, releases the domain immediately — there is no confirmation step — and the Aglyn subdomain resumes serving within a minute or so. Disconnecting requires the site admin role.\\nAglyn never touches your DNS: the CNAME, ALIAS, or A record at your registrar is yours, so remember to remove or repoint it yourself after disconnecting.' },\n { path: '/building-sites/custom-domains/connect-a-domain', title: 'Connect a domain', heading: 'Related', anchor: '#related', text: '- Troubleshoot verification\\n- Redirects\\n- SEO toolkit' },\n { path: '/building-sites/custom-domains/overview', title: 'Custom Domains', heading: '', anchor: '', text: 'Custom Domains\\nEvery site starts on an Aglyn subdomain. When you\\'re ready, connect your own domain — a subdomain like www.example.com or the bare apex example.com — self-serve, from the site\\'s Admin page.\\nPlan availability Starter and above.' },\n { path: '/building-sites/custom-domains/overview', title: 'Custom Domains', heading: 'Connect a domain', anchor: '#connect-a-domain', text: '1. In Admin, open the Custom Domain tab.\\n2. Add one DNS record at your registrar — a CNAME to sites.aglyn.app for a subdomain, or an ALIAS/ANAME to the same hostname for a bare apex (an A record where your registrar offers no ALIAS).\\n3. Type the domain into the Domain field and press Verify & connect. Aglyn checks DNS and connects the domain in the same click; SSL provisions automatically.\\nEach site carries one custom domain, shown as a chip on the card with Re-attach and Disconnect actions beside it. Once it serves, your .aglyn.app subdomain redirects to it, path preserved, so old links keep working.\\nHow-tos\\n- Connect a domain — steps, apex records (ALIAS first, A as the fallback), and what happens after\\n- Troubleshoot verification' },\n { path: '/building-sites/custom-domains/overview', title: 'Custom Domains', heading: 'Related', anchor: '#related', text: '- Getting started: create a site\\n- SEO toolkit' },\n { path: '/building-sites/custom-domains/troubleshooting', title: 'Troubleshoot verification', heading: '', anchor: '', text: 'Troubleshoot verification\\nIf Verify & connect won\\'t go green, it\\'s almost always DNS. Work through these.\\nPlan availability Starter and above.' },\n { path: '/building-sites/custom-domains/troubleshooting', title: 'Troubleshoot verification', heading: 'Checklist', anchor: '#checklist', text: '1. Give it time. DNS changes propagate on a TTL — wait a few minutes and press Verify & connect again. Aglyn resolves your record through public DNS resolvers, so a change your registrar shows as saved can still take a while to be visible.\\n2. Read the message. A failed verify tells you what it found: either the record it saw and the target it expected, or that no record exists yet. That\\'s usually the whole diagnosis.\\n3. Match the record to the name.\\n- A subdomain (www.example.com) needs a CNAME to sites.aglyn.app.\\n- A bare apex (example.com) needs an ALIAS/ANAME to sites.aglyn.app — an apex cannot carry a CNAME, and Aglyn verifies it by the addresses it resolves to.\\n- If your registrar offers no ALIAS/ANAME, use an A record to 216.198.79.1 instead. It verifies the same way. If an apex that used to work has stopped resolving, check this address is still the one in your zone — an ALIAS never needs that check, which is why it\\'s the first choice.\\n4. Remove conflicting records. Delete any old A/AAAA/CNAME records for the same host that point elsewhere. Two cases bite hardest:\\n- An apex with a wrong CNAME (some DNS hosts allow one) never falls through to the A-record check — remove the CNAME so the A record can be evaluated.\\n- A stale A record left over from a previous host answers alongside your ALIAS and wins unpredictably — delete the old A record outright.\\n5. Disable proxying temporarily. If your DNS provider proxies traffic (e.g. an orange-cloud toggle), the record resolves to the proxy\\'s addresses instead of Aglyn\\'s. Turn it off until verification succeeds.' },\n { path: '/building-sites/custom-domains/troubleshooting', title: 'Troubleshoot verification', heading: 'Verified but not serving?', anchor: '#verified-but-not-serving', text: 'Verification and attachment are separate steps under one button, and the chip next to your domain says which one you are waiting on — see the chip table for all of them. The three that come up most:\\n- \"— issuing certificate\" is not a problem. DNS is right and the domain is attached; the certificate takes a few minutes. Press Check status rather than reconnecting. A security warning in the browser during this window is expected and goes away on its own.\\n- \"— ownership check needed\" means your domain is registered to another account on our hosting platform. The card prints a TXT record — add it at your registrar exactly as shown, then press Re-attach. Nothing else will release the domain.\\n- \"— not attached\" means the domain is saved on your site but not on the serving platform. Press Retry attachment. If it keeps returning, the domain is held somewhere else and support needs the domain name to release it.\\nIf connecting answers \"That domain is already connected to another site\", the domain is attached to a different Aglyn site — every domain can serve only one. Disconnect it there first.' },\n { path: '/building-sites/custom-domains/troubleshooting', title: 'Troubleshoot verification', heading: 'The site loads for some people and not others', anchor: '#the-site-loads-for-some-people-and-not-others', text: 'That is almost always two records answering for the same name — typically an A record left behind by a previous host sitting next to a correct ALIAS. DNS hands out one or the other, so it looks fine every time you try it.\\nAglyn names the offending records: Verify & connect warns about addresses that are not ours, and the card keeps showing them under the chip afterwards, even while the domain is live. Delete them at your registrar; nothing needs reconnecting once they are gone.' },\n { path: '/building-sites/custom-domains/troubleshooting', title: 'Troubleshoot verification', heading: 'Still stuck?', anchor: '#still-stuck', text: 'Confirm the record resolves from your machine (dig www.example.com CNAME or dig example.com A), then press Verify & connect again.\\nTwo different checks run, and it helps to know which one you are reading. Verify & connect is the one that decides whether a domain may be connected at all, and it runs only when you press it. Check status asks the serving platform what the domain is doing right now — including whether DNS still points here — so a record you changed after connecting shows up there rather than needing a disconnect and reconnect.' },\n { path: '/building-sites/custom-domains/troubleshooting', title: 'Troubleshoot verification', heading: 'Related', anchor: '#related', text: '- Connect a domain' },\n { path: '/building-sites/menus-and-navigation/overview', title: 'Menus & navigation', heading: '', anchor: '', text: 'Menus & navigation\\nReal navigation needs more than a row of links. The Navigation group in the element picker ships menu elements with the interactivity built in — no code, no raw JSON:\\n| Element | What it does |\\n| Dropdown Menu | A nav item that opens a dropdown of links — click-toggles out of the box, interactions do the rest. |\\n| Mega Menu | A SaaS-style wide panel with a free-form slot for columns of links and promos. |\\n| Drawer | A slide-in panel (left or right) with a slot for anything — the mobile menu building block. |\\n| Menu Button | A hamburger icon button that opens/closes a drawer. |\\n| Mobile Nav | A one-insert preset: menu button + drawer + inline desktop links, responsive wiring included. |\\n| Dropdown Panel | A primitive-built hover panel — Stack + Button + hidden panel with the hover interactions pre-wired and editable. |\\nAll of them render closed on the live site until the visitor interacts, and the canvas mirrors that: menus show just their trigger and drawers a slim placeholder. Select the element — or anything inside it — and it expands in place so its contents stay selectable and editable; click elsewhere and it collapses again.' },\n { path: '/building-sites/menus-and-navigation/overview', title: 'Menus & navigation', heading: 'Dropdown menu', anchor: '#dropdown-menu', text: 'Insert Dropdown Menu, set its Label, and drop screen links (or anything else) inside. Its only attribute is Label — how the menu opens is not an attribute:\\n- By default, clicking the nav item toggles the menu. No configuration needed; the menu also closes on outside clicks and Esc .\\n- Everything else is an interaction — e.g. When hovered → Open a menu on the menu element itself gives you a hover menu, and any other element can open, close, or toggle any menu.\\nMenus opened by a hover interaction close themselves once the pointer leaves the nav item and its panel, with a short grace period so the pointer can travel between the two.' },\n { path: '/building-sites/menus-and-navigation/overview', title: 'Menus & navigation', heading: 'Mega menu', anchor: '#mega-menu', text: 'Insert Mega Menu inside your nav bar. Its panel is a normal canvas slot — the preset starts you with three columns of links, but rows, images, and promo cards all work. Attributes:\\n- Panel width — Fit content, Wide (720px), or Full width (edge-to-edge under the nav item).\\nIt opens exactly like the dropdown: click-toggle by default, and the classic SaaS hover behavior is one interaction — on the mega menu element, When hovered → Open a menu (the target defaults to the element itself). Hover-opened panels close on pointer leave automatically.' },\n { path: '/building-sites/menus-and-navigation/overview', title: 'Menus & navigation', heading: 'Drawer & menu button', anchor: '#drawer--menu-button', text: 'The Drawer slides in from the page edge and holds any canvas children — typically a vertical stack of screen links. It opens three ways:\\n1. A Menu Button element. Clicking it toggles the page\\'s first drawer — no configuration at all (which is why the Mobile Nav preset works with zero wiring). Its only attribute is Accessibility label; how it targets a drawer is not an attribute.\\n2. An interaction with the Open / Close / Open-close a drawer actions (below) — on the Menu Button itself when a page has several drawers and you need to point it at a specific one, or on any other element.\\n3. The built-in close button, backdrop click, or Esc — closing is always handled for you.\\nDrawer attributes: Slides in from (left/right) and Width. The canvas renders the drawer inline at its configured width, so its links and headers stay full-size and editable.' },\n { path: '/building-sites/menus-and-navigation/overview', title: 'Menus & navigation', heading: 'The Mobile Nav preset', anchor: '#the-mobile-nav-preset', text: 'Insert Mobile Nav (Navigation group) into your nav bar\\'s toolbar and you get a working responsive pattern immediately:\\n- a Menu Button — hidden on desktop,\\n- an inline link row — hidden on mobile and tablet,\\n- a Drawer with a vertical link stack, already answering the button.\\nSwap the placeholder links for your screens and you\\'re done. The show/hide wiring is plain visibility styling, so you can retune the breakpoints per element afterwards.' },\n { path: '/building-sites/menus-and-navigation/overview', title: 'Menus & navigation', heading: 'The Dropdown Panel preset', anchor: '#the-dropdown-panel-preset', text: 'The menu elements above are purpose-built components. Dropdown Panel is the same result assembled from primitives — insert it and you get:\\n- a wrapper Stack (position: relative) holding\\n- a trigger Button, and\\n- a panel Stack positioned under the trigger, three link columns inside, already set to Hide on published site so it starts closed on the live site (the canvas starts it closed too — open it with the eye on its hierarchy row when you want to design it).\\nThe difference from hand-building: the hover choreography arrives pre-wired. Inserting the preset creates two real interactions on the wrapper, enabled immediately and listed on its Interactions panel like any you\\'d author yourself:\\n1. When hovered → Show the panel, with Close on Esc and Close on outside click.\\n2. When hover ends → Hide the panel with a 250ms delay, so the pointer can travel from trigger to panel.\\nBecause they\\'re ordinary interactions and ordinary elements, everything is editable: restyle the panel, change the delay, swap the trigger for an icon button, or delete one interaction and wire your own. Use it when you want full styling control that the Mega Menu\\'s panel attributes don\\'t reach — and see the walkthrough\\'s from-scratch recipe for the same pattern built by hand.' },\n { path: '/building-sites/menus-and-navigation/overview', title: 'Menus & navigation', heading: 'Interactions for menus', anchor: '#interactions-for-menus', text: 'The props panel\\'s Add interaction system understands menu choreography (interactions guide):\\n- Triggers: When clicked…, When hovered…, When hover ends…, When scrolled into view…\\n- Actions: Show / Hide / Show-hide an element (pick the target from a list of canvas elements), Open / Close / Open-close a menu (drives a Dropdown or Mega Menu; picking defaults to the element itself when it is a menu, or the page\\'s first menu), and Open / Close / Open-close a drawer (defaults to the element itself when it is a drawer, or the page\\'s first drawer).\\n- Frequency: new interactions default to Every time so toggles keep working; the legacy once per page view and the per-session / per-visitor / cooldown caps are still there for announcements.\\nThree patterns cover most menus:\\n- Hover menu — on the Dropdown/Mega Menu element itself: When hovered → Open a menu. Leaving the nav item and its panel closes it automatically — no hover-ends interaction needed.\\n- Custom hover reveal — on the trigger element: When hovered → Show an element (your panel), plus a second interaction When hover ends → Hide an element.\\n- Custom hamburger — on any button or icon: When clicked → Open/close a drawer (or a menu).\\nStart hidden To make a show-target start hidden, open its ⋮ menu — on its Hierarchy row or on the canvas — and choose Start hidden until an interaction shows it. The live site hides it from the very first paint, and so does the canvas, so you are designing what a visitor sees.\\nSelecting the element, or anything alongside it inside its container, opens it on the canvas for as long as it is selected, so you can work on it. That is a canvas-only preview: it is never saved and never changes what the published site does.\\nUnder the hood this is the aglyn-hidden class, which you will see on the element\\'' },\n { path: '/building-sites/menus-and-navigation/overview', title: 'Menus & navigation', heading: 'Responsive visibility', anchor: '#responsive-visibility', text: 'Styles → Visibility hides the selected element on whole device bands:\\n- Hide on mobile (under 600px)\\n- Hide on tablet (600–899px)\\n- Hide on desktop (900px and up)\\nUse it to hide the desktop link cluster on small screens and show a menu button instead. Bands are range-scoped, so hiding one never changes how the element displays on the others.\\nCanvas preview Pick a device in the artboard preview switcher and the canvas renders at that width — bands apply, the Mobile Nav swaps to its menu button, and breakpoint-scoped styles resolve for that device. In Fluid Responsive mode the canvas follows the real browser window instead. See responsive styling for how breakpoint-scoped styles behave on the canvas.' },\n { path: '/building-sites/menus-and-navigation/overview', title: 'Menus & navigation', heading: 'Related', anchor: '#related', text: '- Walkthrough: build a mega menu with hover interactions\\n- Interactions & custom HTML\\n- Responsive styling & custom CSS\\n- Screens & layouts' },\n { path: '/building-sites/multilingual/add-a-locale', title: 'Add a locale', heading: '', anchor: '', text: 'Add a locale\\nPublish your site in another language by adding a locale variant.\\nPlan availability Paid. This is multilingual v1.' },\n { path: '/building-sites/multilingual/add-a-locale', title: 'Add a locale', heading: 'Steps', anchor: '#steps', text: '1. Add a locale (e.g. es, fr) to your site.\\n2. Aglyn creates locale variants of your content, sharing the same structure as your default language.\\n3. Translate the screens\\' text for that locale.\\n4. Publish. Aglyn emits hreflang tags so search engines serve the right language to each visitor.' },\n { path: '/building-sites/multilingual/add-a-locale', title: 'Add a locale', heading: 'Tips', anchor: '#tips', text: '- Keep structure identical across locales — translate the words, not the layout.\\n- Add a language switcher so visitors can choose their language.' },\n { path: '/building-sites/multilingual/add-a-locale', title: 'Add a locale', heading: 'Related', anchor: '#related', text: '- Language switcher\\n- SEO toolkit' },\n { path: '/building-sites/multilingual/language-switcher', title: 'Add a language switcher', heading: '', anchor: '', text: 'Add a language switcher\\nOnce you\\'ve added a locale, give visitors a language switcher so they can pick their language.\\nPlan availability Paid.' },\n { path: '/building-sites/multilingual/language-switcher', title: 'Add a language switcher', heading: 'Steps', anchor: '#steps', text: '1. Add the language switcher to a screen or your shared layout (so it appears site-wide).\\n2. It lists your active locales and links each visitor to the matching variant of the current page.\\n3. Publish.' },\n { path: '/building-sites/multilingual/language-switcher', title: 'Add a language switcher', heading: 'Tips', anchor: '#tips', text: '- Put the switcher in your layout header/footer so it\\'s available on every page.\\n- Pair it with hreflang (emitted automatically) so both people and search engines land on the right language.' },\n { path: '/building-sites/multilingual/language-switcher', title: 'Add a language switcher', heading: 'Related', anchor: '#related', text: '- Add a locale' },\n { path: '/building-sites/multilingual/overview', title: 'Multilingual', heading: '', anchor: '', text: 'Multilingual\\nReach a wider audience by publishing your site in more than one language.\\nPlan availability Paid. Multilingual is available on paid tiers.' },\n { path: '/building-sites/multilingual/overview', title: 'Multilingual', heading: 'Locale variants', anchor: '#locale-variants', text: 'Create locale variants of your content so each language has its own version, while sharing the same structure.' },\n { path: '/building-sites/multilingual/overview', title: 'Multilingual', heading: 'hreflang & discovery', anchor: '#hreflang--discovery', text: 'Aglyn emits hreflang tags so search engines serve the right language to the right visitor, and the same set reaches social networks as og:locale — the language this page is in — plus one og:locale:alternate for each language it is translated into.\\nA language reaches both only once its variant is a published page: a locale you have added but not yet translated is never advertised as a translation that exists.' },\n { path: '/building-sites/multilingual/overview', title: 'Multilingual', heading: 'Language switcher', anchor: '#language-switcher', text: 'Add a language switcher so visitors can move between locales themselves.\\nHow-tos\\n- Add a locale\\n- Add a language switcher\\nThis is multilingual v1.' },\n { path: '/building-sites/multilingual/overview', title: 'Multilingual', heading: 'Related', anchor: '#related', text: '- SEO toolkit\\n- Screens & layouts' },\n { path: '/building-sites/redirects/create-a-redirect', title: 'Create a redirect', heading: '', anchor: '', text: 'Create a redirect\\nSend an old or alternate URL to the right screen.\\nPlan availability Paid.' },\n { path: '/building-sites/redirects/create-a-redirect', title: 'Create a redirect', heading: 'Add a rule', anchor: '#add-a-rule', text: '1. Open the redirect manager page.\\n2. Enter the source path (the URL to redirect) and the destination.\\n3. Save. The rule is validated on save, with chain-loop and screen-collision checks so it can\\'t loop or shadow a real screen.\\nThe published site enforces redirect rules during route resolution, so the redirect takes effect on the live site immediately.' },\n { path: '/building-sites/redirects/create-a-redirect', title: 'Create a redirect', heading: 'Read hit metrics', anchor: '#read-hit-metrics', text: 'Each rule tracks sampled hit metrics. Use them to:\\n- Confirm a redirect is actually being used.\\n- Find rules with zero hits that you can safely delete.' },\n { path: '/building-sites/redirects/create-a-redirect', title: 'Create a redirect', heading: 'Related', anchor: '#related', text: '- Migration patterns\\n- Redirects overview' },\n { path: '/building-sites/redirects/migration-patterns', title: 'Migration patterns', heading: '', anchor: '', text: 'Migration patterns\\nRedirects earn their keep during change. Here are the patterns you\\'ll reach for most.\\nPlan availability Paid.' },\n { path: '/building-sites/redirects/migration-patterns', title: 'Migration patterns', heading: 'Renamed a screen', anchor: '#renamed-a-screen', text: 'When you change a screen\\'s slug, add a redirect from the old path to the new one so existing links and search results keep working.' },\n { path: '/building-sites/redirects/migration-patterns', title: 'Migration patterns', heading: 'Consolidated pages', anchor: '#consolidated-pages', text: 'Merging two pages into one? Redirect the retired path to the survivor. The screen-collision check keeps you from redirecting a path that\\'s still a live screen.' },\n { path: '/building-sites/redirects/migration-patterns', title: 'Migration patterns', heading: 'Moved a site into Aglyn', anchor: '#moved-a-site-into-aglyn', text: 'Recreate your highest-traffic old URLs as redirects to their new Aglyn screens. Use the hit metrics to spot old URLs you missed — if a rule is getting traffic, people still rely on it.' },\n { path: '/building-sites/redirects/migration-patterns', title: 'Migration patterns', heading: 'Avoiding loops', anchor: '#avoiding-loops', text: 'The chain-loop check blocks redirects that would send visitors in circles (A→B→A). If a save is rejected, look for an existing rule that already redirects your destination.' },\n { path: '/building-sites/redirects/migration-patterns', title: 'Migration patterns', heading: 'Related', anchor: '#related', text: '- Create a redirect\\n- Screens & layouts' },\n { path: '/building-sites/redirects/overview', title: 'Redirects', heading: '', anchor: '', text: 'Redirects\\nThe redirect manager sends old or alternate URLs to the right place — essential when you rename screens or migrate a site.\\nPlan availability Paid. The redirect manager is a paid feature.' },\n { path: '/building-sites/redirects/overview', title: 'Redirects', heading: 'Manage redirects', anchor: '#manage-redirects', text: '- Create, edit, and delete redirect rules from the redirect manager page.\\n- Rules are validated on save, with chain-loop and screen-collision checks so a redirect can\\'t send visitors in circles or shadow a real screen.\\n- The published site enforces redirect rules during route resolution.\\nWho can manage redirects A redirect changes what the live site serves at an address, so it takes a publishing role — Editor or Admin. The Author role can see the redirects page but cannot add, change or remove a rule. See Teams & roles.' },\n { path: '/building-sites/redirects/overview', title: 'Redirects', heading: 'Sending visitors to another site', anchor: '#sending-visitors-to-another-site', text: 'A destination can be an internal path (/pricing) or an absolute https:// URL — useful for vanity domains and campaign links.\\nBecause an off-site destination sends your visitors somewhere you don\\'t control, the published site only follows one after a publishing role has saved the rule. A rule with an external destination that nobody has confirmed shows a not serving badge on the redirects page and is skipped until an Editor or Admin opens it and saves. Internal destinations are never affected.' },\n { path: '/building-sites/redirects/overview', title: 'Redirects', heading: 'Metrics', anchor: '#metrics', text: 'Each rule tracks hit metrics (sampled), so you can see which redirects are actually used and prune the ones that aren\\'t.' },\n { path: '/building-sites/redirects/overview', title: 'Redirects', heading: 'Match modes (v2)', anchor: '#match-modes-v2', text: 'Rules match one of three ways:\\n- Exact path — the original mode; /old-page only.\\n- Path prefix — /blog catches /blog and everything under it (/blog/post-1), but not /blogging.\\n- Regular expression — anchored to the whole path, with capture groups substituted into the destination: pattern /product/(\\\\d+) and destination /products/item-$1 sends /product/42 to /products/item-42. Invalid patterns are rejected at save and can never take the site down.\\nWhen several rules match, the lowest priority number fires first. Use the inline tester at the bottom of the redirects card to paste a path and see exactly which rule (if any) catches it — it runs the same matcher the live site uses.\\nHow-tos\\n- Create a redirect\\n- Migration patterns' },\n { path: '/building-sites/redirects/overview', title: 'Redirects', heading: 'Related', anchor: '#related', text: '- Screens & layouts\\n- Site protection & error pages' },\n { path: '/building-sites/screens-and-layouts/generate-a-page', title: 'Generate a page from a prompt', heading: '', anchor: '', text: 'Generate a page from a prompt\\nDescribe the page you want, and an AI build job builds it for the site you have open: a new screen in your layout, made from your theme and the components and forms your site already has. It arrives as an unpublished draft, and nothing on your live site changes until you publish it.\\nAvailability Aglyn AI build jobs are released gradually. This page describes page jobs as they reach your workspace.' },\n { path: '/building-sites/screens-and-layouts/generate-a-page', title: 'Generate a page from a prompt', heading: 'Describe the page', anchor: '#describe-the-page', text: 'Open Screens for the site and choose Describe it, beside Templates and Create New Screen. You can also start one from AI jobs in the Assist panel, with Describe a page.\\nWrite what the page is for in one box: who it is for, what it should say and what visitors should do next. You can also pick a page type (Landing, About, Pricing, Contact, Blog index, Product, Service or Event) to tell the job what kind of page it is. Then choose Plan the page.' },\n { path: '/building-sites/screens-and-layouts/generate-a-page', title: 'Generate a page from a prompt', heading: 'Review the plan', anchor: '#review-the-plan', text: 'The job proposes a plan before it builds anything: the layout the page renders in, the components and forms it places, anything it needs to create first, the page\\'s address and search title, and its sections from top to bottom. Open AI jobs in the Assist panel to read it, with what the whole job is estimated to use, then choose Confirm plan to build it, or cancel.\\nWhen the page needs something your site does not have yet, such as a layout, a reusable component for cards that repeat, or a form, the plan lists it, and confirming builds it first, in the same job, before the page that uses it. A plan only proposes what your workspace can create: what your plan includes and what your site has room for. Something a page job does not build, such as a page template or a dataset, is not offered for confirmation: the job stops before you confirm anything and tells you what to create first and where.\\nOn a plan without reusable components or saved forms, such as Free, the page is built from what that plan can make: a block that repeats, such as a row of service cards, is built into the page each time, in one section, and a form is part of the page, with its fields, collecting submissions into your inbox like any form.' },\n { path: '/building-sites/screens-and-layouts/generate-a-page', title: 'Generate a page from a prompt', heading: 'How the page is built', anchor: '#how-the-page-is-built', text: 'The job first builds anything the plan creates, one at a time: a layout, a form or a reusable component, each as a draft, the same way a layout, form or component job builds one. Then it builds the page one section at a time, top to bottom, in the background, so you can leave the Screens page while it works. Each section is checked against the building rules as part of the whole page: one top-level heading and headings in order, your theme\\'s colors, spacing and type, and your components placed as instances instead of copied.\\n- Pictures are image slots with a description of what belongs there, for you to fill from the media library.\\n- Facts the brief does not give, such as a price, a phone number or an address, appear in square brackets for you to fill in, instead of being invented. AI jobs lists them beside the draft, including the ones a component on the page shows until you set your own, named once for that component however often the page places it.' },\n { path: '/building-sites/screens-and-layouts/generate-a-page', title: 'Generate a page from a prompt', heading: 'The draft', anchor: '#the-draft', text: 'When the job is done, the page is a new screen in Screens, and Open draft on the job opens it in the Besigner.\\n- It is not live. The screen has no address on your site until you publish it, so it is not in your navigation, sitemap or site search, and visitors cannot reach it. Publish it from the Besigner like any other screen; see versions and publishing.\\n- It is ready to publish. It renders in the layout the plan chose, and it has an address, a search title and a search description written from the page\\'s own text.\\n- Navigation is yours to add. When the brief calls for a navigation entry, the job suggests its label and address. Add it to your menu once the page is live.\\n- What it created is a draft too. A layout, form or component the job built first is in Layouts, Forms or Components, and AI jobs links to each. The page renders in the new layout and places the new component and form; nothing else on your site uses them until you do.\\n- It counts like a screen you create. The draft counts against your plan\\'s screen allowance. When the site has no screen to spare, the job says so before it spends anything.' },\n { path: '/building-sites/screens-and-layouts/generate-a-page', title: 'Generate a page from a prompt', heading: 'What a page job uses', anchor: '#what-a-page-job-uses', text: 'Each step of a page job counts toward your AI usage, including a section the job had to ask for again. AI jobs shows what each job used. A page with more sections uses more.\\nOn Free, a page\\'s plan asks for no more sections than your monthly AI credits can always build, even when every step uses the most it can: nine sections, or six when the job also builds your site\\'s layout.' },\n { path: '/building-sites/screens-and-layouts/generate-a-page', title: 'Generate a page from a prompt', heading: 'Who can use it', anchor: '#who-can-use-it', text: 'Page jobs need the Generate with AI permission. See who can use AI assist.' },\n { path: '/building-sites/screens-and-layouts/generate-a-page', title: 'Generate a page from a prompt', heading: 'Related', anchor: '#related', text: '- How Aglyn AI builds\\n- Screens\\n- Layouts\\n- Versions and publishing' },\n { path: '/building-sites/screens-and-layouts/layouts', title: 'Layouts', heading: '', anchor: '', text: 'Layouts\\nA layout is the shared frame a screen renders inside. It is not a page of its own and never has a URL: it has a slot, and whatever screen you bind to it renders in that slot. For the pages themselves, see Screens.\\nPlan availability Free. Every plan can create layouts and nest them.' },\n { path: '/building-sites/screens-and-layouts/layouts', title: 'Layouts', heading: 'What a layout is', anchor: '#what-a-layout-is', text: 'A layout holds the furniture many screens have in common — header, navigation, footer, a cookie banner, a site-wide search box. Bind a screen to a layout in the Besigner and the layout chrome wraps the screen both in the editor and on the published site.\\nBecause the chrome lives in one document, changing it changes every screen bound to it at once. That is the point, and it is also the thing to keep in mind before you publish: see Versions & scheduled publishing.' },\n { path: '/building-sites/screens-and-layouts/layouts', title: 'Layouts', heading: 'Nested layouts', anchor: '#nested-layouts', text: 'A layout can render inside another layout. Set Renders inside on a layout\\'s detail page and its chrome is wrapped by the outer layout\\'s, exactly as a screen is wrapped by its own — so site-wide furniture can live in one place while a section keeps a more specific frame around it.\\nA screen inherits the whole chain: bind it to the inner layout and it renders inside that, which renders inside the outer one, up to five layouts deep.\\nA layout can never sit inside itself, or inside a layout already nested within it — that would be a loop with no outermost frame to render. The picker only offers layouts that are legal choices, so you cannot select one by mistake.' },\n { path: '/building-sites/screens-and-layouts/layouts', title: 'Layouts', heading: 'Layout properties', anchor: '#layout-properties', text: 'A layout can take properties, the way a reusable component does, so one frame can differ from screen to screen — a banner one screen shows and another hides, a call to action worded per page — without a second layout.\\n1. Open the layout in the Besigner and choose File ▸ Properties…\\n2. Add property. Every property type a reusable component offers is here, with its default, help, settings and condition set with the same controls.\\n3. Inside the layout, put the property\\'s token — {{prop.bannerText}} — in any text or string attribute, or bind a field to it with the field\\'s {} button: a switch to a Yes / no, an icon picker to an Icon. Hide when and Hide unless take a property too, so a screen can remove part of the layout\\'s chrome.\\nEach screen sets the values in Screen Properties, under Shared layout, below the Layout picker: one field per property, drawn with the control its type names. Leave a field empty and the layout\\'s default renders. Save layout values stores them on the screen version being edited, beside its layout binding, so publishing that version publishes its values with it.\\nValues are kept per layout. Binding a screen to a different layout starts from that layout\\'s defaults, and a screen inside nested layouts sets each layout\\'s properties separately.\\nProperties saved on the layout\\'s published version reach live screens straight away — that version is what they render. On any other version they go live when you publish it.\\nPages the site builds without a screen of their own — search results, author pages, and a collection with no template screen — render inside the site\\'s built-in page layout with its properties\\' defaults.' },\n { path: '/building-sites/screens-and-layouts/layouts', title: 'Layouts', heading: 'Duplicate', anchor: '#duplicate', text: 'Duplicate… in a layout\\'s row menu (and under More on its detail page) makes a second layout with the same element tree and properties as the latest saved version, named Copy of unless you type another name. No screen uses the copy until you assign it, so nothing on the live site changes. The copy counts against your shared-layout allowance like a new layout.' },\n { path: '/building-sites/screens-and-layouts/layouts', title: 'Layouts', heading: 'Generate a layout with Aglyn AI', anchor: '#generate-a-layout-with-aglyn-ai', text: 'An AI build job can make a layout from a brief: the header, navigation and footer you have in mind. The job proposes a plan first, and builds once you confirm it: a header that links your screens, the slot each page renders in, and a footer. When your site already has a navigation or menu component, the layout places it rather than building another. The layout follows the building rules, so it carries no page title of its own and takes its colors and type from your theme.\\nOpen Layouts for the site and choose Describe it, beside Templates and Create New Layout. Write what the header, navigation and footer should hold, then choose Plan the layout, and open AI jobs in the Assist panel to read the plan and confirm it.\\nA link in the layout goes only to a screen that does what its words say, and one on a colored band, such as a footer in your primary color, uses the band\\'s text color. Where the brief leaves out a fact, such as your address or opening hours, the footer marks it in square brackets, and AI jobs lists those gaps beside the draft.\\nThe layout arrives as a new draft in Layouts, and Open draft on the job opens it in the Besigner. No screen uses it until you assign it, so nothing on the live site changes. It counts against your shared-layout allowance like a layout you create yourself, and when the site has none to spare the job says so before it starts, not after it has spent anything.' },\n { path: '/building-sites/screens-and-layouts/layouts', title: 'Layouts', heading: 'Used by', anchor: '#used-by', text: 'A layout\\'s detail page has a Used by card listing everything that renders inside it, so you can see what a change or a deletion would reach:\\n- screens bound to it, published or not, and\\n- layouts nested inside it — deleting the outer one unwraps every screen underneath those too.\\nA layout used by neither is genuinely unused.' },\n { path: '/building-sites/screens-and-layouts/layouts', title: 'Layouts', heading: 'Layouts vs. reusable components', anchor: '#layouts-vs-reusable-components', text: 'Both let you build something once and use it in many places, and they are not interchangeable:\\n| | Layout | Reusable component |\\n| What it is | A frame with a slot | A subtree you insert as an instance |\\n| How a screen uses it | Binds to it; the layout wraps the screen | Inserts one or more instances anywhere in its own tree |\\n| How many per screen | One chain, outermost first | As many instances as you like |\\n| Where properties are set | Screen Properties, per screen | The Attributes panel, per instance |\\n| Good for | Header, nav, footer, site chrome | A card, a call-to-action, a pricing block |\\nIf the thing wraps your content, it is a layout. If the thing is content you want to repeat, it is a reusable component.' },\n { path: '/building-sites/screens-and-layouts/layouts', title: 'Layouts', heading: 'Related', anchor: '#related', text: '- Screens\\n- Versions & scheduled publishing\\n- Reusable components\\n- Menus & navigation' },\n { path: '/building-sites/screens-and-layouts/overview', title: 'Screens & Layouts', heading: '', anchor: '', text: 'Screens & Layouts\\nScreens are your pages. Layouts are the shared frames those pages render inside. Reusable components are the repeatable pieces you drop into either. Templates are saved starting points you can build from. Each one has its own page — start with the question you have.' },\n { path: '/building-sites/screens-and-layouts/overview', title: 'Screens & Layouts', heading: 'Which one do you want?', anchor: '#which-one-do-you-want', text: '| You want to… | Go to |\\n| Add a page, change its URL, or check your screen allowance | Screens |\\n| Put a header, nav or footer around many pages at once | Layouts |\\n| Build a card or block once and repeat it | Reusable components |\\n| Start from a saved page, layout or whole site | Your templates library |\\n| Publish, roll back, or schedule a version | Versions & scheduled publishing |\\nPlan availability Free for core screens and layouts. Higher tiers raise caps on screens, versions, and reusable components.' },\n { path: '/building-sites/screens-and-layouts/overview', title: 'Screens & Layouts', heading: 'Related', anchor: '#related', text: '- The Besigner\\n- Bindings & variables\\n- Site protection & error pages\\n- SEO toolkit' },\n { path: '/building-sites/screens-and-layouts/screens', title: 'Screens', heading: '', anchor: '', text: 'Screens\\nA screen is a page of your site. It has a title, a URL slug, and a place in a hierarchy that decides the address visitors reach it at.\\nThe screen hierarchy maps directly to your URL paths:\\nPlan availability Free for core screens. Higher tiers raise the cap on how many screens a site can publish.' },\n { path: '/building-sites/screens-and-layouts/screens', title: 'Screens', heading: 'Screens & routing', anchor: '#screens--routing', text: '- Each screen has a title and a URL slug. Aglyn normalizes slugs and keeps a site routing map.\\n- Screens form a hierarchy: pick a parent and the child inherits a nested path (/services/pricing). Changing a slug or parent cascades safe rewrites across the map, with cycle guards so you can\\'t create a loop.\\n- Reorder the hierarchy with drag-and-drop in the screens list.\\n- The screens list shows each screen\\'s Published date, and the screen\\'s detail page shows Date published. Both stay empty until the screen goes live, and clear again if you unpublish it (including a scheduled unpublish) — so the column tells you what\\'s live, not what exists. A screen you\\'ve just created is a draft: it holds its slug but nothing resolves there until you publish it.\\n- Unpublish takes a live screen off your site. It\\'s in the row\\'s ⋮ menu on the screens list (on published screens), on the screen\\'s detail page under Publishing, and on the Besigner toolbar. The screen, its versions and its slug are all kept, so publishing again puts it back at the same address.' },\n { path: '/building-sites/screens-and-layouts/screens', title: 'Screens', heading: 'What counts against your screen allowance', anchor: '#what-counts-against-your-screen-allowance', text: 'Your plan\\'s screen limit counts the screens that are pages of your site — the ones a visitor can reach at an address of their own. Some screens you design are not pages, and those don\\'t count:\\n| Screen | Counts? | Why |\\n| A page you publish at a slug | Yes | It has a URL of its own. |\\n| A collection\\'s list template | Yes | /{collection} renders that exact screen. |\\n| A collection\\'s entry template | No | One screen composes every entry; it has no address of its own. |\\n| An error screen you\\'ve assigned | No | It renders on addresses that matched nothing. |\\n| An email you design | No | It\\'s sent, never served at a URL. |\\n| A screen you\\'ve deleted | No | Deleting frees the slot straight away. |\\nThe rule behind the table is one question: does the screen occupy a URL of its own? So an error screen that is also still published at its own address — say a 404 screen you published at /404 while designing it — is a page, and it counts until you remove that address. The Error pages card tells you when that\\'s the case and offers the one-click Remove address; the screen carries on rendering for its status code afterwards.' },\n { path: '/building-sites/screens-and-layouts/screens', title: 'Screens', heading: 'Duplicate a screen', anchor: '#duplicate-a-screen', text: 'Every row on the Screens list has Duplicate… in its menu. The copy takes the latest saved version whole — the element tree, layout values, description and SEO fields — under the name you give it (Copy of Home by default; a name another screen already has gets a number). Its slug becomes home-copy, then home-copy-2, so it never claims the original\\'s address.\\nThe copy is a draft: it has no address on the live site until you publish it, exactly like a screen you created from scratch. Components and layouts placed on the original are shared, not copied — the duplicate points at the same ones. A duplicate of a page counts against your screen allowance the moment it is made, and is refused with the same message a new screen would be when the allowance is spent.\\nA collection\\'s entry template can be duplicated too, which is how a second collection starts from the first one\\'s design. The copy is still an entry template, with every {{entry.}} binding kept: it isn\\'t published, and no collection uses it until you pick it as a collection\\'s Entry screen in Content. Like the original, it doesn\\'t count against your screen allowance. An error screen can\\'t be duplicated.' },\n { path: '/building-sites/screens-and-layouts/screens', title: 'Screens', heading: 'Error & maintenance screens', anchor: '#error--maintenance-screens', text: 'You can design custom 404 / 401 / 403 / 503 screens and turn on maintenance mode. See Site protection & error pages.' },\n { path: '/building-sites/screens-and-layouts/screens', title: 'Screens', heading: 'Related', anchor: '#related', text: '- Layouts — the shared frame a screen renders inside\\n- Versions & scheduled publishing\\n- The Besigner\\n- Bindings & variables\\n- SEO toolkit' },\n { path: '/building-sites/screens-and-layouts/versions-and-publishing', title: 'Versions & scheduled publishing', heading: '', anchor: '', text: 'Versions & scheduled publishing\\nScreens, layouts, and reusable components all keep versions. A version is a named snapshot of the saved document; exactly one version of each is published — the one your visitors get.' },\n { path: '/building-sites/screens-and-layouts/versions-and-publishing', title: 'Versions & scheduled publishing', heading: 'The versions dialog', anchor: '#the-versions-dialog', text: 'Open the version name in the Besigner\\'s app bar to see the Versions dialog, with each version\\'s created/updated times and a Published chip on the live one.\\n- New version saves a named snapshot of the current saved document (prefilled \"Copy of …\" — rename it to something meaningful).\\n- Open switches the Besigner to that version. Viewing an old version never publishes it.\\n- Delete versions you no longer need — the published version can\\'t be deleted, and the one you have open must be closed first.' },\n { path: '/building-sites/screens-and-layouts/versions-and-publishing', title: 'Versions & scheduled publishing', heading: 'Publish & roll back', anchor: '#publish--roll-back', text: 'Publish on any row makes that version the live one — which is also how you roll back: publishing an older version moves the live pointer, destroys nothing, and is symmetrical, so rolling forward again is the same click.\\nA save is not a publish. Saving updates the version you have open; publishing is what changes the live site. Publishing a layout version reaches every screen bound to that layout, so check its Used by card first — see Layouts. A reusable component version reaches every page that places it the same way.' },\n { path: '/building-sites/screens-and-layouts/versions-and-publishing', title: 'Versions & scheduled publishing', heading: 'Scheduled publishing', anchor: '#scheduled-publishing', text: 'Schedule publishes a version automatically at a chosen future time; the row then shows a \"Publishes …\" chip you can clear to cancel. Screens and layouts can be scheduled; a reusable component cannot, so its Schedule is disabled — publish that version when it is ready.\\nIf a scheduled publish comes due on a plan that no longer includes scheduling — after a downgrade, say — it is skipped and shown as skipped on the screen\\'s page, never silently dropped, so you can dismiss it or upgrade and reschedule.' },\n { path: '/building-sites/screens-and-layouts/versions-and-publishing', title: 'Versions & scheduled publishing', heading: 'Plan requirements', anchor: '#plan-requirements', text: 'Gating is enforced where you click:\\n- Creating versions requires Pro or above — on a lower tier the editor answers \"Versioning requires a Pro plan — see Billing to upgrade\" instead of opening the name dialog.\\n- Scheduled publishing requires Business or above.\\n- A version is snapshotted from the saved document, so the editor asks you to save the canvas first rather than silently capturing (or losing) unsaved edits.' },\n { path: '/building-sites/screens-and-layouts/versions-and-publishing', title: 'Versions & scheduled publishing', heading: 'Related', anchor: '#related', text: '- Screens\\n- Layouts\\n- Reusable components\\n- Live co-editing' },\n { path: '/building-sites/seo/overview', title: 'SEO Toolkit', heading: '', anchor: '', text: 'SEO Toolkit\\nThe SEO toolkit helps your pages rank and share well. Set metadata per screen, and Aglyn emits the right tags, sitemap, and structured data automatically.\\nPlan availability Free for core SEO fields; richer structured data ships for published sites.' },\n { path: '/building-sites/seo/overview', title: 'SEO Toolkit', heading: 'Per-screen SEO', anchor: '#per-screen-seo', text: 'Every screen\\'s detail page has an SEO card with four fields:\\n- Search title — up to 60 characters, published exactly as you type it.\\n- Search description — up to 155 characters, the meta description.\\n- Breadcrumb label — up to 40 characters, the page\\'s short name in a breadcrumb trail.\\n- Social image — the picture shown when the page is shared. Press Choose image to pick one from your media library (or the organization\\'s shared library); Clear puts the site default back.\\nFill them in and press Save SEO — it saves independently of the canvas, so you can update metadata without touching the design. The published site emits these into the page head, deduping descriptions so you never get conflicting tags.\\nThe social image is a media pick, not a URL you type. That is deliberate: a picked asset is stored by identity, so moving it into a folder or replacing it with a new version keeps every card that references it working.\\nAnything you leave blank falls back sensibly: the description falls back to the screen\\'s own description and then the site\\'s, and the social image to the site-wide one.\\nHow a page title is built\\nA search title you write is the whole title — nothing is appended to it. That is what lets you keep every page inside the 60 characters a search result shows, and say the brand once rather than twice.\\nA page with no search title of its own is titled from its name — the screen\\'s display name, a blog post\\'s headline, a collection\\'s name — joined to the site Title with your Separator:\\n| Page | Rendered title |\\n| Search title About Aglyn — one platform for the open web | About Aglyn — one platform for the open web |\\n| Screen named Contact, no search title | Contact – Acme Widgets |\\n| Neither | Acme Widgets |\\nYour brand still travels with every share regardless: og:siten' },\n { path: '/building-sites/seo/overview', title: 'SEO Toolkit', heading: 'Search engine visibility', anchor: '#search-engine-visibility', text: 'You decide what search engines are allowed to index, at two levels.\\nThe whole site\\nSetup → SEO → Search engines has a Discourage search engines from indexing this site switch. Turn it on while you stage a launch — the site stays live and anyone with a link can still reach it, but:\\n- robots.txt refuses every crawler (Disallow: /) and names no sitemap\\n- sitemap.xml is served empty (valid but with no URLs, so search consoles read \"nothing here\" rather than an error they retry)\\n- every page carries a noindex robots meta tag — links are still followed, but the page is not listed\\nWhile it is on, a warning banner follows you through the console on every page of that site. That is deliberate — a site missing from Google weeks after launch is almost always this switch, left on and forgotten.\\nTurning it off restores everything within about a minute. Search engines take longer: re-indexing a site is their schedule, not yours, so allow days.\\nrobots.txt and noindex answer different questions. Disallow asks a crawler not to fetch a page; noindex asks it not to list one. Aglyn sends both, because a page linked from somewhere else can be listed without ever being fetched — and a crawler that obeys the disallow never sees the noindex that would have stopped it.\\nA single page\\nUse the page\\'s own Visibility, in Page Access on the screen\\'s detail page. Only Public pages are offered to search engines. Unlisted, Password protected and Members only pages are all kept out of search results and out of the sitemap, while staying reachable to whoever has the link or the credentials.\\nUse the site-wide switch while nothing is ready, and per-screen Unlisted once you\\'re launching page by page — the switch also covers pages you haven\\'t created yet, which per-screen visibility cannot.\\nnoindex is a reque' },\n { path: '/building-sites/seo/overview', title: 'SEO Toolkit', heading: 'Sitemap & robots', anchor: '#sitemap--robots', text: 'Aglyn generates sitemap.xml and robots.txt for your site automatically, so search engines can crawl your site correctly. The sitemap always names your site by its real address (your custom domain when you have one), and includes:\\n- every published screen whose visibility is Public — template screens (blog list/ entry templates, product page templates) are excluded, since their real URLs are the entries and products they render;\\n- your product and catalog collection URLs, once a product-page or collection-page template is set;\\n- your content collections — each collection\\'s list URL and its published entries. A scheduled entry joins the sitemap once its publish time passes, and a collection with nothing published yet is left out until its first entry is.\\nOne index, one file per section\\nsitemap.xml is a sitemap index: instead of listing your URLs directly, it names a child sitemap for each part of your site, and each child holds that part\\'s URLs.\\nSubmit sitemap.xml and nothing else — every search engine follows an index to its children on its own. You never write these child URLs yourself, and the index adds a collection\\'s file with its first published entry and drops it once nothing is published.\\nThe split is what keeps a growing site correct. A single sitemap may hold at most 50,000 URLs, and anything past that is not submitted at all — silently. A section that outgrows one file simply continues into a second (/sitemaps/content-blog/2.xml), and the index names both.\\nEvery URL with a known date carries a last-modified date (lastmod) — the day a screen was last published, the day an entry, product or catalog collection last changed, and for a listing the day of its newest entry — so a crawler can skip what has not moved since its last visit. A URL whose date is not known s' },\n { path: '/building-sites/seo/overview', title: 'SEO Toolkit', heading: 'Social cards', anchor: '#social-cards', text: 'Every published page emits Open Graph and Twitter metadata: title, description, canonical URL, site name, the page\\'s language, and — once an image is set — og:image with its og:image:width, og:image:height and og:image:alt.\\nWhat each kind of page emits\\nSome properties are the same on every page of your site; others describe the one page you are looking at. Both sets are listed here so you can check a page against them.\\nOn every page, whatever kind it is:\\n| Property | Where it comes from |\\n| og:title, og:description | The page\\'s search title and description |\\n| og:url | The page\\'s canonical address, always absolute |\\n| og:sitename | Setup → SEO Title, then the Entity name, then the site\\'s name |\\n| og:locale | The page\\'s language — the screen\\'s, then the site\\'s default |\\n| og:image + width, height, alt | The winning card image, below |\\n| twitter:card, title, description, image | The same values as the card above |\\n| twitter:site | The X profile in Setup → Details → Social links |\\nAnd per kind of page:\\n| Page | og:type | What it adds |\\n| Home, and any other screen | website | og:locale:alternate for each language this page is translated into |\\n| A collection listing, and a category listing | website | — |\\n| A blog or collection entry | article | article:publishedtime, article:modifiedtime, article:author (their author page), article:section (its category), article:tag, and twitter:creator |\\n| An author page | profile | profile:username and twitter:creator, from the author\\'s own X profile |\\nAnything with nothing behind it is left out rather than filled in. A site that has set no name at all publishes no og:sitename; an entry that was never published publishes no article:publishedtime; an author with no X profile is still shown on a card, just without a byline credit on it. ' },\n { path: '/building-sites/seo/overview', title: 'SEO Toolkit', heading: 'Structured data', anchor: '#structured-data', text: 'Published sites emit JSON-LD, giving search engines and AI assistants rich context about your content.\\nEvery page carries two site-wide nodes:\\n- a top-level Organization (or Person) describing who publishes the site — the entity an assistant reads when someone asks who you are or how to reach you. It is filled in from Setup → SEO → Entity, and it falls back to your site name and description, so a site that has never touched that form still publishes a named, described entity;\\n- a WebSite naming the site and its address, with the language it is published in (inLanguage).\\nAnd per kind of page:\\n| Page | What it publishes |\\n| A collection listing, and a category listing | ItemList of the entries on that page, in their real positions |\\n| A blog or collection entry | The article type its collection declares — BlogPosting, NewsArticle, Article — with its headline, cover, dates, byline, section and language |\\n| An author page | ProfilePage wrapping the author as a Person or Organization |\\n| A product page | Product with its Offer, availability and — where you have reviews — an AggregateRating |\\n| Any page more than one level deep | BreadcrumbList naming the trail to it |\\nYour social links, from Setup → Details → Business details, are published as the entity\\'s sameAs — the property that tells a search engine your site, your LinkedIn page and your X account are all the same organization. You do not have to retype them anywhere; the list the site already renders is the list it publishes.\\nTwo fields are worth adding by hand, because nothing can guess them:\\n- Contact email / phone, published as contactPoint;\\n- Address, published as a PostalAddress. Partial is fine — a city and a country are still a real answer.' },\n { path: '/building-sites/seo/overview', title: 'SEO Toolkit', heading: 'AI agents', anchor: '#ai-agents', text: 'Agents read a site differently from a browser. Aglyn publishes four things for them, on every site, with nothing to switch on.\\nMarkdown for any page\\nEvery page serves a Markdown version of itself with the navigation, styling and scripts removed. Two ways to ask:\\nBoth return the same document: the page title, its summary, the content region, and a Source: line carrying the canonical URL. Site chrome is left out — that is the point. The HTML page links to its own Markdown with , so an agent reading the HTML can find it.\\nA request that accepts neither HTML nor Markdown — Accept: application/pdf, say — is answered 406 Not Acceptable with a plain-text list of what IS available. Ordinary browser requests are never affected.\\nThe search page is the one exception: its results are computed in the browser, so there is no Markdown version to serve and it stays HTML.\\n/llms.txt\\nA short guide at https://your-site/llms.txt telling an agent what the site is for and which addresses answer what — your collections that have published entries and their entry counts, search, the sitemap, the API description, and a contact route when you publish one.\\nYou can lead it with your own words. Setup → SEO → AI agents has two boxes:\\n- When to use this site — the questions you are the best source for. Be specific; an agent discounts a claim it cannot check, and generic marketing copy does not read as guidance.\\n- How an agent should call you — anything worth knowing before it fetches.\\nBoth are optional. The rest of the file is derived from what your site actually publishes, so it is never empty and never out of date.\\n/openapi.json\\nA machine-readable OpenAPI 3.1 description of everything your site serves, generated per site so it always names your own domain. Every operation carries a unique operationId' },\n { path: '/building-sites/seo/overview', title: 'SEO Toolkit', heading: 'Analytics integration', anchor: '#analytics-integration', text: 'Add your Google Analytics ID to track traffic alongside Aglyn\\'s built-in analytics.' },\n { path: '/building-sites/seo/overview', title: 'SEO Toolkit', heading: 'Related', anchor: '#related', text: '- Analytics\\n- Content collections & blog' },\n { path: '/building-sites/seo/seo-by-ai', title: 'AI SEO for your website', heading: '', anchor: '', text: 'AI SEO for your website\\nAI can write the search listing for a page or a product, audit your whole site, and draft the structured data and agent guidance your site publishes. Everything it does is a proposal: it lands in the editor or the form as unsaved changes, or in a new draft version, and your live site changes only when you save or publish.\\nSEO by AI is part of AI generation. Each proposal uses AI credits, and every card shows how many a request used.' },\n { path: '/building-sites/seo/seo-by-ai', title: 'AI SEO for your website', heading: 'Write a page\\'s listing', anchor: '#write-a-pages-listing', text: 'On a screen\\'s detail page, the SEO card has a Write with AI section:\\n1. Optionally add target keywords, separated by commas.\\n2. Press Write SEO.\\n3. Review the proposal. Each value shows its length against the field\\'s limit.\\n4. Press Put in the fields, check the fields, and press Save SEO.\\nThe proposal covers every field of the card:\\n| Field | Limit | Written from |\\n| Title | 60 characters | What the page is about, specifically. Published exactly as written. |\\n| Description | 155 characters | A one- or two-sentence summary of the page. |\\n| Breadcrumb label | 40 characters | The page\\'s short name in a breadcrumb trail. |\\n| Image description | 300 characters | Only when the page has a social image, and only from what the page says about the picture. |\\nThe listing is written from the version you are looking at, in the language of its text. It never invents a fact, a price or a claim the page does not make, and it does not reuse a title another page of your site already uses.' },\n { path: '/building-sites/seo/seo-by-ai', title: 'AI SEO for your website', heading: 'Write a product\\'s listing', anchor: '#write-a-products-listing', text: 'In the product editor, the Search engine listing section has the same Write with AI control. It proposes an SEO title and an SEO description from the product\\'s name and description. Put them in the fields, then press Save product. Give the product a name first.' },\n { path: '/building-sites/seo/seo-by-ai', title: 'AI SEO for your website', heading: 'Audit the whole site', anchor: '#audit-the-whole-site', text: 'Setup → SEO has an SEO audit card. Press Run audit; it runs in the background, and you can leave the page while it works.\\nThe audit covers the pages your sitemap lists: every published page whose visibility is Public, without template screens or error pages. A very large site is audited on its first 150 pages, and the card says how many it left out. For each page it checks:\\n| Check | What counts as a finding |\\n| Search title | Missing, over 60 characters, or the same as another page\\'s. |\\n| Search description | Missing, over 155 characters, or the same as another page\\'s. |\\n| Main heading | No h1, more than one, or one that says little (Home, Welcome, a single short word). |\\n| Images | An image with no description. |\\n| Links | No other page or shared layout links to the page. The home page never counts. |\\n| Target keywords | A keyword you named that the page never says. |\\nIt also checks the site as a whole: whether search engines are asked to stay away, whether your structured data names and describes who publishes the site, and whether your /llms.txt carries guidance of your own.\\nEach page gets a score, and each finding gets a proposed fix. A page nothing links to gets advice instead of a fix, because only you can choose where the link belongs.\\nTarget keywords\\nThe optional Target keywords by page box takes one line per page:\\nThe audit reports where each page already says its keywords. A proposed title or description uses a keyword only where the page is about it — at most once in a title and once in a description — and never lists keywords or repeats a word to rank.' },\n { path: '/building-sites/seo/seo-by-ai', title: 'AI SEO for your website', heading: 'Apply all as drafts', anchor: '#apply-all-as-drafts', text: 'When the audit finishes, Apply all as drafts does three things, and none of them changes your live site:\\n- Content fixes open a new draft version of each page that has one: a description for each undescribed image, and one clear main heading. Your published version is not touched. Open the draft from the card, review it, and publish it when you are ready.\\n- Titles and descriptions wait in each page\\'s SEO card. Open the page from the card, press Put in the fields under Proposed by the site audit, and save.\\n- Structured data and agent guidance go into the SEO form below the card as unsaved changes. Review them and press Update.\\nApplying the same audit again opens no second draft of a page.' },\n { path: '/building-sites/seo/seo-by-ai', title: 'AI SEO for your website', heading: 'Structured data and /llms.txt', anchor: '#structured-data-and-llmstxt', text: 'The audit proposes the parts of Setup → SEO → Entity and AI agents that are still empty: whether an organization or a person publishes the site, a one-sentence description of the publisher, and guidance on when an AI agent should use your site. A contact email or phone number is proposed only when one of your pages shows it.\\nPreview /llms.txt shows the file as it will read once you save the guidance. The file your site serves changes only after you press Update in the form.' },\n { path: '/building-sites/seo/seo-by-ai', title: 'AI SEO for your website', heading: 'Related', anchor: '#related', text: '- SEO Toolkit\\n- AI assist' },\n { path: '/building-sites/site-protection/error-screens', title: 'Design custom error screens', heading: '', anchor: '', text: 'Design custom error screens\\nWhen something goes wrong, visitors should still see your site. Design custom error screens for each status.' },\n { path: '/building-sites/site-protection/error-screens', title: 'Design custom error screens', heading: 'The error screens', anchor: '#the-error-screens', text: '| Status | Picker label | When it shows |\\n| 404 | 404 · Not found | The URL doesn\\'t match any screen. |\\n| 401 | 401 · Members only | A members-only screen, requested by a signed-out visitor. |\\n| 403 | 403 · Forbidden | Reserved for future access rules — nothing serves it yet. |\\n| 503 | 503 · Maintenance | Shown on every page while maintenance mode is on. |' },\n { path: '/building-sites/site-protection/error-screens', title: 'Design custom error screens', heading: 'Design one', anchor: '#design-one', text: '1. Create a screen for the status, in Screens, and design it in the Besigner like any other screen — add your header/layout, a helpful message, and a link home.\\n2. Assign it. Go to Admin → Error pages. Each status has its own picker — 404 · Not found, 401 · Members only, 403 · Forbidden, 503 · Maintenance — listing every screen on the site. Choose your screen in the matching picker. The Admin area is limited to site admins, so that is who assigns these.\\nStep 2 is the one that\\'s easy to miss: designing a screen does not make it an error page on its own. Until it\\'s picked in the Error pages card, the status renders the Built-in fallback page, which is what every picker is set to until you change it.' },\n { path: '/building-sites/site-protection/error-screens', title: 'Design custom error screens', heading: 'What the built-in fallback gives you', anchor: '#what-the-built-in-fallback-gives-you', text: 'The fallback is not a blank error page. It renders under your site\\'s theme and carries your logo, links to your site\\'s public top-level pages, a search box (on the 404) and a footer — so a visitor who lands on a missing address can always get somewhere.\\nWhat it cannot do is show the header, navigation and footer you designed: those live in the screen you built them on, and on a 404 that screen is precisely the one that wasn\\'t found. Its page links come from your published pages, and only the public ones — an unlisted, password-protected or members-only page never appears there.\\nAssigning a designed screen is still the better answer for any site that cares how this moment looks. The fallback is the floor beneath it.\\nAssigned error screens are automatically kept out of search results, so a 404 page can\\'t itself turn up in a search. A missing address also answers with a real 404 status and a server-rendered page title — your 404 screen\\'s SEO title when you\\'ve set one, otherwise Page not found followed by your site\\'s title — so the browser tab and search engines know what the page is before any script runs.' },\n { path: '/building-sites/site-protection/error-screens', title: 'Design custom error screens', heading: 'Error screens are free', anchor: '#error-screens-are-free', text: 'An assigned error screen doesn\\'t count against your plan\\'s screen allowance. Nothing about it is a page of your site: it renders on addresses that matched nothing, so it has no URL of its own — the same reason a collection\\'s entry template doesn\\'t count. See what counts against your screen allowance.\\nThere\\'s one thing to know, and it\\'s the reason you don\\'t need to publish the screen at all:\\nDon\\'t give it an address An error screen that is also published at its own address — a 404 screen you published at /404 so you could preview it, say — is a page, so it counts like any other page until you remove that address.\\nIf you\\'ve already published one, the Error pages card says so and offers Remove address. Removing it frees the allowance slot and the screen carries on rendering for its status code, which is the only place it was ever meant to appear.' },\n { path: '/building-sites/site-protection/error-screens', title: 'Design custom error screens', heading: 'Tips', anchor: '#tips', text: '- Put a search box or navigation on your 404 so visitors can recover.\\n- Keep the 503 lightweight — it shows when the site is under maintenance.' },\n { path: '/building-sites/site-protection/error-screens', title: 'Design custom error screens', heading: 'Related', anchor: '#related', text: '- Maintenance mode\\n- Password-protect a screen' },\n { path: '/building-sites/site-protection/maintenance-mode', title: 'Maintenance mode', heading: '', anchor: '', text: 'Maintenance mode\\nNeed to pause your live site while you make changes? Maintenance mode serves your designed 503 screen to visitors until you switch it back.' },\n { path: '/building-sites/site-protection/maintenance-mode', title: 'Maintenance mode', heading: 'Turn it on', anchor: '#turn-it-on', text: '1. Open Admin → Error pages for the site — the same card that assigns your error screens. The switch takes every page of the site offline for every visitor, so the Admin area is limited to site admins.\\n2. Switch on Maintenance mode — show the 503 screen on every page. It confirms with \"Maintenance mode on — visitors see the 503 screen\".\\n3. Visitors now see your 503 error screen on every path.\\n4. When you\\'re done, switch it back off and the site returns.\\nIf you haven\\'t assigned a 503 · Maintenance screen, maintenance mode still works — but visitors get Aglyn\\'s plain built-in notice (\"This site is undergoing maintenance. Please check back shortly.\") rather than anything on-brand. Design and assign the 503 first.' },\n { path: '/building-sites/site-protection/maintenance-mode', title: 'Maintenance mode', heading: 'Tips', anchor: '#tips', text: '- Design the 503 screen first (see custom error screens) so maintenance mode shows something on-brand.\\n- Tell visitors when you\\'ll be back — a simple line on the 503 goes a long way.' },\n { path: '/building-sites/site-protection/maintenance-mode', title: 'Maintenance mode', heading: 'Related', anchor: '#related', text: '- Design custom error screens' },\n { path: '/building-sites/site-protection/overview', title: 'Site Protection & Error Pages', heading: '', anchor: '', text: 'Site Protection & Error Pages\\nControl who can see your pages and what visitors see when something\\'s not available.\\nPlan availability Included on every plan. Per-screen visibility, page passwords, custom error screens and maintenance mode are not tier-gated.' },\n { path: '/building-sites/site-protection/overview', title: 'Site Protection & Error Pages', heading: 'Where these controls live', anchor: '#where-these-controls-live', text: 'The three features on this page are set in two different places, and neither is the screens list:\\n| What you want | Where it is |\\n| A screen\\'s visibility (public, unlisted, password, members-only) and its page password | The Page Access card on that screen\\'s own page |\\n| Error screens for 404 / 401 / 403 / 503 | The Error pages section of the site\\'s Admin area |\\n| Maintenance mode | The same Error pages section, in Admin |\\nA screen\\'s own visibility is set by anyone who can edit that screen. The Error pages section is different: both settings there are site-wide — maintenance mode replaces every page for every visitor — so, like deleting the site, they live in Admin and are limited to site admins.' },\n { path: '/building-sites/site-protection/overview', title: 'Site Protection & Error Pages', heading: 'Per-screen passwords', anchor: '#per-screen-passwords', text: 'Protect individual screens with a password, so only people with the code can view them. You can also set a custom not-found screen for missing pages.' },\n { path: '/building-sites/site-protection/overview', title: 'Site Protection & Error Pages', heading: 'Custom error screens', anchor: '#custom-error-screens', text: 'Design your own 404 / 401 / 403 / 503 screens in the Besigner, so error pages match your brand instead of showing a generic message.' },\n { path: '/building-sites/site-protection/overview', title: 'Site Protection & Error Pages', heading: 'Maintenance mode', anchor: '#maintenance-mode', text: 'Flip your site into maintenance mode to show a designed 503 while you work, then switch it back when you\\'re ready.\\nHow-tos\\n- Password-protect a screen\\n- Design custom error screens\\n- Maintenance mode' },\n { path: '/building-sites/site-protection/overview', title: 'Site Protection & Error Pages', heading: 'Related', anchor: '#related', text: '- Screens & layouts\\n- Redirects' },\n { path: '/building-sites/site-protection/password-a-screen', title: 'Password-protect a screen', heading: '', anchor: '', text: 'Password-protect a screen\\nLock a single screen behind a password — useful for drafts, client previews, or gated content, without setting up full membership.' },\n { path: '/building-sites/site-protection/password-a-screen', title: 'Password-protect a screen', heading: 'Steps', anchor: '#steps', text: '1. Open Screens and click the screen you want to protect. The screens list has no visibility column — the control is on the screen\\'s own page.\\n2. Find the Page Access card and set Visibility to Password protected.\\n3. A Page password field appears. Type the password and click Save. Once one is set, the field reads \"A password is set — save a new one to change it.\"\\n4. Publish. Visitors are prompted for the password before they can view the screen.\\nThe other visibility options on the same card are Public (anyone with the link, listed in navigation and offered to search engines), Unlisted (reachable by URL only, kept out of search results and the sitemap), and Members only. Password-protected and unlisted screens are both kept out of search results.\\nPassword attempts are limited to 10 per minute per screen, per visitor address, so the password can\\'t be guessed by brute force. Someone who trips it is asked to wait a moment and try again. The limit counts per address, so a shared office connection tripping it won\\'t lock out anyone else — but if a whole team is unlocking the same screen at once, expect the occasional retry prompt.' },\n { path: '/building-sites/site-protection/password-a-screen', title: 'Password-protect a screen', heading: 'Password vs. members-only', anchor: '#password-vs-members-only', text: '| Password | Members-only |\\n| One shared secret | Individual accounts |\\n| No sign-up needed | Visitors sign in / sign up |\\n| Best for one-off gating | Best for ongoing member areas |\\nFor account-based access, use a members-only area instead.' },\n { path: '/building-sites/site-protection/password-a-screen', title: 'Password-protect a screen', heading: 'Related', anchor: '#related', text: '- Custom error screens\\n- Members-only areas' },\n { path: '/building-sites/site-search/add-search', title: 'Add search to your site', heading: '', anchor: '', text: 'Add search to your site\\nAdding search is a matter of placing one element. The search page and matcher are built in.\\nPlan availability Included on every plan.' },\n { path: '/building-sites/site-search/add-search', title: 'Add search to your site', heading: 'Steps', anchor: '#steps', text: '1. In the Besigner, open the elements drawer and add the Search Box (under Forms) to a screen — or to your shared layout so it appears site-wide.\\n2. Optionally set the box\\'s Placeholder text in the Attributes panel.\\n3. Publish. Submitting the box sends visitors to your site\\'s /search page with their query.\\nThat\\'s the whole setup — there\\'s no search setting to switch on and no index to build.' },\n { path: '/building-sites/site-search/add-search', title: 'Add search to your site', heading: 'Tips', anchor: '#tips', text: '- Put the Search Box in your header layout so it\\'s reachable from every page.\\n- Search covers dataset records and blog entries, so well-structured content and clear titles make results more useful.\\n- Dataset records only surface when a published screen repeats over that dataset — build a listing screen for anything you want to be findable.' },\n { path: '/building-sites/site-search/add-search', title: 'Add search to your site', heading: 'Related', anchor: '#related', text: '- Site search overview\\n- Datasets & dynamic content' },\n { path: '/building-sites/site-search/overview', title: 'Site Search', heading: '', anchor: '', text: 'Site Search\\nGive visitors a Search Box to find content across your site. Aglyn ships a built-in search page — there\\'s nothing to configure, enable, or index.\\nPlan availability Included on every plan, free tiers too.' },\n { path: '/building-sites/site-search/overview', title: 'Site Search', heading: 'How it works', anchor: '#how-it-works', text: 'The Search Box element is a simple form: a visitor types a query and submits it to your site\\'s built-in /search page, which renders the matching results. There\\'s no JavaScript and no external search service — the page reads your published content directly when the visitor searches.' },\n { path: '/building-sites/site-search/overview', title: 'Site Search', heading: 'What it searches', anchor: '#what-it-searches', text: 'A search matches the query as a case-insensitive substring across three kinds of content:\\n- Screens — the screen\\'s name, description, and SEO title and description.\\n- Blog & collection entries — published entries\\' title, excerpt, and body (see collections).\\n- Dataset records — a record\\'s dataset values. A record only appears in results when a published screen repeats over that dataset, so the result can link to a real page.\\nResults are listed in that order (screens, then entries, then records), up to 50 matches. There\\'s no relevance ranking — matching is a straight substring test — so clear titles and descriptions make results more useful. The /search results page is excluded from search engines, and search is a reserved screen slug.' },\n { path: '/building-sites/site-search/overview', title: 'Site Search', heading: 'The layout built-in pages use', anchor: '#the-layout-built-in-pages-use', text: 'The results page is built for you, but the header, navigation and footer around it are yours. It renders inside one of your layouts, so a visitor who lands on /search gets the same site around them as on every other page.\\nWhich layout is a setting: Setup → Built-in page layout. Left unset it follows the layout your home page uses, which is what most sites want and why there is nothing to do here on a site with a single layout. Pick a different one when your home page has a layout of its own — a transparent header meant to sit over a hero image looks wrong above a list of results.\\nThe same setting covers the other page Aglyn builds for you: a blog entry on a collection with no entry template, so both built-in pages of a site cannot end up in different chrome.' },\n { path: '/building-sites/site-search/overview', title: 'Site Search', heading: 'Configure it', anchor: '#configure-it', text: 'The Search Box exposes two settings — its Placeholder text and Starting text, which prefills the field (the results page uses it to keep the query you searched for in the box). There are no scope, result-limit, or styling options; the box inherits your site theme.\\nHow-tos\\n- Add search to your site' },\n { path: '/building-sites/site-search/overview', title: 'Site Search', heading: 'Related', anchor: '#related', text: '- Datasets & dynamic content\\n- SEO toolkit' },\n { path: '/building-sites/site-templates/build-a-blog', title: 'Build a blog', heading: '', anchor: '', text: 'Build a blog\\nAglyn\\'s blog is built on collections — a collection holds your entries, and its pages are first-class designed pages: the list at /{collection} and each entry at /{collection}/{entry} render through your site\\'s theme and shared layout, and can be fully designed in the besigner via template screens.\\nPlan availability Free to start; content features scale with your tier.' },\n { path: '/building-sites/site-templates/build-a-blog', title: 'Build a blog', heading: '1. Create a collection', anchor: '#1-create-a-collection', text: 'In Content, create a collection for your posts. Manage entries from the console.\\nA new collection is not a page yet. Its listing at /{collection} goes live with the first entry you publish (or a scheduled one, once its time comes), and so do its paged and category listings and its RSS feed. Until then those addresses answer 404 and your sitemap and /llms.txt leave the collection out, so an empty section never shows up on your site or in search. Unpublish or delete every entry and the listing comes down the same way.\\nDelete a collection\\nDelete collection is at the bottom of Collection settings, the panel under the Collection dropdown in Collections & Entries, and acts on the collection currently picked in that dropdown. It is a site admin action: if your role on this site is editor or viewer the button is not there at all, and an org role on its own does not grant it — the check reads your role on this site. Ask a site admin, or have one raise your role in Members.\\nAglyn refuses the delete while anything still depends on the collection, and the dialog tells you which of these it is before you can type anything:\\n- A template screen still points at it. The message names the screen and which picker holds it — \"Blog\" is still the source for \"Blog index\" (list template). Set that collection\\'s List screen or Entry screen, under Template screens, back to the built-in option first. Deleting it while a published page renders from it would leave that page with nothing to draw.\\n- It still has entries. The message gives the count — \"Blog\" still has 12 entries. Delete them from the entries table first, one at a time; deleting a collection never removes published entries for you.\\nThe dialog also names the published screens, layouts and components that link to the collection\\'s listing ' },\n { path: '/building-sites/site-templates/build-a-blog', title: 'Build a blog', heading: '2. Write entries', anchor: '#2-write-entries', text: 'Add rich blog entries with images, a live preview, and scheduling so posts publish at the right time.\\nNew entry, or clicking any row in the entries table, opens that entry on its own page. The page has its own address, so you can bookmark an entry you\\'re mid-way through or paste the link to a colleague and they land on the same one. Back to entries — or your browser\\'s Back button — returns to the list; if the entry has edits you haven\\'t saved, you\\'re asked before they\\'re dropped.\\nThe table opens with the most recently Published entries first. Click the Title, Status, Updated or Published header to sort by that column, and again to reverse it. Entries with nothing in the sorted column, such as a draft with no published date, follow the rest in either direction rather than dropping out. Filters in the table\\'s toolbar narrows the list to one Status or one Category across the whole collection, not only the page you\\'re on.\\nEach entry carries, besides the title, excerpt, cover image, and markdown body:\\n- Category — a single bucket (e.g. Guides) used for filtering and related posts, picked from the collection\\'s category list (see below), never typed free-form.\\n- Tags — comma-separated labels (e.g. nextjs, seo).\\n- Author — the entry\\'s byline: an author record from the Authors tab, a one-off custom byline, or the site\\'s own publisher entity when both are blank. See Authors.\\n- SEO title / SEO description — search & social overrides; they fall back to the title and excerpt when blank.\\n- Featured video — a film for the entry\\'s page: one from your media library, a video link, or a Wistia link. It plays where the cover image would sit. See Build a video collection.\\nScheduling\\nThe same three publication controls live in two places, so you can decide when a post goes live without leavi' },\n { path: '/building-sites/site-templates/build-a-blog', title: 'Build a blog', heading: '3. Design the pages with template screens', anchor: '#3-design-the-pages-with-template-screens', text: 'Each collection has two template pickers in Content:\\n- List template screen — renders /{collection}. Drop the Collection Entries block on it: its children repeat once per published entry, with {{entry.}} tokens substituted per entry. The default card ships title, date, excerpt, and a Read more link, so dropping it in works instantly.\\n- Entry template screen — renders /{collection}/{entry}. Use {{entry.}} bindings and the Entry Body block, which renders the entry\\'s markdown as themed headings, paragraphs, lists, links, and images.\\nTemplate screens go through the normal published pipeline — site theme, shared layout, reusable components, variables — exactly like any other screen (the same mechanism as commerce product/collection templates).\\nA second collection whose entries should look like the first one\\'s starts from a copy: choose Duplicate… on the first collection\\'s entry template in Screens, then pick the copy as the new collection\\'s Entry screen, under Template screens. See Duplicate a screen.\\nBlog blocks\\nBesides Collection Entries and Entry Body, three entry-page blocks are available in the block library:\\n- Entry Meta — an author · date · category line plus tag chips, with an optional round avatar in front of it. On an entry template it fills itself in from the entry being rendered, so drop it on and it works; each part can be hidden with its Show switch. Typing into Author / Date / Category / Tags overrides what it would have shown — including the {{entry.author}} / {{entry.date}} / {{entry.category}} / {{entry.tags}} bindings, which still work and are still what the block\\'s preset seeds. Date format picks how the published date reads — Site default (8/9/2026), Month and year (Aug 2026), Short date, Long date or ISO. It applies to the date the block fills in for it' },\n { path: '/building-sites/site-templates/build-a-blog', title: 'Build a blog', heading: '4. Publish & syndicate', anchor: '#4-publish--syndicate', text: 'There is no publish step for the collection itself: publishing its first entry is what takes it live (see Create a collection). From then on its pages join the site\\'s sitemap automatically, and each entry\\'s uses its SEO title/description (falling back to title/excerpt) and its cover image as the social card.\\nAglyn also generates an RSS feed per collection, live from its first published entry, at:\\ne.g. https://acme.com/blog/rss.xml. This is the URL to link — it names no site, so it keeps working if you connect a custom domain later, and it is the one to put behind a \"Subscribe\" link.\\nThe same feed answers on the explicit form, if you need to point at another site\\'s:\\nhost accepts your site\\'s platform origin (acme.aglyn.app), your custom domain (acme.com), or the bare subdomain (acme), and collection is the collection\\'s slug.\\nItems carry the entry\\'s title, link, publish date, excerpt, and its category and tags as feed categories, newest first. Link it from your own footer or share it with aggregators. Feed readers also find it on their own: every collection page — the list, each paginated page, each category, and each entry — declares the feed in its page head, so pasting your blog\\'s own address into a reader subscribes it to the feed.' },\n { path: '/building-sites/site-templates/build-a-blog', title: 'Build a blog', heading: 'Build a video collection', anchor: '#video-collection', text: 'Give every film a page of its own: a collection whose entries are articles with the film where a post\\'s cover image would sit. Search engines list a video from a page that is about that video, and an entry page is exactly that.\\nGive each entry its film. In the entry editor, Featured video takes a film from your media library, a video link, or a Wistia link. Picking a library film that has a captured frame also fills an empty Cover image with that frame; a cover you already chose is kept.\\nPlace the player on the entry template. Add a Video element where the cover would go and set its fields to the entry\\'s tokens, typed or picked with the field\\'s {x} button under Entry:\\n| Video field | Token |\\n| Video source | {{entry.coverVideo}} |\\n| Poster image | {{entry.coverImage}} |\\n| Video title | {{entry.title}} |\\n| Video description | {{entry.excerpt}} |\\n| Publication date | {{entry.publishedAt}} |\\nUse {{entry.publishedAt}} for the date, not {{entry.date}}: the date token is written for readers, like 8/9/2026, which a search engine cannot read as a date. See the fields a search result reads.\\nKeep list cards to a picture. In the list template\\'s Collection Entries card, show each film as an Image bound to {{entry.coverImage}}. Don\\'t put a Video element in a card: a list with a player in every card is one page holding many videos, not a page about any one of them.\\nNo entry template? The built-in entry page plays the featured video with no setup, in the cover\\'s place, with the cover as its poster. A Wistia film needs a cover image to show as its poster; without one, the page shows the article without the player.' },\n { path: '/building-sites/site-templates/build-a-blog', title: 'Build a blog', heading: 'Tips', anchor: '#tips', text: '- Schedule entries ahead of time and let Aglyn publish them for you.\\n- Pair the blog with the SEO toolkit — entries emit JSON-LD for rich results.' },\n { path: '/building-sites/site-templates/build-a-blog', title: 'Build a blog', heading: 'Related', anchor: '#related', text: '- Save a template\\n- Datasets & dynamic content\\n- SEO toolkit' },\n { path: '/building-sites/site-templates/overview', title: 'Templates, Blocks & Content', heading: '', anchor: '', text: 'Templates, Blocks & Content\\nBuild faster by starting from something. Aglyn gives you site templates, a section & block library, and a blog with content collections.\\nPlan availability Free to use the starter gallery and block library. Some templates and content features scale with your tier.' },\n { path: '/building-sites/site-templates/overview', title: 'Templates, Blocks & Content', heading: 'Site templates & starter gallery', anchor: '#site-templates--starter-gallery', text: '- Spin up a new site from the starter template gallery, which instantiates published screens for you. Starters are copied into your site\\'s own template library, so you can edit and version them like any other template.\\n- Save a site as a template from Setup → Site template, which publishes it to the marketplace — see save & share a template. Browsing and installing marketplace templates happens in the Start from a template picker, opened by the Templates button on the Screens, Layouts and Components lists.' },\n { path: '/building-sites/site-templates/overview', title: 'Templates, Blocks & Content', heading: 'Section & block library', anchor: '#section--block-library', text: 'Drop in pre-built sections and blocks from the library instead of assembling every page from primitives — a fast way to build common layouts.\\nThe library is a first-class group in the Besigner: open INSERT → New Element (or the components drawer) and the Sections & Blocks category sits at the top of the list, ahead of the primitive element groups. Every entry inserts a ready-made subtree with sensible props — drop it in, then edit text and styles in place.\\nThe library ships these sections:\\n| Block | What it inserts |\\n| Nav Bar | App bar + toolbar with brand text and three screen links, in page flow (switch its Position attribute to Sticky for a pinned header) |\\n| Hero | Centered headline, tagline, and call-to-action button |\\n| Feature Grid | Three selling-point columns that wrap on small screens |\\n| Image + Text | Image beside a heading, paragraph, and link button |\\n| Testimonials | Three customer quote cards |\\n| Pricing Table | Three plan columns with features and plan buttons |\\n| FAQ | Question-and-answer list with a heading |\\n| Call to Action | Accent-colored band with a heading and button |\\n| Contact Section | Heading plus a working name/email/message form |\\n| Announcement Bar | Accent strip with a message and link |\\n| Image Gallery | Three-across image row |\\n| Footer | Semantic with brand, screen links, social icons, and copyright |\\nBlocks are ordinary elements once inserted — nothing is locked. Screen links resolve against your site\\'s screens (pick targets in the link\\'s attributes), the contact form submits like any form, and social icons appear once you add profile URLs.' },\n { path: '/building-sites/site-templates/overview', title: 'Templates, Blocks & Content', heading: 'Content collections & blog', anchor: '#content-collections--blog', text: '- Create collections managed in the console.\\n- Publish a blog with rich entries (images, preview, scheduling) and an RSS feed.\\n- Use entry-template screens with {{entry.}} bindings to render each collection item.\\nHow-tos\\n- Build a blog\\n- Save & share a template' },\n { path: '/building-sites/site-templates/overview', title: 'Templates, Blocks & Content', heading: 'Related', anchor: '#related', text: '- The Besigner\\n- Datasets & dynamic content\\n- SEO toolkit' },\n { path: '/building-sites/site-templates/save-a-template', title: 'Save & share a template', heading: '', anchor: '', text: 'Save & share a template\\nTurn a site you\\'ve built into a template others can start from — or start your own site from the marketplace gallery.\\nPlan availability Free to use the starter gallery; sharing scales with your tier.' },\n { path: '/building-sites/site-templates/save-a-template', title: 'Save & share a template', heading: 'Start from a template', anchor: '#start-from-a-template', text: 'Open the starter template gallery and install a template. It lands in your templates library as one page template per page — publishing nothing until you choose which pages you want. Aglyn\\'s own starter templates still create their pages straight away, since you pick those when a site is empty.\\nWhere to find it\\nTemplates sits next to the create button on three lists, and each one offers templates of its own kind:\\n| List | What the picker offers |\\n| Screens | Page templates, plus Aglyn\\'s starter sites and marketplace templates |\\n| Layouts | Layout templates |\\n| Components | Component templates |\\nStarter sites and marketplace templates are whole-site page bundles, so they appear only on Screens — a layout or component picker would have nothing to do with them.\\nUse the filter icon in the dialog\\'s header to search by name, description or category.' },\n { path: '/building-sites/site-templates/save-a-template', title: 'Save & share a template', heading: 'Save your site as a template', anchor: '#save-your-site-as-a-template', text: 'Saving a whole site is not done from the screens list — it lives in Setup.\\n1. Open Setup for the site and find the Site template card, under the basic details.\\n2. Click Publish as template.\\n3. Fill in Template name, Description, Category (e.g. Portfolio, Restaurant) and Price (USD, 0 = free), then publish.\\nThis captures every published screen plus the theme, and publishes it to the marketplace — so it is a share, not a private snapshot. Re-publishing bumps the version. Add a preview image afterwards from Manage → Marketplace.\\nSaving a single page instead\\nTo reuse one page rather than a whole site, use the ⋮ row menu on the Screens list and choose Save as template. Layouts and Components have the same row action. These save into your own templates library and are not published anywhere until you publish them separately.' },\n { path: '/building-sites/site-templates/save-a-template', title: 'Save & share a template', heading: 'Tips', anchor: '#tips', text: '- Templates are a fast way to reuse a proven layout across client sites.\\n- Combine with the section & block library for page-level reuse without a whole template.' },\n { path: '/building-sites/site-templates/save-a-template', title: 'Save & share a template', heading: 'Related', anchor: '#related', text: '- Build a blog\\n- Templates, blocks & content' },\n { path: '/building-sites/site-templates/templates-library', title: 'Your templates library', heading: '', anchor: '', text: 'Your templates library\\nTemplates are saved starting points. A template is inert: nothing in your library is on your live site until you deliberately use it. That is the whole point of the library — it means installing something from the marketplace can never publish pages to a site you are running.\\nFind it at Templates in a site\\'s navigation, alongside Screens, Layouts and Components — the three things a template can produce.\\nA template is a copy, not a live link: once you use one, the result is yours outright and editing the template never changes what you already made. If you want one edit to update every place a thing appears, you want a reusable component. If you want one frame around many pages, you want a layout.' },\n { path: '/building-sites/site-templates/templates-library', title: 'Your templates library', heading: 'The three kinds', anchor: '#the-three-kinds', text: '| Kind | What it holds | What you get from it |\\n| Page | A screen\\'s content | A new screen, unpublished |\\n| Component | An element tree | A reusable component, or a drop onto a screen |\\n| Layout | Page chrome — header, footer, navigation | A new shared layout |' },\n { path: '/building-sites/site-templates/templates-library', title: 'Your templates library', heading: 'Installing from the marketplace', anchor: '#installing-from-the-marketplace', text: 'Installing a marketplace template adds it to your library and publishes nothing. A site template arrives as one page template per page it contains, so you choose which of them you actually want, and when.\\nThis is deliberate: browsing and installing should never be able to change a site that real people are looking at. If the template was designed around a particular theme, that theme travels with it rather than being applied to your site.\\nA marketplace component or layout brings the properties it declares — each one\\'s kind, label, help text, answers, settings and condition — so its {{prop.}} tokens still have properties to fill them, and updating to a newer version takes the publisher\\'s new properties with it. A property\\'s default is checked before it reaches your site the same way the published design is: a link, an image address, or formatted text that could run script is removed, and the property arrives with no default instead. The property itself always comes through, so its field still shows wherever you use the component or layout. A link or image the design takes from a Link or Image property arrives still bound to it, so each page that places the component sets its own address. A condition rule whose pattern could not be matched on your pages is removed on the way in, and the rest of the condition comes through.' },\n { path: '/building-sites/site-templates/templates-library', title: 'Your templates library', heading: 'Saving something as a template', anchor: '#saving-something-as-a-template', text: 'Look for Save as template:\\n- Screens list — saves the page\\'s published content\\n- Layouts list — saves the layout, including its content slot\\n- Components list — saves the component definition\\nA page template captures the published version of a screen. If a screen has never been published there is nothing to capture, and Aglyn will tell you to publish it first rather than saving an empty template that fails later.\\nThe original is never touched. Saving a template copies it.' },\n { path: '/building-sites/site-templates/templates-library', title: 'Your templates library', heading: 'Using a template', anchor: '#using-a-template', text: 'Use creates something new from the template — the template itself stays in your library, so you can use it as many times as you like.\\n- A page template asks for a name and an address, then creates the page and publishes it. If the address is already taken, Aglyn adds a number rather than overwriting the page that\\'s there.\\n- Component and layout templates just create the component or layout; there is no address to pick. The properties the component or layout declared come with it, so every {{prop.}} token in it still has a property to fill it.\\nIf a template defines placeholders, you\\'ll be asked to fill them in first — the values are substituted into the content as it\\'s created. A template might use {{who}} in its copy and ask you for \"Who\". A component or layout property\\'s {{prop.}} token is never a placeholder: it stays in the content, bound to its property.\\nNothing you create is linked back to the template afterwards. Editing a page will never change the template, and updating a template will never change pages you already made.' },\n { path: '/building-sites/site-templates/templates-library', title: 'Your templates library', heading: 'Generate a page template with Aglyn AI', anchor: '#generate-a-page-template-with-aglyn-ai', text: 'An AI build job can make a page template for the pages your site builds from records it already keeps: each entry of a content collection (a blog post, an event, a case study), each product, or each author. Everything that changes from one record to the next is bound with the tokens the Besigner\\'s insert picker offers for that page, such as {{entry.title}} in the heading and {{entry.coverImage}} in the cover image, and the blocks that fill themselves on that page, such as Entry Meta and Entry Body, are placed where they belong. The job proposes a plan first and builds once you confirm it, following the building rules.\\nOpen Templates for the site and choose Describe it, beside Create Template. Write what each page should show, and under One page for each pick what the site draws a page for: Collection entry, then the collection whose entries it shows, Product or Author. Then choose Plan the template, and open AI jobs in the Assist panel to read the plan and confirm it.\\nThe template lands in your library as Saved here, like one you saved yourself. It is bound to no collection, changes nothing on your site, and counts against your template allowance. To put it to work, Use it to create the page, then pick that page right away as the collection\\'s Entry template screen in Content, the product page template in Store settings, or the author page screen on the Authors tab. Until you pick it, the new page shows its tokens at its own address; once picked, it stops serving there and renders each record instead.' },\n { path: '/building-sites/site-templates/templates-library', title: 'Your templates library', heading: 'Where a template came from', anchor: '#where-a-template-came-from', text: 'Every template shows a badge:\\n- Saved here — you created it on this site\\n- Marketplace — installed from a marketplace listing, with its version\\n- Starter — one of Aglyn\\'s first-party starter templates\\nThat badge is set by Aglyn, not by whoever made the template, so a listing cannot claim to be something it is not.' },\n { path: '/building-sites/site-templates/templates-library', title: 'Your templates library', heading: 'First-party starters', anchor: '#first-party-starters', text: 'Aglyn\\'s starter sites (Landing Page, Business, Portfolio, the two Shop starters) are ordinary templates in your library — not a separate, locked-down kind. Every site gets its own copy, so you can open a starter in the besigner, edit it, keep versions of it, and use it to create pages exactly like a template you saved yourself. They carry the Starter badge, and they don\\'t count against your plan\\'s template allowance.\\nA multi-page starter arrives as one template per page (the Shop starters are five), and Start from a template still shows them grouped as one card that creates all of the pages at once.\\nYour Templates list groups them the same way: one row per starter, labeled with its page count, and its actions act on all of those pages — Use creates every page, and Delete removes every page. Open the row to see the pages individually; each one is an ordinary template you can edit, use or delete on its own from there.\\nBecause your copy is yours, editing a starter is safe: Aglyn will not overwrite it later. Delete one you don\\'t want and it stays deleted.' },\n { path: '/building-sites/site-templates/templates-library', title: 'Your templates library', heading: 'Templates are per-site', anchor: '#templates-are-per-site', text: 'A template lives on the site you saved it to. That keeps a template next to the screens, layouts and components it was built from.\\nMarketplace plugins and add-ons work differently: those install once for the whole organization and apply to every site, and a site can override the organization\\'s choice for itself.' },\n { path: '/building-sites/site-templates/templates-library', title: 'Your templates library', heading: 'Duplicating', anchor: '#duplicating', text: 'Duplicate… in a template\\'s row menu makes a second template with the same element tree, placeholders, properties and SEO fields, named Copy of unless you type another name. A copy of a marketplace or starter template is your own — it is listed as Saved here and counts against your template allowance like one you saved yourself. Starter bundles are used, not duplicated: use the bundle to get its pages.' },\n { path: '/building-sites/site-templates/templates-library', title: 'Your templates library', heading: 'Deleting', anchor: '#deleting', text: 'Deleting a template removes it from your library. Anything you already created from it is unaffected — a page made from a template is a normal page, with no ongoing link back to it.' },\n { path: '/building-sites/site-templates/templates-library', title: 'Your templates library', heading: 'Related', anchor: '#related', text: '- Save & share a template — publishing a whole site as a template for other organizations to install' },\n { path: '/building-sites/theme-builder/edit-your-theme', title: 'Edit your theme', heading: '', anchor: '', text: 'Edit your theme\\nYour theme controls how the whole site looks — applied consistently across every screen and previewed live as you edit.\\nPlan availability Free.' },\n { path: '/building-sites/theme-builder/edit-your-theme', title: 'Edit your theme', heading: 'Open the editor', anchor: '#open-the-editor', text: 'Go to Setup → Theme editor. Changes render in a live preview so you see them immediately.' },\n { path: '/building-sites/theme-builder/edit-your-theme', title: 'Edit your theme', heading: 'Set colors and fonts', anchor: '#set-colors-and-fonts', text: '- Choose your palette and typography.\\n- Fonts load through a Google Fonts URL builder.\\n- Configure both light and dark schemes. Published sites follow the visitor\\'s system scheme (or their choice in the theme mode switcher); anything you leave unset under Dark comes from the platform\\'s default dark palette, so a site goes dark without a dark design of its own.\\n- Dark scheme — set it to Off when your content only reads well in light: every visitor stays on light and the theme mode switcher is hidden on published pages.' },\n { path: '/building-sites/theme-builder/edit-your-theme', title: 'Edit your theme', heading: 'It follows you into the Besigner', anchor: '#it-follows-you-into-the-besigner', text: 'The theme you set here is supplied to the Besigner canvas, so what you design previews under the real site theme in both light and dark.\\nYour palette also powers the Besigner\\'s color pickers: every color field offers your theme\\'s colors as references first (Primary, Background, Surface, Text…), each swatch previewing its light and dark resolutions. Elements colored by reference re-color automatically when you adjust the theme — or when the visitor\\'s scheme flips. See scheme-scoped colors.' },\n { path: '/building-sites/theme-builder/edit-your-theme', title: 'Edit your theme', heading: 'Change it with AI', anchor: '#change-it-with-ai', text: 'Rolling out. The Theme assistant above the editor proposes changes to these same controls from a description — \"warmer\", \"match our brand\", \"bigger headings on mobile\" — with a before and after preview. A proposal goes into the editor as unsaved changes, and nothing is saved until you save it. See Change your site\\'s theme with AI.' },\n { path: '/building-sites/theme-builder/edit-your-theme', title: 'Edit your theme', heading: 'Tips', anchor: '#tips', text: '- Set both schemes — a site that only looks right in light mode breaks for dark-mode visitors. Until it does, switch Dark scheme off rather than shipping unreadable pages.\\n- Prefer theme color references over fixed hex values when styling elements; references adapt per scheme, fixed colors don\\'t (though the Besigner can scope custom colors per scheme too).\\n- The theme also styles screen previews and published pages, so there\\'s one source of truth.' },\n { path: '/building-sites/theme-builder/edit-your-theme', title: 'Edit your theme', heading: 'Related', anchor: '#related', text: '- The Besigner\\n- Screens & layouts' },\n { path: '/building-sites/theme-builder/overview', title: 'Theme Builder', heading: '', anchor: '', text: 'Theme Builder\\nThe theme builder controls how your whole site looks — colors, fonts, and light/dark schemes — applied consistently across every screen.\\nPlan availability Free. Theming is core to every site.' },\n { path: '/building-sites/theme-builder/overview', title: 'Theme Builder', heading: 'Edit your theme', anchor: '#edit-your-theme', text: '- Open the theme editor under Setup.\\n- Set your palette and typography; changes render in a live preview.\\n- Configure both light and dark schemes — published sites apply the visitor\\'s scheme automatically.\\n- Fonts load via a Google Fonts URL builder.\\nThe theme you set here is supplied to the Besigner canvas too, so previews match the live site.\\nHow-tos\\n- Edit your theme' },\n { path: '/building-sites/theme-builder/overview', title: 'Theme Builder', heading: 'Related', anchor: '#related', text: '- The Besigner\\n- Screens & layouts' },\n { path: '/commerce-and-bookings/bookings/overview', title: 'Bookings & Scheduling', heading: '', anchor: '', text: 'Bookings & Scheduling\\nBookings turn your site into a scheduling tool: define services, publish your availability, and let visitors book time — with optional payment and automatic reminders.\\nPlan availability Paid. Paid bookings use Stripe; reminder emails are included.\\nPaid services need Stripe connected first A paid booking is a destination charge into your own Stripe account, so the booking widget cannot take money until Stripe Connect onboarding is finished and Stripe reports charges enabled. Until then a visitor who picks a paid service is told \"Payments are not set up yet\" and the slot is not held. Free services are unaffected — they never touch Stripe. Connect your account from the Payments card on the Products hub; see Commerce.' },\n { path: '/commerce-and-bookings/bookings/overview', title: 'Bookings & Scheduling', heading: 'Set up bookings', anchor: '#set-up-bookings', text: '1. Define services (what can be booked, duration, price).\\n2. Configure availability — the windows when slots are offered.\\n3. Add the booking widget to a screen as a canvas element.' },\n { path: '/commerce-and-bookings/bookings/overview', title: 'Bookings & Scheduling', heading: 'Taking bookings', anchor: '#taking-bookings', text: '- Visitors pick a slot through the booking widget; the booking API records it.\\n- For paid services, Stripe collects payment and a slot hold prevents double-booking during checkout.\\n- Reminder emails go out automatically ahead of the appointment — see below.\\n24-hour reminders {#reminders}\\nEvery confirmed booking gets one reminder email roughly a day before it starts. A background pass runs each hour and mails the bookings that are then 23–25 hours out, so a reminder lands about 24 hours ahead rather than at an exact minute.\\nA booking is reminded once. The pass records that it has sent, so a booking is never mailed twice, and these do not go out for canceled bookings or for bookings taken without an email address.\\nThe reminder uses your designed booking reminder email template if you have one, and your own brand if your plan includes white-labeling; otherwise it sends a plain text reminder naming the service and the time.\\nWhere to see it. The Upcoming bookings card shows 24-hour reminders · N due in the next pass · N already sent, counted with the same rule the sender uses — so it is the queue that will actually be drained, not an estimate.' },\n { path: '/commerce-and-bookings/bookings/overview', title: 'Bookings & Scheduling', heading: 'Payments and fees', anchor: '#payments-and-fees', text: 'Money for a paid booking goes to you, not to Aglyn. The charge is created on your connected Stripe account, and Aglyn takes its platform fee out of it as the Stripe Connect application fee — the same way a storefront sale works.\\n- A booking is priced as a service, which bills at your plan\\'s digital transaction rate: 5% on Starter, 3% on Pro, 2% on Business, 1% on Scale, and 0% from Advanced up. It is the same rate and the same ladder as digital products — bookings are not charged separately or additionally.\\n- The fee is taken on the service price only. Stripe\\'s own processing fee is separate and comes out of your account as usual.\\n- Your current rate is shown on the Payments card of the Products hub, and the full ladder is on the plans page.\\nService tax\\nPaid bookings charge no tax by default, and that stays true for every site that does not change it.\\nA service is not goods: the sales-tax rate configured for your store is a goods rate, and whether a service is taxable is a different question with frequently the opposite answer. So Aglyn does not apply your store\\'s sales rate to an appointment. Instead, Commerce → Settings → Taxes → Service tax is where you set your own rate for it.\\nWhen you set one, Aglyn adds it to the booking charge as its own receipt line using the label you choose, and records the amount and the regime on the booking. It is always your own rate — Stripe Tax is never asked to compute it, because it has no service tax code for this and would apply a goods rate to an appointment.\\nThe platform fee is charged on the service price, never on the tax.\\nAglyn does not provide tax advice Aglyn applies the rate you enter and records what was charged. It does not determine whether service tax applies to you, at what rate, or where it should be paid. Confirm your' },\n { path: '/commerce-and-bookings/bookings/overview', title: 'Bookings & Scheduling', heading: 'Manage', anchor: '#manage', text: 'Use the console bookings page to see and manage upcoming appointments.\\nEach upcoming booking carries View in CRM, which opens the booker\\'s contact — the CRM\\'s Contacts list asked for their address, moving straight on to the record when exactly one person matches. A booking taken without an address has no link. A contact\\'s timeline links back to this page from the booking that captured them.\\nBooking from the CRM {#booking-from-the-crm}\\nA contact, a lead or a deal in the CRM carries Book a meeting whenever this site runs Bookings and your plan includes it. It lists the site\\'s services, each with the public link a visitor books it at — the page the booking widget sits on, with that service preselected — and a copy button. From Send email on a record, Insert booking link drops the chosen link into the message where the caret is.\\nEvery link dropped from a record carries that record with it, so the booking lands on the right timeline even when the person books with a different address. A booking taken without a record reference is matched to a contact by the booker\\'s address instead. Links are per service, not per person: a service has one calendar, so there is nothing narrower to link to.\\nEach service has two switches in its dialog:\\n- Log a meeting on the CRM record when this service is booked (on by default) — a confirmed booking is filed as a meeting on the record\\'s timeline, titled with the service and the slot.\\n- Create a follow-up task on the CRM record when this service is booked (off by default) — a Follow up after &lt;service&gt; task is filed on the record, due one business day after the slot, on whoever holds the relationship.\\nNeither files anything on a site where the CRM plugin is switched off, and a booking is filed once however many times its payment confirmati' },\n { path: '/commerce-and-bookings/bookings/overview', title: 'Bookings & Scheduling', heading: 'Related', anchor: '#related', text: '- Commerce\\n- CRM activities & the timeline\\n- Events calendar\\n- Email campaigns' },\n { path: '/commerce-and-bookings/commerce/catalog', title: 'Product catalog', heading: '', anchor: '', text: 'Product catalog\\nThe catalog (AGL-276) is the foundation of Aglyn commerce: every storefront block, checkout, and order line points back at these documents.' },\n { path: '/commerce-and-bookings/commerce/catalog', title: 'Product catalog', heading: 'Products, options, and variants', anchor: '#products-options-and-variants', text: 'A product is what you manage; a variant is what a customer actually buys. Products define up to 3 options (like Size or Color, each with up to 25 values), and the products hub expands them into a variants matrix — up to 100 variants per product, each with its own SKU, barcode, price, compare-at price, weight, image, and inventory count.\\n- Types: physical (shippable), digital (delivered as downloads), or service.\\n- Status: draft (invisible to visitors), active, or archived.\\n- Pricing: a variant with a compare-at price above its price shows a sale badge on storefront blocks.\\n- Inventory: leave blank for untracked, 0 means sold out — the same semantics the original product block used. Stock is not tracked on a digital or service subscription-only product: nothing decrements it, on the first charge or on any renewal, so the field is disabled there. A physical subscription tracks normally — every paid cycle decrements one unit per box shipped — and Both — buyer chooses keeps tracking on the one-time sales.\\nProducts created with the earlier single-price product block are lifted into this model automatically as a single default variant — nothing breaks and no migration step is needed.' },\n { path: '/commerce-and-bookings/commerce/catalog', title: 'Product catalog', heading: 'Billing modes and subscriptions', anchor: '#billing-modes-and-subscriptions', text: 'Each product\\'s Billing setting picks how buyers pay:\\n- One-time purchase (default) — a normal order.\\n- Monthly / Yearly subscription — buyers subscribe instead of buying once; the product page prices as $X/mo or $X/yr, the buy button reads Subscribe, and an optional free trial (in days) can precede the first charge. An active subscription is what members-only content checks.\\n- Both — buyer chooses — the product page shows a one-time / subscribe toggle (same price either way, at the interval you pick) and defaults to one-time. The choice is validated server-side against the product: a one-time sale here is a plain order, while the subscribe choice creates a recurring subscription exactly like a subscription-only product.\\nSelling subscriptions (including the subscribe side of Both) requires a plan with storefront subscriptions (Business and above). Subscription billing applies to the product page\\'s direct checkout — cart checkouts always charge one-time.\\nWhether a subscription moves stock follows the product\\'s Type. A physical subscription decrements one unit per paid cycle — the first charge and every renewal each create an order on the Subscription channel and take the box off the shelf, so the stock field stays live and low-stock alerts apply. A digital or service subscription never moves stock, and the stock field is disabled there; any number already saved is kept and shown, but it does not cap subscribers and will not change on its own. A saved 0 still stops new subscribers, so clear it (Clear stock on the notice in the editor) if that is not what you want. On a Both product the stock field stays live, because the one-time sales do decrement it.' },\n { path: '/commerce-and-bookings/commerce/catalog', title: 'Product catalog', heading: 'Write and propose with AI', anchor: '#ai', text: 'Aglyn AI can write a product\\'s description, search listing and tags from what it says and shows, write copy for many products or a CSV import at once, propose a first catalog from a brief, and propose categories and discounts. Every proposal is reviewed before it is saved. Proposed products are created as drafts with their prices left empty, and proposed discounts are created switched off. See Products with AI.' },\n { path: '/commerce-and-bookings/commerce/catalog', title: 'Product catalog', heading: 'Categories and tags', anchor: '#categories-and-tags', text: 'Categories are hierarchical (each may have a parent) and slugged for URLs. Tags are free-form labels. Both drive storefront filtering and smart collections.' },\n { path: '/commerce-and-bookings/commerce/catalog', title: 'Product catalog', heading: 'Collections', anchor: '#collections', text: 'Collections group products for landing pages and storefront blocks:\\n- Manual collections are an ordered, hand-picked list.\\n- Smart collections define rules — match by tag, category, price, name, or product type, with all or any semantics — and membership updates automatically as products change. Draft and archived products never appear.' },\n { path: '/commerce-and-bookings/commerce/catalog', title: 'Product catalog', heading: 'Slugs', anchor: '#slugs', text: 'Products, categories, and collections each have a host-unique slug used in storefront URLs (/products/{slug}, /collections/{slug}). Slugs are lowercase letters, numbers, and dashes.' },\n { path: '/commerce-and-bookings/commerce/catalog', title: 'Product catalog', heading: 'Google Merchant Center feed', anchor: '#merchant-center-feed', text: 'Your catalog is also published as a product feed for Google Merchant Center. The address is on the Store settings card under Commerce → Settings: a read-only Google Merchant Center feed URL field with a Copy feed URL button beside it.\\nIn Merchant Center, add it under Products → Feeds as a scheduled fetch. Merchant Center then re-reads the URL on the schedule you set there, so catalog changes reach it without another upload.\\nWhat the feed contains:\\n- One item per active product. Draft, archived, and deleted products never appear. Items are per product, not per variant — the price is the lowest price across the product\\'s variants.\\n- Per item: the product id, name, description (its name again if the description is empty), a link to /products/{slug} on your store, the product\\'s first image if it has one, the price in USD, an availability value, and the condition new.\\n- Availability is outofstock when a tracked stock count has reached zero and the product does not allow backorders. Everything else — including untracked stock — is instock.\\n- Up to 500 products. A larger catalog is not fully represented in the feed.\\nTwo things worth knowing before you submit it:\\n- Use the URL the card gives you. Each item\\'s link is built from the address the feed was requested on, so submitting the wrong one puts the wrong domain on every product in Merchant Center.\\n- The URL appears once the site has an address — a subdomain or a custom domain. Until then the card says so rather than showing a partial URL.\\nThe feed needs no credentials to read, and it carries only what your storefront already shows publicly. Responses are cached for an hour, so a price or stock change can take that long to appear in a fetch.' },\n { path: '/commerce-and-bookings/commerce/catalog', title: 'Product catalog', heading: 'Related', anchor: '#related', text: '- Commerce overview\\n- SEO — products emit structured data and join the sitemap automatically.' },\n { path: '/commerce-and-bookings/commerce/overview', title: 'Commerce', heading: '', anchor: '', text: 'Commerce\\nAglyn commerce sells from your own Stripe account: buyers pay you directly, and Aglyn collects a per-sale platform fee set by your plan (see Billing & plans — higher plans reduce fees to 0%).\\nPlan availability Paid. Starter sells up to 100 products (2% physical / 5% digital fee); Pro and Business raise limits and remove fees.' },\n { path: '/commerce-and-bookings/commerce/overview', title: 'Commerce', heading: 'Products hub', anchor: '#products-hub', text: 'The Products page is the catalog manager:\\n- Products with up to 3 options and 100 variants each — per-variant SKU, barcode, price, compare-at (sale badge), weight, and stock. See Product catalog.\\n- Categories & collections — a category tree plus manual and smart (rule-based) collections with a live match preview.\\n- CSV import/export in the Shopify column dialect, with a dry-run report — switching from Shopify is a file upload.\\n- Payments — Stripe Connect onboarding status and your plan\\'s fee ladder.' },\n { path: '/commerce-and-bookings/commerce/overview', title: 'Commerce', heading: 'Inventory', anchor: '#inventory', text: '- Per-variant stock; blank = untracked, 0 = sold out.\\n- Oversell policy per product: stop selling or allow backorders.\\n- Low-stock alerts notify managers once per threshold crossing.\\n- Adjustment history with reason codes (sale, restock, correction…).\\n- Locations split stock across warehouses/storefronts (plan-capped); adjustments and POS sales bucket per location.\\n- Reserved in checkout holds units for a shopper who has reached the payment page, so the last one cannot be sold twice.\\nReserved stock {#reserved-stock}\\nWhen a shopper reaches the payment page, the units in their basket are reserved — the next shopper is offered what is left, not the same unit. It is what stops two people paying for the last one.\\nYour stock count does not move when a unit is reserved. A reservation is a promise, not a sale, and the count means what is on the shelf. The products list names the difference instead: 3 (1 reserved) is three units on the shelf with one of them spoken for, so two are available to buy right now.\\nA reservation is released when the shopper pays (the count moves then), when they abandon the checkout, or after 31 minutes, whichever comes first. A checkout page left open expires on its own, so nothing stays reserved by someone who walked away.\\nTwo cases reserve nothing, deliberately: a variant with no stock tracking has no number to reserve against, and a product set to allow backorders is one you have told us to keep selling past zero.\\nSales at the register are not affected — the till never refuses a sale, because the shelf in front of the cashier is the truth.\\nStock movements {#stock-movements}\\nInventory → Stock movements is the history behind every tracked count — the answer to \"the shelf says four and the console says six, what happened?\"\\nFive things write to it and ' },\n { path: '/commerce-and-bookings/commerce/overview', title: 'Commerce', heading: 'Gift cards & store credit', anchor: '#gift-cards', text: 'Gift cards lists every card your store has issued, what is left on each one, and what that adds up to. Business plan and above.\\nCards arrive two ways:\\n- A shopper buys one. A gift-card purchase mints a card with a code at the moment the payment settles.\\n- You issue one by hand. Enter an amount and, optionally, an email address and the code is created and mailed to that person. This is the goodwill gesture and the service-recovery path — a refund you would rather keep as store credit.\\nBalances apply automatically at checkout; a shopper enters the code and the card is drawn down by what the order uses. Search the list by code or by recipient email to find one.\\nOutstanding balance is a liability, not revenue The outstanding total at the top is money customers have already paid you and have not yet spent. It is store credit you owe against future orders, and it belongs on the liability side of your books — not in a sales figure. Cards are counted at zero or above, so a card that has somehow gone negative cannot flatter that total downward.\\nVoiding a card zeroes whatever is left on it. The holder can no longer redeem it and the action cannot be undone, so you are asked to confirm the remaining amount first.' },\n { path: '/commerce-and-bookings/commerce/overview', title: 'Commerce', heading: 'Recovery & alerts', anchor: '#recovery-and-alerts', text: 'Two queues the storefront fills and Aglyn drains for you, both visible so you can see they are moving.\\nAbandoned checkouts — a shopper who reached checkout, entered their email and left. Aglyn emails them once, with a link back to the cart they had built, about fifteen minutes after that checkout has been idle for an hour. A checkout that gets completed stops reminding itself, and one that is never completed is given up on after seven days. The card shows how many are due a reminder, how many are still inside the first hour, and how many have already been reminded, with the most recent few named. Pro plan and above.\\nBack-in-stock alerts — anyone who used \"Notify me when it\\'s back\" on a sold-out product is emailed once its stock goes above zero. The count of shoppers waiting is a demand signal worth restocking against. Available on every plan that includes commerce.\\nBoth are read-only here on purpose. Sending is the scheduled job\\'s to do, and a \"send now\" button would race it for a reminder you cannot un-send.' },\n { path: '/commerce-and-bookings/commerce/overview', title: 'Commerce', heading: 'Orders', anchor: '#orders', text: 'Every paid checkout becomes an order with a sequential number, line-item snapshots, totals, and a timeline:\\n- Statuses: pending → paid → fulfilled (or partially) → delivered, with cancel/refund exits guarded by a status machine. The seven of them, and the labels the console shows, are in Statuses and channels.\\n- Fulfill with tracking, print packing slips, add internal notes.\\n- Refunds (full or partial) go through Stripe and reverse the platform fee. Refunding needs the admin role and organization-wide membership — a workspace owner or admin, or a member given access to every site. A collaborator invited to this one site is an admin of the site, which is enough to run the till and fulfill orders but not to send money back out of the business.\\n- Chargebacks are shown apart from refunds. When a shopper disputes a charge with their bank, the order gets a Chargeback open badge and the list warns you with the date Stripe needs your evidence by — answer it in the Stripe dashboard, because an unanswered dispute is decided for the shopper. If the dispute is lost the money is reversed, the order reads Charged back rather than Refunded, and the buyer loses the downloads, membership and verified-purchase review a refund would have withdrawn. A dispute you win reverses nothing. Filter the list by Disputes to find either.\\n- Draft orders: build an order in the console and send the buyer a payment link (Shopify parity). Requires an active plan with commerce — see the note below.\\nThe Orders screen {#orders-screen}\\nOpen your site\\'s Products hub and choose the Orders tab. Before your first sale the tab is an invitation rather than a table: it explains where orders come from and offers Draft order, so you can invoice a customer you already have. The filters and Export CSV appear once there' },\n { path: '/commerce-and-bookings/commerce/overview', title: 'Commerce', heading: 'Shipping & taxes', anchor: '#shipping--taxes', text: '- Shipping zones own countries (\\'\\' = rest of world); rates are flat, free-over-subtotal, or subtotal/weight tiers; optional local pickup.\\n- Taxes: manual per-region rates (state beats country, VAT-style inclusive pricing supported) or Stripe Tax automatic calculation; products can be tax-exempt.\\nLodging tax on reservations\\nA stay is not goods. The sales-tax settings above configure a goods rate resolved against an address; occupancy (lodging/hotel) tax is a separate regime with its own rates, its own registration and its own return, so reservations do not use them.\\nCommerce → Settings → Taxes → Lodging tax is where you set your own rate for it. It is off by default — leave it blank and reservations charge no lodging tax, exactly as before.\\nWhen you set a rate, Aglyn adds it to the reservation charge as its own receipt line using the label you choose, and records the amount and the rate on the reservation. It is always your own rate: Stripe Tax cannot compute occupancy tax from a reservation session, so a store using Stripe Tax for goods still sets this one by hand.\\nAglyn does not provide tax advice Aglyn applies the rate you enter and records what was charged. It does not determine whether lodging tax applies to you, at what rate, or where it should be paid. Confirm your obligations with a qualified tax professional.\\nDeposits. A reservation usually charges a deposit rather than the whole stay, and the rate is applied to the amount actually charged — the deposit. Aglyn does not decide whether your jurisdiction wants occupancy tax on the full stay at booking, on the deposit, or at check-out. If tax is due on more than the deposit, collect the difference the way you collect the rest of the balance.\\nStorefront sales tax\\nAnalytics → Storefront sales tax shows what your store' },\n { path: '/commerce-and-bookings/commerce/overview', title: 'Commerce', heading: 'Dropshipping', anchor: '#dropshipping', text: 'Assign a supplier to a product and paid orders route automatically — by email and/or HMAC-signed webhook — with a token link the supplier uses to post tracking back, which fulfills the order. Pro plan and above.' },\n { path: '/commerce-and-bookings/commerce/overview', title: 'Commerce', heading: 'Related', anchor: '#related', text: '- Product catalog\\n- Billing & plans\\n- Bookings & scheduling' },\n { path: '/commerce-and-bookings/commerce/pos-and-reservations', title: 'POS & reservations', heading: '', anchor: '', text: 'POS & reservations\\nPlan availability POS requires Pro or above. The number of registers a site can run at once follows your plan — Pro 1, Business 2, Advanced 5 — plus any additional register seats you\\'ve assigned to that site. A seat is bought once for the workspace and then placed on one site, so buying one does not raise the limit everywhere; see Assigning register seats. Opening more browser tabs does not give you more registers; each sale runs through a register you\\'ve created.' },\n { path: '/commerce-and-bookings/commerce/pos-and-reservations', title: 'POS & reservations', heading: 'Registers', anchor: '#registers', text: 'Create your registers under Commerce → Settings → POS registers — one per till or device that takes in-person payments. Give each a name (and, if you use inventory locations, the location it sells from). Every POS sale is tagged with its register so you can reconcile end-of-day takings per till. Your plan caps how many registers you can run; add more with the register add-on in Billing. If you downgrade below your register count, the extra registers (newest first) stop taking payments and show Over plan limit until you remove them or upgrade again — none are deleted.' },\n { path: '/commerce-and-bookings/commerce/pos-and-reservations', title: 'POS & reservations', heading: 'The register', anchor: '#the-register', text: 'Open /{site}/pos in the console for a touch-first register. Pick which register you\\'re on at the top of the panel (skipped automatically when you have only one):\\n- Product grid — tap to add; products with variants show quick chips.\\n- Barcode scanners — any keyboard-wedge scanner works: it types the code into the search box and presses Enter, which adds the exact SKU/barcode match. No drivers or pairing needed.\\n- Payments — cash (with change calculation), card via QR (the customer scans and pays on their phone; the sale completes automatically), or charge to room for checked-in reservation guests. Stripe Terminal readers can replace the QR step later without changing the flow.\\n- Receipts print through the browser\\'s print dialog — any receipt printer with a system print driver works. Set the paper size to your roll width once and the browser remembers it.\\nPOS sales create normal orders tagged pos, decrement the same inventory as your online store (per location if you use locations), and appear in the orders list under the channel filter.\\nPlatform fees at the register\\nYour plan\\'s platform fee is charged on the sale, not on how it was paid — the rate is the same whether the customer hands you cash, scans the QR, or charges it to their room. What differs is only how it reaches us:\\n| Tender | How the fee is collected |\\n| Card (QR) | Deducted from your Stripe payout for that sale |\\n| Cash | No payout to deduct from — added to your next monthly invoice |\\n| Charge to room | Same as cash: added to your next monthly invoice |\\nThe customer never pays the fee, and the amount Due on the register is the same on every tender. On plans with a 0% fee there is nothing to collect either way. See Billing & plans for your plan\\'s rates.\\nSelling past the count\\nIf a line is for more units than ' },\n { path: '/commerce-and-bookings/commerce/pos-and-reservations', title: 'POS & reservations', heading: 'Reservations', anchor: '#reservations', text: 'For stays (cabins, rooms, rentals):\\n1. Add resources on the Products page — nightly rate, weekend multiplier, minimum nights, deposit percent, and free-cancellation window.\\n2. Drop the Reservation widget on any screen in the besigner and point it at the resource id. Guests pick dates, see a live quote, and pay the deposit (or full amount) at checkout.\\n3. Manage stays from the Reservations card: check in, check out (with a folio summary if the guest charged store purchases to the room), walk-ins, no-shows, and cancellations.\\nDeposits need a plan with commerce Taking a reservation deposit is a sale, and it is checked against your plan at the moment a guest tries to pay — not just when you added the widget. On a plan without commerce the widget still renders but checkout answers \"Reservations are not enabled\". Enabling the plugin is your switch; including commerce is your plan\\'s. See downgrading.' },\n { path: '/commerce-and-bookings/commerce/pos-and-reservations', title: 'POS & reservations', heading: 'Related', anchor: '#related', text: '- Commerce overview\\n- Product catalog' },\n { path: '/concepts/glossary', title: 'Glossary & naming conventions', heading: '', anchor: '', text: 'Glossary & naming conventions\\nAglyn\\'s vocabulary grew through a couple of migrations, so several words name the same thing at different layers. This page is the canonical map (codified in AGL-443). For definitions of every product term — screens, nodes, plugins, workflows, and the rest — see the Term reference; this page is specifically the ruling on the four contested words.' },\n { path: '/concepts/glossary', title: 'Glossary & naming conventions', heading: 'The hierarchy', anchor: '#the-hierarchy', text: '' },\n { path: '/concepts/glossary', title: 'Glossary & naming conventions', heading: 'Organization (org)', anchor: '#organization-org', text: 'The entity. One subscription, one members roster (owner / admin / editor / viewer), one isolation boundary in Firestore (orgs/{orgId}), one plugin switchboard (enabledPlugins), org-scoped shared data (datasets, contacts, media, installs). Every current API, type, and permission is org-named: AglynOrganization, resolveOrgPermissions, getOrgForHost, the console\\'s /org/ section.\\nRule: new code that touches the entity says org.' },\n { path: '/concepts/glossary', title: 'Glossary & naming conventions', heading: 'Workspace', anchor: '#workspace', text: 'The same entity, in the user\\'s language. \"Your workspace\" is what we call an organization in UI copy and user docs — Slack-style, including the {slug}.aglyn.com workspace subdomain. It intentionally never names a collection or a type.\\nRule: workspace appears in UI copy and docs only, never in code identifiers.' },\n { path: '/concepts/glossary', title: 'Glossary & naming conventions', heading: 'Tenant', anchor: '#tenant', text: 'Two meanings — this is the word to be careful with:\\n1. The published-site runtime (current, correct use): apps/tenant is the app that renders customers\\' live sites, and the shared @aglyn/tenant- libraries serve that side of the platform. \"Multi-tenant\" in architecture prose uses this sense: the org is the tenancy boundary.\\n2. A historic alias for the org\\'s billing doc (now removed): before the organizations migration, billing lived at tenants/{uid}. The collection is retired and, as of AGL-444, so are the alias identifiers — AglynTenant is now AglynOrgBilling, useCurrentTenant() is useCurrentOrg() (returning org), the plugin-page tenant prop is org, and the Tenant permission/entitlement types are Org. The persisted strings went in AGL-445, while the platform was still pre-launch: Stripe billing metadata is keyed orgId, host docs carry only orgId/memberRoles, and the users.{uid}.tenants map is gone. No tenant-spelled data remains to migrate.\\nRule: \"tenant\" means the site runtime, full stop. Code that means the entity says org and types it AglynOrgBilling.' },\n { path: '/concepts/glossary', title: 'Glossary & naming conventions', heading: 'Tenant vs. host — not the same thing', anchor: '#tenant-vs-host--not-the-same-thing', text: 'These are the two most-confused terms because both relate to published sites:\\n- A host is one site: hosts/{hostId}, with its own domain or subdomain, screens, media, and member-role projection. An organization owns many hosts (\"3 of 15 sites\"). In UI copy a host is called a site.\\n- The tenant app is the runtime that serves every host: one Next.js deployment that resolves the incoming hostname to a host doc (hostIndex), loads that host\\'s org, and renders its published screens.\\nSo: an org (workspace) owns hosts (sites); the tenant app serves them. A request to bakery.example.com is resolved to a host, whose org decides plugins/entitlements, rendered by the tenant app.' },\n { path: '/concepts/glossary', title: 'Glossary & naming conventions', heading: 'Quick reference', anchor: '#quick-reference', text: '| Term | Layer | Names in code | Use for new work |\\n| Organization / org | Data model, APIs, permissions | orgs/{orgId}, AglynOrganization, resolveOrg | ✅ the entity |\\n| Workspace | UX / docs | — (copy only) | ✅ user-facing copy |\\n| Tenant (runtime) | Published-site side | apps/tenant, @aglyn/tenant- | ✅ that side of the platform |\\n| Tenant (billing alias) | Removed (AGL-444/445) | none — identifiers and persisted strings are both gone | ❌ don\\'t reintroduce |\\n| Host / site | One published site | hosts/{hostId}, AglynHost | ✅ host in code, \"site\" in copy |' },\n { path: '/concepts/term-reference', title: 'Term reference', heading: '', anchor: '', text: 'Term reference\\nEvery term you\\'ll meet in Aglyn, defined in a sentence or three, with links to the page that covers it in depth. For the rules about which word to use where (org vs. workspace vs. tenant vs. host), see the Glossary & naming conventions — this page is the vocabulary, that page is the ruling.\\nJump to a group: Platform & accounts · Sites & content · The node tree · Besigner (the editor) · Plugins & marketplace · Data & logic · Automation & marketing · Commerce · Billing & plans\\nEvery term below is a heading, so the table of contents on the right lists them all alphabetically within each group.' },\n { path: '/concepts/term-reference', title: 'Term reference', heading: 'Platform & accounts', anchor: '#platform--accounts', text: 'Aglyn\\nThe platform these docs cover — the console you build in and the sites you publish. Pronounced AG-lin — \"ag\" as in agriculture, with the stress on the first syllable (IPA /ˈæɡlɪn/).\\nAglyn AI\\nThe generative side of the assistant: the doors that build a page, layout, page template, reusable component, form, email, campaign, product copy, SEO text or theme change from a description. An add-on on any paid plan, carried by agreement on Enterprise, and metered in credits. Everything it makes is a draft — it never publishes, changes a live page in place or sends anything. Currently rolling out. → Aglyn AI, Add-ons\\nAglyn Assist\\nThe chat helper in the console, which answers questions about the product from this documentation and cites the sections it drew on. Aglyn AI is the same assistant when it builds rather than explains. → Aglyn Assist\\nOrganization (org)\\nThe account entity: one subscription, one team roster, one plugin switchboard, one data-isolation boundary. Everything you own in Aglyn — sites, datasets, media, installs — belongs to an organization. In code and APIs this is always org. → Glossary, Teams & roles\\nWorkspace\\nThe same entity as an organization, in the product\\'s user-facing language — \"your workspace\", including the {slug}.aglyn.com workspace URL. Workspace never names anything in code; it\\'s the word the UI uses for your org. → Glossary\\nTenant\\nThe published-site runtime: the single deployment that takes any incoming domain, resolves it to one of your sites, and renders its published screens. \"Tenant\" always means this serving side of the platform — it is not another word for organization. → Glossary, Architecture & multi-tenancy\\nHost\\nOne site, as the data model sees it: its subdomain or custom domain, its screens and layouts, its media, its member-role pr' },\n { path: '/concepts/term-reference', title: 'Term reference', heading: 'Sites & content', anchor: '#sites--content', text: 'Screen\\nOne page of a site: a slug, publish state, and a designed node tree. Screens can nest (parent/child slugs) and can bind to a shared layout. → Screens\\nLayout\\nShared site chrome — app bar, footer, navigation — designed once and rendered around every screen bound to it. → Layouts\\nSlug\\nA screen\\'s URL segment. Nested screens compose their ancestors\\' slugs into the full path. → Screens\\nVersion\\nAn immutable snapshot of a screen\\'s (or layout\\'s) node tree. You edit a draft version; publishing points the live site at a version. Scheduled publishing flips the pointer at a set time. → Publish your first screen\\nRedirect\\nA rule that forwards one path to another on your published site — for migrations, renamed pages, or vanity URLs. → Redirects\\nError screens\\nDesignable screens served for 404/401/403/503 instead of a generic error page. → Error screens\\nMaintenance mode\\nA host-level switch that serves the designed 503 screen on every path while you work. → Maintenance mode\\nLocale\\nA language your site serves (e.g. en, es). Multilingual sites pick a default locale and can render a language switcher. → Multilingual\\nSite template\\nA snapshot of screens, layouts, and content that can be applied to a new site — yours to save and reuse, or installed from the marketplace. → Site templates\\nTheme\\nThe site-wide design system — palette, typography, spacing, component styling — edited in the theme builder and applied to every screen. → Theme builder\\nCustom domain\\nYour own domain attached to a site in place of the default subdomain, with guided DNS verification. → Custom domains\\nSubdomain\\nThe default address every site gets on the platform\\'s serving domain, before (or alongside) a custom domain. → Custom domains' },\n { path: '/concepts/term-reference', title: 'Term reference', heading: 'The node tree', anchor: '#the-node-tree', text: 'The rendering model shared by the editor and the published site. Every designed screen is a tree of nodes; the renderer walks it with a family of components named like the parts of a tree.\\nNode\\nThe atom of a design: { $id, componentId, props, sx, nodes }. Each node names the registered component that renders it, carries its props and styling, and lists its child node ids. A screen version stores its whole design as a map of nodes. → Besigner overview\\nTree\\nThe node hierarchy of one screen or layout — what you see in the Besigner\\'s hierarchy panel and what the renderer walks to produce the page. → Drag-and-drop hierarchy\\nTree root\\nThe renderer\\'s entry point: takes the root node and mounts the walk, letting hosts (the editor, the published site) swap in their own trunk, stem, branch, or leaf implementations.\\nTrunk\\nThe first hop of the walk — renders the root node through the current stem. The editor substitutes its own trunk to add design-time behavior without touching the production renderer.\\nStem\\nThe per-node resolver: looks up a node\\'s componentId in the component registry and renders it, recursing into the node\\'s children.\\nBranch\\nRenders a node\\'s children in order — the fan-out step between a parent node and its child stems.\\nLeaf\\nThe terminal wrapper that actually mounts a node\\'s registered component with its resolved props. Each leaf tags its DOM with the node id, which is what the editor\\'s selection and drag-and-drop hang on to.\\nComponent\\nA registered, renderable building block (button, section, product card…) a node can reference by componentId. Component ids are persisted in screen documents, so they are never renamed. → Besigner overview\\nComponent bundle\\nA named set of components a plugin registers together for the canvas — the base MUI bundle plus whatever featur' },\n { path: '/concepts/term-reference', title: 'Term reference', heading: 'Besigner (the editor)', anchor: '#besigner-the-editor', text: 'Besigner\\nAglyn\\'s visual designer — the editor where you build screens, layouts, and designed emails by composing the node tree on a live canvas. → Besigner overview\\nCanvas\\nThe live design surface inside the Besigner: your screen rendering for real, with selection, drag-and-drop, and inline text editing layered on top. → Besigner overview\\nHierarchy panel\\nThe tree view of the current screen\\'s nodes — select, reorder, and reparent from the structure instead of the canvas. → Drag-and-drop hierarchy\\nDrawer\\nThe Besigner\\'s component palette: registered components, presets, and installed marketplace components, ready to drag onto the canvas. → Besigner overview\\nBinding\\nA token in designed content that resolves to live data at render time — dataset fields, variables, page context. → Bindings' },\n { path: '/concepts/term-reference', title: 'Term reference', heading: 'Plugins & marketplace', anchor: '#plugins--marketplace', text: 'Plugin\\nA self-contained feature package that extends the console, the Besigner canvas, the published site, or the server APIs — without touching core code. First-party features and marketplace submissions use the same system. → Plugins overview\\nAdd-on\\nThe user-facing word for optional capability on top of your plan — installed plugins and purchasable extras, managed from the Marketplace (Installed tab). → Plugins overview\\nSurface\\nThe part of the product a plugin registers into: the console client, the site (canvas + published pages), or the server tiers. A plugin declares which surfaces it provides. → Plugin manifest\\nConsole extension\\nEverything a plugin adds to the console shell — nav items and pages, dashboard cards, settings sections, widgets, providers. → Extension points\\nWidget\\nA plugin component rendered into a named shell zone (dashboard footer, org settings, listing detail…). → Injection zones\\nInjection zone\\nA named slot in the console shell that plugin widgets can target. The guaranteed zones are a typed catalog; custom zones are open strings. → Injection zones\\nPlugin manifest\\nThe generated loader table mapping plugin ids to their import entry points per app — the only sanctioned place plugins are referenced by the apps. → Manifest & envs\\nEnabled plugins\\nThe per-organization switchboard deciding which plugins load for a workspace. Enablement, plan entitlement, and release flags gate together. → Plugins overview\\nFeature flag\\nA plugin\\'s plan-entitlement gate: the extension names the flag, and the shell only serves it when the org\\'s plan (or overrides) grants it. → Billing & plans\\nRelease flag\\nA staff-controlled rollout switch (off / staff / percentage / on) evaluated per workspace — how features ship dark and roll out gradually. → Feature flags & releases\\nPlugin ' },\n { path: '/concepts/term-reference', title: 'Term reference', heading: 'Data & logic', anchor: '#data--logic', text: 'Dataset\\nA structured, org-scoped data collection with a typed schema — the backing store for dynamic content and forms. → Datasets\\nRecord\\nOne row of a dataset. → Datasets\\nField\\nOne typed column of a dataset\\'s schema. Plugins can register custom field types that ride on the built-in storage types. → Model builder\\nRelation\\nA link between datasets (reference fields), letting records point at records. → Relations\\nContact\\nA person captured into your workspace\\'s audience — from forms, commerce, or manual entry. → CRM\\nSegment\\nA saved filter over contacts, used to target campaigns and automations. → CRM\\nMedia library\\nPer-site uploaded assets with folders, tags, CDN delivery, and generated variants. → Media\\nVariable\\nA named site-level value (text, number, toggle) referenced from bindings and logic — change it once, it updates everywhere it\\'s used. → Bindings\\nFunction (fx)\\nA site-level computed expression — inputs, conditions, and operations that produce a value bindings can consume. Edited from the Besigner\\'s ƒx panel and the Logic page. → Bindings\\nForm\\nA designed input block on a published page whose submissions flow into the forms pipeline (and from there to contacts, workflows, and notifications). → Forms' },\n { path: '/concepts/term-reference', title: 'Term reference', heading: 'Automation & marketing', anchor: '#automation--marketing', text: 'Event\\nSomething that happened on a site or in the workspace — a form submission, an order, a booking — that automations and workflows can react to. → Workflows & actions\\nWorkflow\\nA configured sequence of actions that runs when its trigger event fires, with run logs to audit each execution. → Build a workflow\\nAction\\nOne step inside a workflow — send an email, call a webhook, update a record. → Actions builder\\nAutomation\\nClient-side reactive behavior on the published site (show, hide, trigger on visitor activity), configured in the marketing tools and executed by the site runtime. → Marketing overlays\\nOverlay\\nSite-wide attention elements — the announcement bar (persistent top strip) and the popup (triggered by delay, scroll, or exit intent, with optional email capture). → Marketing overlays\\nExperiment\\nAn A/B test on a screen: visitors are assigned variants and engagement is tracked so a winner can be promoted. → Marketing overlays\\nEmail campaign\\nA one-off or automated email send to contacts or a segment, with delivery and engagement tracking. → Email campaigns\\nDesigned email\\nAn email document built in the Besigner — the same node tree, rendered to email-safe markup. → Designed emails\\nMerge tag\\nA personalization token in an email or overlay (\"first name\", \"order total\") resolved per recipient at send time. → Email campaigns' },\n { path: '/concepts/term-reference', title: 'Term reference', heading: 'Commerce', anchor: '#commerce', text: 'Commerce\\nThe commerce plugin family: catalog, storefront components, cart and checkout, orders, discounts, and analytics. → Commerce\\nProduct\\nA sellable catalog item — physical, digital, or service — with variants, pricing, and inventory. → Catalog\\nOrder\\nA completed purchase with its line items, payment state, and fulfillment history. → Commerce\\nPOS\\nPoint of sale — in-person checkout from the console against the same catalog and inventory. → POS & reservations\\nBooking\\nA scheduled appointment or reservation taken through the bookings plugin\\'s services, availability, and slots. → Bookings' },\n { path: '/concepts/term-reference', title: 'Term reference', heading: 'Billing & plans', anchor: '#billing--plans', text: 'Plan\\nThe subscription tier an organization is on. Entitlements resolve from the plan at read time; workspaces without a plan run dark-launch (ungated). → Billing & plans\\nEntitlement\\nA capability or limit granted by the plan — feature switches and quotas — optionally overridden per organization by staff. → Billing & plans\\nQuota\\nA numeric entitlement (sites, datasets, storage) checked at the point of use, with warnings as you approach the limit. → Billing & plans\\nSeat\\nA team-member allowance. Plans include seats; addon seats can be purchased up to a per-plan maximum. → Teams & roles\\nMetered usage\\nConsumption billed monthly in arrears (storage, delivery) with a live estimate shown before it lands on an invoice. → Billing & plans\\nCredit (AI credit)\\nThe unit every AI request is measured in — an assistant answer, a rewrite, a generated section, a whole site — drawn from one monthly pool per workspace rather than a meter per feature. What a request costs varies with how much work it is, and a job estimates itself before you confirm. A plan includes a band each month, the Aglyn AI add-on widens it, and a workspace can stop at the band or set a ceiling on what it will pay past it. → Credits and caps, AI assist overage\\nAllotment\\nOne member\\'s, one site collaborator\\'s or one whole site\\'s monthly share of the workspace\\'s AI credits. Hard stops AI there; soft keeps working and notifies. A share of the pool, never an addition to it. → AI allotments' },\n { path: '/content-and-data/crm/activities', title: 'Activities & the timeline', heading: '', anchor: '', text: 'Activities & the timeline\\nAn activity is something a person on your team did about a record — a call made, an email sent, a meeting held, a note worth keeping. It is logged by hand, from the record it is about — or, for an email, written and sent from there — and it sits in one timeline beside everything the platform captured about that person on its own and every campaign email it sent them.' },\n { path: '/content-and-data/crm/activities', title: 'Activities & the timeline', heading: 'Four kinds of history', anchor: '#four-kinds-of-history', text: 'A contact\\'s timeline is one newest-first stream drawn from four sources, and every entry says which it is:\\n| Entry | Where it comes from | Who can change it |\\n| Captured | The platform recorded it — a form submission, an order, a booking, a member sign-up, a newsletter opt-in, an API or import create, or a contact added by hand. Each carries the door it came through and, for a form, the page the person was on. | Nobody. It is a record of what happened. |\\n| Logged | A member of your team logged it — a call, an email, a meeting, a note, or something else. | Its author, or an org-wide member. |\\n| Sent | An email the platform sent to this person from their record — by a teammate with Send email, or by an automation\\'s Send an email step. It carries its subject, the address it went to, and where delivery got to. See Sending an email. | Nobody edits what was sent; its author or an org-wide member may delete it. |\\n| Campaign | A campaign email the platform delivered to the person — which email, when it went out, and what became of it. See Campaign email on the timeline. | Nobody. It is the delivery record. |\\nA captured entry from this site carries a link to its record: Open submission lands on the Inbox with the reader open on that submission, Open order on the orders list with the order\\'s dialog open, and a booking or a sign-up on the Bookings or Users page. An entry from another site in your workspace — read on that site\\'s console — or one written before the site was stamped on it is shown without a link.\\nCaptured history lives on the contact and is kept to the most recent fifty entries. Logged activity is a collection of its own, bounded at 5,000 entries per record — a call a day for fourteen years, so nobody working a real relationship reaches it, and an automation logging ' },\n { path: '/content-and-data/crm/activities', title: 'Activities & the timeline', heading: 'Campaign email on the timeline', anchor: '#campaign-email', text: 'Every campaign email sent to the person from this site — or from a site declared to be one sender with it — appears at the moment it was sent, in one line:\\n> Spring sale · sent · delivered · opened ×2 · clicked\\nThe name is the email as your team named it on Emails › Messages, or its subject when it has no name, and it links to that email\\'s own report. The words after it are the states the email reached, in order: delivered, opened (with a count past one), clicked (likewise), bounced, or marked as spam. The subject line rides under the entry when it differs from the name.\\nWhat appears, and what does not:\\n- Campaigns only. A receipt, a booking confirmation, a password reset or an invite is not on a CRM timeline — those are account mail, sent to the person rather than to an audience.\\n- Your sites\\' campaigns only. A contact record is shared by every site in your workspace, and other businesses in the same workspace that mail the same person never see each other\\'s campaigns here. A campaign sent by a sibling site in your own consent group is listed, without a link — that site\\'s Emails console is its own.\\n- The newest fifty messages to the address are read. Somebody mailed heavily by several sites can have older campaign entries fall past the window.\\n- Opens and clicks need the delivery webhook. An email sent before it was connected shows as sent and nothing more, the same gap the campaign report itself reports.\\n- An email your team has deleted keeps its entry, named by the subject the person received, with no report to link to.\\nThe timeline says so when the delivery log could not be read, rather than showing an empty history — the two lead to opposite conclusions about the relationship.\\nThe most recent open or click across all of these is the record\\'s Last engaged stamp, whi' },\n { path: '/content-and-data/crm/activities', title: 'Activities & the timeline', heading: 'Logging an activity', anchor: '#logging-an-activity', text: 'Open the record — a contact\\'s page under CRM › Contacts, a company\\'s under Companies, a deal\\'s under Deals, or a lead\\'s under Leads — and choose Log activity. The activity is filed against that record; there is no picker, because what you are looking at is what the activity is about.\\nLogging an activity is part of the CRM, included from Starter. On Free the CRM is shown locked, so there is no record page to log from.\\n| Field | What it is |\\n| Kind | Call, Email, Meeting, Note or Other. |\\n| When | When it happened, which is not always when you are logging it. Defaults to now. |\\n| What happened | The body of the entry. |\\n| Outcome | For a call or a meeting: how it ended — \"left a voicemail\", \"agreed to a trial\". |\\n| Minutes | For a call or a meeting: how long it took. |\\nEach entry shows who logged it and how long ago. The author can edit or delete their own entries; an org-wide member can edit or delete anyone\\'s. A colleague with access to only some of your sites can read an entry they did not write, and cannot change it.\\nA meeting from a booking {#meeting-from-a-booking}\\nA confirmed booking files a meeting on its record\\'s timeline without anyone logging it — titled with the service and the slot, and linked to the booking. The record is the one the booking link was dropped from (a contact\\'s, a lead\\'s or a deal\\'s Book a meeting, or Insert booking link in Send email), which wins even when the person books with another address; a booking taken from the widget with no record attached is matched to a contact by the booker\\'s address. When the service asks for it, a Follow up after &lt;service&gt; task is filed beside the meeting, due one business day after the slot. Nothing is filed on a site where the CRM is switched off, or for a workspace whose plan does not include the CRM.' },\n { path: '/content-and-data/crm/activities', title: 'Activities & the timeline', heading: 'Click to call', anchor: '#click-to-call', text: 'A contact\\'s, a lead\\'s and a company\\'s page carries Call beside Send email: one tap dials the number the record holds, through whatever your device uses for a phone. The number itself is a link everywhere it is printed — on the record page and on the properties card — so it can be dialed from where you are reading it rather than copied out.\\nOnly a number a dialer can take is a link. Write phone numbers with the country code, like +1 512 555 0107; spaces, dots, dashes and parentheses are fine and are ignored. A value that is not a number — \"ask reception\", an extension typed as x44 — is shown as plain text and Call says why it is unavailable, rather than offering a link that would ring nothing.\\nLog a call sits beside it and opens the dialog above with the kind already set to Call and the record already bound, so logging what was said is one button and one sentence. It is there whether or not the record has a number: a call placed from your own mobile is still a call to log. The kind is a starting point, not a lock — change it in the dialog if the conversation turned out to be something else. Calling and logging a call are part of the CRM, included from Starter; on Free the record pages that carry them are locked with the rest of the CRM.' },\n { path: '/content-and-data/crm/activities', title: 'Activities & the timeline', heading: 'Sending an email', anchor: '#sending-an-email', text: 'A contact\\'s, a lead\\'s and a deal\\'s page each carry Send email: one message, from you, to the person the record is about. It is a letter, not a campaign — there is no audience, no designed layout and no unsubscribe footer — and it is logged on the timeline the moment it leaves, with its delivery state following. One-to-one email is part of the CRM, included from Starter; on Free the record pages that send it are locked with the rest of the CRM.\\n| Field | What it is |\\n| To | The record\\'s own address. It cannot be changed here: a deal writes to its contact, a lead to the address it was captured with. |\\n| From | The site\\'s sending address — the same identity campaigns and transactional mail leave on — with your name in front of it. |\\n| Reply-to | Your own address, so a reply lands in your inbox rather than the site\\'s mailbox. |\\n| Template | A saved letter to start from — fills in the subject and the message. Shown once the workspace has one; see Email templates & snippets. |\\n| Subject and Message | What you write. The message is plain text; a blank line starts a new paragraph. Insert drops a snippet or a merge field — {{contact.firstName}}, {{deal.amount}} — at the cursor, filled in from the record when the email is sent, and Save as template… keeps the draft for next time. |\\nThe send is refused, and the dialog says why, when:\\n- the site has no sending identity in effect — an admin sets one up under Emails › Sending, and the dialog links there;\\n- the address is on a suppression list because it bounced or reported a message as spam, or the person has asked this site not to email them;\\n- the organization has reached its one-to-one email allowance for the day — see the cap, which resets at midnight UTC;\\n- you have sent more than twenty emails in the last minute.\\nEvery sent ema' },\n { path: '/content-and-data/crm/activities', title: 'Activities & the timeline', heading: 'Captured email', anchor: '#captured-email', text: 'A reply to a sent email reaches your mailbox, because that is where Reply-to points — and the console cannot see your mailbox. To put the reply on the record, hand it the message: forward it to the workspace\\'s capture address, or put that address in BCC when you write to a contact from your own mail client. The address is printed under Reply-to in the Send email dialog and on the Email capture card under CRM → Settings, each with a copy button.\\nA message that arrives is filed as one email entry on the timeline:\\n- Whose record. The first address among From, To and Cc that is not a workspace member\\'s and belongs to a contact or a lead in the CRM. A member\\'s addresses are the one they sign in with and each one they have verified under Your sending addresses, so a message sent from a verified send-as alias is filed on the person it was written to. A lead\\'s message files on the lead. The record\\'s own site is the site the entry lands under, so at the organization level a capture works for every site at once.\\n- Which way. A message a correspondent wrote reads Received, with the sender beside the time; a message a member wrote and copied in reads Sent, without a delivery chip, because the platform did not carry it. Neither carries the Logged chip — nobody logged them — and neither can be edited, though whoever may delete an entry may delete these.\\n- What is kept. The subject and a bounded plain-text excerpt of the message, cut above the quoted history (On … wrote:), plus the message\\'s own identifier so a second delivery of the same message is filed once. Attachments are not kept.\\n- What is dropped. A message matching nobody in the CRM is not filed; the organization\\'s Setup → Activity feed notes Inbound email matched no record with the sender\\'s domain, never the message. A messa' },\n { path: '/content-and-data/crm/activities', title: 'Activities & the timeline', heading: 'Where an activity is visible', anchor: '#where-an-activity-is-visible', text: 'An activity is visible exactly where a contact captured on the same site would be: to that site alone, or to every site declared to be one sender with it, or to the whole organization when the organization has chosen an org-wide default. Logging an activity never widens what a scoped member can see, and a section of the CRM can never show an activity to somebody who could not open the record it belongs to.' },\n { path: '/content-and-data/crm/activities', title: 'Activities & the timeline', heading: 'The recent activity feed', anchor: '#the-recent-activity-feed', text: 'The Contacts section shows the newest activity logged across the CRM under the list — the last few calls, emails, meetings and notes anyone on the team filed against any record — each linking to the record it is about. It is a glance at what the team has been doing; the record\\'s own page is where the whole log lives.' },\n { path: '/content-and-data/crm/activities', title: 'Activities & the timeline', heading: 'Related', anchor: '#related', text: '- CRM overview\\n- The contact record — the page a contact\\'s timeline lives on\\n- Tasks & follow-ups — what is owed, as opposed to what happened\\n- Email templates & snippets — the letters Send email starts from, and the merge fields it fills in\\n- Automations for the CRM — the Log a CRM activity step, and how a Send an email step lands on the timeline\\n- Sending domains — the identity a one-to-one email leaves on\\n- REST API — activities' },\n { path: '/content-and-data/crm/automations', title: 'Automations for the CRM', heading: '', anchor: '', text: 'Automations for the CRM\\nThe actions builder and workflows can start on what happens in the CRM, and act on it. Every event below is announced by the server path that performed the write, so a record written straight into the database — by hand, by an import, or over the REST API — announces nothing; what each event carries is in CRM events.' },\n { path: '/content-and-data/crm/automations', title: 'Automations for the CRM', heading: 'The events', anchor: '#the-events', text: '| In the trigger picker | Fires when | Read more |\\n| Contact created | A capture on your site made a new contact. A repeat visit by somebody already on the list is an interaction, not a new contact. The event carries the lifecycleStage the capture set — lead for a form or a booking request, subscriber for a sign-up or a newsletter opt-in, customer for an order — so a filter can pick the form captures out of the sign-ups — and formId when the capture came through a form, so a condition can pick one form\\'s people out of every other door\\'s. | CRM events |\\n| Contact changed stage | A contact\\'s lifecycle stage was moved — from the contact\\'s page, from Set stage on the contacts table, or by another automation. Setting the stage a contact already has fires nothing. | Lifecycle stages |\\n| Deal moved | A deal moved between open stages, or was reopened. | Moving, winning and losing |\\n| Deal won | A deal was marked won. | Moving, winning and losing |\\n| Deal lost | A deal was marked lost; the reason given travels with the event. | Moving, winning and losing |\\n| CRM task completed | A task was ticked done. Reopening a task fires nothing. | Completing and reopening |\\nPick one and the Filter field\\'s helper text lists the keys the event puts in scope, so a filter such as lifecycleStage == \"customer\" or a condition such as stageId equals negotiation can be written without leaving the editor.' },\n { path: '/content-and-data/crm/automations', title: 'Automations for the CRM', heading: 'The steps', anchor: '#the-steps', text: 'Plan availability The five CRM steps are part of the CRM, included from Starter. On a workspace whose plan does not include it, a step that reaches the CRM does nothing and the run history records why — the plan that carries the steps is named — the same way a webhook step reports the plan it needs. See The CRM.\\nFive server steps act on the CRM: Set the contact\\'s lifecycle stage, Tag the contact, Assign the contact an owner, Create a CRM task and Log a CRM activity. Each acts on the contact the triggering event names — by contactId when the event carries one, otherwise by the email in the event\\'s data — and does nothing, with the reason in the run history, when the event names nobody this site can see. Fields and behavior are in CRM steps.\\nAssigning an owner, or rotating one\\nAssign the contact an owner has two modes. A team member names one person by email address, matched against the workspace roster when the automation runs. Round robin hands the contact to the next member of the pool kept under CRM → Settings, moving the rotation on — the same pool an assignment rule\\'s round robin draws from, so a rule and an automation share one rotation.\\nEither mode reassigns: an automation that assigns an owner means to, so a contact that already has one is handed over, and the site\\'s lead for the same person follows. Naming the owner the contact already has changes nothing. The new owner gets a console notification, Contact assigned to you, linking to the contact.\\nThis is the deliberate counterpart of the assignment rules, which run on capture and only for a contact with no owner. Use a rule to decide who gets a new contact; use this step to move a contact when something happens to them — a stage change, a won deal.' },\n { path: '/content-and-data/crm/automations', title: 'Automations for the CRM', heading: 'An automated email on the timeline', anchor: '#an-automated-email-on-the-timeline', text: 'The Send an email step is not a CRM step — it mails whichever address the event carries — but when that address is the contact the event is about, the message is logged on the contact\\'s timeline as a sent email, exactly as a teammate\\'s Send email is: with its subject, the address it went to, and a delivery state that follows the message. So a welcome sequence shows on the person\\'s history beside the calls a rep made.\\nNothing is logged when the step writes to somebody else — an internal alert sent to your own address through the To field — or when the address belongs to nobody this site can see. The row needs the CRM like the five steps above, and a record already at the activity ceiling gets the email without the entry. Automated email is metered as usage and is not counted against the one-to-one email allowance, which is for what people send by hand.' },\n { path: '/content-and-data/crm/automations', title: 'Automations for the CRM', heading: 'Recipes', anchor: '#recipes', text: 'The common CRM automations do not have to be built from scratch. Beside Add action on Automation → Actions, the Recipes menu lists four ready-to-edit actions. Choosing one opens the action editor already filled in — name, trigger, conditions and steps — with a line saying which recipe it started from. Change anything, then save; nothing is saved until you do, and a recipe closed without saving leaves no trace.\\n| Recipe | Starts on | What it builds |\\n| Welcome a new lead | Contact created, with the condition source equals form | Assign the contact an owner on Round robin (the pool under CRM → Settings; with no pool the step fails, the run carries on, and the run history says so), then Create a CRM task — a call, due in 1 day, assignee blank so it goes to the owner just chosen — then Send an email thanking them (sent from your workspace\\'s identity to the address the event carries, as an immediate reply rather than marketing, and logged on the contact\\'s timeline), then Tag the contact website. |\\n| Follow up a won deal | Deal won | Create a CRM task — a call, due in 7 days, to the contact\\'s owner. No stage step: a won deal makes its contact a customer on its own before the event fires, and a step that set the stage here would move an evangelist back. |\\n| Re-engage a stale lead | Contact changed stage, with the condition lifecycleStage equals lead | Wait for something to happen — the next Contact changed stage for this person, giving up after a week — then Create a CRM task (a call, due in 1 day) with the step condition waitTimedOut is not empty, so the call is booked only when the week ran out. A lead whose stage moved on in the meantime skips it. |\\n| Tag by form | Contact created, with the condition formId equals the form you pick | Tag the contact with the form\\'s name. Th' },\n { path: '/content-and-data/crm/automations', title: 'Automations for the CRM', heading: 'Example: tag every new contact from a form', anchor: '#example-tag-every-new-contact-from-a-form', text: '1. Open Automation → Actions and choose Add action.\\n2. Trigger event: Contact created. To narrow it to forms, add the condition source equals form.\\n3. Do: Tag the contact, with the tag website.\\n4. Save. Submit a form on your site from an address the workspace has not seen before, then open the contact under CRM → Contacts: the tag is on it.\\nA stage set by an automation is a stage change like any other, so a second action on Contact changed stage with the condition lifecycleStage equals customer can create the follow-up task.' },\n { path: '/content-and-data/crm/automations', title: 'Automations for the CRM', heading: 'Example: spread qualified leads across the team', anchor: '#example-spread-qualified-leads-across-the-team', text: '1. Trigger event: Contact changed stage, with the condition lifecycleStage equals sales-qualified.\\n2. Do: Assign the contact an owner, set to Round robin.\\n3. Under CRM → Settings → Round robin, tick the members who work qualified leads.\\n4. Save. Each contact that reaches Sales qualified goes to the next member in turn, who is notified, and the rotation shows who is next up.' },\n { path: '/content-and-data/crm/automations', title: 'Automations for the CRM', heading: 'Example: follow up on a won deal', anchor: '#example-follow-up-on-a-won-deal', text: '1. Trigger event: Deal won.\\n2. Do: Set the contact\\'s lifecycle stage to Customer, then Create a CRM task — kind Call, due in 7 days, assignee left blank so it goes to the contact\\'s owner.\\n3. Save. Mark a deal won from its page or the board: the contact it names moves to Customer, and a call is owed to them a week out.' },\n { path: '/content-and-data/crm/automations', title: 'Automations for the CRM', heading: 'Related', anchor: '#related', text: '- Actions builder\\n- Build a workflow\\n- CRM settings — the default owner, assignment rules and the round-robin pool\\n- CRM overview\\n- Deals pipeline · Tasks & follow-ups · Activities & the timeline' },\n { path: '/content-and-data/crm/bulk-actions', title: 'Bulk actions', heading: '', anchor: '', text: 'Bulk actions\\nEvery table in the CRM has a checkbox on each row. Tick one or more — or the checkbox in the header for the whole page — and a bar appears above the table saying how many are selected, with one action for all of them. The rows on offer are the ones the open view shows, so narrowing to \"my leads in Texas\" first and ticking the header checkbox acts on exactly those. Each section\\'s bar offers what that record can do; every bar has Export CSV, Export all… and Clear.\\nPlan availability Bulk actions are part of the CRM, included from Starter. On Free every CRM section is shown locked, and no bar is offered. The complete contacts and leads files that Export all… writes are on every plan under Settings → Privacy. See What each plan includes.' },\n { path: '/content-and-data/crm/bulk-actions', title: 'Bulk actions', heading: 'Two exports, and the difference matters', anchor: '#two-exports-and-the-difference-matters', text: '- Export CSV writes the rows on screen — the selection, or the page the table has loaded. Instant, and exactly what you are looking at.\\n- Export all… writes the whole collection, whatever the view is narrowed to and however many rows there are. The server streams it, so a workspace with forty thousand contacts gets forty thousand rows.\\nBoth files have the same columns in the same order, so the two open the same way in a spreadsheet.\\nExport all… checks itself. The server counts the collection before it starts sending and states that number in the response; if fewer rows arrive than were promised — a connection dropped mid-download — nothing is saved and you are told how many of how many arrived. A half-written export that looks complete is worse than no export, because nothing about a shorter file says it is short.\\nA complete export writes what the SERVER can resolve: owners and assignees by email address, a deal\\'s pipeline and stage by name, and a task\\'s contact, company and deal by name. Past the first two thousand linked records a task file writes the record\\'s id instead.\\nWho can take one: the same people who can open the CRM — the surface has to be available to you and your role must be able to manage data. A collaborator scoped to some of the workspace\\'s sites gets their rows and not the workspace\\'s, enforced on the server rather than in the browser.' },\n { path: '/content-and-data/crm/bulk-actions', title: 'Bulk actions', heading: 'Contacts', anchor: '#contacts', text: '| Action | What happens |\\n| Add tag | Type a tag; it is added to every selected contact that does not already have it. Tags are lowercased, the same as on a contact\\'s profile. A contact already holding 20 tags is skipped and named. |\\n| Remove tag | Type a tag; it is removed from every selected contact that has it. Contacts without it are left alone. |\\n| Set owner | Pick a team member. Every selected contact is assigned to them. Choose Nobody to clear the owner. |\\n| Set stage | Pick a lifecycle stage — subscriber, lead, marketing qualified, sales qualified, opportunity, customer, evangelist or other. Each contact is moved through the server, one at a time, so every move fires Contact changed stage exactly as a move on the contact\\'s page does; a contact already at that stage fires nothing. A contact the move is refused for is named. |\\n| Set company | Pick a company — the same picker a contact\\'s record has, with a Create row for a name nobody has filed yet. Every selected contact is linked to it, and its name is written to their records. Leave the picker empty to unlink the selection. A contact already at that company is left alone. |\\n| Add to list | Put the selected contacts on an email audience. This runs the same check the audience\\'s own page runs — see below. |\\n| Export CSV | Download the selected rows as contacts-selected.csv — the same file the table\\'s own Export CSV writes over the whole page. See the contacts file. |\\n| Remove from this site | After a confirmation, the selected contacts leave this site\\'s CRM. A person another site in your workspace also captured keeps that site\\'s record; a person only this site held is deleted. This is not a privacy erasure: the person\\'s form submissions, orders, bookings and membership records are separate and are deleted from thei' },\n { path: '/content-and-data/crm/bulk-actions', title: 'Bulk actions', heading: 'Companies', anchor: '#companies', text: '| Action | What happens |\\n| Add tag / Remove tag | As on contacts: lowercased, up to 20 per company, a company at the cap skipped and named. A company\\'s tags show in the list and on its page, and its form has a Tags field. |\\n| Set owner | Pick a team member, or Nobody to clear the owner. |\\n| Export CSV | Download the selected companies as companies-selected.csv — see Export CSV, whose header is the companies import\\'s own. |\\n| Delete | After a confirmation, each selected company is unlinked from its contacts and deleted, one after another, and each deletion is logged in the site\\'s activity feed. A company with more than 500 linked contacts is unlinked from 500, kept, and named — delete again to continue. |' },\n { path: '/content-and-data/crm/bulk-actions', title: 'Bulk actions', heading: 'Deals', anchor: '#deals', text: 'The bar appears on the deals table (switch from the board with the Board / Table control).\\n| Action | What happens |\\n| Set stage | Pick a stage of the selected deals\\' pipeline — an open stage or Won. Each deal is moved through the server, one at a time, so every move fires its dealStageChanged or dealWon event exactly as a drag on the board does. A selection spanning two pipelines has no one list of stages, and the dialog says so. |\\n| Set owner | Pick a team member, or Nobody to clear the owner. |\\n| Mark lost | Asks for one reason, then marks each deal lost through the server; the reason is kept on every deal and sent with every dealLost event. |\\n| Export CSV | Download the selected deals as deals-selected.csv — the same file the table\\'s Export CSV writes: title, pipeline and stage by name, the amount in major units with its currency, the owner by email address, the expected close date, status, the contact and the company, when it closed, the lost reason and notes. |\\n| Delete | After a confirmation, the selected deals are deleted; the contacts and companies they name are untouched. Each deletion is logged in the site\\'s activity feed. |' },\n { path: '/content-and-data/crm/bulk-actions', title: 'Bulk actions', heading: 'Tasks', anchor: '#tasks', text: '| Action | What happens |\\n| Complete | Each open selected task is completed through the server, one at a time, so every one fires its taskCompleted event. Tasks already done are left alone. |\\n| Assign | Pick a team member, or Nobody to clear the assignee. Each task is saved through the server, so the new assignee gets the same notification the drawer sends — assigning to yourself sends nothing. |\\n| Set due | Pick a date and time for every selected task, or leave it empty to clear the due date. |\\n| Export CSV | Download the selected tasks as tasks-selected.csv — the same file the list\\'s Export CSV writes for the view on screen: title, kind, priority, status, the due date and the completion as timestamps, the assignee by email address, the contact, company and deal by name, and notes. |\\n| Delete | After a confirmation, the selected tasks are deleted for everyone who can see them. A finished task is better ticked done, which keeps it in the Done view. |' },\n { path: '/content-and-data/crm/bulk-actions', title: 'Bulk actions', heading: 'Leads', anchor: '#leads', text: '| Action | What happens |\\n| Set owner | Pick a team member, or Nobody to clear the owner. The lead alone is assigned; its contact, when there is one, is assigned from its own record. |\\n| Set status | Pick New or Working. A lead already at that status is skipped and named, and a converted lead is left alone — its status is its conversion. Setting a closed lead back to Working reopens it and clears its reason, as the row does. |\\n| Unqualify | Asks for one reason, then closes every open selected lead with it; the reason is kept on each so the lead funnel can count it. Leads already closed or converted are skipped and named. |\\n| Export CSV | Download the selected leads as leads-selected.csv — the same file the list\\'s Export CSV writes: email, name, status, the owner by email address, the sources by name joined with \\\\|, first and last seen, how many times the person was captured, the unqualified reason, when the lead converted, and notes. The list\\'s Import CSV reads the columns a file may set back in; the capture columns are its to write, not yours. |\\n| Clear | Deselect everything. |\\nA lead\\'s owner and status are the team\\'s own notes on a capture — no event and no notification fire — so the bar writes them directly, the way the row\\'s inline controls do.' },\n { path: '/content-and-data/crm/bulk-actions', title: 'Bulk actions', heading: 'At the organization level', anchor: '#at-the-organization-level', text: 'The same bars, over every site\\'s rows at once, on the organization\\'s hub. Each action writes one line into the organization\\'s activity feed (Team → Activity) — \"Owner set on 3 deals\" — where a site\\'s hub writes into the site\\'s own feed. A stage move or a loss over deals is recorded per deal instead, by the move itself. The leads bar writes each lead under the site that captured it, and its export gains a Site column after Owner, naming which — and Export all… there covers every site\\'s leads in one file, up to two hundred sites.\\nExport all… for contacts writes each person through their primary holder at the organization level — the first site that captured them — which is the profile the organization\\'s own table shows. Under a site it writes that site\\'s, as the table does.\\nThe tasks bar\\'s Complete and Assign run there as one request for the whole selection, authorized by the organization rather than task by task against each task\\'s site; every task is still answered on its own, so one that was deleted while the page was open is named in the notice and the rest go through. Completing fires each task\\'s event on the site it was created from — none for an organization task.' },\n { path: '/content-and-data/crm/bulk-actions', title: 'Bulk actions', heading: 'When a row cannot be changed', anchor: '#when-a-row-cannot-be-changed', text: 'A bulk action writes in batches. If any row in the selection cannot be written — usually because your access is scoped to certain sites and the record belongs elsewhere, or because it was deleted while the page was open — the rest are still written, and the ones that were not appear by name (a contact\\'s email address, a company\\'s name, a deal\\'s or a task\\'s title) in a notice under the bar, with the reason. Dismiss the notice when you have read it. A row the action skipped on purpose (already tagged, already at the tag limit, already done) is listed the same way. An action that goes through the server — a stage move, a completion, an assignment — reports the server\\'s own sentence for each refused row.' },\n { path: '/content-and-data/crm/bulk-actions', title: 'Bulk actions', heading: 'Adding people to an audience', anchor: '#adding-people-to-an-audience', text: 'Add to list on the contacts bar opens a dialog that:\\n1. Lets you pick an audience from your workspace\\'s lists. Audiences are managed by members with access to every site in the workspace; if that is not you, the dialog says so instead of showing an empty list.\\n2. Checks every selected address against its consent record and both suppression lists, and tells you how many already opted in, how many have no opt-in on record, and how many cannot be added at all (with the reason, per address).\\n3. Offers Add only after the check. People with no opt-in on record are added only if you tick the attestation that you have their permission — the same statement the audience page asks for, recorded against your account with the date.\\n4. Names anyone who was not added, and why.\\nA large selection is checked and added a hundred addresses at a time; the counts you see are for the whole selection. A person\\'s own page in the CRM has the same Add to list button for one contact.' },\n { path: '/content-and-data/crm/bulk-actions', title: 'Bulk actions', heading: 'Related', anchor: '#related', text: '- CRM overview\\n- The contact record — the same tags, owner and stage, one person at a time\\n- Import contacts from CSV and import companies — the files an export re-imports as\\n- Companies, Deals pipeline, Tasks & follow-ups, Leads — each section\\'s records\\n- Email audiences — including audiences built from a rule, which can target a contact\\'s owner, lifecycle stage, company and custom fields.' },\n { path: '/content-and-data/crm/companies', title: 'Companies', heading: '', anchor: '', text: 'Companies\\nA company is the organization behind one or more of your contacts. Where a contact is a person, a company is the account they work for: it has a domain, an owner on your team, an address and notes of its own, and a list of the people at it. The Companies section lives in the CRM hub at …/hosts/{site}/crm/companies.\\nPlan availability Companies are part of the CRM, included from Starter. On Free the section is shown locked, with the rest of the CRM. A company is a CRM record, counted with contacts and deals against your plan\\'s records band — see CRM records.' },\n { path: '/content-and-data/crm/companies', title: 'Companies', heading: 'The companies list', anchor: '#the-companies-list', text: 'The list shows every company your site may see, newest activity first, with its domain, how many contacts are linked to it, its owner, when it was last changed and its next activity — when the earliest open task against it is due, with a No next activity chip beside the view control to keep only the companies with nothing scheduled. Open the column filter on Company to find one by name — the search reaches every company, not only the page on screen — and filter Owner to see one teammate\\'s accounts. Clicking a row opens the company\\'s page.\\nThe Contacts column is a count kept on the company and moved with every link and unlink, so a page of companies costs no lookup per row. A company linked before the count existed can read lower than its page shows; the company\\'s own page counts live, and is the figure to trust when the two differ.' },\n { path: '/content-and-data/crm/companies', title: 'Companies', heading: 'Create a company', anchor: '#create-a-company', text: 'Choose New company above the list. A company needs a name; everything else is optional:\\n- Domain — the bare hostname (acme.com). Anything pasted with it — a protocol, www., a path — is stripped, because the domain is a key: it is what suggests a company for a contact from their email address.\\n- Website, phone, industry, address and notes.\\n- Tags — comma-separated, lowercased, up to 20; the same kind of tag a contact carries, shown in the list and set on many companies at once from the bulk bar.\\n- Owner — the member of your team responsible for the account. It defaults to you.\\nThe company is saved and its page opens.' },\n { path: '/content-and-data/crm/companies', title: 'Companies', heading: 'A company\\'s page', anchor: '#a-companys-page', text: 'The page names the company in the heading and the trail, and holds its properties, its contacts, its deals, its open tasks and the activity logged against it. The header of the first card carries the domain under the company\\'s kind, the industry and the owner as chips, Back to companies, Call and Log a call (see click to call), and Edit, which opens the same form the company was created with; Delete company is in the header\\'s menu (⋮). The company\\'s phone number is a link on the properties card too. Every CRM record page — contact, company, deal and lead — is headed the same way.\\nUnder the fixed properties the card lists every custom field defined on the Companies tab of the Fields section, and Edit carries a control for each, so a company\\'s own properties are saved in the same form as its name and domain.' },\n { path: '/content-and-data/crm/companies', title: 'Companies', heading: 'Contacts at a company', anchor: '#contacts-at-a-company', text: 'The Contacts card lists the people linked to this company a page at a time, most recently updated first, with the total counted by the database in its footer; each row opens the person\\'s page. Add contact finds a person by email address or by name among the contacts your site may see and links them. A contact belongs to one company at a time from your site\\'s point of view; linking them to a second company moves them.\\nThe link can be made from either side. On a contact\\'s own page the Company field of the Properties card is a picker over the companies your site may see — type to search by name or domain, choose one, or type a name nobody has filed yet and choose Create to make the company on the spot. A company made this way is a new record like one made from the list: it counts against the records band, and on a plan whose band is a hard limit it is refused at the band with the same message. Clearing the field unlinks the person. A contact whose record carries a company name but no link — from an import, or from before the picker existed — is offered that name as the company to link or create. The contacts table\\'s bulk bar has Set company for many people at once.\\nA lead converted with a company email address is offered the company whose domain matches it, and a CSV import that names a company links each row to it — matching an existing company by name, or creating one.' },\n { path: '/content-and-data/crm/companies', title: 'Companies', heading: 'Linked on capture', anchor: '#linked-on-capture', text: 'A contact created by a capture — a form, a sign-up, an order, a booking — with a work email address is linked to the company at that address\\'s domain on its own, the moment the contact is created. jane@acme.com is filed under the company whose Domain is acme.com, which is why the domain is worth filling in on every company.\\nThree things have to be true:\\n- Exactly one company visible to the capturing site carries the domain. Two companies at one domain is an ambiguity the capture does not resolve by picking one; the contact waits for a person.\\n- The contact is new to the workspace. A repeat visit by somebody the address book already holds is another interaction, not a new person, and is not re-filed.\\n- The capture did not already name a company — a contact added by hand with a company picked, or an imported row with a company column, keeps the company you chose.\\nAddresses at a public mailbox (Gmail, Outlook, iCloud and the like) never link. When no company carries the domain, nothing is created unless the workspace has turned on Create companies from work email domains in CRM settings, in which case the company is created from the domain and the contact linked to it.' },\n { path: '/content-and-data/crm/companies', title: 'Companies', heading: 'Import from CSV', anchor: '#import-from-csv', text: 'Import CSV, above the list, takes a spreadsheet of companies — an export from another CRM, an account list — and files each one. A company already in your list is updated rather than added twice: a row is matched to an existing company by its domain first, and by its name when the row has no domain or no company carries it. Importing needs the same Manage data permission as creating a company.\\nThe three steps are the ones the contacts import walks: choose the file (up to 5,000 rows), match its columns — Aglyn proposes a match from the header names and shows the first row\\'s value beside each — check the ten-row preview, then import in batches of 200 with a progress bar and a result that says how many were added, updated and skipped, with the skipped rows downloadable as a CSV that says why. Download template hands you the export\\'s own header over no rows, so a sheet filled in against it maps itself.\\n| Field | What is read |\\n| Company name | Required. A row without one is skipped. |\\n| Domain | The bare hostname (acme.com); a URL or www. is stripped. What a row is matched on first. A cell that is not a hostname is dropped and reported. |\\n| Website, phone, industry, notes | As on the company form; a phone that cannot be read is dropped and reported. |\\n| Owner | The email address of a member of your organization. An address that matches nobody leaves the company without an owner, and the result names those addresses. |\\n| Address line 1, line 2, city, state, postal code, country | The postal address. Country must be a two-letter code. |\\n| Tags | Separated by , or \\\\|, lowercased. Added to any tags the company already has. |\\n| Custom fields | Every field defined on the Companies tab of Fields is offered as a target, and a header that matches a field\\'s label or key is proposed f' },\n { path: '/content-and-data/crm/companies', title: 'Companies', heading: 'Export CSV', anchor: '#export-csv', text: 'Export CSV, above the list, downloads the companies on screen as companies.csv; the bulk bar\\'s Export CSV downloads the selected rows as companies-selected.csv. Both write the same file: every column above plus the contacts count, with the owner written as their email address, under the header the import reads — so an export re-imports without a hand mapping, and the count maps to nothing.' },\n { path: '/content-and-data/crm/companies', title: 'Companies', heading: 'Deleting a company', anchor: '#deleting-a-company', text: 'Deleting a company unlinks it from every contact first, so no contact is left pointing at a record that no longer exists. Up to 500 contacts are unlinked in one pass; a company with more than that reports how many remain, and deleting again continues from where it stopped. The contacts themselves are untouched. The list\\'s bulk bar deletes a selection the same way, one company after another.' },\n { path: '/content-and-data/crm/companies', title: 'Companies', heading: 'Who can see a company', anchor: '#who-can-see-a-company', text: 'A company follows the same per-site visibility as your contacts. It is created in the scope a contact captured on the same site would land in — the whole workspace when the workspace shares its data, and otherwise the sites that present as one sender — so a section of the CRM can never show a company to a reader who could not open the contacts at it.' },\n { path: '/content-and-data/crm/companies', title: 'Companies', heading: 'Files', anchor: '#files', text: 'The Files card attaches assets from the media library to this record: contracts, quotes, a signed proposal, a photo of the site.\\n- Attach… opens the media browser. At a site you can pick from the site\\'s own library and the workspace\\'s shared one; at the organization hub only the shared library is offered, because a record there belongs to the workspace rather than to one site.\\n- A record holds up to 20 files.\\n- The ✕ beside a file removes the attachment. The file itself is untouched and stays in the library.\\nFiles are stored by id, not by address. A file moved between folders keeps its attachment, and a private file is still served through the signed link that checks who is asking rather than through a public URL. A file deleted from the library is still listed, marked as no longer there, so an attachment never disappears without saying so.\\nAttachments are readable and writable through the REST API as mediaIds.' },\n { path: '/content-and-data/crm/companies', title: 'Companies', heading: 'Related', anchor: '#related', text: '- CRM overview\\n- The contact record — the people a company is made of\\n- CRM settings — whether a capture creates the company it could not find\\n- Deals pipeline — every deal names the company it is with\\n- Email campaigns — audiences built from a rule can target a contact\\'s company\\n- REST API — companies' },\n { path: '/content-and-data/crm/contact-record', title: 'The contact record', heading: '', anchor: '', text: 'The contact record\\nEvery person in your CRM has a record: the email that identifies them, the profile your team keeps on them, where they came from, what they are filed under, and what the site recorded about them. You can add a person by hand, and edit every part of that profile from their own page.\\nPlan availability The contact record is part of the CRM, included from Starter: reading a contact, adding one by hand, and every edit to one — their name, phone, job title and address, tags and notes, owner, lifecycle stage and company, custom fields, tasks, deals, files, logged calls and activities, one-to-one email and merging two records. On Free the Contacts section is shown locked, with the rest of the CRM. The people your sites capture are still kept, and workspace owners and admins can export them, or erase a person, from Settings → Privacy. See What each plan includes.' },\n { path: '/content-and-data/crm/contact-record', title: 'The contact record', heading: 'Adding a contact by hand', anchor: '#adding-a-contact-by-hand', text: 'Open CRM → Contacts and choose New contact. A drawer opens over the list — the list itself stays exactly where it was.\\n| Field | Notes |\\n| Email | Required. It is the one thing every site shares about a person and what makes two captures one record. |\\n| Name | Optional. Your own name for the person; the record also keeps the name they gave on a form. |\\n| Phone | Entered with its country code, like +1 512 555 0107. Stored in E.164 so the same number typed two ways is one number. |\\n| Job title | Free text. |\\n| Company | A picker over the companies your site may see. Type to search by name or domain; a name nobody has filed yet gets a Create row that makes the company and selects it. Once the email is typed, a company whose domain matches it is suggested beneath the field — offered, never applied. Leave it empty for no company. |\\n| Lifecycle stage | Where the person sits in your funnel; see Lifecycle stages. |\\n| Owner | The team member responsible for the relationship, picked from your workspace roster. |\\n| Tags | Comma-separated. Tags are lower-cased and deduplicated, so VIP and vip are one tag. |\\n| Address | Street, city, state or region, postal code and a two-letter country code. |\\n| Opted in to marketing email | Tick it only if the person actually agreed. Adding a contact is never itself consent, and the checkbox says so. |\\nIf the email already belongs to a contact, nothing is duplicated: what you typed merges into the existing record and the page tells you so. On a plan whose records band is a hard limit, a full band refuses the new record with the same wording the list shows.\\nAdding a contact by hand is part of the CRM, included from Starter. On Free the Contacts section is shown locked, with the rest of the CRM.' },\n { path: '/content-and-data/crm/contact-record', title: 'The contact record', heading: 'The record page', anchor: '#the-record-page', text: 'Clicking a row in the list opens the person\\'s own page at …/crm/contacts/{id} — an address you can paste and that every other CRM record links to. The page opens with the person\\'s name, their lifecycle stage, their owner, when they last engaged with one of your campaigns, and — for a buyer — how many orders they have placed and what they have spent; the order count is a link to the site\\'s orders list narrowed to the person\\'s address. Beside Send email it carries Call, which dials the person\\'s number, and Log a call — see click to call. Under it, one card per aspect of the record:\\n- Properties — the editable profile: name, phone (with a dial button beside it, see click to call), job title, company, lifecycle stage, owner, tags, address and an About box for your team\\'s notes. One Save writes everything at once. The email is shown but cannot be edited here, because it is the shared identity. The Company field is the same picker the New contact drawer has: search the companies your site may see, create one by typing its name, or clear the field to unlink the person. A record that carries a company name with no link — an import, or a save from before the picker — shows the name as a note beneath the field, with one click to link the company of that name or create it. Changing the company here moves the count on the companies list with it.\\n- Relationship — the sources that created the record (a form, a checkout, a booking, an import, or by hand), the campaign attribution recorded from the link the person followed, the marketing email basis this site holds (opted in, opted out, or no record — the last is not a refusal), and the campaigns your team has filed the person under. Filing is your own bookkeeping; it never adds anyone to a send.\\n- Custom fields — one control per field' },\n { path: '/content-and-data/crm/contact-record', title: 'The contact record', heading: 'Deleting and erasing', anchor: '#deleting-and-erasing', text: 'Delete contact removes the person from this site\\'s CRM — its notes, tags and timeline. Other sites in your workspace that captured the same person keep their own records, and the person\\'s form submissions, orders, bookings and membership records are separate and are deleted from their own pages. It is a housekeeping act, and it is not a privacy erasure.\\nErase this person is the privacy erasure — what you use when someone asks to be forgotten under GDPR, the CCPA or any similar law. It removes the person from every site in the workspace, whoever captured them, and it cannot be undone. Only a workspace admin or owner can do it; for everyone else the item is present but disabled, and its tooltip says who can. It is offered on a site\\'s hub and on the organization\\'s alike. The same erasure can be filed by address, without opening a record, from Settings → Privacy — on every plan, including Free, where the CRM is locked.\\nChoosing it opens a dialog that lists exactly what will happen, and asks you to type the person\\'s email address back before it files anything:\\n| | |\\n| Removed across the workspace | The contact record on every site — profile, notes, tags, timeline and custom fields; every lead for the address on every site; the tasks and logged activities filed against the person; the person on any email audience list; the campaign delivery log for the address, with its opens and clicks. |\\n| Kept, with the person taken off | Orders and bookings stay as financial and appointment records with the name, email, phone and addresses removed. Deals stay on the pipeline, unlinked from the person. Companies stay, with their contact count reduced. |\\n| Not reached — finish these by hand | Form submissions, which keep the address inside the answers under whatever the form called the fiel' },\n { path: '/content-and-data/crm/contact-record', title: 'The contact record', heading: 'What each site keeps to itself', anchor: '#what-each-site-keeps-to-itself', text: 'A contact document is shared by every site in your workspace — one human who touched two of your sites is one row. Almost nothing on it is shared: the notes, tags, timeline, phone, title, company, address, owner and stage are one site\\'s knowledge of the person and are never shown to another site\\'s console. The email and the name the person gave are the only shared identity.' },\n { path: '/content-and-data/crm/contact-record', title: 'The contact record', heading: 'Merging two records', anchor: '#merging-two-records', text: 'A contact is one record per email address, so the same person cannot be captured twice under one address — but one human with a work address and a personal one is two records, with tags, deals, tasks and a timeline split between them. Merge into…, in the overflow menu on either record, folds the two into one.\\nThe dialog first finds the other record — type an email address for an exact match, or a name to search among the contacts your site may see — and then shows the two side by side, field by field, with the value the kept record will carry. Pick which record to keep; the other is merged into it and deleted. At the organization level the search spans every site and a record no site captured merges too. The rule is the same for every field:\\n| What | After the merge |\\n| Name, phone, job title, company, stage, owner, address, custom fields | The kept record\\'s value where it has one; an empty field fills from the other record. Nothing the kept record holds is overwritten. |\\n| Tags, campaign filings | Combined. |\\n| Timeline | Combined and ordered, newest first. The other record\\'s calls, notes and logged activities move across too. |\\n| Notes | The other record\\'s notes are added below the kept record\\'s. |\\n| Orders and lifetime value | Added together — two records of one person\\'s purchases are one person\\'s purchases. |\\n| Deals, tasks, activities, and a converted lead | Repointed at the kept record. |\\n| Companies | The kept record is filed under every company either record named. |\\n| Marketing email | A site\\'s opt-in on either record stands on the kept one. A recorded opt-out on either record stands too: the merge says these are one person, and that person said no. |\\n| Email address | The kept record\\'s address stays the identity. The other record\\'s address is recorded as an al' },\n { path: '/content-and-data/crm/contact-record', title: 'The contact record', heading: 'Owner', anchor: '#owner', text: 'The Owner is the team member responsible for the relationship. The contacts list has an Owner column and an Assigned to me toggle that narrows the list to the people assigned to you; the record page shows the owner beside the heading and lets you reassign.' },\n { path: '/content-and-data/crm/contact-record', title: 'The contact record', heading: 'Last engaged', anchor: '#last-engaged', text: 'Last engaged is when the person last opened or clicked a campaign email sent by this site, or by a site declared to be one sender with it. The record page shows it beside the heading; the contacts list carries it as an optional Last engaged column, off until you turn it on from the column menu, and the CSV export always includes it as lastEngaged.\\nIt is set by the delivery webhook on the first open and the first click of each campaign email, so a reader opening one newsletter six times moves it once, and it only ever moves forward. Account mail — a receipt, a booking confirmation — never moves it, and neither does a campaign another business in your workspace sent the same person: it is your relationship\\'s pulse. Campaign mail opened before the stamp existed did not set it, so a long-standing reader can show no value until their next open.\\nA list built from a rule can select on it — Engaged with a campaign within (days) — which is how a re-engagement audience is built.' },\n { path: '/content-and-data/crm/contact-record', title: 'The contact record', heading: 'Lifecycle stages', anchor: '#lifecycle-stages', text: 'The Stage says where a person is in your funnel. The list has a Stage column and a Stage filter; the record page lets you set it.\\nEvery capture on your site sets the earliest stage that describes what happened, and never moves anyone back: a form submission makes a Lead, a newsletter opt-in or a member sign-up makes a Subscriber, a booking makes a Lead — a Customer once it is paid — an order makes a Customer, and so does a deal marked won with the person on it. A subscriber who then submits a form becomes a lead; a customer who submits one stays a customer. A contact you add by hand, import, or create over the API gets the stage you give it, or none, and a contact captured before this rule carries the stage its earlier captures imply.\\n| Stage | Meaning |\\n| Subscriber | Signed up to hear from you and nothing more yet. Set by a newsletter opt-in or a member sign-up. |\\n| Lead | Showed interest — filled in a form, asked a question. Set by a form submission or a booking request. |\\n| Marketing qualified | Engaged enough for marketing to hand over. |\\n| Sales qualified | A real prospect somebody is working. |\\n| Opportunity | There is a deal on the table. |\\n| Customer | Has bought. A purchase on your site, or a deal with them marked won, sets this automatically for anyone at an earlier stage, and never moves anyone back. |\\n| Evangelist | A customer who sends others your way. |\\n| Other | A step of your own that none of the names fit. A sale never overwrites it. |\\nWhere the person\\'s lead is\\nThe Relationship card shows Lead on this site when this site holds a lead for the address — a sign-up, a booking, or a submission to a form with lead routing on. It opens the lead. A contact that came in through an order, an import or a form without lead routing has no lead, and the link is sim' },\n { path: '/content-and-data/crm/contact-record', title: 'The contact record', heading: 'Finding a contact', anchor: '#finding-a-contact', text: 'The list\\'s search and column filters reach every contact in the workspace by name and email. The console\\'s search (the magnifying glass in the top bar) finds contacts too — by name, email, phone number or company name — and opens the person\\'s page. Contacts appear in that search only for members who can manage data on the site, and only while the CRM is available to them.\\nThe phone number and company name are kept on the record itself for that lookup, as this site last saved them; the values you see on a person\\'s page are always your own site\\'s.' },\n { path: '/content-and-data/crm/contact-record', title: 'The contact record', heading: 'Files', anchor: '#files', text: 'The Files card attaches assets from the media library to this record: contracts, quotes, a signed proposal, a photo of the site.\\n- Attach… opens the media browser. At a site you can pick from the site\\'s own library and the workspace\\'s shared one; at the organization hub only the shared library is offered, because a record there belongs to the workspace rather than to one site.\\n- A record holds up to 20 files.\\n- The ✕ beside a file removes the attachment. The file itself is untouched and stays in the library.\\nFiles are stored by id, not by address. A file moved between folders keeps its attachment, and a private file is still served through the signed link that checks who is asking rather than through a public URL. A file deleted from the library is still listed, marked as no longer there, so an attachment never disappears without saying so.\\nFiles are per site, like everything else on a person\\'s record: an agency running two client brands has one contact document between them, and a contract one client filed is not the other client\\'s to see. See what each site keeps to itself.\\nAttachments are readable and writable through the REST API as mediaIds.' },\n { path: '/content-and-data/crm/contact-record', title: 'The contact record', heading: 'Related', anchor: '#related', text: '- CRM overview\\n- Import contacts from CSV and Bulk actions — many records at once\\n- Companies — the account a person works for\\n- Activities & the timeline — the history on the record page\\n- Email campaigns\\n- REST API — contacts — the same record, with its per-site profile, over the API' },\n { path: '/content-and-data/crm/custom-fields', title: 'Custom fields', heading: '', anchor: '', text: 'Custom fields\\nA contact carries a fixed set of properties — name, email, tags, notes, and the CRM\\'s own owner, lifecycle stage and company; a company its domain, address and industry; a deal its amount, stage and close date. Custom fields add the properties your business describes each of them by: an annual revenue, a tier, a renewal date, a VIP checkbox, a link to a profile.\\nA field is defined once for the whole organization, for one kind of record — see fields per record. It then appears on every record of that kind, as an optional column on that kind\\'s list, and on the REST API; a contact field is also a destination a form field can save into. Definitions are shared across every site in the organization, like the records themselves; the values a site records on a contact are that site\\'s own view of the person.\\nPlan availability Custom fields are part of the CRM, included from Starter. On Free the Fields section is shown locked, with the rest of the CRM. See What each plan includes.' },\n { path: '/content-and-data/crm/custom-fields', title: 'Custom fields', heading: 'Define a field', anchor: '#define-a-field', text: 'Open CRM → Fields, pick the tab for the record the field describes — Contacts, Companies or Deals — and choose New field.\\n- Label — how the field reads everywhere it shows.\\n- Key — derived from the label as you type (Annual revenue becomes annualrevenue); edit it to choose your own. A key starts with a letter and uses lowercase letters, digits and underscores, up to 40 characters. Values are stored under the key, so it cannot change once the field exists — a rename is a new field and a retirement of the old one.\\n- Type — Text, Number, Date, Choice, Checkbox or Link. Every value is read by its type, so the type is fixed once the field exists.\\n- Choices — for a Choice field, one per line. A stored value has to be one of them.\\n- Required on the … form — the record\\'s page will not save the field empty. It does not make a website form field required; that is set on the form itself.\\nFields are listed in the order they appear on a record and in the list\\'s columns; the arrows on each row move a field up or down.\\nHow many records carry a value under each field is not counted. That would read every record in the organization each time the page opened, so the list says so rather than showing a number it cannot keep true.' },\n { path: '/content-and-data/crm/custom-fields', title: 'Custom fields', heading: 'Fields per record', anchor: '#fields-per-record', text: 'Each tab of the Fields section is its own list: a field belongs to contacts, to companies or to deals, and never to more than one. Keys are unique within a tab, so a company field and a contact field may both be called region — they are two fields, each read by its own type.\\n| Record | Where its fields show |\\n| Contacts | The Custom fields card on a contact\\'s page, optional columns and filters on the contacts list, the CSV import, a form field\\'s destination, and custom on /v1/contacts. |\\n| Companies | Rows on a company\\'s page and controls on its Edit form, optional columns on the companies list, the companies CSV import, and custom on /v1/companies. |\\n| Deals | Rows on a deal\\'s page and controls on its Edit form, optional columns on the deals table, and custom on /v1/deals. |\\nOnly a contact field can be the destination of a website form field, because a submission is a fact about a person; the drawer on the other two tabs says so. Company and deal columns show values and are not filterable, for the reason the contact columns are not: the value lives in a map no index covers.\\nFields defined before companies and deals could carry them are contact fields, and need nothing done to them.' },\n { path: '/content-and-data/crm/custom-fields', title: 'Custom fields', heading: 'Where values show', anchor: '#where-values-show', text: '- A contact\\'s page carries a Custom fields card with one control per field — a text box, a number box, a date picker, a choice list, a checkbox or a link box. Save writes only the fields you changed; clearing a control clears the value.\\n- The contacts list offers one optional column per field. The columns show the value as it reads — a date as a day, a checkbox as Yes or No, a link as a link — and are not sortable or filterable.\\n- Exports and the API carry the values under their keys.\\nA value belongs to the site that recorded it. Two sites in one organization that both know a person each see their own values, never each other\\'s.' },\n { path: '/content-and-data/crm/custom-fields', title: 'Custom fields', heading: 'Save a form field into a custom field', anchor: '#save-a-form-field', text: 'On a form\\'s own page (Forms → the form), Saves to contact fields lists every field the published design declares, with a choice beside each. Pick a custom field and every submission\\'s answer to that form field is stored under it on the contact the submission creates or updates.\\nAnswers arrive as text and are converted by the field\\'s type:\\n| Field type | What is stored |\\n| Text, Link | The trimmed answer. A Link has to be an http(s) URL, or nothing is stored. |\\n| Number | The number. An answer that is not one number is dropped rather than stored as text. |\\n| Date | The date, as an ISO 8601 stamp, whichever way the visitor typed it. |\\n| Choice | The answer, only when it is one of the field\\'s choices exactly. |\\n| Checkbox | true or false, from what a ticked or unticked box sends. |\\nAn answer left blank writes nothing — a submission never clears a value you set by hand. A mapping onto a retired field writes nothing either.\\nMappings are kept by field name across publishes: redrawing the form and publishing a new version does not lose them. A field renamed on the canvas is a new field and starts unmapped; a field removed takes its mapping with it.\\nThe sender\\'s name and email are recognized from the field name — see who a submission is from — and never need mapping.' },\n { path: '/content-and-data/crm/custom-fields', title: 'Custom fields', heading: 'Over the API', anchor: '#over-the-api', text: 'The /v1/contacts, /v1/companies and /v1/deals resources each carry custom, an object keyed by field key — {} on a record that has none, so a client can index it without a guard. Each resource is judged against the definitions of its own record: a company body is validated against the company fields, whatever a contact field of the same key holds.\\n- POST and PATCH accept custom. Values are converted by type exactly as a form answer is.\\n- A PATCH merges the keys it sends and keeps the rest; send null under a key to clear it.\\n- A key that is not a field of that record, a field that is retired, or a value the type cannot hold is refused with 400 validationfailed and named in fields as custom. — nothing is stored from that request.' },\n { path: '/content-and-data/crm/custom-fields', title: 'Custom fields', heading: 'Retire, restore and delete', anchor: '#retire-restore-delete', text: 'Retire takes a field off every record page, every column and every form mapping. Values already saved under it are kept and still export, and Restore brings the field back with them intact.\\nDelete is offered only on a retired field. It removes the definition for good; values saved under its key stay on the records that carry them, but nothing will show them again — and a new field created with the same key would read them as its own, which is why the key of a retired field still counts as taken when you create one on that tab.' },\n { path: '/content-and-data/crm/custom-fields', title: 'Custom fields', heading: 'Recompute next activity', anchor: '#recompute-next-activity', text: 'Recompute next activity at the top of the Fields section rewrites every contact\\'s, company\\'s and deal\\'s next activity from its open tasks — the figure every task write keeps, recomputed for the whole organization at once. Run it after importing records that existed before the figure did, or whenever a list\\'s Next activity column looks stale; it is safe to run any time, and it reads every open task once. An organization holding more than twenty thousand open tasks is told only the first batch was read: the records those tasks name are still written, but nothing stale is cleared, since \"nothing is scheduled against this record\" cannot be said from a partial read.' },\n { path: '/content-and-data/crm/custom-fields', title: 'Custom fields', heading: 'Related', anchor: '#related', text: '- CRM overview\\n- The contact record — the fixed properties custom fields sit beside\\n- Companies and deals — the other two records that carry them\\n- Import contacts from CSV — every custom field is an import target\\n- Forms & lead capture\\n- REST API — contacts, companies, deals' },\n { path: '/content-and-data/crm/deals', title: 'Deals pipeline', heading: '', anchor: '', text: 'Deals pipeline\\nDeals is the sales pipeline inside the CRM: one card per opportunity, moving through the stages your business sells in. Open it at …/hosts/{site}/crm/deals; a single deal has its own page at …/crm/deals/{id}.\\nPlan availability Deals are part of the CRM, included from Starter. On Free the section is shown locked, with the rest of the CRM. A deal is a CRM record, counted with contacts and companies against your plan\\'s records band — see CRM records.' },\n { path: '/content-and-data/crm/deals', title: 'Deals pipeline', heading: 'Pipelines', anchor: '#pipelines', text: 'Every workspace on a plan with the CRM starts with one pipeline, Sales, and a default set of stages: Qualified, Contact made, Proposal sent, Negotiation, then Won and Lost. The pipeline is created the first time somebody opens the Deals section, so there is nothing to set up before the first deal.\\nA business that sells more than one way — new accounts and renewals, retail and wholesale — can run more than one pipeline, each with its own stages and its own board. Open Pipelines on the Deals section to:\\n- Create a pipeline. Give it a name; it starts with the default stages, which you can then edit. Names are unique among the active pipelines.\\n- Rename a pipeline.\\n- Set as default. New deals land in the default pipeline unless the New deal drawer picks another; there is always exactly one default.\\n- Archive a pipeline that holds no open deal. The default pipeline and the last active one cannot be archived; close or move the open deals first. An archived pipeline is never deleted — the deals it closed still show their stages — and can be restored later.\\n- Edit stages of a pipeline (below).\\nWhen there is more than one active pipeline, a pipeline switcher appears in the section header. The board, the table, the three figures above them and the New deal drawer all follow it.\\nStages\\nEach stage carries a probability — the chance a deal in that stage closes — which is what the weighted forecast multiplies by. Won is always 100%, Lost is always 0%, and the stages in between are yours to set.\\nEdit stages on a pipeline lets you:\\n- Rename a stage or change its probability.\\n- Reorder the open stages with the up and down arrows.\\n- Add a stage. New stages are open stages; every pipeline keeps exactly one Won and one Lost.\\n- Remove a stage that no deal is in. A stage with deals in it can' },\n { path: '/content-and-data/crm/deals', title: 'Deals pipeline', heading: 'The board and the table', anchor: '#the-board-and-the-table', text: 'The section opens as a board of the chosen pipeline: one column per open stage, with Won and Lost folded away at the end until you expand them. Each card shows the deal\\'s title, amount, the contact and company it is with, its owner, and how many days it has sat in its current stage. Drag a card between columns to move it, or use the card\\'s menu to move it, mark it won, or mark it lost from the keyboard.\\nAbove the board, three figures summarize what is open: the open count, the pipeline value (every open deal\\'s amount, per currency) and the weighted value (each amount multiplied by its stage\\'s probability).\\nSwitch to the table for a paged list with the title, stage, amount, owner, expected close date, status and next activity of every deal in the pipeline, including the closed ones. The No next activity chip beside the status filter keeps only the deals on the page with no open task scheduled against them — the ones the reports page counts as stuck.\\nThe table\\'s rows have checkboxes: tick some and a bulk bar appears above it to set their stage, set their owner, mark them lost with one reason, export them or delete them. Export CSV beside the status filter downloads the deals on screen as deals.csv — title, pipeline and stage by name, the amount in major units beside its currency, the owner by email address, the expected close date, status, the contact and the company, when it closed, the lost reason and notes; the bar\\'s Export CSV writes the same file over the selection.' },\n { path: '/content-and-data/crm/deals', title: 'Deals pipeline', heading: 'Import from CSV', anchor: '#import-from-csv', text: 'Import CSV beside the table\\'s status filter takes a spreadsheet of deals — an export from another CRM, a forecast sheet — and files each row as a new deal. A deal has no key the way a contact has an address, so nothing is merged: importing a file twice files it twice. Importing needs the same Manage data permission as creating a deal.\\nThe three steps are the ones the contacts import walks: choose the file (up to 5,000 rows), match its columns — Aglyn proposes a match from the header names and shows the first row\\'s value beside each — check the ten-row preview, then import in batches of 200 with a progress bar and a result that says how many were added and skipped, with the skipped rows downloadable as a CSV that says why. Download template hands you the export\\'s own header over no rows, so a sheet filled in against it maps itself.\\n| Field | What is read |\\n| Title | Required. A row without one is skipped. |\\n| Pipeline | By name, as it appears in Pipelines, spelled either way. Left empty, the row lands in the default pipeline. A name your workspace has no pipeline for skips the row as No pipeline by that name. |\\n| Stage | By name within that pipeline. Left empty, the pipeline\\'s first open stage. A name the pipeline has no stage for skips the row as No stage by that name in that pipeline. A row filed into Won or Lost is closed on arrival; no dealWon or dealLost event fires for it. |\\n| Amount | In major units (1250.00, $1,250); the currency symbol and separators are ignored. A cell that is not a number is dropped and reported. |\\n| Currency | A three-letter code (USD, EUR); USD when empty. Anything else is dropped and reported. |\\n| Owner | The email address of a member of your organization. An address that matches nobody leaves the deal without an owner, and the result names' },\n { path: '/content-and-data/crm/deals', title: 'Deals pipeline', heading: 'Creating a deal', anchor: '#creating-a-deal', text: 'New deal opens a drawer. A deal needs only a title; everything else is optional:\\n| Field | What it is |\\n| Pipeline and stage | Where the deal starts. The pipeline list holds every active pipeline and opens on the one the board is showing; the stage defaults to its first open stage. |\\n| Amount and currency | What the deal is worth. Currency defaults to US dollars; the amount is stored in minor units, so 1,250.00 is exact. On a deal with line items the amount is their sum and is read-only here. |\\n| Expected close | The date you expect to close it — what a forecast by month reads. |\\n| Owner | The teammate responsible. Picked from your workspace\\'s members. |\\n| Contact | The person the deal is with, searched by name or email from your contacts. |\\n| Company | The organization, searched by name from your companies. |\\n| Notes | Anything the card cannot carry. |\\nA deal is visible to the same sites as a contact captured on this site would be, so a site that cannot see the person cannot see the deal.' },\n { path: '/content-and-data/crm/deals', title: 'Deals pipeline', heading: 'Line items', anchor: '#line-items', text: 'A deal\\'s amount can be a number you type, or the sum of the products behind it. The Products card on a deal\\'s page lists its line items — each a name, a quantity and a unit amount in the deal\\'s currency — and Add line opens a dialog with two doors:\\n- From the catalog searches this site\\'s active products by name and offers each variant at its catalog price. Catalog prices are in US dollars; on a deal in another currency the dialog says so, and the unit amount can be edited before the line is added.\\n- By hand takes a name and a price with no product behind them. A plan without commerce has no catalog to search, so this is the only door it shows.\\nOnce a deal has a line item, its amount is the lines\\' sum: the Amount field in the Edit drawer turns read-only with a caption saying so, and every change to the lines — a quantity edited in place, a line removed — writes the new sum with it. Remove the last line and the amount is yours to type again, starting from the last sum. A deal carries at most fifty lines, all in the deal\\'s currency.\\nLine items travel over the REST API as lineItems, with the same rules.' },\n { path: '/content-and-data/crm/deals', title: 'Deals pipeline', heading: 'Moving, winning and losing', anchor: '#moving-winning-and-losing', text: 'Stage changes go through the server rather than being written directly, so that automations can hear them. Three events fire, and each can trigger a workflow or action — see Automations for the CRM:\\n| Event | When |\\n| dealStageChanged | A deal moves between open stages, or is reopened. |\\n| dealWon | A deal is marked won. |\\n| dealLost | A deal is marked lost. Marking a deal lost asks for a reason, which is kept on the deal. |\\nEvery event carries the deal\\'s id, title, amount and currency, its new and previous stage, and the owner, contact and company ids, so a workflow can notify the owner, file a task, or act on the person.\\nA won deal makes its contact a customer {#a-won-deal-makes-its-contact-a-customer}\\nMarking a deal won sets the linked contact\\'s lifecycle stage to Customer — the same rule an order applies: a person with no stage or an earlier one becomes a customer, and nobody is ever moved back, so an Evangelist who closes another deal stays an evangelist and a stage of Other is never overwritten. It happens on the move into the won stage itself, whichever way the deal got there — the board, a card\\'s menu, the deal\\'s page, the bulk bar, the organization-level board, or the REST API — and there is nothing to switch on: a won deal is a customer by definition. The stage is written for the site the deal was made on, so a sibling site that also knows the person keeps its own reading of them.\\nWhen the stage did move, a Contact changed stage event follows the Deal won event, so an automation that starts when somebody becomes a customer hears it; the organization\\'s activity feed line for the win says contact now a customer. An automation on Deal won is for what happens next — the Follow up a won deal recipe books a check-in call and sets no stage, because the win already di' },\n { path: '/content-and-data/crm/deals', title: 'Deals pipeline', heading: 'A deal\\'s page', anchor: '#a-deals-page', text: 'Opening a deal shows:\\n- The header — the deal\\'s title in the page heading and the trail, the pipeline and stage under its kind, its status, amount and owner as chips, Back to deals, Edit, and a menu (⋮) carrying Delete deal.\\n- Stage — a stepper across the open stages, with Won and Lost buttons and, on a closed deal, the way to reopen it.\\n- Properties — the amount and its weighted value, the expected close, the owner, links to the contact and the company, the notes, and one row per custom field defined on the Deals tab of the Fields section; Edit carries a control for each.\\n- Products — the line items behind the amount, with the door to add one from the catalog or by hand.\\n- Tasks and Activity — what is owed on this deal and what has happened on it.\\nA deal also appears on the pages of the contact and the company it names, each with a New deal shortcut that starts a deal already linked to them.' },\n { path: '/content-and-data/crm/deals', title: 'Deals pipeline', heading: 'Files', anchor: '#files', text: 'The Files card attaches assets from the media library to this record: contracts, quotes, a signed proposal, a photo of the site.\\n- Attach… opens the media browser. At a site you can pick from the site\\'s own library and the workspace\\'s shared one; at the organization hub only the shared library is offered, because a record there belongs to the workspace rather than to one site.\\n- A record holds up to 20 files.\\n- The ✕ beside a file removes the attachment. The file itself is untouched and stays in the library.\\nFiles are stored by id, not by address. A file moved between folders keeps its attachment, and a private file is still served through the signed link that checks who is asking rather than through a public URL. A file deleted from the library is still listed, marked as no longer there, so an attachment never disappears without saying so.\\nAttachments are readable and writable through the REST API as mediaIds.' },\n { path: '/content-and-data/crm/deals', title: 'Deals pipeline', heading: 'Related', anchor: '#related', text: '- CRM overview\\n- Companies and the contact record — the two records a deal is with\\n- Tasks & follow-ups and Activities & the timeline — what is owed on a deal and what has happened on it\\n- Bulk actions — stage, owner, loss, export and delete over a selection\\n- Import contacts from CSV — the same three steps the deals import walks\\n- Reports — the open pipeline, its weighted forecast, and won against lost\\n- Automations for the CRM — the three deal events, and what a won deal does on its own\\n- Workflows & actions\\n- REST API — deals and pipelines' },\n { path: '/content-and-data/crm/email-templates', title: 'Email templates', heading: '', anchor: '', text: 'Email templates & snippets\\nSend email on a contact\\'s, a lead\\'s or a deal\\'s page writes one message to one person — see Sending an email. A rep writes the same few letters all week, so the CRM keeps them: a template is a subject and a message under a name, filled in whole; a snippet is a paragraph under a name, dropped in where the cursor is. Both can carry merge fields — {{contact.firstName}}, {{deal.amount}} — that are filled in from the record the moment the email is sent, so one letter reads as written to each person it goes to.' },\n { path: '/content-and-data/crm/email-templates', title: 'Email templates', heading: 'Templates and snippets', anchor: '#templates-and-snippets', text: '| Kind | What it holds | What it does in Send email |\\n| Template | A subject and a message | Picked from the Template field above the subject; fills in both. If you have already written a message, the dialog asks before replacing it. A template with no subject leaves yours alone. |\\n| Snippet | A paragraph | Picked from Insert beside the message; lands at the cursor. |\\nA template\\'s message is plain text, like the email itself: a blank line starts a new paragraph, and nothing is formatted.' },\n { path: '/content-and-data/crm/email-templates', title: 'Email templates', heading: 'Merge fields', anchor: '#merge-fields', text: 'A merge field is written as {{group.field}}, spaces inside the braces allowed. Insert lists every field that applies to the record you are writing to, so you need not type them.\\n| Field | What it fills in |\\n| {{contact.firstName}}, {{contact.lastName}}, {{contact.name}} | The person\\'s name as the sending site knows it — the first word, the rest, or the whole. |\\n| {{contact.email}} | The address the email goes to. |\\n| {{contact.company}} | The company the person is filed under, as typed or picked on their record. |\\n| {{contact.title}} | Their job title. |\\n| {{lead.firstName}}, {{lead.lastName}}, {{lead.name}}, {{lead.email}} | The same for a lead, when the email is sent from a lead\\'s page. |\\n| {{deal.name}}, {{deal.amount}} | The deal\\'s title and its amount with its currency, e.g. $1,250.00, when sent from a deal\\'s page. |\\n| {{sender.firstName}}, {{sender.name}}, {{sender.email}} | You — the name on your account, and the address replies come to. |\\n| {{site.name}} | The name of the site the email leaves from. |\\nA field that has nothing to fill — a contact with no job title, a deal with no amount, a field written for a lead on a contact\\'s page — is filled with nothing, and so is a field the CRM does not know. That can leave \"Hi ,\" in a letter, so the dialog previews the message with the fields filled as soon as it names one, and counts the empty ones under the message: 2 fields have no value: {{contact.firstName}}, {{deal.amount}}. What the preview shows is what leaves — the server fills the fields with the same rules — and the timeline logs the letter as sent, not the template.' },\n { path: '/content-and-data/crm/email-templates', title: 'Email templates', heading: 'Keeping a letter as a template', anchor: '#saving', text: 'Write the email as you would send it, then Save as template… beneath the message asks for a name and whose it is. It saves the subject and the message as written, merge fields included, without sending anything.' },\n { path: '/content-and-data/crm/email-templates', title: 'Email templates', heading: 'Managing templates', anchor: '#managing-templates', text: 'CRM → Settings carries an Email templates card: every template and snippet you can use, with its kind and whether it is shared or personal. New template opens a drawer for the name, the kind, who it is for, the subject (for a template) and the message; the row\\'s menu edits or deletes one. The card is on a site\\'s CRM hub and on the organization\\'s alike.\\nA template written under a site is that site\\'s, and is listed on that site\\'s hub and to every org-wide member. One written from the organization\\'s own CRM is the whole workspace\\'s, and every site\\'s hub lists it.\\nDuplicate a template\\nDuplicate… in a template\\'s row menu makes a second template with the same subject, body and merge fields under the name you give it. The copy keeps the original\\'s kind and scope — a site\\'s template stays that site\\'s — and is filed under you: a copy of a personal template is personal to you. The listing\\'s ceiling applies to copies as it does to new templates.' },\n { path: '/content-and-data/crm/email-templates', title: 'Email templates', heading: 'Shared or personal', anchor: '#shared-or-personal', text: '| | Who sees it | Who can change or delete it |\\n| Shared | Everyone who can send email from the CRM. | Any CRM editor, and any org-wide member. |\\n| Personal | You alone. | You, and an org-wide owner or admin tidying up. |\\nA personal template is your own working arrangement, the way a private saved view is: it is kept out of colleagues\\' menus rather than locked, so an admin can still remove one a departed teammate left behind.' },\n { path: '/content-and-data/crm/email-templates', title: 'Email templates', heading: 'Over the REST API', anchor: '#over-the-rest-api', text: 'Templates and snippets are the /v1/email-templates resource, for an integration that seeds a workspace\\'s letters or keeps them in step with another tool.' },\n { path: '/content-and-data/crm/email-templates', title: 'Email templates', heading: 'Related', anchor: '#related', text: '- Activities & the timeline — the Send email dialog itself, and what the send is refused for\\n- CRM settings — where the card lives\\n- Saved views — the other thing the CRM keeps per person' },\n { path: '/content-and-data/crm/import', title: 'Import contacts from CSV', heading: '', anchor: '', text: 'Import contacts from CSV\\nImport CSV, on the Contacts section of the CRM, takes a spreadsheet of people — an export from another CRM, a sign-up sheet, a customer list — and adds each person to your contacts. Someone already in your contacts is updated rather than added twice: the file\\'s phone, title, tags and other details are merged onto the record you already have.\\nThe address is …/hosts/{site}/crm/contacts. Importing needs the same Manage data permission as editing a contact.\\nPlan availability Importing is part of the CRM, included from Starter. On Free the Contacts and Leads sections are shown locked with the rest of the CRM, and there is no Import CSV. See What each plan includes.' },\n { path: '/content-and-data/crm/import', title: 'Import contacts from CSV', heading: 'Three steps', anchor: '#three-steps', text: '1. Choose the file. A CSV with a header row. Up to 5,000 rows per file — split a larger one and import the pieces. Nothing is sent until you press Import. Download template beside it hands you the export\\'s own header over no rows — every field below, plus one column per custom field — so a sheet filled in against it maps itself.\\n2. Match the columns. Each column in the file is matched to a contact field. Aglyn proposes a match from the header names (Email, Phone, Company, Tags and their usual variants) and shows the first row\\'s value beside each one so you can correct it. Choose Do not import for a column you do not want. The email column is required; every other column is optional. A preview of the first ten rows shows exactly what each mapping produces.\\n3. Import. Rows are sent in batches of 200 with a progress bar. When it finishes, the result says how many people were added, how many existing contacts were updated, and how many rows were skipped — with a Download skipped rows button that gives you those rows back as a CSV, with a last column saying why, so you can fix them and import that file again.\\nClosing the drawer during an import stops it after the batch in progress. Rows already imported stay imported; run the same file again to finish, and the rows from the first run are reported as updated.' },\n { path: '/content-and-data/crm/import', title: 'Import contacts from CSV', heading: 'What each column can hold', anchor: '#what-each-column-can-hold', text: '| Field | What is read |\\n| Email | Required. Trimmed and lowercased; a row whose email is not a valid address is skipped. |\\n| Name | The display name. |\\n| Phone | Stored in international format (+15125550123). A ten-digit number is read as US/Canada; anything else needs its country code. A number that cannot be read is dropped and reported. Importing a phone number never sends anything to it. |\\n| Job title | Free text. |\\n| Company name | Matched to an existing company of yours by name, or a new company is created — the result counts the new ones. |\\n| Address line 1, line 2, city, state or region, postal code, country | The postal address. Country must be a two-letter code (US, GB); a spelled-out country name is dropped and reported. |\\n| Tags | Separated by , or \\\\|, lowercased. Added to any tags the contact already has. |\\n| Owner | The email address of a member of your organization. An address that matches nobody on the team leaves the contact without an owner, and the result names those addresses. |\\n| Lifecycle stage | One of subscriber, lead, marketing-qualified, sales-qualified, opportunity, customer, evangelist, other — by id or by label. |\\n| Marketing consent | yes, true or 1 records a marketing opt-in for this site, dated today. Anything else records nothing. This is your statement that the person agreed; it is not a substitute for their own opt-in. |\\n| Custom fields | Every contact field you have defined under Fields is offered as a target. A value that does not fit the field\\'s type — a word in a number field, a choice a dropdown does not offer — is dropped and reported. The companies import offers the company fields the same way. |\\nA value that cannot be read never stops the row: the person is imported without it, and the result names the values it could not keep' },\n { path: '/content-and-data/crm/import', title: 'Import contacts from CSV', heading: 'What is skipped, and why', anchor: '#what-is-skipped-and-why', text: '| Reason | Meaning |\\n| Not a valid email address | The email cell is empty or not an address. |\\n| Appears earlier in the file | The same address is on an earlier row; the first row wins. |\\n| Contact limit reached | Your plan\\'s records band is full, on a plan whose band is a hard limit — see CRM records. |\\n| Could not be saved | Something went wrong writing the record. Try that row again. |\\nA file the CRM itself exported — from the table\\'s Export CSV or the bulk bar\\'s — re-imports without a hand mapping: its header is this vocabulary, its owner column carries email addresses, and its Sources and Last interaction columns are proposed as Do not import, because the platform records those and a file cannot set them.\\nEvery imported person shows Import as a source on their contact and \"Imported from CSV\" at the top of their activity. Contacts are shared across your organization the same way captured ones are: a person imported on one site is visible to that site (and any sites declared to be one sender with it), not to every site in the account.' },\n { path: '/content-and-data/crm/import', title: 'Import contacts from CSV', heading: 'Related', anchor: '#related', text: '- CRM overview\\n- The contact record — the fields an imported row lands in\\n- Bulk actions — tag, assign or stage the people you just imported, all at once\\n- Import companies from CSV — the same three steps for the organizations behind them\\n- Import deals from CSV and import tasks from CSV — the same three steps, the pipeline, the stage and the assignee resolved by name\\n- Import a list into an email audience — for adding people to a mailing list rather than to the CRM' },\n { path: '/content-and-data/crm/leads', title: 'Leads', heading: '', anchor: '', text: 'Leads\\nA lead is somebody your site has met but you have not yet qualified: a visitor who signed up, booked, or submitted a form that routes leads. The Leads section of the CRM is where the team works them — decides who owns each one, keeps notes, and either converts the lead into a contact or closes it with a reason.\\nLeads live under the site that captured them, so each site\\'s list is its own. Open CRM → Leads in the console, or use Open in CRM on a lead row in the Inbox\\'s Site Members & Leads section.\\nPlan availability Leads are part of the CRM, included from Starter. On Free the section is shown locked, with the rest of the CRM. Leads are still filed on Free, exactly as described below, and kept; workspace owners and admins can export every lead, or erase a person, from Settings → Privacy. See What each plan includes.' },\n { path: '/content-and-data/crm/leads', title: 'Leads', heading: 'What makes a lead', anchor: '#what-makes-a-lead', text: 'Every capture lands in Contacts — one row per person, at the earliest lifecycle stage that describes what happened. A lead is created in addition, and only by a lead surface:\\n- a member sign-up;\\n- a booking;\\n- a form whose own page has Also create a lead from the address someone gives this form switched on. A form without it still updates the contact at stage Lead; it just files nothing here.\\nThe plan changes none of this. On Free too, a form files a lead only when its lead routing is on, and the lead is kept, though the Leads section that lists it is locked with the rest of the CRM.\\nSo Contacts is the address book and Leads is the working list. The Leads section opens with which surfaces create leads on this site — sign-ups, bookings, and the lead-routed forms by name, each linking to the form\\'s page — and offers Turn on lead routing beside a form that could route. A form with no email field cannot key a lead, and a form that records no consent would file leads the team cannot email; in either case the control says so instead of switching, because publishing the form would refuse the same thing. One person is one lead: a second submission updates the lead the first one created. Leads from before the Leads section existed were created from those earlier submissions and bookings, so a person who wrote in through a lead-routed form before lead routing existed is in the Open view too, unassigned.\\nAt the organization level — CRM → Leads from the organization\\'s own tab, where every site\\'s leads are one list — the same note opens the section grouped by site. Sign-ups and bookings are named once, since they create a lead on every site; then each site is a group of its own, named and linking to that site\\'s Leads section, with its lead-routed forms by name, Turn on lead routing ' },\n { path: '/content-and-data/crm/leads', title: 'Leads', heading: 'The Leads list', anchor: '#the-leads-list', text: 'The list shows the most recently seen leads first — a person who booked yesterday sits above one who signed up last month, whichever came first. Each row carries:\\n| Column | What it shows |\\n| Lead | The name the person gave, with their email beneath it — or the email alone. |\\n| Status | New, Working, Qualified or Unqualified. Change it in place from the row. |\\n| Owner | The team member working the lead, or Unassigned. A lead inherits its contact\\'s owner when one is assigned on capture. |\\n| Source | Every surface that captured this person: Sign-up, Booking, or the form they submitted. |\\n| Last seen | When the person last did something on your site. |\\nThe Show control at the top of the card picks the view. Open — the default — is every lead that still needs working (New and Working). The other views are one status each, and All shows everything. A lead nobody has touched yet has no status of its own and reads as New, so the leads your site collected before the CRM existed are already in the Open view.\\nThe list reads the 200 most recently seen leads and shows them a page at a time, with the usual footer to turn the page and pick how many rows it holds. When a site holds more than 200, a notice says so; the status filter narrows those 200, and older leads are still listed in the Inbox and still reached by campaign audiences.\\nWorking a lead from the row\\n- Status — click the status chip to change it to New or Working. Choosing Unqualified… asks for a reason first.\\n- Row menu (⋮) — Open lead, Convert…, Assign owner, or Unqualify. Convert… opens the same dialog as the lead\\'s page (see Converting a lead) without opening the page first; on a lead already converted, unqualified, or whose person has an erasure pending, it is disabled with the reason as its tooltip.\\n- Click anywhere' },\n { path: '/content-and-data/crm/leads', title: 'Leads', heading: 'A lead\\'s page', anchor: '#a-leads-page', text: 'Click a row to open the lead. The page has two cards.\\nLead holds what the team decides: the status, the owner, and free-text notes with a Save notes button. It also shows the identity the capture recorded — email and name, and a phone number when the form that captured them took one — and the person\\'s marketing consent: whether they opted in (and when), declined, or never recorded a choice. A lead with no recorded consent cannot be sent marketing email, which is worth knowing before you promise them a newsletter. The card\\'s header carries Call and Log a call beside Send email — see click to call.\\nCaptured history is read-only: when the person was first and last seen, how many times your site captured them, every source that did, and — under Where this lead came from — the campaign the capture is credited to, when there is one.' },\n { path: '/content-and-data/crm/leads', title: 'Leads', heading: 'Converting a lead', anchor: '#converting-a-lead', text: 'When a lead is real, click Convert on the lead\\'s page, or choose Convert… from its row\\'s menu on the list — the same dialog either way, from the list in one click. The dialog asks three things:\\n1. Contact. The lead becomes a contact at the Sales qualified lifecycle stage, owned by whoever you pick — the lead\\'s owner by default. Pick nobody and the workspace\\'s assignment rules and the site\\'s default owner decide, and failing those the contact is yours. A colleague you pick is notified; you are not told about a contact you kept. If this email address is already a contact, the conversion joins that contact rather than creating a second one — the address book stays one row per person — and a contact that already has an owner keeps them.\\n2. Company. No company, Link an existing company, or Create a company. The dialog proposes the company the lead\\'s email domain implies: if your workspace already has a company at that domain, it is preselected; otherwise a new one is proposed, named after the domain, with the domain filled in. Public mailboxes such as Gmail propose nothing.\\n3. Deal. Tick Open a deal to open one in your default pipeline with a title, an amount, a currency and a starting stage. A workspace with no pipeline yet gets a Sales pipeline with the default stages created along with the deal.\\nConverting marks the lead Qualified, records what it became, and takes you to the new contact\\'s page. Back on the lead, the card links to the contact, the company and the deal. A converted lead cannot be converted again — opening the dialog on one simply takes you to its contact.\\nA lead whose person has an erasure pending cannot be converted either: Convert stays on the page but is disabled, with the reason as its tooltip, until the nightly job has run — and the job removes the le' },\n { path: '/content-and-data/crm/leads', title: 'Leads', heading: 'Unqualifying a lead', anchor: '#unqualifying-a-lead', text: 'Unqualify — from the row\\'s menu on the list, or from the menu (⋮) in the header of the lead\\'s page — closes a lead without converting it. A reason is required, so the closed leads can be counted by why they closed. An unqualified lead drops out of the Open view and keeps its reason on its page; set its status back to Working to reopen it, which clears the reason.' },\n { path: '/content-and-data/crm/leads', title: 'Leads', heading: 'Erasing the person', anchor: '#erasing-the-person', text: 'The menu (⋮) in the header of the lead\\'s page also carries Erase this person — the privacy erasure, for a person who asks to be forgotten. It is the same request the contact record offers, filed by the lead\\'s address: the person is removed from every site in the workspace, the address is closed to capture at once, the lead shows an Erasure pending banner with its Convert button disabled, and the sweep runs with the nightly erasure job. Workspace admins only. What it removes, what it keeps and what it does not reach are listed in Deleting and erasing.' },\n { path: '/content-and-data/crm/leads', title: 'Leads', heading: 'Who can do this', anchor: '#who-can-do-this', text: 'Anyone who can open the CRM can work leads — that is the workspace\\'s data permission, the same one that guards contacts. Converting a lead also requires a role on the site the lead belongs to.\\nLeads are part of the CRM, included from Starter. On Free the Leads section is shown locked, with the rest of the CRM.' },\n { path: '/content-and-data/crm/leads', title: 'Leads', heading: 'Related', anchor: '#related', text: '- CRM overview\\n- CRM settings — the default owner and assignment rules a lead inherits\\n- Reports — the lead funnel: where a period\\'s leads stand, and why the unqualified ones were closed\\n- The contact record — what a converted lead becomes\\n- Companies and the deals pipeline — the other two records a conversion can open\\n- Forms & lead capture\\n- Email campaigns\\n- REST API — leads — the same queue and the same conversion, for an integration' },\n { path: '/content-and-data/crm/overview', title: 'CRM', heading: '', anchor: '', text: 'CRM\\nThe CRM is where your team works the people who interact with your sites. Aglyn builds the contact list on its own from everything a visitor does — a form submitted, an account created, an order placed, a booking made — and the CRM puts the rest of a sales team\\'s records around that list: leads to qualify, the companies people work for, a deals pipeline, tasks, a timeline of calls and meetings, reports, and the custom fields your business describes people by.\\nIn the console the plugin is called CRM. It is one tab, and a hub of eight sections, each with its own address under your site at …/hosts/{site}/crm/ ; a bare …/crm lands on the first. The same hub opens at the organization level, over every site at once, at …/{organization}/crm/ — see At the organization level. The surface was called Contacts while it was a single list, and a link kept from then — the older …/contacts address, under a site or under the organization — still opens the hub.' },\n { path: '/content-and-data/crm/overview', title: 'CRM', heading: 'What\\'s in the CRM area', anchor: '#whats-in-the-crm-area', text: '| Section | Address | What lives there |\\n| Contacts | /crm/contacts | Every person your site may see, as a list with Owner and Stage columns, filters and search; a person\\'s own page is /crm/contacts/{id}. CSV import, bulk actions, saved views and the timeline live here too. |\\n| Leads | /crm/leads | People a site has captured but not yet qualified — a status, an owner and notes on each, and a conversion into a contact, a company and a deal. CSV import and export too. |\\n| Companies | /crm/companies | The organizations your contacts belong to, keyed by domain — a captured contact is linked to the company at their email domain on its own; a company\\'s page is /crm/companies/{id}. CSV import and export and bulk actions too. |\\n| Deals | /crm/deals | The sales pipeline — open deals by stage, with an amount, an owner and an expected close, as a board or a table with export and bulk actions; a deal\\'s page is /crm/deals/{id}. |\\n| Tasks | /crm/tasks | Calls, emails, meetings and to-dos by due date, each linked to the contact, company or deal it is for, with export and bulk actions. |\\n| Reports | /crm/reports | New contacts over time, sources and the lifecycle funnel, conversion by source, the lead funnel, the open pipeline and its forecast, won and lost, activity by teammate, and the task load — every table exportable as CSV. |\\n| Fields | /crm/fields | The custom fields on a contact — text, number, date, choice, checkbox or link — which a form field can save into. |\\n| Settings | /crm/settings | What the CRM does on its own for every site in the workspace — whether a company is created from a captured contact\\'s work email domain — and the email templates your team sends from a record. |\\nThree things cut across the sections rather than having one of their own. Saved views keep a list' },\n { path: '/content-and-data/crm/overview', title: 'CRM', heading: 'Unified ingestion', anchor: '#unified-ingestion', text: 'Contacts are ingested from across your site:\\n- Form submissions\\n- Site members (sign-ups)\\n- Orders from commerce\\n- Bookings from scheduling\\nDuplicate signals from the same person are unified into one contact. A person your site has met but not yet qualified is also a lead; converting the lead joins the same contact rather than making a second one.\\nEvery plan with the CRM includes a CRM records band — one number for contacts, companies and deals together. Paid tiers never drop a record: growth past the band meters as overage on the monthly invoice. Free has no CRM, but the same count caps what its sites capture — past 100 records a site adds no new person. See CRM records under Billing & plans.' },\n { path: '/content-and-data/crm/overview', title: 'CRM', heading: 'What each plan includes', anchor: '#what-each-plan-includes', text: '| | Free | Starter and above |\\n| The CRM — Leads, Contacts, Companies, Deals, Tasks, Reports, Fields and Settings; working a lead — status, owner, notes, conversion and import; on Contacts, New contact, Import CSV, saved views, and every edit to a contact — profile, tags, notes, owner, lifecycle stage, company, custom fields, tasks, deals, files, logged calls and activities and merges — one record at a time or from the bulk bar; CSV export and Erase this person from the CRM\\'s own pages; the two CRM dashboard cards; the CRM automation steps; the CRM\\'s REST API resources | None. The CRM tab stays in the navigation, and every section is shown locked beside the upgrade notice | Yes |\\n| One-to-one email from a record | None | A daily cap by tier — see One-to-one email |\\n| Capture — form submissions, member sign-ups, orders and bookings updating the person in Contacts, and a lead from a form whose lead routing is on | Yes, up to the 100-record band | Yes |\\n| Privacy requests — exporting every contact and every lead, and erasing a person, from Settings → Privacy | Yes | Yes |\\nThe CRM is for a plan that includes it, from Starter, while the subscription is live. On Free — and on a paid plan whose subscription has ended — the CRM tab stays in the navigation, every section is drawn locked in the rail beside a notice that the CRM is not included in your current plan, with View plans, and a bare …/crm shows that notice. Nothing in the CRM can be read or changed there: not in the console, not through its routes, and not over the REST API, whose CRM resources refuse with planrequired.\\nCapture does not wait for the plan. Every form submission, sign-up, order or booking that carries an email address updates the person in Contacts, and a form files a lead only when its lead routing is on' },\n { path: '/content-and-data/crm/overview', title: 'CRM', heading: 'The contacts page', anchor: '#the-contacts-page', text: 'The Contacts section is the list the rest of the CRM is built on. It lets you:\\n- Browse the list, with Owner and Stage columns, a stage filter, an Assigned to me toggle, and one optional column per custom field.\\n- Add a contact by hand with New contact — see The contact record.\\n- Open a contact\\'s own page to edit their profile, see where they came from, and read their history.\\n- Add tags and notes.\\n- Log calls, emails, meetings and notes against a person, and read them in one timeline with everything the platform captured — see Activities & the timeline.\\n- Tick rows and act on all of them at once — tag them, set their owner or stage, add them to an email audience, export them, or remove them from this site; see Bulk actions.\\n- Export to CSV, and Import CSV — see below.\\nThe Contacts section is part of the CRM, included from Starter. On Free it is shown locked in the rail with the rest of the CRM; the people your sites capture are still kept, and can be exported from Settings → Privacy.\\nUnder the list, a Recent activity feed shows the newest calls, emails, meetings and notes anyone on the team has logged against any record.' },\n { path: '/content-and-data/crm/overview', title: 'CRM', heading: 'Import from CSV', anchor: '#import-from-csv', text: 'Import CSV on the Contacts section brings a spreadsheet of people in — map its columns to contact fields, preview the first rows, and import in batches with a report of what was added, updated and skipped. See Import contacts from CSV. Importing is part of the CRM, included from Starter.' },\n { path: '/content-and-data/crm/overview', title: 'CRM', heading: 'Segments', anchor: '#segments', text: 'Group contacts into segments — saved filters over tags and sources, kept from the Contacts list\\'s filter bar through Save as segment… in the views menu — and target them directly in email campaigns. A saved view keeps more than a segment — every filter, the columns and the sort — and a contacts view can be an audience in its own right, picked beside a segment when an audience is built from a rule. An audience built from a rule can also read a contact\\'s owner, lifecycle stage, company and custom fields; see email audiences.' },\n { path: '/content-and-data/crm/overview', title: 'CRM', heading: 'Everywhere the CRM shows up', anchor: '#everywhere-the-crm-shows-up', text: 'The CRM is wired into the rest of the console rather than standing beside it. Every place a person\\'s record is met links into the CRM, and the CRM links back out to the record that put the person there.\\n| Where | Into the CRM | Out of the CRM |\\n| Search (the top bar) | Contacts by name, email, phone or company; leads by name or email; companies by name or domain; deals by title. Each result opens the record. The CRM groups appear only for members who may open the CRM. | — |\\n| Inbox | A submission row\\'s ⋮ → Open contact in CRM; a member or lead row\\'s Open in CRM. | A contact\\'s timeline opens the submission that captured them, in the Inbox reader. |\\n| Forms | A form\\'s page: See the contacts this form captured in the CRM, and the CRM routing card. | — |\\n| Products → Orders | An order\\'s dialog: View customer in CRM. | The contact header\\'s order count opens the orders list narrowed to the person\\'s address; a timeline entry opens the order itself. |\\n| Bookings | Each upcoming booking: View in CRM. | A timeline entry for a booking opens the Bookings page. |\\n| Users | A site user\\'s row menu: Open in CRM. | A timeline entry for a sign-up opens the Users page. |\\n| Dashboard | The Inbox card counts the site\\'s open leads and links to Leads; the CRM at a glance card\\'s figures open Contacts, Deals and Tasks. The organization\\'s Sites page carries CRM at a glance and Tasks due above the site grid, totaling every site and opening the organization-level hub. | — |\\n| Marketing | — | The Relationship card\\'s campaign attribution opens the campaign\\'s page. |\\n| Setup → Activity | — | Adding, deleting or converting a contact, company, deal or lead is logged, and each entry opens the record. |\\nA link that arrives by a person\\'s address — from an order, a booking or a site user — opens the Contac' },\n { path: '/content-and-data/crm/overview', title: 'CRM', heading: 'Capture replies', anchor: '#capture-replies', text: 'The CRM sends one-to-one email and follows its delivery, but the reply lands in your own mailbox. To file it on the record, forward it — or BCC your own outgoing mail — to the workspace\\'s capture address, crm+…@in.aglyn.com, shown in the Send email dialog and under CRM → Settings. The message is filed on the timeline of the contact or lead it was with, as Captured email describes.\\nIf you send from an address other than the one you sign in with, such as a Gmail send-as alias on an outbound domain, add it under Your sending addresses and open the verification link it is sent. Until you do, a BCC from that address is read as a stranger writing in and filed under the alias, not under the person you wrote to.' },\n { path: '/content-and-data/crm/overview', title: 'CRM', heading: 'At the organization level', anchor: '#at-the-organization-level', text: 'The CRM tab under Organization opens the same hub over every site in the organization at once: the same eight sections, the same record pages, the same bulk actions, import, saved views, reports and settings, at …/{organization}/crm/ . A record captured on any of your sites is listed there, and opening it edits the same record the site\\'s own hub edits.\\nWhat differs from a site\\'s hub:\\n- Every create names a site. A contact, company, deal or logged activity is always captured by a site — that is what decides which of your sites may see it and whose marketing consent a contact\\'s checkbox records — so New contact, New company, New deal, Log activity and Import CSV each begin with a required Site picker. The pick is remembered for the session, and an organization with exactly one site never sees the field. The REST API asks the same question as consentSiteId. A task is the one exception: New task offers This organization (no site) beside the sites, for work the organization itself owes — see Organization tasks.\\n- The cross-site facts. The contacts list gains a Known by column — the sites that have captured each person — and a contact\\'s page gains a Known by card listing those sites, each with the person\\'s marketing consent for that site (opted in, opted out, or no record — never a bare \"consented\") and a link to open the person in that site\\'s hub.\\n- Reports total the organization, not one site — the Lead funnel included, which reads every site\\'s leads, at most 200 per site, and says so when a site held more.\\n- Leads are listed from every site with a Site column, since a lead belongs to the site that met the person; the per-site note about which forms file a lead stays on each site\\'s own hub.\\n- Every action runs here too. A deal moves on the organization\\'s board, two records ' },\n { path: '/content-and-data/crm/overview', title: 'CRM', heading: 'Who can open the CRM', anchor: '#who-can-open-the-crm', text: 'Opening the CRM takes the workspace\\'s manage data permission — owners, admins and editors by default, or a custom role granting it. What a reader then sees follows the site they have open: a record captured on one site is visible to that site, to every site declared to be one sender with it, or to the whole organization when the organization has chosen to share its data. A collaborator scoped to one site sees that site\\'s people, deals and tasks and no others. The rule is the same in every section, so no section can show a record the reader could not open as a contact.' },\n { path: '/content-and-data/crm/overview', title: 'CRM', heading: 'Related', anchor: '#related', text: '- The contact record · Leads · Companies · Deals pipeline · Tasks & follow-ups · Reports · Custom fields\\n- Forms & lead capture\\n- Email campaigns\\n- Automations for the CRM\\n- CRM by AI: summaries, next steps, email drafts and column matching\\n- REST API — contacts, companies, pipelines, deals, tasks and activities' },\n { path: '/content-and-data/crm/reports', title: 'Reports', heading: '', anchor: '', text: 'Reports\\nReports is the CRM in aggregate. Open it from the CRM\\'s section rail or at …/hosts/{site}/crm/reports. Every figure on the page is computed over the same records the other sections list — a report can never count a contact, deal or task the reader could not open — and most of them are counted by the database rather than downloaded, so the page stays quick on a large CRM.' },\n { path: '/content-and-data/crm/reports', title: 'Reports', heading: 'Choosing a period', anchor: '#choosing-a-period', text: 'A picker at the top of the page selects the window the flow reports measure: Last 7 days, Last 30 days (the default), Last 90 days or This month. A day period counts back from the moment you pick it; \"This month\" starts on the first of your calendar month. Each period is compared to the one before it of the same length — thirty days against the thirty before them, this month against last month — which is what the percentage next to a figure is measured against.\\nThree cards ignore the period on purpose. The Pipeline, Forecast by close month and Tasks cards describe what is open now; \"the pipeline over the last 30 days\" would be a different question.\\nEvery card with a table carries an Export CSV button — see Exporting a table.' },\n { path: '/content-and-data/crm/reports', title: 'Reports', heading: 'Contacts', anchor: '#contacts', text: '- New contacts in the period, with the change against the previous period. The percentage is left off when the previous period had nothing — a first contact has no growth rate.\\n- Total contacts visible to this site.\\n- New contacts per week — one bar per seven days from the start of the period; the last bar may be shorter.\\nA contact is \"new\" on the day it was first captured, not the day it was last edited.' },\n { path: '/content-and-data/crm/reports', title: 'Reports', heading: 'Sources and lifecycle', anchor: '#sources-and-lifecycle', text: '- By source — how many contacts came through each capture surface (forms, orders, bookings, members, and so on). A person captured two ways counts under both.\\n- Lifecycle funnel — one row per lifecycle stage. Each row counts everyone who reached that stage or went further, so a customer also counts as a lead, and the funnel narrows as it goes down. Beside each row is how many people sit at exactly that stage now and the conversion from the step before. Contacts with no stage, and those marked Other, are reported under the funnel rather than folded into a step.\\nThese two charts need a field off each contact, which the database cannot aggregate, so they are read from the newest 1,000 contacts your site can see. When the CRM holds more than that, the card says so and calls the figures a sample.' },\n { path: '/content-and-data/crm/reports', title: 'Reports', heading: 'Conversion by source', anchor: '#conversion-by-source', text: 'Of the people each capture surface brought in during the period, how many are customers now.\\n- Captured — contacts first captured in the period, counted on the server.\\n- Now customers — of those, the ones who have bought: their lifecycle stage is Customer or Evangelist, or they have at least one order on the books whatever the stage says. A contact marked Other is not counted as a customer, because the report cannot know whether that stage is past a purchase.\\n- Conversion — customers over everyone captured.\\n- By source — one row per capture surface with its captured count, its customers and its conversion rate, most captured first. A person captured two ways counts under both sources, so the rows can add to more than the tile; the tile counts each person once. Contacts with no source on your site are counted in the tiles and left out of the table.\\nThis is a cohort: the people captured in the period, followed to where they stand today. A lead captured on the first day who bought three weeks later is a conversion that source earned; a long-standing customer who bought again this month is not evidence about this period\\'s sources. The table is read from the period\\'s newest 1,000 contacts, through your site\\'s own view of each person, and says so when the period held more.' },\n { path: '/content-and-data/crm/reports', title: 'Reports', heading: 'Lead funnel', anchor: '#lead-funnel', text: 'Of the leads this site captured in the period, where each one stands now, and why the ones closed without converting were closed.\\n- Leads captured — leads first seen in the period, counted on the server, with the change against the previous period.\\n- Qualified and Unqualified — how many of those captured have been converted, or closed without converting, each with its share of the leads captured.\\n- Still open — leads that are New or Working.\\n- Where they stand — one bar per status. A lead nobody has touched yet is New.\\n- Why leads were unqualified — the reasons given when leads were unqualified, most common first, each with its share of the unqualified leads. Different spellings of one reason are counted together; a lead closed with no reason is listed as No reason given.\\nA lead is placed by when it was first seen, so a returning visitor\\'s second form does not make a second lead in a second period. The funnel is read from the period\\'s 200 most recently captured leads — the same window the Leads list shows — and says so when the period held more. Because leads belong to a site rather than to the workspace, this card reports the site you are looking at; at the organization level it reads every site — at most 200 leads per site — and totals them, naming the per-site window when any site held more.' },\n { path: '/content-and-data/crm/reports', title: 'Reports', heading: 'Pipeline', anchor: '#pipeline', text: '- Open deals and their pipeline value — the face value of every open deal, counted on the server.\\n- Weighted forecast — each open deal at the odds of its stage (a deal in a 60% stage counts 60% of its amount). Won and lost deals are never in the pipeline: a won deal is revenue and a lost one is history.\\n- Deals by stage, per pipeline, each stage with its count, value and weighted value. A deal sitting in a stage the pipeline no longer has, or in no pipeline at all, is listed separately and is worth nothing to the forecast.\\n- Top open deals — the ten largest, each a link to the deal.\\n- Stuck deals — the open deals with no next activity: nothing scheduled against them. Painted as a warning above zero and a link to the deals list, whose No next activity chip lists them.\\nDeals in more than one currency are added as numbers and shown in the most common currency, with a note saying so.' },\n { path: '/content-and-data/crm/reports', title: 'Reports', heading: 'Forecast by close month', anchor: '#forecast-by-close-month', text: 'Every open deal, laid out by the month it is expected to close — one row per month for the next six, starting with the current month — and one column per pipeline, with a column for all of them together when there is more than one. Each cell shows the deals\\' face value, their weighted value (each at the odds of its stage) and how many there are.\\nThree rows sit beside the months so the column adds up to the open pipeline:\\n- No expected close — open deals with no close date. Their own row on purpose: it is the size of the pipeline nobody has scheduled, which is usually the number worth acting on.\\n- Before this month — deals whose expected close has passed. Shown only when there are any.\\n- Later — deals expected past the sixth month. Shown only when there are any.\\nThe last row is the whole open pipeline per column. Months are your calendar months, and a deal dated the first of a month belongs to that month. Won and lost deals are never forecast. The card reads the same window of open deals the Pipeline card reads — the 1,000 most recently updated — and says so when the window was full.' },\n { path: '/content-and-data/crm/reports', title: 'Reports', heading: 'Won and lost', anchor: '#won-and-lost', text: '- Won in the period, with the value of what was won.\\n- Lost in the period.\\n- Win rate — won over everything that closed in the period.\\n- Won vs lost per week — two bars per week, placed by the moment each deal was closed.' },\n { path: '/content-and-data/crm/reports', title: 'Reports', heading: 'Won and lost by owner', anchor: '#won-and-lost-by-owner', text: 'The same closed deals, grouped by the person who owned each one at the close — not by whoever created it, so a deal reassigned mid-cycle counts for whoever carried it over the line.\\n- One row per owner: won, lost, the win rate over what closed for them, and the value of what they won.\\n- Rows rank by how much closed, not by win rate. An owner who closed one deal and won it is not the best closer on the team, and ranking by rate would say they were.\\n- Deals nobody owns are kept as a row called Unassigned rather than dropped, so the rows add up to what the card above them reports.\\n- An owner with nothing closed in the period has no rate rather than 0%.\\nThis card reads the same 500 most recently closed of each outcome the Won and lost card does, and says so when the period held more.' },\n { path: '/content-and-data/crm/reports', title: 'Reports', heading: 'Activity by teammate', anchor: '#activity-by-teammate', text: 'Who did what in the period, busiest first.\\n- Activities logged — every call, email, meeting, note and other activity logged in the period, counted on the server, with the change against the previous period.\\n- Tasks completed — tasks ticked off in the period, counted on the server.\\n- Teammates active — how many people logged or completed something.\\n- Who did what — one row per teammate: their calls, emails, meetings, notes and other activities, the total, and the tasks they completed. A task is credited to whoever completed it, or to its assignee when the completer was not recorded. Activities an automation logged, and tasks completed with nobody assigned, share one row called No teammate.\\nTeammates are named from the workspace roster when it knows them, and otherwise by the name each activity was signed with, so a former member\\'s work still reads under their name. The table is grouped from the period\\'s 1,000 most recent activities and 1,000 most recently completed tasks and says so when the period held more; the tiles are always the server\\'s count.' },\n { path: '/content-and-data/crm/reports', title: 'Reports', heading: 'Tasks', anchor: '#tasks', text: '- Open tasks, Overdue (due before today) and Due today. Today and overdue are decided on your calendar day, the same way the Tasks list decides them, so the two never disagree about the same task.\\n- Open tasks by assignee — for each person, their overdue, due-today, upcoming and undated tasks, and the open total. Unassigned tasks are their own row.' },\n { path: '/content-and-data/crm/reports', title: 'Reports', heading: 'Exporting a table', anchor: '#exporting-a-table', text: 'Every card with a table — activity by teammate, conversion by source, the lead funnel, open tasks by assignee and the top open deals — has an Export CSV button beneath the table. The file holds exactly the rows on screen, in the same order with the same names and counts, so you can check it against the page; it is written in your browser from what the card has already read and costs no further reads.\\nBecause the file is the loaded window, it has the window\\'s limits: when a card is grouped from a bounded read — the 1,000 most recent activities, say — the caption beside the button says so, and the file holds the rows grouped from that window rather than from every record. The tiles above the table remain the server\\'s count. Files are named for the card and the period, such as crm-activity-30d.csv; the two cards that have no period name none.' },\n { path: '/content-and-data/crm/reports', title: 'Reports', heading: 'CRM at a glance', anchor: '#crm-at-a-glance', text: 'The site dashboard carries a CRM at a glance card with five numbers: contacts, new contacts this week, the value of every open deal, tasks due today or overdue, and leads to work — the leads on this site that are still new or being worked. Each is a link into the section that explains it, and each is a single server-side count or sum — the card never downloads records to count them. Switch it off from the dashboard\\'s customize dialog if you do not want it there.\\nThe organization\\'s Sites page (Organization → Sites) carries the same card above the site grid, for an organization owner, admin or editor. There every figure totals the whole organization — the leads figure is the open leads on every site, added up — and each link opens the matching section of the organization-level hub. That row is not customizable: the customize dialog belongs to a site\\'s dashboard, and an arrangement made there does not reach the organization\\'s page.' },\n { path: '/content-and-data/crm/reports', title: 'Reports', heading: 'How the numbers are counted', anchor: '#how-the-numbers-are-counted', text: '- A figure that has not arrived, or that the database refused, shows as a dash — never as a zero. A zero is a measurement; a dash is the absence of one.\\n- Counts and sums are taken by the database. Where a chart needs a field off each record — a lifecycle stage, an assignee, the stage a deal sits in, its expected close, who logged an activity — it reads a bounded window (the newest 1,000 contacts, the period\\'s newest 1,000 contacts, the 1,000 most recently updated open deals, the 1,000 soonest-due open tasks, the 500 most recently closed deals of each outcome, the period\\'s 1,000 most recent activities and 1,000 most recently completed tasks, the period\\'s 200 most recently captured leads) and says when the window was full. Two cards that need the same window share one read.\\n- Everything follows the same per-site visibility as the contacts themselves. A collaborator scoped to one site sees that site\\'s totals, not the organization\\'s.\\n- The figures are remembered for a minute. Leaving the section to open a deal it named and coming straight back costs no reads and shows the same numbers; Refresh asks the database again at once, and a different period or a different site is always its own read.' },\n { path: '/content-and-data/crm/reports', title: 'Reports', heading: 'Related', anchor: '#related', text: '- CRM overview\\n- Deals pipeline — the stages and probabilities the pipeline and forecast figures read\\n- Tasks & follow-ups — the views the task counts mirror\\n- Leads — the statuses and reasons the lead funnel counts\\n- Activities — what the activity leaderboard counts\\n- The contact record — the lifecycle stages the funnel is built from' },\n { path: '/content-and-data/crm/sequences', title: 'Sequences', heading: '', anchor: '', text: 'Sequences\\nRolling out Sequences is a release-flagged feature, currently being rolled out — it is not available in your workspace yet. This page says what it is for, and grows with the feature.' },\n { path: '/content-and-data/crm/sequences', title: 'Sequences', heading: 'What it is for', anchor: '#what-it-is-for', text: 'Sequences is for one-to-one selling. A rep writes a short sequence — a first email and the follow-ups after it, each a set number of days apart — and enrolls people from the CRM in it. Every email goes to one person, as a message from the rep rather than as a campaign.\\nIt is not a way to send one message to an audience. That is what email campaigns are for, sent from your site\\'s sending identity to a segment of your contacts.' },\n { path: '/content-and-data/crm/sequences', title: 'Sequences', heading: 'Sent from your own mailbox', anchor: '#sent-from-your-own-mailbox', text: 'A sequence sends from the rep\\'s own mailbox, connected to the workspace through the rep\\'s Google account. The email leaves from that address, sits in that rep\\'s sent mail like any other, and a reply comes back to the same inbox — which is what makes it a conversation between two people rather than a broadcast.' },\n { path: '/content-and-data/crm/sequences', title: 'Sequences', heading: 'Where it lives', anchor: '#where-it-lives', text: 'Sequences is a tab of its own at the organization level, beside the CRM. It has three sections: All sequences, Mailboxes and Compliance.\\nOpening it takes the Use Sequences permission, which owners and admins hold by default. A custom role can grant it to other members of the organization. A collaborator added to particular sites cannot open it, because Sequences covers the whole organization.' },\n { path: '/content-and-data/crm/sequences', title: 'Sequences', heading: 'Connect a mailbox', anchor: '#connect-a-mailbox', text: 'Each rep connects their own Google mailbox in Sequences → Mailboxes:\\n1. Select Connect with Google and choose your Google account.\\n2. Google asks you to let Aglyn send email on your behalf and read your email. Allow both. Sending is how a sequence\\'s messages go out as you; reading is how a reply or a bounce is noticed, so a sequence stops when somebody answers. If either is left unticked, the connection is refused and nothing is saved.\\n3. You come back to Mailboxes with the mailbox listed as Active.\\nThe access Google grants is stored encrypted and is never shown to anyone, administrators included. Connecting the same Google account again updates the mailbox you already have and keeps its settings. Each member can connect up to five mailboxes.\\nIf Mailboxes says connecting a Google mailbox isn\\'t configured, your deployment has no Google OAuth client set up. On a self-hosted install, see Environment variables.\\nSend-as address and display name {#send-as}\\nA mailbox sends as one address: the account\\'s own, or an alias Gmail has already verified for it — one added under Settings → Accounts → Send mail as in Gmail and confirmed there. Only addresses Gmail lists as verified are offered. To use a new alias, add it in Gmail first, then connect the mailbox again so the new alias is seen.\\nIf the same address is waiting for verification under Your sending addresses, connecting the mailbox verifies it there too, because Gmail has already confirmed you can send as it.\\nThe display name is the name recipients see beside the address.\\nDaily cap, warm-up and sending window {#daily-cap}\\n- Daily cap — the most the mailbox sends in one day. It starts at 20 and can be set up to 50.\\n- Warm-up — a newly connected mailbox ramps up: at most 10 a day in its first week, 20 in its second and 30 from i' },\n { path: '/content-and-data/crm/sequences', title: 'Sequences', heading: 'Compliance settings', anchor: '#compliance-settings', text: 'Every email a sequence sends ends with a footer saying who sent it, where they can be reached by post, that the email is a business solicitation, and how to stop more of them:\\nThe United States\\' CAN-SPAM Act requires those things of a commercial email, so the footer is added to every email after your text — no template or merge field can leave it off. You set what it says in Sequences → Compliance:\\n- Legal name — your organization\\'s legal name, as the footer prints it.\\n- Brand name — the name the solicitation sentence uses, when it isn\\'t the legal name. Leave it empty to use the legal name.\\n- Postal address — a valid physical postal address: a street address, a post office box registered with the US Postal Service, or a private mailbox registered with the US Postal Service under its rules for commercial mail receiving agencies. Write it over as many lines as it takes; the footer prints them on one line.\\nNo sequence can be activated while the legal name or the postal address is empty, and an email with neither is never sent.\\nAllowed countries {#allowed-countries}\\nThe countries a sequence may send to at all, United States by default. Every sequence sends only to the countries both it and this list allow, so taking a country off here takes it off every sequence at once.\\nThe others are off because the law is different there. Canada, the United Kingdom and most of the European Union require a consent basis that an email to someone who never contacted you does not have. So a cold email — to someone who never filled in a form, signed up, ordered or booked with you, or wrote to you — never goes outside the United States, whatever this list says. Add another country only for people who came to you first.' },\n { path: '/content-and-data/crm/sequences', title: 'Sequences', heading: 'Sequences', anchor: '#sequences', text: 'A sequence is the emails, and the tasks between them, one person gets from one rep. Sequences → All sequences lists each one with its mailbox, its status, and how many people it has enrolled, and how many of them are still active, replied, bounced or opted out.\\nBuild a sequence {#build-a-sequence}\\nSelect New sequence, name it, and choose the site whose CRM the people you enroll are contacts of and the mailbox it sends from. A sequence sends as the member who connected its mailbox, so only that member, or an organization owner or admin, can edit or activate it.\\nThe Mailbox list offers the mailboxes you connected, and an owner or admin every mailbox in the organization. A disconnected mailbox is never offered. When exactly one of your own mailboxes is active, a new sequence starts on it. With none, connect one first.\\nA sequence holds up to eight steps, of which up to four are emails:\\n- Email — a subject and a body, or a CRM email template whose body is sent. Insert merge fields — the contact\\'s first name, company or job title, your own name, the site\\'s name — and {{enrollment.personalLine}}, the sentence you write for each person when you enroll them. Every email after the first replies in the first one\\'s thread unless you turn Reply in the same thread off, in which case it starts a new thread and needs its own subject.\\n- Task — a LinkedIn touch, a Call or a To-do for you, with a title.\\nEach step waits a number of business days after the one before it — up to 30, and at least 1 between emails — counted in the mailbox\\'s timezone. The first step can wait 0 days: it goes at the next opening of the sending window. By default a sequence sends in its mailbox\\'s sending hours; Sending hours sets hours of its own for this sequence.\\nThe Preview beside the editor shows each email as' },\n { path: '/content-and-data/crm/sequences', title: 'Sequences', heading: 'Enroll people', anchor: '#enroll', text: 'Select Enroll people on an active sequence, and choose them from a saved Contacts view or by searching your contacts at the sequence\\'s site — up to 50 at a time. Enrolling reads your contacts, so it takes the Manage data permission as well as Use Sequences.\\nBefore anything is enrolled, each person is marked:\\n- Eligible — enrolled as they are.\\n- Needs you — enrolled once you\\'ve written their personal line and confirmed what\\'s asked below.\\n- Blocked — not enrolled, with the reason: a personal mailbox, which a cold email never goes to; an address outside the allowed countries, or in no known country; on the platform\\'s or your site\\'s suppression list after a bounce, a complaint or an unsubscribe; opted out of sales email from the site; on your organization\\'s do-not-contact list; a member of your workspace; a customer, unless the sequence includes customers; or already in a sequence.\\nCold contacts {#cold-contacts}\\nA contact is cold when nothing on your site shows they came to you: no form, sign-up, order or booking, and no email from them. For each cold contact you write a personal line — one sentence on why you\\'re writing to this person now, which the sequence puts where {{enrollment.personalLine}} is — and confirm three things only you can know:\\n- This is a US business address.\\n- They or their company published this address, or they gave it to us — never guessed from a name, and never bought.\\n- This address was verified as deliverable.\\nEach confirmation is stored on the enrollment with who made it and when.\\nEnroll checks every person again as it enrolls them, so someone who unsubscribed a moment ago is refused even though they were eligible when you looked. A person goes through a sequence once: they can\\'t be enrolled in the same sequence again, even after it finishes.' },\n { path: '/content-and-data/crm/sequences', title: 'Sequences', heading: 'Enrollments', anchor: '#enrollments', text: 'A sequence\\'s Enrollments tab lists everyone in it: their status, the step they\\'re on, when the next one is due in the mailbox\\'s timezone, when the last one went, and why they stopped when they have. For each one you can:\\n- Pause and Resume — nothing is sent while they\\'re paused.\\n- Stop — they get nothing more from this sequence.\\n- Mark do-not-contact — puts the address on your organization\\'s do-not-contact list, which every sequence checks before every send, and stops them in every other sequence too.' },\n { path: '/content-and-data/crm/sequences', title: 'Sequences', heading: 'How sends are scheduled', anchor: '#sending', text: 'Every 15 minutes, the steps that have come due are sent, from each rep\\'s own mailbox:\\n- In the sending hours of the sequence, or of its mailbox, read in the mailbox\\'s timezone. A step that comes due outside them waits for the next opening.\\n- Within the day\\'s limit — the daily cap and the warm-up — spread across the sending hours, so a day\\'s emails do not all leave in its first half hour. One run sends at most five emails from a mailbox, follow-ups before first emails.\\n- After every check again. Everything enrolling checked is checked again before each email — the do-not-contact list, the suppression lists, the site\\'s sales-email opt-outs, whether the person became a customer or joined your workspace — because a person enrolled on Monday can unsubscribe by Thursday.\\nEach email is sent once: two runs that overlap never send the same step twice, and never go over a mailbox\\'s cap between them. It is filed on the contact\\'s timeline in the CRM as an email from the rep, and a task step becomes a CRM task for the rep, due the day it comes due.\\nNothing is sent while your organization\\'s legal name or postal address is missing, or while a mailbox is paused or needs reconnecting — those sequences wait.' },\n { path: '/content-and-data/crm/sequences', title: 'Sequences', heading: 'What stops a sequence', anchor: '#what-stops-a-sequence', text: 'Each connected mailbox is read every 15 minutes for what came back:\\n| What happens | What it does |\\n| The person replies | Stops the sequence (Replied), files the reply on the contact\\'s timeline, and gives the rep the task Reply from their name. |\\n| An out-of-office reply | Keeps the sequence going, and moves the next step to at least five business days after the reply. |\\n| They ask not to be emailed — a reply such as \"no\" or \"unsubscribe\", the unsubscribe link, or a message to the unsubscribe address | Stops the sequence (Opted out), puts the address on your do-not-contact list, unsubscribes it from the site\\'s Sales outreach email, and stops it in every other sequence. |\\n| The email hard-bounces | Stops the sequence (Bounced) and puts the address on your do-not-contact list and on the platform\\'s suppression list. |\\n| A check refuses them before the next email — a customer now, a member of your workspace, on a suppression list | Stops the sequence, saying which check. |\\n| The mailbox is disconnected or removed, or the member who connected it leaves the organization | Stops the sequences that send from it. |\\n| The next email needs something the contact doesn\\'t have — a first name for {{contact.firstName}} | Pauses the enrollment, naming what is missing. Fill it in and select Resume. |\\nA reply that arrives after the sequence finished, or after it was stopped, is still read: the enrollment moves to Replied, or to Opted out when the reply asks not to be emailed.' },\n { path: '/content-and-data/crm/sequences', title: 'Sequences', heading: 'Unsubscribe', anchor: '#unsubscribe', text: 'Every email a sequence sends carries two ways out beside the footer\\'s \"reply no\":\\n- a one-click unsubscribe link in the email\\'s List-Unsubscribe header, which Gmail, Yahoo and most mail apps show as an Unsubscribe button;\\n- an unsubscribe address — the rep\\'s own address with +unsubscribe added — for mail apps that unsubscribe by email.\\nEither one stops the person\\'s sequences, puts the address on your do-not-contact list and unsubscribes it from the site\\'s Sales outreach email. The link needs no sign-in, shows a plain page saying it is done, and keeps working for good — including after Sequences is paused for your workspace.\\nWhen a person is erased from your workspace, their enrollments are deleted with them. Their do-not-contact entry stays — it holds no address, only a one-way key — so they are never emailed again.' },\n { path: '/content-and-data/crm/sequences', title: 'Sequences', heading: 'Related', anchor: '#related', text: '- CRM\\n- Email campaigns\\n- Custom roles' },\n { path: '/content-and-data/crm/settings', title: 'CRM settings', heading: '', anchor: '', text: 'CRM settings\\nCRM → Settings holds what the CRM does on its own, for every site in your workspace. A setting here is a fact about how your business files people, not about one site, so it is stored on the workspace and applies wherever a contact is captured. The section lives in the CRM hub at …/hosts/{site}/crm/settings, last in the rail, and at the organization level at …/{organization}/crm/settings, where it also holds the Recipes card.\\nOnly a workspace owner or admin can change a setting. Everyone who can open the CRM can read the page; a member who cannot change a switch sees it disabled, with a note saying why. Two cards are exceptions: the Email templates card, which every CRM editor may write to, because a template is working material, not policy; and Your sending addresses, which is each member\\'s own list.' },\n { path: '/content-and-data/crm/settings', title: 'CRM settings', heading: 'Companies', anchor: '#companies', text: 'Create companies from work email domains\\nOff by default.\\nA contact captured with a work email address — a form submitted from jane@acme.com, a booking, an order, a sign-up — is linked to the company whose domain is acme.com the moment the contact is created, provided exactly one such company is visible to the capturing site. That happens whether this switch is on or off; see Companies — Linked on capture.\\nThe switch decides what happens when no company carries the domain:\\n- Off — nothing is created. The contact waits for somebody to pick or create the company from their record, or for the company to be created from the Companies list, after which the next capture at that domain links on its own.\\n- On — a company is created from the domain, named after it (acme.com becomes Acme, initech.co.uk becomes Initech), with the domain filled in and the new contact already linked. It is created in the same scope the contact landed in — the sites that present as one sender, or the whole workspace when the workspace shares its data — so a site never sees a company it could not see the contact for. Rename the company, add its owner and address, from its own page.\\nAddresses at a public mailbox — Gmail, Outlook, iCloud, Yahoo, Proton and the like — never create a company and never link to one, on or off. A consumer list is not a list of accounts, and a company called \"Gmail\" with half your contacts at it is the one thing this setting must never produce.\\nA contact the CRM already knows — a repeat form submission by somebody in the address book — is not linked again. A contact added by hand with a company picked, or imported with a company column, keeps the company you chose; the domain is not consulted.' },\n { path: '/content-and-data/crm/settings', title: 'CRM settings', heading: 'Default owner', anchor: '#default-owner', text: 'Who gets the contacts captured on this site when no assignment rule claims them. Pick a member of the workspace, or Nobody — leave unassigned, which is the default and is what the CRM did before the setting existed.\\nThe default is per site — an agency workspace gives each client\\'s site its own rep — but it is stored on the workspace with the other CRM settings and changed from any site\\'s Settings section for that site.\\nA contact captured on the site — a form submission, a sign-up, a booking, an order, a newsletter capture — that no rule claims is handed to the default owner the moment the contact is created, and they get a console notification (Contact assigned to you, or Lead assigned to you when the site filed a lead for the same person, linking to the record they will work). Only a new contact is assigned: a repeat visit by somebody the workspace already holds changes nothing, and a contact added by hand, imported with an owner column, or converted from a lead with an owner picked keeps the owner you chose.\\nA default owner who later leaves the workspace is shown as a former member and assigns nobody until you pick somebody else.' },\n { path: '/content-and-data/crm/settings', title: 'CRM settings', heading: 'Assignment rules', anchor: '#assignment-rules', text: 'An ordered list of rules tried, top to bottom, for every new contact captured on any site in the workspace. The first rule whose every condition holds assigns the owner; a contact no rule claims goes to the capturing site\\'s default owner, or stays unassigned. Rules are what a sales team sets up first — bookings to one rep, a partner\\'s domain to another, everything else in turn.\\nAdd rule opens a drawer with four optional conditions and a target:\\n| Condition | Matches when |\\n| Source | The contact came in through that door — Form, Booking, Customer (an order), Member (a sign-up), Newsletter, API, Import, or Added by hand. Any source when blank. |\\n| Form | The capture came through the form with that id (from the form\\'s address in Forms). Any form when blank. |\\n| Email domain | The captured address is at that domain — acme.com, typed with or without the @. Unlike the company link, a public mailbox such as gmail.com counts here, so a rule can route consumer sign-ups to one rep. |\\n| Tag | The capture carries the tag, or the contact already has it. Case does not matter. |\\nA rule with every condition blank matches everything, which is how a catch-all is written — put it last. The target is a team member, picked from the workspace roster by name, or Round robin, the next member of the pool in turn.\\nA rule naming a member who has since left the workspace is skipped, as is a round-robin rule while the pool is empty; the next rule down is tried. Use the arrows to reorder, and the row menu to delete. A workspace keeps at most 50 rules.\\nRules run for every capture door on every site, including a lead that is converted without an owner picked, and only on a contact that has no owner yet. To reassign a contact that already has one, use an automation — the Assign the contact an owner st' },\n { path: '/content-and-data/crm/settings', title: 'CRM settings', heading: 'Round robin', anchor: '#round-robin', text: 'The members handed contacts in turn by a round-robin rule, or by an automation set to round robin. Tick the members to include; the order they were ticked is the rotation order, shown beneath the list with who is next up.\\nEach new record goes to the member after the one who got the last record, wrapping round to the first. The pointer is moved by the server in the same write that assigns the owner, so two contacts captured at the same moment go to two different people, and editing the pool — adding, removing or reordering a member — never skips or repeats anybody: the next record goes to whoever follows the last recipient in the pool as it now stands. A member who leaves the workspace is skipped. A pool can hold up to 50 members.' },\n { path: '/content-and-data/crm/settings', title: 'CRM settings', heading: 'Email templates', anchor: '#email-templates', text: 'The Email templates card lists every template and snippet you can use in Send email — the team\\'s shared ones and your own personal ones — with its kind and who it is for. New template opens a drawer for the name, the kind, who it is for, the subject and the message; a row\\'s menu edits or deletes it. A template written under a site is that site\\'s; one written from the organization\\'s own CRM is the whole workspace\\'s, and every site lists it. Merge fields, the preview, and what shared and personal mean are on Email templates & snippets.' },\n { path: '/content-and-data/crm/settings', title: 'CRM settings', heading: 'Email capture', anchor: '#email-capture', text: 'The Email capture card holds the workspace\\'s one capture address — crm+…@in.aglyn.com — with Copy and Rotate address. A message forwarded to it, or sent with it in BCC, is filed on the timeline of the contact or lead it was with, so a reply that reached your own mailbox lands on the record too. How a message is matched, what is kept and what is dropped is described under Captured email.\\nThe address is the workspace\\'s, not a site\\'s: the card reads the same under every site and at the organization level, and the record\\'s own site decides which timeline the message lands on. It is minted the first time somebody opens the card or the Send email dialog, and everybody who can open the CRM can copy it.\\nRotate address replaces it, behind a confirmation, and only a workspace owner or admin may. The moment the new address is written the old one goes quiet: mail sent to it afterwards is dropped without a trace, so a mailbox rule or a saved shortcut that carried the old address has to be updated. Rotate when the address has leaked outside the team — anybody who holds it can file mail on your records, though only mail from or to somebody already in the CRM is ever filed.' },\n { path: '/content-and-data/crm/settings', title: 'CRM settings', heading: 'Your sending addresses', anchor: '#your-sending-addresses', text: 'Directly under Email capture, Your sending addresses lists the addresses you send from besides the one you sign in with, such as a Gmail Send mail as alias on an outbound domain. Emails you send from these addresses and BCC to your capture address are filed under the person you wrote to.\\nWithout the list, a message from an address the workspace does not know is yours reads as a stranger writing in. The capture files it under the alias itself, as Received, or drops it when the alias is on no record, and the person you wrote to gets nothing on their timeline.\\n- Add an address and a verification email is sent to it. The row reads Verification email sent, and Resend sends another.\\n- Open the link in that email while signed in as yourself. The row then reads Verified, and from then on mail from that address is filed as yours: Sent, on the record of the person in To, with your name on the entry.\\n- An address that is not verified changes nothing. A link opened while signed in as anyone else confirms nothing, and a link stops working after 24 hours or once its address is removed.\\n- Connecting a Google mailbox in Sequences verifies any address on your list that Gmail already lists as one the account can send as — Gmail has confirmed it, so no email is needed. An address you have not added is never added for you.\\n- Remove takes an address off the list. Removing a verified address asks first, and adding it again sends a new verification email.\\nEach member keeps their own list in each workspace, and nobody else can see or change it. It reads the same under every site and at the organization level. The address you sign in with already counts, so it is never listed. You can keep up to five addresses. Leaving the workspace, or erasing your account, removes your list.\\nAn address that t' },\n { path: '/content-and-data/crm/settings', title: 'CRM settings', heading: 'Recipes', anchor: '#recipes', text: 'At the organization level only — …/{organization}/crm/settings — the Settings section ends with a Recipes card: the four automation recipes, each with the sites that already carry it and an Install button. Automation is a site feature, and the site\\'s own Actions page opens a recipe in the editor for you to change and save; this card is for the organization that runs several sites and wants the same automation on more than one of them without building it on each.\\nInstall opens a drawer: pick the site, and for Tag by form one of that site\\'s live forms, then confirm. The action the recipe builds is written into that site\\'s Automation → Actions exactly as the recipe defines it — enabled, with the trigger, conditions and steps the recipes table describes — and stamped with the recipe it came from. The card then links to the site\\'s Actions page, where the action can be edited like any other. Installing needs the same Manage data permission the organization\\'s CRM needs, and the plan that carries the actions builder; on a plan without it the buttons are disabled and say so.\\nEach recipe lists the sites it is installed on, read from that stamp, and links each into the site\\'s Actions page. A site that carries actions saved before the stamp existed is listed as may already have it: those actions could have started from the recipe, and the card will not call the site clear. Installing the same recipe on a site that already carries it is refused — Already installed on this site — rather than installed twice; delete the site\\'s copy from its Actions page first if you want a fresh one.' },\n { path: '/content-and-data/crm/settings', title: 'CRM settings', heading: 'Related', anchor: '#related', text: '- CRM overview\\n- Captured email — how a message sent to the capture address is matched to a record\\n- Email templates & snippets — what the Email templates card keeps, and the merge fields\\n- Automations for the CRM — what each recipe builds\\n- Companies — the records these settings create and link\\n- The contact record — picking a company or an owner by hand\\n- Leads — a lead follows its contact\\'s owner\\n- Automations for the CRM — reassigning, and rotating, after a contact is created' },\n { path: '/content-and-data/crm/tasks', title: 'Tasks & follow-ups', heading: '', anchor: '', text: 'Tasks & follow-ups\\nA task is a piece of work somebody on your team owes a person in the CRM: a call to return, an email to send, a meeting to hold, or a plain to-do. Every task has a title, a kind, a priority, an optional due date and time, an optional assignee, notes, and a link to the contact, company or deal it is about.\\nTasks live in the CRM hub at …/hosts/{site}/crm/tasks and, over every site at once, at …/{organization}/crm/tasks; every record page carries its own short list of them. They follow the same per-site visibility as the contacts themselves: a task made from one site\\'s console is seen from that site (and the sites it shares an audience with), and an organization that has widened its default sharing sees every task everywhere. A task is also the one CRM record that can belong to no site at all — see Organization tasks.\\nPlan availability Tasks are part of the CRM, included from Starter. On Free the section is shown locked, with the rest of the CRM. See What each plan includes.' },\n { path: '/content-and-data/crm/tasks', title: 'Tasks & follow-ups', heading: 'The tasks page', anchor: '#the-tasks-page', text: 'The Tasks section is one list with six views, chosen from the control above it:\\n| View | What it shows |\\n| My tasks | Open tasks assigned to you, soonest due first. Tasks with no due date come last. |\\n| Overdue | Open tasks whose due date is before today. |\\n| Today | Open tasks due at any time today. |\\n| Upcoming | Open tasks due tomorrow or later. |\\n| All open | Every open task, whoever it is assigned to. |\\n| Done | Completed tasks, most recently due first. |\\n\"Overdue\" and \"today\" are not stored on the task — nothing runs at midnight to stamp them. Each view is a window over the due date computed from your own clock and time zone when you look, and a tab left open across midnight repaints yesterday\\'s work as overdue on its own.\\nEach row shows a checkbox that completes or reopens the task, the title (with the first line of notes under it), the kind, the priority, the due date colored by where it stands — red when overdue, amber when due today — with a snooze beside it, the assignee, and the record it is for, as a link into that record\\'s page. A view shows at most 200 rows and says so when it is full; narrow the view to see the rest.\\nThe calendar view\\nList and Calendar sit beside the view control. The calendar draws one month, placing each task on the day it is due, and clicking one opens the same drawer a row does. The arrows page a month at a time and Today comes back.\\nThe calendar draws the same tasks the list is showing — the same view, the same window, and no extra reading — so a month that looks empty may simply be a month the view\\'s tasks are not in. It says so underneath: how many of them are due outside the month on screen, how many have no due date at all (a calendar cannot place those), and whether the view is showing only its first page.\\nA day that has more t' },\n { path: '/content-and-data/crm/tasks', title: 'Tasks & follow-ups', heading: 'Organization tasks', anchor: '#organization-tasks', text: 'Every other CRM record is captured by a site — a contact, a company, a deal is a fact about a person some site met. A task need not be: renewing the agency\\'s own insurance or chasing the organization\\'s own invoice is owed by nobody\\'s brand. So on the organization\\'s hub the New task drawer\\'s Site picker offers This organization (no site) beside your sites — and shows even when the organization has only one site, because there is a choice to make. Under a site\\'s hub the picker never appears; a task made there belongs to that site, as it always has.\\nAn organization task records no site. It is listed from the organization\\'s hub, and — like any record shared with the whole organization — from every site\\'s tasks list and dashboard card too: sharing with the organization is sharing with its sites. What sets it apart is that no site created it, so completing one fires no taskCompleted event (there is no site whose automations could hear it), the assignee\\'s notification opens the organization\\'s hub rather than a site\\'s, and the REST API reports its siteId as null. Opening an organization task at the org level says so under its title — Filed with the organization — no site — where a site\\'s task names its site.\\nA task made from a record\\'s page on the organization\\'s hub defaults to the record\\'s own site, as before; This organization is a choice you make, never a default.' },\n { path: '/content-and-data/crm/tasks', title: 'Tasks & follow-ups', heading: 'Tasks on a contact, company or deal', anchor: '#tasks-on-a-contact-company-or-deal', text: 'Each record\\'s page has a Tasks card listing that record\\'s open tasks, soonest due first, with the same checkbox to complete one inline; the card\\'s heading counts how many are open and how many are done. New task on the card opens the drawer with the record already linked, and All tasks jumps to the section.' },\n { path: '/content-and-data/crm/tasks', title: 'Tasks & follow-ups', heading: 'Reminders', anchor: '#reminders', text: 'Every task with a due date and time has a reminder at that time. Within the hour after it, the assignee gets a console notification — Task reminder, opening the task\\'s contact, deal or company page, or the tasks list when it is linked to nothing — and one email listing every task of theirs that fell due in that hour. The drawer\\'s Remind me field, under Due, shows it.\\n- It follows the due date. Move the due date — in the drawer, with a snooze, or over the API — and a reminder that sat on the old due time moves with it. Set Remind me to a time of your own (an hour before, the evening before) and it stays there when the due date moves. The bulk bar\\'s Set due moves due dates only; a reminder on a task it touches stays where it was.\\n- No reminder clears it. A task with no due date has no reminder unless you set one, and a task made before reminders existed has none until you set one.\\n- It fires once. A reminder that has been sent is not sent again; changing its time makes a new one. Completing a task cancels a reminder that has not fired yet, and reopening the task does not bring it back.\\n- It goes to the assignee, and only to a member who can open the CRM (the same manage data permission as everything else here). A task assigned to nobody reminds nobody; the digest is where unassigned work shows up.\\nReminders are checked once an hour, at the top of the hour, so \"remind me at 3:00\" arrives between 3:00 and 4:00. The time in the message is written in the same time zone the daily digest uses. Reminders are sent only in organizations whose plan includes the daily digest — a plan that gets no digest gets no reminders either.\\nTasks created by an automation or over the REST API get the same default: a reminder at the due time, unless the API call says otherwise (remindAt).\\nTurning' },\n { path: '/content-and-data/crm/tasks', title: 'Tasks & follow-ups', heading: 'Next activity', anchor: '#next-activity', text: 'Every contact, company and deal carries a Next activity: when its earliest open task is due. It is a column on the contacts, companies and deals lists — overdue in the warning color, today emphasized, a dash when nothing is scheduled — and a No next activity chip beside each list\\'s view control keeps only the records with nothing planned. A view saved with the chip on reopens with it on. On the reports page, the pipeline card\\'s Stuck deals counts the open deals with nothing scheduled.\\nThe figure is kept by every task write: creating, editing, snoozing, completing, reopening or deleting a task recomputes it for the records the task names, and so does a task filed by an automation or over the API. A record that existed before the figure did shows a dash until a task against it is written — or until Recompute next activity in the Fields section rewrites every record\\'s figure from its open tasks.' },\n { path: '/content-and-data/crm/tasks', title: 'Tasks & follow-ups', heading: 'The daily digest', anchor: '#the-daily-digest', text: 'Because overdue and today are read off the clock, nothing on a task fires on its own except its reminder. What says what is owed is the daily CRM digest: once a morning, at 8:00 (America/Chicago), every member with open work gets one console notification and one email saying what they owe — for example, \"3 tasks due today, 2 overdue, 1 unworked lead\".\\nIt counts, per organization:\\n- Overdue and due today — your open tasks, by the same day boundaries the Tasks page paints with, read in the digest\\'s time zone rather than yours.\\n- Unworked leads — leads on the sites you can reach that are still New after two days and have no owner, plus any lead whose owner is you. A lead with an owner is only on that owner\\'s list.\\nThe notification opens the Tasks section of the site the first task belongs to (or the Leads section when only leads are owed). The email lists each section — up to ten items apiece, with a count of the rest — and links to the same pages. Members who have nothing due, nothing late and no unworked lead get nothing; a digest is sent at most once per member per day, so a re-run on the same day reaches only whoever the first run missed.\\nThe digest goes only to members who can open the CRM (the same manage data permission as everything else here), and only in organizations whose plan includes it.\\nTurning it off\\nDaily CRM digest is a switch under Account settings → Notifications, on by default. Off, it stops both the notification and the email, on every workspace you belong to. Muting the Forms & bookings category on that same page silences the console notification only — the email still arrives while the digest is on. (A reminder is the other way round: the category mute is its only switch, and stops its email too.) See Workspace settings & notifications.' },\n { path: '/content-and-data/crm/tasks', title: 'Tasks & follow-ups', heading: 'The dashboard card', anchor: '#the-dashboard-card', text: 'The site dashboard shows a Tasks due card: how many of your tasks are overdue, how many are due today, and the next five assigned to you, each with its due date. View all opens the Tasks section. The card appears only on a workspace that has at least one open task, and only for readers who can open the CRM; a workspace that has never made a task sees no card at all.\\nThe same card sits at the top of the organization\\'s Sites page (Organization → Sites), above the site grid, for an organization owner, admin or editor. There it counts your open tasks across every site in the organization, and View all opens the Tasks section of the organization-level hub. A collaborator added to particular sites does not see the row — it reads across every site, which is the one thing a site-scoped membership cannot do.' },\n { path: '/content-and-data/crm/tasks', title: 'Tasks & follow-ups', heading: 'Who can do what', anchor: '#who-can-do-what', text: 'Tasks share the CRM\\'s permission: anyone whose role can manage data (owners, admins and editors by default, or a custom role granting it) can read, create, edit, complete and delete tasks on the sites they can reach. A collaborator scoped to one site sees that site\\'s tasks — and the organization\\'s, which every site shares — and no other site\\'s. Filing or completing a task from the organization\\'s hub takes what the hub itself takes: a member of the whole organization holding manage data.' },\n { path: '/content-and-data/crm/tasks', title: 'Tasks & follow-ups', heading: 'Related', anchor: '#related', text: '- CRM overview\\n- Activities & the timeline — what happened, as opposed to what is owed\\n- Bulk actions — complete, assign, reschedule, export and delete over a selection\\n- Import contacts from CSV — the same three steps the tasks import walks\\n- Reports — open, overdue and due-today tasks by assignee\\n- Automations for the CRM — the Create a CRM task step and the CRM task completed event\\n- Workflows & actions\\n- REST API — tasks' },\n { path: '/content-and-data/crm/views', title: 'Saved views', heading: '', anchor: '', text: 'Saved views\\nEvery list in the CRM — Contacts, Leads, Companies, Deals (the table) and Tasks — can be saved as a view: the filters narrowing it, the columns showing, and the column it is sorted by, under a name. A view is the list the way you work it — \"my open leads in Texas\" — and it is there again when you come back.\\nPlan availability Saved views are part of the CRM, included from Starter. On Free every CRM list is shown locked, and with it the views menu and the Contacts list\\'s segments.' },\n { path: '/content-and-data/crm/views', title: 'Saved views', heading: 'The views control', anchor: '#the-views-control', text: 'Above each list, a button names the view the list is showing — All contacts when none is — and opens the views menu:\\n- My views are the ones you saved for yourself.\\n- Shared with the team lists views a colleague saved and shared.\\n- On the Contacts list, your saved segments are offered under their own heading; choosing one puts its tags and sources on the filter bar.\\nBeneath them are the acts on the open view:\\n| Action | What happens |\\n| Save changes | The filters, columns and sort as they are now become the view\\'s. Offered on a view you may change — one you made, or any view when you are an organization-wide member. |\\n| Save as view… / Save as new view… | Name the current arrangement and keep it. Tick Share with the team to list it for everyone who can open the section. |\\n| Rename… | Change the name. |\\n| Share with the team / Stop sharing | List the view for the whole team, or take it back to yours alone. |\\n| Set as default / Clear default | Open the section on this view whenever you come to it. The default is yours: a colleague\\'s default is theirs. |\\n| Discard changes / Clear filters | Back to the view\\'s saved arrangement, or to the plain list. |\\n| Save as segment… | Contacts only — see Segments and views. |\\n| Delete view… | After a confirmation. The records themselves are untouched. |\\nA view you have changed is marked Modified beside its name, so a list that reads \"My open leads\" and shows something else cannot pass for the saved one.' },\n { path: '/content-and-data/crm/views', title: 'Saved views', heading: 'A view is a link', anchor: '#a-view-is-a-link', text: 'The address of a section carries the open view as ?view=…. Copy the address and a colleague who can open the section lands on the same list. On the Contacts list the key sits beside the ones a form\\'s page or the Inbox link with, so \"the people this form captured, in my usual view\" is one address.' },\n { path: '/content-and-data/crm/views', title: 'Saved views', heading: 'Filters on the Contacts list', anchor: '#filters-on-the-contacts-list', text: 'The Contacts list keeps its filters as chips above the table, and Add filter adds one: pick a field, a condition and a value. The fields are the list\\'s columns and a few that are not — owner, stage, source, company, tags, form, orders, lifetime value, created and updated dates, and one field per custom field your organization has defined. Owner, stage, source and company are picked from a list; a date from a calendar; anything else typed.\\nOne chip reaches every contact and the rest narrow what it found. The database answers one filter per query, so the first filter it can serve — a name, an email, a tag, a form, a date, an order count — is run against the whole collection, and every other filter narrows the newest thousand contacts that query returned. The served chip is filled; the others are outlined; and the caption under the chips says which is which. Owner, stage, source, company and custom-field filters always narrow rather than reach, because those facts are kept per site and no query can reach them directly. Sorting reorders the loaded window.\\nOn the other four lists the filter is the control the list already had — the companies grid\\'s column filter, the deals table\\'s All / Open / Won / Lost, the task view, the leads Show — and a view keeps it with the columns and the sort.\\nThe contacts, companies and deals lists also have a No next activity chip beside the view control: on, the list keeps only the records with no open task scheduled against them (see next activity). It is a clause on the view — a view saved with it on reopens with it on — and on the companies and deals lists it narrows the page on screen rather than the whole collection, the way a contacts filter that cannot be served does.' },\n { path: '/content-and-data/crm/views', title: 'Saved views', heading: 'Columns and sort', anchor: '#columns-and-sort', text: 'Manage columns in any list\\'s column menu chooses what shows, and Move left / Move right in the same menu put a column where you want it. A view keeps both — the choice and the order — and a view saved before a column existed shows the new column too, after the ones it names. Click a column header to sort; a view keeps that as well.' },\n { path: '/content-and-data/crm/views', title: 'Saved views', heading: 'Segments and views', anchor: '#segments-and-views', text: 'A segment is the older, narrower thing: saved tags and sources, usable as a campaign audience. Views keep everything a segment kept and more, and the two meet in two places:\\n- Every segment is offered in the Contacts views menu. Choosing one puts its tags and sources on the filter bar, where you can narrow further and save the result as a view.\\n- Save as segment… in the views menu keeps the tag and source filters of the current view as a segment. The other filters stay on the view.\\nA contacts view can also be an email audience in its own right. When you build an audience from a rule, pick it under Saved view, beside Saved segment: the view\\'s filters — owner, stage, company, tags, sources, dates, purchases and custom fields — always apply, the way a segment\\'s do. Only views whose filters can be an audience are offered: a view filtered by a name or an email, or by the updated date, describes a list on a screen rather than a set of people, and is left out. A view that is deleted, or edited past what an audience can express after a rule named it, makes that audience select nobody rather than everybody.' },\n { path: '/content-and-data/crm/views', title: 'Saved views', heading: 'Who sees what', anchor: '#who-sees-what', text: 'A view is read by whoever can read the section, but a view you have not shared is listed for you alone. Changing or deleting a view is the creator\\'s, or an organization-wide member\\'s; a colleague on the same site who opens a shared view can save a copy of their own, and cannot rename or remove yours. Your default view per section is kept on your own profile, beside your notification settings.' },\n { path: '/content-and-data/crm/views', title: 'Saved views', heading: 'Related', anchor: '#related', text: '- CRM — the hub and its sections\\n- Bulk actions — act on the rows a view shows\\n- Custom fields — each becomes a filter and a column\\n- Email campaigns — segments and views as audiences' },\n { path: '/content-and-data/datasets/import-export', title: 'Import & export', heading: '', anchor: '', text: 'Import & export\\nDatasets round-trip through CSV and JSON, so you can edit data in bulk elsewhere and bring it back.\\nPlan availability Starter and above (datasets need the data store).' },\n { path: '/content-and-data/datasets/import-export', title: 'Import & export', heading: 'Export', anchor: '#export', text: 'Export a dataset\\'s records to CSV or JSON — useful for backups, bulk edits in a spreadsheet, or moving data into another tool. The CSV and JSON buttons on the data toolbar export the whole dataset, not the page of records currently on screen.\\nWhat you get {#export-contents}\\n- Every record, in a stable order that is the same on every export of the same data.\\n- Every field in the model, in model order — the CSV\\'s header row is the field ids, and the JSON is an array of objects keyed by the same ids. A field with no value comes out empty rather than being omitted.\\n- Values in their portable form, which is the form the importer reads back: dates as ISO-8601, coordinates as latitude, longitude, lists and references comma-joined, and map fields as JSON. That is what makes the round-trip lossless.\\nThe file is named after the dataset. Nothing is filtered out — a search or sort you have applied in the table does not narrow the export.\\nLarge datasets {#large-exports}\\nThe server streams the file rather than assembling it in your browser, so the size of the dataset is not the limit it used to be. Two things follow:\\n- The button reads Exporting… while it runs. A large dataset takes a while; leave the page open until the download starts.\\n- The download is checked before it is saved. The server states how many records it is sending, and if fewer arrive — a dropped connection mid-transfer — the export is refused with \"Export incomplete\" and nothing is saved. Run it again. A short file is otherwise perfectly well-formed and gives you no way to notice it is short.\\nRecords added while an export is running may or may not be included; the count is taken when the export starts.' },\n { path: '/content-and-data/datasets/import-export', title: 'Import & export', heading: 'Import', anchor: '#import', text: 'Import CSV or JSON back into a dataset. Imports are validated against the model on the way in, so malformed rows are caught rather than silently corrupting your data.\\nUpsert on a key field\\nPick a \"Match on field\" in the import dialog to de-duplicate: rows whose key value (say, an email) matches an existing record update it in place instead of appending a duplicate, and repeated keys within the same file collapse to the last row. Only genuinely new rows count against your record limit.' },\n { path: '/content-and-data/datasets/import-export', title: 'Import & export', heading: 'Tips', anchor: '#tips', text: '- Export first to get the exact column shape, edit that file, then re-import.\\n- Keep reference fields pointing at valid record ids so relations survive the round-trip.' },\n { path: '/content-and-data/datasets/import-export', title: 'Import & export', heading: 'Related', anchor: '#related', text: '- Build a data model\\n- Relations' },\n { path: '/content-and-data/datasets/model-builder', title: 'Build a data model', heading: '', anchor: '', text: 'Build a data model\\nA model is the shape of a dataset — its fields and their types. Records you add or import in the console, or write through the REST API, are validated against it.\\nPlan availability Starter and above. Free plans have no data store; add-ons raise the dataset cap.' },\n { path: '/content-and-data/datasets/model-builder', title: 'Build a data model', heading: 'Define the model', anchor: '#define-the-model', text: '1. In Data, create a dataset and open the schema dialog.\\n2. Add typed fields — text, number, date, reference, and more.\\n3. Save. The model is stored on the dataset. The console and the REST API validate records against it; form submissions and automation steps store values as text without checking them.\\nDisplay name vs reference ID\\nEvery field has two names:\\n- Display name — the human label shown in the record editor, table headers, and anywhere the field appears. Rename it any time.\\n- Reference ID — the stable key used in bindings ({{item.referenceid}}), form field mappings, and CSV import/export.\\nWhen you create a field, the Reference ID auto-fills from the display name (for example, Roast preference → roastpreference). Edit it in the same dialog to set your own — handy when you want a short, code-friendly key that differs from the label. Once the field is created the ID is fixed: records, bindings, and forms all point at it, so it can\\'t change without orphaning that data. Pick it deliberately, then rename the display name freely afterward.' },\n { path: '/content-and-data/datasets/model-builder', title: 'Build a data model', heading: 'Edit records', anchor: '#edit-records', text: 'Open the typed document editor to add and edit records. Each field renders the input for its type (a date picker for dates, a number field for numbers, and so on), which keeps data consistent.' },\n { path: '/content-and-data/datasets/model-builder', title: 'Build a data model', heading: 'Tips', anchor: '#tips', text: '- Model the data first, then bind it — a clean model makes repeatable components and bindings straightforward.\\n- Give fields a description in the schema dialog — it appears as a hint in the record editor, as a tooltip on table headers, and under the field\\'s name in the schema dialog itself. See naming & describing fields.\\n- Use a reference field to link records; see relations.' },\n { path: '/content-and-data/datasets/model-builder', title: 'Build a data model', heading: 'Related', anchor: '#related', text: '- Relations\\n- Import & export\\n- Datasets overview' },\n { path: '/content-and-data/datasets/overview', title: 'Datasets & Dynamic Content', heading: '', anchor: '', text: 'Datasets & Dynamic Content\\nDatasets are your structured content: a typed model plus the records that fill it. Screens read from datasets to render dynamic, repeatable content, and forms write new records back in.\\nDatasets belong to your organization, not to a single site — every site in the organization shares the same collections. Manage them from the organization Data page (next to Media in the organization tabs), or from any site\\'s Data page; both edit the same data. Dataset limits, storage, and add-ons are billed at the organization level.\\nPlan availability Starter and above. Free plans have no data store; higher tiers raise the dataset and record caps, and extra-dataset add-ons are available.' },\n { path: '/content-and-data/datasets/overview', title: 'Datasets & Dynamic Content', heading: 'Model builder', anchor: '#model-builder', text: 'Define a model in the schema dialog with typed fields (text, number, date, reference, and more). The model is stored on the dataset itself. Records added or imported in the console, or written through the REST API, are validated against it; form submissions and automation steps store their values as text without validating them.' },\n { path: '/content-and-data/datasets/overview', title: 'Datasets & Dynamic Content', heading: 'Typed documents', anchor: '#typed-documents', text: 'Edit records in the typed document editor — each field renders the right input for its type, so data stays clean.' },\n { path: '/content-and-data/datasets/overview', title: 'Datasets & Dynamic Content', heading: 'Relations', anchor: '#relations', text: 'Fields can reference other records, including many-to-many relations, letting you model real structures (posts ↔ authors, products ↔ categories).' },\n { path: '/content-and-data/datasets/overview', title: 'Datasets & Dynamic Content', heading: 'Query layer', anchor: '#query-layer', text: 'A dataset query layer powers both the editor and screen bindings, so the same data is available to design-time previews and the live site.' },\n { path: '/content-and-data/datasets/overview', title: 'Datasets & Dynamic Content', heading: 'Repeatable components', anchor: '#repeatable-components', text: 'Point any element at a dataset and it renders once per record — a list, a grid or a gallery driven by your data. A card, an image, a row, a whole placed component: each one is copied per record, and an element that holds a list can repeat its contents instead, so the element stays the frame the items fill.\\nThe Besigner draws the real copies on the canvas, from your real records and in the order the published page will render them, and a repeat badge names what an element repeats over and how many records it found. The copy you edit is the design; changing it changes all of them.\\nA filter, a sort and a limit narrow what renders, and a repeat renders at most 100 records however high the limit is set.\\nSee Repeat over data for the full walkthrough.' },\n { path: '/content-and-data/datasets/overview', title: 'Datasets & Dynamic Content', heading: 'Who a dataset is shared with', anchor: '#who-a-dataset-is-shared-with', text: 'Datasets belong to the workspace, not to a single site, so one dataset can drive pages on every site you run. When that isn\\'t what you want, the Sharing control on each dataset decides which sites can see it:\\n- All sites — everyone in the workspace, on every site.\\n- Selected sites… — pick the sites that share it, up to 30.\\nWhere a new dataset starts depends on where you create it and on your workspace\\'s Default sharing for new data and media, the setting at the top of the workspace Media page:\\n- Created on a site\\'s Data page — it follows that setting. Set to All sites, the dataset starts on All sites. Set to Only the site they were created in, it starts shared with that site alone, and its control reads Selected sites… with just that site picked.\\n- Created on the organization Data page — it starts on All sites whatever the setting says, because there is no site to limit it to.\\n- Installed from the Marketplace, or created through the REST API — it starts on All sites too. Both act for the whole organization, not for one site.\\nThe setting only decides where a new dataset starts. It changes nothing that already exists, and you can widen or narrow any dataset afterwards.\\nA dataset with no sharing stored is visible to no site; its control reads Not shared with any site until you choose one of the two.\\nThis matters most for agencies. If you run three internal sites alongside twelve client sites, your rate card can be shared with the internal three and stay invisible to the clients — including to the client collaborators you have invited, who will not see it in the Data page, the pickers, or anywhere else.\\nSharing is enforced on the server, not just in the console. A site cannot render a dataset it hasn\\'t been shared with even if a page explicitly asks for it by name.\\nA few co' },\n { path: '/content-and-data/datasets/overview', title: 'Datasets & Dynamic Content', heading: 'Import & export', anchor: '#import--export', text: 'Datasets round-trip via CSV and JSON: export your records, edit them elsewhere, and re-import with validation on the way in.\\nA whole-site export includes the datasets and media that site can see, and nothing else — an agency exporting a client site gets that client\\'s data only. It carries up to 50 datasets and 1,000 records from each, so export a larger dataset on its own. On restore, everything comes back shared with the site you restored into, not with whatever it was shared with before. Widen it afterwards if you meant to share it. That direction is deliberate: a bundle can be restored into a different site or a different workspace entirely, and quietly re-publishing one client\\'s data across another\\'s sites would be far worse than an extra click.' },\n { path: '/content-and-data/datasets/overview', title: 'Datasets & Dynamic Content', heading: 'Related', anchor: '#related', text: '- Forms & lead capture\\n- Bindings, variables & functions\\n- Content collections & blog' },\n { path: '/content-and-data/datasets/relations', title: 'Relations', heading: '', anchor: '', text: 'Relations\\nReal data is connected — posts have authors, products have categories. Relations let a record reference other records.\\nPlan availability Starter and above — wherever datasets are available.' },\n { path: '/content-and-data/datasets/relations', title: 'Relations', heading: 'Reference fields', anchor: '#reference-fields', text: 'Add a reference field to a model to point at records in another dataset — the target picker never offers the dataset you are editing. For example, a post can reference its author.' },\n { path: '/content-and-data/datasets/relations', title: 'Relations', heading: 'Many-to-many', anchor: '#many-to-many', text: 'Relations support many-to-many, so a record can link to several others and vice versa — a product in many categories, each category holding many products.' },\n { path: '/content-and-data/datasets/relations', title: 'Relations', heading: 'Using relations', anchor: '#using-relations', text: 'The dataset query layer resolves related records for both the editor and screen bindings, so you can render a record together with the things it links to.' },\n { path: '/content-and-data/datasets/relations', title: 'Relations', heading: 'Tips', anchor: '#tips', text: '- Point references at a stable model — renaming fields is safe, but restructuring a related model affects everything that references it.' },\n { path: '/content-and-data/datasets/relations', title: 'Relations', heading: 'Related', anchor: '#related', text: '- Build a data model\\n- Import & export' },\n { path: '/content-and-data/events/overview', title: 'Events Calendar', heading: '', anchor: '', text: 'Events Calendar\\nThe Events Calendar is a schedule your workspace edits in the console and your visitors read on the site. Each event carries a time, a place, an organizer, a description and a cover image, and stays invisible until you publish it.\\nPlan availability Add-on. The Event Calendar is a flat monthly add-on that covers the whole workspace on any paid plan — buy it on Billing → Add-ons (see Add-ons). Until it\\'s enabled the Events page explains the add-on, and the Event List element renders nothing on your site.' },\n { path: '/content-and-data/events/overview', title: 'Events Calendar', heading: 'Manage events', anchor: '#manage-events', text: 'The console Events page lists your site\\'s events, newest start first. Add event opens the editor; each event has:\\n- Title — required.\\n- Starts and Ends — a start time is required; leaving the end blank (or setting it before the start) gives the event one hour.\\n- Location and Organizer — both optional, and both shown next to the date.\\n- Cover image URL — optional thumbnail, shown beside the event.\\n- Description — optional detail paragraph.\\nSet published / Set to draft flips an event\\'s status, shown as a chip in the list. Delete removes an event from your site.\\nDrafts never leave the console The public listing filters to published events on the server, so a draft is never sent to a visitor\\'s browser — it\\'s a safe place to stage next month\\'s schedule.' },\n { path: '/content-and-data/events/overview', title: 'Events Calendar', heading: 'Show events on a screen', anchor: '#show-events-on-a-screen', text: 'Events reach visitors through the Event List canvas element (Data display category) — drop it on any screen in the Besigner and set:\\n- Heading — a title above the list; empty hides it.\\n- Show — Upcoming (default) or Past events.\\n- Max items — how many to render (default 10).\\nUpcoming lists the next events by start time; past lists the most recent first. The list is served through the public events API and cached briefly at the CDN, so a newly published event can take about a minute to appear.' },\n { path: '/content-and-data/events/overview', title: 'Events Calendar', heading: 'Search engines', anchor: '#search-engines', text: 'Every rendered event emits schema.org Event JSON-LD — name, start and end, place, organizer, description and image — so search engines can show your events as rich results. Nothing to configure; see SEO for the rest of your site\\'s structured data.' },\n { path: '/content-and-data/events/overview', title: 'Events Calendar', heading: 'Related', anchor: '#related', text: '- Add-ons\\n- Bookings & scheduling\\n- SEO & structured data' },\n { path: '/content-and-data/forms/overview', title: 'Forms & Lead Capture', heading: '', anchor: '', text: 'Forms & Lead Capture\\nForms let visitors send you information — contact requests, sign-ups, lead capture. Submissions land in your inbox and can flow straight into a dataset and your CRM.\\nPlan availability Free for basic forms and the inbox. Higher tiers raise submission and dataset caps.' },\n { path: '/content-and-data/forms/overview', title: 'Forms & Lead Capture', heading: 'Reading submissions from code', anchor: '#reading-submissions-from-code', text: 'Everything on this page describes the console inbox. The same submissions are available over the REST API — list them, mark each one read as your integration processes it, and delete them once they\\'re archived elsewhere. That read flag is what stops a nightly sync from pushing the same lead twice.\\nSee Form submissions in the API reference, or start from Your first API call.' },\n { path: '/content-and-data/forms/overview', title: 'Forms & Lead Capture', heading: 'Build a form', anchor: '#build-a-form', text: '1. Drop form components onto a screen in the Besigner (fields, submit button).\\n2. Configure the fields and the submit behavior.\\n3. Publish — the form posts to Aglyn\\'s submit API.\\nYou can also describe the form you need and have Aglyn AI build it as a draft on the Forms page, with its fields, marketing consent and routing agreeing with each other. See Generate a form from a brief.\\nPer-visitor rate limit Submissions are capped at 10 per minute per site, per visitor address. A visitor over the limit gets a short retry delay, not a permanent block. It\\'s per address, so one spammer can\\'t lock out everyone else — but bulk-importing through your own public form will trip it. Use datasets for imports instead.\\nThat per-address limit is one of several protections in front of your form — see Spam and abuse protection.\\nMonthly allowance per plan\\nEach tier includes a monthly form-submission allowance, counted per site:\\n| Plan | Submissions / month |\\n| Free | 20 |\\n| Starter | 200 |\\n| Pro | 1,000 |\\n| Business | 8,000 |\\n| Scale | 25,000 |\\n| Advanced | 40,000 |\\n| Agency | 25,000 |\\n| Enterprise | 50,000 by default; more by agreement |\\nOn Free, that allowance is a hard wall: at the cap, further submissions are declined — the visitor sees the form\\'s error message rather than a fake success. On every paid tier it is a band, not a wall: submissions past the included count keep working and bill as metered overage, the same way storage and API requests do, because a dropped submission is a lost lead. Either way the count resets with the calendar month (UTC), and the billing page\\'s usage meters warn you at 80% before you get there.\\nThe allowance is per site. On Enterprise the figure is a default your agreement raises; Enterprise is not metered, so at that line further submissions are declined u' },\n { path: '/content-and-data/forms/overview', title: 'Forms & Lead Capture', heading: 'Spam and abuse protection', anchor: '#spam-and-abuse-protection', text: 'A published form is a public endpoint on the open internet — that is the point of it. Three things stand in front of it, and none of them asks your visitors to prove they are human:\\n- A honeypot field. Every Aglyn form carries an input that humans never see and never fill. A bot that fills it in gets an ordinary-looking success and nothing is stored — it learns nothing and keeps wasting its time.\\n- A per-address rate limit. The 10-per-minute limit above, enforced across all of our servers rather than per instance, so it still holds when traffic is spread out.\\n- A per-site monthly ceiling. Below.\\nThere is deliberately no CAPTCHA and no attestation check on the submit endpoint. A challenge in front of a lead-capture form is paid for by every real visitor so that a bot doesn\\'t get through, and it costs conversions. Whether that trade is the right one is an open question we have not settled — see Trust & security for the same statement from the reviewer\\'s side. Until it changes, the ceiling is what bounds the damage.\\nThe per-site monthly ceiling\\nSeparate from your plan allowance, each site has a monthly ceiling on how many submissions the endpoint will accept at all. It is abuse containment, not a quota: you are not metered against it, it is not something to plan capacity around, and no legitimate site reaches it. It sits at ten times your plan\\'s included allowance, or 5,000, whichever is larger — and at 1,000,000 on the unlimited tiers — so it is always far above the volume you actually bought.\\nIf a site does cross it:\\n- Further submissions are refused for the rest of the calendar month, and a refused submission is not billed. It is turned away before the billing counter moves, so a flood cannot add anything to your invoice — not even the part that was refused.\\n- Nothing i' },\n { path: '/content-and-data/forms/overview', title: 'Forms & Lead Capture', heading: 'Field types', anchor: '#field-types', text: 'Each Form Field has a type that controls what visitors see and what is submitted:\\n| Type | Visitor sees | Submitted value |\\n| Text (default) | Single-line input | The text |\\n| Email | Email input | The address |\\n| Multiline | Multi-row text area | The text |\\n| Dropdown | Select menu | The chosen option |\\n| Radio choice | One radio button per option | The chosen option |\\n| Checkboxes | One checkbox per option | All ticked options, joined with , |\\n| Star rating | Five stars | The number of stars (e.g. 4) |\\nDropdown, radio, and checkbox fields take their choices from the field\\'s Options setting — enter one choice per line (or separate them with commas). Blank entries are ignored. The Required? switch works per type: a required checkbox group needs at least one box ticked.\\nLabels and placeholders\\nEvery field has a Label — the visible name of the input, and the one screen readers announce. A field can also carry a Placeholder: a gray example inside the empty input, such as you@company.com or \"Tell us about your setup\". Set one and the label moves above the box so both are readable at once; leave it empty and the label keeps its floating behavior.\\nA placeholder is a hint, never a substitute for the label — it vanishes the moment a visitor starts typing, and a field labeled only by its placeholder is unusable with a screen reader. Text, email, and multiline fields show it inside the input; a dropdown displays it until a choice is made; radio, checkbox, and rating fields ignore it. Clearing the setting removes the hint.\\nExample: a quick survey\\nBuild a feedback survey with four fields:\\n1. A Star rating field named satisfaction, labeled \"How satisfied are you?\".\\n2. A Radio choice field named visit-frequency with options First time, Monthly, Weekly.\\n3. A Checkboxes field named top' },\n { path: '/content-and-data/forms/overview', title: 'Forms & Lead Capture', heading: 'After submit', anchor: '#after-submit', text: 'The form\\'s After submit setting controls what a successful submission does:\\n| Outcome | What the visitor sees |\\n| Show the success message (default) | The form is replaced by your Success message |\\n| Redirect the visitor | The browser navigates to a screen you pick (rename-safe — slug changes never break it) or, with no screen picked, to a Redirect URL (a same-site path like /thanks or an https URL; anything else is ignored) |\\n| Reveal a hidden element | An element you pick from the canvas — hidden on the published page until then — appears in place of the form |\\nFor the reveal outcome, drop the follow-up content (a thank-you block, a download link, an embedded video) anywhere on the screen, then select it as the Element to reveal. It stays hidden for visitors until the form is submitted; in the Besigner it stays visible so you can keep editing it. If a redirect target can\\'t be resolved (the screen was deleted, the URL was rejected), the form falls back to the success message.\\nExample: grow an email list from a signup form\\nCombine an outcome with a conditional automation:\\n1. Add the Marketing consent field from Forms in the Besigner\\'s element picker: a Checkboxes field named marketingConsent, labeled Marketing emails, with one option, Email me news and offers. If you reword the option, keep it free of commas: the Options setting starts a new box at every comma.\\n2. On the form\\'s own page, pick marketingConsent as the Marketing consent field, so a tick is recorded as the person\\'s consent.\\n3. Set After submit to Redirect the visitor and pick your /thanks screen.\\n4. On Automation → Actions, add an action on formSubmission with the condition \"A field is not empty\" → marketingConsent and the step Enroll in a list, picking your email audience.\\nVisitors who tick the box are add' },\n { path: '/content-and-data/forms/overview', title: 'Forms & Lead Capture', heading: 'Where submissions go', anchor: '#where-submissions-go', text: '- Inbox — every submission is captured; open it in the console\\'s mail reader dialog.\\n- Datasets — bind a form to a dataset (Write to dataset on the Form element) and each submission is also appended as a record. The inbox always gets its copy, and each submission\\'s own detail dialog carries chips saying where it went — including saying nothing about a dataset when the record wasn\\'t created.\\n- Contacts — every submission that carries an email address updates the person in the CRM at stage Lead: a new contact when the address is new, an interaction on their timeline when it is not, and a customer stays a customer. A field can also save its answer into one of your custom contact fields — pick the field under Saves to contact fields on the form\\'s own page.\\n- Leads — only when the form\\'s CRM routing card has the lead switch on. See What makes a lead.\\nThe inbox\\nThe site\\'s Inbox page collects everything visitors send, in three tabs — Submissions, Members & leads, and Campaigns. The Form Submissions table reads as a list of people, not of forms: From, Message, Received, and the row actions.\\n- From shows the sender — a colored initials avatar and their name, with the form name as a caption underneath (the Form element\\'s Form name attribute, so name your forms distinctly: \"Contact\", \"Newsletter\", \"Survey\"). The avatar\\'s color is derived from the name, so one sender keeps one color on every machine.\\n- Unread submissions are bold with a dot at the left of the row. There is no \"New\" chip — bold text and a chip saying New are the same fact twice. Site managers also get an in-app notification per submission.\\n- The row\\'s ⋮ menu holds Open contact in CRM — the person this submission updated, found by the address they gave — beside Mark read and Delete. A submission that carried no email' },\n { path: '/content-and-data/forms/overview', title: 'Forms & Lead Capture', heading: 'Duplicate a form', anchor: '#duplicate-a-form', text: 'Duplicate… in a form\\'s row menu makes a second form with the same design, fields, validation, consent field and routing, named Copy of unless you type another name. It is a form of its own: submissions stay with the original, the copy starts collecting only once you place it on a page and publish, and it counts against your form allowance like a new form.' },\n { path: '/content-and-data/forms/overview', title: 'Forms & Lead Capture', heading: 'Switch Forms off for one site', anchor: '#switch-forms-off-for-one-site', text: 'Forms is on for every site in a workspace. A site admin can switch it off for one site on that site\\'s Admin → Plugins → Forms page. The switch asks first: it lists the published pages on the site that carry a form, because those forms stop rendering and stop accepting submissions the moment Forms is off.\\nWith Forms off for a site:\\n- a form on its published pages is not drawn. The rest of the page stays; where the form was, there is empty space and no error;\\n- every submission sent to the site is refused with This site is not accepting form submissions, including one from a page a visitor opened before the switch;\\n- publishing a form, or a page, layout or component that carries one, is refused until Forms is back on for the site. Remove the form to publish the rest of the page;\\n- the Forms page leaves the site\\'s navigation.\\nWhat it keeps: the submissions already received, the workspace\\'s form catalog and the CRM leads its forms created are untouched, and forms keep working on the workspace\\'s other sites. A Marketing popup that collects an email address is a Marketing element rather than a form, so it keeps collecting while Marketing is on for the site.\\nSwitch Forms back on and the forms on the site\\'s published pages render and accept submissions again.' },\n { path: '/content-and-data/forms/overview', title: 'Forms & Lead Capture', heading: 'Related', anchor: '#related', text: '- Datasets & dynamic content\\n- CRM\\n- Email campaigns' },\n { path: '/content-and-data/media/overview', title: 'Media Library & CDN', heading: '', anchor: '', text: 'Media Library & CDN\\nThe media library stores your images, video, and files, keeps them organized, and serves them quickly. Media plugs into components like Image and Video and into the theme\\'s favicon.\\nPlan availability Free with storage quotas. CDN delivery with WebP variants is on every plan, free included; document uploads and higher storage are gated by plan. Video uploads are paused on every plan for now — see Upload.' },\n { path: '/content-and-data/media/overview', title: 'Media Library & CDN', heading: 'Organize', anchor: '#organize', text: '- Arrange media in a folder hierarchy. Folders appear as cards in the grid (folders first, before files) as well as in a side rail — open one to browse into it, and use the breadcrumb to step back out.\\n- Drag and drop to reorganize: drag a file (or a whole selection) onto a folder to move it in, drag a folder onto another to nest it, or drop onto a breadcrumb to move items up and out. Nesting depth and name-collision rules are enforced automatically.\\n- Move to folder… shows the full path, not just the folder name — Blog / Covers and Press / Covers are two different destinations and the menu says so. The same paths appear in the detail drawer\\'s Folder field.\\n- Big moves are safe to interrupt. Moving a large selection relocates the stored objects one at a time, so the library keeps working through it and tells you how far it has got. If it cannot finish everything, it reports the real split — Moved 7 of 19 — 12 could not be moved — and leaves the files that did not move selected, so clicking Move again picks up exactly where it stopped. Nothing is lost either way: a file is only ever in one folder, its URL keeps working, and a repeat move of a file that already arrived does nothing.\\n- Filter by type, date, and size, search, and sort the library.\\n- Search covers everything you authored, not just file names — alt text, description, tags, folder name and your own custom metadata values are all searchable. Results update as you type, and ✕ (or Esc) clears the box.\\n- Wildcards: mock--noshadow.png matches a whole family of files. is any run of characters, ? is exactly one, and a wildcard pattern matches the whole value.\\n- Search one field: tag:hero, name:logo, alt:product, desc:landing, folder:brand, or a custom field by key — meta.campaign:spring. Combine them with plain words' },\n { path: '/content-and-data/media/overview', title: 'Media Library & CDN', heading: 'Upload', anchor: '#upload', text: '- Upload images, PDFs, ZIP archives and documents (Word, Excel, PowerPoint, CSV, RTF, plain text, Markdown and JSON). Click Upload media, or drag files straight from your desktop onto the library — dropped files land in the folder you have open.\\n- Video uploads are paused. New MP4, WebM and QuickTime files are not accepted on any plan for now, from the console or the API, and a video\\'s file cannot be replaced. Videos already in your library keep playing, and the Video element can still use them. While the pause lasts, the library shows a Video uploads paused chip beside Upload media.\\n- Documents and archives are stored and served exactly as you uploaded them — nothing is opened, extracted or converted. Macro-enabled Office files (.docm, .xlsm, .pptm) are not accepted.\\n- Rename, replace the file behind an asset, and edit images in place. Replace works for images, PDFs, archives and documents alike, and for video once video uploads resume — it is available from the asset\\'s details drawer and straight from the card\\'s overflow menu, and it keeps the asset\\'s link, folder, tags, alt text, custom fields and sharing exactly as they were. Swapping one kind of file for another is not allowed: upload that as a new file. Cropping, rotating and resizing stay images-only, for the obvious reason.\\nSize and plan limits\\n| Upload | Cap | Plan |\\n| Images | 15 MB per file | Every plan |\\n| PDFs | 25 MB per file | Pro and above |\\n| Documents (Word, Excel, CSV, RTF, text, Markdown, JSON) | 25 MB per file | Pro and above |\\n| Presentations (PowerPoint) | 50 MB per file | Pro and above |\\n| ZIP archives | 50 MB per file | Pro and above |\\n| Video | 200 MB per file | Paused on every plan |\\nAny file over 3 MB automatically uses signed-URL uploads, so big files go straight to storage without tying up ' },\n { path: '/content-and-data/media/overview', title: 'Media Library & CDN', heading: 'Deliver over CDN', anchor: '#deliver-over-cdn', text: 'Every plan serves media via a CDN with automatic WebP variants, so images load fast and cache well.\\nURLs are stable\\nA media URL is keyed to the asset, not to its bytes or its location. That means the link you copied stays correct when you:\\n- Replace the file — every screen, layout, and content entry that embeds it serves the new file immediately, with no re-linking. A replaced video also drops the poster frame and any encoded versions of the old footage, so nothing left over is served under the new file\\'s link.\\n- Move it between folders — organizing your library never breaks a live page.\\nSo replacing a logo across a whole site is one upload, not a hunt for every reference. Links copied before this behavior shipped keep working too.\\nPage elements point at the asset, not at a link\\nWhen you place an image with Browse media, the element records which asset you chose rather than a link to it. You never have to copy or paste a path, and the element keeps working through folder moves, file replacements, and any future change to how we deliver media. Copy URL is still there for pasting a link somewhere outside Aglyn.\\nYou can also type any external image URL into the field by hand — useful for hotlinking an image hosted elsewhere. Images placed before this shipped keep rendering exactly as they did.\\nWhat hotlinking means for your visitors {#hotlinking-and-your-visitors}\\nHotlinking is a supported feature and we do not intend to remove it, but it has a consequence worth knowing before you rely on it: your visitor\\'s browser fetches that file from that host directly, and we do not proxy it. Aglyn never sits in the middle of the request, so the host you named receives the visitor\\'s IP address, their browser user-agent and the address of the page they are reading, and it can set its o' },\n { path: '/content-and-data/media/overview', title: 'Media Library & CDN', heading: 'Who an asset is shared with', anchor: '#who-an-asset-is-shared-with', text: 'Workspace media is shared across every site by default. The Shared with control narrows that, with the same two choices as datasets — All sites or Selected sites…. You\\'ll find it in three places:\\n- on a single asset, in its details drawer;\\n- on a selection — tick several files and use Shared with… in the toolbar;\\n- on a folder, from its ⋮ menu, which offers to apply the same sharing to the files inside it and its subfolders (it names the count, so you know what you\\'re about to change).\\nA folder applies its sharing when you save it — files keep their own setting afterwards, so moving a file into a \"Client A\" folder later does not re-share it. Narrowing a file that sites are already using names those sites first and asks you to confirm. Only workspace owners and admins can change sharing.\\nA new folder or file starts shared with All sites, so it appears everywhere the moment you create it — or with the site you were working in, if your workspace has been set to make new resources site-scoped by default. That default is Default sharing for new data and media, at the top of the workspace\\'s Media page, and it changes nothing that already exists. The site you were working in is the one whose Media tab you uploaded on, or the one you were editing when you opened the media picker. A folder or file created on the workspace Media page has no site to limit it to, so it starts on All sites either way. The default applies to new datasets the same way: one created on a site\\'s Data page follows it, and one created on the organization Data page starts on All sites. If the Shared with dialog ever opens on \"Not shared with any site\", that folder or file has no sharing stored at all: it is hidden from every site, and any file inside it turns up under No folder there. Pick a value and save ' },\n { path: '/content-and-data/media/overview', title: 'Media Library & CDN', heading: 'Private files', anchor: '#private-files', text: 'Private is a separate switch from sharing, and it answers a different question:\\n| | Question it answers |\\n| -- | -- |\\n| Shared with | Which of your sites may use this file? |\\n| Private | May anyone fetch these bytes at all? |\\nA private file:\\n- has no public URL — the normal media link does not exist for it, and Copy URL is hidden on it in both the card menu and the drawer,\\n- loses any public URL it already had. Turning Private on revokes the file\\'s public address at the moment you confirm it, so a link you copied and sent last month stops working too. That is the part people expect and the part that is easy to get wrong: a switch that only stopped new links would leave the one already in circulation serving the file forever,\\n- cannot be placed on a page; the picker refuses it and says why. The one place a private file can be added is a product\\'s members videos and digital downloads,\\n- is viewable and downloadable in the console by people who can already see it, through a temporary link that stops working after about fifteen minutes.\\nTwo menu items do that last part, and they are not interchangeable:\\n- Copy temporary link (organization library, organization-wide members) puts a fifteen-minute link on your clipboard — for handing to one person, once, now. Nothing in Aglyn renews it, so it is a trap in a document or a page: what you paste stops working while the page still looks fine.\\n- Download file saves the bytes. Aglyn mints a fresh link for that click, so the expiry never reaches you.\\nCopy URL being hidden rather than disabled is deliberate: there is no permanent address to hand over, and a copied link that 404s is worse than no button.\\nThat expiry is the point. A normal media URL, once shared, works forever and there\\'s no way to take it back. A private file\\'s link di' },\n { path: '/content-and-data/media/overview', title: 'Media Library & CDN', heading: 'Approved image hosts', anchor: '#approved-image-hosts', text: 'Most images on your site are ones you uploaded here, and those always work. But you can also point a block at an image that lives somewhere else by pasting its URL — a photo on your own CDN, say, or an image your supplier hosts.\\nThose are worth being deliberate about, for a reason that is easy to miss: when a visitor opens a page with an external image on it, their browser fetches that image directly from the other site. So that site sees your visitor\\'s IP address and which page they were on, whether or not you have any relationship with them. It is a real disclosure, and it is one you are making on your visitors\\' behalf.\\nSo your site keeps a list of the external hosts it is allowed to load images from, under Admin → Security → Approved image hosts. Add a host and images from it load normally. Leave it off the list and browsers refuse it.\\nYou do not have to think about this for anything you upload — your own media is always allowed, and so is your site\\'s own address.\\nAdding a host {#adding-an-approved-image-host}\\nEnter the host on its own, with no https:// and no path:\\n- cdn.example.com allows exactly that host.\\n- .example.com allows every subdomain of it, which is what most image CDNs need because they give each account its own.\\nIf you paste a URL into an image block for a host you have not approved, the editor tells you so while you are editing, rather than letting you publish a page whose image is blank for everyone but you. The warning names the host, so approving it is a copy and paste.\\nWhen an image does not appear {#approved-image-host-troubleshooting}\\nIf an image shows in the editor but not on the published page, an unapproved host is the first thing to check. Open the page, right-click the missing image, and the browser console will name the host it refused.' },\n { path: '/content-and-data/media/overview', title: 'Media Library & CDN', heading: 'Reference', anchor: '#reference', text: 'The details behind the screens above, for anyone wiring media into their own code.\\nVariant widths {#variant-widths}\\nAglyn generates WebP variants at 320, 640 and 1280 pixels wide when an image is uploaded. Those three are the whole set — there is no arbitrary resizing service behind the CDN URL.\\nAdd ?w= to a CDN URL to ask for one: …/api/media/cdn/org:{orgId}/{mediaId}?w=640. A width the asset does not have generated is not an error and not a resize — the original bytes are served instead, so a ?w=200 request answers with the full-size file. Check the drawer\\'s delivery line to see which widths a given file has.\\n?download=1 on the same URL makes the response save rather than open in a tab. Both parameters are read after every access check, so neither widens what is served, and both are part of the cache key, so they cannot bleed into each other.\\nTag limits {#tag-limits}\\nTags are stored lower-cased and de-duplicated. A tag longer than 40 characters is dropped, and a file keeps at most 20 tags — entries past the twentieth are dropped rather than replacing an earlier one.\\nOver the API\\nMedia is read-only over the REST API: file names, sizes, dimensions, tags, folders, and both URL forms. See the Media API resource, including what it does not return.' },\n { path: '/content-and-data/media/overview', title: 'Media Library & CDN', heading: 'Components', anchor: '#components', text: '- Image — place and bind images from the library.\\n- Video — embed a video that is already in your library.\\n- Favicon picker — choose the site favicon from your media.\\n- App icon picker — choose the square mark your site installs with.\\nEverywhere you pick media — the Image and Video components, the logo, favicon and app icon pickers, and the organization logo field — the same media picker opens, with This site and Organization (shared) tabs so you can pull from either library without leaving the dialog.' },\n { path: '/content-and-data/media/overview', title: 'Media Library & CDN', heading: 'Related', anchor: '#related', text: '- Bindings\\n- SEO toolkit (Open Graph & Twitter images)\\n- Media API resource — reading the library from your own code' },\n { path: '/developers/domain-providers', title: 'Domain providers', heading: '', anchor: '', text: 'Domain providers\\nAglyn hands out hostnames. A workspace gets {slug}. , a published site gets {subdomain}. , and a customer can connect shop.acme.com of their own. Something has to make each of those resolve, and on a self-hosted install that something is not a hosting vendor\\'s API — it is a driver you choose.\\nThis page is the runbook. Environment variables is the per-variable reference for the same system; keep it open beside this one rather than reading one instead of the other. This page answers which driver, and what do I have to build; that page answers what does this variable do.' },\n { path: '/developers/domain-providers', title: 'Domain providers', heading: 'Which driver', anchor: '#choosing', text: 'Answer these about your own setup, in order. The first \"yes\" is your driver.\\n1. Are you deploying to Vercel? → vercel. The two projects — console and tenant — are the driver\\'s configuration.\\n2. Do you intend to let customers connect their own domains? → webhook. This is the only driver on a self-host that can register a name you do not already own, because registering somebody else\\'s shop.acme.com means touching your proxy, and only you know how to do that.\\n3. Otherwise: does one wildcard DNS record and one wildcard certificate cover every name you serve? → wildcard. This is the ordinary Docker answer.\\n4. Are hostnames handled entirely outside the product — a platform team\\'s own DNS pipeline, or an install that only ever uses path routing? → none, or leave it unset.\\nUnset, the deployment detects: vercel when VERCELTOKEN is present, none otherwise. An explicit value always wins, none included — switching a driver off while its credentials are still in the environment means it. An unrecognized value logs [domain-provider] unknown AGLYNDOMAINPROVIDER and falls back to detection, so a typo cannot silently disable domains.\\nDetection never picks wildcard The wildcard driver reports a name as serving without checking anything — there is nothing to check, because your DNS record and certificate either cover the apex or they do not. Inferring it from an apex somebody merely configured would put a green chip beside an address that resolves nowhere. Claiming to serve a name is your assertion, so it takes your explicit setting.\\nWhat each driver cannot do {#limits}\\n| Driver | Cannot |\\n| wildcard | Register a customer\\'s own domain. No wildcard covers somebody else\\'s apex. It also cannot see such a name to report on it, and cannot un-serve one of the names it does cover. |\\n| webhook |' },\n { path: '/developers/domain-providers', title: 'Domain providers', heading: 'The `wildcard` path, end to end', anchor: '#wildcard', text: 'Four things have to be true, and the driver checks none of them — it takes your word for all four. Do them in this order.\\nThe worked example below uses the shape .env.selfhost.example ships with:\\n| Value | Serves | Container |\\n| NEXTPUBLICCONSOLEURL=https://console.example.com | The console | 127.0.0.1:4200 |\\n| NEXTPUBLICWORKSPACEDOMAIN=example.com | Workspace subdomains, {slug}.example.com | 127.0.0.1:4200 |\\n| NEXTPUBLICTENANTDOMAIN=sites.example.com | Published sites, {subdomain}.sites.example.com | 127.0.0.1:4500 |\\nNote that a workspace subdomain is served by the console container and a site subdomain by the tenant container. They are two wildcards pointing at two upstreams, not one.\\n1. DNS {#wildcard-dns}\\nOne record per apex you serve, plus the console\\'s own name:\\nAdd AAAA records alongside if you serve IPv6. If your proxy sits behind a load balancer with a hostname rather than an address, use CNAME for the wildcards and an ALIAS/ANAME for anything at an apex.\\n2. Certificates {#wildcard-certificates}\\nOne certificate per wildcard, and a wildcard certificate can only be issued over a DNS-01 challenge — HTTP-01 cannot prove control of a whole apex. That means your ACME client needs credentials for your DNS provider\\'s API. This is the step that surprises people; budget for it.\\nA wildcard covers exactly one label .example.com serves a.example.com. It does not serve a.b.example.com, and it does not serve example.com itself — and a certificate issued for .example.com covers exactly the same set.\\nThis is why two apexes need two records and two certificates, and why the driver matches one label rather than \"ends with the suffix\". The looser test would report a.b.example.com as serving and your visitor would meet a TLS error before you did.\\n3. The proxy rules {#wildcard-proxy' },\n { path: '/developers/domain-providers', title: 'Domain providers', heading: 'The `webhook` contract', anchor: '#webhook', text: 'Aglyn POSTs three operations to one endpoint you run and takes its word for the answer. What is behind it is entirely your business: Caddy\\'s admin API, a Traefik dynamic-configuration file, a cert-manager Certificate, a registrar\\'s API, or twenty lines of shell.\\nThis is the extension point rather than a module path the app imports, because a module path does not survive the product: both apps are bundled by Next, so a path resolved at runtime is not in the bundle, and an operator running a built image has no build step in which to add one. A webhook needs no rebuild.\\nPut this endpoint on your own network The request carries the bearer token and the names of your customers\\' domains, and the reply decides whether the console tells someone their site is live.\\nThe request {#webhook-request}\\nPOST to your URL, with Content-Type: application/json and — only when AGLYNDOMAINWEBHOOKTOKEN is set — Authorization: Bearer . When it is unset the header is omitted entirely rather than sent empty.\\n| Field | Values |\\n| action | attach, detach or status. |\\n| scope | console — the admin app, its workspace subdomains and any white-label console domain. tenant — published sites and customers\\' own domains. |\\n| domain | Always lowercased, trimmed, and with any trailing dot removed. |\\n| redirectTo | Present only when the name should forward rather than serve. Always a bare hostname. |\\nredirectTo is a registration, not a special case: create the name carrying the redirect, or point an existing entry at the target. Two things in the product send it — a site\\'s platform subdomain forwarding to that site\\'s custom domain, and the secondary names of a multi-name white-label console domain forwarding to the primary. Aglyn reduces the value to a bare hostname before the wire and refuses to send anythin' },\n { path: '/developers/domain-providers', title: 'Domain providers', heading: 'When no driver is configured', anchor: '#unconfigured', text: 'This is a supported state, not a broken one. A deployment can legitimately have names handled entirely outside the product, and the correct behavior there is silence rather than a failure logged on every signup forever.\\nWorkspace subdomains. The attach returns skipped and the organization is created anyway — the console is path-routed and {console}/{slug} is the canonical form, so a workspace with no subdomain is fully usable. The console still advertises {slug}. , so that name has to resolve some other way or it will not load. The deployment says so once per process, not once per signup:\\nA customer\\'s custom domain. /api/domains/attach answers 501:\\n> Domain attachment is not configured on this deployment (no domain provider —\\n> set AGLYNDOMAINPROVIDER).\\nThe same 501 is given whether there is no driver at all or a driver that does not manage this particular name — which is honest, because from the customer\\'s side those are the same situation. The customer sees an info toast, the domain sits behind a red alert saying it is not attached, and a Retry attachment button that cannot succeed until the deployment can register the name.\\nThe site serves anyway. The Firestore claim on the domain is written before the refusal, and the tenant runtime resolves an unrecognized hostname against that claim. So on wildcard or none:\\n1. Point the customer\\'s DNS at your reverse proxy.\\n2. Route that hostname to the tenant container by hand.\\n3. The site is served.\\nWhat the refusal actually costs is the canonical redirect. A host carrying cnameAttachmentPending is not treated as having a live custom domain, so the platform subdomain keeps serving the site rather than forwarding to the custom domain, and both addresses serve the same pages until the flag clears.' },\n { path: '/developers/domain-providers', title: 'Domain providers', heading: 'Status states', anchor: '#status-states', text: 'attach answers \"did the registration succeed\", which is a weaker question than \"is this name serving\". A domain can be registered and serve nothing — ownership unproven, DNS not pointed, certificate not issued — and all three follow a perfectly successful attach. status is the question the console actually renders.\\n| State | What it means | Visitors sent there? | Your move |\\n| serving | Registered, routed, certificate covers it. | Yes | Nothing. |\\n| certificate-pending | Accepted and routed, but no certificate exists yet, so the name answers with a TLS error. | No | Wait. If it never clears, look at your ACME client. |\\n| ownership-pending | The provider is holding the name until a challenge record is added. verification carries the record and is shown to the customer. | No | The customer adds the record at their registrar. |\\n| dns-misconfigured | The provider\\'s own DNS check says the records do not point here. conflicts names foreign records answering for it. | No | Fix the records; remove any conflicting ones. |\\n| not-attached | The name is not registered on this deployment at all. | No | Attach it — or find out which deployment holds it. |\\n| skipped | No driver, or a driver that does not manage this name. | Yes | Nothing, unless you meant to manage it. |\\n| unknown | The probe could not answer. | Yes | Nothing on its own. Look for a pattern. |\\nWhy unknown counts as serving A status probe that could not answer is not evidence of a problem. Treating it as one would strand every live domain on a deployment whose driver has no status API to ask — which is most of them, wildcard included. The failure mode of the other choice is far worse: every customer\\'s working site going dark in the console because your endpoint restarted.\\ncertificate-pending is the one people expect to ' },\n { path: '/developers/domain-providers', title: 'Domain providers', heading: 'Migrating between drivers', anchor: '#migrating', text: 'Every domain variable is read at runtime, so switching drivers is an env change and a restart — not a rebuild. (NEXTPUBLICWORKSPACEDOMAIN and NEXTPUBLICTENANTDOMAIN are the exception, and you are not changing those here.)\\nwildcard → webhook, to start selling custom domains {#migrating-webhook}\\nThe usual move. Everything the wildcard covered still has to work afterwards, which is the part worth planning.\\n1. Stand up the endpoint and test it with curl before Aglyn ever sees it — an attach, a status, and a detach against a throwaway name.\\n2. Teach it your wildcard apexes. Once the driver is webhook, every name goes through it, workspace subdomains included. Those names are already served by your wildcard, so the endpoint should recognize them and answer already-exists for attach and serving for status, rather than writing a per-name route it does not need:\\nAnswering skipped for them is also correct and costs nothing — it is what the wildcard driver itself did.\\n3. Switch and restart:\\n4. Re-attach the domains that were refused while you had no driver for them. Nothing does this for you: the completer cron only probes, so a domain that was never registered stays not-attached forever. Open each affected site\\'s domain settings and press Retry attachment. New connections go through the new driver from the moment it is live.\\nwebhook → wildcard, or to none {#migrating-down}\\nSet the value and restart. Names your endpoint registered do not disappear — they are in your proxy\\'s configuration, and Aglyn will simply stop asking about them. Customer domains outside your wildcard will start reporting unknown rather than a real state, and new ones will be refused with the 501 above.\\nBe explicit rather than removing credentials: AGLYNDOMAINPROVIDER=none wins over a VERCELTOKEN that is still' },\n { path: '/developers/domain-providers', title: 'Domain providers', heading: 'Related', anchor: '#related', text: '- Self-hosting — the full runbook, including which addresses this install calls its own and the reverse-proxy rules.\\n- Environment variables — the per-variable reference for everything on this page.\\n- Custom Domains — what a site owner sees at the other end of all this.' },\n { path: '/developers/plugins/building-feature-plugins', title: 'Building feature plugins', heading: '', anchor: '', text: 'Building feature plugins\\nAglyn features that add both console surface and site components ship as a single library (AGL-277) — never merged into the core mui plugin, which stays purely component and theme definitions for the Besigner and hosts.\\nPlugins live directly under libs/plugins/{feature} (moved out of the old .../ui/ nesting in AGL-395) and publish as @aglyn/plugins-{feature}. A plugin keeps its Besigner component, its console page, and both registration functions in the one lib — the console half is a separate registerConsole() export, so the shell can register nav + pages at app load without pulling in the Besigner canvas bundle.\\nSharing app hooks. A relocated console page can\\'t import console-app hooks. Genuinely reusable ones move into a shared lib the plugin can import — e.g. useFirestoreCollection, useFirestoreDoc, and useHostOrgId now live in @aglyn/tenant-feature-instance, and HubTabs in @aglyn/shared-ui-next (the app keeps thin re-export shims).\\nReaching app-only UI (media browser). Some app UI can\\'t move — the media library is coupled to the org/session context. Instead of importing it, a plugin consumes it through a context: MediaPickerContext / useMediaPicker() in @aglyn/aglyn exposes pickMedia(): Promise<PickedMedia\\n| null>. The shell mounts the app\\'s ConsoleMediaPickerProvider (which owns the real dialog) around every plugin page, so a plugin component just calls pickMedia() and gets the chosen asset — see the commerce product editor. This is the escape hatch for the dependency cascade a naive promotion would hit.' },\n { path: '/developers/plugins/building-feature-plugins', title: 'Building feature plugins', heading: 'The UI half', anchor: '#the-ui-half', text: 'Build a bundle with defineUiFeatureBundle from @aglyn/aglyn (the plugin-manager registration API, AGL-415) and register it the same way the mui bundle registers itself. The bundle automatically declares a dependency on the mui bundle, so primitives and theming load first.\\nComponent and bundle ids are persisted componentId and pluginId are stored in screen documents. Never rename them — when a component moves between bundles, keep the old ids resolving (the mui plugin\\'s legacy-id aliases are the precedent).' },\n { path: '/developers/plugins/building-feature-plugins', title: 'Building feature plugins', heading: 'The console half', anchor: '#the-console-half', text: 'Export a ConsoleExtension and register it at console startup. The console shell renders the nav items and their pages from the registry — and applies the featureFlag entitlement gate itself, so an extension can\\'t bypass plans. A feature plugin adds a menu item to the host app bar and a new page without editing any core console file (AGL-394).\\nThe shell applies the permission gate the same way, for the other question: featureFlag is what the organization bought, permission is what the person reading may open. Name a key on the extension (or on one nav item, to narrow that surface) and the shell refuses to construct the page for a reader who does not hold it — see the plugin-manager API reference. Gating inside the page instead means the surface has already mounted and opened its listeners before the check runs.\\nA nav item that carries a Component becomes a full page: the shell\\'s generic host route (apps/console/app/(app)/[orgSlug]/hosts/[host]/[...pluginSlug]/page.tsx) mounts it under the active host, wires the breadcrumb/header, resolves the featureFlag entitlement, and passes it in as entitled.\\nThe page component receives ConsolePluginPageProps — { hostId, entitled, org, permissions, releaseFlag, basePath, sections, section, segments } — so it stays free of console-app hooks. org is the resolved org billing doc the shell already loaded, so the page can run its own checkEntitlement/checkQuota (e.g. per-plan limits) without the app\\'s org/session hooks:\\nHow the shell consumes the registry\\n1. apps/console/constants/register-console-plugins.ts calls each plugin\\'s registerConsole() at import time; app imports it so the registry is populated before any nav renders. This registers only the console half — never the Besigner canvas bundle — so plugin canvas code stays out of the' },\n { path: '/developers/plugins/building-feature-plugins', title: 'Building feature plugins', heading: 'Routed sections (AGL-2501)', anchor: '#routed-sections-agl-2501', text: 'A surface can be a hub of real URLs instead of a tab strip. Declare sections on the nav item and each becomes a route at ${href}/${section.id}:\\nThe shell resolves the URL and hands the page back:\\n- section — the id the URL names, or undefined on the surface\\'s own href. It is always one of the declared ids: the shell 404s an id it does not recognize rather than falling back to the first section, so the page needs no branch for a section it does not have.\\n- sections — the declared list with an absolute href per section and the release verdict already applied to visible. Feed it straight to HubSections; a plugin cannot compute visible itself, because release flags live in scope:app.\\n- basePath — the surface\\'s absolute path, e.g. /acme/hosts/shop/products.\\n- segments — everything beneath basePath. segments[0] is section; anything after it belongs to the section, so a section may own deeper routes (…/orders/ord123) without another registry change.\\nThe surface\\'s own href (/products) names no section. Redirect it to the first visible one rather than rendering that section in place — rendering it would pay for its reads on a URL that is about to be replaced.\\nWhy routes rather than tabs. A tab strip mounts every panel, so every panel\\'s Firestore listens open on load and the reader is looking at one of them. The commerce console was the worst case: one load issued 32 listens with a ceiling of 4,462 documents to render one tab. A URL per section makes \"mount only what is open\" structural instead of a flag somebody has to remember — and a section becomes linkable, the back button walks sections, and which one is open is a fact about the URL rather than state kept in sync with it.\\nsections is optional, and omitting it is not a lesser option — it means the surface is one page. A nav ' },\n { path: '/developers/plugins/building-feature-plugins', title: 'Building feature plugins', heading: 'Loading: org-gated and dynamic (AGL-417)', anchor: '#loading-org-gated-and-dynamic-agl-417', text: 'Apps never import @aglyn/plugins-. plugins.config.json maps plugin ids to packages and register entry points; tools/scripts/generate-plugin-manifests.mjs emits the per-app loader manifests (plugins..generated.ts) — the ONLY sanctioned plugin references outside libs/plugins (an nx scope:app boundary rule enforces this). At runtime the core plugin loader activates the plugins in org.enabledPlugins (AGL-416, org settings → Plugins): console surfaces behind the providers gate, site/canvas surfaces behind the editor/tenant suspense gates, API handlers lazily on first dispatch with a per-request org gate (a disabled plugin\\'s API 404s for that workspace).\\nEach first-party plugin is additionally release-flagged (AGL-422, FirstPartyPlugin.releaseFlag): a flag staff turn off in the console Feature Flags page subtracts the plugin from every workspace\\'s effective set — nav, editor, published sites, and API — with the usual staff preview bypass. Register a flag for any new plugin (registry + Remote Config template + the catalog entry).' },\n { path: '/developers/plugins/building-feature-plugins', title: 'Building feature plugins', heading: 'Extending beyond pages: slots, providers, runtimes, hooks (AGL-418/419)', anchor: '#extending-beyond-pages-slots-providers-runtimes-hooks-agl-418419', text: '- Widgets — ConsoleExtension.widgets render into named shell slots via the app\\'s PluginWidgetSlot. The guaranteed zones (and the props each receives) are the exported CONSOLEWIDGETSLOTS catalog (AGL-433): hostActivity, hostDashboard, orgDashboard (the organization\\'s Sites page, handed the org mount and no site), commerceGlance, orgData, besignerFunctions, marketplaceListing, orgAddons, dashboardFooter, orgSettings, hostSettings, and the staff-only adminOrgDetail.\\n- Providers — ConsoleExtension.providers mount around every console page (e.g. marketplace\\'s AI-assist provider).\\n- Site runtimes — registerSiteRuntime components run on every rendered tenant page (marketing\\'s overlays/experiments/automations), reading back the props their server enricher wrote.\\n- Site-page hooks — /server entries register redirect resolvers, page resolvers (commerce PDP/PLP), and enrichers into the tenant loader.\\n- Billing webhook hooks — registerBillingWebhookHandler receives the platform Stripe events (commerce orders, booking payments, marketplace purchases live in their plugins).\\n- Permissions (AGL-435) — registerPluginPermissions([{key, label, defaults: {admin, editor, viewer}}]) contributes role-resolved keys: they ride every resolved permission set (ConsolePluginPageProps. permissions, API-side resolution), and custom roles override them key-by-key. Reference adopter: commerce\\'s managePos.\\n- Scheduled jobs (AGL-435) — registerPluginJob({pluginId, name, intervalMinutes, handler}) on the /server surface; the deployment\\'s scheduler POSTs /api/plugins/run-jobs (shared PLUGINJOBSSECRET header, 501 when unconfigured) and due jobs run with error isolation. Keep handlers idempotent and bounded. Reference adopter: bookings\\' expire-stale-holds.\\n- Custom field types (AGL-434) — registerCustomField' },\n { path: '/developers/plugins/building-feature-plugins', title: 'Building feature plugins', heading: 'Remote bundles: the trusted realm tier (AGL-420)', anchor: '#remote-bundles-the-trusted-realm-tier-agl-420', text: 'Marketplace plugins normally run sandboxed in the cross-origin PluginFrame. Listings a staff member has reviewed and signed (trust: \\'realm\\' + an Ed25519 signature over the bundle\\'s sha256 on the version doc) instead load INTO the app realm and use every registry above — first-party-grade extensions installed per workspace, no repo change.\\n- Build with tools/plugin-loader/realm/rollup.config.mjs: your react, react/jsx-runtime, and @aglyn/aglyn imports compile to lookups on globalThis.AGLYNPLUGINHOST (the app\\'s own singletons), so the emitted bundle imports nothing. Export register(host) for client surfaces; registerApi() for server handlers.\\n- Publish through the marketplace pipeline, install (pins {version, sha256}), then staff grant trust via POST /api/admin/sign-plugin.\\n- The console loads an org\\'s realm installs before the shell renders; published sites load them post-hydration (additive runtimes — first paint never waits on a marketplace CDN). Every load verifies the sha256 pin and the platform signature and fails closed; revocations is the hard kill switch.\\n- Server handler bundles are additionally gated by PLUGINREMOTESERVER=enabled + a per-deploy PLUGINREMOTESERVERBUNDLES allowlist, default off everywhere.\\nFull trust chain, env matrix, and walkthrough: docs/PLUGINLOADING.md in the repo; a runnable demo lives at tools/plugin-loader/realm/demo.' },\n { path: '/developers/plugins/building-feature-plugins', title: 'Building feature plugins', heading: 'The server half (API routes)', anchor: '#the-server-half-api-routes', text: 'A feature\\'s server logic — Next.js API routes backed by firebase-admin — moves into its plugin the same way, through the API-route registry (AGL-396), the server counterpart to the ConsoleExtension registry.\\nEach Next app ships one catch-all dispatcher that imports a server-only registration module (registerPluginApis()) and resolves the request path against the registry. Both apps are on the App Router (app/api/[...pluginApi]/route.ts, AGL-408/410); the same PluginApiHandler runs unchanged in either. The dispatcher runs the handler through the runLegacyHandler adapter (@aglyn/aglyn/server, AGL-407), which bridges the Web Request/Response to the framework-light (req,res) contract:\\nNamed API routes win over the catch-all, so URLs are preserved: to migrate /api/events/list, delete the named route file and register the handler — the dispatcher serves the same URL. Import the plugin\\'s /server entry only from the dispatcher\\'s registration module (server code); the client barrel must never pull it in, or firebase-admin leaks into the browser bundle. Reference: libs/plugins/events-calendar/src/lib/server.ts.\\nBoth Next apps carry a dispatcher. The tenant (site-facing) app registers via registerTenantPluginApis; the console (authoring) app has its own dispatcher + registerConsolePluginApis, and plugins expose a separate registerConsoleApi() for console-only handlers (staff/merchant ops, cron jobs) so each app registers only what it serves. A migrated route keeps its /api/... URL in whichever app owned it.\\nRaw-body routes. On the App Router a handler reads the raw body directly (request.text()/arrayBuffer()), so webhooks (Stripe billing/webhook, Svix email/events) and size-capped uploads need no bodyParser config — the old Pages Router export const config exceptions are gone; byt' },\n { path: '/developers/plugins/building-feature-plugins', title: 'Building feature plugins', heading: 'Project setup', anchor: '#project-setup', text: 'Scaffold a new first-party plugin instead of hand-copying one (AGL-425):\\nThat generates the complete libs/plugins/my-plugin library (correctly tagged, with register entries per surface and a passing spec), registers it in plugins.config.json + the tsconfig aliases, re-runs the manifest codegen, and prints the two manual follow-ups (catalog entry + release flag). Marketplace/marketplace authors start from tools/plugin-loader/realm/template instead — a standalone npm package that builds a host-ABI realm bundle.\\nConventions the scaffold already applies:\\n- Tag new plugin libs with exactly [\"aglyn:addons\"] — nothing else (AGL-409). This single tag is a plugin\\'s whole module-boundary identity: as a dependency target no core scope\\'s allowlist reaches it, so nx lint rejects any core lib importing @aglyn/plugins- (the app can run without the plugin). As a source, the aglyn:addons rule still lets a plugin import any lib and other plugins. Do NOT add scope:lib/scope:aglyn back — that reopens the hole (every lib carries scope:lib).\\n- Plugins are wired in only through the generated loader manifests (plugins.config.json → plugins..generated.ts, AGL-417); core libs and app feature code must never import a plugin.\\n- Plugin libs may import @aglyn/plugins-mui for primitives; nothing may import a feature plugin from @aglyn/plugins-mui (that direction is the anti-pattern this rule exists to stop).' },\n { path: '/developers/plugins/building-feature-plugins', title: 'Building feature plugins', heading: 'Reference implementations', anchor: '#reference-implementations', text: '- Events calendar (libs/plugins/events-calendar) — the reference: its whole console page lives in the plugin (AGL-313/394).\\n- Bookings (libs/plugins/bookings) — the second full extraction (AGL-395): the booking canvas component moved out of plugins-mui and the bookings manager out of the app; the page reads plan limits via the org prop + checkQuota.\\n- Redirects (libs/plugins/redirects) — a console-only plugin (AGL-395): redirects enforce server-side (ISR), not through a canvas component, so it exports only registerRedirectsConsole() and ships no UI bundle — nothing to register in the tenant/besigner. The minimal shape when a feature has console surface but no site component.\\n- Logic (libs/plugins/logic) — console-only (AGL-395): variables + no-code functions and the reference-integrity audit. It also exports shared tooling — the where-used dialog and its fetch util, plus the variable/function cards — which the app\\'s workflows surface and besigner ƒx button import from @aglyn/plugins-logic. Always-on (not release-flagged).\\n- CRM (libs/plugins/crm) — console-only (AGL-395): one nav item whose sections — contacts, leads, companies, deals, tasks, reports, fields — are routes the shell resolves and gates, each with record pages beneath it. The plugin id is crm; it was contacts while the surface was one list, and the old id is still read from every org\\'s enabled-plugins list. The FeatureGate and layout wrapper come from the shell; the contacts section reads the contactsPerHost quota off the org prop.\\n- Inbox (libs/plugins/inbox) — console-only (AGL-395): form-submissions reader, site members + leads, and the borrowed Campaigns section. Its three sections are routes rather than tabs (AGL-2501), declared in inbox-console-sections.ts and registered on the nav item. Depends on @a' },\n { path: '/developers/plugins/guides/console-and-site', title: 'Guide: console extensions & site surfaces', heading: '', anchor: '', text: 'Console extensions & site surfaces\\nTask-ordered recipes; the deep semantics live in Building feature plugins and the API reference.' },\n { path: '/developers/plugins/guides/console-and-site', title: 'Guide: console extensions & site surfaces', heading: 'Add a console page', anchor: '#add-a-console-page', text: '1. Export a registerConsole() that calls registerConsoleExtension with a navItems entry carrying a lazy Component.\\n2. The shell serves it at /[hostId]/ , wires breadcrumbs/header, resolves the featureFlag entitlement, and passes ConsolePluginPageProps { hostId, entitled, org, permissions }.\\n3. Need data? Use @aglyn/tenant-feature-instance hooks (useFirestoreCollection, usePluginConfig, …) — never console-app hooks.\\n4. First-party: add the register fn to your plugins.config.json entry under console and re-run the manifest codegen (the scaffolder did both if you used it).' },\n { path: '/developers/plugins/guides/console-and-site', title: 'Guide: console extensions & site surfaces', heading: 'Add a widget to a shell zone', anchor: '#add-a-widget-to-a-shell-zone', text: 'Pick a zone from the catalog, then:\\nYour component receives the zone\\'s documented props. Multiple plugins can target one zone; the shell renders all of them.' },\n { path: '/developers/plugins/guides/console-and-site', title: 'Guide: console extensions & site surfaces', heading: 'Wrap every console page (providers)', anchor: '#wrap-every-console-page-providers', text: 'providers: [MyProvider] mounts around every console page once the registry is populated — the marketplace plugin\\'s AI-assist provider is the reference. Providers receive the org billing doc as tenant.' },\n { path: '/developers/plugins/guides/console-and-site', title: 'Guide: console extensions & site surfaces', heading: 'Add a canvas component (Besigner + published sites)', anchor: '#add-a-canvas-component-besigner--published-sites', text: 'Use defineUiFeatureBundle on your site surface — components get schemas + presets and appear in the Besigner drawer. Component ids are persisted in screen documents; never rename them. The editor and the published site both suspend until your site surface registers, so the canvas never renders unregistered components.' },\n { path: '/developers/plugins/guides/console-and-site', title: 'Guide: console extensions & site surfaces', heading: 'Add a site runtime', anchor: '#add-a-site-runtime', text: 'A runtime renders on every published page of workspaces that enable your plugin — overlay engines, analytics beacons, experiment runners:\\nPair it with a server enricher (registerSitePageEnricher, /server) that writes the props slice your runtime reads back from page. Enricher errors are isolated — your slice drops, the page survives. Cross-plugin wiring belongs in bootstrap (), which runs after every plugin in the batch has registered.' },\n { path: '/developers/plugins/guides/console-and-site', title: 'Guide: console extensions & site surfaces', heading: 'Troubleshooting', anchor: '#troubleshooting', text: '- Nav item missing: the plugin isn\\'t enabled/released for the workspace, or the register fn name in plugins.config.json drifted (the loader logs missing register fn).\\n- Widget renders nowhere: check the slot name against the catalog — unknown slots render only where an app mounts a custom PluginWidgetSlot.\\n- Canvas blank in the editor: your component registered on the console surface instead of site — the editor gates load site.' },\n { path: '/developers/plugins/guides/examples', title: 'Worked examples', heading: '', anchor: '', text: 'Worked examples\\nThe best examples are the shipping plugins — each one is the reference implementation of a pattern. Generated skeletons stay fresh by construction: node tools/scripts/create-plugin.mjs sample --surfaces console,tenantApi scaffolds a complete, test-passing plugin you can read and delete. First-party plugin libs are deliberately not duplicated as \"example\" libs — they would rot, and the scaffolder and the live plugins can\\'t. The one checked-in example is the sandbox marketplace plugin below, which exists because the sandbox contract has no first-party equivalent to read.\\n| Pattern | Read this | Why |\\n| Console-only plugin | libs/plugins/redirects | The minimal shape: one nav item + page, server-side enforcement, no canvas bundle |\\n| Full-stack feature | libs/plugins/commerce | Pages, widgets, canvas components, page resolvers, APIs, billing hooks — everything at once |\\n| Site enricher + runtime pair | libs/plugins/marketing | Server enricher writes page props; the registered runtime reads them back |\\n| Plugin→plugin composition | libs/plugins/inbox | Composes a section from marketing — the dependency direction plugins are allowed |\\n| Site canvas components | libs/plugins/bookings | Canvas component + console manager + APIs + a config schema (maxDaysAhead) and a scheduled job |\\n| Custom field type | libs/plugins/marketplace (rating) | Pure-data type + client Input, registered from both surfaces |\\n| Plugin permissions | libs/plugins/commerce (managePos) | Per-tier defaults riding every resolved role set |\\n| Marketplace realm bundle | tools/plugin-loader/realm/demo (+ EXPLAINED.md) | The standalone track, narrated line by line |\\n| Marketplace starter | tools/plugin-loader/realm/template | What you actually copy to begin |\\n| Sandbox marketplace plugin | exampl' },\n { path: '/developers/plugins/guides/first-plugin', title: 'Build your first plugin', heading: '', anchor: '', text: 'Build your first plugin\\nTwo tracks share the same registries. First-party plugins live in this repo under libs/plugins/ and load through the generated manifests; marketplace plugins are standalone bundles published to the marketplace. This walkthrough does marketplace end-to-end and notes the first-party deltas.' },\n { path: '/developers/plugins/guides/first-plugin', title: 'Build your first plugin', heading: '1. Scaffold', anchor: '#1-scaffold', text: 'The marketplace starter gives you src/index.js (the entry contract), manifest.json, a build config that compiles your react / @aglyn/aglyn imports into host-ABI lookups, and a README that becomes your marketplace listing docs. The first-party scaffolder generates the whole Nx library, wires plugins.config.json, and prints the two manual follow-ups (catalog entry + release flag).' },\n { path: '/developers/plugins/guides/first-plugin', title: 'Build your first plugin', heading: '2. Write the entry', anchor: '#2-write-the-entry', text: 'Every registry in the API reference is available on host.aglyn; the zones catalog lists where widgets can render.' },\n { path: '/developers/plugins/guides/first-plugin', title: 'Build your first plugin', heading: '3. Develop against a live workspace', anchor: '#3-develop-against-a-live-workspace', text: 'Start the console/tenant dev server with the dev-loop variable and refresh after each rebuild:\\nThe bundle loads unverified (localhost only; the whole path is compiled out of production builds). The browser console logs dev realm bundle loaded (UNVERIFIED): my-plugin. If a change doesn\\'t stick, clear the Next dev cache (rm -rf apps/console/.next/cache) — inlined env values live in it.' },\n { path: '/developers/plugins/guides/first-plugin', title: 'Build your first plugin', heading: '4. Verify', anchor: '#4-verify', text: 'The same checks the publish API enforces, run over the bundle\\'s parse tree: entry exports, no leftover static imports, no eval/Function/computed access on a global, no import() it cannot resolve, size — and every network call diffed against your manifest\\'s capabilities.network, which is why the manifest goes in too. It lists every area it checked, so you can tell a pass from a check that never ran, and prints the bundle\\'s sha256 — the content pin every install verifies.' },\n { path: '/developers/plugins/guides/first-plugin', title: 'Build your first plugin', heading: '5. Publish', anchor: '#5-publish', text: 'Publish from Marketplace → Publish (choose A plugin (upload a bundle)) or the API — the bundle plus manifest.json fields and your listing content (README, logo, screenshots, links, license, categories — see the publisher handbook). New listings enter the review queue as submitted; staff list (or verify ✅) them before they appear in browse. Publishing requires a marketplace profile, a Pro plan, and payouts onboarding for paid listings; there\\'s a daily publish cap.' },\n { path: '/developers/plugins/guides/first-plugin', title: 'Build your first plugin', heading: '6. Install, enable, load', anchor: '#6-install-enable-load', text: 'A workspace admin installs from the marketplace: the install pins your exact {version, sha256}, enables the plugin on the org switchboard, and it loads on the next visit — sandboxed by default, or into the app realm once staff have signed the version (trust: \\'realm\\'). Upgrades are explicit re-pins; your published artifacts are immutable, so nobody\\'s pinned install can change underneath them.' },\n { path: '/developers/plugins/guides/first-plugin', title: 'Build your first plugin', heading: '7. Uninstall', anchor: '#7-uninstall', text: 'Marketplace → Installed → Uninstall, or the Uninstall button on the listing\\'s own detail page (Uninstall org-wide for an org-scope install), removes the pin and the switchboard entry. Data your plugin created stays untouched — reinstalls pick up where they left off. Design for that: namespace your documents and tolerate finding old ones.' },\n { path: '/developers/plugins/guides/first-plugin', title: 'Build your first plugin', heading: 'Troubleshooting', anchor: '#troubleshooting', text: '- Nothing loads: check the browser console for the loader\\'s reason — sha mismatch (stale artifact URL), missing/invalid signature (not realm-signed yet — expected for sandbox-tier), host ABI mismatch (rebuild against the current template), or the plugin isn\\'t in org.enabledPlugins.\\n- Blank widgets / hook errors: your bundle carries its own React. Realm bundles must import react only through the template\\'s build config (host-ABI lookups) — run the verifier; leftover static imports fail it.\\n- Publish 422: the response lists exactly which static check failed.\\n- Works in dev, not after publish: the dev loop skips verification; the published path doesn\\'t. Run the verifier locally first.' },\n { path: '/developers/plugins/guides/realm-bundles', title: 'Guide: trusted realm bundles', heading: '', anchor: '', text: 'Trusted realm bundles\\nMarketplace plugins run sandboxed by default (cross-origin iframe, capability bridge). The realm tier is for plugins the platform has reviewed and signed: they load into the app realm and use every registry first-party plugins use. This guide is the operator + publisher view; the architecture is docs/PLUGINLOADING.md in the repo.' },\n { path: '/developers/plugins/guides/realm-bundles', title: 'Guide: trusted realm bundles', heading: 'Build against the host ABI', anchor: '#build-against-the-host-abi', text: 'Use the template\\'s build config: your react, react/jsx-runtime, and @aglyn/aglyn imports compile to lookups on globalThis.AGLYNPLUGINHOST — the app injects its own singletons there, which is what keeps your components on the app\\'s React and your registrations in the app\\'s registries. Export register(host) (client surfaces) and, only if you truly need server handlers, registerApi(). Declare hostAbi in your manifest; a bundle built for another generation never loads.' },\n { path: '/developers/plugins/guides/realm-bundles', title: 'Guide: trusted realm bundles', heading: 'The chain that runs before a byte executes', anchor: '#the-chain-that-runs-before-a-byte-executes', text: '1. Content pin — the install doc\\'s sha256 must match the fetched bytes (immutable content-addressed artifacts).\\n2. Platform signature — Ed25519 over the sha, granted by super staff after review; verification fails closed.\\n3. Kill switch — a revocations/{listingId} doc beats everything.\\n4. ABI check — declared hostAbi must equal the host\\'s generation.' },\n { path: '/developers/plugins/guides/realm-bundles', title: 'Guide: trusted realm bundles', heading: 'Granting trust (staff)', anchor: '#granting-trust-staff', text: 'Admin → Plugin reviews has a Listed plugins — realm trust section: every listed and verified plugin with its recent versions, each showing a Realm-trusted or Sandboxed chip and a Latest marker, with per-version Grant realm trust and Revoke realm trust buttons. Trust is granted to a specific reviewed version, not to the listing — a newer version stays sandboxed until it is reviewed and granted in turn.\\nBoth actions are super-staff only and adminAudit\\'d. The equivalent API is POST /api/admin/sign-plugin { listingId, version }, with { action: \\'revoke\\' } to revoke; a hard-kill still needs a revocation doc. Signing requires PLUGINTRUSTPRIVATEKEY on the console deployment — generate the pair with tools/scripts/generate-plugin-trust-key.mjs.' },\n { path: '/developers/plugins/guides/realm-bundles', title: 'Guide: trusted realm bundles', heading: 'Where realm bundles load', anchor: '#where-realm-bundles-load', text: '- Console: before the shell renders, from the org\\'s trusted installs.\\n- Published sites: post-hydration (additive — first paint never waits on a marketplace CDN), and only on a page that uses the plugin: one that places a component its manifest declares under contributes.site, or on every page when it declares a site feature. A plugin that only adds console widgets never loads on a published page, and neither does the host that runs it. See contributes.\\n- Server (rare): only on deployments with PLUGINREMOTESERVER=enabled plus a per-deploy listingId@version allowlist; every load is audited.' },\n { path: '/developers/plugins/guides/realm-bundles', title: 'Guide: trusted realm bundles', heading: 'Key rotation', anchor: '#key-rotation', text: 'generate-plugin-trust-key.mjs → resign-realm-plugins.mjs (re-signs every granted version with the new key, --dry-run first) → deploy the new public keys → retire the old private key. Re-sign before the public-key swap so nothing stops loading mid-rotation.' },\n { path: '/developers/plugins/guides/realm-bundles', title: 'Guide: trusted realm bundles', heading: 'Troubleshooting', anchor: '#troubleshooting', text: '- Loads in dev loop, not from the marketplace: dev loop skips verification. Check the browser console for the loader\\'s reason — usually \"missing signature\" (not signed yet) or \"sha256 mismatch\".\\n- AGLYNPLUGINHOST is not set: your bundle executed outside a host surface — the console gate and site effect set the ABI before loading; don\\'t import the bundle yourself.\\n- Server bundle didn\\'t register: the dispatcher logs which chain link failed (not realm-trusted, built for host ABI N, fetch, signature) and skips — check the deployment logs and the allowlist entry.' },\n { path: '/developers/plugins/guides/server-apis', title: 'Guide: server APIs, webhooks & jobs', heading: '', anchor: '', text: 'Server APIs, webhooks & jobs\\nEverything here lives on your plugin\\'s /server entry and runs inside the apps\\' [...pluginApi] dispatchers.' },\n { path: '/developers/plugins/guides/server-apis', title: 'Guide: server APIs, webhooks & jobs', heading: 'An API route', anchor: '#an-api-route', text: '- Served at /api/my-plugin/ping on the app(s) whose manifest lists your register fn (tenantApi, consoleApi, or both). Declare apiPrefixes: [\"my-plugin\"] in plugins.config.json.\\n- Gating is automatic: requests carrying a hostId (query or JSON body) 404 when the target workspace has your plugin disabled or its release flag is off. Handlers still self-check entitlements (checkEntitlement/checkQuota with the org doc) for plan gating.\\n- A request that names no site is gated as anonymous unless its route says who it is for — see Naming the subject below.' },\n { path: '/developers/plugins/guides/server-apis', title: 'Guide: server APIs, webhooks & jobs', heading: 'Naming the subject a request is for', anchor: '#route-subject', text: 'The release gate reads per-organization overrides and rollout buckets off an organization, and learns it from the hostId a request names. Two kinds of request name no site and still belong to one organization: a request from an organization-level surface, and a provider\\'s OAuth redirect back to your route, which carries no bearer token. Declare how your route reads its subject:\\n- The resolver receives a clone of the request, so your handler still reads the body. It is consulted only when the request names no hostId.\\n- A subject is no bypass: the gate still refuses an organization the flag is off for. It only asks its question about the right organization.\\n- Return a uid only from a route that verified a signature binding that account to the request (a signed OAuth state). The gate lets a staff account named that way preview a released-off plugin, and it ignores the uid whenever the request carries a bearer token.\\n- A resolver that throws, or returns anything that is not a plain id, names no subject.\\n- Settings: getPluginConfig(orgId, pluginId, { hostId }) returns your declared defaults merged with the workspace\\'s values and then the site\\'s overrides. Pass hostId whenever the request has one — without it the site\\'s override is silently ignored while the console still shows it. See Plugin configuration.' },\n { path: '/developers/plugins/guides/server-apis', title: 'Guide: server APIs, webhooks & jobs', heading: 'Webhooks with signature verification', anchor: '#webhooks-with-signature-verification', text: 'PluginApiRequest.rawBody carries the unparsed payload:' },\n { path: '/developers/plugins/guides/server-apis', title: 'Guide: server APIs, webhooks & jobs', heading: 'Platform billing events', anchor: '#platform-billing-events', text: 'Your handler receives the platform\\'s Stripe events (with requestHost for callback URLs). Throwing propagates to a 500 and Stripe redelivers — write idempotent handlers.' },\n { path: '/developers/plugins/guides/server-apis', title: 'Guide: server APIs, webhooks & jobs', heading: 'Scheduled jobs', anchor: '#scheduled-jobs', text: 'The deployment\\'s scheduler POSTs /api/plugins/run-jobs with the x-plugin-jobs-secret header (PLUGINJOBSSECRET); due jobs run error-isolated, and last-run marks persist across cold starts. Keep handlers bounded (limits, no unbounded scans) — they share the API process.\\nLockdown — lockdown is required\\nA job runs on platform credentials with no visitor, no session and no org, so none of the gates that cover the request paths are anywhere near it. A site can be locked for maintenance, non-payment, abuse or a legal takedown, and a beat that kept emailing its customers or moving its stock would be acting for a site the platform has taken off the air.\\nSo every registration declares what it touches, and the runner injects the verdict:\\n| lockdown | Means | The runner hands your handler |\\n| -- | -- | -- |\\n| { scope: \\'per-host\\' } | The job acts for sites. | A working gate — ask it for every host you touch. |\\n| { scope: \\'platform\\', reason: \\'…\\' } | Nothing a lock could be about. | A gate that throws if asked, so a wrong declaration fails loudly. |\\nTwo rules the platform\\'s own jobs follow, and yours should:\\n- Take the gate you are given. Calling pluginJobHostGate() yourself works at runtime but is refused by the coverage guard: a job free to mint its own gate is free to declare platform and never meet the check that the declaration is true. (The x-cron-secret doors that drive a pass by hand are the exception — they have no runner to hand them one.)\\n- Skip, do not drop. A lockdown is a pause, not a cancellation. Leave the row exactly as it was — unstamped, undeleted, its retry count untouched — so the work lands on the first beat after the lift.' },\n { path: '/developers/plugins/guides/server-apis', title: 'Guide: server APIs, webhooks & jobs', heading: 'Troubleshooting', anchor: '#troubleshooting', text: '- 404 on your route: path prefix not in apiPrefixes, plugin disabled for the target workspace, or its release flag is off (staff bearer tokens bypass for preview).\\n- Webhook signature failures: you parsed body instead of verifying rawBody.\\n- Job never runs: PLUGINJOBSSECRET unset (route 501s) or the scheduler isn\\'t POSTing; the route\\'s response lists every registered job and what ran.' },\n { path: '/developers/plugins/overview', title: 'Plugins & Marketplace', heading: '', anchor: '', text: 'Plugins & Marketplace\\nPlugins extend Aglyn with new components and capabilities. You install them from the organization marketplace (Marketplace in the org navigation), configure them per site, and they run sandboxed so they can\\'t compromise your site.\\nThe marketplace lives at the organization level rather than inside each site. Its own Navigation panel lists Browse All and Installed; publishers additionally get Upload / Publish, Publisher Profile, Listings, Payouts and Sales.\\nMarketplace is now purely the market. Turning plugins on and off for the workspace moved out to its own Plugins section in the org navigation, next to Marketplace. Installed remains as a convenience for uninstalling something you just installed — every row links through to Plugins.\\nPlan availability Free to install marketplace plugins; some plugins and marketplace monetization features are paid.\\nNew to this? This page is the reference. If you\\'d rather be walked through it click by click — find something, choose which sites get it, install, upgrade, remove — start with Install your first marketplace item.' },\n { path: '/developers/plugins/overview', title: 'Plugins & Marketplace', heading: 'Install & upgrade', anchor: '#install--upgrade', text: '- Open Marketplace in the organization navigation and Browse the listings. Browse is a catalog: cards link to the detail page, which is the only place an install happens, and a confirmation dialog names exactly where it will land before anything is written.\\n- When you install a plugin, choose where it applies: All sites (organization-wide — including sites you add later) or Selected sites (a specific subset). Only plugins and dataset schemas install org-wide; components, templates, layouts, themes and email templates are site-scoped, so \"All sites\" installs them onto every current site and does not cover sites added later. The listing\\'s What\\'s included box says which of the two you are looking at.\\n- Installs are version-pinned, and you can upgrade deliberately.\\n- Installed plugins appear as named entries in the Besigner drawer, alongside built-in components.\\n- Manage what you have from the marketplace\\'s Installed section: every marketplace install, with upgrade, uninstall, and share-with-organization actions. A listing\\'s own detail page also carries Uninstall (or Uninstall org-wide) once you have it installed.\\n- Turning a plugin on or off is the Plugins section, not Marketplace. Plugins in the organization navigation lists Installed from the marketplace and Built in, each row with its switch, its release state, and a link through to that plugin\\'s own settings. If you are hunting for a switch, that is where it is.\\n- Rating a listing needs a verified email and an organization that actually installed it; commenting is open to any signed-in user.\\n- Installing enables the plugin for the workspace automatically; uninstalling disables it once no site keeps its own pin. Uninstalling never deletes the data a plugin created — reinstall and it picks up where it left off.' },\n { path: '/developers/plugins/overview', title: 'Plugins & Marketplace', heading: 'What a browse card shows', anchor: '#browse-card', text: 'Every card in Browse All carries the same four claims, so two listings side by side are comparable:\\n- A price chip, always. A paid listing shows the price — $29 — and a free one shows the word Free. There is no such thing as a card without a price chip: a missing chip would read as an oversight next to the $29 beside it, which is the one thing \"free\" must not look like. The card\\'s button repeats the price for a paid listing you don\\'t own yet: View details · $29.\\n- A star rating, with its count. Stars plus the average and the number of ratings in brackets — 4.8 (12). The count is never dropped, because \"5.0\" from one rating and \"5.0\" from forty are not the same claim.\\n- Not yet rated, in words, when nobody has rated it. An unrated listing draws no stars at all, and silence reads as \"zero stars\" rather than \"no ratings yet\".\\n- Version, publisher and installs — v3 · by @handle · 41 installs — plus the Reviewed badge when the version on offer has passed review.\\nThe listing\\'s own page repeats the price chip and the rating in its header, where the install decision is actually made. On that page an unrated listing shows nothing rather than the \"Not yet rated\" wording.' },\n { path: '/developers/plugins/overview', title: 'Plugins & Marketplace', heading: 'What\\'s included', anchor: '#whats-included', text: 'A listing page carries a What\\'s included box. Aglyn generates it from the listing — it is not publisher copy, so it cannot promise something the install does not do, and it says nothing about how much content is in there.\\nThe first row is what the install physically produces, which depends on the type:\\n| Type | The row reads |\\n| Plugin | A plugin, sandboxed on its own origin with a per-plugin CSP |\\n| Component | An editable component you can place on any screen |\\n| Template | Editable screens you can rework in Besigner |\\n| Layout | An editable layout you can apply to any screen |\\n| Dataset schema | A new empty dataset with its fields already defined |\\n| Email template | An editable email design you can send campaigns from |\\n| Theme | A theme applied to the site you choose |\\nThen, in order, up to four more rows:\\n- Where it lands. Plugins and dataset schemas install org-wide — Installs org-wide, covering sites you add later. Everything else is site-scoped and says so: Installs per site — new sites are not covered automatically. That row is marked as a note, not a tick, because it is a limit you are being told about rather than something you are getting.\\n- Review — This version passed marketplace review — only when the version on offer is approved. A new release from the same publisher starts without it.\\n- The license, when the publisher set one: Licensed MIT.\\n- Price and updates. A paid listing reads A one-time purchase — updates to this listing are included; a free one reads Free, including every future update. Either way, updates are not a second charge — but they are still deliberate, not automatic.\\nWhat the box will never tell you is how many screens or blocks a template contains. Aglyn collects no manifest of that, and counting it would mean inventing a number, so th' },\n { path: '/developers/plugins/overview', title: 'Plugins & Marketplace', heading: 'What the badges on a listing mean', anchor: '#what-the-badges-on-a-listing-mean', text: 'Two badges can appear on a listing, and they say different things. Read them as two separate claims, because that is what they are (AGL-1121).\\n| Badge | What Aglyn is claiming | What it survives |\\n| Verified publisher | A human at Aglyn confirmed who the publisher is, and that their listing describes what their code does. | A version bump. It is a claim about the publisher, not about any particular release. |\\n| Reviewed | A human at Aglyn read these exact bytes — the version currently on offer — against a required checklist. | Nothing. It is re-earned per version, so a new release starts without it. |\\nSo a new release from a verified publisher shows Verified publisher but not Reviewed, and that is honest rather than a gap: we vouch for the person, and nobody has read this particular code yet.\\nNeither badge is a security guarantee. Every plugin runs in the same sandbox with the same limits whether it is badged or not, and installability is identical — a listed plugin is installable by every workspace regardless. The badges tell you how much human attention this listing has had, not whether the code is safe.\\nA listing with neither badge is not necessarily suspect; it may simply be new. It does mean nobody at Aglyn has looked at it, so read the publisher and the docs before installing.' },\n { path: '/developers/plugins/overview', title: 'Plugins & Marketplace', heading: 'How plugins run', anchor: '#how-plugins-run', text: '- Each plugin loads into a sandboxed PluginFrame host runtime, isolated by origin.\\n- A manifest + sandbox bridge protocol defines what a plugin can do.\\n- A host-mediated network bridge lets plugins make network calls without direct access to your environment.\\n- See Sandbox security model for what that isolation actually enforces — including the per-plugin network policy — if you\\'re writing one.\\n- Your workspace decides which plugins are enabled at all, and a site admin can narrow that further per site from the site\\'s Admin → Plugins page. A plugin disabled for a site disappears from that site\\'s navigation, editor, published pages, and API — other sites in the workspace are unaffected, and a site can never enable a plugin the workspace has switched off.\\n- The site\\'s Admin → Plugins page lists both kinds in two groups — what ships with the platform, and what the workspace installed from the marketplace — because which one a row is is the first thing you need to know before switching it. A marketplace plugin gets the same per-site switch as a built-in one.\\n- Most plugins are on for a site unless it turns them off. User Accounts is the exception: it is off until a site turns it on, because it is the one that decides whether the site serves /signin, /signup and /recover — and a sign-in page on a marketing site is worse than a missing one. See Member accounts for what the switch does.\\n- AI has a site switch and no workspace switch. It is on for every workspace, because the AI add-on, credits, allotments and overage billing belong to the workspace rather than to any site. A site can switch AI off for itself on its Admin → Plugins page: that hides AI on the site, refuses every AI request made for it and stops its queued jobs without spending credits, while the workspace\\'s AI ke' },\n { path: '/developers/plugins/overview', title: 'Plugins & Marketplace', heading: 'When one plugin depends on another', anchor: '#when-one-plugin-depends-on-another', text: 'Some plugins cannot run without another one. User Accounts is the case that exists today: its Members blocks and its membership/ API handlers ship inside the Commerce bundle, so User Accounts with Commerce switched off is a site that still routes /signin while nothing can answer the sign-in request.\\nSwitching off a plugin something else depends on therefore asks first. The dialog names every dependent, says what disabling each one does to a site that is already published, and offers Cancel or Continue and disable those too:\\n- Cancel writes nothing and the switch stays where it was.\\n- Continue disables the plugin and every dependent in a single save, so a half-applied cascade cannot happen.\\n- Dependents are followed transitively — if C needs B and B needs A, switching off A names both.\\n- Re-enabling does not undo a cascade. Turning the plugin back on later does not switch its dependents back on; re-enable each one yourself.\\nTwo consequences are possible and the dialog distinguishes them, because they are very different decisions:\\n| The dependent | What disabling it does |\\n| Registers site components (Commerce, Bookings, Email, Events Calendar, Marketing) | Elements already placed on published pages stop rendering. For a single site the dialog counts them. |\\n| Serves routes (User Accounts, Redirects, Automation) | Published pages keep rendering, but the routes or rules it serves stop. |\\n| Console-only (CRM, Data, Inbox, Logic, Marketplace) | It leaves navigation and the editor. Published pages are unaffected. |\\nCounts come from scanning the published version of each screen, layout and component, and are capped — where the scan hits its cap the dialog says \"at least\". Drafts are not scanned.\\nThe warning covers built-in plugins only First-party plugins declare what they req' },\n { path: '/developers/plugins/overview', title: 'Plugins & Marketplace', heading: 'Configure', anchor: '#configure', text: 'A plugin declares the settings it takes, and the console renders the form. A workspace sets a value once and every site follows it. See Plugin configuration.\\nThis is the same for a marketplace plugin. A published manifest may carry a config block declaring the plugin\\'s fields and their defaults, and the console renders it from the pinned manifest — the same form, the same workspace-default and per-site-override behavior, with nothing for the publisher to build. See Plugin manifest.\\nSettings for one site {#configure-site}\\nA site that needs a different answer overrides that one field and keeps inheriting the rest — including later changes the workspace makes to the fields it did not override. So the same plugin behaves differently on each site without the author writing any inheritance of their own.\\nEach field on a site\\'s plugin page says which state it is in, shows the workspace\\'s own value beside it, and offers a one-click way back to inheriting. Editing a field is what overrides it; reverting deletes the site\\'s value rather than storing a copy of the workspace\\'s, so the site resumes following.' },\n { path: '/developers/plugins/overview', title: 'Plugins & Marketplace', heading: 'Publish your own', anchor: '#publish-your-own', text: 'The publish + install pipeline lets developers ship plugins to the marketplace with version pinning. The marketplace also supports paid listings, Stripe Connect payouts, and a publisher ledger.' },\n { path: '/developers/plugins/overview', title: 'Plugins & Marketplace', heading: 'Related', anchor: '#related', text: '- Install your first marketplace item — the click-by-click walkthrough of everything on this page\\n- The Besigner\\n- Site templates & block library\\n- Building feature plugins — the developer guide to every extension surface\\n- Repo docs: docs/PLUGINLOADING.md (loading architecture and trust tiers) and docs/PLUGINPLATFORMGAPS.md (competitive analysis and the v2 roadmap)' },\n { path: '/developers/plugins/publish-a-plugin', title: 'Publish a plugin', heading: '', anchor: '', text: 'Publish a plugin\\nThis page covers the mechanics; the complete marketplace guide — review process, listing authoring, versioning, payouts — is the Publisher handbook.\\nBuilt something reusable? Publish it to the marketplace marketplace so other Aglyn users can install it — free or paid.\\nPlan availability Publishing is open to developers; paid listings use Stripe Connect for payouts.' },\n { path: '/developers/plugins/publish-a-plugin', title: 'Publish a plugin', heading: 'The publish pipeline', anchor: '#the-publish-pipeline', text: 'Plugins go through a publish + install pipeline with version pinning, so installs are reproducible and upgrades are deliberate:\\n1. Package your plugin against the manifest + sandbox bridge protocol (see Plugins overview).\\n2. Verify locally: node tools/scripts/verify-plugin-bundle.mjs dist/plugin.bundle.mjs — the publish API enforces the same checks.\\n3. Publish a version to the marketplace: Marketplace → Publish → \"A plugin (upload a bundle)\" → Publish a plugin… — a page at / /marketplace/publish/plugin where you upload the bundle and its manifest.json, choose who can install it, and set the listing details, repository URL and price. What you type is kept as a local draft until you publish; the files are not, so re-choose them. You confirm a short pre-publish checklist — repository, license, data disclosure, network hosts, testing — which is recorded against that exact bundle and shown to the reviewer. Uploads publish sandboxed; staff review lists (and may sign) them.\\n4. Installers get that pinned version and choose when to upgrade — ship updates by bumping the manifest version and uploading again.' },\n { path: '/developers/plugins/publish-a-plugin', title: 'Publish a plugin', heading: 'Private plugins', anchor: '#private-plugins', text: 'Not everything you build is for sale. When you upload a bundle, Who can install this offers:\\n- Anyone — publish to the marketplace. The default. Listed for every workspace once a reviewer approves it.\\n- Only this organization — private plugin. Never listed in the marketplace, for anyone, and installable only by your own sites.\\nPrivate is a choice about audience, not about trust. A private plugin still executes on Aglyn infrastructure and still reaches the host ABI, so it goes through the identical pipeline: the same review queue, the same reviewer checklist, the same bundle verifier, and the same super-staff signature if it ever needs realm trust. It is not a way to ship unreviewed code.\\nPrivate plugins are always free — nobody else can install them — and you reach yours from Marketplace → Listings, not from Browse.\\nA private plugin can go public later from that same Listings tab. The bytes were already approved, so it needs no re-review; it does need what any marketplace listing needs first — a description, a README, and a license. Going back to private is always available.' },\n { path: '/developers/plugins/publish-a-plugin', title: 'Publish a plugin', heading: 'Paid listings', anchor: '#paid-listings', text: 'You can list a plugin as paid:\\n- Payments run through Stripe Connect.\\n- Earnings are tracked in a publisher ledger.\\nThe Sales card reports net paid out — what actually reached your Stripe account. It is the pre-tax listing price less the platform fee, which is the fixed amount Stripe transfers to you on each sale.\\nThree things it deliberately is not:\\n- Not what the buyer paid. Buyers are charged sales tax on top of your price. Aglyn collects and remits that tax under its marketplace-provider registration, so it is never part of your share. The card shows what buyers paid, the tax and the platform fee beside your payout so the four reconcile.\\n- Not gross minus the fee. The fee is calculated on the pre-tax price, so subtracting it from the tax-inclusive gross would count the tax as your earnings.\\n- Not counting refunds or chargebacks. A refunded sale and a lost dispute leave the payout total; the amount pulled back out of your account is shown on its own line so a total that shrank can be reconciled.' },\n { path: '/developers/plugins/publish-a-plugin', title: 'Publish a plugin', heading: 'Your publisher profile', anchor: '#your-publisher-profile', text: 'Your publisher profile is a storefront: its own page in the marketplace listing everything you\\'ve published, reachable from the Publisher card on any of your listings and from the by @handle link on every browse card. Buyers who like one of your plugins use it to find the rest, so the handle, display name, and bio you set are worth the same care as a listing.' },\n { path: '/developers/plugins/publish-a-plugin', title: 'Publish a plugin', heading: 'Tips', anchor: '#tips', text: '- Bump versions intentionally — installers stay on their pinned version until they upgrade.\\n- Keep the manifest\\'s declared capabilities minimal; the sandbox enforces them.' },\n { path: '/developers/plugins/publish-a-plugin', title: 'Publish a plugin', heading: 'Related', anchor: '#related', text: '- Plugins & marketplace' },\n { path: '/developers/plugins/publishing/publisher-handbook', title: 'Publisher handbook', heading: '', anchor: '', text: 'Publisher handbook\\nEverything a marketplace publisher needs. The developer side (building the bundle) is the first-plugin guide; this is the marketplace side.' },\n { path: '/developers/plugins/publishing/publisher-handbook', title: 'Publisher handbook', heading: 'Before your first publish', anchor: '#before-your-first-publish', text: '1. Publisher profile (Marketplace → Profile) — your handle and display name appear on every listing. Publishing is organization-owned: the listing belongs to your organization, not your personal account.\\n2. Publisher agreement (Marketplace → Profile) — accept it once, on behalf of the organization. See The publisher agreement.\\n3. Plan: publishing requires a Pro plan.\\n4. Payouts (paid listings only): complete Stripe Connect onboarding from Marketplace → Payouts. The platform fee is 20% (30% on free plans).' },\n { path: '/developers/plugins/publishing/publisher-handbook', title: 'Publisher handbook', heading: 'The publisher agreement', anchor: '#the-publisher-agreement', text: 'Your organization — not you personally — is the publishing party, so the organization accepts the Marketplace Publisher Agreement once, from Marketplace → Profile. Only an owner or admin can accept it, because only they can bind the organization, and we record who accepted it and when.\\nYou can read the agreement in full at aglyn.com/legal/marketplace-publisher-agreement, alongside the rest of the legal center. The version in force is stated at the top of that page, and it is the same string the panel records against your acceptance.\\nIt is a different thing from the pre-publish checklist, and they are not interchangeable:\\n| | Publisher agreement | Pre-publish checklist |\\n| About | The relationship | The bytes in this bundle |\\n| Scope | Your organization | One version |\\n| Asked | Once, and again when the terms change | Every publish, including a republish of the same version number |\\nThe summary shown above the accept button is the part that tends to surprise people later — the license you grant us to host, verify and distribute; what you warrant about each version; that you cannot recall code already installed; that we can disable a version everywhere without notice if it looks dangerous; that review is a safety screen and not an endorsement; and that on paid listings we process the sale, pay your share into your own Stripe Connect account less a platform fee, and collect and remit sales tax — while refunds, chargebacks and support on your artifacts stay with you. Read the full agreement before accepting.\\nIf we change the agreement, publishing stops until someone who can bind your organization reads and accepts the new version. An older acceptance is never carried forward — that is the whole reason the agreement is versioned. Nothing already published is affected, and re' },\n { path: '/developers/plugins/publishing/publisher-handbook', title: 'Publisher handbook', heading: 'Where to publish from', anchor: '#where-to-publish-from', text: 'Publishing lives at the organization level — Marketplace → Publish. Pick a source site, then what to publish:\\n- A component — a reusable component from that site.\\n- A layout — a published layout from that site.\\n- A dataset schema — the field model of one of your organization\\'s datasets. Records are never published — only the structure travels.\\n- An email template — a transactional email you\\'ve designed for that site (only emails that already have a saved design are offered).\\n- This entire site — its published screens and theme, as an installable template.\\nDataset schemas are the one exception to the source-site picker: datasets belong to the organization rather than to a site, so the picker is hidden and the schema publishes from the org directly.\\nThe per-site shortcuts still exist for convenience — the Publish actions on a site\\'s Components and Layouts pages, and Publish as template on the Setup page — and open the same listing form.\\nWhat installing each type does\\nInstalling never changes a running site. What that means per type:\\n| Type | Where it lands | Live immediately? |\\n| Component | The site\\'s components | Yes, once you place it |\\n| Layout / Site template | The site\\'s Templates library | No |\\n| Plugin | An org or per-site version pin | Yes |\\n| Dataset schema | A new, empty dataset in the org | Yes (it\\'s new) |\\n| Email template | A draft version of that email | No |\\n| Email starter | A new email in the site\\'s Email templates list | No |\\nThree details worth knowing before you publish one:\\n- A dataset schema installs as a brand-new dataset every time; it never merges into an existing one, since a schema change over existing rows would reinterpret live data. The new dataset is shared with every site in the installing organization. reference fields are relinked by dis' },\n { path: '/developers/plugins/publishing/publisher-handbook', title: 'Publisher handbook', heading: 'Publishing a version', anchor: '#publishing-a-version', text: 'Run the local verifier first — the publish API enforces the same checks and rejects with the exact problem list:\\nThe verifier parses your bundle rather than reading it as text, so it sees what a source scan could not: property access computed at runtime on globalThis/window/document, the Function constructor reached through .constructor(), and import() with a specifier it cannot resolve. All of those are refused.\\nDeclare every origin you call. The verifier collects your fetch, XMLHttpRequest, WebSocket and sendBeacon calls and diffs them against capabilities.network in your manifest; an origin you call but do not declare fails the publish. This is the same list your plugin\\'s CSP is built from, so an undeclared call would have been blocked at runtime anyway — failing here means you find out at publish time instead of in production. A URL your code only knows at runtime cannot be checked, so it is reported as a warning and a reviewer will ask about it.\\nCalling through a local name does not change the answer: const f = fetch, const d = globalThis.fetch and const {fetch: h} = globalThis are all resolved back to fetch, and a URL kept in a constant — a plain string, a template, or a concatenation of them — is read rather than shrugged at. What is not read is a name that gets reassigned, shadowed by a parameter or declared twice; there the value at the call site would be a guess, so the call is reported as a URL known only at runtime.\\nIf you hand a URL to a helper the checker cannot follow, it says so rather than reporting no network calls — so declare the origins your plugin talks to and the row goes quiet on its own.\\nPass the manifest (as above, or leave it beside your bundle or one level up) or the network checks can only warn locally while the publish API still rejects.\\nIt' },\n { path: '/developers/plugins/publishing/publisher-handbook', title: 'Publisher handbook', heading: 'Review: what happens after you publish', anchor: '#review-what-happens-after-you-publish', text: 'New plugin listings enter the queue as submitted and don\\'t appear in public browse until staff list them. Reviewers see your listing content, declared capabilities, and a fresh static-verification run. Outcomes:\\n- Listed — publicly browsable.\\n- Verified publisher ✅ — a badge on you, not on any one release. See below.\\n- Rejected — you\\'re notified with the reason; fix and republish.\\n- Realm trust (separate, rare): staff may additionally sign a version so it runs in the app realm instead of the sandbox — first-party-grade placement for plugins that earn it.\\nThe two badges, and what each one promises\\nThe marketplace shows two separate claims, because they answer different questions and conflating them would let one vouch for the other:\\n| Badge | What it means | Scope |\\n| Verified publisher | A human at Aglyn confirmed who you are and that your listing describes what your code does. | The publisher. It survives a version bump. |\\n| Reviewed | A reviewer completed the checklist against these exact bytes ({version, sha256}). | That one version. |\\nPublishing a new version clears the Reviewed badge and puts the release back in the queue — including on a listing whose publisher is already verified. That is deliberate: the badge is a statement about code someone read, so it cannot follow code nobody has.\\nYour Verified publisher badge is unaffected by a version bump. Buyers see both, so a new release from a verified publisher reads honestly: we vouch for you, and we have not read this release yet.\\nUntil a version is Reviewed, its listing page carries the standard \"review the publisher and docs before installing\" note. Being a verified publisher does not remove it — the plugin still runs sandboxed either way.\\nSpeed the review up: a real README, a license, sane capabilities (request o' },\n { path: '/developers/plugins/publishing/publisher-handbook', title: 'Publisher handbook', heading: 'Private plugins', anchor: '#private-plugins', text: 'Choose Only this organization under Who can install this when you upload, and the listing becomes private: never browsable in the marketplace by anyone, and installable only by your own organization\\'s sites. That last part is enforced when the install is requested, not by hiding the page — knowing a private listing\\'s id gets a stranger nowhere.\\nPrivate changes the audience, not the bar. Your bundle still runs on Aglyn infrastructure and still reaches the host ABI, so it goes through the same queue, the same reviewer checklist, the same static verifier, and the same super-staff signature if it ever needs realm trust. Staff see private submissions in the queue marked Private.\\nPrivate plugins are always free, and you find yours under Marketplace → Listings rather than in Browse — View opens the same detail page you install from.\\nGoing public later takes no re-review: the bytes were already approved, and who may install them was never part of that approval. It does ask for what any marketplace listing needs first — a description, a README, and a license — via Make public on the Listings row. Going back to private is always available and takes effect immediately.' },\n { path: '/developers/plugins/publishing/publisher-handbook', title: 'Publisher handbook', heading: 'Authoring your listing', anchor: '#authoring-your-listing', text: 'Your listing IS your storefront — it renders on the detail page every buyer sees. Editable at publish time or any time after, no republish needed: open Edit listing on your own listing\\'s detail page (the whole page becomes the editor), or use the Edit action on Marketplace → Listings.\\n| Field | Guidance |\\n| Name | The display name on every browse card and at the top of the detail page; ≤80 chars. A typo here is fixable without republishing — leaving it blank keeps the current name rather than clearing it. |\\n| Description | The one-line summary under the name on browse cards; ≤500 chars. |\\n| Body (About) | The main docs: what it does, setup, what it adds, data & permissions. Rich-text editor with headings, bold/italic, links, and inline images; ≤20k chars. |\\n| Preview image | The hero shown on browse cards and at the top of the detail page — pick it from your media library. |\\n| Logo | Square; pick from the media library or paste an https URL. |\\n| Screenshots | Up to 6, from the media library or https URLs; the detail page shows a gallery with click-to-zoom. |\\n| Categories | Up to 3 from the fixed taxonomy. |\\n| License | Short label (e.g. MIT) — listings without one get flagged in review. |\\n| Homepage / repository | Public links build trust; reviewers check them. The repository is asked for at publish and required — editing it here changes the listing, not what past versions declared. |\\nBe explicit about data & permissions in the README: what your plugin reads/writes and every host in your manifest\\'s network allowlist — unverified sandbox listings show buyers a risk disclaimer, and good docs are what overcomes it.' },\n { path: '/developers/plugins/publishing/publisher-handbook', title: 'Publisher handbook', heading: 'What your listing can say about Aglyn', anchor: '#what-your-listing-can-say-about-aglyn', text: 'Your listing renders inside Aglyn\\'s own console, on the page a customer is reading while they decide whether to upgrade. That is why this one has a rule attached: a plugin sold as \"the cheap way out of Business\" is doing its selling with our pricing page, not with your plugin.\\nFine — say all of this:\\n- What your plugin does, and how well it does it.\\n- Which Aglyn plans or features it requires: \"needs the Pro plan\", \"uses custom domains\", \"requires the datasets API\".\\n- Which plans or features it is compatible with, stated as fact.\\n- What it costs, and what a buyer gets for it.\\nNot fine:\\n- Disparaging Aglyn or the platform (\"Aglyn\\'s built-in analytics are useless\").\\n- Stating or implying what an Aglyn plan costs or contains (\"Business is $X for features you won\\'t use\", \"the Pro plan caps you at 5 sites\"). Plans and prices change; a listing that quotes them goes stale and misinforms buyers who trust it.\\n- Comparing your price against ours (\"half the price of an upgrade\").\\n- Naming, framing or marketing the plugin around avoiding, replacing or discouraging an Aglyn plan or upgrade — \"Skip the upgrade\", \"Business features without the Business plan\", \"don\\'t pay for Pro\".\\nThe line is simple: describe your plugin, not our pricing. If you need a customer to know they must be on Pro for your plugin to work, say exactly that — a requirement is a fact about your plugin, and it belongs in the listing.\\nThis is §3(h) of the publisher agreement, you confirm it at every publish, and a reviewer checks the title, description and README against it. A listing that crosses it is rejected with the reason rather than quietly edited, so you get to fix your own words.' },\n { path: '/developers/plugins/publishing/publisher-handbook', title: 'Publisher handbook', heading: 'Versioning & updates', anchor: '#versioning--updates', text: 'Shipping a new version\\nPublish new version on your listing — the button on the listing\\'s own detail page, or the action in the row menu on Marketplace → Listings. It opens the same publish page bound to that listing, so you upload the new bundle and manifest and write a changelog rather than describing a listing you already own; name, description, README, license, repository and price arrive filled in from the listing and are yours to edit.\\nWhat the page tells you, because it is the part worth knowing: the version that installs today keeps installing until a reviewer approves the new one. Nobody is upgraded onto unreviewed code, and nothing you publish can change a version somebody already has.\\nTwo things it will stop you on:\\n- A mismatched manifest id. An update is recognized by your publisher organization plus the manifest id — so a bundle carrying a different id would create a separate listing, not a new version. The page says so and refuses rather than letting you find out from a duplicate listing you cannot un-publish the bytes of.\\n- The changelog confirmation. On an update the pre-publish checklist asks for it up front, because here we know it applies.\\nWho can install it is fixed when the listing is created and a new version never changes it — change that from the listing itself.\\n- Artifacts are immutable and installs pin {version, sha256} — you can never change the code a consumer runs; ship a new version instead.\\n- Users update explicitly (an Update to vX action appears when your latestVersion passes their pin). Write a changelog every publish — it renders in the detail page\\'s Version history card, where every published version is listed with its changelog, a Latest marker, and a Realm-trusted chip on versions that carry it. That card is what a cautious buyer re' },\n { path: '/developers/plugins/publishing/publisher-handbook', title: 'Publisher handbook', heading: 'How installs work (the buyer side)', anchor: '#how-installs-work-the-buyer-side', text: 'Browse is a catalog — cards link to the detail page, which is the only place an install happens. The buyer picks targets (all sites, selected sites, or org-wide for plugins), confirms a dialog that names exactly where the install lands, and the pin is written: your exact {version, sha256} pinned to the site or org, the plugin enabled for the workspace, loaded on their next visit. Buyers can uninstall from the listing\\'s own detail page (Uninstall, or Uninstall org-wide for an org-scope pin) as well as from Marketplace → Installed — removing the pin and disabling it. Data your plugin created stays, so reinstalls resume cleanly. Paid listings require purchase before install; you see sales in your publisher ledger.\\nThe detail page shows two install figures side by side: installs is the cumulative all-time total and only ever grows, while active counts the organizations and sites that currently hold a pin — it goes down when someone uninstalls. A big gap between the two is churn worth investigating.\\nEvery listing shows its artifact type as a chip — Plugin, Component, Site template, Layout, Dataset schema, Email template or Email starter — so buyers know what they\\'re getting before they open it. Dataset schemas and email templates read \"Added (v1) · add again\" rather than \"Installed\", because adding one creates a new dataset or a new draft email each time; re-adding is a legitimate action, not a no-op.\\nRatings and comments: any signed-in user can comment on your listing; star ratings are reserved for verified-email accounts in an organization that actually installed it. The publishing organization can never rate or comment on its own listing.' },\n { path: '/developers/plugins/publishing/publisher-handbook', title: 'Publisher handbook', heading: 'Getting paid', anchor: '#getting-paid', text: 'One-time prices in whole USD: $0 (free), or $3 to $1000. Purchases flow through the platform\\'s Stripe; your share (80%, or 70% on free plans) pays out via your Connect account. The Marketplace → Sales tab tracks every sale.\\nYour share is calculated on the pre-tax price. Buyers also pay sales tax on top of it, which Aglyn collects and remits under its marketplace-provider registration — that money is never yours and is not part of your payout. The Sales tab reports net paid out, the amount that reached your Stripe account, with what buyers paid, the tax and the platform fee shown beside it.\\nRefunds and lost chargebacks are excluded from that total: your share is pulled back from your Connect account, and the amount returned is listed separately.\\nThe $3 minimum on paid listings {#low-prices-and-processing}\\nA paid listing must be $3 or more. Type less than that into any publish form and the price field turns red, the hint says why, the publish button holds, and the publish route refuses it — the same figure in all four places.\\nThe reason is the payment processor\\'s per-transaction cost: a percentage of what the buyer pays (the price plus sales tax) plus a fixed amount per transaction. That fixed amount is what makes small prices uneconomic — it does not shrink with the price, while the platform\\'s 20% cut does. At $1 processing the payment costs about $0.36, more than the $0.20 platform fee, so the sale loses money for the platform that has to settle it.\\nThe minimum is not a round number picked for effect: it is the cheapest whole dollar at which the platform\\'s cut covers the cost of collecting it, computed at the 20% take rate — the band that breaks even latest, so the number holds whichever plan you are on — and against the most expensive payment method a buyer may choose.' },\n { path: '/developers/plugins/reference/extension-points', title: 'Extension-point catalog', heading: '', anchor: '', text: 'Extension-point catalog\\nThe surface matrix: what a plugin can extend, from which entry (barrel = client, /server = API process), and when it runs.\\n| Extension point | Entry | Reaches | Runs |\\n| Canvas components (defineUiFeatureBundle) | barrel (site) | Besigner + published sites | Editor gates / SSR-suspended page load |\\n| Console nav + pages (ConsoleExtension.navItems) | barrel (console) | Console host area | After the org resolves, before the shell paints |\\n| Widgets (ConsoleExtension.widgets) | barrel (console) | Named console zones (dashboard, org, billing, team, staff, besigner, the assistant dock) — a widget with a column is a column of a shell-owned table | With their host page |\\n| Providers (ConsoleExtension.providers) | barrel (console) | Around every console page | Once the registry is populated |\\n| Zones a plugin HOSTS (definePluginZone / registerPluginZone) | barrel (console) | A position on a page the plugin owns, with the props it hands each widget carried on the token — so another plugin writes a widget for it without importing the owner | With the page that draws useConsoleWidgetSlot(); the token itself is type-only and ships nothing |\\n| Staff pages (ConsoleExtension.staffPages) | barrel (staff) | The staff area — a tab in the staff strip and a page at /admin/{id} | Once the staff area has loaded its plugins, behind the staff guard |\\n| Site runtimes (registerSiteRuntime) | barrel (site) | Every published page | Client render, reading enricher props |\\n| Redirect resolvers / page resolvers / enrichers | /server | Tenant page pipeline | Per request, in that order; enricher errors isolated |\\n| API routes (registerPluginApiRoute) | /server | /api/ on both apps | Per request behind the org + release gates |\\n| Billing webhook handlers | /server | Platform Stri' },\n { path: '/developers/plugins/reference/injection-zones', title: 'Injection zones', heading: '', anchor: '', text: 'Injection zones\\nRegister a widget with ConsoleExtension.widgets: [{ slot, widgetId, title?, Component }]; the shell renders it through PluginWidgetSlot. The guaranteed zones are the exported CONSOLEWIDGETSLOTS catalog — slot stays an open string so custom zones don\\'t need a core release.\\n| Zone | Where it renders | Props your widget receives |\\n| hostActivity | Host dashboard + screen-view activity column | hostId, targetId?, header?, viewAllHref? |\\n| hostDashboard | Host dashboard glance row, one card per capability | hostId |\\n| orgDashboard | The organization\\'s Sites page, above the site grid — the org-level twin of hostDashboard, rendered only for an org-wide member who may open the org-level CRM | hostId (always null), orgMount, basePath (the org-level hub\\'s path) |\\n| commerceGlance | Host dashboard commerce summary | hostId |\\n| orgData | Organization → Data page body | orgId, org |\\n| besignerFunctions | Besigner ƒx panel | hostId |\\n| marketplaceListing | Marketplace listing detail body | hostId, listingId, permissions |\\n| orgAddons | Plugins & add-ons hub, installs section | hostId (the acting site) |\\n| dashboardFooter | Bottom of the host dashboard | hostId |\\n| orgSettings | Organization → Settings, below the tabs | orgId, org |\\n| hostSettings | Host setup page, below the built-in cards | hostId |\\n| adminOrgDetail | Staff admin org detail page (staff-only) | orgId |\\n| orgBillingUsage | Billing → Usage, below the meters | orgId, org (the billing-merged org doc), canManage |\\n| orgBillingOverview | Billing → Overview, among the plan and add-on cards | orgId, org, plan, canManage |\\n| staffOrg | Staff org page, among its cards (staff-only) | orgId |\\n| staffUser | Staff user page, below the account\\'s activity (staff-only) | uid |\\n| staffOrgsListColumn | A column of the s' },\n { path: '/developers/plugins/reference/injection-zones', title: 'Injection zones', heading: 'Zones a plugin hosts', anchor: '#zones-a-plugin-hosts', text: 'A zone can sit on a plugin\\'s own surface rather than on a console page, such as hostForms on the forms plugin\\'s Forms page, hostAutomations, automationEditor and automationRun on the workflows plugin\\'s Automation page, or recordInsights, recordEmail and importMapping on the CRM plugin\\'s record pages, one-to-one composer and import drawers. A plugin cannot import the console\\'s PluginWidgetSlot, so the shell hands its renderer down: read it with useConsoleWidgetSlot() from @aglyn/aglyn and draw the zone through it.\\nThe renderer is the same gated slot a console page mounts, so a widget there passes the same enablement, entitlement and permission gates. Outside the console shell it is null, and the zone draws nothing.' },\n { path: '/developers/plugins/reference/injection-zones', title: 'Injection zones', heading: 'How a zone spaces your widget', anchor: '#how-a-zone-spaces-your-widget', text: 'Most zones are a stack. The shell draws their widgets one under another, with the same gap the page puts between its own cards, and keeps that gap between the zone and the page\\'s cards beside it. Render your card with no outer margin: the zone spaces it, and a margin on your widget\\'s root is reset.\\nThe other zones hand each widget to a layout the page draws itself, and the page spaces it there:\\n- hostDashboard, commerceGlance and orgDashboard: a tile of a dashboard grid.\\n- hostScreens, hostTemplates, hostLayouts, hostForms, hostComponents and besignerToolbar: a control in a row.\\n- hostAutomations, automationEditor and automationRun: a control the workflows plugin places beside its Actions buttons, in an automation\\'s editor, and on a failed run.\\n- besignerInspector and seoFields: a section among a panel\\'s own fields.\\n- productEditor, productsHub and productImport: a section the commerce plugin places among its product editor\\'s fields, above its catalog table, and in its CSV import dialog.\\n- recordEmail and importMapping: a section the CRM plugin places under its one-to-one composer\\'s message and under an import drawer\\'s column matching.\\n- besignerFunctions, orgData, orgMarketplace, orgAddons and marketplaceListing: the body of a dialog or a page.\\n- assistPanel: a floating dock.\\n- orgMembersListColumn, staffOrgsListColumn and staffOrgUsageColumn: a column of a table, or, on staffOrgUsageColumn, a line above it.\\nA widget that renders nothing leaves no gap in either kind of zone.' },\n { path: '/developers/plugins/reference/injection-zones', title: 'Injection zones', heading: 'Staff zones', anchor: '#staff-zones', text: 'adminOrgDetail, staffOrg, staffUser, staffOrgsListColumn and staffOrgUsageColumn are on the staff pages, which name no workspace: a staff page is about an org or an account, not about the reader\\'s own. So these zones do not read an org\\'s enabled plugins. The staff area loads every plugin whose plugins.config.json entry names a staff register surface (see the manifest), before any staff page renders, and a staff zone renders those plugins\\' widgets. A widget\\'s featureFlag and permission are not consulted there: both are answers about a workspace, and the staff area\\'s guard admits the reader. A plugin with a widget on a staff zone and no staff surface is never loaded on the staff pages, so its widget never renders.' },\n { path: '/developers/plugins/reference/injection-zones', title: 'Injection zones', heading: 'Column zones', anchor: '#column-zones', text: 'A zone documented as a column (orgMembersListColumn and staffOrgsListColumn, and hostMembers and staffOrgUsageColumn when you want a column rather than a card) takes a widget with a column:\\nThe table draws the header and mounts your component once per row with the row beside the zone\\'s props; sortKey names the row field a sortable table orders by (the two member tables render in fetch order today and carry it for the ones that will). A widget on a column zone without a column is not a column and renders nothing there — register a card on a card zone instead. On a zone that takes both, a column widget is drawn only in the table, never among the cards.' },\n { path: '/developers/plugins/reference/injection-zones', title: 'Injection zones', heading: '`widgetId` is a persisted identifier', anchor: '#widgetid-is-a-persisted-identifier', text: 'On the host dashboard a person chooses which cards they keep and in what order, and that choice is stored by widgetId. Give a retired id to a different card and a returning reader gets an arrangement they never made — a card they never hid, hidden. Retire an id by leaving it reserved and minting a new one; never reuse it.\\nGive every dashboard widget a title, matching the heading the card itself renders. It is the name beside the switch that controls the card, and the two sit a click apart. Without one the shell falls back to the extension\\'s displayName, which reads correctly for a plugin contributing one card and ambiguously for one contributing several.\\nThe reader\\'s choice is applied strictly after enablement and entitlement and can only subtract: a widget the workspace is not entitled to stays absent however the stored preference is written, and a widget no stored preference mentions renders. Nothing a plugin declares participates in that decision.' },\n { path: '/developers/plugins/reference/manifest-and-envs', title: 'Manifests, trust lifecycle & environment', heading: '', anchor: '', text: 'Manifests, trust lifecycle & environment' },\n { path: '/developers/plugins/reference/manifest-and-envs', title: 'Manifests, trust lifecycle & environment', heading: 'Plugin manifest (published with every version)', anchor: '#plugin-manifest-published-with-every-version', text: 'Validation is server-side (validatePluginManifest) — invalid manifests never publish. capabilities are enforced by the sandbox tier: the plugin origin stamps a CSP from network, the bridge drops undeclared props and events.\\ncontributes — where the plugin loads\\nA plugin loads only where something uses it. Installing one loads nothing: the platform reads contributes to decide which published pages and which console screens fetch your bundle, and it never runs your code to find out.\\n| Key | What it declares | Where the plugin then loads |\\n| site.components | The canvas component ids register() registers | A published page whose node tree places one of them |\\n| site.features | The runtimeId of each site runtime it mounts | Every page of a site that has the plugin switched on |\\n| console.slots | The widget slots it fills, including the panels the shell draws as slots (assistPanel, besignerInspector) | A console screen that renders one of those slots |\\n| console.routes / console.orgRoutes | The console routes its pages serve (/my-plugin) | The screens under those routes |\\n| console.shell | It adds a nav tab, a provider or a staff page, which the shell draws on every screen | Every screen of the workspace |\\nThe declaration is the whole contract, so it has to be complete:\\n- The verifier compares it with your bundle. verify-plugin-bundle.mjs and the publish API read what register() registers (a widget\\'s slot, a nav item\\'s href, a runtime\\'s runtimeId, a component\\'s $id) and refuse a registration the declaration omits: a slot you forget to declare is a slot whose screens never load your plugin.\\n- A new version must declare. A bundle whose register() registers anything and whose manifest has no contributes fails publishing, with the declaration the verifier read printed for you to ' },\n { path: '/developers/plugins/reference/manifest-and-envs', title: 'Manifests, trust lifecycle & environment', heading: 'Listing & version documents', anchor: '#listing--version-documents', text: '- marketplaceListings/{listingId} — public: displayName, description, categories[] (fixed taxonomy), logoUrl, screenshots[], readme (markdown), homepageUrl, repositoryUrl, license, priceUsd, latestVersion, installCount (cumulative installs ever), activeInstalls (installs live right now — the detail page shows both, e.g. 41 installs · 12 active), reviewStatus.\\n- marketplaceListings/{id}/pluginVersions/{version} — server-only: sha256, objectPath, manifest, changelog, trust?, signature?. The buyer-safe subset (version/changelog/trust/hostAbi/date) is exposed by GET /api/marketplace/listing-versions.\\n- Installs pin {version, sha256} at hosts/{hostId}/installs/{listingId} (host tier) or orgs/{orgId}/installs/{listingId} (org tier). Artifacts are immutable content-addressed objects: artifacts/{listingId}/{version}/{sha256}.bundle.' },\n { path: '/developers/plugins/reference/manifest-and-envs', title: 'Manifests, trust lifecycle & environment', heading: 'Review & trust lifecycle', anchor: '#review--trust-lifecycle', text: '- listed/verified (or legacy/absent) plugin listings appear in browse; everything else is owner-and-staff-only. Verified adds the ✅ badge.\\n- Realm trust is separate and orthogonal: a super-staff signature over a version\\'s sha256 (POST /api/admin/sign-plugin) sets trust: \\'realm\\', letting that version load into the app realm. The revocations/{listingId} kill switch beats everything.' },\n { path: '/developers/plugins/reference/manifest-and-envs', title: 'Manifests, trust lifecycle & environment', heading: 'Environment variables', anchor: '#environment-variables', text: '| Variable | Runtime | Purpose |\\n| NEXTPUBLICPLUGINORIGIN | client + server | Dedicated plugin origin: serves /load (sandbox) and /artifacts/... (realm fetches) |\\n| PLUGINARTIFACTSBASE | server | Optional server-side artifacts base override |\\n| PLUGINARTIFACTSBUCKET | console server | Isolated bucket the publish flow writes to |\\n| NEXTPUBLICPLUGINTRUSTPUBLICKEY | client | Ed25519 public key (base64 raw); when set, client realm loads require valid signatures |\\n| PLUGINTRUSTPUBLICKEY | server | Same key, server loaders (mandatory there) |\\n| PLUGINTRUSTPRIVATEKEY | console server only | Signing key (base64 PKCS8 DER) for the staff sign-plugin route |\\n| PLUGINREMOTESERVER | server | enabled turns on remote server bundles (default off everywhere) |\\n| PLUGINREMOTESERVERBUNDLES | server | Per-deploy listingId@version allowlist |\\n| PLUGINJOBSSECRET | tenant server | Shared secret the scheduler sends to /api/plugins/run-jobs |\\n| NEXTPUBLICPLUGINDEVBUNDLES | client, dev only | Unverified localhost bundle loading for the authoring loop; dead code in production builds |' },\n { path: '/developers/plugins/reference/manifest-and-envs', title: 'Manifests, trust lifecycle & environment', heading: '`plugins.config.json` (first-party contributors)', anchor: '#pluginsconfigjson-first-party-contributors', text: 'The single source mapping plugin ids to packages, register entry points per surface (site, console, staff, tenantApi, consoleApi, the declaration surfaces declarations, serverDeclarations and consoleServerDeclarations, and subprocessors), apiPrefixes, and activityMutationPaths — the plugin\\'s modules that create, transfer or destroy a durable customer object, which check-activity-coverage.mjs holds to writing an activity entry.\\nAn entry may also name modules: the subpath a surface registers from, when that surface\\'s code must not ride with the package root\\'s. The loaders read a loaded module\\'s register function by name, so a bundler keeps everything the module exports; a plugin with both a site and a console surface therefore gives the site surface its own module (\"modules\": { \"site\": \"site\" } loads @aglyn/plugins-forms/site), and a published page that uses the plugin fetches the canvas half without the console registrar.\\nEvery entry also carries contributes, the same declaration a marketplace manifest carries (above), and it is required: the generator refuses a plugin without a valid one, and apps/console/specs/plugin-contributions-declared.spec.ts runs every registrar and fails when what it registers differs from what the entry declares. node tools/scripts/generate-plugin-manifests.mjs turns it into the four generated loader manifests, the three declarations manifests and the subprocessors manifest — the only files allowed to reference @aglyn/plugins- outside libs/plugins (an nx boundary rule enforces this), and every reference is an import() so the apps never depend on a plugin statically.\\nA declarations entry (@aglyn/plugins-x/declarations, client and server) is a light module that registers what core must know before any surface of the plugin has loaded — billing an' },\n { path: '/developers/plugins/reference/plugin-config', title: 'Plugin configuration', heading: '', anchor: '', text: 'Plugin configuration\\nA plugin declares the settings it takes; Aglyn stores the answers and renders the form. You write no settings UI, and you never invent your own inheritance — a setting can be answered at three levels, and the platform resolves them the same way for every plugin.' },\n { path: '/developers/plugins/reference/plugin-config', title: 'Plugin configuration', heading: 'The three layers', anchor: '#layers', text: '| Layer | Where it lives | Who sets it |\\n| Schema defaults | Your code | You, the plugin author |\\n| Workspace value | orgs/{orgId}/pluginSettings/{pluginId} | An org manager, in Plugins & add-ons |\\n| Site override | hosts/{hostId}/pluginSettings/{pluginId} | A site admin, in that site\\'s plugin settings |\\nEach layer narrows the one before it, per key. A workspace sets a value once and every site it enabled the plugin on follows it; a site that needs a different answer overrides that one field and keeps inheriting the rest. One booking horizon across a chain, with the flagship branch taking bookings further out, is the shape this exists for.\\nOnly the keys the site document actually holds are overrides. The site document is not a full copy of the config — it is a sparse patch.' },\n { path: '/developers/plugins/reference/plugin-config', title: 'Plugin configuration', heading: 'Declare a schema', anchor: '#declare', text: 'There is no manifest field for this. A schema is registered in code, at module scope, from a file imported by both your client barrel and your /server entry — so whichever surface loads first, the schema is there.\\nRegistration is idempotent and keyed by pluginId; registering again replaces the schema.\\nField types {#field-types}\\n| type | Editor | Coercion |\\n| string | Text field | Anything that is not a string falls back. An empty string is a valid value and is kept. |\\n| number | Number field | Must be a finite number, then clamped to min / max — a value outside the range is pulled to the bound, not rejected. |\\n| boolean | Switch | Must be an actual boolean. \"true\" is not one. |\\n| select | Dropdown | Must match one of options[].value. |\\nlabel is required and description is optional help text under the field.\\nvalidate is a cross-field hook run against the merged values before a save. Return an error string to block the save, or null to allow it.\\nValues are not trusted The settings documents are client-writable by org managers and site admins, so every read coerces defensively. A value of the wrong type falls back rather than reaching your code — and keys your schema does not declare are dropped entirely, so a field left behind by an older version of your plugin cannot resurface as config.' },\n { path: '/developers/plugins/reference/plugin-config', title: 'Plugin configuration', heading: 'Read the config', anchor: '#read', text: 'On the server, from a handler:\\nPass hostId whenever you have one. Without it you get the workspace\\'s answer and the site\\'s override is silently ignored — which is worse than not having the feature: the console shows the number the operator typed while your handler keeps using the workspace\\'s, a disagreement with no error and no surface that shows both sides. The site read is conditional and runs alongside the org read, so passing it costs no extra round trip.\\nIn the console or another client surface:\\nready is false until every document it needs has answered. Do not read config before then; it is the schema defaults, which is a plausible-looking wrong answer.\\nWith no registered schema {#no-schema}\\nBoth readers degrade rather than failing: you get the raw stored documents with the site\\'s spread over the workspace\\'s, key for key, and no coercion. That keeps a handler working when its schema module has not loaded, but it is not the behavior to design against.' },\n { path: '/developers/plugins/reference/plugin-config', title: 'Plugin configuration', heading: 'Resolution, exactly', anchor: '#resolution', text: '1. Start from schema.defaults.\\n2. Apply the workspace document, for declared keys only.\\n3. Apply the site document, for the keys it actually holds.\\n4. Coerce once, at the end.\\nStep 4 is the subtle one. Because coercion happens last, a site override of the wrong type falls back to the workspace value it was trying to replace, not to the schema default. The other way round, one malformed site setting would silently discard a workspace answer the operator can see in their own console.\\nA key is an override when it is present on the site document. undefined counts as absent — a form that writes { key: undefined } to mean \"stop overriding\" is doing the same thing as one that never wrote the key, and treating the two differently would make \"revert to the workspace value\" depend on which code path cleared it.\\nClearing an override means deleting the field setDoc(..., { merge: true }) leaves an existing field exactly as it is when the new object omits it. A form that drops empty inputs therefore cannot clear anything by saving — the override survives, invisibly, and the site keeps ignoring a workspace value the operator has since changed.\\nDeleting the field is the only write that returns a key to inherited. The console\\'s own settings form does this behind its Inherited control.' },\n { path: '/developers/plugins/reference/plugin-config', title: 'Plugin configuration', heading: 'What the console renders', anchor: '#console-ui', text: 'The Plugins & add-ons hub builds a form from your schema, so a plugin gets a settings UI without writing one. At site scope the same form marks each field Set for this site or Inherited, and offers a control to drop the override.\\nWho may write which document is enforced by security rules, not by the form: the workspace document takes an org manager, the site document takes a site admin. Writes to either are frozen while the organization is suspended or the site\\'s writes are frozen.\\nNever put a secret in plugin config There is no secret field type and no redaction. Both documents are read client-side — the workspace document by any member of the organization — so anything stored here should be assumed visible to everyone in the workspace and present in the browser. API keys, tokens and signing secrets belong in the environment; see Manifests & environment.' },\n { path: '/developers/plugins/reference/plugin-config', title: 'Plugin configuration', heading: 'API summary', anchor: '#api', text: '| Symbol | From | Purpose |\\n| registerPluginConfigSchema(schema) | @aglyn/aglyn | Declare the settings. Idempotent per pluginId. |\\n| getPluginConfigSchema(pluginId) | @aglyn/aglyn | The registered schema, or undefined. |\\n| listPluginConfigSchemas() | @aglyn/aglyn | Every registered schema — what the console\\'s hub enumerates. |\\n| mergePluginConfig(schema, stored) | @aglyn/aglyn | Defaults merged with one document, coerced. |\\n| resolvePluginConfig(schema, { org, host }) | @aglyn/aglyn | The full three-layer resolution. |\\n| pluginConfigOverrides(schema, host) | @aglyn/aglyn | Which keys a site is answering for itself. |\\n| validatePluginConfigValues(schema, values) | @aglyn/aglyn | Pre-save check: coerce, then run validate. |\\n| getPluginConfig(orgId, pluginId, { hostId }) | @aglyn/aglyn/server | The resolved config for a request. |\\n| usePluginConfig(orgId, pluginId) | client | Workspace-scope resolved config. |\\n| useSitePluginConfig(orgId, hostId, pluginId) | client | Site-scope resolved config, plus overrides. |' },\n { path: '/developers/plugins/reference/plugin-config', title: 'Plugin configuration', heading: 'Related', anchor: '#related', text: '- Plugin-manager API reference\\n- Manifests, trust lifecycle & environment\\n- Building feature plugins' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: '', anchor: '', text: 'Plugin-manager API reference\\nEverything a plugin registers goes through libs/aglyn/src/lib/plugin-manager, re-exported from @aglyn/aglyn (client + isomorphic) and @aglyn/aglyn/server (adds the server-only loaders). Hand-written by design: the surface is small and curated, and each entry needs semantics (ordering, caching, failure behavior) that generated signatures can\\'t carry.' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Console extensions — `feature-plugins`', anchor: '#console-extensions--feature-plugins', text: '| API | What it does |\\n| registerConsoleExtension(extension) | Declares everything a plugin adds to the console shell. Idempotent by pluginId (re-registration replaces). |\\n| listConsoleNavItems() / resolveConsolePluginPage(href) | How the shell renders nav + serves plugin pages under /[orgSlug]/hosts/[host]/[...pluginSlug]. The resolver matches an exact href, or a declared section beneath one — longest href wins, prefixes match on a segment boundary, and a tie between two enabled plugins refuses. It answers { extension, navItem, section?, segments }. |\\n| listConsoleOrgNavItems() / resolveConsoleOrgPluginPage(href) | The same pair for organization-level surfaces — the extension\\'s orgNavItems, listed on the organization\\'s tab strip and served under /[orgSlug]/[...pluginSlug] with the same matching rules. Neither pair reads the other\\'s list. |\\n| listConsoleWidgets(slot) | Widgets registered for a named zone — see Injection zones. |\\n| listConsoleStaffPages() / resolveConsoleStaffPage(id) | How the shell draws a plugin\\'s staff pages: a tab after the staff strip\\'s own, and a page at /admin/{id} from the generic staff route. Two plugins claiming one id resolve to nothing, and say so. |\\n| listConsoleProviders() | App-level providers mounted around every console page. |\\n| defineUiFeatureBundle(options, components) | Site/canvas component bundle; auto-depends on the base mui bundle. Component and bundle ids are persisted in screen docs — never rename. |\\n| CONSOLEWIDGETSLOTS | The typed injection-zone catalog. A widget with a column: { header, sortKey?, align? } is a column of a shell-owned table on the zones documented as column zones. |\\nConsoleExtension fields: pluginId, displayName, featureFlag? (plan-entitlement gate the shell applies — extensions cannot bypass plans), permiss' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Loading — `plugin-loader`', anchor: '#loading--plugin-loader', text: '| API | Semantics |\\n| createPluginLoader(manifest) | One loader per generated manifest; loads are cached per plugin, registrations per plugin+surface+use. |\\n| loader.ensure(ids, surfaces, use?) | Loads + registers the given plugins\\' surfaces. Returns a stable promise per (ids, surfaces, use) so React use() can suspend on it during SSR — the canvas never renders against an empty registry. Unknown ids are ignored (marketplace realm plugins load separately); alwaysOn entries activate regardless. |\\n| loader.ensureAll(surfaces) | Every manifest plugin — the API dispatchers\\' lazy-load-all. |\\n| loader.pluginIdForApiPath(path) | Prefix-map fallback for the per-request org gate. |\\nLifecycle: every module in an ensure batch is fetched at once, then the register fns run one at a time in manifest order and each is awaited, then each module\\'s optional bootstrap () export runs (manifest order, once per plugin+surface, failures logged not fatal) — the sanctioned place for cross-plugin wiring. Plugins loaded by a later ensure bootstrap in that batch, so read registries lazily rather than snapshotting.\\nuse — what the surface actually uses (AGL-3141). { componentIds } as the page computed it, handed to every register fn unchanged. A plugin that can register a part of itself reads it and imports only those modules; one that cannot ignores it. Omit it and every plugin registers all of itself, which is what the console and the besigner want — their palette shows every element — and what any surface that cannot say what it places MUST do: an element whose component never registered renders nothing, silently. Registration is bookkept per plugin+surface+use, so a second page that places a component the first did not still reaches the plugin.' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Server APIs — `api-plugins` (`/server` only)', anchor: '#server-apis--api-plugins-server-only', text: '| API | Semantics |\\n| registerPluginApiRoute(path, handler, options?) | Registers a path under the [...pluginApi] dispatchers. Ownership is recorded at registration time for the per-request org gate — a disabled plugin\\'s paths 404 for that workspace. path may carry :name segments (ai/jobs/:jobId/cancel); an exact registration wins over a pattern. options.subject names the organization (and, for a signed tokenless redirect, the account) a request is for when it names no site — see Naming the subject. |\\n| resolvePluginApiRequestSubject(path, request) | What a dispatcher asks before its release gate when no hostId was named: the declared resolver\\'s answer, read from a clone, or null for an undeclared route, a resolver that threw, or ids that are not plain path segments. |\\n| options.recipientLink / isPluginRecipientLinkRoute(path) | A route that answers a link the platform mailed to somebody — an unsubscribe in a List-Unsubscribe header. Both dispatchers skip their per-site enablement and release gates for it and nothing else: lockdown and the rate limit still apply. An opt-out has to keep working after its plugin is paused for the workspace (CAN-SPAM holds it open for thirty days after the send), so the route authenticates the link itself, by a signature it verifies. |\\n| handler as (req, res) or { web } | The node shape takes PluginApiRequest / PluginApiResponse. The Web shape, { web: (request, { params }) => Response }, takes the dispatcher\\'s own Request and answers a Response — the form for a door that streams (server-sent events, a chat answer) or reads the raw body itself; params carries the path segments and every :name filled. |\\n| PluginApiRequest | { method, query, body, headers, rawBody? } — rawBody carries the unparsed payload for Stripe/Svix signature verificatio' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Site pipeline — `site-runtime`, `site-page-hooks` (`/server` for hooks)', anchor: '#site-pipeline--site-runtime-site-page-hooks-server-for-hooks', text: '| API | Semantics |\\n| registerSiteRuntime({runtimeId, Component}) | Components rendered on every published page (overlay engines, experiment runners); they read back the props their server enricher wrote. |\\n| registerSiteRedirectResolver(fn) | Runs before route resolution; first non-null redirect wins. |\\n| registerSitePageResolver(fn) | Composes plugin-owned pages (commerce PDP/PLP). |\\n| registerSitePageEnricher(fn) | Contributes page-prop slices to every page that renders nodes — published screens, collection routes, designed auth screens and a resolver\\'s own page alike; a resolver\\'s keys win, and pageData merges per plugin. Gated screens (password-protected, members-only) enrich behind the gate and deliver the slice with their nodes. The designed 404 body sets pathUnknown — it is cached per host, so contribute only what does not depend on a path and never substitute one. Maintenance, lockdown and bandwidth-containment notices are not enriched. Enricher errors are isolated — a broken plugin drops its slice, never the page. |' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Stylesheets — `plugin-styles`', anchor: '#stylesheets--plugin-styles', text: 'Raw CSS your plugin ships, routed so the besigner canvas resolves it exactly as a published page does.\\n| API | Semantics |\\n| registerPluginStyles({pluginId, styleId?, css}) | Registers (or replaces) one stylesheet. Rendered as a plain at the site-content root of every surface — published tenant, editor Preview, and inside the besigner canvas\\'s shadow root. |\\n| unregisterPluginStyles(pluginId, styleId?) | Drops one sheet, or all of a plugin\\'s. |\\n| listPluginStyles() / subscribeToPluginStyles(fn) | The registry, and a change subscription. |\\n| capturePluginStyles(pluginId, load) | Used by the realm loader; see below. |\\nWhy you cannot just write to document.head. You can, and on a published page it works — but the besigner canvas renders site content inside a closed shadow root, which a document-level rule never reaches. Measured: the same rule that beats every MUI declaration on the published page has no effect at all on the canvas. So a plugin that styles itself that way looks one way in the editor and another way live.\\nloadRealmPlugins therefore wraps each bundle\\'s module evaluation and register() in capturePluginStyles, which picks up any the bundle appends to document.head — the shape import \\'./styles.css\\' compiles to in every bundler — and mirrors it into the canvas. The original element is left where the bundle put it, so CSS you ship for a console surface keeps working. Anything injected outside that window (lazily, or from a ) is not mirrored: call registerPluginStyles for those.\\nCascade position. Plugin CSS is deliberately unlayered, while all MUI and author sx output sits in @layer mui. An unlayered normal declaration beats every layered one regardless of specificity, so a one-tag selector of yours outranks a MUI component default — by design, and identically on ' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Billing — `billing-webhook-hooks` (`/server`)', anchor: '#billing--billing-webhook-hooks-server', text: 'registerBillingWebhookHandler(eventTypePrefix, handler) — receives the platform Stripe events. Handler errors propagate to a 500; make handlers idempotent. Handlers run sequentially with no error isolation, deliberately: isolating them would trade a duplicated side effect for a dropped one, which is the worse trade on a money path.\\nA handler may return { claimed: true } to tell the platform it recognized the event as its own — it found the order, the purchase, the booking. Returning nothing keeps the previous meaning (\"not mine\"), so existing handlers need no change. Every registered handler still runs after one has claimed; the claim is a report, never a dispatch rule.\\nClaim your chargebacks The one event where this matters today is charge.dispute.. A dispute carries no metadata, so plugins self-select by joining on the payment intent — and a plugin that finds nothing looks identical to a plugin that failed. The console route raises a staff alert for any dispute nothing claimed, because that is money moving with no record of it anywhere. If your plugin owns disputes, claim the ones it handles, or every one of them will be reported as a platform fault.\\nA 500 does not always mean a redelivery A throw that reaches the route after any dispatch has begun makes the platform hold its Stripe-event idempotency claim instead of releasing it, so the redelivery short-circuits as a duplicate and your handler is not run again — the failure is recorded on the claim and escalated to staff to reconcile by hand. Releasing the claim would let a redelivery re-apply whatever the failed dispatch had already committed (an inventory decrement, a gift-card balance, a coupon redemption), and those cannot be un-applied.\\nA throw raised before any dispatch began still releases the claim and is red' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Service contracts — `plugin-services`', anchor: '#service-contracts--plugin-services', text: 'The typed registry one plugin opens for others to fill. registerBillingWebhookHandler and registerSiteRuntime are the precedent — one registry per seam, filled at registration, read lazily — and this is that shape with the implementation TYPE carried on a token, so a seam a plugin invents needs no core module.\\n| API | Semantics |\\n| definePluginServiceContract (id, { multiple }) | Declares a contract token. multiple: true is a set every implementation joins; false is a slot one plugin holds. Idempotent by id; redefining with a different multiple throws. |\\n| registerPluginService(contract, impl, { pluginId?, priority? }) | Registers an implementation. Owner = the loader\\'s marker inside a register fn, else pluginId (module scope runs before the marker is set); no owner throws. An unknown contract throws. The same plugin re-registering replaces its entry; on a single contract a different plugin throws naming both, and the incumbent keeps serving. |\\n| resolvePluginServices(contract) / resolvePluginService(contract) | Every implementation, highest priority first and registration order within a priority — or the first one. |\\n| hasPluginService(contract) / unregisterPluginServices(pluginId) / resetPluginServicesForTests() | Presence, a plugin unloading, and the spec reset. |\\nWorked example — the AI plugin declares its provider contract and a marketplace plugin adds a provider:' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Zones a plugin hosts — `plugin-zones`', anchor: '#zones-a-plugin-hosts--plugin-zones', text: 'CONSOLEWIDGETSLOTS is the catalog of zones the console SHELL draws, and a slot id has always been an open string — so a plugin could already host a zone of its own on a page it owns. What it could not do is say what that zone HANDS a widget. A zone token carries the props type, so the plugin declares both.\\n| API | Semantics |\\n| definePluginZone (id) | The token. Pure — it registers nothing and reads nothing — so a plugin may define it at module scope and register it from its register fn. |\\n| registerPluginZone({ zone, label, surface, description?, layout? }, { pluginId? }) | Publishes it. Owner = the loader\\'s marker inside a register fn, else pluginId; no owner throws, and a zone id another plugin declared throws naming both while the incumbent keeps its zone. The same plugin re-declaring replaces its own. |\\n| layout | stack (the default): the zone is one block of the page, its widgets are cards, and it takes no room when nothing drew. bare: the zone adds no element, because each widget is one item of a layout the HOST draws — a button in a row of actions, a control beside a form\\'s fields, a dialog that portals out. The shell reads it for a plugin-hosted zone; the core catalog\\'s zones keep the layouts the console gives them. |\\n| listPluginZones() / pluginZone(id) / pluginIdForZone(id) | Every declared zone with its owner, one by id, and the owner alone. |\\n| PluginZoneProps | The props type, off the token. |\\nA token ships nothing. It is { id }, and props never holds a value — the props are carried at the type level and erased. That is load-bearing, not tidy: the zone catalog is on the published page\\'s static graph, so a declaration that put prop SHAPES there would make every published page carry the console\\'s vocabulary. Import this module by its own subpath (@aglyn/agly' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Host subcollections — `plugin-host-collections`', anchor: '#host-subcollections--plugin-host-collections', text: 'A plugin writes ordinary documents under hosts/{hostId}, and three core surfaces have to know which plugin owns which: the media-usage scan, a reference row\\'s deep link, and the site\\'s artifact counters.\\n| API | Semantics |\\n| registerPluginHostCollections(collections, { pluginId? }) | One collection has one owner: a name another plugin declared throws naming both, and refuses the WHOLE call — half a plugin\\'s storage map landing is worse than none. Re-registering replaces that plugin\\'s own. |\\n| mediaScan | generic (the default) flattens each document and searches it; own means a pass that knows its shape already reads it; none needs mediaScanReason, and a declaration without one is refused. |\\n| pluginHostCollectionsScannedGenerically() / pluginHostCollectionsExcludedFromMediaScan() | What the scan reads, and what it skips with the reason given. |\\n| pluginHostCollectionRouteSlug(name) / pluginHostCollectionLabel(name) | A reference row\\'s deep link and its wording. The label is derived from the name when the owner declares none (productCategories → \"Product category\"), so a new collection reads correctly with no second list. |\\n| pluginHostArtifactCollections() | The collections whose documents a site counts beside its screens, layouts and components. |\\n| listPluginHostCollections() / pluginHostCollection(name) / pluginIdForHostCollection(name) | Everything declared, one by name, and its owner. |\\nThe default is to scan, and a declaration says only why it should not be. That is inverted on purpose: \"does this carry media?\" needs a judgment about a schema nobody wrote down, and a collection nobody has thought about is READ, so a plugin shipping a media-bearing collection is covered the day it lands. A declaration names no FIELDS — the scan flattens generically, and a field li' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Record addresses — `plugin-record-routes`', anchor: '#record-addresses--plugin-record-routes', text: 'Where a plugin\\'s records are read, published by the plugin that owns them, so no other surface spells its URLs. Keyed by RECORD KIND, like plugin-record-facts and plugin-record-timeline: one word answers what a record is, what happened on it, and where a person reads it.\\n| API | Semantics |\\n| registerPluginRecordRoute(kind, route, { pluginId? }) | A kind another plugin publishes throws naming both; the incumbent keeps serving, and the owner re-registering replaces its own. |\\n| pluginRecordHref(kind, context, id) / pluginRecordListHref(kind, context) | One record\\'s page, and the list it lives on. |\\n| pluginRecordByEmailHref(kind, context, email) / pluginRecordFilteredHref(kind, context, filter, value) | The optional halves: a list opened on one person\\'s address, and a list narrowed by one of the OWNER\\'s filters. A filter the owner does not know answers null rather than a list that ignores the narrowing. |\\n| pluginRecordRoute(kind) / listPluginRecordRouteKinds() | The route with its owner, and every published kind. |\\ncontext is { orgSlug, host } — the two route params, both already in the URL the calling surface renders on, so a link costs no document read. host: null asks for the organization-level address. Every answer can be null: no plugin publishes the kind, or the owner has no address at that scope. Render text, which is what these surfaces already do while their route params settle. A link is not access: the page at the far end applies its own gates.' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Record cards — `plugin-record-cards` (`/server`)', anchor: '#record-cards--plugin-record-cards-server', text: 'What a plugin\\'s record looks like in one line and one image, published by the plugin that owns it, for a surface that has to DRAW the record rather than link to it — a designed email that features a product, a picker over what another plugin keeps. Keyed by record kind, like the facts reader, the timeline and the route.\\n| API | Semantics |\\n| registerPluginRecordCardReader(kind, reader, { pluginId? }) | A kind another plugin publishes throws naming both; the incumbent keeps serving, and the owner re-registering replaces its own. |\\n| readPluginRecordCard(kind, { hostId, id }) | { title, caption?, imageUrl?, path? }, or null when no plugin publishes the kind or the record is gone. A caller treats both alike: there is nothing to draw. |\\n| pluginRecordCardReader(kind) / listPluginRecordCardKinds() | The reader with its owner, and every published kind. |\\nA card carries nothing a caller could compute on. The caption is a string the owner has already worded — a price, a date, a status — so the rule behind it lives in one place. A caller that read the owner\\'s document and imported its model to agree is the coupling this replaces.\\nServer-side and unauthenticated, on purpose. A reader runs with the Admin SDK for a caller that has already decided the read is the workspace\\'s to make. plugin-record-facts answers one MEMBER and applies their permissions; the two are separate contracts so neither borrows the other\\'s trust. Both apps load every plugin\\'s server entry before a plugin handler runs, so a reader registered from a server registrar is there wherever a handler asks. Import it by its own subpath (@aglyn/aglyn/plugin-manager/plugin-record-cards).' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'The tenant\\'s tax rule — `plugin-tax-profile` (`/server`)', anchor: '#the-tenants-tax-rule--plugin-tax-profile-server', text: 'More than one plugin takes money, and a merchant has one tax profile. The plugin that keeps it answers what a flat rate adds to a charge and which regime a settled payment was taxed under; any other plugin that charges asks here instead of importing the owner\\'s model.\\n| API | Semantics |\\n| registerPluginTaxProfile(profile, { pluginId? }) | A slot: a workspace has one tax profile, so a second plugin\\'s is refused and the incumbent keeps serving. |\\n| pluginTaxProfile() | The rule — and it throws when no plugin registered one. |\\n| flatTax(rate, chargeCents, fallbackLabel) | { taxCents, label, pct }, exclusive and rounded to the cent. rate is the merchant\\'s stored setting passed as read; an absent, zero, negative or out-of-range one answers all-zero and never throws. |\\n| taxModeOf(settledPayment, manualTaxCents?) | The regime as the owner records it. manualTaxCents is tax the caller added as a line of its own, which the processor reports as none. |\\n| pluginTaxProfileOwner() | The owner\\'s plugin id, or null — for a caller that only wants to know who it is. |\\nNo profile is a refusal, never a zero. Every other seam here answers null for \"nobody home\", and this one must not: a caller that read null as \"no tax\" would charge an untaxed total and record it as untaxed, and the merchant would owe the difference. A refused sale is seen the same day. It cannot happen in a working build — both apps load every plugin\\'s server entry before a plugin handler, a cron or the billing webhook runs — and tax-profile-is-registered.spec.ts in each app runs the real registrars and holds the real rule, because a plugin\\'s own spec may not import the owner.' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Contact capture — `plugin-contact-capture` (`/server`)', anchor: '#contact-capture--plugin-contact-capture-server', text: 'A silo that meets a person — a form submission, a member sign-up, an order, a booking — hands them to whichever plugin keeps people, instead of importing its writer.\\n| API | Semantics |\\n| registerPluginContactCaptureWriter(writer, { pluginId? }) | A slot: a workspace keeps one set of people, so a second plugin\\'s writer throws naming both and the incumbent keeps serving. |\\n| capturePluginContact(request) | { ok: true, contactId, created } — created: false is a returning visit — or { ok: false, reason, error }, or null when no plugin keeps people. null and a refusal are different answers: null is a workspace with no record system, where nothing has gone wrong. |\\n| registerPluginContactSource({ source, label, openLabel?, recordKind? }, { pluginId? }) | One source word has one owner. openLabel is what a link from a timeline entry says, and a door with nothing to open declares none. recordKind is the kind plugin-record-routes addresses the silo\\'s own document by. |\\n| pluginContactCaptureWriter() / listPluginContactSources() / pluginContactSource(source) | The writer with its owner, every declared door, and one by its word. |\\nA refusal is returned, never thrown. Each of these silos has already accepted the submission or taken the money, so a throw would cost it the thing it was recording. reason is invalid-email, band, erased or error, and error is customer-safe.\\nprofile is the owner\\'s own field names with scalar values, not a core type. A person\\'s record shape belongs to the plugin that models it. So does everything else the owner decides: normalizing and keying the address, new person or returning visit, what a stage, an owner, a company or a tag means, and what a new person sets off. The silo reports what it saw. Like plugin-record-timeline, the registry authenticates nobo' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Record timeline — `plugin-record-timeline` (`/server`)', anchor: '#record-timeline--plugin-record-timeline-server', text: 'A plugin that sends mail or books meetings files what happened on the contact, company or deal it happened with — an email sent, a reply read, a task for a person — without writing the record system\\'s documents or importing its plugin. The record system registers one writer (a slot), and a caller asks for it:\\n| API | Semantics |\\n| registerPluginRecordTimelineWriter(writer, { pluginId? }) | The record system\\'s writer. A single-implementation contract: a second plugin\\'s writer throws naming both, and the incumbent keeps serving. The CRM registers it from its console API surface. |\\n| pluginRecordTimelineWriter() | The writer with its owner, or null when no plugin keeps records — a caller then files nothing. |\\n| writer.logActivity(request) | An activity on a record, scoped like one a member made on hostId. An email is filed ONCE per Message-ID, under the id the capture address files a copy of the same message by; any other kind once per the caller\\'s dedupeKey. |\\n| writer.createTask(request) | A task for assigneeUid, due at dueAtMs, filed once per dedupeKey. |\\nBoth answer { ok: true, id, created } — created: false for an entry a previous call, or a copy through another door, already filed — or a refusal ({ ok: false, status, error }: no record system on the plan, a record at its activity ceiling), and never throw for a refusal. The registry authenticates nobody: the caller has already decided the entry is the workspace\\'s to write.' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Media delivery — `media-delivery-provider` (`/server`)', anchor: '#media-delivery--media-delivery-provider-server', text: 'A slot one plugin holds (core.media-delivery, built on the service contracts above): an object store with its own edge, which serves library video in place of the platform\\'s media route. Core keeps every access decision and the copy rules; the provider stores copies and mints URLs. Register it from a serverDeclarations entry, so both apps hold it before the first request.\\n| API | Semantics |\\n| registerMediaDeliveryProvider(provider, { pluginId? }) | Fills the slot. The same plugin registering again replaces its provider; a second plugin throws naming both. |\\n| mediaDeliveryProvider(capability) | The provider when it is configured for \\'store\\' (copies) or \\'deliver\\' (URLs), else null. Synchronous and free of I/O, so a hot path asks it first. |\\nA provider implements isConfigured(capability), putObject({ key, body, contentLength, contentType }), deleteObject(key), deleteObjectsWithPrefix(prefix) and deliveryUrl({ key, expiresAtMs, claims }). Keys come from core and carry the content hash they were copied from; claims carry the org, site, scope and media id a URL was minted for, and grant nothing. A video is served from the provider only when the releasevideodelivery flag, off by default, is on for its org and a copy of its current bytes exists; everything else serves from the platform exactly as before.' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Platform events — `plugin-events` (`/server`)', anchor: '#platform-events--plugin-events-server', text: 'Core raises the events; a plugin that must react to what a core route did subscribes from its serverDeclarations entry, so the subscription is in place before the first request. Payloads carry the actor and the before / after, never a reference to the plugin.\\n| Event | Raised by | Payload |\\n| org.seatAddons.changed | the add-on checkout and the billing webhook | { orgId, actor, before, after } — the org.seatAddons maps |\\n| org.permissions.changed | the member, role and host-member routes | { orgId, actor, subject: { type, id?, name? }, permission, granted } |\\n| billing.invoice.paid | the billing webhook, once the invoice resolves to a workspace | { orgId, invoiceId, amountPaidCents, currency, paidOutOfBand, metadata } |\\n| billing.invoice.failed | the billing webhook | { orgId, invoiceId, amountDueCents, metadata } |\\n| billing.invoice.closed | the billing webhook, on voided and on markeduncollectible | { orgId, invoiceId, reason: \\'voided\\' \\\\| \\'uncollectible\\', metadata } |\\n| billing.dispute.opened | the billing webhook, on a dispute that matched a workspace | { orgId, chargeId, invoiceId, amountCents } |\\n| billing.paymentMethod.changed | the billing webhook, after reading the customer\\'s default | { orgId, defaultType } |\\nThe five billing events carry the Stripe object\\'s own metadata, which core does not read. A plugin that stamped its own id on an invoice it created recognizes its own by it; every other handler ignores the event. They are awaited in the route rather than deferred, because a plugin\\'s decision about whether a workspace may keep spending must not lag the payment that settled it.' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'A meter line a plugin bills itself — `plugin-metered-lines` (`/server`)', anchor: '#a-meter-line-a-plugin-bills-itself--plugin-metered-lines-server', text: 'The monthly usage sweep prices several lines into one figure and posts it as a single Stripe meter event. A plugin that bills one of those lines its own way claims it, and the sweep leaves it out of the metered figure from the month the claim names — so one month\\'s usage reaches exactly one invoice.\\n| API | Semantics |\\n| registerPluginMeteredLine({ lineId, billsFrom, closeMonth?, pluginId? }) | Claims a line. billsFrom() answers the first YYYY-MM the plugin bills, or null while it is registered and not yet switched on; it is called per question, so a deployment change takes effect without a restart. One plugin per line — a second claimant throws. |\\n| pluginBillsMeteredLine(lineId, month) | What the sweep asks. false for an unclaimed line, a month before the claim, an unparseable start month, and a claim that throws — so every failure bills through the sweep, which already works. |\\n| runPluginMeteredLineClose(lineId, context) | Run by the sweep once a month has CLOSED, per workspace, with { orgId, month, org, stripeCustomerId } — the remainder of a line charged as it accrues is owed whether or not the meter reported. Errors are logged, never the sweep\\'s. |\\n| pluginMeteredLineOwner(lineId) | The claiming plugin, for diagnostics. |\\nOnly the BILLING moves. A claimed line is still measured, still priced and still written to the month\\'s audit fields, so a month\\'s usage history reads the same either way and the handover is countable from the rows.\\n| API | Semantics |\\n| registerPluginEventHandler(event, handler, { pluginId? }) | Subscribes; attributed to the registering plugin. Idempotence is the subscriber\\'s to keep — check listPluginEventHandlers(event) before subscribing again after a registry reset. |\\n| runPluginEventHandlers(event, payload) | What the core route calls afte' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Account erasure — `plugin-user-erasure` (`/server`)', anchor: '#account-erasure--plugin-user-erasure-server', text: 'The account erasure deletes what core stores about a person. A plugin that keeps data about a person where core\\'s deletes do not reach — documents under an org keyed by the uid, a collection carrying the uid as a field — registers an eraser from its serverDeclarations entry:\\n| API | Semantics |\\n| registerPluginUserEraser(eraser, { pluginId? }) | One eraser per plugin, attributed like an event handler; registering again replaces it in place. Check listPluginUserErasers() before registering again after a registry reset. |\\n| runPluginUserErasers({ uid, orgIds }) | What the erasure calls once the person\\'s memberships are removed: every eraser in registration order, with every workspace the person belonged to. Answers each plugin\\'s report by plugin id, or null for an eraser that threw — logged, and never the erasure\\'s failure. |\\nA report is counts and flags (Record ), never the erased content: it lands in the erasure\\'s audit record, which outlives the data. null in a field is a figure the eraser could not measure, and a null report says the plugin\\'s data may remain — neither is zero.' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Workspace erasure — `plugin-org-erasure` (`/server`)', anchor: '#workspace-erasure--plugin-org-erasure-server', text: 'The workspace erasure destroys the organization\\'s document tree, its sites and the top-level records keyed to it by a field. A plugin that holds something those deletes cannot finish on their own — a grant at a provider only the plugin can revoke, a record outside every path and field the erasure sweeps — registers an eraser from its declarations:\\n| API | Semantics |\\n| registerPluginOrgEraser(eraser, { pluginId? }) | One eraser per plugin, attributed like an event handler; registering again replaces it in place. Check listPluginOrgErasers() before registering again after a registry reset. |\\n| runPluginOrgErasers({ orgId, dryRun }) | What the erasure calls BEFORE it deletes anything of its own, so an eraser that acts through a record — opening a stored grant to revoke it — finds the record there. Every eraser in registration order; answers each plugin\\'s report by plugin id, or null for an eraser that threw — logged, and never the erasure\\'s failure. |\\nThe report lands under plugins on the erasure\\'s result and audit record, on the same terms as an account erasure\\'s. A plan (dryRun: true) is handed to every eraser, which then touches no provider and writes nothing: it counts, and a figure it did not measure is null. An eraser that opens a credential only the console holds registers from consoleServerDeclarations, which the tenant runtime never loads.' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Person erasure — `plugin-person-erasure` (`/server`)', anchor: '#person-erasure--plugin-person-erasure-server', text: 'The person erasure removes one person from one workspace: the contact, what the CRM files beside it, and the leads, list memberships and delivery log under the address. A plugin that keeps records about such a person under the organization — keyed by the contact or by the address — registers an eraser from its declarations:\\n| API | Semantics |\\n| registerPluginPersonEraser(eraser, { pluginId? }) | One eraser per plugin; registering again replaces it in place. |\\n| runPluginPersonErasers({ orgId, email, key, contactIds, dryRun }) | What the erasure calls after every site\\'s suppression row is written and before the contacts are deleted. Every eraser in registration order; each plugin\\'s report by plugin id, or null for an eraser that threw — logged, and never the erasure\\'s failure. |\\nkey is personKey(email): how every suppression list names the person without the address. A record that the person asked not to be contacted, keyed that way, is kept — the promise outlives the data — with anything that could identify the person removed from it. A dry run counts and writes nothing. Reports land under plugins on the erasure\\'s counts and audit row.' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Console jobs — `plugin-console-crons` (`/server`)', anchor: '#console-jobs--plugin-console-crons-server', text: 'A plugin whose scheduled work needs what only the console holds — a provider key, a sealed grant to a person\\'s mailbox — declares a job from its consoleServerDeclarations, with the body imported when it first runs:\\n| API | Semantics |\\n| registerPluginConsoleCron(job, { pluginId? }) | Declares a job. Its id starts with the plugin\\'s own (acme-mail-…); a platform job\\'s id, another plugin\\'s, a job with no label or no drives sentence is refused. The same plugin declaring the id again replaces it. |\\n| runPluginConsoleCrons({ nowMs, deadlineMs, jobIds?, beat }) | What POST /api/admin/plugin-crons calls every fifteen minutes (the console\\'s consoleFastCrons tick, on the console\\'s CRONSECRET): every job — or the one a manual run names — concurrently, each after its own platformCronBeats mark. A job that throws is null in the answer and the route answers 207. |\\n| pluginConsoleCronScheduledJobs() | Each job as a row of /api/health/crons, judged against the fifteen-minute tick with a 45-minute grace, beside the runner\\'s own plugin-console-crons row. |\\nA job reports counts and flags, never content, and starts no new unit of work after deadlineMs. A job that needs a slower cadence keeps its own last-run mark and returns at once. Adding a job needs no route, no scheduler change and no function deploy.' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Usage alerts — `plugin-manager/usage-alert-contributors`', anchor: '#usage-alerts--plugin-managerusage-alert-contributors', text: 'The usage-alerts sweep walks every org once, reads its usage, and sends core\\'s own alerts: the plan quotas, the customer\\'s budget and the free plan\\'s bandwidth cap. A plugin that meters a cost or enforces a ceiling core knows nothing about adds staff alerts to the same sweep by registering a contributor from its serverDeclarations entry, importing the rule itself lazily. Import the registry by its subpath; it is not on a barrel.\\n| API | Semantics |\\n| registerUsageAlertContributor(contributor) | Idempotent per plugin and id: the same pair again replaces the earlier contributor in place. A contributor with no plugin id, no id or no evaluate throws. |\\n| listUsageAlertContributors() | What the sweep runs for each org, after the budget alert and before the bandwidth cap: FIRSTPARTYPLUGINS catalog order, then any other plugin id, then registration order within a plugin. |\\n| context.recordAlert(key, threshold) | Records the dedupe guard and answers whether the alert may be sent. On an org\\'s first, silent evaluation it records the guard and answers false. Guard keys share one map with core\\'s checks, so name yours for what it measures. |\\n| context.alertStaff(alert) | The sweep\\'s own sender: the staff bell, the staff inbox with the same words, and a row in the run\\'s report. It sends nothing on an org\\'s first, silent evaluation. |\\n| context.org / spend / guards / month | The org document, its spend this month, its guard map as read, and the month the run dedupes against. A contributor never writes a guard itself. |\\nContributors run one at a time and are isolated: a throw is logged against the contributor, a guard it recorded for an alert it never delivered is dropped so the next sweep tries again, and the next contributor runs.' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Activity actions — `plugin-activity-actions`', anchor: '#activity-actions--plugin-activity-actions', text: 'A plugin whose activity rows are read by more than a person stores a CODE (ai.job.output) and declares what it means:\\n| API | Semantics |\\n| registerPluginActivityActions({ pluginId, group, actions }) | Idempotent per plugin; a code another plugin declared refuses the registration. Declare it from both entries, in code that is sure to run: the register function your declarations entry calls, or the top level of a file your package.json lists in sideEffects. |\\n| pluginActivityActionLabel(action) | What a reader sees for a code — activityActionLabel in the presenter reads it, so every feed, table and card shows the same words. |\\n| listPluginActivityFilters() | One chip per group with the codes it keeps: the org feed and the actor table draw their chips from this, and send action isAnyOf …. |\\n| pluginStaffAuditActionGroup(action) / pluginStaffAuditActionGroupLabel(group) | The staff audit facet\\'s grouping: a registered group by code or by staffAuditPrefixes, else the action\\'s leading namespace. |\\n| isPluginStaffAuditAccess(action) | Whether a staff audit action is one a plugin\\'s group names in staffAuditAccessActions — the actions its staff doors write when staff READ something rather than change it. The audit log files them as accesses. Matched exactly. |\\nAn org activity row names its target, { type, id, name }. Core\\'s target types are core\\'s own resources; a plugin files a row about one of ITS resources under its own namespace, pluginId:noun (PluginActivityTargetType, e.g. outreach:mailbox), which the org log accepts without naming the plugin. The feed labels it by the noun (Mailbox), prefers the target\\'s name wherever it has one, and links it nowhere.' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Billing and access keys — `plugin-entitlements`', anchor: '#billing-and-access-keys--plugin-entitlements', text: 'A plugin that sells something, gates something or can be paused in an incident declares the KEYS; the numbers stay where they are checked (PLANPRICING for a price, reconciled by check-pricing-drift).\\n| API | Semantics |\\n| registerPluginEntitlements(registration) | Idempotent per plugin. A seat add-on, lockdown or catalog permission key another plugin owns refuses the registration. Permissions are forwarded to registerPluginPermissions with the owner filled in. |\\n| listPluginOrgPermissions() | The keys plugins add to the org permission catalog, in catalog order, with their owner. ORGPERMISSIONS and ORGPERMISSIONKEYS list them after the core keys and are kept in step in place, so a module that imported them first still reads them. resolveOrgPermissions layers a declared key like a core one — role default, custom role, per-member override — the role editor lists it, and a member, role or collaborator write raises org.permissions.changed once per declared key it moved (pluginPermissionChanges). A declaration naming a core key is refused. |\\n| hostRoleDefaults on a catalog key | Makes the key per-site for a site collaborator: resolveCollaboratorHostPermissions and resolveMemberHostPermissions decide it from the host role, refined by the per-site toggle on the member document, and projectHostMemberPermissions stamps the site\\'s memberPermissions projection. On the server, memberHasPermissionOnHost, permissionRefusal and setHostPermissions are a door\\'s rung, its 403 and the toggle write. |\\n| listPluginSeatAddons() / pluginSeatAddon(key) | What resolveOrgEntitlements folds: the quota named gains perUnitByPlan[plan] × units and the features switch on, after the org\\'s overrides and before nothing. pluginSeatAddonUnits(org.seatAddons, key) / hasPluginSeatAddon(org, key) in plan-enti' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Typed entitlement keys — `plugin-entitlement-keys`', anchor: '#typed-entitlement-keys--plugin-entitlement-keys', text: 'The TYPE half of the keys above. registerPluginEntitlements lets a plugin REGISTER a key; this is what makes the key part of OrgEntitlements and OrgFeatureFlags without the core listing it.\\nFrom then on entitlements.bottlesPerHost type-checks everywhere OrgEntitlements is read, keyof OrgFeatureFlags admits cellarTastings, and nothing in the core names a bottle.\\n| API | Semantics |\\n| PluginEntitlementQuotas / PluginEntitlementFeatures | Empty interfaces to augment with declare module. Declare each key OPTIONAL — an entitlement is resolved and may be absent, like every key the core declares. |\\n| registerPluginEntitlementKeys({ pluginId, keys }) | The runtime twin: who owns a key, and what it means. Idempotent per plugin; a key another plugin declared refuses the whole registration, naming both. |\\n| listPluginEntitlementKeys() / pluginEntitlementKey(key) / pluginIdForEntitlementKey(key) / listPluginEntitlementKeysOfKind(kind) | Everything declared, one key, its owner, and the quota or feature half. |\\n| unregisterPluginEntitlementKeys(pluginId) / resetPluginEntitlementKeysForTests() | A bundle unloading, and the spec reset. |\\nRequired is over the CORE halves. CoreOrgEntitlements and CoreOrgFeatureFlags are what PLANENTITLEMENTS declares and what ResolvedOrgEntitlements keeps exhaustive — a plan that forgets a platform quota or gate does not compile. A plugin\\'s band comes from its own seat add-on declaration instead, so it joins as declared: present, and optional, because a workspace without the plugin has no value for it.\\nNo prices here. A seat add-on\\'s price stays a PLANPRICING row that the Stripe wiring reads and check-pricing-drift reconciles. Two places holding a number are two places that can disagree about what a customer is charged.\\nThe organization DOCUMENT does not' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Enablement, flags, config, fields, permissions, jobs', anchor: '#enablement-flags-config-fields-permissions-jobs', text: '| API | Semantics |\\n| resolveEnabledPlugins(org) | The org switchboard: absent field → all first-party; alwaysOn and alwaysOnForWorkspace ids unioned in, so no stored list can switch them off for a workspace; unknown (marketplace) ids kept. |\\n| resolveHostEnabledPlugins(org, host) / isHostPluginEnabled(org, host, id) | One site\\'s set: the org\\'s set minus the host\\'s disabledPlugins deny-list, minus the default-off ids it has not opted into. Only alwaysOn ids (the base component library) survive the deny-list; an alwaysOnForWorkspace plugin (AI) is switched off for a site like any other. An absent host field means on. Forms is alwaysOnForWorkspace too, and core asks this about FORMSPLUGINID wherever a form\\'s server half lives: the submit route, the publish-time contract check and the published render. |\\n| isLockedOnForWorkspace(id) / isLockedOnForSite(id) | Whether a switch is inert: the workspace switch for alwaysOn and alwaysOnForWorkspace plugins, the site switch for alwaysOn alone. A catalog entry\\'s siteOff carries the copy the site page shows beside the switch — what switching it off stops, and what it keeps running — and siteOff.confirm makes the site switch ask first, naming the published pages its siteOff.pages sentences describe. |\\n| EnabledPluginsContext / isSwitchedOffForRenderedSite(pluginId, ids) | The rendered site\\'s plugin set, published by the host app. The node renderer draws a registered component whose FIRST-PARTY plugin is not in it exactly as it draws an unregistered one, so a server holding a bundle another site loaded renders the same page the site\\'s browser does. |\\n| filterPluginsByReleaseFlags(ids, isFlagOn, {staffBypass}) | Subtracts release-flagged-off first-party plugins (AGL-422). |\\n| registerPluginConfigSchema(schema) / mergePluginConfig / re' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Remote bundles — `realm-plugins` (isomorphic), `realm-server` (`/server`)', anchor: '#remote-bundles--realm-plugins-isomorphic-realm-server-server', text: '| API | Semantics |\\n| PLUGINHOSTABIVERSION / setRealmPluginHost(host) | The AGLYNPLUGINHOST ABI slot; the app composes it from its own React/jsxRuntime/registry singletons. |\\n| verifyRealmBundle(bytes, install, publicKey?) | sha256 pin always; Ed25519 signature mandatory when a key is configured (fails closed). |\\n| loadRealmPlugins(installs, {artifactsBase, publicKeyBase64}) | Fetch → verify → blob-URL import → register(host). Cached per listing@version; ABI mismatches refused; per-bundle failures logged and skipped. |\\n| loadRemoteServerBundles(source) | Env-gated server tier (default OFF); returns what loaded so callers can audit. |\\n| isCompatibleHostAbi(hostAbi?) | The ABI gate: undeclared = legacy (allowed with a warning). |' },\n { path: '/developers/plugins/reference/plugin-manager-api', title: 'Plugin-manager API reference', heading: 'Sandbox — `plugin-bridge`', anchor: '#sandbox--plugin-bridge', text: 'The versioned postMessage protocol between the host PluginFrame and a sandboxed bundle: parseGuestMessage (origin/source/schema-validated), filterPluginProps (manifest allowlist), message types ready/init/ props/resize/event/fetch-request/fetch-response/error. The bridge never evals or grafts anything from the frame — sized output and named events only; network goes through the host-mediated fetch (server-side allowlist re-check).' },\n { path: '/developers/plugins/reference/sandbox-security', title: 'Sandbox security model', heading: '', anchor: '', text: 'Sandbox security model\\nMarketplace plugins run sandboxed by default: in an iframe on a separate origin, with a Content-Security-Policy built from your own manifest. This page is what that means in practice, because most of it only becomes visible when something you wrote doesn\\'t work.\\nThe alternative tier — realm bundles, which run in the host realm — is covered in Realm bundles.' },\n { path: '/developers/plugins/reference/sandbox-security', title: 'Sandbox security model', heading: 'A separate origin', anchor: '#a-separate-origin', text: 'Sandbox iframes are served from plugins.aglyn.com, not from the console or the site. That\\'s a different origin, so the browser itself — not our code — is what stops a plugin from touching the host page\\'s DOM, cookies, or storage.\\nEverything a sandboxed plugin does to the outside world goes through the bridge: declared props in, declared events out. Undeclared props are dropped before your code sees them, and undeclared events are dropped before the host does.\\nThe loader only allows framing from the console, .aglyn.app sites, and each site\\'s verified custom domain — so your plugin renders on a customer\\'s own domain exactly as it does on their aglyn.app address.' },\n { path: '/developers/plugins/reference/sandbox-security', title: 'Sandbox security model', heading: 'Per-manifest network policy', anchor: '#per-manifest-network-policy', text: 'This is the one that surprises people.\\nThe loader stamps a CSP whose connect-src is \\'self\\' plus exactly the origins you declared in your manifest\\'s capabilities.network. A fetch to anywhere else is blocked by the browser.\\nWith that manifest, fetch(\\'https://api.example.com/…\\') works and fetch(\\'https://api.other.com/…\\') does not.\\nThe failure mode is a CSP error, not a 4xx A blocked request never reaches the network. You\\'ll see a CSP violation in the browser console and a rejected promise — not an HTTP status. If a request \"just fails\" with no response, check your manifest\\'s network list first.\\nIf the manifest lookup fails for any reason, the loader falls back to \\'self\\' — the strict end. It never fails open.\\nWhen you can\\'t declare the origin\\nDeclaring every origin up front doesn\\'t fit every case — a URL the site owner configures, or an API that redirects across hosts. For those, use the host-mediated hostFetch escape hatch: the request is made by the host, subject to its own rules, and the response is handed back over the bridge. See Server APIs.' },\n { path: '/developers/plugins/reference/sandbox-security', title: 'Sandbox security model', heading: 'Pinned, immutable artifacts', anchor: '#pinned-immutable-artifacts', text: 'Bundles are streamed from a private bucket at /artifacts/{listing}/{version}/{sha}.bundle, and every consumer verifies the sha256 before running a byte of it.\\nInstalls pin {version, sha256}, so:\\n- You can never change the code an existing install runs. Ship a new version.\\n- A tampered artifact fails the hash check and doesn\\'t execute.\\n- \"Update to vX\" is always an explicit act by the person who installed it.' },\n { path: '/developers/plugins/reference/sandbox-security', title: 'Sandbox security model', heading: 'What this means when you build', anchor: '#what-this-means-when-you-build', text: '- Declare network honestly and minimally. It\\'s both your allowlist and a disclosure buyers read — an over-broad list costs you trust in review.\\n- Test in the sandbox, not just locally. A plugin that works against a dev server with no CSP will hit the policy the moment it\\'s published.\\n- Document data & permissions in your README. Unverified sandbox listings show buyers a risk disclaimer; clear docs are what overcomes it.\\n- Don\\'t reach for realm trust to dodge the CSP. Realm trust is granted by Aglyn staff for a specific reviewed version, not on request.' },\n { path: '/developers/plugins/reference/sandbox-security', title: 'Sandbox security model', heading: 'Related', anchor: '#related', text: '- Manifest & environments — the capabilities block.\\n- Realm bundles — the trusted tier and how trust is granted.\\n- Publisher handbook — review expectations.' },\n { path: '/developers/self-hosting', title: 'Self-hosting', heading: '', anchor: '', text: 'Self-hosting Aglyn\\nAglyn is Apache-2.0 and the whole platform is public on GitHub. You can run it yourself with Docker on any infrastructure — a cloud VM, a VPS, or bare metal.\\nWhat self-hosting v1 means You run the same code Aglyn\\'s cloud runs, pointed at your own Firebase project (Google account required — the free tier is fine to start) plus your own Stripe and Resend keys. A fully vendor-neutral backend that removes the Firebase dependency is a separate roadmap track.' },\n { path: '/developers/self-hosting', title: 'Self-hosting', heading: 'The short version', anchor: '#the-short-version', text: 'There is no image to pull — you build it Aglyn publishes no Docker images, on any registry. docker compose up\\n--build from a clone is the only supported path, and that is deliberate rather than pending: Next inlines every NEXTPUBLIC value into the client bundles when the image is built, and this deployment has 27 of them — your Firebase client config, your console URL and tenant apex, your brand name, and the operator and DMCA-agent details shown on your public abuse intake. An image built by someone else would ship their answers to all of those inside your bundles. If you find something claiming Aglyn offers prebuilt images, it is wrong.\\nGive Docker more memory than it starts with Docker Desktop allocates 2 CPUs and 4 GB by default on macOS and Windows, and the Next production build does not fit in it — it is killed by the out-of-memory killer about ninety seconds in, with a message that never mentions memory:\\nRaise it to 8 GB or more under Settings → Resources before you build. If you cannot, build the two images one at a time (docker compose build console, then docker compose build tenant) — up --build builds them concurrently and roughly doubles peak demand. Capping Node\\'s heap does not help: Next builds with Turbopack, which allocates outside the heap --max-old-space-size bounds. Measured: 4 GB fails, 12 GB builds both in about six minutes (AGL-2437).\\n- Console (the management app) on http://localhost:4200\\n- Tenant runtime on http://localhost:4500 — a specific site is served at .localhost:4500; the bare port serves the demo tenant.\\nDon\\'t skip any of the three A stack that boots without the rules deployed is a stack whose data is not protected the way the application assumes. The rules scripts talk to Firebase\\'s API directly with your service-account credentials — n' },\n { path: '/developers/self-hosting', title: 'Self-hosting', heading: 'The full runbook', anchor: '#the-full-runbook', text: 'Every variable this deployment reads — what it drives, where to get its value, what shape it takes, and whether changing it needs a rebuild — is in Environment variables. Keep it open beside this page while you fill in .env.selfhost.\\nThe complete, always-current runbook lives in the repository: docs/SELFHOSTING.md. It covers:\\n1. Creating your Firebase project (Auth, Firestore, Storage, Realtime Database, and Remote Config) and minting a service account.\\n2. Deploying the repo\\'s security rules to your project with the bundled scripts.\\n3. Filling in .env.selfhost — which values are baked into the client at image-build time versus read at runtime.\\n4. Reverse-proxy setup for the console and the tenant runtime.' },\n { path: '/developers/self-hosting', title: 'Self-hosting', heading: 'Who runs this install', anchor: '#who-runs-this-install', text: 'Two values are not optional in the way the keys below are, because leaving them out changes what your users and third parties are told about who is responsible for your site:\\nThey name you on the public abuse intake (/api/report-abuse), the §512 counter-notice intake (/api/counter-notice), the lockdown 503, the media quarantine notice and the sanctions 451. These pages ship with the software and the first two are unauthenticated — a copyright holder or a browser vendor can reach them without an account.\\nThere is deliberately no fallback to Aglyn\\'s addresses. Earlier builds hardcoded them, which meant a self-hosted deployment published a DMCA intake directing statutory notices to Aglyn — about content Aglyn does not host and cannot remove. Unset now renders an explicit \"not configured\" state instead.\\nBecause these are NEXTPUBLIC, they are compiled into the client bundles. Set them before docker compose build, not just before up.\\nYour DMCA position is your own\\nThe counter-notice flow, the 10–14 business-day put-back clock and the repeat-infringer strike ledger all work on your install. Aglyn\\'s designated agent registration does not extend to you: §512(c)(2) makes registering an agent with the U.S. Copyright Office a precondition of the safe harbor rather than a formality.\\nSo NEXTPUBLICOPERATORDMCAAGENT defaults to unset, unset means the product claims nothing, and NEXTPUBLICOPERATORDMCAAGENTREGISTERED must be exactly true before anything states a registration. Naming an agent never implies one.\\nRegistering is only half the duty. §512(c)(2) also requires the agent\\'s name, address, phone number and email to be available to the public through your service — a separate obligation the Copyright Office filing does not discharge. Set all four (NAME, ADDRESS, PHONE, EMAIL) and th' },\n { path: '/developers/self-hosting', title: 'Self-hosting', heading: 'Which addresses this install calls its own', anchor: '#addresses', text: 'The operator identity above says who you are. This section is about where your deployment lives, and it is the group most often left at its default — with the default being one of Aglyn\\'s own hostnames.\\nNEXTPUBLICTENANTDOMAIN — set this one first {#tenant-domain}\\nThe apex your sites\\' assigned subdomains hang off. A site whose subdomain is acme is served at acme. .\\nUnset, it is aglyn.app — Aglyn\\'s cloud. This is the single most consequential value on the page, because the damage is not confined to your own console. The same constant is what every published site uses to describe itself to the outside world:\\n- on every page\\n- /api/sitemap and /api/robots\\n- item links in /api/collections-rss\\n- /api/manifest\\n- og:image\\n- the origin an inbox-bound in email resolves against\\nLeft at the default, your customers\\' sites tell Google, every feed reader and every inbox that they live at an address you do not control and Aglyn does not serve for you.\\nAGLYNTENANTHOSTCNAME — and its near-twin {#tenant-host-cname}\\nThis is what the tenant runtime matches an incoming Host header against. A request for that name, or for anything under . , resolves to one of your published sites; anything else falls through to the custom-domain path. It has no default.\\nThere is a second, similarly-named variable, and they are not the same one:\\n| Variable | Read by | If unset |\\n| AGLYNTENANTHOSTCNAME | The tenant runtime, to resolve incoming hosts | No apex matches, and every visitor falls through |\\n| NEXTPUBLICAGLYNTENANTHOSTCNAME | The console, as the CNAME target it displays and verifies in the custom-domain wizard | Defaults to sites.aglyn.app |\\n.env.selfhost.example carries both, on adjacent lines. Set both, to the same value. The first is consumed at image-build time, so it has to be in the env file bef' },\n { path: '/developers/self-hosting', title: 'Self-hosting', heading: 'Renaming the product', anchor: '#platform-brand', text: 'NEXTPUBLICPLATFORMBRANDNAME renames the product everywhere it names itself: browser-tab titles, the installable PWA, the relying-party name the operating system shows when someone saves a passkey, transactional email, and the / x-powered-by fingerprint on every site you publish. Unset, it is Aglyn.\\n| Variable | Unset means |\\n| NEXTPUBLICPLATFORMBRANDNAME | Aglyn |\\n| NEXTPUBLICPLATFORMBRANDLEGALNAME | LLC — a US company form, so set it if you are not one |\\n| NEXTPUBLICPLATFORMSUPPORTURL | Falls back to NEXTPUBLICOPERATORSUPPORTEMAIL as a mailto: before it ever falls back to Aglyn\\'s support page |\\nBrand images are not environment variables and are not meant to be: the favicon, app icons and social card are files under apps/console/public/static/images/brand and the matching tenant path. Replace them in your Docker build context.\\nNEXTPUBLIC is baked in at build time Every variable on this page whose name starts with NEXTPUBLIC is compiled into the client bundles when the image is built. Changing one needs docker compose build, not a restart. A restart will appear to do nothing, which is the failure mode worth recognizing.\\nThis is the deployment-level rename, and it is orthogonal to the per-organization white-label feature. The platform brand is what every organization on your install falls back to; white-label is a paid entitlement that lets one organization override it for itself. The two compose: on your deployment, a non-white-label organization renders your brand, not Aglyn\\'s.' },\n { path: '/developers/self-hosting', title: 'Self-hosting', heading: 'Optional keys', anchor: '#optional-keys', text: 'Beyond the Firebase blocks and the addresses above, most of what is left is optional, and a missing key disables its feature rather than breaking the stack. This is the index; Environment variables has the value shape, the default and the symptom for each one.\\n| Feature | Keys |\\n| Billing & commerce checkout | STRIPE — the secret key alone covers storefront checkout; plan checkout also needs the publishable key, the webhook secret and the STRIPEPRICE ids |\\n| Transactional & campaign email | RESENDAPIKEY, USAGEEMAILFROM — the sender address gates all outbound mail, not just usage mail |\\n| One-click unsubscribe | EMAILUNSUBSCRIBESECRET, which falls back to CRONSECRET |\\n| Publishing that takes effect immediately | REVALIDATESECRET — without it a publish reports success and the live page stays stale for up to ten minutes |\\n| AI | a provider key — ANTHROPICAPIKEY, or an OpenAI-compatible endpoint and its key — plus the spend caps beside it |\\n| Scheduled jobs | CRONSECRET — the job routes stay dormant without it. See below for what that silently switches off |\\n| Scheduled plugin jobs | PLUGINJOBSSECRET — without it no scheduled publishing and no booking-hold expiry ever runs |\\n| Customer issue reports | LINEARAPIKEY and LINEARCUSTOMERREPORTSTEAMID — both required. See below |\\n| The every-minute job beat | AGLYNJOBRUNNERURL, only if you deploy cloud/functions. See below |\\n| The console\\'s scheduled sweeps, and AI jobs past their first step | AGLYNCONSOLEURL, only if you deploy cloud/functions. See below |\\nScheduled jobs {#scheduled-jobs}\\nThree separate mechanisms, and missing any of them is quiet rather than loud.\\nCRONSECRET is the shared secret every scheduled route checks. Without it those routes answer 501 and do nothing — including audit archival, erasure runs, retention sw' },\n { path: '/developers/self-hosting', title: 'Self-hosting', heading: 'Publishing your own documentation', anchor: '#docs-build', text: 'apps/docs is a standalone Docusaurus package. It is not part of docker compose; you build and publish it separately if you want your own documentation site. Skip this whole section if you do not.\\nEvery value below is read at build time by docusaurus build, from that build\\'s environment. They are not container runtime environment, and they are not NEXTPUBLIC — Docusaurus is not Next. Set them before the build, not after; the values are baked into the static output.\\n| Variable | Unset means |\\n| DOCSGATRACKINGID | No analytics tag is loaded at all |\\n| DOCSERRORBEACONENDPOINT | The browser error beacon installs no handlers |\\n| DOCSSTATUSTARGETS | /status probes nothing and says so |\\n| DOCSSTATUSFALLBACKURL | /status names no independent monitor to fall back to |\\n| DOCSURL | The canonical origin is https://docs.aglyn.com |\\n| DOCSORGANIZATIONNAME | The footer copyright reads Aglyn LLC, followed by Aglyn\\'s trademark attribution. Setting it replaces the name and drops the attribution — a rebranded build must not claim our marks. |\\nThe rule to hold on to for the first three: unset means off, never ours. They previously defaulted to Aglyn\\'s GA4 property, Aglyn\\'s error collector and Aglyn\\'s production health endpoints — so a published build reported your readers to us and told them our uptime was yours, which is a false all-clear during your own outage.\\nDOCSSTATUSTARGETS is a comma-separated list of name|label|origin|description|path entries. A name and an origin are required; the description may be omitted and an empty label falls back to the name, so console||https://console.example.com is a valid entry:\\nThe fifth field is the health path to probe, and defaults to /api/health. Point a target at a subsystem endpoint when the aggregate is not the signal you want a visitor to see —' },\n { path: '/developers/self-hosting', title: 'Self-hosting', heading: 'Honest limits', anchor: '#honest-limits', text: '| Area | Self-hosted behavior |\\n| Firebase | Required — Auth, Firestore, Storage, RTDB, and Remote Config run in your project. |\\n| Custom-domain self-service | Behind the domain driver. webhook registers a customer\\'s domain automatically through your own proxy\\'s API. wildcard cannot — nobody\\'s wildcard covers somebody else\\'s apex — so it answers 501 and you route the name by hand; the site still serves, because the domain claim is written before the refusal. DNS verification works everywhere. Per-driver detail. |\\n| Custom-domain verification | The verify step requires an exact CNAME match, or an apex address match when the name carries no CNAME at all. There is a soft pass that accepts any CNAME, for local development where no DNS points at a tenant edge. It used to be enabled by the absence of a hosting vendor\\'s environment variable — which a container never sets — so it was on in production on every self-host install, and any domain carrying any CNAME to anywhere verified. A user of your platform could claim a domain they do not control. It now keys on NODEENV, which both Dockerfiles set to production in the image that actually runs. If you run an image built before this fix, upgrade. |\\n| Legal pages & clickwrap | The signup checkbox links Aglyn LLC\\'s Terms and Privacy and records acceptance against Aglyn\\'s document hashes. Nothing breaks, but the agreement is ours, not yours, and is not yet configurable. Replace it before running this for anyone but yourself. |\\n| Marketplace | Visible by default, but backed by Aglyn\\'s Stripe Connect platform. Browsing works; purchase and payout onboarding explain themselves only after a click. Turn releasemarketplace off in Remote Config if you don\\'t want it. |\\n| Wildcard published-site domains | Supported. The tenant runtime resolve' },\n { path: '/developers/self-hosting', title: 'Self-hosting', heading: 'Related', anchor: '#related', text: '- Environment variables — the complete per-variable reference\\n- Domain providers — choosing a driver, the wildcard path end to end, and the webhook contract\\n- White-label\\n- Report an issue\\n- Billing & plans' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: '', anchor: '', text: 'Environment variables\\nThis is the per-variable reference for a self-hosted install. The setup order — Firebase project, security rules, indexes, TTL policies, reverse proxy — is in Self-hosting; read that first and keep this page open beside it while you fill in .env.selfhost.\\nEverything here was read out of the source rather than copied from the template. Where a variable is absent from .env.selfhost.example, this page says so, because the template is what most operators actually fill in.' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Read this before you set anything', anchor: '#build-vs-runtime', text: 'Some of these are frozen when the image is built\\nNext.js replaces the literal text process.env.NAME with its value at build time. It does that for three groups:\\n1. Anything named NEXTPUBLIC, in both server and browser code.\\n2. Anything listed in a next.config.js env block — with-aglyn.nextjs.config.js, apps/console/next.config.js and apps/tenant/next.config.js each declare one.\\n3. Anything reachable from apps/console/middleware.ts, which is compiled into the edge bundle. The edge runtime has no request-time environment at all.\\nFor every variable in those groups, changing the value in .env.selfhost and restarting the container does nothing. There is no error and no warning: the old value is literally compiled into the JavaScript that is running. You have to docker compose build again.\\nOnly the dot form is substituted — the bracket form process.env[\\'NAME\\'] never is — so a variable read that way stays runtime-only even when its neighbors are not.\\nEvery row carries a When column:\\n| When | Meaning |\\n| Runtime | Read on each request or at process start. Change it and restart the container. |\\n| Build | Frozen into the image. Change it and run docker compose build before up. |\\nAnd a Need column:\\n| Need | Meaning |\\n| Required | The deployment does not work without it. |\\n| Feature | One named feature is off without it. The row says which, and what you see. |\\n| Optional | A default applies. The row gives it. |\\n| Aglyn-only | Read by the code but only meaningful on Aglyn\\'s own hosted deployment. Leave unset. |' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Firebase — identity and data', anchor: '#firebase', text: 'Firebase is not optional in self-hosting v1: Auth, Firestore, Storage, Realtime Database and Remote Config are the platform\\'s identity and data layer. Create a project at console.firebase.google.com and enable all of them.\\nClient SDK config {#firebase-client}\\nWhere to get these: Firebase console → Project settings → General → Your apps → add a Web app → copy the firebaseConfig object. Each field below maps to one key of it.\\nEvery one is NEXTPUBLIC, so every one is Build. These end up in the browser bundle by design — Firebase web config is public, and access is controlled by your security rules, not by hiding these values.\\n| Variable | Need | When | Value |\\n| NEXTPUBLICFIREBASEPROJECTID | Required | Build | The project id, e.g. bramble-platform. Also the last of four sources the SAML SSO auth origin is derived from. |\\n| NEXTPUBLICFIREBASEPUBLICAPIKEY | Required | Build | apiKey. Starts AIza…. |\\n| NEXTPUBLICFIREBASEAUTHDOMAIN | Required | Build | authDomain, e.g. bramble-platform.firebaseapp.com. |\\n| NEXTPUBLICFIREBASEDATABASEURL | Required | Build | databaseURL — Realtime Database, not Firestore. Presence and live collaboration use it. |\\n| NEXTPUBLICFIREBASESTORAGEBUCKET | Required | Build | storageBucket, e.g. bramble-platform.firebasestorage.app. Bare bucket name, no gs://. |\\n| NEXTPUBLICFIREBASEMESSAGINGSENDERID | Required | Build | messagingSenderId — a numeric string. |\\n| NEXTPUBLICFIREBASEAPPID | Required | Build | appId, e.g. 1:123456789012:web:abc123def456. |\\n| NEXTPUBLICFIREBASEMEASUREMENTID | Optional | Build | measurementId, G-XXXXXXXXXX, present only if you enabled Google Analytics on the Firebase project. Unset, Firebase Analytics is not configured and server-side conversion events carry no GA client id, so a purchase cannot be stitched back to the browser ' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'The addresses this install calls its own', anchor: '#addresses', text: 'This is the group most often left at its default, and several of the defaults are Aglyn\\'s addresses. Every one is Build.\\n| Variable | Need | When | Value |\\n| NEXTPUBLICCONSOLEURL | Required | Build | Full origin of your console, no trailing slash: https://console.example.com. Feeds the tenant\\'s fallback redirect, the edit-hint return allowlist, the auth-action link base and the CSP frame-ancestors list. Default https://app.aglyn.com, so unset sends a visitor who lands on an unresolvable host to Aglyn\\'s console and builds password-reset links on it. |\\n| NEXTPUBLICWORKSPACEDOMAIN | Required | Build | The apex organization workspaces hang off, bare: example.com. A workspace acme is advertised at acme.example.com. Point a wildcard .example.com at your console, or that URL does not resolve. |\\n| NEXTPUBLICTENANTDOMAIN | Required | Build | The apex your published sites\\' subdomains hang off, bare: sites.example.com. Defaults to aglyn.app — Aglyn\\'s cloud — so leaving it unset makes your console display and link every one of your sites at an address you do not control, and makes each published site advertise that address as its canonical origin to search engines, feed readers and inboxes. |\\n| NEXTPUBLICAGLYNTENANTHOSTCNAME | Required | Build | The CNAME target your console prints and verifies in the custom-domain wizard. Normally the same value as NEXTPUBLICTENANTDOMAIN. Default sites.aglyn.app. |\\n| AGLYNTENANTHOSTCNAME | Required | Build | The same value again, without the prefix. This is the load-bearing half: the tenant middleware matches the incoming Host: header against it. Leave it blank and nothing matches, so every visitor to every published site is redirected to your console — the deployment looks broken rather than misconfigured. Inlined through apps/tenant/next.config.' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Your reverse proxy, the client IP, and geo', anchor: '#proxy', text: 'X-Forwarded-For: tell the product how many proxies you run {#xff}\\n| Variable | Need | When | Value |\\n| AGLYNTRUSTEDPROXYCOUNT | Recommended | Runtime | How many proxies sit between the internet and the container. One reverse proxy is 1, which is also the default. A CDN in front of your own proxy is 2. 0 means nothing is in front and forwarding headers are ignored entirely. Not a header index — you never have to work out which end of the list to count from. |\\nEvery client address in the product is read through one reader, which takes the hop this number identifies and ignores everything to its left. With N proxies in front, the last N entries of x-forwarded-for were written by your proxies and are the only ones a caller cannot forge; the outermost of them recorded the address it actually saw, which is the visitor.\\nSet this if you run more than one proxy\\nnginx\\'s usual proxysetheader X-Forwarded-For $proxyaddxforwardedfor appends. A request arriving with a header the client typed — X-Forwarded-For: 1.2.3.4 — leaves your proxy as 1.2.3.4, . At the default of one trusted proxy the product reads and the forged value is discarded, which is what you want. Configure 2 when a CDN sits in front, or the reader names your own proxy instead of the visitor — several visitors then share one rate-limit bucket and limits bite sooner than they should.\\nToo high is the safer direction than too low: a chain shorter than the configured depth is clamped to its leftmost entry, which was still written by a proxy you trust.\\nWhat this protects:\\n- Authentication throttles — passkey sign-in (both ceremony steps), password-reset mailbombing, identifier resolution, storefront member login and member recovery.\\n- Provisioning throttles — organization creation (the bot-farm control), site creation, scree' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Secrets this deployment signs with', anchor: '#secrets', text: 'Generate each with openssl rand -hex 32. All are runtime, all are server-only, and none of them may ever be prefixed NEXTPUBLIC.\\n| Variable | Need | When | Value |\\n| TOKENSIGNINGSECRET | Required | Runtime | Signs commerce download links, gift-card links, gated-video streams, media access and edit-hint tokens. Must be identical for console and tenant — compose shares one env file, so it is. The code fails closed: unset, it throws rather than issuing an unsigned link. |\\n| MEMBERSESSIONSECRET | Required | Runtime | Signs the storefront member session cookie (30-day TTL). Unset or empty, a fresh random key is generated per boot, so members are signed out on every container restart and replicas disagree with each other. An empty value is treated as unset, not as a key. |\\n| REVALIDATESECRET | Set it | Runtime | The shared secret the console sends when it asks the tenant runtime to drop a published page from cache. Easy to miss, because its absence is completely silent: unset, the console never sends the request at all and records the reason as not-configured: publishing reports success and the live page keeps serving the old HTML for up to 10 minutes and the old site documents for up to an hour. Set the same value on both containers. |\\n| EMAILUNSUBSCRIBESECRET | Feature | Runtime | HMAC key over {hostId}:{email} for one-click unsubscribe links. Falls back to CRONSECRET; with neither set, campaign sends answer 501 and every unsubscribe link answers 400. |\\n| CRONSECRET | Feature | Runtime | The shared secret every scheduled-job route checks — see Scheduled jobs. It is also the fallback unsubscribe key above, which makes rotating it a two-step operation: rotating it alone permanently breaks the unsubscribe link in every marketing email already delivered. |\\n| PLUGINJOBSSECRET | ' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Stripe', anchor: '#stripe', text: 'Stripe is optional. Without it, commerce checkout and paid platform plans are unavailable and the rest of the platform runs.\\nWhere to get these: dashboard.stripe.com → Developers → API keys for the two keys, Developers → Webhooks for the signing secrets, and Product catalog → each price → its price id.\\n| Variable | Need | When | Value |\\n| STRIPESECRETKEY | Feature | Runtime | sklive… or sktest…. Enough on its own for storefront commerce checkout. |\\n| NEXTPUBLICSTRIPEPUBLISHABLEKEY | Feature | Build | pklive… / pktest…. Needed for platform plan checkout; the secret key alone is not enough. |\\n| STRIPEWEBHOOKSECRET | Feature | Runtime | whsec… for the endpoint you create at https://console.example.com/api/billing/webhook. Without it, webhook-driven state — subscription changes, payment settlement — is never applied: the money moves and your database never hears about it. |\\n| STRIPEWEBHOOKSECRETTEST | Optional | Runtime | A second accepted signing secret so test-mode deliveries verify while live keys are in force. Each is tried in turn; the webhook route answers 501 only when all three secrets are unset. |\\n| STRIPECONNECTWEBHOOKSECRET | Feature | Runtime | whsec… for the Connect destination, which feeds Connect readiness. Unset, those deliveries fail signature verification with 400 behind a green \"Active\" badge in the Stripe dashboard. |\\n| STRIPEWEBHOOKURL | Optional | Runtime | The endpoint URL the billing health check expects to find registered in your Stripe account. Default is Aglyn\\'s URL, so a self-host operator who leaves it unset gets a permanent endpoint-missing red on /api/health/billing. Must match the URL exactly as Stripe stores it. |\\n| STRIPELIVEMODE | Optional | Runtime | Overrides whether this deployment considers itself the live one. Normally inferred from t' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Email', anchor: '#email', text: 'Where to get these: resend.com → API Keys, and Domains to verify the domain you send from.\\nThe shared pool is yours to create, and nothing works until it exists\\nA published site never sends from USAGEEMAILFROM. A site with a sending domain of its own sends as that; every other site sends its transactional mail — receipts, password resets, booking confirmations — from a member of a small shared pool inside your mail apex. Nothing provisions that pool for you.\\nBefore any site sends, create AGLYNTENANTSHAREDPOOLSIZE domains at your mail provider — shared1.{mail apex} through shared{n}, four by default — publish the three records each one issues into your own zone, and verify all of them. Twelve records in total for the default pool, and that number does not grow with sites.\\nSkip this and every site without a domain of its own refuses every message, receipts included, with tenant-identity-unprovisioned. Do not create a domain object for the bare mail apex: nothing sends from it, and the pool members are one label deeper so each one signs for itself.\\n| Variable | Need | When | Value |\\n| RESENDAPIKEY | Feature | Runtime | re…. Without it every outbound send — invites, receipts, password resets, campaigns, security alerts — is an inert no-op. Nothing errors; mail simply does not arrive. |\\n| USAGEEMAILFROM | Feature | Runtime | The verified sender identity for your install\\'s own mail — invites, billing, security alerts, console password resets — and the address the \"is email configured\" check every sender consults reads. A bare address or Bramble , on a domain you verified in Resend. A published site never sends from it, under any configuration: a tenant\\'s list quality must not be charged against the domain your own account mail depends on, so tenant mail resolves its own ident' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Analytics and advertising', anchor: '#analytics', text: 'The advertising ids are Aglyn\\'s own marketing funnel. Leave them unset.\\nNEXTPUBLICADSCONVERSIONID, NEXTPUBLICADSSIGNUPLABEL and NEXTPUBLICADSSUBSCRIBELABEL identify a Google Ads account and two conversion actions inside it, so that Aglyn\\'s own signup and subscribe events reach Aglyn\\'s own advertising account. Both events also hand the tag the account\\'s email address for Google\\'s enhanced conversions: the tag hashes it in the browser before anything is sent, and it goes nowhere when the visitor has not granted advertising, when the ids are unset, or when no tag is loaded.\\nNEXTPUBLICMETAPIXELID, NEXTPUBLICLINKEDINPARTNERID and NEXTPUBLICGTMCONTAINERID are the same kind of value one step further: they build retargeting audiences, so a hardcoded one would put your users into Aglyn\\'s advertising lists — a disclosure you never made about people we have no basis to hold. Your own advertising tags are your own business; set your own ids or leave every one of these blank.\\nNothing is compiled in, and the code fires nothing when they are unset — no tag, no request. The half-configured case is handled too: an id with an empty label would produce a target Google Ads accepts and files against the account\\'s default conversion, so the code returns nothing instead.\\nThere is no default because analytics is permitted to emit on any production build including a self-hosted one. A hardcoded id would have every operator reporting their users\\' signups into Aglyn\\'s ad account. Leave them blank, or set your own.\\n| Variable | Need | When | Value |\\n| NEXTPUBLICADSCONVERSIONID | Aglyn-only / your own | Build | Google Ads conversion id, AW- plus digits. Google Ads → Goals → Conversions → the tag\\'s id. |\\n| NEXTPUBLICADSSIGNUPLABEL | Aglyn-only / your own | Build | The opaque conversion label for the' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'AI', anchor: '#assist', text: 'The AI plugin is provider-generic: a provider adapter registers against the plugin\\'s provider contract, and every AI door — the console Assist panel, the besigner\\'s \"Rewrite with AI\", the generative doors and the jobs — routes through the model catalog rather than naming a vendor. Two adapters ship: anthropic and openai-compatible (any endpoint that speaks the OpenAI chat completions shape). Bring your own key; nothing is compiled in. Without a provider key each door answers 501 and says no AI provider is configured. The console panel is additionally behind the releaseassist flag and generation behind releaseaigenerative, both off by default in Remote Config.\\n| Variable | Need | When | Value |\\n| ANTHROPICAPIKEY | Feature | Runtime | sk-ant-…, from the vendor\\'s console under API keys. The key of the anthropic adapter. |\\n| AIOPENAICOMPATBASEURL | Feature | Runtime | The base URL of an OpenAI-compatible endpoint (https://host/v1; a trailing /chat/completions is stripped). The openai-compatible adapter is ready only when this AND its key are set. |\\n| AIOPENAICOMPATAPIKEY | Feature | Runtime | The bearer token for that endpoint. |\\n| AIPROVIDER | Optional | Runtime | Which registered adapter is the platform default: anthropic or openai-compatible, or the id of a provider another plugin registers. Unset, the first ready adapter is the default. A workspace\\'s pluginSettings/ai may pick its own. |\\n| AIDEFAULTMODEL | Optional | Runtime | A model id, served by the default provider, for every step kind. Unset, each step kind takes its catalog tier on that provider. An id absent from the built-in rate table falls back to approximate rates, so cost telemetry and the margin alarm become estimates — and the prompt-cache minimum moves with the model, so a swap can silently stop caching. ' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Video delivery', anchor: '#video-delivery', text: 'Optional. Without these, a video in the media library serves from this install\\'s own media route, as it always has. With them, and the releasevideodelivery flag on for a workspace (off by default in Remote Config), each video is copied to an object store and served from it: the media route and the gated-video stream answer with a short-lived signed redirect, and the store\\'s edge sends the bytes and their ranges. The video-delivery plugin ships the adapter for Cloudflare R2 behind a Worker; the Worker\\'s source and its wrangler.jsonc are in libs/plugins/video-delivery.\\nYour own bucket keeps every video either way, and storage is counted once. Cloudflare receives the videos and, while it serves them, your viewers\\' IP addresses and user agents, so list it wherever you name the vendors your install uses before you turn the flag on.\\n| Variable | Need | When | Value |\\n| R2ACCOUNTID | Feature (console) | Runtime | The Cloudflare account that owns the bucket: 32 hex characters, shown on the account\\'s R2 overview. Only the console writes copies, so only the console needs the four R2 values. |\\n| R2ACCESSKEYID | Feature (console) | Runtime | The access key id of an R2 API token scoped to Object Read & Write on the one bucket. |\\n| R2SECRETACCESSKEY | Feature (console) | Runtime | That token\\'s secret access key. |\\n| R2VIDEOBUCKET | Feature (console) | Runtime | The bucket\\'s name. Keep the bucket private, with no public r2.dev URL: the Worker reads it through its binding. |\\n| MEDIAVIDEODELIVERYHOST | Feature | Runtime | The Worker\\'s hostname, such as video. .workers.dev. A full origin is accepted for local testing with wrangler dev (http://localhost:8787); plain HTTP is refused anywhere else. Console and tenant. |\\n| MEDIAVIDEODELIVERYSECRET | Feature | Runtime | Signs every delivery U' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Scheduled jobs', anchor: '#cron', text: 'Three separate mechanisms, and missing any of them is quiet rather than loud.\\n| Variable | Need | When | Value |\\n| CRONSECRET | Feature | Runtime | Shared secret every scheduled route checks, accepted as Authorization: Bearer or x-cron-secret: . Unset, every one of those routes refuses. openssl rand -hex 32. |\\n| AGLYNJOBRUNNERURL | Feature (cloud functions) | Runtime | The tenant origin the every-minute beat POSTs: https://sites.example.com/api/plugins/run-jobs. No default, deliberately. |\\n| AGLYNCONSOLEURL | Feature (cloud functions) | Runtime | The origin that serves your console — never one that redirects to it, because a redirect drops the POST body and the x-cron-secret header. It drives the fifteen-minute console sweeps and the every-minute AI jobs beat. No default, deliberately. |\\n| CRMDIGESTTIMEZONE | Optional | Runtime | The zone /api/crm/daily-digest reads \"today\" in when it counts overdue and due-today tasks. Default America/Chicago, which is why the schedule below fires at 13:00 UTC; an IANA name (Europe/London). A value the runtime does not know falls back to the default with a warning. Schedule the route for 08:00 in whatever zone you set. |\\nWhat CRONSECRET being unset silently switches off:\\n- Scheduled campaign sends. You can schedule a campaign in the console, watch it be accepted, and watch its send time pass with nothing delivered and no error anywhere.\\n- Audit archival, so the audit collection grows past its retention window and the retention promise goes quietly unkept.\\n- Erasure runs, so personal data a customer asked you to delete is still there while the clock on that request runs.\\n- Metered usage roll-up, so nothing is metered into Stripe and the monthly usage document is never written — which in turn means every usage budget is structurally unab' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Plugins and the sandbox', anchor: '#plugins', text: '| Variable | Need | When | Value |\\n| PLUGINARTIFACTSBUCKET | Feature | Runtime | The separate GCS bucket holding executable plugin bundles — the code refuses to store executable code in the app bucket. Unset, plugin publishing and artifact serving both answer 501. A bare bucket name. Note the bucket is invisible to the Firebase console; manage it in the Google Cloud console. |\\n| PLUGINARTIFACTSBASE | Optional | Runtime | Origin the server-side remote-bundle loader fetches from. Falls back to NEXTPUBLICPLUGINORIGIN. Only relevant with remote server bundles enabled. |\\n| NEXTPUBLICPLUGINORIGIN | Feature | Build | Full origin the plugin sandbox iframe and bundles are served from, and the entry added to the CSP frame-src. Must be a different origin from the app — the cross-origin boundary is the sandbox. Unset, realm plugins do not load and installed executable plugins render as nothing, because the CSP has no entry for them. |\\n| PLUGINTRUSTPRIVATEKEY | Feature | Runtime | Signs realm trust grants. Base64 PKCS8 DER Ed25519, generated by tools/scripts/generate-plugin-trust-key.mjs. Unset, the grant action answers 501. Console only — never deploy it to a tenant runtime. |\\n| PLUGINTRUSTPUBLICKEY | Feature | Runtime | The server-side verification key, base64 raw Ed25519. Required with remote server bundles enabled; unset there, no bundle loads at all — it fails closed rather than degrading to a hash check. |\\n| NEXTPUBLICPLUGINTRUSTPUBLICKEY | Feature | Build | The same public key again, for browser-side verification of realm bundles. A mismatch with the server-side name refuses every bundle. |\\n| PLUGINJOBSSECRET | Feature | Runtime | See Secrets. |\\nDevelopment-only, and never to be set on a real deployment: NEXTPUBLICPLUGINDEV (exactly enabled, and hard-gated off when NODEENV=pr' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Who runs this install', anchor: '#operator', text: 'The reasoning is in Self-hosting → Who runs this install. The reference rows:\\n| Variable | Need | When | Value |\\n| NEXTPUBLICOPERATORNAME | Set it | Build | Your legal or trading name. Printed on the public abuse intake, the §512 counter-notice intake, the lockdown 503, the media quarantine notice and the sanctions 451. There is no fallback to Aglyn\\'s name; unset renders an explicit \"not configured\". |\\n| NEXTPUBLICOPERATORSUPPORTEMAIL | Set it | Build | The address those same pages tell people to write to. |\\n| NEXTPUBLICOPERATORLEGALEMAIL | Optional | Build | A separate mailbox for legal and copyright notices. Defaults to the support address, so one mailbox is a complete configuration. |\\n| NEXTPUBLICOPERATORLEGALORIGIN | Optional | Build | Where your own terms, privacy and DMCA pages live, no trailing slash. The signup clickwrap links here, and it governs which origin counts as a published legal document. |\\n| NEXTPUBLICOPERATORDMCAAGENTNAME | Optional | Build | Designated agent\\'s name. Both this and the address must be set before the block renders at all — a mailbox with no legal person or no physical address behind it is not a designation. |\\n| NEXTPUBLICOPERATORDMCAAGENTADDRESS | Optional | Build | Physical address. |\\n| NEXTPUBLICOPERATORDMCAAGENTEMAIL | Optional | Build | Agent email. Omitting it leaves that line out rather than inventing one. |\\n| NEXTPUBLICOPERATORDMCAAGENTPHONE | Optional | Build | Agent phone. §512(c)(2) enumerates all four. |\\n| NEXTPUBLICOPERATORDMCAAGENTREGISTERED | Optional | Build | Exactly true, and only if you have actually registered the agent with the U.S. Copyright Office. Naming an agent does not set it and nothing infers it. |\\nNEXTPUBLICOPERATORLEGALORIGIN follows your origin, not your publication state The list of paths treated as publi' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Sales tax, and what to do if you are not in Texas', anchor: '#tax', text: 'The three variables below are the BOOTSTRAP. The control is in the console.\\nWhere you file and the numbers you file under live in Staff → Platform settings → Sales tax filing, where changing them needs the super staff role and no deploy. Registering in a new state is an operator action, so it must not require an environment edit and a release.\\nAGLYNTAXJURISDICTION, AGLYNTAXREGISTRATIONID and AGLYNTAXFILINGID are what a fresh install runs on before anybody opens that page: they fill in every field the console has not stored. Anything stored in the console wins, and the card names the layer each value came from — so a variable you set and then override is listed by name as not in force rather than quietly ignored. Clearing the stored record in the console hands these their layer back.\\nUnset, and with nothing stored, the jurisdiction is US-TX and the identifiers are absent, so both surfaces read NOT CONFIGURED rather than printing anyone else\\'s numbers.\\nTexas is the one jurisdiction with a form this software knows. Everywhere else gets a return breakdown — what was collected, and where — labeled as raw material for filing by hand rather than as a return.\\nTax collection and tax filing are different things in this product, and only one of them is portable.\\nCollection is a real feature, and jurisdiction-neutral {#tax-collection}\\nStorefront tax is configured per store, in the console, not by environment variable, and there is nothing Texas-specific about it:\\n- Rates are { country, state?, pct } entries. country is any ISO 3166-1 alpha-2 code and state any subdivision; the most specific match wins, and a country-only rate covers the whole country.\\n- Tax-inclusive pricing is supported, which is what VAT- and GST-style pricing needs.\\n- Modes are manual, stripe or none. Under stri' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Caching, ISR, and running more than one replica', anchor: '#caching', text: 'Single-container is the supported shape.\\nPublished pages are ISR-cached with a 10-minute window, site documents sit behind a one-hour render cache, and no shared cache handler is configured — so every replica keeps its own on-disk cache. Publishing POSTs /api/revalidate on the tenant, which busts the one replica that answered. The console reports an instant publish while every other replica keeps serving the old page for up to 10 minutes and the old documents for up to an hour.\\nIf you scale out, either put a sticky-session proxy in front or fan the revalidate POST out to every replica yourself. Nothing errors in either case, which is what makes it worth knowing.\\n| Variable | Need | When | Value |\\n| REVALIDATESECRET | Set it | Runtime | See Secrets. Without it, no publish ever busts any cache. Set the same value on both containers: the console will not send the request without it, and the tenant endpoint answers 503 naming the variable rather than quietly doing nothing. |\\nYour cache rules set your real takedown window\\nSeveral endpoints send s-maxage with little or no browser max-age, on the assumption that a shared cache honors it — the per-host manifest and robots.txt at 5 minutes to an hour, sitemaps and feeds at 5 minutes, screen-node and commerce endpoints at 60–300 seconds. Behind a proxy that caches nothing they are simply recomputed per request: correct, slower, and several of them hit Firestore each time.\\nThe consequence that is not about performance is media takedown. When staff disable a file, the reach of that action is derived from the media CDN\\'s own cache directives, and the console prints those numbers to the person clicking the button: the origin stops serving it within about 15 seconds, a browser that already has it keeps it for up to 60 seconds, and a s' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Domains: how a hostname becomes reachable', anchor: '#domains', text: 'Registering a name used to be a call to Vercel\\'s API, so a Docker install had no way to make a workspace subdomain resolve at all — it advertised a URL and skipped the registration. The hosting vendor is a driver now, and you pick one.\\nThis section is the per-variable reference. Domain providers is the runbook beside it: how to choose a driver, the wildcard path end to end with worked Caddy and nginx configurations, a complete worked webhook endpoint, what each status state means for you, and how to migrate from one driver to another.\\n| Variable | Need | When | Value |\\n| AGLYNDOMAINPROVIDER | Optional | Runtime | vercel, wildcard, webhook or none. Unset, the deployment detects: vercel when VERCELTOKEN is present, none otherwise. An unrecognized value logs [domain-provider] unknown AGLYNDOMAINPROVIDER and falls back to detection. An explicit value always wins, none included — switching a driver off while its credentials are still in the environment means it. |\\n| Driver | For |\\n| vercel | Aglyn\\'s own hosting, and anyone else on Vercel. |\\n| wildcard | The ordinary Docker answer. A container behind a proxy that already answers for .example.com. |\\n| webhook | Anything else — Caddy, Traefik, cert-manager, a registrar\\'s API, a shell script. |\\n| none | Names are handled entirely outside the product. Not an error state, and not something logged forever. |\\nDetection never picks wildcard, and that is deliberate The wildcard driver reports a name as serving without checking anything — there is nothing to check, because the operator\\'s DNS record and certificate either cover the apex or they do not. Inferring it from an apex somebody merely configured would have the console show a green chip beside an address that resolves nowhere. Claiming to serve a name is your assertion to make, ' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Documentation site build', anchor: '#docs-build', text: 'apps/docs is a standalone Docusaurus package. It is not part of docker compose; build and publish it separately if you want your own documentation site, and skip this section if you do not.\\nEvery value here is read at build time by docusaurus build, from that build\\'s environment. None is NEXTPUBLIC — Docusaurus is not Next.\\n| Variable | Need | Value |\\n| DOCSURL | Optional | The canonical origin your docs are served at. Canonical tags and the sitemap are built from it, so leaving it at the default makes your build claim docs.aglyn.com. |\\n| DOCSORGANIZATIONNAME | Optional | Name in the footer copyright line. Default Aglyn LLC, followed by Aglyn\\'s trademark attribution. Setting it replaces the name and drops the attribution. |\\n| DOCSGATRACKINGID | Optional | Your own GA4 measurement id, G-XXXXXXXXXX. Blank loads no analytics tag at all. |\\n| DOCSERRORBEACONENDPOINT | Optional | Where uncaught browser errors POST — e.g. https://console.example.com/api/errors. Blank installs no handlers. If you point it at your own console, set NEXTPUBLICDOCSORIGIN on that console to your docs origin so its CORS grant accepts you. |\\n| DOCSSTATUSTARGETS | Optional | What /status probes, as comma-separated name\\\\|label\\\\|origin triples: console\\\\|Console\\\\|https://console.example.com,sites\\\\|Sites\\\\|https://sites.example.com. Blank probes nothing and says so. |\\n| DOCSMETAPIXELID | Optional | Meta Pixel id for the docs site — digits only. Loads only for a reader whose consent record grants advertising; see the note below. Blank loads nothing. |\\n| DOCSADSCONVERSIONID | Optional | Google Ads id, AW- plus digits. Rides the gtag.js the analytics tag already loaded rather than fetching a second copy. Same consent gate. Blank loads nothing. |\\n| DOCSLINKEDINPARTNERID | Optional | LinkedIn Insight Tag partner' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Set by the image — do not put these in your env file', anchor: '#image-set', text: '| Variable | Set to | Why you must not override it |\\n| AGLYNSTANDALONE | exactly 1, in the runner stage of both Dockerfiles | This is what tells the software it is a real deployment rather than a laptop. It gates the whole host-resolution switch, the canonical custom-domain redirect, and the CSP that drops Aglyn\\'s own hostnames from your frame-ancestors. It is kept out of .env.selfhost.example on purpose: compose envfile overrides image ENV, so a line there would be a way to delete it and silently break serving. Only the string 1 counts — true and yes do nothing. |\\n| NODEENV | production | Several safety relaxations key on \"not production\", the custom-domain verify soft pass among them. |\\n| PORT | 4200 console, 4500 tenant | Compose publishes these on loopback. |\\n| HOSTNAME | 0.0.0.0 | Listens inside the container\\'s namespace. |\\n| NEXTTELEMETRYDISABLED | 1 | No build telemetry leaves your machine. |\\nStamping which build you are running {#build-stamp}\\n| Variable | Need | When | Value |\\n| COMMITREF | Optional | Build | An identifier for the build. /api/health reports it as commit, and client and server error reports and the staff config report all carry it. Best passed as a build argument, which changes per build: COMMITREF=$(git rev-parse HEAD) docker compose up --build. The build argument wins where both are set. Unset, the health body reports commit: null honestly rather than inventing one. The Dockerfiles read it in the build stage, so passing it at docker run is too late. |\\n| BUILDID | Optional | Build | An explicit build id, checked before COMMITREF. Use it if you stamp builds with something of your own. |\\n| PACKAGEVERSION | Set by the build | Build | Read from package.json at build time and reported by /api/health as version. Setting it in the environment does noth' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Development and Aglyn-internal variables', anchor: '#internal', text: 'Read by the code, but not operator configuration. Listed so that finding one in the source does not send you looking for a value to put in it.\\n| Variable | What it is |\\n| AGLYNTENANTDEMO | The tenant host id served when the request host is the console apex or localhost:4500. Default demo. Aglyn\\'s own demo deployment. |\\n| AGLYNCANARYSITEHOST, AGLYNCANARYMARKETINGHOST | Which hosts the render canary at /api/health/render renders. The site one falls through to AGLYNTENANTDEMO then demo, so an install with no demo host gets a canary reporting red on a host that was never meant to exist. The marketing one derives from your workspace domain and grades not-configured — a deliberate failure, not a pass — when it cannot. |\\n| AGLYNTENANTHOSTID, AGLYNTENANTHOSTHOST, AGLYNTENANTHOSTHOSTNAME, AGLYNTENANTHOSTURL | Single-host tenant pinning, for a one-site deployment. Not used by the multi-tenant compose shape. |\\n| AGLYNHOST, AGLYNHOSTNAME, AGLYNPORT, AGLYNPROTOCOL, AGLYNURL | Build-time origin overrides in the shared Next config. Leave at the defaults. |\\n| AGLYNDISABLEBOOTWARMUP | Skips the tenant\\'s boot warm-up. A debugging aid. |\\n| CONSENTDEVCOUNTRY | On a non-production build only, the country the consent endpoint answers when no geo header is present — the default is US, so the implied-consent path is testable on a laptop. Hard-gated off when NODEENV=production, so it can do nothing on a real deployment. |\\n| NEXTPUBLICAGLYNTENANTLOCALORIGIN, NEXTPUBLICAGLYNTENANTPREVIEWHOST | Where \"Live\" links point from a local or preview console. Both default to Aglyn\\'s own addresses, which matters only on a preview build. |\\n| NEXTPUBLICANALYTICSALLOWNONPROD | Development only. |\\n| NEXTPUBLICPLUGINDEV, NEXTPUBLICPLUGINDEVBUNDLES, PLUGINREMOTESERVER, PLUGINREMOTESERVERBUNDLES | Local plugin de' },\n { path: '/developers/self-hosting-environment', title: 'Environment variables', heading: 'Related', anchor: '#related', text: '- Self-hosting — the setup runbook this reference belongs to\\n- docs/SELFHOSTING.md — the repository runbook, with the Firebase and rules-deploy steps\\n- .env.selfhost.example — the template you copy\\n- White-label' },\n { path: '/enterprise/overview', title: 'Enterprise overview', heading: '', anchor: '', text: 'Enterprise overview\\nEnterprise is the only plan that is not self-serve. It is quoted rather than listed, and it is the plan the pages in this section describe.\\n| | |\\n| Single sign-on | SAML SSO against your identity provider, optionally enforced so passwords stop working. |\\n| Support commitments | A 24–48 hour first-response commitment measured in clock hours, and a named success manager. |\\n| Availability & status | What we measure, what /status shows, and what we have not committed to. |\\n| Security & compliance | The material a security review needs, including what we do not have. |' },\n { path: '/enterprise/overview', title: 'Enterprise overview', heading: 'What Enterprise does not change', anchor: '#what-enterprise-does-not-change', text: 'Worth saying plainly, because it is the usual assumption:\\n- Quotas and features are entitlements, not plan names. An Enterprise workspace can have any combination of limits, and they are set per organization rather than implied by the tier.\\n- The product is the same product. Enterprise is about how we contract, respond and authenticate — not a separate build.' },\n { path: '/enterprise/overview', title: 'Enterprise overview', heading: 'Getting Enterprise', anchor: '#getting-enterprise', text: 'Contact us — Enterprise is the one tier with no self-serve checkout, so the plan itself is arranged with us rather than bought from the billing page.\\nEverything after that is yours to configure. Single sign-on in particular is set up by your own admins in Organization → Settings → Single sign-on: claim your domain, verify a DNS TXT record, exchange IdP metadata, rehearse enforcement, and activate. There is no provisioning step on our side and nothing to wait for.' },\n { path: '/enterprise/security-and-compliance', title: 'Security & compliance', heading: '', anchor: '', text: 'Security & compliance\\nThe full write-up lives on Trust & security. It is written for the security review that precedes an enterprise purchase, and it is kept as one page on purpose — a reviewer should be able to send a single link rather than assemble an answer from several.' },\n { path: '/enterprise/security-and-compliance', title: 'Security & compliance', heading: 'What it covers', anchor: '#what-it-covers', text: '- What we do not have, first rather than buried: no SOC 2 report and no audit in progress, no ISO 27001 / HIPAA / FedRAMP, no third-party penetration test, no bug bounty.\\n- Authentication, including SSO, and how sessions work.\\n- Data handling — where data lives, what is encrypted, and who can reach it.\\n- Availability, and the fact that there is no committed uptime percentage (see Availability & status).\\n- How to report a security issue.' },\n { path: '/enterprise/security-and-compliance', title: 'Security & compliance', heading: 'Contract documents', anchor: '#contract-documents', text: 'Both are published pages — read them without asking anyone, and send them to your reviewers directly:\\n- Data Processing Addendum — the processing terms, including the export and deletion obligations the product implements.\\n- Subprocessor list — the authoritative one. The table on Trust & security is the engineering view of the same set and may lag it.\\nOne thing that list does not contain, stated here so a reviewer does not have to infer it. It names the parties Aglyn engages. It does not name a host your own authors point at — a hotlinked image, or a url(...) in a stylesheet one of them wrote. Those requests go from your visitor\\'s browser to that host directly and Aglyn does not proxy them, so the host is a recipient of your visitors\\' data that only you can enumerate and disclose. It is a property of the builder rather than an omission from the register, and it is written up in full at Trust & security.\\nNeither of those two is acceptance-pinned, so neither carries a version bump your people have to re-accept when it changes. The Terms of Service and the Privacy Policy are the opposite: each acceptance is pinned to a version and to the exact text published under it, and a new version is put back in front of every user. See below.' },\n { path: '/enterprise/security-and-compliance', title: 'Security & compliance', heading: 'When the Terms or Privacy Policy change', anchor: '#legal-reacceptance', text: 'Accepting the Terms accepts the Privacy Policy with them, as one versioned set. An acceptance is recorded against the individual — not against the workspace — with the version, a content hash of each document exactly as it was published at that version, the time from our clock, and which door the acceptance came through. Records are additive and immutable: a later acceptance adds a row rather than overwriting the earlier one, so the history of what a person agreed to, and when, stays intact.\\nWhen we publish a new version, the next time that person opens the console they see a banner. It opens by acknowledging the acceptance we already hold and names the date they agreed, then says the documents have been updated and asks them to confirm. Where we can tell which of the two documents actually moved — by comparing the content hashes on their record against the ones published now — the banner names them; where that comparison cannot be made it says nothing rather than guessing. I agree records the new acceptance and the banner goes. It appears on every console page until it is answered, and it does not lock anyone out of the product in the meantime.\\nA banner also appears for an account we hold no acceptance for at all — accounts created before we captured acceptance, and people who arrived through SSO or an invite without passing a consent control. The wording there is deliberately different, because the fact is different: it says we have no record of acceptance on this account and gives the usual reason, which is a gap in our records rather than anything the person failed to do.\\nTwo consequences worth naming for a review:\\n- Acceptance is per person, not per organization. Every member of your workspace is asked separately, because \"the organization accepted\" and \"this perso' },\n { path: '/enterprise/security-and-compliance', title: 'Security & compliance', heading: 'Why the gaps are listed first', anchor: '#why-the-gaps-are-listed-first', text: 'Because a reviewer needs them, and because a questionnaire surfaces them anyway — three weeks later, with more people in the thread. Leading with the absences is faster for both sides than leading with the controls.\\nIf a certification is a hard requirement for your purchase, raise it at the start. We would rather tell you early that we do not have it than discover it at contract stage.' },\n { path: '/enterprise/security-and-compliance', title: 'Security & compliance', heading: 'Reporting a vulnerability', anchor: '#reporting-a-vulnerability', text: 'Security reports are read even though there is no bug bounty. The reporting route is on Trust & security.' },\n { path: '/enterprise/sso', title: 'Single sign-on (SAML)', heading: '', anchor: '', text: 'Single sign-on (SAML)\\nSSO signs your team in against your own identity provider. It is an Enterprise feature, and once your organization is on Enterprise you set it up yourself in Organization → Settings → Single sign-on. There is no provisioning step on our side and nothing to wait for.' },\n { path: '/enterprise/sso', title: 'Single sign-on (SAML)', heading: 'Setting it up', anchor: '#setting-it-up', text: '1. Verify your domain\\nAdd each email domain your team signs in with. We give you a DNS TXT record to publish:\\n| Record | Value |\\n| aglyn-challenge.yourdomain.com | aglyn-domain-verification=… |\\nPublish it at your DNS provider, then press Verify. DNS can take a few minutes to propagate; re-checking is safe and costs nothing.\\nThis step is not a formality. A verified domain is what tells us to send that domain\\'s sign-ins to your identity provider. If anyone could claim a domain without proving they own it, they could point your domain at their IdP and intercept your team\\'s logins. So nothing else works until a domain passes, and we re-check the record rather than taking your word for it.\\nPublic mailbox domains — gmail.com, outlook.com and the like — cannot be used. They are shared with millions of people outside your organization.\\nLeave the TXT record in place after verification. We re-check it weekly, because a domain that changes hands should not keep routing sign-ins to whoever set it up first.\\nIf the record stops answering, nothing happens to your sign-in. We do not remove a domain automatically, and a failed lookup on our side is never treated on its own as proof the record is gone — a DNS outage looks identical to a deleted record, and we would rather ask you than lock your team out.\\nWhat happens instead: after the check fails three weeks running, we email your organization\\'s owners and admins asking you to restore it. Your SSO keeps working the whole time. Removing a domain is always a deliberate act by you, in Organization → Settings → SSO, or by us with your agreement.\\nSo if you migrate DNS providers, carry aglyn-challenge.yourdomain.com across with the rest of your records.\\n2. Connect your identity provider\\nThe settings page shows the two values your IdP asks for' },\n { path: '/enterprise/sso', title: 'Single sign-on (SAML)', heading: 'How it works', anchor: '#how-it-works', text: 'Each SSO organization gets its own identity pool. Your users authenticate against your IdP, and their Aglyn accounts live in that pool rather than in the shared one. That isolation is the point — your directory governs who can sign in, and removing someone there removes their access here.\\nSigning in goes through a dedicated route rather than the normal email/password form, so people arrive by way of your IdP.' },\n { path: '/enterprise/sso', title: 'Single sign-on (SAML)', heading: 'Enforcement', anchor: '#enforcement', text: 'An organization can turn on SSO enforcement, which is a separate step from having SSO configured.\\n- Configured, not enforced — SSO is available, and other sign-in methods still work.\\n- Enforced — other sign-in methods are stripped from accounts in the pool. Passwords stop being an option, not merely a discouraged one.\\nEnforcement is applied by a sweep over the pool. Rehearse it first — the settings page has a rehearsal that changes nothing and lists exactly which accounts would lose which sign-in methods. An account whose only way in is the method being removed is reported and skipped rather than orphaned.\\nEnforcement cannot be undone by turning it off. We remove a linked credential; we do not keep a copy to put back. Anyone affected re-links for themselves.\\nYou must keep one way in that does not go through your IdP\\nEnforcing is refused until your organization has a way in that survives your identity provider failing. This is not a warning you can dismiss — the button stays disabled and the sweep will not run.\\nThe reason is the failure it protects against. After enforcement, every account in your pool holds nothing but the link to your identity provider. If that provider stops answering — a lapsed signing certificate, an application deleted during a migration, a tenant moved — nobody can sign in, and we cannot let you back in either: your accounts live in your own pool and their only credential is the one that stopped working.\\nThere are two ways to satisfy the requirement, and for most organizations only the first is available.\\nAn owner who signs in outside your identity pool\\nEnforcement sweeps your identity pool and nothing else. An organization owner whose account is not in that pool is not merely spared — the sweep cannot see the account at all, so no certificate, ap' },\n { path: '/enterprise/sso', title: 'Single sign-on (SAML)', heading: 'Consequences worth knowing before you switch', anchor: '#consequences-worth-knowing-before-you-switch', text: 'Passwords cannot be set for SSO accounts. The console refuses rather than silently doing nothing — your IdP owns the credential.\\nSSO users are in a separate pool. This is the isolation working, but it has practical effects the platform has to account for explicitly: an SSO user is not returned by ordinary account lookups unless the lookup searches every pool. Staff surfaces label these accounts with the pool they belong to, so \"can\\'t find them\" and \"they\\'re an SSO account\" are distinguishable.\\nSocial sign-in cannot be linked to an SSO account at all. Not automatically, and not by the user either — the option is not offered, and the account page says why. Your identity provider is the single gate you bought: you revoke there, you enforce MFA there, you offboard there. A personal Google account linked to a governed identity would be a way in that your directory can neither see nor revoke, so we do not allow one to be created.\\nThis holds whether or not you have turned enforcement on. Enforcement decides whether we remove sign-in methods that already exist; it was never a license to keep handing out new ones in the meantime. Nothing about this can lock anyone out, because refusing to add a method never takes away a method someone already has.\\nAn account in your pool is also simply not the same identity as the same email address signing in with Google elsewhere, so the two never merge.' },\n { path: '/enterprise/sso', title: 'Single sign-on (SAML)', heading: 'Testing it', anchor: '#testing-it', text: 'SSO redirects cannot be exercised from localhost — the flow fails with a missing-initial-state error before it reaches your IdP. Test on a deployed environment.' },\n { path: '/enterprise/sso', title: 'Single sign-on (SAML)', heading: 'Related', anchor: '#related', text: '- Signing in and sessions\\n- Trust & security — the security posture behind an enterprise review' },\n { path: '/enterprise/support-tiers', title: 'Support commitments', heading: '', anchor: '', text: 'Support commitments\\nEvery plan gets the community forum. Private ticket support starts at Pro — Free and Starter pay for product, not for our time.\\nThe commitment below is a first response, not a resolution. It is what the Support tickets page shows you, and what the staff queue is ordered by.\\n| Plan | Tier | First response | Named success manager |\\n| Free | Community | Forum only | — |\\n| Starter | Community | Forum only | — |\\n| Pro | Standard | 7–14 business days | — |\\n| Business | Business | 4–6 business days | — |\\n| Scale | Business | 4–6 business days | — |\\n| Advanced | Business | 4–6 business days | — |\\n| Agency | Priority | 1–3 business days | — |\\n| Enterprise | Dedicated | 24–48 hours | Yes |' },\n { path: '/enterprise/support-tiers', title: 'Support commitments', heading: 'Why Enterprise is in hours, not business days', anchor: '#why-enterprise-is-in-hours-not-business-days', text: 'It is the point of the tier. 24–48 clock hours runs through a weekend; \"1–3 business days\" does not. Read as business hours the Enterprise window would be 3–6 business days, which would make it slower than Agency above and invert the ladder.\\nThere is a test asserting the ladder stays monotonic, so this cannot quietly regress into the wrong order.' },\n { path: '/enterprise/support-tiers', title: 'Support commitments', heading: 'What starts and stops the clock', anchor: '#what-starts-and-stops-the-clock', text: '- The clock starts when you open the ticket.\\n- It stops on the first reply from the Aglyn team — write-once, so a later staff message does not reset or re-stamp it.\\n- A reply from you does not stop it. If we have not answered yet, the commitment is still running.' },\n { path: '/enterprise/support-tiers', title: 'Support commitments', heading: 'Where to see it', anchor: '#where-to-see-it', text: 'Organization → Support → Support tickets, reached from Support in the org tabs or in your account menu. On a plan that carries tickets that is where Support opens; on Free and Starter it opens on the forum, with a link across. The page states your own commitment rather than a generic promise, so what you read there is what your plan actually carries.\\nYour plan is per organization. If you belong to more than one, the commitment follows the workspace you are looking at — not your account.' },\n { path: '/enterprise/uptime-and-status', title: 'Availability & status', heading: '', anchor: '', text: 'Availability & status' },\n { path: '/enterprise/uptime-and-status', title: 'Availability & status', heading: 'The status page', anchor: '#the-status-page', text: 'docs.aglyn.com/status checks six things live, from your browser, when you load it — and again every minute while the page is open: the console, an end-to-end render of a real published page, the runtime that serves every published site, the marketing site, billing, and scheduled jobs.\\nIt shows whether each surface is responding right now, in one of three words:\\n| Word | What it means |\\n| Operational | The service returned its own health report saying so. This is the only way a surface goes green. |\\n| Degraded | The service answered and reported a problem, or returned an error. |\\n| No reading | The check could not be completed — a blocked request, a network problem, or a reply the page cannot read. It is not treated as healthy, and it is not a claim that we are down. |\\nThat third state is deliberate. From your browser a real outage and a captive wifi portal look identical, so the page says which of the three it actually observed rather than rounding either way.\\nTwo limits worth knowing before you rely on it:\\n- It covers the surfaces you use, plus billing and scheduled jobs. Those two are not part of serving: a published site keeps working while either is degraded, so a red card there means invoices or background work are affected and nothing else on the page is. Backups, abuse controls and our own error reporting are still not on the page — they are monitored continuously and separately, and none of them changes whether your site is serving.\\n- It is not an independent monitor. It is served from a different deployment than the services it reports on, so a console outage does not take it down. It is not served from a different provider, so an outage broad enough to take out the whole platform could take the page with it. If /status does not load at all, treat that as a sig' },\n { path: '/enterprise/uptime-and-status', title: 'Availability & status', heading: 'There is no committed uptime percentage', anchor: '#there-is-no-committed-uptime-percentage', text: 'Stated plainly because procurement asks, and because a vague answer wastes everyone\\'s time:\\n> We have not published an uptime SLA.\\nWe measure availability and intend to commit to a number backed by that data — rather than one chosen to close a deal. Until there is enough history to stand behind, there is no percentage to quote, and we would rather say so than publish a figure we cannot evidence.\\nIf a committed availability figure is a hard procurement requirement today, that is worth raising early rather than late. See Trust & security for the same list from the reviewer\\'s side.' },\n { path: '/enterprise/uptime-and-status', title: 'Availability & status', heading: 'Where the platform runs', anchor: '#where-the-platform-runs', text: 'Published sites and the console are served from a global edge network, with data in Google Cloud Firestore. A published page is served from cache and regenerated in the background, so a slow origin degrades to a stale page rather than an error.' },\n { path: '/enterprise/uptime-and-status', title: 'Availability & status', heading: 'Reporting an outage', anchor: '#reporting-an-outage', text: 'If something is down and /status disagrees, tell us — the status page checks reachability, which is not the same as everything working.\\nOpen a ticket from Organization → Support → Support tickets. Response is governed by your support commitment; Enterprise is 24–48 clock hours.' },\n { path: '/getting-started/aglyn-assist', title: 'Aglyn Assist', heading: '', anchor: '', text: 'Aglyn Assist\\nAglyn Assist is the chat helper in the bottom-right corner of every console page. Ask it how to do something — publish a screen, connect a domain, set up shipping, invite a teammate — and it answers from this documentation, linking the exact docs section and the console page where you do it.' },\n { path: '/getting-started/aglyn-assist', title: 'Aglyn Assist', heading: 'What it can do', anchor: '#what-it-can-do', text: '- Answer how-to questions about building sites, publishing, domains, commerce, bookings, workflows, datasets, team roles, and billing.\\n- Link you to the source — under each written answer is a Sources list naming the documentation sections it drew on, each one a link straight to that section. See Where an answer came from.\\n- Guide you on the page you\\'re on (Pro plans and up) — the assistant knows which console screen you\\'re viewing and answers about that screen, rather than giving you a general answer you then have to translate.\\n- Offer to take you to the right page (Pro plans and up) — when what you asked for starts somewhere else, it can show a card that opens that page for you. See Offers to open a page.\\n- Propose edits in the Besigner (with AI generation on your plan) — ask for a change to the page, component or layout you have open, and it shows exactly what would change before anything does. See Edits in the Besigner.\\nAglyn Assist answers and directs. It never saves, publishes or changes a setting on its own. The one thing it can change is the canvas you have open in the Besigner — and only when you apply a change it proposed, as unsaved edits you can undo — so there\\'s nothing it can do by accident; you stay in control of every change.' },\n { path: '/getting-started/aglyn-assist', title: 'Aglyn Assist', heading: 'When it builds instead of answering', anchor: '#aglyn-ai', text: 'Everything above is the assistant explaining. With AI generation on your plan — the Aglyn AI add-on, or the AI credits a Free workspace gets — the same assistant also builds: whole pages, layouts, page templates, reusable components, forms, designed emails, campaigns, product copy, your search titles and descriptions, a change to your theme, and a small site from one brief. AI jobs in the panel is where you watch one work and open what it made.\\nEverything it builds arrives as a draft. It never publishes, never changes a live page in place and never sends anything, so a generated page has no address on your live site until you publish it yourself.\\nWhat each door does, what it costs in credits and who can use it is in Aglyn AI.' },\n { path: '/getting-started/aglyn-assist', title: 'Aglyn Assist', heading: 'Answers for beginners and developers', anchor: '#answers-for-beginners-and-developers', text: 'Every answer is written twice over, in one message.\\nThe main answer is in plain words: what to click, in the order you\\'ll click it, with nothing assumed about what you already know.\\nUnderneath, when there\\'s something technical worth adding — the URL of the page, the identifier in it, the field or API behind the screen — there\\'s a collapsed Under the hood line. Open it if you want that detail; ignore it entirely if you don\\'t. Nothing is hidden from you and nothing is explained at you.' },\n { path: '/getting-started/aglyn-assist', title: 'Aglyn Assist', heading: 'Offers to open a page', anchor: '#offers-to-open-a-page', text: 'Sometimes the thing you asked about starts on a different screen. When the assistant knows which one, it shows a small card offering to take you there — with the values to use, when it has worked them out from your question.\\nThe card only opens the page. It never fills in a form for you, never saves anything, and never makes a change on your behalf. You land on the normal screen, fill it in yourself, and press the button yourself — with all the usual permission checks in place. If you\\'d rather stay where you are, choose No thanks and the card goes away.\\nThis is deliberate. A card that only opens a page cannot change your site by misunderstanding you, because it has no way to. The only changes the assistant can make are the edits in the Besigner, and each of those waits for you to apply it.' },\n { path: '/getting-started/aglyn-assist', title: 'Aglyn Assist', heading: 'Edits in the Besigner', anchor: '#edits-in-the-besigner', text: 'With a page, a reusable component or a layout open in the Besigner, you can ask the assistant for a change: \"make this hero darker\", \"add a testimonial band below\", \"point this button at the pricing page\". When your plan includes AI generation — the Aglyn AI add-on, or the AI credits a Free workspace gets — it answers with a Proposed change card listing what would change: elements added, removed, moved or restyled, settings changed, and a page\\'s search title or description filled in. Anything it could not match to your canvas is listed under Left out.\\n- Nothing changes until you apply. Apply as draft puts the changes on the open canvas as unsaved edits, all in one step, so a single Undo takes the whole change back. Nothing is saved or published until you do that yourself. A proposed search title or description is filled into Screen Properties, where Save SEO stores it.\\n- The live version is never edited in place. When the version open is the one your live site shows, the card offers Make a new version instead — the same New version you would use yourself. Once the new version opens, the card offers Apply as draft there.\\n- Select the element you mean. The assistant sees the element you have selected, everything inside it, what surrounds it and the page\\'s sections — not your whole site — and it can only change elements it was shown. With nothing selected it sees only the top of the page and asks you to select one. If the canvas changes before you apply, the card says so and applies nothing.\\n- Who can use it. Your role needs the Generate with AI permission (a site collaborator needs it on that site). Asking uses AI credits like any other assistant message; applying uses none. Each applied change is recorded in the site\\'s activity log under your name.' },\n { path: '/getting-started/aglyn-assist', title: 'Aglyn Assist', heading: 'Where an answer came from', anchor: '#where-an-answer-came-from', text: 'Every answer the assistant writes is followed by a short Sources line — Source when there is only one — listing the documentation sections it drew on. Each entry names the page and the heading within it, and clicking one opens that exact section.\\nThis is there so you never have to take an answer on trust. If the reply is not quite what you needed, the source is usually the fastest next step: the page will have the surrounding detail that a short answer had to leave out.\\nA link the assistant already put in the answer itself is not repeated in the list, so replies quoted straight from the documentation — which lead with the page name and link it — do not carry a duplicate.' },\n { path: '/getting-started/aglyn-assist', title: 'Aglyn Assist', heading: 'Answers straight from the documentation', anchor: '#answers-straight-from-the-documentation', text: 'Most of what people ask the assistant — \"how do I connect a domain\", \"where do I save a template\" — is already written down on these pages. When your question clearly matches one of them, the assistant hands you that page\\'s own words, with a link to the full page, instead of writing you a fresh answer.\\nYou will recognize these replies: they open with the name of the page, quote it, and close by offering to go further if it missed what you meant.\\nThere are two things worth knowing about them.\\n- They are never a summary. The text is copied from the documentation exactly as written, so it cannot drift from what the page actually says. If the assistant is not confident which page answers you, it writes you a real answer instead of quoting a page that only looks close.\\n- They do not count against your message limit, because they cost nothing to produce. Ask as many as you like.\\nIf a quoted page did not cover what you meant, just ask again with more detail — the follow-up gets a written answer.' },\n { path: '/getting-started/aglyn-assist', title: 'Aglyn Assist', heading: 'Message limits', anchor: '#message-limits', text: '- Free workspaces get a limited number of assistant messages each day, with answers and docs links, and 300 AI credits a month for the assistant and AI generation together. The credits belong to the person who owns the workspace, so several Free workspaces owned by one account share them; when they are used, AI pauses until next month or an upgrade, and nothing is ever billed. A new account waits a short while after signing up before it can generate, and free requests are capped per day.\\n- Paid plans include their own monthly AI credits for the assistant and AI generation together, rising with the plan, and sell credits past the band rather than stopping at it — so the assistant keeps working and the month\\'s overage is billed, unless you switch that off or set a ceiling in Billing.\\n- Pro and higher plans also get page-aware guidance and the offers to open a page described above.\\nWhen a free workspace reaches its daily limit, the assistant says so and the counter resets the next day (UTC).\\nAnswers quoted straight from the documentation are free and are not counted, so a workspace at its daily limit can still ask how-to questions.' },\n { path: '/getting-started/aglyn-assist', title: 'Aglyn Assist', heading: 'Feedback', anchor: '#feedback', text: 'Every answer offers a thumbs-up / thumbs-down. Ratings tell us which answers — and which docs — need work, so rating answers directly improves the product.' },\n { path: '/getting-started/aglyn-assist', title: 'Aglyn Assist', heading: 'Privacy', anchor: '#privacy', text: 'Conversations with Aglyn Assist are stored with your workspace so we can improve answers and documentation. See the privacy policy for details on how this data is handled.' },\n { path: '/getting-started/console-search', title: 'Search the console', heading: '', anchor: '', text: 'Search the console\\nThe magnifier in the console app bar — or ⌘ K on a Mac, Ctrl K elsewhere — opens a search palette over whatever page you are on, including the Besigner.\\nType at least two characters. Results are grouped by kind, and clicking one takes you straight to it.' },\n { path: '/getting-started/console-search', title: 'Search the console', heading: 'What it searches', anchor: '#what-it-searches', text: 'Everything below is searched within the site you currently have open, plus your sites across the workspace:\\n| Group | What a row is | Where clicking it goes |\\n| Sites | A site you belong to in this workspace | The site dashboard |\\n| Pages | A screen on the open site | The screen\\'s version view |\\n| Emails | An email screen on the open site | The email in the Besigner |\\n| Components | A reusable component | The component page |\\n| Layouts | A shared layout | The layout page |\\n| Templates | A site template | The template page |\\n| Content | A content collection | The Content page |\\n| Authors | A content author | The Content page |\\n| Workflows | A workflow | The Automation page |\\n| Products | A product | The Products page |\\n| Redirects | A redirect, matched on its source path | The Redirects page |\\n| Services | A bookable service | The Bookings page |\\n| Contacts | A person in the CRM, matched on their name, email, phone number or company name | The contact\\'s own page in the CRM |\\n| Leads | A lead captured on a site, matched on their name or email | The lead\\'s own page in the CRM |\\n| Companies | A company in the CRM, matched on its name or domain | The company\\'s own page in the CRM |\\n| Deals | A deal, matched on its title | The deal\\'s own page in the CRM |\\n| Tasks | A task, matched on its title or notes | The record the task was filed under, or the Tasks list when it names none |\\n| Activities | A logged call, email, meeting or note, matched on the subject of a sent message or the text of a logged one | The record the activity was filed under |\\nA group only appears if your plan includes it. On the Free plan there are no workflows, products, services or redirects, so those groups are neither shown nor searched. Contacts appear only while the CRM is available to you and your role' },\n { path: '/getting-started/console-search', title: 'Search the console', heading: 'Searching from the organization', anchor: '#searching-from-the-organization', text: 'At the organization-level CRM — /{workspace}/crm, with no site in the address — the CRM groups search across every site in the workspace rather than one, and a lead\\'s result opens under the site that captured it. This is offered to organization-wide members only, which is the same rule that opens that hub.\\nThe site groups — pages, emails, components, layouts, templates, content, authors, workflows, products, redirects and services — belong to one site and are not searched from there; open a site first.\\nLeads are read one site at a time, so a workspace with a great many sites has the first ten searched and the group says it was only partly searched.' },\n { path: '/getting-started/console-search', title: 'Search the console', heading: 'How matching works', anchor: '#how-matching-works', text: 'It matches any part of a name, not just the beginning. Searching layout finds \"Main Layout\"; so does lay, main, and main lay.\\nRanking puts the closest match first: an exact name, then a name that starts with what you typed, then a word inside the name, then anything else. Pages and products can also be found by their slug or route, but a match on the name always ranks above a match on a slug.\\nAccents are ignored, so cafe finds \"Café\".' },\n { path: '/getting-started/console-search', title: 'Search the console', heading: 'What it does not search', anchor: '#what-it-does-not-search', text: 'Being precise about this matters — an empty result should mean \"you do not have one\", not \"search could not see it\".\\n- Orders and bookings are not searchable. They are found on their own pages, which have their own filters.\\n- Media has its own, richer search on the Media page, including wildcards and searching by tag or custom field. See the media library.\\n- Other workspaces. Search only ever covers the workspace named in the page address you are on.\\n- Other people\\'s sites. You only ever see sites you are a member of.' },\n { path: '/getting-started/console-search', title: 'Search the console', heading: 'Why a group sometimes says it was only partly searched', anchor: '#why-a-group-sometimes-says-it-was-only-partly-searched', text: 'To keep search fast and cheap, each group reads a capped number of items rather than your entire site. If a group is larger than that cap, the palette says so underneath the results — that line means there may be matches it did not look at, so try a more specific word.\\nMost sites never hit the cap.\\nIf a group could not be read at all, it says that too, in red. That is a different thing from \"no matches\", and it is worth retrying or reporting.' },\n { path: '/getting-started/console-tour', title: 'The console tour', heading: '', anchor: '', text: 'The console tour\\nThe console is where you manage a site. Here\\'s what each part of the chrome does.\\n1. App bar — the Aglyn console wordmark, notifications, and your account menu.\\n2. Workspace switcher — jump between the organizations you belong to.\\n3. Site sections — every area of the selected site (Dashboard, Screens, Media, Content, …).\\n4. Site switcher — switch between the sites in your workspace.\\n5. Page body — the selected section\\'s cards and tables.' },\n { path: '/getting-started/console-tour', title: 'The console tour', heading: 'The app bar', anchor: '#the-app-bar', text: '- Site switcher (left) — jump between the sites you belong to, search them by name with Find site…, or create a new one.\\n- Breadcrumbs — show the current site\\'s display name and where you are.\\n- Screen switcher — a searchable dropdown for hopping between screens without leaving the editor: your recent screens by default, or type to find any screen or layout in the site by name. View all screens opens the full list.\\n- Version dropdown (right, near your avatar) — pick or schedule a version to view.\\n- Notifications — the bell shows an unread badge and drops down your 10 most recent notifications (form submissions, bookings, invoices, team changes), split across an Inbox tab (unread) and an Archive tab (already read). Mark all read clears the inbox, the gear opens your notification preferences, and View all opens the full paginated feed.\\n- Account menu — your avatar opens it. The header shows your name and email with a gear to Manage Account, and the first row below repeats that destination as a labeled Manage account — a gear is not discoverable as a name. Then come Manage Team, Billing and Support, which appear only on a page that names a workspace and only for members whose role can reach those pages; Staff console (Aglyn staff only); a Documentation link that opens this docs site in a new tab; and Report an issue, directly under Documentation because \"the docs didn\\'t answer it\" is the step before reporting it. Report an issue opens a form over the page you\\'re on rather than navigating away — the page you were looking at is the most useful thing the report carries — and unlike the rows above it, it needs no particular role, no resolved workspace, and no paid plan: every signed-in member has it on every page. See Report an issue. Below that the menu holds the theme toggle' },\n { path: '/getting-started/console-tour', title: 'The console tour', heading: 'In-context help', anchor: '#in-context-help', text: 'Look for the small ? icons throughout the console — next to page titles, card titles, form fields, table column headers, and the Besigner\\'s style and attribute panels. Hover one for a one- or two-line explanation, and click it (or the Open documentation link in the tooltip) to jump straight to the matching section of these docs in a new tab.' },\n { path: '/getting-started/console-tour', title: 'The console tour', heading: 'Primary navigation', anchor: '#primary-navigation', text: 'The tabs across the top of a site are its sections. Some are always present; the rest are contributed by the plugins your workspace has enabled — so two sites, or the same site before and after you enable a plugin, can show different tabs. Don\\'t be surprised by a tab strip that doesn\\'t match a screenshot exactly.\\nThese four are where a new site\\'s work happens:\\n| Section | What\\'s there |\\n| Dashboard | The site at a glance — analytics and recent-signup summaries, and the ten most recent activity entries. |\\n| Screens | The screen hierarchy — create, reorder (drag-and-drop), and open screens. This is where you build; start at Publish your first screen. |\\n| Media | The media library — folders, images, video, and files. |\\n| Setup | Basic details, SEO, tracking, theme, and emails. Each tab is deep-linkable — the ?tab= in the URL follows you, so you can bookmark or share the exact one. |\\nAlongside them, and always present: Layouts and Components (the shared frames and reusable pieces screens are assembled from — see Screens & Layouts), Templates (saved starting points), Content (collections and blog entries), Users (the people who sign in to the site you\\'re building, not your own team), and Analytics. Admin appears only if you\\'re an owner or admin of the site; it holds per-site plugin settings, the custom domain, the site\\'s security lists, the full activity log, and the danger zone.\\nSections that come and go Tabs such as Data, Products, Logic, Automation, Inbox, Bookings, Events, Redirects, Marketing and Marketplace are contributed by plugins and appear only where that plugin is enabled for your workspace (they\\'re inserted after Analytics). If a section these docs describe isn\\'t in your tab strip, that\\'s usually why — enable the plugin under Organization → Plugins.\\nBilling is n' },\n { path: '/getting-started/console-tour', title: 'The console tour', heading: 'Editing vs. managing', anchor: '#editing-vs-managing', text: 'You can manage a screen (rename, schedule, view raw JSON) from its detail page without opening the editor. When you want to design it, open the Besigner.' },\n { path: '/getting-started/console-tour', title: 'The console tour', heading: 'The Sites list', anchor: '#the-sites-list', text: 'All Sites is the front door of a workspace: one card per site, for the workspace currently selected in the switcher. A site you can reach in another organization isn\\'t missing — it\\'s behind the workspace switcher.\\nEach card carries:\\n- The site\\'s display name as the title, with its hostname underneath as the subheader — the custom domain when the site has one, otherwise its name.aglyn.app address. Both are also listed inside the card, as Aglyn Domain and Custom Domain.\\n- A status pill (below).\\n- Visit, which opens the live site in a new tab, and Manage, which opens that site\\'s dashboard in the console.\\nThe status pill {#the-status-pill}\\n| Pill | Hover tells you |\\n| Live (green) | How many pages are published — \"12 published pages.\" |\\n| Draft (gray outline) | \"Nothing published yet — visitors see the placeholder.\" |\\n| Maintenance (amber) | \"Every path serves the maintenance screen.\" |\\n| Suspended (red) | \"This site is serving a lockdown notice instead of content.\" |\\nLive means exactly one thing: the site has at least one published page. Publishing writes the routing map the site is served from, and the pill is read off that map — so it costs no extra lookup on a list of a hundred sites, and it is not a health check. A site with a misconfigured domain still reads Live if it has published pages.\\nHow the pill is decided {#how-the-pill-is-decided}\\nReference detail, and the reason the pill is worth reading rather than glancing at: a site can be in several of these states at once, and the pill reports the first that applies, in this order.\\n1. Suspended — the site record carries a suspension that hasn\\'t ended.\\n2. Maintenance — maintenance mode is on.\\n3. Live — at least one published page.\\n4. Draft — everything else.\\nSo a suspended or maintenance site is never shown as Live, howe' },\n { path: '/getting-started/console-tour', title: 'The console tour', heading: 'A site\\'s dashboard', anchor: '#a-sites-dashboard', text: 'Opening a site lands you on its dashboard. It\\'s a place to glance at the site, not to edit it — every card is a summary with a link to the section that owns the detail:\\n- Traffic — pageviews over a window you choose (14 days by default). View details opens Analytics.\\n- Newest site users — the five most recent people who signed up on your site. View all opens Users.\\n- Recent Activity — the ten most recent changes, each naming the thing that changed (\"Saved the screen — Home\") and linking straight to it. A change an integration made through the REST API is attributed to its key by name — API key Zapier — rather than to a person. The full, paginated log lives under Admin → Activity.\\nPlugins contribute the rest, so this list is a floor rather than an exact match for your own dashboard: Last campaign appears once you\\'ve sent an email campaign, and Commerce where the commerce plugin is enabled and the site has products or orders. A brand-new site shows the same cards with empty states — \"No pageviews recorded yet\", \"No activity yet\" — which is what a first visit should look like.\\nTwo things people expect here and won\\'t find: role management lives under Users, and the announcement bar and promotional popup live under Marketing.\\nVisit site, in the dashboard header, opens the published site in a new tab with the admin bar armed — the route from a site\\'s own dashboard to the site itself, without going back out to the Sites list and in through a card action.' },\n { path: '/getting-started/console-tour', title: 'The console tour', heading: 'Next', anchor: '#next', text: '- Publish your first screen' },\n { path: '/getting-started/console-tour', title: 'The console tour', heading: 'Workspace settings & notifications', anchor: '#workspace-settings--notifications', text: 'Organization-wide settings (name, workspace URL) live under Organization → Settings, which also holds Profile, API keys, Branding, Single sign-on, Privacy, Ownership and Delete. Turning plugins on and off for the workspace is its own section, Organization → Plugins — Marketplace is for finding and installing new ones, not for administering what you already run:\\nYour in-app notification feed — billing, publishing, workflow failures — lives under Notifications, with per-category mutes:\\nDaily digests\\nDaily CRM digest, beside the category mutes, is on by default. Each morning it sends you one notification and one email listing your overdue and due-today tasks and the leads nobody has worked, across every workspace you belong to. Switch it off to stop both. It is account-wide, like the category mutes; muting Forms & bookings silences the digest\\'s console notification but not its email. What it counts, and when, is in Tasks & follow-ups.\\nWeekly insights appear beside it once you have turned them on for a workspace from Ask about your numbers, with one switch per workspace. They are off unless you turn them on. See Insights.\\nTask reminders — the notification and email a CRM task sends at its own due time — have no switch of their own. They are Forms & bookings notifications, beside Task assigned to you, and muting that category stops both the notification and the email; a single task is silenced by clearing its Remind me field. See Reminders.\\nAlerts on this device\\nBelow the category mutes, three switches control how a new notification reaches you in this browser:\\n- Unread count in tab title — badges the browser tab, e.g. (3) Aglyn, so you can see new activity from another tab. On by default.\\n- Sound — a short chime when a notification arrives. On by default on a browser you ha' },\n { path: '/getting-started/create-a-site', title: 'Create a site', heading: '', anchor: '', text: 'Create a site\\nA site is one website you own in Aglyn — its screens, theme, data, domain, and settings. Everything you build lives under a site, and you can own several.' },\n { path: '/getting-started/create-a-site', title: 'Create a site', heading: 'Create your first site', anchor: '#create-your-first-site', text: '1. Sign in to the console.\\n2. Open the site switcher in the app bar (top-left) and choose Create site.\\n3. Give it a name. Aglyn generates a working subdomain immediately, so the site has a real address from the first moment — you can attach a custom domain later. The address resolves right away, but it has nothing to serve until you publish a screen at the site root, so a brand-new site answers its own URL with a not-found page. Starting from a starter template creates and publishes a home page for you; building by hand, that\\'s your first screen.\\n4. You land on the new site\\'s Setup page (titled Host Setup), with tabs for Basic details, SEO, Theme, Custom Domain, Emails, and Activity. To start building rather than configuring, use the Screens tab in the site navigation — see Publish your first screen.\\nName collisions Site names must be unique enough to generate a valid subdomain. If a name is taken, the console suggests an available variation.' },\n { path: '/getting-started/create-a-site', title: 'Create a site', heading: 'What a site contains', anchor: '#what-a-site-contains', text: '- Screens & layouts — the pages and the shared frames they render in.\\n- Data — the datasets this site can see. Variables and functions are under Logic, and workflows and actions under Automation.\\n- Media — images, video, and files, organized in folders.\\n- Setup — theme, SEO, tracking, and emails under tabbed settings. The Site logo card (Details tab) sets your brand mark: it\\'s shown on the live site\\'s page-navigation loading overlay — a themed, blurred scrim with a progress bar. Without a logo, the loader shows your site name instead.\\n- Billing — the plan and usage meters that gate features and quotas.' },\n { path: '/getting-started/create-a-site', title: 'Create a site', heading: 'Switching between sites', anchor: '#switching-between-sites', text: 'Use the site switcher in the app bar at any time. It lists your most recently used sites first; with more than a handful, type in the Find site… box to search every site you belong to by name, or choose View all sites. Your current site\\'s display name appears in the breadcrumbs so you always know which site you\\'re editing.' },\n { path: '/getting-started/create-a-site', title: 'Create a site', heading: 'Next', anchor: '#next', text: '- Take the console tour\\n- Publish your first screen' },\n { path: '/getting-started/publish-your-first-screen', title: 'Publish your first screen', heading: '', anchor: '', text: 'Publish your first screen\\nThis walks through the core loop: create → design → publish.\\nPublish from the editor You don\\'t have to leave the Besigner to go live: the Publish button in the top-right of the editor publishes the version you\\'re editing (and flips to Unpublish once the screen is live). Screens without a URL path yet are prompted to set one in Properties first.' },\n { path: '/getting-started/publish-your-first-screen', title: 'Publish your first screen', heading: '1. Create a screen', anchor: '#1-create-a-screen', text: '1. Go to Screens and choose New screen.\\n2. Set a title and a URL slug (e.g. about). Aglyn normalizes the slug and stores it on the screen, ready for when you publish.\\n3. Optionally pick a parent screen — children inherit a nested URL path (/services/pricing).\\nA new screen is a draft Creating a screen doesn\\'t put anything on your live site — its Date published stays empty and nothing resolves at its address until you publish it in step 3. Design it first, publish when it\\'s ready.' },\n { path: '/getting-started/publish-your-first-screen', title: 'Publish your first screen', heading: '2. Design it in the Besigner', anchor: '#2-design-it-in-the-besigner', text: '1. Open the screen to launch the Besigner.\\n2. Drag components from the drawer onto the canvas. Rearrange them in the hierarchy panel or directly on the canvas.\\n3. Double-click text to edit it inline. Set component attributes in the inspector.\\n4. Bind a shared layout if you want a common header/footer.' },\n { path: '/getting-started/publish-your-first-screen', title: 'Publish your first screen', heading: '3. Preview and publish', anchor: '#3-preview-and-publish', text: '1. Use the artboard light/dark toggle to check both color schemes.\\n2. Save the canvas. Saving writes your working version — it isn\\'t publishing.\\n3. Publish. The screen\\'s slug is registered in the routing map and the page goes live on your site\\'s domain.\\nKeeping more than one version is a Pro feature Every screen is created with one working version, and Save updates that version on every plan — the core loop above needs nothing extra. Keeping more than one — named snapshots you can reopen, publish to roll back to, or schedule — requires Pro or above, and scheduling a version requires Business or above. On Free and Starter the editor answers \"Versioning requires a Pro plan — see Billing to upgrade\" instead of opening the name dialog. See Versions & scheduled publishing.\\nThe Live button The Live button is environment-aware — in production it reflects your published saves so you can jump straight to the real page.' },\n { path: '/getting-started/publish-your-first-screen', title: 'Publish your first screen', heading: 'How fast changes go live', anchor: '#how-fast-changes-go-live', text: '- Publishing a screen refreshes its live page immediately — the very next visitor gets the new version, usually within seconds.\\n- Publishing a layout or a reusable component refreshes every page that uses it the same way. When that fan-out is very large, the publish confirms that the remaining pages \"update on their own within an hour\" — and they do.\\n- Saving is not publishing. A save writes your working version; the live site serves the published one. The one subtle case: if you edit the version that is currently live and press Save, the live site catches up on its own within about a minute — and because pages regenerate behind the scenes, you may need to refresh twice to see it.\\n- Your site\\'s sitemap.xml, robots.txt, and RSS feeds refresh within about five minutes (a publish refreshes the sitemap immediately).' },\n { path: '/getting-started/publish-your-first-screen', title: 'Publish your first screen', heading: 'Next', anchor: '#next', text: 'Explore the feature areas in the sidebar, or see What\\'s New for the latest capabilities.' },\n { path: '/guides/build-and-publish-a-survey', title: 'Build & publish a survey', heading: '', anchor: '', text: 'Build & publish a survey\\nThis walkthrough strings together datasets, the Besigner\\'s form elements, and publishing into one loop: model the responses → design the survey → publish → collect. By the end you have a live survey page whose submissions land in a typed dataset you can filter, sort, and export.\\nPlan availability Forms and the inbox are available on every plan. Writing submissions into a dataset needs the data store, which unlocks on Starter and above.' },\n { path: '/guides/build-and-publish-a-survey', title: 'Build & publish a survey', heading: '1. Create the dataset', anchor: '#1-create-the-dataset', text: 'Datasets belong to your organization, so every site can share them. Open the organization Data page (or any site\\'s Data page — both edit the same data).\\n1. Choose Add dataset. In the New dataset dialog give it a Name (say Survey responses) and a comma-separated list of Fields (satisfaction, visit, topics, comments) — at least one, or Create stays disabled. Create it.\\n2. Select your new dataset in the Dataset dropdown and open Schema. The Schema dialog lists each field with its display name, generated field id, and a type chip.\\n3. Use Add field / Edit to type the fields properly:\\n- satisfaction → type Integer. A form stores every value as text, so the rating lands as \"4\"; the Data page\\'s filter and sort still compare it as a number.\\n- visit → type Text (one radio choice).\\n- topics → type Text (ticked checkboxes arrive joined with , ).\\n- comments → type Text.\\n4. Save schema.\\nEach field\\'s Display name is what you see in dialogs and table headers; the field id next to it (a slug generated once from the display name) is the stable key records store values under — the key {{item.fieldId}} bindings reference, and what a form field\\'s Maps to schema field picker stores. Renaming the display name later never changes the id.\\nModel first A clean schema now makes everything downstream typed: the record editor renders the right input per field and imports are validated. Form submissions are the exception — they are stored as text without validation, so a submitted value can disagree with its field\\'s type. See the datasets deep-dive for the full type system.' },\n { path: '/guides/build-and-publish-a-survey', title: 'Build & publish a survey', heading: '2. Add a screen for the survey', anchor: '#2-add-a-screen-for-the-survey', text: '1. Go to Screens and choose New screen.\\n2. Title it Survey and give it the slug survey — that becomes its public URL path (https://your-site/survey).\\n3. Open it in the Besigner.' },\n { path: '/guides/build-and-publish-a-survey', title: 'Build & publish a survey', heading: '3. Insert a Form from the element picker', anchor: '#3-insert-a-form-from-the-element-picker', text: 'With the screen open, add the form:\\n1. In the Elements panel choose Add Element (or right-click a node and pick Add element). The Choose element picker opens, grouped by category.\\n2. Expand the Forms group. It contains the Form container, the Form Field element, and a ready-made Contact Form preset.\\n3. Insert a Form. Then, with the Form selected, add one Form Field child per question (the picker\\'s Form Field preset drops a field with sensible defaults).' },\n { path: '/guides/build-and-publish-a-survey', title: 'Build & publish a survey', heading: '4. Configure the fields', anchor: '#4-configure-the-fields', text: 'Select each Form Field and set its properties in the inspector:\\n| Property | What it does |\\n| Field name | The key the value is stored under in submissions (and in the inbox copy). |\\n| Maps to schema field | Which of the dataset\\'s schema fields this value is stored under. The dropdown lists the fields of the dataset chosen on the Form (see step 5) and stores the stable field id, so renaming the field later never breaks the mapping. Left on None, the value matches a dataset field by name instead. |\\n| Label | The visible input label (\"How satisfied are you?\"). |\\n| Placeholder | An optional gray hint inside the empty input (\"you@company.com\"). It is not a label — it disappears as soon as someone types, so always keep Label set. Radio, checkbox, and rating fields ignore it; a dropdown shows it until a choice is made. Clear it to remove the hint. |\\n| Type | Text (default), Email, Multiline, Dropdown, Radio choice, Checkboxes, or Star rating. |\\n| Options | Choices for dropdown, radio, and checkbox fields — one per line or comma-separated. Ignored by other types. |\\n| Required? | Whether the field must be filled; a required checkbox group needs at least one box ticked. |\\nPick the dataset first \"Maps to schema field\" lists the fields of whichever dataset the Form is pointed at — set the Form\\'s Write to dataset (step 5) before mapping the fields, or the dropdown reads No dataset selected on the form.\\nFor this survey:\\n1. satisfaction — Type Star rating, label \"How satisfied are you?\". The five stars submit their count (4), which the form stores as text.\\n2. visit — Type Radio choice, Options First time, Monthly, Weekly.\\n3. topics — Type Checkboxes, Options Products, Support, Pricing. Visitors can tick several; the submission joins them with , .\\n4. comments — Type Multiline for free' },\n { path: '/guides/build-and-publish-a-survey', title: 'Build & publish a survey', heading: '5. Point the form at the dataset', anchor: '#5-point-the-form-at-the-dataset', text: 'Select the Form container itself and fill its properties:\\n- Form name — identifies the form in your submissions inbox (Visitor survey).\\n- Write to dataset — pick Survey responses from the dropdown, which lists your organization\\'s datasets by display name. The form stores the dataset\\'s id, so renaming the dataset later never breaks the binding. Submissions append a record; the inbox always keeps its own copy either way.\\n- Submit label — the button text (defaults to Send).\\n- Success message — shown in place of the form after a successful submit.\\nRename-safe by id Both pickers bind by stable id: the Form stores the dataset\\'s document id and each field\\'s \"Maps to schema field\" stores the model field id. Rename the dataset or its fields freely — records keep landing. Unmapped values still match schema fields by name (field name = field id); values that match nothing are dropped from the record (the inbox copy still has everything). Forms built before the pickers keep working through their stored dataset name — shown as Write to dataset (legacy name) until you pick the dataset above and clear it.' },\n { path: '/guides/build-and-publish-a-survey', title: 'Build & publish a survey', heading: '6. Publish', anchor: '#6-publish', text: 'Click Publish in the Besigner\\'s top-right (it flips to Unpublish once live). If the screen has no URL path yet you\\'re prompted to set one first. The survey is now served at /survey on your site\\'s domain.' },\n { path: '/guides/build-and-publish-a-survey', title: 'Build & publish a survey', heading: '7. Watch responses arrive', anchor: '#7-watch-responses-arrive', text: 'Every submission does three things:\\n- appends a record to Survey responses — the Data page updates live;\\n- files an inbox copy under the form\\'s name (see Forms & lead capture);\\n- fires the formSubmission automation event, so workflows and actions can react (send a thank-you alert, notify your team, write other datasets).\\nOn the Data page, use Filter (satisfaction >= 4) and Sort (satisfaction desc) over the loaded records, and export everything with the CSV / JSON buttons.\\nChart it Bind a container\\'s Repeat over dataset to Survey responses and reference {{item.satisfaction}} inside it to render responses back onto a (members-only?) results screen. The deep-dive covers repeatables.' },\n { path: '/guides/build-and-publish-a-survey', title: 'Build & publish a survey', heading: 'Related', anchor: '#related', text: '- Datasets & schema deep-dive\\n- Forms & lead capture\\n- Publish your first screen\\n- Workflows & actions' },\n { path: '/guides/coming-soon-launch', title: 'Launch a coming-soon page', heading: '', anchor: '', text: 'Launch a coming-soon page while the rest of your site stays private\\nYou have a domain, a half-built site, and a launch date. You want visitors to land on something deliberate — \"here\\'s what\\'s coming, tell me when it\\'s ready\" — while the forty pages behind it stay out of Google until you\\'re happy with them.\\nThis walkthrough is the whole loop: build the page → make it home → hide the rest → collect signups → flip everything back on launch day. It\\'s the same pattern Aglyn uses on its own site.\\nPlan availability Everything here works on Free: screens, forms, the inbox, per-screen visibility, and the site-wide search switch. Writing signups into a dataset (step 4) needs the data store, which unlocks on Starter and above — the inbox copy is always kept either way.' },\n { path: '/guides/coming-soon-launch', title: 'Launch a coming-soon page', heading: '1. Build the coming-soon page', anchor: '#1-build-the-coming-soon-page', text: 'Create the screen first; you\\'ll point your domain at it in step 2.\\n1. Go to Screens → New screen. Title it Coming soon. Give it any slug for now — coming-soon is fine — you\\'ll change it to / in the next step.\\n2. Open it in the Besigner and put the essentials on it. A coming-soon page that only says \"coming soon\" wastes the visit. Aim for:\\n- What\\'s coming, and roughly when. One sentence and a month beats a countdown timer you\\'ll have to move.\\n- A notify-me form — see below.\\n- How to reach a human. An email address or a contact link; people who arrive early are often the ones who most want to talk to you.\\n- Links out — your documentation, your changelog, your social accounts, wherever you already post. These give an early visitor somewhere to go instead of bouncing.\\n3. Publish the screen.\\nThe notify-me form\\nAdd a Form from the element picker with a single Form Field of type Email, and set the Form\\'s Success message to something that confirms the signup (\"Thanks — we\\'ll email you when we open\"). The Forms & lead capture page covers the form elements and every field property in full; the only thing that matters here is that you give the field a Field name you\\'ll recognize later, such as email.' },\n { path: '/guides/coming-soon-launch', title: 'Launch a coming-soon page', heading: '2. Make it the home page', anchor: '#2-make-it-the-home-page', text: 'A screen\\'s Slug decides the path it\\'s served at, and the slug / means the home page. So swapping your home page is two slug edits.\\nChange the slug, don\\'t delete the screen Move your existing home page aside rather than deleting it. Its design, its content, and its version history are all worth keeping, and you\\'ll want it back in step 5.\\n1. Open your current home screen (Sites → your site → Screens → click the row). In the Publishing card, change Slug from / to something you\\'ll recognize — home works — and choose Update route. The screen stays published, just at /home now.\\n2. Open the Coming soon screen. Change its Slug to / and choose Update route (or Publish, if you haven\\'t published it yet).\\n3. Load your site. The coming-soon page is now what visitors get.\\nA screen can be the home page or have a parent, but not both — if the slug won\\'t take, check that the screen sits at the top level of your screens list.\\nChanging a slug does not create a redirect Aglyn rewrites its own routing map when you change a slug, so links within your site keep working. It does not leave a redirect behind at the old path — that URL simply stops resolving. If the old path was public and people have linked to it, create the rule yourself with the redirect manager (a paid feature) — see Create a redirect.' },\n { path: '/guides/coming-soon-launch', title: 'Launch a coming-soon page', heading: '3. Keep everything else out of search', anchor: '#3-keep-everything-else-out-of-search', text: 'Your unfinished pages are still published and still reachable. Two controls decide whether search engines index them, and which one you want depends on how far along you are.\\nWhile nothing is ready: the site-wide switch\\nGo to Setup → SEO and turn on Discourage search engines from indexing this site. One switch, whole site:\\n- robots.txt refuses every crawler (Disallow: /) and stops naming your sitemap\\n- sitemap.xml is served empty\\n- every page — including your coming-soon page — carries\\nThis is the right choice while the site is a building site. It also covers pages you haven\\'t created yet, which per-screen visibility can\\'t: a screen you publish next Tuesday is indexable by default, so a site hidden page-by-page slowly un-hides itself as it grows.\\nWhile the switch is on, a warning banner follows you through the console on every page of that site. That\\'s deliberate, and it\\'s the answer to the most common support question this feature creates — see step 5.\\nOnce you\\'re launching page by page: per-screen visibility\\nWhen the site is mostly ready and you\\'re releasing sections, turn the site-wide switch off and hide the stragglers individually. Open a screen (Screens → pick it) and use the Visibility dropdown in its Page Access card:\\n| Visibility | Reachable by | In search |\\n| Public | anyone | yes |\\n| Unlisted | anyone with the link | no |\\n| Password protected | anyone with the password | no |\\n| Members only | signed-in members | no |\\nUnlisted is the one you want for a finished-but-not-announced page: it stays live so you can share the link with a reviewer, and it\\'s kept out of both search results and your sitemap.\\nnoindex is a request, not a lock Unlisted and the site-wide switch ask search engines not to list a page. Well-behaved crawlers honor that; nothing stops a person w' },\n { path: '/guides/coming-soon-launch', title: 'Launch a coming-soon page', heading: '4. Collect the signups', anchor: '#4-collect-the-signups', text: 'Every form submission is filed in your Inbox under the form\\'s name, on every plan. That alone is enough to email people on launch day.\\nTo get a list you can actually query, point the form at a dataset:\\n1. Create a dataset on the organization Data page → Add dataset, called Launch signups, with a field for the address — email.\\n2. Back in the Besigner, select the Form container and set Write to dataset to Launch signups in the Attributes panel.\\n3. Select the email Form Field and set Maps to schema field to email.\\nThat mapping is what makes the record land, and it\\'s worth doing explicitly. The picker stores the dataset field\\'s stable Reference ID, so renaming the field later never breaks it.\\nLeft on None (match by field name), the value falls back to matching a dataset field whose Reference ID — the snakecase key beside each field in the Schema dialog, not its Display name — is identical to the form field\\'s Field name. So a form field named email lands in a dataset field whose Reference ID is email with no mapping at all, while one named Your email silently doesn\\'t.\\nValues that match nothing are dropped from the record without failing the submission, and the same is true of a deleted dataset or an exceeded record quota — the append is best-effort by design, so a data-store problem never costs you a signup. The inbox copy always keeps everything, which is why it\\'s the thing to check if a record looks wrong.\\nThe survey walkthrough covers datasets, schema types and the form pickers in much more depth.' },\n { path: '/guides/coming-soon-launch', title: 'Launch a coming-soon page', heading: '5. Launch day: reverse every step', anchor: '#5-launch-day-reverse-every-step', text: 'Turning a site dark is easy to do and easy to forget. Here is the whole reversal — work down it, then verify with step 6.\\n1. Allow search engines again. Setup → SEO → turn Discourage search engines from indexing this site off. The console banner disappears, and robots.txt, the sitemap and the page meta all return within about a minute.\\n2. Set the hidden screens back to Public. The site-wide switch and per-screen visibility are independent — turning the switch off does not promote an Unlisted screen. Go through Screens and set each one\\'s Visibility back to Public in its Page Access card. Anything you leave Unlisted stays out of search, silently and indefinitely.\\n3. Swap the home page back. Open Coming soon, change its Slug to something else (coming-soon) and Update route — or Unpublish it if you\\'re done with it. Then open your real home screen and set its Slug back to /.\\n4. Verify — step 6. Don\\'t skip it. Every step above is a setting that can be half applied, and the failure is silent.\\n\"Why is my site not on Google?\" Nine times out of ten it\\'s this feature, left on. Check, in order: the Setup → SEO switch, then each screen\\'s Visibility. Then remember that re-indexing is the search engine\\'s schedule, not yours — a site that was noindex for a month does not come back the afternoon you fix it. Allow days, and use Google Search Console to request indexing if you\\'re in a hurry.' },\n { path: '/guides/coming-soon-launch', title: 'Launch a coming-soon page', heading: '6. Verify it actually worked', anchor: '#6-verify-it-actually-worked', text: 'Three things a crawler reads, and how to read them yourself. Run these against your real domain.\\nrobots.txt — visit https://your-site/robots.txt, or:\\nHidden looks like this — note that no sitemap is named:\\nVisible looks like this:\\nThe page\\'s robots meta — open the page, View source, and search for robots. Or:\\nA hidden page returns . A visible page returns nothing at all — no robots meta means \"index me\", which is the default.\\nThe sitemap — visit https://your-site/sitemap.xml. While the site-wide switch is on it is an empty ; once you launch it becomes a sitemap index naming one child sitemap per section of your site. To count the URLs you are actually submitting, follow the index into its children:\\nIt should be 0 while the site-wide switch is on, and after launch it should match your public pages. Any page you expect to see and don\\'t is a screen still set to Unlisted.\\nGive it a few minutes These three files are cached for about five minutes. If a change hasn\\'t shown up, wait a beat and re-run rather than changing another setting.' },\n { path: '/guides/coming-soon-launch', title: 'Launch a coming-soon page', heading: 'Related', anchor: '#related', text: '- SEO Toolkit — search engine visibility in full\\n- Forms & lead capture\\n- Build & publish a survey — datasets and form mapping\\n- Site protection — when noindex isn\\'t enough\\n- Create a redirect' },\n { path: '/guides/commerce-end-to-end', title: 'Commerce end to end', heading: '', anchor: '', text: 'Commerce end to end\\nThis walkthrough runs the whole selling loop: catalog → storefront → checkout → orders → subscriptions. It links into the commerce reference rather than repeating it — the goal here is the end-to-end path.\\nPlan availability Selling needs a paid plan (fees drop as you upgrade — see Billing & plans). Storefront subscriptions (including the subscribe side of \"Both — buyer chooses\") need Business and above.' },\n { path: '/guides/commerce-end-to-end', title: 'Commerce end to end', heading: '1. Connect payments', anchor: '#1-connect-payments', text: 'Aglyn sells from your own Stripe account via Stripe Connect. On the Products hub, the payments card walks you through Express onboarding; checkout stays disabled (\"This site has not enabled payments yet\") until Stripe reports charges enabled. Aglyn adds its per-sale platform fee automatically per your plan.' },\n { path: '/guides/commerce-end-to-end', title: 'Commerce end to end', heading: '2. Create products', anchor: '#2-create-products', text: 'On the site\\'s Products page (Catalog tab), choose Add product. The editor dialog covers everything the catalog reference describes:\\n- Basics — Name, Slug (/products/{slug}), Type (Physical / Digital / Service), Status (Draft / Active / Archived), description, tags, categories, media.\\n- Options & variants — up to 3 options expand into a variants matrix with per-variant Price, Compare-at, SKU, Barcode, Stock, and Weight. Blank stock = untracked; 0 shows sold out.\\n- Billing — how buyers pay:\\n- One-time purchase (default) — a normal order.\\n- Monthly subscription / Yearly subscription — the product page prices per interval and the buy button becomes Subscribe.\\n- Both — buyer chooses — the product page shows a one-time vs subscribe toggle (defaulting to one-time); pick the Interval the subscribe side bills at. The buyer\\'s choice is validated server-side against the product.\\n- Any subscription mode reveals Free trial (days) — the trial precedes the first charge.\\n- Digital delivery (digital products) — attach files from the media library, optionally versioned, with a per-order Download limit. Buyers always download the current files; uploading a new version re-delivers. An attached file is made private, and each download hands the buyer a link that works for one hour; see Members videos are private files.\\nSet the product\\'s Status to Active — drafts are invisible to visitors.' },\n { path: '/guides/commerce-end-to-end', title: 'Commerce end to end', heading: '3. Design the storefront', anchor: '#3-design-the-storefront', text: 'Open a screen in the Besigner and add blocks from the Commerce group of the element picker:\\n- Product grid — a responsive catalog grid. Its Show property scopes it to all products, a collection, a category, or a tag; set Columns, Sort, a cap, and optional visitor-facing filter chips. Cards link to /products/{slug}.\\n- Product detail — gallery, variant picker, price (with sale badge, trial caption, and /mo·/yr when subscribing), Add to cart, and the buy button (Buy now / Subscribe, or the one-time-vs-subscribe toggle for \"Both\" products). Leave its Product slug blank on a template screen so it follows the URL. The Product page preset drops the whole commerce-standard page in one go: a Shop / {{product.name}} breadcrumb, the detail block, a You may also like strip, and product reviews.\\n- Cart — two presets: a Cart button (app-bar icon with an item-count badge and a slide-out drawer — the badge updates live as blocks add to the cart) and a Cart page (inline, with coupon and gift-card fields). Both end in a Checkout button.\\nCatalog search, filters, and sort\\nThe Product grid carries a toggle per storefront catalog control, so a bare strip on the home page and a full shop page are the same block:\\n- Search box — a debounced search field above the grid; matches product names, descriptions, and tags.\\n- Category chips — one chip per product category (plus All), built from the categories you manage in the Products hub. Visitors tap to filter.\\n- Sort select — newest, name, price low→high, price high→low, right-aligned above the grid.\\n- Type filter — physical / digital / services chips, useful for mixed catalogs.\\n- Price filter — a two-thumb price range slider, automatically bounded by the lowest and highest prices among the products currently showing (variant-priced products count b' },\n { path: '/guides/commerce-end-to-end', title: 'Commerce end to end', heading: '4. What checkout does', anchor: '#4-what-checkout-does', text: 'Both buy buttons and the cart\\'s Checkout redirect to Stripe-hosted Checkout, then back to your site with a success/canceled marker:\\nPaying without leaving your site\\nThere is a second checkout that keeps the shopper on your own pages: instead of sending them to checkout.stripe.com, the card form opens in place, below the Buy or Checkout button, styled with your site\\'s theme. Leaving the store mid-purchase is one of the most common places a cart gets abandoned, so this exists to remove that step.\\nRolling out. In-page checkout is off by default and is switched on per workspace by Aglyn staff (release flag In-page checkout). Until it is on for your workspace, checkout behaves exactly as described above — the redirect is what your shoppers see.\\nNothing else about a sale changes when it is on, and that is deliberate:\\n- The price, tax, shipping and any coupon are computed by the same code. The in-page form is the same Stripe Checkout Session as the redirect, just drawn by us instead of by Stripe — so the two cannot quote different totals.\\n- Your order is still created by Stripe\\'s webhook, never by the browser. If a shopper pays and immediately closes the tab, the order, the stock decrement, the receipt and the gift-card or license-key steps all still happen. If they pay and then refresh the page, none of it happens twice.\\n- A declined card stays on the form. The basket, address and payment method the shopper already entered are kept, with the decline shown beside them, so they can try another card without starting over. Cards that ask for a bank verification step (3-D Secure) show that challenge in place.\\n- Abandoned in-page checkouts are still recoverable and still feed the abandoned-cart emails, the same as an abandoned redirect.\\nA shopper who cancels the in-page form is ret' },\n { path: '/guides/commerce-end-to-end', title: 'Commerce end to end', heading: '5. Run orders from the console', anchor: '#5-run-orders-from-the-console', text: 'The Products hub\\'s Orders tab lists every sale with product, period, status, and channel filters plus Export CSV and Draft order (build an order by hand and send a payment link).\\nExport CSV writes the orders currently shown — the filters apply — as orders.csv, one row per order: date, product, amountUsd, feeUsd, customerEmail, coupon, orderId, status, channel, refundedUsd, netUsd. amountUsd is the gross charge, so subtract refundedUsd (or read netUsd) when reconciling against a Stripe payout. Orders with several line items name the first and count the rest, e.g. Blue Mug +2 more.\\nOpen an order for the detail dialog — customer, line items, totals, timeline, and internal notes:\\n- Fulfill… — enter a carrier and tracking number; the order moves to fulfilled and the buyer\\'s account page shows the tracking. Mark delivered closes the loop, and Packing slip prints one.\\n- Refund — confirms \"Refund this order?\", then refunds the buyer through Stripe (full or partial) and reverses the platform fee. Site-admin role only.\\n- Cancel order — for orders that shouldn\\'t proceed (does not auto-refund).\\nStatuses move through a guarded machine: pending → paid → fulfilled → delivered, with cancel/refund exits — see the orders reference.' },\n { path: '/guides/commerce-end-to-end', title: 'Commerce end to end', heading: '6. Subscriptions & the Stripe portal', anchor: '#6-subscriptions--the-stripe-portal', text: 'Active subscriptions are recorded per site and surface in two places:\\n- Buyers manage their own — the Customer account block\\'s Subscriptions section has a Manage button that opens the Stripe Billing Portal (update card, cancel, see invoices).\\n- You see each member\\'s subscriptions in the member drawer on the site\\'s Users page — what they subscribed to, the amount and interval, and the renewal date — and managers get a New subscriber notification carrying the amount on each signup, then a Subscription renewed notification carrying the amount on every cycle after it.\\nWhat a subscription puts in Orders depends on the product\\'s Type:\\n- A physical subscription creates one order per paid cycle — the first charge included — on the Subscription channel, so every box has something to fulfill, pack and label against, and recurring revenue shows up in the Orders tab, the analytics card and the orders CSV alongside one-time sales. Each cycle\\'s order also decrements the variant\\'s stock (if tracked) and can trigger the low-stock alert.\\n- A digital or service subscription is not an order, by design: it does not appear in Orders, in the analytics card or in the orders CSV, and it does not decrement stock — there is nothing to ship.\\nEither way, what the subscriber pays is recorded on the subscription itself — each paid invoice, its period and its amount included, so a renewal is a record you hold rather than one only Stripe holds. The amount shown follows the latest cycle, so a price change, a tax change or a trial converting to a paid cycle is reflected rather than frozen at what the first charge was. Stripe keeps its own copy of every invoice, reachable from the Billing Portal.\\nAn active subscription is also what members-only content can check for paid gating.' },\n { path: '/guides/commerce-end-to-end', title: 'Commerce end to end', heading: 'Related', anchor: '#related', text: '- Commerce reference\\n- Product catalog\\n- POS & reservations\\n- Member accounts' },\n { path: '/guides/datasets-and-schema', title: 'Datasets & schema deep-dive', heading: '', anchor: '', text: 'Datasets & schema deep-dive\\nThis guide goes one level below the datasets overview: how the typed model actually works, what the limits are, and every path data takes in and out of a dataset.' },\n { path: '/guides/datasets-and-schema', title: 'Datasets & schema deep-dive', heading: 'Display names vs field ids', anchor: '#display-names-vs-field-ids', text: 'A dataset field has two names, and the distinction matters everywhere:\\n- The display name is what you type in the schema dialog and what table headers and record editors show. Rename it freely.\\n- The reference id is the stable key. It auto-fills from the display name when you create the field (Visit frequency → visitfrequency), and the schema dialog lets you edit it right there to set your own — for example a short, code-friendly key that differs from the label. Once the field is created the id is fixed and never changes. Records store values keyed by reference id — it\\'s the key you use in {{item.referenceid}} bindings and what a form field\\'s Maps to schema field picker stores.\\nThe schema dialog shows both — each field row reads Display name · fieldId. The Reference ID input sits directly under Display name in the field editor; it follows the name as you type until you edit it, then keeps your value. The dataset itself likewise has a Singular name and a Plural name; the plural is the dataset\\'s display name — the label its entry shows in \"Write to dataset\" and repeatable pickers (which store the dataset\\'s id underneath, so renames never break the bindings).' },\n { path: '/guides/datasets-and-schema', title: 'Datasets & schema deep-dive', heading: 'Naming & describing fields', anchor: '#naming--describing-fields', text: 'Open the schema dialog and Edit a field to change how it presents:\\n- Display name — rename it freely, any time. The field id underneath never changes, so {{item.fieldId}} bindings, form field mappings, and stored records are all unaffected by a rename.\\n- Description — a free-form note on what the field is for. When set, it follows the field around the console:\\n- the schema dialog\\'s field list shows it under the Display name · fieldId line,\\n- the records table shows it as a tooltip on the column header,\\n- the record editor shows it as the input\\'s helper text (a validation error takes its place while present).\\nBoth are trimmed on save, and a blank description is dropped rather than stored. Descriptions are console-facing documentation only — they never render on the published site.' },\n { path: '/guides/datasets-and-schema', title: 'Datasets & schema deep-dive', heading: 'The typed model', anchor: '#the-typed-model', text: 'A dataset\\'s model is a map of field definitions plus a display order. Each field definition carries its type, whether it\\'s Required, an optional Description and Default value, validation (regex pattern, min/max length, an enum of Options), and — for reference fields — the target dataset and delete behavior.\\nThe types you can author:\\n| Type | Holds |\\n| Text | Strings; optionally pattern-, length-, or enum-constrained |\\n| Boolean | true / false |\\n| Integer | Whole numbers |\\n| Number | Floating-point numbers |\\n| Date & time | Timestamps |\\n| Coordinates | Latitude / longitude pairs |\\n| List | Ordered arrays |\\n| Reference | A link to a record in another dataset — see relations |\\nPlugins can register custom field types, which appear in the type picker suffixed with the plugin id. The console, imports, site restores and the REST API validate records against the model. Form submissions and the automation dataset steps do not: they store each value as text.\\nLegacy datasets Datasets created before typed models (a flat comma-separated field list) still work: the runtime derives an effective model where every column is an optional text field whose id equals the column name. Open the schema dialog and re-type the fields to upgrade — ids are preserved.\\nReference fields configure what happens when a referenced document is deleted: Clear the reference (default) or Block the delete. Tick Allow multiple for many-to-many relations.' },\n { path: '/guides/datasets-and-schema', title: 'Datasets & schema deep-dive', heading: 'Record quotas per plan', anchor: '#record-quotas-per-plan', text: 'Dataset counts, records per dataset and dataset storage are entitlements of your organization\\'s plan:\\n| Plan | Datasets | Most with add-ons | Records per dataset | Dataset storage |\\n| Free | — (no data store) | — | — | — |\\n| Starter | 3 | 10 | 1,000 | 1 GB |\\n| Pro | 15 | 50 | 10,000 | 5 GB |\\n| Business | 100 | 250 | 100,000 | 25 GB |\\n| Scale | 250 | 500 | 500,000 | 50 GB |\\n| Advanced | 500 | 1,000 | 1,000,000 | 100 GB |\\n| Agency | 2,000 | 5,000 | Unlimited | 500 GB |\\n| Enterprise | Custom | Custom | Custom | Custom |\\n- Creating datasets on Free is rejected with \"Datasets require a Starter plan or higher.\"\\n- Hitting the record cap surfaces \"Record limit reached — see Billing to upgrade\" in the console. An import that would overflow writes the rows that fit and skips the rest, reporting them as over the record limit.\\n- Form submissions never fail on quota — if the dataset is full the record write is skipped silently, but the inbox copy is always kept.\\n- Add-ons raise the dataset count beyond the base allowance, up to the most shown above.\\n- Dataset storage past the included amount bills at $0.36 per GB-month from Starter to Agency. Enterprise has no overage rate, so writes stop at its limit.' },\n { path: '/guides/datasets-and-schema', title: 'Datasets & schema deep-dive', heading: 'Import & export', anchor: '#import--export', text: 'The Data page round-trips CSV and JSON:\\n- Export — the CSV and JSON toolbar buttons download every record in the dataset, not only the rows loaded in the table, with one column per model field.\\n- Import — the Import records dialog takes pasted CSV (with a header row) or a JSON array. Columns match by field id first, then case-insensitively by display name; rows are validated against the model on the way in.\\n- Upsert — pick a Match on field (upsert) key (say, an email field) and matching rows update records in place instead of appending duplicates. Only genuinely new rows count against the record quota.\\nSee import & export for round-trip tips.' },\n { path: '/guides/datasets-and-schema', title: 'Datasets & schema deep-dive', heading: 'Repeatables', anchor: '#repeatables', text: 'Containers (like a Stack) have a Repeat over dataset property. Set it and the container\\'s children become an item template, rendered once per record on the published site:\\n1. Select the container and set Repeat over dataset to your dataset (stored by id, so renames never break it).\\n2. Inside the children, reference record values with {{item.fieldId}} — e.g. {{item.name}}, {{item.price}}.\\n3. Reference fields support one hop: {{item.author.name}} resolves the referenced record\\'s field.\\n4. Optionally set a Repeat limit, a Repeat filter (price , >=, <, <=, contains), and a Repeat sort (price desc).\\nThe canvas doesn\\'t expand rows while designing — it shows a repeat notice on the container (\"children render once per record on the live site\") so you design a single template. Up to 100 records render per repeatable.' },\n { path: '/guides/datasets-and-schema', title: 'Datasets & schema deep-dive', heading: 'Everything that writes records', anchor: '#everything-that-writes-records', text: '| Writer | How it behaves |\\n| Console — Add record / record editor | Typed inputs per field; validated and quota-checked. |\\n| Console — Import records | Bulk CSV/JSON with validation, upsert, and quota math. |\\n| Forms — the Form element\\'s Write to dataset | Appends on each submission. The dataset is bound by id (a dropdown; legacy forms still match by name) and each form field can map to a schema field by id via Maps to schema field, with name-matching as the fallback — so renames never lose data. Best-effort (quota-full submissions keep only the inbox copy). See the survey guide. |\\n| Automations — the Write to a dataset step | Appends a record from a workflow/action run. |\\n| Automations — the Update a dataset record step | Updates a matching record (by email) or appends when none matches. |\\nAutomation steps live in the Do list of an action — see workflows & actions.' },\n { path: '/guides/datasets-and-schema', title: 'Datasets & schema deep-dive', heading: 'Deleting a dataset', anchor: '#deleting-a-dataset', text: 'Delete removes the dataset and every record in it, permanently — the confirmation tells you how many documents go with it. There is no undo and no retention window, so export first (CSV or JSON) if you might want the rows back. Deleting a single record from the table leaves the dataset itself alone.\\nThe same applies to the two other things that hold records inside them: deleting an email list takes its enrolled subscribers, and deleting a collection takes the content entries published under it.' },\n { path: '/guides/datasets-and-schema', title: 'Datasets & schema deep-dive', heading: 'Related', anchor: '#related', text: '- Build & publish a survey\\n- Datasets overview\\n- Build a data model\\n- Relations\\n- Bindings, variables & functions' },\n { path: '/guides/install-your-first-plugin', title: 'Install your first marketplace item', heading: '', anchor: '', text: 'Install your first marketplace item\\nThe Marketplace is where you add capability to Aglyn that you didn\\'t build yourself: plugins, ready-made site templates, themes, layouts, components, dataset schemas and email templates. This guide walks the whole loop once — find, install, use, upgrade, remove — clicking every step.\\nNo code, and nothing you can\\'t undo. The only decision worth slowing down for is which sites it lands on, and step 4 is entirely about that.\\nPlan availability Free to browse and install. Individual listings may cost money — the button says so before you buy. Publishing your own listing is a separate thing and needs Pro; see Publish a plugin.' },\n { path: '/guides/install-your-first-plugin', title: 'Install your first marketplace item', heading: 'Before you start', anchor: '#before-you-start', text: 'You need an organization with at least one site, and a role that can install. By default that\\'s owner, admin and editor — installing is treated as content work, not an account setting. Viewers cannot, and a custom role can only if it carries the Install plugins permission.\\nIf the Marketplace opens but the Install button doesn\\'t, that\\'s the role, not the plan — see Teams & roles.' },\n { path: '/guides/install-your-first-plugin', title: 'Install your first marketplace item', heading: 'Step 1 — Open the Marketplace', anchor: '#step-1-open', text: 'In the organization navigation (the top-level one, not a site\\'s), choose Marketplace.\\nIt\\'s at the organization level on purpose. What you install is available to every site you choose, so it isn\\'t a per-site setting, and you don\\'t repeat the work for each site you run — the thing agencies and multi-site teams notice first.\\nIts own navigation panel lists:\\n- Browse All — the catalog.\\n- Installed — what you already have, with upgrade and uninstall.\\n- and, once you publish, the seller sections: Upload / Publish, Publisher Profile, Listings, Payouts, Sales.\\nMarketplace is the shop; Plugins is the switch There is a separate Plugins section in the same organization navigation. That is where things get turned on and off — including Aglyn\\'s own built-in plugins. Marketplace installs, Plugins toggles. If you are looking for a switch and can\\'t find one, you\\'re in the wrong section. More on the split.' },\n { path: '/guides/install-your-first-plugin', title: 'Install your first marketplace item', heading: 'Step 2 — Find something', anchor: '#step-2-browse', text: 'Browse All is a grid of listing cards you can search, filter and sort. Cards are a catalog only — clicking one takes you to its detail page, which is the only place an install can happen. You cannot install by accident from the grid.\\nTwo things on a card are worth reading before you click into one:\\n- The price chip. Every card has one. A paid listing shows $29; a free one shows the word Free, rather than leaving the corner empty for you to interpret.\\n- The stars, and the number beside them. 4.8 (12) is twelve people\\'s opinion; 5.0 (1) is one person\\'s. A listing nobody has rated says Not yet rated instead of showing no stars.\\nOn the detail page, read three things before anything else:\\n1. The What\\'s included box. Aglyn writes it from the listing itself, not the publisher, so it is the one description that cannot oversell. Its first row names what the install produces — a sandboxed plugin, an editable component, screens you rework in Besigner, a theme, or a new empty dataset with its fields already defined for a dataset schema, which is the reminder that records never travel: you get the shape, not somebody\\'s data. The rows after it cover where it lands (step 4), review, license and updates. See What\\'s included for every row it can produce.\\n2. The badges. They make two different claims, and it\\'s worth knowing which is which — see what the badges mean.\\n3. The price. The header repeats the card\\'s chip — $29, or Free — and the button spells out what pressing it does: a paid listing reads Buy for $29 rather than Install. Buying is one payment; the listing\\'s updates are included in it.' },\n { path: '/guides/install-your-first-plugin', title: 'Install your first marketplace item', heading: 'Step 3 — Read the reviews, and know who can leave one', anchor: '#step-3-reviews', text: 'Ratings on a listing come only from people with a verified email whose organization actually installed it. Comments are open to any signed-in user. So a listing\\'s star rating is a claim by users, and its comment thread is a claim by anyone — weight them differently.' },\n { path: '/guides/install-your-first-plugin', title: 'Install your first marketplace item', heading: 'Step 4 — Choose which sites get it', anchor: '#step-4-targeting', text: 'This is the step to read.\\nOn the detail page you\\'ll find an Install to dropdown with two options:\\n| Option | What it means |\\n| All sites | Everything in the organization. For a plugin, that includes sites you add later. |\\n| Selected sites | Only the sites you tick. A Sites checklist appears underneath — tick at least one. |\\n\"All sites\" does not mean the same thing for every kind of listing, and that is the whole reason this step exists:\\n- Plugins and dataset schemas install organization-wide. A site you create next month gets them too.\\n- Components, templates, layouts, themes and email templates are site-scoped. \"All sites\" copies them onto every site you have right now; a site you add later will not have them.\\nYou will have seen this fact once already, in the What\\'s included box further up the page — Installs org-wide, covering sites you add later, or Installs per site — new sites are not covered automatically. The helper text under the Install to dropdown is the same fact with your own numbers in it: for the site-scoped case it reads \"Installs to all 4 sites. New sites won\\'t get it automatically.\" Two wordings, one behavior — if they ever seem to disagree, the helper text is the one counting your actual sites.\\n\"Selected sites\" only appears when there\\'s a choice to make, so on a one-site organization you won\\'t see it.\\nEvery site here is a live site There is no staging site in Aglyn. Whatever you install is reachable by real visitors as soon as it\\'s on a site. If you want to try something cautiously, install it to one site — ideally the quietest one — rather than to all of them.' },\n { path: '/guides/install-your-first-plugin', title: 'Install your first marketplace item', heading: 'Step 5 — Install it', anchor: '#step-5-install', text: 'Press Install (or Buy for $…). A confirmation dialog names the listing, its type, its version, and — in bold — exactly where it\\'s going:\\n> Install Bookings Pro (plugin, v2.1).\\n> This will be installed to all sites.\\nRead that bold phrase. It is the last chance to catch a mis-set target, and it is why the dialog exists.\\nChoose Install. That\\'s it.\\nTwo things happen that are worth knowing:\\n- The install is version-pinned. You have v2.1 and you keep v2.1 until you deliberately upgrade. A publisher shipping v2.2 tomorrow does not change your sites overnight.\\n- A plugin is enabled for the workspace automatically. You don\\'t have to go and switch it on after installing.' },\n { path: '/guides/install-your-first-plugin', title: 'Install your first marketplace item', heading: 'Step 6 — Use it', anchor: '#step-6-use', text: 'Where it shows up depends on what you installed:\\n- Plugins add a named group to the Besigner\\'s elements drawer, and often a page in the console navigation.\\n- Components, layouts and templates appear in the drawer and in your site\\'s templates library, badged Marketplace with the version they came from.\\n- Themes apply to the site you chose.\\n- Dataset schemas appear as a new, empty dataset under Data.\\n- Email templates arrive as a draft email you can edit before sending.' },\n { path: '/guides/install-your-first-plugin', title: 'Install your first marketplace item', heading: 'Step 7 — Turn it off, or take it back off', anchor: '#step-7-off', text: 'Three different actions, often confused:\\n| You want to | Where | What happens |\\n| Stop it running, keep it | Plugins → its switch | Off everywhere in the workspace. Flip it back any time. |\\n| Remove it from your sites | Marketplace → Installed → Uninstall, or Uninstall org-wide on an org-wide install. The listing\\'s own detail page carries the same button. | The pin and the switchboard entry go. |\\n| Update it | Marketplace → Installed, or the detail page\\'s Update to v2.2 button | You move to the new version, deliberately. |\\nUninstalling never deletes the data a plugin created. Reinstall it and it picks up where it left off. That is deliberate — an accidental uninstall shouldn\\'t cost you a year of bookings — but it also means uninstalling is not how you erase data. For that, delete the data itself.\\nDesign copied onto a site behaves differently, and the difference matters: a component or template you installed and then edited is now your copy. Uninstalling the listing doesn\\'t reach into your screens and remove it, and updating shows you field by field what the publisher\\'s new version would overwrite before it touches your edits.' },\n { path: '/guides/install-your-first-plugin', title: 'Install your first marketplace item', heading: 'What to do next', anchor: '#what-to-do-next', text: '- Running several client sites? Run an agency workspace covers standardizing installs across sites, and automating the rest.\\n- Want to build one? Build your first plugin is the developer half of this page — same product, written for someone who codes.\\n- Selling one? Publish a plugin and the publisher handbook.' },\n { path: '/guides/install-your-first-plugin', title: 'Install your first marketplace item', heading: 'Related', anchor: '#related', text: '- Plugins & Marketplace — the reference: install scopes, badges, sandboxing, and the Marketplace/Plugins split.\\n- Your templates library — where installed design lands, and how a marketplace copy is badged.\\n- Teams & roles — who is allowed to install.' },\n { path: '/guides/mega-menu-with-interactions', title: 'Build a mega menu with hover interactions', heading: '', anchor: '', text: 'Build a mega menu with hover interactions\\nA mega menu is the wide, multi-column panel that drops down from a nav item on big marketing sites. In this walkthrough you\\'ll add one to your header, fill it with columns of links, and wire it to open on hover — all in the Besigner, without writing any CSS or JavaScript.\\nYou can see the finished result on our demo site: hover Explore in the header of northwind-coffee.aglyn.app and a three-column panel (Coffee / Learn / Get in touch) drops open.\\nPlan availability Every plan, including Free. A mega menu and its open/close behavior are basic interactions — pure in-page choreography with no server cost, so they\\'re never gated. Only the powerful automation steps (analytics, overlays, custom JS, server actions) require Pro+. See interactions.' },\n { path: '/guides/mega-menu-with-interactions', title: 'Build a mega menu with hover interactions', heading: 'What you\\'ll build', anchor: '#what-youll-build', text: '- A Mega Menu nav item with a wide panel.\\n- Three columns of screen links inside the panel.\\n- A hover interaction so the panel opens when a visitor points at the nav item — and closes itself when they move away.\\nPut the menu in your layout (not a single screen) so it shows on every page. Open your Main Layout in the Besigner to follow along; if you don\\'t have a layout yet, see Screens & layouts.' },\n { path: '/guides/mega-menu-with-interactions', title: 'Build a mega menu with hover interactions', heading: '1. Insert the Mega Menu', anchor: '#1-insert-the-mega-menu', text: '1. In the Besigner, select your header\\'s nav bar (the App Bar or its Toolbar) so the new element lands inside it.\\n2. Open Add Element and expand the Navigation group.\\n3. Insert Mega Menu. It arrives as a nav item labeled Menu with a starter panel of three link columns.\\nOn the canvas the menu shows just its trigger — that\\'s how it renders on the live site until a visitor interacts. Select the Mega Menu (or anything inside it) and the panel expands in place so you can edit its contents; click elsewhere and it collapses again.\\nSet the label With the Mega Menu selected, set its Label in the inspector (for the demo it\\'s Explore). Use the Panel width attribute to choose Fit content, Wide (720px), or Full width.\\nThe label renders in the case you type it, so it sits beside plain nav links without shouting. If you want it uppercase, set Text transform in Styles → Typography on the Mega Menu itself and the trigger follows.' },\n { path: '/guides/mega-menu-with-interactions', title: 'Build a mega menu with hover interactions', heading: '2. Build the columns', anchor: '#2-build-the-columns', text: 'The panel is an ordinary canvas slot, so you compose it like any other section:\\n1. The starter preset gives you three Stacks side by side — one per column. Select a column and set a heading (e.g. Coffee, Learn, Get in touch).\\n2. Inside each column, drop Screen Link elements from the Navigation group and point each at a screen. Screen links store the screen id, not the URL, so renaming or re-slugging a page never breaks the menu.\\n3. Add as many columns, links, images, or promo cards as you like — a mega menu panel takes any content.\\nMenu links should render as links A Screen Link ships as a button by default, which is right for a call to action but wrong for navigation: it picks up button typography and the theme\\'s uppercase transform, and it announces itself as a button to screen readers. Set Render as to Text link on the links inside a menu panel, a footer, or a body paragraph. The button-only attributes (Variant, Size, Full width) disappear when you do, because a link ignores them.' },\n { path: '/guides/mega-menu-with-interactions', title: 'Build a mega menu with hover interactions', heading: '3. Make it open on hover', anchor: '#3-make-it-open-on-hover', text: 'Out of the box, clicking the nav item already toggles the panel — no setup. To add the classic hover behavior:\\n1. Select the Mega Menu element itself.\\n2. In the inspector, open Add interaction.\\n3. Set the Trigger to When hovered.\\n4. Add the action Open a menu. Leave the target empty — it defaults to the element you\\'re editing.\\n5. Leave Frequency on Every time (the default) so the menu keeps working on every hover, not just once.\\n6. Save. The interaction is enabled immediately and appears in the element\\'s Interactions list with edit, enable/disable, and remove controls.\\nThat\\'s the whole wiring. A menu opened by hover closes itself when the pointer leaves the nav item and its panel, with a short grace period so the pointer can travel between the two — you don\\'t need a separate When hover ends interaction.\\nPick the target by clicking Any interaction that targets an element (open a menu, show/hide, open a drawer) lets you pick the target by clicking it on the canvas and confirming — no CSS selectors to write. A \"define a custom selector\" option is there as a secondary escape hatch.\\nPrefer primitives? Insert the Dropdown Panel preset\\nIf you\\'d rather build the same pattern from plain elements — a Stack wrapper, a Button trigger, and an absolutely-positioned panel you style freely — insert Dropdown Panel from the Navigation group instead. It arrives with the whole hover choreography already wired as ordinary interactions: show on hover (with Close on Esc and Close on outside click) and hide on hover leave with a 250ms grace delay. Edit or delete them on the wrapper\\'s Interactions list like anything you authored yourself.\\nThe menus & navigation reference covers when to reach for the preset versus the Mega Menu element.' },\n { path: '/guides/mega-menu-with-interactions', title: 'Build a mega menu with hover interactions', heading: '4. Add a mobile drawer (optional)', anchor: '#4-add-a-mobile-drawer-optional', text: 'A wide hover panel is a desktop pattern. For small screens, pair it with a slide-in drawer:\\n1. Insert Mobile Nav (Navigation group) into your toolbar. You get a Menu Button (hidden on desktop), an inline link row (hidden on mobile), and a Drawer already wired to the button — clicking the button toggles the drawer with zero configuration.\\n2. Swap the drawer\\'s placeholder links for your screens, and design the drawer panel however you like — it\\'s a normal canvas slot.\\n3. Under Styles → Visibility, confirm the mega menu\\'s nav cluster is hidden on mobile and the menu button is hidden on desktop.\\nSee menus & navigation for the full element reference.' },\n { path: '/guides/mega-menu-with-interactions', title: 'Build a mega menu with hover interactions', heading: '5. Test and publish', anchor: '#5-test-and-publish', text: '1. Use Test in the interaction dialog to confirm the open/close behavior against the canvas.\\n2. Open Preview and hover the nav item — the panel opens and closes exactly as it will on the published site, without publishing anything.\\n3. Toggle the artboard device preview and light/dark scheme to check the panel at different sizes.\\n4. Click Publish.\\nOpen your live site and hover the nav item — the panel drops open, and moving the pointer away closes it.' },\n { path: '/guides/mega-menu-with-interactions', title: 'Build a mega menu with hover interactions', heading: 'How it works under the hood', anchor: '#how-it-works-under-the-hood', text: 'You never touch selectors or state, but here\\'s the model:\\n- Every menu and drawer renders closed on the live site and listens on a small command bus keyed by its node id.\\n- An Open a menu step dispatches an open command to that bus; the menu element receives it and expands. Click-toggle, the hover interaction, and any other element that opens the menu all speak the same bus, so they compose without conflicting.\\n- Hover-opened menus track the pointer and dispatch their own close when it leaves — which is why you only author the open.\\nBecause it\\'s all in-page DOM choreography, it runs on every plan and is never metered.' },\n { path: '/guides/mega-menu-with-interactions', title: 'Build a mega menu with hover interactions', heading: 'Troubleshooting', anchor: '#troubleshooting', text: '- The panel is always open in the editor. That\\'s expected — selecting the menu or its contents expands it as an overlay under the nav item so you can edit it. It renders closed on the published site. Click empty canvas to collapse it, or open Preview to see it behave as published.\\n- Hover does nothing on the live site. Confirm the interaction\\'s Trigger is When hovered, the action is Open a menu, and the interaction is enabled (green switch on the Interactions list). Confirm you published after adding it.\\n- The menu opens but won\\'t close. Make sure you used Open a menu (which auto-closes on pointer leave), not Show an element (which needs a matching When hover ends → Hide interaction).' },\n { path: '/guides/mega-menu-with-interactions', title: 'Build a mega menu with hover interactions', heading: 'Related', anchor: '#related', text: '- Menus & navigation\\n- Interactions & custom HTML\\n- The Besigner\\n- Screens & layouts' },\n { path: '/guides/member-accounts', title: 'Member accounts', heading: '', anchor: '', text: 'Member accounts\\nVisitors can become members of your published site — sign up, sign in, see their orders, and unlock members-only screens. This walkthrough covers the visitor side (built-in auth pages and the account block) and the console side (the Users page and the member drawer).\\nPlan availability Member signups and the account block work wherever commerce does; gating screens to members needs the content-gating feature (Business and above) — see Members-only areas.\\nMembers are not console users Site members are your site\\'s audience, separate from the console teammates you invite to collaborate. Members sign in on your published site only, with a per-site session — they never see the console.' },\n { path: '/guides/member-accounts', title: 'Member accounts', heading: '1. Turn User Accounts on for the site', anchor: '#1-turn-user-accounts-on-for-the-site', text: 'Member accounts are off by default, on every site. Until you switch them on, /signin, /signup and /recover do not exist on that site — they return a genuine 404, the same as any address you have not published.\\nTurn them on at Sites → your site → Admin → Plugins, on the Site plugins card: flip User Accounts and press Save site plugins. It is a per-site switch, exactly like the other plugin toggles there, so one site in a workspace can have member accounts while another does not.\\nWhy it defaults to off Most sites are marketing sites, and a sign-in page on a marketing domain is worse than a missing one: it looks like the place to type a password, while the real sign-in usually lives somewhere else entirely — a separate app domain, or your identity provider. Aglyn\\'s own site is the example. Serving /signin there would invite people to enter console credentials on a page that is not the console. So the pages appear only when you say the site has members.\\nSwitching User Accounts off again does not delete anything. Existing members, their profiles and their order history stay exactly where they are in Users; only the public pages stop being served. Switch it back on and everything is reachable again.\\nSites that were already using member accounts when this switch arrived keep them — the switch was turned on for any site with members, a designated auth screen, or a members-only page.' },\n { path: '/guides/member-accounts', title: 'Member accounts', heading: '2. The built-in sign-in and sign-up pages', anchor: '#2-the-built-in-sign-in-and-sign-up-pages', text: 'Once User Accounts is on, the site serves three ready-made routes — no design work needed:\\n- /signup — \"Create your account\": name, email, and password (8 characters minimum). Submitting creates the member, signs them straight in, and returns them to your home page.\\n- /signin — \"Welcome back\": email and password. The two pages cross-link (\"New here? Create an account\" / \"Already a member? Sign in\").\\n- /recover — \"Reset your password\": a member enters their email and gets a reset link, then sets a new password at the same address.\\nNew members automatically flow into your CRM and appear as leads; memberSignUp and memberSignIn automation events fire so you can trigger welcome actions.\\nPasswords Member sessions are per-site, cookie-based, and last 30 days. A locked-out member can reset their own password at /recover; you can also send them a reset email from the member drawer (see Password help below).' },\n { path: '/guides/member-accounts', title: 'Member accounts', heading: '3. Design an account page', anchor: '#3-design-an-account-page', text: 'For a richer home for members, add the Customer account block (found in the Commerce group of the element picker) to a screen — conventionally at the slug account, which is where commerce gates point anonymous visitors:\\n- Signed out, the block shows sign-in / create-account tabs in place, with your configurable Signed-out heading above.\\n- Signed in, it renders the member\\'s profile with Sign out, plus sections for Orders (with status chips and tracking), Subscriptions (each with a Manage button that opens the Stripe Billing Portal), Downloads (digital purchases and license keys), and Addresses.\\nGive the account page the same chrome as the rest of your site by binding a shared layout: in the screen\\'s Properties, pick one under Shared layout — the appbar/footer are then maintained once for every bound screen (see screens & layouts).' },\n { path: '/guides/member-accounts', title: 'Member accounts', heading: '4. Gate screens to members', anchor: '#4-gate-screens-to-members', text: 'To restrict a screen: open its version view, and in the Page Access card set Visibility to Members only, then publish. Anonymous visitors get a \"This page is for members\" prompt with sign-in / create-account links instead of the content (or your designed 401 error screen if you\\'ve assigned one). The screen\\'s content is never in the anonymous page source — members fetch it with their session after sign-in.\\nFor one-off protection without accounts, use a per-screen password instead.\\nGate part of a page, not all of it\\nThree blocks gate a region instead of a whole screen, so one public page can hold both the pitch and the members-only part:\\n- Members gate — a container that shows its children only to entitled members. Everyone else sees the Teaser text and a call-to-action button you point wherever joining starts. Required product id names the product whose buyers or subscribers get in; leave it blank to accept any live subscription.\\n- Members video — plays one of a product\\'s videos for members entitled to that product, and resumes where each member left off. Video number picks which one (the first is 0), and Locked text is what sits over the poster frame for everyone else. The video file is private and every link a member\\'s player gets expires, so a link copied out of the page stops working on its own; see Members videos are private files.\\n- Member feed — posts only entitled members can see, newest first, with a Heading, Empty text for when there are none, and a cap on how many show. Non-members never receive the posts, not merely a hidden copy of them.\\nA gated region is not a substitute for screen visibility when the whole page is for members — that setting keeps the content out of the anonymous page source entirely.' },\n { path: '/guides/member-accounts', title: 'Member accounts', heading: '5. Manage members from the console', anchor: '#5-manage-members-from-the-console', text: 'The site\\'s Users page has two cards: Site users (your members — this guide) and Users (console collaborators). The Site users card is searchable and paged, newest first, with Email, Name, Joined, and Status columns. For members who may open the CRM, each row\\'s ⋮ menu holds Open in CRM — the member\\'s contact, found by the address they signed up with.\\nClick a member to open the member drawer:\\n- Profile — email, display name, and join date, with a Suspended chip when applicable.\\n- Lifetime purchases — charged order totals minus refunds (pending and canceled orders excluded).\\n- Orders — the member\\'s payment history, newest first: order number, status, total, date, any refunded amount, and the Stripe payment reference. Needs the editor or admin role on the site.\\n- Subscriptions — each with its renewal date and status chip.\\n- Addresses — the member\\'s saved addresses.\\nSuspend & reactivate\\nThe drawer\\'s action button flips between Suspend member and Reactivate member, each behind a confirmation:\\n- Suspend — the member can no longer sign in on the published site (they see an \"account suspended\" message), and their account page signs out on its next load. Orders, subscriptions, and history are all kept.\\n- Reactivate — restores access with the member\\'s existing password.\\nBoth actions are recorded in the site\\'s activity log.\\nPassword help\\nThe drawer\\'s Password section gets a member back into their account. This is their sign-in for this site only — it has nothing to do with any Aglyn console account the same person might also have.\\n- Send password reset email — emails the member a link to the site\\'s /recover page. The link works once and expires after an hour, and their current password keeps working until they use it. This is the option to reach for. Capped at a few sends per hour ' },\n { path: '/guides/member-accounts', title: 'Member accounts', heading: 'Related', anchor: '#related', text: '- Members-only areas\\n- Commerce end to end\\n- CRM\\n- Site protection & error screens' },\n { path: '/guides/run-an-agency-workspace', title: 'Run an agency workspace', heading: '', anchor: '', text: 'Run an agency workspace\\nIf you build sites for other people — an agency, a freelancer with a roster, an internal team serving several brands — Aglyn is designed to be one workspace holding many sites, rather than one account per client. This page is the whole shape of that setup, in the order you\\'d actually do it.\\nIt\\'s written for someone who has built one Aglyn site already. If you haven\\'t, do Build your first site first — this page assumes you know what a screen and the besigner are.' },\n { path: '/guides/run-an-agency-workspace', title: 'Run an agency workspace', heading: 'The mental model, first', anchor: '#the-model', text: 'Three words, and getting them straight up front saves a lot of confusion later:\\n| Term | What it is | For an agency |\\n| Organization | The billing and identity boundary. Owns the plan, the invoice, the members, the shared library. | You. One organization for your agency. |\\n| Site (or host) | One published website with its own screens, domain, media and store. | One per client project. |\\n| Workspace | What the console calls your organization when you\\'re working in it. | Same thing as organization — you\\'ll see both words. |\\nThe consequence that matters: your plan\\'s limits are per organization, and your access controls are per site. You buy 25 sites once; you decide separately who can touch which one.\\nOne organization or one per client? Use one organization when the sites are yours to run: you hold the domains, you bill the client for a service, you\\'re the one on call. That\\'s the normal agency shape, and it\\'s what the Agency plan is priced for.\\nUse a separate organization per client when the client ultimately owns the account and you\\'re only building it — they hold the card, the domain, and the risk. You then join their organization as a member and can leave cleanly.\\nThe awkward middle — building in your workspace and later moving the site to theirs — is the one thing that isn\\'t self-serve, so decide before you start. Ask support if you\\'re not sure which side a project falls on.' },\n { path: '/guides/run-an-agency-workspace', title: 'Run an agency workspace', heading: 'Step 1 — Pick the plan by site count, not by features', anchor: '#step-1-plan', text: 'For agencies the binding limit is almost always how many sites you can publish, not which features you get. Check the site allowance on the plan table and pick from there.\\nTwo things that change the arithmetic:\\n- Extra sites are an add-on, so you don\\'t have to jump a whole tier for one more client. They\\'re cheaper per site on higher plans.\\n- The platform fee on client storefront sales falls to 0% on higher plans. If your clients sell, that fee can dominate the subscription price — do that sum before choosing.\\nUpgrades are instant, downgrades are not An upgrade applies immediately and prorates. A downgrade waits until the end of the period you already paid for, and $0 is due on the day you choose it. Plan seasonal capacity accordingly: dropping a tier in January doesn\\'t reduce a January invoice. When each change takes effect.' },\n { path: '/guides/run-an-agency-workspace', title: 'Run an agency workspace', heading: 'Step 2 — Build your house style once', anchor: '#step-2-templates', text: 'The single biggest saving in agency work is not rebuilding the same footer.\\n1. Build one site properly — nav, footer, contact page, legal pages, theme.\\n2. Save it as a template from the site\\'s settings. See Save a template.\\n3. Start every new client from that template instead of from blank.\\nTwo more things worth doing once:\\n- Reusable components for the pieces you\\'ll place on many screens within a site. Edit the component, every instance updates.\\n- The organization media library for assets shared across clients — your own logo, stock photography you\\'ve licensed, icon sets. Files there are available to every site in the workspace, so you upload once.\\nKeep client assets in the client\\'s site library The organization library is shared across every site. Client logos and photography belong in that site\\'s library, not the shared one — otherwise the next project\\'s media picker is full of the last client\\'s brand.' },\n { path: '/guides/run-an-agency-workspace', title: 'Run an agency workspace', heading: 'Step 3 — Decide who can touch what', anchor: '#step-3-access', text: 'Aglyn has two shapes of teammate, and the distinction is the one agency-specific thing worth learning properly:\\n| | Workspace manager | Site collaborator |\\n| Sees | Every site in the organization | Only the sites you grant |\\n| Costs | A manager seat | A collaborator seat on each site they can reach |\\n| Use for | Your own staff | Contractors, and the client |\\nThe Access column is where the difference shows: a manager reads All sites, a collaborator reads the number of sites they were granted. The line above the table counts the two separately, because only managers use a team seat.\\nGive clients collaborator access, not manager access. A manager can see every other client\\'s site, their content and their form submissions. That is usually a contractual problem, and it\\'s not undone by anyone being careful.\\nSteps:\\n1. Organization → Team to add your own staff as managers — leave All sites ticked on the invite row.\\n2. For a collaborator, invite them from the same page with All sites unticked, then use their Access cell to pick the sites and give each one a role. Access is granted per site from here, not from the site itself.\\n3. Give a client who should draft but not go live the Author role on their site. It is the site role between Viewer and Editor: it can edit every kind of content — pages, layouts, components and collection entries — and it cannot publish any of it. No route, no live version, no scheduled publish, no published entry. It also cannot read the site\\'s orders.\\n4. Use custom roles for narrowing at the organization level — who may manage members, buy add-ons, or see billing.\\nSeats are metered and enforced per plan, and both kinds are available as add-ons if you outgrow the included count. A pending invite holds a seat — the count is checked when you send it and aga' },\n { path: '/guides/run-an-agency-workspace', title: 'Run an agency workspace', heading: 'Step 4 — Domains and handover', anchor: '#step-4-domains', text: 'Each site gets a free .aglyn.app subdomain immediately, which is what you build and demo on. Connect the real domain at launch.\\nThe handover question is who owns the DNS, and it\\'s worth settling in writing at the start of a project rather than at 5pm on launch day:\\n- You hold the registrar — fastest to launch, and you can fix things. It also means you\\'re the one a client has to ask to leave, which some clients dislike and some contracts forbid.\\n- The client holds the registrar — slower (you\\'ll be sending them records to paste), cleaner to hand over, and the honest default for most work.\\nEither way the records are the same, and the site\\'s setup page shows them. See Connect a domain.' },\n { path: '/guides/run-an-agency-workspace', title: 'Run an agency workspace', heading: 'Step 5 — Back up before you hand over', anchor: '#step-5-backups', text: 'Every site exports a full backup from its own settings. Take one:\\n- before any large redesign, so \"put it back how it was\" is a real option;\\n- at handover, and give the client a copy — it is theirs, and having it makes the relationship easier to end well;\\n- before deleting anything. Deleting a site is immediate and permanent, with no hold period — unlike deleting an organization, which has one. There is no undo and we keep no copy.\\nTake one from the site\\'s Admin → Backup & template. It includes the datasets that site can see — up to 50, with up to 1,000 records each — so a larger dataset also wants its own export from the Data tab (CSV or JSON), which downloads every record.' },\n { path: '/guides/run-an-agency-workspace', title: 'Run an agency workspace', heading: 'Step 6 — Billing, and what to tell clients', anchor: '#step-6-billing', text: 'The invoice is yours, at the organization level. Aglyn doesn\\'t bill your clients and doesn\\'t split an invoice per site. However you charge them — retainer, per site, marked-up hosting — is between you and them.\\nTwo things that are per site, and are the ones clients ask about:\\n- Storefront platform fees come out of that site\\'s own sales.\\n- Usage meters (page views, storage, form submissions, emails) roll up to your organization total. A single client having a very good month can move your invoice, so watch the usage figures on the billing page rather than being surprised.\\nSales tax is added on top of your plan price where applicable, calculated from your workspace\\'s billing address — see Sales tax.' },\n { path: '/guides/run-an-agency-workspace', title: 'Run an agency workspace', heading: 'Step 7 — Automate the repetitive part', anchor: '#step-7-automate', text: 'Once you\\'re running several sites, the console stops being the fastest way to do routine things. The REST API reads across one organization with one key:\\n- Sites — enumerate every site in that organization and its domain, which is the basis of any status dashboard you build.\\n- Form submissions — pull the leads from every site in the organization into one place, or into a CRM, and mark each one read as you go so the next run doesn\\'t send it twice.\\n- Orders and Products — for clients who sell.\\n- Media — audit alt text across the organization\\'s sites, or find what\\'s filling your storage quota.\\nStart with Your first API call, which walks through creating a key and making a request from scratch.\\nA key never spans organizations API keys are minted per organization, and every endpoint is scoped to the organization the key belongs to. There is no key that reads across several.\\nSo this step is only a single request if you took the one organization side of the choice in the mental model — clients as sites in your workspace. If you took the other side and each client owns their own organization, you need one key per client organization and your script loops over them. That is a real difference in effort, and it is the trade for the clean exit a separate organization gives you.\\nOne key per purpose Create a separate key for each integration, scoped to the least it needs. A key that only reads form submissions can\\'t be turned into a data breach across every site in the organization, and revoking one doesn\\'t take the others down.' },\n { path: '/guides/run-an-agency-workspace', title: 'Run an agency workspace', heading: 'A checklist you can reuse per project', anchor: '#checklist', text: 'This is a list to keep yourself — in your own tracker, or pasted into a project brief. The console does not track it for you.\\n- [ ] Site created from your house template\\n- [ ] Client added as a site collaborator, not a manager\\n- [ ] Theme, logo, favicon set from the client\\'s brand\\n- [ ] Legal pages present (privacy, terms, cookie banner if needed)\\n- [ ] Custom domain connected and verified\\n- [ ] SEO basics: titles, descriptions, social image, sitemap reachable\\n- [ ] Analytics visible to the client\\n- [ ] Backup taken and a copy given to the client\\n- [ ] Who holds the registrar, written down somewhere you\\'ll find it' },\n { path: '/guides/run-an-agency-workspace', title: 'Run an agency workspace', heading: 'Related', anchor: '#related', text: '- Teams, roles & membership\\n- Site templates\\n- Add-ons\\n- Your first API call' },\n { path: '/guides/your-first-api-call', title: 'Your first API call', heading: '', anchor: '', text: 'Your first API call\\nBy the end of this page you\\'ll have made a real request to your own data and handled the errors properly. It assumes nothing — if you\\'ve never used an API before, start at the top and follow along; if you have, skip to Step 3 and then read the API reference instead of the rest of this.\\nEverything here uses curl, which is already installed on macOS and Linux and ships with Windows 10 and later. There\\'s a JavaScript version of each step underneath.\\nWhat you need\\n- An organization on a plan that includes API access (Business or Advanced).\\n- Permission to manage the organization — only owners and admins can create keys.\\n- A terminal.' },\n { path: '/guides/your-first-api-call', title: 'Your first API call', heading: 'Step 1 — Create an API key', anchor: '#step-1-create-a-key', text: '1. Open the console and switch to the organization you want to use.\\n2. Go to Organization → Settings → API keys.\\n3. Choose Create API key.\\n4. Give it a name that says what it\\'s for — zapier-orders-sync, not key 1. The name is what you\\'ll be looking at in six months deciding whether it\\'s safe to revoke.\\n5. Select its scopes. A scope is one permission. Grant the fewest that do the job: for this walkthrough, tick Datasets — read.\\n6. Choose Create.\\nCopy the key now. It\\'s shown exactly once. Aglyn stores only a hash of it and genuinely cannot show it to you again — if you lose it, the fix is to revoke it and make a new one, which costs you nothing but a minute.\\nA key looks like this:\\nTreat it like a password The key carries your organization\\'s access, not your personal login. Anyone holding it can do everything its scopes allow.\\n- Never commit it to git. Put it in an environment variable or a secrets manager.\\n- Never put it in a web page, a mobile app, or anything a browser downloads — a key in client-side JavaScript is a public key.\\n- Use one key per integration, so you can revoke one without breaking the others.\\nStore it in your shell for the rest of this page:' },\n { path: '/guides/your-first-api-call', title: 'Your first API call', heading: 'Step 2 — Check that the key works', anchor: '#step-2-check-the-key', text: 'Before writing anything real, confirm the key is valid and see what it can do:\\nThat\\'s the whole handshake. Authorization: Bearer is how every request authenticates — there is no login step, no token exchange, no session.\\nIf you get something else, jump to errors.\\nDo this at startup in real code GET /v1/me is the cheapest way to fail fast with a clear message. An integration that checks its key on boot tells you \"the key is wrong\" at deploy time instead of \"undefined is not a function\" at 3am.' },\n { path: '/guides/your-first-api-call', title: 'Your first API call', heading: 'Step 3 — Your first request', anchor: '#step-3-your-first-request', text: 'List your organization\\'s datasets:\\nThree things to notice, because they\\'re the same on every list endpoint:\\n- data holds the results. Always an array, even with one result.\\n- hasmore tells you whether there\\'s another page. This is the only reliable end-of-list signal — see step 5.\\n- object on each item names its type. Handy when you\\'re logging.\\nNow read records out of one of those datasets, using its id:\\nYour data lives in values, keyed by field id — which is not the same as the display name you see in the console. Email address in the console is email here. The dataset\\'s fields array lists those ids but not their display names; the console\\'s Schema dialog shows both, as Display name · fieldId.\\nIn JavaScript\\nRead the error body before throwing. error.type is a stable machine-readable string; error.message is a sentence for a human. Branch on type, log message.' },\n { path: '/guides/your-first-api-call', title: 'Your first API call', heading: 'Step 4 — Write something', anchor: '#step-4-write-something', text: 'Writes need a write scope. Go back to the console, create a second key with Datasets — write, or add the scope when you create the key. Scopes have no hierarchy: datasets:write does not include datasets:read. A key that writes a record and then reads it back needs both, ticked separately.\\nA new record returns 201.\\nAbout that Idempotency-Key {#idempotency}\\nIt\\'s the header that makes retrying safe, and it is worth understanding on day one rather than after your first duplicate.\\nThe problem it solves: you POST, the record is created, and then your connection drops before the response reaches you. You have no idea whether it worked. Retrying might create a second record; not retrying might lose the first.\\nWith an Idempotency-Key, retrying is simply correct. Send the same key and:\\n- if the original succeeded, you get 200 with the original record — not a duplicate;\\n- if the original failed, the key is released, so a retry genuinely re-runs;\\n- if the original is still running, you get 409 conflict — wait and retry.\\n201 means created, 200 means replayed. That\\'s how you tell them apart.\\nUse a fresh UUID per logical operation — one key per record you intend to create, generated before the first attempt and reused across every retry of that attempt. Keys never expire, so a key really is single-use forever.\\nThe full rules, including how it behaves on DELETE, are in Conventions → Idempotency.' },\n { path: '/guides/your-first-api-call', title: 'Your first API call', heading: 'Step 5 — Page through everything', anchor: '#step-5-page-through-everything', text: 'Lists come back in pages. To get everything, follow the cursor:\\nTwo things that will bite you\\n1. A short page is not the last page. Some endpoints filter rows out after reading a page, so data.length can be less than limit while hasmore is still true. A loop written as while (page.data.length === limit) stops early and looks like it worked. Loop on the cursor.\\n2. Page 1 is not \"the newest 25\". Every list is ordered by id, not by date. There is no sort parameter and no filter on created. Paging start to finish gives you every item exactly once — that\\'s what a full sync needs — but \"give me today\\'s records\" is not a request this API can answer. Page everything and compare created yourself, or store the ids you\\'ve already handled.' },\n { path: '/guides/your-first-api-call', title: 'Your first API call', heading: 'The four errors you will hit', anchor: '#the-four-errors-you-will-hit', text: 'Every error has the same shape, and type is the field to branch on:\\n| You see | It means | Do this |\\n| 401 unauthorized | Key missing, mistyped, or revoked. | Check the header is Authorization: Bearer aglynsk…. A common cause is a shell variable that didn\\'t expand — echo $AGLYNAPIKEY. |\\n| 403 insufficientscope | The key is fine; it lacks a permission. code names the exact scope. | Create a key with that scope. You can\\'t add a scope to an existing key. |\\n| 403 planrequired | The organization\\'s plan doesn\\'t include API access — or, on commerce endpoints, doesn\\'t include commerce (code: \"commerce\"). | Not transient. Don\\'t retry — alert a human. |\\n| 429 ratelimited | Over 120 requests/minute for this key. | Back off. Retry-After says for how long, and every response carries the remaining budget in its headers. |\\nRetry 429 and 500. Never retry 401, 403, or 400 — nothing about them will change on the second attempt, and a loop that retries them turns a config mistake into a rate-limit ban.' },\n { path: '/guides/your-first-api-call', title: 'Your first API call', heading: 'Letting an AI agent call it for you', anchor: '#mcp', text: 'Everything above is the same API reached with an HTTP client. If what you are wiring up is a model — Claude, ChatGPT, or anything else that speaks the Model Context Protocol — point it at the MCP server instead and it gets the same operations as tools, with no client code to write:\\nIt is the same API, the same key and the same scopes. A tool call runs through the identical pipeline a request does, so an operation your key has no scope for is refused there exactly as it is here, with the same error body. The tool list is generated from the API description on this site, so it is never out of date with the reference.\\nAuthenticate with the key you made in Step 1, as a bearer token:\\nThe transport is Streamable HTTP, and the server keeps no session between calls — every request carries its own key, so there is nothing to resume and nothing to expire. Point your client\\'s MCP configuration at the URL above and it will discover the tools itself; /.well-known/api-catalog on any site names it alongside the REST API for clients that look there first.' },\n { path: '/guides/your-first-api-call', title: 'Your first API call', heading: 'Where to go next', anchor: '#where-to-go-next', text: '- API reference — every resource, every field, every error.\\n- Conventions — pagination, ordering, errors, idempotency, in one place. Worth reading once, end to end.\\n- Rate limits & usage — the 120/minute limit and how monthly usage bills.\\n- Webhooks — for reacting to events, don\\'t poll. Polling a list endpoint on a timer is the most common way to hit the rate limit.\\nIf you\\'re syncing a store, Orders and Products each carry a recipes section with the whole loop written out.' },\n { path: '/guides/your-first-api-call', title: 'Your first API call', heading: 'Related', anchor: '#related', text: '- Datasets & schema deep-dive — field ids, the typed model, quotas.\\n- Billing & plans — which plans include the API.' },\n { path: '/marketing-and-automation/analytics/cookie-consent', title: 'Cookie consent', heading: '', anchor: '', text: 'Cookie consent\\nIf your site uses Google Analytics or Google Tag Manager, Aglyn manages visitor consent for you. The control is real, not decoration: whenever a visitor\\'s recorded state does not grant analytics, the script never loads — it is not loaded and then suppressed.' },\n { path: '/marketing-and-automation/analytics/cookie-consent', title: 'Cookie consent', heading: 'How it works', anchor: '#how-it-works', text: 'Consent UI appears only on sites that use a consent-gated feature — today, a configured Google Analytics measurement ID or a Google Tag Manager container ID. Either one is enough. A site with neither has nothing to consent to, so its visitors never see anything.\\nYour site\\'s consent mode (Site setup → SEO → Cookie consent) decides how visitors are handled:\\n- Geo-conditional (recommended, and the behavior when you have never touched the setting): visitors in prior-consent regions — the EU/EEA, the UK, and any visitor whose region cannot be determined — see a consent banner first, and analytics loads only after they allow it. Visitors everywhere else (for example the US) are tracked from their first visit with no banner; their state is recorded as implied consent, and the persistent Your Privacy Choices control lets them opt out at any time.\\n- Opt-in everywhere: every visitor, everywhere, sees the prior-consent banner.\\nThe banner offers Allow, Decline, and Preferences side by side — no dark patterns, and declining is one click, same as allowing.\\nAdvertising is a second, separate question — and it is off until you turn it on. By default Aglyn asks your visitors about analytics and nothing else, and tells Google that advertising storage is denied for every visitor, in both directions. If you have Google Ads linked to your GA4 property and you need a basis for advertising storage, turn on Also ask visitors about advertising storage in Site setup → SEO → Cookie consent. The switch needs a Google tag of some kind — a measurement ID or a container ID — to do anything; there is no advertising storage to ask about without one.\\nTurning it on grants nothing by itself. It adds a second question, with its own checkbox, to the banner and to the preferences panel, so that a visitor has ' },\n { path: '/marketing-and-automation/analytics/cookie-consent', title: 'Cookie consent', heading: 'What needs consent', anchor: '#what-needs-consent', text: 'Consent-gated — held back unless the visitor\\'s recorded state grants analytics:\\n- Google Analytics (your configured measurement ID) and every event Aglyn sends to it — see Google Analytics events.\\n- Google Tag Manager (your configured container ID). The container is gated the same way and never more loosely, so it cannot be used to install a tag that runs before consent.\\n- Core Web Vitals reporting. Page-speed metrics reach your property only through the same tag, and are discarded rather than held if consent is refused while a page is open.\\n- Cross-visit A/B test identity: without an analytics grant, experiment variant assignment is remembered only for the visit (sessionStorage) instead of across visits.\\n- Remembering the campaign a visitor first arrived from. Reading the campaign on the click itself needs no grant; keeping it across visits does.\\n- A Wistia player loaded with the page — see Videos that load with the page.\\n- Advertising storage (adstorage, aduserdata, adpersonalization), on sites that have turned the advertising question on. Where it starts depends on the visitor\\'s region, and it is the same split analytics uses:\\n- Prior-consent regions. The EU/EEA, the UK, and any visitor whose region cannot be determined see the banner first, and advertising storage is denied until they tick that specific box. Not objecting is never a yes here.\\n- Everywhere else. Advertising runs from their first visit, alongside analytics, on the same implied basis — these visitors see no banner, and Your Privacy Choices is where they turn it off. That is the opt-out posture the Privacy Policy and the Cookie Policy both describe.\\n- A visitor who declines, opts out, or arrives with a Global Privacy Control signal gets no advertising storage anywhere in the world. A refusal outranks th' },\n { path: '/marketing-and-automation/analytics/cookie-consent', title: 'Cookie consent', heading: 'Videos that load with the page', anchor: '#videos-that-load-with-the-page', text: 'A Video element with Load the player with the page on is consent-gated like the list above: it puts Wistia\\'s player in place of its poster only for a visitor whose recorded state grants analytics, and everyone else gets the poster. The player keeps its own storage inside its frame (wistia and wistia-video-progress-{media id}) as soon as it loads, before anyone presses play. A press loads the player for any visitor, with Wistia\\'s do-not-track option unless analytics is granted, and the player keeps the same storage then.' },\n { path: '/marketing-and-automation/analytics/cookie-consent', title: 'Cookie consent', heading: 'Your Privacy Choices — the persistent control', anchor: '#privacy-choices--the-persistent-control', text: 'Whenever consent management is active, every page of your site shows a small Your Privacy Choices control. Visitors use it to change their state in either direction at any time — opt out after being tracked by default, or allow analytics after declining. If your site asks about advertising, that choice lives in the same panel, on its own checkbox. In geo-conditional mode this control is the only opt-out surface visitors in implied-consent regions see, which is why it is shown by the platform on every page rather than depending on your template.\\nThe wording is deliberate and you should not expect it to be configurable. Once a site shares personal information for cross-context behavioral advertising, California\\'s CPRA requires an opt-out link with that exact title, so Aglyn ships it as the title on every site rather than as a preference an individual site could get wrong.\\nYou can also link any element (a footer link, for example) to #aglyn-consent — clicking it opens the same preferences panel.' },\n { path: '/marketing-and-automation/analytics/cookie-consent', title: 'Cookie consent', heading: 'Where the choice is kept', anchor: '#where-the-choice-is-kept', text: 'A visitor\\'s choice is stored in their browser\\'s local storage, under a key that includes your site\\'s id. Three consequences worth knowing before you answer a visitor\\'s question about it:\\n- It is per site. A yes on one site is not a yes on another, even when the two share a preview address.\\n- It does not expire. Nothing re-asks a visitor on a schedule. Clearing site data resets them to undecided, and they are then handled by your consent mode as if for the first time.\\n- What was granted is re-derived from the recorded decision on every read, never trusted as a stored flag. That is why turning the advertising question off makes existing advertising grants stop counting immediately.\\nWithdrawing consent removes the cookies too. When a visitor opts out or declines, Aglyn deletes the Google analytics and advertising cookies it can reach (ga, gid, gcl and their variants) across your domain and its parent, and tells any tag still resident on the page to stop measuring. A visitor who checks their cookie list after opting out sees them gone, which is the behavior to describe if one asks.' },\n { path: '/marketing-and-automation/analytics/cookie-consent', title: 'Cookie consent', heading: 'Global Privacy Control', anchor: '#global-privacy-control', text: 'Browsers can send the Global Privacy Control (GPC) signal. Aglyn honors it as an automatic opt-out: a visitor sending GPC is never tracked by default, in either mode, and their state is recorded as a GPC opt-out. If they explicitly allow analytics in Your Privacy Choices, that specific choice takes precedence over the blanket signal.' },\n { path: '/marketing-and-automation/analytics/cookie-consent', title: 'Cookie consent', heading: 'Previewing what visitors see', anchor: '#previewing-what-visitors-see', text: 'You configure from one country; your visitors come from many. In any screen\\'s Preview, use the Consent preview picker (top right) to view your site as-if-from the EU, the US, an unknown region, or a GPC-sending browser — the real banner, driven by the real rules, with nothing saved. On a published page you can append ?aglynConsent=ask to any URL to see the prior-consent banner as an EU visitor would (this override can only ever show more consent UI, never less, so it is safe to share).' },\n { path: '/marketing-and-automation/analytics/cookie-consent', title: 'Cookie consent', heading: 'Turn the banner off', anchor: '#turn-the-banner-off', text: 'The switch at the top of the Cookie consent card turns the whole tool off. That means Google Analytics loads for every visitor without being asked — do this only if you run your own consent solution, and remember you remain responsible for the consent your visitors\\' jurisdictions require.' },\n { path: '/marketing-and-automation/analytics/google-analytics', title: 'Google Analytics events', heading: '', anchor: '', text: 'Google Analytics events\\nAdd your own Google Analytics measurement ID and your site sends events to your property, under your account, alongside Aglyn\\'s own built-in pageview analytics. This page is the list: every event name, every parameter, and where it comes from.\\nYou need this list before the data arrives, because GA4 will not show you a custom dimension you have not registered — and it cannot register one for a parameter it has never received.' },\n { path: '/marketing-and-automation/analytics/google-analytics', title: 'Google Analytics events', heading: 'Turn it on', anchor: '#setup', text: 'Site → Setup → Tracking.\\n| Field | Format |\\n| Google Analytics measurement ID | G- followed by 4–16 letters and digits |\\n| Google Tag Manager container ID | GTM- followed by 5–10 letters and digits |\\nBoth are optional and independent. A value that does not match the format is ignored rather than injected, so a mistyped ID fails silently — check the field if nothing arrives.\\nNothing is sent from a preview or a development server Tracking only runs on a production deployment. A site you are previewing sends no events at all, to your property or ours, so an empty GA4 report from a test run is expected rather than broken.' },\n { path: '/marketing-and-automation/analytics/google-analytics', title: 'Google Analytics events', heading: 'What consent does to it', anchor: '#consent', text: 'The tag never loads for a visitor whose recorded consent does not grant analytics. This is structural, not a filter: with no tag on the page, every event below is dropped where it is raised, and nothing is queued for later. A visitor who grants consent halfway through a session starts being measured from that point, not retroactively.\\nWhich visitors that covers depends on your consent posture — see Cookie consent. The short version: under the opt-in posture nobody is measured until they say yes, and under opt-out everyone is measured until they say no.\\nYour Google Tag Manager container is gated the same way and never more loosely. A container that loads only after analytics consent cannot be used to install a tag that runs before it.' },\n { path: '/marketing-and-automation/analytics/google-analytics', title: 'Google Analytics events', heading: 'Events GA4 collects by itself', anchor: '#automatic', text: 'Aglyn sends no pageview of its own. Your property gets GA4\\'s automatic collection — pageview, sessionstart, firstvisit, userengagement — plus whatever enhanced measurement you have enabled in the property (scroll depth, outbound clicks, file downloads, and so on).\\nEverything in the tables below is in addition to that.' },\n { path: '/marketing-and-automation/analytics/google-analytics', title: 'Google Analytics events', heading: 'Engagement', anchor: '#engagement', text: '| Event | Parameters | Sent when |\\n| generatelead | formname (string), formlocation (the page path) | A form is submitted successfully. formname is the form\\'s name, or Form if unnamed; Newsletter for a newsletter signup block, Booking for a free booking, Popup for an overlay form. |\\n| selectcontent | contenttype (always cta), contentid, surface (site) | A visitor clicks a call-to-action link. contentid combines the section, the link label and a plan= qualifier where the destination carries one; it falls back to the destination path so an unlabeled CTA is still counted. |\\n| click | linkdomain (the destination hostname), linkid (when the link is identifiable), surface (site) | A visitor clicks a link to another domain. Same-host links are not sent — the destination\\'s own pageview already counts them. |\\n| aglynoverlay | overlayaction, overlayid | An overlay or popup is shown, dismissed or converted. |\\n| aglynexperiment | experimentid, variantid, experimentaction (exposure or conversion) | A visitor is assigned to an A/B variant, and again when that variant converts. |\\n| signup | method (always password) | A visitor creates a site member account. |\\n| login | method (always password) | A site member signs in. |' },\n { path: '/marketing-and-automation/analytics/google-analytics', title: 'Google Analytics events', heading: 'Commerce', anchor: '#commerce', text: 'These come from the commerce and bookings features and follow GA4\\'s standard ecommerce shape, so GA4\\'s built-in ecommerce reports work without any mapping.\\n| Event | Parameters |\\n| viewitem | items: one entry with itemid and itemname |\\n| addtocart | items; plus currency and value only when the item has a price. An unpriced item sends the pair not at all rather than sending zero. |\\n| viewcart | currency, value (the server-calculated subtotal), items |\\n| begincheckout | currency, value, items |\\n| purchase | transactionid, currency, value (gross, excluding tax), shipping, items with itemid, itemname, price and quantity |\\nThree things to know before you build a revenue report:\\n- value on purchase excludes tax. There is no tax parameter, so a GA4 revenue figure will not match a tax-inclusive settlement report.\\n- Items carry no itemcategory. Category breakdowns are not available from this data.\\n- Refunds are not sent from the browser. Cancellations and refunds happen server-side and do not reach your property, so GA4 revenue is gross of them.' },\n { path: '/marketing-and-automation/analytics/google-analytics', title: 'Google Analytics events', heading: 'Page speed', anchor: '#web-vitals', text: 'Core Web Vitals are reported as events named after the metric: LCP, CLS, INP and TTFB. Each carries:\\n| Parameter | Meaning |\\n| value | The metric delta, not the total. GA sums value, and summing deltas keeps a twice-reported metric\\'s page total correct. |\\n| metricid | An id unique to that measurement, for de-duplicating |\\n| metricvalue | The metric\\'s current value |\\n| metricdelta | The change since the last report of the same metric |\\n| metricrating | good, needs-improvement or poor, when the library assigns one |\\n| surface | site |\\nReport on metricvalue — averaging value will give you the average change, which is not a number that means anything.' },\n { path: '/marketing-and-automation/analytics/google-analytics', title: 'Google Analytics events', heading: 'Your own events', anchor: '#authored-events', text: 'Any element on a screen can send an event you name yourself, via the Track an analytics event interaction step. It lands in the same property as everything above. See Interactions for the naming rules, the parameter limits and what gets stripped.' },\n { path: '/marketing-and-automation/analytics/google-analytics', title: 'Google Analytics events', heading: 'What your property never receives', anchor: '#never-sent', text: 'Some measurement exists on Aglyn\\'s side and is deliberately kept off your property:\\n- Aglyn\\'s own product events — workspace and site creation, publishing, billing and assistant usage. These go to Aglyn\\'s console analytics, never to a site\\'s property.\\n- traffictype and contentgroup — internal-traffic marking and content grouping are applied only to Aglyn\\'s own marketing site.\\n- Advertising tags — Meta, Google Ads and LinkedIn tags are installed only on Aglyn\\'s own marketing site. On your site the advertising machinery renders nothing and installs nothing.\\n- Server-side events, including refunds and subscription cancellations. They are sent from the server with credentials that are not per-site, so there is no way to route them to your property.\\n- The built-in pageview beacon. Aglyn\\'s own Traffic card is fed by a first-party, cookieless request that is not GA4 and does not appear in your property.' },\n { path: '/marketing-and-automation/analytics/google-analytics', title: 'Google Analytics events', heading: 'Related', anchor: '#related', text: '- Analytics — the built-in Traffic card and per-screen figures\\n- Cookie consent — what has to be true before any of this runs\\n- Interactions — sending an event of your own' },\n { path: '/marketing-and-automation/analytics/insights', title: 'Insights', heading: '', anchor: '', text: 'Insights\\nAsk a question about your own numbers — \"which pages brought in the most visitors, and did anything change?\" or \"what is the average order total by state?\" — and Aglyn AI answers from your site\\'s figures. It never guesses a number: every insight links to the rows it was read from, and an insight whose numbers cannot be found in those rows is left out.\\nAvailability Insights are part of Aglyn AI, which is released gradually. This page applies as insights reach your workspace. Asking a question uses AI credits, like any AI job.' },\n { path: '/marketing-and-automation/analytics/insights', title: 'Insights', heading: 'Asking a question', anchor: '#asking-a-question', text: 'Open the Assist panel on one of these pages and choose Ask about your numbers under AI jobs:\\n- a site\\'s Analytics page — traffic, top pages, where visits came from, forms, bookings, campaigns, A/B tests and store sales;\\n- a site\\'s CRM → Reports page — the same figures, for questions about where people came from and what they did;\\n- a site\\'s Data page, or your organization\\'s Data page — your datasets.\\nType your question, pick the window it covers (the last 7, 14, 30 or 90 days), and choose Ask. Answers usually take about a minute. You can close the dialog while you wait: the answer stays under AI jobs, where View answer opens it again.' },\n { path: '/marketing-and-automation/analytics/insights', title: 'Insights', heading: 'How an answer is made', anchor: '#how-an-answer-is-made', text: '1. The figures are read, never written. Aglyn AI chooses which of your figures answer the question — for example your traffic and your top pages — and Aglyn reads them the same way the console\\'s own cards do. AI never writes a database query and never sees a single visitor, contact, order, booking or submission: only totals, counts and rates.\\n2. Each insight cites its rows. Choose Show the figures under an insight to see the rows it was written from, and follow the link to the page those figures come from.\\n3. Untraceable insights are left out. Every number an insight writes must appear in a row it cites — as the table shows it, or rounded. An insight that adds figures together, works out a rate of its own, says a figure rose when it fell, or names a person is left out, and the answer says how many were.\\nWhen your figures cannot answer part of a question, the answer says so in one sentence rather than filling the gap.' },\n { path: '/marketing-and-automation/analytics/insights', title: 'Insights', heading: 'Asking about datasets', anchor: '#asking-about-datasets', text: 'On a Data page, a question can summarize a dataset — how many records it holds, how many fill each field, and for a number field its lowest, average and highest value — or group its records by one field, such as the average order total by state.\\n- A site\\'s Data page reads only the datasets shared with that site, and your organization\\'s Data page reads the datasets you can see.\\n- A breakdown reads up to the first 2,000 records of a dataset and says so when there are more.\\n- Group by a field many records share, such as a state, a status or a category. A field with more than 60 different values, such as a name or an order number, can\\'t be grouped by.\\n- Groups of fewer than three records are counted together as Other. No record is ever sent to AI.' },\n { path: '/marketing-and-automation/analytics/insights', title: 'Insights', heading: 'Weekly insights', anchor: '#weekly-insights', text: 'In the Ask about your numbers dialog, turn on Send me weekly insights for this workspace every Monday. Each Monday, Aglyn AI writes a few insights for up to five of your busiest sites you can reach: the change in traffic, the most viewed page, the form that turned the most views into submissions, a campaign that did better than the rest, and any day that stood out — only those the figures support.\\n- They arrive as a notification and an email, and are listed under AI jobs.\\n- Each site\\'s weekly insights are an AI job and use AI credits. When your workspace has used its AI credits for the month, that week\\'s insights are skipped without a message.\\n- Turn them off from the same dialog, or under Weekly insights on the Notifications page. Muting Forms & bookings there silences the notification but not the email.\\n- Weekly insights are made only for people who can run AI jobs on the site.' },\n { path: '/marketing-and-automation/analytics/insights', title: 'Insights', heading: 'Privacy', anchor: '#privacy', text: '- Only totals, counts, rates and labels are sent to the AI provider: page paths, referring sites and campaign tags; the names of forms, services, products and A/B tests; the subjects of campaign emails; and for a dataset, its field names and types and, in a breakdown, the values that at least three records share. Email addresses and phone numbers are removed from every label before anything is sent.\\n- An answer is kept with your workspace for 180 days, like other AI jobs. Only the person who asked can open it; weekly insights can be opened by the people who reach the site.\\n- Nothing is changed, published or sent to your contacts. The weekly email goes only to people in your workspace who asked for it.' },\n { path: '/marketing-and-automation/analytics/overview', title: 'Analytics', heading: '', anchor: '', text: 'Analytics\\nAglyn tracks how your site is doing out of the box — pageviews, referrers, devices, and per-screen traffic — so you don\\'t need a third-party tool to see the basics.\\nPlan availability Free dashboard traffic panel. Per-screen traffic and the screenAnalytics entitlement are Pro+.' },\n { path: '/marketing-and-automation/analytics/overview', title: 'Analytics', heading: 'Pageview tracking', anchor: '#pageview-tracking', text: 'A lightweight pageview beacon records visits into daily counters. The site dashboard shows a traffic panel summarizing your site\\'s activity. Tracking is cookieless — no visitor IDs, no fingerprinting.' },\n { path: '/marketing-and-automation/analytics/overview', title: 'Analytics', heading: 'Visitors (approximate)', anchor: '#visitors-approximate', text: 'Alongside raw pageviews, the traffic panel shows an approximate visitor count. It is deliberately imprecise so it can stay cookieless: each browser tab counts once per day, so a visitor using two tabs — or returning on another day — counts again. Nothing links one day\\'s visits to the next.' },\n { path: '/marketing-and-automation/analytics/overview', title: 'Analytics', heading: 'The Traffic card', anchor: '#traffic-card', text: 'The Traffic card on a site\\'s dashboard is the one most people read. Its range picker offers 7 days, 14 days, 30 days and 90 days, and defaults to 14.\\nIt shows six figures for the window you picked:\\n| Figure | What it means |\\n| Page views | Total views in the window, with a growth figure beside it |\\n| Visitors | The approximate count described above |\\n| Avg / day | Page views divided by the days in the window |\\n| Mobile / Desktop | The share of views from each, largest first |\\n| Top page | The most-viewed path, with its view count |\\n| Top referrer | Where most visits came from, with its count |\\nWhat the growth figure compares against {#traffic-delta}\\nThe +12.4% vs prior beside Page views compares the window you selected against the window of the same length immediately before it — 7 against the previous 7, 90 against the previous 90. Change the range and the comparison changes with it; hovering the figure says which one, in words.\\nIt is green above zero, red below, and neutral at exactly zero.\\nA first window shows no growth figure at all. When there is no prior period to compare against — a site published last week, asked for 30 days — Aglyn renders nothing rather than\\n+100% or +0%, both of which would be claims it cannot support. The same rule applies to every delta in the product, including the ones on the Orders screen.\\nPercentages in the device split are each rounded on their own, so they need not add to exactly 100. A device with no views is left out rather than shown as 0%.' },\n { path: '/marketing-and-automation/analytics/overview', title: 'Analytics', heading: 'Insights', anchor: '#insights', text: 'Dig deeper with top referrers and full device breakdowns over the same selectable ranges.' },\n { path: '/marketing-and-automation/analytics/overview', title: 'Analytics', heading: 'Campaign tracking (UTM)', anchor: '#campaign-tracking-utm', text: 'Links tagged with utmsource, utmmedium, and utmcampaign are broken down on the analytics page, so you can see which newsletters, ads, or campaigns actually bring visitors. Tag your links as you would for any analytics tool; Aglyn records the labels from the URL — nothing about the visitor.' },\n { path: '/marketing-and-automation/analytics/overview', title: 'Analytics', heading: 'Per-screen traffic', anchor: '#per-screen-traffic', text: 'Pro+ sites get a per-screen traffic panel on each screen\\'s view page, with pageviews attributed to the screen and broken down by referrer and device — and a Screens table on the site\\'s analytics page comparing pageview share across all your screens over a selectable window. Plan cards surface analytics as an upsell for lower tiers.\\nTo find it: open a site, go to Screens, open a screen, open the version you want, and look for the Screen traffic (14 days) card.\\nAverage time on a screen {#dwell-time}\\nBeside Screen views, that card shows Avg. time on this screen — how long a visitor stayed before leaving it. It reads as 45s, 2m 04s or 1h 05m.\\nThings worth knowing before you draw a conclusion from it:\\n- It is an average over the visits that reported one, not over all views. A visitor whose tab is killed, or whose browser closes before it can send, reports nothing. The figure divides by the visits that were measured.\\n- It is capped at 30 minutes. A tab left open all afternoon is counted as 30 minutes rather than as an afternoon, so one abandoned tab cannot dominate the average.\\n- Visits under a second are ignored. A bounce through a redirect is not time on the page.\\n- It appears only once there is something to average. A screen with no measured visits shows no figure at all — not a zero, not a dash.\\n- Time is collected on every plan, including Free. Only reading it is Pro+, so the history is already waiting the moment an organization upgrades.\\nAglyn stores a running total and a count of measurements per screen per day. It never keeps an individual visit\\'s duration — that would be a behavioral record of a person, and the average is the whole of what the feature needs.' },\n { path: '/marketing-and-automation/analytics/overview', title: 'Analytics', heading: 'Google Analytics', anchor: '#google-analytics', text: 'Prefer your own tooling? Add a Google Analytics measurement ID under Site → Setup → Tracking and Aglyn injects it alongside the built-in tracking. There is a Google Tag Manager container field beside it, and both are gated on the visitor\\'s analytics consent — the tag never loads for a visitor whose recorded state does not grant it.\\nYour site then sends a defined set of events to your own property: form submissions, CTA and outbound clicks, the full ecommerce funnel, overlay and A/B events, and Core Web Vitals. Google Analytics events lists every one of them with the exact parameters it carries — read it before you build a report, because GA4 will not surface a custom dimension you have not registered.' },\n { path: '/marketing-and-automation/analytics/overview', title: 'Analytics', heading: 'Related', anchor: '#related', text: '- Google Analytics events\\n- SEO toolkit\\n- Billing & plans' },\n { path: '/marketing-and-automation/email-campaigns/designed-emails', title: 'Designed emails', heading: '', anchor: '', text: 'Designed emails\\nDesign campaign emails in the besigner — the same editor you use for pages — with an email-safe block set. There is no separate email editor to learn.' },\n { path: '/marketing-and-automation/email-campaigns/designed-emails', title: 'Designed emails', heading: 'Create a template', anchor: '#create-a-template', text: 'On Marketing → Email, click New email template. That creates an email document and opens it in the besigner with the email blocks:\\n- Email section — the 600px container with background and padding.\\n- Email text — heading/subheading/body/caption styles; supports merge tokens.\\n- Email rich text — formatted HTML (sanitized like the custom HTML block).\\n- Email image, Email button, Email divider, Email spacer — the essentials, rendered in email-client-safe markup.\\n- Email product — pick a product by id; its current name, price, and image fill in at send time (renames never break it).\\n- Email custom HTML — raw table markup for advanced layouts, sanitized.' },\n { path: '/marketing-and-automation/email-campaigns/designed-emails', title: 'Designed emails', heading: 'Duplicate a template', anchor: '#duplicate-a-template', text: 'Every template on the list has Duplicate… in its row menu. The copy takes the latest saved design whole, under the name you give it, and is sent by nothing until a campaign picks it — so a copy is the safe way to try a variation without touching the design a campaign is already using.' },\n { path: '/marketing-and-automation/email-campaigns/designed-emails', title: 'Designed emails', heading: 'Styling email blocks', anchor: '#styling-email-blocks', text: 'The styles panel works on email blocks exactly as on page elements — your edits (fonts, colors, spacing) apply on top of each block\\'s email-safe defaults.\\nBecause email clients run no JavaScript, the attributes panel does not offer the Interactions section while an email document is open — use links (Email button, image links) for anything clickable.' },\n { path: '/marketing-and-automation/email-campaigns/designed-emails', title: 'Designed emails', heading: 'Merge tokens', anchor: '#merge-tokens', text: 'Use these anywhere in text, rich text, or button links:\\n| Token | Fills with |\\n| {{contact.firstName}} | Recipient\\'s first name |\\n| {{contact.name}} | Full name |\\n| {{contact.email}} | Email address |\\n| {{site.url}} | Your site\\'s base URL |\\n| {{unsubscribeUrl}} | Signed link to this recipient\\'s email preferences |\\nUnknown tokens stay visible in the output so a typo shows up in your test send instead of silently rendering blank.\\n{{unsubscribeUrl}} opens the preference page, where the recipient can leave just the topic this email belongs to or unsubscribe from everything. You do not have to place it: a design that carries no opt-out gets one appended to the bottom of both the HTML and the plain-text part. Put it in your own footer if you would rather choose where it sits — the appended one is skipped when your design already has the link.' },\n { path: '/marketing-and-automation/email-campaigns/designed-emails', title: 'Designed emails', heading: 'Send it', anchor: '#send-it', text: 'Open the email you want to send and choose Write this email. Set How this email is written to Designed in the besigner, then pick your template under Email design (stored by id — renaming the template never breaks scheduled sends).\\nAn email is written one way or the other, never both. Choosing a design puts the message box away, so a typed message cannot be silently discarded in favor of the design — if you had already typed one, the composer offers it to you as the plain-text version rather than dropping it.\\nThe plain-text version\\nEvery campaign goes out as two alternatives: the styled HTML most people see, and a plain-text version for readers whose mail shows no styling.\\n- Generated from the design is the default. Buttons and product blocks keep their links in it, and it follows the design as you edit.\\n- Written here replaces it with your own. Merge tags resolve in it, and the unsubscribe link is added as a plain address at the end.\\nEditing the design never overwrites a plain-text version you wrote. If the design changes afterwards, the composer says so and offers to take the design\\'s text — nothing is rewritten behind you.\\nPreview email shows both halves, so you can read the plain-text version before it goes.\\n- Send test delivers a proof to a real address without recording a campaign.\\n- Scheduling and A/B experiments work exactly as with plain campaigns.' },\n { path: '/marketing-and-automation/email-campaigns/designed-emails', title: 'Designed emails', heading: 'Start from a brief instead', anchor: '#start-from-a-brief-instead', text: 'Generate an email with AI writes a draft design from a description — blocks, copy, and three subject lines to choose between — which you then edit here like any other.' },\n { path: '/marketing-and-automation/email-campaigns/generate-with-ai', title: 'Generate an email campaign with AI', heading: '', anchor: '', text: 'Generate an email campaign with AI\\nDescribe the email you want and AI generation writes a draft design: one email built from the email blocks, with three subject lines and three preheaders to choose between. Ask for a campaign instead and it writes the same design plus the draft campaign that would send it.\\nBoth are drafts. Nothing is sent, scheduled or queued, and the campaign is aimed at nobody until you pick an audience yourself.' },\n { path: '/marketing-and-automation/email-campaigns/generate-with-ai', title: 'Generate an email campaign with AI', heading: 'What you get', anchor: '#what-you-get', text: 'An email design. It appears under Marketing → Email beside the templates you made by hand, and opens in the besigner exactly as those do. Every block is an email block — sections, text, buttons, images, dividers, spacers and product cards — so it renders the same way in every mail client.\\nThree subject lines and three preheaders. They are stored with the design, the strongest as the subject and the preheader, the rest as alternatives. Change which one leads at any time, or write your own.\\nA campaign, when you asked for one. A draft campaign holding that one email, with the subject and preheader already filled in. Open it from Marketing → Campaigns.' },\n { path: '/marketing-and-automation/email-campaigns/generate-with-ai', title: 'Generate an email campaign with AI', heading: 'Write the brief', anchor: '#write-the-brief', text: 'Say what the email is for, who it speaks to, and what you want the reader to do. The more the brief gives, the less the email guesses.\\n> Announce the autumn menu to the regulars. Mention that the Tuesday bread\\n> class is running again, and point people at the class page to book.\\nA few things are worth knowing:\\n- Pick the kind of email — a welcome, a newsletter, a launch, a cart reminder or an event reminder — and the email is written in that shape.\\n- Name a page and it gets linked. A button pointing at one of your pages is written as that page\\'s address and completed with your domain when the email is sent.\\n- A fact the brief does not give — a date, a price, a place — arrives in square brackets for you to fill, rather than invented.\\n- No unsubscribe link is written. Every campaign email gets one added when it is sent.' },\n { path: '/marketing-and-automation/email-campaigns/generate-with-ai', title: 'Generate an email campaign with AI', heading: 'Products', anchor: '#products', text: 'An email can carry product cards, which fill in each product\\'s current name, price and picture at the moment the email is sent, so a rename or a price change never leaves a stale email behind.\\nPick the products for the job, or name them in the brief exactly as they are named in your catalog. The generated email places one card for each, in the order you gave them, and binds each card to its product by id. No product\\'s details are sent for the email to be written — only how many cards to place.' },\n { path: '/marketing-and-automation/email-campaigns/generate-with-ai', title: 'Generate an email campaign with AI', heading: 'Who receives it', anchor: '#who-receives-it', text: 'Choosing the audience is yours. A drafted campaign has no list, no segment and no schedule.\\nWhen your brief names one of your lists, the campaign\\'s note says which one it is, and — if that list has enough history on this site — the day and hour its past campaigns were opened most, with how many sends that reading is taken from. Pick the list on the campaign, check the send time, and schedule it when you are ready.\\nYour lists, contacts and past send figures are read on the server to work that out. None of them is sent for the email to be written.' },\n { path: '/marketing-and-automation/email-campaigns/generate-with-ai', title: 'Generate an email campaign with AI', heading: 'Merge tokens', anchor: '#merge-tokens', text: 'Generated copy greets the reader with the merge tokens every campaign fills: {{contact.firstName}}, {{contact.name}} and {{contact.email}}, and only in text — never in a link, a subject line or a preheader, where they are not filled. Add any other token yourself in the besigner.' },\n { path: '/marketing-and-automation/email-campaigns/generate-with-ai', title: 'Generate an email campaign with AI', heading: 'What it will not do', anchor: '#what-it-will-not-do', text: '- It will not send, schedule or queue anything.\\n- It will not publish an email design or mark a template live.\\n- It will not pick who receives a campaign.\\n- It will not write raw HTML into an email. Add the Email custom HTML block yourself if you need it.' },\n { path: '/marketing-and-automation/email-campaigns/generate-with-ai', title: 'Generate an email campaign with AI', heading: 'Where it runs', anchor: '#where-it-runs', text: 'Email generation needs AI generation switched on for your workspace, the Email plugin switched on for the site, and permission to generate. Drafting a campaign also needs the Marketing plugin on and a plan that sends campaign email — see Billing & Plans. Without that, you can still generate email designs.' },\n { path: '/marketing-and-automation/email-campaigns/generate-with-ai', title: 'Generate an email campaign with AI', heading: 'Related', anchor: '#related', text: '- Designed emails — the blocks, and editing a design by hand.\\n- How Aglyn AI builds — the rules every generated document is held to.' },\n { path: '/marketing-and-automation/email-campaigns/overview', title: 'Email Campaigns', heading: '', anchor: '', text: 'Email Campaigns\\nEmail campaigns let you reach the people in your CRM. Build an audience, compose a send, and Aglyn handles delivery, caps, and unsubscribes.\\nPlan availability Campaign email starts at Pro. Free and Starter include a monthly campaign allowance of zero, so a campaign of any size is refused before it sends. From Pro up the allowance rises with the plan.\\nAccount email is not part of that. Order receipts, password resets, booking confirmations and replies to form submissions send on every plan, Free included, and are never refused by a campaign cap. See sending domains for why the two are treated differently.' },\n { path: '/marketing-and-automation/email-campaigns/overview', title: 'Email Campaigns', heading: 'Send a campaign', anchor: '#send-a-campaign', text: '1. Create a campaign on Marketing → Campaigns — a name, the dates it runs between, and the lists it is aimed at.\\n2. Open it and write an email inside it. Pick the audience for that email — leads, site members, segments, or an email list. An audience built from a rule can also reuse a saved Contacts view beside a segment.\\n3. Send — subject to your plan\\'s send cap.\\nA campaign lives under Marketing because that is what it is: a window of dates, a set of lists, a topic and the revenue it earned, which happens to reach people by email. The individual messages it sent, the templates they are built from, the audiences, the topics, the sending domains and the suppression list are all on the Emails page, and each message links back to the campaign it belongs to.\\nA campaign holds many emails {#campaigns-group-emails}\\nA campaign is a container, not a single message. It carries a name, a start and end date, and the lists it is aimed at; the emails you send inside it are its contents, and the campaign\\'s page adds their figures up.\\n- The campaigns list shows one row per campaign: its window, its lists, how many emails it has sent, and the totals across them.\\n- Opening a campaign shows the lists it is aimed at, every email inside it, the delivery and engagement figures summed across those emails, and a composer for writing the next one.\\n- Each email keeps its own report, reached from the campaign page. Nothing about an individual send\\'s report or its link changed.\\nCampaigns you sent before this existed are still there, each listed as a campaign of one and marked Single send. Their reports open at the same address they always did, and the unsubscribe links in mail already delivered are untouched.\\nWhat belongs to a campaign {#what-belongs-to-a-campaign}\\nA push is rarely only its mail. A' },\n { path: '/marketing-and-automation/email-campaigns/overview', title: 'Email Campaigns', heading: 'Email lists', anchor: '#email-lists', text: 'Lists are audiences shared across your organization\\'s sites. Create them on the Marketing page and target any of them from the campaign composer. A list is one of two kinds, chosen when you create it.\\nManual lists\\nA manual list holds the people you put in it. Grow it with the \"Enroll in a list\" automation step (e.g. on form completion), popup email capture, the Add to a list card in the Inbox, or by hand from the list itself. Members stay until they are removed.\\nSee and manage who is on a list {#list-members}\\nPress Members on any list row to open it. You get the membership a page at a time — address, name, when they joined, how they got there, and what their consent record says — plus the controls to change it. Press Close to collapse it again; nothing is read until you open a list.\\nHow tells a rule match apart from somebody who was added. A person a rule enrolled leaves when they stop matching; a person who was added stays.\\nConsent is the basis the membership carries, read for the site you are on. The kinds are not the same fact and the column keeps them apart:\\n- Opted in — this person ticked a box, on the date shown. Their own decision.\\n- Attested by your team — somebody on your team stated they had this person\\'s permission. That statement is the basis, and it is recorded against their account with the date.\\n- Opted out — a recorded refusal. Nothing makes this person mailable again except their own opt-in.\\n- Opted in to another site — they agreed to hear from a different site in your account, and permission given to one brand is not permission for another. Ask them yourself, or attest that you have it.\\n- Opted in — no site recorded — a grant from before consent named the site it was given to. It names no brand, so it grants to none.\\n- No basis on record — nothing eith' },\n { path: '/marketing-and-automation/email-campaigns/overview', title: 'Email Campaigns', heading: 'Experiments', anchor: '#experiments', text: 'Business plans can A/B test screens, sections, and emails from the Experiments card on the Marketing page: weighted variants, deterministic visitor assignment, a conversion goal, and per-variant exposure/conversion rates with a pick-the-winner flow.\\nTwo ways to finish a test without watching it:\\n- End date — past it, visitors get the default and stats stop accruing until you pick a winner.\\n- Auto-declare winner — opt in with a minimum exposure count per variant and a confidence level (90/95/99%); once a challenger clears the bar (or every challenger confidently loses to the control), the experiment completes itself and serves the winner. Auto-completed tests carry a chip in the results dialog.\\n- Screens & sections — variants pin a screen version; visitors are split deterministically and the winning version serves to everyone once you pick it. Start a section test straight from the Besigner\\'s Interactions panel.\\n- Emails — variants override the campaign\\'s subject and/or body. Attach a running email experiment in the campaign composer; each recipient deterministically receives one variant (re-sends reach the same variant), sends count as exposures, and once a winner is picked every later send uses the winning copy.' },\n { path: '/marketing-and-automation/email-campaigns/overview', title: 'Email Campaigns', heading: 'Opens & clicks', anchor: '#opens--clicks', text: 'With the Resend webhook configured, campaign history shows opens and clicks per campaign, and clicks on A/B sends count as that variant\\'s conversions — so the experiment results table fills in by itself.\\nThe campaign report\\nReport, beside any campaign that has been sent, opens the full picture for that one send.\\nIt leads with what the campaign did — what it caused, what it earned, and where it sent people — and the mail\\'s own figures come under that: delivery, engagement, rates and the audience it was taken from. A campaign holding several emails is laid out the same way, with the list of its emails last, so the two read alike.\\nEvery rate names the population it is taken over, right next to the number, because the same label means different things in different tools:\\n| Rate | Taken over |\\n| Delivery rate | Sent — what the provider accepted |\\n| Open rate | Delivered, and counting readers, not opens |\\n| Click rate | Delivered |\\n| Click-to-open rate | The readers who opened — a different question from the click rate, and usually three or four times larger |\\n| Bounce rate | Sent |\\n| Complaint rate | Delivered |\\n| Unsubscribe rate | Delivered |\\nTwo things the report will refuse to show you, and says so on screen:\\n- A rate with no denominator. Delivery events are recorded by the webhook, so a campaign sent before it was connected has no delivered count. The report leaves those rates blank rather than dividing by sent, which would read higher than the same campaign measured anywhere else.\\n- A click rate for a send whose links were not trackable. Click tracking rewrites links in the HTML part of a message, so a send that carried none reports zero clicks whatever recipients did. The click count is still shown; no rate is computed from it.\\nOpens and readers who opened are both sh' },\n { path: '/marketing-and-automation/email-campaigns/overview', title: 'Email Campaigns', heading: 'Compliance', anchor: '#compliance', text: '- Every send includes an unsubscribe link, and the header mailbox providers look for — Gmail and Yahoo\\'s one-click List-Unsubscribe.\\n- Clicking it opens a page; only submitting that page actually changes anything. That matters because corporate mail scanners open every link in a message before the recipient sees it, and a link that unsubscribed on open would quietly shrink your list.\\n- Unsubscribes are honored automatically so you stay compliant.\\nTopics {#topics}\\nA topic is a stream of email somebody can leave on its own. Every campaign belongs to one, picked in the composer, and the link in that email offers to stop that stream rather than all of them.\\nFour topics come with every organization — Promotions and offers, Newsletter, Product updates, and Sales outreach. Rename them, change what recipients are told they get, or add your own under Emails ▸ Topics. Topics are shared across every site in your organization, the same way your lists are.\\nYou cannot delete a topic, only retire it. A retired topic disappears from the composer and from the preference page, and campaigns already sent under it keep working — their unsubscribe links still name it. Restore it at any time.\\nThe preference page {#preference-page}\\nThe link at the bottom of every campaign opens a page where the recipient sees your topics, ticks the ones they want to keep, and saves. Whatever they untick stops; everything else carries on. \"Unsubscribe from everything\" is on the same page, one button away, for anyone who wants it.\\nThis is why an unsubscribe is no longer all-or-nothing: somebody who only wanted the sales mail to stop can stop it and keep your newsletter, instead of leaving your list entirely — or reaching for \"report spam\", which costs you far more.\\nOne exception, and it is deliberate: the one-c' },\n { path: '/marketing-and-automation/email-campaigns/overview', title: 'Email Campaigns', heading: 'Related', anchor: '#related', text: '- CRM\\n- Forms & lead capture\\n- Marketing overlays (email capture popups)' },\n { path: '/marketing-and-automation/marketing-overlays/overview', title: 'Marketing Overlays', heading: '', anchor: '', text: 'Marketing Overlays\\nMarketing overlays are the announcement bars and popups that sit on top of your site to promote offers and capture emails — without touching your page designs. Manage them from your site\\'s Marketing page.\\nPlan availability Paid, gated by the marketingOverlays entitlement.' },\n { path: '/marketing-and-automation/marketing-overlays/overview', title: 'Marketing Overlays', heading: 'Announcement bar', anchor: '#announcement-bar', text: 'A site-wide announcement bar shows a message across every page — ideal for sales, notices, or launches. It\\'s controlled centrally and gated by the marketing-overlays entitlement. Its text can show variables and site details.' },\n { path: '/marketing-and-automation/marketing-overlays/overview', title: 'Marketing Overlays', heading: 'Promotional popups', anchor: '#promotional-popups', text: 'Popups give you more control:\\n- Triggers — After a delay (a number of seconds), On scroll (a percentage of the page), or On exit intent.\\n- Frequency capping — don\\'t nag returning visitors. See below.\\n- Scheduling — run a popup only during a campaign window.\\nFrequency: how often a popup comes back {#frequency}\\nIn the popup editor, Frequency offers two mutually exclusive choices.\\nOnce per session shows the popup at most once for as long as the visitor keeps the tab open. Close the tab, come back tomorrow — or open your site in a second tab — and they see it again. This is the right choice for a popup tied to the visit rather than to the person: a first-order discount, a cookie or age notice, a \"we\\'re closed today\" message.\\nRe-show after a while takes a number of days — Re-show after (days), 7 by default — and hides the popup for that long after it is dismissed, across sessions and browser restarts. This is the right choice for a newsletter capture you do not want to ask twice for in a week.\\nOnly one applies. Choosing Once per session hides the days field entirely, because a popup cannot be capped both ways.\\nWhere the choice is remembered The cap is remembered in the visitor\\'s browser, not on your site — per-session in session storage, per-days in local storage. A visitor who clears their browser data, or arrives in a private window, is a new visitor as far as the cap is concerned. If storage is unavailable altogether the popup falls back to showing at most once per page view.\\nThe cap is also per popup, keyed to its content. Editing a popup\\'s content resets its cap, so an edited popup is shown again to visitors who had already dismissed the old one.\\nThe site-wide default popup has only the day cap The Once per session choice is on the multi-overlay editor, where you manage' },\n { path: '/marketing-and-automation/marketing-overlays/overview', title: 'Marketing Overlays', heading: 'Multiple overlays, scheduling & page targeting', anchor: '#multiple-overlays-scheduling--page-targeting', text: 'The Marketing page manages any number of bars and popups, each with:\\n- A schedule window (show from / show until) — run a bar only during a sale.\\n- Page targeting — comma-separated paths, with /blog/ matching a whole section, plus a \"never show on\" exclude list.\\n- An enable switch and a status chip (Live / Scheduled / Off).\\nWhen several overlays match a page, the first bar and the first popup (by order) show. The single announcement bar and popup on the same page remain as your always-on default surfaces; configured overlays take priority over them.' },\n { path: '/marketing-and-automation/marketing-overlays/overview', title: 'Marketing Overlays', heading: 'Variables and site details in copy', anchor: '#variables-in-copy', text: 'A bar\\'s text and a popup\\'s headline and body are filled in when the page renders:\\n- A variable — type its name in double braces, such as {{saleEndsAt}} for a variable named saleEndsAt on the Logic page. Saving stores the variable\\'s id, so renaming the variable later does not break the copy.\\n- A site detail — {{host.businessName}} and the other site details. A detail the site hasn\\'t set renders as nothing.\\nA token nothing fills in, such as a name no variable carries or a function call, would reach visitors exactly as typed, so the field turns red and names it. A date variable shows as a date, not as a live countdown.' },\n { path: '/marketing-and-automation/marketing-overlays/overview', title: 'Marketing Overlays', heading: 'Engagement stats', anchor: '#engagement-stats', text: 'Each overlay tracks its own lifetime views, clicks, and dismissals, shown in the Engagement column of the overlays table — so you can tell whether a bar earns its screen space. Dismissals persist per visitor: a closed bar stays closed until you edit its text.\\nWith Google Analytics connected, the same events also land in your own GA property as aglynoverlay events (with the overlay id and action), so you can segment sessions by overlay engagement.' },\n { path: '/marketing-and-automation/marketing-overlays/overview', title: 'Marketing Overlays', heading: 'Related', anchor: '#related', text: '- Email campaigns\\n- CRM\\n- Billing & plans' },\n { path: '/marketing-and-automation/workflows-and-actions/actions-builder', title: 'Actions builder', heading: '', anchor: '', text: 'Actions builder\\nWhen you just need \"when X happens, do Y\", the actions builder is faster than a full workflow. It maps one event to one action.\\nPlan availability Basic interactions are on every plan (including Free): menu and drawer open/close, show/hide an element, toggle a CSS class, sticky nav, navigation, and site alerts — pure in-page effects with no server cost and no metering.\\nThe automations engine — server-side steps, custom JS, analytics events, and Marketing overlays — is Pro+, with metered runs. You build both in the same place; steps that need a higher plan are labeled in the editor.' },\n { path: '/marketing-and-automation/workflows-and-actions/actions-builder', title: 'Actions builder', heading: 'Create an action', anchor: '#create-an-action', text: '1. Open the actions builder from Automation → Actions.\\n2. Choose the event (the trigger).\\n3. Choose the steps to run in response.\\n4. Save.\\nThat\\'s it — no multi-step logic to manage. Reach for a workflow when you need several steps, branching, or composition.\\nStart from a recipe {#recipes}\\nBeside Add action, the Recipes menu lists four ready-to-edit CRM automations: Welcome a new lead, Follow up a won deal, Re-engage a stale lead and Tag by form. Choosing one opens the same editor already filled in — trigger, conditions and steps — with a line saying which recipe it started from; change anything, then save. Nothing is saved until you do. Tag by form asks for one of this site\\'s forms first, because the trigger is keyed on it. What each recipe builds, step by step, is in Automations for the CRM → Recipes. An organization with several sites can also install a recipe on any of them, without the editor, from the organization\\'s CRM → Settings → Recipes.\\nDescribe it with AI {#describe-it}\\nBeside Recipes, Describe it drafts an automation from a sentence, using only the triggers and steps your plan includes. The draft arrives in the list switched off, with anything your site is missing left in square brackets for you to fill in. A saved automation\\'s editor also has Explain it, and a failed run has Why did this fail?. See Draft and explain automations with AI.' },\n { path: '/marketing-and-automation/workflows-and-actions/actions-builder', title: 'Actions builder', heading: 'Triggers', anchor: '#triggers', text: 'Beyond server events (form submissions, page views, sign-ins, leads, bookings), actions can fire on visitor behavior in the page:\\n- Scroll depth — the visitor scrolls past a percentage.\\n- Scroll to / element visible — a CSS-selected element enters the viewport.\\n- Element click — a CSS-selected element is clicked.\\n- Exit intent — the pointer leaves toward the top of the window.\\n- Time on page — a dwell-time threshold passes.\\n- Page visit — the page loads.\\nPage triggers can be limited to certain paths (/pricing, /blog/), and a Frequency setting controls re-fires: every matching pageview, once per session, once per visitor, or with a cooldown (a minimum number of minutes between fires for the same browser).\\nCRM events {#crm-events}\\nSix server events come from the CRM — two about contacts, three about deals and one about tasks. Pick one and the Filter field\\'s helper text lists the keys below, so a filter such as lifecycleStage == \"customer\" or a condition such as source equals booking can be written without leaving the editor.\\n| Event | Fires when | Keys in scope for filters and conditions |\\n| Contact created (contactCreated) | A capture on your site makes a new contact: a form submission, a member sign-up, a newsletter subscription, an order or a booking from an address your workspace did not already hold. A repeat visit by somebody already on the list is recorded as an interaction and does not fire it. | contactId · email · name (empty when the capture had none) · source (form, member, newsletter, order or booking) · hostId · lifecycleStage (the stage the capture set — lead, subscriber or customer; empty when it set none) · campaignIds (comma-joined; present only when the capture came through a campaign) · formId (present only when the capture came through a form — the ke' },\n { path: '/marketing-and-automation/workflows-and-actions/actions-builder', title: 'Actions builder', heading: 'Steps', anchor: '#steps', text: 'Steps run in order and mix in-page effects with server-side work:\\n- Basic in-page effects (all plans): open/close/toggle a menu or drawer, show/hide or toggle an element, add/remove/toggle a CSS class, set or remove an ARIA or data attribute, scroll to an element, play a video, make the navigation sticky, redirect, show a site alert. These are pure DOM choreography — they run everywhere and are never metered.\\n- Advanced in-page effects (Pro+): show a popup or bar from your Marketing overlays, show custom HTML, track an analytics event, and run custom JS (Business).\\n- On the server (Pro+): run a workflow, write to or update a dataset, send a webhook (Business), send an email, notify site admins, enroll the contact in a list, assign a campaign, fire a custom event to chain more actions.\\n- In the CRM (Pro+): set the contact\\'s lifecycle stage, tag the contact, assign the contact an owner, create a CRM task, log a CRM activity. See CRM steps.\\n- Flow steps (Pro+): Wait, Wait for something to happen, and End the flow here. See Sequences below.\\nOn plans without the automations entitlement, an automation that mixes tiers still runs its basic in-page steps — the Pro+ steps are simply skipped until you upgrade.\\nEverything above that runs on the server — the server-side, CRM and flow steps — is also a step you can add to a workflow, beside its function calls. The in-page effects are not, because a workflow runs on a server event with no page open.\\nCRM steps {#crm-steps}\\nFive server steps act on the CRM. None of them asks which contact: each acts on the person the triggering event names — by contactId when the event carries one (every CRM event does), otherwise by the email in the event\\'s data, which is what a form submission, a sign-up, a booking or a lead carries. When neither nam' },\n { path: '/marketing-and-automation/workflows-and-actions/actions-builder', title: 'Actions builder', heading: 'Sequences', anchor: '#sequences', text: 'A Wait step splits an automation in two. Everything before it runs immediately, and everything after it runs later, on its own — so a welcome series, a win-back, or a \"three days after they sign up, ask how it\\'s going\" is one automation rather than several.\\n- Wait holds for anything from a minute to 90 days.\\n- Wait for something to happen continues as soon as the event you pick happens for that person, or when the time you set runs out — whichever comes first. Put an Only if condition of waitTimedOut is not empty on the next step to make it the \"they never did it\" path.\\n- End the flow here stops the rest. With an Only if condition it is the exit — \"stop if they have ordered.\"\\nA few things worth knowing before you build one:\\n- A waiting automation needs to know who it is waiting for. The trigger\\'s information has to include an email address; a wait step reports an error without one.\\n- One at a time per person. Somebody already partway through an automation is not enrolled in it a second time until they finish.\\n- Editing an automation does not change it for people already waiting inside it. They finish the version they started. Your edit applies to everybody who enters afterwards.\\n- Turning an automation off, or deleting it, stops it for everyone — including people mid-wait.\\n- In-page steps after a wait do not run. By the time the wait ends the visitor\\'s browser has long since moved on, so put popups, alerts and element effects before the first wait.\\nEmails sent from a step after a wait are treated as marketing: they carry an unsubscribe link and header, skip anyone who has unsubscribed or bounced, respect the topic the step is set to, count toward how much mail one person receives from your site in a day, and go only to people with a marketing consent record. An email se' },\n { path: '/marketing-and-automation/workflows-and-actions/actions-builder', title: 'Actions builder', heading: 'Run history', anchor: '#run-history', text: 'Every automation row has a Runs button. It opens Runs — your automation with a Recent runs table of that one automation\\'s executions, in four columns:\\n| Column | What\\'s in it |\\n| Time | The clock time of the run; hover it for the full date and time. |\\n| Trigger | The event, in words — formSubmission shows as Form submitted. A custom event keeps the name you gave it. |\\n| Result | Succeeded, Failed, or Skipped. |\\n| What happened | For a run, what each step did, joined with ·. For a failure, the errors. For a skip, which condition stopped it. |\\nSkipped is a result, not an absence — see Only run when a field matches. A run that fired but whose steps errored is Failed, and the errors are in the last column rather than in a log you have to go and find.\\nAt the top of the Actions tab — and again on the Workflows tab, counting workflow runs — a line reads 1,284 action runs this month · 50,000 included: how many metered runs this site has used this calendar month, against the number your plan includes. This line is the only place action runs are counted for you — the billing usage meters meter workflow runs, not action runs. Watch it: once the month\\'s runs reach the limit, triggered automations stop running rather than queueing or billing on. The line renders nothing at all while the counter or the plan is still loading — 0 runs this month on a site that has run thousands is the one reading that would make you stop debugging.\\nWhat is and isn\\'t recorded {#what-is-and-isnt-recorded}\\nReference detail. Runs and skips are recorded on different rules, and the gaps are deliberate:\\n- A skip on pageView is never logged. Page-view actions run on every visit to every published page, so a record per visitor per non-matching action would be a write storm, not a run history. A pageView run tha' },\n { path: '/marketing-and-automation/workflows-and-actions/actions-builder', title: 'Actions builder', heading: 'Interactions from the Besigner', anchor: '#interactions-from-the-besigner', text: 'Select any element in the Besigner and use the Interactions section of the attributes panel to attach a when clicked, when hovered, or when scrolled into view trigger to that exact element — no CSS selectors to write. Pairing a when hovered trigger with an open menu or open drawer step is how you build hover-to-reveal navigation, and — like all basic in-page effects — it works on every plan. The interaction is saved and enabled on the element itself — it belongs to the document it is on, not to this section. The same panel lists the element\\'s existing interactions with an enable switch and a remove button — so you can pause or retire one without leaving the canvas — and offers \"A/B test this section\", which creates a draft section experiment for the element.' },\n { path: '/marketing-and-automation/workflows-and-actions/actions-builder', title: 'Actions builder', heading: 'When to use which', anchor: '#when-to-use-which', text: 'Both builders run the same steps — everything listed under Steps that runs on the server is offered in a workflow too, with the same fields, conditions and plan tiers. What differs is what starts the automation and whether there is a page in front of it:\\n| Use the actions builder | Use a workflow |\\n| Something a visitor does — a click, a hover, exit intent, a scroll depth | Something that happened on the server — a submission, an order, a CRM event |\\n| Anything that changes the page: menus, drawers, class toggles, overlays, redirects | Server-side work only; the in-page effects are not offered |\\n| Fastest to set up | Composes your functions and variables, and binds each result for the steps after it |' },\n { path: '/marketing-and-automation/workflows-and-actions/actions-builder', title: 'Actions builder', heading: 'Related', anchor: '#related', text: '- Build a workflow\\n- Webhooks' },\n { path: '/marketing-and-automation/workflows-and-actions/build-a-workflow', title: 'Build a workflow', heading: '', anchor: '', text: 'Build a workflow\\nA workflow is a sequence of steps that runs automatically when something happens on your site. This guide builds one end to end.\\nPlan availability Pro+. Workflow runs are metered per tier.' },\n { path: '/marketing-and-automation/workflows-and-actions/build-a-workflow', title: 'Build a workflow', heading: '1. Open the workflows page', anchor: '#1-open-the-workflows-page', text: 'In the console, go to Automation → Workflows and choose New workflow. Give it a name that describes the outcome (e.g. \"Welcome new member\").' },\n { path: '/marketing-and-automation/workflows-and-actions/build-a-workflow', title: 'Build a workflow', heading: '2. Choose a trigger', anchor: '#2-choose-a-trigger', text: 'Pick the site event that starts the workflow — for example a form submission, a new member, an order, or a CRM event such as Contact created. The event\\'s data is available to every step that follows, and the Filter field\\'s helper text names the keys the chosen event puts in scope.' },\n { path: '/marketing-and-automation/workflows-and-actions/build-a-workflow', title: 'Build a workflow', heading: '3. Add steps', anchor: '#3-add-steps', text: 'Add steps in order. Each one\\'s Do picker offers two kinds of step.\\nCall a function is what a workflow has always been, and what a new step starts as. It runs one of your functions and binds the answer under a Result name — step1 when you leave it blank — so every step after it can use the value in an expression. You can compose an existing workflow inside a function or variable, and vice versa; workflows are composable.\\nEverything else in the picker is a step from the actions builder — write to or update a dataset, send an email, notify site admins, enroll the contact in a list, assign a campaign, send a webhook, the CRM steps, and the flow steps below. They take the same fields, the same Only if condition, and the same plan tiers they take in the actions builder: server-side steps are Pro+, a webhook and custom JS are Business, and the CRM steps need the CRM.\\nThe in-page effects — menus, drawers, class toggles, redirects, analytics events — are not offered. A workflow runs on a server event, and there is no page open to perform them on. If you need one, build it as an action instead.\\nWait, and ending early {#waiting}\\nThe three flow steps make a workflow a sequence rather than a single burst, and they behave exactly as they do in an action:\\n- Wait holds for anything from a minute to 90 days. Everything before it runs at once; the rest picks up later, with the results your function calls had already bound still in scope.\\n- Wait for something to happen continues as soon as the event you pick happens for that person, or when the timeout you set runs out. Set an Only if condition of waitTimedOut is not empty on the next step to tell the clock from the event.\\n- End the flow here stops the rest. With an Only if condition it is a branch.\\nA waiting workflow needs to know who it' },\n { path: '/marketing-and-automation/workflows-and-actions/build-a-workflow', title: 'Build a workflow', heading: '4. Save and test', anchor: '#4-save-and-test', text: 'Save the workflow. When the trigger fires, the workflow runs and each run counts once toward your tier\\'s metered allowance — however many steps it has, and whatever they are. Its steps take their own plan gates one at a time, and none of them is counted as an action run on top.\\nTest run evaluates the function calls and nothing else. A step that sends, writes or charges is not something to rehearse, so it is left to the run itself; what comes back is each call\\'s result and the workflow\\'s return value.\\nAt the top of the Workflows tab, 12 workflow runs this month · 5,000 included reports the metered allowance you\\'re spending. When the month\\'s runs reach the limit, triggered workflows stop running — silently, without queueing and without billing on — so it\\'s a number worth glancing at before you wonder why an automation went quiet.\\nEach workflow row has a Runs button. It opens the run-history table described under Run history — Time, Trigger, Result, What happened.\\nWhat happened reads what each step did, joined — saved to Leads · sent email · tagged website — or the error when a step failed. A workflow of function calls alone reads Ran, with the time it took beneath.\\nRuns recorded before this shipped are not in the table Workflow executions were written in a shape the table did not recognize, so anything that ran before AGL-2222 is filtered out of it. Those runs are not lost — the dashboard\\'s Recent Activity card and Admin → Activity show every one of them, as \"Workflow ran on formSubmission\" with the duration appended and failures in red.' },\n { path: '/marketing-and-automation/workflows-and-actions/build-a-workflow', title: 'Build a workflow', heading: 'Duplicate a workflow', anchor: '#duplicate-a-workflow', text: 'Each workflow on the list has a Duplicate… button beside Edit. The copy keeps every step and the return value under the name you give it, and arrives disarmed: its trigger is cleared, so nothing runs twice until you open the copy and choose a trigger. It counts against your workflow allowance like a new workflow.' },\n { path: '/marketing-and-automation/workflows-and-actions/build-a-workflow', title: 'Build a workflow', heading: 'Tips', anchor: '#tips', text: '- Keep steps small and named — a workflow reads like a checklist.\\n- Watch your metered run count on the billing usage meters if a workflow runs on a high-frequency event.' },\n { path: '/marketing-and-automation/workflows-and-actions/build-a-workflow', title: 'Build a workflow', heading: 'Related', anchor: '#related', text: '- Actions builder\\n- Webhooks\\n- Bindings, variables & functions' },\n { path: '/marketing-and-automation/workflows-and-actions/overview', title: 'Automation', heading: '', anchor: '', text: 'Automation\\nAutomate what happens on your site. The section holds three tabs — Workflows, Actions and Webhooks — over one engine. A workflow step is either a call to one of your functions or any of the steps the actions builder offers, so \"write to a dataset, send an email, update the contact\" is a sequence of one workflow rather than two automations that have to find each other. Webhooks connect Aglyn to outside systems.\\nThe two tabs are two ways into the same steps, kept apart by what they run on:\\n| | What starts it | What it can do |\\n| Workflows | a server event — a form submission, an order, a new member, a CRM event | function calls and every server-side step |\\n| Actions | the same server events, and what a visitor does on the page — a click, a hover, exit intent, a scroll depth | everything a workflow can do, plus the in-page effects |\\nSo an automation that touches the page is an action; one triggered by something that happened on the server is a workflow; and the steps in between are the same steps. Nothing you have already built moves or changes.\\nPlan availability Basic in-page interactions (menu/drawer open-close, show/hide, class toggles, sticky nav, navigation, site alerts) are on every plan and never metered. The automations engine — server-side steps, analytics, overlays, and custom JS — is Pro+ with metered runs per tier. Webhooks are Business.' },\n { path: '/marketing-and-automation/workflows-and-actions/overview', title: 'Automation', heading: 'Workflows', anchor: '#workflows', text: '- Build workflows on the workflows page: a trigger and an ordered list of steps.\\n- Each step is a call to a function — composing your functions and variables — or any of the server-side steps the actions builder offers: write to a dataset, send an email, enroll in a list, assign a campaign, post a webhook, the CRM steps, and Wait.\\n- The in-page effects are not offered here. A workflow runs on a server event, where there is no page to toggle a class on or redirect.\\n- A run is metered once, however many steps it has and whatever they are.' },\n { path: '/marketing-and-automation/workflows-and-actions/overview', title: 'Automation', heading: 'Actions builder', anchor: '#actions-builder', text: 'The actions builder turns an event into an action — event → action automation without code, and the place to build anything that touches the page a visitor is looking at. Basic in-page effects (menus, drawers, show/hide, navigation) run on every plan; the server-side and advanced steps are Pro+ with metered runs.' },\n { path: '/marketing-and-automation/workflows-and-actions/overview', title: 'Automation', heading: 'Webhooks', anchor: '#webhooks', text: 'Outbound and inbound webhooks let Aglyn notify other systems and receive events from them. Webhooks are a Business-tier feature.' },\n { path: '/marketing-and-automation/workflows-and-actions/overview', title: 'Automation', heading: 'Run history and the run allowance', anchor: '#run-history', text: 'The Workflows and Actions tabs each open with a line reading 1,284 action runs this month · 50,000 included — the metered run allowance this site is spending. When a site reaches the month\\'s limit, triggered automations stop running rather than queueing or billing on.\\nEach row has a Runs button opening a four-column table — Time, Trigger, Result, What happened — with Succeeded, Failed and Skipped chips. The table is runs only: publishes, media saves and member changes stay in the site\\'s general activity feed, where there is nothing to say Succeeded about.\\nSkipped is the row that earns the table: an automation stopped by an unmet trigger condition is now recorded, naming the condition field, so \"why didn\\'t my automation fire?\" is answered where the runs are. A skip is not a metered run. See Run history for what is and isn\\'t recorded — page-view skips deliberately aren\\'t — and Build a workflow for where workflow executions currently show up.\\nMore detailed how-tos coming Recipes for common automations (notify on form submit, sync orders, etc.) are on the way.' },\n { path: '/marketing-and-automation/workflows-and-actions/overview', title: 'Automation', heading: 'Related', anchor: '#related', text: '- Build a workflow\\n- Actions builder\\n- Bindings, variables & functions\\n- Billing & plans' },\n { path: '/marketing-and-automation/workflows-and-actions/webhooks', title: 'Webhooks', heading: '', anchor: '', text: 'Webhooks\\nWebhooks connect Aglyn to the outside world. Outbound webhooks notify another system when something happens on your site; inbound webhooks let another system trigger something in Aglyn.\\nPlan availability Business. Webhooks are a Business-tier feature.' },\n { path: '/marketing-and-automation/workflows-and-actions/webhooks', title: 'Webhooks', heading: 'Outbound webhooks', anchor: '#outbound-webhooks', text: 'Send an HTTP request to a URL you control when a site event fires — for example, post to a Slack endpoint or your own API when an order is placed.\\n1. Add an outbound webhook and paste the destination URL.\\n2. Choose the event that should fire it.\\n3. Save. Aglyn calls your URL with the event payload each time it fires.' },\n { path: '/marketing-and-automation/workflows-and-actions/webhooks', title: 'Webhooks', heading: 'Inbound webhooks', anchor: '#inbound-webhooks', text: 'Expose an endpoint that external systems can call to trigger Aglyn — for example to kick off a workflow from a third-party tool.' },\n { path: '/marketing-and-automation/workflows-and-actions/webhooks', title: 'Webhooks', heading: 'Tips', anchor: '#tips', text: '- Pair an outbound webhook with a workflow when you need to transform data before sending it.\\n- Treat webhook URLs as secrets — anyone with the inbound URL can trigger it.' },\n { path: '/marketing-and-automation/workflows-and-actions/webhooks', title: 'Webhooks', heading: 'Related', anchor: '#related', text: '- Build a workflow\\n- Actions builder' },\n { path: '/workspace-and-billing/billing-and-plans/add-ons', title: 'Add-ons', heading: '', anchor: '', text: 'Add-ons\\nAdd-ons let you buy more of a specific thing without jumping a whole plan tier. They bill as extra line items on your existing subscription, prorate onto your current billing period, and take effect immediately.\\nManage them on Billing → Add-ons (requires the billing.manage permission and an active plan subscription — add-ons ride your plan\\'s subscription, so Free workspaces pick a plan first).' },\n { path: '/workspace-and-billing/billing-and-plans/add-ons', title: 'Add-ons', heading: 'What you can add', anchor: '#what-you-can-add', text: 'Prices live on one page Per-unit add-on prices are on aglyn.com/pricing, and what YOUR workspace would pay is shown in Billing before you confirm. This page covers what each add-on does and how it is billed; restating the numbers here would just be a second copy to keep in step.\\n| Add-on | What it does | How it is priced |\\n| Manager seats | Workspace manager seats beyond the included count | Per seat, per month — cheaper on higher plans |\\n| Collaborator seats | Per-site collaborator (teammate) seats beyond the included count | Per seat, per month — cheaper on higher plans |\\n| Extra datasets | Additional shared datasets across the workspace | Per dataset, per month — cheaper on higher plans |\\n| Extra sites | Publish more sites than your plan includes | Per site, per month — cheaper on higher plans |\\n| POS registers | One extra point-of-sale register, assigned to one site | Flat per register, per month — the same on every plan |\\n| Event Calendar | The event manager + calendar elements, workspace-wide | Flat per month — the same on every plan |\\n| Aglyn AI | Generative building (rolling out) — pages, emails, campaigns, products and more — plus a monthly band of AI credits added to your plan\\'s, workspace-wide | Flat per month, priced by plan — higher on higher plans, and so is the band it adds |\\n- Prices are per unit per month. On annual billing, add-ons bill yearly alongside your plan (12× the monthly price) — a subscription has one billing interval.\\n- POS registers also require a plan with POS (Pro and above), and each one is assigned to a specific site — see Assigning register seats.\\n- Collaborator seats work the same way as POS registers: bought once for the workspace, then assigned to a site — see Assigning collaborator seats.\\n- Event Calendar is a single workspace-wide' },\n { path: '/workspace-and-billing/billing-and-plans/add-ons', title: 'Add-ons', heading: 'Assigning register seats', anchor: '#assigning-register-seats', text: 'Unlike every other add-on, a POS register seat is bought once for your workspace and then assigned to one site. Buying a seat does not raise the register limit everywhere — it adds one register\\'s worth of capacity that you place where you need it.\\nYour plan already gives every site a register allowance on its own (Pro 1, Business 2, Advanced 5). A seat is added on top of that, for the one site you assign it to.\\nTo assign one, go to Billing → POS register seats, directly under Plan add-ons. The card shows how many seats you\\'ve purchased, how many are assigned, and how many are unassigned, then lets you add or remove seats per site.\\n- Seats are reassignable. Moving a seat between sites is immediate and costs nothing — you\\'re not buying or canceling anything, just relocating capacity.\\n- Taking a seat off a busy site doesn\\'t delete anything. If the site is running more registers than it can after the move, the extra ones stay set up but stop taking sales until you assign seats back or remove them. The console warns you before the move, with the number affected.\\n- Deleting a site returns its seats to the pool automatically, in the same action that removes the site. There\\'s nothing to reclaim by hand.\\n- You need the Manage billing permission to move seats, the same permission that buys them.\\nIf every purchased seat is already assigned and a site needs another register, either move a seat off a site that isn\\'t using it, or buy another seat under Plan add-ons.' },\n { path: '/workspace-and-billing/billing-and-plans/add-ons', title: 'Add-ons', heading: 'Assigning collaborator seats', anchor: '#assigning-collaborator-seats', text: 'Collaborator seats work exactly like register seats: a seat is bought once for your workspace and then assigned to one site. Buying a seat does not raise the collaborator limit on every site — it adds one site\\'s worth of capacity that you place where you need it.\\nYour plan already gives every site a collaborator allowance on its own. A purchased seat is added on top of that, for the one site you assign it to, up to your plan\\'s per-site maximum. Past that maximum, more seats can\\'t raise the limit and the only path is a plan upgrade.\\nTo assign one, go to Billing → Site collaborator seats, directly under Plan add-ons. The card shows how many seats you\\'ve purchased, how many are assigned, and how many are unassigned, then lets you add or remove seats per site. The same numbers appear on each site\\'s own Collaborators card.\\n- Seats are reassignable. Moving a seat between sites is immediate and costs nothing — you\\'re not buying or canceling anything, just relocating capacity.\\n- Nobody is ever removed for being over the limit. If a site has more collaborators than its limit — because a seat moved, a plan changed, or the limit was corrected — everyone on it keeps their access and stays signed in. The only effect is that the site can\\'t take on another collaborator until it\\'s back under. The console warns you before a move that would leave a site over, with the number affected.\\n- Deleting a site returns its seats to the pool automatically, in the same action that removes the site. There\\'s nothing to reclaim by hand.\\n- You need the Manage billing permission to move seats, the same permission that buys them.\\nIf every purchased seat is already assigned and a site needs another collaborator, either move a seat off a site that isn\\'t using it, or buy another seat under Plan add-ons.' },\n { path: '/workspace-and-billing/billing-and-plans/add-ons', title: 'Add-ons', heading: 'Aglyn AI', anchor: '#aglyn-ai', text: 'Generative building is rolling out. The add-on is described here in full, but the doors that build are not open in every workspace yet. Aglyn Assist — the assistant that answers questions — is available today.\\nAglyn AI is the generative add-on: with it on, the assistant builds for you — pages, components, emails, campaigns, products and more — rather than only answering questions about them. It is bought once for the workspace, on any paid plan, and applies to every member who has the permission to generate. On Starter it also unlocks the guided AI assist that Pro and up already include. What each door does is under Aglyn AI.\\n- Everything it builds is a draft. The add-on cannot publish a page, change a live one or send an email. It writes drafts and unpublished versions, which a person reviews and publishes through the buttons that already exist — see the draft rule.\\n- One pool, one meter. The add-on adds a band of AI credits to the band your plan already includes, every month. There is no second meter: the AI credits meter on Billing → Usage shows your plan\\'s credits and the add-on\\'s as one figure, and the same overage rate, stop switch and monthly ceiling apply to the whole band — see AI credits and overage.\\n- Priced by plan. The add-on\\'s monthly price and the credits it adds both rise with the plan. The figures are on aglyn.com/pricing and in Billing before you confirm; on annual billing it bills yearly with the plan, like every add-on. Enterprise workspaces carry generative building in their agreement instead of buying it.\\n- Starter widens a band it already has. Starter includes its own monthly AI credits, and credits past them are sold at the same rate as Pro — so the stop switch and the ceiling are on its billing page with or without the add-on. Adding Aglyn AI in' },\n { path: '/workspace-and-billing/billing-and-plans/add-ons', title: 'Add-ons', heading: 'How changes bill', anchor: '#how-changes-bill', text: '- Adding or removing units shows a prorated preview of today\\'s charge before you confirm, and takes effect immediately.\\n- Removing an add-on credits the unused time onto your next invoice.\\nAdd-ons are not on the plan-switch schedule Add-on changes are always immediate and always prorated, in both directions. Plan switches are not: since Aglyn moved to end-of-cycle downgrades, a plan downgrade waits for your period end and charges $0 today, while only a plan upgrade is immediate and prorated. Removing an add-on still credits you today. See when each change takes effect.\\n- Hard caps: seat and dataset add-ons stop at your plan\\'s maximum (for example, Starter tops out at 5 workspace managers). Past the cap, the answer is a plan upgrade — the Billing page tells you when you\\'re there.' },\n { path: '/workspace-and-billing/billing-and-plans/add-ons', title: 'Add-ons', heading: 'Plan switches and cancellation', anchor: '#plan-switches-and-cancellation', text: '- Switching plans keeps your add-on quantities and re-prices them at the new plan\\'s rates. If the new plan doesn\\'t sell one of your add-ons, it\\'s removed and the switch confirmation says so. On an upgrade this happens today, in the same prorated update as the plan change. On a downgrade it happens on the effective date along with the plan — your add-ons keep running, at your current plan\\'s rates, for the rest of the period you already paid for.\\n- Canceling (or a subscription that dies after failed payments) ends your add-ons with the plan — they bill on that subscription, so entitlement enforcement stops counting them at the same moment the plan downgrades to Free.' },\n { path: '/workspace-and-billing/billing-and-plans/add-ons', title: 'Add-ons', heading: 'Related', anchor: '#related', text: '- Billing & plans overview\\n- Downgrading, canceling & your data\\n- Teams, roles & membership' },\n { path: '/workspace-and-billing/billing-and-plans/bandwidth', title: 'Bandwidth', heading: '', anchor: '', text: 'Bandwidth\\nBandwidth is how much traffic your published sites serve in a calendar month. Every plan includes an amount. What happens when you pass it is the part worth reading, because it is not the same on Free as it is on a paid plan.\\nPlan availability Every plan has a bandwidth allowance. On a paid plan going past it is metered and billed, and your sites keep serving. On Free it is a hard stop: the site is paused until the start of the next month.' },\n { path: '/workspace-and-billing/billing-and-plans/bandwidth', title: 'Bandwidth', heading: 'What each plan includes', anchor: '#what-each-plan-includes', text: '| Plan | Included bandwidth per month | Past the allowance |\\n| Free | 2 GB | Sites are paused until the start of next month |\\n| Starter | 50 GB | Keeps serving; the extra is billed |\\n| Pro | 125 GB | Keeps serving; the extra is billed |\\n| Business | 185 GB | Keeps serving; the extra is billed |\\n| Scale | 290 GB | Keeps serving; the extra is billed |\\n| Advanced | 345 GB | Keeps serving; the extra is billed |\\n| Agency | 1,540 GB | Keeps serving; the extra is billed |\\n| Enterprise | 3,080 GB by default; more by agreement | Keeps serving; not billed — your agreement sets the terms |\\nThe allowance is per organization, across every site in it — not per site. If you run four sites on one Business plan, they share the 185 GB.' },\n { path: '/workspace-and-billing/billing-and-plans/bandwidth', title: 'Bandwidth', heading: 'Where to see your usage', anchor: '#where-to-see-it', text: '1. In the console, open Billing from the organization menu.\\n2. Find the Usage card.\\n3. Read the row labeled Bandwidth (this month, organization).\\nThe meter resets on the first of each month, in UTC.\\nYou do not have to be watching it. Organization admins get an in-app notification and an email at 80% of the allowance and again at 100%, once per threshold per month. What those messages say depends on your plan, and the difference is the point:\\n- On a paid plan the 100% message reads \"You\\'re past your included monthly bandwidth — extra usage is now billed\", and tells you the overage appears on your monthly invoice.\\n- On Free it reads \"You\\'ve reached your monthly bandwidth limit\", and points you at Billing to upgrade — because on Free there is no overage to bill, only a stop.' },\n { path: '/workspace-and-billing/billing-and-plans/bandwidth', title: 'Bandwidth', heading: 'What a paused Free site looks like', anchor: '#paused', text: 'When a Free organization passes its band, its published sites stop serving pages and answer with a plain notice instead:\\n> Over the monthly traffic limit\\n>\\n> This site has used all of the traffic included with it this month, so its pages are\\n> paused until the start of next month. Nothing is wrong with your connection, and\\n> nothing here has been removed.\\nThree things are true about that page and are worth knowing before you see it:\\n- Nothing is deleted. Your screens, media, datasets and settings are untouched. The console keeps working normally — only the public site is paused.\\n- It clears itself. The pause is stamped with the month it belongs to. When the month turns over it stops applying, with no action from you and nothing to un-set.\\n- Upgrading lifts it within about a minute. The check re-reads your plan every time, so a plan change releases the pause without waiting for the next month or for any sweep to run.\\nSearch engines are told not to index the notice, so a pause does not cost you your rankings.' },\n { path: '/workspace-and-billing/billing-and-plans/bandwidth', title: 'Bandwidth', heading: 'Why a site can go over before it is paused', anchor: '#timing', text: 'Usage is totalled where page views are already counted — the analytics beacon, on a sampled cadence — so a Free site can pass its band and keep serving for a few hundred more views before the pause takes hold. That is deliberate: the alternative is metering every single request, which would put a database read on every page of every site on the platform to answer a question only Free organizations can ever fail. The allowance is a monthly budget, not a per-second valve.\\nA daily job re-checks the same thing organization-wide, so an organization running more than one site is still totalled across all of them.\\nThe reverse is much faster still: an upgrade releases within roughly a minute.' },\n { path: '/workspace-and-billing/billing-and-plans/bandwidth', title: 'Bandwidth', heading: 'Reducing bandwidth', anchor: '#reducing-bandwidth', text: 'Your meter counts page views, not what a page weighs. Every counted view moves it by the same fixed amount (see How usage is counted), so using less of the allowance means having fewer views counted.\\n- Check Analytics → Traffic for a page that is unexpectedly popular. Views of that page are what move the meter.\\n- Stop counting your own visits while you check your pages, with ?aglyninternal=1 (see Which views are counted). Previews and the design canvas are never counted.\\n- If the traffic is real and you need more room, the lever is a plan with a larger band.\\nWhat a page contains does not change the meter:\\n- Images. A 4 MB photograph costs your visitors load time, not allowance. Serving a smaller image through CDN delivery makes the page faster; it does not stretch your band.\\n- Video. Playing, pausing and seeking an embedded video are not page views, so plays do not move the meter, however long the video is or however many times it is watched.' },\n { path: '/workspace-and-billing/billing-and-plans/bandwidth', title: 'Bandwidth', heading: 'Reference', anchor: '#reference', text: 'For developers and operators. None of this is needed to use the feature.\\nHow usage is counted\\nBandwidth is derived from page views rather than measured byte-for-byte at the edge. The platform uses a fixed accounting figure of 1,012.8 KB per page view and converts in both directions, so the meter you read in GB and the counters the analytics pipeline writes are the same number expressed differently. On Free, 2 GB works out to roughly 2,070 page views a month; on Starter, 50 GB is roughly 51,800, and the same division gives every other band.\\nWhich views are counted\\nA view is counted when a published page reports one from a visitor\\'s browser. Two kinds of traffic are deliberately not counted, so they neither bill nor consume your band:\\n- Views served by a preview or development build. Only a real production deployment reports. A local development server and a preview URL serve your pages without moving your meter. (A self-hosted deployment at a real domain is a production deployment and counts normally; one served over localhost does not.)\\n- Your own browser, once you tell the platform it is yours. Visit any page of your site with ?aglyninternal=1 on the URL and that browser stops being counted on that site — useful while you are checking your own pages. The visit carrying the parameter is not counted either. The setting lives in that browser and lasts until you clear its storage or visit with ?aglyninternal=0, and it is per site address, so set it once per domain you want to exclude.\\nEditing your site does not count either: the design canvas and the console\\'s preview never report a view, so building a page is free however long you spend on it.\\nThat 1,012.8 KB is a billing convention, not a measurement of your pages. It is set from a real cold load of one of our own publis' },\n { path: '/workspace-and-billing/billing-and-plans/bandwidth', title: 'Bandwidth', heading: 'Related', anchor: '#related', text: '- Billing & plans\\n- Storage overage\\n- Analytics\\n- Media library & CDN' },\n { path: '/workspace-and-billing/billing-and-plans/downgrading-and-canceling', title: 'Downgrading, canceling & your data', heading: '', anchor: '', text: 'Downgrading, canceling & your data\\nChanging to a lower plan, canceling, or deleting are three different things with three different effects. The short version: downgrading and canceling never delete anything — only deletion does, and deletion is deliberate and reversible within a hold window.' },\n { path: '/workspace-and-billing/billing-and-plans/downgrading-and-canceling', title: 'Downgrading, canceling & your data', heading: 'When each change takes effect', anchor: '#when-changes-take-effect', text: 'Plan changes are deliberately asymmetric, and knowing which direction you\\'re going tells you what you\\'ll be charged — and when. Nothing is taken from your card at the moment you change a plan or an add-on: the adjustment is written onto your next invoice, so a mid-cycle upgrade appears there rather than as a charge today.\\n| You\\'re doing | When it takes effect | What you are charged, and when |\\n| Upgrading to a higher plan | Immediately | Nothing today. A prorated amount for the rest of the current period is added to your next invoice |\\n| Downgrading to a lower paid plan | At the end of your current billing period | Nothing |\\n| Canceling | At the end of your current billing period | Nothing |\\n| Adding or removing an add-on | Immediately | Nothing today. Prorated onto your next invoice — removing one credits you back the same way |\\nThe reason downgrades wait is simple: you already paid for the current period, so you keep everything you paid for until it runs out. There is no proration credit on a downgrade because there is nothing to credit — nothing changes today.' },\n { path: '/workspace-and-billing/billing-and-plans/downgrading-and-canceling', title: 'Downgrading, canceling & your data', heading: 'Downgrading to a lower plan', anchor: '#downgrading-to-a-lower-plan', text: 'A downgrade changes your entitlements — which features you can use and how much — but it does not remove anything you\\'ve already created, and it does not take effect the moment you click.\\nThe pending-downgrade window {#pending-downgrade}\\nWhen you switch to a lower paid plan:\\n1. Confirm the switch. The dialog states the effective date and that $0 is due today.\\n2. Your current plan keeps running to the end of the period you already paid for. Every feature of the higher plan stays on the whole time.\\n3. A chip appears on the Current plan card — \"moves to \\\\{plan\\\\} on \\\\{date\\\\}\" — so the scheduled change is visible every time you open Billing, not just in the confirmation you clicked past.\\n4. On the effective date the plan changes and the lower price starts.\\nChanging your mind {#keep-my-current-plan}\\nWhile a downgrade is pending, the Current plan card shows Keep my current plan. Clicking it cancels the scheduled change, and the plan you\\'re on simply continues at its normal price. You can also do this by switching to any higher plan — an upgrade cancels the pending downgrade and then applies immediately, in that order.\\nCanceling your subscription while a downgrade is pending also drops the schedule, so the two never collide: you cancel from the plan you\\'re on, not from the one you were scheduled to land on.\\nWhat changes when the downgrade lands {#what-changes-on-a-downgrade}\\nNothing is deleted. Every site, file, dataset, product, and team member you have keeps existing.\\nWhat changes:\\n- You can\\'t create more past the new limit. If Pro includes 3 sites and you have 5, all 5 keep working — but you can\\'t add a 6th until you\\'re back under the limit or upgrade again. The same applies to datasets, team seats, products, POS registers, and storage.\\n- Paid features you no longer have tur' },\n { path: '/workspace-and-billing/billing-and-plans/downgrading-and-canceling', title: 'Downgrading, canceling & your data', heading: 'Canceling your subscription', anchor: '#canceling-your-subscription', text: '- Cancel any time. Your subscription runs to the end of the paid period — a chip on the Billing page shows the end date, and you can resume before it hits.\\n- When the period ends, your organization resolves to the Free plan. This is the same as a downgrade: nothing is deleted, over-limit resources persist, and paid features turn off.\\n- A failed payment doesn\\'t cancel you immediately — your access continues while Stripe retries the charge (a past-due banner shows), and entitlements only drop to Free if the subscription actually lapses.\\nWhat the Cancel button actually opens {#the-cancel-dialog}\\nCancel subscription doesn\\'t cancel on the first click. It opens a short dialog with up to four steps. We\\'d rather tell you what\\'s in it here than have it surprise you:\\n1. \"Why are you leaving?\" — one choice from a fixed list (too expensive, missing features, not using it enough, switching to something else, technical problems, just pausing, something else) plus an optional comment box. This is the only required step, and the comment is genuinely optional.\\n2. A downsell, if a smaller paid plan would still fit you. It names the plan and its price. No thanks, continue moves on.\\n3. A winback discount, if you haven\\'t used one before. It states the exact percentage and the exact number of months it lasts, because a discount whose end date you can\\'t see isn\\'t an offer. No thanks, continue moves on.\\n4. The confirmation. This step also lists anything you\\'ll be over the Free plan\\'s limits on once the cancel lands — extra sites, datasets, seats — so the consequence is visible at the moment you decide, not afterwards.\\nThings worth knowing about the offers:\\n- Every step has a visible way past it. There is no step you can only leave by staying. Keep my plan on the last step closes the dialog and' },\n { path: '/workspace-and-billing/billing-and-plans/downgrading-and-canceling', title: 'Downgrading, canceling & your data', heading: 'Deleting a single site', anchor: '#deleting-a-single-site', text: 'To remove just one site (not the whole organization), open the site\\'s Admin → Danger zone and use Delete site. A site admin types the site name to confirm, and it\\'s deleted immediately — its screens, media, and settings are permanently removed and its address stops resolving. Unlike an organization deletion there\\'s no hold, so export a backup first (Setup → Backup & restore) if you might want it back. Your other sites and the organization are unaffected.' },\n { path: '/workspace-and-billing/billing-and-plans/downgrading-and-canceling', title: 'Downgrading, canceling & your data', heading: 'Deleting your organization', anchor: '#deleting-your-organization', text: 'Deletion is separate from canceling and is the only thing that removes your data. It\\'s intentional and reversible for a short window:\\n1. Request deletion — an owner starts it from the organization settings; you confirm by typing the organization name. Export anything you want to keep before you start (Setup → Backup & restore) — the hold is the window for it, and nothing is kept for you afterwards.\\n2. A hold period follows the request. During the hold nothing is deleted and you can cancel the request to fully restore.\\n3. After the hold, the organization — its sites, files, datasets, and account records — is permanently erased. This step is irreversible, and we keep no copy of the erased data. What we retain is an internal record that the erasure happened: which organization, when it was requested, and how many sites, members and credentials were removed.\\nDeletion covers your organization\\'s sites and their stored files and data. If you own an organization that still has other members, transfer or remove them first — a shared organization isn\\'t deleted out from under its members.\\nGDPR / right to erasure You can request erasure of your personal data at any time. Deletion permanently removes your records and leaves us with no copy of them — only a record that the erasure was carried out, which is itself kept for a limited period. Export anything you need during the hold. Contact support if you need an erasure completed outside the self-serve flow.' },\n { path: '/workspace-and-billing/billing-and-plans/downgrading-and-canceling', title: 'Downgrading, canceling & your data', heading: 'Related', anchor: '#related', text: '- Billing & Plans\\n- Teams, roles & membership' },\n { path: '/workspace-and-billing/billing-and-plans/overview', title: 'Billing & Plans', heading: '', anchor: '', text: 'Billing & Plans\\nYour plan determines which features you can use and how much of each. Aglyn checks entitlements (can you use a feature) and quotas (how much) throughout the product, and shows usage meters so you always know where you stand.\\nPlan availability Every site has a plan. Free, Starter, Pro, Business, Scale, Advanced, and Agency unlock progressively more, and Enterprise sits above them all.' },\n { path: '/workspace-and-billing/billing-and-plans/overview', title: 'Billing & Plans', heading: 'Tiers & entitlements', anchor: '#tiers--entitlements', text: 'Prices live on one page Current prices — monthly, annual, transaction fees and per-unit overage rates — are on aglyn.com/pricing, and your own plan and next invoice are in Billing in the console. This page explains how billing WORKS; it deliberately does not restate the numbers, because a second copy is a copy that goes stale.\\n| Plan | Commerce |\\n| Free | Build & publish only — no selling |\\n| Starter | Sell products, with the highest transaction fee of the paid tiers |\\n| Pro | More products, a lower fee, POS, abandoned-cart recovery, reviews, dropshipping |\\n| Business | More products again, a lower fee, subscriptions & paywalls, gift cards |\\n| Scale | Higher product and site limits, lower fee again |\\n| Advanced | Unlimited products, no transaction fee, high-volume commerce & API |\\n| Agency | Many sites under one organization, white-label |\\n| Enterprise | Twice Agency\\'s capacity by default and more by agreement, SSO, white-label, no transaction fee |\\nTransaction fees are Aglyn platform fees on the sales you take through your site — storefront orders, paid memberships and paid bookings alike — separate from Stripe\\'s payment-processing fees. Upgrading is the way to reduce them.\\nEvery plan also includes an amount of monthly traffic — 2 GB on Free, rising to 3,080 GB on Enterprise by default (an agreement can set more). Passing it is metered and billed on a paid plan, and pauses the site until the start of next month on Free. See Bandwidth for the table and for what a paused site shows a visitor.\\nEnterprise\\nEnterprise is the one tier you cannot buy from the Billing page. It has no list price — the plan, term, and invoicing are agreed with us, and we provision the organization directly. On top of everything in Agency it doubles every quota Agency bounds — and your agreement c' },\n { path: '/workspace-and-billing/billing-and-plans/overview', title: 'Billing & Plans', heading: 'Usage meters', anchor: '#usage-meters', text: '- The billing page shows meters for every quota — storage, bandwidth, datasets, seats, sends, CRM records (with the contacts, companies and deals beneath the total), one-to-one emails sent today, AI assist credits, and more — with redesigned plan cards.\\n- A usage-cap banner appears site-wide at 80% and 100% of a quota, with an upgrade link.\\n- Org admins also get an in-app notification when email sends, dataset count, data storage, or bandwidth crosses 80% or 100% — once per threshold per month, so nobody has to be watching the console to find out. The bandwidth message differs by plan, because what happens next differs by plan: paid organizations are told the extra is billed, Free organizations are told the site will be paused.\\n- The monthly email allowance caps campaign sends. Transactional mail — password resets, invites, order confirmations, booking reminders and workflow notifications — is counted toward your usage but is never blocked by the cap, at any plan. Going over shows up as an overage on the usage rollup rather than as mail your customers never receive.\\n- Usage is rolled up with a cost-plus estimate for metered features.\\nWho is generating what {#who-is-generating-what}\\nBeneath the meters on Billing → Usage, a table lists each member\\'s AI credits for a month — their share of the workspace\\'s spend, requests, and how often the assistant refused them — dearest first, with the kinds of request they mostly made. The month picker walks the last thirteen months; Export CSV hands the same month to a spreadsheet, and the console refuses to save a file that arrived short.\\nOpen a member from the table for their page, which splits the figure by site and by kind for this month and last — so an agency or a multi-brand workspace can see which client site\\'s people are gener' },\n { path: '/workspace-and-billing/billing-and-plans/overview', title: 'Billing & Plans', heading: 'Storage overage', anchor: '#storage-overage', text: 'Each site includes a fixed amount of storage. On a paid plan, going past it is not a wall:\\n- Uploads keep working. You are never stopped from adding files because you reached your included storage.\\n- The extra storage is billed on your monthly invoice at our infrastructure cost plus 30% — the exact per-GB rate is on the pricing page and in Billing → Storage cap.\\n- We tell you before it happens. You get an alert as you approach your included storage and another when you cross it, so the invoice is never the first you hear of it. See usage meters.\\nIf you would rather uploads stopped\\nSet a monthly storage cap in Billing → Storage cap. This is optional and off unless you choose it. Once a month\\'s storage overage would pass the amount you set, new uploads are refused and you are never billed above that number.\\nYou can change or remove the cap at any time, including while you are over your included allowance. Nothing is ever deleted — a cap only affects new uploads, and removing one takes effect immediately.\\nFree plans are never billed for storage On the Free plan there is no storage overage at all. Your included storage is a fixed cap: uploads stop there, nothing is metered, and no amount of usage produces a charge — so there is nothing to cap and nothing to configure. Enterprise storage is a fixed default of 120 GB per site that an agreement raises, with no overage either: uploads stop there.\\nNo surprise bills Two things prevent one, and they work in different ways. The alerts at 80% and 100% of your allowance mean nobody first learns about overage from an invoice. The optional cap means anyone who wants a hard ceiling can have one, at a number they choose.' },\n { path: '/workspace-and-billing/billing-and-plans/overview', title: 'Billing & Plans', heading: 'AI assist overage', anchor: '#assist-overage', text: 'Paid plans include a monthly band of AI assist credits — the Aglyn Assist credits meter on the billing page shows how much of it this month\\'s assistant use has drawn. The Aglyn AI add-on widens that band and opens generative building; it is one pool, so everything below applies to the widened band as a whole. What the credits are spent on, and what each kind of request costs relative to the others, is under Aglyn AI. On a plan that sells credits past the band, reaching it is not a wall:\\n- The assistant keeps answering. Nothing stops because you reached your included credits.\\n- The extra credits are billed on your monthly invoice, per 1,000 credits over the band, at your plan\\'s rate — the exact figure is on the pricing page and in Billing → Usage → AI assist overage.\\nIf you would rather the assistant stopped {#stop-ai-assist-at-the-included-band}\\nTurn on Stop AI assist at the included band in Billing → Usage → AI assist overage. This is optional and off unless you choose it. Once it is on, the assistant refuses once the included credits are used — the refusal names this switch and where it lives — and you are never billed for AI assist. Turn it off at any time to keep going at your plan\\'s rate; either change takes effect on the next message. Changing it needs the Manage billing permission.\\nIf you would rather it kept going, up to a figure you choose {#ai-overage-ceiling}\\nWith the switch off, the same card offers a monthly ceiling on the extra credits you will pay for: Stop AI when this month\\'s overage reaches a dollar figure you type. Once the month\\'s overage — the extra credits priced at your plan\\'s rate, exactly as the invoice would price them — reaches that figure, the assistant stops for the rest of the month, and the refusal names the ceiling and where to raise or r' },\n { path: '/workspace-and-billing/billing-and-plans/overview', title: 'Billing & Plans', heading: 'Usage budget', anchor: '#usage-budget', text: 'A usage budget is a monthly amount you choose, plus the percentages of it you want to hear about — the same shape as a Google Cloud billing budget. Set it in Billing → Monthly usage budget.\\n- It warns, it never limits. Passing a budget sends a notification and an email. Nothing stops, no upload is refused, and your bill is unaffected. If you want usage to actually stop, that is the storage cap or the AI assist stop and ceiling above — different controls, deliberately kept separate.\\n- You choose the alert points. The default is 50%, 90% and 100%; you can set your own, including percentages above 100 so a runaway month keeps speaking. Up to six.\\n- One alert per percentage per month. Crossing 50% tells you once, not once an hour. Climbing to the next percentage alerts again, and every percentage resets when the month does.\\n- Both channels. Alerts arrive in the console notification menu and by email to your workspace owners and admins, so you do not have to be signed in to find out.\\n- The figure is the invoice\\'s own. Budget alerts quote the same metered total the billing page shows and the invoice charges — totalled once a day, so the first days of a month may show no total yet.\\nChanging the amount clears the alert history for the month, so a budget you lower starts warning you against the new number straight away.' },\n { path: '/workspace-and-billing/billing-and-plans/overview', title: 'Billing & Plans', heading: 'Seats', anchor: '#seats', text: '- Team seats (workspace-wide) and per-site collaborator seats are metered and enforced per tier — seats cover the people who build and manage your sites.\\n- Buy paid seat add-ons to grow your team beyond the included seats — self-serve from Billing → Add-ons, alongside extra sites, datasets, POS registers, the Event Calendar, and Aglyn AI.\\n- Site member accounts are not seats: visitors who sign up to your published site are unlimited on every plan.' },\n { path: '/workspace-and-billing/billing-and-plans/overview', title: 'Billing & Plans', heading: 'CRM records', anchor: '#crm-records', text: 'Your CRM — the contacts captured from forms, member sign-ups, buyers and bookings, and the companies and deals your team files beside them — is priced as one CRM records band, not a hard cap. A record is a contact, a company or a deal; tasks, logged activities, pipelines and custom fields are not counted.\\n- Each tier includes a band: Free 100, Starter 1,000, Pro 10,000, Business 50,000, Scale 100,000, Advanced 150,000, Agency 500,000 records; Enterprise 1,000,000 by default, and more by agreement.\\n- On paid tiers, growing past the band never blocks or drops anything — extra records are metered overage on your monthly invoice, at a per-1,000 rate that falls as you move up the plans — see pricing for the current rate on your plan, per extra 1,000 records per month. Upgrading a tier is always cheaper than sustained overage.\\n- On Free, which includes no CRM, the band is a hard limit on capture: past 100 records a site adds no new person — a new visitor keeps their member account and orders, but no contact is kept — and the count of missed captures is recorded. On Billing → Usage the band reads Stored records (organization), captioned At this limit your sites stop adding new people. Working with them is part of the CRM, included from Starter. The Free plan card names no CRM records.\\n- The billed count is the size of your CRM at the end of the month, not its size on any other day. The three collections are a running total rather than a monthly tally, so a month has to be charged on one moment in it, and that moment is the last daily reading taken before the month closes. Records added after a month ends belong to the new month; records deleted after it ends do not undo the month that already ran.\\n- On a plan with the CRM, the billing page\\'s CRM records (organization) meter sh' },\n { path: '/workspace-and-billing/billing-and-plans/overview', title: 'Billing & Plans', heading: 'Organization data', anchor: '#organization-data', text: '- Datasets are organization-scoped: counts and storage meter against your organization, not individual sites.\\n- Each paid tier includes a number of datasets and an included data storage size (Starter 1 GB, Pro 5 GB, Business 25 GB, Scale 50 GB, Advanced 100 GB, Agency 500 GB).\\n- Extra datasets are a monthly add-on whose price falls as you move up the plans; storage beyond the included size is metered overage, billed per GB-month on your monthly invoice.\\n- The billed size is what you were storing at the end of the month, on the same basis as the audience band above: the last daily reading taken before the month closes. Clearing datasets after a month has ended lowers the next invoice, not the one for the month that just finished.' },\n { path: '/workspace-and-billing/billing-and-plans/overview', title: 'Billing & Plans', heading: 'API access', anchor: '#api-access', text: 'The customer REST API is a Business tier and above feature — mint scoped API keys and call the versioned /v1 endpoints from anywhere. Requests are metered per organization:\\n| Plan | Included requests / month | Overage |\\n| Business | 100,000 | Metered per additional 1,000 |\\n| Scale | 300,000 | Metered, at a lower rate than Business |\\n| Advanced | 1,000,000 | Metered, lower again |\\n| Agency | 5,000,000 | Metered, the lowest per-unit rate |\\n| Enterprise | 10,000,000 by default; more by agreement | — |\\n- Requests past the included quota keep working and bill as metered overage on your monthly invoice — never a hard wall mid-integration.\\n- The billing page shows an API requests meter, and the usage-cap banner warns at 80% and 100% of the included quota.\\n- Only requests that pass authentication and the rate limit are counted — rejected calls (bad key, wrong plan, rate-limited) are never billed.' },\n { path: '/workspace-and-billing/billing-and-plans/overview', title: 'Billing & Plans', heading: 'Payments', anchor: '#payments', text: 'Billing runs through Stripe. Paid features (commerce, bookings, campaigns) share the same Stripe integration.\\nYour billing details live on the Billing page in the console, one card per thing you might want to change. Each card saves on its own, so editing your address never touches your tax ID and vice versa.\\nPaying an outstanding invoice {#outstanding}\\nIf a payment fails, the invoice stays open and the Billing page shows it with a Pay now button.\\n- It works even if the subscription has already been canceled. Dunning cancels subscriptions after enough failed retries; the invoice is still owed and still payable, and paying it does not require a plan.\\n- Your bank may ask you to confirm the payment. That step is your bank\\'s, not ours, and it appears for the same reason it does anywhere else.\\n- We do not mark it paid because the button worked. The invoice updates when Stripe confirms the money arrived, so what you see is the settled state rather than an optimistic one.\\n- The amount is the amount the invoice was issued for. Tax on an invoice is fixed when it is issued and is never recalculated later.\\nWhat a plan will cost {#plan-total}\\nBefore you subscribe, the Billing page quotes the plan you are looking at with tax included, taken from Stripe\\'s own invoice preview rather than worked out here.\\n- A total is only shown as final once tax has been calculated, which needs your billing address. Until then the page says so rather than showing a total that quietly leaves tax out.\\n- A zero tax is explained, because a zero has several meanings: reverse charge applies and you account for the tax under your own registration; your account is registered as tax-exempt; or nothing is charged in your location.\\n- Promotion codes are applied here. An invalid or expired code is refused immedia' },\n { path: '/workspace-and-billing/billing-and-plans/overview', title: 'Billing & Plans', heading: 'Related', anchor: '#related', text: '- Add-ons\\n- Teams, roles & membership\\n- Analytics' },\n { path: '/workspace-and-billing/manage-account', title: 'Manage Account', heading: '', anchor: '', text: 'Manage Account\\nManage Account is your personal account page, separate from your workspace and its team roster. Open it from the account menu (your avatar, top right) — click your name in the menu header, or the gear icon beside it.\\nEverything here is yours alone: changing it follows you into every workspace you belong to. Workspace-level things — the team roster, roles, billing — live under Organization → Team and Billing instead.\\nThe page is five tabs.' },\n { path: '/workspace-and-billing/manage-account', title: 'Manage Account', heading: 'Account', anchor: '#account', text: 'Your primary email and how you sign in.\\nThe email field here is read-only, with an Email verified or Email unverified chip beside it. It shows your primary address — the one Aglyn\\'s sign-in record holds. To add other addresses, or to make a different one primary, use the Email addresses tab.\\nSign-in methods\\nBelow the email, every method linked to your account is listed:\\n- Google — shows the connected Google address and a Disconnect button.\\n- Email & password — carries a Required chip. Password sign-in is permanent: it\\'s the method that always works, so it can\\'t be disconnected.\\nIf Google isn\\'t linked yet, a Continue with Google button connects it. Linking both means you can sign in either way with one account — the same profile, the same workspaces, no duplicate account.\\nYou can never remove your last sign-in method; the attempt is refused rather than locking you out. Closing the Google popup mid-flow simply cancels — nothing changes.\\nIf your organization uses single sign-on Accounts that sign in through a company identity provider cannot link Google or any other method — the option is shown as unavailable, with the reason. That is deliberate, not a limitation we intend to lift.\\nSingle sign-on exists so your organization\\'s identity provider is the only gate: they revoke access there, require MFA there, and offboard people there. A linked personal Google account would be a way in that your IT administrators cannot see or revoke, which is the thing SSO is bought to prevent.\\nIf your organization turns on SSO enforcement later, sign-in methods that were linked before are removed at that point, and any active sessions using them end. Your account is never left with no way in: if removing a method would orphan an account, it is skipped and reported to your administrators inst' },\n { path: '/workspace-and-billing/manage-account', title: 'Manage Account', heading: 'Email addresses', anchor: '#email-addresses', text: 'You can keep up to five addresses on one account — a work address and a personal one, say — instead of running two accounts.\\nType an address into Add an email address and Aglyn emails a confirmation link to it. Until you follow that link the address is listed as Unconfirmed and does nothing at all: it cannot be used to sign in, it cannot receive an invitation, and it cannot become your primary. Confirming is the only thing that makes an address count, because following a link in an inbox is the proof that the inbox is actually yours.\\nEach address in the list carries a Confirmed or Unconfirmed chip, and one carries Primary. The buttons beside a row change with its state:\\n- Resend — on an unconfirmed address, sends a fresh confirmation link. Links expire after 24 hours; an expired one tells you to send a new one rather than quietly failing.\\n- Make primary — on a confirmed address that isn\\'t already primary.\\n- Remove — on any address that isn\\'t your primary.\\nWhat each address does\\nOnce confirmed, an address is two things and nothing more:\\n- A way to sign in. Type any confirmed address on the sign-in page with your usual password and you\\'re in. You don\\'t have to remember which one the account was opened with.\\n- A place an invitation can arrive. If a workspace administrator invites the address, you can accept it from the account you already have.\\nYour primary address is the one that receives receipts, password resets, and account notices. It\\'s also the address shown on the Account tab.\\nAdding an address never joins you to a workspace This is the one thing people expect that is deliberately not true. Adding you@bigcompany.com to your account does not put you in BigCompany\\'s workspace, and confirming it doesn\\'t either.\\nAccess to a workspace comes from an invitation somebody th' },\n { path: '/workspace-and-billing/manage-account', title: 'Manage Account', heading: 'Profile image', anchor: '#profile-image', text: 'Your avatar, shown in the account menu, on the workspace team roster, and beside your activity. Pick an image from the organization\\'s media library or paste an https image URL.\\nNever set one and Aglyn falls back to the photo your sign-in provider gave us, and then to your initials, drawn by Aglyn. Nothing about you is sent to an outside avatar service to work out what to show. The preview beside the field shows exactly what everyone else sees.\\nClearing the field is different from never having set one. Emptying it and saving removes your picture and keeps it removed — signing in again does not put your provider\\'s photo back. Save an image here again whenever you want that prefill returned.\\nIf your company signs you in through its own identity provider and this is blank, that provider is most likely not sending a picture — many cannot, including Google Workspace SAML, which has no photo attribute to map. Set one here: the media library accepts an upload, or paste an https link.' },\n { path: '/workspace-and-billing/manage-account', title: 'Manage Account', heading: 'Basic info', anchor: '#basic-info', text: 'Your first and last name. Saving also updates the display name your teammates see — the Team page, activity entries, and comments all pick it up.\\nBoth are prefilled from however you signed up: the name you typed on the sign-up form, or the one your Google account or company identity provider sent us. Editing them here wins permanently — signing in again never re-applies the provider\\'s version over your own. If your company signs you in through its own identity provider and these are blank, that provider is not sending a name; type one here.\\nContact details\\nPhone number, organization name, and your address.\\nYour phone number is stored in international format — type it however you like, including (512) 555-0123, and it is saved as +15125550123. Include the country code for anywhere outside the US and Canada, since a bare national number cannot be matched to a country without guessing.\\nCountry is the two-letter code (US, GB, DE). Stripe Tax cannot compute anything from a spelled-out country name, which is why the field insists.\\nIf your company signs you in through its own identity provider, your address is prefilled from that directory only when the directory sends a street line. A provider that releases a city, region or postcode on its own leaves these fields empty for you to fill in: those parts are not an address anyone could post to, and Aglyn does not keep personal details it has no use for. As with your name, anything you type here wins permanently — signing in again never re-applies the provider\\'s version, and clearing the address tells us to stop prefilling it altogether.\\nWho can see this Your address and phone live on your personal account, visible only to you and to Aglyn staff. They are not copied onto your organization\\'s member list — that list is readable by ' },\n { path: '/workspace-and-billing/manage-account', title: 'Manage Account', heading: 'Security', anchor: '#security', text: 'Change your password.\\nThis tab appears only if your account has a password. A Google-only account has no password to change — connect Email & password from the Account tab first, and the Security tab appears.' },\n { path: '/workspace-and-billing/manage-account', title: 'Manage Account', heading: 'Related', anchor: '#related', text: '- Signing in & sessions\\n- Teams, roles & membership\\n- Notifications' },\n { path: '/workspace-and-billing/report-an-issue', title: 'Report an issue', heading: '', anchor: '', text: 'Report an issue\\nFound something broken? Open the account menu in the top right and choose Report an issue. The dialog is available from every console page, and on every plan — including Free.\\nIt also has a direct link — app.aglyn.com/manage/report-issue — which is the one to share with someone, or to bookmark. Opening it from the account menu is still the better route while you are looking at the problem: the report carries the page you were on, and that page is the most useful thing in it.' },\n { path: '/workspace-and-billing/report-an-issue', title: 'Report an issue', heading: 'Is it Aglyn, or your own site?', anchor: '#is-it-us-or-your-site', text: 'This channel is for problems with Aglyn itself — the editor, the console, publishing, domains, billing. It is not the place to change something on a site built with Aglyn.\\nThe line is not simply \"platform versus content\", so here it is in cases:\\n| What you noticed | Where it belongs |\\n| The heading on your About page is the wrong color, or says the wrong thing | Your site. Change it in the editor, or ask whoever builds the site for you. |\\n| Headings render at the wrong size on every site you open | Aglyn. Report it. |\\n| A page returns 404 after you renamed it | Aglyn. Report it. |\\n| The contact form on your site emails the wrong address | Your site — that address is a setting on the form. |\\n| The contact form never delivers, whatever address you set | Aglyn. Report it. |\\nThe pattern: if changing it means editing your content or settings, it is yours. If it happens the same way regardless of what any one site contains, it is ours.\\nIf you did not build the site and cannot change it, the people who can are whoever set the workspace up — an agency, a colleague, or whoever sent you your invitation. Aglyn cannot edit a customer\\'s site on their behalf. Some workspaces put their own support link in the dialog; when one is configured, it appears as Get help with this site.\\nStill unsure? Send it anyway. A report filed in the wrong place costs us a moment; a problem nobody hears about costs everyone more.' },\n { path: '/workspace-and-billing/report-an-issue', title: 'Report an issue', heading: 'What to write', anchor: '#what-to-write', text: 'The dialog asks different things depending on what kind of report it is, because a bug and an idea need different answers. Pick the kind first.\\nSummary — always required, one line, the way you would describe it to a colleague. It becomes the filed issue\\'s title.\\nIf something is broken {#a-bug}\\nFour questions, all required. They are what turn a report into a fix:\\n- What were you doing when it broke? — step by step, so we can follow along. This is the single most valuable thing you can write.\\n- What did you expect to happen?\\n- What happened instead? — quote any error message exactly.\\n- Does it happen every time? — one click: every time, sometimes, once, or you have not tried again. It changes what we do first, so it is not optional.\\nIf it is an idea {#an-idea}\\nOne required question, and it is deliberately not \"what should we build\":\\n- What are you trying to do that you can\\'t? — the problem, not the feature. There may already be a way to do it, and where there is not, knowing the goal gets you a better answer than a specification would.\\n- How do you handle it today? and If you already have something in mind are both optional.\\nIf it is a question {#a-question}\\n- What do you need to know? — required.\\n- What have you already tried or read? — optional, and it saves us sending you back to a page you have read.\\nQuestions take one extra step: before anything is filed, we check whether the documentation already answers you. If it does, the dialog shows the relevant pages, word for word, under This may already be answered — and nothing is filed. If that covers it you are done; if it does not, This didn\\'t answer it — send anyway files the question as normal.\\nSend report stays disabled until Summary and every required question for that kind have something in them.\\nThe length caps {#l' },\n { path: '/workspace-and-billing/report-an-issue', title: 'Report an issue', heading: 'What gets attached for you', anchor: '#what-gets-attached-for-you', text: 'You do not need to describe your setup. The report already carries:\\n| Attached automatically | Why it helps |\\n| The page you were on | Points straight at the surface, without a screenshot |\\n| The site you were working on | Names the affected site, when you were on one |\\n| Your workspace and plan | Says which features and limits were in play |\\n| Your role | A bug that only bites one role is a different bug |\\n| Which features were switched on | Recognizes a report against a surface still rolling out |\\n| The app version and build | Says exactly which release you were running |\\n| Your browser and window size | Separates a layout problem from a logic one |\\n| A reference id | Ties your report to our server logs for that moment |\\nYour name and email come from the account you are signed in with — there is nothing to fill in.\\nNothing else is collected. No passwords, keys or session data are ever attached, your IP address is not recorded, and the workspace and site named on a report are only ever ones your own account can already reach.' },\n { path: '/workspace-and-billing/report-an-issue', title: 'Report an issue', heading: 'Being contacted', anchor: '#being-contacted', text: 'The dialog ends with \"You can contact me about this report\", ticked by default. Untick it and we will still read and track the report; we just will not reply to you about it. Leave it ticked if the answer might need a back-and-forth — most bugs do.' },\n { path: '/workspace-and-billing/report-an-issue', title: 'Report an issue', heading: 'Where it goes', anchor: '#where-it-goes', text: 'Reports land in a dedicated intake in Aglyn\\'s issue tracker, kept separate from our own engineering backlog so that what customers report is read on its own terms rather than buried in internal work. You will see a reference like AGL-42 when it is filed; quote it if you follow up.\\nA report is closed when you have an answer — not when the code merges. Those are different moments, and you are owed the first one.\\nThis is not a support ticket A report is a defect we track. A support ticket is a conversation with the support team, carrying a first-response commitment on your plan.\\nUse Report an issue for \"this is broken\" or \"this should exist\" — from any plan. Use Support when you need an answer from a person about your own account, and your plan includes tickets.' },\n { path: '/workspace-and-billing/report-an-issue', title: 'Report an issue', heading: 'When it does not send', anchor: '#when-it-does-not-send', text: 'Whatever the reason, the dialog stays open with your text intact. A report that fails never costs you what you wrote — fix the cause and press Send report again, or close the dialog yourself.\\nYou are filing faster than the limit {#rate-limits}\\nTwo limits apply, per person, and a report has to pass both:\\n| Window | Reports |\\n| Per minute | 2 |\\n| Per hour | 20 |\\nExceeding either shows \"You have filed several reports just now — try again shortly.\" — as a warning rather than an error, because nothing is wrong with the report, only with its timing. Wait out the window and send it again.\\nThis is the message most often mistaken for a broken dialog. The two windows exist together deliberately: a per-minute limit on its own can be spread into a steady drip that still buries triage, so a burst and a drip are both capped.\\nYour email address is not verified {#verified-email}\\nFiling needs a verified email address. An account that has not confirmed its address is refused with \"Verify your email to continue\" and nothing is filed — the dialog gives no hint that this is about your account rather than about the report. Manage account shows an Email verified or Email unverified chip beside your address, which is where to check. Once the address is verified, the dialog works straight away.\\nSomething went wrong on our side\\nA report we could not file tells you so plainly, hands you a reference id you can quote to support, and leaves everything you typed in the open dialog. It is never reported back to you as sent. Retrying is safe.\\nSelf-hosting Issue reporting is one of the optional integrations in a self-hosted install, switched on with LINEARAPIKEY and LINEARCUSTOMERREPORTSTEAMID — both listed in that page\\'s Optional keys table, plus an optional LINEARCUSTOMERREPORTSPROJECTID if you separa' },\n { path: '/workspace-and-billing/report-an-issue', title: 'Report an issue', heading: 'Related', anchor: '#related', text: '- Support & community\\n- Manage account\\n- Console tour\\n- Self-hosting' },\n { path: '/workspace-and-billing/signing-in-and-sessions', title: 'Signing In & Sessions', heading: '', anchor: '', text: 'Signing In & Sessions\\nSign in to the console with email & password or Google. A few behaviors are worth knowing about, especially if you work across devices or multiple organization workspaces.' },\n { path: '/workspace-and-billing/signing-in-and-sessions', title: 'Signing In & Sessions', heading: 'Google sign-in', anchor: '#google-sign-in', text: '- On desktop, Google sign-in opens a popup window and returns you to the console when it completes.\\n- On mobile browsers, the console uses a full-page redirect instead: you\\'re taken to Google, sign in there, and land back on the console already authenticated. (Mobile browsers can\\'t reliably hand a popup\\'s result back to the opening page.)' },\n { path: '/workspace-and-billing/signing-in-and-sessions', title: 'Signing In & Sessions', heading: 'Sign-in methods', anchor: '#sign-in-methods', text: 'You can link both methods to one account, and then sign in either way — same profile, same workspaces, no duplicate account. Manage them under Manage Account → Account:\\n- Continue with Google links Google to an account that signed up with a password.\\n- Disconnect unlinks Google again.\\n- Email & password is marked Required and can\\'t be disconnected — it\\'s the method that always works.\\nYour last remaining sign-in method can never be removed, so there\\'s no way to lock yourself out.\\nSigning in with any of your addresses\\nIf you keep more than one email address on your account, you can type any confirmed one on the sign-in page with your usual password — you don\\'t have to remember which address the account was opened with. Unconfirmed addresses don\\'t work here, and neither does an address on somebody else\\'s account.\\nSingle sign-on accounts are the exception. If your company signs you in through its own identity provider, no other method can be linked — see Manage Account for why, and for what happens to methods linked before your organization enabled enforcement.' },\n { path: '/workspace-and-billing/signing-in-and-sessions', title: 'Signing In & Sessions', heading: 'Resetting your password', anchor: '#resetting-your-password', text: 'Forgot your password? From the sign-in screen, choose Account recovery and enter your email. We email you a secure reset link, then walk you through the rest:\\n1. Request — enter your email on the account recovery screen and submit. We always show the same \"check your email\" confirmation, whether or not an account exists for that address (so the screen can\\'t be used to probe who has an account).\\n2. Email — open the message and follow the reset link. Links expire after a short while and can only be used once.\\n3. Choose a new password — the link opens the reset screen, which confirms whose account it\\'s for and asks for a new password (entered twice). Password rules match the sign-up screen.\\n4. Done — once saved, head back to the sign-in screen and sign in with your new password.\\nIf a link has expired or was already used, the reset screen offers to send a fresh one.\\nIf you signed up with Google and have never set a password, there\\'s nothing to reset — connect Email & password from Manage Account first.\\nSelf-hosting Aglyn composes and sends the reset and verification emails itself, through your configured mail provider — you do not need to set a Firebase action URL, and the Firebase email templates are not used at all. Firebase still mints the one-time code; only the message and the link\\'s host are Aglyn\\'s, and the link is built from your own console origin.\\nWhat you do still need: the console domain listed under Authentication → Settings → Authorized domains, and a working mail provider. If mail is unconfigured, account recovery still returns its usual \"check your email\" screen — deliberately, so the screen can\\'t be used to probe who has an account — but no message goes out. Check your server logs for [auth/send-password-reset] email is not configured.' },\n { path: '/workspace-and-billing/signing-in-and-sessions', title: 'Signing In & Sessions', heading: 'One session across workspaces', anchor: '#one-session-across-workspaces', text: 'Signing in on the main console signs you in to every {org} workspace subdomain too — the session is shared at the parent domain, so hopping between workspaces never re-prompts for credentials. Signing out anywhere retires the shared session everywhere.' },\n { path: '/workspace-and-billing/signing-in-and-sessions', title: 'Signing In & Sessions', heading: 'Automatic sign-out after inactivity', anchor: '#automatic-sign-out-after-inactivity', text: 'For security, an idle console session expires after 1 hour of no activity (in any open tab, on any of your workspace subdomains). When that happens you\\'re returned to the sign-in screen, and the page you were on is preserved — after signing back in you resume exactly where you left off.\\nActivity means any interaction: pointer movement, typing, scrolling, or touching. Active work in one tab keeps your other tabs alive too.\\nInactivity is the only thing that ends a session early: a background tab losing its own authentication (a laptop waking from sleep, a suspended tab, a network blip) recovers silently from the shared session instead of signing you out everywhere. Only an explicit sign-out — yours, or a staff-initiated revocation — retires the shared session.\\nSelf-hosting The idle window is configurable via the NEXTPUBLICAUTHIDLETIMEOUTMINUTES environment variable (default 60; set 0 to disable).' },\n { path: '/workspace-and-billing/signing-in-and-sessions', title: 'Signing In & Sessions', heading: 'When the console asks you to sign in again', anchor: '#when-the-console-asks-you-to-sign-in-again', text: 'Sometimes a session stops working without ending. A tab left open overnight, a laptop that slept for a long weekend, or a sign-out somewhere else can leave the console holding credentials the server no longer accepts. The pages you already loaded keep rendering from their cache, so nothing looks broken — but anything the console tries to fetch comes back refused, and lists load empty.\\nWhen that happens the console opens Sign in again to verify your device over whatever you were doing. You do not have to find a message and work out what to do about it: the dialog is the fix.\\n- You stay where you are. No redirect, no lost page, and unsaved changes stay on screen behind the dialog.\\n- Sign in with your usual method — your password, your Google or single sign-on account, or a passkey. The dialog closes and the data that would not load, loads.\\n- Not now puts it away. The page stays usable and read-only-ish: what is already on screen is fine, but lists that failed will still be incomplete, and each of them offers Sign in again to bring the dialog back.\\nIt is a real sign-in, not a confirmation. There is no way to dismiss your way back into a working session, because the session itself is what stopped working.\\nOccasionally the console instead says it cannot reach your data and that signing in again will not help. That is a different problem — it is not your account and not your session, nothing has been deleted, and it usually clears on its own. Signing out at that point only makes it harder to diagnose, so don\\'t.' },\n { path: '/workspace-and-billing/signing-in-and-sessions', title: 'Signing In & Sessions', heading: 'Recent sign-ins', anchor: '#recent-sign-ins', text: 'When your account is signed in to from a device it has not been used on before, we email you: the device, the approximate location, the IP address, and the time. Manage Account → Security → Recent sign-ins is the list that email points at — every device that has signed in to your account, newest first, up to the last 50.\\nEach row names the device as a summary of its browser and operating system (\"Chrome on Windows\"), and beneath it the approximate location, the IP address, when it was last used, and when it was first seen. Location and IP come from the network the request arrived on, so they are approximate — a mobile connection or a VPN can put you in a city you have never been to.\\nA device here is a browser that holds a long-lived cookie we set when you sign in, not a fingerprint of your hardware. Clearing cookies, using a private window, or switching browsers therefore shows up as a new device, and the first device ever recorded on an account never triggers the email.\\nWhen we do not email you\\nWe do not send the alert for a device that signs in from the same IP address and the same operating system as a device already on your list, seen within the last 30 days. That is the shape of clearing your cookies, opening a private window, making a new browser profile, or using a second browser on the same machine — all the same person on the same computer, and an alert people learn to ignore is worth less than no alert.\\nThree things bound it, because \"same network\" is not \"same person\" on an office or café connection:\\n- the device is still recorded and still listed, marked with the reason no email was sent, so a sign-in is never invisible even when it is silent;\\n- a device on a different operating system behind the same address always gets an email;\\n- at most three alerts a da' },\n { path: '/workspace-and-billing/signing-in-and-sessions', title: 'Signing In & Sessions', heading: 'Passkeys', anchor: '#passkeys', text: 'Manage Account → Security → Passkeys lists every passkey on your account, when it was added and when it was last used, with Set up a passkey to add one and Remove on each row.\\nPasskeys are additive. Adding one never weakens or replaces anything — your password and your sign-in providers keep working exactly as before — so losing an authenticator costs you a convenience, not your account.\\nRemoving one\\nUse Remove when a device is lost or stolen, or when you no longer want a particular key on the account. Removing a passkey:\\n- stops that passkey signing in, immediately and everywhere;\\n- does not sign you out anywhere. Sessions the passkey already started are ended by signing out of a device, which is a separate control;\\n- does not touch your password or your other sign-in methods.\\nYou can set the same device up again afterwards — removing a passkey frees it completely, rather than blocklisting it.\\n\"Blocked — possible credential copy\"\\nEvery authenticator keeps a counter that only goes up. If a passkey presents a counter that has gone backwards, that is the signature of a copied credential, so Aglyn refuses the sign-in and marks the passkey Blocked.\\nA Blocked passkey stays refused. It is not a warning you can sign past, and it does not clear itself — the only signal there will ever be is the one regression, so throwing it away after a single refusal would throw away the detection. Sign in another way, then Remove the passkey and set the device up again if you still have it.\\nRarely, an authenticator with a buggy counter can trigger this on a device nobody has copied. The remedy is the same, and it is the reason Remove exists next to it.' },\n { path: '/workspace-and-billing/signing-in-and-sessions', title: 'Signing In & Sessions', heading: 'Downloading your data', anchor: '#downloading-your-data', text: 'Manage Account → Close account → Download my data gives you a machine-readable JSON copy of everything we hold about you: your profile and contact details, your workspace memberships, your passkeys, your public publisher handle, and the support messages you wrote. It is the same file we would send if you asked us in writing.\\nIt is deliberately scoped to you. A workspace is shared, so your colleagues\\' details are not in it — a support thread you took part in contributes only the messages you wrote yourself, not the replies. To take a whole workspace with you, use the workspace export below instead.\\nIt never contains a secret. API keys, webhook secrets, password hashes and payment links are listed as present rather than reproduced, so the file tells you what exists without becoming something you can lose. An API key\\'s identifier is withheld too, because that identifier is derived from the key itself.\\nThat is decided by what a value looks like, not only by what the field is called — a credential is withheld whether it sits in a field named webhooksecret, one named supplierToken, or one named nothing in particular. A credential carried inside a URL is stripped out of the address while the address itself stays, so you can still see where your own files live. Ordinary content, prose, identifiers and content hashes are not touched: a list of names is a list somebody has to keep up to date, and the whole point of this file is that it does not quietly stop covering something.\\nThe file opens with a coverage section naming every place we looked and what was done with each — including the two things that are deliberately not included, and why. If something you expected is missing, that section is where to check first.\\nDownloading a whole workspace\\nThe workspace owner can export an ' },\n { path: '/workspace-and-billing/signing-in-and-sessions', title: 'Signing In & Sessions', heading: 'Privacy requests', anchor: '#privacy-requests', text: 'Settings → Privacy is where a workspace answers the people its sites hold: someone who asks for the data you keep about them, or asks to be forgotten. The section is shown to workspace owners and admins, and it is on every plan, Free included. These are obligations rather than features, so they sit outside the CRM and do not wait for a plan that includes it.\\nExporting contacts and leads\\nExport contacts downloads a CSV file of every contact the workspace holds, and Export leads one of every lead its sites captured. Each file is complete — the same file the CRM\\'s Export all… writes. If fewer rows arrive than the server promised, because a connection dropped, nothing is saved and you are told how many of how many arrived; try again.\\nErasing a person\\nErase a person is the privacy erasure, for someone who asks to be forgotten under the GDPR, the CCPA or any similar law. Type the person\\'s email address, type it again to confirm, and choose Erase permanently. It cannot be undone.\\n- It is filed by address, across the workspace. It reaches every site of the workspace, whether or not any page shows the person.\\n- Capture stops at once. From the moment the request is filed, your sites stop capturing the address, and no campaign is sent to it.\\n- The nightly erasure job completes it. Before you choose, the card lists what the job removes, what it keeps with the person taken off, and what it does not reach:\\n| | |\\n| Removed across the workspace | The contact record on every site — profile, notes, tags, timeline and custom fields; every lead for the address, on every site; the tasks and logged activities filed against the person; the person on any email audience list; the campaign delivery log for the address, with its opens and clicks. |\\n| Kept, with the person taken off | Orders and b' },\n { path: '/workspace-and-billing/signing-in-and-sessions', title: 'Signing In & Sessions', heading: 'Product updates', anchor: '#product-updates', text: 'Aglyn emails you about product updates only if you said so. There are three places to say it, and one record behind all of them:\\n- an optional checkbox on the sign-up form — unticked until you tick it, and never required;\\n- the Product updates switch under Manage Account → Email addresses, which turns the emails on or off at any time;\\n- a one-time card on your workspace list, for accounts that were never asked. Yes and No thanks are both remembered and never asked again; closing the card without answering asks once more in ninety days.\\nEach answer is stored with the exact time and the version of the sentence you agreed to. Leaving the sign-up box unticked records nothing — it is not a refusal, so the card may still ask later. Turning the switch off records a refusal, which is honored everywhere Aglyn sends marketing email. Receipts, invites, security alerts and other account mail are unaffected by this choice.' },\n { path: '/workspace-and-billing/signing-in-and-sessions', title: 'Signing In & Sessions', heading: 'Closing your account', anchor: '#closing-your-account', text: 'Manage Account → Close account permanently deletes your personal account. It removes your profile and contact details, your avatar, your notifications, and your sign-in, and takes you off every workspace roster you\\'re on.\\nThis cannot be undone, and support cannot restore it.\\nWorkspaces are never deleted by closing an account. A workspace outlives the person who created it, and deleting one as a side effect of someone closing their personal account would take its sites and data with it. So if you are the sole owner of a workspace, closing your account is refused until you hand that workspace over — the message names each one, along with whether it has an active subscription and how many other members it has. Transfer ownership from Team → Permissions, then come back here.\\nBeing a member, editor, admin or site collaborator is never a blocker; only sole ownership is.\\nYou\\'ll be asked to confirm twice: once by proving it\\'s you (your password, or a fresh sign-in through your provider), and once by typing DELETE. Both are required, and the confirmation of identity has to be recent — starting the flow and leaving it open for an hour will ask you again.\\nSingle sign-on If your organization uses SSO, your account also exists in your identity provider\\'s directory. Closing it here removes you from Aglyn; it does not touch your account with your identity provider, and an administrator there may be able to provision you again.' },\n { path: '/workspace-and-billing/support-and-community', title: 'Support & community', heading: '', anchor: '', text: 'Support & community\\nTwo ways to get help, each with its own page under Support: private support tickets with the Aglyn team, and a community forum shared with other Aglyn builders.\\nOpen Support from Organization → Support, or from Support in your account menu. It lands on whichever channel your plan makes primary — the forum on Free and Starter, tickets from Pro upward — and each page links to the other, so you always have both.\\nIf you were invited to a single site Support isn\\'t part of a site collaborator\\'s console. A ticket is a conversation between the organization and Aglyn — it isn\\'t scoped to one site, and the thread is readable by the organization\\'s team — so the console doesn\\'t offer it to someone whose access stops at a single site.\\nRaise anything you run into with whoever invited you. They can open a ticket, and because tickets belong to the organization rather than to the person who filed them, any of their teammates can carry the thread on.\\nPlan availability The community forum is on every plan, including Free. Private support tickets start on Pro — Free and Starter are forum-only tiers.\\nEach plan with tickets carries a first-response target, shown on the Support tickets page itself:\\n| Plan | Tier | First response |\\n| Free · Starter | Community | Forum only — no ticket channel |\\n| Pro | Standard | 7–14 business days |\\n| Business · Scale · Advanced | Business | 4–6 business days |\\n| Agency | Priority | 1–3 business days |\\n| Enterprise | Dedicated | 24–48 hours, plus a named success manager |\\nEnterprise is measured in clock hours rather than business days, so a ticket opened on Friday is still answered over the weekend. The target is the upper bound of the range, measured to our first reply — not to resolution. See Billing & plans.' },\n { path: '/workspace-and-billing/support-and-community', title: 'Support & community', heading: 'Support tickets', anchor: '#support-tickets', text: 'At Organization → Support → Support tickets.\\nA ticket is a private conversation between your organization and Aglyn staff. Anyone in the organization can open one and follow the thread.\\nOn Free and Starter this page explains where ticket support starts and points you at the forum instead — it never offers a New ticket button the server would refuse.\\n1. Choose New ticket.\\n2. Give it a Subject (up to 150 characters) and describe what\\'s going on in the message body (up to 5,000 characters).\\n3. Submit. The ticket appears in your list marked open, and Aglyn staff are notified immediately.\\nOpen a ticket from the list to read the thread and reply. Each message shows who sent it, so a staff reply is always distinguishable from your own.\\nTicket status\\n- open — with you or with Aglyn; new replies from either side keep it open.\\n- closed — resolved. Either side can close a ticket, and replying to a closed ticket reopens it automatically — you never need to file a duplicate to follow up on something you thought was finished.\\nTickets belong to the organization, not to the person who opened them, so a teammate can pick up a thread while you\\'re away. They stay in the list after closing as a record of what was asked and answered.\\nWhat to include\\nSupport answers faster with the specifics: the site and screen involved, what you expected versus what happened, and the exact error text if there is one. If it\\'s about billing, say which plan and which invoice.' },\n { path: '/workspace-and-billing/support-and-community', title: 'Support & community', heading: 'Community forum', anchor: '#community-forum', text: 'At Organization → Support → Community forum, and the page Free and Starter workspaces land on.\\nThe forum is for questions other builders can answer, and for sharing what you\\'ve made. Threads are visible to everyone with forum access across all organizations — so don\\'t post anything private (account details, customer data, keys). That\\'s what a support ticket is for.\\nThreads are organized into four categories:\\n| Category | For |\\n| General | Anything that doesn\\'t fit the others. |\\n| Building | How-to questions about designing and shipping sites. |\\n| Showcase | Show the site you built. |\\n| Feedback | Product feedback and feature requests. |\\nFilter the list by category, open a thread to read and reply, or start your own with a title and body (5,000 characters). Your display name and avatar are shown as the author.' },\n { path: '/workspace-and-billing/support-and-community', title: 'Support & community', heading: 'Related', anchor: '#related', text: '- Billing & plans\\n- Manage Account' },\n { path: '/workspace-and-billing/teams-and-roles/custom-roles', title: 'Custom roles & permissions', heading: '', anchor: '', text: 'Custom roles & permissions\\nControl what each teammate can do with roles — named permission sets — plus optional per-member overrides.\\nPlan availability Paid.' },\n { path: '/workspace-and-billing/teams-and-roles/custom-roles', title: 'Custom roles & permissions', heading: 'Create a custom role', anchor: '#create-a-custom-role', text: '1. On the organization Team page, find the Custom roles card and choose New role.\\n2. Give it a name and set each permission to granted, denied, or inherit (inherit falls back to the member\\'s org role default).\\n3. Save, then assign it to members from the roster\\'s Custom role column.\\nThe permission catalog covers organization settings, the activity log, billing (view and manage separately), member management, site creation and deletion, organization data, marketplace publishing, plugin installs, and the two AI keys.\\nThe AI permissions {#ai-permissions}\\nTwo keys decide who may spend the workspace\\'s AI credits:\\n| Permission | What it opens | Owner / Admin | Editor | Viewer |\\n| Use AI assistance (ai.use) | The assistant, copy rewrites, and \"generate a section\" in the Besigner | Yes | Yes | No |\\n| Generate with AI (ai.generate) | AI generation jobs and AI edits — pages, components, emails, campaigns, products, CRM, insights, workflows | Yes | Yes | No |\\nBoth are ordinary catalog keys: a custom role can grant or deny either, and a per-member override wins over the role. They are enforced at the AI endpoints themselves — a member whose role lacks a key is refused with a message naming it and asking them to see an organization admin, whichever button they reached it by. A viewer may not ask the assistant even on the Free plan, so an admin can switch AI off for a read-only teammate without changing their role.\\nSite collaborators are not decided by these keys. Their AI access is set per site, from the site role — see AI access for collaborators.\\nPermissions are enforced on the server — every one of them is checked by the API that performs the action, not merely used to hide a button — so a role reliably limits what a member can do, whichever route they reach it by. Members without' },\n { path: '/workspace-and-billing/teams-and-roles/custom-roles', title: 'Custom roles & permissions', heading: 'Effective permissions', anchor: '#effective-permissions', text: 'Not sure what someone can actually do? Every member row has a Permissions viewer that resolves their org role defaults, custom role, and per-member overrides into the final yes/no grant list.' },\n { path: '/workspace-and-billing/teams-and-roles/custom-roles', title: 'Custom roles & permissions', heading: 'Per-member overrides', anchor: '#per-member-overrides', text: 'Need one person to have slightly more (or less) than their role? Apply a per-member override on top of the role instead of creating a whole new role for one exception.' },\n { path: '/workspace-and-billing/teams-and-roles/custom-roles', title: 'Custom roles & permissions', heading: 'Tips', anchor: '#tips', text: '- Model roles around jobs (\"Editor\", \"Marketer\"), not individuals.\\n- Use overrides sparingly — too many overrides make access hard to reason about.' },\n { path: '/workspace-and-billing/teams-and-roles/custom-roles', title: 'Custom roles & permissions', heading: 'Related', anchor: '#related', text: '- Invite teammates\\n- Members-only areas' },\n { path: '/workspace-and-billing/teams-and-roles/invite-teammates', title: 'Invite teammates', heading: '', anchor: '', text: 'Invite teammates\\nBring collaborators into a site so they can build and manage alongside you.\\nPlan availability Paid. Team seats are metered per tier; buy seat add-ons to grow. See Billing & plans.' },\n { path: '/workspace-and-billing/teams-and-roles/invite-teammates', title: 'Invite teammates', heading: 'Invite someone', anchor: '#invite-someone', text: '1. Open the organization\\'s Team page and choose Add or invite.\\n2. Enter their email and assign a role.\\n3. What happens next depends on whether they already have an Aglyn account: already on Aglyn — they join right away and see the workspace on their next visit; new to Aglyn — we email them an invite to accept.\\nPending invites\\nInvites that haven\\'t been accepted are listed with Resend (send the email again, for the address that lost it in a spam folder) and Revoke. A revoked invite\\'s link stops working immediately.\\nWho gets told\\n- The person you invited gets the invite email. The confirmation you see says whether one actually went out — \"email sent\" versus \"they\\'ll see it when they sign in\" — so a workspace with email delivery unavailable never leaves you assuming a message was delivered.\\n- Your workspace\\'s owner and admins get an in-app notification when an invite is created and again when it is accepted, so whoever sent it finds out it was taken up without re-checking the Team page. Both appear under Team & access in the notification bell, and can be muted there like any other category.' },\n { path: '/workspace-and-billing/teams-and-roles/invite-teammates', title: 'Invite teammates', heading: 'Accepting an invite', anchor: '#accepting-an-invite', text: 'If you\\'re the one being invited, sign up or sign in with the address the invite was sent to — usually via Google, if that\\'s the address. You land on the Workspaces page, which leads with \"You\\'ve been invited\" and an accept banner. Accept there and the workspace opens.\\nIf that invite is your only workspace, the page also offers Create my own site instead — accepting isn\\'t your only option. If you already belong to other workspaces, the invite banner appears above your usual workspace picker.\\nAn invitation never changes who owns the workspace\\nAccepting an invite normally sets your role to whatever the invite offered. The one role it cannot touch is the owner\\'s. If an invitation is sent to the address that owns the workspace, accepting it is refused rather than applied — otherwise the owner would be quietly stepped down to a viewer or an admin, and only the owner can transfer ownership back, so nobody left in the workspace could undo it.\\nOwnership moves one way only: Settings → Transfer ownership, by the current owner, to somebody already on the team. The previous owner becomes an admin. Nothing else in the product creates or moves an owner — an invitation cannot grant the role either.' },\n { path: '/workspace-and-billing/teams-and-roles/invite-teammates', title: 'Invite teammates', heading: 'How team members act', anchor: '#how-team-members-act', text: 'Team members act in the owner\\'s organization, not their own — so their changes apply to your site, and permissions are enforced across the console\\'s APIs and surfaces. Seat limits are enforced per tier; if you\\'re at your limit, add a seat add-on before inviting more.' },\n { path: '/workspace-and-billing/teams-and-roles/invite-teammates', title: 'Invite teammates', heading: 'You are a site collaborator\\'s support channel', anchor: '#you-are-a-site-collaborators-support-channel', text: 'Someone you invite to a single site gets a console scoped to that site, which leaves out the organization pages — including Support. That is deliberate: a support ticket is a conversation between the whole organization and Aglyn, readable by your team, not a per-site channel.\\nThe practical consequence is that a site collaborator who hits a problem comes to you. Open a ticket on their behalf if it needs Aglyn — include the site and the exact error, as Support & community describes, since you will be relaying rather than reproducing. Someone invited to the whole organization keeps Support unchanged.' },\n { path: '/workspace-and-billing/teams-and-roles/invite-teammates', title: 'Invite teammates', heading: 'Help a teammate who is locked out', anchor: '#help-a-teammate-who-is-locked-out', text: 'Open the member from the Team page and use the Password card. It has two options, and the first is almost always the right one.\\nSend password reset email emails the member a link to choose their own password. Their current password keeps working until they use the link, and nobody else ever sees the new one. This works for every member. A few sends per hour to the same person is the cap — it\\'s their inbox, and a reset link is good for an hour anyway.\\nSet a password directly replaces the member\\'s password with one you choose. Use it only when the member cannot receive email at all — you then have to pass the new password to them over a channel you trust. When you do:\\n- they are signed out on every device, and\\n- they are emailed to say an administrator changed their password.\\nWhy you can\\'t always set a password\\nAn Aglyn login is personal, not per-organization. One person has one account no matter how many organizations they work with, so setting someone\\'s password can hand you the keys to workspaces that have nothing to do with yours.\\nThe Set a password directly option is therefore unavailable — with the reason shown in its place — when the member:\\n- also belongs to another organization,\\n- is the organization owner (use Settings → Transfer ownership instead),\\n- is an Aglyn staff account, or\\n- is you (change your own password in your account settings).\\nIn every one of those cases, Send password reset email still works. The link goes to the member\\'s own inbox, so it is safe regardless of what else their account can reach.\\nBoth actions are recorded in the organization\\'s activity log, along with who performed them. The password itself is never written to the log.' },\n { path: '/workspace-and-billing/teams-and-roles/invite-teammates', title: 'Invite teammates', heading: 'Activity log', anchor: '#activity-log', text: 'The organization\\'s Team page shows a Recent Activity feed — renames, workspace URL changes, ownership transfers, members added/removed or re-roled, and invites sent, revoked, or accepted — each with who did it and when.\\nVisible to members whose role carries the Activity & audit log permission. Owners and admins have it by default; viewers do not, and neither does a custom role until you grant it. Revoking it does not merely hide the card — the feed is served by an API that checks the permission, so a member without it cannot read the log by any route.\\nEntries name the thing that changed and link straight to it, so \"Saved the screen — Home\" takes you to that screen. Entries recorded before this shipped show a plain description instead of a link.\\nAI actions in the log {#ai-actions}\\nEverything the AI does in your workspace is recorded in the same feed, attributed to the member who asked for it — or to nobody, when nothing a person did caused it (a generation that paused because the included band ran out, or the add-on leaving at the end of a billing period). The feed shows an AI chip whenever the page holds one of these rows; press it to see only them.\\n| Entry | What it records |\\n| Started an AI generation | A generation was briefed: what it builds, how long the brief was, and the site. |\\n| AI generated | One thing a generation produced — a screen, a layout, a component — linked like any other change. A site\\'s own activity carries a copy. |\\n| Canceled an AI generation | A generation was stopped before it finished. |\\n| AI generation paused for input | A generation stopped and asked for a person, and why: the included band, the overage ceiling, the monthly message cap, or the job\\'s own budget. |\\n| Applied AI edits | Edits from a proposal landed on a screen version, with a co' },\n { path: '/workspace-and-billing/teams-and-roles/invite-teammates', title: 'Invite teammates', heading: 'AI usage per member', anchor: '#ai-usage', text: 'Each member\\'s page carries an AI usage card: their AI credits this month and last, their share of the workspace\\'s pool, their requests, the kinds of request they mostly made, and the split by site. The Team roster shows the same month\\'s credits as a column you can sort on, and every member for the month is on Billing → Usage.\\nThe card is visible to the member themselves and to members whose role carries View billing or Activity & audit log; a manager without either sees the page without the card. It describes who drew what, on which site — nothing more.\\nAI allotment {#ai-allotment}\\nThe same page carries an AI allotment card: the share of the workspace\\'s AI credits the member may draw each month — across every site for a team member, one per site for a site collaborator — hard or soft, with any list of models. Members with Manage billing set it there or on Billing → Usage; a site\\'s admin can set a collaborator\\'s on that site. The member sees their own allotment, and the assistant shows it beside their usage as they work — see AI allotments, usage and model choice.' },\n { path: '/workspace-and-billing/teams-and-roles/invite-teammates', title: 'Invite teammates', heading: 'Tips', anchor: '#tips', text: '- Start people on a least-privilege role and widen it with per-member overrides only where needed.\\n- Removing a member frees their seat.' },\n { path: '/workspace-and-billing/teams-and-roles/invite-teammates', title: 'Invite teammates', heading: 'Related', anchor: '#related', text: '- Custom roles\\n- Members-only areas\\n- Billing & plans' },\n { path: '/workspace-and-billing/teams-and-roles/members-only', title: 'Members-only areas', heading: '', anchor: '', text: 'Members-only areas\\nBeyond your team, your site visitors can become members — and you can restrict certain screens to signed-in members only.\\nPlan availability Business and above. Members-only screens, gated video, and the content paywall need the content-gating feature; on lower plans (or if a subscription lapses) gated content is not served.' },\n { path: '/workspace-and-billing/teams-and-roles/members-only', title: 'Members-only areas', heading: 'Let visitors sign up', anchor: '#let-visitors-sign-up', text: 'Enable site membership so visitors can sign in / sign up to your site. New members flow into your CRM automatically.' },\n { path: '/workspace-and-billing/teams-and-roles/members-only', title: 'Members-only areas', heading: 'Sign-in, sign-up, and recovery pages', anchor: '#sign-in-sign-up-and-recovery-pages', text: 'Every site serves three membership routes out of the box:\\n- /signin — email + password sign-in\\n- /signup — create an account\\n- /recover — forgotten-password recovery\\nBy default these render simple built-in forms styled with your site theme. To make them fully yours, design them in the besigner like any other screen:\\n1. Create a screen and drop in the matching block from the Members group of the element picker — Member sign-in, Member sign-up, or Password recovery. Add anything else you like around it: your logo, imagery, copy.\\n2. Open Admin → Plugins → User Accounts and assign the screen to its route on the Sign-in & sign-up pages card.\\nAssigned screens render through the normal pipeline — your theme, shared layout, and all — and are kept out of search results. Clearing a slot falls back to the built-in form.\\nSend members back where they came from The sign-in and sign-up blocks honor a continue query parameter (e.g. /signin?continue=/members/welcome), so links from gated pages can return the visitor to the page they wanted. Only same-site paths are followed.\\nForgotten passwords\\nMembers who forget their password can request a reset from the Forgot password? link on the sign-in form (or by visiting /recover directly):\\n1. The member enters their account email. The response is always the same, so the form can\\'t be used to check whether an address has an account.\\n2. If the address belongs to a member, they receive an email with a reset link to your site. The link works once and expires after one hour — and completing a reset invalidates any other outstanding links.\\n3. The member sets a new password and signs in with it. Suspended members don\\'t receive reset emails.' },\n { path: '/workspace-and-billing/teams-and-roles/members-only', title: 'Members-only areas', heading: 'Gate a screen', anchor: '#gate-a-screen', text: '1. Open the screen you want to protect.\\n2. Mark it members-only.\\n3. Publish. Only signed-in members can now view it; everyone else is prompted to sign in.' },\n { path: '/workspace-and-billing/teams-and-roles/members-only', title: 'Members-only areas', heading: 'Manage your members', anchor: '#manage-your-members', text: 'The site\\'s Users page lists everyone who signed up on your published site — searchable and paged, newest first. Click a member to open their detail drawer:\\n- Profile — email, display name, join date, and saved addresses.\\n- Order history — the member\\'s payment records, newest first: order number, status, total, and date, plus the Stripe payment reference and any refunded amount. Order history is visible to site admins and editors.\\n- Subscriptions — active and past storefront subscriptions with their renewal date.\\n- Lifetime purchases — everything the member actually paid: charged order totals minus refunds (pending and canceled orders don\\'t count).\\nSuspend or reactivate a member\\nFrom the drawer you can suspend a member (with a confirmation). A suspended member:\\n- can no longer sign in on your published site — they see a clear \"account suspended\" message;\\n- is signed out the next time their account page loads;\\n- keeps all orders, subscriptions, and history — nothing is deleted.\\nReactivate restores access with the member\\'s existing password. The member list shows each account\\'s Active/Suspended status at a glance.' },\n { path: '/workspace-and-billing/teams-and-roles/members-only', title: 'Members-only areas', heading: 'Tips', anchor: '#tips', text: '- Combine members-only screens with email campaigns to nurture your member base.\\n- For one-off protection without accounts, use a per-screen password instead.' },\n { path: '/workspace-and-billing/teams-and-roles/members-only', title: 'Members-only areas', heading: 'Related', anchor: '#related', text: '- Invite teammates\\n- CRM\\n- Site protection & error pages' },\n { path: '/workspace-and-billing/teams-and-roles/overview', title: 'Teams, Roles & Membership', heading: '', anchor: '', text: 'Teams, Roles & Membership\\nAglyn has two kinds of people: your team (who build and manage the site) and site members (visitors who sign up to your site). Both are permission-aware.\\nPlan availability Paid. Seats are metered per tier; see Billing & plans.' },\n { path: '/workspace-and-billing/teams-and-roles/overview', title: 'Teams, Roles & Membership', heading: 'Team roles', anchor: '#team-roles', text: '- Invite teammates to a site and assign roles.\\n- Create custom roles with unique permission sets.\\n- Apply per-member overrides on top of a role.\\n- Permissions are enforced across the console\\'s APIs and surfaces, and team members act in the owner\\'s organization, not their own.' },\n { path: '/workspace-and-billing/teams-and-roles/overview', title: 'Teams, Roles & Membership', heading: 'Organizations', anchor: '#organizations', text: 'Aglyn is built around organization workspaces: an organization subscribes once, owns multiple websites, and shares media, plugins, dynamic data and billing across them. You can belong to several organizations with a different role in each (owner, admin, editor, viewer), and editors/viewers can be limited to specific sites. Each organization gets a Slack-style workspace address (your-org.aglyn.com).\\nEvery account operates inside an organization — solo accounts simply get a personal workspace automatically. The team features above keep working unchanged.\\nThree kinds of user\\nThe Team table labels every row with its type, because the type — not the role — decides which seat it uses. The same editor role is one or the other depending on whether their reach is the whole organization or a list of sites:\\n| Type | Reach | Seat |\\n| Team manager | The whole organization | One of your team seats (managersPerOrg) |\\n| Site collaborator | Only the site(s) they\\'re granted | A collaborator seat on each site they can reach — never a team seat |\\n| Site member | A published site, as a visitor with an account | None. Free and unlimited on every plan |\\nPending invites carry the same label, so you can tell a pending manager from a pending collaborator before it\\'s accepted.\\nSite roles\\nEvery person with access to a site holds one of four site roles. Managers get admin on every site in the organization; collaborators are given a role per site.\\n| Site role | Can edit content | Can publish | Notes |\\n| Viewer | No | No | Read-only |\\n| Author | Yes | No | Drafts and edits everything; nothing they do goes live |\\n| Editor | Yes | Yes | The usual working role |\\n| Admin | Yes | Yes | Also manages the site\\'s people, domain and plugins |\\nAuthor is the role to give a client who should work on their own con' },\n { path: '/workspace-and-billing/teams-and-roles/overview', title: 'Teams, Roles & Membership', heading: 'Site membership', anchor: '#site-membership', text: '- Visitors can sign in / sign up to your site.\\n- Member accounts are unlimited on every plan — including Free. Signups are never metered, capped, or charged per account.\\n- Gate screens as members-only so only signed-in members can view them.\\n- New members flow into your CRM.\\nThe platform safety limit {#visitor-record-ceiling}\\nThere is one ceiling behind all of that, and it is an abuse control rather than a plan dimension — the same number on every plan, free through enterprise, with nothing to buy:\\n| Records on one site | Ceiling |\\n| Member accounts | 50,000 |\\n| Leads | 200,000 |\\nPast the ceiling, new sign-ups (or lead captures) are refused. The visitor sees a plain \"not accepting new accounts right now\" message that does not name a limit, and your console shows a New member sign-ups are paused notice with the count of refusals this month.\\nIt does not clear on its own. Unlike the form and bandwidth ceilings, this one counts records you currently hold rather than a month\\'s activity, so nothing lifts it at the month boundary — the notice deliberately never names a date. Deleting records below the limit starts acceptance again immediately, and if the traffic is real, support will raise it.' },\n { path: '/workspace-and-billing/teams-and-roles/overview', title: 'Teams, Roles & Membership', heading: 'Seats', anchor: '#seats', text: 'Seats cover your team only: organization seats (workspace-wide) and per-site collaborator seats (teammates limited to one site). Both are metered and enforced; buy seat add-ons to grow. Site member accounts — visitors who sign up to your published site — are not seats and are never capped. See Billing & plans.\\nHow-tos\\n- Invite teammates\\n- Custom roles & permissions\\n- Members-only areas' },\n { path: '/workspace-and-billing/teams-and-roles/overview', title: 'Teams, Roles & Membership', heading: 'Related', anchor: '#related', text: '- Billing & plans\\n- Staff console (Aglyn staff only)' },\n { path: '/workspace-and-billing/white-label', title: 'White-label', heading: '', anchor: '', text: 'White-label\\nWhite-labeling replaces the Aglyn brand everywhere your organization and its sites are shown: the console your team signs in to, the sites you publish, and the transactional email your members and customers receive.\\nIt is one setting card, and the fields are short. The part worth reading closely is what each field actually replaces — a few of them change a surface you never look at yourself.\\nPlan availability White-labeling is the whiteLabel entitlement. Exactly two plans carry it by default: Agency and Enterprise. No lower tier does, and no add-on buys it.\\nEntitlements can also be granted per organization on top of a plan\\'s defaults, which is how an Enterprise agreement enables it for one organization — the card\\'s own upgrade notice says so.\\nAn organization that white-labels and then downgrades reverts cleanly: the stored brand is ignored, not deleted, and the Aglyn brand comes back everywhere at once. Upgrade again and the same values return.' },\n { path: '/workspace-and-billing/white-label', title: 'White-label', heading: 'Where the settings are', anchor: '#where', text: '1. Open your organization\\'s Settings page — the heading reads Organization Settings.\\n2. Choose the Branding tab.\\n3. The card is White-label brand.\\nYou need the admin role. The Branding tab is not shown at all to members below it, so a teammate who cannot find it is looking at a shorter tab strip than you are, not at a hidden card.\\nWhile your plan is still being read the card says \"Checking your plan…\". On a plan without the entitlement it shows an information notice instead of the editor: \"White-labeling the platform is included on the Agency plan — see Billing to upgrade. Enterprise plans can enable it per organization.\"' },\n { path: '/workspace-and-billing/white-label', title: 'White-label', heading: 'The fields', anchor: '#fields', text: 'Every field is optional. Leave one blank and it keeps the Aglyn default for that field — the grayed-out placeholder text in each box is that default, so a blank box is showing you exactly what will be used.\\nTwo fields are the exception, and both fall back to nothing rather than to an Aglyn value: Support URL and Email logo URL. Their placeholders say so. Leaving Support URL blank means no support link is shown anywhere — your customers are never sent to Aglyn\\'s support desk, which could not help them and would name us to someone who was never told we exist.\\n| Field | Helper text under it | What it replaces |\\n| Product name | Shown in the console chrome, site badge, and emails. | The word \"Aglyn\" wherever the product names itself |\\n| Email from-name | Display name on transactional email (the verified sending address is unchanged). | The display name in the recipient\\'s inbox |\\n| Support URL | Linked from branded surfaces and email footers. Leave it blank and no support link appears at all — your customers are never sent to Aglyn. | Where a \"need help?\" link points, or nothing |\\n| Primary color | CSS hex color used for the console primary and site badge. | The console\\'s primary color, live |\\n| Logo URL | Console chrome + site badge. Browse the org media library or paste an https URL. | The Aglyn wordmark in the console header |\\n| Favicon URL | Browser tab icon for branded console surfaces. | The browser-tab icon |\\n| Email logo URL | Logo shown in transactional email headers (a hosted PNG works best). | Nothing — it adds a header to your email. See below |\\n| Custom console domain | Saved now; domain routing to it ships in a later phase. | Nothing yet. See below |\\nLogo URL, Favicon URL and Email logo URL each carry a Browse button that opens your organization\\'s media library' },\n { path: '/workspace-and-billing/white-label', title: 'White-label', heading: 'Where you will see the change', anchor: '#where-you-see-it', text: '- Console header. Your Logo URL replaces the Aglyn wordmark. Set a Product name but no logo and the name is rendered as text in the same place instead.\\n- Console theme and tab. Primary color and Favicon URL are applied to the page you are already on, so the change is visible without a reload.\\n- Published sites. The site badge and the site favicon follow the same brand.\\n- Site fingerprint. The tag and the x-powered-by header that otherwise credit Aglyn are suppressed on a white-label organization\\'s published sites. This one is worth knowing about because it is invisible in a browser and is the part a technically curious visitor would otherwise read.\\n- Transactional email. Covered in its own section next.\\nOne counter-intuitive rule governs all of it: the branded chrome activates only once you have actually set a product name or a logo, not the moment your plan grants the entitlement. An Agency organization that has never opened this card keeps the plain Aglyn wordmark — there is no half-branded state and no flash of one brand turning into the other.' },\n { path: '/workspace-and-billing/white-label', title: 'White-label', heading: 'The email half', anchor: '#email', text: 'Email is where white-labeling used to leak, and it is the part that changed most recently.\\nMerge tokens {#merge-tokens}\\nEvery system email resolves three brand tokens, for every organization:\\n| Token | Resolves to |\\n| {{brand.productName}} | Your Product name, or Aglyn |\\n| {{brand.fromName}} | Your Email from-name, or Aglyn |\\n| {{brand.supportUrl}} | Your Support URL, or empty |\\nThey resolve on Aglyn\\'s own organizations too, which is what lets one email template be correct for both populations instead of hard-coding the majority case. If your organization is not white-labeled, the tokens render the Aglyn values.\\n{{brand.supportUrl}} is the one that can render as nothing, and only for a white-labeled organization that left the field blank. Aglyn\\'s own support page is never substituted — the email your customer receives reads as you throughout, and a link to our desk in it would be the one thing on the page that is not. Aglyn\\'s built-in emails drop the whole \"Need help?\" line rather than leave a dangling phrase; a template you design places the token yourself, so put it somewhere a blank reads cleanly.\\nThe email logo {#email-logo}\\nEmail logo URL renders as a centered header row at the top of the email body, above whatever the template itself lays out, inside the same 600-pixel column — so on a phone it scales with the message rather than floating against the viewport. Its height is capped rather than fixed, so a wide wordmark and a square mark both land at a sensible size without you supplying dimensions.\\nTwo details are deliberate and quiet:\\n- A blank Email logo URL emits nothing at all — not an empty row, not a spacer. (Support URL behaves the same way, for the same reason.) An email with a gap where a logo should be reads as broken; an email with no logo reads as plain' },\n { path: '/workspace-and-billing/white-label', title: 'White-label', heading: 'Known limitation: the custom console domain', anchor: '#custom-console-domain', text: 'Custom console domain is saved but does nothing yet, and the field says so in its own helper text: \"Saved now; domain routing to it ships in a later phase.\" Your team still signs in at the standard console address.\\nSaving it is not entirely inert. The name is reserved for your organization when you save it, so nobody else can claim it in the meantime, and the reservation is released if you clear the field or change it to a different name. What has not shipped is the routing and certificate half — until that lands, a reservation is all the field buys, and pointing DNS at it achieves nothing.' },\n { path: '/workspace-and-billing/white-label', title: 'White-label', heading: 'Not the same as renaming a self-hosted platform', anchor: '#platform-brand', text: 'These two get confused constantly, and they are unrelated mechanisms:\\n| | White-label brand | Platform brand |\\n| Who sets it | An organization admin, in the console | Whoever runs the deployment, in its environment |\\n| Scope | That one organization | The entire installation, every organization on it |\\n| How | The card on this page | NEXTPUBLICPLATFORMBRANDNAME and friends |\\n| Gated on | The whiteLabel entitlement | Nothing — it is deployment configuration |\\n| Takes effect | On save | On the next image build, not a restart |\\nIf you are running Aglyn yourself and want the product to stop calling itself Aglyn for everybody, that is the platform brand, not this card — see Self-hosting. The two compose: on a self-hosted installation, an organization without the whiteLabel entitlement falls back to the operator\\'s platform brand rather than to Aglyn\\'s.' },\n { path: '/workspace-and-billing/white-label', title: 'White-label', heading: 'Related', anchor: '#related', text: '- Billing & plans\\n- Teams, roles & membership\\n- Self-hosting' },\n]\n"],"names":["ASSIST_DOCS_INDEX","path","title","heading","anchor","text"],"mappings":"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"}