@smileid/web-components 11.4.5 → 11.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/esm/DocumentCaptureScreens-DjSTdVP-.js +5398 -0
  2. package/dist/esm/DocumentCaptureScreens-DjSTdVP-.js.map +1 -0
  3. package/dist/esm/{Navigation-Bb7MPLE8.js → Navigation-6DH3vF4-.js} +28 -22
  4. package/dist/esm/Navigation-6DH3vF4-.js.map +1 -0
  5. package/dist/esm/{PoweredBySmileId-CxbaihMu.js → PoweredBySmileId-DoKwoPUd.js} +424 -6
  6. package/dist/esm/PoweredBySmileId-DoKwoPUd.js.map +1 -0
  7. package/dist/esm/SelfieCaptureScreens-CtX-4Tco.js +11470 -0
  8. package/dist/esm/SelfieCaptureScreens-CtX-4Tco.js.map +1 -0
  9. package/dist/esm/combobox.js +1 -1
  10. package/dist/esm/document.js +1 -1
  11. package/dist/esm/end-user-consent.js +713 -2
  12. package/dist/esm/end-user-consent.js.map +1 -1
  13. package/dist/esm/index-BqyuTk9f.js +1366 -0
  14. package/dist/esm/{index-C4RTMbgw.js.map → index-BqyuTk9f.js.map} +1 -1
  15. package/dist/esm/localisation.js +1 -1
  16. package/dist/esm/main.js +14 -14
  17. package/dist/esm/navigation.js +1 -1
  18. package/dist/esm/package-CjZI-cNQ.js +2540 -0
  19. package/dist/esm/package-CjZI-cNQ.js.map +1 -0
  20. package/dist/esm/selfie.js +1 -1
  21. package/dist/esm/smart-camera-web.js +81 -37
  22. package/dist/esm/smart-camera-web.js.map +1 -1
  23. package/dist/esm/totp-consent.js +731 -2
  24. package/dist/esm/totp-consent.js.map +1 -1
  25. package/dist/esm/validate.js +31 -0
  26. package/dist/esm/validate.js.map +1 -0
  27. package/dist/smart-camera-web.js +1513 -383
  28. package/dist/smart-camera-web.js.map +1 -1
  29. package/dist/types/main.d.ts +18 -1
  30. package/dist/types/validate.d.ts +21 -0
  31. package/lib/components/document/src/DocumentCaptureScreens.js +97 -18
  32. package/lib/components/document/src/assets/lottie.d.ts +12 -0
  33. package/lib/components/document/src/assets/svg-inline.d.ts +8 -0
  34. package/lib/components/document/src/document-auto-capture/DocumentAutoCapture.stories.js +75 -0
  35. package/lib/components/document/src/document-auto-capture/DocumentAutoCapture.tsx +1458 -0
  36. package/lib/components/document/src/document-auto-capture/README.md +73 -0
  37. package/lib/components/document/src/document-auto-capture/assets/Greenbook_Shimmer.svg +42 -0
  38. package/lib/components/document/src/document-auto-capture/assets/ID_Back_Shimmer.svg +8 -0
  39. package/lib/components/document/src/document-auto-capture/assets/ID_Front_Shimmer.svg +20 -0
  40. package/lib/components/document/src/document-auto-capture/assets/Passport-Shimmer.svg +143 -0
  41. package/lib/components/document/src/document-auto-capture/assets/shimmers.ts +21 -0
  42. package/lib/components/document/src/document-auto-capture/assets/svg-raw.d.ts +4 -0
  43. package/lib/components/document/src/document-auto-capture/components/CaptureButton.tsx +122 -0
  44. package/lib/components/document/src/document-auto-capture/components/Overlay.tsx +167 -0
  45. package/lib/components/document/src/document-auto-capture/components/TuningPanel.tsx +856 -0
  46. package/lib/components/document/src/document-auto-capture/constants/captureLayout.ts +58 -0
  47. package/lib/components/document/src/document-auto-capture/detection/cvErrorRecovery.ts +40 -0
  48. package/lib/components/document/src/document-auto-capture/detection/documentAspect.ts +20 -0
  49. package/lib/components/document/src/document-auto-capture/detection/qualityScoring.ts +35 -0
  50. package/lib/components/document/src/document-auto-capture/detection/seamRejection.ts +209 -0
  51. package/lib/components/document/src/document-auto-capture/detection/synthesisTiming.ts +10 -0
  52. package/lib/components/document/src/document-auto-capture/hooks/useCamera.ts +117 -0
  53. package/lib/components/document/src/document-auto-capture/hooks/useCardDetection.ts +3059 -0
  54. package/lib/components/document/src/document-auto-capture/index.ts +4 -0
  55. package/lib/components/document/src/document-auto-capture/theme.ts +40 -0
  56. package/lib/components/document/src/document-auto-capture/utils/debug.ts +25 -0
  57. package/lib/components/document/src/document-auto-capture/utils/opencvLoader.ts +86 -0
  58. package/lib/components/document/src/document-capture-instructions/DocumentCaptureInstructions.tsx +327 -244
  59. package/lib/components/document/src/document-capture-review/DocumentCaptureReview.js +153 -189
  60. package/lib/components/document/src/document-capture-submission/DocumentCaptureSubmission.tsx +432 -0
  61. package/lib/components/document/src/document-capture-submission/index.js +3 -0
  62. package/lib/components/navigation/src/Navigation.js +27 -8
  63. package/lib/components/selfie/README.md +13 -0
  64. package/lib/components/selfie/src/SelfieCaptureScreens.js +56 -8
  65. package/lib/components/selfie/src/enhanced-smartselfie-capture/EnhancedSmartSelfieCapture.tsx +684 -0
  66. package/lib/components/selfie/src/enhanced-smartselfie-capture/EnhancedSmartSelfieConsent.tsx +71 -0
  67. package/lib/components/selfie/src/enhanced-smartselfie-capture/EnhancedSmartSelfieSubmission.tsx +181 -0
  68. package/lib/components/selfie/src/enhanced-smartselfie-capture/OvalProgress.tsx +87 -0
  69. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/Icon.svg +8 -0
  70. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/accessories.svg +77 -0
  71. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/active_liveness_animation.lottie +0 -0
  72. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/device.svg +12 -0
  73. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/device_orientation.lottie +0 -0
  74. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/good.svg +52 -0
  75. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/id-card.svg +9 -0
  76. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/illustrations.tsx +852 -0
  77. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/instructions-img.svg +3 -0
  78. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/multiple-faces.svg +69 -0
  79. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/person.svg +6 -0
  80. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/phone.svg +8 -0
  81. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/poor-lighting.svg +53 -0
  82. package/lib/components/selfie/src/enhanced-smartselfie-capture/assets/too_dark_animation.lottie +0 -0
  83. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/ActiveLivenessOverlay.tsx +226 -0
  84. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/AlertDisplay.tsx +38 -0
  85. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/BackNavigation.tsx +45 -0
  86. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/CameraPreview.tsx +96 -0
  87. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/CaptureControls.tsx +97 -0
  88. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/CaptureGuidelines.tsx +374 -0
  89. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/ConsentView.tsx +460 -0
  90. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/SubmissionView.tsx +426 -0
  91. package/lib/components/selfie/src/enhanced-smartselfie-capture/components/index.ts +3 -0
  92. package/lib/components/selfie/src/enhanced-smartselfie-capture/constants.ts +23 -0
  93. package/lib/components/selfie/src/enhanced-smartselfie-capture/hooks/index.ts +2 -0
  94. package/lib/components/selfie/src/enhanced-smartselfie-capture/hooks/useCamera.ts +238 -0
  95. package/lib/components/selfie/src/enhanced-smartselfie-capture/hooks/useFaceCapture.ts +1075 -0
  96. package/lib/components/selfie/src/enhanced-smartselfie-capture/index.ts +1 -0
  97. package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/alertMessages.ts +20 -0
  98. package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/canvas.ts +108 -0
  99. package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/faceDetection.ts +545 -0
  100. package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/imageCapture.ts +66 -0
  101. package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/imageQuality.ts +151 -0
  102. package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/index.ts +5 -0
  103. package/lib/components/selfie/src/enhanced-smartselfie-capture/utils/mediapipeManager.ts +215 -0
  104. package/lib/components/selfie/src/selfie-capture-wrapper/SelfieCaptureWrapper.tsx +24 -1
  105. package/lib/components/selfie/src/smartselfie-capture/SmartSelfieCapture.tsx +2 -2
  106. package/lib/components/selfie/src/smartselfie-capture/hooks/useFaceCapture.ts +15 -7
  107. package/lib/components/selfie/src/smartselfie-capture/utils/canvas.ts +4 -6
  108. package/lib/components/signature-pad/package.json +1 -1
  109. package/lib/components/smart-camera-web/src/README.md +11 -0
  110. package/lib/components/smart-camera-web/src/SmartCameraWeb.js +89 -8
  111. package/lib/components/totp-consent/src/TotpConsent.js +1 -1
  112. package/lib/domain/localisation/index.js +2 -2
  113. package/package.json +9 -5
  114. package/dist/esm/DocumentCaptureScreens-D2G0NOQr.js +0 -4147
  115. package/dist/esm/DocumentCaptureScreens-D2G0NOQr.js.map +0 -1
  116. package/dist/esm/EndUserConsent-uHfA3txP.js +0 -717
  117. package/dist/esm/EndUserConsent-uHfA3txP.js.map +0 -1
  118. package/dist/esm/Navigation-Bb7MPLE8.js.map +0 -1
  119. package/dist/esm/PoweredBySmileId-CxbaihMu.js.map +0 -1
  120. package/dist/esm/SelfieCaptureScreens-Dr7VzON7.js +0 -7651
  121. package/dist/esm/SelfieCaptureScreens-Dr7VzON7.js.map +0 -1
  122. package/dist/esm/TotpConsent-Depzg0ti.js +0 -734
  123. package/dist/esm/TotpConsent-Depzg0ti.js.map +0 -1
  124. package/dist/esm/index-C4RTMbgw.js +0 -1360
  125. package/dist/esm/package-D6YrpMcO.js +0 -565
  126. package/dist/esm/package-D6YrpMcO.js.map +0 -1
  127. package/dist/esm/styles-BTEClL7R.js +0 -419
  128. package/dist/esm/styles-BTEClL7R.js.map +0 -1
  129. /package/lib/components/document/src/assets/lottie/{taking photo of green book passport.lottie → greenbook.lottie} +0 -0
  130. /package/lib/components/document/src/assets/lottie/{taking photo of ID FLIP 2D.lottie → id-card-flip.lottie} +0 -0
  131. /package/lib/components/document/src/assets/lottie/{taking photo of ID.lottie → id-card.lottie} +0 -0
  132. /package/lib/components/document/src/assets/lottie/{taking photo of passport 2.lottie → passport.lottie} +0 -0
