locadot 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1710 @@
1
+ "use strict";
2
+ /**
3
+ * The dashboard's styles: design tokens, base, app shell, shared components, pages, responsive rules.
4
+ *
5
+ * Component classes other page code should use (see core.ts for the JS helpers):
6
+ * Page header .page-head > .page-title + .page-sub + .page-actions (actions sit right, wrap under on mobile)
7
+ * Cards .card > .card-head (.card-title-wrap > .card-title/.card-desc, .card-tools) + .card-body + .card-foot
8
+ * .card-body-tight (no padding, for lists), .card-interactive (hover lift), .card-flush (no shadow)
9
+ * Buttons .btn (secondary) | .btn-primary | .btn-secondary | .btn-ghost | .btn-danger | .btn-danger-solid | .btn-ghost-danger
10
+ * sizes .btn-sm / .btn-lg, square icon button .btn-icon, "more" button .btn-more (or makeMoreButton()), .btn-block, .busy
11
+ * Forms .field > label + .input / select.input, .field-hint, .field-error, .input-sm, .input-search
12
+ * .switch[role=switch] > .switch-knob (toggle .on + aria-checked), native checkbox is restyled,
13
+ * .check-card (label wrapping a checkbox/radio + .check-card-body > .check-card-title/.check-card-desc), .option-card
14
+ * Badges .badge + .badge-up/.badge-down/.badge-warn/.badge-accent/.badge-muted, .badge-dot (leading dot), .badges (row)
15
+ * Dots .dot + .up/.down/.warn/.accent
16
+ * Tabs .tabs[role=tablist][data-tabs=<view>] > button[role=tab][data-tab=<name>]; panels [role=tabpanel][data-tab-panel=<name>]
17
+ * Menu built by openMenu(); .menu, .menu-item, .menu-sep, .menu-info; sheet on <=640px
18
+ * Dialogs <dialog> > .modal-head (.modal-title, .modal-sub, .modal-close) + .modal-body + .modal-foot; bottom sheet on <=640px
19
+ * Empty state .empty > .empty-icon + .empty-title + .empty-desc (or p) + .empty-actions
20
+ * Skeletons .skel-bar, .skel-line, .skel-circle, .skel-block
21
+ * Motion .anim-in (one element), .anim-list (children stagger in); all motion off under prefers-reduced-motion
22
+ * Tooltips data-tip="text" on any element (data-tip-pos="right|bottom|top")
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.css = exports.lightVars = void 0;
26
+ exports.lightVars = `
27
+ color-scheme: light;
28
+ --bg: #f7f7f8;
29
+ --surface: #ffffff;
30
+ --surface-2: #f4f4f5;
31
+ --surface-3: #ebebee;
32
+ --surface-raised: #ffffff;
33
+ --border: #e6e6ea;
34
+ --border-strong: #d4d4da;
35
+ --fg: #0b0b0f;
36
+ --fg-2: #3f3f47;
37
+ --muted: #676773;
38
+ --muted-dim: #a1a1ab;
39
+ --accent: #2563eb;
40
+ --accent-strong: #1d4ed8;
41
+ --accent-hover: #1d4ed8;
42
+ --accent-bg: rgba(37, 99, 235, 0.08);
43
+ --accent-ring: rgba(37, 99, 235, 0.25);
44
+ --accent-fg: #ffffff;
45
+ --up: #16a34a;
46
+ --up-bg: rgba(22, 163, 74, 0.1);
47
+ --down: #dc2626;
48
+ --down-bg: rgba(220, 38, 38, 0.08);
49
+ --down-ring: rgba(220, 38, 38, 0.22);
50
+ --warn: #c2680a;
51
+ --warn-bg: rgba(217, 119, 6, 0.1);
52
+ --focus: #2563eb;
53
+ --overlay: rgba(12, 12, 16, 0.38);
54
+ --nav-active: rgba(12, 12, 16, 0.055);
55
+ --tooltip-bg: #18181b;
56
+ --tooltip-fg: #fafafa;
57
+ --shadow-xs: 0 1px 2px rgba(16, 16, 24, 0.05);
58
+ --shadow-sm: 0 1px 2px rgba(16, 16, 24, 0.05), 0 1px 3px rgba(16, 16, 24, 0.04);
59
+ --shadow-md: 0 4px 12px rgba(16, 16, 24, 0.08), 0 1px 3px rgba(16, 16, 24, 0.06);
60
+ --shadow-lg: 0 16px 40px rgba(16, 16, 24, 0.16), 0 2px 6px rgba(16, 16, 24, 0.06);
61
+ `;
62
+ exports.css = `
63
+ :root {
64
+ color-scheme: dark;
65
+ --bg: #09090b;
66
+ --surface: #111114;
67
+ --surface-2: #18181c;
68
+ --surface-3: #222227;
69
+ --surface-raised: #1a1a1f;
70
+ --border: #25252b;
71
+ --border-strong: #34343c;
72
+ --fg: #f4f4f5;
73
+ --fg-2: #cfcfd6;
74
+ --muted: #8f8f9a;
75
+ --muted-dim: #5f5f6a;
76
+ --accent: #3b82f6;
77
+ --accent-strong: #60a5fa;
78
+ --accent-hover: #2f6fe0;
79
+ --accent-bg: rgba(59, 130, 246, 0.14);
80
+ --accent-ring: rgba(96, 165, 250, 0.35);
81
+ --accent-fg: #ffffff;
82
+ --up: #22c55e;
83
+ --up-bg: rgba(34, 197, 94, 0.14);
84
+ --down: #f05252;
85
+ --down-bg: rgba(239, 68, 68, 0.14);
86
+ --down-ring: rgba(239, 68, 68, 0.3);
87
+ --warn: #f59e0b;
88
+ --warn-bg: rgba(245, 158, 11, 0.14);
89
+ --focus: #60a5fa;
90
+ --overlay: rgba(0, 0, 0, 0.6);
91
+ --nav-active: rgba(255, 255, 255, 0.06);
92
+ --tooltip-bg: #f4f4f5;
93
+ --tooltip-fg: #111114;
94
+ --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.4);
95
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
96
+ --shadow-md: 0 6px 16px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.02);
97
+ --shadow-lg: 0 20px 48px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 255, 255, 0.04);
98
+
99
+ --radius-xs: 6px;
100
+ --radius-sm: 8px;
101
+ --radius-md: 10px;
102
+ --radius: 12px;
103
+ --radius-lg: 16px;
104
+ --radius-full: 999px;
105
+ --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px; --s-5: 20px; --s-6: 24px; --s-8: 32px; --s-10: 40px;
106
+ --fs-xs: 11.5px; --fs-sm: 12.5px; --fs-base: 14px; --fs-md: 15px; --fs-lg: 18px; --fs-xl: 22px; --fs-2xl: 28px;
107
+ --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
108
+ --ease-in: cubic-bezier(0.4, 0, 1, 1);
109
+ --dur-1: 120ms; --dur-2: 180ms; --dur-3: 240ms;
110
+ --ring: 0 0 0 3px var(--accent-ring);
111
+ --topbar-h: 56px;
112
+ --bottombar-h: 60px;
113
+ --safe-b: env(safe-area-inset-bottom, 0px);
114
+ --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
115
+ --sans: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
116
+ }
117
+ @media (prefers-color-scheme: light) {
118
+ :root:not([data-theme="dark"]) {${exports.lightVars}}
119
+ }
120
+ :root[data-theme="light"] {${exports.lightVars}}
121
+ * { box-sizing: border-box; }
122
+ html { -webkit-text-size-adjust: 100%; -webkit-tap-highlight-color: transparent; }
123
+ html, body { height: 100%; }
124
+ body {
125
+ margin: 0;
126
+ font-family: var(--sans);
127
+ font-size: var(--fs-base);
128
+ color: var(--fg);
129
+ line-height: 1.5;
130
+ background: var(--bg);
131
+ min-height: 100vh;
132
+ -webkit-font-smoothing: antialiased;
133
+ -moz-osx-font-smoothing: grayscale;
134
+ text-rendering: optimizeLegibility;
135
+ }
136
+ ::selection { background: var(--accent-ring); }
137
+ .mono { font-family: var(--mono); font-feature-settings: normal; }
138
+ a { color: var(--accent); text-decoration: none; }
139
+ a:hover, a:focus-visible { text-decoration: underline; text-underline-offset: 2px; }
140
+ button, input, select, textarea { font-family: inherit; }
141
+ button { -webkit-tap-highlight-color: transparent; }
142
+ *:focus-visible {
143
+ outline: 2px solid var(--focus);
144
+ outline-offset: 2px;
145
+ border-radius: var(--radius-xs);
146
+ }
147
+ .visually-hidden {
148
+ position: absolute;
149
+ width: 1px; height: 1px;
150
+ padding: 0; margin: -1px;
151
+ overflow: hidden;
152
+ clip: rect(0, 0, 0, 0);
153
+ white-space: nowrap;
154
+ border: 0;
155
+ }
156
+ .clip-helper { position: absolute; left: -9999px; top: -9999px; }
157
+ .muted { color: var(--muted); }
158
+ .dim { color: var(--muted-dim); }
159
+ .label {
160
+ font-size: 12px;
161
+ color: var(--muted);
162
+ font-weight: 500;
163
+ letter-spacing: 0.01em;
164
+ }
165
+ .text-sm { font-size: var(--fs-sm); }
166
+ .text-xs { font-size: var(--fs-xs); }
167
+ .nowrap { white-space: nowrap; }
168
+ .truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
169
+ .stack { display: flex; flex-direction: column; gap: var(--s-3); }
170
+ .row { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
171
+ .spacer { flex: 1; }
172
+ kbd { font-family: var(--mono); font-size: 11px; padding: 1px 5px; border: 1px solid var(--border-strong); border-bottom-width: 2px; border-radius: 4px; background: var(--surface-2); color: var(--fg-2); }
173
+
174
+ /* ---------- top bar ---------- */
175
+ header {
176
+ position: sticky;
177
+ top: 0;
178
+ z-index: 5;
179
+ background: var(--bg);
180
+ border-bottom: 1px solid var(--border);
181
+ }
182
+ @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
183
+ header {
184
+ background: color-mix(in srgb, var(--bg) 78%, transparent);
185
+ -webkit-backdrop-filter: saturate(180%) blur(12px);
186
+ backdrop-filter: saturate(180%) blur(12px);
187
+ }
188
+ }
189
+ .topbar {
190
+ max-width: 1360px;
191
+ margin: 0 auto;
192
+ padding: 0 32px;
193
+ height: var(--topbar-h);
194
+ display: flex;
195
+ align-items: center;
196
+ justify-content: space-between;
197
+ gap: 12px;
198
+ }
199
+ .brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
200
+ .logo {
201
+ width: 24px; height: 24px;
202
+ border-radius: 7px;
203
+ background: linear-gradient(145deg, #4f8ff7, var(--accent) 55%, #1e4fd6);
204
+ position: relative;
205
+ flex-shrink: 0;
206
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.16), 0 1px 2px rgba(37, 99, 235, 0.35);
207
+ }
208
+ .logo::after {
209
+ content: "";
210
+ position: absolute;
211
+ left: 8px; top: 8px;
212
+ width: 8px; height: 8px;
213
+ border-radius: 50%;
214
+ background: #fff;
215
+ }
216
+ .wordmark {
217
+ font-size: 15px;
218
+ font-weight: 650;
219
+ margin: 0;
220
+ letter-spacing: -0.02em;
221
+ color: var(--fg);
222
+ }
223
+ .crumbs { display: flex; align-items: center; gap: 8px; min-width: 0; font-size: 13.5px; }
224
+ .crumb-root { color: var(--muted); }
225
+ .crumb-sep { color: var(--muted-dim); }
226
+ .crumb-current { color: var(--fg); font-weight: 550; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
227
+ .header-meta { display: flex; align-items: center; gap: 8px; font-size: 12px; min-width: 0; }
228
+ .btn.theme-toggle { width: 32px; height: 32px; padding: 0; border-radius: var(--radius-full); color: var(--muted); flex-shrink: 0; }
229
+ .btn.theme-toggle:hover { color: var(--fg); }
230
+ .theme-toggle svg { width: 16px; height: 16px; flex-shrink: 0; display: none; }
231
+ .theme-toggle[data-mode="system"] .i-system, .theme-toggle[data-mode="light"] .i-light, .theme-toggle[data-mode="dark"] .i-dark { display: block; }
232
+ #updated { color: var(--muted-dim); font-size: 12px; white-space: nowrap; font-variant-numeric: tabular-nums; }
233
+ .chip {
234
+ display: inline-flex;
235
+ align-items: center;
236
+ gap: 6px;
237
+ height: 26px;
238
+ padding: 0 10px;
239
+ border-radius: var(--radius-full);
240
+ border: 1px solid var(--border);
241
+ background: var(--surface-2);
242
+ font-size: 12px;
243
+ color: var(--muted);
244
+ white-space: nowrap;
245
+ }
246
+ .chip:empty { display: none; }
247
+ .status-pill {
248
+ display: inline-flex;
249
+ align-items: center;
250
+ gap: 8px;
251
+ height: 32px;
252
+ padding: 0 10px 0 12px;
253
+ border-radius: var(--radius-full);
254
+ border: 1px solid var(--border);
255
+ background: var(--surface);
256
+ box-shadow: var(--shadow-xs);
257
+ color: var(--fg-2);
258
+ font: 500 12.5px var(--sans);
259
+ white-space: nowrap;
260
+ cursor: pointer;
261
+ min-width: 0;
262
+ transition: border-color var(--dur-1) ease, background-color var(--dur-1) ease, transform var(--dur-1) ease;
263
+ }
264
+ .status-pill:hover { border-color: var(--border-strong); background: var(--surface-2); }
265
+ .status-pill:active { transform: scale(0.97); }
266
+ .status-pill[aria-expanded="true"] { border-color: var(--border-strong); background: var(--surface-2); }
267
+ .status-extra { display: inline-flex; align-items: center; gap: 8px; color: var(--muted); font-variant-numeric: tabular-nums; min-width: 0; }
268
+ .status-extra:empty { display: none; }
269
+ .status-sep { width: 1px; height: 12px; background: var(--border-strong); flex-shrink: 0; }
270
+ .status-caret { width: 14px; height: 14px; flex-shrink: 0; color: var(--muted-dim); fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; transition: transform var(--dur-2) var(--ease); }
271
+ .status-pill[aria-expanded="true"] .status-caret { transform: rotate(180deg); }
272
+ body.offline .status-pill { color: var(--down); border-color: var(--down-ring); background: var(--down-bg); }
273
+ .chip-live { color: var(--fg-2); font-weight: 500; }
274
+ .live-dot {
275
+ position: relative;
276
+ display: inline-block;
277
+ width: 8px; height: 8px;
278
+ border-radius: 50%;
279
+ background: var(--muted-dim);
280
+ flex-shrink: 0;
281
+ }
282
+ .live-dot.up { background: var(--up); box-shadow: 0 0 0 3px var(--up-bg); }
283
+ .live-dot.up::after {
284
+ content: "";
285
+ position: absolute;
286
+ inset: 0;
287
+ border-radius: 50%;
288
+ background: var(--up);
289
+ animation: live-ping 2.4s var(--ease) infinite;
290
+ }
291
+ .live-dot.down { background: var(--down); box-shadow: 0 0 0 3px var(--down-bg); }
292
+ body.offline .live-label { color: var(--down); }
293
+ @keyframes live-ping { 0% { transform: scale(1); opacity: 0.55; } 70%, 100% { transform: scale(2.6); opacity: 0; } }
294
+
295
+ /* ---------- sidebar / rail / bottom bar ---------- */
296
+ :root { --sb-w: 240px; }
297
+ .sidebar {
298
+ position: fixed;
299
+ top: 0; bottom: 0; left: 0;
300
+ z-index: 7;
301
+ width: var(--sb-w);
302
+ display: flex;
303
+ flex-direction: column;
304
+ background: var(--surface);
305
+ border-right: 1px solid var(--border);
306
+ overflow: hidden;
307
+ }
308
+ .shell { margin-left: var(--sb-w); min-height: 100vh; display: flex; flex-direction: column; }
309
+ .shell > main { flex: 1; width: 100%; }
310
+ .sb-head { height: var(--topbar-h); display: flex; align-items: center; gap: 10px; padding: 0 18px; flex-shrink: 0; white-space: nowrap; }
311
+ .sb-brand { display: flex; align-items: center; gap: 10px; color: inherit; min-width: 0; }
312
+ .sb-brand:hover { text-decoration: none; }
313
+ .sb-version, #version { font-size: 11px; color: var(--muted); font-family: var(--mono); padding: 1px 6px; border: 1px solid var(--border); border-radius: var(--radius-full); }
314
+ #version:empty { display: none; }
315
+ .sb-links { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 8px 10px; display: flex; flex-direction: column; gap: 2px; }
316
+ .sb-group { display: flex; flex-direction: column; gap: 2px; }
317
+ .sb-section { font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted-dim); padding: 18px 12px 6px; white-space: nowrap; }
318
+ .sb-link {
319
+ position: relative;
320
+ display: flex;
321
+ align-items: center;
322
+ gap: 11px;
323
+ height: 36px;
324
+ padding: 0 10px;
325
+ border: 0;
326
+ border-radius: var(--radius-sm);
327
+ background: none;
328
+ color: var(--muted);
329
+ font: 500 13.5px var(--sans);
330
+ white-space: nowrap;
331
+ cursor: pointer;
332
+ text-align: left;
333
+ width: 100%;
334
+ transition: background-color var(--dur-1) ease, color var(--dur-1) ease;
335
+ }
336
+ .sb-link:hover { background: var(--nav-active); color: var(--fg); text-decoration: none; }
337
+ .sb-link:active { background: var(--surface-3); }
338
+ .sb-link[aria-current] { background: var(--nav-active); color: var(--fg); }
339
+ .sb-link[aria-current] svg { color: var(--accent); }
340
+ .sb-link:focus-visible { outline-offset: -2px; }
341
+ .sb-link svg { position: relative; z-index: 1; width: 18px; height: 18px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; transition: transform var(--dur-2) var(--ease); }
342
+ .sb-badges { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; }
343
+ .sb-count { font-size: 11px; font-weight: 600; min-width: 20px; height: 18px; padding: 0 6px; border-radius: var(--radius-full); background: var(--surface-3); color: var(--fg-2); display: inline-flex; align-items: center; justify-content: center; font-variant-numeric: tabular-nums; }
344
+ .sb-count-accent { background: var(--accent-bg); color: var(--accent); }
345
+ .sb-count-accent::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; margin-right: 4px; }
346
+ .sb-dot { margin-left: 0; }
347
+ .sb-foot { display: flex; flex-direction: column; gap: 2px; padding: 8px 10px 12px; border-top: 1px solid var(--border); flex-shrink: 0; }
348
+ .sb-collapse svg { transition: transform var(--dur-2) var(--ease); }
349
+ .sb-signout[hidden] { display: none; }
350
+ .sb-menu, .brand-mobile { display: none; }
351
+ .btn.sb-menu { display: none; }
352
+ .sb-backdrop { position: fixed; inset: 0; z-index: 6; background: var(--overlay); }
353
+ .card, details.card { scroll-margin-top: 76px; }
354
+ @media (min-width: 641px) {
355
+ :root[data-sidebar="collapsed"] { --sb-w: 64px; }
356
+ :root[data-sidebar="collapsed"] .sb-label { display: none; }
357
+ :root[data-sidebar="collapsed"] .sb-head { padding: 0; justify-content: center; }
358
+ :root[data-sidebar="collapsed"] .sb-links, :root[data-sidebar="collapsed"] .sb-foot { padding-left: 10px; padding-right: 10px; }
359
+ :root[data-sidebar="collapsed"] .sb-section { display: block; height: 1px; padding: 0; margin: 12px 8px; background: var(--border); overflow: hidden; font-size: 0; }
360
+ :root[data-sidebar="collapsed"] .sb-link { justify-content: center; padding: 0; height: 40px; }
361
+ :root[data-sidebar="collapsed"] .sb-collapse svg { transform: scaleX(-1); }
362
+ :root[data-sidebar="collapsed"] .sb-badges { position: absolute; top: 4px; right: 4px; gap: 2px; }
363
+ :root[data-sidebar="collapsed"] .sb-count { min-width: 16px; height: 16px; padding: 0 4px; font-size: 10px; background: var(--accent); color: var(--accent-fg); box-shadow: 0 0 0 2px var(--surface); }
364
+ :root[data-sidebar="collapsed"] .sb-count-accent, :root[data-sidebar="collapsed"] #sb-remotes-count { display: none; }
365
+ :root[data-sidebar="collapsed"] #sb-hub-dot { position: absolute; top: 6px; right: 6px; }
366
+ }
367
+
368
+ /* ---------- page layout ---------- */
369
+ main {
370
+ max-width: 1360px;
371
+ margin: 0 auto;
372
+ padding: 32px 32px 48px;
373
+ display: flex;
374
+ flex-direction: column;
375
+ gap: 24px;
376
+ }
377
+ .page-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 16px; row-gap: 4px; align-items: center; }
378
+ .page-head > :not(.page-actions) { grid-column: 1; }
379
+ .page-title { font-size: var(--fs-xl); font-weight: 650; letter-spacing: -0.025em; margin: 0; line-height: 1.25; }
380
+ .page-sub { margin: 0; color: var(--muted); font-size: var(--fs-base); }
381
+ .page-actions { grid-column: 2; grid-row: 1 / span 2; display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }
382
+ .page-head + .tabs { margin-top: -8px; }
383
+ .layout {
384
+ display: grid;
385
+ grid-template-columns: minmax(0, 1fr);
386
+ gap: 24px;
387
+ align-items: start;
388
+ }
389
+ .col-primary, .col-secondary { display: flex; flex-direction: column; gap: 24px; min-width: 0; }
390
+ @media (min-width: 1100px) {
391
+ .layout { grid-template-columns: minmax(0, 1fr) 340px; }
392
+ }
393
+ @media (min-width: 1400px) {
394
+ .layout { grid-template-columns: minmax(0, 1fr) 372px; }
395
+ }
396
+
397
+ /* ---------- cards ---------- */
398
+ .card {
399
+ background: var(--surface);
400
+ border: 1px solid var(--border);
401
+ border-radius: var(--radius);
402
+ box-shadow: var(--shadow-sm);
403
+ min-width: 0;
404
+ }
405
+ .card-flush { box-shadow: none; }
406
+ .card-interactive { transition: border-color var(--dur-2) ease, box-shadow var(--dur-2) ease, transform var(--dur-2) var(--ease); }
407
+ .card-interactive:hover { border-color: var(--border-strong); box-shadow: var(--shadow-md); transform: translateY(-1px); text-decoration: none; }
408
+ .card-head {
409
+ display: flex;
410
+ align-items: center;
411
+ justify-content: space-between;
412
+ gap: 12px;
413
+ flex-wrap: wrap;
414
+ padding: 14px 20px;
415
+ min-height: 56px;
416
+ border-bottom: 1px solid var(--border);
417
+ }
418
+ .card-title-wrap { display: flex; align-items: center; gap: 10px; min-width: 0; }
419
+ .card-title { font-size: var(--fs-md); font-weight: 600; margin: 0; letter-spacing: -0.01em; }
420
+ .card-desc { margin: 2px 0 0; font-size: var(--fs-sm); color: var(--muted); }
421
+ .card-tools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
422
+ .card-body { padding: 20px; }
423
+ .card-body-tight { padding: 8px 0; }
424
+ .card-foot {
425
+ display: flex;
426
+ align-items: center;
427
+ justify-content: flex-end;
428
+ gap: 8px;
429
+ flex-wrap: wrap;
430
+ padding: 12px 20px;
431
+ border-top: 1px solid var(--border);
432
+ background: var(--surface-2);
433
+ border-radius: 0 0 var(--radius) var(--radius);
434
+ font-size: var(--fs-sm);
435
+ color: var(--muted);
436
+ }
437
+ .card-foot .spacer, .card-foot-start { margin-right: auto; }
438
+
439
+ /* ---------- badges ---------- */
440
+ .badge {
441
+ display: inline-flex;
442
+ align-items: center;
443
+ gap: 5px;
444
+ height: 20px;
445
+ padding: 0 8px;
446
+ border-radius: var(--radius-full);
447
+ font-size: 11px;
448
+ font-weight: 550;
449
+ line-height: 1;
450
+ letter-spacing: 0.01em;
451
+ white-space: nowrap;
452
+ border: 1px solid var(--border-strong);
453
+ background: var(--surface-2);
454
+ color: var(--fg-2);
455
+ font-variant-numeric: tabular-nums;
456
+ }
457
+ .badge-warn { color: var(--warn); border-color: transparent; background: var(--warn-bg); }
458
+ .badge-cors, .badge-accent { color: var(--accent); border-color: transparent; background: var(--accent-bg); }
459
+ .badge-up { color: var(--up); border-color: transparent; background: var(--up-bg); }
460
+ .badge-down { color: var(--down); border-color: transparent; background: var(--down-bg); }
461
+ .badge-muted { color: var(--muted); border-color: transparent; background: var(--surface-3); }
462
+ .badge-count { font-family: var(--mono); color: var(--muted); }
463
+ .badge-dot::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
464
+ .badge-lg { height: 24px; padding: 0 10px; font-size: 12px; }
465
+ .badges { display: flex; flex-wrap: wrap; gap: 4px; }
466
+ td.badges { display: table-cell; }
467
+ td.badges .badge + .badge { margin-left: 4px; }
468
+
469
+ /* ---------- buttons ---------- */
470
+ .btn {
471
+ position: relative;
472
+ display: inline-flex;
473
+ align-items: center;
474
+ justify-content: center;
475
+ gap: 6px;
476
+ height: 32px;
477
+ padding: 0 12px;
478
+ font-size: 13px;
479
+ font-weight: 550;
480
+ line-height: 1;
481
+ border-radius: var(--radius-sm);
482
+ border: 1px solid var(--border-strong);
483
+ background: var(--surface);
484
+ color: var(--fg);
485
+ cursor: pointer;
486
+ white-space: nowrap;
487
+ box-shadow: var(--shadow-xs);
488
+ user-select: none;
489
+ -webkit-user-select: none;
490
+ transition: border-color var(--dur-1) ease, background-color var(--dur-1) ease, color var(--dur-1) ease, opacity var(--dur-1) ease, box-shadow var(--dur-1) ease, transform var(--dur-1) var(--ease);
491
+ }
492
+ a.btn { text-decoration: none; }
493
+ .btn:hover:not(:disabled) { background: var(--surface-2); border-color: var(--muted-dim); text-decoration: none; }
494
+ .btn:active:not(:disabled) { transform: scale(0.97); }
495
+ .btn:disabled { opacity: 0.5; cursor: not-allowed; box-shadow: none; }
496
+ .btn svg { width: 15px; height: 15px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
497
+ .btn-secondary { background: var(--surface); }
498
+ .btn-sm { height: 28px; padding: 0 10px; font-size: 12px; border-radius: 7px; }
499
+ .btn-lg { height: 40px; padding: 0 18px; font-size: 14px; border-radius: var(--radius-md); }
500
+ .btn-icon { width: 32px; padding: 0; }
501
+ .btn-sm.btn-icon { width: 28px; padding: 0; color: var(--muted); }
502
+ .btn-lg.btn-icon { width: 40px; }
503
+ .btn-icon svg { width: 16px; height: 16px; }
504
+ .btn-sm.btn-icon svg { width: 15px; height: 15px; }
505
+ .btn-icon.is-set { color: var(--accent); border-color: transparent; background: var(--accent-bg); }
506
+ .btn-block { width: 100%; }
507
+ .btn-primary { border-color: var(--accent); background: var(--accent); color: var(--accent-fg); box-shadow: 0 1px 2px rgba(37, 99, 235, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.12); }
508
+ .btn-primary:hover:not(:disabled) { background: var(--accent-hover); border-color: var(--accent-hover); }
509
+ .btn-danger { border-color: var(--down-ring); color: var(--down); background: transparent; box-shadow: none; }
510
+ .btn-danger:hover:not(:disabled) { background: var(--down-bg); border-color: var(--down); }
511
+ .btn-danger-solid, .modal-foot .btn-danger { background: var(--down); border-color: var(--down); color: #fff; }
512
+ .btn-danger-solid:hover:not(:disabled), .modal-foot .btn-danger:hover:not(:disabled) { background: var(--down); border-color: var(--down); filter: brightness(0.92); }
513
+ .btn-ghost { background: transparent; border-color: transparent; box-shadow: none; color: var(--muted); }
514
+ .btn-ghost:hover:not(:disabled) { background: var(--nav-active); border-color: transparent; color: var(--fg); }
515
+ .btn-ghost[aria-expanded="true"] { background: var(--nav-active); color: var(--fg); }
516
+ .btn-ghost-danger { background: transparent; border-color: transparent; box-shadow: none; color: var(--muted); }
517
+ .btn-ghost-danger:hover:not(:disabled) { color: var(--down); background: var(--down-bg); border-color: transparent; }
518
+ .btn-more { color: var(--muted); }
519
+ .btn-more svg { fill: currentColor; stroke: none; }
520
+ .row-actions { display: flex; gap: 6px; justify-content: flex-end; align-items: center; }
521
+ .row-actions .btn-danger:not(:hover):not(:focus-visible) { border-color: var(--border-strong); color: var(--muted); }
522
+ .btn-group { display: inline-flex; }
523
+ .btn-group > .btn { border-radius: 0; }
524
+ .btn-group > .btn:first-child { border-radius: var(--radius-sm) 0 0 var(--radius-sm); }
525
+ .btn-group > .btn:last-child { border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }
526
+ .btn-group > .btn + .btn { margin-left: -1px; }
527
+ .btn.busy { opacity: 0.7; cursor: wait; }
528
+ .btn.busy, #ca-toggle.busy, #startup-toggle.busy { color: transparent !important; pointer-events: none; }
529
+ .btn.busy svg { opacity: 0; }
530
+ .btn.busy::after {
531
+ content: "";
532
+ position: absolute;
533
+ width: 13px; height: 13px;
534
+ top: 50%; left: 50%;
535
+ margin: -6.5px 0 0 -6.5px;
536
+ border-radius: 50%;
537
+ border: 2px solid rgba(127, 127, 127, 0.35);
538
+ border-top-color: var(--accent-strong);
539
+ animation: spin 0.7s linear infinite;
540
+ }
541
+ .btn-primary.busy::after { border-color: rgba(255, 255, 255, 0.35); border-top-color: #fff; }
542
+ .copy-btn {
543
+ display: inline-flex;
544
+ align-items: center;
545
+ height: 22px;
546
+ padding: 0 8px;
547
+ font-size: 11px;
548
+ font-weight: 550;
549
+ border-radius: var(--radius-xs);
550
+ border: 1px solid var(--border);
551
+ background: var(--surface);
552
+ color: var(--muted);
553
+ cursor: pointer;
554
+ white-space: nowrap;
555
+ transition: color var(--dur-1) ease, border-color var(--dur-1) ease, background-color var(--dur-1) ease;
556
+ }
557
+ .copy-btn:hover { color: var(--fg); border-color: var(--muted-dim); }
558
+ .copy-btn:active { background: var(--surface-2); }
559
+
560
+ /* ---------- switch ---------- */
561
+ .switch {
562
+ position: relative;
563
+ width: 38px;
564
+ height: 22px;
565
+ border-radius: var(--radius-full);
566
+ border: 1px solid transparent;
567
+ background: var(--border-strong);
568
+ cursor: pointer;
569
+ flex-shrink: 0;
570
+ padding: 0;
571
+ transition: background-color var(--dur-2) ease, box-shadow var(--dur-2) ease;
572
+ }
573
+ .switch::before { content: ""; position: absolute; inset: -11px -4px; }
574
+ .switch:hover:not(:disabled) { box-shadow: 0 0 0 4px var(--nav-active); }
575
+ .switch .switch-knob {
576
+ position: absolute;
577
+ top: 2px; left: 2px;
578
+ width: 16px; height: 16px;
579
+ border-radius: 50%;
580
+ background: #fff;
581
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
582
+ transition: transform var(--dur-2) var(--ease), opacity var(--dur-1) ease;
583
+ }
584
+ .switch:active:not(:disabled) .switch-knob { transform: scaleX(1.15); }
585
+ .switch.on { background: var(--accent); }
586
+ .switch.on .switch-knob { transform: translateX(16px); }
587
+ .switch.on:active:not(:disabled) .switch-knob { transform: translateX(14px) scaleX(1.15); }
588
+ .switch.busy { opacity: 0.6; cursor: wait; }
589
+ .switch:disabled { cursor: not-allowed; opacity: 0.5; }
590
+ .switch.busy .switch-knob { opacity: 0; }
591
+ .switch.busy::after {
592
+ content: "";
593
+ position: absolute;
594
+ top: 3px; left: 11px;
595
+ width: 14px; height: 14px;
596
+ border-radius: 50%;
597
+ border: 2px solid var(--border-strong);
598
+ border-top-color: var(--accent);
599
+ animation: spin 0.7s linear infinite;
600
+ }
601
+ body.offline .switch, body.offline #add-submit { opacity: 0.5; pointer-events: none; }
602
+
603
+ /* ---------- inputs ---------- */
604
+ .input, .field input[type="text"], .field input[type="password"], .field input[type="number"], .field input[type="url"], .edit-input, textarea.input {
605
+ height: 36px;
606
+ width: 100%;
607
+ padding: 0 12px;
608
+ font-family: var(--mono);
609
+ font-size: 13px;
610
+ color: var(--fg);
611
+ background: var(--surface);
612
+ border: 1px solid var(--border-strong);
613
+ border-radius: var(--radius-sm);
614
+ box-shadow: var(--shadow-xs);
615
+ transition: border-color var(--dur-1) ease, box-shadow var(--dur-1) ease;
616
+ }
617
+ textarea.input { height: auto; min-height: 80px; padding: 8px 12px; line-height: 1.5; resize: vertical; }
618
+ .input:hover:not(:disabled):not(:focus), .field input:hover:not(:disabled):not(:focus) { border-color: var(--muted-dim); }
619
+ .input::placeholder, .field input::placeholder { color: var(--muted-dim); }
620
+ .input:focus-visible, .field input[type="text"]:focus-visible, .field input[type="password"]:focus-visible, .field input[type="number"]:focus-visible, .field input[type="url"]:focus-visible, .edit-input:focus-visible, textarea.input:focus-visible {
621
+ outline: none;
622
+ border-color: var(--accent);
623
+ box-shadow: var(--ring);
624
+ }
625
+ .input:disabled { opacity: 0.6; cursor: not-allowed; background: var(--surface-2); }
626
+ .input-sm { height: 30px; font-size: 12px; padding: 0 10px; font-family: var(--sans); }
627
+ .input-search { width: 200px; max-width: 100%; }
628
+ .field input.invalid, .edit-input.invalid, .input.invalid { border-color: var(--down); }
629
+ .field input.invalid:focus-visible, .edit-input.invalid:focus-visible, .input.invalid:focus-visible { box-shadow: 0 0 0 3px var(--down-ring); }
630
+ .edit-input { height: 30px; min-width: 0; padding: 0 10px; }
631
+ input[type="checkbox"] {
632
+ appearance: none;
633
+ -webkit-appearance: none;
634
+ position: relative;
635
+ width: 16px; height: 16px;
636
+ margin: 0;
637
+ flex-shrink: 0;
638
+ border: 1.5px solid var(--border-strong);
639
+ border-radius: 4.5px;
640
+ background: var(--surface);
641
+ cursor: pointer;
642
+ transition: background-color var(--dur-1) ease, border-color var(--dur-1) ease, box-shadow var(--dur-1) ease;
643
+ }
644
+ input[type="checkbox"]:hover:not(:disabled) { border-color: var(--muted); }
645
+ input[type="checkbox"]:checked { background: var(--accent); border-color: var(--accent); }
646
+ input[type="checkbox"]::after {
647
+ content: "";
648
+ position: absolute;
649
+ left: 4.5px; top: 1.5px;
650
+ width: 4px; height: 8px;
651
+ border: solid var(--accent-fg);
652
+ border-width: 0 2px 2px 0;
653
+ transform: rotate(45deg) scale(0);
654
+ transition: transform var(--dur-1) var(--ease);
655
+ }
656
+ input[type="checkbox"]:checked::after { transform: rotate(45deg) scale(1); }
657
+ input[type="checkbox"]:focus-visible { outline: none; box-shadow: var(--ring); border-color: var(--accent); }
658
+ input[type="checkbox"]:disabled { opacity: 0.5; cursor: not-allowed; }
659
+ input[type="radio"] { accent-color: var(--accent); width: 16px; height: 16px; margin: 0; }
660
+ select.input, .field select {
661
+ height: 36px;
662
+ width: 100%;
663
+ padding: 0 30px 0 12px;
664
+ font-family: var(--sans);
665
+ font-size: 13px;
666
+ color: var(--fg);
667
+ background-color: var(--surface);
668
+ border: 1px solid var(--border-strong);
669
+ border-radius: var(--radius-sm);
670
+ box-shadow: var(--shadow-xs);
671
+ appearance: none;
672
+ -webkit-appearance: none;
673
+ cursor: pointer;
674
+ background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%);
675
+ background-position: calc(100% - 16px) center, calc(100% - 11px) center;
676
+ background-size: 5px 5px, 5px 5px;
677
+ background-repeat: no-repeat;
678
+ transition: border-color var(--dur-1) ease, box-shadow var(--dur-1) ease;
679
+ }
680
+ select.input:focus-visible, .field select:focus-visible {
681
+ outline: none;
682
+ border-color: var(--accent);
683
+ box-shadow: var(--ring);
684
+ }
685
+ select.input-sm { height: 28px; font-size: 12px; padding: 0 24px 0 8px; background-position: calc(100% - 13px) center, calc(100% - 8px) center; }
686
+ .field-hint { font-size: 12px; color: var(--muted); margin: 0; }
687
+ .check-card {
688
+ display: flex;
689
+ align-items: flex-start;
690
+ gap: 12px;
691
+ padding: 12px 14px;
692
+ border: 1px solid var(--border);
693
+ border-radius: var(--radius-md);
694
+ background: var(--surface);
695
+ cursor: pointer;
696
+ transition: border-color var(--dur-1) ease, background-color var(--dur-1) ease, box-shadow var(--dur-1) ease;
697
+ }
698
+ .check-card:hover { border-color: var(--border-strong); }
699
+ .check-card input { margin-top: 2px; }
700
+ .check-card:has(input:checked) { border-color: var(--accent); background: var(--accent-bg); }
701
+ .check-card:has(input:focus-visible) { box-shadow: var(--ring); }
702
+ .check-card:has(input:disabled) { opacity: 0.6; cursor: not-allowed; }
703
+ .check-card-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
704
+ .check-card-title { font-size: 13.5px; font-weight: 550; color: var(--fg); }
705
+ .check-card-desc { font-size: var(--fs-sm); color: var(--muted); line-height: 1.45; }
706
+ .check-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 8px; }
707
+ /* ---------- add host form ---------- */
708
+ .form-grid { display: flex; flex-direction: column; gap: 14px; }
709
+ .field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
710
+ .field label { font-size: 12.5px; color: var(--fg-2); font-weight: 500; }
711
+ .form-checks { display: flex; flex-wrap: wrap; gap: 8px 18px; padding-top: 2px; }
712
+ .field-checkbox { flex-direction: row; align-items: center; gap: 8px; }
713
+ .field-checkbox label { font-size: 13px; color: var(--fg-2); font-weight: 400; cursor: pointer; }
714
+ .field-error {
715
+ font-size: 13px;
716
+ color: var(--down);
717
+ background: var(--down-bg);
718
+ border: 1px solid var(--down);
719
+ border-radius: var(--radius-sm);
720
+ padding: 8px 12px;
721
+ margin-top: 14px;
722
+ display: flex;
723
+ flex-wrap: wrap;
724
+ align-items: center;
725
+ gap: 8px;
726
+ }
727
+
728
+ /* ---------- system list ---------- */
729
+ .stat-list { display: flex; flex-direction: column; }
730
+ .stat-row {
731
+ display: flex;
732
+ align-items: flex-start;
733
+ justify-content: space-between;
734
+ gap: 16px;
735
+ padding: 14px 20px;
736
+ border-bottom: 1px solid var(--border);
737
+ }
738
+ .stat-row:last-child { border-bottom: none; }
739
+ .stat-main { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
740
+ .stat-side { flex-shrink: 0; padding-top: 2px; }
741
+ .tile-value { font-size: 14px; font-weight: 600; color: var(--fg); }
742
+ .tile-value.ok { color: var(--up); }
743
+ .tile-value.warn { color: var(--warn); }
744
+ .tile-sub { font-size: 12px; color: var(--muted); overflow-wrap: anywhere; }
745
+ .tile-sub.mono { font-size: 11.5px; }
746
+ .tile-hint { font-size: 12px; color: var(--warn); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 4px; }
747
+ .tile-hint code { overflow-wrap: anywhere; }
748
+ .tile-actions { margin-top: 10px; }
749
+
750
+ /* ---------- hosts table ---------- */
751
+ .table-wrap { overflow-x: auto; }
752
+ table { width: 100%; border-collapse: collapse; min-width: 720px; }
753
+ th, td {
754
+ text-align: left;
755
+ padding: 12px 12px;
756
+ border-bottom: 1px solid var(--border);
757
+ font-size: 13px;
758
+ white-space: nowrap;
759
+ vertical-align: middle;
760
+ }
761
+ th:first-child, td:first-child { padding-left: 20px; }
762
+ th:last-child, td:last-child { padding-right: 20px; }
763
+ th {
764
+ color: var(--muted);
765
+ font-weight: 500;
766
+ font-size: 11.5px;
767
+ letter-spacing: 0.04em;
768
+ text-transform: uppercase;
769
+ background: var(--surface-2);
770
+ padding-top: 9px;
771
+ padding-bottom: 9px;
772
+ }
773
+ th.num, td.num { text-align: right; }
774
+ th.actions { text-align: right; }
775
+ tbody tr { transition: background 0.15s ease; }
776
+ tbody tr:hover { background: var(--surface-2); }
777
+ tbody tr:last-child td { border-bottom: none; }
778
+ tbody tr:last-child td:first-child { border-bottom-left-radius: var(--radius); }
779
+ tbody tr:last-child td:last-child { border-bottom-right-radius: var(--radius); }
780
+ #table th:nth-child(2), #table td.arrow { display: none; }
781
+ #table tbody td { padding-top: 10px; padding-bottom: 10px; }
782
+ .host-cell { display: flex; align-items: center; gap: 8px; font-weight: 500; }
783
+ .host-cell a { color: var(--fg); }
784
+ .host-cell a:hover { color: var(--accent); }
785
+ .host-target, .traffic { font-size: 12px; color: var(--muted); margin-top: 3px; font-family: var(--mono); }
786
+ .host-target { max-width: 240px; overflow: hidden; text-overflow: ellipsis; }
787
+ .host-td .edit-input { margin-top: 6px; width: 100%; min-width: 150px; }
788
+ .host-cell .copy-btn, .tunnel-status .copy-btn { opacity: 0; transition: opacity 0.12s ease; }
789
+ tbody tr:hover .copy-btn, .copy-btn:focus-visible, #table tbody td:hover .copy-btn { opacity: 1; }
790
+ tr.row-down td:first-child { box-shadow: inset 3px 0 0 var(--down); }
791
+ .pill { display: inline-flex; align-items: center; gap: 7px; font-family: var(--mono); font-size: 12px; }
792
+ .dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; background: var(--muted-dim); }
793
+ .dot.up { background: var(--up); box-shadow: 0 0 0 3px var(--up-bg); }
794
+ .dot.down { background: var(--down); box-shadow: 0 0 0 3px var(--down-bg); }
795
+ .dot.warn { background: var(--warn); box-shadow: 0 0 0 3px var(--warn-bg); }
796
+ .dot.accent { background: var(--accent); box-shadow: 0 0 0 3px var(--accent-bg); }
797
+ .edit-hint { font-size: 12px; }
798
+ td.edit-error { color: var(--down); white-space: normal; }
799
+ td .inline-check + .inline-check { margin-top: 4px; }
800
+ .inline-check { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
801
+ .tunnel-cell { white-space: normal; }
802
+ .tunnel-status { display: flex; align-items: center; gap: 6px; max-width: 240px; }
803
+ .tunnel-login { display: flex; flex-direction: column; gap: 2px; font-size: 12px; white-space: nowrap; }
804
+ .tunnel-login a { font-weight: 500; }
805
+ .tunnel-status a { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 180px; display: inline-block; vertical-align: middle; font-size: 12px; }
806
+ .tunnel-error { color: var(--down); cursor: help; }
807
+ .tunnel-error-text {
808
+ color: var(--down);
809
+ cursor: help;
810
+ overflow: hidden;
811
+ text-overflow: ellipsis;
812
+ white-space: nowrap;
813
+ max-width: 170px;
814
+ display: inline-block;
815
+ vertical-align: middle;
816
+ font-size: 12px;
817
+ }
818
+ .spinner {
819
+ display: inline-block;
820
+ width: 12px; height: 12px;
821
+ border-radius: 50%;
822
+ border: 2px solid var(--border-strong);
823
+ border-top-color: var(--accent);
824
+ animation: spin 0.7s linear infinite;
825
+ }
826
+ @keyframes spin { to { transform: rotate(360deg); } }
827
+
828
+ .empty {
829
+ padding: 40px 24px;
830
+ color: var(--muted);
831
+ text-align: center;
832
+ font-size: 13.5px;
833
+ display: flex;
834
+ flex-direction: column;
835
+ align-items: center;
836
+ gap: 12px;
837
+ }
838
+ .empty p { margin: 0; }
839
+ .empty-icon {
840
+ width: 40px; height: 40px;
841
+ border-radius: 10px;
842
+ border: 1px dashed var(--border-strong);
843
+ background: var(--surface-2);
844
+ display: flex;
845
+ align-items: center;
846
+ justify-content: center;
847
+ color: var(--muted-dim);
848
+ font-size: 18px;
849
+ }
850
+ #hosts-error .empty-icon { color: var(--down); border-color: var(--down); background: var(--down-bg); }
851
+ .empty pre { display: inline-block; text-align: left; }
852
+
853
+ pre.code-block {
854
+ background: var(--surface-2);
855
+ border: 1px solid var(--border);
856
+ border-radius: var(--radius-sm);
857
+ padding: 10px 12px;
858
+ overflow-x: auto;
859
+ white-space: pre-wrap;
860
+ word-break: break-all;
861
+ font-family: var(--mono);
862
+ font-size: 12px;
863
+ line-height: 1.55;
864
+ color: var(--fg-2);
865
+ margin: 0;
866
+ }
867
+ .cli-list { display: flex; flex-direction: column; gap: 16px; }
868
+ .cli-item { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
869
+ .cli-item-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
870
+
871
+ /* ---------- remote access ---------- */
872
+ .hub-section { padding: 16px 20px; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 10px; }
873
+ .hub-section:first-child { border-top: none; }
874
+ .hub-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
875
+ .hub-url { font-family: var(--mono); font-size: 12.5px; word-break: break-all; color: var(--fg-2); }
876
+ .hub-warn { font-size: 12px; color: var(--warn); background: var(--warn-bg); border-radius: var(--radius-sm); padding: 6px 10px; }
877
+ .hub-actions, .invite-actions { display: flex; gap: 8px; flex-wrap: wrap; }
878
+ .hub-form-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: flex-end; }
879
+ .hub-form-row .field { flex: 1; min-width: 140px; }
880
+ .invite-result { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px 12px; display: flex; flex-direction: column; gap: 8px; }
881
+ .invite-result-row { display: flex; align-items: center; gap: 8px; }
882
+ .invite-string { font-family: var(--mono); font-size: 11.5px; word-break: break-all; flex: 1; min-width: 0; }
883
+ .invite-countdown { font-family: var(--mono); font-size: 12px; color: var(--muted); white-space: nowrap; }
884
+ .host-checks { display: flex; flex-direction: column; gap: 6px; max-height: 140px; overflow-y: auto; border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 8px 10px; }
885
+ .host-checks[hidden] { display: none; }
886
+ .host-checks label { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--fg-2); cursor: pointer; }
887
+ .peer-list, .invite-list, .remote-list { display: flex; flex-direction: column; }
888
+ .remote-list:not(:empty) { padding: 0 20px 12px; }
889
+ .peer-row, .invite-row, .remote-item { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 0; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
890
+ .peer-row:last-child, .invite-row:last-child, .remote-item:last-child { border-bottom: none; }
891
+ .peer-main, .remote-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
892
+ .peer-name { font-weight: 500; font-size: 13px; }
893
+ .peer-controls, .remote-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
894
+ .remote-item { flex-direction: column; align-items: stretch; }
895
+ .remote-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
896
+ .remote-hosts-list { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
897
+ .remote-host-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; padding: 6px 0; border-top: 1px solid var(--border); }
898
+ .remote-host-row:first-child { border-top: none; }
899
+ .badge-role { text-transform: capitalize; }
900
+ .remote-card-body { display: flex; flex-direction: column; gap: 14px; }
901
+ .mini-form { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
902
+ .mini-form input { flex: 1; min-width: 90px; }
903
+ .host-cell .badge-via { margin-left: 4px; }
904
+
905
+ /* ---------- banners ---------- */
906
+ .banner {
907
+ border-radius: var(--radius);
908
+ padding: 12px 16px;
909
+ font-size: 13.5px;
910
+ border: 1px solid var(--down);
911
+ background: var(--down-bg);
912
+ color: var(--fg);
913
+ }
914
+ .banner-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
915
+ .banner .btn { background: var(--surface); }
916
+
917
+ /* ---------- logs ---------- */
918
+ details.card summary {
919
+ cursor: pointer;
920
+ list-style: none;
921
+ padding: 16px 20px;
922
+ display: flex;
923
+ align-items: center;
924
+ justify-content: space-between;
925
+ gap: 10px;
926
+ border-radius: var(--radius);
927
+ }
928
+ details.card summary::-webkit-details-marker { display: none; }
929
+ details.card summary .card-title { display: flex; align-items: center; gap: 8px; }
930
+ details.card summary .card-title::before {
931
+ content: "";
932
+ width: 6px; height: 6px;
933
+ border-right: 1.5px solid var(--muted);
934
+ border-bottom: 1.5px solid var(--muted);
935
+ transform: rotate(-45deg);
936
+ transition: transform 0.15s ease;
937
+ margin-right: 2px;
938
+ }
939
+ details.card[open] summary .card-title::before { transform: rotate(45deg); }
940
+ details.card[open] summary { border-bottom: 1px solid var(--border); border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
941
+ .logs-body { padding: 16px 20px 20px; display: flex; flex-direction: column; gap: 12px; }
942
+ .logs-actions { display: flex; gap: 8px; }
943
+ .logs-box {
944
+ height: 280px;
945
+ overflow-y: auto;
946
+ background: var(--surface-2);
947
+ border: 1px solid var(--border);
948
+ border-radius: var(--radius-sm);
949
+ padding: 12px 14px;
950
+ font-family: var(--mono);
951
+ font-size: 12px;
952
+ line-height: 1.55;
953
+ color: var(--fg-2);
954
+ white-space: pre-wrap;
955
+ word-break: break-all;
956
+ margin: 0;
957
+ }
958
+
959
+ /* ---------- dialogs (all <dialog>s; bottom sheets on <=640px) ---------- */
960
+ dialog {
961
+ width: min(460px, calc(100vw - 32px));
962
+ max-height: calc(100vh - 48px);
963
+ max-height: calc(100dvh - 48px);
964
+ padding: 0;
965
+ border: 1px solid var(--border);
966
+ border-radius: var(--radius-lg);
967
+ background: var(--surface-raised);
968
+ color: var(--fg);
969
+ box-shadow: var(--shadow-lg);
970
+ overflow: hidden;
971
+ }
972
+ dialog[open] { display: flex; flex-direction: column; animation: dlg-in var(--dur-3) var(--ease); }
973
+ dialog::backdrop { background: var(--overlay); }
974
+ dialog[open]::backdrop { animation: fade-in var(--dur-3) var(--ease); }
975
+ dialog.closing { animation: dlg-out 150ms var(--ease-in) forwards; pointer-events: none; }
976
+ dialog.closing::backdrop { animation: fade-out 150ms var(--ease-in) forwards; }
977
+ @keyframes dlg-in { from { opacity: 0; transform: translateY(8px) scale(0.97); } to { opacity: 1; transform: none; } }
978
+ @keyframes dlg-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(4px) scale(0.98); } }
979
+ @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
980
+ @keyframes fade-out { from { opacity: 1; } to { opacity: 0; } }
981
+ #allow-dialog { width: min(520px, calc(100vw - 32px)); }
982
+ .modal-head {
983
+ display: flex;
984
+ align-items: flex-start;
985
+ justify-content: space-between;
986
+ gap: 12px;
987
+ padding: 18px 20px 14px;
988
+ border-bottom: 1px solid var(--border);
989
+ flex-shrink: 0;
990
+ }
991
+ .modal-head > div { min-width: 0; }
992
+ .modal-title { font-size: 16px; font-weight: 650; margin: 0; letter-spacing: -0.015em; line-height: 1.35; }
993
+ .modal-sub { margin: 3px 0 0; font-size: var(--fs-sm); color: var(--muted); overflow-wrap: anywhere; }
994
+ .modal-close { width: 30px; height: 30px; padding: 0; border-radius: var(--radius-full); flex-shrink: 0; margin: -4px -6px 0 0; }
995
+ .modal-close svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
996
+ .modal-body { padding: 20px; display: flex; flex-direction: column; gap: 14px; overflow-y: auto; overscroll-behavior: contain; flex: 1 1 auto; min-height: 0; }
997
+ .modal-desc { margin: 0; font-size: 13.5px; color: var(--muted); line-height: 1.55; }
998
+ .modal-warn {
999
+ font-size: var(--fs-sm);
1000
+ color: var(--warn);
1001
+ background: var(--warn-bg);
1002
+ border: 1px solid color-mix(in srgb, var(--warn) 40%, transparent);
1003
+ border-radius: var(--radius-sm);
1004
+ padding: 8px 12px;
1005
+ line-height: 1.5;
1006
+ }
1007
+ .allow-list { display: flex; flex-direction: column; gap: 6px; max-height: 220px; overflow-y: auto; }
1008
+ .allow-row {
1009
+ display: flex;
1010
+ align-items: center;
1011
+ justify-content: space-between;
1012
+ gap: 10px;
1013
+ padding: 6px 10px;
1014
+ border: 1px solid var(--border);
1015
+ border-radius: var(--radius-sm);
1016
+ background: var(--surface-2);
1017
+ }
1018
+ .allow-row span { font-size: 13px; word-break: break-all; }
1019
+ .allow-remove {
1020
+ display: inline-flex;
1021
+ align-items: center;
1022
+ justify-content: center;
1023
+ width: 22px;
1024
+ height: 22px;
1025
+ padding: 0;
1026
+ font-size: 15px;
1027
+ line-height: 1;
1028
+ border-radius: var(--radius-xs);
1029
+ border: 1px solid transparent;
1030
+ background: transparent;
1031
+ color: var(--muted);
1032
+ cursor: pointer;
1033
+ flex-shrink: 0;
1034
+ }
1035
+ .allow-remove:hover { color: var(--down); background: var(--down-bg); border-color: transparent; }
1036
+ .allow-empty { font-size: 13px; padding: 4px 0; }
1037
+ .allow-add-row { display: flex; gap: 8px; }
1038
+ .allow-add-row .input { flex: 1; }
1039
+ .allow-add-row .btn { height: auto; align-self: stretch; padding: 0 14px; }
1040
+ .modal-hint { margin: 0; font-size: 12px; color: var(--muted-dim); }
1041
+ .modal-foot {
1042
+ display: flex;
1043
+ justify-content: flex-end;
1044
+ align-items: center;
1045
+ flex-wrap: wrap;
1046
+ gap: 8px;
1047
+ padding: 14px 20px;
1048
+ border-top: 1px solid var(--border);
1049
+ background: var(--surface-2);
1050
+ flex-shrink: 0;
1051
+ }
1052
+ #confirm-dialog, #prompt-dialog { width: min(420px, calc(100vw - 32px)); }
1053
+ #share-dialog, #protect-dialog { width: min(480px, calc(100vw - 32px)); }
1054
+ #confirm-dialog .modal-head { border-bottom: 0; padding-bottom: 0; }
1055
+ #confirm-dialog .modal-body { padding-top: 8px; }
1056
+ .field-password { position: relative; }
1057
+ .field-password .input { width: 100%; padding-right: 40px; }
1058
+ .field-toggle {
1059
+ position: absolute;
1060
+ top: 4px;
1061
+ right: 4px;
1062
+ width: 28px;
1063
+ height: 28px;
1064
+ padding: 0;
1065
+ border: 1px solid transparent;
1066
+ border-radius: var(--radius-xs);
1067
+ background: transparent;
1068
+ color: var(--muted);
1069
+ cursor: pointer;
1070
+ display: inline-flex;
1071
+ align-items: center;
1072
+ justify-content: center;
1073
+ }
1074
+ .field-toggle:hover { background: var(--surface-2); color: var(--fg); }
1075
+ .field-toggle svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
1076
+ .option-check {
1077
+ width: 16px;
1078
+ height: 16px;
1079
+ margin-top: 2px;
1080
+ flex-shrink: 0;
1081
+ border-radius: 4px;
1082
+ border: 1.5px solid var(--border-strong);
1083
+ background: var(--surface);
1084
+ position: relative;
1085
+ }
1086
+ .option-card[aria-checked="true"] .option-check { border-color: var(--accent); background: var(--accent); }
1087
+ .option-card[aria-checked="true"] .option-check::after {
1088
+ content: "";
1089
+ position: absolute;
1090
+ left: 4px;
1091
+ top: 1px;
1092
+ width: 4px;
1093
+ height: 8px;
1094
+ border: solid var(--accent-fg);
1095
+ border-width: 0 2px 2px 0;
1096
+ transform: rotate(45deg);
1097
+ }
1098
+ #protect-remove { margin-right: auto; }
1099
+ .share-options { display: flex; flex-direction: column; gap: 10px; }
1100
+ .option-card {
1101
+ display: flex;
1102
+ align-items: flex-start;
1103
+ gap: 12px;
1104
+ width: 100%;
1105
+ text-align: left;
1106
+ padding: 14px;
1107
+ border: 1px solid var(--border);
1108
+ border-radius: var(--radius);
1109
+ background: var(--surface-2);
1110
+ color: inherit;
1111
+ font: inherit;
1112
+ cursor: pointer;
1113
+ }
1114
+ .option-card:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-bg); }
1115
+ .option-card[aria-checked="true"] { border-color: var(--accent); background: var(--accent-bg); }
1116
+ .option-dot {
1117
+ width: 16px;
1118
+ height: 16px;
1119
+ margin-top: 2px;
1120
+ flex-shrink: 0;
1121
+ border-radius: 999px;
1122
+ border: 1.5px solid var(--border-strong);
1123
+ background: var(--surface);
1124
+ position: relative;
1125
+ }
1126
+ .option-card[aria-checked="true"] .option-dot { border-color: var(--accent); }
1127
+ .option-card[aria-checked="true"] .option-dot::after {
1128
+ content: "";
1129
+ position: absolute;
1130
+ top: 3px;
1131
+ left: 3px;
1132
+ width: 8px;
1133
+ height: 8px;
1134
+ border-radius: 999px;
1135
+ background: var(--accent);
1136
+ }
1137
+ .option-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
1138
+ .option-title { font-size: 13.5px; font-weight: 600; }
1139
+ .option-desc { font-size: 12.5px; color: var(--muted); line-height: 1.45; }
1140
+ .option-extra { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; }
1141
+ .option-extra .field-error { margin-top: 4px; }
1142
+ .share-progress { display: flex; gap: 14px; align-items: flex-start; padding: 16px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2); }
1143
+ .share-progress-icon { width: 36px; height: 36px; flex-shrink: 0; border-radius: 999px; display: grid; place-items: center; background: var(--accent-bg); color: var(--accent); }
1144
+ .share-progress-icon svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
1145
+ .share-progress-icon .spinner { margin: 0; }
1146
+ .share-progress-body { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
1147
+ .share-progress-title { font-size: 14px; font-weight: 600; margin: 0; overflow-wrap: anywhere; }
1148
+ .share-progress-text { font-size: 12.5px; color: var(--muted); line-height: 1.5; margin: 0; }
1149
+ .share-progress-body .btn { align-self: flex-start; margin-top: 6px; text-decoration: none; }
1150
+ .share-steps { margin: 2px 0 0; padding-left: 18px; font-size: 12.5px; color: var(--muted); line-height: 1.6; }
1151
+
1152
+ /* ---------- toasts ---------- */
1153
+ .toast-container {
1154
+ position: fixed;
1155
+ inset: auto 20px 20px auto;
1156
+ margin: 0;
1157
+ padding: 0;
1158
+ border: 0;
1159
+ background: transparent;
1160
+ overflow: visible;
1161
+ z-index: 90;
1162
+ display: flex;
1163
+ flex-direction: column;
1164
+ align-items: flex-end;
1165
+ gap: 8px;
1166
+ width: 380px;
1167
+ max-width: calc(100vw - 24px);
1168
+ pointer-events: none;
1169
+ }
1170
+ .toast-container:empty { display: none; }
1171
+ .toast {
1172
+ position: relative;
1173
+ pointer-events: auto;
1174
+ width: 100%;
1175
+ background: var(--surface-raised);
1176
+ color: var(--fg);
1177
+ border: 1px solid var(--border);
1178
+ border-radius: var(--radius-md);
1179
+ padding: 12px 14px 12px 38px;
1180
+ font-size: 13px;
1181
+ line-height: 1.45;
1182
+ box-shadow: var(--shadow-lg);
1183
+ overflow-wrap: anywhere;
1184
+ opacity: 0;
1185
+ transform: translateY(10px) scale(0.97);
1186
+ transition: opacity var(--dur-2) var(--ease), transform var(--dur-3) var(--ease);
1187
+ }
1188
+ .toast::before {
1189
+ content: "";
1190
+ position: absolute;
1191
+ left: 14px; top: 15px;
1192
+ width: 10px; height: 10px;
1193
+ border-radius: 50%;
1194
+ background: var(--accent);
1195
+ box-shadow: 0 0 0 4px var(--accent-bg);
1196
+ }
1197
+ .toast.show { opacity: 1; transform: none; }
1198
+ .toast-success::before { background: var(--up); box-shadow: 0 0 0 4px var(--up-bg); }
1199
+ .toast-error::before { background: var(--down); box-shadow: 0 0 0 4px var(--down-bg); }
1200
+ .toast-warn::before { background: var(--warn); box-shadow: 0 0 0 4px var(--warn-bg); }
1201
+ .toast-error { border-color: var(--down-ring); }
1202
+ .toast-hint { margin-top: 8px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
1203
+ .toast-hint code { font-size: 11.5px; color: var(--fg-2); background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-xs); padding: 2px 6px; }
1204
+
1205
+ /* ---------- skeleton / boot ---------- */
1206
+ @keyframes skeleton-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
1207
+ body.boot .tile-value,
1208
+ body.boot .tile-sub,
1209
+ body.boot #version,
1210
+ body.boot #uptime,
1211
+ body.boot #pid,
1212
+ .skel-bar, .skel-line, .skel-circle, .skel-block {
1213
+ background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
1214
+ background-size: 400% 100%;
1215
+ animation: skeleton-shimmer 1.4s ease infinite;
1216
+ }
1217
+ body.boot .tile-value,
1218
+ body.boot .tile-sub,
1219
+ body.boot #version,
1220
+ body.boot #uptime,
1221
+ body.boot #pid {
1222
+ color: transparent !important;
1223
+ display: inline-block;
1224
+ min-width: 70px;
1225
+ border-radius: 4px;
1226
+ border-color: transparent;
1227
+ }
1228
+ body.boot #updated { visibility: hidden; }
1229
+ body.boot #stop-proxy-btn, body.offline #stop-proxy-btn { display: none; }
1230
+ .skel-bar { display: block; width: 80%; height: 14px; border-radius: 4px; }
1231
+ .skel-line { display: block; width: 100%; height: 12px; border-radius: 4px; }
1232
+ .skel-line + .skel-line { margin-top: 8px; }
1233
+ .skel-line.short { width: 40%; }
1234
+ .skel-circle { display: inline-block; width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0; }
1235
+ .skel-block { display: block; width: 100%; height: 72px; border-radius: var(--radius); }
1236
+ #hosts-skeleton table { min-width: 0; }
1237
+ .skeleton-row td { border-bottom: 1px solid var(--border); padding: 17px 20px; }
1238
+ .skeleton-row:nth-child(1) .skel-bar { width: 92%; }
1239
+ .skeleton-row:nth-child(2) .skel-bar { width: 70%; }
1240
+ .skeleton-row:nth-child(3) .skel-bar { width: 82%; }
1241
+
1242
+ footer { text-align: center; padding: 8px 20px 32px; color: var(--muted-dim); font-size: 12px; }
1243
+ footer a { color: var(--muted); }
1244
+ [hidden] { display: none !important; }
1245
+
1246
+ /* ---------- pages ---------- */
1247
+ .view { display: flex; flex-direction: column; gap: 24px; min-width: 0; }
1248
+ .tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
1249
+ @media (min-width: 1100px) { .tiles { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
1250
+ .tile { display: flex; flex-direction: column; gap: 4px; padding: 16px 18px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-sm); color: var(--fg); min-width: 0; transition: border-color 0.15s ease; }
1251
+ .tile:hover { border-color: var(--border-strong); text-decoration: none; }
1252
+ .tile-big { font-size: 26px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.2; }
1253
+ .tile-big.tile-word { font-size: 18px; line-height: 1.8; }
1254
+ .tile-big.ok { color: var(--up); }
1255
+ .tile-big.bad { color: var(--down); }
1256
+ .tile-big.warn { color: var(--warn); }
1257
+ .tile .tile-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1258
+ .grid-2 { display: grid; grid-template-columns: minmax(0, 1fr); gap: 24px; align-items: start; }
1259
+ @media (min-width: 1000px) { .grid-2 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
1260
+ .ov-list { display: flex; flex-direction: column; }
1261
+ .ov-row { display: flex; align-items: center; gap: 12px; padding: 12px 20px; border-bottom: 1px solid var(--border); min-width: 0; }
1262
+ .ov-row:last-child { border-bottom: none; }
1263
+ .ov-main { display: flex; flex-direction: column; min-width: 0; flex: 1; }
1264
+ .ov-title { font-family: var(--mono); font-size: 13px; color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1265
+ #ov-checks .ov-title { font-family: var(--sans); }
1266
+ .ov-sub { font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1267
+ .ov-side { display: flex; align-items: center; gap: 8px; flex-shrink: 0; font-size: 12px; color: var(--muted); }
1268
+ .ov-side a.ov-link { max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--mono); }
1269
+ .ov-more { padding: 10px 20px; font-size: 12.5px; }
1270
+ .logs-full { height: calc(100vh - 290px); min-height: 320px; max-height: none; margin: 0; border-radius: 0; border: 0; border-top: 1px solid var(--border); }
1271
+ .logs-short { height: auto; max-height: 220px; margin: 0; border-radius: 0 0 var(--radius) var(--radius); border: 0; }
1272
+ .logs-head { flex-wrap: wrap; gap: 10px; }
1273
+ .logs-tools { flex-wrap: wrap; gap: 8px; flex: 1; min-width: 0; }
1274
+ .logs-tools .input { width: auto; }
1275
+ .logs-tools select.input { padding-right: 28px; }
1276
+ .logs-tools .input-search { width: 220px; }
1277
+ .logs-meta { padding: 8px 20px; font-size: 12px; border-top: 1px solid var(--border); }
1278
+ .check-inline { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--fg-2); cursor: pointer; }
1279
+ select.input { padding-right: 8px; cursor: pointer; }
1280
+ .segmented { display: inline-flex; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
1281
+ .segmented button { border: 0; background: var(--surface); color: var(--muted); font-size: 12.5px; padding: 6px 12px; cursor: pointer; }
1282
+ .segmented button + button { border-left: 1px solid var(--border); }
1283
+ .segmented button[aria-checked="true"] { background: var(--accent-bg); color: var(--accent-strong); font-weight: 600; }
1284
+ .form-row-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-bottom: 10px; }
1285
+ .form-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
1286
+ #ports-form .hub-warn { margin-top: 12px; }
1287
+ .token-body { display: flex; flex-direction: column; gap: 12px; }
1288
+ .token-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
1289
+ .token-value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; padding: 6px 10px; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-xs); }
1290
+ .help-list { display: flex; flex-direction: column; gap: 10px; font-size: 13px; color: var(--fg-2); }
1291
+ .help-list p { margin: 0; }
1292
+ .toggle-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border); }
1293
+ .remote-local { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
1294
+ .remote-local a { font-family: var(--mono); font-size: 13px; }
1295
+ .role-table { display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: var(--radius-sm); }
1296
+ .role-table div { display: flex; gap: 10px; padding: 8px 12px; font-size: 12.5px; }
1297
+ .role-table div + div { border-top: 1px solid var(--border); }
1298
+ .role-table strong { width: 56px; flex-shrink: 0; }
1299
+ .role-table span { color: var(--muted); }
1300
+ .danger-card { border-color: var(--down-bg); }
1301
+ .danger-card .card-title { color: var(--down); }
1302
+ .sb-dot { margin-left: auto; }
1303
+ input.input[type="number"] { font-family: var(--mono); }
1304
+
1305
+ /* ---------- responsive ---------- */
1306
+ @media (min-width: 1200px) {
1307
+ th, td { padding-left: 10px; padding-right: 10px; }
1308
+ .tunnel-status .copy-btn { display: none; }
1309
+ .tunnel-status a { max-width: 150px; }
1310
+ .host-target { max-width: 200px; }
1311
+ }
1312
+ @media (min-width: 1200px) and (max-width: 1399px) {
1313
+ .tunnel-status a { max-width: 110px; }
1314
+ .host-target { max-width: 170px; }
1315
+ }
1316
+ @media (max-width: 1099px) {
1317
+ .topbar, main { padding-left: 24px; padding-right: 24px; }
1318
+ }
1319
+ @media (min-width: 700px) and (max-width: 1099px) {
1320
+ .tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); }
1321
+ }
1322
+ @media (min-width: 641px) and (max-width: 899px), (min-width: 1100px) and (max-width: 1279px) {
1323
+ #table tbody { display: grid !important; grid-template-columns: repeat(2, minmax(0, 1fr)); }
1324
+ #table tbody tr:nth-child(odd) { border-right: 1px solid var(--border); }
1325
+ #table tbody tr:nth-last-child(2):nth-child(odd) { border-bottom: none; }
1326
+ }
1327
+ @media (max-width: 899px), (min-width: 1100px) and (max-width: 1279px) {
1328
+ #table { min-width: 0; }
1329
+ #table thead { display: none; }
1330
+ #table, #table tbody, #table tr { display: block; width: 100%; }
1331
+ #table tr { padding: 14px 20px; border-bottom: 1px solid var(--border); }
1332
+ #table tr:last-child { border-bottom: none; }
1333
+ #table td { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 5px 0; border: none; white-space: normal; text-align: right; min-width: 0; }
1334
+ #table td::before { content: attr(data-label); color: var(--muted); font-size: 12px; flex-shrink: 0; text-align: left; }
1335
+ #table td[data-label=""]::before { content: none; }
1336
+ #table td[data-label=""]:first-child { padding-bottom: 8px; justify-content: flex-start; font-weight: 600; }
1337
+ #table td.host-td, #table td.status-td { flex-direction: column; align-items: flex-start; gap: 0; }
1338
+ #table td.status-td { align-items: flex-end; }
1339
+ #table td.status-td::before { align-self: flex-start; margin-bottom: 4px; }
1340
+ #table td.host-td .edit-input { margin-top: 8px; }
1341
+ .host-target { max-width: 100%; }
1342
+ .host-cell .copy-btn, .tunnel-status .copy-btn { opacity: 1; }
1343
+ #table td[data-label=""] { justify-content: flex-end; padding-top: 10px; }
1344
+ #table .edit-input { min-width: 0; }
1345
+ tr.row-down td:first-child { box-shadow: none; }
1346
+ tr.row-down { box-shadow: inset 3px 0 0 var(--down); }
1347
+ .tunnel-status, .tunnel-status a, .tunnel-error-text { max-width: 100%; min-width: 0; }
1348
+ .tunnel-status { justify-content: flex-end; flex: 0 1 auto; }
1349
+ .tunnel-status a { flex: 0 1 auto; }
1350
+ }
1351
+ /* ---------- empty states ---------- */
1352
+ .empty {
1353
+ padding: 44px 24px;
1354
+ color: var(--muted);
1355
+ text-align: center;
1356
+ font-size: 13.5px;
1357
+ display: flex;
1358
+ flex-direction: column;
1359
+ align-items: center;
1360
+ gap: 10px;
1361
+ }
1362
+ .empty p, .empty-desc { margin: 0; max-width: 420px; line-height: 1.55; }
1363
+ .empty-icon {
1364
+ width: 44px; height: 44px;
1365
+ border-radius: var(--radius);
1366
+ border: 1px solid var(--border);
1367
+ background: linear-gradient(180deg, var(--surface), var(--surface-2));
1368
+ box-shadow: var(--shadow-sm);
1369
+ display: flex;
1370
+ align-items: center;
1371
+ justify-content: center;
1372
+ color: var(--muted);
1373
+ font-size: 18px;
1374
+ margin-bottom: 4px;
1375
+ }
1376
+ .empty-icon svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
1377
+ .empty-title { margin: 0; font-size: var(--fs-md); font-weight: 600; color: var(--fg); }
1378
+ .empty-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; margin-top: 6px; }
1379
+
1380
+ /* ---------- banners ---------- */
1381
+ .banner {
1382
+ border-radius: var(--radius);
1383
+ padding: 12px 16px;
1384
+ font-size: 13.5px;
1385
+ border: 1px solid var(--down-ring);
1386
+ background: var(--down-bg);
1387
+ color: var(--fg);
1388
+ animation: rise-in var(--dur-3) var(--ease);
1389
+ }
1390
+
1391
+ /* ---------- option cards / tiles polish ---------- */
1392
+ .option-card { transition: border-color var(--dur-1) ease, background-color var(--dur-1) ease, box-shadow var(--dur-1) ease; }
1393
+ .option-card:hover:not([aria-checked="true"]) { border-color: var(--border-strong); }
1394
+ .option-check, .option-dot { transition: border-color var(--dur-1) ease, background-color var(--dur-1) ease; }
1395
+ .tile { transition: border-color var(--dur-2) ease, box-shadow var(--dur-2) ease, transform var(--dur-2) var(--ease); }
1396
+ a.tile:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }
1397
+ a.tile:active { transform: none; }
1398
+ .segmented { background: var(--surface-2); padding: 2px; gap: 2px; border-radius: var(--radius-sm); }
1399
+ .segmented button { border-radius: 6px; background: transparent; transition: background-color var(--dur-1) ease, color var(--dur-1) ease; }
1400
+ .segmented button + button { border-left: 0; }
1401
+ .segmented button:hover { color: var(--fg); }
1402
+ .segmented button[aria-checked="true"] { background: var(--surface); color: var(--fg); box-shadow: var(--shadow-xs); }
1403
+
1404
+ /* ---------- tabs ---------- */
1405
+ .tabs {
1406
+ position: relative;
1407
+ display: flex;
1408
+ align-items: stretch;
1409
+ gap: 2px;
1410
+ border-bottom: 1px solid var(--border);
1411
+ overflow-x: auto;
1412
+ overflow-y: hidden;
1413
+ scrollbar-width: none;
1414
+ -webkit-overflow-scrolling: touch;
1415
+ scroll-behavior: smooth;
1416
+ flex-shrink: 0;
1417
+ }
1418
+ .tabs::-webkit-scrollbar { display: none; }
1419
+ .tabs [role="tab"] {
1420
+ position: relative;
1421
+ flex-shrink: 0;
1422
+ display: inline-flex;
1423
+ align-items: center;
1424
+ gap: 8px;
1425
+ height: 42px;
1426
+ padding: 0 12px;
1427
+ margin-bottom: -1px;
1428
+ border: 0;
1429
+ background: none;
1430
+ color: var(--muted);
1431
+ font: 550 13.5px var(--sans);
1432
+ white-space: nowrap;
1433
+ cursor: pointer;
1434
+ border-radius: var(--radius-sm) var(--radius-sm) 0 0;
1435
+ transition: color var(--dur-1) ease;
1436
+ }
1437
+ .tabs [role="tab"]::after { content: ""; position: absolute; inset: 6px 2px; border-radius: var(--radius-sm); background: var(--nav-active); opacity: 0; transition: opacity var(--dur-1) ease; z-index: -1; }
1438
+ .tabs [role="tab"]:hover { color: var(--fg); }
1439
+ .tabs [role="tab"]:hover::after { opacity: 1; }
1440
+ .tabs [role="tab"][aria-selected="true"] { color: var(--fg); }
1441
+ .tabs [role="tab"]:focus-visible { outline-offset: -4px; }
1442
+ .tabs [role="tab"] svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
1443
+ .tabs [role="tab"] .badge, .tabs [role="tab"] .tab-count { height: 18px; padding: 0 6px; font-size: 10.5px; }
1444
+ .tabs { isolation: isolate; }
1445
+ .tabs-ink {
1446
+ position: absolute;
1447
+ left: 0; bottom: 0;
1448
+ width: 1px;
1449
+ height: 2px;
1450
+ border-radius: 2px;
1451
+ background: var(--fg);
1452
+ transform-origin: 0 0;
1453
+ pointer-events: none;
1454
+ opacity: 0;
1455
+ }
1456
+ .tabs.ink-ready .tabs-ink { opacity: 1; transition: transform var(--dur-3) var(--ease); }
1457
+ .tabs.ink-ready.ink-moving .tabs-ink { transition: transform var(--dur-3) var(--ease), opacity var(--dur-1) ease; }
1458
+ [role="tabpanel"]:not([hidden]) { animation: panel-in var(--dur-2) var(--ease); }
1459
+ [role="tabpanel"]:focus-visible { outline-offset: 4px; }
1460
+ .tab-panels { display: flex; flex-direction: column; gap: 24px; min-width: 0; }
1461
+ [data-tab-panel] { min-width: 0; }
1462
+ [data-tab-panel]:not([hidden]) { display: flex; flex-direction: column; gap: 24px; }
1463
+
1464
+ /* ---------- menus (openMenu) ---------- */
1465
+ .menu {
1466
+ position: fixed;
1467
+ inset: auto;
1468
+ z-index: 85;
1469
+ margin: 0;
1470
+ min-width: 200px;
1471
+ max-width: min(320px, calc(100vw - 16px));
1472
+ max-height: min(420px, calc(100vh - 16px));
1473
+ overflow-y: auto;
1474
+ padding: 5px;
1475
+ border: 1px solid var(--border);
1476
+ border-radius: var(--radius-md);
1477
+ background: var(--surface-raised);
1478
+ color: var(--fg);
1479
+ box-shadow: var(--shadow-lg);
1480
+ font-size: 13px;
1481
+ transform-origin: top right;
1482
+ animation: menu-in var(--dur-2) var(--ease);
1483
+ }
1484
+ .menu:focus { outline: none; }
1485
+ .menu.menu-up { transform-origin: bottom right; animation-name: menu-in-up; }
1486
+ .menu.menu-start { transform-origin: top left; }
1487
+ .menu.menu-up.menu-start { transform-origin: bottom left; }
1488
+ .menu.menu-closing { animation: menu-out 120ms var(--ease-in) forwards; pointer-events: none; }
1489
+ @keyframes menu-in { from { opacity: 0; transform: translateY(-4px) scale(0.97); } to { opacity: 1; transform: none; } }
1490
+ @keyframes menu-in-up { from { opacity: 0; transform: translateY(4px) scale(0.97); } to { opacity: 1; transform: none; } }
1491
+ @keyframes menu-out { to { opacity: 0; transform: scale(0.98); } }
1492
+ .menu-item {
1493
+ display: flex;
1494
+ align-items: center;
1495
+ gap: 10px;
1496
+ width: 100%;
1497
+ min-height: 34px;
1498
+ padding: 0 10px;
1499
+ border: 0;
1500
+ border-radius: 7px;
1501
+ background: none;
1502
+ color: var(--fg);
1503
+ font: 500 13px var(--sans);
1504
+ text-align: left;
1505
+ cursor: pointer;
1506
+ white-space: nowrap;
1507
+ }
1508
+ .menu-item:hover:not(:disabled), .menu-item:focus-visible { background: var(--nav-active); outline: none; }
1509
+ .menu-item:active:not(:disabled) { background: var(--surface-3); }
1510
+ .menu-item:disabled { color: var(--muted-dim); cursor: not-allowed; }
1511
+ .menu-item.danger { color: var(--down); }
1512
+ .menu-item.danger:hover:not(:disabled), .menu-item.danger:focus-visible { background: var(--down-bg); }
1513
+ .menu-icon { width: 16px; height: 16px; flex-shrink: 0; color: var(--muted); fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
1514
+ .menu-item.danger .menu-icon { color: currentColor; }
1515
+ .menu-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
1516
+ .menu-hint { margin-left: auto; padding-left: 12px; color: var(--muted); font-size: 12px; font-family: var(--mono); overflow: hidden; text-overflow: ellipsis; }
1517
+ .menu-sep { height: 1px; margin: 5px -5px; background: var(--border); }
1518
+ .menu-info { display: flex; align-items: center; gap: 10px; min-height: 30px; padding: 0 10px; color: var(--muted); font-size: 12.5px; white-space: nowrap; }
1519
+ .menu-info .menu-hint { color: var(--fg-2); max-width: 200px; }
1520
+ .menu-title { padding: 8px 10px 6px; font-size: 12px; font-weight: 600; color: var(--muted); letter-spacing: 0.02em; }
1521
+ .menu-cancel { display: none; }
1522
+
1523
+ /* ---------- tooltips ---------- */
1524
+ .tooltip {
1525
+ position: fixed;
1526
+ z-index: 95;
1527
+ top: 0; left: 0;
1528
+ max-width: 260px;
1529
+ padding: 5px 9px;
1530
+ border-radius: var(--radius-xs);
1531
+ background: var(--tooltip-bg);
1532
+ color: var(--tooltip-fg);
1533
+ font-size: 12px;
1534
+ font-weight: 500;
1535
+ line-height: 1.35;
1536
+ pointer-events: none;
1537
+ opacity: 0;
1538
+ transition: opacity var(--dur-1) ease;
1539
+ box-shadow: var(--shadow-md);
1540
+ }
1541
+ .tooltip.show { opacity: 1; }
1542
+
1543
+ /* ---------- motion ---------- */
1544
+ @keyframes rise-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
1545
+ @keyframes panel-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
1546
+ .view:not([hidden]) { animation: rise-in var(--dur-3) var(--ease); }
1547
+ .anim-in, .anim-list > * { animation: rise-in var(--dur-2) var(--ease) both; }
1548
+ .anim-list > :nth-child(2) { animation-delay: 30ms; }
1549
+ .anim-list > :nth-child(3) { animation-delay: 60ms; }
1550
+ .anim-list > :nth-child(4) { animation-delay: 90ms; }
1551
+ .anim-list > :nth-child(5) { animation-delay: 120ms; }
1552
+ .anim-list > :nth-child(n+6) { animation-delay: 150ms; }
1553
+
1554
+ /* ---------- tablet (icon rail by default) ---------- */
1555
+ @media (min-width: 641px) and (max-width: 1099px) {
1556
+ .topbar, main { padding-left: 24px; padding-right: 24px; }
1557
+ #pid, .status-pid-sep, #updated { display: none; }
1558
+ }
1559
+
1560
+ /* ---------- mobile: compact top bar + bottom tab bar + sheets ---------- */
1561
+ @media (max-width: 640px) {
1562
+ :root, :root[data-sidebar="collapsed"] { --sb-w: 0px; --topbar-h: 52px; }
1563
+ .shell { padding-bottom: calc(var(--bottombar-h) + var(--safe-b)); }
1564
+ .topbar { padding: 0 12px 0 16px; }
1565
+ .crumbs, #updated, .status-extra, #version { display: none; }
1566
+ .brand-mobile { display: inline-block; }
1567
+ span.wordmark.brand-mobile { display: inline; }
1568
+ .status-pill { height: 36px; padding: 0 10px 0 12px; }
1569
+ .btn.theme-toggle { width: 36px; height: 36px; }
1570
+ main { padding: 20px 16px 32px; gap: 20px; }
1571
+ footer { padding-bottom: 20px; }
1572
+
1573
+ .sidebar {
1574
+ top: auto; right: 0; bottom: 0; left: 0;
1575
+ width: auto;
1576
+ height: calc(var(--bottombar-h) + var(--safe-b));
1577
+ padding-bottom: var(--safe-b);
1578
+ flex-direction: row;
1579
+ border-right: 0;
1580
+ border-top: 1px solid var(--border);
1581
+ z-index: 8;
1582
+ overflow: visible;
1583
+ }
1584
+ @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
1585
+ .sidebar {
1586
+ background: color-mix(in srgb, var(--surface) 86%, transparent);
1587
+ -webkit-backdrop-filter: saturate(180%) blur(14px);
1588
+ backdrop-filter: saturate(180%) blur(14px);
1589
+ }
1590
+ }
1591
+ .sb-head, .sb-section, .sb-foot { display: none; }
1592
+ .sb-links { flex-direction: row; align-items: stretch; flex: 1; padding: 0 4px; gap: 0; overflow: visible; }
1593
+ .sb-group { display: contents; }
1594
+ .sb-link {
1595
+ flex: 1 1 0;
1596
+ min-width: 0;
1597
+ height: var(--bottombar-h);
1598
+ flex-direction: column;
1599
+ justify-content: center;
1600
+ gap: 3px;
1601
+ padding: 0 2px;
1602
+ border-radius: 0;
1603
+ font-size: 11px;
1604
+ font-weight: 550;
1605
+ letter-spacing: 0.01em;
1606
+ background: none !important;
1607
+ }
1608
+ .sb-link .sb-label { display: block; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
1609
+ .sb-link svg { width: 22px; height: 22px; }
1610
+ .sb-link::before {
1611
+ content: "";
1612
+ position: absolute;
1613
+ top: 7px; left: 50%;
1614
+ width: 52px; height: 28px;
1615
+ margin-left: -26px;
1616
+ border-radius: var(--radius-full);
1617
+ background: var(--accent-bg);
1618
+ opacity: 0;
1619
+ transform: scaleX(0.5);
1620
+ transition: opacity var(--dur-2) ease, transform var(--dur-2) var(--ease);
1621
+ }
1622
+ .sb-link[aria-current] { color: var(--accent); }
1623
+ .sb-link[aria-current]::before { opacity: 1; transform: none; }
1624
+ .sb-link:active svg { transform: scale(0.9); }
1625
+ .sb-badges { position: absolute; top: 4px; left: calc(50% + 6px); margin: 0; gap: 2px; z-index: 2; }
1626
+ .sb-count { min-width: 17px; height: 17px; padding: 0 4px; font-size: 10px; background: var(--accent); color: var(--accent-fg); box-shadow: 0 0 0 2px var(--surface); }
1627
+ .sb-count-accent, #sb-remotes-count { display: none; }
1628
+ #sb-hub-dot { box-shadow: 0 0 0 2px var(--surface); margin-top: 3px; }
1629
+
1630
+ .page-head { grid-template-columns: minmax(0, 1fr); }
1631
+ .page-actions { grid-column: 1; grid-row: auto; justify-content: flex-start; margin-top: 8px; }
1632
+ .page-title { font-size: 20px; }
1633
+ .tabs { margin-left: -16px; margin-right: -16px; padding: 0 12px; }
1634
+ .tabs [role="tab"] { height: 44px; }
1635
+ .col-primary, .col-secondary, .layout { gap: 16px; }
1636
+ .card-head, .card-body, .card-foot, .stat-row, .logs-body, details.card summary { padding-left: 16px; padding-right: 16px; }
1637
+ #table tr { padding-left: 16px; padding-right: 16px; }
1638
+ .skeleton-row td { padding-left: 16px; padding-right: 16px; }
1639
+ .input-search { width: 100%; }
1640
+ .card-tools { width: 100%; }
1641
+ .row-actions { flex-wrap: wrap; }
1642
+ .input, select.input, .field input[type="text"], .field input[type="password"] { font-size: 16px; }
1643
+ .input-sm, select.input-sm { font-size: 14px; }
1644
+
1645
+ .toast-container { inset: auto 12px calc(var(--bottombar-h) + var(--safe-b) + 12px) 12px; width: auto; max-width: none; align-items: stretch; }
1646
+ .toast { transform: translateY(16px); }
1647
+ body:has(dialog[open]) .toast-container { inset: calc(12px + env(safe-area-inset-top, 0px)) 12px auto 12px; flex-direction: column-reverse; }
1648
+ body:has(dialog[open]) .toast { transform: translateY(-16px); }
1649
+ body:has(dialog[open]) .toast.show { transform: none; }
1650
+
1651
+ dialog, #allow-dialog, #confirm-dialog, #prompt-dialog, #share-dialog, #protect-dialog {
1652
+ width: 100%;
1653
+ max-width: 100%;
1654
+ max-height: 92vh;
1655
+ max-height: 92dvh;
1656
+ margin: auto 0 0;
1657
+ border-radius: 18px 18px 0 0;
1658
+ border-bottom: 0;
1659
+ }
1660
+ dialog[open] { animation: sheet-in var(--dur-3) var(--ease); }
1661
+ dialog.closing { animation: sheet-out 180ms var(--ease-in) forwards; }
1662
+ dialog .modal-head { padding-top: 22px; position: relative; }
1663
+ dialog .modal-head::before { content: ""; position: absolute; top: 8px; left: 50%; width: 36px; height: 4px; margin-left: -18px; border-radius: 4px; background: var(--border-strong); }
1664
+ .modal-foot { padding-bottom: calc(14px + var(--safe-b)); }
1665
+ .modal-foot .btn { flex: 1 1 0; height: 42px; }
1666
+
1667
+ .menu.menu-sheet {
1668
+ inset: auto 0 0 0;
1669
+ width: 100%;
1670
+ max-width: 100%;
1671
+ max-height: 80vh;
1672
+ padding: 20px 8px calc(8px + var(--safe-b));
1673
+ border-radius: 18px 18px 0 0;
1674
+ border-bottom: 0;
1675
+ box-shadow: 0 0 0 100vmax var(--overlay), var(--shadow-lg);
1676
+ animation: sheet-in var(--dur-3) var(--ease);
1677
+ font-size: 15px;
1678
+ }
1679
+ .menu.menu-sheet::before { content: ""; position: absolute; top: 8px; left: 50%; width: 36px; height: 4px; margin-left: -18px; border-radius: 4px; background: var(--border-strong); }
1680
+ .menu.menu-sheet.menu-closing { animation: sheet-out 180ms var(--ease-in) forwards; }
1681
+ .menu-sheet .menu-item { min-height: 48px; font-size: 15px; padding: 0 14px; border-radius: var(--radius-md); }
1682
+ .menu-sheet .menu-icon { width: 20px; height: 20px; }
1683
+ .menu-sheet .menu-info { min-height: 36px; padding: 0 14px; font-size: 14px; }
1684
+ .menu-sheet .menu-sep { margin: 6px 0; }
1685
+ .menu-sheet .menu-cancel { display: flex; justify-content: center; margin-top: 6px; background: var(--surface-2); font-weight: 600; }
1686
+ }
1687
+ @keyframes sheet-in { from { transform: translateY(100%); } to { transform: none; } }
1688
+ @keyframes sheet-out { from { transform: none; } to { transform: translateY(100%); } }
1689
+
1690
+ /* ---------- touch: comfortable targets ---------- */
1691
+ @media (pointer: coarse) {
1692
+ .btn { height: 36px; }
1693
+ .btn-sm { height: 32px; }
1694
+ .btn-icon { width: 36px; }
1695
+ .btn-sm.btn-icon { width: 32px; }
1696
+ .copy-btn { height: 28px; padding: 0 10px; }
1697
+ .host-cell .copy-btn, .tunnel-status .copy-btn { opacity: 1; }
1698
+ }
1699
+
1700
+ @media (prefers-reduced-motion: reduce) {
1701
+ *, *::before, *::after {
1702
+ animation-duration: 0.001ms !important;
1703
+ animation-delay: 0ms !important;
1704
+ animation-iteration-count: 1 !important;
1705
+ transition-duration: 0.001ms !important;
1706
+ scroll-behavior: auto !important;
1707
+ }
1708
+ .live-dot.up::after { display: none; }
1709
+ }
1710
+ `;