/* ============================================================
   Красный флаг — дизайн-система

   Структура, геометрия и композиция взяты из утверждённого
   референса: плитка логотипа в шапке, ряд карточек-показателей,
   ряд действий (залитая пилюля + контурная + квадрат с иконкой),
   карточки-сплитки, нижняя панель на пять пунктов с залитым
   кругом под активной иконкой.

   Палитра переведена на красный. Оттенок взят глубокий кирпичный,
   а не сигнальный: референс держится на приглушённости, и чистый
   алый на больших плоскостях в приложении о насилии читался бы
   как тревога, а не как фирменный цвет.

   Типографика: Lora — заголовки, Golos Text — интерфейс,
   JetBrains Mono — служебные подписи и метки.
   ============================================================ */

:root{
  /* ----- красная шкала ----- */
  --red-900:#6E1811;
  --red-700:#8E1F16;
  --red-600:#A82A1E;   /* основной */
  --red-500:#C0392B;
  --red-100:#F6DDD8;
  --red-50:#FBEEEA;

  --bg:#FAF6F3;
  --surface:#FFFFFF;
  --surface-2:#F4EEE9;
  --ink:#1E1917;
  --ink-2:#4A413D;
  --muted:#6F645E;        /* 5.3:1 на --bg: прежний #877B75 давал 4.1 */
  --line:#EDE4DF;
  --line-strong:#DFD3CC;

  --accent:var(--red-600);
  --accent-ink:var(--red-700);
  --accent-soft:var(--red-50);
  --accent-border:var(--red-100);
  /* Текст на акцентной заливке. В светлой теме — белый на кирпичном (7:1);
     в тёмной акцент коралловый, и белый на нём — 3.4:1, поэтому там тёмный. */
  --accent-contrast:#fff;

  --ok:#2E7D53;
  --ok-soft:#E4F0E9;
  --ok-ink:#1F5E3D;

  --warn-soft:#FBF2E3;
  --warn-ink:#7A5A1B;

  --mark-bg:#F7E6C4;
  --mark-fg:#4A3406;
  /* Нажим полосы у ЕЁ карточек отчёта. Он свой на тему, и это не
     вкусовщина: у него полоса градуирует степень (прямая угроза 6.97:1 в
     светлой, 4.81 в тёмной), а её полоса говорит «твоя сторона». В полную
     силу `--mark-fg` даёт 11.76:1 и 13.88 — громче ЛЮБОЙ его тактики, то
     есть самый сильный нажим левой кромки вставал на её реплики. Числа
     подобраны так, чтобы полоса оставалась видимой (порог 3:1 для
     нетекстового) и не перебивала тревогу: 3.63:1 в светлой и 3.56 в
     тёмной. Одним значением это не берётся — его акцент в тёмной сам
     всего 4.81. */
  --mine-stripe:.6;

  /* Маскот. Заливка своя, а не `--surface`: в тёмной теме карточка почти
     чёрная, и зверёк на ней сливался бы в пятно с обводкой. */
  --pet-fill:#FFFFFF;
  --pet-blush:#F6DDD8;

  --shadow-sm:0 1px 2px rgba(60,35,25,.05);
  --shadow:0 1px 2px rgba(60,35,25,.05), 0 6px 18px rgba(60,35,25,.06);
  --shadow-lg:0 8px 30px rgba(60,35,25,.12);

  --r-xs:8px; --r-sm:12px; --r-md:16px; --r-lg:20px; --r-pill:999px;

  /* Шкала отступов. Радиусы и шрифты жили в токенах, а вертикальный ритм —
     нет: 26 разных значений margin на файл, в одном блоке «Тест» одиннадцать.
     Попиксельно всё сходилось, но измеримого ритма у интерфейса не было, и
     именно это читается как «собрано по месту». Экран результата собран на
     этой шкале целиком; остальные блоки переводятся отдельным заходом —
     там смена значений двигает геометрию, которую сторожит layout-check. */
  --s-1:4px; --s-2:8px; --s-3:12px; --s-4:16px;
  --s-5:24px; --s-6:32px; --s-7:48px;

  --f-display:"Lora",Georgia,serif;
  --f-body:"Golos Text",system-ui,-apple-system,sans-serif;
  --f-mono:"JetBrains Mono",ui-monospace,monospace;
}

@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    /* Поверхность карточек отличалась от фона на 1.08:1, а границы на 1.36:1 —
       в тёмной теме карточки теряли края и сливались в одно полотно. */
    --bg:#141110; --surface:#221E1C; --surface-2:#262120;
    --ink:#F2EAE6; --ink-2:#D6CAC4; --muted:#A2948D;
    --line:#3C3430; --line-strong:#463B37;
    --red-600:#E4614C; --red-700:#F08A78; --red-50:#33201C; --red-100:#4A2A24;
    --accent:var(--red-600); --accent-ink:var(--red-700);
    --accent-soft:var(--red-50); --accent-border:var(--red-100);
    --accent-contrast:#1A100E;
    --ok:#5FC98F; --ok-soft:#16302280; --ok-ink:#8FDCB0;
    --warn-soft:#332A18; --warn-ink:#E0C289;
    --mark-bg:#584318; --mark-fg:#FFE9BC; --mine-stripe:.42;
    --pet-fill:#3A3330; --pet-blush:#6B3A31;
    --shadow-sm:0 1px 2px rgba(0,0,0,.4);
    --shadow:0 1px 2px rgba(0,0,0,.4), 0 6px 18px rgba(0,0,0,.3);
    --shadow-lg:0 8px 30px rgba(0,0,0,.45);
  }
}

*{box-sizing:border-box}
body{
  margin:0; background:var(--bg); color:var(--ink);
  font-family:var(--f-body); font-size:15px; line-height:1.55;
  -webkit-font-smoothing:antialiased;
}
a{color:inherit}
.sr{position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap}

/* Отступ снизу — от измеренной высоты композера (js/layout.js), не константа */
/* Прокручивается ОБОЛОЧКА, а не документ, и это не вкусовщина.
   На айфоне в Safari `position:fixed` считается от разметочного вьюпорта, и
   пока строка адреса то сворачивается, то разворачивается при прокрутке
   страницы, композер и нижняя панель едут по экрану — это и было видно
   живьём. Когда сам документ не прокручивается, строке адреса нечего
   сворачивать: панели стоят намертво, как в телеграме и в чате.
   `dvh` вместо `vh` — по той же причине: `vh` на телефоне считается по
   развёрнутой строке адреса и даёт лишние сто пикселей под экраном. */
html{height:100%}
/* touch-action:manipulation убирает зум по двойному тапу — тот самый
   непрошеный масштаб, который человек не просила. Заодно снимает задержку
   в 300 мс, которую браузер держит на каждом нажатии, ожидая второго тапа:
   все кнопки приложения начинают отзываться сразу. */
/* ОКНО ПРИЛОЖЕНИЯ, А НЕ СТРАНИЦА.

   body прижат к видимой области: js/layout.js ставит ему height и top по
   visualViewport, и при открытой клавиатуре окно просто становится ниже.
   Полосы внутри стоят на absolute и оказаться где-то ещё не могут —
   поэтому их больше не нужно догонять сдвигом, чем и занимались две
   предыдущие попытки. Подробности — в шапке js/layout.js.

   100dvh здесь — значение до первого замера и на случай, если
   visualViewport недоступен. */
body{
  position:fixed; top:0; left:0; right:0; height:100dvh;
  overflow:hidden; overscroll-behavior:none; touch-action:manipulation;
}
.shell{
  max-width:600px; margin:0 auto; height:100%; overflow-y:auto;
  -webkit-overflow-scrolling:touch; overscroll-behavior:contain;
  padding:0 16px calc(var(--composer-h, 90px) + 16px);
}

/* ============================================================
   Шапка
   ============================================================ */
.topbar{
  position:sticky; top:0; z-index:40;
  /* Фон НЕПРОЗРАЧНЫЙ, и это не вкусовщина. Было 94% с blur(14px): на
     снимках в тёмной теме коралловый пузырь просвечивал через шапку ровным
     прямоугольником с видимой левой кромкой, а строка под её нижней
     границей была разрезана по середине букв. Это читается не как матовое
     стекло, а как сбой отрисовки. Растворение снизу делает ::after. */
  background:var(--bg);
  display:flex; align-items:center; gap:11px;
  /* Безопасная зона прибавляется к верхнему отступу, а не задаётся отдельным
     padding-top: сокращённое свойство ниже затёрло бы его. На домашнем экране
     статус-бар прозрачный и содержимое уходит под часы; в обычном Safari
     отступ равен нулю — там чёлку закрывает строка адреса. */
  padding-block:calc(env(safe-area-inset-top) + 14px) 12px;
  border-bottom:1px solid var(--line); margin-bottom:20px;
}
/* Полоса растворения живёт в margin-bottom шапки и ничего не двигает:
   текст должен уходить в туман, а не обрываться по кромке. */
.topbar::after{
  content:""; position:absolute; left:0; right:0; top:100%; height:18px;
  pointer-events:none;
  background:linear-gradient(to bottom, var(--bg), color-mix(in srgb, var(--bg) 0%, transparent));
}
.brand-tile{
  width:40px; height:40px; flex:none; border-radius:var(--r-sm);
  background:var(--accent); color:var(--accent-contrast); display:grid; place-items:center;
}
.brand-tile svg{width:21px; height:21px}
.wordmark{
  font-family:var(--f-display); font-weight:700; font-size:20px;
  letter-spacing:-.015em; line-height:1; margin-right:auto;
}
.iconbtn{
  width:44px; height:44px; flex:none; padding:0; border-radius:var(--r-pill);
  border:1px solid var(--line-strong); background:var(--surface); color:var(--ink-2);
  display:grid; place-items:center; cursor:pointer; box-shadow:var(--shadow-sm);
}
@media (hover:hover){.iconbtn:hover{background:var(--surface-2); color:var(--ink)}}
/* Точка на шестерёнке, пока модель не подключена: подсказывает,
   что там есть что сделать, не крича об этом. */
.iconbtn[data-dot="1"]{position:relative}
.iconbtn[data-dot="1"]::after{
  content:""; position:absolute; top:6px; right:6px;
  width:7px; height:7px; border-radius:50%;
  background:var(--accent); border:1.5px solid var(--surface);
}
.iconbtn svg{width:20px; height:20px}
.iconbtn.avatar{background:var(--accent); color:var(--accent-contrast); border-color:var(--accent)}
@media (hover:hover){.iconbtn.avatar:hover{background:var(--accent); opacity:.9}}

/* ============================================================
   Показатели — три карточки в ряд
   ============================================================ */