@@ -0,0 +1,97 @@
1
+ import type { FunctionComponent } from 'preact';
2
+ import { t } from '../../../../../domain/localisation';
3
+
4
+ interface CaptureControlsProps {
5
+ isCapturing: boolean;
6
+ hasFinishedCapture: boolean;
7
+ isReadyToCapture: boolean;
8
+ captureButtonFallbackEnabled: boolean;
9
+ allowAgentMode: boolean;
10
+ agentSupported: boolean;
11
+ showAgentModeForTests: boolean;
12
+ facingMode: 'user' | 'environment';
13
+ themeColor?: string;
14
+ onStartCapture: () => void;
15
+ onSwitchCamera: () => void;
16
+ }
17
+
18
+ export const CaptureControls: FunctionComponent<CaptureControlsProps> = ({
19
+ isCapturing,
20
+ hasFinishedCapture,
21
+ isReadyToCapture,
22
+ captureButtonFallbackEnabled,
23
+ allowAgentMode,
24
+ agentSupported,
25
+ showAgentModeForTests,
26
+ facingMode,
27
+ themeColor = '#001096',
28
+ onStartCapture,
29
+ onSwitchCamera,
30
+ }) => (
31
+ <>
32
+ <div className="controls">
33
+ <button
34
+ id="start-image-capture"
35
+ class="btn-primary"
36
+ onClick={onStartCapture}
37
+ disabled={
38
+ isCapturing ||
39
+ hasFinishedCapture ||
40
+ (!isReadyToCapture && !captureButtonFallbackEnabled)
41
+ }
42
+ >
43
+ {t('selfie.capture.button.startCapture')}
44
+ </button>
45
+
46
+ {allowAgentMode && (agentSupported || showAgentModeForTests) && (
47
+ <button
48
+ id="switch-camera"
49
+ onClick={onSwitchCamera}
50
+ className="agent-mode-btn"
51
+ disabled={isCapturing || hasFinishedCapture}
52
+ >
53
+ {facingMode === 'user'
54
+ ? t('selfie.capture.agentMode.off')
55
+ : t('selfie.capture.agentMode.on')}
56
+ </button>
57
+ )}
58
+ </div>
59
+
60
+ <style>{`
61
+ .controls {
62
+ margin-top: clamp(12px, 2.5dvh, 20px);
63
+ display: flex;
64
+ flex-direction: column;
65
+ gap: 1rem;
66
+ justify-content: center;
67
+ width: 100%;
68
+ }
69
+
70
+ .controls .btn-primary {
71
+ width: 100%;
72
+ background: ${themeColor};
73
+ }
74
+
75
+ .controls .btn-primary:hover,
76
+ .controls .btn-primary:focus,
77
+ .controls .btn-primary:active {
78
+ background: ${themeColor};
79
+ }
80
+
81
+ .agent-mode-btn {
82
+ padding: 8px 16px;
83
+ font-size: 14px;
84
+ background: ${themeColor};
85
+ color: white;
86
+ border: none;
87
+ border-radius: 4px;
88
+ cursor: pointer;
89
+ }
90
+
91
+ .agent-mode-btn:disabled {
92
+ background: #ccc;
93
+ cursor: not-allowed;
94
+ }
95
+ `}</style>
96
+ </>
97
+ );
@@ -0,0 +1,374 @@
1
+ import type { FunctionComponent } from 'preact';
2
+ import { useEffect, useRef } from 'preact/hooks';
3
+ import { IconLoader2 } from '@tabler/icons-preact';
4
+ import { DotLottie } from '@lottiefiles/dotlottie-web';
5
+ import { t } from '../../../../../domain/localisation';
6
+ // eslint-disable-next-line import/no-unresolved
7
+ import activeLivenessSrc from '../assets/active_liveness_animation.lottie';
8
+ // Illustration cards are real Preact components — the brand-blue pill in
9
+ // the "Good" example takes `themeColor` as a prop, which avoids the
10
+ // previous `.svg?raw` + `dangerouslySetInnerHTML` recolour and keeps the
11
+ // markup inside Preact's normal reconciliation.
12
+ import {
13
+ GoodIllustration,
14
+ AccessoriesIllustration,
15
+ MultipleFacesIllustration,
16
+ PoorLightingIllustration,
17
+ } from '../assets/illustrations';
18
+ import { BackNavigation } from './BackNavigation';
19
+
20
+ type IllustrationKind =
21
+ | 'good'
22
+ | 'accessories'
23
+ | 'multipleFaces'
24
+ | 'poorLighting';
25
+
26
+ interface Guideline {
27
+ status: 'good' | 'bad';
28
+ label: string;
29
+ illustration: IllustrationKind;
30
+ }
31
+
32
+ const GUIDELINES: Guideline[] = [
33
+ {
34
+ status: 'good',
35
+ label: 'selfie.ess.instructions.tile.good',
36
+ illustration: 'good',
37
+ },
38
+ {
39
+ status: 'bad',
40
+ label: 'selfie.ess.instructions.tile.accessories',
41
+ illustration: 'accessories',
42
+ },
43
+ {
44
+ status: 'bad',
45
+ label: 'selfie.ess.instructions.tile.multipleFaces',
46
+ illustration: 'multipleFaces',
47
+ },
48
+ {
49
+ status: 'bad',
50
+ label: 'selfie.ess.instructions.tile.poorLighting',
51
+ illustration: 'poorLighting',
52
+ },
53
+ ];
54
+
55
+ const ILLUSTRATION_COMPONENTS: Record<
56
+ IllustrationKind,
57
+ FunctionComponent<{ themeColor: string }>
58
+ > = {
59
+ good: GoodIllustration,
60
+ accessories: AccessoriesIllustration,
61
+ multipleFaces: MultipleFacesIllustration,
62
+ poorLighting: PoorLightingIllustration,
63
+ };
64
+
65
+ interface CaptureGuidelinesProps {
66
+ themeColor: string;
67
+ hideAttribution: boolean;
68
+ /**
69
+ * When false, the Continue button shows a spinner and is disabled —
70
+ * the camera + Mediapipe model are still loading in the background.
71
+ */
72
+ isReady?: boolean;
73
+ onContinue: () => void;
74
+ onBack?: () => void;
75
+ }
76
+
77
+ const HeroAnimation: FunctionComponent = () => {
78
+ const canvasRef = useRef<HTMLCanvasElement>(null);
79
+ const animRef = useRef<DotLottie | null>(null);
80
+
81
+ useEffect(() => {
82
+ if (!canvasRef.current) return undefined;
83
+ const anim = new DotLottie({
84
+ canvas: canvasRef.current,
85
+ src: activeLivenessSrc,
86
+ loop: true,
87
+ autoplay: true,
88
+ renderConfig: { autoResize: true },
89
+ });
90
+ animRef.current = anim;
91
+ return () => {
92
+ anim.destroy();
93
+ animRef.current = null;
94
+ };
95
+ }, []);
96
+
97
+ return (
98
+ <canvas
99
+ ref={canvasRef}
100
+ aria-hidden="true"
101
+ style={{
102
+ width: '120%',
103
+ height: '120%',
104
+ display: 'block',
105
+ }}
106
+ />
107
+ );
108
+ };
109
+
110
+ export const CaptureGuidelines: FunctionComponent<CaptureGuidelinesProps> = ({
111
+ themeColor,
112
+ hideAttribution,
113
+ isReady = true,
114
+ onContinue,
115
+ onBack,
116
+ }) => {
117
+ // Defensive scroll reset: ensures the guidelines screen always starts
118
+ // at the top, even if the parent page was scrolled by a previous view.
119
+ useEffect(() => {
120
+ try {
121
+ window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
122
+ } catch {
123
+ window.scrollTo(0, 0);
124
+ }
125
+ }, []);
126
+
127
+ return (
128
+ <div className="enhanced-instructions">
129
+ {onBack && <BackNavigation onBack={onBack} themeColor={themeColor} />}
130
+
131
+ <h1 className="instructions-title">
132
+ {t('selfie.ess.instructions.titlePrefix')}{' '}
133
+ <span style={{ color: themeColor }}>
134
+ {t('selfie.ess.instructions.titleAccent')}
135
+ </span>
136
+ </h1>
137
+
138
+ <div className="hero-oval">
139
+ <HeroAnimation />
140
+ </div>
141
+
142
+ <div className="guidelines-header">
143
+ <svg
144
+ width="18"
145
+ height="18"
146
+ viewBox="0 0 24 24"
147
+ fill="none"
148
+ stroke={themeColor}
149
+ stroke-width="2"
150
+ stroke-linecap="round"
151
+ stroke-linejoin="round"
152
+ >
153
+ <circle cx="12" cy="12" r="10" />
154
+ <polyline points="9 12 11 14 15 10" />
155
+ </svg>
156
+ <span>{t('selfie.ess.instructions.guidelinesHeader')}</span>
157
+ </div>
158
+
159
+ <div className="guidelines-grid">
160
+ {GUIDELINES.map((g) => {
161
+ const Illustration = ILLUSTRATION_COMPONENTS[g.illustration];
162
+ return (
163
+ <div className="guideline-tile" key={g.label}>
164
+ <div className="tile-illustration">
165
+ <div className="tile-svg">
166
+ <Illustration themeColor={themeColor} />
167
+ </div>
168
+ <span
169
+ className={`tile-badge ${g.status === 'good' ? 'badge-good' : 'badge-bad'}`}
170
+ aria-hidden="true"
171
+ >
172
+ {g.status === 'good' ? '✓' : '✕'}
173
+ </span>
174
+ </div>
175
+ {/* Label sits below the artwork in HTML so it can be themed and translated. */}
176
+ <span className="tile-label" style={{ color: themeColor }}>
177
+ {t(g.label)}
178
+ </span>
179
+ </div>
180
+ );
181
+ })}
182
+ </div>
183
+
184
+ <div className="instructions-footer">
185
+ <button
186
+ type="button"
187
+ className="continue-button"
188
+ style={{ background: themeColor, opacity: isReady ? 1 : 0.85 }}
189
+ onClick={onContinue}
190
+ disabled={!isReady}
191
+ aria-busy={!isReady}
192
+ >
193
+ {isReady ? (
194
+ t('selfie.ess.instructions.continue')
195
+ ) : (
196
+ <span className="continue-loading">
197
+ <IconLoader2 size={20} stroke={2.5} color="white" />
198
+ <span>{t('selfie.ess.instructions.settingUpCamera')}</span>
199
+ </span>
200
+ )}
201
+ </button>
202
+ </div>
203
+
204
+ {!hideAttribution && (
205
+ // @ts-expect-error preact-custom-element types
206
+ <powered-by-smile-id />
207
+ )}
208
+
209
+ <style>{`
210
+ :host { display: block; height: 100%; }
211
+ .enhanced-instructions {
212
+ position: relative;
213
+ display: flex;
214
+ flex-direction: column;
215
+ align-items: center;
216
+ height: 100%;
217
+ overflow: hidden;
218
+ padding: clamp(1.5rem, 3.5dvh, 2.25rem) clamp(0.75rem, 4vw, 1rem) 0;
219
+ font-family: "DM Sans", system-ui, sans-serif;
220
+ background: #F5F7FA;
221
+ box-sizing: border-box;
222
+ }
223
+ .ess-back-navigation {
224
+ position: absolute;
225
+ top: 1.5rem;
226
+ left: 1rem;
227
+ }
228
+ .instructions-title {
229
+ font-family: "DM Sans", system-ui, sans-serif;
230
+ font-size: clamp(18px, 3dvh, 24px);
231
+ font-style: normal;
232
+ font-weight: 800;
233
+ line-height: 1.25;
234
+ letter-spacing: -0.6px;
235
+ margin: clamp(0.25rem, 0.8dvh, 0.5rem) 0 clamp(0.15rem, 0.5dvh, 0.25rem);
236
+ align-self: stretch;
237
+ text-align: left;
238
+ color: #0F172B;
239
+ flex-shrink: 0;
240
+ }
241
+ /* Back nav (absolute, top:1.5rem, 40px tall) ends at ~62px. The title
242
+ starts after the container's top padding plus this margin, leaving
243
+ roughly a 24px gap between the button's bottom edge and the title. */
244
+ .ess-back-navigation + .instructions-title {
245
+ margin-top: 4rem;
246
+ }
247
+ .hero-oval {
248
+ width: clamp(135px, 22dvh, 195px);
249
+ height: clamp(160px, 26dvh, 230px);
250
+ max-width: 100%;
251
+ margin: clamp(0.5rem, 1.5dvh, 1rem) 0 0;
252
+ border: 2px solid #C4C4C4;
253
+ border-radius: 50%;
254
+ overflow: hidden;
255
+ display: flex;
256
+ align-items: center;
257
+ justify-content: center;
258
+ background: #F5F7FA;
259
+ flex-shrink: 0;
260
+ }
261
+ .guidelines-header {
262
+ display: flex;
263
+ align-items: center;
264
+ gap: 0.4rem;
265
+ align-self: flex-start;
266
+ color: #0F172B;
267
+ font-family: "DM Sans", system-ui, sans-serif;
268
+ font-size: clamp(10px, 1.4dvh, 11px);
269
+ font-style: normal;
270
+ font-weight: 700;
271
+ line-height: 1.5;
272
+ letter-spacing: 0.55px;
273
+ text-transform: uppercase;
274
+ margin: clamp(0.75rem, 2dvh, 1.25rem) 0 clamp(0.4rem, 1dvh, 0.75rem);
275
+ flex-shrink: 0;
276
+ }
277
+ .guidelines-grid {
278
+ display: grid;
279
+ grid-template-columns: repeat(4, 1fr);
280
+ gap: clamp(12px, 4vw, 44px);
281
+ width: 100%;
282
+ flex-shrink: 0;
283
+ }
284
+ .guideline-tile {
285
+ display: flex;
286
+ flex-direction: column;
287
+ align-items: center;
288
+ }
289
+ .tile-illustration {
290
+ position: relative;
291
+ width: 100%;
292
+ aspect-ratio: 47 / 79;
293
+ }
294
+ .tile-svg {
295
+ width: 100%;
296
+ height: 100%;
297
+ display: block;
298
+ }
299
+ .tile-svg svg {
300
+ width: 100%;
301
+ height: 100%;
302
+ display: block;
303
+ }
304
+ .tile-label {
305
+ margin-top: 0.5rem;
306
+ text-align: center;
307
+ font-family: "DM Sans", system-ui, sans-serif;
308
+ font-size: 10px;
309
+ font-style: normal;
310
+ font-weight: 500;
311
+ line-height: 10px;
312
+ letter-spacing: 0.117px;
313
+ }
314
+ .tile-badge {
315
+ position: absolute;
316
+ top: -6px;
317
+ right: -6px;
318
+ width: 22px;
319
+ height: 22px;
320
+ border-radius: 50%;
321
+ display: inline-flex;
322
+ align-items: center;
323
+ justify-content: center;
324
+ font-size: 12px;
325
+ color: white;
326
+ font-weight: 700;
327
+ }
328
+ .badge-good { background: #1DB954; }
329
+ .badge-bad { background: #E5484D; }
330
+ .instructions-footer {
331
+ width: 100%;
332
+ display: flex;
333
+ flex-direction: column;
334
+ align-items: stretch;
335
+ gap: 0.75rem;
336
+ margin-top: clamp(16px, 3dvh, 28px);
337
+ flex-shrink: 0;
338
+ }
339
+ .enhanced-instructions > powered-by-smile-id {
340
+ display: block;
341
+ width: 100%;
342
+ margin-top: 0;
343
+ padding: clamp(0.4rem, 1dvh, 0.6rem) 0;
344
+ background: #F5F7FA;
345
+ flex-shrink: 0;
346
+ }
347
+ .continue-button {
348
+ width: 100%;
349
+ padding: clamp(0.65rem, 1.6dvh, 1rem) 2rem;
350
+ color: white;
351
+ border: none;
352
+ border-radius: 2.5rem;
353
+ font-size: clamp(0.95rem, 1.8dvh, 1.05rem);
354
+ font-weight: 600;
355
+ font-family: inherit;
356
+ cursor: pointer;
357
+ }
358
+ .continue-button:disabled { cursor: progress; }
359
+ .continue-loading {
360
+ display: inline-flex;
361
+ align-items: center;
362
+ gap: 0.5rem;
363
+ justify-content: center;
364
+ }
365
+ .continue-loading svg { animation: ess-instr-spin 1.1s linear infinite; }
366
+ @keyframes ess-instr-spin {
367
+ to { transform: rotate(360deg); }
368
+ }
369
+ `}</style>
370
+ </div>
371
+ );
372
+ };
373
+
374
+ export default CaptureGuidelines;