feat(mobile): mobile-first driving layout, bottom-sheet dialogs, safe-area insets, and drawer controls

This commit is contained in:
2026-09-12 09:28:04 -07:00
parent f2a8660f10
commit fef8ab6417
3 changed files with 95 additions and 41 deletions
+79 -27
View File
@@ -203,6 +203,14 @@ body {
box-shadow: 0 0 12px rgba(6, 182, 212, 0.4); box-shadow: 0 0 12px rgba(6, 182, 212, 0.4);
} }
.nav-text-mobile {
display: none;
}
.nav-text-desktop {
display: inline;
}
.nav-right { .nav-right {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -1865,9 +1873,22 @@ body {
/* Responsive Mobile Driving Mode (Calm, Uncluttered, Immersive Map) */ /* Responsive Mobile Driving Mode (Calm, Uncluttered, Immersive Map) */
@media (max-width: 768px) { @media (max-width: 768px) {
html, body {
overscroll-behavior: none;
-webkit-overflow-scrolling: touch;
}
/* Full Bleed Radar Canvas on Mobile */
.radar-view-container {
height: calc(100dvh - 48px);
height: calc(100svh - 48px);
width: 100%;
}
/* Ultra-Compact Site Nav */ /* Ultra-Compact Site Nav */
.site-nav { .site-nav {
padding: 6px 12px; padding: max(6px, env(safe-area-inset-top)) max(10px, env(safe-area-inset-right)) 6px max(10px, env(safe-area-inset-left));
min-height: 48px;
} }
.brand-text .brand-tagline, .brand-text .brand-tagline,
.badge-live-pulse { .badge-live-pulse {
@@ -1883,6 +1904,12 @@ body {
padding: 5px 8px; padding: 5px 8px;
font-size: 0.72rem; font-size: 0.72rem;
} }
.nav-text-desktop {
display: none;
}
.nav-text-mobile {
display: inline;
}
.user-profile-pill { .user-profile-pill {
padding: 4px 8px; padding: 4px 8px;
font-size: 0.72rem; font-size: 0.72rem;
@@ -1891,13 +1918,13 @@ body {
/* Sleek Floating Top Driving Capsule */ /* Sleek Floating Top Driving Capsule */
.driver-hud { .driver-hud {
position: absolute; position: absolute;
top: 10px; top: max(8px, env(safe-area-inset-top));
left: 10px; left: max(10px, env(safe-area-inset-left));
right: 10px; right: max(10px, env(safe-area-inset-right));
width: auto; width: auto;
padding: 8px 12px; padding: 8px 12px;
border-radius: 16px; border-radius: 16px;
background: rgba(10, 14, 23, 0.88); background: rgba(10, 14, 23, 0.92);
backdrop-filter: blur(20px); backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.12); border: 1px solid rgba(255, 255, 255, 0.12);
@@ -2020,7 +2047,7 @@ body {
-webkit-backdrop-filter: blur(25px); -webkit-backdrop-filter: blur(25px);
border-top: 1px solid rgba(255, 255, 255, 0.18); border-top: 1px solid rgba(255, 255, 255, 0.18);
border-radius: 22px 22px 0 0; border-radius: 22px 22px 0 0;
padding: 16px 16px 36px; padding: 16px 16px max(28px, env(safe-area-inset-bottom));
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
@@ -2030,6 +2057,7 @@ body {
box-shadow: 0 -12px 35px rgba(0, 0, 0, 0.75); box-shadow: 0 -12px 35px rgba(0, 0, 0, 0.75);
max-height: 75vh; max-height: 75vh;
overflow-y: auto; overflow-y: auto;
-webkit-overflow-scrolling: touch;
} }
.map-top-controls.open-drawer { .map-top-controls.open-drawer {
@@ -2094,8 +2122,8 @@ body {
/* Compact Floating Action Button on Mobile */ /* Compact Floating Action Button on Mobile */
.driver-fab-container { .driver-fab-container {
position: absolute; position: absolute;
bottom: 22px; bottom: max(18px, env(safe-area-inset-bottom));
right: 14px; right: max(14px, env(safe-area-inset-right));
z-index: 500; z-index: 500;
margin: 0; margin: 0;
pointer-events: auto; pointer-events: auto;
@@ -2124,12 +2152,16 @@ body {
/* Minimal Recenter Button on Mobile */ /* Minimal Recenter Button on Mobile */
.btn-recenter-pill { .btn-recenter-pill {
bottom: 22px; position: absolute;
left: 14px; bottom: max(18px, env(safe-area-inset-bottom));
left: max(14px, env(safe-area-inset-left));
right: auto;
transform: none;
padding: 10px 16px; padding: 10px 16px;
border-radius: 9999px; border-radius: 9999px;
font-size: 0.82rem; font-size: 0.82rem;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5); box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
z-index: 500;
} }
.map-legend { .map-legend {
@@ -2140,6 +2172,42 @@ body {
.leaflet-control-zoom { .leaflet-control-zoom {
display: none !important; /* Touch pinch-to-zoom is native */ display: none !important; /* Touch pinch-to-zoom is native */
} }
/* Native Mobile Bottom Sheet for all Modals */
.agency-dialog,
.hardware-proof-dialog {
position: fixed !important;
bottom: 0 !important;
top: auto !important;
left: 0 !important;
right: 0 !important;
transform: none !important;
width: 100% !important;
max-width: 100% !important;
border-radius: 22px 22px 0 0 !important;
border-left: none !important;
border-right: none !important;
border-bottom: none !important;
max-height: 88dvh !important;
margin: 0 !important;
padding-bottom: max(20px, env(safe-area-inset-bottom)) !important;
box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.85) !important;
animation: sheet-slide-up 0.28s cubic-bezier(0.16, 1, 0.3, 1) !important;
}
@keyframes sheet-slide-up {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
.modal-header {
padding: 18px 20px 14px;
border-radius: 22px 22px 0 0;
}
.modal-agency-name {
font-size: 1.25rem;
}
} }
/* --- SLEEK RADAR DARK MODE FOR MAP TILES (Zero API Key, Zero Watermarks, 100% Free) --- */ /* --- SLEEK RADAR DARK MODE FOR MAP TILES (Zero API Key, Zero Watermarks, 100% Free) --- */
@@ -2405,23 +2473,7 @@ body {
to { opacity: 1; transform: translateX(-50%) translateY(0); } to { opacity: 1; transform: translateX(-50%) translateY(0); }
} }
/* 5. Mobile Adjustments for Driver FAB */
@media (max-width: 768px) {
.driver-fab-container {
bottom: 20px;
right: 15px;
}
.btn-driver-fab {
padding: 12px 18px;
font-size: 0.85rem;
}
.fab-icon-glow {
font-size: 1.3rem;
}
.btn-recenter-pill {
bottom: 85px;
}
}
/* ========================================================================== /* ==========================================================================
PHOTO EVIDENCE & DEMOCRATIC VERIFICATION ENGINE PHOTO EVIDENCE & DEMOCRATIC VERIFICATION ENGINE
+10 -8
View File
@@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>FlockRadar | Live ALPR Camera Awareness & National Transparency Directory</title> <title>FlockRadar | Live ALPR Camera Awareness & National Transparency Directory</title>
<meta name="description" content="A Waze-style real-time ALPR radar navigation app alerting drivers to active Flock camera recorded zones with live proximity alerts, audio warnings, and a 1,439-portal public directory."> <meta name="description" content="A Waze-style real-time ALPR radar navigation app alerting drivers to active Flock camera recorded zones with live proximity alerts, audio warnings, and a 1,439-portal public directory.">
<meta name="keywords" content="Flock Safety, ALPR, camera radar, waze for flock, live radar, license plate readers, police surveillance, camera alerts"> <meta name="keywords" content="Flock Safety, ALPR, camera radar, waze for flock, live radar, license plate readers, police surveillance, camera alerts">
@@ -28,7 +28,7 @@
<meta name="apple-mobile-web-app-title" content="FlockRadar"> <meta name="apple-mobile-web-app-title" content="FlockRadar">
<!-- Main Stylesheet --> <!-- Main Stylesheet -->
<link rel="stylesheet" href="css/styles.css?v=8"> <link rel="stylesheet" href="css/styles.css?v=9">
</head> </head>
<body> <body>
@@ -56,11 +56,13 @@
<div class="nav-mode-switcher" role="tablist"> <div class="nav-mode-switcher" role="tablist">
<button id="nav-map-btn" class="nav-mode-btn active" role="tab" aria-selected="true"> <button id="nav-map-btn" class="nav-mode-btn active" role="tab" aria-selected="true">
<span class="mode-icon">🗺️</span> <span class="mode-icon">🗺️</span>
<span>Live Radar Map</span> <span class="nav-text-desktop">Live Radar Map</span>
<span class="nav-text-mobile">Radar</span>
</button> </button>
<button id="nav-dir-btn" class="nav-mode-btn" role="tab" aria-selected="false"> <button id="nav-dir-btn" class="nav-mode-btn" role="tab" aria-selected="false">
<span class="mode-icon">📋</span> <span class="mode-icon">📋</span>
<span>National Directory (1,439)</span> <span class="nav-text-desktop">National Directory (1,439)</span>
<span class="nav-text-mobile">Directory</span>
</button> </button>
</div> </div>
</div> </div>
@@ -969,10 +971,10 @@
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script> <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<!-- Data & Application Logic with Cache Busting --> <!-- Data & Application Logic with Cache Busting -->
<script src="data/portals.js?v=8"></script> <script src="data/portals.js?v=9"></script>
<script src="js/auth.js?v=8"></script> <script src="js/auth.js?v=9"></script>
<script src="js/radar.js?v=8"></script> <script src="js/radar.js?v=9"></script>
<script src="js/app.js?v=8"></script> <script src="js/app.js?v=9"></script>
<!-- Service Worker Instant Cache Invalidation --> <!-- Service Worker Instant Cache Invalidation -->
<script> <script>
+6 -6
View File
@@ -1,13 +1,13 @@
// Service Worker for FlockRadar Offline Driving & Live Updates // Service Worker for FlockRadar Offline Driving & Live Updates
const CACHE_NAME = 'flockradar-v8'; const CACHE_NAME = 'flockradar-v9';
const ASSETS_TO_CACHE = [ const ASSETS_TO_CACHE = [
'./', './',
'./index.html', './index.html',
'./css/styles.css?v=8', './css/styles.css?v=9',
'./js/app.js?v=8', './js/app.js?v=9',
'./js/auth.js?v=8', './js/auth.js?v=9',
'./js/radar.js?v=8', './js/radar.js?v=9',
'./data/portals.js?v=8', './data/portals.js?v=9',
'./manifest.json' './manifest.json'
]; ];