.stats{display:grid; grid-template-columns:repeat(3,1fr); gap:10px; margin-bottom:18px}
/* Пустое табло не должно занимать отступ: пока считать нечего, его нет */
.stats:empty{display:none}
.stat{
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);
  padding:14px 10px; text-align:center; box-shadow:var(--shadow-sm);
}
.stat-icon{
  width:38px; height:38px; margin:0 auto 10px; border-radius:var(--r-sm);
  background:var(--accent-soft); color:var(--accent); display:grid; place-items:center;
}
.stat-icon svg{width:19px; height:19px}
.stat-num{
  font-family:var(--f-display); font-weight:600; font-size:27px; line-height:1;
  font-variant-numeric:tabular-nums; margin-bottom:6px;
}
.stat-label{
  font-family:var(--f-mono); font-size:9px; letter-spacing:.07em;
  text-transform:uppercase; color:var(--muted); line-height:1.3;
}

/* ============================================================
   Ряд действий
   ============================================================ */
.actions{display:flex; gap:10px; margin-bottom:22px}
.btn{
  font-family:var(--f-body); font-size:14.5px; font-weight:500; cursor:pointer;
  border-radius:var(--r-pill); border:1px solid var(--line-strong);
  background:var(--surface); color:var(--ink);
  padding:13px 20px; display:inline-flex; align-items:center; justify-content:center; gap:8px;
  transition:background .15s, border-color .15s, opacity .15s;
}
@media (hover:hover){.btn:hover{background:var(--surface-2)}}
.btn:focus-visible{outline:2px solid var(--accent); outline-offset:2px}

/* ------------------------------------------------------------
   Отклик на нажатие.

   Его не было вовсе: во всём файле тринадцать правил `:hover` и ни
   одного `:active`. На телефоне наведения не существует как состояния,
   зато на айфоне `:hover` срабатывает по ПЕРВОМУ тапу и залипает до
   следующего тапа в стороне — то есть единственным следом нажатия была
   кнопка, которая осталась подсвеченной, хотя её уже отпустили. А самого
   нажатия не видно: подпись меняется («Скопировано», «Собираю…») только
   после того, как действие выполнилось.

   Поэтому наведение уведено под `@media (hover:hover)` — туда, где мышь
   есть, — а нажатие отвечает всем и сразу. `transition-duration:0s` —
   чтобы отклик был мгновенным на нажатие и плавным на отпускании: у
   `.btn` переход по opacity уже стоит.
   ------------------------------------------------------------ */
.btn:active, .iconbtn:active, .linkbtn:active, .chip:active,
.opts button:active, .q-next:not([disabled]):active,
.smart button:active, .connectbar .offer button:active,
.chat-plus:active, .chat-send:active, .kb-down:active,
.tab:active, .card:active, .back:active,
details.card-acc > summary:active, .find .more:active{
  opacity:.6; transition-duration:0s;
}
.btn svg{width:18px; height:18px; flex:none}
.btn-primary{background:var(--accent); border-color:var(--accent); color:var(--accent-contrast); flex:1}
@media (hover:hover){.btn-primary:hover{background:var(--accent); opacity:.9}}
.btn-ghost{flex:1}
/* Ряд действий главной делится НЕ поровну, и это замер, а не вкус. `.actions`
   раздавал обеим кнопкам по половине (`flex:1` у обеих), а подписи не равны:
   «Новый разбор» со значком просит 170px и получал 167 на 375 и 159 на 360.
   Замерено: подпись шла в два этажа, обе кнопки становились 62px вместо 47, и
   «Архив» рядом одной строкой висел посреди пустой пилюли — на первом экране,
   который видит вернувшаяся женщина. Свободного места в ряду при этом 343 при
   нужных 292.

   Правило стоит ЗДЕСЬ, ниже `.btn-primary`, а не в блоке `.actions` выше:
   специфичность у них одинаковая, и побеждает то, что ниже. Этот капкан в
   памяти проекта записан дважды — композером и медиазапросом по высоте. */
.actions .btn-primary{flex:1.35}
.btn-square{width:52px; padding:0; flex:none}
/* Высоты у класса просто не было заявлено, и кнопка выходила 34px — на всём
   экране теста это единственная цель меньше нормы. Тем же классом набраны
   «Удалить диалог», «Удалить всё безвозвратно» и «Сбросить всё и
   перезагрузить»: самые разрушительные действия были самыми мелкими целями.
   Отступы прежние, высоту держит min-height — .btn центрует подпись сам. */
.btn-sm{padding:var(--s-2) var(--s-4); font-size:13.5px; min-height:44px}
.btn[disabled]{opacity:.5; cursor:default}

/* ============================================================
   Карточка-сплитка: превью слева, содержимое справа
   ============================================================ */
.cards{display:flex; flex-direction:column; gap:12px}
.card{
  display:grid; grid-template-columns:112px 1fr;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);
  overflow:hidden; box-shadow:var(--shadow); text-decoration:none; color:inherit;
}
@media (hover:hover){.card:hover{border-color:var(--line-strong)}}
.card-preview{
  position:relative; background:var(--surface-2);
  display:grid; place-items:center; color:var(--muted); min-height:118px;
}
.card-preview svg{width:34px; height:34px; opacity:.55}
/* Дата на карточке диалога. Было 8.5px — самый мелкий кегль всего
   приложения, мельче тех 9px, которые уже поднимали до 12 у строки про
   приватность, — и при этом ЕДИНСТВЕННАЯ залитая поверхность строки:
   залитая форма читается громче текста равного контраста. То есть в
   списке, куда женщина приходит искать ленту по ИМЕНИ, первым в глаза
   бросалась дата, а имя шло третьим. Теперь дата — контурная метка,
   имя — самое заметное в строке. */
.card-tag{
  position:absolute; left:8px; bottom:8px;
  font-family:var(--f-mono); font-size:10px; letter-spacing:.06em; text-transform:uppercase;
  background:var(--surface); color:var(--muted); border:1px solid var(--line-strong);
  padding:3px 7px; border-radius:var(--r-pill);
}
.card-tag.accent{background:var(--accent); color:var(--accent-contrast)}
.card-body{padding:14px 15px; min-width:0}
.card-head{display:flex; align-items:flex-start; gap:8px; margin-bottom:8px}
.card-title{
  font-family:var(--f-display); font-weight:600; font-size:18px; line-height:1.22;
  letter-spacing:-.01em; margin:0; flex:1; min-width:0;
}
.chips{display:flex; flex-wrap:wrap; gap:6px; margin-bottom:10px}
.chip{
  display:inline-flex; align-items:center; gap:4px;
  font-size:12px; padding:4px 10px; border-radius:var(--r-pill);
  background:var(--surface-2); color:var(--muted); border:1px solid transparent;
  white-space:nowrap;
}
.chip-accent{background:var(--accent-soft); color:var(--accent-ink)}
.chip-code{font-family:var(--f-mono); font-size:11px; background:none; padding:4px 0}
.badge{
  font-size:11px; font-weight:600; padding:4px 9px; border-radius:var(--r-pill);
  background:var(--accent); color:var(--accent-contrast); white-space:nowrap; flex:none;
}
.badge-quiet{background:var(--surface-2); color:var(--muted)}

/* сетка показателей внутри карточки */
.metrics{
  display:grid; grid-template-columns:1fr 1fr; gap:12px 14px;
  border-top:1px solid var(--line); padding-top:11px; margin-top:2px;
}
.metric-label{
  font-family:var(--f-mono); font-size:9px; letter-spacing:.07em;
  text-transform:uppercase; color:var(--muted); display:block; margin-bottom:3px; line-height:1.3;
}
.metric-value{font-size:14.5px; font-weight:500; font-variant-numeric:tabular-nums}
.metric-value.ok{color:var(--ok)}
.card-foot{
  display:flex; align-items:center; justify-content:flex-end; gap:8px;
  border-top:1px solid var(--line); margin-top:11px; padding-top:10px;
}
.card-foot .metric-label{margin:0}
.trend{font-weight:600; color:var(--ok); font-variant-numeric:tabular-nums}

/* ============================================================
   Нижняя панель
   ============================================================ */
.tabbar{
  position:absolute; left:0; right:0; bottom:0; z-index:50;
  /* Тоже непрозрачная, по той же причине: на 96% под подписями вкладок
     читались половинки слов — «Серый камень» разрезан верхней кромкой
     панели, его низ висел призраком прямо над «Главная/Архив». */
  background:var(--surface);
  border-top:1px solid var(--line);
  padding-bottom:env(safe-area-inset-bottom);
}
/* Растворение над панелью. В разборе его не рисуем: там эту роль уже
   играет градиент композера, а два тумана подряд дают полосу. */
/* На широком экране полосы держатся той же колонки, что и содержимое.
   Шапка живёт внутри `.shell` и колонкой ограничена сама; нижняя панель и
   композер стоят на absolute от окна и шли от кромки до кромки — заливка и
   линия во всю ширину под колонкой в 600. Верх и низ выглядели собранными
   из разных макетов. Двигать шапку наружу нельзя: она sticky внутри
   прокручиваемой оболочки, а прокрутка здесь выстрадана. */
@media (min-width:601px){
  /* Композера здесь нет намеренно: его базовое правило стоит НИЖЕ по
     файлу, специфичность у них одинаковая, и `left:0` оттуда перебивал
     этот `left:50%`, а `transform` оставался — композер уезжал ровно на
     полширины. Замерено на 1280: полоса на −300…300 при колонке
     340…940, то есть половина поля ввода за левой кромкой экрана, на
     любом экране шире 600. Его колонка задана рядом с ним самим. */
  .tabbar{ left:50%; right:auto; width:600px; transform:translateX(-50%) }
  .tabbar{ border-inline:1px solid var(--line) }
}

body:not([data-pane="chat"]) .tabbar::before{
  content:""; position:absolute; left:0; right:0; bottom:100%; height:22px;
  pointer-events:none;
  background:linear-gradient(to top, var(--bg), color-mix(in srgb, var(--bg) 0%, transparent));
}
.tabbar .inner{max-width:600px; margin:0 auto; display:grid; grid-template-columns:repeat(5,1fr)}
.tab{
  border:0; background:none; cursor:pointer; padding:9px 2px 10px;
  display:flex; flex-direction:column; align-items:center; gap:5px;
  font-family:var(--f-body); font-size:10.5px; color:var(--muted);
}
.tab-ico{
  width:38px; height:32px; border-radius:var(--r-pill);
  display:grid; place-items:center; transition:background .18s, color .18s;
}
.tab-ico svg{width:20px; height:20px}
.tab[aria-selected="true"]{color:var(--accent); font-weight:600}
.tab[aria-selected="true"] .tab-ico{background:var(--accent); color:var(--accent-contrast)}

/* ============================================================
   Общая типографика экранов
   ============================================================ */
.pane{display:none}
.pane.on{display:block; animation:rise .26s ease both}
@keyframes rise{from{opacity:0; transform:translateY(5px)} to{opacity:1; transform:none}}

