diff --git a/backup-monitor/static/css/style.css b/backup-monitor/static/css/style.css index 9452692..d0afffd 100644 --- a/backup-monitor/static/css/style.css +++ b/backup-monitor/static/css/style.css @@ -1 +1,721 @@ -/* Sentinel Design System – all styles via Tailwind CSS */ +:root { + color-scheme: dark; + --canvas: #101210; + --surface: #171a17; + --surface-raised: #1d211d; + --surface-hover: #232823; + --border: #2c312c; + --border-strong: #3a413a; + --text: #edf1eb; + --text-soft: #b8c0b7; + --text-muted: #7f897f; + --accent: #7abf8a; + --accent-soft: #203326; + --status-ok: #7abf8a; + --status-warning: #d0a15e; + --status-error: #d67b76; + --status-disabled: #7f897f; + --font-sans: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + --font-mono: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace; + --radius: 6px; + --max-width: 1280px; +} + +* { box-sizing: border-box; } + +html { min-width: 320px; background: var(--canvas); } + +body { + margin: 0; + min-height: 100vh; + background: var(--canvas); + color: var(--text); + font-family: var(--font-sans); + font-size: 15px; + line-height: 1.5; + -webkit-font-smoothing: antialiased; +} + +button, +input { font: inherit; } + +button, +a { -webkit-tap-highlight-color: transparent; } + +button { color: inherit; } + +a { color: inherit; } + +:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 3px; +} + +.hidden { display: none !important; } + +.site-header { + position: sticky; + top: 0; + z-index: 30; + border-bottom: 1px solid var(--border); + background: rgba(16, 18, 16, 0.96); +} + +.topbar { + width: min(100% - 40px, var(--max-width)); + min-height: 64px; + margin: 0 auto; + display: grid; + grid-template-columns: auto 1fr auto; + align-items: center; + gap: 28px; +} + +.brand { + display: inline-flex; + align-items: center; + gap: 10px; + text-decoration: none; + white-space: nowrap; +} + +.brand-mark { + display: grid; + place-items: center; + width: 30px; + height: 30px; + border: 1px solid var(--border-strong); + border-radius: 5px; + background: var(--surface-raised); + color: var(--accent); + font-family: var(--font-mono); + font-size: 13px; + font-weight: 700; +} + +.brand strong, +.brand small { display: block; } + +.brand strong { + font-size: 14px; + line-height: 1.2; + letter-spacing: -0.01em; +} + +.brand small { + margin-top: 2px; + color: var(--text-muted); + font-size: 11px; +} + +.main-nav { + display: flex; + align-items: stretch; + align-self: stretch; + gap: 2px; +} + +.main-nav a { + position: relative; + display: inline-flex; + align-items: center; + padding: 0 12px; + color: var(--text-muted); + font-size: 13px; + font-weight: 500; + text-decoration: none; +} + +.main-nav a::after { + content: ""; + position: absolute; + right: 12px; + bottom: -1px; + left: 12px; + height: 2px; + background: transparent; +} + +.main-nav a:hover { color: var(--text-soft); } +.main-nav a.is-active { color: var(--text); } +.main-nav a.is-active::after { background: var(--accent); } + +.topbar-actions { + display: flex; + align-items: center; + gap: 8px; +} + +.system-state { + display: inline-flex; + align-items: center; + gap: 8px; + min-height: 34px; + padding: 0 11px; + border: 1px solid var(--border); + border-radius: var(--radius); + color: var(--text-soft); + font-size: 12px; + white-space: nowrap; +} + +.status-dot { + display: inline-block; + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--status-disabled); + flex: 0 0 auto; +} + +.status--ok { --status-color: var(--status-ok); } +.status--stale { --status-color: var(--status-warning); } +.status--error { --status-color: var(--status-error); } +.status--disabled { --status-color: var(--status-disabled); } +.status-dot.status--ok, +.status-dot.status--stale, +.status-dot.status--error, +.status-dot.status--disabled { background: var(--status-color); } + +.icon-button { + display: inline-grid; + place-items: center; + width: 36px; + height: 36px; + padding: 0; + border: 1px solid var(--border); + border-radius: var(--radius); + background: transparent; + color: var(--text-soft); + cursor: pointer; + font-size: 20px; + line-height: 1; +} + +.icon-button:hover { background: var(--surface-raised); color: var(--text); } + +.app-shell { + width: min(100% - 40px, var(--max-width)); + margin: 0 auto; + padding: 48px 0 64px; +} + +.page-header { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: 32px; + margin-bottom: 28px; +} + +.eyebrow { + margin: 0 0 6px; + color: var(--text-muted); + font-family: var(--font-mono); + font-size: 11px; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +h1, +h2, +h3, +p { margin-top: 0; } + +h1 { + margin-bottom: 6px; + font-size: clamp(28px, 4vw, 38px); + font-weight: 600; + line-height: 1.15; + letter-spacing: -0.035em; +} + +h2 { + margin-bottom: 0; + font-size: 16px; + font-weight: 600; + letter-spacing: -0.01em; +} + +.lede { + margin-bottom: 0; + color: var(--text-soft); + font-size: 14px; +} + +.scan-state { + min-width: 150px; + padding-left: 18px; + border-left: 1px solid var(--border); +} + +.scan-state span, +.scan-state time { display: block; } + +.scan-state span { + margin-bottom: 4px; + color: var(--text-muted); + font-size: 11px; +} + +.scan-state time { + color: var(--text-soft); + font-family: var(--font-mono); + font-size: 13px; +} + +.metric-strip { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + margin-bottom: 20px; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--surface); + overflow: hidden; +} + +.metric { + min-height: 122px; + padding: 22px 24px; + border-right: 1px solid var(--border); +} + +.metric:last-child { border-right: 0; } + +.metric-label { + display: block; + margin-bottom: 16px; + color: var(--text-muted); + font-size: 12px; +} + +.metric strong { + display: block; + font-family: var(--font-mono); + font-size: clamp(23px, 3vw, 31px); + font-weight: 500; + line-height: 1; + letter-spacing: -0.04em; +} + +.metric small { + display: block; + margin-top: 8px; + color: var(--text-muted); + font-family: var(--font-mono); + font-size: 11px; +} + +.metric--issue.has-issues strong { color: var(--status-error); } + +.text-button { + margin-top: 8px; + padding: 0; + border: 0; + background: none; + color: var(--text-muted); + cursor: pointer; + font-size: 11px; + text-decoration: underline; + text-underline-offset: 3px; +} + +.text-button:hover { color: var(--text); } + +.dashboard-grid { + display: grid; + grid-template-columns: minmax(0, 2fr) minmax(290px, 1fr); + gap: 20px; + margin-bottom: 20px; +} + +.panel { + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--surface); +} + +.panel-header { + padding: 19px 22px; + border-bottom: 1px solid var(--border); +} + +.panel-header--split { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; +} + +.panel-header p { + margin: 5px 0 0; + color: var(--text-muted); + font-size: 12px; +} + +.quiet-note { + color: var(--text-muted); + font-family: var(--font-mono); + font-size: 10px; +} + +.chart { + height: 248px; + padding: 26px 22px 20px; +} + +.chart svg { + display: block; + width: 100%; + height: 100%; + overflow: visible; +} + +.cluster-list { + max-height: 248px; + padding: 10px 12px 14px; + overflow-y: auto; +} + +.cluster-group + .cluster-group { margin-top: 14px; } + +.cluster-heading { + display: flex; + align-items: center; + gap: 7px; + padding: 5px 8px; + color: var(--text-muted); + font-family: var(--font-mono); + font-size: 10px; + letter-spacing: 0.07em; + text-transform: uppercase; +} + +.cluster-row { + width: 100%; + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; + align-items: center; + gap: 10px; + padding: 8px; + border: 0; + border-radius: 4px; + background: transparent; + color: var(--text); + cursor: pointer; + text-align: left; +} + +.cluster-row:hover { background: var(--surface-hover); } +.cluster-row strong { min-width: 0; overflow: hidden; text-overflow: ellipsis; font-size: 13px; } +.cluster-row small { color: var(--text-muted); font-family: var(--font-mono); font-size: 10px; } + +.status-pill { + display: inline-flex; + align-items: center; + min-height: 22px; + padding: 0 7px; + border: 1px solid color-mix(in srgb, var(--status-color) 35%, transparent); + border-radius: 999px; + color: var(--status-color); + font-family: var(--font-mono); + font-size: 9px; + letter-spacing: 0.04em; + text-transform: uppercase; + white-space: nowrap; +} + +.event-list { padding: 4px 20px 10px; } + +.event-row { + display: grid; + grid-template-columns: 70px 8px minmax(0, 1fr) auto; + align-items: center; + gap: 12px; + min-height: 44px; + border-bottom: 1px solid var(--border); +} + +.event-row:last-child { border-bottom: 0; } +.event-time { color: var(--text-muted); font-family: var(--font-mono); font-size: 10px; } +.event-message { min-width: 0; color: var(--text-soft); font-size: 13px; } +.event-message strong { color: var(--text); font-weight: 600; } +.event-detail { max-width: 260px; overflow: hidden; color: var(--status-error); font-family: var(--font-mono); font-size: 10px; text-overflow: ellipsis; white-space: nowrap; } + +.inline-counts { + display: flex; + margin: 0; + border: 1px solid var(--border); + border-radius: var(--radius); + overflow: hidden; +} + +.inline-counts > div { + min-width: 112px; + padding: 12px 16px; +} + +.inline-counts > div + div { border-left: 1px solid var(--border); } +.inline-counts dt { color: var(--text-muted); font-size: 11px; } +.inline-counts dd { margin: 2px 0 0; font-family: var(--font-mono); font-size: 20px; } + +.alert-list, +.host-grid { display: grid; gap: 10px; } + +.alert-row { + display: grid; + grid-template-columns: 8px minmax(0, 1fr) auto; + align-items: center; + gap: 16px; + min-height: 72px; + padding: 14px 18px; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--surface); +} + +.alert-copy h3 { margin: 0 0 4px; font-size: 14px; font-weight: 600; } +.alert-meta { display: flex; flex-wrap: wrap; gap: 14px; color: var(--text-muted); font-family: var(--font-mono); font-size: 10px; } + +.empty-state { + margin: 0; + padding: 48px 20px; + border: 1px solid var(--border); + border-radius: var(--radius); + color: var(--text-muted); + text-align: center; +} + +.host-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } + +.host-card { + width: 100%; + padding: 18px; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--surface); + color: var(--text); + cursor: pointer; + text-align: left; +} + +.host-card:hover { border-color: var(--border-strong); background: var(--surface-raised); } +.host-card.is-disabled { opacity: 0.55; } + +.host-card-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-bottom: 18px; +} + +.host-card-header strong { overflow: hidden; font-size: 14px; text-overflow: ellipsis; } + +.host-facts { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 14px; + margin: 0; +} + +.host-facts div { min-width: 0; } +.host-facts dt { color: var(--text-muted); font-size: 10px; } +.host-facts dd { margin: 3px 0 0; overflow: hidden; font-family: var(--font-mono); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; } + +.settings-list { + max-width: 760px; + margin: 0; + border-top: 1px solid var(--border); +} + +.settings-list > div { + display: grid; + grid-template-columns: minmax(160px, 1fr) minmax(220px, 2fr); + gap: 24px; + padding: 18px 0; + border-bottom: 1px solid var(--border); +} + +.settings-list dt { color: var(--text-soft); } +.settings-list dd { margin: 0; color: var(--text-muted); } +code { color: var(--accent); font-family: var(--font-mono); font-size: 12px; } + +.button { + min-height: 40px; + padding: 0 15px; + border: 1px solid var(--border-strong); + border-radius: var(--radius); + background: var(--surface-raised); + color: var(--text-soft); + cursor: pointer; + font-size: 13px; + font-weight: 600; +} + +.button:hover { background: var(--surface-hover); color: var(--text); } +.button--primary { border-color: #537c5d; background: var(--accent-soft); color: #bfe3c6; } +.button--primary:hover { background: #29432f; } +.button--danger { border-color: transparent; background: transparent; color: var(--status-error); } + +.backdrop { + position: fixed; + inset: 0; + z-index: 40; + background: rgba(0, 0, 0, 0.62); + opacity: 1; + transition: opacity 150ms ease; +} + +.backdrop.is-hidden { opacity: 0; pointer-events: none; } + +.drawer { + position: fixed; + top: 0; + right: 0; + bottom: 0; + z-index: 50; + width: min(540px, 94vw); + border-left: 1px solid var(--border); + background: var(--canvas); + transform: translateX(0); + transition: transform 180ms ease; +} + +.drawer.is-closed { transform: translateX(100%); pointer-events: none; } + +.modal { + position: fixed; + top: 50%; + left: 50%; + z-index: 60; + width: min(440px, calc(100vw - 32px)); + border: 1px solid var(--border-strong); + border-radius: var(--radius); + background: var(--surface); + transform: translate(-50%, -50%) scale(1); + opacity: 1; + transition: opacity 150ms ease, transform 150ms ease; +} + +.modal.is-closed { + transform: translate(-50%, -48%) scale(0.98); + opacity: 0; + pointer-events: none; +} + +.overlay-header { + min-height: 68px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 14px 20px; + border-bottom: 1px solid var(--border); +} + +.overlay-header h2 { font-size: 17px; } +.drawer-body { height: calc(100% - 68px); padding: 22px; overflow-y: auto; } + +.drawer-stats { + display: grid; + grid-template-columns: repeat(3, 1fr); + border: 1px solid var(--border); + border-radius: var(--radius); + overflow: hidden; +} + +.drawer-stat { padding: 14px; text-align: left; } +.drawer-stat + .drawer-stat { border-left: 1px solid var(--border); } +.drawer-stat strong { display: block; font-family: var(--font-mono); font-size: 18px; font-weight: 500; } +.drawer-stat span { display: block; margin-top: 5px; color: var(--text-muted); font-size: 10px; } + +.detail-section { margin-top: 24px; } +.detail-section h3 { margin-bottom: 10px; color: var(--text-muted); font-size: 11px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; } + +.calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; } +.calendar-day { aspect-ratio: 1; display: grid; place-items: center; border: 1px solid var(--border); border-radius: 3px; color: var(--text-muted); font-family: var(--font-mono); font-size: 9px; } +.calendar-day.has-backup { border-color: #35533c; background: #1b2b20; color: #9fd0aa; } +.calendar-day.has-error { border-color: #5a3533; background: #2c1d1c; color: #e0a09b; } + +.size-bars { height: 68px; display: flex; align-items: end; gap: 2px; border-bottom: 1px solid var(--border); } +.size-bar { flex: 1; min-width: 2px; background: var(--accent); opacity: 0.65; } +.size-bar.is-empty { opacity: 0.12; } + +.history-list { border-top: 1px solid var(--border); } +.history-row { display: grid; grid-template-columns: 92px 8px 1fr auto auto; align-items: center; gap: 10px; min-height: 39px; border-bottom: 1px solid var(--border); font-size: 11px; } +.history-date { color: var(--text-muted); font-family: var(--font-mono); } +.history-size, +.history-files { color: var(--text-muted); font-family: var(--font-mono); text-align: right; } + +.drawer-actions { display: flex; gap: 8px; margin-top: 28px; padding-top: 18px; border-top: 1px solid var(--border); } + +.form-stack { display: grid; gap: 18px; padding: 22px; } +.form-stack label > span { display: block; margin-bottom: 7px; color: var(--text-soft); font-size: 12px; } +.form-stack input[type="text"], +.form-stack input[type="url"] { width: 100%; min-height: 42px; padding: 0 12px; border: 1px solid var(--border-strong); border-radius: var(--radius); background: var(--canvas); color: var(--text); } +.form-stack input::placeholder { color: #667066; } +.checkbox-row { display: flex; align-items: center; gap: 9px; } +.checkbox-row span { margin: 0 !important; } +.form-actions { display: flex; justify-content: flex-end; gap: 8px; padding-top: 6px; } + +.toast-stack { position: fixed; right: 20px; bottom: 20px; z-index: 80; display: grid; gap: 8px; } +.toast { min-width: 220px; padding: 11px 13px; border: 1px solid var(--border-strong); border-radius: var(--radius); background: var(--surface-raised); color: var(--text-soft); font-size: 12px; } + +::-webkit-scrollbar { width: 8px; height: 8px; } +::-webkit-scrollbar-track { background: transparent; } +::-webkit-scrollbar-thumb { border: 2px solid transparent; border-radius: 999px; background: var(--border-strong); background-clip: padding-box; } + +@media (max-width: 900px) { + .topbar { grid-template-columns: auto 1fr; gap: 16px; padding: 10px 0; } + .main-nav { grid-row: 2; grid-column: 1 / -1; min-height: 40px; overflow-x: auto; border-top: 1px solid var(--border); } + .main-nav a { min-height: 40px; padding: 0 10px; } + .topbar-actions { justify-self: end; } + .dashboard-grid { grid-template-columns: 1fr; } + .host-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } +} + +@media (max-width: 720px) { + .topbar, + .app-shell { width: min(100% - 24px, var(--max-width)); } + .topbar { gap: 10px; } + .brand small { display: none; } + .system-state span:last-child { display: none; } + .system-state { min-width: 44px; min-height: 44px; padding: 0 17px; } + .icon-button { width: 44px; height: 44px; } + .main-nav, + .main-nav a { min-height: 44px; } + .app-shell { padding-top: 32px; } + .page-header { align-items: flex-start; flex-direction: column; gap: 16px; } + .scan-state { padding-left: 0; border-left: 0; } + .metric-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .metric { min-height: 112px; padding: 18px; } + .metric:nth-child(2) { border-right: 0; } + .metric:nth-child(-n + 2) { border-bottom: 1px solid var(--border); } + .panel-header { padding: 16px; } + .chart { height: 210px; padding: 22px 16px 16px; } + .event-list { padding: 4px 14px 8px; } + .event-row { grid-template-columns: 56px 8px minmax(0, 1fr); } + .event-detail { display: none; } + .host-grid { grid-template-columns: 1fr; } + .alert-row { grid-template-columns: 8px minmax(0, 1fr); } + .alert-row .button { grid-column: 2; justify-self: start; } + .inline-counts { width: 100%; } + .inline-counts > div { min-width: 0; flex: 1; } + .settings-list > div { grid-template-columns: 1fr; gap: 6px; } + .drawer-body { padding: 16px; } + .history-row { grid-template-columns: 76px 7px 1fr auto; } + .history-files { display: none; } + .toast-stack { right: 12px; bottom: 12px; left: 12px; } +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + scroll-behavior: auto !important; + transition-duration: 0.01ms !important; + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + } +}