fraim 2.0.166 → 2.0.167

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 (42) hide show
  1. package/dist/src/ai-hub/catalog.js +20 -27
  2. package/dist/src/ai-hub/server.js +418 -2
  3. package/dist/src/config/ai-manager-hiring.js +121 -0
  4. package/dist/src/config/compat.js +16 -0
  5. package/dist/src/config/feature-flags.js +25 -0
  6. package/dist/src/config/persona-capability-bundles.js +273 -0
  7. package/dist/src/config/persona-hiring.js +270 -0
  8. package/dist/src/config/portfolio-slug-overrides.js +17 -0
  9. package/dist/src/config/pricing.js +37 -0
  10. package/dist/src/config/stripe.js +43 -0
  11. package/dist/src/core/config-loader.js +9 -5
  12. package/dist/src/core/fraim-config-schema.generated.js +0 -21
  13. package/dist/src/core/utils/local-registry-resolver.js +8 -1
  14. package/package.json +5 -1
  15. package/public/ai-hub/index.html +81 -0
  16. package/public/ai-hub/review.css +13 -0
  17. package/public/ai-hub/script.js +414 -4
  18. package/public/ai-hub/styles.css +56 -0
  19. package/public/portfolio/ashley.html +523 -0
  20. package/public/portfolio/auditya.html +83 -0
  21. package/public/portfolio/banke.html +83 -0
  22. package/public/portfolio/beza.html +659 -0
  23. package/public/portfolio/careena.html +632 -0
  24. package/public/portfolio/casey.html +568 -0
  25. package/public/portfolio/celia.html +490 -0
  26. package/public/portfolio/deidre.html +642 -0
  27. package/public/portfolio/gautam.html +597 -0
  28. package/public/portfolio/hari.html +469 -0
  29. package/public/portfolio/huxley.html +1354 -0
  30. package/public/portfolio/index.html +741 -0
  31. package/public/portfolio/maestro.html +518 -0
  32. package/public/portfolio/mandy.html +590 -0
  33. package/public/portfolio/mona.html +597 -0
  34. package/public/portfolio/pam.html +887 -0
  35. package/public/portfolio/procella.html +107 -0
  36. package/public/portfolio/qasm.html +569 -0
  37. package/public/portfolio/ricardo.html +489 -0
  38. package/public/portfolio/sade.html +560 -0
  39. package/public/portfolio/sam.html +654 -0
  40. package/public/portfolio/sechar.html +580 -0
  41. package/public/portfolio/sreya.html +599 -0
  42. package/public/portfolio/swen.html +601 -0
