/* ============================================================================
   Юрон — веб-интерфейс.

   Тёмная тема одна, светлой нет намеренно: сервис читают длинными кусками
   юридического текста, и переключатель темы здесь дал бы вторую сетку
   контрастов, которую пришлось бы вычитывать вручную по каждому экрану.

   Токены собраны в :root. Ни один цвет ниже не задан числом — если понадобится
   перекрасить продукт, правится этот блок, а не 900 строк под ним.
   ========================================================================= */

:root {
  /* — поверхности — */
  --bg:            #292828;
  --bg-side:       #232222;
  --surface:       #333131;
  --surface-hi:    #3c3939;
  --surface-sunk:  #2b2a2a;
  --border:        rgba(255, 255, 255, .08);
  --border-hi:     rgba(255, 255, 255, .16);

  /* — текст — */
  --text:          #f2f0ee;
  --text-dim:      #a3a0a0;
  --text-faint:    #7a7777;

  /* — акцент: оранжевый → розовый, как на карточках и кнопке доступа — */
  --accent:        #f0663b;
  --accent-2:      #f2547d;
  --accent-soft:   rgba(240, 102, 59, .14);
  --gradient:      linear-gradient(97deg, #f0663b 0%, #f2547d 100%);

  /* — состояния — */
  --ok:            #62b58a;
  --warn:          #e8b84b;
  --err:           #e56a6a;

  /* — геометрия — */
  --r-sm:  10px;
  --r-md:  14px;
  --r-lg:  20px;
  --r-xl:  26px;
  --sidebar-w: 268px;

  --shadow:    0 10px 34px rgba(0, 0, 0, .38);
  --shadow-sm: 0 4px 14px rgba(0, 0, 0, .26);

  --ease: cubic-bezier(.2, .7, .3, 1);

  /* — полосы прокрутки — */
  --scroll-thumb:       rgba(0, 0, 0, .55);
  --scroll-thumb-hover: rgba(0, 0, 0, .75);
}

/* ------------------------------------------------------------------ основа */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.55 "Inter", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;               /* прокручиваются области, а не страница */
}

h1, h2, h3, p, ul, figure { margin: 0; }
ul { padding: 0; list-style: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
textarea { font: inherit; color: inherit; }

/* Полоса прокрутки задаётся один раз на всё приложение: своя полоса у каждой
   прокручиваемой области — это четыре разных полосы в одном интерфейсе.
   `scrollbar-*` понимают Firefox и свежие Chromium, `::-webkit-scrollbar` —
   Safari и Chromium постарше; вместе они закрывают всё, где сервис работает.
   Дорожка прозрачная намеренно: полоса лежит поверх тёмных панелей разного
   тона, и собственный фон дорожки прочертил бы по ним светлую линейку. */
* { scrollbar-width: thin; scrollbar-color: var(--scroll-thumb) transparent; }

::-webkit-scrollbar { width: 10px; height: 10px; }
/* ⚠️ Как только у полосы задан хоть один собственный стиль, Chromium рисует
   её сам — и вместе с ползунком выводит кнопки-стрелки, которых у системной
   полосы здесь нет. Именно они торчали двумя треугольниками справа в пустом
   поле ввода. Скрываются только явно. */
::-webkit-scrollbar-button { display: none; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--scroll-thumb);
  border: 3px solid transparent;      /* поля вокруг ползунка, а не вокруг дорожки */
  border-radius: 999px;
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--scroll-thumb-hover); background-clip: padding-box; }
::-webkit-scrollbar-corner { background: transparent; }

