// chat.jsx — Кабинет: chat with agent. Document-style.
// Each of 9 roles has its own unique case (data-driven via AGENT_CASES).

// ─────────────────────────────────────────────────────────────────────────────
// AGENT_CASES — sample case per role. Each case mirrors realistic work for that role.
// ─────────────────────────────────────────────────────────────────────────────

const AGENT_CASES = {
  jurist: {
    no: '2026/0142',
    title: 'Договор аренды ООО «Восход»',
    date: '14 мая 2026 · среда',
    systemNote: 'Дело открыто 14:30:02. Используется база знаний компании: профиль ООО «Хорошее дело», раздел «Контрагенты», документ «Шаблон протокола разногласий».',
    file: { name: 'Договор аренды_ООО_Восход.pdf', meta: '14 стр. · 312 КБ · PDF', type: 'PDF' },
    userMsg: 'Проверьте этот договор аренды на риски. Мы — арендатор, помещение 80 м², срок 11 месяцев. На что обратить внимание? Что точно нужно править перед подписанием?',
    userTime: '14:32',
    agentTime: '14:32:18',
    agentTokens: 2840,
    intro: <>Прочитал договор полностью. Нашёл <strong>7 рисков, из них 2 критичных</strong>. Привожу разбор с цитатами из текста и ссылками на статьи ГК РФ.</>,
    blocks: [
      { type: 'risk', level: 'критично', n: '01', title: 'Односторонний пересмотр ставки',
        cite: 'п. 4.3 — «Арендодатель вправе пересматривать размер арендной платы в одностороннем порядке каждые три месяца»',
        note: 'Противоречит ст. 614 ГК РФ. Рекомендую: «не чаще одного раза в год и не более чем на 10%».' },
      { type: 'risk', level: 'критично', n: '02', title: 'Невозврат обеспечительного платежа',
        cite: 'п. 7.1 — «При любом нарушении условий настоящего договора обеспечительный платёж не возвращается»',
        note: 'Противоречит ст. 381.1 ГК РФ. Удержание возможно только при существенном нарушении с перечнем.' },
      { type: 'risk', level: 'внимание', n: '03', title: 'Лимит по электричеству',
        cite: 'п. 5.2 — «Свыше 3 кВт·ч в сутки оплачивается отдельно по тарифу 9 ₽/кВт·ч»',
        note: 'Тариф выше рыночного на 18%. Уточнить или зафиксировать индексацию.' },
    ],
    outro: <>Ещё 4 риска уровня «внимание» — могу разобрать, если нужно. Готов подготовить проект протокола разногласий со ссылками на ГК РФ — ответьте «подготовь протокол» и я пришлю DOCX.</>,
    note: 'Для критичных решений рекомендую согласовать протокол со штатным юристом — Делегат это AI-помощник, а не замена адвоката.',
    followUp: 'Подготовь протокол разногласий. И ещё — у нас опцион на продление ещё на 11 месяцев. Можем ли мы зафиксировать в протоколе текущую ставку на весь срок продления?',
    followUpTime: '14:38',
    typingTime: '14:38:09',
    typingMsg: 'Готовит документ «Протокол разногласий»…',
    company: { name: 'ООО «Хорошее дело»', inn: '7700123456', sphere: 'Розничная торговля', usn: '6% (доходы)' },
    files: [
      { name: 'Договор аренды_ООО_Восход.pdf', size: '312 КБ · 14 стр.', active: true },
      { name: 'Свидетельство о праве собственности.pdf', size: '180 КБ · 2 стр.' },
      { name: 'Шаблон_протокол_разногласий.docx', size: '42 КБ · из базы', tag: 'база' },
    ],
    sources: [
      { cite: 'ГК РФ ст. 614', desc: 'Размер арендной платы' },
      { cite: 'ГК РФ ст. 381.1', desc: 'Обеспечительный платёж' },
      { cite: 'ГК РФ ст. 421', desc: 'Свобода договора' },
    ],
  },

  accountant: {
    no: '2026/0141',
    title: 'УПД от ИП Соловьёв за июль',
    date: '13 мая 2026 · вторник',
    systemNote: 'Дело открыто 17:08:14. Используется база знаний: профиль ООО «Хорошее дело», УСН 6%, реквизиты ИП Соловьёв из раздела «Постоянные подрядчики».',
    file: { name: 'УПД_Соловьёв_июль_2026.pdf', meta: '2 стр. · 184 КБ · PDF', type: 'PDF' },
    userMsg: 'Проверьте этот УПД на ошибки в реквизитах. И заодно посчитайте УСН 6% к уплате по итогам июля — выручка 1 440 000 ₽, прошлогодний остаток 0.',
    userTime: '17:08',
    agentTime: '17:08:42',
    agentTokens: 1980,
    intro: <>Проверил УПД и посчитал налог. <strong>Нашёл 3 ошибки в реквизитах подрядчика</strong>, УПД нужно вернуть на исправление. Расчёт УСН готов.</>,
    blocks: [
      { type: 'risk', level: 'критично', n: '01', title: 'КПП не соответствует ИНН',
        cite: 'ИНН 770112345678 (12 цифр — ИП), но указан КПП 770101001',
        note: 'У ИП не может быть КПП. Удалить поле — иначе ФНС отклонит зачёт расходов.' },
      { type: 'risk', level: 'внимание', n: '02', title: 'Дата отгрузки позже даты документа',
        cite: 'Дата УПД: 31.07.2026, дата отгрузки в графе 11: 02.08.2026',
        note: 'Несостыковка может вызвать вопросы ФНС. Привести даты в соответствие.' },
      { type: 'risk', level: 'внимание', n: '03', title: 'Сумма прописью не совпадает с цифрами',
        cite: 'Цифрами: 184 000,00 ₽ / прописью: «сто восемьдесят тысяч»',
        note: 'Расхождение в 4 000 ₽. Главный документ — цифры, но устранить нужно.' },
      { type: 'metric', title: 'Расчёт УСН 6% за июль',
        rows: [
          ['Выручка июль', '1 440 000 ₽'],
          ['Налог УСН 6%', '86 400 ₽'],
          ['Страховые ИП за квартал (если есть)', '— нет, вы ООО'],
          ['К уплате до 28 августа', '86 400 ₽'],
        ] },
    ],
    outro: <>Готов <strong>сформировать платёжное поручение</strong> с КБК и реквизитами ФНС вашего региона. Ответьте «подготовь ПП».</>,
    note: 'Окончательную сдачу декларации делает ваш бухгалтер или налоговый сервис (Контур.Эльба, Моё дело). Я закрываю первичку и быстрые расчёты.',
    followUp: 'Подготовь платёжку. И сразу за август пришли напоминание за 5 дней до срока.',
    followUpTime: '17:14',
    typingTime: '17:14:11',
    typingMsg: 'Готовит платёжное поручение №27 от 13.05.2026…',
    company: { name: 'ООО «Хорошее дело»', inn: '7700123456', sphere: 'Розничная торговля', usn: '6% (доходы)' },
    files: [
      { name: 'УПД_Соловьёв_июль_2026.pdf', size: '184 КБ · 2 стр.', active: true },
      { name: 'Договор подряда_Соловьёв.pdf', size: '420 КБ · 5 стр.' },
      { name: 'НК РФ глава 26.2 УСН.txt', size: '128 КБ · из базы', tag: 'база' },
    ],
    sources: [
      { cite: 'НК РФ ст. 346.15', desc: 'Доходы при УСН' },
      { cite: 'НК РФ ст. 346.21', desc: 'Порядок исчисления налога' },
      { cite: 'Письмо ФНС № СД-4-3/19895', desc: 'УПД как первичный документ' },
    ],
  },

  marketer: {
    no: '2026/0139',
    title: 'Расчёт unit-экономики Q3',
    date: '11 мая 2026 · воскресенье',
    systemNote: 'Дело открыто 09:42:11. Используется база знаний: профиль ООО «Хорошее дело», раздел «Маркетинг», выгрузки Я.Метрики и amoCRM за июль-сентябрь.',
    file: { name: 'Funnel_Q3_2026_amocrm_export.xlsx', meta: '12 800 строк · 1.8 МБ · XLSX', type: 'XLSX' },
    userMsg: 'Посчитайте unit-экономику воронки за Q3. Где сливаем? И главное — окупается ли наш платный трафик с Я.Директа.',
    userTime: '09:42',
    agentTime: '09:42:55',
    agentTokens: 3210,
    intro: <>Разобрал воронку. <strong>Платный трафик сходится, но с риском</strong>: payback 3.1 мес. при churn ≤7%/мес. Главная утечка — на этапе trial→paid (12% против бенчмарка 25-30%).</>,
    blocks: [
      { type: 'metric', title: 'Unit-экономика Q3 2026',
        rows: [
          ['Лидов', '2 840'],
          ['Trial', '1 420 (50%)'],
          ['Paid', '170 (12% от trial)'],
          ['CAC blended', '4 800 ₽'],
          ['ARPU', '5 900 ₽/мес'],
          ['Gross margin', '78%'],
          ['LTV (cohort)', '18 200 ₽'],
          ['LTV/CAC', '3.8'],
          ['Payback', '3.1 мес.'],
        ] },
      { type: 'risk', level: 'критично', n: '01', title: 'Trial → Paid: 12% (бенчмарк 25-30%)',
        cite: 'Из 1 420 trial → 170 paid за 14 дней',
        note: 'Слабый onboarding или fit. Сделать 3 cohort-A/B: длиннее trial, обязательная карта, welcome-call.' },
      { type: 'risk', level: 'внимание', n: '02', title: 'Я.Директ CAC растёт месяц к месяцу',
        cite: 'Июль 3 900 ₽ → август 4 600 ₽ → сентябрь 5 800 ₽',
        note: 'Аукцион перегревается. Сместить 30% бюджета в SEO + Telegram Ads.' },
    ],
    outro: <>Готов <strong>сделать брифы на A/B-тесты</strong> по trial-воронке (3 эксперимента) и план перераспределения медийного бюджета. Скажите «делай брифы».</>,
    note: 'Метрики выгружены 14.05 в 09:30. Если данные обновятся в amoCRM — пересчитаю автоматически.',
    followUp: 'Делай брифы. А ещё — если повысим тариф «Команда» с 4 900 на 5 900, что будет с LTV/CAC и payback?',
    followUpTime: '09:51',
    typingTime: '09:51:34',
    typingMsg: 'Считает чувствительность LTV/CAC к +20% цене…',
    company: { name: 'ООО «Хорошее дело»', inn: '7700123456', sphere: 'Розничная торговля', usn: '6% (доходы)' },
    files: [
      { name: 'Funnel_Q3_2026_amocrm_export.xlsx', size: '1.8 МБ · 12 800 строк', active: true },
      { name: 'Yandex_Metrika_Q3.csv', size: '480 КБ · цели и каналы' },
      { name: 'Brand_voice_guide.pdf', size: '2.1 МБ · из базы', tag: 'база' },
    ],
    sources: [
      { cite: 'Reforge: Growth Unit Economics', desc: 'Методика расчёта LTV cohort' },
      { cite: 'Я.Директ Help / CPL benchmarks', desc: 'CAC бенчмарки B2B РФ 2026' },
      { cite: 'Внутренний: «Brand voice ХД»', desc: 'Тональность в коммуникации' },
    ],
  },

  copywriter: {
    no: '2026/0138',
    title: '50 описаний для Wildberries',
    date: '10 мая 2026 · суббота',
    systemNote: 'Дело открыто 11:20:08. Используется база знаний: бренд-гид ХД, тон голоса «тёплый эксперт», карточки конкурентов за май.',
    file: { name: 'Товары_WB_май_50шт.xlsx', meta: '50 SKU · 240 КБ · XLSX', type: 'XLSX' },
    userMsg: 'Напишите 50 описаний для Wildberries по этой таблице. Тон — тёплый эксперт, длина 280-380 знаков, ключевые слова из колонки «keywords» вшить органично. Без воды и без «лучший на рынке».',
    userTime: '11:20',
    agentTime: '11:20:35',
    agentTokens: 18420,
    intro: <>Готово. <strong>50 описаний</strong> сгенерированы по вашему брифу, выгружены в XLSX рядом с исходником. Средняя длина 326 знаков, все ключи вписаны органично.</>,
    blocks: [
      { type: 'metric', title: 'Сводка по партии',
        rows: [
          ['Описаний готово', '50 / 50'],
          ['Средняя длина', '326 знаков'],
          ['Запрещённые слова («лучший», «№1»)', '0'],
          ['Ключи из колонки keywords', '100% покрытие'],
          ['Уникальность Text.ru (выборка 5)', '94-98%'],
          ['Время', '1 час 14 мин.'],
        ] },
      { type: 'items', title: 'Пример: SKU 17 «Кружка керамическая 350 мл»',
        items: [
          ['Headline', 'Тёплая керамика для утренних ритуалов'],
          ['Описание', 'Толстостенная керамика держит температуру дольше тонкого фарфора — кофе остаётся горячим 22 минуты. Эргономичная ручка под взрослую ладонь, ровное дно — не царапает столешницу. Подходит для посудомойки и микроволновки. Объём 350 мл — стандартный американо или капучино.'],
          ['Длина', '342 знака'],
          ['Ключи', 'керамическая кружка / 350 мл / для микроволновки'],
        ] },
    ],
    outro: <>Файл выгружен: <strong>WB_descriptions_50_2026-05-10.xlsx</strong>. Структура колонок совместима с массовой загрузкой WB. Готов сделать ещё 50 — или ту же партию для Ozon (там другой лимит знаков).</>,
    note: 'Для уникальности > 90% Text.ru держу style randomization. Если нужно >95% — увеличу разброс синонимов, но потеряю в кохерентности.',
    followUp: 'Сделай те же 50 под Ozon. И ещё одно письмо для email-рассылки по этим товарам — заголовок + 3 абзаца, CTA «Посмотреть в каталоге».',
    followUpTime: '11:34',
    typingTime: '11:34:22',
    typingMsg: 'Адаптирует описания под Ozon (лимит 1000 знаков)…',
    company: { name: 'ООО «Хорошее дело»', inn: '7700123456', sphere: 'Розничная торговля', usn: '6% (доходы)' },
    files: [
      { name: 'Товары_WB_май_50шт.xlsx', size: '240 КБ · 50 SKU', active: true },
      { name: 'WB_descriptions_50_2026-05-10.xlsx', size: '480 КБ · готовый файл', tag: 'результат' },
      { name: 'Brand_voice_guide.pdf', size: '2.1 МБ · из базы', tag: 'база' },
    ],
    sources: [
      { cite: 'Внутренний: «Brand voice ХД»', desc: 'Тональность «тёплый эксперт»' },
      { cite: 'WB Seller Guide 2026', desc: 'Лимиты и требования к карточкам' },
      { cite: 'Text.ru API', desc: 'Проверка уникальности (выборка)' },
    ],
  },

  sales: {
    no: '2026/0140',
    title: 'Скрипт для холодных звонков B2B',
    date: '12 мая 2026 · понедельник',
    systemNote: 'Дело открыто 16:15:50. Используется база знаний: продуктовая матрица ХД, прошлые записи звонков (47 шт.), методика SPIN/Challenger.',
    file: { name: 'Запись_звонок_Метинвест_28мин.mp3', meta: '28 мин. · 12 МБ · MP3', type: 'MP3' },
    userMsg: 'Разбери звонок с Метинвест от 11 мая. Где я провалил квалификацию? Что улучшить? И сделай follow-up письмо на основе того что услышал.',
    userTime: '16:15',
    agentTime: '16:16:12',
    agentTokens: 2710,
    intro: <>Прослушал 28 минут. Транскрипт сохранил. Главное: <strong>BANT-budget пропущен полностью</strong>, клиент дал 4 сигнала готовности, вы зашли только на 1. 6 точек роста.</>,
    blocks: [
      { type: 'risk', level: 'критично', n: '01', title: 'Бюджет не выяснен',
        cite: '14:22 — клиент: «у нас сейчас сильно режут косты»; ваш ответ: «понимаю, у всех так»',
        note: 'Это был открытый сигнал. Скрипт: «Расскажите подробнее? Какой бюджет всё-таки рассматриваете на эту задачу в этом квартале?»' },
      { type: 'risk', level: 'критично', n: '02', title: 'Decision-maker не подтверждён',
        cite: '08:11 — клиент: «нужно посоветоваться с финдиром»',
        note: 'Должны были спросить: «Кто принимает финальное решение? Можем сразу позвать его на следующую встречу?»' },
      { type: 'metric', title: 'Чек-лист BANT',
        rows: [
          ['Budget', '❌ не выяснен'],
          ['Authority', '⚠️ есть финдир, не подключён'],
          ['Need', '✅ боль артикулирована'],
          ['Timeline', '⚠️ «в течение полугода» — размыто'],
        ] },
    ],
    outro: <>Готовлю <strong>follow-up письмо</strong>: благодарность + 3 ключевых тезиса из разговора + предложение календарного слота с финдиром. Также — отдельная памятка для следующей встречи (вопросы по бюджету и срокам).</>,
    note: 'Я слышу intent, но не вижу финальной цифры пайплайна. Заглядывайте в дашборд amoCRM — там размечал теги по звонку.',
    followUp: 'Пиши письмо. И прокрути ещё раз момент 19:30 — мне показалось, он там как раз про сроки сказал что-то важное.',
    followUpTime: '16:24',
    typingTime: '16:24:48',
    typingMsg: 'Делает выборку 19:00–20:00 транскрипта…',
    company: { name: 'ООО «Хорошее дело»', inn: '7700123456', sphere: 'Розничная торговля', usn: '6% (доходы)' },
    files: [
      { name: 'Запись_звонок_Метинвест_28мин.mp3', size: '12 МБ · 28 мин.', active: true },
      { name: 'Транскрипт_Метинвест_11.05.txt', size: '32 КБ · сгенерирован' },
      { name: 'Продуктовая_матрица_ХД.pdf', size: '1.4 МБ · из базы', tag: 'база' },
    ],
    sources: [
      { cite: 'SPIN Selling (Rackham)', desc: 'Методика вопросов S-P-I-N' },
      { cite: 'Challenger Sale (Dixon)', desc: 'Подход «учитель, а не помощник»' },
      { cite: 'ASR-движок премиум-класса', desc: 'Транскрипция аудио на 99.2% точности' },
    ],
  },

  hr: {
    no: '2026/0136',
    title: 'Скрининг 80 резюме · Senior Backend',
    date: '8 мая 2026 · четверг',
    systemNote: 'Дело открыто 10:05:33. Используется база знаний: ТЗ вакансии Senior Backend Python, профиль идеального кандидата, политика РФ-резидентства.',
    file: { name: 'resumes_senior_backend_80.zip', meta: '80 PDF · 14 МБ · ZIP', type: 'ZIP' },
    userMsg: 'Скринь 80 резюме на Senior Backend Python. Стек обязательно FastAPI + Postgres + Redis. Опыт 5+ лет. РФ-резидент. Ранжируй топ-10 для интервью.',
    userTime: '10:05',
    agentTime: '10:05:48',
    agentTokens: 14200,
    intro: <>Обработал 80 резюме. <strong>Топ-10 ранжированы</strong>. Из них 3 «hot»: Senior+, релевантный стек, локация Москва/Казань, не в job-search режиме. Привожу выжимку.</>,
    blocks: [
      { type: 'items', title: 'Топ-3 кандидата',
        items: [
          ['#1 Анна П.', '9 лет · OpenStack contributor · стек 100% совпадение · Москва · gross 380К'],
          ['#2 Дмитрий С.', '7 лет · 2 высоконагруженных продукта (300K RPS) · Казань · gross 320К'],
          ['#3 Игорь К.', '6 лет · сильный Postgres (партиционирование, репликация) · Тбилиси, РФ-резидент · gross 340К'],
        ] },
      { type: 'metric', title: 'Сводка по 80 резюме',
        rows: [
          ['Прошли скрининг (5+ лет, стек)', '34 / 80'],
          ['Из них РФ-резиденты', '21 / 34'],
          ['Из них в active search', '14 / 21'],
          ['Топ-10 для оффера', '10'],
          ['Время обработки', '7 мин. 42 сек.'],
        ] },
    ],
    outro: <>Готовлю <strong>файл со списком топ-10</strong> для рекрутера (ФИО, telegram, ссылка на резюме, ключевые поинты, ожидаемая ЗП) и <strong>вопросы для тех. интервью</strong> с топ-3.</>,
    note: 'Скоринг — это вход, а не окончательное решение. Финальная оценка через тех. интервью + cultural fit. Резюме без согласия на обработку ПДн — отбросил (3 шт.).',
    followUp: 'Подготовь вопросы для технического интервью топ-3. И ещё — текст оффера-шаблона на 380К gross со всеми соцпакетами.',
    followUpTime: '10:11',
    typingTime: '10:11:18',
    typingMsg: 'Готовит 12 вопросов и шаблон оффера…',
    company: { name: 'ООО «Хорошее дело»', inn: '7700123456', sphere: 'Розничная торговля', usn: '6% (доходы)' },
    files: [
      { name: 'resumes_senior_backend_80.zip', size: '14 МБ · 80 PDF', active: true },
      { name: 'ТЗ_вакансия_Senior_Backend.docx', size: '64 КБ · из базы', tag: 'база' },
      { name: 'Тариф_грейды_2026.xlsx', size: '120 КБ · из базы', tag: 'база' },
    ],
    sources: [
      { cite: 'Хабр Карьера salary index Q1-2026', desc: 'Бенчмарк ЗП Senior Backend' },
      { cite: '152-ФЗ ст. 9', desc: 'Согласие на обработку ПДн' },
      { cite: 'Внутренний: «Профиль кандидата»', desc: 'Hard- и soft-критерии' },
    ],
  },

  analyst: {
    no: '2026/0135',
    title: 'Продажи Q3 vs Q2 — расследование падения',
    date: '6 мая 2026 · вторник',
    systemNote: 'Дело открыто 12:33:00. Используется база знаний: схема склада, классификатор SKU, история кампаний Q1-Q3.',
    file: { name: 'sales_full_export_2026_q1-q3.xlsx', meta: '12 400 строк · 4 МБ · XLSX', type: 'XLSX' },
    userMsg: 'Сравни выручку Q3 и Q2. Где падение? Хочу понять — это сезон, конкуренты или мы сами обвалили.',
    userTime: '12:33',
    agentTime: '12:33:54',
    agentTokens: 4180,
    intro: <>Сравнил выгрузки. <strong>Q3 −18% к Q2</strong> по выручке (−7.4 млн ₽). Падение неравномерное: <strong>3 SKU дают 84% от падения</strong>. Причина — не сезон, не конкуренты.</>,
    blocks: [
      { type: 'metric', title: 'Q3 vs Q2 — общие цифры',
        rows: [
          ['Выручка Q2', '41.2 млн ₽'],
          ['Выручка Q3', '33.8 млн ₽'],
          ['Δ', '−7.4 млн (−18%)'],
          ['Чеков Q2 → Q3', '8 920 → 7 410 (−17%)'],
          ['Средний чек', '4 619 → 4 561 (−1.2%)'],
          ['Вывод', 'Упал не средний чек, а количество заказов'],
        ] },
      { type: 'risk', level: 'критично', n: '01', title: 'SKU «ХД-401» провалился на −52%',
        cite: 'Q2: 2.8 млн ₽ → Q3: 1.35 млн ₽',
        note: 'Совпадает с датой возврата на сайт регулярной цены (была акция -30% до 28 июня). Цена выше восприятия — clutch перепозиционирования.' },
      { type: 'risk', level: 'критично', n: '02', title: 'SKU «ХД-217» провалился на −44%',
        cite: 'Q2: 1.9 млн ₽ → Q3: 1.06 млн ₽',
        note: 'Параллельно — Я.Маркет ранжирование. Карточка слетела с 1-й страницы (видно в выгрузке рейтинга). Восстановить → отзывы + цена.' },
      { type: 'risk', level: 'внимание', n: '03', title: 'SKU «ХД-302» провалился на −37%',
        cite: 'Q2: 1.4 млн ₽ → Q3: 880К ₽',
        note: 'Связан с ХД-401 (комплементарный товар). При восстановлении первого должен подтянуться.' },
    ],
    outro: <>Готов <strong>сделать когортный анализ по каналам</strong> и проверить гипотезу «упала повторная покупка», а не привлечение. Скажите «делай по каналам».</>,
    note: 'Анализ читает данные out-of-box. Решения по цене / маркетингу — за вами, я только подсвечиваю аномалии.',
    followUp: 'Делай по каналам. И ещё — проверь, не упали ли в Q3 запросы по этим SKU в Я.Wordstat. Может, и спрос реально просел.',
    followUpTime: '12:42',
    typingTime: '12:42:30',
    typingMsg: 'Берёт когорты по каналам · загружает Wordstat…',
    company: { name: 'ООО «Хорошее дело»', inn: '7700123456', sphere: 'Розничная торговля', usn: '6% (доходы)' },
    files: [
      { name: 'sales_full_export_2026_q1-q3.xlsx', size: '4 МБ · 12 400 строк', active: true },
      { name: 'sku_classifier.csv', size: '88 КБ · из базы', tag: 'база' },
      { name: 'price_history_2026.csv', size: '420 КБ · из базы', tag: 'база' },
    ],
    sources: [
      { cite: 'Внутренний: SKU-классификатор', desc: 'Группы товаров и сезонность' },
      { cite: 'Wordstat / Я.Метрика', desc: 'Поисковый спрос и переходы' },
      { cite: 'Code Interpreter', desc: 'Pivot, groupby, корреляции' },
    ],
  },

  secretary: {
    no: '2026/0134',
    title: 'Протокол встречи команды 14.05',
    date: '5 мая 2026 · понедельник',
    systemNote: 'Дело открыто 19:08:22. Используется база знаний: список участников команды, тематика встречи «Q3 planning», шаблон протокола ООО «ХД».',
    file: { name: 'team_meeting_2026-05-14.mp4', meta: '1 час 22 мин. · 480 МБ · MP4', type: 'MP4' },
    userMsg: 'Сделай протокол встречи планирования Q3. Участники: я, Аня, Денис, Кирилл, Марина. Нужны: задачи с ответственными и сроками, ключевые решения, точка следующей сверки.',
    userTime: '19:08',
    agentTime: '19:09:01',
    agentTokens: 3920,
    intro: <>Транскрибировал 1 час 22 мин. <strong>3 ключевых решения, 6 задач с ответственными</strong>, следующая сверка 21 мая. Протокол готов к рассылке.</>,
    blocks: [
      { type: 'items', title: 'Решения встречи',
        items: [
          ['Р-1', 'Запускаем Q3-фокус на удержание (NPS-кампания + onboarding-перезалив)'],
          ['Р-2', 'Сокращаем Я.Директ на 30%, бюджет переводим в Telegram Ads + контент'],
          ['Р-3', 'Найм CSM откладываем на 2 недели — сначала валидация процесса с founder-led CS'],
        ] },
      { type: 'metric', title: 'Задачи',
        rows: [
          ['№ 1 · Маркетинг brief NPS-кампании', 'Аня · до 18 мая'],
          ['№ 2 · Onboarding-сценарий v2', 'Денис · до 22 мая'],
          ['№ 3 · Перевод бюджета Я.Директ → TG', 'Кирилл · до 17 мая'],
          ['№ 4 · CSM-процесс прототип', 'Дамир · до 20 мая'],
          ['№ 5 · Прайс-чувствительность анализ', 'Марина · до 25 мая'],
          ['№ 6 · Следующая сверка', 'все · 21 мая 19:00'],
        ] },
    ],
    outro: <>Отправил DOCX в почту участникам. Поставил напоминания о сроках в Telegram-бот. Если будут расхождения — отметьте, перевыложу версию.</>,
    note: 'Аудио хранится 30 дней, потом авто-удаление. Транскрипт остаётся в деле бессрочно (если не закрыть).',
    followUp: 'Скинь 1-абзацное резюме встречи в WhatsApp для совета директоров. Без задач, только три ключевых решения.',
    followUpTime: '19:14',
    typingTime: '19:14:18',
    typingMsg: 'Сжимает до 1 абзаца, без задач…',
    company: { name: 'ООО «Хорошее дело»', inn: '7700123456', sphere: 'Розничная торговля', usn: '6% (доходы)' },
    files: [
      { name: 'team_meeting_2026-05-14.mp4', size: '480 МБ · 1 час 22 мин.', active: true },
      { name: 'Протокол_14.05.2026_v1.docx', size: '76 КБ · сгенерирован', tag: 'результат' },
      { name: 'Шаблон_протокол_ХД.docx', size: '38 КБ · из базы', tag: 'база' },
    ],
    sources: [
      { cite: 'ASR-движок премиум-класса', desc: 'Транскрипция с разметкой спикеров' },
      { cite: 'LLM-стек Делегата', desc: 'Структурирование и сокращение' },
      { cite: 'Внутренний: Шаблон протокола', desc: 'Стандарт оформления ХД' },
    ],
  },

  designer: {
    no: '2026/0143',
    title: 'Бренд «Зерновая» · логотип + брендбук',
    date: '15 мая 2026 · четверг',
    systemNote: 'Дело открыто 14:18:40. Используется база знаний: бриф клиента, mood-board (32 референса), палитра «тёплый минимализм», запрет на использование изображений колосьев.',
    file: { name: 'brief_zernovaya_bakery.pdf', meta: '5 стр. · 220 КБ · PDF', type: 'PDF' },
    userMsg: 'Нужен логотип для бренда «Зерновая» — крафтовая пекарня, премиум-сегмент. Стиль современный, тёплый, без булочек и колосьев. И черновик мини-брендбука на 4-6 страниц.',
    userTime: '14:18',
    agentTime: '14:18:50',
    agentTokens: 4860,
    intro: <>Прочитал бриф, изучил mood-board. Сделал <strong>3 концепта логотипа</strong> (SVG + PNG @1x/@2x/@4x) и черновик мини-брендбука на 6 страниц. Файлы в дело.</>,
    blocks: [
      { type: 'items', title: '3 концепта логотипа',
        items: [
          ['№ 01 · Wordmark «Зерновая»', 'Кастомная типографика на базе Adelle — мягкие засечки, открытая «о», уникальная лигатура «зе». Цвет: умбра #6B4F35. SVG + PNG.'],
          ['№ 02 · Монограмма «З»', 'Геометрическая «З» в круге, ассоциация с печатью пекарни XIX в. Подходит для печати на упаковке тиснением. SVG + 3D-mockup на коробке.'],
          ['№ 03 · Абстрактный знак', 'Минималистичный знак — три волны (зерно → мука → хлеб). Самый «современный» вариант. SVG + анимированный logo-reveal MP4.'],
        ] },
      { type: 'metric', title: 'Брендбук v1 (черновик 6 стр.)',
        rows: [
          ['Стр. 1', 'Идентичность: смысл, миссия, обещание'],
          ['Стр. 2', 'Логотип: использование, отступы, запреты'],
          ['Стр. 3', 'Палитра: умбра #6B4F35 / песочный #E8D8B4 / уголь #1A1715 + акценты'],
          ['Стр. 4', 'Типографика: PT Serif Caption (заголовки) + Inter (основной)'],
          ['Стр. 5', 'Голос бренда: «знающий, спокойный, без пафоса»'],
          ['Стр. 6', 'Примеры применения: упаковка, ценник, баннер, соцсети'],
        ] },
    ],
    outro: <>Отдельно прикладываю <strong>3D-мокап упаковки</strong> и <strong>15-секундный motion-ролик</strong> для Reels с раскрытием знака. Готов сделать карточки для маркетплейса и презентацию для встречи с инвестором.</>,
    note: 'Авторские права на сгенерированные ассеты — ваши, без watermark. Если выбираете один концепт — финализирую под печать (CMYK, контурный SVG, спецификация).',
    followUp: 'Беру концепт №2 — монограмму. Сделай вариант для тёмного фона + favicon + презентацию на 10 слайдов для встречи с инвестором в пятницу.',
    followUpTime: '14:29',
    typingTime: '14:29:42',
    typingMsg: 'Готовит pack: dark-version + favicon + 10-slide deck…',
    company: { name: 'ООО «Хорошее дело»', inn: '7700123456', sphere: 'Розничная торговля', usn: '6% (доходы)' },
    files: [
      { name: 'brief_zernovaya_bakery.pdf', size: '220 КБ · 5 стр.', active: true },
      { name: 'moodboard_zernovaya_32refs.zip', size: '48 МБ · 32 PNG' },
      { name: 'logo_concepts_3.zip', size: '12 МБ · SVG + PNG @1x/@2x/@4x', tag: 'результат' },
      { name: 'brandbook_v1_draft.pdf', size: '6.2 МБ · 6 стр.', tag: 'результат' },
      { name: 'mockup_packaging_3d.png', size: '4.8 МБ · 2048×2048', tag: 'результат' },
      { name: 'motion_reveal_15s.mp4', size: '8 МБ · 1080×1080 · 15 сек.', tag: 'результат' },
    ],
    sources: [
      { cite: 'Бренд-стратегический LLM', desc: 'Тексты брендбука и нейминг' },
      { cite: 'Image-стек премиум-класса', desc: 'Концепты логотипа и mood-board' },
      { cite: '3D-движок', desc: 'Мокап упаковки и текстуры' },
      { cite: 'Video-модель нового поколения', desc: 'Motion-ролик 15 сек.' },
    ],
  },
};

