@incodetech/prizma-core-screens 0.2.7 → 0.2.14

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 (191) 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/DemoScreensLayout.css +16 -0
  8. package/dist/DemoScreensRedirectScreen.d.ts +6 -1
  9. package/dist/DemoScreensRedirectScreen.js +3 -2
  10. package/dist/DemoScreensStrings.d.ts +5 -0
  11. package/dist/DigitalIdDeviceWalletsDocumentSelectionScreen.d.ts +34 -0
  12. package/dist/DigitalIdDeviceWalletsDocumentSelectionScreen.js +47 -0
  13. package/dist/DigitalIdDeviceWalletsResultScreen.d.ts +28 -0
  14. package/dist/DigitalIdDeviceWalletsResultScreen.js +23 -0
  15. package/dist/DigitalIdDeviceWalletsStrings.d.ts +55 -0
  16. package/dist/DigitalIdDeviceWalletsStrings.js +19 -0
  17. package/dist/DigitalIdProvidersDocumentSelectionScreen.d.ts +31 -0
  18. package/dist/DigitalIdProvidersDocumentSelectionScreen.js +37 -0
  19. package/dist/DigitalIdProvidersErrorScreen.d.ts +33 -0
  20. package/dist/DigitalIdProvidersErrorScreen.js +33 -0
  21. package/dist/DigitalIdProvidersLayout.css +59 -0
  22. package/dist/DigitalIdProvidersRedirectScreen.d.ts +24 -0
  23. package/dist/DigitalIdProvidersRedirectScreen.js +18 -0
  24. package/dist/DigitalIdProvidersStrings.d.ts +72 -0
  25. package/dist/DigitalIdProvidersStrings.js +15 -0
  26. package/dist/DigitalIdUploadErrorScreen.d.ts +31 -0
  27. package/dist/DigitalIdUploadErrorScreen.js +26 -0
  28. package/dist/DigitalIdUploadFileTooLargeScreen.d.ts +27 -0
  29. package/dist/DigitalIdUploadFileTooLargeScreen.js +21 -0
  30. package/dist/DigitalIdUploadReviewScreen.d.ts +57 -0
  31. package/dist/DigitalIdUploadReviewScreen.js +44 -0
  32. package/dist/DigitalIdUploadStrings.d.ts +64 -0
  33. package/dist/DigitalIdUploadStrings.js +19 -0
  34. package/dist/DigitalIdUploadTutorialScreen.d.ts +28 -0
  35. package/dist/DigitalIdUploadTutorialScreen.js +23 -0
  36. package/dist/DocumentCaptureIllustration.d.ts +1 -1
  37. package/dist/EkybCountryScreen.d.ts +6 -6
  38. package/dist/EkybCountryScreen.js +20 -7
  39. package/dist/EkybStrings.d.ts +6 -1
  40. package/dist/ErrorScreensScreen.d.ts +5 -2
  41. package/dist/ErrorScreensScreen.js +15 -4
  42. package/dist/ErrorScreensStrings.d.ts +17 -2
  43. package/dist/ErrorScreensStrings.js +18 -10
  44. package/dist/FaceCaptureCapturingScreen.d.ts +63 -0
  45. package/dist/FaceCaptureCapturingScreen.js +62 -2
  46. package/dist/FaceCaptureLayout.css +69 -4
  47. package/dist/FaceCaptureStrings.d.ts +23 -0
  48. package/dist/FormScreen.css +14 -0
  49. package/dist/FormScreen.d.ts +28 -0
  50. package/dist/FormScreen.js +3 -2
  51. package/dist/IdCaptureLayout.css +165 -2
  52. package/dist/IdCaptureStageScreen.d.ts +47 -0
  53. package/dist/IdCaptureStageScreen.js +39 -3
  54. package/dist/IdCaptureStrings.d.ts +19 -0
  55. package/dist/ListScreen.d.ts +19 -0
  56. package/dist/ListScreen.js +7 -2
  57. package/dist/LoaderScreen.d.ts +7 -5
  58. package/dist/LoaderScreen.js +7 -5
  59. package/dist/LoadersStrings.d.ts +18 -5
  60. package/dist/OcrReviewErrorScreen.d.ts +30 -0
  61. package/dist/OcrReviewErrorScreen.js +24 -0
  62. package/dist/OcrReviewLayout.css +24 -0
  63. package/dist/OcrReviewLoadingScreen.d.ts +25 -0
  64. package/dist/OcrReviewLoadingScreen.js +19 -0
  65. package/dist/OcrReviewScreen.d.ts +76 -0
  66. package/dist/OcrReviewScreen.js +65 -0
  67. package/dist/OcrReviewStrings.d.ts +47 -0
  68. package/dist/OcrReviewStrings.js +17 -0
  69. package/dist/OcrReviewSuccessScreen.d.ts +30 -0
  70. package/dist/OcrReviewSuccessScreen.js +24 -0
  71. package/dist/PermissionsAllowScreen.d.ts +8 -11
  72. package/dist/PermissionsAllowScreen.js +8 -11
  73. package/dist/PermissionsAskScreen.d.ts +41 -0
  74. package/dist/PermissionsAskScreen.js +43 -0
  75. package/dist/PermissionsBrowserStepsScreen.d.ts +9 -2
  76. package/dist/PermissionsBrowserStepsScreen.js +12 -3
  77. package/dist/PermissionsLayout.css +47 -0
  78. package/dist/PermissionsStrings.d.ts +20 -9
  79. package/dist/PermissionsStrings.js +10 -4
  80. package/dist/PhoneInputOtpScreen.d.ts +8 -0
  81. package/dist/PhoneInputOtpScreen.js +26 -19
  82. package/dist/PhoneInputStrings.d.ts +11 -3
  83. package/dist/PhoneInputVerifyScreen.js +21 -5
  84. package/dist/QualifiedSignatureDocumentScreen.d.ts +8 -5
  85. package/dist/QualifiedSignatureDocumentScreen.js +9 -6
  86. package/dist/QualifiedSignatureResultScreen.d.ts +16 -13
  87. package/dist/QualifiedSignatureResultScreen.js +17 -18
  88. package/dist/QualifiedSignatureStrings.d.ts +11 -4
  89. package/dist/ResultScreen.d.ts +8 -0
  90. package/dist/ResultScreen.js +14 -3
  91. package/dist/ReusabilityCredentialSelectionScreen.d.ts +95 -0
  92. package/dist/ReusabilityCredentialSelectionScreen.js +94 -0
  93. package/dist/ReusabilityLayout.css +241 -0
  94. package/dist/ReusabilityPrivacyPolicyScreen.d.ts +55 -0
  95. package/dist/ReusabilityPrivacyPolicyScreen.js +44 -0
  96. package/dist/ReusabilityStrings.d.ts +85 -0
  97. package/dist/ReusabilityStrings.js +37 -0
  98. package/dist/ReviewScreen.d.ts +38 -12
  99. package/dist/ReviewScreen.js +41 -8
  100. package/dist/ScreenLayout.css +382 -6
  101. package/dist/VideoSelfieErrorScreen.js +1 -1
  102. package/dist/VideoSelfieIdCaptureScreen.js +1 -1
  103. package/dist/VideoSelfieLayout.css +304 -2
  104. package/dist/VideoSelfieSelfieCaptureScreen.js +1 -1
  105. package/dist/VideoSelfieSpeakScreen.js +1 -1
  106. package/dist/VideoSelfieStrings.d.ts +4 -1
  107. package/dist/VideoSelfieTurnHeadScreen.js +14 -11
  108. package/dist/VideoSelfieTutorialScreen.d.ts +9 -0
  109. package/dist/VideoSelfieTutorialScreen.js +10 -1
  110. package/dist/VideoSelfieWaveScreen.js +1 -1
  111. package/dist/ViewfinderScreen.css +36 -0
  112. package/dist/flows.d.ts +8 -0
  113. package/dist/flows.js +17 -0
  114. package/dist/index.d.ts +23 -0
  115. package/dist/index.js +17 -0
  116. package/dist/styles.css +4 -0
  117. package/package.json +94 -3
  118. package/samples/video-selfie-camera-wide.jpg +0 -0
  119. package/samples/video-selfie-room-wide.jpg +0 -0
  120. package/src/BiometricConsentScreen.tsx +192 -0
  121. package/src/BiometricConsentStrings.ts +119 -0
  122. package/src/CaptureScreen.tsx +17 -0
  123. package/src/DemoScreensLayout.css +16 -0
  124. package/src/DemoScreensRedirectScreen.tsx +14 -6
  125. package/src/DemoScreensStrings.ts +5 -0
  126. package/src/DigitalIdDeviceWalletsDocumentSelectionScreen.tsx +108 -0
  127. package/src/DigitalIdDeviceWalletsResultScreen.tsx +76 -0
  128. package/src/DigitalIdDeviceWalletsStrings.ts +80 -0
  129. package/src/DigitalIdProvidersDocumentSelectionScreen.tsx +112 -0
  130. package/src/DigitalIdProvidersErrorScreen.tsx +104 -0
  131. package/src/DigitalIdProvidersLayout.css +59 -0
  132. package/src/DigitalIdProvidersRedirectScreen.tsx +65 -0
  133. package/src/DigitalIdProvidersStrings.ts +98 -0
  134. package/src/DigitalIdUploadErrorScreen.tsx +83 -0
  135. package/src/DigitalIdUploadFileTooLargeScreen.tsx +67 -0
  136. package/src/DigitalIdUploadReviewScreen.tsx +119 -0
  137. package/src/DigitalIdUploadStrings.ts +88 -0
  138. package/src/DigitalIdUploadTutorialScreen.tsx +59 -0
  139. package/src/DocumentCaptureIllustration.tsx +1 -1
  140. package/src/EkybCountryScreen.tsx +23 -6
  141. package/src/EkybStrings.ts +6 -1
  142. package/src/ErrorScreensScreen.tsx +15 -4
  143. package/src/ErrorScreensStrings.ts +37 -12
  144. package/src/FaceCaptureCapturingScreen.tsx +102 -13
  145. package/src/FaceCaptureLayout.css +69 -4
  146. package/src/FaceCaptureStrings.ts +24 -0
  147. package/src/FormScreen.css +14 -0
  148. package/src/FormScreen.tsx +34 -0
  149. package/src/IdCaptureLayout.css +165 -2
  150. package/src/IdCaptureStageScreen.tsx +75 -0
  151. package/src/IdCaptureStrings.ts +19 -0
  152. package/src/ListScreen.tsx +48 -9
  153. package/src/LoaderScreen.tsx +7 -5
  154. package/src/LoadersStrings.ts +18 -5
  155. package/src/OcrReviewErrorScreen.tsx +53 -0
  156. package/src/OcrReviewLayout.css +24 -0
  157. package/src/OcrReviewLoadingScreen.tsx +48 -0
  158. package/src/OcrReviewScreen.tsx +190 -0
  159. package/src/OcrReviewStrings.ts +69 -0
  160. package/src/OcrReviewSuccessScreen.tsx +52 -0
  161. package/src/PermissionsAllowScreen.tsx +9 -10
  162. package/src/PermissionsAskScreen.tsx +105 -0
  163. package/src/PermissionsBrowserStepsScreen.tsx +11 -3
  164. package/src/PermissionsLayout.css +47 -0
  165. package/src/PermissionsStrings.ts +42 -12
  166. package/src/PhoneInputOtpScreen.tsx +23 -3
  167. package/src/PhoneInputStrings.ts +11 -3
  168. package/src/PhoneInputVerifyScreen.tsx +24 -4
  169. package/src/QualifiedSignatureDocumentScreen.tsx +9 -7
  170. package/src/QualifiedSignatureResultScreen.tsx +28 -26
  171. package/src/QualifiedSignatureStrings.ts +11 -4
  172. package/src/ResultScreen.tsx +33 -2
  173. package/src/ReusabilityCredentialSelectionScreen.tsx +246 -0
  174. package/src/ReusabilityLayout.css +241 -0
  175. package/src/ReusabilityPrivacyPolicyScreen.tsx +109 -0
  176. package/src/ReusabilityStrings.ts +127 -0
  177. package/src/ReviewScreen.tsx +96 -27
  178. package/src/ScreenLayout.css +382 -6
  179. package/src/VideoSelfieErrorScreen.tsx +1 -1
  180. package/src/VideoSelfieIdCaptureScreen.tsx +9 -1
  181. package/src/VideoSelfieLayout.css +304 -2
  182. package/src/VideoSelfieSelfieCaptureScreen.tsx +9 -1
  183. package/src/VideoSelfieSpeakScreen.tsx +9 -1
  184. package/src/VideoSelfieStrings.ts +4 -2
  185. package/src/VideoSelfieTurnHeadScreen.tsx +25 -14
  186. package/src/VideoSelfieTutorialScreen.tsx +11 -0
  187. package/src/VideoSelfieWaveScreen.tsx +9 -1
  188. package/src/ViewfinderScreen.css +36 -0
  189. package/src/flows.ts +25 -0
  190. package/src/index.ts +116 -3
  191. package/src/styles.css +4 -0
