@invisibleloop/pulse 0.20.10 → 0.20.19

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 (266) hide show
  1. package/package.json +6 -5
  2. package/public/.pulse-ui-version +1 -1
  3. package/scripts/build.js +26 -0
  4. package/scripts/release-version.js +17 -8
  5. package/scripts/strip-server.test.js +60 -0
  6. package/src/agent/checklist.md +8 -12
  7. package/src/agent/commands/pulse-start.md +2 -0
  8. package/src/agent/commands/verify.md +30 -8
  9. package/src/agent/guide-components.md +7 -25
  10. package/src/agent/guide-routing.md +0 -12
  11. package/src/agent/guide-server.md +102 -1
  12. package/src/agent/guide-spec.md +8 -1
  13. package/src/agent/guide-styles.md +23 -0
  14. package/src/agent/identity.md +9 -0
  15. package/src/agent/workflow.md +4 -23
  16. package/src/cli/check-bundles.js +113 -0
  17. package/src/cli/check-bundles.test.js +137 -0
  18. package/src/cli/cli.test.js +105 -0
  19. package/src/cli/dev.js +48 -3
  20. package/src/cli/diagnose.js +105 -0
  21. package/src/cli/diagnose.test.js +124 -0
  22. package/src/cli/discover.js +9 -2
  23. package/src/cli/index.js +171 -9
  24. package/src/cli/process-utils.js +57 -0
  25. package/src/cli/process-utils.test.js +114 -0
  26. package/src/cli/review.js +391 -0
  27. package/src/cli/review.test.js +143 -0
  28. package/src/cli/scaffold.js +9 -4
  29. package/src/cli/stamp.js +102 -0
  30. package/src/cli/stamp.test.js +149 -0
  31. package/src/cli/start.js +15 -2
  32. package/src/cli/validate.js +184 -0
  33. package/src/cli/validate.test.js +153 -0
  34. package/src/mcp/mcp.test.js +261 -30
  35. package/src/mcp/server.js +207 -515
  36. package/src/mcp/validate-worker.js +18 -1
  37. package/src/runtime/index.js +144 -11
  38. package/src/runtime/runtime.test.js +199 -0
  39. package/src/runtime/ssr.js +14 -7
  40. package/src/runtime/ssr.test.js +31 -0
  41. package/src/server/index.js +163 -10
  42. package/src/server/server.test.js +225 -2
  43. package/src/spec/schema.js +1 -1
  44. package/src/ui/index.js +0 -5
  45. package/src/ui/ui.test.js +0 -75
  46. package/types/ui.d.ts +0 -21
  47. package/.claude/commands/build-page.md +0 -63
  48. package/.claude/commands/new-doc-page.md +0 -45
  49. package/.claude/commands/pulse-start.md +0 -11
  50. package/.claude/commands/pulse-stop.md +0 -5
  51. package/.claude/commands/verify.md +0 -118
  52. package/.claude/pulse-checklist.md +0 -189
  53. package/.github/workflows/ci.yml +0 -25
  54. package/.github/workflows/publish.yml +0 -45
  55. package/.pulse/load-reports/home/1773432711417.json +0 -22
  56. package/.pulse-verified +0 -1
  57. package/AGENTS.md +0 -697
  58. package/CLAUDE.md +0 -697
  59. package/README.md +0 -95
  60. package/benchmark/public/dist/counter.boot-PO6SNSJZ.js +0 -8
  61. package/benchmark/public/dist/home.boot-YK6VAEQI.js +0 -6
  62. package/benchmark/public/dist/manifest.json +0 -5
  63. package/benchmark/public/dist/runtime-5Y7ESAIB.js +0 -2
  64. package/benchmark/public/dist/runtime-KO4BHUQ3.js +0 -49
  65. package/benchmark/results/2026-03-27-09-20.json +0 -188
  66. package/benchmark/results/2026-03-27-09-21.json +0 -188
  67. package/benchmark/results/2026-03-27-09-29.json +0 -188
  68. package/benchmark/results/2026-03-27-10-14.json +0 -188
  69. package/benchmark/results/2026-03-27-10-18.json +0 -232
  70. package/benchmark/src/pages/counter.js +0 -37
  71. package/benchmark/src/pages/home.js +0 -21
  72. package/docs/.claude/commands/pulse-build.md +0 -7
  73. package/docs/.claude/commands/pulse-dev.md +0 -7
  74. package/docs/.claude/commands/pulse-start.md +0 -9
  75. package/docs/.claude/commands/pulse-stop.md +0 -5
  76. package/docs/.claude/commands/verify.md +0 -84
  77. package/docs/.claude/coverage-check.js +0 -196
  78. package/docs/.claude/pulse-checklist.md +0 -189
  79. package/docs/public/.pulse-ui-version +0 -1
  80. package/docs/public/andypixel.css +0 -237
  81. package/docs/public/bookworms.css +0 -422
  82. package/docs/public/coutumes.css +0 -561
  83. package/docs/public/docs.css +0 -2388
  84. package/docs/public/favicon.svg +0 -4
  85. package/docs/public/home-brut.css +0 -982
  86. package/docs/public/images/bookworms/hero-books.jpg +0 -0
  87. package/docs/public/images/bookworms/how-we-read-now.jpg +0 -0
  88. package/docs/public/images/bookworms/lantern-keepers.jpg +0 -0
  89. package/docs/public/images/bookworms/provincial-bookseller.jpg +0 -0
  90. package/docs/public/images/journey/178.jpg +0 -0
  91. package/docs/public/images/journey/29.jpg +0 -0
  92. package/docs/public/images/retrowheels/hero.jpg +0 -0
  93. package/docs/public/images/shop/devbook-pro.jpg +0 -0
  94. package/docs/public/images/suzukibento/1015.jpg +0 -0
  95. package/docs/public/images/suzukibento/1025.jpg +0 -0
  96. package/docs/public/images/suzukibento/1043.jpg +0 -0
  97. package/docs/public/images/suzukibento/1059.jpg +0 -0
  98. package/docs/public/images/suzukibento/1062.jpg +0 -0
  99. package/docs/public/images/suzukibento/1084.jpg +0 -0
  100. package/docs/public/images/swish/1469334031218-e382a71b716b.jpg +0 -0
  101. package/docs/public/images/swish/1483985988355-763728e1935b.jpg +0 -0
  102. package/docs/public/images/swish/1485518882345-15568b007407.jpg +0 -0
  103. package/docs/public/images/swish/1490481651871-ab68de25d43d.jpg +0 -0
  104. package/docs/public/images/swish/1496747611176-843222e1e57c.jpg +0 -0
  105. package/docs/public/images/swish/1502716119720-b23a93e5fe1b.jpg +0 -0
  106. package/docs/public/images/swish/1539109136881-3be0616acf4b.jpg +0 -0
  107. package/docs/public/images/swish/1551803091-e20673f15770.jpg +0 -0
  108. package/docs/public/images/swish/1558769132-cb1aea458c5e.jpg +0 -0
  109. package/docs/public/images/swish/1572804013309-59a88b7e92f1.jpg +0 -0
  110. package/docs/public/img/heic1501a.jpg +0 -0
  111. package/docs/public/img/placeholder-landscape.svg +0 -18
  112. package/docs/public/img/placeholder-square.svg +0 -14
  113. package/docs/public/img/placeholder-tall.svg +0 -22
  114. package/docs/public/journey.css +0 -528
  115. package/docs/public/menu.js +0 -327
  116. package/docs/public/pulse-ui.css +0 -3028
  117. package/docs/public/pulse-ui.js +0 -257
  118. package/docs/public/retrowheels.css +0 -425
  119. package/docs/public/shop.css +0 -269
  120. package/docs/public/sparkys.css +0 -382
  121. package/docs/public/suzukibento.css +0 -517
  122. package/docs/public/swish.css +0 -394
  123. package/docs/public/theme.css +0 -52
  124. package/docs/public/themes/lumio.css +0 -50
  125. package/docs/public/themes/observatory.css +0 -104
  126. package/docs/server.js +0 -161
  127. package/docs/src/lib/component-page.js +0 -49
  128. package/docs/src/lib/highlight.js +0 -2
  129. package/docs/src/lib/layout.js +0 -190
  130. package/docs/src/lib/metrics-store.js +0 -6
  131. package/docs/src/lib/nav.js +0 -171
  132. package/docs/src/lib/stats.js +0 -81
  133. package/docs/src/pages/accessibility.js +0 -157
  134. package/docs/src/pages/actions.js +0 -194
  135. package/docs/src/pages/auth.js +0 -176
  136. package/docs/src/pages/caching.js +0 -95
  137. package/docs/src/pages/components/accordion.js +0 -48
  138. package/docs/src/pages/components/alert.js +0 -35
  139. package/docs/src/pages/components/app-badge.js +0 -41
  140. package/docs/src/pages/components/avatar.js +0 -35
  141. package/docs/src/pages/components/badge.js +0 -36
  142. package/docs/src/pages/components/breadcrumbs.js +0 -94
  143. package/docs/src/pages/components/button.js +0 -84
  144. package/docs/src/pages/components/card.js +0 -225
  145. package/docs/src/pages/components/charts.js +0 -283
  146. package/docs/src/pages/components/checkbox.js +0 -129
  147. package/docs/src/pages/components/cluster.js +0 -47
  148. package/docs/src/pages/components/container.js +0 -40
  149. package/docs/src/pages/components/cta.js +0 -54
  150. package/docs/src/pages/components/divider.js +0 -37
  151. package/docs/src/pages/components/empty.js +0 -36
  152. package/docs/src/pages/components/feature.js +0 -60
  153. package/docs/src/pages/components/fieldset.js +0 -65
  154. package/docs/src/pages/components/fileupload.js +0 -128
  155. package/docs/src/pages/components/footer.js +0 -58
  156. package/docs/src/pages/components/grid.js +0 -169
  157. package/docs/src/pages/components/heading.js +0 -107
  158. package/docs/src/pages/components/hero.js +0 -123
  159. package/docs/src/pages/components/icons.js +0 -410
  160. package/docs/src/pages/components/image.js +0 -71
  161. package/docs/src/pages/components/input.js +0 -51
  162. package/docs/src/pages/components/media.js +0 -51
  163. package/docs/src/pages/components/modal.js +0 -111
  164. package/docs/src/pages/components/nav.js +0 -136
  165. package/docs/src/pages/components/phone-frame.js +0 -74
  166. package/docs/src/pages/components/progress.js +0 -102
  167. package/docs/src/pages/components/prose.js +0 -111
  168. package/docs/src/pages/components/pullquote.js +0 -71
  169. package/docs/src/pages/components/radio.js +0 -194
  170. package/docs/src/pages/components/rating.js +0 -106
  171. package/docs/src/pages/components/search.js +0 -61
  172. package/docs/src/pages/components/section.js +0 -98
  173. package/docs/src/pages/components/segmented.js +0 -121
  174. package/docs/src/pages/components/select.js +0 -45
  175. package/docs/src/pages/components/slider.js +0 -115
  176. package/docs/src/pages/components/spinner.js +0 -73
  177. package/docs/src/pages/components/stack.js +0 -48
  178. package/docs/src/pages/components/stat.js +0 -55
  179. package/docs/src/pages/components/stepper.js +0 -66
  180. package/docs/src/pages/components/table.js +0 -45
  181. package/docs/src/pages/components/textarea.js +0 -31
  182. package/docs/src/pages/components/toggle.js +0 -84
  183. package/docs/src/pages/components/tooltip.js +0 -48
  184. package/docs/src/pages/components.js +0 -200
  185. package/docs/src/pages/config.js +0 -193
  186. package/docs/src/pages/constraints.js +0 -100
  187. package/docs/src/pages/deploy.js +0 -233
  188. package/docs/src/pages/error-pages.js +0 -99
  189. package/docs/src/pages/extending.js +0 -198
  190. package/docs/src/pages/faq.js +0 -99
  191. package/docs/src/pages/forms.js +0 -129
  192. package/docs/src/pages/getting-started.js +0 -147
  193. package/docs/src/pages/guard.js +0 -120
  194. package/docs/src/pages/home.js +0 -345
  195. package/docs/src/pages/how-it-works.js +0 -244
  196. package/docs/src/pages/hydration.js +0 -108
  197. package/docs/src/pages/images.js +0 -120
  198. package/docs/src/pages/markdown.js +0 -156
  199. package/docs/src/pages/meta.js +0 -193
  200. package/docs/src/pages/mutations.js +0 -107
  201. package/docs/src/pages/navigation.js +0 -85
  202. package/docs/src/pages/performance.js +0 -157
  203. package/docs/src/pages/persist.js +0 -89
  204. package/docs/src/pages/project-structure.js +0 -91
  205. package/docs/src/pages/prompt-examples.js +0 -186
  206. package/docs/src/pages/raw-responses.js +0 -122
  207. package/docs/src/pages/routing.js +0 -120
  208. package/docs/src/pages/server-api.js +0 -291
  209. package/docs/src/pages/server-data.js +0 -203
  210. package/docs/src/pages/slash-commands.js +0 -55
  211. package/docs/src/pages/spec.js +0 -207
  212. package/docs/src/pages/state.js +0 -102
  213. package/docs/src/pages/store.js +0 -203
  214. package/docs/src/pages/streaming.js +0 -107
  215. package/docs/src/pages/stripe.js +0 -194
  216. package/docs/src/pages/supabase.js +0 -324
  217. package/docs/src/pages/templates/andypixel.js +0 -260
  218. package/docs/src/pages/templates/blog-post.js +0 -226
  219. package/docs/src/pages/templates/bookworms.js +0 -227
  220. package/docs/src/pages/templates/coutumes.js +0 -237
  221. package/docs/src/pages/templates/journey.js +0 -186
  222. package/docs/src/pages/templates/mobile-app.js +0 -371
  223. package/docs/src/pages/templates/retrowheels.js +0 -355
  224. package/docs/src/pages/templates/shop.js +0 -268
  225. package/docs/src/pages/templates/sparkys.js +0 -264
  226. package/docs/src/pages/templates/suzukibento.js +0 -200
  227. package/docs/src/pages/templates/swish.js +0 -313
  228. package/docs/src/pages/templates.js +0 -534
  229. package/docs/src/pages/testing.js +0 -199
  230. package/docs/src/pages/validation.js +0 -138
  231. package/examples/.claude/commands/pulse-build.md +0 -7
  232. package/examples/.claude/commands/pulse-dev.md +0 -7
  233. package/examples/.claude/commands/pulse-start.md +0 -9
  234. package/examples/.claude/commands/pulse-stop.md +0 -5
  235. package/examples/.claude/commands/verify.md +0 -84
  236. package/examples/.claude/coverage-check.js +0 -196
  237. package/examples/.claude/pulse-checklist.md +0 -189
  238. package/examples/andypixel.css +0 -237
  239. package/examples/andypixel.js +0 -258
  240. package/examples/bookworms.css +0 -422
  241. package/examples/bookworms.js +0 -225
  242. package/examples/contact.js +0 -166
  243. package/examples/counter.js +0 -94
  244. package/examples/dev.server.js +0 -110
  245. package/examples/examples.test.js +0 -394
  246. package/examples/journey.css +0 -528
  247. package/examples/journey.js +0 -186
  248. package/examples/pricing.js +0 -262
  249. package/examples/products.js +0 -190
  250. package/examples/public/.pulse-ui-version +0 -1
  251. package/examples/public/pulse-ui.css +0 -3028
  252. package/examples/public/pulse-ui.js +0 -257
  253. package/examples/quiz.js +0 -208
  254. package/examples/retrowheels.css +0 -425
  255. package/examples/retrowheels.js +0 -353
  256. package/examples/shared.js +0 -84
  257. package/examples/suzukibento.css +0 -513
  258. package/examples/suzukibento.js +0 -200
  259. package/examples/swish.css +0 -394
  260. package/examples/swish.js +0 -311
  261. package/examples/todos.js +0 -162
  262. package/src/ui/fieldset.js +0 -37
  263. package/src/ui/phone-frame.js +0 -40
  264. package/src/ui/search.js +0 -78
  265. package/src/ui/segmented.js +0 -50
  266. package/src/ui/tooltip.js +0 -28
