/* GNSS Guard Server - Styles */ @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600&display=swap'); :root { --bg-main: #060b10; --bg-panel: #121821; --bg-card: #171f2b; --bg-card-alt: #1c2533; --border-subtle: #222b38; --text-main: #e5e9f5; --text-muted: #9aa3b8; --accent-red: #c62828; --accent-green: #1fad3a; --accent-amber: #ffa726; } * { box-sizing: border-box; } body { margin: 0; font-family: 'Montserrat', sans-serif; background: var(--bg-main); color: var(--text-main); } /* ============================================================================= LOGIN PAGE ============================================================================= */ .login-page { display: flex; align-items: center; justify-content: center; min-height: 100vh; background: radial-gradient(circle at top left, #182333 0, #05080d 55%); } .login-container { width: 100%; max-width: 420px; padding: 20px; } .login-box { background: var(--bg-panel); border-radius: 16px; padding: 40px; border: 1px solid var(--border-subtle); box-shadow: 0 24px 48px rgba(0,0,0,0.4); } .login-header { text-align: center; margin-bottom: 32px; } .login-title { font-size: 28px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; } .login-subtitle { font-size: 14px; color: var(--text-muted); margin-top: 8px; } .login-form { display: flex; flex-direction: column; gap: 20px; } .form-group { display: flex; flex-direction: column; gap: 8px; } .form-group label { font-size: 13px; font-weight: 500; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; } .form-group input { background: var(--bg-card); border: 1px solid var(--border-subtle); border-radius: 8px; padding: 12px 16px; font-size: 15px; color: var(--text-main); font-family: 'Montserrat', sans-serif; transition: border-color 0.2s; } .form-group input:focus { outline: none; border-color: var(--accent-green); } .form-error { background: rgba(198,40,40,0.15); border: 1px solid rgba(229,115,115,0.5); color: #ffcdd2; padding: 10px 14px; border-radius: 8px; font-size: 13px; } .form-error.hidden { display: none; } .login-btn { background: var(--accent-green); color: white; border: none; border-radius: 8px; padding: 14px; font-size: 15px; font-weight: 600; font-family: 'Montserrat', sans-serif; text-transform: uppercase; letter-spacing: 0.06em; cursor: pointer; transition: background 0.2s; } .login-btn:hover { background: #17a330; } .login-footer { text-align: center; margin-top: 24px; font-size: 12px; color: var(--text-muted); } /* ============================================================================= HEADER ============================================================================= */ .header { background: #05080d; color: var(--text-main); padding: 14px 28px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border-subtle); } .header-left { display: flex; flex-direction: column; } .header-title { font-size: 26px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; } .header-sub { font-size: 12px; color: var(--text-muted); margin-top: 4px; } .header-right { display: flex; gap: 16px; align-items: center; } .user-menu { display: flex; align-items: center; gap: 12px; } .user-name { font-size: 13px; color: var(--text-muted); } .logout-btn { background: transparent; border: 1px solid var(--border-subtle); color: var(--text-muted); padding: 6px 12px; border-radius: 6px; font-size: 12px; font-family: 'Montserrat', sans-serif; cursor: pointer; transition: all 0.2s; } .logout-btn:hover { background: var(--bg-card); color: var(--text-main); } .status-pill { padding: 6px 14px; border-radius: 999px; font-size: 13px; font-weight: 500; background: var(--bg-card); color: var(--text-muted); white-space: nowrap; border: 1px solid var(--border-subtle); } .status-pill.ok { background: var(--accent-green); color: #fff; border-color: var(--accent-green); } .status-pill.warn { background: var(--accent-amber); color: #111; border-color: var(--accent-amber); } .status-pill.crit { background: var(--accent-red); color: #fff; border-color: var(--accent-red); } /* ============================================================================= ALERT BANNER ============================================================================= */ .alert-banner { padding: 10px 28px; font-size: 14px; display: flex; align-items: center; gap: 12px; border-bottom: 1px solid var(--border-subtle); } .alert-banner.hidden { display: none; } .alert-critical { background: rgba(198, 40, 40, 0.18); color: #ffbdbd; } .alert-warning { background: rgba(255, 167, 38, 0.16); color: #ffe0b2; } .alert-indicator { width: 10px; height: 10px; border-radius: 50%; background: var(--accent-red); box-shadow: 0 0 8px rgba(198,40,40,0.9); } /* ============================================================================= MOBILE ASSET DROPDOWN ============================================================================= */ .mobile-asset-dropdown { display: none; padding: 12px 20px; background: #05080d; border-bottom: 1px solid var(--border-subtle); } .mobile-asset-dropdown select { width: 100%; background: var(--bg-card); border: 1px solid var(--border-subtle); color: var(--text-main); padding: 10px 14px; border-radius: 8px; font-family: 'Montserrat', sans-serif; font-size: 14px; } /* ============================================================================= GNSS STATUS CONTAINERS ============================================================================= */ /* Desktop GNSS status - visible by default */ .desktop-gnss-status { margin-bottom: 16px; } .desktop-gnss-status .status-pill { display: inline-block; } /* Mobile GNSS status - hidden by default, shown on mobile */ .mobile-gnss-status { display: none; padding: 10px 20px; background: #05080d; border-bottom: 1px solid var(--border-subtle); text-align: center; } .mobile-gnss-status .status-pill { display: inline-block; } /* ============================================================================= MAIN LAYOUT ============================================================================= */ .layout { display: flex; height: calc(100vh - 70px); background: radial-gradient(circle at top left, #182333 0, #05080d 55%); } /* ============================================================================= ASSET PANEL (Desktop) ============================================================================= */ .asset-panel { width: 220px; background: linear-gradient(180deg, #0a0f15 0%, #04070d 100%); border-right: 1px solid var(--border-subtle); padding: 16px; overflow-y: auto; } .asset-list { display: flex; flex-direction: column; gap: 8px; } .asset-loading { font-size: 13px; color: var(--text-muted); padding: 12px; text-align: center; } .asset-item { padding: 12px 14px; background: var(--bg-card); border-radius: 8px; cursor: pointer; border-left: 3px solid transparent; transition: all 0.2s; } .asset-item:hover { background: var(--bg-card-alt); } .asset-item.active { background: var(--bg-card-alt); border-left-color: var(--accent-green); } .asset-item.offline { opacity: 0.6; } .asset-item .asset-name { font-weight: 500; font-size: 14px; margin-bottom: 4px; } .asset-item .asset-status { font-size: 11px; display: flex; align-items: center; gap: 6px; } .asset-item .status-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--text-muted); } .asset-item .status-dot.online { background: var(--accent-green); } .asset-item .status-dot.alert { background: var(--accent-red); } .asset-item .status-dot.degraded { background: var(--accent-amber); } /* ============================================================================= TIME SELECTOR ============================================================================= */ .time-selector { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--border-subtle); } .time-radio-group { display: flex; flex-direction: column; gap: 8px; } .time-radio { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-muted); cursor: pointer; padding: 8px 10px; border-radius: 6px; transition: all 0.2s; } .time-radio:hover { background: var(--bg-card); } .time-radio input[type="radio"] { accent-color: var(--accent-green); width: 14px; height: 14px; margin: 0; } .time-radio input[type="radio"]:checked + span { color: var(--text-main); } .datetime-picker { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; } .datetime-picker.hidden { display: none; } .datetime-picker input[type="datetime-local"] { width: 100%; background: var(--bg-card); border: 1px solid var(--border-subtle); border-radius: 6px; padding: 8px 10px; font-size: 12px; color: var(--text-main); font-family: 'Montserrat', sans-serif; } .datetime-picker input[type="datetime-local"]:focus { outline: none; border-color: var(--accent-green); } /* Webkit datetime-local styling */ .datetime-picker input[type="datetime-local"]::-webkit-calendar-picker-indicator { filter: invert(0.7); cursor: pointer; } .apply-time-btn { background: var(--accent-green); color: white; border: none; border-radius: 6px; padding: 8px 14px; font-size: 12px; font-weight: 600; font-family: 'Montserrat', sans-serif; text-transform: uppercase; letter-spacing: 0.04em; cursor: pointer; transition: background 0.2s; } .apply-time-btn:hover { background: #17a330; } .selected-time-display { margin-top: 10px; padding: 8px 10px; background: rgba(31, 173, 58, 0.15); border: 1px solid rgba(31, 173, 58, 0.4); border-radius: 6px; font-size: 11px; color: #c8ffcf; } .selected-time-display.hidden { display: none; } .selected-time-display span { font-weight: 600; } /* Mobile time selector */ .mobile-time-selector { display: none; padding: 10px 20px 12px; background: #05080d; border-bottom: 1px solid var(--border-subtle); } .mobile-time-selector .time-radio-group { flex-direction: row; gap: 16px; } .mobile-time-selector .time-radio { padding: 6px 8px; } .mobile-time-selector .datetime-picker { flex-direction: row; align-items: center; gap: 10px; } .mobile-time-selector .datetime-picker input[type="datetime-local"] { flex: 1; } .mobile-time-selector .selected-time-display { margin-top: 8px; } /* ============================================================================= STATUS TAB ============================================================================= */ .tab-status { width: 420px; display: flex; flex-direction: column; background: linear-gradient(180deg, #0d121b 0%, #04070d 100%); border-right: 1px solid var(--border-subtle); } .left-panel { flex: 1; padding: 20px 20px 12px; overflow-y: auto; } .panel-title { font-weight: 600; font-size: 16px; margin-bottom: 12px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-muted); } .no-asset-selected { padding: 40px 20px; text-align: center; color: var(--text-muted); font-size: 14px; } /* ============================================================================= CARDS ============================================================================= */ .card { background: var(--bg-card); border-radius: 12px; padding: 14px 14px 12px; margin-bottom: 16px; border-left: 4px solid var(--border-subtle); box-shadow: 0 16px 30px rgba(0,0,0,0.35); } .card.ok { border-color: var(--accent-green); } .card.warn { border-color: var(--accent-amber); } .card.crit { border-color: var(--accent-red); } .card.stale { border-color: var(--accent-red); } .card.offline { border-color: #78909c; } .card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } .card-title { font-weight: 600; font-size: 15px; } .badge { padding: 3px 9px; border-radius: 999px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; border: 1px solid transparent; } .badge-healthy { background: rgba(31,173,58,0.18); border-color: rgba(31,173,58,0.8); color: #c8ffcf; } .badge-warning { background: rgba(255,167,38,0.14); border-color: rgba(255,183,77,0.9); color: #ffe0b2; } .badge-danger { background: rgba(198,40,40,0.18); border-color: rgba(229,115,115,0.9); color: #ffcdd2; } .badge-offline { background: rgba(120,144,156,0.14); border-color: rgba(144,164,174,0.9); color: #eceff1; } .badge-stale { background: rgba(198,40,40,0.18); border-color: rgba(229,115,115,0.9); color: #ffcdd2; } .card-line { font-size: 13px; color: var(--text-muted); margin-top: 2px; } .card-line strong { color: var(--text-main); font-weight: 500; } .card-line.alert { color: #ffbdbd; } .card-line .stale-text, .stale-text { color: #ff8a80; } /* ============================================================================= MAP ============================================================================= */ .map-panel { flex: 1; position: relative; } #map { height: 100%; width: 100%; } .map-overlay-legend { position: absolute; bottom: 14px; left: 16px; background: rgba(5,8,13,0.92); border-radius: 10px; padding: 10px 12px; font-size: 11px; color: var(--text-muted); border: 1px solid rgba(255,255,255,0.08); z-index: 1000; } .map-overlay-legend div { margin: 2px 0; } .legend-section { font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; margin-top: 8px; margin-bottom: 4px; color: var(--text-main); font-size: 10px; } .legend-section:first-child { margin-top: 0; } .legend-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 6px; vertical-align: middle; } .legend-primary { background: #9c27b0; } .legend-ais { background: #82b1ff; } .legend-starlink-gps { background: #ffeb3b; } .legend-starlink-location { background: #2ecc71; } .legend-secondary { background: #b0bec5; } .legend-valid { background: var(--accent-green); } .legend-degraded { background: var(--accent-amber); } .legend-alert { background: var(--accent-red); } .map-route-toggle { position: absolute; top: 14px; right: 16px; background: rgba(5,8,13,0.92); border-radius: 8px; padding: 8px 12px; font-size: 12px; color: var(--text-muted); border: 1px solid rgba(255,255,255,0.08); z-index: 1000; } .map-route-toggle label { display: flex; align-items: center; gap: 8px; cursor: pointer; } .map-route-toggle input { accent-color: var(--accent-green); } /* ============================================================================= EVENT LOG ============================================================================= */ .event-log { height: auto; flex-shrink: 0; background: #05080d; border-top: 1px solid var(--border-subtle); padding: 8px 20px 6px; overflow: hidden; } .event-log-title { font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-muted); margin-bottom: 4px; } .event { padding: 4px 0; border-bottom: 1px solid rgba(255,255,255,0.03); font-size: 12px; color: var(--text-muted); } .event span.level { font-weight: 600; margin-right: 6px; } .event.level-crit span.level { color: #ff8a80; } .event.level-warn span.level { color: #ffd180; } .event.level-info span.level { color: #81d4fa; } /* ============================================================================= COPYRIGHT ============================================================================= */ .copyright { padding: 8px 20px; font-size: 11px; color: var(--text-muted); text-align: center; background: #05080d; border-top: 1px solid var(--border-subtle); } /* ============================================================================= SCROLLBARS ============================================================================= */ .left-panel::-webkit-scrollbar, .event-log::-webkit-scrollbar, .asset-panel::-webkit-scrollbar { width: 6px; height: 6px; } .left-panel::-webkit-scrollbar-thumb, .event-log::-webkit-scrollbar-thumb, .asset-panel::-webkit-scrollbar-thumb { background: #263244; border-radius: 3px; } .left-panel::-webkit-scrollbar-track, .event-log::-webkit-scrollbar-track, .asset-panel::-webkit-scrollbar-track { background: transparent; } /* ============================================================================= MOBILE TABS ============================================================================= */ .mobile-tabs { display: none; background: #05080d; border-bottom: 1px solid var(--border-subtle); } .tab-btn { flex: 1; padding: 12px 16px; background: transparent; border: none; border-bottom: 2px solid transparent; color: var(--text-muted); font-family: 'Montserrat', sans-serif; font-size: 14px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.06em; cursor: pointer; transition: all 0.2s ease; } .tab-btn.active { color: var(--text-main); border-bottom-color: var(--accent-green); } .tab-btn:hover { color: var(--text-main); } /* Tab content visibility (desktop - map tab uses contents) */ .tab-map { display: contents; } /* ============================================================================= RESPONSIVE (Mobile/Portrait) ============================================================================= */ @media (max-width: 960px) { /* Hide desktop asset panel, show mobile dropdown */ .asset-panel { display: none; } .mobile-asset-dropdown { display: block; } .mobile-time-selector { display: block; } /* Show mobile GNSS status, hide desktop */ .mobile-gnss-status { display: block; } .desktop-gnss-status { display: none; } /* Show mobile tabs */ .mobile-tabs { display: flex; } /* Layout changes for portrait */ .layout { flex-direction: column; height: calc(100vh - 170px); overflow: hidden; } /* Tab content behavior */ .tab-status, .tab-map { display: none; flex-direction: column; width: 100%; height: 100%; overflow-y: auto; border-right: none; } .tab-content.active { display: flex; } /* Status tab - scrollable container */ .tab-status { overflow-y: auto; } .tab-status .left-panel { width: 100%; height: auto; border-right: none; border-bottom: none; overflow: visible; } .tab-status .event-log { border-top: 1px solid var(--border-subtle); margin-top: auto; } /* Map tab - full height */ .tab-map { height: 100%; } .tab-map .map-panel { width: 100%; height: 100%; } /* Adjust header for mobile */ .header { padding: 10px 16px; flex-wrap: wrap; } .header-title { font-size: 20px; } .user-menu { display: none; } } /* ============================================================================= LEAFLET CUSTOMIZATIONS ============================================================================= */ .leaflet-container { font-family: 'Montserrat', sans-serif; background: var(--bg-main); } /* Route point popup */ .route-popup { font-family: 'Montserrat', sans-serif; } .route-popup .popup-header { font-weight: 600; margin-bottom: 8px; padding-bottom: 6px; border-bottom: 1px solid #ddd; } .route-popup .popup-row { font-size: 12px; margin: 4px 0; } .route-popup .popup-row strong { color: #333; } .route-popup .status-valid { color: #2e7d32; } .route-popup .status-degraded { color: #f57c00; } .route-popup .status-alert { color: #c62828; }