@koraidv/react 1.7.11 → 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,7 +47,8 @@ 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);
@@ -246,14 +247,20 @@ function useKoraIDV() {
246
247
  ...prev,
247
248
  completedChallenges: nextIndex,
248
249
  currentChallenge: nextChallenge,
249
- isLoading: false
250
+ isLoading: false,
251
+ // Clear any prior retake message — the user just succeeded.
252
+ lastChallengeError: null
250
253
  }));
251
254
  if (!nextChallenge) {
252
255
  setState((prev) => ({ ...prev, step: "processing" }));
253
256
  }
254
257
  return true;
255
258
  }
256
- setState((prev) => ({ ...prev, isLoading: false }));
259
+ setState((prev) => ({
260
+ ...prev,
261
+ isLoading: false,
262
+ lastChallengeError: retakeMessageForChallenge(currentChallenge.type)
263
+ }));
257
264
  return false;
258
265
  } catch (error) {
259
266
  setState((prev) => ({
@@ -304,7 +311,8 @@ function useKoraIDV() {
304
311
  currentChallenge: null,
305
312
  completedChallenges: 0,
306
313
  isLoading: false,
307
- error: null
314
+ error: null,
315
+ lastChallengeError: null
308
316
  });
309
317
  }, [sdk]);
310
318
  const retry = useCallback(() => {
@@ -331,9 +339,27 @@ function useKoraIDV() {
331
339
  sdk
332
340
  };
333
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
+ }
334
360
 
335
361
  // src/components/VerificationFlow.tsx
336
- import { useEffect as useEffect8, useState as useState6 } from "react";
362
+ import { useEffect as useEffect11, useState as useState9 } from "react";
337
363
  import { KoraError as KoraError2, KoraErrorCode } from "@koraidv/core";
338
364
 
339
365
  // src/components/styles.ts
@@ -400,6 +426,36 @@ function injectKeyframes() {
400
426
  from { opacity: 0; transform: translateY(8px); }
401
427
  to { opacity: 1; transform: translateY(0); }
402
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
+ }
403
459
  `;
404
460
  document.head.appendChild(style);
405
461
  }
@@ -1313,7 +1369,7 @@ var styles = {
1313
1369
  };
1314
1370
 
1315
1371
  // src/components/DesignSystem.tsx
1316
- import { useEffect as useEffect2 } from "react";
1372
+ import { useEffect as useEffect2, useState as useState2 } from "react";
1317
1373
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
1318
1374
  function StepProgressBar({ total, current, isDark = false }) {
1319
1375
  return /* @__PURE__ */ jsx2("div", { style: styles.progressBar, children: Array.from({ length: total }).map((_, i) => /* @__PURE__ */ jsx2(
@@ -1386,10 +1442,26 @@ function ScoreMetricRow({ label, score, icon, status, message }) {
1386
1442
  }
1387
1443
  );
1388
1444
  }
1389
- 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 }) {
1390
1451
  useEffect2(() => {
1391
1452
  injectKeyframes();
1392
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
+ }));
1393
1465
  return /* @__PURE__ */ jsxs("div", { style: styles.processingContainer, children: [
1394
1466
  /* @__PURE__ */ jsxs("div", { style: styles.spinnerContainer, children: [
1395
1467
  /* @__PURE__ */ jsx2(
@@ -1440,7 +1512,7 @@ function ProcessingScreen({ steps }) {
1440
1512
  }
1441
1513
  )
1442
1514
  ] }),
1443
- /* @__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: [
1444
1516
  /* @__PURE__ */ jsx2(
1445
1517
  "div",
1446
1518
  {
@@ -1608,11 +1680,11 @@ function ConsentItem({
1608
1680
  }
1609
1681
 
1610
1682
  // src/components/CountrySelectionScreen.tsx
1611
- import { useState as useState2, useMemo as useMemo2 } from "react";
1683
+ import { useState as useState3, useMemo as useMemo2 } from "react";
1612
1684
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1613
1685
  function CountrySelectionScreen({ countries, onSelect, onCancel }) {
1614
- const [selected, setSelected] = useState2(null);
1615
- const [searchQuery, setSearchQuery] = useState2("");
1686
+ const [selected, setSelected] = useState3(null);
1687
+ const [searchQuery, setSearchQuery] = useState3("");
1616
1688
  const filteredCountries = useMemo2(() => {
1617
1689
  const countryList = countries || [];
1618
1690
  if (!searchQuery.trim()) return countryList;
@@ -1762,8 +1834,243 @@ function getIcon(type) {
1762
1834
  }
1763
1835
 
1764
1836
  // src/components/DocumentCaptureScreen.tsx
1765
- import { useRef as useRef2, useEffect as useEffect3, useState as useState3, useCallback as useCallback2 } from "react";
1766
- 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";
1767
2074
  var qualityIssueMessages = {
1768
2075
  face_blurred: "Photo on document is blurry. Retake in better lighting.",
1769
2076
  low_resolution: "Image quality too low. Move closer to document.",
@@ -1780,23 +2087,25 @@ function DocumentCaptureScreen({
1780
2087
  requiresBack = true,
1781
2088
  onQualityCheck,
1782
2089
  onCapture,
1783
- onCancel
2090
+ onCancel,
2091
+ showVisualGuides = true
1784
2092
  }) {
1785
- const videoRef = useRef2(null);
1786
- const canvasRef = useRef2(null);
1787
- const guideRef = useRef2(null);
1788
- const [stream, setStream] = useState3(null);
1789
- const [isCapturing, setIsCapturing] = useState3(false);
1790
- const [error, setError] = useState3(null);
1791
- const [capturedImage, setCapturedImage] = useState3(null);
1792
- const [capturedBlob, setCapturedBlob] = useState3(null);
1793
- const [qualityResult, setQualityResult] = useState3(null);
1794
- const [isCheckingQuality, setIsCheckingQuality] = useState3(false);
1795
- const [retakeCount, setRetakeCount] = useState3(0);
1796
- 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(() => {
1797
2106
  injectKeyframes();
1798
2107
  }, []);
1799
- useEffect3(() => {
2108
+ useEffect5(() => {
1800
2109
  let mounted = true;
1801
2110
  async function startCamera() {
1802
2111
  try {
@@ -1818,7 +2127,7 @@ function DocumentCaptureScreen({
1818
2127
  mounted = false;
1819
2128
  };
1820
2129
  }, [capturedImage]);
1821
- useEffect3(() => {
2130
+ useEffect5(() => {
1822
2131
  return () => {
1823
2132
  stream?.getTracks().forEach((t) => t.stop());
1824
2133
  };
@@ -1911,24 +2220,24 @@ function DocumentCaptureScreen({
1911
2220
  }
1912
2221
  };
1913
2222
  if (error) {
1914
- return /* @__PURE__ */ jsx6("div", { style: styles.container, children: /* @__PURE__ */ jsxs5("div", { style: styles.errorContainer, children: [
1915
- /* @__PURE__ */ jsx6("p", { style: styles.errorText, children: error }),
1916
- /* @__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" })
1917
2226
  ] }) });
1918
2227
  }
1919
2228
  if (capturedImage) {
1920
2229
  const qualityPassed = qualityResult && qualityResult.qualityScore >= 60;
1921
2230
  const qualityFailed = qualityResult && qualityResult.qualityScore < 60;
1922
2231
  const canContinueAnyway = qualityFailed && retakeCount >= 2;
1923
- return /* @__PURE__ */ jsxs5("div", { style: styles.darkContainer, children: [
1924
- /* @__PURE__ */ jsx6(StepProgressBar, { total: 5, current: 3, isDark: true }),
1925
- /* @__PURE__ */ jsxs5("div", { style: styles.darkScreenHeader, children: [
1926
- /* @__PURE__ */ jsx6("div", { style: { width: 40 } }),
1927
- /* @__PURE__ */ jsx6("h1", { style: styles.darkScreenTitle, children: "Review your photo" }),
1928
- /* @__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" })
1929
2238
  ] }),
1930
- /* @__PURE__ */ jsx6("div", { style: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: "24px" }, children: /* @__PURE__ */ jsxs5("div", { style: styles.reviewCard, children: [
1931
- /* @__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(
1932
2241
  "img",
1933
2242
  {
1934
2243
  src: capturedImage,
@@ -1936,42 +2245,42 @@ function DocumentCaptureScreen({
1936
2245
  style: { width: "100%", maxWidth: "300px", borderRadius: "16px", display: "block", margin: "0 auto" }
1937
2246
  }
1938
2247
  ),
1939
- 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..." }) }),
1940
- qualityPassed && /* @__PURE__ */ jsxs5(Fragment, { children: [
1941
- /* @__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: [
1942
2251
  "\u2713 Quality score: ",
1943
2252
  Math.round(qualityResult.qualityScore),
1944
2253
  "%"
1945
2254
  ] }) }),
1946
- /* @__PURE__ */ jsxs5("div", { style: styles.qualityChecks, children: [
1947
- /* @__PURE__ */ jsx6(QualityCheck, { label: "Sharp" }),
1948
- /* @__PURE__ */ jsx6(QualityCheck, { label: "Well-lit" }),
1949
- /* @__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" })
1950
2259
  ] })
1951
2260
  ] }),
1952
- qualityFailed && /* @__PURE__ */ jsxs5(Fragment, { children: [
1953
- /* @__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: [
1954
2263
  "\u26A0 Quality score: ",
1955
2264
  Math.round(qualityResult.qualityScore),
1956
2265
  "%"
1957
2266
  ] }) }),
1958
- /* @__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)) })
1959
2268
  ] }),
1960
- !qualityResult && !isCheckingQuality && /* @__PURE__ */ jsxs5(Fragment, { children: [
1961
- /* @__PURE__ */ jsx6("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsx6("span", { style: styles.reviewBadge, children: "\u2713 Good quality" }) }),
1962
- /* @__PURE__ */ jsxs5("div", { style: styles.qualityChecks, children: [
1963
- /* @__PURE__ */ jsx6(QualityCheck, { label: "Sharp" }),
1964
- /* @__PURE__ */ jsx6(QualityCheck, { label: "Well-lit" }),
1965
- /* @__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" })
1966
2275
  ] })
1967
2276
  ] })
1968
2277
  ] }) }),
1969
- /* @__PURE__ */ jsx6("div", { style: styles.reviewButtonsRow, children: qualityFailed ? /* @__PURE__ */ jsxs5(Fragment, { children: [
1970
- /* @__PURE__ */ jsx6("button", { style: { ...styles.darkOutlineButton, flex: 1 }, onClick: handleRetake, children: "Retake" }),
1971
- canContinueAnyway && /* @__PURE__ */ jsx6("button", { style: { ...styles.primaryButton, flex: 1 }, onClick: handleContinueAnyway, children: "Continue anyway" })
1972
- ] }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
1973
- /* @__PURE__ */ jsx6("button", { style: { ...styles.darkOutlineButton, flex: 1 }, onClick: handleRetake, children: "Retake" }),
1974
- /* @__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(
1975
2284
  "button",
1976
2285
  {
1977
2286
  style: { ...styles.primaryButton, flex: 1, opacity: isCheckingQuality ? 0.5 : 1 },
@@ -1983,29 +2292,54 @@ function DocumentCaptureScreen({
1983
2292
  ] }) })
1984
2293
  ] });
1985
2294
  }
1986
- return /* @__PURE__ */ jsxs5("div", { style: styles.captureContainer, children: [
1987
- /* @__PURE__ */ jsx6(StepProgressBar, { total: 5, current: 3, isDark: true }),
1988
- /* @__PURE__ */ jsxs5("div", { style: styles.darkScreenHeader, children: [
1989
- /* @__PURE__ */ jsx6("div", { style: { width: 40 } }),
1990
- /* @__PURE__ */ jsxs5("div", { style: { flex: 1, textAlign: "center" }, children: [
1991
- /* @__PURE__ */ jsx6("h1", { style: { ...styles.darkScreenTitle, margin: 0 }, children: side === "front" ? "Front of ID" : "Back of ID" }),
1992
- 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 })
1993
2302
  ] }),
1994
- /* @__PURE__ */ jsx6("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2303
+ /* @__PURE__ */ jsx7("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
1995
2304
  ] }),
1996
- /* @__PURE__ */ jsxs5("div", { style: styles.cameraContainer, children: [
1997
- /* @__PURE__ */ jsx6("video", { ref: videoRef, autoPlay: true, playsInline: true, muted: true, style: styles.cameraVideo }),
1998
- /* @__PURE__ */ jsx6("div", { style: styles.documentOverlay, children: /* @__PURE__ */ jsxs5("div", { ref: guideRef, style: styles.documentFrame, children: [
1999
- /* @__PURE__ */ jsx6("div", { style: { ...styles.corner, top: 0, left: 0 } }),
2000
- /* @__PURE__ */ jsx6("div", { style: { ...styles.corner, top: 0, right: 0, transform: "rotate(90deg)" } }),
2001
- /* @__PURE__ */ jsx6("div", { style: { ...styles.corner, bottom: 0, right: 0, transform: "rotate(180deg)" } }),
2002
- /* @__PURE__ */ jsx6("div", { style: { ...styles.corner, bottom: 0, left: 0, transform: "rotate(270deg)" } }),
2003
- /* @__PURE__ */ jsx6("div", { style: styles.scanLine })
2004
- ] }) }),
2005
- /* @__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" } })
2006
2340
  ] }),
2007
- /* @__PURE__ */ jsxs5("div", { style: styles.stepPillsRow, children: [
2008
- /* @__PURE__ */ jsx6(
2341
+ /* @__PURE__ */ jsxs6("div", { style: styles.stepPillsRow, children: [
2342
+ /* @__PURE__ */ jsx7(
2009
2343
  "span",
2010
2344
  {
2011
2345
  style: {
@@ -2016,7 +2350,7 @@ function DocumentCaptureScreen({
2016
2350
  children: "Front"
2017
2351
  }
2018
2352
  ),
2019
- requiresBack && /* @__PURE__ */ jsx6(
2353
+ requiresBack && /* @__PURE__ */ jsx7(
2020
2354
  "span",
2021
2355
  {
2022
2356
  style: {
@@ -2028,7 +2362,7 @@ function DocumentCaptureScreen({
2028
2362
  }
2029
2363
  )
2030
2364
  ] }),
2031
- /* @__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(
2032
2366
  "span",
2033
2367
  {
2034
2368
  style: {
@@ -2037,44 +2371,44 @@ function DocumentCaptureScreen({
2037
2371
  color: colors.teal
2038
2372
  },
2039
2373
  children: [
2040
- /* @__PURE__ */ jsx6("span", { style: { ...styles.pulsingDot, backgroundColor: colors.teal } }),
2374
+ /* @__PURE__ */ jsx7("span", { style: { ...styles.pulsingDot, backgroundColor: colors.teal } }),
2041
2375
  "Scanning document..."
2042
2376
  ]
2043
2377
  }
2044
2378
  ) }),
2045
- /* @__PURE__ */ jsx6("div", { style: styles.captureFooter, children: /* @__PURE__ */ jsx6(
2379
+ /* @__PURE__ */ jsx7("div", { style: styles.captureFooter, children: /* @__PURE__ */ jsx7(
2046
2380
  "button",
2047
2381
  {
2048
2382
  style: { ...styles.captureButton, opacity: isCapturing ? 0.5 : 1 },
2049
2383
  onClick: handleCapture,
2050
2384
  disabled: isCapturing,
2051
- children: /* @__PURE__ */ jsx6("div", { style: styles.captureButtonInner })
2385
+ children: /* @__PURE__ */ jsx7("div", { style: styles.captureButtonInner })
2052
2386
  }
2053
2387
  ) })
2054
2388
  ] });
2055
2389
  }
2056
2390
  function QualityCheck({ label }) {
2057
- return /* @__PURE__ */ jsxs5("div", { style: styles.qualityCheck, children: [
2058
- /* @__PURE__ */ jsx6("div", { style: styles.qualityCheckIcon, children: "\u2713" }),
2059
- /* @__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 })
2060
2394
  ] });
2061
2395
  }
2062
2396
 
2063
2397
  // src/components/FlipDocumentScreen.tsx
2064
- import { useEffect as useEffect4 } from "react";
2065
- 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";
2066
2400
  function FlipDocumentScreen({ onContinue, onCancel }) {
2067
- useEffect4(() => {
2401
+ useEffect6(() => {
2068
2402
  injectKeyframes();
2069
2403
  }, []);
2070
- return /* @__PURE__ */ jsxs6("div", { style: styles.darkContainer, children: [
2071
- /* @__PURE__ */ jsx7(StepProgressBar, { total: 5, current: 3, isDark: true }),
2072
- /* @__PURE__ */ jsxs6("div", { style: styles.darkScreenHeader, children: [
2073
- /* @__PURE__ */ jsx7("div", { style: { width: 40 } }),
2074
- /* @__PURE__ */ jsx7("h1", { style: styles.darkScreenTitle, children: "Flip your document" }),
2075
- /* @__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" })
2076
2410
  ] }),
2077
- /* @__PURE__ */ jsxs6("div", { style: {
2411
+ /* @__PURE__ */ jsxs7("div", { style: {
2078
2412
  flex: 1,
2079
2413
  display: "flex",
2080
2414
  flexDirection: "column",
@@ -2083,7 +2417,7 @@ function FlipDocumentScreen({ onContinue, onCancel }) {
2083
2417
  padding: "24px",
2084
2418
  gap: "32px"
2085
2419
  }, children: [
2086
- /* @__PURE__ */ jsx7("div", { style: {
2420
+ /* @__PURE__ */ jsx8("div", { style: {
2087
2421
  width: "120px",
2088
2422
  height: "120px",
2089
2423
  borderRadius: "50%",
@@ -2091,18 +2425,18 @@ function FlipDocumentScreen({ onContinue, onCancel }) {
2091
2425
  display: "flex",
2092
2426
  alignItems: "center",
2093
2427
  justifyContent: "center"
2094
- }, children: /* @__PURE__ */ jsxs6("svg", { width: "56", height: "56", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
2095
- /* @__PURE__ */ jsx7("path", { d: "M9 3L5 6.99H8V14H10V6.99H13L9 3Z", fill: colors.teal }),
2096
- /* @__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 })
2097
2431
  ] }) }),
2098
- /* @__PURE__ */ jsxs6("div", { style: { textAlign: "center" }, children: [
2099
- /* @__PURE__ */ jsx7("h2", { style: {
2432
+ /* @__PURE__ */ jsxs7("div", { style: { textAlign: "center" }, children: [
2433
+ /* @__PURE__ */ jsx8("h2", { style: {
2100
2434
  fontSize: "22px",
2101
2435
  fontWeight: 700,
2102
2436
  color: colors.white,
2103
2437
  margin: "0 0 12px 0"
2104
2438
  }, children: "Now capture the back" }),
2105
- /* @__PURE__ */ jsx7("p", { style: {
2439
+ /* @__PURE__ */ jsx8("p", { style: {
2106
2440
  fontSize: "15px",
2107
2441
  color: "rgba(255,255,255,0.6)",
2108
2442
  margin: 0,
@@ -2110,38 +2444,38 @@ function FlipDocumentScreen({ onContinue, onCancel }) {
2110
2444
  maxWidth: "280px"
2111
2445
  }, children: "Turn your document over to the back side, then tap continue to take a photo." })
2112
2446
  ] }),
2113
- /* @__PURE__ */ jsxs6("div", { style: styles.stepPillsRow, children: [
2114
- /* @__PURE__ */ jsx7("span", { style: {
2447
+ /* @__PURE__ */ jsxs7("div", { style: styles.stepPillsRow, children: [
2448
+ /* @__PURE__ */ jsx8("span", { style: {
2115
2449
  ...styles.stepPill,
2116
2450
  backgroundColor: "rgba(16,185,129,0.15)",
2117
2451
  color: colors.success
2118
2452
  }, children: "\u2713 Front" }),
2119
- /* @__PURE__ */ jsx7("span", { style: {
2453
+ /* @__PURE__ */ jsx8("span", { style: {
2120
2454
  ...styles.stepPill,
2121
2455
  backgroundColor: colors.teal,
2122
2456
  color: colors.white
2123
2457
  }, children: "Back" })
2124
2458
  ] })
2125
2459
  ] }),
2126
- /* @__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" }) })
2127
2461
  ] });
2128
2462
  }
2129
2463
 
2130
2464
  // src/components/SelfieCaptureScreen.tsx
2131
- import { useRef as useRef3, useEffect as useEffect5, useState as useState4, useCallback as useCallback3 } from "react";
2132
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
2133
- function SelfieCaptureScreen({ onCapture, onCancel }) {
2134
- const videoRef = useRef3(null);
2135
- const canvasRef = useRef3(null);
2136
- const [stream, setStream] = useState4(null);
2137
- const [isCapturing, setIsCapturing] = useState4(false);
2138
- const [error, setError] = useState4(null);
2139
- const [capturedImage, setCapturedImage] = useState4(null);
2140
- const [capturedBlob, setCapturedBlob] = useState4(null);
2141
- 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(() => {
2142
2476
  injectKeyframes();
2143
2477
  }, []);
2144
- useEffect5(() => {
2478
+ useEffect7(() => {
2145
2479
  let mounted = true;
2146
2480
  async function startCamera() {
2147
2481
  try {
@@ -2163,7 +2497,7 @@ function SelfieCaptureScreen({ onCapture, onCancel }) {
2163
2497
  mounted = false;
2164
2498
  };
2165
2499
  }, [capturedImage]);
2166
- useEffect5(() => {
2500
+ useEffect7(() => {
2167
2501
  return () => {
2168
2502
  stream?.getTracks().forEach((t) => t.stop());
2169
2503
  };
@@ -2207,22 +2541,22 @@ function SelfieCaptureScreen({ onCapture, onCancel }) {
2207
2541
  }
2208
2542
  };
2209
2543
  if (error) {
2210
- return /* @__PURE__ */ jsx8("div", { style: styles.container, children: /* @__PURE__ */ jsxs7("div", { style: styles.errorContainer, children: [
2211
- /* @__PURE__ */ jsx8("p", { style: styles.errorText, children: error }),
2212
- /* @__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" })
2213
2547
  ] }) });
2214
2548
  }
2215
2549
  if (capturedImage) {
2216
- return /* @__PURE__ */ jsxs7("div", { style: styles.darkContainer, children: [
2217
- /* @__PURE__ */ jsx8(StepProgressBar, { total: 5, current: 4, isDark: true }),
2218
- /* @__PURE__ */ jsxs7("div", { style: styles.darkScreenHeader, children: [
2219
- /* @__PURE__ */ jsx8("div", { style: { width: 40 } }),
2220
- /* @__PURE__ */ jsx8("h1", { style: styles.darkScreenTitle, children: "Does this look like you?" }),
2221
- /* @__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" })
2222
2556
  ] }),
2223
- /* @__PURE__ */ jsx8("p", { style: styles.darkScreenSubtitle, children: "Check clarity and lighting" }),
2224
- /* @__PURE__ */ jsx8("div", { style: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: "24px" }, children: /* @__PURE__ */ jsxs7("div", { style: { position: "relative" }, children: [
2225
- /* @__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(
2226
2560
  "img",
2227
2561
  {
2228
2562
  src: capturedImage,
@@ -2230,31 +2564,32 @@ function SelfieCaptureScreen({ onCapture, onCancel }) {
2230
2564
  style: { width: "100%", height: "100%", objectFit: "cover" }
2231
2565
  }
2232
2566
  ) }),
2233
- /* @__PURE__ */ jsx8("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsx8("span", { style: styles.reviewBadge, children: "\u2713 Face detected" }) }),
2234
- /* @__PURE__ */ jsxs7("div", { style: styles.qualityChecks, children: [
2235
- /* @__PURE__ */ jsx8(QualityCheck2, { label: "Clear" }),
2236
- /* @__PURE__ */ jsx8(QualityCheck2, { label: "Centered" }),
2237
- /* @__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" })
2238
2572
  ] })
2239
2573
  ] }) }),
2240
- /* @__PURE__ */ jsxs7("div", { style: styles.reviewButtonsRow, children: [
2241
- /* @__PURE__ */ jsx8("button", { style: { ...styles.darkOutlineButton, flex: 1 }, onClick: handleRetake, children: "Retake" }),
2242
- /* @__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" })
2243
2577
  ] })
2244
2578
  ] });
2245
2579
  }
2246
- return /* @__PURE__ */ jsxs7("div", { style: styles.captureContainer, children: [
2247
- /* @__PURE__ */ jsx8(StepProgressBar, { total: 5, current: 4, isDark: true }),
2248
- /* @__PURE__ */ jsxs7("div", { style: styles.darkScreenHeader, children: [
2249
- /* @__PURE__ */ jsx8("div", { style: { width: 40 } }),
2250
- /* @__PURE__ */ jsxs7("div", { style: { flex: 1, textAlign: "center" }, children: [
2251
- /* @__PURE__ */ jsx8("h1", { style: { ...styles.darkScreenTitle, margin: 0, fontSize: "24px", fontWeight: 700 }, children: "Face the camera" }),
2252
- /* @__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" })
2253
2587
  ] }),
2254
- /* @__PURE__ */ jsx8("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2588
+ /* @__PURE__ */ jsx9("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
2255
2589
  ] }),
2256
- /* @__PURE__ */ jsxs7("div", { style: styles.cameraContainer, children: [
2257
- /* @__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(
2258
2593
  "video",
2259
2594
  {
2260
2595
  ref: videoRef,
@@ -2264,10 +2599,10 @@ function SelfieCaptureScreen({ onCapture, onCancel }) {
2264
2599
  style: { ...styles.cameraVideo, transform: "scaleX(-1)" }
2265
2600
  }
2266
2601
  ),
2267
- /* @__PURE__ */ jsx8("div", { style: styles.selfieOverlay, children: /* @__PURE__ */ jsx8("div", { style: styles.faceGuide, children: /* @__PURE__ */ jsx8("div", { style: styles.rotatingRing }) }) }),
2268
- /* @__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" } })
2269
2604
  ] }),
2270
- /* @__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(
2271
2606
  "span",
2272
2607
  {
2273
2608
  style: {
@@ -2276,32 +2611,88 @@ function SelfieCaptureScreen({ onCapture, onCancel }) {
2276
2611
  color: colors.teal
2277
2612
  },
2278
2613
  children: [
2279
- /* @__PURE__ */ jsx8("span", { style: { ...styles.pulsingDot, backgroundColor: colors.teal } }),
2614
+ /* @__PURE__ */ jsx9("span", { style: { ...styles.pulsingDot, backgroundColor: colors.teal } }),
2280
2615
  "Position your face in the oval"
2281
2616
  ]
2282
2617
  }
2283
2618
  ) }),
2284
- /* @__PURE__ */ jsx8("div", { style: styles.captureFooter, children: /* @__PURE__ */ jsx8(
2619
+ /* @__PURE__ */ jsx9("div", { style: styles.captureFooter, children: /* @__PURE__ */ jsx9(
2285
2620
  "button",
2286
2621
  {
2287
2622
  style: { ...styles.captureButton, opacity: isCapturing ? 0.5 : 1 },
2288
2623
  onClick: handleCapture,
2289
2624
  disabled: isCapturing,
2290
- children: /* @__PURE__ */ jsx8("div", { style: styles.captureButtonInner })
2625
+ children: /* @__PURE__ */ jsx9("div", { style: styles.captureButtonInner })
2291
2626
  }
2292
2627
  ) })
2293
2628
  ] });
2294
2629
  }
2295
2630
  function QualityCheck2({ label }) {
2296
- return /* @__PURE__ */ jsxs7("div", { style: styles.qualityCheck, children: [
2297
- /* @__PURE__ */ jsx8("div", { style: styles.qualityCheckIcon, children: "\u2713" }),
2298
- /* @__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 })
2299
2634
  ] });
2300
2635
  }
2301
2636
 
2302
2637
  // src/components/LivenessScreen.tsx
2303
- import { useEffect as useEffect6, useRef as useRef4, useState as useState5, useCallback as useCallback4 } from "react";
2304
- 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";
2305
2696
  function LivenessScreen({
2306
2697
  session,
2307
2698
  currentChallenge,
@@ -2309,22 +2700,25 @@ function LivenessScreen({
2309
2700
  onChallengeComplete,
2310
2701
  onStart,
2311
2702
  onComplete,
2312
- onCancel
2703
+ onCancel,
2704
+ lastChallengeError,
2705
+ showVisualGuides = true
2313
2706
  }) {
2314
- const videoRef = useRef4(null);
2315
- const canvasRef = useRef4(null);
2316
- const [stream, setStream] = useState5(null);
2317
- const [cameraError, setCameraError] = useState5(null);
2318
- const [phase, setPhase] = useState5("preparing");
2319
- const [countdown, setCountdown] = useState5(3);
2320
- const [capturing, setCapturing] = useState5(false);
2321
- 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(() => {
2322
2716
  injectKeyframes();
2323
2717
  }, []);
2324
- useEffect6(() => {
2718
+ useEffect9(() => {
2325
2719
  if (!session) onStart();
2326
2720
  }, [session, onStart]);
2327
- useEffect6(() => {
2721
+ useEffect9(() => {
2328
2722
  let mounted = true;
2329
2723
  async function startCamera() {
2330
2724
  try {
@@ -2356,12 +2750,12 @@ function LivenessScreen({
2356
2750
  mounted = false;
2357
2751
  };
2358
2752
  }, []);
2359
- useEffect6(() => {
2753
+ useEffect9(() => {
2360
2754
  return () => {
2361
2755
  stream?.getTracks().forEach((t) => t.stop());
2362
2756
  };
2363
2757
  }, [stream]);
2364
- useEffect6(() => {
2758
+ useEffect9(() => {
2365
2759
  if (!currentChallenge) return;
2366
2760
  setPhase("preparing");
2367
2761
  setCountdown(3);
@@ -2389,7 +2783,7 @@ function LivenessScreen({
2389
2783
  0.85
2390
2784
  );
2391
2785
  }, [currentChallenge, capturing, onChallengeComplete]);
2392
- useEffect6(() => {
2786
+ useEffect9(() => {
2393
2787
  if (!currentChallenge || capturing) return;
2394
2788
  if (countdown === 0) {
2395
2789
  if (phase === "preparing") {
@@ -2403,32 +2797,32 @@ function LivenessScreen({
2403
2797
  const t = setTimeout(() => setCountdown((c) => c - 1), 1e3);
2404
2798
  return () => clearTimeout(t);
2405
2799
  }, [countdown, currentChallenge?.id, capturing, captureFrame, phase]);
2406
- useEffect6(() => {
2800
+ useEffect9(() => {
2407
2801
  if (session && !currentChallenge && completedChallenges > 0) {
2408
2802
  onComplete();
2409
2803
  }
2410
2804
  }, [session, currentChallenge, completedChallenges, onComplete]);
2411
2805
  if (cameraError) {
2412
- return /* @__PURE__ */ jsx9("div", { style: styles.darkContainer, children: /* @__PURE__ */ jsxs8("div", { style: styles.errorContainer, children: [
2413
- /* @__PURE__ */ jsx9("p", { style: styles.errorText, children: cameraError }),
2414
- /* @__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" })
2415
2809
  ] }) });
2416
2810
  }
2417
2811
  if (!session) {
2418
- return /* @__PURE__ */ jsx9("div", { style: styles.darkContainer, children: /* @__PURE__ */ jsxs8("div", { style: styles.loadingContainer, children: [
2419
- /* @__PURE__ */ jsx9("div", { style: styles.spinner }),
2420
- /* @__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..." })
2421
2815
  ] }) });
2422
2816
  }
2423
2817
  const totalChallenges = session.challenges.length;
2424
- return /* @__PURE__ */ jsxs8("div", { style: styles.captureContainer, children: [
2425
- /* @__PURE__ */ jsx9(StepProgressBar, { total: 5, current: 5, isDark: true }),
2426
- /* @__PURE__ */ jsxs8("div", { style: styles.darkScreenHeader, children: [
2427
- /* @__PURE__ */ jsx9("div", { style: { width: 40 } }),
2428
- /* @__PURE__ */ jsx9("h1", { style: styles.darkScreenTitle, children: "Liveness Check" }),
2429
- /* @__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" })
2430
2824
  ] }),
2431
- /* @__PURE__ */ jsxs8(
2825
+ /* @__PURE__ */ jsxs9(
2432
2826
  "div",
2433
2827
  {
2434
2828
  style: {
@@ -2441,8 +2835,8 @@ function LivenessScreen({
2441
2835
  padding: "16px 0"
2442
2836
  },
2443
2837
  children: [
2444
- /* @__PURE__ */ jsxs8("div", { style: { position: "relative" }, children: [
2445
- /* @__PURE__ */ jsx9(
2838
+ /* @__PURE__ */ jsxs9("div", { style: { position: "relative" }, children: [
2839
+ /* @__PURE__ */ jsx10(
2446
2840
  "div",
2447
2841
  {
2448
2842
  style: {
@@ -2453,7 +2847,7 @@ function LivenessScreen({
2453
2847
  backgroundColor: "#000",
2454
2848
  border: "3px solid rgba(255,255,255,0.2)"
2455
2849
  },
2456
- children: /* @__PURE__ */ jsx9(
2850
+ children: /* @__PURE__ */ jsx10(
2457
2851
  "video",
2458
2852
  {
2459
2853
  ref: videoRef,
@@ -2470,7 +2864,7 @@ function LivenessScreen({
2470
2864
  )
2471
2865
  }
2472
2866
  ),
2473
- /* @__PURE__ */ jsx9(
2867
+ /* @__PURE__ */ jsx10(
2474
2868
  "svg",
2475
2869
  {
2476
2870
  style: {
@@ -2482,7 +2876,7 @@ function LivenessScreen({
2482
2876
  width: "256",
2483
2877
  height: "316",
2484
2878
  viewBox: "0 0 256 316",
2485
- children: /* @__PURE__ */ jsx9(
2879
+ children: /* @__PURE__ */ jsx10(
2486
2880
  "ellipse",
2487
2881
  {
2488
2882
  cx: "128",
@@ -2498,9 +2892,30 @@ function LivenessScreen({
2498
2892
  }
2499
2893
  )
2500
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
+ }
2501
2916
  )
2502
2917
  ] }),
2503
- currentChallenge && /* @__PURE__ */ jsxs8(
2918
+ currentChallenge && /* @__PURE__ */ jsxs9(
2504
2919
  "div",
2505
2920
  {
2506
2921
  style: {
@@ -2513,7 +2928,14 @@ function LivenessScreen({
2513
2928
  transition: "background-color 200ms, border-color 200ms"
2514
2929
  },
2515
2930
  children: [
2516
- /* @__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(
2517
2939
  "p",
2518
2940
  {
2519
2941
  style: {
@@ -2527,7 +2949,7 @@ function LivenessScreen({
2527
2949
  children: capturing ? "Checking..." : phase === "preparing" ? "Get ready" : "Now \u2014 hold the pose"
2528
2950
  }
2529
2951
  ),
2530
- /* @__PURE__ */ jsx9(
2952
+ /* @__PURE__ */ jsx10(
2531
2953
  "h2",
2532
2954
  {
2533
2955
  style: {
@@ -2538,7 +2960,7 @@ function LivenessScreen({
2538
2960
  children: currentChallenge.instruction
2539
2961
  }
2540
2962
  ),
2541
- !capturing && /* @__PURE__ */ jsx9(
2963
+ !capturing && /* @__PURE__ */ jsx10(
2542
2964
  "p",
2543
2965
  {
2544
2966
  style: {
@@ -2550,16 +2972,33 @@ function LivenessScreen({
2550
2972
  },
2551
2973
  children: countdown
2552
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
+ }
2553
2992
  )
2554
2993
  ]
2555
2994
  }
2556
2995
  ),
2557
- /* @__PURE__ */ jsx9("canvas", { ref: canvasRef, style: { display: "none" } })
2996
+ /* @__PURE__ */ jsx10("canvas", { ref: canvasRef, style: { display: "none" } })
2558
2997
  ]
2559
2998
  }
2560
2999
  ),
2561
- /* @__PURE__ */ jsxs8("div", { style: { padding: "16px 0" }, children: [
2562
- /* @__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(
2563
3002
  "div",
2564
3003
  {
2565
3004
  style: {
@@ -2569,7 +3008,7 @@ function LivenessScreen({
2569
3008
  },
2570
3009
  index
2571
3010
  )) }),
2572
- /* @__PURE__ */ jsxs8("p", { style: styles.progressText, children: [
3011
+ /* @__PURE__ */ jsxs9("p", { style: styles.progressText, children: [
2573
3012
  "Challenge ",
2574
3013
  Math.min(completedChallenges + 1, totalChallenges),
2575
3014
  " of",
@@ -2577,7 +3016,7 @@ function LivenessScreen({
2577
3016
  totalChallenges
2578
3017
  ] })
2579
3018
  ] }),
2580
- /* @__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(
2581
3020
  "p",
2582
3021
  {
2583
3022
  style: {
@@ -2592,35 +3031,35 @@ function LivenessScreen({
2592
3031
  }
2593
3032
 
2594
3033
  // src/components/ResultScreen.tsx
2595
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
3034
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2596
3035
  function ResultScreen({ verification, onDone, onRetry, resultPageMode, simplified, customMessages }) {
2597
3036
  const { status } = verification;
2598
3037
  const effectiveMode = resultPageMode ?? (simplified ? "simplified" : "detailed");
2599
3038
  if (effectiveMode === "simplified") {
2600
3039
  switch (status) {
2601
3040
  case "approved":
2602
- return /* @__PURE__ */ jsx10(SimplifiedSuccess, { onDone, customMessages });
3041
+ return /* @__PURE__ */ jsx11(SimplifiedSuccess, { onDone, customMessages });
2603
3042
  case "rejected":
2604
- return /* @__PURE__ */ jsx10(SimplifiedFailed, { onRetry: onRetry || onDone, customMessages });
3043
+ return /* @__PURE__ */ jsx11(SimplifiedFailed, { onRetry: onRetry || onDone, customMessages });
2605
3044
  case "review_required":
2606
- return /* @__PURE__ */ jsx10(SimplifiedReview, { verification, onDone, customMessages });
3045
+ return /* @__PURE__ */ jsx11(SimplifiedReview, { verification, onDone, customMessages });
2607
3046
  case "expired":
2608
- 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." } });
2609
3048
  default:
2610
- return /* @__PURE__ */ jsx10(SimplifiedSuccess, { onDone, customMessages });
3049
+ return /* @__PURE__ */ jsx11(SimplifiedSuccess, { onDone, customMessages });
2611
3050
  }
2612
3051
  }
2613
3052
  switch (status) {
2614
3053
  case "approved":
2615
- return /* @__PURE__ */ jsx10(SuccessResult, { verification, onDone });
3054
+ return /* @__PURE__ */ jsx11(SuccessResult, { verification, onDone });
2616
3055
  case "rejected":
2617
- return /* @__PURE__ */ jsx10(RejectedResult, { verification, onRetry: onRetry || onDone });
3056
+ return /* @__PURE__ */ jsx11(RejectedResult, { verification, onRetry: onRetry || onDone });
2618
3057
  case "expired":
2619
- return /* @__PURE__ */ jsx10(ExpiredResult, { verification, onRetry: onRetry || onDone });
3058
+ return /* @__PURE__ */ jsx11(ExpiredResult, { verification, onRetry: onRetry || onDone });
2620
3059
  case "review_required":
2621
- return /* @__PURE__ */ jsx10(ManualReviewResult, { verification, onDone });
3060
+ return /* @__PURE__ */ jsx11(ManualReviewResult, { verification, onDone });
2622
3061
  default:
2623
- return /* @__PURE__ */ jsx10(SuccessResult, { verification, onDone });
3062
+ return /* @__PURE__ */ jsx11(SuccessResult, { verification, onDone });
2624
3063
  }
2625
3064
  }
2626
3065
  function SuccessResult({ verification, onDone }) {
@@ -2628,16 +3067,16 @@ function SuccessResult({ verification, onDone }) {
2628
3067
  verification.scores?.overall ?? 100 - (verification.riskScore ?? 16)
2629
3068
  );
2630
3069
  const metrics = computeScoreBreakdown(verification);
2631
- return /* @__PURE__ */ jsxs9("div", { style: styles.resultContainer, children: [
2632
- /* @__PURE__ */ jsxs9("div", { style: styles.resultContent, children: [
2633
- /* @__PURE__ */ jsx10(
3070
+ return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
3071
+ /* @__PURE__ */ jsxs10("div", { style: styles.resultContent, children: [
3072
+ /* @__PURE__ */ jsx11(
2634
3073
  "div",
2635
3074
  {
2636
3075
  style: {
2637
3076
  ...styles.resultIconOuterRing,
2638
3077
  backgroundColor: `${colors.success}15`
2639
3078
  },
2640
- children: /* @__PURE__ */ jsx10(
3079
+ children: /* @__PURE__ */ jsx11(
2641
3080
  "div",
2642
3081
  {
2643
3082
  style: {
@@ -2651,9 +3090,9 @@ function SuccessResult({ verification, onDone }) {
2651
3090
  )
2652
3091
  }
2653
3092
  ),
2654
- /* @__PURE__ */ jsx10("h1", { style: styles.resultTitle, children: "Verification approved" }),
2655
- /* @__PURE__ */ jsx10("p", { style: styles.resultSubtitle, children: "Your identity has been successfully verified." }),
2656
- /* @__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(
2657
3096
  ScoreCard,
2658
3097
  {
2659
3098
  score,
@@ -2661,9 +3100,9 @@ function SuccessResult({ verification, onDone }) {
2661
3100
  gradient: `linear-gradient(135deg, ${colors.teal}, ${colors.tealDark})`
2662
3101
  }
2663
3102
  ),
2664
- metrics.map((m, i) => /* @__PURE__ */ jsx10(ScoreMetricRow, { ...m }, i))
3103
+ metrics.map((m, i) => /* @__PURE__ */ jsx11(ScoreMetricRow, { ...m }, i))
2665
3104
  ] }),
2666
- /* @__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" }) })
2667
3106
  ] });
2668
3107
  }
2669
3108
  function RejectedResult({ verification, onRetry }) {
@@ -2671,16 +3110,16 @@ function RejectedResult({ verification, onRetry }) {
2671
3110
  verification.scores?.overall ?? 100 - (verification.riskScore ?? 58)
2672
3111
  );
2673
3112
  const metrics = computeScoreBreakdown(verification);
2674
- return /* @__PURE__ */ jsxs9("div", { style: styles.resultContainer, children: [
2675
- /* @__PURE__ */ jsxs9("div", { style: styles.resultContent, children: [
2676
- /* @__PURE__ */ jsx10(
3113
+ return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
3114
+ /* @__PURE__ */ jsxs10("div", { style: styles.resultContent, children: [
3115
+ /* @__PURE__ */ jsx11(
2677
3116
  "div",
2678
3117
  {
2679
3118
  style: {
2680
3119
  ...styles.resultIconOuterRing,
2681
3120
  backgroundColor: `${colors.error}15`
2682
3121
  },
2683
- children: /* @__PURE__ */ jsx10(
3122
+ children: /* @__PURE__ */ jsx11(
2684
3123
  "div",
2685
3124
  {
2686
3125
  style: {
@@ -2694,9 +3133,9 @@ function RejectedResult({ verification, onRetry }) {
2694
3133
  )
2695
3134
  }
2696
3135
  ),
2697
- /* @__PURE__ */ jsx10("h1", { style: styles.resultTitle, children: "Verification rejected" }),
2698
- /* @__PURE__ */ jsx10("p", { style: styles.resultSubtitle, children: "We could not verify your identity. Please try again with a valid document." }),
2699
- /* @__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(
2700
3139
  ScoreCard,
2701
3140
  {
2702
3141
  score,
@@ -2704,15 +3143,15 @@ function RejectedResult({ verification, onRetry }) {
2704
3143
  gradient: `linear-gradient(135deg, ${colors.error}, #B91C1C)`
2705
3144
  }
2706
3145
  ),
2707
- metrics.map((m, i) => /* @__PURE__ */ jsx10(ScoreMetricRow, { ...m }, i))
3146
+ metrics.map((m, i) => /* @__PURE__ */ jsx11(ScoreMetricRow, { ...m }, i))
2708
3147
  ] }),
