diff --git a/app/sovran_systemsos_web/static/css/base.css b/app/sovran_systemsos_web/static/css/base.css index 1dd2f62..8c011d0 100644 --- a/app/sovran_systemsos_web/static/css/base.css +++ b/app/sovran_systemsos_web/static/css/base.css @@ -1,6 +1,11 @@ /* Sovran_SystemsOS Hub — Web UI Stylesheet - Dark theme — near-black with green accents matching the Sovran Hub icon - v8 — Black-forward, green used for accents/borders/highlights only */ + The Hub redesign — softer dark theme, GNOME 50 / libadwaita surfaces, + Sovran green reserved for status and actions. + + Design tokens are defined once here. Legacy variable names from the + previous theme are aliased to the new values so every stylesheet + (support, security, domain-setup, onboarding, …) re-skins without + needing per-rule edits. */ *, *::before, *::after { box-sizing: border-box; @@ -9,22 +14,48 @@ } :root { - --bg-color: #080a09; - --surface-color: rgba(14, 16, 15, 0.7); - --card-color: rgba(20, 22, 21, 0.6); - --border-color: rgba(255, 255, 255, 0.06); - --text-primary: #ecf3ef; - --text-secondary: #8aaa9a; - --text-dim: #4a6658; - --accent-color: #5EAD8A; - --green: #6DBF8B; - --yellow: #e5a50a; - --red: #e01b24; - --grey: #5E7A6A; - --radius-card: 18px; - --radius-btn: 8px; - --shadow-card: 0 4px 16px rgba(0, 0, 0, 0.4); - --shadow-hover: 0 8px 32px rgba(0, 0, 0, 0.5); + color-scheme: dark; + + /* ── The Hub palette ─────────────────────────────────────────── */ + --bg: #141a16; + --surface: #181f1b; + --card: #1e2621; + --card-hover: #232d27; + --elevated: #212925; + --inset: #121814; + --border: rgba(255, 255, 255, 0.07); + --border-strong: rgba(255, 255, 255, 0.14); + --text: #e3ede7; + --text-2: #a8bcb0; + --text-3: #6e8578; + --accent: #3ecf8e; + --accent-strong: #42f39a; + --accent-deep: #1aa45d; + --accent-dim: rgba(66, 243, 154, 0.12); + --amber: #e9b64a; + --red: #f66151; + --blue: #78aeed; + --mono: 'JetBrains Mono', 'Fira Code', ui-monospace, 'SF Mono', Menlo, Consolas, monospace; + --radius-card: 20px; + --radius-dialog: 26px; + --shadow-card: 0 10px 30px rgba(0, 0, 0, 0.25); + --shadow-hover: 0 16px 36px rgba(0, 0, 0, 0.38); + --shadow-pop: 0 30px 80px rgba(0, 0, 0, 0.5); + + /* ── Legacy aliases (previous theme) — keep every old sheet working */ + --bg-color: var(--bg); + --surface-color: var(--surface); + --card-color: var(--card); + --border-color: var(--border); + --text-primary: var(--text); + --text-secondary: var(--text-2); + --text-dim: var(--text-3); + --accent-color: var(--accent); + --green: var(--accent); + --yellow: var(--amber); + --red: var(--red); + --grey: var(--text-3); + --radius-btn: 12px; } html, body { @@ -32,19 +63,39 @@ html, body { } body { - font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; - background: - radial-gradient(ellipse at top, rgba(94, 173, 138, 0.04) 0%, transparent 50%), - var(--bg-color); - color: var(--text-primary); + font-family: Inter, Cantarell, 'Segoe UI', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif; + background: var(--bg); + color: var(--text); + font-size: 15px; line-height: 1.5; + -webkit-font-smoothing: antialiased; min-height: 100vh; - display: flex; - flex-direction: column; overflow: hidden; } -/* ── Login page ──────────────────────────────────────────────────── */ +/* Soft brand wash so the canvas never reads flat-black */ +body::before { + content: ""; + position: fixed; + inset: 0; + background: + radial-gradient(1100px 520px at 16% -12%, rgba(66, 243, 154, 0.04), transparent 60%), + radial-gradient(900px 600px at 110% 110%, rgba(26, 169, 110, 0.05), transparent 55%); + pointer-events: none; + z-index: 0; +} + +button { font-family: inherit; } + +::selection { background: rgba(66, 243, 154, 0.25); } + +/* ── Scrollbars ─────────────────────────────────────────────────── */ +::-webkit-scrollbar { width: 10px; height: 10px; } +::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.10); border-radius: 99px; border: 2px solid transparent; background-clip: padding-box; } +::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.18); background-clip: padding-box; } +::-webkit-scrollbar-track { background: transparent; } + +/* ── Login page ─────────────────────────────────────────────────── */ .login-wrapper { display: flex; @@ -52,86 +103,103 @@ body { justify-content: center; min-height: 100vh; padding: 24px; + position: relative; + z-index: 1; } .login-card { - background-color: rgba(14, 16, 15, 0.75); - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 20px; - padding: 48px 40px; width: 100%; - max-width: 400px; - box-shadow: 0 8px 32px rgba(0,0,0,0.5); + max-width: 402px; + text-align: center; + background: var(--elevated); + border: 1px solid var(--border-strong); + border-radius: 28px; + padding: 46px 42px 38px; + box-shadow: var(--shadow-pop); } .login-header { text-align: center; - margin-bottom: 32px; + margin-bottom: 26px; } .login-logo { - height: 64px; - margin-bottom: 16px; + width: 78px; + height: 78px; + margin-bottom: 18px; + filter: drop-shadow(0 8px 24px rgba(28, 196, 120, 0.3)); } .login-title { - font-size: 1.25rem; - font-weight: 700; - color: var(--text-primary); + font-size: 1.3rem; + font-weight: 800; + letter-spacing: -0.01em; + color: var(--text); +} + +.login-title em { font-style: normal; color: var(--accent); } + +.login-sub { + color: var(--text-2); + font-size: 0.85rem; + margin: 7px 0 0; } .login-form { display: flex; flex-direction: column; - gap: 16px; + gap: 14px; } .form-group label { display: block; - font-size: 0.82rem; - font-weight: 600; - color: var(--text-secondary); + font-size: 0.8rem; + font-weight: 650; + color: var(--text-2); margin-bottom: 6px; + text-align: left; } .form-group input { width: 100%; - padding: 10px 14px; - border: 1px solid var(--border-color); - border-radius: var(--radius-btn); - background-color: var(--card-color); - color: var(--text-primary); + padding: 11px 16px; + border: 1px solid var(--border-strong); + border-radius: 14px; + background: var(--inset); + color: var(--text); + font: inherit; font-size: 0.92rem; + outline: 0; + transition: border-color 0.15s, box-shadow 0.15s; } .form-group input:focus { - outline: none; - border-color: var(--accent-color); + border-color: rgba(66, 243, 154, 0.55); + box-shadow: 0 0 0 3px rgba(66, 243, 154, 0.13); } .btn-login { width: 100%; padding: 12px; - border-radius: var(--radius-btn); - background-color: var(--accent-color); - color: #0A1A10; - font-size: 0.95rem; + border-radius: 99px; + background: linear-gradient(180deg, #3fd68e, #1ea263); + color: #04220f; + font-size: 0.92rem; font-weight: 700; margin-top: 8px; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28), 0 6px 18px rgba(35, 199, 124, 0.22); } -.btn-login:hover { - opacity: 0.88; +.btn-login:hover:not(:disabled) { + filter: brightness(1.08); } .login-error { - background-color: rgba(224, 27, 36, 0.12); - border: 1px solid var(--red); - color: #f87171; + background: rgba(246, 97, 81, 0.10); + border: 1px solid rgba(246, 97, 81, 0.35); + color: #f8a39b; padding: 10px 14px; - border-radius: 8px; + border-radius: 12px; font-size: 0.85rem; display: none; } diff --git a/app/sovran_systemsos_web/static/css/buttons.css b/app/sovran_systemsos_web/static/css/buttons.css index b90a9eb..c90487d 100644 --- a/app/sovran_systemsos_web/static/css/buttons.css +++ b/app/sovran_systemsos_web/static/css/buttons.css @@ -5,34 +5,133 @@ button { cursor: pointer; border: none; outline: none; - transition: opacity 0.15s, box-shadow 0.15s, background-color 0.15s; + transition: background 0.16s, border-color 0.16s, color 0.16s, transform 0.16s, filter 0.16s, opacity 0.15s; } button:disabled { - opacity: 0.45; + opacity: 0.55; cursor: default; + pointer-events: none; } .btn { - padding: 7px 16px; - border-radius: var(--radius-btn); - font-size: 0.88rem; - font-weight: 600; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + border-radius: 99px; + padding: 8px 18px; + font-size: 0.84rem; + font-weight: 700; + letter-spacing: 0.01em; + cursor: pointer; + border: 1px solid transparent; + white-space: nowrap; } +.btn svg { width: 15px; height: 15px; } +.btn:active { transform: scale(0.97); } + .btn-primary { - background-color: var(--accent-color); - color: #0A1A10; + background: linear-gradient(180deg, #3fd68e, #1ea263); + color: #04220f; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28), 0 6px 18px rgba(35, 199, 124, 0.22); } .btn-primary:hover:not(:disabled) { - opacity: 0.88; + filter: brightness(1.08); + transform: translateY(-1px); } -/* Update System button: uses accent green by default */ +.btn-primary:active:not(:disabled) { transform: scale(0.97); } + +/* Ghost — the default secondary action (legacy: btn-close-modal / btn-save) */ +.btn-ghost, +.btn-close-modal, +.btn-save { + background: transparent; + border-color: var(--border-strong); + color: var(--text-2); +} + +.btn-ghost:hover:not(:disabled), +.btn-close-modal:hover:not(:disabled), +.btn-save:hover:not(:disabled) { + color: var(--text); + border-color: rgba(255, 255, 255, 0.26); + background: rgba(255, 255, 255, 0.04); +} + +/* Amber — destructive-ish confirmations (restart, reboot, retry) */ +.btn-amber, +.btn-reboot, +.btn-restart-amber, +.btn-warning { + background: rgba(233, 182, 74, 0.10); + border-color: rgba(233, 182, 74, 0.32); + color: #f2c45e; +} + +.btn-amber:hover:not(:disabled), +.btn-reboot:hover:not(:disabled), +.btn-restart-amber:hover:not(:disabled), +.btn-warning:hover:not(:disabled) { + background: rgba(233, 182, 74, 0.18); + border-color: rgba(233, 182, 74, 0.5); +} + +.btn-danger { + background: rgba(246, 97, 81, 0.10); + border-color: rgba(246, 97, 81, 0.35); + color: #f8a39b; +} + +.btn-danger:hover:not(:disabled) { + background: rgba(246, 97, 81, 0.18); + border-color: rgba(246, 97, 81, 0.5); +} + +.btn-sm { padding: 6px 14px; font-size: 0.77rem; } + +/* ── Header / topbar buttons ────────────────────────────────────── */ + +.btn-header-reboot { + background: rgba(233, 182, 74, 0.10); + border: 1px solid rgba(233, 182, 74, 0.32); + color: #f2c45e; + font-size: 0.77rem; + font-weight: 700; + padding: 6px 14px; + border-radius: 99px; +} + +.btn-header-reboot:hover:not(:disabled) { + background: rgba(233, 182, 74, 0.18); + border-color: rgba(233, 182, 74, 0.5); +} + +.btn-logout { + background: transparent; + border: 1px solid var(--border-strong); + color: var(--text-2); + font-size: 0.77rem; + font-weight: 700; + padding: 6px 14px; + border-radius: 99px; +} + +.btn-logout:hover { + color: var(--text); + border-color: rgba(255, 255, 255, 0.26); + background: rgba(255, 255, 255, 0.04); +} + +/* ── Update System button (sidebar) ─────────────────────────────── */ + .btn-update { - background-color: #4A9474; - color: #E0F2EA; + background: transparent; + border: 1px solid var(--border-strong); + color: var(--text-2); position: relative; display: flex; align-items: center; @@ -40,24 +139,22 @@ button:disabled { } .btn-update:hover:not(:disabled) { - opacity: 0.88; + color: var(--text); + border-color: rgba(255, 255, 255, 0.26); + background: rgba(255, 255, 255, 0.04); } -/* Update System button: brighter green when updates are available */ .btn-update.has-updates { - background-color: #5EAD8A; - color: #0A1A10; -} - -.btn-update.has-updates:hover:not(:disabled) { - background-color: #78C8A2; + background: rgba(66, 243, 154, 0.12); + border-color: rgba(66, 243, 154, 0.4); + color: var(--accent); } .update-badge { display: none; width: 10px; height: 10px; - background-color: var(--yellow); + background-color: var(--amber); border-radius: 50%; animation: pulse-badge 1.4s ease-in-out infinite; } @@ -71,9 +168,11 @@ button:disabled { 50% { opacity: 0.5; transform: scale(1.35); } } +/* ── Icon buttons ───────────────────────────────────────────────── */ + .btn-icon { background: none; - color: var(--text-secondary); + color: var(--text-2); padding: 6px; border-radius: 50%; font-size: 1.1rem; @@ -81,6 +180,6 @@ button:disabled { } .btn-icon:hover:not(:disabled) { - background-color: var(--border-color); - color: var(--text-primary); + background-color: rgba(255, 255, 255, 0.06); + color: var(--text); } diff --git a/app/sovran_systemsos_web/static/css/domain-setup.css b/app/sovran_systemsos_web/static/css/domain-setup.css index 6bcf638..d673a6f 100644 --- a/app/sovran_systemsos_web/static/css/domain-setup.css +++ b/app/sovran_systemsos_web/static/css/domain-setup.css @@ -18,7 +18,7 @@ domain-field-label { domain-field-input { width: 100%; - background-color: #0c0f0e; + background-color: var(--inset); color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 8px; @@ -75,7 +75,7 @@ domain-field-actions { .domain-field-input { width: 100%; - background-color: #0c0f0e; + background-color: var(--inset); color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 8px; @@ -198,21 +198,21 @@ domain-field-actions { } .port-proto-badge--tcp { - color: #6dbf8b; - background: rgba(109, 191, 139, 0.12); - border: 1px solid rgba(109, 191, 139, 0.4); + color: var(--accent); + background: rgba(66, 243, 154, 0.12); + border: 1px solid rgba(66, 243, 154, 0.4); } .port-proto-badge--udp { - color: #6aa9e0; + color: var(--blue); background: rgba(106, 169, 224, 0.12); border: 1px solid rgba(106, 169, 224, 0.4); } .port-proto-badge--both { - color: #e5a50a; - background: rgba(229, 165, 10, 0.12); - border: 1px solid rgba(229, 165, 10, 0.45); + color: var(--amber); + background: rgba(233, 182, 74, 0.12); + border: 1px solid rgba(233, 182, 74, 0.45); } .port-proto-note { @@ -230,7 +230,7 @@ domain-field-actions { margin-bottom: 14px; padding: 10px 14px; background: rgba(255, 180, 0, 0.10); - border: 1px solid var(--warning-color, #f59e0b); + border: 1px solid var(--warning-color, var(--amber)); border-radius: 8px; font-size: 0.88rem; line-height: 1.6; diff --git a/app/sovran_systemsos_web/static/css/features.css b/app/sovran_systemsos_web/static/css/features.css index ac343e4..3bef023 100644 --- a/app/sovran_systemsos_web/static/css/features.css +++ b/app/sovran_systemsos_web/static/css/features.css @@ -1,143 +1,196 @@ -/* ── Feature Manager styles ──────────────────────────────────────── */ +/* ── Sidebar: Feature Manager + Preferences ─────────────────────── */ -.feature-manager-section { - margin-bottom: 32px; +.category-section.autolaunch-section, +.category-section.feature-manager-section { + margin-bottom: 0; } -.feature-subcategory { - margin-bottom: 16px; +/* The old "Preferences" header + hr becomes a small sidebar label. + features.js renders: .section-header + hr.section-divider + cards */ +.autolaunch-section .section-header, +.feature-manager-section .section-header { + font-size: 0.66rem; + font-weight: 750; + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--text-3); + margin: 0; + padding: 14px 10px 7px; + display: block; } +.autolaunch-section .section-divider, +.feature-manager-section .section-divider { + background: none; + height: 0; + margin: 0; +} + +.feature-cards-wrap { display: flex; flex-direction: column; } + +.feature-subcategory { display: flex; flex-direction: column; } + .feature-subcategory-header { - font-size: 0.78rem; - font-weight: 700; + font-size: 0.66rem; + font-weight: 750; + letter-spacing: 0.14em; text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--text-dim); - margin-bottom: 8px; - padding-left: 4px; -} - -.feature-cards-wrap { - display: flex; - flex-direction: column; - gap: 10px; + color: var(--text-3); + padding: 12px 10px 6px; } +/* Feature card — sidebar preference row with a switch */ .feature-card { - background-color: var(--card-color); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 14px 16px; + display: flex; + gap: 10px; + align-items: flex-start; + padding: 9px 10px; + border-radius: 10px; + transition: background 0.15s; } +.feature-card:hover { background: rgba(255, 255, 255, 0.05); } + .feature-card-top { display: flex; align-items: flex-start; - gap: 12px; - margin-bottom: 8px; + gap: 10px; + width: 100%; } -.feature-card-info { - flex: 1; - min-width: 0; -} +.feature-card-info { min-width: 0; flex: 1; } .feature-card-name { - font-size: 0.9rem; - font-weight: 700; - color: var(--text-primary); - margin-bottom: 4px; + font-size: 0.84rem; + font-weight: 600; + color: var(--text); + line-height: 1.3; } .feature-card-desc { - font-size: 0.78rem; - color: var(--text-secondary); - line-height: 1.5; + margin-top: 2px; + font-size: 0.68rem; + color: var(--text-3); + line-height: 1.45; } .feature-card-status { - font-size: 0.72rem; - color: var(--text-dim); + font-size: 0.66rem; + font-weight: 700; + padding: 2px 8px; + border-radius: 99px; + flex-shrink: 0; + background: rgba(255, 255, 255, 0.05); + border: 1px solid var(--border); + color: var(--text-2); +} + +.feature-card-status.status-on { + color: var(--accent); + background: var(--accent-dim); + border-color: rgba(66, 243, 154, 0.3); +} + +.feature-conflict-warning { + margin-top: 8px; + font-size: 0.68rem; + color: var(--amber); + line-height: 1.5; +} + +/* Domain badges inside feature cards */ +.feature-domain-badge { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 0.64rem; + font-weight: 700; + padding: 2px 8px; + border-radius: 99px; margin-top: 6px; } +.feature-domain-badge.configured { + color: var(--accent); + background: var(--accent-dim); +} + +.feature-domain-badge.not-configured { + color: var(--amber); + background: rgba(233, 182, 74, 0.10); +} + +.feature-domain-icon { font-size: 0.8rem; } + +.feature-domain-label { + display: inline-flex; + align-items: center; + gap: 5px; + font-size: 0.66rem; + font-weight: 650; + margin-top: 5px; + color: var(--text-3); +} + +.feature-domain-label--ok { color: var(--accent); } +.feature-domain-label--warn { color: var(--amber); } +.feature-domain-label--error { color: var(--red); } +.feature-domain-label--checking { color: var(--blue); } + +/* ── Toggle switch (libadwaita style) ───────────────────────────── */ + .feature-toggle { position: relative; display: inline-block; - width: 44px; - height: 24px; + width: 46px; + height: 26px; flex-shrink: 0; + margin-left: auto; + margin-top: 2px; cursor: pointer; } .feature-toggle-input { - opacity: 0; - width: 0; - height: 0; position: absolute; + inset: 0; + opacity: 0; + margin: 0; + cursor: pointer; } .feature-toggle-slider { position: absolute; inset: 0; - background-color: var(--border-color); - border-radius: 24px; - transition: background-color 0.2s; + border-radius: 99px; + background: rgba(255, 255, 255, 0.12); + border: 1px solid var(--border-strong); + transition: 0.2s; + pointer-events: none; } -.feature-toggle-slider::before { +.feature-toggle-slider::after { content: ""; position: absolute; - width: 18px; - height: 18px; - left: 3px; - top: 3px; - background-color: #fff; + top: 2px; + left: 2px; + width: 20px; + height: 20px; border-radius: 50%; - transition: transform 0.2s; + background: #cfd8d2; + transition: 0.2s cubic-bezier(0.3, 0.8, 0.4, 1.2); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); } -.feature-toggle.active .feature-toggle-slider { - background-color: var(--green); +.feature-toggle-input:checked + .feature-toggle-slider { + background: var(--accent); + border-color: transparent; } -.feature-toggle.active .feature-toggle-slider::before { - transform: translateX(20px); +.feature-toggle-input:checked + .feature-toggle-slider::after { + left: 23px; + background: #fff; } -.feature-domain-badge { - display: flex; - align-items: center; - gap: 6px; - margin-top: 6px; - font-size: 0.78rem; -} - -.feature-domain-icon { - flex-shrink: 0; -} - -.feature-domain-label { - color: var(--text-secondary); -} - -.feature-domain-label--checking { - color: var(--text-dim); - font-style: italic; -} - -.feature-domain-label--ok { - color: var(--green); - font-weight: 600; -} - -.feature-domain-label--warn { - color: var(--yellow); - font-weight: 600; -} - -.feature-domain-label--error { - color: var(--red); - font-weight: 600; +.feature-toggle-input:focus-visible + .feature-toggle-slider { + box-shadow: 0 0 0 3px rgba(66, 243, 154, 0.3); } diff --git a/app/sovran_systemsos_web/static/css/header.css b/app/sovran_systemsos_web/static/css/header.css index 0eaf428..4ec98f0 100644 --- a/app/sovran_systemsos_web/static/css/header.css +++ b/app/sovran_systemsos_web/static/css/header.css @@ -1,69 +1,147 @@ -/* ── Header bar ─────────────────────────────────────────────────── */ +/* ── Topbar (replaces the old header-bar + ip-bar) ──────────────── */ -.header-bar { - backdrop-filter: blur(14px); - -webkit-backdrop-filter: blur(14px); - background-color: rgba(10, 12, 11, 0.82); - border-bottom: 1px solid rgba(255, 255, 255, 0.06); - padding: 8px 24px; +.topbar { display: flex; - flex-direction: row; align-items: center; - justify-content: space-between; - gap: 16px; - position: sticky; - top: 0; - z-index: 100; -} - -.header-logo { - height: 80px; - width: auto; - display: block; + gap: 14px; + padding: 13px 26px; + background: var(--surface); + border-bottom: 1px solid var(--border); flex-shrink: 0; + position: relative; + z-index: 5; } -.header-bar .title { - font-size: 1.15rem; - font-weight: 700; - color: var(--text-primary); +.page-title { + font-size: 1.02rem; + font-weight: 800; + letter-spacing: -0.01em; + white-space: nowrap; } -.title-group { +/* ── Search ─────────────────────────────────────────────────────── */ + +.search-box { display: flex; - flex-direction: row; - flex-wrap: wrap; align-items: center; - justify-content: center; - gap: 6px 10px; + gap: 9px; + background: var(--card); + border: 1px solid var(--border); + border-radius: 99px; + padding: 8px 16px; + flex: 1; + max-width: 400px; + min-width: 140px; + margin-left: 18px; + color: var(--text-3); + transition: border-color 0.15s, box-shadow 0.15s; } -.header-buttons { +.search-box:focus-within { + border-color: rgba(66, 243, 154, 0.35); + box-shadow: 0 0 0 3px rgba(66, 243, 154, 0.09); +} + +.search-box svg { width: 15px; height: 15px; flex-shrink: 0; } + +.search-box input { + background: none; + border: 0; + outline: 0; + color: var(--text); + width: 100%; + font: inherit; + font-size: 0.87rem; +} + +.search-box input::placeholder { color: var(--text-3); } + +/* ── Network chip — LAN + WAN always on one line ────────────────── */ + +.net-chip { display: flex; align-items: center; gap: 10px; + padding: 8px 17px; + border-radius: 99px; + background: var(--card); + border: 1px solid var(--border); + font-family: var(--mono); + font-size: 0.73rem; + color: var(--text-3); + white-space: nowrap; + order: 4; } +.net-chip svg { width: 15px; height: 15px; color: var(--text-3); flex-shrink: 0; } +.net-chip .int-wrap, .net-chip .ext-wrap { display: inline-flex; align-items: center; gap: 7px; } + +.net-chip .ip-label { + font-size: 0.6rem; + font-weight: 800; + letter-spacing: 0.08em; + color: var(--text-3); + margin-right: 7px; +} + +.net-chip .ip-value { + color: var(--text); + font-weight: 600; + font-family: var(--mono); +} + +.net-separator { + width: 1px; + height: 15px; + background: var(--border-strong); + margin: 0 3px; + flex-shrink: 0; + color: transparent; +} + +.top-actions { + display: flex; + gap: 9px; + margin-left: auto; + order: 5; +} + +/* Below 1160px the chip wraps to its own full-width topbar row — + it never stacks LAN over WAN and never hides the external IP. */ +@media (max-width: 1160px) { + .topbar { flex-wrap: wrap; row-gap: 10px; } + .net-chip { + order: 6; + flex-basis: 100%; + justify-content: center; + } +} + +/* ── Legacy badges (still used inside dialogs) ──────────────────── */ + .role-badge { - background-color: var(--accent-color); - color: #0A1A10; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 7px; + background: rgba(255, 255, 255, 0.05); + border: 1px solid var(--border-strong); + color: var(--text-2); font-size: 0.72rem; - font-weight: 700; - padding: 3px 10px; - border-radius: 20px; - letter-spacing: 0.03em; + font-weight: 750; + padding: 6px 12px; + border-radius: 99px; + letter-spacing: 0.02em; } -/* ── OS Version Badge — identical styling to the version badge ──── - ── shown next to titles in the service modal windows ──────────── */ .os-version-badge { - background-color: rgba(255, 255, 255, 0.06); - color: var(--text-secondary); - font-size: 0.72rem; + background: rgba(255, 255, 255, 0.05); + color: var(--text-2); + font-size: 0.66rem; font-weight: 600; - padding: 2px 10px; - border-radius: 12px; - border: 1px solid rgba(255, 255, 255, 0.08); + padding: 2px 9px; + border-radius: 99px; + border: 1px solid var(--border); letter-spacing: 0.02em; display: inline-flex; align-items: center; @@ -71,76 +149,9 @@ flex-shrink: 0; } -/* ── IP bar ─────────────────────────────────────────────────────── */ - -.ip-bar { - background-color: rgba(10, 12, 11, 0.7); - backdrop-filter: blur(10px); - -webkit-backdrop-filter: blur(10px); - border-bottom: 1px solid rgba(255, 255, 255, 0.05); - padding: 8px 24px; - display: flex; - align-items: center; - justify-content: center; - gap: 32px; - font-size: 0.82rem; - color: var(--text-secondary); -} - -.ip-bar .ip-label { - color: var(--text-dim); - margin-right: 6px; -} - -.ip-bar .ip-value { - font-family: 'JetBrains Mono', 'Fira Code', 'Source Code Pro', monospace; - color: var(--accent-color); - font-weight: 600; -} - -.ip-separator { - color: var(--border-color); -} -.btn-logout { - background: transparent; - border: 1px solid rgba(255, 255, 255, 0.18); - color: var(--text-secondary); - font-size: 0.78rem; - font-weight: 600; - padding: 4px 12px; - border-radius: var(--radius-btn); - cursor: pointer; - transition: border-color 0.15s, color 0.15s; -} - -.btn-logout:hover { - border-color: var(--accent-color); - color: var(--accent-color); -} - -/* ── Header reboot button ───────────────────────────────────────── */ - -.btn-header-reboot { - background: transparent; - border: 1px solid rgba(184, 125, 0, 0.35); - color: #c98d08; - font-size: 0.78rem; - font-weight: 600; - padding: 4px 12px; - border-radius: var(--radius-btn); - cursor: pointer; - transition: border-color 0.15s, color 0.15s, background-color 0.15s; -} - -.btn-header-reboot:hover { - border-color: #b87d00; - color: #e0a010; - background-color: rgba(184, 125, 0, 0.1); -} - @media (max-width: 480px) { .btn-header-reboot { - padding: 4px 8px; + padding: 5px 10px; font-size: 0.72rem; } } diff --git a/app/sovran_systemsos_web/static/css/layout.css b/app/sovran_systemsos_web/static/css/layout.css index aa90940..4e26ab1 100644 --- a/app/sovran_systemsos_web/static/css/layout.css +++ b/app/sovran_systemsos_web/static/css/layout.css @@ -1,138 +1,367 @@ -/* ── Main content ───────────────────────────────────────────────── */ +/* ── App shell: sidebar + main column ───────────────────────────── */ -.main-content { +.app { display: flex; - align-items: flex-start; + height: 100vh; + position: relative; + z-index: 1; +} + +.main { flex: 1; - overflow: hidden; - max-width: 1400px; - width: 100%; - margin-left: auto; - margin-right: auto; + display: flex; + flex-direction: column; + min-width: 0; +} + +.content { + flex: 1; + overflow-y: auto; + padding: 26px 30px 70px; + min-width: 0; } /* ── Sidebar ────────────────────────────────────────────────────── */ .sidebar { - width: 270px; + width: 250px; flex-shrink: 0; - height: 100%; - overflow-y: auto; - border-right: 1px solid rgba(255, 255, 255, 0.06); - background-color: rgba(12, 14, 13, 0.65); - backdrop-filter: blur(12px); - -webkit-backdrop-filter: blur(12px); - padding: 20px 14px; + background: var(--surface); + border-right: 1px solid var(--border); display: flex; flex-direction: column; - gap: 0; + padding: 20px 14px 14px; + overflow-y: auto; } -/* ── Sidebar: Tech Support button ───────────────────────────────── */ +.brand { + display: flex; + align-items: center; + gap: 12px; + padding: 2px 8px 20px; +} +.brand-logo { + width: 42px; + height: 42px; + flex-shrink: 0; + filter: drop-shadow(0 4px 14px rgba(28, 196, 120, 0.25)); +} + +.brand-text { display: flex; flex-direction: column; min-width: 0; } + +.brand-title { + font-size: 1.02rem; + font-weight: 800; + letter-spacing: -0.01em; + line-height: 1.2; +} + +.brand-title em { font-style: normal; color: var(--accent); } + +.brand-sub { + font-size: 0.7rem; + color: var(--text-3); + font-family: var(--mono); + margin-top: 3px; +} + +.nav-label { + font-size: 0.66rem; + font-weight: 750; + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--text-3); + padding: 14px 10px 7px; +} + +/* Category nav (dashboard.js) */ +.nav-item { + display: flex; + align-items: center; + gap: 11px; + width: 100%; + padding: 8px 10px; + border-radius: 10px; + border: 0; + background: none; + color: var(--text-2); + font-size: 0.87rem; + font-weight: 600; + cursor: pointer; + text-align: left; + transition: background 0.15s, color 0.15s; +} + +.nav-item svg { width: 17px; height: 17px; flex-shrink: 0; } + +.nav-item:hover { + background: rgba(255, 255, 255, 0.05); + color: var(--text); +} + +.nav-item.active { + background: rgba(255, 255, 255, 0.06); + color: var(--text); + box-shadow: inset 2.5px 0 0 var(--accent); +} + +.nav-text { + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.nav-count { + margin-left: auto; + font-size: 0.67rem; + font-weight: 750; + flex-shrink: 0; + color: var(--text-3); + background: rgba(255, 255, 255, 0.05); + padding: 2px 8px; + border-radius: 99px; +} + +.nav-item.active .nav-count { + background: rgba(255, 255, 255, 0.10); + color: var(--text-2); +} + +/* System items (tiles.js renders these as .sidebar-support-btn) */ .sidebar-support-btn { display: flex; align-items: center; - gap: 10px; + gap: 11px; width: 100%; - background-color: var(--card-color); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 12px 14px; - color: var(--text-primary); + padding: 8px 10px; + border-radius: 10px; + border: 0; + background: none; + color: var(--text-2); + font-size: 0.87rem; + font-weight: 600; cursor: pointer; - transition: border-style 0.15s, border-color 0.15s, background-color 0.15s; text-align: left; + transition: background 0.15s, color 0.15s, border-color 0.15s; } .sidebar-support-btn:hover { - border-color: var(--accent-color); - border-style: solid; - background-color: #162320; + background: rgba(255, 255, 255, 0.05); + color: var(--text); } .sidebar-support-btn + .sidebar-support-btn { - margin-top: 8px; + margin-top: 2px; } .sidebar-support-icon { - font-size: 1.5rem; + font-size: 1rem; + width: 17px; + height: 17px; + display: grid; + place-items: center; flex-shrink: 0; } +.sidebar-support-icon svg { width: 17px; height: 17px; } + .sidebar-support-text { display: flex; flex-direction: column; - gap: 2px; + gap: 1px; + min-width: 0; } .sidebar-support-title { - font-size: 0.88rem; - font-weight: 700; - color: var(--text-primary); + font-size: 0.87rem; + font-weight: 600; + line-height: 1.3; + color: var(--text); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .sidebar-support-hint { - font-size: 0.72rem; - color: var(--accent-color); - font-weight: 600; + font-size: 0.68rem; + font-weight: 650; + color: var(--text-3); + display: flex; + align-items: center; + gap: 6px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .sidebar-divider { border: none; - border-top: 1px solid var(--border-color); - margin: 16px 0; + border-top: 1px solid var(--border); + margin: 14px 0 0; } -/* ── Upgrade modal ──────────────────────────────────────────────── */ +.sidebar-spacer { flex: 1; min-height: 18px; } -.upgrade-dialog { - max-width: 480px; +.sidebar-footer { + border-top: 1px solid var(--border); + padding-top: 14px; } -.upgrade-info-box { - background-color: var(--card-color); - border: 1px solid var(--border-color); - border-radius: 10px; - padding: 14px 18px; - margin-bottom: 14px; +.host-note { + text-align: center; + font-size: 0.66rem; + color: var(--text-3); + font-family: var(--mono); + margin-top: 9px; } -.upgrade-info-title { - font-size: 0.88rem; +/* ── Widgets row (dashboard.js) ─────────────────────────────────── */ + +.widgets { + display: grid; + grid-template-columns: 1.1fr 1.45fr; + gap: 16px; + margin-bottom: 32px; +} + +@media (max-width: 900px) { + .widgets { grid-template-columns: 1fr; } +} + +.widget { + background: var(--card); + border: 1px solid var(--border); + border-radius: 20px; + padding: 17px 19px; + display: flex; + gap: 14px; + align-items: center; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03), var(--shadow-card); + transition: border-color 0.18s; + position: relative; + overflow: hidden; + min-width: 0; +} + +.widget:hover { border-color: var(--border-strong); } + +.widget.clickable { cursor: pointer; } +.widget.clickable:focus-visible { + outline: 2px solid rgba(66, 243, 154, 0.45); + outline-offset: 2px; +} + +.widget-chip { + width: 46px; + height: 46px; + border-radius: 14px; + flex-shrink: 0; + display: grid; + place-items: center; + color: #fff; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 6px 16px rgba(0, 0, 0, 0.3); +} + +.widget-chip svg { width: 22px; height: 22px; } +.widget-chip img { width: 46px; height: 46px; display: block; object-fit: contain; } + +.widget-chip.chip-green { + background: linear-gradient(160deg, #2f9f6b, #17683f); +} + +.widget-chip.chip-btc { + background: linear-gradient(160deg, #f7931a, #b96a0a); +} + +.widget-chip.chip-sovran { + background: linear-gradient(160deg, #42f39a, #1aa45d); +} + +.w-body { min-width: 0; } + +.widget h3 { + font-size: 0.9rem; font-weight: 700; - color: var(--text-primary); - margin-bottom: 8px; + letter-spacing: -0.005em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } -.upgrade-info-list { - padding-left: 20px; - font-size: 0.85rem; - color: var(--text-secondary); - line-height: 1.7; - margin: 0; +.widget .sub { + margin-top: 4px; + font-size: 0.78rem; + color: var(--text-2); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } -.upgrade-info-list a { - color: var(--accent-color); +.widget .sub b { color: var(--text); font-weight: 650; } +.widget .sub .good { color: var(--accent); font-weight: 650; } +.widget .sub .warn { color: var(--amber); font-weight: 650; } + +.w-bar { + height: 7px; + border-radius: 99px; + background: rgba(255, 255, 255, 0.07); + overflow: hidden; + margin: 8px 0 7px; } -.upgrade-rebuild-note { - font-style: italic; - color: var(--text-dim); - font-size: 0.82rem; +.w-bar-fill { + height: 100%; + border-radius: 99px; + background: linear-gradient(90deg, #42f39a, #1aa45d); + transition: width 0.6s cubic-bezier(0.3, 0.7, 0.3, 1); +} + +.w-chev { + margin-left: auto; + width: 30px; + height: 30px; + border-radius: 50%; + display: grid; + place-items: center; + color: var(--text-3); + flex-shrink: 0; +} + +.w-chev svg { width: 15px; height: 15px; } +.widget.clickable:hover .w-chev { + color: var(--text-2); + background: rgba(255, 255, 255, 0.07); } /* ── Tiles area ─────────────────────────────────────────────────── */ #tiles-area { - flex: 1; - height: 100%; - overflow-y: auto; - padding: 24px 20px 48px; min-width: 0; } +.dashboard-loading { + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + padding: 72px 24px; + color: var(--text-3); + font-size: 0.9rem; +} + +.dashboard-loading-spinner { + width: 20px; + height: 20px; + border-radius: 50%; + border: 2.5px solid rgba(255, 255, 255, 0.12); + border-top-color: var(--accent); + animation: spin 0.8s linear infinite; +} + +@keyframes spin { to { transform: rotate(360deg); } } + /* ── Category sections ──────────────────────────────────────────── */ .category-section { @@ -140,25 +369,30 @@ } .section-header { - font-size: 0.82rem; - font-weight: 700; - letter-spacing: 0.08em; + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 15px; + font-size: 0.72rem; + font-weight: 750; + letter-spacing: 0.14em; text-transform: uppercase; - color: var(--text-secondary); - margin-bottom: 4px; - padding-left: 4px; + color: var(--text-2); + white-space: nowrap; } .section-divider { border: none; - border-top: 1px solid var(--border-color); - margin-bottom: 16px; + flex: 1; + height: 1px; + background: linear-gradient(90deg, var(--border), transparent); + margin: 0; } .tiles-grid { - display: flex; - flex-wrap: wrap; - gap: 14px; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); + gap: 15px; } /* ── Empty state ────────────────────────────────────────────────── */ @@ -166,10 +400,64 @@ .empty-state { text-align: center; padding: 64px 24px; - color: var(--text-dim); + color: var(--text-3); } .empty-state p { font-size: 1rem; margin-bottom: 8px; } + +/* ── Upgrade modal (kept from previous layout) ──────────────────── */ + +.upgrade-dialog { + max-width: 480px; +} + +.upgrade-info-box { + background: rgba(0, 0, 0, 0.16); + border: 1px solid var(--border-strong); + border-radius: 16px; + padding: 14px 16px; + margin: 14px 0; +} + +.upgrade-info-title { + font-size: 0.88rem; + font-weight: 700; + color: var(--text); + margin-bottom: 8px; +} + +.upgrade-info-list { + padding-left: 20px; + font-size: 0.85rem; + color: var(--text-2); + line-height: 1.7; + margin: 0; +} + +.upgrade-info-list a { + color: var(--accent); +} + +.upgrade-rebuild-note { + font-style: italic; + color: var(--text-3); + font-size: 0.82rem; +} + +/* ── First-login security banner (inside .content) ──────────────── */ + +.security-first-login-banner { + display: flex; + align-items: center; + gap: 12px; + background: rgba(233, 182, 74, 0.08); + border: 1px solid rgba(233, 182, 74, 0.30); + border-radius: 16px; + padding: 12px 16px; + color: var(--text-2); + font-size: 0.85rem; + margin-bottom: 24px; +} diff --git a/app/sovran_systemsos_web/static/css/modals.css b/app/sovran_systemsos_web/static/css/modals.css index 81056e5..bd7057f 100644 --- a/app/sovran_systemsos_web/static/css/modals.css +++ b/app/sovran_systemsos_web/static/css/modals.css @@ -1,458 +1,454 @@ -/* ── NWC Benefits Grid ─────────────────────────────────────────── */ - -.nwc-benefits-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 12px; - margin-top: 14px; -} - -.nwc-benefit-item { - background: rgba(255, 255, 255, 0.02); - border: 1px solid var(--border-color); - border-radius: 10px; - padding: 10px 14px; - display: flex; - gap: 12px; - align-items: flex-start; - transition: background-color 0.2s; -} - -.nwc-benefit-item:hover { - background: rgba(255, 255, 255, 0.04); -} - -.nwc-benefit-icon { - font-size: 1.25rem; - margin-top: 2px; - opacity: 0.9; -} - -.nwc-benefit-content strong { - display: block; - font-size: 0.82rem; - color: var(--text-primary); - margin-bottom: 2px; -} - -.nwc-benefit-content p { - margin: 0; - font-size: 0.75rem; - line-height: 1.45; - color: var(--text-secondary); -} - -@media (max-width: 800px) { - .nwc-benefits-grid { - grid-template-columns: 1fr; - } -} - -/* ── Update modal ────────────────────────────────────────────────── */ - +/* ── Modal shells ───────────────────────────────────────────────── + One dialog anatomy for every modal: elevated surface, 26px radius, + header (icon + title + chips), scrollable body, right-aligned foot. */ .modal-overlay { - display: none; position: fixed; inset: 0; - background-color: rgba(0, 0, 0, 0.6); - backdrop-filter: blur(6px); - -webkit-backdrop-filter: blur(6px); - z-index: 200; + background: rgba(8, 12, 10, 0.62); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + display: flex; align-items: center; justify-content: center; + padding: 26px; + z-index: 200; + opacity: 0; + visibility: hidden; + transition: opacity 0.22s, visibility 0.22s; } .modal-overlay.open { - display: flex; + opacity: 1; + visibility: visible; } -.modal-dialog { - background-color: rgba(14, 16, 15, 0.8); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 16px; - width: 90vw; - max-width: 900px; - max-height: 80vh; +.modal-dialog, +.creds-dialog { + width: 100%; + max-width: 570px; + max-height: min(86vh, 780px); + background: var(--elevated); + border: 1px solid var(--border-strong); + border-radius: var(--radius-dialog); + box-shadow: var(--shadow-pop); display: flex; flex-direction: column; - box-shadow: 0 24px 64px rgba(0, 0, 0, 0.6); + transform: translateY(16px) scale(0.965); + transition: transform 0.24s cubic-bezier(0.2, 0.9, 0.25, 1.15); } +.modal-overlay.open .modal-dialog, +.modal-overlay.open .creds-dialog { + transform: none; +} + +.creds-dialog--wide { max-width: 760px; } +.domain-narrow-dialog { max-width: 480px; } + +/* ── Update / Rebuild modal internals ───────────────────────────── */ + .modal-header { display: flex; align-items: center; - padding: 16px 20px; - border-bottom: 1px solid var(--border-color); - gap: 12px; + gap: 10px; + padding: 20px 24px 0; } .modal-title { - font-size: 1rem; - font-weight: 700; - flex: 1; + font-size: 1.13rem; + font-weight: 800; + letter-spacing: -0.01em; } .modal-status { - font-size: 0.85rem; - color: var(--text-secondary); + margin-left: auto; + font-size: 0.78rem; + font-weight: 650; + color: var(--text-2); } .modal-spinner { - width: 18px; - height: 18px; - border: 2.5px solid var(--border-color); - border-top-color: var(--accent-color); + width: 16px; + height: 16px; border-radius: 50%; - animation: spin 0.75s linear infinite; - display: none; + border: 2.5px solid rgba(255, 255, 255, 0.14); + border-top-color: var(--accent); + flex-shrink: 0; } -.modal-spinner.spinning { - display: block; -} +.modal-spinner.spinning { animation: spin 0.8s linear infinite; } -@keyframes spin { - to { transform: rotate(360deg); } -} +@keyframes spin { to { transform: rotate(360deg); } } .modal-log { - flex: 1; + background: var(--inset); + border: 1px solid var(--border); + border-radius: 14px; + padding: 13px 16px; + margin: 16px 24px 0; + font-family: var(--mono); + font-size: 0.72rem; + line-height: 1.85; + color: var(--text-2); + max-height: 260px; + min-height: 120px; overflow-y: auto; - padding: 12px 16px; - font-family: 'JetBrains Mono', 'Fira Code', 'Source Code Pro', monospace; - font-size: 0.78rem; - line-height: 1.6; - color: var(--text-primary); - background-color: #0c0f0e; white-space: pre-wrap; - word-break: break-all; - min-height: 200px; + word-break: break-word; } .modal-footer { display: flex; - align-items: center; - justify-content: flex-end; gap: 10px; - padding: 12px 20px; - border-top: 1px solid var(--border-color); -} - -/* Reboot = GREEN */ -.modal-footer .btn-reboot, -button.btn-reboot { - background-color: #6DBF8B; - color: #fff; -} - -.modal-footer .btn-reboot:hover:not(:disabled), -button.btn-reboot:hover:not(:disabled) { - background-color: #529E7E; -} - -/* Restart = AMBER (manual restart action) */ -.btn-restart-amber { - background-color: #b87d00; - color: #fff; -} - -.btn-restart-amber:hover:not(:disabled) { - background-color: #9a6800; -} - -/* Restart conflict warning box */ -.restart-conflict-box { - background-color: rgba(180, 100, 0, 0.12); - border-left: 3px solid #c97a00; - border-radius: 6px; - padding: 12px 14px; - margin-bottom: 14px; -} - -.restart-conflict-title { - font-size: 0.88rem; - font-weight: 700; - color: #e69000; - margin: 0 0 6px 0; -} - -.restart-conflict-desc { - font-size: 0.83rem; - color: var(--text-secondary); - line-height: 1.5; - margin: 0; -} - -/* Reboot error card actions row */ -.reboot-error-actions { - display: flex; - gap: 12px; - justify-content: center; + justify-content: flex-end; + padding: 18px 24px 22px; flex-wrap: wrap; - margin-top: 20px; } -.btn-save { - background-color: var(--yellow); - color: #0A1A10; -} - -.btn-save:hover:not(:disabled) { - background-color: #c98d08; -} - -.btn-close-modal { - background-color: var(--border-color); - color: var(--text-primary); -} - -.btn-close-modal:hover:not(:disabled) { - background-color: #1c2a24; -} - -/* ── Credentials info modal ──────────────────────────────────────── */ - -.creds-dialog { - background-color: rgba(14, 16, 15, 0.8); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 16px; - width: 90vw; - max-width: 700px; - max-height: 85vh; - display: flex; - flex-direction: column; - box-shadow: 0 24px 64px rgba(0, 0, 0, 0.6); - animation: creds-fade-in 0.2s ease-out; -} - -@keyframes creds-fade-in { - from { opacity: 0; transform: scale(0.95) translateY(8px); } - to { opacity: 1; transform: scale(1) translateY(0); } -} +/* ── Creds / service-detail modal internals ─────────────────────── */ .creds-header { display: flex; align-items: center; - padding: 20px 28px; - border-bottom: 1px solid var(--border-color); + gap: 14px; + padding: 22px 24px 0; } .creds-title { - font-size: 1.15rem; - font-weight: 700; - flex: 1; display: flex; align-items: center; - gap: 10px; -} - -.creds-title-version-badge { - background-color: rgba(255, 255, 255, 0.06); - color: var(--text-secondary); - font-size: 0.72rem; - font-weight: 600; - padding: 2px 10px; - border-radius: 12px; - border: 1px solid rgba(255, 255, 255, 0.08); - letter-spacing: 0.02em; - display: inline-flex; - align-items: center; + gap: 14px; + font-size: 1.13rem; + font-weight: 800; + letter-spacing: -0.01em; + min-width: 0; + flex-wrap: wrap; } .creds-title-icon { - width: 28px; - height: 28px; - vertical-align: middle; - border-radius: 6px; + width: 54px; + height: 54px; + border-radius: 16px; + object-fit: contain; flex-shrink: 0; + overflow: hidden; } +.creds-title-version-badge { + background: rgba(255, 255, 255, 0.05); + color: var(--text-2); + font-family: var(--mono); + font-size: 0.66rem; + font-weight: 600; + padding: 2px 9px; + border-radius: 99px; + border: 1px solid var(--border); + letter-spacing: 0.02em; + display: inline-flex; + align-items: center; + white-space: nowrap; +} + +.creds-title-status-pill { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 0.69rem; + font-weight: 700; + padding: 3px 10px; + border-radius: 99px; + letter-spacing: 0.01em; + white-space: nowrap; + background: rgba(255, 255, 255, 0.05); + color: var(--text-2); + border: 1px solid var(--border); +} + +.creds-title-status-pill.st-active { background: var(--accent-dim); color: var(--accent); border-color: rgba(66, 243, 154, 0.3); } +.creds-title-status-pill.st-needs-attention { background: rgba(233, 182, 74, 0.10); color: var(--amber); border-color: rgba(233, 182, 74, 0.3); } +.creds-title-status-pill.st-failed { background: rgba(246, 97, 81, 0.10); color: var(--red); border-color: rgba(246, 97, 81, 0.3); } +.creds-title-status-pill.st-syncing, +.creds-title-status-pill.st-loading, +.creds-title-status-pill.st-checking-reachability { background: rgba(120, 174, 237, 0.10); color: var(--blue); border-color: rgba(120, 174, 237, 0.3); } + .creds-close-btn { - background: none; - color: var(--text-secondary); - font-size: 1.3rem; - padding: 4px 8px; - border-radius: 6px; + margin-left: auto; + width: 36px; + height: 36px; + border-radius: 50%; + display: grid; + place-items: center; + flex-shrink: 0; + background: transparent; + border: 1px solid var(--border); + color: var(--text-2); + font-size: 0.9rem; cursor: pointer; - border: none; + transition: 0.15s; } .creds-close-btn:hover { - background-color: var(--border-color); - color: var(--text-primary); -} - -.creds-dialog--wide { - max-width: 980px; + border-color: var(--border-strong); + color: var(--text); + background: rgba(255, 255, 255, 0.04); } .creds-body { - padding: 24px 28px; + padding: 19px 24px 24px; overflow-y: auto; } -.creds-loading { - color: var(--text-dim); - text-align: center; +.creds-loading, +.creds-empty { + color: var(--text-3); + font-size: 0.88rem; padding: 24px 0; + text-align: center; } +/* Sections → titled cards */ +.svc-detail-section { + margin-top: 14px; + border: 1px solid var(--border-strong); + border-radius: 16px; + background: rgba(0, 0, 0, 0.16); + padding: 14px 16px; +} + +.svc-detail-section:first-child { margin-top: 0; } + +.svc-detail-section-title { + display: flex; + align-items: center; + gap: 8px; + font-size: 0.92rem; + font-weight: 750; + margin-bottom: 8px; +} + +.svc-detail-desc { + font-size: 0.82rem; + color: var(--text-2); + line-height: 1.62; +} + +.svc-detail-desc + .svc-detail-desc { margin-top: 8px; } + +.svc-detail-status { + display: inline-flex; + align-items: center; + gap: 8px; + font-size: 0.85rem; + font-weight: 650; +} + +.svc-detail-troubleshoot { + font-size: 0.8rem; + color: var(--text-2); + line-height: 1.6; +} + +.svc-detail-troubleshoot strong { color: var(--text); } + +.svc-detail-troubleshoot + .svc-detail-troubleshoot { margin-top: 10px; } + +.svc-detail-domain-btn { margin-top: 10px; } +.svc-detail-restart-btn { margin-top: 8px; } + +.svc-detail-addon-status { + font-size: 0.85rem; + font-weight: 700; +} + +.svc-detail-related-feature-btn:disabled { + cursor: not-allowed; + opacity: 0.55; +} + +.svc-detail-addon-row, +.svc-detail-option-card .svc-detail-addon-row { + display: flex; + align-items: center; + gap: 10px; + margin-top: 10px; + flex-wrap: wrap; +} + +.addon-status--on { color: var(--accent); font-weight: 700; font-size: 0.82rem; } +.addon-status--off { color: var(--text-3); font-weight: 700; font-size: 0.82rem; } + +.svc-detail-option-list { + margin: 8px 0 0; + padding-left: 20px; + font-size: 0.78rem; + color: var(--text-2); + line-height: 1.7; +} + +.svc-detail-option-privacy { + margin-top: 10px; + font-size: 0.78rem; + color: var(--text-2); + background: var(--accent-dim); + border: 1px solid rgba(66, 243, 154, 0.22); + border-radius: 12px; + padding: 9px 13px; +} + +.svc-detail-restart-section { + border-top: 1px solid var(--border); + padding-top: 16px; + margin-top: 16px; +} + +.svc-detail-restart-result { + margin-top: 10px; + font-size: 0.82rem; + font-weight: 650; + color: var(--accent); + display: none; +} + +.svc-detail-restart-result.error { color: var(--amber); } +.svc-detail-restart-result.visible { display: block; } + +/* ── Credential rows → label + value pill + Copy ────────────────── */ + .creds-row { - margin-bottom: 20px; + margin-top: 11px; } -.creds-row:last-child { - margin-bottom: 0; -} +.creds-row:first-child { margin-top: 0; } .creds-label { font-size: 0.78rem; font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--text-dim); + color: var(--text-2); margin-bottom: 6px; } .creds-value-wrap { display: flex; - align-items: flex-start; - gap: 10px; + align-items: center; + gap: 8px; + min-width: 0; + background: var(--inset); + border: 1px solid var(--border); + border-radius: 10px; + padding: 6px 7px 6px 12px; } .creds-value { flex: 1; - font-family: 'JetBrains Mono', 'Fira Code', 'Source Code Pro', monospace; - font-size: 0.92rem; - color: var(--text-primary); - background-color: #0c0f0e; - padding: 12px 16px; - border-radius: 8px; - word-break: break-all; + min-width: 0; + font-family: var(--mono); + font-size: 0.76rem; + color: var(--text); + text-align: left; + overflow-wrap: anywhere; white-space: pre-wrap; line-height: 1.6; - border: 1px solid var(--border-color); } -.creds-copy-btn { - background-color: var(--border-color); - color: var(--text-primary); - font-size: 0.78rem; - font-weight: 600; - padding: 8px 14px; - border-radius: 6px; - cursor: pointer; - border: none; - white-space: nowrap; - flex-shrink: 0; - align-self: flex-start; - margin-top: 10px; +.creds-value.creds-value--pre { + white-space: pre-line; + word-break: normal; + line-height: 1.65; } -.creds-copy-btn:hover { - background-color: #1c2a24; -} - -.creds-copy-btn.copied { - background-color: var(--green); - color: #fff; -} - -.creds-empty { - color: var(--text-dim); - text-align: center; - padding: 24px 0; - font-size: 0.88rem; -} - -/* ── Credential links ────────────────────────────────────────────── */ - .creds-link { - color: #b8f0c0; + color: var(--accent); text-decoration: none; - word-break: break-all; } -.creds-link:hover { - text-decoration: underline; - color: #defce6; -} +.creds-link:hover { text-decoration: underline; } -/* ── Matrix action buttons ───────────────────────────────────────── */ - -.matrix-actions-divider { - border: none; - border-top: 1px solid var(--border-color); - margin: 18px 0 14px; -} - -.matrix-actions-row { - display: flex; - gap: 12px; - flex-wrap: wrap; -} - -.matrix-action-btn { - background-color: var(--accent-color); - color: #0A1A10; - font-size: 0.88rem; +.creds-copy-btn { + flex-shrink: 0; + background: none; + border: 1px solid var(--border-strong); + color: var(--text-2); + font-size: 0.68rem; font-weight: 700; - padding: 10px 18px; - border-radius: 8px; - border: none; + padding: 3px 11px; + border-radius: 99px; cursor: pointer; - flex: 1; - min-width: 140px; + transition: 0.15s; } -.matrix-action-btn:hover { - background-color: #7CC4A0; +.creds-copy-btn:hover, +.creds-copy-btn.copied { + color: var(--accent); + border-color: rgba(66, 243, 154, 0.5); + background: rgba(66, 243, 154, 0.08); } -.matrix-form-group { - margin-bottom: 14px; +/* QR */ +.creds-qr-wrap { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 8px; } +.creds-qr-img { + width: 150px; + height: 150px; + padding: 10px; + background: #edf4ef; + border-radius: 14px; +} + +.creds-qr-hint { + margin-top: 10px; + font-size: 0.78rem; + color: var(--text-2); + text-align: center; + line-height: 1.55; + max-width: 420px; +} + +/* ── Matrix / system password forms ─────────────────────────────── */ + +.matrix-form-group { margin-top: 13px; } +.matrix-form-group:first-child { margin-top: 0; } + .matrix-form-label { display: block; - font-size: 0.82rem; - color: var(--text-secondary); + font-size: 0.8rem; + font-weight: 650; + color: var(--text-2); margin-bottom: 6px; - font-weight: 600; } .matrix-form-input { width: 100%; - background-color: #0c0f0e; - color: var(--text-primary); - border: 1px solid var(--border-color); - border-radius: 8px; - padding: 10px 12px; - font-size: 0.9rem; - box-sizing: border-box; + background: var(--inset); + border: 1px solid var(--border-strong); + border-radius: 12px; + color: var(--text); + font: inherit; + font-size: 0.88rem; + padding: 9px 14px; + outline: 0; + transition: border-color 0.15s; } -.matrix-form-input:focus { - outline: none; - border-color: var(--accent-color); -} +.matrix-form-input:focus { border-color: rgba(66, 243, 154, 0.5); } .matrix-form-checkbox-row { display: flex; align-items: center; - gap: 10px; - margin-bottom: 14px; + gap: 9px; + margin-top: 13px; } -.matrix-form-checkbox-row input[type="checkbox"] { - width: 16px; - height: 16px; - accent-color: var(--accent-color); +.matrix-form-checkbox-row input { + width: 15px; + height: 15px; + accent-color: var(--accent); + cursor: pointer; } .matrix-form-actions { @@ -461,678 +457,965 @@ button.btn-reboot:hover:not(:disabled) { margin-top: 18px; } -.matrix-form-submit { - background-color: var(--accent-color); - color: #0A1A10; - font-size: 0.88rem; - font-weight: 700; - padding: 10px 20px; - border-radius: 8px; - border: none; - cursor: pointer; - flex: 1; -} - -.matrix-form-submit:hover:not(:disabled) { - background-color: #7CC4A0; -} - -.matrix-form-submit:disabled { - opacity: 0.6; - cursor: default; -} - .matrix-form-back { - background-color: var(--border-color); - color: var(--text-primary); - font-size: 0.88rem; - font-weight: 600; - padding: 10px 20px; - border-radius: 8px; - border: none; + background: none; + border: 1px solid var(--border-strong); + color: var(--text-2); + border-radius: 99px; + padding: 8px 18px; + font-size: 0.82rem; + font-weight: 700; cursor: pointer; + transition: 0.15s; } .matrix-form-back:hover { - background-color: #1c2a24; + color: var(--text); + border-color: rgba(255, 255, 255, 0.26); + background: rgba(255, 255, 255, 0.04); } +.matrix-form-submit { + background: linear-gradient(180deg, #3fd68e, #1ea263); + color: #04220f; + border-radius: 99px; + padding: 8px 18px; + font-size: 0.82rem; + font-weight: 700; + cursor: pointer; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28), 0 6px 18px rgba(35, 199, 124, 0.22); +} + +.matrix-form-submit:hover:not(:disabled) { filter: brightness(1.08); } + .matrix-form-result { - margin-top: 14px; - padding: 12px 16px; - border-radius: 8px; - font-size: 0.88rem; - line-height: 1.5; display: none; + margin-top: 12px; + font-size: 0.8rem; + font-weight: 650; + padding: 9px 13px; + border-radius: 10px; } .matrix-form-result.success { - background-color: rgba(109, 191, 139, 0.12); - border: 1px solid var(--green); - color: var(--green); display: block; + color: var(--accent); + background: var(--accent-dim); + border: 1px solid rgba(66, 243, 154, 0.25); } .matrix-form-result.error { - background-color: rgba(239, 68, 68, 0.12); - border: 1px solid #ef4444; - color: #f87171; display: block; + color: var(--amber); + background: rgba(233, 182, 74, 0.07); + border: 1px solid rgba(233, 182, 74, 0.3); } -/* ── QR code in credentials modal ────────────────────────────────── */ +.matrix-actions-divider { + border: none; + border-top: 1px solid var(--border); + margin: 14px 0 0; +} -.creds-qr-wrap { +.matrix-actions-row { display: flex; - flex-direction: column; - align-items: center; - padding: 20px 0; - margin-bottom: 10px; + gap: 9px; + flex-wrap: wrap; + margin-top: 13px; } -.creds-qr-img { - width: 240px; - height: 240px; - border-radius: 12px; - border: 4px solid #fff; - background-color: #fff; - image-rendering: pixelated; - box-shadow: 0 4px 16px rgba(0,0,0,0.4); +.matrix-action-btn { + background: transparent; + border: 1px solid var(--border-strong); + color: var(--text-2); + border-radius: 99px; + padding: 6px 14px; + font-size: 0.77rem; + font-weight: 700; + cursor: pointer; + transition: 0.15s; } -.creds-qr-hint { - margin-top: 10px; - font-size: 0.82rem; - color: var(--text-secondary); - font-style: italic; +.matrix-action-btn:hover { + color: var(--text); + border-color: rgba(255, 255, 255, 0.26); + background: rgba(255, 255, 255, 0.04); } -.nwc-secret-warning { - margin: 0 0 14px; - padding: 10px 12px; +/* System password change */ +.sys-chpw-header { margin-bottom: 12px; } + +.sys-chpw-title { + font-size: 0.98rem; + font-weight: 750; +} + +.sys-chpw-desc { + margin-top: 6px; + font-size: 0.8rem; + color: var(--text-2); + line-height: 1.6; +} + +.pw-input-wrap { position: relative; } + +.pw-toggle-btn { + position: absolute; + right: 10px; + top: 50%; + transform: translateY(-50%); + background: none; + border: 0; + color: var(--text-3); + cursor: pointer; + font-size: 0.95rem; + padding: 4px 6px; border-radius: 8px; - border: 1px solid #f59e0b; - background: rgba(245, 158, 11, 0.12); - color: #fbbf24; - font-size: 0.86rem; } -/* ── Lightning Wallet Connections: tabbed shell ─────────────────── */ +.pw-toggle-btn:hover { color: var(--text); background: rgba(255, 255, 255, 0.06); } + +.pw-hint { + font-size: 0.7rem; + color: var(--text-3); + margin-top: 5px; +} + +.pw-credentials-note { + margin-top: 14px; + font-size: 0.78rem; + color: var(--amber); + background: rgba(233, 182, 74, 0.07); + border: 1px solid rgba(233, 182, 74, 0.3); + border-radius: 12px; + padding: 10px 13px; + line-height: 1.55; +} + +/* ── Port requirements ──────────────────────────────────────────── */ + +.port-req-table, +.svc-detail-port-table { + width: 100%; + border-collapse: collapse; + margin-top: 10px; + font-size: 0.8rem; +} + +.port-req-table th, +.svc-detail-port-table th { + text-align: left; + font-size: 0.68rem; + font-weight: 750; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--text-3); + padding: 7px 10px; + border-bottom: 1px solid var(--border-strong); +} + +.port-req-table td, +.svc-detail-port-table td { + padding: 9px 10px; + border-bottom: 1px solid var(--border); + color: var(--text-2); + vertical-align: middle; +} + +.port-req-table tr:last-child td, +.svc-detail-port-table tr:last-child td { border-bottom: 0; } + +.port-req-port, +.port-req-internal-ip { + font-family: var(--mono); + font-size: 0.78rem; + color: var(--text); + font-weight: 650; +} + +.port-req-intro, +.port-req-hint, +.svc-detail-port-note { + font-size: 0.78rem; + color: var(--text-2); + line-height: 1.6; +} + +.port-req-steps { + margin-top: 6px; + margin-bottom: 10px; + font-size: 0.76rem; + color: var(--text-3); + line-height: 1.6; +} + +.port-proto-badge { + display: inline-block; + font-size: 0.66rem; + font-weight: 800; + letter-spacing: 0.06em; + padding: 2px 9px; + border-radius: 99px; +} + +.port-proto-badge--tcp { background: rgba(120, 174, 237, 0.12); color: var(--blue); } +.port-proto-badge--udp { background: rgba(66, 243, 154, 0.12); color: var(--accent); } +.port-proto-badge--both { background: rgba(233, 182, 74, 0.12); color: var(--amber); } + +.port-proto-note { + display: block; + font-size: 0.66rem; + color: var(--text-3); + margin-top: 2px; +} + +.port-status-closed, +.port-status-closed-note { + margin-top: 10px; + font-size: 0.78rem; + color: var(--amber); + background: rgba(233, 182, 74, 0.07); + border: 1px solid rgba(233, 182, 74, 0.3); + border-radius: 12px; + padding: 9px 13px; +} + +/* ── Domain setup fields ────────────────────────────────────────── */ + +.domain-setup-intro { + font-size: 0.85rem; + color: var(--text-2); + line-height: 1.65; +} + +.domain-setup-intro p { margin-top: 8px; } +.domain-setup-intro p:first-child { margin-top: 0; } +.domain-setup-intro strong { color: var(--text); } +.domain-setup-intro a { color: var(--accent); } + +.domain-field-group { margin-top: 13px; } + +.domain-field-label { + display: block; + font-size: 0.8rem; + font-weight: 650; + color: var(--text-2); + margin-bottom: 6px; +} + +.domain-field-input { + width: 100%; + background: var(--inset); + border: 1px solid var(--border-strong); + border-radius: 12px; + color: var(--text); + font: inherit; + font-size: 0.88rem; + padding: 9px 14px; + outline: 0; + transition: border-color 0.15s; +} + +.domain-field-input:focus { border-color: rgba(66, 243, 154, 0.5); } + +.domain-field-hint { + font-size: 0.7rem; + color: var(--text-3); + margin-top: 5px; + line-height: 1.5; +} + +.domain-field-actions { + display: flex; + gap: 10px; + margin-top: 18px; + flex-wrap: wrap; +} + +.domain-nwc-warning { + margin-top: 14px; + font-size: 0.78rem; + color: var(--amber); + background: rgba(233, 182, 74, 0.07); + border: 1px solid rgba(233, 182, 74, 0.3); + border-radius: 12px; + padding: 10px 13px; + line-height: 1.55; +} + +.onboarding-port-warn { + margin-top: 14px; + font-size: 0.78rem; + color: var(--text-2); + background: rgba(0, 0, 0, 0.16); + border: 1px solid var(--border-strong); + border-radius: 12px; + padding: 10px 13px; + line-height: 1.55; +} + +/* ── Restart confirm / support shared ───────────────────────────── */ + +.support-desc { + font-size: 0.85rem; + color: var(--text-2); + line-height: 1.65; +} + +.support-desc + .support-desc { margin-top: 10px; } +.support-desc strong { color: var(--text); } + +.restart-conflict-box { + background: rgba(246, 97, 81, 0.08); + border: 1px solid rgba(246, 97, 81, 0.3); + border-radius: 12px; + padding: 12px 15px; + margin-bottom: 14px; +} + +.restart-conflict-title { + font-size: 0.88rem; + font-weight: 750; + color: #f8a39b; +} + +.restart-conflict-desc { + margin-top: 4px; + font-size: 0.8rem; + color: var(--text-2); + line-height: 1.55; +} + +/* ── NWC wallet manager ─────────────────────────────────────────── */ + +.nwc-tabs { display: block; } .nwc-tabs-bar { display: flex; align-items: center; - justify-content: space-between; + gap: 9px; flex-wrap: wrap; - gap: 12px; - margin: -6px 0 20px; - padding-bottom: 12px; - border-bottom: 1px solid var(--border-color); + margin-bottom: 16px; } -.nwc-tabs-btns { - display: flex; - gap: 4px; - background: rgba(255, 255, 255, 0.03); - border: 1px solid var(--border-color); - border-radius: 10px; - padding: 4px; -} +.nwc-tabs-btns { display: flex; gap: 9px; flex-wrap: wrap; } .nwc-tab-btn { - background: none; - border: none; - color: var(--text-secondary); - font-size: 0.86rem; - font-weight: 600; - padding: 8px 16px; - border-radius: 7px; + padding: 7px 16px; + border-radius: 99px; cursor: pointer; - white-space: nowrap; - transition: background-color 0.15s ease, color 0.15s ease; + border: 1px solid var(--border-strong); + background: none; + color: var(--text-2); + font-size: 0.8rem; + font-weight: 700; + transition: 0.15s; } -.nwc-tab-btn:hover:not(.active) { - color: var(--text-primary); - background: rgba(255, 255, 255, 0.04); -} +.nwc-tab-btn:hover { color: var(--text); } .nwc-tab-btn.active { - background: var(--accent-color); - color: #0A1A10; + background: var(--accent-dim); + border-color: rgba(66, 243, 154, 0.4); + color: var(--accent); } +.nwc-tab-panel { display: none; } +.nwc-tab-panel.active { display: block; } + .nwc-header-chips { + margin-left: auto; display: flex; - align-items: center; gap: 8px; + align-items: center; flex-wrap: wrap; } .nwc-header-chip { display: inline-flex; align-items: center; - gap: 6px; - font-size: 0.75rem; - color: var(--text-secondary); - background: rgba(255, 255, 255, 0.03); - border: 1px solid var(--border-color); - border-radius: 999px; - padding: 4px 10px; - max-width: 260px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + font-family: var(--mono); + font-size: 0.66rem; + font-weight: 600; + color: var(--text-2); + background: rgba(255, 255, 255, 0.05); + border: 1px solid var(--border); + padding: 2px 9px; + border-radius: 99px; } -.nwc-header-chip--domain { - font-family: 'JetBrains Mono', 'Fira Code', 'Source Code Pro', monospace; - font-size: 0.72rem; -} - -.nwc-header-chip--warn { - border-color: rgba(245, 158, 11, 0.5); - color: #fbbf24; -} - -.nwc-tab-panel { - display: none; -} - -.nwc-tab-panel.active { - display: block; - animation: nwc-tab-fade 0.16s ease-out; -} - -@keyframes nwc-tab-fade { - from { opacity: 0; transform: translateY(4px); } - to { opacity: 1; transform: translateY(0); } -} - -/* Setup tab: first section shouldn't double up on top padding */ -#nwc-tab-setup .svc-detail-section:first-child { - padding-top: 0; - margin-top: 0; - border-top: none; -} - -.nwc-tab-intro { - display: flex; - flex-direction: column; - align-items: stretch; - gap: 16px; - margin-bottom: 20px; - padding-bottom: 20px; - border-bottom: 1px solid var(--border-color); -} +.nwc-tab-intro { margin-bottom: 14px; } .nwc-tab-intro-title { - font-size: 1rem; - font-weight: 700; - color: var(--text-primary); - margin-bottom: 4px; + font-size: 0.98rem; + font-weight: 750; } .nwc-tab-intro-desc { - margin: 0; - font-size: 0.85rem; - line-height: 1.5; - color: var(--text-secondary); - max-width: 62ch; + margin-top: 5px; + font-size: 0.8rem; + color: var(--text-2); + line-height: 1.6; } -/* ── Wallet toolbar ─────────────────────────────────────────────── */ - .nwc-toolbar { display: flex; align-items: center; - justify-content: space-between; - gap: 12px; + gap: 10px; flex-wrap: wrap; margin-bottom: 14px; } .nwc-toolbar-count { font-size: 0.78rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--text-dim); + color: var(--text-3); + font-weight: 650; } .nwc-toolbar-actions { + margin-left: auto; display: flex; - align-items: center; - gap: 8px; + gap: 9px; + flex-wrap: wrap; } -.nwc-toolbar-actions .matrix-action-btn, -.nwc-toolbar-actions .matrix-form-back { - padding: 8px 14px; - font-size: 0.82rem; -} - -/* ── Domain-required callout ────────────────────────────────────── */ - -.nwc-domain-required { - border: 1px solid rgba(245, 158, 11, 0.4); - background: rgba(245, 158, 11, 0.08); - border-radius: 10px; - padding: 14px 16px; - margin-bottom: 16px; - font-size: 0.86rem; - color: var(--text-primary); -} - -.nwc-domain-required-body { - margin-top: 6px; - color: var(--text-secondary); - line-height: 1.5; -} - -.nwc-domain-required-btn { - margin-top: 12px; -} - -/* ── Empty / disabled states ────────────────────────────────────── */ - -.nwc-empty-state, -.nwc-disabled-state { - text-align: center; - padding: 36px 24px; - border: 1px dashed var(--border-color); - border-radius: 12px; - background: rgba(255, 255, 255, 0.015); -} - -.nwc-empty-icon, -.nwc-disabled-icon { - font-size: 2rem; - margin-bottom: 10px; -} - -.nwc-empty-title, -.nwc-disabled-title { - font-size: 1rem; - font-weight: 700; - color: var(--text-primary); - margin-bottom: 6px; -} - -.nwc-empty-desc, -.nwc-disabled-desc { - margin: 0 auto 14px; - max-width: 46ch; - font-size: 0.86rem; - line-height: 1.55; - color: var(--text-secondary); -} - -/* ── Wallet cards ───────────────────────────────────────────────── */ - -.nwc-wallet-list { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); - gap: 12px; - margin-top: 12px; -} +.nwc-wallet-list { display: flex; flex-direction: column; } .nwc-wallet-card { - border: 1px solid var(--border-color); - border-radius: 12px; + border: 1px solid var(--border-strong); + border-radius: 16px; + background: rgba(0, 0, 0, 0.16); padding: 14px 16px; - background: rgba(255, 255, 255, 0.02); - display: flex; - flex-direction: column; - transition: border-color 0.15s ease; } -.nwc-wallet-card:hover { - border-color: rgba(255, 255, 255, 0.16); -} +.nwc-wallet-card + .nwc-wallet-card { margin-top: 13px; } .nwc-wallet-card-head { display: flex; - align-items: flex-start; - justify-content: space-between; + align-items: center; gap: 10px; - margin-bottom: 12px; + flex-wrap: wrap; } -.nwc-wallet-card-title { - font-weight: 700; - font-size: 0.95rem; - color: var(--text-primary); - word-break: break-word; -} +.nwc-wallet-card-title { font-size: 0.95rem; font-weight: 750; } .nwc-wallet-chips { display: flex; - gap: 6px; + gap: 8px; + align-items: center; flex-wrap: wrap; - justify-content: flex-end; - flex-shrink: 0; } .nwc-wallet-chip { display: inline-flex; align-items: center; - font-size: 0.72rem; - font-weight: 700; - border-radius: 999px; - padding: 4px 10px; - white-space: nowrap; + font-size: 0.66rem; + font-weight: 750; + padding: 2px 9px; + border-radius: 99px; + background: rgba(255, 255, 255, 0.05); + border: 1px solid var(--border); + color: var(--text-2); } -.nwc-wallet-chip--balance { - background: rgba(109, 191, 139, 0.14); - color: var(--green); - font-family: 'JetBrains Mono', 'Fira Code', 'Source Code Pro', monospace; -} - -.nwc-wallet-chip--pending { - background: rgba(245, 158, 11, 0.14); - color: #fbbf24; -} +.nwc-wallet-chip--balance { color: var(--accent); border-color: rgba(66, 243, 154, 0.3); } +.nwc-wallet-chip--pending { color: var(--amber); border-color: rgba(233, 182, 74, 0.3); } .nwc-wallet-address { - margin-bottom: 4px; -} - -.nwc-wallet-address .creds-value { - font-size: 0.82rem; - padding: 9px 12px; -} - -.nwc-wallet-address .creds-copy-btn { - margin-top: 5px; - padding: 7px 12px; + font-family: var(--mono); + font-size: 0.72rem; + color: var(--text-3); } .nwc-wallet-actions { display: flex; - align-items: center; - gap: 8px; + gap: 9px; flex-wrap: wrap; margin-top: 12px; - padding-top: 12px; - border-top: 1px solid var(--border-color); } -.nwc-wallet-actions-spacer { - flex: 1; - min-width: 0; -} +.nwc-wallet-actions-spacer { flex: 1; } -.nwc-wallet-actions .matrix-form-back, -.nwc-wallet-actions .btn-close-modal, -.nwc-wallet-actions .matrix-action-btn { - padding: 7px 12px; - font-size: 0.8rem; -} +.nwc-wallet-danger-btn { color: #f2c45e !important; border-color: rgba(233, 182, 74, 0.35) !important; } -.nwc-wallet-danger-btn { - color: #f08a8a; -} +.nwc-header-chip--domain { font-family: var(--mono); } -.nwc-wallet-danger-btn:hover:not(:disabled) { - background-color: rgba(220, 80, 80, 0.14); - color: #ff9d9d; -} - -/* ── NWC LNURL share / download view ───────────────────────────── */ - -.nwc-share-actions { - display: flex; - gap: 8px; - flex-wrap: wrap; - margin-top: 14px; -} - -.nwc-share-actions .matrix-form-back, -.nwc-share-actions .matrix-form-submit { - padding: 8px 12px; - font-size: 0.82rem; +.nwc-header-chip--warn { + color: var(--amber); + border-color: rgba(233, 182, 74, 0.3); + background: rgba(233, 182, 74, 0.08); } .nwc-lnurl-value { - font-size: 0.72rem; - word-break: break-all; + font-family: var(--mono); + font-size: 0.74rem; + color: var(--text); + overflow-wrap: anywhere; } -.nwc-share-formats-hint { - text-align: center; -} +.nwc-toolbar-refresh, +.nwc-domain-required-btn { margin-top: 12px; } -/* ── NWC Liquidity Guide (collapsible) ──────────────────────────── */ +.nwc-intro-header { margin-bottom: 14px; } -.nwc-liquidity-details { - margin-top: 20px; - border: 1px solid var(--border-color); - border-radius: 12px; - background: rgba(255, 255, 255, 0.015); -} - -.nwc-liquidity-summary { - display: flex; - align-items: center; - gap: 12px; - padding: 14px 16px; - cursor: pointer; - list-style: none; - color: var(--text-primary); - font-size: 0.9rem; -} - -.nwc-liquidity-summary::-webkit-details-marker { - display: none; -} - -.nwc-liquidity-summary::after { - content: "▸"; - margin-left: auto; - color: var(--text-dim); - transition: transform 0.15s ease; -} - -.nwc-liquidity-details[open] .nwc-liquidity-summary::after { - transform: rotate(90deg); -} - -.nwc-liquidity-summary:hover { - background: rgba(255, 255, 255, 0.02); - border-radius: 12px; -} - -.nwc-liquidity-icon { - font-size: 22px; -} - -.nwc-liquidity-subtitle { +.nwc-benefit-content strong { display: block; - margin-top: 2px; - font-size: 0.75rem; - font-weight: 400; - color: var(--text-secondary); -} - -.nwc-liquidity-guide-card { - padding: 0 16px 16px; - color: var(--text-primary); -} - -.nwc-liquidity-body { font-size: 0.82rem; + color: var(--text); + margin-bottom: 2px; +} + +.nwc-benefit-content p { + margin: 0; + font-size: 0.76rem; + color: var(--text-2); line-height: 1.55; - margin: 0 0 12px; - color: var(--text-secondary); -} - -.nwc-liquidity-steps { - display: flex; - flex-direction: column; - gap: 10px; - margin-bottom: 14px; -} - -.nwc-liquidity-step { - display: flex; - gap: 10px; - align-items: flex-start; - font-size: 0.85rem; -} - -.nwc-liquidity-step-desc { - margin: 2px 0 0; - color: var(--text-secondary); - font-size: 0.78rem; - line-height: 1.5; } +/* Generic numbered-step circle (NWC guides) */ .step-num { - background: var(--accent-color); - color: #0A1A10; - width: 20px; - height: 20px; + width: 24px; + height: 24px; border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-weight: 700; - font-size: 0.75rem; flex-shrink: 0; - margin-top: 1px; -} - -.nwc-liquidity-action { - background: rgba(109, 191, 139, 0.08); - border-left: 3px solid var(--green); - padding: 10px 12px; - border-radius: 0 8px 8px 0; - font-size: 0.82rem; -} - -.nwc-liquidity-analogy { - margin: 0 0 10px; - font-size: 0.8rem; - line-height: 1.45; + background: var(--accent-dim); + color: var(--accent); + display: grid; + place-items: center; + font-size: 0.72rem; + font-weight: 800; } .nwc-rtl-btn { display: inline-flex; align-items: center; gap: 6px; - background: var(--border-color); - color: var(--text-primary); - border: none; - padding: 8px 14px; - border-radius: 8px; - font-size: 0.8rem; - cursor: pointer; } -.nwc-rtl-btn:hover { - background: #1c2a24; -} - -/* ── NWC Connect Guide (shown after wallet creation) ────────────── */ - -.nwc-connect-guide { - margin-top: 16px; - padding: 16px; - border: 1px solid var(--border-color); +/* NWC Liquidity Guide (collapsible) */ +.nwc-liquidity-details { + margin-top: 20px; + border: 1px solid var(--border); border-radius: 12px; - background: rgba(109, 191, 139, 0.04); + background: rgba(0, 0, 0, 0.12); +} + +.nwc-liquidity-summary { + display: flex; + align-items: center; + gap: 12px; + padding: 13px 15px; + cursor: pointer; + font-size: 0.88rem; + font-weight: 700; +} + +.nwc-liquidity-icon { font-size: 1.25rem; } + +.nwc-liquidity-title { flex: 1; min-width: 0; } + +.nwc-liquidity-subtitle { + display: block; + margin-top: 2px; + font-size: 0.75rem; + font-weight: 400; + color: var(--text-3); +} + +.nwc-liquidity-chevron { transition: transform 0.2s; color: var(--text-3); } + +.nwc-liquidity-details[open] .nwc-liquidity-chevron { transform: rotate(90deg); } + +.nwc-liquidity-body { + font-size: 0.82rem; + line-height: 1.6; + color: var(--text-2); + margin: 0 0 12px; +} + +.nwc-empty-state, +.nwc-disabled-state { text-align: center; padding: 26px 10px; } + +.nwc-empty-icon, +.nwc-disabled-icon { font-size: 1.8rem; margin-bottom: 8px; } + +.nwc-empty-title, +.nwc-disabled-title { font-size: 1rem; font-weight: 750; } + +.nwc-empty-desc, +.nwc-disabled-desc { + margin-top: 6px; + font-size: 0.8rem; + color: var(--text-2); + line-height: 1.6; + max-width: 420px; + margin-left: auto; + margin-right: auto; +} + +/* Connect guide (Zeus) — numbered steps with green circles */ +.nwc-connect-guide { + margin-top: 17px; + border: 1px solid var(--border-strong); + border-radius: 16px; + background: rgba(0, 0, 0, 0.16); + padding: 14px 16px; } .nwc-connect-guide-title { - font-size: 1rem; - font-weight: 700; - color: var(--text-primary); - margin-bottom: 8px; + display: flex; + align-items: center; + gap: 8px; + font-size: 0.92rem; + font-weight: 750; } .nwc-connect-guide-intro { - margin: 0 0 14px; - font-size: 0.85rem; - line-height: 1.55; - color: var(--text-secondary); + margin-top: 7px; + font-size: 0.8rem; + color: var(--text-2); + line-height: 1.62; } .nwc-connect-steps { display: flex; flex-direction: column; gap: 10px; - margin-bottom: 14px; + margin-top: 12px; } .nwc-connect-step { display: flex; - gap: 10px; + gap: 12px; align-items: flex-start; - font-size: 0.86rem; - line-height: 1.5; + font-size: 0.82rem; + color: var(--text-2); + line-height: 1.55; } +.nwc-connect-step strong { color: var(--text); } + .nwc-step-num { - background: var(--accent-color); - color: #0A1A10; - width: 22px; - height: 22px; + width: 24px; + height: 24px; border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-weight: 700; - font-size: 0.78rem; flex-shrink: 0; - margin-top: 2px; + background: var(--accent-dim); + color: var(--accent); + display: grid; + place-items: center; + font-size: 0.72rem; + font-weight: 800; } .nwc-connect-note { - background: rgba(245, 158, 11, 0.08); - border-left: 3px solid #f59e0b; - padding: 10px 12px; - border-radius: 0 8px 8px 0; + margin-top: 13px; + font-size: 0.78rem; + color: var(--text-2); + background: rgba(233, 182, 74, 0.06); + border: 1px solid rgba(233, 182, 74, 0.28); + border-radius: 12px; + padding: 10px 13px; + line-height: 1.6; +} + +.nwc-secret-warning { + margin-top: 14px; + font-size: 0.78rem; + color: var(--amber); + background: rgba(233, 182, 74, 0.07); + border: 1px solid rgba(233, 182, 74, 0.3); + border-radius: 12px; + padding: 10px 13px; + line-height: 1.55; +} + +/* NWC benefits / liquidity education cards */ +.nwc-benefits-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); + gap: 12px; + margin-top: 12px; +} + +.nwc-benefit-item { + border: 1px solid var(--border); + border-radius: 12px; + padding: 12px 14px; + background: rgba(0, 0, 0, 0.12); +} + +.nwc-benefit-icon { font-size: 1.1rem; margin-bottom: 6px; } + +.nwc-liquidity-guide-card { + margin-top: 14px; + border: 1px solid var(--border-strong); + border-radius: 16px; + background: rgba(0, 0, 0, 0.16); + padding: 14px 16px; +} + +.nwc-liquidity-steps { + display: flex; + flex-direction: column; + gap: 10px; + margin: 12px 0; +} + +.nwc-liquidity-step { + display: flex; + gap: 12px; + align-items: flex-start; + font-size: 0.82rem; + color: var(--text-2); + line-height: 1.55; +} + +.nwc-liquidity-step strong { color: var(--text); } + +.nwc-liquidity-step-desc { margin-top: 2px; font-size: 0.77rem; color: var(--text-3); } + +.nwc-liquidity-action { margin-top: 12px; } + +.nwc-liquidity-analogy { + font-size: 0.78rem; + color: var(--text-2); + line-height: 1.6; +} + +.nwc-share-actions { + display: flex; + gap: 9px; + flex-wrap: wrap; + justify-content: center; + margin-top: 14px; +} + +.nwc-share-formats-hint { + margin-top: 8px; +} + +.nwc-domain-required { + text-align: center; + padding: 22px 10px; +} + +.nwc-domain-required-body p { + font-size: 0.82rem; + color: var(--text-2); + line-height: 1.6; + margin-top: 8px; +} + +/* ── Reboot / security-reset fullscreen overlays ────────────────── */ + +.reboot-overlay, +.security-reset-overlay { + position: fixed; + inset: 0; + z-index: 300; + background: radial-gradient(1000px 640px at 50% 18%, #17211c, #0b100d 72%); + display: flex; + align-items: center; + justify-content: center; + padding: 26px; + opacity: 0; + visibility: hidden; + transition: opacity 0.4s, visibility 0.4s; +} + +.reboot-overlay.visible, +.security-reset-overlay.visible { opacity: 1; visibility: visible; } + +.reboot-card { + text-align: center; + max-width: 470px; + background: var(--elevated); + border: 1px solid var(--border-strong); + border-radius: var(--radius-dialog); + box-shadow: var(--shadow-pop); + padding: 38px 36px; +} + +.reboot-icon { + width: 68px; + height: 68px; + margin: 0 auto 24px; + border-radius: 50%; + border: 4px solid rgba(66, 243, 154, 0.14); + border-top-color: var(--accent); + animation: spin 1s linear infinite; +} + +.reboot-title { + font-size: 1.35rem; + font-weight: 800; + letter-spacing: -0.01em; +} + +.reboot-message { + color: var(--text-2); + font-size: 0.9rem; + line-height: 1.7; + margin-top: 12px; +} + +.reboot-dots { + display: flex; + gap: 8px; + justify-content: center; + margin-top: 22px; +} + +.reboot-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--accent); + animation: reboot-dot-pulse 1.2s ease-in-out infinite; +} + +.reboot-dot:nth-child(2) { animation-delay: 0.2s; } +.reboot-dot:nth-child(3) { animation-delay: 0.4s; } + +@keyframes reboot-dot-pulse { + 0%, 100% { opacity: 0.25; transform: scale(0.85); } + 50% { opacity: 1; transform: scale(1.1); } +} + +.reboot-submessage { + margin-top: 20px; + font-family: var(--mono); + font-size: 0.74rem; + color: var(--text-3); +} + +.reboot-error-actions { + display: flex; + gap: 10px; + justify-content: center; + margin-top: 22px; +} + +/* ── Systems Operational modal (dashboard.js) ───────────────────── */ + +.sysmodal-card { + margin-top: 14px; + border: 1px solid var(--border-strong); + border-radius: 16px; + background: rgba(0, 0, 0, 0.16); + padding: 14px 16px; +} + +.sysmodal-card:first-child { margin-top: 0; } + +.sysmodal-card-title { + display: flex; + align-items: center; + gap: 8px; + font-size: 0.92rem; + font-weight: 750; +} + +.sysmodal-card-title svg { + width: 16px; + height: 16px; + color: var(--text-2); + flex-shrink: 0; +} + +.sysstep { + display: flex; + gap: 12px; + align-items: flex-start; + margin-top: 11px; +} + +.sysnum { + width: 24px; + height: 24px; + border-radius: 50%; + flex-shrink: 0; + background: var(--accent-dim); + color: var(--accent); + display: grid; + place-items: center; + font-size: 0.72rem; + font-weight: 800; +} + +.sysstep-x { flex: 1; min-width: 0; } + +.sysstep-t { + font-size: 0.82rem; + font-weight: 700; + color: var(--text); + line-height: 1.45; +} + +.sysstep-sub { + font-weight: 400; + color: var(--text-3); + font-size: 0.77rem; +} + +.sysval { + display: flex; + align-items: center; + gap: 8px; + margin-top: 7px; + min-width: 0; + background: var(--inset); + border: 1px solid var(--border); + border-radius: 10px; + padding: 6px 12px; +} + +.sysval-text { + flex: 1; + min-width: 0; + font-family: var(--mono); + font-size: 0.76rem; + color: var(--text); + text-align: left; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.sysnote { + margin-top: 13px; + border: 1px solid var(--border-strong); + border-radius: 12px; + padding: 10px 13px; + background: rgba(0, 0, 0, 0.12); +} + +.sysnote-title { + display: flex; + align-items: center; + gap: 8px; + font-size: 0.85rem; + font-weight: 750; +} + +.sysnote-title svg { + width: 15px; + height: 15px; + color: var(--text-2); + flex-shrink: 0; +} + +.sysnote-desc { font-size: 0.8rem; + color: var(--text-2); + line-height: 1.62; + margin-top: 5px; +} + +.sysfineprint { + font-size: 0.72rem; + color: var(--text-3); + margin-top: 10px; line-height: 1.5; - color: var(--text-secondary); -} - -.nwc-connect-note strong { - color: #fbbf24; -} - -/* ── Narrow screens ─────────────────────────────────────────────── */ - -@media (max-width: 720px) { - .nwc-tabs-bar { - align-items: stretch; - flex-direction: column; - } - - .nwc-tabs-btns { - justify-content: stretch; - } - - .nwc-tab-btn { - flex: 1; - padding: 8px 10px; - } - - .nwc-wallet-list { - grid-template-columns: 1fr; - } - - .nwc-wallet-card-head { - flex-direction: column; - } - - .nwc-wallet-chips { - justify-content: flex-start; - } - - .nwc-wallet-actions-spacer { - display: none; - } } diff --git a/app/sovran_systemsos_web/static/css/onboarding.css b/app/sovran_systemsos_web/static/css/onboarding.css index 5b477c9..3a1f454 100644 --- a/app/sovran_systemsos_web/static/css/onboarding.css +++ b/app/sovran_systemsos_web/static/css/onboarding.css @@ -7,7 +7,7 @@ justify-content: flex-start; min-height: 100vh; background: - radial-gradient(ellipse at top, rgba(94, 173, 138, 0.04) 0%, transparent 50%), + radial-gradient(ellipse at top, rgba(62, 207, 142, 0.04) 0%, transparent 50%), var(--bg-color); padding: 24px 16px 48px; overflow-y: auto; @@ -70,13 +70,13 @@ .onboarding-step-dot.active { background-color: var(--accent-color); border-color: var(--accent-color); - color: #0A1A10; + color: #04220f; } .onboarding-step-dot.completed { background-color: var(--green); border-color: var(--green); - color: #0A1A10; + color: #04220f; } .onboarding-step-connector { @@ -139,7 +139,7 @@ /* Cards */ .onboarding-card { - background-color: rgba(14, 16, 15, 0.65); + background-color: rgba(18, 24, 20, 0.65); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); border: 1px solid rgba(255, 255, 255, 0.06); @@ -184,10 +184,10 @@ font-size: 0.82rem; font-weight: 700; color: var(--accent-color); - background-color: rgba(94, 173, 138, 0.10); + background-color: rgba(62, 207, 142, 0.10); padding: 3px 10px; border-radius: 20px; - border: 1px solid rgba(94, 173, 138, 0.25); + border: 1px solid rgba(62, 207, 142, 0.25); } /* Step header */ @@ -371,8 +371,8 @@ .onboarding-port-warn { padding: 10px 14px; - background-color: rgba(229, 165, 10, 0.1); - border: 1px solid rgba(229, 165, 10, 0.35); + background-color: rgba(233, 182, 74, 0.1); + border: 1px solid rgba(233, 182, 74, 0.35); border-radius: 8px; font-size: 0.85rem; color: var(--yellow); @@ -409,8 +409,8 @@ .onboarding-creds-notice { padding: 12px 16px; - background-color: rgba(94, 173, 138, 0.08); - border: 1px solid rgba(94, 173, 138, 0.20); + background-color: rgba(62, 207, 142, 0.08); + border: 1px solid rgba(62, 207, 142, 0.20); border-radius: 8px; font-size: 0.85rem; color: var(--text-secondary); @@ -503,8 +503,8 @@ font-size: 0.72rem; padding: 2px 8px; border-radius: 4px; - background-color: rgba(94, 173, 138, 0.08); - border: 1px solid rgba(94, 173, 138, 0.20); + background-color: rgba(62, 207, 142, 0.08); + border: 1px solid rgba(62, 207, 142, 0.20); color: var(--accent-color); cursor: pointer; white-space: nowrap; @@ -512,7 +512,7 @@ } .onboarding-cred-reveal-btn:hover { - background-color: rgba(94, 173, 138, 0.15); + background-color: rgba(62, 207, 142, 0.15); } /* Completion checklist (Step 5) */ @@ -671,7 +671,7 @@ } .onboarding-password-toggle:hover { - background-color: rgba(94, 173, 138, 0.10); + background-color: rgba(62, 207, 142, 0.10); border-color: var(--accent-color); } @@ -683,8 +683,8 @@ .onboarding-password-warning { padding: 10px 14px; - background-color: rgba(229, 165, 10, 0.1); - border: 1px solid rgba(229, 165, 10, 0.35); + background-color: rgba(233, 182, 74, 0.1); + border: 1px solid rgba(233, 182, 74, 0.35); border-radius: 8px; font-size: 0.85rem; color: var(--yellow); @@ -694,8 +694,8 @@ .onboarding-password-success { padding: 12px 16px; - background-color: rgba(94, 173, 138, 0.10); - border: 1px solid rgba(94, 173, 138, 0.30); + background-color: rgba(62, 207, 142, 0.10); + border: 1px solid rgba(62, 207, 142, 0.30); border-radius: 8px; font-size: 0.92rem; color: var(--green); @@ -748,7 +748,7 @@ } .reboot-card { - background-color: rgba(14, 16, 15, 0.8); + background-color: rgba(18, 24, 20, 0.8); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); border: 1px solid rgba(255, 255, 255, 0.08); diff --git a/app/sovran_systemsos_web/static/css/security.css b/app/sovran_systemsos_web/static/css/security.css index 38d0c8e..9ad17b7 100644 --- a/app/sovran_systemsos_web/static/css/security.css +++ b/app/sovran_systemsos_web/static/css/security.css @@ -28,7 +28,7 @@ .security-warning-box { background-color: rgba(180, 40, 40, 0.10); - border-left: 3px solid #c94040; + border-left: 3px solid var(--red); border-radius: 6px; padding: 12px 14px; margin-bottom: 14px; @@ -78,7 +78,7 @@ } .btn-danger { - background-color: #c94040; + background-color: var(--red); color: #fff; border: none; border-radius: 6px; @@ -90,7 +90,7 @@ } .btn-danger:hover:not(:disabled) { - background-color: #a83030; + background-color: var(--red); } .btn-danger:disabled { @@ -105,8 +105,8 @@ } .security-status-info { color: var(--text-secondary); } -.security-status-ok { color: #6DBF8B; } -.security-status-error { color: #e05252; } +.security-status-ok { color: var(--accent); } +.security-status-error { color: var(--red); } /* ── Verify System Integrity ─────────────────────────────────────── */ @@ -160,7 +160,7 @@ .security-verify-link { font-size: 0.78rem; - color: var(--accent-color, #6DBF8B); + color: var(--accent-color, var(--accent)); text-decoration: none; } @@ -174,13 +174,13 @@ } .security-verify-pass { - background-color: rgba(109, 191, 139, 0.15); - color: #6DBF8B; + background-color: rgba(66, 243, 154, 0.15); + color: var(--accent); } .security-verify-fail { background-color: rgba(224, 82, 82, 0.15); - color: #e05252; + color: var(--red); } .security-verify-errors { @@ -221,12 +221,11 @@ display: none; position: fixed; inset: 0; - background-color: rgba(6, 8, 7, 0.94); - backdrop-filter: blur(8px); - -webkit-backdrop-filter: blur(8px); + background: radial-gradient(1000px 640px at 50% 18%, #17211c, #0b100d 72%); z-index: 1000; align-items: center; justify-content: center; + padding: 26px; animation: security-reset-fade-in 0.35s ease-out; } @@ -253,13 +252,13 @@ } .security-reset-password-box { - font-family: monospace; + font-family: var(--mono); font-size: 1.35rem; font-weight: 700; color: var(--text-primary); - background: rgba(109, 191, 139, 0.10); - border: 1.5px solid rgba(109, 191, 139, 0.35); - border-radius: 8px; + background: var(--accent-dim); + border: 1.5px solid rgba(66, 243, 154, 0.35); + border-radius: 14px; padding: 14px 24px; letter-spacing: 0.04em; text-align: center; @@ -277,10 +276,11 @@ } .security-reset-reboot-btn { - background-color: #6DBF8B; - color: #0a0c0b; + background: linear-gradient(180deg, #3fd68e, #1ea263); + color: #04220f; border: none; - border-radius: 7px; + border-radius: 99px; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28), 0 6px 18px rgba(35, 199, 124, 0.22); padding: 11px 22px; font-size: 0.88rem; font-weight: 700; @@ -290,7 +290,7 @@ } .security-reset-reboot-btn:hover:not(:disabled) { - background-color: #5aab78; + background-color: var(--accent); } .security-reset-reboot-btn:disabled { @@ -303,11 +303,12 @@ .security-first-login-banner { display: flex; align-items: center; - justify-content: space-between; gap: 12px; - padding: 12px 18px; - background-color: rgba(94, 173, 138, 0.08); - border-bottom: 2px solid rgba(94, 173, 138, 0.25); + padding: 12px 16px; + margin-bottom: 24px; + background: rgba(233, 182, 74, 0.08); + border: 1px solid rgba(233, 182, 74, 0.30); + border-radius: 16px; color: var(--text-primary); } @@ -333,19 +334,21 @@ .security-banner-dismiss { background: none; - border: 1px solid var(--border-color); - border-radius: 4px; + border: 1px solid var(--border-strong); + border-radius: 99px; cursor: pointer; font-size: 0.9rem; color: var(--text-secondary); - padding: 2px 7px; + padding: 4px 10px; flex-shrink: 0; line-height: 1.4; - transition: background-color 0.15s; + transition: 0.15s; } .security-banner-dismiss:hover { - background-color: rgba(0,0,0,0.08); + color: var(--text); + border-color: rgba(255, 255, 255, 0.26); + background: rgba(255, 255, 255, 0.04); } /* ── Legacy security inline warning banner ───────────────────────── */ @@ -357,14 +360,14 @@ padding: 12px 14px; margin-bottom: 12px; background-color: rgba(180, 100, 0, 0.12); - border-left: 3px solid #c97a00; + border-left: 3px solid #c98d08; border-radius: 6px; color: var(--text-primary); } .security-inline-icon { font-size: 1rem; - color: #e69000; + color: #e0a010; flex-shrink: 0; } @@ -378,9 +381,9 @@ display: inline-block; font-size: 0.82rem; font-weight: 600; - color: #e69000; + color: #e0a010; text-decoration: none; - border: 1px solid #c97a00; + border: 1px solid #c98d08; border-radius: 4px; padding: 4px 10px; align-self: flex-start; @@ -447,9 +450,9 @@ .pw-credentials-note { font-size: 0.78rem; - color: #c97a00; + color: #c98d08; background-color: rgba(180, 100, 0, 0.10); - border-left: 2px solid #c97a00; + border-left: 2px solid #c98d08; border-radius: 4px; padding: 7px 10px; margin-bottom: 12px; diff --git a/app/sovran_systemsos_web/static/css/support.css b/app/sovran_systemsos_web/static/css/support.css index 503ab35..698a6a5 100644 --- a/app/sovran_systemsos_web/static/css/support.css +++ b/app/sovran_systemsos_web/static/css/support.css @@ -104,7 +104,7 @@ } .support-steps code { - background-color: rgba(94, 173, 138, 0.10); + background-color: rgba(62, 207, 142, 0.10); padding: 2px 6px; border-radius: 4px; font-size: 0.82rem; @@ -116,7 +116,7 @@ padding: 12px; border-radius: var(--radius-btn); background-color: var(--accent-color); - color: #0A1A10; + color: #04220f; font-size: 0.95rem; font-weight: 700; margin-bottom: 10px; @@ -146,7 +146,7 @@ padding: 12px; border-radius: var(--radius-btn); background-color: var(--accent-color); - color: #0A1A10; + color: #04220f; font-size: 0.95rem; font-weight: 700; margin-top: 16px; @@ -168,7 +168,7 @@ } .support-btn-auditlog:hover:not(:disabled) { - background-color: #1c2a24; + background-color: var(--card-hover); } .support-fine-print { @@ -219,13 +219,13 @@ } .support-wallet-protected { - background-color: rgba(109, 191, 139, 0.06); - border-color: rgba(109, 191, 139, 0.3); + background-color: rgba(66, 243, 154, 0.06); + border-color: rgba(66, 243, 154, 0.3); } .support-wallet-unlocked { - background-color: rgba(229, 165, 10, 0.06); - border-color: rgba(229, 165, 10, 0.3); + background-color: rgba(233, 182, 74, 0.06); + border-color: rgba(233, 182, 74, 0.3); } .support-wallet-warning { @@ -290,7 +290,7 @@ padding: 8px 16px; border-radius: var(--radius-btn); background-color: var(--yellow); - color: #0A1A10; + color: #04220f; font-size: 0.82rem; font-weight: 700; } @@ -310,7 +310,7 @@ } .support-btn-wallet-lock:hover:not(:disabled) { - background-color: #529E7E; + background-color: var(--accent); } /* ── Audit log ───────────────────────────────────────────────────── */ @@ -324,7 +324,7 @@ .support-audit-log { max-height: 200px; overflow-y: auto; - background-color: #0c0f0e; + background-color: var(--inset); border-radius: 8px; padding: 10px 14px; } diff --git a/app/sovran_systemsos_web/static/css/tiles.css b/app/sovran_systemsos_web/static/css/tiles.css index 5c0a4e9..f4dedbc 100644 --- a/app/sovran_systemsos_web/static/css/tiles.css +++ b/app/sovran_systemsos_web/static/css/tiles.css @@ -1,473 +1,178 @@ -/* ── Service tile card (status-only) ─────────────────────────────── */ - -.dashboard-loading { - min-height: 180px; - display: flex; - align-items: center; - justify-content: center; - gap: 10px; - color: var(--text-secondary); - font-size: 0.9rem; -} - -.dashboard-loading-spinner { - width: 16px; - height: 16px; - border: 2px solid var(--border-color); - border-top-color: var(--accent-color); - border-radius: 50%; - animation: dashboard-loading-spin 0.8s linear infinite; -} - -@keyframes dashboard-loading-spin { - to { transform: rotate(360deg); } -} +/* ── Service tiles ──────────────────────────────────────────────── */ .service-tile { - width: 160px; - min-height: 130px; - background-color: var(--card-color); - border: 1px solid var(--border-color); - backdrop-filter: blur(8px); - -webkit-backdrop-filter: blur(8px); - border-radius: var(--radius-card); - box-shadow: var(--shadow-card); + background: var(--card); + border: 1px solid var(--border); + border-radius: 20px; + overflow: hidden; + padding: 21px 12px 17px; display: flex; flex-direction: column; align-items: center; - justify-content: center; - padding: 20px 12px 18px; - gap: 0; - transition: box-shadow 0.2s, border-color 0.2s; - position: relative; cursor: pointer; + position: relative; + transition: transform 0.18s, border-color 0.18s, background 0.18s, box-shadow 0.18s, opacity 0.18s; + animation: tileIn 0.45s cubic-bezier(0.2, 0.8, 0.3, 1) backwards; +} + +@keyframes tileIn { + from { opacity: 0; transform: translateY(10px) scale(0.96); } } .service-tile:hover { + transform: translateY(-4px); + border-color: var(--border-strong); + background: var(--card-hover); box-shadow: var(--shadow-hover); - border-color: var(--accent-color); } -.service-tile.disabled { - opacity: 0.45; +.service-tile:active { transform: translateY(-1px) scale(0.985); } + +.service-tile:focus-visible { + outline: 2px solid rgba(66, 243, 154, 0.45); + outline-offset: 2px; } +.service-tile.disabled { opacity: 0.55; } + .tile-icon { - width: 48px; - height: 48px; + width: 62px; + height: 62px; + display: block; object-fit: contain; - margin-bottom: 10px; } .tile-icon-fallback { - width: 48px; - height: 48px; + width: 62px; + height: 62px; display: flex; align-items: center; justify-content: center; - background-color: var(--border-color); - border-radius: 12px; - color: var(--text-dim); - font-size: 1.5rem; - margin-bottom: 10px; + color: var(--text-3); + font-size: 1.2rem; + font-weight: 700; } .tile-name { - font-size: 0.88rem; - font-weight: 600; + margin-top: 13px; + font-size: 0.86rem; + font-weight: 650; text-align: center; - color: var(--text-primary); - line-height: 1.3; - max-width: 140px; - word-break: break-word; - hyphens: auto; - min-height: 1.3em; + line-height: 1.25; + min-height: 2.5em; display: flex; align-items: center; justify-content: center; + color: var(--text); + max-width: 100%; } +/* Status pill — dot + label inside a rounded pill */ .tile-status { - font-size: 0.75rem; - margin-top: 8px; - display: flex; + margin-top: 9px; + display: inline-flex; align-items: center; - gap: 5px; - color: var(--text-secondary); + gap: 6px; + font-size: 0.69rem; + font-weight: 700; + padding: 3px 10px; + border-radius: 99px; + letter-spacing: 0.01em; + white-space: nowrap; + background: rgba(255, 255, 255, 0.05); + color: var(--text-2); } -.tile-version { - font-size: 0.7rem; - color: var(--text-dim); - margin-top: 2px; - text-align: center; -} +.status-text { line-height: 1; } .status-dot { - width: 8px; - height: 8px; + width: 6px; + height: 6px; border-radius: 50%; + background: var(--text-3); flex-shrink: 0; - background-color: var(--grey); } -.status-dot.active { background-color: var(--green); } -.status-dot.inactive { background-color: var(--red); } -.status-dot.loading { background-color: var(--yellow); animation: pulse-badge 1s infinite; } -.status-dot.failed { background-color: var(--red); } -.status-dot.disabled { background-color: var(--grey); } -.status-dot.needs-attention { background-color: var(--yellow); } -.status-dot.syncing { background-color: #f5a623; animation: pulse-badge 1.5s infinite; } -.status-dot.checking-reachability { background-color: var(--accent-color); animation: pulse-badge 1s infinite; } +.status-dot.active { background: var(--accent); } +.status-dot.needs-attention { background: var(--amber); } +.status-dot.failed { background: var(--red); } +.status-dot.inactive, +.status-dot.disabled { background: var(--text-3); } +.status-dot.syncing, +.status-dot.loading, +.status-dot.checking-reachability { background: var(--blue); animation: tile-dot-pulse 1.4s ease-in-out infinite; } +.status-dot.unknown { background: var(--text-3); } -/* ── Bitcoin IBD sync progress bar ──────────────────────────────── */ +@keyframes tile-dot-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.35; } +} + +/* Progressive enhancement: tint the whole pill on modern browsers */ +.tile-status:has(.status-dot.active) { background: var(--accent-dim); color: var(--accent); } +.tile-status:has(.status-dot.needs-attention) { background: rgba(233, 182, 74, 0.10); color: var(--amber); } +.tile-status:has(.status-dot.failed) { background: rgba(246, 97, 81, 0.10); color: var(--red); } +.tile-status:has(.status-dot.syncing), +.tile-status:has(.status-dot.loading), +.tile-status:has(.status-dot.checking-reachability) { background: rgba(120, 174, 237, 0.10); color: var(--blue); } + +.support-status-label { + font-size: 0.69rem; + font-weight: 700; + color: var(--text-2); +} + +/* ── Bitcoin IBD sync tile ──────────────────────────────────────── */ .tile-sync-container { - display: flex; - flex-direction: column; - align-items: center; - gap: 4px; + margin-top: 12px; width: 100%; - margin-top: 6px; + padding: 0 6px; } .tile-sync-label { - font-size: 0.72rem; - color: #f5a623; - font-weight: 600; + font-size: 0.66rem; + font-weight: 750; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--amber); text-align: center; - white-space: nowrap; } .tile-sync-bar-row { display: flex; align-items: center; - gap: 5px; - width: 100%; + gap: 8px; + margin-top: 7px; } .tile-sync-bar-track { flex: 1; - height: 6px; - background-color: var(--border-color); - border-radius: 3px; + height: 7px; + border-radius: 99px; + background: rgba(255, 255, 255, 0.07); overflow: hidden; } .tile-sync-bar-fill { height: 100%; - background-color: #f5a623; - border-radius: 3px; - transition: width 0.6s ease; - min-width: 2px; + border-radius: 99px; + background: linear-gradient(90deg, #42f39a, #1aa45d); + transition: width 0.6s cubic-bezier(0.3, 0.7, 0.3, 1); } .tile-sync-percent { - font-size: 0.72rem; + font-family: var(--mono); + font-size: 0.7rem; font-weight: 700; - color: #f5a623; - white-space: nowrap; - min-width: 2.5em; - text-align: right; + color: var(--amber); + flex-shrink: 0; } .tile-sync-eta { - font-size: 0.68rem; - color: var(--text-dim); + margin-top: 6px; + font-family: var(--mono); + font-size: 0.66rem; + color: var(--text-3); text-align: center; - white-space: nowrap; -} - - -/* ── Service detail modal sections ───────────────────────────────── */ - -.svc-detail-section { - margin-bottom: 20px; - padding-bottom: 16px; - border-bottom: 1px solid var(--border-color); -} - -.svc-detail-section:last-child { - border-bottom: none; - margin-bottom: 0; - padding-bottom: 0; -} - -.svc-detail-section-title { - font-size: 0.78rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--text-dim); - margin-bottom: 10px; -} - -.svc-detail-desc { - font-size: 0.9rem; - color: var(--text-secondary); - line-height: 1.6; -} - -.svc-detail-status { - display: flex; - align-items: center; - gap: 8px; - font-size: 0.9rem; - font-weight: 600; - color: var(--text-primary); -} - -/* ── Service detail: Domain ──────────────────────────────────────── */ - -.svc-detail-domain-value { - font-size: 0.9rem; - color: var(--text-primary); - font-weight: 600; -} - -.tile-domain-label--ok { - color: var(--green); - font-weight: 600; -} - -.tile-domain-label--warn { - color: var(--yellow); - font-weight: 600; -} - -.tile-domain-label--error { - color: var(--red); - font-weight: 600; -} - -/* ── Service detail: Port table ──────────────────────────────────── */ - -.svc-detail-port-table { - width: 100%; - border-collapse: collapse; - font-size: 0.82rem; - margin-top: 8px; -} - -.svc-detail-port-table th { - text-align: left; - color: var(--text-dim); - font-weight: 600; - font-size: 0.72rem; - text-transform: uppercase; - letter-spacing: 0.04em; - padding: 6px 10px; - border-bottom: 1px solid var(--border-color); -} - -.svc-detail-port-table td { - padding: 8px 10px; - border-bottom: 1px solid rgba(30, 45, 39, 0.6); - color: var(--text-primary); -} - -.svc-detail-port-table tr:last-child td { - border-bottom: none; -} - -.svc-detail-port-table-port { - font-family: 'JetBrains Mono', 'Fira Code', 'Source Code Pro', monospace; - font-weight: 600; - color: var(--accent-color); -} - -.svc-detail-port-table-proto { - text-transform: uppercase; - color: var(--text-secondary); -} - -.svc-detail-port-table-desc { - color: var(--text-secondary); -} - -.svc-detail-port-table-status { - font-weight: 600; -} - -.port-status-listening { color: var(--green); } -.port-status-open { color: var(--yellow); } -.port-status-closed { color: var(--red); } -.port-status-unknown { color: var(--text-dim); } - -/* ── Service detail: Troubleshoot box ────────────────────────────── */ - -.svc-detail-troubleshoot { - margin-top: 12px; - padding: 14px 16px; - background-color: rgba(229, 165, 10, 0.08); - border: 1px solid rgba(229, 165, 10, 0.3); - border-radius: 10px; - font-size: 0.85rem; - color: var(--text-secondary); - line-height: 1.6; -} - -.svc-detail-troubleshoot strong { - color: var(--yellow); -} - -.svc-detail-troubleshoot ol { - margin-top: 8px; - padding-left: 20px; -} - -.svc-detail-troubleshoot li { - margin-bottom: 4px; -} - -.svc-detail-troubleshoot code { - background-color: rgba(94, 173, 138, 0.10); - padding: 2px 6px; - border-radius: 4px; - font-size: 0.82rem; - color: var(--accent-color); -} - -.svc-detail-troubleshoot a { - color: var(--accent-color); - text-decoration: none; -} - -.svc-detail-troubleshoot a:hover { - text-decoration: underline; -} - -/* ── Service detail: Domain configure button ─────────────────────── */ - -.svc-detail-domain-btn { - margin-top: 12px; -} - -/* ── Service detail: Addon feature toggle ────────────────────────── */ - -.svc-detail-addon-row { - display: flex; - align-items: center; - gap: 14px; - margin-top: 12px; -} - -.svc-detail-addon-status { - font-size: 0.88rem; - font-weight: 700; -} - -.addon-status--on { - color: var(--green); -} - -.addon-status--off { - color: var(--text-dim); -} - -.svc-detail-option-card { - padding: 16px; - background: rgba(94, 173, 138, 0.06); - border: 1px solid rgba(94, 173, 138, 0.24); - border-radius: 10px; -} - -.svc-detail-option-list { - margin: 10px 0 0; - padding-left: 20px; - color: var(--text-secondary); - font-size: 0.84rem; - line-height: 1.55; -} - -.svc-detail-option-list li { - margin-bottom: 5px; -} - -.svc-detail-option-privacy { - margin-top: 12px; - padding: 10px 12px; - border-left: 3px solid var(--accent-color); - background: rgba(94, 173, 138, 0.08); - border-radius: 6px; - color: var(--text-secondary); - font-size: 0.82rem; - line-height: 1.5; -} - -.svc-detail-option-privacy strong { - color: var(--accent-color); -} - -.svc-detail-related-feature-btn:disabled { - cursor: not-allowed; - opacity: 0.55; -} - -.feature-conflict-warning { - margin-top: 8px; - margin-bottom: 8px; - padding: 10px 14px; - background-color: rgba(229, 165, 10, 0.1); - border: 1px solid rgba(229, 165, 10, 0.3); - border-radius: 8px; - font-size: 0.82rem; - color: var(--yellow); - font-weight: 600; -} - -.btn-warning { - background-color: #d97706; - color: #fff; -} - -.btn-warning:hover:not(:disabled) { - background-color: #b45309; -} - -.svc-detail-restart-section { - border-top: 1px solid var(--border-color); - padding-top: 16px; -} - -.svc-detail-restart-btn { - margin-top: 8px; -} - -.svc-detail-restart-result { - margin-top: 12px; - padding: 12px 16px; - border-radius: 8px; - font-size: 0.88rem; - line-height: 1.5; - display: none; -} - -.svc-detail-restart-result.success { - background-color: rgba(109, 191, 139, 0.12); - border: 1px solid var(--green); - color: var(--green); - display: block; -} - -.svc-detail-restart-result.error { - background-color: rgba(239, 68, 68, 0.12); - border: 1px solid #ef4444; - color: #f87171; - display: block; -} - - -/* ── Desktop launch buttons ──────────────────────────────────────── */ - -.svc-detail-launch-row { - display: flex; - gap: 10px; - flex-wrap: wrap; -} - -.svc-detail-launch-btn { - font-size: 0.85rem; - padding: 8px 18px; - cursor: pointer; } diff --git a/app/sovran_systemsos_web/static/js/dashboard.js b/app/sovran_systemsos_web/static/js/dashboard.js new file mode 100644 index 0000000..fc1e290 --- /dev/null +++ b/app/sovran_systemsos_web/static/js/dashboard.js @@ -0,0 +1,333 @@ +"use strict"; + +/* ── The Hub dashboard chrome ────────────────────────────────────── + Category navigation, service search, status widgets, and the + Systems Operational modal (router ports 80/443 + the live domain + diagnostics checklist). + + Everything lives in an IIFE so no globals leak into the other + Hub scripts; tiles.js calls window.dashboardServicesUpdated() + whenever the service list refreshes. */ + +(function () { + + var $nav = document.getElementById("sidebar-nav"); + var $pageTitle = document.getElementById("page-title"); + var $widgets = document.getElementById("widgets"); + var $search = document.getElementById("search-input"); + var $sysModal = document.getElementById("systems-modal"); + var $sysBody = document.getElementById("systems-body"); + + var _cat = "all"; + var _query = ""; + + var CAT_ICONS = { + "all": "g-grid", + "infrastructure": "g-server", + "bitcoin-base": "g-btc-sym", + "bitcoin-apps": "g-btc-sym", + "communication": "g-chat", + "apps": "g-dots", + "nostr": "g-antenna", + "other": "g-dots" + }; + + var CAT_FALLBACK_LABELS = { + "infrastructure": "Infrastructure", + "bitcoin-base": "Bitcoin", + "bitcoin-apps": "Bitcoin Apps", + "communication": "Communication", + "apps": "Personal Apps", + "nostr": "Nostr", + "other": "Other" + }; + + /* Units that serve a domain — used to find a live diagnostics + checklist for the Systems Operational modal (order matters only + for which service is polled first). */ + var DOMAIN_UNITS = [ + "matrix-synapse.service", + "btcpayserver.service", + "vaultwarden.service", + "phpfpm-nextcloud.service", + "phpfpm-wordpress.service", + "haven-relay.service", + "livekit.service", + "albyhub.service" + ]; + + function icon(id) { + return ''; + } + + function visibleServices() { + var services = (typeof _servicesCache !== "undefined" && _servicesCache) ? _servicesCache : []; + return services.filter(function (s) { + return s.category !== "support" && s.type !== "support"; + }); + } + + /* ── Category navigation ─────────────────────────────────────── */ + + function renderNav() { + if (!$nav) return; + var counts = {}; + var order = []; + visibleServices().forEach(function (s) { + var cat = s.category || "other"; + if (!counts[cat]) { counts[cat] = 0; order.push(cat); } + counts[cat]++; + }); + var total = visibleServices().length; + + var html = '
'; + html += navItem("all", "All services", total); + order.forEach(function (cat) { + html += navItem(cat, catLabel(cat), counts[cat]); + }); + $nav.innerHTML = html; + + $nav.querySelectorAll(".nav-item").forEach(function (btn) { + btn.addEventListener("click", function () { + _cat = btn.dataset.cat; + $nav.querySelectorAll(".nav-item").forEach(function (b) { + b.classList.toggle("active", b === btn); + }); + if ($pageTitle) { + $pageTitle.textContent = _cat === "all" ? "Dashboard" : catLabel(_cat); + } + applyFilter(); + }); + }); + } + + function navItem(cat, label, count) { + return ''; + } + + function catLabel(cat) { + if (typeof _categoryLabels !== "undefined" && _categoryLabels[cat]) return _categoryLabels[cat]; + return CAT_FALLBACK_LABELS[cat] || cat; + } + + /* ── Filtering (category + search, applied together) ─────────── */ + + function applyFilter() { + var area = document.getElementById("tiles-area"); + if (!area) return; + var q = _query.trim().toLowerCase(); + area.querySelectorAll(".category-section").forEach(function (section) { + var catMatch = (_cat === "all") || (section.dataset.category === _cat); + var anyVisible = false; + section.querySelectorAll(".service-tile").forEach(function (tile) { + var nameEl = tile.querySelector(".tile-name"); + var name = nameEl ? nameEl.textContent.toLowerCase() : ""; + var match = catMatch && (!q || name.indexOf(q) !== -1); + tile.style.display = match ? "" : "none"; + if (match) anyVisible = true; + }); + section.style.display = (catMatch && (anyVisible || !q)) ? "" : "none"; + }); + } + + if ($search) { + $search.addEventListener("input", function () { + _query = $search.value; + applyFilter(); + }); + } + + /* ── Status widgets ──────────────────────────────────────────── */ + + function renderWidgets() { + if (!$widgets) return; + var services = visibleServices(); + if (!services.length) { $widgets.style.display = "none"; return; } + + var running = 0, attention = 0, off = 0; + services.forEach(function (s) { + if (!s.enabled) { off++; return; } + var h = s.health || s.status; + if (h === "needs_attention" || h === "failed") attention++; + else running++; + }); + + var html = ""; + + /* Systems operational */ + var sub = '' + running + ' running'; + if (attention) sub += ' · ' + attention + ' needs attention'; + if (off) sub += ' · ' + off + ' off'; + var attentionTitle = attention ? "Systems need attention" : "Systems operational"; + html += + ''; + + /* Bitcoin Core sync */ + var btc = null; + services.forEach(function (s) { + if (s.sync_ibd && s.enabled) btc = s; + }); + if (btc) { + var pct = Math.round((btc.sync_progress || 0) * 100); + var blocks = btc.sync_blocks ? btc.sync_blocks.toLocaleString() : "—"; + var eta = (typeof _calcBtcEta === "function") ? _calcBtcEta(btc.unit + "::" + btc.name, btc.sync_progress || 0) : ""; + html += + ''; + } + + $widgets.innerHTML = html; + $widgets.style.display = ""; + + var wSys = document.getElementById("w-systems"); + if (wSys) { + wSys.addEventListener("click", openSystemsModal); + wSys.addEventListener("keydown", function (e) { if (e.key === "Enter") openSystemsModal(); }); + } + var wBtc = document.getElementById("w-btc"); + if (wBtc && btc) { + (function (svc) { + wBtc.addEventListener("click", function () { openServiceDetailModal(svc.unit, svc.name, svc.icon); }); + wBtc.addEventListener("keydown", function (e) { if (e.key === "Enter") openServiceDetailModal(svc.unit, svc.name, svc.icon); }); + })(btc); + } + } + + /* ── Systems Operational modal ───────────────────────────────── */ + + function internalIp() { + var el = document.getElementById("ip-internal"); + var v = el ? el.textContent.trim() : ""; + return (v && v !== "…" && v !== "—") ? v : ""; + } + + function step(n, title, sub, value) { + return 'No services configured.