Files
felhom-controller/controller/internal/web/templates/style.css
T
admin 3a9d744360 v0.154.0 — R-48: one restore entry per app, and the intent is a described choice
The offsite restore list rendered up to five inline forms per app row. Two of them —
„Helyreállítás az élő adatok közé (csak a hiányzó fájlok)" and „Teljes visszaállítás
(fájlok + adatbázis)" — were sibling buttons whose difference is whether the customer's
data comes back at all. That mis-selection CAUSED the round-2 incident: an operator who
had read the source pressed the missing-only button and /backup/offbox/reconstitute was
never hit (DIAG-immich-restore-round2-2026-07-19, finding 1).

The rule this establishes: two adjacent controls whose difference is "your data comes
back" vs "your data cannot come back" must never be distinguishable only by layout.

Each row now carries ONE „Visszaállítás…" entry linking to a per-app wizard at
GET /backups/restore/app?name=<app>: three intent CARDS with consequence sentences, a
visible phase strip, danger styling plus the R-43 double-confirm carried over verbatim on
the destructive one, and the pair-honesty panel bound to real OffsiteScratchPair facts.

deriveWizardStep is pure over (op running, size-gate flash, scratch ready); the step is
never accepted from the request, and a running op outranks a stale ?full_prep= so no
commit button survives into a restore. While ANY op runs every mutation form is suppressed
server-side instead of being offered and refused.

