/* QADash — внешние стили компонентов. Подключён из templates/base.html ПОСЛЕ инлайн-блока
   с токенами темы, поэтому может переопределять инлайн-правила. Токены (:root и
   html[data-theme="dark"]) живут только в base.html и здесь не дублируются.
   Здесь живут стили всех компонентов приложения (визуальный язык — макет редизайна docs/redesign). */

/* --- Шрифт Inter (self-hosted, OFL; D2-13 — без CDN) ---
   Inter v4.1 (rsms/inter, тег v4.1), sha256 693b77d4f32ee9b8bfc995589b5fad5e99adf2832738661f5402f9978429a8e3 */
@font-face{font-family:"Inter";src:url("../vendor/inter/InterVariable.2bf3d951bf9d.woff2") format("woff2");font-weight:100 900;font-style:normal;font-display:swap;}

/* === templates/base.html: каркас ===
   Перенесено из инлайн-<style> base.html (там остались только токены и анти-мигание:
   :root/тёмная тема, box-sizing, font:inherit, body). Секция стоит ДО остальных секций
   app.css — каскад тот же, что был у инлайн-блока (он шёл раньше app.css). Шелл по макету
   docs/redesign (styles.css 03.1): фиксированный .sidebar + .main-shell (.topbar + main).
   Цвета — только токены, чтобы тёмная тема и акцент проекта (блок theme) работали. */
html[data-theme="dark"]{color-scheme:dark;}
a{color:var(--accent); text-decoration:none;}
a:hover{color:var(--accent2);}
/* Alpine: элементы с x-cloak скрыты до инициализации, иначе мигают при загрузке */
[x-cloak]{display:none !important;}
/* Базовые поля ввода без собственных стилей (напр. «Поиск по дефектам»): фон/рамка из
   токенов, иначе в тёмной теме нативное поле остаётся белым. :where — нулевая
   специфичность, любые правила компонентов и инлайн-style перекрывают. */
:where(input:not([type=checkbox],[type=radio],[type=file],[type=range],[type=color],[type=hidden],[type=submit],[type=button]),select,textarea){
  background:var(--surface); color:var(--text); border:1px solid var(--line); border-radius:7px; padding:7px 10px;}
:where(input,select,textarea):focus{outline:2px solid var(--accent); outline-offset:0;}
:where(input,select,textarea)::placeholder{color:var(--muted); opacity:.8;}

/* --- plain-режим: простая шапка (аноним/логин/токен-гость) --- */
header.site{
  position:sticky; top:0; z-index:10;
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  padding:12px 16px; background:linear-gradient(var(--header-top),var(--header-bot));
  border-bottom:1px solid var(--line);
}
header.site .brand{display:flex; align-items:center; gap:12px;}
header.site .actions{display:flex; align-items:center; gap:8px;}
footer.site{padding:16px; color:var(--muted); font-size:12px; border-top:1px solid var(--line); margin-top:48px;}
.content-inner > footer.site{border:0; margin-top:48px; padding:16px 0;}

/* --- Флеш-сообщения (Django messages): success/info — зелёный, error/warning отдельно --- */
.saved-flash{
  margin:8px 0; padding:8px 12px; border-radius:8px;
  background:var(--green-bg); color:var(--green); border:1px solid var(--green);
  font-weight:600;
}
.saved-flash--error{background:var(--red-bg); color:var(--red); border-color:var(--red);}
.saved-flash--warning{background:var(--orange-bg); color:var(--orange); border-color:var(--orange);}
/* Подсказка внутри формы (напр. «Bitrix не подключён» в форме задачи), НЕ уровень сообщений
   Django: base.html строит saved-flash--{level_tag}, поэтому имя --info занято под messages.info. */
.saved-flash--hint{border-left:3px solid var(--accent);}

/* --- Глобальный тост о завершении AI-генерации (поллер base.html → /aigen/pending/).
   Левый нижний угол, стопка карточек; закрытие крестиком, клик по телу → к ревью. --- */
#aigen-toast-wrap{position:fixed; left:16px; bottom:16px; z-index:1000;
  display:flex; flex-direction:column; gap:10px; max-width:340px;}
.aigen-toast{display:flex; align-items:stretch; gap:4px; padding:0;
  background:var(--surface); border:1px solid var(--line); border-left:4px solid var(--accent);
  border-radius:10px; box-shadow:var(--shadow);
  animation:aigen-toast-in .22s ease-out;}
.aigen-toast--ok{border-left-color:var(--green);}
.aigen-toast--err{border-left-color:var(--red);}
.aigen-toast--out{opacity:0; transform:translateY(8px); transition:opacity .2s, transform .2s;}
.aigen-toast-body{flex:1; min-width:0; display:block; padding:12px 4px 12px 14px;
  text-decoration:none; color:var(--text); cursor:pointer;}
.aigen-toast-title{font-weight:700; font-size:13.5px;}
.aigen-toast--ok .aigen-toast-title{color:var(--green);}
.aigen-toast--err .aigen-toast-title{color:var(--red);}
.aigen-toast-sub{color:var(--muted); font-size:12px; margin-top:2px; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap;}
.aigen-toast-x{flex:none; align-self:flex-start; background:none; border:0; color:var(--muted);
  font-size:20px; line-height:1; padding:8px 10px; cursor:pointer; border-radius:8px;}
.aigen-toast-x:hover{color:var(--text); background:var(--side);}
@keyframes aigen-toast-in{from{opacity:0; transform:translateY(10px);} to{opacity:1; transform:translateY(0);}}
@media (prefers-reduced-motion: reduce){ .aigen-toast{animation:none;} }

/* --- Бейджи. Legacy severity (.badge + .b-{severity}) — белый текст на цвете severity;
   цветовые модификаторы макета (.badge.red/.green/.orange/.purple) — мягкая заливка. --- */
