trails/openspec/changes/visual-redesign/mockup.html
Ullrich Schäfer 36b9b6b382
Add visual redesign OpenSpec change with mockups and logo concepts
Design direction: D1 warmth + D3 lightness (warm off-whites, sage green,
Outfit + Geist Mono). Includes desktop + mobile HTML mockup, 5 logo
concepts, and 33 implementation tasks across 7 phases.

Logo decision: Waypoint Dot mark (3 connected dots) + wordmark combo.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 19:28:08 +01:00

1550 lines
53 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>trails.cool — Revised Direction</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700&family=Geist+Mono:wght@400;500&family=Rethink+Sans:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500&family=Figtree:wght@300;400;500;600;700&family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&family=Nunito+Sans:ital,opsz,wght@0,6..12,300;0,6..12,400;0,6..12,500;0,6..12,600;0,6..12,700;1,6..12,400&family=Epilogue:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400&family=Libre+Franklin:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400&family=Outfit:wght@300;400;500;600;700&family=Mulish:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400&family=Work+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: 14px; }
body { background: #F2EFE8; font-family: 'Outfit', sans-serif; color: #1E1C18; }
/* ─── NAV CHROME ─── */
.page-nav {
position: sticky; top: 0; z-index: 2000;
background: #1E1C18;
padding: 10px 20px;
display: flex; align-items: center; gap: 8px;
border-bottom: 1px solid #2E2C26;
}
.page-nav-label { font-size: 11px; color: #555; text-transform: uppercase; letter-spacing: .1em; margin-right: 6px; font-family: 'Outfit', sans-serif; }
.nav-btn {
padding: 5px 13px; border-radius: 5px; border: 1px solid #333;
background: transparent; color: #777; cursor: pointer;
font-size: 12px; font-family: 'Outfit', sans-serif;
transition: all .15s;
}
.nav-btn:hover { border-color: #666; color: #ccc; }
.nav-btn.active { background: #E8E3D6; color: #1E1C18; border-color: #E8E3D6; font-weight: 600; }
.nav-sep { width: 1px; height: 20px; background: #2E2C26; }
.screen { display: none; }
.screen.active { display: block; }
/* ═══════════════════════════════════════════════
DESIGN SYSTEM — trails.cool revised
Light, warm-neutral. NOT orange-heavy.
Accent: a single muted forest-sage green.
Body font: Outfit (clean, geometric, friendly)
Mono details: Geist Mono for stats/distances
The logo mark: simple wordmark, no drama
═══════════════════════════════════════════════ */
:root {
/* Surface */
--bg: #F5F2EB; /* warm off-white — like uncoated paper */
--bg-subtle: #EDEAE1; /* slightly deeper, card backgrounds */
--bg-raised: #FAF8F4; /* topbar, sidebar, elevated surfaces */
--map-tint: #E4DFD2; /* map background tone */
/* Text */
--text-hi: #1A1916; /* near-black, primary */
--text-md: #5C5847; /* secondary labels */
--text-lo: #9A9484; /* tertiary, placeholders */
--text-inv: #FAF8F4; /* on dark surfaces */
/* Accent: one muted sage-forest green */
--accent: #4A6B40; /* primary accent */
--accent-dim: #6A8B5E; /* softer variant */
--accent-bg: rgba(74,107,64,.08);
--accent-border: rgba(74,107,64,.2);
/* Overnight/stop warm tone — muted, not orange */
--stop: #8B6D3A; /* warm amber-brown for overnight markers */
--stop-bg: rgba(139,109,58,.1);
--stop-border:rgba(139,109,58,.25);
/* Danger / no-go */
--nogo: rgba(160,60,60,.12);
--nogo-border:rgba(160,60,60,.3);
/* UI chrome */
--border: #DDD9D0;
--border-md: #CAC6BC;
--shadow-sm: 0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);
--shadow-md: 0 4px 12px rgba(0,0,0,.08), 0 1px 4px rgba(0,0,0,.05);
/* Elevation gradient colours (for route + chart) */
--eg-lo: #5A8F46;
--eg-mid: #C4A840;
--eg-hi: #C46040;
--font-body: 'Outfit', sans-serif;
--font-mono: 'Geist Mono', monospace;
}
/* ─── TOPBAR ─── */
.topbar {
height: 48px;
background: var(--bg-raised);
border-bottom: 1px solid var(--border);
display: flex; align-items: center;
position: relative; z-index: 100;
}
/* Logo — clean wordmark */
.logo {
padding: 0 18px;
height: 100%;
display: flex; align-items: center; gap: 8px;
border-right: 1px solid var(--border);
text-decoration: none; flex-shrink: 0;
}
.logo-mark {
width: 22px; height: 22px;
display: flex; align-items: center; justify-content: center;
position: relative;
}
/* Simple mountain/trail SVG icon — not emoji */
.logo-mark svg { display: block; }
.logo-wordmark {
font-family: var(--font-body);
font-size: 15px;
font-weight: 600;
color: var(--text-hi);
letter-spacing: -.02em;
line-height: 1;
}
.logo-wordmark span {
color: var(--accent);
font-weight: 400;
}
/* Topbar sections */
.tb-section {
padding: 0 14px;
height: 100%;
display: flex; align-items: center; gap: 8px;
border-right: 1px solid var(--border);
flex-shrink: 0;
}
.tb-label {
font-size: 10px; text-transform: uppercase; letter-spacing: .1em;
color: var(--text-lo); font-weight: 400;
}
.profile-select {
display: flex; align-items: center; gap: 6px;
background: var(--bg-subtle);
border: 1px solid var(--border);
border-radius: 6px;
padding: 4px 9px;
font-size: 12px; font-weight: 500; color: var(--text-hi);
cursor: pointer; font-family: var(--font-body);
white-space: nowrap;
}
.profile-select .chevron {
color: var(--text-lo);
margin-left: 2px;
font-size: 10px;
}
/* ── Participant zone ── */
.participants {
padding: 0 12px;
height: 100%;
display: flex; align-items: center; gap: 8px;
border-right: 1px solid var(--border);
position: relative;
flex-shrink: 0;
}
/* Stacked avatars */
.avatar-stack {
display: flex; align-items: center;
cursor: pointer;
}
.av {
width: 26px; height: 26px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 10px; font-weight: 700; color: #fff;
border: 2px solid var(--bg-raised);
margin-left: -6px;
flex-shrink: 0;
transition: transform .12s;
position: relative; z-index: 1;
}
.av:first-child { margin-left: 0; z-index: 4; }
.av:nth-child(2) { z-index: 3; }
.av:nth-child(3) { z-index: 2; }
.avatar-stack:hover .av { transform: translateX(-2px); }
.avatar-stack:hover .av:first-child { transform: none; }
/* "You" avatar — always shown with name since it's the user */
.av-you-wrap {
display: flex; align-items: center; gap: 6px;
padding-right: 10px;
border-right: 1px solid var(--border);
margin-right: 4px;
cursor: default;
}
.av-you-name {
font-size: 12px; font-weight: 600; color: var(--text-hi); line-height: 1.1;
}
.av-you-role {
font-size: 10px; color: var(--accent); line-height: 1.2;
}
/* Overflow "+N" chip */
.av-overflow {
width: 26px; height: 26px; border-radius: 50%;
background: var(--bg-subtle);
border: 2px solid var(--bg-raised);
display: flex; align-items: center; justify-content: center;
font-size: 9px; font-weight: 700; color: var(--text-md);
margin-left: -6px; flex-shrink: 0; z-index: 0;
}
/* Invite button — lightweight, link-share feel */
.invite-btn {
display: flex; align-items: center; gap: 5px;
padding: 4px 10px; border-radius: 5px;
border: 1px dashed var(--border-md);
background: transparent; color: var(--text-lo);
font-size: 11px; font-weight: 500; font-family: var(--font-body);
cursor: pointer; white-space: nowrap;
transition: all .12s;
}
.invite-btn:hover {
border-color: var(--accent);
color: var(--accent);
background: var(--accent-bg);
}
/* ── Participants panel (dropdown) ── */
.participants-panel {
position: absolute;
top: calc(100% + 6px);
left: 0;
width: 280px;
background: var(--bg-raised);
border: 1px solid var(--border);
border-radius: 10px;
box-shadow: 0 8px 24px rgba(0,0,0,.1), 0 2px 6px rgba(0,0,0,.06);
z-index: 500;
overflow: hidden;
/* shown via JS toggle */
}
.pp-header {
padding: 12px 14px 10px;
border-bottom: 1px solid var(--border);
display: flex; align-items: center; justify-content: space-between;
}
.pp-title {
font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .09em;
color: var(--text-lo);
}
.pp-count {
font-family: var(--font-mono);
font-size: 11px; color: var(--text-lo);
}
.pp-list { padding: 6px 0; }
.pp-row {
display: flex; align-items: center; gap: 10px;
padding: 7px 14px;
transition: background .1s;
}
.pp-row:hover { background: var(--bg-subtle); }
.pp-av {
width: 28px; height: 28px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 11px; font-weight: 700; color: #fff;
flex-shrink: 0;
/* live pulse ring for online users */
position: relative;
}
.pp-av.online::after {
content: '';
position: absolute; bottom: 0; right: 0;
width: 8px; height: 8px; border-radius: 50%;
background: var(--accent);
border: 1.5px solid var(--bg-raised);
}
.pp-info { flex: 1; min-width: 0; }
.pp-name {
font-size: 12px; font-weight: 600; color: var(--text-hi);
display: flex; align-items: center; gap: 5px;
}
.pp-host-tag {
font-size: 9px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em;
color: var(--accent); background: var(--accent-bg);
border: 1px solid var(--accent-border);
border-radius: 3px; padding: 1px 4px;
}
.pp-status {
font-size: 11px; color: var(--text-lo);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pp-cursor-dot {
width: 8px; height: 8px; border-radius: 50%;
flex-shrink: 0;
}
/* Share link row */
.pp-share {
padding: 10px 14px 12px;
border-top: 1px solid var(--border);
}
.pp-share-label {
font-size: 10px; text-transform: uppercase; letter-spacing: .1em;
color: var(--text-lo); margin-bottom: 6px;
}
.pp-share-row {
display: flex; gap: 6px;
}
.pp-share-input {
flex: 1; background: var(--bg-subtle);
border: 1px solid var(--border); border-radius: 5px;
padding: 5px 8px;
font-size: 11px; font-family: var(--font-mono); color: var(--text-md);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
cursor: text;
}
.pp-copy-btn {
padding: 5px 11px; border-radius: 5px;
background: var(--text-hi); color: var(--bg-raised);
font-size: 11px; font-weight: 600; font-family: var(--font-body);
border: none; cursor: pointer; white-space: nowrap;
transition: opacity .12s;
}
.pp-copy-btn:hover { opacity: .85; }
.pp-copy-btn.copied {
background: var(--accent);
}
/* Mobile participant zone */
.mob-pax-zone {
display: flex; align-items: center; gap: 6px;
position: relative;
}
.mob-av-stack { display: flex; align-items: center; cursor: pointer; }
.mob-av {
width: 24px; height: 24px; border-radius: 50%;
border: 2px solid var(--bg-raised);
font-size: 9px; font-weight: 700; color: #fff;
display: flex; align-items: center; justify-content: center;
margin-left: -6px; flex-shrink: 0;
}
.mob-av:first-child { margin-left: 0; }
.mob-av-overflow {
width: 24px; height: 24px; border-radius: 50%;
border: 2px solid var(--bg-raised);
background: var(--bg-subtle);
font-size: 9px; font-weight: 700; color: var(--text-md);
display: flex; align-items: center; justify-content: center;
margin-left: -6px; flex-shrink: 0;
}
/* p-name / p-role kept for any remaining usage */
.p-name {
font-size: 12px; font-weight: 600; color: var(--text-hi); line-height: 1.1;
}
.p-role { font-size: 10px; color: var(--text-lo); line-height: 1.2; }
.p-role.host { color: var(--accent); }
/* Topbar right actions */
.tb-actions {
margin-left: auto;
padding: 0 14px;
display: flex; align-items: center; gap: 8px;
}
.color-mode {
display: flex;
background: var(--bg-subtle);
border: 1px solid var(--border);
border-radius: 6px; overflow: hidden;
}
.cm-btn {
padding: 4px 10px; font-size: 11px; font-weight: 500;
color: var(--text-lo); cursor: pointer;
background: none; border: none;
font-family: var(--font-body);
transition: all .12s;
}
.cm-btn.active { background: var(--text-hi); color: var(--bg-raised); }
.btn-ghost {
display: flex; align-items: center; gap: 5px;
padding: 5px 12px; border-radius: 6px;
border: 1px solid var(--border);
background: transparent; color: var(--text-md);
font-size: 12px; font-weight: 500; font-family: var(--font-body);
cursor: pointer; white-space: nowrap;
transition: all .12s;
}
.btn-ghost:hover { border-color: var(--border-md); color: var(--text-hi); }
.session-status {
display: flex; align-items: center; gap: 5px;
font-size: 11px; color: var(--text-lo);
font-family: var(--font-mono);
white-space: nowrap;
}
.session-dot {
width: 6px; height: 6px; border-radius: 50%;
background: var(--accent);
flex-shrink: 0;
}
/* Progress bar */
.progress-bar {
position: absolute; top: 0; left: 0; height: 2px; z-index: 200;
background: linear-gradient(90deg, var(--accent), var(--eg-mid));
border-radius: 0 2px 2px 0;
animation: pb 4s ease-in-out infinite alternate;
}
@keyframes pb {
0% { width: 18%; opacity: 1; }
100% { width: 72%; opacity: .8; }
}
/* ─── MAIN LAYOUT ─── */
.main {
display: flex;
height: calc(100vh - 48px - 148px);
min-height: 380px;
}
/* ─── MAP ─── */
.map-area {
flex: 1;
position: relative;
overflow: hidden;
background: var(--map-tint);
}
/* subtle topo crosshatch */
.map-area::before {
content: '';
position: absolute; inset: 0; z-index: 0;
background-image:
repeating-linear-gradient(90deg,
rgba(150,140,120,.1) 0, rgba(150,140,120,.1) 1px,
transparent 0, transparent 18px),
repeating-linear-gradient(0deg,
rgba(150,140,120,.1) 0, rgba(150,140,120,.1) 1px,
transparent 0, transparent 18px);
}
.map-credit {
position: absolute; bottom: 6px; right: 8px; z-index: 10;
font-size: 10px; color: rgba(0,0,0,.22);
font-family: var(--font-body);
}
/* Map controls */
.map-ctrl-group {
position: absolute; left: 12px; top: 12px; z-index: 20;
display: flex; flex-direction: column; gap: 0;
box-shadow: var(--shadow-md);
border-radius: 7px; overflow: hidden;
border: 1px solid var(--border);
}
.map-ctrl {
width: 32px; height: 32px;
background: var(--bg-raised);
display: flex; align-items: center; justify-content: center;
font-size: 16px; font-weight: 500; cursor: pointer;
color: var(--text-md);
border-bottom: 1px solid var(--border);
transition: background .12s;
}
.map-ctrl:last-child { border-bottom: none; }
.map-ctrl:hover { background: var(--bg-subtle); }
.map-ctrl.icon { font-size: 13px; }
/* Route SVG */
.route-layer {
position: absolute; inset: 0; z-index: 5;
pointer-events: none;
}
/* No-go polygon */
.nogo {
position: absolute; z-index: 6;
background: var(--nogo);
border: 1.5px dashed var(--nogo-border);
border-radius: 8px;
}
/* Waypoint markers */
.wpt {
position: absolute; z-index: 10;
display: flex; flex-direction: column; align-items: center;
transform: translate(-50%, -100%);
pointer-events: all;
cursor: grab;
}
.wpt-pin {
width: 26px; height: 26px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 11px; font-weight: 700; color: #fff;
box-shadow: 0 2px 6px rgba(0,0,0,.22);
border: 2px solid rgba(255,255,255,.6);
position: relative; flex-shrink: 0;
font-family: var(--font-mono);
}
.wpt-pin.is-start { background: var(--accent); }
.wpt-pin.is-mid { background: #7A9070; } /* minor intermediate - muted */
.wpt-pin.is-overnight { background: var(--stop); }
.wpt-pin.is-end { background: var(--text-hi); }
/* Note dot on pin */
.wpt-note-dot {
position: absolute; top: -1px; right: -1px;
width: 9px; height: 9px; border-radius: 50%;
background: var(--stop);
border: 1.5px solid var(--bg-raised);
}
.wpt-label {
margin-top: 4px;
background: var(--bg-raised);
border: 1px solid var(--border);
border-radius: 5px; padding: 2px 8px;
font-size: 11px; font-weight: 500; color: var(--text-hi);
white-space: nowrap;
box-shadow: var(--shadow-sm);
}
/* Overnight badge above pin */
.wpt-overnight-badge {
position: absolute; bottom: calc(100% + 5px);
background: var(--stop);
color: #fff;
font-size: 9px; font-weight: 600; letter-spacing: .05em;
text-transform: uppercase;
padding: 2px 7px; border-radius: 4px;
white-space: nowrap;
box-shadow: 0 1px 4px rgba(0,0,0,.15);
}
/* Day labels on map */
.day-badge {
position: absolute; z-index: 8;
background: var(--bg-raised);
border: 1px solid var(--border);
border-radius: 5px; padding: 3px 9px;
font-size: 10px; font-weight: 500; color: var(--text-md);
box-shadow: var(--shadow-sm);
white-space: nowrap;
transform: translateX(-50%);
}
/* Cursor label */
.cursor {
position: absolute; z-index: 12;
display: flex; align-items: center; gap: 5px;
pointer-events: none;
}
.cursor-dot {
width: 10px; height: 10px; border-radius: 50%;
border: 2px solid var(--bg-raised);
flex-shrink: 0;
}
.cursor-label {
font-size: 11px; font-weight: 600;
color: #fff;
padding: 2px 8px; border-radius: 4px;
box-shadow: 0 1px 4px rgba(0,0,0,.2);
}
/* ─── SIDEBAR ─── */
.sidebar {
width: 276px;
background: var(--bg-raised);
border-left: 1px solid var(--border);
display: flex; flex-direction: column;
overflow: hidden;
flex-shrink: 0;
}
.sidebar-tabs {
display: flex;
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
.stab {
flex: 1; padding: 10px 8px; text-align: center;
font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .08em;
color: var(--text-lo); background: none; border: none; cursor: pointer;
font-family: var(--font-body);
border-bottom: 2px solid transparent; margin-bottom: -1px;
transition: color .12s;
}
.stab.active { color: var(--text-hi); border-bottom-color: var(--accent); }
.sidebar-body {
flex: 1; overflow-y: auto; padding: 14px;
}
.sidebar-body::-webkit-scrollbar { width: 3px; }
.sidebar-body::-webkit-scrollbar-thumb {
background: var(--border); border-radius: 2px;
}
/* Route header inside sidebar */
.route-header {
margin-bottom: 14px;
padding-bottom: 12px;
border-bottom: 1px solid var(--border);
}
.route-eyebrow {
font-size: 9px; text-transform: uppercase; letter-spacing: .12em;
color: var(--text-lo); margin-bottom: 3px;
}
.route-name {
font-size: 15px; font-weight: 600; color: var(--text-hi);
line-height: 1.3; margin-bottom: 8px;
}
.route-via {
font-size: 12px; font-weight: 400; color: var(--text-md);
}
.route-stats {
display: flex; gap: 14px; flex-wrap: wrap;
}
.r-stat { display: flex; flex-direction: column; gap: 1px; }
.r-stat-val {
font-family: var(--font-mono);
font-size: 13px; font-weight: 500; color: var(--accent);
line-height: 1.1;
}
.r-stat-lbl {
font-size: 9px; text-transform: uppercase; letter-spacing: .09em;
color: var(--text-lo);
}
/* Section title */
.section-title {
font-size: 9px; text-transform: uppercase; letter-spacing: .12em;
color: var(--text-lo);
display: flex; align-items: center; gap: 6px;
margin-bottom: 8px;
}
.soon-pill {
font-size: 9px; text-transform: uppercase; letter-spacing: .08em;
background: var(--accent-bg);
color: var(--accent);
border: 1px solid var(--accent-border);
border-radius: 3px; padding: 1px 5px;
font-weight: 600;
}
/* Day cards */
.day-card {
border: 1px solid var(--border);
border-radius: 7px;
margin-bottom: 7px;
overflow: hidden;
background: var(--bg);
}
.day-card-head {
display: flex; align-items: center; gap: 9px;
padding: 9px 11px; cursor: pointer;
}
.day-num {
font-family: var(--font-mono);
font-size: 12px; font-weight: 500;
color: var(--accent);
flex-shrink: 0; min-width: 18px;
}
.day-info { flex: 1; min-width: 0; }
.day-title {
font-size: 12px; font-weight: 600; color: var(--text-hi);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.day-meta {
font-size: 11px; color: var(--text-md); font-weight: 400;
}
.day-km {
font-family: var(--font-mono);
font-size: 12px; font-weight: 500; color: var(--text-md);
flex-shrink: 0;
}
.chevron { color: var(--text-lo); font-size: 9px; flex-shrink: 0; }
/* Expanded day body — waypoint list */
.day-card-body {
border-top: 1px solid var(--border);
padding: 8px 11px 10px 11px;
background: var(--bg-subtle);
}
.wpt-row {
display: flex; align-items: flex-start; gap: 8px;
padding: 4px 0;
}
.wpt-row-connector {
display: flex; flex-direction: column; align-items: center;
padding-top: 3px; flex-shrink: 0;
}
.wpt-dot {
width: 7px; height: 7px; border-radius: 50%;
background: var(--border-md);
flex-shrink: 0;
}
.wpt-dot.special { background: var(--stop); }
.wpt-dot.accent { background: var(--accent); }
.wpt-line {
width: 1px; flex: 1; min-height: 14px;
background: var(--border); margin: 2px 0;
}
.wpt-row-info { flex: 1; padding-top: 0; }
.wpt-row-name {
font-size: 12px; font-weight: 500; color: var(--text-hi);
display: flex; align-items: center; gap: 5px; line-height: 1.3;
}
.wpt-tag {
font-size: 9px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em;
color: var(--stop); background: var(--stop-bg);
border: 1px solid var(--stop-border);
border-radius: 3px; padding: 0 4px;
}
.wpt-note {
font-size: 11px; color: var(--text-md); font-weight: 400;
font-style: italic; margin-top: 1px;
padding-left: 1px;
}
.wpt-note::before { content: '"'; }
.wpt-note::after { content: '"'; }
/* ─── ELEVATION PANEL ─── */
.elev-panel {
height: 148px;
background: var(--bg-raised);
border-top: 1px solid var(--border);
display: flex; flex-direction: column;
flex-shrink: 0;
}
.elev-header {
display: flex; align-items: center;
padding: 8px 16px 0;
gap: 12px;
}
.elev-eyebrow {
font-size: 9px; text-transform: uppercase; letter-spacing: .12em;
color: var(--text-lo);
}
.elev-stats {
margin-left: auto;
display: flex; gap: 20px;
}
.e-stat {
font-size: 12px; color: var(--text-md);
font-family: var(--font-mono);
}
.e-stat strong { color: var(--text-hi); font-weight: 500; }
.elev-chart {
flex: 1; padding: 4px 16px 10px; position: relative;
}
/* ══════════════════════
MOBILE LAYOUT
══════════════════════ */
.mobile-shell {
width: 390px;
background: var(--bg);
border-radius: 22px;
overflow: hidden;
box-shadow: 0 16px 56px rgba(0,0,0,.18), 0 4px 12px rgba(0,0,0,.1);
margin: 0 auto;
}
/* Mobile topbar */
.mob-topbar {
height: 48px;
background: var(--bg-raised);
border-bottom: 1px solid var(--border);
display: flex; align-items: center; justify-content: space-between;
padding: 0 16px;
position: relative;
}
.mob-logo {
font-family: var(--font-body);
font-size: 15px; font-weight: 600; color: var(--text-hi);
letter-spacing: -.02em;
display: flex; align-items: center; gap: 7px;
}
.mob-logo-mark svg { display: block; }
.mob-logo span { color: var(--accent); font-weight: 400; }
.mob-right { display: flex; align-items: center; gap: 8px; }
.mob-profile-select {
display: flex; align-items: center; gap: 5px;
background: var(--bg-subtle);
border: 1px solid var(--border);
border-radius: 5px; padding: 4px 8px;
font-size: 11px; font-weight: 500; color: var(--text-hi);
cursor: pointer;
}
.mob-participants { display: flex; }
.mob-av {
width: 24px; height: 24px; border-radius: 50%;
border: 2px solid var(--bg-raised);
font-size: 10px; font-weight: 700; color: #fff;
display: flex; align-items: center; justify-content: center;
margin-left: -6px; flex-shrink: 0;
}
.mob-av:first-child { margin-left: 0; }
/* Mobile map */
.mob-map {
height: 290px;
background: var(--map-tint);
position: relative; overflow: hidden;
}
.mob-map::before {
content: '';
position: absolute; inset: 0;
background-image:
repeating-linear-gradient(90deg, rgba(150,140,120,.1) 0, rgba(150,140,120,.1) 1px, transparent 0, transparent 18px),
repeating-linear-gradient(0deg, rgba(150,140,120,.1) 0, rgba(150,140,120,.1) 1px, transparent 0, transparent 18px);
}
/* Bottom sheet */
.mob-sheet {
background: var(--bg-raised);
border-top: 1px solid var(--border);
}
.mob-sheet-handle {
width: 32px; height: 3px; background: var(--border-md);
border-radius: 2px; margin: 9px auto 0;
}
.mob-sheet-summary {
padding: 10px 16px 10px;
border-bottom: 1px solid var(--border);
display: flex; align-items: flex-start; justify-content: space-between;
gap: 10px;
}
.mob-route-name {
font-size: 15px; font-weight: 600; color: var(--text-hi);
line-height: 1.2;
}
.mob-route-via {
font-size: 11px; color: var(--text-lo); margin-top: 2px;
}
.mob-stat-pills {
display: flex; flex-direction: column; gap: 3px; align-items: flex-end;
flex-shrink: 0;
}
.mob-stat-pill {
font-family: var(--font-mono);
font-size: 11px; font-weight: 500; color: var(--text-md);
white-space: nowrap;
}
.mob-stat-pill.km { color: var(--accent); font-size: 13px; }
/* Mini elevation */
.mob-elev { height: 64px; padding: 6px 16px 2px; }
/* Tab bar */
.mob-tabs {
display: flex; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
}
.mob-tab {
flex: 1; padding: 9px 8px; text-align: center;
font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .09em;
color: var(--text-lo); background: none; border: none;
font-family: var(--font-body);
border-bottom: 2px solid transparent; cursor: pointer;
transition: color .12s;
}
.mob-tab.active { color: var(--text-hi); border-bottom-color: var(--accent); }
/* Mobile day rows */
.mob-days { padding: 0; }
.mob-day-row {
display: flex; align-items: center; gap: 10px;
padding: 10px 16px;
border-bottom: 1px solid var(--border);
}
.mob-day-row:last-child { border-bottom: none; }
.mob-day-num {
font-family: var(--font-mono);
font-size: 12px; font-weight: 500; color: var(--accent);
flex-shrink: 0; min-width: 20px;
}
.mob-day-info { flex: 1; }
.mob-day-title { font-size: 13px; font-weight: 600; color: var(--text-hi); }
.mob-day-meta { font-size: 11px; color: var(--text-md); font-weight: 400; }
.mob-day-km {
font-family: var(--font-mono);
font-size: 13px; font-weight: 500; color: var(--text-md);
flex-shrink: 0;
}
/* ─── PAGE WRAPPER ─── */
.page-section {
padding: 36px 40px;
}
.screen-label {
font-size: 10px; text-transform: uppercase; letter-spacing: .12em; color: var(--text-lo);
margin-bottom: 4px; font-family: var(--font-body);
}
.screen-heading {
font-size: 22px; font-weight: 600; color: var(--text-hi);
margin-bottom: 24px; letter-spacing: -.02em;
}
.desktop-frame {
border-radius: 10px; overflow: hidden;
box-shadow: 0 8px 32px rgba(0,0,0,.12), 0 2px 8px rgba(0,0,0,.08);
border: 1px solid var(--border);
}
</style>
</head>
<body>
<!-- NAV -->
<nav class="page-nav">
<span class="page-nav-label">trails.cool v2</span>
<button class="nav-btn active" onclick="show('desktop')">Desktop</button>
<button class="nav-btn" onclick="show('mobile')">Mobile</button>
</nav>
<!-- ╔══════════════╗ -->
<!-- ║ DESKTOP ║ -->
<!-- ╚══════════════╝ -->
<div id="desktop" class="screen active">
<div class="page-section" style="background:var(--bg);">
<div class="screen-label">Revised direction — merging D1 warmth + D3 lightness</div>
<div class="screen-heading">trails.cool — Desktop</div>
<div class="desktop-frame" style="display:flex; flex-direction:column; height:calc(100vh - 180px); min-height:680px;">
<!-- TOP BAR -->
<div class="topbar">
<!-- Logo: Waypoint Dot mark + wordmark -->
<a class="logo">
<div class="logo-mark">
<svg width="24" height="22" viewBox="0 0 128 128" fill="none">
<path d="M28 90 C45 90, 50 50, 64 45 S85 70, 100 35" stroke="var(--accent)" stroke-width="6" stroke-linecap="round" fill="none"/>
<circle cx="28" cy="90" r="11" fill="var(--accent)"/>
<circle cx="64" cy="45" r="11" fill="var(--accent)"/>
<circle cx="100" cy="35" r="11" fill="var(--accent)"/>
</svg>
</div>
<span class="logo-wordmark">trails<span>.cool</span></span>
</a>
<!-- Profile -->
<div class="tb-section">
<div class="profile-select">
<svg width="13" height="13" viewBox="0 0 13 13" fill="none"><circle cx="6.5" cy="6.5" r="5.5" stroke="var(--accent)" stroke-width="1.2"/><path d="M4 7.5c.8 1 4.2 1 4.2 0M3.5 5.5h.5M9 5.5h.5" stroke="var(--accent)" stroke-width="1.1" stroke-linecap="round"/></svg>
Cycling (safe)
<span class="chevron"></span>
</div>
</div>
<!-- Participants — avatar stack, expands on click -->
<div class="participants" id="pax-zone">
<!-- You: always shown with name as it's the local user -->
<div class="av-you-wrap">
<div class="av" style="background:#4A6B40; margin-left:0;">Y</div>
<div>
<div class="av-you-name">You</div>
<div class="av-you-role">Host</div>
</div>
</div>
<!-- Others: stacked avatars, 3 visible max, then +N -->
<div class="avatar-stack" onclick="togglePanel()" title="View all participants">
<div class="av" style="background:#6B7FA8;">A</div>
<div class="av" style="background:#8B6D3A;">S</div>
<div class="av" style="background:#6A7B5A;">M</div>
<div class="av-overflow">+3</div>
</div>
<!-- Invite — link-based, no accounts -->
<button class="invite-btn" onclick="togglePanel()">
<svg width="11" height="11" viewBox="0 0 11 11" fill="none"><path d="M5.5 1v9M1 5.5h9" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>
Invite
</button>
<!-- Participants panel -->
<div class="participants-panel" id="pax-panel" style="display:none;">
<div class="pp-header">
<span class="pp-title">In this session</span>
<span class="pp-count">6 online</span>
</div>
<div class="pp-list">
<div class="pp-row">
<div class="pp-av online" style="background:#4A6B40;">Y</div>
<div class="pp-info">
<div class="pp-name">
You
<span class="pp-host-tag">Host</span>
</div>
<div class="pp-status">Editing route</div>
</div>
<div class="pp-cursor-dot" style="background:#4A6B40;"></div>
</div>
<div class="pp-row">
<div class="pp-av online" style="background:#6B7FA8;">A</div>
<div class="pp-info">
<div class="pp-name">Alex</div>
<div class="pp-status">Viewing · near Dessau</div>
</div>
<div class="pp-cursor-dot" style="background:#6B7FA8;"></div>
</div>
<div class="pp-row">
<div class="pp-av online" style="background:#8B6D3A;">S</div>
<div class="pp-info">
<div class="pp-name">Sara</div>
<div class="pp-status">Editing notes</div>
</div>
<div class="pp-cursor-dot" style="background:#8B6D3A;"></div>
</div>
<div class="pp-row">
<div class="pp-av online" style="background:#6A7B5A;">M</div>
<div class="pp-info">
<div class="pp-name">Mika</div>
<div class="pp-status">Viewing</div>
</div>
<div class="pp-cursor-dot" style="background:#6A7B5A;"></div>
</div>
<div class="pp-row">
<div class="pp-av online" style="background:#8A6080;">L</div>
<div class="pp-info">
<div class="pp-name">Lena</div>
<div class="pp-status">Idle</div>
</div>
<div class="pp-cursor-dot" style="background:#8A6080;"></div>
</div>
<div class="pp-row">
<div class="pp-av online" style="background:#5A7A8A;">T</div>
<div class="pp-info">
<div class="pp-name">Tom</div>
<div class="pp-status">Moving waypoint 2</div>
</div>
<div class="pp-cursor-dot" style="background:#5A7A8A;"></div>
</div>
</div>
<!-- Share link -->
<div class="pp-share">
<div class="pp-share-label">Share link — anyone can join</div>
<div class="pp-share-row">
<div class="pp-share-input">trails.cool/s/cb6ada8e</div>
<button class="pp-copy-btn" id="copy-btn" onclick="copyLink()">Copy</button>
</div>
</div>
</div>
</div>
<!-- Right actions -->
<div class="tb-actions">
<div class="color-mode">
<button class="cm-btn">Plain</button>
<button class="cm-btn active">Elevation</button>
<button class="cm-btn">Surface</button>
</div>
<button class="btn-ghost">
<svg width="11" height="11" viewBox="0 0 11 11" fill="none"><path d="M5.5 1v6.5M2.5 5l3 3.5 3-3.5M1 10h9" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/></svg>
Export GPX
</button>
<div class="session-status">
<div class="session-dot"></div>
Connected · cb6ada8e
</div>
</div>
</div>
<!-- PROGRESS BAR -->
<div class="progress-bar" style="position:relative; height:0; overflow:visible;">
<div class="progress-bar" style="position:absolute; width:62%; top:0;"></div>
</div>
<!-- MAP + SIDEBAR -->
<div class="main">
<div class="map-area">
<!-- Map controls -->
<div class="map-ctrl-group">
<div class="map-ctrl">+</div>
<div class="map-ctrl"></div>
<div class="map-ctrl icon"></div>
<div class="map-ctrl icon"></div>
</div>
<!-- No-go area -->
<div class="nogo" style="left:36%; top:24%; width:82px; height:60px;"></div>
<!-- Route SVG -->
<svg class="route-layer" viewBox="0 0 800 500" preserveAspectRatio="none">
<defs>
<linearGradient id="rte-grad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#5A8F46"/>
<stop offset="38%" style="stop-color:#9ABF50"/>
<stop offset="62%" style="stop-color:#C4A840"/>
<stop offset="84%" style="stop-color:#C46040"/>
<stop offset="100%" style="stop-color:#A03030"/>
</linearGradient>
</defs>
<!-- Main route line -->
<path d="M 375 78
C 362 110 352 140 342 175
C 328 218 316 252 304 285
C 288 325 276 358 268 392
C 262 416 258 435 254 455"
stroke="url(#rte-grad)" stroke-width="3.5" fill="none"
stroke-linecap="round" stroke-linejoin="round" opacity=".92"/>
<!-- Day 1/2 divider hint on route — subtle tick -->
<line x1="267" y1="390" x2="280" y2="390"
stroke="var(--stop)" stroke-width="1.5"
stroke-dasharray="4,3" opacity="0.45"/>
</svg>
<!-- WAYPOINTS -->
<!-- 1: Berlin Alexanderplatz — start -->
<div class="wpt" style="left:46.9%; top:15.5%;">
<div class="wpt-pin is-start">1</div>
<div class="wpt-label">Berlin Alexanderplatz</div>
</div>
<!-- 2: Dessau — overnight stop -->
<div class="wpt" style="left:33.5%; top:78%;">
<div class="wpt-overnight-badge">Night 1</div>
<div class="wpt-pin is-overnight" style="position:relative;">
2
<div class="wpt-note-dot"></div>
</div>
<div class="wpt-label">Dessau</div>
</div>
<!-- 3: Erfurt — end -->
<div class="wpt" style="left:31.8%; top:91%;">
<div class="wpt-pin is-end">3</div>
<div class="wpt-label">Erfurt Altstadt</div>
</div>
<!-- Day labels -->
<div class="day-badge" style="left:40%; top:38%;">Day 1 · 120 km</div>
<div class="day-badge" style="left:33%; top:84.5%;">Day 2 · 130 km</div>
<!-- Multiple live cursors — 6 participants -->
<div class="cursor" style="left:48%; top:52%;">
<div class="cursor-dot" style="background:#6B7FA8;"></div>
<div class="cursor-label" style="background:#6B7FA8;">Alex</div>
</div>
<div class="cursor" style="left:55%; top:30%;">
<div class="cursor-dot" style="background:#8B6D3A;"></div>
<div class="cursor-label" style="background:#8B6D3A;">Sara</div>
</div>
<div class="cursor" style="left:28%; top:60%;">
<div class="cursor-dot" style="background:#5A7A8A;"></div>
<div class="cursor-label" style="background:#5A7A8A;">Tom</div>
</div>
<div class="map-credit">© OpenStreetMap</div>
</div>
<!-- SIDEBAR -->
<div class="sidebar">
<div class="sidebar-tabs">
<button class="stab active">Waypoints</button>
<button class="stab">Notes</button>
</div>
<div class="sidebar-body">
<!-- Route header -->
<div class="route-header">
<div class="route-eyebrow">Active route</div>
<div class="route-name">Berlin → Erfurt <span class="route-via">via Dessau</span></div>
<div class="route-stats">
<div class="r-stat">
<span class="r-stat-val">343 km</span>
<span class="r-stat-lbl">Distance</span>
</div>
<div class="r-stat">
<span class="r-stat-val">↑ 868 m</span>
<span class="r-stat-lbl">Ascent</span>
</div>
<div class="r-stat">
<span class="r-stat-val">3 days</span>
<span class="r-stat-lbl">Duration</span>
</div>
</div>
</div>
<!-- Day breakdown -->
<div class="section-title">
Day breakdown
<span class="soon-pill">Soon</span>
</div>
<!-- Day 1 — expanded -->
<div class="day-card">
<div class="day-card-head">
<span class="day-num">01</span>
<div class="day-info">
<div class="day-title">Berlin → Dessau</div>
<div class="day-meta">↑ 340 m ascent</div>
</div>
<span class="day-km">120 km</span>
<span class="chevron"></span>
</div>
<div class="day-card-body">
<!-- Waypoint list — simple, not every one is a highlight -->
<div class="wpt-row">
<div class="wpt-row-connector">
<div class="wpt-dot accent"></div>
<div class="wpt-line"></div>
</div>
<div class="wpt-row-info">
<div class="wpt-row-name">Berlin Alexanderplatz</div>
</div>
</div>
<div class="wpt-row">
<div class="wpt-row-connector">
<div class="wpt-dot"></div>
<div class="wpt-line"></div>
</div>
<div class="wpt-row-info">
<div class="wpt-row-name" style="color:var(--text-md); font-weight:400; font-size:11px;">Zossen</div>
</div>
</div>
<div class="wpt-row">
<div class="wpt-row-connector">
<div class="wpt-dot"></div>
<div class="wpt-line"></div>
</div>
<div class="wpt-row-info">
<div class="wpt-row-name" style="color:var(--text-md); font-weight:400; font-size:11px;">Jüterbog</div>
</div>
</div>
<div class="wpt-row">
<div class="wpt-row-connector">
<div class="wpt-dot special"></div>
</div>
<div class="wpt-row-info">
<div class="wpt-row-name">
Dessau
<span class="wpt-tag">Overnight</span>
</div>
<div class="wpt-note">Elbe crossing — beautiful views</div>
</div>
</div>
</div>
</div>
<!-- Day 2 — collapsed -->
<div class="day-card">
<div class="day-card-head">
<span class="day-num">02</span>
<div class="day-info">
<div class="day-title">Dessau → Halle</div>
<div class="day-meta">↑ 310 m ascent</div>
</div>
<span class="day-km">130 km</span>
<span class="chevron"></span>
</div>
</div>
<!-- Day 3 — collapsed -->
<div class="day-card">
<div class="day-card-head">
<span class="day-num">03</span>
<div class="day-info">
<div class="day-title">Halle → Erfurt</div>
<div class="day-meta">↑ 218 m ascent</div>
</div>
<span class="day-km">93 km</span>
<span class="chevron"></span>
</div>
</div>
</div>
</div>
</div>
<!-- ELEVATION PANEL -->
<div class="elev-panel">
<div class="elev-header">
<span class="elev-eyebrow">Elevation profile</span>
<div class="elev-stats">
<span class="e-stat"><strong>343.3 km</strong> distance</span>
<span class="e-stat"><strong>↑ 868 m</strong> ascent</span>
</div>
</div>
<div class="elev-chart">
<svg width="100%" height="100%" viewBox="0 0 960 90" preserveAspectRatio="none">
<defs>
<linearGradient id="ec-line" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#5A8F46"/>
<stop offset="35%" style="stop-color:#9ABF50"/>
<stop offset="58%" style="stop-color:#C4A840"/>
<stop offset="78%" style="stop-color:#C46040"/>
<stop offset="100%" style="stop-color:#A03030"/>
</linearGradient>
<linearGradient id="ec-fill" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#4A6B40; stop-opacity:.18"/>
<stop offset="100%" style="stop-color:#4A6B40; stop-opacity:.02"/>
</linearGradient>
</defs>
<!-- Fill -->
<path d="M 0 84 L 48 76 L 96 72 L 144 69
L 192 67 L 240 65 L 288 63 L 320 68
L 352 62 L 384 58 L 416 55 L 448 57
L 480 53 L 520 49 L 560 45 L 600 42
L 640 46 L 672 50 L 710 53 L 748 56
L 786 52 L 830 47 L 870 44 L 910 46
L 960 41
L 960 90 L 0 90 Z"
fill="url(#ec-fill)"/>
<!-- Line -->
<path d="M 0 84 L 48 76 L 96 72 L 144 69
L 192 67 L 240 65 L 288 63 L 320 68
L 352 62 L 384 58 L 416 55 L 448 57
L 480 53 L 520 49 L 560 45 L 600 42
L 640 46 L 672 50 L 710 53 L 748 56
L 786 52 L 830 47 L 870 44 L 910 46
L 960 41"
stroke="url(#ec-line)" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<!-- Day dividers -->
<line x1="320" y1="4" x2="320" y2="90" stroke="var(--stop)" stroke-width="1" stroke-dasharray="4,4" opacity=".35"/>
<line x1="640" y1="4" x2="640" y2="90" stroke="var(--stop)" stroke-width="1" stroke-dasharray="4,4" opacity=".35"/>
<!-- Day labels -->
<text x="160" y="13" fill="var(--text-lo)" font-size="8" text-anchor="middle" font-family="Outfit,sans-serif" letter-spacing=".06em">Day 1</text>
<text x="480" y="13" fill="var(--text-lo)" font-size="8" text-anchor="middle" font-family="Outfit,sans-serif" letter-spacing=".06em">Day 2</text>
<text x="800" y="13" fill="var(--text-lo)" font-size="8" text-anchor="middle" font-family="Outfit,sans-serif" letter-spacing=".06em">Day 3</text>
<!-- Y axis labels -->
<text x="4" y="15" fill="var(--text-lo)" font-size="7.5" font-family="Geist Mono,monospace">286m</text>
<text x="4" y="89" fill="var(--text-lo)" font-size="7.5" font-family="Geist Mono,monospace">31m</text>
<text x="4" y="90" fill="var(--text-lo)" font-size="7" font-family="Geist Mono,monospace" dy="-1">0</text>
<text x="956" y="90" fill="var(--text-lo)" font-size="7" font-family="Geist Mono,monospace" text-anchor="end" dy="-1">342 km</text>
<!-- Hover crosshair -->
<line x1="448" y1="4" x2="448" y2="90" stroke="var(--stop)" stroke-width="1" opacity=".4"/>
<circle cx="448" cy="57" r="3.5" fill="var(--stop)"/>
<!-- Tooltip -->
<rect x="452" y="44" width="50" height="17" rx="4" fill="var(--bg-raised)" stroke="var(--border)" stroke-width="1"/>
<text x="477" y="57" fill="var(--text-hi)" font-size="8" text-anchor="middle" font-family="Geist Mono,monospace">km 171</text>
</svg>
</div>
</div>
</div><!-- /desktop-frame -->
</div><!-- /page-section -->
</div><!-- /desktop screen -->
<!-- ╔══════════════╗ -->
<!-- ║ MOBILE ║ -->
<!-- ╚══════════════╝ -->
<div id="mobile" class="screen">
<div class="page-section" style="background:var(--bg-subtle); min-height:100vh;">
<div class="screen-label">Revised direction</div>
<div class="screen-heading">trails.cool — Mobile</div>
<div class="mobile-shell">
<!-- Mobile topbar -->
<div class="mob-topbar">
<!-- Logo -->
<div class="mob-logo">
<div class="mob-logo-mark">
<svg width="20" height="18" viewBox="0 0 128 128" fill="none">
<path d="M28 90 C45 90, 50 50, 64 45 S85 70, 100 35" stroke="var(--accent)" stroke-width="8" stroke-linecap="round" fill="none"/>
<circle cx="28" cy="90" r="13" fill="var(--accent)"/>
<circle cx="64" cy="45" r="13" fill="var(--accent)"/>
<circle cx="100" cy="35" r="13" fill="var(--accent)"/>
</svg>
</div>
trails<span>.cool</span>
</div>
<div class="mob-right">
<div class="mob-profile-select">
🚲 Cycling (safe)
<span style="color:var(--text-lo); font-size:9px;"></span>
</div>
<div class="mob-pax-zone">
<div class="mob-av-stack">
<div class="mob-av" style="background:#4A6B40;">Y</div>
<div class="mob-av" style="background:#6B7FA8;">A</div>
<div class="mob-av" style="background:#8B6D3A;">S</div>
<div class="mob-av-overflow">+3</div>
</div>
</div>
</div>
</div>
<!-- Map -->
<div class="mob-map">
<!-- Progress bar -->
<div class="progress-bar" style="position:absolute; top:0; z-index:50; width:62%;"></div>
<!-- No-go -->
<div class="nogo" style="left:56%; top:28%; width:62px; height:46px;"></div>
<!-- Route -->
<svg style="position:absolute;inset:0;z-index:5;pointer-events:none;" viewBox="0 0 390 290" preserveAspectRatio="none">
<defs>
<linearGradient id="mob-rte" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#5A8F46"/>
<stop offset="50%" style="stop-color:#C4A840"/>
<stop offset="100%" style="stop-color:#C46040"/>
</linearGradient>
</defs>
<path d="M 195 30 C 185 65 175 100 168 138 C 160 178 154 210 149 240 C 146 258 144 271 142 282"
stroke="url(#mob-rte)" stroke-width="3" fill="none" stroke-linecap="round" stroke-linejoin="round" opacity=".9"/>
</svg>
<!-- Waypoints -->
<div class="wpt" style="left:50%; top:10%;">
<div class="wpt-pin is-start">1</div>
<div class="wpt-label">Berlin</div>
</div>
<div class="wpt" style="left:36.4%; top:83%;">
<div class="wpt-overnight-badge">Night 1</div>
<div class="wpt-pin is-overnight" style="position:relative;">2<div class="wpt-note-dot"></div></div>
<div class="wpt-label">Dessau</div>
</div>
<div class="wpt" style="left:36.4%; top:97%; transform:translate(-50%,-100%);">
<div class="wpt-pin is-end">3</div>
<div class="wpt-label">Erfurt</div>
</div>
<!-- Day label -->
<div class="day-badge" style="left:43%; top:42%;">Day 1 · 120 km</div>
<!-- Cursor -->
<div class="cursor" style="left:56%; top:52%;">
<div class="cursor-dot" style="background:#6B7FA8;"></div>
<div class="cursor-label" style="background:#6B7FA8;">Alex</div>
</div>
<div class="map-credit" style="z-index:10;">© OpenStreetMap</div>
</div>
<!-- Bottom sheet -->
<div class="mob-sheet">
<div class="mob-sheet-handle"></div>
<div class="mob-sheet-summary">
<div>
<div class="mob-route-name">Berlin → Erfurt</div>
<div class="mob-route-via">3 days · via Dessau · Elevation gradient</div>
</div>
<div class="mob-stat-pills">
<span class="mob-stat-pill km">343 km</span>
<span class="mob-stat-pill">↑ 868 m</span>
</div>
</div>
<!-- Mini elevation -->
<div class="mob-elev">
<svg width="100%" height="100%" viewBox="0 0 358 52" preserveAspectRatio="none">
<defs>
<linearGradient id="mob-ec" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#5A8F46"/>
<stop offset="50%" style="stop-color:#C4A840"/>
<stop offset="100%" style="stop-color:#C46040"/>
</linearGradient>
</defs>
<path d="M 0 46 L 44 38 L 88 35 L 120 32 L 155 28 L 190 25 L 219 22 L 248 25 L 280 28 L 315 22 L 358 18 L 358 52 L 0 52 Z" fill="rgba(74,107,64,.12)"/>
<path d="M 0 46 L 44 38 L 88 35 L 120 32 L 155 28 L 190 25 L 219 22 L 248 25 L 280 28 L 315 22 L 358 18" stroke="url(#mob-ec)" stroke-width="1.8" fill="none" stroke-linecap="round"/>
<line x1="120" y1="0" x2="120" y2="52" stroke="var(--stop)" stroke-width=".8" stroke-dasharray="3,3" opacity=".35"/>
<line x1="240" y1="0" x2="240" y2="52" stroke="var(--stop)" stroke-width=".8" stroke-dasharray="3,3" opacity=".35"/>
<text x="60" y="50" fill="var(--text-lo)" font-size="7" font-family="Outfit,sans-serif" text-anchor="middle">Day 1</text>
<text x="180" y="50" fill="var(--text-lo)" font-size="7" font-family="Outfit,sans-serif" text-anchor="middle">Day 2</text>
<text x="299" y="50" fill="var(--text-lo)" font-size="7" font-family="Outfit,sans-serif" text-anchor="middle">Day 3</text>
</svg>
</div>
<!-- Tab bar -->
<div class="mob-tabs">
<button class="mob-tab active">Days</button>
<button class="mob-tab">Waypoints</button>
<button class="mob-tab">Notes</button>
</div>
<!-- Day list -->
<div class="mob-days">
<div class="mob-day-row">
<span class="mob-day-num">01</span>
<div class="mob-day-info">
<div class="mob-day-title">Berlin → Dessau</div>
<div class="mob-day-meta">↑ 340 m ascent</div>
</div>
<span class="mob-day-km">120 km</span>
</div>
<div class="mob-day-row">
<span class="mob-day-num">02</span>
<div class="mob-day-info">
<div class="mob-day-title">Dessau → Halle</div>
<div class="mob-day-meta">↑ 310 m ascent</div>
</div>
<span class="mob-day-km">130 km</span>
</div>
<div class="mob-day-row">
<span class="mob-day-num">03</span>
<div class="mob-day-info">
<div class="mob-day-title">Halle → Erfurt</div>
<div class="mob-day-meta">↑ 218 m ascent</div>
</div>
<span class="mob-day-km">93 km</span>
</div>
</div>
</div>
</div><!-- /mobile-shell -->
</div><!-- /page-section -->
</div><!-- /mobile screen -->
<script>
function show(id) {
document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active'));
document.getElementById(id).classList.add('active');
event.currentTarget.classList.add('active');
window.scrollTo(0,0);
}
function togglePanel() {
const panel = document.getElementById('pax-panel');
const isOpen = panel.style.display !== 'none';
panel.style.display = isOpen ? 'none' : 'block';
}
// Close panel when clicking outside
document.addEventListener('click', function(e) {
const zone = document.getElementById('pax-zone');
const panel = document.getElementById('pax-panel');
if (panel && !zone.contains(e.target)) {
panel.style.display = 'none';
}
});
function copyLink() {
const btn = document.getElementById('copy-btn');
btn.textContent = 'Copied!';
btn.classList.add('copied');
setTimeout(() => {
btn.textContent = 'Copy';
btn.classList.remove('copied');
}, 2000);
}
</script>
</body>
</html>