// ─────────────────────────────────────────────────────────────────────────────
// CHAT SCREEN — main
// ─────────────────────────────────────────────────────────────────────────────

function ChatScreen({ navigate, agentId }) {
  const agent = AGENTS.find((a) => a.id === agentId) || AGENTS[0];
  const [activeAgentId, setActiveAgentId] = React.useState(agent.id);
  const activeAgent = AGENTS.find((a) => a.id === activeAgentId) || agent;
  const activeCase = AGENT_CASES[activeAgent.id] || AGENT_CASES.jurist;

  return (
    <div style={{ display: 'grid', gridTemplateColumns: '260px 1fr 320px', height: 'calc(100vh - 68px)', overflow: 'hidden' }}>
      {/* LEFT SIDEBAR */}
      <aside style={{ borderRight: '1px solid var(--rule)', background: 'var(--paper-2)', overflowY: 'auto', display: 'flex', flexDirection: 'column' }}>
        <div style={{ padding: '20px 20px 12px', borderBottom: '1px solid var(--rule)' }}>
          <div className="mono" style={{ fontSize: 11, letterSpacing: '0.14em', color: 'var(--ink-3)', textTransform: 'uppercase', marginBottom: 12 }}>
            Кабинет
          </div>
          <button
            style={{
              width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8,
              padding: '10px 12px', background: 'var(--ink)', color: 'var(--paper)', border: 0, borderRadius: 4,
              fontFamily: 'var(--font-body)', fontSize: 13, cursor: 'pointer',
            }}
          >
            <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
              <Glyph name="plus" size={14} />
              Открыть дело
            </span>
            <span className="mono" style={{ fontSize: 10, opacity: 0.6 }}>⌘N</span>
          </button>
        </div>

        {/* My agents — all 9 */}
        <div style={{ padding: '20px' }}>
          <div className="mono" style={{ fontSize: 10, letterSpacing: '0.14em', color: 'var(--ink-3)', textTransform: 'uppercase', marginBottom: 10 }}>
            Мои специалисты
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            {AGENTS.map((a) => (
              <button
                key={a.id}
                onClick={() => setActiveAgentId(a.id)}
                style={{
                  display: 'flex', alignItems: 'center', gap: 10, padding: '8px 10px',
                  background: activeAgentId === a.id ? 'var(--paper)' : 'transparent',
                  border: '1px solid ' + (activeAgentId === a.id ? 'var(--ink)' : 'transparent'),
                  borderRadius: 4, cursor: 'pointer', textAlign: 'left',
                  fontFamily: 'var(--font-body)',
                }}
              >
                <Monogram agent={a} size={32} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div className="mono" style={{ fontSize: 9, color: 'var(--ink-3)', letterSpacing: '0.1em' }}>№ {a.no}</div>
                  <div style={{ fontSize: 13, fontWeight: 500 }}>AI-{a.title}</div>
                </div>
              </button>
            ))}
          </div>

          <button style={{
            display: 'flex', alignItems: 'center', gap: 8, padding: '8px 10px', marginTop: 6,
            background: 'transparent', border: 0, borderRadius: 4, cursor: 'pointer',
            fontFamily: 'var(--font-body)', fontSize: 12, color: 'var(--ink-3)', width: '100%',
          }} onClick={() => navigate('catalog')}>
            <Glyph name="plus" size={12} /> Нанять ещё специалиста
          </button>
        </div>

        <div className="rule" style={{ margin: '0 20px' }} />

        {/* Cases — все 9 кейсов, активный — текущего агента */}
        <div style={{ padding: '20px' }}>
          <div className="mono" style={{ fontSize: 10, letterSpacing: '0.14em', color: 'var(--ink-3)', textTransform: 'uppercase', marginBottom: 10 }}>
            Дела этой недели
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
            {AGENTS.map((a) => {
              const c = AGENT_CASES[a.id];
              if (!c) return null;
              return (
                <CaseListItem
                  key={a.id}
                  no={c.no}
                  title={c.title}
                  sub={`AI-${a.title} · ${c.date.replace(' · ', ', ')}`}
                  active={activeAgentId === a.id}
                  onClick={() => setActiveAgentId(a.id)}
                />
              );
            })}
          </div>
        </div>

        <div style={{ marginTop: 'auto', padding: 20, borderTop: '1px solid var(--rule)' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <div style={{
              width: 32, height: 32, borderRadius: '50%', background: 'var(--forest)', color: 'var(--paper)',
              display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 14,
            }}>
              ДВ
            </div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13, fontWeight: 500 }}>Дамир В.</div>
              <div className="mono" style={{ fontSize: 10, color: 'var(--ink-3)', letterSpacing: '0.08em' }}>
                ООО «Хорошее дело»
              </div>
            </div>
            <button style={{ background: 'transparent', border: 0, cursor: 'pointer', color: 'var(--ink-3)' }}>⋯</button>
          </div>
        </div>
      </aside>

      {/* MAIN CHAT */}
      <main style={{ display: 'flex', flexDirection: 'column', background: 'var(--paper)', overflow: 'hidden' }}>
        <ChatHeader agent={activeAgent} caseData={activeCase} />
        <ChatMessages agent={activeAgent} caseData={activeCase} />
        <ChatComposer agent={activeAgent} />
      </main>

      {/* RIGHT SIDEBAR */}
      <aside style={{ borderLeft: '1px solid var(--rule)', background: 'var(--paper-2)', overflowY: 'auto' }}>
        <ContextPanel agent={activeAgent} caseData={activeCase} />
      </aside>
    </div>
  );
}