/* По классу, а не по тегу. Заголовок главной — `<h2 class="screen">`
   (единственный h1 страницы уже занят), и правило его не находило: гротеск
   22.5px вместо Lora вместо 25–31. Единственный заголовок, который видит
   вернувшаяся женщина, был набран не так, как все остальные. */
.screen{
  font-family:var(--f-display); font-weight:600; font-size:clamp(25px,6.6vw,31px);
  letter-spacing:-.015em; line-height:1.15; margin:4px 0 8px; text-wrap:balance;
}
h2.sect{
  font-family:var(--f-display); font-weight:600; font-size:18px; letter-spacing:-.01em;
  margin:28px 0 12px; padding-bottom:9px; border-bottom:1px solid var(--line);
}
h3.sub{font-family:var(--f-display); font-weight:600; font-size:16px; margin:22px 0 8px}
p.lede{color:var(--muted); margin:0 0 20px; max-width:58ch; font-size:14.5px}
.kicker{
  font-family:var(--f-mono); font-size:9.5px; letter-spacing:.08em;
  text-transform:uppercase; color:var(--muted); display:block; margin-bottom:7px;
}
/* Подпись над готовой формулировкой — это заголовок конечного продукта
   приложения. Замерено на 375: «Конфликт · Не защищаться и вернуть
   разговор к своей теме» — 55 знаков, 309×29, ДВЕ строки капслока самым
   мелким кеглем экрана. `.kicker` рассчитан на одно-два слова
   («ПОВТОРЯЕТСЯ», «ПОХОЖЕ, НО НЕ ОНО») и на предложении перестаёт быть
   меткой: капслок, разрядка, моноширинный и перенос разом. Там, где он
   метка, он таким и остаётся — правило узкое. */
.msg-report .verdict > .kicker{
  font-family:var(--f-body); font-size:11.5px; font-weight:500;
  letter-spacing:.02em; text-transform:none;
}
.back{
  background:none; border:0; padding:8px 0; margin-bottom:4px;
  color:var(--muted); font-family:var(--f-body); font-size:14px; cursor:pointer;
}
@media (hover:hover){.back:hover{color:var(--ink)}}
.empty{
  color:var(--muted); font-size:14px; background:var(--surface-2);
  border:1px dashed var(--line-strong); border-radius:var(--r-sm); padding:16px;
}
.rowbtns{display:flex; flex-wrap:wrap; gap:8px; margin:0}

/* ============================================================
   Разбор: переписка и находки
   ============================================================ */
/* ============================================================
   Лента сообщений
   ============================================================ */
/* Без min-height. Пустая лента раздувалась на полэкрана и выталкивала
   всё, что под ней, — предложение подключить модель при первом открытии
   лежало под композером на три четверти (замерено 458–557 при композере
   с 482). Пустого места на этом экране и так хватает: сверху приветствие,
   снизу композер, между ними лента ровно такая, сколько в ней есть. */
.thread{display:flex; flex-direction:column; gap:14px; padding:4px 0 8px}

.msg{max-width:88%; display:flex; flex-direction:column; gap:8px}
.msg-me{align-self:flex-end; align-items:flex-end}
.msg-app{align-self:flex-start; max-width:100%}

.msg-text{
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);
  padding:12px 15px; font-size:14.5px; white-space:pre-wrap; box-shadow:var(--shadow-sm);
}
/* Вставленная переписка — не сообщение собеседнику, а сырой ввод, и
   красить её акцентом нельзя. Замерено на 16 репликах (375×667): пузырь
   даёт 224 862 px² сплошного --accent при 9 472 у кнопки «Скопировать» —
   в двадцать четыре раза больше, и это 94% всего акцента на экране. А
   акцент в этом приложении значит «важно»: им покрашены карточки угрозы.
   Здесь важным оказывалось то, что человек просто скопировала, — вместе
   с ЕГО словами, напечатанными цветом тревоги.

   Вторая цена — перечитывание: белое по кирпичному на пятьсот пикселей
   текста, а перечитывать она будет, потому что находки на него ссылаются.

   «Это моё» держат правое выравнивание и срезанный угол — их достаточно,
   и от сообщений приложения пузырь отличается фоном и рамкой. */
.msg-me .msg-text{background:var(--surface-2); border-color:var(--line-strong); color:var(--ink)}
.msg-app .msg-text{border-bottom-left-radius:6px}
.msg-me  .msg-text{border-bottom-right-radius:6px}

.msg-imgs{display:flex; flex-wrap:wrap; gap:6px; justify-content:flex-end}
.msg-imgs img{
  max-width:190px; max-height:260px; border-radius:var(--r-sm);
  border:1px solid var(--line); object-fit:cover;
}
.msg-report{display:flex; flex-direction:column; gap:12px; width:100%}
/* Кнопка действия под ошибкой модели и сырой ответ сервиса — одной строкой */
.msg-actions{display:flex; gap:8px}
/* Кнопки имён в блоке «Кто здесь ты?» — их столько, сколько людей в
   переписке, и в групповом чате это не два. Замерено на 375: четыре имени
   встают в один ряд до 426 пикселей, то есть четвёртое имя уезжает за
   кромку экрана и пальцем недостижимо, а остальные ужимаются и режут
   текст. Ряд обязан переноситься. Ровно та же оговорка стоит у выбора
   диалога (`.convpick`) и по той же причине. */
.unmarked .msg-actions{flex-wrap:wrap}
.msg-detail{font-family:var(--f-mono); font-size:11.5px; color:var(--muted); word-break:break-word; margin-top:-2px}

.typing{
  display:flex; gap:5px; padding:14px 16px; align-self:flex-start;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);
}
.typing i{width:6px; height:6px; border-radius:50%; background:var(--muted); animation:blip 1.1s infinite}
.typing i:nth-child(2){animation-delay:.15s} .typing i:nth-child(3){animation-delay:.3s}
@keyframes blip{0%,60%,100%{opacity:.3; transform:translateY(0)} 30%{opacity:1; transform:translateY(-3px)}}

.report{display:flex; flex-direction:column; gap:12px; margin-bottom:18px}
.group{
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r-md); overflow:hidden; box-shadow:var(--shadow-sm);
}
/* Заголовок раздела — не метка, а рамка: он один отличает «его приёмы» от
   «за что он может зацепиться», то есть механику ответа от её вины. Был
   9.5px — самым мелким текстом отчёта, мельче даже ссылки «Подробно»
   (13px). 11.5px — кегль, которым в приложении уже набраны мелкие
   моноширинные метки (`.msg-detail`); подпись при нём остаётся в одну
   строку с запасом (замерено: 203 из 326 на 360). */
.group>header{
  display:flex; align-items:center; gap:9px; padding:11px 15px;
  border-bottom:1px solid var(--line);
  font-family:var(--f-mono); font-size:11.5px; letter-spacing:.08em;
  text-transform:uppercase; color:var(--muted);
}
.group>header .count{margin-left:auto; color:var(--ink); font-weight:500; font-variant-numeric:tabular-nums}
.find{
  padding:13px 15px 14px 18px; border-top:1px solid var(--line); position:relative;
  animation:slot .32s cubic-bezier(.16,1,.3,1) both;
}
.find::before{content:""; position:absolute; left:0; top:0; bottom:0; width:3px; background:var(--accent)}
.find[data-sev="mid"]::before{opacity:.55}
.find[data-sev="low"]::before{opacity:.3}
/* Полоса слева — единственный признак стороны, который переживает
   прокрутку: раздел её рисков выше экрана (замерено 762px при 667), и
   заголовок уезжает на −105 при трёх видимых карточках. `--mark-bg` давал
   1.23:1 к белой карточке и 1.76:1 к тёмной — полосы не видно вовсе, и на
   экране оставался список, неотличимый от его приёмов, с карточкой
   «Эмпатия» внутри. Берётся вторая половина той же пары токенов: смысл
   «её сторона» сохраняется, меняется только видимость (11.8:1 и 13.9:1).
   Акцент сюда нельзя: им покрашена угроза, а это не угроза. */
.group.mine .find::before{background:var(--mark-fg); opacity:var(--mine-stripe)}
.find:first-of-type{border-top:0}
.find .name{font-family:var(--f-display); font-weight:600; font-size:16px; margin-bottom:4px}
/* Цитата — то единственное на карточке, что человек может сверить со
   своей перепиской: «он говорит именно так». Была самой мелкой и самой
   светлой строкой карточки — мельче и бледнее и названия приёма (Lora
   16px --ink), и общего определения (14px --ink). Узнавание идёт вперёд
   термина, а не после него. Моноширинный оставлен намеренно: это
   буквальная цитата, и начертание про это и говорит.

   13.5px — не «всё ещё самая мелкая»: мельче неё на карточке стоит ссылка
   «Подробно» (13px). Проверка сперва считала кегль БЕЗ ссылки и требовала
   ровнять цитату с определением — 14px ради полпикселя. Полпикселя не
   видит никто, а цена замерена: `.hit` стоит и в архиве, где это строка
   названий находок, и на 360 она при 14px переставала помещаться — запись
   росла со 137px до 159. Крупнее названия приёма цитата не делается
   намеренно: это другое решение, цена ему тоже замерена (17px обошлись бы
   в 21px на отчёт из четырёх карточек), но принимать его не здесь. */
.find .hit{font-family:var(--f-mono); font-size:13.5px; color:var(--ink-2); margin:2px 0 7px}
.find .why{margin:0 0 7px; font-size:14px}
/* Сохранённая переписка. `pre-wrap` — не украшение: без него абзац
   схлопывает переносы, и «Он: …» с «Я: …» встают в одну строку, то есть
   в датированном доказательстве не видно, где чья реплика. В ленте та же
   переписка показывается через `.msg-text`, и там переносы живут. */
.find .item-text{white-space:pre-wrap}
.find .fix{
  margin:0; font-size:14px; color:var(--ok-ink); background:var(--ok-soft);
  padding:9px 12px; border-radius:var(--r-xs);
}
.find .more{
  display:inline-block; margin-top:9px; font-size:13px; color:var(--accent-ink);
  text-decoration:underline; text-underline-offset:3px;
}
@keyframes slot{from{opacity:0; transform:translateY(7px)} to{opacity:1; transform:none}}

/* угрозы — единственное место, где цвет работает как сигнал.
   Заливка плашки почти равна фону (1.06:1 в светлой, 1.22:1 в тёмной), так что
   держит блок именно левая линия. При скруглении со всех сторон она обрезалась
   и читалась полумесяцем, а не линией: слева углы теперь прямые. */
.alarm{
  background:var(--accent-soft); border:1px solid var(--accent-border);
  border-left:4px solid var(--accent);
  border-radius:0 var(--r-md) var(--r-md) 0; padding:15px 16px;
}
.alarm h3{font-family:var(--f-display); margin:0 0 7px; font-size:16px; color:var(--accent-ink)}
.alarm p{margin:0 0 8px; font-size:14px} .alarm p:last-child{margin:0}
.alarm ol{margin:0; padding-left:19px; font-size:14px}