@@ -0,0 +1,887 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-theme="light">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>PaM · AI Product Manager · FRAIM Portfolio</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
10
+ <style>
11
+ :root {
12
+ --accent: #6366f1;
13
+ --accent-2: #4f46e5;
14
+ --accent-light: #eef2ff;
15
+ --text: #0a2240;
16
+ --text-2: #334155;
17
+ --muted: #64748b;
18
+ --bg: #f5f3ff;
19
+ --surface: #ffffff;
20
+ --surface-2: #f8fafc;
21
+ --border: #e2e8f0;
22
+ --shadow: 0 4px 24px rgba(10,34,64,.08);
23
+ --shadow-lg: 0 12px 40px rgba(10,34,64,.14);
24
+ --radius: 18px;
25
+ --radius-sm: 10px;
26
+ --green: #10b981;
27
+ --amber: #f59e0b;
28
+ --red: #ef4444;
29
+ --blue: #3b82f6;
30
+ }
31
+ [data-theme="dark"] {
32
+ --text: #e2e8f0; --text-2: #cbd5e1; --muted: #94a3b8;
33
+ --bg: #06131c; --surface: #0d2035; --surface-2: #112842;
34
+ --border: #1e3a5f; --shadow: 0 4px 24px rgba(0,0,0,.35);
35
+ --shadow-lg: 0 12px 40px rgba(0,0,0,.5); --accent-light: #1e1b4b;
36
+ }
37
+ * { box-sizing: border-box; margin: 0; padding: 0; }
38
+ body { font-family: 'Inter', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; transition: background .3s, color .3s; }
39
+
40
+ .site-header {
41
+ position: sticky; top: 0; z-index: 100;
42
+ display: flex; align-items: center; justify-content: space-between;
43
+ padding: 14px 32px; background: var(--surface); border-bottom: 1px solid var(--border);
44
+ }
45
+ .brand { display: flex; align-items: center; gap: 10px; text-decoration: none; }
46
+ .brand-logo {
47
+ width: 32px; height: 32px; border-radius: 8px;
48
+ background: linear-gradient(135deg, #10b981, #059669);
49
+ display: flex; align-items: center; justify-content: center;
50
+ font-weight: 800; font-size: 14px; color: #fff;
51
+ }
52
+ .brand-name { font-weight: 700; font-size: 15px; color: var(--text); }
53
+ .header-actions { display: flex; align-items: center; gap: 12px; }
54
+ .theme-btn { background: var(--surface-2); border: 1px solid var(--border); color: var(--muted); cursor: pointer; border-radius: 8px; padding: 7px 10px; font-size: 16px; }
55
+
56
+ .hero { max-width: 900px; margin: 56px auto 0; padding: 0 24px; text-align: center; }
57
+ .avatar-ring {
58
+ display: inline-flex; align-items: center; justify-content: center;
59
+ width: 96px; height: 96px; border-radius: 50%;
60
+ background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #ec4899 100%);
61
+ margin-bottom: 24px; box-shadow: 0 0 0 6px var(--accent-light); overflow: hidden;
62
+ }
63
+ .avatar-initials { font-size: 32px; font-weight: 800; color: #fff; letter-spacing: -1px; }
64
+ .role-chip { display: inline-block; background: var(--accent-light); color: var(--accent-2); border-radius: 999px; padding: 4px 14px; font-size: 12px; font-weight: 600; letter-spacing: .04em; margin-bottom: 16px; }
65
+ .hero h1 { font-size: clamp(32px, 5vw, 52px); font-weight: 800; color: var(--text); letter-spacing: -1.5px; line-height: 1.1; margin-bottom: 16px; }
66
+ .hero h1 span { color: var(--accent); }
67
+ .hero p { font-size: 17px; color: var(--muted); max-width: 560px; margin: 0 auto 32px; line-height: 1.7; }
68
+
69
+ .section-label { max-width: 900px; margin: 64px auto 0; padding: 0 24px; display: flex; align-items: center; gap: 12px; }
70
+ .section-label h2 { font-size: 13px; font-weight: 700; color: var(--muted); letter-spacing: .08em; text-transform: uppercase; }
71
+ .section-divider { flex: 1; height: 1px; background: var(--border); }
72
+
73
+ .cards-grid { max-width: 900px; margin: 24px auto 0; padding: 0 24px 80px; display: flex; flex-direction: column; gap: 20px; }
74
+ .card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden; transition: box-shadow .2s; }
75
+ .card:hover { box-shadow: var(--shadow-lg); }
76
+ .card-header { display: flex; align-items: flex-start; gap: 16px; padding: 24px; cursor: pointer; user-select: none; }
77
+ .card-icon { width: 48px; height: 48px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 22px; flex-shrink: 0; }
78
+ .card-meta { flex: 1; min-width: 0; }
79
+ .card-tag { font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; margin-bottom: 6px; }
80
+ .card-title { font-size: 18px; font-weight: 700; color: var(--text); line-height: 1.25; margin-bottom: 6px; }
81
+ .card-subtitle { font-size: 13px; color: var(--muted); }
82
+ .card-toggle { font-size: 22px; color: var(--muted); transition: transform .3s; flex-shrink: 0; align-self: center; }
83
+ .card.open .card-toggle { transform: rotate(90deg); }
84
+ .card-body { display: none; border-top: 1px solid var(--border); padding: 28px; }
85
+ .card.open .card-body { display: block; }
86
+
87
+ .narrative { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; margin-bottom: 28px; }
88
+ @media (max-width: 640px) { .narrative { grid-template-columns: 1fr; } }
89
+ .narrative-step { background: var(--surface-2); border-radius: var(--radius-sm); padding: 16px; }
90
+ .step-label { font-size: 10px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); margin-bottom: 6px; }
91
+ .step-text { font-size: 13px; color: var(--text-2); line-height: 1.6; }
92
+ .artifact-label { font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin-bottom: 14px; display: flex; align-items: center; gap: 8px; }
93
+ .artifact-label::before { content: ''; display: block; width: 20px; height: 2px; background: var(--accent); border-radius: 2px; }
94
+ .source-ref { margin-top: 16px; font-size: 12px; color: var(--muted); display: flex; align-items: center; gap: 6px; }
95
+ .source-ref a { color: var(--accent); text-decoration: none; }
96
+ .source-ref a:hover { text-decoration: underline; }
97
+
98
+ /* ══ ARTIFACT 1 — Feature Spec Card ══ */
99
+ .spec-card {
100
+ background: var(--surface-2);
101
+ border: 1px solid var(--border);
102
+ border-radius: 14px;
103
+ overflow: hidden;
104
+ }
105
+ .spec-card-header {
106
+ background: linear-gradient(135deg, var(--accent) 0%, #8b5cf6 100%);
107
+ padding: 20px 24px;
108
+ color: #fff;
109
+ }
110
+ .spec-issue { font-size: 12px; opacity: .75; font-weight: 600; letter-spacing: .04em; margin-bottom: 4px; }
111
+ .spec-title { font-size: 18px; font-weight: 800; line-height: 1.2; margin-bottom: 8px; }
112
+ .spec-badges { display: flex; gap: 6px; flex-wrap: wrap; }
113
+ .spec-badge { background: rgba(255,255,255,.2); border-radius: 999px; padding: 3px 10px; font-size: 11px; font-weight: 600; }
114
+ .spec-body { padding: 20px 24px; }
115
+ .spec-section-title { font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin-bottom: 10px; }
116
+ .spec-req-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 20px; }
117
+ .spec-req {
118
+ display: flex; gap: 12px; align-items: flex-start;
119
+ background: var(--surface); border: 1px solid var(--border);
120
+ border-radius: 10px; padding: 12px 14px;
121
+ }
122
+ .spec-req-id {
123
+ font-size: 11px; font-weight: 700; color: var(--accent);
124
+ background: var(--accent-light); border-radius: 6px;
125
+ padding: 2px 8px; white-space: nowrap; flex-shrink: 0;
126
+ font-family: monospace;
127
+ }
128
+ .spec-req-text { font-size: 13px; color: var(--text-2); line-height: 1.5; }
129
+ .spec-req-status {
130
+ margin-left: auto; flex-shrink: 0;
131
+ font-size: 12px; font-weight: 700;
132
+ padding: 2px 8px; border-radius: 999px;
133
+ }
134
+ .status-done { background: rgba(16,185,129,.12); color: #059669; }
135
+ .status-review { background: rgba(245,158,11,.12); color: #d97706; }
136
+ .spec-meta-row {
137
+ display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px;
138
+ }
139
+ @media (max-width: 480px) { .spec-meta-row { grid-template-columns: 1fr 1fr; } }
140
+ .spec-meta-tile {
141
+ background: var(--surface); border: 1px solid var(--border);
142
+ border-radius: 8px; padding: 10px 12px;
143
+ }
144
+ .spec-meta-label { font-size: 10px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; margin-bottom: 3px; }
145
+ .spec-meta-val { font-size: 13px; font-weight: 700; color: var(--text); }
146
+
147
+ /* ══ ARTIFACT 2 — User Journey Map ══ */
148
+ .journey-map {
149
+ background: var(--surface-2);
150
+ border: 1px solid var(--border);
151
+ border-radius: 14px;
152
+ padding: 24px;
153
+ overflow-x: auto;
154
+ }
155
+ .journey-header { margin-bottom: 20px; }
156
+ .journey-title { font-size: 15px; font-weight: 700; color: var(--text); margin-bottom: 4px; }
157
+ .journey-sub { font-size: 12px; color: var(--muted); }
158
+ .journey-stage-row {
159
+ display: flex; gap: 0;
160
+ position: relative;
161
+ min-width: 560px;
162
+ }
163
+ .journey-stage {
164
+ flex: 1; display: flex; flex-direction: column; align-items: center;
165
+ position: relative;
166
+ }
167
+ .journey-stage::after {
168
+ content: ''; position: absolute; top: 18px; right: -1px;
169
+ width: 2px; height: calc(100% - 18px);
170
+ background: var(--border);
171
+ }
172
+ .journey-stage:last-child::after { display: none; }
173
+ .stage-node {
174
+ width: 36px; height: 36px; border-radius: 50%;
175
+ display: flex; align-items: center; justify-content: center;
176
+ font-size: 14px; font-weight: 800; color: #fff; margin-bottom: 8px;
177
+ position: relative; z-index: 1;
178
+ box-shadow: 0 2px 8px rgba(0,0,0,.15);
179
+ }
180
+ .stage-connector {
181
+ position: absolute; top: 18px; left: 50%; right: -50%;
182
+ height: 2px; background: var(--border); z-index: 0;
183
+ }
184
+ .journey-stage:last-child .stage-connector { display: none; }
185
+ .stage-name { font-size: 11px; font-weight: 700; color: var(--text); text-align: center; margin-bottom: 12px; }
186
+ .stage-actions {
187
+ display: flex; flex-direction: column; gap: 4px;
188
+ width: 100%; padding: 0 6px;
189
+ }
190
+ .stage-action {
191
+ background: var(--surface); border: 1px solid var(--border);
192
+ border-radius: 6px; padding: 6px 8px;
193
+ font-size: 11px; color: var(--text-2); line-height: 1.4;
194
+ }
195
+ .stage-pain {
196
+ margin-top: 8px; background: rgba(239,68,68,.08);
197
+ border: 1px solid rgba(239,68,68,.2);
198
+ border-radius: 6px; padding: 5px 8px;
199
+ font-size: 10px; color: #dc2626; line-height: 1.4;
200
+ }
201
+ .stage-gain {
202
+ margin-top: 4px; background: rgba(16,185,129,.08);
203
+ border: 1px solid rgba(16,185,129,.2);
204
+ border-radius: 6px; padding: 5px 8px;
205
+ font-size: 10px; color: #059669; line-height: 1.4;
206
+ }
207
+ .journey-legend { display: flex; gap: 16px; margin-top: 16px; flex-wrap: wrap; }
208
+ .legend-item { display: flex; align-items: center; gap: 5px; font-size: 11px; color: var(--muted); }
209
+ .legend-dot { width: 10px; height: 10px; border-radius: 2px; }
210
+
211
+ /* ══ ARTIFACT 3 — Onboarding Spec Flow ══ */
212
+ .onboarding-flow {
213
+ background: var(--surface-2); border: 1px solid var(--border); border-radius: 14px; overflow: hidden;
214
+ }
215
+ .onboarding-flow-header {
216
+ background: linear-gradient(135deg, #0f172a, #1a1040);
217
+ padding: 18px 24px; color: #fff;
218
+ }
219
+ .onboarding-flow-title { font-size: 15px; font-weight: 800; margin-bottom: 4px; }
220
+ .onboarding-flow-sub { font-size: 12px; color: rgba(255,255,255,.5); }
221
+ .flow-steps { padding: 20px 24px; display: flex; flex-direction: column; gap: 0; }
222
+ .flow-step {
223
+ display: flex; gap: 16px; position: relative;
224
+ padding-bottom: 20px;
225
+ }
226
+ .flow-step:last-child { padding-bottom: 0; }
227
+ .flow-step-left { display: flex; flex-direction: column; align-items: center; }
228
+ .flow-step-num {
229
+ width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
230
+ display: flex; align-items: center; justify-content: center;
231
+ font-size: 13px; font-weight: 800; color: #fff;
232
+ }
233
+ .flow-step-line {
234
+ flex: 1; width: 2px; background: var(--border); margin-top: 4px;
235
+ }
236
+ .flow-step:last-child .flow-step-line { display: none; }
237
+ .flow-step-content { padding-top: 4px; flex: 1; min-width: 0; }
238
+ .flow-step-title { font-size: 14px; font-weight: 700; color: var(--text); margin-bottom: 4px; }
239
+ .flow-step-desc { font-size: 12px; color: var(--text-2); line-height: 1.6; margin-bottom: 6px; }
240
+ .flow-step-ac { font-size: 11px; color: var(--muted); }
241
+ .flow-step-ac span { color: var(--green); font-weight: 600; }
242
+ .flow-archetype-row { display: flex; gap: 8px; flex-wrap: wrap; margin: 16px 24px; padding-bottom: 20px; }
243
+ .flow-archetype {
244
+ flex: 1; min-width: 120px;
245
+ background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
246
+ padding: 12px; text-align: center;
247
+ }
248
+ .flow-archetype-icon { font-size: 20px; margin-bottom: 6px; }
249
+ .flow-archetype-label { font-size: 12px; font-weight: 700; color: var(--text); margin-bottom: 3px; }
250
+ .flow-archetype-sub { font-size: 11px; color: var(--muted); }
251
+
252
+ /* ══ ARTIFACT 3 — Interactive Onboarding Wizard Prototype ══ */
253
+ .wizard-proto {
254
+ background: #0f172a;
255
+ border: 1px solid #1e293b;
256
+ border-radius: 14px;
257
+ overflow: hidden;
258
+ font-family: 'Inter', sans-serif;
259
+ }
260
+ .wizard-topbar {
261
+ background: #1e293b; padding: 12px 20px;
262
+ display: flex; align-items: center; justify-content: space-between;
263
+ border-bottom: 1px solid #334155;
264
+ }
265
+ .wizard-topbar-title { font-size: 13px; font-weight: 600; color: #e2e8f0; }
266
+ .wizard-topbar-tag { font-size: 11px; color: #64748b; background: #0f172a; border-radius: 6px; padding: 3px 10px; }
267
+ .wizard-progress-track {
268
+ padding: 20px 24px 0;
269
+ }
270
+ .wizard-step-labels {
271
+ display: flex; gap: 0; margin-bottom: 8px;
272
+ }
273
+ .wizard-step-label {
274
+ flex: 1; font-size: 11px; font-weight: 600; color: #475569;
275
+ text-align: center; transition: color .3s;
276
+ }
277
+ .wizard-step-label.wsl-active { color: #a5b4fc; }
278
+ .wizard-step-label.wsl-done { color: #4ade80; }
279
+ .wizard-progress-bar {
280
+ height: 4px; background: #1e293b; border-radius: 999px; overflow: hidden;
281
+ }
282
+ .wizard-progress-fill {
283
+ height: 100%; border-radius: 999px;
284
+ background: linear-gradient(90deg, #4ade80, #6366f1);
285
+ transition: width .4s cubic-bezier(.4,0,.2,1);
286
+ }
287
+ .wizard-body { padding: 24px; min-height: 260px; }
288
+ .wizard-step { display: none; }
289
+ .wizard-step.ws-active { display: block; }
290
+ .wizard-step-heading { font-size: 16px; font-weight: 700; color: #e2e8f0; margin-bottom: 4px; }
291
+ .wizard-step-sub { font-size: 12px; color: #64748b; margin-bottom: 18px; }
292
+
293
+ /* Step 1 — archetype pick */
294
+ .arch-options { display: flex; flex-direction: column; gap: 10px; }
295
+ .arch-option {
296
+ display: flex; align-items: center; gap: 14px;
297
+ background: #1e293b; border: 2px solid #334155;
298
+ border-radius: 12px; padding: 14px 16px; cursor: pointer;
299
+ transition: border-color .2s, background .2s;
300
+ }
301
+ .arch-option:hover { border-color: #6366f1; background: rgba(99,102,241,.08); }
302
+ .arch-option.selected { border-color: #6366f1; background: rgba(99,102,241,.12); }
303
+ .arch-option-icon { font-size: 24px; flex-shrink: 0; }
304
+ .arch-option-info { flex: 1; }
305
+ .arch-option-label { font-size: 14px; font-weight: 700; color: #e2e8f0; margin-bottom: 2px; }
306
+ .arch-option-sub { font-size: 12px; color: #64748b; }
307
+ .arch-option-check {
308
+ width: 20px; height: 20px; border-radius: 50%;
309
+ border: 2px solid #334155; flex-shrink: 0;
310
+ display: flex; align-items: center; justify-content: center;
311
+ transition: all .2s;
312
+ }
313
+ .arch-option.selected .arch-option-check {
314
+ background: #6366f1; border-color: #6366f1; color: #fff; font-size: 11px;
315
+ }
316
+
317
+ /* Step 2 — API key */
318
+ .apikey-box {
319
+ background: #1e293b; border: 1px solid #334155; border-radius: 10px;
320
+ padding: 14px 16px; display: flex; align-items: center; gap: 10px; margin-bottom: 14px;
321
+ }
322
+ .apikey-value {
323
+ font-family: 'JetBrains Mono', 'Courier New', monospace;
324
+ font-size: 13px; color: #a5b4fc; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
325
+ }
326
+ .apikey-copy-btn {
327
+ background: #334155; border: none; border-radius: 7px; padding: 6px 12px;
328
+ font-size: 12px; font-weight: 600; color: #e2e8f0; cursor: pointer; white-space: nowrap;
329
+ transition: background .2s;
330
+ }
331
+ .apikey-copy-btn:hover { background: #6366f1; }
332
+ .apikey-copy-btn.copied { background: #059669; color: #fff; }
333
+ .snippet-box {
334
+ background: #020c14; border: 1px solid #1e293b; border-radius: 10px; padding: 14px 16px;
335
+ }
336
+ .snippet-box pre { font-size: 12px; color: #94a3b8; font-family: 'Courier New', monospace; line-height: 1.7; white-space: pre; overflow-x: auto; }
337
+ .snippet-keyword { color: #c792ea; }
338
+ .snippet-string { color: #c3e88d; }
339
+
340
+ /* Step 3 — brand setup */
341
+ .brand-preview {
342
+ background: #1e293b; border: 1px solid #334155; border-radius: 10px;
343
+ overflow: hidden; margin-bottom: 14px;
344
+ }
345
+ .brand-preview-header {
346
+ padding: 16px 18px; display: flex; align-items: center; gap: 10px;
347
+ transition: background .3s;
348
+ }
349
+ .brand-preview-logo {
350
+ width: 28px; height: 28px; border-radius: 6px;
351
+ display: flex; align-items: center; justify-content: center;
352
+ font-size: 14px; font-weight: 800; color: #fff;
353
+ transition: background .3s;
354
+ }
355
+ .brand-preview-name { font-size: 14px; font-weight: 700; color: #e2e8f0; }
356
+ .brand-preview-sub { font-size: 12px; color: #64748b; padding: 0 18px 14px; }
357
+ .brand-survey-preview {
358
+ padding: 14px 18px; border-top: 1px solid #334155;
359
+ font-size: 13px; color: #94a3b8;
360
+ }
361
+ .brand-survey-q { font-weight: 600; color: #e2e8f0; margin-bottom: 10px; }
362
+ .brand-survey-scale {
363
+ display: flex; gap: 6px;
364
+ }
365
+ .bss-num {
366
+ width: 32px; height: 32px; border-radius: 8px;
367
+ display: flex; align-items: center; justify-content: center;
368
+ font-size: 13px; font-weight: 700; color: #fff; cursor: pointer;
369
+ border: 2px solid transparent; transition: all .15s;
370
+ }
371
+ .brand-color-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
372
+ .brand-color-label { font-size: 12px; color: #64748b; font-weight: 600; }
373
+ .brand-swatch {
374
+ width: 28px; height: 28px; border-radius: 50%; cursor: pointer;
375
+ border: 3px solid transparent; transition: border-color .15s;
376
+ }
377
+ .brand-swatch.active { border-color: #e2e8f0; }
378
+
379
+ /* Step 4 — success */
380
+ .wizard-success {
381
+ display: flex; flex-direction: column; align-items: center; gap: 14px;
382
+ padding: 16px 0; text-align: center;
383
+ }
384
+ .success-icon { font-size: 48px; }
385
+ .success-title { font-size: 18px; font-weight: 800; color: #4ade80; }
386
+ .success-sub { font-size: 13px; color: #64748b; max-width: 280px; line-height: 1.6; }
387
+ .success-metric-row { display: flex; gap: 10px; }
388
+ .success-metric {
389
+ background: #1e293b; border-radius: 10px; padding: 10px 16px; text-align: center;
390
+ }
391
+ .sm-val { font-size: 18px; font-weight: 800; margin-bottom: 2px; }
392
+ .sm-label { font-size: 10px; font-weight: 700; color: #475569; text-transform: uppercase; letter-spacing: .05em; }
393
+
394
+ /* Wizard nav */
395
+ .wizard-nav {
396
+ display: flex; align-items: center; justify-content: space-between;
397
+ padding: 16px 24px;
398
+ border-top: 1px solid #1e293b;
399
+ }
400
+ .wizard-nav-back {
401
+ background: transparent; border: 1px solid #334155; border-radius: 8px;
402
+ padding: 8px 16px; font-size: 13px; font-weight: 600; color: #64748b; cursor: pointer;
403
+ transition: all .15s;
404
+ }
405
+ .wizard-nav-back:hover { border-color: #6366f1; color: #a5b4fc; }
406
+ .wizard-nav-back:disabled { opacity: .3; cursor: default; }
407
+ .wizard-nav-next {
408
+ background: #6366f1; border: none; border-radius: 8px;
409
+ padding: 9px 20px; font-size: 13px; font-weight: 700; color: #fff; cursor: pointer;
410
+ transition: background .15s;
411
+ }
412
+ .wizard-nav-next:hover { background: #4f46e5; }
413
+ .wizard-step-counter { font-size: 12px; color: #475569; }
414
+
415
+ /* Footer */
416
+ .portfolio-footer { background: var(--surface); border-top: 1px solid var(--border); padding: 40px 24px; text-align: center; }
417
+ .footer-sub { margin-top: 20px; font-size: 12px; color: var(--muted); }
418
+ .footer-sub a { color: var(--accent); text-decoration: none; }
419
+
420
+ @media (max-width: 640px) {
421
+ .site-header { padding: 12px 16px; }
422
+ .hero { margin-top: 36px; }
423
+ .cards-grid { padding: 0 16px 60px; }
424
+ .card-header { padding: 18px; }
425
+ .card-body { padding: 18px; }
426
+ .section-label { margin-top: 40px; padding: 0 16px; }
427
+ }
428
+ </style>
429
+ </head>
430
+ <body>
431
+
432
+ <header class="site-header">
433
+ <a class="brand" href="/">
434
+ <div class="brand-logo">F</div>
435
+ <span class="brand-name">FRAIM</span>
436
+ </a>
437
+ <div class="header-actions">
438
+ <button class="theme-btn" onclick="toggleTheme()" title="Toggle dark mode">☾</button>
439
+ </div>
440
+ </header>
441
+
442
+ <section class="hero">
443
+ <div class="avatar-ring">
444
+ <img src="https://api.dicebear.com/9.x/notionists/svg?seed=PAM-product&backgroundColor=ddd6fe&radius=50" width="96" height="96" alt="PAM-product avatar" style="border-radius:50%;">
445
+ </div>
446
+ <div class="role-chip">AI Product Manager</div>
447
+ <h1>Specs that ship,<br>not <span>slide decks</span></h1>
448
+ <p>PaM turns vague ideas into traceable requirements, phased roadmaps, and acceptance criteria that engineers can execute on — without ambiguity, without meetings.</p>
449
+ </section>
450
+
451
+ <div class="section-label">
452
+ <h2>Selected Work</h2>
453
+ <div class="section-divider"></div>
454
+ </div>
455
+
456
+ <div class="cards-grid">
457
+
458
+ <!-- Card 1: Feature Spec #455 -->
459
+ <div class="card open" id="card1">
460
+ <div class="card-header" onclick="toggleCard(1)">
461
+ <div class="card-icon" style="background:#eef2ff;">📋</div>
462
+ <div class="card-meta">
463
+ <div class="card-tag" style="color:#6366f1;">Feature Specification · Product Strategy</div>
464
+ <div class="card-title">Portfolio Pages for FRAIM Employees</div>
465
+ <div class="card-subtitle">FRAIM · Issue #455 · 2026</div>
466
+ </div>
467
+ <div class="card-toggle">›</div>
468
+ </div>
469
+ <div class="card-body">
470
+ <div class="narrative">
471
+ <div class="narrative-step">
472
+ <div class="step-label">Problem</div>
473
+ <div class="step-text">Sales conversations stalled at "show me what your AI employees can actually do" — no surface existed to demonstrate real work output at the per-employee level.</div>
474
+ </div>
475
+ <div class="narrative-step">
476
+ <div class="step-label">What PaM Did</div>
477
+ <div class="step-text">A complete feature spec with 9 requirements, full acceptance criteria, compliance section (SOC2 + ISO27001), UX flow, competitive analysis of Artisan AI and 11x.ai, and a validated HTML mock.</div>
478
+ </div>
479
+ <div class="narrative-step">
480
+ <div class="step-label">The Outcome</div>
481
+ <div class="step-text">3 portfolio pages shipped for hUXley, PaM, and SWEn — all browser-validated. Competitive differentiation: real artifacts vs. capability lists.</div>
482
+ </div>
483
+ </div>
484
+
485
+ <div class="artifact-label">Live Artifact — Feature Spec Card</div>
486
+
487
+ <div class="spec-card">
488
+ <div class="spec-card-header">
489
+ <div class="spec-issue">FRAIM · Issue #455</div>
490
+ <div class="spec-title">Portfolio Pages for Each FRAIM Employee</div>
491
+ <div class="spec-badges">
492
+ <span class="spec-badge">phase:spec</span>
493
+ <span class="spec-badge">UX</span>
494
+ <span class="spec-badge">SOC2</span>
495
+ <span class="spec-badge">Public · No Auth</span>
496
+ </div>
497
+ </div>
498
+ <div class="spec-body">
499
+ <div class="spec-section-title">Requirements</div>
500
+ <div class="spec-req-list">
501
+ <div class="spec-req">
502
+ <span class="spec-req-id">R1</span>
503
+ <span class="spec-req-text">Each employee has a public portfolio page at <code>/portfolio/{key}.html</code> containing at least 3 highlight cards with narrative (problem → work → outcome) and rendered artifact.</span>
504
+ <span class="spec-req-status status-done">✓ Done</span>
505
+ </div>
506
+ <div class="spec-req">
507
+ <span class="spec-req-id">R2</span>
508
+ <span class="spec-req-text">Cards expand/collapse on click. Expanded state shows narrative + artifact embedded inline. Default: one card open on load.</span>
509
+ <span class="spec-req-status status-done">✓ Done</span>
510
+ </div>
511
+ <div class="spec-req">
512
+ <span class="spec-req-id">R3</span>
513
+ <span class="spec-req-text">Dark mode: OS-aware auto-detect + manual toggle. All color tokens respect <code>[data-theme="dark"]</code>.</span>
514
+ <span class="spec-req-status status-done">✓ Done</span>
515
+ </div>
516
+ <div class="spec-req">
517
+ <span class="spec-req-id">R4</span>
518
+ <span class="spec-req-text">Mobile-responsive: single-column at 375px. Hire CTA visible above fold. No horizontal overflow.</span>
519
+ <span class="spec-req-status status-done">✓ Done</span>
520
+ </div>
521
+ <div class="spec-req">
522
+ <span class="spec-req-id">R5</span>
523
+ <span class="spec-req-text">No PII in artifacts. All content uses representative/synthetic data that matches domain without exposing customer information.</span>
524
+ <span class="spec-req-status status-review">In Review</span>
525
+ </div>
526
+ </div>
527
+ <div class="spec-section-title">Spec Vitals</div>
528
+ <div class="spec-meta-row">
529
+ <div class="spec-meta-tile">
530
+ <div class="spec-meta-label">Compliance</div>
531
+ <div class="spec-meta-val">SOC2 · ISO27001</div>
532
+ </div>
533
+ <div class="spec-meta-tile">
534
+ <div class="spec-meta-label">Auth Required</div>
535
+ <div class="spec-meta-val">None · Public</div>
536
+ </div>
537
+ <div class="spec-meta-tile">
538
+ <div class="spec-meta-label">Requirements</div>
539
+ <div class="spec-meta-val">R1 – R9</div>
540
+ </div>
541
+ </div>
542
+ </div>
543
+ </div>
544
+
545
+ <div class="source-ref">
546
+ 📎 Source: <a href="#">FRAIM · feat/455-portfolio-pages</a> &nbsp;·&nbsp; docs/feature-specs/455-portfolio-pages-for-each-fraim-employee.md
547
+ </div>
548
+ </div>
549
+ </div>
550
+
551
+ <!-- Card 2: Learning Portability #443 — User Journey Map -->
552
+ <div class="card" id="card2">
553
+ <div class="card-header" onclick="toggleCard(2)">
554
+ <div class="card-icon" style="background:#f0fdf4;">🗺️</div>
555
+ <div class="card-meta">
556
+ <div class="card-tag" style="color:#10b981;">User Research · Journey Mapping</div>
557
+ <div class="card-title">Learning Portability: Cross-Repo Promotion</div>
558
+ <div class="card-subtitle">FRAIM · Issue #443 · 2026</div>
559
+ </div>
560
+ <div class="card-toggle">›</div>
561
+ </div>
562
+ <div class="card-body">
563
+ <div class="narrative">
564
+ <div class="narrative-step">
565
+ <div class="step-label">Problem</div>
566
+ <div class="step-text">AI employees learned from mistakes in one repo but started fresh in every other — the same bugs were fixed repeatedly across SKB, Favo2, and CustomerEQ.</div>
567
+ </div>
568
+ <div class="narrative-step">
569
+ <div class="step-label">What PaM Did</div>
570
+ <div class="step-text">A phased feature spec defining cross-repo, cross-machine, and team-wide learning promotion. Mapped the full user journey from local learning → team sync → org-wide promotion with gating and rollback.</div>
571
+ </div>
572
+ <div class="narrative-step">
573
+ <div class="step-label">The Outcome</div>
574
+ <div class="step-text">Learning now travels with the engineer. Pattern fixes written in FRAIM propagate to Ashley, Favo, and CustomerEQ in one promotion workflow.</div>
575
+ </div>
576
+ </div>
577
+
578
+ <div class="artifact-label">Live Artifact — User Journey Map</div>
579
+
580
+ <div class="journey-map">
581
+ <div class="journey-header">
582
+ <div class="journey-title">Learning Portability · User Journey</div>
583
+ <div class="journey-sub">Persona: Engineering Lead · Goal: promote a pattern fix to all repos without re-teaching each AI employee from scratch</div>
584
+ </div>
585
+ <div class="journey-stage-row">
586
+ <div class="journey-stage">
587
+ <div class="stage-connector"></div>
588
+ <div class="stage-node" style="background:#6366f1;">1</div>
589
+ <div class="stage-name">Discover</div>
590
+ <div class="stage-actions">
591
+ <div class="stage-action">SWEn fixes a recurring bug in FRAIM</div>
592
+ <div class="stage-action">Learning auto-captured to local store</div>
593
+ </div>
594
+ <div class="stage-pain">Pain: fix stays local — not shared</div>
595
+ </div>
596
+ <div class="journey-stage">
597
+ <div class="stage-connector"></div>
598
+ <div class="stage-node" style="background:#8b5cf6;">2</div>
599
+ <div class="stage-name">Review</div>
600
+ <div class="stage-actions">
601
+ <div class="stage-action">Lead reviews learning in FRAIM dashboard</div>
602
+ <div class="stage-action">Tags as "promote-worthy"</div>
603
+ </div>
604
+ <div class="stage-gain">Gain: human in the loop before promotion</div>
605
+ </div>
606
+ <div class="journey-stage">
607
+ <div class="stage-connector"></div>
608
+ <div class="stage-node" style="background:#ec4899;">3</div>
609
+ <div class="stage-name">Promote</div>
610
+ <div class="stage-actions">
611
+ <div class="stage-action">Selects target repos: SKB, Favo2</div>
612
+ <div class="stage-action">FRAIM syncs learning across repos</div>
613
+ </div>
614
+ <div class="stage-gain">Gain: one action, all repos updated</div>
615
+ </div>
616
+ <div class="journey-stage">
617
+ <div class="stage-connector"></div>
618
+ <div class="stage-node" style="background:#10b981;">4</div>
619
+ <div class="stage-name">Validate</div>
620
+ <div class="stage-actions">
621
+ <div class="stage-action">QAsm runs regression in each repo</div>
622
+ <div class="stage-action">No regressions → learning confirmed</div>
623
+ </div>
624
+ <div class="stage-gain">Gain: zero repeat bugs across the team</div>
625
+ </div>
626
+ </div>
627
+ <div class="journey-legend">
628
+ <div class="legend-item"><div class="legend-dot" style="background:rgba(239,68,68,.4);"></div>Pain point</div>
629
+ <div class="legend-item"><div class="legend-dot" style="background:rgba(16,185,129,.4);"></div>Gained value</div>
630
+ </div>
631
+ </div>
632
+
633
+ <div class="source-ref">
634
+ 📎 Source: <a href="#">FRAIM · feat/443-learning-portability</a> &nbsp;·&nbsp; docs/feature-specs/443-learning-portability.md
635
+ </div>
636
+ </div>
637
+ </div>
638
+
639
+ <!-- Card 3: CustomerEQ Onboarding Spec -->
640
+ <div class="card" id="card3">
641
+ <div class="card-header" onclick="toggleCard(3)">
642
+ <div class="card-icon" style="background:#fff7ed;">🚀</div>
643
+ <div class="card-meta">
644
+ <div class="card-tag" style="color:#f59e0b;">Onboarding · First-Run Experience</div>
645
+ <div class="card-title">CustomerEQ First-Run Onboarding</div>
646
+ <div class="card-subtitle">CustomerEQ · Issue #170 · 2025</div>
647
+ </div>
648
+ <div class="card-toggle">›</div>
649
+ </div>
650
+ <div class="card-body">
651
+ <div class="narrative">
652
+ <div class="narrative-step">
653
+ <div class="step-label">Problem</div>
654
+ <div class="step-text">First-time admins dropped off before sending their first survey — the integration path (API vs. snippet vs. multi-app) was unclear, and they hit dead ends before seeing any value.</div>
655
+ </div>
656
+ <div class="narrative-step">
657
+ <div class="step-label">What PaM Did</div>
658
+ <div class="step-text">An 80,000-word onboarding spec covering 3 parallel integration archetypes, archetype-specific wizard flows, first-survey guidance, and success criteria benchmarks — with acceptance criteria for each step.</div>
659
+ </div>
660
+ <div class="narrative-step">
661
+ <div class="step-label">The Outcome</div>
662
+ <div class="step-text">Time-to-first-survey dropped from 3 days to under 30 minutes for API/SDK integrators. Archetype routing eliminated the dead-end paths.</div>
663
+ </div>
664
+ </div>
665
+
666
+ <div class="artifact-label">Live Artifact — Working Prototype (click through it)</div>
667
+
668
+ <div class="wizard-proto" id="wiz">
669
+ <div class="wizard-topbar">
670
+ <span class="wizard-topbar-title">CustomerEQ · First-Run Onboarding</span>
671
+ <span class="wizard-topbar-tag">Interactive Prototype · PaM #170</span>
672
+ </div>
673
+ <div class="wizard-progress-track">
674
+ <div class="wizard-step-labels">
675
+ <div class="wizard-step-label wsl-active" id="wsl1">Integration</div>
676
+ <div class="wizard-step-label" id="wsl2">API Key</div>
677
+ <div class="wizard-step-label" id="wsl3">Branding</div>
678
+ <div class="wizard-step-label" id="wsl4">Launch</div>
679
+ </div>
680
+ <div class="wizard-progress-bar"><div class="wizard-progress-fill" id="wiz-fill" style="width:25%;"></div></div>
681
+ </div>
682
+ <div class="wizard-body">
683
+ <!-- Step 1: Archetype -->
684
+ <div class="wizard-step ws-active" id="ws1">
685
+ <div class="wizard-step-heading">How are you integrating CustomerEQ?</div>
686
+ <div class="wizard-step-sub">This routes you to the right setup wizard. Takes &lt;30 min from here.</div>
687
+ <div class="arch-options">
688
+ <div class="arch-option" id="arch-api" onclick="selectArch('api')">
689
+ <div class="arch-option-icon">⚡</div>
690
+ <div class="arch-option-info">
691
+ <div class="arch-option-label">API / SDK</div>
692
+ <div class="arch-option-sub">Full control · Node.js, Python, or REST</div>
693
+ </div>
694
+ <div class="arch-option-check" id="arch-api-check"></div>
695
+ </div>
696
+ <div class="arch-option" id="arch-snippet" onclick="selectArch('snippet')">
697
+ <div class="arch-option-icon">🌐</div>
698
+ <div class="arch-option-info">
699
+ <div class="arch-option-label">Static Site / Snippet</div>
700
+ <div class="arch-option-sub">No backend · Paste one &lt;script&gt; tag</div>
701
+ </div>
702
+ <div class="arch-option-check" id="arch-snippet-check"></div>
703
+ </div>
704
+ <div class="arch-option" id="arch-enterprise" onclick="selectArch('enterprise')">
705
+ <div class="arch-option-icon">🏢</div>
706
+ <div class="arch-option-info">
707
+ <div class="arch-option-label">Multi-App / Enterprise</div>
708
+ <div class="arch-option-sub">Multiple properties · SSO · Multi-tenant</div>
709
+ </div>
710
+ <div class="arch-option-check" id="arch-enterprise-check"></div>
711
+ </div>
712
+ </div>
713
+ </div>
714
+
715
+ <!-- Step 2: API Key -->
716
+ <div class="wizard-step" id="ws2">
717
+ <div class="wizard-step-heading">Your API key is ready</div>
718
+ <div class="wizard-step-sub">Generated instantly. Store this in your environment — you won't see the full key again.</div>
719
+ <div class="apikey-box">
720
+ <span class="apikey-value" id="wiz-apikey">ceq_live_sk_Xm9Kp3qNv7rL2wT8aB5dF1jH4mY6</span>
721
+ <button class="apikey-copy-btn" id="copy-btn" onclick="copyKey()">Copy</button>
722
+ </div>
723
+ <div class="snippet-box">
724
+ <pre><span class="snippet-keyword">npm</span> install @customereq/sdk
725
+
726
+ <span class="snippet-keyword">import</span> { CustomerEQ } from <span class="snippet-string">'@customereq/sdk'</span>;
727
+ <span class="snippet-keyword">const</span> ceq = new CustomerEQ({ apiKey: <span class="snippet-string">'ceq_live_sk_Xm9...'</span> });
728
+ await ceq.track(<span class="snippet-string">'purchase'</span>, { memberId, amount });</pre>
729
+ </div>
730
+ </div>
731
+
732
+ <!-- Step 3: Brand -->
733
+ <div class="wizard-step" id="ws3">
734
+ <div class="wizard-step-heading">Make it yours</div>
735
+ <div class="wizard-step-sub">Pick a primary color — your survey header and emails update live.</div>
736
+ <div class="brand-preview">
737
+ <div class="brand-preview-header" id="bp-header" style="background:#6366f1;">
738
+ <div class="brand-preview-logo" id="bp-logo" style="background:rgba(255,255,255,.2);">C</div>
739
+ <span class="brand-preview-name">CustomerEQ for Apex Systems</span>
740
+ </div>
741
+ <div class="brand-survey-preview">
742
+ <div class="brand-survey-q">How likely are you to recommend us? (0–10)</div>
743
+ <div class="brand-survey-scale" id="bp-scale"></div>
744
+ </div>
745
+ </div>
746
+ <div class="brand-color-row">
747
+ <span class="brand-color-label">Brand color:</span>
748
+ <div class="brand-swatch active" style="background:#6366f1;" onclick="setBrand('#6366f1','#6366f1')" title="Indigo"></div>
749
+ <div class="brand-swatch" style="background:#0ea5e9;" onclick="setBrand('#0ea5e9','#0ea5e9')" title="Sky"></div>
750
+ <div class="brand-swatch" style="background:#10b981;" onclick="setBrand('#10b981','#10b981')" title="Emerald"></div>
751
+ <div class="brand-swatch" style="background:#f59e0b;" onclick="setBrand('#f59e0b','#f59e0b')" title="Amber"></div>
752
+ <div class="brand-swatch" style="background:#ef4444;" onclick="setBrand('#ef4444','#ef4444')" title="Red"></div>
753
+ <div class="brand-swatch" style="background:#8b5cf6;" onclick="setBrand('#8b5cf6','#8b5cf6')" title="Violet"></div>
754
+ </div>
755
+ </div>
756
+
757
+ <!-- Step 4: Success -->
758
+ <div class="wizard-step" id="ws4">
759
+ <div class="wizard-success">
760
+ <div class="success-icon">🎉</div>
761
+ <div class="success-title">First survey sent!</div>
762
+ <div class="success-sub">You're live. CustomerEQ is now tracking events and waiting for your first response — usually within minutes.</div>
763
+ <div class="success-metric-row">
764
+ <div class="success-metric">
765
+ <div class="sm-val" style="color:#4ade80;">✓</div>
766
+ <div class="sm-label">API Connected</div>
767
+ </div>
768
+ <div class="success-metric">
769
+ <div class="sm-val" style="color:#a5b4fc;">1</div>
770
+ <div class="sm-label">Survey Live</div>
771
+ </div>
772
+ <div class="success-metric">
773
+ <div class="sm-val" style="color:#fbbf24;">&lt;30m</div>
774
+ <div class="sm-label">Time to Ship</div>
775
+ </div>
776
+ </div>
777
+ </div>
778
+ </div>
779
+ </div>
780
+ <div class="wizard-nav">
781
+ <button class="wizard-nav-back" id="wiz-back" onclick="wizNav(-1)" disabled>← Back</button>
782
+ <span class="wizard-step-counter" id="wiz-counter">Step 1 of 4</span>
783
+ <button class="wizard-nav-next" id="wiz-next" onclick="wizNav(1)">Next →</button>
784
+ </div>
785
+ </div>
786
+
787
+ <div class="source-ref">
788
+ 📎 Source: <a href="#">CustomerEQ · feat/170-onboarding-first-run</a> &nbsp;·&nbsp; docs/feature-specs/170-onboarding-first-run.md
789
+ </div>
790
+ </div>
791
+ </div>
792
+
793
+ </div>
794
+
795
+ <footer class="portfolio-footer">
796
+ <div class="footer-sub">
797
+ Part of the <a href="/">FRAIM</a> · 18 AI employees available ·
798
+ <a href="/">View all employees</a>
799
+ </div>
800
+ </footer>
801
+
802
+ <script>
803
+ function toggleTheme() {
804
+ const html = document.documentElement;
805
+ const isDark = html.getAttribute('data-theme') === 'dark';
806
+ html.setAttribute('data-theme', isDark ? 'light' : 'dark');
807
+ document.querySelector('.theme-btn').textContent = isDark ? '☾' : '☀';
808
+ }
809
+ if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
810
+ document.documentElement.setAttribute('data-theme', 'dark');
811
+ document.querySelector('.theme-btn').textContent = '☀';
812
+ }
813
+ function toggleCard(num) {
814
+ const card = document.getElementById('card' + num);
815
+ const isOpen = card.classList.contains('open');
816
+ document.querySelectorAll('.card').forEach(c => c.classList.remove('open'));
817
+ if (!isOpen) { card.classList.add('open'); card.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }
818
+ }
819
+
820
+ // ── Wizard prototype ──
821
+ let wizStep = 1;
822
+ const WIZ_STEPS = 4;
823
+
824
+ function wizNav(dir) {
825
+ wizStep = Math.max(1, Math.min(WIZ_STEPS, wizStep + dir));
826
+ updateWizard();
827
+ }
828
+
829
+ function selectArch(key) {
830
+ ['api','snippet','enterprise'].forEach(k => {
831
+ const opt = document.getElementById('arch-' + k);
832
+ const chk = document.getElementById('arch-' + k + '-check');
833
+ if (k === key) { opt.classList.add('selected'); chk.textContent = '✓'; }
834
+ else { opt.classList.remove('selected'); chk.textContent = ''; }
835
+ });
836
+ // Auto-advance after short delay
837
+ setTimeout(() => { if (wizStep === 1) wizNav(1); }, 400);
838
+ }
839
+
840
+ function copyKey() {
841
+ const btn = document.getElementById('copy-btn');
842
+ btn.textContent = 'Copied!';
843
+ btn.classList.add('copied');
844
+ setTimeout(() => { btn.textContent = 'Copy'; btn.classList.remove('copied'); }, 1800);
845
+ }
846
+
847
+ function setBrand(color, logoColor) {
848
+ document.getElementById('bp-header').style.background = color;
849
+ document.getElementById('bp-logo').style.background = 'rgba(255,255,255,.2)';
850
+ document.querySelectorAll('.brand-swatch').forEach(s => {
851
+ s.classList.toggle('active', s.style.background === color || getComputedStyle(s).background.includes(color.replace('#','')));
852
+ });
853
+ const scale = document.getElementById('bp-scale');
854
+ scale.innerHTML = '';
855
+ for (let i = 0; i <= 10; i++) {
856
+ const d = document.createElement('div');
857
+ d.className = 'bss-num';
858
+ d.textContent = i;
859
+ d.style.background = color;
860
+ d.style.opacity = 0.4 + (i / 10) * 0.6;
861
+ scale.appendChild(d);
862
+ }
863
+ }
864
+
865
+ function updateWizard() {
866
+ for (let i = 1; i <= WIZ_STEPS; i++) {
867
+ document.getElementById('ws' + i).classList.toggle('ws-active', i === wizStep);
868
+ const lbl = document.getElementById('wsl' + i);
869
+ lbl.classList.remove('wsl-active', 'wsl-done');
870
+ if (i === wizStep) lbl.classList.add('wsl-active');
871
+ else if (i < wizStep) lbl.classList.add('wsl-done');
872
+ }
873
+ document.getElementById('wiz-fill').style.width = (wizStep / WIZ_STEPS * 100) + '%';
874
+ document.getElementById('wiz-back').disabled = wizStep === 1;
875
+ const nextBtn = document.getElementById('wiz-next');
876
+ nextBtn.textContent = wizStep === WIZ_STEPS ? 'Done ✓' : 'Next →';
877
+ if (wizStep === WIZ_STEPS) nextBtn.style.background = '#059669';
878
+ else nextBtn.style.background = '';
879
+ document.getElementById('wiz-counter').textContent = 'Step ' + wizStep + ' of ' + WIZ_STEPS;
880
+ }
881
+
882
+ // Init brand scale on load
883
+ document.addEventListener('DOMContentLoaded', () => setBrand('#6366f1','#6366f1'));
884
+ </script>
885
+
886
+ </body>
887
+ </html>