diff --git a/README.md b/README.md
index aa8e552..d41014c 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ Web Docker-образ устанавливает зависимости чере
Предыдущие аудиты и план восстановления доступны в `docs/` как исторические материалы. Их незакрытые на момент составления чекбоксы не являются текущим backlog; статусы сведены в [итоговый отчёт](docs/RECOVERY_FIXES_REPORT.md).
-На ширинах 320, 390, 768 и 1280 px ранее проверено отсутствие горизонтального переполнения основных страниц. Это не полная визуальная приёмка: аудит обнаружил неверную desktop-компоновку фильтров; наполненные карточки, длинные названия, клавиатура и zoom остаются отдельной задачей.
+На ширинах 320, 390, 768 и 1280 px проверено отсутствие горизонтального переполнения основных страниц; исправлена desktop-компоновка фильтров. Наполненные карточки, длинные названия, клавиатура и zoom остаются постоянной частью визуальной приёмки каждого крупного UI-пакета.
Функциональный MVP и локальный production-контур готовятся к открытой альфе: официальный импорт, пользовательские заявки, модерация, объяснимый индекс, staging внешних источников, адаптивный Astro UI, миграции, резервное копирование, retention, мониторинг и security/accessibility-проверки реализованы. На всех страницах подключён компактный баннер открытой альфы со ссылками на статус, правила и отправку улова. В production Compose включён community scheduler; локально он запускается отдельным профилем. Публичный запуск блокируют покупка и настройка сервера, DNS/TLS, реальные секреты, внешний backup, канал уведомлений; публичный адрес обратной связи ещё не задан.
@@ -32,9 +32,9 @@ RF4DB/RF4-STAT/RF4MAP/RF4 Posts сначала принимаются в изо
На сайте у каждой записи отображается источник, а у агрегированной активности — все вошедшие в расчёт источники. Неполные community-наблюдения публикуются сразу в отдельной ленте «Полевые сигналы» с предупреждением и перечнем отсутствующих полей; до подтверждения полноты они не влияют на индекс клёва. Лента раскрывается серверной кнопкой «Показать ещё», сохраняет выбранные фильтры и ограничена 48 сигналами на страницу. Визуально объединяются только повторы одного ID источника; похожие записи разных площадок остаются самостоятельными наблюдениями. Sidebar лидера скрывается при единственном результате, чтобы не повторять ту же карточку.
-Базовый SEO-контур готов для `rf4spotter.ru`: страницы имеют уникальные метаданные, canonical, Open Graph/Twitter Card, фирменное изображение 1200×630 и JSON-LD; доступны динамические `/robots.txt` и `/sitemap.xml`, административные и ошибочные страницы закрыты от индексации, добавлена собственная страница 404. Индексируемые каталоги рыб и водоёмов, detail-страницы и сочетания водоём + рыба строятся из актуального разрешённого справочника и включаются в sitemap. Подключены фирменные favicon/app icons, web manifest и production-кэширование статических ресурсов. Карточки активности показывают единый паспорт данных: источники, свежесть, полноту и уровень доверия. На `/status` опубликована легенда цветов всех источников и статусов качества.
+Базовый SEO-контур готов для `rf4spotter.ru`: страницы имеют уникальные метаданные, canonical, Open Graph/Twitter Card, фирменное изображение 1200×630 и JSON-LD; доступны динамические `/robots.txt` и `/sitemap.xml`, административные и ошибочные страницы закрыты от индексации, добавлена собственная страница 404. Индексируемые каталоги рыб и водоёмов, detail-страницы и сочетания водоём + рыба строятся из актуального разрешённого справочника и включаются в sitemap. Подключены резкие favicon/app icons из SVG-мастера, отдельные полнофоновые maskable-иконки, web manifest и production-кэширование статических ресурсов. Карточки активности показывают единый паспорт данных: источники, свежесть, полноту и уровень доверия. На `/status` опубликована легенда цветов всех источников и статусов качества.
-Публичные точки используют постоянные читаемые адреса вида `/spots/kuori-85x92`; старые UUID-адреса остаются совместимыми и перенаправляются на канонический URL. На странице точки координаты дополнительно показаны фирменным радаром, который не имитирует отсутствующую географию водоёма, а уловы за 72 часа — шкалой-леской с 12-часовым шагом. Каждый улов показывает источник, относительную свежесть и точное время UTC; время получения явно отделено от времени улова. Карточки активности и каталог дополнены лёгкими SVG-силуэтами рыб без внешних графических зависимостей. Пустые и аварийные состояния используют собственную CSS-иллюстрацию поплавка; анимация учитывает системное ограничение движения.
+Публичные точки используют постоянные читаемые адреса вида `/spots/kuori-85x92`; старые UUID-адреса остаются совместимыми и перенаправляются на канонический URL. На странице точки координаты дополнительно показаны фирменным радаром, который не имитирует отсутствующую географию водоёма, а уловы за 72 часа — шкалой-леской с 12-часовым шагом. Каждый улов показывает источник, относительную свежесть и точное время UTC; время получения явно отделено от времени улова. Каталоги оформлены как полевой атлас: тёмный seal показывает объём справочника, карточки рыб используют SVG-силуэты, карточки водоёмов — абстрактные береговые контуры и точку наблюдения. Пустые состояния используют статичную CSS-иллюстрацию поплавка; смысловые анимации полностью учитывают системное ограничение движения.
Все пять community-парсеров подключены к отдельному scheduler-процессу. Попытка резервируется в PostgreSQL до HTTP-запроса, поэтому ошибки тоже расходуют cooldown. Блокировка и минимальный интервал 1800 секунд действуют на весь домен; endpoint одного сайта выбираются по самому давнему запуску и не голодают. Ручной production-запуск использует тот же журнал: `docker compose exec api python -m app.cli fetch-community rf4stat-fishing`. Локально scheduler включается профилем `docker compose --profile scheduler up -d`; detail-URL RF4MAP/RF4 Posts задаются переменными окружения.
diff --git a/apps/web/public/apple-touch-icon.png b/apps/web/public/apple-touch-icon.png
index f6ca2bd..dfb575d 100644
Binary files a/apps/web/public/apple-touch-icon.png and b/apps/web/public/apple-touch-icon.png differ
diff --git a/apps/web/public/favicon-32.png b/apps/web/public/favicon-32.png
index e90f3c9..172a0a8 100644
Binary files a/apps/web/public/favicon-32.png and b/apps/web/public/favicon-32.png differ
diff --git a/apps/web/public/icon-192.png b/apps/web/public/icon-192.png
index c3ac0ef..dc5105d 100644
Binary files a/apps/web/public/icon-192.png and b/apps/web/public/icon-192.png differ
diff --git a/apps/web/public/icon-512.png b/apps/web/public/icon-512.png
index bba258a..e9d0d69 100644
Binary files a/apps/web/public/icon-512.png and b/apps/web/public/icon-512.png differ
diff --git a/apps/web/public/icon-maskable-192.png b/apps/web/public/icon-maskable-192.png
new file mode 100644
index 0000000..e0d9a58
Binary files /dev/null and b/apps/web/public/icon-maskable-192.png differ
diff --git a/apps/web/public/icon-maskable-512.png b/apps/web/public/icon-maskable-512.png
new file mode 100644
index 0000000..73ce7f9
Binary files /dev/null and b/apps/web/public/icon-maskable-512.png differ
diff --git a/apps/web/public/icon-maskable.svg b/apps/web/public/icon-maskable.svg
new file mode 100644
index 0000000..f04cb3d
--- /dev/null
+++ b/apps/web/public/icon-maskable.svg
@@ -0,0 +1,11 @@
+
diff --git a/apps/web/public/site.webmanifest b/apps/web/public/site.webmanifest
index c099e01..b45c85a 100644
--- a/apps/web/public/site.webmanifest
+++ b/apps/web/public/site.webmanifest
@@ -9,7 +9,9 @@
"background_color": "#f2f5ee",
"theme_color": "#082226",
"icons": [
- { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" },
- { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }
+ { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" },
+ { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" },
+ { "src": "/icon-maskable-192.png", "sizes": "192x192", "type": "image/png", "purpose": "maskable" },
+ { "src": "/icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
]
}
diff --git a/apps/web/src/components/PageHero.astro b/apps/web/src/components/PageHero.astro
index 51d7055..60c1570 100644
--- a/apps/web/src/components/PageHero.astro
+++ b/apps/web/src/components/PageHero.astro
@@ -1,8 +1,9 @@
---
-const { eyebrow, title, description } = Astro.props as { eyebrow: string; title: string; description?: string };
+import FishingIcon from "./FishingIcon.astro";
+import FishSilhouette from "./FishSilhouette.astro";
+const { eyebrow, title, description, variant, count } = Astro.props as { eyebrow: string; title: string; description?: string; variant?: "water" | "fish"; count?: number };
---
-
- {eyebrow}
-
}
diff --git a/apps/web/src/components/WaterbodyMark.astro b/apps/web/src/components/WaterbodyMark.astro
new file mode 100644
index 0000000..2ce25ed
--- /dev/null
+++ b/apps/web/src/components/WaterbodyMark.astro
@@ -0,0 +1,15 @@
+---
+const { name, size = 112 } = Astro.props as { name: string; size?: number };
+const variant = [...name].reduce((sum, char) => sum + (char.codePointAt(0) ?? 0), 0) % 3;
+const shores = [
+ "M8 57C22 42 34 48 47 34S75 20 91 34s16 7 25-3",
+ "M7 48c17-20 34 5 50-10s28-15 38 1 13 14 22 9",
+ "M5 54c14-4 20-24 39-18s27-9 40-14 20 11 33 10",
+];
+---
+
diff --git a/apps/web/src/pages/fish/index.astro b/apps/web/src/pages/fish/index.astro
index 0e55b77..c128c68 100644
--- a/apps/web/src/pages/fish/index.astro
+++ b/apps/web/src/pages/fish/index.astro
@@ -13,6 +13,6 @@ if (unavailable) {
}
---
-
- {unavailable ? : }
+
+ {unavailable ? : }
diff --git a/apps/web/src/pages/waterbodies/index.astro b/apps/web/src/pages/waterbodies/index.astro
index cefb784..3e4fd7e 100644
--- a/apps/web/src/pages/waterbodies/index.astro
+++ b/apps/web/src/pages/waterbodies/index.astro
@@ -2,6 +2,7 @@
import Layout from "../../layouts/Layout.astro";
import PageHero from "../../components/PageHero.astro";
import StatePanel from "../../components/StatePanel.astro";
+import WaterbodyMark from "../../components/WaterbodyMark.astro";
import { api, type DictionaryItem } from "../../lib/api";
let waters: DictionaryItem[] = [], unavailable = false;
try { waters = await api("/api/v1/waterbodies?limit=500"); } catch { unavailable = true; }
@@ -12,6 +13,6 @@ if (unavailable) {
}
---
-
- {unavailable ? : }
+
+ {unavailable ? : }
diff --git a/apps/web/src/styles/catalog.css b/apps/web/src/styles/catalog.css
index 079816f..31c700c 100644
--- a/apps/web/src/styles/catalog.css
+++ b/apps/web/src/styles/catalog.css
@@ -1 +1,5 @@
.catalog-hero{padding:68px 0 42px;border-bottom:1px solid #cbd6ce}.catalog-hero h1{margin:12px 0 10px;font:400 clamp(58px,7vw,94px)/.9 Georgia,serif;letter-spacing:-.055em}.catalog-hero p{color:#4d605d;font-size:16px}.catalog-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;padding:34px 0 100px}.catalog-grid>a{display:grid;grid-template-columns:1fr auto;gap:7px;padding:22px;border:1px solid #d3ddd5;border-radius:14px;background:#fbfcf9;text-decoration:none;transition:.18s ease}.catalog-grid>a:hover{transform:translateY(-2px);border-color:#81958a;box-shadow:0 14px 30px #14333812}.catalog-grid span{grid-column:1/-1;color:#657673;font-size:10px;text-transform:uppercase;letter-spacing:.1em}.catalog-grid strong{font:400 22px Georgia,serif}.catalog-grid i{font-style:normal;color:#6d8d2a}.catalog-results{display:grid;grid-template-columns:230px 1fr;gap:26px;padding:34px 0 100px}.catalog-results aside{padding:20px;background:#fff;border:1px solid #d6dfd7;border-radius:14px;align-self:start}.catalog-results aside nav{display:flex;flex-direction:column;margin-top:12px}.catalog-results aside a{padding:10px 0;border-bottom:1px solid #e1e8e2;color:#405552;font-size:13px}.catalog-results>div{display:grid;gap:12px}.catalog-results--single{display:block;max-width:960px}.catalog-results .spot-card{grid-template-columns:45px 1fr 190px}@media(max-width:900px){.catalog-grid{grid-template-columns:repeat(2,1fr)}.catalog-results{grid-template-columns:1fr}.catalog-results aside nav{display:grid;grid-template-columns:repeat(2,1fr);gap:0 16px}}@media(max-width:720px){.catalog-grid{grid-template-columns:1fr;padding-bottom:70px}.catalog-results{padding-bottom:70px}.catalog-results .spot-card{grid-template-columns:34px 1fr}.topbar nav{gap:8px}.topbar nav a{font-size:12px}}
+
+/* Editorial atlas: catalog pages read as a field guide, not a utility list. */
+.catalog-hero--illustrated{position:relative;display:grid;grid-template-columns:minmax(0,1fr) 210px;align-items:center;gap:48px;min-height:310px;overflow:hidden}.catalog-hero--illustrated:before{content:"";position:absolute;right:102px;width:330px;height:190px;border:1px solid color-mix(in srgb,var(--teal) 11%,transparent);border-radius:50%;box-shadow:0 0 0 32px color-mix(in srgb,var(--teal) 5%,transparent),0 0 0 64px color-mix(in srgb,var(--teal) 3%,transparent);transform:rotate(-9deg);pointer-events:none}.catalog-hero__copy{position:relative;z-index:1}.catalog-hero__copy p{max-width:660px;line-height:1.6}.catalog-hero__seal{position:relative;z-index:1;width:176px;height:176px;justify-self:end;display:grid;place-items:center;align-content:center;border:1px solid #345155;border-radius:50%;background:var(--deep);color:var(--lime);box-shadow:0 18px 48px #16383c1b}.catalog-hero__seal>span{height:60px;display:grid;place-items:center}.catalog-hero__seal .fish-silhouette{color:var(--lime)}.catalog-hero__seal strong{margin-top:5px;font:400 32px/1 Georgia,serif;color:var(--white)}.catalog-hero__seal small{margin-top:4px;color:#a9b8b5;font-size:9px;text-transform:uppercase;letter-spacing:.13em}.catalog-grid{grid-template-columns:repeat(auto-fit,minmax(min(100%,330px),1fr));gap:14px}.catalog-grid>a{min-height:146px;align-content:start;padding:24px 25px;border-color:var(--border-soft);background:linear-gradient(145deg,var(--surface),var(--surface-soft))}.catalog-grid>a:before{content:"";position:absolute;inset:0;border-top:3px solid transparent;transition:border-color var(--motion-base) var(--ease-out)}.catalog-grid>a:hover:before{border-color:var(--lime)}.catalog-grid>a>span{color:var(--text-muted)}.catalog-grid>a>strong{position:relative;z-index:1;margin-top:10px;font-size:28px}.catalog-grid>a>i{position:relative;z-index:1;align-self:end;display:flex;align-items:center;gap:8px;margin-top:20px;color:var(--text-muted);font:700 10px Inter,sans-serif;text-transform:uppercase;letter-spacing:.09em}.catalog-grid>a>i b{color:#6d8d2a;font-size:16px}.catalog-grid>a>.fish-silhouette{right:20px;bottom:12px;opacity:.095}.waterbody-mark{position:absolute;right:14px;bottom:8px;fill:none;stroke:#315f63;stroke-width:1.2;opacity:.12;pointer-events:none}.waterbody-mark__shore{stroke-width:2}.waterbody-mark circle{fill:var(--lime);stroke:none}
+@media(max-width:720px){.catalog-hero--illustrated{grid-template-columns:1fr 106px;gap:18px;min-height:230px}.catalog-hero--illustrated:before{right:-35px;width:210px;height:120px}.catalog-hero__seal{width:96px;height:96px}.catalog-hero__seal>span{height:38px}.catalog-hero__seal .fish-icon{width:36px;height:36px}.catalog-hero__seal .fish-silhouette{width:62px}.catalog-hero__seal strong{font-size:20px}.catalog-hero__seal small{display:none}.catalog-grid>a{min-height:132px}.catalog-grid>a>strong{font-size:25px}}
diff --git a/deploy/Caddyfile b/deploy/Caddyfile
index 0289cfc..4307450 100644
--- a/deploy/Caddyfile
+++ b/deploy/Caddyfile
@@ -12,7 +12,7 @@
@immutable path /_astro/*
header @immutable Cache-Control "public, max-age=31536000, immutable"
- @brandAssets path /favicon.svg /favicon-32.png /apple-touch-icon.png /icon-192.png /icon-512.png /og-rf4spotter.png
+ @brandAssets path /favicon.svg /favicon-32.png /apple-touch-icon.png /icon-192.png /icon-512.png /icon-maskable-192.png /icon-maskable-512.png /og-rf4spotter.png
header @brandAssets Cache-Control "public, max-age=86400, stale-while-revalidate=604800"
@manifest path /site.webmanifest
diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md
index 0d2a124..6a5c23a 100644
--- a/docs/ROADMAP.md
+++ b/docs/ROADMAP.md
@@ -25,7 +25,7 @@
- [x] **B03 · Графическая грамматика.** Мотивы закреплены за функциями: крючок — бренд, поплавок — активность/ожидание, леска — время, радар — координаты, силуэт — сущность рыбы. Убраны ложная рябь неполных сигналов и дублирующие радар круги карточки лидера; неполнота теперь подчёркнута спокойной полевой меткой.
- [x] **B04 · Компонентная подпись.** `SectionHeading`, `PageHero` и `StatePanel` унифицируют заголовки, hero и состояния; семантическая иерархия `data-action` согласует CTA публичных и admin-страниц.
- [x] **B05 · Motion-система.** Смысловые микроанимации пульса, поплавка, загрузки и интерактивного отклика используют общие duration/easing-токены; декоративное движение empty-state удалено, reduced motion покрывает элементы и псевдоэлементы.
-- [ ] **B06 · Brand QA.** Проверить favicon, maskable icons, manifest, OG 1200×630, светлый/тёмный фон, 16/32 px и mobile/desktop screenshots; записать правила в [brand-system.md](brand-system.md).
+- [x] **B06 · Brand QA.** Desktop/mobile-проверка усилила каталоги atlas-hero, счётчиками, береговыми контурами и выразительными карточками. Favicon/PWA PNG перегенерированы из SVG-мастера без размытия; `any` и полнофоновые `maskable`-иконки разделены, manifest и Caddy cache обновлены, OG подтверждён как 1200×630.
- [ ] **Q01 · Документы источников.** Приложить или дать устойчивые ссылки на первичные разрешения RF4DB, RF4-STAT, RF4MAP и RF4 Posts; для каждого зафиксировать атрибуцию, точный production-лимит, срок хранения и процедуру удаления.
- [ ] **Q02 · Управляемое удаление источника.** Добавить обнаружение изменённых/удалённых опубликованных записей без отдельного частого обхода: статус, журнал решения и безопасное исключение из активности после проверки.
@@ -38,6 +38,19 @@
- [ ] **Q09 · Release-процедура.** Разделить миграционный/release-шаг и запуск приложения либо документировать выбранную стратегию отката; проверить upgrade с предыдущей ревизии на копии данных.
- [ ] **Q10 · Документальная ревизия.** После каждого пакета обновлять этот файл и README, не возвращая закрытые R/A/T-задачи в активный backlog.
+### Дополнения после ревизии PROJECT_AUDIT_2026-09-10
+
+Аудит выполнен на старой базе `13e04e6`; рекомендации ниже повторно проверены по текущей ветке. Уже реализованные или неприменимые предложения не возвращаются в backlog.
+
+- [ ] **Q11 · Декомпозиция API.** Инкрементально разделить `apps/api/app/main.py` (638 строк, 26 маршрутов) на `APIRouter` по публичному каталогу/activity, submissions и admin/community. Сначала зафиксировать OpenAPI snapshot и сохранить URL, response models, middleware и dependency-поведение без функциональных изменений.
+- [ ] **Q12 · Query-plan gate.** В рамках Q07 снять `EXPLAIN (ANALYZE, BUFFERS)` для activity, records, spot detail и public spot pages на реалистичном наборе данных. Существующие индексы миграции `0011_query_indexes` не дублировать; индекс с `fish_id`, SQL-агрегацию или materialized view добавлять только по измеренному плану и p95.
+- [ ] **Q13 · Production bootstrap в CI.** Добавить отдельный ручной/плановый job для `deploy/test-production-bootstrap.sh`, не удваивая быстрый Compose E2E на каждом коммите; сохранять diagnostics при падении.
+- [ ] **Q14 · Полная CSP.** Расширить текущую CSP (`frame-ancestors`, `base-uri`, `object-src`) до `default-src`, `script-src`, `style-src`, `img-src`, `connect-src` и `form-action`. Сначала инвентаризировать inline scripts/styles Astro, затем внедрить nonce/hash или безопасное вынесение; проверить report/admin/OG без ослабления до произвольных внешних origin.
+- [ ] **Q15 · Частичная деградация главной.** Разделить получение activity, community signals и справочников так, чтобы отказ одного источника не превращал всю главную в общий 503. Для SSR не добавлять искусственный client-side loading/optimistic UI; показывать независимые `StatePanel` и корректный HTTP/cache статус.
+- [ ] **Q16 · Контракт OpenAPI.** Генерировать OpenAPI artifact из приложения в CI и проверять осознанные изменения контракта при декомпозиции API; не поддерживать вручную редактируемую копию.
+- [ ] **Q17 · Эксплуатационные документы.** Добавить короткие ADR по Astro/FastAPI/PostgreSQL, локальному cache и стратегии scheduler, а также incident runbook для заполнения диска/PostgreSQL, отказа MinIO, зависших импортов, ошибок миграции и компрометации секретов.
+- [ ] **Q18 · Минимальная observability.** До открытой альфы определить дешёвые метрики request count/latency/error rate, глубины moderation/staging и возраста последнего успешного импорта. Формат и exporter выбрать после выбора мониторинга сервера; полноценный tracing не внедрять без подтверждённой потребности.
+
## Готовность открытой альфы — требуется сервер или внешний сервис
- [ ] Купить/подготовить Linux-сервер и подтвердить его публичный IPv4/IPv6.
@@ -55,6 +68,7 @@
- [ ] Решить по обратной связи, нужны ли OCR, Telegram, профили и уведомления о клёве.
- [ ] Рассмотреть cursor pagination при росте объёмов; текущая offset pagination достаточна для альфы.
- [ ] Рассмотреть общую инвалидацию кэша при нескольких API-процессах; сейчас действует TTL и локальная инвалидация.
+- [ ] Рассмотреть Redis только при переходе к нескольким API-процессам или после измеренного дефицита локального cache; не добавлять отдельный stateful-сервис заранее.
- [ ] Подготовить тематические обложки и индивидуальные OG для рыб/водоёмов, если страницы подтверждают поисковую ценность.
## Правила выполнения
diff --git a/docs/brand-system.md b/docs/brand-system.md
index 038026e..07331f9 100644
--- a/docs/brand-system.md
+++ b/docs/brand-system.md
@@ -50,10 +50,18 @@ CTA задаются смыслом через `data-action`: `primary` — ос
Движение объясняет состояние: пульс означает живой сигнал, поплавок — активность точки, sweep — загрузку, короткий сдвиг — отклик интерактивного элемента. Декоративные бесконечные анимации не используются. Длительности берутся из `--motion-fast`, `--motion-base`, `--motion-signal`, easing — из `--ease-out`; `prefers-reduced-motion` сводит все анимации и переходы к одному кадру.
+## Каталог как полевой атлас
+
+Каталоги используют собственную редакционную композицию: круглый тёмный seal показывает тип и объём справочника, порядковые номера создают ощущение системного атласа. Водоём обозначается абстрактным береговым контуром и точкой наблюдения, рыба — силуэтом. Эти фоновые знаки всегда декоративны, имеют низкий контраст и не заменяют текстовое название.
+
+## Brand assets и QA
+
+`favicon.svg` — мастер обычных favicon, Apple Touch и PWA `purpose: any`; PNG генерируются из него напрямую в целевом размере, а не масштабируются из малого растра. `icon-maskable.svg` — полнофоновый мастер для отдельных `purpose: maskable` 192/512 px, чтобы системная маска не открывала прозрачные углы. OG имеет ровно 1200×630 px и использует тот же тёмный водный фон, лаймовый поплавок и координатную сетку. Проверены desktop и 390 px каталоги, 32/180/192/512 px assets, светлая поверхность сайта и тёмные бренд-носители.
+
Секции данных используют `SectionHeading`: короткий uppercase eyebrow задаёт контекст, антиквенный заголовок называет сущность, необязательное описание объясняет правила, а счётчик всегда расположен справа или под текстом на мобильном. Так число результатов не конкурирует с названием раздела и одинаково читается в рекордах и полевых сигналах.
Каталоги, detail-выборки и страницы прозрачности используют `PageHero`: один контекст, один `h1`, одно короткое пояснение. Это сохраняет одинаковый вертикальный ритм и SEO-иерархию независимо от наличия данных.
## План внедрения
-Рабочие статусы B01–B06 ведутся в [ROADMAP.md](ROADMAP.md). После каждого пакета выполняются Astro check/build и адресная визуальная проверка 320/390/768/1280 px. Новые bitmap-иллюстрации не добавляются, пока существующие SVG/CSS-мотивы решают задачу без лишнего веса.
+Рабочие статусы B01–B06 ведутся в [ROADMAP.md](ROADMAP.md). После каждого пакета выполняются Astro check/build и адресная визуальная проверка ключевых desktop/mobile-композиций. Новые bitmap-иллюстрации не добавляются, пока существующие SVG/CSS-мотивы решают задачу без лишнего веса.