smartcomply-web-sdk 1.0.51 → 1.0.53

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 (35) hide show
  1. package/dist/camera/ActionDetector.d.ts +0 -5
  2. package/dist/camera/ActionDetector.d.ts.map +1 -1
  3. package/dist/camera/ActionDetector.js +1 -12
  4. package/dist/camera/ActionDetector.js.map +1 -1
  5. package/dist/esm/camera/ActionDetector.d.ts +0 -5
  6. package/dist/esm/camera/ActionDetector.d.ts.map +1 -1
  7. package/dist/esm/camera/ActionDetector.js +1 -12
  8. package/dist/esm/camera/ActionDetector.js.map +1 -1
  9. package/dist/esm/flow/SmartComplyFlow.js +144 -144
  10. package/dist/esm/flow/SmartComplyFlow.js.map +1 -1
  11. package/dist/esm/modules/liveness/LivenessUI.d.ts +10 -2
  12. package/dist/esm/modules/liveness/LivenessUI.d.ts.map +1 -1
  13. package/dist/esm/modules/liveness/LivenessUI.js +129 -7
  14. package/dist/esm/modules/liveness/LivenessUI.js.map +1 -1
  15. package/dist/esm/modules/liveness/liveness.js +3 -3
  16. package/dist/esm/modules/liveness/liveness.js.map +1 -1
  17. package/dist/esm/utils/constants.d.ts +0 -2
  18. package/dist/esm/utils/constants.d.ts.map +1 -1
  19. package/dist/esm/utils/constants.js +0 -2
  20. package/dist/esm/utils/constants.js.map +1 -1
  21. package/dist/flow/SmartComplyFlow.js +144 -144
  22. package/dist/flow/SmartComplyFlow.js.map +1 -1
  23. package/dist/modules/liveness/LivenessUI.d.ts +10 -2
  24. package/dist/modules/liveness/LivenessUI.d.ts.map +1 -1
  25. package/dist/modules/liveness/LivenessUI.js +129 -7
  26. package/dist/modules/liveness/LivenessUI.js.map +1 -1
  27. package/dist/modules/liveness/liveness.js +3 -3
  28. package/dist/modules/liveness/liveness.js.map +1 -1
  29. package/dist/smartcomply.browser.js +206 -107
  30. package/dist/smartcomply.browser.js.map +3 -3
  31. package/dist/utils/constants.d.ts +0 -2
  32. package/dist/utils/constants.d.ts.map +1 -1
  33. package/dist/utils/constants.js +1 -3
  34. package/dist/utils/constants.js.map +1 -1
  35. package/package.json +1 -1