No new mutation endpoint: every card posts to the pre-existing /backup/offbox/* handler
with the same field names and gates, and the page works with JavaScript disabled.
internal/{backup,appbackup,selfupdate} untouched. R-45's job registry stays its own item.

Fixes a latent bug found on the way: offboxRedirectTo hardcoded "?" when appending its
flash, which against the wizard's ?name=<app> target would have buried the flash inside
the name value.

No new agent coupling — MinAgent stays 0.90.0.
2026-07-21 08:30:42 +02:00

3448 lines
82 KiB
CSS

/* Vendored fonts — variable woff2 embedded in the binary, served from /static/fonts/.
No CDN dependency (offline customer nodes). latin-ext is mandatory for Hungarian ő/ű. */
@font-face {
font-family: 'Plus Jakarta Sans';
font-style: normal;
font-weight: 400 800;
font-display: swap;
src: url('/static/fonts/pjs-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Plus Jakarta Sans';
font-style: normal;
font-weight: 400 800;
font-display: swap;
src: url('/static/fonts/pjs-latin-ext.woff2') format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400 500;
font-display: swap;
src: url('/static/fonts/jbm-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400 500;
font-display: swap;
src: url('/static/fonts/jbm-latin-ext.woff2') format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
:root {
--bg-0: #0A1220; --bg-1: #0F1B2E; --bg-2: #16263F;
--line: #22344F; --line-soft: #1A2A42;
--text-1: #EDF2F9; --text-2: #94A6BF; --text-3: #5E7392;
--blue: #0083D8; --blue-bright: #2EA8F5; --blue-dim: rgba(0,131,216,.13);
--warn: #E0A93E; --warn-dim: rgba(224,169,62,.12);
--crit: #E5534B; --crit-dim: rgba(229,83,75,.12);
--radius: 2px;
--font-ui: 'Plus Jakarta Sans', -apple-system, sans-serif;
--font-data: 'JetBrains Mono', monospace;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
font-family: var(--font-ui);
background: var(--bg-0);
color: var(--text-1);
display: flex;
min-height: 100vh;
line-height: 1.6;
}
/* Focus visibility on all interactive elements (design system v2). */
:focus-visible {
outline: 2px solid var(--blue-bright);
outline-offset: 1px;
}
/* Inline icon (vendored Lucide sprite, stroke = currentColor) */
.ico {
width: 16px;
height: 16px;
flex-shrink: 0;
vertical-align: -0.18em;
}
.ico-sm { width: 14px; height: 14px; }
/* Sidebar */
.sidebar {
width: 240px;
background: var(--bg-2);
color: var(--text-1);
display: flex;
flex-direction: column;
position: fixed;
height: 100vh;
overflow-y: auto;
border-right: 1px solid var(--line);
}
.sidebar-header {
padding: 1.5rem;
border-bottom: 1px solid var(--line);
}
.sidebar-logo {
width: 140px;
height: auto;
display: block;
margin-bottom: 0.25rem;
}
.login-logo {
width: 200px;
height: auto;
display: block;
margin: 0 auto 0.5rem;
}
.customer-name {
display: block;
font-size: .85rem;
color: var(--text-2);
margin-top: .25rem;
}
.nav-links {
list-style: none;
padding: 1rem 0;
flex: 1;
}
.nav-links a {
display: flex;
align-items: center;
gap: .6rem;
padding: .75rem 1.5rem;
color: var(--text-2);
text-decoration: none;
font-size: .95rem;
font-weight: 500;
transition: color 0.2s ease, background 0.2s ease;
}
.nav-links a:hover {
color: var(--blue-bright);
background: var(--blue-dim);
}
.nav-links a.active {
color: var(--blue-bright);
background: var(--blue-dim);
border-left: 3px solid var(--blue);
}
.sidebar-footer {
padding: 1rem 1.5rem;
border-top: 1px solid var(--line);
display: flex;
justify-content: space-between;
align-items: center;
font-size: .8rem;
}
.version { color: var(--text-3); }
.logout-link { color: var(--text-3); text-decoration: none; transition: color 0.2s ease; }
.logout-link:hover { color: var(--blue-bright); }
/* Main content */
.content {
margin-left: 240px;
padding: 2rem;
flex: 1;
max-width: 1200px;
}
.page-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1.5rem;
}
.page-header h2 {
font-size: 1.5rem;
font-weight: 600;
color: var(--text-1);
}
.domain-badge {
border: 1px solid var(--line);
color: var(--text-2);
padding: .2rem .6rem;
border-radius: var(--radius);
font-size: .8rem;
font-weight: 500;
font-family: var(--font-data);
}
h3 {
font-size: 1.1rem;
font-weight: 600;
color: var(--text-1);
margin-bottom: 1rem;
}
/* Stats — boxless figures, 1px right hairline dividers (v2). */
.stats-grid {
display: flex;
flex-wrap: wrap;
margin-bottom: 2rem;
}
.stat-card {
flex: 1;
min-width: 140px;
padding: .35rem 1.5rem;
border-right: 1px solid var(--line);
}
.stat-card:first-child { padding-left: 0; }
.stat-card:last-child { border-right: none; }
.stat-value {
font-family: var(--font-data);
font-size: 1.9rem;
font-weight: 500;
color: var(--text-1);
}
.stat-zero .stat-value { color: var(--text-3); }
.stat-warn .stat-value { color: var(--warn); }
.stat-label {
color: var(--text-2);
font-size: .85rem;
margin-top: .25rem;
}
/* System info bar */
.system-info-card {
background: var(--bg-1);
border-radius: var(--radius);
padding: 1rem 1.25rem;
border: 1px solid var(--line);
margin-bottom: 2rem;
}
.system-info-items {
display: flex;
gap: 2rem;
flex-wrap: wrap;
}
.system-info-item {
flex: 1;
min-width: 200px;
}
.system-info-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: .5rem;
}
.system-info-label {
font-size: .8rem;
font-weight: 600;
color: var(--text-2);
text-transform: uppercase;
letter-spacing: .5px;
}
.system-info-value {
font-size: .8rem;
color: var(--text-3);
font-family: var(--font-data);
}
/* Meter — hairline capacity readout. Nominal fill is blue; amber/red appear
only past the 70%/85% thresholds (exception-color principle). The threshold
ticks are neutral hairlines baked into the track. */
.meter-track {
position: relative;
width: 100%;
height: 3px;
background: var(--line-soft);
}
.meter-track::before,
.meter-track::after {
content: '';
position: absolute;
top: -2px;
bottom: -2px;
width: 1px;
background: var(--line);
z-index: 2;
}
.meter-track::before { left: 70%; }
.meter-track::after { left: 85%; }
.meter-track.no-ticks::before,
.meter-track.no-ticks::after { display: none; }
.meter-fill {
position: absolute;
left: 0; top: 0; bottom: 0;
background: var(--blue);
z-index: 1;
transition: width 0.3s ease;
}
.meter.warn .meter-fill { background: var(--warn); }
.meter.crit .meter-fill { background: var(--crit); }
.meter-flag {
display: inline-flex;
align-items: center;
gap: .4rem;
margin-top: .5rem;
padding: .2rem .55rem;
font-size: .75rem;
font-weight: 500;
border-radius: var(--radius);
}
.meter.warn .meter-flag { color: var(--warn); background: var(--warn-dim); }
.meter.crit .meter-flag { color: var(--crit); background: var(--crit-dim); }
.meter-flag .ico { width: 13px; height: 13px; }
/* ─── Shared app-list row (v0.126.0) ──────────────────────────────────
THE canonical list grammar (app_row.html partial): icon + name left, caller action
block right. Compact single-row height unless a secondary line is present — matches
the Alkalmazások backup-status collapsed-header density. */
.app-row-list {
display: flex;
flex-direction: column;
gap: .5rem;
}
.app-row {
position: relative;
display: flex;
align-items: center;
gap: .75rem;
padding: .5rem 1rem;
min-height: 44px;
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: var(--radius);
}
.app-row-icon {
width: 24px;
height: 24px;
border-radius: var(--radius);
object-fit: contain;
background: var(--bg-2);
flex-shrink: 0;
}
.app-row-text {
display: flex;
flex-direction: column;
gap: .1rem;
min-width: 0;
flex: 1;
}
.app-row-name {
font-weight: 500;
font-size: .9rem;
}
.app-row-secondary {
font-size: .8rem;
color: var(--text-2);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.app-row-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: .5rem;
flex-shrink: 0;
flex-wrap: wrap;
}
/* 2px state edge (dashboard rows carry stack-state-<color> as RowClass). */
.app-row::before {
content: '';
position: absolute;
left: 0; top: 0; bottom: 0;
width: 2px;
background: transparent;
}
.app-row.stack-state-run::before,
.app-row.stack-state-progress::before { background: var(--blue); }
.app-row.stack-state-warn::before { background: var(--warn); }
/* Stack list (dashboard) — one panel, hairline-separated shared rows. */
.stack-list {
background: var(--bg-1);
border: 1px solid var(--line);
border-radius: var(--radius);
}
.stack-list .app-row {
background: transparent;
border: none;
border-top: 1px solid var(--line-soft);
border-radius: 0;
padding: .6rem 1.25rem;
transition: background 0.15s ease;
}
.stack-list .app-row:first-child { border-top: none; }
.stack-list .app-row:hover {
background: rgba(0,131,216,.04);
}
.stack-logo-lg {
width: 48px;
height: 48px;
border-radius: var(--radius);
object-fit: contain;
background: var(--bg-2);
padding: 6px;
}
.stack-state-label {
font-size: .8rem;
color: var(--text-2);
margin-right: .5rem;
}
/* Stack detail grid (applications page) */
.stack-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 1rem;
}
.stack-detail-card {
background: var(--bg-1);
border-radius: var(--radius);
padding: 1.25rem;
border: 1px solid var(--line);
transition: border-color 0.3s ease, transform 0.3s ease;
display: flex;
flex-direction: column;
}
.stack-detail-card:hover {
border-color: var(--blue);
transform: translateY(-2px);
}
.stack-detail-card {
position: relative;
}
.stack-detail-card::before {
content: '';
position: absolute;
left: 0; top: 0; bottom: 0;
width: 2px;
background: transparent;
}
.stack-detail-card.stack-state-run::before,
.stack-detail-card.stack-state-progress::before { background: var(--blue); }
.stack-detail-card.stack-state-warn::before { background: var(--warn); }
.stack-detail-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: .75rem;
}
.stack-title-row {
display: flex;
align-items: center;
gap: .75rem;
flex: 1;
min-width: 0;
}
.subdomain-link {
font-size: .8rem;
color: var(--blue-bright);
text-decoration: none;
transition: color 0.2s ease;
}
.subdomain-link:hover { text-decoration: underline; }
/* Tag — square 2px state chip with a 6px dot. Color only on deviation:
run/progress are blue (nominal), warn/crit amber/red, neutral/off gray. */
.tag {
display: inline-flex;
align-items: center;
gap: .45rem;
padding: .15rem .55rem;
border: 1px solid var(--line);
border-radius: var(--radius);
font-size: .75rem;
font-weight: 600;
color: var(--text-2);
white-space: nowrap;
flex-shrink: 0;
}
.tag .dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--text-3);
flex-shrink: 0;
}
.tag-run { border-color: rgba(0,131,216,.45); color: var(--blue-bright); }
.tag-run .dot { background: var(--blue); }
.tag-progress { border-color: rgba(0,131,216,.45); color: var(--blue-bright); }
.tag-progress .dot { background: var(--blue); animation: tag-pulse 1.2s ease-in-out infinite; }
.tag-warn { border-color: var(--warn); color: var(--warn); background: var(--warn-dim); }
.tag-warn .dot { background: var(--warn); }
.tag-crit { border-color: var(--crit); color: var(--crit); background: var(--crit-dim); }
.tag-crit .dot { background: var(--crit); }
.tag-neutral .dot, .tag-off .dot { background: transparent; border: 1px solid var(--text-3); }
@keyframes tag-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.25; }
}
@media (prefers-reduced-motion: reduce) {
.tag-progress .dot { animation: none; }
}
/* State text colors — funcmap emits run/progress/warn/neutral/off (stateColor)
and nominal/warn/crit (usageColor/tempColor). */
.state-text-run { color: var(--blue-bright); }
.state-text-progress { color: var(--blue-bright); }
.state-text-nominal { color: var(--text-1); }
.state-text-warn { color: var(--warn); }
.state-text-crit { color: var(--crit); }
.state-text-neutral, .state-text-off { color: var(--text-3); }
.stack-detail-desc {
color: var(--text-2);
font-size: .85rem;
margin-bottom: .75rem;
}
/* Metadata rows — icon + text, NO container (replaces informational pills). */
.metarows {
display: flex;
flex-wrap: wrap;
gap: .3rem 1rem;
margin: .5rem 0;
}
.metarow {
display: inline-flex;
align-items: center;
gap: .4rem;
font-size: .78rem;
color: var(--text-2);
}
.metarow .ico {
width: 14px;
height: 14px;
color: var(--text-3);
}
.metarow .mono { font-family: var(--font-data); font-size: .75rem; }
/* Legacy informational pill — restyled flat until the Phase-4 sweep replaces the markup. */
.meta-badge {
color: var(--text-2);
padding: .1rem 0;
font-size: .78rem;
}
.meta-badge-ok {
color: var(--text-2);
}
.container-list { margin: .75rem 0; }
.container-list h4 { font-size: .8rem; color: var(--text-3); margin-bottom: .4rem; }
.container-row {
display: flex;
justify-content: space-between;
font-size: .8rem;
padding: .3rem 0;
border-bottom: 1px solid var(--line-soft);
}
.container-row:last-child { border-bottom: none; }
.container-name { font-family: var(--font-data); color: var(--text-2); }
.container-status { font-size: .75rem; }
.stack-detail-actions {
display: flex;
gap: .5rem;
margin-top: auto;
padding-top: 1rem;
flex-wrap: wrap;
}
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
gap: .3rem;
padding: .5rem 1rem;
border: 1px solid var(--line);
border-radius: var(--radius);
font-size: .85rem;
font-weight: 600;
cursor: pointer;
transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
text-decoration: none;
color: var(--text-1);
font-family: inherit;
background: var(--bg-2);
}
.btn:hover { border-color: var(--blue); }
.btn:active { opacity: .85; }
.btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }
.btn.loading { opacity: .6; }
.btn-sm { padding: .3rem .6rem; font-size: .8rem; }
.btn-lg { padding: .65rem 1.5rem; font-size: 1rem; }
.btn-full { width: 100%; justify-content: center; }
.btn-primary {
background: var(--blue);
border-color: var(--blue);
color: #fff;
}
.btn-primary:hover { background: var(--blue-bright); border-color: var(--blue-bright); }
.btn-success {
background: var(--blue);
border-color: var(--blue);
color: #fff;
}
.btn-success:hover { background: var(--blue-bright); border-color: var(--blue-bright); }
.btn-warning {
background: transparent;
border: 1px solid var(--line);
color: var(--text-2);
}
.btn-warning:hover {
border-color: var(--blue);
color: var(--blue-bright);
background: var(--blue-dim);
}
.btn-danger {
background: transparent;
border: 1px solid var(--crit);
color: var(--crit);
}
.btn-danger:hover {
background: var(--crit-dim);
}
.btn-outline {
background: transparent;
border: 1px solid var(--line);
color: var(--text-2);
}
.btn-outline:hover {
border-color: var(--blue);
color: var(--blue-bright);
background: var(--blue-dim);
}
.badge {
display: inline-flex;
align-items: center;
gap: .25rem;
padding: .2rem .6rem;
border-radius: var(--radius);
font-size: .75rem;
font-weight: 500;
}
.badge-protected {
background: var(--bg-2);
color: var(--text-3);
}
/* Deploy page */
.deploy-container { max-width: 700px; }
.deploy-info {
display: flex;
gap: 1rem;
align-items: flex-start;
background: var(--bg-1);
padding: 1.25rem;
border-radius: var(--radius);
border: 1px solid var(--line);
margin-bottom: 1.5rem;
}
.deploy-logo {
width: 64px;
height: 64px;
border-radius: var(--radius);
object-fit: contain;
background: var(--bg-2);
padding: 8px;
flex-shrink: 0;
}
.deploy-info h3 { font-size: 1.2rem; margin-bottom: .25rem; }
.deploy-info p { color: var(--text-2); font-size: .9rem; }
.deploy-form {
background: var(--bg-1);
padding: 1.5rem;
border-radius: var(--radius);
border: 1px solid var(--line);
}
.form-section { margin-bottom: 1.5rem; }
.form-section h4 { font-size: 1rem; margin-bottom: .5rem; color: var(--text-1); }
.form-section-desc { color: var(--text-2); font-size: .85rem; margin-bottom: .75rem; }
.form-group { margin-bottom: 1rem; }
.form-group label { display: block; font-size: .85rem; font-weight: 500; margin-bottom: .4rem; color: var(--text-1); }
.form-group-auto {
padding: .5rem .75rem;
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: var(--radius);
}
.form-group-auto label { display: flex; align-items: center; gap: .5rem; margin-bottom: .4rem; }
.form-group-auto .form-control[readonly] { background: var(--bg-0); }
.auto-generated-badge { color: var(--blue); font-size: .75rem; font-weight: normal; }
.checkbox-group {
display: flex;
flex-direction: column;
gap: .5rem;
margin-top: .25rem;
}
.form-inline {
display: flex;
align-items: center;
gap: .75rem;
}
.form-control-narrow {
width: 80px;
}
.form-hint {
font-size: .85rem;
color: var(--text-3);
}
.form-actions {
display: flex;
gap: .75rem;
margin-top: 1rem;
}
.form-control {
width: 100%;
padding: .55rem .75rem;
border: 1px solid var(--line);
border-radius: var(--radius);
font-size: .9rem;
background: var(--bg-2);
color: var(--text-1);
font-family: inherit;
transition: border-color 0.2s ease;
}
.form-control:focus {
outline: none;
border-color: var(--blue);
}
.form-control:disabled {
background: var(--bg-0);
color: var(--text-3);
cursor: not-allowed;
}
select.form-control { appearance: auto; }
select.form-control option { background: var(--bg-2); color: var(--text-1); }
/* .form-input / .form-row — the backups-page twin of .form-control (v0.126.1): the class
was used across the backups/import/offbox templates with NO backing rule, so those
inputs rendered as browser defaults (the operator's "unstyled clipped placeholder"
screenshot). Same visual spec as .form-control. */
.form-input {
width: 100%;
padding: .55rem .75rem;
border: 1px solid var(--line);
border-radius: var(--radius);
font-size: .9rem;
background: var(--bg-2);
color: var(--text-1);
font-family: inherit;
transition: border-color 0.2s ease;
}
.form-input:focus { outline: none; border-color: var(--blue); }
.form-input::placeholder { color: var(--text-3); }
.form-row { margin-bottom: .75rem; }
.form-row > label { display: block; font-size: .85rem; color: var(--text-2); margin-bottom: .3rem; }
.input-with-button { display: flex; gap: .5rem; }
.input-with-button .form-control { flex: 1; }
.subdomain-input-group { display: flex; align-items: center; gap: .5rem; }
.subdomain-input-group .subdomain-input { width: 8rem; }
.subdomain-input-group .subdomain-suffix {
color: var(--text-2); font-size: .95rem; white-space: nowrap;
}
.form-hint { display: block; font-size: .8rem; color: var(--text-3); margin-top: .25rem; }
.required { color: var(--crit); }
.locked-hint { font-size: .75rem; color: var(--text-3); font-weight: 400; margin-left: .5rem; }
.deploy-actions {
display: flex;
gap: .75rem;
margin-top: 1.5rem;
padding-top: 1rem;
border-top: 1px solid var(--line);
}
/* Deploy progress */
.deploy-progress {
background: var(--bg-1);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 1.5rem;
}
.deploy-progress h3 {
margin-bottom: 0.5rem;
}
.deploy-steps {
margin: 1rem 0;
}
.deploy-step {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.5rem 0;
font-size: .95rem;
color: var(--text-3);
}
.deploy-step.active {
color: var(--text-1);
}
.deploy-step.done {
color: var(--text-1);
}
.deploy-step.done .step-icon {
color: var(--blue);
}
.deploy-step.error .step-icon {
color: var(--crit);
}
.deploy-step.warn .step-icon {
color: var(--warn);
}
.step-icon {
font-size: 1.1rem;
width: 1.5rem;
text-align: center;
flex-shrink: 0;
}
.deploy-elapsed {
color: var(--text-3);
font-size: .85rem;
margin-top: 0.5rem;
}
/* Toggle switch */
.toggle { cursor: pointer; display: flex; align-items: center; gap: .5rem; }
.toggle input[type="checkbox"] { accent-color: var(--blue); }
.toggle-label { font-size: .9rem; color: var(--text-2); }
/* Alerts */
.alert {
padding: .75rem 1rem;
border-radius: var(--radius);
margin-bottom: 1rem;
font-size: .85rem;
border: 1px solid;
}
.alert-error {
background: var(--crit-dim);
color: var(--crit);
border-color: var(--crit-dim);
}
.alert-info {
background: var(--blue-dim);
color: var(--blue-bright);
border-color: rgba(0,131,216,.35);
}
.alert-warning {
background: var(--warn-dim);
color: var(--warn);
border-color: var(--warn-dim);
}
/* Dashboard alert banners */
.alerts-container { margin-bottom: 1rem; }
.alert-banner {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
border-radius: var(--radius);
margin-bottom: 0.5rem;
font-size: 0.9rem;
border: 1px solid;
}
.alert-banner-error {
background: var(--crit-dim);
border-color: var(--crit-dim);
color: var(--crit);
}
.alert-banner-warning {
background: var(--warn-dim);
border-color: var(--warn-dim);
color: var(--warn);
}
.alert-banner-info {
background: var(--blue-dim);
border-color: var(--blue-dim);
color: var(--blue-bright);
}
.alert-icon { flex-shrink: 0; }
.alert-message { flex: 1; }
.alert-link { margin-left: auto; white-space: nowrap; }
/* Inline storage warnings (under storage bars on dashboard and monitoring) */
.inline-warnings { margin-top: 0.75rem; }
.inline-warning {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.4rem 0.75rem;
margin-top: 0.25rem;
font-size: 0.8rem;
border-radius: var(--radius);
}
.inline-warning-warning {
color: var(--warn);
background: var(--warn-dim);
border: 1px solid var(--warn-dim);
}
.inline-warning-error {
color: var(--crit);
background: var(--crit-dim);
border: 1px solid var(--crit-dim);
}
.inline-warning-dot { font-size: 0.6rem; flex-shrink: 0; }
.inline-warning-text { flex: 1; }
.inline-warning-link {
color: inherit;
opacity: 0.8;
white-space: nowrap;
text-decoration: none;
}
.inline-warning-link:hover { opacity: 1; text-decoration: underline; }
/* Memory summary on deploy page */
.memory-summary {
background: var(--bg-1);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 1rem 1.25rem;
margin-bottom: 1rem;
}
.memory-blocked {
border-color: var(--crit-dim);
}
.memory-summary-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.memory-summary-label {
font-size: .8rem;
font-weight: 600;
color: var(--text-2);
text-transform: uppercase;
letter-spacing: .5px;
}
.memory-summary-value {
font-size: .8rem;
color: var(--text-3);
font-family: var(--font-data);
}
/* Deploy-page memory meter — two segments on the meter track:
committed = solid blue, new app = blue at 35% opacity (v2). */
.meter-track-stacked {
height: 3px;
display: flex;
overflow: visible;
position: relative;
background: var(--line-soft);
}
.meter-track-stacked::before,
.meter-track-stacked::after {
content: '';
position: absolute;
top: -2px;
bottom: -2px;
width: 1px;
background: var(--line);
z-index: 2;
}
.meter-track-stacked::before { left: 70%; }
.meter-track-stacked::after { left: 85%; }
.memory-bar-segment {
height: 100%;
position: relative;
z-index: 1;
transition: width 0.3s ease;
}
.memory-bar-committed {
background: var(--blue);
}
.memory-bar-new {
background: rgba(0,131,216,.35);
}
.memory-bar-legend {
display: flex;
flex-wrap: wrap;
gap: 0.5rem 1.25rem;
margin-top: 0.5rem;
}
.memory-legend-item {
display: flex;
align-items: center;
gap: 0.35rem;
font-size: .75rem;
color: var(--text-3);
white-space: nowrap;
}
.memory-legend-dot {
display: inline-block;
width: 10px;
height: 10px;
border-radius: var(--radius);
flex-shrink: 0;
}
.memory-legend-committed {
background: var(--blue);
}
.memory-legend-new {
background: rgba(0,131,216,.35);
}
.memory-dist-bar {
width: 100%;
height: 14px;
border-radius: var(--radius);
display: flex;
overflow: hidden;
background: var(--bg-2);
border: 1px solid var(--line);
}
.memory-dist-bar .memory-bar-segment:first-child {
border-radius: 0;
}
.memory-dist-bar .memory-bar-segment:last-child {
border-radius: 0;
}
.memory-dist-header {
font-size: .85rem;
color: var(--text-2);
margin-bottom: .5rem;
font-family: var(--font-data);
}
/* Logs */
.logs-container {
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 1rem;
overflow-x: auto;
overflow-y: auto;
max-height: 70vh;
margin-bottom: 1rem;
}
.logs-output {
color: var(--text-1);
font-family: var(--font-data);
font-size: .8rem;
line-height: 1.5;
white-space: pre-wrap;
word-break: break-all;
margin: 0;
}
.logs-actions {
display: flex;
gap: .5rem;
align-items: center;
}
.logs-live-indicator {
display: inline-flex;
align-items: center;
gap: .35rem;
font-size: .8rem;
font-weight: 600;
color: var(--blue);
margin-right: .5rem;
}
.logs-live-indicator.logs-live-paused {
color: var(--text-3);
}
.logs-live-dot {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--blue);
animation: logs-pulse 1.5s ease-in-out infinite;
}
@keyframes logs-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
/* Sync toast */
.sync-toast {
padding: .6rem 1rem;
border-radius: var(--radius);
font-size: .85rem;
margin-bottom: 1rem;
border: 1px solid;
transition: opacity 0.3s ease;
}
.sync-toast-ok {
background: var(--blue-dim);
color: var(--blue);
border-color: var(--blue-dim);
}
.sync-toast-err {
background: var(--crit-dim);
color: var(--crit);
border-color: var(--crit-dim);
}
/* --- Filter bar --- */
.filter-bar {
display: flex;
gap: 0.5rem;
margin-bottom: 1.5rem;
flex-wrap: wrap;
}
.filter-btn {
padding: 0.4rem 1rem;
border-radius: var(--radius);
border: 1px solid var(--line);
background: var(--bg-2);
color: var(--text-2);
cursor: pointer;
font-size: 0.85rem;
font-family: inherit;
transition: all 0.2s;
}
.filter-btn:hover {
border-color: var(--blue);
color: var(--text-1);
}
.filter-btn.active {
background: var(--blue);
border-color: var(--blue);
color: #fff;
}
.filter-count {
opacity: 0.7;
font-size: 0.8rem;
}
/* Clickable stat cards */
a.stat-card {
text-decoration: none;
color: inherit;
transition: transform 0.15s;
}
a.stat-card:hover {
transform: translateY(-2px);
}
/* Clickable cards */
[data-href] { cursor: pointer; }
.empty-state { text-align: center; padding: 3rem; color: var(--text-3); }
/* Login page */
.login-body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: var(--bg-0);
}
.login-card {
background: var(--bg-1);
padding: 2.5rem;
border-radius: var(--radius);
border: 1px solid var(--line);
width: 100%;
max-width: 380px;
text-align: center;
position: relative;
z-index: 1;
}
.login-card .login-logo {
width: 220px;
margin-bottom: 0.5rem;
}
.login-subtitle {
color: var(--text-2);
margin-bottom: 1.5rem;
}
.login-footer {
margin-top: 1.5rem;
font-size: .75rem;
color: var(--text-3);
}
.login-footer a {
color: var(--blue-bright);
text-decoration: none;
transition: color 0.2s ease;
}
.login-footer a:hover { color: var(--blue); }
/* --- App Info Page --- */
.app-info-hero {
display: flex;
align-items: center;
gap: 1.5rem;
padding: 1.5rem;
background: var(--bg-1);
border-radius: var(--radius);
border: 1px solid var(--line);
margin-bottom: 1.5rem;
}
.app-info-logo {
width: 80px;
height: 80px;
min-width: 80px;
min-height: 80px;
max-width: 80px;
max-height: 80px;
border-radius: var(--radius);
object-fit: contain;
background: var(--bg-2);
padding: 10px;
flex-shrink: 0;
overflow: hidden;
}
.app-info-tagline {
font-size: 1.1rem;
color: var(--text-1);
margin: 0 0 .75rem 0;
}
.app-screenshots {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
overflow-x: auto;
padding-bottom: .5rem;
}
.app-screenshot {
max-height: 220px;
border-radius: var(--radius);
border: 1px solid var(--line);
object-fit: cover;
}
.app-info-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1rem;
margin-bottom: 1.5rem;
}
.app-info-card {
background: var(--bg-1);
border-radius: var(--radius);
padding: 1.25rem;
border: 1px solid var(--line);
}
.app-info-card h3 {
margin: 0 0 .75rem 0;
font-size: .95rem;
color: var(--text-1);
}
.app-info-list {
margin: 0;
padding-left: 1.25rem;
color: var(--text-2);
font-size: .9rem;
line-height: 1.6;
}
.app-info-creds {
font-family: monospace;
font-size: 1rem;
color: var(--blue-bright);
background: var(--bg-2);
padding: .5rem .75rem;
border-radius: var(--radius);
display: inline-block;
margin: 0 0 .5rem 0;
}
.app-info-creds-warn {
color: var(--warn);
font-size: .85rem;
margin: 0;
}
.app-info-link {
color: var(--blue-bright);
text-decoration: none;
}
.app-info-link:hover { text-decoration: underline; }
/* Optional config section */
.app-optional-config {
background: var(--bg-1);
border-radius: var(--radius);
padding: 1.5rem;
border: 1px solid var(--line);
margin-bottom: 1.5rem;
}
.app-optional-config h3 {
margin: 0 0 1rem 0;
font-size: 1.05rem;
}
.config-group {
margin-bottom: 1.5rem;
}
.config-group h4 {
margin: 0 0 .5rem 0;
font-size: .95rem;
color: var(--text-1);
}
.config-group-desc {
color: var(--text-2);
font-size: .85rem;
margin: 0 0 1rem 0;
}
.config-fields {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 1rem;
}
.config-field {
display: flex;
flex-direction: column;
gap: .25rem;
}
.config-field label {
font-size: .85rem;
font-weight: 500;
color: var(--text-1);
}
.config-field-help {
font-size: .8rem;
color: var(--text-2);
margin: 0;
line-height: 1.4;
}
.config-field-help a {
color: var(--blue-bright);
text-decoration: none;
}
.config-field-help a:hover { text-decoration: underline; }
.config-input {
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: .5rem .75rem;
color: var(--text-1);
font-size: .9rem;
font-family: monospace;
width: 100%;
box-sizing: border-box;
}
.config-input:focus {
outline: none;
border-color: var(--blue);
}
.config-actions {
display: flex;
align-items: center;
gap: 1rem;
margin-top: 1rem;
}
.config-save-status {
font-size: .9rem;
transition: opacity .3s;
}
.config-save-ok { color: var(--blue); }
.config-save-err { color: var(--crit); }
.meta-badge-storage {
background: var(--blue-dim);
color: var(--blue-bright);
}
.meta-badge-warn {
background: var(--warn-dim) !important;
color: var(--warn) !important;
}
.meta-badge-ok {
background: var(--blue-dim) !important;
color: var(--blue) !important;
}
/* Orphan badge */
.badge-orphaned {
background: var(--warn-dim);
color: var(--warn);
}
/* B2b: a deployed app whose data drive was decommissioned/disconnected — distinct warning, not error-spam. */
.badge-missing-storage {
background: var(--warn-dim);
color: var(--warn);
white-space: nowrap;
}
/* F5: an unhealthy/restarting deployed app has its public route withheld by Traefik (404 at the URL). */
.badge-route-unpublished {
background: var(--warn-dim);
color: var(--warn);
white-space: nowrap;
}
.route-unpublished {
display: inline-block;
margin-top: 2px;
font-size: 0.8em;
color: var(--warn);
white-space: nowrap;
}
/* Delete modal */
.modal-overlay {
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-card {
background: var(--bg-1);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 1.5rem;
max-width: 500px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
}
.modal-card h3 {
margin-bottom: .75rem;
}
.modal-hdd-info {
background: var(--bg-2);
border-radius: var(--radius);
padding: .75rem 1rem;
margin: .75rem 0;
font-size: .85rem;
color: var(--text-2);
}
.modal-hdd-path {
font-family: var(--font-data);
font-size: .8rem;
color: var(--text-3);
padding: .2rem 0;
}
.modal-checkbox {
display: flex;
align-items: center;
gap: .5rem;
margin: .75rem 0;
padding: .75rem 1rem;
background: var(--crit-dim);
border: 1px solid var(--crit-dim);
border-radius: var(--radius);
font-size: .85rem;
color: var(--crit);
cursor: pointer;
}
.modal-checkbox input[type="checkbox"] {
accent-color: var(--crit);
}
.modal-actions {
display: flex;
gap: .75rem;
margin-top: 1rem;
justify-content: flex-end;
}
/* Load average text */
.system-load-avg {
font-size: .7rem;
color: var(--text-3);
font-family: var(--font-data);
margin-top: .25rem;
}
/* Temperature — plain mono value; color only on warn/crit (v2). */
.temp-value {
font-family: var(--font-data);
font-size: .85rem;
font-weight: 600;
}
.system-info-item-compact {
flex: 0 1 auto;
min-width: 120px;
}
/* Backup status card */
.backup-status-card {
background: var(--bg-1);
border-radius: var(--radius);
padding: 1rem 1.25rem;
border: 1px solid var(--line);
margin-bottom: 2rem;
}
.backup-status-card h3 {
margin-bottom: .75rem;
}
.backup-info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: .25rem 0;
font-size: .85rem;
}
.backup-label {
color: var(--text-2);
}
.backup-value {
color: var(--text-1);
font-family: var(--font-data);
font-size: .8rem;
}
.backup-status-ok { color: var(--blue); }
.backup-status-fail { color: var(--crit); }
.backup-status-none { color: var(--text-3); }
/* Dashboard backup card link */
.backup-card-link {
color: var(--text-1);
text-decoration: none;
transition: color 0.2s;
}
.backup-card-link:hover {
color: var(--blue-bright);
}
/* --- Backup page --- */
.backup-page-cards {
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.backup-empty-state {
text-align: center;
padding: 4rem 2rem;
background: var(--bg-1);
border-radius: var(--radius);
border: 1px solid var(--line);
}
.backup-empty-icon {
font-size: 3rem;
margin-bottom: 1rem;
}
.backup-empty-state h3 {
margin-bottom: .5rem;
}
.backup-empty-state p {
color: var(--text-2);
font-size: .9rem;
line-height: 1.6;
}
.schedule-card {
background: var(--bg-1);
border-radius: var(--radius);
padding: 1.25rem;
border: 1px solid var(--line);
margin-bottom: 1.5rem;
}
.schedule-card h3 {
margin-bottom: .75rem;
}
.schedule-rows {
margin-bottom: 1rem;
}
.schedule-row {
display: flex;
align-items: center;
gap: 1rem;
padding: .4rem 0;
font-size: .85rem;
border-bottom: 1px solid var(--line-soft);
}
.schedule-row:last-child { border-bottom: none; }
.schedule-task {
color: var(--text-1);
font-weight: 500;
min-width: 160px;
}
.schedule-time {
color: var(--blue-bright);
font-family: var(--font-data);
font-size: .8rem;
min-width: 80px;
}
.schedule-next {
color: var(--text-2);
font-size: .8rem;
}
.schedule-summary {
border-top: 1px solid var(--line);
padding-top: .75rem;
margin-bottom: .75rem;
}
.schedule-summary-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: .25rem 0;
font-size: .85rem;
color: var(--text-2);
}
.schedule-summary-value {
color: var(--text-1);
font-family: var(--font-data);
font-size: .8rem;
}
.schedule-actions {
padding-top: .5rem;
}
.backup-section-card {
background: var(--bg-1);
border-radius: var(--radius);
padding: 1.25rem;
border: 1px solid var(--line);
margin-bottom: 1.5rem;
}
.backup-section-card h3 {
margin-bottom: .75rem;
}
.backup-table-wrap {
overflow-x: auto;
}
.db-table, .snapshot-table {
width: 100%;
border-collapse: collapse;
font-size: .85rem;
}
.db-table th, .snapshot-table th {
text-align: left;
padding: .5rem .75rem;
color: var(--text-3);
font-size: .75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .5px;
border-bottom: 1px solid var(--line);
}
.db-table td, .snapshot-table td {
padding: .5rem .75rem;
color: var(--text-1);
border-bottom: 1px solid var(--line-soft);
}
.db-table tr:nth-child(even), .snapshot-table tr:nth-child(even) {
background: var(--bg-2);
}
.db-table tr:nth-child(odd), .snapshot-table tr:nth-child(odd) {
background: var(--bg-1);
}
.db-table td.mono, .snapshot-table td.mono {
font-family: var(--font-data);
font-size: .8rem;
}
.db-type-badge {
display: inline-block;
padding: .1rem .5rem;
border-radius: var(--radius);
font-size: .75rem;
font-weight: 500;
}
.db-type-postgres {
background: var(--blue-dim);
color: var(--blue-bright);
}
.db-type-mariadb {
background: var(--warn-dim);
color: var(--warn);
}
.validation-badge {
display: inline-block;
padding: .1rem .5rem;
border-radius: var(--radius);
font-size: .75rem;
font-weight: 500;
}
.validation-ok {
background: var(--blue-dim);
color: var(--blue);
}
.validation-fail {
background: var(--crit-dim);
color: var(--crit);
cursor: help;
}
.validation-na {
color: var(--text-3);
}
.col-subtitle {
font-size: .7rem;
font-weight: 400;
color: var(--text-3);
text-transform: none;
letter-spacing: 0;
margin-left: .25rem;
}
.snapshot-footer {
padding: .75rem .75rem 0;
font-size: .8rem;
color: var(--text-2);
}
.backup-table-empty {
padding: 1.5rem;
text-align: center;
color: var(--text-3);
font-size: .85rem;
}
.repo-card {
background: var(--bg-1);
border-radius: var(--radius);
padding: 1.25rem;
border: 1px solid var(--line);
margin-bottom: 1.5rem;
}
.repo-card h3 {
margin-bottom: .75rem;
}
.repo-info-rows {
margin-bottom: 1rem;
}
.repo-info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: .3rem 0;
font-size: .85rem;
}
.repo-label {
color: var(--text-2);
}
.repo-value {
color: var(--text-1);
}
.repo-value.mono {
font-family: var(--font-data);
font-size: .8rem;
}
.repo-paths {
border-top: 1px solid var(--line);
padding-top: .75rem;
margin-bottom: .75rem;
}
.repo-path-list {
list-style: disc;
padding-left: 1.5rem;
margin-top: .5rem;
}
.repo-path-list li {
font-family: var(--font-data);
font-size: .8rem;
color: var(--text-2);
padding: .15rem 0;
}
.repo-remote {
border-top: 1px solid var(--line);
padding-top: .75rem;
}
.repo-tier {
border-top: 1px solid var(--line);
padding-top: .75rem;
margin-top: .75rem;
}
.repo-tier:first-child {
border-top: none;
padding-top: 0;
margin-top: 0;
}
.repo-tier-title {
font-size: .85rem;
font-weight: 600;
color: var(--text-2);
margin-bottom: .5rem;
text-transform: uppercase;
letter-spacing: .3px;
}
.repo-remote-status {
margin-top: .25rem;
display: flex;
flex-direction: column;
gap: .15rem;
}
/* Details tier (collapsible sections in Részletek) */
.details-tier {
border-top: 1px solid var(--line);
margin-top: .5rem;
}
.details-tier:first-child {
border-top: none;
margin-top: 0;
}
.details-tier-header {
display: flex;
align-items: center;
gap: .5rem;
cursor: pointer;
padding: .5rem 0;
user-select: none;
}
.details-tier-header:hover {
opacity: .8;
}
.details-tier-header .expand-icon {
font-size: .7rem;
color: var(--text-3);
min-width: 1rem;
text-align: center;
}
.details-tier-header .repo-tier-title {
margin-bottom: 0;
}
.details-tier-body {
padding-bottom: .75rem;
}
.drive-detail-card {
background: var(--bg-2);
border-radius: var(--radius);
padding: .75rem;
margin-bottom: .5rem;
}
.drive-detail-header {
font-size: .85rem;
font-weight: 600;
color: var(--text-1);
margin-bottom: .5rem;
}
.method-group {
margin-top: .5rem;
}
.method-group-label {
font-size: .8rem;
font-weight: 600;
color: var(--text-2);
margin-bottom: .25rem;
}
.tier-empty-state {
font-size: .85rem;
color: var(--text-3);
padding: .5rem 0;
}
.relative-time {
color: var(--text-3);
font-size: .8rem;
}
/* --- Monitoring page --- */
.monitor-card {
background: var(--bg-1);
border-radius: var(--radius);
padding: 1.25rem;
border: 1px solid var(--line);
margin-bottom: 1.5rem;
}
.monitor-card h3 {
margin-bottom: .75rem;
}
.monitoring-banner {
padding: .75rem 1rem;
border-radius: var(--radius);
font-size: .9rem;
border: 1px solid;
}
.monitoring-banner-green {
background: var(--blue-dim);
color: var(--blue);
border-color: var(--blue-dim);
}
.monitoring-banner-yellow {
background: var(--warn-dim);
color: var(--warn);
border-color: var(--warn-dim);
}
.monitoring-banner-red {
background: var(--warn-dim);
color: var(--warn);
border-color: var(--warn-dim);
}
.monitoring-banner-warn {
background: var(--warn-dim);
border-left: 4px solid var(--warn);
color: var(--warn);
}
.ping-status-ok { color: var(--blue); }
.ping-status-warn { color: var(--warn); }
.ping-schedule {
color: var(--text-3);
font-size: .85rem;
margin-left: .75rem;
}
.monitor-card-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.monitor-card-header h3 {
margin-bottom: 0;
flex: 1;
min-width: 150px;
}
.time-range-bar {
display: flex;
gap: .35rem;
}
.sysinfo-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0;
}
.sysinfo-row {
display: flex;
justify-content: space-between;
align-items: baseline;
padding: .5rem .75rem;
border-bottom: 1px solid var(--line-soft);
font-size: .9rem;
gap: 1rem;
}
.sysinfo-row:last-child { border-bottom: none; }
.sysinfo-label {
color: var(--text-2);
font-weight: 500;
white-space: nowrap;
flex-shrink: 0;
}
.sysinfo-value {
color: var(--text-1);
font-family: var(--font-data);
font-size: .85rem;
font-weight: 600;
text-align: right;
word-break: break-word;
}
.charts-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.charts-grid-2 {
grid-template-columns: 1fr 1fr;
}
.chart-box {
background: var(--bg-2);
border-radius: var(--radius);
padding: .75rem;
border: 1px solid var(--line-soft);
min-width: 0;
overflow: hidden;
}
.chart-box-half {
flex: 1;
min-width: 0;
}
.chart-title {
font-size: .8rem;
font-weight: 600;
color: var(--text-2);
margin-bottom: .5rem;
text-transform: uppercase;
letter-spacing: .3px;
}
.chart-wrap {
position: relative;
height: 180px;
overflow: hidden;
max-width: 100%;
}
.chart-wrap canvas {
max-width: 100%;
}
.chart-wrap-bar {
position: relative;
height: 250px;
overflow: hidden;
max-width: 100%;
}
.container-charts-row {
display: flex;
gap: 1rem;
}
.chart-empty {
text-align: center;
padding: 2rem;
color: var(--text-3);
font-size: .9rem;
}
.storage-bars {
display: flex;
flex-direction: column;
gap: 1rem;
}
.storage-item {
padding: .25rem 0;
}
.storage-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: .4rem;
}
.storage-label {
font-size: .85rem;
font-weight: 600;
color: var(--text-2);
}
.storage-value {
font-size: .8rem;
color: var(--text-3);
font-family: var(--font-data);
}
/* --- Settings page --- */
.settings-card {
background: var(--bg-1);
border-radius: var(--radius);
padding: 1.5rem;
border: 1px solid var(--line);
margin-bottom: 1.5rem;
}
.settings-card h3 {
margin-bottom: .5rem;
}
.settings-card-desc {
color: var(--text-2);
font-size: .85rem;
margin-bottom: 1rem;
}
.settings-grid {
display: flex;
flex-direction: column;
}
.settings-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: .5rem .75rem;
border-bottom: 1px solid var(--line-soft);
font-size: .9rem;
gap: 1rem;
}
.settings-row:last-child { border-bottom: none; }
.settings-label {
color: var(--text-2);
font-weight: 500;
white-space: nowrap;
flex-shrink: 0;
}
.settings-value {
color: var(--text-1);
text-align: right;
word-break: break-word;
}
.settings-value.mono {
font-family: var(--font-data);
font-size: .85rem;
}
.settings-value-truncate {
max-width: 400px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Sidebar bottom section */
.sidebar-bottom {
margin-top: auto;
border-top: 1px solid var(--line);
}
/* --- Backup page: Storage overview grid --- */
.storage-overview-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
align-items: start;
}
.storage-stats {
display: flex;
flex-direction: column;
gap: .5rem;
}
.storage-stat-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: .3rem 0;
font-size: .85rem;
}
.storage-stat-label {
color: var(--text-2);
}
.storage-stat-value {
color: var(--text-1);
}
/* --- Backup page: App backup status (read-only) --- */
.backup-section-desc {
color: var(--text-2);
font-size: .85rem;
margin-bottom: 1rem;
}
.app-backup-list {
display: flex;
flex-direction: column;
gap: .5rem;
margin-bottom: 1rem;
}
.app-backup-item {
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: .75rem 1rem;
}
.app-backup-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.app-backup-name-link {
font-weight: 500;
font-size: .9rem;
color: var(--text-1);
text-decoration: none;
}
.app-backup-name-link:hover {
color: var(--blue);
text-decoration: underline;
}
.app-backup-status-row {
display: flex;
align-items: center;
gap: .5rem;
}
.app-backup-size {
font-size: .8rem;
color: var(--text-3);
}
.app-backup-status {
font-size: .8rem;
font-weight: 500;
display: inline-flex;
align-items: center;
gap: .35rem;
}
.app-backup-status::before {
content: '';
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.app-backup-active {
color: var(--blue);
}
.app-backup-active::before {
background: var(--blue);
}
.app-backup-inactive {
color: var(--text-3);
}
.app-backup-inactive::before {
border: 1px solid var(--text-3);
background: transparent;
width: 6px;
height: 6px;
}
.app-backup-na {
color: var(--text-3);
}
.app-backup-na::before {
content: '—';
width: auto;
height: auto;
border-radius: 0;
background: none;
border: none;
font-size: .8rem;
}
/* --- Backup page: Encryption key --- */
.repo-encryption {
border-top: 1px solid var(--line);
padding-top: .75rem;
margin-bottom: .75rem;
}
.repo-encryption-row {
display: flex;
align-items: center;
gap: .5rem;
margin-top: .5rem;
margin-bottom: .5rem;
}
.restic-pw-field {
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: .4rem .6rem;
color: var(--text-1);
font-size: .85rem;
width: 340px;
max-width: 100%;
}
.repo-encryption-warn {
font-size: .8rem;
color: var(--warn);
line-height: 1.4;
}
/* --- Backup page: Restore type info banners --- */
.restore-info {
padding: 0.5rem 0.75rem;
border-radius: var(--radius);
font-size: 0.85rem;
background: var(--blue-dim);
border: 1px solid var(--blue-dim);
color: var(--blue-bright);
}
.restore-info-partial {
background: var(--warn-dim);
border-color: var(--warn-dim);
color: var(--warn);
}
/* --- Backup page: Restore section --- */
.restore-section {
display: flex;
flex-direction: column;
gap: .75rem;
}
.restore-form-row {
display: flex;
align-items: center;
gap: .75rem;
}
.restore-label {
font-size: .85rem;
color: var(--text-2);
min-width: 110px;
}
.restore-select {
flex: 1;
max-width: 400px;
padding: .4rem .6rem;
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: var(--radius);
color: var(--text-1);
font-size: .85rem;
font-family: inherit;
}
.restore-select option {
background: var(--bg-2);
color: var(--text-1);
}
.restore-paths {
padding: .5rem 0;
}
.restore-warning {
background: var(--crit-dim);
border: 1px solid var(--crit-dim);
border-radius: var(--radius);
padding: .75rem 1rem;
font-size: .85rem;
color: var(--crit);
line-height: 1.5;
}
.restore-confirm {
font-size: .85rem;
color: var(--text-2);
}
.restore-confirm label {
display: flex;
align-items: center;
gap: .5rem;
cursor: pointer;
}
.restore-confirm input[type="checkbox"] {
accent-color: var(--crit);
}
.restore-actions {
padding-top: .25rem;
}
/* --- Flash messages --- */
.flash {
padding: .75rem 1rem;
border-radius: var(--radius);
margin-bottom: 1rem;
font-size: .85rem;
border: 1px solid;
}
.flash-success {
background: var(--blue-dim);
color: var(--blue);
border-color: var(--blue-dim);
}
.flash-error {
background: var(--crit-dim);
color: var(--crit);
border-color: var(--crit-dim);
}
/* --- Settings page: Storage paths --- */
.storage-paths-list {
display: flex;
flex-direction: column;
gap: .75rem;
}
.storage-path-item {
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 1rem;
}
.storage-path-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 1rem;
margin-bottom: .5rem;
}
.storage-path-info {
display: flex;
flex-direction: column;
gap: .15rem;
}
.storage-path-label {
font-weight: 600;
font-size: .95rem;
}
.storage-path-path {
font-family: var(--font-data);
font-size: .8rem;
color: var(--text-3);
}
.storage-path-badges {
display: flex;
gap: .35rem;
flex-shrink: 0;
flex-wrap: wrap;
}
.storage-path-details {
margin: .5rem 0;
}
.storage-path-disk {
margin-bottom: .5rem;
}
.storage-path-fsinfo {
font-size: .75rem;
margin-bottom: .35rem;
}
.storage-path-meta {
font-size: .8rem;
color: var(--text-3);
}
.storage-path-actions {
display: flex;
gap: .5rem;
margin-top: .75rem;
flex-wrap: wrap;
}
.btn-xs {
padding: .2rem .5rem;
font-size: .75rem;
border-radius: var(--radius);
}
.btn-ghost {
background: transparent;
border: none;
color: var(--text-3);
padding: .1rem .3rem;
}
.btn-ghost:hover {
color: var(--blue-bright);
}
.btn-danger-outline {
background: transparent;
border: 1px solid var(--crit-dim);
color: var(--crit);
}
.btn-danger-outline:hover {
background: var(--crit-dim);
border-color: var(--crit);
}
.storage-app-details summary {
font-size: .85rem;
}
.storage-app-list {
margin-top: .5rem;
display: flex;
flex-direction: column;
gap: .25rem;
}
.storage-app-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: .2rem .5rem;
font-size: .85rem;
}
.storage-app-link {
color: var(--blue-bright);
text-decoration: none;
}
.storage-app-link:hover {
text-decoration: underline;
}
/* Badge variants */
.badge-warn {
background: var(--warn-dim);
color: var(--warn);
}
.badge-error {
background: var(--crit-dim);
color: var(--crit);
}
/* Disconnected storage path card */
.storage-disconnected {
opacity: 0.75;
border-style: dashed;
}
.storage-decommissioned {
opacity: 0.6;
border-color: var(--line);
}
.storage-disconnected .storage-disconnected-info,
.storage-decommissioned .storage-disconnected-info {
display: flex;
flex-direction: column;
gap: .25rem;
}
.storage-stopped-apps-info {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: .25rem;
margin-bottom: .5rem;
}
/* Disconnected/unknown metric — empty track, no fill, no color (v2). */
.meter-empty .meter-fill { display: none; }
/* Task progress bar (storage init — not disk usage zone gradient) */
.progress-bar-task {
height: 8px;
background: var(--line);
border-radius: var(--radius);
overflow: hidden;
width: 100%;
}
.progress-bar-task .progress-fill {
height: 100%;
background: var(--blue);
border-radius: var(--radius);
transition: width 0.3s ease;
}
.storage-path-label-wrap {
display: flex;
align-items: center;
gap: .25rem;
}
.storage-add-details {
margin-top: .5rem;
}
.storage-add-details[open] summary {
margin-bottom: 1rem;
}
.storage-add-form {
margin-top: .75rem;
}
/* Responsive */
@media(max-width: 768px) {
.sidebar { width: 100%; height: auto; position: relative; border-right: none; border-bottom: 1px solid var(--line); }
.nav-links { display: flex; padding: 0; overflow-x: auto; }
.nav-links a { padding: .5rem 1rem; white-space: nowrap; }
.nav-links a.active { border-left: none; border-bottom: 2px solid var(--blue); }
.content { margin-left: 0; padding: 1rem; }
body { flex-direction: column; }
.app-row { flex-wrap: wrap; }
.app-row-actions { width: 100%; justify-content: flex-end; }
.stack-grid { grid-template-columns: 1fr; }
.stats-grid { grid-template-columns: repeat(3, 1fr); }
.deploy-info { flex-direction: column; }
.system-info-items { flex-direction: column; gap: 1rem; }
.charts-grid { grid-template-columns: 1fr; }
.container-charts-row { flex-direction: column; }
.sysinfo-grid { grid-template-columns: 1fr; }
.storage-overview-grid { grid-template-columns: 1fr; }
.restore-form-row { flex-direction: column; align-items: stretch; }
.restore-label { min-width: auto; }
.restore-select { max-width: 100%; }
}
/* ===================================================================
Storage Init Wizard & Migration UI
=================================================================== */
.disk-progress-steps {
display: flex;
flex-direction: column;
gap: .5rem;
margin-top: 1rem;
}
.disk-step {
display: flex;
align-items: center;
gap: .75rem;
padding: .5rem .75rem;
border-radius: var(--radius);
color: var(--text-2);
font-size: .9rem;
background: var(--bg-0);
}
.disk-step-icon {
font-size: 1.1rem;
min-width: 1.4rem;
}
.disk-step.disk-step-done {
color: var(--text-1);
}
.disk-step.disk-step-active {
background: var(--blue-dim);
color: var(--blue-bright);
border: 1px solid var(--blue-dim);
}
.disk-progress-bar-wrap {
display: flex;
align-items: center;
gap: 1rem;
}
.disk-progress-bar-wrap .meter { flex: 1; }
.deploy-storage-info {
background: var(--bg-0);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 1.25rem;
margin-bottom: 1rem;
}
.deploy-storage-info h4 {
font-size: .95rem;
font-weight: 600;
color: var(--text-2);
text-transform: uppercase;
letter-spacing: .05em;
margin-bottom: .75rem;
}
.storage-app-row {
display: flex;
align-items: center;
gap: .75rem;
padding: .35rem 0;
}
.storage-app-link {
color: var(--blue-bright);
text-decoration: none;
font-size: .9rem;
}
.storage-app-link:hover {
text-decoration: underline;
}
/* Stale data cleanup */
.deploy-stale-data {
background: var(--bg-1);
border: 1px solid var(--warn);
border-radius: var(--radius);
padding: 1.5rem;
margin-top: 1rem;
margin-bottom: 1.5rem;
}
.deploy-stale-data h4 {
margin: 0 0 0.5rem 0;
color: var(--warn);
}
.stale-data-item {
padding: 1rem;
background: var(--warn-dim);
border-radius: var(--radius);
margin-bottom: 0.75rem;
}
.stale-data-item:last-child {
margin-bottom: 0;
}
/* Red-filled danger buttons inside modal confirmation dialogs */
.modal-actions .btn-danger {
background: var(--crit);
color: white;
border-color: var(--crit);
}
.modal-actions .btn-danger:hover {
opacity: 0.85;
background: var(--crit);
color: white;
}
/* Cross-drive backup card on deploy page */
.deploy-cross-drive {
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 1.5rem;
margin-top: 1rem;
margin-bottom: 1.5rem;
}
.deploy-cross-drive h4 {
margin: 0 0 1rem 0;
color: var(--text-1);
}
.cross-drive-nightly {
margin-bottom: .5rem;
}
/* Cross-drive list on backup page */
.cross-drive-list {
display: flex;
flex-direction: column;
gap: .5rem;
}
.cross-drive-item {
background: var(--bg-2);
border-radius: var(--radius);
padding: .75rem 1rem;
}
.cross-drive-header {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: .5rem;
}
.cross-drive-name {
font-weight: 500;
color: var(--text-1);
text-decoration: none;
}
.cross-drive-name:hover {
text-decoration: underline;
color: var(--blue);
}
.cross-drive-meta {
display: flex;
align-items: center;
gap: .4rem;
flex-wrap: wrap;
}
.cross-drive-actions {
display: flex;
gap: .5rem;
flex-wrap: wrap;
}
/* Info tooltip (i icon with hover popup) */
.info-tooltip {
position: relative;
display: inline-flex;
align-items: center;
margin-left: .35rem;
cursor: help;
}
.info-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
border-radius: 50%;
border: 1px solid var(--text-3);
color: var(--text-3);
font-size: .65rem;
font-weight: 700;
font-style: italic;
font-family: Georgia, serif;
line-height: 1;
}
.info-tooltip-text {
display: none;
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
width: 320px;
padding: .75rem 1rem;
background: var(--bg-0);
border: 1px solid var(--line);
border-radius: var(--radius);
font-size: .8rem;
font-weight: 400;
line-height: 1.5;
color: var(--text-2);
z-index: 100;
white-space: normal;
}
.info-tooltip:hover .info-tooltip-text,
.info-tooltip:focus .info-tooltip-text,
.info-tooltip:focus-within .info-tooltip-text {
display: block;
}
.info-tooltip-text::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: var(--line);
}
/* Nightly backup status indicator (non-interactive) */
.cross-drive-nightly-status {
display: flex;
align-items: center;
gap: .5rem;
}
.nightly-status-indicator {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
}
.nightly-enabled {
background: var(--blue);
}
.nightly-disabled {
background: var(--text-3);
opacity: 0.5;
}
/* meta-badge-fail */
.meta-badge-fail {
background: var(--crit-dim);
color: var(--crit);
}
/* ─── Unified App Backup Rows ──────────────────────────────────────── */
.app-backup-row {
border: 1px solid var(--line);
border-radius: var(--radius);
margin-bottom: .5rem;
background: var(--bg-2);
overflow: hidden;
}
.app-backup-row[data-status="red"] { border-left: 3px solid var(--crit); }
.app-backup-row[data-status="yellow"] { border-left: 3px solid var(--warn); }
.app-backup-row[data-status="green"] { border-left: 3px solid var(--blue); }
.app-backup-row[data-status="auto"] { border-left: 3px solid var(--text-3); }
.app-backup-row-header {
display: flex;
align-items: center;
gap: .75rem;
padding: .5rem 1rem;
min-height: 44px; /* same compact height as the shared .app-row */
cursor: pointer;
user-select: none;
}
.app-backup-row-header:hover {
background: var(--bg-hover, rgba(255,255,255,0.03));
}
.app-backup-row-name {
font-weight: 500;
font-size: .9rem;
flex: 1;
}
.app-backup-row-meta {
display: flex;
align-items: center;
gap: .5rem;
}
.expand-icon {
color: var(--text-3);
font-size: .75rem;
flex-shrink: 0;
}
/* Status dot */
.status-dot {
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
display: inline-block;
}
.status-dot.status-green { background: var(--blue); }
.status-dot.status-yellow { background: var(--warn); }
.status-dot.status-red { background: var(--crit); }
.status-dot.status-auto { background: var(--text-3); opacity: .6; }
/* Expanded detail */
.app-backup-row-detail {
border-top: 1px solid var(--line);
padding: .75rem 1rem;
}
.backup-layers {
display: flex;
flex-direction: column;
gap: .4rem;
}
.backup-layer-row {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: .4rem;
font-size: .85rem;
padding: .2rem 0;
}
.backup-layer-row.layer-row-na {
opacity: .55;
}
.layer-label {
font-weight: 500;
min-width: 14rem;
color: var(--text-2);
}
.layer-badge {
background: var(--blue-dim);
color: var(--blue-bright);
padding: .1rem .45rem;
border-radius: var(--radius);
font-size: .75rem;
}
.layer-na {
color: var(--text-3);
font-style: italic;
font-size: .82rem;
}
.layer-method {
color: var(--text-2);
font-size: .82rem;
}
.layer-dest {
color: var(--text-2);
font-size: .82rem;
}
.layer-schedule {
color: var(--text-3);
font-size: .8rem;
}
.layer-last {
color: var(--text-3);
font-size: .8rem;
margin-left: .25rem;
}
.layer-auto-ok {
color: var(--blue);
font-size: .85rem;
margin-right: .5rem;
}
.layer-unconfigured {
color: var(--warn);
font-weight: 500;
font-size: .85rem;
}
.layer-actions {
display: flex;
gap: .35rem;
margin-left: auto;
}
.btn-xs {
padding: .15rem .5rem;
font-size: .75rem;
border-radius: var(--radius);
}
.layer-warnings {
margin-top: .5rem;
padding-top: .5rem;
border-top: 1px solid var(--line);
}
.backup-layer-warning {
font-size: .82rem;
color: var(--warn);
padding: .2rem 0;
}
.text-ok { color: var(--blue); }
.text-error { color: var(--crit); }
.text-muted { color: var(--text-3); }
.tier-label {
font-weight: 600;
min-width: 5rem;
color: var(--text-1);
}
.tier-location {
color: var(--text-3);
font-size: .85rem;
}
.tier-contents {
color: var(--text-3);
font-size: .8rem;
font-style: italic;
margin-left: .25rem;
}
.tier-size {
color: var(--text-3);
font-size: .8rem;
margin-left: .25rem;
}
.tier-browsable {
font-size: .75rem;
margin-left: .15rem;
cursor: help;
}
/* ── Debug page ────────────────────────────────────────── */
.debug-banner {
background: var(--warn-dim);
border: 1px solid var(--warn-dim);
border-radius: var(--radius);
padding: 0.75rem 1rem;
margin-bottom: 1.5rem;
color: var(--warn);
font-size: 0.85rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.debug-banner strong { color: var(--warn); }
.debug-section {
background: var(--bg-1);
border: 1px solid var(--line);
border-radius: var(--radius);
margin-bottom: 0.75rem;
overflow: hidden;
}
.debug-section-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem 1rem;
cursor: pointer;
user-select: none;
transition: background .15s;
}
.debug-section-header:hover { background: rgba(255,255,255,0.03); }
.debug-section-header h3 {
font-size: 0.95rem;
font-weight: 600;
color: var(--text-1);
margin: 0;
}
.debug-section-toggle {
color: var(--text-3);
font-size: 0.8rem;
transition: transform .2s;
}
.debug-section.open .debug-section-toggle { transform: rotate(180deg); }
.debug-section-body {
display: none;
padding: 0 1rem 1rem;
border-top: 1px solid var(--line);
}
.debug-section.open .debug-section-body { display: block; }
.debug-actions {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-bottom: 0.75rem;
}
.debug-actions .btn { font-size: 0.8rem; padding: 0.35rem 0.75rem; }
.debug-result {
font-size: 0.8rem;
padding: 0.5rem 0.75rem;
border-radius: var(--radius);
margin-top: 0.5rem;
font-family: var(--font-data);
}
.debug-result-ok {
color: var(--blue);
background: var(--blue-dim);
border: 1px solid var(--blue-dim);
}
.debug-result-error {
color: var(--crit);
background: var(--crit-dim);
border: 1px solid var(--crit-dim);
}
.debug-result-pending {
color: var(--warn);
background: var(--warn-dim);
border: 1px solid var(--warn-dim);
}
.debug-kv-grid {
display: grid;
grid-template-columns: auto 1fr;
gap: 0.25rem 1rem;
font-size: 0.85rem;
margin-bottom: 0.75rem;
}
.debug-kv-grid dt { color: var(--text-3); font-weight: 500; white-space: nowrap; }
.debug-kv-grid dd { color: var(--text-1); margin: 0; }
.debug-table {
width: 100%;
border-collapse: collapse;
font-size: 0.8rem;
margin-bottom: 0.75rem;
}
.debug-table th {
text-align: left;
color: var(--text-3);
font-weight: 600;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 0.35rem 0.5rem;
border-bottom: 1px solid var(--line);
}
.debug-table td {
padding: 0.35rem 0.5rem;
color: var(--text-1);
border-bottom: 1px solid var(--line-soft);
}
.debug-table tr:last-child td { border-bottom: none; }
.debug-log-viewer {
background: var(--bg-0);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 0.5rem;
max-height: 400px;
overflow-y: auto;
font-family: var(--font-data);
font-size: 0.75rem;
line-height: 1.5;
}
.debug-log-entry { white-space: pre-wrap; word-break: break-all; }
.debug-log-debug { color: var(--text-3); }
.debug-log-info { color: var(--text-1); }
.debug-log-warn { color: var(--warn); }
.debug-log-error { color: var(--crit); }
.debug-log-controls {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.5rem;
flex-wrap: wrap;
}
.debug-log-controls .btn {
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
}
.debug-log-controls .btn.active {
background: var(--blue);
color: #fff;
}
.debug-log-count {
font-size: 0.75rem;
color: var(--text-3);
margin-left: auto;
}
.debug-confirm-input {
background: var(--bg-0);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 0.4rem 0.75rem;
color: var(--text-1);
font-family: var(--font-data);
font-size: 0.85rem;
width: 120px;
}
.debug-confirm-input:focus {
outline: none;
border-color: var(--crit);
}
/* Debug page: red-filled danger buttons for destructive debug actions */
.debug-section .btn-danger {
background: var(--crit);
color: #fff;
border: none;
border-radius: var(--radius);
padding: 0.4rem 0.85rem;
cursor: pointer;
font-size: 0.85rem;
font-weight: 600;
transition: background .15s, opacity .15s;
}
.debug-section .btn-danger:hover { background: var(--crit); }
.debug-section .btn-danger:disabled { opacity: 0.5; cursor: not-allowed; }
.debug-spinner {
display: inline-block;
width: 14px;
height: 14px;
border: 2px solid var(--text-3);
border-top-color: var(--blue);
border-radius: 50%;
animation: debug-spin 0.6s linear infinite;
vertical-align: middle;
margin-right: 0.3rem;
}
@keyframes debug-spin { to { transform: rotate(360deg); } }
/* --- Geo-restriction UI --- */
.geo-country-selector { position: relative; }
.geo-selected-tags { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.5rem; }
.geo-tag {
display: inline-flex; align-items: center; gap: 0.3rem;
background: var(--blue-dim); color: var(--blue-bright);
padding: 0.25rem 0.55rem; border-radius: var(--radius); font-size: 0.85rem;
white-space: nowrap;
}
.geo-tag-hu { background: var(--blue-dim); color: var(--blue); }
.geo-tag-sm { font-size: 0.75rem; padding: 0.15rem 0.4rem; }
.geo-tag-remove { cursor: pointer; opacity: 0.7; font-size: 1.1em; line-height: 1; }
.geo-tag-remove:hover { opacity: 1; }
.geo-country-list {
position: absolute; z-index: 10; background: var(--bg-1);
border: 1px solid var(--line); border-radius: var(--radius);
max-height: 220px; overflow-y: auto; width: 100%; margin-top: 2px;
display: none;
}
.geo-country-option {
padding: 0.4rem 0.75rem; cursor: pointer; font-size: 0.9rem;
color: var(--text-1);
}
.geo-country-option:hover { background: var(--blue-dim); }
.geo-country-option small { color: var(--text-3); }
.geo-app-override-row {
display: flex; align-items: center; gap: 0.5rem;
padding: 0.5rem 0; border-bottom: 1px solid var(--line);
flex-wrap: wrap;
}
.geo-app-override-row strong { min-width: 120px; color: var(--text-1); }
.geo-edit-overlay {
background: var(--bg-2); border: 1px solid var(--line);
border-radius: var(--radius); padding: 1rem; margin-top: 0.5rem;
}
.geo-edit-grid {
display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 0.2rem; max-height: 250px; overflow-y: auto; margin-top: 0.5rem;
}
.geo-edit-item { font-size: 0.85rem; cursor: pointer; padding: 0.15rem 0; color: var(--text-1); }
.geo-edit-item input { margin-right: 0.3rem; }
.btn-danger-outline {
background: transparent; color: var(--crit); border: 1px solid var(--crit);
border-radius: var(--radius); padding: 0.2rem 0.5rem; cursor: pointer; font-size: 0.8rem;
}
.btn-danger-outline:hover { background: var(--crit-dim); }
/* ============================================================================
Agent drive lists (overview + init/attach selector) — storage authz redesign.
Reuses the existing card/badge/bar tokens; no new design system.
============================================================================ */
.drive-list { display: flex; flex-direction: column; gap: .6rem; margin-top: .25rem; }
.drive-card {
background: var(--bg-1);
border: 1px solid var(--line);
border-left: 4px solid var(--text-3);
border-radius: var(--radius);
padding: .85rem 1rem;
display: flex; flex-direction: column; gap: .55rem;
}
.drive-card.role-user-data { border-left-color: var(--blue); }
.drive-card.role-system,
.drive-card.role-backup { border-left-color: var(--warn); }
.drive-card.is-selectable { cursor: pointer; transition: border-color .15s, background .15s; }
.drive-card.is-selectable:hover { border-color: var(--blue-bright); }
.drive-card.is-picked { border-color: var(--blue-bright); background: var(--blue-dim); }
.drive-card-top { display: flex; align-items: flex-start; justify-content: space-between; gap: .75rem; }
.drive-id { display: flex; flex-direction: column; gap: .15rem; min-width: 0; }
.drive-name { font-size: .95rem; font-weight: 600; color: var(--text-1); }
.drive-sub {
font-size: .78rem; color: var(--text-3);
font-family: var(--font-data); word-break: break-all;
}
.drive-badges { display: flex; flex-wrap: wrap; gap: .35rem; align-items: center; justify-content: flex-end; }
.drive-cap { display: flex; flex-direction: column; gap: .3rem; }
.drive-cap .meter-track { height: 3px; }
.drive-cap-label { font-size: .75rem; color: var(--text-3); font-family: var(--font-data); }
.drive-actions { display: flex; flex-wrap: wrap; gap: .4rem; }
.drive-select { display: flex; align-items: center; gap: .5rem; }
/* badges missing from the global sheet */
.badge-ok { background: var(--blue-dim); color: var(--blue); }
.badge-neutral { background: var(--bg-2); color: var(--text-2); }
.badge-muted { background: var(--bg-2); color: var(--text-3); }
.badge-info { background: var(--blue-dim); color: var(--blue-bright); }
/* Backup-page tier divider between the whole-guest section and the per-app section. */
.backup-tier-divider {
margin: 2rem 0 0.25rem; padding-top: 1.25rem;
border-top: 1px solid var(--line); font-size: 1.05rem;
}
/* Per-card storage purpose description + the tiering one-liner above the drive list. */
.drive-purpose { font-size: .8rem; color: var(--text-2); line-height: 1.4; }
.drive-tiering-note {
font-size: .8rem; color: var(--text-2); line-height: 1.4;
margin: 0 0 .75rem; padding: .5rem .75rem;
background: var(--blue-dim); border-left: 3px solid var(--blue); border-radius: var(--radius);
}
span.mono, .mono { font-family: var(--font-data); }
/* Type-to-confirm modal (destructive user-data eject/wipe) */
.confirm-overlay {
position: fixed; inset: 0; background: rgba(0,0,0,.6);
display: flex; align-items: center; justify-content: center; z-index: 1000; padding: 1rem;
}
.confirm-box {
background: var(--bg-1); border: 1px solid var(--line); border-radius: var(--radius);
max-width: 480px; width: 100%; padding: 1.4rem;
}
.confirm-box h3 { margin: 0 0 .75rem; font-size: 1.05rem; }
.confirm-box .confirm-apps { margin: .5rem 0; padding-left: 1.1rem; }
.confirm-box .confirm-apps li { margin: .15rem 0; }
.confirm-box .confirm-input { margin: .9rem 0 .4rem; }
.confirm-box .form-actions { display: flex; gap: .6rem; margin-top: 1rem; }
/* ===================================================================
Design system v2 primitives (TASK-D0)
=================================================================== */
/* Panel — flat surface: bg-1, hairline border, 2px radius, no shadow. */
.panel {
background: var(--bg-1);
border: 1px solid var(--line);
border-radius: var(--radius);
}
/* Boxless section header: h3 + right-aligned muted hint over a hairline. */
.section-h {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
border-bottom: 1px solid var(--line);
padding-bottom: .5rem;
margin: 1.75rem 0 1rem;
}
.section-h h3 { margin-bottom: 0; }
.section-h .hint {
font-size: .78rem;
color: var(--text-3);
}
/* List — one panel, rows separated by soft hairlines, 2px left state edge. */
.list {
background: var(--bg-1);
border: 1px solid var(--line);
border-radius: var(--radius);
}
.list .row {
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
padding: .85rem 1.25rem;
border-top: 1px solid var(--line-soft);
}
.list .row:first-child { border-top: none; }
.list .row::before {
content: '';
position: absolute;
left: 0; top: 0; bottom: 0;
width: 2px;
background: transparent;
}
.list .row.state-run::before,
.list .row.state-progress::before { background: var(--blue); }
.list .row.state-warn::before { background: var(--warn); }
/* Spinning progress icon (deploy steps, async tasks) */
.ico-spin { animation: ico-spin 1s linear infinite; }
@keyframes ico-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .ico-spin { animation: none; } }
/* Login page — two-tone heading (H1 last word in blue-bright) */
.login-title {
font-size: 1.35rem;
font-weight: 700;
margin-bottom: .25rem;
}
.login-title .title-accent {
color: var(--blue-bright);
}
/* Sidebar settings group (D1) — group label + indented sub-links. */
.nav-group-label {
padding: .75rem 1.5rem .25rem;
font-size: .68rem;
font-weight: 700;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--text-3);
}
.nav-links-sub {
padding: 0 0 .5rem;
flex: none;
}
.nav-links-sub a {
padding: .5rem 1.5rem .5rem 1.75rem;
font-size: .88rem;
}
/* Nested sub-links under a main-nav item (Tárhely → Meghajtók / Hálózati tárhely). */
.nav-links-nested {
padding: 0;
list-style: none;
}
.nav-links-nested a {
padding: .4rem 1.5rem .4rem 3.1rem;
font-size: .85rem;
}
.nav-links-nested a.active {
border-left: 3px solid var(--blue);
}
/* ============================================================================
Nav polish (v0.146.0) — scrollbars + collapsible sidebar groups.
============================================================================ */
/* Scrollbars. The platform default is a light, chunky bar that reads as a bright
stripe against the navy and competes with the content it is scrolling. Thin +
hairline-coloured puts it back in the background where it belongs. Firefox gets
the standard two-value form; WebKit/Blink the pseudo-elements. Both are declared
because neither alone covers the browsers customers actually use. */
* {
scrollbar-width: thin;
scrollbar-color: var(--line) transparent;
}
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
background: var(--line);
border-radius: var(--radius);
}
*::-webkit-scrollbar-thumb:hover { background: var(--text-3); }
/* Track takes the surrounding panel's own background, so the gutter never shows
through as a lighter channel on the two surfaces that actually scroll. */
.sidebar { scrollbar-color: var(--line) var(--bg-2); }
.sidebar::-webkit-scrollbar-track { background: var(--bg-2); }
html { scrollbar-color: var(--line) var(--bg-0); }
html::-webkit-scrollbar-track { background: var(--bg-0); }
/* Collapsible nav groups. The row is a real <button> (keyboard + AT reachable for
free); every group's own landing page is also its first sub-item, so nothing
became unreachable when the header stopped being a link.
The open/close uses grid-template-rows 0fr -> 1fr rather than max-height: it
animates to the content's REAL height, so there is no magic number to drift and
no jump when a group's item count changes. */
.nav-group {
display: grid;
grid-template-rows: auto 0fr;
transition: grid-template-rows .18s ease;
}
.nav-group.is-open { grid-template-rows: auto 1fr; }
.nav-group > .nav-links-nested {
overflow: hidden;
min-height: 0; /* required, or the grid row refuses to collapse below content height */
}
@media (prefers-reduced-motion: reduce) {
.nav-group { transition: none; }
}
/* The toggle inherits the nav link's look exactly — it must not read as a form
control sitting in a list of links. */
.nav-group-toggle {
display: flex;
align-items: center;
gap: .6rem;
width: 100%;
padding: .75rem 1.5rem;
background: none;
border: none;
border-left: 3px solid transparent; /* reserved so .active adds no width shift */
color: var(--text-2);
font-family: inherit;
font-size: .95rem;
font-weight: 500;
text-align: left;
cursor: pointer;
transition: color 0.2s ease, background 0.2s ease;
}
.nav-group-toggle:hover {
color: var(--blue-bright);
background: var(--blue-dim);
}
.nav-group-toggle.active {
color: var(--blue-bright);
background: var(--blue-dim);
border-left-color: var(--blue);
}
.nav-group-toggle:focus-visible {
outline: 2px solid var(--blue-bright);
outline-offset: -2px;
}
.nav-chevron {
margin-left: auto;
width: 14px;
height: 14px;
color: var(--text-3);
transition: transform .18s ease;
}
.nav-group.is-open .nav-chevron { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) {
.nav-chevron { transition: none; }
}
/* Unified drive view enrichment slot — breathing room between the agent tag row,
the agent action row, and the registry action row below. */
.drive-agent-extra {
display: flex;
flex-direction: column;
gap: .6rem;
margin-top: .6rem;
}
.drive-agent-extra:empty { display: none; }
.drive-agent-extra .metarows {
margin: 0;
gap: .3rem .75rem;
}
.drive-agent-extra .drive-actions {
margin: 0;
}
/* Inline two-step confirm (drill F-11) — the in-place replacement for native confirm() */
.inline-confirm { display: inline-flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.inline-confirm-q { font-size: .8rem; color: var(--warn); }
/* R-48 offsite restore wizard — scoped to the wizard page only.
The destructive intent card is marked by a critical-tone left rule, not a shouty background:
the exception-color principle applies to cards too, and the copy carries the warning. */
.restore-wizard-phases {
display: flex;
flex-wrap: wrap;
gap: .35rem .75rem;
margin-bottom: 1.5rem;
font-size: .8rem;
color: var(--text-3);
}
.restore-wizard-phase + .restore-wizard-phase::before {
content: "·";
margin-right: .75rem;
color: var(--line);
}
.restore-wizard-phase.is-current {
color: var(--blue-bright);
font-weight: 600;
}
.restore-danger-card {
border-left: 2px solid var(--crit);
}