# BRD: Enduro Trails — Поиск точек маршрута

**Версия:** 1.1  
**Дата:** 2026-05-05  
**Автор:** Стрим 🌊  
**Статус:** 🔄 В разработке

---

## 1. Контекст и проблема

Сейчас добавить точку маршрута можно только тапом на карту. Если нужно попасть в конкретное место (деревня, перекрёсток, POI) — нужно сначала найти его на карте, потом тапнуть. Это неудобно, особенно на мобиле.

Верхний search bar решал задачу навигации по карте, но не интегрирован с маршрутом — найденное место просто центрировало карту, не добавляя точку. Плюс bar занимал место и мешал карте.

---

## 2. Цель

Убрать верхний search bar. Добавить поиск прямо в список точек маршрута — inline, для каждой точки отдельно. При первом открытии режима маршрута — онбординг с полями ввода старта и финиша.

---

## 3. Что меняется

### 3.1 Убрать верхний search bar

- Удалить `#search-bar` и `#search-results` из HTML
- Удалить все CSS стили (`#search-bar`, `#search-input`, `#search-results`, `.search-result-item`, `.sri-*`)
- Удалить вызов `initSearch()` из JS
- Кнопку темы (`#btn-theme`) перенести в `#map-controls-r` (рядом с компасом и геолокацией)

### 3.2 Онбординг при активации режима маршрута

Когда пользователь тапает «Маршрут» в toolbar и точек ещё нет — показывать вместо пустого списка:

```
┌─────────────────────────────────┐
│ 🔍 Откуда?                      │  ← поле поиска старта
│    или тапни на карте           │  ← подсказка
└─────────────────────────────────┘
```

После выбора старта автоматически появляется:
```
┌─────────────────────────────────┐
│ S  Название старта              │  ← добавленная точка
├─────────────────────────────────┤
│ 🔍 Куда?                        │  ← поле поиска финиша
│    или тапни на карте           │
└─────────────────────────────────┘
```

После выбора финиша — поля исчезают, маршрут строится, показывается обычный `wl-list`.

**Детали:**
- Поля поиска используют тот же Nominatim что и inline поиск в wl-item
- Подсказка «или тапни на карте» — мелкий текст `var(--text3)` под полем
- Тап на карте тоже работает — добавляет точку и переходит к следующему полю
- Если точки уже есть — онбординг не показывается, сразу wl-list

### 3.3 Inline поиск в каждой точке маршрута

В каждом `wl-item` добавить кнопку-лупу. Тап → разворачивается inline панель поиска прямо под точкой.

**UX flow:**
1. Пользователь видит список точек маршрута (S → 1 → F)
2. Тапает иконку 🔍 рядом с нужной точкой
3. Под точкой появляется поле ввода с автофокусом
4. Вводит название места → через 400ms debounce → Nominatim поиск
5. Выбирает результат → точка обновляется, карта летит к ней, маршрут перестраивается
6. Панель закрывается

**Детали:**
- Одновременно открыта только одна панель поиска (остальные закрываются)
- Поиск через Nominatim (`countrycodes=ru`, `accept-language=ru`, limit=5)
- Результат: название + подзаголовок (область/район)
- После выбора: `routeWaypoints[idx]` обновляется, `rebuildWaypointMarkers()` + `renderWaypointsList()` + `debounceBuildRoute()`
- `map.flyTo()` к выбранному месту (zoom 13, duration 600ms)

---

## 4. UI компоненты

### Кнопка лупы в wl-item
- Иконка: Lucide `search` SVG (15×15)
- Цвет: `var(--text3)` → hover `var(--accent)`
- Позиция: между `.wl-info` и `.wl-drag-handle`

### Inline панель поиска
- Фон: `var(--surface)`, бордер сверху `var(--border)`
- Input: `var(--surface2)` фон, `var(--border)` бордер, focus → `var(--accent)` бордер
- Результаты: max-height 180px, scroll, hover `var(--surface2)`
- Каждый результат: название (bold) + подзаголовок (мелкий, `var(--text2)`)

### Кнопка темы (перенос)
- Переносится в `#map-controls-r` как `.map-btn`
- `updateThemeButtonIcon()` продолжает работать — находит `#btn-theme` по id

---

## 5. Технические детали

| Аспект | Решение |
|--------|---------|
| Поиск API | Nominatim `/search` (уже используется в проекте) |
| Debounce | 400ms |
| Состояние | `wpSearchTimeout` — глобальная переменная для debounce |
| Закрытие панели | Тап на лупу повторно — закрывает; открытие другой — закрывает текущую |
| После выбора | `routeWaypoints[idx]` = `{lat, lon}`, rebuild + route |
| Кнопка «Добавить точку» | `wl-add` остаётся без изменений |

---