.badge{display:inline-flex; align-items:center; gap:5px; padding:3px 8px; border-radius:5px; font-size:11px; font-weight:600; color:#fff; line-height:1.4; white-space:nowrap;}
.b-blocker{background:var(--blocker);}
.b-critical{background:var(--sev-critical);}
.b-major{background:var(--major);}
.b-minor{background:var(--minor); color:#26210a;}
.b-none{background:var(--none);}
.badge.red{color:var(--red); background:var(--red-bg);}
.badge.green{color:var(--green); background:var(--green-bg);}
.badge.orange{color:var(--orange); background:var(--orange-bg);}
.badge.purple{color:var(--accent); background:var(--accent-soft);}
/* Статус-чип: мягкая пилюля макета */
.chip{display:inline-flex; align-items:center; padding:3px 8px; border-radius:5px; background:var(--bg); border:1px solid var(--line); font-size:11px; color:var(--text); white-space:nowrap;}
/* Статус из списка: select выглядит как чип; is-saved — короткая подсветка после сохранения. */
.status-inline{margin:0; display:inline-block;}
.status-select{font:inherit; font-size:11px; padding:3px 6px; border-radius:5px; background:var(--bg); border:1px solid var(--line); color:var(--text); cursor:pointer; width:auto; min-height:0;}
.status-inline.is-saved .status-select{animation:status-saved 1.4s ease-out;}
@keyframes status-saved{from{border-color:var(--ok);}to{border-color:var(--line);}}
/* Цветные модификаторы чипа статуса (кейсы, автотесты): рамка + текст в цвет статуса;
   --accent — только рамка («Нужен ответ»). */
.chip--ok{border-color:var(--ok); color:var(--ok);}
.chip--bad{border-color:var(--critical); color:var(--critical);}
/* --muted меняет и border-color — осознанное отличие ре-скина. */
.chip--muted{border-color:var(--muted); color:var(--muted);}
.chip--warn{border-color:var(--major); color:var(--major);}
.chip--accent{border-color:var(--accent);}

/* --- Таблицы. Legacy .tableScroll (дашборд, списки) + контейнеры макета (.panel/.table-wrap).
   Голый table не стилизуется глобально: таблицы ревью AI и форм держат свою разметку. --- */
.tableScroll{overflow:auto; border:1px solid var(--line); border-radius:var(--radius); background:var(--surface);}
.tableScroll table{border-collapse:collapse; width:100%;}
.tableScroll th, .tableScroll td{text-align:left; border-bottom:1px solid var(--line);}
/* Шапки таблиц: uppercase — осознанное отклонение от макета (10px без uppercase читается хуже);
   тот же вид у .panel th/.table-wrap th ниже — один стиль шапок на весь файл. */
.tableScroll th{padding:10px 14px; font-weight:500; font-size:10px; text-transform:uppercase; letter-spacing:.04em;
  color:var(--muted); background:var(--bg); white-space:nowrap;}
.tableScroll thead th{position:sticky; top:0; z-index:1;}
.tableScroll td{padding:12px 14px; font-size:12px; vertical-align:top;}
.tableScroll tbody tr:last-child td{border-bottom:0;}
.tableScroll tbody tr:hover{background:var(--row-hover);}
.tableScroll tbody tr[data-href]{cursor:pointer;}
.table-wrap{overflow:auto;}
.panel table, .table-wrap table{border-collapse:collapse; width:100%; text-align:left; font-size:12px;}
.panel th, .table-wrap th{font-weight:500; font-size:10px; text-transform:uppercase; letter-spacing:.04em; color:var(--muted); background:var(--bg); padding:10px 19px;
  border-top:1px solid var(--line); border-bottom:1px solid var(--line); white-space:nowrap;}
.panel td, .table-wrap td{padding:14px 19px; border-bottom:1px solid var(--line);}
.panel tr:last-child td, .table-wrap tr:last-child td{border-bottom:0;}
tr[data-href]{cursor:pointer;}

/* Миниатюра (legacy .thumb 74×48) */
.thumb{width:74px; height:48px; border-radius:6px; object-fit:cover; background:#000; cursor:zoom-in; border:1px solid var(--line);}

/* --- Карточка / панель. .card — legacy, .panel* — макет --- */
.card{background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); padding:16px;}
/* Чекбоксы/радио не растягиваются (R.1); :where сохраняет вес .card input. */
.card input:where(:not([type=checkbox]):not([type=radio])), .card select, .card textarea{width:100%; padding:8px; border-radius:8px; background:var(--surface); color:var(--text); border:1px solid var(--line); font:inherit;}
.panel{border:1px solid var(--line); border-radius:var(--radius); background:var(--surface); overflow:hidden;}
.panelhead{padding:19px 21px; display:flex; justify-content:space-between; gap:12px; align-items:center;}
.panelhead p{font-size:11px; color:var(--muted); margin:3px 0 0;}
.panelbody{padding:0 21px 21px;}
/* Сворачиваемая карточка (details.card): заголовок-summary с шевроном + счётчиком. */
.collapse-card{padding:0;}
.collapse-card > summary{list-style:none; cursor:pointer; user-select:none;
  display:flex; align-items:center; gap:10px; padding:14px 16px; font-size:16px; font-weight:600;}
.collapse-card > summary::-webkit-details-marker{display:none;}
.collapse-card > summary .chev{color:var(--muted); flex:none; transition:transform .15s ease;}
.collapse-card[open] > summary .chev{transform:rotate(90deg);}
.collapse-card[open] > summary{border-bottom:1px solid var(--line);}
.collapse-card > summary .count{margin-left:auto; font-size:12px; font-weight:600; color:var(--muted);
  background:var(--side); border:1px solid var(--line); border-radius:20px; padding:1px 9px;}
.collapse-card .cc-body{padding:16px;}

/* --- Кнопки (макет: .btn, .btn-primary; .btn.primary — старое имя макета, алиас) --- */
/* hidden управляет видимостью кнопок повторной загрузки и других JS-состояний. */
.btn[hidden]{display:none !important;}
.btn{display:inline-flex; align-items:center; justify-content:center; gap:8px; padding:8px 13px; min-height:35px;
  border:1px solid var(--line); border-radius:7px; background:var(--surface); color:var(--text);
  font-size:12px; font-weight:550; line-height:1.3; white-space:nowrap; cursor:pointer; text-decoration:none;
  vertical-align:middle;}
.btn:hover{border-color:var(--accent); color:var(--accent);}
.btn:disabled{opacity:.5; cursor:default;}
.btn-primary, .btn.primary{background:var(--accent); border-color:var(--accent); color:var(--on-accent);}
.btn-primary:hover, .btn.primary:hover{background:var(--accent2); border-color:var(--accent2); color:var(--on-accent);}
/* Выбранный вариант в группе кнопок (напр. текущий статус кейса) */
.btn.is-active{border-color:var(--accent); color:var(--accent);}
/* Опасное действие (план: .btn.btn-danger = --red): красный текст и рамка везде; hover — заливка --red */
.btn-danger{color:var(--red); border-color:color-mix(in srgb, var(--red) 35%, var(--line));}
.btn-danger:hover{background:var(--red); border-color:var(--red); color:var(--on-accent);}
.iconbtn{display:inline-flex; align-items:center; justify-content:center; min-width:36px; height:36px; padding:0 8px;
  border:0; border-radius:6px; background:none; color:var(--muted); cursor:pointer; font-size:16px; line-height:1;}
.iconbtn:hover{background:var(--accent-soft); color:var(--accent);}

/* Пустое состояние */
.empty{padding:48px 16px; text-align:center; color:var(--muted);}
.empty h2{font-size:28px; font-weight:600; color:var(--text); margin:0 0 8px;}
/* Пустое состояние-карточка (дашборд дефектов): рамка панели макета */
.empty--card{border:1px solid var(--line); border-radius:var(--radius); background:var(--surface); padding:45px 24px;}
/* 18px — компромисс между legacy 28px и базовым h2 макета 15px */
.empty--card h2{font-size:18px; margin:0 0 6px;}
/* Служебное */
.toolbar{display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:16px;}
.log{list-style:none; padding:0; margin:0;}
.log li{font-size:12px; color:var(--muted); padding:4px 0; border-bottom:1px solid var(--line);}
.pair{display:flex; gap:32px; flex-wrap:wrap; margin-bottom:16px;}
.pair .card{flex:1 1 280px;}
:focus-visible{outline:2px solid var(--accent); outline-offset:1px;}

/* --- Галерея вложений и лайтбокс (MEDIA-01, перенос legacy .thumb/.lb) --- */
.gallery{display:flex; flex-wrap:wrap; gap:12px; margin:12px 0;}
.att{position:relative;}
.att .thumb{padding:0; border:1px solid var(--line); display:block;}
.att .att-del{
  position:absolute; top:-8px; right:-8px; width:24px; height:24px;
  border-radius:50%; border:1px solid var(--line); background:var(--side);
  color:var(--muted); font-size:14px; line-height:1; cursor:pointer;
  display:flex; align-items:center; justify-content:center;
}
.att .att-del:hover{border-color:var(--red); color:var(--red);}
.att .att-video{
  width:74px; height:48px; border-radius:6px; border:1px solid var(--line);
  background:#000; color:var(--muted); font-size:11px; display:flex;
  flex-direction:column; gap:4px;
  align-items:center; justify-content:center; text-align:center; padding:4px;
}
.att .att-video-failed{color:var(--red); border-color:var(--red);}
/* --- Инлайн-видео с постером (MEDIA-02, legacy .videoCard radius 12) --- */
.att .videoCard{position:relative;}
.att .videoCard video{
  display:block; max-width:320px; width:100%; border-radius:12px;
  border:1px solid var(--line); background:#000;
}
/* Статус-бейдж вложения-видео (UI-SPEC): Готово/Обрабатывается/Ошибка. */
.att-badge{
  display:inline-block; padding:1px 7px; border-radius:6px;
  font-size:11px; font-weight:600; line-height:1.5;
}
.att .videoCard .att-badge{position:absolute; top:8px; left:8px;}
.att-badge-ok{background:var(--green-bg); color:var(--green);}
.att-badge-muted{background:var(--side); color:var(--muted); border:1px solid var(--line);}
.att-badge-critical{background:var(--red); color:var(--on-accent);}
/* Спиннер обработки видео. */
.att-spinner{
  width:16px; height:16px; border-radius:50%;
  border:2px solid var(--line); border-top-color:var(--accent);
  animation:att-spin .8s linear infinite;
}
@keyframes att-spin{to{transform:rotate(360deg);}}
.lb{
  position:fixed; inset:0; background:rgba(0,0,0,.9); z-index:1000;
  display:flex; align-items:center; justify-content:center; padding:32px;
}
.lb img{max-width:100%; max-height:100%; border-radius:8px;}
.lb .lb-close{
  position:absolute; top:16px; right:20px; background:none; border:none;
  color:#fff; font-size:32px; line-height:1; cursor:pointer; padding:8px;
  min-width:44px; min-height:44px;
}
/* --- Загрузчик: цвет текста зоны (рамку/фон/отступы задаёт секция forms.js ниже) --- */
.dropzone{color:var(--muted);}
.dropzone.dragover{color:var(--text);}
.kbd{
  display:inline-block; padding:1px 6px; border-radius:6px;
  background:var(--side); border:1px solid var(--line);
  font:12px/1.4 ui-monospace,"SF Mono",Menlo,Consolas,monospace; color:var(--text);
}
.uploader-hint{font-size:13px; margin:8px 0;}
.uploader-err{color:var(--red); font-size:13px; margin-top:8px; min-height:18px;}
.uploader-busy{color:var(--muted); font-size:13px; margin-top:8px;}

/* === defects/_dashboard === */
/* Счётчики severity = пилюли-фильтры макета (.filter-pills > .filterpill); .stat = <a> */
.stats{display:flex; gap:8px; flex-wrap:wrap; margin-bottom:20px;}
.stat{display:inline-flex; align-items:center; gap:6px; border:1px solid var(--line); padding:5px 10px;
  border-radius:6px; background:var(--surface); font-size:11px; font-weight:500; color:var(--muted);
  text-decoration:none; cursor:pointer; user-select:none;}
.stat b{font-weight:650; color:var(--text);}
.stat:hover{border-color:var(--accent); color:var(--accent);}
.stat:hover b{color:var(--accent);}
.stat.active{border-color:var(--accent); color:var(--accent); background:var(--accent-soft);}
.stat.active b{color:var(--accent);}
.sev-dot{display:inline-block; width:8px; height:8px; border-radius:50%; flex:none;}
.sev-dot--blocker{background:var(--blocker);}
.sev-dot--critical{background:var(--sev-critical);}
.sev-dot--major{background:var(--major);}
.sev-dot--minor{background:var(--minor);}
.sev-dot--none{background:var(--none);}
/* Общий Alpine-скоуп тулбара и таблицы, не ломающий grid-раскладку */
.contents{display:contents;}
/* Тулбар дашборда: фильтры слева, действия справа (макет .toolbar); общий .toolbar
   других страниц не меняется — раскладка задана модификатором .toolbar--dashboard */
.toolbar--dashboard{align-items:flex-start; gap:12px; flex-wrap:wrap;}
.toolbar-filters, .toolbar-actions{display:flex; align-items:center; gap:8px; flex-wrap:wrap;}
.toolbar-filters{flex:1 1 420px;}
.toolbar-filters input[type=search]{flex:1 1 200px; min-width:160px; min-height:36px; width:auto; font-size:12px;}
.toolbar .cb{max-width:210px; min-width:0;}
.toolbar .cb-trigger{font-size:12px; padding:9px; min-height:36px;}
.toolbar .btn{min-height:36px;}
/* Таблица дефектов */
.defects-table{min-width:960px;}
/* Колонки «Ожидание»/«Факт»: текст переносится, а не сжимается. */
.defects-table .th-text{min-width:170px;}
.tableScroll th.th-check{width:40px;}
.tableScroll td.td-check{padding:0; position:relative; width:40px;}
.td-check label{position:absolute; inset:0; display:flex; align-items:flex-start; padding:12px 14px 0; cursor:pointer;}
.th-sort{color:inherit; text-decoration:none; white-space:nowrap;}
.th-sort:hover{color:var(--accent);}
/* Кнопка-обёртка миниатюры: рамку/фон несёт вложенный img.thumb, без двойной рамки */
.tableScroll button.thumb{padding:0; border:0; background:none; display:block; line-height:0; width:auto; height:auto;}
.table-legend{padding:12px 16px; font-size:12px; color:var(--muted); margin:0; border-top:1px solid var(--line);}
@media (max-width:760px){
  .toolbar .cb{max-width:none; flex:1 1 140px;}
  /* мобильный тач-таргет 40px: перебиваем .toolbar .btn/.cb-trigger (36px) */
  .toolbar .btn, .toolbar .cb-trigger, .toolbar-filters input[type=search]{min-height:40px;}
  .toolbar-filters{flex-basis:100%;}
}

/* === defects/defect_detail ===
   Карточка дефекта (макет 03.1: .pagehead, .panel, .data-grid, .cv-label/.prose,
   .attachment-card, .history-line). .card глобально не меняется — раскладка
   скоупится через .defect-view; модификаторы .card--* общие с подтверждениями. */
/* Шапка: слева возврат + бейджи, справа действия (макет .pagehead) */
.defect-head{display:flex; align-items:flex-start; justify-content:space-between; gap:16px; flex-wrap:wrap; margin-bottom:20px;}
.defect-head-meta{display:flex; flex-direction:column; gap:10px; min-width:0;}
.defect-head-meta .badges{display:flex; flex-wrap:wrap; gap:6px; align-items:center;}
.defect-head-actions{display:flex; flex-wrap:wrap; gap:8px; align-items:center; flex:none; max-width:100%;}
.defect-head-actions .b24-actions{display:inline-flex; gap:8px; align-items:center;}
/* Фолбэк копирования для Б24: textarea вне экрана, но НЕ .sr-only и не display:none.
   В отличие от .sr-only здесь нет clip/overflow:hidden — textarea остаётся полноценным
   выделяемым полем, и select() в defect_copy.js выделяет весь текст. */
.offscreen{position:absolute; left:-9999px; top:0; width:1px; height:1px; opacity:0;}
/* Карточки-секции (макет .panel) */
.defect-view > .card{margin-bottom:16px;}
.card-title{display:block; font-size:12px; font-weight:600; color:var(--muted); margin-bottom:10px;}
/* Поля данных (лежат прямо в .card): подпись = .cv-label, значение = .prose макета.
   Поля форм (статус, комментарии) вложены в <form> и сохраняют вид .fld из секции forms.js. */
.defect-view .card > .fld, .cv-block .prose{font-size:13.5px; line-height:1.55; overflow-wrap:anywhere;}
.defect-view .card > .fld{margin-bottom:14px;}
/* Подпись поля — общий набор с .cv-label шторки тест-кейса; отличаются только отступом. */
.defect-view .card > .fld > label, .cv-label{font-size:11px; font-weight:700; letter-spacing:.06em;
  text-transform:uppercase; color:var(--muted);}
.defect-view .card > .fld > label{display:block; margin-bottom:6px;}
.defect-view .card > .fld:last-child{margin-bottom:0;}
/* Короткие поля — сетка в две колонки (макет .data-grid); margin:0 — после правила .fld той же специфичности */
.card--grid{display:grid; grid-template-columns:1fr 1fr; gap:14px 24px;}
.defect-view .card--grid > .fld{margin:0;}
/* Галерея карточки: миниатюры 320×200 (макет .attachment-card img).
   share/defect_detail.html использует те же размеры (5.1). */
.defect-view .gallery{gap:16px;}
.defect-view .att .thumb{width:auto; height:auto; border-radius:8px; overflow:hidden; line-height:0;}
.defect-view .att .thumb img{display:block; width:320px; height:200px; object-fit:cover;}
.defect-view .att .att-video{width:320px; height:200px; border-radius:8px;}
/* Узкая карточка (форма статуса, страницы подтверждения) и модалка */
.card--narrow{max-width:560px;}
.card--modal{max-width:420px;}
.card--modal h2{margin:0 0 8px; font-size:18px;}
.modal-actions{margin-top:16px; justify-content:flex-end;}
.confirm-list{color:var(--muted); margin:8px 0 14px; padding-left:20px; line-height:1.7;}
/* Пустое состояние внутри карточки */
.empty--inline{padding:24px 16px;}
.empty--inline h2{font-size:20px;}
/* Лента: компактные кнопки и отступы форм.
   На мобильных min-height 40px из медиа-правила .btn (тач-таргет) — не убирать. */
.btn-sm{padding:2px 8px; font-size:12px; min-height:0;}
.c-reply{margin-top:8px;}
.c-form{margin-top:8px;}
.c-new{margin-top:16px;}
/* Утилиты */
.muted{color:var(--muted);}
.inline{display:inline;}
@media (max-width:760px){
  .card--grid{grid-template-columns:1fr;}
  .defect-head-actions{flex:1 1 100%;}
  .defect-view .att, .defect-view .att .thumb{width:100%;}
  .defect-view .att .thumb img{width:100%; height:auto; aspect-ratio:16/10;}
  .defect-view .att .att-video{width:100%; height:auto; aspect-ratio:16/10;}
}

/* === projects / tasks ===
   Список проектов (сетка карточек макета 03.1), форма проекта, страница доступа,
   блок публичных ссылок (_share_links.html на дашборде проекта); формы задач. */
.projects{display:grid; grid-template-columns:repeat(3,1fr); gap:20px;}
.project-card{display:block; padding:23px; text-align:left; text-decoration:none; color:inherit; overflow-wrap:anywhere; transition:border-color .12s;}
.project-card:hover{border-color:var(--accent);}
.project-card .proj-logo, .project-card .proj-logo-ph{height:42px; margin-bottom:22px;}
.project-card .proj-logo{display:block; max-width:160px;}
.project-card .proj-logo-ph{min-width:42px; font-size:15px;}
.project-card h2{font-size:17px; margin:0;}
.project-card p{font-size:12px; color:var(--muted); margin:8px 0 22px;}
.project-card .row{font-size:11px; color:var(--muted);}
.project-card .row b{color:var(--text); font-weight:600;}
/* Форма проекта */
.form-intro{font-size:13px; margin-top:0;}
.project-form input[type=file]{padding:7px;}
.project-form input[type=color]{width:64px; height:40px; padding:4px; cursor:pointer;}
/* Страница доступа */
.page-sub{margin:-8px 0 18px; font-size:13px;}
.card-heading{margin:0 0 6px; font-size:16px;}
/* Публичные ссылки */
.collapse-card--block{margin-bottom:16px;}
.share-form{display:flex; gap:8px; align-items:flex-end; flex-wrap:wrap; margin-bottom:16px;}
.share-label{flex:1 1 240px; margin-bottom:0;}
.share-list{list-style:none; padding:0; margin:0; display:flex; flex-direction:column; gap:12px;}
.share-title{font-weight:600; font-size:14px; margin-bottom:2px;}
.share-meta{font-size:12px; margin-bottom:6px;}
.share-row{display:flex; gap:8px; align-items:center; flex-wrap:wrap;}
.share-url{flex:1 1 320px; min-width:0;}
/* --- tasks: форма задачи и блок «создать из Bitrix24» (task_form.html) --- */
.card--form{max-width:720px;}
.card--after{margin-top:22px;}
@media (max-width:1100px){
  .projects{grid-template-columns:repeat(2,1fr);}
}
@media (max-width:760px){
  .projects{grid-template-columns:1fr;}
}

/* --- Сплеш-экран (RESEARCH Pattern 7, D3-01/02/03); -webkit- для Safari --- */
.splash{position:fixed; inset:0; z-index:2000; display:none; flex-direction:column; gap:16px;
  align-items:center; justify-content:center; background:var(--splash-bg);
  backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px); transition:opacity .3s;}
/* Без JS сплеш скрыт; html.js ставит анти-мигающий скрипт темы в base.html */
html.js .splash{display:flex;}
.splash.done{opacity:0; pointer-events:none;}
.splash-brand{display:flex; align-items:center; gap:13px;}
.splash-mark{width:46px; height:46px; display:block; flex:none;}
.splash-word{font-size:34px; font-weight:700; letter-spacing:-.02em; line-height:1; color:var(--text);}
.splash-word b{color:var(--accent); font-weight:700;}
.splash-bar{width:220px; height:6px; border-radius:3px; background:var(--side); overflow:hidden;}
.splash-fill{height:100%; width:0; border-radius:3px; background:var(--accent); transition:width .2s;}

/* --- Лента активности --- */
.thread{display:flex; flex-direction:column; gap:0; margin-top:8px;}
.thread .comment{background:transparent; border:0; border-radius:0; border-bottom:1px solid var(--line);
  padding:12px 0; font-size:13px; line-height:1.5;}
.thread .replies{margin:8px 0 0 20px; border-left:2px solid var(--line); padding-left:12px;
  display:flex; flex-direction:column; gap:8px;}
.thread .replies .comment{border-bottom:0; padding:8px 0;}
.thread .sys-row{color:var(--muted); font-size:12px; padding:8px 0; border-bottom:1px solid var(--line);}
.thread > :last-child{border-bottom:0;}
.thread .c-meta{color:var(--muted); font-size:12px; display:flex; gap:8px; align-items:center; flex-wrap:wrap;}
.thread .c-deleted{color:var(--muted); font-style:italic;}

/* --- Фирменная марка qadash: светлый растр (тёмный текст) по умолчанию, тёмный — под data-theme --- */
.brand-qadash{height:28px; width:auto;}
.brand-dark{display:none;}
html[data-theme="dark"] .brand-light{display:none;}
html[data-theme="dark"] .brand-dark{display:inline;}
.theme-toggle{font-size:16px;}

/* === testcases ===
   Раздел тест-кейсов задачи (testcases/_section.html на странице task-testcases):
   карточка тест-плана со сводкой и Markdown-телом, таблица кейсов с раскрытием,
   блок «Автотест» в раскрытом кейсе. Всё под #testcases (перебивает .card/.fld/.btn),
   кроме общего подблока ниже и #testplan. */
/* --- Общие для разделов тест-кейсов, автотестов (autotests_page.html) и ревью AI
   (aigen/_run_body.html).
   Шапки карточек — общий .card-head, мелкие подписи — .note (секция aigen ниже).
   .tc-note оставлен: в отличие от .note он обнуляет margin и несёт модификаторы отступов. --- */
.tc-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px;}
/* Строки-секции с акцентной полосой + цветные бейджи приоритета. Белый текст бейджа —
   как у .badge: фоны severity не меняются в тёмной теме, а --on-accent там тёмный. */
