diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7825a8e..85381e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,3 +13,8 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
- Docker configuration
- ET-002: чекбокс «POI» в попапе рельефа — показ/скрытие маркеров POI
с сохранением состояния в localStorage (ключ `poi-visible`)
+- ET-005: переключатель единиц измерения расстояний (км/мили) в попапе
+ рельефа — новый модуль `src/web/units.js` с централизованным
+ форматтером `Units.formatDistance()`; выбор сохраняется в localStorage
+ (ключ `distance_unit`), пересчёт всех видимых расстояний выполняется
+ единым оркестратором по событию `unitchange`
diff --git a/docs/work-items/ET-005/00-business-request.md b/docs/work-items/ET-005/00-business-request.md
new file mode 100644
index 0000000..ed5cded
--- /dev/null
+++ b/docs/work-items/ET-005/00-business-request.md
@@ -0,0 +1,7 @@
+# Business Request: Добавить кнопку переключения единиц измерения (км/мили)
+
+Work Item ID: ET-005
+
+## Description
+
+TBD
diff --git a/docs/work-items/ET-005/01-brd.md b/docs/work-items/ET-005/01-brd.md
new file mode 100644
index 0000000..9a4b612
--- /dev/null
+++ b/docs/work-items/ET-005/01-brd.md
@@ -0,0 +1,18 @@
+# BRD: Переключение единиц измерения (км/мили)
+
+## Проблема
+Пользователи из разных стран используют разные единицы измерения. UI показывает только километры.
+
+## Решение
+Кнопка переключения км/мили в панели настроек карты.
+
+## Метрики успеха
+- Переключение за 1 клик
+- Сохранение в localStorage
+- Мгновенный пересчёт расстояний
+
+## Scope
+- Кнопка в панели настроек
+- Пересчёт расстояний
+- localStorage persistence
+- Default: км
\ No newline at end of file
diff --git a/docs/work-items/ET-005/02-trz.md b/docs/work-items/ET-005/02-trz.md
new file mode 100644
index 0000000..298d875
--- /dev/null
+++ b/docs/work-items/ET-005/02-trz.md
@@ -0,0 +1,19 @@
+# ТЗ: Переключение единиц измерения
+
+## Функциональные требования
+1. Кнопка-toggle в панели настроек карты (рядом с другими controls)
+2. Два состояния: km (default) / mi
+3. При переключении — пересчёт всех видимых расстояний
+4. Сохранение выбора в localStorage (ключ: distance_unit)
+5. При загрузке страницы — читать из localStorage
+
+## Нефункциональные требования
+- Пересчёт < 100ms
+- Кнопка доступна на всех размерах экрана
+- Не блокирует другие UI элементы
+
+## Технический дизайн
+- Новый модуль: src/web/static/js/units.js
+- Коэффициент: 1 km = 0.621371 mi
+- Event: custom event "unitchange" на document
+- Все компоненты с расстояниями слушают "unitchange"
\ No newline at end of file
diff --git a/docs/work-items/ET-005/03-acceptance-criteria.md b/docs/work-items/ET-005/03-acceptance-criteria.md
new file mode 100644
index 0000000..516a852
--- /dev/null
+++ b/docs/work-items/ET-005/03-acceptance-criteria.md
@@ -0,0 +1,21 @@
+# Acceptance Criteria: ET-005
+
+## AC-1: Кнопка переключения
+- [ ] В панели настроек карты есть кнопка km/mi
+- [ ] Кнопка визуально показывает текущий выбор
+- [ ] Клик переключает между km и mi
+
+## AC-2: Пересчёт расстояний
+- [ ] Все расстояния на карте пересчитываются при переключении
+- [ ] Коэффициент: 1 km = 0.621371 mi
+- [ ] Округление до 1 знака после запятой
+
+## AC-3: Persistence
+- [ ] Выбор сохраняется в localStorage
+- [ ] При перезагрузке страницы — восстанавливается
+- [ ] По умолчанию (без сохранённого значения) — km
+
+## AC-4: UI/UX
+- [ ] Кнопка не перекрывает другие элементы
+- [ ] Работает на мобильных устройствах
+- [ ] Переключение < 100ms (без видимой задержки)
\ No newline at end of file
diff --git a/docs/work-items/ET-005/04-test-plan.yaml b/docs/work-items/ET-005/04-test-plan.yaml
new file mode 100644
index 0000000..1c85195
--- /dev/null
+++ b/docs/work-items/ET-005/04-test-plan.yaml
@@ -0,0 +1,33 @@
+name: ET-005 Unit Toggle Test Plan
+tests:
+ - id: TP-01
+ title: Default unit is km
+ steps:
+ - Clear localStorage
+ - Load page
+ expected: All distances shown in km
+
+ - id: TP-02
+ title: Toggle to miles
+ steps:
+ - Click km/mi button
+ expected: All distances recalculated to miles
+
+ - id: TP-03
+ title: Persistence
+ steps:
+ - Switch to miles
+ - Reload page
+ expected: Miles still selected after reload
+
+ - id: TP-04
+ title: Toggle back to km
+ steps:
+ - While in miles mode, click button
+ expected: Distances back in km
+
+ - id: TP-05
+ title: Mobile responsive
+ steps:
+ - Open on 375px viewport
+ expected: Button visible and clickable
\ No newline at end of file
diff --git a/docs/work-items/ET-005/06-adr/adr-0001-unit-toggle-client-side.md b/docs/work-items/ET-005/06-adr/adr-0001-unit-toggle-client-side.md
new file mode 100644
index 0000000..84a4dc3
--- /dev/null
+++ b/docs/work-items/ET-005/06-adr/adr-0001-unit-toggle-client-side.md
@@ -0,0 +1,205 @@
+---
+type: adr
+work_item_id: ET-005
+adr_id: adr-0001
+title: "ADR-0001: Переключение единиц измерения (км/мили) — клиентское решение с централизованным форматтером"
+status: accepted
+created_at: 2026-05-21
+authors:
+ - "agent:architect"
+supersedes: []
+superseded_by: []
+labels: []
+---
+
+# ADR-0001 — Переключение единиц измерения (км/мили): клиентское решение с централизованным форматтером
+
+## Статус
+
+Accepted
+
+## Контекст
+
+ET-005 добавляет переключатель единиц измерения расстояний (км/мили) в
+панель настроек карты. Выбор сохраняется в `localStorage`, по умолчанию —
+километры, при переключении все видимые расстояния пересчитываются за
+< 100 мс (см. `01-brd.md`, `02-trz.md`, `03-acceptance-criteria.md`).
+
+Существующее состояние кодовой базы (`src/web/`):
+
+- Фронтенд **плоский, без сборщика и без модульной системы**:
+ `index.html`, `app.js`, `app.css`, `style.json`, `style-dark.json`.
+ `app.js` (≈ 3036 строк) подключается как **классический скрипт**
+ (``, `index.html:405`), а не ES-модуль.
+ Каталога `src/web/static/js/` в репозитории **нет**.
+- Форматирование расстояний **не централизовано** — захардкоженный
+ паттерн `(m / 1000).toFixed(N) + ' км'` встречается минимум в **13
+ местах** `app.js`: `formatDist()` (стр. 187–189), карточки сегментов
+ (638), карточки маршрута (1157, 1191, 2202, 2212, 2357, 2370, 2605),
+ scale-bar (1416–1440), всплывающие подсказки (1478), линейка/ruler
+ (1837, 1875, 1885, 1931). Единого форматтера нет.
+- Внутренняя каноническая единица расстояния — **метры**
+ (`route.distance_m`), для линейки — километры (`rulerTotal`).
+- Сложился устойчивый паттерн персистентности UI-настроек в
+ `localStorage`: ключи `enduro-theme-mode`, `terrain-hillshade`,
+ `terrain-tri`, `trails-track`, `trails-path`, `poi-visible` (ET-002),
+ `MARKERS_KEY`.
+- «Панель настроек карты» из BRD/ТЗ де-факто реализована как попап
+ `#terrain-popup` (заголовок «Эндуро»), открываемый кнопкой «Рельеф».
+ В ET-002 в этот же попап добавлен чекбокс POI. В `app.css` уже есть
+ готовый компонент сегментированного переключателя `.seg-control` /
+ `.seg-btn` (стр. 360–363).
+
+Backend (FastAPI), БД (SQLite/Spatialite), тайл-сервер и OSRM к выбору
+единиц измерения отношения **не имеют**: расстояния приходят с backend
+в метрах, перевод — исключительно вопрос представления на клиенте.
+
+## Рассматриваемые варианты
+
+### Вариант A — Централизованный модуль-форматтер + единый оркестратор (выбран)
+
+Новый модуль `src/web/units.js`, подключаемый как **классический скрипт
+до `app.js`**, экспортирует глобальный неймспейс `window.Units` с
+функциями состояния (`getUnit()` / `setUnit()`) и форматирования
+(`formatDistance(meters, {precision})`). Все 13 мест форматирования в
+`app.js` переводятся на вызов `Units.formatDistance(...)`. При смене
+единицы `setUnit()` пишет в `localStorage` и диспатчит `unitchange` на
+`document`; в `app.js` регистрируется **один** обработчик-оркестратор
+`onUnitChange()`, который пере-вызывает существующие функции отрисовки
+видимых поверхностей с расстояниями.
+
+- **Плюсы:** единственный источник истины по форматированию убирает
+ риск рассинхрона; нулевые изменения backend/БД/инфраструктуры;
+ пересчёт мгновенный (нет ни сети, ни перезагрузки тайлов) — НФТ
+ «< 100 мс» выполняется тривиально; согласуется с принципом «минимум
+ зависимостей»; модуль покрывается unit-тестами изолированно.
+- **Минусы:** требует рефакторинга 13 мест в `app.js` (риск пропустить
+ вызов — вынесен в `10-tech-risks.md`, R1); глобальный неймспейс вместо
+ ES-import — но это сознательное соответствие текущему стилю `app.js`.
+
+### Вариант B — Рассыпанные подписчики `unitchange` по компонентам (буквальный тех-дизайн ТЗ)
+
+Каждый компонент с расстоянием самостоятельно подписывается на
+`unitchange` и сам себя перерисовывает.
+
+- **Плюсы:** формально совпадает с разделом «Технический дизайн» ТЗ.
+- **Минусы:** N подписок вместо одной — высокая связность, легко
+ забыть подписку у нового компонента; дублирование логики перерисовки;
+ не решает корневую проблему — захардкоженное `+ ' км'` остаётся
+ размазанным. Отклонён: оркестрация в одном месте надёжнее.
+
+### Вариант C — Вынести `units.js` в ES-модуль `src/web/static/js/units.js`
+
+Буквально следовать пути из ТЗ: создать `src/web/static/js/units.js` как
+ES-модуль.
+
+- **Плюсы:** «правильная» модульность.
+- **Минусы:** в проекте нет каталога `static/js/`, нет сборщика, а
+ `app.js` подключён как классический скрипт. Полноценный `import`
+ потребовал бы перевода `app.js` в `type="module"` и реструктуризации
+ ≈ 3000 строк — несопоставимо со scope ET-005 и противоречит принципу
+ «минимум зависимостей». Отклонён.
+
+## Решение
+
+Принимается **Вариант A**.
+
+1. **Только клиент.** Backend, БД, API, тайл-сервер, OSRM, инфраструктура
+ — без изменений. Расстояния продолжают приходить с backend в метрах.
+
+2. **Новый модуль `src/web/units.js`** (НЕ `src/web/static/js/units.js`).
+ Раздел «Технический дизайн» ТЗ носит рекомендательный характер;
+ финальное размещение файлов — мандат архитектуры. Путь скорректирован
+ под фактическую плоскую структуру `src/web/`. Модуль подключается в
+ `index.html` как классический скрипт **строго перед `app.js`**:
+ `` затем `
+
+