From 07d6ef984eddd2b269d43b3f5b487e07e6ec7cc4 Mon Sep 17 00:00:00 2001 From: Smittix Date: Wed, 4 Feb 2026 01:10:42 +0000 Subject: [PATCH] Switch app font to JetBrains Mono --- static/css/adsb_dashboard.css | 4 +- static/css/adsb_history.css | 2 +- static/css/ais_dashboard.css | 12 +- static/css/components/device-cards.css | 8 +- static/css/components/signal-cards.css | 4 +- static/css/core/variables.css | 396 +++---- static/css/fonts-local.css | 127 ++- static/css/global-nav.css | 6 +- static/css/index.css | 40 +- static/css/responsive.css | 1317 ++++++++++++------------ static/css/satellite_dashboard.css | 4 +- static/js/core/app.js | 4 +- static/js/core/settings-manager.js | 4 +- static/js/modes/listening-post.js | 2 +- static/js/modes/spy-stations.js | 2 +- static/vendor/fonts/Terminus.ttf | Bin 118548 -> 0 bytes templates/adsb_dashboard.html | 8 +- templates/adsb_history.html | 12 +- templates/ais_dashboard.html | 2 +- templates/index.html | 12 +- templates/layout/base.html | 12 +- templates/network_monitor.html | 6 +- templates/partials/settings-modal.html | 5 +- templates/satellite_dashboard.html | 10 +- 24 files changed, 989 insertions(+), 1010 deletions(-) delete mode 100644 static/vendor/fonts/Terminus.ttf diff --git a/static/css/adsb_dashboard.css b/static/css/adsb_dashboard.css index 8ef6822..1b8e9ab 100644 --- a/static/css/adsb_dashboard.css +++ b/static/css/adsb_dashboard.css @@ -25,7 +25,7 @@ } body { - font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + font-family: var(--font-sans); background: var(--bg-dark); color: var(--text-primary); min-height: 100vh; @@ -94,7 +94,7 @@ body { } .logo { - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 16px; font-weight: 700; letter-spacing: 2px; diff --git a/static/css/adsb_history.css b/static/css/adsb_history.css index f24740b..1261599 100644 --- a/static/css/adsb_history.css +++ b/static/css/adsb_history.css @@ -20,7 +20,7 @@ } body { - font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + font-family: var(--font-sans); background: var(--bg-dark); color: var(--text-primary); min-height: 100vh; diff --git a/static/css/ais_dashboard.css b/static/css/ais_dashboard.css index 8a8b7c0..38985fc 100644 --- a/static/css/ais_dashboard.css +++ b/static/css/ais_dashboard.css @@ -28,7 +28,7 @@ } body { - font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + font-family: var(--font-sans); background: var(--bg-dark); color: var(--text-primary); min-height: 100vh; @@ -97,7 +97,7 @@ body { } .logo { - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 16px; font-weight: 700; letter-spacing: 2px; @@ -438,7 +438,7 @@ body { padding: 10px 15px; background: rgba(74, 158, 255, 0.05); border-bottom: 1px solid rgba(74, 158, 255, 0.1); - font-family: 'Orbitron', 'Terminus', monospace; + font-family: 'Orbitron', 'JetBrains Mono', monospace; font-size: 11px; font-weight: 500; letter-spacing: 2px; @@ -510,7 +510,7 @@ body { } .vessel-name { - font-family: 'Orbitron', 'Terminus', monospace; + font-family: 'Orbitron', 'JetBrains Mono', monospace; font-size: 16px; font-weight: 700; color: var(--accent-cyan); @@ -604,7 +604,7 @@ body { } .vessel-item-name { - font-family: 'Orbitron', 'Terminus', monospace; + font-family: 'Orbitron', 'JetBrains Mono', monospace; font-size: 12px; font-weight: 600; color: var(--accent-cyan); @@ -1148,7 +1148,7 @@ body { } .dsc-distress-alert .dsc-alert-header { - font-family: 'Orbitron', 'Terminus', monospace; + font-family: 'Orbitron', 'JetBrains Mono', monospace; font-size: 24px; font-weight: 700; color: var(--accent-red); diff --git a/static/css/components/device-cards.css b/static/css/components/device-cards.css index 645d258..af6f39b 100644 --- a/static/css/components/device-cards.css +++ b/static/css/components/device-cards.css @@ -53,7 +53,7 @@ } .device-name { - font-family: 'Inter', -apple-system, sans-serif; + font-family: var(--font-sans); font-size: 14px; font-weight: 600; color: var(--text-primary, #e0e0e0); @@ -221,7 +221,7 @@ } .device-range-band .range-label { - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 11px; font-weight: 600; color: var(--range-color); @@ -262,7 +262,7 @@ } .device-manufacturer .mfr-name { - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); } /* ============================================ @@ -426,7 +426,7 @@ } .message-card-title { - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 13px; font-weight: 600; color: var(--text-primary, #e0e0e0); diff --git a/static/css/components/signal-cards.css b/static/css/components/signal-cards.css index 19e5e73..87e65c4 100644 --- a/static/css/components/signal-cards.css +++ b/static/css/components/signal-cards.css @@ -107,7 +107,7 @@ display: inline-flex; align-items: center; gap: 5px; - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 11px; font-weight: 500; padding: 5px 10px; @@ -864,7 +864,7 @@ .signal-search-input { width: 100%; padding: 6px 10px; - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 11px; color: var(--text-primary); background: var(--bg-card); diff --git a/static/css/core/variables.css b/static/css/core/variables.css index 318717e..0c1d969 100644 --- a/static/css/core/variables.css +++ b/static/css/core/variables.css @@ -1,198 +1,198 @@ -/** - * INTERCEPT Design Tokens - * Single source of truth for colors, spacing, typography, and effects - * Import this file FIRST in any stylesheet that needs design tokens - */ - -:root { - /* ============================================ - COLOR PALETTE - Dark Theme (Default) - ============================================ */ - - /* Backgrounds - layered depth system */ - --bg-primary: #0a0c10; - --bg-secondary: #0f1218; - --bg-tertiary: #151a23; - --bg-card: #121620; - --bg-elevated: #1a202c; - --bg-overlay: rgba(0, 0, 0, 0.7); - - /* Background aliases for components */ - --bg-dark: var(--bg-primary); - --bg-panel: var(--bg-secondary); - - /* Accent colors */ - --accent-cyan: #4a9eff; - --accent-cyan-dim: rgba(74, 158, 255, 0.15); - --accent-cyan-hover: #6bb3ff; - --accent-green: #22c55e; - --accent-green-dim: rgba(34, 197, 94, 0.15); - --accent-red: #ef4444; - --accent-red-dim: rgba(239, 68, 68, 0.15); - --accent-orange: #f59e0b; - --accent-orange-dim: rgba(245, 158, 11, 0.15); - --accent-amber: #d4a853; - --accent-amber-dim: rgba(212, 168, 83, 0.15); - --accent-yellow: #eab308; - --accent-purple: #a855f7; - - /* Text hierarchy */ - --text-primary: #e8eaed; - --text-secondary: #9ca3af; - --text-dim: #4b5563; - --text-muted: #374151; - --text-inverse: #0a0c10; - - /* Borders */ - --border-color: #1f2937; - --border-light: #374151; - --border-glow: rgba(74, 158, 255, 0.2); - --border-focus: var(--accent-cyan); - - /* Status colors */ - --status-online: #22c55e; - --status-warning: #f59e0b; - --status-error: #ef4444; - --status-offline: #6b7280; - --status-info: #3b82f6; - - /* ============================================ - SPACING SCALE - ============================================ */ - --space-1: 4px; - --space-2: 8px; - --space-3: 12px; - --space-4: 16px; - --space-5: 20px; - --space-6: 24px; - --space-8: 32px; - --space-10: 40px; - --space-12: 48px; - --space-16: 64px; - - /* ============================================ - TYPOGRAPHY - ============================================ */ - --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; - --font-mono: 'Terminus', 'JetBrains Mono', 'Fira Code', 'Consolas', monospace; - - /* Font sizes */ - --text-xs: 10px; - --text-sm: 12px; - --text-base: 14px; - --text-lg: 16px; - --text-xl: 18px; - --text-2xl: 20px; - --text-3xl: 24px; - --text-4xl: 30px; - - /* Font weights */ - --font-normal: 400; - --font-medium: 500; - --font-semibold: 600; - --font-bold: 700; - - /* Line heights */ - --leading-tight: 1.25; - --leading-normal: 1.5; - --leading-relaxed: 1.75; - - /* ============================================ - BORDERS & RADIUS - ============================================ */ - --radius-sm: 4px; - --radius-md: 6px; - --radius-lg: 8px; - --radius-xl: 12px; - --radius-full: 9999px; - - /* ============================================ - SHADOWS - ============================================ */ - --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3); - --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4); - --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.5); - --shadow-glow: 0 0 20px rgba(74, 158, 255, 0.15); - - /* ============================================ - TRANSITIONS - ============================================ */ - --transition-fast: 150ms ease; - --transition-base: 200ms ease; - --transition-slow: 300ms ease; - - /* ============================================ - Z-INDEX SCALE - ============================================ */ - --z-base: 0; - --z-dropdown: 100; - --z-sticky: 200; - --z-fixed: 300; - --z-modal-backdrop: 400; - --z-modal: 500; - --z-toast: 600; - --z-tooltip: 700; - - /* ============================================ - LAYOUT - ============================================ */ - --header-height: 60px; - --nav-height: 44px; - --sidebar-width: 280px; - --stats-strip-height: 36px; - --content-max-width: 1400px; -} - -/* ============================================ - LIGHT THEME OVERRIDES - ============================================ */ -[data-theme="light"] { - --bg-primary: #f8fafc; - --bg-secondary: #f1f5f9; - --bg-tertiary: #e2e8f0; - --bg-card: #ffffff; - --bg-elevated: #f8fafc; - --bg-overlay: rgba(255, 255, 255, 0.9); - - /* Background aliases for components */ - --bg-dark: var(--bg-primary); - --bg-panel: var(--bg-secondary); - - --accent-cyan: #2563eb; - --accent-cyan-dim: rgba(37, 99, 235, 0.1); - --accent-cyan-hover: #1d4ed8; - --accent-green: #16a34a; - --accent-green-dim: rgba(22, 163, 74, 0.1); - --accent-red: #dc2626; - --accent-red-dim: rgba(220, 38, 38, 0.1); - --accent-orange: #d97706; - --accent-orange-dim: rgba(217, 119, 6, 0.1); - --accent-amber: #b45309; - --accent-amber-dim: rgba(180, 83, 9, 0.1); - - --text-primary: #0f172a; - --text-secondary: #475569; - --text-dim: #94a3b8; - --text-muted: #cbd5e1; - --text-inverse: #f8fafc; - - --border-color: #e2e8f0; - --border-light: #cbd5e1; - --border-glow: rgba(37, 99, 235, 0.15); - - --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); - --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); - --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.15); - --shadow-glow: 0 0 20px rgba(37, 99, 235, 0.1); -} - -/* ============================================ - REDUCED MOTION - ============================================ */ -@media (prefers-reduced-motion: reduce) { - :root { - --transition-fast: 0ms; - --transition-base: 0ms; - --transition-slow: 0ms; - } -} +/** + * INTERCEPT Design Tokens + * Single source of truth for colors, spacing, typography, and effects + * Import this file FIRST in any stylesheet that needs design tokens + */ + +:root { + /* ============================================ + COLOR PALETTE - Dark Theme (Default) + ============================================ */ + + /* Backgrounds - layered depth system */ + --bg-primary: #0a0c10; + --bg-secondary: #0f1218; + --bg-tertiary: #151a23; + --bg-card: #121620; + --bg-elevated: #1a202c; + --bg-overlay: rgba(0, 0, 0, 0.7); + + /* Background aliases for components */ + --bg-dark: var(--bg-primary); + --bg-panel: var(--bg-secondary); + + /* Accent colors */ + --accent-cyan: #4a9eff; + --accent-cyan-dim: rgba(74, 158, 255, 0.15); + --accent-cyan-hover: #6bb3ff; + --accent-green: #22c55e; + --accent-green-dim: rgba(34, 197, 94, 0.15); + --accent-red: #ef4444; + --accent-red-dim: rgba(239, 68, 68, 0.15); + --accent-orange: #f59e0b; + --accent-orange-dim: rgba(245, 158, 11, 0.15); + --accent-amber: #d4a853; + --accent-amber-dim: rgba(212, 168, 83, 0.15); + --accent-yellow: #eab308; + --accent-purple: #a855f7; + + /* Text hierarchy */ + --text-primary: #e8eaed; + --text-secondary: #9ca3af; + --text-dim: #4b5563; + --text-muted: #374151; + --text-inverse: #0a0c10; + + /* Borders */ + --border-color: #1f2937; + --border-light: #374151; + --border-glow: rgba(74, 158, 255, 0.2); + --border-focus: var(--accent-cyan); + + /* Status colors */ + --status-online: #22c55e; + --status-warning: #f59e0b; + --status-error: #ef4444; + --status-offline: #6b7280; + --status-info: #3b82f6; + + /* ============================================ + SPACING SCALE + ============================================ */ + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-5: 20px; + --space-6: 24px; + --space-8: 32px; + --space-10: 40px; + --space-12: 48px; + --space-16: 64px; + + /* ============================================ + TYPOGRAPHY + ============================================ */ + --font-sans: 'JetBrains Mono', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + --font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace; + + /* Font sizes */ + --text-xs: 10px; + --text-sm: 12px; + --text-base: 14px; + --text-lg: 16px; + --text-xl: 18px; + --text-2xl: 20px; + --text-3xl: 24px; + --text-4xl: 30px; + + /* Font weights */ + --font-normal: 400; + --font-medium: 500; + --font-semibold: 600; + --font-bold: 700; + + /* Line heights */ + --leading-tight: 1.25; + --leading-normal: 1.5; + --leading-relaxed: 1.75; + + /* ============================================ + BORDERS & RADIUS + ============================================ */ + --radius-sm: 4px; + --radius-md: 6px; + --radius-lg: 8px; + --radius-xl: 12px; + --radius-full: 9999px; + + /* ============================================ + SHADOWS + ============================================ */ + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3); + --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4); + --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.5); + --shadow-glow: 0 0 20px rgba(74, 158, 255, 0.15); + + /* ============================================ + TRANSITIONS + ============================================ */ + --transition-fast: 150ms ease; + --transition-base: 200ms ease; + --transition-slow: 300ms ease; + + /* ============================================ + Z-INDEX SCALE + ============================================ */ + --z-base: 0; + --z-dropdown: 100; + --z-sticky: 200; + --z-fixed: 300; + --z-modal-backdrop: 400; + --z-modal: 500; + --z-toast: 600; + --z-tooltip: 700; + + /* ============================================ + LAYOUT + ============================================ */ + --header-height: 60px; + --nav-height: 44px; + --sidebar-width: 280px; + --stats-strip-height: 36px; + --content-max-width: 1400px; +} + +/* ============================================ + LIGHT THEME OVERRIDES + ============================================ */ +[data-theme="light"] { + --bg-primary: #f8fafc; + --bg-secondary: #f1f5f9; + --bg-tertiary: #e2e8f0; + --bg-card: #ffffff; + --bg-elevated: #f8fafc; + --bg-overlay: rgba(255, 255, 255, 0.9); + + /* Background aliases for components */ + --bg-dark: var(--bg-primary); + --bg-panel: var(--bg-secondary); + + --accent-cyan: #2563eb; + --accent-cyan-dim: rgba(37, 99, 235, 0.1); + --accent-cyan-hover: #1d4ed8; + --accent-green: #16a34a; + --accent-green-dim: rgba(22, 163, 74, 0.1); + --accent-red: #dc2626; + --accent-red-dim: rgba(220, 38, 38, 0.1); + --accent-orange: #d97706; + --accent-orange-dim: rgba(217, 119, 6, 0.1); + --accent-amber: #b45309; + --accent-amber-dim: rgba(180, 83, 9, 0.1); + + --text-primary: #0f172a; + --text-secondary: #475569; + --text-dim: #94a3b8; + --text-muted: #cbd5e1; + --text-inverse: #f8fafc; + + --border-color: #e2e8f0; + --border-light: #cbd5e1; + --border-glow: rgba(37, 99, 235, 0.15); + + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); + --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.15); + --shadow-glow: 0 0 20px rgba(37, 99, 235, 0.1); +} + +/* ============================================ + REDUCED MOTION + ============================================ */ +@media (prefers-reduced-motion: reduce) { + :root { + --transition-fast: 0ms; + --transition-base: 0ms; + --transition-slow: 0ms; + } +} diff --git a/static/css/fonts-local.css b/static/css/fonts-local.css index 49e61c6..d605b40 100644 --- a/static/css/fonts-local.css +++ b/static/css/fonts-local.css @@ -1,63 +1,63 @@ -/* Local font declarations for offline mode */ - -/* Inter - Primary UI font */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/static/vendor/fonts/Inter-Regular.woff2') format('woff2'); -} - -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('/static/vendor/fonts/Inter-Medium.woff2') format('woff2'); -} - -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('/static/vendor/fonts/Inter-SemiBold.woff2') format('woff2'); -} - -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url('/static/vendor/fonts/Inter-Bold.woff2') format('woff2'); -} - +/* Local font declarations for offline mode */ + +/* Inter - Primary UI font */ +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/static/vendor/fonts/Inter-Regular.woff2') format('woff2'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url('/static/vendor/fonts/Inter-Medium.woff2') format('woff2'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url('/static/vendor/fonts/Inter-SemiBold.woff2') format('woff2'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('/static/vendor/fonts/Inter-Bold.woff2') format('woff2'); +} + /* JetBrains Mono - Monospace/code font */ -@font-face { - font-family: 'JetBrains Mono'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/static/vendor/fonts/JetBrainsMono-Regular.woff2') format('woff2'); -} - -@font-face { - font-family: 'JetBrains Mono'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('/static/vendor/fonts/JetBrainsMono-Medium.woff2') format('woff2'); -} - -@font-face { - font-family: 'JetBrains Mono'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('/static/vendor/fonts/JetBrainsMono-SemiBold.woff2') format('woff2'); -} - +@font-face { + font-family: 'JetBrains Mono'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/static/vendor/fonts/JetBrainsMono-Regular.woff2') format('woff2'); +} + +@font-face { + font-family: 'JetBrains Mono'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url('/static/vendor/fonts/JetBrainsMono-Medium.woff2') format('woff2'); +} + +@font-face { + font-family: 'JetBrains Mono'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url('/static/vendor/fonts/JetBrainsMono-SemiBold.woff2') format('woff2'); +} + @font-face { font-family: 'JetBrains Mono'; font-style: normal; @@ -65,12 +65,3 @@ font-display: swap; src: url('/static/vendor/fonts/JetBrainsMono-Bold.woff2') format('woff2'); } - -/* Terminus - Monospace UI font */ -@font-face { - font-family: 'Terminus'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/static/vendor/fonts/Terminus.ttf') format('truetype'); -} diff --git a/static/css/global-nav.css b/static/css/global-nav.css index 9b10fbb..7f36cc2 100644 --- a/static/css/global-nav.css +++ b/static/css/global-nav.css @@ -69,7 +69,7 @@ border: 1px solid transparent; border-radius: 6px; color: var(--text-secondary, #b7c1cf); - font-family: 'Space Grotesk', 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 11px; font-weight: 500; cursor: pointer; @@ -116,7 +116,7 @@ border: 1px solid var(--border-light, #2b3645); border-radius: 6px; color: var(--text-primary, #e7ebf2); - font-family: 'Space Grotesk', 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 11px; font-weight: 500; text-decoration: none; @@ -152,7 +152,7 @@ border: 1px solid transparent; border-radius: 6px; color: var(--text-secondary, #b7c1cf); - font-family: 'Space Grotesk', 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 11px; font-weight: 500; cursor: pointer; diff --git a/static/css/index.css b/static/css/index.css index 2cbf607..79d955f 100644 --- a/static/css/index.css +++ b/static/css/index.css @@ -1,13 +1,3 @@ -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap'); - -@font-face { - font-family: 'Terminus'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/static/vendor/fonts/Terminus.ttf') format('truetype'); -} - * { box-sizing: border-box; margin: 0; @@ -81,7 +71,7 @@ } body { - font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + font-family: var(--font-sans); background: var(--bg-primary); color: var(--text-primary); min-height: 100vh; @@ -348,7 +338,7 @@ body { } .changelog-date { - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 0.7rem; color: var(--text-dim); } @@ -360,7 +350,7 @@ body { } .changelog-list li { - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 0.75rem; color: var(--text-secondary); margin-bottom: 6px; @@ -452,7 +442,7 @@ body { } .mode-card .mode-desc { - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 0.65rem; color: var(--text-dim); margin-top: 4px; @@ -525,7 +515,7 @@ body { } .welcome-footer p { - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 0.7rem; color: var(--text-dim); letter-spacing: 0.1em; @@ -739,7 +729,7 @@ header h1 { border: 1px solid transparent; border-radius: 4px; color: var(--text-secondary); - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 11px; font-weight: 500; cursor: pointer; @@ -786,7 +776,7 @@ header h1 { border: 1px solid var(--accent-cyan); border-radius: 4px; color: var(--accent-cyan); - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 11px; font-weight: 500; text-decoration: none; @@ -822,7 +812,7 @@ header h1 { border: 1px solid transparent; border-radius: 4px; color: var(--text-secondary); - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 11px; font-weight: 500; cursor: pointer; @@ -1665,7 +1655,7 @@ header h1 .tagline { background: var(--accent-green); border: none; color: #fff; - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 12px; font-weight: 600; text-transform: uppercase; @@ -1702,7 +1692,7 @@ header h1 .tagline { background: var(--accent-red); border: none; color: #fff; - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 12px; font-weight: 600; text-transform: uppercase; @@ -2097,7 +2087,7 @@ header h1 .tagline { text-transform: uppercase; letter-spacing: 1px; transition: all 0.2s ease; - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); } .control-btn:hover { @@ -2512,7 +2502,7 @@ header h1 .tagline { border-radius: 4px; color: var(--text-secondary); cursor: pointer; - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 11px; text-transform: uppercase; transition: all 0.2s ease; @@ -4892,7 +4882,7 @@ body::before { color: #000; border: none; padding: 12px 40px; - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 14px; font-weight: 600; letter-spacing: 2px; @@ -6027,7 +6017,7 @@ body::before { } .module-header { - font-family: 'Orbitron', 'Terminus', monospace; + font-family: 'Orbitron', 'JetBrains Mono', monospace; font-size: 10px; font-weight: 600; color: var(--accent-cyan); @@ -6194,7 +6184,7 @@ body::before { /* Listening Mode Selector Buttons */ .radio-mode-btn { padding: 12px 24px; - font-family: 'Orbitron', 'Terminus', monospace; + font-family: 'Orbitron', 'JetBrains Mono', monospace; font-size: 13px; font-weight: 600; text-transform: uppercase; diff --git a/static/css/responsive.css b/static/css/responsive.css index 9f4223f..c7e1f0c 100644 --- a/static/css/responsive.css +++ b/static/css/responsive.css @@ -3,667 +3,658 @@ Shared responsive foundation for all pages ============================================ */ -/* Terminus - bundled monospace */ -@font-face { - font-family: 'Terminus'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/static/vendor/fonts/Terminus.ttf') format('truetype'); +/* ============== CSS VARIABLES ============== */ +:root { + /* Touch targets */ + --touch-min: 44px; + --touch-comfortable: 48px; + + /* Responsive spacing */ + --spacing-xs: clamp(4px, 1vw, 8px); + --spacing-sm: clamp(8px, 2vw, 12px); + --spacing-md: clamp(12px, 3vw, 20px); + --spacing-lg: clamp(16px, 4vw, 32px); + + /* Responsive typography */ + --font-xs: clamp(10px, 2.5vw, 11px); + --font-sm: clamp(11px, 2.8vw, 12px); + --font-base: clamp(13px, 3vw, 14px); + --font-md: clamp(14px, 3.5vw, 16px); + --font-lg: clamp(16px, 4vw, 20px); + --font-xl: clamp(20px, 5vw, 28px); + --font-2xl: clamp(24px, 6vw, 40px); + + /* Header height for calculations */ + --header-height: 52px; + --nav-height: 44px; +} + +@media (min-width: 768px) { + :root { + --header-height: 60px; + --nav-height: 48px; + } +} + +@media (min-width: 1024px) { + :root { + --header-height: 96px; + --nav-height: 0px; + } +} + +/* ============== VIEWPORT HEIGHT FIX ============== */ +/* Handles iOS Safari address bar and dynamic viewport */ +.full-height { + height: 100dvh; + height: 100vh; /* Fallback */ +} + +@supports (-webkit-touch-callout: none) { + .full-height { + height: -webkit-fill-available; + } +} + +/* ============== HAMBURGER BUTTON ============== */ +.hamburger-btn { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: var(--touch-min); + height: var(--touch-min); + padding: 10px; + background: transparent; + border: 1px solid var(--border-color, #1f2937); + border-radius: 6px; + cursor: pointer; + position: relative; + z-index: 1001; + flex-shrink: 0; + transition: background 0.2s ease, border-color 0.2s ease; +} + +.hamburger-btn:hover { + background: var(--bg-tertiary, #151a23); + border-color: var(--accent-cyan, #4a9eff); +} + +.hamburger-btn span { + display: block; + width: 18px; + height: 2px; + background: var(--accent-cyan, #4a9eff); + margin: 2px 0; + border-radius: 1px; + transition: transform 0.3s ease, opacity 0.3s ease; +} + +.hamburger-btn.active span:nth-child(1) { + transform: rotate(45deg) translate(4px, 4px); +} + +.hamburger-btn.active span:nth-child(2) { + opacity: 0; +} + +.hamburger-btn.active span:nth-child(3) { + transform: rotate(-45deg) translate(4px, -4px); +} + +/* Hide hamburger on desktop */ +@media (min-width: 1024px) { + .hamburger-btn { + display: none; + } +} + +/* ============== MOBILE DRAWER ============== */ +.mobile-drawer { + position: fixed; + top: 0; + left: 0; + width: min(320px, 85vw); + height: 100dvh; + height: 100vh; /* Fallback */ + background: var(--bg-secondary, #0f1218); + border-right: 1px solid var(--border-color, #1f2937); + transform: translateX(-100%); + transition: transform 0.3s ease; + z-index: 1000; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + padding-top: 60px; +} + +.mobile-drawer.open { + transform: translateX(0); +} + +/* Show sidebar normally on desktop */ +@media (min-width: 1024px) { + .mobile-drawer { + position: static; + transform: none; + width: auto; + height: auto; + padding-top: 0; + z-index: auto; + } +} + +/* ============== DRAWER OVERLAY ============== */ +.drawer-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(2px); + opacity: 0; + visibility: hidden; + transition: opacity 0.3s ease, visibility 0.3s ease; + z-index: 999; +} + +.drawer-overlay.visible { + opacity: 1; + visibility: visible; +} + +/* Hide overlay on desktop */ +@media (min-width: 1024px) { + .drawer-overlay { + display: none; + } +} + +/* ============== TOUCH TARGETS ============== */ +@media (max-width: 1023px) { + /* Ensure minimum touch target size for interactive elements */ + button, + .btn, + .preset-btn, + .mode-nav-btn, + .control-btn, + .nav-action-btn, + .icon-btn { + min-height: var(--touch-min); + min-width: var(--touch-min); + } + + select, + input[type="text"], + input[type="number"], + input[type="search"] { + min-height: var(--touch-min); + padding: 10px 12px; + font-size: 16px; /* Prevents iOS zoom on focus */ + } + + .checkbox-group label, + .radio-group label { + min-height: var(--touch-min); + padding: 10px 14px; + display: flex; + align-items: center; + } +} + +/* ============== RESPONSIVE UTILITIES ============== */ +/* Hide on mobile */ +.hide-mobile { + display: none; +} + +@media (min-width: 768px) { + .hide-mobile { + display: initial; + } +} + +/* Hide on tablet and up */ +.show-mobile-only { + display: initial; +} + +@media (min-width: 768px) { + .show-mobile-only { + display: none; + } +} + +/* Hide on desktop */ +.hide-desktop { + display: initial; +} + +@media (min-width: 1024px) { + .hide-desktop { + display: none; + } +} + +/* Show only on desktop */ +.show-desktop-only { + display: none; +} + +@media (min-width: 1024px) { + .show-desktop-only { + display: initial; + } +} + +/* ============== SCROLLABLE AREAS ============== */ +.scroll-x { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + scrollbar-width: thin; +} + +.scroll-x::-webkit-scrollbar { + height: 4px; +} + +.scroll-x::-webkit-scrollbar-thumb { + background: var(--border-color, #1f2937); + border-radius: 2px; +} + +/* Hide scrollbar on mobile for cleaner look */ +@media (max-width: 767px) { + .scroll-x-mobile-hidden { + scrollbar-width: none; + } + + .scroll-x-mobile-hidden::-webkit-scrollbar { + display: none; + } +} + +/* ============== MOBILE NAVIGATION BAR ============== */ +.mobile-nav-bar { + display: flex; + align-items: center; + gap: 4px; + padding: 6px 10px; + background: var(--bg-tertiary, #151a23); + border-bottom: 1px solid var(--border-color, #1f2937); + overflow-x: auto; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; +} + +.mobile-nav-bar::-webkit-scrollbar { + display: none; +} + +.mobile-nav-btn { + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + padding: 8px 12px; + background: var(--bg-card, #121620); + border: 1px solid var(--border-color, #1f2937); + border-radius: 6px; + color: var(--text-secondary, #9ca3af); + font-size: var(--font-xs); + font-family: inherit; + white-space: nowrap; + cursor: pointer; + transition: all 0.2s ease; + min-height: 36px; +} + +.mobile-nav-btn:hover, +.mobile-nav-btn.active { + background: var(--bg-elevated, #1a202c); + border-color: var(--accent-cyan, #4a9eff); + color: var(--text-primary, #e8eaed); +} + +.mobile-nav-btn svg { + width: 14px; + height: 14px; + flex-shrink: 0; +} + +/* Hide mobile nav bar on desktop */ +@media (min-width: 1024px) { + .mobile-nav-bar { + display: none; + } +} + +/* ============== RESPONSIVE GRID UTILITIES ============== */ +.grid-responsive { + display: grid; + gap: var(--spacing-sm); +} + +/* 1 column base */ +.grid-1-2 { + grid-template-columns: 1fr; +} + +@media (min-width: 480px) { + .grid-1-2 { + grid-template-columns: repeat(2, 1fr); + } +} + +.grid-2-3 { + grid-template-columns: repeat(2, 1fr); +} + +@media (min-width: 768px) { + .grid-2-3 { + grid-template-columns: repeat(3, 1fr); + } +} + +/* ============== TYPOGRAPHY RESPONSIVE ============== */ +.text-responsive-xs { font-size: var(--font-xs); } +.text-responsive-sm { font-size: var(--font-sm); } +.text-responsive-base { font-size: var(--font-base); } +.text-responsive-md { font-size: var(--font-md); } +.text-responsive-lg { font-size: var(--font-lg); } +.text-responsive-xl { font-size: var(--font-xl); } +.text-responsive-2xl { font-size: var(--font-2xl); } + +/* Ensure minimum readable sizes for tiny text */ +.text-min-readable { + font-size: max(10px, var(--font-xs)); +} + +/* ============== MOBILE LAYOUT FIXES ============== */ +@media (max-width: 1023px) { + /* Fix main content to allow scrolling on mobile */ + .main-content { + height: auto !important; + min-height: calc(100dvh - var(--header-height) - var(--nav-height)); + overflow-y: auto !important; + overflow-x: hidden; + -webkit-overflow-scrolling: touch; + } + + /* Container should not clip content */ + .container { + overflow: visible; + height: auto; + min-height: 100dvh; + } + + /* Layout containers need to stack vertically on mobile */ + .wifi-layout-container, + .bt-layout-container { + flex-direction: column !important; + height: auto !important; + max-height: none !important; + min-height: auto !important; + overflow: visible !important; + padding: 10px !important; + } + + /* Visual panels should be scrollable, not clipped */ + .wifi-visuals, + .bt-visuals { + max-height: none !important; + overflow: visible !important; + margin-bottom: 15px; + } + + /* Device lists should have reasonable height on mobile */ + .wifi-device-list, + .bt-device-list { + max-height: 400px; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + } + + /* Visual panels should stack in single column on mobile when visible */ + .wifi-visuals, + .bt-visuals { + display: flex; + flex-direction: column; + gap: 10px; + } + + /* Only apply flex when aircraft visuals are shown (via JS setting display: grid) */ + #aircraftVisuals[style*="grid"] { + display: flex !important; + flex-direction: column !important; + gap: 10px; + } + + /* APRS visuals - only when visible */ + #aprsVisuals[style*="flex"] { + flex-direction: column !important; + } + + .wifi-visual-panel { + grid-column: auto !important; + } +} + +/* ============== MOBILE MAP FIXES ============== */ +@media (max-width: 1023px) { + /* Aircraft map container needs explicit height on mobile */ + .aircraft-map-container { + height: 300px !important; + min-height: 300px !important; + width: 100% !important; + } + + #aircraftMap { + height: 100% !important; + width: 100% !important; + min-height: 250px; + } + + /* APRS map container */ + #aprsMap { + min-height: 300px !important; + height: 300px !important; + width: 100% !important; + } + + /* Satellite embed */ + .satellite-dashboard-embed { + height: 400px !important; + min-height: 400px !important; + } + + /* Map panels should be full width */ + .wifi-visual-panel[style*="grid-column: span 2"] { + grid-column: auto !important; + } + + /* Make map container full width when it has ACARS sidebar */ + .wifi-visual-panel[style*="display: flex"][style*="gap: 0"] { + flex-direction: column !important; + } + + /* ACARS sidebar should be below map on mobile */ + .main-acars-sidebar { + width: 100% !important; + max-width: none !important; + border-left: none !important; + border-top: 1px solid var(--border-color, #1f2937) !important; + } + + .main-acars-sidebar.collapsed { + width: 100% !important; + } + + .main-acars-content { + max-height: 200px !important; + } +} + +/* ============== LEAFLET MOBILE TOUCH FIXES ============== */ +.leaflet-container { + touch-action: pan-x pan-y; + -webkit-tap-highlight-color: transparent; +} + +.leaflet-control-zoom { + touch-action: manipulation; +} + +.leaflet-control-zoom a { + min-width: var(--touch-min, 44px) !important; + min-height: var(--touch-min, 44px) !important; + line-height: var(--touch-min, 44px) !important; + font-size: 18px !important; +} + +/* ============== MOBILE HEADER STATS ============== */ +@media (max-width: 1023px) { + .header-stats { + display: none !important; + } + + /* Simplify header on mobile */ + header h1 { + font-size: 16px !important; + } + + header h1 .tagline, + header h1 .version-badge { + display: none; + } + + header .subtitle { + font-size: 10px !important; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + header .logo svg { + width: 30px !important; + height: 30px !important; + } +} + +/* ============== MOBILE MODE PANELS ============== */ +@media (max-width: 1023px) { + /* Mode panel grids should be single column */ + .data-grid, + .stats-grid, + .sensor-grid { + grid-template-columns: 1fr !important; + } + + /* Section headers should be easier to tap */ + .section h3 { + min-height: var(--touch-min); + padding: 12px !important; + } + + /* Tables need horizontal scroll */ + .message-table, + .sensor-table { + display: block; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } + + /* Ensure messages list is scrollable */ + #messageList, + #sensorGrid, + .aprs-list { + max-height: 60vh; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + } +} + +/* ============== WELCOME PAGE MOBILE ============== */ +@media (max-width: 767px) { + .welcome-container { + padding: 15px !important; + max-width: 100% !important; + } + + .welcome-header { + flex-direction: column; + text-align: center; + gap: 10px; + } + + .welcome-logo svg { + width: 50px; + height: 50px; + } + + .welcome-title { + font-size: 24px !important; + } + + .welcome-content { + grid-template-columns: 1fr !important; + } + + .mode-grid { + grid-template-columns: repeat(2, 1fr) !important; + gap: 8px !important; + } + + .mode-card { + padding: 12px 8px !important; + } + + .mode-icon { + font-size: 20px !important; + } + + .mode-name { + font-size: 11px !important; + } + + .mode-desc { + font-size: 9px !important; + } + + .changelog-release { + padding: 10px !important; + } +} + +/* ============== TSCM MODE MOBILE ============== */ +@media (max-width: 1023px) { + .tscm-layout { + flex-direction: column !important; + height: auto !important; + } + + .tscm-spectrum-panel, + .tscm-detection-panel { + width: 100% !important; + max-width: none !important; + height: auto !important; + min-height: 300px; + } +} + +/* ============== LISTENING POST MOBILE ============== */ +@media (max-width: 1023px) { + .radio-controls-section { + flex-direction: column !important; + gap: 15px; + } + + .knobs-row { + flex-wrap: wrap; + justify-content: center; + } + + .radio-module-box { + width: 100% !important; + } } - -/* ============== CSS VARIABLES ============== */ -:root { - /* Touch targets */ - --touch-min: 44px; - --touch-comfortable: 48px; - - /* Responsive spacing */ - --spacing-xs: clamp(4px, 1vw, 8px); - --spacing-sm: clamp(8px, 2vw, 12px); - --spacing-md: clamp(12px, 3vw, 20px); - --spacing-lg: clamp(16px, 4vw, 32px); - - /* Responsive typography */ - --font-xs: clamp(10px, 2.5vw, 11px); - --font-sm: clamp(11px, 2.8vw, 12px); - --font-base: clamp(13px, 3vw, 14px); - --font-md: clamp(14px, 3.5vw, 16px); - --font-lg: clamp(16px, 4vw, 20px); - --font-xl: clamp(20px, 5vw, 28px); - --font-2xl: clamp(24px, 6vw, 40px); - - /* Header height for calculations */ - --header-height: 52px; - --nav-height: 44px; -} - -@media (min-width: 768px) { - :root { - --header-height: 60px; - --nav-height: 48px; - } -} - -@media (min-width: 1024px) { - :root { - --header-height: 96px; - --nav-height: 0px; - } -} - -/* ============== VIEWPORT HEIGHT FIX ============== */ -/* Handles iOS Safari address bar and dynamic viewport */ -.full-height { - height: 100dvh; - height: 100vh; /* Fallback */ -} - -@supports (-webkit-touch-callout: none) { - .full-height { - height: -webkit-fill-available; - } -} - -/* ============== HAMBURGER BUTTON ============== */ -.hamburger-btn { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - width: var(--touch-min); - height: var(--touch-min); - padding: 10px; - background: transparent; - border: 1px solid var(--border-color, #1f2937); - border-radius: 6px; - cursor: pointer; - position: relative; - z-index: 1001; - flex-shrink: 0; - transition: background 0.2s ease, border-color 0.2s ease; -} - -.hamburger-btn:hover { - background: var(--bg-tertiary, #151a23); - border-color: var(--accent-cyan, #4a9eff); -} - -.hamburger-btn span { - display: block; - width: 18px; - height: 2px; - background: var(--accent-cyan, #4a9eff); - margin: 2px 0; - border-radius: 1px; - transition: transform 0.3s ease, opacity 0.3s ease; -} - -.hamburger-btn.active span:nth-child(1) { - transform: rotate(45deg) translate(4px, 4px); -} - -.hamburger-btn.active span:nth-child(2) { - opacity: 0; -} - -.hamburger-btn.active span:nth-child(3) { - transform: rotate(-45deg) translate(4px, -4px); -} - -/* Hide hamburger on desktop */ -@media (min-width: 1024px) { - .hamburger-btn { - display: none; - } -} - -/* ============== MOBILE DRAWER ============== */ -.mobile-drawer { - position: fixed; - top: 0; - left: 0; - width: min(320px, 85vw); - height: 100dvh; - height: 100vh; /* Fallback */ - background: var(--bg-secondary, #0f1218); - border-right: 1px solid var(--border-color, #1f2937); - transform: translateX(-100%); - transition: transform 0.3s ease; - z-index: 1000; - overflow-y: auto; - -webkit-overflow-scrolling: touch; - padding-top: 60px; -} - -.mobile-drawer.open { - transform: translateX(0); -} - -/* Show sidebar normally on desktop */ -@media (min-width: 1024px) { - .mobile-drawer { - position: static; - transform: none; - width: auto; - height: auto; - padding-top: 0; - z-index: auto; - } -} - -/* ============== DRAWER OVERLAY ============== */ -.drawer-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.6); - backdrop-filter: blur(2px); - opacity: 0; - visibility: hidden; - transition: opacity 0.3s ease, visibility 0.3s ease; - z-index: 999; -} - -.drawer-overlay.visible { - opacity: 1; - visibility: visible; -} - -/* Hide overlay on desktop */ -@media (min-width: 1024px) { - .drawer-overlay { - display: none; - } -} - -/* ============== TOUCH TARGETS ============== */ -@media (max-width: 1023px) { - /* Ensure minimum touch target size for interactive elements */ - button, - .btn, - .preset-btn, - .mode-nav-btn, - .control-btn, - .nav-action-btn, - .icon-btn { - min-height: var(--touch-min); - min-width: var(--touch-min); - } - - select, - input[type="text"], - input[type="number"], - input[type="search"] { - min-height: var(--touch-min); - padding: 10px 12px; - font-size: 16px; /* Prevents iOS zoom on focus */ - } - - .checkbox-group label, - .radio-group label { - min-height: var(--touch-min); - padding: 10px 14px; - display: flex; - align-items: center; - } -} - -/* ============== RESPONSIVE UTILITIES ============== */ -/* Hide on mobile */ -.hide-mobile { - display: none; -} - -@media (min-width: 768px) { - .hide-mobile { - display: initial; - } -} - -/* Hide on tablet and up */ -.show-mobile-only { - display: initial; -} - -@media (min-width: 768px) { - .show-mobile-only { - display: none; - } -} - -/* Hide on desktop */ -.hide-desktop { - display: initial; -} - -@media (min-width: 1024px) { - .hide-desktop { - display: none; - } -} - -/* Show only on desktop */ -.show-desktop-only { - display: none; -} - -@media (min-width: 1024px) { - .show-desktop-only { - display: initial; - } -} - -/* ============== SCROLLABLE AREAS ============== */ -.scroll-x { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - scrollbar-width: thin; -} - -.scroll-x::-webkit-scrollbar { - height: 4px; -} - -.scroll-x::-webkit-scrollbar-thumb { - background: var(--border-color, #1f2937); - border-radius: 2px; -} - -/* Hide scrollbar on mobile for cleaner look */ -@media (max-width: 767px) { - .scroll-x-mobile-hidden { - scrollbar-width: none; - } - - .scroll-x-mobile-hidden::-webkit-scrollbar { - display: none; - } -} - -/* ============== MOBILE NAVIGATION BAR ============== */ -.mobile-nav-bar { - display: flex; - align-items: center; - gap: 4px; - padding: 6px 10px; - background: var(--bg-tertiary, #151a23); - border-bottom: 1px solid var(--border-color, #1f2937); - overflow-x: auto; - -webkit-overflow-scrolling: touch; - scrollbar-width: none; -} - -.mobile-nav-bar::-webkit-scrollbar { - display: none; -} - -.mobile-nav-btn { - display: flex; - align-items: center; - justify-content: center; - gap: 6px; - padding: 8px 12px; - background: var(--bg-card, #121620); - border: 1px solid var(--border-color, #1f2937); - border-radius: 6px; - color: var(--text-secondary, #9ca3af); - font-size: var(--font-xs); - font-family: inherit; - white-space: nowrap; - cursor: pointer; - transition: all 0.2s ease; - min-height: 36px; -} - -.mobile-nav-btn:hover, -.mobile-nav-btn.active { - background: var(--bg-elevated, #1a202c); - border-color: var(--accent-cyan, #4a9eff); - color: var(--text-primary, #e8eaed); -} - -.mobile-nav-btn svg { - width: 14px; - height: 14px; - flex-shrink: 0; -} - -/* Hide mobile nav bar on desktop */ -@media (min-width: 1024px) { - .mobile-nav-bar { - display: none; - } -} - -/* ============== RESPONSIVE GRID UTILITIES ============== */ -.grid-responsive { - display: grid; - gap: var(--spacing-sm); -} - -/* 1 column base */ -.grid-1-2 { - grid-template-columns: 1fr; -} - -@media (min-width: 480px) { - .grid-1-2 { - grid-template-columns: repeat(2, 1fr); - } -} - -.grid-2-3 { - grid-template-columns: repeat(2, 1fr); -} - -@media (min-width: 768px) { - .grid-2-3 { - grid-template-columns: repeat(3, 1fr); - } -} - -/* ============== TYPOGRAPHY RESPONSIVE ============== */ -.text-responsive-xs { font-size: var(--font-xs); } -.text-responsive-sm { font-size: var(--font-sm); } -.text-responsive-base { font-size: var(--font-base); } -.text-responsive-md { font-size: var(--font-md); } -.text-responsive-lg { font-size: var(--font-lg); } -.text-responsive-xl { font-size: var(--font-xl); } -.text-responsive-2xl { font-size: var(--font-2xl); } - -/* Ensure minimum readable sizes for tiny text */ -.text-min-readable { - font-size: max(10px, var(--font-xs)); -} - -/* ============== MOBILE LAYOUT FIXES ============== */ -@media (max-width: 1023px) { - /* Fix main content to allow scrolling on mobile */ - .main-content { - height: auto !important; - min-height: calc(100dvh - var(--header-height) - var(--nav-height)); - overflow-y: auto !important; - overflow-x: hidden; - -webkit-overflow-scrolling: touch; - } - - /* Container should not clip content */ - .container { - overflow: visible; - height: auto; - min-height: 100dvh; - } - - /* Layout containers need to stack vertically on mobile */ - .wifi-layout-container, - .bt-layout-container { - flex-direction: column !important; - height: auto !important; - max-height: none !important; - min-height: auto !important; - overflow: visible !important; - padding: 10px !important; - } - - /* Visual panels should be scrollable, not clipped */ - .wifi-visuals, - .bt-visuals { - max-height: none !important; - overflow: visible !important; - margin-bottom: 15px; - } - - /* Device lists should have reasonable height on mobile */ - .wifi-device-list, - .bt-device-list { - max-height: 400px; - overflow-y: auto; - -webkit-overflow-scrolling: touch; - } - - /* Visual panels should stack in single column on mobile when visible */ - .wifi-visuals, - .bt-visuals { - display: flex; - flex-direction: column; - gap: 10px; - } - - /* Only apply flex when aircraft visuals are shown (via JS setting display: grid) */ - #aircraftVisuals[style*="grid"] { - display: flex !important; - flex-direction: column !important; - gap: 10px; - } - - /* APRS visuals - only when visible */ - #aprsVisuals[style*="flex"] { - flex-direction: column !important; - } - - .wifi-visual-panel { - grid-column: auto !important; - } -} - -/* ============== MOBILE MAP FIXES ============== */ -@media (max-width: 1023px) { - /* Aircraft map container needs explicit height on mobile */ - .aircraft-map-container { - height: 300px !important; - min-height: 300px !important; - width: 100% !important; - } - - #aircraftMap { - height: 100% !important; - width: 100% !important; - min-height: 250px; - } - - /* APRS map container */ - #aprsMap { - min-height: 300px !important; - height: 300px !important; - width: 100% !important; - } - - /* Satellite embed */ - .satellite-dashboard-embed { - height: 400px !important; - min-height: 400px !important; - } - - /* Map panels should be full width */ - .wifi-visual-panel[style*="grid-column: span 2"] { - grid-column: auto !important; - } - - /* Make map container full width when it has ACARS sidebar */ - .wifi-visual-panel[style*="display: flex"][style*="gap: 0"] { - flex-direction: column !important; - } - - /* ACARS sidebar should be below map on mobile */ - .main-acars-sidebar { - width: 100% !important; - max-width: none !important; - border-left: none !important; - border-top: 1px solid var(--border-color, #1f2937) !important; - } - - .main-acars-sidebar.collapsed { - width: 100% !important; - } - - .main-acars-content { - max-height: 200px !important; - } -} - -/* ============== LEAFLET MOBILE TOUCH FIXES ============== */ -.leaflet-container { - touch-action: pan-x pan-y; - -webkit-tap-highlight-color: transparent; -} - -.leaflet-control-zoom { - touch-action: manipulation; -} - -.leaflet-control-zoom a { - min-width: var(--touch-min, 44px) !important; - min-height: var(--touch-min, 44px) !important; - line-height: var(--touch-min, 44px) !important; - font-size: 18px !important; -} - -/* ============== MOBILE HEADER STATS ============== */ -@media (max-width: 1023px) { - .header-stats { - display: none !important; - } - - /* Simplify header on mobile */ - header h1 { - font-size: 16px !important; - } - - header h1 .tagline, - header h1 .version-badge { - display: none; - } - - header .subtitle { - font-size: 10px !important; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - header .logo svg { - width: 30px !important; - height: 30px !important; - } -} - -/* ============== MOBILE MODE PANELS ============== */ -@media (max-width: 1023px) { - /* Mode panel grids should be single column */ - .data-grid, - .stats-grid, - .sensor-grid { - grid-template-columns: 1fr !important; - } - - /* Section headers should be easier to tap */ - .section h3 { - min-height: var(--touch-min); - padding: 12px !important; - } - - /* Tables need horizontal scroll */ - .message-table, - .sensor-table { - display: block; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - - /* Ensure messages list is scrollable */ - #messageList, - #sensorGrid, - .aprs-list { - max-height: 60vh; - overflow-y: auto; - -webkit-overflow-scrolling: touch; - } -} - -/* ============== WELCOME PAGE MOBILE ============== */ -@media (max-width: 767px) { - .welcome-container { - padding: 15px !important; - max-width: 100% !important; - } - - .welcome-header { - flex-direction: column; - text-align: center; - gap: 10px; - } - - .welcome-logo svg { - width: 50px; - height: 50px; - } - - .welcome-title { - font-size: 24px !important; - } - - .welcome-content { - grid-template-columns: 1fr !important; - } - - .mode-grid { - grid-template-columns: repeat(2, 1fr) !important; - gap: 8px !important; - } - - .mode-card { - padding: 12px 8px !important; - } - - .mode-icon { - font-size: 20px !important; - } - - .mode-name { - font-size: 11px !important; - } - - .mode-desc { - font-size: 9px !important; - } - - .changelog-release { - padding: 10px !important; - } -} - -/* ============== TSCM MODE MOBILE ============== */ -@media (max-width: 1023px) { - .tscm-layout { - flex-direction: column !important; - height: auto !important; - } - - .tscm-spectrum-panel, - .tscm-detection-panel { - width: 100% !important; - max-width: none !important; - height: auto !important; - min-height: 300px; - } -} - -/* ============== LISTENING POST MOBILE ============== */ -@media (max-width: 1023px) { - .radio-controls-section { - flex-direction: column !important; - gap: 15px; - } - - .knobs-row { - flex-wrap: wrap; - justify-content: center; - } - - .radio-module-box { - width: 100% !important; - } -} diff --git a/static/css/satellite_dashboard.css b/static/css/satellite_dashboard.css index 72b110c..9d64ecd 100644 --- a/static/css/satellite_dashboard.css +++ b/static/css/satellite_dashboard.css @@ -23,7 +23,7 @@ } body { - font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + font-family: var(--font-sans); background: var(--bg-dark); color: var(--text-primary); min-height: 100vh; @@ -93,7 +93,7 @@ body { } .logo { - font-family: 'Inter', sans-serif; + font-family: var(--font-sans); font-size: 20px; font-weight: 700; letter-spacing: 3px; diff --git a/static/js/core/app.js b/static/js/core/app.js index bfab560..9728497 100644 --- a/static/js/core/app.js +++ b/static/js/core/app.js @@ -373,7 +373,7 @@ function showInfo(text) { const infoEl = document.createElement('div'); infoEl.className = 'info-msg'; - infoEl.style.cssText = 'padding: 12px 15px; margin-bottom: 8px; background: #0a0a0a; border: 1px solid #1a1a1a; border-left: 2px solid #00d4ff; font-family: "Terminus", monospace; font-size: 11px; color: #888; word-break: break-all;'; + infoEl.style.cssText = 'padding: 12px 15px; margin-bottom: 8px; background: #0a0a0a; border: 1px solid #1a1a1a; border-left: 2px solid #00d4ff; font-family: "JetBrains Mono", monospace; font-size: 11px; color: #888; word-break: break-all;'; infoEl.textContent = text; output.insertBefore(infoEl, output.firstChild); } @@ -387,7 +387,7 @@ function showError(text) { const errorEl = document.createElement('div'); errorEl.className = 'error-msg'; - errorEl.style.cssText = 'padding: 12px 15px; margin-bottom: 8px; background: #1a0a0a; border: 1px solid #2a1a1a; border-left: 2px solid #ff3366; font-family: "Terminus", monospace; font-size: 11px; color: #ff6688; word-break: break-all;'; + errorEl.style.cssText = 'padding: 12px 15px; margin-bottom: 8px; background: #1a0a0a; border: 1px solid #2a1a1a; border-left: 2px solid #ff3366; font-family: "JetBrains Mono", monospace; font-size: 11px; color: #ff6688; word-break: break-all;'; errorEl.textContent = '⚠ ' + text; output.insertBefore(errorEl, output.firstChild); } diff --git a/static/js/core/settings-manager.js b/static/js/core/settings-manager.js index 8282e1b..3941c2d 100644 --- a/static/js/core/settings-manager.js +++ b/static/js/core/settings-manager.js @@ -813,11 +813,11 @@ function renderUpdateStatus(data) {
Current Version - v${data.current_version} + v${data.current_version}
Latest Version - v${data.latest_version} + v${data.latest_version}
${data.last_check ? `
diff --git a/static/js/modes/listening-post.js b/static/js/modes/listening-post.js index c3dc4c2..af8353c 100644 --- a/static/js/modes/listening-post.js +++ b/static/js/modes/listening-post.js @@ -1293,7 +1293,7 @@ function drawAudioVisualizer() { } ctx.fillStyle = 'rgba(255, 255, 255, 0.3)'; - ctx.font = '8px Terminus'; + ctx.font = '8px JetBrains Mono'; ctx.fillText('0', 2, canvas.height - 2); ctx.fillText('4kHz', canvas.width / 4, canvas.height - 2); ctx.fillText('8kHz', canvas.width / 2, canvas.height - 2); diff --git a/static/js/modes/spy-stations.js b/static/js/modes/spy-stations.js index 945e9f8..df598c5 100644 --- a/static/js/modes/spy-stations.js +++ b/static/js/modes/spy-stations.js @@ -84,7 +84,7 @@ const SpyStations = (function() { modeContainer.innerHTML = modes.map(m => ` `).join(''); } diff --git a/static/vendor/fonts/Terminus.ttf b/static/vendor/fonts/Terminus.ttf deleted file mode 100644 index 7a82c196abe39d6a9f7ceb87234c191aa83e073b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 118548 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

F_L_y0QPDc=7}IrH=jhdjhx+ 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 RNOEclFzgaJN}vn|1^^`{&DsC} diff --git a/templates/adsb_dashboard.html b/templates/adsb_dashboard.html index be182f8..dc7c481 100644 --- a/templates/adsb_dashboard.html +++ b/templates/adsb_dashboard.html @@ -8,7 +8,7 @@ {% if offline_settings.fonts_source == 'local' %} {% else %} - + {% endif %} {% if offline_settings.assets_source == 'local' %} @@ -1387,7 +1387,7 @@ ACARS: ${r.statistics.acarsMessages} messages`; // Range label const rangeNm = Math.round(maxRange * ratio); ctx.fillStyle = 'rgba(0, 255, 255, 0.5)'; - ctx.font = '10px Terminus'; + ctx.font = '10px JetBrains Mono'; ctx.fillText(`${rangeNm}`, this.centerX + r + 5, this.centerY + 4); }); } @@ -1528,7 +1528,7 @@ ACARS: ${r.statistics.acarsMessages} messages`; // Label if (blip.callsign || blip.selected) { ctx.fillStyle = '#00ffff'; - ctx.font = '9px Terminus'; + ctx.font = '9px JetBrains Mono'; ctx.textAlign = 'left'; ctx.fillText(blip.callsign || blip.icao, blip.x + 8, blip.y - 5); if (blip.altitude) { @@ -1646,7 +1646,7 @@ ACARS: ${r.statistics.acarsMessages} messages`; // Range label const rangeNm = Math.round(maxRange * ratio); ctx.fillStyle = 'rgba(0, 255, 255, 0.4)'; - ctx.font = '10px Terminus'; + ctx.font = '10px JetBrains Mono'; ctx.fillText(`${rangeNm}nm`, centerX + r + 5, centerY); }); diff --git a/templates/adsb_history.html b/templates/adsb_history.html index 73f850e..8b9ea22 100644 --- a/templates/adsb_history.html +++ b/templates/adsb_history.html @@ -4,7 +4,11 @@ ADS-B History // INTERCEPT - + {% if offline_settings.fonts_source == 'local' %} + + {% else %} + + {% endif %} @@ -466,7 +470,7 @@ if (!points.length) { ctx.fillStyle = 'rgba(156, 163, 175, 0.6)'; - ctx.font = '12px "Terminus", monospace'; + ctx.font = '12px "JetBrains Mono", monospace'; ctx.fillText(`No ${label.toLowerCase()} data`, 12, height / 2); return; } @@ -474,7 +478,7 @@ const series = points.map(p => p[field]).filter(v => v !== null && v !== undefined); if (!series.length) { ctx.fillStyle = 'rgba(156, 163, 175, 0.6)'; - ctx.font = '12px "Terminus", monospace'; + ctx.font = '12px "JetBrains Mono", monospace'; ctx.fillText(`No ${label.toLowerCase()} data`, 12, height / 2); return; } @@ -515,7 +519,7 @@ } ctx.fillStyle = 'rgba(226, 232, 240, 0.8)'; - ctx.font = '11px "Terminus", monospace'; + ctx.font = '11px "JetBrains Mono", monospace'; ctx.fillText(`${maxVal} ${unit}`, 12, padding); ctx.fillText(`${minVal} ${unit}`, 12, height - padding); } diff --git a/templates/ais_dashboard.html b/templates/ais_dashboard.html index b481d4d..20e652b 100644 --- a/templates/ais_dashboard.html +++ b/templates/ais_dashboard.html @@ -8,7 +8,7 @@ {% if offline_settings.fonts_source == 'local' %} {% else %} - + {% endif %} {% if offline_settings.assets_source == 'local' %} diff --git a/templates/index.html b/templates/index.html index 87f77e7..69cea1c 100644 --- a/templates/index.html +++ b/templates/index.html @@ -22,7 +22,7 @@ {% if offline_settings.fonts_source == 'local' %} {% else %} - + {% endif %} {% if offline_settings.assets_source == 'local' %} @@ -4223,7 +4223,7 @@ const infoEl = document.createElement('div'); infoEl.className = 'info-msg'; - infoEl.style.cssText = 'padding: 12px 15px; margin-bottom: 8px; background: #0a0a0a; border: 1px solid #1a1a1a; border-left: 2px solid #00d4ff; font-family: "Terminus", monospace; font-size: 11px; color: #888; word-break: break-all;'; + infoEl.style.cssText = 'padding: 12px 15px; margin-bottom: 8px; background: #0a0a0a; border: 1px solid #1a1a1a; border-left: 2px solid #00d4ff; font-family: "JetBrains Mono", monospace; font-size: 11px; color: #888; word-break: break-all;'; infoEl.textContent = text; output.insertBefore(infoEl, output.firstChild); } @@ -4239,7 +4239,7 @@ const errorEl = document.createElement('div'); errorEl.className = 'error-msg'; - errorEl.style.cssText = 'padding: 12px 15px; margin-bottom: 8px; background: #1a0a0a; border: 1px solid #2a1a1a; border-left: 2px solid #ff3366; font-family: "Terminus", monospace; font-size: 11px; color: #ff6688; word-break: break-all;'; + errorEl.style.cssText = 'padding: 12px 15px; margin-bottom: 8px; background: #1a0a0a; border: 1px solid #2a1a1a; border-left: 2px solid #ff3366; font-family: "JetBrains Mono", monospace; font-size: 11px; color: #ff6688; word-break: break-all;'; errorEl.textContent = '⚠ ' + text; output.insertBefore(errorEl, output.firstChild); } @@ -6855,7 +6855,7 @@ // Draw total in center ctx.fillStyle = '#fff'; - ctx.font = 'bold 16px Terminus'; + ctx.font = 'bold 16px JetBrains Mono'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(total, cx, cy); @@ -8625,7 +8625,7 @@ // Label if (el > 0) { ctx.fillStyle = '#444'; - ctx.font = '10px Terminus'; + ctx.font = '10px JetBrains Mono'; ctx.textAlign = 'center'; ctx.fillText(el + '°', cx, cy - r + 12); } @@ -8698,7 +8698,7 @@ // Label ctx.fillStyle = '#fff'; - ctx.font = '11px Terminus'; + ctx.font = '11px JetBrains Mono'; ctx.fillText(pass.satellite, maxX + 10, maxY - 5); } } diff --git a/templates/layout/base.html b/templates/layout/base.html index 09e97ee..be44ccb 100644 --- a/templates/layout/base.html +++ b/templates/layout/base.html @@ -6,12 +6,12 @@ {% block title %}iNTERCEPT{% endblock %} // iNTERCEPT - {# Fonts - Conditional CDN/Local loading #} - {% if offline_settings and offline_settings.fonts_source == 'local' %} - - {% else %} - - {% endif %} + {# Fonts - Conditional CDN/Local loading #} + {% if offline_settings and offline_settings.fonts_source == 'local' %} + + {% else %} + + {% endif %} {# Core CSS (Design System) #} diff --git a/templates/network_monitor.html b/templates/network_monitor.html index 041561a..ebb544f 100644 --- a/templates/network_monitor.html +++ b/templates/network_monitor.html @@ -4,7 +4,11 @@ Network Monitor // INTERCEPT - + {% if offline_settings.fonts_source == 'local' %} + + {% else %} + + {% endif %} diff --git a/templates/partials/settings-modal.html b/templates/partials/settings-modal.html index e4a6385..042d26a 100644 --- a/templates/partials/settings-modal.html +++ b/templates/partials/settings-modal.html @@ -52,7 +52,7 @@

Web Fonts - Inter, Terminus + JetBrains Mono