.sec-row td{background:var(--panel2);font-weight:700;color:var(--txt);font-size:12.5px;
  border-left:3px solid var(--accent);}
.prio{display:inline-block;padding:1px 8px;border-radius:6px;font-size:12px;font-weight:700;color:#fff;}
.prio-p0{background:var(--blocker);}
.prio-p1{background:var(--sev-critical);}
.prio-p2{background:var(--major);}
.prio-p3{background:var(--none);}
/* Раскрытая строка кейса; второй селектор не даёт .tableScroll tbody tr:hover перекрасить её. */
.tc-detail, .tableScroll tbody tr.tc-detail:hover{background:var(--panel2);}
/* --- Раздел тест-кейсов (#testcases) --- */
/* Шапка тест-плана: колонка-подпись выделена фоном + вертикальный разделитель. */
#testcases .tc-summary,#testplan .tc-summary{margin-top:12px;}
#testcases .tc-summary th,#testplan .tc-summary th{white-space:nowrap;width:190px;color:var(--txt);font-weight:600;vertical-align:top;
  background:var(--panel2);border-right:1px solid var(--line);}
#testcases .tc-summary tr:last-child th,#testcases .tc-summary tr:last-child td,#testplan .tc-summary tr:last-child th,#testplan .tc-summary tr:last-child td{border-bottom:0;}
/* Тело плана: узкая мера прозы (гибрид), выделенные шапки таблиц + зебра. */
#testcases .md-body,#testplan .md-body{line-height:1.6;}
#testcases .md-body > p,#testcases .md-body > ul,#testcases .md-body > ol,
#testcases .md-body > h1,#testcases .md-body > h2,#testcases .md-body > h3,
#testcases .md-body > h4,#testcases .md-body > blockquote,#testplan .md-body > p,#testplan .md-body > ul,#testplan .md-body > ol,#testplan .md-body > h1,#testplan .md-body > h2,#testplan .md-body > h3,#testplan .md-body > h4,#testplan .md-body > blockquote{max-width:72ch;}
#testcases .md-body table,#testplan .md-body table{border-collapse:collapse;width:100%;margin:10px 0 16px;border:1px solid var(--line);}
#testcases .md-body thead th,#testplan .md-body thead th{background:var(--panel2);font-weight:600;color:var(--txt);}
#testcases .md-body th,#testcases .md-body td,#testplan .md-body th,#testplan .md-body td{border:1px solid var(--line);padding:7px 12px;text-align:left;vertical-align:top;}
#testcases .md-body tbody tr:nth-child(even) td,#testplan .md-body tbody tr:nth-child(even) td{background:color-mix(in srgb,var(--panel2) 42%,var(--panel));}
#testcases .md-body h1,#testcases .md-body h2,#testcases .md-body h3,#testcases .md-body h4,#testplan .md-body h1,#testplan .md-body h2,#testplan .md-body h3,#testplan .md-body h4{margin:14px 0 6px;}
#testcases .md-body ul,#testcases .md-body ol,#testplan .md-body ul,#testplan .md-body ol{padding-left:22px;}
#testcases .md-body code,#testplan .md-body code{background:var(--panel2);padding:1px 4px;border-radius:4px;}
#testcases .md-body pre,#testplan .md-body pre{background:var(--panel2);padding:12px;border-radius:8px;overflow:auto;}
#testcases .md-body pre code,#testplan .md-body pre code{background:none;padding:0;}
/* Карточки: шапка «заголовок + группа кнопок», отступы блоков и панелей форм. */
/* Единственное правило по id вне #testcases: карточка плана внутри раздела. */
#testplan{margin-bottom:16px;}
#testcases .tc-btns,#testplan .tc-btns{display:flex;gap:8px;flex-wrap:wrap;}
#testcases .tc-row,#testplan .tc-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap;}
#testcases .tc-block,#testplan .tc-block{margin:12px 0 0;}
#testcases .tc-panel{margin-bottom:16px;}
#testcases .tc-mt6{margin-top:6px;}
#testcases .tc-mt8,#testplan .tc-mt8{margin-top:8px;}
#testcases .tc-hr,#testplan .tc-hr{border:none;border-top:1px solid var(--line);margin:16px 0;}
#testcases .tc-prewrap,#testplan .tc-prewrap{white-space:pre-line;}
#testcases .tc-warn,#testplan .tc-warn{color:var(--orange);}
#testcases .tc-accent{color:var(--accent);}
#testcases .tc-link-sm{font-size:12px;}
/* Компактная кнопка «×» (удалить файл / креды доп. стенда). */
#testcases .tc-x,#testplan .tc-x{padding:0 8px;}
/* Мелкие подписи: 12px muted; отступы абзацев — модификаторами. Порядок важен:
   <p class="tc-note tc-p"> получает 4px 0 за счёт более позднего .tc-p. */
#testcases .tc-note,#testplan .tc-note{color:var(--muted);font-size:12px;margin:0;}
#testcases .tc-note--t4,#testplan .tc-note--t4{margin-top:4px;}
#testcases .tc-note--t6{margin-top:6px;}
#testcases .tc-note--t8{margin-top:8px;}
#testcases .tc-note--y8{margin:8px 0;}
#testcases .tc-note--b6{margin-bottom:6px;}
#testcases .tc-note--b8{margin-bottom:8px;}
#testcases .tc-p{margin:4px 0;}
/* Файлы плана. */
#testcases .tc-files-title{font-weight:600;font-size:13px;margin-bottom:6px;}
#testcases .tc-list{list-style:none;padding:0;margin:0 0 8px;}
#testcases .tc-file{padding:3px 0;}
/* История плана и кейса: свёрнутые снимки ревизий. */
#testcases .tc-sum,#testplan .tc-sum{cursor:pointer;font-size:12px;}
#testcases .tc-sum--strong,#testplan .tc-sum--strong{font-weight:600;font-size:13px;}
#testcases .tc-hist-list,#testplan .tc-hist-list{list-style:none;padding:0;margin:6px 0 0;}
#testcases .tc-hist-item,#testplan .tc-hist-item{margin:4px 0;}
/* Источники информации задачи (карточка плана). */
#testcases .tc-src-list,#testplan .tc-src-list{list-style:none;padding:0;margin:8px 0 0;display:grid;gap:6px;}
#testcases .tc-src-item,#testplan .tc-src-item{display:flex;gap:8px;align-items:baseline;font-size:13px;}
#testcases .tc-src-icon,#testplan .tc-src-icon{flex:none;}
#testcases .tc-src-body,#testplan .tc-src-body{display:flex;flex-wrap:wrap;gap:4px 8px;align-items:baseline;min-width:0;}
#testcases .tc-src-name,#testcases .tc-note,#testcases .tc-hist-item summary,#testplan .tc-src-name,#testplan .tc-note,#testplan .tc-hist-item summary{overflow-wrap:anywhere;}
#testcases .tc-src-runs,#testplan .tc-src-runs{font-size:12px;color:var(--muted);}
#testcases .tc-rev,#testplan .tc-rev{font-size:12.5px;padding:6px 0 6px 14px;}
#testcases .tc-rev p,#testplan .tc-rev p{margin:2px 0;}
#testcases .tc-pre,#testplan .tc-pre{white-space:pre-wrap;background:var(--panel2);padding:8px 10px;border-radius:6px;margin-top:6px;}
/* Таблица кейсов: ширины колонок, кликабельная строка, строка действий. */
#testcases .tc-col-num{width:48px;}
#testcases .tc-col-code{width:130px;}
#testcases .tc-col-prio{width:110px;}
#testcases .tc-col-status{width:160px;}
#testcases .tc-case-row{cursor:pointer;}
#testcases .tc-actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-top:8px;}
/* Блок «Автотест» в раскрытом кейсе. */
#testcases .tc-at{margin-top:10px;padding:10px 12px;}
#testcases .tc-at-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
#testcases .tc-at-title{font-size:13px;}
#testcases .tc-code{width:100%;font-family:ui-monospace,monospace;font-size:12px;}
/* Полоса прогресса генерации автотеста (GENERATING): определённая — по этапу
   (Autotest.progress, обновляет JS-поллинг testcases_page.html без перезагрузки);
   неопределённая (--indeterminate) — нет данных от агента, анимация бежит слева направо. */
#testcases .at-gen-progress{margin-top:8px;}
#testcases .at-gen-bar{height:6px;background:var(--line);border-radius:6px;overflow:hidden;}
#testcases .at-gen-bar > span{display:block;height:100%;background:var(--accent);transition:width .3s ease;}
#testcases .at-gen-bar--indeterminate{position:relative;}
#testcases .at-gen-bar--indeterminate > span{width:35% !important;position:absolute;
  animation:at-gen-sweep 1.4s ease-in-out infinite;}
@keyframes at-gen-sweep{
  0%{left:-35%;}
  100%{left:100%;}
}
#testcases .at-gen-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:4px;font-size:11.5px;color:var(--muted);}
#testcases .at-gen-label{font-weight:600;color:var(--txt);}
@media (prefers-reduced-motion: reduce){
  #testcases .at-gen-bar--indeterminate > span{animation:none;left:0;width:100% !important;}
}
/* Название кейса в строке — ссылка на шторку карточки (макет .titlelink). */
#testcases .tc-title{color:var(--text); font-weight:550; text-decoration:none; overflow-wrap:anywhere;}
#testcases .tc-title:hover{color:var(--accent);}

/* === testcases/autotests_page ===
   Страница автотестов задачи (task-autotests): шапка карточки с кнопкой набора из репо,
   панель «Запустить отмеченные / все готовые», таблица кейсов со статусами прогонов.
   Всё под #autotests; строки-секции, бейджи приоритета и чипы статусов — общие
   (.sec-row, .prio-*, .chip--*), чекбокс-колонка — .th-check/.td-check. */
#autotests .at-head-actions{display:flex;align-items:center;gap:12px;flex-wrap:wrap;}
/* Заглушка кнопки без CI-триггера. */
#autotests .at-btn-off{opacity:.55;cursor:not-allowed;}
#autotests .run-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:12px;}
/* Ширины колонок таблицы. */
#autotests .at-col-num{width:48px;}
#autotests .at-col-code{width:150px;}
#autotests .at-col-prio{width:90px;}
#autotests .at-col-status{width:200px;}
#autotests .at-col-time{width:170px;}
/* label растянут на всю ячейку чекбокса; выровнен по ПЕРВОЙ строке текста (padding-top
   как у .tableScroll td), а не по центру — иначе на многострочных строках (заметка/бейджи)
   чекбокс заметно съезжает вниз относительно «№»/«Код» (баг с прода, воспроизводится на
   1280 px). align-items:flex-start перебивает базовый center из .td-check не требуется —
   .td-check label уже flex-start; здесь просто не переопределяем его на center. */
#autotests .at-check{position:absolute;inset:0;display:flex;align-items:flex-start;padding:12px 12px 0;cursor:pointer;}
#autotests .at-time{font-size:12px;color:var(--muted);white-space:nowrap;}
#autotests .skip-note{margin-top:3px;font-size:12px;color:var(--muted);line-height:1.35;}
#autotests a.def-link{color:var(--critical);font-weight:600;text-decoration:none;}
#autotests a.def-link:hover{text-decoration:underline;}
/* Плашка статуса + «▶» — одна строка (gap вместо margin-left на кнопке); предупреждения —
   компактным столбиком под ними, не раздувая высоту строки (WR: разъезжались на 1280 px).
   flex-wrap:nowrap — «▶» не должна переноситься под плашку статуса на 1280 px (колонка
   расширена до 200px под самую длинную плашку «🔎 Требует разбора»). */
#autotests .at-status-row{display:inline-flex; align-items:center; gap:6px; flex-wrap:nowrap;}
#autotests .at-status-badges{display:flex; flex-direction:column; gap:3px; margin-top:4px;}
#autotests .at-badge{font-size:11.5px;color:var(--major);}
#autotests .at-badge--bad{color:var(--critical);}
#autotests .run-one{padding:2px 8px;font-size:12px;}

/* Блок прогресса последнего прогона задачи (над таблицей «Автотесты»): полоса по этапам
   queued→preparing→running→reporting→done|failed, обновляется JS-поллингом (autotests_page.html
   extra_js) или серверным рендером при перезагрузке. */
