zero-query 1.0.9 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +2 -0
  3. package/cli/args.js +33 -33
  4. package/cli/commands/build-api.js +443 -0
  5. package/cli/commands/build.js +254 -216
  6. package/cli/commands/bundle.js +1228 -1183
  7. package/cli/commands/create.js +137 -121
  8. package/cli/commands/dev/devtools/index.js +56 -56
  9. package/cli/commands/dev/devtools/js/components.js +49 -49
  10. package/cli/commands/dev/devtools/js/core.js +423 -423
  11. package/cli/commands/dev/devtools/js/elements.js +421 -421
  12. package/cli/commands/dev/devtools/js/network.js +166 -166
  13. package/cli/commands/dev/devtools/js/performance.js +73 -73
  14. package/cli/commands/dev/devtools/js/router.js +105 -105
  15. package/cli/commands/dev/devtools/js/source.js +132 -132
  16. package/cli/commands/dev/devtools/js/stats.js +35 -35
  17. package/cli/commands/dev/devtools/js/tabs.js +79 -79
  18. package/cli/commands/dev/devtools/panel.html +95 -95
  19. package/cli/commands/dev/devtools/styles.css +244 -244
  20. package/cli/commands/dev/index.js +107 -107
  21. package/cli/commands/dev/logger.js +75 -75
  22. package/cli/commands/dev/overlay.js +858 -858
  23. package/cli/commands/dev/server.js +220 -167
  24. package/cli/commands/dev/validator.js +94 -94
  25. package/cli/commands/dev/watcher.js +172 -172
  26. package/cli/help.js +114 -112
  27. package/cli/index.js +52 -52
  28. package/cli/scaffold/default/LICENSE +21 -21
  29. package/cli/scaffold/default/app/app.js +207 -207
  30. package/cli/scaffold/default/app/components/about.js +201 -201
  31. package/cli/scaffold/default/app/components/api-demo.js +143 -143
  32. package/cli/scaffold/default/app/components/contact-card.js +231 -231
  33. package/cli/scaffold/default/app/components/contacts/contacts.css +706 -706
  34. package/cli/scaffold/default/app/components/contacts/contacts.html +200 -200
  35. package/cli/scaffold/default/app/components/contacts/contacts.js +196 -196
  36. package/cli/scaffold/default/app/components/counter.js +127 -127
  37. package/cli/scaffold/default/app/components/home.js +249 -249
  38. package/cli/scaffold/default/app/components/not-found.js +16 -16
  39. package/cli/scaffold/default/app/components/playground/playground.css +115 -115
  40. package/cli/scaffold/default/app/components/playground/playground.html +161 -161
  41. package/cli/scaffold/default/app/components/playground/playground.js +116 -116
  42. package/cli/scaffold/default/app/components/todos.js +225 -225
  43. package/cli/scaffold/default/app/components/toolkit/toolkit.css +97 -97
  44. package/cli/scaffold/default/app/components/toolkit/toolkit.html +146 -146
  45. package/cli/scaffold/default/app/components/toolkit/toolkit.js +280 -280
  46. package/cli/scaffold/default/app/routes.js +15 -15
  47. package/cli/scaffold/default/app/store.js +101 -101
  48. package/cli/scaffold/default/global.css +552 -552
  49. package/cli/scaffold/default/index.html +99 -99
  50. package/cli/scaffold/minimal/app/app.js +85 -85
  51. package/cli/scaffold/minimal/app/components/about.js +68 -68
  52. package/cli/scaffold/minimal/app/components/counter.js +122 -122
  53. package/cli/scaffold/minimal/app/components/home.js +68 -68
  54. package/cli/scaffold/minimal/app/components/not-found.js +16 -16
  55. package/cli/scaffold/minimal/app/routes.js +9 -9
  56. package/cli/scaffold/minimal/app/store.js +36 -36
  57. package/cli/scaffold/minimal/global.css +300 -300
  58. package/cli/scaffold/minimal/index.html +44 -44
  59. package/cli/scaffold/ssr/app/app.js +41 -41
  60. package/cli/scaffold/ssr/app/components/about.js +55 -55
  61. package/cli/scaffold/ssr/app/components/blog/index.js +65 -65
  62. package/cli/scaffold/ssr/app/components/blog/post.js +86 -86
  63. package/cli/scaffold/ssr/app/components/home.js +37 -37
  64. package/cli/scaffold/ssr/app/components/not-found.js +15 -15
  65. package/cli/scaffold/ssr/app/routes.js +8 -8
  66. package/cli/scaffold/ssr/global.css +228 -228
  67. package/cli/scaffold/ssr/index.html +37 -37
  68. package/cli/scaffold/ssr/package.json +8 -8
  69. package/cli/scaffold/ssr/server/data/posts.js +144 -144
  70. package/cli/scaffold/ssr/server/index.js +213 -213
  71. package/cli/scaffold/webrtc/app/app.js +11 -0
  72. package/cli/scaffold/webrtc/app/components/video-room.js +295 -0
  73. package/cli/scaffold/webrtc/app/lib/room.js +252 -0
  74. package/cli/scaffold/webrtc/assets/.gitkeep +0 -0
  75. package/cli/scaffold/webrtc/global.css +250 -0
  76. package/cli/scaffold/webrtc/index.html +21 -0
  77. package/cli/utils.js +305 -287
  78. package/dist/API.md +7264 -0
  79. package/dist/zquery.dist.zip +0 -0
  80. package/dist/zquery.js +10313 -6252
  81. package/dist/zquery.min.js +8 -601
  82. package/index.d.ts +570 -365
  83. package/index.js +311 -232
  84. package/package.json +76 -69
  85. package/src/component.js +1709 -1454
  86. package/src/core.js +921 -921
  87. package/src/diff.js +497 -497
  88. package/src/errors.js +209 -209
  89. package/src/expression.js +922 -922
  90. package/src/http.js +242 -242
  91. package/src/package.json +1 -1
  92. package/src/reactive.js +255 -254
  93. package/src/router.js +843 -773
  94. package/src/ssr.js +418 -418
  95. package/src/store.js +318 -272
  96. package/src/utils.js +515 -515
  97. package/src/webrtc/e2ee.js +351 -0
  98. package/src/webrtc/errors.js +116 -0
  99. package/src/webrtc/ice.js +301 -0
  100. package/src/webrtc/index.js +131 -0
  101. package/src/webrtc/joinToken.js +119 -0
  102. package/src/webrtc/observe.js +172 -0
  103. package/src/webrtc/peer.js +351 -0
  104. package/src/webrtc/reactive.js +268 -0
  105. package/src/webrtc/room.js +625 -0
  106. package/src/webrtc/sdp.js +302 -0
  107. package/src/webrtc/sfu/index.js +43 -0
  108. package/src/webrtc/sfu/livekit.js +131 -0
  109. package/src/webrtc/sfu/mediasoup.js +150 -0
  110. package/src/webrtc/signaling.js +373 -0
  111. package/src/webrtc/turn.js +237 -0
  112. package/tests/_helpers/webrtcFakes.js +289 -0
  113. package/tests/audit.test.js +4158 -4158
  114. package/tests/cli.test.js +1136 -1023
  115. package/tests/compare.test.js +497 -0
  116. package/tests/component.test.js +3969 -3938
  117. package/tests/core.test.js +1910 -1910
  118. package/tests/dev-server.test.js +489 -0
  119. package/tests/diff.test.js +1416 -1416
  120. package/tests/docs.test.js +1664 -0
  121. package/tests/electron-features.test.js +864 -0
  122. package/tests/errors.test.js +619 -619
  123. package/tests/expression.test.js +1056 -1056
  124. package/tests/http.test.js +648 -648
  125. package/tests/reactive.test.js +819 -819
  126. package/tests/router.test.js +2327 -2327
  127. package/tests/ssr.test.js +870 -870
  128. package/tests/store.test.js +830 -830
  129. package/tests/test-minifier.js +153 -153
  130. package/tests/test-ssr.js +27 -27
  131. package/tests/utils.test.js +1377 -1377
  132. package/tests/webrtc/e2ee.test.js +283 -0
  133. package/tests/webrtc/ice.test.js +202 -0
  134. package/tests/webrtc/joinToken.test.js +89 -0
  135. package/tests/webrtc/observe.test.js +111 -0
  136. package/tests/webrtc/peer.test.js +373 -0
  137. package/tests/webrtc/reactive.test.js +235 -0
  138. package/tests/webrtc/room.test.js +406 -0
  139. package/tests/webrtc/sdp.test.js +151 -0
  140. package/tests/webrtc/sfu-livekit.test.js +119 -0
  141. package/tests/webrtc/sfu.test.js +160 -0
  142. package/tests/webrtc/signaling.test.js +251 -0
  143. package/tests/webrtc/turn.test.js +256 -0
  144. package/types/collection.d.ts +383 -383
  145. package/types/component.d.ts +186 -186
  146. package/types/errors.d.ts +135 -135
  147. package/types/http.d.ts +92 -92
  148. package/types/misc.d.ts +201 -201
  149. package/types/reactive.d.ts +98 -98
  150. package/types/router.d.ts +190 -190
  151. package/types/ssr.d.ts +102 -102
  152. package/types/store.d.ts +146 -145
  153. package/types/utils.d.ts +245 -245
  154. package/types/webrtc.d.ts +653 -0