.verdict{
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r-md); padding:15px 16px; box-shadow:var(--shadow-sm);
}
.draft{
  font-family:var(--f-body); white-space:pre-wrap; background:var(--surface-2);
  border:1px solid var(--line); border-radius:var(--r-xs);
  padding:13px 14px; margin:0 0 11px; font-size:14.5px;
}
.draft b{background:var(--mark-bg); color:var(--mark-fg); font-weight:500; border-radius:3px; padding:0 3px}
.note{font-size:13.5px; color:var(--muted); margin:11px 0 0}

/* Запрос в свой чат — запасной путь, а не главное действие: отделён
   от отчёта, подпись мелкая, кнопки контурные. Цели касания те же
   44px, что и у остальных: кнопка под пальцем не знает, что она
   второстепенная. Ссылка выглядит кнопкой, но остаётся ссылкой —
   на телефоне это единственный способ попасть в приложение чата. */
.handover{margin:12px 0 0; padding:11px 2px 0; border-top:1px solid var(--line)}
.handover > summary{
  list-style:none; cursor:pointer; display:flex; align-items:center; gap:7px;
  min-height:44px; font-family:var(--f-mono); font-size:12px; color:var(--muted);
}
.handover > summary::-webkit-details-marker{display:none}
.handover > summary::after{content:"+"; font-size:15px; line-height:1}
.handover[open] > summary::after{content:"–"}
.handover .sign{margin:0 0 10px; display:block; line-height:1.5}
.handover .rowbtns a.btn{text-decoration:none}

/* ============================================================
   Уровень доказательности
   ============================================================ */
.ev{
  display:inline-flex; align-items:center; gap:6px; font-family:var(--f-mono);
  font-size:9.5px; letter-spacing:.06em; text-transform:uppercase;
  border:1px solid var(--line-strong); border-radius:var(--r-pill);
  padding:4px 10px; color:var(--muted);
}
.ev::before{content:""; width:6px; height:6px; border-radius:50%; background:var(--line-strong)}
.ev[data-ev="исследования"]::before{background:var(--ok)}
.ev[data-ev="практика специалистов"]::before{background:var(--muted)}
.ev[data-ev="опыт сообщества"]::before{background:var(--line-strong)}

.warnbox{
  background:var(--warn-soft); color:var(--warn-ink); border-radius:var(--r-xs);
  padding:12px 14px; font-size:13.5px; margin:12px 0;
}

/* «Похоже, но не оно» — где похожее поведение приёмом не является.
   Намеренно спокойный, не тревожный блок: это уточнение, а не сигнал. */
.notbox{
  background:var(--surface-2); border-left:3px solid var(--line-strong);
  border-radius:var(--r-xs); padding:12px 14px; margin:14px 0;
}
.notbox p{margin:0; font-size:13.5px; color:var(--ink-2)}
.notbox .kicker{margin-bottom:6px}

/* ============================================================
   Композер
   ============================================================ */
/* Только строка ввода и прикреплённые снимки. Всё остальное — чипы целей,
   предложение подключить модель, умный разбор, обещание про приватность —
   уехало в ленту: вместе они занимали 274px из 352, то есть обвес был втрое
   больше самого поля. */
.composer{
  position:absolute; left:0; right:0; bottom:var(--tabbar-h, 0px); z-index:45;
  /* Подложка НЕПРОЗРАЧНА, как у шапки и нижней панели. Был градиент от
     прозрачного к фону с полной непрозрачностью только к 55% высоты — а
     белая пилюля поля начинается на 12-м пикселе: вокруг неё просвечивало
     то, что под ней, и карточка разбора читалась обрезанной по кромке, с
     красной полоской важности сбоку от пилюли. Растворение делает ::before
     НАД композером, не меняя его высоты: её меряет js/layout.js. */
  background:var(--bg);
  padding:12px 16px 8px;  /* безопасную зону снизу держит таббар под композером */
}
/* Та же колонка, что у содержимого и нижней панели. Стоит ЗДЕСЬ, а не в
   общем медиазапросе выше: там правило перебивалось базовым `left:0` из
   блока над ним, и композер уезжал на полширины влево. */
@media (min-width:601px){
  .composer{ left:50%; right:auto; width:600px; transform:translateX(-50%) }
}
.composer::before{
  content:""; position:absolute; left:0; right:0; bottom:100%; height:24px;
  pointer-events:none;
  background:linear-gradient(to top, var(--bg), color-mix(in srgb, var(--bg) 0%, transparent));
}
.composer .inner{max-width:600px; margin:0 auto}
.goals{display:flex; gap:7px; overflow-x:auto; padding-bottom:9px; scrollbar-width:none}
.goals::-webkit-scrollbar{display:none}
.goals .chip{cursor:pointer; border:1px solid var(--line); background:var(--surface)}
.goals .chip[aria-pressed="true"]{background:var(--accent); border-color:var(--accent); color:var(--accent-contrast); font-weight:500}
/* Строка чата: плюс слева, поле, отправка справа */
.chatbar{
  display:flex; gap:6px; align-items:flex-end; background:var(--surface);
  border:1px solid var(--line); border-radius:var(--r-pill);
  padding:5px; box-shadow:var(--shadow);
}
/* Пока текст в одну строку — скруглять пилюлей; как только поле выросло,
   углы смягчаются, иначе многострочный ввод выглядит стадионом. */
.chatbar:has(textarea:not(:placeholder-shown)){border-radius:var(--r-lg)}
.chat-plus, .chat-send{
  flex:none; width:44px; height:44px; padding:0; border-radius:50%;
  display:grid; place-items:center; cursor:pointer; border:1px solid transparent;
}
.chat-plus{background:var(--surface-2); color:var(--ink-2)}
@media (hover:hover){.chat-plus:hover{background:var(--line); color:var(--ink)}}
.chat-send{background:var(--accent); border-color:var(--accent); color:var(--accent-contrast)}
@media (hover:hover){.chat-send:hover{opacity:.9}}
.chat-plus svg, .chat-send svg{width:19px; height:19px}
/* ШЕСТНАДЦАТЬ ПИКСЕЛЕЙ — НЕ ВКУСОВЩИНА.

   Safari на айфоне увеличивает всю страницу при нажатии на любое поле,
   где шрифт меньше 16px. Порог жёсткий, обойти его нельзя, а масштаб
   обратно Safari не возвращает — страница так и остаётся увеличенной.

   Дальше это ломало раскладку: при масштабе больше единицы visualViewport
   отдаёт схлопнутую высоту, подъём полосы считается от неё, и композер
   уезжал за верхнюю кромку. То есть «поле мельче 16px» и «композер улетает
   при нажатии» — одна поломка, и начинается она здесь. Было 15px.

   Любое новое поле ввода в приложении обязано быть не мельче; это сторожит
   проверка в layout-check. */
textarea, input:not([type=file]):not([type=hidden]):not([type=checkbox]):not([type=radio]){
  font-size:16px;
}
textarea{
  flex:1; border:0; resize:none; background:transparent; color:var(--ink);
  font-family:inherit; padding:11px 2px; max-height:140px; min-height:44px;
  line-height:1.45; box-sizing:border-box;
}
textarea:focus{outline:none}

/* ============================================================
   Клавиатура открыта.

   Экран ужимается примерно втрое: из 667 остаётся около 336. Всё, что
   не строка ввода и не лента, в этом состоянии только мешает — человек
   печатает, а не выбирает раздел и не читает обещания. Панель уходит,
   обвес под лентой уходит, композер встаёт вплотную над клавиатурой.
   ============================================================ */
:root.kb-open .tabbar{display:none}
:root.kb-open .belowthread{display:none}
:root.kb-open .composer{bottom:0; padding-top:8px}
/* Экран ужат втрое, а обещание к этому моменту уже прочитано: держать под
   полем 34px ради контрола, который переопределяет то, что и так
   определяется само, — плохая сделка. Окно чтения 153px против 193. */
:root.kb-open .composer .slot{display:none}

/* Убрать клавиатуру, не отправляя написанное. Появляется только когда
   клавиатура открыта: иначе это кнопка, которая ничего не делает.

   Прицеплена к композеру абсолютом, а не стоит в своей системе координат:
   композер уже считается по визуальному вьюпорту, и отстать от него кнопка
   не может. Отсчитанная отдельно, через --composer-h, она уезжала на
   top −123 — за верхнюю кромку экрана, невидимая и ненажимаемая. */
.kb-down{
  display:none; position:absolute; z-index:46;
  left:50%; top:-52px; transform:translateX(-50%);
  width:44px; height:44px; padding:0; border-radius:50%;
  border:1px solid var(--line-strong); background:var(--surface); color:var(--ink-2);
  box-shadow:var(--shadow); cursor:pointer; place-items:center;
}
/* Кнопка поднята над композером на 52px и лежит поверх страницы, а низ
   ленты рассчитан только на композер: замерено при клавиатуре на 375×667
   — кнопка занимает 242–286, а лента кончается на 313, то есть кнопка
   садится прямо на последние строки разбора. Это единственное состояние,
   где экран ужат втрое и каждая строка на счету. 60 = подъём 52 + зазор. */
:root.kb-open .shell{padding-bottom:calc(var(--composer-h, 90px) + 60px)}
:root.kb-open .kb-down{display:grid}
.kb-down svg{width:21px; height:21px}
@media (hover:hover){.kb-down:hover{background:var(--surface-2); color:var(--ink)}}

/* Обвес под лентой: то, что раньше висело в композере. Прокручивается вместе
   с разбором, поэтому не претендует на экран, пока человек читает. */
.belowthread{display:flex; flex-direction:column; gap:var(--s-3); margin-top:var(--s-5)}
.belowthread:empty{display:none}
.belowthread .goals{margin:0}

/* Пустой блок вложений съедал 8px в каждом состоянии: [hidden] — стиль
   браузера, он слабее класса .attachments{display:flex}, и атрибут ничего
   не прятал. */
.attachments[hidden]{display:none}

/* ------------------------------------------------------------
   Подпись под полем: одна строка на две работы.
   Поле пустое — обещание, ради которого человек решается вставить
   переписку. Появился текст — чипы целей: цель выбирают перед
   отправкой, а пока пусто, выбирать нечего. Обмен идёт на :has(),
   тем же приёмом, каким рядом скругляется .chatbar.
   ------------------------------------------------------------ */