#autotests .at-run{margin-bottom:14px;padding:12px 14px;border:1px solid var(--line);
  border-radius:var(--radius);background:var(--surface);}
#autotests .at-run--bad{border-color:var(--critical);}
#autotests .at-run-bar{height:6px;background:var(--line);border-radius:6px;overflow:hidden;}
#autotests .at-run-bar > span{display:block;height:100%;background:var(--accent);transition:width .3s ease;}
#autotests .at-run--bad .at-run-bar > span{background:var(--critical);}
#autotests .at-run-meta{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:8px;font-size:12px;}
#autotests .at-run-label{font-weight:600;color:var(--txt);}
#autotests .at-run-time{color:var(--muted);}
#autotests .at-run-ci{color:var(--accent);}
#autotests .at-run-summary{margin-top:6px;font-size:12px;color:var(--muted);}
#autotests .at-run-summary--bad{color:var(--critical);}
#autotests .at-run-active-note{margin-left:2px;}
#autotests .at-run-queued-note{margin-left:2px;}

/* === aigen/run_review: ревью AI-прогона ===
   Страница ревью AI-прогона (aigen-run-review: run_review.html → _run_body.html, для
   прогона-обновления — _update_review.html/_update_group.html; формы черновика
   _draft_keep_form.html/_draft_edit_form.html). Служебный блок прогона (статус, вход,
   скриншоты, ошибка/пауза доступа/прогресс), черновик плана и кейсов, дифф ревью изменений.
   Всё под #aigen-review; строки-секции, бейджи приоритета, раскрытая строка и сетка формы —
   общие (.sec-row, .prio-*, .tc-detail, .tc-grid), чипы — .chip--ok/--warn, миниатюры — .thumb. */
/* --- Общие шапка карточки, мелкая подпись и строка «чекбокс + подпись» (тест-кейсы,
   автотесты, ревью AI). .fld label.check-row — перебивает .fld label{display:block}. --- */
.card-head{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;}
.card-head h3{margin:0;font-size:15px;}
.card-head--gap{margin-bottom:12px;}
.note{font-size:12px;color:var(--muted);}
.check-row, .fld label.check-row{display:flex;gap:8px;align-items:center;margin:8px 0;}
.check-row--item, .fld label.check-row--item{margin:2px 0;font-weight:400;}
/* --- Ревью AI-прогона (#aigen-review) --- */
/* Карточки — прямые дети секции (включая группы _update_group и свёрнутую «Без изменений»). */
#aigen-review > .card{margin-bottom:16px;}
#aigen-review > .card:last-child{margin-bottom:0;}
#aigen-review .card > h3{margin:0 0 12px;font-size:15px;}
#aigen-review .card > h3.rv-h3--tight{margin-bottom:10px;}
/* Служебная таблица прогона и шапки плана: колонка-подпись выделена фоном. */
#aigen-review .card-head + .run-meta{margin-top:12px;}
#aigen-review .run-meta th{white-space:nowrap;width:190px;color:var(--txt);font-weight:600;vertical-align:top;
  background:var(--panel2);border-right:1px solid var(--line);}
#aigen-review .run-meta tr:last-child th,#aigen-review .run-meta tr:last-child td{border-bottom:0;}
#aigen-review .plan-body{line-height:1.6;white-space:pre-wrap;max-width:82ch;
  background:var(--panel2);padding:12px;border-radius:8px;margin-top:12px;}
/* Бейджи статуса прогона (st-{{ run.status }}). */
#aigen-review .st{display:inline-block;padding:2px 10px;border-radius:6px;font-size:12px;font-weight:700;}
#aigen-review .st-queued{background:var(--panel2);color:var(--muted);border:1px solid var(--line);}
#aigen-review .st-running{background:var(--panel2);color:var(--accent);border:1px solid var(--accent);}
#aigen-review .st-succeeded{background:var(--panel2);color:var(--ok);border:1px solid var(--ok);}
#aigen-review .st-failed{background:var(--panel2);color:var(--critical);border:1px solid var(--critical);}
#aigen-review .st-needs_access{background:var(--panel2);color:var(--major);border:1px solid var(--major);}
/* Пауза «Нужен доступ». */
#aigen-review .run-access{border:1px solid var(--major);border-radius:8px;padding:12px;margin-top:12px;
  background:color-mix(in srgb,var(--major) 8%,var(--panel));}
#aigen-review .run-access ul{margin:8px 0 0;padding-left:18px;}
#aigen-review .run-access li{margin:8px 0;}
#aigen-review .rv-acc-grid{margin-top:6px;max-width:520px;}
/* Индикатор генерации (неопределённый прогресс — истинного % нет; показываем этап). */
#aigen-review .gen-progress{margin-top:14px;}
#aigen-review .gen-progress-label{color:var(--accent);font-size:13px;font-weight:600;
  display:flex;align-items:center;gap:8px;}
#aigen-review .gen-spin, .ai-layout .gen-spin{display:inline-block;width:13px;height:13px;flex:none;
  border:2px solid var(--line);border-top-color:var(--accent);border-radius:50%;
  animation:genspin .8s linear infinite;}
@keyframes genspin{to{transform:rotate(360deg);}}
@media (prefers-reduced-motion: reduce){ #aigen-review .gen-spin, .ai-layout .gen-spin{animation:none;} }
#aigen-review .gen-bar{position:relative;height:6px;max-width:320px;margin-top:8px;border-radius:6px;
  background:var(--panel2);overflow:hidden;}
#aigen-review .gen-bar span{position:absolute;top:0;left:-40%;width:40%;height:100%;border-radius:6px;
  background:var(--accent);animation:genslide 1.1s ease-in-out infinite;}
@keyframes genslide{0%{left:-40%;}100%{left:100%;}}
@media (prefers-reduced-motion: reduce){
  #aigen-review .gen-bar span{animation:none;left:0;width:100%;opacity:.5;}
}
#aigen-review .run-error, .ai-layout .run-error{background:color-mix(in srgb,var(--critical) 12%,var(--panel));
  border:1px solid var(--critical);border-radius:8px;padding:12px;margin-top:12px;color:var(--txt);white-space:pre-wrap;}
/* Блоки служебной карточки: «Что анализировали», «Отправленный текст ТЗ», скриншоты. */
#aigen-review .rv-block{margin-top:14px;}
#aigen-review .rv-block-title{font-weight:600;color:var(--txt);margin-bottom:8px;font-size:13px;}
#aigen-review .rv-hint{color:var(--muted);font-size:13px;}
#aigen-review .rv-shots{display:flex;gap:8px;flex-wrap:wrap;}
/* Свёрнутые блоки: подпись-переключатель. */
#aigen-review .rv-sum{cursor:pointer;font-size:13px;}
#aigen-review .rv-sum--note{color:var(--muted);font-size:12px;}
#aigen-review .rv-sum--title{font-weight:600;font-size:15px;}
/* Раскладки кнопок и чипов. */
#aigen-review .rv-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap;}
#aigen-review .rv-btns{display:flex;gap:8px;flex-wrap:wrap;}
#aigen-review .rv-actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-top:8px;}
#aigen-review .rv-warns{display:flex;gap:6px;flex-wrap:wrap;margin:4px 0 8px;}
#aigen-review .rv-warn{color:var(--major);}
/* Отступы абзацев и блоков. */
#aigen-review .rv-mt8{margin:8px 0 0;}
#aigen-review .rv-my8{margin:8px 0;}
#aigen-review .rv-block .note{margin:6px 0 0;}
#aigen-review .rv-block details > .plan-body{margin-top:8px;}
#aigen-review .card > details{margin-top:10px;}
/* Пустые состояния («Нет.», «План без изменений»); в свёрнутой «Без изменений» и после
   шапки плана — отступ сверху, как у таблицы. */
#aigen-review .rv-empty{margin:0;}
#aigen-review details.card > .rv-empty, #aigen-review details.card > .tableScroll,
#aigen-review .run-meta + .rv-empty{margin-top:12px;}
/* Таблицы черновиков: ширины колонок, кликабельная строка (первая строка Alpine-tbody),
   приглушённые неотобранные — data-keep="0", без JS-класса на <tr>. Сервер всегда ставит
   data-keep на саму строку (I-3: читателю без права правки, где keep-form не рендерится,
   иначе приглушение не работало бы вовсе). Пока строку не переключили через htmx, её
   data-keep актуален; после переключения меняется только keep-form внутри (outerHTML),
   и для строки с формой источник истины — data-keep формы (:has), а не устаревший
   атрибут на <tr>. */
#aigen-review .rv-col-keep{width:56px;}
#aigen-review .rv-col-code{width:130px;}
#aigen-review .rv-col-prio{width:110px;}
#aigen-review tbody[x-data] > tr:first-child{cursor:pointer;}
#aigen-review tr[data-keep="0"]:not(:has(.keep-form)) td,
#aigen-review tr:has(.keep-form[data-keep="0"]) td{opacity:.55;}
#aigen-review .tc-detail p{margin:4px 0;}
/* Формы черновика: компактная кнопка ☑/☐ keep-тоггла. */
#aigen-review .keep-form .btn{padding:2px 8px;}
/* Ревью изменений: построчный дифф плана и пословный дифф полей кейса. */
#aigen-review .plan-diff{white-space:pre-wrap;background:var(--panel2);padding:10px 12px;border-radius:8px;
  font-family:ui-monospace,monospace;font-size:12.5px;line-height:1.5;margin:0;overflow:auto;}
#aigen-review .d-add{background:var(--green-bg);}
#aigen-review .d-del{background:var(--red-bg);}
#aigen-review .d-ctx{color:var(--muted);}
#aigen-review .d-hdr{color:var(--muted);font-weight:700;}
#aigen-review .field-diff{margin-top:6px;}
#aigen-review .field-diff del{background:var(--red-bg);text-decoration:line-through;}
#aigen-review .field-diff ins{background:var(--green-bg);text-decoration:none;}
#aigen-review .field-diff th{width:130px;white-space:nowrap;vertical-align:top;}
#aigen-review .field-diff td{vertical-align:top;}
#aigen-review .md-summary ul,#aigen-review .md-summary ol{padding-left:22px;margin:6px 0;}

/* === accounts/manage + aigen/bitrix_settings: управление доступом и интеграции ===
   Страницы member_list/member_form/api_key_list (accounts/manage) и bitrix_settings (aigen).
   Скоуп — класс .manage: класс manage навешивается шаблоном страницы на .content-inner
   через блок content_class; у страниц нет корневого id.
   Таблицы, карточки, .fld-поля, чипы и .inline-формы — глобальные. */
/* --- Предупреждение/одноразовый показ; макет .notice (глобальный компонент). --- */
.notice{border-radius:6px;background:var(--orange-bg);color:var(--orange);padding:10px 12px;font-size:12px;
  display:flex;gap:8px;align-items:center;margin:0 0 16px;}
/* Блочный вариант: абзац и код друг под другом (новый API-ключ). */
.notice--block{flex-direction:column;align-items:stretch;}
.notice p{margin:0 0 6px;}
.notice--block p{margin:0;}
.notice strong{font-weight:700;}
.notice code{display:block;flex:1 1 auto;padding:10px;background:var(--surface);color:var(--text);
  border:1px solid var(--line);border-radius:6px;word-break:break-all;user-select:all;font-size:12px;}
/* --- Заголовки, карточки, отступы. --- */
.manage .toolbar h2{margin:0;font-size:18px;}
.manage .card > h2{margin:0 0 12px;font-size:16px;}
.manage .card > h3{margin:0 0 8px;font-size:15px;}
.manage > .card + .card,.manage > p + .card{margin-top:16px;}
.manage > .card + p{margin-top:16px;}
.manage .card .tableScroll{margin-bottom:16px;}
.manage .card-help{color:var(--muted);font-size:13px;margin:0 0 12px;}
.manage .card--w520{max-width:520px;}
.manage .card--w640{max-width:640px;}
.manage .accent{color:var(--accent);}
/* Поле метки ключа в тулбаре (вне .card/.fld): фон/рамка — из глобального :where(input…),
   здесь только отличия размеров и фокуса. */
.manage .key-label{min-width:280px;min-height:36px;padding:7px 11px;font-size:13px;border-radius:9px;}
.manage .key-label:focus{outline:none;border-color:var(--accent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent);}
@media (max-width:760px){ .manage .key-label{min-width:0;flex:1 1 200px;min-height:40px;} }
/* --- Bitrix24 (.card--w640 только там): абзацы карточек; «статус + пояснение» — 6px
   (14px и -8px схлопываются), последний абзац без нижнего отступа. --- */
.manage .card--w640 > p{margin:0 0 14px;}
.manage .card--w640 > p + p{margin-top:-8px;}
.manage .card--w640 > p:last-child{margin-bottom:0;}

/* ===== Шелл: фиксированный сайдбар + .main-shell (топбар + прокручиваемый main) =====
   Только для аутентифицированных (не токен-гость). Аноним/гость → .app.plain.
   main.content — собственная область прокрутки на всю высоту под топбаром: дашборд
   (.content-inner--grid) и форма дефекта (.df-page) растягиваются на height:100%. */
html.sidebar-collapsed{--sidebar-w:64px;}
.sidebar{position:fixed; top:0; bottom:0; left:0; z-index:30; width:var(--sidebar-w);
  background:var(--side); border-right:1px solid var(--line);
  display:flex; flex-direction:column; min-height:0; overflow:hidden;
  transition:width .16s ease;}
.main-shell{margin-left:var(--sidebar-w); height:100vh; height:100dvh; display:flex; flex-direction:column; min-width:0;
  transition:margin-left .16s ease;}
.sidebar .brand{display:flex; align-items:center; gap:9px; flex:none; height:var(--top-h); padding:0 18px;
  border-bottom:1px solid var(--line); overflow:hidden;}
.sidebar .brand .brand-qadash{height:26px; flex:none;}
.topbar{flex:none; height:var(--top-h); padding:0 28px; display:flex; align-items:center; gap:12px;
  background:var(--surface); border-bottom:1px solid var(--line);}
.topbar .spacer{flex:1 1 auto;}
.top-actions{display:flex; align-items:center; gap:8px; flex:none;}
.shell-hamburger{display:none;}

