@koraidv/react 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,3273 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.tsx
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ConsentScreen: () => ConsentScreen,
34
+ CountrySelectionScreen: () => CountrySelectionScreen,
35
+ DocumentCaptureScreen: () => DocumentCaptureScreen,
36
+ DocumentSelectionScreen: () => DocumentSelectionScreen,
37
+ ErrorScreen: () => ErrorScreen,
38
+ KoraIDVProvider: () => KoraIDVProvider,
39
+ LivenessScreen: () => LivenessScreen,
40
+ ProcessingScreen: () => ProcessingScreen,
41
+ QrHandoffScreen: () => QrHandoffScreen,
42
+ ResultScreen: () => ResultScreen,
43
+ ScoreCard: () => ScoreCard,
44
+ ScoreMetricRow: () => ScoreMetricRow,
45
+ SelfieCaptureScreen: () => SelfieCaptureScreen,
46
+ StepProgressBar: () => StepProgressBar,
47
+ VerificationFlow: () => VerificationFlow,
48
+ useKoraIDV: () => useKoraIDV
49
+ });
50
+ module.exports = __toCommonJS(index_exports);
51
+
52
+ // src/context/KoraIDVProvider.tsx
53
+ var import_react = require("react");
54
+ var import_core = require("@koraidv/core");
55
+ var import_jsx_runtime = require("react/jsx-runtime");
56
+ var KoraIDVContext = (0, import_react.createContext)(null);
57
+ function KoraIDVProvider({
58
+ apiKey,
59
+ tenantId,
60
+ config = {},
61
+ children
62
+ }) {
63
+ const sdk = (0, import_react.useMemo)(() => {
64
+ return new import_core.KoraIDV({
65
+ apiKey,
66
+ tenantId,
67
+ ...config
68
+ });
69
+ }, [apiKey, tenantId, config]);
70
+ const value = (0, import_react.useMemo)(
71
+ () => ({
72
+ sdk,
73
+ isConfigured: true
74
+ }),
75
+ [sdk]
76
+ );
77
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(KoraIDVContext.Provider, { value, children });
78
+ }
79
+ function useKoraIDVContext() {
80
+ const context = (0, import_react.useContext)(KoraIDVContext);
81
+ if (!context) {
82
+ throw new Error("useKoraIDV must be used within a KoraIDVProvider");
83
+ }
84
+ return context;
85
+ }
86
+
87
+ // src/hooks/useKoraIDV.ts
88
+ var import_react2 = require("react");
89
+ var import_core2 = require("@koraidv/core");
90
+ function useKoraIDV() {
91
+ const { sdk } = useKoraIDVContext();
92
+ const [state, setState] = (0, import_react2.useState)({
93
+ step: "consent",
94
+ verification: null,
95
+ livenessSession: null,
96
+ currentChallenge: null,
97
+ completedChallenges: 0,
98
+ isLoading: false,
99
+ error: null
100
+ });
101
+ const [selectedDocumentType, setSelectedDocumentType] = (0, import_react2.useState)(null);
102
+ const [documentFrontCaptured, setDocumentFrontCaptured] = (0, import_react2.useState)(false);
103
+ const startVerification = (0, import_react2.useCallback)(
104
+ async (externalId, tier = "standard") => {
105
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
106
+ try {
107
+ await sdk.startVerification(
108
+ { externalId, tier },
109
+ {
110
+ onStepChange: (step) => {
111
+ setState((prev) => ({ ...prev, step }));
112
+ },
113
+ onComplete: (verification) => {
114
+ setState((prev) => ({
115
+ ...prev,
116
+ verification,
117
+ step: "complete",
118
+ isLoading: false
119
+ }));
120
+ },
121
+ onError: (error) => {
122
+ setState((prev) => ({ ...prev, error, isLoading: false }));
123
+ }
124
+ }
125
+ );
126
+ setState((prev) => ({
127
+ ...prev,
128
+ verification: sdk.getCurrentVerification(),
129
+ isLoading: false
130
+ }));
131
+ } catch (error) {
132
+ setState((prev) => ({
133
+ ...prev,
134
+ error,
135
+ isLoading: false
136
+ }));
137
+ }
138
+ },
139
+ [sdk]
140
+ );
141
+ const resumeVerification = (0, import_react2.useCallback)(
142
+ async (verificationId) => {
143
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
144
+ try {
145
+ await sdk.resumeVerification(verificationId, {
146
+ onStepChange: (step) => {
147
+ setState((prev) => ({ ...prev, step }));
148
+ },
149
+ onComplete: (verification) => {
150
+ setState((prev) => ({
151
+ ...prev,
152
+ verification,
153
+ step: "complete",
154
+ isLoading: false
155
+ }));
156
+ },
157
+ onError: (error) => {
158
+ setState((prev) => ({ ...prev, error, isLoading: false }));
159
+ }
160
+ });
161
+ setState((prev) => ({
162
+ ...prev,
163
+ verification: sdk.getCurrentVerification(),
164
+ isLoading: false
165
+ }));
166
+ } catch (error) {
167
+ setState((prev) => ({
168
+ ...prev,
169
+ error,
170
+ isLoading: false
171
+ }));
172
+ }
173
+ },
174
+ [sdk]
175
+ );
176
+ const acceptConsent = (0, import_react2.useCallback)(() => {
177
+ setState((prev) => ({ ...prev, step: "document_selection" }));
178
+ }, []);
179
+ const selectDocumentType = (0, import_react2.useCallback)((type) => {
180
+ setSelectedDocumentType(type);
181
+ setDocumentFrontCaptured(false);
182
+ setState((prev) => ({ ...prev, step: "document_front" }));
183
+ }, []);
184
+ const checkDocumentQuality = (0, import_react2.useCallback)(
185
+ async (imageData) => {
186
+ if (!selectedDocumentType) {
187
+ return { success: false, qualityScore: 0, qualityIssues: ["No document type selected"], details: { textReadability: 0, faceQuality: 0, imageClarity: 0 } };
188
+ }
189
+ return sdk.checkDocumentQuality(imageData, selectedDocumentType);
190
+ },
191
+ [sdk, selectedDocumentType]
192
+ );
193
+ const uploadDocument = (0, import_react2.useCallback)(
194
+ async (imageData, side) => {
195
+ if (!selectedDocumentType) return false;
196
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
197
+ try {
198
+ const result = await sdk.uploadDocument(imageData, side, selectedDocumentType);
199
+ if (result.success) {
200
+ if (side === "front") {
201
+ setDocumentFrontCaptured(true);
202
+ const typeInfo = await import("@koraidv/core").then(
203
+ (m) => m.getDocumentTypeInfo(selectedDocumentType)
204
+ );
205
+ if (typeInfo.requiresBack) {
206
+ setState((prev) => ({ ...prev, step: "document_back", isLoading: false }));
207
+ } else {
208
+ setState((prev) => ({ ...prev, step: "selfie", isLoading: false }));
209
+ }
210
+ } else {
211
+ setState((prev) => ({ ...prev, step: "selfie", isLoading: false }));
212
+ }
213
+ return true;
214
+ } else {
215
+ setState((prev) => ({
216
+ ...prev,
217
+ error: new import_core2.KoraError("QUALITY_VALIDATION_FAILED", result.qualityIssues),
218
+ isLoading: false
219
+ }));
220
+ return false;
221
+ }
222
+ } catch (error) {
223
+ setState((prev) => ({
224
+ ...prev,
225
+ error,
226
+ isLoading: false
227
+ }));
228
+ return false;
229
+ }
230
+ },
231
+ [sdk, selectedDocumentType]
232
+ );
233
+ const uploadSelfie = (0, import_react2.useCallback)(
234
+ async (imageData) => {
235
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
236
+ try {
237
+ const result = await sdk.uploadSelfie(imageData);
238
+ if (result.success) {
239
+ setState((prev) => ({ ...prev, step: "liveness", isLoading: false }));
240
+ return true;
241
+ } else {
242
+ setState((prev) => ({
243
+ ...prev,
244
+ error: new import_core2.KoraError("QUALITY_VALIDATION_FAILED", result.qualityIssues),
245
+ isLoading: false
246
+ }));
247
+ return false;
248
+ }
249
+ } catch (error) {
250
+ setState((prev) => ({
251
+ ...prev,
252
+ error,
253
+ isLoading: false
254
+ }));
255
+ return false;
256
+ }
257
+ },
258
+ [sdk]
259
+ );
260
+ const startLiveness = (0, import_react2.useCallback)(async () => {
261
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
262
+ try {
263
+ const session = await sdk.startLivenessSession();
264
+ setState((prev) => ({
265
+ ...prev,
266
+ livenessSession: session,
267
+ currentChallenge: session.challenges[0] || null,
268
+ completedChallenges: 0,
269
+ isLoading: false
270
+ }));
271
+ } catch (error) {
272
+ setState((prev) => ({
273
+ ...prev,
274
+ error,
275
+ isLoading: false
276
+ }));
277
+ }
278
+ }, [sdk]);
279
+ const submitChallenge = (0, import_react2.useCallback)(
280
+ async (imageData) => {
281
+ const { currentChallenge, livenessSession } = state;
282
+ if (!currentChallenge || !livenessSession) return false;
283
+ setState((prev) => ({ ...prev, isLoading: true }));
284
+ try {
285
+ const result = await sdk.submitLivenessChallenge(currentChallenge, imageData);
286
+ if (result.passed) {
287
+ const nextIndex = state.completedChallenges + 1;
288
+ const nextChallenge = livenessSession.challenges[nextIndex] || null;
289
+ setState((prev) => ({
290
+ ...prev,
291
+ completedChallenges: nextIndex,
292
+ currentChallenge: nextChallenge,
293
+ isLoading: false
294
+ }));
295
+ if (!nextChallenge) {
296
+ setState((prev) => ({ ...prev, step: "processing" }));
297
+ }
298
+ return true;
299
+ }
300
+ setState((prev) => ({ ...prev, isLoading: false }));
301
+ return false;
302
+ } catch (error) {
303
+ setState((prev) => ({
304
+ ...prev,
305
+ error,
306
+ isLoading: false
307
+ }));
308
+ return false;
309
+ }
310
+ },
311
+ [sdk, state]
312
+ );
313
+ const complete = (0, import_react2.useCallback)(async () => {
314
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
315
+ try {
316
+ const verification = await sdk.completeVerification();
317
+ setState((prev) => ({
318
+ ...prev,
319
+ verification,
320
+ step: "complete",
321
+ isLoading: false
322
+ }));
323
+ return verification;
324
+ } catch (error) {
325
+ setState((prev) => ({
326
+ ...prev,
327
+ error,
328
+ isLoading: false
329
+ }));
330
+ return null;
331
+ }
332
+ }, [sdk]);
333
+ const cancel = (0, import_react2.useCallback)(() => {
334
+ sdk.reset();
335
+ setState({
336
+ step: "consent",
337
+ verification: null,
338
+ livenessSession: null,
339
+ currentChallenge: null,
340
+ completedChallenges: 0,
341
+ isLoading: false,
342
+ error: null
343
+ });
344
+ }, [sdk]);
345
+ const retry = (0, import_react2.useCallback)(() => {
346
+ setState((prev) => ({
347
+ ...prev,
348
+ error: null,
349
+ isLoading: false
350
+ }));
351
+ }, []);
352
+ return {
353
+ state,
354
+ startVerification,
355
+ resumeVerification,
356
+ acceptConsent,
357
+ selectDocumentType,
358
+ checkDocumentQuality,
359
+ uploadDocument,
360
+ uploadSelfie,
361
+ startLiveness,
362
+ submitChallenge,
363
+ complete,
364
+ cancel,
365
+ retry,
366
+ sdk
367
+ };
368
+ }
369
+
370
+ // src/components/VerificationFlow.tsx
371
+ var import_react10 = require("react");
372
+ var import_core4 = require("@koraidv/core");
373
+
374
+ // src/components/styles.ts
375
+ var colors = {
376
+ teal: "#0D9488",
377
+ tealDark: "#0F766E",
378
+ tealLight: "#F0FDFA",
379
+ cyan: "#06B6D4",
380
+ success: "#10B981",
381
+ successBg: "#DCFCE7",
382
+ error: "#DC2626",
383
+ errorBg: "#FEF2F2",
384
+ warning: "#D97706",
385
+ warningBg: "#FFFBEB",
386
+ info: "#0284C7",
387
+ infoBg: "#EFF6FF",
388
+ purple: "#7C3AED",
389
+ white: "#FFFFFF",
390
+ black: "#000000",
391
+ darkBg: "#111111",
392
+ surface: "#F9FAFB",
393
+ border: "#E5E7EB",
394
+ borderLight: "#F3F4F6",
395
+ textPrimary: "#111111",
396
+ textSecondary: "#6B7280",
397
+ textTertiary: "#9CA3AF",
398
+ textWhite: "#FFFFFF"
399
+ };
400
+ var keyframesInjected = false;
401
+ function injectKeyframes() {
402
+ if (keyframesInjected || typeof document === "undefined") return;
403
+ keyframesInjected = true;
404
+ const style = document.createElement("style");
405
+ style.textContent = `
406
+ @keyframes kora-spin {
407
+ from { transform: rotate(0deg); }
408
+ to { transform: rotate(360deg); }
409
+ }
410
+ @keyframes kora-scan {
411
+ 0%, 100% { transform: translateY(0); }
412
+ 50% { transform: translateY(100%); }
413
+ }
414
+ @keyframes kora-rotate-ring {
415
+ from { transform: rotate(0deg); }
416
+ to { transform: rotate(360deg); }
417
+ }
418
+ @keyframes kora-pulse {
419
+ 0%, 100% { opacity: 1; }
420
+ 50% { opacity: 0.4; }
421
+ }
422
+ @keyframes kora-ring1 {
423
+ from { transform: rotate(0deg); }
424
+ to { transform: rotate(360deg); }
425
+ }
426
+ @keyframes kora-ring2 {
427
+ from { transform: rotate(0deg); }
428
+ to { transform: rotate(-360deg); }
429
+ }
430
+ @keyframes kora-ring3 {
431
+ from { transform: rotate(0deg); }
432
+ to { transform: rotate(360deg); }
433
+ }
434
+ @keyframes kora-fade-in {
435
+ from { opacity: 0; transform: translateY(8px); }
436
+ to { opacity: 1; transform: translateY(0); }
437
+ }
438
+ `;
439
+ document.head.appendChild(style);
440
+ }
441
+ var styles = {
442
+ // ─── Container ─────────────────────────────────────────────────────────
443
+ container: {
444
+ display: "flex",
445
+ flexDirection: "column",
446
+ minHeight: "100vh",
447
+ backgroundColor: colors.white
448
+ },
449
+ darkContainer: {
450
+ display: "flex",
451
+ flexDirection: "column",
452
+ minHeight: "100vh",
453
+ backgroundColor: colors.darkBg
454
+ },
455
+ // ─── Header ────────────────────────────────────────────────────────────
456
+ header: {
457
+ padding: "32px 24px",
458
+ textAlign: "center"
459
+ },
460
+ iconContainer: {
461
+ marginBottom: "20px"
462
+ },
463
+ title: {
464
+ fontSize: "24px",
465
+ fontWeight: 700,
466
+ color: colors.textPrimary,
467
+ margin: 0
468
+ },
469
+ subtitle: {
470
+ fontSize: "15px",
471
+ color: colors.textSecondary,
472
+ margin: "8px 0 0 0",
473
+ lineHeight: 1.5
474
+ },
475
+ // ─── Content ───────────────────────────────────────────────────────────
476
+ content: {
477
+ flex: 1,
478
+ padding: "0 24px",
479
+ overflowY: "auto"
480
+ },
481
+ scrollContent: {
482
+ flex: 1,
483
+ padding: "0 24px",
484
+ overflowY: "auto"
485
+ },
486
+ section: {
487
+ marginBottom: "24px"
488
+ },
489
+ sectionTitle: {
490
+ fontSize: "16px",
491
+ fontWeight: 600,
492
+ color: colors.textPrimary,
493
+ margin: "0 0 12px 0"
494
+ },
495
+ // ─── Checklist ─────────────────────────────────────────────────────────
496
+ checkList: {
497
+ display: "flex",
498
+ flexDirection: "column",
499
+ gap: "16px"
500
+ },
501
+ checklistItem: {
502
+ display: "flex",
503
+ alignItems: "flex-start",
504
+ gap: "14px"
505
+ },
506
+ checklistIconBox: {
507
+ width: "44px",
508
+ height: "44px",
509
+ borderRadius: "12px",
510
+ display: "flex",
511
+ alignItems: "center",
512
+ justifyContent: "center",
513
+ flexShrink: 0,
514
+ fontSize: "20px"
515
+ },
516
+ checklistTextWrapper: {
517
+ display: "flex",
518
+ flexDirection: "column",
519
+ paddingTop: "2px"
520
+ },
521
+ checklistTitle: {
522
+ fontSize: "15px",
523
+ fontWeight: 600,
524
+ color: colors.textPrimary
525
+ },
526
+ checklistDescription: {
527
+ fontSize: "13px",
528
+ color: colors.textSecondary,
529
+ marginTop: "2px"
530
+ },
531
+ // ─── Body text ─────────────────────────────────────────────────────────
532
+ bodyText: {
533
+ fontSize: "14px",
534
+ color: colors.textSecondary,
535
+ lineHeight: 1.6,
536
+ margin: 0
537
+ },
538
+ // ─── Footer ────────────────────────────────────────────────────────────
539
+ footer: {
540
+ padding: "16px 24px 32px",
541
+ backgroundColor: colors.white
542
+ },
543
+ darkFooter: {
544
+ padding: "16px 24px 32px",
545
+ backgroundColor: colors.darkBg
546
+ },
547
+ // ─── Buttons ───────────────────────────────────────────────────────────
548
+ primaryButton: {
549
+ width: "100%",
550
+ padding: "16px",
551
+ fontSize: "17px",
552
+ fontWeight: 600,
553
+ color: colors.white,
554
+ background: `linear-gradient(135deg, ${colors.teal}, ${colors.tealDark})`,
555
+ border: "none",
556
+ borderRadius: "16px",
557
+ cursor: "pointer",
558
+ transition: "opacity 0.2s",
559
+ display: "flex",
560
+ alignItems: "center",
561
+ justifyContent: "center",
562
+ gap: "8px"
563
+ },
564
+ secondaryButton: {
565
+ width: "100%",
566
+ padding: "16px",
567
+ fontSize: "17px",
568
+ fontWeight: 600,
569
+ color: colors.textPrimary,
570
+ backgroundColor: "transparent",
571
+ border: `2px solid ${colors.border}`,
572
+ borderRadius: "16px",
573
+ cursor: "pointer",
574
+ transition: "border-color 0.2s"
575
+ },
576
+ darkOutlineButton: {
577
+ width: "100%",
578
+ padding: "16px",
579
+ fontSize: "17px",
580
+ fontWeight: 600,
581
+ color: colors.white,
582
+ backgroundColor: "transparent",
583
+ border: "2px solid rgba(255,255,255,0.3)",
584
+ borderRadius: "16px",
585
+ cursor: "pointer"
586
+ },
587
+ textButton: {
588
+ width: "100%",
589
+ padding: "12px",
590
+ fontSize: "14px",
591
+ color: colors.textSecondary,
592
+ backgroundColor: "transparent",
593
+ border: "none",
594
+ cursor: "pointer"
595
+ },
596
+ // ─── Screen header ─────────────────────────────────────────────────────
597
+ screenHeader: {
598
+ display: "flex",
599
+ alignItems: "center",
600
+ padding: "16px 24px",
601
+ gap: "12px"
602
+ },
603
+ screenTitle: {
604
+ fontSize: "18px",
605
+ fontWeight: 600,
606
+ color: colors.textPrimary,
607
+ margin: 0,
608
+ flex: 1
609
+ },
610
+ backButton: {
611
+ width: "40px",
612
+ height: "40px",
613
+ display: "flex",
614
+ alignItems: "center",
615
+ justifyContent: "center",
616
+ fontSize: "20px",
617
+ color: colors.textPrimary,
618
+ backgroundColor: colors.borderLight,
619
+ border: "none",
620
+ borderRadius: "50%",
621
+ cursor: "pointer",
622
+ flexShrink: 0
623
+ },
624
+ closeButton: {
625
+ width: "40px",
626
+ height: "40px",
627
+ display: "flex",
628
+ alignItems: "center",
629
+ justifyContent: "center",
630
+ fontSize: "18px",
631
+ color: colors.textSecondary,
632
+ backgroundColor: colors.borderLight,
633
+ border: "none",
634
+ borderRadius: "50%",
635
+ cursor: "pointer",
636
+ flexShrink: 0
637
+ },
638
+ // ─── Dark header ──────────────────────────────────────────────────────
639
+ darkScreenHeader: {
640
+ display: "flex",
641
+ alignItems: "center",
642
+ padding: "12px 24px",
643
+ gap: "12px"
644
+ },
645
+ darkScreenTitle: {
646
+ fontSize: "18px",
647
+ fontWeight: 600,
648
+ color: colors.white,
649
+ margin: 0,
650
+ flex: 1,
651
+ textAlign: "center"
652
+ },
653
+ darkScreenSubtitle: {
654
+ fontSize: "14px",
655
+ color: "rgba(255,255,255,0.6)",
656
+ margin: "4px 0 0 0",
657
+ textAlign: "center"
658
+ },
659
+ glassCloseButton: {
660
+ width: "40px",
661
+ height: "40px",
662
+ display: "flex",
663
+ alignItems: "center",
664
+ justifyContent: "center",
665
+ fontSize: "18px",
666
+ color: colors.white,
667
+ backgroundColor: "rgba(255,255,255,0.15)",
668
+ border: "none",
669
+ borderRadius: "50%",
670
+ cursor: "pointer",
671
+ flexShrink: 0,
672
+ backdropFilter: "blur(8px)"
673
+ },
674
+ // ─── Search bar ────────────────────────────────────────────────────────
675
+ searchBar: {
676
+ display: "flex",
677
+ alignItems: "center",
678
+ gap: "10px",
679
+ padding: "12px 16px",
680
+ backgroundColor: colors.surface,
681
+ borderRadius: "14px",
682
+ border: `2px solid transparent`,
683
+ margin: "0 0 16px 0"
684
+ },
685
+ searchInput: {
686
+ flex: 1,
687
+ border: "none",
688
+ outline: "none",
689
+ backgroundColor: "transparent",
690
+ fontSize: "15px",
691
+ color: colors.textPrimary
692
+ },
693
+ // ─── Country selection ─────────────────────────────────────────────────
694
+ countryGrid: {
695
+ display: "grid",
696
+ gridTemplateColumns: "1fr 1fr",
697
+ gap: "10px"
698
+ },
699
+ countryCard: {
700
+ display: "flex",
701
+ alignItems: "center",
702
+ gap: "10px",
703
+ padding: "14px",
704
+ backgroundColor: colors.white,
705
+ border: `2px solid ${colors.border}`,
706
+ borderRadius: "14px",
707
+ cursor: "pointer",
708
+ textAlign: "left",
709
+ transition: "border-color 0.15s"
710
+ },
711
+ countryCardSelected: {
712
+ borderColor: colors.teal,
713
+ backgroundColor: colors.tealLight
714
+ },
715
+ countryFlag: {
716
+ fontSize: "24px",
717
+ flexShrink: 0
718
+ },
719
+ countryName: {
720
+ fontSize: "14px",
721
+ fontWeight: 500,
722
+ color: colors.textPrimary,
723
+ flex: 1,
724
+ overflow: "hidden",
725
+ textOverflow: "ellipsis",
726
+ whiteSpace: "nowrap"
727
+ },
728
+ countryCheck: {
729
+ width: "20px",
730
+ height: "20px",
731
+ borderRadius: "50%",
732
+ backgroundColor: colors.teal,
733
+ color: colors.white,
734
+ display: "flex",
735
+ alignItems: "center",
736
+ justifyContent: "center",
737
+ fontSize: "12px",
738
+ fontWeight: 700,
739
+ flexShrink: 0
740
+ },
741
+ // ─── Document selection ────────────────────────────────────────────────
742
+ documentCard: {
743
+ display: "flex",
744
+ alignItems: "center",
745
+ width: "100%",
746
+ padding: "16px",
747
+ marginBottom: "10px",
748
+ backgroundColor: colors.white,
749
+ border: `2px solid ${colors.border}`,
750
+ borderRadius: "16px",
751
+ cursor: "pointer",
752
+ textAlign: "left",
753
+ transition: "border-color 0.15s, background-color 0.15s"
754
+ },
755
+ documentCardSelected: {
756
+ borderColor: colors.teal,
757
+ backgroundColor: colors.tealLight
758
+ },
759
+ documentIconBox: {
760
+ width: "48px",
761
+ height: "48px",
762
+ borderRadius: "14px",
763
+ display: "flex",
764
+ alignItems: "center",
765
+ justifyContent: "center",
766
+ marginRight: "14px",
767
+ flexShrink: 0,
768
+ fontSize: "24px"
769
+ },
770
+ documentInfo: {
771
+ flex: 1,
772
+ display: "flex",
773
+ flexDirection: "column"
774
+ },
775
+ documentName: {
776
+ fontSize: "15px",
777
+ fontWeight: 600,
778
+ color: colors.textPrimary
779
+ },
780
+ documentSubtext: {
781
+ fontSize: "13px",
782
+ color: colors.textSecondary,
783
+ marginTop: "2px"
784
+ },
785
+ documentChevron: {
786
+ fontSize: "18px",
787
+ color: colors.textTertiary,
788
+ flexShrink: 0
789
+ },
790
+ // ─── Capture screens ──────────────────────────────────────────────────
791
+ captureContainer: {
792
+ display: "flex",
793
+ flexDirection: "column",
794
+ height: "100vh",
795
+ backgroundColor: colors.darkBg,
796
+ position: "relative"
797
+ },
798
+ cameraContainer: {
799
+ flex: 1,
800
+ position: "relative",
801
+ overflow: "hidden"
802
+ },
803
+ cameraVideo: {
804
+ width: "100%",
805
+ height: "100%",
806
+ objectFit: "cover"
807
+ },
808
+ // ─── Document viewfinder ──────────────────────────────────────────────
809
+ documentOverlay: {
810
+ position: "absolute",
811
+ top: 0,
812
+ left: 0,
813
+ right: 0,
814
+ bottom: 0,
815
+ display: "flex",
816
+ alignItems: "center",
817
+ justifyContent: "center"
818
+ },
819
+ documentFrame: {
820
+ width: "85%",
821
+ maxWidth: "342px",
822
+ aspectRatio: "1.586",
823
+ border: "2px solid rgba(255,255,255,0.3)",
824
+ borderRadius: "20px",
825
+ position: "relative",
826
+ backgroundColor: "transparent"
827
+ },
828
+ corner: {
829
+ position: "absolute",
830
+ width: "28px",
831
+ height: "28px",
832
+ borderColor: colors.teal,
833
+ borderStyle: "solid",
834
+ borderWidth: "3px 0 0 3px"
835
+ },
836
+ scanLine: {
837
+ position: "absolute",
838
+ left: "10px",
839
+ right: "10px",
840
+ height: "2px",
841
+ background: `linear-gradient(90deg, transparent, ${colors.teal}80, transparent)`,
842
+ animation: "kora-scan 2.5s ease-in-out infinite"
843
+ },
844
+ // ─── Selfie viewfinder ────────────────────────────────────────────────
845
+ selfieOverlay: {
846
+ position: "absolute",
847
+ top: 0,
848
+ left: 0,
849
+ right: 0,
850
+ bottom: 0,
851
+ display: "flex",
852
+ alignItems: "center",
853
+ justifyContent: "center"
854
+ },
855
+ faceGuide: {
856
+ width: "240px",
857
+ height: "300px",
858
+ border: "3px solid rgba(255,255,255,0.2)",
859
+ borderRadius: "50%",
860
+ backgroundColor: "transparent",
861
+ position: "relative"
862
+ },
863
+ rotatingRing: {
864
+ position: "absolute",
865
+ top: "-6px",
866
+ left: "-6px",
867
+ right: "-6px",
868
+ bottom: "-6px",
869
+ borderRadius: "50%",
870
+ border: "3px solid transparent",
871
+ borderTopColor: colors.teal,
872
+ borderRightColor: colors.cyan,
873
+ animation: "kora-rotate-ring 3s linear infinite"
874
+ },
875
+ // ─── Step pills ────────────────────────────────────────────────────────
876
+ stepPillsRow: {
877
+ display: "flex",
878
+ justifyContent: "center",
879
+ gap: "8px",
880
+ padding: "8px 0"
881
+ },
882
+ stepPill: {
883
+ padding: "6px 16px",
884
+ borderRadius: "20px",
885
+ fontSize: "13px",
886
+ fontWeight: 600,
887
+ border: "none",
888
+ cursor: "default"
889
+ },
890
+ // ─── Guidance pill ─────────────────────────────────────────────────────
891
+ guidancePill: {
892
+ display: "inline-flex",
893
+ alignItems: "center",
894
+ gap: "8px",
895
+ padding: "10px 20px",
896
+ borderRadius: "24px",
897
+ fontSize: "14px",
898
+ fontWeight: 500
899
+ },
900
+ pulsingDot: {
901
+ width: "8px",
902
+ height: "8px",
903
+ borderRadius: "50%",
904
+ animation: "kora-pulse 1.5s ease-in-out infinite"
905
+ },
906
+ // ─── Review screen ────────────────────────────────────────────────────
907
+ reviewCard: {
908
+ backgroundColor: "rgba(255,255,255,0.05)",
909
+ borderRadius: "20px",
910
+ padding: "20px",
911
+ margin: "0 24px"
912
+ },
913
+ reviewBadge: {
914
+ display: "inline-flex",
915
+ alignItems: "center",
916
+ gap: "6px",
917
+ padding: "6px 14px",
918
+ borderRadius: "20px",
919
+ backgroundColor: "rgba(16,185,129,0.15)",
920
+ color: colors.success,
921
+ fontSize: "13px",
922
+ fontWeight: 600
923
+ },
924
+ qualityChecks: {
925
+ display: "flex",
926
+ justifyContent: "center",
927
+ gap: "20px",
928
+ marginTop: "16px"
929
+ },
930
+ qualityCheck: {
931
+ display: "flex",
932
+ flexDirection: "column",
933
+ alignItems: "center",
934
+ gap: "4px"
935
+ },
936
+ qualityCheckIcon: {
937
+ width: "28px",
938
+ height: "28px",
939
+ borderRadius: "50%",
940
+ backgroundColor: "rgba(16,185,129,0.15)",
941
+ color: colors.success,
942
+ display: "flex",
943
+ alignItems: "center",
944
+ justifyContent: "center",
945
+ fontSize: "14px"
946
+ },
947
+ qualityCheckLabel: {
948
+ fontSize: "12px",
949
+ color: "rgba(255,255,255,0.6)"
950
+ },
951
+ // ─── Review buttons ───────────────────────────────────────────────────
952
+ reviewButtonsRow: {
953
+ display: "flex",
954
+ gap: "12px",
955
+ padding: "24px"
956
+ },
957
+ // ─── Liveness ──────────────────────────────────────────────────────────
958
+ challengeTitle: {
959
+ fontSize: "24px",
960
+ fontWeight: 700,
961
+ color: colors.white,
962
+ textAlign: "center",
963
+ margin: "0 32px"
964
+ },
965
+ challengeSubtitle: {
966
+ fontSize: "15px",
967
+ color: "rgba(255,255,255,0.6)",
968
+ textAlign: "center",
969
+ margin: "8px 32px 0"
970
+ },
971
+ countdownBadge: {
972
+ width: "40px",
973
+ height: "40px",
974
+ borderRadius: "50%",
975
+ backgroundColor: colors.error,
976
+ color: colors.white,
977
+ display: "flex",
978
+ alignItems: "center",
979
+ justifyContent: "center",
980
+ fontSize: "18px",
981
+ fontWeight: 700,
982
+ position: "absolute",
983
+ top: "-20px",
984
+ left: "50%",
985
+ transform: "translateX(-50%)"
986
+ },
987
+ progressDots: {
988
+ display: "flex",
989
+ justifyContent: "center",
990
+ gap: "8px"
991
+ },
992
+ progressDot: {
993
+ width: "10px",
994
+ height: "10px",
995
+ borderRadius: "50%",
996
+ transition: "background-color 0.3s"
997
+ },
998
+ progressText: {
999
+ fontSize: "14px",
1000
+ color: "rgba(255,255,255,0.6)",
1001
+ textAlign: "center",
1002
+ margin: "8px 0 0 0"
1003
+ },
1004
+ // ─── Progress bar ──────────────────────────────────────────────────────
1005
+ progressBar: {
1006
+ display: "flex",
1007
+ gap: "4px",
1008
+ padding: "12px 24px"
1009
+ },
1010
+ progressSegment: {
1011
+ flex: 1,
1012
+ height: "4px",
1013
+ borderRadius: "2px",
1014
+ transition: "background-color 0.3s"
1015
+ },
1016
+ // ─── Processing screen ─────────────────────────────────────────────────
1017
+ processingContainer: {
1018
+ display: "flex",
1019
+ flexDirection: "column",
1020
+ alignItems: "center",
1021
+ justifyContent: "center",
1022
+ minHeight: "100vh",
1023
+ backgroundColor: colors.darkBg,
1024
+ padding: "24px"
1025
+ },
1026
+ spinnerContainer: {
1027
+ position: "relative",
1028
+ width: "120px",
1029
+ height: "120px",
1030
+ marginBottom: "48px"
1031
+ },
1032
+ spinnerRing: {
1033
+ position: "absolute",
1034
+ borderRadius: "50%",
1035
+ border: "2px solid transparent"
1036
+ },
1037
+ processingSteps: {
1038
+ display: "flex",
1039
+ flexDirection: "column",
1040
+ gap: "16px",
1041
+ width: "100%",
1042
+ maxWidth: "280px"
1043
+ },
1044
+ processingStep: {
1045
+ display: "flex",
1046
+ alignItems: "center",
1047
+ gap: "12px",
1048
+ fontSize: "15px"
1049
+ },
1050
+ processingStepIcon: {
1051
+ width: "24px",
1052
+ height: "24px",
1053
+ borderRadius: "50%",
1054
+ display: "flex",
1055
+ alignItems: "center",
1056
+ justifyContent: "center",
1057
+ fontSize: "12px",
1058
+ flexShrink: 0
1059
+ },
1060
+ // ─── Result screens ────────────────────────────────────────────────────
1061
+ resultContainer: {
1062
+ display: "flex",
1063
+ flexDirection: "column",
1064
+ minHeight: "100vh",
1065
+ backgroundColor: colors.white
1066
+ },
1067
+ resultContent: {
1068
+ flex: 1,
1069
+ padding: "32px 24px",
1070
+ textAlign: "center"
1071
+ },
1072
+ resultIconCircle: {
1073
+ width: "72px",
1074
+ height: "72px",
1075
+ borderRadius: "50%",
1076
+ display: "flex",
1077
+ alignItems: "center",
1078
+ justifyContent: "center",
1079
+ margin: "0 auto 20px",
1080
+ fontSize: "32px"
1081
+ },
1082
+ resultIconOuterRing: {
1083
+ width: "88px",
1084
+ height: "88px",
1085
+ borderRadius: "50%",
1086
+ display: "flex",
1087
+ alignItems: "center",
1088
+ justifyContent: "center",
1089
+ margin: "0 auto 20px"
1090
+ },
1091
+ resultTitle: {
1092
+ fontSize: "24px",
1093
+ fontWeight: 700,
1094
+ color: colors.textPrimary,
1095
+ margin: "0 0 8px 0"
1096
+ },
1097
+ resultSubtitle: {
1098
+ fontSize: "15px",
1099
+ color: colors.textSecondary,
1100
+ margin: "0 0 24px 0",
1101
+ lineHeight: 1.5
1102
+ },
1103
+ // ─── Score card ────────────────────────────────────────────────────────
1104
+ scoreCard: {
1105
+ borderRadius: "20px",
1106
+ padding: "24px",
1107
+ margin: "0 0 24px 0",
1108
+ textAlign: "center",
1109
+ color: colors.white
1110
+ },
1111
+ scoreValue: {
1112
+ fontSize: "48px",
1113
+ fontWeight: 700,
1114
+ lineHeight: 1
1115
+ },
1116
+ scoreBadge: {
1117
+ display: "inline-block",
1118
+ padding: "4px 12px",
1119
+ borderRadius: "12px",
1120
+ fontSize: "12px",
1121
+ fontWeight: 700,
1122
+ letterSpacing: "0.05em",
1123
+ margin: "8px 0 16px",
1124
+ backgroundColor: "rgba(255,255,255,0.2)"
1125
+ },
1126
+ scoreProgressBg: {
1127
+ height: "6px",
1128
+ borderRadius: "3px",
1129
+ backgroundColor: "rgba(255,255,255,0.2)",
1130
+ overflow: "hidden"
1131
+ },
1132
+ scoreProgressFill: {
1133
+ height: "100%",
1134
+ borderRadius: "3px",
1135
+ backgroundColor: colors.white,
1136
+ transition: "width 0.8s ease-out"
1137
+ },
1138
+ // ─── Metric rows ──────────────────────────────────────────────────────
1139
+ metricRow: {
1140
+ display: "flex",
1141
+ alignItems: "center",
1142
+ padding: "14px 16px",
1143
+ borderRadius: "14px",
1144
+ marginBottom: "8px",
1145
+ gap: "12px"
1146
+ },
1147
+ metricIcon: {
1148
+ width: "36px",
1149
+ height: "36px",
1150
+ borderRadius: "10px",
1151
+ display: "flex",
1152
+ alignItems: "center",
1153
+ justifyContent: "center",
1154
+ fontSize: "18px",
1155
+ flexShrink: 0
1156
+ },
1157
+ metricInfo: {
1158
+ flex: 1
1159
+ },
1160
+ metricLabel: {
1161
+ fontSize: "14px",
1162
+ fontWeight: 500,
1163
+ color: colors.textPrimary
1164
+ },
1165
+ metricMessage: {
1166
+ fontSize: "12px",
1167
+ marginTop: "2px"
1168
+ },
1169
+ metricScore: {
1170
+ fontSize: "15px",
1171
+ fontWeight: 700
1172
+ },
1173
+ metricBadge: {
1174
+ fontSize: "11px",
1175
+ fontWeight: 600,
1176
+ padding: "2px 8px",
1177
+ borderRadius: "8px",
1178
+ marginLeft: "8px"
1179
+ },
1180
+ // ─── Expired document ──────────────────────────────────────────────────
1181
+ expiryCard: {
1182
+ backgroundColor: colors.surface,
1183
+ borderRadius: "16px",
1184
+ padding: "20px",
1185
+ marginBottom: "24px",
1186
+ textAlign: "left"
1187
+ },
1188
+ expiryRow: {
1189
+ display: "flex",
1190
+ justifyContent: "space-between",
1191
+ alignItems: "center",
1192
+ marginBottom: "12px"
1193
+ },
1194
+ expiryLabel: {
1195
+ fontSize: "13px",
1196
+ color: colors.textSecondary
1197
+ },
1198
+ expiryValue: {
1199
+ fontSize: "14px",
1200
+ fontWeight: 600,
1201
+ color: colors.textPrimary
1202
+ },
1203
+ expiryBadge: {
1204
+ display: "inline-block",
1205
+ padding: "4px 10px",
1206
+ borderRadius: "10px",
1207
+ fontSize: "12px",
1208
+ fontWeight: 600,
1209
+ backgroundColor: colors.errorBg,
1210
+ color: colors.error
1211
+ },
1212
+ guidanceTip: {
1213
+ display: "flex",
1214
+ alignItems: "flex-start",
1215
+ gap: "12px",
1216
+ marginBottom: "16px"
1217
+ },
1218
+ guidanceTipNumber: {
1219
+ width: "28px",
1220
+ height: "28px",
1221
+ borderRadius: "50%",
1222
+ backgroundColor: colors.tealLight,
1223
+ color: colors.teal,
1224
+ display: "flex",
1225
+ alignItems: "center",
1226
+ justifyContent: "center",
1227
+ fontSize: "13px",
1228
+ fontWeight: 700,
1229
+ flexShrink: 0
1230
+ },
1231
+ guidanceTipText: {
1232
+ fontSize: "14px",
1233
+ color: colors.textSecondary,
1234
+ lineHeight: 1.5,
1235
+ paddingTop: "4px"
1236
+ },
1237
+ // ─── Info card ─────────────────────────────────────────────────────────
1238
+ infoCard: {
1239
+ width: "100%",
1240
+ backgroundColor: colors.surface,
1241
+ borderRadius: "16px",
1242
+ overflow: "hidden",
1243
+ marginBottom: "24px"
1244
+ },
1245
+ infoCardHeader: {
1246
+ display: "flex",
1247
+ alignItems: "center",
1248
+ gap: "10px",
1249
+ padding: "16px",
1250
+ borderBottom: `1px solid ${colors.border}`
1251
+ },
1252
+ infoCardIcon: {
1253
+ fontSize: "20px"
1254
+ },
1255
+ infoCardTitle: {
1256
+ fontSize: "15px",
1257
+ fontWeight: 600,
1258
+ color: colors.textPrimary
1259
+ },
1260
+ infoCardBody: {
1261
+ padding: "16px"
1262
+ },
1263
+ infoRow: {
1264
+ display: "flex",
1265
+ justifyContent: "space-between",
1266
+ marginBottom: "10px"
1267
+ },
1268
+ infoLabel: {
1269
+ fontSize: "13px",
1270
+ color: colors.textSecondary
1271
+ },
1272
+ infoValue: {
1273
+ fontSize: "14px",
1274
+ fontWeight: 500,
1275
+ color: colors.textPrimary
1276
+ },
1277
+ // ─── Loading ───────────────────────────────────────────────────────────
1278
+ loadingContainer: {
1279
+ flex: 1,
1280
+ display: "flex",
1281
+ flexDirection: "column",
1282
+ alignItems: "center",
1283
+ justifyContent: "center",
1284
+ gap: "16px",
1285
+ padding: "24px"
1286
+ },
1287
+ spinner: {
1288
+ width: "40px",
1289
+ height: "40px",
1290
+ border: "3px solid rgba(13,148,136,0.15)",
1291
+ borderTopColor: colors.teal,
1292
+ borderRadius: "50%",
1293
+ animation: "kora-spin 1s linear infinite"
1294
+ },
1295
+ loadingText: {
1296
+ fontSize: "15px",
1297
+ color: colors.textSecondary
1298
+ },
1299
+ // ─── Error ─────────────────────────────────────────────────────────────
1300
+ errorContainer: {
1301
+ flex: 1,
1302
+ display: "flex",
1303
+ flexDirection: "column",
1304
+ alignItems: "center",
1305
+ justifyContent: "center",
1306
+ padding: "24px",
1307
+ textAlign: "center"
1308
+ },
1309
+ errorText: {
1310
+ fontSize: "16px",
1311
+ color: colors.error,
1312
+ marginBottom: "24px"
1313
+ },
1314
+ // ─── Capture footer ───────────────────────────────────────────────────
1315
+ captureFooter: {
1316
+ padding: "24px",
1317
+ display: "flex",
1318
+ justifyContent: "center"
1319
+ },
1320
+ captureButton: {
1321
+ width: "72px",
1322
+ height: "72px",
1323
+ borderRadius: "50%",
1324
+ border: "4px solid #FFFFFF",
1325
+ backgroundColor: "transparent",
1326
+ cursor: "pointer",
1327
+ display: "flex",
1328
+ alignItems: "center",
1329
+ justifyContent: "center",
1330
+ padding: "4px"
1331
+ },
1332
+ captureButtonInner: {
1333
+ width: "100%",
1334
+ height: "100%",
1335
+ borderRadius: "50%",
1336
+ backgroundColor: "#FFFFFF"
1337
+ },
1338
+ // ─── Capture instructions ─────────────────────────────────────────────
1339
+ captureInstructions: {
1340
+ textAlign: "center",
1341
+ padding: "16px 24px"
1342
+ },
1343
+ instructionText: {
1344
+ fontSize: "15px",
1345
+ color: "rgba(255,255,255,0.8)",
1346
+ margin: 0
1347
+ }
1348
+ };
1349
+
1350
+ // src/components/DesignSystem.tsx
1351
+ var import_react3 = require("react");
1352
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1353
+ function StepProgressBar({ total, current, isDark = false }) {
1354
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: styles.progressBar, children: Array.from({ length: total }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1355
+ "div",
1356
+ {
1357
+ style: {
1358
+ ...styles.progressSegment,
1359
+ backgroundColor: i < current ? colors.teal : isDark ? "rgba(255,255,255,0.15)" : colors.border
1360
+ }
1361
+ },
1362
+ i
1363
+ )) });
1364
+ }
1365
+ function ScoreCard({ score, badge, gradient }) {
1366
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { ...styles.scoreCard, background: gradient }, children: [
1367
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.scoreValue, children: [
1368
+ score,
1369
+ "%"
1370
+ ] }),
1371
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: styles.scoreBadge, children: badge }),
1372
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: styles.scoreProgressBg, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...styles.scoreProgressFill, width: `${score}%` } }) })
1373
+ ] });
1374
+ }
1375
+ function ScoreMetricRow({ label, score, icon, status, message }) {
1376
+ const bgColor = status === "pass" ? colors.successBg : status === "fail" ? colors.errorBg : colors.warningBg;
1377
+ const borderColor = status === "pass" ? colors.success : status === "fail" ? colors.error : colors.warning;
1378
+ const textColor = borderColor;
1379
+ const badgeText = status === "pass" ? "PASS" : status === "fail" ? "FAIL" : "REVIEW";
1380
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1381
+ "div",
1382
+ {
1383
+ style: {
1384
+ ...styles.metricRow,
1385
+ backgroundColor: bgColor,
1386
+ borderLeft: `3px solid ${borderColor}`
1387
+ },
1388
+ children: [
1389
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1390
+ "div",
1391
+ {
1392
+ style: {
1393
+ ...styles.metricIcon,
1394
+ backgroundColor: `${borderColor}15`
1395
+ },
1396
+ children: icon
1397
+ }
1398
+ ),
1399
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.metricInfo, children: [
1400
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: styles.metricLabel, children: label }),
1401
+ message && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { ...styles.metricMessage, color: textColor }, children: message })
1402
+ ] }),
1403
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center" }, children: [
1404
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { ...styles.metricScore, color: textColor }, children: [
1405
+ score,
1406
+ "%"
1407
+ ] }),
1408
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1409
+ "span",
1410
+ {
1411
+ style: {
1412
+ ...styles.metricBadge,
1413
+ backgroundColor: `${borderColor}15`,
1414
+ color: textColor
1415
+ },
1416
+ children: badgeText
1417
+ }
1418
+ )
1419
+ ] })
1420
+ ]
1421
+ }
1422
+ );
1423
+ }
1424
+ function ProcessingScreen({ steps }) {
1425
+ (0, import_react3.useEffect)(() => {
1426
+ injectKeyframes();
1427
+ }, []);
1428
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.processingContainer, children: [
1429
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.spinnerContainer, children: [
1430
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1431
+ "div",
1432
+ {
1433
+ style: {
1434
+ ...styles.spinnerRing,
1435
+ inset: "0",
1436
+ borderTopColor: `${colors.teal}40`,
1437
+ animation: "kora-ring1 3s linear infinite"
1438
+ }
1439
+ }
1440
+ ),
1441
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1442
+ "div",
1443
+ {
1444
+ style: {
1445
+ ...styles.spinnerRing,
1446
+ inset: "15px",
1447
+ borderRightColor: `${colors.cyan}40`,
1448
+ animation: "kora-ring2 2s linear infinite"
1449
+ }
1450
+ }
1451
+ ),
1452
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1453
+ "div",
1454
+ {
1455
+ style: {
1456
+ ...styles.spinnerRing,
1457
+ inset: "30px",
1458
+ borderBottomColor: `${colors.teal}40`,
1459
+ animation: "kora-ring3 1.5s linear infinite"
1460
+ }
1461
+ }
1462
+ ),
1463
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1464
+ "div",
1465
+ {
1466
+ style: {
1467
+ position: "absolute",
1468
+ inset: "0",
1469
+ display: "flex",
1470
+ alignItems: "center",
1471
+ justifyContent: "center",
1472
+ fontSize: "28px"
1473
+ },
1474
+ children: "\u{1F6E1}\uFE0F"
1475
+ }
1476
+ )
1477
+ ] }),
1478
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: styles.processingSteps, children: steps.map((step, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.processingStep, children: [
1479
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1480
+ "div",
1481
+ {
1482
+ style: {
1483
+ ...styles.processingStepIcon,
1484
+ backgroundColor: step.status === "done" ? colors.success : step.status === "active" ? colors.teal : "rgba(255,255,255,0.1)",
1485
+ color: step.status === "pending" ? "rgba(255,255,255,0.3)" : colors.white
1486
+ },
1487
+ children: step.status === "done" ? "\u2713" : step.status === "active" ? "\u2026" : "\xB7"
1488
+ }
1489
+ ),
1490
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1491
+ "span",
1492
+ {
1493
+ style: {
1494
+ color: step.status === "pending" ? "rgba(255,255,255,0.3)" : colors.white
1495
+ },
1496
+ children: step.label
1497
+ }
1498
+ )
1499
+ ] }, i)) })
1500
+ ] });
1501
+ }
1502
+ function computeScoreBreakdown(verification) {
1503
+ const liveness = verification.livenessVerification?.livenessScore ?? 0;
1504
+ const livenessPercent = Math.round(liveness * 100);
1505
+ const docQuality = verification.documentVerification?.authenticityScore ?? 0;
1506
+ const docPercent = Math.round(docQuality * 100);
1507
+ const nameMatch = verification.documentVerification?.firstName && verification.documentVerification?.lastName ? 100 : 0;
1508
+ const selfieMatch = verification.faceVerification?.matchScore ?? 0;
1509
+ const selfiePercent = Math.round(selfieMatch * 100);
1510
+ function getStatus(score) {
1511
+ if (score >= 75) return "pass";
1512
+ if (score >= 50) return "borderline";
1513
+ return "fail";
1514
+ }
1515
+ function getMessage(status) {
1516
+ if (status === "fail") return "Below threshold";
1517
+ if (status === "borderline") return "Requires review";
1518
+ return void 0;
1519
+ }
1520
+ const metrics = [
1521
+ {
1522
+ label: "Liveness",
1523
+ score: livenessPercent,
1524
+ icon: "\u{1F441}\uFE0F",
1525
+ status: getStatus(livenessPercent),
1526
+ message: getMessage(getStatus(livenessPercent))
1527
+ },
1528
+ {
1529
+ label: "Name Match",
1530
+ score: nameMatch,
1531
+ icon: "\u{1F4DD}",
1532
+ status: getStatus(nameMatch),
1533
+ message: getMessage(getStatus(nameMatch))
1534
+ },
1535
+ {
1536
+ label: "Document Quality",
1537
+ score: docPercent,
1538
+ icon: "\u{1F4C4}",
1539
+ status: getStatus(docPercent),
1540
+ message: getMessage(getStatus(docPercent))
1541
+ },
1542
+ {
1543
+ label: "Selfie Match",
1544
+ score: selfiePercent,
1545
+ icon: "\u{1F933}",
1546
+ status: getStatus(selfiePercent),
1547
+ message: getMessage(getStatus(selfiePercent))
1548
+ }
1549
+ ];
1550
+ return metrics;
1551
+ }
1552
+
1553
+ // src/components/ConsentScreen.tsx
1554
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1555
+ function ConsentScreen({ onAccept, onDecline }) {
1556
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles.container, children: [
1557
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StepProgressBar, { total: 5, current: 1 }),
1558
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles.header, children: [
1559
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: styles.iconContainer, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1560
+ "div",
1561
+ {
1562
+ style: {
1563
+ width: "72px",
1564
+ height: "72px",
1565
+ borderRadius: "20px",
1566
+ background: `linear-gradient(135deg, ${colors.teal}, ${colors.cyan})`,
1567
+ display: "flex",
1568
+ alignItems: "center",
1569
+ justifyContent: "center",
1570
+ margin: "0 auto",
1571
+ fontSize: "32px"
1572
+ },
1573
+ children: "\u{1F6E1}\uFE0F"
1574
+ }
1575
+ ) }),
1576
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { style: styles.title, children: "Verify your identity" }),
1577
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: styles.subtitle, children: "We need to confirm your identity to continue" })
1578
+ ] }),
1579
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles.content, children: [
1580
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles.checkList, children: [
1581
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1582
+ ConsentItem,
1583
+ {
1584
+ icon: "\u{1FAAA}",
1585
+ bgColor: colors.infoBg,
1586
+ title: "Government-issued ID",
1587
+ description: "Photo of your passport or front & back of your ID"
1588
+ }
1589
+ ),
1590
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1591
+ ConsentItem,
1592
+ {
1593
+ icon: "\u{1F4F8}",
1594
+ bgColor: colors.successBg,
1595
+ title: "Selfie photo",
1596
+ description: "A quick photo to match your face to your document"
1597
+ }
1598
+ ),
1599
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1600
+ ConsentItem,
1601
+ {
1602
+ icon: "\u2728",
1603
+ bgColor: "#F3E8FF",
1604
+ title: "Liveness check",
1605
+ description: "Follow simple prompts to confirm you're a real person"
1606
+ }
1607
+ )
1608
+ ] }),
1609
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { marginTop: "24px" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { style: styles.bodyText, children: [
1610
+ "Your data is encrypted and stored securely. We only use your information for identity verification purposes and in accordance with our",
1611
+ " ",
1612
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: colors.teal, cursor: "pointer" }, children: "privacy policy" }),
1613
+ "."
1614
+ ] }) })
1615
+ ] }),
1616
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles.footer, children: [
1617
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { style: styles.primaryButton, onClick: onAccept, children: [
1618
+ "Get started ",
1619
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontSize: "18px" }, children: "\u2192" })
1620
+ ] }),
1621
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { style: styles.textButton, onClick: onDecline, children: "Decline" })
1622
+ ] })
1623
+ ] });
1624
+ }
1625
+ function ConsentItem({
1626
+ icon,
1627
+ bgColor,
1628
+ title,
1629
+ description
1630
+ }) {
1631
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles.checklistItem, children: [
1632
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { ...styles.checklistIconBox, backgroundColor: bgColor }, children: icon }),
1633
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles.checklistTextWrapper, children: [
1634
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: styles.checklistTitle, children: title }),
1635
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: styles.checklistDescription, children: description })
1636
+ ] })
1637
+ ] });
1638
+ }
1639
+
1640
+ // src/components/CountrySelectionScreen.tsx
1641
+ var import_react4 = require("react");
1642
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1643
+ function CountrySelectionScreen({ countries, onSelect, onCancel }) {
1644
+ const [selected, setSelected] = (0, import_react4.useState)(null);
1645
+ const [searchQuery, setSearchQuery] = (0, import_react4.useState)("");
1646
+ const filteredCountries = (0, import_react4.useMemo)(() => {
1647
+ const countryList = countries || [];
1648
+ if (!searchQuery.trim()) return countryList;
1649
+ const q = searchQuery.toLowerCase();
1650
+ return countryList.filter((c) => c.name.toLowerCase().includes(q));
1651
+ }, [searchQuery, countries]);
1652
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.container, children: [
1653
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StepProgressBar, { total: 5, current: 2 }),
1654
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.screenHeader, children: [
1655
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { style: styles.backButton, onClick: onCancel, children: "\u2190" }),
1656
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h1", { style: styles.screenTitle, children: "Select your country" })
1657
+ ] }),
1658
+ selected && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { padding: "0 24px 12px" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1659
+ "div",
1660
+ {
1661
+ style: {
1662
+ ...styles.countryCard,
1663
+ ...styles.countryCardSelected,
1664
+ gridColumn: "1 / -1"
1665
+ },
1666
+ children: [
1667
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: styles.countryFlag, children: selected.flagEmoji }),
1668
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: styles.countryName, children: selected.name }),
1669
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: styles.countryCheck, children: "\u2713" })
1670
+ ]
1671
+ }
1672
+ ) }),
1673
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { padding: "0 24px" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.searchBar, children: [
1674
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { color: colors.textTertiary, fontSize: "16px" }, children: "\u{1F50D}" }),
1675
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1676
+ "input",
1677
+ {
1678
+ style: styles.searchInput,
1679
+ placeholder: "Search countries...",
1680
+ value: searchQuery,
1681
+ onChange: (e) => setSearchQuery(e.target.value)
1682
+ }
1683
+ )
1684
+ ] }) }),
1685
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { ...styles.scrollContent, flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: styles.countryGrid, children: filteredCountries.filter((c) => c.id !== selected?.id).map((country) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1686
+ "button",
1687
+ {
1688
+ style: styles.countryCard,
1689
+ onClick: () => setSelected(country),
1690
+ children: [
1691
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: styles.countryFlag, children: country.flagEmoji }),
1692
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: styles.countryName, children: country.name })
1693
+ ]
1694
+ },
1695
+ country.id
1696
+ )) }) }),
1697
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: styles.footer, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1698
+ "button",
1699
+ {
1700
+ style: {
1701
+ ...styles.primaryButton,
1702
+ opacity: selected ? 1 : 0.5,
1703
+ cursor: selected ? "pointer" : "not-allowed"
1704
+ },
1705
+ onClick: () => selected && onSelect(selected),
1706
+ disabled: !selected,
1707
+ children: "Continue"
1708
+ }
1709
+ ) })
1710
+ ] });
1711
+ }
1712
+
1713
+ // src/components/DocumentSelectionScreen.tsx
1714
+ var import_core3 = require("@koraidv/core");
1715
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1716
+ var defaultDocumentTypes = [
1717
+ import_core3.DocumentType.INTERNATIONAL_PASSPORT,
1718
+ import_core3.DocumentType.US_DRIVERS_LICENSE
1719
+ ];
1720
+ function DocumentSelectionScreen({
1721
+ documentTypes = defaultDocumentTypes,
1722
+ selectedCountry,
1723
+ onSelect,
1724
+ onCancel
1725
+ }) {
1726
+ const countryDocTypes = selectedCountry?.documentTypes ? selectedCountry.documentTypes : null;
1727
+ const availableTypes = countryDocTypes || documentTypes;
1728
+ const typesToShow = availableTypes.length > 0 ? availableTypes : documentTypes;
1729
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: styles.container, children: [
1730
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StepProgressBar, { total: 5, current: 2 }),
1731
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: styles.screenHeader, children: [
1732
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { style: styles.backButton, onClick: onCancel, children: "\u2190" }),
1733
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h1", { style: styles.screenTitle, children: "Choose your document" })
1734
+ ] }),
1735
+ selectedCountry && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { padding: "0 24px 16px" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1736
+ "div",
1737
+ {
1738
+ style: {
1739
+ display: "inline-flex",
1740
+ alignItems: "center",
1741
+ gap: "6px",
1742
+ padding: "6px 12px",
1743
+ borderRadius: "20px",
1744
+ backgroundColor: colors.surface,
1745
+ fontSize: "13px",
1746
+ color: colors.textSecondary
1747
+ },
1748
+ children: [
1749
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: selectedCountry.flagEmoji }),
1750
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: selectedCountry.name })
1751
+ ]
1752
+ }
1753
+ ) }),
1754
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: styles.scrollContent, children: typesToShow.map((type) => {
1755
+ const info = (0, import_core3.getDocumentTypeInfo)(type);
1756
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1757
+ "button",
1758
+ {
1759
+ style: styles.documentCard,
1760
+ onClick: () => onSelect(type),
1761
+ children: [
1762
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1763
+ "div",
1764
+ {
1765
+ style: {
1766
+ ...styles.documentIconBox,
1767
+ backgroundColor: colors.surface
1768
+ },
1769
+ children: getIcon(type)
1770
+ }
1771
+ ),
1772
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: styles.documentInfo, children: [
1773
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: styles.documentName, children: info.displayName }),
1774
+ info.requiresBack && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: styles.documentSubtext, children: "Front and back required" })
1775
+ ] }),
1776
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: styles.documentChevron, children: "\u203A" })
1777
+ ]
1778
+ },
1779
+ type
1780
+ );
1781
+ }) })
1782
+ ] });
1783
+ }
1784
+ function getIcon(type) {
1785
+ if (type === import_core3.DocumentType.INTERNATIONAL_PASSPORT) {
1786
+ return "\u{1F4D5}";
1787
+ }
1788
+ if (type === import_core3.DocumentType.US_DRIVERS_LICENSE) {
1789
+ return "\u{1F697}";
1790
+ }
1791
+ return "\u{1FAAA}";
1792
+ }
1793
+
1794
+ // src/components/DocumentCaptureScreen.tsx
1795
+ var import_react5 = require("react");
1796
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1797
+ var qualityIssueMessages = {
1798
+ face_blurred: "Photo on document is blurry. Retake in better lighting.",
1799
+ low_resolution: "Image quality too low. Move closer to document.",
1800
+ multiple_faces: "Multiple faces detected. Only document should be in frame.",
1801
+ no_face_detected: "No photo detected on document. Ensure front is visible.",
1802
+ low_image_clarity: "Image not clear enough. Hold steady with good lighting.",
1803
+ insufficient_text: "Document not fully in frame. Ensure it's well-lit.",
1804
+ low_ocr_confidence: "Text hard to read. Try better lighting.",
1805
+ face_not_frontal: "Document appears tilted. Place on flat surface."
1806
+ };
1807
+ function DocumentCaptureScreen({
1808
+ side,
1809
+ documentType,
1810
+ requiresBack = true,
1811
+ onQualityCheck,
1812
+ onCapture,
1813
+ onCancel
1814
+ }) {
1815
+ const videoRef = (0, import_react5.useRef)(null);
1816
+ const canvasRef = (0, import_react5.useRef)(null);
1817
+ const [stream, setStream] = (0, import_react5.useState)(null);
1818
+ const [isCapturing, setIsCapturing] = (0, import_react5.useState)(false);
1819
+ const [error, setError] = (0, import_react5.useState)(null);
1820
+ const [capturedImage, setCapturedImage] = (0, import_react5.useState)(null);
1821
+ const [capturedBlob, setCapturedBlob] = (0, import_react5.useState)(null);
1822
+ const [qualityResult, setQualityResult] = (0, import_react5.useState)(null);
1823
+ const [isCheckingQuality, setIsCheckingQuality] = (0, import_react5.useState)(false);
1824
+ const [retakeCount, setRetakeCount] = (0, import_react5.useState)(0);
1825
+ (0, import_react5.useEffect)(() => {
1826
+ injectKeyframes();
1827
+ }, []);
1828
+ (0, import_react5.useEffect)(() => {
1829
+ let mounted = true;
1830
+ async function startCamera() {
1831
+ try {
1832
+ const mediaStream = await navigator.mediaDevices.getUserMedia({
1833
+ video: { facingMode: "environment", width: { ideal: 1920 }, height: { ideal: 1080 } }
1834
+ });
1835
+ if (mounted) {
1836
+ setStream(mediaStream);
1837
+ if (videoRef.current) {
1838
+ videoRef.current.srcObject = mediaStream;
1839
+ }
1840
+ }
1841
+ } catch {
1842
+ if (mounted) setError("Camera access denied. Please enable camera permissions.");
1843
+ }
1844
+ }
1845
+ if (!capturedImage) startCamera();
1846
+ return () => {
1847
+ mounted = false;
1848
+ };
1849
+ }, [capturedImage]);
1850
+ (0, import_react5.useEffect)(() => {
1851
+ return () => {
1852
+ stream?.getTracks().forEach((t) => t.stop());
1853
+ };
1854
+ }, [stream]);
1855
+ const handleCapture = (0, import_react5.useCallback)(() => {
1856
+ if (!videoRef.current || !canvasRef.current || isCapturing) return;
1857
+ setIsCapturing(true);
1858
+ const video = videoRef.current;
1859
+ const canvas = canvasRef.current;
1860
+ const ctx = canvas.getContext("2d");
1861
+ if (!ctx) {
1862
+ setIsCapturing(false);
1863
+ return;
1864
+ }
1865
+ canvas.width = video.videoWidth;
1866
+ canvas.height = video.videoHeight;
1867
+ ctx.drawImage(video, 0, 0);
1868
+ const dataUrl = canvas.toDataURL("image/jpeg", 0.85);
1869
+ canvas.toBlob(
1870
+ (blob) => {
1871
+ if (blob) {
1872
+ setCapturedImage(dataUrl);
1873
+ setCapturedBlob(blob);
1874
+ stream?.getTracks().forEach((t) => t.stop());
1875
+ }
1876
+ setIsCapturing(false);
1877
+ },
1878
+ "image/jpeg",
1879
+ 0.85
1880
+ );
1881
+ }, [isCapturing, stream]);
1882
+ const handleRetake = () => {
1883
+ setCapturedImage(null);
1884
+ setCapturedBlob(null);
1885
+ setQualityResult(null);
1886
+ setRetakeCount((c) => c + 1);
1887
+ };
1888
+ const handleAccept = async () => {
1889
+ if (!capturedBlob) return;
1890
+ if (onQualityCheck && !qualityResult) {
1891
+ setIsCheckingQuality(true);
1892
+ try {
1893
+ const result = await onQualityCheck(capturedBlob);
1894
+ setQualityResult(result);
1895
+ setIsCheckingQuality(false);
1896
+ if (result.qualityScore >= 60) {
1897
+ await onCapture(capturedBlob);
1898
+ }
1899
+ } catch {
1900
+ setIsCheckingQuality(false);
1901
+ await onCapture(capturedBlob);
1902
+ }
1903
+ return;
1904
+ }
1905
+ await onCapture(capturedBlob);
1906
+ };
1907
+ const handleContinueAnyway = async () => {
1908
+ if (capturedBlob) {
1909
+ await onCapture(capturedBlob);
1910
+ }
1911
+ };
1912
+ if (error) {
1913
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: styles.container, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: styles.errorContainer, children: [
1914
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: styles.errorText, children: error }),
1915
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { style: styles.primaryButton, onClick: onCancel, children: "Go Back" })
1916
+ ] }) });
1917
+ }
1918
+ if (capturedImage) {
1919
+ const qualityPassed = qualityResult && qualityResult.qualityScore >= 60;
1920
+ const qualityFailed = qualityResult && qualityResult.qualityScore < 60;
1921
+ const canContinueAnyway = qualityFailed && retakeCount >= 2;
1922
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: styles.darkContainer, children: [
1923
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StepProgressBar, { total: 5, current: 3, isDark: true }),
1924
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: styles.darkScreenHeader, children: [
1925
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { width: 40 } }),
1926
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h1", { style: styles.darkScreenTitle, children: "Review your photo" }),
1927
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
1928
+ ] }),
1929
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: "24px" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: styles.reviewCard, children: [
1930
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1931
+ "img",
1932
+ {
1933
+ src: capturedImage,
1934
+ alt: "Captured document",
1935
+ style: { width: "100%", maxWidth: "300px", borderRadius: "16px", display: "block", margin: "0 auto" }
1936
+ }
1937
+ ),
1938
+ isCheckingQuality && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { ...styles.reviewBadge, backgroundColor: "rgba(255,255,255,0.1)" }, children: "Checking quality..." }) }),
1939
+ qualityPassed && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1940
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: styles.reviewBadge, children: [
1941
+ "\u2713 Quality score: ",
1942
+ Math.round(qualityResult.qualityScore),
1943
+ "%"
1944
+ ] }) }),
1945
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: styles.qualityChecks, children: [
1946
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(QualityCheck, { label: "Sharp" }),
1947
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(QualityCheck, { label: "Well-lit" }),
1948
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(QualityCheck, { label: "Readable" })
1949
+ ] })
1950
+ ] }),
1951
+ qualityFailed && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1952
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: { ...styles.reviewBadge, backgroundColor: "rgba(239,68,68,0.15)", color: "#ef4444" }, children: [
1953
+ "\u26A0 Quality score: ",
1954
+ Math.round(qualityResult.qualityScore),
1955
+ "%"
1956
+ ] }) }),
1957
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { padding: "12px 0" }, children: qualityResult.qualityIssues.map((issue, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: { color: "rgba(255,255,255,0.7)", fontSize: "13px", margin: "4px 0", textAlign: "center" }, children: qualityIssueMessages[issue] || issue }, i)) })
1958
+ ] }),
1959
+ !qualityResult && !isCheckingQuality && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1960
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: styles.reviewBadge, children: "\u2713 Good quality" }) }),
1961
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: styles.qualityChecks, children: [
1962
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(QualityCheck, { label: "Sharp" }),
1963
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(QualityCheck, { label: "Well-lit" }),
1964
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(QualityCheck, { label: "No glare" })
1965
+ ] })
1966
+ ] })
1967
+ ] }) }),
1968
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: styles.reviewButtonsRow, children: qualityFailed ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1969
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { style: { ...styles.darkOutlineButton, flex: 1 }, onClick: handleRetake, children: "Retake" }),
1970
+ canContinueAnyway && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { style: { ...styles.primaryButton, flex: 1 }, onClick: handleContinueAnyway, children: "Continue anyway" })
1971
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1972
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { style: { ...styles.darkOutlineButton, flex: 1 }, onClick: handleRetake, children: "Retake" }),
1973
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1974
+ "button",
1975
+ {
1976
+ style: { ...styles.primaryButton, flex: 1, opacity: isCheckingQuality ? 0.5 : 1 },
1977
+ onClick: handleAccept,
1978
+ disabled: isCheckingQuality,
1979
+ children: "Looks good"
1980
+ }
1981
+ )
1982
+ ] }) })
1983
+ ] });
1984
+ }
1985
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: styles.captureContainer, children: [
1986
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StepProgressBar, { total: 5, current: 3, isDark: true }),
1987
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: styles.darkScreenHeader, children: [
1988
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { width: 40 } }),
1989
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { flex: 1, textAlign: "center" }, children: [
1990
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h1", { style: { ...styles.darkScreenTitle, margin: 0 }, children: side === "front" ? "Front of ID" : "Back of ID" }),
1991
+ documentType && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: styles.darkScreenSubtitle, children: documentType })
1992
+ ] }),
1993
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
1994
+ ] }),
1995
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: styles.cameraContainer, children: [
1996
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("video", { ref: videoRef, autoPlay: true, playsInline: true, muted: true, style: styles.cameraVideo }),
1997
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: styles.documentOverlay, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: styles.documentFrame, children: [
1998
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { ...styles.corner, top: 0, left: 0 } }),
1999
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { ...styles.corner, top: 0, right: 0, transform: "rotate(90deg)" } }),
2000
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { ...styles.corner, bottom: 0, right: 0, transform: "rotate(180deg)" } }),
2001
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { ...styles.corner, bottom: 0, left: 0, transform: "rotate(270deg)" } }),
2002
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: styles.scanLine })
2003
+ ] }) }),
2004
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("canvas", { ref: canvasRef, style: { display: "none" } })
2005
+ ] }),
2006
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: styles.stepPillsRow, children: [
2007
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2008
+ "span",
2009
+ {
2010
+ style: {
2011
+ ...styles.stepPill,
2012
+ backgroundColor: side === "front" ? colors.teal : "rgba(255,255,255,0.15)",
2013
+ color: side === "front" ? colors.white : "rgba(255,255,255,0.5)"
2014
+ },
2015
+ children: "Front"
2016
+ }
2017
+ ),
2018
+ requiresBack && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2019
+ "span",
2020
+ {
2021
+ style: {
2022
+ ...styles.stepPill,
2023
+ backgroundColor: side === "back" ? colors.teal : "rgba(255,255,255,0.15)",
2024
+ color: side === "back" ? colors.white : "rgba(255,255,255,0.5)"
2025
+ },
2026
+ children: "Back"
2027
+ }
2028
+ )
2029
+ ] }),
2030
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { textAlign: "center", padding: "8px 0" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2031
+ "span",
2032
+ {
2033
+ style: {
2034
+ ...styles.guidancePill,
2035
+ backgroundColor: "rgba(13,148,136,0.15)",
2036
+ color: colors.teal
2037
+ },
2038
+ children: [
2039
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { ...styles.pulsingDot, backgroundColor: colors.teal } }),
2040
+ "Scanning document..."
2041
+ ]
2042
+ }
2043
+ ) }),
2044
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: styles.captureFooter, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2045
+ "button",
2046
+ {
2047
+ style: { ...styles.captureButton, opacity: isCapturing ? 0.5 : 1 },
2048
+ onClick: handleCapture,
2049
+ disabled: isCapturing,
2050
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: styles.captureButtonInner })
2051
+ }
2052
+ ) })
2053
+ ] });
2054
+ }
2055
+ function QualityCheck({ label }) {
2056
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: styles.qualityCheck, children: [
2057
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: styles.qualityCheckIcon, children: "\u2713" }),
2058
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: styles.qualityCheckLabel, children: label })
2059
+ ] });
2060
+ }
2061
+
2062
+ // src/components/FlipDocumentScreen.tsx
2063
+ var import_react6 = require("react");
2064
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2065
+ function FlipDocumentScreen({ onContinue, onCancel }) {
2066
+ (0, import_react6.useEffect)(() => {
2067
+ injectKeyframes();
2068
+ }, []);
2069
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.darkContainer, children: [
2070
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StepProgressBar, { total: 5, current: 3, isDark: true }),
2071
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.darkScreenHeader, children: [
2072
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { width: 40 } }),
2073
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h1", { style: styles.darkScreenTitle, children: "Flip your document" }),
2074
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2075
+ ] }),
2076
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: {
2077
+ flex: 1,
2078
+ display: "flex",
2079
+ flexDirection: "column",
2080
+ alignItems: "center",
2081
+ justifyContent: "center",
2082
+ padding: "24px",
2083
+ gap: "32px"
2084
+ }, children: [
2085
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: {
2086
+ width: "120px",
2087
+ height: "120px",
2088
+ borderRadius: "50%",
2089
+ backgroundColor: "rgba(13,148,136,0.15)",
2090
+ display: "flex",
2091
+ alignItems: "center",
2092
+ justifyContent: "center"
2093
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { width: "56", height: "56", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
2094
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M9 3L5 6.99H8V14H10V6.99H13L9 3Z", fill: colors.teal }),
2095
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M16 17.01V10H14V17.01H11L15 21L19 17.01H16Z", fill: colors.teal })
2096
+ ] }) }),
2097
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { textAlign: "center" }, children: [
2098
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { style: {
2099
+ fontSize: "22px",
2100
+ fontWeight: 700,
2101
+ color: colors.white,
2102
+ margin: "0 0 12px 0"
2103
+ }, children: "Now capture the back" }),
2104
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: {
2105
+ fontSize: "15px",
2106
+ color: "rgba(255,255,255,0.6)",
2107
+ margin: 0,
2108
+ lineHeight: 1.6,
2109
+ maxWidth: "280px"
2110
+ }, children: "Turn your document over to the back side, then tap continue to take a photo." })
2111
+ ] }),
2112
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.stepPillsRow, children: [
2113
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: {
2114
+ ...styles.stepPill,
2115
+ backgroundColor: "rgba(16,185,129,0.15)",
2116
+ color: colors.success
2117
+ }, children: "\u2713 Front" }),
2118
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: {
2119
+ ...styles.stepPill,
2120
+ backgroundColor: colors.teal,
2121
+ color: colors.white
2122
+ }, children: "Back" })
2123
+ ] })
2124
+ ] }),
2125
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { padding: "24px" }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: styles.primaryButton, onClick: onContinue, children: "Continue" }) })
2126
+ ] });
2127
+ }
2128
+
2129
+ // src/components/SelfieCaptureScreen.tsx
2130
+ var import_react7 = require("react");
2131
+ var import_jsx_runtime8 = require("react/jsx-runtime");
2132
+ function SelfieCaptureScreen({ onCapture, onCancel }) {
2133
+ const videoRef = (0, import_react7.useRef)(null);
2134
+ const canvasRef = (0, import_react7.useRef)(null);
2135
+ const [stream, setStream] = (0, import_react7.useState)(null);
2136
+ const [isCapturing, setIsCapturing] = (0, import_react7.useState)(false);
2137
+ const [error, setError] = (0, import_react7.useState)(null);
2138
+ const [capturedImage, setCapturedImage] = (0, import_react7.useState)(null);
2139
+ const [capturedBlob, setCapturedBlob] = (0, import_react7.useState)(null);
2140
+ (0, import_react7.useEffect)(() => {
2141
+ injectKeyframes();
2142
+ }, []);
2143
+ (0, import_react7.useEffect)(() => {
2144
+ let mounted = true;
2145
+ async function startCamera() {
2146
+ try {
2147
+ const mediaStream = await navigator.mediaDevices.getUserMedia({
2148
+ video: { facingMode: "user", width: { ideal: 1280 }, height: { ideal: 720 } }
2149
+ });
2150
+ if (mounted) {
2151
+ setStream(mediaStream);
2152
+ if (videoRef.current) {
2153
+ videoRef.current.srcObject = mediaStream;
2154
+ }
2155
+ }
2156
+ } catch {
2157
+ if (mounted) setError("Camera access denied. Please enable camera permissions.");
2158
+ }
2159
+ }
2160
+ if (!capturedImage) startCamera();
2161
+ return () => {
2162
+ mounted = false;
2163
+ };
2164
+ }, [capturedImage]);
2165
+ (0, import_react7.useEffect)(() => {
2166
+ return () => {
2167
+ stream?.getTracks().forEach((t) => t.stop());
2168
+ };
2169
+ }, [stream]);
2170
+ const handleCapture = (0, import_react7.useCallback)(() => {
2171
+ if (!videoRef.current || !canvasRef.current || isCapturing) return;
2172
+ setIsCapturing(true);
2173
+ const video = videoRef.current;
2174
+ const canvas = canvasRef.current;
2175
+ const ctx = canvas.getContext("2d");
2176
+ if (!ctx) {
2177
+ setIsCapturing(false);
2178
+ return;
2179
+ }
2180
+ canvas.width = video.videoWidth;
2181
+ canvas.height = video.videoHeight;
2182
+ ctx.translate(canvas.width, 0);
2183
+ ctx.scale(-1, 1);
2184
+ ctx.drawImage(video, 0, 0);
2185
+ const dataUrl = canvas.toDataURL("image/jpeg", 0.85);
2186
+ canvas.toBlob(
2187
+ (blob) => {
2188
+ if (blob) {
2189
+ setCapturedImage(dataUrl);
2190
+ setCapturedBlob(blob);
2191
+ stream?.getTracks().forEach((t) => t.stop());
2192
+ }
2193
+ setIsCapturing(false);
2194
+ },
2195
+ "image/jpeg",
2196
+ 0.85
2197
+ );
2198
+ }, [isCapturing, stream]);
2199
+ const handleRetake = () => {
2200
+ setCapturedImage(null);
2201
+ setCapturedBlob(null);
2202
+ };
2203
+ const handleAccept = async () => {
2204
+ if (capturedBlob) {
2205
+ await onCapture(capturedBlob);
2206
+ }
2207
+ };
2208
+ if (error) {
2209
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: styles.container, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: styles.errorContainer, children: [
2210
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: styles.errorText, children: error }),
2211
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: styles.primaryButton, onClick: onCancel, children: "Go Back" })
2212
+ ] }) });
2213
+ }
2214
+ if (capturedImage) {
2215
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: styles.darkContainer, children: [
2216
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StepProgressBar, { total: 5, current: 4, isDark: true }),
2217
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: styles.darkScreenHeader, children: [
2218
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { width: 40 } }),
2219
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h1", { style: styles.darkScreenTitle, children: "Does this look like you?" }),
2220
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2221
+ ] }),
2222
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: styles.darkScreenSubtitle, children: "Check clarity and lighting" }),
2223
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: "24px" }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { position: "relative" }, children: [
2224
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { width: "240px", height: "300px", borderRadius: "50%", overflow: "hidden", border: `3px solid ${colors.teal}` }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2225
+ "img",
2226
+ {
2227
+ src: capturedImage,
2228
+ alt: "Selfie",
2229
+ style: { width: "100%", height: "100%", objectFit: "cover" }
2230
+ }
2231
+ ) }),
2232
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.reviewBadge, children: "\u2713 Face detected" }) }),
2233
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: styles.qualityChecks, children: [
2234
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(QualityCheck2, { label: "Clear" }),
2235
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(QualityCheck2, { label: "Centered" }),
2236
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(QualityCheck2, { label: "Well-lit" })
2237
+ ] })
2238
+ ] }) }),
2239
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: styles.reviewButtonsRow, children: [
2240
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: { ...styles.darkOutlineButton, flex: 1 }, onClick: handleRetake, children: "Retake" }),
2241
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: { ...styles.primaryButton, flex: 1 }, onClick: handleAccept, children: "Use this" })
2242
+ ] })
2243
+ ] });
2244
+ }
2245
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: styles.captureContainer, children: [
2246
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StepProgressBar, { total: 5, current: 4, isDark: true }),
2247
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: styles.darkScreenHeader, children: [
2248
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { width: 40 } }),
2249
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { flex: 1, textAlign: "center" }, children: [
2250
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h1", { style: { ...styles.darkScreenTitle, margin: 0, fontSize: "24px", fontWeight: 700 }, children: "Face the camera" }),
2251
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: styles.darkScreenSubtitle, children: "Keep a neutral expression" })
2252
+ ] }),
2253
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2254
+ ] }),
2255
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: styles.cameraContainer, children: [
2256
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2257
+ "video",
2258
+ {
2259
+ ref: videoRef,
2260
+ autoPlay: true,
2261
+ playsInline: true,
2262
+ muted: true,
2263
+ style: { ...styles.cameraVideo, transform: "scaleX(-1)" }
2264
+ }
2265
+ ),
2266
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: styles.selfieOverlay, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: styles.faceGuide, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: styles.rotatingRing }) }) }),
2267
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("canvas", { ref: canvasRef, style: { display: "none" } })
2268
+ ] }),
2269
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { textAlign: "center", padding: "8px 0" }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2270
+ "span",
2271
+ {
2272
+ style: {
2273
+ ...styles.guidancePill,
2274
+ backgroundColor: "rgba(13,148,136,0.15)",
2275
+ color: colors.teal
2276
+ },
2277
+ children: [
2278
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { ...styles.pulsingDot, backgroundColor: colors.teal } }),
2279
+ "Position your face in the oval"
2280
+ ]
2281
+ }
2282
+ ) }),
2283
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: styles.captureFooter, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2284
+ "button",
2285
+ {
2286
+ style: { ...styles.captureButton, opacity: isCapturing ? 0.5 : 1 },
2287
+ onClick: handleCapture,
2288
+ disabled: isCapturing,
2289
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: styles.captureButtonInner })
2290
+ }
2291
+ ) })
2292
+ ] });
2293
+ }
2294
+ function QualityCheck2({ label }) {
2295
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: styles.qualityCheck, children: [
2296
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: styles.qualityCheckIcon, children: "\u2713" }),
2297
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.qualityCheckLabel, children: label })
2298
+ ] });
2299
+ }
2300
+
2301
+ // src/components/LivenessScreen.tsx
2302
+ var import_react8 = require("react");
2303
+ var import_jsx_runtime9 = require("react/jsx-runtime");
2304
+ function LivenessScreen({
2305
+ session,
2306
+ currentChallenge,
2307
+ completedChallenges,
2308
+ onChallengeComplete,
2309
+ onStart,
2310
+ onComplete,
2311
+ onCancel
2312
+ }) {
2313
+ const [countdown, setCountdown] = (0, import_react8.useState)(3);
2314
+ (0, import_react8.useEffect)(() => {
2315
+ injectKeyframes();
2316
+ }, []);
2317
+ (0, import_react8.useEffect)(() => {
2318
+ if (!session) onStart();
2319
+ }, [session, onStart]);
2320
+ (0, import_react8.useEffect)(() => {
2321
+ if (session && !currentChallenge && completedChallenges > 0) {
2322
+ onComplete();
2323
+ }
2324
+ }, [session, currentChallenge, completedChallenges, onComplete]);
2325
+ (0, import_react8.useEffect)(() => {
2326
+ if (!currentChallenge) return;
2327
+ setCountdown(3);
2328
+ const interval = setInterval(() => {
2329
+ setCountdown((c) => {
2330
+ if (c <= 1) {
2331
+ clearInterval(interval);
2332
+ return 0;
2333
+ }
2334
+ return c - 1;
2335
+ });
2336
+ }, 1e3);
2337
+ return () => clearInterval(interval);
2338
+ }, [currentChallenge?.id]);
2339
+ if (!session) {
2340
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: styles.darkContainer, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: styles.loadingContainer, children: [
2341
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: styles.spinner }),
2342
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: { ...styles.loadingText, color: "rgba(255,255,255,0.6)" }, children: "Starting liveness check..." })
2343
+ ] }) });
2344
+ }
2345
+ const totalChallenges = session.challenges.length;
2346
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: styles.captureContainer, children: [
2347
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StepProgressBar, { total: 5, current: 5, isDark: true }),
2348
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: styles.darkScreenHeader, children: [
2349
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { width: 40 } }),
2350
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h1", { style: styles.darkScreenTitle, children: "Liveness Check" }),
2351
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2352
+ ] }),
2353
+ currentChallenge && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { padding: "16px 0" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { style: styles.challengeTitle, children: currentChallenge.instruction }) }),
2354
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { position: "relative" }, children: [
2355
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: styles.faceGuide, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2356
+ "svg",
2357
+ {
2358
+ style: { position: "absolute", top: "-8px", left: "-8px" },
2359
+ width: "256",
2360
+ height: "316",
2361
+ viewBox: "0 0 256 316",
2362
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2363
+ "ellipse",
2364
+ {
2365
+ cx: "128",
2366
+ cy: "158",
2367
+ rx: "124",
2368
+ ry: "154",
2369
+ fill: "none",
2370
+ stroke: colors.teal,
2371
+ strokeWidth: "5",
2372
+ strokeDasharray: `${completedChallenges / totalChallenges * 880} 880`,
2373
+ transform: "rotate(-90 128 158)",
2374
+ strokeLinecap: "round"
2375
+ }
2376
+ )
2377
+ }
2378
+ ) }),
2379
+ countdown > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: styles.countdownBadge, children: countdown })
2380
+ ] }) }),
2381
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { padding: "16px 0" }, children: [
2382
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: styles.progressDots, children: session.challenges.map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2383
+ "div",
2384
+ {
2385
+ style: {
2386
+ ...styles.progressDot,
2387
+ backgroundColor: index < completedChallenges ? colors.success : index === completedChallenges ? colors.teal : "rgba(255,255,255,0.15)"
2388
+ }
2389
+ },
2390
+ index
2391
+ )) }),
2392
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("p", { style: styles.progressText, children: [
2393
+ "Challenge ",
2394
+ completedChallenges + 1,
2395
+ " of ",
2396
+ totalChallenges
2397
+ ] })
2398
+ ] }),
2399
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { padding: "16px 24px 32px" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2400
+ "button",
2401
+ {
2402
+ style: styles.primaryButton,
2403
+ onClick: async () => {
2404
+ const canvas = document.createElement("canvas");
2405
+ canvas.width = 100;
2406
+ canvas.height = 100;
2407
+ canvas.toBlob(async (blob) => {
2408
+ if (blob) await onChallengeComplete(blob);
2409
+ });
2410
+ },
2411
+ children: "Complete Challenge"
2412
+ }
2413
+ ) })
2414
+ ] });
2415
+ }
2416
+
2417
+ // src/components/ResultScreen.tsx
2418
+ var import_jsx_runtime10 = require("react/jsx-runtime");
2419
+ function ResultScreen({ verification, onDone, onRetry, resultPageMode, simplified, customMessages }) {
2420
+ const { status } = verification;
2421
+ const effectiveMode = resultPageMode ?? (simplified ? "simplified" : "detailed");
2422
+ if (effectiveMode === "simplified") {
2423
+ switch (status) {
2424
+ case "approved":
2425
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SimplifiedSuccess, { onDone, customMessages });
2426
+ case "rejected":
2427
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SimplifiedFailed, { onRetry: onRetry || onDone, customMessages });
2428
+ case "review_required":
2429
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SimplifiedReview, { verification, onDone, customMessages });
2430
+ case "expired":
2431
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SimplifiedFailed, { onRetry: onRetry || onDone, customMessages: { failedTitle: "Document Expired", failedMessage: "The document you submitted has expired. Please use a valid document." } });
2432
+ default:
2433
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SimplifiedSuccess, { onDone, customMessages });
2434
+ }
2435
+ }
2436
+ switch (status) {
2437
+ case "approved":
2438
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SuccessResult, { verification, onDone });
2439
+ case "rejected":
2440
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(RejectedResult, { verification, onRetry: onRetry || onDone });
2441
+ case "expired":
2442
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ExpiredResult, { verification, onRetry: onRetry || onDone });
2443
+ case "review_required":
2444
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ManualReviewResult, { verification, onDone });
2445
+ default:
2446
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SuccessResult, { verification, onDone });
2447
+ }
2448
+ }
2449
+ function SuccessResult({ verification, onDone }) {
2450
+ const score = verification.riskScore ?? 84;
2451
+ const metrics = computeScoreBreakdown(verification);
2452
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.resultContainer, children: [
2453
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.resultContent, children: [
2454
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2455
+ "div",
2456
+ {
2457
+ style: {
2458
+ ...styles.resultIconOuterRing,
2459
+ backgroundColor: `${colors.success}15`
2460
+ },
2461
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2462
+ "div",
2463
+ {
2464
+ style: {
2465
+ ...styles.resultIconCircle,
2466
+ background: `linear-gradient(135deg, ${colors.success}, #059669)`,
2467
+ color: colors.white,
2468
+ margin: 0
2469
+ },
2470
+ children: "\u2713"
2471
+ }
2472
+ )
2473
+ }
2474
+ ),
2475
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { style: styles.resultTitle, children: "Verification approved" }),
2476
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: styles.resultSubtitle, children: "Your identity has been successfully verified." }),
2477
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2478
+ ScoreCard,
2479
+ {
2480
+ score,
2481
+ badge: "PASSED",
2482
+ gradient: `linear-gradient(135deg, ${colors.teal}, ${colors.tealDark})`
2483
+ }
2484
+ ),
2485
+ metrics.map((m, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ScoreMetricRow, { ...m }, i))
2486
+ ] }),
2487
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: styles.footer, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { style: styles.primaryButton, onClick: onDone, children: "Done" }) })
2488
+ ] });
2489
+ }
2490
+ function RejectedResult({ verification, onRetry }) {
2491
+ const score = verification.riskScore ?? 42;
2492
+ const metrics = computeScoreBreakdown(verification);
2493
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.resultContainer, children: [
2494
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.resultContent, children: [
2495
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2496
+ "div",
2497
+ {
2498
+ style: {
2499
+ ...styles.resultIconOuterRing,
2500
+ backgroundColor: `${colors.error}15`
2501
+ },
2502
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2503
+ "div",
2504
+ {
2505
+ style: {
2506
+ ...styles.resultIconCircle,
2507
+ background: `linear-gradient(135deg, ${colors.error}, #B91C1C)`,
2508
+ color: colors.white,
2509
+ margin: 0
2510
+ },
2511
+ children: "\u2715"
2512
+ }
2513
+ )
2514
+ }
2515
+ ),
2516
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { style: styles.resultTitle, children: "Verification rejected" }),
2517
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: styles.resultSubtitle, children: "We could not verify your identity. Please try again with a valid document." }),
2518
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2519
+ ScoreCard,
2520
+ {
2521
+ score,
2522
+ badge: "REJECTED",
2523
+ gradient: `linear-gradient(135deg, ${colors.error}, #B91C1C)`
2524
+ }
2525
+ ),
2526
+ metrics.map((m, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ScoreMetricRow, { ...m }, i))
2527
+ ] }),
2528
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: styles.footer, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { style: styles.primaryButton, onClick: onRetry, children: "Try again" }) })
2529
+ ] });
2530
+ }
2531
+ function ExpiredResult({ verification, onRetry }) {
2532
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.resultContainer, children: [
2533
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.resultContent, children: [
2534
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2535
+ "div",
2536
+ {
2537
+ style: {
2538
+ ...styles.resultIconOuterRing,
2539
+ backgroundColor: `${colors.warning}15`,
2540
+ border: `2px solid ${colors.warning}30`
2541
+ },
2542
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2543
+ "div",
2544
+ {
2545
+ style: {
2546
+ ...styles.resultIconCircle,
2547
+ background: `linear-gradient(135deg, ${colors.warning}, #B45309)`,
2548
+ color: colors.white,
2549
+ margin: 0
2550
+ },
2551
+ children: "\u26A0"
2552
+ }
2553
+ )
2554
+ }
2555
+ ),
2556
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { style: styles.resultTitle, children: "Document expired" }),
2557
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: styles.resultSubtitle, children: "The document you submitted has expired. Please use a valid, non-expired document." }),
2558
+ verification.documentVerification && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.expiryCard, children: [
2559
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.expiryRow, children: [
2560
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: styles.expiryLabel, children: "Document type" }),
2561
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: styles.expiryValue, children: verification.documentVerification.documentType || "ID Card" })
2562
+ ] }),
2563
+ verification.documentVerification.issuingCountry && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.expiryRow, children: [
2564
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: styles.expiryLabel, children: "Country" }),
2565
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: styles.expiryValue, children: verification.documentVerification.issuingCountry })
2566
+ ] }),
2567
+ verification.documentVerification.expirationDate && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.expiryRow, children: [
2568
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: styles.expiryLabel, children: "Expired on" }),
2569
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: styles.expiryBadge, children: verification.documentVerification.expirationDate })
2570
+ ] })
2571
+ ] }),
2572
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { textAlign: "left" }, children: [
2573
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(GuidanceTip, { number: 1, text: "Check the expiration date on your document" }),
2574
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(GuidanceTip, { number: 2, text: "Use a different document that is currently valid" }),
2575
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(GuidanceTip, { number: 3, text: "Ensure the document details are clearly visible" })
2576
+ ] })
2577
+ ] }),
2578
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: styles.footer, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { style: styles.primaryButton, onClick: onRetry, children: "Try with a valid document" }) })
2579
+ ] });
2580
+ }
2581
+ function ManualReviewResult({ verification, onDone }) {
2582
+ const score = verification.riskScore ?? 68;
2583
+ const metrics = computeScoreBreakdown(verification);
2584
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.resultContainer, children: [
2585
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.resultContent, children: [
2586
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2587
+ "div",
2588
+ {
2589
+ style: {
2590
+ ...styles.resultIconOuterRing,
2591
+ backgroundColor: `${colors.info}15`,
2592
+ border: `2px solid ${colors.info}30`
2593
+ },
2594
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2595
+ "div",
2596
+ {
2597
+ style: {
2598
+ ...styles.resultIconCircle,
2599
+ background: `linear-gradient(135deg, ${colors.info}, #0369A1)`,
2600
+ color: colors.white,
2601
+ margin: 0
2602
+ },
2603
+ children: "\u{1F550}"
2604
+ }
2605
+ )
2606
+ }
2607
+ ),
2608
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { style: styles.resultTitle, children: "Under review" }),
2609
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: styles.resultSubtitle, children: "Your verification requires manual review. We'll notify you of the result." }),
2610
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2611
+ ScoreCard,
2612
+ {
2613
+ score,
2614
+ badge: "REVIEW",
2615
+ gradient: `linear-gradient(135deg, ${colors.info}, #0369A1)`
2616
+ }
2617
+ ),
2618
+ metrics.map((m, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ScoreMetricRow, { ...m }, i))
2619
+ ] }),
2620
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: styles.footer, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { style: styles.primaryButton, onClick: onDone, children: "Got it" }) })
2621
+ ] });
2622
+ }
2623
+ function GuidanceTip({ number, text }) {
2624
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.guidanceTip, children: [
2625
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: styles.guidanceTipNumber, children: number }),
2626
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: styles.guidanceTipText, children: text })
2627
+ ] });
2628
+ }
2629
+ function SimplifiedSuccess({ onDone, customMessages }) {
2630
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.resultContainer, children: [
2631
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
2632
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2633
+ "div",
2634
+ {
2635
+ style: {
2636
+ ...styles.resultIconOuterRing,
2637
+ backgroundColor: `${colors.success}15`,
2638
+ width: 96,
2639
+ height: 96
2640
+ },
2641
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2642
+ "div",
2643
+ {
2644
+ style: {
2645
+ ...styles.resultIconCircle,
2646
+ background: `linear-gradient(135deg, ${colors.success}, #059669)`,
2647
+ color: colors.white,
2648
+ margin: 0,
2649
+ width: 64,
2650
+ height: 64,
2651
+ fontSize: 28
2652
+ },
2653
+ children: "\u2713"
2654
+ }
2655
+ )
2656
+ }
2657
+ ),
2658
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { style: { ...styles.resultTitle, fontSize: 24, marginTop: 16 }, children: customMessages?.successTitle || "Verification Successful" }),
2659
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: { ...styles.resultSubtitle, fontSize: 16, maxWidth: 320, margin: "8px auto 0" }, children: customMessages?.successMessage || "Your identity has been successfully verified. You can now proceed." })
2660
+ ] }),
2661
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: styles.footer, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { style: styles.primaryButton, onClick: onDone, children: "Continue" }) })
2662
+ ] });
2663
+ }
2664
+ function SimplifiedFailed({ onRetry, customMessages }) {
2665
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.resultContainer, children: [
2666
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
2667
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2668
+ "div",
2669
+ {
2670
+ style: {
2671
+ ...styles.resultIconOuterRing,
2672
+ backgroundColor: `${colors.error}15`,
2673
+ width: 96,
2674
+ height: 96
2675
+ },
2676
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2677
+ "div",
2678
+ {
2679
+ style: {
2680
+ ...styles.resultIconCircle,
2681
+ background: `linear-gradient(135deg, ${colors.error}, #B91C1C)`,
2682
+ color: colors.white,
2683
+ margin: 0,
2684
+ width: 64,
2685
+ height: 64,
2686
+ fontSize: 28
2687
+ },
2688
+ children: "\u2715"
2689
+ }
2690
+ )
2691
+ }
2692
+ ),
2693
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { style: { ...styles.resultTitle, fontSize: 24, marginTop: 16 }, children: customMessages?.failedTitle || "Verification Failed" }),
2694
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: { ...styles.resultSubtitle, fontSize: 16, maxWidth: 320, margin: "8px auto 0" }, children: customMessages?.failedMessage || "We could not verify your identity. Please try again with a valid document." })
2695
+ ] }),
2696
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: styles.footer, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { style: styles.primaryButton, onClick: onRetry, children: "Try Again" }) })
2697
+ ] });
2698
+ }
2699
+ function SimplifiedReview({ verification, onDone, customMessages }) {
2700
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.resultContainer, children: [
2701
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
2702
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2703
+ "div",
2704
+ {
2705
+ style: {
2706
+ ...styles.resultIconOuterRing,
2707
+ backgroundColor: `${colors.warning}15`,
2708
+ width: 96,
2709
+ height: 96
2710
+ },
2711
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2712
+ "div",
2713
+ {
2714
+ style: {
2715
+ ...styles.resultIconCircle,
2716
+ background: `linear-gradient(135deg, ${colors.warning}, #B45309)`,
2717
+ color: colors.white,
2718
+ margin: 0,
2719
+ width: 64,
2720
+ height: 64,
2721
+ fontSize: 28
2722
+ },
2723
+ children: "\u{1F550}"
2724
+ }
2725
+ )
2726
+ }
2727
+ ),
2728
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { style: { ...styles.resultTitle, fontSize: 24, marginTop: 16 }, children: customMessages?.reviewTitle || "Verification Under Review" }),
2729
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: { ...styles.resultSubtitle, fontSize: 16, maxWidth: 320, margin: "8px auto 0" }, children: customMessages?.reviewMessage || "Your verification requires additional review. We will notify you of the result." }),
2730
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: {
2731
+ marginTop: 24,
2732
+ padding: "12px 24px",
2733
+ backgroundColor: `${colors.info}10`,
2734
+ borderRadius: 8,
2735
+ border: `1px solid ${colors.info}30`,
2736
+ display: "inline-block"
2737
+ }, children: [
2738
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { fontSize: 12, color: colors.textSecondary }, children: "Reference: " }),
2739
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { fontSize: 14, fontWeight: 600, fontFamily: "monospace" }, children: verification.id.slice(0, 8) })
2740
+ ] })
2741
+ ] }),
2742
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: styles.footer, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { style: styles.primaryButton, onClick: onDone, children: "Got It" }) })
2743
+ ] });
2744
+ }
2745
+
2746
+ // src/components/ErrorScreen.tsx
2747
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2748
+ function ErrorScreen({ error, onRetry, onCancel }) {
2749
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: styles.resultContainer, children: [
2750
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: styles.resultContent, children: [
2751
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2752
+ "div",
2753
+ {
2754
+ style: {
2755
+ ...styles.resultIconOuterRing,
2756
+ backgroundColor: colors.errorBg
2757
+ },
2758
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2759
+ "div",
2760
+ {
2761
+ style: {
2762
+ ...styles.resultIconCircle,
2763
+ background: `linear-gradient(135deg, ${colors.error}, #B91C1C)`,
2764
+ color: colors.white,
2765
+ margin: 0
2766
+ },
2767
+ children: "!"
2768
+ }
2769
+ )
2770
+ }
2771
+ ),
2772
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h1", { style: styles.resultTitle, children: "Something went wrong" }),
2773
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.resultSubtitle, children: error.message }),
2774
+ error.recoverySuggestion && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: { ...styles.bodyText, marginTop: "12px" }, children: error.recoverySuggestion })
2775
+ ] }),
2776
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: styles.footer, children: [
2777
+ error.isRetryable && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { style: styles.primaryButton, onClick: onRetry, children: "Try Again" }),
2778
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { style: styles.textButton, onClick: onCancel, children: "Cancel" })
2779
+ ] })
2780
+ ] });
2781
+ }
2782
+
2783
+ // src/components/LoadingScreen.tsx
2784
+ var import_react9 = require("react");
2785
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2786
+ function LoadingScreen({ message = "Loading..." }) {
2787
+ (0, import_react9.useEffect)(() => {
2788
+ injectKeyframes();
2789
+ }, []);
2790
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: styles.container, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: styles.loadingContainer, children: [
2791
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2792
+ "div",
2793
+ {
2794
+ style: {
2795
+ width: "56px",
2796
+ height: "56px",
2797
+ borderRadius: "50%",
2798
+ background: `linear-gradient(135deg, ${colors.teal}, ${colors.tealDark})`,
2799
+ display: "flex",
2800
+ alignItems: "center",
2801
+ justifyContent: "center",
2802
+ fontSize: "24px",
2803
+ animation: "kora-pulse 2s ease-in-out infinite"
2804
+ },
2805
+ children: "\u{1F6E1}\uFE0F"
2806
+ }
2807
+ ),
2808
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: styles.loadingText, children: message })
2809
+ ] }) });
2810
+ }
2811
+
2812
+ // src/components/VerificationFlow.tsx
2813
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2814
+ function VerificationFlow({
2815
+ externalId,
2816
+ tier = "standard",
2817
+ documentTypes,
2818
+ onComplete,
2819
+ onError,
2820
+ onCancel,
2821
+ className,
2822
+ style
2823
+ }) {
2824
+ const {
2825
+ state,
2826
+ startVerification,
2827
+ acceptConsent,
2828
+ selectDocumentType,
2829
+ checkDocumentQuality,
2830
+ uploadDocument,
2831
+ uploadSelfie,
2832
+ startLiveness,
2833
+ submitChallenge,
2834
+ complete,
2835
+ cancel,
2836
+ retry,
2837
+ sdk
2838
+ } = useKoraIDV();
2839
+ const [selectedCountry, setSelectedCountry] = (0, import_react10.useState)(null);
2840
+ const [flowStep, setFlowStep] = (0, import_react10.useState)("consent");
2841
+ const [showFlipInstruction, setShowFlipInstruction] = (0, import_react10.useState)(true);
2842
+ const [supportedCountries, setSupportedCountries] = (0, import_react10.useState)([]);
2843
+ const [countriesLoading, setCountriesLoading] = (0, import_react10.useState)(false);
2844
+ (0, import_react10.useEffect)(() => {
2845
+ if (state.step === "document_front") {
2846
+ setShowFlipInstruction(true);
2847
+ }
2848
+ }, [state.step]);
2849
+ (0, import_react10.useEffect)(() => {
2850
+ startVerification(externalId, tier);
2851
+ }, [externalId, tier, startVerification]);
2852
+ (0, import_react10.useEffect)(() => {
2853
+ if (state.step === "complete" && state.verification && onComplete) {
2854
+ onComplete(state.verification);
2855
+ }
2856
+ }, [state.step, state.verification, onComplete]);
2857
+ (0, import_react10.useEffect)(() => {
2858
+ if (state.error && onError) {
2859
+ onError(state.error);
2860
+ }
2861
+ }, [state.error, onError]);
2862
+ const fetchCountries = async () => {
2863
+ setCountriesLoading(true);
2864
+ try {
2865
+ const countries = await sdk.getSupportedCountries();
2866
+ setSupportedCountries(
2867
+ countries.map((c) => ({
2868
+ id: c.id,
2869
+ name: c.name,
2870
+ flagEmoji: c.flagEmoji,
2871
+ documentTypes: c.documentTypes
2872
+ }))
2873
+ );
2874
+ } catch (error) {
2875
+ onError?.(
2876
+ error instanceof import_core4.KoraError ? error : new import_core4.KoraError(import_core4.KoraErrorCode.NETWORK_ERROR, "Failed to load supported countries")
2877
+ );
2878
+ } finally {
2879
+ setCountriesLoading(false);
2880
+ }
2881
+ };
2882
+ const handleCancel = () => {
2883
+ cancel();
2884
+ onCancel?.();
2885
+ };
2886
+ const handleAcceptConsent = () => {
2887
+ fetchCountries();
2888
+ setFlowStep("country_selection");
2889
+ };
2890
+ const handleCountrySelect = (country) => {
2891
+ setSelectedCountry(country);
2892
+ setFlowStep("flow");
2893
+ acceptConsent();
2894
+ };
2895
+ const containerStyle = {
2896
+ width: "100%",
2897
+ maxWidth: "480px",
2898
+ margin: "0 auto",
2899
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
2900
+ ...style
2901
+ };
2902
+ if (state.error) {
2903
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className, style: containerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ErrorScreen, { error: state.error, onRetry: retry, onCancel: handleCancel }) });
2904
+ }
2905
+ if (state.isLoading && state.step !== "processing") {
2906
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className, style: containerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(LoadingScreen, {}) });
2907
+ }
2908
+ if (flowStep === "consent" && state.step === "consent") {
2909
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className, style: containerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ConsentScreen, { onAccept: handleAcceptConsent, onDecline: handleCancel }) });
2910
+ }
2911
+ if (flowStep === "country_selection") {
2912
+ if (countriesLoading) {
2913
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className, style: containerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(LoadingScreen, {}) });
2914
+ }
2915
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className, style: containerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2916
+ CountrySelectionScreen,
2917
+ {
2918
+ countries: supportedCountries,
2919
+ onSelect: handleCountrySelect,
2920
+ onCancel: handleCancel
2921
+ }
2922
+ ) });
2923
+ }
2924
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className, style: containerStyle, children: [
2925
+ state.step === "document_selection" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2926
+ DocumentSelectionScreen,
2927
+ {
2928
+ documentTypes,
2929
+ selectedCountry,
2930
+ onSelect: selectDocumentType,
2931
+ onCancel: handleCancel
2932
+ }
2933
+ ),
2934
+ state.step === "document_front" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2935
+ DocumentCaptureScreen,
2936
+ {
2937
+ side: "front",
2938
+ onQualityCheck: (blob) => checkDocumentQuality(blob),
2939
+ onCapture: (imageData) => uploadDocument(imageData, "front"),
2940
+ onCancel: handleCancel
2941
+ }
2942
+ ),
2943
+ state.step === "document_back" && showFlipInstruction && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2944
+ FlipDocumentScreen,
2945
+ {
2946
+ onContinue: () => setShowFlipInstruction(false),
2947
+ onCancel: handleCancel
2948
+ }
2949
+ ),
2950
+ state.step === "document_back" && !showFlipInstruction && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2951
+ DocumentCaptureScreen,
2952
+ {
2953
+ side: "back",
2954
+ onCapture: (imageData) => uploadDocument(imageData, "back"),
2955
+ onCancel: handleCancel
2956
+ }
2957
+ ),
2958
+ state.step === "selfie" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SelfieCaptureScreen, { onCapture: uploadSelfie, onCancel: handleCancel }),
2959
+ state.step === "liveness" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2960
+ LivenessScreen,
2961
+ {
2962
+ session: state.livenessSession,
2963
+ currentChallenge: state.currentChallenge,
2964
+ completedChallenges: state.completedChallenges,
2965
+ onChallengeComplete: submitChallenge,
2966
+ onStart: startLiveness,
2967
+ onComplete: complete,
2968
+ onCancel: handleCancel
2969
+ }
2970
+ ),
2971
+ state.step === "processing" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2972
+ ProcessingScreen,
2973
+ {
2974
+ steps: [
2975
+ { label: "Document analyzed", status: "done" },
2976
+ { label: "Checking face match", status: "active" },
2977
+ { label: "Finalizing results", status: "pending" }
2978
+ ]
2979
+ }
2980
+ ),
2981
+ state.step === "complete" && state.verification && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2982
+ ResultScreen,
2983
+ {
2984
+ verification: state.verification,
2985
+ onDone: () => onComplete?.(state.verification),
2986
+ onRetry: retry
2987
+ }
2988
+ )
2989
+ ] });
2990
+ }
2991
+
2992
+ // src/components/QrHandoffScreen.tsx
2993
+ var import_react11 = require("react");
2994
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2995
+ function QrHandoffScreen({
2996
+ session,
2997
+ onMobileCaptureComplete,
2998
+ onContinueOnDevice,
2999
+ onExpired,
3000
+ onRefresh,
3001
+ eventSource
3002
+ }) {
3003
+ const [timeLeft, setTimeLeft] = (0, import_react11.useState)(session.expiresIn);
3004
+ const [scanned, setScanned] = (0, import_react11.useState)(false);
3005
+ const [expired, setExpired] = (0, import_react11.useState)(false);
3006
+ const timerRef = (0, import_react11.useRef)();
3007
+ (0, import_react11.useEffect)(() => {
3008
+ setTimeLeft(session.expiresIn);
3009
+ setExpired(false);
3010
+ setScanned(false);
3011
+ timerRef.current = setInterval(() => {
3012
+ setTimeLeft((prev) => {
3013
+ if (prev <= 1) {
3014
+ clearInterval(timerRef.current);
3015
+ setExpired(true);
3016
+ onExpired();
3017
+ return 0;
3018
+ }
3019
+ return prev - 1;
3020
+ });
3021
+ }, 1e3);
3022
+ return () => clearInterval(timerRef.current);
3023
+ }, [session.token]);
3024
+ (0, import_react11.useEffect)(() => {
3025
+ if (!eventSource) return;
3026
+ const handleStatus = (event) => {
3027
+ try {
3028
+ const data = JSON.parse(event.data);
3029
+ if (data.status === "document_required" || data.status === "selfie_required") {
3030
+ setScanned(true);
3031
+ }
3032
+ } catch {
3033
+ }
3034
+ };
3035
+ const handleComplete = () => {
3036
+ clearInterval(timerRef.current);
3037
+ onMobileCaptureComplete();
3038
+ };
3039
+ eventSource.addEventListener("status", handleStatus);
3040
+ eventSource.addEventListener("complete", handleComplete);
3041
+ return () => {
3042
+ eventSource.removeEventListener("status", handleStatus);
3043
+ eventSource.removeEventListener("complete", handleComplete);
3044
+ };
3045
+ }, [eventSource, onMobileCaptureComplete]);
3046
+ const minutes = Math.floor(timeLeft / 60);
3047
+ const seconds = timeLeft % 60;
3048
+ const qrSize = 200;
3049
+ if (expired) {
3050
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: qrStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: qrStyles.content, children: [
3051
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: qrStyles.expiredIcon, children: "\u23F1" }),
3052
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h2", { style: qrStyles.title, children: "QR Code Expired" }),
3053
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { style: qrStyles.subtitle, children: "The QR code has expired. Generate a new one to continue." }),
3054
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { style: qrStyles.primaryButton, onClick: onRefresh, children: "Generate New QR Code" }),
3055
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { style: qrStyles.secondaryButton, onClick: onContinueOnDevice, children: "Continue on this device instead" })
3056
+ ] }) });
3057
+ }
3058
+ if (scanned) {
3059
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: qrStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: qrStyles.content, children: [
3060
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: qrStyles.spinnerContainer, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: qrStyles.spinner }) }),
3061
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h2", { style: qrStyles.title, children: "Capturing on your phone..." }),
3062
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { style: qrStyles.subtitle, children: "Complete the document scan and selfie on your mobile device. This page will update automatically when done." }),
3063
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: qrStyles.statusBadge, children: [
3064
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { style: qrStyles.statusDot }),
3065
+ "Connected \u2014 waiting for capture"
3066
+ ] })
3067
+ ] }) });
3068
+ }
3069
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: qrStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: qrStyles.content, children: [
3070
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h2", { style: qrStyles.title, children: "Scan with your phone" }),
3071
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { style: qrStyles.subtitle, children: "Use your phone's camera for a better capture experience. Scan the QR code below to continue on your mobile device." }),
3072
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: qrStyles.qrContainer, children: [
3073
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: {
3074
+ ...qrStyles.qrBox,
3075
+ width: qrSize,
3076
+ height: qrSize
3077
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3078
+ "img",
3079
+ {
3080
+ src: `https://api.qrserver.com/v1/create-qr-code/?size=${qrSize}x${qrSize}&data=${encodeURIComponent(session.captureUrl)}&margin=8`,
3081
+ alt: "QR Code",
3082
+ width: qrSize,
3083
+ height: qrSize,
3084
+ style: { borderRadius: 12 }
3085
+ }
3086
+ ) }),
3087
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: qrStyles.timer, children: [
3088
+ minutes,
3089
+ ":",
3090
+ seconds.toString().padStart(2, "0"),
3091
+ " remaining"
3092
+ ] })
3093
+ ] }),
3094
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: qrStyles.steps, children: [
3095
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Step, { number: 1, text: "Open your phone's camera" }),
3096
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Step, { number: 2, text: "Point at the QR code" }),
3097
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Step, { number: 3, text: "Complete the capture on your phone" })
3098
+ ] }),
3099
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: qrStyles.divider, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { style: qrStyles.dividerText, children: "or" }) }),
3100
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { style: qrStyles.secondaryButton, onClick: onContinueOnDevice, children: "Continue on this device" })
3101
+ ] }) });
3102
+ }
3103
+ function Step({ number, text }) {
3104
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: qrStyles.step, children: [
3105
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: qrStyles.stepNumber, children: number }),
3106
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { style: qrStyles.stepText, children: text })
3107
+ ] });
3108
+ }
3109
+ var qrStyles = {
3110
+ container: {
3111
+ display: "flex",
3112
+ flexDirection: "column",
3113
+ alignItems: "center",
3114
+ justifyContent: "center",
3115
+ minHeight: "100%",
3116
+ padding: 24,
3117
+ fontFamily: "Inter, system-ui, sans-serif"
3118
+ },
3119
+ content: {
3120
+ textAlign: "center",
3121
+ maxWidth: 400,
3122
+ width: "100%"
3123
+ },
3124
+ title: {
3125
+ fontSize: 22,
3126
+ fontWeight: 700,
3127
+ color: "#1a1a2e",
3128
+ marginBottom: 8
3129
+ },
3130
+ subtitle: {
3131
+ fontSize: 14,
3132
+ color: "#6b7280",
3133
+ lineHeight: "1.5",
3134
+ marginBottom: 24
3135
+ },
3136
+ qrContainer: {
3137
+ display: "flex",
3138
+ flexDirection: "column",
3139
+ alignItems: "center",
3140
+ gap: 12,
3141
+ marginBottom: 24
3142
+ },
3143
+ qrBox: {
3144
+ padding: 16,
3145
+ background: "#ffffff",
3146
+ borderRadius: 16,
3147
+ boxShadow: "0 4px 24px rgba(0,0,0,0.08)",
3148
+ border: "1px solid #e5e7eb"
3149
+ },
3150
+ timer: {
3151
+ fontSize: 13,
3152
+ color: "#9ca3af",
3153
+ fontVariantNumeric: "tabular-nums"
3154
+ },
3155
+ steps: {
3156
+ textAlign: "left",
3157
+ marginBottom: 24
3158
+ },
3159
+ step: {
3160
+ display: "flex",
3161
+ alignItems: "center",
3162
+ gap: 12,
3163
+ marginBottom: 12
3164
+ },
3165
+ stepNumber: {
3166
+ width: 28,
3167
+ height: 28,
3168
+ borderRadius: 14,
3169
+ background: `${colors.teal}15`,
3170
+ color: colors.teal,
3171
+ display: "flex",
3172
+ alignItems: "center",
3173
+ justifyContent: "center",
3174
+ fontSize: 13,
3175
+ fontWeight: 700,
3176
+ flexShrink: 0
3177
+ },
3178
+ stepText: {
3179
+ fontSize: 14,
3180
+ color: "#374151"
3181
+ },
3182
+ divider: {
3183
+ position: "relative",
3184
+ textAlign: "center",
3185
+ margin: "16px 0",
3186
+ borderTop: "1px solid #e5e7eb"
3187
+ },
3188
+ dividerText: {
3189
+ position: "relative",
3190
+ top: -10,
3191
+ background: "#f9fafb",
3192
+ padding: "0 12px",
3193
+ fontSize: 13,
3194
+ color: "#9ca3af"
3195
+ },
3196
+ primaryButton: {
3197
+ width: "100%",
3198
+ padding: "12px 24px",
3199
+ fontSize: 15,
3200
+ fontWeight: 600,
3201
+ color: "#ffffff",
3202
+ background: colors.teal,
3203
+ border: "none",
3204
+ borderRadius: 10,
3205
+ cursor: "pointer",
3206
+ marginBottom: 12
3207
+ },
3208
+ secondaryButton: {
3209
+ width: "100%",
3210
+ padding: "12px 24px",
3211
+ fontSize: 14,
3212
+ fontWeight: 500,
3213
+ color: "#6b7280",
3214
+ background: "transparent",
3215
+ border: "1px solid #d1d5db",
3216
+ borderRadius: 10,
3217
+ cursor: "pointer"
3218
+ },
3219
+ expiredIcon: {
3220
+ fontSize: 48,
3221
+ marginBottom: 16
3222
+ },
3223
+ spinnerContainer: {
3224
+ display: "flex",
3225
+ justifyContent: "center",
3226
+ marginBottom: 16
3227
+ },
3228
+ spinner: {
3229
+ width: 48,
3230
+ height: 48,
3231
+ borderRadius: "50%",
3232
+ border: `3px solid ${colors.teal}30`,
3233
+ borderTopColor: colors.teal,
3234
+ animation: "spin 1s linear infinite"
3235
+ },
3236
+ statusBadge: {
3237
+ display: "inline-flex",
3238
+ alignItems: "center",
3239
+ gap: 8,
3240
+ padding: "8px 16px",
3241
+ background: `${colors.teal}10`,
3242
+ borderRadius: 20,
3243
+ fontSize: 13,
3244
+ color: colors.teal,
3245
+ fontWeight: 500,
3246
+ marginTop: 16
3247
+ },
3248
+ statusDot: {
3249
+ width: 8,
3250
+ height: 8,
3251
+ borderRadius: 4,
3252
+ background: colors.teal
3253
+ }
3254
+ };
3255
+ // Annotate the CommonJS export names for ESM import in node:
3256
+ 0 && (module.exports = {
3257
+ ConsentScreen,
3258
+ CountrySelectionScreen,
3259
+ DocumentCaptureScreen,
3260
+ DocumentSelectionScreen,
3261
+ ErrorScreen,
3262
+ KoraIDVProvider,
3263
+ LivenessScreen,
3264
+ ProcessingScreen,
3265
+ QrHandoffScreen,
3266
+ ResultScreen,
3267
+ ScoreCard,
3268
+ ScoreMetricRow,
3269
+ SelfieCaptureScreen,
3270
+ StepProgressBar,
3271
+ VerificationFlow,
3272
+ useKoraIDV
3273
+ });