function CaseListItem({ no, title, sub, active, onClick }) {
  return (
    <button
      onClick={onClick}
      style={{
        textAlign: 'left',
        padding: '10px 10px',
        background: active ? 'var(--paper)' : 'transparent',
        border: '1px solid ' + (active ? 'var(--ink)' : 'transparent'),
        borderRadius: 4, cursor: 'pointer',
        fontFamily: 'var(--font-body)',
      }}
    >
      <div className="mono" style={{ fontSize: 9, color: 'var(--ink-3)', letterSpacing: '0.1em', marginBottom: 2 }}>
        № {no}
      </div>
      <div style={{ fontSize: 13, fontWeight: active ? 500 : 400, marginBottom: 2, lineHeight: 1.3 }}>
        {title}
      </div>
      <div style={{ fontSize: 11, color: 'var(--ink-3)' }}>{sub}</div>
    </button>
  );
}

function ChatHeader({ agent, caseData }) {
  return (
    <header style={{ padding: '14px 28px', borderBottom: '1px solid var(--rule)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', background: 'var(--paper)' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
        <Monogram agent={agent} size={40} />
        <div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
            <span className="serif" style={{ fontSize: 20, fontWeight: 700, letterSpacing: '-0.01em' }}>
              AI-{agent.title}
            </span>
            <span className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', letterSpacing: '0.1em' }}>
              · ДЕЛО № {caseData.no}
            </span>
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 2 }}>
            <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--forest)' }} />
            <span className="mono" style={{ fontSize: 11, color: 'var(--forest)', letterSpacing: '0.08em' }}>
              ГОТОВ К РАБОТЕ
            </span>
            <span className="muted" style={{ fontSize: 12, marginLeft: 12 }}>
              {caseData.title}
            </span>
          </div>
        </div>
      </div>
      <div style={{ display: 'flex', gap: 4 }}>
        <HeaderBtn label="Экспорт" icon="doc" />
        <HeaderBtn label="Очистить" />
        <HeaderBtn label="Закрыть дело" />
      </div>
    </header>
  );
}