/* Меню пользователя: нативный <details>, панель справа под триггером. */
.usermenu{position:relative;}
.usermenu > summary{list-style:none;}
.usermenu > summary::-webkit-details-marker{display:none;}
.usermenu-trigger{gap:8px; padding:4px 10px 4px 4px; border-radius:20px;}
.usermenu-name{max-width:160px; overflow:hidden; text-overflow:ellipsis;}
.avatar{width:27px; height:27px; display:inline-flex; align-items:center; justify-content:center; flex:none;
  border-radius:50%; font-size:11px; font-weight:600; background:var(--accent-soft); color:var(--accent);}
.usermenu-panel{position:absolute; right:0; top:calc(100% + 6px); z-index:50; min-width:210px; padding:6px;
  display:flex; flex-direction:column; gap:1px;
  background:var(--surface); border:1px solid var(--line); border-radius:11px; box-shadow:var(--shadow);}
.usermenu-panel form{margin:0;}
/* Пункты меню несут .btn (инвентарь UI считает их действиями) — вид кнопки сброшен. */
.usermenu-panel .usermenu-item{display:block; width:100%; min-height:0; padding:8px 10px; border-radius:8px; text-align:left;
  background:none; border:0; color:var(--text); font-size:13px; font-weight:500; line-height:1.4; white-space:nowrap;
  cursor:pointer; text-decoration:none;}
.usermenu-panel .usermenu-item:hover{background:var(--accent-soft); color:var(--accent);}

/* Сайдбар-дерево */
.proj-head{display:flex; align-items:center; gap:10px; padding:11px 14px; border-bottom:1px solid var(--line); min-height:54px; flex:none;}
.proj-logo{height:28px; width:auto; max-width:118px; border-radius:4px; object-fit:contain; flex:none;}
.proj-logo-ph{height:28px; min-width:38px; padding:0 8px; border-radius:4px; background:var(--text); color:var(--surface);
  display:inline-flex; align-items:center; justify-content:center; font-weight:800; font-size:12px; letter-spacing:.4px; flex:none;}
.proj-head .who{min-width:0;}
.proj-head .who b{display:block; font-size:14px; line-height:1.2; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.proj-head .who span{display:block; font-size:11px; color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.nav{flex:1 1 auto; overflow-y:auto; padding:10px 8px; min-height:0;}
.nav-empty{color:var(--muted); font-size:12.5px; padding:6px 10px; margin:0;}
.back-link{display:flex; align-items:center; gap:8px; width:100%; padding:7px 9px; border-radius:7px;
  border:0; background:transparent; color:var(--muted); font-size:12.5px; text-align:left; cursor:pointer; text-decoration:none;}
.back-link:hover{background:var(--line); color:var(--text);}
.group-label{padding:14px 10px 5px; font-size:10px; font-weight:650; letter-spacing:1px; text-transform:uppercase; color:var(--muted);}
.nav .node{display:block;}
.nav-item{display:flex; align-items:center; gap:9px; width:100%; padding:7px 9px; border-radius:7px;
  border:0; background:transparent; color:var(--text); text-align:left; position:relative; line-height:1.3;
  cursor:pointer; text-decoration:none; font-size:13px; font-weight:500;}
.nav-item:hover{background:var(--line);}
summary.nav-item{list-style:none;}
summary.nav-item::-webkit-details-marker{display:none;}
.nav-item .nav-label{flex:1 1 auto; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.nav-item .chev{color:var(--muted); transition:transform .15s ease;}
details.node[open] > summary .chev{transform:rotate(90deg);}
.nav-item .icon{color:var(--muted); flex:none;}
.nav-item.active, .nav-item.active .icon{color:var(--accent);}
.nav-item.active{background:var(--accent-soft); font-weight:650;}
.nav-item.active::before{content:""; position:absolute; left:-8px; top:6px; bottom:6px; width:3px;
  border-radius:0 3px 3px 0; background:var(--accent);}
.nav-count{flex:none; margin-left:auto; font-size:10px; color:var(--muted); background:var(--surface); border:1px solid var(--line);
  border-radius:20px; padding:0 7px; line-height:17px;}
.children{margin:2px 0 2px 18px; padding-left:10px; border-left:1px solid var(--line); display:flex; flex-direction:column; gap:1px;}
.children .nav-item{padding:6px 9px; font-size:12.5px;}
.side-foot{flex:none; border-top:1px solid var(--line); padding:8px;}
/* Действие «Создать задачу»/«Новый проект» в сайдбаре — акцентная строка навигации. */
.nav-add, .nav-add .icon{color:var(--accent);}
.nav-add{font-weight:600;}
/* Живой индикатор идущей AI-генерации (наполняет поллер base.html). Скрыт, пока пусто. */
.aigen-active-group{padding:6px 8px 4px;}
.aigen-active-item{color:var(--accent);}
.aigen-active-spin{flex:none; width:14px; height:14px; display:inline-block;
  border:2px solid var(--line); border-top-color:var(--accent); border-radius:50%;
  animation:aigen-spin .8s linear infinite;}
@keyframes aigen-spin{to{transform:rotate(360deg);}}
@media (prefers-reduced-motion:reduce){ .aigen-active-spin{animation:none;} }
.aigen-active-wait{flex:none; width:14px; height:14px; display:inline-flex; align-items:center;
  justify-content:center; border-radius:50%; background:var(--major); color:#fff;
  font-size:11px; font-weight:700;}

/* Хлебные крошки — в топбаре. */
.crumbs{display:flex; align-items:center; gap:8px; min-width:0; overflow:hidden; font-size:12px; color:var(--muted); white-space:nowrap;}
.crumbs a{color:var(--muted); overflow:hidden; text-overflow:ellipsis;}
.crumbs a:hover{color:var(--accent);}
.crumbs b{font-weight:500; color:var(--text); overflow:hidden; text-overflow:ellipsis;}
.crumbs .sep{color:var(--line); flex:none;}

/* Контент */
.content{flex:1 1 auto; min-height:0; overflow-y:auto; min-width:0; padding:24px 32px 40px;}
/* Гибрид-контейнер: единая читаемая ширина контента на всех страницах каркаса. */
.content-inner{max-width:1180px; margin:0 auto;}
/* Дашборд-грид: на десктопе таблица дефектов — самостоятельная прокручиваемая область
   на всю высоту под топбаром (липкая шапка таблицы, второго вертикального скролла нет).
   Правило ниже — явная страховка, а не источник поведения: внутренний скролл и липкий
   thead уже дают flex-раскладка .content-inner--grid и автоматический min-height:0.
   .tableScroll — внук через div.contents, поэтому селектор потомка, не ">". */
.content-inner--grid{display:flex; flex-direction:column; height:100%; max-width:none;}
@media (min-width:901px){
  .content-inner--grid .tableScroll{flex:1 1 auto; min-height:0;}
}
.content-inner > h1{font-size:22px; margin:0 0 16px; letter-spacing:-.01em; font-weight:600;}
/* Шапка страницы с действием справа (напр. «Создать задачу» на дашборде). */
.page-head{display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; margin:0 0 16px;}
.page-head > h1{margin:0; font-size:22px; letter-spacing:-.01em; font-weight:600;}
/* Группа кнопок шапки: держится вместе у правого края, на узком экране переносится. */
.page-actions{display:flex; gap:8px; flex-wrap:wrap; margin-left:auto; align-items:center;}

/* --- Диалоги и шторка (макет 03.1; наполняет drawer.js в Этапе 1) --- */
dialog{color:var(--text); background:var(--surface); padding:0; border:1px solid var(--line); box-shadow:var(--shadow);
  border-radius:13px; width:min(580px,calc(100vw - 32px)); max-height:90vh;}
dialog::backdrop{background:#17142560; backdrop-filter:blur(3px);}
.dialoghead{display:flex; justify-content:space-between; align-items:center; gap:16px; padding:20px 24px; border-bottom:1px solid var(--line);}
.dialoghead h2{font-size:17px; margin:0;}
.dialogbody{padding:24px; overflow-wrap:anywhere;}
.dh-titles{min-width:0; flex:1;}
.dh-sub{font-size:12px; color:var(--muted); margin-top:3px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.dh-actions{display:flex; align-items:center; gap:8px; flex:none;}
.dh-actions .btn{padding:8px 14px; min-height:36px; font-size:13px;}
.drawer{margin:0 0 0 auto; top:0; height:100vh; max-height:100vh; height:100dvh; max-height:100dvh; width:min(820px,100vw); max-width:100vw;
  border-radius:12px 0 0 12px; border-right:0;}
.drawer .dialoghead{position:sticky; top:0; background:var(--surface); z-index:1;}
.drawer .dialogbody{padding:24px 30px;}
.drawer--wide{width:min(1080px,100vw);}
.drawer--wide .dialogbody{background:var(--bg); padding:20px 24px 24px; overflow-wrap:normal;}

/* Свёрнутый сайдбар — иконочная полоса (только десктоп: на ≤900px off-canvas всегда с подписями). */
@media (min-width:901px){
  html.sidebar-collapsed .nav-label,
  html.sidebar-collapsed .nav-count,
  html.sidebar-collapsed .chev,
  html.sidebar-collapsed .group-label,
  html.sidebar-collapsed .proj-head .who{display:none;}
  html.sidebar-collapsed .nav-item,
  html.sidebar-collapsed .back-link,
  html.sidebar-collapsed .proj-head{justify-content:center; padding-left:0; padding-right:0;}
  html.sidebar-collapsed .children{margin-left:0; padding-left:0; border-left:0;}
  html.sidebar-collapsed .sidebar .brand{padding:0 10px;}
}

/* plain-режим (аноним/логин/токен-гость): без сайдбара, контент по центру. */
.app.plain .main-shell{margin-left:0; height:auto; display:block;}
.app.plain .content{max-width:1180px; margin:0 auto; padding:16px; overflow:visible;}
.app.plain .content-inner{max-width:none; margin:0;}
.app.plain .content-inner > h1{font-size:18px;}

@media (max-width:1100px){
  html:not(.sidebar-collapsed) .app{--sidebar-w:220px;}
  .topbar{padding:0 20px;}
  .content{padding:20px 24px 36px;}
  .dialoghead{padding:17px 20px;}
  .dialogbody{padding:20px;}
  .drawer--wide{width:100vw; border-radius:0;}
}
@media (max-width:900px){
  .app:not(.plain) .sidebar{width:250px; transform:translateX(-100%); visibility:hidden;
    transition:transform .18s ease, visibility .18s;}
  .app.mobile-open .sidebar{transform:none; visibility:visible;}
  /* Настоящий бэкдроп (не box-shadow): перехватывает тап, чтобы он не прошёл в контент
     (строки tr[data-href], ссылки). Клик приходит на #app → base.html закрывает сайдбар. */
  .app.mobile-open::before{content:""; position:fixed; inset:0; z-index:29; background:rgba(0,0,0,.45);}
  .app:not(.plain) .main-shell{margin-left:0;}
  .shell-hamburger{display:inline-flex;}
  .side-foot{display:none;}
  .topbar{padding:0 12px; gap:8px;}
  .usermenu-name{display:none;}
}
@media (max-width:760px){
  .topbar{height:56px;}
  /* Крошки: на узком экране — только текущий пункт (он всегда <b>). */
  .crumbs > :not(b){display:none;}
  .content{padding:18px 16px 32px;}
  .panelhead{padding:16px; flex-wrap:wrap;}
  .panelbody{padding:0 16px 16px;}
  .panel td, .panel th, .table-wrap td, .table-wrap th{padding:12px 14px;}
  .top-actions{gap:4px;}
  .btn{min-height:40px;}
  .dh-actions .btn{min-height:40px;} /* перебивает десктопные 36px шапки шторки */
  .drawer--wide .dialogbody{padding:14px;}
  .dialoghead{flex-wrap:wrap;}
  .dh-titles{flex-basis:100%;}
  /* Отдельная строка под заголовком: кнопки переносятся и прижаты вправо, «×» не уезжает за край. */
  .dh-actions{flex:1 1 100%; flex-wrap:wrap; justify-content:flex-end;}
}
@media (prefers-reduced-motion:reduce){.sidebar,.main-shell,.nav-item .chev,.collapse-card > summary .chev{transition:none !important;}}

/* --- Кнопка «Аннотировать» на превью/вложении (MEDIA-04) --- */
.annot-btn{
  display:inline-block; padding:4px 10px; border-radius:8px;
  background:var(--panel2); color:var(--txt); border:1px solid var(--line);
  font-size:12px; font-weight:600; cursor:pointer; line-height:1.4;
}
.annot-btn:hover{border-color:var(--accent); color:var(--txt);}
/* На вложении галереи — компактная кнопка поверх превью (верхний левый угол; «×» — в правом),
   ряд ссылок .att-public идёт ниже без наложений. #uploader-annotate не затрагивается. */
.att .annot-btn{
  position:absolute; top:8px; left:8px; z-index:1; padding:2px 8px;
  font-size:11px;
}

/* --- Публичная ссылка на скрин (без логина, capability-токен) --- */
.att-public{display:flex; gap:6px; margin-top:6px; align-items:center;}
.att-public a, .att-copylink{
  padding:2px 8px; border-radius:8px; font-size:11px; font-weight:600;
  border:1px solid var(--line); background:var(--panel2); color:var(--txt);
  cursor:pointer; text-decoration:none; line-height:1.5;
}
.att-public a:hover, .att-copylink:hover{border-color:var(--accent);}

/* --- Модальный редактор аннотаций (MEDIA-04, D2-09/D2-10; Fabric.js 7) --- */
.annot-modal{
  position:fixed; inset:0; z-index:1100;
  display:flex; align-items:center; justify-content:center; padding:24px;
  background:rgba(0,0,0,.6);
}
.annot-modal[hidden]{display:none;}
.annot-dialog{
  display:flex; flex-direction:column; gap:12px;
  max-width:min(96vw, 1100px); max-height:92vh; overflow:auto;
  background:var(--panel); border:1px solid var(--line);
  border-radius:12px; padding:24px;
}
/* Тулбар инструментов — панельный фон, кнопки 44px (touch-target WCAG 2.5.5). */
.annot-toolbar{
  display:flex; flex-wrap:wrap; gap:8px; padding:8px;
  background:var(--panel2); border:1px solid var(--line); border-radius:8px;
}
.annot-tool{
  display:flex; align-items:center; justify-content:center;
  min-width:44px; min-height:44px; padding:0;
  border-radius:8px; border:1px solid var(--line);
  background:var(--panel); color:var(--txt);
  font-size:18px; line-height:1; cursor:pointer;
}
.annot-tool:hover{background:var(--panel2); border-color:var(--accent);}
/* Активный инструмент — акцент (UI-SPEC: выбранный инструмент = --accent). */
.annot-tool.is-active{
  background:var(--accent2); border-color:var(--accent); color:var(--on-accent);
}
.annot-tool:focus-visible{outline:2px solid var(--accent); outline-offset:1px;}
/* Предупреждение о необратимости замазки (D2-10). */
.annot-cover-note{
  margin:0; font-size:12px; color:var(--muted);
}
.annot-canvas-wrap{
  display:flex; align-items:center; justify-content:center;
  padding:24px; background:var(--panel2);
  border:1px solid var(--line); border-radius:8px; overflow:auto;
}
.annot-canvas-wrap canvas{max-width:100%;}
.annot-err{color:var(--critical); font-size:13px; margin:0; min-height:18px;}
.annot-actions{
  display:flex; justify-content:flex-end; gap:8px;
}
.annot-actions button:disabled, .annot-tool:disabled{
  opacity:.55; cursor:wait;
}

/* === forms.js: комбобокс и дропзона (макет 03.1/03.3) ===
   Источник: docs/redesign/styles.css, блоки 03.1 и 03.3; имена классов как в макете
   (их создаёт static/js/forms.js). Форма дефекта (секция defects/defect_form ниже)
   их не дублирует — только раскладка .df-* и узкие отличия. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
/* Поле формы */
.fld{margin-bottom:14px;min-width:0}
.fld label{display:block;font-size:13px;font-weight:600;color:var(--text);margin-bottom:5px}
.fld input:not(:where([type=checkbox],[type=radio])),.fld select,.fld textarea{display:block;width:100%;background:var(--surface);border:1px solid var(--line);border-radius:9px;padding:9px 11px;font-size:14px;line-height:1.4;transition:border-color .12s,box-shadow .12s}
.fld input:not(:where([type=checkbox],[type=radio])):focus,.fld select:focus,.fld textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)}
.fld textarea{resize:vertical;min-height:0}
/* Чекбоксы/радио внутри .fld не растягивать (legacy .fld input{width:100%} из base.html). */
.fld input:is([type=checkbox],[type=radio]){width:auto;display:inline-block;padding:0}
.fld ::placeholder{color:var(--muted);opacity:.7}
.req{color:var(--red);font-weight:700}
.req-legend{font-size:12px;color:var(--muted);margin:0}
.fld-help{font-size:12px;color:var(--muted);margin:5px 0 0}
.fld-error{font-size:12px;color:var(--red);margin:5px 0 0;font-weight:600}
.fld--err input,.fld--err select,.fld--err textarea{border-color:var(--red)}
.form-errors{background:var(--red-bg);border:1px solid var(--red);border-radius:9px;padding:10px 12px;color:var(--text);margin:0 0 14px}
/* Дропзона вложений. */
.dropzone{border:2px dashed var(--line);border-radius:12px;padding:20px 14px;text-align:center;background:var(--bg);transition:border-color .15s,background .15s;display:block}
/* Курсор-«руку» получает только кликабельная зона (label.dropzone) и оверлей input;
   div.dropzone загрузчика на карточке дефекта кликается только по своим кнопкам. */
label.dropzone{cursor:pointer}
.dropzone.dragover{border-color:var(--accent);background:var(--accent-soft)}
.dz-icons{display:flex;gap:10px;justify-content:center;color:var(--accent);margin-bottom:8px}
.dz-title{font-size:14px;font-weight:700;color:var(--text)}
.dz-sub{font-size:12px;color:var(--muted);margin:3px 0 12px}
.dropzone input[type=file]{width:100%;font-size:12px;color:var(--muted);border:0;background:none;padding:0}
.dz-hint{font-size:11px;color:var(--muted);margin:10px 0 0;line-height:1.5}
.dz-files{font-size:12px;color:var(--accent);font-weight:600;margin-top:8px;overflow-wrap:anywhere}
/* Кастомный комбобокс поверх нативного select (JS-апгрейд, прогрессивно). */
.cb{position:relative}
select.cb-native{position:absolute;opacity:0;width:1px;height:1px;padding:0;margin:0;border:0;pointer-events:none}
.cb-trigger{display:flex;align-items:center;gap:9px;width:100%;padding:9px 11px;border-radius:7px;background:var(--surface);border:1px solid var(--line);color:var(--text);font-size:inherit;line-height:1.4;cursor:pointer;text-align:left;min-height:0}
.fld .cb-trigger{font-size:14px;border-radius:9px}
.cb-trigger[disabled]{opacity:.55;cursor:default}
.cb-trigger[disabled]:hover{border-color:var(--line)}
.cb-none{display:none}
.cb-trigger .cb-none+.cb-label,.cb-opt .cb-none+.cb-opt-label{margin-left:0}
.cb-trigger:hover{border-color:var(--accent)}
.cb-trigger:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)}
/* Фокус на скрытом select (после invalid) — кольцо на триггере; невалидный — красная рамка. */
.cb:focus-within .cb-trigger{border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)}
.cb-trigger[aria-invalid="true"]{border-color:var(--red)}
.cb-label{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cb-chev{flex:none;color:var(--muted);display:flex;transition:transform .15s}
.cb[data-open="true"] .cb-chev{transform:rotate(180deg)}
.cb-dot{flex:none;width:10px;height:10px;border-radius:50%;background:var(--muted)}
.cb-dot--empty{background:transparent;border:1.5px solid var(--line)}
.cb-ic{flex:none;display:flex;width:16px;height:16px}
.cb-ic svg{width:16px;height:16px}
.cb-panel{position:absolute;top:calc(100% + 6px);left:0;min-width:100%;width:max-content;max-width:min(480px,calc(100vw - 32px));z-index:60;background:var(--surface);border:1px solid var(--line);border-radius:11px;box-shadow:0 12px 32px #22233c2e;padding:6px}
.cb-search{width:100%;padding:8px 10px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--text);font-size:13px;line-height:1.4;margin-bottom:4px}
.cb-search:focus{outline:none;border-color:var(--accent)}
.cb-list{max-height:260px;overflow:auto;display:flex;flex-direction:column;gap:1px}
.cb--right .cb-panel{left:auto;right:0}
.cb--up .cb-panel{top:auto;bottom:calc(100% + 6px)}
.cb-opt{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:8px;cursor:pointer;color:var(--text);font-size:13px}
.fld .cb-opt{font-size:14px}
.cb-opt .cb-opt-label{overflow-wrap:anywhere}
.cb-opt:hover,.cb-opt[data-active="true"]{background:var(--accent-soft)}
.cb-opt-label{flex:1 1 auto}
.cb-check{flex:none;margin-left:auto;color:var(--accent);opacity:0;display:flex}
.cb-opt[aria-selected="true"] .cb-check{opacity:1}
.cb-opt[data-hidden="true"]{display:none}
.cb-empty-note{padding:8px 10px;color:var(--muted);font-size:13px}
.row>label:has(>.cb){display:inline-flex;align-items:center;gap:8px}
/* 03.3 · Дропзона вместо нативного input[type=file]: прозрачный input поверх зоны */
.dropzone{position:relative}
.dropzone input[type=file]{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer;font-size:0}
.dropzone:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 22%,transparent)}
.dz-btn{pointer-events:none;margin-top:2px;padding:7px 14px;font-size:12px;background:var(--surface)}
.dropzone.dragover .dz-btn{border-color:var(--accent);color:var(--accent)}
.dz-files{position:relative;z-index:1;pointer-events:none}
.dropzone--compact{padding:14px 14px 16px}
.dropzone--compact .dz-icons svg{width:20px;height:20px}
.dropzone--compact .dz-title{font-size:13px}
.dropzone--compact .dz-sub{margin-bottom:10px}
.dz-field>label:first-child{display:block;font-size:13px;font-weight:600;color:var(--text);margin-bottom:6px}
.dz-field .dz-hint{margin-top:8px}

