botmux 2.61.0 → 2.63.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 (173) hide show
  1. package/README.en.md +1 -1
  2. package/README.md +1 -1
  3. package/dist/bot-registry.d.ts +15 -0
  4. package/dist/bot-registry.d.ts.map +1 -1
  5. package/dist/bot-registry.js +14 -0
  6. package/dist/bot-registry.js.map +1 -1
  7. package/dist/cli.d.ts.map +1 -1
  8. package/dist/cli.js +28 -31
  9. package/dist/cli.js.map +1 -1
  10. package/dist/config.d.ts +12 -0
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +12 -0
  13. package/dist/config.js.map +1 -1
  14. package/dist/core/command-handler.d.ts.map +1 -1
  15. package/dist/core/command-handler.js +14 -8
  16. package/dist/core/command-handler.js.map +1 -1
  17. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  18. package/dist/core/dashboard-ipc-server.js +2 -1
  19. package/dist/core/dashboard-ipc-server.js.map +1 -1
  20. package/dist/core/dashboard-rows.d.ts +13 -1
  21. package/dist/core/dashboard-rows.d.ts.map +1 -1
  22. package/dist/core/dashboard-rows.js +10 -4
  23. package/dist/core/dashboard-rows.js.map +1 -1
  24. package/dist/core/session-activity.d.ts +16 -0
  25. package/dist/core/session-activity.d.ts.map +1 -1
  26. package/dist/core/session-activity.js +35 -0
  27. package/dist/core/session-activity.js.map +1 -1
  28. package/dist/core/trigger-session.d.ts.map +1 -1
  29. package/dist/core/trigger-session.js +9 -10
  30. package/dist/core/trigger-session.js.map +1 -1
  31. package/dist/core/worker-pool.d.ts.map +1 -1
  32. package/dist/core/worker-pool.js +6 -0
  33. package/dist/core/worker-pool.js.map +1 -1
  34. package/dist/daemon.d.ts.map +1 -1
  35. package/dist/daemon.js +10 -2
  36. package/dist/daemon.js.map +1 -1
  37. package/dist/dashboard/auth.d.ts +6 -0
  38. package/dist/dashboard/auth.d.ts.map +1 -1
  39. package/dist/dashboard/auth.js +33 -12
  40. package/dist/dashboard/auth.js.map +1 -1
  41. package/dist/dashboard/bot-onboarding.d.ts.map +1 -1
  42. package/dist/dashboard/bot-onboarding.js +10 -13
  43. package/dist/dashboard/bot-onboarding.js.map +1 -1
  44. package/dist/dashboard/connector-api.d.ts.map +1 -1
  45. package/dist/dashboard/connector-api.js +22 -17
  46. package/dist/dashboard/connector-api.js.map +1 -1
  47. package/dist/dashboard/public-redact.d.ts +16 -0
  48. package/dist/dashboard/public-redact.d.ts.map +1 -0
  49. package/dist/dashboard/public-redact.js +62 -0
  50. package/dist/dashboard/public-redact.js.map +1 -0
  51. package/dist/dashboard/web/app.d.ts +1 -0
  52. package/dist/dashboard/web/app.d.ts.map +1 -1
  53. package/dist/dashboard/web/app.js +171 -25
  54. package/dist/dashboard/web/app.js.map +1 -1
  55. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  56. package/dist/dashboard/web/bot-defaults.js +136 -64
  57. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  58. package/dist/dashboard/web/connectors.d.ts.map +1 -1
  59. package/dist/dashboard/web/connectors.js +120 -25
  60. package/dist/dashboard/web/connectors.js.map +1 -1
  61. package/dist/dashboard/web/cyber-fx.d.ts +6 -0
  62. package/dist/dashboard/web/cyber-fx.d.ts.map +1 -0
  63. package/dist/dashboard/web/cyber-fx.js +289 -0
  64. package/dist/dashboard/web/cyber-fx.js.map +1 -0
  65. package/dist/dashboard/web/groups.d.ts.map +1 -1
  66. package/dist/dashboard/web/groups.js +6 -1
  67. package/dist/dashboard/web/groups.js.map +1 -1
  68. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  69. package/dist/dashboard/web/i18n.js +146 -8
  70. package/dist/dashboard/web/i18n.js.map +1 -1
  71. package/dist/dashboard/web/overview.d.ts.map +1 -1
  72. package/dist/dashboard/web/overview.js +206 -48
  73. package/dist/dashboard/web/overview.js.map +1 -1
  74. package/dist/dashboard/web/preferences.d.ts +16 -0
  75. package/dist/dashboard/web/preferences.d.ts.map +1 -1
  76. package/dist/dashboard/web/preferences.js +72 -1
  77. package/dist/dashboard/web/preferences.js.map +1 -1
  78. package/dist/dashboard/web/roles.js +3 -3
  79. package/dist/dashboard/web/roles.js.map +1 -1
  80. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  81. package/dist/dashboard/web/sessions.js +404 -64
  82. package/dist/dashboard/web/sessions.js.map +1 -1
  83. package/dist/dashboard/web/settings.d.ts +2 -0
  84. package/dist/dashboard/web/settings.d.ts.map +1 -0
  85. package/dist/dashboard/web/settings.js +135 -0
  86. package/dist/dashboard/web/settings.js.map +1 -0
  87. package/dist/dashboard/web/skin-intro.d.ts +4 -0
  88. package/dist/dashboard/web/skin-intro.d.ts.map +1 -0
  89. package/dist/dashboard/web/skin-intro.js +49 -0
  90. package/dist/dashboard/web/skin-intro.js.map +1 -0
  91. package/dist/dashboard/web/team-federation.js +4 -4
  92. package/dist/dashboard/web/team-federation.js.map +1 -1
  93. package/dist/dashboard/web/theme-menu.d.ts +3 -0
  94. package/dist/dashboard/web/theme-menu.d.ts.map +1 -0
  95. package/dist/dashboard/web/theme-menu.js +97 -0
  96. package/dist/dashboard/web/theme-menu.js.map +1 -0
  97. package/dist/dashboard/web/ui.d.ts +16 -2
  98. package/dist/dashboard/web/ui.d.ts.map +1 -1
  99. package/dist/dashboard/web/ui.js +137 -7
  100. package/dist/dashboard/web/ui.js.map +1 -1
  101. package/dist/dashboard/webhook-routes.d.ts +1 -5
  102. package/dist/dashboard/webhook-routes.d.ts.map +1 -1
  103. package/dist/dashboard/webhook-routes.js +127 -76
  104. package/dist/dashboard/webhook-routes.js.map +1 -1
  105. package/dist/dashboard-web/app.js +704 -512
  106. package/dist/dashboard-web/index.html +19 -14
  107. package/dist/dashboard-web/skins/bluearchive-hero.webp +0 -0
  108. package/dist/dashboard-web/skins/dragonball-goku.webp +0 -0
  109. package/dist/dashboard-web/skins/dragonball-wukong.webp +0 -0
  110. package/dist/dashboard-web/skins/fallout-vaultboy.webp +0 -0
  111. package/dist/dashboard-web/skins/genshin-breeze.webp +0 -0
  112. package/dist/dashboard-web/skins/ikun-hero.webp +0 -0
  113. package/dist/dashboard-web/skins/prts-priestess.webp +0 -0
  114. package/dist/dashboard-web/skins/zzz-hero.webp +0 -0
  115. package/dist/dashboard-web/skins/zzz-pattern.svg +25 -0
  116. package/dist/dashboard-web/style.css +2870 -16
  117. package/dist/dashboard.js +91 -31
  118. package/dist/dashboard.js.map +1 -1
  119. package/dist/global-config.d.ts +15 -0
  120. package/dist/global-config.d.ts.map +1 -1
  121. package/dist/global-config.js +51 -2
  122. package/dist/global-config.js.map +1 -1
  123. package/dist/im/lark/card-builder.d.ts.map +1 -1
  124. package/dist/im/lark/card-builder.js +17 -3
  125. package/dist/im/lark/card-builder.js.map +1 -1
  126. package/dist/im/lark/card-handler.d.ts.map +1 -1
  127. package/dist/im/lark/card-handler.js +5 -0
  128. package/dist/im/lark/card-handler.js.map +1 -1
  129. package/dist/im/lark/client.d.ts.map +1 -1
  130. package/dist/im/lark/client.js +7 -5
  131. package/dist/im/lark/client.js.map +1 -1
  132. package/dist/im/lark/event-dispatcher.d.ts +2 -1
  133. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  134. package/dist/im/lark/event-dispatcher.js +15 -9
  135. package/dist/im/lark/event-dispatcher.js.map +1 -1
  136. package/dist/im/lark/lark-hosts.d.ts +44 -0
  137. package/dist/im/lark/lark-hosts.d.ts.map +1 -0
  138. package/dist/im/lark/lark-hosts.js +49 -0
  139. package/dist/im/lark/lark-hosts.js.map +1 -0
  140. package/dist/services/connector-store.d.ts +2 -3
  141. package/dist/services/connector-store.d.ts.map +1 -1
  142. package/dist/services/connector-store.js.map +1 -1
  143. package/dist/services/trigger-types.d.ts +1 -0
  144. package/dist/services/trigger-types.d.ts.map +1 -1
  145. package/dist/services/trigger-types.js.map +1 -1
  146. package/dist/services/webhook-lifecycle-extractors.d.ts +4 -13
  147. package/dist/services/webhook-lifecycle-extractors.d.ts.map +1 -1
  148. package/dist/services/webhook-lifecycle-extractors.js +8 -23
  149. package/dist/services/webhook-lifecycle-extractors.js.map +1 -1
  150. package/dist/setup/verify-permissions.d.ts +2 -1
  151. package/dist/setup/verify-permissions.d.ts.map +1 -1
  152. package/dist/setup/verify-permissions.js +5 -8
  153. package/dist/setup/verify-permissions.js.map +1 -1
  154. package/dist/types.d.ts +1 -0
  155. package/dist/types.d.ts.map +1 -1
  156. package/dist/utils/lark-upload.d.ts +2 -1
  157. package/dist/utils/lark-upload.d.ts.map +1 -1
  158. package/dist/utils/lark-upload.js +11 -7
  159. package/dist/utils/lark-upload.js.map +1 -1
  160. package/dist/utils/screen-analyzer.d.ts +8 -0
  161. package/dist/utils/screen-analyzer.d.ts.map +1 -1
  162. package/dist/utils/screen-analyzer.js +32 -11
  163. package/dist/utils/screen-analyzer.js.map +1 -1
  164. package/dist/utils/user-token.d.ts +12 -4
  165. package/dist/utils/user-token.d.ts.map +1 -1
  166. package/dist/utils/user-token.js +64 -25
  167. package/dist/utils/user-token.js.map +1 -1
  168. package/dist/worker.js +4 -1
  169. package/dist/worker.js.map +1 -1
  170. package/dist/workflows/trigger-from-envelope.d.ts.map +1 -1
  171. package/dist/workflows/trigger-from-envelope.js +1 -0
  172. package/dist/workflows/trigger-from-envelope.js.map +1 -1
  173. package/package.json +2 -2
@@ -1,3 +1,5 @@
1
+ /* Base tokens — the default look (incl. the "数字员工工作台" redesign from
2
+ PR #123) is layered later in this file. Skins override these per [data-skin]. */
1
3
  :root {
2
4
  color-scheme: light;
3
5
  --bg: #f4f6f8;
@@ -59,6 +61,10 @@
59
61
  --shadow: 0 18px 44px rgba(0, 0, 0, 0.28);
60
62
  }
61
63
 
64
+ /* Named skins bring their own backdrop — hide PR #123's dark aurora overlay so
65
+ it doesn't tint them (it stays for the default skin's dark mode). */
66
+ :root:not([data-skin="default"]):not([data-skin=""]) .aurora { display: none; }
67
+
62
68
  * { box-sizing: border-box; }
63
69
 
64
70
  body {
@@ -133,7 +139,7 @@ a { color: inherit; }
133
139
  align-items: center;
134
140
  min-height: 36px;
135
141
  padding: 8px 10px;
136
- border-radius: 7px;
142
+ border-radius: var(--radius-sm);
137
143
  color: var(--muted);
138
144
  text-decoration: none;
139
145
  font-weight: 600;
@@ -234,6 +240,79 @@ a { color: inherit; }
234
240
  white-space: nowrap;
235
241
  }
236
242
 