function HeaderBtn({ label, icon }) {
  return (
    <button
      style={{
        display: 'flex', alignItems: 'center', gap: 6, padding: '6px 10px',
        background: 'transparent', border: '1px solid var(--rule)', borderRadius: 4,
        fontFamily: 'var(--font-body)', fontSize: 12, color: 'var(--ink-2)', cursor: 'pointer',
        transition: 'all 150ms',
      }}
      onMouseEnter={(e) => { e.currentTarget.style.borderColor = 'var(--ink)'; e.currentTarget.style.color = 'var(--ink)'; }}
      onMouseLeave={(e) => { e.currentTarget.style.borderColor = 'var(--rule)'; e.currentTarget.style.color = 'var(--ink-2)'; }}
    >
      {icon && <Glyph name={icon} size={12} />}
      {label}
    </button>
  );
}

function ChatMessages({ agent, caseData }) {
  return (
    <div style={{ flex: 1, overflowY: 'auto', padding: '32px 64px 40px', display: 'flex', flexDirection: 'column', gap: 32 }}>
      {/* Date stamp */}
      <div style={{ textAlign: 'center', margin: '0 auto' }}>
        <span className="mono" style={{ fontSize: 11, letterSpacing: '0.16em', color: 'var(--ink-3)', textTransform: 'uppercase', padding: '4px 12px', border: '1px solid var(--rule)', borderRadius: 12 }}>
          {caseData.date}
        </span>
      </div>

      {/* System opener */}
      <div className="marginalia" style={{ maxWidth: 720, margin: '0 auto', textAlign: 'center', color: 'var(--ink-3)' }}>
        {caseData.systemNote}
      </div>

      {/* User message with attachment */}
      <UserMessage time={caseData.userTime}>
        {caseData.file && <FileAttachment name={caseData.file.name} meta={caseData.file.meta} type={caseData.file.type} />}
        <p style={{ marginTop: caseData.file ? 12 : 0 }}>
          {caseData.userMsg}
        </p>
      </UserMessage>

      {/* Agent reply */}
      <AgentMessage agent={agent} time={caseData.agentTime} tokens={caseData.agentTokens}>
        <p>{caseData.intro}</p>

        <div style={{ marginTop: 16, display: 'flex', flexDirection: 'column', gap: 10 }}>
          {caseData.blocks.map((b, i) => {
            if (b.type === 'risk') {
              return <RiskRow key={i} level={b.level} n={b.n} title={b.title} cite={b.cite} note={b.note} />;
            }
            if (b.type === 'metric') {
              return <MetricBlock key={i} title={b.title} rows={b.rows} />;
            }
            if (b.type === 'items') {
              return <ItemsBlock key={i} title={b.title} items={b.items} />;
            }
            return null;
          })}
        </div>

        <p style={{ marginTop: 16, color: 'var(--ink-2)' }}>
          {caseData.outro}
        </p>

        <div className="marginalia" style={{ marginTop: 18, paddingTop: 14, borderTop: '1px dashed var(--rule)' }}>
          <em>Прим.</em> {caseData.note}
        </div>
      </AgentMessage>

      {/* User follow-up */}
      <UserMessage time={caseData.followUpTime}>
        <p>{caseData.followUp}</p>
      </UserMessage>

      <AgentMessage agent={agent} time={caseData.typingTime} tokens={1640} typing>
        <p style={{ color: 'var(--ink-3)', fontStyle: 'italic' }}>{caseData.typingMsg}</p>
      </AgentMessage>
    </div>
  );
}