/* === defects/defect_form ===
   Двухколоночная раскладка .df-form — формы дефекта и кейса: редактор дефекта
   (defects/_defect_form_body.html, классы defect-form + df-form) на странице defect_form.html
   и в шторке, тело шторки тест-кейса (testcases/_testcase_body.html, только df-form).
   Общие .fld, .req, комбобокс .cb-… и .dropzone — в секции forms.js выше; здесь только раскладка
   .df-* и узкие отличия внутри формы. Радиус карточек 12px и тень — по макету 03.1; отступы и шрифты
   карточек (padding:20px, gap:18px, font-weight:800 у .df-head) — прежние из старого шаблона. */
.df-form .df-cols{display:grid; grid-template-columns:minmax(0,68fr) minmax(300px,32fr);
  gap:20px; align-items:start;}
.df-form .df-main, .df-form .df-side{display:flex; flex-direction:column; gap:18px; min-width:0;}
.df-form .df-side{position:sticky; top:16px;}
.df-form .df-card{background:var(--surface); border:1px solid var(--line); border-radius:12px;
  padding:20px; box-shadow:0 1px 2px #22233c0a;}
.df-form .df-head{font-size:11px; font-weight:800; letter-spacing:.08em; text-transform:uppercase;
  color:var(--muted); margin:0 0 14px;}
.df-form .df-gap{margin-top:14px;}
.df-form .df-gap > .fld:last-child{margin-bottom:0;}
/* Крупное поле заголовка сверху. */
.df-form .df-title .fld{margin-bottom:0;}
.df-form .df-title input{font-size:16px; font-weight:600; padding:11px 13px;}
/* Сетка окружения / парные поля. */
.df-form .df-grid2{display:grid; grid-template-columns:1fr 1fr; gap:14px;}
.df-form .df-grid2 .fld{margin-bottom:0;}
.df-form .df-card > .fld:last-child{margin-bottom:0;}
/* Отступы легенды и сводки ошибок над колонками (глобально: легенда 0, сводка 0 0 14px). */
.df-form .req-legend{margin-bottom:16px;}
.df-form .form-errors{margin-bottom:16px;}
/* Метаданные: чуть плотнее (много полей друг под другом). */
.df-form .df-side .fld{margin-bottom:13px;}
.df-form .df-side .fld:last-child{margin-bottom:0;}
/* Действия. */
.df-form .df-actions{display:flex; gap:10px; align-items:center; margin-top:2px;}
.df-form .df-actions .btn{padding:10px 18px; border-radius:11px;}
/* Независимый скролл колонок (десктоп): страница целиком не прокручивается —
   левая и правая колонки получают собственные скроллбары. На узких экранах
   (≤900px, колонки стопкой) — обычный поток, прокручивается вся страница. */
@media (min-width: 901px){
  .content-inner.df-page{height:100%; display:flex; flex-direction:column; min-height:0;}
  .content-inner.df-page > .defect-form{flex:1 1 auto; min-height:0; display:flex; flex-direction:column;}
  .df-form .df-cols{flex:1 1 auto; min-height:0; align-items:stretch;}
  .df-form .df-main, .df-form .df-side{overflow-y:auto; min-height:0; padding-right:6px; padding-bottom:8px;}
  .df-form .df-side{position:static;}  /* колонка сама скроллится — sticky не нужен */
}
@media (max-width: 900px){
  .df-form .df-cols{grid-template-columns:1fr;}
  .df-form .df-grid2{grid-template-columns:1fr;}
  .df-form .df-side{position:static;}
}

/* === testcases/_testcase_body: шторка кейса (макет 03.2) ===
   Карточка тест-кейса в шторке: раскладка — .df-* выше, здесь только блоки .cv-* макета.
   Цвета — только токены. */
.df-form .cv-title{padding:20px 22px 18px;}
.cv-badges{display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin-bottom:12px;}
.cv-h1{font-size:21px; line-height:1.25; letter-spacing:-.4px; font-weight:650; margin:0; overflow-wrap:anywhere;}
.cv-sub{margin:8px 0 0; font-size:12.5px; color:var(--muted);}
.cv-block{padding:13px 0; border-top:1px solid var(--line);}
/* Первый блок карточки (сразу под .df-head) — без рамки и верхнего отступа. */
.df-head + .cv-block, .cv-block:first-child{border-top:0; padding-top:0;}
.cv-block:last-child{padding-bottom:0;}
/* Шрифт .cv-label и .cv-block .prose — общий с полями карточки дефекта (секция defect-view). */
.cv-label{margin-bottom:7px;}
.cv-empty{color:var(--muted); font-size:13px;}
.cv-meta{display:grid; grid-template-columns:1fr 1fr; gap:12px 14px; margin:0;}
.cv-meta div{min-width:0;}
.cv-meta dt{font-size:11px; color:var(--muted); margin-bottom:3px;}
.cv-meta dd{margin:0; font-size:13px; font-weight:550; overflow-wrap:anywhere;}
.cv-check-ro{margin-bottom:12px;}
.cv-linked{margin-top:12px; padding-top:12px; border-top:1px solid var(--line);}
.cv-linked .btn{width:100%; justify-content:flex-start;}
@media (max-width:760px){
  .df-form .cv-title{padding:16px;}
}

/* === drawer.js: шторка форм дефекта (макет 03.1) и тест-кейса (макет 03.2) ===
   Те же формы .df-form (_defect_form_body.html, _testcase_body.html) внутри
   <dialog id="drawer" class="drawer drawer--wide">.
   Всё скоуплено на шторку — страница формы не меняется. Кнопки действий — в шапке шторки. */
#drawer .df-actions{display:none;}
.drawer--wide .df-form{display:flex; flex-direction:column; gap:16px;}
.drawer--wide .df-form .req-legend, .drawer--wide .df-form .form-errors{margin-bottom:0;} /* отступ даёт gap */
/* Широкий экран: шапка сверху, колонки скроллятся независимо, шторка целиком не прокручивается. */
@media (min-width:1100px){
  .drawer--wide[open]{display:flex; flex-direction:column; overflow:hidden;}
  .drawer--wide .dialoghead{flex:none; position:static;}
  .drawer--wide .dialogbody{flex:1 1 auto; min-height:0; display:flex; flex-direction:column;}
  .drawer--wide .df-form{flex:1 1 auto; min-height:0;}
  .drawer--wide .df-cols{flex:1 1 auto; min-height:0; align-items:stretch;}
  .drawer--wide .df-main, .drawer--wide .df-side{overflow-y:auto; min-height:0; padding-right:6px; padding-bottom:8px; scrollbar-width:thin;}
  /* «Документы задачи» (_documents_list.html): три карточки .td-form естественной высоты —
     правило flex:1 1 auto; min-height:0 выше сжимало их под высоту экрана, и при 2+ документах
     или нескольких выбранных файлах кнопка «Загрузить» уезжала под соседнюю карточку.
     Здесь колонок со своим скроллом нет — прокручивается тело шторки целиком. */
  .drawer--wide .td-form{flex:none;}
  .drawer--wide .dialogbody:has(> .td-form){overflow-y:auto;}
}
/* Уже 1100px: одна колонка, скроллится шторка целиком (шапка sticky); ширину 100vw задаёт
   правило ≤1100px у .drawer--wide выше. Страничные правила (min-width:901px) дают колонкам
   свой скролл и отступы под скроллбар — в одноколоночной шторке они не нужны. */
@media (max-width:1099px){
  .drawer--wide .df-cols, .drawer--wide .df-grid2{grid-template-columns:1fr;}
  .drawer--wide .df-main, .drawer--wide .df-side{overflow-y:visible; padding-right:0; padding-bottom:0;}
}

/* === accounts/login + registration/password_change (макет 03.x login) ===
   Экран входа: .login-layout — две колонки (арт-панель + форма), ≤760px арт скрыт.
   Смена пароля: узкая центрированная карточка формы. Глобальный .card--narrow (560px,
   подтверждения/форма статуса) не трогаем — сужение и центр скоупятся на .pw-page. */
.pw-page .card--narrow{max-width:480px; margin:0 auto;} /* узкая центрированная карточка формы */
.pw-actions{margin:16px 0 0;}

.login-layout{display:grid; grid-template-columns:1fr 1fr;
  min-height:min(640px, calc(100vh - 220px)); min-height:min(640px, calc(100dvh - 220px));
  border-radius:14px; overflow:hidden; border:1px solid var(--line);}
/* Арт-панель намеренно тёмная в обеих темах: белый текст как у .prio (--on-accent в тёмной теме тёмный).
   В тёмной теме --accent2 светлый, поэтому фон панели там — --side (правило ниже). */
.login-art{padding:48px; background:var(--accent2); color:#fff;
  display:flex; flex-direction:column; justify-content:center;}
html[data-theme="dark"] .login-art{background:var(--side); color:var(--text); border-right:1px solid var(--line);}
.login-art-mark{display:flex; align-items:center; justify-content:center; width:44px; height:44px;
  border-radius:10px; border:1px solid currentColor; opacity:.9; font-weight:700; letter-spacing:.02em;}
html[data-theme="dark"] .login-art-mark{color:var(--accent);}
.login-art h2{font-size:38px; line-height:1.2; letter-spacing:-1.2px; margin:35px 0 20px;}
.login-art p{margin:0; font-size:14px; max-width:340px; opacity:.78;}
.login-form{background:var(--surface); padding:50px; display:flex; flex-direction:column; justify-content:center;}
.login-form h1{font-size:25px; margin:0 0 10px;}
.login-form .form-errors{margin:8px 0 0;}
.login-form .fld{margin:18px 0;}
.login-form .btn{width:100%;}
.login-note{color:var(--muted); font-size:12px; margin:16px 0 0;}
@media (max-width:1100px){
  .login-art, .login-form{padding:30px;}
}
@media (max-width:760px){
  .login-layout{grid-template-columns:1fr; min-height:0;}
  .login-art{display:none;}
  .login-form{padding:30px;}
  .login-form input{font-size:16px;} /* iOS не зумит поле при фокусе */
}

/* === share: режим отчёта (макет .report-mode) ===
   Публичный отчёт заказчика по токену: plain-шелл без сайдбара (base.html вешает
   .report-mode на #app при is_share_guest). Ширина контента как в макете
   (.report-mode main{max-width:1250px}) — перекрывает .app.plain .content (1180px).
   Таблица гостя уже основной (нет чекбоксов/исполнителя/задачи/комментариев),
   поэтому свой минимум ширины вместо .defects-table. */
.app.plain.report-mode .content{max-width:1250px;}
.share-table{min-width:1080px;}

/* === testcases/_documents_list.html ===
   Шторка «Документы задачи» (и страница documents_page.html без JS): три соседние формы
   .df-form (список / загрузка / ссылка), вход — кнопка + счётчик в карточке тест-плана. */
.td-entry{display:flex;align-items:center;gap:8px;}
.td-form + .td-form{margin-top:14px;}
.td-page .td-form{max-width:1100px;}
.td-table{width:100%;font-size:13px;}
.td-table th,.td-table td{vertical-align:top;}
.td-name{overflow-wrap:anywhere;min-width:160px;}
.td-nowrap{white-space:nowrap;}
.td-status .chip{white-space:normal;}
.td-meta{font-size:12px;color:var(--muted);white-space:nowrap;}
.td-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:0 12px;margin-top:12px;}
.td-actions{display:flex;justify-content:flex-end;margin-top:4px;}
/* Выбранные файлы (taskdocs.js): по одному в строке, дубль по имени — красным с пометкой. */
.td-form .dz-files ul{list-style:none;margin:0;padding:0;display:inline-block;text-align:left;}
.td-form .dz-files li{line-height:1.5;}
.td-form .dz-files li.is-dup{color:var(--red);}
.td-form .dz-files li.is-dup::before{content:"⚠ ";}
.td-dup-errors{margin:12px 0 0;}
.drawer--wide .df-form .td-dup-errors{margin-top:0;} /* в шторке отступ даёт gap .df-form */
/* Спиннер (keyframes genspin — общий, см. #aigen-review .gen-spin). */
.td-spin{display:inline-block;width:12px;height:12px;flex:none;vertical-align:-2px;
  border:2px solid var(--line);border-top-color:var(--accent);border-radius:50%;animation:genspin .8s linear infinite;}