.composer .slot{min-height:34px; margin-top:6px; display:flex; align-items:center}
.composer .slot > *{margin:0; width:100%; min-width:0}
.composer .slot .goals{
  display:none; padding:0; gap:6px;
  /* Пять чипов не влезают: 524px при 343 доступных. Видно три из пяти на
     375 и 360. Обрез должен быть виден, иначе последние чипы для человека
     не существуют вовсе. */
  -webkit-mask-image:linear-gradient(to right, #000 calc(100% - 28px), transparent);
          mask-image:linear-gradient(to right, #000 calc(100% - 28px), transparent);
}
/* 44px — то же правило, что у всех кнопок приложения: у чипа есть своя
   поверхность (фон и рамка), значит он под правило попадает. Было 34, и не
   мерил их никто: чипы показываются только при непустом поле, а обход целей
   пальца ходил по экрану с очищенным — целый ряд кнопок мимо прибора.
   Цена замерена и мала: композер в этом состоянии и так прыгает с 77 на 117,
   когда она набирает первый знак после отчёта; здесь прибавляется 10. */
.composer .slot .goals .chip{min-height:44px; padding:6px 13px; font-size:12.5px}
.composer .privacy{font-size:12.5px; line-height:1.25; margin:0; text-align:center}
.composer .inner:has(#chat-input:not(:placeholder-shown)) .slot .goals{display:flex}
.composer .inner:has(#chat-input:not(:placeholder-shown)) .slot .privacy{display:none}
/* …кроме того случая, когда менять обещание не на что. До первого отчёта
   чипы целей спрятаны (applyOffers), и правило выше оставляло под полем
   тридцать четыре пикселя пустоты ровно тогда, когда женщина вставила
   переписку и решает, отправлять ли. Метку ставит applyOffers. */
.composer.no-goals .inner:has(#chat-input:not(:placeholder-shown)) .slot .privacy{display:block}

/* Обещание приватности живёт под полем ровно до первого разбора.

   Дальше оно прочитано, а полоса под композером продолжает занимать сорок
   пикселей — на телефоне это две строки самого сообщения, которых из-за неё
   не видно. Прячется весь слот, а не только текст: пустой слот держит
   min-height и отступ, то есть занимает место, ничего не показывая.

   Чипы целей это не задевает: они появляются, когда в поле есть текст, и
   тогда слот снова нужен. */
:root.thread-filled .composer .inner:has(#chat-input:placeholder-shown) .slot{display:none}

/* Выбор диалога в ленте вместо серого системного окна. */
.convpick p{margin:0 0 var(--s-2); font-size:14px}
.convpick .msg-actions{flex-wrap:wrap}
.convnew{display:flex; gap:var(--s-2); margin-top:var(--s-2)}
.convnew input{
  flex:1; min-width:0; min-height:44px; padding:0 var(--s-3);
  border:1px solid var(--line-strong); border-radius:var(--r-pill);
  background:var(--surface); color:var(--ink); font-family:inherit;
}
.convnew input:focus{outline:2px solid var(--accent); outline-offset:1px}

/* Поле для ключа модели — в самом блоке способа, а не в системном окне.

   Кегль 16px обязателен и не наследуется случайно: Safari на айфоне
   увеличивает страницу при фокусе на поле мельче 16px и масштаб обратно
   НЕ возвращает, а увеличенная страница ломает раскладку — у неё
   `visualViewport.height` схлопывается пропорционально масштабу. */
.keyform{display:flex; flex-wrap:wrap; gap:var(--s-2); margin-top:var(--s-3)}
.keyform input{
  flex:1; min-width:0; min-height:44px; padding:0 var(--s-3); font-size:16px;
  border:1px solid var(--line-strong); border-radius:var(--r-pill);
  background:var(--surface); color:var(--ink); font-family:inherit;
}
.keyform input:focus{outline:2px solid var(--accent); outline-offset:1px}
/* Отказ говорится строкой под полем, а не системным окном: набранное
   остаётся на месте, и видно, что именно правится. */
.keyform .keyerr{
  flex-basis:100%; margin:0; min-height:0;
  font-size:13px; color:var(--accent-ink);
}
.keyform .keyerr:empty{display:none}

/* Поле имени диалога в архиве — там же, где кнопка, а не в системном окне.
   Кегль 16px по той же причине, что и у поля ключа. */
.nameform{display:flex; flex-wrap:wrap; gap:var(--s-2); margin-top:var(--s-2)}
.nameform input{
  flex:1 1 100%; min-width:0; min-height:44px; padding:0 var(--s-3); font-size:16px;
  border:1px solid var(--line-strong); border-radius:var(--r-pill);
  background:var(--surface); color:var(--ink); font-family:inherit;
}
.nameform input:focus{outline:2px solid var(--accent); outline-offset:1px}

/* Подложка в шаблоне — настоящее поле, и выглядеть должна как поле.

   Прежде это были просто жёлтые буквы: человек тапала, ничего не
   происходило, и в буфер уходил текст с квадратными скобками. Пунктирный
   низ говорит «сюда писать», а не «тут выделено». */
.draft .fillme{
  background:var(--mark-bg); color:var(--mark-fg);
  border-bottom:1.5px dashed currentColor; border-radius:3px;
  padding:0 3px; font-weight:500; cursor:text;
  /* Заполненная подложка перестаёт звать: дело сделано. */
  &:focus{outline:2px solid var(--accent); outline-offset:1px}
}

/* Разбор без подписей: сказано, чего приложение НЕ сделало.

   Приглушённая рамка, а не тревожная: это не беда, а ограничение, и
   пугать им человека, которая только что вставила свою переписку, нельзя.
   Но и потеряться среди находок оно не должно — отсюда своя рамка. */
.group.unmarked{
  border:1px dashed var(--line-strong); border-radius:var(--r-md);
  padding:var(--s-3); background:var(--surface-2);
}
.group.unmarked .name{color:var(--ink-2)}
.group.unmarked .why{margin-top:var(--s-1)}

/* Пустая строка подключения не должна оставлять за собой отступ: зазор
   колонки отдаётся даже нулевому ребёнку — 30px пустоты там, где ничего
   не нарисовано. */
#connectbar:empty{display:none}

/* Прикреплённые скриншоты до отправки */
.attachments{display:flex; gap:7px; flex-wrap:wrap; padding-bottom:8px}
.att{position:relative; width:58px; height:58px; border-radius:var(--r-sm); overflow:hidden;
  border:1px solid var(--line); background:var(--surface-2)}
.att img{width:100%; height:100%; object-fit:cover; display:block}
.att-x{
  position:absolute; top:2px; right:2px; width:26px; height:26px; padding:0;
  border:0; border-radius:50%; background:rgba(0,0,0,.62); color:#fff;
  font-size:15px; line-height:1; cursor:pointer; display:grid; place-items:center;
}
#chat.dropping{outline:2px dashed var(--accent); outline-offset:8px; border-radius:var(--r-md)}
/* Единственная строка, отвечающая на «безопасно ли по ней нажимать», а
   набрана была самым мелким кеглем на экране — мельче дисклеймера (12px).
   Экран часто смотрят с общего телефона; обещание должно быть читаемым. */
.privacy{
  font-size:12.5px; color:var(--muted); text-align:center;
  margin:var(--s-3) 0 0; text-wrap:balance;
}

/* Варианты подключения на экране настроек */
.way{
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);
  padding:14px 15px; margin:10px 0; box-shadow:var(--shadow-sm);
}
/* Перенос разрешён: плашка обещания переезжает на свою строку целиком,
   а не ломает пополам заголовок способа. */
.way-head{display:flex; align-items:center; gap:8px; margin-bottom:var(--s-2);
  font-size:15px; flex-wrap:wrap}
/* 12px, а не 9. Это та самая строка, что отвечает на «безопасно ли по ней
   нажимать»: такое же обещание под полем ввода уже подняли до 12.5px по той
   же причине, а здесь оно оставалось самым мелким кеглем экрана — и на 375
   переносилось на вторую строку, разрывая заголовок «Скачать модель на /
   устройство». */
.way-tag{
  font-family:var(--f-mono); font-size:12px; letter-spacing:.07em; text-transform:uppercase;
  background:var(--accent-soft); color:var(--accent-ink); padding:3px 8px;
  border-radius:var(--r-pill); white-space:nowrap;
}
/* Со шкалы, а не с потолка: было 6, 9 и 10 пикселей подряд — разница в
   пиксель глазом не читается как иерархия, а согласованность стоит. */
.way p{margin:0 0 var(--s-3); font-size:13.5px}
.steps{margin:0 0 var(--s-3); padding-left:20px; font-size:13.5px}
.steps li{margin-bottom:4px}
.steps a{color:var(--accent-ink)}
.way-off{opacity:.72}
.local-slot .bar{height:4px; border-radius:2px; background:var(--surface-2); margin:22px 0 6px; position:relative}
.local-slot .bar i{display:block; height:100%; background:var(--accent); border-radius:2px; transition:width .3s ease; position:relative}
.local-slot .status{font-size:12px; color:var(--muted)}

/* Подключение модели — прямо в чате, где и возникает потребность,
   а не на служебном экране под шестерёнкой. */
.connectbar{margin-top:9px}
#home-connect{margin:0 0 16px}
#home-connect:empty{display:none}
.connectbar .offer{
  display:flex; align-items:center; gap:10px;
  background:var(--accent-soft); border:1px solid var(--accent-border);
  border-radius:var(--r-sm); padding:10px 12px;
}
.connectbar .offer p{margin:0; font-size:12.5px; color:var(--accent-ink); flex:1}
/* 44px, как у всего остального в приложении (.btn-sm, .iconbtn, кружки
   композера, вкладки). Было 31: у .btn-sm в этом же файле записано, что
   самые разрушительные действия были самыми мелкими целями и это чинили —
   здесь та же ошибка наоборот, самыми мелкими целями были самые ценные.
   Этот блок в памяти проекта назван тем, что решает, появится ли у
   человека собеседник; промах по нему стоит дороже промаха по «Удалить». */
.connectbar .offer button{
  flex:none; font-family:var(--f-body); font-size:12.5px; font-weight:500; cursor:pointer;
  background:var(--accent); border:1px solid var(--accent); color:var(--accent-contrast);
  border-radius:var(--r-pill); min-height:44px; padding:0 16px;
}
@media (hover:hover){.connectbar .offer button:hover{opacity:.9}}
.connectbar .done{
  font-size:11.5px; color:var(--ok); text-align:center;
  display:flex; gap:8px; justify-content:center; align-items:center;
}
.connectbar .done button{
  border:0; background:none; padding:0; font-family:var(--f-body);
  font-size:11.5px; color:var(--muted); text-decoration:underline;
  text-underline-offset:3px; cursor:pointer;
}

/* Включение смыслового разбора и его прогресс */
.smart{margin-top:9px; text-align:center}
.smart button{
  font-family:var(--f-body); font-size:12.5px; cursor:pointer;
  border:1px solid var(--line); background:var(--surface); color:var(--ink-2);
  border-radius:var(--r-pill); min-height:44px; padding:0 18px;
}
@media (hover:hover){.smart button:hover{background:var(--surface-2)}}
.smart .bar{height:4px; border-radius:2px; background:var(--surface-2); margin:22px 0 5px; position:relative}
.smart .bar i{display:block; height:100%; background:var(--accent); border-radius:2px; transition:width .3s ease; position:relative}

/* ------------------------------------------------------------
   Маскот на полосе загрузки

   Он сидит ВНУТРИ заливки, на её переднем крае, — и едет сам, без второго
   счётчика процентов: заливка уже знает, сколько скачано. Отсюда и
   `overflow` у полосы снят, а скругление осталось на самой заливке: иначе
   зверёк обрезался бы по четырём пикселям высоты.

   Бежит он там, где бег что-то значит. Слой смысла — это 112 мегабайт и
   минуты ожидания, про которые в памяти проекта записано, что человек
   смотрит на неподвижную полосу и решает, что всё зависло. Ни на разборе,
   ни в тесте, ни на экране восстановления его нет и быть не должно.
   ------------------------------------------------------------ */
.pet{
  position:absolute; right:-10px; bottom:1px; width:20px; height:20px;
  color:var(--ink-2); pointer-events:none;
}
.pet svg{display:block; width:100%; height:100%}
.bar i .pet{animation:pet-hop .62s ease-in-out infinite}
@keyframes pet-hop{
  0%,100%{transform:translateY(0) rotate(-4deg)}
  50%{transform:translateY(-3px) rotate(4deg)}
}
/* Уставшему от движения экран не дёргается: зверёк просто едет с полосой. */
@media (prefers-reduced-motion:reduce){
  .bar i .pet{animation:none}
}
/* Вне полосы он стоит в строке, а не висит: `position:absolute` там
   вырвало бы его из потока и посадило в угол коробки.

   Селектор `.smart .on`, а НЕ голый `.on`: `on` — это класс активной
   ПАНЕЛИ (`.pane.on`), и правило по нему ловило каждого зверька на
   экране, в том числе того, что едет на полосе. Поймано на снимке: он
   висел под полосой вместо того, чтобы стоять на ней. */
.smart .on .pet, .empty .pet, .petsay .pet, .q-nav .pet{
  position:static; display:inline-block; vertical-align:-5px; margin-right:6px;
}
.empty .pet{width:26px; height:26px; vertical-align:-7px}

/* ---------- Зверёк говорит ----------
   Экран подключения — последний шаг воронки и единственное место
   приложения, где на человека сыплются чужие слова: ключ, API, аккаунт.
   Зверёк рядом с объяснением переводит его из инструкции в разговор.
   Он крупнее обычного и стоит НАД строкой, а не в ней: три предложения
   в строку с ним не поместятся, а разорванные вокруг него — читаются
   хуже сплошных. */
.petsay{
  display:flex; gap:10px; align-items:flex-start;
  margin:0 0 var(--s-3); padding:var(--s-3);
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-lg);
}
.petsay .pet{width:34px; height:34px; flex:none; margin:0; vertical-align:0}
.petsay p{margin:0; font-size:14px; line-height:1.5; color:var(--ink)}

/* ---------- Зверёк у композера ----------
   Места он не стоит ни пикселя: absolute вынут из потока, --composer-h
   от него не меняется (замерено — 116 и 116, отступ ленты 201 и 201).
   Над кромкой композера у ленты ровно 16px резерва, поэтому потолок
   подъёма — 4px: покой на −10, самый высокий кадр на −14.

   Последним ребёнком композера и без своего z-index: так он рисуется
   поверх растворения `.composer::before`, иначе уши линяют под
   градиентом. Лапы ему закрывает пилюля поля — у неё свой слой. */
.pet-chat{
  left:16px; right:auto; top:-10px; bottom:auto; width:38px; height:38px;
  transform-origin:50% 100%; pointer-events:auto; touch-action:manipulation;
  animation:pet-breathe 3s ease-in-out infinite alternate, pet-call 14s ease-out 2;
  /* Хвост здесь не рисуется: над кромкой композера видно 22px, тело
     закрыто пилюлей поля, и хвост обрывался бы пилюлей на середине
     дуги — рядом со щекой оставалась тупая закорючка. Символ при этом
     один на все места: свойство наследуется внутрь `<use>`, и в тесте,
     где зверёк виден целиком, хвост остаётся. Поймано снимком. */
  --pet-tail:transparent;
}
.chatbar{position:relative; z-index:1}

/* Где его нет. Не на разборе — нет вовсе; при открытой клавиатуре —
   экран ужат втрое, там каждый пиксель чужой; ПОСЛЕ ПЕРВОГО ОТЧЁТА —
   потому что правило не «его нет на разборе», а «его нет рядом с
   находками». `recovering` — самый страшный экран приложения. */
body:not([data-pane="chat"]) .pet-chat,
:root.kb-open .pet-chat,
:root.recovering .pet-chat{display:none}
:root.report-shown .pet-chat{animation:pet-leave .26s ease-in forwards}

/* Есть текст в поле — зверёк наклоняется к нему и дышит тише: работа
   идёт, он смотрит. Хук чистый css, той же формы, что и у чипов цели. */
.composer:has(#chat-input:not(:placeholder-shown)) .pet-chat{
  animation:pet-peek 3.6s ease-in-out infinite alternate;
}
/* Идёт разбор — тот же бег, что на полосе загрузки. */
:root.pet-busy .pet-chat{animation:pet-hop .62s ease-in-out infinite}
/* Нажали — приседание и подскок. Сильнее всех прочих состояний. */
.pet-chat.pet-tap{animation:pet-tap .48s cubic-bezier(.34,1.3,.64,1)}

@keyframes pet-breathe{
  from{transform:translateY(0)}
  to{transform:translateY(-1.5px)}
}
/* «Зовёт» ровно дважды за заход и только на пустом экране. Бесконечное
   привлечение внимания рядом с текстом, который надо читать, — это
   метроном, и бьёт он по тем же людям, ради которых заведено. */
@keyframes pet-call{
  0%,38%,50%,100%{transform:translateY(0)}
  41%{transform:translateY(-5px)}
  44%{transform:translateY(0)}
  47%{transform:translateY(-3px)}
}
@keyframes pet-peek{
  from{transform:rotate(7deg) translate(2px, 0)}
  to{transform:rotate(7deg) translate(2px, -1px)}
}
@keyframes pet-tap{
  0%{transform:scaleX(1.12) scaleY(.88)}
  40%{transform:translateY(-6px)}
  100%{transform:translateY(0)}
}
@keyframes pet-leave{
  from{transform:translateY(0); opacity:1}
  to{transform:translateY(10px); opacity:0; visibility:hidden}
}

/* ---------- Зверёк в тесте ----------
   Флекс-элементом ряда, а не абсолютом: замерено, ряд остаётся 52px, а
   на 375×568 до нижней панели от него всего 21px — всё, что добавит
   высоту, ляжет под панель. `margin-left:auto` переезжает с кнопки на
   зверька, иначе они оба уедут вправо и слипнутся. */
.q-nav .pet{
  display:block; width:30px; height:30px; flex:none; margin:0 0 0 auto;
  vertical-align:0; pointer-events:auto; touch-action:manipulation;
  transform-origin:50% 100%;
}
.q-nav:has(.pet) .q-next{margin-left:0}
.pet-in{animation:pet-in .26s ease-out, pet-hop .62s ease-in-out .26s 1}
.pet-nod{animation:pet-nod .36s ease-in-out}
.pet-quiz.pet-tap{animation:pet-tap .48s cubic-bezier(.34,1.3,.64,1)}
@keyframes pet-in{
  from{transform:translateY(6px); opacity:0}
  to{transform:translateY(0); opacity:1}
}
@keyframes pet-nod{
  0%,100%{transform:rotate(0)}
  30%{transform:rotate(-6deg)}
  70%{transform:rotate(6deg)}
}

/* Уставшему от движения зверёк остаётся рисунком: он нигде не
   единственный способ что-то понять, и потерять с ним нечего.

   Гасить его анимации тут НЕ НАДО — ниже в этом же файле стоит общее
   правило `*{animation:none !important}`, и оно накрывает всех. Первая
   попытка их всё-таки перечисляла, и проверка на неё не краснела бы
   никогда: убери весь блок — общее правило по-прежнему гасит движение.

   А вот УХОД зверька с отчётом общее правило как раз ломает: уходит он
   анимацией `pet-leave`, которая кончается `visibility:hidden`, — и
   погашенная, она оставляла бы зверька рядом с находкой «прямая
   угроза». Поэтому здесь ровно одна строка, и она про это. */
@media (prefers-reduced-motion:reduce){
  :root.report-shown .pet-chat{display:none}
}
.smart .status{font-size:11px; color:var(--muted)}
.smart .on{color:var(--ok); font-size:11.5px}

/* Находка по смыслу: помечаем, что это похожесть, а не точная фраза */
.bymeaning{
  font-family:var(--f-mono); font-size:10.5px; letter-spacing:.04em;
  color:var(--muted); margin:0 0 7px;
}

/* ============================================================
   Архив
   ============================================================ */
.convlist{display:flex; flex-direction:column; gap:10px}
.linkbtn{
  margin-left:auto; border:0; background:none; padding:6px 0; min-height:24px;
  font-family:var(--f-body); font-size:11.5px; color:var(--muted);
  text-decoration:underline; text-underline-offset:3px; cursor:pointer;
}
/* У `.linkbtn` две роли. Три из четырёх — слово внутри строки («Тест уже
   пройден. Пройти заново»), и палец там целится в строку, а не в кнопку.
   Четвёртая, «Удалить» в шапке записи архива, стоит одна и справа: это
   самостоятельная кнопка, и мимо 26px палец промахивается ровно так же,
   как мимо пилюли той же высоты. Цель растится до 44, вид остаётся
   ссылочным — поверхности у неё по-прежнему нет. Запись от этого не
   становится выше: в шапке уже было 49px при кнопке в 26, отступы
   ужимаются на ту же величину (замерено: 49 до, 51 после). */
#archive .group > header{padding-top:3px; padding-bottom:3px}
#archive .group > header .linkbtn{min-height:44px; padding:0}
@media (hover:hover){.linkbtn:hover{color:var(--ink)}}
/* Вход в тест с главной — цель пальца, а не строчка текста. Был 26px при
   44 у всего остального в приложении, и рядом с ним «Новый разбор» во всю
   ширину: самый дешёвый первый шаг выглядел самым мелким. Правило узкое —
   `.linkbtn` в ленте разбора и в архиве остаётся строчной ссылкой. */
#home-quiz .linkbtn{min-height:44px; display:inline-flex; align-items:center}

/* ============================================================
   Тест — один вопрос на экран

   Кнопка «Дальше» стоит на одном месте и не прыгает вслед за длиной
   отклика: под стрессом переезжающая кнопка читается как сбой. Цели
   касания — 48 и 52 px вместо прежних 38: промах по варианту ответа
   на телефоне стоит дороже, чем лишние десять пикселей.
   ============================================================ */
/* Заголовок раздела и лид — во весь рост только на входе. Внутри воронки они
   не меняются двенадцать экранов подряд, а вопрос при этом на 40% мельче:
   взгляд под стрессом идёт на самое крупное, то есть каждый раз на подпись.
   Замер на 375×667: шапка занимала 224px из 667, вопрос начинался на 285-м. */
#quiz:has(.q-txt) h1.screen,
#quiz:has(.q-h) h1.screen{
  font-size:15px; font-weight:600; letter-spacing:0; line-height:1.3;
  color:var(--muted); margin:0 0 var(--s-3);
}
#quiz:has(.q-txt) .lede,
#quiz:has(.q-h) .lede{display:none}
/* …кроме самого первого экрана. Правило выше прячет лид при наличии вопроса,
   а вопрос на экране есть ВСЕГДА — то есть лид не видел никто и никогда, и
   вместе с ним пропало единственное место, где названа длина теста и сказано,
   что ответы остаются на устройстве. Заявленная длина — самый сильный
   измеренный рычаг на входе в опрос. */
/* Отступ сверху, а не снизу: лид переехал под вопрос (см. index.html).
   Отступ и длина текста считаны: на 360×640 весь входной экран вместе с
   лидом обязан уложиться до нижней панели, иначе обещание, ради которого
   он тут стоит, кончается под ней. */
#quiz[data-step="start"] .lede{display:block; margin:var(--s-3) 0 0}

/* Полоса прогресса: разница между «отвечено» и «нет» была 1.27:1 при норме
   3:1 — в тёмной теме двенадцать сегментов сливались в одну линию. */
/* Одна линия, без делений: делений двенадцать — это и есть «сколько
   вопросов», а полоса не должна их называть. Ход нелинейный, считается в
   js/app.js; переход плавный, чтобы прирост читался как движение, а не как
   подмена картинки. */
.qbar{
  height:5px; border-radius:2.5px; background:var(--line-strong);
  margin:0 0 var(--s-3); overflow:hidden;
}
.qbar i{
  display:block; height:100%; width:0; border-radius:2.5px;
  background:var(--accent); transition:width .45s cubic-bezier(.22,.61,.36,1);
}
/* Номер вопроса — единственный буквальный ответ на «где я», а был самой
   мелкой типографикой экрана (11px --muted). Полоса выше теперь набрана
   --ink-2; номер берёт тот же цвет, и пара «полоса + номер» читается одним
   узлом навигации, а не подписью к нему. */
.qnum{
  font-family:var(--f-mono); font-size:12.5px; color:var(--ink-2);
  letter-spacing:.08em; margin:0 0 var(--s-3);
}
.q-resume{font-size:12.5px; color:var(--muted); margin:0 0 var(--s-2)}
.q-resume .linkbtn{margin-left:0}
.q-txt{
  font-family:var(--f-display); font-size:22px; line-height:1.3;
  margin:0 0 var(--s-4); outline:none;
}
.q-txt:focus-visible{outline:2px solid var(--accent); outline-offset:4px}
.opts{display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s-2)}
/* Пилюля, как у всех кнопочных поверхностей приложения (.btn, .iconbtn,
   .chip, .badge, вкладки, композер, .way-tag). Кнопки теста были
   единственным исключением на весь файл — а первый экран приложения это
   первый вопрос теста, то есть знакомство шло по форме, которой больше
   нигде нет. Геометрия не меняется: 109×48 остаётся 109×48. */
.opts button{
  font-family:var(--f-body); font-size:14px; min-height:48px; padding:9px 4px;
  cursor:pointer; border:1px solid var(--line-strong); border-radius:var(--r-pill);
  background:var(--surface); color:var(--ink);
}
.opts button[aria-pressed="true"]{
  background:var(--accent); border-color:var(--accent);
  color:var(--accent-contrast); font-weight:600;
}
.q-feedback{min-height:76px; margin:var(--s-3) 0 0}
.q-feedback p{margin:0 0 6px; font-size:14px} .q-feedback p:last-child{margin:0}
.q-dim{color:var(--muted); font-size:13px}
/* Подпись про частоту объясняет варианты ответа, а стояла от них на тех же
   13px, что и до отклика, — то есть висела третьим элементом сама по себе.
   Прижата к вариантам: это подпись к ним, а не строка экрана. */
.q-scale{margin:var(--s-2) 0 0; line-height:1.4}
.q-nav{display:flex; align-items:center; gap:10px; margin:var(--s-4) 0 0}
/* Низкий экран — это не старый телефон, это ОБЫЧНЫЙ телефон в браузере.
   С домашнего экрана 375×667 отдаёт все 667, а в Safari строка адреса
   забирает 70–100: живая высота там 560–600. Замерено на первом экране
   теста: лид кончается на 547, а нижняя панель при высоте 600 начинается
   с 531 и при 568 — с 499. То есть обещание «двенадцать утверждений,
   минуты две» — единственное место, где названа длина теста, и, по
   замеру, самый сильный рычаг на входе в опрос — лежало под панелью у
   всякого, кто открыл приложение ссылкой, а не установил. А ссылкой его
   открывают все и всегда в первый раз.

   Съедается это отступами, а не кеглем: вопрос и варианты не трогаются.
   На 320×568 сверх того прячется подпись раздела — она видна двенадцать
   экранов подряд и там дороже места, которое занимает; прячется в `.sr`,
   а не `display:none`, чтобы остаться для читалки с экрана. */
@media (max-height:620px){
  #quiz .qbar{margin-bottom:var(--s-2)}
  #quiz .q-txt{margin-bottom:var(--s-3)}
  #quiz .q-feedback{margin-top:var(--s-2)}
  #quiz .q-nav{margin-top:var(--s-3)}
  #quiz[data-step="start"] .lede{margin-top:var(--s-2)}
  #quiz:has(.q-txt) h1.screen,
  #quiz:has(.q-h) h1.screen{margin-bottom:var(--s-2)}
}
@media (max-height:580px){
  #quiz .q-txt{margin-bottom:var(--s-2)}
  #quiz[data-step="start"] .lede{margin-top:var(--s-1)}
  #quiz:has(.q-txt) h1.screen,
  #quiz:has(.q-h) h1.screen{
    position:absolute; width:1px; height:1px; margin:-1px; padding:0;
    overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
  }
}
.back{
  border:0; background:none; padding:8px 0; min-height:44px; cursor:pointer;
  font-family:var(--f-body); font-size:13.5px; color:var(--muted);
}
/* Выход липнет под шапкой на экранах, где нижней панели нет.

   Замерено на 375×667: карточка приёма — 2797px прокрутки, экран «Место и
   данные» — 2821, и на обоих единственный выход уезжал на −2039 и −2063.
   В приложении, поставленном на домашний экран, ни строки адреса, ни
   кнопки «назад» у браузера нет — выйти было нечем, кроме прокрутки на
   две тысячи пикселей вверх.

   Проверка на «выход есть» при этом была зелёной: она спрашивала, есть ли
   кнопка, а не достанет ли до неё палец. Это ровно тот способ обмануться,
   который записан в памяти проекта первым.

   Правило узкое: в тесте `.back` стоит в ряду с «Дальше» и липнуть не
   должен, поэтому только прямые дети трёх панелей. */
#detail > .back, #settings > .back, #archive > .back{
  position:sticky; top:var(--topbar-h, 71px); z-index:39;
  background:var(--bg); margin:0 -16px 4px; padding:8px 16px;
  width:calc(100% + 32px); text-align:left;
}
/* Растворение под липким выходом — тем же способом, что у шапки и по той
   же причине: без него текст и кромки карточек режутся по нижней границе
   полосы, и на снимках это читается не как слой, а как сбой отрисовки. */
