Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_steel/src/srv/admin/assets/style.css

31.6 KiB, 175 runs

created by r1870400018:10459, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1/*
2 * Steel admin dashboard stylesheet.
3 *
4 * Palette:
5 * --accent rgb(243, 60, 87) Hematite red for action + highlights
6 * --blue rgb(171, 202, 222) soft blue (inline code backgrounds)
7 * --grey-0 rgb(252, 252, 252) page background
8 * --grey-1 rgb(244, 244, 244) panel background
9 * --grey-2 rgb(228, 228, 228) panel borders
10 * --grey-3 rgb(190, 190, 190) strong borders
11 * --grey-4 rgb(120, 120, 120) secondary text
12 * --grey-5 rgb(60, 60, 60) primary text
13 * --green rgb(46, 125, 50) 2xx status
14 * --orange rgb(237, 108, 2) 3xx status
15 * --red rgb(198, 40, 40) 5xx status
16 * --amber rgb(191, 128, 0) Fleet: over clear, under distress
17 * --pane-a rgb( 51, 102, 204) Fleet: host resources (the blue family)
18 * --pane-b rgb(237, 108, 2) Fleet: traffic and attack (the orange family)
19 *
20 * Typography:
21 * Libertinus Serif > Georgia > Times for body and headings.
22 * Monospace: Iosevka > Source Code Pro > Menlo > monospace.
23 *
24 * Kept intentionally short. Shipped inline inside every response.
25 */
26
27/* Oxanium variable font, served from /admin/assets/oxanium.ttf
28 * so the browser caches it between pages instead of re-parsing
29 * a data URI on every response. Used for page-level headings
30 * and KPI chips via the `--font-heading` stack below. */
31@font-face {
32 font-family: 'Oxanium';
33 src: url('/admin/assets/oxanium.ttf') format('truetype-variations'),
34 url('/admin/assets/oxanium.ttf') format('truetype');
35 font-weight: 200 800;
36 font-style: normal;
37 font-display:swap;
38}
39
40:root {
41 /* Semantic names map to the light palette by default. */
42 --accent: rgb(243, 60, 87);
43 --accent-dark: rgb(213, 40, 65);
44 --blue: rgb(171, 202, 222);
45 --bg-page: rgb(252, 252, 252);
46 --bg-panel: rgb(244, 244, 244);
47 --bg-card: white;
48 --bg-inset: rgb(244, 244, 244);
49 --bg-table-row-hover: rgb(250, 250, 250);
50 --border-soft: rgb(228, 228, 228);
51 --border-firm: rgb(200, 200, 200);
52 --text-muted: rgb(120, 120, 120);
53 --text-body: rgb(60, 60, 60);
54 --text-heading: rgb(34, 34, 34);
55 --green: rgb(46, 125, 50);
56 --orange: rgb(237, 108, 2);
57 --red: rgb(198, 40, 40);
58 --amber: rgb(191, 128, 0);
59 --pane-a: rgb( 51, 102, 204);
60 --pane-b: rgb(237, 108, 2);
61 --inline-code-bg: rgb(171, 202, 222);
62 --inline-code-fg: rgb(20, 40, 60);
63 --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.05);
64 /* Corner radius for interactive controls (inputs, buttons).
65 Matches elearnity's --radius-lg so the dashboard feels
66 visually consistent with the apps it administers. */
67 --radius-input: 8px;
68}
69
70html.dark {
71 --accent: rgb(243, 78, 103);
72 --accent-dark: rgb(255, 108, 128);
73 --bg-page: rgb(19, 22, 27);
74 --bg-panel: rgb(26, 30, 38);
75 --bg-card: rgb(31, 35, 44);
76 --bg-inset: rgb(34, 39, 49);
77 --bg-table-row-hover: rgb(39, 44, 54);
78 --border-soft: rgb(46, 52, 64);
79 --border-firm: rgb(66, 72, 86);
80 --text-muted: rgb(150, 158, 170);
81 --text-body: rgb(215, 220, 230);
82 --text-heading: rgb(240, 243, 248);
83 --green: rgb(120, 200, 130);
84 --orange: rgb(250, 170, 90);
85 --red: rgb(255, 120, 140);
86 --amber: rgb(240, 190, 80);
87 --pane-a: rgb(130, 170, 240);
88 --pane-b: rgb(250, 170, 90);
89 --inline-code-bg: rgb(52, 72, 92);
90 --inline-code-fg: rgb(200, 220, 240);
91 --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.45);
92}
93
94* {
95 box-sizing: border-box;
96}
97
98html, body {
99 margin: 0;
100 padding: 0;
101 height: 100%;
102 background: var(--bg-page);
103 color: var(--text-body);
104 font-family: "Libertinus Serif", Georgia, "Times New Roman", serif;
105 font-size: 16px;
106 line-height: 1.5;
107 transition: background 120ms ease, color 120ms ease;
108}
109
110a {
111 color: var(--accent);
112 text-decoration:none;
113}
114
115a:hover {
116 text-decoration:underline;
117}
118
119/* Mono fallback chain ends in DejaVu Sans Mono, which is the
120 font Typst picks for raw blocks in the Hematite manual when
121 no `raw` font is set. Iosevka and Source Code Pro come first
122 because they are sharper at small sizes when present. */
123code, pre, .mono {
124 font-family: "Iosevka", "Source Code Pro", "DejaVu Sans Mono", "Menlo", monospace;
125 font-size: 0.92em;
126}
127
128code {
129 background: var(--inline-code-bg);
130 padding: 0.08em 0.35em;
131 border-radius: 3px;
132 color: var(--inline-code-fg);
133}
134
135/* ──────────────────────────────────────────────────────────────────────── */
136/* Page header */
137/* ──────────────────────────────────────────────────────────────────────── */
138
139header.steel-header {
140 background: var(--bg-card);
141 border-bottom: 1px solid var(--border-soft);
142 padding: 0.55rem 1.5rem;
143 display: flex;
144 align-items: center;
145 justify-content:space-between;
146 font-size: 0.95rem;
147}
148
149header.steel-header .brand {
150 display: flex;
151 align-items: center;
152 gap: 0.7rem;
153}
154
155header.steel-header .brand-logo {
156 display: inline-block;
157 line-height: 0;
158}
159
160header.steel-header .brand-logo svg {
161 height: 28px;
162 width: auto;
163}
164
165/* The fe2o3 wordmark inside the logo SVG is a fill of #4d4d4d.
166 * Recolour it in dark mode so it is still legible on the dark
167 * card. The geometric mark stroke stays Hematite red in both
168 * themes. */
169html.dark header.steel-header .brand-logo svg path[fill="#4d4d4d"] {
170 fill: var(--text-heading);
171}
172
173header.steel-header .brand-section {
174 color: var(--accent);
175 font-family: 'Oxanium', "Libertinus Serif", Georgia, serif;
176 font-weight: 500;
177 font-size: 1.05rem;
178 letter-spacing: 0.01em;
179}
180
181header.steel-header .session {
182 display: flex;
183 align-items: center;
184 gap: 0.9rem;
185 color: var(--text-muted);
186}
187
188header.steel-header .session-user {
189 color: var(--text-body);
190}
191
192header.steel-header .session-signout {
193 color: var(--text-muted);
194}
195
196header.steel-header .session-signout:hover {
197 color: var(--accent);
198}
199
200/* ──────────────────────────────────────────────────────────────────────── */
201/* Theme toggle */
202/* ──────────────────────────────────────────────────────────────────────── */
203
204.theme-toggle {
205 display: inline-flex;
206 align-items: center;
207 justify-content:center;
208 background: transparent;
209 border: 1px solid var(--border-soft);
210 color: var(--text-muted);
211 width: 2rem;
212 height: 2rem;
213 padding: 0;
214 cursor: pointer;
215 border-radius: 4px;
216 transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
217}
218
219.theme-toggle:hover {
220 color: var(--accent);
221 border-color: var(--accent);
222 background: var(--bg-panel);
223}
224
225.theme-toggle .icon {
226 width: 18px;
227 height: 18px;
228}
229
230.theme-toggle .theme-icon-moon {
231 display: none;
232}
233
234html.dark .theme-toggle .theme-icon-sun {
235 display: none;
236}
237
238html.dark .theme-toggle .theme-icon-moon {
239 display: inline-flex;
240}
241
242/* ──────────────────────────────────────────────────────────────────────── */
243/* Page layout: sidebar + main */
244/* ──────────────────────────────────────────────────────────────────────── */
245
246.page {
247 display: grid;
248 grid-template-columns: 12rem 1fr;
249 min-height: calc(100vh - 3rem);
250}
251
252nav.sidebar {
253 background: var(--bg-panel);
254 border-right: 1px solid var(--border-soft);
255 padding: 0;
256 font-size: 0.95rem;
257}
258
259.sidebar-brand {
260 padding: 0.9rem 1rem 0.9rem 1.2rem;
261 border-bottom: 1px solid var(--border-soft);
262 display: flex;
263 align-items: center;
264 justify-content:flex-start;
265}
266
267.sidebar-brand svg {
268 height: 24px;
269 width: auto;
270 pointer-events: none;
271}
272
273html.dark .sidebar-brand svg path[fill="#4d4d4d"] {
274 fill: var(--text-heading);
275}
276
277nav.sidebar a {
278 display: block;
279 padding: 0.45rem 1.4rem;
280 color: var(--text-body);
281 border-left: 3px solid transparent;
282}
283
284nav.sidebar a:hover {
285 background: var(--bg-inset);
286 text-decoration:none;
287}
288
289nav.sidebar a.current {
290 border-left: 3px solid var(--accent);
291 background: var(--bg-card);
292 font-weight: bold;
293 color: var(--text-heading);
294}
295
296nav.sidebar .group {
297 text-transform: uppercase;
298 font-size: 0.7rem;
299 letter-spacing: 0.08em;
300 color: var(--text-muted);
301 padding: 1rem 1.4rem 0.3rem;
302}
303
304main.content {
305 padding: 2rem 2.5rem 3rem;
306 max-width: 72rem;
307}
308
309/* Headings use the Oxanium variable font, pulled via @font-face
310 * at the top of this sheet. */
311main.content h1 {
312 font-family: 'Oxanium', "Libertinus Serif", Georgia, serif;
313 font-weight: 600;
314 font-size: 1.9rem;
315 margin: 0 0 1rem;
316 color: var(--text-heading);
317 letter-spacing: 0.01em;
318 display: flex;
319 align-items: center;
320 gap: 0.7rem;
321}
322
323main.content h2 {
324 font-family: 'Oxanium', "Libertinus Serif", Georgia, serif;
325 font-weight: 600;
326 font-size: 1.1rem;
327 color: var(--text-muted);
328 text-transform: uppercase;
329 letter-spacing: 0.08em;
330 border-bottom: 1px solid var(--border-soft);
331 padding-bottom: 0.3rem;
332 margin: 2rem 0 0.6rem;
333}
334
335/* Inline logo that sits beside a page heading (e.g. the ozone
336 * mark next to the Database h1). Sized to match the heading
337 * height rather than a hard pixel value. */
338.heading-logo {
339 height: 1.7rem;
340 width: auto;
341 flex: 0 0 auto;
342}
343
344html.dark .heading-logo path[fill="#4d4d4d"] {
345 fill: var(--text-heading);
346}
347
348main.content p {
349 margin: 0.6rem 0;
350}
351
352main.content .meta {
353 color: var(--text-muted);
354 font-size: 0.9rem;
355}
356
357main.content ul {
358 padding-left: 1.2rem;
359}
360
361main.content li {
362 margin: 0.2rem 0;
363}
364
365/* ──────────────────────────────────────────────────────────────────────── */
366/* Headline chip row */
367/* ──────────────────────────────────────────────────────────────────────── */
368
369.chip-row {
370 display: flex;
371 flex-wrap: wrap;
372 gap: 0.9rem;
373 margin: 1rem 0 1.5rem;
374}
375
376.chip {
377 background: var(--bg-card);
378 border: 1px solid var(--border-soft);
379 border-top: 3px solid var(--border-firm);
380 padding: 0.6rem 1rem 0.7rem;
381 min-width: 8rem;
382 flex: 0 0 auto;
383 box-shadow: var(--shadow-card);
384}
385
386.chip-label {
387 font-size: 0.75rem;
388 text-transform: uppercase;
389 letter-spacing: 0.06em;
390 color: var(--text-muted);
391}
392
393.chip-value {
394 font-size: 1.8rem;
395 font-weight: bold;
396 color: var(--text-heading);
397 font-variant-numeric: tabular-nums;
398 line-height: 1.1;
399 margin-top: 0.15rem;
400}
401
402.chip-sub {
403 font-size: 0.75rem;
404 color: var(--text-muted);
405 margin-top: 0.15rem;
406}
407
408.chip-total {
409 border-top-color: var(--accent);
410}
411.chip-total .chip-value {
412 color: var(--accent);
413}
414.chip-ok {
415 border-top-color: var(--green);
416}
417.chip-redirect {
418 border-top-color: var(--orange);
419}
420.chip-client {
421 border-top-color: var(--orange);
422}
423.chip-server {
424 border-top-color: var(--red);
425}
426
427/* ──────────────────────────────────────────────────────────────────────── */
428/* Overview sparkline strip */
429/* ──────────────────────────────────────────────────────────────────────── */
430
431.spark-row {
432 display: grid;
433 grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
434 gap: 0.75rem;
435 margin: 0.75rem 0 1.5rem;
436}
437
438.spark-card {
439 background: var(--bg-card);
440 border: 1px solid var(--border-soft);
441 border-top: 3px solid var(--border-firm);
442 box-shadow: var(--shadow-card);
443 padding: 0.6rem 0.85rem 0.2rem;
444 display: flex;
445 flex-direction: column;
446 min-height: 7rem;
447}
448
449.spark-header {
450 display: flex;
451 align-items: baseline;
452 justify-content:space-between;
453 margin-bottom: 0.25rem;
454}
455
456.spark-label {
457 font-size: 0.75rem;
458 text-transform: uppercase;
459 letter-spacing: 0.06em;
460 color: var(--text-muted);
461}
462
463.spark-value {
464 font-size: 1.1rem;
465 font-weight: 600;
466 color: var(--text-heading);
467 font-variant-numeric: tabular-nums;
468}
469
470.spark-plot {
471 flex: 1 1 auto;
472 min-height: 60px;
473}
474
475/* ──────────────────────────────────────────────────────────────────────── */
476/* Status pills (security view, reusable for any short label badge) */
477/* ──────────────────────────────────────────────────────────────────────── */
478
479.pill {
480 display: inline-block;
481 padding: 0.1rem 0.55rem;
482 border-radius: 999px;
483 font-size: 0.75rem;
484 text-transform: uppercase;
485 letter-spacing: 0.04em;
486 background: var(--bg-card);
487 border: 1px solid var(--border-soft);
488 color: var(--text-muted);
489 font-weight: 600;
490}
491.pill-monitor {
492 background: rgba(120, 120, 120, 0.10);
493 color: var(--text-body);
494 border-color: var(--border-soft);
495}
496.pill-throttle {
497 background: rgba(237, 108, 2, 0.12);
498 color: var(--orange);
499 border-color: rgba(237, 108, 2, 0.5);
500}
501.pill-blacklist {
502 background: rgba(198, 40, 40, 0.14);
503 color: var(--red);
504 border-color: rgba(198, 40, 40, 0.5);
505}
506.pill-whitelist {
507 background: rgba(46, 125, 50, 0.12);
508 color: var(--green);
509 border-color: rgba(46, 125, 50, 0.5);
510}
511
512/* Inline action form used in the security table row: keep the
513 * three buttons on one line with tight spacing so they read as a
514 * single row-level control group rather than a dominant block. */
515.inline-form {
516 display: inline-flex;
517 gap: 0.25rem;
518 margin: 0;
519}
520.inline-form button {
521 padding: 0.2rem 0.6rem;
522 font-size: 0.75rem;
523 background: var(--bg-card);
524 color: var(--text-body);
525 border: 1px solid var(--border-soft);
526 cursor: pointer;
527}
528.inline-form button:hover {
529 background: var(--accent);
530 color: #fff;
531 border-color: var(--accent);
532}
533
534/* ──────────────────────────────────────────────────────────────────────── */
535/* Chart panel */
536/* ──────────────────────────────────────────────────────────────────────── */
537
538.chart-panel {
539 background: var(--bg-card);
540 border: 1px solid var(--border-soft);
541 padding: 0.8rem 0.8rem 0.4rem;
542 margin: 0.5rem 0 1.5rem;
543 min-height: 260px;
544 box-shadow: var(--shadow-card);
545 /* uPlot's outer `.uplot` container is display: inline-block, so
546 * it shrinks to fit its child `.u-wrap`. We size `.u-wrap` by
547 * passing the panel's clientWidth to `new uPlot(...)` at render
548 * time, not via CSS, otherwise forcing width: 100% on .u-wrap
549 * creates a sizing loop and collapses everything to a narrow
550 * strip. Nothing to override here. */
551}
552
553/* Dark theme overrides for uPlot itself: the library's default
554 * stylesheet uses hard-coded black axis labels and white
555 * backgrounds. Retarget them against our palette. */
556html.dark .uplot {
557 color: var(--text-body);
558}
559html.dark .u-legend {
560 color: var(--text-muted);
561}
562html.dark .u-axis {
563 color: var(--text-muted);
564}
565
566.chart-panel .notice {
567 margin: 0;
568}
569
570.chart-panel .u-legend {
571 font-size: 0.8rem;
572 color: var(--text-muted);
573}
574
575/* ──────────────────────────────────────────────────────────────────────── */
576/* Forms */
577/* ──────────────────────────────────────────────────────────────────────── */
578
579form.steel-form {
580 background: var(--bg-card);
581 border: 1px solid var(--border-soft);
582 border-left: 3px solid var(--accent);
583 padding: 1.2rem 1.5rem;
584 margin: 1rem 0;
585 box-shadow: var(--shadow-card);
586}
587
588form.steel-form label {
589 display: block;
590 font-size: 0.85rem;
591 margin-top: 0.6rem;
592 color: var(--text-muted);
593 text-transform: uppercase;
594 letter-spacing: 0.04em;
595}
596
597form.steel-form input[type="text"],
598form.steel-form input[type="password"],
599form.steel-form input[type="number"] {
600 width: 100%;
601 padding: 0.5rem 0.75rem;
602 font-size: 1rem;
603 border: 1px solid var(--border-firm);
604 border-radius: var(--radius-input);
605 background: var(--bg-page);
606 font-family: inherit;
607 color: var(--text-body);
608}
609
610form.steel-form input:focus {
611 outline: 0;
612 border-color: var(--accent);
613 background: var(--bg-card);
614}
615
616form.steel-form input[type="number"] {
617 width: 8rem;
618}
619
620form.steel-form .row {
621 display: flex;
622 gap: 1rem;
623 align-items: flex-end;
624}
625
626form.steel-form .row > * {
627 flex: 1 1 auto;
628}
629
630form.steel-form button,
631button.primary {
632 background: var(--accent);
633 color: white;
634 border: 0;
635 border-radius: var(--radius-input);
636 padding: 0.55rem 1.2rem;
637 font-size: 0.95rem;
638 font-family: inherit;
639 cursor: pointer;
640 margin-top: 0.8rem;
641 letter-spacing: 0.02em;
642}
643
644form.steel-form button:hover,
645button.primary:hover {
646 background: var(--accent-dark);
647}
648
649/* ──────────────────────────────────────────────────────────────────────── */
650/* Tables */
651/* ──────────────────────────────────────────────────────────────────────── */
652
653table.steel-table {
654 border-collapse:collapse;
655 width: 100%;
656 margin: 0.5rem 0 1rem;
657 background: var(--bg-card);
658 border: 1px solid var(--border-soft);
659 font-size: 0.92rem;
660 box-shadow: var(--shadow-card);
661}
662
663table.steel-table th,
664table.steel-table td {
665 padding: 0.45rem 0.7rem;
666 text-align: left;
667 border-bottom: 1px solid var(--border-soft);
668}
669
670table.steel-table th {
671 background: var(--bg-panel);
672 font-weight: bold;
673 color: var(--text-heading);
674 text-transform: uppercase;
675 font-size: 0.72rem;
676 letter-spacing: 0.06em;
677}
678
679table.steel-table tbody tr:last-child td {
680 border-bottom: 0;
681}
682
683table.steel-table tbody tr:hover {
684 background: var(--bg-table-row-hover);
685}
686
687table.steel-table td.path code {
688 background: transparent;
689 color: var(--text-body);
690 padding: 0;
691}
692
693table.steel-table td.num,
694table.steel-table th.num {
695 text-align: right;
696 font-variant-numeric: tabular-nums;
697}
698
699table.steel-table td.status,
700table.steel-table th.status {
701 text-align: right;
702 font-variant-numeric: tabular-nums;
703 font-weight: bold;
704}
705
706table.steel-table td.status.chip-ok { color: var(--green); }
707table.steel-table td.status.chip-redirect { color: var(--orange); }
708table.steel-table td.status.chip-client { color: var(--orange); }
709table.steel-table td.status.chip-server { color: var(--red); }
710
711/* ──────────────────────────────────────────────────────────────────────── */
712/* Notices */
713/* ──────────────────────────────────────────────────────────────────────── */
714
715.notice {
716 padding: 0.7rem 1rem;
717 margin: 1rem 0;
718 border-left: 3px solid var(--border-firm);
719 background: var(--bg-panel);
720}
721
722.notice.error {
723 border-left-color: var(--accent);
724 background: var(--bg-inset);
725 color: var(--accent);
726}
727
728.notice.empty {
729 color: var(--text-muted);
730 font-style: italic;
731}
732
733/* A state the operator must act on, but which is not an error: the
734 sealed banner on the login form is the case this exists for. */
735.notice.warn {
736 border-left-color: var(--warn, #d08a2c);
737 background: var(--bg-inset);
738}
739
740/* ──────────────────────────────────────────────────────────────────────── */
741/* Ozone browser split view */
742/* ──────────────────────────────────────────────────────────────────────── */
743
744.ozone-split {
745 display: grid;
746 grid-template-columns: minmax(0, 1fr) 22rem;
747 gap: 1rem;
748 margin: 0.5rem 0 1.5rem;
749}
750@media (max-width: 1100px) { .ozone-split { grid-template-columns: 1fr; } }
751
752.ozone-list { min-width: 0; }
753.ozone-list table.steel-table a { color: inherit; }
754.ozone-list table.steel-table tr.selected td {
755 background: var(--bg-inset);
756 font-weight: bold;
757}
758
759.ozone-detail {
760 background: var(--bg-card);
761 border: 1px solid var(--border-soft);
762 border-top: 3px solid var(--accent);
763 box-shadow: var(--shadow-card);
764 padding: 0.8rem 1rem 1rem;
765 font-size: 0.88rem;
766 min-width: 0;
767}
768
769.ozone-detail h3 {
770 margin: 0.1rem 0 0.6rem;
771 font-size: 0.72rem;
772 text-transform: uppercase;
773 letter-spacing: 0.08em;
774 color: var(--text-muted);
775}
776
777.ozone-detail .field { margin-bottom: 0.6rem; }
778.ozone-detail .field .lbl {
779 display: block;
780 font-size: 0.7rem;
781 text-transform: uppercase;
782 letter-spacing: 0.06em;
783 color: var(--text-muted);
784 margin-bottom: 0.1rem;
785}
786.ozone-detail .field .val {
787 font-family: "Iosevka", "Source Code Pro", "DejaVu Sans Mono", "Menlo", monospace;
788 color: var(--text-body);
789 word-break: break-all;
790}
791.ozone-detail pre.val {
792 background: var(--bg-inset);
793 padding: 0.5rem 0.7rem;
794 border-radius: 4px;
795 margin: 0;
796 font-size: 0.82rem;
797 white-space: pre-wrap;
798 max-height: 18rem;
799 overflow: auto;
800}
801
802/* ──────────────────────────────────────────────────────────────────────── */
803/* Fleet view */
804/* ──────────────────────────────────────────────────────────────────────── */
805
806/* One row per host, two panes per row. The pane is told by its family colour
807 * (blue for resources, orange for traffic), the judgement by the cell's top
808 * border (green, amber, red, or the plain grey of no threshold), so the two
809 * never read as one scale. */
810
811.fleet-rows {
812 display: flex;
813 flex-direction: column;
814 gap: 1.1rem;
815 margin: 1rem 0 0.5rem;
816}
817
818.fleet-rows.is-behind {
819 opacity: 0.6;
820}
821
822.fleet-row {
823 background: var(--bg-card);
824 border: 1px solid var(--border-soft);
825 box-shadow: var(--shadow-card);
826 padding: 0.7rem 0.9rem 0.8rem;
827}
828
829.fleet-row-head {
830 display: flex;
831 flex-wrap: wrap;
832 align-items: baseline;
833 gap: 0.3rem 0.8rem;
834 margin-bottom: 0.6rem;
835}
836
837.fleet-host {
838 font-family: 'Oxanium', "Libertinus Serif", Georgia, serif;
839 font-weight: 600;
840 font-size: 1.25rem;
841 color: var(--text-heading);
842}
843
844.fleet-state {
845 font-size: 0.78rem;
846 font-weight: bold;
847 text-transform: uppercase;
848 letter-spacing: 0.06em;
849 color: var(--text-muted);
850}
851
852/* Red is the watcher's own call, the one that alarms. A sealed peer still answers, so
853 nothing alarms on the word; its Seal cell is coloured where a threshold judges it. */
854.fleet-state-up { color: var(--green); }
855.fleet-state-distressed { color: var(--amber); }
856.fleet-state-down { color: var(--red); }
857
858.fleet-row-meta {
859 font-size: 0.85rem;
860 color: var(--text-muted);
861}
862
863.fleet-panes {
864 display: grid;
865 grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
866 gap: 0.8rem;
867}
868
869.fleet-pane {
870 border-left: 3px solid var(--border-firm);
871 padding-left: 0.6rem;
872 min-width: 0;
873}
874
875.fleet-pane-a { border-left-color: var(--pane-a); }
876.fleet-pane-b { border-left-color: var(--pane-b); }
877.fleet-pane-c { border-left-color: var(--border-firm); }
878
879.fleet-pane-title {
880 font-size: 0.72rem;
881 text-transform: uppercase;
882 letter-spacing: 0.08em;
883 margin-bottom: 0.35rem;
884}
885
886.fleet-pane-a .fleet-pane-title { color: var(--pane-a); }
887.fleet-pane-b .fleet-pane-title { color: var(--pane-b); }
888.fleet-pane-c .fleet-pane-title { color: var(--text-muted); }
889
890.fleet-cells {
891 display: grid;
892 grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
893 gap: 0.5rem;
894}
895
896.fleet-cell {
897 background: var(--bg-inset);
898 border-top: 3px solid var(--border-firm);
899 padding: 0.35rem 0.5rem 0.3rem;
900 min-width: 0;
901}
902
903.fleet-cell.tone-green { border-top-color: var(--green); }
904.fleet-cell.tone-amber { border-top-color: var(--amber); }
905.fleet-cell.tone-red { border-top-color: var(--red); }
906
907.fleet-cell-label {
908 font-size: 0.68rem;
909 text-transform: uppercase;
910 letter-spacing: 0.06em;
911 color: var(--text-muted);
912 overflow: hidden;
913 text-overflow: ellipsis;
914 white-space: nowrap;
915}
916
917.fleet-cell-value {
918 font-size: 1.05rem;
919 font-weight: 600;
920 color: var(--text-heading);
921 font-variant-numeric: tabular-nums;
922 overflow-wrap: anywhere;
923}
924
925.fleet-cell.tone-red .fleet-cell-value { color: var(--red); }
926
927.fleet-cell-sub {
928 font-size: 0.72rem;
929 color: var(--text-muted);
930}
931
932.fleet-spark {
933 display: block;
934 width: 100%;
935 height: 24px;
936 margin-top: 0.2rem;
937}
938
939.fleet-spark path {
940 fill: none;
941 stroke-width: 1.4;
942 vector-effect: non-scaling-stroke;
943}
944
945.fleet-pane-a .fleet-spark path { stroke: var(--pane-a); }
946.fleet-pane-b .fleet-spark path { stroke: var(--pane-b); }
947.fleet-pane-c .fleet-spark path { stroke: var(--text-muted); }
948
949/* A row with no current reading keeps its last numbers, greyed; no colour
950 * survives, because green is a claim that fresh data was read and judged. */
951.fleet-row.is-dim .fleet-cell,
952.fleet-row.is-dim .fleet-pane-title {
953 opacity: 0.5;
954}
955
956.fleet-services {
957 display: flex;
958 flex-wrap: wrap;
959 align-items: baseline;
960 gap: 0.3rem 0.9rem;
961 margin-top: 0.6rem;
962 padding-top: 0.5rem;
963 border-top: 1px solid var(--border-soft);
964 font-size: 0.85rem;
965}
966
967.fleet-services-label {
968 font-size: 0.68rem;
969 text-transform: uppercase;
970 letter-spacing: 0.06em;
971 color: var(--text-muted);
972}
973
974.fleet-service {
975 font-weight: 600;
976}
977
978.fleet-stamp {
979 margin-top: 0.8rem;
980}
981
982/* ──────────────────────────────────────────────────────────────────────── */
983/* Login standalone layout (no sidebar) */
984/* ──────────────────────────────────────────────────────────────────────── */
985
986body.login {
987 display: flex;
988 align-items: center;
989 justify-content:center;
990 min-height: 100vh;
991 background: var(--bg-panel);
992}
993
994body.login .login-card {
995 background: var(--bg-card);
996 padding: 2rem 2.5rem;
997 border-top: 3px solid var(--accent);
998 box-shadow: var(--shadow-card);
999 width: 24rem;
1000 max-width: 90vw;
1001}
1002
1003body.login .login-logo {
1004 display: flex;
1005 justify-content:center;
1006 margin-bottom: 0.4rem;
1007}
1008
1009body.login .login-logo svg {
1010 height: 38px;
1011 width: auto;
1012}
1013
1014html.dark body.login .login-logo svg path[fill="#4d4d4d"] {
1015 fill: var(--text-heading);
1016}
1017
1018body.login .login-sub {
1019 text-align: center;
1020 color: var(--text-muted);
1021 font-style: italic;
1022 font-size: 0.9rem;
1023 margin-bottom: 1.2rem;
1024 letter-spacing: 0.04em;
1025}