@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.
- package/package.json +6 -5
- package/public/.pulse-ui-version +1 -1
- package/scripts/build.js +26 -0
- package/scripts/release-version.js +17 -8
- package/scripts/strip-server.test.js +60 -0
- package/src/agent/checklist.md +8 -12
- package/src/agent/commands/pulse-start.md +2 -0
- package/src/agent/commands/verify.md +30 -8
- package/src/agent/guide-components.md +7 -25
- package/src/agent/guide-routing.md +0 -12
- package/src/agent/guide-server.md +102 -1
- package/src/agent/guide-spec.md +8 -1
- package/src/agent/guide-styles.md +23 -0
- package/src/agent/identity.md +9 -0
- package/src/agent/workflow.md +4 -23
- package/src/cli/check-bundles.js +113 -0
- package/src/cli/check-bundles.test.js +137 -0
- package/src/cli/cli.test.js +105 -0
- package/src/cli/dev.js +48 -3
- package/src/cli/diagnose.js +105 -0
- package/src/cli/diagnose.test.js +124 -0
- package/src/cli/discover.js +9 -2
- package/src/cli/index.js +171 -9
- package/src/cli/process-utils.js +57 -0
- package/src/cli/process-utils.test.js +114 -0
- package/src/cli/review.js +391 -0
- package/src/cli/review.test.js +143 -0
- package/src/cli/scaffold.js +9 -4
- package/src/cli/stamp.js +102 -0
- package/src/cli/stamp.test.js +149 -0
- package/src/cli/start.js +15 -2
- package/src/cli/validate.js +184 -0
- package/src/cli/validate.test.js +153 -0
- package/src/mcp/mcp.test.js +261 -30
- package/src/mcp/server.js +207 -515
- package/src/mcp/validate-worker.js +18 -1
- package/src/runtime/index.js +144 -11
- package/src/runtime/runtime.test.js +199 -0
- package/src/runtime/ssr.js +14 -7
- package/src/runtime/ssr.test.js +31 -0
- package/src/server/index.js +163 -10
- package/src/server/server.test.js +225 -2
- package/src/spec/schema.js +1 -1
- package/src/ui/index.js +0 -5
- package/src/ui/ui.test.js +0 -75
- package/types/ui.d.ts +0 -21
- package/.claude/commands/build-page.md +0 -63
- package/.claude/commands/new-doc-page.md +0 -45
- package/.claude/commands/pulse-start.md +0 -11
- package/.claude/commands/pulse-stop.md +0 -5
- package/.claude/commands/verify.md +0 -118
- package/.claude/pulse-checklist.md +0 -189
- package/.github/workflows/ci.yml +0 -25
- package/.github/workflows/publish.yml +0 -45
- package/.pulse/load-reports/home/1773432711417.json +0 -22
- package/.pulse-verified +0 -1
- package/AGENTS.md +0 -697
- package/CLAUDE.md +0 -697
- package/README.md +0 -95
- package/benchmark/public/dist/counter.boot-PO6SNSJZ.js +0 -8
- package/benchmark/public/dist/home.boot-YK6VAEQI.js +0 -6
- package/benchmark/public/dist/manifest.json +0 -5
- package/benchmark/public/dist/runtime-5Y7ESAIB.js +0 -2
- package/benchmark/public/dist/runtime-KO4BHUQ3.js +0 -49
- package/benchmark/results/2026-03-27-09-20.json +0 -188
- package/benchmark/results/2026-03-27-09-21.json +0 -188
- package/benchmark/results/2026-03-27-09-29.json +0 -188
- package/benchmark/results/2026-03-27-10-14.json +0 -188
- package/benchmark/results/2026-03-27-10-18.json +0 -232
- package/benchmark/src/pages/counter.js +0 -37
- package/benchmark/src/pages/home.js +0 -21
- package/docs/.claude/commands/pulse-build.md +0 -7
- package/docs/.claude/commands/pulse-dev.md +0 -7
- package/docs/.claude/commands/pulse-start.md +0 -9
- package/docs/.claude/commands/pulse-stop.md +0 -5
- package/docs/.claude/commands/verify.md +0 -84
- package/docs/.claude/coverage-check.js +0 -196
- package/docs/.claude/pulse-checklist.md +0 -189
- package/docs/public/.pulse-ui-version +0 -1
- package/docs/public/andypixel.css +0 -237
- package/docs/public/bookworms.css +0 -422
- package/docs/public/coutumes.css +0 -561
- package/docs/public/docs.css +0 -2388
- package/docs/public/favicon.svg +0 -4
- package/docs/public/home-brut.css +0 -982
- package/docs/public/images/bookworms/hero-books.jpg +0 -0
- package/docs/public/images/bookworms/how-we-read-now.jpg +0 -0
- package/docs/public/images/bookworms/lantern-keepers.jpg +0 -0
- package/docs/public/images/bookworms/provincial-bookseller.jpg +0 -0
- package/docs/public/images/journey/178.jpg +0 -0
- package/docs/public/images/journey/29.jpg +0 -0
- package/docs/public/images/retrowheels/hero.jpg +0 -0
- package/docs/public/images/shop/devbook-pro.jpg +0 -0
- package/docs/public/images/suzukibento/1015.jpg +0 -0
- package/docs/public/images/suzukibento/1025.jpg +0 -0
- package/docs/public/images/suzukibento/1043.jpg +0 -0
- package/docs/public/images/suzukibento/1059.jpg +0 -0
- package/docs/public/images/suzukibento/1062.jpg +0 -0
- package/docs/public/images/suzukibento/1084.jpg +0 -0
- package/docs/public/images/swish/1469334031218-e382a71b716b.jpg +0 -0
- package/docs/public/images/swish/1483985988355-763728e1935b.jpg +0 -0
- package/docs/public/images/swish/1485518882345-15568b007407.jpg +0 -0
- package/docs/public/images/swish/1490481651871-ab68de25d43d.jpg +0 -0
- package/docs/public/images/swish/1496747611176-843222e1e57c.jpg +0 -0
- package/docs/public/images/swish/1502716119720-b23a93e5fe1b.jpg +0 -0
- package/docs/public/images/swish/1539109136881-3be0616acf4b.jpg +0 -0
- package/docs/public/images/swish/1551803091-e20673f15770.jpg +0 -0
- package/docs/public/images/swish/1558769132-cb1aea458c5e.jpg +0 -0
- package/docs/public/images/swish/1572804013309-59a88b7e92f1.jpg +0 -0
- package/docs/public/img/heic1501a.jpg +0 -0
- package/docs/public/img/placeholder-landscape.svg +0 -18
- package/docs/public/img/placeholder-square.svg +0 -14
- package/docs/public/img/placeholder-tall.svg +0 -22
- package/docs/public/journey.css +0 -528
- package/docs/public/menu.js +0 -327
- package/docs/public/pulse-ui.css +0 -3028
- package/docs/public/pulse-ui.js +0 -257
- package/docs/public/retrowheels.css +0 -425
- package/docs/public/shop.css +0 -269
- package/docs/public/sparkys.css +0 -382
- package/docs/public/suzukibento.css +0 -517
- package/docs/public/swish.css +0 -394
- package/docs/public/theme.css +0 -52
- package/docs/public/themes/lumio.css +0 -50
- package/docs/public/themes/observatory.css +0 -104
- package/docs/server.js +0 -161
- package/docs/src/lib/component-page.js +0 -49
- package/docs/src/lib/highlight.js +0 -2
- package/docs/src/lib/layout.js +0 -190
- package/docs/src/lib/metrics-store.js +0 -6
- package/docs/src/lib/nav.js +0 -171
- package/docs/src/lib/stats.js +0 -81
- package/docs/src/pages/accessibility.js +0 -157
- package/docs/src/pages/actions.js +0 -194
- package/docs/src/pages/auth.js +0 -176
- package/docs/src/pages/caching.js +0 -95
- package/docs/src/pages/components/accordion.js +0 -48
- package/docs/src/pages/components/alert.js +0 -35
- package/docs/src/pages/components/app-badge.js +0 -41
- package/docs/src/pages/components/avatar.js +0 -35
- package/docs/src/pages/components/badge.js +0 -36
- package/docs/src/pages/components/breadcrumbs.js +0 -94
- package/docs/src/pages/components/button.js +0 -84
- package/docs/src/pages/components/card.js +0 -225
- package/docs/src/pages/components/charts.js +0 -283
- package/docs/src/pages/components/checkbox.js +0 -129
- package/docs/src/pages/components/cluster.js +0 -47
- package/docs/src/pages/components/container.js +0 -40
- package/docs/src/pages/components/cta.js +0 -54
- package/docs/src/pages/components/divider.js +0 -37
- package/docs/src/pages/components/empty.js +0 -36
- package/docs/src/pages/components/feature.js +0 -60
- package/docs/src/pages/components/fieldset.js +0 -65
- package/docs/src/pages/components/fileupload.js +0 -128
- package/docs/src/pages/components/footer.js +0 -58
- package/docs/src/pages/components/grid.js +0 -169
- package/docs/src/pages/components/heading.js +0 -107
- package/docs/src/pages/components/hero.js +0 -123
- package/docs/src/pages/components/icons.js +0 -410
- package/docs/src/pages/components/image.js +0 -71
- package/docs/src/pages/components/input.js +0 -51
- package/docs/src/pages/components/media.js +0 -51
- package/docs/src/pages/components/modal.js +0 -111
- package/docs/src/pages/components/nav.js +0 -136
- package/docs/src/pages/components/phone-frame.js +0 -74
- package/docs/src/pages/components/progress.js +0 -102
- package/docs/src/pages/components/prose.js +0 -111
- package/docs/src/pages/components/pullquote.js +0 -71
- package/docs/src/pages/components/radio.js +0 -194
- package/docs/src/pages/components/rating.js +0 -106
- package/docs/src/pages/components/search.js +0 -61
- package/docs/src/pages/components/section.js +0 -98
- package/docs/src/pages/components/segmented.js +0 -121
- package/docs/src/pages/components/select.js +0 -45
- package/docs/src/pages/components/slider.js +0 -115
- package/docs/src/pages/components/spinner.js +0 -73
- package/docs/src/pages/components/stack.js +0 -48
- package/docs/src/pages/components/stat.js +0 -55
- package/docs/src/pages/components/stepper.js +0 -66
- package/docs/src/pages/components/table.js +0 -45
- package/docs/src/pages/components/textarea.js +0 -31
- package/docs/src/pages/components/toggle.js +0 -84
- package/docs/src/pages/components/tooltip.js +0 -48
- package/docs/src/pages/components.js +0 -200
- package/docs/src/pages/config.js +0 -193
- package/docs/src/pages/constraints.js +0 -100
- package/docs/src/pages/deploy.js +0 -233
- package/docs/src/pages/error-pages.js +0 -99
- package/docs/src/pages/extending.js +0 -198
- package/docs/src/pages/faq.js +0 -99
- package/docs/src/pages/forms.js +0 -129
- package/docs/src/pages/getting-started.js +0 -147
- package/docs/src/pages/guard.js +0 -120
- package/docs/src/pages/home.js +0 -345
- package/docs/src/pages/how-it-works.js +0 -244
- package/docs/src/pages/hydration.js +0 -108
- package/docs/src/pages/images.js +0 -120
- package/docs/src/pages/markdown.js +0 -156
- package/docs/src/pages/meta.js +0 -193
- package/docs/src/pages/mutations.js +0 -107
- package/docs/src/pages/navigation.js +0 -85
- package/docs/src/pages/performance.js +0 -157
- package/docs/src/pages/persist.js +0 -89
- package/docs/src/pages/project-structure.js +0 -91
- package/docs/src/pages/prompt-examples.js +0 -186
- package/docs/src/pages/raw-responses.js +0 -122
- package/docs/src/pages/routing.js +0 -120
- package/docs/src/pages/server-api.js +0 -291
- package/docs/src/pages/server-data.js +0 -203
- package/docs/src/pages/slash-commands.js +0 -55
- package/docs/src/pages/spec.js +0 -207
- package/docs/src/pages/state.js +0 -102
- package/docs/src/pages/store.js +0 -203
- package/docs/src/pages/streaming.js +0 -107
- package/docs/src/pages/stripe.js +0 -194
- package/docs/src/pages/supabase.js +0 -324
- package/docs/src/pages/templates/andypixel.js +0 -260
- package/docs/src/pages/templates/blog-post.js +0 -226
- package/docs/src/pages/templates/bookworms.js +0 -227
- package/docs/src/pages/templates/coutumes.js +0 -237
- package/docs/src/pages/templates/journey.js +0 -186
- package/docs/src/pages/templates/mobile-app.js +0 -371
- package/docs/src/pages/templates/retrowheels.js +0 -355
- package/docs/src/pages/templates/shop.js +0 -268
- package/docs/src/pages/templates/sparkys.js +0 -264
- package/docs/src/pages/templates/suzukibento.js +0 -200
- package/docs/src/pages/templates/swish.js +0 -313
- package/docs/src/pages/templates.js +0 -534
- package/docs/src/pages/testing.js +0 -199
- package/docs/src/pages/validation.js +0 -138
- package/examples/.claude/commands/pulse-build.md +0 -7
- package/examples/.claude/commands/pulse-dev.md +0 -7
- package/examples/.claude/commands/pulse-start.md +0 -9
- package/examples/.claude/commands/pulse-stop.md +0 -5
- package/examples/.claude/commands/verify.md +0 -84
- package/examples/.claude/coverage-check.js +0 -196
- package/examples/.claude/pulse-checklist.md +0 -189
- package/examples/andypixel.css +0 -237
- package/examples/andypixel.js +0 -258
- package/examples/bookworms.css +0 -422
- package/examples/bookworms.js +0 -225
- package/examples/contact.js +0 -166
- package/examples/counter.js +0 -94
- package/examples/dev.server.js +0 -110
- package/examples/examples.test.js +0 -394
- package/examples/journey.css +0 -528
- package/examples/journey.js +0 -186
- package/examples/pricing.js +0 -262
- package/examples/products.js +0 -190
- package/examples/public/.pulse-ui-version +0 -1
- package/examples/public/pulse-ui.css +0 -3028
- package/examples/public/pulse-ui.js +0 -257
- package/examples/quiz.js +0 -208
- package/examples/retrowheels.css +0 -425
- package/examples/retrowheels.js +0 -353
- package/examples/shared.js +0 -84
- package/examples/suzukibento.css +0 -513
- package/examples/suzukibento.js +0 -200
- package/examples/swish.css +0 -394
- package/examples/swish.js +0 -311
- package/examples/todos.js +0 -162
- package/src/ui/fieldset.js +0 -37
- package/src/ui/phone-frame.js +0 -40
- package/src/ui/search.js +0 -78
- package/src/ui/segmented.js +0 -50
- 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">“${quote}”</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
|
-
}
|