#detail > .back::after, #settings > .back::after, #archive > .back::after{
  content:""; position:absolute; left:0; right:0; top:100%; height:14px;
  pointer-events:none;
  background:linear-gradient(to bottom, var(--bg), color-mix(in srgb, var(--bg) 0%, transparent));
}
@media (hover:hover){.back:hover{color:var(--ink)}}
.q-next{
  margin-left:auto; min-height:52px; min-width:132px; padding:0 22px; cursor:pointer;
  font-family:var(--f-body); font-size:15px; font-weight:600;
  border:1px solid var(--accent); border-radius:var(--r-pill);
  background:var(--accent); color:var(--accent-contrast);
}
.q-next[disabled]{
  background:transparent; border:1px solid var(--line-strong);
  color:var(--muted); cursor:default; font-weight:500;
}

/* Результат: балл вторым планом, действие первым. */
.q-h{font-family:var(--f-display); font-size:21px; line-height:1.3; margin:0 0 var(--s-3)}
.score{
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r-md); padding:18px; margin:16px 0; box-shadow:var(--shadow-sm);
}
.score .big{
  font-family:var(--f-display); font-weight:700; font-size:38px; line-height:1;
  font-variant-numeric:tabular-nums; color:var(--accent);
}
.score .big small{font-size:16px; font-weight:500; color:var(--muted)}
.score-sm{padding:13px 15px; margin:0 0 var(--s-4)}
.score-sm .big{font-size:22px; color:var(--muted)}
.score-sm .big small{font-size:13px}
.score h3{font-family:var(--f-display); margin:12px 0 7px; font-size:17px}
.score p{margin:0 0 8px; font-size:14px} .score p:last-child{margin:0}
.meter{height:6px; border-radius:3px; background:var(--surface-2); overflow:hidden; margin:13px 0 0}
.meter i{display:block; height:100%; background:var(--accent); border-radius:3px; transition:width .5s ease}
/* Линия цитаты была --line-strong: 1.36:1 в светлой, 1.74 в тёмной — блок
   читался абзацем со случайным отступом, причина отступа не видна. Цвет линии
   не акцентный намеренно: акцент на этом экране оставлен блоку безопасности. */
