diff --git a/containers/libreportal/frontend/css/admin.css b/containers/libreportal/frontend/css/admin.css
index bc2f70f..00fab6d 100644
--- a/containers/libreportal/frontend/css/admin.css
+++ b/containers/libreportal/frontend/css/admin.css
@@ -1,6 +1,16 @@
/* Admin area — Overview board + shared admin-page chrome. Visually aligned
with the backup dashboard (tile/card style) and the config page header. */
+:root {
+ /* Per-area identity hues. Each admin area owns one colour, reused going
+ forward (Overview action buttons today; page headers/accents next).
+ The *-rgb companions feed rgba() tints. */
+ --page-updates: #4f8cff; --page-updates-rgb: 79, 140, 255;
+ --page-backups: #1fb88a; --page-backups-rgb: 31, 184, 138;
+ --page-ssh: #9b7bf0; --page-ssh-rgb: 155, 123, 240;
+ --page-system: #f0883e; --page-system-rgb: 240, 136, 62;
+}
+
.admin-page {
padding: 4px 2px 40px;
}
@@ -110,6 +120,38 @@
color: rgba(var(--text-rgb), 0.55);
}
+/* Per-page identity button. The card sets --page / --page-rgb inline; the
+ button (and its icon, via currentColor) takes the area's hue. Reusable on any
+ admin surface going forward — set the two vars and use .admin-action-btn. */
+.admin-action-btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ padding: 9px 16px;
+ border-radius: 8px;
+ font-size: 0.875rem;
+ font-weight: 600;
+ cursor: pointer;
+ color: var(--page, var(--accent));
+ background: rgba(var(--page-rgb, var(--accent-rgb)), 0.14);
+ border: 1px solid rgba(var(--page-rgb, var(--accent-rgb)), 0.35);
+ transition: background 0.12s ease, transform 0.12s ease, box-shadow 0.12s ease;
+}
+.admin-action-btn:hover {
+ background: rgba(var(--page-rgb, var(--accent-rgb)), 0.22);
+ transform: translateY(-1px);
+}
+.admin-action-btn:disabled { opacity: 0.6; cursor: default; transform: none; box-shadow: none; }
+/* Primary action (e.g. Update now): filled with the area's hue. */
+.admin-action-btn.is-primary {
+ color: #fff;
+ background: var(--page, var(--accent));
+ border-color: transparent;
+}
+.admin-action-btn.is-primary:hover {
+ box-shadow: 0 6px 18px rgba(var(--page-rgb, var(--accent-rgb)), 0.40);
+}
+
/* ============================================================
Admin → System (in-depth statistics page)
============================================================ */
diff --git a/containers/libreportal/frontend/js/components/admin/admin-overview.js b/containers/libreportal/frontend/js/components/admin/admin-overview.js
index 7ceb40f..b414430 100644
--- a/containers/libreportal/frontend/js/components/admin/admin-overview.js
+++ b/containers/libreportal/frontend/js/components/admin/admin-overview.js
@@ -170,9 +170,10 @@ class AdminOverview {
if (vDisp) updBody += this.integrityLine(vDisp);
// Update now takes priority when one's available; otherwise offer Verify now.
+ const updPage = 'style="--page:var(--page-updates);--page-rgb:var(--page-updates-rgb)"';
const updActions = (updAvail && upd.can_update)
- ? ``
- : ``;
+ ? ``
+ : ``;
const updCard = this.card(
'Updates',
@@ -194,7 +195,7 @@ class AdminOverview {
this.line('Apps protected', `${protectedApps} / ${apps.length}`)
+ this.line('Locations', String(locs.length))
+ this.line('Stored', this.bytes(totalSize)),
- ``
+ ``
);
// SSH & Security
@@ -209,7 +210,7 @@ class AdminOverview {
this.line('Password login', pwOn ? 'On' : 'Key-only')
+ this.line('Authorized keys', String(keyCount))
+ this.line('Login user', s.user || '—'),
- ``
+ ``
);
// System health
@@ -226,7 +227,7 @@ class AdminOverview {
? `${this.gb(mem.used)} / ${this.gb(mem.total)} (${Math.round(mem.percent) || 0}%)`
: (mem.text || '—'))
+ this.line('Uptime', this.shortUptime(info.uptime)),
- ``
+ ``
);
root.innerHTML = `