feat: redesign landing page with full feature showcase

Replace minimal 3-card landing page with comprehensive feature
presentation matching the rApp ecosystem style:

- Hero with ecosystem badge and gradient accents
- 4 core feature cards (GPS, navigation, meeting points, privacy)
- 6 differentiator cards (c3nav, pinging, Google import, PWA, QR, CRDT)
- How It Works steps
- Use case cards (festivals, city exploration, group coordination)
- Technical highlights grid (MapLibre, OSRM, c3nav, Automerge, etc.)
- Preserved existing Get Started form and room join flow

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-02-25 14:09:55 -08:00
parent 56901d8e45
commit 460dc5045f
1 changed files with 298 additions and 101 deletions

View File

@ -7,10 +7,8 @@ import { AuthButton } from '@/components/AuthButton';
import { EcosystemFooter } from '@/components/EcosystemFooter';
import { useAuthStore } from '@/stores/auth';
// Emoji options for avatars
const EMOJI_OPTIONS = ['🐙', '🦊', '🐻', '🐱', '🦝', '🐸', '🦉', '🐧', '🦋', '🐝'];
// Generate a URL-safe room slug
function generateSlug(): string {
return nanoid(8).toLowerCase();
}
@ -26,8 +24,6 @@ export default function HomePage() {
const [isLoaded, setIsLoaded] = useState(false);
const [lastRoom, setLastRoom] = useState<string | null>(null);
// Load saved user info from localStorage on mount
// If opened as installed PWA (standalone mode), auto-redirect to last room
useEffect(() => {
let loadedEmoji = '';
try {
@ -40,7 +36,6 @@ export default function HomePage() {
loadedEmoji = user.emoji;
}
}
// Load last visited room (show "Rejoin" button, but don't auto-redirect)
const lastVisited = localStorage.getItem('rmaps_last_room');
if (lastVisited) {
setLastRoom(lastVisited);
@ -48,14 +43,12 @@ export default function HomePage() {
} catch {
// Ignore parse errors
}
// Set random emoji if none loaded
if (!loadedEmoji) {
setEmoji(EMOJI_OPTIONS[Math.floor(Math.random() * EMOJI_OPTIONS.length)]);
}
setIsLoaded(true);
}, [router]);
// Auto-fill name from EncryptID when authenticated
useEffect(() => {
if (isAuthenticated && authUsername && !name) {
setName(authUsername);
@ -64,31 +57,21 @@ export default function HomePage() {
const handleCreateRoom = async () => {
if (!name.trim()) return;
// Require EncryptID auth to create rooms
if (!isAuthenticated) {
alert('Please sign in with EncryptID to create a room.');
return;
}
const slug = roomName.trim()
? roomName.toLowerCase().replace(/[^a-z0-9]/g, '-').slice(0, 20)
: generateSlug();
// Store user info in localStorage for the session
localStorage.setItem('rmaps_user', JSON.stringify({ name, emoji }));
localStorage.setItem('rmaps_last_room', slug);
// Navigate to the room (will create it if it doesn't exist)
router.push(`/${slug}`);
};
const handleJoinRoom = () => {
if (!name.trim() || !joinSlug.trim()) return;
localStorage.setItem('rmaps_user', JSON.stringify({ name, emoji }));
// Clean the slug
const cleanSlug = joinSlug.toLowerCase().replace(/[^a-z0-9-]/g, '');
localStorage.setItem('rmaps_last_room', cleanSlug);
router.push(`/${cleanSlug}`);
@ -100,173 +83,395 @@ export default function HomePage() {
router.push(`/${lastRoom}`);
};
return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900">
{/* ── Hero Section ─────────────────────────────────────────── */}
<section className="relative overflow-hidden">
{/* Subtle background grid */}
<div className="absolute inset-0 opacity-[0.03]" style={{
backgroundImage: 'radial-gradient(circle, #10b981 1px, transparent 1px)',
backgroundSize: '40px 40px',
}} />
<div className="relative max-w-5xl mx-auto px-6 pt-16 pb-12 text-center">
{/* Logo */}
<div className="flex items-center justify-center gap-3 mb-8">
<div className="w-12 h-12 bg-gradient-to-br from-emerald-400 to-emerald-600 rounded-xl flex items-center justify-center font-bold text-slate-900 text-lg">
{/* ── Hero ──────────────────────────────────────────────────── */}
<section className="relative overflow-hidden">
{/* Background glow */}
<div className="absolute top-[-40%] left-[20%] w-[600px] h-[600px] bg-emerald-500/8 rounded-full blur-[120px] pointer-events-none" />
<div className="absolute top-[-20%] right-[10%] w-[400px] h-[400px] bg-cyan-500/6 rounded-full blur-[100px] pointer-events-none" />
<div className="relative max-w-4xl mx-auto px-6 pt-20 pb-16 text-center">
{/* Ecosystem badge */}
<div className="inline-flex items-center gap-2 px-4 py-1.5 rounded-full bg-emerald-500/10 border border-emerald-500/20 text-emerald-400 text-sm mb-8">
Part of the rSpace Ecosystem
</div>
{/* Logo + Name */}
<div className="flex items-center justify-center gap-3 mb-6">
<div className="w-14 h-14 bg-gradient-to-br from-emerald-400 to-teal-600 rounded-xl flex items-center justify-center font-bold text-slate-900 text-xl shadow-lg shadow-emerald-500/20">
rM
</div>
<h1 className="text-4xl sm:text-5xl font-bold">
<h1 className="text-5xl sm:text-6xl font-bold">
<span className="text-rmaps-primary">r</span>Maps
</h1>
</div>
{/* Headline */}
<h2 className="text-3xl sm:text-5xl font-bold mb-4 bg-gradient-to-r from-emerald-300 via-teal-200 to-cyan-300 bg-clip-text text-transparent leading-tight">
Collaborative Maps for Everyone
<h2 className="text-3xl sm:text-5xl font-bold mb-5 bg-gradient-to-r from-emerald-300 via-teal-200 to-cyan-300 bg-clip-text text-transparent leading-tight">
Real-Time Collaborative Maps
</h2>
<p className="text-lg sm:text-xl text-white/60 max-w-2xl mx-auto mb-8">
A flexible, privacy-first map tool for real-time location sharing, event navigation, and group coordination.
Create a room, share a link, and see your crew on the map.
<p className="text-lg sm:text-xl text-slate-400 max-w-2xl mx-auto mb-10 leading-relaxed">
Share live locations, navigate indoor and outdoor spaces, coordinate meetups
&mdash; all from the browser. No app install. No tracking. No data collection.
</p>
{/* CTA buttons */}
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 mb-6">
<a
href="https://demo.rmaps.online"
className="px-8 py-3.5 bg-emerald-600 hover:bg-emerald-500 text-white font-medium rounded-xl transition-all shadow-lg shadow-emerald-900/30 text-lg"
className="px-8 py-3.5 bg-gradient-to-r from-emerald-600 to-teal-600 hover:from-emerald-500 hover:to-teal-500 text-white font-medium rounded-xl transition-all shadow-lg shadow-emerald-900/30 text-lg"
>
Try the Demo
</a>
<a
href="#get-started"
className="px-8 py-3.5 bg-white/5 hover:bg-white/10 text-white font-medium rounded-xl transition-colors border border-white/10 text-lg"
className="px-8 py-3.5 bg-white/5 hover:bg-white/10 text-white font-medium rounded-xl transition-colors border border-slate-700 text-lg"
>
Get Started
Create a Room
</a>
</div>
<p className="text-sm text-white/40">
No app install. No sign-up required to join a room.
<p className="text-sm text-slate-500">
No sign-up required to join. Works on any device.
</p>
</div>
</section>
{/* ── Feature Cards ────────────────────────────────────────── */}
<section className="max-w-5xl mx-auto px-6 pb-16">
<div className="grid sm:grid-cols-3 gap-6">
{/* Real-time GPS */}
<div className="bg-slate-800/40 rounded-2xl border border-slate-700/40 p-6 text-center hover:border-emerald-500/30 transition-colors">
<div className="w-14 h-14 mx-auto mb-4 bg-emerald-500/10 rounded-xl flex items-center justify-center">
<svg className="w-7 h-7 text-emerald-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
{/* ── Core Features ─────────────────────────────────────────── */}
<section className="max-w-6xl mx-auto px-6 py-16">
<div className="text-center mb-12">
<span className="inline-block px-3 py-1 rounded-full bg-emerald-500/10 border border-emerald-500/20 text-emerald-400 text-xs font-medium uppercase tracking-wider mb-4">
Core Features
</span>
<h2 className="text-2xl sm:text-3xl font-bold text-white">
Everything you need to find your friends
</h2>
</div>
<div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-5">
{/* Live Location */}
<div className="bg-slate-800/30 rounded-2xl border border-slate-700/50 p-6 hover:border-emerald-500/30 transition-colors">
<div className="w-11 h-11 mb-4 bg-emerald-500/10 border border-emerald-500/20 rounded-xl flex items-center justify-center">
<svg className="w-6 h-6 text-emerald-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M15 10.5a3 3 0 11-6 0 3 3 0 016 0z" />
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1115 0z" />
</svg>
</div>
<h3 className="text-lg font-semibold mb-2 text-white">Real-time GPS</h3>
<p className="text-sm text-white/50">
Share your live location with friends. See everyone on the map updating in real time as you move.
<h3 className="font-semibold mb-2 text-white">Live GPS Sharing</h3>
<p className="text-sm text-slate-400 leading-relaxed">
Real-time location updates via WebSocket. See everyone on the map
as they move, with stale detection and high-accuracy fallback.
</p>
</div>
{/* Event Maps */}
<div className="bg-slate-800/40 rounded-2xl border border-slate-700/40 p-6 text-center hover:border-indigo-500/30 transition-colors">
<div className="w-14 h-14 mx-auto mb-4 bg-indigo-500/10 rounded-xl flex items-center justify-center">
<svg className="w-7 h-7 text-indigo-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
{/* Indoor + Outdoor Navigation */}
<div className="bg-slate-800/30 rounded-2xl border border-slate-700/50 p-6 hover:border-cyan-500/30 transition-colors">
<div className="w-11 h-11 mb-4 bg-cyan-500/10 border border-cyan-500/20 rounded-xl flex items-center justify-center">
<svg className="w-6 h-6 text-cyan-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M9 6.75V15m6-6v8.25m.503 3.498l4.875-2.437c.381-.19.622-.58.622-1.006V4.82c0-.836-.88-1.38-1.628-1.006l-3.869 1.934c-.317.159-.69.159-1.006 0L9.503 3.252a1.125 1.125 0 00-1.006 0L3.622 5.689C3.24 5.88 3 6.27 3 6.695V19.18c0 .836.88 1.38 1.628 1.006l3.869-1.934c.317-.159.69-.159 1.006 0l4.994 2.497c.317.158.69.158 1.006 0z" />
</svg>
</div>
<h3 className="text-lg font-semibold mb-2 text-white">Event Maps</h3>
<p className="text-sm text-white/50">
Navigate festivals, camps, and conferences. Custom maps with labeled stages, food courts, and meeting points.
<h3 className="font-semibold mb-2 text-white">Indoor + Outdoor Nav</h3>
<p className="text-sm text-slate-400 leading-relaxed">
Turn-by-turn routing via OSRM outdoors, seamless switch to c3nav
for indoor venues. Multi-floor, level-aware navigation.
</p>
</div>
{/* Privacy First */}
<div className="bg-slate-800/40 rounded-2xl border border-slate-700/40 p-6 text-center hover:border-rose-500/30 transition-colors">
<div className="w-14 h-14 mx-auto mb-4 bg-rose-500/10 rounded-xl flex items-center justify-center">
<svg className="w-7 h-7 text-rose-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
{/* Meeting Points */}
<div className="bg-slate-800/30 rounded-2xl border border-slate-700/50 p-6 hover:border-indigo-500/30 transition-colors">
<div className="w-11 h-11 mb-4 bg-indigo-500/10 border border-indigo-500/20 rounded-xl flex items-center justify-center">
<svg className="w-6 h-6 text-indigo-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M3 3v1.5M3 21v-6m0 0l2.77-.693a9 9 0 016.208.682l.108.054a9 9 0 006.086.71l3.114-.732a48.524 48.524 0 01-.005-10.499l-3.11.732a9 9 0 01-6.085-.711l-.108-.054a9 9 0 00-6.208-.682L3 4.5M3 15V4.5" />
</svg>
</div>
<h3 className="font-semibold mb-2 text-white">Meeting Points</h3>
<p className="text-sm text-slate-400 leading-relaxed">
Drop waypoints for meetups, events, and points of interest.
Search by address, share coordinates, or pin from your location.
</p>
</div>
{/* Privacy */}
<div className="bg-slate-800/30 rounded-2xl border border-slate-700/50 p-6 hover:border-rose-500/30 transition-colors">
<div className="w-11 h-11 mb-4 bg-rose-500/10 border border-rose-500/20 rounded-xl flex items-center justify-center">
<svg className="w-6 h-6 text-rose-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z" />
</svg>
</div>
<h3 className="text-lg font-semibold mb-2 text-white">Privacy First</h3>
<p className="text-sm text-white/50">
Zero-knowledge architecture. You control who sees you. Go invisible anytime. No tracking, no data collection.
<h3 className="font-semibold mb-2 text-white">Privacy First</h3>
<p className="text-sm text-slate-400 leading-relaxed">
Ghost mode, precision levels, one-toggle location sharing.
Zero tracking, zero data collection. You control who sees you.
</p>
</div>
</div>
</section>
{/* ── How It Works ─────────────────────────────────────────── */}
<section className="max-w-5xl mx-auto px-6 pb-20">
<h2 className="text-2xl sm:text-3xl font-bold text-center mb-10 text-white">How It Works</h2>
{/* ── What Makes rMaps Different ────────────────────────────── */}
<section className="max-w-6xl mx-auto px-6 py-16">
<div className="text-center mb-12">
<span className="inline-block px-3 py-1 rounded-full bg-teal-500/10 border border-teal-500/20 text-teal-400 text-xs font-medium uppercase tracking-wider mb-4">
Beyond Google Maps
</span>
<h2 className="text-2xl sm:text-3xl font-bold text-white">
What makes rMaps different
</h2>
</div>
<div className="grid sm:grid-cols-2 gap-6">
{/* CCC Event Integration */}
<div className="bg-slate-800/30 rounded-2xl border border-slate-700/50 p-6">
<div className="flex items-start gap-4">
<div className="w-10 h-10 shrink-0 bg-emerald-500/10 border border-emerald-500/20 rounded-lg flex items-center justify-center text-lg">
🏕
</div>
<div>
<h3 className="font-semibold mb-1.5 text-white">CCC Event Integration</h3>
<p className="text-sm text-slate-400 leading-relaxed">
Native c3nav integration for 39C3, Camp, and other CCC events. Indoor maps with
multi-floor routing, venue bounds detection, and automatic map switching
when you walk inside.
</p>
</div>
</div>
</div>
{/* Push Notifications & Pinging */}
<div className="bg-slate-800/30 rounded-2xl border border-slate-700/50 p-6">
<div className="flex items-start gap-4">
<div className="w-10 h-10 shrink-0 bg-cyan-500/10 border border-cyan-500/20 rounded-lg flex items-center justify-center text-lg">
📡
</div>
<div>
<h3 className="font-semibold mb-1.5 text-white">Location Pinging</h3>
<p className="text-sm text-slate-400 leading-relaxed">
Request a friend&apos;s location with one tap. Push notifications via
Web Push API with vibration alerts. Works even when the app is
backgrounded via service worker.
</p>
</div>
</div>
</div>
{/* Google Maps Import */}
<div className="bg-slate-800/30 rounded-2xl border border-slate-700/50 p-6">
<div className="flex items-start gap-4">
<div className="w-10 h-10 shrink-0 bg-indigo-500/10 border border-indigo-500/20 rounded-lg flex items-center justify-center text-lg">
📦
</div>
<div>
<h3 className="font-semibold mb-1.5 text-white">Google Maps Import</h3>
<p className="text-sm text-slate-400 leading-relaxed">
Import your saved places from Google Takeout ZIP exports. GeoJSON
parsing, auto-emoji mapping by place type, and preview before
importing.
</p>
</div>
</div>
</div>
{/* PWA & Offline */}
<div className="bg-slate-800/30 rounded-2xl border border-slate-700/50 p-6">
<div className="flex items-start gap-4">
<div className="w-10 h-10 shrink-0 bg-amber-500/10 border border-amber-500/20 rounded-lg flex items-center justify-center text-lg">
📱
</div>
<div>
<h3 className="font-semibold mb-1.5 text-white">PWA &amp; Offline Mode</h3>
<p className="text-sm text-slate-400 leading-relaxed">
Install as a native app. Three-tier service worker caching keeps
maps accessible offline with up to 500 cached tiles. Background
sync handles location updates.
</p>
</div>
</div>
</div>
{/* QR Sharing */}
<div className="bg-slate-800/30 rounded-2xl border border-slate-700/50 p-6">
<div className="flex items-start gap-4">
<div className="w-10 h-10 shrink-0 bg-violet-500/10 border border-violet-500/20 rounded-lg flex items-center justify-center text-lg">
🔗
</div>
<div>
<h3 className="font-semibold mb-1.5 text-white">Instant Room Sharing</h3>
<p className="text-sm text-slate-400 leading-relaxed">
Generate a QR code or shareable link for any room. Friends scan or tap
to join instantly &mdash; no account needed, no app download.
Native share dialog on mobile.
</p>
</div>
</div>
</div>
{/* CRDT Sync */}
<div className="bg-slate-800/30 rounded-2xl border border-slate-700/50 p-6">
<div className="flex items-start gap-4">
<div className="w-10 h-10 shrink-0 bg-rose-500/10 border border-rose-500/20 rounded-lg flex items-center justify-center text-lg">
🔄
</div>
<div>
<h3 className="font-semibold mb-1.5 text-white">Conflict-Free Sync</h3>
<p className="text-sm text-slate-400 leading-relaxed">
Automerge CRDT architecture ensures everyone sees the same map state,
even through disconnections. WebSocket real-time sync with automatic
reconnection and state recovery.
</p>
</div>
</div>
</div>
</div>
</section>
{/* ── How It Works ──────────────────────────────────────────── */}
<section className="max-w-5xl mx-auto px-6 py-16">
<div className="text-center mb-12">
<span className="inline-block px-3 py-1 rounded-full bg-cyan-500/10 border border-cyan-500/20 text-cyan-400 text-xs font-medium uppercase tracking-wider mb-4">
How It Works
</span>
<h2 className="text-2xl sm:text-3xl font-bold text-white">
Three steps to find your crew
</h2>
</div>
<div className="grid sm:grid-cols-3 gap-8">
{/* Step 1 */}
<div className="flex flex-col items-center text-center">
<div className="w-12 h-12 rounded-full bg-emerald-500/15 border border-emerald-500/30 flex items-center justify-center text-emerald-400 font-bold text-lg mb-4">
<div className="w-14 h-14 rounded-full bg-emerald-500/15 border border-emerald-500/30 flex items-center justify-center text-emerald-400 font-bold text-lg mb-4">
1
</div>
<h3 className="font-semibold text-white mb-2">Create a Map Room</h3>
<p className="text-sm text-white/50">
Sign in and create a room for your event. Get a shareable link like <span className="text-white/70 font-mono text-xs">rmaps.online/ccc-camp</span>
<p className="text-sm text-slate-400">
Sign in and name your room. Get a link like{' '}
<span className="text-white/70 font-mono text-xs">camp.rmaps.online</span>{' '}
or a custom slug.
</p>
</div>
{/* Step 2 */}
<div className="flex flex-col items-center text-center">
<div className="w-12 h-12 rounded-full bg-indigo-500/15 border border-indigo-500/30 flex items-center justify-center text-indigo-400 font-bold text-lg mb-4">
<div className="w-14 h-14 rounded-full bg-cyan-500/15 border border-cyan-500/30 flex items-center justify-center text-cyan-400 font-bold text-lg mb-4">
2
</div>
<h3 className="font-semibold text-white mb-2">Share with Friends</h3>
<p className="text-sm text-white/50">
Send the link to your crew. They join instantly from any device -- no app download, no account needed.
<p className="text-sm text-slate-400">
Send the link or scan the QR code. Friends join from any browser
&mdash; no app download, no account creation needed.
</p>
</div>
{/* Step 3 */}
<div className="flex flex-col items-center text-center">
<div className="w-12 h-12 rounded-full bg-cyan-500/15 border border-cyan-500/30 flex items-center justify-center text-cyan-400 font-bold text-lg mb-4">
<div className="w-14 h-14 rounded-full bg-indigo-500/15 border border-indigo-500/30 flex items-center justify-center text-indigo-400 font-bold text-lg mb-4">
3
</div>
<h3 className="font-semibold text-white mb-2">Find Each Other</h3>
<p className="text-sm text-white/50">
See everyone on the map in real time. Set your status, share meeting points, and never lose your friends again.
<h3 className="font-semibold text-white mb-2">Navigate Together</h3>
<p className="text-sm text-slate-400">
See everyone in real time. Drop meeting points, get turn-by-turn
directions, and ping friends when you need to regroup.
</p>
</div>
</div>
</section>
{/* ── Use Cases ─────────────────────────────────────────────── */}
<section className="max-w-5xl mx-auto px-6 py-16">
<div className="text-center mb-12">
<span className="inline-block px-3 py-1 rounded-full bg-indigo-500/10 border border-indigo-500/20 text-indigo-400 text-xs font-medium uppercase tracking-wider mb-4">
Built For
</span>
<h2 className="text-2xl sm:text-3xl font-bold text-white">
Maps for every gathering
</h2>
</div>
<div className="grid sm:grid-cols-3 gap-6">
<div className="bg-slate-800/30 rounded-2xl border border-slate-700/50 p-6 text-center hover:border-emerald-500/30 transition-colors">
<div className="text-3xl mb-3">🏕</div>
<h3 className="font-semibold mb-2 text-white">Festivals &amp; Camps</h3>
<p className="text-sm text-slate-400">
Navigate massive venues with indoor maps. Find stages, food courts,
and your crew across multi-day events like CCC Camp.
</p>
</div>
<div className="bg-slate-800/30 rounded-2xl border border-slate-700/50 p-6 text-center hover:border-cyan-500/30 transition-colors">
<div className="text-3xl mb-3">🏙</div>
<h3 className="font-semibold mb-2 text-white">City Exploration</h3>
<p className="text-sm text-slate-400">
Exploring a new city with friends? Share locations, drop pins at
restaurants and landmarks, import your Google Maps saved places.
</p>
</div>
<div className="bg-slate-800/30 rounded-2xl border border-slate-700/50 p-6 text-center hover:border-indigo-500/30 transition-colors">
<div className="text-3xl mb-3">🤝</div>
<h3 className="font-semibold mb-2 text-white">Group Coordination</h3>
<p className="text-sm text-slate-400">
Conferences, retreats, team offsites. Set meeting points, ping
stragglers, and get walking directions to the next session.
</p>
</div>
</div>
{/* Connector lines (decorative) */}
<div className="hidden sm:flex items-center justify-center mt-8">
<div className="flex items-center gap-2 text-white/20 text-sm">
<span>Built for</span>
<div className="flex items-center justify-center mt-8">
<div className="flex items-center gap-2 text-slate-500 text-sm">
<span>First built for</span>
<a
href="https://events.ccc.de/"
target="_blank"
rel="noopener noreferrer"
className="text-rmaps-primary/60 hover:text-rmaps-primary transition-colors"
className="text-emerald-500/70 hover:text-emerald-400 transition-colors"
>
CCC events
</a>
<span>and beyond</span>
<span>&mdash; now for any gathering</span>
</div>
</div>
</section>
{/* ── Get Started (existing login/room interface) ──────────── */}
<section id="get-started" className="scroll-mt-8 pb-16">
{/* ── Technical Highlights ──────────────────────────────────── */}
<section className="max-w-5xl mx-auto px-6 py-16">
<div className="text-center mb-10">
<span className="inline-block px-3 py-1 rounded-full bg-slate-700/50 border border-slate-600/50 text-slate-400 text-xs font-medium uppercase tracking-wider mb-4">
Under the Hood
</span>
<h2 className="text-2xl sm:text-3xl font-bold text-white">
Built on open standards
</h2>
</div>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4 text-center">
{[
{ label: 'MapLibre GL', desc: 'Open-source maps' },
{ label: 'OSRM', desc: 'Outdoor routing' },
{ label: 'c3nav', desc: 'Indoor navigation' },
{ label: 'Automerge', desc: 'CRDT sync' },
{ label: 'Web Push', desc: 'Notifications' },
{ label: 'Service Worker', desc: 'Offline & PWA' },
{ label: 'WebSocket', desc: 'Real-time sync' },
{ label: 'EncryptID', desc: 'Identity & auth' },
].map((tech) => (
<div key={tech.label} className="bg-slate-800/20 rounded-xl border border-slate-700/30 p-4">
<p className="text-sm font-medium text-white">{tech.label}</p>
<p className="text-xs text-slate-500 mt-0.5">{tech.desc}</p>
</div>
))}
</div>
</section>
{/* ── Get Started ───────────────────────────────────────────── */}
<section id="get-started" className="scroll-mt-8 py-16">
<main className="flex flex-col items-center px-4">
<div className="max-w-md w-full space-y-8">
{/* Section heading */}
<div className="text-center">
<h2 className="text-2xl sm:text-3xl font-bold mb-2 text-white">Get Started</h2>
<p className="text-white/50 text-sm mb-4">Create or join a map room</p>
<p className="text-slate-500 text-sm mb-4">Create or join a map room</p>
<div className="mt-3">
<AuthButton />
</div>
</div>
{/* Quick Rejoin Card - show when user has saved info and last room */}
{/* Quick Rejoin */}
{isLoaded && name && lastRoom && (
<div className="room-panel rounded-2xl p-6">
<p className="text-white/60 text-sm text-center mb-4">Welcome back, {name}!</p>
@ -285,10 +490,8 @@ export default function HomePage() {
{/* Main Card */}
<div className="room-panel rounded-2xl p-6 space-y-6">
{/* User Setup */}
<div className="space-y-4">
<h3 className="text-lg font-medium">Your Profile</h3>
<div>
<label className="block text-sm text-white/60 mb-2">Your Name</label>
<input
@ -300,7 +503,6 @@ export default function HomePage() {
maxLength={20}
/>
</div>
<div>
<label className="block text-sm text-white/60 mb-2">Your Avatar</label>
<div className="flex gap-2 flex-wrap">
@ -323,7 +525,6 @@ export default function HomePage() {
<hr className="border-white/10" />
{/* Create Room */}
{!isCreating ? (
<div className="space-y-4">
<button
@ -334,10 +535,7 @@ export default function HomePage() {
>
{isAuthenticated ? 'Create New Map' : 'Sign in to Create Map'}
</button>
<div className="text-center text-white/40 text-sm">or</div>
{/* Join Room */}
<div className="space-y-3">
<input
type="text"
@ -376,7 +574,6 @@ export default function HomePage() {
Leave blank for a random code
</p>
</div>
<div className="flex gap-3">
<button
onClick={() => setIsCreating(false)}
@ -399,7 +596,7 @@ export default function HomePage() {
</main>
</section>
{/* ── Ecosystem Footer ─────────────────────────────────────── */}
{/* ── Ecosystem Footer ─────────────────────────────────────── */}
<EcosystemFooter current="rMaps" />
</div>
);