smartcomply-web-sdk 1.0.62 → 1.0.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/esm/flow/DocumentCapture.d.ts +21 -1
  2. package/dist/esm/flow/DocumentCapture.d.ts.map +1 -1
  3. package/dist/esm/flow/DocumentCapture.js +155 -84
  4. package/dist/esm/flow/DocumentCapture.js.map +1 -1
  5. package/dist/esm/flow/SmartComplyFlow.d.ts +1 -0
  6. package/dist/esm/flow/SmartComplyFlow.d.ts.map +1 -1
  7. package/dist/esm/flow/SmartComplyFlow.js +89 -105
  8. package/dist/esm/flow/SmartComplyFlow.js.map +1 -1
  9. package/dist/esm/flow/icons.d.ts +14 -0
  10. package/dist/esm/flow/icons.d.ts.map +1 -0
  11. package/dist/esm/flow/icons.js +34 -0
  12. package/dist/esm/flow/icons.js.map +1 -0
  13. package/dist/esm/flow/spinner.d.ts +15 -0
  14. package/dist/esm/flow/spinner.d.ts.map +1 -0
  15. package/dist/esm/flow/spinner.js +55 -0
  16. package/dist/esm/flow/spinner.js.map +1 -0
  17. package/dist/esm/flow/theme.d.ts +19 -0
  18. package/dist/esm/flow/theme.d.ts.map +1 -1
  19. package/dist/esm/flow/theme.js +19 -0
  20. package/dist/esm/flow/theme.js.map +1 -1
  21. package/dist/esm/modules/liveness/LivenessUI.d.ts +10 -0
  22. package/dist/esm/modules/liveness/LivenessUI.d.ts.map +1 -1
  23. package/dist/esm/modules/liveness/LivenessUI.js +17 -16
  24. package/dist/esm/modules/liveness/LivenessUI.js.map +1 -1
  25. package/dist/esm/modules/liveness/liveness.d.ts +2 -0
  26. package/dist/esm/modules/liveness/liveness.d.ts.map +1 -1
  27. package/dist/esm/modules/liveness/liveness.js +1 -1
  28. package/dist/esm/modules/liveness/liveness.js.map +1 -1
  29. package/dist/flow/DocumentCapture.d.ts +21 -1
  30. package/dist/flow/DocumentCapture.d.ts.map +1 -1
  31. package/dist/flow/DocumentCapture.js +155 -84
  32. package/dist/flow/DocumentCapture.js.map +1 -1
  33. package/dist/flow/SmartComplyFlow.d.ts +1 -0
  34. package/dist/flow/SmartComplyFlow.d.ts.map +1 -1
  35. package/dist/flow/SmartComplyFlow.js +88 -104
  36. package/dist/flow/SmartComplyFlow.js.map +1 -1
  37. package/dist/flow/icons.d.ts +14 -0
  38. package/dist/flow/icons.d.ts.map +1 -0
  39. package/dist/flow/icons.js +37 -0
  40. package/dist/flow/icons.js.map +1 -0
  41. package/dist/flow/spinner.d.ts +15 -0
  42. package/dist/flow/spinner.d.ts.map +1 -0
  43. package/dist/flow/spinner.js +59 -0
  44. package/dist/flow/spinner.js.map +1 -0
  45. package/dist/flow/theme.d.ts +19 -0
  46. package/dist/flow/theme.d.ts.map +1 -1
  47. package/dist/flow/theme.js +20 -1
  48. package/dist/flow/theme.js.map +1 -1
  49. package/dist/modules/liveness/LivenessUI.d.ts +10 -0
  50. package/dist/modules/liveness/LivenessUI.d.ts.map +1 -1
  51. package/dist/modules/liveness/LivenessUI.js +17 -16
  52. package/dist/modules/liveness/LivenessUI.js.map +1 -1
  53. package/dist/modules/liveness/liveness.d.ts +2 -0
  54. package/dist/modules/liveness/liveness.d.ts.map +1 -1
  55. package/dist/modules/liveness/liveness.js +1 -1
  56. package/dist/modules/liveness/liveness.js.map +1 -1
  57. package/dist/smartcomply.browser.js +333 -209
  58. package/dist/smartcomply.browser.js.map +4 -4
  59. package/package.json +1 -1
