diff --git a/app/sovran_systemsos_web/static/style.css b/app/sovran_systemsos_web/static/style.css index 3f89d3f..90babca 100644 --- a/app/sovran_systemsos_web/static/style.css +++ b/app/sovran_systemsos_web/static/style.css @@ -1,3 +1,74 @@ +/* Sovran_SystemsOS Hub — Web UI Stylesheet + Dark theme matching the Adwaita dark aesthetic + v6 — Status-only tiles (no controls) */ + +*, *::before, *::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +:root { + --bg-color: #1e1e2e; + --surface-color: #2a2a3c; + --card-color: #313244; + --border-color: #45475a; + --text-primary: #cdd6f4; + --text-secondary: #a6adc8; + --text-dim: #6c7086; + --accent-color: #89b4fa; + --green: #2ec27e; + --yellow: #e5a50a; + --red: #e01b24; + --grey: #888888; + --radius-card: 18px; + --radius-btn: 8px; + --shadow-card: 0 2px 8px rgba(0,0,0,0.4); + --shadow-hover: 0 6px 20px rgba(0,0,0,0.6); +} + +html, body { + height: 100%; +} + +body { + font-family: 'Cantarell', 'Inter', 'Segoe UI', sans-serif; + background-color: var(--bg-color); + color: var(--text-primary); + line-height: 1.5; + min-height: 100vh; + display: flex; + flex-direction: column; + overflow: hidden; +} + +/* ── Header bar ─────────────────────────────────────────────────── */ + +.header-bar { + background-color: var(--surface-color); + border-bottom: 1px solid var(--border-color); + padding: 10px 24px; + display: flex; + align-items: center; + gap: 16px; + position: sticky; + top: 0; + z-index: 100; + justify-content: flex-end; +} + +.header-bar .title { + font-size: 1.15rem; + font-weight: 700; + color: var(--text-primary); + position: absolute; + left: 0; + right: 0; + text-align: center; + pointer-events: none; + white-space: nowrap; +} + .header-logo { height: 36px; width: auto; @@ -5,4 +76,1021 @@ margin-right: 10px; } -/* Other styles remain unchanged */ +.role-badge { + background-color: var(--accent-color); + color: #1e1e2e; + font-size: 0.72rem; + font-weight: 700; + padding: 3px 10px; + border-radius: 20px; + letter-spacing: 0.03em; +} + +/* ── Buttons ────────────────────────────────────────────────────── */ + +button { + font-family: inherit; + cursor: pointer; + border: none; + outline: none; + transition: opacity 0.15s, box-shadow 0.15s, background-color 0.15s; +} + +button:disabled { + opacity: 0.45; + cursor: default; +} + +.btn { + padding: 7px 16px; + border-radius: var(--radius-btn); + font-size: 0.88rem; + font-weight: 600; +} + +.btn-primary { + background-color: var(--accent-color); + color: #1e1e2e; +} + +.btn-primary:hover:not(:disabled) { + opacity: 0.88; +} + +/* Update System button: BLUE by default */ +.btn-update { + background-color: #89b4fa; + color: #1e1e2e; + position: relative; + display: flex; + align-items: center; + gap: 8px; +} + +.btn-update:hover:not(:disabled) { + opacity: 0.88; +} + +/* Update System button: GREEN when updates are available */ +.btn-update.has-updates { + background-color: #2ec27e; + color: #fff; +} + +.btn-update.has-updates:hover:not(:disabled) { + background-color: #27ae6e; +} + +.update-badge { + display: none; + width: 10px; + height: 10px; + background-color: var(--yellow); + border-radius: 50%; + animation: pulse-badge 1.4s ease-in-out infinite; +} + +.update-badge.visible { + display: inline-block; +} + +@keyframes pulse-badge { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.5; transform: scale(1.35); } +} + +.btn-icon { + background: none; + color: var(--text-secondary); + padding: 6px; + border-radius: 50%; + font-size: 1.1rem; + line-height: 1; +} + +.btn-icon:hover:not(:disabled) { + background-color: var(--border-color); + color: var(--text-primary); +} + +/* ── IP bar ─────────────────────────────────────────────────────── */ + +.ip-bar { + background-color: var(--surface-color); + border-bottom: 1px solid var(--border-color); + 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); +} + +/* ── Main content ───────────────────────────────────────────────── */ + +.main-content { + display: flex; + align-items: flex-start; + flex: 1; + overflow: hidden; + max-width: 1400px; + width: 100%; + margin-left: auto; + margin-right: auto; +} + +/* ── Sidebar ────────────────────────────────────────────────────── */ + +.sidebar { + width: 270px; + flex-shrink: 0; + height: 100%; + overflow-y: auto; + border-right: 1px solid var(--border-color); + background-color: var(--surface-color); + padding: 20px 14px; + display: flex; + flex-direction: column; + gap: 0; +} + +/* ── Sidebar: Tech Support button ───────────────────────────────── */ + +.sidebar-support-btn { + display: flex; + align-items: center; + gap: 10px; + width: 100%; + background-color: var(--card-color); + border: 2px dashed var(--accent-color); + border-radius: 12px; + padding: 12px 14px; + color: var(--text-primary); + cursor: pointer; + transition: border-style 0.15s, border-color 0.15s, background-color 0.15s; + text-align: left; +} + +.sidebar-support-btn:hover { + border-style: solid; + border-color: #a8c8ff; + background-color: #35354a; +} + +.sidebar-support-icon { + font-size: 1.5rem; + flex-shrink: 0; +} + +.sidebar-support-text { + display: flex; + flex-direction: column; + gap: 2px; +} + +.sidebar-support-title { + font-size: 0.88rem; + font-weight: 700; + color: var(--text-primary); +} + +.sidebar-support-hint { + font-size: 0.72rem; + color: var(--accent-color); + font-weight: 600; +} + +.sidebar-divider { + border: none; + border-top: 1px solid var(--border-color); + margin: 16px 0; +} + +/* ── Tiles area ─────────────────────────────────────────────────── */ + +#tiles-area { + flex: 1; + height: 100%; + overflow-y: auto; + padding: 24px 20px 48px; + min-width: 0; +} + +/* ── Category sections ──────────────────────────────────────────── */ + +.category-section { + margin-bottom: 32px; +} + +.section-header { + font-size: 0.82rem; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--text-secondary); + margin-bottom: 4px; + padding-left: 4px; +} + +.section-divider { + border: none; + border-top: 1px solid var(--border-color); + margin-bottom: 16px; +} + +.tiles-grid { + display: flex; + flex-wrap: wrap; + gap: 14px; +} + +/* ── Service tile card (status-only) ─────────────────────────────── */ + +.service-tile { + width: 160px; + min-height: 130px; + background-color: var(--card-color); + border: 1px solid var(--border-color); + border-radius: var(--radius-card); + box-shadow: var(--shadow-card); + 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; +} + +.service-tile:hover { + box-shadow: var(--shadow-hover); + border-color: #6c7086; +} + +.service-tile.disabled { + opacity: 0.45; +} + +.tile-icon { + width: 48px; + height: 48px; + object-fit: contain; + margin-bottom: 10px; +} + +.tile-icon-fallback { + width: 48px; + height: 48px; + 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; +} + +.tile-name { + font-size: 0.88rem; + font-weight: 600; + text-align: center; + color: var(--text-primary); + line-height: 1.3; + max-width: 140px; + word-break: break-word; + hyphens: auto; + min-height: 1.3em; + display: flex; + align-items: center; + justify-content: center; +} + +.tile-status { + font-size: 0.75rem; + margin-top: 8px; + display: flex; + align-items: center; + gap: 5px; + color: var(--text-secondary); +} + +.status-dot { + width: 8px; + height: 8px; + border-radius: 50%; + 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); } + +/* ── Update modal ────────────────────────────────────────────────── */ + +.modal-overlay { + display: none; + position: fixed; + inset: 0; + background-color: rgba(0,0,0,0.65); + z-index: 200; + align-items: center; + justify-content: center; +} + +.modal-overlay.open { + display: flex; +} + +.modal-dialog { + background-color: var(--surface-color); + border: 1px solid var(--border-color); + border-radius: 16px; + width: 90vw; + max-width: 900px; + max-height: 80vh; + display: flex; + flex-direction: column; + box-shadow: 0 16px 48px rgba(0,0,0,0.7); +} + +.modal-header { + display: flex; + align-items: center; + padding: 16px 20px; + border-bottom: 1px solid var(--border-color); + gap: 12px; +} + +.modal-title { + font-size: 1rem; + font-weight: 700; + flex: 1; +} + +.modal-status { + font-size: 0.85rem; + color: var(--text-secondary); +} + +.modal-spinner { + width: 18px; + height: 18px; + border: 2.5px solid var(--border-color); + border-top-color: var(--accent-color); + border-radius: 50%; + animation: spin 0.75s linear infinite; + display: none; +} + +.modal-spinner.spinning { + display: block; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +.modal-log { + flex: 1; + 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: #12121c; + white-space: pre-wrap; + word-break: break-all; + min-height: 200px; +} + +.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: #2ec27e; + color: #fff; +} + +.modal-footer .btn-reboot:hover:not(:disabled), +button.btn-reboot:hover:not(:disabled) { + background-color: #27ae6e; +} + +.btn-save { + background-color: var(--yellow); + color: #1e1e2e; +} + +.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: #5a5c72; +} + +/* ── Credentials info modal ──────────────────────────────────────── */ + +.creds-dialog { + background-color: var(--surface-color); + border: 1px solid var(--border-color); + border-radius: 16px; + width: 90vw; + max-width: 700px; + max-height: 85vh; + display: flex; + flex-direction: column; + box-shadow: 0 16px 48px rgba(0,0,0,0.7); + 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-header { + display: flex; + align-items: center; + padding: 20px 28px; + border-bottom: 1px solid var(--border-color); +} + +.creds-title { + font-size: 1.15rem; + font-weight: 700; + flex: 1; +} + +.creds-close-btn { + background: none; + color: var(--text-secondary); + font-size: 1.3rem; + padding: 4px 8px; + border-radius: 6px; + cursor: pointer; + border: none; +} + +.creds-close-btn:hover { + background-color: var(--border-color); + color: var(--text-primary); +} + +.creds-body { + padding: 24px 28px; + overflow-y: auto; +} + +.creds-loading { + color: var(--text-dim); + text-align: center; + padding: 24px 0; +} + +.creds-row { + margin-bottom: 20px; +} + +.creds-row:last-child { + margin-bottom: 0; +} + +.creds-label { + font-size: 0.78rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--text-dim); + margin-bottom: 6px; +} + +.creds-value-wrap { + display: flex; + align-items: flex-start; + gap: 10px; +} + +.creds-value { + flex: 1; + font-family: 'JetBrains Mono', 'Fira Code', 'Source Code Pro', monospace; + font-size: 0.92rem; + color: var(--text-primary); + background-color: #12121c; + padding: 12px 16px; + border-radius: 8px; + word-break: break-all; + 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-copy-btn:hover { + background-color: #5a5c72; +} + +.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; + text-decoration: none; + word-break: break-all; +} + +.creds-link:hover { + text-decoration: underline; + color: #defce6; +} + +/* ── 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: #0f0f19; + font-size: 0.88rem; + font-weight: 700; + padding: 10px 18px; + border-radius: 8px; + border: none; + cursor: pointer; + flex: 1; + min-width: 140px; +} + +.matrix-action-btn:hover { + background-color: #a8c8ff; +} + +.matrix-form-group { + margin-bottom: 14px; +} + +.matrix-form-label { + display: block; + font-size: 0.82rem; + color: var(--text-secondary); + margin-bottom: 6px; + font-weight: 600; +} + +.matrix-form-input { + width: 100%; + background-color: #12121c; + color: var(--text-primary); + border: 1px solid var(--border-color); + border-radius: 8px; + padding: 10px 12px; + font-size: 0.9rem; + box-sizing: border-box; +} + +.matrix-form-input:focus { + outline: none; + border-color: var(--accent-color); +} + +.matrix-form-checkbox-row { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 14px; +} + +.matrix-form-checkbox-row input[type="checkbox"] { + width: 16px; + height: 16px; + accent-color: var(--accent-color); +} + +.matrix-form-actions { + display: flex; + gap: 10px; + margin-top: 18px; +} + +.matrix-form-submit { + background-color: var(--accent-color); + color: #0f0f19; + 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: #a8c8ff; +} + +.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; + cursor: pointer; +} + +.matrix-form-back:hover { + background-color: #5a5c72; +} + +.matrix-form-result { + margin-top: 14px; + padding: 12px 16px; + border-radius: 8px; + font-size: 0.88rem; + line-height: 1.5; + display: none; +} + +.matrix-form-result.success { + background-color: rgba(74, 222, 128, 0.12); + border: 1px solid var(--green); + color: var(--green); + display: block; +} + +.matrix-form-result.error { + background-color: rgba(239, 68, 68, 0.12); + border: 1px solid #ef4444; + color: #f87171; + display: block; +} + +/* ── QR code in credentials modal ────────────────────────────────── */ + +.creds-qr-wrap { + display: flex; + flex-direction: column; + align-items: center; + padding: 20px 0; + margin-bottom: 10px; +} + +.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); +} + +.creds-qr-hint { + margin-top: 10px; + font-size: 0.82rem; + color: var(--text-secondary); + font-style: italic; +} + +/* ── Reboot overlay ─────────────────────────────────────────────── */ + +.reboot-overlay { + display: none; + position: fixed; + inset: 0; + background-color: rgba(15, 15, 25, 0.92); + z-index: 999; + align-items: center; + justify-content: center; +} + +.reboot-overlay.visible { + display: flex; +} + +.reboot-card { + background-color: var(--surface-color); + border: 1px solid var(--border-color); + border-radius: 20px; + padding: 48px 56px; + text-align: center; + max-width: 480px; + box-shadow: 0 24px 64px rgba(0, 0, 0, 0.8); + animation: reboot-fade-in 0.4s ease-out; +} + +@keyframes reboot-fade-in { + from { opacity: 0; transform: scale(0.92) translateY(12px); } + to { opacity: 1; transform: scale(1) translateY(0); } +} + +.reboot-icon { + font-size: 3rem; + color: var(--accent-color); + margin-bottom: 16px; + animation: reboot-spin 2s linear infinite; + display: inline-block; +} + +@keyframes reboot-spin { + to { transform: rotate(360deg); } +} + +.reboot-title { + font-size: 1.35rem; + font-weight: 700; + color: var(--text-primary); + margin-bottom: 12px; +} + +.reboot-message { + font-size: 0.92rem; + color: var(--text-secondary); + line-height: 1.6; + margin-bottom: 24px; +} + +.reboot-dots { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + margin-bottom: 16px; +} + +.reboot-dot { + width: 10px; + height: 10px; + border-radius: 50%; + background-color: var(--accent-color); + animation: reboot-bounce 1.4s ease-in-out infinite; +} + +.reboot-dot:nth-child(2) { animation-delay: 0.2s; } +.reboot-dot:nth-child(3) { animation-delay: 0.4s; } + +@keyframes reboot-bounce { + 0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); } + 40% { opacity: 1; transform: scale(1.2); } +} + +.reboot-submessage { + font-size: 0.82rem; + color: var(--text-dim); + font-style: italic; +} + +/* ── Empty state ────────────────────────────────────────────────── */ + +.empty-state { + text-align: center; + padding: 64px 24px; + color: var(--text-dim); +} + +.empty-state p { + font-size: 1rem; + margin-bottom: 8px; +} + +/* ── Responsive ─────────────────────────────────────────────────── */ + +@media (max-width: 768px) { + body { + overflow: auto; + } + .main-content { + flex-direction: column; + overflow: visible; + } + .sidebar { + width: 100%; + height: auto; + border-right: none; + border-bottom: 1px solid var(--border-color); + padding: 14px 12px; + } + #tiles-area { + height: auto; + overflow-y: visible; + padding: 16px 12px 40px; + } +} + +@media (max-width: 600px) { + .header-bar { + padding: 10px 14px; + gap: 10px; + } + .header-bar .title { + font-size: 0.95rem; + } + .ip-bar { + gap: 16px; + flex-wrap: wrap; + padding: 8px 14px; + } + .tiles-grid { + justify-content: center; + } + .service-tile { + width: 140px; + min-height: 130px; + } + .reboot-card { + padding: 36px 28px; + margin: 0 16px; + } + .creds-dialog { + margin: 0 12px; + } + .creds-qr-img { + width: 200px; + height: 200px; + } +} + +/* ── Tech Support tile ───────────────────────────────────────────── */ + +.support-tile { + border-color: var(--accent-color); + border-width: 2px; + border-style: dashed; +} + +.support-tile:hover { + border-color: #a8c8ff; + border-style: solid; +} + +/* ── Login page ──────────────────────────────────────────────────── */ + +.login-wrapper { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + padding: 24px; +} + +.login-card { + background-color: var(--surface-color); + border: 1px solid var(--border-color); + border-radius: 20px; + padding: 48px 40px; + width: 100%; + max-width: 400px; + box-shadow: 0 8px 32px rgba(0,0,0,0.5); +} + +.login-header { + text-align: center; + margin-bottom: 32px; +} + +.login-logo { + height: 64px; + margin-bottom: 16px; +} + +.login-title { + font-size: 1.25rem; + font-weight: 700; + color: var(--text-primary); +} + +.login-form { + display: flex; + flex-direction: column; + gap: 16px; +} + +.form-group label { + display: block; + font-size: 0.82rem; + font-weight: 600; + color: var(--text-secondary); + margin-bottom: 6px; +} + +.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); + font-size: 0.92rem; +} + +.form-group input:focus { + outline: none; + border-color: var(--accent-color); +} + +.btn-login { + width: 100%; + padding: 12px; + border-radius: var(--radius-btn); + background-color: var(--accent-color); + color: #1e1e2e; + font-size: 0.95rem; + font-weight: 700; + margin-top: 8px; +} + +.btn-login:hover { + opacity: 0.88; +} + +.login-error { + background-color: rgba(224, 27, 36, 0.12); + border: 1px solid var(--red); + color: #f87171; + padding: 10px 14px; + border-radius: 8px; + font-size: 0.85rem; + display: none; +} + +.login-error.visible { + display: block; +} \ No newline at end of file