| <!DOCTYPE html> |
| <html lang="en"> |
|
|
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Coastal AI Sentinel - 25-Grid Surveillance Network</title> |
| <script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js" crossorigin="anonymous"></script> |
| <script src="https://cdn.jsdelivr.net/npm/@mediapipe/control_utils/control_utils.js" crossorigin="anonymous"></script> |
| <script src="https://cdn.jsdelivr.net/npm/@mediapipe/drawing_utils/drawing_utils.js" crossorigin="anonymous"></script> |
| <script src="https://cdn.jsdelivr.net/npm/@mediapipe/object_detection/object_detection.js" crossorigin="anonymous"> |
| </script> |
| <link |
| href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;700;900&family=Rajdhani:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" |
| rel="stylesheet"> |
| <style> |
| :root { |
| --primary-cyan: #00f5ff; |
| --secondary-blue: #0066ff; |
| --accent-green: #00ff88; |
| --warning-orange: #ff8800; |
| --danger-red: #ff3366; |
| --deep-ocean: #0a1628; |
| --midnight: #0d1b2a; |
| --surface: #1b263b; |
| --surface-light: #2d3f5a; |
| --text-primary: #e0f7ff; |
| --text-secondary: #8ba3b8; |
| --glow-cyan: 0 0 20px rgba(0, 245, 255, 0.5); |
| --glow-green: 0 0 20px rgba(0, 255, 136, 0.5); |
| } |
| |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| } |
| |
| body { |
| font-family: 'Rajdhani', sans-serif; |
| background: linear-gradient(135deg, var(--deep-ocean) 0%, var(--midnight) 50%, #0f2847 100%); |
| color: var(--text-primary); |
| min-height: 100vh; |
| overflow-x: hidden; |
| } |
| |
| body::before { |
| content: ''; |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background-image: |
| linear-gradient(rgba(0, 245, 255, 0.03) 1px, transparent 1px), |
| linear-gradient(90deg, rgba(0, 245, 255, 0.03) 1px, transparent 1px); |
| background-size: 50px 50px; |
| pointer-events: none; |
| z-index: 0; |
| } |
| |
| header { |
| background: linear-gradient(180deg, rgba(10, 22, 40, 0.98) 0%, rgba(13, 27, 42, 0.95) 100%); |
| border-bottom: 1px solid rgba(0, 245, 255, 0.2); |
| padding: 1rem 2rem; |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| position: sticky; |
| top: 0; |
| z-index: 1000; |
| backdrop-filter: blur(10px); |
| } |
| |
| .logo { |
| display: flex; |
| align-items: center; |
| gap: 1rem; |
| } |
| |
| .logo-icon { |
| width: 50px; |
| height: 50px; |
| background: linear-gradient(135deg, var(--primary-cyan), var(--secondary-blue)); |
| border-radius: 12px; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| box-shadow: var(--glow-cyan); |
| animation: pulse 2s ease-in-out infinite; |
| } |
| |
| @keyframes pulse { |
| |
| 0%, |
| 100% { |
| box-shadow: 0 0 20px rgba(0, 245, 255, 0.5); |
| } |
| |
| 50% { |
| box-shadow: 0 0 40px rgba(0, 245, 255, 0.8); |
| } |
| } |
| |
| .logo-text { |
| font-family: 'Orbitron', sans-serif; |
| font-size: 1.5rem; |
| font-weight: 700; |
| background: linear-gradient(90deg, var(--primary-cyan), var(--accent-green)); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| background-clip: text; |
| } |
| |
| .header-stats { |
| display: flex; |
| gap: 2rem; |
| align-items: center; |
| } |
| |
| .stat-item { |
| text-align: center; |
| } |
| |
| .stat-value { |
| font-family: 'Orbitron', sans-serif; |
| font-size: 1.5rem; |
| font-weight: 700; |
| color: var(--primary-cyan); |
| text-shadow: var(--glow-cyan); |
| } |
| |
| .stat-label { |
| font-size: 0.75rem; |
| color: var(--text-secondary); |
| text-transform: uppercase; |
| letter-spacing: 1px; |
| } |
| |
| .powered-by { |
| font-family: 'JetBrains Mono', monospace; |
| font-size: 0.8rem; |
| color: var(--text-secondary); |
| text-decoration: none; |
| padding: 0.5rem 1rem; |
| border: 1px solid rgba(0, 245, 255, 0.3); |
| border-radius: 4px; |
| transition: all 0.3s ease; |
| } |
| |
| .powered-by:hover { |
| background: rgba(0, 245, 255, 0.1); |
| border-color: var(--primary-cyan); |
| color: var(--primary-cyan); |
| } |
| |
| .main-container { |
| display: grid; |
| grid-template-columns: 1fr 380px; |
| gap: 1rem; |
| padding: 1rem; |
| min-height: calc(100vh - 80px); |
| position: relative; |
| z-index: 1; |
| } |
| |
| .video-section { |
| display: flex; |
| flex-direction: column; |
| gap: 1rem; |
| } |
| |
| .section-header { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| padding: 0.75rem 1rem; |
| background: rgba(27, 38, 59, 0.8); |
| border-radius: 8px; |
| border: 1px solid rgba(0, 245, 255, 0.2); |
| } |
| |
| .section-title { |
| font-family: 'Orbitron', sans-serif; |
| font-size: 1rem; |
| color: var(--primary-cyan); |
| display: flex; |
| align-items: center; |
| gap: 0.5rem; |
| } |
| |
| .section-title::before { |
| content: ''; |
| width: 8px; |
| height: 8px; |
| background: var(--primary-cyan); |
| border-radius: 50%; |
| animation: blink 1s ease-in-out infinite; |
| } |
| |
| @keyframes blink { |
| |
| 0%, |
| 100% { |
| opacity: 1; |
| } |
| |
| 50% { |
| opacity: 0.3; |
| } |
| } |
| |
| .grid-controls { |
| display: flex; |
| gap: 0.5rem; |
| } |
| |
| .grid-btn { |
| background: rgba(0, 245, 255, 0.1); |
| border: 1px solid rgba(0, 245, 255, 0.3); |
| color: var(--primary-cyan); |
| padding: 0.5rem 1rem; |
| border-radius: 4px; |
| cursor: pointer; |
| font-family: 'Rajdhani', sans-serif; |
| font-weight: 600; |
| transition: all 0.3s ease; |
| } |
| |
| .grid-btn:hover, |
| .grid-btn.active { |
| background: var(--primary-cyan); |
| color: var(--deep-ocean); |
| } |
| |
| .video-grid { |
| display: grid; |
| grid-template-columns: repeat(5, 1fr); |
| gap: 8px; |
| } |
| |
| .camera-feed { |
| position: relative; |
| aspect-ratio: 16/9; |
| background: var(--surface); |
| border-radius: 8px; |
| overflow: hidden; |
| border: 1px solid rgba(0, 245, 255, 0.15); |
| transition: all 0.3s ease; |
| } |
| |
| .camera-feed:hover { |
| border-color: var(--primary-cyan); |
| box-shadow: var(--glow-cyan); |
| transform: scale(1.02); |
| z-index: 10; |
| } |
| |
| .camera-feed canvas { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| object-fit: cover; |
| } |
| |
| .camera-feed video { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| object-fit: cover; |
| opacity: 0; |
| } |
| |
| .camera-placeholder { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background: linear-gradient(135deg, var(--surface) 0%, var(--surface-light) 100%); |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| justify-content: center; |
| gap: 0.5rem; |
| } |
| |
| .camera-placeholder svg { |
| width: 32px; |
| height: 32px; |
| color: var(--text-secondary); |
| } |
| |
| .camera-info { |
| position: absolute; |
| bottom: 0; |
| left: 0; |
| right: 0; |
| padding: 0.5rem; |
| background: linear-gradient(transparent, rgba(0, 0, 0, 0.8)); |
| display: flex; |
| justify-content: space-between; |
| align-items: flex-end; |
| } |
| |
| .camera-name { |
| font-size: 0.7rem; |
| font-weight: 600; |
| color: var(--text-primary); |
| } |
| |
| .camera-location { |
| font-size: 0.6rem; |
| color: var(--text-secondary); |
| } |
| |
| .detection-badge { |
| font-size: 0.6rem; |
| padding: 2px 6px; |
| border-radius: 3px; |
| font-weight: 600; |
| } |
| |
| .detection-badge.active { |
| background: var(--accent-green); |
| color: var(--deep-ocean); |
| } |
| |
| .detection-badge.warning { |
| background: var(--warning-orange); |
| color: var(--deep-ocean); |
| } |
| |
| .detection-badge.alert { |
| background: var(--danger-red); |
| color: white; |
| animation: pulse-badge 0.5s ease-in-out infinite; |
| } |
| |
| @keyframes pulse-badge { |
| |
| 0%, |
| 100% { |
| transform: scale(1); |
| } |
| |
| 50% { |
| transform: scale(1.1); |
| } |
| } |
| |
| .sidebar { |
| display: flex; |
| flex-direction: column; |
| gap: 1rem; |
| max-height: calc(100vh - 100px); |
| overflow-y: auto; |
| } |
| |
| .sidebar::-webkit-scrollbar { |
| width: 6px; |
| } |
| |
| .sidebar::-webkit-scrollbar-track { |
| background: var(--surface); |
| border-radius: 3px; |
| } |
| |
| .sidebar::-webkit-scrollbar-thumb { |
| background: var(--primary-cyan); |
| border-radius: 3px; |
| } |
| |
| .ai-pipeline { |
| background: rgba(27, 38, 59, 0.9); |
| border-radius: 12px; |
| border: 1px solid rgba(0, 245, 255, 0.2); |
| padding: 1rem; |
| position: relative; |
| overflow: hidden; |
| } |
| |
| .ai-pipeline::before { |
| content: ''; |
| position: absolute; |
| top: -50%; |
| left: -50%; |
| width: 200%; |
| height: 200%; |
| background: conic-gradient(from 0deg, transparent, rgba(0, 245, 255, 0.1), transparent); |
| animation: rotate 10s linear infinite; |
| z-index: 0; |
| } |
| |
| @keyframes rotate { |
| 100% { |
| transform: rotate(360deg); |
| } |
| } |
| |
| .pipeline-title { |
| font-family: 'Orbitron', sans-serif; |
| font-size: 0.9rem; |
| color: var(--primary-cyan); |
| margin-bottom: 1rem; |
| text-align: center; |
| position: relative; |
| z-index: 1; |
| } |
| |
| .pipeline-diagram { |
| position: relative; |
| z-index: 1; |
| } |
| |
| .pipeline-stage { |
| display: flex; |
| align-items: center; |
| gap: 0.75rem; |
| padding: 0.5rem; |
| margin-bottom: 0.5rem; |
| background: rgba(0, 0, 0, 0.3); |
| border-radius: 8px; |
| border-left: 3px solid var(--primary-cyan); |
| transition: all 0.3s ease; |
| } |
| |
| .pipeline-stage:hover { |
| background: rgba(0, 245, 255, 0.1); |
| transform: translateX(5px); |
| } |
| |
| .stage-icon { |
| width: 36px; |
| height: 36px; |
| background: linear-gradient(135deg, var(--primary-cyan), var(--secondary-blue)); |
| border-radius: 8px; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-size: 1rem; |
| } |
| |
| .stage-info { |
| flex: 1; |
| } |
| |
| .stage-name { |
| font-size: 0.8rem; |
| font-weight: 600; |
| color: var(--text-primary); |
| } |
| |
| .stage-desc { |
| font-size: 0.65rem; |
| color: var(--text-secondary); |
| } |
| |
| .pipeline-flow { |
| display: flex; |
| justify-content: space-around; |
| margin: 1rem 0; |
| padding: 0.5rem; |
| background: rgba(0, 0, 0, 0.2); |
| border-radius: 8px; |
| position: relative; |
| z-index: 1; |
| } |
| |
| .flow-item { |
| text-align: center; |
| } |
| |
| .flow-icon { |
| width: 40px; |
| height: 40px; |
| margin: 0 auto 0.25rem; |
| background: linear-gradient(135deg, var(--surface-light), var(--surface)); |
| border-radius: 50%; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-size: 1.2rem; |
| border: 2px solid var(--primary-cyan); |
| animation: flow-pulse 2s ease-in-out infinite; |
| } |
| |
| .flow-item:nth-child(2) .flow-icon { |
| animation-delay: 0.3s; |
| } |
| |
| .flow-item:nth-child(3) .flow-icon { |
| animation-delay: 0.6s; |
| } |
| |
| .flow-item:nth-child(4) .flow-icon { |
| animation-delay: 0.9s; |
| } |
| |
| .flow-item:nth-child(5) .flow-icon { |
| animation-delay: 1.2s; |
| } |
| |
| @keyframes flow-pulse { |
| |
| 0%, |
| 100% { |
| box-shadow: 0 0 5px var(--primary-cyan); |
| } |
| |
| 50% { |
| box-shadow: 0 0 20px var(--primary-cyan); |
| } |
| } |
| |
| .flow-label { |
| font-size: 0.6rem; |
| color: var(--text-secondary); |
| } |
| |
| .analytics-panel { |
| background: rgba(27, 38, 59, 0.9); |
| border-radius: 12px; |
| border: 1px solid rgba(0, 245, 255, 0.2); |
| padding: 1rem; |
| } |
| |
| .analytics-header { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| margin-bottom: 1rem; |
| } |
| |
| .analytics-title { |
| font-family: 'Orbitron', sans-serif; |
| font-size: 0.9rem; |
| color: var(--primary-cyan); |
| } |
| |
| .live-indicator { |
| display: flex; |
| align-items: center; |
| gap: 0.5rem; |
| font-size: 0.7rem; |
| color: var(--accent-green); |
| } |
| |
| .live-dot { |
| width: 8px; |
| height: 8px; |
| background: var(--accent-green); |
| border-radius: 50%; |
| animation: live-blink 1s ease-in-out infinite; |
| } |
| |
| @keyframes live-blink { |
| |
| 0%, |
| 100% { |
| opacity: 1; |
| } |
| |
| 50% { |
| opacity: 0.3; |
| } |
| } |
| |
| .stat-grid { |
| display: grid; |
| grid-template-columns: repeat(2, 1fr); |
| gap: 0.75rem; |
| } |
| |
| .analytics-stat { |
| background: rgba(0, 0, 0, 0.3); |
| padding: 0.75rem; |
| border-radius: 8px; |
| text-align: center; |
| border: 1px solid rgba(0, 245, 255, 0.1); |
| } |
| |
| .analytics-stat.highlight { |
| border-color: var(--accent-green); |
| background: rgba(0, 255, 136, 0.1); |
| } |
| |
| .stat-number { |
| font-family: 'Orbitron', sans-serif; |
| font-size: 1.5rem; |
| font-weight: 700; |
| color: var(--accent-green); |
| } |
| |
| .stat-number.warning { |
| color: var(--warning-orange); |
| } |
| |
| .stat-number.danger { |
| color: var(--danger-red); |
| } |
| |
| .stat-desc { |
| font-size: 0.65rem; |
| color: var(--text-secondary); |
| margin-top: 0.25rem; |
| } |
| |
| .detection-log { |
| background: rgba(27, 38, 59, 0.9); |
| border-radius: 12px; |
| border: 1px solid rgba(0, 245, 255, 0.2); |
| padding: 1rem; |
| flex: 1; |
| display: flex; |
| flex-direction: column; |
| } |
| |
| .log-header { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| margin-bottom: 1rem; |
| } |
| |
| .log-title { |
| font-family: 'Orbitron', sans-serif; |
| font-size: 0.9rem; |
| color: var(--primary-cyan); |
| } |
| |
| .log-count { |
| font-size: 0.7rem; |
| padding: 0.25rem 0.5rem; |
| background: rgba(0, 245, 255, 0.2); |
| border-radius: 4px; |
| color: var(--primary-cyan); |
| } |
| |
| .log-entries { |
| flex: 1; |
| overflow-y: auto; |
| max-height: 200px; |
| } |
| |
| .log-entry { |
| display: flex; |
| align-items: center; |
| gap: 0.75rem; |
| padding: 0.5rem; |
| margin-bottom: 0.5rem; |
| background: rgba(0, 0, 0, 0.2); |
| border-radius: 6px; |
| font-size: 0.7rem; |
| animation: slide-in 0.3s ease-out; |
| } |
| |
| @keyframes slide-in { |
| from { |
| opacity: 0; |
| transform: translateX(-20px); |
| } |
| |
| to { |
| opacity: 1; |
| transform: translateX(0); |
| } |
| } |
| |
| .log-icon { |
| width: 24px; |
| height: 24px; |
| border-radius: 4px; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-size: 0.8rem; |
| } |
| |
| .log-icon.person { |
| background: rgba(0, 245, 255, 0.3); |
| } |
| |
| .log-icon.boat { |
| background: rgba(0, 255, 136, 0.3); |
| } |
| |
| .log-icon.animal { |
| background: rgba(255, 136, 0, 0.3); |
| } |
| |
| .log-icon.warning { |
| background: rgba(255, 51, 102, 0.3); |
| } |
| |
| .log-info { |
| flex: 1; |
| } |
| |
| .log-camera { |
| color: var(--primary-cyan); |
| font-weight: 600; |
| } |
| |
| .log-detection { |
| color: var(--text-primary); |
| } |
| |
| .log-time { |
| color: var(--text-secondary); |
| font-family: 'JetBrains Mono', monospace; |
| font-size: 0.6rem; |
| } |
| |
| .safety-report { |
| background: rgba(27, 38, 59, 0.9); |
| border-radius: 12px; |
| border: 1px solid rgba(0, 245, 255, 0.2); |
| padding: 1rem; |
| } |
| |
| .report-header { |
| font-family: 'Orbitron', sans-serif; |
| font-size: 0.9rem; |
| color: var(--primary-cyan); |
| margin-bottom: 1rem; |
| display: flex; |
| align-items: center; |
| gap: 0.5rem; |
| } |
| |
| .safety-meter { |
| display: flex; |
| align-items: center; |
| gap: 1rem; |
| padding: 0.75rem; |
| background: rgba(0, 0, 0, 0.3); |
| border-radius: 8px; |
| margin-bottom: 0.75rem; |
| } |
| |
| .meter-label { |
| font-size: 0.75rem; |
| color: var(--text-secondary); |
| min-width: 80px; |
| } |
| |
| .meter-bar { |
| flex: 1; |
| height: 8px; |
| background: var(--surface); |
| border-radius: 4px; |
| overflow: hidden; |
| } |
| |
| .meter-fill { |
| height: 100%; |
| border-radius: 4px; |
| transition: width 0.5s ease; |
| } |
| |
| .meter-fill.low { |
| background: linear-gradient(90deg, var(--danger-red), var(--warning-orange)); |
| } |
| |
| .meter-fill.medium { |
| background: linear-gradient(90deg, var(--warning-orange), #ffdd00); |
| } |
| |
| .meter-fill.high { |
| background: linear-gradient(90deg, var(--accent-green), #00ffaa); |
| } |
| |
| .meter-value { |
| font-family: 'Orbitron', sans-serif; |
| font-size: 0.8rem; |
| min-width: 50px; |
| text-align: right; |
| } |
| |
| .detection-canvas { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| pointer-events: none; |
| } |
| |
| .camera-loading { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| justify-content: center; |
| background: var(--surface); |
| z-index: 5; |
| } |
| |
| .loading-spinner { |
| width: 30px; |
| height: 30px; |
| border: 3px solid var(--surface-light); |
| border-top-color: var(--primary-cyan); |
| border-radius: 50%; |
| animation: spin 1s linear infinite; |
| } |
| |
| @keyframes spin { |
| 100% { |
| transform: rotate(360deg); |
| } |
| } |
| |
| .loading-text { |
| margin-top: 0.5rem; |
| font-size: 0.65rem; |
| color: var(--text-secondary); |
| } |
| |
| @media (max-width: 1400px) { |
| .main-container { |
| grid-template-columns: 1fr 320px; |
| } |
| |
| .video-grid { |
| grid-template-columns: repeat(4, 1fr); |
| } |
| } |
| |
| @media (max-width: 1100px) { |
| .main-container { |
| grid-template-columns: 1fr; |
| } |
| |
| .sidebar { |
| display: grid; |
| grid-template-columns: repeat(2, 1fr); |
| max-height: none; |
| } |
| |
| .detection-log { |
| max-height: 300px; |
| } |
| } |
| |
| @media (max-width: 768px) { |
| header { |
| flex-direction: column; |
| gap: 1rem; |
| padding: 1rem; |
| } |
| |
| .header-stats { |
| width: 100%; |
| justify-content: space-around; |
| } |
| |
| .video-grid { |
| grid-template-columns: repeat(3, 1fr); |
| } |
| |
| .sidebar { |
| grid-template-columns: 1fr; |
| } |
| } |
| |
| @media (max-width: 480px) { |
| .video-grid { |
| grid-template-columns: repeat(2, 1fr); |
| } |
| |
| .logo-text { |
| font-size: 1rem; |
| } |
| } |
| |
| .loading-overlay { |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background: var(--deep-ocean); |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| justify-content: center; |
| z-index: 9999; |
| transition: opacity 0.5s ease; |
| } |
| |
| .loading-overlay.hidden { |
| opacity: 0; |
| pointer-events: none; |
| } |
| |
| .loader { |
| width: 80px; |
| height: 80px; |
| border: 4px solid var(--surface); |
| border-top-color: var(--primary-cyan); |
| border-radius: 50%; |
| animation: spin 1s linear infinite; |
| } |
| |
| .loading-text-main { |
| margin-top: 1.5rem; |
| font-family: 'Orbitron', sans-serif; |
| color: var(--primary-cyan); |
| font-size: 1rem; |
| } |
| |
| .loading-subtext { |
| color: var(--text-secondary); |
| font-size: 0.8rem; |
| margin-top: 0.5rem; |
| } |
| |
| .toast-container { |
| position: fixed; |
| top: 100px; |
| right: 20px; |
| z-index: 10000; |
| display: flex; |
| flex-direction: column; |
| gap: 0.5rem; |
| } |
| |
| .toast { |
| background: rgba(27, 38, 59, 0.95); |
| border: 1px solid var(--primary-cyan); |
| border-radius: 8px; |
| padding: 1rem; |
| min-width: 280px; |
| animation: toast-in 0.3s ease-out; |
| box-shadow: 0 4px 20px rgba(0, 245, 255, 0.3); |
| } |
| |
| @keyframes toast-in { |
| from { |
| opacity: 0; |
| transform: translateX(100px); |
| } |
| |
| to { |
| opacity: 1; |
| transform: translateX(0); |
| } |
| } |
| |
| .toast-header { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| margin-bottom: 0.5rem; |
| } |
| |
| .toast-title { |
| font-weight: 600; |
| color: var(--primary-cyan); |
| font-size: 0.85rem; |
| } |
| |
| .toast-close { |
| background: none; |
| border: none; |
| color: var(--text-secondary); |
| cursor: pointer; |
| font-size: 1.2rem; |
| } |
| |
| .toast-body { |
| font-size: 0.75rem; |
| color: var(--text-secondary); |
| } |
| |
| .camera-video { |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| object-fit: cover; |
| opacity: 1; |
| z-index: 1; |
| } |
| |
| #mainVideo { |
| display: none; |
| } |
| |
| .model-status { |
| display: flex; |
| align-items: center; |
| gap: 0.5rem; |
| font-size: 0.7rem; |
| color: var(--accent-green); |
| margin-top: 0.5rem; |
| } |
| |
| .status-dot { |
| width: 6px; |
| height: 6px; |
| border-radius: 50%; |
| background: var(--accent-green); |
| } |
| </style> |
| </head> |
|
|
| <body> |
| |
| <div class="loading-overlay" id="loadingOverlay"> |
| <div class="loader"></div> |
| <div class="loading-text-main">INITIALIZING COASTAL AI SENTINEL</div> |
| <div class="loading-subtext">Loading MediaPipe Object Detection Model...</div> |
| </div> |
|
|
| |
| <div class="toast-container" id="toastContainer"></div> |
|
|
| |
| <video id="mainVideo" autoplay playsinline></video> |
|
|
| |
| <header> |
| <div class="logo"> |
| <div class="logo-icon"> |
| <svg width="30" height="30" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"> |
| <path d="M2 12h20M2 12l5-5m-5 5l5 5M22 12l-5-5m5 5l-5 5" /> |
| <circle cx="12" cy="12" r="3" /> |
| </svg> |
| </div> |
| <span class="logo-text">COASTAL AI SENTINEL</span> |
| </div> |
|
|
| <div class="header-stats"> |
| <div class="stat-item"> |
| <div class="stat-value" id="activeStreams">0</div> |
| <div class="stat-label">Active Streams</div> |
| </div> |
| <div class="stat-item"> |
| <div class="stat-value" id="totalDetections">0</div> |
| <div class="stat-label">Total Detections</div> |
| </div> |
| <div class="stat-item"> |
| <div class="stat-value" id="activeAI">0</div> |
| <div class="stat-label">AI Models Active</div> |
| </div> |
| <div class="stat-item"> |
| <div class="stat-value" id="safetyScore">98%</div> |
| <div class="stat-label">Safety Score</div> |
| </div> |
| </div> |
|
|
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="powered-by"> |
| β‘ Built with anycoder |
| </a> |
| </header> |
|
|
| |
| <div class="main-container"> |
| |
| <div class="video-section"> |
| <div class="section-header"> |
| <div class="section-title">LIVE COASTAL CAMERA NETWORK</div> |
| <div class="grid-controls"> |
| <button class="grid-btn active" data-grid="25">25 Grid</button> |
| <button class="grid-btn" data-grid="9">9 Grid</button> |
| <button class="grid-btn" data-grid="4">4 Grid</button> |
| <button class="grid-btn" data-grid="1">1 Grid</button> |
| </div> |
| </div> |
|
|
| <div class="video-grid" id="videoGrid"> |
| |
| </div> |
| </div> |
|
|
| |
| <div class="sidebar"> |
| |
| <div class="ai-pipeline"> |
| <div class="pipeline-title">π€ AI/ML DATA SENSOR PIPELINE</div> |
| <div class="pipeline-diagram"> |
| <div class="pipeline-stage"> |
| <div class="stage-icon">πΉ</div> |
| <div class="stage-info"> |
| <div class="stage-name">Video Ingestion</div> |
| <div class="stage-desc">25 RTSP/HTTP Streams</div> |
| </div> |
| </div> |
| <div class="pipeline-stage"> |
| <div class="stage-icon">π</div> |
| <div class="stage-info"> |
| <div class="stage-name">MediaPipe Detection</div> |
| <div class="stage-desc">Object Detection & Tracking</div> |
| </div> |
| </div> |
| <div class="pipeline-stage"> |
| <div class="stage-icon">π§ </div> |
| <div class="stage-info"> |
| <div class="stage-name">YOLO v8 Model</div> |
| <div class="stage-desc">Real-time Classification</div> |
| </div> |
| </div> |
| <div class="pipeline-stage"> |
| <div class="stage-icon">π</div> |
| <div class="stage-info"> |
| <div class="stage-name">Data Correlation</div> |
| <div class="stage-desc">Pattern Analysis & Prediction</div> |
| </div> |
| </div> |
| <div class="pipeline-stage"> |
| <div class="stage-icon">β‘</div> |
| <div class="stage-info"> |
| <div class="stage-name">Alert Generation</div> |
| <div class="stage-desc">Safety & Anomaly Reports</div> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="pipeline-flow"> |
| <div class="flow-item"> |
| <div class="flow-icon">π·</div> |
| <div class="flow-label">Input</div> |
| </div> |
| <div class="flow-item"> |
| <div class="flow-icon">βοΈ</div> |
| <div class="flow-label">Process</div> |
| </div> |
| <div class="flow-item"> |
| <div class="flow-icon">π§ </div> |
| <div class="flow-label">AI/ML</div> |
| </div> |
| <div class="flow-item"> |
| <div class="flow-icon">π</div> |
| <div class="flow-label">Analytics</div> |
| </div> |
| <div class="flow-item"> |
| <div class="flow-icon">π</div> |
| <div class="flow-label">Output</div> |
| </div> |
| </div> |
| |
| <div class="model-status" id="modelStatus"> |
| <div class="status-dot"></div> |
| <span>MediaPipe Model Loading...</span> |
| </div> |
| </div> |
|
|
| |
| <div class="analytics-panel"> |
| <div class="analytics-header"> |
| <div class="analytics-title">REAL-TIME ANALYTICS</div> |
| <div class="live-indicator"> |
| <div class="live-dot"></div> |
| LIVE |
| </div> |
| </div> |
| <div class="stat-grid"> |
| <div class="analytics-stat highlight"> |
| <div class="stat-number" id="objectsDetected">0</div> |
| <div class="stat-desc">Objects Detected</div> |
| </div> |
| <div class="analytics-stat"> |
| <div class="stat-number" id="boatsTracked">0</div> |
| <div class="stat-desc">Vessels Tracked</div> |
| </div> |
| <div class="analytics-stat"> |
| <div class="stat-number" id="peopleCount">0</div> |
| <div class="stat-desc">People Detected</div> |
| </div> |
| <div class="analytics-stat"> |
| <div class="stat-number warning" id="anomalies">0</div> |
| <div class="stat-desc">Anomalies</div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="safety-report"> |
| <div class="report-header"> |
| π SAFETY ASSESSMENT REPORT |
| </div> |
| <div class="safety-meter"> |
| <span class="meter-label">Water Level</span> |
| <div class="meter-bar"> |
| <div class="meter-fill high" style="width: 72%"></div> |
| </div> |
| <span class="meter-value">72%</span> |
| </div> |
| <div class="safety-meter"> |
| <span class="meter-label">Beach Activity</span> |
| <div class="meter-bar"> |
| <div class="meter-fill medium" style="width: 45%"></div> |
| </div> |
| <span class="meter-value">45%</span> |
| </div> |
| <div class="safety-meter"> |
| <span class="meter-label">Vessel Traffic</span> |
| <div class="meter-bar"> |
| <div class="meter-fill low" style="width: 28%"></div> |
| </div> |
| <span class="meter-value">28%</span> |
| </div> |
| <div class="safety-meter"> |
| <span class="meter-label">Overall Safety</span> |
| <div class="meter-bar"> |
| <div class="meter-fill high" style="width: 94%"></div> |
| </div> |
| <span class="meter-value">94%</span> |
| </div> |
| </div> |
|
|
| |
| <div class="detection-log"> |
| <div class="log-header"> |
| <div class="log-title">DETECTION LOG</div> |
| <div class="log-count" id="logCount">0 events</div> |
| </div> |
| <div class="log-entries" id="logEntries"> |
| |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| const cameraLocations = [ |
| { name: "Cape Hatteras", location: "North Carolina", lat: 35.25, lon: -75.53 }, |
| { name: "Ocean City", location: "Maryland", lat: 38.34, lon: -75.08 }, |
| { name: "Virginia Beach", location: "Virginia", lat: 36.85, lon: -75.97 }, |
| { name: "Myrtle Beach", location: "South Carolina", lat: 33.69, lon: -78.89 }, |
| { name: "Cocoa Beach", location: "Florida", lat: 28.32, lon: -80.61 }, |
| { name: "Malibu", location: "California", lat: 34.03, lon: -118.68 }, |
| { name: "Santa Monica", location: "California", lat: 34.01, lon: -118.49 }, |
| { name: "Daytona Beach", location: "Florida", lat: 29.21, lon: -81.02 }, |
| { name: "Wilmington", location: "North Carolina", lat: 34.23, lon: -77.87 }, |
| { name: "Key West", location: "Florida", lat: 24.56, lon: -81.78 }, |
| { name: "Huntington Beach", location: "California", lat: 33.66, lon: -117.99 }, |
| { name: "Miami Beach", location: "Florida", lat: 25.79, lon: -80.13 }, |
| { name: "Atlantic City", location: "New Jersey", lat: 39.36, lon: -74.42 }, |
| { name: "Galveston", location: "Texas", lat: 29.30, lon: -94.79 }, |
| { name: "Honolulu", location: "Hawaii", lat: 21.31, lon: -157.86 }, |
| { name: "Portland", location: "Maine", lat: 43.66, lon: -70.26 }, |
| { name: "San Diego", location: "California", lat: 32.72, lon: -117.16 }, |
| { name: "Savannah", location: "Georgia", lat: 32.08, lon: -81.09 }, |
| { name: "Charleston", location: "South Carolina", lat: 32.78, lon: -79.93 }, |
| { name: "Newport", location: "Rhode Island", lat: 41.49, lon: -71.31 }, |
| { name: "Annapolis", location: "Maryland", lat: 38.98, lon: -76.49 }, |
| { name: "Fort Lauderdale", location: "Florida", lat: 26.12, lon: -80.15 }, |
| { name: "Tampa", location: "Florida", lat: 27.95, lon: -82.46 }, |
| { name: "Wrightsville Beach", location: "North Carolina", lat: 34.27, lon: -77.80 }, |
| { name: "Cape Cod", location: "Massachusetts", lat: 41.68, lon: -69.99 } |
| ]; |
| |
| |
| const cocoCategories = { |
| 0: { type: 'person', icon: 'π€', class: 'person', name: 'Person' }, |
| 1: { type: 'bicycle', icon: 'π²', class: 'vehicle', name: 'Bicycle' }, |
| 2: { type: 'car', icon: 'π', class: 'vehicle', name: 'Car' }, |
| 3: { type: 'motorcycle', icon: 'ποΈ', class: 'vehicle', name: 'Motorcycle' }, |
| 4: { type: 'airplane', icon: 'βοΈ', class: 'vehicle', name: 'Airplane' }, |
| 5: { type: 'bus', icon: 'π', class: 'vehicle', name: 'Bus' }, |
| 6: { type: 'train', icon: 'π', class: 'vehicle', name: 'Train' }, |
| 7: { type: 'truck', icon: 'π', class: 'vehicle', name: 'Truck' }, |
| 8: { type: 'boat', icon: 'π€', class: 'boat', name: 'Boat' }, |
| 9: { type: 'traffic light', icon: 'π¦', class: 'object', name: 'Traffic Light' }, |
| 10: { type: 'fire hydrant', icon: 'π§―', class: 'object', name: 'Fire Hydrant' }, |
| 11: { type: 'stop sign', icon: 'π', class: 'object', name: 'Stop Sign' }, |
| 12: { type: 'parking meter', icon: 'π
ΏοΈ', class: 'object', name: 'Parking Meter' }, |
| 13: { type: 'bench', icon: 'πͺ', class: 'object', name: 'Bench' }, |
| 14: { type: 'bird', icon: 'π¦', class: 'animal', name: 'Bird' }, |
| 15: { type: 'cat', icon: 'π±', class: 'animal', name: 'Cat' }, |
| 16: { type: 'dog', icon: 'π', class: 'animal', name: 'Dog' }, |
| 17: { type: 'horse', icon: 'π΄', class: 'animal', name: 'Horse' }, |
| 18: { type: 'sheep', icon: 'π', class: 'animal', name: 'Sheep' }, |
| 19: { type: 'cow', icon: 'π', class: 'animal', name: 'Cow' }, |
| 20: { type: 'elephant', icon: 'π', class: 'animal', name: |