@@ -6,6 +6,8 @@ const theme_1 = require("./theme");
6
6
  const DocumentCapture_1 = require("./DocumentCapture");
7
7
  const device_1 = require("../utils/device");
8
8
  const documentRules_1 = require("../utils/documentRules");
9
+ const icons_1 = require("./icons");
10
+ const spinner_1 = require("./spinner");
9
11
  const STEP_ORDER = [
10
12
  "loading",
11
13
  "welcome",
@@ -229,18 +231,6 @@ function _formatDocType(code) {
229
231
  }
230
232
  // SmartComplyFlow
231
233
  // ─────────────────────────────────────────────────────────────────────
232
- // Add CSS animation keyframes for spin animations
233
- const style = document.createElement('style');
234
- style.textContent = `
235
- @keyframes sc-spin {
236
- 0% { transform: rotate(0deg); }
237
- 100% { transform: rotate(360deg); }
238
- }
239
- `;
240
- if (!document.head.querySelector('style[data-sc-animations]')) {
241
- style.setAttribute('data-sc-animations', 'true');
242
- document.head.appendChild(style);
243
- }
244
234
  class SmartComplyFlow {
245
235
  constructor(options) {
246
236
  this.theme = (0, theme_1.getTheme)("default");
@@ -265,6 +255,11 @@ class SmartComplyFlow {
265
255
  this.idNumber = "";
266
256
  this.documentBlob = null;
267
257
  this.documentBackBlob = null;
258
+ // Whether the document's front image came from the live camera vs. a
259
+ // gallery/file upload — gates whether pre-warming the liveness camera on
260
+ // the confirm screens makes sense (reusing an already-open camera) or
261
+ // would surprise a user who hasn't touched their camera yet this session.
262
+ this._documentViaCamera = false;
268
263
  this.identityCheckId = null;
269
264
  this.verificationResult = null;
270
265
  this.livenessEntryId = null;
@@ -612,27 +607,9 @@ class SmartComplyFlow {
612
607
  // ── Loading ─────────────────────────────────────────────────────
613
608
  renderLoading(container) {
614
609
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px;gap:16px;";
615
- const spinnerWrap = document.createElement("div");
616
- spinnerWrap.style.cssText = "position:relative;width:60px;height:60px;flex-shrink:0;";
617
- const ring = document.createElement("div");
618
- ring.style.cssText = `
619
- position:absolute;inset:0;border-radius:50%;
620
- border:3px solid ${this.theme.border};
621
- border-top-color:${this.theme.primary};
622
- animation:sc-spin 0.9s linear infinite;
623
- `;
624
- const centre = document.createElement("div");
625
- centre.style.cssText = `
626
- position:absolute;inset:0;
627
- display:flex;align-items:center;justify-content:center;
628
- font-size:22px;
629
- `;
630
- centre.textContent = "\uD83D\uDEE1\uFE0F";
631
- spinnerWrap.appendChild(ring);
632
- spinnerWrap.appendChild(centre);
633
- container.appendChild(spinnerWrap);
610
+ container.appendChild((0, spinner_1.renderSpinner)(this.theme, { size: 60, icon: (0, icons_1.icon)("shield", 22) }));
634
611
  const label = document.createElement("div");
635
- label.style.cssText = `color:${this.theme.text};font-size:15px;font-weight:600;`;
612
+ label.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.base}px;font-weight:600;`;
636
613
  label.textContent = "Setting up secure session";
637
614
  container.appendChild(label);
638
615
  const sub = document.createElement("div");
@@ -651,11 +628,11 @@ class SmartComplyFlow {
651
628
  iconWrap.style.cssText = `
652
629
  width:50px;height:50px;border-radius:14px;
653
630
  display:flex;align-items:center;justify-content:center;
654
- font-size:22px;
631
+ color:${this.theme.primary};
655
632
  background:linear-gradient(135deg,${this.theme.primary}22,${this.theme.primaryGlow});
656
633
  border:1.5px solid ${this.theme.primary}33;
657
634
  `;
658
- iconWrap.textContent = isDocFlow ? "🪪" : "🛡️";
635
+ iconWrap.innerHTML = (0, icons_1.icon)(isDocFlow ? "id-card" : "shield", 24);
659
636
  topRow.appendChild(iconWrap);
660
637
  const badge = document.createElement("div");
661
638
  badge.style.cssText = `
@@ -674,7 +651,7 @@ class SmartComplyFlow {
674
651
  titleWrap.style.cssText = "display:flex;flex-direction:column;gap:5px;";
675
652
  const title = document.createElement("h2");
676
653
  title.style.cssText = `
677
- color:${this.theme.text};font-size:20px;font-weight:700;
654
+ color:${this.theme.text};font-size:${theme_1.TYPE.heading}px;font-weight:700;
678
655
  margin:0;line-height:1.2;
679
656
  `;
680
657
  // W2: Use client brand name in title
@@ -708,7 +685,7 @@ class SmartComplyFlow {
708
685
  const stepsWrap = document.createElement("div");
709
686
  stepsWrap.style.cssText = `
710
687
  width:100%;display:flex;flex-direction:column;gap:0;
711
- border-radius:12px;overflow:hidden;
688
+ border-radius:${theme_1.RADIUS.md}px;overflow:hidden;
712
689
  border:1.5px solid ${this.theme.border};
713
690
  `;
714
691
  steps.forEach((step, i) => {
@@ -721,7 +698,7 @@ class SmartComplyFlow {
721
698
  `;
722
699
  const iconEl = document.createElement("div");
723
700
  iconEl.style.cssText = `
724
- width:28px;height:28px;border-radius:8px;flex-shrink:0;
701
+ width:28px;height:28px;border-radius:${theme_1.RADIUS.sm}px;flex-shrink:0;
725
702
  display:flex;align-items:center;justify-content:center;
726
703
  font-size:13px;
727
704
  background:${this.theme.primary}12;
@@ -949,7 +926,7 @@ class SmartComplyFlow {
949
926
  // Dropdown field
950
927
  const select = document.createElement("select");
951
928
  select.style.cssText = `
952
- width:100%;padding:13px 16px;border-radius:12px;
929
+ width:100%;padding:13px 16px;border-radius:${theme_1.RADIUS.md}px;
953
930
  font-size:15px;
954
931
  background:${this.theme.inputBg};
955
932
  color:${this.theme.text};
@@ -984,7 +961,7 @@ class SmartComplyFlow {
984
961
  fileInput.type = "file";
985
962
  fileInput.accept = "image/*";
986
963
  fileInput.style.cssText = `
987
- width:100%;padding:12px 16px;border-radius:12px;
964
+ width:100%;padding:12px 16px;border-radius:${theme_1.RADIUS.md}px;
988
965
  font-size:14px;font-weight:500;
989
966
  background:${this.theme.inputBg};
990
967
  color:${this.theme.text};
@@ -1028,7 +1005,7 @@ class SmartComplyFlow {
1028
1005
  if (rule?.numeric)
1029
1006
  input.inputMode = "numeric";
1030
1007
  input.style.cssText = `
1031
- width:100%;padding:14px 16px;border-radius:12px;
1008
+ width:100%;padding:14px 16px;border-radius:${theme_1.RADIUS.md}px;
1032
1009
  font-size:16px;font-weight:500;
1033
1010
  background:${this.theme.inputBg};
1034
1011
  color:${this.theme.text};
@@ -1090,7 +1067,7 @@ class SmartComplyFlow {
1090
1067
  errorEl.id = "sc-id-error";
1091
1068
  errorEl.style.cssText = `
1092
1069
  color:${this.theme.error};font-size:13px;display:none;
1093
- padding:8px 12px;border-radius:8px;background:${this.theme.errorBg};
1070
+ padding:8px 12px;border-radius:${theme_1.RADIUS.sm}px;background:${this.theme.errorBg};
1094
1071
  `;
1095
1072
  container.appendChild(errorEl);
1096
1073
  // Submit
@@ -1111,7 +1088,7 @@ class SmartComplyFlow {
1111
1088
  };
1112
1089
  submitBtn.addEventListener("click", async () => {
1113
1090
  submitBtn.disabled = true;
1114
- submitBtn.innerHTML = `<span style="display:inline-flex;align-items:center;justify-content:center;gap:8px;"><span style="width:13px;height:13px;border-radius:50%;border:2px solid rgba(255,255,255,0.3);border-top-color:#fff;animation:sc-spin 0.6s linear infinite;display:inline-block;flex-shrink:0;"></span>Verifying...</span>`;
1091
+ submitBtn.innerHTML = `<span style="display:inline-flex;align-items:center;justify-content:center;gap:8px;">${(0, spinner_1.renderInlineSpinner)("#fff", 13)}Verifying...</span>`;
1115
1092
  errorEl.style.display = "none";
1116
1093
  try {
1117
1094
  this.verificationResult = await this.sdk.onboarding.verify({
@@ -1238,11 +1215,11 @@ class SmartComplyFlow {
1238
1215
  // W13: Pre-warm camera silently while user reads this screen
1239
1216
  this._prewarmCamera();
1240
1217
  const iconWrap = document.createElement("div");
1241
- iconWrap.style.cssText = `width:64px;height:64px;border-radius:50%;background:${this.theme.primary}15;border:2px solid ${this.theme.primary}30;display:flex;align-items:center;justify-content:center;font-size:28px;`;
1242
- iconWrap.textContent = "🛡️";
1218
+ iconWrap.style.cssText = `width:64px;height:64px;border-radius:50%;background:${this.theme.primary}15;border:2px solid ${this.theme.primary}30;display:flex;align-items:center;justify-content:center;color:${this.theme.primary};`;
1219
+ iconWrap.innerHTML = (0, icons_1.icon)("shield", 28);
1243
1220
  container.appendChild(iconWrap);
1244
1221
  const title = document.createElement("h2");
1245
- title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1222
+ title.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.heading}px;font-weight:700;margin:0;`;
1246
1223
  title.textContent = this._ivsName ? `Is this you?` : "Identity Confirmed";
1247
1224
  container.appendChild(title);
1248
1225
  if (this._ivsName) {
@@ -1304,14 +1281,32 @@ class SmartComplyFlow {
1304
1281
  this._renderRetryLimitReached(container);
1305
1282
  return;
1306
1283
  }
1307
- // W13: Pre-warm camera while user reads
1308
- this._prewarmCamera();
1284
+ // W13: Pre-warm camera while user reads — but only if the document photo
1285
+ // itself came from the camera. Pre-warming here when the user uploaded
1286
+ // from gallery means requesting camera access before they've touched
1287
+ // their camera at all this session, which reads as an unexplained/
1288
+ // unexpected prompt rather than a seamless continuation.
1289
+ if (this._documentViaCamera)
1290
+ this._prewarmCamera();
1291
+ // Step badge — pairs with DocumentCapture.showPreview's "Step 1 of 2"
1292
+ // badge shown right after capture, so this doesn't read as unexplained
1293
+ // repetition: that screen checked photo clarity, this one checks the
1294
+ // OCR-extracted data.
1295
+ const stepBadge = document.createElement("div");
1296
+ stepBadge.style.cssText = `
1297
+ display:inline-flex;align-items:center;gap:5px;
1298
+ padding:3px 11px;border-radius:${theme_1.RADIUS.pill}px;
1299
+ font-size:10.5px;font-weight:600;letter-spacing:0.2px;
1300
+ color:${this.theme.primary};background:${this.theme.primary}15;border:1px solid ${this.theme.primary}30;
1301
+ `;
1302
+ stepBadge.textContent = "STEP 2 OF 2 · CHECK WHAT WE READ";
1303
+ container.appendChild(stepBadge);
1309
1304
  const iconWrap = document.createElement("div");
1310
- iconWrap.style.cssText = `width:64px;height:64px;border-radius:50%;background:${this.theme.primary}15;border:2px solid ${this.theme.primary}30;display:flex;align-items:center;justify-content:center;font-size:28px;`;
1311
- iconWrap.textContent = "📄";
1305
+ iconWrap.style.cssText = `width:64px;height:64px;border-radius:50%;background:${this.theme.primary}15;border:2px solid ${this.theme.primary}30;display:flex;align-items:center;justify-content:center;color:${this.theme.primary};`;
1306
+ iconWrap.innerHTML = (0, icons_1.icon)("document", 28);
1312
1307
  container.appendChild(iconWrap);
1313
1308
  const title = document.createElement("h2");
1314
- title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1309
+ title.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.heading}px;font-weight:700;margin:0;`;
1315
1310
  title.textContent = "Are these your details?";
1316
1311
  container.appendChild(title);
1317
1312
  const sub = document.createElement("p");
@@ -1403,19 +1398,10 @@ class SmartComplyFlow {
1403
1398
  // ── W6+W7+W8: OCR Gate (document verification) ───────────────────
1404
1399
  renderOcrGate(container) {
1405
1400
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:14px;padding:20px 0;text-align:center;";
1406
- const spinnerWrap = document.createElement("div");
1407
- spinnerWrap.style.cssText = "position:relative;width:52px;height:52px;flex-shrink:0;";
1408
- const ring = document.createElement("div");
1409
- ring.style.cssText = `position:absolute;inset:0;border-radius:50%;border:3px solid ${this.theme.border};border-top-color:${this.theme.primary};animation:sc-spin 0.9s linear infinite;`;
1410
- const centre = document.createElement("div");
1411
- centre.style.cssText = "position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;";
1412
- centre.textContent = "📄";
1413
- spinnerWrap.appendChild(ring);
1414
- spinnerWrap.appendChild(centre);
1415
- container.appendChild(spinnerWrap);
1401
+ container.appendChild((0, spinner_1.renderSpinner)(this.theme, { size: 52, icon: (0, icons_1.icon)("document", 20) }));
1416
1402
  const statusEl = document.createElement("div");
1417
1403
  statusEl.setAttribute("aria-live", "polite");
1418
- statusEl.style.cssText = `color:${this.theme.text};font-size:15px;font-weight:600;`;
1404
+ statusEl.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.base}px;font-weight:600;`;
1419
1405
  statusEl.textContent = "Reading your document…";
1420
1406
  container.appendChild(statusEl);
1421
1407
  const subEl = document.createElement("div");
@@ -1516,7 +1502,7 @@ class SmartComplyFlow {
1516
1502
  icon.textContent = "📅";
1517
1503
  container.appendChild(icon);
1518
1504
  const t = document.createElement("h2");
1519
- t.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1505
+ t.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.heading}px;font-weight:700;margin:0;`;
1520
1506
  t.textContent = "Document Expired";
1521
1507
  container.appendChild(t);
1522
1508
  const d = document.createElement("p");
@@ -1542,7 +1528,7 @@ class SmartComplyFlow {
1542
1528
  icon.textContent = "🔍";
1543
1529
  container.appendChild(icon);
1544
1530
  const t = document.createElement("h2");
1545
- t.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1531
+ t.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.heading}px;font-weight:700;margin:0;`;
1546
1532
  t.textContent = "Document Photo Unclear";
1547
1533
  container.appendChild(t);
1548
1534
  const msg = reason === "image_too_blurry" ? "Your document photo is too blurry. Please retake it in good lighting with a steady hand."
@@ -1579,11 +1565,11 @@ class SmartComplyFlow {
1579
1565
  verificationResult: this.verificationResult || undefined,
1580
1566
  });
1581
1567
  const iconWrap = document.createElement("div");
1582
- iconWrap.style.cssText = `width:80px;height:80px;border-radius:50%;background:${this.theme.successBg};border:3px solid ${this.theme.success};display:flex;align-items:center;justify-content:center;font-size:36px;font-weight:700;color:${this.theme.success};animation:sc-pop 0.4s ease;`;
1583
- iconWrap.textContent = "✓";
1568
+ iconWrap.style.cssText = `width:80px;height:80px;border-radius:50%;background:${this.theme.successBg};border:3px solid ${this.theme.success};display:flex;align-items:center;justify-content:center;color:${this.theme.success};animation:sc-pop 0.4s ease;`;
1569
+ iconWrap.innerHTML = (0, icons_1.icon)("check", 36);
1584
1570
  container.appendChild(iconWrap);
1585
1571
  const title = document.createElement("h2");
1586
- title.style.cssText = `color:${this.theme.text};font-size:22px;font-weight:700;margin:0;`;
1572
+ title.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.headingLg}px;font-weight:700;margin:0;`;
1587
1573
  title.textContent = "You're all done!";
1588
1574
  container.appendChild(title);
1589
1575
  const desc = document.createElement("p");
@@ -1610,7 +1596,7 @@ class SmartComplyFlow {
1610
1596
  icon.textContent = "⚠️";
1611
1597
  container.appendChild(icon);
1612
1598
  const t = document.createElement("h2");
1613
- t.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1599
+ t.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.heading}px;font-weight:700;margin:0;`;
1614
1600
  t.textContent = "Unable to Complete Verification";
1615
1601
  container.appendChild(t);
1616
1602
  const d = document.createElement("p");
@@ -1645,6 +1631,7 @@ class SmartComplyFlow {
1645
1631
  // below so compression only happens once.
1646
1632
  this.documentBlob = await _compressImage(result.front, 1600, 0.85);
1647
1633
  this.documentBackBlob = result.back ? await _compressImage(result.back, 1600, 0.85) : null;
1634
+ this._documentViaCamera = result.viaCamera;
1648
1635
  // Process the front image — pass the already-compressed blob
1649
1636
  await this.processDocumentBlob(this.documentBlob, captureContainer, container);
1650
1637
  }
@@ -1665,15 +1652,12 @@ class SmartComplyFlow {
1665
1652
  }
1666
1653
  this.documentBlob = blob;
1667
1654
  // Show scanning state
1668
- captureContainer.innerHTML = `
1669
- <div style="display:flex;flex-direction:column;align-items:center;gap:14px;padding:28px 20px;">
1670
- <div style="position:relative;width:52px;height:52px;">
1671
- <div style="position:absolute;inset:0;border-radius:50%;border:3px solid ${this.theme.border};border-top-color:${this.theme.primary};animation:sc-spin 0.8s linear infinite;"></div>
1672
- <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">🔍</div>
1673
- </div>
1655
+ captureContainer.innerHTML = "";
1656
+ captureContainer.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:14px;padding:28px 20px;";
1657
+ captureContainer.appendChild((0, spinner_1.renderSpinner)(this.theme, { size: 52, icon: (0, icons_1.icon)("search", 20) }));
1658
+ captureContainer.insertAdjacentHTML("beforeend", `
1674
1659
  <div style="color:${this.theme.text};font-size:14px;font-weight:600;">Scanning document...</div>
1675
- <div style="color:${this.theme.textSecondary};font-size:12px;text-align:center;max-width:200px;line-height:1.5;">Running OCR and verifying your identity</div>
1676
- </div>`;
1660
+ <div style="color:${this.theme.textSecondary};font-size:12px;text-align:center;max-width:200px;line-height:1.5;">Running OCR and verifying your identity</div>`);
1677
1661
  // Create liveness entry with document.
1678
1662
  // Use the first collected field value as identifier when available
1679
1663
  // (document flow has no data-verification step, so collectedFields is
@@ -1737,15 +1721,12 @@ class SmartComplyFlow {
1737
1721
  console.log(`Retrying document processing (attempt ${retryCount}/${maxRetries})`);
1738
1722
  // Show retry message
1739
1723
  if (captureContainer) {
1740
- captureContainer.innerHTML = `
1741
- <div style="display:flex;flex-direction:column;align-items:center;gap:16px;padding:28px 20px;text-align:center;">
1742
- <div style="position:relative;width:52px;height:52px;">
1743
- <div style="position:absolute;inset:0;border-radius:50%;border:3px solid ${this.theme.border};border-top-color:${this.theme.primary};animation:sc-spin 0.8s linear infinite;"></div>
1744
- <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">🔄</div>
1745
- </div>
1724
+ captureContainer.innerHTML = "";
1725
+ captureContainer.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:16px;padding:28px 20px;text-align:center;";
1726
+ captureContainer.appendChild((0, spinner_1.renderSpinner)(this.theme, { size: 52, icon: (0, icons_1.icon)("refresh", 20) }));
1727
+ captureContainer.insertAdjacentHTML("beforeend", `
1746
1728
  <div style="color:${this.theme.text};font-size:14px;font-weight:600;">Retrying...</div>
1747
- <div style="color:${this.theme.textSecondary};font-size:12px;max-width:240px;line-height:1.5;">Attempt ${retryCount} of ${maxRetries}</div>
1748
- </div>`;
1729
+ <div style="color:${this.theme.textSecondary};font-size:12px;max-width:240px;line-height:1.5;">Attempt ${retryCount} of ${maxRetries}</div>`);
1749
1730
  }
1750
1731
  // Wait before retry
1751
1732
  await new Promise(resolve => setTimeout(resolve, 2000));
@@ -1807,7 +1788,7 @@ class SmartComplyFlow {
1807
1788
  const retryBtn = document.createElement("button");
1808
1789
  retryBtn.textContent = "Try Again";
1809
1790
  retryBtn.setAttribute("aria-label", "Retake document photo");
1810
- retryBtn.style.cssText = `margin-top:8px;padding:12px 24px;border-radius:8px;background:${this.theme.primary};color:#fff;border:none;font-size:14px;font-weight:600;cursor:pointer;`;
1791
+ retryBtn.style.cssText = `margin-top:8px;padding:12px 24px;border-radius:${theme_1.RADIUS.sm}px;background:${this.theme.primary};color:#fff;border:none;font-size:14px;font-weight:600;cursor:pointer;`;
1811
1792
  retryBtn.addEventListener("click", () => this.showStep("document_capture"));
1812
1793
  wrap.appendChild(retryBtn);
1813
1794
  }
@@ -1868,6 +1849,12 @@ class SmartComplyFlow {
1868
1849
  : "liveness_only",
1869
1850
  // B4: pass pre-warmed stream so camera.open() is skipped
1870
1851
  prewarmedStream: this._prewarmedStream || undefined,
1852
+ colors: {
1853
+ primary: this.theme.primary,
1854
+ success: this.theme.success,
1855
+ error: this.theme.error,
1856
+ warning: this.theme.warning,
1857
+ },
1871
1858
  }, ["BLINK"], this.livenessEntryId || undefined)
1872
1859
  .then((result) => {
1873
1860
  // W11: After submit, end user is done — show "You're all done" screen.
@@ -1909,14 +1896,14 @@ class SmartComplyFlow {
1909
1896
  // /v1/sdk/liveness/submit/) are an operator-facing problem, not something
1910
1897
  // the end user can act on — never show "wallet"/"balance" language to them.
1911
1898
  const isBillingErr = /wallet|balance|insufficient.*fund|payment.*required/i.test(message);
1912
- const errorIcon = isCameraErr ? "📷"
1913
- : isNetworkErr ? "📡"
1914
- : isAuthErr ? "⏱"
1915
- : isDocQuality ? "🔍"
1916
- : isExpiredDoc ? "📅"
1917
- : isFaceMatch ? "🤳"
1918
- : isBillingErr ? "⚠️"
1919
- : "✕";
1899
+ const errorIconName = isCameraErr ? "camera"
1900
+ : isNetworkErr ? "network"
1901
+ : isAuthErr ? "clock"
1902
+ : isDocQuality ? "search"
1903
+ : isExpiredDoc ? "calendar"
1904
+ : isFaceMatch ? "face"
1905
+ : isBillingErr ? "warning"
1906
+ : "close";
1920
1907
  const errorTitle = isCameraErr ? "Camera Access Required"
1921
1908
  : isNetworkErr ? "Connection Problem"
1922
1909
  : isAuthErr ? "Session Expired"
@@ -1945,14 +1932,14 @@ class SmartComplyFlow {
1945
1932
  iconWrap.style.cssText = `
1946
1933
  width:72px;height:72px;border-radius:50%;
1947
1934
  display:flex;align-items:center;justify-content:center;
1948
- font-size:28px;
1935
+ color:${this.theme.error};
1949
1936
  background:${this.theme.errorBg};
1950
1937
  border:3px solid ${this.theme.error};
1951
1938
  `;
1952
- iconWrap.textContent = errorIcon;
1939
+ iconWrap.innerHTML = (0, icons_1.icon)(errorIconName, 28);
1953
1940
  wrapper.appendChild(iconWrap);
1954
1941
  const title = document.createElement("h2");
1955
- title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1942
+ title.style.cssText = `color:${this.theme.text};font-size:${theme_1.TYPE.heading}px;font-weight:700;margin:0;`;
1956
1943
  title.textContent = errorTitle;
1957
1944
  wrapper.appendChild(title);
1958
1945
  const desc = document.createElement("p");
@@ -1991,7 +1978,7 @@ class SmartComplyFlow {
1991
1978
  this.headerBackBtn.innerHTML = "&#8592;";
1992
1979
  this.headerBackBtn.setAttribute("aria-label", "Go back to previous step");
1993
1980
  this.headerBackBtn.style.cssText = `
1994
- width:30px;height:30px;border-radius:8px;flex-shrink:0;
1981
+ width:30px;height:30px;border-radius:${theme_1.RADIUS.sm}px;flex-shrink:0;
1995
1982
  display:none;align-items:center;justify-content:center;
1996
1983
  border:1.5px solid ${this.theme.border};cursor:pointer;
1997
1984
  font-size:16px;line-height:1;
@@ -2049,7 +2036,7 @@ class SmartComplyFlow {
2049
2036
  closeBtn.innerHTML = "✕";
2050
2037
  closeBtn.setAttribute("aria-label", "Close identity verification");
2051
2038
  closeBtn.style.cssText = `
2052
- width:32px;height:32px;border-radius:8px;flex-shrink:0;
2039
+ width:32px;height:32px;border-radius:${theme_1.RADIUS.sm}px;flex-shrink:0;
2053
2040
  display:flex;align-items:center;justify-content:center;
2054
2041
  border:none;cursor:pointer;font-size:16px;
2055
2042
  background:${this.theme.shimmer};color:${this.theme.textMuted};
@@ -2086,7 +2073,7 @@ class SmartComplyFlow {
2086
2073
  createStepTitle(text) {
2087
2074
  const el = document.createElement("h3");
2088
2075
  el.style.cssText = `
2089
- color:${this.theme.text};font-size:18px;font-weight:700;
2076
+ color:${this.theme.text};font-size:${theme_1.TYPE.heading}px;font-weight:700;
2090
2077
  margin:0;
2091
2078
  `;
2092
2079
  el.textContent = text;
@@ -2096,7 +2083,7 @@ class SmartComplyFlow {
2096
2083
  const card = document.createElement("button");
2097
2084
  card.setAttribute("aria-label", subtitle ? `${title}: ${subtitle}` : title);
2098
2085
  card.style.cssText = `
2099
- width:100%;padding:13px 16px;border-radius:12px;
2086
+ width:100%;padding:13px 16px;border-radius:${theme_1.RADIUS.md}px;
2100
2087
  background:${this.theme.cardBg};
2101
2088
  border:2px solid ${this.theme.border};
2102
2089
  cursor:pointer;text-align:left;
@@ -2136,7 +2123,7 @@ class SmartComplyFlow {
2136
2123
  const btn = document.createElement("button");
2137
2124
  btn.textContent = text;
2138
2125
  btn.style.cssText = `
2139
- width:100%;padding:14px;border-radius:12px;
2126
+ width:100%;padding:14px;border-radius:${theme_1.RADIUS.md}px;
2140
2127
  font-size:15px;font-weight:600;
2141
2128
  background:${this.theme.primary};color:#fff;
2142
2129
  border:none;cursor:pointer;
@@ -2157,7 +2144,7 @@ class SmartComplyFlow {
2157
2144
  const btn = document.createElement("button");
2158
2145
  btn.textContent = text;
2159
2146
  btn.style.cssText = `
2160
- width:100%;padding:12px;border-radius:12px;
2147
+ width:100%;padding:12px;border-radius:${theme_1.RADIUS.md}px;
2161
2148
  font-size:14px;font-weight:500;
2162
2149
  background:transparent;color:${this.theme.textSecondary};
2163
2150
  border:none;cursor:pointer;
@@ -2295,9 +2282,6 @@ class SmartComplyFlow {
2295
2282
  const style = document.createElement("style");
2296
2283
  style.id = "sc-flow-styles";
2297
2284
  style.textContent = `
2298
- @keyframes sc-spin {
2299
- to { transform: rotate(360deg); }
2300
- }
2301
2285
  @keyframes sc-pop {
2302
2286
  0% { transform: scale(0.8); opacity: 0; }
2303
2287
  50% { transform: scale(1.05); }