@invisibleloop/pulse 0.20.5 → 0.20.6

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 (79) hide show
  1. package/.claude/pulse-checklist.md +1 -2
  2. package/CLAUDE.md +1 -2
  3. package/docs/.claude/pulse-checklist.md +1 -2
  4. package/docs/public/coutumes.css +21 -44
  5. package/docs/public/docs.css +67 -0
  6. package/docs/public/pulse-ui.css +8 -1037
  7. package/docs/public/retrowheels.css +23 -0
  8. package/docs/public/shop.css +35 -1
  9. package/docs/public/sparkys.css +56 -23
  10. package/docs/server.js +0 -11
  11. package/docs/src/lib/nav.js +0 -11
  12. package/docs/src/pages/components/charts.js +2 -1
  13. package/docs/src/pages/components/grid.js +9 -5
  14. package/docs/src/pages/components.js +0 -4
  15. package/docs/src/pages/home.js +16 -1
  16. package/docs/src/pages/how-it-works.js +20 -14
  17. package/docs/src/pages/templates/coutumes.js +83 -72
  18. package/docs/src/pages/templates/mobile-app.js +36 -9
  19. package/docs/src/pages/templates/retrowheels.js +17 -4
  20. package/docs/src/pages/templates/shop.js +21 -10
  21. package/docs/src/pages/templates/sparkys.js +35 -20
  22. package/examples/.claude/pulse-checklist.md +1 -2
  23. package/examples/examples.test.js +4 -4
  24. package/examples/pricing.js +21 -3
  25. package/examples/public/pulse-ui.css +8 -1037
  26. package/examples/retrowheels.css +23 -0
  27. package/examples/retrowheels.js +17 -4
  28. package/package.json +5 -1
  29. package/public/.pulse-ui-version +1 -1
  30. package/public/pulse-ui.css +8 -1037
  31. package/src/agent/checklist.md +1 -2
  32. package/src/agent/guide-components.md +43 -67
  33. package/src/agent/guide-design-gallery.md +14 -14
  34. package/src/agent/guide-design-references.md +21 -21
  35. package/src/agent/guide-styles.md +2 -2
  36. package/src/agent/guide-templates.md +6 -6
  37. package/src/agent/identity.md +1 -1
  38. package/src/agent/workflow.md +1 -2
  39. package/src/mcp/server.js +31 -24
  40. package/src/ui/index.js +0 -27
  41. package/src/ui/ui.test.js +0 -137
  42. package/types/charts.d.ts +61 -0
  43. package/types/ui.d.ts +1 -129
  44. package/docs/src/pages/components/banner.js +0 -44
  45. package/docs/src/pages/components/carousel.js +0 -71
  46. package/docs/src/pages/components/code-window.js +0 -56
  47. package/docs/src/pages/components/decorate.js +0 -74
  48. package/docs/src/pages/components/gallery.js +0 -102
  49. package/docs/src/pages/components/list.js +0 -111
  50. package/docs/src/pages/components/marquee.js +0 -76
  51. package/docs/src/pages/components/photo-card.js +0 -74
  52. package/docs/src/pages/components/pricing.js +0 -67
  53. package/docs/src/pages/components/testimonial.js +0 -48
  54. package/docs/src/pages/components/timeline.js +0 -226
  55. package/src/ui/banner.js +0 -41
  56. package/src/ui/brand-statement.js +0 -58
  57. package/src/ui/carousel.js +0 -59
  58. package/src/ui/code-window.js +0 -39
  59. package/src/ui/colophon.js +0 -29
  60. package/src/ui/contact-row.js +0 -41
  61. package/src/ui/decorate.js +0 -55
  62. package/src/ui/display-heading.js +0 -44
  63. package/src/ui/drop-cap.js +0 -29
  64. package/src/ui/editorial-hero.js +0 -63
  65. package/src/ui/entry-list.js +0 -36
  66. package/src/ui/gallery.js +0 -60
  67. package/src/ui/image-pair.js +0 -72
  68. package/src/ui/list.js +0 -36
  69. package/src/ui/marquee.js +0 -48
  70. package/src/ui/photo-card.js +0 -44
  71. package/src/ui/pricing.js +0 -56
  72. package/src/ui/product-card.js +0 -65
  73. package/src/ui/section-label.js +0 -38
  74. package/src/ui/service-card.js +0 -47
  75. package/src/ui/stat-grid.js +0 -40
  76. package/src/ui/testimonial.js +0 -59
  77. package/src/ui/timeline.js +0 -72
  78. package/src/ui/top-strip.js +0 -47
  79. package/src/ui/trust-list.js +0 -46
