smartcomply-web-sdk 1.0.62 → 1.0.63

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 (57) hide show
  1. package/dist/esm/flow/DocumentCapture.d.ts +19 -1
  2. package/dist/esm/flow/DocumentCapture.d.ts.map +1 -1
  3. package/dist/esm/flow/DocumentCapture.js +144 -77
  4. package/dist/esm/flow/DocumentCapture.js.map +1 -1
  5. package/dist/esm/flow/SmartComplyFlow.d.ts.map +1 -1
  6. package/dist/esm/flow/SmartComplyFlow.js +76 -103
  7. package/dist/esm/flow/SmartComplyFlow.js.map +1 -1
  8. package/dist/esm/flow/icons.d.ts +14 -0
  9. package/dist/esm/flow/icons.d.ts.map +1 -0
  10. package/dist/esm/flow/icons.js +34 -0
  11. package/dist/esm/flow/icons.js.map +1 -0
  12. package/dist/esm/flow/spinner.d.ts +15 -0
  13. package/dist/esm/flow/spinner.d.ts.map +1 -0
  14. package/dist/esm/flow/spinner.js +55 -0
  15. package/dist/esm/flow/spinner.js.map +1 -0
  16. package/dist/esm/flow/theme.d.ts +19 -0
  17. package/dist/esm/flow/theme.d.ts.map +1 -1
  18. package/dist/esm/flow/theme.js +19 -0
  19. package/dist/esm/flow/theme.js.map +1 -1
  20. package/dist/esm/modules/liveness/LivenessUI.d.ts +10 -0
  21. package/dist/esm/modules/liveness/LivenessUI.d.ts.map +1 -1
  22. package/dist/esm/modules/liveness/LivenessUI.js +17 -16
  23. package/dist/esm/modules/liveness/LivenessUI.js.map +1 -1
  24. package/dist/esm/modules/liveness/liveness.d.ts +2 -0
  25. package/dist/esm/modules/liveness/liveness.d.ts.map +1 -1
  26. package/dist/esm/modules/liveness/liveness.js +1 -1
  27. package/dist/esm/modules/liveness/liveness.js.map +1 -1
  28. package/dist/flow/DocumentCapture.d.ts +19 -1
  29. package/dist/flow/DocumentCapture.d.ts.map +1 -1
  30. package/dist/flow/DocumentCapture.js +144 -77
  31. package/dist/flow/DocumentCapture.js.map +1 -1
  32. package/dist/flow/SmartComplyFlow.d.ts.map +1 -1
  33. package/dist/flow/SmartComplyFlow.js +75 -102
  34. package/dist/flow/SmartComplyFlow.js.map +1 -1
  35. package/dist/flow/icons.d.ts +14 -0
  36. package/dist/flow/icons.d.ts.map +1 -0
  37. package/dist/flow/icons.js +37 -0
  38. package/dist/flow/icons.js.map +1 -0
  39. package/dist/flow/spinner.d.ts +15 -0
  40. package/dist/flow/spinner.d.ts.map +1 -0
  41. package/dist/flow/spinner.js +59 -0
  42. package/dist/flow/spinner.js.map +1 -0
  43. package/dist/flow/theme.d.ts +19 -0
  44. package/dist/flow/theme.d.ts.map +1 -1
  45. package/dist/flow/theme.js +20 -1
  46. package/dist/flow/theme.js.map +1 -1
  47. package/dist/modules/liveness/LivenessUI.d.ts +10 -0
  48. package/dist/modules/liveness/LivenessUI.d.ts.map +1 -1
  49. package/dist/modules/liveness/LivenessUI.js +17 -16
  50. package/dist/modules/liveness/LivenessUI.js.map +1 -1
  51. package/dist/modules/liveness/liveness.d.ts +2 -0
  52. package/dist/modules/liveness/liveness.d.ts.map +1 -1
  53. package/dist/modules/liveness/liveness.js +1 -1
  54. package/dist/modules/liveness/liveness.js.map +1 -1
  55. package/dist/smartcomply.browser.js +316 -202
  56. package/dist/smartcomply.browser.js.map +4 -4
  57. package/package.json +1 -1
