@incodetech/prizma-core-screens 0.2.4 → 0.2.12

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 (243) hide show
  1. package/dist/BiometricConsentScreen.d.ts +95 -0
  2. package/dist/BiometricConsentScreen.js +91 -0
  3. package/dist/BiometricConsentStrings.d.ts +84 -0
  4. package/dist/BiometricConsentStrings.js +31 -0
  5. package/dist/CaptureScreen.d.ts +13 -0
  6. package/dist/CaptureScreen.js +2 -2
  7. package/dist/CurpEnterScreen.d.ts +7 -0
  8. package/dist/CurpEnterScreen.js +17 -3
  9. package/dist/CurpGenerateScreen.d.ts +8 -0
  10. package/dist/CurpGenerateScreen.js +28 -13
  11. package/dist/CurpLayout.css +8 -34
  12. package/dist/CurpStrings.d.ts +16 -2
  13. package/dist/DemoScreensLayout.css +16 -0
  14. package/dist/DemoScreensRedirectScreen.d.ts +6 -1
  15. package/dist/DemoScreensRedirectScreen.js +3 -2
  16. package/dist/DemoScreensStrings.d.ts +5 -0
  17. package/dist/DigitalIdUploadErrorScreen.d.ts +31 -0
  18. package/dist/DigitalIdUploadErrorScreen.js +26 -0
  19. package/dist/DigitalIdUploadFileTooLargeScreen.d.ts +27 -0
  20. package/dist/DigitalIdUploadFileTooLargeScreen.js +21 -0
  21. package/dist/DigitalIdUploadReviewScreen.d.ts +57 -0
  22. package/dist/DigitalIdUploadReviewScreen.js +44 -0
  23. package/dist/DigitalIdUploadStrings.d.ts +64 -0
  24. package/dist/DigitalIdUploadStrings.js +19 -0
  25. package/dist/DigitalIdUploadTutorialScreen.d.ts +28 -0
  26. package/dist/DigitalIdUploadTutorialScreen.js +23 -0
  27. package/dist/DocumentCaptureIllustration.d.ts +1 -1
  28. package/dist/EkybAddressDetailsScreen.d.ts +7 -0
  29. package/dist/EkybAddressDetailsScreen.js +25 -7
  30. package/dist/EkybBusinessDetailsScreen.d.ts +6 -0
  31. package/dist/EkybBusinessDetailsScreen.js +23 -6
  32. package/dist/EkybCountryScreen.d.ts +13 -5
  33. package/dist/EkybCountryScreen.js +41 -10
  34. package/dist/EkybOwnersScreen.d.ts +8 -0
  35. package/dist/EkybOwnersScreen.js +26 -3
  36. package/dist/EkybStrings.d.ts +32 -10
  37. package/dist/EkycCountryCatalog.d.ts +4 -1
  38. package/dist/EkycCountryCatalog.js +5 -3
  39. package/dist/EkycCountryFormScreen.d.ts +12 -3
  40. package/dist/EkycCountryFormScreen.js +28 -10
  41. package/dist/EkycDetailsScreen.d.ts +7 -0
  42. package/dist/EkycDetailsScreen.js +21 -4
  43. package/dist/EkycStrings.d.ts +14 -2
  44. package/dist/ElectronicSignatureLayout.css +6 -0
  45. package/dist/EmailInputStrings.d.ts +11 -1
  46. package/dist/EmailInputVerifyScreen.d.ts +7 -0
  47. package/dist/EmailInputVerifyScreen.js +17 -3
  48. package/dist/ErrorScreensScreen.d.ts +5 -2
  49. package/dist/ErrorScreensScreen.js +15 -4
  50. package/dist/ErrorScreensStrings.d.ts +17 -2
  51. package/dist/ErrorScreensStrings.js +18 -10
  52. package/dist/FaceCaptureCapturingScreen.d.ts +63 -0
  53. package/dist/FaceCaptureCapturingScreen.js +62 -2
  54. package/dist/FaceCaptureLayout.css +69 -4
  55. package/dist/FaceCaptureStrings.d.ts +23 -0
  56. package/dist/FaceMatchComparisonScreen.d.ts +3 -3
  57. package/dist/FaceMatchLayout.css +21 -12
  58. package/dist/FormScreen.css +30 -0
  59. package/dist/FormScreen.d.ts +28 -0
  60. package/dist/FormScreen.js +3 -2
  61. package/dist/FormsLongFormScreen.d.ts +8 -0
  62. package/dist/FormsLongFormScreen.js +34 -13
  63. package/dist/FormsStrings.d.ts +11 -1
  64. package/dist/GeolocationBrowserStepsScreen.d.ts +6 -2
  65. package/dist/GeolocationBrowserStepsScreen.js +7 -3
  66. package/dist/GeolocationStrings.d.ts +3 -0
  67. package/dist/IdCaptureLayout.css +205 -2
  68. package/dist/IdCaptureStageScreen.d.ts +47 -0
  69. package/dist/IdCaptureStageScreen.js +39 -3
  70. package/dist/IdCaptureStrings.d.ts +19 -0
  71. package/dist/ListScreen.d.ts +21 -0
  72. package/dist/ListScreen.js +7 -2
  73. package/dist/LoaderScreen.d.ts +7 -5
  74. package/dist/LoaderScreen.js +7 -5
  75. package/dist/LoadersStrings.d.ts +18 -5
  76. package/dist/OcrReviewErrorScreen.d.ts +30 -0
  77. package/dist/OcrReviewErrorScreen.js +24 -0
  78. package/dist/OcrReviewLayout.css +24 -0
  79. package/dist/OcrReviewLoadingScreen.d.ts +25 -0
  80. package/dist/OcrReviewLoadingScreen.js +19 -0
  81. package/dist/OcrReviewScreen.d.ts +76 -0
  82. package/dist/OcrReviewScreen.js +65 -0
  83. package/dist/OcrReviewStrings.d.ts +47 -0
  84. package/dist/OcrReviewStrings.js +17 -0
  85. package/dist/OcrReviewSuccessScreen.d.ts +30 -0
  86. package/dist/OcrReviewSuccessScreen.js +24 -0
  87. package/dist/PermissionsAllowScreen.d.ts +8 -11
  88. package/dist/PermissionsAllowScreen.js +8 -11
  89. package/dist/PermissionsAskScreen.d.ts +41 -0
  90. package/dist/PermissionsAskScreen.js +43 -0
  91. package/dist/PermissionsBrowserStepsScreen.d.ts +9 -2
  92. package/dist/PermissionsBrowserStepsScreen.js +12 -3
  93. package/dist/PermissionsLayout.css +47 -0
  94. package/dist/PermissionsStrings.d.ts +20 -9
  95. package/dist/PermissionsStrings.js +10 -4
  96. package/dist/PhoneInputLayout.css +10 -9
  97. package/dist/PhoneInputOtpScreen.d.ts +8 -0
  98. package/dist/PhoneInputOtpScreen.js +26 -19
  99. package/dist/PhoneInputStrings.d.ts +22 -4
  100. package/dist/PhoneInputVerifyScreen.d.ts +8 -0
  101. package/dist/PhoneInputVerifyScreen.js +36 -7
  102. package/dist/QualifiedSignatureAcceptScreen.js +6 -1
  103. package/dist/QualifiedSignatureDocumentScreen.d.ts +17 -10
  104. package/dist/QualifiedSignatureDocumentScreen.js +22 -17
  105. package/dist/QualifiedSignatureLayout.css +76 -0
  106. package/dist/QualifiedSignatureResultScreen.d.ts +16 -13
  107. package/dist/QualifiedSignatureResultScreen.js +17 -18
  108. package/dist/QualifiedSignatureStrings.d.ts +19 -2
  109. package/dist/ResultScreen.d.ts +8 -0
  110. package/dist/ResultScreen.js +14 -3
  111. package/dist/ReusabilityCredentialSelectionScreen.d.ts +95 -0
  112. package/dist/ReusabilityCredentialSelectionScreen.js +94 -0
  113. package/dist/ReusabilityLayout.css +241 -0
  114. package/dist/ReusabilityPrivacyPolicyScreen.d.ts +55 -0
  115. package/dist/ReusabilityPrivacyPolicyScreen.js +44 -0
  116. package/dist/ReusabilityStrings.d.ts +85 -0
  117. package/dist/ReusabilityStrings.js +37 -0
  118. package/dist/ReviewScreen.d.ts +38 -12
  119. package/dist/ReviewScreen.js +41 -8
  120. package/dist/ScreenLayout.css +382 -6
  121. package/dist/ScreenNav.css +8 -0
  122. package/dist/ScreenNav.d.ts +15 -2
  123. package/dist/ScreenNav.js +10 -3
  124. package/dist/VideoSelfieErrorScreen.js +1 -1
  125. package/dist/VideoSelfieIdCaptureScreen.d.ts +10 -3
  126. package/dist/VideoSelfieIdCaptureScreen.js +45 -14
  127. package/dist/VideoSelfieLayout.css +467 -34
  128. package/dist/VideoSelfieSelfieCaptureScreen.d.ts +44 -27
  129. package/dist/VideoSelfieSelfieCaptureScreen.js +65 -39
  130. package/dist/VideoSelfieSpeakScreen.js +19 -2
  131. package/dist/VideoSelfieStrings.d.ts +82 -13
  132. package/dist/VideoSelfieTurnHeadScreen.d.ts +51 -0
  133. package/dist/VideoSelfieTurnHeadScreen.js +66 -0
  134. package/dist/VideoSelfieTutorialScreen.d.ts +9 -0
  135. package/dist/VideoSelfieTutorialScreen.js +22 -5
  136. package/dist/VideoSelfieWaveScreen.d.ts +53 -0
  137. package/dist/VideoSelfieWaveScreen.js +62 -0
  138. package/dist/ViewfinderScreen.css +36 -0
  139. package/dist/WatchlistBusinessFormScreen.d.ts +11 -7
  140. package/dist/WatchlistBusinessFormScreen.js +27 -13
  141. package/dist/WatchlistBusinessStrings.d.ts +21 -7
  142. package/dist/flows.d.ts +8 -0
  143. package/dist/flows.js +17 -0
  144. package/dist/index.d.ts +19 -1
  145. package/dist/index.js +14 -0
  146. package/dist/styles.css +3 -0
  147. package/dist/types.d.ts +3 -1
  148. package/package.json +79 -3
  149. package/samples/video-selfie-camera-wide.jpg +0 -0
  150. package/samples/video-selfie-room-wide.jpg +0 -0
  151. package/samples/video-selfie-room.png +0 -0
  152. package/src/BiometricConsentScreen.tsx +192 -0
  153. package/src/BiometricConsentStrings.ts +119 -0
  154. package/src/CaptureScreen.tsx +17 -0
  155. package/src/CurpEnterScreen.tsx +21 -4
  156. package/src/CurpGenerateScreen.tsx +46 -22
  157. package/src/CurpLayout.css +8 -34
  158. package/src/CurpStrings.ts +22 -2
  159. package/src/DemoScreensLayout.css +16 -0
  160. package/src/DemoScreensRedirectScreen.tsx +14 -6
  161. package/src/DemoScreensStrings.ts +5 -0
  162. package/src/DigitalIdUploadErrorScreen.tsx +83 -0
  163. package/src/DigitalIdUploadFileTooLargeScreen.tsx +67 -0
  164. package/src/DigitalIdUploadReviewScreen.tsx +119 -0
  165. package/src/DigitalIdUploadStrings.ts +88 -0
  166. package/src/DigitalIdUploadTutorialScreen.tsx +59 -0
  167. package/src/DocumentCaptureIllustration.tsx +1 -1
  168. package/src/EkybAddressDetailsScreen.tsx +32 -4
  169. package/src/EkybBusinessDetailsScreen.tsx +23 -5
  170. package/src/EkybCountryScreen.tsx +45 -7
  171. package/src/EkybOwnersScreen.tsx +34 -3
  172. package/src/EkybStrings.ts +34 -10
  173. package/src/EkycCountryCatalog.ts +9 -4
  174. package/src/EkycCountryFormScreen.tsx +51 -12
  175. package/src/EkycDetailsScreen.tsx +22 -1
  176. package/src/EkycStrings.ts +14 -2
  177. package/src/ElectronicSignatureLayout.css +6 -0
  178. package/src/EmailInputStrings.ts +12 -1
  179. package/src/EmailInputVerifyScreen.tsx +21 -4
  180. package/src/ErrorScreensScreen.tsx +15 -4
  181. package/src/ErrorScreensStrings.ts +37 -12
  182. package/src/FaceCaptureCapturingScreen.tsx +102 -13
  183. package/src/FaceCaptureLayout.css +69 -4
  184. package/src/FaceCaptureStrings.ts +24 -0
  185. package/src/FaceMatchComparisonScreen.tsx +3 -3
  186. package/src/FaceMatchLayout.css +21 -12
  187. package/src/FormScreen.css +30 -0
  188. package/src/FormScreen.tsx +34 -0
  189. package/src/FormsLongFormScreen.tsx +41 -13
  190. package/src/FormsStrings.ts +12 -1
  191. package/src/GeolocationBrowserStepsScreen.tsx +6 -3
  192. package/src/GeolocationStrings.ts +3 -0
  193. package/src/IdCaptureLayout.css +205 -2
  194. package/src/IdCaptureStageScreen.tsx +75 -0
  195. package/src/IdCaptureStrings.ts +19 -0
  196. package/src/ListScreen.tsx +58 -10
  197. package/src/LoaderScreen.tsx +7 -5
  198. package/src/LoadersStrings.ts +18 -5
  199. package/src/OcrReviewErrorScreen.tsx +53 -0
  200. package/src/OcrReviewLayout.css +24 -0
  201. package/src/OcrReviewLoadingScreen.tsx +48 -0
  202. package/src/OcrReviewScreen.tsx +190 -0
  203. package/src/OcrReviewStrings.ts +69 -0
  204. package/src/OcrReviewSuccessScreen.tsx +52 -0
  205. package/src/PermissionsAllowScreen.tsx +9 -10
  206. package/src/PermissionsAskScreen.tsx +105 -0
  207. package/src/PermissionsBrowserStepsScreen.tsx +11 -3
  208. package/src/PermissionsLayout.css +47 -0
  209. package/src/PermissionsStrings.ts +42 -12
  210. package/src/PhoneInputLayout.css +10 -9
  211. package/src/PhoneInputOtpScreen.tsx +23 -3
  212. package/src/PhoneInputStrings.ts +23 -4
  213. package/src/PhoneInputVerifyScreen.tsx +41 -5
  214. package/src/QualifiedSignatureAcceptScreen.tsx +12 -1
  215. package/src/QualifiedSignatureDocumentScreen.tsx +31 -19
  216. package/src/QualifiedSignatureLayout.css +76 -0
  217. package/src/QualifiedSignatureResultScreen.tsx +28 -26
  218. package/src/QualifiedSignatureStrings.ts +19 -2
  219. package/src/ResultScreen.tsx +33 -2
  220. package/src/ReusabilityCredentialSelectionScreen.tsx +246 -0
  221. package/src/ReusabilityLayout.css +241 -0
  222. package/src/ReusabilityPrivacyPolicyScreen.tsx +109 -0
  223. package/src/ReusabilityStrings.ts +127 -0
  224. package/src/ReviewScreen.tsx +96 -27
  225. package/src/ScreenLayout.css +382 -6
  226. package/src/ScreenNav.css +8 -0
  227. package/src/ScreenNav.tsx +35 -5
  228. package/src/VideoSelfieErrorScreen.tsx +1 -1
  229. package/src/VideoSelfieIdCaptureScreen.tsx +125 -31
  230. package/src/VideoSelfieLayout.css +467 -34
  231. package/src/VideoSelfieSelfieCaptureScreen.tsx +110 -57
  232. package/src/VideoSelfieSpeakScreen.tsx +34 -15
  233. package/src/VideoSelfieStrings.ts +89 -14
  234. package/src/VideoSelfieTurnHeadScreen.tsx +162 -0
  235. package/src/VideoSelfieTutorialScreen.tsx +45 -23
  236. package/src/VideoSelfieWaveScreen.tsx +143 -0
  237. package/src/ViewfinderScreen.css +36 -0
  238. package/src/WatchlistBusinessFormScreen.tsx +35 -16
  239. package/src/WatchlistBusinessStrings.ts +21 -7
  240. package/src/flows.ts +25 -0
  241. package/src/index.ts +88 -3
  242. package/src/styles.css +3 -0
  243. package/src/types.ts +3 -1
