@aracreate/arm-ui 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,769 @@
1
+ /* ── Poppins font binding ──────────────────────────────────────────────────
2
+ * The app's fonts.css self-hosts Poppins via @fontsource; the DS package
3
+ * doesn't own font files, so consumers must load Poppins themselves and this
4
+ * only binds Radix's font-family variables to it. */
5
+ .radix-themes {
6
+ --default-font-family: var(--font-poppins), sans-serif;
7
+ --heading-font-family: var(--font-poppins), sans-serif;
8
+ }
9
+
10
+ /* ── DS-specific custom tokens ──────────────────────────────────────────────
11
+ * Only the tokens the components in src/ actually reference — a minimal
12
+ * subset of the app's full theme-overrides.css, not the whole file (which
13
+ * also carries app-only tokens). Keep in sync by hand if either file's
14
+ * values change. */
15
+ :root {
16
+ --color-paused: #d21120; /* "Paused" badge — Figma's measured red
17
+ (purer than red-10, deliberate anchor) */
18
+ --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.04), 0 2px 8px rgba(0, 0, 0, 0.08);
19
+
20
+ /* The fixed shell surface white. The platform ships light appearance only,
21
+ * so these have no alternate value. The ink tokens moved to the contested
22
+ * block below. */
23
+ --shell-surface: #ffffff;
24
+ --border-hairline: var(--gray-4);
25
+ }
26
+
27
+ /* ── Contested tokens — declared on .radix-themes, NOT :root ────────────────
28
+ * Consuming apps declare these same tokens in their own theme-overrides.css on
29
+ * `:root`, and their stylesheet is bundled after this one — so a `:root`
30
+ * declaration here always loses on source order. `.radix-themes` is the Theme
31
+ * wrapper div, an inner element, so its value beats one inherited from `:root`
32
+ * (html) by proximity regardless of source order. That is what makes these
33
+ * effective without editing the apps.
34
+ *
35
+ * The four --color-* aliases carry literal hex rather than var(--amber-9) /
36
+ * var(--gray-12) on purpose: this package never defines the --amber-* scale and
37
+ * re-anchors --gray-12 to #1a1a1a in radix-brand-tokens.css, so a var() would
38
+ * resolve to a different color in Storybook than in the shell. The hex values
39
+ * are the shell's resolved values. Nothing references these four yet — they are
40
+ * a meaning-first layer awaiting adoption at the call sites. */
41
+ .radix-themes {
42
+ --color-success: #186A43; /* account "active" status */
43
+ --color-warning: #F9BF3B; /* brand amber — metric "warn" status */
44
+ --color-danger: #B3261E; /* account "suspended" status */
45
+ --color-info: #555555; /* flat ink — "sync" activity / info copy */
46
+
47
+ --surface-content: #F6F6F6; /* main content/page background */
48
+ --surface-input: #FFFFFF; /* text-field + OTP fill */
49
+ --surface-readonly: #CECECE; /* disabled/readonly input fill */
50
+
51
+ /* Ink tiers. Strong and mid used to be the same #555555, so every component
52
+ * that reached for the strong tier (page titles, auth headings, field labels,
53
+ * hover states) got nothing for it and hierarchy rested on size and weight
54
+ * alone. Strong is now its own darker step of the same neutral: 12.63:1 on
55
+ * white and 8.22:1 on the amber bar, against mid's 7.46:1 and 4.85:1.
56
+ * Declared here rather than on :root because core-fe and calendar-fe still
57
+ * carry their own :root copies at #555555, which would otherwise win. */
58
+ --ink-strong: #333333; /* headings, labels, emphasis */
59
+ --ink-mid: #555555; /* body ink, secondary labels */
60
+ --shell-ink-strong: #333333; /* shell chrome — same tiers, pinned */
61
+ --shell-ink-mid: #555555;
62
+ }
63
+
64
+ /* --gray-11 / --gray-a11. Radix declares these on `:root, .light, .light-theme`
65
+ * and the preset preflight in style.css on `.radix-themes` — both 0,1,0 on the
66
+ * Theme div, so this rule wins on source order alone: tokens.css must load after
67
+ * `@radix-ui/themes/styles.css` and `style.css`, as every consumer's import
68
+ * order already has it. No app declares its own copy of this row any more. */
69
+ .radix-themes {
70
+ --gray-11: #646464; /* AA on white (5.92:1), #F6F6F6 (5.48:1) and the gray-3 selected-row wash (4.96:1) */
71
+
72
+ /* Solid, not #6F6F6Fe3. The alpha variant is what components actually render
73
+ * for muted text, and 0xe3 = 0.89 composited against the page gave 4.00:1 on
74
+ * white and 3.76:1 on #F6F6F6 — under the 4.5:1 AA floor for normal text, on
75
+ * body copy, table cells, addresses, counts and descriptions across every
76
+ * page. The accessible value was already defined on the line above and was
77
+ * not the one being used.
78
+ *
79
+ * It shipped because the base token was inspected and the base token is fine:
80
+ * #6F6F6F passes. The failure only exists after alpha compositing against a
81
+ * real background, which reading a token value never shows. Check this token
82
+ * by compositing against the rendered background, not by reading it.
83
+ *
84
+ * Darkening the base instead would have needed #616161 to clear 4.5:1 at
85
+ * alpha 0.89, landing at 4.52:1 — over the line by 0.02. Dropping the alpha
86
+ * kept the colour and gave 4.65:1/5.02:1.
87
+ *
88
+ * #6F6F6F then still failed on the tinted surfaces muted text also sits on:
89
+ * 4.21:1 on --gray-3 (a selected search result) and 4.37:1 on --gray-a3 (a
90
+ * selected table row). #646464 is Radix's own stock gray-11, the same
91
+ * neutral, and clears all of them. contrast-pairs.test.ts measures each. */
92
+ --gray-a11: #646464;
93
+ }
94
+
95
+ /* ── Premium amber primary button (ConfirmDialog's Confirm action) ─────────
96
+ * Color/shadow treatment ONLY — geometry (height, padding, font-size) comes
97
+ * from the Radix `size` prop so every button stays on the DS scale. */
98
+ .btn-amber-primary {
99
+ font-family: var(--font-poppins), sans-serif !important;
100
+ font-weight: 600 !important;
101
+ box-shadow: none !important;
102
+ }
103
+
104
+ .btn-amber-primary:focus-visible {
105
+ outline: 2px solid var(--accent-9) !important;
106
+ outline-offset: 2px !important;
107
+ box-shadow: none !important;
108
+ }
109
+
110
+ /* 0.55 left the label reading roughly 1.3:1 against its own fill — legible as a
111
+ shape, not as words. Disabled controls are exempt from the contrast rules,
112
+ but this is the state the login screen *opens* in, so its primary action was
113
+ effectively invisible until the form validated. Still plainly inactive at
114
+ 0.7, just readable. */
115
+ .btn-amber-primary:disabled {
116
+ opacity: 0.7 !important;
117
+ }
118
+
119
+ /* ── Skeleton loading pulse ─────────────────────────────────────────────── */
120
+
121
+ .skeleton {
122
+ background: var(--gray-4);
123
+ animation: skeletonPulse 1.4s ease-in-out infinite;
124
+ }
125
+
126
+ @keyframes skeletonPulse {
127
+ 0%, 100% { opacity: 0.45; }
128
+ 50% { opacity: 1; }
129
+ }
130
+
131
+ /* ── Auth layout — full-page split (AuthCard) ──────────────────────────── */
132
+
133
+ @media (max-width: 767px) {
134
+ .auth-card-left {
135
+ display: none !important;
136
+ }
137
+ /* Tightens rather than loosens. This block used to raise the horizontal
138
+ padding from --space-5 (21.6px) to --space-6 (28.8px), adding 14.4px per
139
+ side to the card's minimum width at exactly the widths where the login
140
+ page was already overflowing. */
141
+ .auth-card-right {
142
+ padding: var(--space-6) var(--space-4) !important;
143
+ }
144
+ }
145
+
146
+ /* Auth art card — white rounded panel on the amber page (BrandPanel). */
147
+ .auth-art-card {
148
+ background: var(--accent-9);
149
+ border-radius: var(--radius-5);
150
+ height: 100%;
151
+ display: flex;
152
+ align-items: center;
153
+ justify-content: center;
154
+ overflow: hidden;
155
+ }
156
+
157
+ /* ── OTP input boxes (OtpInput) ────────────────────────────────────────── */
158
+
159
+ .otp-box.rt-TextFieldRoot {
160
+ width: var(--space-9);
161
+ height: var(--space-9);
162
+ border-radius: var(--radius-5);
163
+ background: var(--shell-surface);
164
+ padding: 0;
165
+ }
166
+
167
+ .otp-box .rt-TextFieldInput {
168
+ height: 100%;
169
+ text-align: center;
170
+ text-indent: 0;
171
+ color: var(--shell-ink-strong);
172
+ font-family: var(--font-poppins), sans-serif;
173
+ font-size: var(--font-size-6);
174
+ font-weight: 600;
175
+ padding: 0;
176
+ line-height: normal;
177
+ }
178
+
179
+ .otp-box.rt-TextFieldRoot:where(:has(.rt-TextFieldInput:focus)) {
180
+ box-shadow: 0 0 0 1px var(--gray-6), 0 0 0 3px rgba(85, 85, 85, 0.10);
181
+ }
182
+
183
+ /* ── Underlined ink links on amber (OtpVerifyStep's "Use a Different Email") ── */
184
+
185
+ .auth-underline-link {
186
+ color: var(--ink-mid);
187
+ font-family: var(--font-poppins), sans-serif;
188
+ text-decoration: underline;
189
+ text-underline-offset: 2px;
190
+ font-weight: 500;
191
+ border-radius: var(--radius-1);
192
+ transition: color 120ms ease;
193
+ background: none;
194
+ border: none;
195
+ cursor: pointer;
196
+ padding: 0;
197
+ /* With `padding: 0` the height came from the text line box alone — 18px,
198
+ under the 24px pointer-target minimum. These are real buttons ("Resend
199
+ code", "Use a different email"), so the inline-text exception does not
200
+ cover them. */
201
+ display: inline-flex;
202
+ align-items: center;
203
+ min-height: var(--space-6);
204
+ }
205
+ .auth-underline-link:hover { color: var(--ink-strong); }
206
+ .auth-underline-link:focus-visible {
207
+ outline: 2px solid var(--shell-ink-strong);
208
+ outline-offset: 2px;
209
+ }
210
+
211
+ /* ── Shell nav (Sidebar) ────────────────────────────────────────────────── */
212
+
213
+ /*
214
+ * shell-nav-item: applied to <Link> (renders as <a>) and action <button>s.
215
+ * The row is a full-bleed click target; hover/focus visuals land on the inset
216
+ * .nav-pill surface (inset pill, not a full-width wash).
217
+ */
218
+ .shell-nav-item {
219
+ outline: none; /* suppress default browser outline */
220
+ -webkit-tap-highlight-color: transparent;
221
+ }
222
+ /* Inactive: neutral gray wash on the pill on hover */
223
+ .shell-nav-item:not([data-active]):hover .nav-pill {
224
+ background-color: var(--gray-a3);
225
+ }
226
+ /* Active (solid amber pill): deeper amber on hover */
227
+ .shell-nav-item[data-active="true"]:hover .nav-pill {
228
+ background-color: var(--accent-10);
229
+ }
230
+ .shell-nav-item:focus-visible .nav-pill {
231
+ outline: 2px solid var(--accent-8);
232
+ outline-offset: -2px;
233
+ }
234
+
235
+ .shell-profile-chip:hover {
236
+ background-color: var(--gray-a3);
237
+ }
238
+
239
+ /* Sidebar icon button (collapse toggle, close, hamburger) */
240
+ .sidebar-icon-btn {
241
+ display: flex;
242
+ align-items: center;
243
+ justify-content: center;
244
+ width: var(--space-6);
245
+ height: var(--space-6);
246
+ border-radius: var(--radius-2);
247
+ border: none;
248
+ background: none;
249
+ cursor: pointer;
250
+ color: var(--gray-9);
251
+ transition: background 100ms ease, color 100ms ease;
252
+ flex-shrink: 0;
253
+ outline: none;
254
+ }
255
+ .sidebar-icon-btn:hover {
256
+ background: var(--gray-a3);
257
+ color: var(--gray-12);
258
+ }
259
+ .sidebar-icon-btn:focus-visible {
260
+ outline: 2px solid var(--accent-8);
261
+ outline-offset: 1px;
262
+ }
263
+
264
+ /* Sidebar collapse toggle — white square straddling the sidebar's right edge.
265
+ Position (top/right) set inline. */
266
+ .sidebar-collapse-btn {
267
+ position: absolute;
268
+ display: flex;
269
+ align-items: center;
270
+ justify-content: center;
271
+ width: var(--space-5);
272
+ height: var(--space-5);
273
+ padding: 0;
274
+ background: var(--shell-surface);
275
+ border: 1px solid var(--gray-6);
276
+ border-radius: var(--radius-2);
277
+ color: var(--shell-ink-mid);
278
+ cursor: pointer;
279
+ transition: background 100ms ease;
280
+ }
281
+ /* The visible square is 21.6px on purpose — it straddles the rail edge and the
282
+ Figma treatment depends on that size. Pointer-target size is measured on the
283
+ clickable area rather than the paint, so the hit box is widened to 28.8px
284
+ here instead of growing the button and pushing it off the edge. */
285
+ .sidebar-collapse-btn::before {
286
+ content: "";
287
+ position: absolute;
288
+ inset: calc((var(--space-6) - var(--space-5)) / -2);
289
+ }
290
+
291
+ .sidebar-collapse-btn:hover {
292
+ background: var(--gray-2);
293
+ }
294
+ .sidebar-collapse-btn:focus-visible {
295
+ outline: 2px solid var(--accent-8);
296
+ outline-offset: 1px;
297
+ }
298
+
299
+ /* ── Topbar search trigger ─────────────────────────────────────────────── */
300
+
301
+ /* White search field on the amber bar — Radix space scale + radius-3. */
302
+ .topbar-search {
303
+ display: flex;
304
+ align-items: center;
305
+ gap: var(--space-2);
306
+ padding: 0 var(--space-2) 0 var(--space-4);
307
+ height: var(--space-7);
308
+ flex: 1;
309
+ max-width: calc(4 * var(--space-9) + var(--space-3));
310
+ border-radius: var(--radius-3);
311
+ border: none;
312
+ background: var(--shell-surface);
313
+ cursor: pointer;
314
+ outline: none;
315
+ flex-shrink: 1;
316
+ /* No floor. It used to be `calc(3 * var(--space-8))` (129.6px), which the
317
+ pill refused to shrink past — so on a 320px bar, once the hamburger,
318
+ breadcrumb and bell were placed, the pill was painted 93px on top of the
319
+ breadcrumb text. Below 500px it collapses to an icon instead (see the
320
+ media query below), so it needs no minimum here. */
321
+ min-width: 0;
322
+ }
323
+ .topbar-search:focus-visible {
324
+ outline: 2px solid var(--shell-ink-strong);
325
+ outline-offset: 2px;
326
+ }
327
+ .topbar-search-placeholder {
328
+ font-family: var(--font-poppins), sans-serif;
329
+ font-size: var(--font-size-2);
330
+ /* Fixed — the pill is pinned white regardless of appearance. #8b8d98 was the
331
+ cool gray's step 9, a background step, at 3.30:1; this is step 11 of the
332
+ same hue, 5.94:1. */
333
+ color: #60646c;
334
+ flex: 1;
335
+ text-align: left;
336
+ white-space: nowrap;
337
+ overflow: hidden;
338
+ /* Without this the label was cut mid-word — "Search for so". */
339
+ text-overflow: ellipsis;
340
+ }
341
+
342
+ /* Phone widths: there is no room for a search field beside a breadcrumb, so
343
+ the pill becomes a square icon button matching the bell. The button keeps
344
+ its accessible name, so nothing changes for a screen reader. */
345
+ @media (max-width: 499px) {
346
+ .topbar-search {
347
+ flex: 0 0 var(--space-7);
348
+ width: var(--space-7);
349
+ padding: 0;
350
+ justify-content: center;
351
+ }
352
+ .topbar-search-placeholder {
353
+ display: none;
354
+ }
355
+ }
356
+
357
+ /* Topbar breadcrumb parent segment — clickable back to the section landing
358
+ page; underline on hover signals it's a link, not just a label. */
359
+ .topbar-breadcrumb-link:hover {
360
+ text-decoration: underline;
361
+ }
362
+ .topbar-breadcrumb-link:focus-visible {
363
+ outline: 2px solid var(--shell-ink-strong);
364
+ outline-offset: 2px;
365
+ }
366
+
367
+ /* Bell (and future topbar actions): matches .topbar-search height and radius. */
368
+ .topbar-icon-circle {
369
+ display: flex;
370
+ align-items: center;
371
+ justify-content: center;
372
+ width: var(--space-7);
373
+ height: var(--space-7);
374
+ border-radius: var(--radius-3);
375
+ border: none;
376
+ background: var(--shell-surface);
377
+ color: var(--shell-ink-strong);
378
+ cursor: pointer;
379
+ flex-shrink: 0;
380
+ outline: none;
381
+ padding: 0;
382
+ }
383
+ .topbar-icon-circle:focus-visible {
384
+ outline: 2px solid var(--shell-ink-strong);
385
+ outline-offset: 2px;
386
+ }
387
+ .topbar-icon-circle:disabled {
388
+ opacity: 0.55;
389
+ cursor: not-allowed;
390
+ }
391
+ .topbar-icon-circle:disabled:hover {
392
+ background: var(--shell-surface);
393
+ }
394
+
395
+ /* ── Search result items ───────────────────────────────────────────────── */
396
+
397
+ .search-result-item {
398
+ transition: background 80ms ease;
399
+ font-family: var(--font-poppins), sans-serif;
400
+ outline: none;
401
+ }
402
+ .search-result-item:focus-visible {
403
+ outline: 2px solid var(--accent-8);
404
+ outline-offset: -2px;
405
+ }
406
+
407
+ /* Search overlay query field — body size matches Radix Text size 2 */
408
+ .search-overlay-input {
409
+ flex: 1;
410
+ border: none;
411
+ outline: none;
412
+ background: transparent;
413
+ font-family: var(--font-poppins);
414
+ font-size: var(--font-size-2);
415
+ color: var(--gray-12);
416
+ line-height: 1;
417
+ }
418
+
419
+ /* ── Sentry-style data density foundation ──────────────────────────────── */
420
+
421
+ /* Monospace for IDs, timestamps, version strings, error codes */
422
+ .data-mono {
423
+ font-family: ui-monospace, "SF Mono", "Cascadia Code", "Fira Code", monospace;
424
+ font-size: var(--font-size-1);
425
+ letter-spacing: 0;
426
+ font-variant-numeric: tabular-nums;
427
+ }
428
+
429
+ /* Dense data row hover (DataTableRow) */
430
+ .data-table-row:hover {
431
+ background: var(--gray-a2);
432
+ }
433
+
434
+ /* Caps eyebrow / section label, applied as `<Text className="ds-section-label">`
435
+ * for DataTableRow column headers. gray-11 (Radix accessible muted text) for
436
+ * legible low-contrast labels. */
437
+ .ds-section-label {
438
+ font-size: var(--font-size-1);
439
+ font-weight: 600;
440
+ line-height: 1.4;
441
+ letter-spacing: 0.07em;
442
+ text-transform: uppercase;
443
+ color: var(--gray-11);
444
+ /* letter-spacing visually shifts the first glyph right of the element's
445
+ * own left edge (browsers apply tracking before AND after each
446
+ * character, not just after) — table headers using this class then sit
447
+ * out of alignment with plain-text cells below them in the same grid
448
+ * column. Pull it back by the same amount so the glyph's visible left
449
+ * edge lines up with the box edge, matching sibling cells. */
450
+ display: inline-block;
451
+ margin-inline-start: calc(-0.07em);
452
+ }
453
+ /* size="2": matches Radix Text size-1 (scale-aware) for the larger calendar
454
+ * eyebrows, so swapping them in changes nothing but the source of the styles. */
455
+ .ds-section-label[data-size="2"] {
456
+ font-size: var(--font-size-1);
457
+ }
458
+
459
+ /* Interactive data row (DataTableRow's onActivate prop): native keyboard
460
+ * focus affordance, amber ring to match the rest of the app's focus treatment. */
461
+ .data-table-row--interactive {
462
+ cursor: pointer;
463
+ outline: none;
464
+ -webkit-tap-highlight-color: transparent;
465
+ }
466
+ .data-table-row--interactive:focus-visible {
467
+ outline: 2px solid var(--accent-8);
468
+ outline-offset: -2px;
469
+ }
470
+ .data-table-row--selected {
471
+ background: var(--gray-a3);
472
+ }
473
+
474
+ /* ── MetricCard ──────────────────────────────────────────────────────────── */
475
+
476
+ .metric-card-value {
477
+ font-family: var(--font-poppins);
478
+ font-size: var(--font-size-6);
479
+ font-weight: 500;
480
+ letter-spacing: -0.03em;
481
+ line-height: 1;
482
+ color: var(--gray-12);
483
+ margin: 0 0 var(--space-4);
484
+ }
485
+
486
+ .metric-card {
487
+ box-shadow: var(--shadow-card) !important;
488
+ transition: box-shadow 140ms ease, transform 110ms ease;
489
+ }
490
+ .metric-card:hover {
491
+ box-shadow:
492
+ 0 2px 4px rgba(0, 0, 0, 0.05),
493
+ 0 4px 14px rgba(0, 0, 0, 0.06) !important;
494
+ transform: translateY(-1px);
495
+ }
496
+
497
+
498
+ /* ── Component classes the library's own components emit ─────────────────── *
499
+ *
500
+ * Everything below is rendered by a component in this package, so the rules
501
+ * belong in the package too. They were absent, which left each consumer to
502
+ * rediscover the gap and hand-port a copy — the calendar app had already done
503
+ * so for the provider rows and the add-account tile, while the three calendar
504
+ * legend/picker rules were simply unstyled everywhere.
505
+ *
506
+ * `src/components/emitted-classes.test.ts` fails if a component starts emitting
507
+ * a class that has no rule here.
508
+ */
509
+
510
+ /* Provider choice rows (ConnectProviderList, ConnectProviderRows) */
511
+ .provider-choice {
512
+ display: flex;
513
+ align-items: center;
514
+ gap: var(--space-4);
515
+ width: 100%;
516
+ min-width: 0;
517
+ padding: var(--space-4);
518
+ background: var(--color-panel-solid);
519
+ border: 1px solid var(--gray-5);
520
+ border-radius: var(--radius-3);
521
+ cursor: pointer;
522
+ text-align: left;
523
+ font-family: var(--font-poppins), sans-serif;
524
+ color: inherit;
525
+ outline: none;
526
+ -webkit-tap-highlight-color: transparent;
527
+ transition: border-color 80ms ease, background 80ms ease, box-shadow 80ms ease;
528
+ }
529
+
530
+ .provider-choice:hover:not(:disabled) {
531
+ border-color: var(--accent-8);
532
+ background: var(--accent-2);
533
+ }
534
+
535
+ .provider-choice:active:not(:disabled) {
536
+ border-color: var(--accent-9);
537
+ background: var(--accent-3);
538
+ }
539
+
540
+ .provider-choice:focus-visible {
541
+ outline: 2px solid var(--accent-8);
542
+ outline-offset: 2px;
543
+ }
544
+
545
+ .provider-choice[data-connecting="true"] {
546
+ border-color: var(--accent-8);
547
+ background: var(--accent-2);
548
+ cursor: progress;
549
+ }
550
+
551
+ .provider-choice:disabled:not([data-connecting="true"]) {
552
+ opacity: 0.5;
553
+ cursor: not-allowed;
554
+ }
555
+
556
+ .provider-choice__copy {
557
+ flex: 1 1 0;
558
+ min-width: 0;
559
+ overflow: hidden;
560
+ }
561
+ .provider-choice__chevron {
562
+ flex: 0 0 auto;
563
+ display: inline-flex;
564
+ align-items: center;
565
+ justify-content: center;
566
+ width: var(--space-5);
567
+ height: var(--space-5);
568
+ margin-left: var(--space-2);
569
+ color: var(--gray-9);
570
+ }
571
+
572
+ /* Add-account tile (AddAccountMenu's tile variant; calendar-fe's "Add Calendar")
573
+ *
574
+ * A grid tile, not a button: it sits in the card grid as the last cell, so its
575
+ * height is the card height (layout.connectedAccountCardH, 54px at 90%), not a
576
+ * button role's 29px. Squashing it to button height would leave a short cell in
577
+ * a row of cards and hide the page's onboarding action.
578
+ *
579
+ * It used to be `height: 100%`, which gave it no height of its own — each
580
+ * consumer had to wrap it in a box sized to the cards, and a wrapper of any
581
+ * other height produced a tile that did not line up with its row. The tile now
582
+ * owns the card height, and AddAccountMenu.test.tsx fails if the two drift. */
583
+ .connect-account-tile {
584
+ display: flex;
585
+ align-items: center;
586
+ justify-content: center;
587
+ gap: var(--space-2);
588
+ box-sizing: border-box;
589
+ width: 100%;
590
+ height: calc(var(--space-9) - var(--space-1));
591
+ padding: var(--space-3);
592
+ background: transparent;
593
+ border: 1px dashed var(--gray-7);
594
+ border-radius: var(--radius-3);
595
+ color: var(--gray-10);
596
+ font-family: var(--font-poppins), sans-serif;
597
+ font-size: var(--font-size-2);
598
+ font-weight: var(--font-weight-medium);
599
+ cursor: pointer;
600
+ outline: none;
601
+ -webkit-tap-highlight-color: transparent;
602
+ transition: border-color 80ms ease, background 80ms ease, color 80ms ease;
603
+ }
604
+
605
+ /* Guarded on :not(:disabled), and paired with a :disabled rule — the tile is a
606
+ * <button> the calendar app disables while a connect popup is already open, and
607
+ * the reference's bare :hover gave a dead control full hover feedback. */
608
+ .connect-account-tile:hover:not(:disabled) {
609
+ border-color: var(--accent-8);
610
+ background: var(--accent-2);
611
+ color: var(--gray-12);
612
+ }
613
+
614
+ .connect-account-tile:disabled {
615
+ opacity: 0.55;
616
+ cursor: default;
617
+ }
618
+
619
+ .connect-account-tile:focus-visible {
620
+ outline: 2px solid var(--accent-8);
621
+ outline-offset: 2px;
622
+ }
623
+
624
+ /* Card title button (AccountCard, ConnectedAccountCard). A clickable card can't
625
+ * itself be role="button" when its kebab sits inside it — axe nested-interactive.
626
+ * The card keeps the mouse click; the title is the keyboard control, styled to
627
+ * read as the plain text it replaces. */
628
+ .card-title-button {
629
+ display: inline-flex;
630
+ align-items: center;
631
+ min-width: 0;
632
+ max-width: 100%;
633
+ min-height: var(--space-6);
634
+ padding: 0;
635
+ background: none;
636
+ border: none;
637
+ font: inherit;
638
+ color: inherit;
639
+ text-align: left;
640
+ cursor: pointer;
641
+ outline: none;
642
+ }
643
+
644
+ .card-title-button:focus-visible {
645
+ outline: 2px solid var(--accent-8);
646
+ outline-offset: 2px;
647
+ border-radius: var(--radius-1);
648
+ }
649
+
650
+ /* "Or" divider (AuthCard consumers) */
651
+ .auth-divider {
652
+ display: flex;
653
+ align-items: center;
654
+ gap: var(--space-3);
655
+ margin: var(--space-5) 0;
656
+ }
657
+
658
+ .auth-divider::before,
659
+ .auth-divider::after {
660
+ content: "";
661
+ flex: 1;
662
+ height: 1px;
663
+ background: var(--gray-6); /* same hairline as TextField / social-login button borders on the white card */
664
+ }
665
+
666
+ /* Auth CTA geometry (AuthCard consumers) */
667
+ .btn-auth-cta {
668
+ width: 100%;
669
+ border-radius: var(--radius-3) !important;
670
+ font-family: var(--font-poppins), sans-serif !important;
671
+ font-size: var(--font-size-3) !important;
672
+ font-weight: 600 !important;
673
+ }
674
+
675
+ /* Large auth field label (AuthCard consumers) */
676
+ .field-label-lg {
677
+ display: block;
678
+ font-family: var(--font-poppins), sans-serif;
679
+ font-size: var(--font-size-2);
680
+ font-weight: 600;
681
+ color: var(--ink-strong);
682
+ letter-spacing: 0em;
683
+ margin-bottom: var(--space-2);
684
+ cursor: default;
685
+ user-select: none;
686
+ }
687
+
688
+ /* Merged-view legend rows (CalendarMergedView) */
689
+ .calendar-legend-account-row:hover,
690
+ .calendar-legend-row:hover {
691
+ background: var(--gray-a3);
692
+ }
693
+
694
+ /* Calendar picker rows (ImportCalendarsDialog) */
695
+ .calendar-picker-row {
696
+ border: 1px solid var(--gray-5);
697
+ border-radius: var(--radius-3);
698
+ background: var(--color-panel-solid);
699
+ transition: border-color 80ms ease, background 80ms ease;
700
+ }
701
+
702
+ .calendar-picker-row:hover {
703
+ border-color: var(--accent-8);
704
+ background: var(--accent-2);
705
+ }
706
+
707
+ .calendar-picker-row:has(:focus-visible) {
708
+ outline: 2px solid var(--accent-8);
709
+ outline-offset: 2px;
710
+ }
711
+
712
+ .calendar-picker-row :focus-visible {
713
+ outline: none;
714
+ box-shadow: none;
715
+ }
716
+
717
+ .calendar-picker-row[data-selected="true"] {
718
+ border-color: var(--accent-8);
719
+ background: var(--accent-2);
720
+ }
721
+
722
+ .calendar-picker-row[data-disabled="true"] {
723
+ opacity: 0.5;
724
+ cursor: not-allowed;
725
+ }
726
+
727
+ .calendar-picker-row[data-disabled="true"]:hover {
728
+ border-color: var(--gray-5);
729
+ background: var(--color-panel-solid);
730
+ }
731
+
732
+ /* ── Wizard stepper dividers (WizardStepper) ───────────────────────────── */
733
+
734
+ /* Matched on role + label rather than a class: the component emits no class of
735
+ its own, and `emitted-classes.test.ts` pairs every className literal with a
736
+ rule here, so introducing one purely for a divider would mean carrying it in
737
+ both places for no gain.
738
+
739
+ The segments wrap to a column when they no longer fit three abreast, and a
740
+ right-hand rule on a full-width row reads as a stray mark — so below the
741
+ width where that happens the divider becomes a bottom rule. The breakpoint
742
+ approximates the wrap point: the stepper sits inside the page column, so the
743
+ exact width it wraps at depends on the shell around it. */
744
+ [role="list"][aria-label="Wizard progress"] > [role="listitem"]:not(:last-child) {
745
+ border-right: 1px solid var(--gray-5);
746
+ }
747
+
748
+ @media (max-width: 699px) {
749
+ [role="list"][aria-label="Wizard progress"] > [role="listitem"]:not(:last-child) {
750
+ border-right: none;
751
+ border-bottom: 1px solid var(--gray-5);
752
+ }
753
+ }
754
+
755
+ /* Phone widths: the breadcrumb section and its separator give way to the page
756
+ title.
757
+ Both carry flexShrink: 0, so at 375px the whole squeeze landed on the <h1> —
758
+ measured at 53px against a 67px title, ellipsised on every admin page while
759
+ "Admin Portal /" beside it stayed whole. The page title is the primary
760
+ orientation cue and the one thing the sidebar does not already show, so it is
761
+ the segment that should survive.
762
+ Same 499px breakpoint the search pill collapses at, so the row changes shape
763
+ once rather than twice. */
764
+ @media (max-width: 499px) {
765
+ .topbar-breadcrumb-link,
766
+ .topbar-breadcrumb-sep {
767
+ display: none;
768
+ }
769
+ }