@@ -1,552 +1,552 @@
1
- /* global.css - responsive scaffold styles
2
- *
3
- * Uses CSS custom properties for easy theming. Feel free to modify.
4
- * Dark theme by default, light theme via [data-theme="light"].
5
- */
6
-
7
- /* -- Theme Variables -- */
8
- :root {
9
- --bg: #0d1117;
10
- --bg-surface: #161b22;
11
- --bg-card: rgba(22, 27, 34, 0.55);
12
- --bg-hover: #21262d;
13
- --border: rgba(48, 54, 61, 0.45);
14
- --text: #e6edf3;
15
- --text-muted: #7d8590;
16
- --accent: #58a6ff;
17
- --accent-hover:#79c0ff;
18
- --accent-soft: rgba(88, 166, 255, 0.10);
19
- --accent-glow: rgba(88, 166, 255, 0.06);
20
- --success: #3fb950;
21
- --danger: #f85149;
22
- --info: #58a6ff;
23
- --warning: #d29922;
24
- --radius: 8px;
25
- --radius-lg: 12px;
26
- --sidebar-w: 240px;
27
- --sidebar-bg: var(--bg-surface);
28
- --sidebar-border: var(--border);
29
- --topbar-h: 56px;
30
- --font: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
31
- --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
32
- }
33
-
34
- [data-theme="light"] {
35
- --bg: #f6f8fa;
36
- --bg-surface: #ffffff;
37
- --bg-card: rgba(255, 255, 255, 0.8);
38
- --bg-hover: #eaeef2;
39
- --border: rgba(208, 215, 222, 0.65);
40
- --text: #1f2328;
41
- --text-muted: #656d76;
42
- --accent: #0969da;
43
- --accent-hover:#0550ae;
44
- --accent-soft: rgba(9,105,218,0.08);
45
- --accent-glow: rgba(9,105,218,0.04);
46
- --sidebar-bg: #eef2f9;
47
- --sidebar-border: rgba(9, 105, 218, 0.12);
48
- --success: #1a7f37;
49
- --danger: #cf222e;
50
- --info: #0969da;
51
- --warning: #9a6700;
52
- }
53
-
54
- /* -- Reset -- */
55
- *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
56
- html { scroll-behavior: smooth; scrollbar-width: thin; scrollbar-color: #30363d transparent; }
57
- ::-webkit-scrollbar { width: 6px; height: 4px; }
58
- ::-webkit-scrollbar-track { background: transparent; }
59
- ::-webkit-scrollbar-thumb { background: #30363d; border-radius: 4px; }
60
- ::-webkit-scrollbar-thumb:hover { background: #484f58; }
61
-
62
- body {
63
- font-family: var(--font);
64
- font-size: 15px;
65
- line-height: 1.6;
66
- color: var(--text);
67
- background: var(--bg);
68
- overflow-x: hidden;
69
- }
70
-
71
- a { color: var(--accent); text-decoration: none; transition: color .15s; }
72
- a:hover { color: var(--accent-hover); }
73
- code { background: var(--bg-hover); padding: 2px 6px; border-radius: 4px; font-size: 0.85em; }
74
-
75
- /* -- Sidebar -- */
76
- .sidebar {
77
- position: fixed;
78
- top: 0; left: 0; bottom: 0;
79
- width: var(--sidebar-w);
80
- background: var(--sidebar-bg);
81
- border-right: 1px solid var(--sidebar-border);
82
- display: flex;
83
- flex-direction: column;
84
- z-index: 100;
85
- transition: transform 0.3s var(--ease-out);
86
- }
87
-
88
- .sidebar-header {
89
- padding: 1.25rem 1.25rem 1rem;
90
- border-bottom: 1px solid var(--border);
91
- }
92
-
93
- .brand {
94
- font-size: 1.15rem;
95
- font-weight: 700;
96
- color: var(--text);
97
- letter-spacing: -0.02em;
98
- }
99
-
100
- .sidebar-nav {
101
- flex: 1;
102
- padding: 0.75rem 0;
103
- overflow-y: auto;
104
- }
105
-
106
- .nav-link {
107
- display: flex;
108
- align-items: center;
109
- gap: 0.6rem;
110
- padding: 0.6rem 1.25rem;
111
- color: var(--text-muted);
112
- font-size: 0.9rem;
113
- font-weight: 500;
114
- transition: all 0.15s ease;
115
- border-left: 3px solid transparent;
116
- cursor: pointer;
117
- user-select: none;
118
- }
119
-
120
- .nav-link:hover {
121
- color: var(--text);
122
- background: var(--bg-hover);
123
- }
124
-
125
- .nav-link.active {
126
- color: var(--accent);
127
- background: var(--accent-soft);
128
- border-left-color: var(--accent);
129
- }
130
-
131
- .nav-icon { display: inline-flex; align-items: center; justify-content: center; width: 1.4rem; }
132
- .nav-icon svg { width: 18px; height: 18px; flex-shrink: 0; }
133
-
134
- .sidebar-footer {
135
- padding: 0.75rem 1.25rem;
136
- border-top: 1px solid var(--border);
137
- color: var(--text-muted);
138
- font-size: 0.8rem;
139
- }
140
-
141
- [data-theme="light"] .sidebar {
142
- background: var(--sidebar-bg);
143
- border-right-color: var(--sidebar-border);
144
- }
145
-
146
- /* -- Sidebar Contacts (live status) -- */
147
- .sidebar-contacts {
148
- border-top: 1px solid var(--border);
149
- padding: 0.5rem 0;
150
- }
151
-
152
- .sc-header {
153
- display: flex;
154
- align-items: center;
155
- gap: 0.35rem;
156
- padding: 0.25rem 1.25rem 0.45rem;
157
- font-size: 0.72rem;
158
- font-weight: 600;
159
- text-transform: uppercase;
160
- letter-spacing: 0.04em;
161
- color: var(--text-muted);
162
- }
163
-
164
- .sc-list {
165
- display: flex;
166
- flex-direction: column;
167
- gap: 1px;
168
- }
169
-
170
- .sc-item {
171
- display: flex;
172
- align-items: center;
173
- gap: 0.55rem;
174
- padding: 0.3rem 1.25rem;
175
- position: relative;
176
- cursor: pointer;
177
- border-radius: var(--radius);
178
- transition: background 0.15s ease;
179
- }
180
-
181
- .sc-item:hover {
182
- background: var(--bg-hover);
183
- }
184
-
185
- .sc-avatar {
186
- width: 22px;
187
- height: 22px;
188
- border-radius: 50%;
189
- display: flex;
190
- align-items: center;
191
- justify-content: center;
192
- font-size: 0.6rem;
193
- font-weight: 700;
194
- color: #fff;
195
- flex-shrink: 0;
196
- }
197
-
198
- .sc-dot {
199
- width: 7px;
200
- height: 7px;
201
- border-radius: 50%;
202
- flex-shrink: 0;
203
- margin-left: -14px;
204
- margin-top: 12px;
205
- position: relative;
206
- z-index: 1;
207
- border: 1.5px solid var(--bg-surface);
208
- }
209
-
210
- .sc-dot-online { background: var(--success); box-shadow: 0 0 4px rgba(63, 185, 80, 0.5); }
211
- .sc-dot-away { background: var(--warning); }
212
- .sc-dot-offline { background: var(--text-muted); opacity: 0.4; }
213
-
214
- .sc-name {
215
- font-size: 0.8rem;
216
- color: var(--text);
217
- white-space: nowrap;
218
- overflow: hidden;
219
- text-overflow: ellipsis;
220
- }
221
-
222
- /* -- Sidebar Stats Panel -- */
223
- .sidebar-stats {
224
- border-top: 1px solid var(--border);
225
- }
226
-
227
- .sidebar-stats-toggle {
228
- display: flex;
229
- align-items: center;
230
- gap: 0.45rem;
231
- width: 100%;
232
- padding: 0.6rem 1.25rem;
233
- background: none;
234
- border: none;
235
- color: var(--text-muted);
236
- font-size: 0.78rem;
237
- font-weight: 600;
238
- font-family: inherit;
239
- text-transform: uppercase;
240
- letter-spacing: 0.04em;
241
- cursor: pointer;
242
- transition: all 0.15s ease;
243
- }
244
-
245
- .sidebar-stats-toggle:hover {
246
- color: var(--text);
247
- background: var(--bg-hover);
248
- }
249
-
250
- .sidebar-stats-arrow {
251
- margin-left: auto;
252
- font-size: 0.7rem;
253
- transition: transform 0.15s ease;
254
- }
255
-
256
- .sidebar-stats-arrow.open {
257
- transform: rotate(90deg);
258
- }
259
-
260
- .sidebar-stats-body {
261
- padding: 0.25rem 1.25rem 0.65rem;
262
- }
263
-
264
- .ss-group-label {
265
- font-size: 0.68rem;
266
- font-weight: 600;
267
- text-transform: uppercase;
268
- letter-spacing: 0.05em;
269
- color: var(--text-muted);
270
- opacity: 0.6;
271
- padding: 0.4rem 0 0.1rem;
272
- margin-top: 0.15rem;
273
- border-top: 1px solid rgba(255,255,255,0.04);
274
- }
275
-
276
- .ss-row {
277
- display: flex;
278
- justify-content: space-between;
279
- align-items: center;
280
- padding: 0.2rem 0;
281
- }
282
-
283
- .ss-row.ss-indent {
284
- padding-left: 0.5rem;
285
- }
286
-
287
- .ss-label {
288
- font-size: 0.78rem;
289
- color: var(--text-muted);
290
- }
291
-
292
- .ss-value {
293
- font-size: 0.8rem;
294
- font-weight: 600;
295
- color: var(--accent);
296
- font-variant-numeric: tabular-nums;
297
- }
298
-
299
- /* -- Mobile Top Bar -- */
300
- .topbar {
301
- display: none;
302
- position: fixed;
303
- top: 0; left: 0; right: 0;
304
- height: var(--topbar-h);
305
- background: var(--bg-surface);
306
- border-bottom: 1px solid var(--border);
307
- align-items: center;
308
- padding: 0 1rem;
309
- z-index: 101;
310
- }
311
-
312
- .topbar-brand {
313
- font-weight: 700;
314
- font-size: 1.05rem;
315
- margin-left: 0.75rem;
316
- }
317
-
318
- /* Hamburger */
319
- .hamburger {
320
- display: flex;
321
- flex-direction: column;
322
- justify-content: center;
323
- gap: 5px;
324
- width: 32px;
325
- height: 32px;
326
- background: none;
327
- border: none;
328
- cursor: pointer;
329
- padding: 4px;
330
- }
331
-
332
- .hamburger span {
333
- display: block;
334
- height: 2px;
335
- background: var(--text);
336
- border-radius: 2px;
337
- transition: all 0.25s var(--ease-out);
338
- }
339
-
340
- .hamburger.active span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
341
- .hamburger.active span:nth-child(2) { opacity: 0; }
342
- .hamburger.active span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
343
-
344
- /* Overlay */
345
- .overlay {
346
- display: none;
347
- position: fixed;
348
- inset: 0;
349
- background: rgba(0, 0, 0, 0.5);
350
- z-index: 99;
351
- opacity: 0;
352
- transition: opacity 0.3s ease;
353
- }
354
-
355
- .overlay.visible { display: block; opacity: 1; }
356
-
357
- /* -- Main Content -- */
358
- .content {
359
- margin-left: auto;
360
- margin-right: auto;
361
- padding: 2.5rem 3rem 2.5rem calc(var(--sidebar-w) + 3rem);
362
- max-width: calc(1200px + var(--sidebar-w));
363
- min-height: 100vh;
364
- }
365
-
366
- /* -- Page Header -- */
367
- .page-header { margin-bottom: 2rem; }
368
- .page-header h1 { font-size: 1.85rem; font-weight: 700; margin-bottom: 0.35rem; letter-spacing: -0.02em; }
369
- .page-header.center { text-align: center; padding: 4rem 1rem; }
370
- .subtitle { color: var(--text-muted); font-size: 0.95rem; }
371
-
372
- /* -- Cards -- */
373
- .card {
374
- background: var(--bg-card);
375
- border: 1px solid var(--border);
376
- border-radius: var(--radius-lg);
377
- padding: 1.5rem;
378
- margin-bottom: 1.25rem;
379
- backdrop-filter: blur(8px);
380
- -webkit-backdrop-filter: blur(8px);
381
- transition: border-color .2s ease, box-shadow .2s ease;
382
- min-width: 0;
383
- }
384
-
385
- .card:hover {
386
- border-color: rgba(88, 166, 255, 0.15);
387
- box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(88, 166, 255, 0.04);
388
- }
389
-
390
- .card h3 { font-size: 1.1rem; margin-bottom: 0.5rem; }
391
- .card p { color: var(--text-muted); font-size: 0.9rem; margin-bottom: 0.75rem; }
392
- .card-accent { border-left: 3px solid var(--accent); }
393
- .card-muted { background: var(--bg-surface); backdrop-filter: none; }
394
- .card-error { background: rgba(248,113,113,0.08); border-color: var(--danger); color: var(--danger); }
395
-
396
- .card-grid {
397
- display: grid;
398
- grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr));
399
- gap: 1rem;
400
- margin-bottom: 1.25rem;
401
- }
402
-
403
- .card-grid > .card {
404
- display: flex;
405
- flex-direction: column;
406
- margin-bottom: 0;
407
- }
408
-
409
- .card-grid > .card > .btn,
410
- .card-grid > .card > a.btn {
411
- margin-top: auto;
412
- align-self: flex-start;
413
- }
414
-
415
- /* -- Buttons -- */
416
- .btn {
417
- display: inline-flex;
418
- align-items: center;
419
- gap: 0.4rem;
420
- padding: 0.5rem 1.1rem;
421
- font-size: 0.9rem;
422
- font-weight: 500;
423
- border: none;
424
- border-radius: var(--radius);
425
- cursor: pointer;
426
- transition: all 0.15s ease;
427
- text-decoration: none;
428
- font-family: inherit;
429
- }
430
-
431
- .btn:active { transform: scale(0.97); }
432
-
433
- .btn-primary { background: var(--accent); color: #fff; }
434
- .btn-primary:hover { background: var(--accent-hover); color: #fff; box-shadow: 0 0 20px var(--accent-glow); }
435
-
436
- .btn-outline { background: transparent; border: 1px solid var(--border); color: var(--accent); }
437
- .btn-outline:hover { border-color: var(--accent); background: var(--accent-soft); }
438
-
439
- .btn-ghost { background: transparent; color: var(--text-muted); }
440
- .btn-ghost:hover { color: var(--text); background: var(--bg-hover); }
441
-
442
- .btn-sm { padding: 0.35rem 0.75rem; font-size: 0.82rem; }
443
-
444
- /* -- Inputs -- */
445
- .input {
446
- width: 100%;
447
- padding: 0.55rem 0.85rem;
448
- background: var(--bg);
449
- border: 1px solid var(--border);
450
- border-radius: var(--radius);
451
- color: var(--text);
452
- font-size: 0.9rem;
453
- font-family: inherit;
454
- outline: none;
455
- transition: border-color 0.2s ease, box-shadow 0.2s ease;
456
- }
457
-
458
- .input:hover { border-color: rgba(88,166,255,0.2); }
459
- .input:focus { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(88,166,255,0.1), 0 0 12px var(--accent-glow); }
460
- .input-sm { width: auto; padding: 0.35rem 0.6rem; font-size: 0.82rem; }
461
-
462
- /* -- About -- */
463
- .feature-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr)); gap: 0.5rem; }
464
- .feature-item {
465
- display: flex;
466
- flex-direction: column;
467
- padding: 0.65rem 0.85rem;
468
- background: var(--bg-hover);
469
- border-radius: var(--radius);
470
- font-size: 0.85rem;
471
- transition: background .15s;
472
- }
473
- .feature-item:hover { background: var(--accent-soft); }
474
- .feature-item strong { color: var(--accent); font-size: 0.82rem; margin-bottom: 0.1rem; }
475
- .feature-item span { color: var(--text-muted); font-size: 0.8rem; }
476
-
477
- .next-steps { padding-left: 1.25rem; }
478
- .next-steps li { margin-bottom: 0.4rem; font-size: 0.9rem; color: var(--text-muted); }
479
- .next-steps a { color: var(--accent); }
480
-
481
- .muted { color: var(--text-muted); }
482
-
483
- /* -- Toast Notifications -- */
484
- .toast-container {
485
- position: fixed;
486
- bottom: 1.5rem;
487
- right: 1.5rem;
488
- display: flex;
489
- flex-direction: column;
490
- gap: 0.5rem;
491
- z-index: 200;
492
- pointer-events: none;
493
- }
494
-
495
- .toast {
496
- padding: 0.65rem 1.1rem;
497
- border-radius: var(--radius);
498
- font-size: 0.85rem;
499
- font-weight: 500;
500
- color: #fff;
501
- animation: toast-in 0.3s var(--ease-out);
502
- pointer-events: auto;
503
- backdrop-filter: blur(8px);
504
- }
505
-
506
- .toast-success { background: rgba(63,185,80,0.9); }
507
- .toast-error { background: rgba(248,81,73,0.9); }
508
- .toast-info { background: rgba(88,166,255,0.9); }
509
-
510
- .toast-exit { animation: toast-out 0.3s ease forwards; }
511
-
512
- @keyframes toast-in { from { opacity: 0; transform: translateY(10px) scale(0.95); } to { opacity: 1; transform: translateY(0) scale(1); } }
513
- @keyframes toast-out { from { opacity: 1; transform: translateY(0) scale(1); } to { opacity: 0; transform: translateY(-10px) scale(0.95); } }
514
-
515
- /* -- Route Transition -- */
516
- z-outlet { display: block; animation: fade-in 0.25s var(--ease-out); }
517
- @keyframes fade-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
518
-
519
- /* -- Responsive: Mobile -- */
520
- @media (max-width: 768px) {
521
- .sidebar {
522
- transform: translateX(-100%);
523
- width: 260px;
524
- }
525
- .sidebar.open { transform: translateX(0); }
526
-
527
- .topbar { display: flex; }
528
-
529
- .content {
530
- margin-left: 0;
531
- padding: 1.25rem;
532
- padding-top: calc(var(--topbar-h) + 1.25rem);
533
- max-width: 100%;
534
- overflow-x: hidden;
535
- }
536
-
537
- .card { padding: 1.15rem; }
538
- .card-grid { grid-template-columns: 1fr; }
539
- .feature-grid { grid-template-columns: 1fr; }
540
-
541
- .page-header h1 { font-size: 1.4rem; }
542
- .page-header.center { padding: 2rem 0.75rem; }
543
-
544
- .toast-container { right: 0.75rem; left: 0.75rem; align-items: flex-end; }
545
- }
546
-
547
- @media (max-width: 480px) {
548
- .content { padding: 0.75rem; padding-top: calc(var(--topbar-h) + 0.75rem); }
549
- .card { padding: 1rem; }
550
- .page-header h1 { font-size: 1.25rem; }
551
- .subtitle { font-size: 0.85rem; }
552
- }
1
+ /* global.css - responsive scaffold styles
2
+ *
3
+ * Uses CSS custom properties for easy theming. Feel free to modify.
4
+ * Dark theme by default, light theme via [data-theme="light"].
5
+ */
6
+
7
+ /* -- Theme Variables -- */
8
+ :root {
9
+ --bg: #0d1117;
10
+ --bg-surface: #161b22;
11
+ --bg-card: rgba(22, 27, 34, 0.55);
12
+ --bg-hover: #21262d;
13
+ --border: rgba(48, 54, 61, 0.45);
14
+ --text: #e6edf3;
15
+ --text-muted: #7d8590;
16
+ --accent: #58a6ff;
17
+ --accent-hover:#79c0ff;
18
+ --accent-soft: rgba(88, 166, 255, 0.10);
19
+ --accent-glow: rgba(88, 166, 255, 0.06);
20
+ --success: #3fb950;
21
+ --danger: #f85149;
22
+ --info: #58a6ff;
23
+ --warning: #d29922;
24
+ --radius: 8px;
25
+ --radius-lg: 12px;
26
+ --sidebar-w: 240px;
27
+ --sidebar-bg: var(--bg-surface);
28
+ --sidebar-border: var(--border);
29
+ --topbar-h: 56px;
30
+ --font: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
31
+ --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
32
+ }
33
+
34
+ [data-theme="light"] {
35
+ --bg: #f6f8fa;
36
+ --bg-surface: #ffffff;
37
+ --bg-card: rgba(255, 255, 255, 0.8);
38
+ --bg-hover: #eaeef2;
39
+ --border: rgba(208, 215, 222, 0.65);
40
+ --text: #1f2328;
41
+ --text-muted: #656d76;
42
+ --accent: #0969da;
43
+ --accent-hover:#0550ae;
44
+ --accent-soft: rgba(9,105,218,0.08);
45
+ --accent-glow: rgba(9,105,218,0.04);
46
+ --sidebar-bg: #eef2f9;
47
+ --sidebar-border: rgba(9, 105, 218, 0.12);
48
+ --success: #1a7f37;
49
+ --danger: #cf222e;
50
+ --info: #0969da;
51
+ --warning: #9a6700;
52
+ }
53
+
54
+ /* -- Reset -- */
55
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
56
+ html { scroll-behavior: smooth; scrollbar-width: thin; scrollbar-color: #30363d transparent; }
57
+ ::-webkit-scrollbar { width: 6px; height: 4px; }
58
+ ::-webkit-scrollbar-track { background: transparent; }
59
+ ::-webkit-scrollbar-thumb { background: #30363d; border-radius: 4px; }
60
+ ::-webkit-scrollbar-thumb:hover { background: #484f58; }
61
+
62
+ body {
63
+ font-family: var(--font);
64
+ font-size: 15px;
65
+ line-height: 1.6;
66
+ color: var(--text);
67
+ background: var(--bg);
68
+ overflow-x: hidden;
69
+ }
70
+
71
+ a { color: var(--accent); text-decoration: none; transition: color .15s; }
72
+ a:hover { color: var(--accent-hover); }
73
+ code { background: var(--bg-hover); padding: 2px 6px; border-radius: 4px; font-size: 0.85em; }
74
+
75
+ /* -- Sidebar -- */
76
+ .sidebar {
77
+ position: fixed;
78
+ top: 0; left: 0; bottom: 0;
79
+ width: var(--sidebar-w);
80
+ background: var(--sidebar-bg);
81
+ border-right: 1px solid var(--sidebar-border);
82
+ display: flex;
83
+ flex-direction: column;
84
+ z-index: 100;
85
+ transition: transform 0.3s var(--ease-out);
86
+ }
87
+
88
+ .sidebar-header {
89
+ padding: 1.25rem 1.25rem 1rem;
90
+ border-bottom: 1px solid var(--border);
91
+ }
92
+
93
+ .brand {
94
+ font-size: 1.15rem;
95
+ font-weight: 700;
96
+ color: var(--text);
97
+ letter-spacing: -0.02em;
98
+ }
99
+
100
+ .sidebar-nav {
101
+ flex: 1;
102
+ padding: 0.75rem 0;
103
+ overflow-y: auto;
104
+ }
105
+
106
+ .nav-link {
107
+ display: flex;
108
+ align-items: center;
109
+ gap: 0.6rem;
110
+ padding: 0.6rem 1.25rem;
111
+ color: var(--text-muted);
112
+ font-size: 0.9rem;
113
+ font-weight: 500;
114
+ transition: all 0.15s ease;
115
+ border-left: 3px solid transparent;
116
+ cursor: pointer;
117
+ user-select: none;
118
+ }
119
+
120
+ .nav-link:hover {
121
+ color: var(--text);
122
+ background: var(--bg-hover);
123
+ }
124
+
125
+ .nav-link.active {
126
+ color: var(--accent);
127
+ background: var(--accent-soft);
128
+ border-left-color: var(--accent);
129
+ }
130
+
131
+ .nav-icon { display: inline-flex; align-items: center; justify-content: center; width: 1.4rem; }
132
+ .nav-icon svg { width: 18px; height: 18px; flex-shrink: 0; }
133
+
134
+ .sidebar-footer {
135
+ padding: 0.75rem 1.25rem;
136
+ border-top: 1px solid var(--border);
137
+ color: var(--text-muted);
138
+ font-size: 0.8rem;
139
+ }
140
+
141
+ [data-theme="light"] .sidebar {
142
+ background: var(--sidebar-bg);
143
+ border-right-color: var(--sidebar-border);
144
+ }
145
+
146
+ /* -- Sidebar Contacts (live status) -- */
147
+ .sidebar-contacts {
148
+ border-top: 1px solid var(--border);
149
+ padding: 0.5rem 0;
150
+ }
151
+
152
+ .sc-header {
153
+ display: flex;
154
+ align-items: center;
155
+ gap: 0.35rem;
156
+ padding: 0.25rem 1.25rem 0.45rem;
157
+ font-size: 0.72rem;
158
+ font-weight: 600;
159
+ text-transform: uppercase;
160
+ letter-spacing: 0.04em;
161
+ color: var(--text-muted);
162
+ }
163
+
164
+ .sc-list {
165
+ display: flex;
166
+ flex-direction: column;
167
+ gap: 1px;
168
+ }
169
+
170
+ .sc-item {
171
+ display: flex;
172
+ align-items: center;
173
+ gap: 0.55rem;
174
+ padding: 0.3rem 1.25rem;
175
+ position: relative;
176
+ cursor: pointer;
177
+ border-radius: var(--radius);
178
+ transition: background 0.15s ease;
179
+ }
180
+
181
+ .sc-item:hover {
182
+ background: var(--bg-hover);
183
+ }
184
+
185
+ .sc-avatar {
186
+ width: 22px;
187
+ height: 22px;
188
+ border-radius: 50%;
189
+ display: flex;
190
+ align-items: center;
191
+ justify-content: center;
192
+ font-size: 0.6rem;
193
+ font-weight: 700;
194
+ color: #fff;
195
+ flex-shrink: 0;
196
+ }
197
+
198
+ .sc-dot {
199
+ width: 7px;
200
+ height: 7px;
201
+ border-radius: 50%;
202
+ flex-shrink: 0;
203
+ margin-left: -14px;
204
+ margin-top: 12px;
205
+ position: relative;
206
+ z-index: 1;
207
+ border: 1.5px solid var(--bg-surface);
208
+ }
209
+
210
+ .sc-dot-online { background: var(--success); box-shadow: 0 0 4px rgba(63, 185, 80, 0.5); }
211
+ .sc-dot-away { background: var(--warning); }
212
+ .sc-dot-offline { background: var(--text-muted); opacity: 0.4; }
213
+
214
+ .sc-name {
215
+ font-size: 0.8rem;
216
+ color: var(--text);
217
+ white-space: nowrap;
218
+ overflow: hidden;
219
+ text-overflow: ellipsis;
220
+ }
221
+
222
+ /* -- Sidebar Stats Panel -- */
223
+ .sidebar-stats {
224
+ border-top: 1px solid var(--border);
225
+ }
226
+
227
+ .sidebar-stats-toggle {
228
+ display: flex;
229
+ align-items: center;
230
+ gap: 0.45rem;
231
+ width: 100%;
232
+ padding: 0.6rem 1.25rem;
233
+ background: none;
234
+ border: none;
235
+ color: var(--text-muted);
236
+ font-size: 0.78rem;
237
+ font-weight: 600;
238
+ font-family: inherit;
239
+ text-transform: uppercase;
240
+ letter-spacing: 0.04em;
241
+ cursor: pointer;
242
+ transition: all 0.15s ease;
243
+ }
244
+
245
+ .sidebar-stats-toggle:hover {
246
+ color: var(--text);
247
+ background: var(--bg-hover);
248
+ }
249
+
250
+ .sidebar-stats-arrow {
251
+ margin-left: auto;
252
+ font-size: 0.7rem;
253
+ transition: transform 0.15s ease;
254
+ }
255
+
256
+ .sidebar-stats-arrow.open {
257
+ transform: rotate(90deg);
258
+ }
259
+
260
+ .sidebar-stats-body {
261
+ padding: 0.25rem 1.25rem 0.65rem;
262
+ }
263
+
264
+ .ss-group-label {
265
+ font-size: 0.68rem;
266
+ font-weight: 600;
267
+ text-transform: uppercase;
268
+ letter-spacing: 0.05em;
269
+ color: var(--text-muted);
270
+ opacity: 0.6;
271
+ padding: 0.4rem 0 0.1rem;
272
+ margin-top: 0.15rem;
273
+ border-top: 1px solid rgba(255,255,255,0.04);
274
+ }
275
+
276
+ .ss-row {
277
+ display: flex;
278
+ justify-content: space-between;
279
+ align-items: center;
280
+ padding: 0.2rem 0;
281
+ }
282
+
283
+ .ss-row.ss-indent {
284
+ padding-left: 0.5rem;
285
+ }
286
+
287
+ .ss-label {
288
+ font-size: 0.78rem;
289
+ color: var(--text-muted);
290
+ }
291
+
292
+ .ss-value {
293
+ font-size: 0.8rem;
294
+ font-weight: 600;
295
+ color: var(--accent);
296
+ font-variant-numeric: tabular-nums;
297
+ }
298
+
299
+ /* -- Mobile Top Bar -- */
300
+ .topbar {
301
+ display: none;
302
+ position: fixed;
303
+ top: 0; left: 0; right: 0;
304
+ height: var(--topbar-h);
305
+ background: var(--bg-surface);
306
+ border-bottom: 1px solid var(--border);
307
+ align-items: center;
308
+ padding: 0 1rem;
309
+ z-index: 101;
310
+ }
311
+
312
+ .topbar-brand {
313
+ font-weight: 700;
314
+ font-size: 1.05rem;
315
+ margin-left: 0.75rem;
316
+ }
317
+
318
+ /* Hamburger */
319
+ .hamburger {
320
+ display: flex;
321
+ flex-direction: column;
322
+ justify-content: center;
323
+ gap: 5px;
324
+ width: 32px;
325
+ height: 32px;
326
+ background: none;
327
+ border: none;
328
+ cursor: pointer;
329
+ padding: 4px;
330
+ }
331
+
332
+ .hamburger span {
333
+ display: block;
334
+ height: 2px;
335
+ background: var(--text);
336
+ border-radius: 2px;
337
+ transition: all 0.25s var(--ease-out);
338
+ }
339
+
340
+ .hamburger.active span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
341
+ .hamburger.active span:nth-child(2) { opacity: 0; }
342
+ .hamburger.active span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
343
+
344
+ /* Overlay */
345
+ .overlay {
346
+ display: none;
347
+ position: fixed;
348
+ inset: 0;
349
+ background: rgba(0, 0, 0, 0.5);
350
+ z-index: 99;
351
+ opacity: 0;
352
+ transition: opacity 0.3s ease;
353
+ }
354
+
355
+ .overlay.visible { display: block; opacity: 1; }
356
+
357
+ /* -- Main Content -- */
358
+ .content {
359
+ margin-left: auto;
360
+ margin-right: auto;
361
+ padding: 2.5rem 3rem 2.5rem calc(var(--sidebar-w) + 3rem);
362
+ max-width: calc(1200px + var(--sidebar-w));
363
+ min-height: 100vh;
364
+ }
365
+
366
+ /* -- Page Header -- */
367
+ .page-header { margin-bottom: 2rem; }
368
+ .page-header h1 { font-size: 1.85rem; font-weight: 700; margin-bottom: 0.35rem; letter-spacing: -0.02em; }
369
+ .page-header.center { text-align: center; padding: 4rem 1rem; }
370
+ .subtitle { color: var(--text-muted); font-size: 0.95rem; }
371
+
372
+ /* -- Cards -- */
373
+ .card {
374
+ background: var(--bg-card);
375
+ border: 1px solid var(--border);
376
+ border-radius: var(--radius-lg);
377
+ padding: 1.5rem;
378
+ margin-bottom: 1.25rem;
379
+ backdrop-filter: blur(8px);
380
+ -webkit-backdrop-filter: blur(8px);
381
+ transition: border-color .2s ease, box-shadow .2s ease;
382
+ min-width: 0;
383
+ }
384
+
385
+ .card:hover {
386
+ border-color: rgba(88, 166, 255, 0.15);
387
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(88, 166, 255, 0.04);
388
+ }
389
+
390
+ .card h3 { font-size: 1.1rem; margin-bottom: 0.5rem; }
391
+ .card p { color: var(--text-muted); font-size: 0.9rem; margin-bottom: 0.75rem; }
392
+ .card-accent { border-left: 3px solid var(--accent); }
393
+ .card-muted { background: var(--bg-surface); backdrop-filter: none; }
394
+ .card-error { background: rgba(248,113,113,0.08); border-color: var(--danger); color: var(--danger); }
395
+
396
+ .card-grid {
397
+ display: grid;
398
+ grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr));
399
+ gap: 1rem;
400
+ margin-bottom: 1.25rem;
401
+ }
402
+
403
+ .card-grid > .card {
404
+ display: flex;
405
+ flex-direction: column;
406
+ margin-bottom: 0;
407
+ }
408
+
409
+ .card-grid > .card > .btn,
410
+ .card-grid > .card > a.btn {
411
+ margin-top: auto;
412
+ align-self: flex-start;
413
+ }
414
+
415
+ /* -- Buttons -- */
416
+ .btn {
417
+ display: inline-flex;
418
+ align-items: center;
419
+ gap: 0.4rem;
420
+ padding: 0.5rem 1.1rem;
421
+ font-size: 0.9rem;
422
+ font-weight: 500;
423
+ border: none;
424
+ border-radius: var(--radius);
425
+ cursor: pointer;
426
+ transition: all 0.15s ease;
427
+ text-decoration: none;
428
+ font-family: inherit;
429
+ }
430
+
431
+ .btn:active { transform: scale(0.97); }
432
+
433
+ .btn-primary { background: var(--accent); color: #fff; }
434
+ .btn-primary:hover { background: var(--accent-hover); color: #fff; box-shadow: 0 0 20px var(--accent-glow); }
435
+
436
+ .btn-outline { background: transparent; border: 1px solid var(--border); color: var(--accent); }
437
+ .btn-outline:hover { border-color: var(--accent); background: var(--accent-soft); }
438
+
439
+ .btn-ghost { background: transparent; color: var(--text-muted); }
440
+ .btn-ghost:hover { color: var(--text); background: var(--bg-hover); }
441
+
442
+ .btn-sm { padding: 0.35rem 0.75rem; font-size: 0.82rem; }
443
+
444
+ /* -- Inputs -- */
445
+ .input {
446
+ width: 100%;
447
+ padding: 0.55rem 0.85rem;
448
+ background: var(--bg);
449
+ border: 1px solid var(--border);
450
+ border-radius: var(--radius);
451
+ color: var(--text);
452
+ font-size: 0.9rem;
453
+ font-family: inherit;
454
+ outline: none;
455
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
456
+ }
457
+
458
+ .input:hover { border-color: rgba(88,166,255,0.2); }
459
+ .input:focus { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(88,166,255,0.1), 0 0 12px var(--accent-glow); }
460
+ .input-sm { width: auto; padding: 0.35rem 0.6rem; font-size: 0.82rem; }
461
+
462
+ /* -- About -- */
463
+ .feature-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr)); gap: 0.5rem; }
464
+ .feature-item {
465
+ display: flex;
466
+ flex-direction: column;
467
+ padding: 0.65rem 0.85rem;
468
+ background: var(--bg-hover);
469
+ border-radius: var(--radius);
470
+ font-size: 0.85rem;
471
+ transition: background .15s;
472
+ }
473
+ .feature-item:hover { background: var(--accent-soft); }
474
+ .feature-item strong { color: var(--accent); font-size: 0.82rem; margin-bottom: 0.1rem; }
475
+ .feature-item span { color: var(--text-muted); font-size: 0.8rem; }
476
+
477
+ .next-steps { padding-left: 1.25rem; }
478
+ .next-steps li { margin-bottom: 0.4rem; font-size: 0.9rem; color: var(--text-muted); }
479
+ .next-steps a { color: var(--accent); }
480
+
481
+ .muted { color: var(--text-muted); }
482
+
483
+ /* -- Toast Notifications -- */
484
+ .toast-container {
485
+ position: fixed;
486
+ bottom: 1.5rem;
487
+ right: 1.5rem;
488
+ display: flex;
489
+ flex-direction: column;
490
+ gap: 0.5rem;
491
+ z-index: 200;
492
+ pointer-events: none;
493
+ }
494
+
495
+ .toast {
496
+ padding: 0.65rem 1.1rem;
497
+ border-radius: var(--radius);
498
+ font-size: 0.85rem;
499
+ font-weight: 500;
500
+ color: #fff;
501
+ animation: toast-in 0.3s var(--ease-out);
502
+ pointer-events: auto;
503
+ backdrop-filter: blur(8px);
504
+ }
505
+
506
+ .toast-success { background: rgba(63,185,80,0.9); }
507
+ .toast-error { background: rgba(248,81,73,0.9); }
508
+ .toast-info { background: rgba(88,166,255,0.9); }
509
+
510
+ .toast-exit { animation: toast-out 0.3s ease forwards; }
511
+
512
+ @keyframes toast-in { from { opacity: 0; transform: translateY(10px) scale(0.95); } to { opacity: 1; transform: translateY(0) scale(1); } }
513
+ @keyframes toast-out { from { opacity: 1; transform: translateY(0) scale(1); } to { opacity: 0; transform: translateY(-10px) scale(0.95); } }
514
+
515
+ /* -- Route Transition -- */
516
+ z-outlet { display: block; animation: fade-in 0.25s var(--ease-out); }
517
+ @keyframes fade-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
518
+
519
+ /* -- Responsive: Mobile -- */
520
+ @media (max-width: 768px) {
521
+ .sidebar {
522
+ transform: translateX(-100%);
523
+ width: 260px;
524
+ }
525
+ .sidebar.open { transform: translateX(0); }
526
+
527
+ .topbar { display: flex; }
528
+
529
+ .content {
530
+ margin-left: 0;
531
+ padding: 1.25rem;
532
+ padding-top: calc(var(--topbar-h) + 1.25rem);
533
+ max-width: 100%;
534
+ overflow-x: hidden;
535
+ }
536
+
537
+ .card { padding: 1.15rem; }
538
+ .card-grid { grid-template-columns: 1fr; }
539
+ .feature-grid { grid-template-columns: 1fr; }
540
+
541
+ .page-header h1 { font-size: 1.4rem; }
542
+ .page-header.center { padding: 2rem 0.75rem; }
543
+
544
+ .toast-container { right: 0.75rem; left: 0.75rem; align-items: flex-end; }
545
+ }
546
+
547
+ @media (max-width: 480px) {
548
+ .content { padding: 0.75rem; padding-top: calc(var(--topbar-h) + 0.75rem); }
549
+ .card { padding: 1rem; }
550
+ .page-header h1 { font-size: 1.25rem; }
551
+ .subtitle { font-size: 0.85rem; }
552
+ }