/* Спрайт иконок в потоке не участвует — иначе он занял бы первый экран. */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.ic {
  width: 20px; height: 20px;
  flex: none;
  stroke: currentColor;
  fill: none;
  vertical-align: middle;
}
.ic--sm { width: 16px; height: 16px; }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  padding: 10px 16px; background: var(--surface); border-radius: 0 0 var(--r-sm) 0;
}
.skip:focus { left: 0; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ---------------------------------------------------------------- раскладка */

.layout {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  height: 100dvh;
}

/* ═══════════════════════════════════════════════════════════ САЙДБАР ═══ */

.sidebar {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: 14px 12px 12px;
  background: var(--bg-side);
  border-right: 1px solid var(--border);
  overflow: hidden;
}

/* Свёрнутая панель.
 *
 * ⚠️ Сворачивать её через `display: none` НЕЛЬЗЯ, хотя так и просится. Раскладка —
 * грид из двух колонок, и панель, выпав из потока, освобождает первую колонку:
 * основная область автоматически переезжает в неё, а первая колонка в свёрнутом
 * состоянии шириной в ноль. Получается пустой экран — ровно этот баг и был.
 * Поэтому панель остаётся в гриде и просто становится нулевой ширины;
 * `visibility` заодно убирает её содержимое из обхода табом и из дерева
 * доступности, чего одна лишь нулевая ширина не делает.
 *
 * Правило живёт в широком экране намеренно: на узком панель наезжает поверх
 * содержимого, и «свернуть» там значит «закрыть» — этим занимается is-open. */
@media (min-width: 901px) {
  .layout.is-collapsed { grid-template-columns: 0 1fr; }
  .layout.is-collapsed .sidebar {
    visibility: hidden;
    padding-inline: 0;
    border-right: 0;
  }
  /* Свернув панель, человек прячет и кнопку, которая её вернёт: единственная
     такая кнопка живёт в верхней строке, а она на широком экране скрыта.
     Без этого правила из свёрнутого состояния выход только перезагрузкой. */
  .layout.is-collapsed .topbar { display: flex; }
}

.sidebar__head {
  display: flex; align-items: center; gap: 8px;
  padding: 0 4px 14px;
}

.brand { display: flex; align-items: center; gap: 10px; min-width: 0; margin-right: auto; }

.brand__mark {
  display: grid; place-items: center;
  width: 32px; height: 32px;
  border-radius: 9px;
  background: var(--gradient);
  color: #fff;
  box-shadow: 0 4px 12px rgba(240, 102, 59, .3);
}
.brand__mark .ic { width: 18px; height: 18px; stroke-width: 2; }

.brand__name { font-size: 17px; font-weight: 640; letter-spacing: -.01em; }

.icon-btn {
  display: grid; place-items: center;
  width: 34px; height: 34px;
  border-radius: var(--r-sm);
  color: var(--text-dim);
  transition: background .15s var(--ease), color .15s var(--ease);
}
.icon-btn:hover { background: var(--surface); color: var(--text); }

/* — создать чат — */

.new-chat {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 11px 14px; margin-bottom: 16px;
  border: 1px solid var(--border-hi);
  border-radius: var(--r-md);
  font-weight: 560;
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.new-chat:hover { background: var(--surface); border-color: transparent; }
.new-chat .ic { color: var(--accent); }

/* — навигация — */

.nav { display: flex; flex-direction: column; gap: 2px; margin-bottom: 18px; }

.nav__item {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 10px;
  border-radius: var(--r-md);
  color: var(--text-dim);
  text-align: left;
  transition: background .15s var(--ease), color .15s var(--ease);
}
.nav__item:hover { background: var(--surface); color: var(--text); }
.nav__item.is-active { background: var(--surface); color: var(--text); }
.nav__item.is-active .nav__ic { border-color: transparent; background: var(--accent-soft); }

/* Иконка в тонкой оранжевой рамке — на референсе именно она держит ритм
   бокового меню, поэтому обводка есть и в неактивном состоянии. */
.nav__ic {
  display: grid; place-items: center;
  width: 30px; height: 30px;
  border: 1px solid rgba(240, 102, 59, .38);
  border-radius: 9px;
  color: var(--accent);
}
.nav__ic .ic { width: 16px; height: 16px; }

.nav__label { flex: 1; min-width: 0; font-size: 14px; font-weight: 520; }

.nav__badge {
  font-size: 12px; color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

/* — список диалогов — */

.chats { flex: 1; min-height: 0; display: flex; flex-direction: column; }

.chats__title {
  padding: 0 10px 8px;
  font-size: 11.5px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: var(--text-faint);
}

.chats__list { flex: 1; min-height: 0; overflow-y: auto; margin: 0 -4px; padding: 0 4px; }

.chats__empty { padding: 4px 10px; font-size: 13px; color: var(--text-faint); }
.chats__empty[hidden] { display: none; }

.chat-item {
  display: flex; align-items: center; gap: 4px;
  border-radius: var(--r-sm);
  transition: background .15s var(--ease);
}
.chat-item:hover,
.chat-item.is-active { background: var(--surface); }
.chat-item.is-active .chat-item__btn { color: var(--text); }

.chat-item__btn {
  flex: 1; min-width: 0;
  padding: 8px 4px 8px 10px;
  font-size: 13.5px; color: var(--text-dim); text-align: left;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.chat-item__del {
  display: grid; place-items: center;
  width: 28px; height: 28px; margin-right: 4px;
  border-radius: 7px;
  color: var(--text-faint);
  opacity: 0;
  transition: opacity .15s var(--ease), color .15s var(--ease), background .15s var(--ease);
}
.chat-item:hover .chat-item__del,
.chat-item__del:focus-visible { opacity: 1; }
.chat-item__del:hover { background: var(--surface-hi); color: var(--err); }
.chat-item__del .ic { width: 15px; height: 15px; }

/* — подвал: квота и апгрейд — */

.sidebar__foot { padding-top: 14px; }

.quota {
  padding: 12px 14px 13px;
  background: var(--surface);
  border-radius: var(--r-md);
  margin-bottom: 10px;
}
.quota__label { font-size: 12px; color: var(--text-dim); }

.quota__value {
  display: flex; align-items: baseline; gap: 6px;
  margin: 3px 0 8px;
  font-size: 21px; font-weight: 640; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.quota__of { font-size: 13px; font-weight: 450; color: var(--text-faint); }

.quota__bar {
  height: 4px; border-radius: 4px;
  background: rgba(255, 255, 255, .09);
  overflow: hidden;
}
.quota__bar i {
  display: block; height: 100%; width: 0;
  background: var(--gradient);
  transition: width .4s var(--ease);
}

/* Заполнение полосы — классом, а не шириной в стиле элемента: инлайновые
   стили запрещены политикой безопасности страницы, и заданная в них ширина
   молча отбрасывалась (полоса всегда была пустой). Шаг 5% — полоса высотой
   в четыре пикселя, точнее глазом всё равно не прочесть. */
.quota__bar i.q-5   { width: 5%; }
.quota__bar i.q-10  { width: 10%; }
.quota__bar i.q-15  { width: 15%; }
.quota__bar i.q-20  { width: 20%; }
.quota__bar i.q-25  { width: 25%; }
.quota__bar i.q-30  { width: 30%; }
.quota__bar i.q-35  { width: 35%; }
.quota__bar i.q-40  { width: 40%; }
.quota__bar i.q-45  { width: 45%; }
.quota__bar i.q-50  { width: 50%; }
.quota__bar i.q-55  { width: 55%; }
.quota__bar i.q-60  { width: 60%; }
.quota__bar i.q-65  { width: 65%; }
.quota__bar i.q-70  { width: 70%; }
.quota__bar i.q-75  { width: 75%; }
.quota__bar i.q-80  { width: 80%; }
.quota__bar i.q-85  { width: 85%; }
.quota__bar i.q-90  { width: 90%; }
.quota__bar i.q-95  { width: 95%; }
.quota__bar i.q-100 { width: 100%; }

.quota__reset { margin-top: 8px; font-size: 11.5px; color: var(--text-faint); }

.cta {
  display: block; width: 100%;
  padding: 11px 14px;
  border-radius: var(--r-md);
  background: var(--gradient);
  color: #fff; font-weight: 600; font-size: 14px;
  box-shadow: 0 6px 18px rgba(240, 102, 59, .26);
  transition: transform .15s var(--ease), box-shadow .15s var(--ease);
}
.cta:hover { transform: translateY(-1px); box-shadow: 0 10px 24px rgba(240, 102, 59, .34); }
.cta:active { transform: none; }

/* Затемнение под выехавшим сайдбаром — только на узком экране. */
.scrim { display: none; }

/* ═══════════════════════════════════════════════════════ ОСНОВНАЯ ЧАСТЬ ═══ */

.main {
  display: flex; flex-direction: column;
  min-width: 0; min-height: 0;
}

.topbar {
  display: none;
  align-items: center; gap: 8px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
}
.topbar__title {
  flex: 1; min-width: 0;
  font-size: 15px; font-weight: 560; text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.screen { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.screen[hidden] { display: none; }

/* ------------------------------------------------------------ главный экран */

.screen--home { overflow-y: auto; }

/* Сетка команд шире строки ввода: карточек четырнадцать, и в три колонки
   они превращают первый экран в длинный список. Верхняя часть остаётся
   узкой — см. комментарий в разметке. */
.home {
  width: min(100%, 1120px);
  margin: 0 auto;
  padding: min(8vh, 76px) 24px 48px;
}
.home__top { width: min(100%, 780px); margin: 0 auto; }

.hero { text-align: center; margin-bottom: 18px; }

.hero__title {
  font-size: clamp(28px, 4.6vw, 40px);
  font-weight: 660;
  letter-spacing: -.025em;
}
.hero__sub { margin-top: 10px; color: var(--text-dim); font-size: 15px; }

/* ------------------------------------------------- строка возможностей */

/* Высота задана под ДВЕ строки и не зависит от длины фразы: иначе при каждой
   смене текста форма ввода дёргалась бы вверх-вниз на пол-экрана. */
.canline {
  display: flex; align-items: center; justify-content: center;
  min-height: 3em;
  margin-bottom: 14px;
  padding: 0 8px;
  font-size: 14.5px; line-height: 1.5; text-align: center;
  color: var(--text-dim);
}

.canline__text { display: inline-block; }

/* Слово целиком: буквы — `inline-block`, а строка из них переносится
   по буквам, а не по пробелам. `nowrap` возвращает перенос по словам. */
.canline__w { display: inline-block; white-space: nowrap; }

/* Очередь букв ставит разметчик (`canVolna` в app.js) — классом, а не
   задержкой в стиле знака. ⚠️ Так сделано не из вкуса: на боевой странице
   политика безопасности запрещает инлайновые стили, и `animation-delay`
   в стиле буквы молча отбрасывался — фраза появлялась разом.

   До своей очереди буквы нет: `opacity: 0` без анимации. Правило ухода
   стоит ПОСЛЕ правила прихода намеренно — вес у них одинаковый, и побеждает
   последнее, а на уходящей букве висят оба класса. */
.canline__ch { display: inline-block; opacity: 0; }
.canline__ch.is-in  { animation: canIn .6s cubic-bezier(.25, .46, .45, .94) both; }  /* power2.out */
.canline__ch.is-out { animation: canOut .5s cubic-bezier(.55, .085, .68, .53) both; } /* power2.in */

/* Расфокус, а не растворение: текст собирается из размытия и уходит обратно
   в него — так же, как это делают строки подсказок в современных ассистентах.
   Тайминги и кривые взяты с образца, на который сослался заказчик: буква
   появляется 0.6 с и уходит 0.5 с, вход тормозит к концу, уход разгоняется.
   ⚠️ Смещение и размытие заданы в `em`, а не в пикселях: в образце строка
   набрана кеглем ~72 px, и его 20 px подъёма — это чуть больше четверти буквы.
   Перенеси те же пиксели на наши 14.5 px — и подъём станет больше строки,
   а размытие съест текст целиком. В долях кегля эффект переносится, а числа —
   нет. */
/* ⚠️ Оба кадра заданы явно, недостающий подставлять НЕЛЬЗЯ. Пропущенный кадр
   браузер берёт из обычных стилей знака, а там теперь `opacity: 0` — буква
   до своей очереди невидима. С одним кадром `from` появление шло бы из нуля
   в ноль, и строка не показывалась бы вовсе. */
@keyframes canIn {
  from { opacity: 0; filter: blur(.5em); transform: translateY(.3em); }
  to   { opacity: 1; filter: blur(0);    transform: none; }
}
@keyframes canOut {
  from { opacity: 1; filter: blur(0);    transform: none; }
  to   { opacity: 0; filter: blur(.5em); transform: translateY(-.3em); }
}

/* ---------------------------------------------------------------- composer */

.composer {
  position: relative;
  padding: 14px 14px 12px;
  background: var(--surface);
  border: 2px solid var(--border-hi);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow);
  transition: border-color .18s var(--ease);
}
.composer:focus-within { border-color: rgba(240, 102, 59, .62); }

/* Свечение за формой. `z-index: -1` кладёт его ПОД собственный непрозрачный
   фон формы, поэтому размытие видно только по краям и текст оно не трогает.
   `inset` отрицательный — свечение обязано выходить за габарит, иначе это
   не свечение, а тень. */
.composer::before {
  content: "";
  position: absolute; inset: -16px; z-index: -1;
  border-radius: inherit;
  background: var(--gradient);
  opacity: .22;
  filter: blur(28px);
  pointer-events: none;
  transition: opacity .3s var(--ease);
}
.composer:focus-within::before { opacity: .42; }

/* В диалоге свечение выключено: там форма стоит над лентой сообщений,
   и подсвеченная рамка спорила бы с текстом ответа за внимание. */
.composer--dock::before { display: none; }

.composer--hero { padding: 20px 18px 14px; }
.composer--hero .composer__input { min-height: 54px; font-size: 16.5px; }

.composer__input {
  display: block;
  width: 100%;
  max-height: 220px;
  padding: 4px 6px 10px;
  background: none; border: 0; resize: none;
  font-size: 15.5px; line-height: 1.5;
  overflow-y: auto;
}
.composer__input::placeholder { color: var(--text-faint); }
.composer__input:focus { outline: none; }

.composer__row { display: flex; align-items: center; gap: 8px; }
.composer__filters { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; flex-wrap: wrap; }

.chip {
  display: inline-flex; align-items: center; gap: 7px;
  max-width: 100%;
  padding: 7px 11px;
  border: 1px solid var(--border-hi);
  border-radius: 999px;
  font-size: 13px; color: var(--text-dim);
  white-space: nowrap;
  transition: background .15s var(--ease), color .15s var(--ease), border-color .15s var(--ease);
}
.chip:hover { background: var(--surface-hi); color: var(--text); }
.chip .ic { width: 15px; height: 15px; }
.chip__text { overflow: hidden; text-overflow: ellipsis; }
.chip__caret { transition: transform .18s var(--ease); }
.chip--static { cursor: default; }
.chip--static:hover { background: none; color: var(--text-dim); }

.chip__badge {
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 11.5px; font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.dd { position: relative; }
.dd.is-open .chip { background: var(--surface-hi); color: var(--text); border-color: var(--accent); }
.dd.is-open .chip__caret { transform: rotate(180deg); }

.send {
  display: grid; place-items: center;
  width: 38px; height: 38px; flex: none;
  border-radius: 50%;
  background: var(--gradient);
  color: #fff;
  transition: opacity .15s var(--ease), transform .15s var(--ease);
}
.send:hover:not(:disabled) { transform: scale(1.06); }
.send:disabled { opacity: .38; cursor: not-allowed; }
.send .ic { stroke-width: 2.25; }

/* ------------------------------------------------------- выпадающий список */

/* Меню раскрывается ВНИЗ, из-под кнопки. Раньше оно уходило вверх — и на
   главной, где форма стоит в верхней трети экрана, список накрывал заголовок
   и упирался в край окна: последние отрасли становились недосягаемы. */
.dd__menu {
  position: absolute; top: calc(100% + 10px); left: 0; z-index: 40;
  width: max-content; min-width: 240px; max-width: min(88vw, 340px);
  padding: 6px;
  background: var(--surface-hi);
  border: 1px solid var(--border-hi);
  border-radius: var(--r-md);
  box-shadow: var(--shadow);
}
.dd__menu[hidden] { display: none; }

.dd__head {
  padding: 8px 10px 8px;
  font-size: 13px; font-weight: 620;
}

/* Список короче, чем мог бы быть: раскрываясь вниз от формы, он обязан
   целиком помещаться под ней даже на невысоком окне ноутбука, иначе
   последние отрасли уезжают за край и до них не дотянуться. */
.dd__list { max-height: min(34vh, 320px); overflow-y: auto; }

.dd__group {
  padding: 10px 10px 4px;
  font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-faint);
}

.dd__opt {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 8px 10px;
  border-radius: var(--r-sm);
  font-size: 13.5px; color: var(--text-dim); text-align: left;
  transition: background .12s var(--ease), color .12s var(--ease);
}
.dd__opt:hover { background: rgba(255, 255, 255, .06); color: var(--text); }
.dd__opt.is-on { color: var(--text); }

.dd__label { flex: 1; min-width: 0; }
.dd__note { font-size: 11.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; }

/* Галочка занимает место всегда — иначе строки прыгают при выборе. */
.dd__tick { width: 16px; flex: none; color: var(--accent); opacity: 0; }
.dd__opt.is-on .dd__tick { opacity: 1; }

.dd__sep { height: 1px; margin: 5px 8px; background: var(--border); }

.dd__reset {
  display: block; width: 100%;
  margin-top: 4px; padding: 8px 10px;
  border-top: 1px solid var(--border);
  font-size: 12.5px; color: var(--text-faint); text-align: left;
}
.dd__reset:hover { color: var(--accent); }

/* ------------------------------------------------- переключатель источника */

/* Галочка отвечает на вопрос «что выбрано из списка», а тумблер — «включено
   ли это». Источники именно включаются: по умолчанию ищем по всем, и человек
   гасит лишние. Поэтому здесь тумблер, а не галочка. */
.sw {
  display: flex; align-items: center; gap: 12px;
  width: 100%; padding: 9px 10px;
  border-radius: var(--r-sm);
  font-size: 13.5px; color: var(--text); text-align: left;
  transition: background .12s var(--ease);
}
.sw:hover { background: rgba(255, 255, 255, .06); }

.sw__label { flex: 1; min-width: 0; }
.sw__note { font-size: 11.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; }

.sw__box {
  position: relative; flex: none;
  width: 34px; height: 20px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .16);
  transition: background .18s var(--ease);
}
.sw__box::after {
  content: "";
  position: absolute; top: 2px; left: 2px;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: #fff;
  transition: transform .18s var(--ease);
}
.sw.is-on .sw__box { background: var(--accent); }
.sw.is-on .sw__box::after { transform: translateX(14px); }

/* -------------------------------------------------------------- дисклеймер */

.disclaimer {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 14px 4px 0;
  font-size: 12.5px; line-height: 1.5; color: var(--text-faint);
}
.disclaimer .ic { margin-top: 1px; color: var(--warn); opacity: .8; }
.disclaimer--tight { justify-content: center; margin: 8px 0 0; font-size: 11.5px; }

/* --------------------------------------------------------- готовые команды */

.presets { margin-top: 44px; }

.presets__head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  margin-bottom: 16px;
}
.presets__title { font-size: 17px; font-weight: 640; letter-spacing: -.01em; }
.presets__sub { margin-top: 4px; font-size: 13px; color: var(--text-dim); }

.presets__more {
  display: inline-flex; align-items: center; gap: 3px; flex: none;
  padding-top: 3px;
  font-size: 13px; color: var(--text-dim);
  transition: color .15s var(--ease);
}
.presets__more:hover { color: var(--accent); }

.presets__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(238px, 1fr));
  gap: 12px;
}

/* Заголовок категории пресетов. */
.presets__cat {
  grid-column: 1 / -1;
  margin: 12px 0 6px;
  font-size: 12px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-faint);
}
.presets__cat:first-of-type { margin-top: 0; }

/* Карточка — колонка, а не сетка: иконка с бейджем сверху, описание тянется,
   троеточие прижато к низу. На сетке нижний ряд пришлось бы держать
   `1fr`-строкой, и карточки без описания схлопывались бы по-разному. */
.preset {
  display: flex; flex-direction: column;
  min-height: 176px;
  padding: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  text-align: left;
  transition: border-color .16s var(--ease), transform .16s var(--ease), background .16s var(--ease);
}
.preset:hover {
  background: var(--surface-hi);
  border-color: rgba(240, 102, 59, .34);
  transform: translateY(-2px);
}

.preset__top { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }

.preset__ic {
  display: grid; place-items: center;
  width: 36px; height: 36px; flex: none;
  border: 1px solid rgba(240, 102, 59, .34);
  border-radius: 11px;
  color: var(--accent);
}
.preset__ic .ic { width: 18px; height: 18px; }

.preset__badge {
  flex: none;
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .07);
  font-size: 11px; color: var(--text-dim);
  white-space: nowrap;
}