243
+ /* Theme dropdown — custom listbox so each option can show a real Lucide line
244
+ icon (a native <select> can't render SVG). Sits with the other topbar chips. */
245
+ .theme-menu { position: relative; }
246
+
247
+ .theme-menu-btn {
248
+ display: inline-flex;
249
+ align-items: center;
250
+ gap: 7px;
251
+ min-height: 34px;
252
+ padding: 0 10px;
253
+ border: 1px solid var(--border);
254
+ border-radius: var(--radius);
255
+ background: var(--surface);
256
+ color: var(--fg);
257
+ font-size: 12px;
258
+ font-weight: 700;
259
+ cursor: pointer;
260
+ transition: border-color .15s ease;
261
+ }
262
+ .theme-menu-btn:hover { border-color: var(--muted); }
263
+
264
+ .tm-svg { display: block; width: 15px; height: 15px; }
265
+ .theme-menu-btn .tm-chev .tm-svg { width: 14px; height: 14px; opacity: 0.6; }
266
+ .tm-ic { display: inline-flex; }
267
+
268
+ .theme-menu-pop {
269
+ position: absolute;
270
+ right: 0;
271
+ top: calc(100% + 6px);
272
+ z-index: 50;
273
+ min-width: 192px;
274
+ max-height: min(70vh, 460px);
275
+ overflow: auto;
276
+ padding: 6px;
277
+ display: flex;
278
+ flex-direction: column;
279
+ background: var(--surface-raised);
280
+ border: 1px solid var(--border);
281
+ border-radius: 10px;
282
+ box-shadow: var(--modal-shadow);
283
+ }
284
+ .theme-menu-pop[hidden] { display: none; }
285
+
286
+ .tm-group {
287
+ padding: 7px 8px 3px;
288
+ font-size: 10px;
289
+ font-weight: 800;
290
+ letter-spacing: 0.06em;
291
+ text-transform: uppercase;
292
+ color: var(--faint);
293
+ }
294
+
295
+ .tm-item {
296
+ display: flex;
297
+ align-items: center;
298
+ justify-content: flex-start; /* override the base button{justify-content:center} */
299
+ gap: 9px;
300
+ width: 100%;
301
+ padding: 7px 9px;
302
+ border: 0;
303
+ border-radius: 7px;
304
+ background: transparent;
305
+ color: var(--fg);
306
+ font-size: 13px;
307
+ font-weight: 600;
308
+ text-align: left;
309
+ cursor: pointer;
310
+ }
311
+ .tm-item .tm-svg { color: var(--muted); flex: 0 0 auto; }
312
+ .tm-item:hover { background: var(--surface-muted); }
313
+ .tm-item.active { background: var(--accent-soft); color: var(--accent-strong); }
314
+ .tm-item.active .tm-svg { color: var(--accent-strong); }
315
+
237
316
  main {
238
317
  width: 100%;
239
318
  max-width: 1480px;
@@ -283,6 +362,13 @@ main {
283
362
  box-shadow: var(--shadow);
284
363
  }
285
364
 
365
+ /* Team pages: reserve a stable 2-line height for the heading lede so switching
366
+ between 我的团队 / 团队管理 (whose descriptions wrap to a different number of
367
+ lines) doesn't shift the sub-nav below it — no more flash on tab switch. */
368
+ .tf-lede {
369
+ min-height: 41px;
370
+ }
371
+
286
372
  .panel {
287
373
  overflow: hidden;
288
374
  }
@@ -308,6 +394,11 @@ main {
308
394
  font-size: 13px;
309
395
  }
310
396
 
397
+ /* Keep the "View all →" action on one line; let the title block shrink instead
398
+ (narrow cards like Next Runs were wrapping the button onto two lines). */
399
+ .panel-header > div { min-width: 0; }
400
+ .panel-header .btn-link { white-space: nowrap; flex: 0 0 auto; }
401
+
311
402
  .metric-grid {
312
403
  display: grid;
313
404
  grid-template-columns: repeat(5, minmax(140px, 1fr));
@@ -378,22 +469,81 @@ main {
378
469
  box-shadow: var(--shadow);
379
470
  }
380
471
 
381
- input,
382
- select,
383
472
  button,
384
473
  .btn-link {
385
474
  font: inherit;
386
475
  }
387
476
 
388
- input::placeholder {
477
+ /* ── Unified form controls ──────────────────────────────────────────────────
478
+ One consistent look for every text input, <select> and <textarea>: themed
479
+ surface, 1px border, var(--radius) corners, a custom dropdown chevron and a
480
+ soft accent focus ring. Native appearance is reset so WebKit search fields and
481
+ selects stop rendering their own mismatched pill/rounded shapes. */
482
+ input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]):not([type=submit]):not([type=button]),
483
+ select,
484
+ textarea {
485
+ min-height: 34px;
486
+ padding: 7px 11px;
487
+ border: 1px solid var(--border);
488
+ border-radius: var(--radius);
489
+ background-color: var(--surface-raised);
490
+ color: var(--fg);
491
+ font: inherit;
492
+ font-size: 13px;
493
+ line-height: 1.4;
494
+ -webkit-appearance: none;
495
+ -moz-appearance: none;
496
+ appearance: none;
497
+ transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
498
+ }
499
+
500
+ textarea {
501
+ min-height: 84px;
502
+ resize: vertical;
503
+ }
504
+
505
+ select {
506
+ padding-right: 30px;
507
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='none' stroke='%23808a99' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E");
508
+ background-repeat: no-repeat;
509
+ background-position: right 11px center;
510
+ cursor: pointer;
511
+ }
512
+
513
+ /* Multi-selects show a list, not a single value — no chevron, full padding. */
514
+ select[multiple] {
515
+ background-image: none;
516
+ padding-right: 8px;
517
+ }
518
+
519
+ input:not([type=checkbox]):not([type=radio]):hover:not(:disabled),
520
+ select:hover:not(:disabled),
521
+ textarea:hover:not(:disabled) {
522
+ border-color: var(--muted);
523
+ }
524
+
525
+ input:not([type=checkbox]):not([type=radio]):focus,
526
+ select:focus,
527
+ textarea:focus {
528
+ outline: none;
529
+ border-color: var(--accent);
530
+ box-shadow: 0 0 0 3px var(--accent-soft);
531
+ }
532
+
533
+ input::placeholder,
534
+ textarea::placeholder {
389
535
  color: var(--faint);
390
536
  }
391
537
 
538
+ input:disabled,
539
+ select:disabled,
540
+ textarea:disabled {
541
+ opacity: .55;
542
+ cursor: not-allowed;
543
+ }
544
+
392
545
  button:focus-visible,
393
546
  .btn-link:focus-visible,
394
- input:focus-visible,
395
- select:focus-visible,
396
- textarea:focus-visible,
397
547
  a:focus-visible {
398
548
  outline: 2px solid var(--accent);
399
549
  outline-offset: 2px;
@@ -408,7 +558,7 @@ a:focus-visible {
408
558
  min-height: 34px;
409
559
  padding: 0 10px;
410
560
  border: 1px solid var(--border);
411
- border-radius: 7px;
561
+ border-radius: var(--radius-sm);
412
562
  color: var(--fg);
413
563
  background: var(--surface-raised);
414
564
  }
@@ -432,6 +582,10 @@ a:focus-visible {
432
582
  width: 150px;
433
583
  }
434
584
 
585
+ .sessions-view-toggle {
586
+ flex: 0 0 auto;
587
+ }
588
+
435
589
  .filter-toggle {
436
590
  display: inline-flex;
437
591
  align-items: center;
@@ -478,11 +632,12 @@ a:focus-visible {
478
632
  min-height: 32px;
479
633
  padding: 0 7px;
480
634
  border: 1px solid var(--border);
481
- border-radius: 7px;
635
+ border-radius: var(--radius-sm);
482
636
  background: var(--surface-raised);
483
637
  color: var(--muted);
484
638
  font-size: 11px;
485
639
  font-weight: 700;
640
+ line-height: 1;
486
641
  }
487
642
 
488
643
  .filter-check:hover {
@@ -544,9 +699,10 @@ button,
544
699
  gap: 6px;
545
700
  padding: 0 11px;
546
701
  border: 1px solid var(--border);
547
- border-radius: 7px;
702
+ border-radius: var(--radius-sm);
548
703
  background: var(--surface-raised);
549
704
  color: var(--fg);
705
+ line-height: 1;
550
706
  text-decoration: none;
551
707
  cursor: pointer;
552
708
  -webkit-appearance: none;
@@ -581,11 +737,13 @@ button.primary {
581
737
  .status {
582
738
  display: inline-flex;
583
739
  align-items: center;
740
+ justify-content: center;
584
741
  min-height: 22px;
585
742
  padding: 0 8px;
586
- border-radius: 999px;
743
+ border-radius: var(--radius-sm);
587
744
  font-size: 11px;
588
745
  font-weight: 800;
746
+ line-height: 1;
589
747
  text-transform: uppercase;
590
748
  letter-spacing: 0;
591
749
  text-decoration: none;
@@ -634,6 +792,275 @@ button.primary {
634
792
  .status-closed { background: var(--danger-soft); color: var(--danger); }
635
793
  .status-analyzing { background: var(--warning-soft); color: var(--warning); }
636
794
  .status-starting { background: var(--success-soft); color: var(--success); }
795
+ .status-limited { background: var(--danger-soft); color: var(--danger); }
796
+
797
+ /* ── Sessions board — "control-room signal wall" ─────────────────────────────
798
+ Each column is a signal channel: a LED dot + uppercase channel label in the
799
+ header, and every card inherits the channel color through `--col-signal`.
800
+ The needs-you channel pulses; everything else stays calm. Terminal-native
801
+ typography (mono labels, tabular numerals) over generic SaaS chrome. */
802
+ .sessions-board {
803
+ display: grid;
804
+ grid-template-columns: repeat(4, minmax(220px, 1fr));
805
+ gap: 12px;
806
+ align-items: start;
807
+ /* faint blueprint grid behind the wall — atmosphere, not decoration */
808
+ background-image:
809
+ repeating-linear-gradient(0deg, color-mix(in srgb, var(--border-soft) 38%, transparent) 0 1px, transparent 1px 24px),
810
+ repeating-linear-gradient(90deg, color-mix(in srgb, var(--border-soft) 38%, transparent) 0 1px, transparent 1px 24px);
811
+ border-radius: var(--radius);
812
+ padding: 10px;
813
+ }
814
+
815
+ .sessions-board[hidden] {
816
+ display: none;
817
+ }
818
+
819
+ .session-board-column {
820
+ --col-signal: var(--border);
821
+ min-width: 0;
822
+ border: 1px solid var(--border-soft);
823
+ border-radius: var(--radius);
824
+ background: var(--surface);
825
+ box-shadow: var(--shadow);
826
+ overflow: hidden;
827
+ }
828
+
829
+ .session-board-column > header {
830
+ display: flex;
831
+ align-items: flex-start;
832
+ justify-content: space-between;
833
+ gap: 10px;
834
+ padding: 12px;
835
+ border-bottom: 1px solid var(--border-soft);
836
+ background:
837
+ linear-gradient(180deg, color-mix(in srgb, var(--col-signal) 7%, transparent), transparent 80%),
838
+ var(--surface-muted);
839
+ }
840
+
841
+ .session-board-column h2 {
842
+ margin: 0;
843
+ font-family: var(--mono);
844
+ font-size: 12px;
845
+ font-weight: 700;
846
+ line-height: 1.4;
847
+ letter-spacing: 0.12em;
848
+ text-transform: uppercase;
849
+ display: flex;
850
+ align-items: center;
851
+ gap: 8px;
852
+ }
853
+
854
+ /* channel LED */
855
+ .session-board-column h2::before {
856
+ content: '';
857
+ width: 8px;
858
+ height: 8px;
859
+ border-radius: 50%;
860
+ background: var(--col-signal);
861
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--col-signal) 18%, transparent);
862
+ flex: none;
863
+ }
864
+
865
+ .session-board-column p {
866
+ margin: 4px 0 0;
867
+ color: var(--muted);
868
+ font-size: 12px;
869
+ line-height: 1.35;
870
+ }
871
+
872
+ .session-board-count {
873
+ min-width: 28px;
874
+ min-height: 24px;
875
+ display: inline-flex;
876
+ align-items: center;
877
+ justify-content: center;
878
+ border-radius: var(--radius-sm);
879
+ background: color-mix(in srgb, var(--col-signal) 10%, var(--surface-raised));
880
+ border: 1px solid color-mix(in srgb, var(--col-signal) 35%, var(--border));
881
+ color: color-mix(in srgb, var(--col-signal) 75%, var(--fg));
882
+ font-family: var(--mono);
883
+ font-weight: 700;
884
+ line-height: 1;
885
+ font-variant-numeric: tabular-nums;
886
+ }
887
+
888
+ .session-board-list {
889
+ display: grid;
890
+ gap: 10px;
891
+ padding: 10px;
892
+ }
893
+
894
+ .session-board-empty {
895
+ min-height: 64px;
896
+ display: grid;
897
+ place-items: center;
898
+ color: var(--faint);
899
+ border: 1px dashed var(--border);
900
+ border-radius: var(--radius);
901
+ background: transparent;
902
+ font-family: var(--mono);
903
+ font-size: 12px;
904
+ letter-spacing: 0.04em;
905
+ }
906
+
907
+ .session-board-needs-you {
908
+ --col-signal: var(--danger);
909
+ background: linear-gradient(180deg, color-mix(in srgb, var(--danger) 4%, var(--surface)), var(--surface) 140px);
910
+ }
911
+
912
+ /* the one channel that's allowed to breathe */
913
+ .session-board-needs-you h2::before {
914
+ animation: board-led-pulse 1.6s ease-in-out infinite;
915
+ }
916
+
917
+ .session-board-starting {
918
+ --col-signal: var(--warning);
919
+ }
920
+
921
+ .session-board-working {
922
+ --col-signal: var(--success);
923
+ }
924
+
925
+ .session-board-idle {
926
+ --col-signal: var(--faint);
927
+ }
928
+
929
+ @keyframes board-led-pulse {
930
+ 0%, 100% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--col-signal) 18%, transparent); }
931
+ 50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--col-signal) 30%, transparent); }
932
+ }
933
+
934
+ .session-card {
935
+ display: grid;
936
+ gap: 10px;
937
+ padding: 11px;
938
+ border: 1px solid var(--border);
939
+ border-radius: var(--radius);
940
+ background: var(--surface-raised);
941
+ cursor: pointer;
942
+ position: relative;
943
+ overflow: hidden;
944
+ transition: border-color 120ms ease, transform 120ms ease, box-shadow 120ms ease;
945
+ }
946
+
947
+ /* Entry animation ONLY on the board's first paint (.board-enter set by JS).
948
+ * SSE-driven re-renders rebuild the DOM — replaying the staggered reveal on
949
+ * every status update made the whole page flash. */
950
+ .board-enter .session-board-list .session-card {
951
+ animation: board-card-in 280ms ease backwards;
952
+ }
953
+
954
+ /* stagger the first paint — one orchestrated reveal, then calm */
955
+ .board-enter .session-board-list .session-card:nth-child(1) { animation-delay: 30ms; }
956
+ .board-enter .session-board-list .session-card:nth-child(2) { animation-delay: 75ms; }
957
+ .board-enter .session-board-list .session-card:nth-child(3) { animation-delay: 120ms; }
958
+ .board-enter .session-board-list .session-card:nth-child(4) { animation-delay: 165ms; }
959
+ .board-enter .session-board-list .session-card:nth-child(n+5) { animation-delay: 210ms; }
960
+
961
+ @keyframes board-card-in {
962
+ from { opacity: 0; transform: translateY(4px); }
963
+ to { opacity: 1; transform: none; }
964
+ }
965
+
966
+ .session-card:hover {
967
+ border-color: color-mix(in srgb, var(--col-signal) 55%, var(--border));
968
+ transform: translateY(-1px);
969
+ box-shadow: 0 6px 18px color-mix(in srgb, var(--col-signal) 12%, rgba(15, 23, 42, 0.10));
970
+ }
971
+
972
+ /* whole-card selection (no checkbox) — accent ring + soft tint */
973
+ .session-card.selected {
974
+ border-color: var(--accent);
975
+ box-shadow: 0 0 0 1px var(--accent);
976
+ background: color-mix(in srgb, var(--accent) 5%, var(--surface-raised));
977
+ }
978
+
979
+ @media (prefers-reduced-motion: reduce) {
980
+ .session-card { animation: none; transition: none; }
981
+ .session-board-needs-you h2::before { animation: none; }
982
+ .session-card:hover { transform: none; }
983
+ }
984
+
985
+ .session-card-top {
986
+ display: grid;
987
+ grid-template-columns: minmax(0, 1fr) max-content;
988
+ align-items: start;
989
+ gap: 8px;
990
+ }
991
+
992
+ .session-card-title {
993
+ min-width: 0;
994
+ }
995
+
996
+ .session-card-title strong,
997
+ .session-card-title span {
998
+ display: block;
999
+ overflow: hidden;
1000
+ text-overflow: ellipsis;
1001
+ white-space: nowrap;
1002
+ }
1003
+
1004
+ .session-card-title strong {
1005
+ color: var(--fg);
1006
+ font-size: 13px;
1007
+ }
1008
+
1009
+ .session-card-title span {
1010
+ margin-top: 2px;
1011
+ color: var(--muted);
1012
+ font-size: 12px;
1013
+ }
1014
+
1015
+ .session-card-meta,
1016
+ .session-card-time,
1017
+ .session-card-actions {
1018
+ display: flex;
1019
+ align-items: center;
1020
+ gap: 7px;
1021
+ flex-wrap: wrap;
1022
+ min-width: 0;
1023
+ }
1024
+
1025
+ .session-card-meta {
1026
+ color: var(--muted);
1027
+ font-size: 12px;
1028
+ }
1029
+
1030
+ .session-card-time {
1031
+ justify-content: space-between;
1032
+ color: var(--muted);
1033
+ font-size: 12px;
1034
+ }
1035
+
1036
+ .session-signal {
1037
+ display: inline-flex;
1038
+ align-items: center;
1039
+ justify-content: center;
1040
+ min-height: 22px;
1041
+ padding: 0 8px;
1042
+ color: var(--danger);
1043
+ font-family: var(--mono);
1044
+ font-weight: 700;
1045
+ font-size: 11px;
1046
+ line-height: 1;
1047
+ letter-spacing: 0.04em;
1048
+ border-radius: var(--radius-sm);
1049
+ background: color-mix(in srgb, var(--danger) 9%, transparent);
1050
+ border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent);
1051
+ }
1052
+
1053
+ .session-card-actions {
1054
+ padding-top: 2px;
1055
+ justify-content: flex-end;
1056
+ }
1057
+
1058
+ .session-card-actions button,
1059
+ .session-card-actions .btn-link {
1060
+ min-height: 30px;
1061
+ padding: 0 9px;
1062
+ font-size: 12px;
1063
+ }
637
1064
 
638
1065
  dialog {
639
1066
  width: min(680px, calc(100vw - 32px));
@@ -853,7 +1280,7 @@ input.row-select,
853
1280
  width: 100%;
854
1281
  padding: 9px 10px;
855
1282
  border: 1px solid var(--border);
856
- border-radius: 8px;
1283
+ border-radius: var(--radius);
857
1284
  background: var(--surface);
858
1285
  color: var(--text);
859
1286
  font-size: 14px;
@@ -986,7 +1413,7 @@ input.row-select,
986
1413
  color: var(--fg);
987
1414
  background: var(--surface-raised);
988
1415
  border: 1px solid var(--border);
989
- border-radius: 7px;
1416
+ border-radius: var(--radius);
990
1417
  resize: vertical;
991
1418
  }
992
1419
 
@@ -1023,6 +1450,7 @@ input.row-select,
1023
1450
  }
1024
1451
  main {
1025
1452
  padding: 16px;
1453
+ overflow-x: hidden;
1026
1454
  }
1027
1455
  }
1028
1456
 
@@ -1047,6 +1475,7 @@ input.row-select,
1047
1475
  }
1048
1476
  .page-heading {
1049
1477
  flex-direction: column;
1478
+ align-items: stretch;
1050
1479
  }
1051
1480
  .filters {
1052
1481
  align-items: stretch;
@@ -1063,6 +1492,19 @@ input.row-select,
1063
1492
  .sessions-filters select {
1064
1493
  width: 100%;
1065
1494
  }
1495
+ .sessions-view-toggle {
1496
+ display: flex;
1497
+ width: 100%;
1498
+ max-width: calc(100vw - 32px);
1499
+ min-width: 0;
1500
+ }
1501
+ .sessions-view-toggle button {
1502
+ flex: 1 1 0;
1503
+ min-width: 0;
1504
+ }
1505
+ .sessions-board {
1506
+ grid-template-columns: 1fr;
1507
+ }
1066
1508
  .filter-check-group {
1067
1509
  grid-template-columns: repeat(2, minmax(0, 1fr));
1068
1510
  }
@@ -1271,7 +1713,7 @@ input.row-select,
1271
1713
  font-size: 13px;
1272
1714
  padding: 14px;
1273
1715
  border: 1px solid var(--border);
1274
- border-radius: 6px;
1716
+ border-radius: var(--radius);
1275
1717
  resize: vertical;
1276
1718
  line-height: 1.55;
1277
1719
  background: var(--surface-muted);
@@ -1421,7 +1863,7 @@ input.row-select,
1421
1863
  .wf-io-meta { margin-top: 0.35rem; color: var(--muted); font-size: 12px; overflow-wrap: anywhere; }
1422
1864
  .wf-approval-box { margin-top: 0.6rem; padding: 0.55rem; border: 1px solid var(--warning); border-radius: 4px; background: var(--warning-soft); }
1423
1865
  .wf-approval-box label span { display: block; margin-bottom: 0.25rem; color: var(--muted); font-size: 12px; }
1424
- .wf-approval-comment { width: 100%; resize: vertical; min-height: 48px; padding: 0.35rem 0.45rem; border: 1px solid var(--border); border-radius: 4px; font: inherit; background: var(--surface); color: var(--fg); }
1866
+ .wf-approval-comment { width: 100%; resize: vertical; min-height: 48px; padding: 0.35rem 0.45rem; border: 1px solid var(--border); border-radius: var(--radius); font: inherit; background: var(--surface); color: var(--fg); }
1425
1867
  .wf-approval-actions { display: flex; align-items: center; gap: 0.45rem; margin-top: 0.45rem; flex-wrap: wrap; }
1426
1868
  .wf-approval-status { margin: 0.5rem 0 0; }
1427
1869
  .wf-io-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 0.55rem; margin-top: 0.6rem; }
@@ -1444,7 +1886,7 @@ input.row-select,
1444
1886
  .catalog-head h2, .catalog-detail-head h2 { margin: 0 0 0.15rem; font-size: 18px; line-height: 1.25; }
1445
1887
  .catalog-run-form { display: grid; gap: 0.65rem; max-width: 900px; }
1446
1888
  .catalog-run-form label span, .catalog-param header { display: block; margin-bottom: 0.25rem; color: var(--muted); font-size: 12px; }
1447
- .catalog-run-form textarea, .catalog-run-form input[type=text] { width: 100%; padding: 0.4rem 0.5rem; border: 1px solid var(--border); border-radius: 4px; font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: var(--fg); background: var(--surface); }
1889
+ .catalog-run-form textarea, .catalog-run-form input[type=text] { width: 100%; padding: 0.4rem 0.5rem; border: 1px solid var(--border); border-radius: var(--radius); font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: var(--fg); background: var(--surface); }
1448
1890
  .catalog-run-form textarea { resize: vertical; min-height: 150px; }
1449
1891
  .catalog-chat-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.65rem; }
1450
1892
  .catalog-param-errors { background: var(--warning-soft); border-left: 3px solid var(--warning); padding: 0.45rem 0.65rem; border-radius: 3px; }
@@ -1476,3 +1918,2415 @@ input.row-select,
1476
1918
  .wf-approval-actions { flex-direction: column; align-items: stretch; }
1477
1919
  .wf-approval-actions button { width: 100%; }
1478
1920
  }
