/* Палитра каталога. Цвета стояли литералами по всему файлу — один только
   основной синий шестнадцатью вхождениями, из-за чего правка любого оттенка
   означала обход всего файла. Менять палитру не собираемся: токены заведены
   ради одного места правки, а не ради смены темы. Имя описывает роль, потому
   что по роли цвет и ищут; там, где ролей у оттенка две, они перечислены. */
:root{
	--c-text:#454958;          /* основной текст */
	/* Вторичный текст — один на всё (UX_PLAN.md §10). Раньше их было два, и оба
	   не проходили норму контраста 4.5:1 на белом: #a4b6da давал 2.0:1, #7d8bb0
	   — 3.4:1. Нынешний даёт 5.3:1 на белом и 4.6:1 на самой тёмной подложке,
	   на которой он встречается (--c-accent-soft у подсказок и цены клиенту), —
	   то есть проходит везде, где стоит. Темнее делать незачем: тогда он
	   перестанет отличаться от основного, а он именно вторичный. */
	--c-text-second:#5e6b8c;   /* подписи, счётчики, пояснения */
	--c-accent:#133c8b;        /* основной синий: ссылки, кнопки, акценты */
	--c-accent-hover:#e40613;  /* красный ховер */
	--c-accent-soft:#eaedfb;   /* рамки полей и подложка плашек */
	--c-accent-line:#ccd1e6;   /* вторичная кнопка и разделители нижней панели */
	--c-bg-soft:#f8f8fb;       /* подложка шапки таблицы и карточек */
	--c-bg-tint:#f5f7ff;       /* границы ячеек и подсветка по наведению */
	--c-line-th:#f0f1f5;       /* боковые границы шапки таблицы */
	--c-row-active:#d3f5ff;    /* выбранная строка */
	--c-mark:#fffed3;          /* поле или пункт, по которому идёт фильтр */
	/* Лестница надёжности поставки: от красного к зелёному, по 20 шагов
	   рейтинга на ступень. Классы rating-* раздаёт шаблон. */
	--c-rating-1:#ffbcbc;
	--c-rating-2:#ffedbc;
	--c-rating-3:#fffdbc;
	--c-rating-4:#efffbc;
	--c-rating-5:#ccffbc;
}

