@koraidv/react 1.7.10 → 1.8.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.mjs CHANGED
@@ -47,16 +47,22 @@ function useKoraIDV() {
47
47
  currentChallenge: null,
48
48
  completedChallenges: 0,
49
49
  isLoading: false,
50
- error: null
50
+ error: null,
51
+ lastChallengeError: null
51
52
  });
52
53
  const [selectedDocumentType, setSelectedDocumentType] = useState(null);
53
54
  const [documentFrontCaptured, setDocumentFrontCaptured] = useState(false);
54
55
  const startVerification = useCallback(
55
- async (externalId, tier = "standard") => {
56
+ async (externalId, tier = "standard", expectedFirstName, expectedLastName) => {
56
57
  setState((prev) => ({ ...prev, isLoading: true, error: null }));
57
58
  try {
58
59
  await sdk.startVerification(
59
- { externalId, tier },
60
+ {
61
+ externalId,
62
+ tier,
63
+ expectedFirstName,
64
+ expectedLastName
65
+ },
60
66
  {
61
67
  onStepChange: (step) => {
62
68
  setState((prev) => ({ ...prev, step }));
@@ -241,14 +247,20 @@ function useKoraIDV() {
241
247
  ...prev,
242
248
  completedChallenges: nextIndex,
243
249
  currentChallenge: nextChallenge,
244
- isLoading: false
250
+ isLoading: false,
251
+ // Clear any prior retake message — the user just succeeded.
252
+ lastChallengeError: null
245
253
  }));
246
254
  if (!nextChallenge) {
247
255
  setState((prev) => ({ ...prev, step: "processing" }));
248
256
  }
249
257
  return true;
250
258
  }
251
- setState((prev) => ({ ...prev, isLoading: false }));
259
+ setState((prev) => ({
260
+ ...prev,
261
+ isLoading: false,
262
+ lastChallengeError: retakeMessageForChallenge(currentChallenge.type)
263
+ }));
252
264
  return false;
253
265
  } catch (error) {
254
266
  setState((prev) => ({
@@ -299,7 +311,8 @@ function useKoraIDV() {
299
311
  currentChallenge: null,
300
312
  completedChallenges: 0,
301
313
  isLoading: false,
302
- error: null
314
+ error: null,
315
+ lastChallengeError: null
303
316
  });
304
317
  }, [sdk]);
305
318
  const retry = useCallback(() => {
@@ -326,9 +339,27 @@ function useKoraIDV() {
326
339
  sdk
327
340
  };
328
341
  }
342
+ function retakeMessageForChallenge(type) {
343
+ switch (type) {
344
+ case "blink":
345
+ return "We didn't catch the blink \u2014 close both eyes briefly and try again.";
346
+ case "smile":
347
+ return "We didn't catch the smile \u2014 show your teeth and try again.";
348
+ case "turn_left":
349
+ return "Turn your head a bit further to the left and try again.";
350
+ case "turn_right":
351
+ return "Turn your head a bit further to the right and try again.";
352
+ case "nod_up":
353
+ return "Tilt your head a bit higher and try again.";
354
+ case "nod_down":
355
+ return "Tilt your head a bit lower and try again.";
356
+ default:
357
+ return "That attempt didn't pass \u2014 follow the prompt and try again.";
358
+ }
359
+ }
329
360
 
330
361
  // src/components/VerificationFlow.tsx
331
- import { useEffect as useEffect8, useState as useState6 } from "react";
362
+ import { useEffect as useEffect11, useState as useState9 } from "react";
332
363
  import { KoraError as KoraError2, KoraErrorCode } from "@koraidv/core";
333
364
 
334
365
  // src/components/styles.ts
@@ -395,6 +426,36 @@ function injectKeyframes() {
395
426
  from { opacity: 0; transform: translateY(8px); }
396
427
  to { opacity: 1; transform: translateY(0); }
397
428
  }
429
+ /* \u2500\u2500\u2500 VisualGuides motion (v1.8.0) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
430
+ @keyframes kora-head-turn-right {
431
+ 0%, 100% { transform: rotate(0deg); }
432
+ 50% { transform: rotate(22deg); }
433
+ }
434
+ @keyframes kora-head-turn-left {
435
+ 0%, 100% { transform: rotate(0deg); }
436
+ 50% { transform: rotate(-22deg); }
437
+ }
438
+ @keyframes kora-head-tilt-up {
439
+ 0%, 100% { transform: translateY(0); }
440
+ 50% { transform: translateY(-6px) rotate(-6deg); }
441
+ }
442
+ @keyframes kora-head-tilt-down {
443
+ 0%, 100% { transform: translateY(0); }
444
+ 50% { transform: translateY(6px) rotate(6deg); }
445
+ }
446
+ @keyframes kora-smile {
447
+ 0%, 100% { transform: scaleY(0.5); }
448
+ 50% { transform: scaleY(1.2); }
449
+ }
450
+ @keyframes kora-blink {
451
+ 0%, 80%, 100% { transform: scaleY(1); }
452
+ 88% { transform: scaleY(0.05); }
453
+ }
454
+ @keyframes kora-nfc-wave {
455
+ 0% { opacity: 0; transform: translateX(-4px); }
456
+ 40% { opacity: 1; }
457
+ 100% { opacity: 0; transform: translateX(6px); }
458
+ }
398
459
  `;
399
460
  document.head.appendChild(style);
400
461
  }
@@ -1308,7 +1369,7 @@ var styles = {
1308
1369
  };
1309
1370
 
1310
1371
  // src/components/DesignSystem.tsx
1311
- import { useEffect as useEffect2 } from "react";
1372
+ import { useEffect as useEffect2, useState as useState2 } from "react";
1312
1373
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
1313
1374
  function StepProgressBar({ total, current, isDark = false }) {
1314
1375
  return /* @__PURE__ */ jsx2("div", { style: styles.progressBar, children: Array.from({ length: total }).map((_, i) => /* @__PURE__ */ jsx2(
@@ -1381,10 +1442,26 @@ function ScoreMetricRow({ label, score, icon, status, message }) {
1381
1442
  }
1382
1443
  );
1383
1444
  }
1384
- function ProcessingScreen({ steps }) {
1445
+ var DEFAULT_AUTO_STEPS = [
1446
+ "Document analyzed",
1447
+ "Checking face match",
1448
+ "Finalizing results"
1449
+ ];
1450
+ function ProcessingScreen({ steps, autoAdvance = true }) {
1385
1451
  useEffect2(() => {
1386
1452
  injectKeyframes();
1387
1453
  }, []);
1454
+ const [autoIndex, setAutoIndex] = useState2(0);
1455
+ useEffect2(() => {
1456
+ if (steps || !autoAdvance) return;
1457
+ if (autoIndex >= DEFAULT_AUTO_STEPS.length - 1) return;
1458
+ const t = setTimeout(() => setAutoIndex((i) => i + 1), 1400);
1459
+ return () => clearTimeout(t);
1460
+ }, [autoIndex, steps, autoAdvance]);
1461
+ const renderedSteps = steps ? steps : DEFAULT_AUTO_STEPS.map((label, i) => ({
1462
+ label,
1463
+ status: i < autoIndex ? "done" : i === autoIndex ? "active" : "pending"
1464
+ }));
1388
1465
  return /* @__PURE__ */ jsxs("div", { style: styles.processingContainer, children: [
1389
1466
  /* @__PURE__ */ jsxs("div", { style: styles.spinnerContainer, children: [
1390
1467
  /* @__PURE__ */ jsx2(
@@ -1435,7 +1512,7 @@ function ProcessingScreen({ steps }) {
1435
1512
  }
1436
1513
  )
1437
1514
  ] }),
1438
- /* @__PURE__ */ jsx2("div", { style: styles.processingSteps, children: steps.map((step, i) => /* @__PURE__ */ jsxs("div", { style: styles.processingStep, children: [
1515
+ /* @__PURE__ */ jsx2("div", { style: styles.processingSteps, children: renderedSteps.map((step, i) => /* @__PURE__ */ jsxs("div", { style: styles.processingStep, children: [
1439
1516
  /* @__PURE__ */ jsx2(
1440
1517
  "div",
1441
1518
  {
@@ -1603,11 +1680,11 @@ function ConsentItem({
1603
1680
  }
1604
1681
 
1605
1682
  // src/components/CountrySelectionScreen.tsx
1606
- import { useState as useState2, useMemo as useMemo2 } from "react";
1683
+ import { useState as useState3, useMemo as useMemo2 } from "react";
1607
1684
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1608
1685
  function CountrySelectionScreen({ countries, onSelect, onCancel }) {
1609
- const [selected, setSelected] = useState2(null);
1610
- const [searchQuery, setSearchQuery] = useState2("");
1686
+ const [selected, setSelected] = useState3(null);
1687
+ const [searchQuery, setSearchQuery] = useState3("");
1611
1688
  const filteredCountries = useMemo2(() => {
1612
1689
  const countryList = countries || [];
1613
1690
  if (!searchQuery.trim()) return countryList;
@@ -1757,8 +1834,243 @@ function getIcon(type) {
1757
1834
  }
1758
1835
 
1759
1836
  // src/components/DocumentCaptureScreen.tsx
1760
- import { useRef as useRef2, useEffect as useEffect3, useState as useState3, useCallback as useCallback2 } from "react";
1761
- import { Fragment, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1837
+ import { useRef as useRef3, useEffect as useEffect5, useState as useState5, useCallback as useCallback2 } from "react";
1838
+
1839
+ // src/components/VisualGuides.tsx
1840
+ import { useEffect as useEffect3 } from "react";
1841
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1842
+ function visualGuideForChallenge(challengeType) {
1843
+ switch (challengeType) {
1844
+ case "turn_left":
1845
+ return "livenessTurnLeft";
1846
+ case "turn_right":
1847
+ return "livenessTurnRight";
1848
+ case "nod_up":
1849
+ return "livenessLookUp";
1850
+ case "nod_down":
1851
+ return "livenessLookDown";
1852
+ case "smile":
1853
+ return "livenessSmile";
1854
+ case "blink":
1855
+ return "livenessBlink";
1856
+ default:
1857
+ return null;
1858
+ }
1859
+ }
1860
+ function VisualGuide({ kind, size = 96 }) {
1861
+ useEffect3(() => {
1862
+ injectKeyframes();
1863
+ }, []);
1864
+ const common = { width: size, height: size, viewBox: "0 0 100 100" };
1865
+ const fg = colors.teal;
1866
+ const dim = "rgba(255,255,255,0.3)";
1867
+ switch (kind) {
1868
+ case "docFront":
1869
+ return /* @__PURE__ */ jsx6(DocFront, { ...common, fg, dim });
1870
+ case "docBack":
1871
+ return /* @__PURE__ */ jsx6(DocBack, { ...common, fg, dim });
1872
+ case "selfie":
1873
+ return /* @__PURE__ */ jsx6(Selfie, { ...common, fg, dim });
1874
+ case "nfcScan":
1875
+ return /* @__PURE__ */ jsx6(NfcScan, { ...common, fg, dim });
1876
+ case "livenessTurnLeft":
1877
+ return /* @__PURE__ */ jsx6(HeadTurn, { ...common, fg, dim, right: false });
1878
+ case "livenessTurnRight":
1879
+ return /* @__PURE__ */ jsx6(HeadTurn, { ...common, fg, dim, right: true });
1880
+ case "livenessLookUp":
1881
+ return /* @__PURE__ */ jsx6(HeadTilt, { ...common, fg, dim, up: true });
1882
+ case "livenessLookDown":
1883
+ return /* @__PURE__ */ jsx6(HeadTilt, { ...common, fg, dim, up: false });
1884
+ case "livenessSmile":
1885
+ return /* @__PURE__ */ jsx6(Smile, { ...common, fg, dim });
1886
+ case "livenessBlink":
1887
+ return /* @__PURE__ */ jsx6(Blink, { ...common, fg, dim });
1888
+ }
1889
+ }
1890
+ function DocFront({ width, height, viewBox, fg, dim }) {
1891
+ return /* @__PURE__ */ jsxs5("svg", { width, height, viewBox, fill: "none", children: [
1892
+ /* @__PURE__ */ jsx6("rect", { x: "8", y: "26", width: "84", height: "48", rx: "5", stroke: fg, strokeWidth: "2.5" }),
1893
+ /* @__PURE__ */ jsx6("rect", { x: "14", y: "34", width: "22", height: "28", rx: "2", fill: dim }),
1894
+ /* @__PURE__ */ jsx6("rect", { x: "42", y: "36", width: "44", height: "3", rx: "1.5", fill: fg }),
1895
+ /* @__PURE__ */ jsx6("rect", { x: "42", y: "44", width: "36", height: "2.5", rx: "1.25", fill: dim }),
1896
+ /* @__PURE__ */ jsx6("rect", { x: "42", y: "50", width: "40", height: "2.5", rx: "1.25", fill: dim }),
1897
+ /* @__PURE__ */ jsx6("rect", { x: "42", y: "56", width: "30", height: "2.5", rx: "1.25", fill: dim })
1898
+ ] });
1899
+ }
1900
+ function DocBack({ width, height, viewBox, fg, dim }) {
1901
+ return /* @__PURE__ */ jsxs5("svg", { width, height, viewBox, fill: "none", children: [
1902
+ /* @__PURE__ */ jsx6("rect", { x: "8", y: "26", width: "84", height: "48", rx: "5", stroke: fg, strokeWidth: "2.5" }),
1903
+ [16, 19, 22, 26, 28, 32, 35, 39, 42, 46, 49, 53, 56, 60].map((x, i) => /* @__PURE__ */ jsx6(
1904
+ "rect",
1905
+ {
1906
+ x,
1907
+ y: "34",
1908
+ width: i % 3 === 0 ? 2 : 1.2,
1909
+ height: "20",
1910
+ fill: fg
1911
+ },
1912
+ i
1913
+ )),
1914
+ /* @__PURE__ */ jsx6("line", { x1: "14", y1: "64", x2: "58", y2: "64", stroke: dim, strokeWidth: "1.5" }),
1915
+ /* @__PURE__ */ jsx6("rect", { x: "66", y: "34", width: "20", height: "20", rx: "1", fill: dim })
1916
+ ] });
1917
+ }
1918
+ function Selfie({ width, height, viewBox, fg, dim }) {
1919
+ return /* @__PURE__ */ jsxs5("svg", { width, height, viewBox, fill: "none", children: [
1920
+ /* @__PURE__ */ jsx6("ellipse", { cx: "50", cy: "42", rx: "22", ry: "28", stroke: fg, strokeWidth: "2.5" }),
1921
+ /* @__PURE__ */ jsx6("circle", { cx: "42", cy: "38", r: "2.5", fill: fg }),
1922
+ /* @__PURE__ */ jsx6("circle", { cx: "58", cy: "38", r: "2.5", fill: fg }),
1923
+ /* @__PURE__ */ jsx6("path", { d: "M 40 50 Q 50 56 60 50", stroke: fg, strokeWidth: "2", strokeLinecap: "round", fill: "none" }),
1924
+ /* @__PURE__ */ jsx6("path", { d: "M 18 92 Q 18 76 36 70 L 64 70 Q 82 76 82 92", stroke: dim, strokeWidth: "2", fill: "none" })
1925
+ ] });
1926
+ }
1927
+ function NfcScan({ width, height, viewBox, fg, dim }) {
1928
+ return /* @__PURE__ */ jsxs5("svg", { width, height, viewBox, fill: "none", children: [
1929
+ /* @__PURE__ */ jsx6("rect", { x: "14", y: "40", width: "40", height: "50", rx: "3", stroke: dim, strokeWidth: "2" }),
1930
+ /* @__PURE__ */ jsx6("circle", { cx: "34", cy: "60", r: "6", stroke: dim, strokeWidth: "1.5" }),
1931
+ /* @__PURE__ */ jsx6("rect", { x: "62", y: "22", width: "26", height: "52", rx: "4", stroke: fg, strokeWidth: "2.5" }),
1932
+ /* @__PURE__ */ jsx6("rect", { x: "66", y: "26", width: "18", height: "38", rx: "1.5", fill: dim, opacity: "0.5" }),
1933
+ /* @__PURE__ */ jsx6("path", { d: "M 58 48 Q 52 48 50 56", stroke: fg, strokeWidth: "2", fill: "none", style: { animation: "kora-nfc-wave 1.6s ease-out infinite" } }),
1934
+ /* @__PURE__ */ jsx6("path", { d: "M 58 44 Q 50 44 46 56", stroke: fg, strokeWidth: "2", fill: "none", opacity: "0.7", style: { animation: "kora-nfc-wave 1.6s ease-out infinite 0.3s" } })
1935
+ ] });
1936
+ }
1937
+ function HeadTurn({ width, height, viewBox, fg, dim, right }) {
1938
+ const arrowPath = right ? "M 30 12 L 70 12 L 64 6 M 70 12 L 64 18" : "M 70 12 L 30 12 L 36 6 M 30 12 L 36 18";
1939
+ return /* @__PURE__ */ jsxs5("svg", { width, height, viewBox, fill: "none", children: [
1940
+ /* @__PURE__ */ jsx6("path", { d: arrowPath, stroke: fg, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", fill: "none" }),
1941
+ /* @__PURE__ */ jsxs5(
1942
+ "g",
1943
+ {
1944
+ style: {
1945
+ transformOrigin: "50px 56px",
1946
+ animation: right ? "kora-head-turn-right 2s ease-in-out infinite" : "kora-head-turn-left 2s ease-in-out infinite"
1947
+ },
1948
+ children: [
1949
+ /* @__PURE__ */ jsx6("ellipse", { cx: "50", cy: "55", rx: "20", ry: "26", stroke: fg, strokeWidth: "2.5" }),
1950
+ /* @__PURE__ */ jsx6("circle", { cx: "42", cy: "50", r: "2", fill: fg }),
1951
+ /* @__PURE__ */ jsx6("circle", { cx: "58", cy: "50", r: "2", fill: fg }),
1952
+ /* @__PURE__ */ jsx6("path", { d: "M 50 54 L 50 62", stroke: dim, strokeWidth: "1.5", strokeLinecap: "round" })
1953
+ ]
1954
+ }
1955
+ )
1956
+ ] });
1957
+ }
1958
+ function HeadTilt({ width, height, viewBox, fg, dim, up }) {
1959
+ const arrowPath = up ? "M 50 92 L 50 14 M 44 22 L 50 14 L 56 22" : "M 50 14 L 50 92 M 44 84 L 50 92 L 56 84";
1960
+ return /* @__PURE__ */ jsxs5("svg", { width, height, viewBox, fill: "none", children: [
1961
+ /* @__PURE__ */ jsx6("path", { d: arrowPath, stroke: fg, strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", opacity: "0.4", fill: "none" }),
1962
+ /* @__PURE__ */ jsxs5(
1963
+ "g",
1964
+ {
1965
+ style: {
1966
+ transformOrigin: "50px 56px",
1967
+ animation: up ? "kora-head-tilt-up 2s ease-in-out infinite" : "kora-head-tilt-down 2s ease-in-out infinite"
1968
+ },
1969
+ children: [
1970
+ /* @__PURE__ */ jsx6("ellipse", { cx: "50", cy: "55", rx: "20", ry: "26", stroke: fg, strokeWidth: "2.5" }),
1971
+ /* @__PURE__ */ jsx6("circle", { cx: "42", cy: "50", r: "2", fill: fg }),
1972
+ /* @__PURE__ */ jsx6("circle", { cx: "58", cy: "50", r: "2", fill: fg }),
1973
+ /* @__PURE__ */ jsx6("path", { d: "M 50 54 L 50 62", stroke: dim, strokeWidth: "1.5", strokeLinecap: "round" })
1974
+ ]
1975
+ }
1976
+ )
1977
+ ] });
1978
+ }
1979
+ function Smile({ width, height, viewBox, fg, dim }) {
1980
+ return /* @__PURE__ */ jsxs5("svg", { width, height, viewBox, fill: "none", children: [
1981
+ /* @__PURE__ */ jsx6("ellipse", { cx: "50", cy: "50", rx: "24", ry: "30", stroke: fg, strokeWidth: "2.5" }),
1982
+ /* @__PURE__ */ jsx6("circle", { cx: "40", cy: "44", r: "2.5", fill: fg }),
1983
+ /* @__PURE__ */ jsx6("circle", { cx: "60", cy: "44", r: "2.5", fill: fg }),
1984
+ /* @__PURE__ */ jsx6(
1985
+ "path",
1986
+ {
1987
+ d: "M 38 60 Q 50 68 62 60",
1988
+ stroke: fg,
1989
+ strokeWidth: "2.5",
1990
+ strokeLinecap: "round",
1991
+ fill: "none",
1992
+ style: { animation: "kora-smile 2s ease-in-out infinite", transformOrigin: "50px 60px" }
1993
+ }
1994
+ ),
1995
+ /* @__PURE__ */ jsx6("line", { x1: "40", y1: "60", x2: "60", y2: "60", stroke: dim, strokeWidth: "1.5", opacity: "0.3" })
1996
+ ] });
1997
+ }
1998
+ function Blink({ width, height, viewBox, fg, dim }) {
1999
+ return /* @__PURE__ */ jsxs5("svg", { width, height, viewBox, fill: "none", children: [
2000
+ /* @__PURE__ */ jsx6("ellipse", { cx: "50", cy: "50", rx: "24", ry: "30", stroke: fg, strokeWidth: "2.5" }),
2001
+ /* @__PURE__ */ jsxs5("g", { style: { animation: "kora-blink 1.6s ease-in-out infinite" }, children: [
2002
+ /* @__PURE__ */ jsx6("circle", { cx: "40", cy: "44", r: "3", fill: fg }),
2003
+ /* @__PURE__ */ jsx6("circle", { cx: "60", cy: "44", r: "3", fill: fg })
2004
+ ] }),
2005
+ /* @__PURE__ */ jsx6("path", { d: "M 42 60 Q 50 64 58 60", stroke: dim, strokeWidth: "2", strokeLinecap: "round", fill: "none" })
2006
+ ] });
2007
+ }
2008
+
2009
+ // src/hooks/useDocumentDetection.ts
2010
+ import { useEffect as useEffect4, useRef as useRef2, useState as useState4 } from "react";
2011
+ var FRAME_INTERVAL_MS = 300;
2012
+ function useDocumentDetection(videoRef, side) {
2013
+ const [signals, setSignals] = useState4({
2014
+ documentDetected: false,
2015
+ detectorActive: false
2016
+ });
2017
+ const detectorRef = useRef2(null);
2018
+ const intervalRef = useRef2(null);
2019
+ useEffect4(() => {
2020
+ let cancelled = false;
2021
+ async function setup() {
2022
+ const win = typeof window !== "undefined" ? window : null;
2023
+ if (!win) return;
2024
+ let DetectorCtor = null;
2025
+ if (side === "front" && "FaceDetector" in win) {
2026
+ DetectorCtor = win["FaceDetector"];
2027
+ } else if (side === "back" && "BarcodeDetector" in win) {
2028
+ DetectorCtor = win["BarcodeDetector"];
2029
+ }
2030
+ if (!DetectorCtor) {
2031
+ return;
2032
+ }
2033
+ try {
2034
+ const detector = new DetectorCtor(
2035
+ side === "front" ? { fastMode: true, maxDetectedFaces: 1 } : { formats: ["pdf417", "qr_code", "data_matrix", "code_128"] }
2036
+ );
2037
+ if (cancelled) return;
2038
+ detectorRef.current = detector;
2039
+ setSignals((prev) => ({ ...prev, detectorActive: true }));
2040
+ } catch {
2041
+ return;
2042
+ }
2043
+ intervalRef.current = setInterval(async () => {
2044
+ const detector = detectorRef.current;
2045
+ const video = videoRef.current;
2046
+ if (!detector || !video || video.readyState < 2) return;
2047
+ try {
2048
+ const results = await detector.detect(video);
2049
+ if (cancelled) return;
2050
+ setSignals((prev) => {
2051
+ const next = results.length > 0;
2052
+ if (prev.documentDetected === next) return prev;
2053
+ return { ...prev, documentDetected: next };
2054
+ });
2055
+ } catch {
2056
+ }
2057
+ }, FRAME_INTERVAL_MS);
2058
+ }
2059
+ setup();
2060
+ return () => {
2061
+ cancelled = true;
2062
+ if (intervalRef.current) {
2063
+ clearInterval(intervalRef.current);
2064
+ intervalRef.current = null;
2065
+ }
2066
+ detectorRef.current = null;
2067
+ };
2068
+ }, [videoRef, side]);
2069
+ return signals;
2070
+ }
2071
+
2072
+ // src/components/DocumentCaptureScreen.tsx
2073
+ import { Fragment, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1762
2074
  var qualityIssueMessages = {
1763
2075
  face_blurred: "Photo on document is blurry. Retake in better lighting.",
1764
2076
  low_resolution: "Image quality too low. Move closer to document.",
@@ -1775,23 +2087,25 @@ function DocumentCaptureScreen({
1775
2087
  requiresBack = true,
1776
2088
  onQualityCheck,
1777
2089
  onCapture,
1778
- onCancel
2090
+ onCancel,
2091
+ showVisualGuides = true
1779
2092
  }) {
1780
- const videoRef = useRef2(null);
1781
- const canvasRef = useRef2(null);
1782
- const guideRef = useRef2(null);
1783
- const [stream, setStream] = useState3(null);
1784
- const [isCapturing, setIsCapturing] = useState3(false);
1785
- const [error, setError] = useState3(null);
1786
- const [capturedImage, setCapturedImage] = useState3(null);
1787
- const [capturedBlob, setCapturedBlob] = useState3(null);
1788
- const [qualityResult, setQualityResult] = useState3(null);
1789
- const [isCheckingQuality, setIsCheckingQuality] = useState3(false);
1790
- const [retakeCount, setRetakeCount] = useState3(0);
1791
- useEffect3(() => {
2093
+ const videoRef = useRef3(null);
2094
+ const canvasRef = useRef3(null);
2095
+ const guideRef = useRef3(null);
2096
+ const [stream, setStream] = useState5(null);
2097
+ const documentSignals = useDocumentDetection(videoRef, side);
2098
+ const [isCapturing, setIsCapturing] = useState5(false);
2099
+ const [error, setError] = useState5(null);
2100
+ const [capturedImage, setCapturedImage] = useState5(null);
2101
+ const [capturedBlob, setCapturedBlob] = useState5(null);
2102
+ const [qualityResult, setQualityResult] = useState5(null);
2103
+ const [isCheckingQuality, setIsCheckingQuality] = useState5(false);
2104
+ const [retakeCount, setRetakeCount] = useState5(0);
2105
+ useEffect5(() => {
1792
2106
  injectKeyframes();
1793
2107
  }, []);
1794
- useEffect3(() => {
2108
+ useEffect5(() => {
1795
2109
  let mounted = true;
1796
2110
  async function startCamera() {
1797
2111
  try {
@@ -1813,7 +2127,7 @@ function DocumentCaptureScreen({
1813
2127
  mounted = false;
1814
2128
  };
1815
2129
  }, [capturedImage]);
1816
- useEffect3(() => {
2130
+ useEffect5(() => {
1817
2131
  return () => {
1818
2132
  stream?.getTracks().forEach((t) => t.stop());
1819
2133
  };
@@ -1906,24 +2220,24 @@ function DocumentCaptureScreen({
1906
2220
  }
1907
2221
  };
1908
2222
  if (error) {
1909
- return /* @__PURE__ */ jsx6("div", { style: styles.container, children: /* @__PURE__ */ jsxs5("div", { style: styles.errorContainer, children: [
1910
- /* @__PURE__ */ jsx6("p", { style: styles.errorText, children: error }),
1911
- /* @__PURE__ */ jsx6("button", { style: styles.primaryButton, onClick: onCancel, children: "Go Back" })
2223
+ return /* @__PURE__ */ jsx7("div", { style: styles.container, children: /* @__PURE__ */ jsxs6("div", { style: styles.errorContainer, children: [
2224
+ /* @__PURE__ */ jsx7("p", { style: styles.errorText, children: error }),
2225
+ /* @__PURE__ */ jsx7("button", { style: styles.primaryButton, onClick: onCancel, children: "Go Back" })
1912
2226
  ] }) });
1913
2227
  }
1914
2228
  if (capturedImage) {
1915
2229
  const qualityPassed = qualityResult && qualityResult.qualityScore >= 60;
1916
2230
  const qualityFailed = qualityResult && qualityResult.qualityScore < 60;
1917
2231
  const canContinueAnyway = qualityFailed && retakeCount >= 2;
1918
- return /* @__PURE__ */ jsxs5("div", { style: styles.darkContainer, children: [
1919
- /* @__PURE__ */ jsx6(StepProgressBar, { total: 5, current: 3, isDark: true }),
1920
- /* @__PURE__ */ jsxs5("div", { style: styles.darkScreenHeader, children: [
1921
- /* @__PURE__ */ jsx6("div", { style: { width: 40 } }),
1922
- /* @__PURE__ */ jsx6("h1", { style: styles.darkScreenTitle, children: "Review your photo" }),
1923
- /* @__PURE__ */ jsx6("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2232
+ return /* @__PURE__ */ jsxs6("div", { style: styles.darkContainer, children: [
2233
+ /* @__PURE__ */ jsx7(StepProgressBar, { total: 5, current: 3, isDark: true }),
2234
+ /* @__PURE__ */ jsxs6("div", { style: styles.darkScreenHeader, children: [
2235
+ /* @__PURE__ */ jsx7("div", { style: { width: 40 } }),
2236
+ /* @__PURE__ */ jsx7("h1", { style: styles.darkScreenTitle, children: "Review your photo" }),
2237
+ /* @__PURE__ */ jsx7("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
1924
2238
  ] }),
1925
- /* @__PURE__ */ jsx6("div", { style: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: "24px" }, children: /* @__PURE__ */ jsxs5("div", { style: styles.reviewCard, children: [
1926
- /* @__PURE__ */ jsx6(
2239
+ /* @__PURE__ */ jsx7("div", { style: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: "24px" }, children: /* @__PURE__ */ jsxs6("div", { style: styles.reviewCard, children: [
2240
+ /* @__PURE__ */ jsx7(
1927
2241
  "img",
1928
2242
  {
1929
2243
  src: capturedImage,
@@ -1931,42 +2245,42 @@ function DocumentCaptureScreen({
1931
2245
  style: { width: "100%", maxWidth: "300px", borderRadius: "16px", display: "block", margin: "0 auto" }
1932
2246
  }
1933
2247
  ),
1934
- isCheckingQuality && /* @__PURE__ */ jsx6("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsx6("span", { style: { ...styles.reviewBadge, backgroundColor: "rgba(255,255,255,0.1)" }, children: "Checking quality..." }) }),
1935
- qualityPassed && /* @__PURE__ */ jsxs5(Fragment, { children: [
1936
- /* @__PURE__ */ jsx6("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsxs5("span", { style: styles.reviewBadge, children: [
2248
+ isCheckingQuality && /* @__PURE__ */ jsx7("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsx7("span", { style: { ...styles.reviewBadge, backgroundColor: "rgba(255,255,255,0.1)" }, children: "Checking quality..." }) }),
2249
+ qualityPassed && /* @__PURE__ */ jsxs6(Fragment, { children: [
2250
+ /* @__PURE__ */ jsx7("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsxs6("span", { style: styles.reviewBadge, children: [
1937
2251
  "\u2713 Quality score: ",
1938
2252
  Math.round(qualityResult.qualityScore),
1939
2253
  "%"
1940
2254
  ] }) }),
1941
- /* @__PURE__ */ jsxs5("div", { style: styles.qualityChecks, children: [
1942
- /* @__PURE__ */ jsx6(QualityCheck, { label: "Sharp" }),
1943
- /* @__PURE__ */ jsx6(QualityCheck, { label: "Well-lit" }),
1944
- /* @__PURE__ */ jsx6(QualityCheck, { label: "Readable" })
2255
+ /* @__PURE__ */ jsxs6("div", { style: styles.qualityChecks, children: [
2256
+ /* @__PURE__ */ jsx7(QualityCheck, { label: "Sharp" }),
2257
+ /* @__PURE__ */ jsx7(QualityCheck, { label: "Well-lit" }),
2258
+ /* @__PURE__ */ jsx7(QualityCheck, { label: "Readable" })
1945
2259
  ] })
1946
2260
  ] }),
1947
- qualityFailed && /* @__PURE__ */ jsxs5(Fragment, { children: [
1948
- /* @__PURE__ */ jsx6("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsxs5("span", { style: { ...styles.reviewBadge, backgroundColor: "rgba(239,68,68,0.15)", color: "#ef4444" }, children: [
2261
+ qualityFailed && /* @__PURE__ */ jsxs6(Fragment, { children: [
2262
+ /* @__PURE__ */ jsx7("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsxs6("span", { style: { ...styles.reviewBadge, backgroundColor: "rgba(239,68,68,0.15)", color: "#ef4444" }, children: [
1949
2263
  "\u26A0 Quality score: ",
1950
2264
  Math.round(qualityResult.qualityScore),
1951
2265
  "%"
1952
2266
  ] }) }),
1953
- /* @__PURE__ */ jsx6("div", { style: { padding: "12px 0" }, children: qualityResult.qualityIssues.map((issue, i) => /* @__PURE__ */ jsx6("p", { style: { color: "rgba(255,255,255,0.7)", fontSize: "13px", margin: "4px 0", textAlign: "center" }, children: qualityIssueMessages[issue] || issue }, i)) })
2267
+ /* @__PURE__ */ jsx7("div", { style: { padding: "12px 0" }, children: qualityResult.qualityIssues.map((issue, i) => /* @__PURE__ */ jsx7("p", { style: { color: "rgba(255,255,255,0.7)", fontSize: "13px", margin: "4px 0", textAlign: "center" }, children: qualityIssueMessages[issue] || issue }, i)) })
1954
2268
  ] }),
1955
- !qualityResult && !isCheckingQuality && /* @__PURE__ */ jsxs5(Fragment, { children: [
1956
- /* @__PURE__ */ jsx6("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsx6("span", { style: styles.reviewBadge, children: "\u2713 Good quality" }) }),
1957
- /* @__PURE__ */ jsxs5("div", { style: styles.qualityChecks, children: [
1958
- /* @__PURE__ */ jsx6(QualityCheck, { label: "Sharp" }),
1959
- /* @__PURE__ */ jsx6(QualityCheck, { label: "Well-lit" }),
1960
- /* @__PURE__ */ jsx6(QualityCheck, { label: "No glare" })
2269
+ !qualityResult && !isCheckingQuality && /* @__PURE__ */ jsxs6(Fragment, { children: [
2270
+ /* @__PURE__ */ jsx7("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsx7("span", { style: styles.reviewBadge, children: "\u2713 Good quality" }) }),
2271
+ /* @__PURE__ */ jsxs6("div", { style: styles.qualityChecks, children: [
2272
+ /* @__PURE__ */ jsx7(QualityCheck, { label: "Sharp" }),
2273
+ /* @__PURE__ */ jsx7(QualityCheck, { label: "Well-lit" }),
2274
+ /* @__PURE__ */ jsx7(QualityCheck, { label: "No glare" })
1961
2275
  ] })
1962
2276
  ] })
1963
2277
  ] }) }),
1964
- /* @__PURE__ */ jsx6("div", { style: styles.reviewButtonsRow, children: qualityFailed ? /* @__PURE__ */ jsxs5(Fragment, { children: [
1965
- /* @__PURE__ */ jsx6("button", { style: { ...styles.darkOutlineButton, flex: 1 }, onClick: handleRetake, children: "Retake" }),
1966
- canContinueAnyway && /* @__PURE__ */ jsx6("button", { style: { ...styles.primaryButton, flex: 1 }, onClick: handleContinueAnyway, children: "Continue anyway" })
1967
- ] }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
1968
- /* @__PURE__ */ jsx6("button", { style: { ...styles.darkOutlineButton, flex: 1 }, onClick: handleRetake, children: "Retake" }),
1969
- /* @__PURE__ */ jsx6(
2278
+ /* @__PURE__ */ jsx7("div", { style: styles.reviewButtonsRow, children: qualityFailed ? /* @__PURE__ */ jsxs6(Fragment, { children: [
2279
+ /* @__PURE__ */ jsx7("button", { style: { ...styles.darkOutlineButton, flex: 1 }, onClick: handleRetake, children: "Retake" }),
2280
+ canContinueAnyway && /* @__PURE__ */ jsx7("button", { style: { ...styles.primaryButton, flex: 1 }, onClick: handleContinueAnyway, children: "Continue anyway" })
2281
+ ] }) : /* @__PURE__ */ jsxs6(Fragment, { children: [
2282
+ /* @__PURE__ */ jsx7("button", { style: { ...styles.darkOutlineButton, flex: 1 }, onClick: handleRetake, children: "Retake" }),
2283
+ /* @__PURE__ */ jsx7(
1970
2284
  "button",
1971
2285
  {
1972
2286
  style: { ...styles.primaryButton, flex: 1, opacity: isCheckingQuality ? 0.5 : 1 },
@@ -1978,29 +2292,54 @@ function DocumentCaptureScreen({
1978
2292
  ] }) })
1979
2293
  ] });
1980
2294
  }
1981
- return /* @__PURE__ */ jsxs5("div", { style: styles.captureContainer, children: [
1982
- /* @__PURE__ */ jsx6(StepProgressBar, { total: 5, current: 3, isDark: true }),
1983
- /* @__PURE__ */ jsxs5("div", { style: styles.darkScreenHeader, children: [
1984
- /* @__PURE__ */ jsx6("div", { style: { width: 40 } }),
1985
- /* @__PURE__ */ jsxs5("div", { style: { flex: 1, textAlign: "center" }, children: [
1986
- /* @__PURE__ */ jsx6("h1", { style: { ...styles.darkScreenTitle, margin: 0 }, children: side === "front" ? "Front of ID" : "Back of ID" }),
1987
- documentType && /* @__PURE__ */ jsx6("p", { style: styles.darkScreenSubtitle, children: documentType })
2295
+ return /* @__PURE__ */ jsxs6("div", { style: styles.captureContainer, children: [
2296
+ /* @__PURE__ */ jsx7(StepProgressBar, { total: 5, current: 3, isDark: true }),
2297
+ /* @__PURE__ */ jsxs6("div", { style: styles.darkScreenHeader, children: [
2298
+ /* @__PURE__ */ jsx7("div", { style: { width: 40 } }),
2299
+ /* @__PURE__ */ jsxs6("div", { style: { flex: 1, textAlign: "center" }, children: [
2300
+ /* @__PURE__ */ jsx7("h1", { style: { ...styles.darkScreenTitle, margin: 0 }, children: side === "front" ? "Front of ID" : "Back of ID" }),
2301
+ documentType && /* @__PURE__ */ jsx7("p", { style: styles.darkScreenSubtitle, children: documentType })
1988
2302
  ] }),
1989
- /* @__PURE__ */ jsx6("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2303
+ /* @__PURE__ */ jsx7("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
1990
2304
  ] }),
1991
- /* @__PURE__ */ jsxs5("div", { style: styles.cameraContainer, children: [
1992
- /* @__PURE__ */ jsx6("video", { ref: videoRef, autoPlay: true, playsInline: true, muted: true, style: styles.cameraVideo }),
1993
- /* @__PURE__ */ jsx6("div", { style: styles.documentOverlay, children: /* @__PURE__ */ jsxs5("div", { ref: guideRef, style: styles.documentFrame, children: [
1994
- /* @__PURE__ */ jsx6("div", { style: { ...styles.corner, top: 0, left: 0 } }),
1995
- /* @__PURE__ */ jsx6("div", { style: { ...styles.corner, top: 0, right: 0, transform: "rotate(90deg)" } }),
1996
- /* @__PURE__ */ jsx6("div", { style: { ...styles.corner, bottom: 0, right: 0, transform: "rotate(180deg)" } }),
1997
- /* @__PURE__ */ jsx6("div", { style: { ...styles.corner, bottom: 0, left: 0, transform: "rotate(270deg)" } }),
1998
- /* @__PURE__ */ jsx6("div", { style: styles.scanLine })
1999
- ] }) }),
2000
- /* @__PURE__ */ jsx6("canvas", { ref: canvasRef, style: { display: "none" } })
2305
+ showVisualGuides && /* @__PURE__ */ jsx7("div", { style: { display: "flex", justifyContent: "center", padding: "6px 0 0" }, children: /* @__PURE__ */ jsx7(VisualGuide, { kind: side === "front" ? "docFront" : "docBack", size: 56 }) }),
2306
+ /* @__PURE__ */ jsxs6("div", { style: styles.cameraContainer, children: [
2307
+ /* @__PURE__ */ jsx7("video", { ref: videoRef, autoPlay: true, playsInline: true, muted: true, style: styles.cameraVideo }),
2308
+ /* @__PURE__ */ jsxs6("div", { style: styles.documentOverlay, children: [
2309
+ /* @__PURE__ */ jsxs6("div", { ref: guideRef, style: styles.documentFrame, children: [
2310
+ /* @__PURE__ */ jsx7("div", { style: { ...styles.corner, top: 0, left: 0 } }),
2311
+ /* @__PURE__ */ jsx7("div", { style: { ...styles.corner, top: 0, right: 0, transform: "rotate(90deg)" } }),
2312
+ /* @__PURE__ */ jsx7("div", { style: { ...styles.corner, bottom: 0, right: 0, transform: "rotate(180deg)" } }),
2313
+ /* @__PURE__ */ jsx7("div", { style: { ...styles.corner, bottom: 0, left: 0, transform: "rotate(270deg)" } }),
2314
+ /* @__PURE__ */ jsx7("div", { style: styles.scanLine })
2315
+ ] }),
2316
+ documentSignals.detectorActive && /* @__PURE__ */ jsx7(
2317
+ "div",
2318
+ {
2319
+ style: {
2320
+ position: "absolute",
2321
+ bottom: "24px",
2322
+ left: "50%",
2323
+ transform: "translateX(-50%)",
2324
+ padding: "6px 14px",
2325
+ borderRadius: "999px",
2326
+ fontSize: "12px",
2327
+ fontWeight: 600,
2328
+ color: documentSignals.documentDetected ? colors.success : "rgba(255,255,255,0.55)",
2329
+ backgroundColor: documentSignals.documentDetected ? "rgba(16,185,129,0.15)" : "rgba(0,0,0,0.35)",
2330
+ border: documentSignals.documentDetected ? "1px solid rgba(16,185,129,0.4)" : "1px solid rgba(255,255,255,0.12)",
2331
+ transition: "all 200ms",
2332
+ pointerEvents: "none",
2333
+ whiteSpace: "nowrap"
2334
+ },
2335
+ children: documentSignals.documentDetected ? "\u2713 Document detected \u2014 fill the frame" : "Position your ID inside the guide"
2336
+ }
2337
+ )
2338
+ ] }),
2339
+ /* @__PURE__ */ jsx7("canvas", { ref: canvasRef, style: { display: "none" } })
2001
2340
  ] }),
2002
- /* @__PURE__ */ jsxs5("div", { style: styles.stepPillsRow, children: [
2003
- /* @__PURE__ */ jsx6(
2341
+ /* @__PURE__ */ jsxs6("div", { style: styles.stepPillsRow, children: [
2342
+ /* @__PURE__ */ jsx7(
2004
2343
  "span",
2005
2344
  {
2006
2345
  style: {
@@ -2011,7 +2350,7 @@ function DocumentCaptureScreen({
2011
2350
  children: "Front"
2012
2351
  }
2013
2352
  ),
2014
- requiresBack && /* @__PURE__ */ jsx6(
2353
+ requiresBack && /* @__PURE__ */ jsx7(
2015
2354
  "span",
2016
2355
  {
2017
2356
  style: {
@@ -2023,7 +2362,7 @@ function DocumentCaptureScreen({
2023
2362
  }
2024
2363
  )
2025
2364
  ] }),
2026
- /* @__PURE__ */ jsx6("div", { style: { textAlign: "center", padding: "8px 0" }, children: /* @__PURE__ */ jsxs5(
2365
+ /* @__PURE__ */ jsx7("div", { style: { textAlign: "center", padding: "8px 0" }, children: /* @__PURE__ */ jsxs6(
2027
2366
  "span",
2028
2367
  {
2029
2368
  style: {
@@ -2032,44 +2371,44 @@ function DocumentCaptureScreen({
2032
2371
  color: colors.teal
2033
2372
  },
2034
2373
  children: [
2035
- /* @__PURE__ */ jsx6("span", { style: { ...styles.pulsingDot, backgroundColor: colors.teal } }),
2374
+ /* @__PURE__ */ jsx7("span", { style: { ...styles.pulsingDot, backgroundColor: colors.teal } }),
2036
2375
  "Scanning document..."
2037
2376
  ]
2038
2377
  }
2039
2378
  ) }),
2040
- /* @__PURE__ */ jsx6("div", { style: styles.captureFooter, children: /* @__PURE__ */ jsx6(
2379
+ /* @__PURE__ */ jsx7("div", { style: styles.captureFooter, children: /* @__PURE__ */ jsx7(
2041
2380
  "button",
2042
2381
  {
2043
2382
  style: { ...styles.captureButton, opacity: isCapturing ? 0.5 : 1 },
2044
2383
  onClick: handleCapture,
2045
2384
  disabled: isCapturing,
2046
- children: /* @__PURE__ */ jsx6("div", { style: styles.captureButtonInner })
2385
+ children: /* @__PURE__ */ jsx7("div", { style: styles.captureButtonInner })
2047
2386
  }
2048
2387
  ) })
2049
2388
  ] });
2050
2389
  }
2051
2390
  function QualityCheck({ label }) {
2052
- return /* @__PURE__ */ jsxs5("div", { style: styles.qualityCheck, children: [
2053
- /* @__PURE__ */ jsx6("div", { style: styles.qualityCheckIcon, children: "\u2713" }),
2054
- /* @__PURE__ */ jsx6("span", { style: styles.qualityCheckLabel, children: label })
2391
+ return /* @__PURE__ */ jsxs6("div", { style: styles.qualityCheck, children: [
2392
+ /* @__PURE__ */ jsx7("div", { style: styles.qualityCheckIcon, children: "\u2713" }),
2393
+ /* @__PURE__ */ jsx7("span", { style: styles.qualityCheckLabel, children: label })
2055
2394
  ] });
2056
2395
  }
2057
2396
 
2058
2397
  // src/components/FlipDocumentScreen.tsx
2059
- import { useEffect as useEffect4 } from "react";
2060
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2398
+ import { useEffect as useEffect6 } from "react";
2399
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
2061
2400
  function FlipDocumentScreen({ onContinue, onCancel }) {
2062
- useEffect4(() => {
2401
+ useEffect6(() => {
2063
2402
  injectKeyframes();
2064
2403
  }, []);
2065
- return /* @__PURE__ */ jsxs6("div", { style: styles.darkContainer, children: [
2066
- /* @__PURE__ */ jsx7(StepProgressBar, { total: 5, current: 3, isDark: true }),
2067
- /* @__PURE__ */ jsxs6("div", { style: styles.darkScreenHeader, children: [
2068
- /* @__PURE__ */ jsx7("div", { style: { width: 40 } }),
2069
- /* @__PURE__ */ jsx7("h1", { style: styles.darkScreenTitle, children: "Flip your document" }),
2070
- /* @__PURE__ */ jsx7("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2404
+ return /* @__PURE__ */ jsxs7("div", { style: styles.darkContainer, children: [
2405
+ /* @__PURE__ */ jsx8(StepProgressBar, { total: 5, current: 3, isDark: true }),
2406
+ /* @__PURE__ */ jsxs7("div", { style: styles.darkScreenHeader, children: [
2407
+ /* @__PURE__ */ jsx8("div", { style: { width: 40 } }),
2408
+ /* @__PURE__ */ jsx8("h1", { style: styles.darkScreenTitle, children: "Flip your document" }),
2409
+ /* @__PURE__ */ jsx8("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2071
2410
  ] }),
2072
- /* @__PURE__ */ jsxs6("div", { style: {
2411
+ /* @__PURE__ */ jsxs7("div", { style: {
2073
2412
  flex: 1,
2074
2413
  display: "flex",
2075
2414
  flexDirection: "column",
@@ -2078,7 +2417,7 @@ function FlipDocumentScreen({ onContinue, onCancel }) {
2078
2417
  padding: "24px",
2079
2418
  gap: "32px"
2080
2419
  }, children: [
2081
- /* @__PURE__ */ jsx7("div", { style: {
2420
+ /* @__PURE__ */ jsx8("div", { style: {
2082
2421
  width: "120px",
2083
2422
  height: "120px",
2084
2423
  borderRadius: "50%",
@@ -2086,18 +2425,18 @@ function FlipDocumentScreen({ onContinue, onCancel }) {
2086
2425
  display: "flex",
2087
2426
  alignItems: "center",
2088
2427
  justifyContent: "center"
2089
- }, children: /* @__PURE__ */ jsxs6("svg", { width: "56", height: "56", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
2090
- /* @__PURE__ */ jsx7("path", { d: "M9 3L5 6.99H8V14H10V6.99H13L9 3Z", fill: colors.teal }),
2091
- /* @__PURE__ */ jsx7("path", { d: "M16 17.01V10H14V17.01H11L15 21L19 17.01H16Z", fill: colors.teal })
2428
+ }, children: /* @__PURE__ */ jsxs7("svg", { width: "56", height: "56", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
2429
+ /* @__PURE__ */ jsx8("path", { d: "M9 3L5 6.99H8V14H10V6.99H13L9 3Z", fill: colors.teal }),
2430
+ /* @__PURE__ */ jsx8("path", { d: "M16 17.01V10H14V17.01H11L15 21L19 17.01H16Z", fill: colors.teal })
2092
2431
  ] }) }),
2093
- /* @__PURE__ */ jsxs6("div", { style: { textAlign: "center" }, children: [
2094
- /* @__PURE__ */ jsx7("h2", { style: {
2432
+ /* @__PURE__ */ jsxs7("div", { style: { textAlign: "center" }, children: [
2433
+ /* @__PURE__ */ jsx8("h2", { style: {
2095
2434
  fontSize: "22px",
2096
2435
  fontWeight: 700,
2097
2436
  color: colors.white,
2098
2437
  margin: "0 0 12px 0"
2099
2438
  }, children: "Now capture the back" }),
2100
- /* @__PURE__ */ jsx7("p", { style: {
2439
+ /* @__PURE__ */ jsx8("p", { style: {
2101
2440
  fontSize: "15px",
2102
2441
  color: "rgba(255,255,255,0.6)",
2103
2442
  margin: 0,
@@ -2105,38 +2444,38 @@ function FlipDocumentScreen({ onContinue, onCancel }) {
2105
2444
  maxWidth: "280px"
2106
2445
  }, children: "Turn your document over to the back side, then tap continue to take a photo." })
2107
2446
  ] }),
2108
- /* @__PURE__ */ jsxs6("div", { style: styles.stepPillsRow, children: [
2109
- /* @__PURE__ */ jsx7("span", { style: {
2447
+ /* @__PURE__ */ jsxs7("div", { style: styles.stepPillsRow, children: [
2448
+ /* @__PURE__ */ jsx8("span", { style: {
2110
2449
  ...styles.stepPill,
2111
2450
  backgroundColor: "rgba(16,185,129,0.15)",
2112
2451
  color: colors.success
2113
2452
  }, children: "\u2713 Front" }),
2114
- /* @__PURE__ */ jsx7("span", { style: {
2453
+ /* @__PURE__ */ jsx8("span", { style: {
2115
2454
  ...styles.stepPill,
2116
2455
  backgroundColor: colors.teal,
2117
2456
  color: colors.white
2118
2457
  }, children: "Back" })
2119
2458
  ] })
2120
2459
  ] }),
2121
- /* @__PURE__ */ jsx7("div", { style: { padding: "24px" }, children: /* @__PURE__ */ jsx7("button", { style: styles.primaryButton, onClick: onContinue, children: "Continue" }) })
2460
+ /* @__PURE__ */ jsx8("div", { style: { padding: "24px" }, children: /* @__PURE__ */ jsx8("button", { style: styles.primaryButton, onClick: onContinue, children: "Continue" }) })
2122
2461
  ] });
2123
2462
  }
2124
2463
 
2125
2464
  // src/components/SelfieCaptureScreen.tsx
2126
- import { useRef as useRef3, useEffect as useEffect5, useState as useState4, useCallback as useCallback3 } from "react";
2127
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
2128
- function SelfieCaptureScreen({ onCapture, onCancel }) {
2129
- const videoRef = useRef3(null);
2130
- const canvasRef = useRef3(null);
2131
- const [stream, setStream] = useState4(null);
2132
- const [isCapturing, setIsCapturing] = useState4(false);
2133
- const [error, setError] = useState4(null);
2134
- const [capturedImage, setCapturedImage] = useState4(null);
2135
- const [capturedBlob, setCapturedBlob] = useState4(null);
2136
- useEffect5(() => {
2465
+ import { useRef as useRef4, useEffect as useEffect7, useState as useState6, useCallback as useCallback3 } from "react";
2466
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
2467
+ function SelfieCaptureScreen({ onCapture, onCancel, showVisualGuides = true }) {
2468
+ const videoRef = useRef4(null);
2469
+ const canvasRef = useRef4(null);
2470
+ const [stream, setStream] = useState6(null);
2471
+ const [isCapturing, setIsCapturing] = useState6(false);
2472
+ const [error, setError] = useState6(null);
2473
+ const [capturedImage, setCapturedImage] = useState6(null);
2474
+ const [capturedBlob, setCapturedBlob] = useState6(null);
2475
+ useEffect7(() => {
2137
2476
  injectKeyframes();
2138
2477
  }, []);
2139
- useEffect5(() => {
2478
+ useEffect7(() => {
2140
2479
  let mounted = true;
2141
2480
  async function startCamera() {
2142
2481
  try {
@@ -2158,7 +2497,7 @@ function SelfieCaptureScreen({ onCapture, onCancel }) {
2158
2497
  mounted = false;
2159
2498
  };
2160
2499
  }, [capturedImage]);
2161
- useEffect5(() => {
2500
+ useEffect7(() => {
2162
2501
  return () => {
2163
2502
  stream?.getTracks().forEach((t) => t.stop());
2164
2503
  };
@@ -2202,22 +2541,22 @@ function SelfieCaptureScreen({ onCapture, onCancel }) {
2202
2541
  }
2203
2542
  };
2204
2543
  if (error) {
2205
- return /* @__PURE__ */ jsx8("div", { style: styles.container, children: /* @__PURE__ */ jsxs7("div", { style: styles.errorContainer, children: [
2206
- /* @__PURE__ */ jsx8("p", { style: styles.errorText, children: error }),
2207
- /* @__PURE__ */ jsx8("button", { style: styles.primaryButton, onClick: onCancel, children: "Go Back" })
2544
+ return /* @__PURE__ */ jsx9("div", { style: styles.container, children: /* @__PURE__ */ jsxs8("div", { style: styles.errorContainer, children: [
2545
+ /* @__PURE__ */ jsx9("p", { style: styles.errorText, children: error }),
2546
+ /* @__PURE__ */ jsx9("button", { style: styles.primaryButton, onClick: onCancel, children: "Go Back" })
2208
2547
  ] }) });
2209
2548
  }
2210
2549
  if (capturedImage) {
2211
- return /* @__PURE__ */ jsxs7("div", { style: styles.darkContainer, children: [
2212
- /* @__PURE__ */ jsx8(StepProgressBar, { total: 5, current: 4, isDark: true }),
2213
- /* @__PURE__ */ jsxs7("div", { style: styles.darkScreenHeader, children: [
2214
- /* @__PURE__ */ jsx8("div", { style: { width: 40 } }),
2215
- /* @__PURE__ */ jsx8("h1", { style: styles.darkScreenTitle, children: "Does this look like you?" }),
2216
- /* @__PURE__ */ jsx8("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2550
+ return /* @__PURE__ */ jsxs8("div", { style: styles.darkContainer, children: [
2551
+ /* @__PURE__ */ jsx9(StepProgressBar, { total: 5, current: 4, isDark: true }),
2552
+ /* @__PURE__ */ jsxs8("div", { style: styles.darkScreenHeader, children: [
2553
+ /* @__PURE__ */ jsx9("div", { style: { width: 40 } }),
2554
+ /* @__PURE__ */ jsx9("h1", { style: styles.darkScreenTitle, children: "Does this look like you?" }),
2555
+ /* @__PURE__ */ jsx9("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2217
2556
  ] }),
2218
- /* @__PURE__ */ jsx8("p", { style: styles.darkScreenSubtitle, children: "Check clarity and lighting" }),
2219
- /* @__PURE__ */ jsx8("div", { style: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: "24px" }, children: /* @__PURE__ */ jsxs7("div", { style: { position: "relative" }, children: [
2220
- /* @__PURE__ */ jsx8("div", { style: { width: "240px", height: "300px", borderRadius: "50%", overflow: "hidden", border: `3px solid ${colors.teal}` }, children: /* @__PURE__ */ jsx8(
2557
+ /* @__PURE__ */ jsx9("p", { style: styles.darkScreenSubtitle, children: "Check clarity and lighting" }),
2558
+ /* @__PURE__ */ jsx9("div", { style: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: "24px" }, children: /* @__PURE__ */ jsxs8("div", { style: { position: "relative" }, children: [
2559
+ /* @__PURE__ */ jsx9("div", { style: { width: "240px", height: "300px", borderRadius: "50%", overflow: "hidden", border: `3px solid ${colors.teal}` }, children: /* @__PURE__ */ jsx9(
2221
2560
  "img",
2222
2561
  {
2223
2562
  src: capturedImage,
@@ -2225,31 +2564,32 @@ function SelfieCaptureScreen({ onCapture, onCancel }) {
2225
2564
  style: { width: "100%", height: "100%", objectFit: "cover" }
2226
2565
  }
2227
2566
  ) }),
2228
- /* @__PURE__ */ jsx8("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsx8("span", { style: styles.reviewBadge, children: "\u2713 Face detected" }) }),
2229
- /* @__PURE__ */ jsxs7("div", { style: styles.qualityChecks, children: [
2230
- /* @__PURE__ */ jsx8(QualityCheck2, { label: "Clear" }),
2231
- /* @__PURE__ */ jsx8(QualityCheck2, { label: "Centered" }),
2232
- /* @__PURE__ */ jsx8(QualityCheck2, { label: "Well-lit" })
2567
+ /* @__PURE__ */ jsx9("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsx9("span", { style: styles.reviewBadge, children: "\u2713 Face detected" }) }),
2568
+ /* @__PURE__ */ jsxs8("div", { style: styles.qualityChecks, children: [
2569
+ /* @__PURE__ */ jsx9(QualityCheck2, { label: "Clear" }),
2570
+ /* @__PURE__ */ jsx9(QualityCheck2, { label: "Centered" }),
2571
+ /* @__PURE__ */ jsx9(QualityCheck2, { label: "Well-lit" })
2233
2572
  ] })
2234
2573
  ] }) }),
2235
- /* @__PURE__ */ jsxs7("div", { style: styles.reviewButtonsRow, children: [
2236
- /* @__PURE__ */ jsx8("button", { style: { ...styles.darkOutlineButton, flex: 1 }, onClick: handleRetake, children: "Retake" }),
2237
- /* @__PURE__ */ jsx8("button", { style: { ...styles.primaryButton, flex: 1 }, onClick: handleAccept, children: "Use this" })
2574
+ /* @__PURE__ */ jsxs8("div", { style: styles.reviewButtonsRow, children: [
2575
+ /* @__PURE__ */ jsx9("button", { style: { ...styles.darkOutlineButton, flex: 1 }, onClick: handleRetake, children: "Retake" }),
2576
+ /* @__PURE__ */ jsx9("button", { style: { ...styles.primaryButton, flex: 1 }, onClick: handleAccept, children: "Use this" })
2238
2577
  ] })
2239
2578
  ] });
2240
2579
  }
2241
- return /* @__PURE__ */ jsxs7("div", { style: styles.captureContainer, children: [
2242
- /* @__PURE__ */ jsx8(StepProgressBar, { total: 5, current: 4, isDark: true }),
2243
- /* @__PURE__ */ jsxs7("div", { style: styles.darkScreenHeader, children: [
2244
- /* @__PURE__ */ jsx8("div", { style: { width: 40 } }),
2245
- /* @__PURE__ */ jsxs7("div", { style: { flex: 1, textAlign: "center" }, children: [
2246
- /* @__PURE__ */ jsx8("h1", { style: { ...styles.darkScreenTitle, margin: 0, fontSize: "24px", fontWeight: 700 }, children: "Face the camera" }),
2247
- /* @__PURE__ */ jsx8("p", { style: styles.darkScreenSubtitle, children: "Keep a neutral expression" })
2580
+ return /* @__PURE__ */ jsxs8("div", { style: styles.captureContainer, children: [
2581
+ /* @__PURE__ */ jsx9(StepProgressBar, { total: 5, current: 4, isDark: true }),
2582
+ /* @__PURE__ */ jsxs8("div", { style: styles.darkScreenHeader, children: [
2583
+ /* @__PURE__ */ jsx9("div", { style: { width: 40 } }),
2584
+ /* @__PURE__ */ jsxs8("div", { style: { flex: 1, textAlign: "center" }, children: [
2585
+ /* @__PURE__ */ jsx9("h1", { style: { ...styles.darkScreenTitle, margin: 0, fontSize: "24px", fontWeight: 700 }, children: "Face the camera" }),
2586
+ /* @__PURE__ */ jsx9("p", { style: styles.darkScreenSubtitle, children: "Keep a neutral expression" })
2248
2587
  ] }),
2249
- /* @__PURE__ */ jsx8("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2588
+ /* @__PURE__ */ jsx9("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2250
2589
  ] }),
2251
- /* @__PURE__ */ jsxs7("div", { style: styles.cameraContainer, children: [
2252
- /* @__PURE__ */ jsx8(
2590
+ showVisualGuides && /* @__PURE__ */ jsx9("div", { style: { display: "flex", justifyContent: "center", padding: "6px 0 0" }, children: /* @__PURE__ */ jsx9(VisualGuide, { kind: "selfie", size: 56 }) }),
2591
+ /* @__PURE__ */ jsxs8("div", { style: styles.cameraContainer, children: [
2592
+ /* @__PURE__ */ jsx9(
2253
2593
  "video",
2254
2594
  {
2255
2595
  ref: videoRef,
@@ -2259,10 +2599,10 @@ function SelfieCaptureScreen({ onCapture, onCancel }) {
2259
2599
  style: { ...styles.cameraVideo, transform: "scaleX(-1)" }
2260
2600
  }
2261
2601
  ),
2262
- /* @__PURE__ */ jsx8("div", { style: styles.selfieOverlay, children: /* @__PURE__ */ jsx8("div", { style: styles.faceGuide, children: /* @__PURE__ */ jsx8("div", { style: styles.rotatingRing }) }) }),
2263
- /* @__PURE__ */ jsx8("canvas", { ref: canvasRef, style: { display: "none" } })
2602
+ /* @__PURE__ */ jsx9("div", { style: styles.selfieOverlay, children: /* @__PURE__ */ jsx9("div", { style: styles.faceGuide, children: /* @__PURE__ */ jsx9("div", { style: styles.rotatingRing }) }) }),
2603
+ /* @__PURE__ */ jsx9("canvas", { ref: canvasRef, style: { display: "none" } })
2264
2604
  ] }),
2265
- /* @__PURE__ */ jsx8("div", { style: { textAlign: "center", padding: "8px 0" }, children: /* @__PURE__ */ jsxs7(
2605
+ /* @__PURE__ */ jsx9("div", { style: { textAlign: "center", padding: "8px 0" }, children: /* @__PURE__ */ jsxs8(
2266
2606
  "span",
2267
2607
  {
2268
2608
  style: {
@@ -2271,32 +2611,88 @@ function SelfieCaptureScreen({ onCapture, onCancel }) {
2271
2611
  color: colors.teal
2272
2612
  },
2273
2613
  children: [
2274
- /* @__PURE__ */ jsx8("span", { style: { ...styles.pulsingDot, backgroundColor: colors.teal } }),
2614
+ /* @__PURE__ */ jsx9("span", { style: { ...styles.pulsingDot, backgroundColor: colors.teal } }),
2275
2615
  "Position your face in the oval"
2276
2616
  ]
2277
2617
  }
2278
2618
  ) }),
2279
- /* @__PURE__ */ jsx8("div", { style: styles.captureFooter, children: /* @__PURE__ */ jsx8(
2619
+ /* @__PURE__ */ jsx9("div", { style: styles.captureFooter, children: /* @__PURE__ */ jsx9(
2280
2620
  "button",
2281
2621
  {
2282
2622
  style: { ...styles.captureButton, opacity: isCapturing ? 0.5 : 1 },
2283
2623
  onClick: handleCapture,
2284
2624
  disabled: isCapturing,
2285
- children: /* @__PURE__ */ jsx8("div", { style: styles.captureButtonInner })
2625
+ children: /* @__PURE__ */ jsx9("div", { style: styles.captureButtonInner })
2286
2626
  }
2287
2627
  ) })
2288
2628
  ] });
2289
2629
  }
2290
2630
  function QualityCheck2({ label }) {
2291
- return /* @__PURE__ */ jsxs7("div", { style: styles.qualityCheck, children: [
2292
- /* @__PURE__ */ jsx8("div", { style: styles.qualityCheckIcon, children: "\u2713" }),
2293
- /* @__PURE__ */ jsx8("span", { style: styles.qualityCheckLabel, children: label })
2631
+ return /* @__PURE__ */ jsxs8("div", { style: styles.qualityCheck, children: [
2632
+ /* @__PURE__ */ jsx9("div", { style: styles.qualityCheckIcon, children: "\u2713" }),
2633
+ /* @__PURE__ */ jsx9("span", { style: styles.qualityCheckLabel, children: label })
2294
2634
  ] });
2295
2635
  }
2296
2636
 
2297
2637
  // src/components/LivenessScreen.tsx
2298
- import { useEffect as useEffect6, useRef as useRef4, useState as useState5, useCallback as useCallback4 } from "react";
2299
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
2638
+ import { useEffect as useEffect9, useRef as useRef6, useState as useState8, useCallback as useCallback4 } from "react";
2639
+
2640
+ // src/hooks/useLivenessSignals.ts
2641
+ import { useEffect as useEffect8, useRef as useRef5, useState as useState7 } from "react";
2642
+ var FRAME_INTERVAL_MS2 = 250;
2643
+ function useLivenessSignals(videoRef) {
2644
+ const [signals, setSignals] = useState7({
2645
+ faceDetected: false,
2646
+ detectorActive: false
2647
+ });
2648
+ const detectorRef = useRef5(null);
2649
+ const intervalRef = useRef5(null);
2650
+ useEffect8(() => {
2651
+ let cancelled = false;
2652
+ async function setupDetector() {
2653
+ const NativeFaceDetector = typeof window !== "undefined" && "FaceDetector" in window ? window.FaceDetector : null;
2654
+ if (!NativeFaceDetector) {
2655
+ return;
2656
+ }
2657
+ try {
2658
+ const detector = new NativeFaceDetector({ fastMode: true, maxDetectedFaces: 1 });
2659
+ if (cancelled) return;
2660
+ detectorRef.current = detector;
2661
+ setSignals((prev) => ({ ...prev, detectorActive: true }));
2662
+ } catch {
2663
+ return;
2664
+ }
2665
+ intervalRef.current = setInterval(async () => {
2666
+ const detector = detectorRef.current;
2667
+ const video = videoRef.current;
2668
+ if (!detector || !video || video.readyState < 2) return;
2669
+ try {
2670
+ const faces = await detector.detect(video);
2671
+ if (cancelled) return;
2672
+ setSignals((prev) => {
2673
+ const next = faces.length > 0;
2674
+ if (prev.faceDetected === next) return prev;
2675
+ return { ...prev, faceDetected: next };
2676
+ });
2677
+ } catch {
2678
+ }
2679
+ }, FRAME_INTERVAL_MS2);
2680
+ }
2681
+ setupDetector();
2682
+ return () => {
2683
+ cancelled = true;
2684
+ if (intervalRef.current) {
2685
+ clearInterval(intervalRef.current);
2686
+ intervalRef.current = null;
2687
+ }
2688
+ detectorRef.current = null;
2689
+ };
2690
+ }, [videoRef]);
2691
+ return signals;
2692
+ }
2693
+
2694
+ // src/components/LivenessScreen.tsx
2695
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
2300
2696
  function LivenessScreen({
2301
2697
  session,
2302
2698
  currentChallenge,
@@ -2304,22 +2700,25 @@ function LivenessScreen({
2304
2700
  onChallengeComplete,
2305
2701
  onStart,
2306
2702
  onComplete,
2307
- onCancel
2703
+ onCancel,
2704
+ lastChallengeError,
2705
+ showVisualGuides = true
2308
2706
  }) {
2309
- const videoRef = useRef4(null);
2310
- const canvasRef = useRef4(null);
2311
- const [stream, setStream] = useState5(null);
2312
- const [cameraError, setCameraError] = useState5(null);
2313
- const [phase, setPhase] = useState5("preparing");
2314
- const [countdown, setCountdown] = useState5(3);
2315
- const [capturing, setCapturing] = useState5(false);
2316
- useEffect6(() => {
2707
+ const videoRef = useRef6(null);
2708
+ const canvasRef = useRef6(null);
2709
+ const [stream, setStream] = useState8(null);
2710
+ const livenessSignals = useLivenessSignals(videoRef);
2711
+ const [cameraError, setCameraError] = useState8(null);
2712
+ const [phase, setPhase] = useState8("preparing");
2713
+ const [countdown, setCountdown] = useState8(3);
2714
+ const [capturing, setCapturing] = useState8(false);
2715
+ useEffect9(() => {
2317
2716
  injectKeyframes();
2318
2717
  }, []);
2319
- useEffect6(() => {
2718
+ useEffect9(() => {
2320
2719
  if (!session) onStart();
2321
2720
  }, [session, onStart]);
2322
- useEffect6(() => {
2721
+ useEffect9(() => {
2323
2722
  let mounted = true;
2324
2723
  async function startCamera() {
2325
2724
  try {
@@ -2351,12 +2750,12 @@ function LivenessScreen({
2351
2750
  mounted = false;
2352
2751
  };
2353
2752
  }, []);
2354
- useEffect6(() => {
2753
+ useEffect9(() => {
2355
2754
  return () => {
2356
2755
  stream?.getTracks().forEach((t) => t.stop());
2357
2756
  };
2358
2757
  }, [stream]);
2359
- useEffect6(() => {
2758
+ useEffect9(() => {
2360
2759
  if (!currentChallenge) return;
2361
2760
  setPhase("preparing");
2362
2761
  setCountdown(3);
@@ -2384,7 +2783,7 @@ function LivenessScreen({
2384
2783
  0.85
2385
2784
  );
2386
2785
  }, [currentChallenge, capturing, onChallengeComplete]);
2387
- useEffect6(() => {
2786
+ useEffect9(() => {
2388
2787
  if (!currentChallenge || capturing) return;
2389
2788
  if (countdown === 0) {
2390
2789
  if (phase === "preparing") {
@@ -2398,32 +2797,32 @@ function LivenessScreen({
2398
2797
  const t = setTimeout(() => setCountdown((c) => c - 1), 1e3);
2399
2798
  return () => clearTimeout(t);
2400
2799
  }, [countdown, currentChallenge?.id, capturing, captureFrame, phase]);
2401
- useEffect6(() => {
2800
+ useEffect9(() => {
2402
2801
  if (session && !currentChallenge && completedChallenges > 0) {
2403
2802
  onComplete();
2404
2803
  }
2405
2804
  }, [session, currentChallenge, completedChallenges, onComplete]);
2406
2805
  if (cameraError) {
2407
- return /* @__PURE__ */ jsx9("div", { style: styles.darkContainer, children: /* @__PURE__ */ jsxs8("div", { style: styles.errorContainer, children: [
2408
- /* @__PURE__ */ jsx9("p", { style: styles.errorText, children: cameraError }),
2409
- /* @__PURE__ */ jsx9("button", { style: styles.primaryButton, onClick: onCancel, children: "Go back" })
2806
+ return /* @__PURE__ */ jsx10("div", { style: styles.darkContainer, children: /* @__PURE__ */ jsxs9("div", { style: styles.errorContainer, children: [
2807
+ /* @__PURE__ */ jsx10("p", { style: styles.errorText, children: cameraError }),
2808
+ /* @__PURE__ */ jsx10("button", { style: styles.primaryButton, onClick: onCancel, children: "Go back" })
2410
2809
  ] }) });
2411
2810
  }
2412
2811
  if (!session) {
2413
- return /* @__PURE__ */ jsx9("div", { style: styles.darkContainer, children: /* @__PURE__ */ jsxs8("div", { style: styles.loadingContainer, children: [
2414
- /* @__PURE__ */ jsx9("div", { style: styles.spinner }),
2415
- /* @__PURE__ */ jsx9("p", { style: { ...styles.loadingText, color: "rgba(255,255,255,0.6)" }, children: "Starting liveness check..." })
2812
+ return /* @__PURE__ */ jsx10("div", { style: styles.darkContainer, children: /* @__PURE__ */ jsxs9("div", { style: styles.loadingContainer, children: [
2813
+ /* @__PURE__ */ jsx10("div", { style: styles.spinner }),
2814
+ /* @__PURE__ */ jsx10("p", { style: { ...styles.loadingText, color: "rgba(255,255,255,0.6)" }, children: "Starting liveness check..." })
2416
2815
  ] }) });
2417
2816
  }
2418
2817
  const totalChallenges = session.challenges.length;
2419
- return /* @__PURE__ */ jsxs8("div", { style: styles.captureContainer, children: [
2420
- /* @__PURE__ */ jsx9(StepProgressBar, { total: 5, current: 5, isDark: true }),
2421
- /* @__PURE__ */ jsxs8("div", { style: styles.darkScreenHeader, children: [
2422
- /* @__PURE__ */ jsx9("div", { style: { width: 40 } }),
2423
- /* @__PURE__ */ jsx9("h1", { style: styles.darkScreenTitle, children: "Liveness Check" }),
2424
- /* @__PURE__ */ jsx9("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2818
+ return /* @__PURE__ */ jsxs9("div", { style: styles.captureContainer, children: [
2819
+ /* @__PURE__ */ jsx10(StepProgressBar, { total: 5, current: 5, isDark: true }),
2820
+ /* @__PURE__ */ jsxs9("div", { style: styles.darkScreenHeader, children: [
2821
+ /* @__PURE__ */ jsx10("div", { style: { width: 40 } }),
2822
+ /* @__PURE__ */ jsx10("h1", { style: styles.darkScreenTitle, children: "Liveness Check" }),
2823
+ /* @__PURE__ */ jsx10("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2425
2824
  ] }),
2426
- /* @__PURE__ */ jsxs8(
2825
+ /* @__PURE__ */ jsxs9(
2427
2826
  "div",
2428
2827
  {
2429
2828
  style: {
@@ -2436,8 +2835,8 @@ function LivenessScreen({
2436
2835
  padding: "16px 0"
2437
2836
  },
2438
2837
  children: [
2439
- /* @__PURE__ */ jsxs8("div", { style: { position: "relative" }, children: [
2440
- /* @__PURE__ */ jsx9(
2838
+ /* @__PURE__ */ jsxs9("div", { style: { position: "relative" }, children: [
2839
+ /* @__PURE__ */ jsx10(
2441
2840
  "div",
2442
2841
  {
2443
2842
  style: {
@@ -2448,7 +2847,7 @@ function LivenessScreen({
2448
2847
  backgroundColor: "#000",
2449
2848
  border: "3px solid rgba(255,255,255,0.2)"
2450
2849
  },
2451
- children: /* @__PURE__ */ jsx9(
2850
+ children: /* @__PURE__ */ jsx10(
2452
2851
  "video",
2453
2852
  {
2454
2853
  ref: videoRef,
@@ -2465,7 +2864,7 @@ function LivenessScreen({
2465
2864
  )
2466
2865
  }
2467
2866
  ),
2468
- /* @__PURE__ */ jsx9(
2867
+ /* @__PURE__ */ jsx10(
2469
2868
  "svg",
2470
2869
  {
2471
2870
  style: {
@@ -2477,7 +2876,7 @@ function LivenessScreen({
2477
2876
  width: "256",
2478
2877
  height: "316",
2479
2878
  viewBox: "0 0 256 316",
2480
- children: /* @__PURE__ */ jsx9(
2879
+ children: /* @__PURE__ */ jsx10(
2481
2880
  "ellipse",
2482
2881
  {
2483
2882
  cx: "128",
@@ -2493,9 +2892,30 @@ function LivenessScreen({
2493
2892
  }
2494
2893
  )
2495
2894
  }
2895
+ ),
2896
+ livenessSignals.detectorActive && /* @__PURE__ */ jsx10(
2897
+ "div",
2898
+ {
2899
+ style: {
2900
+ position: "absolute",
2901
+ bottom: "-32px",
2902
+ left: "50%",
2903
+ transform: "translateX(-50%)",
2904
+ padding: "6px 14px",
2905
+ borderRadius: "999px",
2906
+ fontSize: "12px",
2907
+ fontWeight: 600,
2908
+ color: livenessSignals.faceDetected ? colors.success : "rgba(255,255,255,0.55)",
2909
+ backgroundColor: livenessSignals.faceDetected ? "rgba(16,185,129,0.15)" : "rgba(255,255,255,0.06)",
2910
+ border: livenessSignals.faceDetected ? "1px solid rgba(16,185,129,0.4)" : "1px solid rgba(255,255,255,0.12)",
2911
+ transition: "all 200ms",
2912
+ whiteSpace: "nowrap"
2913
+ },
2914
+ children: livenessSignals.faceDetected ? "\u2713 Face detected" : "Position your face in the oval"
2915
+ }
2496
2916
  )
2497
2917
  ] }),
2498
- currentChallenge && /* @__PURE__ */ jsxs8(
2918
+ currentChallenge && /* @__PURE__ */ jsxs9(
2499
2919
  "div",
2500
2920
  {
2501
2921
  style: {
@@ -2508,7 +2928,14 @@ function LivenessScreen({
2508
2928
  transition: "background-color 200ms, border-color 200ms"
2509
2929
  },
2510
2930
  children: [
2511
- /* @__PURE__ */ jsx9(
2931
+ showVisualGuides && visualGuideForChallenge(currentChallenge.type) && /* @__PURE__ */ jsx10("div", { style: { display: "flex", justifyContent: "center", marginBottom: "12px" }, children: /* @__PURE__ */ jsx10(
2932
+ VisualGuide,
2933
+ {
2934
+ kind: visualGuideForChallenge(currentChallenge.type),
2935
+ size: 64
2936
+ }
2937
+ ) }),
2938
+ /* @__PURE__ */ jsx10(
2512
2939
  "p",
2513
2940
  {
2514
2941
  style: {
@@ -2522,7 +2949,7 @@ function LivenessScreen({
2522
2949
  children: capturing ? "Checking..." : phase === "preparing" ? "Get ready" : "Now \u2014 hold the pose"
2523
2950
  }
2524
2951
  ),
2525
- /* @__PURE__ */ jsx9(
2952
+ /* @__PURE__ */ jsx10(
2526
2953
  "h2",
2527
2954
  {
2528
2955
  style: {
@@ -2533,7 +2960,7 @@ function LivenessScreen({
2533
2960
  children: currentChallenge.instruction
2534
2961
  }
2535
2962
  ),
2536
- !capturing && /* @__PURE__ */ jsx9(
2963
+ !capturing && /* @__PURE__ */ jsx10(
2537
2964
  "p",
2538
2965
  {
2539
2966
  style: {
@@ -2545,16 +2972,33 @@ function LivenessScreen({
2545
2972
  },
2546
2973
  children: countdown
2547
2974
  }
2975
+ ),
2976
+ lastChallengeError && phase === "preparing" && !capturing && /* @__PURE__ */ jsx10(
2977
+ "p",
2978
+ {
2979
+ style: {
2980
+ margin: "14px 0 0",
2981
+ padding: "10px 12px",
2982
+ borderRadius: "10px",
2983
+ fontSize: "13px",
2984
+ fontWeight: 500,
2985
+ color: "#fca5a5",
2986
+ backgroundColor: "rgba(239,68,68,0.10)",
2987
+ border: "1px solid rgba(239,68,68,0.25)",
2988
+ lineHeight: 1.35
2989
+ },
2990
+ children: lastChallengeError
2991
+ }
2548
2992
  )
2549
2993
  ]
2550
2994
  }
2551
2995
  ),
2552
- /* @__PURE__ */ jsx9("canvas", { ref: canvasRef, style: { display: "none" } })
2996
+ /* @__PURE__ */ jsx10("canvas", { ref: canvasRef, style: { display: "none" } })
2553
2997
  ]
2554
2998
  }
2555
2999
  ),
2556
- /* @__PURE__ */ jsxs8("div", { style: { padding: "16px 0" }, children: [
2557
- /* @__PURE__ */ jsx9("div", { style: styles.progressDots, children: session.challenges.map((_, index) => /* @__PURE__ */ jsx9(
3000
+ /* @__PURE__ */ jsxs9("div", { style: { padding: "16px 0" }, children: [
3001
+ /* @__PURE__ */ jsx10("div", { style: styles.progressDots, children: session.challenges.map((_, index) => /* @__PURE__ */ jsx10(
2558
3002
  "div",
2559
3003
  {
2560
3004
  style: {
@@ -2564,7 +3008,7 @@ function LivenessScreen({
2564
3008
  },
2565
3009
  index
2566
3010
  )) }),
2567
- /* @__PURE__ */ jsxs8("p", { style: styles.progressText, children: [
3011
+ /* @__PURE__ */ jsxs9("p", { style: styles.progressText, children: [
2568
3012
  "Challenge ",
2569
3013
  Math.min(completedChallenges + 1, totalChallenges),
2570
3014
  " of",
@@ -2572,7 +3016,7 @@ function LivenessScreen({
2572
3016
  totalChallenges
2573
3017
  ] })
2574
3018
  ] }),
2575
- /* @__PURE__ */ jsx9("div", { style: { padding: "0 24px 24px", textAlign: "center" }, children: /* @__PURE__ */ jsx9(
3019
+ /* @__PURE__ */ jsx10("div", { style: { padding: "0 24px 24px", textAlign: "center" }, children: /* @__PURE__ */ jsx10(
2576
3020
  "p",
2577
3021
  {
2578
3022
  style: {
@@ -2587,35 +3031,35 @@ function LivenessScreen({
2587
3031
  }
2588
3032
 
2589
3033
  // src/components/ResultScreen.tsx
2590
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
3034
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2591
3035
  function ResultScreen({ verification, onDone, onRetry, resultPageMode, simplified, customMessages }) {
2592
3036
  const { status } = verification;
2593
3037
  const effectiveMode = resultPageMode ?? (simplified ? "simplified" : "detailed");
2594
3038
  if (effectiveMode === "simplified") {
2595
3039
  switch (status) {
2596
3040
  case "approved":
2597
- return /* @__PURE__ */ jsx10(SimplifiedSuccess, { onDone, customMessages });
3041
+ return /* @__PURE__ */ jsx11(SimplifiedSuccess, { onDone, customMessages });
2598
3042
  case "rejected":
2599
- return /* @__PURE__ */ jsx10(SimplifiedFailed, { onRetry: onRetry || onDone, customMessages });
3043
+ return /* @__PURE__ */ jsx11(SimplifiedFailed, { onRetry: onRetry || onDone, customMessages });
2600
3044
  case "review_required":
2601
- return /* @__PURE__ */ jsx10(SimplifiedReview, { verification, onDone, customMessages });
3045
+ return /* @__PURE__ */ jsx11(SimplifiedReview, { verification, onDone, customMessages });
2602
3046
  case "expired":
2603
- return /* @__PURE__ */ jsx10(SimplifiedFailed, { onRetry: onRetry || onDone, customMessages: { failedTitle: "Document Expired", failedMessage: "The document you submitted has expired. Please use a valid document." } });
3047
+ return /* @__PURE__ */ jsx11(SimplifiedFailed, { onRetry: onRetry || onDone, customMessages: { failedTitle: "Document Expired", failedMessage: "The document you submitted has expired. Please use a valid document." } });
2604
3048
  default:
2605
- return /* @__PURE__ */ jsx10(SimplifiedSuccess, { onDone, customMessages });
3049
+ return /* @__PURE__ */ jsx11(SimplifiedSuccess, { onDone, customMessages });
2606
3050
  }
2607
3051
  }
2608
3052
  switch (status) {
2609
3053
  case "approved":
2610
- return /* @__PURE__ */ jsx10(SuccessResult, { verification, onDone });
3054
+ return /* @__PURE__ */ jsx11(SuccessResult, { verification, onDone });
2611
3055
  case "rejected":
2612
- return /* @__PURE__ */ jsx10(RejectedResult, { verification, onRetry: onRetry || onDone });
3056
+ return /* @__PURE__ */ jsx11(RejectedResult, { verification, onRetry: onRetry || onDone });
2613
3057
  case "expired":
2614
- return /* @__PURE__ */ jsx10(ExpiredResult, { verification, onRetry: onRetry || onDone });
3058
+ return /* @__PURE__ */ jsx11(ExpiredResult, { verification, onRetry: onRetry || onDone });
2615
3059
  case "review_required":
2616
- return /* @__PURE__ */ jsx10(ManualReviewResult, { verification, onDone });
3060
+ return /* @__PURE__ */ jsx11(ManualReviewResult, { verification, onDone });
2617
3061
  default:
2618
- return /* @__PURE__ */ jsx10(SuccessResult, { verification, onDone });
3062
+ return /* @__PURE__ */ jsx11(SuccessResult, { verification, onDone });
2619
3063
  }
2620
3064
  }
2621
3065
  function SuccessResult({ verification, onDone }) {
@@ -2623,16 +3067,16 @@ function SuccessResult({ verification, onDone }) {
2623
3067
  verification.scores?.overall ?? 100 - (verification.riskScore ?? 16)
2624
3068
  );
2625
3069
  const metrics = computeScoreBreakdown(verification);
2626
- return /* @__PURE__ */ jsxs9("div", { style: styles.resultContainer, children: [
2627
- /* @__PURE__ */ jsxs9("div", { style: styles.resultContent, children: [
2628
- /* @__PURE__ */ jsx10(
3070
+ return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
3071
+ /* @__PURE__ */ jsxs10("div", { style: styles.resultContent, children: [
3072
+ /* @__PURE__ */ jsx11(
2629
3073
  "div",
2630
3074
  {
2631
3075
  style: {
2632
3076
  ...styles.resultIconOuterRing,
2633
3077
  backgroundColor: `${colors.success}15`
2634
3078
  },
2635
- children: /* @__PURE__ */ jsx10(
3079
+ children: /* @__PURE__ */ jsx11(
2636
3080
  "div",
2637
3081
  {
2638
3082
  style: {
@@ -2646,9 +3090,9 @@ function SuccessResult({ verification, onDone }) {
2646
3090
  )
2647
3091
  }
2648
3092
  ),
2649
- /* @__PURE__ */ jsx10("h1", { style: styles.resultTitle, children: "Verification approved" }),
2650
- /* @__PURE__ */ jsx10("p", { style: styles.resultSubtitle, children: "Your identity has been successfully verified." }),
2651
- /* @__PURE__ */ jsx10(
3093
+ /* @__PURE__ */ jsx11("h1", { style: styles.resultTitle, children: "Verification approved" }),
3094
+ /* @__PURE__ */ jsx11("p", { style: styles.resultSubtitle, children: "Your identity has been successfully verified." }),
3095
+ /* @__PURE__ */ jsx11(
2652
3096
  ScoreCard,
2653
3097
  {
2654
3098
  score,
@@ -2656,9 +3100,9 @@ function SuccessResult({ verification, onDone }) {
2656
3100
  gradient: `linear-gradient(135deg, ${colors.teal}, ${colors.tealDark})`
2657
3101
  }
2658
3102
  ),
2659
- metrics.map((m, i) => /* @__PURE__ */ jsx10(ScoreMetricRow, { ...m }, i))
3103
+ metrics.map((m, i) => /* @__PURE__ */ jsx11(ScoreMetricRow, { ...m }, i))
2660
3104
  ] }),
2661
- /* @__PURE__ */ jsx10("div", { style: styles.footer, children: /* @__PURE__ */ jsx10("button", { style: styles.primaryButton, onClick: onDone, children: "Done" }) })
3105
+ /* @__PURE__ */ jsx11("div", { style: styles.footer, children: /* @__PURE__ */ jsx11("button", { style: styles.primaryButton, onClick: onDone, children: "Done" }) })
2662
3106
  ] });
2663
3107
  }
2664
3108
  function RejectedResult({ verification, onRetry }) {
@@ -2666,16 +3110,16 @@ function RejectedResult({ verification, onRetry }) {
2666
3110
  verification.scores?.overall ?? 100 - (verification.riskScore ?? 58)
2667
3111
  );
2668
3112
  const metrics = computeScoreBreakdown(verification);
2669
- return /* @__PURE__ */ jsxs9("div", { style: styles.resultContainer, children: [
2670
- /* @__PURE__ */ jsxs9("div", { style: styles.resultContent, children: [
2671
- /* @__PURE__ */ jsx10(
3113
+ return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
3114
+ /* @__PURE__ */ jsxs10("div", { style: styles.resultContent, children: [
3115
+ /* @__PURE__ */ jsx11(
2672
3116
  "div",
2673
3117
  {
2674
3118
  style: {
2675
3119
  ...styles.resultIconOuterRing,
2676
3120
  backgroundColor: `${colors.error}15`
2677
3121
  },
2678
- children: /* @__PURE__ */ jsx10(
3122
+ children: /* @__PURE__ */ jsx11(
2679
3123
  "div",
2680
3124
  {
2681
3125
  style: {
@@ -2689,9 +3133,9 @@ function RejectedResult({ verification, onRetry }) {
2689
3133
  )
2690
3134
  }
2691
3135
  ),
2692
- /* @__PURE__ */ jsx10("h1", { style: styles.resultTitle, children: "Verification rejected" }),
2693
- /* @__PURE__ */ jsx10("p", { style: styles.resultSubtitle, children: "We could not verify your identity. Please try again with a valid document." }),
2694
- /* @__PURE__ */ jsx10(
3136
+ /* @__PURE__ */ jsx11("h1", { style: styles.resultTitle, children: "Verification rejected" }),
3137
+ /* @__PURE__ */ jsx11("p", { style: styles.resultSubtitle, children: "We could not verify your identity. Please try again with a valid document." }),
3138
+ /* @__PURE__ */ jsx11(
2695
3139
  ScoreCard,
2696
3140
  {
2697
3141
  score,
@@ -2699,15 +3143,15 @@ function RejectedResult({ verification, onRetry }) {
2699
3143
  gradient: `linear-gradient(135deg, ${colors.error}, #B91C1C)`
2700
3144
  }
2701
3145
  ),
2702
- metrics.map((m, i) => /* @__PURE__ */ jsx10(ScoreMetricRow, { ...m }, i))
3146
+ metrics.map((m, i) => /* @__PURE__ */ jsx11(ScoreMetricRow, { ...m }, i))
2703
3147
  ] }),
2704
- /* @__PURE__ */ jsx10("div", { style: styles.footer, children: /* @__PURE__ */ jsx10("button", { style: styles.primaryButton, onClick: onRetry, children: "Try again" }) })
3148
+ /* @__PURE__ */ jsx11("div", { style: styles.footer, children: /* @__PURE__ */ jsx11("button", { style: styles.primaryButton, onClick: onRetry, children: "Try again" }) })
2705
3149
  ] });
2706
3150
  }
2707
3151
  function ExpiredResult({ verification, onRetry }) {
2708
- return /* @__PURE__ */ jsxs9("div", { style: styles.resultContainer, children: [
2709
- /* @__PURE__ */ jsxs9("div", { style: styles.resultContent, children: [
2710
- /* @__PURE__ */ jsx10(
3152
+ return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
3153
+ /* @__PURE__ */ jsxs10("div", { style: styles.resultContent, children: [
3154
+ /* @__PURE__ */ jsx11(
2711
3155
  "div",
2712
3156
  {
2713
3157
  style: {
@@ -2715,7 +3159,7 @@ function ExpiredResult({ verification, onRetry }) {
2715
3159
  backgroundColor: `${colors.warning}15`,
2716
3160
  border: `2px solid ${colors.warning}30`
2717
3161
  },
2718
- children: /* @__PURE__ */ jsx10(
3162
+ children: /* @__PURE__ */ jsx11(
2719
3163
  "div",
2720
3164
  {
2721
3165
  style: {
@@ -2729,29 +3173,29 @@ function ExpiredResult({ verification, onRetry }) {
2729
3173
  )
2730
3174
  }
2731
3175
  ),
2732
- /* @__PURE__ */ jsx10("h1", { style: styles.resultTitle, children: "Document expired" }),
2733
- /* @__PURE__ */ jsx10("p", { style: styles.resultSubtitle, children: "The document you submitted has expired. Please use a valid, non-expired document." }),
2734
- verification.documentVerification && /* @__PURE__ */ jsxs9("div", { style: styles.expiryCard, children: [
2735
- /* @__PURE__ */ jsxs9("div", { style: styles.expiryRow, children: [
2736
- /* @__PURE__ */ jsx10("span", { style: styles.expiryLabel, children: "Document type" }),
2737
- /* @__PURE__ */ jsx10("span", { style: styles.expiryValue, children: verification.documentVerification.documentType || "ID Card" })
3176
+ /* @__PURE__ */ jsx11("h1", { style: styles.resultTitle, children: "Document expired" }),
3177
+ /* @__PURE__ */ jsx11("p", { style: styles.resultSubtitle, children: "The document you submitted has expired. Please use a valid, non-expired document." }),
3178
+ verification.documentVerification && /* @__PURE__ */ jsxs10("div", { style: styles.expiryCard, children: [
3179
+ /* @__PURE__ */ jsxs10("div", { style: styles.expiryRow, children: [
3180
+ /* @__PURE__ */ jsx11("span", { style: styles.expiryLabel, children: "Document type" }),
3181
+ /* @__PURE__ */ jsx11("span", { style: styles.expiryValue, children: verification.documentVerification.documentType || "ID Card" })
2738
3182
  ] }),
2739
- verification.documentVerification.issuingCountry && /* @__PURE__ */ jsxs9("div", { style: styles.expiryRow, children: [
2740
- /* @__PURE__ */ jsx10("span", { style: styles.expiryLabel, children: "Country" }),
2741
- /* @__PURE__ */ jsx10("span", { style: styles.expiryValue, children: verification.documentVerification.issuingCountry })
3183
+ verification.documentVerification.issuingCountry && /* @__PURE__ */ jsxs10("div", { style: styles.expiryRow, children: [
3184
+ /* @__PURE__ */ jsx11("span", { style: styles.expiryLabel, children: "Country" }),
3185
+ /* @__PURE__ */ jsx11("span", { style: styles.expiryValue, children: verification.documentVerification.issuingCountry })
2742
3186
  ] }),
2743
- verification.documentVerification.expirationDate && /* @__PURE__ */ jsxs9("div", { style: styles.expiryRow, children: [
2744
- /* @__PURE__ */ jsx10("span", { style: styles.expiryLabel, children: "Expired on" }),
2745
- /* @__PURE__ */ jsx10("span", { style: styles.expiryBadge, children: verification.documentVerification.expirationDate })
3187
+ verification.documentVerification.expirationDate && /* @__PURE__ */ jsxs10("div", { style: styles.expiryRow, children: [
3188
+ /* @__PURE__ */ jsx11("span", { style: styles.expiryLabel, children: "Expired on" }),
3189
+ /* @__PURE__ */ jsx11("span", { style: styles.expiryBadge, children: verification.documentVerification.expirationDate })
2746
3190
  ] })
2747
3191
  ] }),
2748
- /* @__PURE__ */ jsxs9("div", { style: { textAlign: "left" }, children: [
2749
- /* @__PURE__ */ jsx10(GuidanceTip, { number: 1, text: "Check the expiration date on your document" }),
2750
- /* @__PURE__ */ jsx10(GuidanceTip, { number: 2, text: "Use a different document that is currently valid" }),
2751
- /* @__PURE__ */ jsx10(GuidanceTip, { number: 3, text: "Ensure the document details are clearly visible" })
3192
+ /* @__PURE__ */ jsxs10("div", { style: { textAlign: "left" }, children: [
3193
+ /* @__PURE__ */ jsx11(GuidanceTip, { number: 1, text: "Check the expiration date on your document" }),
3194
+ /* @__PURE__ */ jsx11(GuidanceTip, { number: 2, text: "Use a different document that is currently valid" }),
3195
+ /* @__PURE__ */ jsx11(GuidanceTip, { number: 3, text: "Ensure the document details are clearly visible" })
2752
3196
  ] })
2753
3197
  ] }),
2754
- /* @__PURE__ */ jsx10("div", { style: styles.footer, children: /* @__PURE__ */ jsx10("button", { style: styles.primaryButton, onClick: onRetry, children: "Try with a valid document" }) })
3198
+ /* @__PURE__ */ jsx11("div", { style: styles.footer, children: /* @__PURE__ */ jsx11("button", { style: styles.primaryButton, onClick: onRetry, children: "Try with a valid document" }) })
2755
3199
  ] });
2756
3200
  }
2757
3201
  function ManualReviewResult({ verification, onDone }) {
@@ -2759,9 +3203,9 @@ function ManualReviewResult({ verification, onDone }) {
2759
3203
  verification.scores?.overall ?? 100 - (verification.riskScore ?? 32)
2760
3204
  );
2761
3205
  const metrics = computeScoreBreakdown(verification);
2762
- return /* @__PURE__ */ jsxs9("div", { style: styles.resultContainer, children: [
2763
- /* @__PURE__ */ jsxs9("div", { style: styles.resultContent, children: [
2764
- /* @__PURE__ */ jsx10(
3206
+ return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
3207
+ /* @__PURE__ */ jsxs10("div", { style: styles.resultContent, children: [
3208
+ /* @__PURE__ */ jsx11(
2765
3209
  "div",
2766
3210
  {
2767
3211
  style: {
@@ -2769,7 +3213,7 @@ function ManualReviewResult({ verification, onDone }) {
2769
3213
  backgroundColor: `${colors.info}15`,
2770
3214
  border: `2px solid ${colors.info}30`
2771
3215
  },
2772
- children: /* @__PURE__ */ jsx10(
3216
+ children: /* @__PURE__ */ jsx11(
2773
3217
  "div",
2774
3218
  {
2775
3219
  style: {
@@ -2783,9 +3227,9 @@ function ManualReviewResult({ verification, onDone }) {
2783
3227
  )
2784
3228
  }
2785
3229
  ),
2786
- /* @__PURE__ */ jsx10("h1", { style: styles.resultTitle, children: "Under review" }),
2787
- /* @__PURE__ */ jsx10("p", { style: styles.resultSubtitle, children: "Your verification requires manual review. We'll notify you of the result." }),
2788
- /* @__PURE__ */ jsx10(
3230
+ /* @__PURE__ */ jsx11("h1", { style: styles.resultTitle, children: "Under review" }),
3231
+ /* @__PURE__ */ jsx11("p", { style: styles.resultSubtitle, children: "Your verification requires manual review. We'll notify you of the result." }),
3232
+ /* @__PURE__ */ jsx11(
2789
3233
  ScoreCard,
2790
3234
  {
2791
3235
  score,
@@ -2793,21 +3237,21 @@ function ManualReviewResult({ verification, onDone }) {
2793
3237
  gradient: `linear-gradient(135deg, ${colors.info}, #0369A1)`
2794
3238
  }
2795
3239
  ),
2796
- metrics.map((m, i) => /* @__PURE__ */ jsx10(ScoreMetricRow, { ...m }, i))
3240
+ metrics.map((m, i) => /* @__PURE__ */ jsx11(ScoreMetricRow, { ...m }, i))
2797
3241
  ] }),
2798
- /* @__PURE__ */ jsx10("div", { style: styles.footer, children: /* @__PURE__ */ jsx10("button", { style: styles.primaryButton, onClick: onDone, children: "Got it" }) })
3242
+ /* @__PURE__ */ jsx11("div", { style: styles.footer, children: /* @__PURE__ */ jsx11("button", { style: styles.primaryButton, onClick: onDone, children: "Got it" }) })
2799
3243
  ] });
2800
3244
  }
2801
3245
  function GuidanceTip({ number, text }) {
2802
- return /* @__PURE__ */ jsxs9("div", { style: styles.guidanceTip, children: [
2803
- /* @__PURE__ */ jsx10("div", { style: styles.guidanceTipNumber, children: number }),
2804
- /* @__PURE__ */ jsx10("span", { style: styles.guidanceTipText, children: text })
3246
+ return /* @__PURE__ */ jsxs10("div", { style: styles.guidanceTip, children: [
3247
+ /* @__PURE__ */ jsx11("div", { style: styles.guidanceTipNumber, children: number }),
3248
+ /* @__PURE__ */ jsx11("span", { style: styles.guidanceTipText, children: text })
2805
3249
  ] });
2806
3250
  }
2807
3251
  function SimplifiedSuccess({ onDone, customMessages }) {
2808
- return /* @__PURE__ */ jsxs9("div", { style: styles.resultContainer, children: [
2809
- /* @__PURE__ */ jsxs9("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
2810
- /* @__PURE__ */ jsx10(
3252
+ return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
3253
+ /* @__PURE__ */ jsxs10("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
3254
+ /* @__PURE__ */ jsx11(
2811
3255
  "div",
2812
3256
  {
2813
3257
  style: {
@@ -2816,7 +3260,7 @@ function SimplifiedSuccess({ onDone, customMessages }) {
2816
3260
  width: 96,
2817
3261
  height: 96
2818
3262
  },
2819
- children: /* @__PURE__ */ jsx10(
3263
+ children: /* @__PURE__ */ jsx11(
2820
3264
  "div",
2821
3265
  {
2822
3266
  style: {
@@ -2833,16 +3277,16 @@ function SimplifiedSuccess({ onDone, customMessages }) {
2833
3277
  )
2834
3278
  }
2835
3279
  ),
2836
- /* @__PURE__ */ jsx10("h1", { style: { ...styles.resultTitle, fontSize: 24, marginTop: 16 }, children: customMessages?.successTitle || "Verification Successful" }),
2837
- /* @__PURE__ */ jsx10("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." })
3280
+ /* @__PURE__ */ jsx11("h1", { style: { ...styles.resultTitle, fontSize: 24, marginTop: 16 }, children: customMessages?.successTitle || "Verification Successful" }),
3281
+ /* @__PURE__ */ jsx11("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." })
2838
3282
  ] }),
2839
- /* @__PURE__ */ jsx10("div", { style: styles.footer, children: /* @__PURE__ */ jsx10("button", { style: styles.primaryButton, onClick: onDone, children: "Continue" }) })
3283
+ /* @__PURE__ */ jsx11("div", { style: styles.footer, children: /* @__PURE__ */ jsx11("button", { style: styles.primaryButton, onClick: onDone, children: "Continue" }) })
2840
3284
  ] });
2841
3285
  }
2842
3286
  function SimplifiedFailed({ onRetry, customMessages }) {
2843
- return /* @__PURE__ */ jsxs9("div", { style: styles.resultContainer, children: [
2844
- /* @__PURE__ */ jsxs9("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
2845
- /* @__PURE__ */ jsx10(
3287
+ return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
3288
+ /* @__PURE__ */ jsxs10("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
3289
+ /* @__PURE__ */ jsx11(
2846
3290
  "div",
2847
3291
  {
2848
3292
  style: {
@@ -2851,7 +3295,7 @@ function SimplifiedFailed({ onRetry, customMessages }) {
2851
3295
  width: 96,
2852
3296
  height: 96
2853
3297
  },
2854
- children: /* @__PURE__ */ jsx10(
3298
+ children: /* @__PURE__ */ jsx11(
2855
3299
  "div",
2856
3300
  {
2857
3301
  style: {
@@ -2868,16 +3312,16 @@ function SimplifiedFailed({ onRetry, customMessages }) {
2868
3312
  )
2869
3313
  }
2870
3314
  ),
2871
- /* @__PURE__ */ jsx10("h1", { style: { ...styles.resultTitle, fontSize: 24, marginTop: 16 }, children: customMessages?.failedTitle || "Verification Failed" }),
2872
- /* @__PURE__ */ jsx10("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." })
3315
+ /* @__PURE__ */ jsx11("h1", { style: { ...styles.resultTitle, fontSize: 24, marginTop: 16 }, children: customMessages?.failedTitle || "Verification Failed" }),
3316
+ /* @__PURE__ */ jsx11("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." })
2873
3317
  ] }),
2874
- /* @__PURE__ */ jsx10("div", { style: styles.footer, children: /* @__PURE__ */ jsx10("button", { style: styles.primaryButton, onClick: onRetry, children: "Try Again" }) })
3318
+ /* @__PURE__ */ jsx11("div", { style: styles.footer, children: /* @__PURE__ */ jsx11("button", { style: styles.primaryButton, onClick: onRetry, children: "Try Again" }) })
2875
3319
  ] });
2876
3320
  }
2877
3321
  function SimplifiedReview({ verification, onDone, customMessages }) {
2878
- return /* @__PURE__ */ jsxs9("div", { style: styles.resultContainer, children: [
2879
- /* @__PURE__ */ jsxs9("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
2880
- /* @__PURE__ */ jsx10(
3322
+ return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
3323
+ /* @__PURE__ */ jsxs10("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
3324
+ /* @__PURE__ */ jsx11(
2881
3325
  "div",
2882
3326
  {
2883
3327
  style: {
@@ -2886,7 +3330,7 @@ function SimplifiedReview({ verification, onDone, customMessages }) {
2886
3330
  width: 96,
2887
3331
  height: 96
2888
3332
  },
2889
- children: /* @__PURE__ */ jsx10(
3333
+ children: /* @__PURE__ */ jsx11(
2890
3334
  "div",
2891
3335
  {
2892
3336
  style: {
@@ -2903,9 +3347,9 @@ function SimplifiedReview({ verification, onDone, customMessages }) {
2903
3347
  )
2904
3348
  }
2905
3349
  ),
2906
- /* @__PURE__ */ jsx10("h1", { style: { ...styles.resultTitle, fontSize: 24, marginTop: 16 }, children: customMessages?.reviewTitle || "Verification Under Review" }),
2907
- /* @__PURE__ */ jsx10("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." }),
2908
- /* @__PURE__ */ jsxs9("div", { style: {
3350
+ /* @__PURE__ */ jsx11("h1", { style: { ...styles.resultTitle, fontSize: 24, marginTop: 16 }, children: customMessages?.reviewTitle || "Verification Under Review" }),
3351
+ /* @__PURE__ */ jsx11("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." }),
3352
+ /* @__PURE__ */ jsxs10("div", { style: {
2909
3353
  marginTop: 24,
2910
3354
  padding: "12px 24px",
2911
3355
  backgroundColor: `${colors.info}10`,
@@ -2913,27 +3357,27 @@ function SimplifiedReview({ verification, onDone, customMessages }) {
2913
3357
  border: `1px solid ${colors.info}30`,
2914
3358
  display: "inline-block"
2915
3359
  }, children: [
2916
- /* @__PURE__ */ jsx10("span", { style: { fontSize: 12, color: colors.textSecondary }, children: "Reference: " }),
2917
- /* @__PURE__ */ jsx10("span", { style: { fontSize: 14, fontWeight: 600, fontFamily: "monospace" }, children: verification.id.slice(0, 8) })
3360
+ /* @__PURE__ */ jsx11("span", { style: { fontSize: 12, color: colors.textSecondary }, children: "Reference: " }),
3361
+ /* @__PURE__ */ jsx11("span", { style: { fontSize: 14, fontWeight: 600, fontFamily: "monospace" }, children: verification.id.slice(0, 8) })
2918
3362
  ] })
2919
3363
  ] }),
2920
- /* @__PURE__ */ jsx10("div", { style: styles.footer, children: /* @__PURE__ */ jsx10("button", { style: styles.primaryButton, onClick: onDone, children: "Got It" }) })
3364
+ /* @__PURE__ */ jsx11("div", { style: styles.footer, children: /* @__PURE__ */ jsx11("button", { style: styles.primaryButton, onClick: onDone, children: "Got It" }) })
2921
3365
  ] });
2922
3366
  }
2923
3367
 
2924
3368
  // src/components/ErrorScreen.tsx
2925
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
3369
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2926
3370
  function ErrorScreen({ error, onRetry, onCancel }) {
2927
- return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
2928
- /* @__PURE__ */ jsxs10("div", { style: styles.resultContent, children: [
2929
- /* @__PURE__ */ jsx11(
3371
+ return /* @__PURE__ */ jsxs11("div", { style: styles.resultContainer, children: [
3372
+ /* @__PURE__ */ jsxs11("div", { style: styles.resultContent, children: [
3373
+ /* @__PURE__ */ jsx12(
2930
3374
  "div",
2931
3375
  {
2932
3376
  style: {
2933
3377
  ...styles.resultIconOuterRing,
2934
3378
  backgroundColor: colors.errorBg
2935
3379
  },
2936
- children: /* @__PURE__ */ jsx11(
3380
+ children: /* @__PURE__ */ jsx12(
2937
3381
  "div",
2938
3382
  {
2939
3383
  style: {
@@ -2947,26 +3391,26 @@ function ErrorScreen({ error, onRetry, onCancel }) {
2947
3391
  )
2948
3392
  }
2949
3393
  ),
2950
- /* @__PURE__ */ jsx11("h1", { style: styles.resultTitle, children: "Something went wrong" }),
2951
- /* @__PURE__ */ jsx11("p", { style: styles.resultSubtitle, children: error.message }),
2952
- error.recoverySuggestion && /* @__PURE__ */ jsx11("p", { style: { ...styles.bodyText, marginTop: "12px" }, children: error.recoverySuggestion })
3394
+ /* @__PURE__ */ jsx12("h1", { style: styles.resultTitle, children: "Something went wrong" }),
3395
+ /* @__PURE__ */ jsx12("p", { style: styles.resultSubtitle, children: error.message }),
3396
+ error.recoverySuggestion && /* @__PURE__ */ jsx12("p", { style: { ...styles.bodyText, marginTop: "12px" }, children: error.recoverySuggestion })
2953
3397
  ] }),
2954
- /* @__PURE__ */ jsxs10("div", { style: styles.footer, children: [
2955
- error.isRetryable && /* @__PURE__ */ jsx11("button", { style: styles.primaryButton, onClick: onRetry, children: "Try Again" }),
2956
- /* @__PURE__ */ jsx11("button", { style: styles.textButton, onClick: onCancel, children: "Cancel" })
3398
+ /* @__PURE__ */ jsxs11("div", { style: styles.footer, children: [
3399
+ error.isRetryable && /* @__PURE__ */ jsx12("button", { style: styles.primaryButton, onClick: onRetry, children: "Try Again" }),
3400
+ /* @__PURE__ */ jsx12("button", { style: styles.textButton, onClick: onCancel, children: "Cancel" })
2957
3401
  ] })
2958
3402
  ] });
2959
3403
  }
2960
3404
 
2961
3405
  // src/components/LoadingScreen.tsx
2962
- import { useEffect as useEffect7 } from "react";
2963
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
3406
+ import { useEffect as useEffect10 } from "react";
3407
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
2964
3408
  function LoadingScreen({ message = "Loading..." }) {
2965
- useEffect7(() => {
3409
+ useEffect10(() => {
2966
3410
  injectKeyframes();
2967
3411
  }, []);
2968
- return /* @__PURE__ */ jsx12("div", { style: styles.container, children: /* @__PURE__ */ jsxs11("div", { style: styles.loadingContainer, children: [
2969
- /* @__PURE__ */ jsx12(
3412
+ return /* @__PURE__ */ jsx13("div", { style: styles.container, children: /* @__PURE__ */ jsxs12("div", { style: styles.loadingContainer, children: [
3413
+ /* @__PURE__ */ jsx13(
2970
3414
  "div",
2971
3415
  {
2972
3416
  style: {
@@ -2983,16 +3427,19 @@ function LoadingScreen({ message = "Loading..." }) {
2983
3427
  children: "\u{1F6E1}\uFE0F"
2984
3428
  }
2985
3429
  ),
2986
- /* @__PURE__ */ jsx12("p", { style: styles.loadingText, children: message })
3430
+ /* @__PURE__ */ jsx13("p", { style: styles.loadingText, children: message })
2987
3431
  ] }) });
2988
3432
  }
2989
3433
 
2990
3434
  // src/components/VerificationFlow.tsx
2991
- import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
3435
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
2992
3436
  function VerificationFlow({
2993
3437
  externalId,
2994
3438
  tier = "standard",
2995
3439
  documentTypes,
3440
+ expectedFirstName,
3441
+ expectedLastName,
3442
+ showVisualGuides = true,
2996
3443
  onComplete,
2997
3444
  onError,
2998
3445
  onCancel,
@@ -3014,20 +3461,20 @@ function VerificationFlow({
3014
3461
  retry,
3015
3462
  sdk
3016
3463
  } = useKoraIDV();
3017
- const [selectedCountry, setSelectedCountry] = useState6(null);
3018
- const [flowStep, setFlowStep] = useState6("consent");
3019
- const [showFlipInstruction, setShowFlipInstruction] = useState6(true);
3020
- const [supportedCountries, setSupportedCountries] = useState6([]);
3021
- const [countriesLoading, setCountriesLoading] = useState6(false);
3022
- useEffect8(() => {
3464
+ const [selectedCountry, setSelectedCountry] = useState9(null);
3465
+ const [flowStep, setFlowStep] = useState9("consent");
3466
+ const [showFlipInstruction, setShowFlipInstruction] = useState9(true);
3467
+ const [supportedCountries, setSupportedCountries] = useState9([]);
3468
+ const [countriesLoading, setCountriesLoading] = useState9(false);
3469
+ useEffect11(() => {
3023
3470
  if (state.step === "document_front") {
3024
3471
  setShowFlipInstruction(true);
3025
3472
  }
3026
3473
  }, [state.step]);
3027
- useEffect8(() => {
3028
- startVerification(externalId, tier);
3029
- }, [externalId, tier, startVerification]);
3030
- useEffect8(() => {
3474
+ useEffect11(() => {
3475
+ startVerification(externalId, tier, expectedFirstName, expectedLastName);
3476
+ }, [externalId, tier, expectedFirstName, expectedLastName, startVerification]);
3477
+ useEffect11(() => {
3031
3478
  if (state.error && onError) {
3032
3479
  onError(state.error);
3033
3480
  }
@@ -3073,19 +3520,19 @@ function VerificationFlow({
3073
3520
  ...style
3074
3521
  };
3075
3522
  if (state.error) {
3076
- return /* @__PURE__ */ jsx13("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx13(ErrorScreen, { error: state.error, onRetry: retry, onCancel: handleCancel }) });
3523
+ return /* @__PURE__ */ jsx14("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx14(ErrorScreen, { error: state.error, onRetry: retry, onCancel: handleCancel }) });
3077
3524
  }
3078
3525
  if (state.isLoading && state.step !== "processing") {
3079
- return /* @__PURE__ */ jsx13("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx13(LoadingScreen, {}) });
3526
+ return /* @__PURE__ */ jsx14("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx14(LoadingScreen, {}) });
3080
3527
  }
3081
3528
  if (flowStep === "consent" && state.step === "consent") {
3082
- return /* @__PURE__ */ jsx13("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx13(ConsentScreen, { onAccept: handleAcceptConsent, onDecline: handleCancel }) });
3529
+ return /* @__PURE__ */ jsx14("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx14(ConsentScreen, { onAccept: handleAcceptConsent, onDecline: handleCancel }) });
3083
3530
  }
3084
3531
  if (flowStep === "country_selection") {
3085
3532
  if (countriesLoading) {
3086
- return /* @__PURE__ */ jsx13("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx13(LoadingScreen, {}) });
3533
+ return /* @__PURE__ */ jsx14("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx14(LoadingScreen, {}) });
3087
3534
  }
3088
- return /* @__PURE__ */ jsx13("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx13(
3535
+ return /* @__PURE__ */ jsx14("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx14(
3089
3536
  CountrySelectionScreen,
3090
3537
  {
3091
3538
  countries: supportedCountries,
@@ -3094,8 +3541,8 @@ function VerificationFlow({
3094
3541
  }
3095
3542
  ) });
3096
3543
  }
3097
- return /* @__PURE__ */ jsxs12("div", { className, style: containerStyle, children: [
3098
- state.step === "document_selection" && /* @__PURE__ */ jsx13(
3544
+ return /* @__PURE__ */ jsxs13("div", { className, style: containerStyle, children: [
3545
+ state.step === "document_selection" && /* @__PURE__ */ jsx14(
3099
3546
  DocumentSelectionScreen,
3100
3547
  {
3101
3548
  documentTypes,
@@ -3104,32 +3551,41 @@ function VerificationFlow({
3104
3551
  onCancel: handleCancel
3105
3552
  }
3106
3553
  ),
3107
- state.step === "document_front" && /* @__PURE__ */ jsx13(
3554
+ state.step === "document_front" && /* @__PURE__ */ jsx14(
3108
3555
  DocumentCaptureScreen,
3109
3556
  {
3110
3557
  side: "front",
3111
3558
  onQualityCheck: (blob) => checkDocumentQuality(blob),
3112
3559
  onCapture: (imageData) => uploadDocument(imageData, "front", selectedCountry?.id),
3113
- onCancel: handleCancel
3560
+ onCancel: handleCancel,
3561
+ showVisualGuides
3114
3562
  }
3115
3563
  ),
3116
- state.step === "document_back" && showFlipInstruction && /* @__PURE__ */ jsx13(
3564
+ state.step === "document_back" && showFlipInstruction && /* @__PURE__ */ jsx14(
3117
3565
  FlipDocumentScreen,
3118
3566
  {
3119
3567
  onContinue: () => setShowFlipInstruction(false),
3120
3568
  onCancel: handleCancel
3121
3569
  }
3122
3570
  ),
3123
- state.step === "document_back" && !showFlipInstruction && /* @__PURE__ */ jsx13(
3571
+ state.step === "document_back" && !showFlipInstruction && /* @__PURE__ */ jsx14(
3124
3572
  DocumentCaptureScreen,
3125
3573
  {
3126
3574
  side: "back",
3127
3575
  onCapture: (imageData) => uploadDocument(imageData, "back", selectedCountry?.id),
3128
- onCancel: handleCancel
3576
+ onCancel: handleCancel,
3577
+ showVisualGuides
3129
3578
  }
3130
3579
  ),
3131
- state.step === "selfie" && /* @__PURE__ */ jsx13(SelfieCaptureScreen, { onCapture: uploadSelfie, onCancel: handleCancel }),
3132
- state.step === "liveness" && /* @__PURE__ */ jsx13(
3580
+ state.step === "selfie" && /* @__PURE__ */ jsx14(
3581
+ SelfieCaptureScreen,
3582
+ {
3583
+ onCapture: uploadSelfie,
3584
+ onCancel: handleCancel,
3585
+ showVisualGuides
3586
+ }
3587
+ ),
3588
+ state.step === "liveness" && /* @__PURE__ */ jsx14(
3133
3589
  LivenessScreen,
3134
3590
  {
3135
3591
  session: state.livenessSession,
@@ -3138,20 +3594,13 @@ function VerificationFlow({
3138
3594
  onChallengeComplete: submitChallenge,
3139
3595
  onStart: startLiveness,
3140
3596
  onComplete: complete,
3141
- onCancel: handleCancel
3597
+ onCancel: handleCancel,
3598
+ lastChallengeError: state.lastChallengeError,
3599
+ showVisualGuides
3142
3600
  }
3143
3601
  ),
3144
- state.step === "processing" && /* @__PURE__ */ jsx13(
3145
- ProcessingScreen,
3146
- {
3147
- steps: [
3148
- { label: "Document analyzed", status: "done" },
3149
- { label: "Checking face match", status: "active" },
3150
- { label: "Finalizing results", status: "pending" }
3151
- ]
3152
- }
3153
- ),
3154
- state.step === "complete" && state.verification && /* @__PURE__ */ jsx13(
3602
+ state.step === "processing" && /* @__PURE__ */ jsx14(ProcessingScreen, {}),
3603
+ state.step === "complete" && state.verification && /* @__PURE__ */ jsx14(
3155
3604
  ResultScreen,
3156
3605
  {
3157
3606
  verification: state.verification,
@@ -3163,8 +3612,8 @@ function VerificationFlow({
3163
3612
  }
3164
3613
 
3165
3614
  // src/components/QrHandoffScreen.tsx
3166
- import { useEffect as useEffect9, useState as useState7, useRef as useRef5 } from "react";
3167
- import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
3615
+ import { useEffect as useEffect12, useState as useState10, useRef as useRef7 } from "react";
3616
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
3168
3617
  function QrHandoffScreen({
3169
3618
  session,
3170
3619
  onMobileCaptureComplete,
@@ -3173,11 +3622,11 @@ function QrHandoffScreen({
3173
3622
  onRefresh,
3174
3623
  eventSource
3175
3624
  }) {
3176
- const [timeLeft, setTimeLeft] = useState7(session.expiresIn);
3177
- const [scanned, setScanned] = useState7(false);
3178
- const [expired, setExpired] = useState7(false);
3179
- const timerRef = useRef5();
3180
- useEffect9(() => {
3625
+ const [timeLeft, setTimeLeft] = useState10(session.expiresIn);
3626
+ const [scanned, setScanned] = useState10(false);
3627
+ const [expired, setExpired] = useState10(false);
3628
+ const timerRef = useRef7();
3629
+ useEffect12(() => {
3181
3630
  setTimeLeft(session.expiresIn);
3182
3631
  setExpired(false);
3183
3632
  setScanned(false);
@@ -3194,7 +3643,7 @@ function QrHandoffScreen({
3194
3643
  }, 1e3);
3195
3644
  return () => clearInterval(timerRef.current);
3196
3645
  }, [session.token]);
3197
- useEffect9(() => {
3646
+ useEffect12(() => {
3198
3647
  if (!eventSource) return;
3199
3648
  const handleStatus = (event) => {
3200
3649
  try {
@@ -3220,34 +3669,34 @@ function QrHandoffScreen({
3220
3669
  const seconds = timeLeft % 60;
3221
3670
  const qrSize = 200;
3222
3671
  if (expired) {
3223
- return /* @__PURE__ */ jsx14("div", { style: qrStyles.container, children: /* @__PURE__ */ jsxs13("div", { style: qrStyles.content, children: [
3224
- /* @__PURE__ */ jsx14("div", { style: qrStyles.expiredIcon, children: "\u23F1" }),
3225
- /* @__PURE__ */ jsx14("h2", { style: qrStyles.title, children: "QR Code Expired" }),
3226
- /* @__PURE__ */ jsx14("p", { style: qrStyles.subtitle, children: "The QR code has expired. Generate a new one to continue." }),
3227
- /* @__PURE__ */ jsx14("button", { style: qrStyles.primaryButton, onClick: onRefresh, children: "Generate New QR Code" }),
3228
- /* @__PURE__ */ jsx14("button", { style: qrStyles.secondaryButton, onClick: onContinueOnDevice, children: "Continue on this device instead" })
3672
+ return /* @__PURE__ */ jsx15("div", { style: qrStyles.container, children: /* @__PURE__ */ jsxs14("div", { style: qrStyles.content, children: [
3673
+ /* @__PURE__ */ jsx15("div", { style: qrStyles.expiredIcon, children: "\u23F1" }),
3674
+ /* @__PURE__ */ jsx15("h2", { style: qrStyles.title, children: "QR Code Expired" }),
3675
+ /* @__PURE__ */ jsx15("p", { style: qrStyles.subtitle, children: "The QR code has expired. Generate a new one to continue." }),
3676
+ /* @__PURE__ */ jsx15("button", { style: qrStyles.primaryButton, onClick: onRefresh, children: "Generate New QR Code" }),
3677
+ /* @__PURE__ */ jsx15("button", { style: qrStyles.secondaryButton, onClick: onContinueOnDevice, children: "Continue on this device instead" })
3229
3678
  ] }) });
3230
3679
  }
3231
3680
  if (scanned) {
3232
- return /* @__PURE__ */ jsx14("div", { style: qrStyles.container, children: /* @__PURE__ */ jsxs13("div", { style: qrStyles.content, children: [
3233
- /* @__PURE__ */ jsx14("div", { style: qrStyles.spinnerContainer, children: /* @__PURE__ */ jsx14("div", { style: qrStyles.spinner }) }),
3234
- /* @__PURE__ */ jsx14("h2", { style: qrStyles.title, children: "Capturing on your phone..." }),
3235
- /* @__PURE__ */ jsx14("p", { style: qrStyles.subtitle, children: "Complete the document scan and selfie on your mobile device. This page will update automatically when done." }),
3236
- /* @__PURE__ */ jsxs13("div", { style: qrStyles.statusBadge, children: [
3237
- /* @__PURE__ */ jsx14("span", { style: qrStyles.statusDot }),
3681
+ return /* @__PURE__ */ jsx15("div", { style: qrStyles.container, children: /* @__PURE__ */ jsxs14("div", { style: qrStyles.content, children: [
3682
+ /* @__PURE__ */ jsx15("div", { style: qrStyles.spinnerContainer, children: /* @__PURE__ */ jsx15("div", { style: qrStyles.spinner }) }),
3683
+ /* @__PURE__ */ jsx15("h2", { style: qrStyles.title, children: "Capturing on your phone..." }),
3684
+ /* @__PURE__ */ jsx15("p", { style: qrStyles.subtitle, children: "Complete the document scan and selfie on your mobile device. This page will update automatically when done." }),
3685
+ /* @__PURE__ */ jsxs14("div", { style: qrStyles.statusBadge, children: [
3686
+ /* @__PURE__ */ jsx15("span", { style: qrStyles.statusDot }),
3238
3687
  "Connected \u2014 waiting for capture"
3239
3688
  ] })
3240
3689
  ] }) });
3241
3690
  }
3242
- return /* @__PURE__ */ jsx14("div", { style: qrStyles.container, children: /* @__PURE__ */ jsxs13("div", { style: qrStyles.content, children: [
3243
- /* @__PURE__ */ jsx14("h2", { style: qrStyles.title, children: "Scan with your phone" }),
3244
- /* @__PURE__ */ jsx14("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." }),
3245
- /* @__PURE__ */ jsxs13("div", { style: qrStyles.qrContainer, children: [
3246
- /* @__PURE__ */ jsx14("div", { style: {
3691
+ return /* @__PURE__ */ jsx15("div", { style: qrStyles.container, children: /* @__PURE__ */ jsxs14("div", { style: qrStyles.content, children: [
3692
+ /* @__PURE__ */ jsx15("h2", { style: qrStyles.title, children: "Scan with your phone" }),
3693
+ /* @__PURE__ */ jsx15("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." }),
3694
+ /* @__PURE__ */ jsxs14("div", { style: qrStyles.qrContainer, children: [
3695
+ /* @__PURE__ */ jsx15("div", { style: {
3247
3696
  ...qrStyles.qrBox,
3248
3697
  width: qrSize,
3249
3698
  height: qrSize
3250
- }, children: /* @__PURE__ */ jsx14(
3699
+ }, children: /* @__PURE__ */ jsx15(
3251
3700
  "img",
3252
3701
  {
3253
3702
  src: `https://api.qrserver.com/v1/create-qr-code/?size=${qrSize}x${qrSize}&data=${encodeURIComponent(session.captureUrl)}&margin=8`,
@@ -3257,26 +3706,26 @@ function QrHandoffScreen({
3257
3706
  style: { borderRadius: 12 }
3258
3707
  }
3259
3708
  ) }),
3260
- /* @__PURE__ */ jsxs13("div", { style: qrStyles.timer, children: [
3709
+ /* @__PURE__ */ jsxs14("div", { style: qrStyles.timer, children: [
3261
3710
  minutes,
3262
3711
  ":",
3263
3712
  seconds.toString().padStart(2, "0"),
3264
3713
  " remaining"
3265
3714
  ] })
3266
3715
  ] }),
3267
- /* @__PURE__ */ jsxs13("div", { style: qrStyles.steps, children: [
3268
- /* @__PURE__ */ jsx14(Step, { number: 1, text: "Open your phone's camera" }),
3269
- /* @__PURE__ */ jsx14(Step, { number: 2, text: "Point at the QR code" }),
3270
- /* @__PURE__ */ jsx14(Step, { number: 3, text: "Complete the capture on your phone" })
3716
+ /* @__PURE__ */ jsxs14("div", { style: qrStyles.steps, children: [
3717
+ /* @__PURE__ */ jsx15(Step, { number: 1, text: "Open your phone's camera" }),
3718
+ /* @__PURE__ */ jsx15(Step, { number: 2, text: "Point at the QR code" }),
3719
+ /* @__PURE__ */ jsx15(Step, { number: 3, text: "Complete the capture on your phone" })
3271
3720
  ] }),
3272
- /* @__PURE__ */ jsx14("div", { style: qrStyles.divider, children: /* @__PURE__ */ jsx14("span", { style: qrStyles.dividerText, children: "or" }) }),
3273
- /* @__PURE__ */ jsx14("button", { style: qrStyles.secondaryButton, onClick: onContinueOnDevice, children: "Continue on this device" })
3721
+ /* @__PURE__ */ jsx15("div", { style: qrStyles.divider, children: /* @__PURE__ */ jsx15("span", { style: qrStyles.dividerText, children: "or" }) }),
3722
+ /* @__PURE__ */ jsx15("button", { style: qrStyles.secondaryButton, onClick: onContinueOnDevice, children: "Continue on this device" })
3274
3723
  ] }) });
3275
3724
  }
3276
3725
  function Step({ number, text }) {
3277
- return /* @__PURE__ */ jsxs13("div", { style: qrStyles.step, children: [
3278
- /* @__PURE__ */ jsx14("div", { style: qrStyles.stepNumber, children: number }),
3279
- /* @__PURE__ */ jsx14("span", { style: qrStyles.stepText, children: text })
3726
+ return /* @__PURE__ */ jsxs14("div", { style: qrStyles.step, children: [
3727
+ /* @__PURE__ */ jsx15("div", { style: qrStyles.stepNumber, children: number }),
3728
+ /* @__PURE__ */ jsx15("span", { style: qrStyles.stepText, children: text })
3280
3729
  ] });
3281
3730
  }
3282
3731
  var qrStyles = {