/* Local Montserrat font */ @font-face { font-family: 'Montserrat'; font-style: normal; font-weight: 300; font-display: swap; src: url('/static/fonts/montserrat-300.woff2') format('woff2'); } @font-face { font-family: 'Montserrat'; font-style: normal; font-weight: 400; font-display: swap; src: url('/static/fonts/montserrat-400.woff2') format('woff2'); } @font-face { font-family: 'Montserrat'; font-style: normal; font-weight: 500; font-display: swap; src: url('/static/fonts/montserrat-500.woff2') format('woff2'); } @font-face { font-family: 'Montserrat'; font-style: normal; font-weight: 600; font-display: swap; src: url('/static/fonts/montserrat-600.woff2') format('woff2'); } :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; } html, body { margin: 0; padding: 0; overflow: hidden; height: 100%; } /* HEALTH STATUS BORDER FRAME */ .health-border-frame { position: fixed; top: 0; left: 0; right: 0; bottom: 0; pointer-events: none; z-index: 9999; border: 0 solid transparent; transition: border-color 0.3s ease, border-width 0.3s ease; } .health-border-frame.warn { border: 6px solid var(--accent-amber); box-shadow: inset 0 0 30px rgba(255, 167, 38, 0.3); } .health-border-frame.crit { border: 6px solid var(--accent-red); box-shadow: inset 0 0 30px rgba(198, 40, 40, 0.4); animation: border-pulse-crit 1.5s ease-in-out infinite; } @keyframes border-pulse-crit { 0%, 100% { box-shadow: inset 0 0 30px rgba(198, 40, 40, 0.4); } 50% { box-shadow: inset 0 0 50px rgba(198, 40, 40, 0.6); } } body { font-family: 'Montserrat', sans-serif; background: var(--bg-main); color: var(--text-main); } /* 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; } /* Status Pill Button - Touch-friendly alarm acknowledge button */ .status-pill-btn { display: flex; align-items: center; gap: 10px; padding: 12px 20px; border-radius: 999px; font-size: 15px; font-weight: 600; font-family: 'Montserrat', sans-serif; background: var(--accent-green); color: #fff; white-space: nowrap; border: none; cursor: pointer; transition: all 0.2s ease; min-height: 48px; /* Touch-friendly minimum size */ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); } .status-pill-btn:hover { transform: scale(1.02); box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4); } .status-pill-btn:active { transform: scale(0.98); } .status-pill-btn.warn { background: var(--accent-amber); color: #111; } .status-pill-btn.crit { background: var(--accent-red); color: #fff; } /* Speaker icon container - hidden by default, shown when alarm is active */ .speaker-icon { display: none; align-items: center; justify-content: center; } .speaker-icon svg { width: 20px; height: 20px; } /* Hide muted icon by default, show sound icon */ .speaker-icon .icon-muted { display: none; } .speaker-icon .icon-sound { display: block; } /* Show speaker icon container when alarm state (warn or crit) */ .status-pill-btn.warn .speaker-icon, .status-pill-btn.crit .speaker-icon { display: flex; } /* Pulsing animation for speaker icon when alarm is active (not acknowledged) */ .status-pill-btn.alarm-active .speaker-icon { animation: speaker-pulse 1s ease-in-out infinite; } @keyframes speaker-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.6; transform: scale(1.1); } } /* Acknowledged state - show muted icon, hide sound icon */ .status-pill-btn.alarm-acknowledged .speaker-icon .icon-sound { display: none; } .status-pill-btn.alarm-acknowledged .speaker-icon .icon-muted { display: block; } .status-pill-btn.alarm-acknowledged .speaker-icon { animation: none; opacity: 0.7; } /* Legacy support for non-button status pill */ .status-pill { padding: 6px 14px; border-radius: 999px; font-size: 13px; font-weight: 500; background: var(--accent-green); color: #fff; white-space: nowrap; } .status-pill.warn { background: var(--accent-amber); color: #111; } .status-pill.crit { background: var(--accent-red); color: #fff; } /* 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); } /* MAIN LAYOUT */ .layout { display: flex; height: calc(100vh - 70px); background: radial-gradient(circle at top left, #182333 0, #05080d 55%); overflow: hidden; } /* STATUS TAB (desktop: left column with sources + event log) */ .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 (sources area) */ .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); } /* 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-amber); } .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-clean { background: rgba(76,175,80,0.12); border-color: rgba(129,199,132,0.9); color: #e0ffea; } .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(255,167,38,0.14); border-color: rgba(255,183,77,0.9); color: #ffe0b2; } .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: #ffd180; } /* MAP */ .map-panel { flex: 1; position: relative; } #map { height: 100%; width: 100%; } .map-overlay-legend { position: absolute; bottom: 14px; left: 16px; z-index: 1000; background: rgba(5,8,13,0.86); border-radius: 10px; padding: 8px 10px; font-size: 11px; color: var(--text-muted); border: 1px solid rgba(255,255,255,0.08); } .map-overlay-legend div { margin: 2px 0; } .legend-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 6px; vertical-align: middle; } .legend-section { font-weight: 600; color: var(--text-main); margin-top: 6px; margin-bottom: 2px; font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; } .legend-section:first-child { margin-top: 0; } .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: #1fad3a; } .legend-degraded { background: #ffa726; } .legend-alert { background: #c62828; } /* Route toggle */ .map-route-toggle { position: absolute; top: 14px; right: 16px; z-index: 1000; background: rgba(5,8,13,0.86); border-radius: 8px; padding: 8px 12px; font-size: 12px; color: var(--text-muted); border: 1px solid rgba(255,255,255,0.08); } .map-route-toggle label { display: flex; align-items: center; gap: 8px; cursor: pointer; } .map-route-toggle input { cursor: pointer; } /* Route point popup */ .route-popup { font-size: 12px; } .route-popup .popup-header { font-weight: 600; margin-bottom: 6px; padding: 2px 6px; border-radius: 4px; } .route-popup .popup-row { margin: 2px 0; } .route-popup .popup-row strong { color: #666; } .route-popup .status-valid { background: rgba(31,173,58,0.2); color: #1fad3a; } .route-popup .status-degraded { background: rgba(255,167,38,0.2); color: #ffa726; } .route-popup .status-alert { background: rgba(198,40,40,0.2); color: #c62828; } /* 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 (webkit) */ .left-panel::-webkit-scrollbar, .event-log::-webkit-scrollbar { width: 6px; height: 6px; } .left-panel::-webkit-scrollbar-thumb, .event-log::-webkit-scrollbar-thumb { background: #263244; border-radius: 3px; } .left-panel::-webkit-scrollbar-track, .event-log::-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; } @media (max-width: 960px) { /* Show mobile tabs */ .mobile-tabs { display: flex; } /* Layout changes for portrait */ .layout { flex-direction: column; height: calc(100vh - 120px); 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%; } } /* Leaflet custom styles */ .leaflet-container { font-family: 'Montserrat', sans-serif; background: var(--bg-main); } /* Larger zoom controls (1.5x) */ .leaflet-control-zoom a { width: 44px !important; height: 44px !important; line-height: 44px !important; font-size: 27px !important; } .leaflet-control-zoom { border-radius: 6px !important; }