@@ -327,6 +327,29 @@ h1, h2, h3, h4, h5, h6,
327
327
  margin-bottom: var(--ui-space-4);
328
328
  }
329
329
 
330
+ /* Review cards — replaces the removed testimonial() component */
331
+ .rw-review-stars {
332
+ display: flex;
333
+ gap: 2px;
334
+ color: var(--rw-orange-dark);
335
+ margin-bottom: var(--ui-space-3);
336
+ }
337
+
338
+ .rw-review-quote {
339
+ font-style: italic;
340
+ color: var(--ui-text);
341
+ }
342
+
343
+ .rw-review-name {
344
+ font-family: var(--ui-font-display);
345
+ font-weight: 900;
346
+ }
347
+
348
+ .rw-review-role {
349
+ font-size: var(--ui-text-sm);
350
+ color: var(--ui-muted);
351
+ }
352
+
330
353
  .rw-service-price {
331
354
  font-family: 'Courier New', monospace;
332
355
  font-size: var(--ui-text-sm);
@@ -41,7 +41,7 @@ body {
41
41
  padding: var(--ui-space-16) var(--ui-space-6) var(--ui-space-8);
42
42
  }
43
43
 
44
- .product-display .ui-display-heading {
44
+ .product-display .product-display-title {
45
45
  font-size: clamp(3rem, 10vw, 8rem);
46
46
  line-height: 1;
47
47
  letter-spacing: -0.04em;
@@ -49,6 +49,40 @@ body {
49
49
  color: var(--ui-text);
50
50
  }
51
51
 
52
+ /* Section eyebrow + rule — replaces the removed sectionLabel() component */
53
+ .shop-section-label {
54
+ margin-bottom: var(--ui-space-6);
55
+ }
56
+ .shop-section-label-eyebrow {
57
+ font-size: var(--ui-text-sm);
58
+ letter-spacing: 0.1em;
59
+ text-transform: uppercase;
60
+ font-weight: 600;
61
+ color: var(--ui-color-muted);
62
+ margin-bottom: var(--ui-space-2);
63
+ }
64
+ .shop-section-label-heading {
65
+ margin-bottom: var(--ui-space-3);
66
+ }
67
+ .shop-section-label-rule {
68
+ border: none;
69
+ border-top: 1px solid var(--ui-border);
70
+ }
71
+
72
+ /* End-of-page metadata — replaces the removed colophon() component */
73
+ .shop-colophon {
74
+ padding: var(--ui-space-8) var(--ui-space-6);
75
+ text-align: center;
76
+ border-top: 1px solid var(--ui-border);
77
+ }
78
+ .shop-colophon-inner {
79
+ font-size: var(--ui-text-sm);
80
+ color: var(--ui-color-muted);
81
+ }
82
+ .shop-colophon-inner a {
83
+ color: inherit;
84
+ }
85
+
52
86
  .product-display .product-tagline {
53
87
  margin-top: var(--ui-space-4);
54
88
  font-size: var(--ui-text-xl);
@@ -41,17 +41,23 @@ body {
41
41
  }
42
42
 
43
43
  /* ── Top utility strip ─────────────────────────────────────────────────── */
44
- /* top-strip rendered via topStrip() component — background/color set inline */
45
- .ui-top-strip {
46
- max-width: none;
47
- padding-inline: var(--ui-space-6);
44
+ /* raw two-column bar — replaces the removed topStrip() component */
45
+ .top-strip {
46
+ display: flex;
47
+ align-items: center;
48
+ justify-content: space-between;
49
+ gap: var(--ui-space-4);
50
+ padding: var(--ui-space-2) var(--ui-space-6);
51
+ background: var(--brand-navy-deep);
52
+ color: var(--brand-white);
53
+ font-size: var(--ui-text-sm);
48
54
  }
49
- .ui-top-strip a {
55
+ .top-strip a {
50
56
  color: inherit;
51
57
  text-decoration: none;
52
58
  font-weight: 600;
53
59
  }
54
- .ui-top-strip a:hover { text-decoration: underline; }
60
+ .top-strip a:hover { text-decoration: underline; }
55
61
 
56
62
  /* ── Hero ──────────────────────────────────────────────────────────────── */
57
63
  .hero-split {
@@ -105,10 +111,23 @@ body {
105
111
  color: var(--ui-muted);
106
112
  font-weight: 600;
107
113
  }
108
- /* trustList() inside trust slot — override default small size */
109
- .hero-split__trust .ui-trust-list-item {
114
+ /* trust-inline list — replaces the removed trustList() component */
115
+ .trust-inline {
116
+ display: flex;
117
+ flex-wrap: wrap;
118
+ gap: var(--ui-space-5);
119
+ list-style: none;
120
+ }
121
+ .trust-inline__item {
122
+ display: flex;
123
+ align-items: center;
124
+ gap: var(--ui-space-2);
110
125
  font-weight: 600;
111
126
  }
127
+ .trust-inline__icon {
128
+ display: inline-flex;
129
+ color: var(--brand-amber);
130
+ }
112
131
 
113
132
  .hero-split__panel {
114
133
  background: linear-gradient(160deg, var(--ui-accent) 0%, var(--brand-navy-deep) 100%);
@@ -198,22 +217,29 @@ body {
198
217
  color: var(--ui-muted);
199
218
  }
200
219
 
201
- /* ── Services grid — serviceCard() component ───────────────────────────── */
202
- .services {
220
+ /* ── Services grid — card() component with a bordered service-card look ─── */
221
+ .service-card {
222
+ border: 1px solid var(--ui-border);
223
+ box-shadow: none;
224
+ }
225
+ .service-card .ui-service-card-icon {
226
+ width: 44px;
227
+ height: 44px;
228
+ border-radius: var(--ui-radius-md);
229
+ background: var(--ui-accent-dim);
230
+ color: var(--ui-accent);
203
231
  display: grid;
204
- grid-template-columns: repeat(3, 1fr);
205
- gap: var(--ui-space-5);
232
+ place-items: center;
233
+ margin-bottom: var(--ui-space-4);
206
234
  }
207
- /* serviceCard() title size override */
208
- .services .ui-service-card-title {
235
+ .service-card .ui-service-card-title {
209
236
  font-size: var(--ui-text-xl);
210
237
  font-weight: 700;
238
+ margin-bottom: var(--ui-space-2);
211
239
  }
212
- .services .ui-service-card-desc {
240
+ .service-card .ui-service-card-desc {
213
241
  font-size: var(--ui-text-base);
214
- }
215
- .services .ui-service-card--linked:hover {
216
- transform: translateY(-2px);
242
+ color: var(--ui-muted);
217
243
  }
218
244
 
219
245
  /* ── Why grid ──────────────────────────────────────────────────────────── */
@@ -295,17 +321,24 @@ body {
295
321
  flex-direction: column;
296
322
  gap: var(--ui-space-3);
297
323
  }
298
- .contact-strip__details .ui-contact-row {
324
+ /* contact-inline list — replaces the removed contactRow() component */
325
+ .contact-inline {
326
+ display: flex;
299
327
  flex-direction: column;
328
+ gap: var(--ui-space-3);
329
+ list-style: none;
300
330
  }
301
- .contact-strip__details .ui-contact-row-item {
331
+ .contact-inline__item {
332
+ display: flex;
333
+ align-items: center;
334
+ gap: var(--ui-space-3);
302
335
  color: var(--brand-white);
303
336
  font-weight: 600;
304
337
  font-size: var(--ui-text-lg);
305
338
  }
306
- .contact-strip__details .ui-contact-row-item a { color: var(--brand-white); }
307
- .contact-strip__details .ui-contact-row-item a:hover { color: var(--brand-amber-strong); }
308
- .contact-strip__details .ui-contact-row-icon {
339
+ .contact-inline__item a { color: var(--brand-white); text-decoration: none; }
340
+ .contact-inline__item a:hover { color: var(--brand-amber-strong); }
341
+ .contact-inline__icon {
309
342
  width: 36px;
310
343
  height: 36px;
311
344
  border-radius: 50%;
package/docs/server.js CHANGED
@@ -76,7 +76,6 @@ await createServer(
76
76
  new URL('./src/pages/components/button.js', import.meta.url),
77
77
  new URL('./src/pages/components/card.js', import.meta.url),
78
78
  new URL('./src/pages/components/checkbox.js', import.meta.url),
79
- new URL('./src/pages/components/carousel.js', import.meta.url),
80
79
  new URL('./src/pages/components/charts.js', import.meta.url),
81
80
  new URL('./src/pages/components/empty.js', import.meta.url),
82
81
  new URL('./src/pages/components/fieldset.js', import.meta.url),
@@ -84,12 +83,8 @@ await createServer(
84
83
  new URL('./src/pages/components/heading.js', import.meta.url),
85
84
  new URL('./src/pages/components/icons.js', import.meta.url),
86
85
  new URL('./src/pages/components/image.js', import.meta.url),
87
- new URL('./src/pages/components/gallery.js', import.meta.url),
88
86
  new URL('./src/pages/components/input.js', import.meta.url),
89
- new URL('./src/pages/components/list.js', import.meta.url),
90
- new URL('./src/pages/components/marquee.js', import.meta.url),
91
87
  new URL('./src/pages/components/modal.js', import.meta.url),
92
- new URL('./src/pages/components/photo-card.js', import.meta.url),
93
88
  new URL('./src/pages/components/progress.js', import.meta.url),
94
89
  new URL('./src/pages/components/prose.js', import.meta.url),
95
90
  new URL('./src/pages/components/pullquote.js', import.meta.url),
@@ -104,7 +99,6 @@ await createServer(
104
99
  new URL('./src/pages/components/stepper.js', import.meta.url),
105
100
  new URL('./src/pages/components/table.js', import.meta.url),
106
101
  new URL('./src/pages/components/textarea.js', import.meta.url),
107
- new URL('./src/pages/components/timeline.js', import.meta.url),
108
102
  new URL('./src/pages/components/toggle.js', import.meta.url),
109
103
  new URL('./src/pages/components/tooltip.js', import.meta.url),
110
104
  // Landing Components
@@ -115,8 +109,6 @@ await createServer(
115
109
  new URL('./src/pages/components/hero.js', import.meta.url),
116
110
  new URL('./src/pages/components/nav.js', import.meta.url),
117
111
  new URL('./src/pages/components/phone-frame.js', import.meta.url),
118
- new URL('./src/pages/components/pricing.js', import.meta.url),
119
- new URL('./src/pages/components/testimonial.js', import.meta.url),
120
112
  // Templates
121
113
  new URL('./src/pages/templates.js', import.meta.url),
122
114
  new URL('./src/pages/templates/mobile-app.js', import.meta.url),
@@ -131,11 +123,8 @@ await createServer(
131
123
  new URL('./src/pages/templates/journey.js', import.meta.url),
132
124
  new URL('./src/pages/templates/shop.js', import.meta.url),
133
125
  // Layout Components
134
- new URL('./src/pages/components/banner.js', import.meta.url),
135
126
  new URL('./src/pages/components/cluster.js', import.meta.url),
136
- new URL('./src/pages/components/code-window.js', import.meta.url),
137
127
  new URL('./src/pages/components/container.js', import.meta.url),
138
- new URL('./src/pages/components/decorate.js', import.meta.url),
139
128
  new URL('./src/pages/components/divider.js', import.meta.url),
140
129
  new URL('./src/pages/components/footer.js', import.meta.url),
141
130
  new URL('./src/pages/components/grid.js', import.meta.url),
@@ -84,7 +84,6 @@ export const NAV = [
84
84
  { label: 'Button', href: '/components/button' },
85
85
  { label: 'Card', href: '/components/card' },
86
86
  { label: 'Checkbox', href: '/components/checkbox' },
87
- { label: 'Carousel', href: '/components/carousel' },
88
87
  { label: 'Charts', href: '/components/charts' },
89
88
  { label: 'Empty', href: '/components/empty' },
90
89
  { label: 'Fieldset', href: '/components/fieldset' },
@@ -92,12 +91,8 @@ export const NAV = [
92
91
  { label: 'Heading', href: '/components/heading' },
93
92
  { label: 'Icons', href: '/components/icons' },
94
93
  { label: 'Image', href: '/components/image' },
95
- { label: 'Gallery', href: '/components/gallery' },
96
94
  { label: 'Input', href: '/components/input' },
97
- { label: 'List', href: '/components/list' },
98
- { label: 'Marquee', href: '/components/marquee' },
99
95
  { label: 'Modal', href: '/components/modal' },
100
- { label: 'Photo Card', href: '/components/photo-card' },
101
96
  { label: 'Progress', href: '/components/progress' },
102
97
  { label: 'Prose', href: '/components/prose' },
103
98
  { label: 'Pullquote', href: '/components/pullquote' },
@@ -112,7 +107,6 @@ export const NAV = [
112
107
  { label: 'Stepper', href: '/components/stepper' },
113
108
  { label: 'Table', href: '/components/table' },
114
109
  { label: 'Textarea', href: '/components/textarea' },
115
- { label: 'Timeline', href: '/components/timeline' },
116
110
  { label: 'Toggle', href: '/components/toggle' },
117
111
  { label: 'Tooltip', href: '/components/tooltip' },
118
112
  ],
@@ -127,18 +121,13 @@ export const NAV = [
127
121
  { label: 'Hero', href: '/components/hero' },
128
122
  { label: 'Nav', href: '/components/nav' },
129
123
  { label: 'Phone Frame', href: '/components/phone-frame' },
130
- { label: 'Pricing', href: '/components/pricing' },
131
- { label: 'Testimonial', href: '/components/testimonial' },
132
124
  ],
133
125
  },
134
126
  {
135
127
  section: 'Layout Components',
136
128
  items: [
137
- { label: 'Banner', href: '/components/banner' },
138
129
  { label: 'Cluster', href: '/components/cluster' },
139
- { label: 'Code Window', href: '/components/code-window' },
140
130
  { label: 'Container', href: '/components/container' },
141
- { label: 'Decorate', href: '/components/decorate' },
142
131
  { label: 'Divider', href: '/components/divider' },
143
132
  { label: 'Footer', href: '/components/footer' },
144
133
  { label: 'Grid', href: '/components/grid' },
@@ -1,7 +1,8 @@
1
1
  import { renderComponentPage, demo } from '../../lib/component-page.js'
2
2
  import { prevNext } from '../../lib/nav.js'
3
3
  import { table } from '../../lib/layout.js'
4
- import { barChart, lineChart, donutChart, sparkline, stat, card, grid } from '../../../../src/ui/index.js'
4
+ import { stat, card, grid } from '../../../../src/ui/index.js'
5
+ import { barChart, lineChart, donutChart, sparkline } from '../../../../src/ui/charts.js'
5
6
 
6
7
  const { prev, next } = prevNext('/components/charts')
7
8
 
@@ -1,7 +1,7 @@
1
1
  import { renderComponentPage, demo } from '../../lib/component-page.js'
2
2
  import { prevNext } from '../../lib/nav.js'
3
3
  import { table } from '../../lib/layout.js'
4
- import { grid, card, stat, feature, testimonial } from '../../../../src/ui/index.js'
4
+ import { grid, card, stat, feature } from '../../../../src/ui/index.js'
5
5
 
6
6
  const { prev, next } = prevNext('/components/grid')
7
7
 
@@ -96,18 +96,22 @@ export default {
96
96
  )}
97
97
 
98
98
  <h3 class="doc-h3">Testimonial grid</h3>
99
+ <p>There is no dedicated <code>testimonial</code> component — build one with <code>card()</code>: the quote in <code>content</code>, the name and role in <code>footer</code>.</p>
99
100
  ${demo(
100
101
  grid({
101
102
  cols: 3,
102
103
  content: [
103
- testimonial({ quote: 'Shipped our redesign in a weekend. No boilerplate, no config hell.', name: 'Alex Morgan', role: 'CTO at Launchpad', rating: 5 }),
104
- testimonial({ quote: 'The streaming SSR makes our pages feel instant. Lighthouse is happy.', name: 'Sara Kim', role: 'Lead Engineer, Orbit', rating: 5 }),
105
- testimonial({ quote: 'Finally a UI kit that doesn\'t fight the platform. Just HTML.', name: 'Dan Okafor', role: 'Founder, Stackly', rating: 5 }),
104
+ card({ content: `<p style="color:var(--muted);margin:0">&ldquo;Shipped our redesign in a weekend. No boilerplate, no config hell.&rdquo;</p>`, footer: `<strong>Alex Morgan</strong><br><span style="color:var(--muted)">CTO at Launchpad</span>` }),
105
+ card({ content: `<p style="color:var(--muted);margin:0">&ldquo;The streaming SSR makes our pages feel instant. Lighthouse is happy.&rdquo;</p>`, footer: `<strong>Sara Kim</strong><br><span style="color:var(--muted)">Lead Engineer, Orbit</span>` }),
106
+ card({ content: `<p style="color:var(--muted);margin:0">&ldquo;Finally a UI kit that doesn\'t fight the platform. Just HTML.&rdquo;</p>`, footer: `<strong>Dan Okafor</strong><br><span style="color:var(--muted)">Founder, Stackly</span>` }),
106
107
  ].join(''),
107
108
  }),
108
109
  `grid({
109
110
  cols: 3,
110
- content: testimonials.map(t => testimonial(t)).join(''),
111
+ content: testimonials.map(t => card({
112
+ content: \`<p>\${t.quote}</p>\`,
113
+ footer: \`<strong>\${t.name}</strong><br>\${t.role}\`,
114
+ })).join(''),
111
115
  })`
112
116
  )}
113
117
 
@@ -94,8 +94,6 @@ export default {
94
94
  ['<a href="/components/hero">hero</a>', 'Full-width hero section with eyebrow, title, and actions.'],
95
95
  ['<a href="/components/app-badge">appBadge</a>', 'App Store / Google Play download badge.'],
96
96
  ['<a href="/components/feature">feature</a>', 'Icon + title + description block for feature grids.'],
97
- ['<a href="/components/testimonial">testimonial</a>', 'Customer quote with avatar and star rating.'],
98
- ['<a href="/components/pricing">pricing</a>', 'Plan card with feature list and CTA. Supports highlighted state.'],
99
97
  ['<a href="/components/accordion">accordion</a>', 'Collapsible FAQ items — no JS, native <code>&lt;details&gt;</code>.'],
100
98
  ['<a href="/components/cta">cta</a>', 'Call-to-action block with eyebrow, heading, body, and actions slot.'],
101
99
  ]
@@ -111,9 +109,7 @@ export default {
111
109
  ['<a href="/components/stack">stack</a>', 'Flex column with consistent vertical gap.'],
112
110
  ['<a href="/components/cluster">cluster</a>', 'Flex row with wrapping — for badges, buttons, etc.'],
113
111
  ['<a href="/components/divider">divider</a>', 'Horizontal rule, optionally with centred label.'],
114
- ['<a href="/components/banner">banner</a>', 'Full-width announcement bar above the nav.'],
115
112
  ['<a href="/components/media">media</a>', 'Two-column image + text layout, stacks on mobile.'],
116
- ['<a href="/components/code-window">codeWindow</a>', 'macOS window chrome around a code block. Accepts pre-highlighted HTML.'],
117
113
  ['<a href="/components/footer">footer</a>', 'Site footer with logo, nav links, and legal text. Stacks on mobile.'],
118
114
  ]
119
115
  )}
@@ -2,9 +2,24 @@
2
2
  import { highlight } from '../lib/highlight.js'
3
3
  import { iconZap, iconShield, iconSettings } from '../../../src/ui/icons.js'
4
4
  import { metricsStore } from '../lib/metrics-store.js'
5
- import { codeWindow } from '../../../src/ui/code-window.js'
6
5
  import pkg from '../../../package.json' with { type: 'json' }
7
6
 
7
+ // macOS-style code window chrome — raw HTML (creative override), replacing
8
+ // the removed codeWindow() component. Reuses the same .ui-code-window*
9
+ // CSS classes still defined in pulse-ui.css.
10
+ const codeWindow = ({ content = '', filename = '', lang = '' }) => `
11
+ <div class="ui-code-window" role="region"${filename ? ` aria-label="${filename}"` : ''}>
12
+ <div class="ui-code-window-chrome" aria-hidden="true">
13
+ <span class="ui-code-window-dot"></span>
14
+ <span class="ui-code-window-dot"></span>
15
+ <span class="ui-code-window-dot"></span>
16
+ ${filename ? `<span class="ui-code-window-filename">${filename}</span>` : ''}
17
+ ${lang ? `<span class="ui-code-window-lang">${lang}</span>` : ''}
18
+ </div>
19
+ <pre class="ui-code-window-pre"><code class="ui-code-window-code">${content}</code></pre>
20
+ </div>
21
+ `
22
+
8
23
  const { version } = pkg
9
24
 
10
25
  const exampleSpec = highlight(`export default {
@@ -1,67 +1,73 @@
1
1
  import { renderLayout, h1, lead, section, callout } from '../lib/layout.js'
2
2
  import { prevNext } from '../lib/nav.js'
3
- import { timeline, card } from '../../../src/ui/index.js'
3
+ import { card } from '../../../src/ui/index.js'
4
4
 
5
5
  const { prev, next } = prevNext('/how-it-works')
6
6
 
7
- const agentFlow = timeline({
8
- items: [
7
+ // Numbered vertical flow diagram — raw HTML (creative override), replacing
8
+ // the removed timeline()/timelineItem() components. Styled via .hiw-flow*
9
+ // classes in docs.css.
10
+ const flowStep = ({ dot, label, content }) => `
11
+ <li class="hiw-flow-item">
12
+ <div class="hiw-flow-side">
13
+ <div class="hiw-flow-connector hiw-flow-connector--before"></div>
14
+ <div class="hiw-flow-dot" aria-hidden="true">${dot}</div>
15
+ <div class="hiw-flow-connector hiw-flow-connector--after"></div>
16
+ </div>
17
+ <div class="hiw-flow-main">
18
+ <span class="hiw-flow-label">${label}</span>
19
+ <div class="hiw-flow-body">${content}</div>
20
+ </div>
21
+ </li>
22
+ `
23
+
24
+ const agentFlow = `<ol class="hiw-flow">${[
9
25
  {
10
26
  dot: '1',
11
- dotColor: 'accent',
12
27
  label: 'Understand',
13
28
  content: card({ content: `<strong style="color:var(--ui-text)">Read the guide and inspect the project</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">The agent fetches <code>pulse://workflow</code> and the relevant <code>pulse://guide/*</code> sections, then calls <code>pulse_list_structure</code> to see every existing page, component, and store. Ambiguities are surfaced before any files are touched.</p>` }),
14
29
  },
15
30
  {
16
31
  dot: '2',
17
- dotColor: 'accent',
18
32
  label: 'Plan',
19
33
  content: card({ content: `<strong style="color:var(--ui-text)">Present a plan — wait for confirmation</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">Before writing anything the agent describes what it intends to build: the route, state shape, server data, interactions, and any components or integrations it will use. The task does not proceed until you confirm.</p>` }),
20
34
  },
21
35
  {
22
36
  dot: '3',
23
- dotColor: 'accent',
24
37
  label: 'Build',
25
38
  content: card({ content: `<strong style="color:var(--ui-text)">Write the spec and any supporting files</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">The spec is written as a plain JS object: route, state, server data, mutations, actions, view. The guide constrains every decision — where state lives, how validation is wired, how the view is structured.</p>` }),
26
39
  },
27
40
  {
28
41
  dot: '4',
29
- dotColor: 'accent',
30
42
  label: 'Validate',
31
43
  content: card({ content: `<strong style="color:var(--ui-text)">Call <code>pulse_validate</code> — fix all errors and warnings</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">The spec is checked against the Pulse schema. Every error and warning is resolved before moving on — missing hydrate, heading order violations, missing escaping, structural mistakes. A clean output is the gate to the next phase.</p>` }),
32
44
  },
33
45
  {
34
46
  dot: '5',
35
- dotColor: 'accent',
36
47
  label: 'Browser',
37
48
  content: card({ content: `<strong style="color:var(--ui-text)">Screenshot + Lighthouse — desktop and mobile</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">The agent navigates to the route, takes a screenshot to confirm the rendered output, then runs Lighthouse on both desktop and mobile. Accessibility, Best Practices, and SEO must all be 100. Any failure is fixed and re-verified before continuing.</p>` }),
38
49
  },
39
50
  {
40
51
  dot: '6',
41
- dotColor: 'accent',
42
52
  label: 'Tests',
43
53
  content: card({ content: `<strong style="color:var(--ui-text)">Write tests, run them, fix failures</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">Unit tests cover any pure logic extracted from the spec. View tests use <code>renderSync</code> / <code>render</code> to assert HTML output. All tests must pass. When fixing a bug, a failing test is written first to pin the behaviour.</p>` }),
44
54
  },
45
55
  {
46
56
  dot: '7',
47
- dotColor: 'accent',
48
57
  label: 'Review',
49
58
  content: card({ content: `<strong style="color:var(--ui-text)">Call <code>pulse_review</code> — only after phases 4–6 all pass</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">The agent switches into reviewer mode — reading the source and rendered output against the full spec checklist. Accessibility, empty states, error handling, component usage, and security are all checked. The review agent is always last.</p>` }),
50
59
  },
51
60
  {
52
61
  dot: '8',
53
- dotColor: 'accent',
54
62
  label: 'Fix and re-verify',
55
63
  content: card({ content: `<strong style="color:var(--ui-text)">Fix every review issue — re-run affected gates</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">Every issue raised in review is resolved. Validate, Lighthouse, and tests are re-run to confirm all gates still pass. The task is complete only when every phase clears cleanly.</p>` }),
56
64
  },
57
65
  {
58
66
  dot: '✓',
59
- dotColor: 'success',
60
67
  label: 'Done',
61
68
  content: card({ content: `<strong style="color:var(--ui-text)">The spec is the source of truth</strong><p style="color:var(--ui-muted);margin:.25rem 0 0">Validate clean. Lighthouse 100. Tests passing. Review clear. When all four gates pass, the page is done.</p>` }),
62
69
  },
63
- ],
64
- })
70
+ ].map(flowStep).join('')}</ol>`
65
71
 
66
72
  export default {
67
73
  route: '/how-it-works',