feat(core): reconcile local development, hybrid design, sensor fusion, and docs

- Consolidate Hybrid v1 tokens, theme extensions, and core UI components (HybridCard, HybridMetricTile, HybridRidingActionBar, HybridStaticRecBadge)
This commit is contained in:
Developer 2026-10-09 05:12:01 -04:00
parent 602aa962e2
commit 20da9ee27d
191 changed files with 16323 additions and 1569 deletions

18
.gitignore vendored
View File

@ -76,3 +76,21 @@ scratch_*.py
1.29
graphify-out/
devtools_options.yaml
# Temporary outputs, root screenshots, and emulator caches
/screen*.png
/emulator_screenshot.png
/.firebase-tmp/
/output/
firebase.website-qa.json
# Scratch tools and ephemeral test runs
tools/*.py
tools/*.mjs
tools/*.cjs
tools/*.js
docs/qa-2026-10-01/auth.verify-*.json
docs/qa-2026-10-01/auth.verify-*.mjs
docs/qa-2026-10-01/chat.cross-account-results.json
docs/qa-2026-10-01/scenarios_machine_readable.json

18
GEMINI.md Normal file
View File

@ -0,0 +1,18 @@
\## Android Emulator / Device Policy
\- For all Android emulator testing, UI validation, screenshots, integration testing, and interactive app launches, ALWAYS use the AVD named `avdslim`.
\- Treat `avdslim` as the canonical MotorSignal Android test emulator.
\- Do not create, select, launch, or switch to another Android Virtual Device unless the user explicitly instructs you to do so.
\- Before running emulator-dependent tests, verify that `avdslim` is running and available through `adb devices`.
\- If `avdslim` is not running, start `avdslim` rather than choosing another emulator.
\- When multiple Android devices/emulators are connected, explicitly target `avdslim` for Flutter/ADB commands instead of relying on automatic device selection.
\- Physical devices should only be used when the user explicitly requests physical-device testing.

View File

@ -26,5 +26,9 @@ A high-performance, dark-themed telemetry and analytics dashboard for motorcycle
* **Database:** Drift (SQLite) for robust, local storage.
* **Sensor Fusion:** Complementary filter combining accelerometer and gyroscope for precise lean angles.
## Documentation
The active documentation map and dated evidence archive are indexed in [docs/README.md](docs/README.md).
## Disclaimer
This application relies on the accuracy of your smartphone's internal sensors. It should be securely mounted to the motorcycle using a rigid mount. Do not interact with the screen while riding. Ride safely!

View File

@ -48,6 +48,8 @@ android {
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
testInstrumentationRunner = "pl.leancode.patrol.PatrolJUnitRunner"
testInstrumentationRunnerArguments["clearPackageData"] = "false"
}
signingConfigs {

View File

@ -0,0 +1,31 @@
package app.motorsignal.motorcycle;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import pl.leancode.patrol.PatrolJUnitRunner;
@RunWith(Parameterized.class)
public class MainActivityTest {
@Parameters(name = "{0}")
public static Object[] testCases() {
PatrolJUnitRunner instrumentation = (PatrolJUnitRunner) InstrumentationRegistry.getInstrumentation();
instrumentation.setUp(MainActivity.class);
instrumentation.waitForPatrolAppService();
return instrumentation.listDartTests();
}
public MainActivityTest(String dartTestName) {
this.dartTestName = dartTestName;
}
private final String dartTestName;
@Test
public void runDartTest() {
PatrolJUnitRunner instrumentation = (PatrolJUnitRunner) InstrumentationRegistry.getInstrumentation();
instrumentation.runDartTest(dartTestName);
}
}

30
docs/README.md Normal file
View File

@ -0,0 +1,30 @@
# Documentation
This index separates active reference material from dated planning, audit, QA, and deployment evidence. `AGENTS.md`, current code, schemas, and tests remain the authority when they conflict with documentation.
## Repository entrypoints
- [Repository README](../README.md): setup and application overview.
- [Agent rules](../AGENTS.md): mandatory development boundaries and verification rules.
- [Product brief](../PRODUCT.md): product intent and constraints.
- [Changelog](../CHANGELOG.md): canonical release history.
## Active reference
- [Architecture](architecture/): telemetry, cloud, and data-model references.
- [Design](design/): interface language and visual tokens.
- [Product](product/): entitlement baseline and roadmap.
- [Guides](guides/): field-testing procedure.
- [Release operations](RELEASING.md) and [versioning](VERSIONING.md).
- [Agent domain map](agents/domain.md): documentation authority and subsystem routing.
## Operational and historical material
- [Website and production records](#website-and-production-records): current scoped deployment and credential evidence remains at this level.
- [Archive](archive/): preserved dated planning, audit, QA, and legacy summaries. These documents are evidence, not current implementation proof.
## Website and production records
- `website-*.md`: scoped website verification, deployment, replay, and credential records dated 2026-10-04.
- `qa-*.md` and `QA_AUDIT_REPORT_2026-09-30.md`: dated QA outcomes and follow-up evidence.
- `OPS4_*.md`, `PRODUCTION_DEPLOYMENT_CHECKLIST.md`, and `WEBSITE_ADMIN_CURRENT_STATE.md`: operational references that require validation against the live configuration before use.

View File

@ -13,13 +13,13 @@ When documents conflict, verify behavior in code and tests. Do not revive obsole
## Documentation Map
- Telemetry and field validation: `TELEMETRY_ARCHITECTURE.md`, `TELEMETRY_VALIDATION.md`, and `FIELD_TESTING.md`.
- Cloud and sync: `MOTORSIGNAL_FINAL_CLOUD_DATA_MODEL.md`, `MOTORSIGNAL_CLOUD_ARCHITECTURE.md`, and `cloudflare/motorsignal-r2-worker/README.md`.
- Product and entitlement intent: `MOTORSIGNAL_FREE_PRO_SPEC.md` and `MOTORSIGNAL_PRODUCT_ROADMAP.md`. These are planning baselines, not proof that features exist.
- Interface language and visual tokens: `DESIGN.md`. Validate behavioral and timing claims against current code.
- Telemetry and field validation: `docs/architecture/telemetry-architecture.md`, `docs/architecture/telemetry-validation.md`, and `docs/guides/field-testing.md`.
- Cloud and sync: `docs/architecture/cloud-data-model.md`, `docs/architecture/cloud-architecture.md`, and `cloudflare/motorsignal-r2-worker/README.md`.
- Product and entitlement intent: `docs/product/free-pro-spec.md` and `docs/product/roadmap.md`. These are planning baselines, not proof that features exist.
- Interface language and visual tokens: `docs/design/design-system.md`. Validate behavioral and timing claims against current code.
- Releases and versions: `docs/RELEASING.md` and `docs/VERSIONING.md`. Preserve Semantic Versioning and monotonically increasing build numbers; use current `AGENTS.md` verification gates and current schema values where old examples conflict.
- Waze relay operation: `server/waze_relay/README.md`.
- `CHANGELOG.md`, `FEATURES_CHANGELOG.md`, `DEVELOPMENT_HISTORY.md`, `MOTORSIGNAL_AI_ENVIRONMENT_AUDIT.md`, phase reports, discovery documents, and gap audits are dated evidence. Never treat their old test counts, release status, schema numbers, or pending physical-validation claims as current proof.
- `CHANGELOG.md` is the canonical release record. `docs/archive/` contains dated feature histories, environment audits, phase reports, discovery documents, and gap audits. Never treat their old test counts, release status, schema numbers, or pending physical-validation claims as current proof.
## Stable Vocabulary

View File

@ -0,0 +1,7 @@
# Legacy summaries
This directory consolidates discovery for the overlapping August 2026 historical summaries while preserving each original record and its unique details. Use the root [CHANGELOG](../../../../CHANGELOG.md) for release history and current code for current behavior.
- [Development history](development-history.md): commit and phase chronology.
- [Features changelog](features-changelog.md): historical feature inventory.
- [Project summary](project-summary.md): historical architecture overview.

View File

@ -91,4 +91,4 @@ f7fbe71 (2026-08-27) - fix(telemetry): solve sensor fusion false lean, 3D G-forc
* **Landscape UI Optimization**: Compacted toolbars and AppBars to **36px** in landscape mode to maximize map and gauge visibility.
* **TomTom v5 API Upgrade**: Migrated to official TomTom query-parameter v5 endpoints.
* **Waze Alert Relay Server**: Built and dockerized self-hosted Node.js relay backend in `server/waze_relay/`.
* **Markdown Documentation**: Generated `FEATURES_CHANGELOG.md` and `DEVELOPMENT_HISTORY.md`.
* **Markdown Documentation**: Generated `features-changelog.md` and `development-history.md`.

14
docs/archive/README.md Normal file
View File

@ -0,0 +1,14 @@
# Documentation Archive
These files preserve dated evidence and planning context. They are not current specifications. Validate present behavior against `AGENTS.md`, source, schemas, and tests.
- [2026-08](2026-08/): Phase 2 planning, field-test/QA reports, legacy summaries, and the environment audit.
- [2026-09](2026-09/): dated architecture gap audit.
## Consolidated legacy discovery
The following overlapping historical summaries are retained as separate source records because each has unique detail. `CHANGELOG.md` at the repository root is the canonical release record.
- [Development history](2026-08/legacy/development-history.md): commit and phase chronology.
- [Features changelog](2026-08/legacy/features-changelog.md): historical feature inventory.
- [Project summary](2026-08/legacy/project-summary.md): historical architecture overview.

View File

@ -0,0 +1,103 @@
# DESIGN.md — MotorSignal Hybrid v1 Canonical Tokens & Components
## 1. Visual Language & Principles
- **Theme Name**: MotorSignal Precision Telemetry & Community (Hybrid v1)
- **Visual Direction**: Conventional cockpit layout clarity, high-contrast anti-glare data hierarchy, restrained cyan branding accents, and strict zero-clutter riding ergonomics.
- **Static Recording Discipline**: Active recording indicators (`REC • LIVE`) must remain 100% static (no pulsating, flashing, or shifting animations) to eliminate rider distraction during high-speed operation.
- **Physical Touch Enforcement**: Strict minimum `48×48 CSS px` (48×48dp) touch boundaries across all buttons, segmented toggles, navigation tabs, and modal triggers. Primary riding controls enforce `56dp` minimum height.
- **Platform Inset Rules**: Dynamic runtime safe-area clearance (`SafeArea` / `MediaQuery.of(context).padding` in Flutter; `env(safe-area-inset-*)` in Web/PWA).
- **Accessibility & Runtime Verification**: Accessibility scaling (200% text scale) and runtime performance claims are treated as unverified targets until proven via emulator/device test execution.
---
## 2. Canonical Token Set (Synchronized Day & Night Modes)
> [!NOTE]
> Canonical Canvas Token Reconciliation: `#0B1326` is the canonical night canvas background. Hex variants like `#081326` or `#0B111E` appearing in intermediate drafts are reconciled to canonical `#0B1326`.
```json
{
"theme": "MotorSignal Hybrid v1",
"modes": {
"night": {
"surface_canvas": "#0B1326",
"surface_card_primary": "#111C38",
"surface_card_secondary": "#17223B",
"surface_card_lowest": "#060E20",
"surface_border": "#1E293B",
"surface_border_subtle": "#24324D",
"text_primary": "#DAE2FD",
"text_muted": "#869397",
"brand_cyan": "#06B6D4",
"brand_cyan_bright": "#22D3EE",
"brand_cyan_container": "#00424F",
"rec_static_green": "#10B981",
"rec_static_green_bg": "#064E3B",
"caution_amber": "#F59E0B",
"danger_red": "#EF4444"
},
"day": {
"surface_canvas": "#F8FAFC",
"surface_card_primary": "#FFFFFF",
"surface_card_secondary": "#F1F5F9",
"surface_card_lowest": "#E2E8F0",
"surface_border": "#CBD5E1",
"surface_border_subtle": "#E2E8F0",
"text_primary": "#0F172A",
"text_muted": "#64748B",
"brand_cyan": "#0284C7",
"brand_cyan_bright": "#0EA5E9",
"brand_cyan_container": "#E0F2FE",
"rec_static_green": "#10B981",
"rec_static_green_bg": "#D1FAE5",
"caution_amber": "#D97706",
"danger_red": "#DC2626"
}
},
"typography": {
"font_primary": "Inter, -apple-system, BlinkMacSystemFont, sans-serif",
"font_telemetry": "JetBrains Mono, monospace",
"scale": {
"display_speed": "58px–72px (font-black / tabular)",
"telemetry_primary": "24px–28px (font-bold / tabular)",
"telemetry_unit": "10px–12px (uppercase, tracking-wider)",
"headline_lg": "24px–28px (font-bold)",
"headline_md": "18px–20px (font-semibold)",
"body_regular": "14px (line-height: 20px)",
"label_nav": "10px–11px (font-medium / bold)"
}
},
"touch_targets": {
"standard_minimum": "48px × 48px",
"primary_riding_cta": "56px height",
"nav_bar_height": "72px"
}
}
```
---
## 3. Reusable Component Guidelines
1. **Persistent 6-Destination Bottom Nav**:
- Items: `Dash` (1), `Map` (2), `Activity` (3), `Garage` (4), `Social` (5), `Settings` (6).
- Target bounding box: `≥48×48dp` per tab icon & label.
2. **Riding Dashboard Telemetry Cards**:
- Speed Hero: High-contrast large numeral with instant unit label (`mph` or `km/h`).
- Secondary Telemetry: Distance, Elapsed Time, Lean Dynamics (neutral max lean styling, live lean in cyan), Elevation Gain, and Braking G.
- Pinned Action Bar: `Pause Ride` (Amber, min 56dp) and `Finish Ride` (Dark/Slate with Cyan/Green accent, min 56dp), pinned to screen base with scrollable telemetry above in accessibility text scales.
- Non-pulsing `REC • LIVE` status badge.
3. **Group Ride Cards & Hub**:
- Decoupled Logistics: Date, time, and staging location placed on a dedicated full-width row with clock icon to avoid button/text truncation.
- Hitboxes: Segmented switcher (Feed vs Hub) and ride card buttons (`Manage Ride`, `Open Hub`, `Ride Details`, `Leave Pack`) sized to minimum `48dp` height.
- Visibility & Join Policies: Strict preservation of existing permissions, host/organizer badges, and join policies.
4. **Replay HUD**:
- Dedicated map controls locked to Top-Right quadrant, isolated from scrub timeline and lower telemetry gauges.
- Telemetry loading/status reflects real storage retrieval (e.g. SQLite database cache or cloud bundle retrieval), replacing speculative "100% buffered in-memory" labels.
- Scoped deletion options (*Local Device Cache Only* vs *Cloud & Local Records*). Never preselected.
5. **Account & System Status**:
- Reflects actual device sensors (Internal Phone IMU at authoritative 20Hz pipeline). Discards unsupported 100Hz claims and invalid Pro restrictions on basic telemetry acquisition.

View File

@ -0,0 +1,72 @@
# HANDOFF.md — MotorSignal Hybrid v1 Architecture & Implementation Notice
## 1. Prototype Scope & Verification Disclaimer
This handoff package contains **visual and interaction design prototypes** retrieved from Stitch project `5138779337547529796` ("MotorSignal UI/UX Redesign Exploration"). These specifications establish layout hierarchy, typography, touch target boundaries, and user experience flows for the MotorSignal mobile client (target viewport: `390 × 844 px`).
**CRITICAL IMPLEMENTATION PRINCIPLE:**
Mockups represent target client UI. **Repository code and backend APIs are authoritative** regarding:
1. Actual binary telemetry storage formats (authoritative SQLite Drift Schema 7, R2 telemetry bundles).
2. Authoritative Telemetry Acquisition (frozen at 20 Hz / 50 ms via `TelemetryService`).
3. Sensor fusion (frozen in `SensorFusionService`; no independent lean-angle calculations).
4. Ride lifecycle and motion semantics (frozen in `RideStateMachine`; 3.0 MPH moving gate, kickstand isolation).
5. Cloud synchronization and offline sync architecture (`SyncManager`, `SyncOutbox`, `FirebaseSyncProvider`).
6. Authentication providers and entitlements (free vs pro capabilities).
7. Existing group ride visibility, roles, and join policies.
Do **not** implement speculative backend mechanisms or invent hardware capabilities based on UI mockups.
---
## 2. Inconsistency Resolutions & Corrections
1. **Replay Telemetry Retrieval Wording**:
- Stitch Replay mockups contain the label `"100% Buffered (Local cache)"` or references to `"100% buffered in-memory"`.
- *Resolution*: In production code and documentation, loading and status wording must reflect the actual telemetry retrieval pipeline (retrieving points from the local SQLite database or streaming/downloading the complete bundle from cloud storage/R2). Do not present speculative "100% buffered in-memory" messaging.
2. **Account Screen Sample Rates and Pro Restrictions**:
- An older mockup displayed `Internal Phone IMU: Active (100Hz Gyro / Accel)` and claimed `Multi-sensor telemetry sync requires Pro subscription`.
- *Resolution*: Discarded. The authoritative acquisition cadence is 20 Hz (50 ms). Basic telemetry acquisition and synchronization are not restricted by unsupported Pro paywalls; current entitlements and free/pro models documented in the repository are strictly preserved.
3. **Ride Visibility and Join Policies**:
- Preserve all existing group ride visibility, membership, and join policies (public, unlisted, club-only, invite-only, host-approved join requests, participant lists, and role permissions). UI components adapt to these existing models without redefining policies.
4. **Canonical Token Reconciliation (#0B1326 vs #081326)**:
- Stitch HTML templates occasionally used `#081326` or `#0B111E` for dark backgrounds.
- *Resolution*: Canonical night surface canvas is strictly `#0B1326` as defined in `DESIGN.md`.
5. **Accessibility and Runtime Claims**:
- Visual mockups demonstrate 200% text scaling and proclaim "Contrast Ratio: 18.2:1 (Calc sRGB)" and "Design Target: 48×48px Minimum Target Discipline".
- *Resolution*: All accessibility, contrast, and runtime performance claims are treated as unverified until validated with unit/widget tests and on-device / emulator verification.
---
## 3. Phased Implementation Roadmap
### Phase 1: Mobile App Only (Current Implementation Target)
1. **Shared Day/Night Theme and Reusable Components**:
- Canonical tokens (`#0B1326` night canvas, `#F8FAFC` day canvas, `#06B6D4` / `#0284C7` brand cyan, `#10B981` static green, `#F59E0B` / `#D97706` amber, `#EF4444` / `#DC2626` red).
- Reusable cockpit telemetry card containers with 1px border.
- Pinned riding action buttons with minimum 56dp height and 48dp touch bounds.
- 6-destination bottom navigation bar with ≥48×48dp hitboxes.
- Static (non-pulsing) recording status indicator.
2. **Riding Dashboard**:
- High-contrast Speed hero with unit pill.
- Telemetry metrics (Trip Distance, Elapsed Time, Lean Dynamics with neutral max lean, Elevation Gain, Braking G).
- Live binding to authoritative `telemetryStreamProvider`, `rideStateMachineProvider`, and vehicle profile.
- Scrollable telemetry container for accessibility text scales, maintaining pinned Pause and Finish controls.
3. **Authenticated Group Ride Hub**:
- Decoupled meeting logistics row (Date, Time, Staging point) to prevent collision with action buttons.
- Segmented Feed / Hub switcher with ≥48dp touch targets.
- Ride cards supporting Host ("Manage Ride", "Open Hub") and Member ("Ride Details", "Leave Pack") states.
- Binding to live `groupRideServiceProvider` and user state.
### Phase 2: Planned Client Roadmap (Future Implementation)
- 2-step Create Group Ride wizard (staging typeahead, date/time pickers, access review, QR rendezvous code).
- Route calculation engine loading and fallback states ("direct distance—not a road route").
- Share ride sheet with recipient dispatch (Clubs, rider DMs, GPX export).
- Explicit scoped deletion dialog (Local cache only vs Cloud & local deletion).
### Phase 3: Capabilities Requiring Further Discovery
- External BLE sensor GATT protocol integration.
- Two-factor authentication (2FA) setup flows.
- Dynamic telemetry streaming throttles.

View File

@ -0,0 +1,46 @@
# SCREEN_INDEX.md — MotorSignal Hybrid v1 Final Design Handoff
This index maps the 14 required screen flows (and companion accessibility views) to their canonical Stitch project (`projects/5138779337547529796`) screen IDs, composite frame breakdowns, and implementation status.
## Screen Catalog & Resolved Stitch Identifiers
| # | Screen / State Name | Actual Stitch Screen ID | Composite Frame / Viewport | Status / Verification Scope | Description & Key Corrections |
|---|---|---|---|---|---|
| **01** | Riding Dashboard — Day | `fbbd68740d11447e8035cd0776133053` | Left Frame (Default Scale, 390×844) | **Tier 1: Verified UI** | Anti-glare daylight contrast (`#F8FAFC`), neutral max lean, static REC pill, pinned 56dp Pause/Finish controls, 6-tab nav. |
| **01b**| Riding Dashboard — Day (200% Text Scale Companion) | `fbbd68740d11447e8035cd0776133053` | Right Frame (200% Text Scale, 390×844) | **Accessibility Target** | Simulated 200% scale showing pinned controls and scrollable telemetry container. Unverified until tested. |
| **02** | Riding Dashboard — Night | `8f336a84e92c44d8aea012b2c6703b55` | Left Frame (Default Scale, 390×844) | **Tier 1: Verified UI** | Deep slate `#0B1326` canvas, neutral 27° max lean readout, static REC pill, zero-scroll phone fit. |
| **02b**| Riding Dashboard — Night (200% Text Scale Companion) | `8f336a84e92c44d8aea012b2c6703b55` | Right Frame (200% Text Scale, 390×844) | **Accessibility Target** | 200% scale demonstration with ≥48dp physical target boundary indicators. Unverified until tested. |
| **03** | Authenticated Group Ride Hub | `a661c1c9c82843a2bcb6b84d6d7855e1` | Left Frame (Default Scale, 390×844) | **Tier 1: Verified UI** | Dedicated logistics row, min-48dp Feed/Hub switch, 48dp action buttons. Preserves existing join/visibility policies. |
| **03b**| Group Ride Hub (200% Text Scale Companion) | `a661c1c9c82843a2bcb6b84d6d7855e1` | Right Frame (200% Text Scale, 390×844) | **Accessibility Target** | Validates text wrapping on meeting details without action button overlap. Unverified until tested. |
| **04** | Ride History & Archive | `934e8a33a5fa44009d40c292aacc630e` | Viewport 1 (Left Frame, 390×844) | **Tier 1 (UI) + Tier 2 (Sync UX)** | Sample data labeled, offline queued sync banner (2 rides queued), 3×2 telemetry card grid. |
| **05** | Ride Replay HUD | `934e8a33a5fa44009d40c292aacc630e` | Viewport 2 (Right Frame, 390×844) | **Tier 1 (UI) + Tier 2 (Proposed HUD)** | Top-right map controls, telemetry retrieval status follows actual database/bundle retrieval (discards '100% buffered in-memory'), 56dp playback controls. |
| **06** | Community Feed | `17a5177fab12466da2dea6639c8fd88f` | Frame 1 (390×844) | **Tier 1 (UI) + Tier 2 (Moderation)** | 4 feed tabs: Following, Clubs, Nearby, Discover. Generic community moderation card. Pro = subscription badge. |
| **07** | Create Group Ride — Step 1: Details & Staging | `17a5177fab12466da2dea6639c8fd88f` | Frame 2a (390×844) | **Tier 2: Proposed Feature** | Staging location typeahead, calendar/time pickers, route preview teaser. |
| **08** | Route Calculation Engine — Loading & Failure | `17a5177fab12466da2dea6639c8fd88f` | Frame 2b (390×844) | **Tier 2 / Tier 3 (Engine UX)** | Calculating state (spinner) vs Fallback state (GPX upload / "direct distance—not a road route"). |
| **09** | Create Group Ride — Step 2: Review & Access | `17a5177fab12466da2dea6639c8fd88f` | Frame 2c (390×844) | **Tier 2: Proposed Feature** | Ride summary recap, Public vs Invite-Only access toggles, pace selector, safety notes. Preserves existing policies. |
| **10** | Ride Created — Join Code & Dynamic QR | `17a5177fab12466da2dea6639c8fd88f` | Frame 2d (390×844) | **Tier 2: Proposed Feature** | High-contrast staging QR code, `#SKY92` rendezvous code, share invite triggers. |
| **11** | Account, Security & Sensor Connections | `fd0e6370af424ff4becfdaecda7f29de` | Frame 3 (390×844) | **Tier 1 (UI) + Tier 3 (Backend/Sensors)** | Internal Phone IMU active (20Hz authoritative pipeline; discarded older 100Hz and Pro restriction assumptions); truthful BLE disconnected state. |
| **12** | Share Ride — Recipient Selection Dialog | `17a5177fab12466da2dea6639c8fd88f` | Modal 1 (Overlay) | **Tier 2: Proposed Feature** | Scoped modal: Club channel, Rider DM, or native GPX export. |
| **13** | Delete Ride — Explicit Scope Selection | `17a5177fab12466da2dea6639c8fd88f` | Modal 2 (Overlay) | **Tier 2: Proposed Feature** | Explicit scope required: "Delete Local Cache Only" vs "Request Cloud & Local Deletion". No preselection. |
| **14** | Supporting System States Matrix | `17a5177fab12466da2dea6639c8fd88f` | States Grid (5 Cards) | **Tier 2: System UX** | Comprehensive previews: Loading/Downloading, Empty Filter, Sync Failure, Offline Queued, Location Denied. |
---
## Historical & Superseded Iterations
The following screens in project `5138779337547529796` were intermediate drafts or superseded revisions:
- `2cd85dda96ea48ff99a03395c432bfa1`: MotorSignal A/B/C Comparative Exploration (Early 3-way layout benchmark)
- `456a0df6c5a2486b8192c77f8f297c1c`: Hybrid Riding Dashboard (Night Mode) — Initial draft
- `dcad02763b2b499e845aac50dc5126c2`: Revised Authenticated Group Ride Hub — Initial draft
- `a82d7b2aced7407cb43dc0cd2501d690`: Hybrid Riding Dashboard (Day Mode) — Initial draft
- `5e0a7784ecec46eaad2f505a43d96576`: Hybrid Riding Dashboard - Night (Default vs Enlarged Text) — Intermediate draft
- `f9faeec4eec142349267ebb8d4c9a484`: Hybrid Riding Dashboard - Day (Default vs Enlarged Text) — Intermediate draft
- `2f39581f42734138991a2199601038ae`: Revised Group Ride Hub (Default vs Enlarged Text) — Intermediate draft
- `aa6aed675a3044e7a3025333cf732c2d`: MotorSignal - Ride History & Replay HUD — Redundant instance of `934e8a33a5fa44009d40c292aacc630e`
Uploaded Reference Screenshots from physical test runs:
- `7166114457503287420`: `Screenshot_20261006-020215.png` (Riding Dashboard night dark state)
- `7166114457503285870`: `Screenshot_20261006-020217.png` (Map and live cluster state)
- `7166114457503288416`: `Screenshot_20261006-020220.png` (Activity log & telemetry cards)
- `7166114457503286424`: `Screenshot_20261006-020224.png` (Group ride listing & cards)
- `7166114457503288970`: `Screenshot_20261006-020229.png` (Settings and sensor connection status)

View File

@ -0,0 +1,254 @@
{
"handoff_package": "MotorSignal Hybrid v1 — Final Design Handoff",
"project_id": "5138779337547529796",
"project_title": "MotorSignal UI/UX Redesign Exploration",
"canonical_tokens": {
"night": {
"surface_canvas": "#0B1326",
"surface_card_primary": "#111C38",
"surface_card_secondary": "#17223B",
"surface_card_lowest": "#060E20",
"surface_border": "#1E293B",
"surface_border_subtle": "#24324D",
"text_primary": "#DAE2FD",
"text_muted": "#869397",
"brand_cyan": "#06B6D4",
"brand_cyan_bright": "#22D3EE",
"brand_cyan_container": "#00424F",
"rec_static_green": "#10B981",
"rec_static_green_bg": "#064E3B",
"caution_amber": "#F59E0B",
"danger_red": "#EF4444"
},
"day": {
"surface_canvas": "#F8FAFC",
"surface_card_primary": "#FFFFFF",
"surface_card_secondary": "#F1F5F9",
"surface_card_lowest": "#E2E8F0",
"surface_border": "#CBD5E1",
"surface_border_subtle": "#E2E8F0",
"text_primary": "#0F172A",
"text_muted": "#64748B",
"brand_cyan": "#0284C7",
"brand_cyan_bright": "#0EA5E9",
"brand_cyan_container": "#E0F2FE",
"rec_static_green": "#10B981",
"rec_static_green_bg": "#D1FAE5",
"caution_amber": "#D97706",
"danger_red": "#DC2626"
}
},
"screens": [
{
"index": "01",
"name": "Riding Dashboard — Day",
"screen_id": "fbbd68740d11447e8035cd0776133053",
"composite_frame": "Left Frame (Default Scale)",
"viewport": { "width": 390, "height": 844 },
"tier": "Tier 1: Verified UI",
"reference_files": [
"references/screen_day_dashboard.png",
"references/screen_day_dashboard.html"
]
},
{
"index": "01b",
"name": "Riding Dashboard — Day (200% Text Scale Companion)",
"screen_id": "fbbd68740d11447e8035cd0776133053",
"composite_frame": "Right Frame (200% Accessibility Scale)",
"viewport": { "width": 390, "height": 844 },
"tier": "Accessibility Target (Unverified)",
"reference_files": [
"references/screen_day_dashboard.png",
"references/screen_day_dashboard.html"
]
},
{
"index": "02",
"name": "Riding Dashboard — Night",
"screen_id": "8f336a84e92c44d8aea012b2c6703b55",
"composite_frame": "Left Frame (Default Scale)",
"viewport": { "width": 390, "height": 844 },
"tier": "Tier 1: Verified UI",
"reference_files": [
"references/screen_night_dashboard.png",
"references/screen_night_dashboard.html"
]
},
{
"index": "02b",
"name": "Riding Dashboard — Night (200% Text Scale Companion)",
"screen_id": "8f336a84e92c44d8aea012b2c6703b55",
"composite_frame": "Right Frame (200% Accessibility Scale)",
"viewport": { "width": 390, "height": 844 },
"tier": "Accessibility Target (Unverified)",
"reference_files": [
"references/screen_night_dashboard.png",
"references/screen_night_dashboard.html"
]
},
{
"index": "03",
"name": "Authenticated Group Ride Hub",
"screen_id": "a661c1c9c82843a2bcb6b84d6d7855e1",
"composite_frame": "Left Frame (Default Scale)",
"viewport": { "width": 390, "height": 844 },
"tier": "Tier 1: Verified UI",
"reference_files": [
"references/screen_group_ride_hub.png",
"references/screen_group_ride_hub.html"
]
},
{
"index": "03b",
"name": "Group Ride Hub (200% Text Scale Companion)",
"screen_id": "a661c1c9c82843a2bcb6b84d6d7855e1",
"composite_frame": "Right Frame (200% Accessibility Scale)",
"viewport": { "width": 390, "height": 844 },
"tier": "Accessibility Target (Unverified)",
"reference_files": [
"references/screen_group_ride_hub.png",
"references/screen_group_ride_hub.html"
]
},
{
"index": "04",
"name": "Ride History & Archive",
"screen_id": "934e8a33a5fa44009d40c292aacc630e",
"composite_frame": "Viewport 1 (Left Frame)",
"viewport": { "width": 390, "height": 844 },
"tier": "Tier 1 (UI) + Tier 2 (Sync UX)",
"reference_files": [
"references/screen_replay_hud.png",
"references/screen_replay_hud.html"
]
},
{
"index": "05",
"name": "Ride Replay HUD",
"screen_id": "934e8a33a5fa44009d40c292aacc630e",
"composite_frame": "Viewport 2 (Right Frame)",
"viewport": { "width": 390, "height": 844 },
"tier": "Tier 1 (UI) + Tier 2 (Proposed HUD)",
"reference_files": [
"references/screen_replay_hud.png",
"references/screen_replay_hud.html"
]
},
{
"index": "06",
"name": "Community Feed",
"screen_id": "17a5177fab12466da2dea6639c8fd88f",
"composite_frame": "Frame 1",
"viewport": { "width": 390, "height": 844 },
"tier": "Tier 1 (UI) + Tier 2 (Moderation)",
"reference_files": [
"references/screen_consolidated_flows.png",
"references/screen_consolidated_flows.html"
]
},
{
"index": "07",
"name": "Create Group Ride — Step 1: Details & Staging",
"screen_id": "17a5177fab12466da2dea6639c8fd88f",
"composite_frame": "Frame 2a",
"viewport": { "width": 390, "height": 844 },
"tier": "Tier 2: Proposed Feature",
"reference_files": [
"references/screen_consolidated_flows.png",
"references/screen_consolidated_flows.html"
]
},
{
"index": "08",
"name": "Route Calculation Engine — Loading & Failure",
"screen_id": "17a5177fab12466da2dea6639c8fd88f",
"composite_frame": "Frame 2b",
"viewport": { "width": 390, "height": 844 },
"tier": "Tier 2 / Tier 3 (Engine UX)",
"reference_files": [
"references/screen_consolidated_flows.png",
"references/screen_consolidated_flows.html"
]
},
{
"index": "09",
"name": "Create Group Ride — Step 2: Review & Access",
"screen_id": "17a5177fab12466da2dea6639c8fd88f",
"composite_frame": "Frame 2c",
"viewport": { "width": 390, "height": 844 },
"tier": "Tier 2: Proposed Feature",
"reference_files": [
"references/screen_consolidated_flows.png",
"references/screen_consolidated_flows.html"
]
},
{
"index": "10",
"name": "Ride Created — Join Code & Dynamic QR",
"screen_id": "17a5177fab12466da2dea6639c8fd88f",
"composite_frame": "Frame 2d",
"viewport": { "width": 390, "height": 844 },
"tier": "Tier 2: Proposed Feature",
"reference_files": [
"references/screen_consolidated_flows.png",
"references/screen_consolidated_flows.html"
]
},
{
"index": "11",
"name": "Account, Security & Sensor Connections",
"screen_id": "fd0e6370af424ff4becfdaecda7f29de",
"composite_frame": "Frame 3",
"viewport": { "width": 390, "height": 844 },
"tier": "Tier 1 (UI) + Tier 3 (Backend/Sensors)",
"reference_files": [
"references/screen_remaining_flows.png",
"references/screen_remaining_flows.html"
]
},
{
"index": "12",
"name": "Share Ride — Recipient Selection Dialog",
"screen_id": "17a5177fab12466da2dea6639c8fd88f",
"composite_frame": "Modal 1 (Overlay)",
"viewport": { "width": 390, "height": 844 },
"tier": "Tier 2: Proposed Feature",
"reference_files": [
"references/screen_consolidated_flows.png",
"references/screen_consolidated_flows.html"
]
},
{
"index": "13",
"name": "Delete Ride — Explicit Scope Selection",
"screen_id": "17a5177fab12466da2dea6639c8fd88f",
"composite_frame": "Modal 2 (Overlay)",
"viewport": { "width": 390, "height": 844 },
"tier": "Tier 2: Proposed Feature",
"reference_files": [
"references/screen_consolidated_flows.png",
"references/screen_consolidated_flows.html"
]
},
{
"index": "14",
"name": "Supporting System States Matrix",
"screen_id": "17a5177fab12466da2dea6639c8fd88f",
"composite_frame": "System States Grid (5 Cards)",
"viewport": { "width": 1280, "height": 800 },
"tier": "Tier 2: System UX",
"reference_files": [
"references/screen_consolidated_flows.png",
"references/screen_consolidated_flows.html"
]
}
],
"inconsistencies_resolved": {
"replay_telemetry_retrieval_wording": "Loading/status wording must follow actual database/bundle retrieval rather than speculative '100% buffered in-memory' label.",
"account_screen_assumptions": "Discarded unsupported 100Hz IMU claims and Pro restrictions on multi-sensor telemetry; conforms to frozen 20Hz pipeline and current free/pro model.",
"ride_visibility_and_join_policies": "Preserved all existing membership, host approval, unlisted/public/club-only/invite-only policies.",
"night_surface_canvas_reconciliation": "Reconciled to canonical #0B1326.",
"accessibility_and_runtime_claims": "Treated as unverified targets until tested on device/emulator."
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@ -0,0 +1,994 @@
<!DOCTYPE html>
<html class="dark" lang="en">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>MotorSignal - Remaining Flows & Consolidated Architecture Handoff</title>
<!-- Material Symbols Outlined -->
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
<!-- JetBrains Mono & Inter Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet"/>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
"background": "#0b1326",
"surface": "#0b1326",
"surface-container-lowest": "#060e20",
"surface-container-low": "#111c38",
"surface-container": "#17223b",
"surface-container-high": "#222d4a",
"surface-container-highest": "#2d3859",
"outline": "#869397",
"outline-variant": "#2a3652",
"primary": "#4cd7f6",
"primary-container": "#06b6d4",
"on-primary": "#003640",
"on-primary-container": "#002028",
"on-surface": "#dae2fd",
"on-surface-variant": "#94a3b8",
"tertiary": "#4edea3",
"warning": "#f59e0b",
"error": "#ef4444",
"error-container": "#450a0a"
},
fontFamily: {
sans: ["Inter", "-apple-system", "sans-serif"],
mono: ["JetBrains Mono", "monospace"]
}
}
}
}
</script>
<style>
.material-symbols-outlined {
font-family: 'Material Symbols Outlined';
font-weight: normal;
font-style: normal;
font-size: 20px;
line-height: 1;
display: inline-block;
}
.viewport-scroll::-webkit-scrollbar {
width: 4px;
}
.viewport-scroll::-webkit-scrollbar-track {
background: #0b1326;
}
.viewport-scroll::-webkit-scrollbar-thumb {
background: #2a3652;
border-radius: 4px;
}
</style>
</head>
<body class="bg-background text-on-surface antialiased min-h-screen py-10 px-4 sm:px-8 selection:bg-primary-container selection:text-on-primary-container">
<!-- Document Header & Specification Bar -->
<header class="max-w-[1440px] mx-auto mb-10 pb-6 border-b border-outline-variant">
<div class="flex flex-wrap items-center justify-between gap-4">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-xl bg-surface-container-low border border-outline-variant flex items-center justify-center text-primary-container shadow-md">
<span class="material-symbols-outlined" style="font-size: 24px;">dataset</span>
</div>
<div>
<div class="flex items-center gap-2">
<span class="text-xs uppercase tracking-wider font-mono font-bold text-primary px-2 py-0.5 rounded bg-surface-container-high border border-outline-variant">Prototype Spec v2.8</span>
<span class="text-xs text-on-surface-variant">MotorSignal UI Engineering Package</span>
<span class="text-xs px-2 py-0.5 rounded bg-warning/10 text-warning border border-warning/30 font-medium">Design Mockups — Non-Verified Backend</span>
</div>
<h1 class="text-2xl sm:text-3xl font-bold tracking-tight text-white mt-1">Consolidated Mobile Flows & Architecture Handoff</h1>
</div>
</div>
<div class="flex flex-wrap items-center gap-2 text-xs font-mono">
<span class="px-3 py-1.5 rounded-lg bg-surface-container-low border border-outline-variant text-on-surface">Target Frame: 390 × 844 CSS px</span>
<span class="px-3 py-1.5 rounded-lg bg-surface-container-low border border-outline-variant text-tertiary">Touch Target Minimum: ≥48×48dp</span>
</div>
</div>
</header>
<main class="max-w-[1440px] mx-auto space-y-16">
<!-- ======================================================== -->
<!-- SECTION 1: THREE CORE REMAINING SCREEN VIEWPORTS -->
<!-- ======================================================== -->
<div>
<div class="flex items-center justify-between mb-6">
<div>
<h2 class="text-xl font-bold text-white flex items-center gap-2">
<span class="w-3 h-3 rounded-full bg-primary-container"></span>
Core Remaining Screen Flows (Default Viewports)
</h2>
<p class="text-sm text-on-surface-variant mt-0.5">High-fidelity 390×844 prototype viewports with persistent 6-destination navigation and explicit sample-data notation.</p>
</div>
<div class="text-xs font-mono text-on-surface-variant bg-surface-container-low px-3 py-1.5 rounded-lg border border-outline-variant">
Frames 1–3 of 3
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-8 justify-items-center">
<!-- ========================================== -->
<!-- VIEWPORT 1: COMMUNITY FEED & MODERATION -->
<!-- ========================================== -->
<div class="flex flex-col items-center w-full max-w-[390px]">
<div class="w-full flex items-center justify-between mb-2 px-1 text-xs">
<span class="font-bold text-white flex items-center gap-1.5">
<span class="w-2 h-2 rounded-full bg-primary"></span> 1. Community Feed & Moderation
</span>
<span class="font-mono text-on-surface-variant">Social &gt; Feed</span>
</div>
<!-- Phone Mockup Frame (390 x 844) -->
<div class="relative w-[390px] h-[844px] bg-background border-[5px] border-surface-container-highest rounded-[42px] shadow-2xl overflow-hidden flex flex-col justify-between ring-1 ring-outline-variant/50">
<!-- iOS Simulated Top Safe Area / Status Bar -->
<div class="h-11 w-full bg-background flex items-center justify-between px-6 shrink-0 select-none z-30">
<span class="text-xs font-semibold text-white">09:41</span>
<div class="w-24 h-4 bg-surface-container-lowest rounded-full flex items-center justify-center">
<span class="w-2 h-2 rounded-full bg-surface-container-high mr-2"></span>
<span class="w-2.5 h-2.5 rounded-full bg-primary-container/40"></span>
</div>
<div class="flex items-center gap-1.5 text-xs text-white">
<span class="material-symbols-outlined" style="font-size: 14px;">signal_cellular_alt</span>
<span class="material-symbols-outlined" style="font-size: 14px;">wifi</span>
<span class="material-symbols-outlined" style="font-size: 14px;">battery_full</span>
</div>
</div>
<!-- Feed Top App Bar -->
<div class="px-4 pt-2 pb-3 bg-surface-container-low/95 backdrop-blur border-b border-outline-variant shrink-0 z-20">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="text-base font-bold text-white tracking-tight">Social Network</span>
<span class="text-[10px] font-mono uppercase px-1.5 py-0.5 rounded bg-surface-container text-on-surface-variant border border-outline-variant">Sample Data</span>
</div>
<div class="flex items-center gap-1">
<!-- Feed / Hub Switch (Min 48px target) -->
<div class="flex h-11 p-1 bg-surface-container-lowest rounded-xl border border-outline-variant">
<button class="px-3.5 rounded-lg bg-surface-container-high text-primary font-bold text-xs flex items-center justify-center">Feed</button>
<button class="px-3.5 rounded-lg text-on-surface-variant font-medium text-xs flex items-center justify-center hover:text-white">Hub</button>
</div>
</div>
</div>
<!-- Feed Filter Tabs (Min 48px height) -->
<div class="mt-2.5 flex items-center gap-2 overflow-x-auto pb-0.5 no-scrollbar text-xs">
<button class="h-10 px-3.5 rounded-full bg-surface-container-high border border-primary text-primary font-bold flex items-center shrink-0">All Posts</button>
<button class="h-10 px-3.5 rounded-full bg-surface-container border border-outline-variant text-on-surface-variant font-medium flex items-center shrink-0 hover:text-white">Clubs</button>
<button class="h-10 px-3.5 rounded-full bg-surface-container border border-outline-variant text-on-surface-variant font-medium flex items-center shrink-0 hover:text-white">Nearby</button>
<button class="h-10 px-3.5 rounded-full bg-surface-container border border-outline-variant text-on-surface-variant font-medium flex items-center shrink-0 hover:text-white">My Pack</button>
</div>
</div>
<!-- Feed Scrollable Content -->
<div class="flex-1 overflow-y-auto viewport-scroll p-4 space-y-4 bg-background">
<!-- Feed Card 1: Normal Telemetry Session Post -->
<article class="p-3.5 rounded-2xl bg-surface-container-low border border-outline-variant space-y-3">
<!-- Author Header with Overflow Trigger -->
<div class="flex items-center justify-between">
<div class="flex items-center gap-2.5">
<div class="w-9 h-9 rounded-full bg-primary-container/20 border border-primary/40 flex items-center justify-center text-primary font-bold text-xs">AM</div>
<div>
<div class="flex items-center gap-1.5">
<span class="text-xs font-bold text-white">@ApexMarcus</span>
<span class="text-[10px] font-mono px-1.5 rounded bg-primary-container/20 text-primary border border-primary/30">PRO</span>
</div>
<span class="text-[11px] text-on-surface-variant">Bay Area Twisty Club • 2h ago</span>
</div>
</div>
<!-- 48×48px Touch Menu Action -->
<button class="w-12 h-12 rounded-xl flex items-center justify-center text-on-surface-variant hover:text-white active:bg-surface-container transition-colors" title="Post Options">
<span class="material-symbols-outlined" style="font-size: 20px;">more_vert</span>
</button>
</div>
<!-- Post Content -->
<div class="space-y-1">
<h3 class="text-xs font-bold text-white">Sunday Skyline &amp; Pescadero Carve</h3>
<p class="text-[11px] text-on-surface-variant leading-snug">Clean coastal run before fog rolled into Pescadero. 2024 Ducati Streetfighter V4 feeling dialed on new rubber.</p>
</div>
<!-- Compact Telemetry Strip (Sample Data) -->
<div class="grid grid-cols-3 gap-1.5 p-2 rounded-xl bg-surface-container-lowest border border-outline-variant/60 font-mono text-center">
<div>
<span class="text-[9px] text-on-surface-variant uppercase">Distance</span>
<p class="text-xs font-bold text-white">68.4 <span class="text-[9px] text-on-surface-variant">MI</span></p>
</div>
<div>
<span class="text-[9px] text-on-surface-variant uppercase">Peak Lean</span>
<p class="text-xs font-bold text-primary">51° <span class="text-[9px] text-on-surface-variant">R</span></p>
</div>
<div>
<span class="text-[9px] text-on-surface-variant uppercase">Duration</span>
<p class="text-xs font-bold text-white">1h 14m</p>
</div>
</div>
<!-- Action Bar (Min 48px hit areas) -->
<div class="flex items-center justify-between pt-1 border-t border-outline-variant/40">
<div class="flex items-center gap-1">
<button class="h-11 px-3 rounded-lg text-on-surface-variant hover:text-white flex items-center gap-1.5 text-xs">
<span class="material-symbols-outlined text-primary" style="font-size: 18px;">favorite</span>
<span class="font-mono text-[11px]">24</span>
</button>
<button class="h-11 px-3 rounded-lg text-on-surface-variant hover:text-white flex items-center gap-1.5 text-xs">
<span class="material-symbols-outlined" style="font-size: 18px;">chat_bubble</span>
<span class="font-mono text-[11px]">7</span>
</button>
</div>
<button class="h-11 px-3 rounded-lg bg-surface-container text-primary font-bold text-xs flex items-center gap-1 border border-outline-variant">
<span class="material-symbols-outlined" style="font-size: 16px;">visibility</span>
Inspect Run
</button>
</div>
</article>
<!-- Feed Card 2: Explicit Moderation & Content Warning State -->
<article class="p-3.5 rounded-2xl bg-surface-container-low border border-warning/40 space-y-3 relative overflow-hidden">
<div class="absolute left-0 top-0 bottom-0 w-1 bg-warning"></div>
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="material-symbols-outlined text-warning" style="font-size: 18px;">flag</span>
<span class="text-xs font-bold text-warning">Flagged by Community Review</span>
</div>
<!-- Overflow Menu -->
<button class="w-12 h-12 rounded-xl flex items-center justify-center text-on-surface-variant hover:text-white" title="Moderation Details">
<span class="material-symbols-outlined" style="font-size: 20px;">info</span>
</button>
</div>
<div class="p-3 rounded-xl bg-surface-container-lowest/80 border border-outline-variant space-y-2">
<div class="flex items-start gap-2">
<span class="material-symbols-outlined text-error mt-0.5" style="font-size: 18px;">speed</span>
<div>
<p class="text-xs font-bold text-white">Aggressive Speed Content Hidden</p>
<p class="text-[11px] text-on-surface-variant mt-0.5 leading-snug">Public highway speed exceeded community safety guideline (+40% above zone speed ceiling). GPS telemetry map blurred.</p>
</div>
</div>
<!-- Moderation Disclosure Actions (≥48px targets) -->
<div class="flex items-center gap-2 pt-1">
<button class="h-11 px-4 rounded-lg bg-surface-container text-white text-xs font-bold border border-outline-variant hover:border-primary flex items-center justify-center">
Reveal Post
</button>
<button class="h-11 px-4 rounded-lg text-error hover:bg-error-container/30 text-xs font-semibold flex items-center gap-1">
<span class="material-symbols-outlined" style="font-size: 16px;">report</span>
Report Violation
</button>
</div>
</div>
</article>
<!-- Feed Card 3: Proposed Feature Badge Annotation -->
<div class="p-3 rounded-xl bg-surface-container border border-dashed border-outline-variant text-center space-y-1">
<span class="text-[10px] font-mono text-tertiary uppercase font-bold tracking-wider">Proposed Flow: Pack Live Beacon</span>
<p class="text-xs text-white">Broadcast real-time pack proximity during scheduled weekend group rides.</p>
<span class="text-[10px] text-on-surface-variant italic">Requires field connectivity testing before production release.</span>
</div>
</div>
<!-- Global Bottom Navigation Bar (Persistent 6 Destinations, min 48px hit areas) -->
<nav class="h-[68px] bg-surface-container-lowest border-t border-outline-variant px-2 flex items-center justify-around shrink-0 select-none z-30">
<button class="flex flex-col items-center justify-center w-12 h-12 text-on-surface-variant hover:text-white">
<span class="material-symbols-outlined" style="font-size: 20px;">speed</span>
<span class="text-[10px] font-bold mt-0.5">Dash</span>
</button>
<button class="flex flex-col items-center justify-center w-12 h-12 text-on-surface-variant hover:text-white">
<span class="material-symbols-outlined" style="font-size: 20px;">explore</span>
<span class="text-[10px] font-bold mt-0.5">Map</span>
</button>
<button class="flex flex-col items-center justify-center w-12 h-12 text-on-surface-variant hover:text-white">
<span class="material-symbols-outlined" style="font-size: 20px;">analytics</span>
<span class="text-[10px] font-bold mt-0.5">Activity</span>
</button>
<button class="flex flex-col items-center justify-center w-12 h-12 text-on-surface-variant hover:text-white">
<span class="material-symbols-outlined" style="font-size: 20px;">two_wheeler</span>
<span class="text-[10px] font-bold mt-0.5">Garage</span>
</button>
<button class="flex flex-col items-center justify-center w-12 h-12 text-primary">
<span class="material-symbols-outlined" style="font-variation-settings: 'FILL' 1; font-size: 20px;">groups</span>
<span class="text-[10px] font-bold mt-0.5">Social</span>
</button>
<button class="flex flex-col items-center justify-center w-12 h-12 text-on-surface-variant hover:text-white">
<span class="material-symbols-outlined" style="font-size: 20px;">settings</span>
<span class="text-[10px] font-bold mt-0.5">Settings</span>
</button>
</nav>
<!-- Safe Area Platform Bottom Indicator -->
<div class="h-3 w-full bg-surface-container-lowest flex justify-center pb-1">
<div class="w-32 h-1 bg-outline-variant rounded-full"></div>
</div>
</div>
</div>
<!-- ========================================== -->
<!-- VIEWPORT 2: TWO-STEP CREATE GROUP RIDE -->
<!-- ========================================== -->
<div class="flex flex-col items-center w-full max-w-[390px]">
<div class="w-full flex items-center justify-between mb-2 px-1 text-xs">
<span class="font-bold text-white flex items-center gap-1.5">
<span class="w-2 h-2 rounded-full bg-primary-container"></span> 2. Two-Step Create Group Ride
</span>
<span class="font-mono text-on-surface-variant">Step 1 of 2</span>
</div>
<!-- Phone Mockup Frame (390 x 844) -->
<div class="relative w-[390px] h-[844px] bg-background border-[5px] border-surface-container-highest rounded-[42px] shadow-2xl overflow-hidden flex flex-col justify-between ring-1 ring-outline-variant/50">
<!-- Top Safe Area Bar -->
<div class="h-11 w-full bg-background flex items-center justify-between px-6 shrink-0 select-none z-30">
<span class="text-xs font-semibold text-white">09:42</span>
<div class="w-24 h-4 bg-surface-container-lowest rounded-full flex items-center justify-center">
<span class="w-2 h-2 rounded-full bg-surface-container-high mr-2"></span>
<span class="w-2.5 h-2.5 rounded-full bg-primary-container/40"></span>
</div>
<div class="flex items-center gap-1.5 text-xs text-white">
<span class="material-symbols-outlined" style="font-size: 14px;">signal_cellular_alt</span>
<span class="material-symbols-outlined" style="font-size: 14px;">wifi</span>
<span class="material-symbols-outlined" style="font-size: 14px;">battery_full</span>
</div>
</div>
<!-- Header / Step Indicator App Bar -->
<div class="px-4 py-3 bg-surface-container-low/95 backdrop-blur border-b border-outline-variant shrink-0 z-20">
<div class="flex items-center justify-between">
<button class="w-12 h-12 -ml-2 rounded-xl flex items-center justify-center text-on-surface-variant hover:text-white" title="Cancel Creation">
<span class="material-symbols-outlined" style="font-size: 22px;">close</span>
</button>
<div class="text-center">
<h2 class="text-sm font-bold text-white">Create Group Ride</h2>
<span class="text-[10px] font-mono text-primary">STEP 1: LOGISTICS &amp; ROUTE</span>
</div>
<!-- Step Counter Badge -->
<span class="text-xs font-mono font-bold px-2 py-1 rounded bg-surface-container-high border border-outline-variant text-on-surface">1/2</span>
</div>
<!-- Visual Stepper Progress Bar -->
<div class="mt-2.5 flex items-center gap-1.5">
<div class="h-1.5 flex-1 bg-primary rounded-full"></div>
<div class="h-1.5 flex-1 bg-surface-container-highest rounded-full"></div>
</div>
</div>
<!-- Step 1 Form Content (Scrollable) -->
<div class="flex-1 overflow-y-auto viewport-scroll p-4 space-y-4 bg-background">
<!-- Ride Title Input -->
<div class="space-y-1.5">
<label class="block text-xs font-bold text-white">Ride Title <span class="text-primary">*</span></label>
<input type="text" value="Sunday Skyline & Pescadero Carve" class="w-full h-12 px-3.5 rounded-xl bg-surface-container-low border border-outline-variant text-xs text-white focus:border-primary focus:ring-0 font-medium"/>
</div>
<!-- Route Selection Card with Static Vector Map Preview -->
<div class="space-y-1.5">
<div class="flex items-center justify-between text-xs">
<label class="font-bold text-white">Select Route &amp; Distance</label>
<button class="text-[11px] font-bold text-primary hover:underline">Choose from GPX</button>
</div>
<div class="rounded-2xl border border-outline-variant bg-surface-container-low overflow-hidden">
<!-- Map Simulation Canvas -->
<div class="h-28 bg-[#091426] relative flex items-center justify-center">
<svg class="w-full h-full opacity-60" viewBox="0 0 320 112" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 90 Q 60 40, 130 65 T 250 20 T 310 50" stroke="#06b6d4" stroke-width="4" stroke-linecap="round" fill="none"/>
<circle cx="10" cy="90" r="5" fill="#4edea3"/>
<circle cx="310" cy="50" r="5" fill="#ef4444"/>
</svg>
<div class="absolute bottom-2 left-2 px-2 py-0.5 rounded bg-surface-container-lowest/90 border border-outline-variant text-[10px] font-mono text-white">
Skyline Blvd &rarr; Hwy 84
</div>
<div class="absolute top-2 right-2 px-2 py-0.5 rounded bg-surface-container-lowest/90 border border-outline-variant text-[10px] font-mono text-primary font-bold">
68.4 MI Calculated
</div>
</div>
<div class="p-3 flex items-center justify-between text-xs">
<div>
<p class="font-bold text-white">Pescadero Coastal Loop</p>
<p class="text-[11px] text-on-surface-variant font-mono">Elevation: +2,140 ft • Est. 1h 45m</p>
</div>
<button class="h-10 px-3 rounded-lg bg-surface-container border border-outline-variant text-xs text-white font-medium hover:border-primary">Change</button>
</div>
</div>
</div>
<!-- Date & Staging Location (Decoupled, No Collisions) -->
<div class="space-y-3">
<div class="space-y-1.5">
<label class="block text-xs font-bold text-white">Date &amp; Kickstands Up (KSU)</label>
<div class="grid grid-cols-2 gap-2">
<input type="text" value="Sun, Oct 12" class="h-12 px-3 rounded-xl bg-surface-container-low border border-outline-variant text-xs text-white font-mono"/>
<input type="text" value="09:00 AM KSU" class="h-12 px-3 rounded-xl bg-surface-container-low border border-outline-variant text-xs text-white font-mono"/>
</div>
</div>
<div class="space-y-1.5">
<label class="block text-xs font-bold text-white">Staging Location</label>
<div class="relative">
<input type="text" value="Alice's Restaurant, Woodside CA" class="w-full h-12 pl-10 pr-3 rounded-xl bg-surface-container-low border border-outline-variant text-xs text-white font-medium"/>
<span class="material-symbols-outlined absolute left-3 top-3 text-on-surface-variant" style="font-size: 18px;">pin_drop</span>
</div>
</div>
</div>
<!-- Pace & Safety Guidelines Segmented Selector -->
<div class="space-y-1.5">
<label class="block text-xs font-bold text-white">Target Pace Profile</label>
<div class="grid grid-cols-3 gap-2">
<button class="h-12 rounded-xl bg-surface-container border border-outline-variant text-xs text-on-surface-variant font-medium hover:text-white">Casual</button>
<button class="h-12 rounded-xl bg-surface-container-high border-2 border-primary text-primary text-xs font-bold">Spirited</button>
<button class="h-12 rounded-xl bg-surface-container border border-outline-variant text-xs text-on-surface-variant font-medium hover:text-white">Track Pace</button>
</div>
</div>
<!-- Step 2 Preview Teaser: Join Code & Passkey Notice -->
<div class="p-3 rounded-xl bg-surface-container-lowest border border-outline-variant flex items-center gap-3">
<div class="w-10 h-10 rounded-lg bg-surface-container-high flex items-center justify-center text-primary shrink-0">
<span class="material-symbols-outlined" style="font-size: 20px;">qr_code_2</span>
</div>
<div>
<p class="text-xs font-bold text-white">Next Step: Join Code &amp; Invites</p>
<p class="text-[11px] text-on-surface-variant">Step 2 generates your pack join code (#SKY92) and shareable QR code.</p>
</div>
</div>
</div>
<!-- Bottom Action Suite (Pinned, Min 56px height) -->
<div class="p-4 bg-surface-container-low border-t border-outline-variant shrink-0 z-30">
<button class="w-full h-14 rounded-2xl bg-primary-container text-on-primary-container font-bold text-sm flex items-center justify-center gap-2 shadow-lg active:scale-[0.99] transition-transform">
Continue to Step 2: Pack Access
<span class="material-symbols-outlined" style="font-size: 18px;">arrow_forward</span>
</button>
</div>
<!-- iOS Safe Area Indicator -->
<div class="h-3 w-full bg-surface-container-low flex justify-center pb-1">
<div class="w-32 h-1 bg-outline-variant rounded-full"></div>
</div>
</div>
</div>
<!-- ========================================== -->
<!-- VIEWPORT 3: ACCOUNT, SECURITY & SENSORS -->
<!-- ========================================== -->
<div class="flex flex-col items-center w-full max-w-[390px]">
<div class="w-full flex items-center justify-between mb-2 px-1 text-xs">
<span class="font-bold text-white flex items-center gap-1.5">
<span class="w-2 h-2 rounded-full bg-tertiary"></span> 3. Account, Security &amp; Sensors
</span>
<span class="font-mono text-on-surface-variant">Truthful States</span>
</div>
<!-- Phone Mockup Frame (390 x 844) -->
<div class="relative w-[390px] h-[844px] bg-background border-[5px] border-surface-container-highest rounded-[42px] shadow-2xl overflow-hidden flex flex-col justify-between ring-1 ring-outline-variant/50">
<!-- Top Safe Area Bar -->
<div class="h-11 w-full bg-background flex items-center justify-between px-6 shrink-0 select-none z-30">
<span class="text-xs font-semibold text-white">09:43</span>
<div class="w-24 h-4 bg-surface-container-lowest rounded-full flex items-center justify-center">
<span class="w-2 h-2 rounded-full bg-surface-container-high mr-2"></span>
<span class="w-2.5 h-2.5 rounded-full bg-primary-container/40"></span>
</div>
<div class="flex items-center gap-1.5 text-xs text-white">
<span class="material-symbols-outlined" style="font-size: 14px;">signal_cellular_alt</span>
<span class="material-symbols-outlined" style="font-size: 14px;">wifi</span>
<span class="material-symbols-outlined" style="font-size: 14px;">battery_full</span>
</div>
</div>
<!-- Settings Top App Bar -->
<div class="px-4 py-3 bg-surface-container-low/95 backdrop-blur border-b border-outline-variant shrink-0 z-20">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="text-base font-bold text-white tracking-tight">Account &amp; System</span>
</div>
<span class="text-[11px] font-mono text-on-surface-variant">Build 2.8.4</span>
</div>
</div>
<!-- Settings Scrollable Content Area -->
<div class="flex-1 overflow-y-auto viewport-scroll p-4 space-y-4 bg-background">
<!-- User Identity & Entitlement Card -->
<div class="p-3.5 rounded-2xl bg-surface-container-low border border-outline-variant space-y-3">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-12 h-12 rounded-full bg-surface-container-high border border-outline-variant flex items-center justify-center text-primary font-bold text-base">B</div>
<div>
<h3 class="text-xs font-bold text-white">Bill Ponders</h3>
<p class="text-[11px] text-on-surface-variant font-mono">bponders12@gmail.com</p>
</div>
</div>
<button class="h-11 px-3.5 rounded-xl bg-surface-container border border-outline-variant text-xs text-white font-medium hover:border-primary">
Manage
</button>
</div>
<!-- Explicit Free Tier Breakdown (No unverified claims) -->
<div class="p-2.5 rounded-xl bg-surface-container-lowest border border-outline-variant/60 space-y-1">
<div class="flex items-center justify-between text-xs">
<span class="font-bold text-white">Tier: MotorSignal Free</span>
<span class="text-[10px] font-mono text-on-surface-variant">Active</span>
</div>
<p class="text-[11px] text-on-surface-variant leading-snug">Standard GPS logging, local ride archives, and public feed access. Multi-sensor telemetry sync requires Pro subscription.</p>
</div>
</div>
<!-- Hardware & Sensors (Truthful States, Internal vs External) -->
<div class="space-y-2">
<div class="flex items-center justify-between text-xs px-1">
<span class="font-bold text-white uppercase tracking-wider text-[11px]">Sensor Connections</span>
<span class="text-[10px] font-mono text-tertiary">1 Active • 1 Disconnected</span>
</div>
<!-- Internal Sensor Card: Active -->
<div class="p-3 rounded-xl bg-surface-container-low border border-outline-variant flex items-center justify-between">
<div class="flex items-center gap-2.5">
<div class="w-9 h-9 rounded-lg bg-surface-container-high flex items-center justify-center text-tertiary">
<span class="material-symbols-outlined" style="font-size: 18px;">smartphone</span>
</div>
<div>
<p class="text-xs font-bold text-white">Internal Phone IMU</p>
<p class="text-[11px] text-tertiary font-mono">Active (100Hz Gyro / Accel)</p>
</div>
</div>
<span class="text-xs text-tertiary font-bold">Online</span>
</div>
<!-- External BLE Sensor: Truthful Disconnected State -->
<div class="p-3 rounded-xl bg-surface-container-low border border-outline-variant flex items-center justify-between">
<div class="flex items-center gap-2.5">
<div class="w-9 h-9 rounded-lg bg-surface-container-high flex items-center justify-center text-on-surface-variant">
<span class="material-symbols-outlined" style="font-size: 18px;">bluetooth_searching</span>
</div>
<div>
<p class="text-xs font-bold text-white">External Bike IMU Sensor</p>
<p class="text-[11px] text-on-surface-variant font-mono">Disconnected • No Paired BLE</p>
</div>
</div>
<button class="h-11 px-3.5 rounded-lg bg-surface-container border border-outline-variant text-primary text-xs font-bold hover:border-primary">
Pair Sensor
</button>
</div>
</div>
<!-- Security & Two-Factor Authentication (Truthfully Unconfigured) -->
<div class="space-y-2">
<span class="font-bold text-white uppercase tracking-wider text-[11px] px-1">Account Security</span>
<div class="p-3.5 rounded-xl bg-surface-container-low border border-outline-variant space-y-2.5">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2.5">
<div class="w-9 h-9 rounded-lg bg-surface-container-high flex items-center justify-center text-warning">
<span class="material-symbols-outlined" style="font-size: 18px;">security</span>
</div>
<div>
<p class="text-xs font-bold text-white">Two-Factor Authentication (2FA)</p>
<p class="text-[11px] text-on-surface-variant font-mono">Status: Disabled (Not Configured)</p>
</div>
</div>
<button class="h-11 px-3.5 rounded-lg bg-surface-container-high text-primary border border-outline-variant text-xs font-bold hover:border-primary">
Set Up
</button>
</div>
<p class="text-[11px] text-on-surface-variant leading-snug">Protect cloud session telemetry and garage specs with TOTP authenticator or hardware passkeys.</p>
</div>
</div>
<!-- Cloud Sync & Data Management (Scope Notice) -->
<div class="p-3 rounded-xl bg-surface-container-low border border-outline-variant space-y-2">
<div class="flex items-center justify-between text-xs">
<span class="font-bold text-white">Telemetry Sync Scope</span>
<span class="text-[10px] font-mono text-warning">Proposed Protocol</span>
</div>
<p class="text-[11px] text-on-surface-variant leading-snug">
Automatic synchronization is a proposed background service pending device battery and bandwidth testing. Deletions distinguish between local cache vs. cloud archive purge.
</p>
</div>
</div>
<!-- Bottom Navigation Bar (Persistent 6 Destinations) -->
<nav class="h-[68px] bg-surface-container-lowest border-t border-outline-variant px-2 flex items-center justify-around shrink-0 select-none z-30">
<button class="flex flex-col items-center justify-center w-12 h-12 text-on-surface-variant hover:text-white">
<span class="material-symbols-outlined" style="font-size: 20px;">speed</span>
<span class="text-[10px] font-bold mt-0.5">Dash</span>
</button>
<button class="flex flex-col items-center justify-center w-12 h-12 text-on-surface-variant hover:text-white">
<span class="material-symbols-outlined" style="font-size: 20px;">explore</span>
<span class="text-[10px] font-bold mt-0.5">Map</span>
</button>
<button class="flex flex-col items-center justify-center w-12 h-12 text-on-surface-variant hover:text-white">
<span class="material-symbols-outlined" style="font-size: 20px;">analytics</span>
<span class="text-[10px] font-bold mt-0.5">Activity</span>
</button>
<button class="flex flex-col items-center justify-center w-12 h-12 text-on-surface-variant hover:text-white">
<span class="material-symbols-outlined" style="font-size: 20px;">two_wheeler</span>
<span class="text-[10px] font-bold mt-0.5">Garage</span>
</button>
<button class="flex flex-col items-center justify-center w-12 h-12 text-on-surface-variant hover:text-white">
<span class="material-symbols-outlined" style="font-size: 20px;">groups</span>
<span class="text-[10px] font-bold mt-0.5">Social</span>
</button>
<button class="flex flex-col items-center justify-center w-12 h-12 text-primary">
<span class="material-symbols-outlined" style="font-variation-settings: 'FILL' 1; font-size: 20px;">settings</span>
<span class="text-[10px] font-bold mt-0.5">Settings</span>
</button>
</nav>
<!-- Platform Inset Pill -->
<div class="h-3 w-full bg-surface-container-lowest flex justify-center pb-1">
<div class="w-32 h-1 bg-outline-variant rounded-full"></div>
</div>
</div>
</div>
</div>
</div>
<!-- ======================================================== -->
<!-- SECTION 2: SYSTEM DIALOGS, SHARING & MODAL FLOWS -->
<!-- ======================================================== -->
<div class="pt-8 border-t border-outline-variant">
<div class="mb-6">
<h2 class="text-xl font-bold text-white flex items-center gap-2">
<span class="w-3 h-3 rounded-full bg-warning"></span>
Interactive Modal Architecture &amp; System Dialogs
</h2>
<p class="text-sm text-on-surface-variant mt-0.5">Specifications for Share Ride recipient selection, scoped deletion confirmation, and network fallback states.</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<!-- Dialog 1: Share Ride Recipient Selection Modal -->
<div class="p-5 rounded-2xl bg-surface-container-low border border-outline-variant space-y-4">
<div class="flex items-center justify-between border-b border-outline-variant pb-3">
<div class="flex items-center gap-2">
<span class="material-symbols-outlined text-primary" style="font-size: 20px;">share</span>
<h3 class="text-sm font-bold text-white">Share Telemetry Replay</h3>
</div>
<span class="text-[10px] font-mono px-2 py-0.5 rounded bg-surface-container text-primary">Recipient Selection</span>
</div>
<p class="text-xs text-on-surface-variant leading-snug">Select specific riders, pack channels, or external platforms to dispatch session telemetry.</p>
<!-- Recipient Radio / Checkbox Options -->
<div class="space-y-2">
<label class="flex items-center justify-between p-2.5 rounded-xl bg-surface-container border border-primary/50 cursor-pointer">
<div class="flex items-center gap-2.5">
<div class="w-8 h-8 rounded-full bg-primary-container/20 flex items-center justify-center text-primary font-bold text-xs">BT</div>
<div>
<p class="text-xs font-bold text-white">Bay Area Twisty Club</p>
<p class="text-[10px] text-on-surface-variant font-mono">14 Active Pack Members</p>
</div>
</div>
<input type="radio" checked name="share_dest" class="text-primary focus:ring-0 w-4 h-4 bg-surface-container-highest"/>
</label>
<label class="flex items-center justify-between p-2.5 rounded-xl bg-surface-container border border-outline-variant cursor-pointer hover:border-outline">
<div class="flex items-center gap-2.5">
<div class="w-8 h-8 rounded-full bg-surface-container-high flex items-center justify-center text-on-surface font-bold text-xs">@M</div>
<div>
<p class="text-xs font-bold text-white">@Marcus (Direct DM)</p>
<p class="text-[10px] text-on-surface-variant font-mono">Rode sweep on Skyline run</p>
</div>
</div>
<input type="radio" name="share_dest" class="text-primary focus:ring-0 w-4 h-4 bg-surface-container-highest"/>
</label>
<label class="flex items-center justify-between p-2.5 rounded-xl bg-surface-container border border-outline-variant cursor-pointer hover:border-outline">
<div class="flex items-center gap-2.5">
<div class="w-8 h-8 rounded-lg bg-surface-container-high flex items-center justify-center text-on-surface">
<span class="material-symbols-outlined" style="font-size: 16px;">ios_share</span>
</div>
<div>
<p class="text-xs font-bold text-white">System Sheet / GPX Export</p>
<p class="text-[10px] text-on-surface-variant font-mono">Raw .GPX file &amp; replay web link</p>
</div>
</div>
<input type="radio" name="share_dest" class="text-primary focus:ring-0 w-4 h-4 bg-surface-container-highest"/>
</label>
</div>
<div class="flex items-center gap-2 pt-2">
<button class="h-11 flex-1 rounded-xl bg-primary-container text-on-primary-container text-xs font-bold">Confirm Share</button>
<button class="h-11 px-4 rounded-xl bg-surface-container text-white text-xs font-semibold border border-outline-variant">Cancel</button>
</div>
</div>
<!-- Dialog 2: Scoped Deletion Confirmation Modal -->
<div class="p-5 rounded-2xl bg-surface-container-low border border-error/50 space-y-4">
<div class="flex items-center justify-between border-b border-outline-variant pb-3">
<div class="flex items-center gap-2">
<span class="material-symbols-outlined text-error" style="font-size: 20px;">delete_forever</span>
<h3 class="text-sm font-bold text-white">Delete Ride Session</h3>
</div>
<span class="text-[10px] font-mono px-2 py-0.5 rounded bg-error-container text-error">Scoped Purge</span>
</div>
<p class="text-xs text-on-surface-variant leading-snug">
Specify deletion scope. Do not assume cloud records are automatically expunged when clearing phone storage.
</p>
<!-- Scope Option Selector -->
<div class="space-y-2">
<label class="flex items-start gap-2.5 p-2.5 rounded-xl bg-surface-container border border-outline-variant cursor-pointer">
<input type="radio" name="del_scope" class="mt-0.5 text-error focus:ring-0 w-4 h-4 bg-surface-container-highest"/>
<div>
<p class="text-xs font-bold text-white">Delete Local Device Cache Only</p>
<p class="text-[10px] text-on-surface-variant">Frees up 14.2 MB on phone. Ride remains accessible in your cloud archive.</p>
</div>
</label>
<label class="flex items-start gap-2.5 p-2.5 rounded-xl bg-error-container/30 border border-error/40 cursor-pointer">
<input type="radio" checked name="del_scope" class="mt-0.5 text-error focus:ring-0 w-4 h-4 bg-surface-container-highest"/>
<div>
<p class="text-xs font-bold text-error">Delete Both Local &amp; Cloud Records</p>
<p class="text-[10px] text-on-surface-variant">Permanent purge. GPS track, lean dynamics, and community stats will be unrecoverable.</p>
</div>
</label>
</div>
<div class="flex items-center gap-2 pt-2">
<button class="h-11 flex-1 rounded-xl bg-error text-white text-xs font-bold">Execute Deletion</button>
<button class="h-11 px-4 rounded-xl bg-surface-container text-white text-xs font-semibold border border-outline-variant">Cancel</button>
</div>
</div>
<!-- Dialog 3: Telemetry Buffering vs. Downloading Specification -->
<div class="p-5 rounded-2xl bg-surface-container-low border border-outline-variant space-y-4">
<div class="flex items-center justify-between border-b border-outline-variant pb-3">
<div class="flex items-center gap-2">
<span class="material-symbols-outlined text-tertiary" style="font-size: 20px;">cloud_download</span>
<h3 class="text-sm font-bold text-white">Data Pipeline Protocol</h3>
</div>
<span class="text-[10px] font-mono px-2 py-0.5 rounded bg-surface-container text-tertiary">Architecture Note</span>
</div>
<div class="space-y-3 text-xs text-on-surface-variant leading-relaxed">
<div class="p-2.5 rounded-xl bg-surface-container-lowest border border-outline-variant space-y-1">
<span class="font-bold text-white flex items-center gap-1.5">
<span class="w-1.5 h-1.5 rounded-full bg-primary"></span> 1. File Download Stage
</span>
<p class="text-[11px]">When replaying a cloud ride, client fetches the complete binary `.pb` or SQLite session record before parsing. Not live video-style streaming.</p>
</div>
<div class="p-2.5 rounded-xl bg-surface-container-lowest border border-outline-variant space-y-1">
<span class="font-bold text-white flex items-center gap-1.5">
<span class="w-1.5 h-1.5 rounded-full bg-tertiary"></span> 2. Local Memory Buffering
</span>
<p class="text-[11px]">"100% Buffered" indicator denotes in-memory decompression of 100Hz IMU vectors to prevent scrubbing stutter.</p>
</div>
<div class="p-2.5 rounded-xl bg-surface-container-lowest border border-outline-variant space-y-1">
<span class="font-bold text-white flex items-center gap-1.5">
<span class="w-1.5 h-1.5 rounded-full bg-warning"></span> 3. Proposed Auto-Sync Rule
</span>
<p class="text-[11px]">Automatic background sync is documented as proposed UX pending validation of operating system background execution limits.</p>
</div>
</div>
</div>
</div>
</div>
<!-- ======================================================== -->
<!-- SECTION 3: SYSTEM STATES MATRIX -->
<!-- ======================================================== -->
<div class="pt-8 border-t border-outline-variant">
<div class="mb-6">
<h2 class="text-xl font-bold text-white flex items-center gap-2">
<span class="w-3 h-3 rounded-full bg-primary"></span>
Edge &amp; Exception States Matrix
</h2>
<p class="text-sm text-on-surface-variant mt-0.5">Comprehensive fallback patterns for loading, empty filters, network error, offline queueing, and permission denial.</p>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4">
<!-- State 1: Loading / Downloading -->
<div class="p-4 rounded-2xl bg-surface-container-low border border-outline-variant space-y-2.5 text-center flex flex-col items-center justify-between">
<div class="w-10 h-10 rounded-full bg-primary-container/20 flex items-center justify-center text-primary mt-2">
<span class="material-symbols-outlined animate-spin" style="font-size: 22px;">progress_activity</span>
</div>
<div>
<h4 class="text-xs font-bold text-white">Loading Telemetry</h4>
<p class="text-[10px] text-on-surface-variant mt-1 leading-snug">Downloading session binary (14.2 MB) from encrypted cloud store...</p>
</div>
<div class="w-full bg-surface-container-highest h-1.5 rounded-full overflow-hidden">
<div class="bg-primary h-full w-[65%]"></div>
</div>
<span class="text-[9px] font-mono text-primary">State: Downloading</span>
</div>
<!-- State 2: Empty Filter -->
<div class="p-4 rounded-2xl bg-surface-container-low border border-outline-variant space-y-2.5 text-center flex flex-col items-center justify-between">
<div class="w-10 h-10 rounded-full bg-surface-container-high flex items-center justify-center text-on-surface-variant mt-2">
<span class="material-symbols-outlined" style="font-size: 22px;">filter_alt_off</span>
</div>
<div>
<h4 class="text-xs font-bold text-white">No Rides Found</h4>
<p class="text-[10px] text-on-surface-variant mt-1 leading-snug">No telemetry recorded for "Track Mode" under selected date filter.</p>
</div>
<button class="h-9 px-3 rounded-lg bg-surface-container border border-outline-variant text-[11px] text-primary font-bold">
Reset Filter
</button>
<span class="text-[9px] font-mono text-on-surface-variant">State: Empty</span>
</div>
<!-- State 3: Network Error -->
<div class="p-4 rounded-2xl bg-surface-container-low border border-error/40 space-y-2.5 text-center flex flex-col items-center justify-between">
<div class="w-10 h-10 rounded-full bg-error-container flex items-center justify-center text-error mt-2">
<span class="material-symbols-outlined" style="font-size: 22px;">cloud_off</span>
</div>
<div>
<h4 class="text-xs font-bold text-white">Sync Failure</h4>
<p class="text-[10px] text-on-surface-variant mt-1 leading-snug">Cloud handshake timed out. Remote server unreachable.</p>
</div>
<button class="h-9 px-3 rounded-lg bg-surface-container border border-outline-variant text-[11px] text-white font-bold">
Retry Sync
</button>
<span class="text-[9px] font-mono text-error">State: Error</span>
</div>
<!-- State 4: Offline Queue -->
<div class="p-4 rounded-2xl bg-surface-container-low border border-warning/40 space-y-2.5 text-center flex flex-col items-center justify-between">
<div class="w-10 h-10 rounded-full bg-warning/20 flex items-center justify-center text-warning mt-2">
<span class="material-symbols-outlined" style="font-size: 22px;">save</span>
</div>
<div>
<h4 class="text-xs font-bold text-white">Offline Queued</h4>
<p class="text-[10px] text-on-surface-variant mt-1 leading-snug">2 rides stored locally in SQLite database cache pending uplink.</p>
</div>
<span class="text-[10px] font-mono text-warning font-bold">Auto-sync Proposed</span>
<span class="text-[9px] font-mono text-warning">State: Queued</span>
</div>
<!-- State 5: Permission Denied -->
<div class="p-4 rounded-2xl bg-surface-container-low border border-outline-variant space-y-2.5 text-center flex flex-col items-center justify-between">
<div class="w-10 h-10 rounded-full bg-surface-container-high flex items-center justify-center text-error mt-2">
<span class="material-symbols-outlined" style="font-size: 22px;">location_disabled</span>
</div>
<div>
<h4 class="text-xs font-bold text-white">Location Denied</h4>
<p class="text-[10px] text-on-surface-variant mt-1 leading-snug">Precise background GPS permission required for route telemetry.</p>
</div>
<button class="h-9 px-3 rounded-lg bg-primary-container text-on-primary-container text-[11px] font-bold">
Open Settings
</button>
<span class="text-[9px] font-mono text-error">State: Denied</span>
</div>
</div>
</div>
<!-- ======================================================== -->
<!-- SECTION 4: CONSOLIDATED DESIGN TOKENS & HANDOFF MATRIX -->
<!-- ======================================================== -->
<div class="pt-8 border-t border-outline-variant">
<div class="mb-6">
<h2 class="text-xl font-bold text-white flex items-center gap-2">
<span class="w-3 h-3 rounded-full bg-tertiary"></span>
Consolidated Design Tokens &amp; Multi-Platform Handoff
</h2>
<p class="text-sm text-on-surface-variant mt-0.5">Authoritative token values, component patterns, and platform safe-area implementation contracts.</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Column 1: Semantic Color Tokens -->
<div class="p-5 rounded-2xl bg-surface-container-low border border-outline-variant space-y-4">
<h3 class="text-sm font-bold text-white flex items-center gap-2">
<span class="material-symbols-outlined text-primary" style="font-size: 18px;">palette</span>
Semantic Color Architecture
</h3>
<div class="space-y-2 text-xs font-mono">
<div class="flex items-center justify-between p-2 rounded-lg bg-surface-container">
<span class="text-white">Canvas Surface</span>
<span class="text-primary font-bold">#0B1326</span>
</div>
<div class="flex items-center justify-between p-2 rounded-lg bg-surface-container">
<span class="text-white">Card Primary</span>
<span class="text-primary font-bold">#111C38</span>
</div>
<div class="flex items-center justify-between p-2 rounded-lg bg-surface-container">
<span class="text-white">Card Secondary</span>
<span class="text-primary font-bold">#17223B</span>
</div>
<div class="flex items-center justify-between p-2 rounded-lg bg-surface-container">
<span class="text-white">Border / Stroke</span>
<span class="text-primary font-bold">#2A3652</span>
</div>
<div class="flex items-center justify-between p-2 rounded-lg bg-surface-container">
<span class="text-white">Brand Cyan Accent</span>
<span class="text-primary-container font-bold">#06B6D4</span>
</div>
<div class="flex items-center justify-between p-2 rounded-lg bg-surface-container">
<span class="text-white">Active Recording (REC)</span>
<span class="text-tertiary font-bold">#10B981</span>
</div>
<div class="flex items-center justify-between p-2 rounded-lg bg-surface-container">
<span class="text-white">Caution / Pause Ride</span>
<span class="text-warning font-bold">#F59E0B</span>
</div>
<div class="flex items-center justify-between p-2 rounded-lg bg-surface-container">
<span class="text-white">Danger / Scoped Delete</span>
<span class="text-error font-bold">#EF4444</span>
</div>
</div>
</div>
<!-- Column 2: Typography & Touch Boundaries -->
<div class="p-5 rounded-2xl bg-surface-container-low border border-outline-variant space-y-4">
<h3 class="text-sm font-bold text-white flex items-center gap-2">
<span class="material-symbols-outlined text-primary" style="font-size: 18px;">touch_app</span>
Touch Targets &amp; Type Hierarchy
</h3>
<div class="space-y-3 text-xs leading-relaxed text-on-surface-variant">
<div>
<span class="font-bold text-white block">Standard Interactive Minimum:</span>
<p class="font-mono text-primary text-[11px]">48px × 48px CSS / Flutter Logical Pixels</p>
<p class="text-[11px] mt-0.5">Enforced on all list items, segmented tabs, menu dots, and header navigation buttons.</p>
</div>
<div>
<span class="font-bold text-white block">Primary Riding Controls:</span>
<p class="font-mono text-primary text-[11px]">56px–64px Target Height</p>
<p class="text-[11px] mt-0.5">Pinned Pause and Finish Ride actions maximize hit-area under motorcycle vibration.</p>
</div>
<div>
<span class="font-bold text-white block">Typography Stack:</span>
<ul class="text-[11px] space-y-1 list-disc pl-4 mt-1 font-mono">
<li>Speed Hero: 72px JetBrains Mono bold</li>
<li>Metrics: 16–24px JetBrains Mono bold</li>
<li>Headings: 14–18px Inter bold</li>
<li>Labels/Captions: 10–12px Inter medium</li>
</ul>
</div>
</div>
</div>
<!-- Column 3: Platform Safe Area Implementation Contract -->
<div class="p-5 rounded-2xl bg-surface-container-low border border-outline-variant space-y-4">
<h3 class="text-sm font-bold text-white flex items-center gap-2">
<span class="material-symbols-outlined text-primary" style="font-size: 18px;">devices</span>
Platform Safe Areas &amp; Implementation
</h3>
<div class="space-y-2.5 text-xs text-on-surface-variant leading-relaxed">
<div class="p-2.5 rounded-xl bg-surface-container-lowest border border-outline-variant space-y-1">
<span class="font-bold text-white font-mono text-[11px]">Flutter Implementation:</span>
<p class="text-[11px]">
Use <code class="text-primary font-mono">SafeArea(top: true, bottom: true)</code> or query <code class="text-primary font-mono">MediaQuery.of(context).padding</code> for Notch, Dynamic Island, and gesture bar clearances.
</p>
</div>
<div class="p-2.5 rounded-xl bg-surface-container-lowest border border-outline-variant space-y-1">
<span class="font-bold text-white font-mono text-[11px]">Web / PWA Implementation:</span>
<p class="text-[11px]">
Apply <code class="text-primary font-mono">env(safe-area-inset-top)</code> to sticky app bars and <code class="text-primary font-mono">env(safe-area-inset-bottom)</code> to fixed navigation sheets.
</p>
</div>
<div class="p-2.5 rounded-xl bg-surface-container-lowest border border-outline-variant space-y-1">
<span class="font-bold text-white font-mono text-[11px]">Map Overlay Separation Goal:</span>
<p class="text-[11px]">
Top-right map controls vs. bottom telemetry is designated as a <strong>layout goal pending physical multi-device testing</strong> across varied screen aspect ratios.
</p>
</div>
</div>
</div>
</div>
</div>
</main>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

View File

@ -0,0 +1,583 @@
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>MotorSignal Telemetry &amp; Community - Ride History &amp; Replay HUD</title>
<!-- Tailwind CSS CDN with Forms & Container Plugins -->
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
brand: {
dark: '#0B1326',
card: '#111C38',
cardSubtle: '#171F33',
border: '#1E293B',
borderLight: '#334155',
cyan: '#06B6D4',
cyanBright: '#22D3EE',
green: '#10B981',
amber: '#F59E0B',
danger: '#EF4444'
}
},
fontFamily: {
mono: ['JetBrains Mono', 'ui-monospace', 'SFMono-Regular', 'Menlo', 'monospace']
}
}
}
}
</script>
<!-- BEGIN: Custom Device Frame & Decorative Styles -->
<style data-purpose="device-mockup-frame">
.mobile-screen-container {
width: 390px;
height: 844px;
max-height: 844px;
border-radius: 46px;
box-shadow: 0 25px 60px -15px rgba(0, 0, 0, 0.8), 0 0 0 10px #1E293B, 0 0 0 12px #334155;
overflow: hidden;
display: flex;
flex-direction: column;
position: relative;
background-color: #0B1326;
}
/* Dynamic Notch / Dynamic Island */
.mobile-island {
width: 122px;
height: 32px;
background-color: #000000;
border-radius: 20px;
position: absolute;
top: 10px;
left: 50%;
transform: translateX(-50%);
z-index: 50;
}
/* Safe Area Callout Overlays */
.safe-area-top-guide {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 47px;
pointer-events: none;
border-bottom: 1px dashed rgba(6, 182, 212, 0.4);
z-index: 60;
}
.safe-area-bottom-guide {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 34px;
pointer-events: none;
border-top: 1px dashed rgba(6, 182, 212, 0.4);
z-index: 60;
}
/* Custom scrollbar hiding */
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
</style>
<style data-purpose="hud-telemetry-decorations">
/* Subtle grid line pattern for maps */
.topo-grid-pattern {
background-size: 24px 24px;
background-image:
linear-gradient(to right, rgba(255, 255, 255, 0.04) 1px, transparent 1px),
linear-gradient(to bottom, rgba(255, 255, 255, 0.04) 1px, transparent 1px);
}
.lean-gauge-arc {
transform-origin: center;
transition: stroke-dashoffset 0.3s ease;
}
</style>
<!-- END: Custom Device Frame & Decorative Styles -->
</head>
<body class="bg-[#070C1A] text-slate-100 min-h-screen font-sans antialiased py-10 px-4">
<!-- BEGIN: MasterSpecificationContainer -->
<div class="max-w-7xl mx-auto">
<!-- Header Documentation Banner -->
<header class="mb-10 text-center md:text-left flex flex-col md:flex-row md:items-end md:justify-between border-b border-brand-border pb-6">
<div>
<div class="flex items-center gap-2 justify-center md:justify-start">
<span class="inline-block w-2.5 h-2.5 rounded-full bg-brand-cyan animate-pulse"></span>
<span class="text-xs uppercase tracking-widest text-brand-cyan font-semibold">MotorSignal Telemetry OS</span>
</div>
<h1 class="text-3xl font-extrabold text-white mt-1">Ride History &amp; Replay HUD Specification</h1>
<p class="text-slate-400 text-sm mt-1">High-Fidelity Dual-Viewport Mobile Prototype (iOS 390x844 Frame Specification)</p>
</div>
<div class="mt-4 md:mt-0 flex gap-3 text-xs font-mono text-slate-300 justify-center">
<span class="bg-brand-card border border-brand-border px-3 py-1.5 rounded-lg flex items-center gap-1.5">
<span class="w-2 h-2 rounded-full bg-brand-green"></span> Spec v2.6.4 Live
</span>
<span class="bg-brand-card border border-brand-border px-3 py-1.5 rounded-lg text-slate-400">
Target: 390x844 @3x
</span>
</div>
</header>
<!-- BEGIN: DualViewportPresentationGrid -->
<main class="flex flex-col lg:flex-row items-center justify-center gap-12 mb-16">
<!-- ========================================== -->
<!-- VIEWPORT 1: RIDE HISTORY ARCHIVE -->
<!-- ========================================== -->
<section class="flex flex-col items-center" data-purpose="viewport-ride-history">
<!-- Viewport Label -->
<div class="mb-3 flex items-center justify-between w-[390px] px-2 text-xs font-medium text-slate-400">
<span class="text-brand-cyan font-semibold flex items-center gap-1.5">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
VIEWPORT 1: Session Archive
</span>
<span class="text-slate-500 font-mono">Screen: Activity.History</span>
</div>
<div class="mobile-screen-container relative">
<!-- Dynamic Island Notch -->
<div class="mobile-island"></div>
<!-- Safe Area Top Guide Callout (Visual Tag) -->
<div class="safe-area-top-guide flex items-end justify-end pr-3 pb-1">
<span class="text-[9px] font-mono text-brand-cyan/60 uppercase">env(safe-area-inset-top)</span>
</div>
<!-- Phone Status Bar -->
<div class="h-12 pt-3 px-7 flex justify-between items-center text-xs font-semibold text-white z-40">
<span>2:02</span>
<div class="flex items-center gap-1.5 text-slate-200">
<svg class="w-3.5 h-3.5" fill="currentColor" viewbox="0 0 24 24"><path d="M12 3c-4.97 0-9 4.03-9 9 0 2.12.74 4.07 1.97 5.61L4.35 18.25l1.41 1.41.64-.64C7.93 20.26 9.88 21 12 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm0 16c-3.86 0-7-3.14-7-7s3.14-7 7-7 7 3.14 7 7-3.14 7-7 7z"></path></svg>
<svg class="w-3.5 h-3.5" fill="currentColor" viewbox="0 0 24 24"><path d="M12 4a8 8 0 00-8 8c0 2.21.89 4.21 2.34 5.66l1.41-1.41C6.6 15.1 6 13.62 6 12a6 6 0 1112 0c0 1.62-.6 3.1-1.76 4.24l1.41 1.41A8.002 8.002 0 0012 4z"></path></svg>
<div class="w-5 h-2.5 border border-white/80 rounded-sm p-0.5 flex items-center">
<div class="h-full bg-white w-4/5 rounded-2xs"></div>
</div>
</div>
</div>
<!-- BEGIN: Sticky Screen Header -->
<header class="px-5 pt-1 pb-3 bg-brand-dark/95 backdrop-blur border-b border-brand-border/60 z-30">
<h2 class="text-xl font-bold tracking-tight text-center text-white">Ride History</h2>
<!-- Segmented Switch Rides vs Records -->
<div class="mt-3 grid grid-cols-2 p-1 bg-brand-cardSubtle rounded-xl border border-brand-border text-xs font-semibold">
<button class="py-2 rounded-lg bg-cyan-950/80 text-brand-cyan border border-brand-cyan/40 shadow flex items-center justify-center gap-1.5">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M5 13l4 4L19 7" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5"></path></svg>
Rides (13)
</button>
<button class="py-2 rounded-lg text-slate-400 hover:text-slate-200 flex items-center justify-center gap-1.5 transition">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M12 8v13m0-13V6a2 2 0 112 2h-2zm0 0V5.5A2.5 2.5 0 109.5 8H12zm-7 4h14M5 12a2 2 0 110-4h14a2 2 0 110 4M5 12v7a2 2 0 002 2h10a2 2 0 002-2v-7" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
Records (4)
</button>
</div>
<!-- Motorcycle Dropdown Filter Button (Minimum 48px Target) -->
<div class="mt-3 flex items-center gap-2">
<button aria-label="Filter by bike" class="w-12 h-12 flex items-center justify-center rounded-xl bg-brand-card border border-brand-border text-brand-cyan hover:bg-slate-800 transition">
<svg class="w-4 h-4" fill="currentColor" viewbox="0 0 24 24"><path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"></path></svg>
</button>
<button aria-label="Select motorcycle fleet" class="flex-1 h-12 px-4 rounded-xl bg-brand-card border border-brand-border flex items-center justify-between text-xs font-semibold text-slate-200 hover:border-slate-600 transition">
<span class="flex items-center gap-2 truncate">
<span class="w-2 h-2 rounded-full bg-brand-cyan"></span>
All Motorcycles
</span>
<svg class="w-4 h-4 text-slate-400" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M19 9l-7 7-7-7" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
</button>
</div>
</header>
<!-- END: Sticky Screen Header -->
<!-- Scrollable Ride Content Feed -->
<div class="flex-1 overflow-y-auto px-4 py-3 space-y-3 no-scrollbar pb-24">
<!-- Offline Sync Status Banner -->
<div class="bg-amber-950/40 border border-brand-amber/40 rounded-xl p-3 flex items-center gap-2.5" data-purpose="sync-banner">
<span class="w-2.5 h-2.5 rounded-full bg-brand-amber shrink-0 animate-ping"></span>
<div class="text-[11px] leading-tight text-amber-200">
<strong class="font-semibold">Offline Mode:</strong> 2 rides queued locally (auto-sync on connection).
</div>
</div>
<!-- PRIMARY CARD: Ride 12 (Targeted Detailed Session) -->
<article class="bg-brand-card border-2 border-brand-cyan/60 rounded-2xl p-3.5 shadow-lg relative overflow-hidden" data-purpose="primary-ride-card">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<h3 class="text-base font-bold text-white tracking-wide">Ride 12</h3>
<span class="text-[10px] bg-cyan-950 text-brand-cyan font-mono font-medium px-2 py-0.5 rounded border border-brand-cyan/30">
2001 YAMAHA Y...
</span>
</div>
<time class="text-[10px] text-slate-400 font-mono">2026-10-10 03:23</time>
</div>
<!-- Route Title Tag -->
<p class="text-xs font-medium text-slate-300 mt-1">Skyline Blvd &amp; Pescadero Carve</p>
<!-- Metrics Matrix Grid -->
<div class="grid grid-cols-3 gap-2 mt-3 pt-2.5 border-t border-brand-border/60">
<div>
<span class="text-[9px] uppercase tracking-wider text-slate-400 font-semibold block">Trip</span>
<span class="text-sm font-bold text-white">15.7 <span class="text-[10px] font-normal text-slate-400">mi</span></span>
</div>
<div>
<span class="text-[9px] uppercase tracking-wider text-slate-400 font-semibold block">Time</span>
<span class="text-sm font-bold text-white">16 <span class="text-[10px] font-normal text-slate-400">min</span></span>
</div>
<div>
<span class="text-[9px] uppercase tracking-wider text-slate-400 font-semibold block">Top Spd</span>
<span class="text-sm font-bold text-white">84 <span class="text-[10px] font-normal text-slate-400">mph</span></span>
</div>
<div>
<span class="text-[9px] uppercase tracking-wider text-slate-400 font-semibold block">Max Lean</span>
<span class="text-sm font-bold text-brand-cyanBright">27° <span class="text-[9px] font-normal text-slate-400">R</span></span>
</div>
<div>
<span class="text-[9px] uppercase tracking-wider text-slate-400 font-semibold block">Hard Brk</span>
<span class="text-sm font-bold text-red-400">0.49 G</span>
</div>
<div>
<span class="text-[9px] uppercase tracking-wider text-slate-400 font-semibold block">Elev Gain</span>
<span class="text-sm font-bold text-slate-200">69 m</span>
</div>
</div>
<!-- Minimum 48px Interactive CTA to Replay -->
<button class="mt-3.5 w-full h-12 bg-brand-cyan hover:bg-brand-cyanBright text-slate-950 font-bold rounded-xl flex items-center justify-center gap-2 text-xs transition active:scale-[0.98] shadow-md shadow-cyan-500/20">
<svg class="w-4 h-4 fill-current" viewbox="0 0 24 24"><path d="M8 5v14l11-7z"></path></svg>
REPLAY TELEMETRY SESSION
</button>
</article>
<!-- SECONDARY CARD: Ride 11 (Completed & Synced) -->
<article class="bg-brand-card border border-brand-border rounded-2xl p-3.5">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<h3 class="text-base font-bold text-slate-200">Ride 11</h3>
<span class="text-[10px] bg-slate-800 text-slate-300 font-mono px-2 py-0.5 rounded">
DUCATI V4
</span>
</div>
<span class="text-[9px] bg-emerald-950/80 text-brand-green border border-emerald-800/40 px-2 py-0.5 rounded-full flex items-center gap-1 font-mono">
<span class="w-1.5 h-1.5 rounded-full bg-brand-green"></span> Synced
</span>
</div>
<div class="grid grid-cols-3 gap-2 mt-2 pt-2 border-t border-brand-border/40 text-xs">
<div>
<span class="text-[9px] text-slate-400 block font-semibold">TRIP</span>
<span class="font-bold text-slate-100">42.1 mi</span>
</div>
<div>
<span class="text-[9px] text-slate-400 block font-semibold">TIME</span>
<span class="font-bold text-slate-100">54 min</span>
</div>
<div>
<span class="text-[9px] text-slate-400 block font-semibold">TOP SPD</span>
<span class="font-bold text-slate-100">72 mph</span>
</div>
</div>
</article>
<!-- EMPTY FILTER STATE CARD: Track Mode demonstration -->
<div class="border border-dashed border-brand-borderLight rounded-2xl p-4 text-center bg-brand-card/40" data-purpose="empty-state-notice">
<div class="w-8 h-8 rounded-full bg-slate-800 flex items-center justify-center mx-auto text-slate-400">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M3 21v-4m0 0V5a2 2 0 012-2h6.5l1 1H21l-3 6 3 6h-8.5l-1-1H5a2 2 0 00-2 2zm9-13.5V9" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
</div>
<p class="text-xs font-semibold text-slate-300 mt-2">No rides found for Track Mode</p>
<p class="text-[10px] text-slate-500 mt-0.5">Change telemetry preset or switch filter above.</p>
</div>
</div>
<!-- BEGIN: Fixed Mobile Bottom Navigation Bar -->
<nav aria-label="Primary mobile nav" class="absolute bottom-0 left-0 right-0 h-20 bg-brand-card/95 backdrop-blur-md border-t border-brand-border flex items-start justify-around px-2 pt-2 z-40">
<!-- Dash -->
<button class="flex flex-col items-center justify-center w-12 h-12 text-slate-400 hover:text-slate-200">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
<span class="text-[9px] mt-1 font-medium">Dash</span>
</button>
<!-- Map -->
<button class="flex flex-col items-center justify-center w-12 h-12 text-slate-400 hover:text-slate-200">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
<span class="text-[9px] mt-1 font-medium">Map</span>
</button>
<!-- Activity (ACTIVE HIGHLIGHT) -->
<button class="flex flex-col items-center justify-center w-12 h-12 text-brand-cyan">
<div class="relative">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5"></path></svg>
<span class="absolute -top-1 -right-1.5 w-2 h-2 rounded-full bg-brand-cyan"></span>
</div>
<span class="text-[9px] mt-1 font-bold">Activity</span>
</button>
<!-- Garage -->
<button class="flex flex-col items-center justify-center w-12 h-12 text-slate-400 hover:text-slate-200">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
<span class="text-[9px] mt-1 font-medium">Garage</span>
</button>
<!-- Social -->
<button class="flex flex-col items-center justify-center w-12 h-12 text-slate-400 hover:text-slate-200">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
<span class="text-[9px] mt-1 font-medium">Social</span>
</button>
<!-- Settings -->
<button class="flex flex-col items-center justify-center w-12 h-12 text-slate-400 hover:text-slate-200">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
<span class="text-[9px] mt-1 font-medium">Settings</span>
</button>
</nav>
<!-- END: Fixed Mobile Bottom Navigation Bar -->
<!-- Safe Area Bottom Guide Callout -->
<div class="safe-area-bottom-guide flex items-start justify-center pt-0.5">
<span class="text-[9px] font-mono text-brand-cyan/60 uppercase">env(safe-area-inset-bottom)</span>
</div>
</div>
</section>
<!-- ========================================== -->
<!-- VIEWPORT 2: RIDE REPLAY HUD -->
<!-- ========================================== -->
<section class="flex flex-col items-center" data-purpose="viewport-ride-replay">
<!-- Viewport Label -->
<div class="mb-3 flex items-center justify-between w-[390px] px-2 text-xs font-medium text-slate-400">
<span class="text-brand-cyanBright font-semibold flex items-center gap-1.5">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path><path d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
VIEWPORT 2: Telemetry Replay HUD
</span>
<span class="text-slate-500 font-mono">Screen: Replay.ActiveHUD</span>
</div>
<div class="mobile-screen-container relative bg-slate-950">
<!-- Dynamic Island Notch -->
<div class="mobile-island"></div>
<!-- Safe Area Top Guide Callout -->
<div class="safe-area-top-guide flex items-end justify-end pr-3 pb-1">
<span class="text-[9px] font-mono text-brand-cyan/60 uppercase">env(safe-area-inset-top)</span>
</div>
<!-- Phone Status Bar -->
<div class="h-12 pt-3 px-7 flex justify-between items-center text-xs font-semibold text-white z-40">
<span>2:02</span>
<div class="flex items-center gap-1.5 text-slate-200">
<svg class="w-3.5 h-3.5" fill="currentColor" viewbox="0 0 24 24"><path d="M12 3c-4.97 0-9 4.03-9 9 0 2.12.74 4.07 1.97 5.61L4.35 18.25l1.41 1.41.64-.64C7.93 20.26 9.88 21 12 21c4.97 0 9-4.03 9-9s-4.03-9-9-9z"></path></svg>
<div class="w-5 h-2.5 border border-white/80 rounded-sm p-0.5 flex items-center">
<div class="h-full bg-white w-4/5 rounded-2xs"></div>
</div>
</div>
</div>
<!-- Replay Header Action Bar (All touch targets >= 48px) -->
<header class="h-14 px-3 flex items-center justify-between border-b border-brand-border/60 bg-brand-dark/90 backdrop-blur z-30">
<!-- Back to Archive Button -->
<button aria-label="Exit Replay HUD" class="w-12 h-12 flex items-center justify-center text-slate-300 hover:text-white rounded-xl active:bg-slate-800">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M15 19l-7-7 7-7" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5"></path></svg>
</button>
<!-- Replay Track Title -->
<div class="text-center px-1 truncate">
<h2 class="text-xs font-bold text-white truncate">Ride 12 • Skyline &amp; Pescadero</h2>
<span class="text-[10px] text-brand-cyan font-mono block">GPS Telemetry Synced</span>
</div>
<!-- Header Action Buttons (Share & Delete) -->
<div class="flex items-center gap-0.5">
<!-- Share Button -->
<button aria-label="Share Session" class="w-12 h-12 flex items-center justify-center text-slate-300 hover:text-brand-cyan rounded-xl active:bg-slate-800">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
</button>
<!-- Destructive Delete Button (Red strictly reserved for deletion) -->
<button aria-label="Delete Session" class="w-12 h-12 flex items-center justify-center text-brand-danger hover:bg-red-950/40 rounded-xl transition">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
</button>
</div>
</header>
<!-- BEGIN: Map Visualizer Canvas Container -->
<div class="relative flex-1 bg-slate-950 topo-grid-pattern overflow-hidden" data-purpose="replay-map-container">
<!-- Telemetry Buffering / Cache State Pill -->
<div class="absolute top-3 left-4 z-20">
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-[10px] font-mono bg-brand-card/90 border border-brand-green/40 text-brand-green backdrop-blur shadow">
<span class="w-1.5 h-1.5 rounded-full bg-brand-green animate-pulse"></span>
100% Buffered (Local cache)
</span>
</div>
<!-- STRICT ISOLATION: Floating Map Controls (Top-Right Quadrant, isolated from telemetry overlays) -->
<div class="absolute top-3 right-3 flex flex-col gap-1.5 z-20" data-purpose="isolated-map-controls">
<button aria-label="Zoom in" class="w-12 h-12 rounded-xl bg-brand-card/90 border border-brand-border text-slate-200 flex items-center justify-center hover:bg-brand-cardSubtle active:scale-95 shadow backdrop-blur">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M12 6v6m0 0v6m0-6h6m-6 0H6" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
</button>
<button aria-label="Zoom out" class="w-12 h-12 rounded-xl bg-brand-card/90 border border-brand-border text-slate-200 flex items-center justify-center hover:bg-brand-cardSubtle active:scale-95 shadow backdrop-blur">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M18 12H6" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
</button>
<button aria-label="Toggle map layer styles" class="w-12 h-12 rounded-xl bg-brand-card/90 border border-brand-border text-slate-200 flex items-center justify-center hover:bg-brand-cardSubtle active:scale-95 shadow backdrop-blur">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
</button>
</div>
<!-- Stylized GPS Trace Path SVG Visualizer -->
<svg class="absolute inset-0 w-full h-full pointer-events-none" fill="none" viewbox="0 0 390 380">
<!-- Route Path Trajectory Glow -->
<path d="M 40 310 C 80 290, 110 240, 130 180 C 150 120, 200 90, 240 140 C 270 180, 290 260, 330 220 C 350 200, 360 140, 370 120" opacity="0.25" stroke="#06B6D4" stroke-linecap="round" stroke-linejoin="round" stroke-width="6"></path>
<!-- Route Path Completed Segment -->
<path d="M 40 310 C 80 290, 110 240, 130 180 C 150 120, 200 90, 240 140" stroke="#22D3EE" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5"></path>
<!-- Route Path Remaining Segment -->
<path d="M 240 140 C 270 180, 290 260, 330 220 C 350 200, 360 140, 370 120" stroke="#475569" stroke-dasharray="4 4" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"></path>
<!-- Apex Caution Indicator (Amber semantic) -->
<circle cx="205" cy="94" fill="#F59E0B" r="5"></circle>
<text fill="#F59E0B" font-family="monospace" font-size="9" font-weight="bold" x="214" y="98">51° APEX</text>
<!-- Live Vehicle Cursor Position -->
<g transform="translate(240, 140)">
<circle class="animate-ping" cx="0" cy="0" fill="#06B6D4" opacity="0.2" r="14"></circle>
<circle cx="0" cy="0" fill="#06B6D4" r="7" stroke="#FFFFFF" stroke-width="2"></circle>
<!-- Direction Pointer Arrow -->
<polygon fill="#22D3EE" points="0,-12 4,-5 -4,-5" transform="rotate(45)"></polygon>
</g>
</svg>
<!-- Bottom Elevation Profile Mini HUD Pill -->
<div class="absolute bottom-4 left-4 z-20">
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg text-[10px] font-mono bg-brand-card/90 border border-brand-border text-slate-200 backdrop-blur shadow">
<svg class="w-3 h-3 text-brand-cyan" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
ELV +2,140 FT • GRADE 6.2%
</span>
</div>
</div>
<!-- END: Map Visualizer Canvas Container -->
<!-- BEGIN: Docked Telemetry Cluster & Playback Engine -->
<div class="bg-brand-card border-t border-brand-border px-4 pt-3 pb-8 z-30" data-purpose="docked-telemetry-engine">
<!-- Live Real-Time Telemetry Cluster -->
<div class="grid grid-cols-3 gap-2 py-2 mb-3 bg-brand-dark/80 rounded-xl border border-brand-border/60 text-center">
<!-- Live Speed -->
<div class="px-2">
<span class="text-[9px] uppercase tracking-wider text-slate-400 font-semibold block">Live Speed</span>
<div class="text-xl font-black text-white tracking-tight">58 <span class="text-[10px] font-medium text-brand-cyan">MPH</span></div>
</div>
<!-- Lean Angle Gauge with Visual Arc -->
<div class="px-2 border-x border-brand-border/50 flex flex-col items-center justify-center">
<span class="text-[9px] uppercase tracking-wider text-slate-400 font-semibold block">Lean Angle</span>
<div class="flex items-center gap-1">
<!-- Lean Direction Arc Indicator -->
<svg class="w-4 h-4 text-brand-cyan" fill="none" stroke="currentColor" viewbox="0 0 24 24">
<path d="M19 14l-7 7m0 0l-7-7m7 7V3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path>
</svg>
<span class="text-xl font-black text-brand-cyanBright">21° <span class="text-[10px] font-semibold text-slate-300">R</span></span>
</div>
</div>
<!-- Lateral G Force -->
<div class="px-2">
<span class="text-[9px] uppercase tracking-wider text-slate-400 font-semibold block">Lateral G</span>
<div class="text-xl font-black text-white tracking-tight">0.38 <span class="text-[10px] font-medium text-slate-400">G</span></div>
</div>
</div>
<!-- Scrubbable Timeline Track & Time Displays -->
<div class="space-y-1.5">
<div class="flex justify-between items-center text-[10px] font-mono text-slate-400">
<span class="text-brand-cyan font-bold">08:42</span>
<span class="text-slate-500">SESSION PROGRESS</span>
<span>16:00</span>
</div>
<!-- Timeline Bar with 48px touch-target area for scrubbing -->
<div class="relative h-6 flex items-center cursor-pointer group">
<div class="w-full h-2 bg-slate-800 rounded-full overflow-hidden">
<div class="h-full bg-gradient-to-r from-brand-cyan to-brand-cyanBright rounded-full" style="width: 54.3%;"></div>
</div>
<!-- Interactive Scrub Handle Thumb -->
<div class="absolute top-1/2 -translate-y-1/2 w-4 h-4 bg-white border-2 border-brand-cyan rounded-full shadow-md pointer-events-none" style="left: calc(54.3% - 8px);"></div>
</div>
</div>
<!-- Playback Control Bar (All Minimum 48px Touch Targets) -->
<div class="flex items-center justify-between pt-1">
<!-- Rewind 15s -->
<button aria-label="Skip backwards 15 seconds" class="w-12 h-12 rounded-xl flex items-center justify-center text-slate-300 hover:text-white active:bg-slate-800 transition">
<div class="relative flex items-center justify-center">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M12.066 11.2a1 1 0 000 1.6l5.334 4A1 1 0 0019 16V8a1 1 0 00-1.6-.8l-5.334 4zM4.066 11.2a1 1 0 000 1.6l5.334 4A1 1 0 0011 16V8a1 1 0 00-1.6-.8l-5.334 4z" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
<span class="text-[8px] font-bold absolute -bottom-1 text-slate-400">15</span>
</div>
</button>
<!-- Primary Play/Pause Button (56dp Highlighted Button) -->
<button aria-label="Pause replay" class="w-14 h-14 rounded-full bg-brand-cyan hover:bg-brand-cyanBright text-slate-950 flex items-center justify-center shadow-lg shadow-cyan-500/25 active:scale-95 transition">
<svg class="w-6 h-6 fill-current" viewbox="0 0 24 24"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"></path></svg>
</button>
<!-- Forward 15s -->
<button aria-label="Skip forward 15 seconds" class="w-12 h-12 rounded-xl flex items-center justify-center text-slate-300 hover:text-white active:bg-slate-800 transition">
<div class="relative flex items-center justify-center">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M11.933 12.8a1 1 0 000-1.6L6.6 7.2A1 1 0 005 8v8a1 1 0 001.6.8l5.333-4zM19.933 12.8a1 1 0 000-1.6l-5.333-4A1 1 0 0013 8v8a1 1 0 001.6.8l5.333-4z" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
<span class="text-[8px] font-bold absolute -bottom-1 text-slate-400">15</span>
</div>
</button>
<!-- Playback Speed Multiplier Pill (Min 48px Target) -->
<button aria-label="Replay speed toggle" class="h-12 px-3 rounded-xl bg-brand-cardSubtle border border-brand-border text-brand-cyan text-xs font-mono font-bold flex items-center justify-center hover:bg-slate-800 transition">
2.0x
</button>
</div>
</div>
<!-- END: Docked Telemetry Cluster & Playback Engine -->
<!-- Safe Area Bottom Guide Callout -->
<div class="safe-area-bottom-guide flex items-start justify-center pt-0.5">
<span class="text-[9px] font-mono text-brand-cyan/60 uppercase">env(safe-area-inset-bottom)</span>
</div>
</div>
</section>
</main>
<!-- END: DualViewportPresentationGrid -->
<!-- ========================================== -->
<!-- LOWER ARCHITECTURAL CALLOUT CARDS -->
<!-- ========================================== -->
<!-- BEGIN: ArchitecturalSpecSection -->
<section class="mt-8 border-t border-brand-border pt-10" data-purpose="architectural-spec-cards">
<div class="mb-6">
<h3 class="text-xl font-bold text-white flex items-center gap-2">
<span class="w-2 h-2 rounded bg-brand-cyan"></span>
System Architecture &amp; Interaction Specifications
</h3>
<p class="text-sm text-slate-400">Engineered constraints, platform layout standards, and user experience guarantees.</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<!-- CARD 1: Map Controls vs Telemetry Overlay Isolation -->
<article class="bg-brand-card border border-brand-border rounded-2xl p-5 flex flex-col justify-between">
<div>
<div class="w-10 h-10 rounded-xl bg-cyan-950/60 border border-brand-cyan/40 text-brand-cyan flex items-center justify-center mb-4">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
</div>
<h4 class="text-base font-bold text-white mb-2">1. Map Controls vs Telemetry Overlay</h4>
<p class="text-xs text-slate-300 leading-relaxed">
Floating map controls (+, -, layers) are strictly docked to the <strong>Top-Right quadrant</strong> with isolated pointer-event boundaries. They are decoupled from lower telemetry clusters to prevent scrub mis-taps while riding or reviewing.
</p>
</div>
<div class="mt-4 pt-3 border-t border-brand-border/60">
<span class="text-[10px] font-mono text-brand-cyan">Rule: 48dp separation zone</span>
</div>
</article>
<!-- CARD 2: Platform Safe Areas -->
<article class="bg-brand-card border border-brand-border rounded-2xl p-5 flex flex-col justify-between">
<div>
<div class="w-10 h-10 rounded-xl bg-blue-950/60 border border-blue-500/40 text-blue-400 flex items-center justify-center mb-4">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
</div>
<h4 class="text-base font-bold text-white mb-2">2. Platform Safe Areas</h4>
<p class="text-xs text-slate-300 leading-relaxed">
Dynamic padding utilizes <code class="text-brand-cyan font-mono text-[11px]">env(safe-area-inset-top)</code> (47px iOS island clearance) and <code class="text-brand-cyan font-mono text-[11px]">env(safe-area-inset-bottom)</code> (34px home indicator buffer) ensuring zero occlusion on gesture devices.
</p>
</div>
<div class="mt-4 pt-3 border-t border-brand-border/60">
<span class="text-[10px] font-mono text-blue-400">Rule: Full notch &amp; home bar immunity</span>
</div>
</article>
<!-- CARD 3: Telemetry Streaming/Buffering States -->
<article class="bg-brand-card border border-brand-border rounded-2xl p-5 flex flex-col justify-between">
<div>
<div class="w-10 h-10 rounded-xl bg-emerald-950/60 border border-brand-green/40 text-brand-green flex items-center justify-center mb-4">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
</div>
<h4 class="text-base font-bold text-white mb-2">3. Telemetry Streaming &amp; Cache</h4>
<p class="text-xs text-slate-300 leading-relaxed">
Sessions store IMU 100Hz packets in IndexedDB/SQLite. Visualizer displays green <span class="text-brand-green font-semibold">100% Buffered</span> state. When roaming without LTE, offline queue alerts notify riders of local persistence.
</p>
</div>
<div class="mt-4 pt-3 border-t border-brand-border/60">
<span class="text-[10px] font-mono text-brand-green">State: Zero packet loss cache</span>
</div>
</article>
<!-- CARD 4: Destructive Deletion Confirmation Pattern -->
<article class="bg-brand-card border border-brand-border rounded-2xl p-5 flex flex-col justify-between">
<div>
<div class="w-10 h-10 rounded-xl bg-red-950/60 border border-brand-danger/40 text-brand-danger flex items-center justify-center mb-4">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewbox="0 0 24 24"><path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path></svg>
</div>
<h4 class="text-base font-bold text-white mb-2">4. Destructive Deletion Pattern</h4>
<p class="text-xs text-slate-300 leading-relaxed">
Red (<span class="text-brand-danger font-semibold">#EF4444</span>) is strictly reserved for destructive deletion. Tapping the header trash icon requires an intentional two-step confirmation sheet to purge irreversible GPS and lean-angle logs.
</p>
</div>
<div class="mt-4 pt-3 border-t border-brand-border/60">
<span class="text-[10px] font-mono text-brand-danger">Security: 2-step verification modal</span>
</div>
</article>
</div>
</section>
<!-- END: ArchitecturalSpecSection -->
<!-- Footer Meta -->
<footer class="mt-12 text-center text-xs text-slate-500 pb-6 border-t border-brand-border/40 pt-6">
MotorSignal Telemetry Design System • High-Performance Motorcycle Telemetry Platform • Specification Screen
</footer>
</div>
<!-- END: MasterSpecificationContainer -->
</body></html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -0,0 +1,363 @@
# MotorSignal Platform — Prerelease Integrity & Evidence Reconciliation Report
**Date:** October 4, 2026
**Target Release:** `v1.0.2-build102` (Prerelease)
**Target Git Tag & Commit:** `v1.0.2-build102` (`bd278cd`)
**Active Branch:** `release/v1.0.2-build102-community-feed` | **HEAD:** `bd278cdfd3619242a3056c6a2738abb0aff70feb` (`bd278cd`)
**Auditor:** Antigravity Autonomous QA Coordinator & Independent Reviewer Suite
**Scope:** Final evidence reconciliation, 11-screenshot audit, gap verification, 44-row matrix mapping, and release-readiness review.
---
## 1. Executive Summary & Corrected QA Verdict
### 1.1 Corrected QA Verdict
**Verdict: CONDITIONAL APPROVAL FOR INTERNAL TESTING ONLY — BLOCK PUBLIC RELEASE CANDIDATE**
> [!NOTE]
> **Map Defects Remediated & Verified in Runtime:**
> 1. **Telemetry Replay Map (WEB-05) — VERIFIED PASS:** Resolved React 18 StrictMode unmount race and container destruction on tab switch. Verified live OpenStreetMap raster tiles, street labels (`US 19`, `NC Highway 141`, `Valley River`), multi-color lean angle polyline, START/FINISH badges, and responsive mobile rendering (390x844).
> 2. **Authenticated Formation Cockpit (WEB-12) — VERIFIED PASS:** Transitioned from abstract SVG graphic to real Web Mercator GIS map with switchable CartoDB Dark, ESRI Satellite, and OpenTopoMap contour tiles. Bound to live Firestore `live_presence` collection with 2 eligible participants (`rider-lead`, `rider-wing`), live position updates, heartbeat freshness lifecycle (<30s Active, 30-120s Stale, >120s Lost Link), and unauthorized access barrier. Public showcase simulation remains clearly labeled.
>
> **Remaining Non-Map Scope Gaps (Intentionally Separate Unfinished / Blocked Work):**
> 1. **Mobile Social Feed (SOC-01..03):** Client-side mock (`_memoryFeed`).
> 2. **Web Member Chat (CLB-01):** Founder portal admin-only; regular members have no web chat surface.
> 3. **Sync Integration Fixture:** Uses in-memory SQLite and mock SharedPreferences.
> 4. **Hardware & External Cloud Boundaries:** ELM327 OBD-II, Google Play Store billing, and Cloudflare R2 worker.
---
## 2. Screenshot & Evidence Audit (Preserved Historical & Current Artifacts)
Every browser screenshot located in `C:\Users\Bill\.gemini\antigravity\brain\96c4edb2-b5ed-4b9c-90e9-6888eb8a767f\` was inspected bit-for-bit. To preserve immutable evidence history without overwriting, all remediated areas maintain distinct Before and After artifacts:
### 2.1 Remediated Map & Cockpit Evidence (Before vs After Pairs)
| Artifact State | File Name | Size (Bytes) | Timestamp | SHA-256 Hash | Observed Rendering & Verdict |
| :--- | :--- | :---: | :---: | :---: | :--- |
| **BEFORE** (Failure) | `browser_telemetry_replay_map_before_failure.png` | 218,766 | 10/3/2026 8:46:17 PM | `B834F82127DFAE2A0155EF8480B3296245279AB650EB0404DCBED73EB29D946A` | **DEFECT REPRODUCED:** Black rectangle rendered behind HUD. Zero tile network requests. |
| **AFTER** (Fixed) | `browser_telemetry_replay_map_after_fixed.png` | 489,370 | 10/3/2026 10:07:56 PM | `C9E5C8588FA7E90288C343855211C8026651A9392918B08E99C4BE63ED9A35F1` | **RUNTIME PASS:** Full OpenStreetMap tiles with road labels (`US 19`, `NC 141`), speed/lean polyline, START/FINISH pins. |
| **BEFORE** (Failure) | `browser_telemetry_replay_mobile_before.png` | 1,015,601 | 10/3/2026 8:46:41 PM | `39764BC3138A34DB0AEF8C66163483DED13BD25E54A83425984A30101CDC4D81` | **DEFECT REPRODUCED (Mobile):** Pitch-black canvas behind HUD in mobile viewport (390x844). |
| **AFTER** (Fixed) | `browser_telemetry_replay_mobile_after_fixed.png` | 244,395 | 10/3/2026 10:11:03 PM | `813C2485F2C57C7E0780EFD34E5AD048B120C257B69F55A12046A15C7350DF8D` | **RUNTIME PASS (Mobile):** Responsive mobile modal layout verified; tiles and route geometry rendered cleanly. |
| **BEFORE** (Graphic) | `browser_radar_cockpit_before_svg.png` | 209,052 | 10/3/2026 8:52:38 PM | `8D0AE0C4D6EF3CC81CF6B27EAF456D78F870581183ED9C28528AB09483D075A3` | **INITIAL IMPLEMENTATION:** Abstract concentric SVG HUD rings with decorative SVG mode toggles. Not a GIS map. |
| **AFTER** (Fixed) | `browser_radar_cockpit_after_gis.png` | 92,596 | 10/3/2026 10:11:01 PM | `D6627AFB9BF6C937AB3DFE3F6D57989C04DD28AD63210FFD6A4C8C79D4EBD79E` | **RUNTIME PASS (GIS Cockpit):** Web Mercator projection, real CartoDB Dark raster tiles, 2 live Firestore participants. |
| **BEFORE** (Unclear) | `browser_radar_simulated_ping_before.png` | 207,858 | 10/3/2026 8:52:31 PM | `CCF5DA4FD22207955099A7C561F1378FCADD6CE5A82FAD1DC809A6198C49CABA` | **UNVERIFIED CLAIM:** Button labeled "Ping Swarm" only displayed a toast, implying physical mesh communication. |
| **AFTER** (Fixed) | `browser_radar_simulated_ping_after_labeled.png` | 187,537 | 10/3/2026 10:04:17 PM | `83B92544E44A3BDF50DC1ECE3B14D9676A58DEA051AA990E0A6005C8C393C079` | **RUNTIME PASS (Demo Labeling):** Labeled "Ping Swarm (Simulate Demo)" with banner disclaiming hardware execution. |
### 2.2 Supporting Browser Feature Artifacts
| Screenshot File | Size (Bytes) | Timestamp | SHA-256 Hash | URL & Action | Observed Outcome & Role | Data Fidelity |
| :--- | :---: | :---: | :---: | :--- | :--- | :--- |
| `browser_sessions_paginated.png` | 364,267 | 10/3/2026 8:47:45 PM | `A1E96E79EF624033440C712E133DD7F143090B53B6A694FDAD16D8DB061691E5` | `/account`<br>Clicked "Show more" (+10) | 20 cards rendered reverse-chronological (`ride-01`..`ride-20`). Button reads "Show more (+2 remaining)". Role: `qa-admin` | **PASS:** Verifies client-side 10 -> 20 pagination on seeded array. |
| `browser_device_sessions_paginated.png` | 290,193 | 10/3/2026 8:48:14 PM | `B5F8C74ACC444F58D21C597C13B8CA3D50EED33932B4C369A94AC8F454349963` | `/account`<br>Clicked "Show more sessions" | Header: "Sessions (Showing 20 of 22)". 20 device rows with "Revoke" button. Role: Admin | **MOCKED / INJECTED:** Injected into React state 22s prior; reverted 26s later. |
| `browser_billing_fallback_and_watch_status.png` | 398,031 | 10/3/2026 8:48:40 PM | `77C4C0089CF79B23C301D6325E8B7AD3D83C63C489CEF9016C1B65C16A15174F` | `/account`<br>Clicked "Manage Plan & Invoices" | Amber banner: "Web billing portal is currently unavailable (404)...". Watch: "Not Connected (Unavailable on Web)". Role: Signed-in QA user | **PASS:** Validates graceful 404 fallback handling & honest watch status. |
| `browser_group_ride_create_wizard.png` | 198,395 | 10/3/2026 8:49:32 PM | `D770F62D09C18882787316415AD1FE55B077486DD3F6B8872824A58318FF083A` | `/group-rides/manage`<br>Opened Create Ride modal | Step 1 active: Start Time (+1hr), Paved Asphalt, Brisk Pace, Limit 12 pre-filled. Role: Organizer | **PASS:** Validates sensible defaults and layout. |
| `browser_group_ride_review_step.png` | 181,124 | 10/3/2026 8:49:46 PM | `6347F620E39E9BFE108C8DCD2A4A8B784B21D3D8C9E6EC601227E564E7666037` | `/group-rides/manage`<br>Filled fields, clicked "Review" | Step 2 active: Pre-Flight Review summary cards with route parameters. Role: Organizer | **PASS:** Validates pre-flight parameter review. |
| `browser_group_ride_cross_account_discovered.png` | 160,060 | 10/3/2026 8:50:27 PM | `8CC06FAB66BE276C3E38F32F8193698F6E2EC7C33234FDCD36430693A05CADFB` | `/group-rides`<br>Loaded community rides tab | Published "Blood Mountain Canyon Sweep" visible with "Request Pack Slot" CTA. Role: Public visitor | **PASS:** Confirms public discovery of created ride. |
| `browser_chat_realtime_receipt.png` | 375,867 | 10/3/2026 8:57:39 PM | `D6FD3530D7C909FA14EA4B11C5AFD92539D41C49747058EAC695D0D9B0374FD4` | `/clubs/qa-isolated-riders/manage/chat`<br>Sent unique test messages | Displays 3 messages in `#general-squad`. User badge: "Club Founder - ADMIN". Role: Club Founder/Admin | **ADMIN-ONLY INTERFACE:** All 3 messages sent by QA Admin. Regular members have no web chat surface. |
---
## 3. Resolution of 4 Specific Technical Gaps
### 3.1 Cockpit Radar Map Transitioned to GIS Geographic Map (REMEDIATED & PASS)
- **Remediation in `website/components/group-rides/live-pack-radar.tsx` & `group-rides-management.tsx`:**
- Implemented interactive Web Mercator GIS canvas (`projectMercator`) with real raster tile rendering across 3 tactical modes:
- `tactical`: CartoDB Dark Matter raster tiles (`basemaps.cartocdn.com/rastertiles/dark_all/...`).
- `satellite`: ESRI World Imagery raster tiles (`server.arcgisonline.com/.../World_Imagery/...`).
- `elevation`: OpenTopoMap contour tiles (`tile.opentopomap.org/...`).
- Added pan-drag interaction, zoom controls (`+ / -`), and pack centroid recentering.
- Bound participant chevrons directly to live Firestore point-read doc streams on `group_rides/{rideId}/live_presence/{uid}`.
- **Runtime Verification in Browser (`browser_radar_cockpit_map.png`):**
- Authenticated as `qa-lead@motorsignal.example` (`rider-lead`).
- Navigated to `/group-rides?tab=radar&rideId=ride-blood-mtn-canyon`.
- Loaded real geographic map with 2 live pack nodes at true GPS coordinates (`34.7335, -83.9175`).
- Live position updates demonstrated real-time telemetry card updates (`52 MPH`, heading 50°).
- Freshness lifecycle verified: Active (<30s), Stale (30s-120s with lag callout), Disconnected (>120s with lost link).
- Telemetry access barrier verified: unauthorized rider is halted at security boundary (`telemetry-access-barrier`).
- Public showcase simulation strictly separated and badged with disclaimer (`browser_radar_simulated_ping.png`).
- **Verdict:** **REMEDIATED & VERIFIED PASS**. Real geographic map with live participant binding complete.
### 3.2 Device Session Revocation Test Fidelity & Backend Status
- **Backend Implementation Reality (`backend/motorsignal_api/app/main.py`):**
- Backend session revocation is **fully implemented** in FastAPI:
- `DELETE /api/v1/auth/sessions/{session_id}` (lines 774–784) verifies authorization, sets `session.revoked_at`, and publishes an `auth.session_revoked` audit event.
- `POST /api/v1/auth/logout-all` (lines 787–795) revokes all active sessions for the current user.
- Next.js API route `website/app/api/account/sessions/[sessionId]/route.ts` forwards client requests directly to `${MOTORSIGNAL_API_BASE_URL}/api/v1/auth/sessions/${sessionId}`.
- The feature is **NOT missing or unimplemented** in production source code.
- **Execution Reality in QA:**
- No FastAPI backend was running during the browser QA session (port 8000 was Docker, without an active python/uvicorn process).
- The 22 device sessions in `browser_device_sessions_paginated.png` were injected into React state via DevTools `evaluate_script` with identical timestamps (`8:47:52 PM`).
- 26 seconds later (`browser_billing_fallback_and_watch_status.png`), the session list reverted to "0 Sessions".
- **Resolution:** The browser test verified **client-side state removal and pagination UX only**. The backend implementation exists, but was **UNTESTED against a running FastAPI instance** in this mock browser environment.
### 3.3 Web Chat Surface & Role Boundaries
- **Code Reality (`website/app/clubs/[clubId]/manage/chat/page.tsx`):**
- The only chat route on the website is inside `FounderCommunityPage` at `/clubs/[clubId]/manage/chat`.
- In `website/components/founder-portal/founder-community-page.tsx` line 38:
```tsx
if (!result.permissions.canManageSettings && !result.isDemoFixture) setAccess('restricted');
```
- When a non-admin club member visits this route, lines 50–52 render:
> *"Management Access Restricted: Only club Owners and Admins can access this management page."*
(Proven by `browser_chat_member_restricted.png`).
- There is NO public or regular-member chat page on the website.
- **Execution Reality:**
- In `browser_chat_realtime_receipt.png`, the user is `Club Founder - ADMIN` and all messages displayed were sent by QA Admin.
- Cross-account chat delivery between Admin and Member was executed strictly in headless Node.js (`tools/verify_cross_account_chat_live.mjs`), never in the browser UI.
- **Resolution:** Web chat is strictly an **Admin Founder Portal tool**. Regular members have **ZERO web chat surface**.
### 3.4 Offline Sync Integration Fixture Realism & Architectural Consistency
- **Code Reality (`test/isolated_sync_integration_fixture_test.dart`):**
- Database initialized via `AppDatabase.forTesting(NativeDatabase.memory())` (in-memory SQLite).
- Preferences initialized via `SharedPreferences.setMockInitialValues({})` (in-memory mock).
- Phase C reconstructs `SyncOutbox(prefs)` and `SyncManager` passing the **exact same in-memory `prefs` and in-memory `db` objects**.
- **Execution Reality:**
- In an OS process termination, all memory heaps and in-memory databases are destroyed. A true restart requires re-reading from disk (`motorsignal.sqlite` and Android XML shared preferences).
- The fixture uses `MockClient` for HTTP PUT and `_IsolatedContractSyncProvider`. Zero traffic reaches Cloudflare R2 or Firebase.
- **Architectural Observation vs Feature Requirement:**
- **Distributed Two-Phase Commit (2PC):** Multi-cloud atomicity across Cloudflare R2 and Google Cloud Firestore is an **architectural observation**, not an outstanding or missing feature requirement. The production design deliberately employs safe sequential writes: R2 telemetry bundles must upload successfully before Firestore ride records are created/updated. If R2 fails, Firestore is never touched; if Firestore subsequently fails, the durable `SyncOutbox` retries the Firestore transaction idempotently using stable `cloudRideId`.
- **Resolution:** `prefs` and `db` in the fixture are **100% in-memory**. The fixture proves Dart state machine logic, queue ordering, and retry idempotency, but is classified as **simulated integration contract testing**, not proof of OS process termination or live cloud provider synchronization.
---
## 4. Authoritative 44-Scenario Reconciliation Matrix
Every single row from the authoritative baseline (`docs/QA_AUDIT_REPORT_2026-09-30.md`) is explicitly accounted for below, alongside the 3 Multi-Role contracts and 8 new Website Issue rows (**55 Total Authoritative Scenarios**):
### 4.1 Original 44 Baseline Rows
| Original ID | Subsystem | Surface | Baseline Claim | Reconciliation Status | Category | Evidence & Mapping Notes |
| :--- | :--- | :---: | :---: | :---: | :---: | :--- |
| **DASH-01** | Dashboard Minimal HUD | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `dash_dark.png` (Pixel 8 Pro smoke check) |
| **DASH-02** | Dashboard Sport Cockpit | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `dash_cockpit.png` (Pixel 8 Pro smoke check) |
| **DASH-03** | Dashboard Touring Cockpit | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `dash_touring.png` (Pixel 8 Pro smoke check) |
| **DASH-04** | Lifecycle Start Session | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `verify_session_started.png` (Pixel 8 Pro smoke check) |
| **DASH-05** | Lifecycle Stop Session | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `verify_session_stopped.png` (DEFECT-01 fixed) |
| **PERF-01** | 0–60 Launch Mode | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `perf_launch_armed.png` (Pixel 8 Pro smoke check) |
| **PERF-02** | 0–100 Launch Mode | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `perf_launch_mode.png` (Pixel 8 Pro smoke check) |
| **MAP-01** | Tactical Vector Map | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `map_dark.png` (Pixel 8 Pro smoke check) |
| **MAP-02** | Tactical Layer Sheet | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `tactical_layers_sheet.png` (Pixel 8 Pro smoke check) |
| **MAP-03** | Road Hazard Report | Mobile | PASS | **RETAINED PASS** | Runtime Pass | DEFECT-03 resolved; `quick_swarm_report_modal_test.dart` passes |
| **GAR-01** | NHTSA Make Search | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `verify_search_yamaha.png` (Pixel 8 Pro smoke check) |
| **GAR-02** | Model & Trim Selection | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `verify_select_model.png` (Pixel 8 Pro smoke check) |
| **GAR-03** | Add to Garage | Mobile | PASS | **RETAINED PASS** | Runtime Pass | DEFECT-02 resolved; attribution HTML sanitized |
| **GAR-04** | Maintenance Due | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `bike_maint.png` (Pixel 8 Pro smoke check) |
| **GAR-05** | Log Service Record | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `log_service.png` (Pixel 8 Pro smoke check) |
| **SOC-01** | Community Feed View | Mobile | PASS | **DOWNGRADED** | Mock / In-Memory | In-memory `_memoryFeed`; resets on app restart |
| **SOC-02** | Fist Bump Kudos | Mobile | PASS | **DOWNGRADED** | Mock / In-Memory | In-memory counter; resets on app restart |
| **SOC-03** | Comments Posting | Mobile | PASS | **DOWNGRADED** | Mock / In-Memory | In-memory list; resets on app restart |
| **SOC-04** | Telemetry Waveform | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `telemetry_graph.png` (Pixel 8 Pro smoke check) |
| **SOC-05** | Social Hub Navigation | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `verify_social_hub_tapped2.png` (Pixel 8 Pro smoke check) |
| **SOC-06** | Clubs Auth Gate | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `verify_clubs_auth_required.png` (Pixel 8 Pro smoke check) |
| **SOC-07** | Group Rides Auth Gate | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `verify_group_rides_auth_required.png` (Pixel 8 Pro smoke check) |
| **AUTH-01**| Mobile Sign In Form | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `verify_mobile_auth_attempt.png` (Pixel 8 Pro smoke check) |
| **AUTH-02**| Real Offline SQLite | Mobile | PASS | **RETAINED PASS** | Runtime Pass | 532 samples written to SQLite while offline (`verify_offline_recording.png`) |
| **SET-01** | Cockpit Dark Mode | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `settings_dark_active.png` (Pixel 8 Pro smoke check) |
| **SET-02** | Calibrate Mount Angle | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `calibrate_pressed.png` (Pixel 8 Pro smoke check) |
| **SET-03** | Sensor Diagnostics | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `sensor_diagnostics.png` (Pixel 8 Pro smoke check) |
| **ACT-01** | Personal Records Trophies | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `activity_dark.png` (Pixel 8 Pro smoke check) |
| **ACT-02** | Ride History List | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `verify_activity_tab.png` (Pixel 8 Pro smoke check) |
| **ACT-03** | Ride Detail Telemetry | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `verify_ride_detail.png` (Pixel 8 Pro smoke check) |
| **ACT-04** | GPX / CSV File Contents | Mobile | PASS | **RETAINED PASS** | Automated Test | Valid GPX 1.1; Pro CSV gating verified in unit test |
| **ACT-05** | 20Hz Ride Replay | Mobile | PASS | **RETAINED PASS** | Runtime Pass | `replay_ride1.png` (Pixel 8 Pro smoke check) |
| **WEB-01** | Web Homepage & Features | Web | PASS | **RETAINED PASS** | Runtime Pass | `browser_home.png` (Chromium DevTools E2E) |
| **WEB-02** | Group Rides Showcase | Web | PASS | **RETAINED PASS** | Runtime Pass | `browser_group_rides_filtered.png` (Chromium DevTools E2E) |
| **WEB-03** | Web Hub / Clubs Gates | Web | PASS | **RETAINED PASS** | Runtime Pass | `browser_group_rides_manage_gate.png` (Chromium DevTools E2E) |
| **WEB-04** | Web Account 401 Gate | Web | PASS | **RETAINED PASS** | Runtime Pass | `browser_account_gate.png` (Chromium DevTools E2E) |
| **OPS-01** | Ops Console 307 Redirect| Web | PASS | **RETAINED PASS** | Runtime Pass | `browser_admin_gate.png` (Chromium DevTools E2E) |
| **OPS-02** | Dual Speedtest | Web | PASS | **RETAINED PASS** | Automated Test | `tests/operations-speedtest.test.tsx` (6/6 passing) |
| **OPS-03** | Support Ticket Modal | Web | PASS | **RETAINED PASS** | Automated Test | `tests/support-report-modal.test.tsx` (8/8 passing) |
| **OPS-04** | CMS Revision Workflow | Web | PASS | **RETAINED PASS** | Automated Test | `website/tests/ops-experience.test.cjs` (44/44 passing) |
| **HW-01** | BLE OBD-II CAN Bus | Mobile | BLOCKED | **RETAINED BLOCKED** | Blocker | Physical ELM327 Bluetooth adapter required |
| **HW-02** | Road Vibration Fusion | Mobile | HW REQ | **RETAINED CARRIED** | Hardware Req | Motorcycle dynamic road test required |
| **HW-03** | Cellular Handoff & GPS | Mobile | HW REQ | **RETAINED CARRIED** | Hardware Req | Field transit required |
| **HW-04** | Solar Thermal Load | Mobile | HW REQ | **RETAINED CARRIED** | Hardware Req | Outdoor cockpit test required |
### 4.2 Multi-Role Contracts
| ID | Area | Surface | Status | Category | Evidence & Reality |
| :--- | :--- | :---: | :---: | :---: | :--- |
| **GRP-01** | Multi-Role Group Ride | Mobile | **PASS** | Runtime Pass | Join request, approve, decline verified in Pixel 8 Pro emulator |
| **CLB-01** | Club Member Roster | Direct SDK / Web | **PARTIAL** | Untested / Blocker | SDK rules pass; member web roster interface unbuilt |
| **CLB-02** | Club Administration | Direct SDK / Web | **PASS** | Runtime Pass | Live chat, route planning, wall posts pass in Founder Portal |
### 4.3 New Website Issues Evaluated (Distinct Rows)
| ID | Feature / Defect Area | Surface | Status | Category | Evidence & Reality |
| :--- | :--- | :---: | :---: | :---: | :--- |
| **WEB-05** | Telemetry Forensic Replay Map | Web | **PASS** | Runtime Pass | `browser_telemetry_replay_map.png` & `browser_telemetry_replay_mobile.png` render live OSM tiles, street labels (`US 19`, `NC 141`), lean heatmap, and pins. |
| **WEB-06** | Recent Sessions Pagination | Web | **PASS** | Runtime Pass | `browser_sessions_paginated.png` (10 + "Show more" 10 on seeded array) |
| **WEB-07** | Active Devices Pagination | Web | **PASS (UI Only)** | Mock / In-Memory | `browser_device_sessions_paginated.png` (Synthetic injected sessions) |
| **WEB-08** | Billing Portal Graceful Fallback| Web | **PASS** | Runtime Pass | `browser_billing_fallback_and_watch_status.png` (Graceful 404 banner) |
| **WEB-09** | Apple Watch Status Clarity | Web | **PASS** | Runtime Pass | Displays `"Not Connected (Unavailable on Web)"` |
| **WEB-10** | Group Ride Public Discovery | Web | **PASS** | Runtime Pass | `browser_group_ride_cross_account_discovered.png` (API returns 200) |
| **WEB-11** | 2-Step Group Ride Create Wizard| Web | **PASS** | Runtime Pass | `browser_group_ride_create_wizard.png` & `browser_group_ride_review_step.png` |
| **WEB-12** | Live Radar HUD & Formation Map | Web | **PASS** | Runtime Pass | `browser_radar_cockpit_map.png` renders real GIS Web Mercator tiles with 2 live Firestore participants; public demo clearly badged in `browser_radar_simulated_ping.png`. |
### 4.4 Matrix Provenance & Exact Set Differences
- **Baseline Source vs Current JSON Comparison:**
- Original baseline source: [`docs/QA_AUDIT_REPORT_2026-09-30.md`](file:///c:/Users/Bill/Desktop/flutter-app/docs/QA_AUDIT_REPORT_2026-09-30.md) Section 4 contains **exactly 44 unique scenario IDs**:
- Mobile: `DASH-01..05`, `PERF-01..02`, `MAP-01..03`, `GAR-01..05`, `SOC-01..07`, `AUTH-01..02`, `SET-01..03`, `ACT-01..05` (32 IDs, including `SOC-06` and `SOC-07`)
- Web Browser: `WEB-01..04`, `OPS-01` (5 IDs)
- Automated Web: `OPS-02..04` (3 IDs)
- Hardware: `HW-01..04` (4 IDs, including `HW-03` and `HW-04`)
- Current machine-readable dataset: [`docs/qa-2026-10-01/scenarios_machine_readable.json`](file:///c:/Users/Bill/Desktop/flutter-app/docs/qa-2026-10-01/scenarios_machine_readable.json) contains **exactly 55 unique scenario IDs**.
- **Exact Set Difference Analysis:**
- **Omissions from Original Baseline:** $\text{Original} \setminus \text{Current JSON} = \emptyset$ (**0 IDs omitted**; every single original ID is preserved).
- **Additions to Current Scope:** $\text{Current JSON} \setminus \text{Original} = \mathbf{11\text{ IDs}}$:
- 3 Multi-Role Group/Club Contracts: `GRP-01`, `CLB-01`, `CLB-02` (promoted from Section 10 narrative into formal scenario rows).
- 8 New Website Audit Issues: `WEB-05` (Replay Map), `WEB-06` (Sessions Pagination), `WEB-07` (Devices Pagination), `WEB-08` (Billing Fallback), `WEB-09` (Apple Watch Status), `WEB-10` (Group Ride Discovery), `WEB-11` (Group Ride Wizard), `WEB-12` (Formation Radar Cockpit).
- **Renamed IDs:** $\emptyset$ (**0 IDs renamed**; all original IDs preserve identical string identifiers).
- **Mathematical Proof:** $\text{Original Baseline (44)} + \text{Additions (11)} - \text{Omissions (0)} = \mathbf{55\text{ Authoritative Scenarios}}$.
- **Baseline ID Range Analysis (42 vs 44 Reconciliation):**
- When baseline ID ranges were summarized with Social scoped to `SOC-01..05` (5 items) instead of `SOC-01..07` (7 items), the enumerated ranges totaled **exactly 42**:
$$\text{DASH (5)} + \text{PERF (2)} + \text{MAP (3)} + \text{GAR (5)} + \mathbf{\text{SOC-01..05 (5)}} + \text{AUTH (2)} + \text{SET (3)} + \text{ACT (5)} + \text{WEB (4)} + \text{OPS (4)} + \text{HW (4)} = \mathbf{42}$$
- The **genuinely missing IDs** from that 42-count range list are **`SOC-06` (Clubs Auth Gate)** and **`SOC-07` (Group Rides Auth Gate)**.
- When `SOC-06` and `SOC-07` are restored (`SOC-01..07`, 7 items), the baseline range sum equals the full authoritative 44 baseline scenarios:
$$32\text{ (Mobile)} + 4\text{ (Web Browser)} + 4\text{ (Web Ops)} + 4\text{ (Hardware)} = \mathbf{44}$$
- Adding the 11 audited additions (`GRP-01`, `CLB-01`, `CLB-02`, and `WEB-05`..`12`) yields the final complete scope:
$$44 + 11 = \mathbf{55\text{ Authoritative Scenarios}}$$
- All category classifications, percentages, and totals below are derived directly from the exact 55-element machine-readable dataset ([`scenarios_machine_readable.json`](file:///c:/Users/Bill/Desktop/flutter-app/docs/qa-2026-10-01/scenarios_machine_readable.json)).
### 4.5 Category Tally Directly Derived from Machine-Readable Dataset
```
┌────────────────────────────────────────────────────────────────────────────────────────┐
│ RECONCILED CATEGORY TOTALS │
├──────────────────────────────────────────┬───────────┬───────────────┬─────────────────┤
│ Category │ Count │ Percentage │ Status │
├──────────────────────────────────────────┼───────────┼───────────────┼─────────────────┤
│ 1. Runtime Pass (Legacy Smoke Evidence) │ 30 / 55 │ 54.5% │ PASS (HISTORIC) │
│ 2. Runtime Pass (Current Tested Build) │ 12 / 55 │ 21.8% │ PASS (CURRENT) │
│ 3. Automated Test Suite Passes │ 4 / 55 │ 7.3% │ PASS │
│ 4. Mocked / In-Memory Functionality │ 4 / 55 │ 7.3% │ MOCKED / IN-MEM │
│ 5. Hardware Required (Physical Transit) │ 3 / 55 │ 5.5% │ HW REQUIRED │
│ 6. Blocked External Dependencies (HW-01) │ 1 / 55 │ 1.8% │ BLOCKED │
│ 7. Untested User Surfaces (CLB-01) │ 1 / 55 │ 1.8% │ UNTESTED │
├──────────────────────────────────────────┼───────────┼───────────────┼─────────────────┤
│ Total Scenarios │ 55 / 55 │ 100.0% │ AUDITED │
└──────────────────────────────────────────┴───────────┴───────────────┴─────────────────┘
```
*(Supporting Verification Gates: `npm run test:components` 165/165 passed across 16 files, `npm run build` 37/37 routes OK, `flutter test` 1,431 passed, `flutter analyze` 0 issues, `firebase/emulator-tests` 222 passed, `ops-experience` 44 passed).*
---
## 5. Working Tree Governance & Process Separation
### 5.1 Process State vs Clean Working Tree Distinction
- **Test Processes (Stopped):** All QA test processes have been terminated cleanly:
- Node.js Next.js development server on port 3100: **STOPPED**
- Firebase emulator suite (ports 9099, 8088, 8085): **STOPPED**
- ADB emulator shell sessions and port forwarders: **STOPPED**
- No background test listeners or orphaned ports remain active.
- **Git Working Tree (Preserved Dirty State):** The working tree is deliberately dirty to preserve pre-existing user changes, uncommitted QA tools, test fixtures, and rules enhancements across the repository. In accordance with strict instructions, no files have been committed, reset, staged, or pushed.
### 5.2 Tracked Modified Files (22 Files)
```text
docs/qa-2026-10-01/auth.cleanup-results.json
docs/qa-2026-10-01/auth.extra-results.json
docs/qa-2026-10-01/auth.firebase.json
docs/qa-2026-10-01/auth.firestore.rules
docs/qa-2026-10-01/auth.results.json
firebase/emulator-tests/club-wall.rules.test.js
firestore.rules
lib/features/group_rides/group_ride_detail_screen.dart
lib/services/clubs/club_providers.dart
lib/services/group_rides/group_ride_models.dart
lib/services/group_rides/group_ride_providers.dart
lib/services/group_rides/group_ride_service.dart
lib/services/location_service.dart
test/billing_lifecycle_test.dart
test/purchase_verification_service_test.dart
tools/qa_auth_main.dart
website/app/account/page.tsx
website/components/app-cloud-data.tsx
website/components/group-rides/group-rides-management.tsx
website/components/group-rides/live-pack-radar.tsx
website/components/web-telemetry-replay-modal.tsx
website/lib/firebase-admin.ts
```
### 5.3 Untracked Verification Artifacts (11 Files)
```text
docs/prerelease-v1.0.2-build102-followup-audit.md
docs/qa-2026-10-01/auth.verify-direct-sdk-results.json
docs/qa-2026-10-01/auth.verify-routes-chat-mobile.json
docs/qa-2026-10-01/auth.verify-routes-chat-mobile.mjs
docs/qa-2026-10-01/chat.cross-account-results.json
firebase/emulator-tests/club-routes-chat.rules.test.js
test/isolated_sync_integration_fixture_test.dart
test/location_permission_refresh_test.dart
test/pro_csv_export_verification_test.dart
tools/verify_cross_account_chat_live.mjs
tools/verify_isolated_e2e.mjs
```
### 5.4 Diagnostic & Scratch Tools (Preserved Non-Production)
```text
tools/check_participant.mjs
tools/check_synced_rides.mjs
tools/inspect_sqlite.py
tools/seed_profiles.mjs
tools/setup_e2e_rides.mjs
tools/submit_admin_request.mjs
tools/test_dart_transaction.mjs
tools/test_join_requests_endpoint.mjs
tools/test_probe.mjs
tools/test_verify_token.mjs
website/check_requests.cjs
website/test_verify.cjs
.firebase-tmp/
```
### 5.5 Test Account Cleanup Status
- Role promotions (`qa-member` promoted to admin for Founder Portal chat receipt test) must be revoked prior to production deployment.
- Seeded test rides (`ride-blood-mtn-canyon`) and test chat messages exist strictly within the local Firebase emulator database (`demo-motorsignal-qa`), with **zero contamination of production cloud services**.
---
## 6. Release-Readiness Assessment
### 6.1 Verified Fixes
- **Web Telemetry Replay Map (WEB-05):** Resolved React 18 StrictMode unmount race, persistent container sizing, CSS hidden/block tab switching, and multi-CDN loader. Verified high-contrast OpenStreetMap raster tiles, street labels (`US 19`, `NC 141`), lean angle heatmap trace, START/FINISH pins, and mobile responsive rendering (390x844).
- **Authenticated Formation Cockpit & Live Radar (WEB-12):** Transitioned to real Web Mercator GIS map with switchable CartoDB Dark, ESRI Satellite, and OpenTopoMap contour tiles. Bound to live Firestore `live_presence` collection with 2 eligible participants (`rider-lead`, `rider-wing`), live position updates, freshness lifecycle (Active/Stale/Disconnected), and unauthorized access barrier. Public simulation clearly separated and badged.
- **Recent Telemetry Sessions Pagination:** Capped at 10 initial with "+10" expansion (`visibleRideCount`), reverse-chronological ordering, accurate totals, and actions preserved.
- **Active Device Sessions UI Pagination:** Sliced at 10 initial with "+10" expansion.
- **Billing Portal Fallback:** Connected to `/api/billing/portal` with dismissible unconfigured 404 banner.
- **Apple Watch Companion Honesty:** Replaced placeholder sync with `"Not Connected (Unavailable on Web)"`.
- **Group Rides Error Banner Elimination:** Replaced floating red error with dedicated in-tab loading skeletons, empty state with filters/CTAs, and retryable error cards.
- **2-Step Group Ride Create Wizard:** Sensible defaults, plain labels, validation, and pre-flight review.
- **Billing Test Fixes:** Dynamic relative time fixtures (`add/subtract Duration`) replacing static future years.
- **Firestore Security Rules:** 222 / 222 rules unit tests passed.
### 6.2 Explicit Feature Gaps & Untested Behavior Distinctions
#### A. Unimplemented Features (Missing Code)
1. **Mobile Social Persistence (`SOC-01`..`03`):**
- *Status:* **UNIMPLEMENTED IN CODE**
- *Detail:* `lib/features/social/feed/social_feed_screen.dart` uses an in-memory collection (`_memoryFeed`). Feed posts, comments, and fist-bump reactions operate strictly in local memory and are not backed by Firestore collections or security rules. State resets upon application process restart.
2. **Regular-Member Web Chat (`CLB-01`):**
- *Status:* **UNIMPLEMENTED IN CODE**
- *Detail:* The web squad chat interface exists solely within the Founder Portal at `/clubs/[clubId]/manage/chat`. It enforces strict role gates (`isClubAdmin(uid) || isCallerClubOwner()`). No web chat route or component exists for regular club members.
#### B. Implemented Features with Untested Behavior in QA (Verification Gaps)
3. **Completed Device-to-Backend Cloud Sync:**
- *Status:* **SIMULATED INTEGRATION / UNTESTED E2E**
- *Detail:* Verified via isolated unit/contract test `test/isolated_sync_integration_fixture_test.dart` using Drift Schema 7 `AppDatabase`, `SyncOutbox`, and `SyncBundleWorker`. However, the test runs with `NativeDatabase.memory()`, mock `SharedPreferences`, and mock HTTP clients. Live end-to-end device-to-backend upload against Cloudflare R2 and production Firestore remains unverified.
4. **Backend Session Revocation Verification:**
- *Status:* **IMPLEMENTED IN BACKEND / UNTESTED IN BROWSER QA**
- *Detail:* The session termination endpoints are implemented in the FastAPI backend (`DELETE /api/v1/auth/sessions/{id}` and `POST /api/v1/auth/logout-all` in `backend/motorsignal_api/app/main.py`) and proxied by Next.js (`/api/account/sessions/[sessionId]`). However, during browser QA, the FastAPI backend was not running; session revocation was demonstrated only via client-side state manipulation in the `Active Devices` component list. Server-side session invalidation remains untested in this environment.
#### C. Unconfigured Environment Fallback
5. **Successful Billing Portal Access:**
- *Status:* **UNCONFIGURED PREVIEW / GRACEFUL FALLBACK**
- *Detail:* The Next.js endpoint `/api/billing/portal` is wired and tested. In preview and local test environments, Stripe Customer Portal is unprovisioned, so the route returns a graceful HTTP 404 fallback banner (`"Billing portal is currently unavailable in this preview environment"`). Actual redirection to an active Stripe portal session remains unverified.
#### D. Hardware-Dependent Verification (Physical Boundaries)
6. **Physical Hardware Scenarios (`HW-01`..`HW-04`):**
- **`HW-01` (Bluetooth OBD-II CAN Bus Link):** **BLOCKED** — Requires physical ELM327 BLE hardware adapter and motorcycle CAN bus.
- **`HW-02` (6-Axis Road Vibration IMU Fusion):** **HARDWARE REQUIRED** — Requires physical motorcycle road test under 100–300 Hz engine harmonics.
- **`HW-03` (Cellular Carrier Handoff & GPS Dead-Zones):** **HARDWARE REQUIRED** — Requires physical highway transit through real RF shadow zones.
- **`HW-04` (Direct Sunlight Solar Thermal Dissipation):** **HARDWARE REQUIRED** — Requires outdoor motorcycle cockpit mount under sustained direct sunlight and thermal load.
### 6.3 Readiness for Code Review
**The branch IS READY for code review of all verified fixes (telemetry replay map, authenticated formation cockpit, security rules, 2-step group ride flow, billing relative fixtures, and account fallbacks).**
However, **PUBLIC RELEASE CANDIDATE MUST REMAIN CONDITIONALLY GATED** until external hardware, production billing provisioning, and cross-account cloud sync dependencies are validated in staging.

View File

@ -1,6 +1,6 @@
{
"project": "demo-motorsignal-qa",
"executedAt": "2026-10-01T10:51:32.634Z",
"executedAt": "2026-10-03T16:00:31.398Z",
"results": [
{
"service": "auth",

View File

@ -1,6 +1,6 @@
{
"projectId": "demo-motorsignal-qa",
"executedAt": "2026-10-01T05:49:09.255Z",
"executedAt": "2026-10-03T18:21:35.368Z",
"kind": "Real Auth and Firestore emulator SDK; not UI E2E",
"results": [
{

View File

@ -1,2 +1,2 @@
{"firestore":{"rules":"auth.firestore.rules"},"emulators":{"auth":{"host":"127.0.0.1","port":9099},"firestore":{"host":"127.0.0.1","port":8088},"ui":{"enabled":false},"singleProjectMode":false}}
{"firestore":{"rules":"auth.firestore.rules"},"emulators":{"auth":{"host":"0.0.0.0","port":9099},"firestore":{"host":"0.0.0.0","port":8088},"ui":{"enabled":false},"singleProjectMode":false}}

View File

@ -1254,6 +1254,241 @@ service cloud.firestore {
allow delete: if false;
}
// --- CLUB WALL ---
// Uses the existing mobile wall_posts model (embedded comments).
// Membership gives access to club discussions only, never ride presence.
match /wall_posts/{postId} {
function canUseWall() {
return isRegisteredUser()
&& getClubDoc().data.archivedAt == null
&& !isBanned(request.auth.uid)
&& (isCallerClubOwner() || isActiveMember(request.auth.uid));
}
function canModerateWall() {
return isCallerClubOwner() || isClubAdmin(request.auth.uid);
}
function validWallAuthor(data) {
return data.authorUid == request.auth.uid
&& data.authorDisplayName is string
&& data.authorDisplayName.size() > 0 && data.authorDisplayName.size() <= 100
&& ((isCallerClubOwner() && data.authorRole == 'owner')
|| (!isCallerClubOwner() && data.authorRole == memberData(request.auth.uid).role))
&& (!('authorHandle' in data) || (data.authorHandle is string && data.authorHandle.size() <= 100))
&& (!('authorAvatarUrl' in data) || isValidClubImageUrl(data.authorAvatarUrl))
&& (!('authorDisplayRank' in data) || (data.authorDisplayRank is string
&& data.authorDisplayRank == memberData(request.auth.uid).displayRank));
}
function validWallContent(data, maxLength) {
return data.content is string && data.content.trim().size() > 0 && data.content.size() <= maxLength;
}
function validPostCreate() {
let data = request.resource.data;
return data.keys().hasOnly(['id', 'clubId', 'authorUid', 'authorDisplayName',
'authorHandle', 'authorAvatarUrl', 'authorDisplayRank', 'authorRole',
'content', 'createdAt', 'isPinned', 'fistBumpCount', 'fistBumpUserIds', 'comments'])
&& data.id == postId && data.clubId == clubId
&& validWallAuthor(data) && validWallContent(data, 5000)
&& data.createdAt is timestamp
&& data.isPinned is bool && (!data.isPinned || canModerateWall())
&& data.fistBumpCount == 0 && data.fistBumpUserIds == [] && data.comments == [];
}
function validReplyAppend() {
let old = resource.data.comments;
let next = request.resource.data.comments;
let reply = next[next.size() - 1];
return request.resource.data.diff(resource.data).affectedKeys().hasOnly(['comments'])
&& old is list && next is list && old.size() < 200
&& next.size() == old.size() + 1 && next == old.concat([reply])
&& reply is map
&& reply.keys().hasOnly(['id', 'postId', 'authorUid', 'authorDisplayName',
'authorHandle', 'authorAvatarUrl', 'authorDisplayRank', 'authorRole', 'content', 'createdAt'])
&& reply.id is string && reply.id.size() > 0 && reply.id.size() <= 100
&& reply.postId == postId && validWallAuthor(reply) && validWallContent(reply, 2000)
// Mobile serializes embedded comment timestamps as ISO strings.
&& reply.createdAt is string && reply.createdAt.size() <= 40
&& reply.createdAt.matches('^[0-9]{4}-[0-9]{2}-[0-9]{2}T.*Z$');
}
function validFistBumpToggle() {
let old = resource.data.fistBumpUserIds;
let next = request.resource.data.fistBumpUserIds;
return request.resource.data.diff(resource.data).affectedKeys().hasOnly(['fistBumpCount', 'fistBumpUserIds'])
&& next is list && next.size() <= 5000
&& next.size() == next.toSet().size()
&& request.resource.data.fistBumpCount == next.size()
&& ((!(request.auth.uid in old) && next == old.concat([request.auth.uid]))
|| (request.auth.uid in old && next == old.removeAll([request.auth.uid])));
}
allow read: if canUseWall();
allow create: if canUseWall() && validPostCreate();
allow update: if canUseWall() && (
(canModerateWall()
&& request.resource.data.diff(resource.data).affectedKeys().hasOnly(['isPinned'])
&& request.resource.data.isPinned is bool)
|| validReplyAppend() || validFistBumpToggle()
);
allow delete: if canUseWall() && (canModerateWall() || resource.data.authorUid == request.auth.uid);
}
// --- CLUB ROUTES ---
// Curated motorcycle routes created by club members or owner.
// Read access is public if club is public; otherwise active members/owner only.
// Deletions are restricted to club admins and owner.
match /routes/{routeId} {
function canReadClubRoutes() {
return isRegisteredUser()
&& !isBanned(request.auth.uid)
&& (
getClubDoc().data.visibility == 'public'
|| isCallerClubOwner()
|| isActiveMember(request.auth.uid)
);
}
function isValidClubRoute(data) {
return data.keys().hasAll([
'id', 'clubId', 'title', 'description', 'distanceMiles',
'estimatedMinutes', 'elevationGainFt', 'curvatureRating',
'surfaceType', 'startLocation', 'endLocation', 'curatorUid',
'curatorName', 'createdAt'
])
&& data.keys().hasOnly([
'id', 'clubId', 'title', 'description', 'distanceMiles',
'estimatedMinutes', 'elevationGainFt', 'curvatureRating',
'surfaceType', 'startLocation', 'endLocation', 'gpxUrl',
'curatorUid', 'curatorName', 'createdAt'
])
&& data.id == routeId
&& data.clubId == clubId
&& data.curatorUid == request.auth.uid
&& data.curatorName is string
&& data.curatorName.size() >= 1 && data.curatorName.size() <= 100
&& data.title is string
&& data.title.size() >= 1 && data.title.size() <= 120
&& data.description is string
&& data.description.size() <= 1000
&& (data.distanceMiles is int || data.distanceMiles is float)
&& data.distanceMiles > 0
&& (data.estimatedMinutes is int || data.estimatedMinutes is float)
&& data.estimatedMinutes > 0
&& (data.elevationGainFt is int || data.elevationGainFt is float)
&& data.elevationGainFt >= 0
&& data.curvatureRating in ['High Twisties', 'Moderate', 'Scenic Cruise', 'Track Pace']
&& data.surfaceType in ['Paved Asphalt', 'Mixed Surface', 'Mountain Pass']
&& data.startLocation is string
&& data.startLocation.size() >= 1 && data.startLocation.size() <= 120
&& data.endLocation is string
&& data.endLocation.size() >= 1 && data.endLocation.size() <= 120
&& (!('gpxUrl' in data) || data.gpxUrl == null || (data.gpxUrl is string && data.gpxUrl.size() <= 2048))
&& data.createdAt is timestamp;
}
function canCreateClubRoute() {
return isRegisteredUser()
&& getClubDoc().data.archivedAt == null
&& !isBanned(request.auth.uid)
&& (isCallerClubOwner() || isActiveMember(request.auth.uid))
&& isValidClubRoute(request.resource.data);
}
function canDeleteClubRoute() {
return isRegisteredUser()
&& getClubDoc().data.archivedAt == null
&& !isBanned(request.auth.uid)
&& (isCallerClubOwner() || isClubAdmin(request.auth.uid));
}
allow read: if canReadClubRoutes();
allow create: if canCreateClubRoute();
allow update: if false;
allow delete: if canDeleteClubRoute();
}
// --- CLUB CHAT MESSAGING ---
// Real-time squad chat messaging partitioned by channels.
// General channels (general, ride-intel): active members and owner.
// Officers channel: club admin and owner only.
// Messages are immutable once posted.
match /chat_channels/{channelId}/messages/{messageId} {
function canReadChatMessage() {
return isRegisteredUser()
&& getClubDoc().data.archivedAt == null
&& !isBanned(request.auth.uid)
&& (
channelId == 'officers'
? (isCallerClubOwner() || isClubAdmin(request.auth.uid))
: (isCallerClubOwner() || isActiveMember(request.auth.uid))
);
}
function validChatAuthor(data) {
return data.authorUid == request.auth.uid
&& data.authorDisplayName is string
&& data.authorDisplayName.size() >= 1 && data.authorDisplayName.size() <= 100
&& ((isCallerClubOwner() && data.authorRole == 'owner')
|| (!isCallerClubOwner() && data.authorRole == memberData(request.auth.uid).role))
&& (!('authorHandle' in data) || data.authorHandle == null || (data.authorHandle is string && data.authorHandle.size() <= 100))
&& (!('authorDisplayRank' in data) || data.authorDisplayRank == null || (
data.authorDisplayRank is string && data.authorDisplayRank.size() <= 100
&& (isCallerClubOwner() || ('displayRank' in memberData(request.auth.uid) && data.authorDisplayRank == memberData(request.auth.uid).displayRank))
));
}
function isValidChatMessage(data) {
return data.keys().hasAll([
'id', 'clubId', 'channelId', 'content', 'authorUid',
'authorDisplayName', 'authorRole', 'createdAt'
])
&& data.keys().hasOnly([
'id', 'clubId', 'channelId', 'content', 'authorUid',
'authorDisplayName', 'authorRole', 'authorHandle',
'authorDisplayRank', 'createdAt'
])
&& data.id == messageId
&& data.clubId == clubId
&& data.channelId == channelId
&& data.content is string
&& data.content.trim().size() > 0
&& data.content.size() <= 1000
&& data.createdAt is timestamp
&& validChatAuthor(data);
}
function canCreateChatMessage() {
return isRegisteredUser()
&& getClubDoc().data.archivedAt == null
&& !isBanned(request.auth.uid)
&& (
channelId == 'officers'
? (isCallerClubOwner() || isClubAdmin(request.auth.uid))
: (isCallerClubOwner() || isActiveMember(request.auth.uid))
)
&& isValidChatMessage(request.resource.data);
}
function canDeleteChatMessage() {
return isRegisteredUser()
&& getClubDoc().data.archivedAt == null
&& !isBanned(request.auth.uid)
&& (
channelId == 'officers'
? (isCallerClubOwner() || isClubAdmin(request.auth.uid))
: (resource.data.authorUid == request.auth.uid || isCallerClubOwner() || isClubAdmin(request.auth.uid))
);
}
allow read: if canReadChatMessage();
allow create: if canCreateChatMessage();
allow update: if false;
allow delete: if canDeleteChatMessage();
}
// --- ADMINISTRATIVE BANS SUBCOLLECTION ---
match /bans/{bannedUid} {
allow read, write: if isRegisteredUser() && isClubAdmin(request.auth.uid);

View File

@ -3,27 +3,27 @@
"kind": "Real Auth + Firestore emulators via SDK, not UI E2E",
"accounts": {
"owner": {
"uid": "HMlGy4DNyJUUR4CDr43aThYQX8nd",
"uid": "oiTCNDzizWElfSWfa1GYtcDvxWuq",
"email": "qa-owner@motorsignal.example"
},
"admin": {
"uid": "vgQtjVSuz5aPATM2ghUVxUuProG3",
"uid": "Mub6ejs3oIuVyxsXqdJI1TF7XRjz",
"email": "qa-admin@motorsignal.example"
},
"organizer": {
"uid": "iPE9feykd6rJqs7b7gvxGkhAkJz3",
"uid": "e1bEZPhCgWiEEzKgdOhO6qvs0ODp",
"email": "qa-organizer@motorsignal.example"
},
"participant": {
"uid": "xvMH3PcgmZZeVpvVk3HbUKx5bRzd",
"uid": "uVKcwcRlv2nzmNXUtZQCf1KAtWXj",
"email": "qa-participant@motorsignal.example"
},
"member": {
"uid": "5O24hx3Hvoct5YE0dtT8Q0eMqWen",
"uid": "a8DDZOUIDPsEGb29tkaikjSNHGUF",
"email": "qa-member@motorsignal.example"
},
"outsider": {
"uid": "HcgPrXi4vX8AOvsXXDS856ySWj7G",
"uid": "z5Mb3oOVMbNQMYT936S50Kw4fXkC",
"email": "qa-outsider@motorsignal.example"
}
},

View File

@ -0,0 +1,75 @@
# Authenticated website verification — October 4, 2026
The focused local phase passed 15 browser/backend checks and 42 relevant automated tests. The website compiled successfully, including TypeScript, in one build. These results establish isolated website behavior; they do not establish authenticated production behavior.
| Workflow | Result | Evidence and limits |
| --- | --- | --- |
| Telemetry sessions | PASS, isolated Firebase E2E | 25 owned sessions; initial 10, then 20 and 25; newest-first ordering; replay, GPX/CSV downloads, and deletion verified through the browser and backend. The fixtures use the website's supported inline telemetry format; R2 bundle retrieval was not tested. |
| Device sessions | PASS for website UI/proxy; actual backend unverified | 25 sessions from the existing token-verifying local fixture API; initial 10, then 20 and 25; newest-first ordering, current-session protection, retained revoke buttons, and successful revoke verified. This is not PostgreSQL session-service E2E. |
| Community discovery | PASS, isolated Firebase E2E | Populated and empty results; absent/null `clubId` personal rides included; private and club rides excluded; both directions of blocking excluded. |
| Two-step wizard | PASS, isolated Firebase E2E | Browser configuration and review, publication, matching Firestore record and accepted organizer participant, and persistence after reload. |
| Live radar | PASS, isolated Firebase E2E | Two authorized SDK publishers; inspector coordinates and projected marker positions changed; actual elapsed-time stale/disconnected transitions occurred without writes; empty/error/denied states, revoked access, real Firestore `removeTarget`, and browser Leave Pack persistence verified. |
| Billing fallback | PASS | The actual local `/api/billing/portal` request returned 404 and the account page displayed the fallback notice. |
| Actual billing portal | BLOCKED | No portal route or legitimate isolated billing configuration exists in the inspected website/backend. No portal response was mocked or represented as portal verification. |
| Authenticated production | NOT RUN | This phase was local. No deployment, production fixtures, commits, pushes, or mobile changes were made. |
## Isolation and configuration
The running emulator hub independently reported Auth `127.0.0.1:9199` and Firestore `127.0.0.1:8188`. The dedicated configuration is `firebase.website-qa.json`, which loads the current root `firestore.rules` and indexes. The rules SHA-256 was `4b67b2d16235b8259a57852a386f7c8bf98dddf0c6e0588e51209e819f327cf0` and remained unchanged.
Browser Firebase, website Firebase Admin, fixture Admin, and authorized publisher clients all used `demo-motorsignal-web-qa`. The browser's actual Firebase app options, Auth emulator configuration, and Firestore host were asserted. Authenticated server discovery returned the same seeded project records. The fixture API verified token audience/issuer before issuing isolated opaque sessions. Server HTTP forwarding targeted `http://127.0.0.1:8189`. The browser harness aborted external destinations. No production environment file was overwritten.
The handoff's `NEXT_PUBLIC_USE_FIREBASE_EMULATOR` flag is not the flag used by current source. The actual flag is `NEXT_PUBLIC_FIREBASE_EMULATORS`, together with explicit project and emulator ports. Emulator host variables alone were not used as proof of project isolation.
## Reproduced fixes and changed files
- `website/app/api/group-rides/route.ts`: accept absent/null club association, consistent with the current schema and rules.
- `website/app/api/account/sessions/route.ts` and `website/app/api/account/sessions/[sessionId]/route.ts`: forward successful 204 revocations with a null body; previously they threw and returned 500.
- `website/components/group-rides/live-pack-radar.tsx`: clear cached presence and stop listeners on access revocation/errors; reject late callbacks; distinguish stream failures from an empty pack.
- `website/components/group-rides/group-rides-management.tsx`: keep inaccessible requested ride IDs in real radar mode so reload after Leave Pack shows the clearance barrier rather than a simulated pack; reject a cached summary for a different ride.
- New focused regression files: `website/tests/community-personal-rides.test.ts`, `website/tests/website-session-proxy.test.ts`, and `website/tests/radar-access-lifecycle.test.tsx`. Failures were observed before the corresponding product fixes.
- Reused/extended `tools/verify_isolated_e2e.mjs` and `website/test-support/qa-auth-backend.mjs`; added `tools/verify_website_authenticated.mjs`, `tools/website-qa-isolation.mjs`, and `firebase.website-qa.json`.
Earlier harness failures caused by incomplete telemetry fixture fields or an incorrect wizard selector were corrected in the harness, not treated as product defects. Earlier failed runs are retained as evidence.
## Commands and results
All commands ran from `C:\Users\Bill\Desktop\flutter-app`. The focused harness used these process-only settings:
```powershell
$env:NEXT_PUBLIC_FIREBASE_EMULATORS='true'
$env:NEXT_PUBLIC_FIREBASE_QA_PROJECT_ID='demo-motorsignal-web-qa'
$env:NEXT_PUBLIC_FIREBASE_AUTH_EMULATOR_PORT='9199'
$env:NEXT_PUBLIC_FIREBASE_FIRESTORE_EMULATOR_PORT='8188'
$env:FIREBASE_PROJECT_ID='demo-motorsignal-web-qa'
$env:GCLOUD_PROJECT='demo-motorsignal-web-qa'
$env:FIRESTORE_EMULATOR_HOST='127.0.0.1:8188'
$env:FIREBASE_AUTH_EMULATOR_HOST='127.0.0.1:9199'
$env:MOTORSIGNAL_API_BASE_URL='http://127.0.0.1:8189'
$env:QA_FOCUSED_WEBSITE='true'
```
| Command | Result |
| --- | --- |
| `firebase emulators:start --config firebase.website-qa.json --only auth,firestore --project demo-motorsignal-web-qa` | Ready; hub verified the configured loopback ports. |
| `npm --prefix website run dev -- --hostname 127.0.0.1 -p 3100` | Local website ready with the process settings above. |
| `node website/test-support/qa-auth-backend.mjs` with `QA_DEVICE_SESSION_COUNT=25` | Local token-verifying fixture listening on 8189. |
| `node tools/verify_isolated_e2e.mjs` with `QA_WEBSITE_MODE=seed` | 5 owned users, 25 telemetry sessions, and 7 scoped group-ride fixtures seeded. |
| Same entry point with `QA_WEBSITE_MODE=run`, `QA_RUN_LABEL=completed` | Exit 0; 15/15 checks passed. |
| `node website/node_modules/vitest/vitest.mjs run --root website tests/community-personal-rides.test.ts tests/website-session-proxy.test.ts tests/radar-access-lifecycle.test.tsx tests/group-rides-page.test.tsx tests/telemetry-replay-export.test.tsx tests/firebase-client-emulator.test.ts --maxWorkers=1 --testTimeout=15000 --reporter=default --reporter=json --outputFile=../output/playwright/authenticated-20261004/focused-tests-completed.json` | Exit 0; 6 files, 42 tests passed. |
| Focused harness with `QA_WEBSITE_MODE=build` | Exit 0; runs the installed Next `build` once in `website`; TypeScript passed. The local artifact is built with demo Firebase configuration. |
| Focused harness with `QA_WEBSITE_MODE=cleanup` | Owned user and ride trees removed; cleanup assertions passed. |
| Focused harness with `QA_WEBSITE_MODE=audit` | Preservation and production-target guard evidence in the files below. |
| Focused harness with `QA_WEBSITE_MODE=service-audit` | All six owned website, fixture, and emulator endpoint ports refused connections after shutdown. |
For the recorded harness/test commands, Node was invoked directly using `C:\Users\Bill\AppData\Roaming\devbox-pro\resources\nodejs\24\win\node.exe`; the available `node.cmd`/npm shim did not reliably propagate failing exit codes. `rtk` was unavailable, so native commands were used.
The build reported three whole-project filesystem-tracing warnings at lines 37, 42, and 71 of the pre-existing, unchanged `website/app/api/account/rides/[rideId]/telemetry/route.ts`. They were not fixed within this phase. Tests also printed the existing Vite configuration warning, jsdom canvas limitation, and legacy test emulator connection warning; no test failed. Deliberate denied-access tests emitted expected Firestore permission errors. Broad Flutter/production guest suites were not restarted.
## Evidence, cleanup, and Git
Evidence directory: `output/playwright/authenticated-20261004/`. Primary files are `results-completed.json`, `focused-tests-completed.json`, `created-ride.json`, `emulator-topology.json`, `website-build.log`, `website-build-result.json`, `guard-results.json`, `cleanup.json`, `service-cleanup.json`, `git-before.json`, and `git-final.json`. GPX/CSV downloads and failure-only screenshots are retained there. Generic unavailable/missing stream states also have focused component coverage; deletion under current rules produces a real permission-denied barrier in the browser.
The final cleanup removed five owned Auth users, eight group-ride trees including the browser-created ride, their presence/participant records, and the owned user trees containing telemetry and blocks. The QA browser and local services were stopped. The local fixture backend's in-memory device sessions disappear when it stops. No pre-existing emulator project was cleared wholesale.
Main branch/HEAD remain `release/v1.0.2-build102-community-feed` / `bd278cdfd3619242a3056c6a2738abb0aff70feb`. The main worktree retains its pre-existing changes plus the scoped files/evidence above, with nothing staged. Next dev temporarily generated development type paths in `website/next-env.d.ts`; the single build returned it to the original clean HEAD content, independently asserted by the audit. All other unrelated baseline hashes remain unchanged. The deployment worktree remains clean on `deploy/website-20261004` / `69662eae46b92b2d824558b40a82fd487d15ba1b`. Complete final status, including untracked files, and preservation hashes are recorded in `git-final.json`.

View File

@ -0,0 +1,76 @@
# Scoped website deployment — October 4, 2026
Deployed commit: `8b3a7aa29290b58a9456a564785e9c73cd8e3093` on `deploy/website-20261004`. Only five reviewed product files and three regression files were committed. No push was performed. Website HTTP/artifact smoke passed. A subsequent administrator configuration resolved the credential blocker for the **user-confirmed production authenticated empty-state workflow**; populated production discovery remains untested. This report update did not rebuild, redeploy, restart, rerun configuration, or independently recheck production.
## Included scope
| Fix | Product files |
| --- | --- |
| Personal discovery accepts absent/null `clubId` | `website/app/api/group-rides/route.ts` |
| Successful session revocation forwards 204 without a body | `website/app/api/account/sessions/route.ts`; `website/app/api/account/sessions/[sessionId]/route.ts` |
| Radar clears cached presence and stops obsolete listeners after errors/revocation | `website/components/group-rides/live-pack-radar.tsx` |
| Requested ride identity survives Leave Pack/reload; denied rides do not become simulated packs | `website/components/group-rides/group-rides-management.tsx` |
Regression files: `website/tests/community-personal-rides.test.ts`, `website/tests/website-session-proxy.test.ts`, and `website/tests/radar-access-lifecycle.test.tsx`. The commit contains 180 insertions and 16 deletions across these eight files. Product differences are 49 insertions and 16 deletions, with no authentication, rule, entitlement, or dependency-version changes.
QA fixtures, emulator configuration, regression source, test-support files, environment files, evidence, caches, and Windows dependency trees were excluded from the production payload. The regression tests remain in the deployment Git commit but are not runtime package contents. Next's generated external-package alias is recreated as a relative Linux link to the verified existing Firebase Admin package.
## Production configuration and build
- Fresh build from the clean deployment commit; the main workspace's demo `.next` was not used or changed.
- Browser Firebase project verified at deployment was `motoapp-d22e0`; served client assets contained its production options. At cutover, Firebase Admin retained the same production project fallback with no runtime project override; subsequent administrator runtime configuration is recorded above.
- Build explicitly set `FIREBASE_PROJECT_ID` and `GCLOUD_PROJECT` to `motoapp-d22e0`; emulator/QA settings were stripped before environment loading. No environment files loaded in the build. At cutover, the production service also had no emulator flags/hosts or environment files. The later runtime configuration does not change this build evidence.
- Backend URL verified at deployment was `https://api.motorsignal.app`; service listened on `127.0.0.1:3001` under `motorsignal`. Systemd configuration was not modified by the deployment phase; the later administrator configuration was separate.
- The pre-existing dependency junction caused runner/build-path problems. It and the old deployment build were preserved under the ignored evidence subdirectories. A fresh `npm ci --no-audit --no-fund` installed 344 packages from the unchanged committed lockfile. There were no version upgrades.
- Fresh Next/Turbopack build and TypeScript passed. Three known tracing warnings remain in the unchanged telemetry route at lines 37, 42, and 71. Earlier junction builds and their warnings were discarded, not deployed.
- Runtime versions checked on Linux: Next 16.3.4, React/React DOM 19.1.1, Firebase Admin 14.4.0. Firebase browser SDK 12.18.0 is bundled.
- Build ID: `oo8nnoCyk_My8qXXYMOcr`. Artifact SHA-256: `2d9874f1fb23e8e22e5f700ead1db6ac90882063af4b9d5b82f0ec7a0482c0f9`.
## Verification
| Check | Result |
| --- | --- |
| Focused Vitest: three regression files plus `group-rides-page.test.tsx` | 31 tests passed, four files, exit 0 |
| Deployment revision contract test | PASS |
| Fresh production build, TypeScript, artifact scan | PASS; no isolated project/session/emulator endpoint contamination |
| Linux candidate on loopback 3102 | PASS; target revision, account/group pages, 13 client assets, protected API 401 responses; process stopped |
| Public HTTPS `/api/version` | PASS; exact deployed SHA and no-store |
| Public HTTPS account/group pages and 13 client assets | PASS; every asset hash matches the fresh package |
| Unauthenticated discovery/session APIs | PASS; both return 401 |
| Existing authenticated session: My Pack | PASS; ten existing rides displayed; no sign-in or record mutation performed |
| Existing authenticated session: real radar | PASS for an existing empty ride; authenticated mode, empty-state message, zero simulated-pilot cards; left radar view without leaving the ride |
| Authenticated community discovery | Initially BLOCKED with credential-loading 500; subsequent production empty state PASS — user-confirmed **No Community Rides Scheduled**. Populated production discovery remains UNTESTED |
The browser restored an existing Firebase session after navigation. Initial HTTP-only evidence saying authentication was not run is superseded by `authenticated-readonly.json`; no credentials were exported or new session sign-in performed. The management route's HTTP 200 credential-check skeleton is expected: its redirect runs only after client Auth resolution. The initial HTTP-redirect assertion was corrected after source inspection.
Backup inspection proved Firebase Admin initialization source was unchanged. Old and new standalone server configurations embedded only the deployment-revision variable. At deployment verification, the service had neither `FIREBASE_SERVICE_ACCOUNT` nor `GOOGLE_APPLICATION_CREDENTIALS` configured. The deployment phase did not modify credential-loading configuration. An authenticated request before cutover was not independently recorded, so the initial credential failure is not falsely claimed as a verified pre-deployment outcome.
On 2026-10-04, the administrator subsequently reported applying the separately prepared runtime configuration: credential probes passed, the website restarted, revision remained `8b3a7aa`, guest discovery returned 401, and post-restart credential/discovery failures were zero. In the administrator's existing signed-in Group Ride Hub, **No Community Rides Scheduled** appeared. These are **user-confirmed production results**, not independent agent retests; no raw authenticated HTTP status/JSON was supplied in this follow-up. Populated production discovery remains untested. See `docs/website-firebase-admin-blocker-2026-10-04.md` for the attributed follow-up and preserved original blocker evidence.
No production fixture creation, ride creation, session revocation, Leave Pack, access revocation, or presence publication was performed. Their behavioral coverage remains the prior isolated verification and current focused regression tests. Billing, PostgreSQL, R2, Android, Firestore rules, and unrelated backend services were not expanded or modified. The existing Tailwind CDN warning was observed and left unchanged.
## Cutover and rollback
The existing website-only packaging/restart process was retained, with shipping-and-launch safeguards: Linux candidate smoke, verified full backup before activation, preserved previous runtime, and automatic rollback on activation failure. The global `/opt/motorsignal/current` link was not moved. Backend and nginx PIDs, active states, restart counters, and backend path remained unchanged. Only `motorsignal-website` restarted; its automatic restart count remains zero. Previous static assets were retained for existing tabs, without overwriting fresh assets; all active server code is from the new build.
Rollback archive:
`/home/motorsignal-admin/rollback-website-69662eae-to-8b3a7aa-20261004.tar.gz`
SHA-256: `6e70037312d16d8967334e790cfad68e2d82df40a943db2261630f1174723712`. Its old server and build-ID contents were compared before cutover. The prior runtime is preserved at `/opt/motorsignal/releases/20260920-master-7efd29a/website/.next.rollback-8b3a7aa29290`.
Rollback command on the verified VPS, if authorized/needed:
```sh
python3 /home/motorsignal-admin/deploy-scoped-8b3a7aa29290.py rollback
```
The helper restores the preserved runtime and original five source files, server and package metadata, restarts only the website, and verifies previous revision `69662eae46b92b2d824558b40a82fd487d15ba1b`. Rollback is ready and verified structurally; it was not activated merely to test it.
## Commands, evidence, and preservation
Commands used direct bundled `node.exe`, installed Vitest/Next entry points, `npm ci`, `git diff --check`, explicit eight-file staging and local commit, `scp`, the scoped Python deployment helper, and read-only HTTP/browser checks. No broad QA suites were repeated. Build/deployment helper sources and exact settings are retained in `output/deployment/20261004-scoped/`.
Primary evidence: `focused-tests.json`, `production-config.json`, `production-build-result.json`, `production-build.log`, `artifact-scan.json`, `package.json`, `candidate-smoke.json`, `deployment-result.json`, `production-smoke.json`, `authenticated-readonly.json`, `credential-baseline.json`, `baseline.json`, and `git-final.json`.
The temporary candidate process and agent-created browser tab were closed; the user's existing signed-in session was not signed out. Owned staging directories, failed-build diagnostics, production package, previous local dependency junction/build, rollback archive, and old live runtime remain recoverable. No existing data was deleted. Main branch/HEAD remain `release/v1.0.2-build102-community-feed` / `bd278cdfd3619242a3056c6a2738abb0aff70feb`; preservation audit checks all 117 original dirty/untracked file hashes. Deployment worktree is clean at the scoped commit. Full final status, including untracked evidence additions, is in `git-final.json`.

Some files were not shown because too many files have changed in this diff Show More