@omega.js/web 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1162) hide show
  1. package/LICENSE +98 -0
  2. package/README.md +640 -0
  3. package/bin/omega +2 -0
  4. package/bin/omega-web +2 -0
  5. package/bin/omg +2 -0
  6. package/core/_includes/admin/sections/sidebar.json +100 -0
  7. package/core/_includes/admin/sections/topbar.json +11 -0
  8. package/core/_includes/backend/sections/sidebar.json +39 -0
  9. package/core/_includes/backend/sections/topbar.json +16 -0
  10. package/core/_includes/core/body.html +102 -0
  11. package/core/_includes/core/foot.html +536 -0
  12. package/core/_includes/core/head.html +423 -0
  13. package/core/_includes/core/pricing/price-per-unit.html +33 -0
  14. package/core/_includes/core/showcase/entry-body.html +94 -0
  15. package/core/_includes/core/showcase/index-body.html +43 -0
  16. package/core/_includes/core/showcase/nav.html +60 -0
  17. package/core/_includes/frontend/sections/footer.json +74 -0
  18. package/core/_includes/frontend/sections/nav.json +70 -0
  19. package/core/_includes/global/sections/account.json +31 -0
  20. package/core/_includes/modules/engagement/giscus.html +26 -0
  21. package/core/_layouts/blueprint/404.html +10 -0
  22. package/core/_layouts/blueprint/about.html +10 -0
  23. package/core/_layouts/blueprint/account/index.html +11 -0
  24. package/core/_layouts/blueprint/admin/calendar/index.html +590 -0
  25. package/core/_layouts/blueprint/admin/dashboard/index.html +301 -0
  26. package/core/_layouts/blueprint/admin/firebase/index.html +214 -0
  27. package/core/_layouts/blueprint/admin/posts/editor.html +157 -0
  28. package/core/_layouts/blueprint/admin/posts/index.html +126 -0
  29. package/core/_layouts/blueprint/admin/stackblitz/index.html +30 -0
  30. package/core/_layouts/blueprint/admin/studio/index.html +85 -0
  31. package/core/_layouts/blueprint/admin/users/index.html +272 -0
  32. package/core/_layouts/blueprint/admin/users/new.html +82 -0
  33. package/core/_layouts/blueprint/admin/verts/index.html +204 -0
  34. package/core/_layouts/blueprint/alternatives/alternative.html +15 -0
  35. package/core/_layouts/blueprint/alternatives/index.html +10 -0
  36. package/core/_layouts/blueprint/app.html +10 -0
  37. package/core/_layouts/blueprint/auth/reset.html +19 -0
  38. package/core/_layouts/blueprint/auth/signin.html +19 -0
  39. package/core/_layouts/blueprint/auth/signup.html +19 -0
  40. package/core/_layouts/blueprint/auth/token.html +19 -0
  41. package/core/_layouts/blueprint/blog/categories/category.html +8 -0
  42. package/core/_layouts/blueprint/blog/categories/index.html +11 -0
  43. package/core/_layouts/blueprint/blog/index.html +10 -0
  44. package/core/_layouts/blueprint/blog/post.html +5 -0
  45. package/core/_layouts/blueprint/blog/tags/index.html +11 -0
  46. package/core/_layouts/blueprint/blog/tags/tag.html +8 -0
  47. package/core/_layouts/blueprint/careers.md +13 -0
  48. package/core/_layouts/blueprint/collection/category.html +9 -0
  49. package/core/_layouts/blueprint/collection/document.html +9 -0
  50. package/core/_layouts/blueprint/collection/index.html +9 -0
  51. package/core/_layouts/blueprint/connections/callback.html +11 -0
  52. package/core/_layouts/blueprint/contact.html +14 -0
  53. package/core/_layouts/blueprint/dashboard/index.html +10 -0
  54. package/core/_layouts/blueprint/download.html +20 -0
  55. package/core/_layouts/blueprint/extension/index.html +14 -0
  56. package/core/_layouts/blueprint/extension/installed.html +11 -0
  57. package/core/_layouts/blueprint/feedback.html +10 -0
  58. package/core/_layouts/blueprint/index.html +16 -0
  59. package/core/_layouts/blueprint/legal/cookies.md +57 -0
  60. package/core/_layouts/blueprint/legal/privacy.md +109 -0
  61. package/core/_layouts/blueprint/legal/terms.md +155 -0
  62. package/core/_layouts/blueprint/payment/checkout.html +15 -0
  63. package/core/_layouts/blueprint/payment/confirmation.html +14 -0
  64. package/core/_layouts/blueprint/portal/email-preferences.html +15 -0
  65. package/core/_layouts/blueprint/pricing.html +20 -0
  66. package/core/_layouts/blueprint/status.html +10 -0
  67. package/core/_layouts/blueprint/team/index.html +10 -0
  68. package/core/_layouts/blueprint/team/member.html +5 -0
  69. package/core/_layouts/blueprint/updates/index.html +10 -0
  70. package/core/_layouts/blueprint/updates/update.html +10 -0
  71. package/core/_layouts/core/root.html +45 -0
  72. package/core/_layouts/modules/utilities/redirect.html +129 -0
  73. package/core/css/_studio.scss +237 -0
  74. package/core/css/components/_index.scss +146 -0
  75. package/core/css/core/_.scss +0 -0
  76. package/core/css/core/_alert.scss +87 -0
  77. package/core/css/core/_animations.scss +343 -0
  78. package/core/css/core/_bindings.scss +116 -0
  79. package/core/css/core/_blog-search.scss +57 -0
  80. package/core/css/core/_chatsy.scss +61 -0
  81. package/core/css/core/_consent.scss +424 -0
  82. package/core/css/core/_custom-font-awesome.scss +146 -0
  83. package/core/css/core/_exit-popup.scss +42 -0
  84. package/core/css/core/_generated-badge.scss +33 -0
  85. package/core/css/core/_initialize.scss +29 -0
  86. package/core/css/core/_lazy-loading.scss +104 -0
  87. package/core/css/core/_social-sharing.scss +41 -0
  88. package/core/css/core/_tones.scss +32 -0
  89. package/core/css/core/_utilities.scss +157 -0
  90. package/core/css/main.scss +54 -0
  91. package/core/css/motion/_index.scss +368 -0
  92. package/core/css/pages/404/index.scss +0 -0
  93. package/core/css/pages/admin/calendar/index.scss +692 -0
  94. package/core/css/pages/admin/firebase/index.scss +33 -0
  95. package/core/css/pages/admin/index.scss +9 -0
  96. package/core/css/pages/admin/users/index.scss +9 -0
  97. package/core/css/pages/admin/verts/index.scss +9 -0
  98. package/core/css/pages/alternatives/[alternative]/index.scss +0 -0
  99. package/core/css/pages/blog/[slug].scss +55 -0
  100. package/core/css/pages/dashboard/account/index.scss +242 -0
  101. package/core/css/pages/download/index.scss +480 -0
  102. package/core/css/pages/extension/installed/index.scss +317 -0
  103. package/core/css/pages/feedback/index.scss +31 -0
  104. package/core/css/pages/payment/confirmation/index.scss +14 -0
  105. package/core/css/pages/pricing/index.scss +0 -0
  106. package/core/css/pages/status/index.scss +162 -0
  107. package/core/css/pages/test/libraries/layers/index.scss +28 -0
  108. package/core/css/pages/updates/[update].scss +46 -0
  109. package/core/css/pages/updates/index.scss +13 -0
  110. package/core/css/shell/_index.scss +207 -0
  111. package/core/css/tokens/_index.scss +166 -0
  112. package/core/icons/flags/ar.svg +1 -0
  113. package/core/icons/flags/bd.svg +1 -0
  114. package/core/icons/flags/ca.svg +1 -0
  115. package/core/icons/flags/ch.svg +1 -0
  116. package/core/icons/flags/cn.svg +1 -0
  117. package/core/icons/flags/co.svg +1 -0
  118. package/core/icons/flags/cw.svg +1 -0
  119. package/core/icons/flags/cz.svg +1 -0
  120. package/core/icons/flags/de.svg +1 -0
  121. package/core/icons/flags/dk.svg +1 -0
  122. package/core/icons/flags/eg.svg +1 -0
  123. package/core/icons/flags/es.svg +1 -0
  124. package/core/icons/flags/fi.svg +1 -0
  125. package/core/icons/flags/fr.svg +1 -0
  126. package/core/icons/flags/gb.svg +1 -0
  127. package/core/icons/flags/gq.svg +1 -0
  128. package/core/icons/flags/gr.svg +1 -0
  129. package/core/icons/flags/gt.svg +1 -0
  130. package/core/icons/flags/hn.svg +1 -0
  131. package/core/icons/flags/id.svg +1 -0
  132. package/core/icons/flags/ie.svg +1 -0
  133. package/core/icons/flags/in.svg +1 -0
  134. package/core/icons/flags/ir.svg +1 -0
  135. package/core/icons/flags/it.svg +1 -0
  136. package/core/icons/flags/jp.svg +1 -0
  137. package/core/icons/flags/kp.svg +1 -0
  138. package/core/icons/flags/kr.svg +1 -0
  139. package/core/icons/flags/ly.svg +1 -0
  140. package/core/icons/flags/ma.svg +1 -0
  141. package/core/icons/flags/mu.svg +1 -0
  142. package/core/icons/flags/mx.svg +1 -0
  143. package/core/icons/flags/ng.svg +1 -0
  144. package/core/icons/flags/pa.svg +1 -0
  145. package/core/icons/flags/ph.svg +1 -0
  146. package/core/icons/flags/pk.svg +1 -0
  147. package/core/icons/flags/pt.svg +1 -0
  148. package/core/icons/flags/ru.svg +1 -0
  149. package/core/icons/flags/sa.svg +1 -0
  150. package/core/icons/flags/sd.svg +1 -0
  151. package/core/icons/flags/se.svg +1 -0
  152. package/core/icons/flags/th.svg +1 -0
  153. package/core/icons/flags/tm.svg +1 -0
  154. package/core/icons/flags/tr.svg +1 -0
  155. package/core/icons/flags/us.svg +1 -0
  156. package/core/icons/flags/uy.svg +1 -0
  157. package/core/icons/flags/vn.svg +1 -0
  158. package/core/icons/flags/za.svg +1 -0
  159. package/core/images/exit-popup/subscriber-1.jpg +0 -0
  160. package/core/images/exit-popup/subscriber-2.jpg +0 -0
  161. package/core/images/exit-popup/subscriber-3.jpg +0 -0
  162. package/core/images/exit-popup/subscriber-4.jpg +0 -0
  163. package/core/images/placeholder/photo-1.jpg +0 -0
  164. package/core/images/placeholder/photo-2.jpg +0 -0
  165. package/core/images/placeholder/photo-3.jpg +0 -0
  166. package/core/js/core/analytics-loader.js +253 -0
  167. package/core/js/core/app-shell.js +136 -0
  168. package/core/js/core/appearance.js +195 -0
  169. package/core/js/core/auth.js +457 -0
  170. package/core/js/core/complete.js +72 -0
  171. package/core/js/core/consent.js +384 -0
  172. package/core/js/core/dev-icon-audit.js +63 -0
  173. package/core/js/core/dev-palette.js +670 -0
  174. package/core/js/core/dev-sections.js +53 -0
  175. package/core/js/core/exit-popup.js +276 -0
  176. package/core/js/core/initialize.js +8 -0
  177. package/core/js/core/language-switcher.js +167 -0
  178. package/core/js/core/lazy-loading.js +479 -0
  179. package/core/js/core/motion.js +28 -0
  180. package/core/js/core/query-strings.js +146 -0
  181. package/core/js/core/service-worker.js +28 -0
  182. package/core/js/core/social-sharing.js +293 -0
  183. package/core/js/first-paint.js +35 -0
  184. package/core/js/layouts/modules/utilities/redirect.js +122 -0
  185. package/core/js/libs/admin-helpers.js +62 -0
  186. package/core/js/libs/alert-dismiss.js +35 -0
  187. package/core/js/libs/analytics.js +368 -0
  188. package/core/js/libs/auth/email.js +144 -0
  189. package/core/js/libs/auth/errors.js +119 -0
  190. package/core/js/libs/auth/forms.js +206 -0
  191. package/core/js/libs/auth/index.js +181 -0
  192. package/core/js/libs/auth/oauth.js +394 -0
  193. package/core/js/libs/auth/orphan.js +110 -0
  194. package/core/js/libs/auth/password-toggle.js +44 -0
  195. package/core/js/libs/auth/session-params.js +266 -0
  196. package/core/js/libs/auth/tracking.js +41 -0
  197. package/core/js/libs/charts.js +516 -0
  198. package/core/js/libs/consent-region.js +127 -0
  199. package/core/js/libs/dev.js +150 -0
  200. package/core/js/libs/graph.js +201 -0
  201. package/core/js/libs/initialize-tooltips.js +31 -0
  202. package/core/js/libs/logger.js +38 -0
  203. package/core/js/libs/omega-copy.js +89 -0
  204. package/core/js/libs/path-prefix.js +44 -0
  205. package/core/js/libs/payment-config.js +76 -0
  206. package/core/js/libs/recaptcha.js +94 -0
  207. package/core/js/libs/sale-name.js +295 -0
  208. package/core/js/libs/showcase-frames.js +37 -0
  209. package/core/js/libs/srcset.js +69 -0
  210. package/core/js/libs/tracking-consent.js +161 -0
  211. package/core/js/main.js +144 -0
  212. package/core/js/pages/404/index.js +57 -0
  213. package/core/js/pages/admin/calendar/calendar-core.js +701 -0
  214. package/core/js/pages/admin/calendar/calendar-events.js +853 -0
  215. package/core/js/pages/admin/calendar/calendar-renderer.js +807 -0
  216. package/core/js/pages/admin/calendar/campaign-preview.js +105 -0
  217. package/core/js/pages/admin/calendar/index.js +40 -0
  218. package/core/js/pages/admin/firebase/index.js +727 -0
  219. package/core/js/pages/admin/index.js +531 -0
  220. package/core/js/pages/admin/posts/editor/index.js +230 -0
  221. package/core/js/pages/admin/posts/index.js +237 -0
  222. package/core/js/pages/admin/studio/index.js +538 -0
  223. package/core/js/pages/admin/users/index.js +669 -0
  224. package/core/js/pages/admin/users/new/index.js +65 -0
  225. package/core/js/pages/admin/verts/index.js +407 -0
  226. package/core/js/pages/alternatives/[alternative]/index.js +17 -0
  227. package/core/js/pages/app/index.js +135 -0
  228. package/core/js/pages/blog/[slug].js +132 -0
  229. package/core/js/pages/blog/_search.mjs +108 -0
  230. package/core/js/pages/blog/index.js +193 -0
  231. package/core/js/pages/blog/page/[num]/index.js +7 -0
  232. package/core/js/pages/connections/callback/index.js +148 -0
  233. package/core/js/pages/contact/index.js +170 -0
  234. package/core/js/pages/cookies.js +2 -0
  235. package/core/js/pages/dashboard/account/index.js +366 -0
  236. package/core/js/pages/dashboard/account/modules/orders.js +82 -0
  237. package/core/js/pages/dashboard/account/sections/api-keys.js +149 -0
  238. package/core/js/pages/dashboard/account/sections/billing.js +1667 -0
  239. package/core/js/pages/dashboard/account/sections/connections.js +333 -0
  240. package/core/js/pages/dashboard/account/sections/data-request.js +225 -0
  241. package/core/js/pages/dashboard/account/sections/delete.js +80 -0
  242. package/core/js/pages/dashboard/account/sections/notifications.js +167 -0
  243. package/core/js/pages/dashboard/account/sections/orders.js +151 -0
  244. package/core/js/pages/dashboard/account/sections/profile.js +200 -0
  245. package/core/js/pages/dashboard/account/sections/referrals.js +217 -0
  246. package/core/js/pages/dashboard/account/sections/refund.js +253 -0
  247. package/core/js/pages/dashboard/account/sections/security.js +726 -0
  248. package/core/js/pages/dashboard/account/sections/team.js +247 -0
  249. package/core/js/pages/download/index.js +418 -0
  250. package/core/js/pages/extension/index.js +164 -0
  251. package/core/js/pages/extension/installed/index.js +135 -0
  252. package/core/js/pages/feedback/index.js +193 -0
  253. package/core/js/pages/legal/_document.js +91 -0
  254. package/core/js/pages/payment/checkout/index.js +420 -0
  255. package/core/js/pages/payment/checkout/modules/api.js +139 -0
  256. package/core/js/pages/payment/checkout/modules/dev-section.js +175 -0
  257. package/core/js/pages/payment/checkout/modules/discount.js +69 -0
  258. package/core/js/pages/payment/checkout/modules/pricing.js +73 -0
  259. package/core/js/pages/payment/checkout/modules/state.js +339 -0
  260. package/core/js/pages/payment/checkout/modules/tracking.js +54 -0
  261. package/core/js/pages/payment/confirmation/index.js +91 -0
  262. package/core/js/pages/payment/confirmation/modules/celebration.js +45 -0
  263. package/core/js/pages/payment/confirmation/modules/state.js +112 -0
  264. package/core/js/pages/payment/confirmation/modules/tracking.js +133 -0
  265. package/core/js/pages/payment/confirmation/modules/verify.js +142 -0
  266. package/core/js/pages/portal/email-preferences/index.js +183 -0
  267. package/core/js/pages/pricing/index.js +425 -0
  268. package/core/js/pages/privacy.js +2 -0
  269. package/core/js/pages/reset/index.js +12 -0
  270. package/core/js/pages/signin/index.js +22 -0
  271. package/core/js/pages/signup/index.js +23 -0
  272. package/core/js/pages/status/index.js +687 -0
  273. package/core/js/pages/terms.js +2 -0
  274. package/core/js/pages/test/components/[category]/[name]/index.js +26 -0
  275. package/core/js/pages/test/libraries/appearance/index.js +220 -0
  276. package/core/js/pages/test/libraries/form-manager/index.js +328 -0
  277. package/core/js/pages/test/libraries/layers/index.js +16 -0
  278. package/core/js/pages/test/libraries/verts/index.js +68 -0
  279. package/core/js/pages/test/sections/[category]/[name]/index.js +28 -0
  280. package/core/js/pages/test/styleguide/index.js +35 -0
  281. package/core/js/pages/token/index.js +203 -0
  282. package/core/logos/brandmarks/original/apple-app-store.svg +1 -0
  283. package/core/logos/brandmarks/original/apple.svg +6 -0
  284. package/core/logos/brandmarks/original/bitbucket.svg +14 -0
  285. package/core/logos/brandmarks/original/bitcoin.svg +1 -0
  286. package/core/logos/brandmarks/original/bluesky.svg +7 -0
  287. package/core/logos/brandmarks/original/bootstrap.svg +43 -0
  288. package/core/logos/brandmarks/original/brave.svg +1 -0
  289. package/core/logos/brandmarks/original/chrome.svg +1 -0
  290. package/core/logos/brandmarks/original/discord.svg +1 -0
  291. package/core/logos/brandmarks/original/dropbox.svg +10 -0
  292. package/core/logos/brandmarks/original/edge.svg +1 -0
  293. package/core/logos/brandmarks/original/facebook-f.svg +1 -0
  294. package/core/logos/brandmarks/original/facebook.svg +8 -0
  295. package/core/logos/brandmarks/original/figma.svg +10 -0
  296. package/core/logos/brandmarks/original/firefox.svg +1 -0
  297. package/core/logos/brandmarks/original/github.svg +6 -0
  298. package/core/logos/brandmarks/original/gitlab.svg +17 -0
  299. package/core/logos/brandmarks/original/google-a-s.svg +1 -0
  300. package/core/logos/brandmarks/original/google-chrome-webstore.svg +49 -0
  301. package/core/logos/brandmarks/original/google-play-store.svg +1 -0
  302. package/core/logos/brandmarks/original/google-web-search.svg +1 -0
  303. package/core/logos/brandmarks/original/google.svg +1 -0
  304. package/core/logos/brandmarks/original/gulp.svg +4 -0
  305. package/core/logos/brandmarks/original/html-5.svg +1 -0
  306. package/core/logos/brandmarks/original/instagram.svg +1 -0
  307. package/core/logos/brandmarks/original/javascript.svg +1 -0
  308. package/core/logos/brandmarks/original/jira.svg +18 -0
  309. package/core/logos/brandmarks/original/kick.svg +7 -0
  310. package/core/logos/brandmarks/original/linkedin.svg +1 -0
  311. package/core/logos/brandmarks/original/linux.svg +28 -0
  312. package/core/logos/brandmarks/original/messenger.svg +16 -0
  313. package/core/logos/brandmarks/original/microsoft.svg +1 -0
  314. package/core/logos/brandmarks/original/npm.svg +8 -0
  315. package/core/logos/brandmarks/original/opera.svg +1 -0
  316. package/core/logos/brandmarks/original/patreon.svg +6 -0
  317. package/core/logos/brandmarks/original/pinterest.svg +6 -0
  318. package/core/logos/brandmarks/original/reddit.svg +1 -0
  319. package/core/logos/brandmarks/original/safari.svg +31 -0
  320. package/core/logos/brandmarks/original/sass.svg +3 -0
  321. package/core/logos/brandmarks/original/signal.svg +6 -0
  322. package/core/logos/brandmarks/original/skype.svg +47 -0
  323. package/core/logos/brandmarks/original/slack.svg +33 -0
  324. package/core/logos/brandmarks/original/soundcloud.svg +1 -0
  325. package/core/logos/brandmarks/original/spotify.svg +5 -0
  326. package/core/logos/brandmarks/original/steam.svg +6 -0
  327. package/core/logos/brandmarks/original/telegram.svg +14 -0
  328. package/core/logos/brandmarks/original/tiktok.svg +1 -0
  329. package/core/logos/brandmarks/original/trello.svg +16 -0
  330. package/core/logos/brandmarks/original/tumblr.svg +1 -0
  331. package/core/logos/brandmarks/original/twitch.svg +6 -0
  332. package/core/logos/brandmarks/original/twitter.svg +6 -0
  333. package/core/logos/brandmarks/original/unity.svg +6 -0
  334. package/core/logos/brandmarks/original/x.svg +7 -0
  335. package/core/logos/brandmarks/original/yahoo-y.svg +1 -0
  336. package/core/logos/brandmarks/original/youtube-square.svg +1 -0
  337. package/core/logos/brandmarks/original/youtube.svg +1 -0
  338. package/core/logos/combomarks/dark/airbnb.svg +30 -0
  339. package/core/logos/combomarks/dark/slack.svg +38 -0
  340. package/core/logos/combomarks/dark/spotify.svg +35 -0
  341. package/core/logos/combomarks/dark/twilio.svg +26 -0
  342. package/core/logos/combomarks/dark/weebly.svg +35 -0
  343. package/core/logos/combomarks/gray/airbnb.svg +33 -0
  344. package/core/logos/combomarks/gray/alphabet.svg +23 -0
  345. package/core/logos/combomarks/gray/amazon.svg +38 -0
  346. package/core/logos/combomarks/gray/boring-company.svg +91 -0
  347. package/core/logos/combomarks/gray/burberry.svg +239 -0
  348. package/core/logos/combomarks/gray/embark.svg +21 -0
  349. package/core/logos/combomarks/gray/fitbit.svg +61 -0
  350. package/core/logos/combomarks/gray/fred-perry.svg +94 -0
  351. package/core/logos/combomarks/gray/google.svg +27 -0
  352. package/core/logos/combomarks/gray/hollister.svg +34 -0
  353. package/core/logos/combomarks/gray/hubspot.svg +27 -0
  354. package/core/logos/combomarks/gray/kaplan.svg +33 -0
  355. package/core/logos/combomarks/gray/lenovo.svg +27 -0
  356. package/core/logos/combomarks/gray/levis.svg +25 -0
  357. package/core/logos/combomarks/gray/mapbox.svg +37 -0
  358. package/core/logos/combomarks/gray/netflix.svg +15 -0
  359. package/core/logos/combomarks/gray/new-balance.svg +32 -0
  360. package/core/logos/combomarks/gray/nike.svg +17 -0
  361. package/core/logos/combomarks/gray/paypal.svg +41 -0
  362. package/core/logos/combomarks/gray/puma.svg +82 -0
  363. package/core/logos/combomarks/gray/samsung.svg +26 -0
  364. package/core/logos/combomarks/gray/shopify.svg +36 -0
  365. package/core/logos/combomarks/gray/slack.svg +42 -0
  366. package/core/logos/combomarks/gray/spotify.svg +38 -0
  367. package/core/logos/combomarks/gray/stripe.svg +24 -0
  368. package/core/logos/combomarks/gray/tnf.svg +30 -0
  369. package/core/logos/combomarks/gray/twilio.svg +29 -0
  370. package/core/logos/combomarks/gray/uber.svg +24 -0
  371. package/core/logos/combomarks/gray/weebly.svg +38 -0
  372. package/core/logos/combomarks/original/airbnb.svg +33 -0
  373. package/core/logos/combomarks/original/alphabet.svg +23 -0
  374. package/core/logos/combomarks/original/amazon.svg +38 -0
  375. package/core/logos/combomarks/original/asana.svg +8 -0
  376. package/core/logos/combomarks/original/boring-company.svg +91 -0
  377. package/core/logos/combomarks/original/discord.svg +6 -0
  378. package/core/logos/combomarks/original/embark.svg +24 -0
  379. package/core/logos/combomarks/original/fitbit.svg +60 -0
  380. package/core/logos/combomarks/original/github.svg +6 -0
  381. package/core/logos/combomarks/original/gitlab.svg +20 -0
  382. package/core/logos/combomarks/original/google.svg +28 -0
  383. package/core/logos/combomarks/original/hubspot.svg +29 -0
  384. package/core/logos/combomarks/original/instagram.svg +7 -0
  385. package/core/logos/combomarks/original/kickstarter.svg +6 -0
  386. package/core/logos/combomarks/original/lenovo.svg +24 -0
  387. package/core/logos/combomarks/original/linkedin.svg +6 -0
  388. package/core/logos/combomarks/original/mapbox.svg +39 -0
  389. package/core/logos/combomarks/original/mastodon.svg +7 -0
  390. package/core/logos/combomarks/original/medium.svg +6 -0
  391. package/core/logos/combomarks/original/meta.svg +22 -0
  392. package/core/logos/combomarks/original/microsoft.svg +13 -0
  393. package/core/logos/combomarks/original/monday.svg +12 -0
  394. package/core/logos/combomarks/original/netflix.svg +15 -0
  395. package/core/logos/combomarks/original/notion.svg +9 -0
  396. package/core/logos/combomarks/original/paypal.svg +41 -0
  397. package/core/logos/combomarks/original/reddit.svg +13 -0
  398. package/core/logos/combomarks/original/samsung.svg +25 -0
  399. package/core/logos/combomarks/original/shopify.svg +35 -0
  400. package/core/logos/combomarks/original/slack.svg +47 -0
  401. package/core/logos/combomarks/original/spotify.svg +37 -0
  402. package/core/logos/combomarks/original/stackoverflow.svg +28 -0
  403. package/core/logos/combomarks/original/stripe.svg +24 -0
  404. package/core/logos/combomarks/original/tiktok.svg +12 -0
  405. package/core/logos/combomarks/original/tumblr.svg +6 -0
  406. package/core/logos/combomarks/original/uber.svg +24 -0
  407. package/core/logos/combomarks/original/weebly.svg +34 -0
  408. package/core/logos/combomarks/original/whatsapp.svg +20 -0
  409. package/core/logos/combomarks/original/youtube.svg +10 -0
  410. package/core/logos/combomarks/original/zoom.svg +7 -0
  411. package/core/logos/combomarks/white/airbnb.svg +33 -0
  412. package/core/logos/combomarks/white/alphabet.svg +23 -0
  413. package/core/logos/combomarks/white/amazon.svg +38 -0
  414. package/core/logos/combomarks/white/boring-company.svg +91 -0
  415. package/core/logos/combomarks/white/embark.svg +21 -0
  416. package/core/logos/combomarks/white/fitbit.svg +61 -0
  417. package/core/logos/combomarks/white/google.svg +27 -0
  418. package/core/logos/combomarks/white/hubspot.svg +27 -0
  419. package/core/logos/combomarks/white/kaplan.svg +33 -0
  420. package/core/logos/combomarks/white/lenovo.svg +27 -0
  421. package/core/logos/combomarks/white/mapbox.svg +37 -0
  422. package/core/logos/combomarks/white/netflix.svg +15 -0
  423. package/core/logos/combomarks/white/paypal.svg +40 -0
  424. package/core/logos/combomarks/white/samsung.svg +26 -0
  425. package/core/logos/combomarks/white/shopify.svg +36 -0
  426. package/core/logos/combomarks/white/slack.svg +42 -0
  427. package/core/logos/combomarks/white/spotify.svg +38 -0
  428. package/core/logos/combomarks/white/stripe.svg +24 -0
  429. package/core/logos/combomarks/white/twilio.svg +29 -0
  430. package/core/logos/combomarks/white/uber.svg +24 -0
  431. package/core/logos/combomarks/white/weebly.svg +38 -0
  432. package/defaults/pages/404.md +4 -0
  433. package/defaults/pages/about.md +4 -0
  434. package/defaults/pages/admin/calendar/index.html +4 -0
  435. package/defaults/pages/admin/firebase/index.html +4 -0
  436. package/defaults/pages/admin/index.md +4 -0
  437. package/defaults/pages/admin/posts/editor.html +4 -0
  438. package/defaults/pages/admin/posts/index.html +4 -0
  439. package/defaults/pages/admin/stackblitz/index.html +4 -0
  440. package/defaults/pages/admin/studio/index.html +4 -0
  441. package/defaults/pages/admin/users/index.html +4 -0
  442. package/defaults/pages/admin/users/new.html +4 -0
  443. package/defaults/pages/admin/verts/index.html +4 -0
  444. package/defaults/pages/ads-txt.html +11 -0
  445. package/defaults/pages/alternatives/index.md +4 -0
  446. package/defaults/pages/app.md +4 -0
  447. package/defaults/pages/auth/reset.md +4 -0
  448. package/defaults/pages/auth/signin.md +4 -0
  449. package/defaults/pages/auth/signup.md +4 -0
  450. package/defaults/pages/auth/token.md +4 -0
  451. package/defaults/pages/blog/categories/category.html +21 -0
  452. package/defaults/pages/blog/categories.md +4 -0
  453. package/defaults/pages/blog/index-json.html +27 -0
  454. package/defaults/pages/blog/tags/tag.html +21 -0
  455. package/defaults/pages/blog/tags.md +4 -0
  456. package/defaults/pages/blog.md +22 -0
  457. package/defaults/pages/careers.md +4 -0
  458. package/defaults/pages/connections/callback.md +4 -0
  459. package/defaults/pages/contact.md +4 -0
  460. package/defaults/pages/dashboard/account.md +4 -0
  461. package/defaults/pages/dashboard/index.md +12 -0
  462. package/defaults/pages/download.md +4 -0
  463. package/defaults/pages/extension/index.html +4 -0
  464. package/defaults/pages/extension/installed.html +4 -0
  465. package/defaults/pages/feedback.md +4 -0
  466. package/defaults/pages/feeds/posts-json.html +40 -0
  467. package/defaults/pages/feeds/posts-xml.html +38 -0
  468. package/defaults/pages/humans.html +25 -0
  469. package/defaults/pages/index.md +4 -0
  470. package/defaults/pages/legal/cookies.md +4 -0
  471. package/defaults/pages/legal/privacy.md +4 -0
  472. package/defaults/pages/legal/terms.md +4 -0
  473. package/defaults/pages/llms-txt.html +46 -0
  474. package/defaults/pages/opensearch.html +16 -0
  475. package/defaults/pages/pages-json.html +42 -0
  476. package/defaults/pages/payment/checkout.md +4 -0
  477. package/defaults/pages/payment/confirmation.md +4 -0
  478. package/defaults/pages/portal/email-preferences.md +4 -0
  479. package/defaults/pages/pricing.md +4 -0
  480. package/defaults/pages/redirects/account.md +12 -0
  481. package/defaults/pages/redirects/admin/dashboard.md +11 -0
  482. package/defaults/pages/redirects/auth/change-password.md +9 -0
  483. package/defaults/pages/redirects/auth/forgot.md +9 -0
  484. package/defaults/pages/redirects/auth/join.md +9 -0
  485. package/defaults/pages/redirects/auth/login.md +9 -0
  486. package/defaults/pages/redirects/auth/recover.md +9 -0
  487. package/defaults/pages/redirects/auth/register.md +9 -0
  488. package/defaults/pages/redirects/auth/reset-password.md +9 -0
  489. package/defaults/pages/redirects/billing/cancel.md +9 -0
  490. package/defaults/pages/redirects/billing/refund.md +9 -0
  491. package/defaults/pages/robots.html +36 -0
  492. package/defaults/pages/security-txt.html +11 -0
  493. package/defaults/pages/sitemap.html +37 -0
  494. package/defaults/pages/status.md +4 -0
  495. package/defaults/pages/team/index.md +4 -0
  496. package/defaults/pages/test/account/dashboard.html +16 -0
  497. package/defaults/pages/test/index.md +12 -0
  498. package/defaults/pages/test/libraries/appearance.html +268 -0
  499. package/defaults/pages/test/libraries/cover.html +50 -0
  500. package/defaults/pages/test/libraries/error.html +30 -0
  501. package/defaults/pages/test/libraries/firestore.html +202 -0
  502. package/defaults/pages/test/libraries/form-manager.html +374 -0
  503. package/defaults/pages/test/libraries/layers.html +52 -0
  504. package/defaults/pages/test/libraries/lazy-loading.html +450 -0
  505. package/defaults/pages/test/libraries/verts.html +149 -0
  506. package/defaults/pages/test/redirect/external.md +13 -0
  507. package/defaults/pages/test/redirect/internal.md +13 -0
  508. package/defaults/pages/test/styleguide/index.html +588 -0
  509. package/defaults/pages/test/translation/index.md +50 -0
  510. package/defaults/pages/updates/index.md +4 -0
  511. package/defaults/sample-posts/2026-01-12-welcome-to-the-blog.md +26 -0
  512. package/defaults/sample-posts/2026-02-03-a-field-guide-to-good-defaults.md +50 -0
  513. package/defaults/sample-posts/2026-02-24-shipping-week-notes.md +37 -0
  514. package/defaults/sample-posts/2026-03-17-the-case-for-one-config.md +36 -0
  515. package/defaults/sample-posts/2026-04-09-write-release-notes-people-read.md +34 -0
  516. package/defaults/sample-posts/2026-05-21-dark-mode-done-properly.md +38 -0
  517. package/defaults/sample-posts/2026-06-02-the-boring-deploy-is-the-good-deploy.md +30 -0
  518. package/defaults/sample-posts/2026-06-11-naming-things-a-survival-guide.md +30 -0
  519. package/defaults/sample-posts/2026-06-19-your-pricing-page-is-a-product.md +28 -0
  520. package/defaults/sample-posts/2026-06-30-email-is-an-api-with-feelings.md +28 -0
  521. package/defaults/sample-posts/2026-07-08-ship-the-docs-with-the-diff.md +30 -0
  522. package/defaults/sample-team/avery-quinn.md +22 -0
  523. package/defaults/sample-team/jordan-reyes.md +25 -0
  524. package/defaults/sample-team/riley-nakamura.md +22 -0
  525. package/defaults/sample-team/sam-okafor.md +22 -0
  526. package/defaults/sample-updates/v1.0.0.md +28 -0
  527. package/defaults/sample-updates/v1.1.0.md +27 -0
  528. package/defaults/sample-updates/v1.2.0.md +27 -0
  529. package/defaults/sample-updates/v1.3.0.md +28 -0
  530. package/defaults/showcase/components.html +62 -0
  531. package/defaults/showcase/entry.html +76 -0
  532. package/defaults/showcase/frame.html +62 -0
  533. package/defaults/showcase/index.html +59 -0
  534. package/dist/assets.js +1349 -0
  535. package/dist/brand-tokens.js +195 -0
  536. package/dist/build.js +192 -0
  537. package/dist/cachebreak-html.js +71 -0
  538. package/dist/cli-run.js +25 -0
  539. package/dist/cli.js +47 -0
  540. package/dist/collections.js +183 -0
  541. package/dist/commands/audit.js +470 -0
  542. package/dist/commands/build.js +197 -0
  543. package/dist/commands/clean.js +18 -0
  544. package/dist/commands/customize.js +125 -0
  545. package/dist/commands/deploy.js +441 -0
  546. package/dist/commands/dev.js +1067 -0
  547. package/dist/commands/install.js +31 -0
  548. package/dist/commands/lib/deploy-precheck.js +43 -0
  549. package/dist/commands/lib/ensure-target.js +115 -0
  550. package/dist/commands/migrate.js +86 -0
  551. package/dist/commands/purge.js +31 -0
  552. package/dist/commands/test.js +237 -0
  553. package/dist/commands/translate.js +57 -0
  554. package/dist/commands/update.js +22 -0
  555. package/dist/commands/version.js +11 -0
  556. package/dist/config-sections.js +245 -0
  557. package/dist/consumer-scan.js +118 -0
  558. package/dist/consumer.js +49 -0
  559. package/dist/customize.js +328 -0
  560. package/dist/decisions.js +153 -0
  561. package/dist/dist-audit.js +417 -0
  562. package/dist/dynamic-pages.js +357 -0
  563. package/dist/engine.js +1402 -0
  564. package/dist/firebase-auth-helpers.js +198 -0
  565. package/dist/font-metrics.js +420 -0
  566. package/dist/frontmatter-liquid.js +253 -0
  567. package/dist/github-secrets.js +80 -0
  568. package/dist/hero-animations.js +170 -0
  569. package/dist/imagemin.js +261 -0
  570. package/dist/index.js +41 -0
  571. package/dist/inline-icons.js +65 -0
  572. package/dist/language-flags.js +55 -0
  573. package/dist/layers.js +80 -0
  574. package/dist/layouts.js +44 -0
  575. package/dist/limit-collections.js +199 -0
  576. package/dist/link-resolver.js +303 -0
  577. package/dist/markdown-images.js +85 -0
  578. package/dist/merge.js +20 -0
  579. package/dist/migrate/codemod.js +172 -0
  580. package/dist/migrate/config-convert.js +448 -0
  581. package/dist/migrate/consumer-assets.js +130 -0
  582. package/dist/migrate/index.js +223 -0
  583. package/dist/migrate/lint.js +108 -0
  584. package/dist/migrate/rules.js +1929 -0
  585. package/dist/minify-html.js +148 -0
  586. package/dist/mode-helpers.js +96 -0
  587. package/dist/omega-bin.js +7 -0
  588. package/dist/overrides.js +190 -0
  589. package/dist/path-prefix.js +167 -0
  590. package/dist/paths.js +37 -0
  591. package/dist/pricing.js +301 -0
  592. package/dist/purge.js +95 -0
  593. package/dist/redirect-page.js +45 -0
  594. package/dist/sample-content.js +210 -0
  595. package/dist/scaffold.js +123 -0
  596. package/dist/sections.js +810 -0
  597. package/dist/service-worker.js +200 -0
  598. package/dist/social-pages.js +142 -0
  599. package/dist/srcset.js +67 -0
  600. package/dist/static-assets.js +154 -0
  601. package/dist/target-shortlinks.js +123 -0
  602. package/dist/theme-vocabulary.js +76 -0
  603. package/dist/translate/collect-text-nodes.js +93 -0
  604. package/dist/translate/default-routes.js +152 -0
  605. package/dist/translate/generate-defaults.js +297 -0
  606. package/dist/translate/index.js +568 -0
  607. package/dist/translate/packaged-defaults.js +127 -0
  608. package/dist/translate/sitemap.js +108 -0
  609. package/dist/vendor/account/engine.js +182 -0
  610. package/dist/vendor/account/features.js +220 -0
  611. package/dist/vendor/account/index.js +53 -0
  612. package/dist/vendor/account/schema.js +272 -0
  613. package/dist/vendor/account/subscription.js +38 -0
  614. package/dist/vendor/config/company.js +31 -0
  615. package/dist/vendor/config/defaults.js +173 -0
  616. package/dist/vendor/config/demo.js +18 -0
  617. package/dist/vendor/config/desktop-artifacts.js +110 -0
  618. package/dist/vendor/config/edit.js +769 -0
  619. package/dist/vendor/config/env-delivery.js +145 -0
  620. package/dist/vendor/config/env-rules.js +93 -0
  621. package/dist/vendor/config/env-schema.js +1078 -0
  622. package/dist/vendor/config/env.js +445 -0
  623. package/dist/vendor/config/hooks.js +97 -0
  624. package/dist/vendor/config/index.js +237 -0
  625. package/dist/vendor/config/instances.js +208 -0
  626. package/dist/vendor/config/load.js +490 -0
  627. package/dist/vendor/config/merge.js +68 -0
  628. package/dist/vendor/config/order.js +139 -0
  629. package/dist/vendor/config/ports.js +374 -0
  630. package/dist/vendor/config/providers.js +32 -0
  631. package/dist/vendor/config/repo.js +142 -0
  632. package/dist/vendor/config/retired-keys.js +430 -0
  633. package/dist/vendor/config/schema.js +1610 -0
  634. package/dist/vendor/config/secrets.js +50 -0
  635. package/dist/vendor/config/seed.js +34 -0
  636. package/dist/vendor/config/site-global.js +205 -0
  637. package/dist/vendor/config/validate.js +554 -0
  638. package/dist/vendor/config/winback.js +61 -0
  639. package/dist/vendor/devkit/actions-secrets.js +145 -0
  640. package/dist/vendor/devkit/attach-log-file.js +262 -0
  641. package/dist/vendor/devkit/bare-requires.js +153 -0
  642. package/dist/vendor/devkit/bundle.js +292 -0
  643. package/dist/vendor/devkit/ci-workflows.js +520 -0
  644. package/dist/vendor/devkit/cli-router.js +156 -0
  645. package/dist/vendor/devkit/command-path.js +46 -0
  646. package/dist/vendor/devkit/defaults-engine.js +372 -0
  647. package/dist/vendor/devkit/deploy-precheck.js +54 -0
  648. package/dist/vendor/devkit/deploy-record.js +180 -0
  649. package/dist/vendor/devkit/deploy.js +286 -0
  650. package/dist/vendor/devkit/env-watch.js +132 -0
  651. package/dist/vendor/devkit/framework-deps.js +46 -0
  652. package/dist/vendor/devkit/icons.js +178 -0
  653. package/dist/vendor/devkit/license.js +155 -0
  654. package/dist/vendor/devkit/local-https.js +360 -0
  655. package/dist/vendor/devkit/local.js +1905 -0
  656. package/dist/vendor/devkit/logger.js +128 -0
  657. package/dist/vendor/devkit/merge-line-files.js +296 -0
  658. package/dist/vendor/devkit/omega-bin.js +345 -0
  659. package/dist/vendor/devkit/reads.js +274 -0
  660. package/dist/vendor/devkit/safe-install.js +18 -0
  661. package/dist/vendor/devkit/scaffold-guard.js +96 -0
  662. package/dist/vendor/devkit/stop-signals.js +28 -0
  663. package/dist/vendor/devkit/strip-dev-blocks-plugin.js +29 -0
  664. package/dist/vendor/devkit/strip-dev-blocks.js +32 -0
  665. package/dist/vendor/devkit/target-secrets.js +165 -0
  666. package/dist/vendor/devkit/test/scope.js +162 -0
  667. package/dist/vendor/devkit/translate/cache.js +84 -0
  668. package/dist/vendor/devkit/translate/engine.js +243 -0
  669. package/dist/vendor/devkit/translate/index.js +49 -0
  670. package/dist/vendor/devkit/translate/languages.js +134 -0
  671. package/dist/vendor/devkit/translate/providers.js +188 -0
  672. package/dist/vendor/devkit/update.js +569 -0
  673. package/dist/vendor/template-kit/data/language-flags.js +54 -0
  674. package/dist/vendor/template-kit/data/languages.js +195 -0
  675. package/dist/vendor/template-kit/data/social-urls.js +26 -0
  676. package/dist/vendor/template-kit/filters.js +227 -0
  677. package/dist/vendor/template-kit/index.js +28 -0
  678. package/dist/vendor/template-kit/jekyll-compat.js +268 -0
  679. package/dist/vendor/template-kit/register-liquid.js +0 -0
  680. package/dist/vendor/template-kit/tags/collections.js +191 -0
  681. package/dist/vendor/template-kit/tags/conditionals.js +101 -0
  682. package/dist/vendor/template-kit/tags/content.js +175 -0
  683. package/dist/vendor/template-kit/tags/index.js +33 -0
  684. package/dist/vendor/template-kit/tags/media.js +376 -0
  685. package/dist/vendor/template-kit/variable-resolver.js +163 -0
  686. package/docs/ads-system.md +109 -0
  687. package/docs/classy-v2/DIRECTION.md +92 -0
  688. package/docs/classy-v2/draft-1.html +1932 -0
  689. package/docs/classy-v2/draft-2.html +1956 -0
  690. package/docs/frontmatter.md +153 -0
  691. package/docs/index.md +254 -0
  692. package/docs/libs.md +298 -0
  693. package/docs/omega-sections-spec.md +159 -0
  694. package/docs/page-contract.md +112 -0
  695. package/docs/sections.md +845 -0
  696. package/docs/shared/agent-docs.md +89 -0
  697. package/docs/shared/analytics.md +612 -0
  698. package/docs/shared/brands.md +51 -0
  699. package/docs/shared/breaking-changes.md +497 -0
  700. package/docs/shared/config.md +1387 -0
  701. package/docs/shared/deploys.md +215 -0
  702. package/docs/shared/icons.md +201 -0
  703. package/docs/shared/local-dev.md +147 -0
  704. package/docs/shared/logging.md +202 -0
  705. package/docs/shared/monitoring.md +153 -0
  706. package/docs/shared/publishing.md +183 -0
  707. package/docs/shared/rulings.md +34 -0
  708. package/docs/shared/testing.md +147 -0
  709. package/docs/shared/theming.md +604 -0
  710. package/docs/shared/translation.md +291 -0
  711. package/docs/shared/updates.md +61 -0
  712. package/docs/template-kit.md +128 -0
  713. package/docs/test-framework.md +163 -0
  714. package/package.json +122 -0
  715. package/runtime/boot.js +149 -0
  716. package/runtime/icons.js +77 -0
  717. package/runtime/manager.js +36 -0
  718. package/scaffold/.github/workflows/build.yml +139 -0
  719. package/scaffold/.nvmrc +1 -0
  720. package/scaffold/AGENTS.md +55 -0
  721. package/scaffold/CLAUDE.md +1 -0
  722. package/scaffold/_.gitattributes +4 -0
  723. package/scaffold/_.gitignore +27 -0
  724. package/scaffold/config/omega.json5 +20 -0
  725. package/scaffold/src/assets/css/pages/.gitkeep +0 -0
  726. package/scaffold/src/assets/images/.gitkeep +0 -0
  727. package/scaffold/src/assets/js/pages/.gitkeep +0 -0
  728. package/scaffold/src/pages/.gitkeep +0 -0
  729. package/scaffold/src/pages/example.md.txt +94 -0
  730. package/scaffold/src/service-worker.js +16 -0
  731. package/sw/entry.js +19 -0
  732. package/sw/manager.js +281 -0
  733. package/themes/_template/README.md +115 -0
  734. package/themes/_template/_config.scss +60 -0
  735. package/themes/_template/_theme.js +14 -0
  736. package/themes/_template/_theme.scss +28 -0
  737. package/themes/_template/css/base/_root.scss +19 -0
  738. package/themes/_template/css/components/_components.scss +23 -0
  739. package/themes/base/README.md +5 -0
  740. package/themes/base/_components/data/org-chart/component.html +33 -0
  741. package/themes/base/_components/data/org-chart/component.json5 +33 -0
  742. package/themes/base/_components/data/org-chart/component.scss +183 -0
  743. package/themes/base/_components/heading/masthead/component.html +22 -0
  744. package/themes/base/_components/heading/masthead/component.json5 +30 -0
  745. package/themes/base/_components/heading/section-head/component.html +18 -0
  746. package/themes/base/_components/heading/section-head/component.json5 +27 -0
  747. package/themes/base/_components/pricing/features/component.html +29 -0
  748. package/themes/base/_components/pricing/features/component.json5 +30 -0
  749. package/themes/base/_hero/orbit/index.html +30 -0
  750. package/themes/base/_hero/orbit/script.js +33 -0
  751. package/themes/base/_hero/orbit/style.scss +133 -0
  752. package/themes/base/_includes/admin/sections/sidebar.html +3 -0
  753. package/themes/base/_includes/admin/sections/topbar.html +3 -0
  754. package/themes/base/_includes/backend/sections/sidebar.html +3 -0
  755. package/themes/base/_includes/backend/sections/topbar.html +3 -0
  756. package/themes/base/_includes/frontend/components/connection-card.html +49 -0
  757. package/themes/base/_includes/frontend/components/post-card.html +54 -0
  758. package/themes/base/_includes/frontend/sections/account-connections.html +42 -0
  759. package/themes/base/_includes/frontend/sections/account-section-header.html +11 -0
  760. package/themes/base/_includes/frontend/sections/footer.html +154 -0
  761. package/themes/base/_includes/frontend/sections/nav.html +276 -0
  762. package/themes/base/_includes/global/sections/account.html +72 -0
  763. package/themes/base/_includes/global/sections/app-sidebar.html +248 -0
  764. package/themes/base/_includes/global/sections/app-topbar.html +228 -0
  765. package/themes/base/_includes/global/sections/page-header.html +99 -0
  766. package/themes/base/_layouts/admin/core/minimal-viewport-locked.html +22 -0
  767. package/themes/base/_layouts/admin/core/minimal.html +23 -0
  768. package/themes/base/_layouts/backend/core/base.html +57 -0
  769. package/themes/base/_layouts/backend/core/minimal-viewport-locked.html +32 -0
  770. package/themes/base/_layouts/backend/core/minimal.html +32 -0
  771. package/themes/base/_layouts/backend/pages/dashboard/index.html +230 -0
  772. package/themes/base/_layouts/frontend/core/base.html +31 -0
  773. package/themes/base/_layouts/frontend/core/cover.html +40 -0
  774. package/themes/base/_layouts/frontend/core/minimal.html +47 -0
  775. package/themes/base/_layouts/frontend/pages/404.html +54 -0
  776. package/themes/base/_layouts/frontend/pages/about.html +144 -0
  777. package/themes/base/_layouts/frontend/pages/account/index.html +2104 -0
  778. package/themes/base/_layouts/frontend/pages/alternatives/alternative.html +319 -0
  779. package/themes/base/_layouts/frontend/pages/alternatives/index.html +186 -0
  780. package/themes/base/_layouts/frontend/pages/app.html +142 -0
  781. package/themes/base/_layouts/frontend/pages/auth/reset.html +53 -0
  782. package/themes/base/_layouts/frontend/pages/auth/signin.html +90 -0
  783. package/themes/base/_layouts/frontend/pages/auth/signup.html +141 -0
  784. package/themes/base/_layouts/frontend/pages/auth/token.html +44 -0
  785. package/themes/base/_layouts/frontend/pages/blog/categories/category.html +88 -0
  786. package/themes/base/_layouts/frontend/pages/blog/categories/index.html +88 -0
  787. package/themes/base/_layouts/frontend/pages/blog/index.html +255 -0
  788. package/themes/base/_layouts/frontend/pages/blog/post.html +261 -0
  789. package/themes/base/_layouts/frontend/pages/blog/tags/index.html +84 -0
  790. package/themes/base/_layouts/frontend/pages/blog/tags/tag.html +88 -0
  791. package/themes/base/_layouts/frontend/pages/collection/category.html +72 -0
  792. package/themes/base/_layouts/frontend/pages/collection/document.html +54 -0
  793. package/themes/base/_layouts/frontend/pages/collection/index.html +84 -0
  794. package/themes/base/_layouts/frontend/pages/connections/callback.html +55 -0
  795. package/themes/base/_layouts/frontend/pages/contact.html +214 -0
  796. package/themes/base/_layouts/frontend/pages/download.html +793 -0
  797. package/themes/base/_layouts/frontend/pages/extension/index.html +253 -0
  798. package/themes/base/_layouts/frontend/pages/extension/installed.html +198 -0
  799. package/themes/base/_layouts/frontend/pages/feedback.html +171 -0
  800. package/themes/base/_layouts/frontend/pages/index.html +66 -0
  801. package/themes/base/_layouts/frontend/pages/legal/document.html +78 -0
  802. package/themes/base/_layouts/frontend/pages/payment/checkout.html +459 -0
  803. package/themes/base/_layouts/frontend/pages/payment/confirmation.html +131 -0
  804. package/themes/base/_layouts/frontend/pages/portal/email-preferences.html +104 -0
  805. package/themes/base/_layouts/frontend/pages/pricing.html +596 -0
  806. package/themes/base/_layouts/frontend/pages/status.html +361 -0
  807. package/themes/base/_layouts/frontend/pages/team/index.html +210 -0
  808. package/themes/base/_layouts/frontend/pages/team/member.html +125 -0
  809. package/themes/base/_layouts/frontend/pages/updates/index.html +119 -0
  810. package/themes/base/_layouts/frontend/pages/updates/update.html +118 -0
  811. package/themes/base/_sections/about/hero/section.html +68 -0
  812. package/themes/base/_sections/about/hero/section.json5 +58 -0
  813. package/themes/base/_sections/about/letter/section.html +60 -0
  814. package/themes/base/_sections/about/letter/section.json5 +56 -0
  815. package/themes/base/_sections/about/photo-band/section.html +24 -0
  816. package/themes/base/_sections/about/photo-band/section.json5 +23 -0
  817. package/themes/base/_sections/about/principles/section.html +17 -0
  818. package/themes/base/_sections/about/principles/section.json5 +34 -0
  819. package/themes/base/_sections/about/timeline/section.html +20 -0
  820. package/themes/base/_sections/about/timeline/section.json5 +34 -0
  821. package/themes/base/_sections/marketing/bento/section.html +91 -0
  822. package/themes/base/_sections/marketing/bento/section.json5 +152 -0
  823. package/themes/base/_sections/marketing/cta/section.html +44 -0
  824. package/themes/base/_sections/marketing/cta/section.json5 +47 -0
  825. package/themes/base/_sections/marketing/faq/section.html +52 -0
  826. package/themes/base/_sections/marketing/faq/section.json5 +68 -0
  827. package/themes/base/_sections/marketing/hero/section.html +349 -0
  828. package/themes/base/_sections/marketing/hero/section.json5 +335 -0
  829. package/themes/base/_sections/marketing/newsletter-cta/section.html +38 -0
  830. package/themes/base/_sections/marketing/newsletter-cta/section.js +86 -0
  831. package/themes/base/_sections/marketing/newsletter-cta/section.json5 +29 -0
  832. package/themes/base/_sections/marketing/pricing-cards/section.html +115 -0
  833. package/themes/base/_sections/marketing/pricing-cards/section.json5 +72 -0
  834. package/themes/base/_sections/marketing/product-demo/section.html +62 -0
  835. package/themes/base/_sections/marketing/product-demo/section.js +44 -0
  836. package/themes/base/_sections/marketing/product-demo/section.json5 +52 -0
  837. package/themes/base/_sections/marketing/prose/section.html +22 -0
  838. package/themes/base/_sections/marketing/prose/section.json5 +41 -0
  839. package/themes/base/_sections/marketing/showcase/section.html +35 -0
  840. package/themes/base/_sections/marketing/showcase/section.json5 +52 -0
  841. package/themes/base/_sections/marketing/stats/section.html +32 -0
  842. package/themes/base/_sections/marketing/stats/section.json5 +44 -0
  843. package/themes/base/_sections/marketing/testimonials/section.html +54 -0
  844. package/themes/base/_sections/marketing/testimonials/section.json5 +70 -0
  845. package/themes/base/_sections/marketing/trusted-by/section.html +30 -0
  846. package/themes/base/_sections/marketing/trusted-by/section.json5 +38 -0
  847. package/themes/base/_sections/verts/unit/section.html +7 -0
  848. package/themes/base/_sections/verts/unit/section.js +22 -0
  849. package/themes/base/_sections/verts/unit/section.json5 +24 -0
  850. package/themes/base/_sections/verts/unit/section.scss +24 -0
  851. package/themes/base/_theme.js +13 -0
  852. package/themes/base/_theme.scss +4 -0
  853. package/themes/bootstrap/_theme.js +16 -0
  854. package/themes/bootstrap/_theme.scss +11 -0
  855. package/themes/bootstrap/js/index.esm.js +19 -0
  856. package/themes/bootstrap/js/index.umd.js +34 -0
  857. package/themes/bootstrap/js/src/alert.js +87 -0
  858. package/themes/bootstrap/js/src/base-component.js +86 -0
  859. package/themes/bootstrap/js/src/button.js +72 -0
  860. package/themes/bootstrap/js/src/carousel.js +474 -0
  861. package/themes/bootstrap/js/src/collapse.js +297 -0
  862. package/themes/bootstrap/js/src/dom/data.js +55 -0
  863. package/themes/bootstrap/js/src/dom/event-handler.js +317 -0
  864. package/themes/bootstrap/js/src/dom/manipulator.js +71 -0
  865. package/themes/bootstrap/js/src/dom/selector-engine.js +126 -0
  866. package/themes/bootstrap/js/src/dropdown.js +458 -0
  867. package/themes/bootstrap/js/src/modal.js +378 -0
  868. package/themes/bootstrap/js/src/offcanvas.js +282 -0
  869. package/themes/bootstrap/js/src/popover.js +97 -0
  870. package/themes/bootstrap/js/src/scrollspy.js +296 -0
  871. package/themes/bootstrap/js/src/tab.js +315 -0
  872. package/themes/bootstrap/js/src/toast.js +224 -0
  873. package/themes/bootstrap/js/src/tooltip.js +632 -0
  874. package/themes/bootstrap/js/src/util/backdrop.js +151 -0
  875. package/themes/bootstrap/js/src/util/component-functions.js +35 -0
  876. package/themes/bootstrap/js/src/util/config.js +65 -0
  877. package/themes/bootstrap/js/src/util/focustrap.js +115 -0
  878. package/themes/bootstrap/js/src/util/index.js +306 -0
  879. package/themes/bootstrap/js/src/util/sanitizer.js +117 -0
  880. package/themes/bootstrap/js/src/util/scrollbar.js +114 -0
  881. package/themes/bootstrap/js/src/util/swipe.js +146 -0
  882. package/themes/bootstrap/js/src/util/template-factory.js +160 -0
  883. package/themes/bootstrap/overrides/_accordion.scss +21 -0
  884. package/themes/bootstrap/overrides/_avatars.scss +51 -0
  885. package/themes/bootstrap/overrides/_blog-images.scss +45 -0
  886. package/themes/bootstrap/overrides/_buttons-adaptive.scss +141 -0
  887. package/themes/bootstrap/overrides/_color-shades.scss +25 -0
  888. package/themes/bootstrap/overrides/_cursor-utilities.scss +13 -0
  889. package/themes/bootstrap/overrides/_index.scss +30 -0
  890. package/themes/bootstrap/overrides/_links.scss +26 -0
  891. package/themes/bootstrap/overrides/_soft-colors.scss +99 -0
  892. package/themes/bootstrap/overrides/_spacing-extended.scss +74 -0
  893. package/themes/bootstrap/overrides/_spinners.scss +22 -0
  894. package/themes/bootstrap/overrides/_typography-utilities.scss +40 -0
  895. package/themes/bootstrap/scss/_accordion.scss +153 -0
  896. package/themes/bootstrap/scss/_alert.scss +68 -0
  897. package/themes/bootstrap/scss/_badge.scss +38 -0
  898. package/themes/bootstrap/scss/_breadcrumb.scss +40 -0
  899. package/themes/bootstrap/scss/_button-group.scss +147 -0
  900. package/themes/bootstrap/scss/_buttons.scss +216 -0
  901. package/themes/bootstrap/scss/_card.scss +238 -0
  902. package/themes/bootstrap/scss/_carousel.scss +226 -0
  903. package/themes/bootstrap/scss/_close.scss +66 -0
  904. package/themes/bootstrap/scss/_containers.scss +41 -0
  905. package/themes/bootstrap/scss/_dropdown.scss +250 -0
  906. package/themes/bootstrap/scss/_forms.scss +9 -0
  907. package/themes/bootstrap/scss/_functions.scss +302 -0
  908. package/themes/bootstrap/scss/_grid.scss +39 -0
  909. package/themes/bootstrap/scss/_helpers.scss +12 -0
  910. package/themes/bootstrap/scss/_images.scss +42 -0
  911. package/themes/bootstrap/scss/_list-group.scss +199 -0
  912. package/themes/bootstrap/scss/_maps.scss +174 -0
  913. package/themes/bootstrap/scss/_mixins.scss +42 -0
  914. package/themes/bootstrap/scss/_modal.scss +240 -0
  915. package/themes/bootstrap/scss/_nav.scss +197 -0
  916. package/themes/bootstrap/scss/_navbar.scss +289 -0
  917. package/themes/bootstrap/scss/_offcanvas.scss +147 -0
  918. package/themes/bootstrap/scss/_pagination.scss +109 -0
  919. package/themes/bootstrap/scss/_placeholders.scss +51 -0
  920. package/themes/bootstrap/scss/_popover.scss +196 -0
  921. package/themes/bootstrap/scss/_progress.scss +68 -0
  922. package/themes/bootstrap/scss/_reboot.scss +611 -0
  923. package/themes/bootstrap/scss/_root.scss +187 -0
  924. package/themes/bootstrap/scss/_spinners.scss +85 -0
  925. package/themes/bootstrap/scss/_tables.scss +171 -0
  926. package/themes/bootstrap/scss/_toasts.scss +73 -0
  927. package/themes/bootstrap/scss/_tooltip.scss +119 -0
  928. package/themes/bootstrap/scss/_transitions.scss +27 -0
  929. package/themes/bootstrap/scss/_type.scss +106 -0
  930. package/themes/bootstrap/scss/_utilities.scss +806 -0
  931. package/themes/bootstrap/scss/_variables-dark.scss +102 -0
  932. package/themes/bootstrap/scss/_variables.scss +1753 -0
  933. package/themes/bootstrap/scss/bootstrap-grid.scss +62 -0
  934. package/themes/bootstrap/scss/bootstrap-reboot.scss +10 -0
  935. package/themes/bootstrap/scss/bootstrap-utilities.scss +19 -0
  936. package/themes/bootstrap/scss/bootstrap.scss +52 -0
  937. package/themes/bootstrap/scss/forms/_floating-labels.scss +97 -0
  938. package/themes/bootstrap/scss/forms/_form-check.scss +189 -0
  939. package/themes/bootstrap/scss/forms/_form-control.scss +214 -0
  940. package/themes/bootstrap/scss/forms/_form-range.scss +91 -0
  941. package/themes/bootstrap/scss/forms/_form-select.scss +80 -0
  942. package/themes/bootstrap/scss/forms/_form-text.scss +11 -0
  943. package/themes/bootstrap/scss/forms/_input-group.scss +132 -0
  944. package/themes/bootstrap/scss/forms/_labels.scss +36 -0
  945. package/themes/bootstrap/scss/forms/_validation.scss +12 -0
  946. package/themes/bootstrap/scss/helpers/_clearfix.scss +3 -0
  947. package/themes/bootstrap/scss/helpers/_color-bg.scss +7 -0
  948. package/themes/bootstrap/scss/helpers/_colored-links.scss +30 -0
  949. package/themes/bootstrap/scss/helpers/_focus-ring.scss +5 -0
  950. package/themes/bootstrap/scss/helpers/_icon-link.scss +25 -0
  951. package/themes/bootstrap/scss/helpers/_position.scss +36 -0
  952. package/themes/bootstrap/scss/helpers/_ratio.scss +26 -0
  953. package/themes/bootstrap/scss/helpers/_stacks.scss +15 -0
  954. package/themes/bootstrap/scss/helpers/_stretched-link.scss +15 -0
  955. package/themes/bootstrap/scss/helpers/_text-truncation.scss +7 -0
  956. package/themes/bootstrap/scss/helpers/_visually-hidden.scss +8 -0
  957. package/themes/bootstrap/scss/helpers/_vr.scss +8 -0
  958. package/themes/bootstrap/scss/mixins/_alert.scss +18 -0
  959. package/themes/bootstrap/scss/mixins/_backdrop.scss +14 -0
  960. package/themes/bootstrap/scss/mixins/_banner.scss +7 -0
  961. package/themes/bootstrap/scss/mixins/_border-radius.scss +78 -0
  962. package/themes/bootstrap/scss/mixins/_box-shadow.scss +18 -0
  963. package/themes/bootstrap/scss/mixins/_breakpoints.scss +127 -0
  964. package/themes/bootstrap/scss/mixins/_buttons.scss +70 -0
  965. package/themes/bootstrap/scss/mixins/_caret.scss +69 -0
  966. package/themes/bootstrap/scss/mixins/_clearfix.scss +9 -0
  967. package/themes/bootstrap/scss/mixins/_color-mode.scss +21 -0
  968. package/themes/bootstrap/scss/mixins/_color-scheme.scss +7 -0
  969. package/themes/bootstrap/scss/mixins/_container.scss +11 -0
  970. package/themes/bootstrap/scss/mixins/_deprecate.scss +10 -0
  971. package/themes/bootstrap/scss/mixins/_forms.scss +163 -0
  972. package/themes/bootstrap/scss/mixins/_gradients.scss +47 -0
  973. package/themes/bootstrap/scss/mixins/_grid.scss +151 -0
  974. package/themes/bootstrap/scss/mixins/_image.scss +16 -0
  975. package/themes/bootstrap/scss/mixins/_list-group.scss +26 -0
  976. package/themes/bootstrap/scss/mixins/_lists.scss +7 -0
  977. package/themes/bootstrap/scss/mixins/_pagination.scss +10 -0
  978. package/themes/bootstrap/scss/mixins/_reset-text.scss +17 -0
  979. package/themes/bootstrap/scss/mixins/_resize.scss +6 -0
  980. package/themes/bootstrap/scss/mixins/_table-variants.scss +24 -0
  981. package/themes/bootstrap/scss/mixins/_text-truncate.scss +8 -0
  982. package/themes/bootstrap/scss/mixins/_transition.scss +26 -0
  983. package/themes/bootstrap/scss/mixins/_utilities.scss +97 -0
  984. package/themes/bootstrap/scss/mixins/_visually-hidden.scss +38 -0
  985. package/themes/bootstrap/scss/tests/jasmine.js +16 -0
  986. package/themes/bootstrap/scss/tests/mixins/_auto-import-of-variables-dark.test.scss +7 -0
  987. package/themes/bootstrap/scss/tests/mixins/_color-modes.test.scss +69 -0
  988. package/themes/bootstrap/scss/tests/mixins/_media-query-color-mode-full.test.scss +8 -0
  989. package/themes/bootstrap/scss/tests/mixins/_utilities.test.scss +393 -0
  990. package/themes/bootstrap/scss/tests/sass-true/register.js +14 -0
  991. package/themes/bootstrap/scss/tests/sass-true/runner.js +17 -0
  992. package/themes/bootstrap/scss/tests/utilities/_api.test.scss +75 -0
  993. package/themes/bootstrap/scss/utilities/_api.scss +47 -0
  994. package/themes/bootstrap/scss/vendor/_rfs.scss +348 -0
  995. package/themes/classy/README.md +89 -0
  996. package/themes/classy/_config.scss +245 -0
  997. package/themes/classy/_theme.js +31 -0
  998. package/themes/classy/_theme.scss +57 -0
  999. package/themes/classy/css/app/_panels.scss +183 -0
  1000. package/themes/classy/css/base/_fonts.scss +79 -0
  1001. package/themes/classy/css/base/_no-raise.scss +49 -0
  1002. package/themes/classy/css/base/_root.scss +113 -0
  1003. package/themes/classy/css/base/_typography.scss +154 -0
  1004. package/themes/classy/css/base/_utilities.scss +284 -0
  1005. package/themes/classy/css/components/_alerts.scss +51 -0
  1006. package/themes/classy/css/components/_badges.scss +46 -0
  1007. package/themes/classy/css/components/_buttons.scss +175 -0
  1008. package/themes/classy/css/components/_cards.scss +41 -0
  1009. package/themes/classy/css/components/_dropdowns.scss +48 -0
  1010. package/themes/classy/css/components/_forms.scss +171 -0
  1011. package/themes/classy/css/components/_receipt.scss +45 -0
  1012. package/themes/classy/css/layout/_footer.scss +298 -0
  1013. package/themes/classy/css/layout/_general.scss +209 -0
  1014. package/themes/classy/css/layout/_nav.scss +154 -0
  1015. package/themes/classy/css/layout/_shell.scss +458 -0
  1016. package/themes/classy/css/marketing/_bento.scss +238 -0
  1017. package/themes/classy/css/marketing/_content.scss +1328 -0
  1018. package/themes/classy/css/marketing/_downloads.scss +246 -0
  1019. package/themes/classy/css/marketing/_hero.scss +311 -0
  1020. package/themes/classy/css/marketing/_sections.scss +649 -0
  1021. package/themes/classy/css/pages/404/index.scss +67 -0
  1022. package/themes/classy/css/pages/_auth.scss +235 -0
  1023. package/themes/classy/css/pages/admin/firebase/index.scss +22 -0
  1024. package/themes/classy/css/pages/blog/[slug].scss +56 -0
  1025. package/themes/classy/css/pages/cookies.scss +2 -0
  1026. package/themes/classy/css/pages/dashboard/account/index.scss +31 -0
  1027. package/themes/classy/css/pages/feedback/index.scss +39 -0
  1028. package/themes/classy/css/pages/legal/_document.scss +192 -0
  1029. package/themes/classy/css/pages/payment/checkout/index.scss +585 -0
  1030. package/themes/classy/css/pages/payment/confirmation/index.scss +9 -0
  1031. package/themes/classy/css/pages/privacy.scss +2 -0
  1032. package/themes/classy/css/pages/status/index.scss +164 -0
  1033. package/themes/classy/css/pages/terms.scss +2 -0
  1034. package/themes/classy/css/pages/updates/index.scss +128 -0
  1035. package/themes/classy/fonts/inter-italic-latin-ext.woff2 +0 -0
  1036. package/themes/classy/fonts/inter-italic-latin.woff2 +0 -0
  1037. package/themes/classy/fonts/inter-normal-latin-ext.woff2 +0 -0
  1038. package/themes/classy/fonts/inter-normal-latin.woff2 +0 -0
  1039. package/themes/classy/fonts/newsreader-italic-latin-ext.woff2 +0 -0
  1040. package/themes/classy/fonts/newsreader-italic-latin.woff2 +0 -0
  1041. package/themes/classy/fonts/newsreader-normal-latin-ext.woff2 +0 -0
  1042. package/themes/classy/fonts/newsreader-normal-latin.woff2 +0 -0
  1043. package/themes/classy/js/hero-demo-form.js +56 -0
  1044. package/themes/neobrutalism/README.md +118 -0
  1045. package/themes/neobrutalism/_config.scss +139 -0
  1046. package/themes/neobrutalism/_layouts/frontend/core/base.html +30 -0
  1047. package/themes/neobrutalism/_layouts/frontend/pages/index.html +275 -0
  1048. package/themes/neobrutalism/_theme.js +24 -0
  1049. package/themes/neobrutalism/_theme.scss +61 -0
  1050. package/themes/neobrutalism/css/base/_mixins.scss +46 -0
  1051. package/themes/neobrutalism/css/base/_root.scss +169 -0
  1052. package/themes/neobrutalism/css/base/_typography.scss +61 -0
  1053. package/themes/neobrutalism/css/base/_utilities.scss +89 -0
  1054. package/themes/neobrutalism/css/components/_billing-toggle.scss +74 -0
  1055. package/themes/neobrutalism/css/components/_buttons.scss +148 -0
  1056. package/themes/neobrutalism/css/components/_cards.scss +47 -0
  1057. package/themes/neobrutalism/css/components/_forms.scss +88 -0
  1058. package/themes/neobrutalism/css/components/_marketing.scss +244 -0
  1059. package/themes/neobrutalism/css/layout/_general.scss +213 -0
  1060. package/themes/neobrutalism/css/layout/_navigation.scss +171 -0
  1061. package/themes/neobrutalism/css/pages/index.scss +203 -0
  1062. package/themes/neobrutalism/css/pages/pricing/index.scss +58 -0
  1063. package/themes/neobrutalism/css/pages/test/libraries/layers/index.scss +7 -0
  1064. package/themes/neobrutalism/js/pages/test/libraries/layers/index.js +9 -0
  1065. package/themes/newsflash/README.md +117 -0
  1066. package/themes/newsflash/_components/heading/lede/component.html +14 -0
  1067. package/themes/newsflash/_components/heading/lede/component.json5 +23 -0
  1068. package/themes/newsflash/_components/heading/rule-head/component.html +17 -0
  1069. package/themes/newsflash/_components/heading/rule-head/component.json5 +23 -0
  1070. package/themes/newsflash/_components/news/byline/component.html +12 -0
  1071. package/themes/newsflash/_components/news/byline/component.json5 +18 -0
  1072. package/themes/newsflash/_components/news/story-card/component.html +25 -0
  1073. package/themes/newsflash/_components/news/story-card/component.json5 +21 -0
  1074. package/themes/newsflash/_config.scss +147 -0
  1075. package/themes/newsflash/_layouts/frontend/core/base.html +51 -0
  1076. package/themes/newsflash/_layouts/frontend/pages/blog/index.html +299 -0
  1077. package/themes/newsflash/_layouts/frontend/pages/blog/post.html +235 -0
  1078. package/themes/newsflash/_layouts/frontend/pages/index.html +328 -0
  1079. package/themes/newsflash/_sections/marketing/desks/section.html +39 -0
  1080. package/themes/newsflash/_sections/marketing/desks/section.json5 +49 -0
  1081. package/themes/newsflash/_sections/marketing/newsletter-cta/section.html +103 -0
  1082. package/themes/newsflash/_sections/marketing/newsletter-cta/section.json5 +50 -0
  1083. package/themes/newsflash/_sections/marketing/rundown/section.html +28 -0
  1084. package/themes/newsflash/_sections/marketing/rundown/section.json5 +40 -0
  1085. package/themes/newsflash/_theme.js +24 -0
  1086. package/themes/newsflash/_theme.scss +70 -0
  1087. package/themes/newsflash/css/base/_fonts.scss +80 -0
  1088. package/themes/newsflash/css/base/_mixins.scss +51 -0
  1089. package/themes/newsflash/css/base/_root.scss +257 -0
  1090. package/themes/newsflash/css/base/_typography.scss +49 -0
  1091. package/themes/newsflash/css/base/_utilities.scss +122 -0
  1092. package/themes/newsflash/css/components/_badges.scss +65 -0
  1093. package/themes/newsflash/css/components/_billing-toggle.scss +61 -0
  1094. package/themes/newsflash/css/components/_buttons.scss +139 -0
  1095. package/themes/newsflash/css/components/_cards.scss +47 -0
  1096. package/themes/newsflash/css/components/_editorial.scss +173 -0
  1097. package/themes/newsflash/css/components/_forms.scss +75 -0
  1098. package/themes/newsflash/css/components/_infinite-scroll.scss +102 -0
  1099. package/themes/newsflash/css/components/_panels.scss +156 -0
  1100. package/themes/newsflash/css/components/_ticker.scss +67 -0
  1101. package/themes/newsflash/css/layout/_general.scss +330 -0
  1102. package/themes/newsflash/css/layout/_navigation.scss +168 -0
  1103. package/themes/newsflash/css/pages/404/index.scss +79 -0
  1104. package/themes/newsflash/css/pages/blog/[slug].scss +186 -0
  1105. package/themes/newsflash/css/pages/blog/index.scss +17 -0
  1106. package/themes/newsflash/css/pages/index.scss +157 -0
  1107. package/themes/newsflash/css/pages/pricing/index.scss +55 -0
  1108. package/themes/newsflash/css/pages/test/libraries/layers/index.scss +7 -0
  1109. package/themes/newsflash/fonts/fraunces-italic-latin-ext.woff2 +0 -0
  1110. package/themes/newsflash/fonts/fraunces-italic-latin.woff2 +0 -0
  1111. package/themes/newsflash/fonts/fraunces-normal-latin-ext.woff2 +0 -0
  1112. package/themes/newsflash/fonts/fraunces-normal-latin.woff2 +0 -0
  1113. package/themes/newsflash/fonts/schibsted-grotesk-italic-latin-ext.woff2 +0 -0
  1114. package/themes/newsflash/fonts/schibsted-grotesk-italic-latin.woff2 +0 -0
  1115. package/themes/newsflash/fonts/schibsted-grotesk-normal-latin-ext.woff2 +0 -0
  1116. package/themes/newsflash/fonts/schibsted-grotesk-normal-latin.woff2 +0 -0
  1117. package/themes/newsflash/js/pages/blog/[slug].js +29 -0
  1118. package/themes/newsflash/js/pages/test/libraries/layers/index.js +9 -0
  1119. package/translations/es/pages/404.json +86 -0
  1120. package/translations/es/pages/account.json +36 -0
  1121. package/translations/es/pages/app.json +44 -0
  1122. package/translations/es/pages/cancel.json +36 -0
  1123. package/translations/es/pages/change-password.json +36 -0
  1124. package/translations/es/pages/connections/callback.json +41 -0
  1125. package/translations/es/pages/dashboard/account.json +475 -0
  1126. package/translations/es/pages/dashboard.json +36 -0
  1127. package/translations/es/pages/forgot.json +36 -0
  1128. package/translations/es/pages/join.json +36 -0
  1129. package/translations/es/pages/login.json +36 -0
  1130. package/translations/es/pages/payment/checkout.json +83 -0
  1131. package/translations/es/pages/payment/confirmation.json +62 -0
  1132. package/translations/es/pages/portal/email-preferences.json +46 -0
  1133. package/translations/es/pages/recover.json +36 -0
  1134. package/translations/es/pages/refund.json +36 -0
  1135. package/translations/es/pages/register.json +36 -0
  1136. package/translations/es/pages/reset-password.json +36 -0
  1137. package/translations/es/pages/reset.json +42 -0
  1138. package/translations/es/pages/signin.json +45 -0
  1139. package/translations/es/pages/signup.json +60 -0
  1140. package/translations/es/pages/token.json +39 -0
  1141. package/translations/fa/pages/404.json +86 -0
  1142. package/translations/fa/pages/account.json +36 -0
  1143. package/translations/fa/pages/app.json +44 -0
  1144. package/translations/fa/pages/cancel.json +36 -0
  1145. package/translations/fa/pages/change-password.json +36 -0
  1146. package/translations/fa/pages/connections/callback.json +41 -0
  1147. package/translations/fa/pages/dashboard/account.json +475 -0
  1148. package/translations/fa/pages/dashboard.json +36 -0
  1149. package/translations/fa/pages/forgot.json +36 -0
  1150. package/translations/fa/pages/join.json +36 -0
  1151. package/translations/fa/pages/login.json +36 -0
  1152. package/translations/fa/pages/payment/checkout.json +83 -0
  1153. package/translations/fa/pages/payment/confirmation.json +62 -0
  1154. package/translations/fa/pages/portal/email-preferences.json +46 -0
  1155. package/translations/fa/pages/recover.json +36 -0
  1156. package/translations/fa/pages/refund.json +36 -0
  1157. package/translations/fa/pages/register.json +36 -0
  1158. package/translations/fa/pages/reset-password.json +36 -0
  1159. package/translations/fa/pages/reset.json +42 -0
  1160. package/translations/fa/pages/signin.json +45 -0
  1161. package/translations/fa/pages/signup.json +60 -0
  1162. package/translations/fa/pages/token.json +39 -0
