/* ── Variables ──────────────────────────────────────────────────────── */ :root { --blue: #006FFF; --blue-dark: #00439C; --blue-dim: rgba(0,111,255,.1); --green: #10B981; --red: #EF4444; --orange: #F59E0B; --yellow: #FBBF24; --grey: #6B7280; --grey-lt: #F3F4F6; --border: #E5E7EB; --text: #111827; --text-sub: #6B7280; --card-bg: #FFFFFF; --bg: #F8FAFC; --radius: 10px; --shadow: 0 1px 3px rgba(0,0,0,.08), 0 4px 12px rgba(0,0,0,.06); --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; --mono: 'SF Mono', 'Fira Code', Consolas, monospace; } /* ── Reset ──────────────────────────────────────────────────────────── */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: var(--font); background: var(--bg); color: var(--text); font-size: 14px; line-height: 1.5; } a { color: var(--blue); text-decoration: none; } a:hover { text-decoration: underline; } /* ── Navbar ─────────────────────────────────────────────────────────── */ .navbar { background: linear-gradient(135deg, var(--blue-dark) 0%, var(--blue) 100%); color: white; display: flex; align-items: center; gap: 24px; padding: 0 24px; height: 56px; box-shadow: 0 2px 8px rgba(0,0,0,.2); } .nav-brand { display: flex; align-items: center; gap: 8px; flex-shrink: 0; } .nav-logo { font-size: 20px; } .nav-title { font-weight: 700; font-size: 16px; letter-spacing: .05em; } .nav-sub { font-size: 11px; opacity: .7; font-weight: 400; } .nav-links { display: flex; gap: 4px; flex: 1; } .nav-link { color: rgba(255,255,255,.8); padding: 6px 14px; border-radius: 6px; font-size: 13px; transition: background .15s, color .15s; } .nav-link:hover, .nav-link.active { background: rgba(255,255,255,.15); color: white; text-decoration: none; } .nav-user { font-size: 12px; opacity: .8; } /* ── Main layout ─────────────────────────────────────────────────────── */ .main { max-width: 1400px; margin: 0 auto; padding: 24px 20px; } .page-header { margin-bottom: 24px; } .page-title { font-size: 22px; font-weight: 700; } .page-sub { color: var(--text-sub); margin-top: 4px; } /* ── Status bar ──────────────────────────────────────────────────────── */ .status-bar { display: flex; align-items: center; justify-content: space-between; gap: 16px; background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 20px; margin-bottom: 24px; box-shadow: var(--shadow); } .status-chips { display: flex; gap: 8px; flex-wrap: wrap; } .chip { display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px; border-radius: 20px; font-size: 13px; font-weight: 600; } .chip-critical { background: rgba(239,68,68,.12); color: var(--red); border: 1px solid rgba(239,68,68,.3); } .chip-warning { background: rgba(245,158,11,.12); color: var(--orange); border: 1px solid rgba(245,158,11,.3); } .chip-ok { background: rgba(16,185,129,.12); color: var(--green); border: 1px solid rgba(16,185,129,.3); } .status-meta { display: flex; align-items: center; gap: 12px; white-space: nowrap; } .last-check { font-size: 12px; color: var(--text-sub); } .btn-refresh { background: var(--blue-dim); border: 1px solid rgba(0,111,255,.3); color: var(--blue); border-radius: 6px; padding: 4px 12px; font-size: 12px; cursor: pointer; transition: background .15s; } .btn-refresh:hover { background: rgba(0,111,255,.2); } /* ── Sections ────────────────────────────────────────────────────────── */ .section { margin-bottom: 32px; } .section-title { font-size: 16px; font-weight: 700; margin-bottom: 14px; display: flex; align-items: center; gap: 8px; } .section-badge { font-size: 11px; font-weight: 600; background: var(--red); color: white; padding: 2px 7px; border-radius: 10px; } .section-badge:not(.badge-critical) { background: var(--grey); } /* ── Topology diagram ────────────────────────────────────────────────── */ .topology { background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px 16px 16px; margin-bottom: 20px; text-align: center; box-shadow: var(--shadow); overflow-x: auto; } .topo-row { display: flex; justify-content: center; gap: 16px; flex-wrap: wrap; } .topo-row-internet { margin-bottom: 4px; } .topo-hosts-row { flex-wrap: wrap; gap: 12px; } .topo-connectors { display: flex; justify-content: center; gap: 80px; height: 20px; margin: 2px 0; } .topo-connectors.single { gap: 0; } .topo-connectors.wide { gap: 60px; } .topo-line { width: 2px; height: 100%; background: var(--border); } .topo-line-labeled { position: relative; } .topo-line-labeled::after { content: attr(data-link-label); position: absolute; left: 6px; top: 50%; transform: translateY(-50%); font-size: 10px; color: var(--text-dim); white-space: nowrap; } .topo-node { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 8px 14px; border-radius: 8px; border: 1.5px solid var(--border); background: var(--grey-lt); min-width: 100px; font-size: 12px; position: relative; transition: border-color .2s; } .topo-internet { border-color: var(--blue); background: var(--blue-dim); font-weight: 600; } .topo-switch { border-color: var(--blue); background: var(--blue-dim); } .topo-host { cursor: default; } .topo-icon { font-size: 16px; } .topo-label { font-weight: 500; font-size: 11px; text-align: center; } .topo-badge { font-size: 10px; padding: 2px 6px; border-radius: 4px; font-weight: 600; } .topo-badge-up { background: rgba(16,185,129,.15); color: var(--green); } .topo-badge-down { background: rgba(239,68,68,.15); color: var(--red); } .topo-badge-degraded { background: rgba(245,158,11,.15); color: var(--orange); } .topo-status-{{ 'up' }} { border-color: var(--green); } .topo-status-down { border-color: var(--red); } .topo-status-degraded { border-color: var(--orange); } .topo-status-up { border-color: var(--green); } .topo-status-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--grey); position: absolute; top: 6px; right: 6px; } /* ── Host cards ──────────────────────────────────────────────────────── */ .host-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 14px; } .host-card { background: var(--card-bg); border: 1.5px solid var(--border); border-radius: var(--radius); padding: 14px; box-shadow: var(--shadow); transition: border-color .2s, box-shadow .2s; } .host-card:hover { box-shadow: 0 4px 16px rgba(0,0,0,.1); } .host-card-up { border-left: 4px solid var(--green); } .host-card-down { border-left: 4px solid var(--red); } .host-card-degraded { border-left: 4px solid var(--orange); } .host-card-header { margin-bottom: 10px; } .host-name-row { display: flex; align-items: center; gap: 7px; margin-bottom: 4px; } .host-name { font-weight: 700; font-size: 14px; } .host-meta { display: flex; gap: 8px; align-items: center; } .host-ip { font-family: var(--mono); font-size: 11px; color: var(--text-sub); } .host-source { font-size: 10px; padding: 1px 6px; border-radius: 4px; font-weight: 600; background: var(--grey-lt); color: var(--text-sub); } .source-prometheus { color: #E6522C; background: rgba(230,82,44,.1); } .source-ping { color: var(--blue); background: var(--blue-dim); } .iface-list { border-top: 1px solid var(--border); padding-top: 8px; margin-bottom: 10px; } .iface-row { display: flex; align-items: center; gap: 7px; padding: 3px 0; } .iface-name { font-family: var(--mono); font-size: 12px; flex: 1; color: var(--text); } .iface-state { font-size: 11px; font-weight: 600; } .state-up { color: var(--green); } .state-down { color: var(--red); } .host-ping-note { font-size: 11px; color: var(--text-sub); font-style: italic; margin-bottom: 10px; padding-top: 6px; border-top: 1px solid var(--border); } .host-actions { border-top: 1px solid var(--border); padding-top: 8px; } /* ── Status dots ─────────────────────────────────────────────────────── */ .host-status-dot, .iface-dot, .dot-up, .dot-down, .dot-degraded, .dot-unknown { display: inline-block; width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; } .dot-up, .host-status-dot.dot-up { background: var(--green); box-shadow: 0 0 0 2px rgba(16,185,129,.2); } .dot-down, .host-status-dot.dot-down { background: var(--red); box-shadow: 0 0 0 2px rgba(239,68,68,.2); animation: pulse-red 2s infinite; } .dot-degraded { background: var(--orange); box-shadow: 0 0 0 2px rgba(245,158,11,.2); } .dot-unknown { background: var(--grey); } @keyframes pulse-red { 0%,100% { box-shadow: 0 0 0 2px rgba(239,68,68,.2); } 50% { box-shadow: 0 0 0 5px rgba(239,68,68,.4); } } /* ── Badges ──────────────────────────────────────────────────────────── */ .badge { display: inline-block; padding: 2px 8px; border-radius: 6px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; } .badge-critical { background: rgba(239,68,68,.12); color: var(--red); } .badge-warning { background: rgba(245,158,11,.12); color: var(--orange); } .badge-info { background: rgba(0,111,255,.1); color: var(--blue); } .badge-ok { background: rgba(16,185,129,.12); color: var(--green); } .badge-neutral { background: var(--grey-lt); color: var(--grey); } .badge-suppressed { background: rgba(107,114,128,.12); color: var(--grey); font-size: 14px; padding: 0; } /* ── Tables ──────────────────────────────────────────────────────────── */ .table-wrap { background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden; } .data-table { width: 100%; border-collapse: collapse; } .data-table th { background: var(--grey-lt); padding: 10px 14px; text-align: left; font-size: 11px; font-weight: 700; color: var(--text-sub); text-transform: uppercase; letter-spacing: .06em; border-bottom: 1px solid var(--border); white-space: nowrap; } .data-table td { padding: 10px 14px; border-bottom: 1px solid var(--border); vertical-align: middle; } .data-table tr:last-child td { border-bottom: none; } .data-table tr:hover td { background: rgba(0,111,255,.03); } .row-critical td { background: rgba(239,68,68,.04); } .row-critical td:first-child { border-left: 3px solid var(--red); } .row-warning td { background: rgba(245,158,11,.04); } .row-warning td:first-child { border-left: 3px solid var(--orange); } .row-resolved td { opacity: .6; } .data-table-sm td, .data-table-sm th { padding: 7px 12px; font-size: 12px; } .ts-cell { font-family: var(--mono); font-size: 11px; color: var(--text-sub); } .desc-cell { max-width: 300px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .ticket-link { font-family: var(--mono); font-weight: 600; } .empty-state { padding: 32px; text-align: center; color: var(--text-sub); } .empty-row td { text-align: center; color: var(--text-sub); } /* ── Buttons ─────────────────────────────────────────────────────────── */ .btn { display: inline-flex; align-items: center; gap: 6px; padding: 8px 16px; border-radius: 6px; border: none; cursor: pointer; font-size: 13px; font-weight: 600; transition: opacity .15s, background .15s; } .btn:hover { opacity: .88; } .btn:active { opacity: .75; } .btn-primary { background: var(--blue); color: white; } .btn-secondary { background: var(--grey-lt); color: var(--text); border: 1px solid var(--border); } .btn-danger { background: rgba(239,68,68,.1); color: var(--red); border: 1px solid rgba(239,68,68,.2); } .btn-lg { padding: 10px 20px; font-size: 14px; } .btn-sm { padding: 3px 8px; font-size: 11px; border-radius: 5px; cursor: pointer; border: none; font-weight: 600; transition: opacity .15s; } .btn-suppress { background: rgba(107,114,128,.1); color: var(--grey); border: 1px solid var(--border) !important; } .btn-suppress:hover { background: rgba(107,114,128,.2); } .btn-danger.btn-sm { background: rgba(239,68,68,.1); color: var(--red); border: 1px solid rgba(239,68,68,.2) !important; } /* ── Modal ───────────────────────────────────────────────────────────── */ .modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 100; display: flex; align-items: center; justify-content: center; backdrop-filter: blur(2px); } .modal { background: var(--card-bg); border-radius: 12px; box-shadow: 0 20px 60px rgba(0,0,0,.2); width: 480px; max-width: 95vw; padding: 24px; } .modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } .modal-header h3 { font-size: 17px; font-weight: 700; } .modal-close { background: none; border: none; cursor: pointer; font-size: 18px; color: var(--text-sub); line-height: 1; padding: 2px 6px; border-radius: 4px; transition: background .15s; } .modal-close:hover { background: var(--grey-lt); } .modal-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--border); } /* ── Forms ───────────────────────────────────────────────────────────── */ .form-card { background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; box-shadow: var(--shadow); } .form-row { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 14px; } .form-row-align { align-items: flex-end; } .form-group { display: flex; flex-direction: column; gap: 5px; min-width: 180px; flex: 1; } .form-group-wide { flex: 3; } .form-group-submit { flex: 0 0 auto; min-width: unset; } .form-group label { font-size: 12px; font-weight: 600; color: var(--text-sub); text-transform: uppercase; letter-spacing: .05em; } .form-group input, .form-group select { padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 13px; background: white; color: var(--text); transition: border-color .15s, box-shadow .15s; } .form-group input:focus, .form-group select:focus { outline: none; border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-dim); } .form-hint { font-size: 11px; color: var(--text-sub); margin-top: 2px; } .required { color: var(--red); } /* ── Duration pills ──────────────────────────────────────────────────── */ .duration-pills { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 6px; } .pill { padding: 5px 12px; border-radius: 20px; border: 1.5px solid var(--border); background: white; font-size: 12px; font-weight: 600; cursor: pointer; color: var(--text-sub); transition: all .15s; } .pill:hover { border-color: var(--blue); color: var(--blue); } .pill.active, .pill-manual.active { background: var(--blue); border-color: var(--blue); color: white; } /* ── Targets grid (suppressions page) ───────────────────────────────── */ .targets-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; } .target-card { background: var(--card-bg); border: 1px solid var(--border); border-radius: 8px; padding: 12px; } .target-name { font-weight: 700; font-size: 14px; margin-bottom: 4px; } .target-type { font-size: 11px; color: var(--text-sub); margin-bottom: 8px; } .target-ifaces { display: flex; flex-wrap: wrap; gap: 4px; } .iface-chip { font-family: var(--mono); font-size: 10px; background: var(--grey-lt); border-radius: 4px; padding: 1px 6px; color: var(--text-sub); } /* ── Card (generic) ──────────────────────────────────────────────────── */ .card { background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; box-shadow: var(--shadow); } /* ── Toast notifications ─────────────────────────────────────────────── */ .toast-container { position: fixed; bottom: 24px; right: 24px; z-index: 200; display: flex; flex-direction: column; gap: 10px; } .toast { padding: 12px 20px; border-radius: 8px; font-size: 13px; font-weight: 600; box-shadow: 0 4px 16px rgba(0,0,0,.15); animation: slide-in .2s ease; } .toast-success { background: #065f46; color: white; } .toast-error { background: #7f1d1d; color: white; } @keyframes slide-in { from { transform: translateX(120%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } /* ── Responsive ──────────────────────────────────────────────────────── */ @media (max-width: 768px) { .host-grid { grid-template-columns: 1fr; } .topology { display: none; } .form-row { flex-direction: column; } .status-bar { flex-direction: column; align-items: flex-start; } }