@visns-studio/visns-components 6.6.3 → 6.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +1658 -18
  2. package/package.json +10 -4
  3. package/src/components/DataGrid.jsx +619 -158
  4. package/src/components/Fetch.jsx +80 -3
  5. package/src/components/Form.jsx +9 -0
  6. package/src/components/Navigation.jsx +109 -50
  7. package/src/components/Notification.jsx +279 -9
  8. package/src/components/TableFilter.jsx +14 -1
  9. package/src/components/auth/AuthBrandPanel.jsx +43 -0
  10. package/src/components/auth/AuthLoading.jsx +78 -0
  11. package/src/components/auth/AuthShell.jsx +59 -0
  12. package/src/components/auth/ClientAuth.jsx +161 -0
  13. package/src/components/auth/ClientAuthFrame.jsx +59 -0
  14. package/src/components/auth/ClientLogin.jsx +266 -55
  15. package/src/components/auth/ClientOTPVerify.jsx +587 -115
  16. package/src/components/auth/ImpersonateGate.jsx +254 -0
  17. package/src/components/auth/Login.jsx +134 -41
  18. package/src/components/auth/LogoutScreen.jsx +112 -0
  19. package/src/components/auth/Reset.jsx +134 -77
  20. package/src/components/auth/TwoFactorAuth.jsx +475 -297
  21. package/src/components/auth/Verify.jsx +237 -126
  22. package/src/components/auth/authEndpoints.js +105 -0
  23. package/src/components/auth/authFont.js +23 -0
  24. package/src/components/auth/authHelpers.js +465 -0
  25. package/src/components/auth/clientAuthProtocols.js +240 -0
  26. package/src/components/auth/useOptionalRouter.js +49 -0
  27. package/src/components/callQueue/CallQueuePop.jsx +1502 -0
  28. package/src/components/callQueue/CallQueueSettings.jsx +508 -0
  29. package/src/components/callQueue/callQueueHelpers.js +280 -0
  30. package/src/components/callQueue/callQueueSettingsHelpers.js +75 -0
  31. package/src/components/columns/AutoGrowCell.jsx +141 -0
  32. package/src/components/columns/ColumnRenderers.jsx +53 -7
  33. package/src/components/generic/GenericAuth.jsx +163 -96
  34. package/src/components/generic/GenericDetail.jsx +215 -90
  35. package/src/components/generic/GenericIndex.jsx +20 -47
  36. package/src/components/generic/GenericMain.jsx +5 -0
  37. package/src/components/generic/GroupedReportRenderer.jsx +1 -5
  38. package/src/components/generic/StandardModal.jsx +17 -5
  39. package/src/components/generic/reportSemanticSteps/SemanticEntityStep.jsx +1 -6
  40. package/src/components/generic/reportSemanticSteps/SemanticFieldsStep.jsx +4 -11
  41. package/src/components/generic/reportSemanticSteps/SemanticFiltersStep.jsx +9 -27
  42. package/src/components/generic/reportSemanticSteps/SemanticGroupingStep.jsx +5 -13
  43. package/src/components/generic/reportSemanticSteps/SemanticParameterPrompt.jsx +5 -13
  44. package/src/components/generic/reportSemanticSteps/SemanticPreviewStep.jsx +14 -31
  45. package/src/components/generic/reportSemanticSteps/SemanticRelationsStep.jsx +3 -9
  46. package/src/components/generic/reportSemanticSteps/SemanticValueInput.jsx +2 -15
  47. package/src/components/navActive.js +60 -0
  48. package/src/components/notify/desktopNotifications.js +256 -0
  49. package/src/components/sketch/SketchField.jsx +12 -2
  50. package/src/components/sms/SmsComposeModal.jsx +495 -0
  51. package/src/components/sms/SmsInbox.jsx +596 -0
  52. package/src/components/sms/SmsInboxBadge.jsx +468 -0
  53. package/src/components/sms/SmsLineSettings.jsx +854 -0
  54. package/src/components/sms/SmsThreadPanel.jsx +1184 -0
  55. package/src/components/sms/smsEndpoints.js +56 -0
  56. package/src/components/sms/smsHelpers.js +1075 -0
  57. package/src/components/sms/smsLiveState.js +132 -0
  58. package/src/components/sms/useSmsLive.js +307 -0
  59. package/src/components/styles/CallQueuePop.module.scss +646 -0
  60. package/src/components/styles/CallQueueSettings.module.scss +460 -0
  61. package/src/components/styles/ClientAuth.module.scss +229 -0
  62. package/src/components/styles/DataGrid.module.scss +10 -0
  63. package/src/components/styles/GenericDetail.module.scss +153 -26
  64. package/src/components/styles/GenericIndex.module.scss +13 -0
  65. package/src/components/styles/GenericMain.module.scss +19 -2
  66. package/src/components/styles/ImpersonateGate.module.scss +85 -0
  67. package/src/components/styles/Login.module.scss +13 -122
  68. package/src/components/styles/LogoutScreen.module.scss +89 -0
  69. package/src/components/styles/Navigation.module.scss +509 -223
  70. package/src/components/styles/Notification.module.scss +103 -0
  71. package/src/components/styles/Reset.module.scss +52 -186
  72. package/src/components/styles/Sms.module.scss +2382 -0
  73. package/src/components/styles/TableFilter.module.scss +118 -98
  74. package/src/components/styles/TwoFactorAuth.module.scss +115 -176
  75. package/src/components/styles/Vault.module.scss +1983 -0
  76. package/src/components/styles/Verify.module.scss +57 -180
  77. package/src/components/styles/_authBrandPanel.scss +163 -0
  78. package/src/components/styles/_authShell.scss +369 -0
  79. package/src/components/styles/global-datagrid.css +29 -0
  80. package/src/components/utils/buildEnv.js +67 -0
  81. package/src/components/utils/displayValue.js +94 -0
  82. package/src/components/utils/useDensity.js +345 -9
  83. package/src/components/vault/OtpChip.jsx +208 -0
  84. package/src/components/vault/PasswordGenerator.jsx +145 -0
  85. package/src/components/vault/QrScanner.jsx +326 -0
  86. package/src/components/vault/VaultAccessLog.jsx +225 -0
  87. package/src/components/vault/VaultConfirmPanel.jsx +113 -0
  88. package/src/components/vault/VaultEntryForm.jsx +740 -0
  89. package/src/components/vault/VaultManager.jsx +1000 -0
  90. package/src/components/vault/VaultQuickSearch.jsx +681 -0
  91. package/src/components/vault/qrDecode.js +257 -0
  92. package/src/components/vault/useDebouncedValue.js +22 -0
  93. package/src/components/vault/useVaultReveal.js +160 -0
  94. package/src/components/vault/vaultClipboard.js +33 -0
  95. package/src/components/vault/vaultEndpoints.js +40 -0
  96. package/src/components/vault/vaultFit.js +116 -0
  97. package/src/components/vault/vaultHelpers.js +547 -0
  98. package/src/components/vault/vaultNavigation.jsx +58 -0
  99. package/src/components/vault/vaultOtp.js +105 -0
  100. package/src/index.js +233 -1
