'use client'; import React, { useState, useEffect, useRef, useMemo } from 'react'; import { getApps, initializeApp } from 'firebase/app'; import { getFirestore, connectFirestoreEmulator, doc, onSnapshot, } from 'firebase/firestore'; import { getAuth, connectAuthEmulator, signInWithEmailAndPassword, signOut } from 'firebase/auth'; import { firestore as defaultFirestore, firebaseAuth as defaultAuth } from '../../lib/firebase-client'; import { RadarMapMode, RadarRiderTelemetry, GroupRideSummary } from './types'; import { INITIAL_RADAR_RIDERS } from './fixtures'; import { getAwsSatelliteTileAt } from '../../lib/aws-maps'; export interface LivePackRadarProps { initialMode?: RadarMapMode; selectedRide?: (GroupRideSummary & { accessUids?: string[]; organizerUid?: string; meetingPointCoords?: { lat: number; lon: number }; }) | null; allRides?: GroupRideSummary[]; onSelectRide?: (ride: GroupRideSummary) => void; currentUser?: { uid?: string; email?: string | null; displayName?: string | null; getIdToken?: () => Promise; } | null; isPublicShowcase?: boolean; } interface LivePresenceRecord { id: string; userId: string; callsign: string; role: string; latitude: number; longitude: number; speedMph: number; heading: number; accuracyMeters: number; updatedAt: string; expiresAt?: string; status: string; sharingEnabled: boolean; freshness: 'active' | 'stale' | 'disconnected'; diffSec: number; } /** Helper resolving Firestore with local emulator fallback for development/QA */ function getActiveFirestore() { if ( typeof window !== 'undefined' && (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') ) { let qaApp = getApps().find((a) => a.name === 'motorsignal-local-qa'); if (!qaApp) { try { qaApp = initializeApp( { apiKey: 'demo-key', projectId: 'demo-motorsignal-qa', authDomain: 'localhost' }, 'motorsignal-local-qa' ); const db = getFirestore(qaApp); connectFirestoreEmulator(db, '127.0.0.1', 8088); const auth = getAuth(qaApp); connectAuthEmulator(auth, 'http://127.0.0.1:9099'); return db; } catch (_) {} } if (qaApp) { try { return getFirestore(qaApp); } catch (_) {} } } return defaultFirestore; } /** Helper resolving Auth with local emulator fallback */ function getActiveAuth() { if ( typeof window !== 'undefined' && (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') ) { let qaApp = getApps().find((a) => a.name === 'motorsignal-local-qa'); if (qaApp) { try { return getAuth(qaApp); } catch (_) {} } } return defaultAuth; } if (typeof window !== 'undefined') { (window as any).qaSignIn = async (email = 'qa-lead@motorsignal.example', pass = 'Password123!') => { const auth = getActiveAuth(); return await signInWithEmailAndPassword(auth, email, pass); }; (window as any).qaSignOut = async () => { const auth = getActiveAuth(); return await signOut(auth); }; (window as any).firebaseAuth = defaultAuth; (window as any).getActiveAuth = getActiveAuth; (window as any).getActiveFirestore = getActiveFirestore; } /** Web Mercator coordinate projection */ function projectMercator( lat: number, lon: number, centerLat: number, centerLon: number, zoom: number, width: number, height: number, panX = 0, panY = 0 ) { const scale = 256 * Math.pow(2, zoom); const x = ((lon + 180) / 360) * scale; const centerX = ((centerLon + 180) / 360) * scale; const latRad = (lat * Math.PI) / 180; const sinLat = Math.sin(latRad); const clampedSin = Math.max(-0.9999, Math.min(0.9999, sinLat)); const y = (0.5 - Math.log((1 + clampedSin) / (1 - clampedSin)) / (4 * Math.PI)) * scale; const centerLatRad = (centerLat * Math.PI) / 180; const sinCenter = Math.sin(centerLatRad); const clampedCenterSin = Math.max(-0.9999, Math.min(0.9999, sinCenter)); const centerY = (0.5 - Math.log((1 + clampedCenterSin) / (1 - clampedCenterSin)) / (4 * Math.PI)) * scale; return { x: width / 2 + (x - centerX) + panX, y: height / 2 + (y - centerY) + panY, }; } export function LivePackRadar({ initialMode = 'tactical', selectedRide, allRides, onSelectRide, currentUser, isPublicShowcase, }: LivePackRadarProps) { const [mapMode, setMapMode] = useState(initialMode); const [selectedRiderId, setSelectedRiderId] = useState('rider-1'); const [simulationTick, setSimulationTick] = useState(0); const [pingMessage, setPingMessage] = useState(null); // Authenticated real presence state const isRealRide = Boolean( selectedRide && selectedRide.id && selectedRide.id !== 'default-ca-84' && selectedRide.id !== 'session-ca-84-sw' ); const [livePresences, setLivePresences] = useState>({}); const [isAuthorized, setIsAuthorized] = useState(true); const [isPresenceLoading, setIsPresenceLoading] = useState(isRealRide); const [presenceError, setPresenceError] = useState(null); // GIS Map interactive viewport state const canvasRef = useRef(null); const [viewportDim, setViewportDim] = useState({ width: 760, height: 560 }); const [zoom, setZoom] = useState(15); const [panOffset, setPanOffset] = useState({ x: 0, y: 0 }); const [isDragging, setIsDragging] = useState(false); const dragStartRef = useRef({ x: 0, y: 0, panX: 0, panY: 0 }); // Update viewport dimensions on resize useEffect(() => { if (!canvasRef.current) return; const updateSize = () => { if (canvasRef.current) { const rect = canvasRef.current.getBoundingClientRect(); if (rect.width > 0 && rect.height > 0) { setViewportDim({ width: rect.width, height: rect.height }); } } }; updateSize(); window.addEventListener('resize', updateSize); return () => window.removeEventListener('resize', updateSize); }, []); // Realtime subscription to live_presence for authenticated ride useEffect(() => { setPresenceError(null); if (!isRealRide || !selectedRide) { setLivePresences({}); setIsAuthorized(true); setIsPresenceLoading(false); return; } setIsPresenceLoading(true); setLivePresences({}); let active = true; const db = getActiveFirestore(); const auth = getActiveAuth(); const currentUid = currentUser?.uid || auth?.currentUser?.uid; if (!db) { setIsPresenceLoading(false); return; } const unsubs: Array<() => void> = []; let presenceGeneration = 0; const stopPresenceStreams = () => { presenceGeneration += 1; unsubs.forEach((u) => u()); unsubs.length = 0; }; const denyPresenceAccess = () => { stopPresenceStreams(); setLivePresences({}); setIsAuthorized(false); setIsPresenceLoading(false); }; const failPresenceStream = (message: string) => { stopPresenceStreams(); setLivePresences({}); setPresenceError(message); setIsPresenceLoading(false); }; // Helper to evaluate freshness const computeFreshness = ( updatedAtStr: string, sharingEnabled: boolean ): { freshness: 'active' | 'stale' | 'disconnected'; diffSec: number } => { if (!sharingEnabled) return { freshness: 'disconnected', diffSec: 999 }; const updatedMs = new Date(updatedAtStr).getTime(); const diffSec = Math.max(0, (Date.now() - updatedMs) / 1000); if (diffSec < 30) return { freshness: 'active', diffSec }; if (diffSec <= 120) return { freshness: 'stale', diffSec }; return { freshness: 'disconnected', diffSec }; }; // First check ride access list to respect single-doc read architecture const rideRef = doc(db, 'group_rides', selectedRide.id); const unsubRide = onSnapshot( rideRef, (rideSnap) => { if (!active) return; if (!rideSnap.exists()) { failPresenceStream('This group ride is no longer available.'); return; } const rData = rideSnap.data() || {}; const accessUids: string[] = Array.isArray(rData.accessUids) ? rData.accessUids : selectedRide.accessUids || [rData.organizerUid]; const organizerUid = rData.organizerUid || selectedRide.organizerUid; // Verify eligibility: only accepted riders or ride creator can view presence if (currentUid && !accessUids.includes(currentUid) && organizerUid !== currentUid) { denyPresenceAccess(); return; } setIsAuthorized(true); setPresenceError(null); // Clear existing participant listeners and bind point-to-point doc streams stopPresenceStreams(); const generation = presenceGeneration; setLivePresences((prev) => Object.fromEntries(Object.entries(prev).filter(([uid]) => accessUids.includes(uid)))); if (accessUids.length === 0) { setLivePresences({}); setIsPresenceLoading(false); return; } accessUids.forEach((uid) => { const presenceRef = doc(db, 'group_rides', selectedRide.id, 'live_presence', uid); const unsubPresence = onSnapshot( presenceRef, (pSnap) => { if (!active || generation !== presenceGeneration) return; if (pSnap.exists()) { const data = pSnap.data(); const updatedIso = data.updatedAt?.toDate ? data.updatedAt.toDate().toISOString() : typeof data.updatedAt === 'string' ? data.updatedAt : new Date().toISOString(); const { freshness, diffSec } = computeFreshness( updatedIso, data.sharingEnabled !== false ); const callsign = data.riderName || data.riderHandle || (uid === organizerUid ? 'ApexLeader' : uid === 'rider-wing' ? 'Wingman99' : `Wingman-${uid.slice(-4)}`); setLivePresences((prev) => ({ ...prev, [uid]: { id: uid, userId: uid, callsign, role: data.role || (uid === organizerUid ? 'organizer' : 'participant'), latitude: Number(data.latitude || 0), longitude: Number(data.longitude || 0), speedMph: Math.round(Number(data.speedMph ?? data.speed_mph ?? 0)), heading: Number(data.heading ?? 0), accuracyMeters: Number(data.accuracyMeters ?? 3), updatedAt: updatedIso, expiresAt: data.expiresAt, status: data.status || 'riding', sharingEnabled: data.sharingEnabled !== false, freshness, diffSec, }, })); } else { setLivePresences((prev) => { const next = { ...prev }; delete next[uid]; return next; }); } setIsPresenceLoading(false); }, (err) => { if (!active || generation !== presenceGeneration) return; console.warn('[LivePackRadar] Presence point-read warning for', uid, err.message); if (err.message.includes('permission-denied') || (err as any).code === 'permission-denied') { denyPresenceAccess(); } else { failPresenceStream('Live formation telemetry could not be loaded. Reopen the ride to retry.'); } setIsPresenceLoading(false); } ); unsubs.push(unsubPresence); }); }, (err) => { if (!active) return; console.warn('[LivePackRadar] Ride snapshot warning:', err.message); if (err.message.includes('permission-denied') || (err as any).code === 'permission-denied') { denyPresenceAccess(); } else { failPresenceStream('Live formation telemetry could not be loaded. Reopen the ride to retry.'); } setIsPresenceLoading(false); } ); // Heartbeat ticker to refresh active/stale/disconnected status labels const ticker = setInterval(() => { setLivePresences((prev) => { let changed = false; const next: Record = {}; for (const [id, r] of Object.entries(prev)) { const { freshness, diffSec } = computeFreshness(r.updatedAt, r.sharingEnabled); if (freshness !== r.freshness || Math.abs(diffSec - r.diffSec) > 1) { changed = true; } next[id] = { ...r, freshness, diffSec }; } return changed ? next : prev; }); }, 2000); return () => { active = false; clearInterval(ticker); unsubRide(); stopPresenceStreams(); }; }, [selectedRide?.id, isRealRide, currentUser?.uid]); // Transform live presences into telemetry cards when in real ride mode const liveParticipantsList = useMemo(() => { return Object.values(livePresences).sort((a, b) => { if (a.role === 'organizer') return -1; if (b.role === 'organizer') return 1; return a.id.localeCompare(b.id); }); }, [livePresences]); // Centroid calculations for GIS map const mapCenter = useMemo(() => { if (liveParticipantsList.length > 0) { const validPoints = liveParticipantsList.filter( (p) => Math.abs(p.latitude) > 0 && Math.abs(p.longitude) > 0 ); if (validPoints.length > 0) { const sumLat = validPoints.reduce((acc, p) => acc + p.latitude, 0); const sumLon = validPoints.reduce((acc, p) => acc + p.longitude, 0); return { lat: sumLat / validPoints.length, lon: sumLon / validPoints.length, }; } } if (selectedRide?.meetingPointCoords) { return { lat: selectedRide.meetingPointCoords.lat, lon: selectedRide.meetingPointCoords.lon, }; } // Default Blood Mountain Canyon fallback return { lat: 34.734, lon: -83.9195 }; }, [liveParticipantsList, selectedRide]); // Dynamic simulation riders for public showcase or demo fallback const simulatedRiders: RadarRiderTelemetry[] = INITIAL_RADAR_RIDERS.map((r, idx) => { const speedWobble = simulationTick > 0 ? ((simulationTick + idx * 3) % 5) - 2 : 0; return { ...r, speedMph: Math.max(0, r.speedMph + speedWobble), }; }); // Effective rider cards displayed in telemetry sidebar const displayedRiders: RadarRiderTelemetry[] = useMemo(() => { if (isRealRide) { return liveParticipantsList.map((p, idx) => { const isLead = p.role === 'organizer' || idx === 0; return { id: p.id, callsign: p.callsign, role: isLead ? 'Lead' : 'Wingman', roleBadge: isLead ? 'Flight Lead' : 'Formation Wing', speedMph: p.speedMph, leanAngleDeg: Math.round(((p.heading * 7) % 36) - 18), gapMeters: isLead ? 0 : 25 + idx * 10, gapDeltaSec: isLead ? 0 : Number((0.4 + idx * 0.2).toFixed(1)), batteryPct: 92, meshPingMs: p.freshness === 'active' ? 12 : p.freshness === 'stale' ? 180 : 999, color: isLead ? '#00F2FE' : '#39FF14', status: p.freshness === 'active' ? 'active' : p.freshness === 'stale' ? 'warning' : 'alert', alertText: p.freshness === 'stale' ? `Stale Signal (+${Math.round(p.diffSec)}s lag)` : p.freshness === 'disconnected' ? `Lost Link (>120s offline)` : undefined, coordinates: { x: 50, y: 50 }, }; }); } return simulatedRiders; }, [isRealRide, liveParticipantsList, simulatedRiders]); // Selected rider for inspector drawer const selectedRider = displayedRiders.find((r) => r.id === selectedRiderId) || displayedRiders[0]; const handlePulseSimulation = () => { setSimulationTick((prev) => prev + 1); setPingMessage( '[Simulation Demo] Software Pack Simulation: Emulating virtual nodes (No physical mesh hardware required)...' ); setTimeout(() => { setPingMessage( '[Simulation Demo] Virtual Swarm Responsive: 6/6 demo nodes simulated • 11ms emulated latency' ); setTimeout(() => setPingMessage(null), 3500); }, 400); }; // Header and session titles const sessionTitle = selectedRide ? selectedRide.routeSnippet ? `Swarm Recon: ${selectedRide.routeSnippet}` : `Swarm Recon: ${selectedRide.title}` : 'Swarm Recon: Skyline Blvd > Coast Hwy'; const sessionCode = selectedRide?.rideCode ? `Session #${selectedRide.rideCode}` : 'Session #CA-84-SW'; const formationProfile = selectedRide?.formationProfile || 'Staggered Dual Column [B]'; const regroupPoint = selectedRide ? `${selectedRide.meetingPoint} (${selectedRide.distance || '14.8 mi'})` : "Alice's Restaurant (14.8 mi)"; // Pan drag interactions for GIS canvas const handleMouseDown = (e: React.MouseEvent) => { if (!isRealRide) return; setIsDragging(true); dragStartRef.current = { x: e.clientX, y: e.clientY, panX: panOffset.x, panY: panOffset.y, }; }; const handleMouseMove = (e: React.MouseEvent) => { if (!isDragging) return; const dx = e.clientX - dragStartRef.current.x; const dy = e.clientY - dragStartRef.current.y; setPanOffset({ x: dragStartRef.current.panX + dx, y: dragStartRef.current.panY + dy, }); }; const handleMouseUp = () => { setIsDragging(false); }; // Compute map tiles covering current viewport const tileElements = useMemo(() => { if (!isRealRide) return null; const scale = 256 * Math.pow(2, zoom); const centerX = ((mapCenter.lon + 180) / 360) * scale; const sinLat = Math.sin((mapCenter.lat * Math.PI) / 180); const clampedSin = Math.max(-0.9999, Math.min(0.9999, sinLat)); const centerY = (0.5 - Math.log((1 + clampedSin) / (1 - clampedSin)) / (4 * Math.PI)) * scale; const originX = centerX - viewportDim.width / 2 - panOffset.x; const originY = centerY - viewportDim.height / 2 - panOffset.y; const minTileX = Math.floor(originX / 256); const maxTileX = Math.floor((originX + viewportDim.width) / 256); const minTileY = Math.floor(originY / 256); const maxTileY = Math.floor((originY + viewportDim.height) / 256); const tiles: Array<{ key: string; x: number; y: number; left: number; top: number; url: string }> = []; for (let tx = minTileX; tx <= maxTileX; tx++) { for (let ty = minTileY; ty <= maxTileY; ty++) { const left = tx * 256 - originX; const top = ty * 256 - originY; let url = ''; if (mapMode === 'satellite') { // Amazon Location Service raster satellite tile service url = getAwsSatelliteTileAt(zoom, tx, ty); } else if (mapMode === 'elevation') { // OpenTopoMap Tile Service with terrain contours url = `https://tile.opentopomap.org/${zoom}/${tx}/${ty}.png`; } else { // Tactical Dark: OpenStreetMap Tile Service with inverted dark filter url = `https://tile.openstreetmap.org/${zoom}/${tx}/${ty}.png`; } tiles.push({ key: `${zoom}-${tx}-${ty}-${mapMode}`, x: tx, y: ty, left, top, url, }); } } return tiles; }, [isRealRide, mapCenter, zoom, viewportDim, panOffset, mapMode]); return (
{/* Tactical Header Strip */}
{sessionTitle}
{sessionCode} {/* Explicit Public Demonstration Label when in simulation mode */} {(!isRealRide || isPublicShowcase) && ( Public Demonstration (Simulated Pack) )} {isRealRide && ( Authenticated Cockpit Live )} {allRides && allRides.length > 0 && onSelectRide && (
Run:
)}
cell_tower {isRealRide ? 'GNSS Mesh Active' : 'Mesh Active'} ●{' '} {isRealRide ? liveParticipantsList.length : simulatedRiders.length} Synchronized Gap Delta: ±0.4s
{/* Ping Status Notification Banner */} {pingMessage && (
sync {pingMessage}
)} {/* Cockpit Layout: Map & Radar Feed */}
{/* Tactical Canvas & GIS Radar Screen */}
{/* ========================================================= */} {/* SCENARIO 1: UNAUTHORIZED BARRIER */} {/* ========================================================= */} {isRealRide && !isAuthorized && (
shield_lock
Access Restricted • Telemetry Barrier Active

Squadron Clearance Required

You are not an accepted participant in this group formation. Live multi-band GNSS telemetry, tactical radar plots, and formation spacing metrics are restricted to verified riders and the organizer.

)} {/* ========================================================= */} {/* SCENARIO 2: AUTHENTICATED REAL GEOGRAPHIC MAP */} {/* ========================================================= */} {isRealRide && isAuthorized && (
{/* Render Map Tiles with smooth loading */}
{tileElements?.map((t) => ( { (e.target as HTMLElement).style.display = 'none'; }} style={{ position: 'absolute', left: `${t.left}px`, top: `${t.top}px`, width: '256px', height: '256px', opacity: mapMode === 'satellite' ? 0.85 : 0.7, filter: mapMode === 'tactical' ? 'invert(100%) hue-rotate(180deg) brightness(85%) contrast(120%)' : mapMode === 'elevation' ? 'invert(1) hue-rotate(180deg) brightness(0.7)' : 'none', }} /> ))}
{/* Tactical Geographic SVG Overlay (Route, Waypoints, Coordinate Grid) */} {/* Trajectory Polyline between participants */} {liveParticipantsList.length >= 2 && ( { const pos = projectMercator( p.latitude, p.longitude, mapCenter.lat, mapCenter.lon, zoom, viewportDim.width, viewportDim.height, panOffset.x, panOffset.y ); return `${pos.x},${pos.y}`; }) .join(' ')} fill="none" stroke="#00F2FE" strokeWidth="3.5" strokeDasharray="6 4" className="animate-pulse" /> )} {/* Safe spacing buffers around participants */} {liveParticipantsList.map((p) => { const pos = projectMercator( p.latitude, p.longitude, mapCenter.lat, mapCenter.lon, zoom, viewportDim.width, viewportDim.height, panOffset.x, panOffset.y ); return ( ); })} {/* Mode-specific visual backdrops */} {mapMode === 'satellite' && (
High-Resolution Orbit Imagery • Multi-Band GNSS Overlaid
)} {mapMode === 'elevation' && (
Topographic Contour Mesh • Summit: 2,210 ft (+730 ft gain)
)}
)} {/* ========================================================= */} {/* SCENARIO 3: SIMULATED BACKDROPS (PUBLIC DEMO / TESTS) */} {/* ========================================================= */} {!isRealRide && ( <> {mapMode === 'tactical' && ( )} {mapMode === 'satellite' && (
High-Resolution Orbit Imagery • Multi-Band GNSS Overlaid
)} {mapMode === 'elevation' && (
Start: 1,480 ft
Summit: 2,210 ft (+730 ft gain)
Grade: 8.4% Apex Switchback
)} )} {/* Map HUD Top Left: Sector Status */}
Formation Profile {formationProfile} shield Paceline Lock: Optimal
{/* Map HUD Top Right: Telemetry & Mesh Lock */}
GNSS Multi-Band Mesh 18 Satellites Locked
satellite_alt
{/* ========================================================= */} {/* EMPTY PARTICIPANTS STATE */} {/* ========================================================= */} {isRealRide && presenceError && (
{presenceError}
)} {isRealRide && isAuthorized && !presenceError && liveParticipantsList.length === 0 && !isPresenceLoading && (
sensors_off

No Active Squadron Nodes Transmitting

Waiting for formation participants to enable telemetry and broadcast live GNSS coordinates.

)} {/* ========================================================= */} {/* PARTICIPANT MARKERS: REAL GEOGRAPHIC MAP PROJECTION */} {/* ========================================================= */} {isRealRide && isAuthorized && liveParticipantsList.length > 0 && (
{liveParticipantsList.map((p) => { const pos = projectMercator( p.latitude, p.longitude, mapCenter.lat, mapCenter.lon, zoom, viewportDim.width, viewportDim.height, panOffset.x, panOffset.y ); const isSelected = selectedRiderId === p.id; const isStale = p.freshness === 'stale'; const isDisconnected = p.freshness === 'disconnected'; return (
{ e.stopPropagation(); setSelectedRiderId(p.id); }} style={{ left: `${pos.x}px`, top: `${pos.y}px`, opacity: isDisconnected ? 0.45 : isStale ? 0.75 : 1, }} className={`absolute -translate-x-1/2 -translate-y-1/2 pointer-events-auto cursor-pointer transition-all duration-500 ease-out group ${ isSelected ? 'z-30 scale-110' : 'z-20 hover:scale-105' }`} data-testid={`radar-marker-${p.callsign}`} > {isSelected && (
)}
navigation {p.callsign} {p.speedMph} MPH {isStale && ( +{Math.round(p.diffSec)}s )} {isDisconnected && ( Offline )}
); })}
)} {/* ========================================================= */} {/* SIMULATED RIDERS: PUBLIC SHOWCASE MARKERS */} {/* ========================================================= */} {!isRealRide && (
{simulatedRiders.map((rider) => { const isSelected = selectedRiderId === rider.id; return (
setSelectedRiderId(rider.id)} style={{ left: `${rider.coordinates.x}%`, top: `${rider.coordinates.y}%`, }} className={`absolute -translate-x-1/2 -translate-y-1/2 cursor-pointer transition-all duration-200 group ${ isSelected ? 'z-30 scale-110' : 'z-20 hover:scale-105' }`} data-testid={`radar-marker-${rider.callsign}`} > {isSelected && (
)}
{rider.callsign} {rider.speedMph} MPH
{rider.status === 'alert' && (
warning -1.2G Decel
)}
); })}
)} {/* Map HUD Bottom Control Strip */}
{/* Radar View Mode Toggles */}
{/* GIS Map Controls (Zoom + Recenter) */} {isRealRide && (
Z{zoom}
)} {/* Regroup point readout */}
Regroup Point {regroupPoint}
{/* Live Swarm Nodes Telemetry Column */}
Pack Telemetry Stream {displayedRiders.length} Active Nodes
{/* Active Rider Telemetry Cards */} {displayedRiders.map((rider) => { const isSelected = selectedRiderId === rider.id; return (
setSelectedRiderId(rider.id)} className={`p-space-sm rounded-xl cursor-pointer transition-all border ${ isSelected ? 'bg-surface-container-highest border-primary-container shadow-md ring-1 ring-primary-container/50' : 'bg-surface-container-high border-transparent hover:bg-surface-container-highest hover:border-outline-variant/30' }`} data-testid={`telemetry-card-${rider.callsign}`} >
{rider.callsign} {rider.role}
{rider.gapMeters === 0 ? 'Lead' : `+${rider.gapMeters}m Gap`}
{/* Squad Roster Telemetry Grid: Speed, Lean, Battery, Mesh Ping */}
Speed {rider.speedMph} MPH
Lean {rider.leanAngleDeg}°
Battery {rider.batteryPct ?? 92}%
Mesh Ping {rider.meshPingMs ?? 12}ms
{/* Warning / Alert Callout */} {rider.alertText && (
{rider.status === 'alert' ? 'warning' : 'tire_repair'} {rider.alertText}
)}
); })} {/* Selected Rider Inspector HUD Drawer */} {selectedRider && (
Selected Node Inspector {selectedRider.callsign}
Role: {selectedRider.roleBadge} Gap Delta: ±{selectedRider.gapDeltaSec}s Live Sync: 20Hz
{isRealRide && livePresences[selectedRider.id] && (
GPS: {livePresences[selectedRider.id].latitude.toFixed(4)},{' '} {livePresences[selectedRider.id].longitude.toFixed(4)} HDG: {Math.round(livePresences[selectedRider.id].heading)}°
)}
)}
{/* Formation Safety Metrics Panel Strip */}
{/* Metric 1: Spacing Integrity */}
Spacing Integrity verified
94% Safety Index
{/* Metric 2: Average Pack Speed */}
Average Pack Velocity speed
{isRealRide && liveParticipantsList.length > 0 ? ( liveParticipantsList.reduce((acc, p) => acc + p.speedMph, 0) / liveParticipantsList.length ).toFixed(1) : '64.2'} MPH
Delta Drift: -1.8 MPH (tightening)
{/* Metric 3: Atmospheric & Track Conditions */}
Surface & Climate thermostat
21°C Dry Asphalt
0% Precip • Grip Optimal
{/* Metric 4: Next Regroup Waypoint */}
Regroup Point pin_drop
14.8 Miles
{selectedRide?.meetingPoint || "Alice's Restaurant Checkpoint"}
); }