@@ -1,371 +0,0 @@
1
- /**
2
- * Template: Mobile App Landing Page
3
- *
4
- * A complete landing page for a mobile app — nav, hero, features,
5
- * stats, testimonials, pricing, FAQ, CTA, footer.
6
- * Built entirely from Pulse UI components (testimonials and pricing tiers
7
- * use card() — the former testimonial()/pricing() components were removed
8
- * from the library as narrow single-purpose wrappers).
9
- */
10
-
11
- import { asset } from '../../lib/layout.js'
12
- import {
13
- nav as uiNav,
14
- hero,
15
- phoneFrame,
16
- section,
17
- container,
18
- grid,
19
- feature,
20
- stat,
21
- card,
22
- badge,
23
- accordion,
24
- cta,
25
- footer,
26
- appBadge,
27
- button,
28
- iconFeather,
29
- iconAi,
30
- iconSmile,
31
- iconLock,
32
- iconZap,
33
- iconRefresh,
34
- iconBarChart,
35
- iconStar,
36
- } from '../../../../src/ui/index.js'
37
-
38
- // ── Navigation ────────────────────────────────────────────────────────────────
39
-
40
- const pageNav = uiNav({
41
- logo: `${iconFeather({ size: 20 })}<span class="u-font-bold" style="letter-spacing:-0.02em;color:#fff">Lumio</span>`,
42
- links: [
43
- { label: 'Features', href: '#features' },
44
- { label: 'Pricing', href: '#pricing' },
45
- { label: 'Testimonials', href: '#testimonials' },
46
- { label: 'FAQ', href: '#faq' },
47
- ],
48
- action: button({ label: 'Download Free', href: '#download', size: 'sm' }),
49
- sticky: true,
50
- background: 'rgba(58, 78, 88, 0.75)',
51
- color: '#fff',
52
- })
53
-
54
- // ── Hero ──────────────────────────────────────────────────────────────────────
55
-
56
- const phoneScreen = `
57
- <div style="background:#1a2c24;height:100%;padding:0.75rem;display:flex;flex-direction:column;gap:0.625rem;font-family:system-ui,sans-serif;">
58
-
59
- <!-- status bar -->
60
- <div style="display:flex;justify-content:space-between;align-items:center;padding:0 0.25rem;font-size:0.6rem;color:rgba(255,255,255,0.5)">
61
- <span>9:41</span>
62
- <div style="display:flex;gap:4px;align-items:center">
63
- <svg width="12" height="8" viewBox="0 0 12 8" fill="rgba(255,255,255,0.5)" aria-hidden="true"><rect x="0" y="2" width="2" height="6" rx="1"/><rect x="3" y="1" width="2" height="7" rx="1"/><rect x="6" y="0" width="2" height="8" rx="1"/><rect x="9" y="0" width="2" height="8" rx="1" opacity="0.3"/></svg>
64
- <svg width="10" height="8" viewBox="0 0 10 8" fill="rgba(255,255,255,0.5)" aria-hidden="true"><path d="M5 1.5C3.1 1.5 1.4 2.3 0.2 3.6L1.6 5c0.9-1 2.1-1.5 3.4-1.5s2.5.6 3.4 1.5L9.8 3.6C8.6 2.3 6.9 1.5 5 1.5zm0 3L6.4 6c-.4-.4-.9-.6-1.4-.6s-1 .2-1.4.6L5 4.5zm0-6C2.1-1.5.2-.6-1.2.8L.2 2.2C1.9.8 3.4 0 5 0s3.1.8 4.8 2.2L11.2.8C9.8-.6 7.9-1.5 5-1.5z" transform="translate(0,1.5)"/></svg>
65
- <svg width="20" height="8" viewBox="0 0 20 8" fill="none" aria-hidden="true"><rect x="0.5" y="0.5" width="16" height="7" rx="2" stroke="rgba(255,255,255,0.35)" stroke-width="1"/><rect x="1.5" y="1.5" width="12" height="5" rx="1" fill="rgba(255,255,255,0.7)"/><path d="M18 3v2c.8-.3.8-1.7 0-2z" fill="rgba(255,255,255,0.6)"/></svg>
66
- </div>
67
- </div>
68
-
69
- <!-- greeting + streak badge -->
70
- <div style="display:flex;justify-content:space-between;align-items:center">
71
- <div>
72
- <div style="font-size:0.62rem;color:rgba(255,255,255,0.55)">Good morning</div>
73
- <div style="font-size:0.85rem;font-weight:700;color:#fff">Sarah</div>
74
- </div>
75
- <div style="background:rgba(229,193,189,0.18);border:1px solid rgba(229,193,189,0.35);border-radius:999px;padding:0.2rem 0.5rem;display:flex;align-items:center;gap:0.25rem;font-size:0.6rem;color:#e5c1bd;font-weight:600">
76
- 🔥 28
77
- </div>
78
- </div>
79
-
80
- <!-- today's entry card -->
81
- <div style="background:linear-gradient(135deg,#2a4d3c,#3a6650);border-radius:0.75rem;padding:0.75rem;flex-shrink:0">
82
- <div style="font-size:0.6rem;color:rgba(255,255,255,0.6);margin-bottom:0.3rem;text-transform:uppercase;letter-spacing:0.04em">Today's entry</div>
83
- <div style="font-size:0.78rem;font-weight:600;color:#fff;margin-bottom:0.2rem">Morning reflection</div>
84
- <div style="font-size:0.65rem;color:rgba(255,255,255,0.7);line-height:1.4">Feeling grateful today. The early morning…</div>
85
- <div style="display:flex;gap:0.25rem;margin-top:0.5rem">
86
- ${['😊','🌤','✨'].map(e=>`<span style="font-size:0.75rem">${e}</span>`).join('')}
87
- </div>
88
- </div>
89
-
90
- <!-- mood chart -->
91
- <div style="background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.08);border-radius:0.625rem;padding:0.625rem">
92
- <div style="font-size:0.6rem;color:rgba(255,255,255,0.55);margin-bottom:0.4rem;font-weight:500">MOOD THIS WEEK</div>
93
- <div style="display:flex;gap:3px;align-items:flex-end;height:32px">
94
- ${[55,70,45,85,65,80,100].map((h,i)=>`<div style="flex:1;background:${i===6?'#7b9e87':'rgba(123,158,135,0.3)'};border-radius:2px 2px 0 0;height:${h}%;transition:height 0.3s"></div>`).join('')}
95
- </div>
96
- <div style="display:flex;justify-content:space-between;margin-top:3px">
97
- ${['M','T','W','T','F','S','S'].map((d,i)=>`<span style="font-size:0.5rem;color:${i===6?'#b6be9c':'rgba(255,255,255,0.25)'};flex:1;text-align:center">${d}</span>`).join('')}
98
- </div>
99
- </div>
100
-
101
- <!-- AI insight -->
102
- <div style="background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.07);border-radius:0.625rem;padding:0.625rem;display:flex;gap:0.4rem;align-items:flex-start">
103
- <span style="font-size:0.75rem;flex-shrink:0">✦</span>
104
- <div style="font-size:0.6rem;color:rgba(255,255,255,0.5);line-height:1.5">You write more on days you exercise. 3-day pattern detected.</div>
105
- </div>
106
-
107
- </div>`
108
-
109
- const pageHero = hero({
110
- eyebrow: 'Your mindful journaling companion',
111
- title: 'Write, reflect, and grow every day',
112
- subtitle: 'Lumio uses AI to help you understand your thoughts, track your mood, and build lasting habits — all in a beautifully simple journal.',
113
- background: '#7b9e87',
114
- eyebrowColor: '#1a2e24',
115
- color: '#1a2e24',
116
- image: phoneFrame({ content: phoneScreen }),
117
- actions: `
118
- <div class="u-flex u-gap-3 u-flex-wrap u-justify-center">
119
- ${appBadge({ store: 'apple', href: '#download' })}
120
- ${appBadge({ store: 'google', href: '#download' })}
121
- </div>
122
- <p class="u-mt-3 u-text-sm u-text-muted">Free forever · No credit card required</p>
123
- `,
124
- })
125
-
126
- // ── Stats ─────────────────────────────────────────────────────────────────────
127
-
128
- const statsSection = section({
129
- variant: 'alt',
130
- padding: 'sm',
131
- content: container({
132
- content: grid({
133
- cols: 4,
134
- gap: 'md',
135
- content: [
136
- stat({ label: 'Active users', value: '127K+', trend: 'up', change: '↑ 23% this month', center: true }),
137
- stat({ label: 'Journal entries', value: '4.2M', trend: 'up', change: '↑ 18% this month', center: true }),
138
- stat({ label: 'App Store rating', value: '4.9', trend: 'neutral', change: '★ 8,400 reviews', center: true }),
139
- stat({ label: 'Days avg. streak', value: '34', trend: 'up', change: '↑ 12% vs last year', center: true }),
140
- ].join(''),
141
- }),
142
- }),
143
- })
144
-
145
- // ── Features ──────────────────────────────────────────────────────────────────
146
-
147
- const featuresSection = section({
148
- id: 'features',
149
- eyebrow: 'Why people love Lumio',
150
- title: 'Everything you need to build a journaling habit',
151
- align: 'center',
152
- content: container({
153
- content: grid({
154
- cols: 3,
155
- gap: 'lg',
156
- content: [
157
- feature({ icon: iconAi({ size: 36 }), title: 'AI-powered insights', description: 'Lumio reads between the lines. Get weekly summaries, pattern recognition, and personalised prompts that help you dig deeper.' }),
158
- feature({ icon: iconSmile({ size: 36 }), title: 'Mood & habit tracking', description: 'Log your mood in seconds with an emoji or a word. Lumio connects the dots over time so you can see what affects how you feel.' }),
159
- feature({ icon: iconLock({ size: 36 }), title: 'End-to-end encrypted', description: 'Your journal is yours alone. Every entry is encrypted on your device before it ever touches our servers. We literally cannot read it.' }),
160
- feature({ icon: iconZap({ size: 36 }), title: 'Daily prompts', description: 'Never stare at a blank page. Choose from 300+ prompts crafted by therapists and coaches, or let Lumio suggest one based on your week.' }),
161
- feature({ icon: iconRefresh({ size: 36 }), title: 'Sync across devices', description: 'Start on your phone, continue on your tablet. Everything syncs instantly and works offline — your entries are always available.' }),
162
- feature({ icon: iconBarChart({ size: 36 }), title: 'Progress & streaks', description: 'Streaks keep you accountable. Track your journaling consistency with beautiful charts and celebrate milestones along the way.' }),
163
- ].map(f => `<div>${f}</div>`).join(''),
164
- }),
165
- }),
166
- })
167
-
168
- // ── Testimonials ──────────────────────────────────────────────────────────────
169
-
170
- // Testimonial card — replaces the removed testimonial() component with card()
171
- const testimonialCard = ({ quote, name, role, rating }) => card({
172
- content: `
173
- <div class="u-flex u-gap-1" aria-hidden="true" style="color:var(--ui-accent)">${iconStar({ size: 14 }).repeat(rating)}</div>
174
- <p class="u-mt-3" style="font-style:italic">&ldquo;${quote}&rdquo;</p>
175
- `,
176
- footer: `<p class="u-font-bold">${name}</p><p class="u-text-sm u-text-muted">${role}</p>`,
177
- })
178
-
179
- const testimonialsSection = section({
180
- id: 'testimonials',
181
- variant: 'alt',
182
- eyebrow: 'Loved by 127,000+ journalers',
183
- title: 'Real people, real results',
184
- align: 'center',
185
- content: container({
186
- content: grid({
187
- cols: 3,
188
- gap: 'md',
189
- content: [
190
- testimonialCard({ quote: 'I\'ve tried every journaling app out there. Lumio is the first one that actually helped me understand why I was feeling burnt out. The AI insights are genuinely useful, not gimmicky.', name: 'Sarah K.', role: 'Product Designer', rating: 5 }),
191
- testimonialCard({ quote: 'The streak feature sounds simple but it completely changed my relationship with writing. 112 days in and I haven\'t missed one. My therapist noticed a difference before I did.', name: 'Marcus T.', role: 'Software Engineer', rating: 5 }),
192
- testimonialCard({ quote: 'I was sceptical about the AI part but it\'s surprisingly good at spotting patterns. It told me I write less when I skip my morning run — completely true and now I use it as motivation.', name: 'Priya R.', role: 'Clinical Psychologist', rating: 5 }),
193
- ].map(t => `<div>${t}</div>`).join(''),
194
- }),
195
- }),
196
- })
197
-
198
- // ── Pricing ───────────────────────────────────────────────────────────────────
199
-
200
- // Pricing tile — replaces the removed pricing() component with card()
201
- const pricingCard = ({ name, price, period, description, features, badge: badgeLabel, highlighted, action }) => card({
202
- variant: highlighted ? 'elevated' : 'bordered',
203
- content: `
204
- ${badgeLabel ? badge({ label: badgeLabel }) : ''}
205
- <h3 class="u-text-xl u-font-bold u-mt-2">${name}</h3>
206
- <p class="u-text-3xl u-font-bold u-mt-2">${price}<span class="u-text-sm u-text-muted">${period}</span></p>
207
- <p class="u-text-sm u-text-muted u-mt-2">${description}</p>
208
- <ul class="u-mt-4 u-flex u-flex-col u-gap-2">
209
- ${features.map(f => `<li>${f}</li>`).join('')}
210
- </ul>
211
- `,
212
- footer: action,
213
- })
214
-
215
- const pricingSection = section({
216
- id: 'pricing',
217
- eyebrow: 'Simple pricing',
218
- title: 'Start free, upgrade when you\'re ready',
219
- subtitle: 'No contracts. Cancel any time. All plans include a 14-day free trial of Lumio Pro.',
220
- align: 'center',
221
- content: container({
222
- size: 'md',
223
- content: grid({
224
- cols: 3,
225
- gap: 'md',
226
- content: [
227
- pricingCard({
228
- name: 'Free',
229
- price: '£0',
230
- period: 'forever',
231
- description: 'Everything you need to start a journaling habit.',
232
- features: [
233
- 'Unlimited journal entries',
234
- 'Basic mood tracking',
235
- '30-day history',
236
- '5 daily prompts per month',
237
- 'iOS & Android apps',
238
- ],
239
- action: button({ label: 'Get started free', href: '#download', variant: 'outline', full: true }),
240
- }),
241
- pricingCard({
242
- name: 'Pro',
243
- price: '£4.99',
244
- period: '/month',
245
- description: 'AI insights and unlimited everything for serious journalers.',
246
- badge: 'Most popular',
247
- highlighted: true,
248
- features: [
249
- 'Everything in Free',
250
- 'AI-powered weekly insights',
251
- 'Unlimited prompt library',
252
- 'Mood pattern analysis',
253
- 'End-to-end encryption',
254
- 'Export to PDF',
255
- ],
256
- action: button({ label: 'Start free trial', href: '#download', full: true }),
257
- }),
258
- pricingCard({
259
- name: 'Family',
260
- price: '£9.99',
261
- period: '/month',
262
- description: 'Share Lumio Pro with up to 5 family members.',
263
- features: [
264
- 'Everything in Pro',
265
- 'Up to 5 accounts',
266
- 'Shared family streaks',
267
- 'Parental controls',
268
- 'Priority support',
269
- ],
270
- action: button({ label: 'Start free trial', href: '#download', variant: 'outline', full: true }),
271
- }),
272
- ].map(p => `<div>${p}</div>`).join(''),
273
- }),
274
- }),
275
- })
276
-
277
- // ── FAQ ───────────────────────────────────────────────────────────────────────
278
-
279
- const faqSection = section({
280
- id: 'faq',
281
- variant: 'alt',
282
- eyebrow: 'FAQ',
283
- title: 'Common questions',
284
- align: 'center',
285
- content: container({
286
- size: 'sm',
287
- content: accordion({
288
- items: [
289
- {
290
- question: 'Is my journal really private?',
291
- answer: 'Yes — completely. Entries are encrypted on your device using AES-256 before upload. Our servers only ever see ciphertext. Even Lumio employees cannot read your journal.',
292
- },
293
- {
294
- question: 'What happens when my 14-day trial ends?',
295
- answer: 'You\'ll automatically drop to the Free plan. All your entries are kept — you just lose access to Pro features like AI insights and the full prompt library. No surprise charges.',
296
- },
297
- {
298
- question: 'Can I export my data?',
299
- answer: 'Pro users can export the full journal history as a PDF or JSON file at any time from Settings → Export. Free users can export the last 30 days.',
300
- },
301
- {
302
- question: 'Does it work offline?',
303
- answer: 'Yes. The full app works offline — write, browse, and edit entries without a connection. Changes sync automatically when you\'re back online.',
304
- },
305
- {
306
- question: 'How does the AI know what to say?',
307
- answer: 'The AI reads your entries (on-device, before encryption) to spot recurring themes, emotional patterns, and topics. It never sends plain-text content to our servers.',
308
- },
309
- ],
310
- }),
311
- }),
312
- })
313
-
314
- // ── Download CTA ──────────────────────────────────────────────────────────────
315
-
316
- const downloadCta = section({
317
- id: 'download',
318
- content: cta({
319
- eyebrow: 'Ready to start?',
320
- title: 'Your journal is waiting',
321
- subtitle: 'Join 127,000 people who journal with Lumio every day. Free forever — no credit card needed.',
322
- actions: `
323
- <div class="u-flex u-gap-3 u-flex-wrap u-justify-center">
324
- ${appBadge({ store: 'apple', href: '#' })}
325
- ${appBadge({ store: 'google', href: '#' })}
326
- </div>`,
327
- }),
328
- })
329
-
330
- // ── Footer ────────────────────────────────────────────────────────────────────
331
-
332
- const pageFooter = footer({
333
- logo: `${iconFeather({ size: 18 })}<span class="u-font-bold" style="letter-spacing:-0.02em">Lumio</span>`,
334
- logoHref: '/',
335
- links: [
336
- { label: 'Privacy', href: '#' },
337
- { label: 'Terms', href: '#' },
338
- { label: 'Security', href: '#' },
339
- { label: 'Blog', href: '#' },
340
- { label: 'Twitter', href: '#' },
341
- ],
342
- legal: `© ${new Date().getFullYear()} Lumio Inc. All rights reserved.`,
343
- })
344
-
345
- // ── Spec ──────────────────────────────────────────────────────────────────────
346
-
347
- export default {
348
- route: '/templates/mobile-app',
349
-
350
- meta: {
351
- title: 'Lumio — Journal with clarity',
352
- description: 'The AI-powered journaling app that helps you understand your thoughts, track your mood, and build lasting habits.',
353
- theme: 'light',
354
- styles: ['/pulse-ui.css', '/themes/lumio.css'],
355
- },
356
-
357
- view: () => `
358
- <a href="#main-content" class="skip-link" style="position:absolute;left:-9999px;top:1rem;z-index:9999;padding:0.5rem 1rem;background:var(--color-accent);color:white;border-radius:0.25rem;text-decoration:none">Skip to main content</a>
359
- ${pageNav}
360
- <main id="main-content">
361
- ${pageHero}
362
- ${statsSection}
363
- ${featuresSection}
364
- ${testimonialsSection}
365
- ${pricingSection}
366
- ${faqSection}
367
- ${downloadCta}
368
- </main>
369
- ${pageFooter}
370
- <script src="${asset('/pulse-ui.js')}" defer></script>`,
371
- }