## 6. Баги после редизайна (требуют исправления)

| Баг | Описание | Вероятная причина |
|-----|---------|------------------|
| Метки в верхнем левом углу | Попап метки отображается не на позиции маркера, а в углу экрана | CSS `position` маркера сбивает позиционирование MapLibre |
| Метки линейки в верхнем углу | Попап точки линейки отображается не на треке, а в углу | Та же причина — CSS позиционирование маркера |
| Подсказка «Тапни на карте» пропала | `#route-status` не виден когда точек нет | Скрыт или перекрыт после редизайна |

**Вероятная причина багов с маркерами:** в редизайне изменились глобальные CSS стили для `position`, `transform` или `z-index` — это сбивает позиционирование MapLibre маркеров. Маркер MapLibre должен иметь `position: absolute` без переопределения в глобальных стилях.

**Фикс:** проверить CSS для `.maplibregl-marker`, `.marker-flag`, `.ruler-marker` — убрать любой `position: fixed/relative/static` который может переопределять поведение маркера.

---

## 7. Definition of Done

| Критерий | Статус |
|----------|--------|
| Верхний search bar отсутствует | ⬜ |
| Кнопка темы работает в map-controls-r | ⬜ |
| При активации маршрута без точек — поле поиска старта | ⬜ |
| После старта — поле поиска финиша | ⬜ |
| Подсказка «или тапни на карте» видна | ⬜ |
| Иконка лупы в каждом wl-item | ⬜ |
| Тап на лупу → inline поиск открывается | ⬜ |
| Поиск находит места через Nominatim | ⬜ |
| Выбор результата → waypoint обновляется | ⬜ |
| Маршрут перестраивается после выбора | ⬜ |
| Карта летит к выбранному месту | ⬜ |
| Одновременно открыта только одна панель | ⬜ |
| Метки отображаются на позиции маркера (не в углу) | ⬜ |
| Метки линейки отображаются на треке | ⬜ |
| Деплой + health check OK | ⬜ |

---

## 8. Файлы

| Файл | Изменения |
|------|-----------|
| `prototype/static/index.html` | Удалить `#search-bar`, `#search-results`; перенести `#btn-theme` в `#map-controls-r` |
| `prototype/static/app.css` | Удалить стили search bar; добавить `.wl-search-*`; исправить позиционирование маркеров |
| `prototype/static/app.js` | Удалить `initSearch()`; добавить онбординг, `openWaypointSearch()`, `doWaypointSearch()`, `selectWaypointSearchResult()` |

---

---

## 9. Дополнительные замечания (05.05.2026 — пакет 2)

| # | Замечание | Детали |
|---|-----------|--------|
| 1 | Иконка колеса | Заменить на нормальное мотокросс колесо |
| 2 | Флажок финиша | Чёрно-белая шахматная раскраска как финишный флаг |
| 3 | Спиннер в основном листе | Вращающееся колесо пока строится маршрут (как в мини-баре) |
| 4 | Тёмная карта | При тёмной теме — тёмный стиль карты, при светлой — светлый |
| 5 | Автозум на маршрут | Плавный `fitBounds` после построения маршрута, маршрут по центру без выхода за границы |
| 6 | Мини-бар перекрывает кнопки справа | Исправить z-index/позиционирование `#sheet-route-mini` |

### Детали по пунктам

**П.1 Иконка колеса:**  
Заменить текущую SVG на нормальное мотокросс колесо — спицы, кноблинг, центральная втулка. Использовать Lucide `bike` или нарисовать SVG колеса с спицами.

**П.2 Флажок финиша:**  
В `waypointPinSvg('F', ...)` для финишной точки использовать шахматный узор (SVG `pattern` чёрные/белые клетки) вместо красного цвета.

**П.3 Спиннер в основном листе:**  
В `#route-cards` показывать skeleton/спиннер пока идёт запрос к OSRM. Тот же спиннер что в мини-баре.

**П.4 Тёмная карта:**  
`map.setStyle()` при смене темы. Тёмный стиль: `/style-dark.json` (или текущий тёмный), светлый: `/style-light.json` (или текущий светлый). После `setStyle` пересоздать слои маршрутов через `map.on('style.load', onMapStyleLoad)`.

**П.5 Автозум:**  
После `drawRouteResults()` вызывать:
```js
const coords = routeResults[0].geometry.coordinates;
const bounds = coords.reduce((b, c) => b.extend(c), new maplibregl.LngLatBounds(coords[0], coords[0]));
map.fitBounds(bounds, { padding: 60, duration: 1000, maxZoom: 14 });
```

**П.6 Мини-бар:**  
`#sheet-route-mini` перекрывает `#map-controls-r`. Исправить `right` или `width` мини-бара чтобы не залезал на кнопки справа.