.preset__title {
  margin-top: 15px;
  font-size: 14.5px; font-weight: 600; line-height: 1.3;
}
.preset__hint {
  flex: 1;
  margin-top: 7px;
  font-size: 12.5px; line-height: 1.45; color: var(--text-dim);
}

.preset__dots {
  align-self: flex-end;
  margin-top: 10px;
  color: var(--text-faint);
  opacity: 0;
  transition: opacity .16s var(--ease);
}
.preset:hover .preset__dots { opacity: 1; }
.preset__dots .ic { width: 16px; height: 16px; }

/* ═════════════════════════════════════════════════════════════ ДИАЛОГ ═══ */

.thread {
  flex: 1; min-height: 0;
  overflow-y: auto;
  padding: 28px 24px 8px;
  scroll-behavior: smooth;
}

/* Реплика проступает целиком и один раз — узел создаётся на сообщение.
   Ни абзацы, ни строки внутри анимировать НЕЛЬЗЯ: их перерисовывает каждый
   кусок потока, и ответ мигал бы всё время, пока модель пишет. */
.msg {
  width: min(100%, 780px); margin: 0 auto 22px;
  animation: msgVsplyt .3s var(--ease) backwards;
}
@keyframes msgVsplyt {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

.msg[data-msg-role="user"] { display: flex; justify-content: flex-end; }
.msg[data-msg-role="user"] .msg__body {
  max-width: 82%;
  padding: 11px 16px;
  background: var(--surface);
  border-radius: var(--r-lg) var(--r-lg) 6px var(--r-lg);
}

.msg__body { min-width: 0; }

.msg__text {
  overflow-wrap: anywhere;
  font-size: 15px; line-height: 1.68;
}

/* Реплика человека — обычный текст: переносы, которые он набрал, обязаны
   остаться. У ответа переносы уже разобраны в блоки (`mdInto` в app.js),
   и «сохранять как есть» там нечего. */
.msg[data-msg-role="user"] .msg__text { white-space: pre-wrap; }

.msg__text > * + * { margin-top: .85em; }

/* Подзаголовок внутри ответа — тот самый абзац, который модель отбивает
   звёздочками. Он не заголовок страницы, поэтому не крупнее текста:
   отличается насыщенностью и воздухом сверху. */
.md-h { font-weight: 640; color: var(--text); }
.msg__text > .md-h + * { margin-top: .45em; }
.msg__text > * + .md-h { margin-top: 1.25em; }

/* Маркеры возвращаются вручную: в проекте у списков они сняты глобально
   (`ul { list-style: none }`), а перечень в ответе без маркеров читается
   как слипшиеся абзацы. */
/* `ol` в общий сброс отступов не входит — обнуляем здесь, иначе его
   собственный внешний отступ сложится с расстоянием между блоками. */
.md-ul, .md-ol { margin: 0; padding-left: 1.35em; }
.md-ul { list-style: disc outside; }
.md-ol { list-style: decimal outside; }
.md-ul li, .md-ol li { margin-top: .3em; }
.md-ul li::marker, .md-ol li::marker { color: var(--text-faint); }

/* Пустой абзац ответа — это «модель думает». Курсор ставится через ::after,
   чтобы не заводить отдельный элемент-заглушку и не убирать его потом. */
.msg__text:empty::after {
  content: "";
  display: inline-block;
  width: 8px; height: 15px;
  border-radius: 2px;
  background: var(--accent);
  animation: blink 1.05s steps(2, start) infinite;
  vertical-align: -2px;
}
@keyframes blink { to { opacity: 0; } }

.msg__meta {
  margin-top: 10px;
  font-size: 11.5px; color: var(--text-faint);
}

/* — найденные нормы — */

.norms { margin: 4px 0 14px; }

.norms__head {
  display: flex; align-items: center; gap: 7px;
  margin-bottom: 8px;
  font-size: 12px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  color: var(--text-faint);
}
.norms__head .ic { width: 14px; height: 14px; color: var(--accent); }

.norms__list { display: flex; flex-wrap: wrap; gap: 7px; }

.norm {
  padding: 7px 12px;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 12.5px; color: var(--text-dim);
  transition: border-color .15s var(--ease), color .15s var(--ease);
}
.norm:hover { border-color: rgba(240, 102, 59, .4); color: var(--text); }
.norm b { color: var(--text); font-weight: 580; }

/* Взятые в работу и оставленные в стороне.
   Разница намеренно сдержанная: отложенные нормы не ошибка поиска и не мусор —
   юрист их посмотрел и может к ним вернуться, поэтому они гаснут, а не
   вычёркиваются. Взятые же выделены рамкой, а не заливкой: сплошной цвет
   на восьми чипах превращает перечень в светофор. */
.norm.is-used {
  border-color: rgba(240, 102, 59, .55);
  color: var(--text);
}
.norm.is-used b { color: var(--accent); }
.norm.is-aside { --norm-op: .55; opacity: .55; }
.norm.is-aside:hover { opacity: 1; }

/* Нормы проступают из пустоты одна за другой, а не выпадают стеной.

   ⚠️ Три тонкости, каждая — из грабель:

   1. Заполнение `backwards`, а НЕ `both`. При `both` анимация продолжает
      диктовать прозрачность и после окончания, то есть перебивает и `.is-aside`
      (отложенная норма перестала бы гаснуть), и `:hover` (наведение перестало
      бы её возвращать). `backwards` действует только ДО старта — ровно там,
      где чип обязан быть невидим, пока ждёт своей очереди.
   2. Конец волны — `var(--norm-op, 1)`, а не единица: у отложенных норм своя
      прозрачность, и приходить им положено сразу приглушёнными, иначе в конце
      анимации они рывком темнеют.
   3. Очередь задана классами `.is-n1…8`, потому что `animation-delay`
      в стиле знака политика безопасности страницы молча отбрасывает. */
.norm { --norm-op: 1; animation: normVsplyt .45s cubic-bezier(.25, .46, .45, .94) backwards; }
.norm.is-n2 { animation-delay: .05s; }
.norm.is-n3 { animation-delay: .1s; }
.norm.is-n4 { animation-delay: .15s; }
.norm.is-n5 { animation-delay: .2s; }
.norm.is-n6 { animation-delay: .25s; }
.norm.is-n7 { animation-delay: .3s; }
.norm.is-n8 { animation-delay: .35s; }

@keyframes normVsplyt {
  from { opacity: 0; filter: blur(4px); transform: translateY(6px); }
  to   { opacity: var(--norm-op, 1); filter: blur(0); transform: none; }
}

.norms__all {
  font-weight: 500; letter-spacing: 0; text-transform: none;
  color: var(--text-faint);
}

/* Полный текст нормы раскрывается по клику: юристу сначала нужен перечень,
   а текст — выборочно, иначе экран превращается в простыню. */
.norm__full {
  width: 100%;
  margin-top: 2px; padding: 12px 14px;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-size: 13px; line-height: 1.6; color: var(--text-dim);
  overflow-wrap: anywhere;
}
.norm__full[hidden] { display: none; }

/* Откуда норма взялась — служебная подпись, поэтому мельче и бледнее самого
   текста статьи: раскрывают статью, чтобы прочитать закон, а не подпись. */
.norm__why { font-size: 11.5px; color: var(--text-faint); }
.norm__body { margin-top: 8px; }
.norm__body.is-wait { color: var(--text-faint); }
.norm__path { font-size: 11.5px; color: var(--text-faint); }
.norm__head { margin-top: 2px; font-weight: 620; color: var(--text); }

/* Текст закона выше по контрасту, чем обвязка вокруг: это то единственное,
   ради чего статью раскрывают. Высота строки крупнее обычной — читают его
   не по диагонали, а дословно. */
.norm__text { margin-top: 6px; color: var(--text); line-height: 1.7; }
.norm__text > * + * { margin-top: .6em; }
.norm__text .md-ol, .norm__text .md-ul { padding-left: 1.2em; }

/* Редакция — обязательная часть цитаты закона: та же статья год назад могла
   звучать иначе, и человек имеет право видеть, что именно он читает. */
.norm__rev {
  margin-top: 8px; padding-top: 7px;
  border-top: 1px solid var(--border);
  font-size: 11.5px; color: var(--text-faint);
}

/* — этапы обработки — */

/* — ход работы над ответом —
   Показывается то, что реально произошло на сервере, с настоящим временем
   из трассы конвейера. Пока идёт работа блок раскрыт, по завершении
   сворачивается, но остаётся раскрываемым: юристу важно видеть, на чём
   построен ответ. */

.think {
  margin-bottom: 10px;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
}

.think__head {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px;
  cursor: pointer;
  font-size: 12.5px; color: var(--text-dim);
  list-style: none;
}
.think__head::-webkit-details-marker { display: none; }
.think__head:hover { color: var(--text); }

.think__icon { width: 15px; height: 15px; color: var(--accent); }
/* Пульс живёт только пока работа идёт: застывшая анимация над готовым
   ответом врала бы, что сервис всё ещё думает. */
.think:not(.think--done) .think__icon { animation: think-pulse 1.2s var(--ease) infinite; }
@keyframes think-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) {
  .think:not(.think--done) .think__icon { animation: none; }
}