function UserMessage({ time, children }) {
  return (
    <div style={{ maxWidth: 720, marginLeft: 'auto', width: '100%' }}>
      <div className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', letterSpacing: '0.1em', marginBottom: 6, textAlign: 'right' }}>
        Вы · {time}
      </div>
      <div style={{
        background: 'var(--ink)',
        color: 'var(--paper)',
        padding: '16px 20px',
        borderRadius: 4,
        fontSize: 15,
        lineHeight: 1.6,
      }}>
        {children}
      </div>
    </div>
  );
}

function AgentMessage({ agent, time, tokens, typing, children }) {
  return (
    <div style={{ display: 'flex', gap: 16, alignItems: 'flex-start', maxWidth: 820 }}>
      <Monogram agent={agent} size={40} />
      <div style={{ flex: 1, minWidth: 0 }}>
        <div className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', letterSpacing: '0.1em', marginBottom: 8, display: 'flex', justifyContent: 'space-between' }}>
          <span>AI-{agent.title} · {time}</span>
          {tokens && <span>{tokens.toLocaleString('ru')} симв.</span>}
        </div>
        <div style={{ fontSize: 15, lineHeight: 1.65, color: 'var(--ink)' }}>
          {children}
        </div>
        {!typing && (
          <div style={{ display: 'flex', gap: 4, marginTop: 14 }}>
            {['Скопировать', 'Сохранить в дело', 'Поделиться'].map((b) => (
              <button key={b} style={{
                background: 'transparent', border: '1px solid var(--rule)', borderRadius: 4,
                padding: '4px 10px', fontSize: 11, color: 'var(--ink-3)', cursor: 'pointer',
                fontFamily: 'var(--font-body)',
              }}>{b}</button>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

function FileAttachment({ name, meta, type }) {
  return (
    <div style={{
      display: 'flex', gap: 12, alignItems: 'center',
      padding: '10px 12px',
      background: 'rgba(244,240,230,0.08)',
      borderRadius: 3,
      border: '1px solid rgba(244,240,230,0.12)',
    }}>
      <div style={{
        width: 36, height: 36, border: '1px solid rgba(244,240,230,0.4)', display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '0.1em', borderRadius: 2,
      }}>{type || 'PDF'}</div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 13, lineHeight: 1.3, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{name}</div>
        <div className="mono" style={{ fontSize: 10, opacity: 0.6, letterSpacing: '0.08em' }}>{meta}</div>
      </div>
    </div>
  );
}

function RiskRow({ level, n, title, cite, note }) {
  const color = level === 'критично' ? 'var(--burgundy)' : 'var(--ochre)';
  return (
    <div style={{
      padding: '14px 16px',
      background: 'var(--paper-2)',
      borderLeft: `3px solid ${color}`,
      borderTop: '1px solid var(--rule)',
      borderRight: '1px solid var(--rule)',
      borderBottom: '1px solid var(--rule)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 6 }}>
        <span className="mono" style={{ fontSize: 11, color, fontWeight: 600, letterSpacing: '0.12em', textTransform: 'uppercase' }}>
          {level} · риск № {n}
        </span>
      </div>
      <div style={{ fontSize: 15, fontWeight: 600, marginBottom: 8 }}>{title}</div>
      <blockquote style={{
        margin: 0,
        padding: '6px 12px',
        borderLeft: '2px solid var(--rule)',
        fontFamily: 'var(--font-display)',
        fontStyle: 'italic',
        fontSize: 14,
        color: 'var(--ink-2)',
        marginBottom: 8,
      }}>
        {cite}
      </blockquote>
      <div style={{ fontSize: 13, color: 'var(--ink-2)' }}>
        <strong style={{ color: 'var(--ink)' }}>Что делать:</strong> {note}
      </div>
    </div>
  );
}

// New block: metric table (key/value rows)
function MetricBlock({ title, rows }) {
  return (
    <div style={{
      padding: '14px 16px',
      background: 'var(--paper-2)',
      borderLeft: '3px solid var(--forest)',
      borderTop: '1px solid var(--rule)',
      borderRight: '1px solid var(--rule)',
      borderBottom: '1px solid var(--rule)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 10 }}>
        <span className="mono" style={{ fontSize: 11, color: 'var(--forest)', fontWeight: 600, letterSpacing: '0.12em', textTransform: 'uppercase' }}>
          Расчёт · сводка
        </span>
      </div>
      <div style={{ fontSize: 15, fontWeight: 600, marginBottom: 10 }}>{title}</div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
        {rows.map(([k, v], i) => (
          <div key={i} style={{
            display: 'flex', justifyContent: 'space-between', alignItems: 'baseline',
            padding: '6px 0', borderTop: i === 0 ? '1px solid var(--rule)' : 'none', borderBottom: '1px solid var(--rule)',
            fontSize: 13,
          }}>
            <span style={{ color: 'var(--ink-2)' }}>{k}</span>
            <span className="mono" style={{ color: 'var(--ink)', fontWeight: 500 }}>{v}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// New block: items list (e.g. logo concepts, candidate list)
function ItemsBlock({ title, items }) {
  return (
    <div style={{
      padding: '14px 16px',
      background: 'var(--paper-2)',
      borderLeft: '3px solid var(--ink)',
      borderTop: '1px solid var(--rule)',
      borderRight: '1px solid var(--rule)',
      borderBottom: '1px solid var(--rule)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 10 }}>
        <span className="mono" style={{ fontSize: 11, color: 'var(--ink)', fontWeight: 600, letterSpacing: '0.12em', textTransform: 'uppercase' }}>
          Перечень · разбор
        </span>
      </div>
      <div style={{ fontSize: 15, fontWeight: 600, marginBottom: 10 }}>{title}</div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {items.map(([k, v], i) => (
          <div key={i} style={{
            paddingBottom: 8, borderBottom: '1px dashed var(--rule)',
          }}>
            <div className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', letterSpacing: '0.08em', marginBottom: 3 }}>{k}</div>
            <div style={{ fontSize: 13, color: 'var(--ink)', lineHeight: 1.5 }}>{v}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

function ChatComposer({ agent }) {
  const [value, setValue] = React.useState('');
  return (
    <div style={{ padding: '20px 32px 28px', borderTop: '1px solid var(--rule)', background: 'var(--paper)' }}>
      <div style={{
        border: '1px solid var(--rule)',
        borderRadius: 6,
        padding: '14px 16px',
        background: 'var(--paper)',
        transition: 'border-color 180ms',
      }}>
        <textarea
          value={value}
          onChange={(e) => setValue(e.target.value)}
          placeholder={`Напишите запрос для AI-${agent.title}…`}
          rows={2}
          style={{
            width: '100%',
            background: 'transparent', border: 0, outline: 0, resize: 'none',
            fontFamily: 'var(--font-body)', fontSize: 15, color: 'var(--ink)', lineHeight: 1.5,
          }}
        />
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 8 }}>
          <div style={{ display: 'flex', gap: 4 }}>
            <ComposerBtn icon="paperclip" label="Прикрепить" />
            <ComposerBtn icon="doc" label="Из базы знаний" />
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
            <span className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', letterSpacing: '0.08em' }}>
              План: <strong style={{ color: 'var(--ink-2)' }}>Команда</strong> · осталось 287 / 500 запросов
            </span>
            <button
              className="btn btn-primary btn-sm"
              style={{ padding: '8px 16px' }}
            >
              Отправить <Glyph name="send" size={14} />
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

function ComposerBtn({ icon, label }) {
  return (
    <button style={{
      display: 'flex', alignItems: 'center', gap: 6,
      background: 'transparent', border: 0, padding: '6px 8px', borderRadius: 4,
      cursor: 'pointer', color: 'var(--ink-3)', fontSize: 12,
      fontFamily: 'var(--font-body)', transition: 'color 150ms, background 150ms',
    }}
      onMouseEnter={(e) => { e.currentTarget.style.color = 'var(--ink)'; e.currentTarget.style.background = 'var(--paper-2)'; }}
      onMouseLeave={(e) => { e.currentTarget.style.color = 'var(--ink-3)'; e.currentTarget.style.background = 'transparent'; }}
    >
      {icon && <Glyph name={icon} size={14} />}
      {label}
    </button>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Context Panel — what's loaded for this case
// ─────────────────────────────────────────────────────────────────────────────

function ContextPanel({ agent, caseData }) {
  const c = caseData.company || { name: 'ООО «Хорошее дело»', inn: '7700123456', sphere: 'Розничная торговля', usn: '6% (доходы)' };
  return (
    <div style={{ padding: '20px' }}>
      <div className="mono" style={{ fontSize: 11, letterSpacing: '0.14em', color: 'var(--ink-3)', textTransform: 'uppercase', marginBottom: 14 }}>
        Контекст дела
      </div>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        <ContextBlock title="Профиль компании" sub="Используется автоматически">
          <ContextItem k="Название" v={c.name} />
          <ContextItem k="ИНН" v={c.inn} mono />
          <ContextItem k="Сфера" v={c.sphere} />
          <ContextItem k="УСН" v={c.usn} mono />
        </ContextBlock>

        <ContextBlock title="Файлы в этом деле" sub={`${caseData.files.length} документов`}>
          {caseData.files.map((f, i) => (
            <ContextFile key={i} name={f.name} size={f.size} active={f.active} tag={f.tag} />
          ))}
        </ContextBlock>

        <ContextBlock title="Источники, на которые ссылается агент">
          {caseData.sources.map((s, i) => (
            <SourceItem key={i} cite={s.cite} desc={s.desc} />
          ))}
        </ContextBlock>

        <div style={{ padding: 14, background: 'var(--forest-tint)', border: '1px solid var(--forest)', borderRadius: 4, marginTop: 8 }}>
          <div className="mono" style={{ fontSize: 10, letterSpacing: '0.14em', color: 'var(--forest)', textTransform: 'uppercase', marginBottom: 6 }}>
            <Glyph name="shield" size={12} color="var(--forest)" /> Безопасность
          </div>
          <div style={{ fontSize: 12, color: 'var(--ink-2)', lineHeight: 1.5 }}>
            Документы хранятся в защищённом контуре в РФ.
            Удаление через 30 дней после закрытия дела.
            Не используются для дообучения модели.
          </div>
        </div>
      </div>
    </div>
  );
}

function ContextBlock({ title, sub, children }) {
  return (
    <div style={{ background: 'var(--paper)', border: '1px solid var(--rule)', borderRadius: 4, padding: '14px 16px' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 10 }}>
        <div style={{ fontSize: 13, fontWeight: 600 }}>{title}</div>
        {sub && <div className="mono" style={{ fontSize: 10, color: 'var(--ink-3)', letterSpacing: '0.08em' }}>{sub}</div>}
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
        {children}
      </div>
    </div>
  );
}

function ContextItem({ k, v, mono }) {
  return (
    <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12 }}>
      <span style={{ color: 'var(--ink-3)' }}>{k}</span>
      <span className={mono ? 'mono' : ''} style={{ color: 'var(--ink)' }}>{v}</span>
    </div>
  );
}

function ContextFile({ name, size, active, tag }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 8,
      padding: '6px 0',
      borderTop: '1px solid var(--rule)',
    }}>
      <Glyph name="doc" size={14} color={active ? 'var(--forest)' : 'var(--ink-3)'} />
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 12, lineHeight: 1.3, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
          {name}
        </div>
        <div className="mono" style={{ fontSize: 10, color: 'var(--ink-3)', letterSpacing: '0.06em' }}>
          {size}
        </div>
      </div>
      {tag && <Tag>{tag}</Tag>}
    </div>
  );
}

function SourceItem({ cite, desc }) {
  return (
    <div style={{ padding: '6px 0', borderTop: '1px solid var(--rule)' }}>
      <div className="mono" style={{ fontSize: 12, color: 'var(--forest)', fontWeight: 500 }}>
        {cite}
      </div>
      <div style={{ fontSize: 11, color: 'var(--ink-3)' }}>{desc}</div>
    </div>
  );
}

window.ChatScreen = ChatScreen;