2709
- /* @__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" }) })
2710
3149
  ] });
2711
3150
  }
2712
3151
  function ExpiredResult({ verification, onRetry }) {
2713
- return /* @__PURE__ */ jsxs9("div", { style: styles.resultContainer, children: [
2714
- /* @__PURE__ */ jsxs9("div", { style: styles.resultContent, children: [
2715
- /* @__PURE__ */ jsx10(
3152
+ return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
3153
+ /* @__PURE__ */ jsxs10("div", { style: styles.resultContent, children: [
3154
+ /* @__PURE__ */ jsx11(
2716
3155
  "div",
2717
3156
  {
2718
3157
  style: {
@@ -2720,7 +3159,7 @@ function ExpiredResult({ verification, onRetry }) {
2720
3159
  backgroundColor: `${colors.warning}15`,
2721
3160
  border: `2px solid ${colors.warning}30`
2722
3161
  },
2723
- children: /* @__PURE__ */ jsx10(
3162
+ children: /* @__PURE__ */ jsx11(
2724
3163
  "div",
2725
3164
  {
2726
3165
  style: {
@@ -2734,29 +3173,29 @@ function ExpiredResult({ verification, onRetry }) {
2734
3173
  )
2735
3174
  }
2736
3175
  ),
2737
- /* @__PURE__ */ jsx10("h1", { style: styles.resultTitle, children: "Document expired" }),
2738
- /* @__PURE__ */ jsx10("p", { style: styles.resultSubtitle, children: "The document you submitted has expired. Please use a valid, non-expired document." }),
2739
- verification.documentVerification && /* @__PURE__ */ jsxs9("div", { style: styles.expiryCard, children: [
2740
- /* @__PURE__ */ jsxs9("div", { style: styles.expiryRow, children: [
2741
- /* @__PURE__ */ jsx10("span", { style: styles.expiryLabel, children: "Document type" }),
2742
- /* @__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" })
2743
3182
  ] }),
2744
- verification.documentVerification.issuingCountry && /* @__PURE__ */ jsxs9("div", { style: styles.expiryRow, children: [
2745
- /* @__PURE__ */ jsx10("span", { style: styles.expiryLabel, children: "Country" }),
2746
- /* @__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 })
2747
3186
  ] }),
2748
- verification.documentVerification.expirationDate && /* @__PURE__ */ jsxs9("div", { style: styles.expiryRow, children: [
2749
- /* @__PURE__ */ jsx10("span", { style: styles.expiryLabel, children: "Expired on" }),
2750
- /* @__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 })
2751
3190
  ] })
2752
3191
  ] }),
2753
- /* @__PURE__ */ jsxs9("div", { style: { textAlign: "left" }, children: [
2754
- /* @__PURE__ */ jsx10(GuidanceTip, { number: 1, text: "Check the expiration date on your document" }),
2755
- /* @__PURE__ */ jsx10(GuidanceTip, { number: 2, text: "Use a different document that is currently valid" }),
2756
- /* @__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" })
2757
3196
  ] })
2758
3197
  ] }),
2759
- /* @__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" }) })
2760
3199
  ] });
2761
3200
  }
2762
3201
  function ManualReviewResult({ verification, onDone }) {
@@ -2764,9 +3203,9 @@ function ManualReviewResult({ verification, onDone }) {
2764
3203
  verification.scores?.overall ?? 100 - (verification.riskScore ?? 32)
2765
3204
  );
2766
3205
  const metrics = computeScoreBreakdown(verification);
2767
- return /* @__PURE__ */ jsxs9("div", { style: styles.resultContainer, children: [
2768
- /* @__PURE__ */ jsxs9("div", { style: styles.resultContent, children: [
2769
- /* @__PURE__ */ jsx10(
3206
+ return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
3207
+ /* @__PURE__ */ jsxs10("div", { style: styles.resultContent, children: [
3208
+ /* @__PURE__ */ jsx11(
2770
3209
  "div",
2771
3210
  {
2772
3211
  style: {
@@ -2774,7 +3213,7 @@ function ManualReviewResult({ verification, onDone }) {
2774
3213
  backgroundColor: `${colors.info}15`,
2775
3214
  border: `2px solid ${colors.info}30`
2776
3215
  },
2777
- children: /* @__PURE__ */ jsx10(
3216
+ children: /* @__PURE__ */ jsx11(
2778
3217
  "div",
2779
3218
  {
2780
3219
  style: {
@@ -2788,9 +3227,9 @@ function ManualReviewResult({ verification, onDone }) {
2788
3227
  )
2789
3228
  }
2790
3229
  ),
2791
- /* @__PURE__ */ jsx10("h1", { style: styles.resultTitle, children: "Under review" }),
2792
- /* @__PURE__ */ jsx10("p", { style: styles.resultSubtitle, children: "Your verification requires manual review. We'll notify you of the result." }),
2793
- /* @__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(
2794
3233
  ScoreCard,
2795
3234
  {
2796
3235
  score,
@@ -2798,21 +3237,21 @@ function ManualReviewResult({ verification, onDone }) {
2798
3237
  gradient: `linear-gradient(135deg, ${colors.info}, #0369A1)`
2799
3238
  }
2800
3239
  ),
2801
- metrics.map((m, i) => /* @__PURE__ */ jsx10(ScoreMetricRow, { ...m }, i))
3240
+ metrics.map((m, i) => /* @__PURE__ */ jsx11(ScoreMetricRow, { ...m }, i))
2802
3241
  ] }),
2803
- /* @__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" }) })
2804
3243
  ] });
2805
3244
  }
2806
3245
  function GuidanceTip({ number, text }) {
2807
- return /* @__PURE__ */ jsxs9("div", { style: styles.guidanceTip, children: [
2808
- /* @__PURE__ */ jsx10("div", { style: styles.guidanceTipNumber, children: number }),
2809
- /* @__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 })
2810
3249
  ] });
2811
3250
  }
2812
3251
  function SimplifiedSuccess({ onDone, customMessages }) {
2813
- return /* @__PURE__ */ jsxs9("div", { style: styles.resultContainer, children: [
2814
- /* @__PURE__ */ jsxs9("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
2815
- /* @__PURE__ */ jsx10(
3252
+ return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
3253
+ /* @__PURE__ */ jsxs10("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
3254
+ /* @__PURE__ */ jsx11(
2816
3255
  "div",
2817
3256
  {
2818
3257
  style: {
@@ -2821,7 +3260,7 @@ function SimplifiedSuccess({ onDone, customMessages }) {
2821
3260
  width: 96,
2822
3261
  height: 96
2823
3262
  },
2824
- children: /* @__PURE__ */ jsx10(
3263
+ children: /* @__PURE__ */ jsx11(
2825
3264
  "div",
2826
3265
  {
2827
3266
  style: {
@@ -2838,16 +3277,16 @@ function SimplifiedSuccess({ onDone, customMessages }) {
2838
3277
  )
2839
3278
  }
2840
3279
  ),
2841
- /* @__PURE__ */ jsx10("h1", { style: { ...styles.resultTitle, fontSize: 24, marginTop: 16 }, children: customMessages?.successTitle || "Verification Successful" }),
2842
- /* @__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." })
2843
3282
  ] }),
2844
- /* @__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" }) })
2845
3284
  ] });
2846
3285
  }
2847
3286
  function SimplifiedFailed({ onRetry, customMessages }) {
2848
- return /* @__PURE__ */ jsxs9("div", { style: styles.resultContainer, children: [
2849
- /* @__PURE__ */ jsxs9("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
2850
- /* @__PURE__ */ jsx10(
3287
+ return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
3288
+ /* @__PURE__ */ jsxs10("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
3289
+ /* @__PURE__ */ jsx11(
2851
3290
  "div",
2852
3291
  {
2853
3292
  style: {
@@ -2856,7 +3295,7 @@ function SimplifiedFailed({ onRetry, customMessages }) {
2856
3295
  width: 96,
2857
3296
  height: 96
2858
3297
  },
2859
- children: /* @__PURE__ */ jsx10(
3298
+ children: /* @__PURE__ */ jsx11(
2860
3299
  "div",
2861
3300
  {
2862
3301
  style: {
@@ -2873,16 +3312,16 @@ function SimplifiedFailed({ onRetry, customMessages }) {
2873
3312
  )
2874
3313
  }
2875
3314
  ),
2876
- /* @__PURE__ */ jsx10("h1", { style: { ...styles.resultTitle, fontSize: 24, marginTop: 16 }, children: customMessages?.failedTitle || "Verification Failed" }),
2877
- /* @__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." })
2878
3317
  ] }),
2879
- /* @__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" }) })
2880
3319
  ] });
2881
3320
  }
2882
3321
  function SimplifiedReview({ verification, onDone, customMessages }) {
2883
- return /* @__PURE__ */ jsxs9("div", { style: styles.resultContainer, children: [
2884
- /* @__PURE__ */ jsxs9("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
2885
- /* @__PURE__ */ jsx10(
3322
+ return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
3323
+ /* @__PURE__ */ jsxs10("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
3324
+ /* @__PURE__ */ jsx11(
2886
3325
  "div",
2887
3326
  {
2888
3327
  style: {
@@ -2891,7 +3330,7 @@ function SimplifiedReview({ verification, onDone, customMessages }) {
2891
3330
  width: 96,
2892
3331
  height: 96
2893
3332
  },
2894
- children: /* @__PURE__ */ jsx10(
3333
+ children: /* @__PURE__ */ jsx11(
2895
3334
  "div",
2896
3335
  {
2897
3336
  style: {
@@ -2908,9 +3347,9 @@ function SimplifiedReview({ verification, onDone, customMessages }) {
2908
3347
  )
2909
3348
  }
2910
3349
  ),
2911
- /* @__PURE__ */ jsx10("h1", { style: { ...styles.resultTitle, fontSize: 24, marginTop: 16 }, children: customMessages?.reviewTitle || "Verification Under Review" }),
2912
- /* @__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." }),
2913
- /* @__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: {
2914
3353
  marginTop: 24,
2915
3354
  padding: "12px 24px",
2916
3355
  backgroundColor: `${colors.info}10`,
@@ -2918,27 +3357,27 @@ function SimplifiedReview({ verification, onDone, customMessages }) {
2918
3357
  border: `1px solid ${colors.info}30`,
2919
3358
  display: "inline-block"
2920
3359
  }, children: [
2921
- /* @__PURE__ */ jsx10("span", { style: { fontSize: 12, color: colors.textSecondary }, children: "Reference: " }),
2922
- /* @__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) })
2923
3362
  ] })
2924
3363
  ] }),
2925
- /* @__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" }) })
2926
3365
  ] });
2927
3366
  }
2928
3367
 
2929
3368
  // src/components/ErrorScreen.tsx
2930
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
3369
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2931
3370
  function ErrorScreen({ error, onRetry, onCancel }) {
2932
- return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
2933
- /* @__PURE__ */ jsxs10("div", { style: styles.resultContent, children: [
2934
- /* @__PURE__ */ jsx11(
3371
+ return /* @__PURE__ */ jsxs11("div", { style: styles.resultContainer, children: [
3372
+ /* @__PURE__ */ jsxs11("div", { style: styles.resultContent, children: [
3373
+ /* @__PURE__ */ jsx12(
2935
3374
  "div",
2936
3375
  {
2937
3376
  style: {
2938
3377
  ...styles.resultIconOuterRing,
2939
3378
  backgroundColor: colors.errorBg
2940
3379
  },
2941
- children: /* @__PURE__ */ jsx11(
3380
+ children: /* @__PURE__ */ jsx12(
2942
3381
  "div",
2943
3382
  {
2944
3383
  style: {
@@ -2952,26 +3391,26 @@ function ErrorScreen({ error, onRetry, onCancel }) {
2952
3391
  )
2953
3392
  }
2954
3393
  ),
2955
- /* @__PURE__ */ jsx11("h1", { style: styles.resultTitle, children: "Something went wrong" }),
2956
- /* @__PURE__ */ jsx11("p", { style: styles.resultSubtitle, children: error.message }),
2957
- 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 })
2958
3397
  ] }),
2959
- /* @__PURE__ */ jsxs10("div", { style: styles.footer, children: [
2960
- error.isRetryable && /* @__PURE__ */ jsx11("button", { style: styles.primaryButton, onClick: onRetry, children: "Try Again" }),
2961
- /* @__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" })
2962
3401
  ] })
2963
3402
  ] });
2964
3403
  }
2965
3404
 
2966
3405
  // src/components/LoadingScreen.tsx
2967
- import { useEffect as useEffect7 } from "react";
2968
- 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";
2969
3408
  function LoadingScreen({ message = "Loading..." }) {
2970
- useEffect7(() => {
3409
+ useEffect10(() => {
2971
3410
  injectKeyframes();
2972
3411
  }, []);
2973
- return /* @__PURE__ */ jsx12("div", { style: styles.container, children: /* @__PURE__ */ jsxs11("div", { style: styles.loadingContainer, children: [
2974
- /* @__PURE__ */ jsx12(
3412
+ return /* @__PURE__ */ jsx13("div", { style: styles.container, children: /* @__PURE__ */ jsxs12("div", { style: styles.loadingContainer, children: [
3413
+ /* @__PURE__ */ jsx13(
2975
3414
  "div",
2976
3415
  {
2977
3416
  style: {
@@ -2988,18 +3427,19 @@ function LoadingScreen({ message = "Loading..." }) {
2988
3427
  children: "\u{1F6E1}\uFE0F"
2989
3428
  }
2990
3429
  ),
2991
- /* @__PURE__ */ jsx12("p", { style: styles.loadingText, children: message })
3430
+ /* @__PURE__ */ jsx13("p", { style: styles.loadingText, children: message })
2992
3431
  ] }) });
2993
3432
  }
2994
3433
 
2995
3434
  // src/components/VerificationFlow.tsx
2996
- import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
3435
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
2997
3436
  function VerificationFlow({
2998
3437
  externalId,
2999
3438
  tier = "standard",
3000
3439
  documentTypes,
3001
3440
  expectedFirstName,
3002
3441
  expectedLastName,
3442
+ showVisualGuides = true,
3003
3443
  onComplete,
3004
3444
  onError,
3005
3445
  onCancel,
@@ -3021,20 +3461,20 @@ function VerificationFlow({
3021
3461
  retry,
3022
3462
  sdk
3023
3463
  } = useKoraIDV();
3024
- const [selectedCountry, setSelectedCountry] = useState6(null);
3025
- const [flowStep, setFlowStep] = useState6("consent");
3026
- const [showFlipInstruction, setShowFlipInstruction] = useState6(true);
3027
- const [supportedCountries, setSupportedCountries] = useState6([]);
3028
- const [countriesLoading, setCountriesLoading] = useState6(false);
3029
- 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(() => {
3030
3470
  if (state.step === "document_front") {
3031
3471
  setShowFlipInstruction(true);
3032
3472
  }
3033
3473
  }, [state.step]);
3034
- useEffect8(() => {
3474
+ useEffect11(() => {
3035
3475
  startVerification(externalId, tier, expectedFirstName, expectedLastName);
3036
3476
  }, [externalId, tier, expectedFirstName, expectedLastName, startVerification]);
3037
- useEffect8(() => {
3477
+ useEffect11(() => {
3038
3478
  if (state.error && onError) {
3039
3479
  onError(state.error);
3040
3480
  }
@@ -3080,19 +3520,19 @@ function VerificationFlow({
3080
3520
  ...style
3081
3521
  };
3082
3522
  if (state.error) {
3083
- 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 }) });
3084
3524
  }
3085
3525
  if (state.isLoading && state.step !== "processing") {
3086
- return /* @__PURE__ */ jsx13("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx13(LoadingScreen, {}) });
3526
+ return /* @__PURE__ */ jsx14("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx14(LoadingScreen, {}) });
3087
3527
  }
3088
3528
  if (flowStep === "consent" && state.step === "consent") {
3089
- 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 }) });
3090
3530
  }
3091
3531
  if (flowStep === "country_selection") {
3092
3532
  if (countriesLoading) {
3093
- return /* @__PURE__ */ jsx13("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx13(LoadingScreen, {}) });
3533
+ return /* @__PURE__ */ jsx14("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx14(LoadingScreen, {}) });
3094
3534
  }
3095
- return /* @__PURE__ */ jsx13("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx13(
3535
+ return /* @__PURE__ */ jsx14("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx14(
3096
3536
  CountrySelectionScreen,
3097
3537
  {
3098
3538
  countries: supportedCountries,
@@ -3101,8 +3541,8 @@ function VerificationFlow({
3101
3541
  }
3102
3542
  ) });
3103
3543
  }
3104
- return /* @__PURE__ */ jsxs12("div", { className, style: containerStyle, children: [
3105
- state.step === "document_selection" && /* @__PURE__ */ jsx13(
3544
+ return /* @__PURE__ */ jsxs13("div", { className, style: containerStyle, children: [
3545
+ state.step === "document_selection" && /* @__PURE__ */ jsx14(
3106
3546
  DocumentSelectionScreen,
3107
3547
  {
3108
3548
  documentTypes,
@@ -3111,32 +3551,41 @@ function VerificationFlow({
3111
3551
  onCancel: handleCancel
3112
3552
  }
3113
3553
  ),
3114
- state.step === "document_front" && /* @__PURE__ */ jsx13(
3554
+ state.step === "document_front" && /* @__PURE__ */ jsx14(
3115
3555
  DocumentCaptureScreen,
3116
3556
  {
3117
3557
  side: "front",
3118
3558
  onQualityCheck: (blob) => checkDocumentQuality(blob),
3119
3559
  onCapture: (imageData) => uploadDocument(imageData, "front", selectedCountry?.id),
3120
- onCancel: handleCancel
3560
+ onCancel: handleCancel,
3561
+ showVisualGuides
3121
3562
  }
3122
3563
  ),
3123
- state.step === "document_back" && showFlipInstruction && /* @__PURE__ */ jsx13(
3564
+ state.step === "document_back" && showFlipInstruction && /* @__PURE__ */ jsx14(
3124
3565
  FlipDocumentScreen,
3125
3566
  {
3126
3567
  onContinue: () => setShowFlipInstruction(false),
3127
3568
  onCancel: handleCancel
3128
3569
  }
3129
3570
  ),
3130
- state.step === "document_back" && !showFlipInstruction && /* @__PURE__ */ jsx13(
3571
+ state.step === "document_back" && !showFlipInstruction && /* @__PURE__ */ jsx14(
3131
3572
  DocumentCaptureScreen,
3132
3573
  {
3133
3574
  side: "back",
3134
3575
  onCapture: (imageData) => uploadDocument(imageData, "back", selectedCountry?.id),
3135
- onCancel: handleCancel
3576
+ onCancel: handleCancel,
3577
+ showVisualGuides
3136
3578
  }
3137
3579
  ),
3138
- state.step === "selfie" && /* @__PURE__ */ jsx13(SelfieCaptureScreen, { onCapture: uploadSelfie, onCancel: handleCancel }),
3139
- 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(
3140
3589
  LivenessScreen,
3141
3590
  {
3142
3591
  session: state.livenessSession,
@@ -3145,20 +3594,13 @@ function VerificationFlow({
3145
3594
  onChallengeComplete: submitChallenge,
3146
3595
  onStart: startLiveness,
3147
3596
  onComplete: complete,
3148
- onCancel: handleCancel
3597
+ onCancel: handleCancel,
3598
+ lastChallengeError: state.lastChallengeError,
3599
+ showVisualGuides
3149
3600
  }
3150
3601
  ),
3151
- state.step === "processing" && /* @__PURE__ */ jsx13(
3152
- ProcessingScreen,
3153
- {
3154
- steps: [
3155
- { label: "Document analyzed", status: "done" },
3156
- { label: "Checking face match", status: "active" },
3157
- { label: "Finalizing results", status: "pending" }
3158
- ]
3159
- }
3160
- ),
3161
- state.step === "complete" && state.verification && /* @__PURE__ */ jsx13(
3602
+ state.step === "processing" && /* @__PURE__ */ jsx14(ProcessingScreen, {}),
3603
+ state.step === "complete" && state.verification && /* @__PURE__ */ jsx14(
3162
3604
  ResultScreen,
3163
3605
  {
3164
3606
  verification: state.verification,
@@ -3170,8 +3612,8 @@ function VerificationFlow({
3170
3612
  }
3171
3613
 
3172
3614
  // src/components/QrHandoffScreen.tsx
3173
- import { useEffect as useEffect9, useState as useState7, useRef as useRef5 } from "react";
3174
- 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";
3175
3617
  function QrHandoffScreen({
3176
3618
  session,
3177
3619
  onMobileCaptureComplete,
@@ -3180,11 +3622,11 @@ function QrHandoffScreen({
3180
3622
  onRefresh,
3181
3623
  eventSource
3182
3624
  }) {
3183
- const [timeLeft, setTimeLeft] = useState7(session.expiresIn);
3184
- const [scanned, setScanned] = useState7(false);
3185
- const [expired, setExpired] = useState7(false);
3186
- const timerRef = useRef5();
3187
- 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(() => {
3188
3630
  setTimeLeft(session.expiresIn);
3189
3631
  setExpired(false);
3190
3632
  setScanned(false);
@@ -3201,7 +3643,7 @@ function QrHandoffScreen({
3201
3643
  }, 1e3);
3202
3644
  return () => clearInterval(timerRef.current);
3203
3645
  }, [session.token]);
3204
- useEffect9(() => {
3646
+ useEffect12(() => {
3205
3647
  if (!eventSource) return;
3206
3648
  const handleStatus = (event) => {
3207
3649
  try {
@@ -3227,34 +3669,34 @@ function QrHandoffScreen({
3227
3669
  const seconds = timeLeft % 60;
3228
3670
  const qrSize = 200;
3229
3671
  if (expired) {
3230
- return /* @__PURE__ */ jsx14("div", { style: qrStyles.container, children: /* @__PURE__ */ jsxs13("div", { style: qrStyles.content, children: [
3231
- /* @__PURE__ */ jsx14("div", { style: qrStyles.expiredIcon, children: "\u23F1" }),
3232
- /* @__PURE__ */ jsx14("h2", { style: qrStyles.title, children: "QR Code Expired" }),
3233
- /* @__PURE__ */ jsx14("p", { style: qrStyles.subtitle, children: "The QR code has expired. Generate a new one to continue." }),
3234
- /* @__PURE__ */ jsx14("button", { style: qrStyles.primaryButton, onClick: onRefresh, children: "Generate New QR Code" }),
3235
- /* @__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" })
3236
3678
  ] }) });
3237
3679
  }
3238
3680
  if (scanned) {
3239
- return /* @__PURE__ */ jsx14("div", { style: qrStyles.container, children: /* @__PURE__ */ jsxs13("div", { style: qrStyles.content, children: [
3240
- /* @__PURE__ */ jsx14("div", { style: qrStyles.spinnerContainer, children: /* @__PURE__ */ jsx14("div", { style: qrStyles.spinner }) }),
3241
- /* @__PURE__ */ jsx14("h2", { style: qrStyles.title, children: "Capturing on your phone..." }),
3242
- /* @__PURE__ */ jsx14("p", { style: qrStyles.subtitle, children: "Complete the document scan and selfie on your mobile device. This page will update automatically when done." }),
3243
- /* @__PURE__ */ jsxs13("div", { style: qrStyles.statusBadge, children: [
3244
- /* @__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 }),
3245
3687
  "Connected \u2014 waiting for capture"
3246
3688
  ] })
3247
3689
  ] }) });
3248
3690
  }
3249
- return /* @__PURE__ */ jsx14("div", { style: qrStyles.container, children: /* @__PURE__ */ jsxs13("div", { style: qrStyles.content, children: [
3250
- /* @__PURE__ */ jsx14("h2", { style: qrStyles.title, children: "Scan with your phone" }),
3251
- /* @__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." }),
3252
- /* @__PURE__ */ jsxs13("div", { style: qrStyles.qrContainer, children: [
3253
- /* @__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: {
3254
3696
  ...qrStyles.qrBox,
3255
3697
  width: qrSize,
3256
3698
  height: qrSize
3257
- }, children: /* @__PURE__ */ jsx14(
3699
+ }, children: /* @__PURE__ */ jsx15(
3258
3700
  "img",
3259
3701
  {
3260
3702
  src: `https://api.qrserver.com/v1/create-qr-code/?size=${qrSize}x${qrSize}&data=${encodeURIComponent(session.captureUrl)}&margin=8`,
@@ -3264,26 +3706,26 @@ function QrHandoffScreen({
3264
3706
  style: { borderRadius: 12 }
3265
3707
  }
3266
3708
  ) }),
3267
- /* @__PURE__ */ jsxs13("div", { style: qrStyles.timer, children: [
3709
+ /* @__PURE__ */ jsxs14("div", { style: qrStyles.timer, children: [
3268
3710
  minutes,
3269
3711
  ":",
3270
3712
  seconds.toString().padStart(2, "0"),
3271
3713
  " remaining"
3272
3714
  ] })
3273
3715
  ] }),
3274
- /* @__PURE__ */ jsxs13("div", { style: qrStyles.steps, children: [
3275
- /* @__PURE__ */ jsx14(Step, { number: 1, text: "Open your phone's camera" }),
3276
- /* @__PURE__ */ jsx14(Step, { number: 2, text: "Point at the QR code" }),
3277
- /* @__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" })
3278
3720
  ] }),
3279
- /* @__PURE__ */ jsx14("div", { style: qrStyles.divider, children: /* @__PURE__ */ jsx14("span", { style: qrStyles.dividerText, children: "or" }) }),
3280
- /* @__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" })
3281
3723
  ] }) });
3282
3724
  }
3283
3725
  function Step({ number, text }) {
3284
- return /* @__PURE__ */ jsxs13("div", { style: qrStyles.step, children: [
3285
- /* @__PURE__ */ jsx14("div", { style: qrStyles.stepNumber, children: number }),
3286
- /* @__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 })
3287
3729
  ] });
3288
3730
  }
3289
3731
  var qrStyles = {