/* Прогресс загрузки: определённая полоса по xhr.upload (htmx:xhr:progress), затем
   --indeterminate — файлы отправлены, сервер сохраняет и читает документы (keyframes at-gen-sweep — общий). */
.td-progress{margin-top:12px;}
.td-progress-label{display:flex;align-items:center;gap:8px;color:var(--accent);font-size:13px;font-weight:600;}
.td-progress-bar{position:relative;height:6px;margin-top:8px;border-radius:6px;background:var(--line);overflow:hidden;}
.td-progress-bar > span{display:block;height:100%;border-radius:6px;background:var(--accent);transition:width .2s ease;}
.td-progress--indeterminate .td-progress-bar > span{position:absolute;top:0;width:35% !important;animation:at-gen-sweep 1.4s ease-in-out infinite;}
@media (prefers-reduced-motion: reduce){
  .td-spin{animation:none;}
  .td-progress--indeterminate .td-progress-bar > span{animation:none;left:0;width:100% !important;opacity:.5;}
}

/* === testcases/_tabs.html ===
   Вкладки раздела тест-кейсов задачи («Тест-кейсы» / «Чек-листы»), по мотивам макета
   редизайна: подчёркивание активной вкладки акцентом. */
.tabs{display:flex;gap:23px;border-bottom:1px solid var(--line);margin-bottom:22px;overflow:auto;}
.tab{padding:0 1px 13px;color:var(--muted);font-size:12px;white-space:nowrap;border-bottom:2px solid transparent;text-decoration:none;}
.tab:hover{color:var(--text);}
.tab.active{color:var(--accent);border-color:var(--accent);font-weight:600;}

/* === testcases/_checklists.html ===
   Вкладка «Чек-листы»: карточка на чек-лист (шапка с действиями, счётчики, формы правки
   и «+ Пункт», таблица пунктов). Всё под #checklists; бейджи приоритета — общие .prio-*,
   техника и счётчики — .chip/.chip--*, сетка полей — .tc-grid. */
#checklists .card{margin-bottom:16px;}
#checklists .cl-btns{display:flex;gap:8px;flex-wrap:wrap;align-items:center;}
#checklists .cl-btns form, #checklists .cl-actions form{margin:0;}
#checklists .cl-desc{margin:8px 0 0;font-size:12.5px;}
#checklists .cl-counters{display:flex;gap:6px;flex-wrap:wrap;margin:10px 0 12px;}
#checklists .cl-panel{margin:0 0 16px;}
#checklists .cl-col-num{width:40px;}
#checklists .cl-col-prio{width:90px;}
#checklists .cl-col-status{width:190px;}
#checklists .cl-text{font-weight:550;overflow-wrap:anywhere;}
#checklists .status-form{display:flex;gap:6px;align-items:center;margin:0;}
#checklists .status-form.is-saved select, #checklists .status-form.is-saved .cb{outline:2px solid var(--green);outline-offset:1px;}
#checklists .cl-actions{display:flex;gap:6px;flex-wrap:wrap;align-items:flex-start;}
#checklists .cl-edit-row td{background:var(--panel2);}

/* === aigen/assistant.html ===
   Страница AI-помощника: слева треды (≤ 900 px — сворачиваемый блок сверху), в центре лента
   ходов и композер. Всё под .ai-layout; спиннер и блок ошибки — общие .gen-spin/.run-error
   (секция aigen/run_review выше), чипы — .chip, поля — .fld, кнопки — .btn*. */