body, html{width:100%; padding:0; margin:0;  }
html{ font-size:16px; height: 100%;}
body{background: #fff; color:var(--c-text); font:400 16px/1.6 'Inter', sans-serif;}
input, select, textarea, button{background: #fff; padding:.5rem; color:var(--c-text); font:400 16px/1.6 'Inter', sans-serif;}
input, select, textarea{border:1px solid var(--c-accent-soft);}
input:focus, select:focus, textarea:focus{outline:none; border-color:var(--c-accent);}

/* Фокус с клавиатуры (UX_PLAN.md §10). Не был показан нигде: `:focus-visible`
   не встречался в файле ни разу, а `input:focus` менял только цвет рамки —
   кнопки, ссылки и заголовки колонок не показывали фокус ничем.
   Пока по таблице ходили мышью, это была придирка; с клавиатурой из §9 по ней
   ходят стрелками, и человек, ушедший из поля фильтра клавишей, не видел, где
   он.
   Именно `:focus-visible`, а не `:focus`: браузер сам решает, показывать ли
   рамку, и щелчок мышью её не рисует — иначе каждое нажатие на кнопку
   оставляло бы рамку висеть, и первым же желанием было бы её убрать.
   Правило стоит сразу за сбросом выше и перебивает его порядком, а не весом:
   у `input:focus` и `input:focus-visible` вес одинаковый. */
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible,
textarea:focus-visible, summary:focus-visible, [tabindex]:focus-visible{
	outline:2px solid var(--c-accent); outline-offset:1px;}
*{box-sizing: border-box;}
.text-right{text-align: right}
.text-left{text-align: left}
.text-center{text-align: center}
.text-bold{font-weight:bold;}
.text-nowrap{white-space: nowrap;}
.text-second{color:var(--c-text-second);}
a{color:var(--c-accent); transition:all .25s ease;}
a:hover{color:var(--c-accent-hover);}
.icon{display:inline-block; width:16px; height:16px; fill:var(--c-accent);}
.arrow-left{transform: rotate(180deg);}
.form-group{position: relative; display: flex;}
.btn{display: inline-block; cursor:pointer; border:none; padding:.5rem 2rem; text-align: center; transition: background .25s ease; background:var(--c-accent); color:#fff; font-weight: bold; text-decoration: none; }
.btn:hover{background:var(--c-accent-hover); color:#fff;}
.btn-link{background:transparent; border:0; cursor: pointer;}
/* Сброс рамки остаётся только для щелчка мышью: без `:not(:focus-visible)` это
   правило перебивало бы общее правило фокуса выше по весу (класс против
   элемента), и кнопки поиска в шапке таблицы — а их там два десятка — не
   показывали бы фокус по-прежнему. */
.btn-link:focus:not(:focus-visible){outline:none; color:var(--c-accent);}
.btn-second{background:var(--c-accent-line); color:var(--c-accent);}
.btn-second:hover{background:var(--c-accent); color:#fff;}
.col{padding:0 .5rem;}

/* Высоту шапки и первой строки шапки таблицы проставляет main.js: от них
   зависят отступ страницы и позиции липких строк, а раньше это были магические
   числа (75/62/107), из-за которых шапка наезжала на таблицу. */
:root{--header-h:64px; --thead-h:48px; --control-h:36px; --bottom-h:0px;
	/* Ширина карточки строки. Значение по умолчанию — те же 28rem, что стояли
	   в `.row-card` до ручки; растянутое мышью проставляет main.js сюда же
	   (UX_PLAN.md §8.8). */
	--card-w:28rem;}

body{padding-top:var(--header-h);}
main{min-height:80vh;}
#header{position: fixed; top:0; background: #fff; width:100%; left:0; padding:.5rem 0; z-index: 20; box-shadow:0 1px 0 var(--c-accent-soft);}
#header.fixed{z-index: 10; opacity: 10;}
.header-logo{display:flex; align-items:center; flex:0 0 auto;}
.header-logo svg, .header-logo img{width: 60px; height:35px;}

.header-user_link .icon{ margin-right:6px; position: relative; top:3px; }
.header-user_link-name{font-weight: bold; color:var(--c-accent);}
.header-user_link:hover .header-user_link-name{color:var(--c-accent-hover);}
.header-user_link:hover .icon{fill:var(--c-accent-hover); }

.js-color.rating-5{background: var(--c-rating-1)}
.js-color.rating-10{background: var(--c-rating-1)}
.js-color.rating-15{background: var(--c-rating-1)}
.js-color.rating-20{background: var(--c-rating-1)}

.js-color.rating-25{background: var(--c-rating-2)}
.js-color.rating-30{background: var(--c-rating-2)}
.js-color.rating-35{background: var(--c-rating-2)}
.js-color.rating-40{background: var(--c-rating-2)}

.js-color.rating-45{background: var(--c-rating-3)}
.js-color.rating-50{background: var(--c-rating-3)}
.js-color.rating-55{background: var(--c-rating-3)}
.js-color.rating-60{background: var(--c-rating-3)}

.js-color.rating-65{background: var(--c-rating-4)}
.js-color.rating-70{background: var(--c-rating-4)}
.js-color.rating-75{background: var(--c-rating-4)}
.js-color.rating-80{background: var(--c-rating-4)}

.js-color.rating-85{background: var(--c-rating-5)}
.js-color.rating-90{background: var(--c-rating-5)}
.js-color.rating-95{background: var(--c-rating-5)}
.js-color.rating-100{background: var(--c-rating-5)}

.dropdown{position: relative; cursor:pointer;}
.dropdown-block{display:none; padding:0; position: absolute; text-align: left; background: #fff; top:100%; z-index: 10; left:0; box-shadow: 0 0 50px rgba(0,0,0,.1); left:0;}
.dropdown.is_active .dropdown-block{display: flex; flex-direction: column;}
/* .logout-button обёрнут в свою <form>; display:contents делает форму
   прозрачной для раскладки, чтобы кнопка стояла в общей колонке пунктов меню. */
.dropdown-block .logout-form{display: contents;}
.dropdown-block a, .dropdown-block .logout-button{display: block; text-decoration: none; padding:1rem; white-space: nowrap; max-width:200px; border-bottom:1px solid var(--c-accent-soft);}
.dropdown-block a:hover, .dropdown-block .logout-button:hover{background:var(--c-bg-tint);}
.dropdown-block a:last-child, .dropdown-block .logout-button:last-child{border:0;}
/* Кнопка выхода: сброс стилей браузера, чтобы не отличалась от соседних ссылок */
.dropdown-block .logout-button{width:100%; text-align:left; background:none; border-left:0; border-right:0; border-top:0; font:inherit; color:inherit; cursor:pointer;}
.text-right .dropdown-block{left:auto; right:0;}

.input-group{display: -ms-flexbox; display: -webkit-box; display: flex; align-items: center;}
.input-group span{padding:0 .5rem; font-weight: normal}
.input-group input{padding:.2rem .5rem!important; min-width:68px;}
.input-group .btn-link{position: relative!important;}

input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {-webkit-appearance: none;}

input[type='number'] { -moz-appearance: textfield;}

.table-responsive { margin-bottom:3rem;}
table {border-collapse: separate; border-spacing: 0; position: relative; width:100%;}
table tr{transition: all .25s ease; }
table td, table th{border:1px solid var(--c-bg-tint); padding:.5rem; line-height: 1.2;}
table tr:hover{ box-shadow: 0 0 60px rgba(0,0,0,.05) inset;}

thead th{position: sticky; top:var(--header-h); padding:0 .5rem; z-index: 2; background: var(--c-bg-soft);}
thead tr:first-child{height:48px;}
thead tr:first-child th{padding-bottom:0;}
thead th{border-left-color:var(--c-line-th); border-right-color:var(--c-line-th);}
thead tr:hover{box-shadow:none;}
thead .search-row th{top:calc(var(--header-h) + var(--thead-h)); padding-bottom:.5rem;}
/* Поле фильтра в шапке таблицы. Класс .form-control стоял в разметке с самого
   начала и не был определён нигде: поля выглядели прилично только потому, что
   те же свойства раздавали селекторы по элементу — `thead input, thead select`.
   Те цепляли заодно и галочки «искать по этой колонке» из первой строки шапки,
   для которых ничего из этого не предназначалось. Теперь стили висят на
   классе, а элемент в селекторе означает ровно то, что написано.
   Подложка прозрачная нарочно: поле сливается с шапкой и проявляется по
   наведению на ячейку (правило ниже) — иначе двадцать белых прямоугольников
   спорят за внимание с самой выдачей. */
.form-control{display:inline-block; width:100%; padding:.2rem .5rem;
	background:transparent; font-size:14px; font-weight:400; line-height:1.3;}
/* Справа в поле стоит кнопка поиска — 32px под неё. Селекту отступ не нужен:
   там на этом месте стрелка, которую рисует браузер. */
input.form-control{line-height:1.6; padding-right:32px;}

/* Галочка «искать по этой колонке» под подписью заголовка. Ширину ей раньше
   давало общее правило `thead input`, заодно с полями фильтров: сама галочка от
   этого не растягивается — Chromium рисует её в своём размере, — но щёлкнуть
   можно в любом месте под подписью, и попадать так заметно легче. Правило
   заведено отдельно, чтобы удобство не пропало вместе с общим селектором. */
.td-title input[type="checkbox"]{width:100%;}

thead .search-row th:hover .form-control{background: #fff;}
thead .btn-link, .search-form .btn-link{opacity: .2; padding:0 .5rem; position: absolute; top:0; right:0; bottom:0; width:32px; height:30px; margin:auto;}
thead .btn-link:hover, .search-form .btn-link:hover,
/* Кнопка нарисована вполсилы и проявляется по наведению; с клавиатуры наводить
   нечем, и без этой строки фокус вставал бы на почти невидимую кнопку. */
thead .btn-link:focus-visible, .search-form .btn-link:focus-visible{opacity: 1;}
thead .btn-link .icon, .search-form .btn-link .icon{width:14px; height:14px;}
tr.is_active, tr.is_active:hover{background: var(--c-row-active);}
thead .small-text{font-size:10px; display: block;}
.td-title{min-width:100px!important;}
.select{position: relative; font-size:14px; font-weight:400;}
.select-link{white-space:nowrap; overflow: hidden; padding-right:20px; position: relative; border: 1px solid var(--c-accent-soft); line-height: 1.6; cursor: pointer; display: block;  height:32px; padding: .3rem; padding-right:20px;}
.select-link:hover{background: #fff;}
.select-link:after{content:''; display: block; position: absolute; top:2px; right:5px; bottom:0; margin:auto; width:0; height:0; border:3px solid transparent; border-top:7px solid var(--c-text)}
.select-link:before{content:''; display: block; position: absolute; top:0; right:0; bottom:0;  background: #fff; margin:auto; width:20px; height:100%;}
.select-list{position:absolute; z-index: 10; display: none; flex-direction: column; top:100%; left:0; list-style: none; text-align: left; margin:0; padding:.5rem 0; background: #fff; box-shadow: 0 10px 40px rgba(0,0,0,.1)}
.select:focus .select-list{display: flex; }
.select:focus .select-link{border-color:var(--c-accent);  outline:none;}
.select-option{white-space: nowrap; cursor:pointer; display: block; padding:.5rem 1rem; text-decoration: none; border-bottom:1px solid var(--c-bg-soft)}
.select-option .icon{position: relative; top:3px; margin-right:6px;}
.select-option:hover{background:var(--c-bg-soft); color:var(--c-accent-hover);}
.select-option:hover .icon{fill:var(--c-accent-hover)}
.select-list li:last-child a{border-bottom:0;}
.in_search{background: var(--c-mark)}

/* display:contents убирает форму из раскладки, чтобы .search-bar_fields и
   .search-bar_flags вели себя как прямые flex-элементы .search-bar (ниже),
   хоть и лежат внутри <form>. */
.search-form{position: relative; display: contents;}
.search-form input:focus{border-color:var(--c-accent);}
.input-mark{background:var(--c-mark)!important;}

/* Строка поиска в шапке. Раньше это была сетка flexboxgrid: селекты не влезали
   в свои колонки и наезжали друг на друга, а высоты полей (44/43/40) не
   совпадали. Теперь одна флекс-строка с общим шагом и общей высотой контролов. */
.search-bar{display:flex; align-items:center; flex-wrap:wrap; gap:.5rem .75rem;}
.search-bar_fields{display:flex; align-items:center; flex-wrap:wrap; gap:.5rem; flex:1 1 22rem; min-width:0;}
/* Галочки — сеткой в две колонки: так блок всегда занимает две ровные строки
   и не растягивает шапку в зависимости от длины подписей. */
.search-bar_flags{display:grid; grid-template-columns:repeat(2, max-content); align-content:center; gap:.25rem 1rem; flex:0 1 auto;}
#header .header-user{margin-left:auto; white-space:nowrap;}

.search-field{position:relative; flex:1 1 11rem; min-width:9rem; max-width:18rem; margin:0;}
#header input[type="text"], #header select{width:100%; height:var(--control-h); padding:.25rem .5rem; border:1px solid var(--c-accent-soft); background:#fff; font-size:14px; line-height:1.2; text-overflow:ellipsis;}
#header .search-field input{padding-right:30px;}
#header select{width:auto; flex:0 1 auto; min-width:7rem; max-width:10rem; cursor:pointer;}
/* Селект в шапке, по которому идёт отбор, — подсвечен той же меткой, что поля
   фильтров. Класс раздаёт syncFiltersFromUrl, и раздавал всегда, но в шапке
   подсветка не появлялась ни разу: общее `.in_search` (0,1,0) проигрывает
   правилу выше (1,0,1), и его белая подложка перекрывала метку. Видно это
   стало на умолчании по типу (§7.2) — селект, молча отфильтровавший половину
   каталога, обязан выглядеть занятым. Заодно метку получил и «Сортировка». */
#header select.in_search{background:var(--c-mark);}
#header select[name="sort"]{min-width:9rem; max-width:11.5rem;}
#header .btn-link{position:absolute; top:0; right:0; bottom:0; width:30px; height:auto; padding:0; margin:0; opacity:.35;}
#header .btn-link:hover, #header .btn-link:focus-visible{opacity:1;}

.checkbox-label{display:flex; align-items:center; gap:.35rem; margin:0; line-height:1.2; font-size:13px; white-space:nowrap; cursor:pointer;}
.checkbox-label input[type="checkbox"]{flex:0 0 auto; width:14px; height:14px; margin:0; padding:0;}


.login{min-height:80vh;}

.card{background: var(--c-bg-soft); padding:1rem;}
.card-header{font-size:2rem;}
.card .form-group, .card .button-group{padding:1rem 0;}
.card .form-group input{padding:.5rem; border: 1px solid var(--c-accent-soft); width:100%;}
/* Тот же .form-control стоит и на полях форм входа: класс в проекте один, а
   ставит его фильтр addclass в шаблонах. Без этого правила поле формы получило
   бы вид фильтра в шапке таблицы — мелкий шрифт и прозрачную подложку, из-за
   которой на сером фоне карточки поля не видно вовсе. */
.card .form-control{padding:.5rem; background:#fff; font-size:1rem; line-height:1.4;}
/* Подпись поля. Раньше на этом месте стоял col-form-label из bootstrap,
   которого в проекте нет, и подпись прижималась к верху своей колонки. */
.form-label{display:block; padding:.5rem 0;}
.required{margin-left:.15rem; color:var(--c-accent-hover);}
/* Пояснение под полем: требования к паролю, формат имени. */
.form-text{display:block; margin-top:.25rem; font-size:.8125rem; color:var(--c-text-second);}

.alert{padding:.5rem .75rem; margin-bottom:1rem;}
.alert-info{background:var(--c-accent-soft);}
.alert-danger{color:var(--c-accent-hover);}
/* Список ошибок формы Django приходит разметкой <ul>, а маркеры и отступ
   браузера здесь только мешают: это одна-две строки внутри плашки. */
.alert ul{margin:0; padding:0; list-style:none;}

.pagination{display: -ms-flexbox; display: -webkit-box; display: flex; align-items:center; padding:0; list-style: none;}
.pagination li{display: block; }
.pagination li .page-link {text-decoration: none; display: block; text-align: center; padding:0 3px; min-width:30px; height:30px; line-height: 30px;}
.pagination li a:hover{background:var(--c-bg-tint)}
.pagination .active a, .pagination .active a:hover{background:var(--c-accent); color:#fff;}
.pagination .icon{vertical-align: middle; width:14px; height:14px; margin-top:-2px;}
.page-item{margin:0 2px;}
.page-link_disable{opacity: .5;}

.bottom-block{position: fixed; width:100%; padding:.5rem 0; left:0; bottom:0; background: #fff; z-index: 10; border-top:2px solid var(--c-accent);}

/* Панель прибита к нижнему краю и раньше накрывала последние строки таблицы:
   компенсации её высоты не было вовсе. Высоту меряет main.js — она зависит от
   того, перенеслась ли строка на узком экране. */
body{padding-bottom:var(--bottom-h);}

.bottom-bar{display:flex; align-items:center; flex-wrap:wrap; gap:.35rem 0;}
/* Сброс — маленькой кнопкой сразу за страницами: это соседнее по смыслу
   действие («уйти с этой выдачи»), а прежняя широкая кнопка по центру занимала
   треть панели. */
.bottom-bar_reset{padding:.15rem .9rem; font-size:.875rem;}
.bottom-bar_group{display:flex; align-items:center; flex-wrap:wrap; gap:0 .5rem;}
/* Мелкие прописные с разрядкой — самая тонкая подпись в панели; после сведения
   двух цветов вторичного текста в один (§10) она набрана тем же, что и всё
   остальное вторичное, и наконец читается. */
.bottom-bar_caption{color:var(--c-text-second); font-size:.75rem; text-transform:uppercase; letter-spacing:.04em;}
/* Разделитель между группами — всем, кроме первой. Так его не приходится
   заводить в разметке и держать в уме, какой блок сегодня показан: панель
   собирается из разного набора групп (цены есть не у всех прав, оценка — не у
   всех групп), а линия всегда оказывается ровно между соседями.
   Рисуется псевдоэлементом, а не границей блока: у блока с ценой своя
   подложка и своя высота, и граница получилась бы короче остальных линий. */
.bottom-bar > * + *{position:relative; margin-left:1.5rem;}
.bottom-bar > * + *::before{content:''; position:absolute; left:-.75rem; top:50%; width:1px; height:1.75rem; margin-top:-.875rem; background:var(--c-accent-line);}
/* Счётчик всегда у правого края, что бы ни выросло слева. Правило идёт после
   разделителей намеренно: вес у селекторов одинаковый, и margin-left:auto
   должен перебить их отступ, иначе счётчик отъедет от края. */
.bottom-bar_count{display:flex; align-items:center; margin-left:auto; font-size:.875rem;}

/* Диалог «Колонки» (UX_PLAN.md §6.4). Собран на <details>: показать и спрятать
   список — ровно то, что этот элемент умеет сам, и заводить ради этого свой
   обработчик, который пришлось бы поднимать заново после каждой подмены
   таблицы, незачем.
   Панель раскрывается вверх: нижняя панель прибита к краю экрана, и вниз ей
   некуда. */
.columns-picker{position:relative;}
.columns-picker_button{display:inline-block; list-style:none; user-select:none;}
/* Треугольник-маркер <summary> в Chromium рисуется псевдоэлементом, а в
   остальных браузерах — list-style; кнопке он не нужен ни там, ни там. */
.columns-picker_button::-webkit-details-marker{display:none;}
.columns-picker_panel{position:absolute; left:0; bottom:calc(100% + .5rem); z-index:20;
	width:26rem; max-height:60vh; overflow-y:auto; overflow-x:hidden; padding:.75rem;
	background:#fff; border:1px solid var(--c-accent-line);
	box-shadow:0 4px 24px rgba(0,0,0,.15);}
/* Подпись с нормализованным кодом (MOTORS_PLAN.md §14.4) — пункт над списком
   и за чертой: галочки ниже включают и выключают колонки, а эта — вторую
   строку внутри одной из них. Ширина списка поделена на два столбца, и, стой
   пункт в нём, он уехал бы в середину панели, где читался бы как двадцать
   шестая колонка. */
.columns-picker_option{align-items:flex-start; white-space:normal;
	margin:0 0 .5rem; padding:0 0 .5rem; font-size:.8125rem; line-height:1.25;
	border-bottom:1px solid var(--c-accent-soft);}
.columns-picker_option input[type="checkbox"]{margin-top:.1rem;}
.columns-picker_list{margin:0 0 .75rem; padding:0; list-style:none;
	/* Двадцать пять колонок в один столбец не помещаются на экран ноутбука, а
	   список, за которым надо прокручивать, ровно та же потеря времени, от
	   которой мы уходим. */
	columns:2; column-gap:1rem;}
.columns-picker_item{break-inside:avoid; margin:0;}
/* .checkbox-label шапки запрещает перенос — там это верно, подписи короткие и
   стоят в одну строку. Здесь подпись бывает с пометкой «пусто для …», и без
   переноса та вылезает в соседний столбец списка. */
.columns-picker_item .checkbox-label{align-items:flex-start; white-space:normal;
	font-size:.8125rem; line-height:1.25; padding:.15rem 0;}
.columns-picker_item .checkbox-label input[type="checkbox"]{margin-top:.1rem;}
.columns-picker_note{display:block; color:var(--c-text-second); font-size:.75rem;}
.columns-picker_actions{display:flex; align-items:center; flex-wrap:wrap; gap:.5rem;}
.columns-picker_actions .btn{padding:.25rem .9rem; font-size:.8125rem;}
/* «По умолчанию» — не кнопка, а ссылка: действие редкое и обратимое, и рядом с
   двумя кнопками третья такая же заставляла бы выбирать из трёх равных. */
.columns-picker_reset{margin-left:auto; padding:0; border:0; background:none;
	color:var(--c-text-second); font-size:.8125rem; text-decoration:underline;
	cursor:pointer;}
.columns-picker_reset:hover{color:var(--c-accent);}

/* Карточка строки (UX_PLAN.md §8). Панель у правого края, а не модальное окно:
   менеджер сравнивает открытую строку с соседними, и затемнять ради этого
   таблицу незачем — поэтому ни подложки во весь экран, ни ловушки фокуса.
   Обёртка пуста, пока карточку не открыли; :empty и прячет её — иначе полоса
   панели висела бы над выдачей всегда. Пустой она становится и при закрытии:
   main.js стирает содержимое, а не переключает класс. */
#row-card:empty{display:none;}
/* Строка выдачи кликабельна — курсором это и показано. Селектор по атрибуту, а
   не по классу: карточка предлагается только персоналу, и hx-get на строке
   стоит ровно там, где она есть. */
tbody tr[hx-get]{cursor:pointer;}
.row-card{position:fixed; right:0; top:var(--header-h); bottom:var(--bottom-h);
	z-index:15; width:var(--card-w); max-width:100%; overflow-y:auto; padding:.75rem 1rem;
	background:#fff; border-left:2px solid var(--c-accent);
	box-shadow:-4px 0 24px rgba(0,0,0,.12);}
.row-card:focus{outline:none;}
/* Ручка ширины карточки (UX_PLAN.md §8.8). Полоса стоит на левой границе
   панели: `right:var(--card-w)` — это ровно её край, потому что сама панель
   прижата к правому краю окна и той же шириной. Позиция fixed, а не absolute
   внутри карточки: карточка прокручивается (`overflow-y:auto`), и вложенная
   ручка уезжала бы вместе с содержимым.
   Ширина 7 пикселей при видимой линии в 2 — попасть в двухпиксельную границу
   мышью нельзя; margin сдвигает полосу так, чтобы она легла на границу, а не
   рядом с ней. */
.row-card_grip{position:fixed; top:var(--header-h); bottom:var(--bottom-h);
	right:var(--card-w); z-index:16; width:7px; margin-right:-3px;
	cursor:col-resize; background:transparent;}
.row-card_grip:hover, .row-card_grip:focus-visible{background:var(--c-accent-soft);}
.row-card_grip:focus-visible{outline:2px solid var(--c-accent); outline-offset:0;}
/* Пока тянут — выделять текст нельзя: протяжка по таблице иначе выделяет
   ячейки, мимо которых прошёл курсор. */
body.is-card-resizing{-webkit-user-select:none; user-select:none;}
/* Курсор — на всём, что под ним: у строк выдачи свой (`cursor:pointer`), и без
   этого правила он менялся бы прямо посреди протяжки. */
body.is-card-resizing, body.is-card-resizing *,
body.is-col-resizing, body.is-col-resizing *{cursor:col-resize;}
.row-card_head{display:flex; align-items:center; flex-wrap:wrap; gap:.5rem;
	margin-bottom:.75rem; padding-bottom:.5rem;
	border-bottom:1px solid var(--c-accent-soft);}
.row-card_num{color:var(--c-text-second); font-size:.8125rem;}
.row-card_code{color:var(--c-accent); font-size:1.0625rem; font-weight:bold;
	overflow-wrap:anywhere;}
.row-card_copy{padding:.1rem .4rem; border:1px solid var(--c-accent-line);
	background:#fff; color:var(--c-accent); font-size:.75rem; cursor:pointer;}
.row-card_copy:hover{background:var(--c-accent-soft);}
.row-card_close{margin-left:auto; padding:.2rem; border:0; background:none;
	line-height:0; cursor:pointer;}
.row-card_close:hover .icon{fill:var(--c-accent-hover);}
/* Подпись слева, значение справа. Ширина подписи ограничена сверху и снизу:
   «Надежность поставки» иначе забирает половину панели у самих значений. */
.row-card_fields{display:grid; grid-template-columns:minmax(6rem,9rem) 1fr;
	gap:.2rem .75rem; margin:0;}
.row-card_fields dt{color:var(--c-text-second); font-size:.8125rem;
	line-height:1.3;}
/* Переносится где угодно нарочно: карточка затем и заведена, чтобы прочитать
   значение целиком, а «Информация» бывает в несколько тысяч знаков — обрезка
   вернула бы ровно то, от чего уходим. */
.row-card_fields dd{margin:0; font-size:.875rem; line-height:1.3;
	overflow-wrap:anywhere;}
.row-card_supplier{margin:.75rem 0 0; font-size:.875rem;}
/* Цена клиенту для этой строки (UX_PLAN.md §9). Набрана крупно и на подложке —
   как оценка в нижней панели, и по той же причине: это единственное число в
   карточке, которое менеджер произносит вслух, остальные он читает. */
.row-card_price{display:flex; align-items:center; flex-wrap:wrap; gap:.35rem .5rem;
	margin:.75rem 0 0; padding:.3rem .5rem; background:var(--c-accent-soft);}
.row-card_estimate{padding:.15rem .6rem; border:1px solid var(--c-accent-line);
	background:#fff; color:var(--c-accent); font-size:.8125rem; cursor:pointer;}
.row-card_estimate:hover{background:#fff; color:var(--c-accent-hover);}
.row-card_estimate[disabled]{opacity:.6; cursor:default;}
.row-card_price-value{color:var(--c-accent); font-size:1.125rem; font-weight:bold;
	line-height:1.2; white-space:nowrap;}
/* «Цены нет» и «оценка недоступна» встают на место числа: это ответ, а не
   поломка, но и не цифра — крупным на подложке они читались бы как цена. */
.row-card_price-value.is-empty{color:var(--c-text-second); font-size:.875rem;
	font-weight:normal;}
/* Марка и код, за которые посчитана цена. Показываются вместе с ответом и
   всегда: цена без них — число неизвестно о чём. */
.row-card_price-note{color:var(--c-text-second); font-size:.75rem;}
/* Блоки про мотор, а не про строку: справочник и база знаний. Отделены от полей
   записи чертой — это ответ на другой вопрос, «что это за мотор» вместо «что
   предлагает поставщик». */
.row-card_ref, .row-card_kb{margin-top:1rem; padding-top:.5rem;
	border-top:1px solid var(--c-accent-soft);}
.row-card_ref-line{margin:0 0 .25rem; font-size:.875rem; line-height:1.35;}
.row-card_ref-code{font-weight:bold; color:var(--c-accent);}
.row-card_ref-analog{font-weight:bold; white-space:nowrap;}
.row-card_subhead{margin:0 0 .35rem; color:var(--c-text-second);
	font-size:.75rem; font-weight:normal; text-transform:uppercase;
	letter-spacing:.04em;}
.row-card_kb-link{margin:0 0 .5rem; font-size:.9375rem; font-weight:bold;}
/* «Особенности и описание» из базы знаний — размеченный текст из TinyMCE, а не
   строка (UX_PLAN.md §8.8). Отступы у своих же абзацев и списков заданы здесь:
   в редакторе их не видно, а по умолчанию `<p>` даёт 1em сверху и снизу, и
   короткое описание в три абзаца растягивало бы карточку вдвое. */
.row-card_kb-text{font-size:.875rem; line-height:1.4; overflow-wrap:anywhere;}
.row-card_kb-text > :first-child{margin-top:0;}
.row-card_kb-text > :last-child{margin-bottom:0;}
.row-card_kb-text p, .row-card_kb-text ul, .row-card_kb-text ol,
.row-card_kb-text table{margin:0 0 .5rem;}
.row-card_kb-text ul, .row-card_kb-text ol{padding-left:1.2rem;}
.row-card_kb-text h1, .row-card_kb-text h2, .row-card_kb-text h3,
.row-card_kb-text h4{margin:.6rem 0 .3rem; font-size:.9375rem;}
/* Картинка в описании бывает шире панели, а панель узкая по построению. */
.row-card_kb-text img{max-width:100%; height:auto;}
.row-card_kb-text table{display:block; max-width:100%; overflow-x:auto;}
.row-card_note{margin:0; color:var(--c-text-second); font-size:.875rem;}

.total-price{padding:0 .25rem; font-size:.875rem;}

/* Спарклайн распределения цен рядом с мин/ср/макс. Три числа не показывают
   формы: выдача по одному мотору почти всегда двугорбая, и среднее падает в
   провал между горбами, где предложений нет ни одного. Столбик — корзина цен,
   его высота — сколько в неё попало; наведение подписывает и то и другое.
   Пустая корзина остаётся засечкой в пиксель другого цвета, иначе провал
   читался бы как конец шкалы. */
.price-spark{position:relative; display:flex; margin-left:.5rem;}
.price-spark_plot{display:block; cursor:crosshair;}
.price-spark_bar{fill:var(--c-accent);}
.price-spark_bar-empty{fill:var(--c-accent-line);}
/* Столбик под курсором и столбики в протяжке — красным, тем же, которым здесь
   отвечает на наведение всё остальное. */
.price-spark_bar-hot{fill:var(--c-accent-hover);}
/* Подложка выделения. Полупрозрачная и без обводки: рамка на пятипиксельных
   столбиках съедала бы сам столбик. */
.price-spark_band{fill:var(--c-accent); opacity:.16;}

/* Подсказка стоит над графиком и вынута из потока: панель прижата к нижнему
   краю экрана, и подсказка под курсором ушла бы за него. Абсолютной она
   сделана ещё и затем, чтобы её появление не раздвигало панель — иначе
   раскладка дёргалась бы на каждом движении мыши. */
.price-spark_tip{position:absolute; left:0; bottom:100%; z-index:1;
	margin-bottom:.35rem; padding:.15rem .5rem; background:var(--c-accent);
	color:#fff; font-size:.75rem; line-height:1.5; white-space:nowrap;
	pointer-events:none;}
.price-spark_tip[hidden]{display:none;}

/* Цена для клиента. В отличие от закупочных цифр это не справка по выдаче, а
   число, которое менеджер произносит вслух, — поэтому оно единственное здесь
   набрано крупно и вынесено на подложку. */
.motor-price{display:flex; align-items:center; gap:.4rem; padding:.15rem .6rem; background:var(--c-accent-soft);}
/* Обязательно: display у класса сильнее, чем [hidden] в стилях браузера, и без
   этой строки пустая плашка висела бы в панели до ответа API — то есть всегда,
   когда его нет. */
.motor-price[hidden]{display:none;}
.motor-price_select{max-width:11rem; padding:.1rem .2rem; font-size:.8125rem; background:#fff; border-color:var(--c-accent-line);}
.motor-price_label{color:var(--c-accent); font-size:.75rem; text-transform:uppercase; letter-spacing:.04em;}
.motor-price_value{color:var(--c-accent); font-size:1.25rem; font-weight:bold; line-height:1.2; white-space:nowrap;}
.motor-price_note{color:var(--c-text-second); font-size:.8125rem;}
.motor-price.is-loading .motor-price_value{opacity:.35;}

/* Подсказка с аналогами на пустой выдаче. Стоит над пустой таблицей и должна
   читаться как ответ поиска, а не как ошибка: подложка та же, что у цены для
   клиента, — это тоже подсказка к продаже, а не сообщение о неудаче. */
.motor-hint{margin:.5rem 0; padding:.5rem .75rem; background:var(--c-accent-soft);}
.motor-hint p{margin:0;}
.motor-hint_head{font-size:.9375rem;}
.motor-hint_code{font-weight:bold; font-size:1.0625rem; color:var(--c-accent);}
.motor-hint_none{color:var(--c-text-second);}
.motor-hint_analogs{margin-top:.35rem !important; font-size:.9375rem; line-height:1.7;}
/* Ссылкой становится только код, по которому есть что показать; остальные
   набраны тускло и намеренно не кликабельны — вести на заведомо пустую выдачу
   значит обмануть дважды. */
.motor-hint_offer{font-weight:bold; white-space:nowrap;}
.motor-hint_empty{color:var(--c-text-second); white-space:nowrap;}
/* Заметка по строкам мотора (MOTOR_DIFF_PLAN.md §9). Своего языка оформления
   она не заводит: та же подложка и тот же кегль, что у подсказок по моторам, —
   это такая же строка над таблицей, только про найденное, а не про пустое.
   Ширина ограничена: заметка в две-три фразы, растянутая на широкий монитор,
   читается хуже абзаца нормальной длины. */
.motor-hint_diff .motor-hint_head{max-width:70rem;}
.motor-hint_note{max-width:70rem;}
/* Наблюдение — одно на строку (§17.6). Их два-три, и они о разных вещах;
   склеенные в абзац, они читались сплошным текстом, в котором не видно, где
   кончается одно и начинается другое. Отступ между ними меньше строки: это
   по-прежнему одна заметка, а не список из трёх пунктов. */
.motor-hint_line{font-size:.9375rem;}
.motor-hint_line + .motor-hint_line{margin-top:.3rem !important;}
/* Спойлер со всеми посчитанными фактами (§13.3). Заметка сверху отвечает на
   «что тут вообще есть», список под спойлером — на «а что ещё посчитали»;
   закрытый он стоит одной строки. Набран ссылкой, а не заголовком, потому что
   ссылка и есть — по ней щёлкают. */
.motor-hint_ask{cursor:pointer; color:var(--c-accent); font-size:.875rem;
	width:fit-content; margin-top:.35rem;}
.motor-hint_ask:hover{color:var(--c-accent-hover);}
.motor-hint_ask::marker{color:var(--c-accent-line);}
.motor-hint_facts{margin:.35rem 0 0; padding-left:1.25rem; font-size:.875rem;
	line-height:1.6; max-width:70rem;}
/* Факты, из которых собрана заметка, — тем же синим, что и остальные акценты:
   видно, что она не с потолка, а из этого же списка. */
.motor-hint_facts .is-used{color:var(--c-accent);}
#motor-diff[hidden]{display:none;}
/* Кнопка «Проанализировать» и «Пересчитать» (§17). Платный прогон уходит по
   нажатию, и кнопка обязана быть заметной — но это по-прежнему строка
   подсказки над таблицей, а не призыв во весь экран: кегль и отступы меньше,
   чем у `.btn`, и рядом с ней помещается подпись, а не только она сама. */
.motor-hint_run{margin-top:.35rem !important; display:flex; flex-wrap:wrap;
	align-items:baseline; gap:.5rem; max-width:70rem;}
/* Собственный `display` сильнее атрибута `hidden`: без этой строки готовая
   заметка выходила со строкой «считается» под собой — та пряталась атрибутом,
   а флексом возвращалась обратно. Тем же правилом закрыт и сам блок выше. */
.motor-hint_run[hidden]{display:none;}
.motor-hint_button{border:0; cursor:pointer; padding:.25rem .75rem;
	background:var(--c-accent); color:#fff; font-size:.875rem; font-weight:bold;
	transition:background .25s ease;}
.motor-hint_button:hover{background:var(--c-accent-hover);}
/* Нажата, но ответа ещё нет. Второй щелчок ничего не добавит: отметку очереди
   ставит сервер, и до его ответа два нажатия выглядели бы как два расчёта. */
.motor-hint_button:disabled{background:var(--c-accent-line);
	color:var(--c-text-second); cursor:default;}
/* Что именно посчитают и по каким строкам — подпись к кнопке, а не вторая
   кнопка: тем же тусклым и тем же кеглем, что строка источника под заметкой. */
.motor-hint_why{color:var(--c-text-second); font-size:.8125rem;}
/* Откуда взялось. Мелко и тускло — но сказано всегда: числа посчитаны по
   каталогу, а формулировку подбирала модель, и выдавать её за слова человека
   нельзя. */
.motor-hint_source{margin-top:.35rem !important; color:var(--c-text-second);
	font-size:.75rem;}

/* Колонка «Модель ДВС» в строке выдачи. Размеры переехали сюда из атрибута
   style, чтобы строке, найденной не по набранному коду, можно было дать чуть
   больше места: рядом с кодом у неё стоит пометка — «аналог K20A3» (§5) или
   «код записан коротко» (§7б). */
.td-model_dvs-cell{max-height:37px; max-width:180px; overflow:hidden;
	text-overflow:ellipsis; white-space:nowrap;}
.td-model_dvs-cell.is-analog{max-width:300px;}
/* Строка с нормализованным кодом (MOTORS_PLAN.md §14) занимает вторую строку
   ячейки — потолок высоты приходится снять, иначе она обрежется по половине
   букв. Растут при этом только те строки выдачи, где эта подпись есть: замер
   на боевом каталоге — 17% строк, около восьми на странице из полусотни.
   Обрезка переезжает с ячейки на обе её строки: многоточие рисует только тот
   блок, у которого своя overflow, а у безымянного блока вокруг текста ячейки
   её взять неоткуда. */
.td-model_dvs-cell.has-norm{max-height:none;}
.td-dvs, .td-norm{display:block; overflow:hidden; text-overflow:ellipsis;
	white-space:nowrap;}
/* Тускло и мельче кода, но не мельче, чем читается: это подпись к строке
   выше, а не сама строка. Полсотни таких подписей на экране обязаны уходить
   на второй план — иначе выдача читается как два столбца кодов вперемешку. */
.td-norm{color:var(--c-text-second); font-size:.75rem; line-height:1.35;}
/* Значок «скопировать» в ячейке с кодом (UX_PLAN.md §9). Место под него занято
   всегда, а виден он вполсилы: строк на экране полсотни, и полсотни ярких
   значков спорили бы с самой выдачей. Высоту строки он не трогает — 14px при
   line-height 1.2 от 16px шрифта, то есть внутри уже занятых 19. */
.td-copy{display:inline-block; width:14px; height:14px; padding:0; margin-left:.25rem;
	border:0; background:none; line-height:0; vertical-align:-2px; opacity:.25;
	cursor:pointer; transition:opacity .15s ease;}
.td-copy .icon{width:11px; height:11px;}
tr:hover .td-copy{opacity:.6;}
.td-copy:hover, tr:hover .td-copy:hover, .td-copy:focus-visible{opacity:1;}
.td-copy.is-copied{opacity:1;}
.td-copy.is-copied .icon{fill:var(--c-accent);}
.td-copy.is-failed{opacity:1;}
.td-copy.is-failed .icon{fill:var(--c-accent-hover);}
/* Обрезка длинных значений в ячейках выдачи. Прежде это был атрибут style,
   выписанный в каждой такой ячейке отдельно, — то есть ширина колонки
   правилась в семи местах разметки и в двух ветках сразу.
   Селектор начинается с `td` нарочно: те же имена классов стоят на
   заголовках колонок и на полях фильтров, а им обрезка не нужна. */
td.td-name, td.td-information, td.td-body,
td.td-number_oem, td.td-number_dvs, td.td-vin_code{max-height:37px;
	overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
td.td-name{max-width:230px;}
td.td-information{max-width:250px;}
td.td-body{max-width:270px;}
td.td-number_oem, td.td-number_dvs{max-width:140px;}
td.td-vin_code{max-width:200px;}
/* Дате нужна не обрезка, а наоборот — запас ширины: «9 августа 2026 г.» иначе
   переносится на вторую строку и растит всю строку выдачи с 37 до 56 пикселей.
   Раньше это делала обёртка <div style="min-width:140px"> внутри ячейки, и 140
   приходились на содержимое; здесь то же число названо для ячейки целиком —
   отсюда 158 = 140 + отступы .5rem с двух сторон + рамки. Перенос запрещён
   отдельно: он, а не ширина, и есть то, от чего колонку берегут. */
td.td-date_placement{min-width:158px; white-space:nowrap;}

/* Заголовок сортируемой колонки — ссылка (UX_PLAN.md §5.4). Цвет подписи
   остаётся прежним: синим горит только та колонка, по которой выдача сейчас
   отсортирована. Гори все четыре без повода — заголовки таблицы читались бы
   как набор ссылок, а не как названия колонок.
   Жирности у отсортированной колонки нет нарочно: она меняет ширину подписи,
   а колонки менеджер расставляет руками. */
.sort-link{display:block; color:inherit; text-decoration:none;}
.sort-link:hover{color:var(--c-accent);}
.sort-link.is-sorted{color:var(--c-accent);}
/* Стрелка — тот же шеврон, что у страниц: вправо, поэтому её разворачивают. */
.sort-arrow{width:11px; height:11px; margin-left:.2rem; vertical-align:middle;}
.sort-arrow_asc{transform:rotate(-90deg);}
.sort-arrow_desc{transform:rotate(90deg);}

/* Индикатор запроса (§5.3). Раньше на время поиска весь экран накрывал
   полупрозрачный гиф — то есть закрывал ровно ту таблицу, ради которой поиск и
   затевался, и не давал ни прочитать прежнюю выдачу, ни щёлкнуть по ней.
   Полоска шириной в страницу и высотой в два пикселя стоит под шапкой и
   ничего собой не закрывает.
   Класс htmx-request вешает htmx по hx-indicator; его же ставит setLoading в
   main.js, когда за списками марок идёт jQuery, — индикатор на странице один
   на всех. */
.load-bar{position:fixed; top:var(--header-h); left:0; width:100%; height:2px;
	z-index:30; overflow:hidden; background:var(--c-accent-soft);
	opacity:0; transition:opacity .15s ease;}
.load-bar.htmx-request{opacity:1;}
.load-bar.htmx-request::before{content:''; display:block; height:100%; width:35%;
	background:var(--c-accent); animation:load-bar-slide .9s linear infinite;}
@keyframes load-bar-slide{from{transform:translateX(-100%)} to{transform:translateX(300%)}}

/* Пометка «аналог K20A3»: без неё выдача с чужими кодами выглядит как
   поломка поиска (MOTORS_PLAN.md §5). */
.analog-tag{margin-left:.25rem; padding:0 .25rem; border-radius:2px;
	background:var(--c-accent-soft); color:var(--c-accent); font-size:.75rem; white-space:nowrap;}

	.container-fluid{padding:0 1rem;}

@media screen and (max-width:48em){
	.btn{line-height: 1.2;}
	.select-list{left:auto; right:0;}
	/* На узком экране шапка складывается: логотип и пользователь сверху,
	   поля и галочки — отдельными строками во всю ширину. */
	.header-logo{order:-2;}
	#header .header-user{order:-1;}
	.search-bar_fields, .search-bar_flags{flex:1 1 100%;}
	.search-field{flex:1 1 100%; max-width:none;}
	#header select, #header select[name="sort"]{flex:1 1 45%; min-width:0; max-width:none;}
	/* Панель внизу складывается в несколько рядов: цена для клиента забирает
	   свой ряд целиком, иначе селекты марки и кода сжимаются до нечитаемых. */
	.motor-price{flex:1 1 100%;}
	.bottom-bar_count{margin-left:0;}
	/* Вертикальные разделители убираем совсем: на перенесённой строке линия
	   оказалась бы у левого края и читалась как рамка, а не как граница между
	   соседями. Ряды и так разделены переносом. */
	.bottom-bar > * + *{margin-left:0;}
	.bottom-bar > * + *::before{display:none;}
	.bottom-bar{gap:.4rem .75rem;}
	/* Карточка строки на телефоне занимает экран целиком: панель в 28rem рядом
	   с таблицей там всё равно ни с чем не сравнить. Ширина здесь перебивает
	   `--card-w` по порядку правил, и растянутая мышью карточка на телефоне
	   всё равно во весь экран — ручку там и не показываем: тянуть панель,
	   занимающую весь экран, некуда, а пальцем в семь пикселей не попасть. */
	.row-card{width:100%;}
	.row-card_grip{display:none;}

	/* Выдача карточками (UX_PLAN.md §10). Двадцать пять колонок на телефоне
	   нечитаемы: строка уезжает вправо на несколько экранов, и что стоит в
	   ячейке — видно только по памяти о заголовке, оставшемся слева.
	   Поэтому на узком экране строка становится карточкой: код мотора
	   заголовком, под ним четыре значения с подписями, остальное — по тапу в
	   карточке строки из §8, которая на телефоне и так открывается во весь
	   экран.
	   Класс стоит только на таблице авторизованного: у гостя разметка своя,
	   классы ячеек в ней местами другие (код мотора носит `td-num`), и
	   карточная раскладка развалилась бы. Под гостя не оптимизируем — §1. */
	.catalog-table, .catalog-table tbody, .catalog-table tr,
	.catalog-table td{display:block;}
	/* Шапка уходит вся: подписи переехали к самим значениям, а строка фильтров
	   в два десятка полей на телефоне всё равно нерабочая — там ищут строкой
	   поиска из шапки сайта. Липкие позиции с ней тоже перестают что-либо
	   значить: --thead-h после этого меряется нулём (syncHeaderMetrics). */
	.catalog-table thead{display:none;}
	.catalog-table tr{padding:.4rem .5rem;
		border:0; border-bottom:1px solid var(--c-accent-soft);}
	/* Ширины и обрезка колонок здесь только мешают: колонок больше нет, а
	   ширина карточки — ширина экрана. */
	.catalog-table td{max-width:none; max-height:none; min-width:0;
		padding:0; border:0; overflow:visible; white-space:normal;}
	.catalog-table tbody td{display:none;}
	.catalog-table tbody td.td-model_dvs-cell{display:block;
		font-size:1.0625rem; font-weight:bold; color:var(--c-accent);}
	/* Обрезка снята с ячейки выше по тем же причинам — снимаем и с её строк,
	   иначе код на карточке обрывался бы многоточием посреди экрана. Размер и
	   цвет подписи остаются своими: заголовком карточки служит код, а не она. */
	.catalog-table .td-dvs, .catalog-table .td-norm{overflow:visible;
		white-space:normal;}
	.catalog-table .td-norm{font-size:.8125rem; font-weight:400;
		color:var(--c-text-second);}
	/* Показываются те колонки, которым сервер дал подпись для узкого экрана
	   (columns.py, `narrow`), — цены, поставщик и надёжность. Перечислять их
	   тут именами значило бы держать список колонок в двух местах. */
	.catalog-table tbody td[data-narrow]{display:inline; margin-right:.6rem;
		font-size:.875rem;}
	.catalog-table tbody td[data-narrow]::before{content:attr(data-narrow) " ";
		color:var(--c-text-second); font-size:.75rem;}
	/* Значок копирования на телефоне виден всегда: наведения там нет, а
	   .25 прозрачности без него — это просто невидимая кнопка. */
	.catalog-table .td-copy{opacity:.6;}
	/* Кнопки корзин остаются и в карточке строки: разбирать выдачу с телефона
	   менеджеру доводится, и звёздочка нужна там ровно затем же. Ширина
	   колонки на карточной раскладке значения не имеет — там нет колонок. */
	.catalog-table tbody td.project-cell{display:block; width:auto; min-width:0;
		text-align:left; font-size:1.25rem;}
	/* Под выдачей стоит вторая таблица — поисковые системы, — и ссылка в ней
	   бывает в полсотни знаков без единого пробела. Ячейка от этого не
	   сжимается, и страница целиком становилась вдвое шире экрана: карточки
	   выдачи складывались верно, а листалась она всё равно вбок, потому что
	   ширину задавал сосед. Замерено: 997 пикселей при экране в 492. */
	.table-responsive td a{overflow-wrap:anywhere;}
}

@media screen and (min-width:48em){
	.card{padding:2rem;}
}


.add_comment{
  width: 20px;
  height: 20px;
  position: absolute; right: 5px;
  z-index: 999;
  cursor: pointer;
  background: url("/static/img/addcomment.afb17c94b3c8.png") no-repeat;
}

.delete_comment{
  display: inline-block;
  width: 20px;
  height: 20px;
  z-index: 999;
  cursor: pointer;
  background: url("/static/img/delete_comment.36e386e1061f.png") no-repeat;
}

.contacts-block__form {
  margin-bottom: 50px;
  padding: 32px 48px;
  position: relative;
  -webkit-backdrop-filter: blur(10px);
          backdrop-filter: blur(10px);
}
.contacts-block__form > div,
.contacts-block__form label {
  position: relative;
}

.contacts-block__form-title {
  font-family: "PF BeauSans Pro", serif;
  color: #241324;
  font-size: 24px;
  line-height: 1.3;
  font-weight: 600;
  margin-bottom: 16px;
}
.contacts-block__form label {
  display: block;
  margin: 0 0 8px;
}
.contacts-block__form .site-btn,
.contacts-block__form a.site-btn {
  width: 50%;
}

.modal {
z-index: 999;
}
.blocker {
z-index: 100000 !important;
}
.my-popup {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  display: -ms-flexbox;
  display: flex;
  -ms-flex-pack: center;
      justify-content: center;
  -ms-flex-align: center;
      align-items: center;
  background: rgba(0,0,0,0.5);
  z-index: 9999;
  opacity: 0;
  visibility: hidden;
}
.my-popup.opened {
  opacity: 1;
  visibility: visible;
}
.my-popup--reviews .my-popup__container {
  max-width: 590px;
}
.my-popup__container {
  position: relative;
  max-height: 100%;
  overflow: auto;
  max-width: 1200px;
  width: 100%;
  padding: 30px 10px 10px;
  -webkit-overflow-scrolling: touch;
}
.my-popup__close {
  cursor: pointer;
  position: absolute;
  right: 5px;
  top: 0;
  font-size: 0;
  line-height: 1;
  z-index: 1;
}
.my-popup__close .svg-icon {
  width: 26px;
  height: 26px;
  color: #fff;
}
.my-popup__form-send {
  text-align: center;
  color: #fff;
  padding: 34px 34px;
  background: gray;
  box-shadow: inset 4px 4px 20px rgba(255,255,255,0.5);
  border-radius: 20px;
}
.my-popup__form-send-title {
  font-weight: 500;
  font-size: 24px;
  line-height: 1.1;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: #fff;
  margin-bottom: 10px;
}
.my-popup__form-send-desc {
  font-weight: 500;
  font-size: 20px;
  line-height: 2;
  text-align: center;
  letter-spacing: 0.07em;
}

.hr-dotted {
	margin: 20px 0;
	padding: 0;
	height: 0;
	border: none;
	border-top: 2px dotted #ddd;
}

@media all and (max-width: 1022px) {
.contacts-block {
    margin-bottom: 40px;
  }
  .contacts-block__form {
    padding: 20px 20px;
    margin-bottom: 10px;
  }
  .contacts-block__form-title {
    margin-bottom: 15px;
    font-size: 18px;
  }
  .contacts-block__form label {
    margin-bottom: 20px;
  }
  .contacts-block__form-button {
    margin-top: 20px;
  }
  .my-popup__form-send {
    padding: 30px 20px;
    border-radius: 20px;
  }
  .my-popup__form-send-title {
    font-size: 22px;
    margin-bottom: 15px;
  }
  .my-popup__form-send-desc {
    line-height: 1.2;
    font-size: 16px;
  }
  }

/* Загрузка прайса: полоска хода и таблица ошибок разбора. */
.upload-progress {
  margin: 20px 0;
  padding: 15px 20px;
  border: 1px solid #ddd;
  border-radius: 8px;
  background: #fafafa;
}
.upload-progress__title {
  font-weight: 700;
  margin-bottom: 8px;
}
.upload-progress__bar {
  height: 14px;
  border-radius: 7px;
  background: #e4e4e4;
  overflow: hidden;
}
.upload-progress__bar > i {
  display: block;
  width: 0;
  height: 100%;
  background: #4c8bf5;
  transition: width 0.3s linear;
}
.upload-progress__note {
  margin-top: 8px;
  font-size: 14px;
  color: #666;
}

.upload-result {
  margin: 20px 0;
  padding: 15px 20px;
  border-radius: 8px;
  border: 1px solid #ddd;
}
.upload-result--ok {
  border-color: #9ccc9c;
  background: #f2fbf2;
}
.upload-result--warn {
  border-color: #e3c37a;
  background: #fdf8ec;
}
.upload-result--bad {
  border-color: #e0a0a0;
  background: #fdf3f3;
}

/* Времена стадий: подпись к «Готово», а не таблица данных — поэтому без
   рамок и во всю ширину не тянется. */
.upload-stats {
  margin: 10px 0;
  border-collapse: collapse;
  font-size: 14px;
  color: #555;
}
.upload-stats td {
  padding: 2px 0;
  text-align: left;
}
.upload-stats td + td {
  padding-left: 16px;
  white-space: nowrap;
}

.upload-errors {
  width: 100%;
  margin: 10px 0;
  border-collapse: collapse;
  font-size: 14px;
}
.upload-errors th,
.upload-errors td {
  padding: 4px 8px;
  border: 1px solid #e0d0d0;
  text-align: left;
  vertical-align: top;
}
.upload-errors__value {
  word-break: break-all;
}
/* Номер строки в файле — второй номер той же строки, поэтому подписью, а не
   отдельной колонкой: главный номер тот, что виден в таблице. */
.upload-errors__file-line {
  display: block;
  font-size: 12px;
  color: #888;
  white-space: nowrap;
}

/* Проекты (PROJECTS_PLAN.md §5). Своих цветов раздел не заводит: он собран из
   тех же токенов и тех же .btn/.dropdown/table, что и каталог, — чтобы вкладка
   рядом с выдачей не выглядела чужим сайтом. */

/* Ссылка на раздел в шапке. Стоит между фильтрами и меню пользователя, и
   прижата к правому краю вместе с ним: .header-user уже несёт margin-left:auto,
   поэтому свободное место шапки достаётся промежутку перед этой парой. */
.header-projects{margin-left:auto; white-space:nowrap; font-weight:bold; text-decoration:none;}
#header .header-projects + .header-user{margin-left:1rem;}

/* «Новый проект» на выдаче — обычная вторичная кнопка. */
.projects-new{display:flex; align-items:center;}
.projects-new .btn{padding:.35rem 1rem; font-size:14px;}

/* Выдача открыта в контексте проекта. Плашка набрана подложкой, а не рамкой:
   она отвечает на вопрос «куда я сейчас складываю» и обязана читаться боковым
   зрением — иначе поиск уедет не в тот проект, и заметят это через неделю. */
/* Ширина плашки: после галочки «только новые» (§12.6) в прежние 22rem имя
   проекта уже не влезало и обрезалось до «2Z...» — то есть плашка отвечала
   «куда я складываю» ровно тем, что в имени и различает проекты. Считается
   она по неизменяемым соседям: счётчики, галочка, кнопка и крестик забирают
   около 20rem, остальное достаётся имени. */
.projects-context{display:flex; align-items:center; gap:.5rem; max-width:34rem; padding:.25rem .5rem; background:var(--c-accent-soft); font-size:14px;}
/* min-width:0 — чтобы имя ужималось до многоточия, а не распирало плашку:
   у flex-элемента минимум по умолчанию равен содержимому, и одного overflow
   тут мало. */
.projects-context_name{min-width:0; font-weight:bold; text-decoration:none; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.projects-context_save{flex:0 0 auto; padding:.25rem .8rem; font-size:13px;}
.projects-context_exit{flex:0 0 auto; line-height:0; color:var(--c-text-second);}
.projects-context_exit .icon{width:11px; height:11px; fill:currentColor;}
.projects-context_exit:hover{color:var(--c-accent-hover);}
/* Счётчики корзин в плашке (§6.1). Ссылкой, а не текстом: числа отвечают на
   «сколько уже отобрано», и следующий вопрос — «покажи», то есть страница
   проекта. Обновляются ответом на нажатие кнопки в строке (hx-swap-oob). */
.projects-context_counts{flex:0 0 auto; white-space:nowrap; text-decoration:none; font-size:13px;}
/* «Только новые» (§12.6): разбор новинок идёт в каталоге, а не в блоке на
   странице проекта, — здесь для него есть и сортировка, и соседи по цене.
   Галочка стоит внутри плашки, потому что без проекта она ничего не значит. */
.projects-context_only-new{flex:0 0 auto; display:flex; align-items:center; gap:.25rem; white-space:nowrap; font-size:13px; cursor:pointer;}
.projects-context_only-new input{margin:0;}

/* Кнопки корзин в строке выдачи (PROJECTS_PLAN.md §6.1). Колонка узкая и
   стоит сразу за номером: решение принимают, прочитав строку слева направо. */
.project-cell{width:3.5rem; min-width:3.5rem; white-space:nowrap; text-align:center;}
/* Значки набраны текстом, а не svg: звезды и крестика в наборе иконок нет, и
   ради двух символов заводить их дороже, чем написать сами символы — теми же,
   какими подписаны колонки счётчиков в списке проектов. */
.project-btn{padding:0 .15rem; border:none; background:none; cursor:pointer;
	font-size:15px; line-height:1; color:var(--c-accent-line);}
.project-btn:hover{color:var(--c-accent);}
.project-btn.is-on{color:var(--c-accent);}
.project-btn--rej.is-on{color:var(--c-accent-hover);}
/* Строка без ссылки и строка, которой больше нет: кнопки не по чему нажимать,
   и вместо них стоит знак с объяснением в подсказке. */
.project-btn.is-off{cursor:help; color:var(--c-text-second);}

/* Слои выдачи: отобранное подсвечено, отвергнутое приглушено (§6.1). Правило
   висит на ячейке корзин, а не на классе строки: htmx подменяет только эту
   ячейку — до `<tr>` он не дотягивается, и класс на строке разошёлся бы с
   содержимым после первого же нажатия. `:has` пересчитывается сам.
   Подсветка — полупрозрачная подложка на ячейках: цвет надёжности строки живёт
   на самой строке и обязан просвечивать сквозь неё, иначе отобранное потеряло
   бы ровно ту раскраску, по которой его отбирали. */
tbody tr:has(> .project-cell[data-status="favorite"]) > td{background:rgba(19,60,139,.10);}
/* Отклонённое приглушается, а не прячется: видно, что уже разобрано, и можно
   передумать. Сама колонка корзин остаётся яркой — ею и передумывают. */
tbody tr:has(> .project-cell[data-status="rejected"]) > td:not(.project-cell){opacity:.45;}

/* Четвёртый слой — новинка: строка, которой в проекте ещё не видели (§7.2).
   Набрана подписью в самой ячейке корзин, а не подложкой строки: подложка уже
   занята избранным, и два фона на одной строке спорили бы за внимание. Слой
   здесь нижний по значимости — решение по строке важнее её возраста, — и
   разводить их правилами не приходится: строка, попавшая в корзину, новинкой
   уже не считается, и пометку сервер в такую ячейку не кладёт. */
.project-new{display:block; margin-top:.15rem; font-size:10px; line-height:1;
	text-transform:uppercase; letter-spacing:.03em; color:var(--c-accent); cursor:help;}
/* Полоска у левого края строки: по ней новинки видно, не читая колонку целиком,
   — при полусотне строк на странице это единственный способ найти три новых. */
tbody tr:has(> .project-cell[data-new="1"]) > td:first-child{box-shadow:inset 3px 0 0 var(--c-accent);}

/* Корзины на странице проекта (§6.2). Показаны снимки, а не каталог: строки
   прайса к этому времени может уже не быть. */
.basket-head{display:inline; margin:0; font-size:1.25rem;}
.basket-count{font-weight:normal; font-size:.875rem;}
.basket-fold{margin:1.5rem 0;}
.basket-fold > summary{cursor:pointer; padding:.25rem 0;}
/* `fixed`, а не `auto`: колонки тянут за край заголовка (main.js,
   initColumnWidths), а при авторазметке ширина заголовка — только пожелание,
   и колонку с длинным «Наименованием» сузить было бы нельзя вовсе — её
   распирало бы содержимое. С фиксированной раскладкой ширину задаёт первая
   строка, а значения переносятся внутри отведённого им места. */
.basket-table{table-layout:fixed;}
.basket-table td, .basket-table th{vertical-align:top; padding:.35rem .5rem;}
/* Значение переносится по любому месту: в наименовании и информации попадаются
   слова в полсотни знаков без пробелов, и без этого они выпирали бы из своей
   колонки поверх соседней. */
.basket-table td{overflow-wrap:anywhere;}
/* Подпись колонки переносится, а не обрезается: приписка «без навеса» уезжает
   на вторую строку тем же `.small-text`, что и в шапке выдачи, и две колонки
   цены остаются различимы в любой ширине. Полное имя вдобавок стоит в подсказке
   браузера — его ставит разметка. */
.basket-table th{overflow:hidden; white-space:normal; vertical-align:bottom;}
/* Значение ячейки живёт в своей коробке ростом в пять строк. Коробка нужна
   потому, что высоту самой ячейки таблицы браузер не ограничивает: «Информация»
   у поставщика бывает абзацем на четыреста знаков, и без потолка одна строка
   корзины занимала бы пол-экрана. Сузишь колонку — текст станет только выше,
   то есть протяжка колонок без этого работала бы наоборот.
   `line-clamp` рисует на обрыве многоточие; браузер, который его не знает,
   покажет текст целиком — это хуже, но не сломано. Целиком значение читается
   подсказкой браузера (`title` у ячейки). */
.basket-cell{display:-webkit-box; -webkit-line-clamp:5; -webkit-box-orient:vertical;
	max-height:7.5em; overflow:hidden;}
/* Каталог обрезает длинные значения по одной строке (`td.td-name` и соседи —
   там выдача, её пробегают глазами). В корзине наоборот: строк десяток, и их
   читают — поэтому обрезка снимается, а текст переносится. */
.basket-table td.td-name, .basket-table td.td-information,
.basket-table td.td-body, .basket-table td.td-number_oem,
.basket-table td.td-number_dvs, .basket-table td.td-vin_code{max-width:none;
	max-height:none; overflow:visible; white-space:normal;}
/* Ширины по умолчанию — процентами, а не пикселями: таблица тянется по ширине
   окна, и на широком мониторе колонки обязаны расти вместе с ней. Сумма
   ровно 100 для корзины; в блоке новинок последних двух колонок нет, и
   оставшееся браузер раздаёт остальным пропорционально. Правится это число
   мышью и переживает перезагрузку (initColumnWidths). */
.basket-table .td-model_dvs{width:10%;}
.basket-table .td-price_without_c, .basket-table .td-price_with_c{width:7%;}
.basket-table .td-currency, .basket-table .td-year,
.basket-table .td-volume{width:5%;}
.basket-table .td-firm{width:10%;}
.basket-table .td-name{width:15%;}
.basket-table .td-information{width:18%;}
.basket-table .basket-who{width:7%;}
.basket-table .basket-actions{width:11%;}
/* Ручка протяжки — полоска у правого края заголовка. Заголовки таблицы липкие
   (`thead th{position:sticky}`), то есть позиционированные, и своей опоры ей
   не нужно. */
.col-grip{position:absolute; top:0; right:0; bottom:0; width:6px;
	cursor:col-resize; background:transparent;}
.col-grip:hover{background:var(--c-accent-soft);}
.basket-who{font-size:.875rem;}
/* Подписи кнопок переносятся, а не тянут колонку: раскладка таблицы
   фиксированная, и «Передумал — отклонить» одной строкой вылезало бы за правый
   край. Слово целиком важнее строки: кнопка должна говорить, что сделает. */
.basket-actions{font-size:.875rem; white-space:normal;}
.basket-actions .btn-link{padding:0; display:block;}
.basket-remove{color:var(--c-accent-hover);}

/* «Пропал из прайса» — итог сверки после импорта (§8.2). Значок красный, а
   строка не приглушена: отклонённое в выдаче гасят потому, что читать его
   больше не нужно, а здесь наоборот — цена и снимок остаются единственным, что
   об этом моторе вообще известно, и читать их придётся именно теперь. Поэтому
   помечается строка полоской у края, как новинка в выдаче, и подписывается
   словами: цветом одним такое не скажешь. */
.basket-gone{display:block; margin-bottom:.35rem; font-size:.75rem;
	line-height:1.2; text-transform:uppercase; letter-spacing:.03em;
	color:var(--c-accent-hover); cursor:help;}
.basket-row--gone > td:first-child{box-shadow:inset 3px 0 0 var(--c-accent-hover);}

/* Новое по поискам (§7.2). Своей таблицы блок не заводит — та же .basket-table,
   что и у корзин: разница между «что появилось» и «что отобрано» в заголовке, а
   не в наборе колонок. */
.project-news_head{margin:1.5rem 0 .25rem;}
.project-news_head a{font-weight:bold;}
/* Заголовок блока и кнопка сброса (§12.5). «Сбросить новое» набрано ссылкой, а
   не кнопкой, и отодвинуто к правому краю: оно необратимо, и нажимать его
   должны нарочно, а не мимоходом целясь в соседний заголовок. */
.project-news_top{display:flex; align-items:center; flex-wrap:wrap; gap:.5rem 1rem;}
.project-news_top h2{margin:0;}
.project-news_reset{margin-left:auto; color:var(--c-text-second);}
.project-news_reset:hover{color:var(--c-accent-hover);}

/* «Новый поиск» на странице проекта — главный способ его пополнять
   (PROJECTS_PLAN.md §5): проект уже выбран тем, что менеджер сюда пришёл, а
   набирать запрос он будет в шапке каталога, где для этого всё есть. */
.project-add{display:flex; align-items:center; flex-wrap:wrap; gap:.25rem 1rem; margin:.5rem 0 1.5rem;}
.project-add .btn{padding:.4rem 1.2rem;}
/* Пояснение переносится на свою строку целиком, а не втискивается между
   кнопкой и краем: на узком экране оно иначе рвётся по букве. */
.project-add .form-text{flex:1 0 100%; margin:0;}
/* Кнопка «создать проект» стоит вплотную за строкой поиска по списку, поэтому
   форма вокруг неё не должна занимать в раскладке ничего своего, а высота
   кнопки обязана совпадать с соседней «Найти». */
.projects-create{display:flex;}
.projects-create .btn{padding:.4rem 1.2rem;}

/* Список проектов. */
.projects-head{display:flex; align-items:center; flex-wrap:wrap; gap:.5rem 1.5rem; margin-top:1rem;}
.projects-head h1{margin:0;}
.projects-filter{display:flex; align-items:center; gap:.5rem; margin-left:auto;}
.projects-filter .form-control{width:18rem; max-width:100%; padding:.4rem .5rem;}
.projects-filter .btn{padding:.4rem 1.2rem;}

/* Инструкция к разделу под спойлером. Свёрнута по умолчанию и набрана
   подложкой, а не рамкой: раскрытая она длиннее самого списка проектов, и
   каждый день она не нужна — её открывают один раз и вспоминают правило про
   новинки. Ширина ограничена: сплошной текст во всю ширину монитора не
   читается, а таблице ниже широкой быть обязательно. */
.projects-help{margin-top:1rem;}
.projects-help > summary{display:inline-block; cursor:pointer; padding:.25rem 0; color:var(--c-accent); font-size:.875rem;}
.projects-help_body{max-width:52rem; margin-top:.5rem; padding:.75rem 1rem; background:var(--c-accent-soft); font-size:.875rem;}
.projects-help_body p{margin:0 0 .5rem;}
.projects-help_body ol{margin:0 0 .75rem; padding-left:1.5rem;}
.projects-help_body li{margin-bottom:.35rem;}
.projects-help_body > *:last-child{margin-bottom:0;}

/* Тумблер «мои/все» — не переключатель прав, а фильтр списка (§2), поэтому
   набран подписями, а не кнопками. */
.projects-tabs{display:flex; align-items:center; gap:1rem; margin:1rem 0 .5rem;}
.projects-tab{text-decoration:none; padding-bottom:.15rem; border-bottom:2px solid transparent;}
.projects-tab.is-current{border-bottom-color:var(--c-accent); font-weight:bold;}
.projects-tab_aside{margin-left:auto; font-size:.875rem; color:var(--c-text-second);}

/* Заметка про клиента бывает в несколько строк; в списке от неё нужна первая. */
.projects-note{max-width:20rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.projects-empty{padding:1.5rem; color:var(--c-text-second); text-align:center;}
.projects-badge{display:inline-block; padding:0 .4rem; border-radius:2px; background:var(--c-accent-soft); font-size:.75rem; color:var(--c-accent);}
.projects-badge--new{background:var(--c-accent); color:#fff; font-weight:bold;}

/* Страница проекта. */
.project-head{margin:1rem 0 2rem;}
.project-head_top{display:flex; align-items:center; justify-content:space-between; gap:1rem; margin-bottom:.5rem;}
.project-back{text-decoration:none;}
/* Название, клиент и номер сделки стоят в одну строку с кнопкой: правят их
   редко, а вертикаль на этой странице принадлежит поискам и корзинам. На узком
   экране поля переносятся сами — каждому задан предел, ниже которого оно не
   сжимается. */
.project-form{display:flex; align-items:flex-end; flex-wrap:wrap; gap:.5rem .75rem; max-width:60rem;}
.project-form_row{flex:1 1 14rem; min-width:0;}
.project-form_row--short{flex:0 1 9rem;}
/* Высота у всех трёх полей и кнопки задана явно и одна на всех (та же
   --control-h, что у фильтров в шапке каталога). Своей высоты у них не
   совпадает — textarea выше input на межстрочный интервал и на зазор
   inline-элемента, — а строка выравнивается по низу, и от разницы разъезжались
   вверх подписи над полями. */
.project-form .form-control{display:block; width:100%; height:var(--control-h); padding:.4rem .5rem;}
.project-form .form-label{padding:0 0 .15rem;}
/* Заметку о клиенте тянут за уголок вниз, когда она в несколько строк; вбок
   тянуть нельзя — рвётся раскладка строки. Межстрочный интервал ниже, чем у
   input, чтобы строка помещалась в общую высоту без полосы прокрутки. */
.project-form textarea.form-control{line-height:1.4; resize:vertical; min-height:var(--control-h);}
.project-form .btn{height:var(--control-h); padding:0 1.2rem;}
.project-meta{margin-top:1rem; font-size:.875rem;}
/* Архивировать и удалить — соседи по смыслу («убрать с глаз»), но не по весу:
   первое обратимо, второе нет, поэтому вторая кнопка набрана ссылкой. */
.project-actions{display:flex; align-items:center; gap:1.5rem; margin:2rem 0;}
.project-delete{color:var(--c-accent-hover); padding:0;}
.project-delete .icon{width:14px; height:14px; vertical-align:middle; fill:currentColor;}
