@yss123/cage-admin 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +49 -2
  3. package/dist/bootstrap.d.ts +17 -0
  4. package/dist/bootstrap.js +43 -0
  5. package/dist/bootstrap.js.map +1 -0
  6. package/dist/catalog.d.ts +43 -0
  7. package/dist/catalog.js +91 -0
  8. package/dist/catalog.js.map +1 -0
  9. package/dist/domain/constraints.d.ts +9 -0
  10. package/dist/domain/constraints.js +61 -0
  11. package/dist/domain/constraints.js.map +1 -0
  12. package/dist/domain/index.d.ts +1 -0
  13. package/dist/domain/index.js +5 -0
  14. package/dist/domain/index.js.map +1 -0
  15. package/dist/domain/org.d.ts +387 -0
  16. package/dist/domain/org.js +238 -0
  17. package/dist/domain/org.js.map +1 -0
  18. package/dist/http.d.ts +4 -0
  19. package/dist/http.js +150 -0
  20. package/dist/http.js.map +1 -0
  21. package/dist/index.d.ts +6 -0
  22. package/dist/index.js +8 -0
  23. package/dist/index.js.map +1 -0
  24. package/package.json +49 -3
  25. package/ui/dist/app/shell.js +110 -0
  26. package/ui/dist/app.js +24 -0
  27. package/ui/dist/modules/audit/audit-page.js +95 -0
  28. package/ui/dist/modules/audit/index.js +1 -0
  29. package/ui/dist/modules/auth/index.js +1 -0
  30. package/ui/dist/modules/auth/login-page.js +30 -0
  31. package/ui/dist/modules/operations/index.js +2 -0
  32. package/ui/dist/modules/operations/operations-page.js +95 -0
  33. package/ui/dist/modules/operations/sync-page.js +48 -0
  34. package/ui/dist/modules/org/index.js +1 -0
  35. package/ui/dist/modules/org/org-page.js +213 -0
  36. package/ui/dist/modules/roles/index.js +1 -0
  37. package/ui/dist/modules/roles/roles-page.js +308 -0
  38. package/ui/dist/modules/tasks/index.js +1 -0
  39. package/ui/dist/modules/tasks/tasks-page.js +60 -0
  40. package/ui/dist/shared/api.js +84 -0
  41. package/ui/dist/shared/dom.js +103 -0
  42. package/ui/dist/shared/labels.js +55 -0
  43. package/ui/dist/shared/navigation.js +7 -0
  44. package/ui/dist/shared/types.js +1 -0
  45. package/ui/index.html +14 -0
  46. package/ui/style.css +1577 -0
