diff --git a/memory/.dreams/events.jsonl b/memory/.dreams/events.jsonl index 573a41a..8547efc 100644 --- a/memory/.dreams/events.jsonl +++ b/memory/.dreams/events.jsonl @@ -12,3 +12,4 @@ {"type":"memory.recall.recorded","timestamp":"2026-04-26T07:04:02.188Z","query":"FR24 ADS-B tracks raw packets track glitches distortions real data analysis","resultCount":5,"results":[{"path":"memory/2026-03-30.md","startLine":26,"endLine":61,"score":0.33961203694343567},{"path":"memory/2026-04-21.md","startLine":32,"endLine":66,"score":0.335136467218399},{"path":"memory/2026-04-19.md","startLine":135,"endLine":145,"score":0.32212974429130553},{"path":"memory/2026-03-30.md","startLine":53,"endLine":83,"score":0.3143289566040039},{"path":"memory/2026-04-21.md","startLine":63,"endLine":93,"score":0.3126189291477203}]} {"type":"memory.recall.recorded","timestamp":"2026-04-26T10:54:35.677Z","query":"mva154 хост сервер характеристики доступ SSH","resultCount":1,"results":[{"path":"memory/2026-04-11.md","startLine":62,"endLine":71,"score":0.3638544976711273}]} {"type":"memory.recall.recorded","timestamp":"2026-05-04T06:57:05.721Z","query":"enduro trails backlog задачи","resultCount":3,"results":[{"path":"memory/2026-05-02.md","startLine":107,"endLine":142,"score":0.375397714972496},{"path":"memory/2026-05-02.md","startLine":86,"endLine":117,"score":0.3636188447475433},{"path":"memory/2026-05-03.md","startLine":1,"endLine":36,"score":0.3552299261093139}]} +{"type":"memory.recall.recorded","timestamp":"2026-05-05T05:03:32.115Z","query":"DEV_TASK_PHASE5 dev agent enduro trails фаза 5","resultCount":1,"results":[{"path":"memory/2026-05-04.md","startLine":1,"endLine":30,"score":0.40014922022819516}]} diff --git a/memory/.dreams/short-term-recall.json b/memory/.dreams/short-term-recall.json index 7a5d245..7509054 100644 --- a/memory/.dreams/short-term-recall.json +++ b/memory/.dreams/short-term-recall.json @@ -1,6 +1,6 @@ { "version": 1, - "updatedAt": "2026-05-04T06:57:05.721Z", + "updatedAt": "2026-05-05T05:03:32.115Z", "entries": { "memory:memory/2026-04-05.md:29:55": { "key": "memory:memory/2026-04-05.md:29:55", @@ -717,6 +717,37 @@ "lev3-5", "raster-saturation" ] + }, + "memory:memory/2026-05-04.md:1:30": { + "key": "memory:memory/2026-05-04.md:1:30", + "path": "memory/2026-05-04.md", + "startLine": 1, + "endLine": 30, + "source": "memory", + "snippet": "# 2026-05-04 — Дневник Стрим ## Enduro Trails — Фаза 3 «Умный маршрут» ### Что делали Полная реализация Фазы 3: BRD, тест-кейсы, реализация, тестирование, баг-фиксы. ### Документы созданы - `tasks/enduro-trails/BRD_PHASE3.md` — бизнес-требования (6 фич, согласовано со Славой) - `tasks/enduro-trails/TEST_CASES_PHASE3.md` — 56 тест-кейсов - `tasks/enduro-trails/DEV_TASK_PHASE3.md` — ТЗ для Dev-агента - `tasks/ui-testing/PROJECT.md` — проект UI-тестирования (Playwright/Puppeteer на mva154) - `tasks/ui-testing/README.md` — как запустить - `tasks/ui-testing/tests/enduro-phase3.js` — 56 TC в Playwright-скрипте - `tasks/ui-testing/tests/template.js` — шаблон для новых проектов - `tasks/ui-testin", + "recallCount": 1, + "dailyCount": 0, + "groundedCount": 0, + "totalScore": 0.40014922022819516, + "maxScore": 0.40014922022819516, + "firstRecalledAt": "2026-05-05T05:03:32.115Z", + "lastRecalledAt": "2026-05-05T05:03:32.115Z", + "queryHashes": [ + "14db74d1ec5d" + ], + "recallDays": [ + "2026-05-05" + ], + "conceptTags": [ + "тест-кейсы", + "баг-фиксы", + "бизнес-требования", + "тест-кейсов", + "dev-агента", + "tasks/ui-testing/project.md", + "ui-тестирования", + "playwright/puppeteer" + ] } } } diff --git a/tasks/enduro-trails/BRD_PHASE5.md b/tasks/enduro-trails/BRD_PHASE5.md index 2fef575..ce3d520 100644 --- a/tasks/enduro-trails/BRD_PHASE5.md +++ b/tasks/enduro-trails/BRD_PHASE5.md @@ -24,7 +24,22 @@ ### 3.1 Цветовая палитра — две темы -Тема переключается кнопкой ☀️/🌙 в search bar. Автодетект: если время 07:00–20:00 → светлая, иначе → тёмная. +Тема переключается кнопкой ☀️/🌙 в search bar. **Три режима:** + +| Режим | Описание | +|-------|----------| +| **Авто** (по умолчанию) | Тема определяется по реальному восходу/закату солнца для текущей геолокации. Если браузер отдал геопозицию — используем её; иначе — запасной город (Москва, 55.75°N). День = светлая, ночь = тёмная. Переключение происходит без перезагрузки. | +| **Светлая** | Принудительно светлая тема, независимо от времени суток. | +| **Тёмная** | Принудительно тёмная тема. | + +**Реализация авто-режима:** +- Используется SunCalc (MIT, ~3KB) через CDN: `https://unpkg.com/suncalc@latest` +- `SunCalc.getTimes(date, lat, lng)` → `sunrise`, `sunset` +- Текущее время между `sunrise` и `sunset` → светлая тема; иначе — тёмная +- При изменении геолокации — пересчёт восхода/заката +- Проверка раз в минуту (на случай, если сессия длительная) + +**UI переключателя:** тап на ☀️/🌙 циклически переключает: Авто → Светлая → Тёмная → Авто. Текущий режим показан маленькой подписью под иконкой (или tooltip на десктопе): «Авто», «День», «Ночь». В авто-режиме иконка динамическая: ☀️ если сейчас день, 🌙 если ночь. **Тёмная (ночная езда):** ``` @@ -489,7 +504,11 @@ Font: system-ui, -apple-system, 'SF Pro Display', 'Segoe UI', sans-serif | ID | Сценарий | Устройство | Шаги | Ожидаемый результат | |----|----------|-----------|------|---------------------| -| T13 | Тёмная тема | Любое | Открыть | Фон #0D1117, текст светлый, нет белых вспышек | +| T13 | Тема — авто по умолчанию | Любое | Открыть (днём) | Фон светлый (бежевый #F5F5F0), иконка ☀️, подпись «Авто» | +| T13a | Тема — авто ночью | Любое | Открыть (после заката) | Фон тёмный #0D1117, иконка 🌙, подпись «Авто» | +| T13b | Тема — ручной цикл | Любое | Тап ☀️/🌙 3 раза | Авто → Светлая → Тёмная → Авто, при каждом тапе тема и подпись меняются | +| T13c | Тема — ручная не зависит от времени | Любое | Переключить на «Светлая» ночью | Фон бежевый, подпись «День», иконка ☀️ | +| T13d | Тема — авто пересчитывает при геолокации | Любое | В авто-режиме, разрешить геолокацию | Восход/закат пересчитаны по реальным координатам, тема соответствует | | T14 | Толстые пальцы в перчатках | iPhone | Нажимать кнопки | Все кнопки min 48×48px, не промахиваешься | | T15 | Landscape поворот | iPhone | Повернуть телефон | UI перестроился, карта видна | | T16 | Десктоп Chrome | MacBook | Открыть | Боковая панель 320px, кнопки слева | @@ -523,7 +542,8 @@ Font: system-ui, -apple-system, 'SF Pro Display', 'Segoe UI', sans-serif - [ ] Все P0 + P1 тест-кейсы прошли на iPhone 14 Pro (393px) - [ ] Bottom sheet плавно открывается/закрывается - [ ] Toolbar: все 6 режимов, min 48px tap target -- [ ] Тёмная тема везде, нет белых фонов +- [ ] Тема: авто (по восходу/закату SunCalc) + ручной переключатель (3 режима: Авто/Светлая/Тёмная) +- [ ] Тёмная тема везде, нет белых вспышек; светлая тема без ослепляющего белого - [ ] SVG иконки (Lucide), никаких emoji в UI-элементах - [ ] Карта видна при активных панелях - [ ] Safe area корректная (notch, home indicator) @@ -537,6 +557,7 @@ Font: system-ui, -apple-system, 'SF Pro Display', 'Segoe UI', sans-serif - MapLibre GL остаётся - Без новых npm-зависимостей (только inline CSS/JS) - Lucide иконки — подключить через CDN: `https://unpkg.com/lucide@latest` +- SunCalc — подключить через CDN: `https://unpkg.com/suncalc@latest` (MIT, ~3KB, для расчёта восхода/заката) - Деплой через ssh2 (стандартная схема) --- diff --git a/tasks/enduro-trails/DEV_TASK_PHASE5.md b/tasks/enduro-trails/DEV_TASK_PHASE5.md new file mode 100644 index 0000000..e21c20d --- /dev/null +++ b/tasks/enduro-trails/DEV_TASK_PHASE5.md @@ -0,0 +1,310 @@ +# Dev Task: Enduro Trails — Фаза 5 «Редизайн» + +**Приоритет:** HIGH +**Проект:** enduro-trails +**BRD:** `BRD_PHASE5.md` +**Дата:** 2026-05-05 + +--- + +## Контекст + +Фазы 3–4 реализованы: роутинг A→B, альтернативы, Разведка, Связка, Красивый маршрут, Линейка, Метки. +UI уже частично обновлён (bottom sheets, toolbar, SVG, тёмная тема), но BRD Phase 5 требует **довести до продакшн-качества** — мобильный first, две темы с авто-режимом по восходу/закату, адаптив, анимации. + +**⚠️ Бэкенд (app.py) НЕ ТРОГАТЬ.** Только фронтенд: `index.html`, `app.css`, `app.js`. + +--- + +## Деплой + +SSH бинарник не работает в контейнере (glibc 2.36 < 2.38). Используй Node.js ssh2: +```js +const { Client } = require('/tmp/node_modules/ssh2'); +``` +Шаблон: `/tmp/deploy_app2.js`. +Статику заливаем через SFTP в `/home/slin/enduro-trails/prototype/static/`, потом `docker cp` в контейнер + `docker restart`. + +--- + +## Что уже есть vs что нужно + +| Компонент | Сейчас (Phase 4) | Надо (Phase 5) | +|-----------|-----------------|----------------| +| Тема | Только тёмная, кнопка ☀️/🌙 но не работает | Три режима: Авто (SunCalc восход/закат), Светлая, Тёмная | +| Цвета | CSS vars есть, но только тёмные | Два набора CSS vars, переключение через `body.theme-dark`/`body.theme-light` | +| Кнопка темы | Есть `#btn-theme` в search-bar | Циклическое переключение Авто→Светлая→Тёмная→Авто, подпись режима | +| Search bar | Есть, `position:fixed` | Остается, но цвета под тему | +| Bottom toolbar | 6 кнопок, SVG, `#toolbar` | Остается, но: активная кнопка = оранжевый фон (не только цвет текста), label появляется под иконкой | +| Bottom sheets | 4 sheets (route/recon/scenic/link) | Остаются, но: цвета под тему, skeleton loading, свайп вниз для закрытия | +| Map buttons | `#map-controls-r` (2 кнопки) | Остаются, цвета под тему | +| Адаптив | Mobile only, без десктопа | Десктоп (≥768px): боковая панель 320px, кнопки слева | +| Анимации | Есть sheet slide | + skeleton loading, + кнопка scale(0.94) при tap, + маркер scale появление | +| Иконки | SVG inline (lucide-style) | ОК, остаются. **НЕ emoji** | +| Карта style | Один style.json | Переключение dark/light style при смене темы | + +--- + +## Задача 1: Дизайн-система — Цвета и Тема + +### 1.1 CSS Custom Properties — две темы + +На `:root` определить ВСЕ переменные для тёмной (по умолчанию). На `body.theme-light` — переопределить. + +**Тёмная (default):** +```css +:root { + --bg: #0D1117; + --surface: #161B22; + --surface2: #21262D; + --border: #30363D; + --text: #E6EDF3; + --text2: #8B949E; + --text3: #484F58; + --accent: #FF6B00; + --accent-hover:#FF8A33; + --gold: #FFD700; + --red: #FF3B1F; + --success: #2EA043; + --shadow: rgba(0,0,0,0.5); + --overlay: rgba(0,0,0,0.6); +} +``` + +**Светлая:** +```css +body.theme-light { + --bg: #F5F5F0; + --surface: #FFFFFF; + --surface2: #F0F0EA; + --border: #D0CFC8; + --text: #1A1A1A; + --text2: #6B6B6B; + --text3: #9A9A9A; + --accent: #E55A00; + --accent-hover:#CC4F00; + --gold: #C89B00; + --red: #CC2200; + --success: #1A7A30; + --shadow: rgba(0,0,0,0.15); + --overlay: rgba(0,0,0,0.3); +} +``` + +**Все компоненты** (search-bar, toolbar, bottom-sheet, map-btn, cards, pills, ruler-info, marker-dialog, search-results) должны использовать ТОЛЬКО CSS vars — никаких хардкоженных цветов. + +### 1.2 Переключатель темы — три режима + +**Состояния:** +- `themeMode = 'auto'` — по умолчанию, тема определяется по восходу/закату +- `themeMode = 'light'` — принудительно светлая +- `themeMode = 'dark'` — принудительно тёмная + +**Циклическое переключение:** тап на `#btn-theme` → `auto → light → dark → auto` + +**UI кнопки:** +- В авто-режиме: иконка динамическая (☀️ если день, 🌙 если ночь) +- В ручном светлом: всегда ☀️ +- В ручном тёмном: всегда 🌙 +- Подпись под иконкой (или рядом, маленький текст): «Авто» / «День» / «Ночь» + +**Сохранение:** `localStorage.setItem('enduro-theme-mode', themeMode)` — при загрузке читать оттуда. + +### 1.3 SunCalc — авто-режим по восходу/закату + +Подключить в `index.html`: +```html + +``` +(до `app.js`) + +**Логика:** +```js +function applyAutoTheme() { + if (themeMode !== 'auto') return; + const now = new Date(); + // Геолокация: если есть — используем, иначе Москва + const lat = userLat || 55.75; + const lon = userLon || 37.62; + const times = SunCalc.getTimes(now, lat, lon); + const isDay = now >= times.sunrise && now < times.sunset; + document.body.className = isDay ? 'theme-light' : 'theme-dark'; + updateThemeButtonIcon(); +} + +// Проверять раз в минуту +setInterval(applyAutoTheme, 60000); +``` + +**При получении геолокации** (кнопка 🎯): обновить `userLat`/`userLon` и пересчитать тему. + +**При переключении карты:** MapLibre.setStyle() на соответствующий style.json (тёмный/светлый). + +### 1.4 Карта — стиль под тему + +Уже есть два style.json: тёмный и светлый. При смене темы: +```js +map.setStyle(isDark ? 'style-dark.json' : 'style-light.json'); +``` +Или если стили инлайн — переключить `map.setStyle()` с нужным объектом. + +**⚠️ Важно:** после `setStyle` нужно пересоздать все слои (routes, markers, circles). Использовать событие `map.on('style.load', ...)`. + +--- + +## Задача 2: Компоненты — все на CSS vars + +### 2.1 Search bar +- Фон: `var(--surface)`, бордер: `var(--border)`, тень с `var(--shadow)` +- Placeholder: `var(--text3)` +- Кнопка темы: иконка + подпись + +### 2.2 Toolbar +- Фон: `var(--surface)`, бордер: `var(--border)` +- Неактивная кнопка: `var(--text2)` цвет +- **Активная кнопка:** `var(--accent)` фон, белый цвет текста/иконки, `border-radius: 10px` +- Активная кнопка: подпись под иконкой видна + +### 2.3 Bottom sheets +- Фон: `var(--surface)`, бордер: `var(--border)` +- Handle: `var(--border)` цвет +- Заголовок: `var(--text)`, подзаголовок/label: `var(--text2)` +- Close button: `var(--text2)` → hover `var(--text)` +- Waypoint chips: `var(--surface2)` фон, `var(--border)` бордер +- Route cards: `var(--surface2)` фон, `var(--border)` бордер +- Stat pills: `var(--bg)` фон, `var(--border)` бордер +- Hint text: `var(--text3)` + +### 2.4 Map buttons +- Фон: `var(--surface)`, бордер: `var(--border)`, тень с `var(--shadow)` +- Active: `var(--accent)` фон + +### 2.5 Search results +- Фон: `var(--surface)`, бордер: `var(--border)` +- Hover: `var(--surface2)` + +### 2.6 Marker dialog +- Фон: `var(--surface)`, внутренности `var(--surface2)`, бордер `var(--border)` + +--- + +## Задача 3: Skeleton Loading + +При загрузке маршрутов/разведки показывать skeleton вместо «Строю маршрут...»: + +```css +.skeleton { + background: linear-gradient(90deg, var(--surface2) 25%, var(--border) 50%, var(--surface2) 75%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; + border-radius: 8px; + height: 64px; + margin-bottom: 8px; +} +@keyframes shimmer { + 0% { background-position: 200% 0; } + 100% { background-position: -200% 0; } +} +``` + +Использовать в `#route-cards`, `#scenic-cards`, `#link-cards` пока идёт запрос к API. + +--- + +## Задача 4: Свайп вниз для закрытия sheet + +**Touch-обработка:** +```js +// На каждом .bottom-sheet +let startY = 0; +sheet.addEventListener('touchstart', e => { + if (e.target.closest('.sheet-handle') || e.touches[0].clientY < sheet.getBoundingClientRect().top + 40) { + startY = e.touches[0].clientY; + } +}); +sheet.addEventListener('touchmove', e => { + const dy = e.touches[0].clientY - startY; + if (dy > 0) sheet.style.transform = `translateY(${dy}px)`; +}); +sheet.addEventListener('touchend', e => { + const dy = e.changedTouches[0].clientY - startY; + if (dy > 80) closeSheet(sheet.id); + else sheet.style.transform = ''; +}); +``` + +--- + +## Задача 5: Адаптив — Десктоп + +При `width >= 768px`: + +```css +@media (min-width: 768px) { + #toolbar { + position: fixed; + bottom: auto; left: 0; top: 60px; + width: 72px; height: auto; + flex-direction: column; + border-top: none; + border-right: 1px solid var(--border); + } + .bottom-sheet { + left: 72px; + max-width: 400px; + border-radius: 20px 20px 0 0; + } + #map-controls-r { + left: 72px; + } + #search-bar { + left: 84px; + } +} +``` + +--- + +## Задача 6: Микро-анимации + +1. **Кнопки toolbar** при tap: `transform: scale(0.94)`, 100ms +2. **Маркеры на карте** при появлении: `transform: scale(0) → scale(1)`, 200ms (MapLibre marker animation) +3. **Карточки** при появлении: `opacity: 0 → 1`, 200ms stagger + +--- + +## Порядок реализации + +1. **Задача 1** — Дизайн-система: CSS vars для двух тем + SunCalc + переключатель +2. **Задача 2** — Все компоненты на CSS vars (замена хардкода) +3. **Задача 3** — Skeleton loading +4. **Задача 4** — Свайп для закрытия sheets +5. **Задача 5** — Десктоп-адаптив +6. **Задача 6** — Микро-анимации +7. Деплой + проверка + +--- + +## Проверка после деплоя + +1. Открыть приложение → проверить тему (если день — светлая, если ночь — тёмная, подпись «Авто») +2. Тапнуть кнопку темы 3 раза → Авто→Светлая→Тёмная→Авто +3. В светлом режиме — бежевый фон, тёмный текст, нет белых вспышек +4. В тёмном режиме — тёмный фон, светлый текст +5. Открыть Маршрут → sheet, карточки, всё в цветах текущей темы +6. Переключить тему с открытым sheet → цвета обновились +7. Десктоп (широкий экран) → toolbar слева вертикально, sheet с max-width +8. Skeleton при построении маршрута +9. Свайп вниз по sheet → закрывается +10. Геолокация → тема пересчитывается по реальным координатам + +--- + +## Технические ограничения + +- **Бэкенд (app.py) НЕ ТРОГАТЬ** — только `index.html`, `app.css`, `app.js` +- MapLibre GL остаётся +- Без npm-зависимостей (inline CSS/JS) +- CDN: `https://unpkg.com/suncalc@1.9.0/suncalc.min.js` (добавить в index.html) +- CDN: `https://unpkg.com/maplibre-gl@4.7.0/...` (уже есть) +- Деплой через ssh2 (шаблон `/tmp/deploy_app2.js`) diff --git a/tasks/enduro-trails/prototype/static/app.css b/tasks/enduro-trails/prototype/static/app.css index df290c9..a69f60c 100644 --- a/tasks/enduro-trails/prototype/static/app.css +++ b/tasks/enduro-trails/prototype/static/app.css @@ -1,13 +1,11 @@ /* ═══════════════════════════════════════════════════════════════════ Enduro Trails — Design System v5.0 - Dark (night ride) + Light (day ride) themes - Mobile-first, thumb-friendly, adventure style + Phase 5: Dual themes, skeleton, swipe, desktop, animations ═══════════════════════════════════════════════════════════════════ */ -/* ── Reset ────────────────────────────────────── */ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0} -/* ── CSS Variables — Dark Theme (default) ───── */ +/* ── Dark Theme (default) ───── */ body.theme-dark { --bg: #0D1117; --surface: #161B22; @@ -28,9 +26,10 @@ body.theme-dark { --success: #2EA043; --shadow: 0 4px 24px rgba(0,0,0,0.6); --shadow-sm: 0 2px 8px rgba(0,0,0,0.4); + --overlay: rgba(0,0,0,0.6); } -/* ── CSS Variables — Light Theme ────────────── */ +/* ── Light Theme ────────────── */ body.theme-light { --bg: #F0EFE8; --surface: #FFFFFF; @@ -51,6 +50,7 @@ body.theme-light { --success: #1A6B2A; --shadow: 0 4px 24px rgba(0,0,0,0.15); --shadow-sm: 0 2px 8px rgba(0,0,0,0.1); + --overlay: rgba(0,0,0,0.3); } /* ── Base ─────────────────────────────────────── */ @@ -62,64 +62,51 @@ html, body { color: var(--text); overflow: hidden; -webkit-font-smoothing: antialiased; + transition: background 0.3s, color 0.3s; } -/* ── Map Container ────────────────────────────── */ -#map { - position: fixed; - inset: 0; - z-index: 0; -} +#map { position: fixed; inset: 0; z-index: 0; } /* ── Search Bar ──────────────────────────────── */ #search-bar { position: fixed; top: max(env(safe-area-inset-top, 0px), 12px); - left: 12px; - right: 12px; + left: 12px; right: 12px; height: 50px; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; - display: flex; - align-items: center; + display: flex; align-items: center; padding: 0 6px 0 14px; - gap: 8px; - z-index: 300; + gap: 8px; z-index: 300; box-shadow: var(--shadow); - transition: border-color 0.15s; -} -#search-bar:focus-within { - border-color: var(--accent); -} -#search-bar .sb-icon { - color: var(--text3); - flex-shrink: 0; - width: 18px; height: 18px; + transition: background 0.3s, border-color 0.3s; } +#search-bar:focus-within { border-color: var(--accent); } +#search-bar .sb-icon { color: var(--text3); flex-shrink: 0; width: 18px; height: 18px; } #search-input { - flex: 1; - background: none; - border: none; - color: var(--text); - font-size: 15px; - outline: none; - min-width: 0; + flex: 1; background: none; border: none; + color: var(--text); font-size: 15px; outline: none; min-width: 0; + transition: color 0.3s; } #search-input::placeholder { color: var(--text3); } #btn-theme { - width: 38px; height: 38px; + width: auto; min-width: 38px; height: 38px; border-radius: 10px; background: var(--surface2); border: 1px solid var(--border); color: var(--text2); display: flex; align-items: center; justify-content: center; - cursor: pointer; - flex-shrink: 0; - transition: all 0.15s; + gap: 3px; cursor: pointer; flex-shrink: 0; + transition: all 0.15s; padding: 0 8px; } -#btn-theme:active { transform: scale(0.9); background: var(--surface3); } +#btn-theme:active { transform: scale(0.94); background: var(--surface3); } #btn-theme svg { width: 16px; height: 16px; } +#theme-label { + font-size: 9px; font-weight: 700; + text-transform: uppercase; letter-spacing: 0.04em; + color: var(--text3); line-height: 1; +} /* Search results */ #search-results { @@ -128,11 +115,10 @@ html, body { left: 12px; right: 12px; background: var(--surface); border: 1px solid var(--border); - border-radius: 14px; - z-index: 300; + border-radius: 14px; z-index: 300; box-shadow: var(--shadow); - overflow: hidden; - display: none; + overflow: hidden; display: none; + transition: background 0.3s, border-color 0.3s; } .search-result-item { padding: 12px 16px; @@ -143,19 +129,16 @@ html, body { } .search-result-item:last-child { border-bottom: none; } .search-result-item:active { background: var(--surface2); } +.search-result-item:hover { background: var(--surface2); } .sri-icon { color: var(--text3); flex-shrink: 0; } .sri-name { font-size: 14px; font-weight: 500; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .sri-sub { font-size: 12px; color: var(--text2); margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -/* ── Map Control Buttons (right side) ─────────── */ +/* ── Map Control Buttons ──────────────────────── */ #map-controls-r { - position: fixed; - right: 12px; + position: fixed; right: 12px; bottom: calc(80px + env(safe-area-inset-bottom, 0px) + 12px); - display: flex; - flex-direction: column; - gap: 8px; - z-index: 200; + display: flex; flex-direction: column; gap: 8px; z-index: 200; } .map-btn { width: 48px; height: 48px; @@ -164,565 +147,139 @@ html, body { border-radius: 12px; color: var(--text2); display: flex; align-items: center; justify-content: center; - cursor: pointer; - box-shadow: var(--shadow-sm); + cursor: pointer; box-shadow: var(--shadow-sm); transition: all 0.15s; -webkit-tap-highlight-color: transparent; position: relative; } .map-btn svg { width: 20px; height: 20px; } -.map-btn:active { transform: scale(0.9); background: var(--surface2); } +.map-btn:active { transform: scale(0.94); background: var(--surface2); } .map-btn.active { background: var(--accent); color: #fff; border-color: var(--accent); } /* ── Bottom Toolbar ───────────────────────────── */ #toolbar { - position: fixed; - bottom: 0; left: 0; right: 0; + position: fixed; bottom: 0; left: 0; right: 0; height: calc(68px + env(safe-area-inset-bottom, 0px)); padding-bottom: env(safe-area-inset-bottom, 0px); background: var(--surface); border-top: 1px solid var(--border); - display: flex; - align-items: center; - justify-content: space-around; + display: flex; align-items: center; justify-content: space-around; z-index: 300; box-shadow: 0 -4px 20px rgba(0,0,0,0.2); + transition: background 0.3s, border-color 0.3s; } .tb-btn { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; - gap: 3px; - height: 56px; + gap: 3px; height: 56px; border: none; background: none; color: var(--text3); font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; - border-radius: 10px; - cursor: pointer; - transition: color 0.15s; + border-radius: 10px; cursor: pointer; + transition: color 0.15s, background 0.15s, transform 0.1s; -webkit-tap-highlight-color: transparent; padding: 0 4px; } -.tb-btn svg { width: 22px; height: 22px; margin-bottom: 1px; } -.tb-btn:active { background: var(--surface2); } -.tb-btn.active { color: var(--accent); } -.tb-btn.active svg { stroke: var(--accent); } +.tb-btn svg { width: 22px; height: 22px; margin-bottom: 1px; transition: transform 0.1s; } +.tb-btn:active { background: var(--surface2); transform: scale(0.94); } +.tb-btn.active { + color: #fff; background: var(--accent); border-radius: 10px; +} +.tb-btn.active svg { stroke: #fff; } .tb-btn span { line-height: 1; } /* ── Bottom Sheet ─────────────────────────────── */ .bottom-sheet { - position: fixed; - bottom: 0; left: 0; right: 0; + position: fixed; bottom: 0; left: 0; right: 0; background: var(--surface); border-radius: 20px 20px 0 0; border-top: 1px solid var(--border); - z-index: 400; - max-height: 78vh; - overflow-y: auto; - overscroll-behavior: contain; + z-index: 400; max-height: 78vh; + overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; transform: translateY(100%); transition: transform 0.3s cubic-bezier(0.32, 0, 0.15, 1); padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px)); + touch-action: pan-y; } -.bottom-sheet.open { - transform: translateY(0); -} +.bottom-sheet.open { transform: translateY(0); } +.bottom-sheet.swiping { transition: none; } .sheet-handle { width: 36px; height: 4px; background: var(--border2); - border-radius: 2px; - margin: 12px auto 0; - cursor: grab; + border-radius: 2px; margin: 12px auto 0; cursor: grab; } .sheet-header { display: flex; align-items: center; - padding: 14px 16px 12px; - gap: 10px; + padding: 14px 16px 12px; gap: 10px; border-bottom: 1px solid var(--border); } .sheet-header svg { width: 20px; height: 20px; stroke: var(--accent); flex-shrink: 0; } -.sheet-header h2 { - flex: 1; - font-size: 15px; font-weight: 700; - color: var(--text); - letter-spacing: 0.02em; -} +.sheet-header h2 { flex: 1; font-size: 15px; font-weight: 700; color: var(--text); letter-spacing: 0.02em; } .sheet-close { - width: 32px; height: 32px; - border-radius: 8px; - background: var(--surface2); - border: 1px solid var(--border); + width: 32px; height: 32px; border-radius: 8px; + background: var(--surface2); border: 1px solid var(--border); color: var(--text2); display: flex; align-items: center; justify-content: center; - cursor: pointer; - flex-shrink: 0; + cursor: pointer; flex-shrink: 0; transition: all 0.15s; } .sheet-close svg { width: 16px; height: 16px; } -.sheet-close:active { background: var(--surface3); } +.sheet-close:active { background: var(--surface3); color: var(--text); } .sheet-body { padding: 14px 16px; } -.sheet-hint { - font-size: 13px; color: var(--text2); - text-align: center; padding: 16px 0 8px; - line-height: 1.5; -} +.sheet-hint { font-size: 13px; color: var(--text2); text-align: center; padding: 16px 0 8px; line-height: 1.5; } -/* Sheet backdrop */ #sheet-backdrop { position: fixed; inset: 0; - background: rgba(0,0,0,0.45); - z-index: 390; - opacity: 0; pointer-events: none; + background: var(--overlay); + z-index: 390; opacity: 0; pointer-events: none; transition: opacity 0.3s; } -#sheet-backdrop.visible { - opacity: 1; pointer-events: auto; -} +#sheet-backdrop.visible { opacity: 1; pointer-events: auto; } /* ── Section Label ────────────────────────────── */ -.section-label { - font-size: 10px; font-weight: 800; - color: var(--text3); - text-transform: uppercase; letter-spacing: 0.12em; - margin-bottom: 8px; - margin-top: 4px; -} +.section-label { font-size: 10px; font-weight: 800; color: var(--text3); text-transform: uppercase; letter-spacing: 0.12em; margin-bottom: 8px; margin-top: 4px; } /* ── Waypoints Row ────────────────────────────── */ -.waypoints-row { - display: flex; align-items: center; gap: 4px; - overflow-x: auto; padding: 0 0 4px; - scrollbar-width: none; -} +.waypoints-row { display: flex; align-items: center; gap: 4px; overflow-x: auto; padding: 0 0 4px; scrollbar-width: none; } .waypoints-row::-webkit-scrollbar { display: none; } -.wp-chip { - display: flex; align-items: center; gap: 6px; - background: var(--surface2); - border: 1px solid var(--border); - border-radius: 10px; - padding: 7px 10px; - flex-shrink: 0; - max-width: 140px; - cursor: pointer; - transition: border-color 0.15s; -} +.wp-chip { display: flex; align-items: center; gap: 6px; background: var(--surface2); border: 1px solid var(--border); border-radius: 10px; padding: 7px 10px; flex-shrink: 0; max-width: 140px; cursor: pointer; transition: border-color 0.15s; } .wp-chip:active { border-color: var(--accent); } -.wp-dot { - width: 10px; height: 10px; - border-radius: 50%; - flex-shrink: 0; -} -.wp-label { - font-size: 12px; font-weight: 600; - color: var(--text); - white-space: nowrap; overflow: hidden; text-overflow: ellipsis; -} +.wp-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; } +.wp-label { font-size: 12px; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .wp-arrow { color: var(--text3); font-size: 18px; flex-shrink: 0; padding: 0 1px; } -.wp-add { - display: flex; align-items: center; gap: 6px; - background: none; - border: 1.5px dashed var(--border2); - border-radius: 10px; - padding: 7px 12px; - font-size: 12px; font-weight: 600; - color: var(--text2); - flex-shrink: 0; - cursor: pointer; - transition: border-color 0.15s, color 0.15s; -} +.wp-add { display: flex; align-items: center; gap: 6px; background: none; border: 1.5px dashed var(--border2); border-radius: 10px; padding: 7px 12px; font-size: 12px; font-weight: 600; color: var(--text2); flex-shrink: 0; cursor: pointer; transition: border-color 0.15s, color 0.15s; } .wp-add:active { border-color: var(--accent); color: var(--accent); } /* ── Waypoints List ───────────────────────────── */ -#waypoints-list { - display: flex; flex-direction: column; gap: 4px; - margin-bottom: 10px; -} -.wl-item { - display: flex; align-items: center; gap: 8px; - background: var(--surface2); - border: 1px solid var(--border); - border-radius: 10px; - padding: 8px 10px; -} +#waypoints-list { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; } +.wl-item { display: flex; align-items: center; gap: 8px; background: var(--surface2); border: 1px solid var(--border); border-radius: 10px; padding: 8px 10px; } .wl-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; } .wl-label { flex: 1; font-size: 13px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -.wl-remove { - width: 28px; height: 28px; - border: none; background: none; - color: var(--text3); cursor: pointer; - display: flex; align-items: center; justify-content: center; - border-radius: 6px; - flex-shrink: 0; -} +.wl-remove { width: 28px; height: 28px; border: none; background: none; color: var(--text3); cursor: pointer; display: flex; align-items: center; justify-content: center; border-radius: 6px; flex-shrink: 0; } .wl-remove:active { background: var(--red-bg); color: var(--red); } .wl-remove svg { width: 14px; height: 14px; } -/* Route action buttons */ -.route-actions { - display: flex; gap: 8px; - margin-bottom: 12px; -} -.btn-action { - flex: 1; - height: 38px; - background: var(--surface2); - border: 1px solid var(--border); - border-radius: 10px; - color: var(--text2); - font-size: 12px; font-weight: 600; - display: flex; align-items: center; justify-content: center; gap: 5px; - cursor: pointer; - transition: all 0.15s; -} +/* Route actions */ +.route-actions { display: flex; gap: 8px; margin-bottom: 12px; } +.btn-action { flex: 1; height: 38px; background: var(--surface2); border: 1px solid var(--border); border-radius: 10px; color: var(--text2); font-size: 12px; font-weight: 600; display: flex; align-items: center; justify-content: center; gap: 5px; cursor: pointer; transition: all 0.15s; } .btn-action svg { width: 14px; height: 14px; } -.btn-action:active { background: var(--surface3); } +.btn-action:active { background: var(--surface3); transform: scale(0.94); } .btn-action.danger:active { background: var(--red-bg); color: var(--red); border-color: var(--red); } .btn-action.primary { border-color: var(--accent); color: var(--accent); } .btn-action.primary:active { background: var(--accent-bg); } /* ── Route Status ─────────────────────────────── */ -#route-status { - font-size: 13px; color: var(--text2); - padding: 8px 0; - display: flex; align-items: center; gap: 6px; -} +#route-status { font-size: 13px; color: var(--text2); padding: 8px 0; display: flex; align-items: center; gap: 6px; } /* ── Route Cards ──────────────────────────────── */ -#route-cards, #link-cards, #scenic-cards { - display: flex; flex-direction: column; gap: 8px; - margin-top: 4px; -} +#route-cards, #link-cards, #scenic-cards { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; } .route-card { - background: var(--surface2); - border: 1.5px solid var(--border); - border-radius: 14px; - padding: 12px 14px; - cursor: pointer; - transition: border-color 0.15s, box-shadow 0.15s; + background: var(--surface2); border: 1.5px solid var(--border); + border-radius: 14px; padding: 12px 14px; cursor: pointer; + transition: border-color 0.15s, box-shadow 0.15s, opacity 0.2s; -webkit-tap-highlight-color: transparent; + animation: cardFadeIn 0.2s ease-out both; } -.route-card.active { - border-color: var(--accent); - box-shadow: 0 0 0 1px var(--accent); -} -.rc-header { - display: flex; align-items: center; gap: 8px; - margin-bottom: 8px; -} -.rc-dot { - width: 10px; height: 10px; - border-radius: 50%; flex-shrink: 0; -} -.rc-title { flex: 1; font-size: 13px; font-weight: 700; color: var(--text); } -.rc-km { font-size: 14px; font-weight: 800; color: var(--text); font-variant-numeric: tabular-nums; } -.rc-time { font-size: 12px; color: var(--text2); font-variant-numeric: tabular-nums; } -.rc-bar { - height: 5px; - border-radius: 3px; - background: var(--surface3); - overflow: hidden; - margin-bottom: 8px; - display: flex; -} -.rc-bar-dirt { background: var(--gold); height: 100%; transition: width 0.4s; } -.rc-bar-asphalt { background: var(--text3); height: 100%; flex: 1; } -.rc-stats { display: flex; flex-wrap: wrap; gap: 5px; } - -/* Stat pills */ -.stat-pill { - display: inline-flex; align-items: center; gap: 4px; - border-radius: 20px; - padding: 3px 9px; - font-size: 11px; font-weight: 700; - letter-spacing: 0.02em; -} -.stat-pill.dirt { background: var(--gold-bg); color: var(--gold); } -.stat-pill.asphalt { background: var(--surface3); color: var(--text2); } -.stat-pill.path { background: var(--red-bg); color: var(--red); } - -/* ── Primary Button ───────────────────────────── */ -.btn-primary { - width: 100%; - height: 48px; - background: var(--accent); - color: #fff; - border: none; - border-radius: 14px; - font-size: 15px; font-weight: 700; - display: flex; align-items: center; justify-content: center; gap: 8px; - cursor: pointer; - transition: background 0.15s, transform 0.1s; - letter-spacing: 0.02em; - margin-top: 12px; -} -.btn-primary svg { width: 18px; height: 18px; } -.btn-primary:active { background: var(--accent-h); transform: scale(0.98); } -.btn-primary:disabled { opacity: 0.5; pointer-events: none; } - -/* ── Radius / Dist Selector ───────────────────── */ -.seg-control { - display: flex; gap: 4px; - background: var(--surface2); - border: 1px solid var(--border); - border-radius: 12px; - padding: 4px; - margin-bottom: 12px; -} -.seg-btn { - flex: 1; height: 34px; - background: none; border: none; - border-radius: 9px; - font-size: 13px; font-weight: 600; - color: var(--text2); - cursor: pointer; - transition: all 0.15s; -} -.seg-btn.active { - background: var(--accent); - color: #fff; - box-shadow: 0 2px 8px rgba(255,107,0,0.35); -} -.seg-btn:not(.active):active { background: var(--surface3); } -.dist-custom { - height: 34px; width: 70px; - background: var(--surface2); - border: 1px solid var(--border); - border-radius: 9px; - color: var(--text); - font-size: 13px; font-weight: 600; - text-align: center; - outline: none; - flex-shrink: 0; -} -.dist-custom:focus { border-color: var(--accent); } - -/* ── Recon Results ────────────────────────────── */ -.recon-grid { - display: grid; grid-template-columns: 1fr 1fr; - gap: 8px; margin-bottom: 14px; -} -.recon-stat { - background: var(--surface2); - border: 1px solid var(--border); - border-radius: 12px; - padding: 10px 12px; -} -.rs-value { - font-size: 22px; font-weight: 800; - color: var(--text); - font-variant-numeric: tabular-nums; - line-height: 1; - margin-bottom: 3px; -} -.rs-value.gold { color: var(--gold); } -.rs-value.red { color: var(--red); } -.rs-label { font-size: 11px; color: var(--text2); font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; } - -.poi-row { - display: flex; align-items: center; justify-content: space-between; - padding: 8px 0; - border-bottom: 1px solid var(--border); -} -.poi-row:last-child { border-bottom: none; } -.poi-row-label { font-size: 13px; color: var(--text); display: flex; align-items: center; gap: 8px; } -.poi-row-count { - font-size: 16px; font-weight: 800; - color: var(--accent); - font-variant-numeric: tabular-nums; -} -.poi-icon { width: 28px; height: 28px; border-radius: 8px; background: var(--surface2); display: flex; align-items: center; justify-content: center; font-size: 14px; } - -/* ── Scenic POI items ─────────────────────────── */ -.scenic-poi-item { - display: flex; align-items: center; gap: 8px; - font-size: 12px; color: var(--text2); - padding: 3px 0; -} -.scenic-score-bar { - height: 4px; border-radius: 2px; - background: var(--surface3); overflow: hidden; margin: 6px 0; -} -.scenic-score-fill { height: 100%; background: var(--gold); border-radius: 2px; } - -/* ── Link Points ──────────────────────────────── */ -.link-points { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; } -.link-pt { - display: flex; align-items: center; gap: 8px; - background: var(--surface2); - border: 1.5px solid var(--border); - border-radius: 10px; - padding: 10px 12px; -} -.link-pt-num { - width: 24px; height: 24px; - border-radius: 50%; - background: var(--accent); - color: #fff; font-size: 12px; font-weight: 800; - display: flex; align-items: center; justify-content: center; - flex-shrink: 0; -} -.link-pt-label { font-size: 13px; color: var(--text); flex: 1; } -.link-pt.empty .link-pt-num { background: var(--surface3); color: var(--text3); } -.link-pt.empty .link-pt-label { color: var(--text3); } -#link-status { font-size: 13px; color: var(--text2); padding: 4px 0 10px; } - -/* ── Scenic Config ───────────────────────────── */ -#scenic-status { font-size: 13px; color: var(--text2); padding: 6px 0; display: flex; align-items: center; gap: 6px; } -.dist-row { display: flex; gap: 4px; align-items: center; margin-bottom: 4px; } - -/* ── Marker Popup / Dialog ────────────────────── */ -#marker-dialog { - position: fixed; - inset: 0; z-index: 500; - display: flex; align-items: flex-end; - justify-content: center; - padding-bottom: env(safe-area-inset-bottom, 0px); - pointer-events: none; opacity: 0; - transition: opacity 0.2s; -} -#marker-dialog.open { pointer-events: auto; opacity: 1; } -.marker-dialog-inner { - background: var(--surface); - border-radius: 20px 20px 0 0; - border-top: 1px solid var(--border); - padding: 0 16px 20px; - width: 100%; - transform: translateY(30px); - transition: transform 0.25s cubic-bezier(0.32, 0, 0.15, 1); -} -#marker-dialog.open .marker-dialog-inner { transform: translateY(0); } -.marker-type-grid { - display: grid; grid-template-columns: repeat(3, 1fr); - gap: 8px; padding: 12px 0; -} -.marker-type-btn { - background: var(--surface2); - border: 1.5px solid var(--border); - border-radius: 12px; - padding: 12px 8px; - cursor: pointer; - transition: all 0.15s; - display: flex; flex-direction: column; align-items: center; gap: 5px; - -webkit-tap-highlight-color: transparent; -} -.marker-type-btn:active { border-color: var(--accent); background: var(--accent-bg); } -.marker-type-btn .mt-icon { font-size: 24px; } -.marker-type-btn .mt-label { font-size: 11px; font-weight: 600; color: var(--text2); text-transform: uppercase; letter-spacing: 0.06em; } - -/* ── No Data Warning ─────────────────────────── */ -#no-data-warning { - display: none; - position: fixed; bottom: 80px; left: 12px; right: 12px; - background: var(--red-bg); - border: 1px solid var(--red); - border-radius: 12px; - padding: 10px 14px; - font-size: 13px; color: var(--red); - z-index: 200; -} -#no-data-warning.visible { display: block; } - -/* ── Loading Skeleton ────────────────────────── */ -.skeleton { - background: linear-gradient(90deg, var(--surface2) 0%, var(--surface3) 50%, var(--surface2) 100%); - background-size: 200% 100%; - animation: skeleton-wave 1.4s infinite; - border-radius: 8px; - height: 14px; -} -@keyframes skeleton-wave { - 0% { background-position: 200% 0; } - 100% { background-position: -200% 0; } -} -.skeleton-card { - background: var(--surface2); - border: 1.5px solid var(--border); - border-radius: 14px; - padding: 14px; - margin-bottom: 8px; -} - -/* ── Ruler ───────────────────────────────────── */ -#ruler-info { - position: fixed; - top: calc(max(env(safe-area-inset-top,0px),12px) + 58px); - left: 12px; right: 12px; - background: var(--surface); - border: 1px solid var(--border); - border-radius: 12px; - padding: 10px 14px; - font-size: 13px; color: var(--text); - font-weight: 600; - z-index: 200; - display: none; - box-shadow: var(--shadow-sm); -} -#ruler-info.visible { display: flex; align-items: center; gap: 8px; } - -/* ── Waypoint Markers on Map ─────────────────── */ -.route-waypoint-marker { - width: 28px; height: 28px; - border-radius: 50%; - display: flex; align-items: center; justify-content: center; - font-size: 11px; font-weight: 800; - color: #fff; - cursor: pointer; - box-shadow: 0 2px 8px rgba(0,0,0,0.5); - border: 2px solid rgba(255,255,255,0.8); -} -.named-marker-el { - font-size: 22px; cursor: pointer; - filter: drop-shadow(0 2px 4px rgba(0,0,0,0.5)); - user-select: none; line-height: 1; -} - -/* ── Desktop Layout ──────────────────────────── */ -@media (min-width: 768px) { - #toolbar { - flex-direction: column; - width: 68px; height: auto; - right: auto; left: 0; - top: 0; bottom: 0; - border-right: 1px solid var(--border); - border-top: none; - padding: 80px 0 20px; - justify-content: flex-start; - gap: 4px; - } - .tb-btn { - width: 60px; height: 56px; - flex: none; - } - .bottom-sheet { - left: 68px; right: auto; - width: 340px; - max-height: 100vh; - border-radius: 0 16px 16px 0; - border-top: none; - border-right: 1px solid var(--border); - top: 0; bottom: 0; - transform: translateX(-120%); - } - .bottom-sheet.open { transform: translateX(0); } - #search-bar { - left: 80px; right: 12px; max-width: 400px; - } - #map-controls-r { - right: 12px; - bottom: 12px; - } - #sheet-backdrop { display: none; } -} - -/* ── Misc ────────────────────────────────────── */ -.text-accent { color: var(--accent); } -.text-gold { color: var(--gold); } -.text-red { color: var(--red); } -.text-muted { color: var(--text2); } -.mt-8 { margin-top: 8px; } -.mt-12 { margin-top: 12px; } -.mb-8 { margin-bottom: 8px; } - -/* cursor crosshair в режиме выбора точки */ -.cursor-crosshair .maplibregl-canvas { cursor: crosshair !important; } +.route-card.active { border-color: var(--accent); box-shadow: 0 0 0 1px var \ No newline at end of file diff --git a/tasks/enduro-trails/prototype/static/index.html b/tasks/enduro-trails/prototype/static/index.html index d991a46..64e868b 100644 --- a/tasks/enduro-trails/prototype/static/index.html +++ b/tasks/enduro-trails/prototype/static/index.html @@ -25,6 +25,7 @@
@@ -230,6 +231,7 @@ + diff --git a/tasks/enduro-trails/reports/dev-2026-05-05-phase5-redesign.md b/tasks/enduro-trails/reports/dev-2026-05-05-phase5-redesign.md new file mode 100644 index 0000000..8ed25e5 --- /dev/null +++ b/tasks/enduro-trails/reports/dev-2026-05-05-phase5-redesign.md @@ -0,0 +1,20 @@ +# Dev Report: Phase 5 Redesign +Дата: 2026-05-05 +Статус: IN PROGRESS + +## Задача +Реализация Фазы 5 «Редизайн» — дизайн-система, две темы, SunCalc, skeleton, свайп, десктоп, микро-анимации. + +## Сделано +- [ ] Задача 1: Дизайн-система (CSS vars + SunCalc + переключатель) +- [ ] Задача 2: Все компоненты на CSS vars +- [ ] Задача 3: Skeleton loading +- [ ] Задача 4: Свайп вниз для закрытия sheets +- [ ] Задача 5: Десктоп-адаптив +- [ ] Задача 6: Микро-анимации +- [ ] Деплой + проверка + +## Изменённые файлы +- `prototype/static/index.html` — SunCalc CDN, theme label +- `prototype/static/app.css` — CSS vars для двух тем, skeleton, свайп, десктоп, анимации +- `prototype/static/app.js` — SunCalc, toggleTheme 3 режима, skeleton, свайп, анимации