librelad 49cf7e8bec ux(system): move Reclaim button top-right, make it actually free space
Three fixes from testing the storage page:

- Placement: the "Reclaim space" button moves into the page header,
  top-right (matching the metric page), instead of sitting in the body.

- It now actually reclaims: build cache needs -a to drop (docker reports
  0 B "reclaimable" without it, but it's pure cache — safe to clear), so
  the CLI uses `docker builder prune -af`. Previously the safe scope
  freed ~nothing on a box whose reclaimable was mostly cache.

- Honest "Reclaimable" number: /api/system/storage was counting the
  whole build cache AND unused tagged images, overstating what the safe
  prune frees (e.g. 340 MB shown, ~96 MB per docker, button cleared 0).
  Reclaimable now = dangling images + build cache only; stopped
  containers and volumes are never counted (the safe prune never touches
  them). Headline now matches the button's effect.

Also simplify the CLI output (drop the jargony scope notice and the
reclaimed-total greps) and re-enable the now-persistent header button
after the post-reclaim refreshes.

Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-28 19:06:02 +01:00

1218 lines
34 KiB
CSS

/* Admin area — Overview board + shared admin-page chrome. Visually aligned
with the backup dashboard (tile/card style) and the config page header. */
.admin-page {
padding: 4px 2px 40px;
}
.admin-breadcrumb {
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: rgba(var(--text-rgb), 0.45);
margin-bottom: 2px;
}
/* Overview cards */
.admin-card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px;
margin-top: 8px;
}
.admin-card {
display: flex;
flex-direction: column;
padding: 16px;
border: 1px solid rgba(var(--text-rgb), 0.10);
border-radius: 12px;
background: var(--card-bg);
}
.admin-card-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.admin-card-title {
font-size: 0.95rem;
font-weight: 700;
}
.admin-status-dot {
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
}
.admin-status-dot.ok { background: #36d399; }
.admin-status-dot.warn { background: #fbbd23; }
.admin-status-dot.none { background: rgba(var(--text-rgb), 0.25); }
.admin-card-body {
display: flex;
flex-direction: column;
gap: 6px;
flex: 1;
}
.admin-card-line {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
font-size: 0.85rem;
color: rgba(var(--text-rgb), 0.6);
}
.admin-card-line strong {
color: var(--text-primary);
font-weight: 600;
text-align: right;
word-break: break-word;
}
.admin-card-actions {
margin-top: 14px;
padding-top: 12px;
border-top: 1px solid rgba(var(--text-rgb), 0.08);
}
.admin-card-ok {
font-size: 0.82rem;
color: rgba(var(--text-rgb), 0.55);
}
/* ============================================================
Admin → System (in-depth statistics page)
============================================================ */
.sys-section-head {
display: flex;
align-items: center;
justify-content: space-between;
margin: 26px 0 12px;
}
.sys-section-head h2 {
font-size: 1.05rem;
font-weight: 700;
margin: 0;
}
.sys-chart-meta {
font-size: 0.78rem;
color: rgba(var(--text-rgb), 0.5);
}
/* Range selector (1h / 6h / 24h) */
.sys-range { display: inline-flex; gap: 4px; }
.sys-range-btn {
padding: 4px 12px;
font-size: 0.78rem;
font-weight: 600;
color: rgba(var(--text-rgb), 0.7);
background: rgba(var(--text-rgb), 0.06);
border: 1px solid rgba(var(--text-rgb), 0.12);
border-radius: 999px;
cursor: pointer;
transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.sys-range-btn:hover { background: rgba(var(--accent-rgb), 0.15); }
.sys-range-btn.active {
color: var(--text-primary);
background: rgba(var(--accent-rgb), 0.22);
border-color: rgba(var(--accent-rgb), 0.55);
}
/* Gauges row */
.sys-gauges {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 16px;
margin-top: 10px;
}
.lp-gauge {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
padding: 14px 12px 10px;
background: var(--card-bg);
border: 1px solid rgba(var(--text-rgb), 0.10);
border-radius: 12px;
}
.lp-gauge-svg { width: 116px; height: 116px; display: block; }
.lp-gauge-center {
position: absolute;
top: 72px;
left: 0; right: 0;
transform: translateY(-50%);
text-align: center;
pointer-events: none;
}
.lp-gauge-value {
font-size: 1.5rem;
font-weight: 700;
color: var(--text-primary);
line-height: 1;
}
.lp-gauge-value span { font-size: 0.8rem; font-weight: 600; opacity: 0.6; margin-left: 1px; }
.lp-gauge-sub { font-size: 0.68rem; color: rgba(var(--text-rgb), 0.5); margin-top: 3px; }
.lp-gauge-label {
margin-top: 6px;
font-size: 0.82rem;
font-weight: 600;
color: rgba(var(--text-rgb), 0.75);
}
/* Trend chart cards */
.sys-charts {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 16px;
}
.sys-chart-card {
background: var(--card-bg);
border: 1px solid rgba(var(--text-rgb), 0.10);
border-radius: 12px;
padding: 14px 16px 10px;
transition: border-color .2s ease, box-shadow .2s ease;
}
.sys-chart-card:hover {
border-color: rgba(var(--accent-rgb), 0.28);
box-shadow: 0 6px 22px rgba(var(--accent-rgb), 0.10);
}
.sys-chart-card:hover .sys-expand { color: var(--accent); background: rgba(var(--accent-rgb), 0.18); }
.sys-chart-head {
display: flex;
align-items: baseline;
justify-content: space-between;
font-size: 0.88rem;
font-weight: 700;
margin-bottom: 8px;
}
.sys-chart-head .sys-chart-meta { font-weight: 500; }
.sys-chart-body { position: relative; }
.lp-chart { width: 100%; height: 92px; display: block; overflow: visible; }
.lp-chart-last {
position: absolute;
top: 0; right: 0;
font-size: 0.85rem;
font-weight: 700;
}
.lp-chart-empty {
height: 92px;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.82rem;
color: rgba(var(--text-rgb), 0.4);
}
.sys-net-legend {
display: flex;
gap: 16px;
margin-top: 6px;
font-size: 0.8rem;
color: rgba(var(--text-rgb), 0.7);
}
.sys-net-legend .dot {
display: inline-block;
width: 8px; height: 8px;
border-radius: 50%;
margin-right: 5px;
}
.sys-net-legend .dot.ok { background: var(--status-success); }
.sys-net-legend .dot.accent { background: var(--accent); }
/* Info / docker stat strips */
.sys-strip {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 12px;
}
.sys-stat {
display: flex;
flex-direction: column;
gap: 3px;
padding: 12px 14px;
background: var(--card-bg);
border: 1px solid rgba(var(--text-rgb), 0.10);
border-radius: 10px;
}
.sys-stat-label {
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.04em;
color: rgba(var(--text-rgb), 0.45);
}
.sys-stat-value {
font-size: 0.92rem;
font-weight: 600;
color: var(--text-primary);
word-break: break-word;
}
/* Per-app table */
.sys-apps-wrap {
background: var(--card-bg);
border: 1px solid rgba(var(--text-rgb), 0.10);
border-radius: 12px;
overflow: hidden;
}
table.sys-apps {
width: 100%;
border-collapse: collapse;
font-size: 0.85rem;
}
table.sys-apps th {
text-align: left;
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.04em;
color: rgba(var(--text-rgb), 0.45);
padding: 12px 14px;
border-bottom: 1px solid rgba(var(--text-rgb), 0.10);
}
table.sys-apps td {
padding: 11px 14px;
border-bottom: 1px solid rgba(var(--text-rgb), 0.06);
vertical-align: middle;
color: var(--text-primary);
}
table.sys-apps tr:last-child td { border-bottom: none; }
table.sys-apps tr:hover td { background: rgba(var(--text-rgb), 0.03); }
.sys-app-name { font-weight: 600; display: flex; align-items: center; gap: 8px; }
.sys-app-sub { font-size: 0.72rem; font-weight: 500; color: rgba(var(--text-rgb), 0.45); }
.sys-cell-val { font-size: 0.78rem; color: rgba(var(--text-rgb), 0.65); margin-left: 2px; }
.sys-net-cell { font-size: 0.78rem; color: rgba(var(--text-rgb), 0.7); white-space: nowrap; }
.sys-spark-cell { width: 110px; }
.sys-apps-empty {
text-align: center;
color: rgba(var(--text-rgb), 0.45);
padding: 24px 14px !important;
}
/* Bars + sparklines (shared by LPCharts) */
.lp-bar {
display: inline-block;
width: 90px;
max-width: 40%;
height: 6px;
border-radius: 3px;
background: rgba(var(--text-rgb), 0.12);
overflow: hidden;
vertical-align: middle;
}
.lp-bar-fill { display: block; height: 100%; border-radius: 3px; transition: width .4s ease; }
.lp-spark { width: 100px; height: 24px; display: block; }
/* Gauge wrapper — each gauge tile is a button that opens the detail overlay.
The visual is identical to before (.lp-gauge); the wrapper adds the press
affordance + the corner expand icon. */
.sys-gauge-wrap {
all: unset;
position: relative;
display: block;
cursor: pointer;
border-radius: 12px;
transition: transform .15s ease, box-shadow .15s ease;
}
.sys-gauge-wrap:focus-visible { outline: 2px solid rgba(var(--accent-rgb), 0.6); outline-offset: 2px; }
.sys-gauge-wrap:hover { transform: translateY(-1px); }
.sys-gauge-wrap:hover .lp-gauge { box-shadow: 0 6px 24px rgba(var(--accent-rgb), 0.18); border-color: rgba(var(--accent-rgb), 0.35); }
.sys-gauge-wrap .lp-gauge { transition: box-shadow .2s ease, border-color .2s ease; height: 100%; }
.sys-gauge-expand {
position: absolute;
top: 8px; right: 10px;
width: 24px; height: 24px;
display: flex;
align-items: center; justify-content: center;
color: rgba(var(--text-rgb), 0.45);
border-radius: 6px;
background: rgba(var(--text-rgb), 0.04);
opacity: 0;
transition: opacity .15s ease, color .15s ease, background .15s ease;
pointer-events: none;
}
.sys-gauge-wrap:hover .sys-gauge-expand,
.sys-gauge-wrap:focus-visible .sys-gauge-expand { opacity: 1; }
.sys-gauge-wrap:hover .sys-gauge-expand { color: var(--accent); background: rgba(var(--accent-rgb), 0.12); }
/* Chart card head — right cluster keeps the meta + expand button aligned. */
.sys-chart-head { gap: 12px; }
.sys-chart-head-right { display: inline-flex; align-items: center; gap: 10px; }
.sys-expand {
all: unset;
display: inline-flex;
align-items: center; justify-content: center;
width: 24px; height: 24px;
color: rgba(var(--text-rgb), 0.5);
background: rgba(var(--text-rgb), 0.05);
border-radius: 6px;
cursor: pointer;
transition: color .15s ease, background .15s ease, transform .15s ease;
}
.sys-expand:hover { color: var(--accent); background: rgba(var(--accent-rgb), 0.14); transform: scale(1.06); }
.sys-expand:focus-visible { outline: 2px solid rgba(var(--accent-rgb), 0.6); outline-offset: 2px; }
/* =========================================================================
Admin → System — sub-pages (metric / app / storage)
========================================================================= */
/* Shared breadcrumb-link style for sub-page back navigation. */
.admin-breadcrumb a {
color: inherit;
text-decoration: none;
transition: color .15s ease;
}
.admin-breadcrumb a:hover { color: var(--accent); }
/* ---- Metric deep-dive page (.sys-metric-page) ---- */
.sys-metric-page {
--metric-rgb: var(--accent-rgb);
}
.sys-metric-page .page-header {
align-items: flex-start;
justify-content: space-between;
gap: 16px;
display: flex;
}
.sys-metric-name {
background: linear-gradient(120deg, var(--text-primary) 0%, rgba(var(--metric-rgb), 1) 110%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.sys-metric-sub {
color: rgba(var(--text-rgb), 0.6);
}
.sys-metric-actions {
display: flex;
align-items: center;
gap: 14px;
margin-top: 6px;
}
.sys-detail-range { display: inline-flex; gap: 4px; padding: 4px; background: rgba(var(--text-rgb), 0.06); border-radius: 999px; }
.sys-detail-range-btn {
all: unset;
padding: 5px 14px;
font-size: 0.8rem;
font-weight: 600;
color: rgba(var(--text-rgb), 0.65);
border-radius: 999px;
cursor: pointer;
transition: background .15s ease, color .15s ease;
}
.sys-detail-range-btn:hover { color: var(--text-primary); }
.sys-detail-range-btn.active {
color: var(--text-on-accent, #0a1426);
background: rgba(var(--metric-rgb), 0.95);
box-shadow: 0 2px 12px rgba(var(--metric-rgb), 0.35);
}
.sys-detail-stats {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 14px;
}
.sys-detail-stat {
padding: 14px 18px 12px;
background: rgba(var(--text-rgb), 0.04);
border: 1px solid rgba(var(--text-rgb), 0.08);
border-radius: 14px;
display: flex;
flex-direction: column;
gap: 4px;
transition: border-color .2s ease;
}
.sys-detail-stat[data-stat="now"] {
background: linear-gradient(135deg, rgba(var(--metric-rgb), 0.16) 0%, rgba(var(--metric-rgb), 0.05) 100%);
border-color: rgba(var(--metric-rgb), 0.35);
}
.sys-detail-stat-k {
font-size: 0.66rem;
text-transform: uppercase;
letter-spacing: 0.14em;
color: rgba(var(--text-rgb), 0.5);
font-weight: 700;
}
.sys-detail-stat-v {
font-size: 1.55rem;
font-weight: 700;
color: var(--text-primary);
line-height: 1.05;
font-variant-numeric: tabular-nums;
}
.sys-detail-stat[data-stat="now"] .sys-detail-stat-v {
background: linear-gradient(120deg, var(--text-primary) 0%, rgba(var(--metric-rgb), 1) 100%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.sys-detail-stat-t {
font-size: 0.72rem;
color: rgba(var(--text-rgb), 0.45);
font-variant-numeric: tabular-nums;
}
.sys-detail-canvas,
.sys-metric-canvas {
position: relative;
min-height: 420px;
background:
radial-gradient(120% 80% at 50% 100%, rgba(var(--metric-rgb), 0.08) 0%, transparent 60%),
rgba(0, 0, 0, 0.22);
border: 1px solid rgba(var(--text-rgb), 0.06);
border-radius: 14px;
overflow: hidden;
margin-top: 16px;
}
.sys-detail-svg { width: 100%; height: 100%; display: block; }
.sys-detail-loading,
.sys-detail-empty {
position: absolute; inset: 0;
display: flex; align-items: center; justify-content: center;
color: rgba(var(--text-rgb), 0.45);
font-size: 0.92rem;
pointer-events: none;
}
.sys-detail-empty { color: rgba(var(--text-rgb), 0.55); }
.sys-detail-grid {
stroke: rgba(var(--text-rgb), 0.07);
stroke-width: 1;
stroke-dasharray: 2 4;
vector-effect: non-scaling-stroke;
}
.sys-detail-axis-line {
stroke: rgba(var(--text-rgb), 0.18);
stroke-width: 1;
vector-effect: non-scaling-stroke;
}
.sys-detail-axis {
fill: rgba(var(--text-rgb), 0.55);
font-size: 11px;
font-variant-numeric: tabular-nums;
}
.sys-detail-axis-y { text-anchor: end; }
.sys-detail-axis-x { text-anchor: middle; }
.sys-detail-peak { fill: var(--status-warning); filter: drop-shadow(0 0 6px rgba(var(--status-warning-rgb, 255 193 7), 0.6)); }
.sys-detail-min { fill: rgba(var(--text-rgb), 0.55); }
.sys-detail-now {
fill: rgb(var(--metric-rgb));
filter: drop-shadow(0 0 8px rgba(var(--metric-rgb), 0.7));
animation: sys-detail-pulse 2.2s ease-in-out infinite;
}
@keyframes sys-detail-pulse {
0%, 100% { r: 5; }
50% { r: 7; }
}
.sys-detail-cross {
stroke: rgba(var(--metric-rgb), 0.55);
stroke-width: 1;
stroke-dasharray: 3 3;
vector-effect: non-scaling-stroke;
pointer-events: none;
}
.sys-detail-cross-dot {
fill: rgb(var(--metric-rgb));
stroke: var(--surface-bg-solid, #0f1729);
stroke-width: 2;
pointer-events: none;
}
.sys-detail-tooltip {
position: absolute;
pointer-events: none;
padding: 8px 12px;
background: rgba(15, 23, 41, 0.92);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(var(--metric-rgb), 0.4);
border-radius: 10px;
color: var(--text-primary);
font-size: 0.85rem;
transition: transform 60ms linear;
will-change: transform;
box-shadow: 0 8px 28px rgba(0,0,0,0.4);
min-width: 110px;
}
.sys-detail-tip-v { font-weight: 700; font-size: 0.95rem; font-variant-numeric: tabular-nums; color: rgb(var(--metric-rgb)); }
.sys-detail-tip-t { font-size: 0.72rem; color: rgba(var(--text-rgb), 0.6); margin-top: 2px; font-variant-numeric: tabular-nums; }
.sys-detail-foot {
display: flex;
justify-content: space-between;
align-items: center;
color: rgba(var(--text-rgb), 0.45);
font-size: 0.75rem;
font-variant-numeric: tabular-nums;
margin-top: 10px;
}
.sys-detail-foot-hint kbd,
.sys-detail-foot-hint { letter-spacing: 0.02em; }
/* Index per-app table rows are now clickable to open the app deep-dive. */
.sys-app-row { cursor: pointer; }
.sys-app-row:hover td { background: rgba(var(--accent-rgb), 0.06) !important; }
.sys-app-row:focus { outline: none; }
.sys-app-row:focus-visible td { background: rgba(var(--accent-rgb), 0.10) !important; }
.sys-app-arrow {
text-align: right;
color: rgba(var(--text-rgb), 0.35);
font-size: 1.1rem;
width: 1em;
}
.sys-app-row:hover .sys-app-arrow { color: var(--accent); }
/* Clickable stat tile in the Docker strip — same chrome as .sys-stat but
feels like a button. */
.sys-stat-link {
all: unset;
display: flex;
flex-direction: column;
gap: 3px;
padding: 12px 14px;
background: var(--card-bg);
border: 1px solid rgba(var(--accent-rgb), 0.25);
border-radius: 10px;
cursor: pointer;
transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
}
.sys-stat-link:hover {
border-color: rgba(var(--accent-rgb), 0.55);
transform: translateY(-1px);
box-shadow: 0 4px 16px rgba(var(--accent-rgb), 0.16);
}
.sys-stat-link .sys-stat-value { color: var(--accent); }
@media (max-width: 900px) {
.sys-metric-page .page-header { flex-direction: column; }
.sys-detail-stats { grid-template-columns: 1fr 1fr; }
.sys-detail-stat-v { font-size: 1.25rem; }
}
/* =========================================================================
Admin → System → App — per-container deep-dive (.sys-app-page)
========================================================================= */
.sys-app-empty {
padding: 36px 20px;
text-align: center;
color: rgba(var(--text-rgb), 0.55);
background: var(--card-bg);
border: 1px dashed rgba(var(--text-rgb), 0.18);
border-radius: 14px;
}
.sys-app-grid {
display: grid;
grid-template-columns: 1fr;
gap: 18px;
margin-top: 18px;
}
.sys-app-card {
background: var(--card-bg);
border: 1px solid rgba(var(--text-rgb), 0.10);
border-radius: 14px;
padding: 18px 20px 14px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
}
.sys-app-card-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-bottom: 10px;
}
.sys-app-card-status {
display: flex;
align-items: center;
gap: 8px;
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.06em;
color: rgba(var(--text-rgb), 0.55);
font-weight: 700;
}
.sys-app-card-svc {
padding: 1px 8px;
background: rgba(var(--accent-rgb), 0.18);
color: var(--accent);
border-radius: 999px;
font-size: 0.7rem;
}
.sys-app-card-name {
font-size: 1.25rem;
font-weight: 700;
margin: 4px 0 2px;
color: var(--text-primary);
}
.sys-app-card-meta {
font-size: 0.8rem;
color: rgba(var(--text-rgb), 0.55);
}
.sys-app-card-sep { margin: 0 6px; opacity: 0.5; }
.sys-app-card-status-line {
font-size: 0.78rem;
color: rgba(var(--text-rgb), 0.45);
text-align: right;
flex-shrink: 0;
font-variant-numeric: tabular-nums;
}
.sys-app-card-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
gap: 10px;
padding: 12px;
background: rgba(0, 0, 0, 0.22);
border: 1px solid rgba(var(--text-rgb), 0.06);
border-radius: 10px;
margin-bottom: 12px;
}
.sys-app-stat {
display: flex;
flex-direction: column;
gap: 2px;
}
.sys-app-stat-k {
font-size: 0.66rem;
text-transform: uppercase;
letter-spacing: 0.10em;
color: rgba(var(--text-rgb), 0.5);
font-weight: 700;
}
.sys-app-stat-v {
font-size: 1.05rem;
font-weight: 700;
color: var(--text-primary);
font-variant-numeric: tabular-nums;
transition: color .2s ease;
}
.sys-app-stat-v.warn { color: var(--status-warning); }
.sys-app-stat-v.danger { color: var(--status-danger); }
.sys-app-card-body { display: flex; flex-direction: column; gap: 14px; }
.sys-app-card-loading,
.sys-app-card-err {
padding: 14px;
color: rgba(var(--text-rgb), 0.5);
text-align: center;
font-size: 0.88rem;
}
.sys-app-card-err { color: var(--status-danger); }
.sys-app-card-limits {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 10px;
}
.sys-app-limit {
padding: 10px 12px;
background: rgba(var(--text-rgb), 0.03);
border: 1px solid rgba(var(--text-rgb), 0.06);
border-radius: 8px;
}
.sys-app-limit-k {
display: block;
font-size: 0.66rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: rgba(var(--text-rgb), 0.45);
font-weight: 700;
margin-bottom: 2px;
}
.sys-app-limit-v {
font-size: 0.9rem;
font-weight: 600;
color: var(--text-primary);
word-break: break-word;
}
.sys-app-section h3 {
font-size: 0.85rem;
font-weight: 700;
margin: 0 0 8px;
color: rgba(var(--text-rgb), 0.7);
text-transform: uppercase;
letter-spacing: 0.06em;
}
.sys-app-table {
width: 100%;
border-collapse: collapse;
font-size: 0.82rem;
background: rgba(var(--text-rgb), 0.03);
border-radius: 8px;
overflow: hidden;
}
.sys-app-table th {
text-align: left;
font-size: 0.68rem;
text-transform: uppercase;
letter-spacing: 0.06em;
color: rgba(var(--text-rgb), 0.45);
padding: 8px 10px;
border-bottom: 1px solid rgba(var(--text-rgb), 0.08);
}
.sys-app-table td {
padding: 8px 10px;
border-bottom: 1px solid rgba(var(--text-rgb), 0.04);
color: rgba(var(--text-rgb), 0.85);
font-variant-numeric: tabular-nums;
}
.sys-app-table tr:last-child td { border-bottom: none; }
.sys-app-mount-type {
display: inline-block;
padding: 1px 7px;
border-radius: 4px;
font-size: 0.66rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.sys-app-mount-volume { background: rgba(var(--status-success-rgb), 0.2); color: var(--status-success); }
.sys-app-mount-bind { background: rgba(var(--accent-rgb), 0.18); color: var(--accent); }
.sys-app-mount-tmpfs { background: rgba(var(--status-warning-rgb), 0.18); color: var(--status-warning); }
.sys-app-mount-path {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.78rem;
word-break: break-all;
}
.sys-app-ports {
list-style: none;
margin: 0; padding: 0;
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.sys-app-ports li {
padding: 4px 10px;
background: rgba(var(--accent-rgb), 0.12);
border: 1px solid rgba(var(--accent-rgb), 0.30);
border-radius: 999px;
font-size: 0.78rem;
font-variant-numeric: tabular-nums;
color: var(--text-primary);
}
.sys-app-health {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 8px;
}
.sys-app-health-pill {
padding: 2px 10px;
border-radius: 999px;
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.sys-app-health-healthy { background: rgba(var(--status-success-rgb), 0.18); color: var(--status-success); }
.sys-app-health-starting { background: rgba(var(--status-warning-rgb), 0.18); color: var(--status-warning); }
.sys-app-health-unhealthy { background: rgba(var(--status-danger-rgb), 0.18); color: var(--status-danger); }
.sys-app-health-unknown { background: rgba(var(--text-rgb), 0.10); color: rgba(var(--text-rgb), 0.6); }
.sys-app-health-fail { color: var(--status-danger); font-size: 0.78rem; font-weight: 600; }
.sys-app-health-log {
background: rgba(var(--text-rgb), 0.03);
border-radius: 6px;
margin-top: 4px;
padding: 4px 8px;
font-size: 0.78rem;
}
.sys-app-health-log summary { cursor: pointer; color: rgba(var(--text-rgb), 0.65); }
.sys-app-health-log pre {
margin: 6px 0 0;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.78rem;
white-space: pre-wrap;
color: rgba(var(--text-rgb), 0.7);
}
.sys-app-card-logs {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid rgba(var(--text-rgb), 0.08);
}
.sys-app-card-logs-head {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
}
.sys-app-logs-toggle {
all: unset;
cursor: pointer;
padding: 4px 12px;
font-size: 0.78rem;
font-weight: 600;
color: rgba(var(--text-rgb), 0.7);
background: rgba(var(--text-rgb), 0.06);
border-radius: 999px;
transition: background .15s ease;
}
.sys-app-logs-toggle:hover { background: rgba(var(--accent-rgb), 0.15); color: var(--accent); }
.sys-app-logs-refresh {
all: unset;
cursor: pointer;
width: 24px; height: 24px;
display: inline-flex; align-items: center; justify-content: center;
color: rgba(var(--text-rgb), 0.5);
border-radius: 6px;
}
.sys-app-logs-refresh:hover { color: var(--accent); background: rgba(var(--accent-rgb), 0.12); }
.sys-app-logs-pre {
margin: 0;
padding: 12px;
background: rgba(0, 0, 0, 0.42);
border-radius: 8px;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.78rem;
line-height: 1.45;
color: rgba(var(--text-rgb), 0.85);
max-height: 400px;
overflow: auto;
white-space: pre-wrap;
word-break: break-word;
}
.sys-app-logs-loading,
.sys-app-logs-err {
padding: 12px;
color: rgba(var(--text-rgb), 0.5);
font-size: 0.85rem;
}
.sys-app-logs-err { color: var(--status-danger); }
@media (min-width: 1100px) {
.sys-app-grid { grid-template-columns: 1fr; }
}
/* =========================================================================
Admin → System → Storage (.sys-storage-page)
========================================================================= */
.sys-storage-loading,
.sys-storage-err {
padding: 36px;
text-align: center;
color: rgba(var(--text-rgb), 0.55);
font-size: 0.92rem;
}
.sys-storage-err { color: var(--status-danger); }
.sys-storage-headline {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
gap: 18px;
margin-top: 14px;
}
@media (max-width: 800px) {
.sys-storage-headline { grid-template-columns: 1fr; }
}
.sys-storage-head-card {
background: var(--card-bg);
border: 1px solid rgba(var(--text-rgb), 0.10);
border-radius: 14px;
padding: 18px;
display: flex;
align-items: center;
gap: 18px;
}
.sys-storage-donut {
width: 200px;
height: 200px;
flex-shrink: 0;
}
.sys-storage-donut-total {
fill: var(--text-primary);
font-size: 22px;
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.sys-storage-donut-sub {
fill: rgba(var(--text-rgb), 0.5);
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.sys-storage-legend {
list-style: none;
margin: 0; padding: 0;
display: flex;
flex-direction: column;
gap: 8px;
flex: 1;
min-width: 0;
}
.sys-storage-legend li {
display: grid;
grid-template-columns: 14px auto 1fr auto;
align-items: center;
gap: 10px;
font-size: 0.85rem;
}
.sys-storage-swatch {
width: 12px; height: 12px;
border-radius: 3px;
}
.sys-storage-leg-k {
color: var(--text-primary);
font-weight: 600;
}
.sys-storage-leg-v {
color: rgba(var(--text-rgb), 0.65);
font-variant-numeric: tabular-nums;
text-align: right;
}
.sys-storage-leg-r {
grid-column: 2 / -1;
font-size: 0.72rem;
color: rgba(var(--text-rgb), 0.45);
margin-top: -4px;
}
.sys-storage-head-stats {
display: grid;
grid-template-rows: auto 1fr;
gap: 12px;
}
.sys-storage-stat {
padding: 18px 20px;
background: var(--card-bg);
border: 1px solid rgba(var(--text-rgb), 0.10);
border-radius: 14px;
display: flex;
flex-direction: column;
gap: 4px;
}
.sys-storage-stat-recl {
border-color: rgba(var(--status-warning-rgb), 0.32);
background: linear-gradient(135deg, rgba(var(--status-warning-rgb), 0.14) 0%, rgba(var(--status-warning-rgb), 0.04) 100%);
}
.sys-storage-stat-k {
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.10em;
color: rgba(var(--text-rgb), 0.5);
font-weight: 700;
}
.sys-storage-stat-v {
font-size: 1.8rem;
font-weight: 700;
color: var(--text-primary);
font-variant-numeric: tabular-nums;
line-height: 1.05;
}
.sys-storage-stat-recl .sys-storage-stat-v { color: var(--status-warning); }
/* Storage header: title left, "Reclaim space" action top-right (matches the
metric page's header layout). */
.sys-storage-page .page-header {
align-items: flex-start;
justify-content: space-between;
}
/* "Reclaim space" action — orange. Safe prune only (build cache + dangling
images); never volumes or in-use images. */
.sys-storage-reclaim {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 9px 16px;
font-size: 0.85rem;
font-weight: 600;
white-space: nowrap;
color: var(--status-warning);
background: rgba(var(--status-warning-rgb), 0.12);
border: 1px solid rgba(var(--status-warning-rgb), 0.4);
border-radius: 10px;
cursor: pointer;
transition: background .15s ease, border-color .15s ease, transform .15s ease;
}
.sys-storage-reclaim:hover {
background: rgba(var(--status-warning-rgb), 0.2);
border-color: rgba(var(--status-warning-rgb), 0.65);
transform: translateY(-1px);
}
.sys-storage-reclaim:disabled,
.sys-storage-reclaim.is-running {
opacity: 0.6;
cursor: progress;
transform: none;
}
.sys-storage-reclaim svg { flex: 0 0 auto; }
.sys-storage-reclaim.is-running svg { animation: sysReclaimSpin 0.8s linear infinite; }
@keyframes sysReclaimSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
.sys-storage-reclaim.is-running svg { animation: none; }
}
.sys-storage-stat-sub {
font-size: 0.78rem;
color: rgba(var(--text-rgb), 0.55);
}
.sys-storage-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 12px;
}
.sys-storage-card {
--cat: var(--accent);
--cat-rgb: var(--accent-rgb);
padding: 16px;
background: var(--card-bg);
border: 1px solid rgba(var(--text-rgb), 0.10);
border-left: 3px solid var(--cat);
border-radius: 12px;
}
.sys-storage-card-head {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 4px;
}
.sys-storage-card-head h3 {
font-size: 0.85rem;
margin: 0;
color: var(--text-primary);
}
.sys-storage-card-count {
font-size: 0.72rem;
color: rgba(var(--text-rgb), 0.5);
font-variant-numeric: tabular-nums;
}
.sys-storage-card-size {
font-size: 1.45rem;
font-weight: 700;
color: var(--text-primary);
font-variant-numeric: tabular-nums;
margin-bottom: 8px;
}
.sys-storage-card-bar {
height: 6px;
border-radius: 3px;
background: rgba(var(--text-rgb), 0.08);
overflow: hidden;
}
.sys-storage-card-bar > span {
display: block;
height: 100%;
background: var(--cat);
transition: width .4s ease;
}
.sys-storage-card-meta {
margin-top: 8px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
font-size: 0.74rem;
color: rgba(var(--text-rgb), 0.5);
}
.sys-storage-card-recl { color: var(--status-warning); font-weight: 600; }
.sys-storage-orphan {
display: inline-block;
margin-left: 4px;
padding: 1px 6px;
background: rgba(var(--status-warning-rgb), 0.18);
color: var(--status-warning);
border-radius: 4px;
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.04em;
}
/* System index — inline Storage summary (donut + legend + reclaimable),
links through to the full breakdown page. */
.sys-storage-summary {
margin-top: 10px;
display: flex;
align-items: center;
gap: 24px;
background: var(--card-bg);
border: 1px solid rgba(var(--text-rgb), 0.10);
border-radius: 14px;
padding: 18px 22px;
}
@media (max-width: 700px) {
.sys-storage-summary { flex-direction: column; align-items: stretch; gap: 16px; }
}
.sys-storage-summary-donut {
all: unset;
display: block;
flex-shrink: 0;
cursor: pointer;
border-radius: 50%;
transition: transform .15s ease, filter .15s ease;
}
.sys-storage-summary-donut:hover {
transform: scale(1.03);
filter: drop-shadow(0 4px 14px rgba(var(--accent-rgb), 0.25));
}
.sys-storage-summary-donut:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 4px;
}
.sys-storage-summary-donut .sys-storage-donut { width: 148px; height: 148px; }
.sys-storage-summary-main {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 14px;
}
.sys-storage-srows { display: flex; flex-direction: column; gap: 9px; }
.sys-storage-srow {
display: grid;
grid-template-columns: 12px auto minmax(60px, 1fr) auto;
align-items: center;
gap: 10px;
font-size: 0.85rem;
}
.sys-storage-srow-k { color: var(--text-primary); font-weight: 600; }
.sys-storage-srow-bar { min-width: 0; }
.sys-storage-srow-bar .lp-bar { width: 100%; max-width: none; }
.sys-storage-srow-v {
color: rgba(var(--text-rgb), 0.65);
font-variant-numeric: tabular-nums;
text-align: right;
}
.sys-storage-summary-foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
border-top: 1px solid rgba(var(--text-rgb), 0.08);
padding-top: 13px;
}
.sys-storage-recl-pill {
font-size: 0.78rem;
font-weight: 600;
color: var(--status-warning);
background: rgba(var(--status-warning-rgb), 0.12);
border: 1px solid rgba(var(--status-warning-rgb), 0.28);
padding: 4px 11px;
border-radius: 999px;
}
.sys-storage-more {
all: unset;
cursor: pointer;
font-size: 0.82rem;
font-weight: 600;
color: var(--accent);
transition: opacity .15s ease;
}
.sys-storage-more:hover { opacity: 0.78; text-decoration: underline; }
.sys-storage-more:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
.sys-storage-summary-empty {
justify-content: space-between;
color: rgba(var(--text-rgb), 0.55);
font-size: 0.9rem;
}