.think__title { flex: 1; }
.think__total { font-size: 11.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; }

.think__list {
  margin: 0; padding: 2px 12px 10px 12px;
  list-style: none;
  border-top: 1px solid var(--border);
}

/* Шаг проступает при появлении и БОЛЬШЕ НЕ повторяет анимацию: `thinkStep`
   переписывает классы узла на каждом обновлении, но имя анимации при этом
   не меняется, а значит браузер её не перезапускает. Ставить проступание
   на строки самого ответа нельзя по обратной причине — они перерисовываются
   на каждом куске потока, и текст мигал бы всё время, пока модель пишет. */
.think__step {
  display: grid;
  grid-template-columns: 14px minmax(0, auto) minmax(0, 1fr) auto;
  align-items: baseline; gap: 8px;
  padding: 5px 0;
  font-size: 12px; color: var(--text-faint);
  animation: normVsplyt .35s cubic-bezier(.25, .46, .45, .94) backwards;
}
.think__dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--text-faint);
  justify-self: center; align-self: center;
}
.think__step--hit .think__dot, .think__step--passed .think__dot { background: var(--ok); }
.think__step--skipped .think__dot { background: var(--warn); }
.think__step--no_hit .think__dot { background: var(--text-faint); }
.think__step--error .think__dot { background: var(--err); }
.think__step--run .think__dot {
  background: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.think__name { color: var(--text-dim); }
.think__note { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.think__ms { font-variant-numeric: tabular-nums; }

/* — стопка карточек статей: то, что юрист сейчас перебирает —

   Живёт в теле ответа, на месте будущего текста: между заданным вопросом
   и первым словом ответа проходит несколько секунд, и раньше там было пусто.

   Карточка выплывает снизу, уменьшенной и размытой, встаёт на место, держится
   и уходит вверх, растворяясь. Такт добавления (900 мс) втрое короче жизни
   карточки (2.6 с), поэтому в стопке одновременно стоят три штуки на разных
   стадиях — это и читается как перебор.

   ⚠️ Ни одной задержки в стиле: политика безопасности страницы запрещает
   инлайновые стили, и `animation-delay`, выставленный из JS, молча пропал бы.
   Очередь получается порядком добавления узлов. */

.peeks {
  display: flex; flex-direction: column; gap: 8px;
  min-height: 132px;                 /* место под три карточки — лента не прыгает */
  margin-bottom: 14px;
  justify-content: flex-end;
}
.peek {
  padding: 10px 13px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-size: 12.5px; line-height: 1.45;
  animation: peekUp 2.6s var(--ease) both;
}
.peek__ref { display: block; color: var(--accent); font-weight: 620; }
.peek__title { display: block; margin-top: 3px; color: var(--text-dim); }

@keyframes peekUp {
  0%        { opacity: 0; transform: translateY(26px) scale(.86); filter: blur(6px); }
  14%, 64%  { opacity: 1; transform: none;                        filter: blur(0); }
  100%      { opacity: 0; transform: translateY(-20px) scale(.96); filter: blur(5px); }
}

/* Человеку, который просил не двигать ничего лишнего, стопка показывается
   без полёта: содержимое то же, движения нет. */
@media (prefers-reduced-motion: reduce) {
  .peek { animation: none; }
}

/* — смена отрасли посреди разговора — */

.switch {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  width: min(100%, 780px); margin: 14px auto;
  font-size: 12.5px; color: var(--text-faint);
}
.switch::before, .switch::after {
  content: ""; flex: 1; height: 1px; background: var(--border);
}
.switch__icon { width: 14px; height: 14px; color: var(--accent); }

/* — док с полем ввода — */

.composer-dock {
  padding: 8px 24px 16px;
  background: linear-gradient(to top, var(--bg) 62%, transparent);
}
.composer-dock .composer { width: min(100%, 780px); margin: 0 auto; }
.composer--dock { border-radius: var(--r-lg); box-shadow: var(--shadow-sm); }

/* — чем сервис занят прямо сейчас —

   Строка над полем ввода: анимированные весы и фраза, собирающаяся по буквам
   той же волной, что и строка возможностей на главной (классы `.canline__ch`
   общие — это одна анимация, а не две похожие).

   ⚠️ Высота задана и не зависит от текста: строка появляется и исчезает
   на каждом запросе, и если бы она занимала место по содержимому, поле ввода
   прыгало бы вверх-вниз при каждом ответе. */
.puls {
  width: min(100%, 780px);
  margin: 0 auto 8px;
  padding: 0 4px;
  min-height: 20px;
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; line-height: 1.35;
  color: var(--text-dim);
}
.puls[hidden] { display: none; }

.puls__ic { flex: none; width: 17px; height: 17px; color: var(--accent); }
.puls__svg {
  width: 100%; height: 100%;
  fill: none; stroke: currentColor;
  stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round;
}

/* Качается только коромысло с чашами — стойка стоит, как у настоящих весов.
   `transform-box: view-box` обязателен: без него точка вращения считается
   от габаритов самой группы, и весы вместо покачивания уезжают вбок. */
.puls__beam {
  transform-box: view-box;
  transform-origin: 12px 7.5px;
  animation: pulsVesy 5.5s ease-in-out infinite;
}
.puls__pin { fill: currentColor; stroke: none; animation: pulsPin 5.5s ease-in-out infinite; }
.puls__stand { opacity: .75; }

/* ⚠️ Медленно и на малый угол — это не вкусовщина. Весы у поля ввода стоят
   рядом с бегущей по буквам строкой, и на прежних 2.6 с коромысло проходило
   середину дважды в секунду: два быстрых движения в одной точке экрана
   читаются как суета, а не как работа. Пять с половиной секунд на полный
   размах и ±5° дают качание, которое видно боковым зрением и не отвлекает
   от текста. */
@keyframes pulsVesy {
  0%, 100% { transform: rotate(-5deg); }
  50%      { transform: rotate(5deg); }
}
@keyframes pulsPin {
  0%, 100% { opacity: .55; }
  50%      { opacity: 1; }
}

/* Слова в служебной строке не переносятся посреди слова — та же обёртка,
   что и в строке возможностей, работает и здесь (класс общий). */
.puls__text { min-width: 0; }

/* — правовое обоснование: столбец справа от разговора —

   Экран диалога становится двумя колонками. Сетка страницы (`.layout`)
   при этом НЕ трогается: её правка когда-то оставляла от интерфейса пустой
   экран при сворачивании боковой панели, и второй раз наступать на это
   незачем. Здесь всё живёт внутри экрана.

   Столбец показывается только на широком экране — ниже 1101px он не
   появляется вовсе, а нормы остаются внутри ответа (см. медиазапросы). */

.screen--chat { flex-direction: row; }
.chatmain {
  flex: 1; min-width: 0; min-height: 0;
  display: flex; flex-direction: column;
}

.basis {
  flex: none;
  width: 290px;
  display: flex; flex-direction: column;
  min-height: 0;
  padding: 22px 18px 18px;
  border-left: 1px solid var(--border);
  overflow-y: auto;
}
.basis[hidden] { display: none; }

.basis__head {
  display: flex; align-items: flex-start; gap: 9px;
  padding-bottom: 12px; margin-bottom: 12px;
  border-bottom: 1px solid var(--border);
  color: var(--text-dim);
}
.basis__head .ic { flex: none; width: 16px; height: 16px; margin-top: 1px; color: var(--accent); }
.basis__title { font-size: 13px; font-weight: 620; color: var(--text); }
.basis__note { margin-top: 3px; font-size: 11.5px; line-height: 1.4; color: var(--text-faint); }

.basis__list { display: flex; flex-direction: column; gap: 6px; }

/* ⚠️ Радиус здесь СВОЙ, и это не вкусовщина. В ленте ответа норма — строка
   в одну высоту, и пилюля (999px) ей идёт. В столбце шириной 290 px тот же
   чип переносится на три-четыре строки, и радиус в половину высоты
   превращает карточку в каплю. Радиус — карточный, одинаковый у чипа
   и у его пояснения, иначе столбец выглядит собранным из чужих деталей. */
.basis__list .norm {
  width: 100%; text-align: left;
  padding: 9px 12px;
  border-radius: var(--r-sm);
}
.basis__list .norm__full {
  padding: 9px 12px;
  border-radius: var(--r-sm);
  font-size: 12px; line-height: 1.5;
}

/* Пока идёт работа, весь список приглушён: это ещё кандидаты, а не основание
   ответа. Гасим ОДНИМ правилом на контейнере, а не классом на каждом чипе —
   иначе после ответа пришлось бы снимать класс с каждого. */
.basis.is-thinking .basis__list .norm { opacity: .62; }
.basis.is-thinking .basis__list .norm:hover { opacity: 1; }

/* ══════════════════════════════════════════════════════ СОСТАВ КОРПУСА ═══ */

.screen--corpus { overflow-y: auto; }

.corpus { width: min(100%, 820px); margin: 0 auto; padding: 44px 24px 60px; }
.corpus__title { font-size: 26px; font-weight: 650; letter-spacing: -.02em; }
.corpus__sub { margin: 8px 0 26px; color: var(--text-dim); font-size: 14px; }

.corpus__group { margin-bottom: 26px; }
.corpus__kind {
  margin-bottom: 10px;
  font-size: 12px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-faint);
}

.act {
  display: flex; align-items: baseline; gap: 12px;
  padding: 11px 14px; margin-bottom: 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
/* Строка-ссылка обязана выглядеть нажимаемой: без этого юрист не догадается,
   что оглавление кодекса вообще можно открыть. */
a.act:hover { border-color: var(--border-hi); background: var(--surface-hi); }
a.act:hover .act__title { color: var(--text); }
.act__code {
  flex: none; min-width: 62px;
  font-size: 13px; font-weight: 620; color: var(--accent);
}
.act__title { flex: 1; min-width: 0; font-size: 13.5px; }
.act__count { flex: none; font-size: 12px; color: var(--text-faint); font-variant-numeric: tabular-nums; }

/* ═════════════════════════════════════════════════════════ УВЕДОМЛЕНИЯ ═══ */

.toasts {
  position: fixed; right: 18px; bottom: 18px; z-index: 90;
  display: flex; flex-direction: column; gap: 8px;
  width: min(360px, calc(100vw - 36px));
  pointer-events: none;
}

.toast {
  padding: 11px 14px;
  background: var(--surface-hi);
  border: 1px solid var(--border-hi);
  border-left: 3px solid var(--text-faint);
  border-radius: var(--r-md);
  box-shadow: var(--shadow);
  font-size: 13px; line-height: 1.45;
  animation: toast-in .22s var(--ease);
}
.toast--error   { border-left-color: var(--err); }
.toast--warning { border-left-color: var(--warn); }
.toast--info    { border-left-color: var(--accent); }

@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } }

@media (prefers-reduced-motion: reduce) {
  /* Задержка гасится наравне с длительностью: без неё побуквенная волна
     остаётся на месте и текст, которому «выключили анимацию», продолжает
     проступать по знакам — просто рывками.

     ⚠️ Буквы строки возможностей выведены из-под этого правила намеренно.
     «Уменьшенное движение» — просьба убрать ДВИЖЕНИЕ (сдвиги, параллакс,
     масштаб), а не запрет на плавность вообще; рекомендованная замена —
     обычное затухание. Без исключения строка меняется мгновенным рывком,
     то есть ровно тем, из-за чего эта анимация и появилась: в Windows 11
     достаточно выключить «Эффекты анимации», и человек видит подмену
     текста вместо перехода. Поэтому здесь остаётся то же появление
     по буквам, но без подъёма и без размытия — одна прозрачность. */
  *:not(.canline__ch), *::before, *::after {
    animation-duration: .01ms !important;
    animation-delay: 0s !important;
    transition-duration: .01ms !important;
  }
  .canline__ch.is-in  { animation: canInTih .3s linear both; }
  .canline__ch.is-out { animation: canOutTih .3s linear both; }
  @keyframes canInTih  { from { opacity: 0; } to { opacity: 1; } }
  @keyframes canOutTih { from { opacity: 1; } to { opacity: 0; } }

  .thread { scroll-behavior: auto; }
}