package/ui/style.css ADDED
@@ -0,0 +1,1577 @@
1
+ /* 管理工作台采用同一套色彩与间距。仅为桌面排版,不引入远程字体或 UI 运行时。 */
2
+ :root {
3
+ color-scheme: light;
4
+ --canvas: #f4f7f5;
5
+ --surface: #fff;
6
+ --ink: #23352f;
7
+ --secondary: #63756c;
8
+ --muted: #79887f;
9
+ --line: #e4ebe6;
10
+ --line-strong: #cfdad3;
11
+ --accent: #23765a;
12
+ --accent-dark: #175d45;
13
+ --accent-soft: #edf6f0;
14
+ --red: #b74c43;
15
+ --amber: #a87a32;
16
+ --radius: 12px;
17
+ --sidebar: 220px;
18
+ font-family: 'Microsoft YaHei UI', 'PingFang SC', 'Noto Sans CJK SC', sans-serif;
19
+ font-size: 13px;
20
+ }
21
+ * {
22
+ box-sizing: border-box;
23
+ }
24
+ [hidden] {
25
+ display: none !important;
26
+ }
27
+ body {
28
+ min-width: 1080px;
29
+ margin: 0;
30
+ background: var(--canvas);
31
+ color: var(--ink);
32
+ line-height: 1.6;
33
+ }
34
+ button,
35
+ input,
36
+ select,
37
+ textarea {
38
+ font: inherit;
39
+ }
40
+ button,
41
+ a,
42
+ input,
43
+ select,
44
+ textarea {
45
+ -webkit-tap-highlight-color: transparent;
46
+ }
47
+ button {
48
+ display: inline-flex;
49
+ align-items: center;
50
+ justify-content: center;
51
+ gap: 7px;
52
+ min-height: 36px;
53
+ padding: 7px 14px;
54
+ border: 1px solid var(--line-strong);
55
+ border-radius: 7px;
56
+ background: white;
57
+ color: var(--ink);
58
+ cursor: pointer;
59
+ transition:
60
+ background 0.15s,
61
+ border-color 0.15s,
62
+ box-shadow 0.15s;
63
+ white-space: nowrap;
64
+ }
65
+ button:hover {
66
+ background: #f6f9f7;
67
+ border-color: #a9beb0;
68
+ }
69
+ button.primary {
70
+ background: var(--accent);
71
+ color: #fff;
72
+ border-color: var(--accent);
73
+ }
74
+ button.primary:hover {
75
+ background: var(--accent-dark);
76
+ border-color: var(--accent-dark);
77
+ }
78
+ button.danger {
79
+ color: var(--red);
80
+ }
81
+ button.danger:hover {
82
+ border-color: #dfb9b4;
83
+ background: #fff7f5;
84
+ }
85
+ button:disabled {
86
+ opacity: 0.48;
87
+ cursor: not-allowed;
88
+ }
89
+ button.small {
90
+ min-height: 30px;
91
+ padding: 4px 10px;
92
+ font-size: 12px;
93
+ }
94
+ button.icon-button {
95
+ width: 34px;
96
+ min-height: 34px;
97
+ padding: 8px;
98
+ background: transparent;
99
+ border-color: transparent;
100
+ color: var(--secondary);
101
+ }
102
+ button.icon-button:hover {
103
+ background: var(--accent-soft);
104
+ }
105
+ button.text-button {
106
+ min-height: 26px;
107
+ padding: 2px 4px;
108
+ background: none;
109
+ border-color: transparent;
110
+ color: var(--accent);
111
+ font-size: 12px;
112
+ }
113
+ button.text-button.danger {
114
+ color: var(--red);
115
+ }
116
+ button.text-button:hover {
117
+ text-decoration: underline;
118
+ }
119
+ :focus-visible {
120
+ outline: 3px solid #8ab9a0;
121
+ outline-offset: 3px;
122
+ }
123
+ input:not([type='checkbox']),
124
+ select,
125
+ textarea {
126
+ width: 100%;
127
+ min-width: 0;
128
+ min-height: 38px;
129
+ padding: 8px 11px;
130
+ border: 1px solid var(--line-strong);
131
+ border-radius: 7px;
132
+ background: #fff;
133
+ color: var(--ink);
134
+ transition:
135
+ border-color 0.15s,
136
+ box-shadow 0.15s;
137
+ }
138
+ input::placeholder,
139
+ textarea::placeholder {
140
+ color: #8a968f;
141
+ }
142
+ input:focus,
143
+ select:focus,
144
+ textarea:focus {
145
+ border-color: #71a88c;
146
+ outline: none;
147
+ box-shadow: 0 0 0 3px #23765a0d;
148
+ }
149
+ input:disabled,
150
+ select:disabled,
151
+ textarea:disabled {
152
+ background: #f5f7f5;
153
+ color: var(--secondary);
154
+ cursor: not-allowed;
155
+ }
156
+ input[type='checkbox'] {
157
+ width: 16px;
158
+ height: 16px;
159
+ flex-shrink: 0;
160
+ accent-color: var(--accent);
161
+ cursor: pointer;
162
+ }
163
+ textarea {
164
+ resize: vertical;
165
+ }
166
+ a {
167
+ color: var(--accent);
168
+ text-decoration: none;
169
+ }
170
+ h1,
171
+ h2,
172
+ h3,
173
+ p {
174
+ margin: 0;
175
+ }
176
+ h1 {
177
+ font-size: 26px;
178
+ line-height: 1.5;
179
+ font-weight: 650;
180
+ letter-spacing: 0.5px;
181
+ }
182
+ h2 {
183
+ font-size: 15px;
184
+ font-weight: 650;
185
+ }
186
+ h3 {
187
+ font-size: 14px;
188
+ font-weight: 600;
189
+ }
190
+ strong {
191
+ font-weight: 600;
192
+ }
193
+ small {
194
+ font-size: 11px;
195
+ }
196
+ code,
197
+ pre {
198
+ font-family: 'Cascadia Code', Consolas, monospace;
199
+ font-size: 11px;
200
+ }
201
+ code {
202
+ overflow-wrap: anywhere;
203
+ }
204
+ pre {
205
+ max-width: 520px;
206
+ max-height: 300px;
207
+ overflow: auto;
208
+ margin: 10px 0 0;
209
+ padding: 12px;
210
+ background: #f4f7f5;
211
+ border: 1px solid var(--line);
212
+ border-radius: 6px;
213
+ color: var(--secondary);
214
+ white-space: pre-wrap;
215
+ overflow-wrap: anywhere;
216
+ }
217
+ .icon {
218
+ width: 18px;
219
+ height: 18px;
220
+ flex-shrink: 0;
221
+ vertical-align: middle;
222
+ }
223
+ .muted {
224
+ color: var(--muted);
225
+ }
226
+ .eyebrow {
227
+ display: block;
228
+ margin-bottom: 4px;
229
+ font-size: 10px;
230
+ letter-spacing: 2px;
231
+ color: var(--muted);
232
+ }
233
+
234
+ /* 固定导航与独立内容滚动视觉,避免每个管理页面再创造一套页面骨架。 */
235
+ .app-sidebar {
236
+ position: fixed;
237
+ inset: 0 auto 0 0;
238
+ z-index: 10;
239
+ display: flex;
240
+ flex-direction: column;
241
+ width: var(--sidebar);
242
+ padding: 29px 17px 20px;
243
+ background: #fbfcfa;
244
+ border-right: 1px solid var(--line);
245
+ }
246
+ .brand {
247
+ display: flex;
248
+ align-items: center;
249
+ gap: 11px;
250
+ padding: 0 14px 30px;
251
+ color: var(--ink);
252
+ }
253
+ .brand > span:last-child {
254
+ font:
255
+ 600 26px/1 Bahnschrift,
256
+ sans-serif;
257
+ letter-spacing: -0.8px;
258
+ }
259
+ .brand small {
260
+ display: block;
261
+ margin-top: 8px;
262
+ font:
263
+ 10px/1.4 'Microsoft YaHei UI',
264
+ sans-serif;
265
+ letter-spacing: 3px;
266
+ color: var(--secondary);
267
+ }
268
+ .brand-mark {
269
+ display: inline-flex;
270
+ align-items: center;
271
+ justify-content: center;
272
+ width: 37px;
273
+ height: 40px;
274
+ border-radius: 10px;
275
+ background: var(--accent);
276
+ color: white;
277
+ box-shadow: inset 0 0 0 1px #ffffff20;
278
+ }
279
+ .brand-mark .icon {
280
+ width: 21px;
281
+ height: 21px;
282
+ stroke-width: 1.6;
283
+ }
284
+ .nav-group {
285
+ margin-top: 15px;
286
+ }
287
+ .nav-caption {
288
+ margin: 0 14px 10px;
289
+ color: #87938a;
290
+ font-size: 10px;
291
+ letter-spacing: 1px;
292
+ }
293
+ .nav-item {
294
+ display: flex;
295
+ align-items: center;
296
+ gap: 12px;
297
+ min-height: 45px;
298
+ margin: 4px 0;
299
+ padding: 10px 14px;
300
+ border-radius: 8px;
301
+ color: var(--secondary);
302
+ }
303
+ .nav-item:hover {
304
+ background: #f0f4ef;
305
+ }
306
+ .nav-item.active {
307
+ background: #e8f2eb;
308
+ color: var(--accent-dark);
309
+ font-weight: 600;
310
+ }
311
+ .nav-active-dot {
312
+ width: 5px;
313
+ height: 5px;
314
+ margin-left: auto;
315
+ border-radius: 100%;
316
+ background: var(--accent);
317
+ }
318
+ .sidebar-bottom {
319
+ margin-top: auto;
320
+ padding: 18px 11px 0;
321
+ }
322
+ .workspace-label {
323
+ display: flex;
324
+ align-items: center;
325
+ gap: 7px;
326
+ padding: 14px 0;
327
+ border-top: 1px solid var(--line);
328
+ font-size: 10px;
329
+ color: var(--muted);
330
+ }
331
+ .connection-dot {
332
+ width: 6px;
333
+ height: 6px;
334
+ border-radius: 50%;
335
+ background: #69a27d;
336
+ }
337
+ .back-to-business {
338
+ display: flex;
339
+ gap: 7px;
340
+ align-items: center;
341
+ font-size: 11px;
342
+ color: var(--secondary);
343
+ }
344
+ .back-to-business .icon {
345
+ width: 14px;
346
+ height: 14px;
347
+ }
348
+ .app-main {
349
+ min-height: 100vh;
350
+ margin-left: var(--sidebar);
351
+ }
352
+ .app-topbar {
353
+ position: sticky;
354
+ top: 0;
355
+ z-index: 8;
356
+ display: flex;
357
+ justify-content: space-between;
358
+ align-items: center;
359
+ height: 75px;
360
+ padding: 0 34px;
361
+ border-bottom: 1px solid var(--line);
362
+ background: #ffffffed;
363
+ backdrop-filter: blur(16px);
364
+ }
365
+ .breadcrumb {
366
+ display: flex;
367
+ align-items: center;
368
+ gap: 12px;
369
+ color: var(--muted);
370
+ font-size: 11px;
371
+ }
372
+ .breadcrumb .icon {
373
+ width: 10px;
374
+ height: 10px;
375
+ color: #adb8b0;
376
+ }
377
+ .breadcrumb strong {
378
+ color: var(--ink);
379
+ font-weight: 500;
380
+ }
381
+ .account {
382
+ display: flex;
383
+ align-items: center;
384
+ gap: 10px;
385
+ }
386
+ .avatar,
387
+ .person-avatar {
388
+ display: inline-flex;
389
+ align-items: center;
390
+ justify-content: center;
391
+ width: 32px;
392
+ height: 32px;
393
+ border-radius: 50%;
394
+ background: #e9efe9;
395
+ color: #49705c;
396
+ font-size: 12px;
397
+ }
398
+ .account-copy {
399
+ display: flex;
400
+ flex-direction: column;
401
+ min-width: 76px;
402
+ line-height: 1.7;
403
+ }
404
+ .account-copy strong {
405
+ font-size: 11px;
406
+ }
407
+ .account-copy small {
408
+ font-size: 9px;
409
+ color: var(--muted);
410
+ }
411
+ .page-container {
412
+ max-width: 1700px;
413
+ margin: 0 auto;
414
+ padding: 28px 34px 18px;
415
+ }
416
+ .page-heading {
417
+ display: flex;
418
+ align-items: center;
419
+ justify-content: space-between;
420
+ gap: 20px;
421
+ margin-bottom: 25px;
422
+ }
423
+ .page-heading p {
424
+ margin-top: 6px;
425
+ font-size: 12px;
426
+ color: var(--secondary);
427
+ }
428
+ .refresh-button {
429
+ color: var(--secondary);
430
+ font-size: 12px;
431
+ }
432
+ .refresh-button .icon {
433
+ width: 14px;
434
+ height: 14px;
435
+ }
436
+ .page-content {
437
+ animation: enter 0.25s ease-out;
438
+ }
439
+ .page-footer {
440
+ display: flex;
441
+ justify-content: space-between;
442
+ align-items: center;
443
+ margin-top: 28px;
444
+ padding-top: 18px;
445
+ border-top: 1px solid var(--line);
446
+ color: var(--muted);
447
+ font-size: 10px;
448
+ }
449
+ .page-footer > span:last-child {
450
+ display: inline-flex;
451
+ align-items: center;
452
+ gap: 5px;
453
+ }
454
+ .page-footer .icon {
455
+ width: 12px;
456
+ height: 12px;
457
+ }
458
+
459
+ /* 通用记录页面:低对比表头、可展开明细,中文名称始终比代码标识更醒目。 */
460
+ .panel {
461
+ min-width: 0;
462
+ margin-bottom: 20px;
463
+ overflow: hidden;
464
+ border: 1px solid var(--line);
465
+ border-radius: var(--radius);
466
+ background: var(--surface);
467
+ }
468
+ .panel-heading {
469
+ display: flex;
470
+ align-items: center;
471
+ justify-content: space-between;
472
+ gap: 16px;
473
+ padding: 21px 24px;
474
+ }
475
+ .panel-heading p {
476
+ margin-top: 5px;
477
+ font-size: 11px;
478
+ color: var(--secondary);
479
+ }
480
+ .table-wrap {
481
+ width: 100%;
482
+ overflow-x: auto;
483
+ }
484
+ table {
485
+ width: 100%;
486
+ border-collapse: collapse;
487
+ text-align: left;
488
+ }
489
+ th {
490
+ padding: 12px 22px;
491
+ border-block: 1px solid var(--line);
492
+ background: #f8faf8;
493
+ color: var(--secondary);
494
+ font-weight: 500;
495
+ font-size: 11px;
496
+ white-space: nowrap;
497
+ }
498
+ td {
499
+ padding: 16px 22px;
500
+ border-bottom: 1px solid #edf1ed;
501
+ vertical-align: middle;
502
+ font-size: 12px;
503
+ }
504
+ tbody tr:last-child td {
505
+ border-bottom: 0;
506
+ }
507
+ tbody tr:hover {
508
+ background: #fbfdfb;
509
+ }
510
+ td select {
511
+ min-width: 180px;
512
+ max-width: 260px;
513
+ font-size: 12px;
514
+ }
515
+ .table-empty {
516
+ height: 128px;
517
+ text-align: center;
518
+ color: var(--muted);
519
+ }
520
+ .stack-cell {
521
+ display: flex;
522
+ flex-direction: column;
523
+ gap: 4px;
524
+ min-width: 95px;
525
+ }
526
+ .stack-cell code,
527
+ .stack-cell small {
528
+ color: var(--muted);
529
+ }
530
+ .entity-cell,
531
+ .person-cell {
532
+ display: flex;
533
+ align-items: center;
534
+ gap: 11px;
535
+ }
536
+ .entity-icon {
537
+ display: flex;
538
+ color: #76927f;
539
+ }
540
+ .person-cell small {
541
+ display: block;
542
+ color: var(--muted);
543
+ }
544
+ .tag-list,
545
+ .row-actions,
546
+ .section-actions {
547
+ display: flex;
548
+ flex-wrap: wrap;
549
+ align-items: center;
550
+ gap: 7px;
551
+ }
552
+ .row-actions {
553
+ flex-wrap: nowrap;
554
+ }
555
+ .badge {
556
+ display: inline-flex;
557
+ align-items: center;
558
+ align-self: flex-start;
559
+ padding: 2px 8px;
560
+ border: 1px solid #e7ece7;
561
+ border-radius: 5px;
562
+ background: #f4f6f3;
563
+ color: #697970;
564
+ font-size: 10px;
565
+ line-height: 1.65;
566
+ white-space: nowrap;
567
+ }
568
+ .badge.green {
569
+ color: #338160;
570
+ background: #edf7f0;
571
+ border-color: #dceee2;
572
+ }
573
+ .badge.red {
574
+ color: var(--red);
575
+ background: #fdf1ee;
576
+ border-color: #f1dad6;
577
+ }
578
+ .badge.amber {
579
+ color: var(--amber);
580
+ background: #fcf6e9;
581
+ border-color: #f1e6cc;
582
+ }
583
+ .stats-row {
584
+ display: grid;
585
+ grid-template-columns: repeat(3, 1fr);
586
+ gap: 18px;
587
+ margin-bottom: 22px;
588
+ }
589
+ .stat-card {
590
+ display: flex;
591
+ align-items: center;
592
+ gap: 18px;
593
+ padding: 22px 24px;
594
+ border: 1px solid var(--line);
595
+ border-radius: var(--radius);
596
+ background: var(--surface);
597
+ }
598
+ .stat-icon {
599
+ display: flex;
600
+ align-items: center;
601
+ justify-content: center;
602
+ width: 44px;
603
+ height: 44px;
604
+ border-radius: 11px;
605
+ background: #f0f5ef;
606
+ color: #688e73;
607
+ }
608
+ .stat-card > div {
609
+ display: flex;
610
+ flex: 1;
611
+ align-items: center;
612
+ justify-content: space-between;
613
+ gap: 12px;
614
+ }
615
+ .stat-card > div > span {
616
+ font-size: 12px;
617
+ color: var(--secondary);
618
+ }
619
+ .stat-card strong {
620
+ font:
621
+ 500 30px/1 Bahnschrift,
622
+ sans-serif;
623
+ color: var(--ink);
624
+ }
625
+ .stat-card.attention .stat-icon {
626
+ background: #fcf4e6;
627
+ color: var(--amber);
628
+ }
629
+ .table-toolbar {
630
+ display: flex;
631
+ align-items: center;
632
+ justify-content: space-between;
633
+ gap: 16px;
634
+ padding: 0 24px 20px;
635
+ }
636
+ .table-toolbar .search-field {
637
+ max-width: 350px;
638
+ }
639
+ details summary {
640
+ cursor: pointer;
641
+ color: var(--accent);
642
+ font-size: 11px;
643
+ }
644
+ .failure-summary {
645
+ max-width: 320px;
646
+ color: var(--red);
647
+ }
648
+ .audit-filters,
649
+ .sync-filters {
650
+ display: grid;
651
+ grid-template-columns: repeat(3, minmax(0, 1fr));
652
+ align-items: end;
653
+ gap: 14px;
654
+ margin: 0 24px 24px;
655
+ padding: 18px;
656
+ border: 1px solid var(--line);
657
+ border-radius: 8px;
658
+ background: #fafcf9;
659
+ }
660
+ .audit-filters > button {
661
+ justify-self: end;
662
+ }
663
+ .sync-filters {
664
+ grid-template-columns: minmax(240px, 1fr) minmax(180px, 1fr) auto;
665
+ }
666
+ .pagination {
667
+ display: flex;
668
+ align-items: center;
669
+ justify-content: space-between;
670
+ padding: 16px 24px;
671
+ border-top: 1px solid var(--line);
672
+ font-size: 11px;
673
+ }
674
+
675
+ /* 角色页把“选谁”和“配什么”分开;权限按业务对象分组,保留稳定标识便于排障。 */
676
+ .role-workspace {
677
+ display: grid;
678
+ grid-template-columns: 244px minmax(0, 1fr);
679
+ gap: 22px;
680
+ align-items: start;
681
+ }
682
+ .role-sidebar {
683
+ position: sticky;
684
+ top: 99px;
685
+ padding: 21px 15px 16px;
686
+ border: 1px solid var(--line);
687
+ border-radius: var(--radius);
688
+ background: var(--surface);
689
+ }
690
+ .role-list-heading {
691
+ display: flex;
692
+ align-items: center;
693
+ justify-content: space-between;
694
+ padding: 0 5px;
695
+ }
696
+ .sidebar-description {
697
+ margin: 10px 5px 17px;
698
+ font-size: 11px;
699
+ line-height: 1.9;
700
+ }
701
+ .role-sidebar .search-field {
702
+ margin: 0 3px 15px;
703
+ }
704
+ .role-sidebar .search-field input {
705
+ font-size: 10px;
706
+ }
707
+ .role-list {
708
+ display: flex;
709
+ flex-direction: column;
710
+ gap: 5px;
711
+ max-height: calc(100vh - 360px);
712
+ min-height: 80px;
713
+ overflow-y: auto;
714
+ }
715
+ .role-item {
716
+ width: 100%;
717
+ min-height: 65px;
718
+ padding: 10px;
719
+ gap: 10px;
720
+ border-color: transparent;
721
+ background: transparent;
722
+ text-align: left;
723
+ white-space: normal;
724
+ }
725
+ .role-item.selected {
726
+ border-color: #d8e8dc;
727
+ background: #eef6ef;
728
+ }
729
+ .role-avatar {
730
+ display: flex;
731
+ align-items: center;
732
+ justify-content: center;
733
+ width: 31px;
734
+ height: 34px;
735
+ flex-shrink: 0;
736
+ border: 1px solid #e1e9e1;
737
+ border-radius: 7px;
738
+ background: #fafcf9;
739
+ color: #83968a;
740
+ }
741
+ .role-item.selected .role-avatar {
742
+ background: #fff;
743
+ color: var(--accent);
744
+ border-color: #d5e6d9;
745
+ }
746
+ .role-avatar .icon {
747
+ width: 16px;
748
+ height: 16px;
749
+ }
750
+ .role-item-copy {
751
+ flex: 1;
752
+ min-width: 0;
753
+ }
754
+ .role-item-copy strong {
755
+ display: block;
756
+ font-size: 12px;
757
+ }
758
+ .role-item-copy small {
759
+ display: block;
760
+ margin-top: 2px;
761
+ font:
762
+ 10px/1.5 'Cascadia Code',
763
+ Consolas,
764
+ monospace;
765
+ overflow-wrap: anywhere;
766
+ color: var(--muted);
767
+ }
768
+ .role-item-count {
769
+ font:
770
+ 12px/1 Bahnschrift,
771
+ sans-serif;
772
+ color: #829388;
773
+ }
774
+ .create-role {
775
+ width: 100%;
776
+ margin-top: 19px;
777
+ }
778
+ .list-empty {
779
+ padding: 25px 10px;
780
+ font-size: 11px;
781
+ text-align: center;
782
+ color: var(--muted);
783
+ }
784
+ .role-details {
785
+ min-width: 0;
786
+ }
787
+ .role-summary {
788
+ display: flex;
789
+ align-items: center;
790
+ justify-content: space-between;
791
+ gap: 15px;
792
+ padding: 26px;
793
+ border: 1px solid var(--line);
794
+ border-bottom: 0;
795
+ border-radius: var(--radius) var(--radius) 0 0;
796
+ background: white;
797
+ }
798
+ .role-title {
799
+ display: flex;
800
+ align-items: center;
801
+ gap: 15px;
802
+ min-width: 0;
803
+ }
804
+ .role-emblem {
805
+ display: flex;
806
+ align-items: center;
807
+ justify-content: center;
808
+ width: 48px;
809
+ height: 52px;
810
+ border-radius: 11px;
811
+ background: #edf5ed;
812
+ color: var(--accent);
813
+ }
814
+ .role-emblem .icon {
815
+ width: 25px;
816
+ height: 25px;
817
+ }
818
+ .title-line {
819
+ display: flex;
820
+ align-items: center;
821
+ gap: 10px;
822
+ }
823
+ .title-line h2 {
824
+ font-size: 20px;
825
+ }
826
+ .role-title p {
827
+ display: flex;
828
+ align-items: center;
829
+ gap: 8px;
830
+ margin-top: 5px;
831
+ color: var(--muted);
832
+ font-size: 10px;
833
+ }
834
+ .permission-total {
835
+ display: flex;
836
+ align-items: center;
837
+ gap: 8px;
838
+ padding-left: 23px;
839
+ border-left: 1px solid var(--line);
840
+ }
841
+ .permission-total strong {
842
+ font:
843
+ 500 28px/1 Bahnschrift,
844
+ sans-serif;
845
+ color: var(--accent);
846
+ }
847
+ .permission-total span {
848
+ font-size: 10px;
849
+ color: var(--secondary);
850
+ }
851
+ .role-tabs {
852
+ display: flex;
853
+ gap: 29px;
854
+ padding: 0 26px;
855
+ border: 1px solid var(--line);
856
+ border-top: 0;
857
+ border-radius: 0 0 var(--radius) var(--radius);
858
+ background: white;
859
+ }
860
+ .role-tabs button {
861
+ position: relative;
862
+ min-height: 51px;
863
+ padding: 12px 1px;
864
+ border: 0;
865
+ border-radius: 0;
866
+ background: none;
867
+ font-size: 12px;
868
+ color: var(--secondary);
869
+ }
870
+ .role-tabs .icon {
871
+ width: 15px;
872
+ height: 15px;
873
+ }
874
+ .role-tabs button.active {
875
+ color: var(--accent);
876
+ font-weight: 600;
877
+ }
878
+ .role-tabs button.active::after {
879
+ position: absolute;
880
+ bottom: 0;
881
+ inset-inline: 0;
882
+ height: 3px;
883
+ border-radius: 3px 3px 0 0;
884
+ background: var(--accent);
885
+ content: '';
886
+ }
887
+ .role-tab-body {
888
+ padding-top: 22px;
889
+ }
890
+ .permission-toolbar {
891
+ display: flex;
892
+ align-items: center;
893
+ justify-content: space-between;
894
+ gap: 12px;
895
+ }
896
+ .permission-toolbar .search-field {
897
+ max-width: 370px;
898
+ }
899
+ .search-field {
900
+ display: flex;
901
+ align-items: center;
902
+ gap: 8px;
903
+ flex: 1;
904
+ min-width: 0;
905
+ padding-left: 11px;
906
+ border: 1px solid var(--line-strong);
907
+ border-radius: 7px;
908
+ background: white;
909
+ color: #8a9a90;
910
+ }
911
+ .search-field .icon {
912
+ width: 15px;
913
+ height: 15px;
914
+ }
915
+ .search-field input {
916
+ min-height: 36px;
917
+ padding-left: 0;
918
+ border: 0;
919
+ background: transparent;
920
+ font-size: 11px;
921
+ box-shadow: none;
922
+ }
923
+ .search-field:focus-within {
924
+ border-color: #71a88c;
925
+ box-shadow: 0 0 0 3px #23765a0d;
926
+ }
927
+ .search-field input:focus {
928
+ box-shadow: none;
929
+ }
930
+ .segmented {
931
+ display: flex;
932
+ flex-shrink: 0;
933
+ padding: 3px;
934
+ gap: 2px;
935
+ border: 1px solid var(--line);
936
+ border-radius: 7px;
937
+ background: #eaf0eb;
938
+ }
939
+ .segmented button {
940
+ min-height: 29px;
941
+ padding: 4px 12px;
942
+ border: 0;
943
+ border-radius: 4px;
944
+ background: transparent;
945
+ font-size: 11px;
946
+ color: var(--secondary);
947
+ }
948
+ .segmented button.active {
949
+ background: #fff;
950
+ color: var(--accent-dark);
951
+ box-shadow: 0 1px 4px #243e2912;
952
+ }
953
+ .permission-help {
954
+ display: flex;
955
+ justify-content: space-between;
956
+ gap: 12px;
957
+ margin: 14px 0 17px;
958
+ font-size: 10px;
959
+ color: var(--secondary);
960
+ }
961
+ .permission-help > span:first-child {
962
+ display: flex;
963
+ gap: 5px;
964
+ align-items: center;
965
+ }
966
+ .permission-help .icon {
967
+ width: 13px;
968
+ height: 13px;
969
+ color: #94a59a;
970
+ }
971
+ .result-count {
972
+ white-space: nowrap;
973
+ }
974
+ .permission-grid {
975
+ display: grid;
976
+ grid-template-columns: repeat(2, minmax(0, 1fr));
977
+ align-items: start;
978
+ gap: 16px;
979
+ }
980
+ .permission-card {
981
+ min-width: 0;
982
+ overflow: hidden;
983
+ border: 1px solid var(--line);
984
+ border-radius: var(--radius);
985
+ background: var(--surface);
986
+ }
987
+ .permission-card-heading {
988
+ display: flex;
989
+ align-items: center;
990
+ gap: 11px;
991
+ padding: 17px 19px;
992
+ border-bottom: 1px solid var(--line);
993
+ background: #fcfdfb;
994
+ }
995
+ .group-icon {
996
+ display: flex;
997
+ justify-content: center;
998
+ align-items: center;
999
+ width: 33px;
1000
+ height: 35px;
1001
+ border: 1px solid #e1ebe1;
1002
+ border-radius: 7px;
1003
+ background: #f0f6ef;
1004
+ color: #6a9477;
1005
+ }
1006
+ .group-icon.system {
1007
+ background: #f4f1e9;
1008
+ color: #9b8e69;
1009
+ border-color: #ede7da;
1010
+ }
1011
+ .group-icon .icon {
1012
+ width: 17px;
1013
+ height: 17px;
1014
+ }
1015
+ .group-title {
1016
+ flex: 1;
1017
+ min-width: 0;
1018
+ }
1019
+ .group-title h3 {
1020
+ font-size: 13px;
1021
+ }
1022
+ .group-title code {
1023
+ color: var(--muted);
1024
+ font-size: 10px;
1025
+ }
1026
+ .group-progress {
1027
+ padding: 2px 7px;
1028
+ border: 1px solid #e6ece6;
1029
+ border-radius: 5px;
1030
+ font:
1031
+ 10px/1.6 'Cascadia Code',
1032
+ Consolas,
1033
+ monospace;
1034
+ color: var(--muted);
1035
+ }
1036
+ .permission-row {
1037
+ position: relative;
1038
+ display: flex;
1039
+ flex-wrap: wrap;
1040
+ align-items: center;
1041
+ gap: 6px;
1042
+ padding: 13px 19px;
1043
+ border-bottom: 1px solid #f0f3ef;
1044
+ }
1045
+ .permission-row:last-child {
1046
+ border-bottom: 0;
1047
+ }
1048
+ .permission-toggle {
1049
+ display: flex;
1050
+ align-items: center;
1051
+ flex: 1;
1052
+ gap: 11px;
1053
+ min-width: 0;
1054
+ cursor: pointer;
1055
+ }
1056
+ /* 保留真实 checkbox 的键盘与读屏语义,仅替换视觉外观。 */
1057
+ .permission-toggle > input {
1058
+ position: absolute;
1059
+ width: 1px;
1060
+ height: 1px;
1061
+ opacity: 0;
1062
+ }
1063
+ .checkbox-visual {
1064
+ display: inline-flex;
1065
+ align-items: center;
1066
+ justify-content: center;
1067
+ width: 16px;
1068
+ height: 16px;
1069
+ flex-shrink: 0;
1070
+ border: 1px solid #cdd9cf;
1071
+ border-radius: 4px;
1072
+ background: #fff;
1073
+ transition: background 0.12s;
1074
+ }
1075
+ .checkbox-visual .icon {
1076
+ width: 12px;
1077
+ height: 12px;
1078
+ stroke-width: 2.5;
1079
+ opacity: 0;
1080
+ }
1081
+ .permission-toggle > input:checked + .checkbox-visual {
1082
+ border-color: var(--accent);
1083
+ background: var(--accent);
1084
+ color: white;
1085
+ }
1086
+ .permission-toggle > input:checked + .checkbox-visual .icon {
1087
+ opacity: 1;
1088
+ }
1089
+ .permission-toggle > input:focus-visible + .checkbox-visual {
1090
+ outline: 3px solid #8ab9a0;
1091
+ outline-offset: 3px;
1092
+ }
1093
+ .permission-toggle > input:disabled + .checkbox-visual {
1094
+ opacity: 0.45;
1095
+ }
1096
+ .permission-toggle:has(input:disabled) {
1097
+ cursor: not-allowed;
1098
+ }
1099
+ .permission-copy {
1100
+ display: flex;
1101
+ flex-direction: column;
1102
+ gap: 1px;
1103
+ min-width: 0;
1104
+ }
1105
+ .permission-name {
1106
+ font-size: 12px;
1107
+ color: var(--ink);
1108
+ }
1109
+ .permission-copy code {
1110
+ font-size: 10px;
1111
+ color: #88968d;
1112
+ }
1113
+ .invocation-badge {
1114
+ padding: 1px 5px;
1115
+ border-radius: 4px;
1116
+ background: #f3f4ef;
1117
+ font-size: 9px;
1118
+ color: #8c947c;
1119
+ }
1120
+ .limit-form {
1121
+ display: flex;
1122
+ align-items: center;
1123
+ gap: 7px;
1124
+ width: 100%;
1125
+ padding: 9px 0 1px 27px;
1126
+ font-size: 10px;
1127
+ color: var(--secondary);
1128
+ }
1129
+ .limit-form input {
1130
+ width: 110px;
1131
+ min-height: 29px;
1132
+ padding: 4px 7px;
1133
+ font-size: 11px;
1134
+ }
1135
+ .limit-form button {
1136
+ min-height: 29px;
1137
+ font-size: 10px;
1138
+ }
1139
+ .scope-grid {
1140
+ display: grid;
1141
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1142
+ gap: 16px;
1143
+ margin-top: 17px;
1144
+ align-items: start;
1145
+ }
1146
+ .scope-card {
1147
+ padding: 21px;
1148
+ border: 1px solid var(--line);
1149
+ border-radius: var(--radius);
1150
+ background: white;
1151
+ }
1152
+ .scope-card.default-scope {
1153
+ grid-column: 1 / -1;
1154
+ border-color: #cbded0;
1155
+ background: #fafdf9;
1156
+ }
1157
+ .scope-heading {
1158
+ display: flex;
1159
+ justify-content: space-between;
1160
+ gap: 10px;
1161
+ margin-bottom: 17px;
1162
+ }
1163
+ .scope-heading code {
1164
+ display: block;
1165
+ margin-top: 3px;
1166
+ color: var(--muted);
1167
+ font-size: 10px;
1168
+ }
1169
+ .scope-description {
1170
+ min-height: 35px;
1171
+ margin-top: 9px;
1172
+ color: var(--secondary);
1173
+ font-size: 11px;
1174
+ }
1175
+ .scope-footer {
1176
+ display: flex;
1177
+ justify-content: space-between;
1178
+ align-items: center;
1179
+ margin-top: 13px;
1180
+ padding-top: 13px;
1181
+ border-top: 1px solid var(--line);
1182
+ }
1183
+ .save-state {
1184
+ color: var(--muted);
1185
+ font-size: 10px;
1186
+ }
1187
+ .save-state.pending {
1188
+ color: var(--amber);
1189
+ }
1190
+ .department-options {
1191
+ display: grid;
1192
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1193
+ gap: 8px;
1194
+ max-height: 200px;
1195
+ overflow: auto;
1196
+ margin-top: 10px;
1197
+ padding: 12px;
1198
+ border: 1px solid var(--line);
1199
+ border-radius: 6px;
1200
+ }
1201
+ .department-options label {
1202
+ display: flex;
1203
+ align-items: center;
1204
+ gap: 7px;
1205
+ font-size: 11px;
1206
+ }
1207
+
1208
+ /* 表单和反馈共用清晰的保存状态;原生 dialog 提供焦点隔离与 Escape 关闭。 */
1209
+ .field {
1210
+ display: flex;
1211
+ flex-direction: column;
1212
+ gap: 7px;
1213
+ min-width: 0;
1214
+ }
1215
+ .field > span {
1216
+ font-size: 12px;
1217
+ font-weight: 500;
1218
+ }
1219
+ .field > small {
1220
+ color: var(--muted);
1221
+ line-height: 1.8;
1222
+ }
1223
+ .stack-form {
1224
+ display: flex;
1225
+ flex-direction: column;
1226
+ gap: 19px;
1227
+ }
1228
+ .form-actions {
1229
+ display: flex;
1230
+ justify-content: flex-end;
1231
+ margin-top: 5px;
1232
+ padding-top: 18px;
1233
+ border-top: 1px solid var(--line);
1234
+ }
1235
+ .modal {
1236
+ width: 470px;
1237
+ max-width: 80vw;
1238
+ max-height: 85vh;
1239
+ padding: 25px;
1240
+ border: 1px solid var(--line);
1241
+ border-radius: 14px;
1242
+ background: white;
1243
+ color: var(--ink);
1244
+ box-shadow: 0 25px 100px #1c382b26;
1245
+ }
1246
+ .modal::backdrop {
1247
+ background: #1a302c55;
1248
+ backdrop-filter: blur(4px);
1249
+ }
1250
+ .modal-heading {
1251
+ display: flex;
1252
+ justify-content: space-between;
1253
+ align-items: center;
1254
+ margin-bottom: 22px;
1255
+ }
1256
+ .modal-heading h2 {
1257
+ font-size: 18px;
1258
+ }
1259
+ .modal-heading .icon-button {
1260
+ font-size: 23px;
1261
+ font-weight: 300;
1262
+ }
1263
+ .choice-list {
1264
+ display: flex;
1265
+ flex-direction: column;
1266
+ gap: 8px;
1267
+ max-height: 330px;
1268
+ overflow: auto;
1269
+ }
1270
+ .choice-list label {
1271
+ display: flex;
1272
+ align-items: center;
1273
+ gap: 12px;
1274
+ padding: 13px;
1275
+ border: 1px solid var(--line);
1276
+ border-radius: 7px;
1277
+ cursor: pointer;
1278
+ }
1279
+ .choice-list small {
1280
+ display: block;
1281
+ color: var(--muted);
1282
+ font-family: 'Cascadia Code', Consolas, monospace;
1283
+ }
1284
+ .inline-info {
1285
+ display: flex;
1286
+ gap: 8px;
1287
+ align-items: flex-start;
1288
+ padding: 12px 14px;
1289
+ border: 1px solid #e0e9de;
1290
+ border-radius: 7px;
1291
+ background: #f4f8f1;
1292
+ color: #6e8069;
1293
+ font-size: 11px;
1294
+ line-height: 1.9;
1295
+ }
1296
+ .inline-info .icon {
1297
+ width: 15px;
1298
+ height: 15px;
1299
+ margin-top: 3px;
1300
+ }
1301
+ #notifications {
1302
+ position: fixed;
1303
+ top: 89px;
1304
+ right: 28px;
1305
+ z-index: 100;
1306
+ display: flex;
1307
+ flex-direction: column;
1308
+ gap: 9px;
1309
+ pointer-events: none;
1310
+ }
1311
+ .toast {
1312
+ display: flex;
1313
+ align-items: flex-start;
1314
+ gap: 9px;
1315
+ max-width: 420px;
1316
+ padding: 14px 18px;
1317
+ border: 1px solid #cde2d3;
1318
+ border-radius: 9px;
1319
+ background: #fafffa;
1320
+ box-shadow: 0 6px 24px #20382a12;
1321
+ color: var(--accent-dark);
1322
+ font-size: 12px;
1323
+ animation: enter 0.2s ease-out;
1324
+ }
1325
+ .toast .icon {
1326
+ width: 16px;
1327
+ height: 16px;
1328
+ margin-top: 2px;
1329
+ }
1330
+ .toast.error {
1331
+ border-color: #e9c9c3;
1332
+ background: #fff9f7;
1333
+ color: var(--red);
1334
+ }
1335
+ .empty-state {
1336
+ display: flex;
1337
+ flex-direction: column;
1338
+ align-items: center;
1339
+ justify-content: center;
1340
+ min-height: 280px;
1341
+ padding: 40px;
1342
+ text-align: center;
1343
+ }
1344
+ .empty-icon {
1345
+ display: flex;
1346
+ align-items: center;
1347
+ justify-content: center;
1348
+ width: 60px;
1349
+ height: 60px;
1350
+ margin-bottom: 19px;
1351
+ border: 1px solid #e1eadf;
1352
+ border-radius: 18px;
1353
+ background: #f3f7f0;
1354
+ color: #8aa080;
1355
+ }
1356
+ .empty-icon .icon {
1357
+ width: 27px;
1358
+ height: 27px;
1359
+ }
1360
+ .empty-state h3 {
1361
+ margin-bottom: 9px;
1362
+ font-size: 16px;
1363
+ }
1364
+ .empty-state p {
1365
+ max-width: 440px;
1366
+ color: var(--secondary);
1367
+ font-size: 12px;
1368
+ line-height: 1.9;
1369
+ }
1370
+ .permission-grid > .empty-state {
1371
+ grid-column: 1 / -1;
1372
+ border: 1px dashed var(--line-strong);
1373
+ border-radius: var(--radius);
1374
+ }
1375
+ .loading-panel {
1376
+ display: flex;
1377
+ align-items: center;
1378
+ justify-content: center;
1379
+ gap: 10px;
1380
+ min-height: 180px;
1381
+ color: var(--secondary);
1382
+ }
1383
+ .spinner {
1384
+ width: 17px;
1385
+ height: 17px;
1386
+ border: 2px solid #e0ebe1;
1387
+ border-top-color: var(--accent);
1388
+ border-radius: 50%;
1389
+ animation: rotate 0.7s linear infinite;
1390
+ }
1391
+ .error-panel {
1392
+ display: flex;
1393
+ gap: 14px;
1394
+ align-items: flex-start;
1395
+ padding: 28px;
1396
+ border: 1px solid #eadbd4;
1397
+ border-radius: var(--radius);
1398
+ background: #fffdfa;
1399
+ }
1400
+ .error-panel > .icon {
1401
+ margin-top: 3px;
1402
+ color: var(--amber);
1403
+ }
1404
+ .error-panel > div {
1405
+ flex: 1;
1406
+ }
1407
+ .error-panel p {
1408
+ margin-top: 8px;
1409
+ color: var(--secondary);
1410
+ font-size: 12px;
1411
+ }
1412
+ .task-actions {
1413
+ display: flex;
1414
+ flex-wrap: wrap;
1415
+ gap: 7px;
1416
+ min-width: 280px;
1417
+ }
1418
+ .task-actions input {
1419
+ width: 100%;
1420
+ }
1421
+
1422
+ /* 登录页保持同一品牌色,以较大的留白和标题区分日常数据页面。 */
1423
+ .login-layout {
1424
+ display: grid;
1425
+ grid-template-columns: 1.08fr 1fr;
1426
+ min-height: 100vh;
1427
+ background: #fbfcf9;
1428
+ }
1429
+ .login-story {
1430
+ position: relative;
1431
+ display: flex;
1432
+ flex-direction: column;
1433
+ justify-content: space-between;
1434
+ overflow: hidden;
1435
+ padding: 48px 66px 38px;
1436
+ background: #214a3b;
1437
+ color: #fff;
1438
+ }
1439
+ .login-story::after {
1440
+ position: absolute;
1441
+ width: 660px;
1442
+ height: 660px;
1443
+ right: -380px;
1444
+ bottom: -290px;
1445
+ border: 1px solid #abc8a933;
1446
+ border-radius: 50%;
1447
+ box-shadow:
1448
+ 0 0 0 60px #abc8a90a,
1449
+ 0 0 0 120px #abc8a90a,
1450
+ 0 0 0 180px #abc8a909;
1451
+ content: '';
1452
+ pointer-events: none;
1453
+ }
1454
+ .login-story > * {
1455
+ position: relative;
1456
+ z-index: 1;
1457
+ }
1458
+ .login-brand {
1459
+ display: flex;
1460
+ align-items: center;
1461
+ gap: 13px;
1462
+ font:
1463
+ 500 31px/1 Bahnschrift,
1464
+ sans-serif;
1465
+ letter-spacing: -0.7px;
1466
+ }
1467
+ .login-brand .brand-mark {
1468
+ background: #ffffff12;
1469
+ border: 1px solid #ffffff30;
1470
+ }
1471
+ .login-story .eyebrow {
1472
+ margin-bottom: 19px;
1473
+ color: #a8c6b4;
1474
+ font-size: 11px;
1475
+ }
1476
+ .login-story h1 {
1477
+ margin-bottom: 25px;
1478
+ font-size: 42px;
1479
+ line-height: 1.6;
1480
+ letter-spacing: 3px;
1481
+ font-weight: 500;
1482
+ }
1483
+ .login-story p {
1484
+ color: #bbd1c3;
1485
+ font-size: 13px;
1486
+ line-height: 2.1;
1487
+ }
1488
+ .login-story > small {
1489
+ color: #9fbdac;
1490
+ font-size: 10px;
1491
+ letter-spacing: 2px;
1492
+ }
1493
+ .login-panel {
1494
+ align-self: center;
1495
+ width: 100%;
1496
+ max-width: 490px;
1497
+ padding: 50px 55px;
1498
+ margin: 0 auto;
1499
+ }
1500
+ .login-panel > .eyebrow {
1501
+ margin-bottom: 12px;
1502
+ color: var(--accent);
1503
+ }
1504
+ .login-panel h2 {
1505
+ margin-bottom: 10px;
1506
+ font-size: 26px;
1507
+ }
1508
+ .login-panel > p {
1509
+ font-size: 12px;
1510
+ }
1511
+ .login-panel .stack-form {
1512
+ margin-top: 38px;
1513
+ }
1514
+ .login-panel .stack-form > button {
1515
+ justify-content: space-between;
1516
+ min-height: 44px;
1517
+ margin-top: 7px;
1518
+ }
1519
+ .sso-link {
1520
+ display: flex;
1521
+ justify-content: center;
1522
+ align-items: center;
1523
+ gap: 9px;
1524
+ margin-top: 22px;
1525
+ font-size: 11px;
1526
+ }
1527
+ .sso-link .icon {
1528
+ width: 13px;
1529
+ height: 13px;
1530
+ }
1531
+ .login-footnote {
1532
+ display: flex;
1533
+ align-items: flex-start;
1534
+ gap: 7px;
1535
+ margin-top: 46px;
1536
+ color: var(--muted);
1537
+ font-size: 10px !important;
1538
+ }
1539
+ .login-footnote .icon {
1540
+ width: 13px;
1541
+ height: 13px;
1542
+ margin-top: 2px;
1543
+ }
1544
+ .connection-state {
1545
+ display: flex;
1546
+ flex-direction: column;
1547
+ align-items: center;
1548
+ justify-content: center;
1549
+ gap: 20px;
1550
+ min-height: 100vh;
1551
+ }
1552
+ .connection-state p {
1553
+ color: var(--secondary);
1554
+ }
1555
+ @keyframes enter {
1556
+ from {
1557
+ opacity: 0;
1558
+ transform: translateY(5px);
1559
+ }
1560
+ to {
1561
+ opacity: 1;
1562
+ transform: translateY(0);
1563
+ }
1564
+ }
1565
+ @keyframes rotate {
1566
+ to {
1567
+ transform: rotate(360deg);
1568
+ }
1569
+ }
1570
+ @media (prefers-reduced-motion: reduce) {
1571
+ *,
1572
+ *::before,
1573
+ *::after {
1574
+ animation: none !important;
1575
+ transition: none !important;
1576
+ }
1577
+ }