package/docs/index.md ADDED
@@ -0,0 +1,254 @@
1
+ # OMEGA Web (@omega.js/web)
2
+
3
+ > **Note for contributors and Claude:** This file is the guide for `@omega.js/web` — identity, the module map, the command surface, and pointers to the deep references. It lives in the monorepo's `docs/` tree and is loaded on demand (the omega Claude plugin's hooks inject it by context; the repo-root AGENTS.md map is the one agent entry — packages carry no agent docs). The **meat** lives in the shared docs ([shared/](shared/)), the web deep references ([sections.md](sections.md), [frontmatter.md](frontmatter.md), [omega-sections-spec.md](omega-sections-spec.md), [template-kit.md](template-kit.md), [libs.md](libs.md), [ads-system.md](ads-system.md)), and [README.md](../README.md), which carries the long-form module/feature detail. Write new detail there and cross-link from here — do NOT inline it.
4
+
5
+ > **Mirrored structure:** the four framework guides — `docs/web/index.md`, `docs/backend/index.md`, `docs/extension/index.md`, and `docs/desktop/index.md` — mirror each other (the legacy UJM/BEM/BXM/EM lineage): shared sections (Supply-Chain Security, Development Workflow, File Conventions, Doc-update parity, etc.) appear in the **same order at the same position** across all four. When adding a section that applies to multiple frameworks, insert it in the same spot in all of them. Each consumer template (`src/defaults/AGENTS.md`; web's lives at `scaffold/AGENTS.md`, beside its one-line `@AGENTS.md` `CLAUDE.md` pointer) mirrors its guide the same way.
6
+
7
+ ## Identity
8
+
9
+ OMEGA Web (`@omega.js/web`) is the framework for building a brand's marketing site + authenticated frontend. It is the **UJM (Jekyll) successor, rebuilt — not ported**: Eleventy 3 + LiquidJS + [`@omega.js/template-kit`](../../packages/template-kit), with layered themes (zero copying), the section/component library, virtual default pages, the esbuild/sass/PurgeCSS asset pipeline, and an ESM boot runtime that hands every page the `@omega.js/client` singleton. It ships the `omega` CLI for the whole consumer lifecycle (dev → build → test → deploy) plus `omega migrate`, the one-command in-place conversion of a legacy UJM consumer.
10
+
11
+ **This repository** is the framework itself. **Consumer projects** are a brand's website target: `config/omega.json5` + a consumer-owned `src/`, everything else delivered from the package as layers. Config comes from `@omega.js/config` (shared sections top-level, web settings under `targets.web`).
12
+
13
+ ## Recommended skills
14
+
15
+ - **`js:patterns`** — JavaScript/Node.js conventions: file structure, JSDoc, defensive coding (`?.` usage), template literals, `package.json` conventions. Auto-loads when creating new `.js` files or touching JS module structure.
16
+ - **`omega:web`** — the router skill from the omega Claude plugin. The inject hook loads it automatically in any project with `@omega.js/web` (and inside `packages/web` here); it points back to this guide + `docs/` (the SSOT).
17
+ - **`omega:seo`** — the search-surface checklist for a page (meta values, the per-type structured-data gates in foot.html, one h1, sitemap/robots, link shape): [agent-plugins/claude/skills/seo/SKILL.md](../../agent-plugins/claude/skills/seo/SKILL.md). The plugin's quality hook fires it on any page, layout, or head/foot-chrome edit.
18
+ - **`omega:accessibility`** — the front-end checklist (landmarks and heading order, alt text, control names, token color, focus visibility, reduced motion): [agent-plugins/claude/skills/accessibility/SKILL.md](../../agent-plugins/claude/skills/accessibility/SKILL.md). Same hook fires it on markup, section, and stylesheet edits.
19
+
20
+ ## 🚨 READ @omega.js/client TOO
21
+
22
+ **`@omega.js/web` ships `@omega.js/client` into every page** (a real runtime dependency, bundled through the boot runtime as a shared chunk so every `import omega from '@omega.js/client'` is the SAME initialized singleton). It powers auth, Firebase, reactive `data-omega-bind` directives, analytics, error tracking, the service-worker registration, and utilities. Any task touching auth flows, Firestore reads/writes, subscription resolution, push notifications, or DOM bindings means you are working with `@omega.js/client` as much as with `@omega.js/web`.
23
+
24
+ **Required reading:** [docs/client/index.md](../client/index.md) and `packages/client/docs/` (Auth, Bindings, Firestore, Notifications, …).
25
+
26
+ ## Quick Start
27
+
28
+ ### For Consuming Projects
29
+
30
+ 1. `npm install @omega.js/web` (in the brand's website target)
31
+ 2. There is no setup step ([#675](https://github.com/Omega-JS-Stack/omega/issues/675)). Every verb below starts by running `ensureTarget()` — scaffold/refresh defaults + sync `package.json` scripts, idempotently, offline, and silently once the target is converged (bare `omega` prints help). It announces the seed mode it detected on a `[setup]` line: **standalone project** (the full `config/omega.json5` template + the per-target agent docs land here) or **brand monorepo** (no local-level config is seeded at all: the brand root's `config/omega.json5` is the target's config, and the agent docs live there too; a local-level file stays the standalone escape hatch you author yourself, and no verb touches one that exists). The scaffolded `src/pages/` carries one file — `example.md.txt`, a commented walkthrough of meta-only frontmatter and `{% section %}` composition. The `.txt` suffix keeps it out of the build; copy it to `<name>.md` to start from it
32
+ 3. `npx omega dev` — Eleventy watch/serve + in-place asset rebuilds. Port 4000 by convention, auto-bumps +1 when taken; `--port=N` or config `ports.website` PINS it. HTTPS by default via mkcert (`--no-https` falls back). Dev pages auto-connect the real Auth/Firestore SDKs to the LOCAL emulators — start them from the brand's backend target (`npx omega emulator`) **before** `omega dev`, because there is no live-Firebase opt-out in dev ([docs/backend/index.md](../backend/index.md)). Without them the site still builds and renders perfectly, and only the data surfaces misbehave — quietly: sign-in and sign-up hang then fail on a connection refused, every Firestore read stays pending so account/subscription state reads as signed-out, and every API request fails the same way (dev resolves the API base to the backend target's local `mgr serve` proxy — `https://localhost:5002` unless a dev port map says otherwise; `?_dev_apiEnvironment=production` is the per-request escape to the live API, and it does nothing for Auth/Firestore, which have no opt-out). A page that "loads fine but nobody can log in" is almost always this. `--local` links every `@omega.js/*` dep from this monorepo first ([docs/shared/local-dev.md](shared/local-dev.md)).
33
+ 4. `npx omega build` — production: assets (hashed, `@dev`-only blocks stripped) → Eleventy → PurgeCSS → Firebase auth helpers (`/__/auth/*`) → `dist/`
34
+ 5. `npx omega test` — PROJECT scope by default (production build + smoke checks + the consumer's `test/`); `framework:`/`omega:`/`web:` run the framework's own suite, `full:` runs both ([docs/shared/testing.md](shared/testing.md)). The smoke checks read the built `dist/`: pages rendered, a themed 404, the main bundle on disk, every internal link resolving ([#430](https://github.com/Omega-JS-Stack/omega/issues/430)), and the four audit checks — page meta (title + description), anchor fragments landing on a real id, `<img alt>`, and sitemap orphans ([#468](https://github.com/Omega-JS-Stack/omega/issues/468)). All of them share one exception file, `config/link-exceptions.json5`, one list per check keyed by the page that owns the finding, where a declaration that has started passing is itself a failure ([docs/test-framework.md](../docs/test-framework.md))
35
+ 6. `npx omega deploy` — THE publish verb: sync, then dispatch the CI build workflow ([docs/shared/deploys.md](shared/deploys.md))
36
+ 7. `npx omega migrate` — convert a UJM consumer in place; `--check` reports everything with zero writes. An already-converted config is not a failure: when the legacy files are gone but an `omega.json5` resolves (the target's own, or the brand root's), the config step validates the found config through the real loader, logs `already converted`, and skips itself while the codemods and legacy-file cleanup still run, so the command exits 0 for scripted pipelines. A project with neither legacy configs nor a resolvable `omega.json5` fails, and so does a found config the loader rejects
37
+
38
+ `npx omega-web <cmd>` runs this CLI directly (no dispatch); `omg` and `mgr` are aliases for `omega`.
39
+
40
+ > **Important:** all `npx omega ...` commands run from the consumer's **target root** (the directory with `package.json` + `config/omega.json5`). `dist/` and `.omega/` are generated — never edit them.
41
+
42
+ ### For Framework Development (This Repository)
43
+
44
+ > **🚫 NEVER use `npx omega ...` from the framework repo.** `npx omega` is for CONSUMER projects. Here, use the `package.json` scripts.
45
+
46
+ 1. `npm install`
47
+ 2. `npm run prepare` — build once: `src/` → `dist/` via prepare-package (copy, no transforms), then the devkit `vendor` hook
48
+ 3. `npm run prepare:watch` (also `npm start`) — watch mode; the root `npm start` runs this for every package at once
49
+ 4. `npm test` — `node --test test/*.test.js` (engine slice, assets/ESM, CLI/scaffold, migrate, ports, sections, themes, translate, …). Fixtures live in `test/fixtures/`, the shared build harness in `test/lib/build.js`.
50
+ 5. Exercise real consumers: `brands/omega-playground` (classy) and `brands/newsflash-brand` (newsflash) are the standing in-repo brands; `brands/sandbox-brand` is the synthetic fixture the automated corpus mangles and resets ([docs/shared/brands.md](shared/brands.md)).
51
+
52
+ ## Architecture
53
+
54
+ Long-form detail (packaged content, URL shape, service worker, design tokens, theming tiers, app shell, pricing, responsive images, engine facts) lives in [README.md](../README.md). This is the map.
55
+
56
+ ### Module map (`src/`)
57
+
58
+ | Module | Owns |
59
+ |---|---|
60
+ | [engine.js](../src/engine.js) | `configureOmega()` — turns an Eleventy instance into the OMEGA engine: Liquid options + template-kit registration, layered layouts, the frontmatter preprocessor, `resolved`/`paginator`/`pageAssets` computed data, site collections, default pages, globals |
61
+ | [layers.js](../src/layers.js) / [layouts.js](../src/layouts.js) | First-layer-wins file resolution across the layer chain; layered layout delivery with zero copying (virtual templates in build, a symlink farm in dev) |
62
+ | [sections.js](../src/sections.js) | The section/component library: the `{% section %}` / `{% component %}` tags, json5 schemas + defaults, the `{% composition %}` page-body guard, `buildSectionLibrary()`, `collectSectionAssets()` |
63
+ | [assets.js](../src/assets.js) | Page/layout modules + main bundle over the layer roots, bundled through [`@omega.js/devkit`'s `bundle`](../devkit/index.md) — the ONE esbuild wrapper ([#736](https://github.com/Omega-JS-Stack/omega/issues/736)), which composes the framework-deps resolve hook, the production `@dev-only` strip, the minify/sourcemap rules and the per-build timing line, leaving web only its boot-stub plugin and its entry shape. Plus layered sass (`omega:` importer, in both languages), the layered page/layout css lanes, font copy, PurgeCSS post-pass, and the critical-css extraction the head inlines ([#750](https://github.com/Omega-JS-Stack/omega/issues/750)) |
64
+ | [build.js](../src/build.js) | `buildSite()` — assets → service worker/meta → static → imagemin → Eleventy → PurgeCSS, with per-phase timings (what `omega build` runs) |
65
+ | [collections.js](../src/collections.js) | posts / alternatives / team / updates collections + taxonomy aggregation, and the same registration for the brand's own collections |
66
+ | [dynamic-pages.js](../src/dynamic-pages.js) | Dynamic pages ([#207](https://github.com/Omega-JS-Stack/omega/issues/207)): the listing + category pages a declared collection gets for free (see Key contracts below) |
67
+ | [limit-collections.js](../src/limit-collections.js) | Dev-mode collection sampling ([#190](https://github.com/Omega-JS-Stack/omega/issues/190)): `targets.web.dev.limitCollections` ({ posts: 50, randomize: true }) drops everything outside the sample before it renders — development builds only, never production |
68
+ | [markdown-images.js](../src/markdown-images.js) | Markdown images render optimized ([#193](https://github.com/Omega-JS-Stack/omega/issues/193)): `![alt](src)` goes through the `omega_image` builder (responsive picture + lazy placeholder; plain lazy img for external/non-raster). Post authors get the `@post/<file>` shorthand for the post's own image dir — off a post it fails the build. Feeds embed the lazy markup (readers see the placeholder), legacy parity |
69
+ | [frontmatter-liquid.js](../src/frontmatter-liquid.js) | Liquid inside frontmatter VALUES — the lane a value takes is decided by its variable ROOTS (cached site-scope renders; page-scoped values defer to a copy-on-write pass; everything else renders against the template's own cascade; the context-free `omega_*` filters are registered, since LiquidJS drops an unknown filter silently) |
70
+ | [consumer.js](../src/consumer.js) / [consumer-scan.js](../src/consumer-scan.js) | Consumer layout (`src/`, `dist/`, `.omega/`) + omega.json5 → site data; permalink scan → default-page suppression |
71
+ | [pricing.js](../src/pricing.js) / [brand-tokens.js](../src/brand-tokens.js) | `site.pricing` composed from `payment.products` × the top-level `features` catalog ([#647](https://github.com/Omega-JS-Stack/omega/issues/647) — the catalog supplies row order, name, icon and definition; a product names only its VALUE); the accent ramp derived from `brand.color` |
72
+ | [imagemin.js](../src/imagemin.js) / [minify-html.js](../src/minify-html.js) / [static-assets.js](../src/static-assets.js) / [cachebreak-html.js](../src/cachebreak-html.js) | Production output passes (the `@dev-only` strip moved to devkit's esbuild plugin, [#736](https://github.com/Omega-JS-Stack/omega/issues/736)) |
73
+ | [service-worker.js](../src/service-worker.js) | `buildServiceWorker()` + `writeBuildMeta()` — the build manifest at `/build.js` + `/build.json` (brand, environment, version, theme, package versions, repo, commit, assets, cacheBreaker), which `@omega.js/client`'s version check polls and the `/status` page displays ([#13](https://github.com/Omega-JS-Stack/omega/issues/13)) |
74
+ | [firebase-auth-helpers.js](../src/firebase-auth-helpers.js) | Self-hosted `/__/auth/*` helper files (authDomain = the brand host), write-through cached per project |
75
+ | [translate/](../src/translate) | The AI translation pass over `dist/` with the committed cache, plus the default-page translations the framework ships in `translations/` — its own plumbing pages never reach a provider and still get their `/{lang}/` copies ([docs/shared/translation.md](shared/translation.md)) |
76
+ | [purge.js](../src/purge.js) | Cloudflare cache purge (`omega purge`, auto after a direct deploy) |
77
+ | [customize.js](../src/customize.js) / [overrides.js](../src/overrides.js) | `omega customize <url>` — materialize a default page into `src/pages/`; the layered override map + single-file materialize (`--list`, `omega customize <path>`), built from the same layer chains the build resolves through |
78
+ | [scaffold.js](../src/scaffold.js) | `scaffoldDefaults()` — the devkit defaults engine over `scaffold/` + the web FILE_MAP |
79
+ | [migrate/](../src/migrate) | `runMigration()` — config conversion, the codemod rule tables (templates, consumer JS, and section `.json` descriptors each get their own walk, [#248](https://github.com/Omega-JS-Stack/omega/issues/248); the renamed section args `hero.tagline` → `hero.badge.text`, `cta.description` → `cta.subheadline`, `cta.button` → `cta.primary_button` ride the same table, [#545](https://github.com/Omega-JS-Stack/omega/issues/545), and a `hero.secondary_button` block with no `enabled` key gains `enabled: true` with a warning — `marketing/hero` defaults that button OFF and a UJM page opted in by writing the block, [#579](https://github.com/Omega-JS-Stack/omega/issues/579); the `page.<key>` rewrite is loop-scope aware and reports what it skipped instead of repointing a read at the loop item, [#541](https://github.com/Omega-JS-Stack/omega/issues/541); the UJM library accessor `webManager.uj()`/`omega.uj()` becomes `omega.library()` and any surviving `.uj(` is an error finding — the client has no `uj()`, [#560](https://github.com/Omega-JS-Stack/omega/issues/560); the retired `{% include modules/adunits/adsense.html … %}` becomes `{% section "verts/unit", type: …, size: … %}` (`vert-size` → `size`), and every other `adunits/*` include is an error finding, [#559](https://github.com/Omega-JS-Stack/omega/issues/559); a class-only Kramdown attribute list on a link, `[text](url){: .a .b }`, becomes `<a href="url" class="a b">text</a>` in MARKDOWN files only, and any other surviving `{: … }` is an error finding — markdown-it reads no IAL and prints them, and the engine loads no attrs plugin, [#565](https://github.com/Omega-JS-Stack/omega/issues/565); `authorizedFetch(…)` becomes `omega.request(…)` carrying the two shapes the rename alone left dead — `response: 'json'` dropped (the client parses by content type and RETURNS the body) and `err.status` → `err.code` INSIDE the catch block that binds the error, with a non-json `response`, options behind an identifier and any other `.status` named at their own line, [#594](https://github.com/Omega-JS-Stack/omega/issues/594); a bare `random_id` read gets the `{% assign %}` UJM handed every page for free, [#595](https://github.com/Omega-JS-Stack/omega/issues/595)), the legacy Jekyll `collections:` block DROPPED with the manual step named per collection — mapping and array spellings alike, because a Jekyll collection carries no grouping `field` and `targets.web.collections` requires one ([#589](https://github.com/Omega-JS-Stack/omega/issues/589)), the dependency-resolution report (every bare `require()` under `src/**` the consumer's own package.json does not declare, named file:line with the fix and never installed — the legacy FLAT install answered them and an OMEGA install answers only by hoisting; the framework's own bundler aliases are never named, because a consumer must NOT install `@omega.js/client`, [#600](https://github.com/Omega-JS-Stack/omega/issues/600)), the legacy-test warning (`migrate --check` names UJM harness files `omega test` would count as a false pass 0, detected by shape not filename), liquid-lint (known names from the real registration path — template-kit's filters/tags AND the framework's `{% section %}` family, [#489](https://github.com/Omega-JS-Stack/omega/issues/489)), consumer-asset fixes, legacy-file removal |
80
+ | [cli.js](../src/cli.js) + [commands/](../src/commands) | The CLI: `bin/omega` → the devkit `omega-bin` dispatcher → `cli-run.js` → `cli.js` → `commands/<name>.js` (`bin/omega-web` skips the dispatch and runs `cli-run.js` directly) |
81
+ | [mode-helpers.js](../src/mode-helpers.js) | The environment surface ([#717](https://github.com/Omega-JS-Stack/omega/issues/717)): `getEnvironment()` + `isDevelopment()` / `isProduction()` / `isTesting()`, mixed into the CLI class via `attachTo()` and read by the build lanes (see Key contracts below) |
82
+ | [paths.js](../src/paths.js) | Packaged content locations + `resolveClientEntry()` |
83
+
84
+ ### Packaged content (shipped, not in `src/`)
85
+
86
+ `themes/` (`base` — the structural `omega-*` markup layer every theme chain terminates at; `classy` — the flagship v2 skin, the default; `newsflash` + `neobrutalism` — skins with bounded identity forks, falling back to base per file; `bootstrap` — the vendored Bootstrap source; `_template` — the theme starter) · `core/` (the theme-agnostic layer: blueprint layouts, the document shell, head/body/foot chrome, the `--omega-*` token sheet, `.omega-shell`, core js) · `defaults/` (~60 default pages at their real URLs, the meta-files, and the dev-only sample content corpus) · `runtime/` (the browser ESM boot: `boot.js` bootMain/bootPage/bootLayout handshake, `manager.js` frontend Manager) · `sw/` (the service worker) · `scaffold/` (what `ensureTarget()` writes on every verb).
87
+
88
+ ### Key contracts
89
+
90
+ - **Layered resolution, everywhere.** Consumer files → active theme → base → core, first-layer-wins per relative path — for layouts, includes, sections, css, and js. A consumer overrides by owning the same path; everything it does not own keeps flowing from the framework.
91
+ - **Sections are the content model.** Pages are compositions of `{% section %}` / `{% component %}` calls; each entry is a folder owning markup + scss + js + json5 schema. Full contract: [docs/web/sections.md](sections.md); design + sequencing: [docs/web/omega-sections-spec.md](omega-sections-spec.md).
92
+ - **A page's JS and CSS bind to its URL — and one `[name]` wildcard file serves a whole page FAMILY** ([#470](https://github.com/Omega-JS-Stack/omega/issues/470)). Nothing in frontmatter points a page at a module (the legacy `asset_path` key is dead, spec §7): `pageAssets` ([engine.js](../src/engine.js)) turns the URL into a key and `resolvePageAsset` ([assets.js](../src/assets.js)) answers it in ONE order — the **exact key** (`js/pages/pricing.js`), then the **per-page-dir spelling** (`js/pages/pricing/index.js`), then a **`[name]` wildcard filename**, the Next.js convention spelled Windows-safe: `js/pages/blog/[slug].js` + `css/pages/blog/[slug].scss` serve every `/blog/<slug>` page, and `js/pages/alternatives/[alternative]/index.js` does the same one directory deeper. A wildcard segment matches exactly ONE URL segment, exact beats wildcard, the most-literal wildcard wins among wildcards (ties break lexicographically), and a trailing `/index` on a wildcard key is the per-page-dir spelling, never a consumed segment. Underscore basenames under `pages/` are shared partials in both languages, never entries (`js/pages/legal/_document.js` backs the flat `terms`/`cookies`/`privacy` entries). **Reach for a wildcard whenever more than one URL shares a module — a generated family, a collection, a set of look-alike pages — and keep the re-export ESCAPE HATCH (a `js/pages/<this page>/index.js` that does `export { default } from '../<other page>/index.js';` plus a `css/pages/<this page>/index.scss` that `@use`s the other sheet) for the one-off page borrowing a single other page's module.** `omega migrate` names both, in that order, wherever it finds an `asset_path` key, and the orphan guard ([#469](https://github.com/Omega-JS-Stack/omega/issues/469)) warns by name when a consumer `js/pages/` file is neither an entry nor imported by one — a legacy 4-segment module (`js/pages/dashboard/agents/edit.js`) is the usual culprit, and a family file is its usual answer. Deep contract: [docs/web/sections.md](sections.md). Pinned by `test/assets.test.js` and `test/slice.test.js`.
93
+ - **ONE rule decides what loads for a page: EVERY layer's file does, in layer order** ([#624](https://github.com/Omega-JS-Stack/omega/issues/624), Ian's ruling — JS and CSS alike, no replace semantics and no extend one-liners to remember). A framework page script always runs, the theme decorates, the consumer adds; a manifest key holds a LIST, and `pageAssets.js` / `pageAssets.css` render one `<script>` / `<link>` per entry (a css entry under the inline budget renders a `<style>` block in the link's place instead, [#767](https://github.com/Omega-JS-Stack/omega/issues/767)) in **core → base theme → active theme → consumer** order, which for module scripts IS execution order and for stylesheets IS the cascade. There is no opt-out: a layer that wants nothing from the layer below writes its own behavior, it does not silence one. Worked examples on the classy chain:
94
+ - `/signin` — core ships `js/pages/signin/index.js` and no theme or consumer file exists, so exactly one script loads. Nothing changed from before.
95
+ - `/pricing` — core ships `js/pages/pricing/index.js` + `css/pages/pricing/index.scss`; a brand that adds `src/assets/css/pages/pricing/index.scss` gets BOTH sheets, its own last, so it restyles by writing the rules it wants rather than by re-copying the framework's.
96
+ - `/blog/<slug>` — core's `js/pages/blog/[slug].js` (in-article verts, reading progress) plus the newsflash theme's `js/pages/blog/[slug].js` (its own progress bar) plus a consumer's, all three. Before #624 the theme's file REPLACED core's while both layers' CSS loaded — two rules for one feature, which is the hole this closes.
97
+ - Outputs are content-hashed per layer and named for their owner (`pages/blog/[slug]-HASH.js`, `…[slug].newsflash-HASH.js`, `…[slug].site-HASH.js`), so dev's stable names never collide either.
98
+ - **Assets can key on a LAYOUT as well as a URL** ([#624](https://github.com/Omega-JS-Stack/omega/issues/624)). `js/layouts/<layout>.js` + `css/layouts/<layout>.scss` are keyed by the layout NAME a page writes in frontmatter, resolved through the manifest and layered exactly like page assets — and applied across the page's whole layout CHAIN, outermost first, so a blueprint sitting on another layout still gets that layout's assets. The framework's own user is the redirect layout: `core/js/layouts/modules/utilities/redirect.js` loads on every page rendering `modules/utilities/redirect` (`/account`, `/careers`, every social and target shortlink) and on nothing else. A layout module reaches the boot handshake as its NAMESPACE (`bootLayout`), so its default export is OPTIONAL: chrome that must act before the client is ready runs at import time and exports nothing, and no generated stub ever names a `default` that is not there ([#742](https://github.com/Omega-JS-Stack/omega/issues/742), where esbuild proved that access undefined and warned on every build; `test/assets.test.js` now pins the js lane's warning count at zero). This replaced the `js/modules/` lane — standalone IIFEs at fixed, un-hashed URLs, scripted by hand from a layout — which is DELETED: `js/modules/` builds nothing now, in any layer, and the build says so by name rather than ignoring the directory ([#249](https://github.com/Omega-JS-Stack/omega/issues/249)).
99
+ - **The site-wide bundle is the one REPLACE-with-extend lane, and JS spells the extend exactly as CSS does.** `js/main.js` and `css/main.scss` resolve first-layer-wins, so a consumer's own file takes over — and reaches the framework's through the same `omega:` name on both sides: `import coreMain from 'omega:main'` (esbuild) and `@use 'omega:main' with (…)` (sass) each resolve `main` from the layers BELOW the importing file. The three customization recipes, end to end:
100
+ - **Site-wide** — `src/assets/css/main.scss` with `@use 'omega:main' with ($primary: …)`, and/or `src/assets/js/main.js` with `import coreMain from 'omega:main'; export default async (context) => { await coreMain(context); /* your boot code */ };`. Skipping the extend line is how you replace the framework bundle outright, deliberately.
101
+ - **Page-specific** — write `src/assets/js/pages/<key>.js` / `src/assets/css/pages/<key>.scss` at the URL's key and it loads AFTER the framework's for that URL. Nothing to import, nothing to declare.
102
+ - **Premade JS** — put shared browser modules in `src/assets/js/libs/` and import them by relative path from any page or main file. That is the consumer twin of the framework's own `core/js/libs/`; both are documented in [docs/web/libs.md](libs.md).
103
+ - **Consumer page frontmatter is META-ONLY.** The allow-list is `meta`, `schema`, `config`, `sitemap`, `redirect`, `templateEngineOverride`, `eleventyExcludeFromCollections` ([engine.js](../src/engine.js) `PAGE_FRONTMATTER_ALLOW`; every key documented in [docs/web/frontmatter.md](frontmatter.md)); plumbing keys (`layout`, `permalink`, `tags`, `pagination`, …) are filtered before the check. Content keys in a page's own frontmatter are STRIPPED from the cascade with a build warning, so sections can never consume them. The two frontmatter mistakes that FAIL the build are the `config:` namespace's two directions (#607): a config section restated bare, and a non-section key under `config:`. Collection entries (`_posts`, `_team`, …) and layouts are exempt from the content-key strip — their frontmatter IS the document. Pinned by `test/frontmatter-guard.test.js`. The page-level lane that DOES exist for a shell layout's band data is the page's sidecar data file, `<page>.11tydata.*` ([#269](https://github.com/Omega-JS-Stack/omega/issues/269)): `resolved` re-applies it with the engine's deepMerge, so a sidecar array REPLACES the layout default instead of concatenating onto it, and object/string keys merge as before ([docs/web/sections.md](sections.md), `test/sidecar-data.test.js`). The EXTENSION does not change the contract ([#543](https://github.com/Omega-JS-Stack/omega/issues/543)): `.11tydata.js` and `.11tydata.cjs` ride the same re-apply as `.11tydata.json`, merged in Eleventy's own order (`.js` → `.cjs` → `.json`); only `.mjs`, which no synchronous read can load, stays on pure cascade behavior.
104
+ - **Frontmatter Liquid renders against the page's own cascade, and `meta:` fails loudly** ([#542](https://github.com/Omega-JS-Stack/omega/issues/542), [#544](https://github.com/Omega-JS-Stack/omega/issues/544) — both found porting a real brand). A frontmatter or sidecar value's variable ROOTS decide its lane ([frontmatter-liquid.js](../src/frontmatter-liquid.js)): `site.*` alone is build-constant and caches by raw source; `page.*`, `resolved.*` (every config ref included, since #607 spells them `resolved.config.*`) and the page's OWN pagination alias defer to the per-page `resolved` pass; anything else — the consumer's `_data` globals, so `{{ brands.size }}` beside a `_data/brands.json` reads the file sitting next to it — renders uncached against the same cascade a template render gets. An alias ref therefore needs no `resolved.` prefix any more (`{{ brand_doc.name }}` works), in the page's head and in the meta files alike. And because a `meta:` value renders ONCE and caches, a meta ref whose ROOT nothing in the cascade defines FAILS the build, naming the file and the ref, instead of caching an empty string onto every page that shares the value; a legit empty optional (`{{ resolved.config.brand.tagline }}` on a brand that sets none) still passes, because only the root is checked. Pinned by `test/data-globals-frontmatter.test.js`, `test/meta-pagination-alias.test.js` and `test/frontmatter-liquid.test.js`.
105
+ - **Markdown renders with the typographer on** ([#547](https://github.com/Omega-JS-Stack/omega/issues/547)). `markdown-it` runs with `typographer: true` as a HARD default on BOTH instances — Eleventy's own (a `.md` body) and the engine's (template-kit's `markdownify`) — so straight quotes, apostrophes, `--` and `...` come out smart. Kramdown did this at build time on every Jekyll site, so without it every brand converted off UJM drifts into straight punctuation on its next build (HARD RULE 5: preserve semantics). There is no consumer knob. Pinned by `test/markdown-typographer.test.js`.
106
+ - **Client-runtime configuration is the `client` key** ([#1](https://github.com/Omega-JS-Stack/omega/issues/1) — it configures `@omega.js/client`; it was `web_manager` through the UJM era and there is no dual-read). It reaches the page through the engine's site composition (`cloud.config` and `payment` compose back into `client.*` at build), the core chrome emits it as the Configuration payload from `resolved.config.client`, and a page or layout may set it under its `config:` block with the layout chain merging underneath (#607). Legacy conversion row: [docs/shared/config.md](shared/config.md).
107
+ - **Three namespaces, and a page overrides config under `config:`** ([#607](https://github.com/Omega-JS-Stack/omega/issues/607) — Ian's ruling, no backwards compatibility). A template reads exactly three things:
108
+ - `resolved.config.*` — the brand's omega.json5 for this target, with THIS page's `config:` block merged over it. Every config key lives here: `resolved.config.brand.name`, `resolved.config.theme.main.class`, `resolved.config.inbound.chat.providers.chatsy.enabled`.
109
+ - `resolved.meta.*` — the page meta walk: page `meta:` → layout `meta` defaults → `brand.name` / `brand.description` (the last step is `core/_includes/core/head.html`'s fallback). `meta` is PAGE machinery, not config: omega.json5 declares no `meta` section at all ([#607 addendum](https://github.com/Omega-JS-Stack/omega/issues/607), Ian 2026-08-26 — meta never exists in two places), and a config still carrying one is a retired-key error. The full key-by-key contract is [docs/web/frontmatter.md](frontmatter.md).
110
+ - `site.*` — BUILD FACTS only: the indexed collections (`site.posts`, `site.team`, …), `site.omega` (cache breaker, version, build date), `site.data`, `site.pricing`, `site.brandTokens`, `site.characters`, `site.license` (the deploy-time license verdict, whose `attribution` gates the footer's omega credit — [docs/shared/publishing.md](shared/publishing.md#the-license-check-320)), and the curated `site.targets` view. Nothing config ever rides it.
111
+
112
+ A collection doc is the Jekyll shape — `id`, `url`, `date`, the document's own frontmatter at the top level, and `content`, its RENDERED body ([#711](https://github.com/Omega-JS-Stack/omega/issues/711)). ONE flattener produces it for both lanes, `site.<name>` and `paginator.posts`, so `{% omega_readtime post.content %}` counts the same words wherever the card is drawn. `content` is lazy and non-enumerable by necessity: the site arrays are captured into the data cascade before collections render, so an eager (or enumerable) read would throw Eleventy's premature-content error on every `resolved` walk. Pinned by `test/site-collection-content.test.js`.
113
+
114
+ What a page's `config:` block looks like, which shapes FAIL THE BUILD, and how far each failure reaches is [docs/web/frontmatter.md](frontmatter.md) — the one home of the key-by-key contract. `omega migrate`'s `config-parent` rule moves a legacy page's bare sections under `config:` (idempotent, one finding per file), and the guard lane (`test/config-namespace-guard.test.js`) fails a framework template that reads config off `site.*` or off the old flat `resolved.<section>` path. Pinned by `test/page-config-overrides.test.js`.
115
+
116
+ READS move with the keys ([#611](https://github.com/Omega-JS-Stack/omega/issues/611)). A `site.<config section>` read does not fail on its own — it renders an EMPTY STRING, on every page carrying one — so the build censuses each template's own source (frontmatter values and body alike) and stops on the first sighting, naming the file, the line, the expression it found and the one that replaces it. `omega migrate`'s `config-reads` rule is the rewrite: `site.<section>` and the old flat `resolved.<section>` both become `resolved.config.<section>`, `site.meta` becomes `resolved.meta`, build facts are untouched, and it runs over section `.json` descriptors too (a `{{ … }}` binding emits the same empty string a template would). Idempotent, one finding per file.
117
+
118
+ Two surfaces the per-template census cannot see ride the SAME rule ([#671](https://github.com/Omega-JS-Stack/omega/issues/671)). The first is the config FILE's own string values — `targets.web.tagline: "Agency - {{ site.brand.name }}"` is a read like any other, rendered empty wherever it lands since #611 — rewritten in the file (never through a re-serialize, which would eat a brand's comments) and previewed by `--check` from the config the run WOULD write, so a legacy root's pre-flight report is the real one. The second is a page's own `meta:` block, whose reads are DROPPED rather than moved: a block that FEEDS the meta walk may not read it (the page shipped the literal `{{ … }}` in its `<title>`), in the block, one-line flow and multi-line flow spellings alike, and absence falls through to the brand default. Every key the drop empties goes with it — nested keys first, up to the `meta:` opener — because a childless key is YAML null and a null REPLACES the default it was meant to fall through to. Each dropped line is an EDIT, so the repair reaches the disk instead of being announced over an unchanged file.
119
+
120
+ What COUNTS as a read has ONE home, `src/config-sections.js`'s `templateReads` — shared by the migrate rule, the build guard and the static guard lane, so none of the three can fail on something the others would not have fixed. A read is a `site.*`/`resolved.*` path in LIQUID context (`{{ … }}` or `{% … %}`) outside a display block: a hostname (`https://site.company.com/x`), a filename (`/site.webmanifest`), a fenced code sample and a `{% raw %}` body are not reads (the #521 doctrine — code display is not markup). The guard's section list is the schema's `CONFIG_SECTIONS` **exactly**, never the frontmatter lane's `hasOwn(config, key)` widening: `site.url` is a build fact and a brand may override `site.omega` / `site.characters`, and a read guard's false positive is a build a brand cannot fix.
121
+ - **No tracker loads without consent** ([#383](https://github.com/Omega-JS-Stack/omega/issues/383)). `core/foot.html` emits the provider IDS and nothing else — the GA4, Meta and TikTok loaders are injected at runtime by `core/js/core/analytics-loader.js`, and only for a category the visitor's consent record allows (ga4 → `analytics`, meta/tiktok → `marketing`). The regime is region-gated off the browser timezone (`core/js/libs/consent-region.js`): a STRICT roster — `Europe/*`, the four Atlantic EEA zones, Brazil, China, South Korea, and anything unplaceable — is real opt-in, where nothing loads until Accept; everywhere else loads immediately with no banner. A country joins the roster only when its law genuinely requires opt-in consent, verified; when in doubt it stays out ([#423](https://github.com/Omega-JS-Stack/omega/issues/423)). Google Consent Mode is wired either way: the `consent default` command is queued into dataLayer before gtag.js can load, and every change pushes a `consent update`. The record lives in `omega.storage()` under `trackingConsent` — NOT `consent`, which is already the signup form's legal consent (`libs/auth/forms.js`, read by the backend signup route) — and is reached through `core/js/libs/tracking-consent.js` (`getTrackingConsent()`), the one seam the banner, the loader, and the checkout/signup payloads share. Granting injects a loader immediately; revoking cannot unload a running script, so it takes effect next page load while Consent Mode updates now. The banner itself (`core/js/core/consent.js`, config `client.consent`) is a cookie-iconed message, one big Accept, and a small Customize opening a panel of instant-saving pill switches with an Accept all / Accept none pair; it never auto-accepts on scroll. In an opt-out region a first visit sees no banner at all — just the Cookies Settings tab that reopens it ([#391](https://github.com/Omega-JS-Stack/omega/issues/391)). Pinned by `test/consent-region.test.js`, `test/tracking-consent.test.js`, `test/consent-gating.test.js` and `test/consent-banner.test.js`.
122
+ - **`site.characters` is engine-owned, brand-overridable** ([#286](https://github.com/Omega-JS-Stack/omega/issues/286)). The literal-character set core templates interpolate rather than type — `characters.copyright` in the head's copyright meta, plus the brace/bracket pairs, `asterisk` and `underscore` — ships as a DEFAULT set from the engine ([engine.js](../src/engine.js)), composed onto the site global beside `site.omega`; a brand that writes a top-level `characters` key in its omega.json5 wins per key and the rest of the set still stands (nothing in OMEGA defined the set, so every page fleet-wide shipped a leading blank in that meta). Pinned by `test/site-characters.test.js`.
123
+ - **Machine files ship by default.** Every build emits `robots.txt` (with the sitemap pointer), `sitemap.xml`, `feeds/posts.xml` (RSS) + `feeds/posts.json` (JSON Feed), `pages.json` (the search index), `ads.txt` (the configured AdSense publisher, an honest comment when no provider is configured), `llms.txt` ([llmstxt.org](https://llmstxt.org) — brand heading, summary, then pages and posts as markdown links, [#4](https://github.com/Omega-JS-Stack/omega/issues/4)), `humans.txt`, `opensearch.xml`, and `.well-known/security.txt`. All are default pages generated from site metadata and the URL-sorted page collection, honoring the same exclusion convention (admin, redirect, draft, noindexed and sitemap-excluded pages, the `/test` dev SEGMENT, and the machine files themselves — each meta template carries the walk, kept in step by the tests) — so a consumer file at the same URL suppresses the framework's like any other default page. Titles and descriptions read another page's data through the `omega_rendered` filter ([#141](https://github.com/Omega-JS-Stack/omega/issues/141)): layout-contributed meta (the `/updates/*` pages' `Version {{ page.update.version }}`) holds raw Liquid until the owning page renders it, so a meta file renders it against THAT page's scope instead of emitting the source. Pinned by `test/meta-files.test.js`.
124
+ - **Structured data rides `core/foot.html`.** The unconditional blocks (BreadcrumbList, the brand node, WebSite) plus gated ones (BlogPosting, Person, Product, SoftwareApplication, ContactPage, FAQPage). The brand node's schema.org type is `brand.type`, optional config that defaults to `Organization` in the template ([#271](https://github.com/Omega-JS-Stack/omega/issues/271) — an unset key shipped `"@type": ""` on every page and collapsed the `<site.url>#<type>` @id every other block cross-references). Pinned by `test/head-foot-fixes.test.js`.
125
+ - **The index posture is ONE decision, and both signals read it** ([#564](https://github.com/Omega-JS-Stack/omega/issues/564), found by a blind verify of a migrated brand — 745 legacy `<loc>` entries became 255 while every dropped page still shipped `content="index"`). `meta.index: false` — set by a page or by the layout it renders on — makes `core/head.html` emit `noindex` AND drops the page from `sitemap.xml`, `pages.json` and `llms.txt`, so no two signals can disagree — one flag governs all three machine files. **The site-wide default is the SAME key one level up, `targets.web.meta.index`** ([#564](https://github.com/Omega-JS-Stack/omega/issues/564), Ian's same-name ruling 2026-09-09, where a global value and its specific override share ONE name and ONE shape; #725's `seo.index` was a second name for one decision and is retired with a pointer): the literal `false` in omega.json5 SEEDS every page's meta walk with `index: false`, so the whole site goes noindex and all three machine files empty through the very same flag — never a second emission path. A page's own `meta.index` still wins both directions, so `meta.index: true` keeps one page indexed on an otherwise dark staging site. **`sitemap.include` is retired with it**: "indexable but out of the sitemap" is not a state a page can be in any more, so the key is off the frontmatter allow-list and the sitemap simply follows the index flag. Consequential-feature doctrine ([#527](https://github.com/Omega-JS-Stack/omega/issues/527)): absent or `true` is exactly today's behavior. **The posture is thin-taxonomy EXCLUSION**: a taxonomy page is a list of links to pages that are themselves indexed, so it earns nothing and competes with them. **Indexed** — posts, authored pages, collection items, and every listing's page 1 (`/blog`, a declared collection's `/docs`); **noindex** — the blog taxonomy entire (tag AND category terms, and both hubs) plus a declared collection's category pages, a listing's `/page/N` (page 1 is the canonical listing), auth, account, checkout, 404, `/extension/installed`, and the framework's `/test` dev surfaces. Every AUTOMATIC exclusion comes from the ENGINE, not from a URL list each template keeps: `resolved` sets `meta.index: false` on a draft, on the `/test` dev surfaces, under `/admin/`, on a redirect-layout page, and on any non-alias paginated page past the first ([engine.js](../src/engine.js)), so a page whose URL merely contains `/page/` stays indexed and listed, and a draft now SAYS noindex instead of silently vanishing from the sitemap while telling crawlers to index it. The templates and the dist audit read that one resolved value and nothing else. `robots.txt` disallows nothing new — a crawler must fetch a page to read its `noindex`. `head.html` answers no URL at all now: the `/test/` dev surfaces (which a production build omits entirely, #554) get the flag from the engine like everything else. **The listing exemption**: `defaults/pages/blog.md` and the generated collection listing are the only paginated templates WITHOUT `eleventyExcludeFromCollections` — a page cannot be in `sitemap.xml` if it is not in the walk, and Eleventy adds only page 0 of a paginated template to collections, so the exemption exposes page 1 and never `/page/N` (`test/collections-gate.test.js` pins which templates get it, and why an alias generator never does). Pinned by `test/index-posture.test.js`; `omega migrate` prints the `<loc>` delta as a conversion note so a migrating brand records it instead of filing it as a regression.
126
+ - **Blog search is client-side and self-contained** ([#44](https://github.com/Omega-JS-Stack/omega/issues/44) item 22): the build emits `/blog/index.json` (title, url, capped description, tags, categories, date; no bodies; sitemap-excluded), and the blog index page lazily fetches it on first focus, ranks title hits first, deep-links `?q=`, and binds Cmd/Ctrl+K. The schema `SearchAction` and `opensearch.xml` target `/blog?q={search_term_string}`; no Google CSE anywhere. Pinned by `test/blog-search.test.js`.
127
+ - **Theming** is the `--omega-*` token contract: themes redefine tokens, components read `var(--omega-*)`. Two consumer tiers — restyle the stock theme from `src/assets/css/main.scss` (`@use 'omega:main' with (…)`), or ship a full theme at `src/themes/<id>/`. A tier-2 theme owes only that dir and a `_theme.scss` (`@forward 'omega:theme';` inherits the packaged skin whole); `_theme.js` and `fonts/` are optional — a theme SHADOWING a packaged id inherits that skin's theme js and font files along with its faces, and a theme with a NEW id lands on base's floor ([#773](https://github.com/Omega-JS-Stack/omega/issues/773)). Full contract: [docs/shared/theming.md](shared/theming.md); visual spec: [docs/web/classy-v2/DIRECTION.md](classy-v2/DIRECTION.md).
128
+ - **The purge pass scans HTML _and_ the built JS.** `purgeCss` ([assets.js](../src/assets.js)) globs `dist/**/*.html` plus `dist/**/*.js`, so classes that exist only inside a client-rendered app's markup strings survive `omega build` with zero config — a consumer never needs a safelist for its own classes (#66). The built-in safelist stays for what no content scan can see: the runtime-stamped `omega-` namespace and Bootstrap's JS-toggled transition classes — and it is the DEFAULT, not the whole truth: `targets.web.purgecss.safelist` merges over it ([#250](https://github.com/Omega-JS-Stack/omega/issues/250)), in PurgeCSS's own shape (`{ standard, deep, greedy, keyframes }`, or a bare array for `standard`). Config is JSON5, so the pattern lanes take strings and the build compiles them to regexes. Pinned by `test/purge-css-safelist.test.js`.
129
+ - **The main sheet does not render-block** ([#750](https://github.com/Omega-JS-Stack/omega/issues/750), Phase 2 of [#467](https://github.com/Omega-JS-Stack/omega/issues/467)). The css lane extracts the FIRST-PAINT subset of the compiled main bundle (`extractCriticalCss` in [assets.js](../src/assets.js)) into `assetManifest.css.critical`, which is css TEXT rather than a url: [head.html](../core/_includes/core/head.html) inlines it in a `<style>` block, then loads the full sheet through `<link rel="preload" as="style" onload="this.rel='stylesheet'">` with a `<noscript>` twin. The `<link>` keeps its document position, so the cascade order never moves and the per-layout and per-page sheets (inlined when small, a blocking link past the budget, see #767 below) still win over it. A page or layout sheet that compiles to nothing is never emitted or linked ([#766](https://github.com/Omega-JS-Stack/omega/issues/766)): an empty blocking request still costs the round trip, about a second of first paint on a slow mobile connection. **A sheet small enough costs no request either** ([#767](https://github.com/Omega-JS-Stack/omega/issues/767)): compiled at or under **8 KB** (`INLINE_MAX_BYTES` in [assets.js](../src/assets.js)) it rides the manifest as css TEXT, so a manifest entry is `{ inline }` instead of `{ href }` and the head prints a `<style>` block in the exact document position the link held, layout sheets before page sheets and layers in load order, which leaves the cascade untouched. Over the budget the sheet keeps its content-hashed file and its link, and the split is per sheet: a heavy layer's link stands beside a light layer's block inside one key. The budget is about the ROUND TRIP rather than the bytes, which is why it is so low: the request queues behind the HTML and the font preloads, so the [#763](https://github.com/Omega-JS-Stack/omega/issues/763) proof measured `/terms`, whose page sheet is 1 KB, first-painting at 1,928 ms against the 824 ms of a home page that ships no page sheet at all. `omega dev` inlines nothing (the reason it keeps the main sheet a plain link: a stable link is what keeps a watch rebuild flash-free), and the critical extractor's content set is unchanged, since an inlined sheet is already whole and already in the head. The font prune reads the inlined text beside the emitted css tree for the same reason ([#768](https://github.com/Omega-JS-Stack/omega/issues/768) lane, `inlinedSheetCss` in [assets.js](../src/assets.js)): a face declared only by a sheet that never reached disk is referenced just as surely as any other, and pruning it would delete it out from under the page that needs it. Pinned by `test/inline-sheets.test.js` and the inlined-face case in `test/assets.test.js`. **The subset is chosen by MARKUP, not by a selector list**: PurgeCSS, already the pipeline's extractor, reads the first-paint templates as its content, layer-resolved like everything else (`_layouts/core/root.html`, each `_layouts/**/core/base.html`, every `_includes/**/sections/nav.html`, the app shell's `app-topbar.html` and `app-sidebar.html`, every `_sections/**/hero/section.html`, and the shared `heading/masthead` component), plus every band any template stamps `data-omega-first-paint`: the extractor scans the layers for those `<section>` chunks and feeds ONLY the band's markup, so the attribute that starts a band's reveals at parse time is also what selects its critical css, and a whole page layout never drags its below-the-fold vocabulary into the block. So a theme, or a consumer that overrides one of those files, gets its own nav and hero rules inlined with no per-theme bookkeeping, and a page that opens on the nav plus a hero (the shipped home page) paints with no layout shift because every class those templates really render carries its rules, the collapsed mobile menu's `.collapse:not(.show)` guard included. A route whose FIRST band is neither of those paints that band with nav-only styling until the deferred sheet lands; stamp the band `data-omega-first-paint` (which also starts its reveals at parse time, ahead of the motion engine) if that shift matters. The one safelisted lane is motion ([#763](https://github.com/Omega-JS-Stack/omega/issues/763)): every reveal rule is scoped `html[data-omega-motion] …` and resolved by `data-omega-inview`, tokens no markup scan can see, so the extractor purged the whole lane and a built page painted its first-paint copy visible, stamped it, and met the deferred sheet with nothing left to transition — no fade at all. A greedy `data-omega-(motion|reveal|inview)` pattern keeps it, about 1.1 KB, and the site-wide greedy `omega-` lane stays out. Two more rules ride the same safelist because no markup scan reaches them (the [#763](https://github.com/Omega-JS-Stack/omega/issues/763) proof measured the playground home at CLS 0.27 without them, 0.07 with): the skip link's `visually-hidden-focusable` hide rule, since body.html renders that link before every band and the rest of that file is `hidden` alert bars whose icon vocabulary would cost 11 KB of block, and the `omega-dotfield__canvas` positioning, since the dotfield module creates its canvas at runtime and an unpositioned canvas lands in flow whenever the module beats the deferred sheet. Keyframes are pruned to what the kept rules reference; every `@font-face` stays, because the kept rules name their family through `var(--omega-font-*)`, which PurgeCSS cannot follow, and a late `@font-face` is a swap-time reflow (the eight declarations cost about a kilobyte, and the faces the head preloads are among them). The block has a **72 KiB budget** (`CRITICAL_MAX_BYTES`): the packaged themes measure 64 KB (neobrutalism) to 71 KB (newsflash; classy 68 KB), 12 to 15 KB gzipped, roughly one initial congestion window arriving with the HTML instead of costing a round trip; over budget the build warns by name and still ships the block, since a heavier theme is that theme's call. `omega dev` extracts nothing (that lane never purges either), and with no critical block the head falls back to the plain blocking link, so a watch rebuild never flashes. Pinned by `test/critical-css.test.js`.
130
+ - **The head warms only the origins the page will really use** ([#467](https://github.com/Omega-JS-Stack/omega/issues/467) Phase 2). [head.html](../core/_includes/core/head.html) emits a `preconnect` + `dns-prefetch` pair per third party, each gated on the SAME config read the loader makes, so a provider that is off costs nothing and no page ships a handshake nobody spends. The list: `https://www.googletagmanager.com` on `analytics.providers.google.id`, `https://connect.facebook.net` on `analytics.providers.meta.id`, `https://analytics.tiktok.com` on `analytics.providers.tiktok.id` (the three origins `core/js/core/analytics-loader.js` injects a script from), and `https://securetoken.googleapis.com` + `https://firestore.googleapis.com` on `cloud.config.apiKey`, which is the very value `@omega.js/client` boots the Firebase SDK on (auth refreshes the persisted token, then the auth module reads the account doc). Chatsy's pair on `inbound.chat.providers.chatsy.enabled` predates all of them and is unchanged. **`crossorigin` follows the REQUEST, not the habit**: the Firebase origins are CORS fetches so their warmup is anonymous, while the three analytics origins are plain `<script src>` loads, where an anonymous preconnect would open a connection the tag cannot reuse. Consent gates the injection, never the warmup: a preconnect sends no request and sets no cookie, and the connection is already open by the time a visitor accepts the banner. The GA4 beacon origin (`www.google-analytics.com`) is deliberately NOT warmed, since nothing reaches it until gtag.js has loaded and consent has been granted, which is well past the first viewport. Pinned by `test/head-preconnect.test.js`.
131
+ - **Font preloads come from the sheet's `@font-face` rules** ([#765](https://github.com/Omega-JS-Stack/omega/issues/765), after [#467](https://github.com/Omega-JS-Stack/omega/issues/467) Phase 2 and [#749](https://github.com/Omega-JS-Stack/omega/issues/749)). The css lane reads the COMPILED main bundle (`firstPaintFontFaces` in [assets.js](../src/assets.js)) and fills `assetManifest.fontPreloads`, which [head.html](../core/_includes/core/head.html) loops into one `<link rel="preload" as="font" crossorigin>` per face. The rule is what the CSS DECLARES, never what a file is named: a face preloads when it carries no `unicode-range` (it covers everything, the single-file variable font case) or when one of its ranges reaches basic latin (U+0000-00FF) — `U+0131`, `U+0100-02BA` and the `U+00??` wildcard form all reduce to their lowest codepoint. So a theme that vendors one variable face, or names its files its own way, is covered the day it ships; the old `site.fontPreloads` name rule (`*-normal-latin.woff2` / `*-italic-latin.woff2` off the first theme layer with a `fonts/` dir) is gone, and so is the `site` key. woff2 only (the one format the `type="font/woff2"` link names), deduped and sorted so the emitted HTML is deterministic, and kept to the faces the build actually SHIPS — the inheritance hatch hands a partial theme the default skin's `@font-face` rules without its font files ([#177](https://github.com/Omega-JS-Stack/omega/issues/177)), and a preload for a face that is not there is a 404. A face hosted on another origin (an absolute or protocol-relative `src`, the CDN case) rides through untouched: the browser can fetch it, and the head's link already carries the `crossorigin` the spec asks for. Both styles ride the list: every display headline renders its accent as an `<em>` (the `heading/masthead` cluster, the hero's `headline_accent`), so the first viewport asks for italic as surely as it asks for normal; once #749 made the hero paint with the document, an unpreloaded italic swapped in AFTER first paint and put a 0.02 CLS on the unthrottled trace at 72 ms. The `-latin-ext` subsets stay OUT by their ranges: they cover glyphs the first viewport's copy does not use, and every extra preload competes with the ones it does. The list is root-relative like every other manifest URL — the HTML transform mounts it under a base path. In `omega dev` the path is explicit, and not a race: the asset watcher rebuilds the sheet on a theme's `@font-face` (or font-file) edit, folds the new list into the live manifest (`refreshLiveManifest` in [commands/dev.js](../src/commands/dev.js)) and — only when the list actually MOVED — rewrites `.omega/asset-manifest.json`. Eleventy watches that file, so its re-render happens after the asset build rather than beside it, and only when the head's list changed. The `assetManifest` global is registered as a CALLBACK for this to land: an object handed to `addGlobalData` is snapshotted at registration, so the re-render has to re-read the live object to see the new list. Classy still preloads exactly four faces (Inter + Newsreader, normal + italic, latin), newsflash its own four; pinned by the font-preload tests in `test/themes.test.js`.
132
+ - **The fallback face is metric-matched, so the swap moves nothing** ([#768](https://github.com/Omega-JS-Stack/omega/issues/768), after [#765](https://github.com/Omega-JS-Stack/omega/issues/765)). A preloaded face still lands late on a slow connection, and `font-display: swap` reflowed every paragraph when it did (the [#763](https://github.com/Omega-JS-Stack/omega/issues/763) proof measured `/terms` at CLS 0.158, one shift at 3.1 s on the body copy). So the css lane GENERATES a fallback face per vendored family: `@font-face{font-family:"Inter Fallback";src:local("Helvetica Neue");size-adjust:105.508%;ascent-override:91.818%;descent-override:22.862%;line-gap-override:0.000%}`, and rewrites every `--omega-font-*` stack to name it immediately after the web family. The system font then occupies the same lines the webfont will, and the swap is free. `size-adjust` is the width ratio of the two faces and each vertical override is the web face's own proportion divided by it (an override is read against the already adjusted em), which is the fontaine / `next/font` formula. The numbers are MEASURED from the font files at build time by [font-metrics.js](../src/font-metrics.js), a dependency-free reader: a WOFF2 is a header, a table directory and one brotli stream (node's own `zlib`), and the reader takes `head`/`hhea`/`OS/2` for the vertical box (`OS/2`'s sTypo* values when the face sets the USE_TYPO_METRICS bit, as a browser does) plus `cmap` and the `hmtx` advances for the width. It reads a bare `.ttf`/`.ttc` through the same table parse, which is how the checked-in table of system-family metrics was measured (see the provenance comment on `SYSTEM_FONT_METRICS`). Width is measured over a fixed English sample rather than read from `OS/2`'s own `xAvgCharWidth`: the spec redefined that field between table versions and vendors did not restate the value (Arial's file says version 3 and still carries its old lowercase figure), so a ratio of two such fields compares two different quantities. Off `OS/2`, classy's Newsreader fallback would size-adjust to 123.5% of Georgia when the text it sets measures 91.2% of Georgia's width, and Inter's to 145.0% of Arial instead of 106.9%; measuring both sides keeps one definition on both ends of every ratio. The generated face lists EVERY measured family the stack names after the web one as a `local()` source, in stack order (classy's UI stack: `Helvetica Neue`, then `Arial`), with the overrides computed against the first: `local()` resolves only where that font is installed, and a face with no resolvable source would hand the swap to an unadjusted tail entry on Windows. The transform (`injectFallbackFonts` in [assets.js](../src/assets.js)) runs on the COMPILED sheet, before the critical extractor, so the inlined block and the deferred sheet carry the same faces and the same stacks; no scss source names a fallback family, and a consumer theme's own faces are covered the day they ship. The fallback family is the theme's OWN choice: the first family after the web font in that token's stack that the metrics table knows (classy ui: Inter → `Helvetica Neue`, classy serif: Newsreader → `georgia`). A stack that reaches no known family gets no face and one warning naming it, never a throw; a face whose file this build does not ship (the [#177](https://github.com/Omega-JS-Stack/omega/issues/177) inheritance hatch) is skipped silently, like its preload. A `local()` face names no url, so the preload list is untouched. Always on, no config key. Pinned by `test/font-fallback.test.js`, `test/font-metrics.test.js` and the inline-block case in `test/critical-css.test.js`.
133
+ - **Charts are the framework's.** `core/js/libs/charts.js` owns [TanStack Charts](https://tanstack.com/charts/latest) — a real dependency of this package, never a runtime CDN load (Ian 2026-07-27), pinned EXACTLY because the library is Alpha ([#772](https://github.com/Omega-JS-Stack/omega/issues/772)). Page code imports the helpers (`loadCharts`, `chartSlot`, `chartColors`, `barChart`/`stackedBarChart`/`doughnutChart`/`lineChart`) and never names the library, so its version and delivery stay ours to change. Every import inside `loadCharts` is dynamic, so ESM splitting puts the library in its own chunks — a page with no chart pays nothing (`test/dataviz.test.js` builds a charting page and pins it). Colors come off the `--omega-chart-*` ramp; `chartSlot` stamps the series onto the box so `@omega.js/client`'s write-on-change `swap` redraws on data-only changes. **Every page goes through the helper — the admin dashboard included** ([#74](https://github.com/Omega-JS-Stack/omega/issues/74)): it passes `colors: ['var(--omega-ok)', …]` to keep STATUS meaning where the categorical ramp would only say "different". The mounted host follows its container's width and is TOLD its height, so a hand-authored host div needs a height-bearing box (`.admin-chart-box`) exactly like `chartSlot`'s markup. A definition is DOM-free, so `chartDefinition(kind, data)` lets node pin all four builders' geometry through `createChartScene`; the PAINT is pinned in the browser by the sandbox e2e lane's chart step (`brands/sandbox-brand/test/e2e/run.js` → `window.__omega.drawCharts()`), which fails a chart whose host holds no SVG mark with a non-zero box.
134
+ - **Graphs are the framework's too.** `core/js/libs/graph.js` owns mermaid on the same terms ([#169](https://github.com/Omega-JS-Stack/omega/issues/169)): a real dependency of this package, never a runtime CDN load, and page code imports the helpers (`loadGraph`, `graphReady`, `graphSlot`, `graphTheme`, `drawGraph`) without ever naming the library. The import inside `loadGraph` is dynamic, so ESM splitting puts mermaid in its own chunk — a page with no diagram pays nothing (`test/graph.test.js` builds a diagramming page and pins it). The module takes a DEFINITION in and puts an SVG out: composing the mermaid text is the page's business, because a diagram's shapes and words are content, not framework. `drawGraph` initializes per render (`startOnLoad: false`, `securityLevel: 'strict'`, `theme: 'base'`) with `themeVariables` resolved from the live tokens — ink, both surfaces, `--omega-line`, `--omega-accent` for the node outline, and the same `--omega-chart-*` ramp charts read, mapped onto mermaid's `cScale`/`pie` slots ([docs/shared/theming.md](shared/theming.md)); mermaid paints with literal colors, so nothing is handed a `var()`, and the read happens at draw time, so a light/dark flip lands on the next redraw exactly like charts. `graphSlot` stamps the definition onto the host so `swap` redraws on definition-only changes, and carries a height FLOOR (`min-height`) — the host is empty until the render lands, and a taller diagram still grows past it. Rendering needs a real DOM, so `drawGraph` itself is not unit-tested, and unlike the chart builders it has no sandbox e2e step yet — the first page that draws a graph should add one.
135
+ - **Browser log lines carry the ONE identity tag.** `core/js/libs/logger.js` owns it: `createLogger('<module>')` returns `{ tag, log, info, warn, error, debug }` printing `[@omega.js/web:<module>] message` with NO timestamp (devtools stamps runtime lines; only the build-time devkit logger in `src/` prefixes `[HH:MM:SS]`) — [#12](https://github.com/Omega-JS-Stack/omega/issues/12). No core/js file hand-writes a tag: the module segment comes from the file's identity (`core/auth.js` → `auth`, `pages/dashboard/account/sections/security.js` → `account:security`), and `console.group`/`%c` lines embed `logger.tag` rather than a literal. Web's own file on purpose — `@omega.js/client`'s twin would stamp the wrong package segment. Every layer reaches it through `__main_assets__/js/libs/logger.js` (the `modules/` lane that imported it relatively is gone, [#624](https://github.com/Omega-JS-Stack/omega/issues/624)). Pinned by `test/logger.test.js` and repo-wide by `scripts/log-tags.test.js`.
136
+ - **Icons** are plain `fa-*` markup everywhere — the build inlines every one the rendered page names, and a runtime watcher upgrades whatever JS creates afterwards ([#619](https://github.com/Omega-JS-Stack/omega/issues/619)): [docs/shared/icons.md](shared/icons.md).
137
+ - **A redirect is a PAGE, and that is the ONLY redirect mechanism the web build has** ([#466](https://github.com/Omega-JS-Stack/omega/issues/466), Ian's ruling 2026-08-22 — pages own URLs, and no config path exists). A page sets `redirect.url` (plus optional `redirect.querystring` forwarding) in frontmatter on the `modules/utilities/redirect` layout, and the build emits **meta-refresh + canonical + noindex**: `<link rel="canonical">` points at the DESTINATION rather than at this URL, `meta.index: false` on the layout keeps the shortlink out of every index and out of the machine files, and `<noscript><meta http-equiv="refresh" content="0; url=…"></noscript>` performs the hop with JavaScript off — all three from the one head ([core/_includes/core/head.html](../core/_includes/core/head.html)), which absolutizes a SITE-RELATIVE destination against `resolved.config.url` because the base-path pass ([#355](https://github.com/Omega-JS-Stack/omega/issues/355)) only rewrites `href`/`src` attributes — and only that one, a single leading slash: a protocol-relative `//cdn.example.com/x` is already absolute and a `mailto:` is not a path, so both ship verbatim. The refresh sits INSIDE `<noscript>` on purpose: with JS on, the layout's `#redirect-config` and its layout-keyed script (`core/js/layouts/modules/utilities/redirect.js`, [#624](https://github.com/Omega-JS-Stack/omega/issues/624)) do the hop and forward the query and the fragment, which a refresh cannot, so the two must never race. That script hops at IMPORT time rather than from a default export — a shortlink must not wait on the @omega.js/client boot the page's other bundles await. The framework ships shortlink defaults (`/account`, the auth aliases, `/billing`, `/admin/dashboard`) a consumer overrides like any default page. Pinned by `test/redirects.test.js`, plus `test/assets.test.js`, `test/contract.test.js`, and `test/slice.test.js`.
138
+ - **A TEMPLATED redirect is a Cloudflare rule, never web config** ([#466](https://github.com/Omega-JS-Stack/omega/issues/466)). `targets.web.redirects` shipped in 0.45.0 and is RETIRED — a config still carrying it fails validation naming its new home. A redirect whose destination is computed from the request path (DashQR's printed QR codes point at `/c/<id>` for unbounded ids) cannot be enumerated as a page, and static hosting has no server to answer it with: the map could only ever be materialized through the built 404 page, so a search engine saw a 404 that redirects rather than a permanent move. Those redirects belong at the edge, where the computation actually happens — `edge.providers.cloudflare.rules.redirect`, reconciled by the manager's edge service ([docs/manager/edge.md](../manager/edge.md)). **`omega dev` does NOT answer edge-owned routes** (the manager call, 2026-08-30): the edge owns them, so a `/c/<id>` request in dev is a plain 404, exactly as it is on the built output — the alternative is a local mirror of Cloudflare's filter language whose only job is to disagree with production. Test the rule against the zone.
139
+ - **A brand's socials come with their shortlinks** ([#429](https://github.com/Omega-JS-Stack/omega/issues/429), legacy UJM parity — brands do NOT hand-author these pages). Every entry of the `socials` config block generates a redirect page at `/<platform>` on the module above ([social-pages.js](../src/social-pages.js)), so `/discord` — which the footer links site-wide — resolves instead of 404ing. The string form is a HANDLE (`twitter: 'somiibo'`) and the destination is the profile URL template-kit's platform patterns derive from it, the same URL JSON-LD `sameAs` and the footer row read; the object form `{ handle, redirect }` adds a destination that is not the profile (kirue's Spotify ARTIST page, where the handle still names the profile `sameAs` points at) and the redirect target wins for the shortlink alone. A blank handle, or a platform with neither a known URL pattern nor a `redirect`, generates nothing — legacy configs list every platform and fill in a few — while a malformed entry (unknown key, non-string handle) is a config ERROR. The pages ride the SAME lane as the default pages, so a consumer page at `/<platform>` takes that URL over, and the layout's own `meta.index: false` keep them noindex and out of the machine files. Pinned by `test/social-pages.test.js`.
140
+ - **So do a brand's download platforms and store listings** ([#561](https://github.com/Omega-JS-Stack/omega/issues/561), the same legacy-parity lane, same FILE-AND-SKIP ruling). An opted-in desktop target generates a redirect page per platform at `/download/<platform>` and one per artifact at `/download/<platform>/<artifact>` (mac/universal, windows/universal, linux/debian + linux/appimage — the URL shape the legacy pages published), and every store listing with a url generates `/extension/<store>` ([target-shortlinks.js](../src/target-shortlinks.js)) — the URLs legacy UJM shipped as hand-maintained default pages (`src/defaults/dist/redirects/download/**`), which store pages, third-party listings and old posts link to and which every migrating brand 404s. The declaration is the SAME curated view `/download` and `/extension` render from ([#85](https://github.com/Omega-JS-Stack/omega/issues/85), [#610](https://github.com/Omega-JS-Stack/omega/issues/610)): `site.targets.desktop.downloads` and `site.targets.extension.listings` — there is no hand-written `download`/`extension` page map any more, and a config still carrying one fails validation. **A download link hands over the FILE, never the releases page** ([#620](https://github.com/Omega-JS-Stack/omega/issues/620)): the shortlinks and the `/download` buttons both redirect to `<releasesUrl>/download/<versionless asset>`, whose names are @omega.js/desktop's own packaging rule (the contract, including the survivor and never-reuse rules for moving the repo: [docs/desktop/releasing.md](../../packages/desktop/docs/releasing.md)) — so a desktop release never touches the website and these URLs never change. Legacy parity on the bare platform URL: `/download/<platform>` points at the platform's FIRST artifact (UJM sent `/download/linux` to the `.deb`). A listing with no url generates nothing — an announced store waits on Coming soon — while a store key that is not a URL segment is a config ERROR, because a shortlink that silently fails to generate is a dead link on a listing nobody controls. Same lane, so a consumer page at one of those URLs takes it over, and the layout keeps them noindex and out of the machine files. Both lanes share the generated-page shape in [redirect-page.js](../src/redirect-page.js). Pinned by `test/target-shortlink-pages.test.js`.
141
+ - **A brand's own collections come with their pages** ([#207](https://github.com/Omega-JS-Stack/omega/issues/207), the legacy dynamic-pages generator's successor). `targets.web.collections` declares them — `docs: { field: 'doc.category', size: 12, title, description, permalink }` — and documents in `_docs/` publish at `/docs/<slug>`, listing at `/docs` (+ `/docs/2…`), one page per category of `field` at `/docs/categories/<slug>`. `field` is the dotted frontmatter path the categories group on (one value or a list), `permalink` moves the whole base, and a built-in collection name is a config error. The pages are generated as virtual templates on the SAME lane as the default pages ([dynamic-pages.js](../src/dynamic-pages.js)): a consumer page at any one of those permalinks takes that URL over, and the markup is the layered `blueprint/collection/*` → `frontend/pages/collection/*` layouts a theme overrides like any other. A declared collection is a SITE collection like the built-ins: its documents publish as `site.<name>` in the `site.posts` shape (`url`, `date`, and the document's own frontmatter at the top level), so a UJM consumer's `{% for product in site.products %}` renders ([#593](https://github.com/Omega-JS-Stack/omega/issues/593) — reaching templates only as `collections.<name>`, frontmatter under `.data`, rendered that loop empty on a green build). Every generated page names itself: category pages carry per-term title/description ([#312](https://github.com/Omega-JS-Stack/omega/issues/312)), and each DOCUMENT of a declared collection gets `meta.title`/`meta.description` filled from its own frontmatter — namespace `title` (or `name`, the other spelling a real corpus uses, [#596](https://github.com/Omega-JS-Stack/omega/issues/596)) and `description` first, bare keys next, a title-cased slug and a backfill sentence last — with the document's own `meta.*` always winning ([#317](https://github.com/Omega-JS-Stack/omega/issues/317), `applyDocumentData`). The layered `blueprint/collection/document` layout gives a document the masthead h1 and back link its siblings have; a document on another layout keeps the derived meta regardless. The blog does NOT ride this mechanism — its default pages stay as they are. Pinned by `test/dynamic-pages.test.js`.
142
+ - **Every page paints under the page paint contract** ([#637](https://github.com/Omega-JS-Stack/omega/issues/637)). Static content paints immediately, user data arrives through bindings with skeletons, an answer the visitor acts on resolves ONCE, every wait behind visible copy has a deadline and a named fallback, and every form's submit control is gated until its answers land: [docs/web/page-contract.md](page-contract.md).
143
+ - **A one-time buy bills on `once`, and its confirmation never polls** ([#668](https://github.com/Omega-JS-Stack/omega/issues/668)). Checkout resolves the frequency from the PRODUCT (`resolveFrequency`, `core/js/pages/payment/checkout/modules/state.js`) — a cadence term only ever belongs to a subscription — so the intent payload, the intent doc, the confirmation URL and every funnel event's `item_variant` carry `once` instead of the `annually` the price-list fallback used to pick; the pricing page's `add_to_cart` says `once` for a one-time product too, and the checkout's funnel value goes through `calculatePrices()`, the one read that knows the `once` key (`begin_checkout` and `add_payment_info` used to report $0 on a product priced `{ once: 49.99 }`). The intent route stamps `type` on the confirmation URL, which the verifier reads as a second guard, so a one-time purchase opens CONFIRMED rather than polling the account for a plan it never writes. Pinned by `test/checkout-one-time-frequency.test.js`, `test/confirmation-verify.test.js`, `test/confirmation-purchase-pixel.test.js` and `test/pricing-switch-cta.test.js`.
144
+ - **A one-time purchase has an account surface, and a refund it can reach** ([#672](https://github.com/Omega-JS-Stack/omega/issues/672)). The account page carries an **Orders** section (`core/js/pages/dashboard/account/sections/orders.js`) listing every purchase — product, order number, date, amount, status — read from `GET /user/orders`, which is the ONLY way a browser can see a one-time purchase at all: `payments-orders` is admin-only to clients and a one-time buy writes nothing to `users/{uid}`, so the route reads the caller's own orders server-side through the admin SDK (the Firestore rules are untouched) and hands back a summary, never the provider's raw resource or the request context. The Refund section reads the SAME fetch (`sections/../modules/orders.js`, one request for both) and is eligible when the subscription is refundable **or** any purchase is: its subject picker lists them, the form posts the picked `orderId`, and the backend's guarded one-time lane — unreachable from the product until now — takes it. `refundable` is the backend's own answer, so a button the page offers is a refund the route accepts, and the success copy no longer tells a one-time buyer their subscription was cancelled. The confirmation page and the one-time receipt email both point at `#orders`. Pinned by `test/account-orders.test.js`.
145
+ - **The crypto pay button is back, on two conditions** ([#642](https://github.com/Omega-JS-Stack/omega/issues/642)). #636 deleted it because no Coinbase provider existed behind it; the provider is real now, so `payment.providers.coinbase.enabled` shows it — AND the product must be one-time. Coinbase Commerce sells a single hosted charge and has no recurring anything, so the backend's intent provider refuses a subscription outright and a button offered on a plan could only end in the checkout's generic failure sentence. `resolveProvider('crypto')` starts a `coinbase` intent and the page redirects to the charge's `hosted_url` like any other provider. Pinned by `test/checkout-crypto.test.js` and `test/checkout-page.test.js`.
146
+ - **A fully discounted total takes the PayPal and crypto buttons away** ([#786](https://github.com/Omega-JS-Stack/omega/issues/786)). PayPal and Coinbase are the two providers whose amount the backend computes and SENDS, and neither API accepts a zero one — so a code covering the whole price (the shipped `WELCOME10OFF` on anything at or under $10) is refused server-side, and `buildBindingsState()` stops offering the buttons that could only end there (`core/js/pages/payment/checkout/modules/state.js`); they come back the moment the code is removed or changed, off the same rebuild every discount change already does. Card checkout is untouched (Stripe and Chargebee hold a real coupon), and a free TRIAL is never this case — nothing is charged today through the provider's own trial machinery and the code comes off the first PAID period, which is exactly what the backend does, so the buttons stay. A brand selling through PayPal (and crypto) alone would be left with an empty grid, so when a code hides every remaining method the discount field's own error line says which code did it and that removing it brings them back — the page-level error surface is not reusable there, it hides the whole checkout and the discount field with it. The terms note dropped its "not available with PayPal" clause with the same change: a PayPal subscription has taken the code as a discounted first period since [#759](https://github.com/Omega-JS-Stack/omega/issues/759). Pinned by `test/checkout-zero-total.test.js`.
147
+ - **One catalog behind /pricing and the account page** ([#647](https://github.com/Omega-JS-Stack/omega/issues/647)). A feature is DEFINED once, in the top-level `features` catalog (name, icon, definition, and a `usage` block on the metered ones), and each product names only its VALUE — so a limit and the row that renders it can no longer disagree, and the definition BACKFILL that used to unify per-product copies is gone with the duplication it papered over. `composePricing(payment, features)` reads both halves for the cards, the common/extra split and the comparison matrix; `payment-config.js`'s `getProductLimits` became the catalog-aware read (only the counted features a product gives a NUMBER); the billing panel's plan bullets read the same two halves, and its **usage bars** read the account's counters through the SAME `@omega.js/account` derivation the backend's gate uses — one bar per counted feature, showing what is left **today** and **this month** with any admin-granted override credits included in the month's number and named beside it. The catalog rides the client blob (`site.client.features`) like `payment` does. Pinned by `test/pricing.test.js`, `test/sections-pricing-cards.test.js`, `test/billing-plan-switcher.test.js` and `test/billing-usage-bars.test.js`.
148
+ - **The account page's connection cards are the CONFIG, and the logos ship in the framework** ([#771](https://github.com/Omega-JS-Stack/omega/issues/771), [#792](https://github.com/Omega-JS-Stack/omega/issues/792), [#793](https://github.com/Omega-JS-Stack/omega/issues/793)). The Connections section used to be closed twice over and a card had two homes: the layout drew one per `connections:` frontmatter row (twelve of them, hardcoded CDN URLs — the Twitch and Kick ones 404), a row WON over the brand's config entry so a brand could not correct one, and `core/js/pages/dashboard/account/sections/connections.js` carried a third list of descriptions naming providers this lane cannot connect. The rows are gone. The card list is every `resolved.config.connections` key carrying a `name` and a `logo` (`_includes/frontend/sections/account-connections.html`, one loop, one card component), the section drives from `Object.keys(connectionsConfig)` alone, and the FRAMEWORK's config defaults carry `name` + `logo` + `description` for the five providers @omega.js/backend ships a module for — so `google: { enabled: true }` is a complete card, and any key a brand writes wins through the ordinary merge chain. `logo` is the NAME of a mark this package ships (`core/logos/brandmarks/original/<name>.svg`, drawn inline by `omega_logo`; `kick.svg` is new) or a full URL (drawn as an `<img>`) — a value with a `/` or a `:` is a URL, anything else is a mark. An enabled provider with no `name`+`logo` still gets the unsupported card naming what its config entry is missing — as does a key that is not a legal provider name (`[a-z0-9-]`, the backend loader's own rule), which is never offered as a live card whatever fields it carries. Pinned by `test/connections-cards.test.js`.
149
+ - **A connect comes back where it started** ([#784](https://github.com/Omega-JS-Stack/omega/issues/784)). `/connections/callback` hardcoded `/dashboard/account#connections` on both its exits, so a brand page that started a connect from its own surface could not get the visitor back to it. The authorize call takes an optional `returnUrl` — one line, `url.searchParams.set('returnUrl', location.pathname + location.search + location.hash)` ([packages/backend/docs/connections.md](../../packages/backend/docs/connections.md)) — which rides the encrypted state and comes back out of `tokenize`; the page navigates there on success and offers it as the way back on an error. It re-checks the rule itself first (a path on this site: one leading `/`, no scheme, no `//`, no backslash, no whitespace), because a value off the wire is never trusted with `location` — anything else, and an answer that named nothing, keeps the default. The account page's own connect passes no `returnUrl` and is unchanged. Pinned by `test/connections-return.test.js`.
150
+ - **The account page collects the address the ad platforms match on** ([#663](https://github.com/Omega-JS-Stack/omega/issues/663)). The profile form's location block carries an optional **Street** and **Postal code** beside country/region/city (`name="personal.location.street"` / `"personal.location.postalCode"`, browser-autofillable), saved by the same FormManager dot-notation write as every other profile field and loaded back by `core/js/pages/dashboard/account/sections/profile.js` — whose payload names every field it loads, so a new input that is not in that literal is written once and never seen again. The schema fields are `@omega.js/account`'s (`personal.location.postalCode` / `.street`, both nullable strings), and the backend's ONE match reader takes the postal code into the `zip` bag key the ad platforms name: it reaches Meta's `zp` on every server conversion, and GA4's `postal_code` / `sha256_street` on the authenticated requests the Measurement Protocol helper builds from the request's own user ([docs/shared/analytics.md](shared/analytics.md)). No signup field and no new flow. Pinned by `test/account-page.test.js`.
151
+ - **A taxonomy term DISPLAYS humanized** ([#457](https://github.com/Omega-JS-Stack/omega/issues/457)). Terms are aggregated keyed by SLUG with the most frequent spelling winning ([collections.js](../src/collections.js)), and that winner is then humanized the way legacy UJM's blog-taxonomy generator titleized every term it named a page after: hyphens and underscores are word breaks (runs collapse), then each word capitalizes first-char-up/rest-down — `time-tracking-tools` reads "Time Tracking Tools" in its page's `<title>`, h1 and description, on blog tags/categories and on a declared collection's category pages alike. Keeping the corpus's own spelling sentence-cased drifted every migrating brand's taxonomy SEO titles. The SLUG is untouched, so no URL moves, and a tag CLOUD still title-cases the raw spelling it iterates (legacy did the same). The one slug rule that is not "non-alphanumeric runs become `-`": an APOSTROPHE is deleted, straight or typographic, so "Beginner's Guides" slugs to `beginners-guides` the way UJM published it instead of splitting to `beginner-s-guides` ([#597](https://github.com/Omega-JS-Stack/omega/issues/597)) — one slugifier ([`@omega.js/template-kit/jekyll-compat`](../../packages/template-kit/src/jekyll-compat.js)) answers both the taxonomy pages and every template's `| slugify`, so the two can never disagree. Pinned by `test/collections.test.js` and `test/blog-taxonomy-meta.test.js`.
152
+ - **Generated sample content announces itself** ([#208](https://github.com/Omega-JS-Stack/omega/issues/208)). The dev-only sample corpus ([sample-content.js](../src/sample-content.js)) renders on the real posts lane with rolling recent dates, so on screen it is indistinguishable from the brand's own writing — until it says so. The generator stamps `generated: true` into every sample document's frontmatter (never the corpus files, so a newly authored sample cannot forget it), and every packaged theme renders a TEST pill wherever such a document shows: base badges the post page, the listing's featured lead and every post card; newsflash (which forks the blog markup) badges its post page, its lead-story splash and its story-card rows. The pill's STYLE is core-homed and theme-agnostic ([core/css/core/_generated-badge.scss](../core/css/core/_generated-badge.scss)), colored off the `--omega-warn` status token family and never a raw hex; its BOX is the shared `omega-chip` (every site writes `omega-chip omega-generated-badge`, so sizing has one home), and it rides the row's meta line ABOVE the title with `align-self: flex-start`, because a card body is a flex column that stretched it into a full-width band under the title ([#322](https://github.com/Omega-JS-Stack/omega/issues/322)) — but the MARKUP is each theme's own, so what stops a forked blog template from dropping the warning is the packaged-theme sweep in `test/sample-generated-badge.test.js`: every theme's blog `index.html`, `post.html` and row unit must carry the conditional, and a future skin fails that test the moment it ships unbadged. The templates key off the frontmatter flag ALONE — no environment check — because samples never inject into a production build; the same suite pins the dev badge and the production absence.
153
+ - **The consumer's whole `src/assets` tree ships verbatim** ([#295](https://github.com/Omega-JS-Stack/omega/issues/295)). The static phase ([static-assets.js](../src/static-assets.js)) copies the framework's core images, then the brand's minted identity set from `<brandRoot>/.omega/assets`, then every child of `src/assets` LAST so consumer files win collisions — so `src/assets/audio/alarm.mp3` serves at `/assets/audio/alarm.mp3` with no configuration (it used to vanish: only `images/` was copied). The exceptions are the three lanes the ASSET pipeline owns as layer roots — `js/`, `css/`, `fonts/` — whose hashed bundles and pruned font set would be undone by a verbatim copy over the top. imagemin still walks `dist/assets/images` alone. Pinned by `test/static.test.js`.
154
+ - **The VISIBLE logo prefers the bridged vector; the CONFIG value stays a raster** ([#467](https://github.com/Omega-JS-Stack/omega/issues/467)). The manager's assets service mints `color-x.svg` beside the png ladder, and the static bridge already shipped both to `assets/images/brand/brandmark.svg` and `.png` — but every template read `brand.images.brandmark`, which is a png in every brand config, so the nav and footer downloaded a raster for a mark that exists as vector. `brand.images.brandmark` cannot simply flip: the manager's payment service hands that same url to Stripe/PayPal as the product image and OG/social cards need a raster too, so a blind swap breaks payment sync. So the READ moves instead, with no new config key: [static-assets.js](../src/static-assets.js)'s `brandmarkSvgUrl()` reports whether the copies about to ship include a `brandmark.svg` (the mint's, or a consumer's own `src/assets/images/brand/brandmark.svg` winning the same collision), the build records it on the asset manifest as `brandmarkSvg` — exactly the lane the favicon links already ride (`manifest.favicons`) — and every template that renders the VISIBLE lockup reads `assetManifest.brandmarkSvg` first, falling back to the configured raster when no mint produced one: the shared chrome (`frontend/sections/nav.html`, `frontend/sections/footer.html`, `global/sections/app-sidebar.html`) and the pages that carry their own mark because they wear no nav (`pages/auth/{signin,signup,reset,token}.html`, `pages/connections/callback.html`, `pages/app.html`, `pages/payment/checkout.html`, `pages/portal/email-preferences.html`, `pages/extension/installed.html`, and the six card sites on `pages/download.html`). The NON-visible reads stay on the raster deliberately: the OG/social image in `core/head.html`, the JSON-LD logo in `core/foot.html`, and the product image the manager's payment service syncs to Stripe/PayPal. A template never guesses the filename: whether the file exists is a build-time fact or it is nothing. An authored `data.logo.src` (a consumer's `_includes/frontend/sections/nav.json`) still outranks both. Pinned by `test/brandmark-svg.test.js`.
155
+ - **`/test/*` is development-only and NEVER deployed** ([#554](https://github.com/Omega-JS-Stack/omega/issues/554), Ian's call 2026-08-24). A production build emits no page whose permalink is `/test` or sits under `/test/` — the framework's own dev surfaces (`/test`, `/test/styleguide`, `/test/libraries/*`, `/test/translation`, `/test/account/*`, `/test/redirect/*`) and a CONSUMER page that claims one of those URLs alike, the same omission the `/test/sections` showcase gallery already rode. Dev builds keep every one of them, which is the whole point of the lane. The boundary is a path SEGMENT, not a bare prefix: `/testimonials` is brand content and ships. Framework defaults drop out at config time (`frameworkPages`), a consumer's page at a `/test` URL through the shared permalink resolver ([engine.js](../src/engine.js)). Pinned by `test/test-pages-production.test.js`.
156
+ - **Production output carries no framework developer comments** ([#330](https://github.com/Omega-JS-Stack/omega/issues/330)). `omega build` renders every `.html` through the production-only minify transform ([minify-html.js](../src/minify-html.js), `keep_comments: false`) and esbuilds every bundle minified, so the head's developer notes and each module's header never reach a built page. Two classes survive on purpose: IE conditional comments (restored verbatim) and third-party LEGAL blocks (`/*! …`) — plus the one class no bundler can reach, comment text inside a generated document STRING. `omega dev` keeps everything for debuggability, and there is no config knob either way. Pinned by `test/production-comments.test.js`.
157
+ - **URLs are flat `.html` with no trailing slash** (`/signin` → `signin.html`, `page.url` extensionless) — legacy UJM/Jekyll parity, in dev and on static hosts alike.
158
+ - **A site can be served under a URL PATH** ([#355](https://github.com/Omega-JS-Stack/omega/issues/355)). Everything a build emits is root-relative, so a GitHub Pages *project* site (`https://<user>.github.io/<repo>/`) used to 404 on every asset. `OMEGA_PATH_PREFIX=/workkit npx omega build` mounts every emitted URL under that path. The value is a PUBLISHER input, never an end-user one — the machinery that publishes the site knows its mount point (workkit's publish reads it off the Pages API), and hosts that serve at the domain root pass nothing — and it is normalized defensively (leading slash added, trailing slash stripped; `/` and unset both mean the domain root, and then no pass runs, so output is unchanged byte for byte). Three lanes carry it ([path-prefix.js](../src/path-prefix.js)): the rendered HTML (`href`/`src`/`srcset`/`action` — asset URLs, favicons, config-sourced `brand.images.*` and every internal link), the emitted CSS (`url()` targets, the theme sheets' `@font-face` sources), and the runtime, which reads the value off `<html data-omega-path-prefix>` through `core/js/libs/path-prefix.js` instead of baking it into a bundle. Page IDENTITY stays site-relative (`data-page-path` is the route the client's route checks compare against), and `omega dev` serves at the root and never sets one. The asset MANIFEST stays root-relative too: the HTML pass is the ONE place its URLs become markup. Pinned by `test/path-prefix.test.js`. The service worker is the fourth lane ([#360](https://github.com/Omega-JS-Stack/omega/issues/360)): `@omega.js/client` reads the same stamp and registers `<prefix>/service-worker.js` at scope `<prefix>/` — the only scope a script served from there can claim — carrying the value to the worker as the `omega-path-prefix` query param on that script URL, because a worker has no document to read the stamp from; `sw/manager.js` reads it back off `self.location.search` and mounts every site URL it builds (`/build.js`, the default notification icon, the default click target). Pinned by `test/service-worker-mount.test.js`.
159
+ - **The Firebase auth helpers are cached, and a failed fetch is not a failed build** ([#548](https://github.com/Omega-JS-Stack/omega/issues/548)). A production build self-hosts the six `/__/auth/*` + `/__/firebase/init.json` files from `{projectId}.firebaseapp.com` ([firebase-auth-helpers.js](../src/firebase-auth-helpers.js)); every fetch is WRITE-THROUGH to `<brandRoot>/.omega/cache/firebase-auth/<projectId>/`, and any failure — offline, sandboxed, rate-limited, `--cached-only` — serves that last good copy with a loud warn naming the date it was fetched. Nothing cached (a first-ever offline build) stays fatal: a site without the handler breaks sign-in silently, and there is nothing to serve. A framework-baked copy is impossible because `init.json` is project-specific, which is why the cache is keyed by project id, and `demo-*` projects skip the whole lane as before. Pinned by `test/firebase-auth-helpers.test.js`.
160
+ - **One environment surface, in the same call form as every sibling framework** ([#717](https://github.com/Omega-JS-Stack/omega/issues/717)). `getEnvironment()` returns `'development' | 'testing' | 'production'` (mutually exclusive — testing wins), and `isDevelopment()` / `isProduction()` / `isTesting()` DERIVE from it, so they can never disagree; `isProduction()` is a real positive check, never `!isDevelopment()`. Gate side effects on the INTENTIONAL check (`isProduction()` for prod-only, `isDevelopment() || isTesting()` for local-or-test). The vocabulary is `@omega.js/config`'s `ENV_ENVIRONMENTS` ([#586](https://github.com/Omega-JS-Stack/omega/issues/586)) — the same three names the `.env.<environment>` overlay files are spelled with — so a `.env.development` and an `isDevelopment()` can never mean different things. Web's Manager equivalent is the CLI class ([cli.js](../src/cli.js)), which mixes the four in via `attachTo()` exactly as `@omega.js/desktop` and `@omega.js/extension` do (statically and on the prototype), and the package export carries them too. Precedence: `OMEGA_TEST_MODE=true` → the context's own `environment` (the deliberate value the verbs thread — `omega build` → production, `omega dev` → development) → `OMEGA_BUILD_MODE` / `NODE_ENV` → development (a bare context is build tooling; a deployed web artifact carries its environment in `/build.json`). Internally the build lanes call it on their own options object — `getEnvironment.call(options)` in [build.js](../src/build.js) / [engine.js](../src/engine.js) / [service-worker.js](../src/service-worker.js), `isProduction.call(options)` in [limit-collections.js](../src/limit-collections.js) / [decisions.js](../src/decisions.js) — instead of comparing a loose `options.environment` string. The BROWSER half already answered the same four ([runtime/manager.js](../runtime/manager.js), off `window.Configuration.environment`) and is fed by this very value — the engine bakes the resolved environment into the `jekyll` global and `/build.json`, so build-time and page-time can never disagree. Pinned by `test/mode-helpers.test.js`.
161
+ - **Nothing generated is committed.** `dist/`, `.omega/` (including the imagemin and Firebase-auth-helper caches and materialized sample content) are build artifacts; the translation cache is the deliberate exception — it IS committed.
162
+
163
+ ## CLI
164
+
165
+ `npx omega <command>` (alias `omega-web`; bare `omega` prints the listing, as does `help`). Every verb runs `ensureTarget()` first — the local scaffold the retired `omega setup` used to own ([#675](https://github.com/Omega-JS-Stack/omega/issues/675)). It writes no `.env` ([#678](https://github.com/Omega-JS-Stack/omega/issues/678)): the brand root's `.env` is the one file humans and the manager edit, and a target `.env` is an optional per-key override you author yourself.
166
+
167
+ | Command | Description |
168
+ |---|---|
169
+ | `install` | `i local` links every `@omega.js/*` dep from this monorepo; `i live`/`prod` restores registry specs tree-wide (aliases `-i`, `i`) |
170
+ | `dev` | Dev server: Eleventy watch/serve + in-place asset rebuilds, mkcert HTTPS, emulator wiring (aliases `serve`, `start`) |
171
+ | `build` | Production build → `dist/` (alias `-b`) |
172
+ | `test` | Project scope by default; `framework:`/`omega:`/`web:`/`full:` select the framework suite (alias `-t`) |
173
+ | `deploy` | Sync + dispatch the CI build workflow; `--dry-run` prints the POST, `--local` builds only (alias `-d`) |
174
+ | `update` | Dependency freshness report; `--apply` installs the safe set, `--major` explicit (aliases `outdated`, `out`) |
175
+ | `migrate` | UJM consumer → `@omega.js/web`, in place; `--check` for a zero-write report (alias `migration`) |
176
+ | `customize` | Materialize a default page into `src/pages/`; `<path>` materializes ONE shadowable file (section/include/css) at its shadowing path with a provenance header; `--list` prints the layered override map (every shadowable file, its owning layer, your shadows); no argument lists every customizable URL + lane |
177
+ | `translate` | Translate `dist/` into `translation.languages` (`omega build` runs it automatically when enabled) |
178
+ | `purge` | Cloudflare cache purge (alias `cloudflare-purge`) |
179
+ | `clean` | Remove `dist/` + `.omega/` (alias `-c`) |
180
+ | `version` | Print the framework version (alias `-v`) |
181
+ | `audit` | Lighthouse over a production build: full build → `dist/` served on an ephemeral loopback port → the home page plus every page path argument scored. Report-only until a `--min-<category>` flag (`--min-performance=90`, `--min-accessibility`, `--min-best-practices`, `--min-seo`) arms the gate — under = loud failure + exit 1. Every page also prints LCP, CLS and the form factor the run emulated beside the scores, and `--max-lcp=<ms>` arms the perceived-usability bar the same way (any page over it = loud failure + exit 1). Headless Chrome comes from `CHROME_PATH`, else puppeteer's Chrome for Testing, else a system Chrome (alias `-a`, bare form — pass page paths with the full `audit` name) |
182
+
183
+ Alias table: [src/cli.js](../src/cli.js).
184
+
185
+ **The standing performance check** ([#467](https://github.com/Omega-JS-Stack/omega/issues/467)): `npx omega audit --max-lcp=1300` in a brand's website target, run BY HAND before a deploy. Lighthouse's default preset is mobile emulation on Slow 4G, the same shape PageSpeed Insights reports for mobile and the one the #467 proofs measured against (2,588 ms LCP on the home page before the fix wave; the shipped pair after [#763](https://github.com/Omega-JS-Stack/omega/issues/763) brought the above-the-fold fade back is first paint 824 ms and LCP 1,232 ms, gzip, the LCP landing at the end of the fade rather than at first paint), so the printed LCP is comparable run to run and `--max-lcp=1300` is the perceived-usability bar. Deploys are deliberate ([docs/shared/deploys.md](shared/deploys.md)), so there is no scheduled job: the check belongs beside the deploy decision, on a full production build. The fast `omega test` tier never runs Lighthouse; its always-on cheap half is the dist audit ([#468](https://github.com/Omega-JS-Stack/omega/issues/468)). The bar is 1,300 ms, the end of the fade, by ruling (Ian 2026-09-09): the fade is the design, first paint stays under 1 s, and 1.3 s is about half of Core Web Vitals' "good" LCP line.
186
+
187
+ ## Dependency Resolution
188
+
189
+ - **`@omega.js/client` owns Firebase on the client side.** Page and module code never imports Firebase directly — `omega.firestore()`, `omega.auth()`. It is a real runtime dependency here, never vendored.
190
+ - **`@omega.js/config`, `@omega.js/devkit`, `@omega.js/template-kit` are devDependencies** — private packages vendored into the published tarball by the `vendor` prepare hook. Consumers never install them.
191
+ - **Bundler aliases**: `__main_assets__/*` → the core layer / themes dir, `__theme__/*` → the active theme (base fallback), `omega:<name>` → the same name from the layers BELOW the importing file (sass's `omega:` importer, spelled for JS — [#624](https://github.com/Omega-JS-Stack/omega/issues/624)), `@omega.js/client` (subpaths included) → the client package.
192
+ - **Anything the framework depends on, a consumer imports bare; anything else it declares itself** ([#2](https://github.com/Omega-JS-Stack/omega/issues/2)). A consumer page module's `import { defineChart } from '@tanstack/charts'` resolves from **`@omega.js/web`'s own installation** — the resolve hook [devkit's `bundle`](../devkit/index.md) composes into every build reads the framework package.json's `dependencies` at build time (no curated list — the declared set IS the list) and re-resolves matching bare specifiers and their subpaths from the package root. Consequences: the framework's copy wins even when the consumer declares its own (one copy, one shared chunk with core's own users of the library — the charts helper's @tanstack/charts, DOMPurify, Popper), and a name the framework does not declare resolves normally from the consumer, failing with esbuild's usual `Could not resolve` when it is missing. Build-time-only dependencies (esbuild, sass, sharp) need no exclusion: resolution happens on demand, so nothing enters a bundle unless browser code imports it by name. No CDN `loadScript()` workaround, and offline apps keep working.
193
+
194
+ ## Development Workflow
195
+
196
+ - **🚫 NEVER use `npx omega ...` from the framework repo** — it is for CONSUMER projects only. Use `npm test`, `npm run prepare`, etc.
197
+ - **🚫 NEVER run a consumer's `omega dev`** — it is the user's long-running dev process. Assume it is running; if it is not, ask the user to start it rather than starting it yourself.
198
+ - **Logs, not scrollback.** Every verb tees its whole run to the target root, truncated per launch and ANSI-stripped: `logs/dev.log` (`omega dev`), `logs/build.log` (`omega build`), `logs/test.log` (`omega test`). Grep them — "did the watcher rebuild?", "what did the last build print?" is a `tail`, never a restart. The mechanism and the full per-surface path table: [docs/shared/logging.md](shared/logging.md).
199
+ - **Prove changes against a real brand.** `brands/omega-playground` and `brands/newsflash-brand` keep both first-party skins alive; the corpus and cross-stack e2e lanes gate behavior changes ([docs/shared/testing.md](shared/testing.md)).
200
+ - **Dev watch targets register themselves — never hand-register one** ([#200](https://github.com/Omega-JS-Stack/omega/issues/200)). Config-time code reads files ONLY through `@omega.js/devkit/reads` ([reads.js](../../packages/devkit/src/reads.js) — devkit-homed per the build-time-harmonization ruling, any framework dev loop may adopt it; `readdir`/`read`/`dirExists`/`fileExists`); each read records its directory, and the recorded union becomes the config-reset watch targets (pruned to ancestors, realpathed outside the consumer dir, missing dirs pre-armed). A direct `fs` read in a config-time module fails the guard test, which names the helper. Scan-shaped questions ("does the brand own posts?", the permalink set) ride a lighter RESCAN lane instead: [decisions.js](../src/decisions.js) re-answers them on their own file events and every rebuild reads fresh answers, so a brand's first real post retires the sample corpus live, with ordinary page edits staying incremental. Permalink collisions surface from the same scan: a loud `[@omega.js/web:decisions]` diagnostic in dev, a failed build in production. Boot-phase reads (the omega.json5 merge, sample-content materialization) stay restart-expected.
201
+ - **Live-verify UI changes via CDP.** Use the `chrome-devtools` MCP tools (screenshot, click, evaluate, console) against the running dev server instead of guessing at rendered output.
202
+ - **Signing in during dev is URL-only.** `npx omega auth:token <uid-or-email>` (the backend CLI — [docs/backend/index.md](../backend/index.md); emulator by default, `--production` explicit) mints a custom token and prints the sign-in URL; open `/signin?authCustomToken=<token>` (with an optional `&authReturnUrl=…`) and the page signs that user in, then goes to the return URL. `?authSignout=true` is the same lane in reverse. `?authPrivateKey=<api.privateKey>` is the DURABLE version, for a URL somebody saves (an OBS dock, a kiosk, a bookmark): a custom token expires in an hour, so the page trades the key for a fresh one at `POST /omega/user/token` on every load, strips the key from the address bar, and never logs it. Every one of these params is read ONLY by the auth pages — `/signin`, `/signup`, `/reset` via [session-params.js](../core/js/libs/auth/session-params.js) — so appending them to any other page does nothing.
203
+ - **Provider signin runs the REDIRECT flow in dev, exactly as it does in production** ([#156](https://github.com/Omega-JS-Stack/omega/issues/156)). The auth emulator's OAuth handler hands the credential back through `sessionStorage` on the origin it is served from, so on the emulator's own port (`http://localhost:9099`) it is a third party to the site: the browser partitions that storage by top-level site, the SDK's helper iframe reads an empty partition, and `getRedirectResult()` resolves null forever. `omega dev` closes that gap by PROXYING the emulator under the site origin — `/emulator/*` plus the `identitytoolkit`/`securetoken` REST prefixes, mounted at the site root because `connectAuthEmulator()` discards any path on the URL it is given — so handler and iframe are first-party and share one partition. It is the dev counterpart of the self-hosted `/__/auth/*` helpers a production build ships. The dev server declares the proxy in the page chrome (`dev.authEmulatorProxy`) and @omega.js/client then points the emulator at `window.location.origin`. [oauth.js](../core/js/libs/auth/oauth.js) keeps the popup for exactly two cases: an iframed page and the `?authPopup=true` override. A redirect that comes home empty is loud, not silent: the page reports it and says so inline.
204
+
205
+ ## CI secrets — `.env` is the source ([#189](https://github.com/Omega-JS-Stack/omega/issues/189))
206
+
207
+ `omega deploy` publishes the target's **composed env** to the brand repo's **GitHub Actions secrets** as a precheck before it dispatches, and every verb's `ensureTarget()` regenerates the scaffolded workflow's env block — one `KEY: ${{ secrets.KEY }}` line per published key — so CI reads the same key set the laptop does. Nothing is hand-created and nothing is hand-edited: add the key to the brand root's `.env`, deploy.
208
+
209
+ - **Collected**: `composeTargetEnv({ targetDir, target: 'web' })` ([#678](https://github.com/Omega-JS-Stack/omega/issues/678)) — company `.env` ← brand `.env` ← target `.env`, the brand-side layers filtered by the env schema to the keys the web target reads (`deliverAs` applied, so the brand's `GOOGLE_ANALYTICS_SECRET_WEB` publishes as `GOOGLE_ANALYTICS_SECRET`), the target layer passing through unfiltered. The brand root's `.env` is the one file a human keeps; a target `.env` is an optional per-key override that no machine writes. An empty value never claims a key, and the name must be UPPER_SNAKE.
210
+ - **FILES only, never the shell**: a key that exists only in the shell is never published, and a shell value never overrides a file's — CI gets what the brand's files say, under any shell.
211
+ - **Except the machine-local lane** ([#454](https://github.com/Omega-JS-Stack/omega/issues/454)): the env schema's `machineLocal: true` flag ([env-schema.js](../../packages/config/src/env-schema.js)) marks the developer-tooling keys that live in `.env` but are not secrets. They never enroll as Actions secrets and never inject into a workflow — the drop happens once, in `deliveredNames()` ([env-delivery.js](../../packages/config/src/env-delivery.js)), the source of both sets. The flag is the seam a future key joins; today it is `OMEGA_FONTAWESOME_ROOT` alone.
212
+ - **The CI FontAwesome story**: `OMEGA_FONTAWESOME_ROOT` is a path to the developer's local Pro download (the fleet convention sets `~/.omega/fontawesome` in every brand-root `.env`). That path does not exist on a runner, so publishing it churned the secret list per machine and handed CI a dead path that fell back silently. **CI resolves Pro icons through the `@fortawesome` npm token lane instead** — the registry auth of [docs/shared/icons.md](shared/icons.md), with `@fortawesome/fontawesome-pro` installed from it; the free floor is the fallback when no token is configured. The filesystem root is a laptop convenience only.
213
+ - **Transport**: the `gh` CLI (`@omega.js/devkit/actions-secrets`), values on **stdin**, never in argv and never logged. No usable `gh` fails LOUD with install/auth instructions — `omega deploy --no-secrets` is the opt-out (the same flag name on desktop and extension). Publishing is unconditional (the API can't report a secret's current value, so "unchanged" isn't detectable); setting the same value twice is harmless.
214
+ - **Skips loudly** (a `[deploy]` line, never silence): CI itself, a target with nothing composed, no git remote, or a remote that isn't the brand's own repo (`repo.providers.github` names a different one — a target inside a framework/test monorepo never arms that monorepo's Actions).
215
+ - The workflow's `env:` block is FRAMEWORK-OWNED and rewritten by every verb's `ensureTarget()`; `GH_TOKEN`/`CLOUDFLARE_TOKEN`/`NODE_VERSION`/`NODE_ENV` stay declared by the template and never duplicate into the generated region.
216
+ - **In a brand monorepo the workflow lives at the BRAND ROOT** ([#265](https://github.com/Omega-JS-Stack/omega/issues/265)): GitHub runs workflows from the repo root's `.github/workflows/` only, so the scaffold skips `.github/**` in the target and composes the template into `<brand>/.github/workflows/<target>-build.yml` via `@omega.js/devkit/ci-workflows` — every post-checkout `run:` step scoped to the target (a per-step `working-directory:`, never a workflow-level `defaults.run.working-directory` — it would scope the pre-checkout steps too, where the target dir does not exist yet), the path inputs an action ignores `working-directory:` for rewritten to the target dir (`actions/cache`'s `path` + its `hashFiles()` key pattern, `peaceiris/actions-gh-pages`'s `publish_dir` — otherwise the publish pushes a repo-root `dist/` no build wrote), a per-target concurrency group, the same `.env` secrets block rendered in, regenerated on every verb (a rerun rewrites that one file, never duplicates), and the target's dead copy swept when the framework wrote every line of it: an exact match, or a copy a SUPERSEDED template wrote (the pre-secrets-block copies #189 left behind are the same file minus the lines it added, [#334](https://github.com/Omega-JS-Stack/omega/issues/334)); a copy carrying a line no current template ships is kept and reported instead (an edit that ONLY deletes template lines still reads as framework-written and is swept). `omega deploy` dispatches the composed name (`website-build.yml`) in a brand and the plain `build.yml` standalone. A standalone project is its own repo root and keeps its `.github/workflows/build.yml`.
217
+
218
+ ## Supply-Chain Security
219
+
220
+ All `npm install` calls in CLI commands (`npx omega i`, the local-linking flows) route through devkit's `safeInstall()` helper (`@omega.js/devkit/safe-install`, invoked by `@omega.js/devkit/local`). It prefixes `sfw` (Socket Firewall) when installed — blocking confirmed malware at the network level before packages reach disk — and falls back to plain npm when sfw is absent. Installs **fail if sfw detects confirmed malware** anywhere in the dependency tree; non-critical CVEs and quality warnings pass through.
221
+
222
+ ## File Conventions
223
+
224
+ - **CommonJS** in `src/` (prepare-package copies `src/` → `dist/` 1:1, no transforms). **ESM** in `runtime/` and `sw/` — those are browser bundles.
225
+ - **`fs-jetpack`** over `fs` / `fs-extra` for file operations.
226
+ - One `module.exports = ...` per file; **short-circuit early returns** rather than nested ifs; **logical operators at the start of continuation lines**.
227
+ - **No backwards compatibility** unless explicitly requested.
228
+ - Every module carries a top-of-file JSDoc block naming what it owns — match it when adding one.
229
+ - **Tests are `node --test`**, one file per concern under `test/`, real builds over fixtures — never mock the engine.
230
+
231
+ ## Doc-update parity
232
+
233
+ Whenever you make a behavioral change (new command, new flag, new contract, removed feature), update:
234
+
235
+ 1. **[README.md](../README.md)** — the long-form package reference
236
+ 2. **`docs/web/index.md`** (this file) — the map, one line or a cross-link
237
+ 3. **`shared/<topic>.md`** and the web deep references here in `docs/web/` — the deep reference (sections, theming, testing, deploys, updates, translation, icons, config)
238
+ 4. **`../../CHANGELOG.md`** — `[Unreleased]`, on ship
239
+
240
+ Validate first, then document — write docs that describe shipped reality, not intentions.
241
+
242
+ ## Documentation
243
+
244
+ - [README.md](../README.md) — the package's own long-form reference (module map, packaged content, URL shape, service worker, tokens, theming tiers, app shell, pricing, responsive images, the test-pinned engine facts, and what is not built yet)
245
+ - [docs/web/sections.md](sections.md) — the section/component contract · [docs/web/omega-sections-spec.md](omega-sections-spec.md) — the ratified architecture spec
246
+ - [docs/web/page-contract.md](page-contract.md) — the five paint rules every page owes a visitor: immediate static content, bindings for user data, one-shot answers, deadlines with named fallbacks, gated form submits
247
+ - [docs/web/libs.md](libs.md): the `core/js/libs/` inventory, covering the `__main_assets__` import idiom, every shared browser module with its exports, and the auth cluster
248
+ - [docs/shared/theming.md](shared/theming.md) — the `--omega-*` design-system contract · [docs/web/classy-v2/DIRECTION.md](classy-v2/DIRECTION.md) — the visual spec
249
+ - [docs/shared/config.md](shared/config.md) — omega.json5 shape, merge chain, and the legacy mapping tables `omega migrate` implements
250
+ - [docs/test-framework.md](../docs/test-framework.md) — the consumer test guide: the layered doctrine, what `omega test` builds and smoke-checks (the internal link check and the four audit checks — meta, fragments, alt, sitemap orphans — with their shared `config/link-exceptions.json5`), the scope grammar, authoring `node --test` suites
251
+ - [docs/shared/testing.md](shared/testing.md) — the verification tiers and which lane gates what
252
+ - [docs/shared/deploys.md](shared/deploys.md) · [docs/shared/updates.md](shared/updates.md) · [docs/shared/translation.md](shared/translation.md) · [docs/shared/icons.md](shared/icons.md) · [docs/web/ads-system.md](ads-system.md)
253
+ - [docs/shared/local-dev.md](shared/local-dev.md) — linking this monorepo into a brand · [docs/shared/brands.md](shared/brands.md) — which brand is which
254
+ - [docs/shared/publishing.md](shared/publishing.md) — the private-latch policy and the proving checkpoint