.q-effects{
  font-size:14px; border-left:2px solid var(--muted);
  padding-left:13px; margin:var(--s-4) 0; color:var(--ink);
}
.q-cta{
  background:var(--surface); border:1px solid var(--line-strong);
  border-radius:var(--r-md); padding:var(--s-4); margin:var(--s-6) 0;
}
.q-cta p:first-child{margin:0 0 var(--s-3); font-size:14.5px}
.q-cta .btn{width:100%; margin-bottom:var(--s-2)}
.q-cta .privacy{margin:var(--s-1) 0 0}
#quiz h3{font-family:var(--f-display); font-size:16px; margin:20px 0 9px}
#quiz-stage .alarm{
  margin:var(--s-5) 0; padding:17px 18px;
  font-size:15px; line-height:1.5;
}
#quiz .alarm h3{margin:0 0 7px; font-size:16px}
/* Тот же низкий экран, но экран РЕЗУЛЬТАТА. Главная кнопка там —
   конверсия всей воронки, и на верхнем уровне её отодвигает блок
   безопасности: он стоит выше кнопки и это правильно. Замерено на
   375×568: кнопка вставала на 513…558 при панели с 499, то есть под
   ней целиком, и нажать её было нельзя. Съедается отступами вокруг
   блоков, а не их содержимым: на этом экране нет ни одного лишнего
   слова, и сокращать надо воздух.

   Блок стоит ЗДЕСЬ, ниже базовых правил, а не рядом с остальными
   медиазапросами теста. Первая попытка была выше `.q-cta` и `.alarm`, и
   не делала ровно ничего: специфичность одинаковая, медиазапрос её не
   добавляет, побеждает то правило, что ниже. Этот капкан в памяти
   проекта уже записан — композером, который на широком экране уезжал
   наполовину за кромку. Поймано замером: числа не сдвинулись ни на
   пиксель. */
