@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,460 @@
1
+ import type { FunctionComponent } from 'preact';
2
+ import { useState } from 'preact/hooks';
3
+ import { t } from '../../../../../domain/localisation';
4
+ import {
5
+ SmileIdLogo,
6
+ CheckIcon,
7
+ ChevronDownIcon,
8
+ } from '../assets/illustrations';
9
+
10
+ // Icon URLs — Vite resolves these to URL strings via the `*.svg` ambient
11
+ // module declaration in lib/types.d.ts.
12
+ // eslint-disable-next-line import/no-unresolved
13
+ import phoneIcon from '../assets/phone.svg';
14
+ // eslint-disable-next-line import/no-unresolved
15
+ import personIcon from '../assets/person.svg';
16
+ // eslint-disable-next-line import/no-unresolved
17
+ import idCardIcon from '../assets/id-card.svg';
18
+ // eslint-disable-next-line import/no-unresolved
19
+ import deviceIcon from '../assets/device.svg';
20
+
21
+ interface ConsentViewProps {
22
+ themeColor: string;
23
+ hideAttribution: boolean;
24
+ partnerName?: string;
25
+ partnerLogo?: string;
26
+ policyUrl?: string;
27
+ onGranted: () => void;
28
+ onDenied: () => void;
29
+ }
30
+
31
+ const ITEMS: { icon: string; key: string }[] = [
32
+ { icon: phoneIcon, key: 'selfie.ess.consent.items.contact' },
33
+ { icon: personIcon, key: 'selfie.ess.consent.items.personal' },
34
+ { icon: idCardIcon, key: 'selfie.ess.consent.items.biometric' },
35
+ { icon: deviceIcon, key: 'selfie.ess.consent.items.device' },
36
+ ];
37
+
38
+ /**
39
+ * Returns the input only if it is a syntactically valid absolute URL whose
40
+ * protocol is `http:` or `https:`. Anything else (including `javascript:`,
41
+ * `data:`, malformed strings, or non-strings) is rejected. This prevents a
42
+ * malicious partner config from injecting an XSS vector through props that
43
+ * are rendered as `href`/`src`.
44
+ */
45
+ const safeHttpUrl = (value?: string): string | undefined => {
46
+ if (typeof value !== 'string' || value.length === 0) return undefined;
47
+ try {
48
+ const parsed = new URL(value);
49
+ if (parsed.protocol === 'https:' || parsed.protocol === 'http:') {
50
+ return parsed.toString();
51
+ }
52
+ } catch {
53
+ /* fall through */
54
+ }
55
+ return undefined;
56
+ };
57
+
58
+ /**
59
+ * Strict-mode consent screen rendered as the first step of the Enhanced
60
+ * SmartSelfie flow. Purpose-built for ESS and intentionally separate from the
61
+ * legacy `<end-user-consent>` element used by KYC products — modifying that
62
+ * element would change behaviour for every other product.
63
+ */
64
+ export const ConsentView: FunctionComponent<ConsentViewProps> = ({
65
+ themeColor,
66
+ hideAttribution,
67
+ partnerName,
68
+ partnerLogo,
69
+ policyUrl,
70
+ onGranted,
71
+ onDenied,
72
+ }) => {
73
+ const [accepted, setAccepted] = useState(false);
74
+ const [learnMoreOpen, setLearnMoreOpen] = useState(false);
75
+ const partner = partnerName;
76
+ const safePolicyUrl = safeHttpUrl(policyUrl);
77
+ const safePartnerLogo = safeHttpUrl(partnerLogo);
78
+
79
+ return (
80
+ <div className="ess-consent">
81
+ <div className="logos">
82
+ {safePartnerLogo && (
83
+ <img className="partner-logo" src={safePartnerLogo} alt={partner} />
84
+ )}
85
+ <div className="smile-logo" aria-hidden="true">
86
+ <SmileIdLogo />
87
+ </div>
88
+ </div>
89
+
90
+ <h1 className="title">
91
+ {partner} {t('selfie.ess.consent.titleSuffix')}{' '}
92
+ <span style={{ whiteSpace: 'nowrap' }}>Smile ID.</span>
93
+ </h1>
94
+
95
+ <p className="body">
96
+ {t('selfie.ess.consent.bodyPrefix')}
97
+ <strong>Smile ID</strong>
98
+ {t('selfie.ess.consent.bodyMiddle')}
99
+ <strong>{partner}</strong>
100
+ {t('selfie.ess.consent.bodySuffix')}
101
+ </p>
102
+
103
+ <ul className="items">
104
+ {ITEMS.map((it) => (
105
+ <li key={it.key}>
106
+ <img src={it.icon} alt="" aria-hidden="true" />
107
+ <span>{t(it.key)}</span>
108
+ </li>
109
+ ))}
110
+ </ul>
111
+
112
+ <label className="checkbox">
113
+ <input
114
+ type="checkbox"
115
+ checked={accepted}
116
+ onChange={(e) => setAccepted((e.target as HTMLInputElement).checked)}
117
+ />
118
+ <span className="box" aria-hidden="true">
119
+ <CheckIcon />
120
+ </span>
121
+ <span className="copy">{t('selfie.ess.consent.consentCheckbox')}</span>
122
+ </label>
123
+
124
+ <button
125
+ type="button"
126
+ className="learn-more"
127
+ onClick={() => setLearnMoreOpen((v) => !v)}
128
+ aria-expanded={learnMoreOpen}
129
+ >
130
+ <span className="chevron" data-open={learnMoreOpen} aria-hidden="true">
131
+ <ChevronDownIcon />
132
+ </span>
133
+ <span>{t('selfie.ess.consent.learnMore')}</span>
134
+ </button>
135
+
136
+ {learnMoreOpen && (
137
+ <p className="learn-more-copy">
138
+ {t('selfie.ess.consent.learnMoreBodyPrefix')}
139
+ <strong>Smile ID</strong>
140
+ {t('selfie.ess.consent.learnMoreBodyAnd')}
141
+ <strong>{partner}</strong>
142
+ {t('selfie.ess.consent.learnMoreBodySuffix')}
143
+ <br />
144
+ {t('selfie.ess.consent.learnMoreShare')}
145
+ <br />
146
+ {t('selfie.ess.consent.learnMoreRightsPrefix')}{' '}
147
+ <a
148
+ href="https://dsar.usesmileid.com"
149
+ target="_blank"
150
+ rel="noopener noreferrer"
151
+ style={{ color: themeColor }}
152
+ >
153
+ {t('selfie.ess.consent.dsarLink')}
154
+ </a>{' '}
155
+ {t('selfie.ess.consent.learnMoreRightsMiddle')}{' '}
156
+ <a
157
+ href="https://usesmileid.com/legal/privacy-policy/"
158
+ target="_blank"
159
+ rel="noopener noreferrer"
160
+ style={{ color: themeColor }}
161
+ >
162
+ Smile ID
163
+ </a>
164
+ {safePolicyUrl ? (
165
+ <>
166
+ {' '}
167
+ {t('selfie.ess.consent.partnerPrivacyTailLinked')}{' '}
168
+ <a
169
+ href={safePolicyUrl}
170
+ target="_blank"
171
+ rel="noopener noreferrer"
172
+ style={{ color: themeColor }}
173
+ >
174
+ {partner}
175
+ </a>{' '}
176
+ {t('selfie.ess.consent.partnerPrivacyTailEnd')}
177
+ </>
178
+ ) : (
179
+ <>
180
+ {' '}
181
+ {t('selfie.ess.consent.partnerPrivacyTailLinked')}{' '}
182
+ <strong>{partner}</strong>{' '}
183
+ {t('selfie.ess.consent.partnerPrivacyTailEnd')}
184
+ </>
185
+ )}
186
+ </p>
187
+ )}
188
+
189
+ <div className="actions">
190
+ <button
191
+ type="button"
192
+ className="allow"
193
+ style={{ background: themeColor, borderColor: themeColor }}
194
+ disabled={!accepted}
195
+ onClick={onGranted}
196
+ >
197
+ {t('selfie.ess.consent.allow')}
198
+ </button>
199
+ <button
200
+ type="button"
201
+ className="deny"
202
+ style={{ color: themeColor, borderColor: themeColor }}
203
+ onClick={onDenied}
204
+ >
205
+ {t('selfie.ess.consent.deny')}
206
+ </button>
207
+ </div>
208
+
209
+ {!hideAttribution && (
210
+ // @ts-expect-error preact-custom-element types
211
+ <powered-by-smile-id />
212
+ )}
213
+
214
+ <style>{`
215
+ :host { display: block; height: 100%; }
216
+ .ess-consent {
217
+ display: flex;
218
+ flex-direction: column;
219
+ min-height: 100%;
220
+ padding: clamp(0.75rem, 2.5dvh, 1.5rem) clamp(1rem, 4vw, 1.25rem) clamp(0.5rem, 1.5dvh, 0.5rem);
221
+ font-family: "DM Sans", system-ui, sans-serif;
222
+ background: #F5F7FA;
223
+ color: #1A1A1A;
224
+ box-sizing: border-box;
225
+ }
226
+ .ess-consent > powered-by-smile-id {
227
+ display: block;
228
+ width: 100%;
229
+ padding-top: clamp(0.5rem, 1.5dvh, 1rem);
230
+ background: #F5F7FA;
231
+ flex-shrink: 0;
232
+ }
233
+ .logos {
234
+ display: flex;
235
+ align-items: center;
236
+ justify-content: center;
237
+ margin-top: clamp(0.5rem, 1.5dvh, 1.25rem);
238
+ margin-bottom: clamp(0.75rem, 2.25dvh, 1.75rem);
239
+ flex-shrink: 0;
240
+ }
241
+ .partner-logo {
242
+ width: 40px;
243
+ height: 40px;
244
+ border-radius: 50%;
245
+ object-fit: cover;
246
+ background: #F9F0E7CC;
247
+ border: 2px solid #F9F0E7CC;
248
+ box-shadow: 0 2px 6px rgba(0,0,0,0.08);
249
+ position: relative;
250
+ z-index: 2;
251
+ }
252
+ .smile-logo {
253
+ width: 40px;
254
+ height: 40px;
255
+ border-radius: 50%;
256
+ background: #F9F0E7CC;
257
+ border: 2px solid #F9F0E7CC;
258
+ box-shadow: 0 2px 6px rgba(0,0,0,0.08);
259
+ display: inline-flex;
260
+ align-items: center;
261
+ justify-content: center;
262
+ margin-left: -5px;
263
+ z-index: 1;
264
+ }
265
+ .smile-logo svg { display: block; }
266
+ .title {
267
+ font-family: "DM Sans", system-ui, sans-serif;
268
+ font-size: clamp(15px, 2.2dvh, 18px);
269
+ font-weight: 600;
270
+ line-height: 1.25;
271
+ text-align: center;
272
+ color: #21232C;
273
+ margin: 0 0 clamp(0.5rem, 1.2dvh, 1rem);
274
+ flex-shrink: 0;
275
+ }
276
+ .body {
277
+ color: #5E646E;
278
+ text-align: justify;
279
+ font-family: "DM Sans", system-ui, sans-serif;
280
+ font-size: clamp(11px, 1.5dvh, 12px);
281
+ font-style: normal;
282
+ font-weight: 400;
283
+ line-height: 1.35;
284
+ margin: 0 0 clamp(0.5rem, 1.2dvh, 1rem);
285
+ flex-shrink: 0;
286
+ }
287
+ .body strong {
288
+ color: #5E646E;
289
+ font-family: "DM Sans", system-ui, sans-serif;
290
+ font-size: 12px;
291
+ font-style: normal;
292
+ font-weight: 700;
293
+ line-height: normal;
294
+ }
295
+ .items {
296
+ list-style: none;
297
+ padding: 0;
298
+ margin: 0 0 clamp(0.5rem, 1.2dvh, 1rem);
299
+ display: flex;
300
+ flex-direction: column;
301
+ gap: clamp(6px, 1.4dvh, 16px);
302
+ flex-shrink: 0;
303
+ }
304
+ .items li {
305
+ display: flex;
306
+ align-items: center;
307
+ gap: 0.85rem;
308
+ color: #21232C;
309
+ font-family: "DM Sans", system-ui, sans-serif;
310
+ font-size: clamp(11px, 1.5dvh, 12px);
311
+ font-style: normal;
312
+ font-weight: 400;
313
+ line-height: 1.35;
314
+ }
315
+ .items img {
316
+ width: clamp(22px, 3.4dvh, 28px);
317
+ height: clamp(22px, 3.4dvh, 28px);
318
+ flex-shrink: 0;
319
+ object-fit: contain;
320
+ object-position: left center;
321
+ }
322
+ .checkbox {
323
+ display: flex;
324
+ align-items: flex-start;
325
+ /* gap = items gap (0.85rem) + half the width difference between an
326
+ items icon (28px) and the 20px checkbox so the trailing text
327
+ column lines up with the text above. */
328
+ gap: calc(0.85rem + 4px);
329
+ margin-bottom: clamp(0.4rem, 1dvh, 0.75rem);
330
+ cursor: pointer;
331
+ flex-shrink: 0;
332
+ }
333
+ .checkbox input { position: absolute; opacity: 0; pointer-events: none; }
334
+ .checkbox .box {
335
+ width: 20px;
336
+ height: 20px;
337
+ /* Center the 20px box within the 28px icon lane used by .items img
338
+ so the icon column visually aligns with those above. */
339
+ margin-left: 4px;
340
+ border-radius: 4px;
341
+ background: transparent;
342
+ border: 1.5px solid #2D2B2A;
343
+ color: #F9F0E7;
344
+ flex-shrink: 0;
345
+ display: inline-flex;
346
+ align-items: center;
347
+ justify-content: center;
348
+ box-sizing: border-box;
349
+ transition: background 120ms ease, border-color 120ms ease;
350
+ }
351
+ .checkbox .box svg { display: block; opacity: 0; transition: opacity 120ms ease; }
352
+ .checkbox input:checked + .box {
353
+ background: #2D2B2A;
354
+ border-color: #2D2B2A;
355
+ }
356
+ .checkbox input:checked + .box svg { opacity: 1; }
357
+ .checkbox .copy {
358
+ color: #5E646E;
359
+ text-align: justify;
360
+ font-family: "DM Sans", system-ui, sans-serif;
361
+ font-size: clamp(11px, 1.5dvh, 12px);
362
+ font-style: normal;
363
+ font-weight: 400;
364
+ line-height: 1.35;
365
+ }
366
+ .learn-more {
367
+ display: inline-flex;
368
+ align-items: center;
369
+ /* Match the checkbox/items icon column so trailing text aligns. */
370
+ gap: calc(0.85rem + 4px);
371
+ background: transparent;
372
+ border: none;
373
+ padding: 0;
374
+ color: #21232C;
375
+ font-family: "DM Sans", system-ui, sans-serif;
376
+ font-size: clamp(11px, 1.5dvh, 12px);
377
+ font-style: normal;
378
+ font-weight: 700;
379
+ line-height: normal;
380
+ cursor: pointer;
381
+ margin-bottom: clamp(0.3rem, 0.8dvh, 0.5rem);
382
+ flex-shrink: 0;
383
+ }
384
+ .learn-more .chevron {
385
+ width: 20px;
386
+ height: 20px;
387
+ /* Center the 20px chevron within the 28px icon lane to align with
388
+ the icons above. */
389
+ margin-left: 4px;
390
+ border-radius: 50%;
391
+ background: #2D2B2A;
392
+ color: #F9F0E7;
393
+ display: inline-flex;
394
+ align-items: center;
395
+ justify-content: center;
396
+ transition: transform 150ms ease;
397
+ flex-shrink: 0;
398
+ }
399
+ .learn-more .chevron[data-open="true"] { transform: rotate(180deg); }
400
+ .learn-more-copy {
401
+ color: #5E646E;
402
+ text-align: justify;
403
+ font-family: "DM Sans", system-ui, sans-serif;
404
+ font-size: clamp(11px, 1.5dvh, 12px);
405
+ font-style: normal;
406
+ font-weight: 400;
407
+ line-height: 1.35;
408
+ margin: 0 0 0.5rem;
409
+ flex-shrink: 1;
410
+ overflow: hidden;
411
+ }
412
+ .learn-more-copy strong {
413
+ color: #5E646E;
414
+ font-family: "DM Sans", system-ui, sans-serif;
415
+ font-size: 12px;
416
+ font-style: normal;
417
+ font-weight: 700;
418
+ line-height: normal;
419
+ }
420
+ .actions {
421
+ display: flex;
422
+ flex-direction: column;
423
+ gap: clamp(0.4rem, 1dvh, 0.75rem);
424
+ align-items: stretch;
425
+ padding-top: clamp(0.5rem, 1.5dvh, 1.5rem);
426
+ flex-shrink: 0;
427
+ }
428
+ .allow {
429
+ width: 100%;
430
+ height: clamp(40px, 6dvh, 48px);
431
+ padding: 0 1rem;
432
+ border-radius: 24px;
433
+ border: 1px solid;
434
+ color: white;
435
+ font-size: clamp(0.95rem, 1.8dvh, 1.05rem);
436
+ font-weight: 600;
437
+ font-family: inherit;
438
+ cursor: pointer;
439
+ box-shadow: 0 1px 2px 0 rgba(16, 24, 40, 0.05);
440
+ }
441
+ .allow:disabled { opacity: 0.45; cursor: not-allowed; }
442
+ .deny {
443
+ width: 100%;
444
+ height: clamp(40px, 6dvh, 48px);
445
+ padding: 0 1rem;
446
+ background: transparent;
447
+ border: 1px solid;
448
+ border-radius: 24px;
449
+ font-size: clamp(0.95rem, 1.8dvh, 1.05rem);
450
+ font-weight: 600;
451
+ font-family: inherit;
452
+ cursor: pointer;
453
+ box-shadow: 0 1px 2px 0 rgba(16, 24, 40, 0.05);
454
+ }
455
+ `}</style>
456
+ </div>
457
+ );
458
+ };
459
+
460
+ export default ConsentView;