@incodetech/web 2.0.0-alpha.1

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 (176) hide show
  1. package/dev/README.md +163 -0
  2. package/dev/getToken.ts +36 -0
  3. package/dev/headless.html +875 -0
  4. package/dev/index.html +366 -0
  5. package/dev/main-headless.tsx +1332 -0
  6. package/dev/main-orchestrated-flow.tsx +1158 -0
  7. package/dev/main-preact.tsx +323 -0
  8. package/dev/main-simplified.tsx +123 -0
  9. package/dev/main-web-component.tsx +256 -0
  10. package/dev/main.tsx +332 -0
  11. package/dev/manual.html +27 -0
  12. package/dev/orchestrated-flow.html +64 -0
  13. package/dev/simplified.html +64 -0
  14. package/dev/tiktok-logo.svg +7 -0
  15. package/package.json +85 -0
  16. package/src/defineCustomElement.tsx +30 -0
  17. package/src/email/email.test.tsx +368 -0
  18. package/src/email/email.tsx +255 -0
  19. package/src/email/emailInput.test.tsx +264 -0
  20. package/src/email/emailInput.tsx +85 -0
  21. package/src/email/styles.css +59 -0
  22. package/src/flow/flow.test.tsx +796 -0
  23. package/src/flow/flow.tsx +292 -0
  24. package/src/flow/flowCompleted.css +30 -0
  25. package/src/flow/flowCompleted.test.tsx +331 -0
  26. package/src/flow/flowCompleted.tsx +121 -0
  27. package/src/flow/flowInit.test.ts +264 -0
  28. package/src/flow/flowInit.ts +94 -0
  29. package/src/flow/flowStart.css +58 -0
  30. package/src/flow/flowStart.test.tsx +49 -0
  31. package/src/flow/flowStart.tsx +41 -0
  32. package/src/flow/incode-logo.svg +8 -0
  33. package/src/flow/index.ts +7 -0
  34. package/src/flow/preloadFlow.test.ts +421 -0
  35. package/src/flow/preloadFlow.ts +171 -0
  36. package/src/flow/styles.css +9 -0
  37. package/src/flow/unsupportedModule.css +21 -0
  38. package/src/flow/unsupportedModule.tsx +39 -0
  39. package/src/flow/useFlowInitialization.test.tsx +292 -0
  40. package/src/flow/useFlowInitialization.ts +128 -0
  41. package/src/flow/useModuleLoader.test.tsx +212 -0
  42. package/src/flow/useModuleLoader.ts +92 -0
  43. package/src/hooks/index.ts +1 -0
  44. package/src/hooks/useManager.test.ts +91 -0
  45. package/src/hooks/useManager.ts +40 -0
  46. package/src/i18n/index.ts +3 -0
  47. package/src/i18n/instance.ts +16 -0
  48. package/src/i18n/setup.ts +184 -0
  49. package/src/i18n/useTranslation.ts +42 -0
  50. package/src/index.ts +27 -0
  51. package/src/permissions/assets/android-dots-icon.svg +7 -0
  52. package/src/permissions/assets/android-settings-icon.svg +16 -0
  53. package/src/permissions/assets/android-toggle-icon.svg +20 -0
  54. package/src/permissions/assets/bank-card-icon.svg +14 -0
  55. package/src/permissions/assets/camera-icon.svg +12 -0
  56. package/src/permissions/assets/camera-ios.svg +53 -0
  57. package/src/permissions/assets/check-icon.svg +8 -0
  58. package/src/permissions/assets/chrome-icon.svg +43 -0
  59. package/src/permissions/assets/password-icon.svg +11 -0
  60. package/src/permissions/assets/permissions-img.svg +51 -0
  61. package/src/permissions/assets/safari-icon.svg +37 -0
  62. package/src/permissions/assets/settings-icon.svg +33 -0
  63. package/src/permissions/assets/toggle-icon.svg +19 -0
  64. package/src/permissions/assets/warning-icon.svg +6 -0
  65. package/src/permissions/boldWithArrow.css +9 -0
  66. package/src/permissions/boldWithArrow.tsx +41 -0
  67. package/src/permissions/denied.css +37 -0
  68. package/src/permissions/denied.tsx +29 -0
  69. package/src/permissions/deniedAndroid.tsx +56 -0
  70. package/src/permissions/deniedDesktop.css +9 -0
  71. package/src/permissions/deniedDesktop.tsx +64 -0
  72. package/src/permissions/deniedIOS.tsx +73 -0
  73. package/src/permissions/deniedInstructions.tsx +19 -0
  74. package/src/permissions/iconWrapper.css +9 -0
  75. package/src/permissions/iconWrapper.tsx +15 -0
  76. package/src/permissions/learnMore.css +37 -0
  77. package/src/permissions/learnMore.tsx +85 -0
  78. package/src/permissions/numberedStep.css +13 -0
  79. package/src/permissions/numberedStep.tsx +14 -0
  80. package/src/permissions/permissions.css +13 -0
  81. package/src/permissions/permissions.tsx +68 -0
  82. package/src/phone/phone.tsx +246 -0
  83. package/src/phone/phoneInput.test.tsx +275 -0
  84. package/src/phone/phoneInput.tsx +249 -0
  85. package/src/phone/styles.css +158 -0
  86. package/src/selfie/cameraButton.css +13 -0
  87. package/src/selfie/cameraButton.tsx +35 -0
  88. package/src/selfie/capture.css +57 -0
  89. package/src/selfie/capture.tsx +232 -0
  90. package/src/selfie/errorModal.tsx +218 -0
  91. package/src/selfie/errorModalContent.css +33 -0
  92. package/src/selfie/errorModalContent.tsx +44 -0
  93. package/src/selfie/faceOutline.css +5 -0
  94. package/src/selfie/faceOutline.tsx +22 -0
  95. package/src/selfie/loadingBorder.css +12 -0
  96. package/src/selfie/loadingBorder.tsx +77 -0
  97. package/src/selfie/manualCaptureButton.css +13 -0
  98. package/src/selfie/manualCaptureButton.tsx +35 -0
  99. package/src/selfie/noMoreAttemptsModal.tsx +44 -0
  100. package/src/selfie/notification.css +9 -0
  101. package/src/selfie/notification.tsx +36 -0
  102. package/src/selfie/retryErrorModal.tsx +56 -0
  103. package/src/selfie/selfie.test.tsx +458 -0
  104. package/src/selfie/selfie.tsx +83 -0
  105. package/src/selfie/selfieTutorial.json +2626 -0
  106. package/src/selfie/styles.css +1 -0
  107. package/src/selfie/tutorial.test.tsx +200 -0
  108. package/src/selfie/tutorial.tsx +43 -0
  109. package/src/setup.ts +33 -0
  110. package/src/shared/baseTutorial/baseTutorial.css +21 -0
  111. package/src/shared/baseTutorial/baseTutorial.test.tsx +184 -0
  112. package/src/shared/baseTutorial/baseTutorial.tsx +55 -0
  113. package/src/shared/baseTutorial/replaceBaseTutorial.test.ts +267 -0
  114. package/src/shared/baseTutorial/replaceBaseTutorial.ts +68 -0
  115. package/src/shared/button/button.css +55 -0
  116. package/src/shared/button/button.test.tsx +101 -0
  117. package/src/shared/button/button.tsx +47 -0
  118. package/src/shared/componentRoot/incodeComponent.tsx +12 -0
  119. package/src/shared/countries/countries.test.ts +75 -0
  120. package/src/shared/countries/countries.ts +139 -0
  121. package/src/shared/countries/index.ts +6 -0
  122. package/src/shared/icons/chevronDown.tsx +22 -0
  123. package/src/shared/icons/index.ts +2 -0
  124. package/src/shared/icons/successIcon.css +5 -0
  125. package/src/shared/icons/successIcon.test.tsx +40 -0
  126. package/src/shared/icons/successIcon.tsx +26 -0
  127. package/src/shared/loader/loadingIcon.css +28 -0
  128. package/src/shared/loader/loadingIcon.tsx +67 -0
  129. package/src/shared/lottie/lottie.tsx +108 -0
  130. package/src/shared/otpInput/otpInput.css +85 -0
  131. package/src/shared/otpInput/otpInput.test.tsx +356 -0
  132. package/src/shared/otpInput/otpInput.tsx +241 -0
  133. package/src/shared/page/incode-logo.svg +3 -0
  134. package/src/shared/page/page.css +47 -0
  135. package/src/shared/page/page.test.tsx +97 -0
  136. package/src/shared/page/page.tsx +91 -0
  137. package/src/shared/page/pageUiConfig.test.ts +112 -0
  138. package/src/shared/page/pageUiConfig.ts +64 -0
  139. package/src/shared/page/verifiedByIncode.css +5 -0
  140. package/src/shared/page/verifiedByIncode.tsx +75 -0
  141. package/src/shared/spacer/spacer.css +149 -0
  142. package/src/shared/spacer/spacer.test.tsx +143 -0
  143. package/src/shared/spacer/spacer.tsx +88 -0
  144. package/src/shared/spinner/index.ts +2 -0
  145. package/src/shared/spinner/spinner.css +28 -0
  146. package/src/shared/spinner/spinner.test.tsx +82 -0
  147. package/src/shared/spinner/spinner.tsx +65 -0
  148. package/src/shared/title/title.css +7 -0
  149. package/src/shared/title/title.tsx +12 -0
  150. package/src/shared/uiConfig/uiConfig.ts +36 -0
  151. package/src/shared/webComponent/incodeModule.ts +29 -0
  152. package/src/shared/webComponent/registerIncodeElement.ts +15 -0
  153. package/src/styles/__mocks__/fetchTheme.ts +19 -0
  154. package/src/styles/applyTheme.ts +37 -0
  155. package/src/styles/cn.test.tsx +57 -0
  156. package/src/styles/cn.tsx +21 -0
  157. package/src/styles/core.css +12 -0
  158. package/src/styles/fetchTheme.test.ts +390 -0
  159. package/src/styles/fetchTheme.ts +88 -0
  160. package/src/styles/generatePalette.ts +111 -0
  161. package/src/styles/reset.css +65 -0
  162. package/src/styles/resolveCssVariableToHex.ts +28 -0
  163. package/src/styles/tailwind.css +291 -0
  164. package/src/styles/themeTypes.ts +18 -0
  165. package/src/styles/tokens/colors.css +190 -0
  166. package/src/styles/tokens/components.css +174 -0
  167. package/src/styles/tokens/index.css +4 -0
  168. package/src/styles/tokens/primitives.css +129 -0
  169. package/src/styles/tokens/semantic.css +51 -0
  170. package/src/svg.d.ts +4 -0
  171. package/src/types/assets.d.ts +1 -0
  172. package/src/types/custom-elements.d.ts +104 -0
  173. package/tsconfig.json +22 -0
  174. package/vite.config.ts +260 -0
  175. package/vitest.config.ts +40 -0
  176. package/vitest.setup.ts +16 -0
