/* ========================================================= Данные клинических кейсов n:true — назначить/выбрать нужно n:false — лишнее/противопоказано (выбирать не надо) voice: pitch/rate/preferMale/breath — параметры озвучки ========================================================= */ const CASES = [ { id:"stemi", theme:"aks", themeLabel:"ОКС / инфаркт", badge:"b-aks", title:"Острый инфаркт миокарда с подъёмом ST", severity:3, patient:{avatar:"👨‍🦰", name:"Пациент А.", age:"58 лет, мужчина", ctx:"Поступил по скорой, ~2 ч от начала боли", voice:{pitch:0.85, rate:0.95, preferMale:true, breath:230, volume:1}}, complaint:"Доктор, часа два назад как прижало за грудиной. Давит сильно, будто слон на грудь сел. Отдаёт в левую руку и в челюсть. Дышать тяжело, бросило в холодный пот, стало страшно. Таблетку нитроглицерина под язык клал — не отпускает.", anamnesis:[ "Боль возникла в покое, после эмоционального стресса на работе.", "Курит 30 лет по пачке в день. Артериальная гипертензия 8 лет.", "Отец умер от инфаркта миокарда в 60 лет.", "Ранее подобных приступов не было. Регулярной терапии не получает." ], objective:{ vitals:{hr:"102", bp:"150/95", rr:"22", spo2:"93%"}, exam:{general:"Кожа бледная, влажная, холодный пот. Пациент тревожен.", heart:"Тоны приглушены, тахикардия, ритм правильный.", lungs:"В нижних отделах с обеих сторон — мелкопузырчатые незвучные хрипы.", other:"Отёков нет."} }, investigations:[ {t:"ЭКГ в 12 отведениях", n:true}, {t:"Тропонин I/T (количественно)", n:true}, {t:"Общий анализ крови", n:true}, {t:"Коагулограмма (МНО, АЧТВ, фибриноген)", n:true}, {t:"Креатинин, мочевина, электролиты, глюкоза", n:true}, {t:"Рентгенография органов грудной клетки", n:true}, {t:"Эхокардиография", n:true}, {t:"Компьютерная томография головного мозга", n:false}, {t:"Люмбальная пункция", n:false}, {t:"Фиброгастроскопия", n:false} ], medications:[ {t:"Аспирин 250–325 мг разжевать", n:true}, {t:"Ингибитор P2Y12 (тикагрелор/прасугрел) нагрузочно", n:true}, {t:"Антикоагулянт (эноксапарин / НФГ)", n:true}, {t:"Морфин при выраженном болевом синдроме", n:true}, {t:"Кислород при SpO₂ < 90%", n:true}, {t:"Нитраты при боли и АД > 100", n:true}, {t:"Бета-блокатор (после стабилизации)", n:true}, {t:"Статин в высокой дозе", n:true}, {t:"Антибиотики широкого спектра", n:false}, {t:"Нестероидные противовоспалительные (ибупрофен)", n:false} ], diagnosis:{ options:[ {t:"Острый инфаркт миокарда с подъёмом ST (STEMI), передне-перегородочный", c:true}, {t:"Острый перикардит", c:false}, {t:"Тромбоэмболия лёгочной артерии", c:false}, {t:"Расслоение аорты", c:false}, {t:"Нестабильная стенокардия", c:false} ], explanation:"Типичный ангинозный синдром, подъём ST в передних отведениях и реципрокная депрессия — STEMI. Время = гибнущий миокард, показана экстренная реперфузия." }, emergency:{ intro:"Пациент в острейшей стадии. У вас 60 секунд, чтобы оказать экстренную помощь.", actions:[ {t:"Вызвать реанимационную бригаду, активировать STEMI-маршрут (door-to-balloon ≤ 90 мин)", n:true}, {t:"Двойная антитромбоцитарная терапия (аспирин + P2Y12)", n:true}, {t:"Ввести антикоагулянт", n:true}, {t:"Обеспечить венозный доступ и мониторинг ЭКГ", n:true}, {t:"Обезболивание (морфин) + нитраты при АД > 100", n:true}, {t:"Принять решение о реперфузии (ЧКВ / тромболизис)", n:true}, {t:"Ввести антибиотики широкого спектра", n:false}, {t:"Назначить НПВС", n:false}, {t:"Выполнить люмбальную пункцию", n:false} ] }, extra:"ЭКГ: подъём ST в I, aVL, V1–V4; реципрокная депрессия в II, III, aVF. Тропонин I повышен.", protocol:"Клинический протокол МЗ РК «Острый коронарный синдром»", protocolNote:"Немедленная реперфузия: первичное ЧКВ (цель door-to-balloon ≤ 90 мин); при недоступности ЧКВ ≤ 120 мин — фибринолитическая терапия (алтеплаза/тенектеплаза), в т.ч. на догоспитальном этапе. Обязательны: ацетилсалициловая кислота + ингибитор P2Y12 (клопидогрел/тикагрелор), антикоагулянт, морфин при боли, кислород при SpO₂ < 90%.", keyPoints:[ {t:"good", x:"Время = миокард: чем раньше реперфузия, тем больше спасённой мышцы."}, {t:"bad", x:"Не откладывайте реперфузию, ожидая тропонин — ЭКГ и клиника достаточны для STEMI."}, {t:"bad", x:"НПВС противопоказаны при ОКС — усиливают риск разрыва и кровотечения."} ] }, { id:"nstemi", theme:"aks", themeLabel:"ОКС / инфаркт", badge:"b-aks", title:"Острый коронарный синдром без подъёма ST", severity:2, patient:{avatar:"👩‍🦳", name:"Пациентка Б.", age:"65 лет, женщина", ctx:"Поступила через сутки от начала симптомов", voice:{pitch:1.12, rate:0.96, preferMale:false, breath:240, volume:1}}, complaint:"Вчера ночью проснулась от боли за грудиной. Давящая, минут двадцать держалась, отдала в спину. Сегодня повторилось дважды, уже при небольшой нагрузке — просто по квартире походила. Раньше такого никогда не было. Как-то страшно, думаю, это сердце.", anamnesis:[ "Сахарный диабет 2 типа 12 лет, ожирение, гиперхолестеринемия.", "Гипертония, препараты принимает нерегулярно.", "Боли провоцируются нагрузкой, купируются в покое за 10–15 мин (прогрессирующая стенокардия).", "За последний месяц снизилась толерантность к нагрузке." ], objective:{ vitals:{hr:"88", bp:"140/85", rr:"18", spo2:"96%"}, exam:{general:"Состояние средней тяжести. Кожные покровы обычной окраски. ИМТ 32.", heart:"Тоны ритмичные, приглушены. Шумов нет.", lungs:"Дыхание везикулярное, хрипов нет.", other:"Отёков нет."} }, investigations:[ {t:"ЭКГ в 12 отведениях (повторно)", n:true}, {t:"Тропонин с оценкой динамики через 3–6 ч", n:true}, {t:"Общий анализ крови, биохимия, коагулограмма", n:true}, {t:"Глюкоза, липидограмма", n:true}, {t:"Креатинин, электролиты", n:true}, {t:"Эхокардиография", n:true}, {t:"Оценка риска по шкале GRACE", n:true}, {t:"Компьютерная томография грудной клетки с контрастом", n:false}, {t:"Люмбальная пункция", n:false}, {t:"Фиброгастроскопия", n:false} ], medications:[ {t:"Аспирин", n:true}, {t:"Ингибитор P2Y12 (тикагрелор/клопидогрел)", n:true}, {t:"Антикоагулянт (фондапаринукс/эноксапарин/НФГ)", n:true}, {t:"Бета-блокатор (при отсутствии противопоказаний)", n:true}, {t:"Статин в высокой дозе", n:true}, {t:"Нитраты при боли", n:true}, {t:"Морфин при сильной боли (осторожно)", n:true}, {t:"Тромболизис (алтеплаза)", n:false}, {t:"Нестероидные противовоспалительные", n:false}, {t:"Антибиотики", n:false} ], diagnosis:{ options:[ {t:"Острый коронарный синдром без подъёма ST (NSTEMI), высокий риск", c:true}, {t:"Стабильная стенокардия напряжения", c:false}, {t:"Костохондрит", c:false}, {t:"Гастроэзофагеальная рефлюксная болезнь", c:false}, {t:"Острый перикардит", c:false} ], explanation:"Депрессия ST + подъём тропонина + высокий риск по GRACE = NSTEMI. Тромболизис при NSTEMI НЕ показан — показана ранняя инвазивная стратегия." }, emergency:{ intro:"Пациентка с NSTEMI высокого риска. У вас 60 секунд на оказание экстренной помощи.", actions:[ {t:"Двойная антитромбоцитарная терапия + антикоагулянт", n:true}, {t:"Обеспечить мониторинг и венозный доступ", n:true}, {t:"Провести стратификацию риска (GRACE)", n:true}, {t:"Принять решение о ранней инвазивной стратегии (ЧКВ ≤ 24 ч)", n:true}, {t:"Нитраты/обезболивание при боли", n:true}, {t:"Кислород при SpO₂ < 90%", n:true}, {t:"Провести тромболизис алтеплазой", n:false}, {t:"Ввести антибиотики", n:false}, {t:"Назначить НПВС", n:false} ] }, extra:"ЭКГ: горизонтальная депрессия ST в V4–V6, инверсия T. Тропонин повышен с динамикой. GRACE — высокий риск.", protocol:"Клинический протокол МЗ РК «Острый коронарный синдром»", protocolNote:"При ОКС без подъёма ST тромболизис НЕ показан. Стратификация риска (шкала GRACE) определяет раннюю инвазивную стратегию (ЧКВ ≤ 24 ч при высоком риске). Двойная антитромбоцитарная терапия + антикоагулянт, статины, бета-блокатор при отсутствии противопоказаний.", keyPoints:[ {t:"good", x:"Женщины и диабетики часто имеют атипичную клинику — не сбрасывайте со счетов."}, {t:"bad", x:"Не ставьте «стабильную стенокардию» при недавней прогрессии симптомов — это ОКС."}, {t:"bad", x:"Тромболизис при NSTEMI не показан — это частая и опасная ошибка."} ] }, { id:"asthma", theme:"odn", themeLabel:"ОДН / астма / ХОБЛ", badge:"b-odn", title:"Острый тяжёлый приступ бронхиальной астмы", severity:3, patient:{avatar:"👩", name:"Пациентка В.", age:"24 года, женщина", ctx:"Доставлена СМП, одышка 1 час", voice:{pitch:1.25, rate:1.05, preferMale:false, breath:420, volume:0.9}}, complaint:"Не могу... выдохнуть... свистит вся грудь... началось час назад... ингалятор уже третий раз пшикала — не помогает... тяжело... страшно задохнуться.", anamnesis:[ "Бронхиальная астма с детства, базисную терапию (ИГКС) принимает нерегулярно.", "Неделю назад перенесла ОРВИ с кашлем.", "За последние дни использовала сальбутамол более 10 раз в сутки.", "Аллергия на пыльцу, домашнюю пыль." ], objective:{ vitals:{hr:"120", bp:"135/85", rr:"30", spo2:"91%"}, exam:{general:"Положение ортопноэ, опирается на руки. Речь прерывистая, отдельные фразы. Работа вспомогательной мускулатуры.", heart:"Тахикардия.", lungs:"Выдох резко удлинён, свистящие хрипы по всем полям.", other:"Втянутые межрёберные промежутки, парадоксальный пульс."} }, investigations:[ {t:"Пульсоксиметрия", n:true}, {t:"Пикфлоуметрия (пиковая скорость выдоха)", n:true}, {t:"Газометрия артериальной крови (при тяжёлом течении)", n:true}, {t:"ЭКГ (исключить аритмию/ишемию)", n:true}, {t:"Рентгенография органов грудной клетки", n:true}, {t:"Общий анализ крови", n:true}, {t:"Компьютерная томография головного мозга", n:false}, {t:"Люмбальная пункция", n:false}, {t:"Бронхоскопия", n:false}, {t:"Эхокардиография (первоочерёдно)", n:false} ], medications:[ {t:"Сальбутамол через небулайзер (повторно/непрерывно)", n:true}, {t:"Ипратропия бромид через небулайзер", n:true}, {t:"Системные ГКС (преднизолон/гидрокортизон)", n:true}, {t:"Магния сульфат в/в при тяжёлом приступе", n:true}, {t:"Кислород (до SpO₂ 94–98%)", n:true}, {t:"Седативные препараты (диазепам)", n:false}, {t:"Опиоиды (морфин)", n:false}, {t:"Бета-блокаторы (пропранолол)", n:false}, {t:"Нестероидные противовоспалительные", n:false} ], diagnosis:{ options:[ {t:"Острый тяжёлый приступ бронхиальной астмы (угроза астматического статуса)", c:true}, {t:"Паническая атака", c:false}, {t:"Обострение ХОБЛ", c:false}, {t:"Тромбоэмболия лёгочной артерии", c:false}, {t:"Инородное тело дыхательных путей", c:false} ], explanation:"Экспираторная одышка, свистящие хрипы, SpO₂ 91%, ПСВ < 50% — острый тяжёлый приступ астмы. Основа: бронходилататоры и системные ГКС." }, emergency:{ intro:"Пациентка задыхается. У вас 60 секунд оказать экстренную помощь.", actions:[ {t:"Кислород до SpO₂ 94–98%", n:true}, {t:"Сальбутамол небулайзер 5 мг, повторно", n:true}, {t:"Ипратропия бромид небулайзер", n:true}, {t:"Системные ГКС как можно раньше", n:true}, {t:"Магния сульфат в/в", n:true}, {t:"Мониторинг, подготовка к ИВЛ", n:true}, {t:"Ввести седативное (диазепам)", n:false}, {t:"Ввести морфин", n:false}, {t:"Дать бета-блокатор", n:false} ] }, extra:"ПСВ < 50% от должного. При жизнеугрожающем статусе — «немое лёгкое» (исчезновение хрипов при нарастающей ОДН).", protocol:"Клинический протокол МЗ РК «Бронхиальная астма»", protocolNote:"Острый тяжёлый приступ: кислород (SpO₂ 94–98%), β2-агонисты короткого действия (сальбутамол) через небулайзер повторно/непрерывно, ипратропия бромид, системные ГКС как можно раньше, магния сульфат в/в. Седативные и опиоиды противопоказаны (угнетение дыхания). При жизнеугрожающем статусе — ИВЛ.", keyPoints:[ {t:"bad", x:"Никогда не давайте седативные/опиоиды при астме — это угнетает дыхание и ведёт к остановке."}, {t:"good", x:"Системные ГКС работают через 4–6 ч, поэтому вводите их сразу, не ждите ухудшения."}, {t:"bad", x:"«Немое лёгкое» = плохо: хрипы исчезли из-за отсутствия резерва — предлетальное состояние."} ] }, { id:"copd", theme:"odn", themeLabel:"ОДН / астма / ХОБЛ", badge:"b-odn", title:"Обострение ХОБЛ с острой дыхательной недостаточностью", severity:2, patient:{avatar:"👴", name:"Пациент Г.", age:"68 лет, мужчина", ctx:"Направлен врачом общей практики", voice:{pitch:0.82, rate:0.9, preferMale:true, breath:300, volume:1}}, complaint:"Уже неделю кашель усилился, мокрота стала густая, жёлто-зелёная. Дышать совсем тяжело, особенно со вчера. Раньше хоть по дому ходил, а сейчас до туалета не дойти — задыхаюсь. Сплю полусидя, две подушки подкладываю. Лихорадит три дня.", anamnesis:[ "ХОБЛ диагностирована 10 лет назад. Курит 40 лет (40 пачко-лет).", "Последние 3 дня — лихорадка до 38.5 °C, гнойная мокрота.", "Целевая SpO₂ 88–92% (компенсированная гиперкапния).", "Дома — ипратропия бромид/фенотерол." ], objective:{ vitals:{hr:"110", bp:"160/90", rr:"28", spo2:"86%", temp:"38.3°C"}, exam:{general:"Состояние тяжёлое. Цианоз губ, акроцианоз.", heart:"Громкий II тон над лёгочной артерией. Тахикардия.", lungs:"Грудная клетка бочкообразная, коробочный звук. Сухие свистящие хрипы.", other:"Периферические отёки голеней — cor pulmonale."} }, investigations:[ {t:"Пульсоксиметрия", n:true}, {t:"Газометрия артериальной крови", n:true}, {t:"Рентгенография органов грудной клетки", n:true}, {t:"ЭКГ", n:true}, {t:"Общий анализ крови, СРБ, прокальцитонин", n:true}, {t:"Мокрота на флору и чувствительность", n:true}, {t:"Биохимия, электролиты", n:true}, {t:"Компьютерная томография головного мозга", n:false}, {t:"Люмбальная пункция", n:false}, {t:"Бронхоскопия", n:false} ], medications:[ {t:"Бронходилататоры небулайзер (ипратропий + фенотерол)", n:true}, {t:"Системные ГКС (преднизолон 30–40 мг)", n:true}, {t:"Антибиотики при гнойной мокроте/лихорадке", n:true}, {t:"Контролируемая О₂-терапия (цель SpO₂ 88–92%)", n:true}, {t:"NIV/BiPAP при тяжёлой гиперкапнической ОДН", n:true}, {t:"Максимальный O₂ до SpO₂ 99–100%", n:false}, {t:"Седативные препараты", n:false}, {t:"Наркотические анальгетики", n:false} ], diagnosis:{ options:[ {t:"Обострение ХОБЛ, ОДН 2 типа (гиперкапническая)", c:true}, {t:"Острый приступ бронхиальной астмы", c:false}, {t:"Отёк лёгких", c:false}, {t:"Внебольничная пневмония без ОДН", c:false}, {t:"Тромбоэмболия лёгочной артерии", c:false} ], explanation:"ХОБЛ в анамнезе, гнойная мокрота, лихорадка, SpO₂ 86%, гиперкапния по газометрии — обострение ХОБЛ с ОДН 2 типа. Кислород титровать до 88–92%." }, emergency:{ intro:"Пациент с гиперкапнической ОДН. У вас 60 секунд оказать экстренную помощь.", actions:[ {t:"Контролируемая О₂-терапия (Venturi 24–28%, цель SpO₂ 88–92%)", n:true}, {t:"Бронходилататоры через небулайзер", n:true}, {t:"Системные ГКС", n:true}, {t:"Антибиотики при признаках инфекции", n:true}, {t:"NIV/BiPAP при pH<7.35 / гиперкапнии", n:true}, {t:"Мониторинг газометрии", n:true}, {t:"Дать максимальный O₂ до SpO₂ 99–100%", n:false}, {t:"Седация для комфорта", n:false}, {t:"Сразу интубировать без попытки NIV", n:false} ] }, extra:"Газометрия: PaO₂ 52 ↓, PaCO₂ 68 ↑, pH 7.31 (дыхательный ацидоз). Гиперкапническая ОДН 2 типа.", protocol:"Клинический протокол МЗ РК «Хроническая обструктивная болезнь легких»", protocolNote:"Контролируемая оксигенотерапия (целевой SpO₂ 88–92%) — избыток O₂ вызывает CO₂-наркоз. Бронходилататоры через небулайзер, системные ГКС, антибиотики при признаках инфекции. При гиперкапнической ОДН (pH < 7.35) — неинвазивная вентиляция лёгких (NIV).", keyPoints:[ {t:"bad", x:"Не давайте 100% O₂ «на всякий случай» при ХОБЛ — это ведёт к CO₂-наркозу."}, {t:"good", x:"NIV снижает потребность в интубации и летальность при обострении ХОБЛ с гиперкапнией."}, {t:"good", x:"Целевой SpO₂ для пациента с ХОБЛ — 88–92%, а не 94–98%."} ] }, { id:"hemoshock", theme:"shock", themeLabel:"Шок и травма", badge:"b-shock", title:"Травматический геморрагический шок", severity:3, patient:{avatar:"🧑", name:"Пациент Д.", age:"30 лет, мужчина", ctx:"ДТП, доставлен бригадой СМП", voice:{pitch:0.92, rate:0.85, preferMale:true, breath:520, volume:0.7}}, complaint:"Всё кружится... холодно почему-то... пить очень хочу... ногу не чувствую... где я... ничего не вижу ясно...", anamnesis:[ "ДТП: лобовое столкновение, пациент за рулём, не пристёгнут.", "Закрытый перелом бедра, подозрение на перелом таза.", "Наружное кровотечение из раны бедра, обширная гематома.", "Время от травмы до поступления ~40 минут." ], objective:{ vitals:{hr:"130", bp:"80/40", rr:"28", spo2:"95%"}, exam:{general:"Сознание оглушение (GCS 13). Резкая бледность, холодный липкий пот.", heart:"Тоны глухие, нитевидный пульс.", lungs:"Дыхание проводится по всем полям, хрипов нет.", other:"Левое бедро деформировано, укорочено, обширная гематома. Кровь на одежде."} }, investigations:[ {t:"Группа крови, Rh, кросс-матч", n:true}, {t:"Общий анализ крови (Hb, Ht)", n:true}, {t:"Коагулограмма", n:true}, {t:"Лактат, газометрия (дефицит базы)", n:true}, {t:"Креатинин, электролиты", n:true}, {t:"Рентгенография органов грудной клетки", n:true}, {t:"УЗИ FAST (поиск внутреннего кровотечения)", n:true}, {t:"Люмбальная пункция", n:false}, {t:"Фиброгастроскопия", n:false}, {t:"ЭЭГ", n:false} ], medications:[ {t:"Кристаллоиды (солевые растворы)", n:true}, {t:"Эритроцитарная масса", n:true}, {t:"Свежезамороженная плазма", n:true}, {t:"Тромбоцитный концентрат при коагулопатии", n:true}, {t:"Транексамовая кислота", n:true}, {t:"Норадреналин временно при рефрактерном шоке", n:true}, {t:"Антикоагулянты (гепарин/эноксапарин)", n:false}, {t:"Тромболизис", n:false}, {t:"Диуретики", n:false} ], diagnosis:{ options:[ {t:"Травматический геморрагический шок III–IV степени", c:true}, {t:"Кардиогенный шок", c:false}, {t:"Септический шок", c:false}, {t:"Анафилактический шок", c:false}, {t:"Нейрогенный шок", c:false} ], explanation:"Травма, массивная кровопотеря, ЧСС 130, АД 80/40, шоковый индекс 1.6, бледность, олигурия — геморрагический шок III–IV. Приоритет — остановка кровотечения и восстановление ОЦК." }, emergency:{ intro:"Пациент в геморрагическом шоке, нестабилен. У вас 60 секунд на оказание экстренной помощи.", actions:[ {t:"Немедленная остановка наружного кровотечения (прямое давление/жгут)", n:true}, {t:"ABCDE, иммобилизация шейного отдела", n:true}, {t:"Два крупных венозных доступа", n:true}, {t:"Согревание пациента (борьба с гипотермией)", n:true}, {t:"Инфузия кристаллоидов + ранняя гемотрансфузия (1:1:1)", n:true}, {t:"Тазовый бинт при переломе таза", n:true}, {t:"Экстренный хирургический гемостаз", n:true}, {t:"Начать антикоагулянтную терапию", n:false}, {t:"Провести тромболизис", n:false} ] }, extra:"Класс шока III–IV (потеря > 30% ОЦК). Лактат повышен, дефицит базы > −6 ммоль/л. Шоковый индекс 1.6.", protocol:"Клинические протоколы МЗ РК «Шок», «Острое кровотечение»", protocolNote:"Приоритет — остановка кровотечения (damage control). ABCDE, иммобилизация. Сбалансированная инфузия кристаллоидов + ранняя гемотрансфузия (эритроциты/плазма/тромбоциты), транексамовая кислота. Борьба с триадой смерти (гипотермия, ацидоз, коагулопатия). Экстренный хирургический гемостаз. Антикоагулянты и тромболизис противопоказаны.", keyPoints:[ {t:"bad", x:"Не тратьте время на полное обследование при нестабильной гемодинамике — пациент гибнет от кровопотери."}, {t:"good", x:"Боритесь с триадой смерти: гипотермия + ацидоз + коагулопатия."}, {t:"bad", x:"Избыток кристаллоидов до гемотрансфузии усугубляет коагулопатию и гипотермию."} ] }, { id:"sepsis", theme:"shock", themeLabel:"Шок и травма", badge:"b-shock", title:"Септический шок", severity:3, patient:{avatar:"🧔", name:"Пациент Е.", age:"55 лет, мужчина", ctx:"Направлен из приёмного покоя", voice:{pitch:0.9, rate:0.92, preferMale:true, breath:340, volume:0.9}}, complaint:"Три дня назад начался озноб, температура под тридцать девять, кашель с мокротой. Со вчера стало хуже — давление упало, в себя прихожу с трудом. Раньше ноги никогда так не отекали, а сейчас задыхаюсь. Пить хочу, но тошнит.", anamnesis:[ "Сахарный диабет 2 типа, плохо компенсирован.", "Заболел как ОРВИ 5 дней назад, затем кашель с гнойной мокротой.", "Самостоятельно принимал парацетамол и «антибиотик из аптечки».", "Аллергия на пенициллин." ], objective:{ vitals:{hr:"125", bp:"85/50", rr:"26", spo2:"92%", temp:"38.9°C"}, exam:{general:"Состояние тяжёлое. Кожа горячая, пятнистая сыпь. Сознание спутано.", heart:"Тоны глухие, тахикардия. САД 85 после инфузии.", lungs:"В правой нижней доле — ослабленное дыхание, влажные хрипы, притупление.", other:"Олигурия. Капиллярное наполнение > 3 сек."} }, investigations:[ {t:"Гемокультуры (до антибиотиков!)", n:true}, {t:"Лактат", n:true}, {t:"Общий анализ крови с формулой, СРБ, прокальцитонин", n:true}, {t:"Культура мокроты и мочи", n:true}, {t:"Газометрия, коагулограмма (ДВС-скрининг)", n:true}, {t:"Креатинин, печёночные пробы, электролиты", n:true}, {t:"Рентгенография органов грудной клетки", n:true}, {t:"ЭКГ", n:true}, {t:"Люмбальная пункция", n:false}, {t:"Фиброгастроскопия", n:false} ], medications:[ {t:"Антибиотики широкого спектра в/в в 1-й час", n:true}, {t:"Кристаллоиды 30 мл/кг", n:true}, {t:"Норадреналин (вазопрессор 1-й линии)", n:true}, {t:"Вазопрессин (2-я линия)", n:true}, {t:"Инсулин (контроль гликемии)", n:true}, {t:"Гидрокортизон при рефрактерном шоке", n:true}, {t:"Глюкокортикоиды рутинно в высоких дозах", n:false}, {t:"Седативные препараты", n:false}, {t:"Диуретики в начале терапии", n:false} ], diagnosis:{ options:[ {t:"Септический шок (на фоне внебольничной пневмонии)", c:true}, {t:"Гиповолемический шок", c:false}, {t:"Кардиогенный шок", c:false}, {t:"Анафилактический шок", c:false}, {t:"Тромбоэмболия лёгочной артерии", c:false} ], explanation:"Инфекционный очаг (пневмония), лихорадка, гипотензия после инфузии, лактат 4.2, олигурия, спутанность сознания — септический шок. Выполняйте hour-1 bundle." }, emergency:{ intro:"Пациент в септическом шоке. У вас 60 секунд выполнить Sepsis hour-1 bundle.", actions:[ {t:"Измерить лактат", n:true}, {t:"Взять гемокультуры до антибиотиков", n:true}, {t:"Ввести антибиотики широкого спектра в/в в 1-й час", n:true}, {t:"Инфузия кристаллоидов 30 мл/кг", n:true}, {t:"Норадреналин при стойкой гипотензии (САД ≥ 65)", n:true}, {t:"Мониторинг диуреза, АД, лактата", n:true}, {t:"Ждать посевов 24 ч перед антибиотиками", n:false}, {t:"Только вазопрессоры без инфузии", n:false}, {t:"Только жаропонижающие и наблюдение", n:false} ] }, extra:"Лактат 4.2 ↑. Лейкоциты 22×10⁹/л, сдвиг. Креатинин повышен. Рентген: инфильтрация нижней доли справа.", protocol:"Клинический протокол МЗ РК «Сепсис и септический шок»", protocolNote:"Часовой пакет (hour-1 bundle): измерить лактат, взять гемокультуры до антибиотиков, ввести антибиотики широкого спектра в/в в 1-й час, инфузия кристаллоидов 30 мл/кг, норадреналин при стойкой гипотензии (целевое САД ≥ 65 мм рт.ст.). Контроль источника инфекции.", keyPoints:[ {t:"bad", x:"Не ждите посевов, чтобы начать антибиотики — каждый час задержки повышает летальность."}, {t:"good", x:"Норадреналин — препарат первого ряда при септическом шоке после адекватной инфузии."}, {t:"good", x:"Контроль источника инфекции так же важен, как антибиотики."} ] } ]; /* ========================================================= Озвучка (Web Speech API) — человеческий голос ========================================================= */ let voices=[]; function loadVoices(){ if("speechSynthesis" in window) voices=window.speechSynthesis.getVoices(); } if("speechSynthesis" in window){ loadVoices(); window.speechSynthesis.onvoiceschanged=loadVoices; } function pickVoice(preferMale){ const ru = voices.filter(v=>v.lang && v.lang.toLowerCase().startsWith("ru")); if(!ru.length) return null; const maleRe=/pavel|yuri|maxim|dmitri|alexander|male|мужской/i; const femaleRe=/irina|katya|milena|tatiana|anna|maria|female|женский/i; if(preferMale){ const m=ru.find(v=>maleRe.test(v.name)); if(m) return m; } else { const f=ru.find(v=>femaleRe.test(v.name)); if(f) return f; } // предпочесть нейросетевые/качественные движки const q=ru.find(v=>/google|yandex|microsoft|neural|premium/i.test(v.name)); if(q) return q; return ru[0]; } let speakState={playing:false, queue:[], idx:0, timer:null, btn:null, avatar:null, opts:null}; function stopSpeak(){ if("speechSynthesis" in window) window.speechSynthesis.cancel(); if(speakState.timer){ clearTimeout(speakState.timer); speakState.timer=null; } speakState.playing=false; if(speakState.btn){ speakState.btn.classList.remove("is-playing"); speakState.btn.textContent="▶ Прослушать"; } if(speakState.avatar) speakState.avatar.classList.remove("is-speaking"); speakState.queue=[]; speakState.idx=0; } function splitPhrases(text){ return text .split(/(?<=[.!?…])\s+|(?<=…)\s*/) .map(s=>s.trim()) .filter(Boolean); } function speakPatient(text, opts, btn, avatar){ const synth=window.speechSynthesis; if(!synth){ alert("Озвучка не поддерживается этим браузером."); return; } if(speakState.playing){ stopSpeak(); return; } stopSpeak(); const phrases=splitPhrases(text); if(!phrases.length) return; speakState.queue=phrases; speakState.idx=0; speakState.btn=btn; speakState.avatar=avatar; speakState.opts=opts; speakState.playing=true; btn.classList.add("is-playing"); btn.textContent="⏹ Остановить"; if(avatar) avatar.classList.add("is-speaking"); playNext(); } function playNext(){ const synth=window.speechSynthesis; if(!speakState.playing || speakState.idx>=speakState.queue.length){ stopSpeak(); return; } const phrase=speakState.queue[speakState.idx]; const opts=speakState.opts||{}; const u=new SpeechSynthesisUtterance(phrase); u.lang="ru-RU"; const v=pickVoice(opts.preferMale); if(v) u.voice=v; u.rate=opts.rate!=null?opts.rate:0.97; u.pitch=opts.pitch!=null?opts.pitch:1; u.volume=opts.volume!=null?opts.volume:1; u.onend=()=>{ if(!speakState.playing) return; speakState.idx++; if(speakState.idx>=speakState.queue.length){ stopSpeak(); return; } speakState.timer=setTimeout(playNext, opts.breath!=null?opts.breath:230); }; u.onerror=()=>{ stopSpeak(); }; synth.speak(u); } window.addEventListener("beforeunload",()=>{ if("speechSynthesis" in window) window.speechSynthesis.cancel(); }); /* ========================================================= Рендер карточек кейсов + фильтры ========================================================= */ const grid=document.getElementById("caseGrid"); function sevDots(n){let s="";for(let i=0;i<3;i++)s+=``;return s;} function renderCards(filter="all"){ grid.innerHTML=""; CASES.filter(c=>filter==="all"||c.theme===filter).forEach(c=>{ const el=document.createElement("article"); el.className=`case-card t-${c.theme}`; el.innerHTML=`
${c.patient.avatar}

${c.title}

${c.patient.name}, ${c.patient.age.split(",")[0]}

Пациент сам расскажет жалобы голосом. Назначьте обследования и лечение, поставьте диагноз, окажите экстренную помощь на время.

${sevDots(c.severity)}
`; el.querySelector(".case-btn").addEventListener("click",e=>{e.stopPropagation(); startExam("single",c.id);}); grid.appendChild(el); }); } document.getElementById("filters").addEventListener("click",e=>{ const b=e.target.closest(".filter"); if(!b) return; document.querySelectorAll(".filter").forEach(f=>f.classList.remove("is-active")); b.classList.add("is-active"); renderCards(b.dataset.filter); }); /* ========================================================= Экзамен (полноэкранный wizard) ========================================================= */ const exam=document.getElementById("exam"); const examBody=document.getElementById("examBody"); const examProgressLabel=document.getElementById("examProgressLabel"); const examDots=document.getElementById("examDots"); const stageTrack=document.getElementById("stageTrack"); const STAGES=[ {key:"patient", label:"Пациент"}, {key:"investigations", label:"Обследование"}, {key:"medications", label:"Лечение"}, {key:"diagnosis", label:"Диагноз"}, {key:"emergency", label:"Помощь ⏱"}, {key:"result", label:"Результат"} ]; const STEP_TAGS=["Пациент","Шаг 1","Шаг 2","Шаг 3","Шаг 4","Результат"]; let state=null; let timerHandle=null; function startExam(mode, caseId){ const queue = mode==="full" ? CASES.map(c=>c.id) : [caseId]; state={ mode, queue, qIdx:0, stage:0, answers:{}, caseScores:[], emergencyAuto:false }; exam.hidden=false; document.body.style.overflow="hidden"; loadCase(); } function closeExam(){ stopSpeak(); if(timerHandle){ clearInterval(timerHandle); timerHandle=null; } exam.hidden=true; document.body.style.overflow=""; state=null; } function curCase(){ return CASES.find(c=>c.id===state.queue[state.qIdx]); } function loadCase(){ stopTimer(); state.stage=0; state.answers={ investigations:new Set(), medications:new Set(), diagnosis:null, emergency:new Set(), submitted:{i:false,m:false,d:false,e:false} }; state.emergencyAuto=false; stopSpeak(); renderExamBar(); renderStageTrack(); renderPatient(); } function renderExamBar(){ examProgressLabel.textContent=`Кейс ${state.qIdx+1} из ${state.queue.length}`; examDots.innerHTML=state.queue.map((_,i)=>{ const done=i`; }).join(""); } function renderStageTrack(){ stageTrack.innerHTML=STAGES.map((s,i)=>{ let cls=""; if(i${mark}${s.label}${i→':""}`; }).join(""); } /* ---- SVG кольцо ---- */ function ring(pct, size, stroke, color, track, num){ const r=(size-stroke)/2, c=2*Math.PI*r, off=c*(1-Math.max(0,Math.min(100,pct))/100); return `
${num}
`; } /* ---- Блок пациента ---- */ function patientBlock(c){ return `
${c.patient.avatar}

${c.title}

${c.patient.name} · ${c.patient.age} · ${c.patient.ctx}
${c.themeLabel}
🔊 Жалобы пациентаВключите звук и нажмите — пациент говорит голосом
«${c.complaint}»
`; } function vitalsHTML(v){ let extra=""; if(v.temp) extra=`
${v.temp}Температура
`; return `
${v.hr}ЧСС
${v.bp}АД
${v.rr}ЧДД
${v.spo2}SpO₂
${extra}
`; } function bindSpeak(c, root){ const btn=root.querySelector("#speakBtn"); const av=root.querySelector("#pAvatar"); if(btn) btn.addEventListener("click",()=>speakPatient(c.complaint, c.patient.voice, btn, av)); } /* ---- Шаг 0: Пациент ---- */ function renderPatient(){ const c=curCase(); examBody.innerHTML=`
${patientBlock(c)}
📋 Анамнез
    ${c.anamnesis.map(a=>`
  • ${a}
  • `).join("")}
🩺 Объективный осмотр
${vitalsHTML(c.objective.vitals)}

Общий осмотр

${c.objective.exam.general}

Сердечно-сосудистая система

${c.objective.exam.heart}

Дыхательная система

${c.objective.exam.lungs}

Прочее

${c.objective.exam.other}

Знакомство с пациентом
`; bindSpeak(c, examBody); examBody.querySelector("#goNext").onclick=()=>{ state.stage=1; renderStageTrack(); renderMulti("investigations"); }; examBody.scrollTop=0; } /* ---- Универсальный шаг множественного выбора ---- */ const MULTI_CFG={ investigations:{tag:"Шаг 1 из 4", title:"Назначьте обследования", sub:"Выберите необходимые исследования. Не назначайте лишнее — за ошибки снижается балл.", btn:"Подтвердить назначения"}, medications:{tag:"Шаг 2 из 4", title:"Назначьте лечение", sub:"Выберите препараты из перечня. Избегайте противопоказанных и неэффективных.", btn:"Подтвердить лечение"}, emergency:{tag:"Шаг 4 из 4", title:"Экстренная помощь ⏱", sub:"", btn:"Завершить помощь"} }; function renderMulti(kind){ const c=curCase(); const cfg=MULTI_CFG[kind]; const items = kind==="investigations"?c.investigations : kind==="medications"?c.medications : c.emergency.actions; const ans = state.answers[kind]; const submitted = kind==="investigations"?state.answers.submitted.i : kind==="medications"?state.answers.submitted.m : state.answers.submitted.e; const isRadio = false; let emergencyTop=""; if(kind==="emergency" && !submitted) emergencyTop=`
`; examBody.innerHTML=`
${cfg.tag}

${cfg.title}

${kind==="emergency"?c.emergency.intro:cfg.sub}

${emergencyTop}
${items.map((it,i)=>{ const checked=ans.has(i)?"checked":""; return ``; }).join("")}
${submitted?`Балл за шаг: `:"Выберите варианты"}
`; const optList=examBody.querySelector("#optList"); optList.addEventListener("click",e=>{ if(submitted) return; const lbl=e.target.closest(".opt"); if(!lbl) return; const i=+lbl.dataset.i; if(ans.has(i)){ ans.delete(i); lbl.querySelector("input").checked=false; } else { ans.add(i); lbl.querySelector("input").checked=true; } }); const actBtn=examBody.querySelector("#actBtn"); if(submitted){ highlightMulti(kind); actBtn.onclick=()=>{ if(kind==="investigations"){ state.stage=2; renderStageTrack(); renderMulti("medications"); } else if(kind==="medications"){ state.stage=3; renderStageTrack(); renderDiagnosis(); } else { state.stage=5; renderStageTrack(); renderCaseResult(); } }; } else { actBtn.onclick=()=>{ if(kind==="investigations") state.answers.submitted.i=true; else if(kind==="medications") state.answers.submitted.m=true; else { state.answers.submitted.e=true; stopTimer(); } renderMulti(kind); if(kind==="emergency"){} // таймер уже не нужен }; } if(kind==="emergency" && !submitted){ startTimer(c); } if(kind==="emergency" && submitted){ /* таймер снят */ } examBody.scrollTop=0; } /* ---- Подсветка результата множественного выбора ---- */ function highlightMulti(kind){ const c=curCase(); const items = kind==="investigations"?c.investigations : kind==="medications"?c.medications : c.emergency.actions; const ans=state.answers[kind]; const labels=examBody.querySelectorAll(".opt"); labels.forEach(lbl=>{ const i=+lbl.dataset.i; const it=items[i]; const got=ans.has(i); if(it.n && got){ lbl.classList.add("correct"); lbl.querySelector(".opt-text").insertAdjacentHTML("beforeend",`верно`); } else if(!it.n && got){ lbl.classList.add("wrong-pick"); lbl.querySelector(".opt-text").insertAdjacentHTML("beforeend",`лишнее`); } else if(it.n && !got){ lbl.classList.add("missed"); lbl.querySelector(".opt-text").insertAdjacentHTML("beforeend",`пропущено`); } }); const sc=multiScore(items,ans); const ss=examBody.querySelector("#stepScore"); if(ss) ss.textContent=sc+"%"; } /* ---- Шаг 3: Диагноз ---- */ function renderDiagnosis(){ const c=curCase(); const submitted=state.answers.submitted.d; const ans=state.answers.diagnosis; examBody.innerHTML=`
Шаг 3 из 4

Поставьте диагноз

Выберите наиболее вероятный диагноз из вариантов.

${c.diagnosis.options.map((o,i)=>{ const checked=ans===i?"checked":""; return ``; }).join("")}
${submitted?`Балл за шаг: `:"Выберите один вариант"}
`; const optList=examBody.querySelector("#optList"); optList.addEventListener("click",e=>{ if(submitted) return; const lbl=e.target.closest(".opt"); if(!lbl) return; state.answers.diagnosis=+lbl.dataset.i; optList.querySelectorAll("input").forEach((inp,ii)=>inp.checked=ii===state.answers.diagnosis); }); const actBtn=examBody.querySelector("#actBtn"); if(submitted){ highlightDiagnosis(); actBtn.onclick=()=>{ state.stage=4; renderStageTrack(); renderMulti("emergency"); }; } else { actBtn.onclick=()=>{ if(state.answers.diagnosis===null){ flashChip(); return; } state.answers.submitted.d=true; renderDiagnosis(); }; } examBody.scrollTop=0; } function highlightDiagnosis(){ const c=curCase(); const labels=examBody.querySelectorAll(".opt"); labels.forEach(lbl=>{ const i=+lbl.dataset.i; const o=c.diagnosis.options[i]; if(o.c){ lbl.classList.add("correct"); lbl.querySelector(".opt-text").insertAdjacentHTML("beforeend",`верный диагноз`); } else if(i===state.answers.diagnosis){ lbl.classList.add("wrong-pick"); lbl.querySelector(".opt-text").insertAdjacentHTML("beforeend",`неверно`); } }); const sc = state.answers.diagnosis!==null && c.diagnosis.options[state.answers.diagnosis].c ? 100 : 0; const ss=examBody.querySelector("#stepScore"); if(ss) ss.textContent=sc+"%"; } function flashChip(){ const chip=examBody.querySelector("#stepChip"); if(!chip) return; chip.style.background="var(--red-50)"; chip.style.color="var(--red)"; setTimeout(()=>{ chip.style.background=""; chip.style.color=""; },700); } /* ---- Таймер экстренной помощи ---- */ function startTimer(c){ const total=c.emergency.time||60; let left=total; const tw=examBody.querySelector("#timerWrap"); if(!tw) return; function tick(){ const pct=left/total*100; const color = left<=10 ? "var(--red)" : "var(--cyan)"; const danger = left<=10 ? ' осталось мало времени!' : ""; tw.innerHTML=` ${ring(pct,74,7,color,"rgba(255,255,255,.15)",left)}

⏱ Экстренная помощь

Отмечайте действия. Время пошло.${danger}

`; if(left<=0){ stopTimer(); if(!state.answers.submitted.e){ state.answers.submitted.e=true; state.emergencyAuto=true; renderMulti("emergency"); } return; } left--; } tick(); timerHandle=setInterval(tick,1000); } function stopTimer(){ if(timerHandle){ clearInterval(timerHandle); timerHandle=null; } } /* ---- Подсчёт баллов ---- */ function multiScore(items,picked){ let tp=0,tn=0,fp=0,fn=0; items.forEach((it,i)=>{ const need=it.n, got=picked.has(i); if(need&&got)tp++; else if(need&&!got)fn++; else if(!need&&got)fp++; else tn++; }); return Math.round(((tp+tn)/items.length)*100); } function computeCaseScore(c){ const s={ investigations:multiScore(c.investigations,state.answers.investigations), medications:multiScore(c.medications,state.answers.medications), diagnosis:(state.answers.diagnosis!==null && c.diagnosis.options[state.answers.diagnosis].c)?100:0, emergency:multiScore(c.emergency.actions,state.answers.emergency) }; s.total=Math.round(s.investigations*0.2+s.medications*0.2+s.diagnosis*0.3+s.emergency*0.3); return s; } /* ---- Результат кейса ---- */ function renderCaseResult(){ const c=curCase(); const sc=computeCaseScore(c); state.caseScores.push({id:c.id, title:c.title, theme:c.theme, themeLabel:c.themeLabel, badge:c.badge, score:sc.total, breakdown:sc}); const grade=gradeFor(sc.total); const col=colorFor(sc.total); examBody.innerHTML=`
${ring(sc.total,150,12,col,"var(--gray-100)",`${sc.total}%оценка за кейс`)}

${grade.title}

${grade.text}
${breakRow("Обследования",sc.investigations,"var(--blue)")} ${breakRow("Лечение",sc.medications,"var(--purple)")} ${breakRow("Диагноз",sc.diagnosis,"var(--cyan)")} ${breakRow("Экстренная помощь",sc.emergency,"var(--red)")}

📝 Правильный диагноз

${c.diagnosis.options.find(o=>o.c).t}${c.diagnosis.explanation}

📋 Протокол МЗ РК

${c.protocol}${c.protocolNote}

🔬 Ключевые данные

${c.extra}

💡 Ключевые моменты и типичные ошибки

${c.keyPoints.map(p=>`
${p.t==="good"?"✅":"⚠️"}${p.x}
`).join("")}
Кейс ${state.qIdx+1} из ${state.queue.length} · ${sc.total}%
`; examBody.querySelector("#actBtn").onclick=nextAfterCase; examBody.scrollTop=0; } function breakRow(name,val,color){ return `
${name}
${val}%
`; } function nextLabel(){ if(state.mode==="single") return "Завершить"; if(state.qIdx=85) return {title:"Отлично!",text:"Уверенное владение алгоритмом неотложной помощи."}; if(pct>=70) return {title:"Хорошо",text:"Хороший уровень, но есть моменты для повторения."}; if(pct>=55) return {title:"Удовлетворительно",text:"Базовые навыки есть, но много ошибок — повторите тему."}; return {title:"Неудовлетворительно",text:"Алгоритмы требуют серьёзной проработки."}; } function colorFor(pct){ if(pct>=85) return "var(--green)"; if(pct>=70) return "var(--cyan)"; if(pct>=55) return "var(--amber)"; return "var(--red)"; } function gradeClass(pct){ return pct>=85?"g5":pct>=70?"g4":pct>=55?"g3":"g2"; } /* ---- Итоговый экран экзамена ---- */ function renderFinal(){ stopTimer(); const scores=state.caseScores; const avg=Math.round(scores.reduce((a,s)=>a+s.score,0)/scores.length); const col=colorFor(avg); const grade=gradeFor(avg); // по темам const themes={}; scores.forEach(s=>{ (themes[s.theme]=themes[s.theme]||[]).push(s.score); }); const themeRows=Object.entries(themes).map(([k,arr])=>{ const t=CASES.find(c=>c.theme===k); return `
${Math.round(arr.reduce((a,b)=>a+b,0)/arr.length)}%${t.themeLabel}
`; }).join(""); examBody.innerHTML=`
${ring(avg,180,14,col,"var(--gray-100)",`${avg}%`)}

Итог экзамена

${grade.title}

${grade.text} Вы прошли ${scores.length} из ${state.queue.length} кейсов.

${themeRows}
${scores.map((s,i)=>`
${i+1} ${s.title} ${s.score}%
`).join("")}
`; examBody.querySelector("#again").onclick=()=>{ startExam("full"); }; examBody.querySelector("#closeFin").onclick=closeExam; examBody.scrollTop=0; } /* ---- Кнопки запуска экзамена ---- */ document.getElementById("startFullExam").addEventListener("click",()=>startExam("full")); document.getElementById("startFullExam2").addEventListener("click",()=>startExam("full")); document.getElementById("examExit").addEventListener("click",()=>{ if(confirm("Выйти из экзамена? Текущий прогресс будет потерян.")) closeExam(); }); document.addEventListener("keydown",e=>{ if(e.key==="Escape" && !exam.hidden){ if(confirm("Выйти из экзамена?")) closeExam(); } }); /* ---- старт ---- */ renderCards();