@@ -4460,8 +4460,7 @@ var SmartComplySDK = (() => {
4460
4460
  var BLINK_HOLD_FRAMES = 2;
4461
4461
  var YAW_MOTION_THRESHOLD_DEG = 6;
4462
4462
  var ActionDetector = class {
4463
- /** Kept for API compatibility with callers that still pass a list in. Unused internally. */
4464
- constructor(_requiredActions) {
4463
+ constructor() {
4465
4464
  this.blinkHoldCount = 0;
4466
4465
  this.blinkDetected = false;
4467
4466
  this.motionDetected = false;
@@ -4498,24 +4497,13 @@ var SmartComplySDK = (() => {
4498
4497
  faceDetected,
4499
4498
  blinkDetected: this.blinkDetected,
4500
4499
  motionDetected: this.motionDetected,
4501
- livenessConfirmed: this.blinkDetected || this.motionDetected,
4502
- progress: 0
4503
- // caller fills in time-based progress
4500
+ livenessConfirmed: this.blinkDetected || this.motionDetected
4504
4501
  };
4505
4502
  }
4506
4503
  getHeadYaw(matrix) {
4507
4504
  if (!matrix || matrix.length < 16) return 0;
4508
4505
  return Math.atan2(-matrix[2], matrix[0]) * (180 / Math.PI);
4509
4506
  }
4510
- livenessConfirmed() {
4511
- return this.blinkDetected || this.motionDetected;
4512
- }
4513
- reset() {
4514
- this.blinkHoldCount = 0;
4515
- this.blinkDetected = false;
4516
- this.motionDetected = false;
4517
- this.baselineYaw = null;
4518
- }
4519
4507
  };
4520
4508
 
4521
4509
  // src/modules/liveness/LivenessUI.ts
@@ -4533,6 +4521,20 @@ var SmartComplySDK = (() => {
4533
4521
  0%,100% { opacity:0.3; transform:scale(0.85); }
4534
4522
  50% { opacity:1; transform:scale(1); }
4535
4523
  }
4524
+ @keyframes sc-scanline-sweep {
4525
+ 0% { transform:translateY(-4%); opacity:0; }
4526
+ 8% { opacity:0.9; }
4527
+ 92% { opacity:0.9; }
4528
+ 100% { transform:translateY(104%); opacity:0; }
4529
+ }
4530
+ @keyframes sc-corner-pulse {
4531
+ 0%,100% { opacity:0.55; }
4532
+ 50% { opacity:1; }
4533
+ }
4534
+ @keyframes sc-oval-breathe {
4535
+ 0%,100% { opacity:0.5; }
4536
+ 50% { opacity:0.85; }
4537
+ }
4536
4538
  `;
4537
4539
  document.head.appendChild(s2);
4538
4540
  }
@@ -4542,11 +4544,15 @@ var SmartComplySDK = (() => {
4542
4544
  this.videoEl = null;
4543
4545
  this.instructionEl = null;
4544
4546
  this.confidenceRingEl = null;
4547
+ this.bgOvalEl = null;
4548
+ this.guideWrapEl = null;
4549
+ this.scanlineEl = null;
4545
4550
  this.overlayEl = null;
4546
4551
  this.dotsEl = null;
4547
4552
  this.docCaptureRoot = null;
4548
4553
  this.scanStartMs = 0;
4549
4554
  this.scanDurationMs = 0;
4555
+ this.faceTracked = false;
4550
4556
  }
4551
4557
  mount(container, challenge) {
4552
4558
  injectLivenessKeyframes();
@@ -4564,41 +4570,110 @@ var SmartComplySDK = (() => {
4564
4570
  this.videoEl.muted = true;
4565
4571
  this.videoEl.style.cssText = `
4566
4572
  width:100%;height:100%;display:block;
4573
+ filter:brightness(1.03) contrast(1.03);
4567
4574
  transform:scaleX(-1);object-fit:cover;
4575
+ `;
4576
+ const vignette = document.createElement("div");
4577
+ vignette.style.cssText = `
4578
+ position:absolute;inset:0;pointer-events:none;
4579
+ background:radial-gradient(ellipse 34% 44% at 50% 47%, transparent 55%, rgba(0,0,0,0.55) 100%);
4568
4580
  `;
4569
4581
  const guideWrap = document.createElement("div");
4570
4582
  guideWrap.style.cssText = `
4571
4583
  position:absolute;top:50%;left:50%;
4572
4584
  transform:translate(-50%,-52%);
4573
4585
  width:64%;aspect-ratio:140/180;pointer-events:none;
4586
+ filter:drop-shadow(0 0 18px rgba(59,130,246,0.35));
4587
+ transition:filter 0.3s ease;
4574
4588
  `;
4589
+ this.guideWrapEl = guideWrap;
4575
4590
  const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
4576
4591
  svg.setAttribute("viewBox", "0 0 140 180");
4577
- svg.style.cssText = "width:100%;height:100%;";
4592
+ svg.style.cssText = "width:100%;height:100%;overflow:visible;";
4593
+ const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
4594
+ const clip = document.createElementNS("http://www.w3.org/2000/svg", "clipPath");
4595
+ clip.setAttribute("id", "sc-oval-clip");
4596
+ const clipOval = document.createElementNS("http://www.w3.org/2000/svg", "ellipse");
4597
+ clipOval.setAttribute("cx", "70");
4598
+ clipOval.setAttribute("cy", "90");
4599
+ clipOval.setAttribute("rx", "64");
4600
+ clipOval.setAttribute("ry", "86");
4601
+ clip.appendChild(clipOval);
4602
+ defs.appendChild(clip);
4603
+ svg.appendChild(defs);
4604
+ const trackOval = document.createElementNS("http://www.w3.org/2000/svg", "ellipse");
4605
+ trackOval.setAttribute("cx", "70");
4606
+ trackOval.setAttribute("cy", "90");
4607
+ trackOval.setAttribute("rx", "58");
4608
+ trackOval.setAttribute("ry", "80");
4609
+ trackOval.setAttribute("fill", "none");
4610
+ trackOval.setAttribute("stroke", "rgba(255,255,255,0.16)");
4611
+ trackOval.setAttribute("stroke-width", "1");
4612
+ trackOval.setAttribute("stroke-dasharray", "2 5");
4613
+ svg.appendChild(trackOval);
4578
4614
  const bgOval = document.createElementNS("http://www.w3.org/2000/svg", "ellipse");
4579
4615
  bgOval.setAttribute("cx", "70");
4580
4616
  bgOval.setAttribute("cy", "90");
4581
4617
  bgOval.setAttribute("rx", "64");
4582
4618
  bgOval.setAttribute("ry", "86");
4583
4619
  bgOval.setAttribute("fill", "none");
4584
- bgOval.setAttribute("stroke", "rgba(255,255,255,0.35)");
4585
- bgOval.setAttribute("stroke-width", "2");
4620
+ bgOval.setAttribute("stroke", "rgba(255,255,255,0.4)");
4621
+ bgOval.setAttribute("stroke-width", "1.5");
4622
+ bgOval.style.cssText = "animation:sc-oval-breathe 2.4s ease-in-out infinite;transition:stroke 0.3s ease;";
4623
+ this.bgOvalEl = bgOval;
4586
4624
  const confOval = document.createElementNS("http://www.w3.org/2000/svg", "ellipse");
4587
4625
  confOval.setAttribute("cx", "70");
4588
4626
  confOval.setAttribute("cy", "90");
4589
4627
  confOval.setAttribute("rx", "64");
4590
4628
  confOval.setAttribute("ry", "86");
4591
4629
  confOval.setAttribute("fill", "none");
4592
- confOval.setAttribute("stroke", "#22c55e");
4593
- confOval.setAttribute("stroke-width", "3");
4630
+ confOval.setAttribute("stroke", "#3b82f6");
4631
+ confOval.setAttribute("stroke-width", "2.5");
4632
+ confOval.setAttribute("stroke-linecap", "round");
4594
4633
  const circ = 2 * Math.PI * Math.sqrt((64 * 64 + 86 * 86) / 2);
4595
4634
  confOval.setAttribute("stroke-dasharray", `${circ}`);
4596
4635
  confOval.setAttribute("stroke-dashoffset", `${circ}`);
4597
- confOval.style.cssText = "transition:stroke-dashoffset 0.2s linear;transform:rotate(-90deg);transform-origin:70px 90px;";
4636
+ confOval.style.cssText = "transition:stroke-dashoffset 0.2s linear,stroke 0.3s ease;transform:rotate(-90deg);transform-origin:70px 90px;";
4598
4637
  this.confidenceRingEl = confOval;
4638
+ const bracketGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
4639
+ bracketGroup.setAttribute("stroke", "#3b82f6");
4640
+ bracketGroup.setAttribute("stroke-width", "3");
4641
+ bracketGroup.setAttribute("stroke-linecap", "round");
4642
+ bracketGroup.setAttribute("fill", "none");
4643
+ bracketGroup.style.cssText = "animation:sc-corner-pulse 2s ease-in-out infinite;";
4644
+ const bracketLen = 14;
4645
+ const bx0 = 70 - 64 * 0.74, bx1 = 70 + 64 * 0.74;
4646
+ const by0 = 90 - 86 * 0.82, by1 = 90 + 86 * 0.82;
4647
+ const brackets = [
4648
+ `M${bx0},${by0 + bracketLen} L${bx0},${by0} L${bx0 + bracketLen},${by0}`,
4649
+ `M${bx1 - bracketLen},${by0} L${bx1},${by0} L${bx1},${by0 + bracketLen}`,
4650
+ `M${bx0},${by1 - bracketLen} L${bx0},${by1} L${bx0 + bracketLen},${by1}`,
4651
+ `M${bx1 - bracketLen},${by1} L${bx1},${by1} L${bx1},${by1 - bracketLen}`
4652
+ ];
4653
+ for (const d2 of brackets) {
4654
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
4655
+ path.setAttribute("d", d2);
4656
+ bracketGroup.appendChild(path);
4657
+ }
4599
4658
  svg.appendChild(bgOval);
4600
4659
  svg.appendChild(confOval);
4660
+ svg.appendChild(bracketGroup);
4601
4661
  guideWrap.appendChild(svg);
4662
+ const scanlineClipWrap = document.createElement("div");
4663
+ scanlineClipWrap.style.cssText = `
4664
+ position:absolute;inset:0;
4665
+ clip-path:ellipse(45.7% 47.8% at 50% 50%);
4666
+ overflow:hidden;
4667
+ `;
4668
+ const scanline = document.createElement("div");
4669
+ scanline.style.cssText = `
4670
+ position:absolute;left:0;right:0;height:26%;top:0;
4671
+ background:linear-gradient(180deg,transparent 0%,rgba(59,130,246,0.32) 45%,rgba(147,197,253,0.55) 50%,rgba(59,130,246,0.32) 55%,transparent 100%);
4672
+ animation:sc-scanline-sweep 2.6s ease-in-out infinite;
4673
+ `;
4674
+ this.scanlineEl = scanline;
4675
+ scanlineClipWrap.appendChild(scanline);
4676
+ guideWrap.appendChild(scanlineClipWrap);
4602
4677
  const camTop = document.createElement("div");
4603
4678
  camTop.style.cssText = `
4604
4679
  position:absolute;top:0;left:0;right:0;
@@ -4622,13 +4697,14 @@ var SmartComplySDK = (() => {
4622
4697
  for (let i2 = 0; i2 < 3; i2++) {
4623
4698
  const dot = document.createElement("div");
4624
4699
  dot.style.cssText = `
4625
- width:6px;height:6px;border-radius:50%;background:#22c55e;
4700
+ width:6px;height:6px;border-radius:50%;background:#3b82f6;
4626
4701
  animation:sc-scan-dot 1.1s ease-in-out ${i2 * 0.18}s infinite;
4627
4702
  `;
4628
4703
  this.dotsEl.appendChild(dot);
4629
4704
  }
4630
4705
  camBottom.appendChild(this.dotsEl);
4631
4706
  this.root.appendChild(this.videoEl);
4707
+ this.root.appendChild(vignette);
4632
4708
  this.root.appendChild(guideWrap);
4633
4709
  this.root.appendChild(camTop);
4634
4710
  this.root.appendChild(camBottom);
@@ -4662,6 +4738,25 @@ var SmartComplySDK = (() => {
4662
4738
  this.confidenceRingEl.setAttribute("stroke-dashoffset", `${circ * (1 - pct)}`);
4663
4739
  this.confidenceRingEl.setAttribute("stroke", livenessConfirmed ? "#22c55e" : "#3b82f6");
4664
4740
  }
4741
+ /**
4742
+ * Reflects whether a face is currently detected in-frame — brightens the
4743
+ * oval guide's glow/brackets when locked on, and dims the scan-line sweep
4744
+ * so the UI visibly distinguishes "searching" from "actively scanning".
4745
+ */
4746
+ setFaceTracking(tracked) {
4747
+ if (this.faceTracked === tracked) return;
4748
+ this.faceTracked = tracked;
4749
+ if (this.guideWrapEl) {
4750
+ this.guideWrapEl.style.filter = tracked ? "drop-shadow(0 0 22px rgba(34,197,94,0.45))" : "drop-shadow(0 0 18px rgba(59,130,246,0.35))";
4751
+ }
4752
+ if (this.bgOvalEl) {
4753
+ this.bgOvalEl.setAttribute("stroke", tracked ? "rgba(74,222,128,0.55)" : "rgba(255,255,255,0.4)");
4754
+ }
4755
+ if (this.scanlineEl) {
4756
+ this.scanlineEl.style.animationPlayState = tracked ? "running" : "paused";
4757
+ this.scanlineEl.style.opacity = tracked ? "1" : "0.4";
4758
+ }
4759
+ }
4665
4760
  updateInstruction(text) {
4666
4761
  if (this.instructionEl) this.instructionEl.textContent = text;
4667
4762
  }
@@ -4803,6 +4898,10 @@ var SmartComplySDK = (() => {
4803
4898
  this.confidenceRingEl = null;
4804
4899
  this.overlayEl = null;
4805
4900
  this.dotsEl = null;
4901
+ this.bgOvalEl = null;
4902
+ this.guideWrapEl = null;
4903
+ this.scanlineEl = null;
4904
+ this.faceTracked = false;
4806
4905
  }
4807
4906
  };
4808
4907
 
@@ -4970,7 +5069,7 @@ var SmartComplySDK = (() => {
4970
5069
  tempVideo.muted = true;
4971
5070
  tempVideo.playsInline = true;
4972
5071
  await tempVideo.play();
4973
- ui2.mount(container, { actions: [], time_limit_seconds: 0, instruction: "Centre your face in the oval" });
5072
+ ui2.mount(container, { time_limit_seconds: 0, instruction: "Centre your face in the oval" });
4974
5073
  const uiVideo = ui2.getVideoElement();
4975
5074
  if (uiVideo) {
4976
5075
  uiVideo.srcObject = stream;
@@ -4983,7 +5082,6 @@ var SmartComplySDK = (() => {
4983
5082
  ui2.updateInstruction("Centre your face in the oval");
4984
5083
  const videoForDetection = uiVideo || tempVideo;
4985
5084
  await new Promise((resolve) => {
4986
- let attempts = 0;
4987
5085
  let stableFrames = 0;
4988
5086
  let missedFrames = 0;
4989
5087
  const STABLE_REQUIRED = 4;
@@ -4997,6 +5095,7 @@ var SmartComplySDK = (() => {
4997
5095
  }
4998
5096
  if (videoForDetection.readyState >= 2) {
4999
5097
  const detection = faceEngine.detect(videoForDetection, performance.now());
5098
+ ui2.setFaceTracking(detection.faceDetected);
5000
5099
  if (detection.faceDetected) {
5001
5100
  stableFrames++;
5002
5101
  missedFrames = 0;
@@ -5045,7 +5144,6 @@ var SmartComplySDK = (() => {
5045
5144
  tempVideo.srcObject = null;
5046
5145
  tempVideo.remove();
5047
5146
  const uiChallenge = {
5048
- actions: entry.challenge_actions,
5049
5147
  time_limit_seconds: LIVENESS_TIME_LIMIT_SECONDS,
5050
5148
  instruction: "Blink and look around naturally"
5051
5149
  };
@@ -5072,6 +5170,7 @@ var SmartComplySDK = (() => {
5072
5170
  const detection = faceEngine.detect(videoEl, performance.now());
5073
5171
  const scan = actionDetector.check(detection);
5074
5172
  ui2.updateScanProgress(scan.livenessConfirmed);
5173
+ ui2.setFaceTracking(detection.faceDetected);
5075
5174
  if (!detection.faceDetected) {
5076
5175
  ui2.updateInstruction("Move your face into the oval");
5077
5176
  } else {
@@ -6347,13 +6446,13 @@ var SmartComplySDK = (() => {
6347
6446
 
6348
6447
  // src/flow/SmartComplyFlow.ts
6349
6448
  var COUNTRY_LABELS = {
6350
- nigeria: "🇳🇬 Nigeria",
6351
- global: "🌍 Other Countries",
6352
- ghana: "🇬🇭 Ghana",
6353
- kenya: "🇰🇪 Kenya",
6354
- south_africa: "🇿🇦 South Africa",
6355
- united_states: "🇺🇸 United States",
6356
- united_kingdom: "🇬🇧 United Kingdom"
6449
+ nigeria: "🇳🇬 Nigeria",
6450
+ global: "🌍 Other Countries",
6451
+ ghana: "🇬🇭 Ghana",
6452
+ kenya: "🇰🇪 Kenya",
6453
+ south_africa: "🇿🇦 South Africa",
6454
+ united_states: "🇺🇸 United States",
6455
+ united_kingdom: "🇬🇧 United Kingdom"
6357
6456
  };
6358
6457
  function isUploadFieldType(type) {
6359
6458
  return type === "upload" || type === "image" || type === "file";
@@ -6367,37 +6466,37 @@ var SmartComplySDK = (() => {
6367
6466
  return t2.includes("voter") || t2.includes("driver") || t2.includes("license") || t2.includes("national id") || t2.includes("national id card");
6368
6467
  };
6369
6468
  if (u2.includes("BVN") || u2.includes("BANK VERIFICATION")) {
6370
- return { icon: "🏦", desc: "Enter your 11-digit Bank Verification Number (BVN)", requiresBack: false };
6469
+ return { icon: "🏦", desc: "Enter your 11-digit Bank Verification Number (BVN)", requiresBack: false };
6371
6470
  }
6372
6471
  if (u2.includes("NIN") || u2.includes("NATIONAL IDENTITY NUMBER")) {
6373
- return { icon: "🆔", desc: "Enter your 11-digit National Identification Number (NIN)", requiresBack: false };
6472
+ return { icon: "🆔", desc: "Enter your 11-digit National Identification Number (NIN)", requiresBack: false };
6374
6473
  }
6375
6474
  if (u2.includes("GHANA")) {
6376
- if (isDocumentFlow) return { icon: "🪪", desc: "Capture or upload your Ghana Card", requiresBack: true };
6377
- return { icon: "🪪", desc: "Enter your Ghana Card number", requiresBack: false };
6475
+ if (isDocumentFlow) return { icon: "🪪", desc: "Capture or upload your Ghana Card", requiresBack: true };
6476
+ return { icon: "🪪", desc: "Enter your Ghana Card number", requiresBack: false };
6378
6477
  }
6379
6478
  if (u2.includes("SSNIT"))
6380
- return { icon: "🏛️", desc: "Enter your SSNIT number", requiresBack: false };
6479
+ return { icon: "🏛️", desc: "Enter your SSNIT number", requiresBack: false };
6381
6480
  if (u2.includes("KENYA") || u2.includes("NATIONAL") && u2.includes("KENYA"))
6382
- return { icon: "🆔", desc: "Enter your Kenya National ID number", requiresBack: false };
6481
+ return { icon: "🆔", desc: "Enter your Kenya National ID number", requiresBack: false };
6383
6482
  if (u2.includes("TIN") || u2.includes("TAX IDENTIFICATION"))
6384
- return { icon: "🧾", desc: "Enter your Tax Identification Number (TIN)", requiresBack: false };
6483
+ return { icon: "🧾", desc: "Enter your Tax Identification Number (TIN)", requiresBack: false };
6385
6484
  if (u2.includes("CAC") || u2.includes("CORPORATE AFFAIRS") || u2.includes("COMPANY"))
6386
- return { icon: "🏢", desc: advanced ? "Enter RC number, company name & type" : "Enter your company registration number", requiresBack: false };
6485
+ return { icon: "🏢", desc: advanced ? "Enter RC number, company name & type" : "Enter your company registration number", requiresBack: false };
6387
6486
  if (u2.includes("VOTER") || u2.includes("PVC")) {
6388
6487
  const requiresBack = requiresBackSide(typeName);
6389
6488
  return {
6390
- icon: "🗳️",
6489
+ icon: "🗳️",
6391
6490
  desc: isDocumentFlow ? `Capture or upload your voter's card${requiresBack ? " (front & back)" : ""}` : "Enter your voter ID number",
6392
6491
  requiresBack: isDocumentFlow ? requiresBack : false
6393
6492
  };
6394
6493
  }
6395
6494
  if (u2.includes("PASSPORT"))
6396
- return { icon: "📘", desc: isDocumentFlow ? "Capture or upload your passport" : "Enter your passport number", requiresBack: false };
6495
+ return { icon: "📘", desc: isDocumentFlow ? "Capture or upload your passport" : "Enter your passport number", requiresBack: false };
6397
6496
  if (u2.includes("DRIVER") || u2.includes("LICENSE") || u2.includes("LICENCE")) {
6398
6497
  const requiresBack = requiresBackSide(typeName);
6399
6498
  return {
6400
- icon: "🚗",
6499
+ icon: "🚗",
6401
6500
  desc: isDocumentFlow ? `Capture or upload your driver's license${requiresBack ? " (front & back)" : ""}` : "Enter your driver's license number",
6402
6501
  requiresBack: isDocumentFlow ? requiresBack : false
6403
6502
  };
@@ -6405,15 +6504,15 @@ var SmartComplySDK = (() => {
6405
6504
  if (u2.includes("NATIONAL ID") || u2.includes("NATIONAL") && u2.includes("CARD")) {
6406
6505
  const requiresBack = requiresBackSide(typeName);
6407
6506
  return {
6408
- icon: "🪪",
6507
+ icon: "🪪",
6409
6508
  desc: isDocumentFlow ? `Capture or upload your ID card${requiresBack ? " (front & back)" : ""}` : "Enter your National ID number",
6410
6509
  requiresBack: isDocumentFlow ? requiresBack : false
6411
6510
  };
6412
6511
  }
6413
6512
  if (u2.includes("LIVENESS") || u2.includes("FACE COMPARISON") || u2.includes("FACE MATCH"))
6414
- return { icon: "🤳", desc: "We'll capture a short selfie video to verify you", requiresBack: false };
6513
+ return { icon: "🤳", desc: "We'll capture a short selfie video to verify you", requiresBack: false };
6415
6514
  return {
6416
- icon: isDocumentFlow ? "📄" : "🆔",
6515
+ icon: isDocumentFlow ? "📄" : "🆔",
6417
6516
  desc: isDocumentFlow ? "Capture or upload your document" : "Enter your ID number",
6418
6517
  requiresBack: false
6419
6518
  };
@@ -6551,7 +6650,7 @@ var SmartComplySDK = (() => {
6551
6650
  this.livenessEntryId = null;
6552
6651
  this.isDestroyed = false;
6553
6652
  this.headerBackBtn = null;
6554
- // Retry safety — max 3 confirmation rejections or liveness failures per session
6653
+ // Retry safety — max 3 confirmation rejections or liveness failures per session
6555
6654
  this._confirmRetries = 0;
6556
6655
  this._livenessFailures = 0;
6557
6656
  // IVS result stored for confirm_identity screen
@@ -6566,9 +6665,9 @@ var SmartComplySDK = (() => {
6566
6665
  this._ocrExpirySoon = false;
6567
6666
  // Camera pre-warm stream (created during confirmation screen, used in liveness)
6568
6667
  this._prewarmedStream = null;
6569
- // OCR gate poll timer — stored so it can be cancelled on navigation or close
6668
+ // OCR gate poll timer — stored so it can be cancelled on navigation or close
6570
6669
  this._ocrPollTimer = null;
6571
- // UI timeout from _startStatusPolling — stored so close() can cancel it
6670
+ // UI timeout from _startStatusPolling — stored so close() can cancel it
6572
6671
  this._uiTimeoutId = null;
6573
6672
  // Session expiry
6574
6673
  this.sessionExpiryTimer = null;
@@ -6643,9 +6742,9 @@ var SmartComplySDK = (() => {
6643
6742
  }
6644
6743
  this.options.onClose?.();
6645
6744
  }
6646
- // ─────────────────────────────────────────────────────────────────
6745
+ // ─────────────────────────────────────────────────────────────────
6647
6746
  // Initialization
6648
- // ─────────────────────────────────────────────────────────────────
6747
+ // ─────────────────────────────────────────────────────────────────
6649
6748
  mount() {
6650
6749
  this.injectStyles();
6651
6750
  this.overlay = document.createElement("div");
@@ -6730,9 +6829,9 @@ var SmartComplySDK = (() => {
6730
6829
  this.showErrorStep(err.message || "Failed to initialize. Please try again.");
6731
6830
  }
6732
6831
  }
6733
- // ─────────────────────────────────────────────────────────────────
6832
+ // ─────────────────────────────────────────────────────────────────
6734
6833
  // Step rendering
6735
- // ─────────────────────────────────────────────────────────────────
6834
+ // ─────────────────────────────────────────────────────────────────
6736
6835
  showStep(step) {
6737
6836
  this.currentStep = step;
6738
6837
  if (!this.contentArea) return;
@@ -6807,7 +6906,7 @@ var SmartComplySDK = (() => {
6807
6906
  wrapper.style.transform = "translateX(0)";
6808
6907
  });
6809
6908
  }
6810
- // ── Loading ─────────────────────────────────────────────────────
6909
+ // ── Loading ─────────────────────────────────────────────────────
6811
6910
  renderLoading(container) {
6812
6911
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px;gap:16px;";
6813
6912
  const spinnerWrap = document.createElement("div");
@@ -6838,7 +6937,7 @@ var SmartComplySDK = (() => {
6838
6937
  sub.textContent = "Connecting to the verification service...";
6839
6938
  container.appendChild(sub);
6840
6939
  }
6841
- // ── Welcome ────────────────────────────────────────────────────
6940
+ // ── Welcome ────────────────────────────────────────────────────
6842
6941
  renderWelcome(container) {
6843
6942
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:12px;";
6844
6943
  const isDocFlow = this.isDocumentFlow();
@@ -6852,7 +6951,7 @@ var SmartComplySDK = (() => {
6852
6951
  background:linear-gradient(135deg,${this.theme.primary}22,${this.theme.primaryGlow});
6853
6952
  border:1.5px solid ${this.theme.primary}33;
6854
6953
  `;
6855
- iconWrap.textContent = isDocFlow ? "🪪" : "🛡️";
6954
+ iconWrap.textContent = isDocFlow ? "🪪" : "🛡️";
6856
6955
  topRow.appendChild(iconWrap);
6857
6956
  const badge = document.createElement("div");
6858
6957
  badge.style.cssText = `
@@ -6863,7 +6962,7 @@ var SmartComplySDK = (() => {
6863
6962
  color:${this.theme.primary};
6864
6963
  border:1px solid ${this.theme.primary}30;
6865
6964
  `;
6866
- badge.textContent = isDocFlow ? "📄 Document Verification" : "🔍 Data Verification";
6965
+ badge.textContent = isDocFlow ? "📄 Document Verification" : "🔍 Data Verification";
6867
6966
  topRow.appendChild(badge);
6868
6967
  container.appendChild(topRow);
6869
6968
  const titleWrap = document.createElement("div");
@@ -6885,13 +6984,13 @@ var SmartComplySDK = (() => {
6885
6984
  titleWrap.appendChild(desc);
6886
6985
  container.appendChild(titleWrap);
6887
6986
  const steps = isDocFlow ? [
6888
- { icon: "🪪", text: "Choose your document type", sub: "Passport, National ID, Driver License" },
6889
- { icon: "📷", text: "Scan your document", sub: "Upload or capture a photo of your ID" },
6890
- { icon: "🤳", text: "Face check", sub: "Quick selfie to confirm it's you" }
6987
+ { icon: "🪪", text: "Choose your document type", sub: "Passport, National ID, Driver License" },
6988
+ { icon: "📷", text: "Scan your document", sub: "Upload or capture a photo of your ID" },
6989
+ { icon: "🤳", text: "Face check", sub: "Quick selfie to confirm it's you" }
6891
6990
  ] : [
6892
- { icon: "🆔", text: "Choose your ID type", sub: "NIN, BVN, Voter's Card and more" },
6893
- { icon: "📝", text: "Enter your ID details", sub: "Exactly as shown on your ID" },
6894
- { icon: "🤳", text: "Face check", sub: "Quick selfie to confirm it's you" }
6991
+ { icon: "🆔", text: "Choose your ID type", sub: "NIN, BVN, Voter's Card and more" },
6992
+ { icon: "📝", text: "Enter your ID details", sub: "Exactly as shown on your ID" },
6993
+ { icon: "🤳", text: "Face check", sub: "Quick selfie to confirm it's you" }
6895
6994
  ];
6896
6995
  const stepsWrap = document.createElement("div");
6897
6996
  stepsWrap.style.cssText = `
@@ -6931,7 +7030,7 @@ var SmartComplySDK = (() => {
6931
7030
  stepsWrap.appendChild(row);
6932
7031
  });
6933
7032
  container.appendChild(stepsWrap);
6934
- const btn = this.createPrimaryButton("Get Started →");
7033
+ const btn = this.createPrimaryButton("Get Started →");
6935
7034
  btn.style.cssText += "margin-top:2px;";
6936
7035
  btn.addEventListener("click", () => this.showStep("country"));
6937
7036
  container.appendChild(btn);
@@ -6940,10 +7039,10 @@ var SmartComplySDK = (() => {
6940
7039
  display:flex;align-items:center;gap:6px;
6941
7040
  color:${this.theme.textMuted};font-size:10.5px;
6942
7041
  `;
6943
- trust.textContent = "🔒 End-to-end encrypted";
7042
+ trust.textContent = "🔒 End-to-end encrypted";
6944
7043
  container.appendChild(trust);
6945
7044
  }
6946
- // ── Country Select ──────────────────────────────────────────────
7045
+ // ── Country Select ──────────────────────────────────────────────
6947
7046
  renderCountrySelect(container) {
6948
7047
  container.style.cssText += "display:flex;flex-direction:column;gap:14px;";
6949
7048
  const channels = this.sdkConfig?.channels || {};
@@ -6971,7 +7070,7 @@ var SmartComplySDK = (() => {
6971
7070
  container.appendChild(card);
6972
7071
  }
6973
7072
  }
6974
- // ── ID Type Select ──────────────────────────────────────────────
7073
+ // ── ID Type Select ──────────────────────────────────────────────
6975
7074
  renderIdTypeSelect(container) {
6976
7075
  container.style.cssText += "display:flex;flex-direction:column;gap:14px;";
6977
7076
  const globalDocFlow = this.isDocumentFlow();
@@ -6993,7 +7092,7 @@ var SmartComplySDK = (() => {
6993
7092
  const requiresBack = requiresBackFromBackend ?? info.requiresBack;
6994
7093
  let subtitle2 = info.desc;
6995
7094
  if (requiresBack && isThisDocFlow) {
6996
- subtitle2 += " ⚠️ Both sides required";
7095
+ subtitle2 += " ⚠️ Both sides required";
6997
7096
  }
6998
7097
  const displayName = typeName.replace(/\s*with\s+face\s*/gi, " ").replace(/\s*\(NIN\)\s*/gi, " ").replace(/\s*\(BVN\)\s*/gi, " ").replace(/\s{2,}/g, " ").trim();
6999
7098
  const card = this.createOptionCard(
@@ -7026,7 +7125,7 @@ var SmartComplySDK = (() => {
7026
7125
  container.appendChild(card);
7027
7126
  }
7028
7127
  }
7029
- // ── ID Input (data verification — dynamic fields) ─────────────
7128
+ // ── ID Input (data verification — dynamic fields) ─────────────
7030
7129
  renderIdInput(container) {
7031
7130
  container.style.cssText += "display:flex;flex-direction:column;gap:14px;";
7032
7131
  const dataFields = this.selectedChannelFields.filter((f2) => f2.type !== "upload");
@@ -7056,13 +7155,13 @@ var SmartComplySDK = (() => {
7056
7155
  { match: "BVN", rule: { minLen: 11, maxLen: 11, numeric: true, hint: "BVN must be exactly 11 digits" } },
7057
7156
  { match: "NIN", rule: { minLen: 11, maxLen: 11, numeric: true, hint: "NIN must be exactly 11 digits" } },
7058
7157
  { match: "NATIONAL IDENTITY", rule: { minLen: 11, maxLen: 11, numeric: true, hint: "NIN must be exactly 11 digits" } },
7059
- { match: "PASSPORT", rule: { minLen: 6, maxLen: 20, alphanumeric: true, hint: "Passport number must be 6–20 alphanumeric characters" } },
7060
- { match: "DRIVER", rule: { minLen: 3, maxLen: 30, alphanumeric: true, hint: "Licence number must be 3–30 alphanumeric characters" } },
7061
- { match: "VOTER", rule: { minLen: 6, maxLen: 30, alphanumeric: true, hint: "Voter ID must be 6–30 alphanumeric characters" } },
7062
- { match: "GHANA", rule: { minLen: 8, maxLen: 20, alphanumeric: true, hint: "Ghana Card number must be 8–20 alphanumeric characters" } },
7063
- { match: "SSNIT", rule: { minLen: 7, maxLen: 15, alphanumeric: true, hint: "SSNIT number must be 7–15 alphanumeric characters" } },
7064
- { match: "TIN", rule: { minLen: 8, maxLen: 20, alphanumeric: true, hint: "TIN must be 8–20 characters" } },
7065
- { match: "KRA", rule: { minLen: 10, maxLen: 12, alphanumeric: true, hint: "KRA PIN must be 10–12 characters" } }
7158
+ { match: "PASSPORT", rule: { minLen: 6, maxLen: 20, alphanumeric: true, hint: "Passport number must be 6–20 alphanumeric characters" } },
7159
+ { match: "DRIVER", rule: { minLen: 3, maxLen: 30, alphanumeric: true, hint: "Licence number must be 3–30 alphanumeric characters" } },
7160
+ { match: "VOTER", rule: { minLen: 6, maxLen: 30, alphanumeric: true, hint: "Voter ID must be 6–30 alphanumeric characters" } },
7161
+ { match: "GHANA", rule: { minLen: 8, maxLen: 20, alphanumeric: true, hint: "Ghana Card number must be 8–20 alphanumeric characters" } },
7162
+ { match: "SSNIT", rule: { minLen: 7, maxLen: 15, alphanumeric: true, hint: "SSNIT number must be 7–15 alphanumeric characters" } },
7163
+ { match: "TIN", rule: { minLen: 8, maxLen: 20, alphanumeric: true, hint: "TIN must be 8–20 characters" } },
7164
+ { match: "KRA", rule: { minLen: 10, maxLen: 12, alphanumeric: true, hint: "KRA PIN must be 10–12 characters" } }
7066
7165
  ];
7067
7166
  function getFieldRule(label) {
7068
7167
  const upper = label.toUpperCase();
@@ -7302,7 +7401,7 @@ var SmartComplySDK = (() => {
7302
7401
  setTimeout(() => firstInput.focus(), 350);
7303
7402
  }
7304
7403
  }
7305
- // ── Document Capture ────────────────────────────────────────────
7404
+ // ── Document Capture ────────────────────────────────────────────
7306
7405
  renderDocumentCapture(container) {
7307
7406
  container.style.cssText += "display:flex;flex-direction:column;gap:14px;";
7308
7407
  const rawName = this.selectedIdTypeName || this.selectedIdType || "your document";
@@ -7338,7 +7437,7 @@ var SmartComplySDK = (() => {
7338
7437
  this.showErrorStep(err.message || "Document capture failed. Please try again.");
7339
7438
  });
7340
7439
  }
7341
- // ── W3: Identity Confirmation (data verification) ───────────────
7440
+ // ── W3: Identity Confirmation (data verification) ───────────────
7342
7441
  renderConfirmIdentity(container) {
7343
7442
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:16px;padding:8px 0;";
7344
7443
  if (this._confirmRetries >= _SmartComplyFlow.MAX_RETRIES) {
@@ -7348,7 +7447,7 @@ var SmartComplySDK = (() => {
7348
7447
  this._prewarmCamera();
7349
7448
  const iconWrap = document.createElement("div");
7350
7449
  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;`;
7351
- iconWrap.textContent = "🛡️";
7450
+ iconWrap.textContent = "🛡️";
7352
7451
  container.appendChild(iconWrap);
7353
7452
  const title = document.createElement("h2");
7354
7453
  title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
@@ -7385,7 +7484,7 @@ var SmartComplySDK = (() => {
7385
7484
  sub.textContent = "Your details have been confirmed. Please continue to the face check.";
7386
7485
  container.appendChild(sub);
7387
7486
  }
7388
- const yesBtn = this.createPrimaryButton("Yes, that's me — continue");
7487
+ const yesBtn = this.createPrimaryButton("Yes, that's me — continue");
7389
7488
  yesBtn.addEventListener("click", () => {
7390
7489
  this._confirmRetries = 0;
7391
7490
  this.showStep("liveness");
@@ -7402,7 +7501,7 @@ var SmartComplySDK = (() => {
7402
7501
  });
7403
7502
  container.appendChild(noBtn);
7404
7503
  }
7405
- // ── W4: Document Confirmation (document verification) ────────────
7504
+ // ── W4: Document Confirmation (document verification) ────────────
7406
7505
  renderDocumentConfirm(container) {
7407
7506
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:14px;padding:8px 0;";
7408
7507
  if (this._confirmRetries >= _SmartComplyFlow.MAX_RETRIES) {
@@ -7412,7 +7511,7 @@ var SmartComplySDK = (() => {
7412
7511
  this._prewarmCamera();
7413
7512
  const iconWrap = document.createElement("div");
7414
7513
  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;`;
7415
- iconWrap.textContent = "📄";
7514
+ iconWrap.textContent = "📄";
7416
7515
  container.appendChild(iconWrap);
7417
7516
  const title = document.createElement("h2");
7418
7517
  title.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
@@ -7435,7 +7534,7 @@ var SmartComplySDK = (() => {
7435
7534
  if (this._ocrExpirySoon && !this._ocrIsExpired) {
7436
7535
  const warn = document.createElement("div");
7437
7536
  warn.style.cssText = `width:100%;padding:10px 14px;border-radius:10px;background:${this.theme.warning}18;border:1px solid ${this.theme.warning}40;color:${this.theme.warning};font-size:12px;text-align:left;line-height:1.5;`;
7438
- warn.textContent = "⚠️ Your document expires soon. Some services may not accept it, but you can still continue.";
7537
+ warn.textContent = "⚠️ Your document expires soon. Some services may not accept it, but you can still continue.";
7439
7538
  container.appendChild(warn);
7440
7539
  }
7441
7540
  const card = document.createElement("div");
@@ -7489,7 +7588,7 @@ var SmartComplySDK = (() => {
7489
7588
  });
7490
7589
  container.appendChild(noBtn);
7491
7590
  }
7492
- // ── W6+W7+W8: OCR Gate (document verification) ───────────────────
7591
+ // ── W6+W7+W8: OCR Gate (document verification) ───────────────────
7493
7592
  renderOcrGate(container) {
7494
7593
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;gap:14px;padding:20px 0;text-align:center;";
7495
7594
  const spinnerWrap = document.createElement("div");
@@ -7498,18 +7597,18 @@ var SmartComplySDK = (() => {
7498
7597
  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;`;
7499
7598
  const centre = document.createElement("div");
7500
7599
  centre.style.cssText = "position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;";
7501
- centre.textContent = "📄";
7600
+ centre.textContent = "📄";
7502
7601
  spinnerWrap.appendChild(ring);
7503
7602
  spinnerWrap.appendChild(centre);
7504
7603
  container.appendChild(spinnerWrap);
7505
7604
  const statusEl = document.createElement("div");
7506
7605
  statusEl.setAttribute("aria-live", "polite");
7507
7606
  statusEl.style.cssText = `color:${this.theme.text};font-size:15px;font-weight:600;`;
7508
- statusEl.textContent = "Reading your document…";
7607
+ statusEl.textContent = "Reading your document…";
7509
7608
  container.appendChild(statusEl);
7510
7609
  const subEl = document.createElement("div");
7511
7610
  subEl.style.cssText = `color:${this.theme.textSecondary};font-size:12px;max-width:260px;line-height:1.5;`;
7512
- subEl.textContent = "This usually takes 5–15 seconds. Please wait.";
7611
+ subEl.textContent = "This usually takes 5–15 seconds. Please wait.";
7513
7612
  container.appendChild(subEl);
7514
7613
  if (!this.livenessEntryId) {
7515
7614
  this.showErrorStep("Document upload failed. Please try again.");
@@ -7554,8 +7653,8 @@ var SmartComplySDK = (() => {
7554
7653
  if (polls < MAX_POLLS && !this.isDestroyed) {
7555
7654
  this._ocrPollTimer = setTimeout(poll, 2e3);
7556
7655
  } else if (!this.isDestroyed) {
7557
- statusEl.textContent = "Taking longer than usual…";
7558
- subEl.textContent = "We'll continue — you can confirm your details on the next screen.";
7656
+ statusEl.textContent = "Taking longer than usual…";
7657
+ subEl.textContent = "We'll continue — you can confirm your details on the next screen.";
7559
7658
  this._ocrPollTimer = setTimeout(() => {
7560
7659
  if (!this.isDestroyed) this.showStep("document_confirm");
7561
7660
  }, 2e3);
@@ -7574,7 +7673,7 @@ var SmartComplySDK = (() => {
7574
7673
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:14px;padding:20px 0;";
7575
7674
  const icon = document.createElement("div");
7576
7675
  icon.style.cssText = `width:72px;height:72px;border-radius:50%;background:${this.theme.errorBg};border:3px solid ${this.theme.error};display:flex;align-items:center;justify-content:center;font-size:28px;`;
7577
- icon.textContent = "📅";
7676
+ icon.textContent = "📅";
7578
7677
  container.appendChild(icon);
7579
7678
  const t2 = document.createElement("h2");
7580
7679
  t2.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
@@ -7603,7 +7702,7 @@ var SmartComplySDK = (() => {
7603
7702
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:14px;padding:20px 0;";
7604
7703
  const icon = document.createElement("div");
7605
7704
  icon.style.cssText = `width:72px;height:72px;border-radius:50%;background:${this.theme.errorBg};border:3px solid ${this.theme.error};display:flex;align-items:center;justify-content:center;font-size:28px;`;
7606
- icon.textContent = "🔍";
7705
+ icon.textContent = "🔍";
7607
7706
  container.appendChild(icon);
7608
7707
  const t2 = document.createElement("h2");
7609
7708
  t2.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
@@ -7623,7 +7722,7 @@ var SmartComplySDK = (() => {
7623
7722
  });
7624
7723
  container.appendChild(retryBtn);
7625
7724
  }
7626
- // ── W11: You're all done (end-user final screen) ─────────────────
7725
+ // ── W11: You're all done (end-user final screen) ─────────────────
7627
7726
  renderDone(container) {
7628
7727
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:16px;padding:24px 0;";
7629
7728
  container.setAttribute("role", "status");
@@ -7636,7 +7735,7 @@ var SmartComplySDK = (() => {
7636
7735
  });
7637
7736
  const iconWrap = document.createElement("div");
7638
7737
  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;`;
7639
- iconWrap.textContent = "✓";
7738
+ iconWrap.textContent = "✓";
7640
7739
  container.appendChild(iconWrap);
7641
7740
  const title = document.createElement("h2");
7642
7741
  title.style.cssText = `color:${this.theme.text};font-size:22px;font-weight:700;margin:0;`;
@@ -7648,7 +7747,7 @@ var SmartComplySDK = (() => {
7648
7747
  container.appendChild(desc);
7649
7748
  const info = document.createElement("div");
7650
7749
  info.style.cssText = `color:${this.theme.textMuted};font-size:11px;display:flex;align-items:center;gap:4px;`;
7651
- info.textContent = "🔒 Your data is encrypted and secure";
7750
+ info.textContent = "🔒 Your data is encrypted and secure";
7652
7751
  container.appendChild(info);
7653
7752
  const doneBtn = this.createPrimaryButton("Close");
7654
7753
  doneBtn.addEventListener("click", () => {
@@ -7656,12 +7755,12 @@ var SmartComplySDK = (() => {
7656
7755
  });
7657
7756
  container.appendChild(doneBtn);
7658
7757
  }
7659
- // ── W14: Retry limit reached ─────────────────────────────────────
7758
+ // ── W14: Retry limit reached ─────────────────────────────────────
7660
7759
  _renderRetryLimitReached(container) {
7661
7760
  container.style.cssText += "display:flex;flex-direction:column;align-items:center;text-align:center;gap:14px;padding:24px 0;";
7662
7761
  const icon = document.createElement("div");
7663
7762
  icon.style.cssText = `width:72px;height:72px;border-radius:50%;background:${this.theme.errorBg};border:3px solid ${this.theme.error};display:flex;align-items:center;justify-content:center;font-size:28px;`;
7664
- icon.textContent = "⚠️";
7763
+ icon.textContent = "⚠️";
7665
7764
  container.appendChild(icon);
7666
7765
  const t2 = document.createElement("h2");
7667
7766
  t2.style.cssText = `color:${this.theme.text};font-size:18px;font-weight:700;margin:0;`;
@@ -7678,7 +7777,7 @@ var SmartComplySDK = (() => {
7678
7777
  });
7679
7778
  container.appendChild(closeBtn);
7680
7779
  }
7681
- // ── W13: Camera pre-warm ─────────────────────────────────────────
7780
+ // ── W13: Camera pre-warm ─────────────────────────────────────────
7682
7781
  _prewarmCamera() {
7683
7782
  if (this._prewarmedStream || this.isDestroyed) return;
7684
7783
  navigator.mediaDevices?.getUserMedia?.({ video: { facingMode: "user" } }).then((stream) => {
@@ -7717,7 +7816,7 @@ var SmartComplySDK = (() => {
7717
7816
  <div style="display:flex;flex-direction:column;align-items:center;gap:14px;padding:28px 20px;">
7718
7817
  <div style="position:relative;width:52px;height:52px;">
7719
7818
  <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>
7720
- <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">🔍</div>
7819
+ <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">🔍</div>
7721
7820
  </div>
7722
7821
  <div style="color:${this.theme.text};font-size:14px;font-weight:600;">Scanning document...</div>
7723
7822
  <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>
@@ -7775,7 +7874,7 @@ var SmartComplySDK = (() => {
7775
7874
  <div style="display:flex;flex-direction:column;align-items:center;gap:16px;padding:28px 20px;text-align:center;">
7776
7875
  <div style="position:relative;width:52px;height:52px;">
7777
7876
  <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>
7778
- <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">🔄</div>
7877
+ <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:20px;">🔄</div>
7779
7878
  </div>
7780
7879
  <div style="color:${this.theme.text};font-size:14px;font-weight:600;">Retrying...</div>
7781
7880
  <div style="color:${this.theme.textSecondary};font-size:12px;max-width:240px;line-height:1.5;">Attempt ${retryCount} of ${maxRetries}</div>
@@ -7816,7 +7915,7 @@ var SmartComplySDK = (() => {
7816
7915
  wrap.setAttribute("role", "alert");
7817
7916
  const icon = document.createElement("div");
7818
7917
  icon.style.cssText = `width:56px;height:56px;border-radius:50%;background:${this.theme.errorBg};border:3px solid ${this.theme.error};display:flex;align-items:center;justify-content:center;font-size:26px;`;
7819
- icon.textContent = "🔍";
7918
+ icon.textContent = "🔍";
7820
7919
  wrap.appendChild(icon);
7821
7920
  const titleEl = document.createElement("div");
7822
7921
  titleEl.style.cssText = `color:${this.theme.text};font-size:14px;font-weight:700;`;
@@ -7840,7 +7939,7 @@ var SmartComplySDK = (() => {
7840
7939
  this.showErrorStep(userMessage);
7841
7940
  }
7842
7941
  }
7843
- // ── Liveness ────────────────────────────────────────────────────
7942
+ // ── Liveness ────────────────────────────────────────────────────
7844
7943
  renderLiveness(container) {
7845
7944
  container.style.cssText += "display:flex;flex-direction:column;gap:14px;";
7846
7945
  if (this._livenessFailures >= _SmartComplyFlow.MAX_RETRIES) {
@@ -7899,7 +7998,7 @@ var SmartComplySDK = (() => {
7899
7998
  }
7900
7999
  renderResult(_container) {
7901
8000
  }
7902
- // ── Error ───────────────────────────────────────────────────────
8001
+ // ── Error ───────────────────────────────────────────────────────
7903
8002
  showErrorStep(message) {
7904
8003
  this.currentStep = "error";
7905
8004
  if (!this.contentArea) return;
@@ -7916,7 +8015,7 @@ var SmartComplySDK = (() => {
7916
8015
  const isDocQuality = /blurry|blur|quality|glare|dark|bright|lighting/i.test(message);
7917
8016
  const isExpiredDoc = /document.*expired|expired.*document/i.test(message);
7918
8017
  const isFaceMatch = /face.*match|selfie.*match|does not match/i.test(message);
7919
- const errorIcon = isCameraErr ? "📷" : isNetworkErr ? "📡" : isAuthErr ? "⏱" : isDocQuality ? "🔍" : isExpiredDoc ? "📅" : isFaceMatch ? "🤳" : "✕";
8018
+ const errorIcon = isCameraErr ? "📷" : isNetworkErr ? "📡" : isAuthErr ? "⏱" : isDocQuality ? "🔍" : isExpiredDoc ? "📅" : isFaceMatch ? "🤳" : "✕";
7920
8019
  const errorTitle = isCameraErr ? "Camera Access Required" : isNetworkErr ? "Connection Problem" : isAuthErr ? "Session Expired" : isDocQuality ? "Document Photo Unclear" : isExpiredDoc ? "Document Expired" : isFaceMatch ? "Face Match Failed" : "Verification Unsuccessful";
7921
8020
  const userMessage = isCameraErr ? "Please allow camera access in your browser settings and try again." : isNetworkErr ? "Check your internet connection and try again." : isAuthErr ? "Your session has expired. Please start again." : isDocQuality ? _sanitizeUserMessage(message) || "Please retake your document photo in good lighting with no glare." : isExpiredDoc ? "The document you submitted has expired. Please use a valid, current ID." : isFaceMatch ? "Your selfie did not match the photo on your ID. Please ensure you are using your own valid document and face the camera directly." : _sanitizeUserMessage(message) || "We were unable to complete your verification. Please try again.";
7922
8021
  const iconWrap = document.createElement("div");
@@ -7952,9 +8051,9 @@ var SmartComplySDK = (() => {
7952
8051
  wrapper.appendChild(closeBtn);
7953
8052
  this.contentArea.appendChild(wrapper);
7954
8053
  }
7955
- // ─────────────────────────────────────────────────────────────────
8054
+ // ─────────────────────────────────────────────────────────────────
7956
8055
  // UI Helpers
7957
- // ─────────────────────────────────────────────────────────────────
8056
+ // ─────────────────────────────────────────────────────────────────
7958
8057
  createHeader() {
7959
8058
  const header = document.createElement("div");
7960
8059
  header.setAttribute("role", "banner");
@@ -8021,7 +8120,7 @@ var SmartComplySDK = (() => {
8021
8120
  stepIndicator.appendChild(this.stepLabel);
8022
8121
  header.appendChild(stepIndicator);
8023
8122
  const closeBtn = document.createElement("button");
8024
- closeBtn.innerHTML = "✕";
8123
+ closeBtn.innerHTML = "✕";
8025
8124
  closeBtn.setAttribute("aria-label", "Close identity verification");
8026
8125
  closeBtn.style.cssText = `
8027
8126
  width:32px;height:32px;border-radius:8px;flex-shrink:0;
@@ -8102,7 +8201,7 @@ var SmartComplySDK = (() => {
8102
8201
  }
8103
8202
  const arrow = document.createElement("span");
8104
8203
  arrow.style.cssText = `color:${this.theme.textMuted};font-size:18px;flex-shrink:0;`;
8105
- arrow.textContent = "→";
8204
+ arrow.textContent = "→";
8106
8205
  card.appendChild(textWrap);
8107
8206
  card.appendChild(arrow);
8108
8207
  return card;
@@ -8174,7 +8273,7 @@ var SmartComplySDK = (() => {
8174
8273
  if (this.currentStep === "loading" || this.currentStep === "welcome") {
8175
8274
  this.stepLabel.textContent = "";
8176
8275
  } else if (this.currentStep === "done" || this.currentStep === "result") {
8177
- this.stepLabel.textContent = "Complete ✓";
8276
+ this.stepLabel.textContent = "Complete ✓";
8178
8277
  } else if (stage2Steps.includes(this.currentStep)) {
8179
8278
  this.stepLabel.textContent = "Stage 2 of 2: Face Check";
8180
8279
  } else if (stage1Steps.includes(this.currentStep)) {