/* ═══════════════════════════════════════════════════ ДОКУМЕНТЫ (Э10.3) ═══

   Три состояния одного пути: предложение собрать документ (docoffer),
   сбор реквизитов по одному вопросу (slots) и готовый документ (docready).
   Все три живут прямо в ленте разговора, а не в модальном окне: документ
   собирается ПО следам разговора, и терять его из виду нельзя. */

.docoffer {
  margin: 2px 0 16px;
  padding: 14px 16px;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  border-left: 2px solid var(--accent);
  border-radius: var(--r-md);
}

.docoffer__head {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 10px;
  font-size: 13px; color: var(--text-dim);
}
.docoffer__head .ic { width: 15px; height: 15px; color: var(--accent); }

.docoffer__list { display: flex; flex-direction: column; gap: 6px; }

.docoffer__item {
  display: flex; flex-direction: column; gap: 2px;
  padding: 9px 12px; text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.docoffer__item:hover { border-color: rgba(240, 102, 59, .45); background: var(--surface-hi); }
.docoffer__item b { font-size: 13.5px; font-weight: 600; }
.docoffer__basis { font-size: 11.5px; color: var(--text-faint); }

.docoffer__skip {
  margin-top: 10px;
  font-size: 12.5px; color: var(--text-faint);
}
.docoffer__skip:hover { color: var(--text-dim); }

/* Развилка, на которую уже ответили. Кнопок нет, остался след выбора —
   поэтому и вид другой: это запись в переписке, а не предложение. Блок
   гасится, но не прячется: через день человек должен понимать, почему
   разговор пошёл этим путём. */
.docoffer--done {
  margin-bottom: 10px;
  padding: 9px 14px;
  border-left-color: var(--border-hi);
}
.docoffer--done .docoffer__head { margin-bottom: 0; color: var(--text-faint); }
.docoffer--done .docoffer__head .ic { color: var(--text-faint); }

/* ------------------------------------------------------- сбор реквизитов */

.slots {
  margin: 4px 0 18px;
  padding: 16px 18px;
  background: var(--surface);
  border: 1px solid var(--border-hi);
  border-radius: var(--r-md);
}

.slots__title { font-size: 15px; font-weight: 600; margin-bottom: 4px; }
.slots__lead  { font-size: 12.5px; color: var(--text-dim); margin-bottom: 12px; }

.slots__done { display: flex; flex-direction: column; gap: 5px; }
.slots__done:not(:empty) { margin-bottom: 12px; }

.slots__row {
  display: flex; align-items: baseline; gap: 7px;
  font-size: 12.5px; color: var(--text-dim);
}
.slots__row b { color: var(--text-faint); font-weight: 500; }
.slots__val { color: var(--text); }
.slots__ok { width: 13px; height: 13px; flex: none; color: var(--ok); align-self: center; }

.slots__step {
  font-size: 11px; letter-spacing: .05em; text-transform: uppercase;
  color: var(--text-faint); margin-bottom: 5px;
}
.slots__q    { font-size: 14.5px; font-weight: 500; }
.slots__hint { margin-top: 5px; font-size: 12.5px; color: var(--text-dim); }
.slots__law  { margin-top: 4px; font-size: 11.5px; color: var(--accent); }

/* Требования формы из норм: нотариус, регистрация, письменная форма.
   Показываются перед началом допроса. */
.slots__warnings { margin-bottom: 12px; }

.slots__warning {
  padding: 10px 12px;
  background: rgba(240, 102, 59, .12);
  border-left: 2px solid var(--accent);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  font-size: 12.5px; line-height: 1.5; color: var(--text-dim);
  margin: 0 0 6px;
}
.slots__warning:last-child { margin-bottom: 0; }

/* Подсказка на необязательный вопрос. */
.slots__optional {
  margin-top: 5px;
  font-size: 11.5px; color: var(--text-faint); font-style: italic;
}

.slots__wait { font-size: 13px; color: var(--text-dim); }
.slots__err  { font-size: 13px; color: var(--err); }
.slots__cancelled { font-size: 12.5px; color: var(--text-faint); }

.slots__cancel, .slots__retry {
  margin: 10px 10px 0 0;
  padding: 6px 12px;
  border: 1px solid var(--border-hi);
  border-radius: 999px;
  font-size: 12.5px; color: var(--text-dim);
}
.slots__cancel:hover, .slots__retry:hover { color: var(--text); border-color: var(--accent); }

/* --------------------------------------------------------- готовый документ */

.docready { margin-top: 4px; }

.docready__head {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 10px; font-size: 14px;
}
.docready__head .ic { width: 15px; height: 15px; color: var(--accent); }

/* Документ показывается моноширинным и с сохранением переносов: это уже
   не разговор, а текст, который пойдёт в суд, и его расположение по строкам
   имеет значение. */
.docready__text {
  max-height: 460px; overflow: auto;
  margin: 0; padding: 14px 16px;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font: 12.5px/1.6 ui-monospace, "SF Mono", "Cascadia Mono", Consolas, monospace;
  white-space: pre-wrap; word-break: break-word;
}

.docready__note {
  margin-top: 8px;
  font-size: 12px; color: var(--warn);
}

.docready__row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }

.docready__dl {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 14px;
  background: var(--surface-hi);
  border: 1px solid var(--border-hi);
  border-radius: 999px;
  font-size: 13px;
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.docready__dl:hover:not(:disabled) { border-color: var(--accent); background: var(--surface); }
.docready__dl:disabled { opacity: .5; cursor: progress; }
.docready__dl .ic { width: 14px; height: 14px; }

/* Редактирование и пересборка документа — для свободной сборки. */
.docready__edit { margin-top: 16px; }

.docready__edit-label { margin-bottom: 6px; font-size: 13px; color: var(--text-dim); }

.docready__input {
  width: 100%; padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: inherit;
  resize: vertical;
}
.docready__input:focus {
  outline: none;
  border-color: var(--accent);
}

.docready__revise {
  margin-top: 8px;
  padding: 8px 14px;
  background: var(--surface-hi);
  border: 1px solid var(--border-hi);
  border-radius: 999px;
  font-size: 13px;
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.docready__revise:hover:not(:disabled) { border-color: var(--accent); background: var(--surface); }
.docready__revise:disabled { opacity: .5; cursor: progress; }

/* ------------------------------------------------- разбор чужого документа */

.review { margin-top: 4px; }

/* Ожидание — единственное место в интерфейсе, где счёт идёт на десятки
   секунд: модель читает весь документ с усилием `high`. Поэтому строка
   ожидания не «спиннер», а фраза, объясняющая, что именно сейчас происходит. */
.review--wait {
  display: flex; align-items: center; gap: 10px;
  /* Строка ожидания живёт в ленте рядом с сообщениями, а не внутри одного
     из них, поэтому ширину повторяет за `.msg` — иначе она вылезает
     из колонки чтения. */
  width: min(100%, 780px); margin: 0 auto 22px;
  padding: 12px 16px;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-size: 13px; color: var(--text-dim);
}
.review--wait .ic { width: 15px; height: 15px; color: var(--accent); flex: none; }
.review--wait span { animation: reviewPulse 1.6s var(--ease) infinite; }

@keyframes reviewPulse { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }

@media (prefers-reduced-motion: reduce) {
  .review--wait span { animation: none; }
}

.review__head {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 10px; font-size: 14px;
}
.review__head .ic { width: 15px; height: 15px; color: var(--accent); flex: none; }

.review__title {
  margin: 16px 0 8px;
  font-size: 12px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-faint);
}
.review__title:first-of-type { margin-top: 4px; }

/* Предупреждение — про обрезанный текст и про ненайденные в корпусе нормы.
   И то и другое означает «разбор неполный», поэтому цвет один. */
.review__warn {
  margin: 10px 0 0; padding: 10px 12px;
  background: rgba(232, 184, 75, .10);
  border-left: 2px solid var(--warn);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  font-size: 12.5px; line-height: 1.6; color: var(--text-dim);
}

.review__list {
  display: flex; flex-direction: column; gap: 10px;
  margin: 0; padding: 0; list-style: none;
  counter-reset: zamechanie;
}

/* Номер замечания рисуется счётчиком, а не маркером списка: у пункта своя
   рамка, и штатный маркер оказался бы за её пределами. */
.review__item {
  position: relative;
  padding: 12px 14px 12px 40px;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  counter-increment: zamechanie;
}
.review__item::before {
  content: counter(zamechanie);
  position: absolute; left: 12px; top: 12px;
  width: 20px; height: 20px;
  display: grid; place-items: center;
  background: var(--accent-soft);
  border-radius: 50%;
  font-size: 11px; font-weight: 600; color: var(--accent);
}

.review__place {
  display: block; margin-bottom: 4px;
  font-size: 11.5px; color: var(--text-faint);
}
.review__problem { margin: 0; font-size: 13.5px; line-height: 1.65; }

.review__article {
  display: inline-block; margin-top: 8px;
  padding: 3px 9px;
  background: var(--accent-soft);
  border-radius: 999px;
  font-size: 11.5px; color: var(--accent);
}

/* Предложение исправления отделено от самого замечания: «что не так»
   и «как починить» — разные мысли, и юрист читает их по отдельности. */
.review__fix {
  margin: 8px 0 0; padding-top: 8px;
  border-top: 1px dashed var(--border);
  font-size: 13px; line-height: 1.6; color: var(--text-dim);
}

.review__missing {
  margin: 0; padding-left: 20px;
  display: flex; flex-direction: column; gap: 6px;
  font-size: 13.5px; line-height: 1.6;
}
.review__missing li::marker { color: var(--accent); }

.review__err {
  margin: 0;
  font-size: 13px; color: var(--err);
}

/* ══════════════════════════════════════════════════════════ АДАПТАЦИЯ ═══ */

/* Нормы показываются В ОДНОМ месте, а не в двух. Есть место под столбец —
   они живут в столбце, и внутри ответа их нет; места нет — столбца нет,
   а нормы остаются в ответе, как и были. Оба представления сразу — это
   один и тот же список, показанный дважды.

   Порог: 268px боковой панели + разговор + 290px столбца. Ниже этого
   разговор становится уже, чем стоит читать. */
@media (min-width: 1181px) {
  .msg .norms { display: none; }
}
@media (max-width: 1180px) {
  .basis { display: none; }
}

@media (max-width: 900px) {
  .layout { grid-template-columns: 1fr; }

  /* На узком экране сайдбар не сжимается, а наезжает поверх: колонка в 268px
     на телефоне съела бы половину ширины чтения. */
  .sidebar {
    position: fixed; inset: 0 auto 0 0; z-index: 60;
    width: var(--sidebar-w);
    transform: translateX(-100%);
    transition: transform .22s var(--ease);
  }
  .layout.is-open .sidebar { transform: none; box-shadow: var(--shadow); }

  .scrim {
    display: block;
    position: fixed; inset: 0; z-index: 50;
    background: rgba(0, 0, 0, .5);
    opacity: 0; pointer-events: none;
    transition: opacity .22s var(--ease);
  }
  .layout.is-open .scrim { opacity: 1; pointer-events: auto; }

  .topbar { display: flex; }

  .home { padding: 34px 16px 40px; }
  .corpus { padding: 28px 16px 48px; }
  .thread { padding: 18px 14px 4px; }
  .composer-dock { padding: 6px 14px 12px; }
  .toasts { right: 12px; bottom: 12px; }

  /* Узкая колонка режет фразу возможностей на три строки — место под них
     резервируется здесь, иначе форма ввода прыгает при каждой смене. */
  .canline { min-height: 4.5em; margin-bottom: 12px; font-size: 13.5px; }
  .composer--hero { padding: 15px 14px 12px; }
  .composer--hero .composer__input { min-height: 44px; font-size: 16px; }
  .composer::before { inset: -10px; filter: blur(20px); }

  /* `min(100%, …)` вместо голых 200px: на экране уже 200px обычный minmax
     вылезает за границу и даёт горизонтальную прокрутку всей страницы. */
  .presets { margin-top: 32px; }
  .presets__head { flex-direction: column; gap: 8px; }
  .presets__grid { grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr)); }
  .preset { min-height: 0; }

  .slots { padding: 14px; }
  .docoffer { padding: 12px 14px; }
  .docready__text { max-height: 320px; padding: 12px; font-size: 12px; }
}

