auto-sync: 2026-05-05 02:00:01

This commit is contained in:
Stream
2026-05-05 02:00:01 +03:00
parent 9eee1ab2e0
commit b4289a7e8e
4 changed files with 960 additions and 684 deletions

View File

@@ -22,23 +22,41 @@
## 3. Дизайн-система
### 3.1 Цветовая палитра
### 3.1 Цветовая палитра — две темы
Тема переключается кнопкой ☀️/🌙 в search bar. Автодетект: если время 07:0020:00 → светлая, иначе → тёмная.
**Тёмная (ночная езда):**
```
Фон приложения: #0D1117 (почти чёрный, как грязь ночью)
Поверхности: #161B22 (тёмно-серый)
Поверхности 2: #21262D (чуть светлее)
Акцент (оранж): #FF6B00 (цвет эндуро-резины, огня)
Акцент hover: #FF8C2A
Золото (Lev1-2): #FFD700
Красный (Lev3-5): #FF3B1F
Текст primary: #E6EDF3
Текст secondary: #8B949E
Текст muted: #484F58
Успех: #2EA043
Граница: #30363D
--bg: #0D1117
--surface: #161B22
--surface2: #21262D
--border: #30363D
--text: #E6EDF3
--text2: #8B949E
--accent: #FF6B00
--gold: #FFD700
--red: #FF3B1F
--success: #2EA043
```
**Светлая (дневная езда):**
```
--bg: #F5F5F0 (бежевый, не слепит на солнце)
--surface: #FFFFFF
--surface2: #F0F0EA
--border: #D0CFC8
--text: #1A1A1A
--text2: #6B6B6B
--accent: #E55A00 (оранжевый чуть темнее — виден на белом)
--gold: #C89B00
--red: #CC2200
--success: #1A7A30
```
Реализация через CSS custom properties на `:root` + класс `body.theme-dark` / `body.theme-light`.
Стиль карты MapLibre меняется соответственно (тёмный/светлый style.json — уже существуют).
### 3.2 Типографика
```

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
// ─── Общее: деактивация всех режимов ────────────────────────────────────────────
function deactivateAllModes() {
if (routeMode) { routeMode = false; document.getElementById('btn-route').classList.remove('active'); document.getElementById('route-panel').style.display = 'none'; clearRoute(); }
if (routeMode) { routeMode = false; document.getElementById('tb-route').classList.remove('active'); closeSheet('sheet-route'); clearRoute(); }
if (rulerMode) toggleRuler();
if (markerMode) toggleMarkerMode();
if (typeof reconMode !== 'undefined' && reconMode) toggleReconMode();
@@ -65,7 +65,7 @@ function locateMe() {
alert('Геолокация недоступна в этом браузере');
return;
}
const btn = document.getElementById('btn-locate');
const btn = document.getElementById('btn-locate-x');
btn.textContent = '⏳';
navigator.geolocation.getCurrentPosition(
(pos) => {
@@ -138,7 +138,7 @@ function getBasePath() {
// ─── Режим маршрута ───────────────────────────────────────────────────────────
function toggleRouteMode() {
routeMode = !routeMode;
const btn = document.getElementById('btn-route');
const btn = document.getElementById('tb-route');
const panel = document.getElementById('route-panel');
if (routeMode) {
deactivateAllModes();
@@ -618,7 +618,7 @@ function saveMarkers(markers) {
function toggleMarkerMode() {
markerMode = !markerMode;
const btn = document.getElementById('btn-markers');
const btn = document.getElementById('tb-marker');
if (markerMode) {
deactivateAllModes();
markerMode = true;
@@ -1030,7 +1030,7 @@ let rulerTotal = 0;
function toggleRuler() {
rulerMode = !rulerMode;
const btn = document.getElementById('btn-ruler');
const btn = document.getElementById('tb-ruler');
if (rulerMode) {
deactivateAllModes();
rulerMode = true;
@@ -1136,7 +1136,7 @@ let reconRadius = 20;
function toggleReconMode() {
reconMode = !reconMode;
const btn = document.getElementById('btn-recon');
const btn = document.getElementById('tb-recon');
if (reconMode) {
deactivateAllModes();
reconMode = true;
@@ -1222,7 +1222,7 @@ function clearRecon() {
if (map.getLayer('recon-circle-fill')) map.removeLayer('recon-circle-fill');
if (map.getLayer('recon-circle-stroke')) map.removeLayer('recon-circle-stroke');
if (map.getSource('recon-circle')) map.removeSource('recon-circle');
document.getElementById('recon-panel').style.display = 'none';
closeSheet('sheet-recon');
reconCenter = null;
}
@@ -1233,13 +1233,13 @@ let linkMarkers = [];
function toggleLinkMode() {
linkMode = !linkMode;
const btn = document.getElementById('btn-link');
const btn = document.getElementById('tb-link');
if (linkMode) {
deactivateAllModes();
linkMode = true;
btn.classList.add('active');
window._map.getCanvas().style.cursor = 'crosshair';
document.getElementById('link-panel').style.display = 'block';
openSheet('sheet-link');
document.getElementById('link-status').textContent = '1⃣ Кликни конец первого трека';
document.getElementById('link-cards').innerHTML = '';
} else {
@@ -1356,7 +1356,7 @@ function clearLink() {
const sid = `link-src-${i}`;
if (map.getSource(sid)) map.removeSource(sid);
}
document.getElementById('link-panel').style.display = 'none';
closeSheet('sheet-link');
document.getElementById('link-cards').innerHTML = '';
}
@@ -1370,13 +1370,13 @@ let activeScenicIdx = 0;
function toggleScenicMode() {
scenicMode = !scenicMode;
const btn = document.getElementById('btn-scenic');
const btn = document.getElementById('tb-scenic');
if (scenicMode) {
deactivateAllModes();
scenicMode = true;
btn.classList.add('active');
window._map.getCanvas().style.cursor = 'crosshair';
document.getElementById('scenic-panel').style.display = 'block';
openSheet('sheet-scenic');
document.getElementById('scenic-status').textContent = 'Кликни точку старта на карте';
} else {
btn.classList.remove('active');
@@ -1499,7 +1499,7 @@ function clearScenic() {
if (scenicStartMarker) { scenicStartMarker.remove(); scenicStartMarker = null; }
scenicStart = null;
scenicRoutes = [];
document.getElementById('scenic-panel').style.display = 'none';
closeSheet('sheet-scenic');
const cardsEl = document.getElementById('scenic-cards');
if (cardsEl) cardsEl.innerHTML = '';
}

View File

@@ -1,206 +1,235 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Enduro Trails — ЦФО + Чувашия</title>
<!-- MapLibre GL JS -->
<link href="https://unpkg.com/maplibre-gl@4.1.3/dist/maplibre-gl.css" rel="stylesheet" />
<script src="https://unpkg.com/maplibre-gl@4.1.3/dist/maplibre-gl.js"></script>
<link rel="stylesheet" href="app.css" />
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=no">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<title>Enduro Trails</title>
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@4.7.0/dist/maplibre-gl.css">
<link rel="stylesheet" href="app.css">
</head>
<body>
<body class="theme-dark">
<div id="header">
<div>
<h1>🏍 Enduro Trails</h1>
<div class="subtitle">ЦФО + Чувашия — грунтовые дороги</div>
</div>
<div id="controls">
<button class="toggle-btn active" id="btn-tracks" onclick="toggleLayer('tracks')">
<span class="dot" style="background:#FFD700"></span><span class="dot" style="background:#FF4400; margin-left:2px"></span> Грунтовки
</button>
<button class="toggle-btn active" id="btn-paths" onclick="toggleLayer('paths')">
<span class="dot" style="background:#cc0000"></span> Тропы
</button>
<button class="toggle-btn active" id="btn-poi" onclick="toggleLayer('poi')">
<span class="dot" style="background:#44ff88"></span> POI
</button>
<button class="toggle-btn active" id="btn-basemap" onclick="toggleLayer('basemap')">
🗺 Подложка
<!-- Map -->
<div id="map"></div>
<!-- Sheet backdrop -->
<div id="sheet-backdrop" onclick="closeAllSheets()"></div>
<!-- ── Search Bar ─────────────────────────── -->
<div id="search-bar">
<svg class="sb-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
<input type="text" id="search-input" placeholder="Поиск места..." autocomplete="off" autocorrect="off">
<button id="btn-theme" onclick="toggleTheme()" title="Переключить тему">
<svg id="theme-icon-sun" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:none"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/></svg>
<svg id="theme-icon-moon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg>
</button>
</div>
<div id="search-results"></div>
<!-- ── Ruler info ─────────────────────────── -->
<div id="ruler-info">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg>
<span id="ruler-dist">0 км</span>
<span style="color:var(--text3);font-size:11px;margin-left:4px">— тапни точки, дважды для завершения</span>
</div>
<!-- ── No data warning ───────────────────── -->
<div id="no-data-warning">⚠️ База данных недоступна</div>
<!-- ── Map Buttons (right) ───────────────── -->
<div id="map-controls-r">
<button class="map-btn" id="btn-compass" onclick="toggleCompass()" title="Компас">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m16.24 7.76-2.12 6.36-6.36 2.12 2.12-6.36 6.36-2.12z"/></svg>
</button>
<button class="map-btn" onclick="locateMe()" title="Моё местоположение">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="3 11 22 2 13 21 11 13 3 11"/></svg>
</button>
</div>
<!-- ════════════════════════════════════════════
BOTTOM SHEETS
════════════════════════════════════════════ -->
<!-- ── Sheet: Маршрут ────────────────────── -->
<div class="bottom-sheet" id="sheet-route">
<div class="sheet-handle"></div>
<div class="sheet-header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 10c-.83 0-1.5-.67-1.5-1.5v-5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5v5c0 .83-.67 1.5-1.5 1.5z"/><path d="M20.5 10H19V8.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/><path d="M9.5 14c.83 0 1.5.67 1.5 1.5v5c0 .83-.67 1.5-1.5 1.5S8 21.33 8 20.5v-5c0-.83.67-1.5 1.5-1.5z"/><path d="M3.5 14H5v1.5c0 .83-.67 1.5-1.5 1.5S2 16.33 2 15.5 2.67 14 3.5 14z"/><path d="M14 5H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-3"/></svg>
<h2>Маршрут</h2>
<button class="sheet-close" onclick="toggleRouteMode()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
</button>
</div>
<div id="search-box">
<input type="text" id="search-input" placeholder="🔍 Поиск места..." autocomplete="off" />
<div id="search-results"></div>
<div class="sheet-body">
<div id="waypoints-list"></div>
<div class="route-actions" id="route-actions" style="display:none">
<button class="btn-action" onclick="addWaypointMode()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg>
Точка
</button>
<button class="btn-action primary" onclick="downloadGPX()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
GPX
</button>
<button class="btn-action danger" onclick="clearRoute()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg>
Сброс
</button>
</div>
<div id="route-status" class="text-muted">Тапни точку старта на карте</div>
<div id="route-cards"></div>
</div>
</div>
<div id="map-container">
<div id="map"></div>
<div id="loading" class="visible">
<div class="spinner"></div>
<div>Загрузка карты...</div>
</div>
<div id="no-data-warning">
<h2>⚠️ Данные не загружены</h2>
<p>
База данных не найдена или пуста.<br><br>
Для загрузки данных выполните:<br>
<code>bash scripts/download.sh</code><br>
<code>python scripts/parse.py</code>
</p>
</div>
<div id="legend">
<h3>Легенда</h3>
<div class="legend-item">
<div class="legend-line" style="background:#FFD700; height:2px"></div>
<span>Lev1-2</span>
</div>
<div class="legend-item">
<div class="legend-line" style="background:#FF4400; height:4px"></div>
<span>Lev3-5</span>
</div>
<div class="legend-item">
<div class="legend-dashed" style="border-top-color:#cc0000"></div>
<span>Тропа</span>
</div>
<div style="margin-top:8px; border-top:1px solid #ddd; padding-top:8px;">
<div class="legend-item">
<span class="dot" style="background:#ff4444"></span><span>Вершина</span>
</div>
<div class="legend-item">
<span class="dot" style="background:#4488ff"></span><span>Вода</span>
</div>
<div class="legend-item">
<span class="dot" style="background:#44ff88"></span><span>Смотровая</span>
</div>
<div class="legend-item">
<span class="dot" style="background:#cc88ff"></span><span>Руины</span>
</div>
<div class="legend-item">
<span class="dot" style="background:#ffaa00"></span><span>Пещера</span>
</div>
<div class="legend-item">
<span class="dot" style="background:#00ccff"></span><span>Брод</span>
</div>
</div>
</div>
<div id="stats">Zoom: <span id="zoom-val">7</span> | Координаты: <span id="coords-val"></span></div>
<!-- ─── Панель маршрута (Фаза 3) ─────────────────────────────────────── -->
<div id="route-panel" style="display:none; position:absolute; bottom:40px;
background:rgba(255,255,255,0.97); border:1px solid #ddd; border-radius:8px;
padding:12px; font-size:13px; z-index:5; width:280px;
box-shadow:0 2px 12px rgba(0,0,0,0.15); max-height:70vh; overflow-y:auto;">
<!-- Панель точек -->
<div id="waypoints-panel">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:8px;">
<span style="font-weight:600; color:#e07b00;">📍 Точки маршрута</span>
<button id="btn-add-waypoint" onclick="startAddWaypoint()"
style="font-size:11px; padding:3px 8px; background:#f0f0f0; border:1px solid #ccc; border-radius:4px; cursor:pointer;">
+ Точка
</button>
</div>
<div id="waypoints-list"></div>
</div>
<div id="route-status" style="color:#888; font-size:12px; margin:8px 0;">Кликни точку старта</div>
<!-- Кнопки действий -->
<div id="route-actions" style="display:none; margin-top:8px;">
<button onclick="buildRoute()" id="btn-build-route"
style="width:100%; padding:6px; background:#ff6600; color:#fff; border:none; border-radius:4px; cursor:pointer; font-size:13px; font-weight:600;">
🗺️ Построить маршрут
</button>
<button onclick="clearRoute()"
style="width:100%; margin-top:4px; padding:4px; background:#f0f0f0; border:1px solid #ccc; border-radius:4px; cursor:pointer; font-size:12px;">
✕ Сбросить всё
</button>
</div>
<!-- Карточки маршрутов -->
<div id="route-cards" style="margin-top:10px;"></div>
</div>
<!-- ─── Панель Разведки (F-14) ──────────────────────────────────────────── -->
<div id="recon-panel" style="display:none; position:absolute; bottom:40px; left:12px;
background:rgba(255,255,255,0.97); border:1px solid #ddd; border-radius:8px;
padding:12px; font-size:13px; z-index:5; width:240px;
box-shadow:0 2px 12px rgba(0,0,0,0.15);">
<div style="font-weight:600; color:#e07b00; margin-bottom:8px;">📍 Разведка</div>
<div id="recon-stats" style="color:#888; font-size:12px;">Кликни на карту для разведки</div>
<div style="margin-top:8px; display:flex; gap:4px;">
<button onclick="setReconRadius(20)" class="recon-radius-btn active" data-km="20">20 км</button>
<button onclick="setReconRadius(50)" class="recon-radius-btn" data-km="50">50 км</button>
<button onclick="setReconRadius(100)" class="recon-radius-btn" data-km="100">100 км</button>
</div>
<button onclick="clearRecon()" style="width:100%; margin-top:6px; padding:4px; background:#f0f0f0; border:1px solid #ccc; border-radius:4px; cursor:pointer; font-size:11px;">
✕ Сбросить
<!-- ── Sheet: Разведка ───────────────────── -->
<div class="bottom-sheet" id="sheet-recon">
<div class="sheet-handle"></div>
<div class="sheet-header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12C2 6.5 6.5 2 12 2a10 10 0 0 1 8 4"/><path d="M5 19.5C5.5 18 6 15 6 12c0-.7.12-1.37.34-2"/><path d="M17.29 21.02c.12-.6.43-2.3.5-3.02"/><path d="M12 10a2 2 0 0 0-2 2c0 1.02-.1 2.51-.26 4"/><path d="M8.65 22c.21-.66.45-1.32.57-2"/><path d="M14 13.12c0 2.38 0 6.38-1 8.88"/><path d="M2 16h.01"/><path d="M21.8 16c.2-2 .131-5.354 0-6"/><path d="M9 6.8a6 6 0 0 1 9 5.2c0 .47 0 1.17-.02 2"/></svg>
<h2>Разведка</h2>
<button class="sheet-close" onclick="toggleReconMode()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
</button>
</div>
<!-- ─── Панель Связки (F-13) ────────────────────────────────────────────── -->
<div id="link-panel" style="display:none; position:absolute; bottom:40px;
background:rgba(255,255,255,0.97); border:1px solid #ddd; border-radius:8px;
padding:12px; font-size:13px; z-index:5; width:280px;
box-shadow:0 2px 12px rgba(0,0,0,0.15); max-height:70vh; overflow-y:auto;">
<div style="font-weight:600; color:#e07b00; margin-bottom:8px;">🔗 Связка</div>
<div id="link-status" style="color:#888; font-size:12px; margin-bottom:8px;">1⃣ Кликни конец первого трека</div>
<div id="link-cards" style="margin-top:8px;"></div>
<button onclick="clearLink()" style="width:100%; margin-top:6px; padding:4px; background:#f0f0f0; border:1px solid #ccc; border-radius:4px; cursor:pointer; font-size:12px;">
✕ Сбросить
</button>
</div>
<!-- ─── Панель Красивого маршрута (F-11) ────────────────────────────────── -->
<div id="scenic-panel" style="display:none; position:absolute; bottom:40px;
background:rgba(255,255,255,0.97); border:1px solid #ddd; border-radius:8px;
padding:12px; font-size:13px; z-index:5; width:280px;
box-shadow:0 2px 12px rgba(0,0,0,0.15); max-height:70vh; overflow-y:auto;">
<div style="font-weight:600; color:#e07b00; margin-bottom:8px;">🎨 Красивый маршрут</div>
<div id="scenic-status" style="color:#888; font-size:12px; margin-bottom:8px;">📍 Точка старта: кликни на карте</div>
<div style="margin-bottom:8px;">
<span style="font-size:12px; color:#555;">📏 Дистанция:</span>
<div style="display:flex; gap:4px; margin-top:4px; flex-wrap:wrap;">
<button onclick="setScenicKm(50)" class="scenic-km-btn" data-km="50">50</button>
<button onclick="setScenicKm(100)" class="scenic-km-btn active" data-km="100">100</button>
<button onclick="setScenicKm(150)" class="scenic-km-btn" data-km="150">150</button>
<button onclick="setScenicKm(200)" class="scenic-km-btn" data-km="200">200</button>
<input type="number" id="scenic-custom-km" placeholder=".." min="20" max="500"
style="width:44px; padding:3px 4px; font-size:12px; border:1px solid #ccc; border-radius:4px; text-align:center;"
onchange="setScenicKm(+this.value||100)" />
</div>
<div class="sheet-body">
<p class="sheet-hint" id="recon-hint">Тапни точку на карте — узнаешь сколько грунтовок рядом</p>
<div class="section-label">РАДИУС</div>
<div class="seg-control">
<button class="seg-btn active" data-km="20" onclick="setReconRadius(20)">20 км</button>
<button class="seg-btn" data-km="50" onclick="setReconRadius(50)">50 км</button>
<button class="seg-btn" data-km="100" onclick="setReconRadius(100)">100 км</button>
</div>
<div id="recon-results" style="display:none">
<div class="section-label">ГРУНТОВКИ</div>
<div class="recon-grid">
<div class="recon-stat">
<div class="rs-value" id="r-total-km"></div>
<div class="rs-label">км всего</div>
</div>
<div class="recon-stat">
<div class="rs-value gold" id="r-lev12-km"></div>
<div class="rs-label">км Lev 1-2</div>
</div>
<div class="recon-stat">
<div class="rs-value red" id="r-lev345-km"></div>
<div class="rs-label">км Lev 3-5</div>
</div>
<div class="recon-stat">
<div class="rs-value" id="r-path-km"></div>
<div class="rs-label">км тропы</div>
</div>
</div>
<div class="section-label">ИНТЕРЕСНОЕ</div>
<div id="r-poi-list"></div>
</div>
<button onclick="buildScenicRoute()" id="btn-build-scenic"
style="width:100%; padding:6px; background:#ff6600; color:#fff; border:none; border-radius:4px; cursor:pointer; font-size:13px; font-weight:600;">
🎨 Построить маршрут
</button>
<div id="scenic-cards" style="margin-top:10px;"></div>
<button onclick="clearScenic()" style="width:100%; margin-top:6px; padding:4px; background:#f0f0f0; border:1px solid #ccc; border-radius:4px; cursor:pointer; font-size:12px;">
✕ Сбросить
</button>
</div>
<div id="map-controls-br" class="custom-map-ctrl">
<button id="btn-compass" class="map-ctrl-btn" title="Свободное вращение" onclick="toggleCompass()">🧭</button>
<button id="btn-route" class="map-ctrl-btn" title="Построить маршрут" onclick="toggleRouteMode()">🗺️</button>
<button id="btn-locate" class="map-ctrl-btn" title="Моё местоположение" onclick="locateMe()">🎯</button>
<button id="btn-ruler" class="map-ctrl-btn" title="Измерить расстояние" onclick="toggleRuler()">📏</button>
<button id="btn-markers" class="map-ctrl-btn" title="Добавить метку" onclick="toggleMarkerMode()">🚩</button>
<button id="btn-scenic" class="map-ctrl-btn" title="Красивый маршрут" onclick="toggleScenicMode()">🎨</button>
<button id="btn-link" class="map-ctrl-btn" title="Связка" onclick="toggleLinkMode()">🔗</button>
<button id="btn-recon" class="map-ctrl-btn" title="Разведка" onclick="toggleReconMode()">📍</button>
</div>
</div>
<script src="app.js" defer></script>
<!-- ── Sheet: Красивый маршрут ───────────── -->
<div class="bottom-sheet" id="sheet-scenic">
<div class="sheet-handle"></div>
<div class="sheet-header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z"/><path d="M5 3v4M19 17v4M3 5h4M17 19h4"/></svg>
<h2>Красивый маршрут</h2>
<button class="sheet-close" onclick="toggleScenicMode()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
</button>
</div>
<div class="sheet-body">
<div id="scenic-status" class="text-muted">Тапни точку старта на карте</div>
<div class="section-label mt-8">ДИСТАНЦИЯ</div>
<div class="dist-row">
<div class="seg-control" style="flex:1">
<button class="seg-btn" data-km="50" onclick="setScenicKm(50)">50</button>
<button class="seg-btn active" data-km="100" onclick="setScenicKm(100)">100</button>
<button class="seg-btn" data-km="150" onclick="setScenicKm(150)">150</button>
<button class="seg-btn" data-km="200" onclick="setScenicKm(200)">200</button>
</div>
<input type="number" id="scenic-custom-km" class="dist-custom" placeholder="км" min="20" max="500" onchange="setScenicKm(+this.value||100)">
</div>
<button class="btn-primary" id="btn-build-scenic" onclick="buildScenicRoute()" style="display:none">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z"/></svg>
Построить маршрут
</button>
<div id="scenic-cards" class="mt-8"></div>
</div>
</div>
<!-- ── Sheet: Связка ─────────────────────── -->
<div class="bottom-sheet" id="sheet-link">
<div class="sheet-handle"></div>
<div class="sheet-header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></svg>
<h2>Связка</h2>
<button class="sheet-close" onclick="toggleLinkMode()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
</button>
</div>
<div class="sheet-body">
<p class="sheet-hint" style="text-align:left;padding-top:0">Соедини два трека — найду оптимальную грунтовую связку</p>
<div class="link-points">
<div class="link-pt empty" id="link-pt-1">
<div class="link-pt-num">1</div>
<div class="link-pt-label">Конец первого трека</div>
</div>
<div class="link-pt empty" id="link-pt-2">
<div class="link-pt-num">2</div>
<div class="link-pt-label">Начало второго трека</div>
</div>
</div>
<div id="link-status" class="text-muted">Тапни первую точку на карте</div>
<div id="link-cards"></div>
</div>
</div>
<!-- ── Marker type dialog ─────────────────── -->
<div id="marker-dialog">
<div class="marker-dialog-inner">
<div class="sheet-handle"></div>
<div style="padding:8px 0 4px;font-size:13px;font-weight:700;color:var(--text)">Тип метки</div>
<div class="marker-type-grid" id="marker-type-grid"></div>
<button onclick="closeMarkerDialog()" style="width:100%;height:44px;background:var(--surface2);border:1px solid var(--border);border-radius:12px;color:var(--text2);font-size:14px;font-weight:600;cursor:pointer;margin-top:4px">Отмена</button>
</div>
</div>
<!-- ════════════════════════════════════════════
BOTTOM TOOLBAR
════════════════════════════════════════════ -->
<nav id="toolbar">
<button class="tb-btn" id="tb-route" onclick="toggleRouteMode()">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3h5l2 9h10l-1.5 7H6.5"/><circle cx="10" cy="20" r="1"/><circle cx="18" cy="20" r="1"/></svg>
<span>Маршрут</span>
</button>
<button class="tb-btn" id="tb-link" onclick="toggleLinkMode()">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></svg>
<span>Связка</span>
</button>
<button class="tb-btn" id="tb-scenic" onclick="toggleScenicMode()">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z"/></svg>
<span>Красивый</span>
</button>
<button class="tb-btn" id="tb-recon" onclick="toggleReconMode()">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12C2 6.5 6.5 2 12 2a10 10 0 0 1 8 4"/><path d="M5 19.5C5.5 18 6 15 6 12c0-.7.12-1.37.34-2"/><path d="M17.29 21.02c.12-.6.43-2.3.5-3.02"/><path d="M12 10a2 2 0 0 0-2 2c0 1.02-.1 2.51-.26 4"/><path d="M8.65 22c.21-.66.45-1.32.57-2"/><path d="M14 13.12c0 2.38 0 6.38-1 8.88"/><path d="M2 16h.01"/><path d="M21.8 16c.2-2 .131-5.354 0-6"/><path d="M9 6.8a6 6 0 0 1 9 5.2c0 .47 0 1.17-.02 2"/></svg>
<span>Разведка</span>
</button>
<button class="tb-btn" id="tb-ruler" onclick="toggleRuler()">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.3 15.3a2.4 2.4 0 0 1 0 3.4l-2.6 2.6a2.4 2.4 0 0 1-3.4 0L2.7 8.7a2.41 2.41 0 0 1 0-3.4l2.6-2.6a2.41 2.41 0 0 1 3.4 0Z"/><path d="m14.5 12.5 2-2"/><path d="m11.5 9.5 2-2"/><path d="m8.5 6.5 2-2"/><path d="m17.5 15.5 2-2"/></svg>
<span>Линейка</span>
</button>
<button class="tb-btn" id="tb-marker" onclick="toggleMarkerMode()">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"/><circle cx="12" cy="10" r="3"/></svg>
<span>Метка</span>
</button>
</nav>
<!-- Scripts -->
<script src="https://unpkg.com/maplibre-gl@4.7.0/dist/maplibre-gl.js"></script>
<script src="app.js"></script>
</body>
</html>