1921
+
1922
+ /* ════════════════════════════════════════════════════════════════════════════
1923
+ 数字员工工作台 — 统一视觉层(未来 / 科技 / 数字人)
1924
+ 设计语言:深空底 + 蓝青极光氛围、玻璃面板、胶囊按钮与标签、
1925
+ 每个 bot 一颗专属色相的"数字生命球"。强视觉只在品牌区(团队卡),
1926
+ 工作面(board / 表格 / 表单页)保持密度与可扫描性。
1927
+ 状态光语义:青=执行中 · 绿=就绪 · 琥珀=需要你 · 红=异常 · 蓝=启动中
1928
+ ══════════════════════════════════════════════════════════════════════════ */
1929
+
1930
+ :root {
1931
+ --info: #2779d8;
1932
+ --info-soft: rgba(39, 121, 216, 0.12);
1933
+ --accent: #0a8bbf;
1934
+ --accent-strong: #066d99;
1935
+ --accent-soft: rgba(10, 139, 191, 0.10);
1936
+ --radius: 14px;
1937
+ --radius-sm: 9px;
1938
+ --sidebar-width: 250px;
1939
+ }
1940
+
1941
+ :root[data-theme="dark"] {
1942
+ --bg: #07090f;
1943
+ --bg-soft: #0b0f17;
1944
+ --surface: #0e141d;
1945
+ --surface-raised: #151d29;
1946
+ --surface-muted: #0b1018;
1947
+ --fg: #eef2f8;
1948
+ --muted: #99a3b3;
1949
+ --faint: #5d6675;
1950
+ --border: #25303f;
1951
+ --border-soft: #1a2330;
1952
+ --accent: #5be3ff;
1953
+ --accent-strong: #8feeff;
1954
+ --accent-soft: rgba(91, 227, 255, 0.13);
1955
+ --success: #4ee69a;
1956
+ --success-soft: rgba(78, 230, 154, 0.13);
1957
+ --warning: #ffc24d;
1958
+ --warning-soft: rgba(255, 194, 77, 0.13);
1959
+ --danger: #ff6b81;
1960
+ --danger-soft: rgba(255, 107, 129, 0.13);
1961
+ --info: #4f8bff;
1962
+ --info-soft: rgba(79, 139, 255, 0.14);
1963
+ --on-accent: #051018;
1964
+ --on-danger: #1c0508;
1965
+ --shadow: 0 14px 40px rgba(0, 0, 0, 0.34);
1966
+ }
1967
+
1968
+ /* ── 极光底(dark 专属氛围;紫色只允许出现在这里)─────────────────────────── */
1969
+ .aurora { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
1970
+ .aurora i { position: absolute; border-radius: 50%; filter: blur(110px); opacity: 0.30; }
1971
+ .aurora .a1 { width: 520px; height: 420px; left: -20px; top: -200px; background: #1b4dff; opacity: 0.18; }
1972
+ .aurora .a2 { width: 520px; height: 440px; right: -90px; top: -80px; background: #00c2d8; opacity: 0.17; }
1973
+ .aurora .a3 { width: 620px; height: 420px; left: 38%; bottom: -280px; background: #6b3df0; opacity: 0.13; }
1974
+ :root[data-theme="light"] .aurora { display: none; }
1975
+
1976
+ .app-shell { position: relative; z-index: 1; }
1977
+
1978
+ /* ── 固定侧栏(标准后台布局:齐顶到底、直角、细分割线,背景与主区连续)─────── */
1979
+ .sidebar {
1980
+ margin: 0;
1981
+ top: 0;
1982
+ height: 100vh;
1983
+ border: 0;
1984
+ border-right: 1px solid var(--border);
1985
+ border-radius: 0;
1986
+ background: var(--surface);
1987
+ }
1988
+ :root[data-theme="dark"] .sidebar {
1989
+ background: rgba(255, 255, 255, 0.03);
1990
+ backdrop-filter: blur(20px);
1991
+ border-right-color: var(--border);
1992
+ }
1993
+
1994
+ /* 品牌:发光的数字生命球 */
1995
+ .brand-mark {
1996
+ width: 30px;
1997
+ height: 30px;
1998
+ border-radius: 50%;
1999
+ background: radial-gradient(circle at 32% 28%, #aef4ff, var(--accent) 38%, #4f8bff 68%, #0a1530 105%);
2000
+ box-shadow: 0 0 16px color-mix(in srgb, var(--accent) 50%, transparent), inset 0 0 8px rgba(255, 255, 255, 0.3);
2001
+ }
2002
+
2003
+ .sidebar-nav { gap: 2px; }
2004
+ .sidebar-nav a {
2005
+ gap: 11px;
2006
+ border-radius: 11px;
2007
+ font-size: 13px;
2008
+ }
2009
+ .sidebar-nav a svg {
2010
+ width: 16px;
2011
+ height: 16px;
2012
+ flex: none;
2013
+ fill: none;
2014
+ stroke: currentColor;
2015
+ stroke-width: 1.6;
2016
+ stroke-linecap: round;
2017
+ stroke-linejoin: round;
2018
+ }
2019
+ .sidebar-nav a.active {
2020
+ background: linear-gradient(120deg, color-mix(in srgb, var(--accent) 16%, transparent), color-mix(in srgb, var(--info) 10%, transparent));
2021
+ box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent);
2022
+ color: var(--accent-strong);
2023
+ }
2024
+
2025
+ /* ── 顶栏:透明玻璃,仅右侧操作区(品牌只在侧栏出现一次,顶栏不重复)──────── */
2026
+ .topbar {
2027
+ background: color-mix(in srgb, var(--bg) 70%, transparent);
2028
+ border-bottom: 1px solid var(--border);
2029
+ justify-content: flex-end;
2030
+ }
2031
+ .topbar-title { display: none; }
2032
+
2033
+ /* ── 全局 attention strip(任何页面常驻,待处理最高优先级)────────────────── */
2034
+ .attention-strip {
2035
+ display: flex;
2036
+ align-items: center;
2037
+ gap: 10px;
2038
+ margin: 14px 24px 0;
2039
+ padding: 9px 16px;
2040
+ border-radius: 12px;
2041
+ border: 1px solid color-mix(in srgb, var(--warning) 45%, transparent);
2042
+ background: linear-gradient(90deg, color-mix(in srgb, var(--warning) 14%, transparent), color-mix(in srgb, var(--warning) 4%, transparent) 70%);
2043
+ font-size: 13px;
2044
+ }
2045
+ .attention-strip[hidden] { display: none; }
2046
+ .attention-strip b { color: var(--warning); }
2047
+ .attention-strip-ic {
2048
+ width: 18px;
2049
+ height: 18px;
2050
+ border-radius: 50%;
2051
+ flex: none;
2052
+ display: grid;
2053
+ place-items: center;
2054
+ background: var(--warning);
2055
+ color: #2a1c00;
2056
+ font-size: 11px;
2057
+ font-weight: 800;
2058
+ animation: strip-pulse 2s ease-in-out infinite;
2059
+ }
2060
+ @keyframes strip-pulse {
2061
+ 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--warning) 40%, transparent); }
2062
+ 50% { box-shadow: 0 0 0 5px color-mix(in srgb, var(--warning) 18%, transparent); }
2063
+ }
2064
+ .attention-strip-longest {
2065
+ color: var(--muted);
2066
+ min-width: 0;
2067
+ overflow: hidden;
2068
+ white-space: nowrap;
2069
+ text-overflow: ellipsis;
2070
+ }
2071
+ .attention-strip-go {
2072
+ margin-left: auto;
2073
+ flex: none;
2074
+ padding: 5px 14px;
2075
+ border-radius: 999px;
2076
+ background: var(--warning);
2077
+ color: #2a1c00;
2078
+ font-size: 12px;
2079
+ font-weight: 700;
2080
+ text-decoration: none;
2081
+ }
2082
+ @media (prefers-reduced-motion: reduce) {
2083
+ .attention-strip-ic { animation: none; }
2084
+ }
2085
+
2086
+ /* ── 胶囊化:按钮 / 标签 / 分段控件 ──────────────────────────────────────── */
2087
+ button,
2088
+ .btn-link {
2089
+ border-radius: 999px;
2090
+ padding: 0 13px;
2091
+ }
2092
+ button.primary,
2093
+ .btn-link.primary {
2094
+ background: linear-gradient(120deg, var(--accent), var(--info));
2095
+ border-color: transparent;
2096
+ color: var(--on-accent);
2097
+ box-shadow: 0 4px 16px color-mix(in srgb, var(--accent) 28%, transparent);
2098
+ }
2099
+ button.contrast {
2100
+ background: transparent;
2101
+ border-color: color-mix(in srgb, var(--danger) 45%, transparent);
2102
+ color: var(--danger);
2103
+ }
2104
+ button.contrast:hover { background: var(--danger-soft); border-color: var(--danger); }
2105
+ .segmented { border-radius: 999px; }
2106
+ .segmented button { border-radius: 999px; }
2107
+ .segmented button.active {
2108
+ background: linear-gradient(120deg, var(--accent), var(--info));
2109
+ color: var(--on-accent);
2110
+ }
2111
+ .badge,
2112
+ .status {
2113
+ border-radius: 999px;
2114
+ text-transform: none;
2115
+ font-weight: 700;
2116
+ }
2117
+
2118
+ /* 状态光语义重排:青=执行中 · 蓝=启动 · 绿=就绪 · 琥珀/红=要人管 */
2119
+ .status-working,
2120
+ .status-active { background: var(--accent-soft); color: var(--accent); }
2121
+ .status-analyzing { background: var(--accent-soft); color: var(--accent); }
2122
+ .status-starting { background: var(--info-soft); color: var(--info); }
2123
+ .status-idle { background: var(--success-soft); color: var(--success); }
2124
+ .status-limited { background: var(--danger-soft); color: var(--danger); }
2125
+
2126
+ /* ── 玻璃面板(dark 下生效;light 保持实底)─────────────────────────────── */
2127
+ :root[data-theme="dark"] .panel,
2128
+ :root[data-theme="dark"] .metric-card,
2129
+ :root[data-theme="dark"] .bd-card,
2130
+ :root[data-theme="dark"] .filters {
2131
+ background: rgba(255, 255, 255, 0.04);
2132
+ backdrop-filter: blur(18px);
2133
+ border-color: rgba(255, 255, 255, 0.08);
2134
+ background-image: linear-gradient(165deg, rgba(255, 255, 255, 0.045), transparent 40%);
2135
+ }
2136
+
2137
+ /* ── 数字生命球(bot avatar,全站统一)──────────────────────────────────── */
2138
+ .orb-avatar {
2139
+ position: relative;
2140
+ width: 42px;
2141
+ height: 42px;
2142
+ border-radius: 50%;
2143
+ flex: none;
2144
+ background: radial-gradient(circle at 32% 28%, #d8fbff, var(--c1, #5be3ff) 38%, var(--c2, #4f8bff) 72%, #060d1c 105%);
2145
+ box-shadow: 0 0 16px color-mix(in srgb, var(--c1, #5be3ff) 32%, transparent), inset 0 0 9px rgba(255, 255, 255, 0.28);
2146
+ }
2147
+ .orb-avatar-sm {
2148
+ width: 24px;
2149
+ height: 24px;
2150
+ box-shadow: inset 0 0 5px rgba(255, 255, 255, 0.28);
2151
+ }
2152
+ .orb-dot {
2153
+ position: absolute;
2154
+ right: -1px;
2155
+ bottom: -1px;
2156
+ width: 12px;
2157
+ height: 12px;
2158
+ border-radius: 50%;
2159
+ border: 2px solid var(--bg);
2160
+ }
2161
+ .orb-dot-ok { background: var(--success); }
2162
+ .orb-dot-busy { background: var(--accent); }
2163
+ .orb-dot-warn { background: var(--warning); }
2164
+ .orb-dot-off { background: var(--faint); }
2165
+
2166
+ /* ── 工作台首页 ─────────────────────────────────────────────────────────── */
2167
+ .hero-pills { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
2168
+ .pill {
2169
+ display: inline-flex;
2170
+ align-items: center;
2171
+ gap: 7px;
2172
+ padding: 7px 14px;
2173
+ border-radius: 999px;
2174
+ background: var(--surface);
2175
+ border: 1px solid var(--border-soft);
2176
+ color: var(--muted);
2177
+ font-size: 12px;
2178
+ font-weight: 600;
2179
+ }
2180
+ :root[data-theme="dark"] .pill { background: rgba(255, 255, 255, 0.045); }
2181
+ .pill b { color: var(--fg); font-size: 14px; }
2182
+ .pill-hot { border-color: color-mix(in srgb, var(--warning) 45%, transparent); color: var(--warning); }
2183
+ .pill-hot b { color: var(--warning); }
2184
+
2185
+ .sect-head { display: flex; align-items: baseline; gap: 10px; margin-top: 2px; }
2186
+ .sect-head h2 { margin: 0; font-size: 15px; font-weight: 700; }
2187
+ .sect-head span { color: var(--faint); font-size: 12px; }
2188
+ .sect-head a { margin-left: auto; color: var(--accent); font-size: 12px; text-decoration: none; font-weight: 600; }
2189
+
2190
+ .team-grid {
2191
+ display: grid;
2192
+ grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
2193
+ gap: 13px;
2194
+ }
2195
+ .mate {
2196
+ display: flex;
2197
+ flex-direction: column;
2198
+ gap: 10px;
2199
+ padding: 16px 15px 13px;
2200
+ border-radius: 16px;
2201
+ border: 1px solid var(--border-soft);
2202
+ background: var(--surface);
2203
+ box-shadow: var(--shadow);
2204
+ }
2205
+ :root[data-theme="dark"] .mate {
2206
+ background: rgba(255, 255, 255, 0.045);
2207
+ backdrop-filter: blur(20px);
2208
+ border-color: rgba(255, 255, 255, 0.09);
2209
+ background-image: linear-gradient(165deg, rgba(255, 255, 255, 0.05), transparent 38%);
2210
+ }
2211
+ .mate-attn {
2212
+ border-color: color-mix(in srgb, var(--warning) 42%, transparent);
2213
+ box-shadow: 0 0 0 1px color-mix(in srgb, var(--warning) 18%, transparent), 0 10px 32px color-mix(in srgb, var(--warning) 9%, transparent);
2214
+ }
2215
+ .mate-off { opacity: 0.55; }
2216
+ .mate-top { display: flex; align-items: center; gap: 11px; }
2217
+ .mate-id { min-width: 0; }
2218
+ .mate-id b { display: block; font-size: 14px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
2219
+ .mate-role {
2220
+ display: inline-block;
2221
+ margin-top: 3px;
2222
+ font-size: 10.5px;
2223
+ font-weight: 600;
2224
+ color: var(--muted);
2225
+ background: var(--surface-muted);
2226
+ border: 1px solid var(--border-soft);
2227
+ padding: 1px 9px;
2228
+ border-radius: 999px;
2229
+ }
2230
+ :root[data-theme="dark"] .mate-role { background: rgba(255, 255, 255, 0.07); border-color: rgba(255, 255, 255, 0.1); }
2231
+ .mate-task {
2232
+ color: var(--muted);
2233
+ font-size: 12px;
2234
+ line-height: 1.5;
2235
+ min-height: 36px;
2236
+ display: -webkit-box;
2237
+ -webkit-line-clamp: 2;
2238
+ -webkit-box-orient: vertical;
2239
+ overflow: hidden;
2240
+ }
2241
+ .mate-task b { color: var(--fg); font-weight: 600; }
2242
+ .mate-foot {
2243
+ display: flex;
2244
+ align-items: center;
2245
+ justify-content: space-between;
2246
+ gap: 8px;
2247
+ font-size: 11px;
2248
+ color: var(--faint);
2249
+ }
2250
+ .tag {
2251
+ padding: 3px 10px;
2252
+ border-radius: 999px;
2253
+ font-size: 10.5px;
2254
+ font-weight: 700;
2255
+ white-space: nowrap;
2256
+ }
2257
+ .tag-run { color: var(--accent); background: var(--accent-soft); }
2258
+ .tag-ok { color: var(--success); background: var(--success-soft); }
2259
+ .tag-warn { color: var(--warning); background: var(--warning-soft); }
2260
+ .tag-off { color: var(--faint); background: var(--surface-muted); }
2261
+ :root[data-theme="dark"] .tag-off { background: rgba(255, 255, 255, 0.06); }
2262
+
2263
+ /* 需要你处理 — 队列卡 */
2264
+ .qgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: 12px; }
2265
+ .qcard {
2266
+ display: flex;
2267
+ align-items: center;
2268
+ gap: 12px;
2269
+ padding: 12px 15px;
2270
+ border-radius: 14px;
2271
+ border: 1px solid color-mix(in srgb, var(--warning) 38%, transparent);
2272
+ background: linear-gradient(120deg, color-mix(in srgb, var(--warning) 10%, transparent), transparent 60%), var(--surface);
2273
+ }
2274
+ :root[data-theme="dark"] .qcard {
2275
+ background: linear-gradient(120deg, color-mix(in srgb, var(--warning) 10%, transparent), transparent 60%), rgba(255, 255, 255, 0.04);
2276
+ backdrop-filter: blur(18px);
2277
+ }
2278
+ .qcard-empty {
2279
+ border-style: dashed;
2280
+ border-color: var(--border);
2281
+ background: transparent;
2282
+ color: var(--faint);
2283
+ font-size: 12px;
2284
+ justify-content: center;
2285
+ }
2286
+ .qcard-tx { min-width: 0; flex: 1; }
2287
+ .qcard-tx b { display: block; font-size: 12.5px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
2288
+ .qcard-tx span { color: var(--muted); font-size: 11.5px; }
2289
+ .qcard-go {
2290
+ flex: none;
2291
+ padding: 6px 14px;
2292
+ border-radius: 999px;
2293
+ background: var(--warning);
2294
+ color: #2a1c00;
2295
+ font-size: 12px;
2296
+ font-weight: 700;
2297
+ text-decoration: none;
2298
+ }
2299
+
2300
+ /* 下方两栏 */
2301
+ .overview-cols { display: grid; grid-template-columns: 1.7fr 1fr; gap: 14px; align-items: start; }
2302
+ .overview-side { display: grid; gap: 14px; }
2303
+ .sess-row {
2304
+ display: flex;
2305
+ align-items: center;
2306
+ gap: 12px;
2307
+ padding: 10px 18px;
2308
+ border-bottom: 1px solid var(--border-soft);
2309
+ }
2310
+ .sess-row:last-child { border-bottom: 0; }
2311
+ .sess-tx { min-width: 0; flex: 1; }
2312
+ .sess-tx b { display: block; font-size: 12.5px; font-weight: 600; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
2313
+ .sess-tx span { color: var(--faint); font-size: 11px; }
2314
+
2315
+ /* 此刻概览圆环 */
2316
+ .donut-row { display: flex; align-items: center; gap: 18px; padding: 14px 18px; }
2317
+ .donut-wrap { position: relative; width: 92px; height: 92px; flex: none; }
2318
+ .donut {
2319
+ width: 100%;
2320
+ height: 100%;
2321
+ border-radius: 50%;
2322
+ -webkit-mask: radial-gradient(farthest-side, transparent 63%, #000 64%);
2323
+ mask: radial-gradient(farthest-side, transparent 63%, #000 64%);
2324
+ }
2325
+ /* flex 列整体居中:grid 两行会均分高度,把数字顶上、标签压到环上 */
2326
+ .donut-center {
2327
+ position: absolute;
2328
+ inset: 0;
2329
+ display: flex;
2330
+ flex-direction: column;
2331
+ align-items: center;
2332
+ justify-content: center;
2333
+ gap: 1px;
2334
+ text-align: center;
2335
+ }
2336
+ .donut-center b { display: block; font-size: 20px; line-height: 1.1; }
2337
+ .donut-center span {
2338
+ display: block;
2339
+ font-size: 9px;
2340
+ line-height: 1.2;
2341
+ color: var(--faint);
2342
+ max-width: 54px; /* 限制在中心孔内,英文 Active Sessions 折两行 */
2343
+ }
2344
+ .donut-legend { display: grid; gap: 7px; font-size: 12px; color: var(--muted); }
2345
+ .donut-legend i { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 7px; }
2346
+
2347
+ /* ── 会话看板:工作面收敛(弱玻璃 + 高密度 + 新状态光)───────────────────── */
2348
+ .sessions-board {
2349
+ background-image: none;
2350
+ padding: 0;
2351
+ }
2352
+ .session-board-column { border-radius: 16px; }
2353
+ :root[data-theme="dark"] .session-board-column {
2354
+ background: rgba(255, 255, 255, 0.03);
2355
+ border-color: rgba(255, 255, 255, 0.08);
2356
+ }
2357
+ :root[data-theme="dark"] .session-board-column > header {
2358
+ background: linear-gradient(180deg, color-mix(in srgb, var(--col-signal) 8%, transparent), transparent 85%);
2359
+ border-color: rgba(255, 255, 255, 0.07);
2360
+ }
2361
+ .session-board-column h2 {
2362
+ font-family: inherit;
2363
+ letter-spacing: 0.02em;
2364
+ text-transform: none;
2365
+ font-size: 13px;
2366
+ }
2367
+ .session-board-count { border-radius: 999px; font-family: inherit; }
2368
+ .session-board-needs-you { --col-signal: var(--warning); }
2369
+ .session-board-needs-you { background: linear-gradient(180deg, color-mix(in srgb, var(--warning) 4%, var(--surface)), var(--surface) 140px); }
2370
+ .session-board-starting { --col-signal: var(--info); }
2371
+ .session-board-working { --col-signal: var(--accent); }
2372
+ .session-board-idle { --col-signal: var(--success); }
2373
+ :root[data-theme="dark"] .session-board-needs-you {
2374
+ background: linear-gradient(180deg, color-mix(in srgb, var(--warning) 6%, transparent), transparent 140px), rgba(255, 255, 255, 0.03);
2375
+ }
2376
+ .session-card { border-radius: 12px; }
2377
+ :root[data-theme="dark"] .session-card {
2378
+ background: rgba(13, 17, 26, 0.6);
2379
+ border-color: rgba(255, 255, 255, 0.08);
2380
+ }
2381
+ .session-card-top {
2382
+ grid-template-columns: max-content minmax(0, 1fr) max-content;
2383
+ align-items: center;
2384
+ }
2385
+ .session-signal {
2386
+ color: var(--warning);
2387
+ background: color-mix(in srgb, var(--warning) 10%, transparent);
2388
+ border-color: color-mix(in srgb, var(--warning) 32%, transparent);
2389
+ font-family: inherit;
2390
+ border-radius: 999px;
2391
+ }
2392
+
2393
+ /* 表格 hover 行 */
2394
+ :root[data-theme="dark"] table { background: rgba(255, 255, 255, 0.03); }
2395
+ :root[data-theme="dark"] th { background: rgba(255, 255, 255, 0.04); }
2396
+
2397
+ /* ── 会话筛选条:一行轻量胶囊,不再是一面板 checkbox 墙 ─────────────────── */
2398
+ .sessions-filters {
2399
+ display: flex;
2400
+ flex-wrap: wrap;
2401
+ align-items: center;
2402
+ gap: 9px;
2403
+ padding: 0;
2404
+ background: transparent;
2405
+ border: 0;
2406
+ box-shadow: none;
2407
+ }
2408
+ .sessions-filters input[type=search] {
2409
+ flex: 0 1 300px;
2410
+ min-width: 200px;
2411
+ min-height: 36px;
2412
+ padding: 0 16px;
2413
+ border-radius: 999px;
2414
+ background: var(--surface);
2415
+ border: 1px solid var(--border-soft);
2416
+ }
2417
+ .sessions-filters select {
2418
+ width: auto;
2419
+ min-height: 36px;
2420
+ padding: 0 30px 0 14px;
2421
+ border-radius: 999px;
2422
+ background-color: var(--surface);
2423
+ border: 1px solid var(--border-soft);
2424
+ appearance: none;
2425
+ -webkit-appearance: none;
2426
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%2399a3b3' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
2427
+ background-repeat: no-repeat;
2428
+ background-position: right 12px center;
2429
+ color: var(--muted);
2430
+ font-weight: 600;
2431
+ font-size: 12.5px;
2432
+ cursor: pointer;
2433
+ }
2434
+ :root[data-theme="dark"] .sessions-filters input[type=search],
2435
+ :root[data-theme="dark"] .sessions-filters select {
2436
+ background-color: rgba(255, 255, 255, 0.045);
2437
+ border-color: rgba(255, 255, 255, 0.09);
2438
+ }
2439
+ .sessions-filters input[type=search]:focus,
2440
+ .sessions-filters select:focus {
2441
+ border-color: color-mix(in srgb, var(--accent) 50%, transparent);
2442
+ }
2443
+
2444
+ /* 「仅活跃」toggle chip:checkbox 隐藏,整个胶囊就是开关 */
2445
+ .sessions-filters .filter-toggle {
2446
+ min-height: 36px;
2447
+ padding: 0 16px;
2448
+ border-radius: 999px;
2449
+ border: 1px solid var(--border-soft);
2450
+ background: var(--surface);
2451
+ font-size: 12.5px;
2452
+ font-weight: 600;
2453
+ cursor: pointer;
2454
+ user-select: none;
2455
+ }
2456
+ :root[data-theme="dark"] .sessions-filters .filter-toggle {
2457
+ background: rgba(255, 255, 255, 0.045);
2458
+ border-color: rgba(255, 255, 255, 0.09);
2459
+ }
2460
+ .sessions-filters .filter-toggle input { position: absolute; opacity: 0; pointer-events: none; }
2461
+ .sessions-filters .filter-toggle span::before {
2462
+ content: "";
2463
+ display: inline-block;
2464
+ width: 7px;
2465
+ height: 7px;
2466
+ border-radius: 50%;
2467
+ margin-right: 7px;
2468
+ background: var(--faint);
2469
+ vertical-align: 1px;
2470
+ }
2471
+ .sessions-filters .filter-toggle:has(input:checked) {
2472
+ color: var(--accent);
2473
+ border-color: color-mix(in srgb, var(--accent) 45%, transparent);
2474
+ background: var(--accent-soft);
2475
+ }
2476
+ .sessions-filters .filter-toggle:has(input:checked) span::before {
2477
+ background: var(--accent);
2478
+ box-shadow: 0 0 8px color-mix(in srgb, var(--accent) 60%, transparent);
2479
+ }
2480
+
2481
+ /* CLI 下拉 chip + 弹层 */
2482
+ .filter-cli { position: relative; }
2483
+ .filter-cli summary {
2484
+ list-style: none;
2485
+ display: inline-flex;
2486
+ align-items: center;
2487
+ gap: 4px;
2488
+ min-height: 36px;
2489
+ padding: 0 30px 0 16px;
2490
+ border-radius: 999px;
2491
+ border: 1px solid var(--border-soft);
2492
+ background-color: var(--surface);
2493
+ color: var(--muted);
2494
+ font-size: 12.5px;
2495
+ font-weight: 600;
2496
+ cursor: pointer;
2497
+ user-select: none;
2498
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%2399a3b3' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
2499
+ background-repeat: no-repeat;
2500
+ background-position: right 12px center;
2501
+ }
2502
+ .filter-cli summary::-webkit-details-marker { display: none; }
2503
+ :root[data-theme="dark"] .filter-cli summary {
2504
+ background-color: rgba(255, 255, 255, 0.045);
2505
+ border-color: rgba(255, 255, 255, 0.09);
2506
+ }
2507
+ .filter-cli summary b { font-weight: 700; color: var(--fg); }
2508
+ .filter-cli summary b.cli-filter-active { color: var(--accent); }
2509
+ .filter-cli[open] summary {
2510
+ border-color: color-mix(in srgb, var(--accent) 50%, transparent);
2511
+ color: var(--fg);
2512
+ }
2513
+ .filter-cli-pop {
2514
+ position: absolute;
2515
+ z-index: 30;
2516
+ top: calc(100% + 8px);
2517
+ left: 0;
2518
+ width: 340px;
2519
+ max-width: calc(100vw - 48px);
2520
+ display: flex;
2521
+ flex-wrap: wrap;
2522
+ gap: 7px;
2523
+ padding: 13px;
2524
+ border-radius: 14px;
2525
+ border: 1px solid var(--border);
2526
+ background: var(--surface-raised);
2527
+ box-shadow: var(--modal-shadow);
2528
+ }
2529
+ :root[data-theme="dark"] .filter-cli-pop {
2530
+ background: #131a26;
2531
+ border-color: rgba(255, 255, 255, 0.12);
2532
+ }
2533
+ .filter-cli-pop .filter-check {
2534
+ min-height: 28px;
2535
+ padding: 0 12px;
2536
+ border-radius: 999px;
2537
+ border: 1px solid var(--border-soft);
2538
+ background: transparent;
2539
+ color: var(--muted);
2540
+ font-size: 11.5px;
2541
+ font-weight: 600;
2542
+ cursor: pointer;
2543
+ }
2544
+ .filter-cli-pop .filter-check input { position: absolute; opacity: 0; pointer-events: none; }
2545
+ .filter-cli-pop .filter-check:has(input:checked) {
2546
+ color: var(--accent);
2547
+ border-color: color-mix(in srgb, var(--accent) 45%, transparent);
2548
+ background: var(--accent-soft);
2549
+ }
2550
+
2551
+ @media (max-width: 720px) {
2552
+ .sessions-filters input[type=search] { flex: 1 1 100%; }
2553
+ .filter-cli-pop { left: auto; right: 0; }
2554
+ }
2555
+
2556
+ /* ── Bot 配置 → 数字员工档案(master-detail)────────────────────────────── */
2557
+ .bd-layout {
2558
+ display: grid;
2559
+ grid-template-columns: 252px minmax(0, 1fr);
2560
+ gap: 16px;
2561
+ align-items: start;
2562
+ }
2563
+
2564
+ /* 左:员工名册 */
2565
+ .bd-roster {
2566
+ border-radius: 16px;
2567
+ border: 1px solid var(--border-soft);
2568
+ background: var(--surface);
2569
+ padding: 8px;
2570
+ display: grid;
2571
+ gap: 3px;
2572
+ position: sticky;
2573
+ top: 84px;
2574
+ }
2575
+ :root[data-theme="dark"] .bd-roster {
2576
+ background: rgba(255, 255, 255, 0.045);
2577
+ backdrop-filter: blur(18px);
2578
+ border-color: rgba(255, 255, 255, 0.09);
2579
+ }
2580
+ .bd-roster-item {
2581
+ display: flex;
2582
+ align-items: center;
2583
+ gap: 10px;
2584
+ padding: 9px 10px;
2585
+ border-radius: 11px;
2586
+ cursor: pointer;
2587
+ }
2588
+ .bd-roster-item:hover { background: var(--surface-muted); }
2589
+ :root[data-theme="dark"] .bd-roster-item:hover { background: rgba(255, 255, 255, 0.06); }
2590
+ .bd-roster-item.on {
2591
+ background: linear-gradient(120deg, color-mix(in srgb, var(--accent) 14%, transparent), color-mix(in srgb, var(--info) 8%, transparent));
2592
+ box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent);
2593
+ }
2594
+ .bd-roster-tx { min-width: 0; flex: 1; }
2595
+ .bd-roster-tx b { display: block; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
2596
+ .bd-roster-tx span { display: block; color: var(--faint); font-size: 10.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
2597
+ .bd-roster-flag {
2598
+ flex: none;
2599
+ font-size: 9px;
2600
+ font-weight: 700;
2601
+ color: var(--warning);
2602
+ border: 1px solid color-mix(in srgb, var(--warning) 42%, transparent);
2603
+ border-radius: 999px;
2604
+ padding: 1px 7px;
2605
+ }
2606
+
2607
+ /* 右:档案 */
2608
+ .bd-profile { max-width: none; padding: 0; background: transparent; border: 0; box-shadow: none; }
2609
+ :root[data-theme="dark"] .bd-profile { background: transparent; backdrop-filter: none; background-image: none; }
2610
+ .bd-profile-head {
2611
+ display: flex;
2612
+ align-items: center;
2613
+ gap: 16px;
2614
+ margin-bottom: 0;
2615
+ padding: 18px 20px;
2616
+ border-radius: 18px;
2617
+ border: 1px solid var(--border-soft);
2618
+ background: var(--surface);
2619
+ box-shadow: var(--shadow);
2620
+ }
2621
+ :root[data-theme="dark"] .bd-profile-head {
2622
+ background: rgba(255, 255, 255, 0.045);
2623
+ backdrop-filter: blur(18px);
2624
+ border-color: rgba(255, 255, 255, 0.09);
2625
+ background-image: linear-gradient(160deg, color-mix(in srgb, var(--accent) 6%, transparent), transparent 45%);
2626
+ }
2627
+ .bd-profile-head .orb-avatar { width: 54px; height: 54px; }
2628
+ .bd-profile-id { min-width: 0; }
2629
+ .bd-profile-id strong { display: block; font-size: 18px; }
2630
+ .bd-profile-id .mate-role { margin-top: 5px; }
2631
+ .bd-profile-id code {
2632
+ display: block;
2633
+ margin-top: 5px;
2634
+ padding: 0;
2635
+ background: transparent;
2636
+ color: var(--faint);
2637
+ font-size: 10.5px;
2638
+ }
2639
+ .bd-profile-meta {
2640
+ margin: 0 0 0 auto;
2641
+ display: grid;
2642
+ gap: 5px;
2643
+ text-align: right;
2644
+ flex: none;
2645
+ }
2646
+ .bd-profile-meta small { color: var(--muted); font-size: 11.5px; }
2647
+ .bd-profile-meta .bd-meta-ok { color: var(--success); }
2648
+
2649
+ /* 配置 tile 两栏 */
2650
+ .bd-grid {
2651
+ display: grid;
2652
+ grid-template-columns: repeat(2, minmax(0, 1fr));
2653
+ gap: 14px;
2654
+ align-items: start;
2655
+ margin-top: 14px;
2656
+ }
2657
+ .bd-tile {
2658
+ border-radius: 16px;
2659
+ border: 1px solid var(--border-soft);
2660
+ background: var(--surface);
2661
+ box-shadow: var(--shadow);
2662
+ padding: 4px 18px 16px;
2663
+ }
2664
+ :root[data-theme="dark"] .bd-tile {
2665
+ background: rgba(255, 255, 255, 0.04);
2666
+ backdrop-filter: blur(18px);
2667
+ border-color: rgba(255, 255, 255, 0.08);
2668
+ }
2669
+ .bd-tile .bd-section { padding-top: 14px; }
2670
+ .bd-tile .bd-section + .bd-section { margin-top: 14px; border-top: 1px solid var(--border-soft); }
2671
+ .bd-tile .bd-section-title {
2672
+ font-size: 12.5px;
2673
+ letter-spacing: 0.02em;
2674
+ text-transform: none;
2675
+ color: var(--fg);
2676
+ display: flex;
2677
+ align-items: center;
2678
+ gap: 8px;
2679
+ }
2680
+ .bd-tile .bd-section-title::before {
2681
+ content: "";
2682
+ width: 7px;
2683
+ height: 7px;
2684
+ border-radius: 50%;
2685
+ background: var(--accent);
2686
+ box-shadow: 0 0 8px color-mix(in srgb, var(--accent) 55%, transparent);
2687
+ flex: none;
2688
+ }
2689
+
2690
+ .settings-grid {
2691
+ display: grid;
2692
+ grid-template-columns: minmax(320px, 760px);
2693
+ gap: 14px;
2694
+ max-width: 100%;
2695
+ min-width: 0;
2696
+ }
2697
+
2698
+ .settings-card {
2699
+ padding: 4px 18px 16px;
2700
+ min-width: 0;
2701
+ overflow: hidden;
2702
+ }
2703
+
2704
+ .settings-card .bd-section { padding-top: 14px; }
2705
+ .settings-card .bd-section + .bd-section { margin-top: 14px; border-top: 1px solid var(--border-soft); }
2706
+ .settings-actions { margin-top: 8px; }
2707
+ .settings-card .toggle-row { min-width: 0; }
2708
+ .settings-card .toggle-tx { min-width: 0; }
2709
+ .settings-card .toggle-tx strong,
2710
+ .settings-card .toggle-tx small {
2711
+ overflow-wrap: anywhere;
2712
+ }
2713
+ .settings-card .toggle-tx small {
2714
+ -webkit-line-clamp: unset;
2715
+ color: var(--muted);
2716
+ }
2717
+
2718
+ /* toggle 开关行:checkbox 隐藏,switch 是视觉开关 */
2719
+ .toggle-row {
2720
+ display: flex;
2721
+ align-items: flex-start;
2722
+ gap: 12px;
2723
+ padding: 9px 0;
2724
+ cursor: pointer;
2725
+ color: var(--fg);
2726
+ }
2727
+ .toggle-row input { position: absolute; opacity: 0; pointer-events: none; }
2728
+ .toggle-row .switch {
2729
+ width: 36px;
2730
+ height: 20px;
2731
+ border-radius: 999px;
2732
+ background: var(--border);
2733
+ position: relative;
2734
+ flex: none;
2735
+ margin-top: 1px;
2736
+ transition: background 140ms ease;
2737
+ }
2738
+ .toggle-row .switch::after {
2739
+ content: "";
2740
+ position: absolute;
2741
+ top: 2px;
2742
+ left: 2px;
2743
+ width: 16px;
2744
+ height: 16px;
2745
+ border-radius: 50%;
2746
+ background: var(--muted);
2747
+ transition: left 140ms ease, background 140ms ease;
2748
+ }
2749
+ .toggle-row input:checked + .switch {
2750
+ background: linear-gradient(120deg, var(--accent), var(--info));
2751
+ box-shadow: 0 0 12px color-mix(in srgb, var(--accent) 28%, transparent);
2752
+ }
2753
+ .toggle-row input:checked + .switch::after { left: 18px; background: #fff; }
2754
+ .toggle-row input:disabled + .switch { opacity: 0.4; }
2755
+ .toggle-tx { min-width: 0; }
2756
+ .toggle-tx strong { display: block; font-size: 12.5px; font-weight: 600; }
2757
+ .toggle-tx small {
2758
+ display: -webkit-box;
2759
+ -webkit-line-clamp: 1;
2760
+ -webkit-box-orient: vertical;
2761
+ overflow: hidden;
2762
+ margin-top: 2px;
2763
+ color: var(--faint);
2764
+ font-size: 11px;
2765
+ line-height: 1.5;
2766
+ cursor: pointer;
2767
+ }
2768
+ .toggle-tx small:hover { color: var(--muted); }
2769
+ .toggle-tx small.open { -webkit-line-clamp: unset; color: var(--muted); }
2770
+ /* toggle 行之外的长帮助文字(签名/额度说明)同样默认一行,点开展开 */
2771
+ .bd-tile small.bd-help {
2772
+ display: -webkit-box;
2773
+ -webkit-line-clamp: 1;
2774
+ -webkit-box-orient: vertical;
2775
+ overflow: hidden;
2776
+ color: var(--faint);
2777
+ font-size: 11px;
2778
+ line-height: 1.5;
2779
+ cursor: pointer;
2780
+ }
2781
+ .bd-tile small.bd-help:hover { color: var(--muted); }
2782
+ .bd-tile small.bd-help.open { -webkit-line-clamp: unset; color: var(--muted); }
2783
+ @media (prefers-reduced-motion: reduce) {
2784
+ .toggle-row .switch, .toggle-row .switch::after { transition: none; }
2785
+ }
2786
+
2787
+ .bd-tile textarea,
2788
+ .bd-tile input[type=text],
2789
+ .bd-tile input[type=number] {
2790
+ width: 100%;
2791
+ box-sizing: border-box;
2792
+ padding: 9px 13px;
2793
+ border: 1px solid var(--border);
2794
+ border-radius: 11px;
2795
+ background: var(--surface-raised);
2796
+ color: var(--fg);
2797
+ font: 12px/1.5 var(--mono);
2798
+ }
2799
+ :root[data-theme="dark"] .bd-tile textarea,
2800
+ :root[data-theme="dark"] .bd-tile input[type=text],
2801
+ :root[data-theme="dark"] .bd-tile input[type=number] {
2802
+ background: rgba(13, 17, 26, 0.55);
2803
+ border-color: rgba(255, 255, 255, 0.1);
2804
+ }
2805
+
2806
+ @media (max-width: 980px) {
2807
+ .bd-layout { grid-template-columns: 1fr; }
2808
+ .bd-roster { position: static; display: flex; overflow-x: auto; }
2809
+ .bd-roster-item { flex: 0 0 auto; }
2810
+ .bd-grid { grid-template-columns: 1fr; }
2811
+ .bd-profile-head { flex-wrap: wrap; }
2812
+ .bd-profile-meta { text-align: left; margin-left: 0; }
2813
+ }
2814
+
2815
+ /* ── 全站收尾:长尾页面对齐设计语言 ──────────────────────────────────────── */
2816
+
2817
+ /* 兜底表单控件基线(零特异性 :where,不会压过上面的胶囊规则):
2818
+ 接入点 / 团队 / 弹窗里的裸 input、select、textarea 统一圆角与底色 */
2819
+ .page :where(input:not([type=checkbox], [type=radio]), select, textarea),
2820
+ dialog :where(input:not([type=checkbox], [type=radio]), select, textarea) {
2821
+ border: 1px solid var(--border);
2822
+ border-radius: 11px;
2823
+ background: var(--surface-raised);
2824
+ color: var(--fg);
2825
+ padding: 8px 12px;
2826
+ font: inherit;
2827
+ }
2828
+ :root[data-theme="dark"] .page :where(input:not([type=checkbox], [type=radio]), select, textarea),
2829
+ :root[data-theme="dark"] dialog :where(input:not([type=checkbox], [type=radio]), select, textarea) {
2830
+ background: rgba(13, 17, 26, 0.55);
2831
+ border-color: rgba(255, 255, 255, 0.1);
2832
+ }
2833
+
2834
+ /* 筛选条统一(群组 / 定时 / 工作流共用 .filters):去面板底,控件胶囊化 */
2835
+ .filters {
2836
+ padding: 0;
2837
+ background: transparent;
2838
+ border: 0;
2839
+ box-shadow: none;
2840
+ }
2841
+ .filters input[type=search],
2842
+ .filters input[type=text],
2843
+ .filters select {
2844
+ min-height: 36px;
2845
+ border-radius: 999px;
2846
+ padding: 0 16px;
2847
+ background: var(--surface);
2848
+ border: 1px solid var(--border-soft);
2849
+ }
2850
+ .filters select {
2851
+ padding-right: 30px;
2852
+ appearance: none;
2853
+ -webkit-appearance: none;
2854
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%2399a3b3' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
2855
+ background-repeat: no-repeat;
2856
+ background-position: right 12px center;
2857
+ color: var(--muted);
2858
+ font-weight: 600;
2859
+ font-size: 12.5px;
2860
+ cursor: pointer;
2861
+ }
2862
+ :root[data-theme="dark"] .filters input[type=search],
2863
+ :root[data-theme="dark"] .filters input[type=text],
2864
+ :root[data-theme="dark"] .filters select {
2865
+ background-color: rgba(255, 255, 255, 0.045);
2866
+ border-color: rgba(255, 255, 255, 0.09);
2867
+ }
2868
+ .filters .filter-toggle {
2869
+ min-height: 36px;
2870
+ padding: 0 16px;
2871
+ border-radius: 999px;
2872
+ border: 1px solid var(--border-soft);
2873
+ background: var(--surface);
2874
+ font-size: 12.5px;
2875
+ font-weight: 600;
2876
+ cursor: pointer;
2877
+ user-select: none;
2878
+ }
2879
+ :root[data-theme="dark"] .filters .filter-toggle {
2880
+ background: rgba(255, 255, 255, 0.045);
2881
+ border-color: rgba(255, 255, 255, 0.09);
2882
+ }
2883
+ .filters .filter-toggle:has(input:checked) {
2884
+ color: var(--accent);
2885
+ border-color: color-mix(in srgb, var(--accent) 45%, transparent);
2886
+ background: var(--accent-soft);
2887
+ }
2888
+ .filters .filter-toggle input { position: absolute; opacity: 0; pointer-events: none; }
2889
+ /* 文本没包 span 的旧式 toggle(群组/定时页):状态点画在 label 上 */
2890
+ .filters .filter-toggle:not(:has(> span))::before {
2891
+ content: "";
2892
+ display: inline-block;
2893
+ width: 7px;
2894
+ height: 7px;
2895
+ border-radius: 50%;
2896
+ margin-right: 7px;
2897
+ background: var(--faint);
2898
+ }
2899
+ .filters .filter-toggle:not(:has(> span)):has(input:checked)::before {
2900
+ background: var(--accent);
2901
+ box-shadow: 0 0 8px color-mix(in srgb, var(--accent) 60%, transparent);
2902
+ }
2903
+
2904
+ /* 通用 .card(接入点等页面在用)→ 玻璃面板 */
2905
+ .card {
2906
+ border: 1px solid var(--border-soft);
2907
+ border-radius: 16px;
2908
+ background: var(--surface);
2909
+ box-shadow: var(--shadow);
2910
+ padding: 16px 18px;
2911
+ }
2912
+ :root[data-theme="dark"] .card {
2913
+ background: rgba(255, 255, 255, 0.04);
2914
+ backdrop-filter: blur(18px);
2915
+ border-color: rgba(255, 255, 255, 0.08);
2916
+ }
2917
+
2918
+ /* 角色管理:树面板对齐名册风格 */
2919
+ :root[data-theme="dark"] .roles-tree-panel,
2920
+ :root[data-theme="dark"] .roles-editor-panel {
2921
+ background: rgba(255, 255, 255, 0.04);
2922
+ backdrop-filter: blur(18px);
2923
+ border-color: rgba(255, 255, 255, 0.08);
2924
+ }
2925
+ .roles-tree-panel,
2926
+ .roles-editor-panel { border-radius: 16px; }
2927
+ .roles-group-icon {
2928
+ display: inline-flex;
2929
+ width: 16px;
2930
+ height: 16px;
2931
+ color: var(--muted);
2932
+ }
2933
+ .roles-group-icon svg {
2934
+ width: 16px;
2935
+ height: 16px;
2936
+ fill: none;
2937
+ stroke: currentColor;
2938
+ stroke-width: 1.6;
2939
+ stroke-linecap: round;
2940
+ stroke-linejoin: round;
2941
+ }
2942
+ .roles-bot-row.selected {
2943
+ background: linear-gradient(120deg, color-mix(in srgb, var(--accent) 14%, transparent), color-mix(in srgb, var(--info) 8%, transparent));
2944
+ border-left: 0;
2945
+ padding-left: 20px;
2946
+ box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent);
2947
+ }
2948
+ .roles-group-row.selected { background: var(--accent-soft); }
2949
+ .roles-badge { border-radius: 999px; }
2950
+
2951
+ /* ── 看板列顺序自定义:列头可拖拽 + hover 出现 ‹/› 按钮 ──────────────────── */
2952
+ .session-board-column > header[draggable] { cursor: grab; }
2953
+ .session-board-column > header[draggable]:active { cursor: grabbing; }
2954
+ .session-board-head-right {
2955
+ display: inline-flex;
2956
+ align-items: center;
2957
+ gap: 6px;
2958
+ flex: none;
2959
+ }
2960
+ .session-board-move { display: inline-flex; gap: 3px; opacity: 0; transition: opacity 120ms ease; }
2961
+ .session-board-column > header:hover .session-board-move,
2962
+ .session-board-move:focus-within { opacity: 1; }
2963
+ @media (hover: none) {
2964
+ .session-board-move { opacity: 1; }
2965
+ }
2966
+ .session-board-move button {
2967
+ min-height: 22px;
2968
+ min-width: 22px;
2969
+ padding: 0;
2970
+ border-radius: 999px;
2971
+ font-size: 13px;
2972
+ line-height: 1;
2973
+ color: var(--muted);
2974
+ background: transparent;
2975
+ border: 1px solid var(--border-soft);
2976
+ }
2977
+ .session-board-move button:hover:not(:disabled) {
2978
+ color: var(--accent);
2979
+ border-color: color-mix(in srgb, var(--accent) 45%, transparent);
2980
+ }
2981
+ .session-board-move button:disabled { opacity: 0.3; }
2982
+ .session-board-column.dragging { opacity: 0.55; }
2983
+ .session-board-column.drag-over {
2984
+ border-color: color-mix(in srgb, var(--accent) 55%, transparent);
2985
+ box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent);
2986
+ }
2987
+ @media (prefers-reduced-motion: reduce) {
2988
+ .session-board-move { transition: none; }
2989
+ }
2990
+
2991
+ /* 工作流子导航:下划线 tab → 胶囊段 */
2992
+ .wf-subnav { border-bottom: 0; gap: 6px; }
2993
+ .wf-subnav a {
2994
+ border: 1px solid transparent;
2995
+ border-radius: 999px;
2996
+ padding: 6px 16px;
2997
+ margin-bottom: 0;
2998
+ font-size: 12.5px;
2999
+ font-weight: 600;
3000
+ }
3001
+ .wf-subnav a.active {
3002
+ color: var(--accent-strong);
3003
+ border-color: color-mix(in srgb, var(--accent) 35%, transparent);
3004
+ background: var(--accent-soft);
3005
+ border-bottom-color: color-mix(in srgb, var(--accent) 35%, transparent);
3006
+ }
3007
+
3008
+ @media (max-width: 980px) {
3009
+ .sidebar { margin: 0; height: auto; border-radius: 0; top: 0; }
3010
+ .overview-cols { grid-template-columns: 1fr; }
3011
+ .attention-strip { margin: 12px 16px 0; }
3012
+ }
3013
+
3014
+ /* ════════════════════════════════════════════════════════════════════════════
3015
+ "2077" CYBERPUNK SKIN
3016
+ Distilled from the kaboo webui (frontend/src/styles/cyberpunk.css). The whole
3017
+ dashboard is variable-driven, so re-pointing the design tokens under
3018
+ :root[data-skin="cyber"] re-skins every component; the rest of this section
3019
+ adds the neon flourishes (grid, scanlines, data-rain, glow, sharp corners).
3020
+ Placed last in the file so it wins over the light/dark token blocks via source
3021
+ order (equal specificity). The light/dark mode is intentionally ignored here —
3022
+ 2077 ships its own dark neon palette.
3023
+ ════════════════════════════════════════════════════════════════════════════ */
3024
+
3025
+ :root[data-skin="cyber"] {
3026
+ color-scheme: dark;
3027
+ --bg: hsl(240 44% 6%);
3028
+ --bg-soft: hsl(240 40% 8%);
3029
+ --surface: hsl(236 40% 9% / 0.72);
3030
+ --surface-raised: hsl(238 42% 12% / 0.84);
3031
+ --surface-muted: hsl(236 30% 14% / 0.7);
3032
+ --fg: hsl(188 72% 88%);
3033
+ --muted: hsl(196 30% 64%);
3034
+ --faint: hsl(196 22% 52%);
3035
+ --border: hsl(196 82% 44% / 0.5);
3036
+ --border-soft: hsl(196 70% 40% / 0.3);
3037
+ --accent: hsl(186 100% 56%);
3038
+ --accent-strong: hsl(186 100% 72%);
3039
+ --accent-soft: hsl(186 100% 52% / 0.16);
3040
+ --success: hsl(150 85% 58%);
3041
+ --success-soft: hsl(150 85% 50% / 0.16);
3042
+ --warning: hsl(56 97% 60%);
3043
+ --warning-soft: hsl(56 97% 52% / 0.16);
3044
+ --danger: hsl(330 100% 64%);
3045
+ --danger-soft: hsl(330 100% 58% / 0.18);
3046
+ --on-accent: hsl(240 60% 6%);
3047
+ --on-danger: hsl(240 60% 6%);
3048
+ --modal-shadow: 0 24px 80px hsl(240 70% 2% / 0.7), 0 0 36px hsl(186 100% 52% / 0.18);
3049
+ --modal-backdrop: hsl(240 60% 3% / 0.72);
3050
+ --qr-surface: #ffffff;
3051
+ --shadow: 0 14px 40px hsl(240 70% 3% / 0.55), 0 0 0 1px hsl(186 100% 52% / 0.06);
3052
+ --radius: 4px;
3053
+ }
3054
+
3055
+ :root[data-skin="cyber"] body {
3056
+ background-color: hsl(240 46% 5%);
3057
+ background-image: linear-gradient(
3058
+ 180deg,
3059
+ hsl(240 44% 6%) 0%,
3060
+ hsl(252 46% 4%) 58%,
3061
+ hsl(240 48% 5%) 100%
3062
+ );
3063
+ background-attachment: fixed;
3064
+ color: var(--fg);
3065
+ }
3066
+
3067
+ /* Lift the real UI above the fixed FX layer (the FX sits at z-index:0). */
3068
+ :root[data-skin="cyber"] .app-shell {
3069
+ position: relative;
3070
+ z-index: 1;
3071
+ }
3072
+
3073
+ /* ── Fixed background FX: neon grid + glow + scanlines + vignette + data-rain ─ */
3074
+ .cyber-fx {
3075
+ position: fixed;
3076
+ inset: 0;
3077
+ z-index: 0;
3078
+ pointer-events: none;
3079
+ overflow: hidden;
3080
+ }
3081
+
3082
+ .cyber-fx-grid {
3083
+ position: absolute;
3084
+ inset: 0;
3085
+ background:
3086
+ radial-gradient(ellipse at 78% 112%, hsl(186 100% 52% / 0.18), transparent 44%),
3087
+ radial-gradient(ellipse at 16% 104%, hsl(330 100% 58% / 0.16), transparent 42%),
3088
+ linear-gradient(0deg, hsl(56 97% 52% / 0.05), transparent 28%),
3089
+ repeating-linear-gradient(0deg, hsl(186 100% 52% / 0.05) 0 1px, transparent 1px 44px),
3090
+ repeating-linear-gradient(90deg, hsl(330 100% 58% / 0.04) 0 1px, transparent 1px 44px);
3091
+ opacity: 0.85;
3092
+ }
3093
+
3094
+ .cyber-fx-scan {
3095
+ position: absolute;
3096
+ inset: 0;
3097
+ background: repeating-linear-gradient(
3098
+ 0deg,
3099
+ hsl(186 100% 52% / 0.04) 0 1px,
3100
+ transparent 1px 3px
3101
+ );
3102
+ opacity: 0.6;
3103
+ }
3104
+
3105
+ /* CRT vignette darkens the corners. */
3106
+ .cyber-fx::after {
3107
+ content: '';
3108
+ position: absolute;
3109
+ inset: 0;
3110
+ background: radial-gradient(ellipse at 50% 44%, transparent 55%, hsl(240 70% 3% / 0.6) 100%);
3111
+ }
3112
+
3113
+ .cyber-rain {
3114
+ position: absolute;
3115
+ inset: 0;
3116
+ }
3117
+
3118
+ .cyber-rain-col {
3119
+ position: absolute;
3120
+ top: 0;
3121
+ writing-mode: vertical-rl;
3122
+ text-orientation: upright;
3123
+ font-family: var(--mono);
3124
+ font-size: var(--sz, 14px);
3125
+ line-height: 1;
3126
+ letter-spacing: 1px;
3127
+ color: hsl(var(--rc, 186 100% 60%));
3128
+ opacity: var(--op, 0.4);
3129
+ text-shadow: 0 0 6px hsl(var(--rc, 186 100% 60%) / 0.7);
3130
+ white-space: nowrap;
3131
+ /* bright leading glyph (bottom) → fading trail (top) */
3132
+ -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 55%, #fff 100%);
3133
+ mask-image: linear-gradient(180deg, transparent 0%, #000 55%, #fff 100%);
3134
+ animation: cyber-rain-fall var(--dur, 8s) linear var(--delay, 0s) infinite;
3135
+ will-change: transform;
3136
+ }
3137
+
3138
+ @keyframes cyber-rain-fall {
3139
+ from { transform: translateY(-115%); }
3140
+ to { transform: translateY(115vh); }
3141
+ }
3142
+
3143
+ @media (prefers-reduced-motion: reduce) {
3144
+ .cyber-rain-col { animation: none; opacity: 0.12; }
3145
+ }
3146
+
3147
+ /* ── Chrome: glassy neon sidebar + topbar ───────────────────────────────────*/
3148
+ :root[data-skin="cyber"] .sidebar {
3149
+ background: hsl(240 40% 7% / 0.78);
3150
+ -webkit-backdrop-filter: blur(14px) saturate(1.15);
3151
+ backdrop-filter: blur(14px) saturate(1.15);
3152
+ border-right-color: hsl(186 90% 50% / 0.28);
3153
+ }
3154
+
3155
+ :root[data-skin="cyber"] .topbar {
3156
+ background: hsl(240 40% 6% / 0.7);
3157
+ border-bottom-color: hsl(186 90% 50% / 0.3);
3158
+ -webkit-backdrop-filter: blur(16px) saturate(1.15);
3159
+ backdrop-filter: blur(16px) saturate(1.15);
3160
+ }
3161
+
3162
+ :root[data-skin="cyber"] .brand-mark {
3163
+ background: linear-gradient(135deg, hsl(186 100% 52%), hsl(330 100% 60%));
3164
+ color: hsl(240 60% 6%);
3165
+ border-radius: 4px;
3166
+ box-shadow: 0 0 14px hsl(186 100% 52% / 0.5);
3167
+ }
3168
+
3169
+ :root[data-skin="cyber"] .brand strong,
3170
+ :root[data-skin="cyber"] .topbar-title strong {
3171
+ font-family: var(--mono);
3172
+ letter-spacing: 1px;
3173
+ text-shadow: 0 0 12px hsl(186 100% 60% / 0.45);
3174
+ }
3175
+
3176
+ /* ── Navigation: neon active rail ───────────────────────────────────────────*/
3177
+ :root[data-skin="cyber"] .sidebar-nav a:hover {
3178
+ background: hsl(186 100% 52% / 0.08);
3179
+ color: var(--accent-strong);
3180
+ }
3181
+
3182
+ :root[data-skin="cyber"] .sidebar-nav a.active {
3183
+ background: linear-gradient(135deg, hsl(186 80% 26% / 0.45), hsl(330 70% 24% / 0.3));
3184
+ color: hsl(186 100% 74%);
3185
+ box-shadow: inset 2px 0 0 hsl(186 100% 56%), 0 0 16px hsl(186 100% 52% / 0.18);
3186
+ }
3187
+
3188
+ /* ── Headings ───────────────────────────────────────────────────────────────*/
3189
+ :root[data-skin="cyber"] .page-heading h1 {
3190
+ font-family: var(--mono);
3191
+ text-shadow: 0 0 18px hsl(186 100% 60% / 0.35);
3192
+ }
3193
+
3194
+ :root[data-skin="cyber"] .eyebrow {
3195
+ color: var(--warning) !important;
3196
+ text-shadow: 0 0 10px hsl(56 97% 60% / 0.4);
3197
+ }
3198
+
3199
+ /* ── Cards & metrics: faint inner neon edge + glowing figures ────────────────*/
3200
+ :root[data-skin="cyber"] .panel,
3201
+ :root[data-skin="cyber"] .metric-card,
3202
+ :root[data-skin="cyber"] .bd-card {
3203
+ border-color: hsl(196 82% 44% / 0.4);
3204
+ box-shadow: var(--shadow), inset 0 1px 0 hsl(186 100% 60% / 0.07);
3205
+ }
3206
+
3207
+ :root[data-skin="cyber"] .metric-card strong {
3208
+ color: var(--accent-strong);
3209
+ text-shadow: 0 0 14px hsl(186 100% 60% / 0.4);
3210
+ }
3211
+
3212
+ /* ── Primary actions: neon gradient ─────────────────────────────────────────*/
3213
+ :root[data-skin="cyber"] .btn-link.primary,
3214
+ :root[data-skin="cyber"] button.primary {
3215
+ background: linear-gradient(135deg, hsl(186 100% 52%), hsl(56 97% 56%));
3216
+ color: hsl(240 60% 6%);
3217
+ border-color: hsl(186 100% 56%);
3218
+ box-shadow: 0 0 16px hsl(186 100% 52% / 0.3);
3219
+ }
3220
+
3221
+ /* ── Segmented switchers (incl. the skin switcher itself) ────────────────────*/
3222
+ :root[data-skin="cyber"] .segmented {
3223
+ border-color: hsl(186 80% 46% / 0.35);
3224
+ background: hsl(240 36% 9% / 0.6);
3225
+ }
3226
+
3227
+ :root[data-skin="cyber"] .segmented button.active {
3228
+ background: linear-gradient(135deg, hsl(186 100% 52%), hsl(186 100% 62%));
3229
+ color: hsl(240 60% 6%);
3230
+ box-shadow: 0 0 12px hsl(186 100% 52% / 0.35);
3231
+ }
3232
+
3233
+ /* ── Status + badges ────────────────────────────────────────────────────────*/
3234
+ :root[data-skin="cyber"] .connection-status.online::before {
3235
+ box-shadow: 0 0 8px hsl(150 85% 58% / 0.85);
3236
+ }
3237
+
3238
+ /* CLI badges: fold the light hard-coded palette into the neon surface tokens
3239
+ (mirrors the existing dark-theme treatment). */
3240
+ :root[data-skin="cyber"] .cli-claude-code,
3241
+ :root[data-skin="cyber"] .cli-codex,
3242
+ :root[data-skin="cyber"] .cli-codex-app,
3243
+ :root[data-skin="cyber"] .cli-cursor,
3244
+ :root[data-skin="cyber"] .cli-gemini,
3245
+ :root[data-skin="cyber"] .cli-opencode,
3246
+ :root[data-skin="cyber"] .cli-mtr,
3247
+ :root[data-skin="cyber"] .cli-hermes,
3248
+ :root[data-skin="cyber"] .cli-mira,
3249
+ :root[data-skin="cyber"] .cli-pi,
3250
+ :root[data-skin="cyber"] .cli-aiden,
3251
+ :root[data-skin="cyber"] .cli-coco {
3252
+ background: var(--surface-muted);
3253
+ color: var(--fg);
3254
+ border-color: var(--border);
3255
+ }
3256
+
3257
+ /* Themed form controls in 2077 — inner glow + accent-cyan focus ring. */
3258
+ :root[data-skin="cyber"] input:not([type=checkbox]):not([type=radio]),
3259
+ :root[data-skin="cyber"] select,
3260
+ :root[data-skin="cyber"] textarea {
3261
+ background-color: hsl(240 36% 9% / 0.7);
3262
+ box-shadow: inset 0 0 12px hsl(186 100% 52% / 0.06);
3263
+ }
3264
+ :root[data-skin="cyber"] input:focus,
3265
+ :root[data-skin="cyber"] select:focus,
3266
+ :root[data-skin="cyber"] textarea:focus {
3267
+ box-shadow: 0 0 0 3px hsl(186 100% 52% / 0.18), 0 0 14px hsl(186 100% 52% / 0.25);
3268
+ }
3269
+
3270
+ /* ════════════════════════════════════════════════════════════════════════════
3271
+ 2077 MOTION FX — CRT flicker / roll line, HUD frame, random glitches, and the
3272
+ KIROSHI NETLINK boot loader. Ported from kaboo's cyberpunk.css. DOM nodes are
3273
+ injected by cyber-fx.ts only while the skin is active.
3274
+ ════════════════════════════════════════════════════════════════════════════ */
3275
+
3276
+ /* CRT flicker + scrolling roll line (inside the behind-content #cyber-fx). */
3277
+ .cyber-flicker {
3278
+ position: absolute;
3279
+ inset: 0;
3280
+ background: hsl(186 100% 52% / 0.04);
3281
+ mix-blend-mode: screen;
3282
+ animation: cyber-flicker 0.18s steps(2, end) infinite;
3283
+ }
3284
+
3285
+ @keyframes cyber-flicker {
3286
+ 0% { opacity: 0.85; }
3287
+ 100% { opacity: 0.42; }
3288
+ }
3289
+
3290
+ .cyber-rollline {
3291
+ position: absolute;
3292
+ left: 0;
3293
+ right: 0;
3294
+ height: 3px;
3295
+ background: linear-gradient(90deg, transparent, hsl(330 100% 64% / 0.5), transparent);
3296
+ box-shadow: 0 0 12px hsl(330 100% 64% / 0.4);
3297
+ opacity: 0.5;
3298
+ animation: cyber-roll 9s linear infinite;
3299
+ }
3300
+
3301
+ @keyframes cyber-roll {
3302
+ 0% { transform: translateY(-6vh); opacity: 0; }
3303
+ 8% { opacity: 0.5; }
3304
+ 60% { opacity: 0.5; }
3305
+ 72%, 100% { transform: translateY(112vh); opacity: 0; }
3306
+ }
3307
+
3308
+ /* Full-screen HUD corner brackets + NETWATCH tag (above content). */
3309
+ .cyber-hud {
3310
+ position: fixed;
3311
+ inset: 0;
3312
+ z-index: 30;
3313
+ pointer-events: none;
3314
+ }
3315
+
3316
+ .cyber-hud-corner {
3317
+ position: absolute;
3318
+ width: 26px;
3319
+ height: 26px;
3320
+ border: 2px solid hsl(186 100% 60% / 0.55);
3321
+ opacity: 0.7;
3322
+ filter: drop-shadow(0 0 6px hsl(186 100% 52% / 0.4));
3323
+ }
3324
+
3325
+ .cyber-hud-corner.tl { top: 12px; left: 12px; border-right: 0; border-bottom: 0; }
3326
+ .cyber-hud-corner.tr { top: 12px; right: 12px; border-left: 0; border-bottom: 0; }
3327
+ .cyber-hud-corner.bl { bottom: 12px; left: 12px; border-right: 0; border-top: 0; }
3328
+ .cyber-hud-corner.br { bottom: 12px; right: 12px; border-left: 0; border-top: 0; }
3329
+
3330
+ .cyber-hud-tag {
3331
+ position: absolute;
3332
+ left: 16px;
3333
+ bottom: 14px;
3334
+ font-family: var(--mono);
3335
+ font-size: 11px;
3336
+ letter-spacing: 0.16em;
3337
+ color: hsl(56 97% 64% / 0.6);
3338
+ text-shadow: 0 0 8px hsl(56 97% 52% / 0.5);
3339
+ }
3340
+
3341
+ /* Random page-glitch overlay (above content); cp-fx-* on <body> drive it. */
3342
+ .cyber-glitch {
3343
+ position: fixed;
3344
+ inset: 0;
3345
+ z-index: 9000;
3346
+ pointer-events: none;
3347
+ mix-blend-mode: screen;
3348
+ opacity: 0;
3349
+ }
3350
+
3351
+ body.cp-fx-tear .cyber-glitch {
3352
+ background: linear-gradient(
3353
+ 180deg,
3354
+ transparent 0 46%,
3355
+ hsl(330 100% 60% / 0.6) 46% 48%,
3356
+ hsl(186 100% 60% / 0.6) 48% 50%,
3357
+ hsl(56 96% 58% / 0.45) 50% 52%,
3358
+ transparent 52%
3359
+ );
3360
+ animation: cp-fx-tear 0.4s steps(2, end);
3361
+ }
3362
+
3363
+ @keyframes cp-fx-tear {
3364
+ 0% { opacity: 0; transform: translateY(0); }
3365
+ 15% { opacity: 0.9; transform: translateY(-26vh) translateX(-8px); }
3366
+ 35% { opacity: 0.5; transform: translateY(28vh) translateX(9px); }
3367
+ 55% { opacity: 0.85; transform: translateY(8vh) translateX(-5px); }
3368
+ 75% { opacity: 0.4; transform: translateY(-14vh) translateX(6px); }
3369
+ 100% { opacity: 0; transform: translateY(40vh); }
3370
+ }
3371
+
3372
+ body.cp-fx-invert .cyber-glitch {
3373
+ mix-blend-mode: difference;
3374
+ background: hsl(186 100% 60%);
3375
+ animation: cp-fx-invert 0.22s steps(1, end);
3376
+ }
3377
+
3378
+ @keyframes cp-fx-invert {
3379
+ 0%, 100% { opacity: 0; }
3380
+ 25% { opacity: 0.95; }
3381
+ 50% { opacity: 0.1; }
3382
+ 75% { opacity: 0.7; }
3383
+ }
3384
+
3385
+ body.cp-fx-blackout .cyber-glitch {
3386
+ mix-blend-mode: normal;
3387
+ background: hsl(240 40% 2%);
3388
+ animation: cp-fx-blackout 0.26s steps(1, end);
3389
+ }
3390
+
3391
+ @keyframes cp-fx-blackout {
3392
+ 0%, 100% { opacity: 0; }
3393
+ 18% { opacity: 0.92; }
3394
+ 34% { opacity: 0.08; }
3395
+ 52% { opacity: 0.85; }
3396
+ 70% { opacity: 0; }
3397
+ }
3398
+
3399
+ body.cp-fx-shake main { animation: cp-fx-shake 0.28s steps(1, end); }
3400
+
3401
+ @keyframes cp-fx-shake {
3402
+ 0%, 100% { transform: translate(0, 0) skewX(0deg); }
3403
+ 15% { transform: translate(-3px, 1px) skewX(0.6deg); }
3404
+ 30% { transform: translate(4px, -2px) skewX(-0.8deg); }
3405
+ 45% { transform: translate(-2px, 1px); }
3406
+ 60% { transform: translate(3px, 0) skewX(0.5deg); }
3407
+ 80% { transform: translate(-1px, -1px); }
3408
+ }
3409
+
3410
+ body.cp-fx-rgb main { animation: cp-fx-rgb 0.34s steps(2, end); }
3411
+
3412
+ @keyframes cp-fx-rgb {
3413
+ 0%, 100% { filter: none; transform: none; }
3414
+ 25% {
3415
+ filter: drop-shadow(3px 0 hsl(330 100% 60% / 0.7)) drop-shadow(-3px 0 hsl(186 100% 60% / 0.7));
3416
+ transform: translateX(-2px);
3417
+ }
3418
+ 50% {
3419
+ filter: drop-shadow(-4px 0 hsl(330 100% 60% / 0.6)) drop-shadow(4px 0 hsl(186 100% 60% / 0.6));
3420
+ transform: translateX(2px);
3421
+ }
3422
+ 75% {
3423
+ filter: drop-shadow(2px 0 hsl(330 100% 60% / 0.7)) drop-shadow(-2px 0 hsl(186 100% 60% / 0.7));
3424
+ }
3425
+ }
3426
+
3427
+ body.cp-fx-slice main { animation: cp-fx-slice 0.32s steps(2, end); }
3428
+
3429
+ @keyframes cp-fx-slice {
3430
+ 0%, 100% { transform: none; }
3431
+ 20% { transform: translateX(-12px) skewX(7deg); }
3432
+ 40% { transform: translateX(10px) skewX(-5deg); }
3433
+ 60% { transform: translateX(-6px) skewX(3deg); }
3434
+ 80% { transform: translateX(4px) skewX(-1deg); }
3435
+ }
3436
+
3437
+ /* ── Boot decrypt loader (plays once when switching into 2077) ───────────────*/
3438
+ .cyber-boot {
3439
+ position: fixed;
3440
+ inset: 0;
3441
+ z-index: 9999;
3442
+ display: flex;
3443
+ align-items: center;
3444
+ justify-content: center;
3445
+ pointer-events: none;
3446
+ background:
3447
+ radial-gradient(ellipse at center, hsl(252 50% 6%) 0%, hsl(252 60% 3%) 100%),
3448
+ repeating-linear-gradient(0deg, hsl(186 100% 52% / 0.06) 0 1px, transparent 1px 3px);
3449
+ animation: cyber-boot-fade 3.2s ease forwards;
3450
+ }
3451
+
3452
+ .cyber-boot::after {
3453
+ content: '';
3454
+ position: absolute;
3455
+ inset: 0;
3456
+ background: radial-gradient(ellipse at 50% 50%, transparent 50%, hsl(252 70% 2% / 0.85) 100%);
3457
+ }
3458
+
3459
+ .cyber-boot-grid {
3460
+ position: absolute;
3461
+ inset: 0;
3462
+ background:
3463
+ repeating-linear-gradient(0deg, hsl(186 100% 52% / 0.05) 0 1px, transparent 1px 40px),
3464
+ repeating-linear-gradient(90deg, hsl(330 100% 58% / 0.04) 0 1px, transparent 1px 40px);
3465
+ -webkit-mask-image: radial-gradient(ellipse at center, #000 28%, transparent 74%);
3466
+ mask-image: radial-gradient(ellipse at center, #000 28%, transparent 74%);
3467
+ animation: cyber-grid-pan 3.2s linear;
3468
+ }
3469
+
3470
+ @keyframes cyber-boot-fade {
3471
+ 0%, 84% { opacity: 1; }
3472
+ 100% { opacity: 0; }
3473
+ }
3474
+
3475
+ @keyframes cyber-blink { 50% { opacity: 0; } }
3476
+
3477
+ @keyframes cyber-grid-pan {
3478
+ from { background-position: 0 -40px, -40px 0; opacity: 0.3; }
3479
+ to { background-position: 0 0, 0 0; opacity: 1; }
3480
+ }
3481
+
3482
+ .cyber-loader {
3483
+ position: relative;
3484
+ z-index: 1;
3485
+ display: flex;
3486
+ align-items: center;
3487
+ justify-content: center;
3488
+ padding: 24px;
3489
+ font-family: var(--mono);
3490
+ }
3491
+
3492
+ .cyber-loader-frame {
3493
+ position: relative;
3494
+ width: min(440px, 86vw);
3495
+ padding: 16px 22px;
3496
+ overflow: hidden;
3497
+ color: hsl(186 100% 72%);
3498
+ background: linear-gradient(135deg, hsl(252 50% 8% / 0.92), hsl(240 60% 5% / 0.94));
3499
+ border: 1px solid hsl(186 100% 52% / 0.4);
3500
+ clip-path: polygon(0 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%);
3501
+ box-shadow: 0 0 36px hsl(186 100% 52% / 0.18), inset 0 0 50px hsl(186 100% 52% / 0.06);
3502
+ text-shadow: 0 0 8px hsl(186 100% 52% / 0.45);
3503
+ }
3504
+
3505
+ .cyber-loader-frame::after {
3506
+ content: '';
3507
+ position: absolute;
3508
+ bottom: 0;
3509
+ left: -40%;
3510
+ width: 40%;
3511
+ height: 2px;
3512
+ background: linear-gradient(90deg, transparent, hsl(56 96% 56%), transparent);
3513
+ box-shadow: 0 0 10px hsl(56 96% 56% / 0.7);
3514
+ animation: cyber-loader-scan 1.6s linear infinite;
3515
+ }
3516
+
3517
+ @keyframes cyber-loader-scan {
3518
+ from { left: -40%; }
3519
+ to { left: 100%; }
3520
+ }
3521
+
3522
+ .cyber-loader-head {
3523
+ display: flex;
3524
+ align-items: baseline;
3525
+ justify-content: space-between;
3526
+ gap: 12px;
3527
+ padding-bottom: 8px;
3528
+ margin-bottom: 12px;
3529
+ border-bottom: 1px solid hsl(186 100% 52% / 0.22);
3530
+ font-size: 11px;
3531
+ letter-spacing: 0.18em;
3532
+ color: hsl(56 96% 70%);
3533
+ }
3534
+
3535
+ .cyber-loader-jp { color: hsl(330 100% 70% / 0.7); letter-spacing: 0.28em; }
3536
+
3537
+ .cyber-loader-line {
3538
+ display: flex;
3539
+ align-items: baseline;
3540
+ gap: 6px;
3541
+ font-size: clamp(13px, 2.4vw, 16px);
3542
+ }
3543
+
3544
+ .cyber-loader-prompt { color: hsl(56 96% 64%); }
3545
+
3546
+ .cyber-loader-text {
3547
+ letter-spacing: 0.08em;
3548
+ color: hsl(186 100% 80%);
3549
+ transition: color 0.2s ease;
3550
+ }
3551
+
3552
+ .cyber-loader-text.done {
3553
+ color: hsl(56 96% 80%);
3554
+ text-shadow: 0 0 10px hsl(56 96% 56% / 0.6);
3555
+ }
3556
+
3557
+ .cyber-loader-cursor {
3558
+ color: hsl(56 96% 70%);
3559
+ animation: cyber-blink 1s steps(1, end) infinite;
3560
+ }
3561
+
3562
+ .cyber-loader-stream {
3563
+ margin-top: 10px;
3564
+ font-size: 11px;
3565
+ letter-spacing: 2px;
3566
+ color: hsl(186 60% 52% / 0.6);
3567
+ white-space: nowrap;
3568
+ overflow: hidden;
3569
+ }
3570
+
3571
+ @media (prefers-reduced-motion: reduce) {
3572
+ .cyber-flicker,
3573
+ .cyber-rollline,
3574
+ .cyber-glitch,
3575
+ .cyber-boot,
3576
+ .cyber-boot-grid,
3577
+ .cyber-loader-frame::after,
3578
+ .cyber-loader-cursor,
3579
+ body.cp-fx-shake main,
3580
+ body.cp-fx-rgb main,
3581
+ body.cp-fx-slice main {
3582
+ animation: none;
3583
+ }
3584
+ }
3585
+
3586
+ /* ════════════════════════════════════════════════════════════════════════════
3587
+ EXTRA SKINS — palettes distilled from the kaboo webui (frontend/src/styles/*).
3588
+ Each is a self-contained token override (variable system re-skins everything);
3589
+ they ship their own light/dark palette and ignore the base light/dark mode.
3590
+ Placed after the base :root blocks so they win by source order.
3591
+ ════════════════════════════════════════════════════════════════════════════ */
3592
+
3593
+ /* 原神 Genshin — light, Teyvat parchment + teal primary + gold accent */
3594
+ :root[data-skin="genshin"] {
3595
+ color-scheme: light;
3596
+ --bg: hsl(216 72% 97%);
3597
+ --bg-soft: hsl(214 56% 94%);
3598
+ --surface: hsl(0 0% 100%);
3599
+ --surface-raised: hsl(0 0% 100%);
3600
+ --surface-muted: hsl(214 48% 95%);
3601
+ --fg: hsl(219 36% 17%);
3602
+ --muted: hsl(219 18% 41%);
3603
+ --faint: hsl(219 16% 55%);
3604
+ --border: hsl(208 34% 82%);
3605
+ --border-soft: hsl(208 34% 88%);
3606
+ --accent: hsl(199 70% 42%);
3607
+ --accent-strong: hsl(199 74% 33%);
3608
+ --accent-soft: hsl(198 68% 92%);
3609
+ --success: hsl(160 60% 35%);
3610
+ --success-soft: hsl(160 52% 92%);
3611
+ --warning: hsl(36 86% 42%);
3612
+ --warning-soft: hsl(42 90% 90%);
3613
+ --danger: hsl(0 72% 48%);
3614
+ --danger-soft: hsl(0 80% 96%);
3615
+ --on-accent: hsl(0 0% 100%);
3616
+ --on-danger: hsl(0 0% 100%);
3617
+ --modal-shadow: 0 24px 80px hsl(219 40% 30% / 0.22);
3618
+ --modal-backdrop: hsl(219 36% 17% / 0.4);
3619
+ --qr-surface: #ffffff;
3620
+ --shadow: 0 18px 44px hsl(216 50% 40% / 0.1);
3621
+ --radius: 10px;
3622
+ }
3623
+
3624
+ /* Fallout — Pip-Boy phosphor green terminal on near-black */
3625
+ :root[data-skin="fallout"] {
3626
+ color-scheme: dark;
3627
+ --bg: hsl(140 32% 5%);
3628
+ --bg-soft: hsl(140 30% 7%);
3629
+ --surface: hsl(140 26% 8%);
3630
+ --surface-raised: hsl(140 26% 11%);
3631
+ --surface-muted: hsl(140 22% 13%);
3632
+ --fg: hsl(140 84% 80%);
3633
+ --muted: hsl(140 28% 56%);
3634
+ --faint: hsl(140 22% 44%);
3635
+ --border: hsl(140 48% 22%);
3636
+ --border-soft: hsl(140 45% 17%);
3637
+ --accent: hsl(138 92% 52%);
3638
+ --accent-strong: hsl(138 92% 66%);
3639
+ --accent-soft: hsl(138 92% 52% / 0.15);
3640
+ --success: hsl(120 80% 55%);
3641
+ --success-soft: hsl(120 70% 50% / 0.16);
3642
+ --warning: hsl(44 96% 56%);
3643
+ --warning-soft: hsl(44 96% 52% / 0.16);
3644
+ --danger: hsl(8 90% 62%);
3645
+ --danger-soft: hsl(8 88% 55% / 0.18);
3646
+ --on-accent: hsl(140 40% 6%);
3647
+ --on-danger: hsl(140 40% 6%);
3648
+ --modal-shadow: 0 24px 80px hsl(140 60% 2% / 0.7), 0 0 30px hsl(138 92% 52% / 0.12);
3649
+ --modal-backdrop: hsl(140 50% 2% / 0.7);
3650
+ --qr-surface: #ffffff;
3651
+ --shadow: 0 14px 40px hsl(140 60% 2% / 0.6);
3652
+ --radius: 2px;
3653
+ }
3654
+
3655
+ /* PRTS (Arknights) — Rhodes Island slate-blue + cyan primary + amber */
3656
+ :root[data-skin="prts"] {
3657
+ color-scheme: dark;
3658
+ --bg: hsl(213 50% 5%);
3659
+ --bg-soft: hsl(213 46% 7%);
3660
+ --surface: hsl(213 44% 8%);
3661
+ --surface-raised: hsl(213 44% 11%);
3662
+ --surface-muted: hsl(213 32% 14%);
3663
+ --fg: hsl(194 65% 92%);
3664
+ --muted: hsl(203 22% 62%);
3665
+ --faint: hsl(203 18% 50%);
3666
+ --border: hsl(194 48% 23%);
3667
+ --border-soft: hsl(194 45% 18%);
3668
+ --accent: hsl(190 95% 52%);
3669
+ --accent-strong: hsl(190 95% 66%);
3670
+ --accent-soft: hsl(190 95% 52% / 0.15);
3671
+ --success: hsl(150 75% 50%);
3672
+ --success-soft: hsl(150 70% 48% / 0.16);
3673
+ --warning: hsl(28 96% 56%);
3674
+ --warning-soft: hsl(28 96% 52% / 0.16);
3675
+ --danger: hsl(0 84% 64%);
3676
+ --danger-soft: hsl(0 80% 55% / 0.18);
3677
+ --on-accent: hsl(213 70% 6%);
3678
+ --on-danger: hsl(213 70% 6%);
3679
+ --modal-shadow: 0 24px 80px hsl(213 60% 2% / 0.7);
3680
+ --modal-backdrop: hsl(213 60% 3% / 0.7);
3681
+ --qr-surface: #ffffff;
3682
+ --shadow: 0 14px 40px hsl(213 60% 2% / 0.55);
3683
+ --radius: 3px;
3684
+ }
3685
+
3686
+ /* 蔚蓝档案 Blue Archive — deep navy + bright blue primary + halo gold */
3687
+ :root[data-skin="bluearchive"] {
3688
+ color-scheme: dark;
3689
+ --bg: hsl(218 51% 12%);
3690
+ --bg-soft: hsl(218 48% 15%);
3691
+ --surface: hsl(217 48% 18%);
3692
+ --surface-raised: hsl(217 48% 21%);
3693
+ --surface-muted: hsl(218 36% 18%);
3694
+ --fg: hsl(0 0% 96%);
3695
+ --muted: hsl(0 0% 76%);
3696
+ --faint: hsl(213 20% 64%);
3697
+ --border: hsl(213 56% 56% / 0.5);
3698
+ --border-soft: hsl(213 56% 56% / 0.3);
3699
+ --accent: hsl(209 100% 52%);
3700
+ --accent-strong: hsl(209 100% 70%);
3701
+ --accent-soft: hsl(209 100% 56% / 0.18);
3702
+ --success: hsl(150 65% 55%);
3703
+ --success-soft: hsl(150 60% 50% / 0.16);
3704
+ --warning: hsl(47 88% 60%);
3705
+ --warning-soft: hsl(47 88% 56% / 0.18);
3706
+ --danger: hsl(0 80% 66%);
3707
+ --danger-soft: hsl(0 78% 58% / 0.18);
3708
+ --on-accent: hsl(0 0% 100%);
3709
+ --on-danger: hsl(0 0% 100%);
3710
+ --modal-shadow: 0 24px 80px hsl(218 60% 4% / 0.6);
3711
+ --modal-backdrop: hsl(218 51% 8% / 0.55);
3712
+ --qr-surface: #ffffff;
3713
+ --shadow: 0 18px 44px hsl(218 60% 4% / 0.4);
3714
+ --radius: 10px;
3715
+ }
3716
+
3717
+ /* 绝区零 ZZZ — acid lime on charcoal, bold hollow vibe */
3718
+ :root[data-skin="zzz"] {
3719
+ color-scheme: dark;
3720
+ --bg: hsl(240 24% 8%);
3721
+ --bg-soft: hsl(244 26% 10%);
3722
+ --surface: hsl(244 28% 11%);
3723
+ --surface-raised: hsl(244 28% 14%);
3724
+ --surface-muted: hsl(244 22% 15%);
3725
+ --fg: hsl(75 90% 86%);
3726
+ --muted: hsl(280 18% 66%);
3727
+ --faint: hsl(280 14% 52%);
3728
+ --border: hsl(75 60% 40% / 0.55);
3729
+ --border-soft: hsl(75 50% 36% / 0.32);
3730
+ --accent: hsl(75 90% 60%);
3731
+ --accent-strong: hsl(75 95% 74%);
3732
+ --accent-soft: hsl(75 90% 60% / 0.16);
3733
+ --success: hsl(140 75% 55%);
3734
+ --success-soft: hsl(140 70% 50% / 0.16);
3735
+ --warning: hsl(48 100% 62%);
3736
+ --warning-soft: hsl(48 100% 56% / 0.16);
3737
+ --danger: hsl(340 90% 66%);
3738
+ --danger-soft: hsl(340 85% 58% / 0.18);
3739
+ --on-accent: hsl(240 30% 8%);
3740
+ --on-danger: hsl(240 30% 8%);
3741
+ --modal-shadow: 0 24px 80px hsl(240 40% 2% / 0.7);
3742
+ --modal-backdrop: hsl(240 30% 3% / 0.7);
3743
+ --qr-surface: #ffffff;
3744
+ --shadow: 0 14px 40px hsl(240 40% 2% / 0.55);
3745
+ --radius: 4px;
3746
+ }
3747
+
3748
+ /* Terminal-flavoured skins: monospace + glow on brand / page titles. */
3749
+ :root[data-skin="fallout"] .brand strong,
3750
+ :root[data-skin="fallout"] .topbar-title strong,
3751
+ :root[data-skin="fallout"] .page-heading h1,
3752
+ :root[data-skin="prts"] .brand strong,
3753
+ :root[data-skin="prts"] .topbar-title strong,
3754
+ :root[data-skin="prts"] .page-heading h1 {
3755
+ font-family: var(--mono);
3756
+ letter-spacing: 0.5px;
3757
+ }
3758
+ :root[data-skin="fallout"] .page-heading h1 { text-shadow: 0 0 14px hsl(138 92% 52% / 0.4); }
3759
+ :root[data-skin="prts"] .page-heading h1 { text-shadow: 0 0 14px hsl(190 95% 52% / 0.4); }
3760
+
3761
+ /* Genshin/Blue Archive carry a gold secondary — tint the brand mark with it. */
3762
+ :root[data-skin="genshin"] .brand-mark { background: linear-gradient(135deg, hsl(199 70% 42%), hsl(42 78% 58%)); color: #fff; }
3763
+ :root[data-skin="bluearchive"] .brand-mark { background: linear-gradient(135deg, hsl(209 100% 52%), hsl(47 88% 60%)); color: hsl(218 51% 12%); }
3764
+
3765
+ /* ════════════════════════════════════════════════════════════════════════════
3766
+ SKIN BACKGROUNDS — the layered neon-glow / grid backgrounds + decorative art
3767
+ that make each theme recognisable (ported from kaboo's body.<skin>-mode rules).
3768
+ Surfaces are made translucent so the fixed background shows through the chrome
3769
+ and cards. Raster art is referenced from kaboo's CDN (loads on-network; if it
3770
+ 404s off-network the gradient layers still carry the theme).
3771
+ ════════════════════════════════════════════════════════════════════════════ */
3772
+
3773
+ /* translucent surfaces so the fixed page background reads through chrome + cards */
3774
+ :root[data-skin="genshin"] { --surface: hsl(0 0% 100% / 0.74); --surface-raised: hsl(0 0% 100% / 0.88); --surface-muted: hsl(214 48% 95% / 0.7); }
3775
+ :root[data-skin="fallout"] { --surface: hsl(140 26% 8% / 0.7); --surface-raised: hsl(140 26% 11% / 0.82); --surface-muted: hsl(140 22% 13% / 0.64); }
3776
+ :root[data-skin="prts"] { --surface: hsl(213 44% 8% / 0.7); --surface-raised: hsl(213 44% 11% / 0.82); --surface-muted: hsl(213 32% 14% / 0.64); }
3777
+ :root[data-skin="bluearchive"] { --surface: hsl(217 48% 18% / 0.74); --surface-raised: hsl(217 48% 21% / 0.86); --surface-muted: hsl(218 36% 18% / 0.66); }
3778
+ :root[data-skin="zzz"] { --surface: hsl(244 28% 11% / 0.7); --surface-raised: hsl(244 28% 14% / 0.82); --surface-muted: hsl(244 22% 15% / 0.64); }
3779
+
3780
+ /* lift content above any fixed ::before art layer */
3781
+ :root[data-skin="genshin"] .app-shell,
3782
+ :root[data-skin="fallout"] .app-shell,
3783
+ :root[data-skin="prts"] .app-shell,
3784
+ :root[data-skin="bluearchive"] .app-shell,
3785
+ :root[data-skin="zzz"] .app-shell { position: relative; z-index: 0; }
3786
+
3787
+ /* 原神 — gentle-breeze parchment + Teyvat glow */
3788
+ :root[data-skin="genshin"] body {
3789
+ background:
3790
+ linear-gradient(180deg, hsl(216 72% 97% / 0.68) 0%, hsl(202 78% 96% / 0.58) 42%, hsl(42 72% 95% / 0.74) 100%),
3791
+ radial-gradient(circle at 20% 16%, hsl(42 88% 78% / 0.36), transparent 28%),
3792
+ radial-gradient(circle at 82% 26%, hsl(198 86% 74% / 0.34), transparent 30%),
3793
+ radial-gradient(circle at 70% 78%, hsl(154 66% 78% / 0.3), transparent 28%),
3794
+ repeating-linear-gradient(115deg, hsl(42 78% 58% / 0.055) 0 1px, transparent 1px 48px),
3795
+ url('/assets/skins/genshin-breeze.webp');
3796
+ background-position: center, 20% 16%, 82% 26%, 70% 78%, center, center;
3797
+ background-repeat: no-repeat, no-repeat, no-repeat, no-repeat, repeat, no-repeat;
3798
+ background-size: cover, cover, cover, cover, 100% 100%, cover;
3799
+ background-attachment: fixed;
3800
+ }
3801
+
3802
+ /* Fallout — phosphor glow + CRT scanlines, Vault-Boy watermark bottom-right */
3803
+ :root[data-skin="fallout"] body {
3804
+ background:
3805
+ radial-gradient(ellipse at 50% -6%, hsl(138 92% 52% / 0.16), transparent 36%),
3806
+ radial-gradient(ellipse at 86% 90%, hsl(96 80% 46% / 0.1), transparent 34%),
3807
+ linear-gradient(180deg, hsl(140 36% 6%) 0%, hsl(150 34% 4%) 52%, hsl(140 40% 5%) 100%),
3808
+ repeating-linear-gradient(0deg, hsl(138 92% 52% / 0.05) 0 1px, transparent 1px 4px),
3809
+ repeating-linear-gradient(90deg, hsl(138 92% 52% / 0.04) 0 1px, transparent 1px 44px);
3810
+ background-attachment: fixed;
3811
+ }
3812
+ :root[data-skin="fallout"] body::before {
3813
+ content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
3814
+ background: url('/assets/skins/fallout-vaultboy.webp') right -1% bottom -8% / auto min(100vh, 1100px) no-repeat;
3815
+ opacity: 0.12;
3816
+ filter: drop-shadow(0 0 24px hsl(138 92% 52% / 0.35));
3817
+ }
3818
+
3819
+ /* PRTS — Rhodes Island operator console (cyan glow + fine grid) */
3820
+ :root[data-skin="prts"] body {
3821
+ background:
3822
+ radial-gradient(ellipse at 18% 12%, hsl(190 95% 52% / 0.18), transparent 34%),
3823
+ radial-gradient(ellipse at 88% 78%, hsl(28 96% 56% / 0.12), transparent 30%),
3824
+ linear-gradient(180deg, hsl(216 56% 6%) 0%, hsl(213 50% 4%) 58%, hsl(217 56% 5%) 100%),
3825
+ repeating-linear-gradient(90deg, hsl(190 95% 52% / 0.055) 0 1px, transparent 1px 72px),
3826
+ repeating-linear-gradient(0deg, hsl(190 95% 52% / 0.045) 0 1px, transparent 1px 36px);
3827
+ background-attachment: fixed;
3828
+ }
3829
+ :root[data-skin="prts"] body::before {
3830
+ content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
3831
+ background: url('/assets/skins/prts-priestess.webp') right -2% bottom / auto min(94vh, 1000px) no-repeat;
3832
+ opacity: 0.16;
3833
+ filter: saturate(1.1);
3834
+ }
3835
+
3836
+ /* 蔚蓝档案 — halo gradients + soft grid, Kei sunset hero bottom-right */
3837
+ :root[data-skin="bluearchive"] body {
3838
+ background:
3839
+ radial-gradient(ellipse at 14% 12%, hsl(209 100% 34% / 0.34), transparent 36%),
3840
+ radial-gradient(ellipse at 88% 16%, hsl(47 88% 60% / 0.2), transparent 30%),
3841
+ radial-gradient(ellipse at 50% 92%, hsl(213 60% 60% / 0.22), transparent 38%),
3842
+ radial-gradient(ellipse at 86% 78%, hsl(188 76% 56% / 0.16), transparent 32%),
3843
+ linear-gradient(180deg, hsl(218 51% 10%) 0%, hsl(217 48% 16%) 48%, hsl(213 42% 14%) 100%),
3844
+ linear-gradient(90deg, hsl(213 56% 56% / 0.08) 1px, transparent 1px),
3845
+ linear-gradient(0deg, hsl(47 88% 60% / 0.06) 1px, transparent 1px);
3846
+ background-size: cover, cover, cover, cover, cover, 40px 40px, 40px 40px;
3847
+ background-attachment: fixed;
3848
+ }
3849
+ :root[data-skin="bluearchive"] body::before {
3850
+ content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
3851
+ background: url('/assets/skins/bluearchive-hero.webp') right bottom / auto min(86vh, 920px) no-repeat;
3852
+ opacity: 0.2;
3853
+ }
3854
+
3855
+ /* 绝区零 — lime/violet glow + Hollow heatmap pattern */
3856
+ :root[data-skin="zzz"] body {
3857
+ background:
3858
+ radial-gradient(ellipse at 50% -6%, hsl(75 90% 60% / 0.14), transparent 36%),
3859
+ radial-gradient(ellipse at 86% 90%, hsl(280 60% 50% / 0.14), transparent 34%),
3860
+ linear-gradient(180deg, hsl(244 30% 7%) 0%, hsl(252 32% 5%) 52%, hsl(244 30% 7%) 100%),
3861
+ repeating-linear-gradient(0deg, hsl(75 90% 60% / 0.05) 0 1px, transparent 1px 56px),
3862
+ repeating-linear-gradient(90deg, hsl(75 90% 60% / 0.04) 0 1px, transparent 1px 56px);
3863
+ background-attachment: fixed;
3864
+ }
3865
+ :root[data-skin="zzz"] body::before {
3866
+ content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
3867
+ background: url('/assets/skins/zzz-hero.webp') center / cover no-repeat;
3868
+ opacity: 0.62;
3869
+ }
3870
+ /* keep the Hollow heatmap pattern as a faint overlay above the wallpaper */
3871
+ :root[data-skin="zzz"] body::after {
3872
+ content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
3873
+ background: url('/assets/skins/zzz-pattern.svg') center / 420px repeat;
3874
+ opacity: 0.16;
3875
+ }
3876
+
3877
+ /* ════════════════════════════════════════════════════════════════════════════
3878
+ 2077 EASTER EGG — "BREACH PROTOCOL". Over-scroll past the bottom of the page
3879
+ to detonate a full-screen RGB-split glitch storm. Driven by cyber-fx.ts.
3880
+ Ported from kaboo's CyberpunkBreach.
3881
+ ════════════════════════════════════════════════════════════════════════════ */
3882
+ .cyber-breach {
3883
+ position: fixed;
3884
+ inset: 0;
3885
+ z-index: 100000;
3886
+ pointer-events: none;
3887
+ overflow: hidden;
3888
+ }
3889
+
3890
+ .cyber-breach-flash {
3891
+ position: absolute;
3892
+ inset: 0;
3893
+ background: linear-gradient(135deg, hsl(186 100% 52% / 0.5), hsl(330 100% 58% / 0.4));
3894
+ mix-blend-mode: screen;
3895
+ animation: cyber-breach-flash 4.2s ease-out forwards;
3896
+ }
3897
+
3898
+ .cyber-breach-grid {
3899
+ position: absolute;
3900
+ inset: 0;
3901
+ background:
3902
+ repeating-linear-gradient(0deg, hsl(186 100% 52% / 0.12) 0 1px, transparent 1px 3px),
3903
+ repeating-linear-gradient(90deg, hsl(330 100% 58% / 0.1) 0 1px, transparent 1px 38px);
3904
+ animation: cyber-breach-grid 4.2s steps(8, end) forwards;
3905
+ }
3906
+
3907
+ .cyber-breach-shards { position: absolute; inset: 0; }
3908
+
3909
+ .cyber-breach-shard {
3910
+ position: absolute;
3911
+ left: 0;
3912
+ right: 0;
3913
+ background: hsl(var(--hue, 186 100% 52%) / 0.4);
3914
+ mix-blend-mode: screen;
3915
+ opacity: 0;
3916
+ animation: cyber-breach-shard var(--dur, 0.4s) steps(2, end) var(--delay, 0s) infinite;
3917
+ }
3918
+
3919
+ .cyber-breach-banner {
3920
+ position: absolute;
3921
+ left: 0;
3922
+ right: 0;
3923
+ top: 34%;
3924
+ display: flex;
3925
+ flex-direction: column;
3926
+ align-items: center;
3927
+ gap: 6px;
3928
+ padding: 0 16px;
3929
+ text-align: center;
3930
+ font-family: var(--mono);
3931
+ animation: cyber-breach-caption 4.2s ease forwards;
3932
+ }
3933
+
3934
+ .cyber-breach-tag {
3935
+ font-size: clamp(11px, 2vw, 16px);
3936
+ letter-spacing: 0.24em;
3937
+ color: hsl(186 100% 70%);
3938
+ text-shadow: 0 0 12px hsl(186 100% 52% / 0.8);
3939
+ }
3940
+
3941
+ .cyber-breach-caption,
3942
+ .cyber-breach-sub {
3943
+ position: relative;
3944
+ font-weight: 700;
3945
+ letter-spacing: 0.16em;
3946
+ color: hsl(56 96% 84%);
3947
+ }
3948
+
3949
+ .cyber-breach-caption {
3950
+ font-size: clamp(20px, 5vw, 54px);
3951
+ text-shadow: 0 0 18px hsl(330 100% 58% / 0.9), 0 0 4px hsl(186 100% 70% / 0.85);
3952
+ }
3953
+
3954
+ .cyber-breach-sub {
3955
+ font-size: clamp(13px, 2.6vw, 28px);
3956
+ color: hsl(186 96% 78%);
3957
+ text-shadow: 0 0 12px hsl(186 100% 52% / 0.8);
3958
+ }
3959
+
3960
+ .cyber-breach-caption::before,
3961
+ .cyber-breach-caption::after,
3962
+ .cyber-breach-sub::before,
3963
+ .cyber-breach-sub::after {
3964
+ content: attr(data-text);
3965
+ position: absolute;
3966
+ inset: 0;
3967
+ mix-blend-mode: screen;
3968
+ }
3969
+
3970
+ .cyber-breach-caption::before,
3971
+ .cyber-breach-sub::before {
3972
+ color: hsl(330 100% 60%);
3973
+ animation: cyber-breach-glitch-a 0.4s steps(2, end) infinite;
3974
+ }
3975
+
3976
+ .cyber-breach-caption::after,
3977
+ .cyber-breach-sub::after {
3978
+ color: hsl(186 100% 56%);
3979
+ animation: cyber-breach-glitch-b 0.4s steps(2, end) infinite;
3980
+ }
3981
+
3982
+ .cyber-breach-quake .app-shell {
3983
+ animation: cyber-breach-quake 0.76s cubic-bezier(0.36, 0.07, 0.19, 0.97);
3984
+ }
3985
+
3986
+ @keyframes cyber-breach-flash {
3987
+ 0% { opacity: 0; }
3988
+ 3% { opacity: 1; }
3989
+ 18% { opacity: 0.4; }
3990
+ 100% { opacity: 0; }
3991
+ }
3992
+
3993
+ @keyframes cyber-breach-grid {
3994
+ 0% { opacity: 0; transform: translateY(-6px); }
3995
+ 10% { opacity: 1; }
3996
+ 86% { opacity: 0.9; }
3997
+ 100% { opacity: 0; transform: translateY(6px); }
3998
+ }
3999
+
4000
+ @keyframes cyber-breach-shard {
4001
+ 0%, 100% { opacity: 0; transform: translateX(0); }
4002
+ 40% { opacity: 0.85; transform: translateX(var(--shift, 10px)); }
4003
+ 60% { opacity: 0.5; transform: translateX(calc(var(--shift, 10px) * -0.6)); }
4004
+ }
4005
+
4006
+ @keyframes cyber-breach-glitch-a {
4007
+ 0%, 100% { transform: translate(-2px, 0); }
4008
+ 50% { transform: translate(-5px, 1px); }
4009
+ }
4010
+
4011
+ @keyframes cyber-breach-glitch-b {
4012
+ 0%, 100% { transform: translate(2px, 0); }
4013
+ 50% { transform: translate(5px, -1px); }
4014
+ }
4015
+
4016
+ @keyframes cyber-breach-caption {
4017
+ 0%, 16% { opacity: 0; }
4018
+ 30% { opacity: 1; }
4019
+ 84% { opacity: 1; }
4020
+ 100% { opacity: 0; }
4021
+ }
4022
+
4023
+ @keyframes cyber-breach-quake {
4024
+ 10%, 90% { transform: translate(-2px, 1px); }
4025
+ 20%, 80% { transform: translate(4px, -2px); }
4026
+ 30%, 50%, 70% { transform: translate(-7px, 2px); }
4027
+ 40%, 60% { transform: translate(7px, -1px); }
4028
+ }
4029
+
4030
+ @media (prefers-reduced-motion: reduce) {
4031
+ .cyber-breach-flash,
4032
+ .cyber-breach-shards { display: none; }
4033
+ .cyber-breach-grid,
4034
+ .cyber-breach-banner { animation: none; opacity: 1; }
4035
+ .cyber-breach-caption::before,
4036
+ .cyber-breach-caption::after,
4037
+ .cyber-breach-sub::before,
4038
+ .cyber-breach-sub::after { display: none; }
4039
+ .cyber-breach-quake .app-shell { animation: none; }
4040
+ }
4041
+
4042
+ /* ════════════════════════════════════════════════════════════════════════════
4043
+ 七龙珠 Dragon Ball — warm shonen energy: Goku-gi orange + Super-Saiyan gold,
4044
+ sunlit cream sky, and the Dragon Ball orb art bottom-right.
4045
+ Orb art: "Dragon ball.svg" by Frédéric Mahé, CC-BY-3.0, via Wikimedia Commons.
4046
+ ════════════════════════════════════════════════════════════════════════════ */
4047
+ :root[data-skin="dragonball"] {
4048
+ color-scheme: light;
4049
+ --bg: hsl(36 92% 96%);
4050
+ --bg-soft: hsl(33 86% 93%);
4051
+ --surface: hsl(0 0% 100% / 0.8);
4052
+ --surface-raised: hsl(0 0% 100% / 0.92);
4053
+ --surface-muted: hsl(35 80% 93% / 0.78);
4054
+ --fg: hsl(24 50% 17%);
4055
+ --muted: hsl(26 22% 40%);
4056
+ --faint: hsl(28 18% 54%);
4057
+ --border: hsl(32 58% 80%);
4058
+ --border-soft: hsl(33 64% 87%);
4059
+ --accent: hsl(26 95% 48%);
4060
+ --accent-strong: hsl(18 92% 42%);
4061
+ --accent-soft: hsl(35 95% 88%);
4062
+ --success: hsl(145 60% 36%);
4063
+ --success-soft: hsl(145 55% 90%);
4064
+ --warning: hsl(42 100% 44%);
4065
+ --warning-soft: hsl(45 100% 88%);
4066
+ --danger: hsl(2 80% 50%);
4067
+ --danger-soft: hsl(2 85% 95%);
4068
+ --on-accent: hsl(0 0% 100%);
4069
+ --on-danger: hsl(0 0% 100%);
4070
+ --modal-shadow: 0 24px 80px hsl(26 60% 30% / 0.25);
4071
+ --modal-backdrop: hsl(24 50% 17% / 0.4);
4072
+ --qr-surface: #ffffff;
4073
+ --shadow: 0 18px 44px hsl(26 70% 40% / 0.12);
4074
+ --radius: 12px;
4075
+ }
4076
+
4077
+ :root[data-skin="dragonball"] .app-shell { position: relative; z-index: 0; }
4078
+
4079
+ :root[data-skin="dragonball"] body {
4080
+ background:
4081
+ radial-gradient(circle at 18% 14%, hsl(45 100% 75% / 0.45), transparent 30%),
4082
+ radial-gradient(circle at 84% 20%, hsl(26 95% 62% / 0.28), transparent 34%),
4083
+ /* warm veil over the wallpaper so cards + text stay readable */
4084
+ linear-gradient(180deg, hsl(38 92% 96% / 0.78), hsl(30 86% 93% / 0.66) 50%, hsl(45 80% 95% / 0.8)),
4085
+ url('/assets/skins/dragonball-goku.webp') center / cover no-repeat;
4086
+ background-attachment: fixed;
4087
+ }
4088
+
4089
+ :root[data-skin="dragonball"] .brand-mark {
4090
+ background: linear-gradient(135deg, hsl(26 95% 52%), hsl(45 100% 55%));
4091
+ color: #fff;
4092
+ }
4093
+ :root[data-skin="dragonball"] .page-heading h1 {
4094
+ text-shadow: 0 2px 0 hsl(45 100% 72% / 0.55);
4095
+ }
4096
+
4097
+ /* ════════════════════════════════════════════════════════════════════════════
4098
+ 中分鸡 iKun — 唱跳 rap 篮球 meme skin: stage-purple backdrop, basketball
4099
+ orange + hot-pink/cyan spotlight glows, and an original basketball motif.
4100
+ (No real-person photo / no song lyrics — original artwork only.)
4101
+ ════════════════════════════════════════════════════════════════════════════ */
4102
+ :root[data-skin="ikun"] {
4103
+ color-scheme: dark;
4104
+ --bg: hsl(268 38% 8%);
4105
+ --bg-soft: hsl(270 36% 10%);
4106
+ --surface: hsl(268 34% 12% / 0.74);
4107
+ --surface-raised: hsl(268 34% 15% / 0.86);
4108
+ --surface-muted: hsl(268 26% 16% / 0.66);
4109
+ --fg: hsl(40 60% 94%);
4110
+ --muted: hsl(280 16% 66%);
4111
+ --faint: hsl(280 13% 52%);
4112
+ --border: hsl(28 70% 50% / 0.5);
4113
+ --border-soft: hsl(28 60% 46% / 0.3);
4114
+ --accent: hsl(24 95% 55%);
4115
+ --accent-strong: hsl(28 100% 67%);
4116
+ --accent-soft: hsl(24 95% 55% / 0.16);
4117
+ --success: hsl(150 70% 52%);
4118
+ --success-soft: hsl(150 65% 48% / 0.16);
4119
+ --warning: hsl(45 100% 58%);
4120
+ --warning-soft: hsl(45 100% 52% / 0.16);
4121
+ --danger: hsl(330 90% 62%);
4122
+ --danger-soft: hsl(330 85% 58% / 0.18);
4123
+ --on-accent: hsl(268 50% 8%);
4124
+ --on-danger: hsl(268 50% 8%);
4125
+ --modal-shadow: 0 24px 80px hsl(270 50% 2% / 0.7);
4126
+ --modal-backdrop: hsl(270 40% 3% / 0.7);
4127
+ --qr-surface: #ffffff;
4128
+ --shadow: 0 14px 40px hsl(270 50% 2% / 0.55);
4129
+ --radius: 14px;
4130
+ }
4131
+
4132
+ :root[data-skin="ikun"] .app-shell { position: relative; z-index: 0; }
4133
+
4134
+ :root[data-skin="ikun"] body {
4135
+ background:
4136
+ radial-gradient(ellipse at 50% -8%, hsl(24 95% 55% / 0.22), transparent 38%),
4137
+ radial-gradient(ellipse at 12% 92%, hsl(330 90% 60% / 0.16), transparent 36%),
4138
+ radial-gradient(ellipse at 88% 86%, hsl(190 90% 55% / 0.14), transparent 36%),
4139
+ linear-gradient(180deg, hsl(270 40% 8%) 0%, hsl(276 42% 6%) 55%, hsl(268 40% 8%) 100%);
4140
+ background-attachment: fixed;
4141
+ }
4142
+ :root[data-skin="ikun"] body::before {
4143
+ content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
4144
+ background: url('/assets/skins/ikun-hero.webp') center / cover no-repeat;
4145
+ opacity: 0.6;
4146
+ }
4147
+
4148
+ :root[data-skin="ikun"] .brand-mark {
4149
+ background: linear-gradient(135deg, hsl(24 95% 55%), hsl(330 90% 60%));
4150
+ color: #fff;
4151
+ }
4152
+
4153
+ /* ── Skin ↔ PR #123 bridge ───────────────────────────────────────────────────
4154
+ PR #123 hardcodes panel/card backgrounds under [data-theme="dark"] (glass look)
4155
+ instead of using --surface, so dark skins would all look alike. Re-point the
4156
+ prominent surfaces to the active skin's --surface so switching is visible.
4157
+ Appended last → wins over the PR's [data-theme="dark"] rules by source order. */
4158
+ :root:not([data-skin="default"]) .panel,
4159
+ :root:not([data-skin="default"]) .metric-card,
4160
+ :root:not([data-skin="default"]) .bd-card,
4161
+ :root:not([data-skin="default"]) .qcard,
4162
+ :root:not([data-skin="default"]) .mate,
4163
+ :root:not([data-skin="default"]) .session-card,
4164
+ :root:not([data-skin="default"]) .session-board-column,
4165
+ :root:not([data-skin="default"]) .filters {
4166
+ background: var(--surface);
4167
+ }
4168
+ :root:not([data-skin="default"]) .session-board-column > header {
4169
+ background: var(--surface-muted);
4170
+ }
4171
+
4172
+ /* ════════════════════════════════════════════════════════════════════════════
4173
+ SKIN SWITCH-IN INTROS — short, original per-skin transitions (mounted by
4174
+ skin-intro.ts; 2077 uses its own boot loader). All fade out via si-fade.
4175
+ ════════════════════════════════════════════════════════════════════════════ */
4176
+ .skin-intro {
4177
+ position: fixed;
4178
+ inset: 0;
4179
+ z-index: 99999;
4180
+ pointer-events: none;
4181
+ overflow: hidden;
4182
+ display: grid;
4183
+ place-items: center;
4184
+ /* frosted mask so the page dims softly behind the intro (less abrupt) */
4185
+ -webkit-backdrop-filter: blur(7px);
4186
+ backdrop-filter: blur(7px);
4187
+ animation: si-fade var(--si-dur, 2s) ease forwards;
4188
+ }
4189
+ /* ease IN at the start + OUT at the end, instead of popping on instantly */
4190
+ @keyframes si-fade {
4191
+ 0% { opacity: 0; }
4192
+ 11% { opacity: 1; }
4193
+ 80% { opacity: 1; }
4194
+ 100% { opacity: 0; visibility: hidden; }
4195
+ }
4196
+
4197
+ /* —— 原神: "原神,启动" — ZZZ-style text loader in Genshin's cream/teal/gold —— */
4198
+ .si-genshin {
4199
+ background:
4200
+ radial-gradient(circle at 50% 46%, hsl(42 92% 82% / 0.5), transparent 42%),
4201
+ radial-gradient(circle at 50% 46%, hsl(198 80% 74% / 0.4), transparent 60%),
4202
+ linear-gradient(180deg, hsl(214 66% 97% / 0.82), hsl(204 72% 94% / 0.82));
4203
+ }
4204
+ .si-genshin .si-gi-shine {
4205
+ position: absolute; inset: 0;
4206
+ background: linear-gradient(100deg, transparent 36%, hsl(42 95% 82% / 0.6) 48%, hsl(198 92% 86% / 0.55) 52%, transparent 66%);
4207
+ background-size: 250% 100%;
4208
+ animation: si-gi-shine var(--si-dur, 2s) ease forwards;
4209
+ }
4210
+ .si-genshin .si-gi-text {
4211
+ position: relative;
4212
+ font-weight: 900;
4213
+ font-size: clamp(30px, 7vw, 64px);
4214
+ letter-spacing: 0.12em;
4215
+ background: linear-gradient(100deg, hsl(199 76% 40%), hsl(186 72% 46%) 42%, hsl(42 88% 50%));
4216
+ -webkit-background-clip: text;
4217
+ background-clip: text;
4218
+ -webkit-text-fill-color: transparent;
4219
+ color: transparent;
4220
+ filter: drop-shadow(0 2px 0 hsl(0 0% 100% / 0.6)) drop-shadow(0 0 28px hsl(42 90% 62% / 0.6));
4221
+ animation: si-gi-pop var(--si-dur, 2s) cubic-bezier(0.2, 0.9, 0.3, 1) forwards;
4222
+ }
4223
+ @keyframes si-gi-shine {
4224
+ 0% { background-position: 120% 0; opacity: 0; }
4225
+ 25% { opacity: 1; }
4226
+ 75% { background-position: -40% 0; opacity: 1; }
4227
+ 100% { opacity: 0; }
4228
+ }
4229
+ @keyframes si-gi-pop {
4230
+ 0% { transform: scale(0.6); opacity: 0; letter-spacing: 0.5em; }
4231
+ 35% { transform: scale(1.06); opacity: 1; letter-spacing: 0.12em; }
4232
+ 60% { transform: scale(1); }
4233
+ 100% { transform: scale(1); opacity: 1; }
4234
+ }
4235
+ @keyframes si-blink { 0%, 100% { opacity: 0.25; } 50% { opacity: 1; } }
4236
+
4237
+ /* —— 绝区零: TV static / channel-switch "NOW LOADING" —— */
4238
+ .si-zzz { background: rgba(7, 7, 13, 0.85); }
4239
+ .si-zzz .si-static {
4240
+ position: absolute; inset: 0;
4241
+ background:
4242
+ repeating-linear-gradient(0deg, hsl(75 90% 60% / 0.05) 0 2px, transparent 2px 4px),
4243
+ repeating-linear-gradient(90deg, rgba(255,255,255,0.04) 0 2px, transparent 2px 3px);
4244
+ mix-blend-mode: screen;
4245
+ animation: si-zzz-static 0.12s steps(2) infinite, si-zzz-off var(--si-dur, 1.9s) ease forwards;
4246
+ }
4247
+ .si-zzz .si-roll {
4248
+ position: absolute; left: 0; right: 0; height: 14%;
4249
+ background: linear-gradient(180deg, transparent, hsl(75 90% 60% / 0.18), transparent);
4250
+ animation: si-zzz-roll 0.7s linear infinite;
4251
+ }
4252
+ .si-zzz .si-now {
4253
+ position: relative; font-family: var(--mono);
4254
+ font-size: clamp(20px, 5vw, 40px); font-weight: 800; letter-spacing: 0.22em;
4255
+ color: hsl(75 95% 64%); text-shadow: 0 0 18px hsl(75 90% 55% / 0.7);
4256
+ animation: si-blink 0.7s steps(2, end) infinite;
4257
+ }
4258
+ @keyframes si-zzz-static { 0% { transform: translateY(0); } 100% { transform: translateY(-3px); } }
4259
+ @keyframes si-zzz-roll { from { top: -14%; } to { top: 100%; } }
4260
+ @keyframes si-zzz-off { 0%, 78% { transform: scaleY(1); opacity: 1; } 90% { transform: scaleY(0.02); opacity: 1; } 100% { transform: scaleY(0); opacity: 0; } }
4261
+
4262
+ /* —— 七龙珠: 悟空骑筋斗云 whooshing across with speed lines —— */
4263
+ .si-dragonball {
4264
+ background: linear-gradient(180deg, hsl(205 85% 72% / 0.8), hsl(198 80% 86% / 0.78) 60%, hsl(45 90% 92% / 0.8));
4265
+ }
4266
+ .si-dragonball .si-cloud {
4267
+ display: block;
4268
+ width: clamp(150px, 26vw, 280px); height: auto;
4269
+ filter: drop-shadow(0 12px 18px hsl(38 80% 35% / 0.45));
4270
+ animation: si-db-fly var(--si-dur, 1.9s) cubic-bezier(0.4, 0.02, 0.45, 1) forwards;
4271
+ }
4272
+ .si-dragonball .si-speed {
4273
+ position: absolute; inset: 0;
4274
+ background: repeating-linear-gradient(90deg, rgba(255,255,255,0.55) 0 2px, transparent 2px 26px);
4275
+ opacity: 0; mix-blend-mode: overlay;
4276
+ animation: si-db-speed var(--si-dur, 1.9s) ease forwards;
4277
+ }
4278
+ @keyframes si-db-fly {
4279
+ 0% { transform: translateX(-65vw) translateY(22px) rotate(-6deg) scale(0.9); }
4280
+ 50% { transform: translateX(0) translateY(-22px) rotate(3deg) scale(1.05); }
4281
+ 100% { transform: translateX(72vw) translateY(16px) rotate(-4deg) scale(0.92); }
4282
+ }
4283
+ @keyframes si-db-speed { 0% { opacity: 0; } 25%, 70% { opacity: 0.85; } 100% { opacity: 0; } }
4284
+
4285
+ /* —— ikun: bouncing 3D basketball (shaded sphere + only the seams spin) —— */
4286
+ .si-ikun {
4287
+ background:
4288
+ radial-gradient(circle at 50% 46%, hsl(24 95% 55% / 0.22), transparent 46%),
4289
+ linear-gradient(180deg, hsl(270 42% 9% / 0.84), hsl(276 44% 6% / 0.84));
4290
+ }
4291
+ .si-ikun .si-bball {
4292
+ position: relative;
4293
+ width: clamp(108px, 19vw, 168px);
4294
+ aspect-ratio: 1;
4295
+ border-radius: 50%;
4296
+ transform-origin: 50% 100%;
4297
+ background: radial-gradient(circle at 35% 28%, #ffbf80 0%, #ef8033 34%, #cf6020 66%, #8f3d0e 100%);
4298
+ box-shadow:
4299
+ inset -12px -14px 30px rgba(60, 20, 0, 0.55),
4300
+ inset 9px 9px 20px rgba(255, 210, 150, 0.4),
4301
+ 0 20px 32px rgba(0, 0, 0, 0.5);
4302
+ animation: si-ikun-bounce var(--si-dur, 1.9s) cubic-bezier(0.5, 0, 0.5, 1) forwards;
4303
+ }
4304
+ .si-ikun .si-bball::after {
4305
+ content: '';
4306
+ position: absolute;
4307
+ left: 18%; top: 12%;
4308
+ width: 32%; height: 24%;
4309
+ border-radius: 50%;
4310
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.55), transparent 72%);
4311
+ filter: blur(3px);
4312
+ }
4313
+ .si-ikun .si-bball-seams {
4314
+ position: absolute;
4315
+ inset: 0;
4316
+ animation: si-spin 0.6s linear infinite;
4317
+ }
4318
+ .si-ikun .si-bball-seams svg { display: block; width: 100%; height: 100%; }
4319
+ @keyframes si-spin { to { transform: rotate(360deg); } }
4320
+ /* uniform scale only — never distort the circle (no squash → always round) */
4321
+ @keyframes si-ikun-bounce {
4322
+ 0% { transform: translateY(-135%) scale(0.72); }
4323
+ 34% { transform: translateY(0) scale(1); }
4324
+ 52% { transform: translateY(-38%) scale(1); }
4325
+ 70% { transform: translateY(0) scale(1); }
4326
+ 84% { transform: translateY(-13%) scale(1); }
4327
+ 100% { transform: translateY(0) scale(1); }
4328
+ }
4329
+
4330
+ @media (prefers-reduced-motion: reduce) {
4331
+ .skin-intro { display: none; }
4332
+ }