@media (max-height:620px){
  .q-h{margin-bottom:var(--s-2)}
  #quiz-stage .alarm{margin:var(--s-4) 0; padding:13px 14px}
  .q-cta{margin:var(--s-4) 0; padding:var(--s-3)}
  .q-cta p:first-child{margin-bottom:var(--s-2)}
  #quiz-stage > h3{margin-top:var(--s-5)}
}


/* ------------------------------------------------------------
   Результат: главы

   Экран результата — почти три экрана подряд, и самый большой зазор в нём
   был 20px: блок безопасности отделялся от стены чипов двадцатью пикселями,
   два соседних абзаца — пятнадцатью. При равных зазорах глав не существует,
   и пролистать, остановиться, вернуться — нечем. Заголовки секций при этом
   весили ровно столько же, сколько заголовок аккордеона под ними.

   Приём взят существующий в приложении — h2.sect с подчёркивающей линией,
   которой размечена вся шпаргалка. Ритм: 32 между главами, 16 между блоками
   внутри главы, 12 от заголовка до его содержимого.

   `>` вместо потомка не случайно: внутри сцены есть h3 у блока безопасности
   и у карточки балла, и они не главы.
   ------------------------------------------------------------ */
#quiz-stage > h3{
  font-family:var(--f-display); font-weight:600; font-size:17px;
  letter-spacing:-.01em; margin:var(--s-6) 0 var(--s-3);
  padding-bottom:var(--s-2); border-bottom:1px solid var(--line);
}
#quiz-stage > p:not([class]){margin:0 0 var(--s-4)}
#quiz-stage > .chips{margin:0 0 var(--s-4)}
/* «Вся шпаргалка» закрывает главу, а не продолжает стопку аккордеонов:
   при отступе карточки в 8px кнопка читалась приклеенной к последней. */
#quiz-stage > p:not([class]):has(.btn){margin:var(--s-4) 0 0}

/* «24 из 24» с залитой во всю ширину акцентной шкалой — это визуальная
   грамматика счёта в игре, показанная человеку, который только что узнал,
   что у него не характер, а система. Число приглушено намеренно; шкала
   приводится к нему и перестаёт перебивать блок безопасности под собой. */
#quiz-stage .score-sm .meter i{background:var(--muted)}

/* Девять акцентных плашек в четыре ряда под «Что ты узнала» — не список
   узнанного, а табло. Вместе со шкалой, плиткой логотипа, точкой на
   шестерёнке, линией .alarm, кнопкой CTA и активной вкладкой на экране
   набиралось семь акцентных поверхностей — при том, что комментарий над
   .alarm обещает цвет как сигнал. Заливку снимаем, цвет остаётся в тексте:
   смысл сохраняется, масса уходит, и сигнальным на экране остаётся
   блок безопасности с действием под ним. */
#quiz-stage .chips .chip-accent{
  background:var(--surface-2); color:var(--accent-ink);
  border-color:var(--accent-border);
}

/* ------------------------------------------------------------
   Результат: подвал

   Концовка шла так: дисклеймер → «Пройти заново» → «остаётся на устройстве»
   → при повторном прохождении ещё «в прошлый раз». Три-четыре почти
   одинаковые серые моно-строки, между которыми зажата кнопка: экран не
   завершался, а иссякал. Линия сверху собирает их в один закрытый блок —
   тот же приём, что у .foot.

   Дисклеймер опознаётся по тому, что он единственный .sign до кнопки:
   строки после неё дописываются условно, а эта есть всегда.
   ------------------------------------------------------------ */
#quiz-stage .sign{margin:var(--s-3) 0 0}
#quiz-stage .rowbtns{margin:var(--s-4) 0 0}
#quiz-stage .sign:has(~ .rowbtns){
  margin-top:var(--s-6); padding-top:var(--s-4); border-top:1px solid var(--line);
}

/* ============================================================
   Шпаргалка
   ============================================================ */
details.card-acc{
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r-md); margin-bottom:var(--s-2); box-shadow:var(--shadow-sm);
}
/* Вес, а не кегль: заголовок раздела (h2.sect, Lora 18/600) и заголовок
   карточки под ним были одной гарнитуры, одного начертания и одного
   цвета, с разницей в 2.5px. На экране из 34 карточек в пяти разделах
   раздел не читался как уровень выше карточки — одиннадцать строк подряд
   выглядели одиннадцатью заголовками. Поднимать кегль раздела нельзя:
   на 19px «Приёмы, которые применяют к тебе» переносится на две строки. */
details.card-acc>summary{
  padding:14px 15px; cursor:pointer; font-family:var(--f-display);
  font-weight:500; font-size:15.5px; list-style:none; display:flex; align-items:center; gap:10px;
}
details.card-acc>summary::-webkit-details-marker{display:none}
details.card-acc>summary::after{content:"+"; margin-left:auto; color:var(--muted); font-size:18px; line-height:1}
details.card-acc[open]>summary::after{content:"−"}
details.card-acc .body{padding:0 15px 15px; font-size:14px}
details.card-acc .body p{margin:0 0 9px} details.card-acc .body p:last-child{margin:0}
/* «Подробно и источники» — строчная цель в 19px, и на результате это
   единственный выход в карточку. Правило узкое намеренно: тем же классом
   в «Рядом» набран ряд ссылок через «·», и там inline-block с высотой
   разорвал бы строку. */
/* Единственный путь из шпаргалки в полную карточку приёма — и
   единственная ссылка приложения, набранная не акцентным цветом: она
   наследовала --ink, то есть цвет абзаца над ней. Все остальные
   («Подробно» в разборе, шаги, источники) — --accent-ink. */
details.card-acc .body .more{
  display:inline-block; padding:var(--s-2) 0; min-height:36px;
  font-size:13px; color:var(--accent-ink);
}
/* `.sign` стоит в десяти местах, и меткой из них является одно —
   «Модель: <имя>». Остальные девять — связные предложения: объяснение,
   куда уйдёт текст в чужом чате (восемь строк подряд), его фразы в
   карточке шпаргалки, оговорка про бесплатный тариф, дисклеймер на
   результате теста. Моноширинный кириллицей на 12px — самый медленный
   набор в приложении, и им была набрана ровно та строка, которая
   отвечает на «безопасно ли по этому нажимать».

   Правило: моноширинный остаётся за однострочными метками (заголовки
   групп, .ev, .metric-label, .card-tag, цитата находки). Всё, что
   переносится, набирается интерфейсным. */
.sign{font-family:var(--f-body); font-size:13px; color:var(--muted)}
.foot{font-size:12.5px; color:var(--muted); margin:26px 0 0; padding-top:14px; border-top:1px solid var(--line)}

/* ============================================================
   Карточка приёма
   ============================================================ */
/* Карточка приёма — самый длинный экран приложения: замерено 2797px,
   больше четырёх экранов связного текста. Единственным, чем человек
   могла по нему ориентироваться, была строка КРУПНЕЕ АБЗАЦА НА ПОЛТОРА
   ПИКСЕЛЯ: 16px против 14.5. Приём, который это решает, в приложении уже
   есть — заголовки глав на экране результата теста (`#quiz-stage > h3`):
   кегль, линия и ритм. Сюда он не доехал. */
.detail h3{
  font-family:var(--f-display); font-weight:600; font-size:17px;
  letter-spacing:-.01em; margin:var(--s-6) 0 var(--s-3);
  padding-bottom:var(--s-2); border-bottom:1px solid var(--line);
}
.detail p{margin:0 0 10px; font-size:14.5px}
.srclist{list-style:none; padding:0; margin:10px 0 0; font-size:13px; color:var(--muted)}
.srclist li{padding:9px 0; border-top:1px solid var(--line)}
.srclist a{color:var(--accent-ink)}

/* ============================================================
   Что видно на каком экране
   ============================================================ */
body:not([data-pane="chat"]) .composer{display:none}

/* Экран восстановления — два действия и ничего больше. Полосы живут
   снаружи `.shell`, и стирание её содержимого их не трогало: под
   «приложение не смогло запуститься» оставался рабочий композер с
   приглашением вставить переписку, а вкладки вели на экраны, которых
   уже нет. */
:root.recovering .composer,
:root.recovering .tabbar{display:none}
:root.recovering .shell{padding-bottom:24px}
body:not([data-pane="chat"]) .shell{padding-bottom:calc(var(--tabbar-h, 70px) + 30px)}
body[data-pane="detail"] .tabbar,
body[data-pane="settings"] .tabbar{display:none}
body[data-pane="detail"] .shell,
body[data-pane="settings"] .shell{padding-bottom:40px}

/* Подсказка в поле была на 330px при доступных 212 — она переносилась на
   вторую строку, и ради второй строки поле держали в 60px. Платили высотой
   за текст, который и так стоит в приветствии первым сообщением ленты
   («вставь текст или прикрепи скриншот через плюс слева»), причём там его
   видно целиком. Подсказка укорочена до 147px, поле вернулось к 44. */

@media (max-width:560px){
  .stat-num{font-size:24px}
  .card{grid-template-columns:92px 1fr}
}

@media (prefers-reduced-motion:reduce){
  *{animation:none !important; transition:none !important}
}

/* Экран восстановления вместо белого экрана */
.recover{padding:32px 0}
.recover .warnbox{font-family:var(--f-mono); font-size:12px; word-break:break-word}

/* ============================================================
   Скриншоты в архиве
   ============================================================ */
.item-imgs{display:flex; flex-wrap:wrap; gap:6px; margin:0 0 9px}
.item-imgs img{
  width:64px; height:64px; object-fit:cover; cursor:zoom-in;
  border-radius:var(--r-xs); border:1px solid var(--line); background:var(--surface-2);
}
.shotview{
  border:0; padding:0; background:none; max-width:100vw; max-height:100vh;
}
.shotview::backdrop{background:rgba(0,0,0,.86)}
.shotview img{max-width:96vw; max-height:92vh; display:block; border-radius:var(--r-sm)}

/* Ответ модели, сохранённый вместе с разбором */
.answer{margin-top:8px}
.answer summary{
  font-family:var(--f-mono); font-size:11px; letter-spacing:.04em;
  color:var(--muted); cursor:pointer;
}
.answer p{margin:7px 0 0; font-size:13.5px; color:var(--ink-2); white-space:pre-wrap}