.ai-layout{display:grid;grid-template-columns:260px 1fr;gap:20px;align-items:start;}
.ai-layout [hidden]{display:none !important;}
.ai-side{position:sticky;top:12px;min-width:0;max-height:calc(100vh - 24px);overflow:auto;}
.ai-side-sum{list-style:none;cursor:default;pointer-events:none;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);margin-bottom:8px;}
.ai-side-sum::-webkit-details-marker{display:none;}
.ai-new{width:100%;margin-bottom:10px;}
.ai-thread-list{display:flex;flex-direction:column;gap:4px;}
.ai-thread{display:flex;flex-direction:column;gap:4px;padding:8px 10px;border-radius:8px;border:1px solid transparent;color:var(--text);text-decoration:none;min-width:0;}
.ai-thread:hover{background:var(--row-hover);}
.ai-thread.active{background:var(--accent-soft);border-color:color-mix(in srgb,var(--accent) 35%,var(--line));}
.ai-thread-title{font-weight:550;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.ai-thread-meta{display:flex;gap:6px;align-items:center;font-size:11px;color:var(--muted);min-width:0;}
.ai-thread--archived .ai-thread-title{color:var(--muted);}
.ai-archive{margin-top:12px;border-top:1px solid var(--line);padding-top:10px;}
.ai-archive-sum{cursor:pointer;font-size:12px;color:var(--muted);margin-bottom:6px;}
.ai-main{min-width:0;display:flex;flex-direction:column;gap:14px;}
.ai-thread-head{display:flex;gap:12px;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;}
.ai-thread-name{margin:0;font-size:17px;overflow-wrap:anywhere;}
.ai-thread-tools{display:flex;gap:8px;align-items:flex-start;flex-wrap:wrap;}
.ai-thread-tools form{margin:0;}
.ai-rename summary{list-style:none;}
.ai-rename summary::-webkit-details-marker{display:none;}
.ai-rename-form{display:flex;gap:6px;margin-top:6px;}
.ai-rename-form input{min-width:0;flex:1;padding:5px 8px;border-radius:7px;border:1px solid var(--line);background:var(--surface);color:var(--text);}
.ai-timeline{display:flex;flex-direction:column;gap:16px;}
.ai-empty{border:1px dashed var(--line);border-radius:var(--radius);padding:18px;background:var(--surface);}
.ai-empty p{margin:0 0 6px;}
.ai-ex{display:flex;flex-direction:column;gap:8px;}
.ai-msg{border-radius:var(--radius);padding:12px 14px;min-width:0;overflow-wrap:anywhere;}
.ai-msg--user{align-self:flex-end;max-width:min(640px,92%);background:var(--accent-soft);border:1px solid color-mix(in srgb,var(--accent) 25%,var(--line));}
.ai-msg--user .chip{margin:0 6px 6px 0;}
.ai-msg-docs{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px;}
.ai-msg--bot{background:var(--surface);border:1px solid var(--line);}
.ai-progress{display:flex;align-items:center;gap:8px;color:var(--accent);font-weight:600;font-size:13px;}
.ai-answer{line-height:1.6;}
.ai-answer > :first-child{margin-top:0;}
.ai-answer > :last-child{margin-bottom:0;}
.ai-answer table{border-collapse:collapse;width:100%;margin:10px 0;border:1px solid var(--line);font-size:12.5px;}
.ai-answer th,.ai-answer td{border:1px solid var(--line);padding:6px 10px;text-align:left;vertical-align:top;}
.ai-answer thead th{background:var(--panel2);}
.ai-answer ul,.ai-answer ol{padding-left:22px;}
.ai-answer pre{overflow:auto;background:var(--bg);padding:10px;border-radius:8px;}
/* --- Стрим частичного ответа (docs/plans/2026-09-28-assistant-streaming.md): пока ход
   RUNNING, ответ — сырой текст в [data-stream-text] (JS дописывает через textContent/
   appendData), поэтому переносы строк держим на white-space, а не на <br>/<p> из render_md;
   мигающая каретка в конце — отключается при prefers-reduced-motion (I-1 из code-review). --- */
.ai-answer--stream{white-space:pre-wrap;}
/* m4(а) (повторное ревью): блок есть ВСЕГДА (даже без текста — JS ждёт куда дописать), но
   пустой .ai-answer--stream + margin-top у .ai-run-actions давали заметный зазор между
   «В очереди» и «Остановить». Скрываем ПУСТОЙ блок целиком (:empty — только пока в нём нет
   ни текста, ни узлов; textContent/appendData на первом чанке делают его непустым — блок
   тогда сразу появляется, ::after-каретка на :empty не видна, это ожидаемо). Отступ перенесён
   на сами кнопки/ссылки (.ai-run-actions > *), а не на контейнер — так пропавший текстовый
   блок не оставляет висящий отступ сам по себе. */
.ai-answer--stream:empty{display:none;}
.ai-answer--stream::after{content:"";display:inline-block;width:2px;height:1em;margin-left:2px;
  vertical-align:text-bottom;background:var(--accent);animation:ai-caret-blink 1s step-end infinite;}
@keyframes ai-caret-blink{50%{opacity:0;}}
@media (prefers-reduced-motion: reduce){ .ai-answer--stream::after{animation:none;} }
.ai-retry{margin:10px 0 0;}
/* Кнопка «Остановить» + ссылка «Обновить» активного chat-хода — в одну строку, компактно
   (btn-sm), а не столбиком (задача 2026-09-28, находка ревью «вёрстка»). Отступ — на кнопках/
   ссылке (не на контейнере): у admin-на-чужом-треде .ai-run-actions под JS может остаться
   пустым (только «Обновить», а её html.js прячет, см. ниже) — margin на контейнере тогда
   висел бы сам по себе (m4(б)); на кнопках он схлопывается вместе с ними. */
.ai-run-actions{display:flex;gap:6px;flex-wrap:wrap;align-items:center;}
.ai-run-actions form{margin:0;}
.ai-run-actions > *{margin-top:10px;}
/* «Обновить» — путь без JS (стрим её заменяет живым обновлением); html.js ставится синхронно
   в <head> (см. base.html), поэтому скрытие не мигает.
   m4(в) (повторное ревью, пограничный случай): если QAPoll не загрузился (сеть/CDN/ошибка
   скрипта), html.js всё равно стоит (ставится синхронно в <head>, ДО загрузки любых скриптов)
   — «Обновить» будет скрыта, а живой стрим её не заменит. Осознанно оставляем как есть: тред
   всё равно виден и обновляется обычным ходом страницы (F5/переход), а различать «html.js
   есть, но QAPoll не подтянулся» от «просто нет сети сейчас» на чистом CSS не выйдет. */
html.js .ai-refresh-nojs{display:none;}
.ai-access{background:color-mix(in srgb,var(--major) 8%,var(--panel));border:1px solid var(--major);border-radius:8px;padding:12px;display:flex;flex-wrap:wrap;gap:8px;align-items:center;}
.ai-questions{margin-top:12px;display:flex;flex-direction:column;gap:10px;}
.ai-q{border:1px solid var(--line);border-radius:10px;padding:10px 12px;background:var(--bg);}
.ai-q-text{font-weight:600;margin-bottom:4px;}
.ai-q-opts{display:flex;flex-direction:column;gap:4px;margin:8px 0;}
.ai-q-opt{display:flex;gap:8px;align-items:flex-start;font-size:13px;cursor:pointer;}
.ai-q-row{display:flex;gap:8px;align-items:center;margin-top:6px;}
.ai-q-own{flex:1;min-width:0;padding:6px 9px;border-radius:7px;border:1px solid var(--line);background:var(--surface);color:var(--text);}
.ai-q-actions{display:flex;gap:8px;flex-wrap:wrap;}
.ai-draft{margin-top:12px;border:1px solid color-mix(in srgb,var(--accent) 35%,var(--line));border-radius:10px;padding:10px 12px;display:flex;gap:10px;justify-content:space-between;align-items:center;flex-wrap:wrap;background:var(--accent-soft);}
.ai-draft-title{font-weight:600;}
.ai-draft-btns{display:flex;gap:6px;flex-wrap:wrap;align-items:center;}
.ai-draft-btns form{margin:0;}
.ai-result-actions{margin-top:12px;display:flex;gap:6px;flex-wrap:wrap;align-items:center;}
.ai-composer{position:relative;border:1px solid var(--line);border-radius:var(--radius);background:var(--surface);padding:14px;}
.ai-start{position:relative;display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin-bottom:10px;}
.ai-start-label{font-size:12px;color:var(--muted);margin-right:4px;}
.ai-chip,.ai-all{cursor:pointer;font-size:12px;padding:5px 10px;}
.ai-chip:hover,.ai-all:hover,.ai-chip.is-active{border-color:var(--accent);color:var(--accent);}
.ai-picker{position:absolute;left:0;right:0;top:calc(100% + 6px);z-index:20;background:var(--surface);border:1px solid var(--line);border-radius:12px;box-shadow:0 12px 32px color-mix(in srgb,var(--text) 18%,transparent);padding:12px;max-height:420px;overflow:auto;}
.ai-picker-search{width:100%;padding:8px 10px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--text);margin-bottom:10px;}
.ai-picker-tabs{display:flex;gap:18px;border-bottom:1px solid var(--line);margin-bottom:10px;}
.ai-picker-tab{background:none;border:0;border-bottom:2px solid transparent;padding:0 1px 8px;color:var(--muted);cursor:pointer;font-size:12px;}
.ai-picker-tab.active{color:var(--accent);border-color:var(--accent);font-weight:600;}
.ai-picker-pane{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:8px;margin-bottom:8px;}
.ai-card{display:grid;grid-template-columns:auto 1fr;gap:2px 8px;text-align:left;padding:10px;border-radius:10px;border:1px solid var(--line);background:var(--bg);color:var(--text);cursor:pointer;}
.ai-card:hover{border-color:var(--accent);}
.ai-card-icon{grid-row:span 2;font-size:18px;line-height:1.2;}
.ai-card-title{font-weight:600;font-size:13px;}
.ai-card-desc{font-size:12px;color:var(--muted);}
.ai-docs-row{display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin-bottom:10px;}
.ai-doc{gap:6px;cursor:pointer;font-size:12px;padding:4px 8px;max-width:100%;overflow:hidden;text-overflow:ellipsis;}
/* Файлы в композере: без JS input виден обычным полем; с JS прячется, открывает его метка-кнопка. */
.ai-attach{cursor:pointer;}
html.js .ai-attach-native{position:absolute;width:1px;height:1px;opacity:0;overflow:hidden;}
.ai-attach:has(+ .ai-attach-native:focus-visible){outline:2px solid var(--accent);outline-offset:2px;}
.ai-attach-list{display:flex;gap:6px;flex-wrap:wrap;align-items:center;min-width:0;}
.ai-attach-list:empty{display:none;}
.ai-file{gap:6px;font-size:12px;padding:4px 4px 4px 8px;max-width:100%;overflow:hidden;text-overflow:ellipsis;}
.ai-file--big{border-color:var(--critical);color:var(--critical);}
.ai-attach-warn{color:var(--critical);}
.ai-file-x{background:none;border:0;color:var(--muted);cursor:pointer;font-size:14px;line-height:1;padding:0 4px;border-radius:4px;}
.ai-file-x:hover,.ai-file-x:focus-visible{color:var(--critical);}
.ai-attach-hint{display:block;margin-bottom:10px;}
.ai-composer.dragover{border-color:var(--accent);background:var(--accent-soft);}
.ai-technique,.ai-bitrix{max-width:420px;}
.ai-text{margin-bottom:10px;}
.ai-send-row{display:flex;gap:12px;justify-content:space-between;align-items:center;flex-wrap:wrap;}
.ai-send-row .note{flex:1;min-width:0;}
.ai-readonly{margin:0;}
@media (max-width: 900px){
  .ai-layout{grid-template-columns:1fr;gap:12px;}
  .ai-side{position:static;max-height:none;overflow:visible;border:1px solid var(--line);border-radius:var(--radius);background:var(--surface);padding:10px 12px;}
  .ai-side-sum{cursor:pointer;pointer-events:auto;list-style:revert;margin-bottom:0;}
  .ai-side-fold[open] .ai-side-sum{margin-bottom:10px;}
  .ai-msg--user{max-width:100%;}
  .ai-send-row .btn{width:100%;}
}

/* === projects/overview === */
/* Страница «Обзор проекта» (docs/redesign app.js metrics()/overview()); .stats/.stat уже
   заняты пилюлями-фильтрами severity (defects/_dashboard.html) — свои классы ov-*. */
/* M-4: auto-fit вместо фиксированных 4 колонок — при <4 карточках (нет права на
   тест-кейсы/автотесты) занятые колонки растягиваются на всю ширину плашки, пустых
   колонок справа не остаётся (пустые repeat-треки схлопываются сами). */
.ov-metrics{display:grid;grid-template-columns:repeat(auto-fit,minmax(0,1fr));gap:0;margin-bottom:20px;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:18px 0;}
.ov-metric{padding:0 22px;border-right:1px solid var(--line);min-width:0;}
.ov-metric:last-child{border-right:0;}
.ov-metric-label{font-size:11px;color:var(--muted);}
.ov-metric-value{font-size:28px;line-height:1.3;letter-spacing:-.02em;margin:8px 0 4px;
  font-weight:650;font-variant-numeric:tabular-nums;}
.ov-metric-note{font-size:10px;color:var(--muted);}
.ov-grid{display:grid;grid-template-columns:minmax(0,1.6fr) minmax(260px,1fr);gap:20px;margin-bottom:20px;}
.ov-statusrow{display:grid;grid-template-columns:140px 1fr 30px;align-items:center;gap:12px;
  width:100%;font-size:12px;text-align:left;padding:9px 0;color:var(--text);}
.ov-statusrow:hover{color:var(--accent);}
.ov-progress{height:6px;background:var(--line);border-radius:6px;overflow:hidden;}
.ov-progress > span{display:block;height:100%;background:var(--accent);}
.ov-tasklist{display:grid;gap:16px;padding-top:2px;}
/* M-5: min-width:0 на строках задач + overflow-wrap на заголовке — длинное слово без
   пробелов (URL, код Bitrix) переносится, а не распирает панель. */
.ov-tasklist > div{min-width:0;}
.ov-task-title{display:block;font-size:13px;font-weight:600;color:var(--text);overflow-wrap:anywhere;}
.ov-task-title:hover{color:var(--accent);}
.ov-task-meta{font-size:11px;color:var(--muted);margin-top:3px;}
@media (max-width:760px){
  /* M-3: на 2 колонках нужен вертикальный зазор — без row-gap вторая строка карточек
     вплотную прилегает к первой (в .ov-metrics базовый gap:0 — только для вертикальных
     разделителей внутри строки на десктопе). */
  .ov-metrics{grid-template-columns:1fr 1fr;row-gap:20px;}
  .ov-metric:nth-child(2){border-right:0;}
  .ov-grid{grid-template-columns:1fr;gap:16px;}
  .ov-statusrow{grid-template-columns:110px 1fr 26px;gap:8px;}
}

/* === reports: публичный отчёт (reports/public_report.html) и диалог «Отчёт» === */
.rp-head{align-items:flex-start;}
.rp-title{display:flex;gap:14px;align-items:center;min-width:0;}
.rp-logo{max-height:40px;max-width:160px;object-fit:contain;}
.rp-group{margin-bottom:20px;}
.rp-plan{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:4px 14px;margin:0 0 14px;}
.rp-plan dt{color:var(--muted);}
.rp-plan dd{margin:0;overflow-wrap:anywhere;}
.rp-pre{white-space:pre-wrap;overflow-wrap:anywhere;}
.rp-table{width:100%;}
.rp-checks{list-style:none;margin:0 0 12px;padding:0;display:grid;gap:6px;}
.rp-sec{font-weight:600;margin-top:10px;}
.rp-case{border-bottom:1px solid var(--line);padding:8px 0;}
.rp-kinds{width:100%;border-collapse:collapse;}
.rp-kinds th{text-align:left;font-weight:500;padding:6px 8px 6px 0;}
.rp-kinds td{padding:6px 0;}
.rp-kinds td .btn{margin-right:6px;}
.rp-link{padding:8px 0;border-bottom:1px solid var(--line);}
.rp-link-meta{margin-bottom:6px;overflow-wrap:anywhere;}

/* === sheets (клиентская таблица: sheets/_table.html, _cell.html, _history.html) === */
.sheet-scroll{overflow-x:auto}
.sheet-pager{display:flex;align-items:center;justify-content:center;gap:12px;margin:12px 0}
.sheet-pager .btn-disabled{opacity:.45;pointer-events:none}
.sheet-media{list-style:none;padding:0;margin:0 0 4px;display:flex;flex-wrap:wrap;gap:4px}
.sheet-thumb{width:64px;height:48px;object-fit:cover;border-radius:var(--radius);border:1px solid var(--line)}
.sheet-link-list{list-style:none;padding:0}
.sheet-link-url{width:100%;max-width:520px}
.sheet-table{border-collapse:collapse;min-width:100%;font-size:13px}
.sheet-table th,.sheet-table td{border:1px solid var(--line);padding:4px 6px;vertical-align:top;text-align:left}
.sheet-table th{background:var(--side);position:sticky;top:0}
.sheet-table input,.sheet-table select,.sheet-table textarea{width:100%;min-width:120px;font:inherit}
.sheet-num{width:40px;color:var(--muted)}
/* ширина по типу колонки: длинный текст и подписи статуса читаются целиком, короткие — узкие */
.sheet-cell--f-text input{min-width:280px}
.sheet-cell--f-expected textarea,.sheet-cell--text textarea{min-width:220px}
.sheet-cell--f-status select{min-width:160px}
.sheet-cell--status select{min-width:150px}
.sheet-cell--link input{min-width:200px}
.sheet-cell--f-section input,.sheet-cell--f-reporter input{min-width:130px}
.sheet-cell--f-priority select{min-width:90px}
.sheet-cell--f-defect{min-width:90px}
/* «Действия» остаются на виду при горизонтальной прокрутке (только на широких экранах) */
@media (min-width:900px){
  .sheet-table td.sheet-actions,.sheet-table th.sheet-actions{position:sticky;right:0;background:var(--surface);box-shadow:-1px 0 0 var(--line)}
  .sheet-table th.sheet-actions{background:var(--side);z-index:1}
}
.sheet-col-hidden{opacity:.55}
.sheet-cell.is-saved{box-shadow:inset 0 0 0 2px var(--green)}
.sheet-cell.has-error{box-shadow:inset 0 0 0 2px var(--red)}
.sheet-history-list{margin:4px 0;padding-left:18px;font-size:12px}
.sheet-col-list{list-style:none;padding:0}
.sheet-col-row{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin-bottom:6px}