@@ -1,52 +1,69 @@
1
1
  import React, { type ReactNode } from 'react';
2
- import { type PrizmaCaptureScreenProps } from './CaptureScreen';
2
+ import { type PrizmaScreenShellProps } from './ScreenShell';
3
3
  import type { PrizmaScreenActionHandler, PrizmaScreenOverrides } from './types';
4
- import type { VideoSelfieFeedbackKind, VideoSelfieStrings } from './VideoSelfieStrings';
4
+ import type { VideoSelfieDocumentType, VideoSelfieFeedbackKind, VideoSelfieStrings } from './VideoSelfieStrings';
5
5
  import './ScreenLayout.css';
6
+ import './ViewfinderScreen.css';
7
+ import './VideoSelfieLayout.css';
6
8
  /** Which purple liveness section to render (group 8). */
7
9
  export type VideoSelfieSelfieState = 'start' | 'processing' | 'success' | 'failed';
8
- export interface VideoSelfieSelfieCaptureScreenProps extends Omit<PrizmaCaptureScreenProps, 'state' | 'feedbackText' | 'statusLabels' | 'verifiedLabel' | 'navLabel'> {
10
+ export interface VideoSelfieSelfieCaptureScreenProps extends Omit<PrizmaScreenShellProps, 'dark' | 'title' | 'children'> {
9
11
  /** Module copy — this screen reads the `selfieCapture` slice. */
10
12
  strings: VideoSelfieStrings;
11
13
  /** Which purple section of group 8 to render. Default `start`. */
12
14
  state?: VideoSelfieSelfieState;
13
15
  /**
14
- * Which of group 9's ELEVEN live-feedback lines to show under the ring.
15
- * Only meaningful while the ring is live (`state="start"`): the sections of
16
+ * Which of group 9's live-feedback lines to show under the ring. Only
17
+ * meaningful while the ring is live (`state="start"`): the sections of
16
18
  * group 9 are that same frame with a different instruction. Omit for `8.0`,
17
19
  * which shows the plain "Look at your front camera" line.
18
20
  */
19
21
  feedback?: VideoSelfieFeedbackKind;
20
- /** Live camera feed (<video>) or sample still (<img>). */
22
+ /** Which document the journey collects — the Stepper's own `documentType`. */
23
+ documentType?: VideoSelfieDocumentType;
24
+ /** Live camera feed (<video>) or sample still (<img>), full-bleed behind the
25
+ * ring — NOT inside it. The overlay recipe makes the ring a window. */
21
26
  camera?: ReactNode;
22
27
  screenId?: string;
23
28
  overrides?: PrizmaScreenOverrides;
24
29
  onAction?: PrizmaScreenActionHandler;
25
30
  }
26
31
  /**
27
- * Video Selfie — the liveness ring (Archetype B, GUIDELINES.md §6-B).
28
- * ONE screen for FIFTEEN purple sections: `selfie capture 8.0–8.3` and every
29
- * `selfie capture feedback 9.1–9.11` message. They are `state` × `feedbackText`
30
- * variants of Core's `PrizmaSelfieCapture`, which owns the ring, the
31
- * silhouette, the ring offset (40), the instruction gap (32), the instruction
32
- * typography and every verdict treatment. None of it is re-implemented (§7),
33
- * and this file declares no layout at all.
32
+ * Video Selfie — the liveness ring (Archetype D, GUIDELINES.md §6-D; always
33
+ * dark). ONE screen for every purple section of groups 8 and 9: `selfie
34
+ * capture 8.0–8.3` and every `selfie capture feedback 9.x` message. They are
35
+ * `state` × `feedbackText` variants of Core's `PrizmaSelfieCapture`, which is
36
+ * exactly how the component is axed.
34
37
  *
35
- * **Figma vs Core — Core wins, reported.** These sections draw the camera
36
- * FULL-BLEED behind a scrimmed ring, on a black surface. Core's
37
- * `PrizmaSelfieCapture` puts the feed inside the ring's circular window on a
38
- * light surface, and pins light while the feed is open on purpose — "the
39
- * capture screen stays WHITE in both modes, light is needed for the shot"
40
- * (owner decision 2026-07-14, DEVIATIONS #F-27). Reshaping it here would mean
41
- * re-implementing a Core primitive with a second full-bleed recipe, so the
42
- * screen ships Core's treatment and the divergence is logged, exactly as the
43
- * Face Capture pilot settled it.
38
+ * **Rebuilt 2026-09-30 to follow the frames.** The first pass shipped Core's
39
+ * INLINE recipe — the feed inside a circular window on a WHITE surface, the
40
+ * verdicts dissolving the ring into a centred mark — and logged the gap. On
41
+ * the module page that read as a different product from the rest of the flow:
42
+ * a small ring on white between two full-bleed dark camera screens, with the
43
+ * ring vanishing on the very beats Figma keeps it (owner, 2026-09-30: "they
44
+ * are looking very broken… a broken experience"). Rather than hand-roll the
45
+ * full-bleed composition here, Core's `PrizmaSelfieCapture` grew the
46
+ * `mode="overlay"` + `scrim` axis `PrizmaIdCapture` already carries — it is
47
+ * the Figma library's own `Capture / Selfie overlay` component — and this
48
+ * screen composes it. Nothing about the ring, the scrim, the glow sweeps, the
49
+ * verdict marks or the instruction typography is re-implemented (§7).
50
+ * DEVIATIONS #VS-22.
44
51
  *
45
- * Figma also draws the journey stepper at the top of all fifteen sections.
46
- * Archetype B has no slot for it and this module does not hand-roll one —
47
- * reported. Nothing collapses without it: the four states and the eleven
48
- * messages already render distinctly through Core's own ring treatments and
49
- * instruction copy.
52
+ * The composition, measured off `8.0` (390×844):
53
+ *
54
+ * - the camera runs FULL-BLEED on `.prizma-viewfinder__surface` (`selfie bg
55
+ * image`, 390×844 — it passes under the browser chrome too);
56
+ * - the journey stepper sits in the band the logo nav normally occupies
57
+ * (`top content` y=56, the Stepper 342 wide at x=24), the module-wide
58
+ * treatment `VideoSelfieLayout.css` owns;
59
+ * - the ring is 335 centred in the remaining air (Figma 27,188 → 362,523
60
+ * against a 708-high content area), with the instruction stacked under it.
61
+ * Core's own Spacing/32 gap replaces Figma's 25.5 (the token wins, §4).
62
+ *
63
+ * NO verified tag: `8.x` and `9.x` draw nothing between the instruction and
64
+ * the bottom of the screen (GUIDELINES §1 — follow the module's section, not
65
+ * the default). The document stage keeps its tag because its own sections
66
+ * draw one (#VS-3).
50
67
  */
51
68
  export declare const VideoSelfieSelfieCaptureScreen: React.ForwardRefExoticComponent<VideoSelfieSelfieCaptureScreenProps & React.RefAttributes<HTMLDivElement>>;
52
69
  export default VideoSelfieSelfieCaptureScreen;
@@ -1,58 +1,84 @@
1
1
  'use client';
2
- import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { forwardRef } from 'react';
4
- import { PrizmaCaptureScreen } from './CaptureScreen';
4
+ import { PrizmaSelfieCapture, } from '@incodetech/prizma-core-web/selfie-capture';
5
+ import { PrizmaStepper } from '@incodetech/prizma-core-web/stepper';
6
+ import { PrizmaScreenShell } from './ScreenShell';
5
7
  import './ScreenLayout.css';
6
- /** Purple state → Core `PrizmaSelfieCapture` state. Core models the in-flight
7
- * and verdict beats as capture STATES — the ring dissolves into the spinner or
8
- * the status mark in its own slot (DEVIATIONS #11) — so all four are one
9
- * archetype call, not a separate processing screen. */
8
+ import './ViewfinderScreen.css';
9
+ import './VideoSelfieLayout.css';
10
+ /**
11
+ * Purple state → Core `PrizmaSelfieCapture` state, in the OVERLAY recipe.
12
+ *
13
+ * `start` is `filled`, not `empty`: `empty` is Core's *searching* beat and
14
+ * draws the dashed silhouette guide, which `8.0` and every `9.x` message
15
+ * deliberately hide (`silhouette hidden=true` on the frames — the guide
16
+ * belongs to the head-turn steps, which do draw it). `filled` is Core's
17
+ * "window open, neutral ring, no guide" beat, and in overlay mode there is no
18
+ * still to fill it with: the host's feed shows straight through the ring.
19
+ */
10
20
  const CAPTURE_STATE = {
11
- start: 'ready',
21
+ start: 'filled',
12
22
  processing: 'uploading',
13
23
  success: 'success',
14
24
  failed: 'error',
15
25
  };
26
+ /** Which beat of the seven-step journey the liveness ring occupies — the
27
+ * microphone grant is 1, the two document sides 2 and 3. */
28
+ const STEP = 4;
16
29
  /**
17
- * Video Selfie — the liveness ring (Archetype B, GUIDELINES.md §6-B).
18
- * ONE screen for FIFTEEN purple sections: `selfie capture 8.0–8.3` and every
19
- * `selfie capture feedback 9.1–9.11` message. They are `state` × `feedbackText`
20
- * variants of Core's `PrizmaSelfieCapture`, which owns the ring, the
21
- * silhouette, the ring offset (40), the instruction gap (32), the instruction
22
- * typography and every verdict treatment. None of it is re-implemented (§7),
23
- * and this file declares no layout at all.
30
+ * Video Selfie — the liveness ring (Archetype D, GUIDELINES.md §6-D; always
31
+ * dark). ONE screen for every purple section of groups 8 and 9: `selfie
32
+ * capture 8.0–8.3` and every `selfie capture feedback 9.x` message. They are
33
+ * `state` × `feedbackText` variants of Core's `PrizmaSelfieCapture`, which is
34
+ * exactly how the component is axed.
35
+ *
36
+ * **Rebuilt 2026-09-30 to follow the frames.** The first pass shipped Core's
37
+ * INLINE recipe — the feed inside a circular window on a WHITE surface, the
38
+ * verdicts dissolving the ring into a centred mark — and logged the gap. On
39
+ * the module page that read as a different product from the rest of the flow:
40
+ * a small ring on white between two full-bleed dark camera screens, with the
41
+ * ring vanishing on the very beats Figma keeps it (owner, 2026-09-30: "they
42
+ * are looking very broken… a broken experience"). Rather than hand-roll the
43
+ * full-bleed composition here, Core's `PrizmaSelfieCapture` grew the
44
+ * `mode="overlay"` + `scrim` axis `PrizmaIdCapture` already carries — it is
45
+ * the Figma library's own `Capture / Selfie overlay` component — and this
46
+ * screen composes it. Nothing about the ring, the scrim, the glow sweeps, the
47
+ * verdict marks or the instruction typography is re-implemented (§7).
48
+ * DEVIATIONS #VS-22.
49
+ *
50
+ * The composition, measured off `8.0` (390×844):
24
51
  *
25
- * **Figma vs Core — Core wins, reported.** These sections draw the camera
26
- * FULL-BLEED behind a scrimmed ring, on a black surface. Core's
27
- * `PrizmaSelfieCapture` puts the feed inside the ring's circular window on a
28
- * light surface, and pins light while the feed is open on purpose — "the
29
- * capture screen stays WHITE in both modes, light is needed for the shot"
30
- * (owner decision 2026-07-14, DEVIATIONS #F-27). Reshaping it here would mean
31
- * re-implementing a Core primitive with a second full-bleed recipe, so the
32
- * screen ships Core's treatment and the divergence is logged, exactly as the
33
- * Face Capture pilot settled it.
52
+ * - the camera runs FULL-BLEED on `.prizma-viewfinder__surface` (`selfie bg
53
+ * image`, 390×844 — it passes under the browser chrome too);
54
+ * - the journey stepper sits in the band the logo nav normally occupies
55
+ * (`top content` y=56, the Stepper 342 wide at x=24), the module-wide
56
+ * treatment `VideoSelfieLayout.css` owns;
57
+ * - the ring is 335 centred in the remaining air (Figma 27,188 → 362,523
58
+ * against a 708-high content area), with the instruction stacked under it.
59
+ * Core's own Spacing/32 gap replaces Figma's 25.5 (the token wins, §4).
34
60
  *
35
- * Figma also draws the journey stepper at the top of all fifteen sections.
36
- * Archetype B has no slot for it and this module does not hand-roll one —
37
- * reported. Nothing collapses without it: the four states and the eleven
38
- * messages already render distinctly through Core's own ring treatments and
39
- * instruction copy.
61
+ * NO verified tag: `8.x` and `9.x` draw nothing between the instruction and
62
+ * the bottom of the screen (GUIDELINES §1 — follow the module's section, not
63
+ * the default). The document stage keeps its tag because its own sections
64
+ * draw one (#VS-3).
40
65
  */
41
- export const VideoSelfieSelfieCaptureScreen = forwardRef(({ strings, state = 'start', feedback, camera, screenId = `video-selfie-selfie-capture-${feedback ?? state}`, overrides, onAction, ...shell }, ref) => {
66
+ export const VideoSelfieSelfieCaptureScreen = forwardRef(({ strings, state = 'start', feedback, documentType = 'id', camera, screenId = `video-selfie-selfie-capture-${feedback ?? state}`, overrides, onAction, className, ...shell }, ref) => {
42
67
  const slice = strings.selfieCapture;
43
68
  /* The live ring carries an instruction; the in-flight and verdict beats
44
69
  carry Core's own status copy instead (its `statusLabels` slot), which is
45
70
  why the feedback line is scoped to the live state. */
46
- const feedbackText = CAPTURE_STATE[state] === 'ready'
47
- ? feedback
48
- ? slice.feedback[feedback]
49
- : slice.startLabel
50
- : undefined;
51
- return (_jsx(PrizmaCaptureScreen, { ref: ref, state: CAPTURE_STATE[state], cameraOpen: state === 'start', camera: camera, feedbackText: feedbackText, statusLabels: {
52
- uploading: slice.processingLabel,
53
- success: slice.successLabel,
54
- error: slice.failedLabel,
55
- }, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, screenId: screenId, overrides: overrides, onAction: onAction, ...shell }));
71
+ const feedbackText = state === 'start' ? (feedback ? slice.feedback[feedback] : slice.startLabel) : undefined;
72
+ return (_jsxs(PrizmaScreenShell, { ref: ref, dark: true, "data-pcs-overlay": "window", className: className, ...shell, children: [_jsx("div", { className: "prizma-viewfinder__surface", children: camera }), _jsx("div", { className: "video-selfie-stepper", children: _jsx(PrizmaStepper, { steps: 7, currentState: STEP, documentType: documentType, completedValueText: strings.stepperCompletedLabel }) }), _jsx("div", { className: [
73
+ 'prizma-viewfinder__content',
74
+ overrides?.align === 'left' ? 'prizma-viewfinder__content--left' : '',
75
+ ]
76
+ .filter(Boolean)
77
+ .join(' '), children: _jsx("div", { className: "video-selfie-band video-selfie-band--ring", children: _jsx(PrizmaSelfieCapture, { mode: "overlay", scrim: true, state: CAPTURE_STATE[state], feedbackText: feedbackText, statusLabels: {
78
+ uploading: slice.processingLabel,
79
+ success: slice.successLabel,
80
+ error: slice.failedLabel,
81
+ } }) }) })] }));
56
82
  });
57
83
  VideoSelfieSelfieCaptureScreen.displayName = 'VideoSelfieSelfieCaptureScreen';
58
84
  export default VideoSelfieSelfieCaptureScreen;
@@ -59,7 +59,21 @@ export const VideoSelfieSpeakScreen = forwardRef(({ strings, family = 'voiceCons
59
59
  onPrimaryClick: () => onAction?.({ type: 'cta', screenId }),
60
60
  secondaryLabel: slice.confirm.retryLabel,
61
61
  onSecondaryClick: () => onAction?.({ type: 'secondary', screenId }),
62
- } })) : stage === 'processing' ? (_jsx(PrizmaModalContent, { title: slice.processingLabel, showButtonGroup: false, closeLabel: slice.closeLabel, onClose: () => onAction?.({ type: 'tertiary', screenId }), body: _jsx(PrizmaLoadingSpinner, { loadingLabel: slice.processingLabel }) })) : stage === 'success' ? (_jsx(PrizmaModalContent, { title: slice.successLabel, showButtonGroup: false, closeLabel: slice.closeLabel, onClose: () => onAction?.({ type: 'tertiary', screenId }), body: _jsx("span", { className: "video-selfie-sheet-status", children: _jsx(IconStatusPositive, { size: "xl" }) }) })) : (_jsx(PrizmaModalContent, { title: prompt, subtitle: slice.eyebrow,
62
+ } })) : stage === 'processing' || stage === 'success' ? (
63
+ /* The two verdict cards. They carry no buttons, so left alone they came
64
+ out shorter than the prompt they replace and the sheet jumped between
65
+ steps; and Core's Modal renders `body` AFTER the heading, which put
66
+ the spinner and the check UNDER their own label. Both are fixed in
67
+ the module: `--status` gives the card the prompt's measure and centres
68
+ its content, and the mark is passed as the LEADING slot so it stacks
69
+ above the copy the way `10.2` / `10.3` draw it.
70
+
71
+ No ✕ either. Figma draws one, but it is the same call #VS-5 already
72
+ made on the prompt: closing mid-step strands the user with no way
73
+ back, and a dismiss that appears on two of five cards and not the
74
+ other three is the inconsistency, not the fix (owner, 2026-09-30).
75
+ DEVIATIONS #VS-25. */
76
+ _jsx(PrizmaModalContent, { title: stage === 'processing' ? slice.processingLabel : slice.successLabel, showCloseIcon: false, showButtonGroup: false, leading: _jsx("span", { className: "video-selfie-sheet-status", children: stage === 'processing' ? (_jsx(PrizmaLoadingSpinner, { loadingLabel: slice.processingLabel })) : (_jsx(IconStatusPositive, { size: "xl" })) }) })) : (_jsx(PrizmaModalContent, { title: prompt, subtitle: slice.eyebrow,
63
77
  /* No dismiss ✕ on the record step: the sheet IS the step, and closing it
64
78
  would strand the user mid-recording with no way back into the flow
65
79
  (owner-reported 2026-08-19). The confirm sheet already suppressed it;
@@ -79,7 +93,7 @@ export const VideoSelfieSpeakScreen = forwardRef(({ strings, family = 'voiceCons
79
93
  secondaryLabel: slice.idleCtaLabel,
80
94
  onSecondaryClick: () => onAction?.({ type: 'cta', screenId }),
81
95
  } }));
82
- return (_jsxs(PrizmaScreenShell, { ref: ref, dark: true, className: className, ...shell, children: [_jsx("div", { className: "prizma-viewfinder__surface", children: camera }), _jsx("div", { className: "video-selfie-stepper", children: _jsx(PrizmaStepper, { steps: 7, currentState: STEP[family], documentType: documentType, completedValueText: strings.stepperCompletedLabel }) }), _jsx("div", { className: [
96
+ return (_jsxs(PrizmaScreenShell, { ref: ref, dark: true, "data-pcs-overlay": "window", className: className, ...shell, children: [_jsx("div", { className: "prizma-viewfinder__surface", children: camera }), _jsx("div", { className: "video-selfie-stepper", children: _jsx(PrizmaStepper, { steps: 7, currentState: STEP[family], documentType: documentType, completedValueText: strings.stepperCompletedLabel }) }), _jsx("div", { className: [
83
97
  'prizma-viewfinder__content',
84
98
  overrides?.align === 'left' ? 'prizma-viewfinder__content--left' : '',
85
99
  ]
@@ -87,6 +101,9 @@ export const VideoSelfieSpeakScreen = forwardRef(({ strings, family = 'voiceCons
87
101
  .join(' '), children: _jsx("div", { className: "video-selfie-band", children: stage === 'recording' ? (_jsxs("span", { className: "video-selfie-recording", children: [_jsx(IconSoundWave, { size: "xl" }), _jsx("span", { className: "prizma-viewfinder__title", children: slice.recordingLabel })] })) : null }) }), isConfirm ? (_jsx("div", { className: "video-selfie-sheet-scrim", "aria-hidden": "true" })) : null, _jsx("div", { "data-prizma-theme": "light", className: [
88
102
  'video-selfie-sheet-panel',
89
103
  isConfirm ? 'video-selfie-sheet-panel--centered' : '',
104
+ stage === 'processing' || stage === 'success'
105
+ ? 'video-selfie-sheet-panel--status'
106
+ : '',
90
107
  ]
91
108
  .filter(Boolean)
92
109
  .join(' '), children: card })] }));
@@ -13,18 +13,58 @@ export interface VideoSelfieTutorialRow {
13
13
  * (its icon set is documented as "the Video Selfie sequence").
14
14
  */
15
15
  export type VideoSelfieTutorialRowKind = 'recording' | 'idFront' | 'idBack' | 'passport' | 'frontCamera' | 'waveHand' | 'questions' | 'speak';
16
- /** The eleven live-feedback messages of group 9, in Figma order. */
17
- export type VideoSelfieFeedbackKind = 'dark' | 'blurry' | 'maskDetected' | 'glassesDetected' | 'headTilted' | 'lookAtCamera' | 'turnRight' | 'turnLeft' | 'alignFace' | 'moveCloser' | 'moveAway';
16
+ /** The live-feedback messages of group 9, in Figma order. Fourteen since
17
+ * 2026-09-30: `9.12`–`9.14` (head cover · closed eyes · face occlusion) were
18
+ * added to the frames after the module's first build and are on the Web, iOS
19
+ * and Android full-flow maps. */
20
+ export type VideoSelfieFeedbackKind = 'dark' | 'blurry' | 'maskDetected' | 'glassesDetected' | 'headTilted' | 'lookAtCamera' | 'turnRight' | 'turnLeft' | 'alignFace' | 'moveCloser' | 'moveAway' | 'headCover' | 'closedEyes' | 'faceOcclusion';
21
+ /** Which head-turn the liveness step is asking for (the `🙂‍↔️ Turn Head` band
22
+ * of the full-flow frames: Left · Right · Upward · Downward, each drawn as a
23
+ * guide beat and a captured beat). */
24
+ export type VideoSelfieTurnDirection = 'left' | 'right' | 'up' | 'down';
18
25
  /** Which face of the document the ID stage is on (groups 3 and 5). */
19
26
  export type VideoSelfieIdSide = 'front' | 'back';
20
- /** Why an ID read was rejected (group 6, the `.1`–`.4` variants). */
21
27
  /**
22
- * REMOVED (2026-08-19). A rejection-REASON axis (`scanFailed | name | photo |
23
- * typeMismatch`) was invented: group 6 draws exactly FOUR sections —
24
- * `6.1.1`/`6.2.1` (front try-again / failed) and `6.3.1`/`6.4.1` (back) — with
25
- * no reason axis at all, so the four kinds multiplied the gallery to 16 error
26
- * cards where the spec has 4. Severity is the only real axis. DEVIATIONS #VS-1.
28
+ * Why an ID read was rejected — the `.1`–`.4` suffix of every group 6 section.
29
+ *
30
+ * RESTORED 2026-09-30. It was removed on 2026-08-19 on the reading that "group
31
+ * 6 draws exactly four sections", because the `📐 Screens specs & behavior`
32
+ * page documents ONE representative per group (`6.1.1`, `6.2.1`, `6.3.1`,
33
+ * `6.4.1`) and the rest are component variants inside it. The `🔸 Full flow
34
+ * map` draws them all, and they carry DIFFERENT copy, not a translation of
35
+ * one line: `6.2.2 - name failed` reads "Name verification failed",
36
+ * `6.2.3 - photo failed` reads "ID photo verification failed",
37
+ * `6.1.4 - type mismatch try again` reads "ID type mismatch, show the same ID".
38
+ * Collapsing them to one line lost three rejections a user can actually hit.
39
+ * DEVIATIONS #VS-1.
27
40
  */
41
+ export type VideoSelfieIdErrorReason = 'scan' | 'name' | 'photo' | 'typeMismatch';
42
+ /**
43
+ * A live CONDITION the document stage can coach the user out of, as opposed
44
+ * to a rejection it has to report.
45
+ *
46
+ * BOTH legs run five of these before the identity checks — the frames draw
47
+ * them on the back only, and the owner extended them to the front on
48
+ * 2026-09-30 ("I think it would be nice to add them for the front as well").
49
+ * Each one draws its OWN screen: the frame in its
50
+ * matching red treatment with a `Capture feedback` bubble above it saying
51
+ * what to do. They were all wired to the single "ID scan failed, try again"
52
+ * card until 2026-09-30 — the five screens are `frame` nodes rather than
53
+ * component instances in the flow map, so the first trace walked straight
54
+ * past them (owner: "we do have those states in figma").
55
+ *
56
+ * All five map 1:1 onto states Core already ships: four onto
57
+ * `PrizmaIdCapture`'s own `error-*` treatments, and `blurry` onto the plain
58
+ * error border plus the bubble, which is the recipe the owner set for
59
+ * detection results on 2026-07-28 (the border is the state, the message is
60
+ * the bubble).
61
+ */
62
+ /** One coaching bubble's copy: what is wrong, and what to do about it. */
63
+ export interface VideoSelfieConditionCopy {
64
+ title: string;
65
+ subtitle: string;
66
+ }
67
+ export type VideoSelfieIdCondition = 'wrongSide' | 'notAligned' | 'blurry' | 'glare' | 'tooDark';
28
68
  /** Whether the rejection invites a retry (`6.1` / `6.3`) or is terminal
29
69
  * (`6.2` / `6.4`). */
30
70
  export type VideoSelfieIdErrorSeverity = 'tryAgain' | 'failed';
@@ -125,9 +165,26 @@ export interface VideoSelfieStrings {
125
165
  processingLabel: string;
126
166
  /** Accepted label (`3.3` / `5.3`). */
127
167
  successLabel: string;
128
- /** Rejection copy, keyed reason → severity (group 6). */
129
- /** Rejection copy per severity (group 6: try-again vs failed). */
130
- errors: Record<VideoSelfieIdErrorSeverity, string>;
168
+ /** Rejection copy, keyed reason → severity (group 6). The two axes are
169
+ * independent: `6.1.x` / `6.3.x` are the try-again lines, `6.2.x` /
170
+ * `6.4.x` the terminal ones, and the `.1`–`.4` suffix is the reason. The
171
+ * two sides share the copy — only the stepper beat differs. */
172
+ errors: Record<VideoSelfieIdErrorReason, Record<VideoSelfieIdErrorSeverity, string>>;
173
+ /**
174
+ * The five coaching bubbles — title + what to do about it.
175
+ *
176
+ * Only `wrongSide` is keyed by side, because it is the only one that
177
+ * NAMES the face the user should be showing; the other four describe the
178
+ * shot, which is the same problem whichever face is up. Keying all five by
179
+ * side would duplicate four strings per language for nothing.
180
+ */
181
+ conditions: {
182
+ wrongSide: Record<VideoSelfieIdSide, VideoSelfieConditionCopy>;
183
+ notAligned: VideoSelfieConditionCopy;
184
+ blurry: VideoSelfieConditionCopy;
185
+ glare: VideoSelfieConditionCopy;
186
+ tooDark: VideoSelfieConditionCopy;
187
+ };
131
188
  };
132
189
  /** Group 4 — the flip interstitial. */
133
190
  flipId: {
@@ -147,8 +204,17 @@ export interface VideoSelfieStrings {
147
204
  successLabel: string;
148
205
  /** `8.3` rejected. */
149
206
  failedLabel: string;
150
- /** Group 9 — the eleven live-feedback lines. */
207
+ /** Group 9 — the fourteen live-feedback lines. */
151
208
  feedback: Record<VideoSelfieFeedbackKind, string>;
209
+ /** The head-turn step's own instruction, one per direction (the
210
+ * `🙂‍↔️ Turn Head` band of the full-flow frames). */
211
+ turn: Record<VideoSelfieTurnDirection, string>;
212
+ /** The wave step's instruction and its three checkpoints (`👋 Wave`). */
213
+ wave: {
214
+ instruction: string;
215
+ steps: string[];
216
+ doneLabel: string;
217
+ };
152
218
  };
153
219
  /** Groups 10 · 11 — the spoken steps. */
154
220
  speak: Record<VideoSelfieSpeakFamily, VideoSelfieSpeakStrings>;
@@ -186,9 +252,12 @@ export interface VideoSelfieStrings {
186
252
  ctaLabel: string;
187
253
  quitLabel: string;
188
254
  };
189
- /** `14.4` — connectivity. */
255
+ /** `14.4` — connectivity. The supporting line is the generic family's
256
+ * (`ErrorScreensStrings.noConnection.subtitle`), so this screen reads
257
+ * like every other module's connectivity screen (#F-34, #PI-8). */
190
258
  noConnection: {
191
259
  title: string;
260
+ subtitle?: string;
192
261
  ctaLabel: string;
193
262
  quitLabel: string;
194
263
  };
@@ -0,0 +1,51 @@
1
+ import React, { type ReactNode } from 'react';
2
+ import { type PrizmaScreenShellProps } from './ScreenShell';
3
+ import type { PrizmaScreenActionHandler, PrizmaScreenOverrides } from './types';
4
+ import type { VideoSelfieDocumentType, VideoSelfieStrings, VideoSelfieTurnDirection } from './VideoSelfieStrings';
5
+ import './ScreenLayout.css';
6
+ import './ViewfinderScreen.css';
7
+ import './VideoSelfieLayout.css';
8
+ /** The two beats each direction is drawn in: the stage ASKING (dashed guide
9
+ * ring) and the stage having SEEN it (the positive ring). */
10
+ export type VideoSelfieTurnStage = 'ask' | 'done';
11
+ export interface VideoSelfieTurnHeadScreenProps extends Omit<PrizmaScreenShellProps, 'dark' | 'title' | 'children'> {
12
+ /** Module copy — this screen reads `selfieCapture.turn`. */
13
+ strings: VideoSelfieStrings;
14
+ /** Which way the user is asked to turn. Default `left`. */
15
+ direction?: VideoSelfieTurnDirection;
16
+ /** Asking, or seen. Default `ask`. */
17
+ stage?: VideoSelfieTurnStage;
18
+ /** Which document the journey collects — the Stepper's own `documentType`. */
19
+ documentType?: VideoSelfieDocumentType;
20
+ /** Live camera feed (<video>) or sample still (<img>), full-bleed behind
21
+ * the ring. */
22
+ camera?: ReactNode;
23
+ screenId?: string;
24
+ overrides?: PrizmaScreenOverrides;
25
+ onAction?: PrizmaScreenActionHandler;
26
+ }
27
+ /**
28
+ * Video Selfie — the head-turn beats of the liveness sequence (Archetype D,
29
+ * GUIDELINES.md §6-D; always dark). NEW 2026-09-30: the `🙂‍↔️ Turn Head` band
30
+ * of the Web / iOS / Android full-flow frames draws four directions, each in
31
+ * two beats, and none of them existed in the screens package — the module
32
+ * jumped straight from the liveness ring to the spoken steps.
33
+ *
34
+ * The frames are the liveness stage with three changes, and nothing else:
35
+ *
36
+ * - the ring is Core's GUIDE stroke (`ring="dashed"`) while the stage waits
37
+ * for the turn, and the positive ring once it has seen it;
38
+ * - the silhouette guide is ON (Core's `empty`), because the user is being
39
+ * asked to move their face rather than hold it still;
40
+ * - a small arrow chip sits between the ring and the instruction, saying
41
+ * which way.
42
+ *
43
+ * The mark under the ring is a canonical bank glyph at 2xl — the disc is part
44
+ * of the drawing, not something this screen paints. `PrizmaTitle.icon` takes
45
+ * `status | spinner | none` only (DEVIATIONS #10), so the block below the ring
46
+ * is composed here; it declares a gap and nothing else. CORE REQUEST stands: a
47
+ * `glyph` slot on the capture stage, so this and Face Capture's future hint
48
+ * marks share one component. DEVIATIONS #VS-20.
49
+ */
50
+ export declare const VideoSelfieTurnHeadScreen: React.ForwardRefExoticComponent<VideoSelfieTurnHeadScreenProps & React.RefAttributes<HTMLDivElement>>;
51
+ export default VideoSelfieTurnHeadScreen;
@@ -0,0 +1,66 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { forwardRef } from 'react';
4
+ import { IconChevronDown, IconChevronLeft, IconChevronRight, IconChevronUp, } from '@incodetech/prizma-core-icons';
5
+ import { PrizmaSelfieCapture } from '@incodetech/prizma-core-web/selfie-capture';
6
+ import { PrizmaStepper } from '@incodetech/prizma-core-web/stepper';
7
+ import { PrizmaScreenShell } from './ScreenShell';
8
+ import './ScreenLayout.css';
9
+ import './ViewfinderScreen.css';
10
+ import './VideoSelfieLayout.css';
11
+ /**
12
+ * Direction → the mark under the ring, from the canonical bank at 2xl (48) —
13
+ * the size the frames draw it (`element container` 342×48, the glyph centred).
14
+ *
15
+ * Since the Core and Enterprise icon unification (Sofia, 2026-10-06) the
16
+ * mark is the library chevron at 2xl: the four solid circle arrows left the
17
+ * bank (their old names now alias the chevrons). Nothing is drawn here (§10).
18
+ *
19
+ * The mark never mirrors in RTL (Sofia, 2026-10-07): a head turn is a
20
+ * physical direction, so the chevrons keep their LTR orientation in Hebrew.
21
+ * The opt-out is one scoped rule in `VideoSelfieLayout.css`; the chevrons
22
+ * still mirror everywhere else.
23
+ */
24
+ const TURN_ICON = {
25
+ left: _jsx(IconChevronLeft, { size: "2xl" }),
26
+ right: _jsx(IconChevronRight, { size: "2xl" }),
27
+ up: _jsx(IconChevronUp, { size: "2xl" }),
28
+ down: _jsx(IconChevronDown, { size: "2xl" }),
29
+ };
30
+ /**
31
+ * Video Selfie — the head-turn beats of the liveness sequence (Archetype D,
32
+ * GUIDELINES.md §6-D; always dark). NEW 2026-09-30: the `🙂‍↔️ Turn Head` band
33
+ * of the Web / iOS / Android full-flow frames draws four directions, each in
34
+ * two beats, and none of them existed in the screens package — the module
35
+ * jumped straight from the liveness ring to the spoken steps.
36
+ *
37
+ * The frames are the liveness stage with three changes, and nothing else:
38
+ *
39
+ * - the ring is Core's GUIDE stroke (`ring="dashed"`) while the stage waits
40
+ * for the turn, and the positive ring once it has seen it;
41
+ * - the silhouette guide is ON (Core's `empty`), because the user is being
42
+ * asked to move their face rather than hold it still;
43
+ * - a small arrow chip sits between the ring and the instruction, saying
44
+ * which way.
45
+ *
46
+ * The mark under the ring is a canonical bank glyph at 2xl — the disc is part
47
+ * of the drawing, not something this screen paints. `PrizmaTitle.icon` takes
48
+ * `status | spinner | none` only (DEVIATIONS #10), so the block below the ring
49
+ * is composed here; it declares a gap and nothing else. CORE REQUEST stands: a
50
+ * `glyph` slot on the capture stage, so this and Face Capture's future hint
51
+ * marks share one component. DEVIATIONS #VS-20.
52
+ */
53
+ export const VideoSelfieTurnHeadScreen = forwardRef(({ strings, direction = 'left', stage = 'ask', documentType = 'id', camera, screenId = `video-selfie-turn-${direction}-${stage}`, overrides, onAction, className, ...shell }, ref) => (_jsxs(PrizmaScreenShell, { ref: ref, dark: true, "data-pcs-overlay": "window", className: className, ...shell, children: [_jsx("div", { className: "prizma-viewfinder__surface", children: camera }), _jsx("div", { className: "video-selfie-stepper", children: _jsx(PrizmaStepper, { steps: 7, currentState: 4, documentType: documentType, completedValueText: strings.stepperCompletedLabel }) }), _jsx("div", { className: [
54
+ 'prizma-viewfinder__content',
55
+ overrides?.align === 'left' ? 'prizma-viewfinder__content--left' : '',
56
+ ]
57
+ .filter(Boolean)
58
+ .join(' '), children: _jsxs("div", { className: "video-selfie-band video-selfie-band--ring", children: [_jsx(PrizmaSelfieCapture, { mode: "overlay", scrim: true,
59
+ /* The ring says WHICH WAY: the half on the side being asked for
60
+ is dashed while the stage waits and positive once it has seen
61
+ the turn, and the other half stays the neutral track. The state
62
+ is `filled` on both beats — the verdict lives in the arc, not in
63
+ the whole ring, so nothing else about the stage changes. */
64
+ guide: { side: direction, state: stage === 'ask' ? 'waiting' : 'seen' }, state: "filled", showFeedback: false }), _jsxs("div", { className: "video-selfie-turn", children: [_jsx("span", { className: "video-selfie-turn__mark", children: TURN_ICON[direction] }), _jsx("p", { className: "prizma-viewfinder__title", children: strings.selfieCapture.turn[direction] })] })] }) })] })));
65
+ VideoSelfieTurnHeadScreen.displayName = 'VideoSelfieTurnHeadScreen';
66
+ export default VideoSelfieTurnHeadScreen;
@@ -3,6 +3,7 @@ import { type PrizmaListScreenProps } from './ListScreen';
3
3
  import type { PrizmaScreenActionHandler, PrizmaScreenOverrides } from './types';
4
4
  import type { VideoSelfieDocumentType, VideoSelfieStrings } from './VideoSelfieStrings';
5
5
  import './ScreenLayout.css';
6
+ import './VideoSelfieLayout.css';
6
7
  export interface VideoSelfieTutorialScreenProps extends Omit<PrizmaListScreenProps, 'status' | 'title' | 'subtitle' | 'children' | 'ctaLabel' | 'secondaryCtaLabel' | 'linkLabel' | 'verifiedLabel' | 'navLabel'> {
7
8
  /** Module copy — this screen reads the `tutorial` slice. */
8
9
  strings: VideoSelfieStrings;
@@ -28,6 +29,14 @@ export interface VideoSelfieTutorialScreenProps extends Omit<PrizmaListScreenPro
28
29
  * NOT archetype A: A centres one illustration in the flexible middle; here the
29
30
  * middle is a seven-row list that must span the column and start at the top —
30
31
  * the distinction `ScreenLayout.css` draws between `__flex` and `__list`.
32
+ *
33
+ * The one class it carries, `video-selfie-tutorial`, exists so the module's
34
+ * stylesheet can reach this screen on a SHORT panel: seven rows fit the 844
35
+ * reference to the pixel (measured 497 of 497), and on a 640 or 693 one they
36
+ * paint straight through the CTA. `VideoSelfieLayout.css` lets them pan there,
37
+ * the same concession the Android settings walk-through already makes with
38
+ * `scrollList` — and `scrollList` itself is not used here because its
39
+ * Spacing/16 margin would take the reference below the 497 the rows need.
31
40
  */
32
41
  export declare const VideoSelfieTutorialScreen: React.ForwardRefExoticComponent<VideoSelfieTutorialScreenProps & React.RefAttributes<HTMLDivElement>>;
33
42
  export default VideoSelfieTutorialScreen;
@@ -1,10 +1,11 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { forwardRef } from 'react';
4
- import { IconBankCard, IconIdFront, IconMessages, IconMicrophone, IconNumberedList, IconPassport, IconUser, IconWavingHand, } from '@incodetech/prizma-core-icons';
5
- import { PrizmaListItem } from '@incodetech/prizma-core-web/list';
4
+ import { IconBankCard, IconMessages, IconMicrophone, IconNumberedList, IconPassportCapture, IconUser, IconUserInformation, IconWavingHand, } from '@incodetech/prizma-core-icons';
5
+ import { PrizmaList } from '@incodetech/prizma-core-web/list';
6
6
  import { PrizmaListScreen } from './ListScreen';
7
7
  import './ScreenLayout.css';
8
+ import './VideoSelfieLayout.css';
8
9
  /**
9
10
  * Journey beat → canonical bank glyph. ONE map, in one place — the same seven
10
11
  * glyphs Core's `PrizmaStepper` carries (its icon set is documented as "the
@@ -13,9 +14,13 @@ import './ScreenLayout.css';
13
14
  */
14
15
  const ROW_ICONS = {
15
16
  recording: _jsx(IconMicrophone, { size: "m" }),
16
- idFront: _jsx(IconIdFront, { size: "m" }),
17
+ // `user-information` and `passport-capture`, NOT `id-front` and `passport`:
18
+ // the tutorial promises the steps the Stepper then counts off, and the
19
+ // Stepper's own sequence uses these two. Corrected 2026-09-30 — the tutorial
20
+ // was drawing a different ID glyph from the one the journey shows.
21
+ idFront: _jsx(IconUserInformation, { size: "m" }),
17
22
  idBack: _jsx(IconBankCard, { size: "m" }),
18
- passport: _jsx(IconPassport, { size: "m" }),
23
+ passport: _jsx(IconPassportCapture, { size: "m" }),
19
24
  frontCamera: _jsx(IconUser, { size: "m" }),
20
25
  waveHand: _jsx(IconWavingHand, { size: "m" }),
21
26
  questions: _jsx(IconNumberedList, { size: "m" }),
@@ -35,7 +40,19 @@ const ROW_ICONS = {
35
40
  * NOT archetype A: A centres one illustration in the flexible middle; here the
36
41
  * middle is a seven-row list that must span the column and start at the top —
37
42
  * the distinction `ScreenLayout.css` draws between `__flex` and `__list`.
43
+ *
44
+ * The one class it carries, `video-selfie-tutorial`, exists so the module's
45
+ * stylesheet can reach this screen on a SHORT panel: seven rows fit the 844
46
+ * reference to the pixel (measured 497 of 497), and on a 640 or 693 one they
47
+ * paint straight through the CTA. `VideoSelfieLayout.css` lets them pan there,
48
+ * the same concession the Android settings walk-through already makes with
49
+ * `scrollList` — and `scrollList` itself is not used here because its
50
+ * Spacing/16 margin would take the reference below the 497 the rows need.
38
51
  */
39
- export const VideoSelfieTutorialScreen = forwardRef(({ strings, documentType = 'id', screenId = `video-selfie-tutorial-${documentType}`, overrides, onAction, ...shell }, ref) => (_jsx(PrizmaListScreen, { ref: ref, title: strings.tutorial.title, subtitle: strings.tutorial.subtitle, ctaLabel: strings.tutorial.ctaLabel, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, screenId: screenId, overrides: overrides, onAction: onAction, ...shell, children: _jsx("ul", { className: "video-selfie-tutorial-list", children: strings.tutorial.rows[documentType].map((row) => (_jsx("li", { children: _jsx(PrizmaListItem, { variant: "icon-m", label: row.label, icon: ROW_ICONS[row.kind] }) }, row.kind))) }) })));
52
+ export const VideoSelfieTutorialScreen = forwardRef(({ strings, documentType = 'id', screenId = `video-selfie-tutorial-${documentType}`, overrides, onAction, className, ...shell }, ref) => (_jsx(PrizmaListScreen, { ref: ref, className: ['video-selfie-tutorial', className].filter(Boolean).join(' '), title: strings.tutorial.title, subtitle: strings.tutorial.subtitle, ctaLabel: strings.tutorial.ctaLabel, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, screenId: screenId, overrides: overrides, onAction: onAction, ...shell, children: _jsx(PrizmaList, { variant: "icons-chain", showItem3: true, showItem4: true, showItem5: true, showItem6: true, showItem7: true, items: strings.tutorial.rows[documentType].map((row) => ({
53
+ variant: 'icon-connector',
54
+ label: row.label,
55
+ icon: ROW_ICONS[row.kind],
56
+ })) }) })));
40
57
  VideoSelfieTutorialScreen.displayName = 'VideoSelfieTutorialScreen';
41
58
  export default VideoSelfieTutorialScreen;