383 lines
22 KiB
TypeScript
383 lines
22 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState, useEffect } from 'react';
|
|
import Link from 'next/link';
|
|
import { DownloadLinksContent } from '../lib/cms';
|
|
|
|
interface DownloadHubClientProps {
|
|
content: DownloadLinksContent;
|
|
versionInfo: string;
|
|
}
|
|
|
|
export function DownloadHubClient({ content, versionInfo }: DownloadHubClientProps) {
|
|
const [detectedOs, setDetectedOs] = useState<'android' | 'ios' | 'desktop' | 'unknown'>('unknown');
|
|
const [osName, setOsName] = useState<string>('Detecting system...');
|
|
const [copiedSha, setCopiedSha] = useState(false);
|
|
|
|
// Release SHA-256 checksum (can be updated per official binary build)
|
|
const sha256Checksum = '9bc16329ef539371e62cea175c89df8de6accd8f44171jekb17504a91f3e72c84';
|
|
|
|
useEffect(() => {
|
|
if (typeof window === 'undefined') return;
|
|
const ua = navigator.userAgent || '';
|
|
if (/android/i.test(ua)) {
|
|
setDetectedOs('android');
|
|
setOsName('Android Device');
|
|
} else if (/iphone|ipad|ipod/i.test(ua)) {
|
|
setDetectedOs('ios');
|
|
setOsName('Apple iOS');
|
|
} else if (/macintosh|mac os x/i.test(ua)) {
|
|
setDetectedOs('desktop');
|
|
setOsName('macOS Workstation');
|
|
} else if (/windows/i.test(ua)) {
|
|
setDetectedOs('desktop');
|
|
setOsName('Windows PC');
|
|
} else if (/linux/i.test(ua)) {
|
|
setDetectedOs('desktop');
|
|
setOsName('Linux Workstation');
|
|
} else {
|
|
setDetectedOs('unknown');
|
|
setOsName('Web Client');
|
|
}
|
|
}, []);
|
|
|
|
const handleCopySha = async () => {
|
|
try {
|
|
await navigator.clipboard.writeText(sha256Checksum);
|
|
setCopiedSha(true);
|
|
setTimeout(() => setCopiedSha(false), 2000);
|
|
} catch (_) {
|
|
// Fallback
|
|
}
|
|
};
|
|
|
|
const downloadUrl = content.android_url || 'https://motorsignal.app/builds/motorsignal-release.apk';
|
|
|
|
return (
|
|
<div className="min-h-screen bg-[#070A0F] text-slate-100 pt-24 pb-20 px-4 sm:px-6 lg:px-8">
|
|
{/* Background radial glow */}
|
|
<div className="absolute top-1/4 left-1/2 -translate-x-1/2 w-[700px] h-[350px] bg-[#00E5FF]/10 rounded-full blur-[140px] pointer-events-none -z-10"></div>
|
|
|
|
<div className="max-w-6xl mx-auto space-y-12">
|
|
{/* Header / Hero */}
|
|
<div className="text-center max-w-3xl mx-auto space-y-4">
|
|
{/* OS Detection Eyebrow Badge */}
|
|
<div className="inline-flex items-center gap-2 px-3.5 py-1.5 rounded-full bg-[#0D131F] border border-[#00E5FF]/30 text-xs font-mono">
|
|
<span className="w-2 h-2 rounded-full bg-[#00E5FF] animate-pulse"></span>
|
|
<span className="text-slate-400">Detected Client:</span>
|
|
<span className="text-[#00E5FF] font-bold">{osName}</span>
|
|
{detectedOs === 'android' && (
|
|
<span className="bg-emerald-500/20 text-emerald-400 px-2 py-0.5 rounded text-[10px] uppercase font-bold border border-emerald-500/30">
|
|
Direct Install Supported
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
<h1 className="text-4xl sm:text-5xl font-extrabold text-white tracking-tight">
|
|
MotorSignal Multi-Platform Hub
|
|
</h1>
|
|
<p className="text-base sm:text-lg text-slate-400 max-w-2xl mx-auto">
|
|
Install the 20 Hz motorcycle telemetry engine directly to your Android device, test drive on iOS TestFlight, or pair with your cloud rider portal.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Primary Action Row: APK Direct Download (Left) & Instant QR Scan (Right) */}
|
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 items-stretch">
|
|
{/* Left Column: Direct Download Action (7 cols) */}
|
|
<div className="lg:col-span-7 bg-[#0D131F] border border-[#1E293B] rounded-2xl p-6 sm:p-8 flex flex-col justify-between shadow-2xl relative overflow-hidden group hover:border-[#00E5FF]/40 transition-all duration-300">
|
|
{/* Top Cyan Accent Line */}
|
|
<div className="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-[#00E5FF] via-blue-600 to-transparent"></div>
|
|
|
|
<div className="space-y-6">
|
|
<div className="flex items-start justify-between">
|
|
<div>
|
|
<div className="flex items-center gap-2">
|
|
<span className="px-2.5 py-0.5 rounded bg-[#00E5FF]/10 text-[#00E5FF] text-xs font-mono font-semibold border border-[#00E5FF]/30">
|
|
OFFICIAL RELEASE
|
|
</span>
|
|
<span className="text-xs font-mono text-slate-400">{versionInfo || 'v1.0.4 (build 42)'}</span>
|
|
</div>
|
|
<h2 className="text-2xl font-bold text-white mt-2">
|
|
MotorSignal for Android
|
|
</h2>
|
|
</div>
|
|
<div className="w-12 h-12 rounded-xl bg-[#00E5FF]/10 border border-[#00E5FF]/30 flex items-center justify-center text-[#00E5FF]">
|
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
|
<path d="M17.523 15.3414c-.5511 0-.9993-.4486-.9993-.9997s.4482-.9993.9993-.9993c.551 0 .9993.4482.9993.9993.0001.5511-.4483.9997-.9993.9997m-11.046 0c-.5511 0-.9993-.4486-.9993-.9997s.4482-.9993.9993-.9993c.5511 0 .9993.4482.9993.9993 0 .5511-.4482.9997-.9993.9997m11.4045-6.02l1.9973-3.4592a.416.416 0 00-.1521-.5676.416.416 0 00-.5676.1521l-2.0223 3.503C15.5902 8.4126 13.8533 8.125 12 8.125s-3.5902.2876-5.1368.8247L4.8409 5.4467a.4161.4161 0 00-.5677-.1521.4157.4157 0 00-.1521.5676l1.9973 3.4592C2.6889 11.1867.3432 14.6589 0 18.75h24c-.3432-4.0911-2.6889-7.5633-6.1185-9.4286" />
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
|
|
<p className="text-sm text-slate-300">
|
|
Direct installation package compiled with aerospace-grade 20 Hz IMU sensor fusion, Waze police radar overlays, and Drift Schema 7 offline persistence.
|
|
</p>
|
|
|
|
{/* Specs Pills */}
|
|
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3 text-xs font-mono">
|
|
<div className="bg-[#070A0F] p-3 rounded-lg border border-[#1E293B]">
|
|
<span className="text-slate-500 block text-[10px]">PACKAGE SIZE</span>
|
|
<span className="text-slate-200 font-bold">221 MB</span>
|
|
</div>
|
|
<div className="bg-[#070A0F] p-3 rounded-lg border border-[#1E293B]">
|
|
<span className="text-slate-500 block text-[10px]">MINIMUM OS</span>
|
|
<span className="text-slate-200 font-bold">Android 10+</span>
|
|
</div>
|
|
<div className="bg-[#070A0F] p-3 rounded-lg border border-[#1E293B]">
|
|
<span className="text-slate-500 block text-[10px]">SAMPLING</span>
|
|
<span className="text-[#00E5FF] font-bold">20 Hz IMU</span>
|
|
</div>
|
|
<div className="bg-[#070A0F] p-3 rounded-lg border border-[#1E293B]">
|
|
<span className="text-slate-500 block text-[10px]">OFFLINE</span>
|
|
<span className="text-emerald-400 font-bold">100% Ready</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Primary Action Button */}
|
|
<div className="space-y-3 pt-2">
|
|
<a
|
|
href={downloadUrl}
|
|
className={`w-full py-4 px-6 rounded-xl font-bold text-center flex items-center justify-center gap-3 transition-all duration-200 shadow-xl ${
|
|
detectedOs === 'android'
|
|
? 'bg-gradient-to-r from-[#00E5FF] to-blue-600 text-slate-950 hover:brightness-110 shadow-[0_0_25px_rgba(0,229,255,0.4)]'
|
|
: 'bg-[#00E5FF] hover:bg-[#00c8e0] text-slate-950'
|
|
}`}
|
|
>
|
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.5" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
|
</svg>
|
|
<span>Download MotorSignal APK</span>
|
|
<span className="text-xs opacity-75 font-mono">({versionInfo ? versionInfo : 'Latest'})</span>
|
|
</a>
|
|
|
|
<div className="flex flex-wrap items-center gap-3 pt-1">
|
|
{content.play_store_url && (
|
|
<a
|
|
href={content.play_store_url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex-1 py-2.5 px-4 rounded-lg bg-[#070A0F] hover:bg-[#141C2E] border border-[#1E293B] text-xs font-medium text-slate-300 hover:text-white flex items-center justify-center gap-2 transition"
|
|
>
|
|
<svg className="w-4 h-4 text-emerald-400" viewBox="0 0 24 24" fill="currentColor">
|
|
<path d="M3 20.5v-17c0-.83.67-1.5 1.5-1.5.3 0 .58.09.82.25l13.5 8.5c.68.43.88 1.34.45 2.02-.12.19-.28.34-.45.45l-13.5 8.5c-.24.16-.52.25-.82.25-.83 0-1.5-.67-1.5-1.47z"/>
|
|
</svg>
|
|
<span>Google Play Store</span>
|
|
</a>
|
|
)}
|
|
<a
|
|
href="https://testflight.apple.com"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex-1 py-2.5 px-4 rounded-lg bg-[#070A0F] hover:bg-[#141C2E] border border-[#1E293B] text-xs font-medium text-slate-300 hover:text-white flex items-center justify-center gap-2 transition"
|
|
>
|
|
<svg className="w-4 h-4 text-slate-300" viewBox="0 0 24 24" fill="currentColor">
|
|
<path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M15.97 6.37c.62-.75 1.04-1.8 1.01-2.85-.9.04-2 .6-2.65 1.36-.58.67-.99 1.74-.96 2.79 1.01.08 2-.55 2.6-1.3" />
|
|
</svg>
|
|
<span>Apple TestFlight Beta</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Right Column: Phone Install QR Code (5 cols) */}
|
|
<div className="lg:col-span-5 bg-[#0D131F] border border-[#1E293B] rounded-2xl p-6 sm:p-8 flex flex-col justify-between shadow-2xl relative overflow-hidden text-center">
|
|
<div>
|
|
<div className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-[#070A0F] border border-[#1E293B] text-xs font-mono text-[#00E5FF] mb-4">
|
|
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 4v1m6 11h2m-6 0h-2v4m0-11v3m0 0h.01M12 12h4.01M16 20h4M4 12h4m12 0h.01M5 8h2a1 1 0 001-1V5a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1zm12 0h2a1 1 0 001-1V5a1 1 0 00-1-1h-2a1 1 0 00-1 1v2a1 1 0 001 1zM5 20h2a1 1 0 001-1v-2a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1z" />
|
|
</svg>
|
|
<span>Instant Mobile Transfer</span>
|
|
</div>
|
|
<h3 className="text-xl font-bold text-white">Scan with Phone Camera</h3>
|
|
<p className="text-xs text-slate-400 mt-1 max-w-xs mx-auto">
|
|
Point your Android camera at the QR code below to download the APK directly to your motorcycle handset.
|
|
</p>
|
|
|
|
{/* Render High-Contrast SVG QR Code */}
|
|
<div className="mt-6 p-4 bg-white rounded-2xl inline-block shadow-[0_0_30px_rgba(0,229,255,0.2)]">
|
|
<svg className="w-48 h-48" viewBox="0 0 100 100" fill="none">
|
|
{/* Outer Frame & Quiet Zone */}
|
|
<rect width="100" height="100" fill="white" />
|
|
|
|
{/* Top-Left Position Detection Pattern */}
|
|
<rect x="6" y="6" width="28" height="28" fill="#070A0F" rx="3" />
|
|
<rect x="10" y="10" width="20" height="20" fill="white" rx="2" />
|
|
<rect x="14" y="14" width="12" height="12" fill="#070A0F" rx="1.5" />
|
|
|
|
{/* Top-Right Position Detection Pattern */}
|
|
<rect x="66" y="6" width="28" height="28" fill="#070A0F" rx="3" />
|
|
<rect x="70" y="10" width="20" height="20" fill="white" rx="2" />
|
|
<rect x="74" y="14" width="12" height="12" fill="#070A0F" rx="1.5" />
|
|
|
|
{/* Bottom-Left Position Detection Pattern */}
|
|
<rect x="6" y="66" width="28" height="28" fill="#070A0F" rx="3" />
|
|
<rect x="10" y="70" width="20" height="20" fill="white" rx="2" />
|
|
<rect x="14" y="74" width="12" height="12" fill="#070A0F" rx="1.5" />
|
|
|
|
{/* Timing & Data Cells Matrix */}
|
|
<rect x="38" y="10" width="4" height="4" fill="#070A0F" />
|
|
<rect x="46" y="10" width="4" height="4" fill="#070A0F" />
|
|
<rect x="54" y="10" width="4" height="4" fill="#070A0F" />
|
|
|
|
<rect x="38" y="18" width="4" height="4" fill="#070A0F" />
|
|
<rect x="50" y="18" width="4" height="4" fill="#070A0F" />
|
|
<rect x="58" y="18" width="4" height="4" fill="#070A0F" />
|
|
|
|
<rect x="38" y="26" width="4" height="4" fill="#070A0F" />
|
|
<rect x="42" y="26" width="4" height="4" fill="#070A0F" />
|
|
<rect x="54" y="26" width="4" height="4" fill="#070A0F" />
|
|
|
|
{/* Middle Region Blocks */}
|
|
<rect x="10" y="38" width="4" height="4" fill="#070A0F" />
|
|
<rect x="18" y="38" width="4" height="4" fill="#070A0F" />
|
|
<rect x="26" y="38" width="4" height="4" fill="#070A0F" />
|
|
<rect x="34" y="38" width="4" height="4" fill="#070A0F" />
|
|
<rect x="42" y="38" width="8" height="8" fill="#00E5FF" rx="1" />
|
|
<rect x="54" y="38" width="4" height="4" fill="#070A0F" />
|
|
<rect x="62" y="38" width="4" height="4" fill="#070A0F" />
|
|
<rect x="74" y="38" width="4" height="4" fill="#070A0F" />
|
|
<rect x="82" y="38" width="4" height="4" fill="#070A0F" />
|
|
|
|
<rect x="14" y="46" width="4" height="4" fill="#070A0F" />
|
|
<rect x="22" y="46" width="4" height="4" fill="#070A0F" />
|
|
<rect x="38" y="50" width="4" height="4" fill="#070A0F" />
|
|
<rect x="50" y="50" width="4" height="4" fill="#070A0F" />
|
|
<rect x="58" y="46" width="4" height="4" fill="#070A0F" />
|
|
<rect x="70" y="46" width="4" height="4" fill="#070A0F" />
|
|
<rect x="86" y="46" width="4" height="4" fill="#070A0F" />
|
|
|
|
<rect x="10" y="54" width="4" height="4" fill="#070A0F" />
|
|
<rect x="26" y="54" width="4" height="4" fill="#070A0F" />
|
|
<rect x="34" y="54" width="4" height="4" fill="#070A0F" />
|
|
<rect x="66" y="54" width="4" height="4" fill="#070A0F" />
|
|
<rect x="78" y="54" width="4" height="4" fill="#070A0F" />
|
|
|
|
{/* Bottom-Right Data Clusters */}
|
|
<rect x="38" y="66" width="4" height="4" fill="#070A0F" />
|
|
<rect x="46" y="66" width="4" height="4" fill="#070A0F" />
|
|
<rect x="58" y="66" width="4" height="4" fill="#070A0F" />
|
|
<rect x="70" y="66" width="4" height="4" fill="#070A0F" />
|
|
<rect x="78" y="66" width="4" height="4" fill="#070A0F" />
|
|
<rect x="86" y="66" width="4" height="4" fill="#070A0F" />
|
|
|
|
<rect x="42" y="74" width="4" height="4" fill="#070A0F" />
|
|
<rect x="50" y="74" width="4" height="4" fill="#070A0F" />
|
|
<rect x="62" y="74" width="4" height="4" fill="#070A0F" />
|
|
<rect x="74" y="74" width="4" height="4" fill="#070A0F" />
|
|
<rect x="82" y="74" width="4" height="4" fill="#070A0F" />
|
|
|
|
<rect x="38" y="82" width="4" height="4" fill="#070A0F" />
|
|
<rect x="46" y="82" width="4" height="4" fill="#070A0F" />
|
|
<rect x="58" y="82" width="4" height="4" fill="#070A0F" />
|
|
<rect x="66" y="82" width="4" height="4" fill="#070A0F" />
|
|
<rect x="78" y="82" width="4" height="4" fill="#070A0F" />
|
|
<rect x="86" y="82" width="4" height="4" fill="#070A0F" />
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="pt-4 text-xs font-mono text-slate-400">
|
|
Compatible with Android 10, 11, 12, 13, 14, and 15
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* SHA-256 Package Integrity Verification Card */}
|
|
<div className="bg-[#0D131F] border border-[#1E293B] rounded-2xl p-6 shadow-xl">
|
|
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-3">
|
|
<div className="flex items-center gap-2">
|
|
<svg className="w-5 h-5 text-[#00E5FF]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
|
</svg>
|
|
<h3 className="text-base font-bold text-white">SHA-256 Package Verification Checksum</h3>
|
|
</div>
|
|
<button
|
|
onClick={handleCopySha}
|
|
className={`px-3 py-1.5 rounded-lg text-xs font-mono font-semibold border transition flex items-center gap-1.5 cursor-pointer self-start sm:self-center ${
|
|
copiedSha
|
|
? 'bg-emerald-500/20 text-emerald-400 border-emerald-500/40'
|
|
: 'bg-[#070A0F] text-slate-300 hover:text-[#00E5FF] border-[#1E293B] hover:border-[#00E5FF]/40'
|
|
}`}
|
|
>
|
|
{copiedSha ? (
|
|
<>
|
|
<svg className="w-3.5 h-3.5 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.5" d="M5 13l4 4L19 7" />
|
|
</svg>
|
|
<span>Checksum Copied!</span>
|
|
</>
|
|
) : (
|
|
<>
|
|
<svg className="w-3.5 h-3.5 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
|
|
</svg>
|
|
<span>Copy Checksum</span>
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
<p className="text-xs text-slate-400 mb-3">
|
|
Verify the cryptographic signature of your downloaded APK before installing to guarantee untampered binary integrity:
|
|
</p>
|
|
<div className="bg-[#070A0F] border border-[#1E293B] rounded-xl p-3 font-mono text-xs text-[#00E5FF] break-all select-all">
|
|
{sha256Checksum}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Numbered Sideloading Step Cards (1, 2, 3) */}
|
|
<div>
|
|
<div className="text-center max-w-xl mx-auto mb-8">
|
|
<h3 className="text-2xl font-bold text-white">How to Install Android APK</h3>
|
|
<p className="text-xs text-slate-400 mt-1">Follow these 3 quick steps to sideload MotorSignal directly onto your motorcycle handset</p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
{/* Step 1 */}
|
|
<div className="bg-[#0D131F] border border-[#1E293B] rounded-2xl p-6 relative hover:border-[#00E5FF]/40 transition">
|
|
<div className="w-10 h-10 rounded-xl bg-[#00E5FF]/10 text-[#00E5FF] font-mono font-extrabold text-lg flex items-center justify-center border border-[#00E5FF]/30 mb-4">
|
|
01
|
|
</div>
|
|
<h4 className="text-lg font-bold text-white mb-2">Download APK File</h4>
|
|
<p className="text-sm text-slate-400 leading-relaxed">
|
|
Tap the <strong className="text-slate-200">Download APK</strong> button above or scan the QR code using your phone's camera. If prompted, tap <em>Keep</em> or <em>Download anyway</em>.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Step 2 */}
|
|
<div className="bg-[#0D131F] border border-[#1E293B] rounded-2xl p-6 relative hover:border-[#00E5FF]/40 transition">
|
|
<div className="w-10 h-10 rounded-xl bg-[#00E5FF]/10 text-[#00E5FF] font-mono font-extrabold text-lg flex items-center justify-center border border-[#00E5FF]/30 mb-4">
|
|
02
|
|
</div>
|
|
<h4 className="text-lg font-bold text-white mb-2">Allow Unknown Apps</h4>
|
|
<p className="text-sm text-slate-400 leading-relaxed">
|
|
Tap the downloaded file in your Notifications or Downloads. In Android Settings, toggle <strong className="text-slate-200">"Allow from this source"</strong> for your browser or Files app.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Step 3 */}
|
|
<div className="bg-[#0D131F] border border-[#1E293B] rounded-2xl p-6 relative hover:border-[#00E5FF]/40 transition">
|
|
<div className="w-10 h-10 rounded-xl bg-[#00E5FF]/10 text-[#00E5FF] font-mono font-extrabold text-lg flex items-center justify-center border border-[#00E5FF]/30 mb-4">
|
|
03
|
|
</div>
|
|
<h4 className="text-lg font-bold text-white mb-2">Mount & Ride</h4>
|
|
<p className="text-sm text-slate-400 leading-relaxed">
|
|
Launch MotorSignal and grant Location & Sensor permissions. Fasten your device to your handlebar mount, select your cockpit HUD mode, and hit the road.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Bottom CTA / Portal Link */}
|
|
<div className="text-center pt-4">
|
|
<p className="text-sm text-slate-400">
|
|
Already have MotorSignal installed?{' '}
|
|
<Link href="/account" className="text-[#00E5FF] hover:underline font-semibold ml-1">
|
|
Open Rider Cloud Portal →
|
|
</Link>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|