@@ -11,13 +11,14 @@ export interface PermissionsReasonRow {
11
11
  description?: string;
12
12
  }
13
13
  /**
14
- * Which browser's "turn it back on" journey to show. `safariIos` is the base
15
- * `1-4-2-id` frame; the other four are the `Android browsers` section of the
16
- * Web flow (4:8159). Re-enabling a permission is a different journey in every
17
- * browser, which is why one screen carries five step lists rather than five
18
- * screens carrying one skeleton.
14
+ * Which browser's "turn it back on" journey to show. The SAME nine browsers,
15
+ * keys and order as Geolocation's (`GeolocationBrowser`), because a refused
16
+ * camera and a refused location are the same journey through the same browser
17
+ * settings; only the permission's name changes (Camila, 2026-10-07). The
18
+ * Permissions Figma draws five of them (`1-4-2-id` for Safari plus the four
19
+ * `Android browsers` frames, 4:8159); the other four follow Geolocation.
19
20
  */
20
- export type PermissionsBrowser = 'safariIos' | 'chromeAndroid' | 'firefoxAndroid' | 'samsungAndroid' | 'operaAndroid';
21
+ export type PermissionsBrowser = 'safariIos' | 'chromeIos' | 'firefoxIos' | 'operaIos' | 'chromeAndroid' | 'firefoxAndroid' | 'operaAndroid' | 'samsungAndroid' | 'unrecognizedAndroid' | 'chromeDesktop' | 'edgeDesktop' | 'firefoxDesktop' | 'safariMac';
21
22
  /**
22
23
  * Step kind → canonical bank glyph, resolved in
23
24
  * `PermissionsBrowserStepsScreen`. Figma leads every row with OS/browser chrome
@@ -28,14 +29,14 @@ export type PermissionsBrowser = 'safariIos' | 'chromeAndroid' | 'firefoxAndroid
28
29
  * escalated (`GeolocationBrowserStepsScreen`); this family reuses it rather
29
30
  * than inventing a second one.
30
31
  */