/* ---------------------------------------------------------------- телефон

   На ширине телефона у формы кончается место, и разбирается это не «как-нибудь
   помельче», а по смыслу каждого элемента.

   Подписи убираются там, где значок говорит то же самое: скрепка — приложить
   документ, книга — выбор источников. У чипа сферы права подпись ОСТАЁТСЯ:
   это не кнопка, а показание — какая отрасль сейчас применяется, и по значку
   её не прочитать. Зато она единственная переменной длины, поэтому режется
   многоточием.

   Ряд намеренно не переносится: перенос — это и есть то самое «съезжает»,
   когда форма то в одну строку, то в две, и кнопка отправки прыгает. Вместо
   переноса ужимается ровно то, что можно ужать, — подпись отрасли. */
@media (max-width: 560px) {
  .composer { padding: 12px 12px 10px; }
  .composer--hero { padding: 14px 12px 10px; }
  .composer-dock { padding: 6px 10px 10px; }

  .composer__row { gap: 6px; }
  .composer__filters { flex-wrap: nowrap; gap: 6px; }

  .chip { min-width: 0; gap: 5px; padding: 7px 9px; }
  .chip .ic { width: 16px; height: 16px; }

  .composer [data-action^="attach"] .chip__text,
  .composer [data-dropdown="sources"] .chip__text { display: none; }

  /* Стрелка выпадающего списка на телефоне не нужна: список открывается
     нажатием по всему чипу, а лишний знак ест ширину подписи. */
  .composer .chip__caret { display: none; }

  .composer [data-dropdown="branch"] .chip__text,
  .composer .chip--static .chip__text { max-width: 40vw; }

  .send { width: 36px; height: 36px; }

  /* Подсказка на узком экране занимает две строки, а поле было ростом
     в полторы: вторая строка обрезалась ровно посередине букв — форма
     выглядела съехавшей. Заодно две строки честно показывают, сколько
     здесь ждут текста: «развод» вместо ситуации — самый частый первый
     вопрос. */
  .composer--hero .composer__input { min-height: 62px; }
}