@@ -1,8 +1,10 @@
1
1
  import { SmartComply } from "../client/Smartcomply";
2
- import { getTheme } from "./theme";
2
+ import { getTheme, TYPE, RADIUS } from "./theme";
3
3
  import { DocumentCapture } from "./DocumentCapture";
4
4
  import { collectDeviceMetadata } from "../utils/device";
5
5
  import { requiresBackSide } from "../utils/documentRules";
6
+ import { icon } from "./icons";
7
+ import { renderSpinner, renderInlineSpinner } from "./spinner";
6
8
  const STEP_ORDER = [
7
9
  "loading",
8
10
  "welcome",
@@ -226,18 +228,6 @@ function _formatDocType(code) {
226
228
  }
227
229
  // SmartComplyFlow
228
230
  // ─────────────────────────────────────────────────────────────────────
229
- // Add CSS animation keyframes for spin animations
230
- const style = document.createElement('style');
231
- style.textContent = `
232
- @keyframes sc-spin {
233
- 0% { transform: rotate(0deg); }
234
- 100% { transform: rotate(360deg); }
235
- }
236
- `;
237
- if (!document.head.querySelector('style[data-sc-animations]')) {
238
- style.setAttribute('data-sc-animations', 'true');
239
- document.head.appendChild(style);
240
- }
241
231
  export class SmartComplyFlow {
242
232
  constructor(options) {
243
233
  this.theme = getTheme("default");
@@ -609,27 +599,9 @@ export class SmartComplyFlow {
609
599
  // ── Loading ─────────────────────────────────────────────────────
610
600
  renderLoading(container) {
611
601
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px;gap:16px;";
612
- const spinnerWrap = document.createElement("div");
613
- spinnerWrap.style.cssText = "position:relative;width:60px;height:60px;flex-shrink:0;";
614
- const ring = document.createElement("div");
615
- ring.style.cssText = `
616
- position:absolute;inset:0;border-radius:50%;
617
- border:3px solid ${this.theme.border};
618
- border-top-color:${this.theme.primary};
619
- animation:sc-spin 0.9s linear infinite;
620
- `;
621
- const centre = document.createElement("div");
622
- centre.style.cssText = `
623
- position:absolute;inset:0;
624
- display:flex;align-items:center;justify-content:center;
625
- font-size:22px;
626
- `;
627
- centre.textContent = "\uD83D\uDEE1\uFE0F";
628
- spinnerWrap.appendChild(ring);
629
- spinnerWrap.appendChild(centre);
630
- container.appendChild(spinnerWrap);
602
+ container.appendChild(renderSpinner(this.theme, { size: 60, icon: icon("shield", 22) }));
631
603
  const label = document.createElement("div");
632
- label.style.cssText = `color:${this.theme.text};font-size:15px;font-weight:600;`;
604
+ label.style.cssText = `color:${this.theme.text};font-size:${TYPE.base}px;font-weight:600;`;
633
605
  label.textContent = "Setting up secure session";
634
606
  container.appendChild(label);
635
607
  const sub = document.createElement("div");
@@ -648,11 +620,11 @@ export class SmartComplyFlow {
648
620
  iconWrap.style.cssText = `
649
621
  width:50px;height:50px;border-radius:14px;
650
622
  display:flex;align-items:center;justify-content:center;
651
- font-size:22px;
623
+ color:${this.theme.primary};
652
624
  background:linear-gradient(135deg,${this.theme.primary}22,${this.theme.primaryGlow});
653
625
  border:1.5px solid ${this.theme.primary}33;
654
626
  `;
655
- iconWrap.textContent = isDocFlow ? "🪪" : "🛡️";
627
+ iconWrap.innerHTML = icon(isDocFlow ? "id-card" : "shield", 24);
656
628
  topRow.appendChild(iconWrap);
657
629
  const badge = document.createElement("div");
658
630
  badge.style.cssText = `
@@ -671,7 +643,7 @@ export class SmartComplyFlow {
671
643
  titleWrap.style.cssText = "display:flex;flex-direction:column;gap:5px;";
672
644
  const title = document.createElement("h2");
673
645
  title.style.cssText = `
674
- color:${this.theme.text};font-size:20px;font-weight:700;
646
+ color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;
675
647
  margin:0;line-height:1.2;
676
648
  `;
677
649
  // W2: Use client brand name in title
@@ -705,7 +677,7 @@ export class SmartComplyFlow {
705
677
  const stepsWrap = document.createElement("div");
706
678
  stepsWrap.style.cssText = `
707
679
  width:100%;display:flex;flex-direction:column;gap:0;
708
- border-radius:12px;overflow:hidden;
680
+ border-radius:${RADIUS.md}px;overflow:hidden;
709
681
  border:1.5px solid ${this.theme.border};
710
682
  `;
711
683
  steps.forEach((step, i) => {
@@ -718,7 +690,7 @@ export class SmartComplyFlow {
718
690
  `;
719
691
  const iconEl = document.createElement("div");
720
692
  iconEl.style.cssText = `
721
- width:28px;height:28px;border-radius:8px;flex-shrink:0;
693
+ width:28px;height:28px;border-radius:${RADIUS.sm}px;flex-shrink:0;
722
694
  display:flex;align-items:center;justify-content:center;
723
695
  font-size:13px;
724
696
  background:${this.theme.primary}12;
@@ -946,7 +918,7 @@ export class SmartComplyFlow {
946
918
  // Dropdown field
947
919
  const select = document.createElement("select");
948
920
  select.style.cssText = `
949
- width:100%;padding:13px 16px;border-radius:12px;
921
+ width:100%;padding:13px 16px;border-radius:${RADIUS.md}px;
950
922
  font-size:15px;
951
923
  background:${this.theme.inputBg};
952
924
  color:${this.theme.text};
@@ -981,7 +953,7 @@ export class SmartComplyFlow {
981
953
  fileInput.type = "file";
982
954
  fileInput.accept = "image/*";
983
955
  fileInput.style.cssText = `
984
- width:100%;padding:12px 16px;border-radius:12px;
956
+ width:100%;padding:12px 16px;border-radius:${RADIUS.md}px;
985
957
  font-size:14px;font-weight:500;
986
958
  background:${this.theme.inputBg};
987
959
  color:${this.theme.text};
@@ -1025,7 +997,7 @@ export class SmartComplyFlow {
1025
997
  if (rule?.numeric)
1026
998
  input.inputMode = "numeric";
1027
999
  input.style.cssText = `
1028
- width:100%;padding:14px 16px;border-radius:12px;
1000
+ width:100%;padding:14px 16px;border-radius:${RADIUS.md}px;
1029
1001
  font-size:16px;font-weight:500;
1030
1002
  background:${this.theme.inputBg};
1031
1003
  color:${this.theme.text};
@@ -1087,7 +1059,7 @@ export class SmartComplyFlow {
1087
1059
  errorEl.id = "sc-id-error";
1088
1060
  errorEl.style.cssText = `
1089
1061
  color:${this.theme.error};font-size:13px;display:none;
1090
- padding:8px 12px;border-radius:8px;background:${this.theme.errorBg};
1062
+ padding:8px 12px;border-radius:${RADIUS.sm}px;background:${this.theme.errorBg};
1091
1063
  `;
1092
1064
  container.appendChild(errorEl);
1093
1065
  // Submit
@@ -1108,7 +1080,7 @@ export class SmartComplyFlow {
1108
1080
  };
1109
1081
  submitBtn.addEventListener("click", async () => {
1110
1082
  submitBtn.disabled = true;
1111
- 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>`;
1083
+ submitBtn.innerHTML = `<span style="display:inline-flex;align-items:center;justify-content:center;gap:8px;">${renderInlineSpinner("#fff", 13)}Verifying...</span>`;
1112
1084
  errorEl.style.display = "none";
1113
1085
  try {
1114
1086
  this.verificationResult = await this.sdk.onboarding.verify({
@@ -1235,11 +1207,11 @@ export class SmartComplyFlow {
1235
1207
  // W13: Pre-warm camera silently while user reads this screen
1236
1208
  this._prewarmCamera();
1237
1209
  const iconWrap = document.createElement("div");
1238
- 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;`;
1239
- iconWrap.textContent = "🛡️";
1210
+ 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};`;
1211
+ iconWrap.innerHTML = icon("shield", 28);
1240
1212
  container.appendChild(iconWrap);
1241
1213
  const title = document.createElement("h2");
1242
- title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1214
+ title.style.cssText = `color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;margin:0;`;
1243
1215
  title.textContent = this._ivsName ? `Is this you?` : "Identity Confirmed";
1244
1216
  container.appendChild(title);
1245
1217
  if (this._ivsName) {
@@ -1303,12 +1275,25 @@ export class SmartComplyFlow {
1303
1275
  }
1304
1276
  // W13: Pre-warm camera while user reads
1305
1277
  this._prewarmCamera();
1278
+ // Step badge — pairs with DocumentCapture.showPreview's "Step 1 of 2"
1279
+ // badge shown right after capture, so this doesn't read as unexplained
1280
+ // repetition: that screen checked photo clarity, this one checks the
1281
+ // OCR-extracted data.
1282
+ const stepBadge = document.createElement("div");
1283
+ stepBadge.style.cssText = `
1284
+ display:inline-flex;align-items:center;gap:5px;
1285
+ padding:3px 11px;border-radius:${RADIUS.pill}px;
1286
+ font-size:10.5px;font-weight:600;letter-spacing:0.2px;
1287
+ color:${this.theme.primary};background:${this.theme.primary}15;border:1px solid ${this.theme.primary}30;
1288
+ `;
1289
+ stepBadge.textContent = "STEP 2 OF 2 · CHECK WHAT WE READ";
1290
+ container.appendChild(stepBadge);
1306
1291
  const iconWrap = document.createElement("div");
1307
- 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;`;
1308
- iconWrap.textContent = "📄";
1292
+ 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};`;
1293
+ iconWrap.innerHTML = icon("document", 28);
1309
1294
  container.appendChild(iconWrap);
1310
1295
  const title = document.createElement("h2");
1311
- title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1296
+ title.style.cssText = `color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;margin:0;`;
1312
1297
  title.textContent = "Are these your details?";
1313
1298
  container.appendChild(title);
1314
1299
  const sub = document.createElement("p");
@@ -1400,19 +1385,10 @@ export class SmartComplyFlow {
1400
1385
  // ── W6+W7+W8: OCR Gate (document verification) ───────────────────
1401
1386
  renderOcrGate(container) {
1402
1387
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:14px;padding:20px 0;text-align:center;";
1403
- const spinnerWrap = document.createElement("div");
1404
- spinnerWrap.style.cssText = "position:relative;width:52px;height:52px;flex-shrink:0;";
1405
- const ring = document.createElement("div");
1406
- 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;`;
1407
- const centre = document.createElement("div");
1408
- centre.style.cssText = "position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;";
1409
- centre.textContent = "📄";
1410
- spinnerWrap.appendChild(ring);
1411
- spinnerWrap.appendChild(centre);
1412
- container.appendChild(spinnerWrap);
1388
+ container.appendChild(renderSpinner(this.theme, { size: 52, icon: icon("document", 20) }));
1413
1389
  const statusEl = document.createElement("div");
1414
1390
  statusEl.setAttribute("aria-live", "polite");
1415
- statusEl.style.cssText = `color:${this.theme.text};font-size:15px;font-weight:600;`;
1391
+ statusEl.style.cssText = `color:${this.theme.text};font-size:${TYPE.base}px;font-weight:600;`;
1416
1392
  statusEl.textContent = "Reading your document…";
1417
1393
  container.appendChild(statusEl);
1418
1394
  const subEl = document.createElement("div");
@@ -1513,7 +1489,7 @@ export class SmartComplyFlow {
1513
1489
  icon.textContent = "📅";
1514
1490
  container.appendChild(icon);
1515
1491
  const t = document.createElement("h2");
1516
- t.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1492
+ t.style.cssText = `color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;margin:0;`;
1517
1493
  t.textContent = "Document Expired";
1518
1494
  container.appendChild(t);
1519
1495
  const d = document.createElement("p");
@@ -1539,7 +1515,7 @@ export class SmartComplyFlow {
1539
1515
  icon.textContent = "🔍";
1540
1516
  container.appendChild(icon);
1541
1517
  const t = document.createElement("h2");
1542
- t.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1518
+ t.style.cssText = `color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;margin:0;`;
1543
1519
  t.textContent = "Document Photo Unclear";
1544
1520
  container.appendChild(t);
1545
1521
  const msg = reason === "image_too_blurry" ? "Your document photo is too blurry. Please retake it in good lighting with a steady hand."
@@ -1576,11 +1552,11 @@ export class SmartComplyFlow {
1576
1552
  verificationResult: this.verificationResult || undefined,
1577
1553
  });
1578
1554
  const iconWrap = document.createElement("div");
1579
- 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;`;
1580
- iconWrap.textContent = "✓";
1555
+ 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;`;
1556
+ iconWrap.innerHTML = icon("check", 36);
1581
1557
  container.appendChild(iconWrap);
1582
1558
  const title = document.createElement("h2");
1583
- title.style.cssText = `color:${this.theme.text};font-size:22px;font-weight:700;margin:0;`;
1559
+ title.style.cssText = `color:${this.theme.text};font-size:${TYPE.headingLg}px;font-weight:700;margin:0;`;
1584
1560
  title.textContent = "You're all done!";
1585
1561
  container.appendChild(title);
1586
1562
  const desc = document.createElement("p");
@@ -1607,7 +1583,7 @@ export class SmartComplyFlow {
1607
1583
  icon.textContent = "⚠️";
1608
1584
  container.appendChild(icon);
1609
1585
  const t = document.createElement("h2");
1610
- t.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1586
+ t.style.cssText = `color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;margin:0;`;
1611
1587
  t.textContent = "Unable to Complete Verification";
1612
1588
  container.appendChild(t);
1613
1589
  const d = document.createElement("p");
@@ -1662,15 +1638,12 @@ export class SmartComplyFlow {
1662
1638
  }
1663
1639
  this.documentBlob = blob;
1664
1640
  // Show scanning state
1665
- captureContainer.innerHTML = `
1666
- <div style="display:flex;flex-direction:column;align-items:center;gap:14px;padding:28px 20px;">
1667
- <div style="position:relative;width:52px;height:52px;">
1668
- <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>
1669
- <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">🔍</div>
1670
- </div>
1641
+ captureContainer.innerHTML = "";
1642
+ captureContainer.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:14px;padding:28px 20px;";
1643
+ captureContainer.appendChild(renderSpinner(this.theme, { size: 52, icon: icon("search", 20) }));
1644
+ captureContainer.insertAdjacentHTML("beforeend", `
1671
1645
  <div style="color:${this.theme.text};font-size:14px;font-weight:600;">Scanning document...</div>
1672
- <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>
1673
- </div>`;
1646
+ <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>`);
1674
1647
  // Create liveness entry with document.
1675
1648
  // Use the first collected field value as identifier when available
1676
1649
  // (document flow has no data-verification step, so collectedFields is
@@ -1734,15 +1707,12 @@ export class SmartComplyFlow {
1734
1707
  console.log(`Retrying document processing (attempt ${retryCount}/${maxRetries})`);
1735
1708
  // Show retry message
1736
1709
  if (captureContainer) {
1737
- captureContainer.innerHTML = `
1738
- <div style="display:flex;flex-direction:column;align-items:center;gap:16px;padding:28px 20px;text-align:center;">
1739
- <div style="position:relative;width:52px;height:52px;">
1740
- <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>
1741
- <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">🔄</div>
1742
- </div>
1710
+ captureContainer.innerHTML = "";
1711
+ captureContainer.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:16px;padding:28px 20px;text-align:center;";
1712
+ captureContainer.appendChild(renderSpinner(this.theme, { size: 52, icon: icon("refresh", 20) }));
1713
+ captureContainer.insertAdjacentHTML("beforeend", `
1743
1714
  <div style="color:${this.theme.text};font-size:14px;font-weight:600;">Retrying...</div>
1744
- <div style="color:${this.theme.textSecondary};font-size:12px;max-width:240px;line-height:1.5;">Attempt ${retryCount} of ${maxRetries}</div>
1745
- </div>`;
1715
+ <div style="color:${this.theme.textSecondary};font-size:12px;max-width:240px;line-height:1.5;">Attempt ${retryCount} of ${maxRetries}</div>`);
1746
1716
  }
1747
1717
  // Wait before retry
1748
1718
  await new Promise(resolve => setTimeout(resolve, 2000));
@@ -1804,7 +1774,7 @@ export class SmartComplyFlow {
1804
1774
  const retryBtn = document.createElement("button");
1805
1775
  retryBtn.textContent = "Try Again";
1806
1776
  retryBtn.setAttribute("aria-label", "Retake document photo");
1807
- 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;`;
1777
+ retryBtn.style.cssText = `margin-top:8px;padding:12px 24px;border-radius:${RADIUS.sm}px;background:${this.theme.primary};color:#fff;border:none;font-size:14px;font-weight:600;cursor:pointer;`;
1808
1778
  retryBtn.addEventListener("click", () => this.showStep("document_capture"));
1809
1779
  wrap.appendChild(retryBtn);
1810
1780
  }
@@ -1865,6 +1835,12 @@ export class SmartComplyFlow {
1865
1835
  : "liveness_only",
1866
1836
  // B4: pass pre-warmed stream so camera.open() is skipped
1867
1837
  prewarmedStream: this._prewarmedStream || undefined,
1838
+ colors: {
1839
+ primary: this.theme.primary,
1840
+ success: this.theme.success,
1841
+ error: this.theme.error,
1842
+ warning: this.theme.warning,
1843
+ },
1868
1844
  }, ["BLINK"], this.livenessEntryId || undefined)
1869
1845
  .then((result) => {
1870
1846
  // W11: After submit, end user is done — show "You're all done" screen.
@@ -1906,14 +1882,14 @@ export class SmartComplyFlow {
1906
1882
  // /v1/sdk/liveness/submit/) are an operator-facing problem, not something
1907
1883
  // the end user can act on — never show "wallet"/"balance" language to them.
1908
1884
  const isBillingErr = /wallet|balance|insufficient.*fund|payment.*required/i.test(message);
1909
- const errorIcon = isCameraErr ? "📷"
1910
- : isNetworkErr ? "📡"
1911
- : isAuthErr ? "⏱"
1912
- : isDocQuality ? "🔍"
1913
- : isExpiredDoc ? "📅"
1914
- : isFaceMatch ? "🤳"
1915
- : isBillingErr ? "⚠️"
1916
- : "✕";
1885
+ const errorIconName = isCameraErr ? "camera"
1886
+ : isNetworkErr ? "network"
1887
+ : isAuthErr ? "clock"
1888
+ : isDocQuality ? "search"
1889
+ : isExpiredDoc ? "calendar"
1890
+ : isFaceMatch ? "face"
1891
+ : isBillingErr ? "warning"
1892
+ : "close";
1917
1893
  const errorTitle = isCameraErr ? "Camera Access Required"
1918
1894
  : isNetworkErr ? "Connection Problem"
1919
1895
  : isAuthErr ? "Session Expired"
@@ -1942,14 +1918,14 @@ export class SmartComplyFlow {
1942
1918
  iconWrap.style.cssText = `
1943
1919
  width:72px;height:72px;border-radius:50%;
1944
1920
  display:flex;align-items:center;justify-content:center;
1945
- font-size:28px;
1921
+ color:${this.theme.error};
1946
1922
  background:${this.theme.errorBg};
1947
1923
  border:3px solid ${this.theme.error};
1948
1924
  `;
1949
- iconWrap.textContent = errorIcon;
1925
+ iconWrap.innerHTML = icon(errorIconName, 28);
1950
1926
  wrapper.appendChild(iconWrap);
1951
1927
  const title = document.createElement("h2");
1952
- title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
1928
+ title.style.cssText = `color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;margin:0;`;
1953
1929
  title.textContent = errorTitle;
1954
1930
  wrapper.appendChild(title);
1955
1931
  const desc = document.createElement("p");
@@ -1988,7 +1964,7 @@ export class SmartComplyFlow {
1988
1964
  this.headerBackBtn.innerHTML = "&#8592;";
1989
1965
  this.headerBackBtn.setAttribute("aria-label", "Go back to previous step");
1990
1966
  this.headerBackBtn.style.cssText = `
1991
- width:30px;height:30px;border-radius:8px;flex-shrink:0;
1967
+ width:30px;height:30px;border-radius:${RADIUS.sm}px;flex-shrink:0;
1992
1968
  display:none;align-items:center;justify-content:center;
1993
1969
  border:1.5px solid ${this.theme.border};cursor:pointer;
1994
1970
  font-size:16px;line-height:1;
@@ -2046,7 +2022,7 @@ export class SmartComplyFlow {
2046
2022
  closeBtn.innerHTML = "✕";
2047
2023
  closeBtn.setAttribute("aria-label", "Close identity verification");
2048
2024
  closeBtn.style.cssText = `
2049
- width:32px;height:32px;border-radius:8px;flex-shrink:0;
2025
+ width:32px;height:32px;border-radius:${RADIUS.sm}px;flex-shrink:0;
2050
2026
  display:flex;align-items:center;justify-content:center;
2051
2027
  border:none;cursor:pointer;font-size:16px;
2052
2028
  background:${this.theme.shimmer};color:${this.theme.textMuted};
@@ -2083,7 +2059,7 @@ export class SmartComplyFlow {
2083
2059
  createStepTitle(text) {
2084
2060
  const el = document.createElement("h3");
2085
2061
  el.style.cssText = `
2086
- color:${this.theme.text};font-size:18px;font-weight:700;
2062
+ color:${this.theme.text};font-size:${TYPE.heading}px;font-weight:700;
2087
2063
  margin:0;
2088
2064
  `;
2089
2065
  el.textContent = text;
@@ -2093,7 +2069,7 @@ export class SmartComplyFlow {
2093
2069
  const card = document.createElement("button");
2094
2070
  card.setAttribute("aria-label", subtitle ? `${title}: ${subtitle}` : title);
2095
2071
  card.style.cssText = `
2096
- width:100%;padding:13px 16px;border-radius:12px;
2072
+ width:100%;padding:13px 16px;border-radius:${RADIUS.md}px;
2097
2073
  background:${this.theme.cardBg};
2098
2074
  border:2px solid ${this.theme.border};
2099
2075
  cursor:pointer;text-align:left;
@@ -2133,7 +2109,7 @@ export class SmartComplyFlow {
2133
2109
  const btn = document.createElement("button");
2134
2110
  btn.textContent = text;
2135
2111
  btn.style.cssText = `
2136
- width:100%;padding:14px;border-radius:12px;
2112
+ width:100%;padding:14px;border-radius:${RADIUS.md}px;
2137
2113
  font-size:15px;font-weight:600;
2138
2114
  background:${this.theme.primary};color:#fff;
2139
2115
  border:none;cursor:pointer;
@@ -2154,7 +2130,7 @@ export class SmartComplyFlow {
2154
2130
  const btn = document.createElement("button");
2155
2131
  btn.textContent = text;
2156
2132
  btn.style.cssText = `
2157
- width:100%;padding:12px;border-radius:12px;
2133
+ width:100%;padding:12px;border-radius:${RADIUS.md}px;
2158
2134
  font-size:14px;font-weight:500;
2159
2135
  background:transparent;color:${this.theme.textSecondary};
2160
2136
  border:none;cursor:pointer;
@@ -2292,9 +2268,6 @@ export class SmartComplyFlow {
2292
2268
  const style = document.createElement("style");
2293
2269
  style.id = "sc-flow-styles";
2294
2270
  style.textContent = `
2295
- @keyframes sc-spin {
2296
- to { transform: rotate(360deg); }
2297
- }
2298
2271
  @keyframes sc-pop {
2299
2272
  0% { transform: scale(0.8); opacity: 0; }
2300
2273
  50% { transform: scale(1.05); }