# Этап 3. Статьи о болезнях

Статус: согласована · 11.10.2026
План MVP: этап 3 «Болезни». Источники: [2026-10-11-stage-3-sources.md](2026-10-11-stage-3-sources.md).

## Цель

На сайте есть шесть статей о болезнях на русском и английском. Каждую статью можно читать на двух уровнях, «пациент» и «студент», внизу стоит список источников, а пока врач её не проверил, сверху висит плашка «Врачом не проверено».

## Что входит

1. Страница статьи `/<locale>/<type>/<slug>/` и список статей типа `/<locale>/<type>/`.
2. Компоненты `<Patient>` и `<Student>` для MDX и переключатель уровня.
3. Блок источников.
4. Шесть статей на русском, затем их перевод на английский.

## Что не входит

Пресеты болезней для «часов сердца» и режим «норма / болезнь» описаны в отдельной спеке [2026-10-11-stage-3-presets.md](2026-10-11-stage-3-presets.md); поле `preset` схема статьи уже принимает. Поля `structures` и `related` из плана MVP добавим в схему вместе с этапом 1, а пока схема их не принимает. Граф связей и около десяти статей по анатомии и основам ЭКГ из этапа 3 плана MVP делаем отдельной спекой после шести болезней.

## Ключевые детали

### Маршруты и fallback

- Коллекция `articles` даёт id вида `ru/disease/atrial-fibrillation`. Slug одинаковый во всех языках и пишется по-английски, чтобы переключатель языка вёл на ту же статью.
- `getStaticPaths` строит страницу для каждой локали из реестра и каждой пары `<type>/<slug>`, которая есть хотя бы в одном языке. Если статьи нет на языке страницы, берётся первый язык из цепочки `fallback`, у которого она есть. Страница тогда показывает пометку «Перевод ещё не готов» со ссылкой на раздел «Adding a language» в `CONTRIBUTING.md`. У текста статьи стоят `lang` и `dir` языка оригинала, а `<link rel="canonical">` указывает на страницу оригинала, чтобы поисковики не считали её дублем.
- Если статьи нет ни в одном языке цепочки, страница не создаётся.
- Список `/<locale>/<type>/` создаётся только для типов из `articleTypes`, у которых есть хотя бы одна статья, и показывает статьи с тем же fallback. У непереведённых пунктов стоят `lang` и `dir` оригинала. Ссылку на список болезней с главной добавляем в PR 3, когда появятся статьи.

### Уровни «пациент» и «студент»

- В MDX автор пишет `<Patient>…</Patient>` и `<Student>…</Student>` без импортов: страница передаёт компоненты через `<Content components={...} />`.
- Текст вне этих тегов виден на обоих уровнях. Так общие факты (определение, когда вызывать скорую) и заголовки разделов пишутся один раз: заголовок внутри обоих уровней без JavaScript показался бы дважды.
- `<Patient>` и `<Student>` блочные: внутри абзаца их ставить нельзя. Внутри тегов нужны пустые строки, иначе MDX не разберёт Markdown.
- Уровни не противоречат друг другу. «Студент» может добавлять патофизиологию, классификацию и критерии, но выводы у обоих уровней одинаковые. Эту формулировку переносим в `CONTRIBUTING.md` вместо «оба уровня говорят одно и то же».
- Переключатель стоит над статьёй. Скрипт в `<head>` всегда ставит атрибут `data-level` на `<html>` до первой отрисовки: сохранённый в `localStorage` уровень или «пациент» по умолчанию. Доступ к `localStorage` обёрнут в `try/catch`, потому что хранилище бывает заблокировано. Текст не мигает.
- Без JavaScript видны оба уровня с подписями «Для пациентов» и «Для студентов», а переключатель скрыт через CSS по отсутствию `html[data-level]`. С JavaScript подписи скрыты, потому что уровень и так виден по нажатой кнопке.
- Переключатель сделан из двух кнопок с `aria-pressed` в `role="group"` с подписью «Уровень объяснения». React-остров для этого не нужен, хватает маленького скрипта.

### Блок источников