31
- export type PermissionsStepKind = 'settings' | 'browser' | 'menu' | 'permissions' | 'camera' | 'toggle' | 'device' | 'apps' | 'return';
32
+ export type PermissionsStepKind = 'addressBar' | 'settings' | 'browser' | 'menu' | 'permissions' | 'camera' | 'toggle' | 'device' | 'apps' | 'return';
32
33
  export interface PermissionsStep {
33
34
  kind: PermissionsStepKind;
34
35
  label: string;
35
36
  }
36
37
  export interface PermissionsBrowserSteps {
37
38
  /** Names the browser under the headline. Figma labels the section, not the
38
- * screen; the label keeps five otherwise near-identical cards apart. */
39
+ * screen; the label keeps nine otherwise near-identical cards apart. */
39
40
  label: string;
40
41
  steps: PermissionsStep[];
41
42
  }
@@ -44,6 +45,16 @@ export interface PermissionsStrings {
44
45
  navLabel?: string;
45
46
  /** "verified by" attribution copy for the footer tag. */
46
47
  verifiedLabel: string;
48
+ /** `1-3-1-id-permission-modal` — the pre-permission sheet, the very first
49
+ * ask. Its "Allow" is what raises the real OS prompt. */
50
+ ask: {
51
+ title: string;
52
+ subtitle?: string;
53
+ /** Primary — raises the OS prompt. */
54
+ allowLabel: string;
55
+ /** Secondary — declines; the flow moves on to `required`. */
56
+ denyLabel: string;
57
+ };
47
58
  /** `1-3-2-id` — the permission was refused and the flow explains why it needs it. */
48
59
  required: {
49
60
  title: string;
@@ -54,7 +65,7 @@ export interface PermissionsStrings {
54
65
  quitLabel: string;
55
66
  ctaLabel: string;
56
67
  };
57
- /** `1-3-2-id-allow-camera` — the first ask, before any refusal. */
68
+ /** `1-3-2-id-allow-camera` — the softer ask after the REAL OS prompt was refused. */
58
69
  allow: {
59
70
  title: string;
60
71
  subtitle?: string;
@@ -12,20 +12,26 @@
12
12
  // is the ID Capture-flavoured copy of the same skeleton; it stays where it is so
13
13
  // nothing that imports it breaks (DEVIATIONS #IDC-26).
14
14
  //
15
- // The section's seven frames collapse onto THREE built screens:
15
+ // The section's seven frames collapse onto FOUR built screens:
16
16
  //
17
+ // 1-3-1-id-permission-modal → PermissionsAskScreen (the sheet)
17
18
  // 1-3-2-id → PermissionsRequiredScreen (reason rows)
18
19
  // 1-3-2-id-allow-camera → PermissionsAllowScreen (subtitle)
19
20
  // 1-4-2-id (+ 4 Android siblings) → PermissionsBrowserStepsScreen
20
21
  //
21
- // The other four are OS chrome and are NOT built:
22
+ // The other three are OS chrome and are NOT built:
22
23
  //
23
- // 1-3-1-id-permission-modal the browser's own "Allow camera access" sheet
24
24
  // 1-4-1-id the iOS "…Would Like to Access the Camera" alert
25
25
  // 1-3-2-id-quit-process an iOS confirm alert layered over 1-3-2-id
26
26
  // 1-3-2-id-allow-camera-quit the same alert layered over the allow screen
27
27
  //
28
- // None of them is a Prizma surface: their chrome does not exist in Core and
28
+ // `1-3-1-id-permission-modal` was on this list until 2026-10-07. It is not the
29
+ // browser's prompt: it is the SDK's own camera pre-permission sheet (the SDK
30
+ // release notes call it "the camera permission sheet", shown by every module
31
+ // that asks for the camera), drawn with Core's Bottom Sheet dialog and Button
32
+ // Group. So it is built, from those two components (DEVIATIONS #GEN-1).
33
+ //
34
+ // None of the remaining three is a Prizma surface: their chrome does not exist in Core and
29
35
  // rebuilding it would mean inventing primitives and shipping platform rasters,
30
36
  // both forbidden (rule 1, GUIDELINES §10). Same call as Document Capture's iOS
31
37
  // action sheets (#DC-20) and Geolocation's system alert. Core Lab draws them as
@@ -13,6 +13,14 @@ export interface PhoneInputOtpScreenProps extends Omit<PrizmaFormScreenProps, 't
13
13
  value?: string;
14
14
  /** How many digits the code carries. Default 6. */
15
15
  length?: number;
16
+ /**
17
+ * Seconds left before the code can be resent. While it is above 0 (and the
18
+ * copy carries `otp.resendCountdownLabel`), the resend line becomes one
19
+ * disabled link, "Resend code in 30s", exactly as Figma draws
20
+ * `phoneNumber.OTP.typing`. The host owns the timer; the screen only draws
21
+ * the number it is given.
22
+ */
23
+ resendIn?: number;
16
24
  screenId?: string;
17
25
  overrides?: PrizmaScreenOverrides;
18
26
  onAction?: PrizmaScreenActionHandler;
@@ -21,24 +21,31 @@ import './PhoneInputLayout.css';
21
21
  * wins; `PrizmaOtp.loading` stays correct for hosts that want the in-field
22
22
  * spinner. DEVIATIONS #PI-5.
23
23
  */
24
- export const PhoneInputOtpScreen = forwardRef(({ strings, state = 'typing', value, length = 6, screenId = `phone-input-otp-${state}`, overrides, onAction, ...shell }, ref) => (_jsx(PrizmaFormScreen, { ref: ref, title: strings.otp.title, subtitle: strings.otp.subtitle, ctaLabel: strings.otp.ctaLabel,
25
- /* Figma greys the CTA until the code is complete. */
26
- ctaDisabled: state === 'typing',
27
- /* The spinner rides the CTA, not the field (see the note above). */
28
- ctaLoading: state === 'processing', ctaLoadingLabel: strings.otp.ctaLoadingLabel,
29
- /* "Didn't receive the code? Resend code or Change phone number" — drawn
30
- under the CTA on every OTP section. ONE sentence, TWO independently
31
- actionable links, so it uses the archetype's composed-line slot rather
32
- than `linkLabel` (a full-width 56px button cannot sit inside a line of
33
- body text, and one button could not tell the two actions apart —
34
- DEVIATIONS #PI-10). */
35
- linkContent: strings.otp.resendPrompt ? (_jsxs("p", { className: "prizma-screen-layout__inline-prompt", children: [strings.otp.resendPrompt, ' ', strings.otp.resendLabel ? (_jsx("button", { type: "button", className: "prizma-screen-layout__inline-link", onClick: () => onAction?.({ type: 'secondary', screenId }), children: strings.otp.resendLabel })) : null, strings.otp.changeNumberLabel ? (_jsxs(_Fragment, { children: [' ', strings.otp.resendSeparator, ' ', _jsx("button", { type: "button", className: "prizma-screen-layout__inline-link", onClick: () => onAction?.({ type: 'tertiary', screenId }), children: strings.otp.changeNumberLabel })] })) : null] })) : undefined, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, screenId: screenId, overrides: overrides, onAction: onAction, ...shell, children: _jsx(PrizmaOtp, { length: length,
36
- /* Uncontrolled, for the same reason as the phone field: `readOnly`
37
- would paint Core's DISABLED surface on the resting states, which is
38
- grey Figma does not draw there (DEVIATIONS #PI-6). */
39
- defaultValue: value ?? '',
40
- /* `processing` is the one section Figma greys — the code stays legible
41
- while the check runs. */
42
- disabled: state === 'processing', error: state === 'error', errorText: state === 'error' ? strings.otp.errorText : undefined }) })));
24
+ export const PhoneInputOtpScreen = forwardRef(({ strings, state = 'typing', value, length = 6, resendIn, screenId = `phone-input-otp-${state}`, overrides, onAction, ...shell }, ref) => {
25
+ const counting = (resendIn ?? 0) > 0 && Boolean(strings.otp.resendCountdownLabel);
26
+ return (_jsx(PrizmaFormScreen, { ref: ref, title: strings.otp.title, subtitle: strings.otp.subtitle, ctaLabel: strings.otp.ctaLabel,
27
+ /* Figma greys the CTA until the code is complete. */
28
+ ctaDisabled: state === 'typing',
29
+ /* The spinner rides the CTA, not the field (see the note above). */
30
+ ctaLoading: state === 'processing', ctaLoadingLabel: strings.otp.ctaLoadingLabel,
31
+ /* "Didn't receive the code? Resend code or Change phone number" — drawn
32
+ under the CTA on every OTP section. ONE sentence, TWO independently
33
+ actionable links, so it uses the archetype's composed-line slot rather
34
+ than `linkLabel` (a full-width 56px button cannot sit inside a line of
35
+ body text, and one button could not tell the two actions apart —
36
+ DEVIATIONS #PI-10). */
37
+ /* While the resend timer runs, the line is ONE disabled link (Figma
38
+ `OTP.typing`: Tertiary / Link · Disabled), not the composed sentence. */
39
+ linkLabel: counting
40
+ ? strings.otp.resendCountdownLabel.replace('{{time}}', String(resendIn))
41
+ : undefined, linkDisabled: counting, linkContent: !counting && strings.otp.resendPrompt ? (_jsxs("p", { className: "prizma-screen-layout__inline-prompt", children: [strings.otp.resendPrompt, ' ', strings.otp.resendLabel ? (_jsx("button", { type: "button", className: "prizma-screen-layout__inline-link", onClick: () => onAction?.({ type: 'secondary', screenId }), children: strings.otp.resendLabel })) : null, strings.otp.changeNumberLabel ? (_jsxs(_Fragment, { children: [' ', strings.otp.resendSeparator, ' ', _jsx("button", { type: "button", className: "prizma-screen-layout__inline-link", onClick: () => onAction?.({ type: 'tertiary', screenId }), children: strings.otp.changeNumberLabel })] })) : null] })) : undefined, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, screenId: screenId, overrides: overrides, onAction: onAction, ...shell, children: _jsx(PrizmaOtp, { length: length,
42
+ /* Uncontrolled, for the same reason as the phone field: `readOnly`
43
+ would paint Core's DISABLED surface on the resting states, which is
44
+ grey Figma does not draw there (DEVIATIONS #PI-6). */
45
+ defaultValue: value ?? '',
46
+ /* `processing` is the one section Figma greys — the code stays legible
47
+ while the check runs. */
48
+ disabled: state === 'processing', error: state === 'error', errorText: state === 'error' ? strings.otp.errorText : undefined }) }));
49
+ });
43
50
  PhoneInputOtpScreen.displayName = 'PhoneInputOtpScreen';
44
51
  export default PhoneInputOtpScreen;
@@ -28,8 +28,9 @@ export type PhoneInputOtpState = 'typing' | 'filled' | 'processing' | 'error';
28
28
  */
29
29
  export type PhoneInputErrorVariant = 'tryAgain' | 'unknown' | 'noNetwork';
30
30
  /** One selectable country in the dial-code dropdown. Runtime data, not copy —
31
- * the host supplies its own list; `flag` is the emoji Figma draws (Core has no
32
- * country-flag primitive — DEVIATIONS #PI-3). */
31
+ * the host supplies its own list. The screen draws the flag from Core's
32
+ * raster bank by `value` (`PrizmaCountryFlag`, DEVIATIONS #PI-3); `flag` is
33
+ * only the emoji fallback for a country outside the bank. */
33
34
  export interface PhoneInputCountry {
34
35
  /** Stable value, e.g. an ISO 3166-1 alpha-2 code. */
35
36
  value: string;
@@ -37,7 +38,7 @@ export interface PhoneInputCountry {
37
38
  dialCode: string;
38
39
  /** Country name for the open menu row. */
39
40
  label: string;
40
- /** Emoji flag, e.g. `🇷🇸`. */
41
+ /** Emoji fallback, e.g. `🇷🇸`, drawn only when the bank has no raster. */
41
42
  flag?: string;
42
43
  }
43
44
  export interface PhoneInputStrings {
@@ -102,6 +103,13 @@ export interface PhoneInputStrings {
102
103
  resendSeparator?: string;
103
104
  /** Second link — returns to phone entry. Emits `tertiary`. */
104
105
  changeNumberLabel?: string;
106
+ /**
107
+ * While the resend timer runs, the resend line is replaced by ONE disabled
108
+ * link: "Resend code in {{time}}s" (Figma `phoneNumber.OTP.typing`). The
109
+ * `{{time}}` placeholder is the SDK's own (`otp.resendCountdown`) and is
110
+ * filled with the screen's `resendIn` seconds.
111
+ */
112
+ resendCountdownLabel?: string;
105
113
  /** Primary CTA. */
106
114
  ctaLabel: string;
107
115
  /** Accessible / visible busy copy while the code is checked (`processing`). */
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { forwardRef } from 'react';
4
4
  import { PrizmaTextField } from '@incodetech/prizma-core-web/text-field';
5
5
  import { PrizmaDropdown } from '@incodetech/prizma-core-web/dropdown';
6
+ import { PrizmaCountryFlag } from '@incodetech/prizma-core-web/combined';
6
7
  import { PrizmaFormScreen } from './FormScreen';
7
8
  import './ScreenLayout.css';
8
9
  import './PhoneInputLayout.css';
@@ -43,10 +44,13 @@ export const PhoneInputVerifyScreen = forwardRef(({ strings, state = 'default',
43
44
  const options = countries.map((c) => ({
44
45
  value: c.value,
45
46
  // The open menu row reads flag + dial code + country name
46
- // ("🇷🇸 +381 Serbia"); the closed field keeps only flag + dial code
47
- // ("🇷🇸 +381") through Core's `fieldLabel`, exactly as Figma draws it.
48
- label: [c.flag, c.dialCode, c.label].filter(Boolean).join(' '),
49
- fieldLabel: [c.flag, c.dialCode].filter(Boolean).join(' '),
47
+ // ("+381 Serbia" after the flag); the closed field keeps only flag +
48
+ // dial code ("+381") through Core's `fieldLabel`, as Figma draws it.
49
+ // The flag is not text: it rides the leading slot below (DEVIATIONS
50
+ // #PI-3), so it is Core's raster on every OS instead of an emoji that
51
+ // Windows draws as the letters "RS".
52
+ label: [c.dialCode, c.label].join(' '),
53
+ fieldLabel: c.dialCode,
50
54
  }));
51
55
  // Figma treats the states as three distinct field treatments, not one:
52
56
  // `loading` greys BOTH fields (the request is in flight and the form is
@@ -64,7 +68,19 @@ export const PhoneInputVerifyScreen = forwardRef(({ strings, state = 'default',
64
68
  /* Figma's `loading` section swaps the CTA label for Core's spinner. */
65
69
  ctaLoading: busy, ctaDisabled: emptyError, ctaLoadingLabel: strings.verify.ctaLoadingLabel, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, screenId: screenId, overrides: overrides, onAction: onAction, ...shell, children: _jsxs("div", { className: "phone-input-combined", children: [_jsx(PrizmaDropdown, { className: "phone-input-combined__country", "aria-label": strings.verify.countryLabel, options: options, value: selected, searchable: true, searchPlaceholder: strings.verify.countrySearchPlaceholder,
66
70
  /* The `countryCode` section IS the open-menu spec state. */
67
- forceOpen: state === 'countryCode', disabled: busy }), _jsx(PrizmaTextField, { className: [
71
+ forceOpen: state === 'countryCode',
72
+ /* Core's canonical flag raster in the closed field and every menu
73
+ row, the WatchlistBusinessFormScreen precedent. A country
74
+ outside the bank falls back to its `flag` emoji. */
75
+ renderLeading: (option) => {
76
+ const match = countries.find((c) => c.value === option.value);
77
+ return match ? (_jsx(PrizmaCountryFlag, { country: {
78
+ code: match.value,
79
+ name: match.label,
80
+ dialCode: match.dialCode,
81
+ flag: match.flag ?? '',
82
+ } })) : null;
83
+ }, disabled: busy }), _jsx(PrizmaTextField, { className: [
68
84
  'phone-input-combined__number',
69
85
  /* Core's own static-focus modifier — the same mechanism the
70
86
  Dropdown exposes as `forceState`, which `PrizmaTextField` has
@@ -35,16 +35,19 @@ export interface QualifiedSignatureDocumentScreenProps extends Omit<PrizmaListSc
35
35
  *
36
36
  * Archetype G is the fit: its `__list` middle is "content filling the space
37
37
  * between the title block and the CTA band", which is exactly what a document
38
- * page is, and both sections carry no CTA at all so the band never renders. The
38
+ * page is, and the band carries only the "Back" secondary (delta 1). The
39
39
  * body is allowed to scroll inside that middle — one module rule RELEASING an
40
40
  * overflow constraint, declaring no spacing or size, the same idiom as CURP's
41
41
  * taller-than-844 form (DEVIATIONS #CURP-1).
42
42
  *
43
43
  * THREE deltas from the frames:
44
- * 1. Both sections draw a back chevron at the nav's left. It is BUILT (owner
45
- * call 2026-09-29): these are viewers the user opened from the accept
46
- * screen, so they need a way back. It is Core's chevron through the opt-in
47
- * `PrizmaScreenNav.backLabel`, emitting `back`; the logo stays centred.
44
+ * 1. Both sections draw a back chevron at the nav's left. These are viewers
45
+ * the user opened from the accept screen, so they need a way back (owner
46
+ * call 2026-09-29). It ships as a full-width SECONDARY "Back" button in the
47
+ * CTA band instead, emitting `secondary` (owner call 2026-10-08): the person
48
+ * reads and returns to sign, and a real button at the thumb is easier to
49
+ * find than a chevron. "Back" over "Done" because nothing is finished here;
50
+ * the signature still waits on the accept screen. The nav stays logo-only.
48
51
  * `terms` replacing the logo row with a chevron + heading bar is still not
49
52
  * followed — the title stays the archetype's H2 below the nav.
50
53
  * 2. `viewdoc` draws NO heading. GUIDELINES §4 is explicit that every screen has
@@ -19,16 +19,19 @@ import './QualifiedSignatureLayout.css';
19
19
  *
20
20
  * Archetype G is the fit: its `__list` middle is "content filling the space
21
21
  * between the title block and the CTA band", which is exactly what a document
22
- * page is, and both sections carry no CTA at all so the band never renders. The
22
+ * page is, and the band carries only the "Back" secondary (delta 1). The
23
23
  * body is allowed to scroll inside that middle — one module rule RELEASING an
24
24
  * overflow constraint, declaring no spacing or size, the same idiom as CURP's
25
25
  * taller-than-844 form (DEVIATIONS #CURP-1).
26
26
  *
27
27
  * THREE deltas from the frames:
28
- * 1. Both sections draw a back chevron at the nav's left. It is BUILT (owner
29
- * call 2026-09-29): these are viewers the user opened from the accept
30
- * screen, so they need a way back. It is Core's chevron through the opt-in
31
- * `PrizmaScreenNav.backLabel`, emitting `back`; the logo stays centred.
28
+ * 1. Both sections draw a back chevron at the nav's left. These are viewers
29
+ * the user opened from the accept screen, so they need a way back (owner
30
+ * call 2026-09-29). It ships as a full-width SECONDARY "Back" button in the
31
+ * CTA band instead, emitting `secondary` (owner call 2026-10-08): the person
32
+ * reads and returns to sign, and a real button at the thumb is easier to
33
+ * find than a chevron. "Back" over "Done" because nothing is finished here;
34
+ * the signature still waits on the accept screen. The nav stays logo-only.
32
35
  * `terms` replacing the logo row with a chevron + heading bar is still not
33
36
  * followed — the title stays the archetype's H2 below the nav.
34
37
  * 2. `viewdoc` draws NO heading. GUIDELINES §4 is explicit that every screen has
@@ -48,7 +51,7 @@ import './QualifiedSignatureLayout.css';
48
51
  */
49
52
  export const QualifiedSignatureDocumentScreen = forwardRef(({ strings, variant = 'document', body, screenId = `qualified-signature-document-${variant}`, overrides, onAction, ...shell }, ref) => {
50
53
  const copy = variant === 'terms' ? strings.document.terms : strings.document.document;
51
- return (_jsx(PrizmaListScreen, { ref: ref, title: copy.title, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, backLabel: strings.document.backLabel, screenId: screenId, overrides: overrides, onAction: onAction, ...shell, children: _jsx("div", { className: [
54
+ return (_jsx(PrizmaListScreen, { ref: ref, title: copy.title, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, secondaryCtaLabel: strings.document.backLabel, screenId: screenId, overrides: overrides, onAction: onAction, ...shell, children: _jsx("div", { className: [
52
55
  'qualified-signature-document',
53
56
  'qualified-signature-document--framed',
54
57
  `qualified-signature-document--${variant}`,
@@ -3,11 +3,16 @@ import { type PrizmaResultScreenProps } from './ResultScreen';
3
3
  import type { PrizmaScreenActionHandler, PrizmaScreenOverrides } from './types';
4
4
  import type { QualifiedSignatureResultVariant, QualifiedSignatureStrings } from './QualifiedSignatureStrings';
5
5
  import './ScreenLayout.css';
6
- export interface QualifiedSignatureResultScreenProps extends Omit<PrizmaResultScreenProps, 'status' | 'title' | 'subtitle' | 'glyph' | 'bandContent' | 'disclaimer' | 'ctaLabel' | 'secondaryCtaLabel' | 'verifiedLabel' | 'navLabel'> {
6
+ export interface QualifiedSignatureResultScreenProps extends Omit<PrizmaResultScreenProps, 'status' | 'title' | 'subtitle' | 'glyph' | 'bandContent' | 'disclaimer' | 'ctaLabel' | 'secondaryCtaLabel' | 'progress' | 'verifiedLabel' | 'navLabel'> {
7
7
  /** Module copy — this screen reads the `result` slice its `variant` names. */
8
8
  strings: QualifiedSignatureStrings;
9
9
  /** Which verdict section to render. Default `signed`. */
10
10
  variant?: QualifiedSignatureResultVariant;
11
+ /** `signed` · `signedPartner` only — draw the "document downloaded" Snackbar,
12
+ * the state after the person pressed the download control. HOST state, like
13
+ * Error screens' `copied`: the host sets it on the `secondary` action and
14
+ * clears it when its own timer runs out. Default `false`. */
15
+ downloaded?: boolean;
11
16
  screenId?: string;
12
17
  overrides?: PrizmaScreenOverrides;
13
18
  onAction?: PrizmaScreenActionHandler;
@@ -20,8 +25,10 @@ export interface QualifiedSignatureResultScreenProps extends Omit<PrizmaResultSc
20
25
  * nav → centred status mark + title block → CTA band → anchored tag — and differ
21
26
  * in status, copy and which actions exist:
22
27
  * - `signed` — `QSign.success` (10196:67485): the positive mark over a
23
- * `Button group` 124 tall = the "Continue" primary plus the "Download PDF"
24
- * underlined link.
28
+ * STACKED pair — "Continue" primary above a "Download PDF" OUTLINED
29
+ * secondary. Figma draws the download as an underlined link; it ships as the
30
+ * partner's secondary Button instead (owner call 2026-10-08, "make the
31
+ * generic one look like Namirial"), so both success endings read alike.
25
32
  * - `signedPlain` — `QSign.success` (10196:67494): the SAME mark and line
26
33
  * with no actions at all, so the band never renders and the screen reads as
27
34
  * the pure §6-C verdict.
@@ -50,16 +57,12 @@ export interface QualifiedSignatureResultScreenProps extends Omit<PrizmaResultSc
50
57
  * The owner rule settles the split in favour of keeping it (#PI-8, #F-30) so
51
58
  * nothing shifts between states.
52
59
  *
53
- * ONE reported delta: on `signed`, Figma puts "Download PDF" BELOW the primary,
54
- * at Core's `PrizmaButtonGroup` Spacing/12. Archetype F has no tertiary-link
55
- * slot — its only band opening above the buttons is `bandContent` — so Core's
56
- * own `tertiary-link` Button is composed there and lands ABOVE the primary at
57
- * the band's Spacing/32 (16 + 16). Ink, semantics and the distinct `secondary`
58
- * action are all correct (#PI-10); only the position moves. #GEO-1 fixes the
59
- * tertiary link's position PER ARCHETYPE and F has never declared one — the
60
- * archetype request is logged instead of a local override. `signedPartner` is
61
- * unaffected: its download control is a real outlined secondary Button, which
62
- * the archetype already places correctly.
60
+ * The download confirmation. Pressing "Download PDF" / "Download document"
61
+ * emits `secondary`; the host then sets `downloaded` and the screen raises a
62
+ * success Snackbar ("Document downloaded") in the archetype's `progress` slot,
63
+ * between the nav and the content, the same slot and component Error screens
64
+ * use for "link copied" (owner call 2026-10-08). Dismissal is the host's: the
65
+ * Snackbar is presentational (snackbar.md §9 item 6).
63
66
  */
64
67
  export declare const QualifiedSignatureResultScreen: React.ForwardRefExoticComponent<QualifiedSignatureResultScreenProps & React.RefAttributes<HTMLDivElement>>;
65
68
  export default QualifiedSignatureResultScreen;
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { forwardRef } from 'react';
4
- import { PrizmaButton } from '@incodetech/prizma-core-web/button';
4
+ import { PrizmaSnackbar } from '@incodetech/prizma-core-web/snackbar';
5
5
  import { PrizmaResultScreen } from './ResultScreen';
6
6
  import './ScreenLayout.css';
7
7
  /**
@@ -12,8 +12,10 @@ import './ScreenLayout.css';
12
12
  * nav → centred status mark + title block → CTA band → anchored tag — and differ
13
13
  * in status, copy and which actions exist:
14
14
  * - `signed` — `QSign.success` (10196:67485): the positive mark over a
15
- * `Button group` 124 tall = the "Continue" primary plus the "Download PDF"
16
- * underlined link.
15
+ * STACKED pair — "Continue" primary above a "Download PDF" OUTLINED
16
+ * secondary. Figma draws the download as an underlined link; it ships as the
17
+ * partner's secondary Button instead (owner call 2026-10-08, "make the
18
+ * generic one look like Namirial"), so both success endings read alike.
17
19
  * - `signedPlain` — `QSign.success` (10196:67494): the SAME mark and line
18
20
  * with no actions at all, so the band never renders and the screen reads as
19
21
  * the pure §6-C verdict.
@@ -42,18 +44,14 @@ import './ScreenLayout.css';
42
44
  * The owner rule settles the split in favour of keeping it (#PI-8, #F-30) so
43
45
  * nothing shifts between states.
44
46
  *
45
- * ONE reported delta: on `signed`, Figma puts "Download PDF" BELOW the primary,
46
- * at Core's `PrizmaButtonGroup` Spacing/12. Archetype F has no tertiary-link
47
- * slot — its only band opening above the buttons is `bandContent` — so Core's
48
- * own `tertiary-link` Button is composed there and lands ABOVE the primary at
49
- * the band's Spacing/32 (16 + 16). Ink, semantics and the distinct `secondary`
50
- * action are all correct (#PI-10); only the position moves. #GEO-1 fixes the
51
- * tertiary link's position PER ARCHETYPE and F has never declared one — the
52
- * archetype request is logged instead of a local override. `signedPartner` is
53
- * unaffected: its download control is a real outlined secondary Button, which
54
- * the archetype already places correctly.
47
+ * The download confirmation. Pressing "Download PDF" / "Download document"
48
+ * emits `secondary`; the host then sets `downloaded` and the screen raises a
49
+ * success Snackbar ("Document downloaded") in the archetype's `progress` slot,
50
+ * between the nav and the content, the same slot and component Error screens
51
+ * use for "link copied" (owner call 2026-10-08). Dismissal is the host's: the
52
+ * Snackbar is presentational (snackbar.md §9 item 6).
55
53
  */
56
- export const QualifiedSignatureResultScreen = forwardRef(({ strings, variant = 'signed', screenId = `qualified-signature-result-${variant}`, overrides, onAction, ...shell }, ref) => {
54
+ export const QualifiedSignatureResultScreen = forwardRef(({ strings, variant = 'signed', downloaded = false, screenId = `qualified-signature-result-${variant}`, overrides, onAction, ...shell }, ref) => {
57
55
  // One slice per frame — nothing is shared between variants, so the frames
58
56
  // can never collapse into each other visually.
59
57
  const signedCopy = variant === 'signed' ? strings.result.signed : undefined;
@@ -63,14 +61,15 @@ export const QualifiedSignatureResultScreen = forwardRef(({ strings, variant = '
63
61
  partnerCopy ??
64
62
  retryCopy ??
65
63
  (variant === 'error' ? strings.result.error : strings.result.signedPlain);
66
- return (_jsx(PrizmaResultScreen, { ref: ref, status: variant === 'error' || variant === 'errorRetry' ? 'warning' : 'success', title: copy.title, subtitle: copy.subtitle,
64
+ return (_jsx(PrizmaResultScreen, { ref: ref, status: variant === 'error' || variant === 'errorRetry' ? 'warning' : 'success', title: copy.title, subtitle: copy.subtitle, progress: downloaded && (signedCopy?.downloadLabel || partnerCopy) ? (_jsx(PrizmaSnackbar, { state: "success", contentResizing: "fill", children: strings.result.downloadedLabel })) : undefined,
67
65
  /* "Continue" · "Done" · "Try again" — the archetype's own primary. */
68
66
  ctaLabel: signedCopy?.ctaLabel ?? partnerCopy?.ctaLabel ?? retryCopy?.ctaLabel,
69
- /* The partner's outlined "Download document", below the primary. */
70
- secondaryCtaLabel: partnerCopy?.secondaryCtaLabel,
67
+ /* The outlined download, below the primary: "Download PDF" on the
68
+ generic ending, "Download document" on the partner's. */
69
+ secondaryCtaLabel: signedCopy?.downloadLabel ?? partnerCopy?.secondaryCtaLabel,
71
70
  /* The partner's "Signed by …" row — the band's centred text slot, which
72
71
  puts it above the button pair exactly as Figma draws it. */
73
- disclaimer: partnerCopy?.signedByLabel, bandContent: signedCopy?.downloadLabel ? (_jsx(PrizmaButton, { variant: "tertiary-link", size: "L", onClick: () => onAction?.({ type: 'secondary', screenId }), children: signedCopy.downloadLabel })) : undefined, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, screenId: screenId, overrides: overrides, onAction: onAction, ...shell }));
72
+ disclaimer: partnerCopy?.signedByLabel, verifiedLabel: strings.verifiedLabel, navLabel: strings.navLabel, screenId: screenId, overrides: overrides, onAction: onAction, ...shell }));
74
73
  });
75
74
  QualifiedSignatureResultScreen.displayName = 'QualifiedSignatureResultScreen';
76
75
  export default QualifiedSignatureResultScreen;
@@ -33,7 +33,8 @@ export type QualifiedSignatureDocumentVariant = 'document' | 'terms';
33
33
  * The FIVE verdict frames. Both purple parents draw success twice and error
34
34
  * twice, and each pair differs in what the signer can do next:
35
35
  * - `signed` — `QSign.success` (10196:67485): the positive mark with
36
- * "Continue" and a "Download PDF" underlined link
36
+ * "Continue" primary above a "Download PDF" outlined secondary (was an
37
+ * underlined link in Figma; owner call 2026-10-08, matches the partner)
37
38
  * - `signedPlain` — `QSign.success` (10196:67494): the same mark and line
38
39
  * with NO actions at all
39
40
  * - `signedPartner` — the `x Namirial` parent's own success frame (6086:40135),
@@ -154,9 +155,11 @@ export interface QualifiedSignatureStrings {
154
155
  terms: {
155
156
  title: string;
156
157
  };
157
- /** Accessible name of the nav's back chevron on both document screens
158
+ /** The way back on both document screens
158
159
  * ("Back") — they are opened from the accept screen, so they carry a way
159
- * back to it. Emits `back`. Omit to render the logo-only nav. */
160
+ * back to it. It is the label of a full-width SECONDARY button in the CTA
161
+ * band (owner call 2026-10-08, was the nav chevron) and emits
162
+ * `secondary`. Omit it and no way back renders. */
160
163
  backLabel?: string;
161
164
  };
162
165
  /** The two in-flight screens. */
@@ -191,7 +194,8 @@ export interface QualifiedSignatureStrings {
191
194
  subtitle?: string;
192
195
  /** Primary CTA ("Continue"). */
193
196
  ctaLabel: string;
194
- /** Underlined tertiary link ("Download PDF"). Emits `secondary`. */
197
+ /** OUTLINED secondary below the primary ("Download PDF"), the same
198
+ * control as the partner's "Download document". Emits `secondary`. */
195
199
  downloadLabel?: string;
196
200
  };
197
201
  /** QSign.success drawn with no actions. */
@@ -216,6 +220,9 @@ export interface QualifiedSignatureStrings {
216
220
  * `secondary`. A real Button, not `signed`'s underlined link. */
217
221
  secondaryCtaLabel: string;
218
222
  };
223
+ /** The success Snackbar raised after either download control was pressed
224
+ * ("Document downloaded"). Shared by `signed` and `signedPartner`. */
225
+ downloadedLabel: string;
219
226
  /** QSign.AcceptSign.error ("Something went wrong"). Figma draws no button. */
220
227
  error: {
221
228
  title: string;
@@ -64,6 +64,14 @@ export interface PrizmaResultScreenProps extends Omit<PrizmaScreenShellProps, 'd
64
64
  * job (Face Match's verdict Snackbar). DEVIATIONS #VS-17.
65
65
  */
66
66
  linkLabel?: string;
67
+ /**
68
+ * Where the tertiary link sits in the stack. `below` (default) is archetype
69
+ * A's position; `above` puts it over the primary CTA, which is what the
70
+ * permission frames draw ("Quit process" over "Allow permission",
71
+ * DEVIATIONS #GEN-3, #VS-17). The slot those rows escalated: the archetype
72
+ * still owns both positions, a screen only picks one of them.
73
+ */
74
+ linkPosition?: 'below' | 'above';
67
75
  /** "verified by" attribution copy for the footer tag. */
68
76
  verifiedLabel: string;
69
77
  navLabel?: string;
@@ -26,12 +26,23 @@ import './ScreenLayout.css';
26
26
  * `ProcessingScreen` stays the pure spinner/no-CTA case; reach for this one as
27
27
  * soon as a result screen needs a button.
28
28
  */
29
- export const PrizmaResultScreen = forwardRef(({ progress, status = 'none', title, subtitle, glyph, children, bandContent, disclaimer, ctaOrientation = 'stacked', ctaLabel, secondaryCtaLabel, linkLabel, verifiedLabel, navLabel, screenId, showHeader = true, showFooter = true, overrides, onAction, ...shell }, ref) => (_jsxs(PrizmaScreenShell, { ref: ref, ...shell, children: [_jsx(PrizmaScreenNav, { reserved: !showHeader, logo: overrides?.logo, navLabel: navLabel }), progress ? (_jsx("div", { className: "prizma-screen-layout__progress", children: progress })) : null, _jsxs("div", { className: "prizma-screen-layout__content prizma-screen-layout__content--centered", children: [glyph, _jsx(PrizmaTitle, { variant: overrides?.align === 'left' ? 'left' : 'centered', title: title, showSubtitle: Boolean(subtitle), subtitle: subtitle, icon: status === 'loading'
29
+ export const PrizmaResultScreen = forwardRef(({ progress, status = 'none', title, subtitle, glyph, children, bandContent, disclaimer, ctaOrientation = 'stacked', ctaLabel, secondaryCtaLabel, linkLabel, linkPosition = 'below', verifiedLabel, navLabel, screenId, showHeader = true, showFooter = true, overrides, onAction, ...shell }, ref) => (_jsxs(PrizmaScreenShell, { ref: ref, ...shell, children: [_jsx(PrizmaScreenNav, { reserved: !showHeader, logo: overrides?.logo, navLabel: navLabel }), progress ? (_jsx("div", { className: "prizma-screen-layout__progress", children: progress })) : null, _jsxs("div", { className: "prizma-screen-layout__content prizma-screen-layout__content--centered", children: [glyph, _jsx(PrizmaTitle, { variant: overrides?.align === 'left' ? 'left' : 'centered', title: title, showSubtitle: Boolean(subtitle), subtitle: subtitle, icon: status === 'loading'
30
30
  ? { type: 'spinner' }
31
31
  : status === 'none'
32
32
  ? { type: 'none' }
33
- : { type: 'status', status } }), children] }), ctaLabel || secondaryCtaLabel || linkLabel || disclaimer || bandContent ? (_jsxs("div", { className: "prizma-screen-layout__band prizma-screen-layout__band--result", children: [bandContent ? (_jsx("div", { className: "prizma-screen-layout__band__content", children: bandContent })) : null, disclaimer ? _jsx(PrizmaDisclaimer, { text: disclaimer }) : null, _jsxs("div", { className: ctaOrientation === 'row'
33
+ : { type: 'status', status } }), children] }), ctaLabel || secondaryCtaLabel || linkLabel || disclaimer || bandContent ? (_jsxs("div", { className: [
34
+ 'prizma-screen-layout__band',
35
+ 'prizma-screen-layout__band--result',
36
+ // No footer tag to reserve `--pcs-footer-space` for — same gap the
37
+ // Review archetype's `--bare` fallback exists for (ScreenLayout.css).
38
+ // Every existing F consumer always shows the tag, so this has never
39
+ // fired before; without it the CTA sits ~40px further from the
40
+ // bottom than Figma's flat Spacing/24 draws.
41
+ !showFooter ? 'prizma-screen-layout__band--bare' : '',
42
+ ]
43
+ .filter(Boolean)
44
+ .join(' '), children: [bandContent ? (_jsx("div", { className: "prizma-screen-layout__band__content", children: bandContent })) : null, disclaimer ? _jsx(PrizmaDisclaimer, { text: disclaimer }) : null, _jsxs("div", { className: ctaOrientation === 'row'
34
45
  ? 'prizma-screen-layout__band__ctas prizma-screen-layout__band__ctas--row'
35
- : 'prizma-screen-layout__band__ctas', children: [ctaLabel ? (_jsx(PrizmaButton, { variant: "primary", size: "L", onClick: () => onAction?.({ type: 'cta', screenId }), children: ctaLabel })) : null, secondaryCtaLabel ? (_jsx(PrizmaButton, { variant: "secondary", size: "L", onClick: () => onAction?.({ type: 'secondary', screenId }), children: secondaryCtaLabel })) : null, linkLabel ? (_jsx(PrizmaButton, { variant: "tertiary-link", size: "L", onClick: () => onAction?.({ type: 'tertiary', screenId }), children: linkLabel })) : null] })] })) : null, showFooter ? (_jsx("div", { className: "prizma-screen-layout__footer", children: _jsx(PrizmaTag, { label: verifiedLabel }) })) : null] })));
46
+ : 'prizma-screen-layout__band__ctas', children: [linkLabel && linkPosition === 'above' ? (_jsx(PrizmaButton, { variant: "tertiary-link", size: "L", onClick: () => onAction?.({ type: 'tertiary', screenId }), children: linkLabel })) : null, ctaLabel ? (_jsx(PrizmaButton, { variant: "primary", size: "L", onClick: () => onAction?.({ type: 'cta', screenId }), children: ctaLabel })) : null, secondaryCtaLabel ? (_jsx(PrizmaButton, { variant: "secondary", size: "L", onClick: () => onAction?.({ type: 'secondary', screenId }), children: secondaryCtaLabel })) : null, linkLabel && linkPosition === 'below' ? (_jsx(PrizmaButton, { variant: "tertiary-link", size: "L", onClick: () => onAction?.({ type: 'tertiary', screenId }), children: linkLabel })) : null] })] })) : null, showFooter ? (_jsx("div", { className: "prizma-screen-layout__footer", children: _jsx(PrizmaTag, { label: verifiedLabel }) })) : null] })));
36
47
  PrizmaResultScreen.displayName = 'PrizmaResultScreen';
37
48
  export default PrizmaResultScreen;