@@ -0,0 +1,875 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Headless SDK Demo | Build Your Own UI</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=JetBrains+Mono:wght@400;500;600;700&family=Space+Grotesk:wght@400;500;600;700&display=swap" rel="stylesheet">
10
+ <style>
11
+ :root {
12
+ --bg-dark: #0a0a0f;
13
+ --bg-card: #12121a;
14
+ --bg-elevated: #1a1a24;
15
+ --bg-input: #0d0d12;
16
+ --border: #2a2a3a;
17
+ --border-focus: #00ff88;
18
+ --text-primary: #e0e0e0;
19
+ --text-secondary: #888899;
20
+ --text-muted: #555566;
21
+ --accent-green: #00ff88;
22
+ --accent-green-dim: #00cc6a;
23
+ --accent-blue: #00aaff;
24
+ --accent-yellow: #ffcc00;
25
+ --accent-red: #ff4466;
26
+ --accent-purple: #aa66ff;
27
+ }
28
+
29
+ * {
30
+ box-sizing: border-box;
31
+ }
32
+
33
+ body {
34
+ font-family: 'Space Grotesk', system-ui, sans-serif;
35
+ background-color: var(--bg-dark);
36
+ color: var(--text-primary);
37
+ margin: 0;
38
+ padding: 0;
39
+ min-height: 100vh;
40
+ background-image:
41
+ radial-gradient(ellipse at top left, rgba(0, 255, 136, 0.03) 0%, transparent 50%),
42
+ radial-gradient(ellipse at bottom right, rgba(0, 170, 255, 0.03) 0%, transparent 50%);
43
+ }
44
+
45
+ /* Layout */
46
+ .layout {
47
+ display: grid;
48
+ grid-template-columns: 1fr 1fr;
49
+ min-height: 100vh;
50
+ gap: 0;
51
+ }
52
+
53
+ @media (max-width: 1024px) {
54
+ .layout {
55
+ grid-template-columns: 1fr;
56
+ }
57
+ }
58
+
59
+ /* Left Panel - Custom UI */
60
+ .ui-panel {
61
+ padding: 2rem;
62
+ display: flex;
63
+ flex-direction: column;
64
+ border-right: 1px solid var(--border);
65
+ }
66
+
67
+ .ui-panel-header {
68
+ margin-bottom: 2rem;
69
+ }
70
+
71
+ .ui-badge {
72
+ display: inline-flex;
73
+ align-items: center;
74
+ gap: 0.5rem;
75
+ background: rgba(0, 255, 136, 0.1);
76
+ border: 1px solid rgba(0, 255, 136, 0.3);
77
+ border-radius: 999px;
78
+ padding: 0.375rem 0.875rem;
79
+ font-size: 0.75rem;
80
+ color: var(--accent-green);
81
+ text-transform: uppercase;
82
+ letter-spacing: 0.1em;
83
+ font-weight: 600;
84
+ margin-bottom: 1rem;
85
+ }
86
+
87
+ .ui-title {
88
+ font-size: 1.75rem;
89
+ font-weight: 700;
90
+ margin: 0 0 0.5rem 0;
91
+ background: linear-gradient(135deg, var(--text-primary) 0%, var(--text-secondary) 100%);
92
+ -webkit-background-clip: text;
93
+ -webkit-text-fill-color: transparent;
94
+ background-clip: text;
95
+ }
96
+
97
+ .ui-subtitle {
98
+ color: var(--text-secondary);
99
+ font-size: 0.875rem;
100
+ margin: 0;
101
+ }
102
+
103
+ .ui-content {
104
+ flex: 1;
105
+ display: flex;
106
+ flex-direction: column;
107
+ }
108
+
109
+ /* Right Panel - SDK State */
110
+ .sdk-panel {
111
+ padding: 2rem;
112
+ background: var(--bg-card);
113
+ display: flex;
114
+ flex-direction: column;
115
+ }
116
+
117
+ .sdk-panel-header {
118
+ margin-bottom: 1.5rem;
119
+ }
120
+
121
+ .sdk-badge {
122
+ display: inline-flex;
123
+ align-items: center;
124
+ gap: 0.5rem;
125
+ background: rgba(0, 170, 255, 0.1);
126
+ border: 1px solid rgba(0, 170, 255, 0.3);
127
+ border-radius: 999px;
128
+ padding: 0.375rem 0.875rem;
129
+ font-size: 0.75rem;
130
+ color: var(--accent-blue);
131
+ text-transform: uppercase;
132
+ letter-spacing: 0.1em;
133
+ font-weight: 600;
134
+ margin-bottom: 1rem;
135
+ }
136
+
137
+ .sdk-title {
138
+ font-size: 1.25rem;
139
+ font-weight: 600;
140
+ margin: 0 0 0.25rem 0;
141
+ color: var(--text-primary);
142
+ }
143
+
144
+ .sdk-subtitle {
145
+ color: var(--text-muted);
146
+ font-size: 0.75rem;
147
+ margin: 0;
148
+ font-family: 'JetBrains Mono', monospace;
149
+ }
150
+
151
+ /* Status indicators */
152
+ .status-row {
153
+ display: flex;
154
+ align-items: center;
155
+ gap: 1rem;
156
+ padding: 0.75rem 1rem;
157
+ background: var(--bg-elevated);
158
+ border-radius: 8px;
159
+ margin-bottom: 1rem;
160
+ }
161
+
162
+ .status-dot {
163
+ width: 10px;
164
+ height: 10px;
165
+ border-radius: 50%;
166
+ animation: pulse 2s infinite;
167
+ }
168
+
169
+ .status-dot.idle { background: var(--text-muted); animation: none; }
170
+ .status-dot.loading { background: var(--accent-blue); }
171
+ .status-dot.active { background: var(--accent-yellow); }
172
+ .status-dot.success { background: var(--accent-green); animation: none; }
173
+ .status-dot.error { background: var(--accent-red); animation: none; }
174
+
175
+ @keyframes pulse {
176
+ 0%, 100% { opacity: 1; }
177
+ 50% { opacity: 0.5; }
178
+ }
179
+
180
+ .status-label {
181
+ font-family: 'JetBrains Mono', monospace;
182
+ font-size: 0.875rem;
183
+ color: var(--text-primary);
184
+ flex: 1;
185
+ }
186
+
187
+ /* State viewer */
188
+ .state-viewer {
189
+ flex: 1;
190
+ min-height: 0;
191
+ background: var(--bg-input);
192
+ border: 1px solid var(--border);
193
+ border-radius: 8px;
194
+ overflow: hidden;
195
+ display: flex;
196
+ flex-direction: column;
197
+ }
198
+
199
+ .state-viewer-header {
200
+ display: flex;
201
+ align-items: center;
202
+ gap: 0.5rem;
203
+ padding: 0.75rem 1rem;
204
+ background: var(--bg-elevated);
205
+ border-bottom: 1px solid var(--border);
206
+ }
207
+
208
+ .state-viewer-header span {
209
+ font-family: 'JetBrains Mono', monospace;
210
+ font-size: 0.75rem;
211
+ color: var(--text-muted);
212
+ }
213
+
214
+ .state-viewer-content {
215
+ flex: 1;
216
+ min-height: 0;
217
+ padding: 1rem;
218
+ overflow: auto;
219
+ font-family: 'JetBrains Mono', monospace;
220
+ font-size: 0.8rem;
221
+ line-height: 1.6;
222
+ }
223
+
224
+ .state-viewer-content pre {
225
+ margin: 0;
226
+ white-space: pre-wrap;
227
+ word-break: break-all;
228
+ }
229
+
230
+ /* Event log */
231
+ .event-log {
232
+ flex: 1;
233
+ min-height: 0;
234
+ margin-top: 1rem;
235
+ overflow: hidden;
236
+ background: var(--bg-input);
237
+ border: 1px solid var(--border);
238
+ border-radius: 8px;
239
+ display: flex;
240
+ flex-direction: column;
241
+ }
242
+
243
+ .event-log-title {
244
+ font-size: 0.75rem;
245
+ color: var(--text-muted);
246
+ text-transform: uppercase;
247
+ letter-spacing: 0.1em;
248
+ padding: 0.75rem 1rem;
249
+ background: var(--bg-elevated);
250
+ border-bottom: 1px solid var(--border);
251
+ }
252
+
253
+ .event-log-content {
254
+ flex: 1;
255
+ min-height: 0;
256
+ overflow-y: auto;
257
+ padding: 0.75rem;
258
+ }
259
+
260
+ .log-entry {
261
+ font-family: 'JetBrains Mono', monospace;
262
+ font-size: 0.7rem;
263
+ padding: 0.25rem 0;
264
+ border-bottom: 1px solid rgba(255,255,255,0.03);
265
+ }
266
+
267
+ .log-time { color: var(--text-muted); margin-right: 0.5rem; }
268
+ .log-event { color: var(--accent-green); font-weight: 600; }
269
+ .log-data { color: var(--text-secondary); }
270
+
271
+ /* Form elements */
272
+ .form-container {
273
+ background: var(--bg-card);
274
+ border: 1px solid var(--border);
275
+ border-radius: 16px;
276
+ padding: 2rem;
277
+ }
278
+
279
+ .form-header {
280
+ text-align: center;
281
+ margin-bottom: 2rem;
282
+ }
283
+
284
+ .form-icon {
285
+ font-size: 3rem;
286
+ margin-bottom: 1rem;
287
+ }
288
+
289
+ .form-title {
290
+ font-size: 1.5rem;
291
+ font-weight: 600;
292
+ margin: 0 0 0.5rem 0;
293
+ }
294
+
295
+ .form-description {
296
+ color: var(--text-secondary);
297
+ font-size: 0.875rem;
298
+ margin: 0;
299
+ }
300
+
301
+ .input-group {
302
+ margin-bottom: 1.5rem;
303
+ }
304
+
305
+ .input-label {
306
+ display: flex;
307
+ flex-direction: column;
308
+ gap: 0.5rem;
309
+ font-size: 0.875rem;
310
+ color: var(--text-secondary);
311
+ }
312
+
313
+ .input-field {
314
+ width: 100%;
315
+ padding: 1rem;
316
+ background: var(--bg-input);
317
+ border: 2px solid var(--border);
318
+ border-radius: 12px;
319
+ color: var(--text-primary);
320
+ font-size: 1.25rem;
321
+ font-family: 'JetBrains Mono', monospace;
322
+ letter-spacing: 0.05em;
323
+ transition: border-color 0.2s, box-shadow 0.2s;
324
+ }
325
+
326
+ .input-field:focus {
327
+ outline: none;
328
+ border-color: var(--border-focus);
329
+ box-shadow: 0 0 0 4px rgba(0, 255, 136, 0.1);
330
+ }
331
+
332
+ .input-field::placeholder {
333
+ color: var(--text-muted);
334
+ }
335
+
336
+ .input-hint {
337
+ font-size: 0.75rem;
338
+ color: var(--text-muted);
339
+ margin-top: 0.5rem;
340
+ }
341
+
342
+ .input-error {
343
+ font-size: 0.75rem;
344
+ color: var(--accent-red);
345
+ margin-top: 0.5rem;
346
+ }
347
+
348
+ .validation-status {
349
+ display: flex;
350
+ align-items: center;
351
+ gap: 0.5rem;
352
+ font-size: 0.875rem;
353
+ margin-top: 0.75rem;
354
+ }
355
+
356
+ .validation-status.valid { color: var(--accent-green); }
357
+ .validation-status.invalid { color: var(--text-muted); }
358
+
359
+ /* Buttons */
360
+ .btn {
361
+ display: inline-flex;
362
+ align-items: center;
363
+ justify-content: center;
364
+ gap: 0.5rem;
365
+ padding: 1rem 2rem;
366
+ border: none;
367
+ border-radius: 12px;
368
+ font-size: 1rem;
369
+ font-weight: 600;
370
+ font-family: 'Space Grotesk', system-ui, sans-serif;
371
+ cursor: pointer;
372
+ transition: all 0.2s;
373
+ width: 100%;
374
+ }
375
+
376
+ .btn:disabled {
377
+ opacity: 0.5;
378
+ cursor: not-allowed;
379
+ }
380
+
381
+ .btn-primary {
382
+ background: linear-gradient(135deg, var(--accent-green) 0%, var(--accent-green-dim) 100%);
383
+ color: var(--bg-dark);
384
+ }
385
+
386
+ .btn-primary:hover:not(:disabled) {
387
+ transform: translateY(-2px);
388
+ box-shadow: 0 8px 20px rgba(0, 255, 136, 0.3);
389
+ }
390
+
391
+ .btn-secondary {
392
+ background: var(--bg-elevated);
393
+ color: var(--text-primary);
394
+ border: 1px solid var(--border);
395
+ }
396
+
397
+ .btn-secondary:hover:not(:disabled) {
398
+ background: var(--border);
399
+ }
400
+
401
+ .btn-ghost {
402
+ background: transparent;
403
+ color: var(--text-secondary);
404
+ padding: 0.5rem 1rem;
405
+ }
406
+
407
+ .btn-ghost:hover:not(:disabled) {
408
+ color: var(--text-primary);
409
+ }
410
+
411
+ .btn-row {
412
+ display: flex;
413
+ gap: 1rem;
414
+ margin-top: 1rem;
415
+ }
416
+
417
+ /* OTP input */
418
+ .otp-container {
419
+ display: flex;
420
+ gap: 0.75rem;
421
+ justify-content: center;
422
+ margin: 1.5rem 0;
423
+ }
424
+
425
+ .otp-input {
426
+ width: 56px;
427
+ height: 64px;
428
+ text-align: center;
429
+ font-size: 1.5rem;
430
+ font-weight: 700;
431
+ font-family: 'JetBrains Mono', monospace;
432
+ background: var(--bg-input);
433
+ border: 2px solid var(--border);
434
+ border-radius: 12px;
435
+ color: var(--text-primary);
436
+ transition: border-color 0.2s, box-shadow 0.2s;
437
+ }
438
+
439
+ .otp-input:focus {
440
+ outline: none;
441
+ border-color: var(--border-focus);
442
+ box-shadow: 0 0 0 4px rgba(0, 255, 136, 0.1);
443
+ }
444
+
445
+ /* Timer */
446
+ .timer {
447
+ text-align: center;
448
+ color: var(--text-secondary);
449
+ font-size: 0.875rem;
450
+ margin: 1rem 0;
451
+ }
452
+
453
+ .timer-value {
454
+ font-family: 'JetBrains Mono', monospace;
455
+ color: var(--accent-yellow);
456
+ }
457
+
458
+ /* Success/Error states */
459
+ .result-container {
460
+ text-align: center;
461
+ padding: 3rem 2rem;
462
+ }
463
+
464
+ .result-icon {
465
+ font-size: 5rem;
466
+ margin-bottom: 1.5rem;
467
+ }
468
+
469
+ .result-title {
470
+ font-size: 1.75rem;
471
+ font-weight: 700;
472
+ margin: 0 0 0.5rem 0;
473
+ }
474
+
475
+ .result-title.success { color: var(--accent-green); }
476
+ .result-title.error { color: var(--accent-red); }
477
+
478
+ .result-description {
479
+ color: var(--text-secondary);
480
+ margin: 0 0 2rem 0;
481
+ }
482
+
483
+ /* Loading state */
484
+ .loading-container {
485
+ display: flex;
486
+ flex-direction: column;
487
+ align-items: center;
488
+ justify-content: center;
489
+ padding: 3rem;
490
+ text-align: center;
491
+ }
492
+
493
+ .spinner {
494
+ width: 48px;
495
+ height: 48px;
496
+ border: 3px solid var(--border);
497
+ border-top-color: var(--accent-green);
498
+ border-radius: 50%;
499
+ animation: spin 1s linear infinite;
500
+ margin-bottom: 1.5rem;
501
+ }
502
+
503
+ @keyframes spin {
504
+ to { transform: rotate(360deg); }
505
+ }
506
+
507
+ .loading-text {
508
+ color: var(--text-secondary);
509
+ font-size: 0.875rem;
510
+ }
511
+
512
+ /* Idle state */
513
+ .idle-container {
514
+ display: flex;
515
+ flex-direction: column;
516
+ align-items: center;
517
+ justify-content: center;
518
+ padding: 3rem;
519
+ text-align: center;
520
+ }
521
+
522
+ .idle-icon {
523
+ font-size: 4rem;
524
+ margin-bottom: 1.5rem;
525
+ filter: grayscale(1) opacity(0.5);
526
+ }
527
+
528
+ .idle-title {
529
+ font-size: 1.25rem;
530
+ font-weight: 600;
531
+ margin: 0 0 0.5rem 0;
532
+ color: var(--text-primary);
533
+ }
534
+
535
+ .idle-description {
536
+ color: var(--text-muted);
537
+ font-size: 0.875rem;
538
+ margin: 0 0 2rem 0;
539
+ max-width: 300px;
540
+ }
541
+
542
+ /* Code comment style */
543
+ .code-comment {
544
+ font-family: 'JetBrains Mono', monospace;
545
+ font-size: 0.7rem;
546
+ color: var(--text-muted);
547
+ margin-top: 2rem;
548
+ padding: 1rem;
549
+ background: rgba(0,0,0,0.3);
550
+ border-radius: 8px;
551
+ white-space: pre-wrap;
552
+ }
553
+
554
+ /* JSON syntax highlighting */
555
+ .json-key { color: var(--accent-green); }
556
+ .json-string { color: var(--accent-yellow); }
557
+ .json-number { color: var(--accent-purple); }
558
+ .json-boolean { color: var(--accent-blue); }
559
+ .json-null { color: var(--text-muted); }
560
+ .json-bracket { color: var(--text-secondary); }
561
+
562
+ /* Log types */
563
+ .log-init { color: var(--accent-blue); }
564
+ .log-action { color: var(--accent-green); }
565
+ .log-state { color: var(--accent-yellow); }
566
+ .log-flow { color: var(--accent-purple); }
567
+ .log-error { color: var(--accent-red); }
568
+
569
+ /* Welcome screen */
570
+ .welcome-container {
571
+ display: flex;
572
+ flex-direction: column;
573
+ align-items: center;
574
+ justify-content: center;
575
+ padding: 2rem;
576
+ text-align: center;
577
+ min-height: 400px;
578
+ }
579
+
580
+ .welcome-icon {
581
+ font-size: 4rem;
582
+ margin-bottom: 1.5rem;
583
+ }
584
+
585
+ .welcome-title {
586
+ font-size: 2rem;
587
+ font-weight: 700;
588
+ margin: 0 0 0.75rem 0;
589
+ background: linear-gradient(135deg, var(--accent-green) 0%, var(--accent-blue) 100%);
590
+ -webkit-background-clip: text;
591
+ -webkit-text-fill-color: transparent;
592
+ background-clip: text;
593
+ }
594
+
595
+ .welcome-description {
596
+ color: var(--text-secondary);
597
+ font-size: 1rem;
598
+ margin: 0 0 2rem 0;
599
+ max-width: 400px;
600
+ }
601
+
602
+ .config-input-group {
603
+ width: 100%;
604
+ max-width: 400px;
605
+ margin-bottom: 1.5rem;
606
+ text-align: left;
607
+ }
608
+
609
+ .config-label {
610
+ display: flex;
611
+ flex-direction: column;
612
+ gap: 0.5rem;
613
+ font-size: 0.875rem;
614
+ color: var(--text-secondary);
615
+ }
616
+
617
+ .config-input {
618
+ width: 100%;
619
+ padding: 0.875rem 1rem;
620
+ background: var(--bg-input);
621
+ border: 2px solid var(--border);
622
+ border-radius: 8px;
623
+ color: var(--text-primary);
624
+ font-size: 0.9rem;
625
+ font-family: 'JetBrains Mono', monospace;
626
+ transition: border-color 0.2s, box-shadow 0.2s;
627
+ }
628
+
629
+ .config-input:focus {
630
+ outline: none;
631
+ border-color: var(--border-focus);
632
+ box-shadow: 0 0 0 4px rgba(0, 255, 136, 0.1);
633
+ }
634
+
635
+ .config-input::placeholder {
636
+ color: var(--text-muted);
637
+ }
638
+
639
+ .feature-list {
640
+ display: flex;
641
+ flex-direction: column;
642
+ gap: 0.75rem;
643
+ margin-bottom: 2rem;
644
+ text-align: left;
645
+ }
646
+
647
+ .feature {
648
+ display: flex;
649
+ align-items: center;
650
+ gap: 0.75rem;
651
+ color: var(--text-secondary);
652
+ font-size: 0.9rem;
653
+ }
654
+
655
+ .feature-icon {
656
+ font-size: 1.25rem;
657
+ }
658
+
659
+ .btn-large {
660
+ padding: 1.25rem 3rem;
661
+ font-size: 1.1rem;
662
+ }
663
+
664
+ /* Flow progress */
665
+ .flow-progress {
666
+ display: flex;
667
+ justify-content: center;
668
+ gap: 0.5rem;
669
+ padding: 1rem;
670
+ margin-bottom: 1rem;
671
+ background: var(--bg-elevated);
672
+ border-radius: 12px;
673
+ }
674
+
675
+ .flow-step {
676
+ display: flex;
677
+ align-items: center;
678
+ gap: 0.5rem;
679
+ padding: 0.5rem 1rem;
680
+ border-radius: 8px;
681
+ font-size: 0.75rem;
682
+ color: var(--text-muted);
683
+ transition: all 0.2s;
684
+ }
685
+
686
+ .flow-step.active {
687
+ background: rgba(0, 255, 136, 0.1);
688
+ color: var(--accent-green);
689
+ }
690
+
691
+ .flow-step.done {
692
+ color: var(--text-secondary);
693
+ }
694
+
695
+ .flow-step.done .step-number {
696
+ background: var(--accent-green);
697
+ color: var(--bg-dark);
698
+ }
699
+
700
+ .step-number {
701
+ display: flex;
702
+ align-items: center;
703
+ justify-content: center;
704
+ width: 20px;
705
+ height: 20px;
706
+ border-radius: 50%;
707
+ background: var(--border);
708
+ font-size: 0.7rem;
709
+ font-weight: 700;
710
+ }
711
+
712
+ .flow-step.active .step-number {
713
+ background: var(--accent-green);
714
+ color: var(--bg-dark);
715
+ }
716
+
717
+ .step-name {
718
+ font-weight: 500;
719
+ }
720
+
721
+ /* Selfie capture */
722
+ .capture-container {
723
+ display: flex;
724
+ flex-direction: column;
725
+ align-items: center;
726
+ gap: 1rem;
727
+ }
728
+
729
+ .camera-wrapper {
730
+ position: relative;
731
+ width: 100%;
732
+ max-width: 320px;
733
+ aspect-ratio: 3/4;
734
+ border-radius: 16px;
735
+ overflow: hidden;
736
+ background: var(--bg-input);
737
+ }
738
+
739
+ .camera-feed {
740
+ width: 100%;
741
+ height: 100%;
742
+ object-fit: cover;
743
+ transform: scaleX(-1);
744
+ }
745
+
746
+ .face-guide {
747
+ position: absolute;
748
+ inset: 0;
749
+ display: flex;
750
+ align-items: center;
751
+ justify-content: center;
752
+ pointer-events: none;
753
+ }
754
+
755
+ .face-oval {
756
+ width: 70%;
757
+ height: 70%;
758
+ }
759
+
760
+ .capture-status {
761
+ position: absolute;
762
+ bottom: 1rem;
763
+ left: 50%;
764
+ transform: translateX(-50%);
765
+ display: flex;
766
+ align-items: center;
767
+ gap: 0.5rem;
768
+ background: rgba(0, 0, 0, 0.7);
769
+ backdrop-filter: blur(8px);
770
+ padding: 0.5rem 1rem;
771
+ border-radius: 999px;
772
+ font-size: 0.875rem;
773
+ }
774
+
775
+ .capture-status.success {
776
+ background: rgba(0, 255, 136, 0.2);
777
+ color: var(--accent-green);
778
+ }
779
+
780
+ .capture-btn {
781
+ margin-top: 1rem;
782
+ }
783
+
784
+ .upload-status {
785
+ display: flex;
786
+ align-items: center;
787
+ gap: 0.5rem;
788
+ color: var(--text-secondary);
789
+ font-size: 0.875rem;
790
+ }
791
+
792
+ /* Tips list */
793
+ .tips-list {
794
+ list-style: none;
795
+ padding: 0;
796
+ margin: 0 0 2rem 0;
797
+ text-align: left;
798
+ }
799
+
800
+ .tips-list li {
801
+ padding: 0.5rem 0;
802
+ color: var(--text-secondary);
803
+ font-size: 0.9rem;
804
+ }
805
+
806
+ /* Finish data */
807
+ .finish-data {
808
+ background: var(--bg-input);
809
+ border: 1px solid var(--border);
810
+ border-radius: 8px;
811
+ padding: 1rem;
812
+ margin: 1.5rem 0;
813
+ max-width: 400px;
814
+ text-align: left;
815
+ }
816
+
817
+ .finish-data pre {
818
+ margin: 0;
819
+ font-family: 'JetBrains Mono', monospace;
820
+ font-size: 0.8rem;
821
+ color: var(--accent-green);
822
+ white-space: pre-wrap;
823
+ }
824
+
825
+ /* Small spinner */
826
+ .spinner-small {
827
+ width: 16px;
828
+ height: 16px;
829
+ border: 2px solid var(--border);
830
+ border-top-color: var(--accent-green);
831
+ border-radius: 50%;
832
+ animation: spin 1s linear infinite;
833
+ display: inline-block;
834
+ }
835
+
836
+ /* Upload error */
837
+ .upload-error {
838
+ margin-top: 1.5rem;
839
+ padding: 1.5rem;
840
+ background: rgba(255, 68, 102, 0.1);
841
+ border: 1px solid rgba(255, 68, 102, 0.3);
842
+ border-radius: 12px;
843
+ text-align: center;
844
+ }
845
+
846
+ .upload-error .error-icon {
847
+ font-size: 2rem;
848
+ margin-bottom: 0.5rem;
849
+ }
850
+
851
+ .upload-error .error-message {
852
+ color: var(--accent-red);
853
+ font-size: 0.9rem;
854
+ margin: 0 0 0.5rem 0;
855
+ }
856
+
857
+ .upload-error .attempts-remaining {
858
+ color: var(--text-secondary);
859
+ font-size: 0.8rem;
860
+ margin: 0 0 1rem 0;
861
+ }
862
+
863
+ .upload-error .no-attempts {
864
+ color: var(--accent-red);
865
+ font-size: 0.8rem;
866
+ font-weight: 600;
867
+ margin: 0;
868
+ }
869
+ </style>
870
+ </head>
871
+ <body>
872
+ <div id="root"></div>
873
+ <script type="module" src="./main-headless.tsx"></script>
874
+ </body>
875
+ </html>