@visns-studio/visns-components 6.6.4 → 6.15.1
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.
- package/README.md +1623 -18
- package/package.json +10 -4
- package/src/components/DataGrid.jsx +181 -38
- package/src/components/Fetch.jsx +80 -3
- package/src/components/Form.jsx +9 -0
- package/src/components/Navigation.jsx +109 -50
- package/src/components/Notification.jsx +279 -9
- package/src/components/TableFilter.jsx +14 -1
- package/src/components/auth/AuthBrandPanel.jsx +43 -0
- package/src/components/auth/AuthLoading.jsx +78 -0
- package/src/components/auth/AuthShell.jsx +59 -0
- package/src/components/auth/ClientAuth.jsx +161 -0
- package/src/components/auth/ClientAuthFrame.jsx +59 -0
- package/src/components/auth/ClientLogin.jsx +266 -55
- package/src/components/auth/ClientOTPVerify.jsx +587 -115
- package/src/components/auth/ImpersonateGate.jsx +254 -0
- package/src/components/auth/Login.jsx +134 -41
- package/src/components/auth/LogoutScreen.jsx +112 -0
- package/src/components/auth/Reset.jsx +134 -77
- package/src/components/auth/TwoFactorAuth.jsx +475 -297
- package/src/components/auth/Verify.jsx +237 -126
- package/src/components/auth/authEndpoints.js +105 -0
- package/src/components/auth/authFont.js +23 -0
- package/src/components/auth/authHelpers.js +465 -0
- package/src/components/auth/clientAuthProtocols.js +240 -0
- package/src/components/auth/useOptionalRouter.js +49 -0
- package/src/components/callQueue/CallQueuePop.jsx +1502 -0
- package/src/components/callQueue/CallQueueSettings.jsx +508 -0
- package/src/components/callQueue/callQueueHelpers.js +280 -0
- package/src/components/callQueue/callQueueSettingsHelpers.js +75 -0
- package/src/components/columns/ColumnRenderers.jsx +53 -7
- package/src/components/generic/GenericAuth.jsx +163 -96
- package/src/components/generic/GenericDetail.jsx +215 -90
- package/src/components/generic/GenericIndex.jsx +20 -47
- package/src/components/generic/GenericMain.jsx +5 -0
- package/src/components/generic/GroupedReportRenderer.jsx +1 -5
- package/src/components/generic/StandardModal.jsx +17 -5
- package/src/components/generic/reportSemanticSteps/SemanticEntityStep.jsx +1 -6
- package/src/components/generic/reportSemanticSteps/SemanticFieldsStep.jsx +4 -11
- package/src/components/generic/reportSemanticSteps/SemanticFiltersStep.jsx +9 -27
- package/src/components/generic/reportSemanticSteps/SemanticGroupingStep.jsx +5 -13
- package/src/components/generic/reportSemanticSteps/SemanticParameterPrompt.jsx +5 -13
- package/src/components/generic/reportSemanticSteps/SemanticPreviewStep.jsx +14 -31
- package/src/components/generic/reportSemanticSteps/SemanticRelationsStep.jsx +3 -9
- package/src/components/generic/reportSemanticSteps/SemanticValueInput.jsx +2 -15
- package/src/components/navActive.js +60 -0
- package/src/components/notify/desktopNotifications.js +256 -0
- package/src/components/sketch/SketchField.jsx +12 -2
- package/src/components/sms/SmsComposeModal.jsx +495 -0
- package/src/components/sms/SmsInbox.jsx +596 -0
- package/src/components/sms/SmsInboxBadge.jsx +468 -0
- package/src/components/sms/SmsLineSettings.jsx +854 -0
- package/src/components/sms/SmsThreadPanel.jsx +1184 -0
- package/src/components/sms/smsEndpoints.js +56 -0
- package/src/components/sms/smsHelpers.js +1075 -0
- package/src/components/sms/smsLiveState.js +132 -0
- package/src/components/sms/useSmsLive.js +307 -0
- package/src/components/styles/CallQueuePop.module.scss +646 -0
- package/src/components/styles/CallQueueSettings.module.scss +460 -0
- package/src/components/styles/ClientAuth.module.scss +229 -0
- package/src/components/styles/GenericDetail.module.scss +153 -26
- package/src/components/styles/GenericIndex.module.scss +13 -0
- package/src/components/styles/GenericMain.module.scss +19 -2
- package/src/components/styles/ImpersonateGate.module.scss +85 -0
- package/src/components/styles/Login.module.scss +13 -122
- package/src/components/styles/LogoutScreen.module.scss +89 -0
- package/src/components/styles/Navigation.module.scss +509 -223
- package/src/components/styles/Notification.module.scss +103 -0
- package/src/components/styles/Reset.module.scss +52 -186
- package/src/components/styles/Sms.module.scss +2382 -0
- package/src/components/styles/TableFilter.module.scss +118 -98
- package/src/components/styles/TwoFactorAuth.module.scss +115 -176
- package/src/components/styles/Vault.module.scss +1983 -0
- package/src/components/styles/Verify.module.scss +57 -180
- package/src/components/styles/_authBrandPanel.scss +163 -0
- package/src/components/styles/_authShell.scss +369 -0
- package/src/components/styles/global-datagrid.css +27 -2
- package/src/components/utils/buildEnv.js +67 -0
- package/src/components/utils/displayValue.js +94 -0
- package/src/components/utils/useDensity.js +345 -9
- package/src/components/vault/OtpChip.jsx +208 -0
- package/src/components/vault/PasswordGenerator.jsx +145 -0
- package/src/components/vault/QrScanner.jsx +326 -0
- package/src/components/vault/VaultAccessLog.jsx +225 -0
- package/src/components/vault/VaultConfirmPanel.jsx +113 -0
- package/src/components/vault/VaultEntryForm.jsx +740 -0
- package/src/components/vault/VaultManager.jsx +1000 -0
- package/src/components/vault/VaultQuickSearch.jsx +681 -0
- package/src/components/vault/qrDecode.js +257 -0
- package/src/components/vault/useDebouncedValue.js +22 -0
- package/src/components/vault/useVaultReveal.js +160 -0
- package/src/components/vault/vaultClipboard.js +33 -0
- package/src/components/vault/vaultEndpoints.js +40 -0
- package/src/components/vault/vaultFit.js +116 -0
- package/src/components/vault/vaultHelpers.js +547 -0
- package/src/components/vault/vaultNavigation.jsx +58 -0
- package/src/components/vault/vaultOtp.js +105 -0
- package/src/index.js +233 -1
|
@@ -0,0 +1,1983 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Vault (components/vault/*)
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// The staff password manager: a popover that lives in the navigation's action
|
|
5
|
+
// pill (VaultQuickSearch), the page behind it (VaultManager), and the small
|
|
6
|
+
// pieces both of them mount — the OTP chip, the confirm panel, the entry form.
|
|
7
|
+
//
|
|
8
|
+
// Every brand value is a CSS custom property with a neutral fallback, as in
|
|
9
|
+
// CallQueueSettings.module.scss, so the library carries no palette of its own.
|
|
10
|
+
//
|
|
11
|
+
// IMPORTANT — why every control below restates its own colour, background,
|
|
12
|
+
// border, font-size AND box size:
|
|
13
|
+
//
|
|
14
|
+
// The popover mounts INSIDE `.hactions > ul`, whose chip rules used to reach
|
|
15
|
+
// every descendant `a` and `button` and hand it white text, a fixed 36px box
|
|
16
|
+
// and a pill radius. On a white panel that renders as blank boxes; it is the
|
|
17
|
+
// bug this file's second pass fixed. Navigation.module.scss now excludes
|
|
18
|
+
// `[data-nav-overlay] *` from those rules, which is the real fix, but a
|
|
19
|
+
// component that survives only because its host is well behaved is a
|
|
20
|
+
// component that breaks the next time it is mounted somewhere new. So nothing
|
|
21
|
+
// here inherits anything it actually needs. No `!important` was required.
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
$vault-mono: 'SFMono-Regular', 'Menlo', 'Consolas', monospace;
|
|
25
|
+
|
|
26
|
+
@mixin vault-tokens {
|
|
27
|
+
--v-primary: var(--primary-color, #00386c);
|
|
28
|
+
--v-secondary: var(--secondary-color, #404040);
|
|
29
|
+
--v-paragraph: var(--paragraph-color, #2b2b2b);
|
|
30
|
+
--v-background: var(--background-color, #fff);
|
|
31
|
+
--v-br: var(--br, 6px);
|
|
32
|
+
--v-btn-br: var(--btn-br, 6px);
|
|
33
|
+
|
|
34
|
+
--v-line: color-mix(in srgb, var(--v-paragraph) 18%, transparent);
|
|
35
|
+
// 76% of the body colour over white lands around #5b5b5b — roughly 6:1,
|
|
36
|
+
// comfortably past 4.5:1. The 62% this started at was 4.0:1 and genuinely
|
|
37
|
+
// hard to read on a tablet.
|
|
38
|
+
--v-muted: color-mix(in srgb, var(--v-paragraph) 76%, transparent);
|
|
39
|
+
--v-surface: color-mix(in srgb, var(--v-paragraph) 4%, var(--v-background));
|
|
40
|
+
--v-hover: color-mix(in srgb, var(--v-primary) 9%, var(--v-background));
|
|
41
|
+
--v-tint: color-mix(in srgb, var(--v-primary) 12%, var(--v-background));
|
|
42
|
+
--v-ok: rgb(31, 122, 71);
|
|
43
|
+
--v-bad: rgb(190, 62, 62);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Undo anything a host may have imposed on a generic element. Applied to every
|
|
47
|
+
// interactive element in the module — see the note at the top of the file.
|
|
48
|
+
@mixin vault-reset {
|
|
49
|
+
box-sizing: border-box;
|
|
50
|
+
width: auto;
|
|
51
|
+
height: auto;
|
|
52
|
+
min-width: 0;
|
|
53
|
+
min-height: 0;
|
|
54
|
+
max-width: none;
|
|
55
|
+
margin: 0;
|
|
56
|
+
font-family: inherit;
|
|
57
|
+
font-weight: 400;
|
|
58
|
+
line-height: 1.3;
|
|
59
|
+
letter-spacing: normal;
|
|
60
|
+
text-transform: none;
|
|
61
|
+
text-decoration: none;
|
|
62
|
+
text-align: left;
|
|
63
|
+
appearance: none;
|
|
64
|
+
outline: none;
|
|
65
|
+
transform: none;
|
|
66
|
+
box-shadow: none;
|
|
67
|
+
// The host's chip rules transition `all`, which makes a control visibly
|
|
68
|
+
// resize on hover once its box is no longer fixed.
|
|
69
|
+
transition: background-color 0.15s ease, color 0.15s ease,
|
|
70
|
+
border-color 0.15s ease;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@mixin vault-focus {
|
|
74
|
+
&:focus-visible {
|
|
75
|
+
outline: 2px solid var(--v-primary);
|
|
76
|
+
outline-offset: 2px;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// A checkbox or a radio, and nothing else.
|
|
81
|
+
//
|
|
82
|
+
// The host CRM styles bare `input` — padding, a min-width, a text-field
|
|
83
|
+
// height, a pill radius — and none of that is typed, so it lands on
|
|
84
|
+
// `input[type=radio]` too and renders the visibility choice as a pair of wide
|
|
85
|
+
// ovals with a dot floating in one of them. Restating the box is not enough on
|
|
86
|
+
// its own: the host's width wins over `width: 16px` often enough (a later
|
|
87
|
+
// rule, a more specific selector) that the only reliable fix is to stop
|
|
88
|
+
// relying on the native painting altogether.
|
|
89
|
+
//
|
|
90
|
+
// So the control is drawn here. `appearance: none` throws away the platform
|
|
91
|
+
// widget, every axis of the box is pinned — width, height, min, max and the
|
|
92
|
+
// flex basis, because a flex parent can stretch a box that has all four — and
|
|
93
|
+
// the mark is painted: an inset ring for the radio's dot, a background tick
|
|
94
|
+
// for the checkbox. Nothing below inherits from the host.
|
|
95
|
+
$vault-tick: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23fff' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.4l3.1 3.1 5.9-6'/%3E%3C/svg%3E";
|
|
96
|
+
|
|
97
|
+
@mixin vault-check {
|
|
98
|
+
@include vault-focus;
|
|
99
|
+
|
|
100
|
+
flex: 0 0 16px;
|
|
101
|
+
box-sizing: border-box;
|
|
102
|
+
width: 16px;
|
|
103
|
+
height: 16px;
|
|
104
|
+
min-width: 16px;
|
|
105
|
+
min-height: 16px;
|
|
106
|
+
max-width: 16px;
|
|
107
|
+
max-height: 16px;
|
|
108
|
+
padding: 0;
|
|
109
|
+
margin: 0;
|
|
110
|
+
border: 1.5px solid var(--v-line);
|
|
111
|
+
border-radius: 3px;
|
|
112
|
+
background: var(--v-background);
|
|
113
|
+
background-image: none;
|
|
114
|
+
box-shadow: none;
|
|
115
|
+
appearance: none;
|
|
116
|
+
-webkit-appearance: none;
|
|
117
|
+
cursor: pointer;
|
|
118
|
+
vertical-align: middle;
|
|
119
|
+
transition: background-color 0.12s ease, border-color 0.12s ease,
|
|
120
|
+
box-shadow 0.12s ease;
|
|
121
|
+
|
|
122
|
+
&:hover:not(:disabled) {
|
|
123
|
+
border-color: var(--v-primary);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
&:checked {
|
|
127
|
+
border-color: var(--v-primary);
|
|
128
|
+
background-color: var(--v-primary);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
&:disabled {
|
|
132
|
+
opacity: 0.55;
|
|
133
|
+
cursor: default;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
&[type='radio'] {
|
|
137
|
+
border-radius: 50%;
|
|
138
|
+
|
|
139
|
+
// Fill, then an inset ring in the surface colour: what is left in the
|
|
140
|
+
// middle is the dot. One property, no pseudo-element — and pseudo
|
|
141
|
+
// elements on an input are exactly the sort of thing a host reset is
|
|
142
|
+
// liable to have opinions about.
|
|
143
|
+
&:checked {
|
|
144
|
+
box-shadow: inset 0 0 0 3px var(--v-background);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
&[type='checkbox'] {
|
|
149
|
+
&:checked {
|
|
150
|
+
background-image: url($vault-tick);
|
|
151
|
+
background-repeat: no-repeat;
|
|
152
|
+
background-position: center;
|
|
153
|
+
background-size: 12px 12px;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@keyframes vaultSpin {
|
|
159
|
+
to {
|
|
160
|
+
transform: rotate(360deg);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
@keyframes vaultPopIn {
|
|
165
|
+
from {
|
|
166
|
+
opacity: 0;
|
|
167
|
+
transform: translateY(-4px);
|
|
168
|
+
}
|
|
169
|
+
to {
|
|
170
|
+
opacity: 1;
|
|
171
|
+
transform: translateY(0);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.spin {
|
|
176
|
+
animation: vaultSpin 1s linear infinite;
|
|
177
|
+
flex-shrink: 0;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.muted {
|
|
181
|
+
color: var(--v-muted);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
// Shared controls
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
.primaryButton {
|
|
189
|
+
@include vault-reset;
|
|
190
|
+
@include vault-focus;
|
|
191
|
+
|
|
192
|
+
display: inline-flex;
|
|
193
|
+
flex-shrink: 0;
|
|
194
|
+
gap: 0.4rem;
|
|
195
|
+
align-items: center;
|
|
196
|
+
justify-content: center;
|
|
197
|
+
padding: 0.5rem 0.85rem;
|
|
198
|
+
border: 1px solid var(--v-primary);
|
|
199
|
+
border-radius: var(--v-btn-br);
|
|
200
|
+
background: var(--v-primary);
|
|
201
|
+
color: #fff;
|
|
202
|
+
font-size: 0.82rem;
|
|
203
|
+
font-weight: 600;
|
|
204
|
+
white-space: nowrap;
|
|
205
|
+
cursor: pointer;
|
|
206
|
+
|
|
207
|
+
svg {
|
|
208
|
+
color: currentColor;
|
|
209
|
+
flex-shrink: 0;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
&:hover:not(:disabled) {
|
|
213
|
+
background: color-mix(in srgb, var(--v-primary) 85%, #000);
|
|
214
|
+
border-color: color-mix(in srgb, var(--v-primary) 85%, #000);
|
|
215
|
+
color: #fff;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
&:disabled {
|
|
219
|
+
opacity: 0.55;
|
|
220
|
+
cursor: default;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.ghostButton {
|
|
225
|
+
@include vault-reset;
|
|
226
|
+
@include vault-focus;
|
|
227
|
+
|
|
228
|
+
display: inline-flex;
|
|
229
|
+
flex-shrink: 0;
|
|
230
|
+
gap: 0.4rem;
|
|
231
|
+
align-items: center;
|
|
232
|
+
justify-content: center;
|
|
233
|
+
padding: 0.45rem 0.75rem;
|
|
234
|
+
border: 1px solid var(--v-line);
|
|
235
|
+
border-radius: var(--v-btn-br);
|
|
236
|
+
background: var(--v-background);
|
|
237
|
+
color: var(--v-paragraph);
|
|
238
|
+
font-size: 0.8rem;
|
|
239
|
+
font-weight: 500;
|
|
240
|
+
white-space: nowrap;
|
|
241
|
+
cursor: pointer;
|
|
242
|
+
|
|
243
|
+
svg {
|
|
244
|
+
color: currentColor;
|
|
245
|
+
flex-shrink: 0;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
&:hover:not(:disabled) {
|
|
249
|
+
background: var(--v-hover);
|
|
250
|
+
border-color: var(--v-primary);
|
|
251
|
+
color: var(--v-primary);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
&:disabled {
|
|
255
|
+
opacity: 0.5;
|
|
256
|
+
cursor: default;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// The row-level actions. Icon plus word: four unexplained glyphs in a row is a
|
|
261
|
+
// puzzle, and both surfaces are used in a hurry.
|
|
262
|
+
.actionPill {
|
|
263
|
+
@include vault-reset;
|
|
264
|
+
@include vault-focus;
|
|
265
|
+
|
|
266
|
+
display: inline-flex;
|
|
267
|
+
flex-shrink: 0;
|
|
268
|
+
gap: 0.3rem;
|
|
269
|
+
align-items: center;
|
|
270
|
+
justify-content: center;
|
|
271
|
+
padding: 0.28rem 0.55rem;
|
|
272
|
+
border: 1px solid var(--v-line);
|
|
273
|
+
border-radius: 999px;
|
|
274
|
+
background: var(--v-background);
|
|
275
|
+
color: var(--v-paragraph);
|
|
276
|
+
font-size: 0.74rem;
|
|
277
|
+
font-weight: 600;
|
|
278
|
+
white-space: nowrap;
|
|
279
|
+
cursor: pointer;
|
|
280
|
+
|
|
281
|
+
svg {
|
|
282
|
+
color: currentColor;
|
|
283
|
+
flex-shrink: 0;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
&:hover:not(:disabled) {
|
|
287
|
+
background: var(--v-tint);
|
|
288
|
+
border-color: var(--v-primary);
|
|
289
|
+
color: var(--v-primary);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
&:disabled {
|
|
293
|
+
opacity: 0.5;
|
|
294
|
+
cursor: default;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// The password copy is the thing people came for, so it leads.
|
|
299
|
+
.actionPillPrimary {
|
|
300
|
+
border-color: color-mix(in srgb, var(--v-primary) 45%, transparent);
|
|
301
|
+
background: var(--v-tint);
|
|
302
|
+
color: var(--v-primary);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.actionPillDanger {
|
|
306
|
+
&:hover:not(:disabled) {
|
|
307
|
+
border-color: var(--v-bad);
|
|
308
|
+
background: color-mix(in srgb, var(--v-bad) 12%, var(--v-background));
|
|
309
|
+
color: var(--v-bad);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// The row's actions, in one cluster: three glyphs that copy something, a
|
|
314
|
+
// hairline, then the pills that change the entry.
|
|
315
|
+
//
|
|
316
|
+
// The copies are icon-only and the pills are not, which is deliberate. A copy
|
|
317
|
+
// is repeatable and harmless, always in the same order, and the row has only
|
|
318
|
+
// so much width; edit and delete are neither repeatable nor harmless, and a
|
|
319
|
+
// bare glyph there asks the user to guess.
|
|
320
|
+
.actionCluster {
|
|
321
|
+
display: inline-flex;
|
|
322
|
+
flex-wrap: nowrap;
|
|
323
|
+
gap: 0.25rem;
|
|
324
|
+
align-items: center;
|
|
325
|
+
justify-content: flex-end;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// The tooltip hangs on this rather than on the button inside it: a disabled
|
|
329
|
+
// button fires no pointer events, and "no 2FA on this entry" is worth reading
|
|
330
|
+
// precisely when the button is disabled.
|
|
331
|
+
.actionIconWrap {
|
|
332
|
+
display: inline-flex;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.actionIcon {
|
|
336
|
+
@include vault-reset;
|
|
337
|
+
@include vault-focus;
|
|
338
|
+
|
|
339
|
+
display: inline-flex;
|
|
340
|
+
flex-shrink: 0;
|
|
341
|
+
box-sizing: border-box;
|
|
342
|
+
width: 28px;
|
|
343
|
+
height: 28px;
|
|
344
|
+
align-items: center;
|
|
345
|
+
justify-content: center;
|
|
346
|
+
padding: 0;
|
|
347
|
+
border: 1px solid var(--v-line);
|
|
348
|
+
border-radius: var(--v-btn-br);
|
|
349
|
+
background: var(--v-background);
|
|
350
|
+
color: var(--v-muted);
|
|
351
|
+
cursor: pointer;
|
|
352
|
+
|
|
353
|
+
svg {
|
|
354
|
+
color: currentColor;
|
|
355
|
+
flex-shrink: 0;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
&:hover:not(:disabled) {
|
|
359
|
+
border-color: var(--v-primary);
|
|
360
|
+
background: var(--v-tint);
|
|
361
|
+
color: var(--v-primary);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
&:disabled {
|
|
365
|
+
opacity: 0.45;
|
|
366
|
+
cursor: default;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// The password is what most people came for, so it reads as the default.
|
|
371
|
+
.actionIconDanger {
|
|
372
|
+
color: var(--v-bad, #b42318);
|
|
373
|
+
|
|
374
|
+
&:hover:not(:disabled) {
|
|
375
|
+
border-color: color-mix(in srgb, var(--v-bad, #b42318) 45%, transparent);
|
|
376
|
+
background: color-mix(in srgb, var(--v-bad, #b42318) 10%, transparent);
|
|
377
|
+
color: var(--v-bad, #b42318);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.actionIconPrimary {
|
|
382
|
+
border-color: color-mix(in srgb, var(--v-primary) 45%, transparent);
|
|
383
|
+
background: var(--v-tint);
|
|
384
|
+
color: var(--v-primary);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// A second and a half of "that worked", where the pointer already is.
|
|
388
|
+
.actionIconDone {
|
|
389
|
+
border-color: var(--v-ok);
|
|
390
|
+
background: color-mix(in srgb, var(--v-ok) 14%, var(--v-background));
|
|
391
|
+
color: var(--v-ok);
|
|
392
|
+
|
|
393
|
+
&:hover:not(:disabled) {
|
|
394
|
+
border-color: var(--v-ok);
|
|
395
|
+
background: color-mix(in srgb, var(--v-ok) 14%, var(--v-background));
|
|
396
|
+
color: var(--v-ok);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.actionDivider {
|
|
401
|
+
display: inline-block;
|
|
402
|
+
flex-shrink: 0;
|
|
403
|
+
width: 1px;
|
|
404
|
+
height: 18px;
|
|
405
|
+
margin: 0 0.2rem;
|
|
406
|
+
background: var(--v-line);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.iconButton {
|
|
410
|
+
@include vault-reset;
|
|
411
|
+
@include vault-focus;
|
|
412
|
+
|
|
413
|
+
display: inline-flex;
|
|
414
|
+
flex-shrink: 0;
|
|
415
|
+
align-items: center;
|
|
416
|
+
justify-content: center;
|
|
417
|
+
padding: 0.3rem;
|
|
418
|
+
border: 1px solid transparent;
|
|
419
|
+
border-radius: var(--v-btn-br);
|
|
420
|
+
background: transparent;
|
|
421
|
+
color: var(--v-paragraph);
|
|
422
|
+
font-size: 0.8rem;
|
|
423
|
+
cursor: pointer;
|
|
424
|
+
|
|
425
|
+
svg {
|
|
426
|
+
color: currentColor;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
&:hover:not(:disabled) {
|
|
430
|
+
border-color: var(--v-line);
|
|
431
|
+
background: var(--v-hover);
|
|
432
|
+
color: var(--v-primary);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
&:disabled {
|
|
436
|
+
opacity: 0.5;
|
|
437
|
+
cursor: default;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.linkButton {
|
|
442
|
+
@include vault-reset;
|
|
443
|
+
@include vault-focus;
|
|
444
|
+
|
|
445
|
+
display: inline-flex;
|
|
446
|
+
align-items: center;
|
|
447
|
+
padding: 0.1rem 0;
|
|
448
|
+
border: none;
|
|
449
|
+
border-radius: 2px;
|
|
450
|
+
background: none;
|
|
451
|
+
color: var(--v-primary);
|
|
452
|
+
font-size: 0.78rem;
|
|
453
|
+
font-weight: 600;
|
|
454
|
+
cursor: pointer;
|
|
455
|
+
|
|
456
|
+
&:hover {
|
|
457
|
+
color: var(--v-primary);
|
|
458
|
+
text-decoration: underline;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.dangerLink {
|
|
463
|
+
@extend .linkButton;
|
|
464
|
+
|
|
465
|
+
color: var(--v-bad);
|
|
466
|
+
|
|
467
|
+
&:hover {
|
|
468
|
+
color: var(--v-bad);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.select {
|
|
473
|
+
@include vault-reset;
|
|
474
|
+
@include vault-focus;
|
|
475
|
+
|
|
476
|
+
padding: 0.45rem 0.6rem;
|
|
477
|
+
border: 1px solid var(--v-line);
|
|
478
|
+
border-radius: var(--v-btn-br);
|
|
479
|
+
background: var(--v-background);
|
|
480
|
+
color: var(--v-paragraph);
|
|
481
|
+
font-size: 0.8rem;
|
|
482
|
+
cursor: pointer;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Every checkbox and radio in the vault carries this, so the box survives
|
|
486
|
+
// being mounted anywhere — see `vault-check`. The descendant selectors below
|
|
487
|
+
// repeat it only as a safety net for markup that forgets the class.
|
|
488
|
+
// Every checkbox and radio in the vault carries this class; the mixin draws
|
|
489
|
+
// the whole control, both shapes included. Qualified with the element so the
|
|
490
|
+
// selector outweighs a host rule of the form `.some-form input`, which is how
|
|
491
|
+
// the CRM was stretching these in the first place.
|
|
492
|
+
input.checkControl {
|
|
493
|
+
@include vault-check;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.inlineCheck {
|
|
497
|
+
display: inline-flex;
|
|
498
|
+
flex-shrink: 0;
|
|
499
|
+
gap: 0.5rem;
|
|
500
|
+
align-items: center;
|
|
501
|
+
color: var(--v-paragraph);
|
|
502
|
+
font-size: 0.8rem;
|
|
503
|
+
line-height: 1.2;
|
|
504
|
+
cursor: pointer;
|
|
505
|
+
|
|
506
|
+
input[type='checkbox'],
|
|
507
|
+
input[type='radio'] {
|
|
508
|
+
@include vault-check;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.searchBar {
|
|
513
|
+
display: flex;
|
|
514
|
+
gap: 0.5rem;
|
|
515
|
+
align-items: center;
|
|
516
|
+
box-sizing: border-box;
|
|
517
|
+
padding: 0.5rem 0.75rem;
|
|
518
|
+
border: 1px solid var(--v-line);
|
|
519
|
+
border-radius: var(--v-btn-br);
|
|
520
|
+
background: var(--v-background);
|
|
521
|
+
color: var(--v-muted);
|
|
522
|
+
|
|
523
|
+
&:focus-within {
|
|
524
|
+
border-color: var(--v-primary);
|
|
525
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--v-primary) 15%, transparent);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.searchInput {
|
|
530
|
+
@include vault-reset;
|
|
531
|
+
|
|
532
|
+
flex: 1;
|
|
533
|
+
min-width: 0;
|
|
534
|
+
padding: 0;
|
|
535
|
+
border: none;
|
|
536
|
+
background: transparent;
|
|
537
|
+
color: var(--v-paragraph);
|
|
538
|
+
font-size: 0.88rem;
|
|
539
|
+
|
|
540
|
+
&::placeholder {
|
|
541
|
+
color: var(--v-muted);
|
|
542
|
+
opacity: 1;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// ---------------------------------------------------------------------------
|
|
547
|
+
// OTP chip
|
|
548
|
+
// ---------------------------------------------------------------------------
|
|
549
|
+
|
|
550
|
+
.otpChip {
|
|
551
|
+
@include vault-tokens;
|
|
552
|
+
@include vault-reset;
|
|
553
|
+
@include vault-focus;
|
|
554
|
+
|
|
555
|
+
display: inline-flex;
|
|
556
|
+
flex-shrink: 0;
|
|
557
|
+
gap: 0.3rem;
|
|
558
|
+
align-items: center;
|
|
559
|
+
justify-content: center;
|
|
560
|
+
padding: 0.28rem 0.55rem;
|
|
561
|
+
border: 1px solid var(--v-line);
|
|
562
|
+
border-radius: 999px;
|
|
563
|
+
background: var(--v-background);
|
|
564
|
+
color: var(--v-paragraph);
|
|
565
|
+
font-size: 0.74rem;
|
|
566
|
+
font-weight: 600;
|
|
567
|
+
white-space: nowrap;
|
|
568
|
+
cursor: pointer;
|
|
569
|
+
|
|
570
|
+
svg {
|
|
571
|
+
color: currentColor;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
&:hover:not(:disabled) {
|
|
575
|
+
border-color: var(--v-primary);
|
|
576
|
+
background: var(--v-tint);
|
|
577
|
+
color: var(--v-primary);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
&:disabled {
|
|
581
|
+
opacity: 0.6;
|
|
582
|
+
cursor: default;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.otpChipLg {
|
|
587
|
+
padding: 0.4rem 0.7rem;
|
|
588
|
+
font-size: 0.85rem;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// The quick search's variant: a 29px square that matches the icon buttons
|
|
592
|
+
// beside it, until a code arrives and the digits become the content.
|
|
593
|
+
.otpChipCompact {
|
|
594
|
+
height: 29px;
|
|
595
|
+
border-radius: var(--v-btn-br);
|
|
596
|
+
|
|
597
|
+
// Reserved so the row does not lurch sideways the moment the code lands.
|
|
598
|
+
&.otpChipOpen {
|
|
599
|
+
min-width: 96px;
|
|
600
|
+
padding: 0 0.45rem;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.otpChipIcon {
|
|
605
|
+
width: 29px;
|
|
606
|
+
min-width: 29px;
|
|
607
|
+
padding: 0;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
.otpChipOpen {
|
|
611
|
+
border-color: color-mix(in srgb, var(--v-primary) 45%, transparent);
|
|
612
|
+
background: var(--v-tint);
|
|
613
|
+
color: var(--v-primary);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
.otpChipExpiring {
|
|
617
|
+
border-color: var(--v-bad);
|
|
618
|
+
background: color-mix(in srgb, var(--v-bad) 12%, var(--v-background));
|
|
619
|
+
color: var(--v-bad);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
.otpCode {
|
|
623
|
+
// Tabular figures, or the code jitters every second as the glyph widths
|
|
624
|
+
// change under it.
|
|
625
|
+
font-family: $vault-mono;
|
|
626
|
+
font-variant-numeric: tabular-nums;
|
|
627
|
+
font-feature-settings: 'tnum';
|
|
628
|
+
letter-spacing: 0.02em;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
.otpLabel {
|
|
632
|
+
letter-spacing: 0.04em;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
.otpRing {
|
|
636
|
+
flex-shrink: 0;
|
|
637
|
+
// 12 o'clock, draining clockwise.
|
|
638
|
+
transform: rotate(-90deg);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.otpRingTrack {
|
|
642
|
+
fill: none;
|
|
643
|
+
stroke: currentColor;
|
|
644
|
+
stroke-opacity: 0.22;
|
|
645
|
+
stroke-width: 2;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
.otpRingValue {
|
|
649
|
+
fill: none;
|
|
650
|
+
stroke: currentColor;
|
|
651
|
+
stroke-width: 2;
|
|
652
|
+
stroke-linecap: round;
|
|
653
|
+
transition: stroke-dashoffset 0.95s linear;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// ---------------------------------------------------------------------------
|
|
657
|
+
// Confirm panel
|
|
658
|
+
// ---------------------------------------------------------------------------
|
|
659
|
+
|
|
660
|
+
.confirmPanel {
|
|
661
|
+
@include vault-tokens;
|
|
662
|
+
|
|
663
|
+
box-sizing: border-box;
|
|
664
|
+
padding: 0.85rem;
|
|
665
|
+
border: 1px solid color-mix(in srgb, var(--v-primary) 35%, transparent);
|
|
666
|
+
border-radius: var(--v-br);
|
|
667
|
+
background: var(--v-tint);
|
|
668
|
+
color: var(--v-paragraph);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
.confirmCompact {
|
|
672
|
+
// Inside the popover the panel replaces the result list. Holding a height
|
|
673
|
+
// close to a couple of rows keeps the popover from collapsing and jumping
|
|
674
|
+
// back out again when the panel closes.
|
|
675
|
+
min-height: 152px;
|
|
676
|
+
margin: 0.6rem;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
.confirmHead {
|
|
680
|
+
display: flex;
|
|
681
|
+
gap: 0.4rem;
|
|
682
|
+
align-items: center;
|
|
683
|
+
margin-bottom: 0.35rem;
|
|
684
|
+
color: var(--v-primary);
|
|
685
|
+
font-size: 0.85rem;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
.confirmMessage {
|
|
689
|
+
margin: 0 0 0.55rem;
|
|
690
|
+
color: var(--v-muted);
|
|
691
|
+
font-size: 0.78rem;
|
|
692
|
+
line-height: 1.45;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
.confirmRow {
|
|
696
|
+
display: flex;
|
|
697
|
+
flex-wrap: wrap;
|
|
698
|
+
gap: 0.4rem;
|
|
699
|
+
align-items: center;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
.confirmInput {
|
|
703
|
+
@include vault-reset;
|
|
704
|
+
@include vault-focus;
|
|
705
|
+
|
|
706
|
+
flex: 1;
|
|
707
|
+
min-width: 150px;
|
|
708
|
+
padding: 0.45rem 0.6rem;
|
|
709
|
+
border: 1px solid var(--v-line);
|
|
710
|
+
border-radius: var(--v-btn-br);
|
|
711
|
+
background: var(--v-background);
|
|
712
|
+
color: var(--v-paragraph);
|
|
713
|
+
font-size: 0.85rem;
|
|
714
|
+
|
|
715
|
+
&[aria-invalid='true'] {
|
|
716
|
+
border-color: var(--v-bad);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
.confirmButton {
|
|
721
|
+
@extend .primaryButton;
|
|
722
|
+
|
|
723
|
+
padding: 0.45rem 0.8rem;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.confirmCancel {
|
|
727
|
+
@extend .ghostButton;
|
|
728
|
+
|
|
729
|
+
padding: 0.45rem 0.7rem;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
.confirmError {
|
|
733
|
+
// Always in the DOM, holding its own height, so an error does not shove
|
|
734
|
+
// the buttons down the instant it appears.
|
|
735
|
+
min-height: 1.15em;
|
|
736
|
+
margin: 0.45rem 0 0;
|
|
737
|
+
color: var(--v-bad);
|
|
738
|
+
font-size: 0.76rem;
|
|
739
|
+
font-weight: 500;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// ---------------------------------------------------------------------------
|
|
743
|
+
// Quick search (header popover)
|
|
744
|
+
// ---------------------------------------------------------------------------
|
|
745
|
+
|
|
746
|
+
.quickSearch {
|
|
747
|
+
@include vault-tokens;
|
|
748
|
+
|
|
749
|
+
position: relative;
|
|
750
|
+
align-items: center;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// Deliberately the same shape as Notification's trigger: these two sit side by
|
|
754
|
+
// side in the action pill and must read as one row of chips, not two designs.
|
|
755
|
+
// This one DOES inherit the header's colour, which is correct — it is a chip.
|
|
756
|
+
.trigger {
|
|
757
|
+
@include vault-focus;
|
|
758
|
+
|
|
759
|
+
position: relative;
|
|
760
|
+
display: flex;
|
|
761
|
+
align-items: center;
|
|
762
|
+
justify-content: center;
|
|
763
|
+
width: 40px;
|
|
764
|
+
height: 40px;
|
|
765
|
+
padding: 8px;
|
|
766
|
+
border: none;
|
|
767
|
+
border-radius: 50%;
|
|
768
|
+
background: transparent;
|
|
769
|
+
color: var(--paragraph-color, #333);
|
|
770
|
+
cursor: pointer;
|
|
771
|
+
transition: all 0.2s ease;
|
|
772
|
+
|
|
773
|
+
&:hover {
|
|
774
|
+
background-color: rgba(var(--primary-color-rgb, 0, 86, 179), 0.1);
|
|
775
|
+
color: var(--primary-color, #0056b3);
|
|
776
|
+
transform: scale(1.05);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
&:active {
|
|
780
|
+
transform: scale(0.95);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
.popoverWrapper {
|
|
785
|
+
position: absolute;
|
|
786
|
+
top: 100%;
|
|
787
|
+
right: 0;
|
|
788
|
+
z-index: 1100;
|
|
789
|
+
margin-top: 10px;
|
|
790
|
+
// The host's chip `li` is a centring flex container. The popover is
|
|
791
|
+
// absolutely positioned so it escapes the flexbox, but `text-align` is
|
|
792
|
+
// inherited and would centre every line of prose inside it.
|
|
793
|
+
text-align: left;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
.popover {
|
|
797
|
+
@include vault-tokens;
|
|
798
|
+
|
|
799
|
+
display: flex;
|
|
800
|
+
flex-direction: column;
|
|
801
|
+
box-sizing: border-box;
|
|
802
|
+
// 560 rather than 480: a real title, a real email username and five
|
|
803
|
+
// actions have to share one line, and 480 was where the actions started
|
|
804
|
+
// wrapping onto a second and third row.
|
|
805
|
+
width: 560px;
|
|
806
|
+
max-width: calc(100vw - 2rem);
|
|
807
|
+
border: 1px solid var(--v-line);
|
|
808
|
+
border-radius: 10px;
|
|
809
|
+
// Opaque, and stated: the header behind this is a dark brand bar, and a
|
|
810
|
+
// panel that inherits any of it is unreadable.
|
|
811
|
+
background: var(--v-background);
|
|
812
|
+
color: var(--v-paragraph);
|
|
813
|
+
font-size: 0.85rem;
|
|
814
|
+
text-align: left;
|
|
815
|
+
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.22), 0 2px 6px rgba(0, 0, 0, 0.12);
|
|
816
|
+
overflow: hidden;
|
|
817
|
+
animation: vaultPopIn 0.18s ease-out;
|
|
818
|
+
|
|
819
|
+
> .searchBar {
|
|
820
|
+
border: none;
|
|
821
|
+
border-bottom: 1px solid var(--v-line);
|
|
822
|
+
border-radius: 0;
|
|
823
|
+
|
|
824
|
+
&:focus-within {
|
|
825
|
+
border-bottom-color: var(--v-primary);
|
|
826
|
+
box-shadow: none;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
.popoverHead {
|
|
832
|
+
display: flex;
|
|
833
|
+
gap: 0.75rem;
|
|
834
|
+
align-items: center;
|
|
835
|
+
justify-content: space-between;
|
|
836
|
+
padding: 0.7rem 0.85rem;
|
|
837
|
+
border-bottom: 1px solid var(--v-line);
|
|
838
|
+
background: var(--v-surface);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
.popoverTitle {
|
|
842
|
+
display: inline-flex;
|
|
843
|
+
gap: 0.4rem;
|
|
844
|
+
align-items: center;
|
|
845
|
+
color: var(--v-paragraph);
|
|
846
|
+
font-size: 0.95rem;
|
|
847
|
+
font-weight: 700;
|
|
848
|
+
|
|
849
|
+
svg {
|
|
850
|
+
color: var(--v-primary);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
.rows {
|
|
855
|
+
max-height: 348px;
|
|
856
|
+
margin: 0;
|
|
857
|
+
padding: 0;
|
|
858
|
+
list-style: none;
|
|
859
|
+
overflow-y: auto;
|
|
860
|
+
|
|
861
|
+
// The same reason every control in this file restates its own box: two
|
|
862
|
+
// hosts reach in here with bare element selectors. Navigation's
|
|
863
|
+
// `.hactions > ul li` handed every row `display: flex`, a 32px minimum
|
|
864
|
+
// box, `justify-content: center` and 2px side margins — at 0,1,2, which
|
|
865
|
+
// the `.row` grid below (0,1,0) cannot outrank — and the CRM's own
|
|
866
|
+
// `ul { padding-left: … }` indented the list. Navigation is scoped to
|
|
867
|
+
// `> li` now, but a component that survives only because its host is well
|
|
868
|
+
// behaved breaks the next time it is mounted somewhere new.
|
|
869
|
+
> li {
|
|
870
|
+
min-width: 0;
|
|
871
|
+
min-height: 0;
|
|
872
|
+
margin: 0;
|
|
873
|
+
padding: 0;
|
|
874
|
+
position: static;
|
|
875
|
+
list-style: none;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// Geometry, restated at 0,2,0 so it wins outright wherever this is mounted.
|
|
880
|
+
// Paint stays in `.row`; only the box is repeated.
|
|
881
|
+
.rows > .row {
|
|
882
|
+
display: grid;
|
|
883
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
884
|
+
align-items: center;
|
|
885
|
+
justify-content: normal;
|
|
886
|
+
box-sizing: border-box;
|
|
887
|
+
min-width: 0;
|
|
888
|
+
min-height: 0;
|
|
889
|
+
margin: 0;
|
|
890
|
+
padding: 0.5rem 0.85rem;
|
|
891
|
+
position: static;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
.rows > .row > .rowMain {
|
|
895
|
+
display: block;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// One row, one line. The identity truncates; the actions never do.
|
|
899
|
+
//
|
|
900
|
+
// It was three stacked blocks — title, meta, then a wrapping strip of labelled
|
|
901
|
+
// pills — which cost three lines per entry and, with a long title beside a
|
|
902
|
+
// long email username, spilled the pills onto two or three rows of their own.
|
|
903
|
+
// A grid with a `minmax(0, 1fr)` first column is what lets the text give way
|
|
904
|
+
// to the buttons instead of pushing them.
|
|
905
|
+
.row {
|
|
906
|
+
display: grid;
|
|
907
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
908
|
+
gap: 0.75rem;
|
|
909
|
+
align-items: center;
|
|
910
|
+
padding: 0.5rem 0.85rem;
|
|
911
|
+
border-bottom: 1px solid color-mix(in srgb, var(--v-line) 55%, transparent);
|
|
912
|
+
border-left: 3px solid transparent;
|
|
913
|
+
cursor: default;
|
|
914
|
+
|
|
915
|
+
&:last-child {
|
|
916
|
+
border-bottom: none;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
.rowActive {
|
|
921
|
+
border-left-color: var(--v-primary);
|
|
922
|
+
background: var(--v-hover);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
.rowMain {
|
|
926
|
+
// `min-width: 0` is the load-bearing declaration: without it a grid item
|
|
927
|
+
// refuses to shrink below its content and the ellipsis never engages.
|
|
928
|
+
min-width: 0;
|
|
929
|
+
// Hard left, and stated rather than inherited: every title in the list
|
|
930
|
+
// starts on the same edge whatever its length, which is what makes the
|
|
931
|
+
// rows scan as a ledger.
|
|
932
|
+
//
|
|
933
|
+
// `stretch` — the default — and NOT `justify-self: start`, which sizes a
|
|
934
|
+
// grid item to fit-content. The content here is `white-space: nowrap`, so
|
|
935
|
+
// its min-content width IS its full width: under `start` the cell would
|
|
936
|
+
// refuse to shrink, overflow its track and slide under the action icons
|
|
937
|
+
// instead of ellipsing. Filling the track is what makes the ellipsis work,
|
|
938
|
+
// and `text-align: left` is what puts the text on its left edge.
|
|
939
|
+
justify-self: stretch;
|
|
940
|
+
overflow: hidden;
|
|
941
|
+
text-align: left;
|
|
942
|
+
color: var(--v-paragraph);
|
|
943
|
+
font-size: 0.88rem;
|
|
944
|
+
line-height: 1.35;
|
|
945
|
+
text-overflow: ellipsis;
|
|
946
|
+
white-space: nowrap;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// Kept for anything still rendering the old two-line shape.
|
|
950
|
+
.rowTitle {
|
|
951
|
+
display: flex;
|
|
952
|
+
gap: 0.35rem;
|
|
953
|
+
align-items: center;
|
|
954
|
+
min-width: 0;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
.rowName {
|
|
958
|
+
color: var(--v-paragraph);
|
|
959
|
+
font-size: 0.9rem;
|
|
960
|
+
font-weight: 700;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
.privateMark {
|
|
964
|
+
flex-shrink: 0;
|
|
965
|
+
margin-right: 0.25rem;
|
|
966
|
+
color: var(--v-muted);
|
|
967
|
+
vertical-align: -1px;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
.rowMeta {
|
|
971
|
+
color: var(--v-muted);
|
|
972
|
+
font-size: 0.82rem;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
.rowActions {
|
|
976
|
+
display: flex;
|
|
977
|
+
flex: 0 0 auto;
|
|
978
|
+
flex-wrap: nowrap;
|
|
979
|
+
gap: 0.25rem;
|
|
980
|
+
// Flush right on every row, so the clusters line up with each other
|
|
981
|
+
// rather than trailing whatever length the title happened to be.
|
|
982
|
+
justify-self: end;
|
|
983
|
+
margin-left: auto;
|
|
984
|
+
align-items: center;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// A square icon button. The label lives in the tooltip and the aria-label,
|
|
988
|
+
// which is what keeps five of them on one line.
|
|
989
|
+
.rowAction {
|
|
990
|
+
@include vault-reset;
|
|
991
|
+
@include vault-focus;
|
|
992
|
+
|
|
993
|
+
display: inline-flex;
|
|
994
|
+
flex: 0 0 auto;
|
|
995
|
+
align-items: center;
|
|
996
|
+
justify-content: center;
|
|
997
|
+
width: 29px;
|
|
998
|
+
height: 29px;
|
|
999
|
+
min-width: 29px;
|
|
1000
|
+
padding: 0;
|
|
1001
|
+
border: 1px solid var(--v-line);
|
|
1002
|
+
border-radius: var(--v-btn-br);
|
|
1003
|
+
background: var(--v-background);
|
|
1004
|
+
color: var(--v-secondary);
|
|
1005
|
+
cursor: pointer;
|
|
1006
|
+
|
|
1007
|
+
svg {
|
|
1008
|
+
flex-shrink: 0;
|
|
1009
|
+
color: currentColor;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
&:hover:not(:disabled) {
|
|
1013
|
+
border-color: var(--v-primary);
|
|
1014
|
+
background: var(--v-tint);
|
|
1015
|
+
color: var(--v-primary);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
&:disabled {
|
|
1019
|
+
opacity: 0.55;
|
|
1020
|
+
cursor: default;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
.rowActionPrimary {
|
|
1025
|
+
border-color: var(--v-primary);
|
|
1026
|
+
background: var(--v-primary);
|
|
1027
|
+
color: #fff;
|
|
1028
|
+
|
|
1029
|
+
&:hover:not(:disabled) {
|
|
1030
|
+
border-color: color-mix(in srgb, var(--v-primary) 85%, #000);
|
|
1031
|
+
background: color-mix(in srgb, var(--v-primary) 85%, #000);
|
|
1032
|
+
color: #fff;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
.empty {
|
|
1037
|
+
padding: 1.75rem 1rem;
|
|
1038
|
+
text-align: center;
|
|
1039
|
+
|
|
1040
|
+
p {
|
|
1041
|
+
margin: 0 0 0.85rem;
|
|
1042
|
+
color: var(--v-muted);
|
|
1043
|
+
font-size: 0.85rem;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
.fallback {
|
|
1048
|
+
display: flex;
|
|
1049
|
+
flex-wrap: wrap;
|
|
1050
|
+
gap: 0.4rem;
|
|
1051
|
+
align-items: center;
|
|
1052
|
+
padding: 0.65rem 0.85rem;
|
|
1053
|
+
border-bottom: 1px solid var(--v-line);
|
|
1054
|
+
background: var(--v-tint);
|
|
1055
|
+
|
|
1056
|
+
label {
|
|
1057
|
+
width: 100%;
|
|
1058
|
+
color: var(--v-paragraph);
|
|
1059
|
+
font-size: 0.75rem;
|
|
1060
|
+
font-weight: 600;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
.fallbackInput {
|
|
1065
|
+
@include vault-reset;
|
|
1066
|
+
@include vault-focus;
|
|
1067
|
+
|
|
1068
|
+
flex: 1;
|
|
1069
|
+
min-width: 160px;
|
|
1070
|
+
padding: 0.4rem 0.5rem;
|
|
1071
|
+
border: 1px solid var(--v-line);
|
|
1072
|
+
border-radius: var(--v-btn-br);
|
|
1073
|
+
background: var(--v-background);
|
|
1074
|
+
color: var(--v-paragraph);
|
|
1075
|
+
font-family: $vault-mono;
|
|
1076
|
+
font-size: 0.82rem;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
.footer {
|
|
1080
|
+
display: flex;
|
|
1081
|
+
gap: 0.5rem;
|
|
1082
|
+
align-items: center;
|
|
1083
|
+
justify-content: center;
|
|
1084
|
+
padding: 0.55rem 0.85rem;
|
|
1085
|
+
border-top: 1px solid var(--v-line);
|
|
1086
|
+
background: var(--v-surface);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
.footerHint {
|
|
1090
|
+
color: var(--v-muted);
|
|
1091
|
+
font-size: 0.75rem; // 12px at a 16px root
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
// ---------------------------------------------------------------------------
|
|
1095
|
+
// Generator
|
|
1096
|
+
// ---------------------------------------------------------------------------
|
|
1097
|
+
|
|
1098
|
+
.generator {
|
|
1099
|
+
@include vault-tokens;
|
|
1100
|
+
|
|
1101
|
+
box-sizing: border-box;
|
|
1102
|
+
margin-top: 0.5rem;
|
|
1103
|
+
padding: 0.75rem;
|
|
1104
|
+
border: 1px solid var(--v-line);
|
|
1105
|
+
border-radius: var(--v-br);
|
|
1106
|
+
background: var(--v-surface);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
.genCandidate {
|
|
1110
|
+
display: flex;
|
|
1111
|
+
gap: 0.5rem;
|
|
1112
|
+
align-items: center;
|
|
1113
|
+
justify-content: space-between;
|
|
1114
|
+
padding: 0.45rem 0.6rem;
|
|
1115
|
+
border: 1px solid var(--v-line);
|
|
1116
|
+
border-radius: var(--v-btn-br);
|
|
1117
|
+
background: var(--v-background);
|
|
1118
|
+
|
|
1119
|
+
code {
|
|
1120
|
+
overflow-wrap: anywhere;
|
|
1121
|
+
color: var(--v-paragraph);
|
|
1122
|
+
font-family: $vault-mono;
|
|
1123
|
+
font-size: 0.88rem;
|
|
1124
|
+
user-select: all;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
.strength {
|
|
1129
|
+
display: flex;
|
|
1130
|
+
gap: 0.5rem;
|
|
1131
|
+
align-items: center;
|
|
1132
|
+
margin-top: 0.5rem;
|
|
1133
|
+
|
|
1134
|
+
&[data-score='0'] .strengthFill {
|
|
1135
|
+
background: var(--v-bad);
|
|
1136
|
+
}
|
|
1137
|
+
&[data-score='1'] .strengthFill {
|
|
1138
|
+
background: rgb(200, 130, 50);
|
|
1139
|
+
}
|
|
1140
|
+
&[data-score='2'] .strengthFill {
|
|
1141
|
+
background: rgb(175, 155, 40);
|
|
1142
|
+
}
|
|
1143
|
+
&[data-score='3'] .strengthFill,
|
|
1144
|
+
&[data-score='4'] .strengthFill {
|
|
1145
|
+
background: var(--v-ok);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
.strengthTrack {
|
|
1150
|
+
flex: 1;
|
|
1151
|
+
height: 5px;
|
|
1152
|
+
border-radius: 999px;
|
|
1153
|
+
background: var(--v-line);
|
|
1154
|
+
overflow: hidden;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
.strengthFill {
|
|
1158
|
+
height: 100%;
|
|
1159
|
+
border-radius: 999px;
|
|
1160
|
+
transition: width 0.2s ease, background-color 0.2s ease;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
.strengthLabel {
|
|
1164
|
+
min-width: 62px;
|
|
1165
|
+
color: var(--v-muted);
|
|
1166
|
+
font-size: 0.75rem;
|
|
1167
|
+
font-weight: 600;
|
|
1168
|
+
text-align: right;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
.genLength {
|
|
1172
|
+
display: block;
|
|
1173
|
+
margin-top: 0.75rem;
|
|
1174
|
+
color: var(--v-muted);
|
|
1175
|
+
font-size: 0.78rem;
|
|
1176
|
+
|
|
1177
|
+
strong {
|
|
1178
|
+
color: var(--v-paragraph);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
input {
|
|
1182
|
+
width: 100%;
|
|
1183
|
+
margin-top: 0.3rem;
|
|
1184
|
+
accent-color: var(--v-primary);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
.genOptions {
|
|
1189
|
+
display: flex;
|
|
1190
|
+
flex-wrap: wrap;
|
|
1191
|
+
gap: 0.75rem;
|
|
1192
|
+
margin-top: 0.6rem;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
.genOption {
|
|
1196
|
+
@extend .inlineCheck;
|
|
1197
|
+
|
|
1198
|
+
font-size: 0.78rem;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
.genActions {
|
|
1202
|
+
display: flex;
|
|
1203
|
+
gap: 0.75rem;
|
|
1204
|
+
align-items: center;
|
|
1205
|
+
margin-top: 0.85rem;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// ---------------------------------------------------------------------------
|
|
1209
|
+
// Entry form
|
|
1210
|
+
// ---------------------------------------------------------------------------
|
|
1211
|
+
|
|
1212
|
+
.form {
|
|
1213
|
+
@include vault-tokens;
|
|
1214
|
+
|
|
1215
|
+
display: flex;
|
|
1216
|
+
flex-direction: column;
|
|
1217
|
+
gap: 0.9rem;
|
|
1218
|
+
box-sizing: border-box;
|
|
1219
|
+
padding: 1rem;
|
|
1220
|
+
color: var(--v-paragraph);
|
|
1221
|
+
font-size: 0.85rem;
|
|
1222
|
+
text-align: left;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
.field {
|
|
1226
|
+
display: flex;
|
|
1227
|
+
flex-direction: column;
|
|
1228
|
+
gap: 0.3rem;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
.label {
|
|
1232
|
+
color: var(--v-muted);
|
|
1233
|
+
font-size: 0.74rem;
|
|
1234
|
+
font-weight: 700;
|
|
1235
|
+
letter-spacing: 0.04em;
|
|
1236
|
+
text-transform: uppercase;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
.input,
|
|
1240
|
+
.textarea {
|
|
1241
|
+
@include vault-reset;
|
|
1242
|
+
@include vault-focus;
|
|
1243
|
+
|
|
1244
|
+
width: 100%;
|
|
1245
|
+
padding: 0.5rem 0.6rem;
|
|
1246
|
+
border: 1px solid var(--v-line);
|
|
1247
|
+
border-radius: var(--v-btn-br);
|
|
1248
|
+
background: var(--v-background);
|
|
1249
|
+
color: var(--v-paragraph);
|
|
1250
|
+
font-size: 0.87rem;
|
|
1251
|
+
|
|
1252
|
+
&::placeholder {
|
|
1253
|
+
color: var(--v-muted);
|
|
1254
|
+
opacity: 1;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
&:focus {
|
|
1258
|
+
border-color: var(--v-primary);
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
&:disabled {
|
|
1262
|
+
background: var(--v-surface);
|
|
1263
|
+
opacity: 0.7;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
.textarea {
|
|
1268
|
+
line-height: 1.5;
|
|
1269
|
+
resize: vertical;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
.help {
|
|
1273
|
+
margin: 0;
|
|
1274
|
+
color: var(--v-muted);
|
|
1275
|
+
font-size: 0.75rem;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
.fieldError {
|
|
1279
|
+
display: flex;
|
|
1280
|
+
gap: 0.3rem;
|
|
1281
|
+
align-items: center;
|
|
1282
|
+
margin: 0;
|
|
1283
|
+
color: var(--v-bad);
|
|
1284
|
+
font-size: 0.76rem;
|
|
1285
|
+
font-weight: 500;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
.totpOk {
|
|
1289
|
+
display: flex;
|
|
1290
|
+
gap: 0.3rem;
|
|
1291
|
+
align-items: center;
|
|
1292
|
+
margin: 0;
|
|
1293
|
+
color: var(--v-ok);
|
|
1294
|
+
font-size: 0.76rem;
|
|
1295
|
+
font-weight: 600;
|
|
1296
|
+
font-variant-numeric: tabular-nums;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
.passwordField {
|
|
1300
|
+
display: flex;
|
|
1301
|
+
flex-direction: column;
|
|
1302
|
+
gap: 0.45rem;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
.inputRow {
|
|
1306
|
+
display: flex;
|
|
1307
|
+
gap: 0.4rem;
|
|
1308
|
+
align-items: center;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
.totpConfigured {
|
|
1312
|
+
display: flex;
|
|
1313
|
+
flex-wrap: wrap;
|
|
1314
|
+
gap: 0.7rem;
|
|
1315
|
+
align-items: center;
|
|
1316
|
+
padding: 0.55rem 0.6rem;
|
|
1317
|
+
border: 1px solid var(--v-line);
|
|
1318
|
+
border-radius: var(--v-btn-br);
|
|
1319
|
+
background: var(--v-surface);
|
|
1320
|
+
color: var(--v-ok);
|
|
1321
|
+
font-size: 0.83rem;
|
|
1322
|
+
font-weight: 600;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// ---------------------------------------------------------------------------
|
|
1326
|
+
// 2FA: the field, and the QR paths into it
|
|
1327
|
+
// ---------------------------------------------------------------------------
|
|
1328
|
+
|
|
1329
|
+
.totpField {
|
|
1330
|
+
display: flex;
|
|
1331
|
+
flex-direction: column;
|
|
1332
|
+
gap: 0.45rem;
|
|
1333
|
+
box-sizing: border-box;
|
|
1334
|
+
padding: 2px;
|
|
1335
|
+
border: 1px dashed transparent;
|
|
1336
|
+
border-radius: var(--v-br);
|
|
1337
|
+
// Only the border colour moves on a drag, so the field does not shift the
|
|
1338
|
+
// fields below it by a pixel when an image is dragged over the modal.
|
|
1339
|
+
transition: border-color 0.15s ease, background-color 0.15s ease;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
.totpFieldDropping {
|
|
1343
|
+
border-color: var(--v-primary);
|
|
1344
|
+
background: var(--v-tint);
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
.qrActions {
|
|
1348
|
+
display: flex;
|
|
1349
|
+
flex-wrap: wrap;
|
|
1350
|
+
gap: 0.5rem;
|
|
1351
|
+
align-items: center;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
// The file input behind "Upload QR image". Not `display: none`, which would
|
|
1355
|
+
// take it out of the accessibility tree along with the label the button
|
|
1356
|
+
// already carries.
|
|
1357
|
+
.visuallyHidden {
|
|
1358
|
+
position: absolute;
|
|
1359
|
+
width: 1px;
|
|
1360
|
+
height: 1px;
|
|
1361
|
+
padding: 0;
|
|
1362
|
+
margin: -1px;
|
|
1363
|
+
overflow: hidden;
|
|
1364
|
+
clip: rect(0, 0, 0, 0);
|
|
1365
|
+
white-space: nowrap;
|
|
1366
|
+
border: 0;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
.qrScanner {
|
|
1370
|
+
display: flex;
|
|
1371
|
+
flex-direction: column;
|
|
1372
|
+
gap: 0.5rem;
|
|
1373
|
+
box-sizing: border-box;
|
|
1374
|
+
padding: 0.6rem;
|
|
1375
|
+
border: 1px solid var(--v-line);
|
|
1376
|
+
border-radius: var(--v-br);
|
|
1377
|
+
background: var(--v-surface);
|
|
1378
|
+
animation: vaultPopIn 0.12s ease-out;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
.qrScannerHead {
|
|
1382
|
+
display: flex;
|
|
1383
|
+
gap: 0.5rem;
|
|
1384
|
+
align-items: center;
|
|
1385
|
+
color: var(--v-paragraph);
|
|
1386
|
+
font-size: 0.8rem;
|
|
1387
|
+
font-weight: 600;
|
|
1388
|
+
|
|
1389
|
+
svg {
|
|
1390
|
+
flex-shrink: 0;
|
|
1391
|
+
color: var(--v-primary);
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
// The title takes the slack, so the controls sit hard right.
|
|
1395
|
+
> span {
|
|
1396
|
+
margin-right: auto;
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
.qrStage {
|
|
1401
|
+
position: relative;
|
|
1402
|
+
overflow: hidden;
|
|
1403
|
+
box-sizing: border-box;
|
|
1404
|
+
width: 100%;
|
|
1405
|
+
max-height: 300px;
|
|
1406
|
+
aspect-ratio: 4 / 3;
|
|
1407
|
+
border-radius: var(--v-br);
|
|
1408
|
+
background: #000;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
.qrVideo {
|
|
1412
|
+
display: block;
|
|
1413
|
+
width: 100%;
|
|
1414
|
+
height: 100%;
|
|
1415
|
+
object-fit: cover;
|
|
1416
|
+
// The preview is a viewfinder, not a mirror: a rear camera pointed at a
|
|
1417
|
+
// printed code must not show the code reversed.
|
|
1418
|
+
transform: none;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
.qrViewfinder {
|
|
1422
|
+
position: absolute;
|
|
1423
|
+
top: 50%;
|
|
1424
|
+
left: 50%;
|
|
1425
|
+
width: min(62%, 200px);
|
|
1426
|
+
aspect-ratio: 1;
|
|
1427
|
+
transform: translate(-50%, -50%);
|
|
1428
|
+
border: 2px solid rgba(255, 255, 255, 0.85);
|
|
1429
|
+
border-radius: 10px;
|
|
1430
|
+
// Everything outside the frame dims, which is what makes the frame read
|
|
1431
|
+
// as a target rather than as a border.
|
|
1432
|
+
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.35);
|
|
1433
|
+
pointer-events: none;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
.qrSweep {
|
|
1437
|
+
position: absolute;
|
|
1438
|
+
top: 0;
|
|
1439
|
+
right: 6px;
|
|
1440
|
+
left: 6px;
|
|
1441
|
+
height: 2px;
|
|
1442
|
+
border-radius: 2px;
|
|
1443
|
+
background: rgba(255, 255, 255, 0.9);
|
|
1444
|
+
animation: vaultQrSweep 2.4s ease-in-out infinite;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
// `top` rather than a transform, because a percentage translate on a 2px-tall
|
|
1448
|
+
// element is 2px — the travel has to be measured against the frame.
|
|
1449
|
+
@keyframes vaultQrSweep {
|
|
1450
|
+
0%,
|
|
1451
|
+
100% {
|
|
1452
|
+
top: 8%;
|
|
1453
|
+
opacity: 0.35;
|
|
1454
|
+
}
|
|
1455
|
+
50% {
|
|
1456
|
+
top: 90%;
|
|
1457
|
+
opacity: 0.9;
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1462
|
+
.qrSweep {
|
|
1463
|
+
top: 50%;
|
|
1464
|
+
animation: none;
|
|
1465
|
+
opacity: 0.5;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
.qrStarting {
|
|
1470
|
+
position: absolute;
|
|
1471
|
+
inset: 0;
|
|
1472
|
+
display: flex;
|
|
1473
|
+
gap: 0.5rem;
|
|
1474
|
+
align-items: center;
|
|
1475
|
+
justify-content: center;
|
|
1476
|
+
background: rgba(0, 0, 0, 0.55);
|
|
1477
|
+
color: #fff;
|
|
1478
|
+
font-size: 0.8rem;
|
|
1479
|
+
font-weight: 600;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
.tagField {
|
|
1483
|
+
display: flex;
|
|
1484
|
+
flex-wrap: wrap;
|
|
1485
|
+
gap: 0.3rem;
|
|
1486
|
+
align-items: center;
|
|
1487
|
+
box-sizing: border-box;
|
|
1488
|
+
padding: 0.35rem 0.45rem;
|
|
1489
|
+
border: 1px solid var(--v-line);
|
|
1490
|
+
border-radius: var(--v-btn-br);
|
|
1491
|
+
background: var(--v-background);
|
|
1492
|
+
|
|
1493
|
+
&:focus-within {
|
|
1494
|
+
border-color: var(--v-primary);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
.tagInput {
|
|
1499
|
+
@include vault-reset;
|
|
1500
|
+
|
|
1501
|
+
flex: 1;
|
|
1502
|
+
min-width: 110px;
|
|
1503
|
+
padding: 0.2rem 0;
|
|
1504
|
+
border: none;
|
|
1505
|
+
background: transparent;
|
|
1506
|
+
color: var(--v-paragraph);
|
|
1507
|
+
font-size: 0.84rem;
|
|
1508
|
+
|
|
1509
|
+
&::placeholder {
|
|
1510
|
+
color: var(--v-muted);
|
|
1511
|
+
opacity: 1;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
.tagPill {
|
|
1516
|
+
display: inline-flex;
|
|
1517
|
+
gap: 0.25rem;
|
|
1518
|
+
align-items: center;
|
|
1519
|
+
padding: 0.12rem 0.5rem;
|
|
1520
|
+
border-radius: 999px;
|
|
1521
|
+
background: var(--v-tint);
|
|
1522
|
+
color: var(--v-primary);
|
|
1523
|
+
font-size: 0.72rem;
|
|
1524
|
+
font-weight: 700;
|
|
1525
|
+
|
|
1526
|
+
button {
|
|
1527
|
+
@include vault-reset;
|
|
1528
|
+
|
|
1529
|
+
display: inline-flex;
|
|
1530
|
+
align-items: center;
|
|
1531
|
+
padding: 0;
|
|
1532
|
+
border: none;
|
|
1533
|
+
background: none;
|
|
1534
|
+
color: inherit;
|
|
1535
|
+
font-size: 0.72rem;
|
|
1536
|
+
cursor: pointer;
|
|
1537
|
+
opacity: 0.7;
|
|
1538
|
+
|
|
1539
|
+
&:hover {
|
|
1540
|
+
color: inherit;
|
|
1541
|
+
opacity: 1;
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
.radioRow {
|
|
1547
|
+
display: flex;
|
|
1548
|
+
flex-wrap: wrap;
|
|
1549
|
+
gap: 1rem;
|
|
1550
|
+
|
|
1551
|
+
label {
|
|
1552
|
+
display: inline-flex;
|
|
1553
|
+
gap: 0.5rem;
|
|
1554
|
+
align-items: center;
|
|
1555
|
+
color: var(--v-paragraph);
|
|
1556
|
+
font-size: 0.84rem;
|
|
1557
|
+
line-height: 1.2;
|
|
1558
|
+
cursor: pointer;
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
input[type='radio'],
|
|
1562
|
+
input[type='checkbox'] {
|
|
1563
|
+
@include vault-check;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
.radioDisabled {
|
|
1568
|
+
cursor: default;
|
|
1569
|
+
opacity: 0.5;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
.formActions {
|
|
1573
|
+
display: flex;
|
|
1574
|
+
gap: 0.6rem;
|
|
1575
|
+
align-items: center;
|
|
1576
|
+
padding-top: 0.5rem;
|
|
1577
|
+
border-top: 1px solid var(--v-line);
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
// ---------------------------------------------------------------------------
|
|
1581
|
+
// Manager page
|
|
1582
|
+
// ---------------------------------------------------------------------------
|
|
1583
|
+
|
|
1584
|
+
.manager {
|
|
1585
|
+
@include vault-tokens;
|
|
1586
|
+
|
|
1587
|
+
box-sizing: border-box;
|
|
1588
|
+
// A tight bottom: the pager is measured to end just above the fold, and a
|
|
1589
|
+
// 2rem skirt under it was enough to put a scrollbar on the page.
|
|
1590
|
+
padding: 1.25rem 1.5rem 1rem;
|
|
1591
|
+
color: var(--v-paragraph);
|
|
1592
|
+
font-size: 0.85rem;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
.managerHead {
|
|
1596
|
+
display: flex;
|
|
1597
|
+
flex-wrap: wrap;
|
|
1598
|
+
gap: 1rem;
|
|
1599
|
+
align-items: flex-start;
|
|
1600
|
+
justify-content: space-between;
|
|
1601
|
+
margin-bottom: 1rem;
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
.pageTitle {
|
|
1605
|
+
margin: 0;
|
|
1606
|
+
color: var(--v-paragraph);
|
|
1607
|
+
font-size: 1.5rem;
|
|
1608
|
+
font-weight: 700;
|
|
1609
|
+
line-height: 1.2;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
.pageIntro {
|
|
1613
|
+
max-width: 62ch;
|
|
1614
|
+
margin: 0.35rem 0 0;
|
|
1615
|
+
color: var(--v-muted);
|
|
1616
|
+
font-size: 0.83rem;
|
|
1617
|
+
line-height: 1.5;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
.toolbar {
|
|
1621
|
+
display: flex;
|
|
1622
|
+
flex-wrap: wrap;
|
|
1623
|
+
gap: 0.6rem;
|
|
1624
|
+
align-items: center;
|
|
1625
|
+
margin-bottom: 1rem;
|
|
1626
|
+
padding: 0.65rem;
|
|
1627
|
+
border: 1px solid var(--v-line);
|
|
1628
|
+
border-radius: var(--v-br);
|
|
1629
|
+
background: var(--v-surface);
|
|
1630
|
+
|
|
1631
|
+
.searchBar {
|
|
1632
|
+
flex: 1;
|
|
1633
|
+
min-width: 240px;
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
.tableWrap {
|
|
1638
|
+
// A last resort only: the table is sized to fit a 1024px tablet, and the
|
|
1639
|
+
// URL column folds away below 1200px so it does not have to scroll there.
|
|
1640
|
+
overflow-x: auto;
|
|
1641
|
+
border: 1px solid var(--v-line);
|
|
1642
|
+
border-radius: var(--v-br);
|
|
1643
|
+
background: var(--v-background);
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
// The list is sized to the window rather than to its contents — the height
|
|
1647
|
+
// comes from JS, which is the only place the distance to the bottom of the
|
|
1648
|
+
// viewport is knowable. What this adds is the clipping: a row that does not
|
|
1649
|
+
// fit is cut off here instead of growing the page and pushing the pager out
|
|
1650
|
+
// of reach. In a window too short even for the five-row floor, the component
|
|
1651
|
+
// switches this to `overflow-y: auto` and the header stays put.
|
|
1652
|
+
.tableWrapFitted {
|
|
1653
|
+
overflow-x: hidden;
|
|
1654
|
+
|
|
1655
|
+
thead th {
|
|
1656
|
+
position: sticky;
|
|
1657
|
+
top: 0;
|
|
1658
|
+
z-index: 1;
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
// Fitted mode: every row is exactly --vault-row-height tall so the page size
|
|
1662
|
+
// is arithmetic. One line per row; anything longer is clipped with an
|
|
1663
|
+
// ellipsis, and the tag chips stop wrapping.
|
|
1664
|
+
.tableWrapFitted {
|
|
1665
|
+
--vault-row-height: 58px;
|
|
1666
|
+
|
|
1667
|
+
tbody tr {
|
|
1668
|
+
height: var(--vault-row-height);
|
|
1669
|
+
max-height: var(--vault-row-height);
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
tbody td {
|
|
1673
|
+
height: var(--vault-row-height);
|
|
1674
|
+
padding-top: 0;
|
|
1675
|
+
padding-bottom: 0;
|
|
1676
|
+
white-space: nowrap;
|
|
1677
|
+
overflow: hidden;
|
|
1678
|
+
text-overflow: ellipsis;
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
tbody td > * {
|
|
1682
|
+
max-width: 100%;
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
// Title + tag line share the fixed row; the tags clip, never wrap.
|
|
1686
|
+
tbody td .tagRow {
|
|
1687
|
+
flex-wrap: nowrap;
|
|
1688
|
+
overflow: hidden;
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
tbody td {
|
|
1692
|
+
line-height: 1.25;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
.table {
|
|
1698
|
+
width: 100%;
|
|
1699
|
+
border-collapse: collapse;
|
|
1700
|
+
color: var(--v-paragraph);
|
|
1701
|
+
font-size: 0.84rem;
|
|
1702
|
+
text-align: left;
|
|
1703
|
+
|
|
1704
|
+
th,
|
|
1705
|
+
td {
|
|
1706
|
+
padding: 0.6rem 0.7rem;
|
|
1707
|
+
text-align: left;
|
|
1708
|
+
vertical-align: middle;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
thead th {
|
|
1712
|
+
border-bottom: 1px solid var(--v-line);
|
|
1713
|
+
background: var(--v-surface);
|
|
1714
|
+
color: var(--v-muted);
|
|
1715
|
+
font-size: 0.72rem;
|
|
1716
|
+
font-weight: 700;
|
|
1717
|
+
letter-spacing: 0.05em;
|
|
1718
|
+
text-transform: uppercase;
|
|
1719
|
+
white-space: nowrap;
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
tbody tr {
|
|
1723
|
+
border-bottom: 1px solid color-mix(in srgb, var(--v-line) 55%, transparent);
|
|
1724
|
+
|
|
1725
|
+
&:last-child {
|
|
1726
|
+
border-bottom: none;
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
&:hover {
|
|
1730
|
+
background: var(--v-hover);
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
tbody td strong {
|
|
1735
|
+
color: var(--v-paragraph);
|
|
1736
|
+
font-size: 0.88rem;
|
|
1737
|
+
font-weight: 700;
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
.sortButton {
|
|
1742
|
+
@include vault-reset;
|
|
1743
|
+
@include vault-focus;
|
|
1744
|
+
|
|
1745
|
+
display: inline-flex;
|
|
1746
|
+
gap: 0.25rem;
|
|
1747
|
+
align-items: center;
|
|
1748
|
+
padding: 0;
|
|
1749
|
+
border: none;
|
|
1750
|
+
background: none;
|
|
1751
|
+
color: inherit;
|
|
1752
|
+
font-size: inherit;
|
|
1753
|
+
font-weight: inherit;
|
|
1754
|
+
letter-spacing: inherit;
|
|
1755
|
+
text-transform: inherit;
|
|
1756
|
+
cursor: pointer;
|
|
1757
|
+
|
|
1758
|
+
svg {
|
|
1759
|
+
color: var(--v-primary);
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
&:hover {
|
|
1763
|
+
color: var(--v-primary);
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
.actionsHead,
|
|
1768
|
+
.actionsCell {
|
|
1769
|
+
text-align: right;
|
|
1770
|
+
white-space: nowrap;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
.actionsCell {
|
|
1774
|
+
display: table-cell;
|
|
1775
|
+
|
|
1776
|
+
// Wraps rather than forcing a horizontal scrollbar on a narrow tablet.
|
|
1777
|
+
> * {
|
|
1778
|
+
margin-left: 0.25rem;
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
.hostLink {
|
|
1783
|
+
@include vault-reset;
|
|
1784
|
+
@include vault-focus;
|
|
1785
|
+
|
|
1786
|
+
display: inline-flex;
|
|
1787
|
+
gap: 0.25rem;
|
|
1788
|
+
align-items: center;
|
|
1789
|
+
color: var(--v-primary);
|
|
1790
|
+
font-size: 0.83rem;
|
|
1791
|
+
font-weight: 500;
|
|
1792
|
+
|
|
1793
|
+
&:hover {
|
|
1794
|
+
color: var(--v-primary);
|
|
1795
|
+
text-decoration: underline;
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
// Only visible once the URL column folds away, so the site is never a mystery
|
|
1800
|
+
// on a tablet.
|
|
1801
|
+
.hostUnderTitle {
|
|
1802
|
+
display: none;
|
|
1803
|
+
color: var(--v-muted);
|
|
1804
|
+
font-size: 0.76rem;
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
.pill {
|
|
1808
|
+
display: inline-flex;
|
|
1809
|
+
gap: 0.25rem;
|
|
1810
|
+
align-items: center;
|
|
1811
|
+
padding: 0.12rem 0.5rem;
|
|
1812
|
+
border-radius: 999px;
|
|
1813
|
+
font-size: 0.72rem;
|
|
1814
|
+
font-weight: 700;
|
|
1815
|
+
white-space: nowrap;
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
.pillShared {
|
|
1819
|
+
background: color-mix(in srgb, var(--v-ok) 15%, var(--v-background));
|
|
1820
|
+
color: var(--v-ok);
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
.pillPrivate {
|
|
1824
|
+
background: color-mix(in srgb, var(--v-paragraph) 12%, var(--v-background));
|
|
1825
|
+
color: var(--v-muted);
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
.deletedRow {
|
|
1829
|
+
opacity: 0.65;
|
|
1830
|
+
|
|
1831
|
+
strong {
|
|
1832
|
+
text-decoration: line-through;
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
.deletedTag {
|
|
1837
|
+
margin-left: 0.4rem;
|
|
1838
|
+
padding: 0.05rem 0.4rem;
|
|
1839
|
+
border-radius: 999px;
|
|
1840
|
+
background: color-mix(in srgb, var(--v-bad) 15%, var(--v-background));
|
|
1841
|
+
color: var(--v-bad);
|
|
1842
|
+
font-size: 0.66rem;
|
|
1843
|
+
font-weight: 700;
|
|
1844
|
+
text-decoration: none;
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
// Under the title, not beside it: the title keeps its own line and the
|
|
1848
|
+
// chips take a compact second one, which is what lets a row stay short.
|
|
1849
|
+
.tagRow {
|
|
1850
|
+
display: flex;
|
|
1851
|
+
flex-wrap: nowrap;
|
|
1852
|
+
gap: 0.25rem;
|
|
1853
|
+
margin: 0.2rem 0 0;
|
|
1854
|
+
max-width: 100%;
|
|
1855
|
+
overflow: hidden;
|
|
1856
|
+
|
|
1857
|
+
.tagPill {
|
|
1858
|
+
flex: 0 0 auto;
|
|
1859
|
+
padding: 0.05rem 0.45rem;
|
|
1860
|
+
font-size: 0.68rem;
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
.tableEmpty {
|
|
1865
|
+
padding: 2.5rem 1rem !important;
|
|
1866
|
+
color: var(--v-muted);
|
|
1867
|
+
text-align: center !important;
|
|
1868
|
+
|
|
1869
|
+
p {
|
|
1870
|
+
margin: 0 0 0.9rem;
|
|
1871
|
+
color: var(--v-muted);
|
|
1872
|
+
font-size: 0.88rem;
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
.pager {
|
|
1877
|
+
display: flex;
|
|
1878
|
+
flex-wrap: wrap;
|
|
1879
|
+
gap: 0.75rem;
|
|
1880
|
+
align-items: center;
|
|
1881
|
+
justify-content: space-between;
|
|
1882
|
+
// PAGER_GAP in VaultManager.jsx budgets for this margin; changing one
|
|
1883
|
+
// without the other loses the bottom of the pager or leaves a gap.
|
|
1884
|
+
margin-top: 0.85rem;
|
|
1885
|
+
color: var(--v-muted);
|
|
1886
|
+
font-size: 0.8rem;
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
.pagerButtons {
|
|
1890
|
+
display: flex;
|
|
1891
|
+
gap: 0.5rem;
|
|
1892
|
+
align-items: center;
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
// An iPad in landscape is 1024px, and the CRM's own chrome takes some of that.
|
|
1896
|
+
// Below 1200 the URL column folds and the host moves under the title, which
|
|
1897
|
+
// keeps the whole table inside the viewport with no horizontal scroll.
|
|
1898
|
+
@media (max-width: 1200px) {
|
|
1899
|
+
.urlColumn {
|
|
1900
|
+
display: none;
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
.hostUnderTitle {
|
|
1904
|
+
display: block;
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
@media (max-width: 900px) {
|
|
1909
|
+
.manager {
|
|
1910
|
+
padding: 1rem 0.85rem 1rem;
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
.actionsCell {
|
|
1914
|
+
text-align: left;
|
|
1915
|
+
|
|
1916
|
+
> * {
|
|
1917
|
+
margin: 0.15rem 0.25rem 0.15rem 0;
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
.actionCluster {
|
|
1922
|
+
justify-content: flex-start;
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
// ---------------------------------------------------------------------------
|
|
1927
|
+
// Access log
|
|
1928
|
+
// ---------------------------------------------------------------------------
|
|
1929
|
+
|
|
1930
|
+
.log {
|
|
1931
|
+
@include vault-tokens;
|
|
1932
|
+
|
|
1933
|
+
box-sizing: border-box;
|
|
1934
|
+
padding: 1rem;
|
|
1935
|
+
color: var(--v-paragraph);
|
|
1936
|
+
font-size: 0.85rem;
|
|
1937
|
+
text-align: left;
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
.logFilters {
|
|
1941
|
+
display: flex;
|
|
1942
|
+
flex-wrap: wrap;
|
|
1943
|
+
gap: 0.5rem;
|
|
1944
|
+
margin-bottom: 0.85rem;
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
.logRows {
|
|
1948
|
+
max-height: 50vh;
|
|
1949
|
+
margin: 0;
|
|
1950
|
+
padding: 0;
|
|
1951
|
+
list-style: none;
|
|
1952
|
+
overflow-y: auto;
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
.logRow {
|
|
1956
|
+
display: flex;
|
|
1957
|
+
flex-wrap: wrap;
|
|
1958
|
+
gap: 0.5rem;
|
|
1959
|
+
align-items: baseline;
|
|
1960
|
+
justify-content: space-between;
|
|
1961
|
+
padding: 0.55rem 0;
|
|
1962
|
+
border-bottom: 1px solid color-mix(in srgb, var(--v-line) 55%, transparent);
|
|
1963
|
+
color: var(--v-paragraph);
|
|
1964
|
+
font-size: 0.83rem;
|
|
1965
|
+
|
|
1966
|
+
&:last-child {
|
|
1967
|
+
border-bottom: none;
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
em {
|
|
1971
|
+
color: var(--v-primary);
|
|
1972
|
+
font-style: normal;
|
|
1973
|
+
font-weight: 600;
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
.logMeta {
|
|
1978
|
+
display: flex;
|
|
1979
|
+
gap: 0.75rem;
|
|
1980
|
+
color: var(--v-muted);
|
|
1981
|
+
font-size: 0.75rem;
|
|
1982
|
+
white-space: nowrap;
|
|
1983
|
+
}
|