- Нумерованный список внизу статьи: название, организация из `trusted-sources.yaml` (для `guideline` и `reference`), год, DOI со ссылкой на `https://doi.org/<doi>`, `url` (если есть, то вместе с DOI: у рекомендаций Минздрава это ссылка на рубрикатор), для учебника ISBN. У пунктов есть якоря `#source-<n>`.
- В тексте автор ставит `<Cite n={2} />` или `<Cite n={[1, 3]} />` после утверждения. Компонент рисует надстрочные ссылки на пункты списка, а сборка падает, если номера нет в `sources`. Так каждое утверждение можно сверить с конкретным источником.
- Названия организаций в `trusted-sources.yaml` пишем по-английски (ESC, NICE, WHO, Ministry of Health of the Russian Federation), потому что список общий для всех языков.
- Под списком строка «Источники сверены <дата>» с датой в формате локали через `Intl.DateTimeFormat` с `timeZone: 'UTC'`: YAML даёт полночь по UTC, и без этого дата сдвинулась бы на день при сборке западнее UTC.
- Плашка «Врачом не проверено» стоит над статьёй, пока `review` равно `null`.

### Как пишутся статьи

Правила из `CLAUDE.md` и спеки этапа 0 действуют полностью. Дополнительно:

- Каждый медицинский факт должен быть в одном из источников статьи и помечен `<Cite>`. Цифры (частота, пороги давления, сроки) берутся только из рекомендаций или StatPearls. Если факт не удалось сверить, его нет в статье.
- Источники берутся из таблиц спеки источников. Устаревшие документы из её списка не цитируются.
- Нет доз, схем лечения и советов «что принять». Лечение описывается на уровне подходов («препараты, которые снижают риск инсульта», «операция по замене клапана»), со ссылкой на врача.
- В каждой статье есть раздел «Когда срочно вызывать скорую», если болезнь бывает неотложной. Номера экстренных служб пишутся так же, как в подвале сайта для этого языка.
- Структура статьи: что это, почему возникает, симптомы, как ставят диагноз, как лечат (подходы), когда срочно к врачу. Уровень «студент» добавляет патофизиологию, классификацию и диагностические критерии из рекомендаций.
- Русская версия пишется первой. Английская версия переводит её смысл, источники те же.
- В каждом PR со статьями независимый субагент сверяет каждое число, порог и утверждение с помеченным `<Cite>` источником и проверяет, что нет доз и советов «что принять». Это главная страховка, пока нет врачей-рецензентов. Для английских версий проверка та же.

## Разбиение на PR

| PR | Содержание | Готово, когда |
|---|---|---|
| 1 | Эта спека | Спека в `main` |
| 2 | Страница статьи и список, `Patient`/`Student` с переключателем, `Cite`, блок источников, пометка о переводе, строки в `messages/<locale>.json`, тесты | Тесты компонентов зелёные, сборка с тестовыми статьями проходит |
| 3 | Статьи на русском: фибрилляция предсердий, артериальная гипертензия, сердечная недостаточность | Сборка зелёная, у каждой статьи не меньше двух источников и хотя бы одна рекомендация |
| 4 | Статьи на русском: ИБС и инфаркт миокарда, аортальный стеноз, ДМПП | То же |
| 5 | Английские версии шести статей | Страницы `/en/disease/*` открываются без пометки о переводе |

### Проверки

- До первой настоящей статьи страницы проверяются на тестовых статьях из `apps/web/test/fixtures/site-content/`. С `HA_TEST_CONTENT=1` коллекция читает их вместо `content/`, как `HA_TEST_LOCALES=1` подключает тестовую локаль. В продакшен-сборку они не попадают.
- e2e на Playwright появляется в PR 6 этапа 0. Когда он в `main`, добавляем сценарии: переключение уровня без мигания и с запоминанием, клавиатура, вид без JavaScript, страница с fallback, RTL-локаль, axe. До этого PR проверяются unit-тестами компонентов, сборкой и ручным просмотром собранной страницы.

## Риски

- Без рецензентов ошибка в статье может дойти до читателя. Защиты три: только проверенные источники, плашка с кнопкой «Сообщить об ошибке» и правило «нет источника, нет факта».
- Рекомендации Минздрава по нескольким болезням должны обновиться в 2026 году. Перед мерджем каждой статьи автор проверяет рубрикатор.