@@ -12,6 +12,120 @@
12
12
  * dropped, which is exactly how a rail like this loses its padding.
13
13
  */
14
14
 
15
+
16
+ /**
17
+ * The one-row tab strip.
18
+ *
19
+ * A rail's items laid out in a row, each an unadorned label, with a 3px accent
20
+ * edge under the selected one instead of the vertical rail's brand fill. It is
21
+ * the treatment narrow viewports get for any rail without expandable parents
22
+ * (`.flatFilter`, inside the phone media query at the bottom of this file),
23
+ * and the treatment a page opts into at every width with
24
+ * `<TableFilter variant="underline">` — a vertical rail spends a fifth of the
25
+ * page on navigation, which pays for itself at seven tabs and does not at two.
26
+ *
27
+ * The selectors inside are deliberately longer than the ones they override:
28
+ * `.subactive` and `.activetab button` carry `!important`, so only a more
29
+ * specific `!important` can take the brand fill back off.
30
+ */
31
+ @mixin tab-strip {
32
+ .tableFilter,
33
+ .tableFilter.mobileOpen {
34
+ display: flex;
35
+ flex-wrap: nowrap;
36
+ align-items: stretch;
37
+ gap: var(--spacing-xs, 0.25rem);
38
+ max-height: none;
39
+ overflow-x: auto;
40
+ overflow-y: hidden;
41
+ /* Momentum panning on iOS. */
42
+ -webkit-overflow-scrolling: touch;
43
+ /* The scrollbar is noise on a two-tab strip; the overflowing tab is
44
+ the affordance. */
45
+ scrollbar-width: none;
46
+ -ms-overflow-style: none;
47
+ /* The dropdown drew itself a card. A strip sits on the panel it is
48
+ already inside. */
49
+ padding: 0;
50
+ border: none;
51
+ border-radius: 0;
52
+ margin-bottom: 0;
53
+ /* No baseline rule under the row: every surface this rail sits on
54
+ (`.grid__subnav` in Profile, GenericIndex and GenericDetail) already
55
+ draws its own border a few pixels below, and a second hairline
56
+ alongside it read as a mistake. The 3px edge under the selected tab
57
+ is the whole marker. */
58
+ box-shadow: none;
59
+ }
60
+
61
+ .tableFilter::-webkit-scrollbar {
62
+ display: none;
63
+ }
64
+
65
+ .tableFilter > li {
66
+ flex: 0 0 auto;
67
+ margin: 0;
68
+ border-bottom: none;
69
+ }
70
+
71
+ /* Inactive tabs are muted labels, not tinted blocks. `white-space: nowrap`
72
+ is what keeps the row one row: without it a two-word tab wraps and the
73
+ strip grows a second line instead of overflowing. */
74
+ .tableFilter > li > .link,
75
+ .tableFilter > li > .parentLink,
76
+ .tableFilter > li > button {
77
+ display: block;
78
+ white-space: nowrap;
79
+ padding: 0.75rem var(--spacing-md, 1rem);
80
+ border: none;
81
+ border-bottom: 3px solid transparent;
82
+ border-radius: 0;
83
+ background: none !important;
84
+ color: var(--paragraph-color, #6b7280) !important;
85
+ font-size: var(--font-size-sm, 0.8125rem);
86
+ font-weight: var(--font-weight-medium, 500);
87
+ cursor: pointer;
88
+ }
89
+
90
+ /* The selected tab. `--primary-color` first because it is what the rail
91
+ already fills the active item with everywhere else; `--accent-color`
92
+ covers apps that only declare that one, and the literal keeps the edge
93
+ visible in an app that declares neither. */
94
+ .tableFilter > li > .subactive,
95
+ .tableFilter > li.activetab > button {
96
+ background: none !important;
97
+ color: var(--heading-color, #111) !important;
98
+ font-weight: var(--font-weight-semibold, 600);
99
+ border-bottom-color: var(
100
+ --primary-color,
101
+ var(--accent-color, #0076c0)
102
+ );
103
+ }
104
+
105
+ /* Touch devices keep :hover stuck on the last tap, which on the old rule
106
+ tinted whichever tab was tapped last and made it compete with the
107
+ selected one. */
108
+ .tableFilter > li > .link:hover,
109
+ .tableFilter > li > .parentLink:hover,
110
+ .tableFilter > li > button:hover {
111
+ background: none !important;
112
+ color: var(--heading-color, #111) !important;
113
+ }
114
+ }
115
+
116
+ /* The opt-in variant. Same strip, at every width; TableFilter sets this class
117
+ alongside `.flatFilter` when a page asks for `variant="underline"`. The
118
+ container's own bottom margin goes: the strip is separated from the panel
119
+ below it by that panel's own spacing, and 20px of air under a tab row reads
120
+ as a gap in the page. */
121
+ .tableFilterContainer.underlineFilter {
122
+ /* Both classes: `.tableFilterContainer` below sets a 20px bottom margin,
123
+ and a single-class selector here would lose to it on source order. */
124
+ margin-bottom: 0;
125
+
126
+ @include tab-strip;
127
+ }
128
+
15
129
  .tableFilterContainer {
16
130
  width: 100%;
17
131
  position: relative;
@@ -299,103 +413,9 @@
299
413
  border-left: none;
300
414
  }
301
415
 
302
- /* ---------------------------------------------------------------------
303
- Flat rails: a one-row tab strip instead of the hamburger.
304
-
305
- `.flatFilter` is set by TableFilter on any rail whose items have no
306
- expandable children, and exists only inside this media query — above
307
- 759px the class matches nothing and the rail is byte-for-byte the
308
- vertical list it has always been.
309
-
310
- The strip pans horizontally with a thumb and marks the selection with a
311
- 3px accent edge rather than the full brand fill: on a phone the fill was
312
- a solid band the width of the screen, which read as a header rather than
313
- as one tab out of several.
314
-
315
- These selectors are deliberately longer than the ones they override —
316
- `.subactive` and `.activetab button` carry `!important`, so only a more
317
- specific `!important` can take the fill back off. --------------------- */
318
-
319
- .flatFilter .tableFilter,
320
- .flatFilter .tableFilter.mobileOpen {
321
- display: flex;
322
- flex-wrap: nowrap;
323
- align-items: stretch;
324
- gap: var(--spacing-xs, 0.25rem);
325
- max-height: none;
326
- overflow-x: auto;
327
- overflow-y: hidden;
328
- /* Momentum panning on iOS. */
329
- -webkit-overflow-scrolling: touch;
330
- /* The scrollbar is noise on a two-tab strip; the overflowing tab is
331
- the affordance. */
332
- scrollbar-width: none;
333
- -ms-overflow-style: none;
334
- /* The dropdown drew itself a card. A strip sits on the panel it is
335
- already inside. */
336
- padding: 0;
337
- border: none;
338
- border-radius: 0;
339
- margin-bottom: 0;
340
- /* No baseline rule under the row: every surface this rail sits on
341
- (`.grid__subnav` in Profile, GenericIndex and GenericDetail) already
342
- draws its own border a few pixels below, and a second hairline
343
- alongside it read as a mistake. The 3px edge under the selected tab
344
- is the whole marker. */
345
- box-shadow: none;
346
- }
347
-
348
- .flatFilter .tableFilter::-webkit-scrollbar {
349
- display: none;
350
- }
351
-
352
- .flatFilter .tableFilter > li {
353
- flex: 0 0 auto;
354
- margin: 0;
355
- border-bottom: none;
356
- }
357
-
358
- /* Inactive tabs are muted labels, not tinted blocks. `white-space: nowrap`
359
- is what keeps the row one row: without it a two-word tab wraps and the
360
- strip grows a second line instead of overflowing. */
361
- .flatFilter .tableFilter > li > .link,
362
- .flatFilter .tableFilter > li > .parentLink,
363
- .flatFilter .tableFilter > li > button {
364
- display: block;
365
- white-space: nowrap;
366
- padding: 0.75rem var(--spacing-md, 1rem);
367
- border: none;
368
- border-bottom: 3px solid transparent;
369
- border-radius: 0;
370
- background: none !important;
371
- color: var(--paragraph-color, #6b7280) !important;
372
- font-size: var(--font-size-sm, 0.8125rem);
373
- font-weight: var(--font-weight-medium, 500);
374
- cursor: pointer;
375
- }
376
-
377
- /* The selected tab. `--primary-color` first because it is what the rail
378
- already fills the active item with everywhere else; `--accent-color`
379
- covers apps that only declare that one, and the literal keeps the edge
380
- visible in an app that declares neither. */
381
- .flatFilter .tableFilter > li > .subactive,
382
- .flatFilter .tableFilter > li.activetab > button {
383
- background: none !important;
384
- color: var(--heading-color, #111) !important;
385
- font-weight: var(--font-weight-semibold, 600);
386
- border-bottom-color: var(
387
- --primary-color,
388
- var(--accent-color, #0076c0)
389
- );
390
- }
391
-
392
- /* Touch devices keep :hover stuck on the last tap, which on the old rule
393
- tinted whichever tab was tapped last and made it compete with the
394
- selected one. */
395
- .flatFilter .tableFilter > li > .link:hover,
396
- .flatFilter .tableFilter > li > .parentLink:hover,
397
- .flatFilter .tableFilter > li > button:hover {
398
- background: none !important;
399
- color: var(--heading-color, #111) !important;
416
+ /* The one-row tab strip. Shared with the opt-in `.underlineFilter`
417
+ variant — see the mixin at the top of this file. */
418
+ .flatFilter {
419
+ @include tab-strip;
400
420
  }
401
421
  }
@@ -1,208 +1,147 @@
1
- .formItem {
2
- width: 100%;
3
- position: relative;
4
- box-sizing: border-box;
5
- margin-bottom: 1rem;
6
-
7
- .fi__label {
8
- position: relative;
9
- display: flex;
10
- align-items: center;
1
+ /**
2
+ * Two-factor verification.
3
+ *
4
+ * Replaces the pre-6.1 centred card (`.lcontainer` / `.lwrap` / `.login`, with
5
+ * its own greys and its own button) with the two-pane sign-in language. Signing
6
+ * in with 2FA on is one journey, and it used to change design halfway through
7
+ * it: the login screen, then a differently-styled card, then the app.
8
+ *
9
+ * Everything shared with the other auth screens comes from _authShell.scss.
10
+ * Only what is particular to entering a code lives here.
11
+ */
12
+
13
+ @use './authShell' as *;
14
+
15
+ @include auth-shell;
16
+
17
+ /* The code field is read digit by digit, so it is set wide, centred and
18
+ monospaced — nothing like the identifier fields on the login screen. */
19
+ .code {
20
+ input {
21
+ font-family: 'SFMono-Regular', 'Menlo', 'Consolas', monospace;
22
+ font-size: 1.3rem;
23
+ font-weight: 600;
24
+ letter-spacing: 0.55rem;
25
+ /* The tracking is applied to the right of each glyph, so without this
26
+ the run of digits sits visibly left of centre. */
27
+ text-indent: 0.55rem;
28
+ text-align: center;
29
+ padding-top: 1.35rem;
11
30
  }
12
31
 
13
- .fi__span {
14
- position: absolute;
15
- transition: all 200ms;
16
- opacity: 0.7;
17
- left: 0;
18
- transform-origin: top left;
19
- cursor: text;
20
- padding: 0 1rem;
21
- font-size: 0.9rem;
22
- color: #6b7280;
32
+ label {
33
+ /* Centred with the value it labels, rather than hanging off the left
34
+ edge of a field whose contents are centred. */
35
+ left: 50%;
36
+ transform: translateX(-50%);
23
37
  }
24
38
 
25
- input[name='verificationCode'] {
26
- letter-spacing: 0.25rem;
27
- font-size: 1.2rem;
28
- text-align: center;
39
+ input:focus + label,
40
+ input:not(:placeholder-shown) + label {
41
+ transform: translateX(-50%) translateY(-0.85rem);
29
42
  }
30
43
  }
31
44
 
32
- .fwItem,
33
- .fwBuilderItem {
34
- grid-column: 1 / -1;
35
- width: 100%;
36
- }
37
-
38
- .lastItem .btn {
39
- width: 100%;
40
- }
41
-
42
- .loginlogo {
43
- display: block;
44
- width: 100%;
45
- max-width: 150px;
46
- height: auto;
47
- margin: 0 auto;
48
- padding-bottom: 1.5rem;
45
+ /* Where the code went, in code mode: the masked number the SMS was sent to.
46
+ Sits directly under the subtitle so it reads as part of the instruction. */
47
+ .destination {
48
+ display: inline-flex;
49
+ align-items: center;
50
+ gap: 0.4rem;
51
+ margin: -1.4rem 0 1.6rem;
52
+ padding: 0.35rem 0.7rem;
53
+ border: 1px solid var(--line);
54
+ border-radius: 999px;
55
+ background: var(--warm);
56
+ font-size: 0.82rem;
57
+ font-weight: 600;
58
+ color: var(--ink);
59
+
60
+ svg {
61
+ flex: none;
62
+ color: var(--navy);
63
+ }
49
64
  }
50
65
 
51
- .logincontainer {
52
- width: 100%;
53
- min-height: 100vh;
54
- display: grid;
55
- place-items: center;
56
- padding: 1rem;
57
- background-color: #f9fafb;
66
+ /* Resend sits on the same line as the expiry note: one is the reason for the
67
+ other. */
68
+ .resendRow {
69
+ display: flex;
70
+ align-items: baseline;
71
+ justify-content: space-between;
72
+ gap: 1rem;
73
+ margin-top: 1.1rem;
74
+ font-size: 0.82rem;
75
+ color: var(--muted);
58
76
  }
59
77
 
60
- .login {
61
- width: 28rem;
62
- padding: 2rem;
78
+ .expiry {
63
79
  margin: 0;
64
- display: grid;
65
- grid-template-columns: 1fr;
66
- gap: 1rem;
67
- background-color: white;
68
- border-radius: 0.75rem;
69
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
70
- 0 2px 4px -1px rgba(0, 0, 0, 0.06);
71
-
72
- > .formItem {
73
- padding: 0;
74
-
75
- small {
76
- position: absolute;
77
- width: 18px;
78
- height: 18px;
79
- right: 14px;
80
- top: 14px;
81
- opacity: 0.4;
82
- color: #6b7280;
83
- }
84
- }
85
-
86
- h2 {
87
- text-align: center;
88
- margin-bottom: 0.5rem;
89
- color: #111827;
90
- font-weight: 600;
91
- font-size: 1.5rem;
92
- }
80
+ font-size: 0.82rem;
81
+ line-height: 1.5;
82
+ color: var(--muted);
93
83
  }
94
84
 
95
- .instructions {
96
- text-align: center;
97
- margin: 0 0 1.5rem;
98
- color: var(--paragraph-color, #6b7280);
99
- font-size: 0.95rem;
100
- line-height: 1.4;
85
+ /* The link is disabled while the request is in flight and, if the consumer set
86
+ a cooldown, for that many seconds after — .link already carries the disabled
87
+ treatment, this only holds the countdown's width steady. */
88
+ .resend {
89
+ flex: none;
90
+ font-variant-numeric: tabular-nums;
101
91
  }
102
92
 
103
- .forgotten span {
104
- display: block;
105
- text-align: center;
106
- color: #6b7280;
107
- padding: 1rem 0;
108
- font-size: 0.875rem;
109
-
110
- a {
111
- color: var(--primary-color, #4f46e5);
112
- text-decoration: none;
113
- font-weight: 500;
114
-
115
- &:hover {
116
- text-decoration: underline;
117
- }
118
- }
93
+ /* "Having trouble?" and the way back to the login screen. Quiet, and last. */
94
+ .help {
95
+ margin: 1.6rem 0 0;
96
+ padding-top: 1.1rem;
97
+ border-top: 1px solid var(--line);
98
+ font-size: 0.82rem;
99
+ line-height: 1.5;
100
+ color: var(--muted);
119
101
  }
120
102
 
121
- .lcontainer {
122
- min-height: 100vh;
123
- display: flex;
103
+ .back {
104
+ display: inline-flex;
124
105
  align-items: center;
125
- justify-content: center;
126
- background-color: #f9fafb;
127
- }
128
-
129
- .lwrap {
130
- display: grid;
131
- grid-template-columns: 1fr;
132
- }
133
-
134
- .btn {
135
- width: max-content;
136
- display: inline-block;
137
- position: relative;
138
- padding: 0.75rem 1rem;
106
+ gap: 0.35rem;
107
+ margin-top: 0.6rem;
108
+ padding: 0;
109
+ border: none;
110
+ background: none;
111
+ font-family: inherit;
112
+ font-size: 0.85rem;
113
+ font-weight: 600;
114
+ color: var(--navy);
139
115
  cursor: pointer;
140
- font-size: 0.875rem;
141
- font-weight: 500;
142
- color: white;
143
- text-decoration: none;
144
- overflow: hidden;
145
- background: var(--primary-color, #4f46e5);
146
- border: 1px solid var(--primary-color, #4f46e5);
147
- border-radius: 0.5rem;
148
- outline: none;
149
- transition: all 0.2s ease;
150
- box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
151
116
 
152
117
  &:hover {
153
- background: var(--secondary-color, #3cbf7d);
154
- border-color: var(--secondary-color, #3cbf7d);
118
+ text-decoration: underline;
155
119
  }
156
120
 
157
- &:focus {
158
- box-shadow: 0 0 0 2px rgba(var(--primary-rgb), 0.25);
121
+ svg {
122
+ flex: none;
159
123
  }
160
124
  }
161
125
 
162
- input:not(:placeholder-shown) + .fi__span,
163
- textarea:not(:placeholder-shown) + .fi__span,
164
- select:not(:placeholder-shown) + .fi__span {
165
- transform: translateY(-135%) translateX(10px) scale(0.85);
166
- opacity: 1;
167
- padding: 0;
168
- background: white;
169
- font-weight: 500;
170
- transition: all 0.3s ease;
171
- color: var(--primary-color, #4f46e5) !important;
172
- }
173
-
174
- .rememberMeIndicator {
126
+ /* Carried over in spirit from the old screen's remember-me confirmation, which
127
+ was the one thing about it worth keeping: after typing a password and then a
128
+ code, it is genuinely useful to be told the box you ticked two screens ago is
129
+ still ticked. */
130
+ .remembered {
175
131
  display: flex;
176
132
  align-items: center;
177
- justify-content: center;
178
- margin: 0.75rem auto 0;
179
- padding: 0.6rem 1rem;
180
- background-color: rgba(var(--primary-rgb), 0.08);
181
- border-radius: var(--br);
182
- border-left: 3px solid var(--primary-color);
183
- max-width: 80%;
184
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
185
- transition: all 0.3s ease;
186
-
187
- &:hover {
188
- background-color: rgba(var(--primary-rgb), 0.12);
189
- transform: translateY(-1px);
190
- box-shadow: 0 3px 6px rgba(0, 0, 0, 0.08);
191
- }
133
+ gap: 0.45rem;
134
+ margin: 0.9rem 0 0;
135
+ padding: 0.55rem 0.75rem;
136
+ border-left: 3px solid var(--navy);
137
+ border-radius: 0.4rem;
138
+ background: var(--warm);
139
+ font-size: 0.82rem;
140
+ font-weight: 500;
141
+ color: var(--ink);
192
142
 
193
- span {
194
- font-size: 0.85rem;
195
- color: var(--primary-color);
196
- font-weight: 500;
197
- display: flex;
198
- align-items: center;
199
-
200
- &::before {
201
- content: '✓';
202
- display: inline-block;
203
- margin-right: 0.5rem;
204
- font-weight: bold;
205
- font-size: 0.9rem;
206
- }
143
+ svg {
144
+ flex: none;
145
+ color: var(--navy);
207
146
  }
208
147
  }