/* ---------------------------------------------------------------
   FORGEX CRM — базовые компоненты по макету 1i (UI-кит).
   Каркас страницы — в app.css. Значения — только токенами.
   Компоненты модулей (списки, табы, модалки, переключатель менеджеров)
   добавляются сюда по мере появления страниц.
   --------------------------------------------------------------- */

/* --- кнопки --- */

.btn {
    height: var(--h-control);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    padding: 0 var(--sp-4);
    border: 1px solid transparent;
    border-radius: var(--r-md);
    background: var(--color-accent);
    color: var(--color-on-accent);
    font-family: var(--font);
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition);
}

.btn:hover { background: var(--color-accent-hover); color: var(--color-on-accent); }

.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.btn[disabled] {
    background: var(--color-surface-2);
    color: var(--color-text-disabled);
    cursor: not-allowed;
}

.btn--ghost {
    background: var(--color-surface);
    border-color: var(--color-border);
    color: var(--color-text-secondary);
}

.btn--ghost:hover {
    background: var(--color-surface-2);
    color: var(--color-text);
}

.btn--icon {
    width: var(--h-control-sm);
    height: var(--h-control-sm);
    padding: 0;
    background: var(--color-surface);
    border-color: var(--color-border);
    color: var(--color-text-secondary);
}

.btn--icon:hover { background: var(--color-surface-2); color: var(--color-text); }

/* Заметное, но не главное действие: «Сохранить в PDF» рядом с «Повторить»,
   макеты 6i и 6j. Ставится вместе с .btn--ghost — меняется только цвет подписи */
.btn--accent { color: var(--color-accent); }
.btn--accent:hover { color: var(--color-accent); }

.btn--danger { color: var(--color-danger); }
.btn--danger:hover { background: var(--color-danger-soft); color: var(--color-danger); }

/* Заливка опасного действия: подтверждение удаления и деактивации, макет 11e */
.btn--red { background: var(--color-danger); color: var(--color-on-accent); }
.btn--red:hover { background: var(--color-danger); color: var(--color-on-accent); opacity: .9; }

/* Заливка подтверждения оплаты, макет 7m: это согласие, а не опасное
   действие, и кнопка на нём зелёная, а не акцентная */
.btn--green { background: var(--color-success); color: var(--color-on-accent); }
.btn--green:hover { background: var(--color-success); color: var(--color-on-accent); opacity: .9; }

/* Опасное действие в строке заголовка: приглушённая заливка, макеты 1i и 2f */
.btn--danger-soft {
    background: var(--color-danger-soft);
    color: var(--color-danger);
}

.btn--danger-soft:hover { background: var(--color-danger-soft); color: var(--color-danger); opacity: .9; }

/* Недоступное действие: своя строка, удаление при живых счетах.
   Кнопкой не является — это <span>, поэтому курсор обычный. */
.btn--off {
    background: var(--color-surface);
    border-color: var(--color-border);
    color: var(--color-text-disabled);
    cursor: default;
}

.btn--off:hover { background: var(--color-surface); color: var(--color-text-disabled); }

.btn--wide { width: 100%; height: var(--h-control-lg); }

/* --- поля --- */

.field {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
}

.field__label {
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    color: var(--color-text-secondary);
}

/* «· необязательно» внутри подписи поля (макет 5g): тот же цвет и размер,
   но обычным начертанием — иначе примечание спорит с самой подписью. */
.field__opt { font-weight: var(--fw-regular); }

.input {
    height: var(--h-control-lg);
    width: 100%;
    padding: 0 var(--sp-4);
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    background: var(--color-surface-2);
    color: var(--color-text);
    font-family: var(--font);
    font-size: var(--fs-md);
    outline: none;
    transition: border-color var(--transition), background var(--transition);
}

.input:focus {
    border-color: var(--color-accent);
    background: var(--color-surface);
}

.input::placeholder { color: var(--color-text-muted); }

.input[disabled] { color: var(--color-text-disabled); cursor: not-allowed; }

/* <select> носит тот же класс .input — отдельного компонента не заводим */
select.input { cursor: pointer; }

/* <textarea> тоже. Высота одной строки ему не подходит: текст многострочный,
   и растягивать поле пользователь вправе сам. */
textarea.input {
    height: auto;
    min-height: calc(var(--h-control-lg) * 2);
    padding: var(--sp-3) var(--sp-4);
    line-height: 1.5;
    resize: vertical;
}

.field__hint {
    font-size: var(--fs-base);
    color: var(--color-text-muted);
}

/* Поле с кнопкой справа: пароль и «Сгенерировать», макет 11c. */
.field__row {
    display: flex;
    gap: var(--sp-2);
}

.field__row .btn { flex: none; }

/* Два поля в ряд, на мобильном — друг под другом. */
.form-row {
    display: flex;
    gap: var(--sp-3);
}

.form-row > * { flex: 1; min-width: 0; }

/* Флажок с подписью. */
.check {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-md);
    cursor: pointer;
}

.check input { accent-color: var(--color-accent); }
.check:has(input[disabled]) { color: var(--color-text-muted); cursor: default; }

/* Выбор одного значения карточками вместо селекта: роль в попапе 11c. */
.radio-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--sp-2);
}

.radio-card {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    padding: var(--sp-3);
    font-size: var(--fs-base);
    cursor: pointer;
    transition: border-color var(--transition), background var(--transition);
}

.radio-card input { accent-color: var(--color-accent); }

.radio-card:has(input:checked) {
    border-color: var(--color-accent);
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-weight: var(--fw-semibold);
}

.radio-card:has(input[disabled]) { cursor: default; }

/* Строка «аватар + кто это»: подтверждения действий, макет 11e. */
.user-line {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
}

.user-line__name {
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
}

.user-line__sub {
    font-size: var(--fs-base);
    color: var(--color-text-muted);
}

/* Аватар и подпись к нему в попапе. */
.avatar-block {
    display: flex;
    align-items: center;
    gap: var(--sp-4);
}

.avatar-block__text {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--sp-2);
    font-size: var(--fs-base);
}

@media (max-width: 768px) {
    .form-row { flex-direction: column; }

    /* Пара коротких полей, которая и на телефоне остаётся в две колонки:
       город с улицей и телефон с email в попапе новой компании (макет 2e). */
    .form-row--pair { flex-direction: row; }
}

/* --- строка заголовка внутри контента (макеты 1c, 8e) ---
   Состав у каждой страницы свой: дата, чип статуса, кнопки действий,
   поэтому строку собирает сама страница, а не header.php. */

.page-head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--sp-3);
}

.page-title {
    font-size: var(--fs-2xl);
    font-weight: var(--fw-bold);
    letter-spacing: -.5px;
}

.page-head__meta {
    font-size: var(--fs-base);
    color: var(--color-text-muted);
}

.page-head__actions { margin-left: auto; display: flex; align-items: center; gap: var(--sp-2); }

/* Добавление записи с двумя точками входа: на десктопе кнопка стоит в строке
   заголовка (макет 2a), на телефоне — квадратной кнопкой рядом с поиском
   (макет 2c). Видна всегда ровно одна, действие одно и то же. */
@media (max-width: 768px) {
    .page-head__add { display: none; }
}

/* Строка с одним действием на странице, где больше ничего сверху нет
   (Пользователи): на десктопе кнопка прижата вправо (макет 11a),
   на телефоне растянута во всю ширину (макет 11e). */
.page-actions { display: flex; }

.page-actions .btn { margin-left: auto; }

@media (max-width: 768px) {
    .page-actions .btn {
        width: 100%;
        margin-left: 0;
        justify-content: center;
    }
}

/* --- карточка --- */

.card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-sm);
    padding: var(--sp-5);
}

/* Карточка со стопкой блоков: заголовок, поля, строки. */
.card--stack {
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
}

.card__title {
    font-size: var(--fs-md);
    font-weight: var(--fw-bold);
}

/* --- сообщения (alert.inc.php) --- */

.alerts {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
}

.alert {
    border: 1px solid transparent;
    border-radius: var(--r-md);
    padding: var(--sp-3) var(--sp-4);
    font-size: var(--fs-md);
    font-weight: var(--fw-medium);
}

.alert--success { background: var(--color-success-soft); color: var(--color-success); }
.alert--error   { background: var(--color-danger-soft);  color: var(--color-danger); }
.alert--warning { background: var(--color-warning-soft); color: var(--color-warning); }
.alert--info    { background: var(--color-accent-soft);  color: var(--color-accent); }

/* --- аватар --- */

.avatar {
    width: var(--h-control-sm);
    height: var(--h-control-sm);
    flex: none;
    border-radius: var(--r-full);
    background: var(--color-avatar-1);
    color: var(--color-on-accent);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    object-fit: cover;
    overflow: hidden;
}

.avatar--sm { width: var(--sz-avatar-sm); height: var(--sz-avatar-sm); font-size: var(--fs-xs); }
.avatar--md { width: var(--sz-avatar-md); height: var(--sz-avatar-md); font-size: var(--fs-md); }
.avatar--lg { width: var(--sz-avatar-lg); height: var(--sz-avatar-lg); font-size: var(--fs-2xl); }

/* Контактное лицо клиента — не пользователь CRM, своего цвета у него нет
   (макеты 2f, 2d). */
.avatar--soft {
    background: var(--color-surface-2);
    border: 1px solid var(--color-border);
    color: var(--color-text-secondary);
}

/* Цвет закреплён за пользователем номером, чтобы не менялся между страницами. */
.avatar--c1 { background: var(--color-avatar-1); }
.avatar--c2 { background: var(--color-avatar-2); }
.avatar--c3 { background: var(--color-avatar-3); }
.avatar--c4 { background: var(--color-avatar-4); }
.avatar--c5 { background: var(--color-avatar-5); }
.avatar--c6 { background: var(--color-avatar-6); }

/* --- бейдж-счётчик --- */

.badge {
    flex: none;
    border-radius: var(--r-full);
    padding: 0 var(--sp-2);
    background: var(--color-accent);
    color: var(--color-on-accent);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
}

/* --- бейдж-пилюля: роль, статус (макеты 11a, 11e) ---
   Отличается от .badge: тот считает записи в сайдбаре, этот подписывает
   значение поля и раскрашивается по смыслу. */

.tag {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-1);
    flex: none;
    border-radius: var(--r-full);
    padding: var(--sp-1) var(--sp-3);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    white-space: nowrap;
}

.tag--accent  { background: var(--color-accent-soft);  color: var(--color-accent); }
.tag--info    { background: var(--color-info-soft);    color: var(--color-info); }
.tag--success { background: var(--color-success-soft); color: var(--color-success); }
.tag--warning { background: var(--color-warning-soft); color: var(--color-warning); }
.tag--danger  { background: var(--color-danger-soft);  color: var(--color-danger); }
.tag--muted   { background: var(--color-surface-2);    color: var(--color-text-muted); }

/* Пилюля с рамкой — «от {автор}» в строке задачи (макет 5a). Без рамки она
   сливается с соседней нейтральной пилюлей дедлайна «до 21 июл». */
.tag--outline {
    background: var(--color-surface-2);
    border: 1px solid var(--color-border);
    color: var(--color-text-secondary);
}

/* Бейдж бывает переключателем — статус в шапке карточки компании.
   Кнопке остаётся снять рамку и вернуть шрифт: заливку, размер и радиус
   задают .tag и .tag--*. */
button.tag {
    border: 0;
    font-family: var(--font);
    cursor: pointer;
}

/* --- чип-счётчик в строке заголовка («6 сотрудников», макет 11a) --- */

.chip {
    border: 1px solid var(--color-border);
    border-radius: var(--r-full);
    padding: var(--sp-1) var(--sp-3);
    background: var(--color-surface-2);
    color: var(--color-text-muted);
    font-size: var(--fs-base);
}

/* --- список записей (макеты 11a на десктопе, 11e на мобильном) ---
   Один компонент на все разделы. На десктопе — сетка колонок, на 768px
   строки становятся карточками: колонки перестраиваются, данные не прячутся. */

.list {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}

.list__head,
.list__row {
    display: grid;
    grid-template-columns: var(--sz-avatar-md) minmax(0, 1.4fr) minmax(0, 1.2fr) auto auto;
    grid-template-areas: "ava name mail tags act";
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-5);
}

.list__head {
    border-bottom: 1px solid var(--color-border);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: .4px;
}

.list__head span {
    color: var(--color-text-muted) !important;
}

.list__row {
    position: relative;
    border-bottom: 1px solid var(--color-border);
    transition: background var(--transition);
}

.list__row:last-child { border-bottom: none; }
.list__row:hover { background: var(--color-surface-2); }

/* Строка кликабельна целиком: ссылка растягивается на всю строку,
   а кнопки действий лежат выше неё и перехватывают клик. */
.list__link {
    color: inherit;
    text-decoration: none;
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
}

.list__link::after {
    content: '';
    position: absolute;
    inset: 0;
}

.list__sub {
    font-size: var(--fs-base);
    color: var(--color-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.list__cell--ava  { grid-area: ava; }
.list__cell--name { grid-area: name; min-width: 0; }
.list__cell--mail { grid-area: mail; min-width: 0; font-size: var(--fs-base); color: var(--color-text-secondary); }
.list__cell--tags { grid-area: tags; display: flex; flex-wrap: wrap; gap: var(--sp-2); }

.list__actions {
    grid-area: act;
    position: relative;
    display: flex;
    gap: var(--sp-2);
}

.list__note {
    display: block;
    margin-top: var(--sp-1);
    font-size: var(--fs-xs);
    color: var(--color-warning);
}

@media (max-width: 768px) {
    /* Заголовки колонок теряют смысл, когда строка стала карточкой.
       Прячется подпись, не данные: все поля ниже остаются на месте. */
    .list__head { display: none; }

    .list {
        background: none;
        border: none;
        box-shadow: none;
        display: flex;
        flex-direction: column;
        gap: var(--sp-2);
        overflow: visible;
    }

    .list__row {
        grid-template-columns: var(--sz-avatar-md) minmax(0, 1fr) auto;
        grid-template-areas:
            "ava name act"
            "ava mail act"
            "tags tags tags";
        background: var(--color-surface);
        border: 1px solid var(--color-border);
        border-radius: var(--r-lg);
        box-shadow: var(--shadow-sm);
        padding: var(--sp-3) var(--sp-4);
    }

    .list__row:last-child { border-bottom: 1px solid var(--color-border); }
}

/* --- список компаний (макет 2a на десктопе, 2c на мобильном) ---
   Свой набор колонок поверх общего компонента .list: ячейки «Пользователей»
   (ava, name, mail, tags) описывают другие данные и не переиспользуются. */

/* Пол у названия и адреса, сжимаемые соседи — по той же причине, что
   в списке КП: 470px жёстких колонок на узком экране не оставляли тексту
   места, и он вылезал из своей ячейки на соседнюю. Пропорции 1.5 : 1.7
   между названием и адресом работают, пока места хватает обоим.

   У менеджера пол по размеру аватара: он flex: none и не сжимается,
   а колонка без пола ужалась бы уже его. */
.list--company .list__head,
.list--company .list__row {
    grid-template-columns: minmax(0, var(--w-col-city))
                           minmax(var(--w-col-name-min), 1.5fr)
                           minmax(var(--w-col-name-min), 1.7fr)
                           minmax(0, var(--w-col-phone))
                           minmax(var(--sz-avatar-sm), var(--w-col-manager));
    grid-template-areas: "city title street phone manager";
}

.list__cell--city    { grid-area: city;   min-width: 0; color: var(--color-text-secondary); }
.list__cell--title   { grid-area: title;  min-width: 0; }
.list__cell--street  { grid-area: street; min-width: 0; color: var(--color-text-secondary); }
.list__cell--phone   { grid-area: phone;  min-width: 0; }

.list__cell--manager {
    grid-area: manager;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}

.list__manager-name {
    color: var(--color-text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

@media (max-width: 768px) {
    /* Карточка 2c: название, «город · улица», телефон и менеджер.
       Данные те же, что на десктопе, — меняется только раскладка. */
    .list--company .list__row {
        grid-template-columns: auto minmax(0, 1fr);
        grid-template-areas:
            "title   title"
            "city    street"
            "phone   manager";
        gap: var(--sp-2) var(--sp-1);
    }

    .list--company .list__cell--street::before { content: '· '; }
    .list--company .list__cell--manager { justify-content: flex-end; }
}

/* --- список КП (макет 6a на десктопе, 6c на мобильном) ---
   Свой набор колонок поверх общего компонента .list. Колонок «Позиций»
   и «Менеджер» с макета тут нет, скидка и срок действия — есть
   (спека модуля, раздел 11). */

/* Шесть колонок на 1024px в строку не помещаются: 660px фиксированных
   ширин съедают всё место, и название клиента оставалось без единого
   пикселя — текст вылезал из своей ячейки поверх суммы. Поэтому у названия
   есть пол, а остальные колонки умеют ужиматься: на широком экране они
   всё так же встают в свою полную ширину, а на узком уступают. */
.list--proposal .list__head,
.list--proposal .list__row {
    grid-template-columns: minmax(0, var(--w-col-number))
                           minmax(var(--w-col-name-min), 1fr)
                           minmax(0, var(--w-col-price))
                           minmax(0, var(--w-col-discount))
                           minmax(0, var(--w-col-date))
                           minmax(0, var(--w-col-date));
    grid-template-areas: "number company amount discount date valid";
}

.list__cell--number   { grid-area: number;   min-width: 0; color: var(--color-text-muted); }
.list__cell--company  { grid-area: company;  min-width: 0; font-weight: var(--fw-semibold); }
.list__cell--amount   { grid-area: amount;   min-width: 0; font-weight: var(--fw-semibold); }
.list__cell--discount { grid-area: discount; min-width: 0; color: var(--color-text-secondary); }
.list__cell--date     { grid-area: date;     min-width: 0; color: var(--color-text-secondary); }
.list__cell--valid    { grid-area: valid;    min-width: 0; color: var(--color-text-secondary); }

/* Номер КП — единственная ссылка строки, но кликабельна вся строка целиком,
   поэтому цвет у него общий для ячейки, а не акцентный */
.list--proposal .list__link { font-weight: var(--fw-regular); color: inherit; }

@media (max-width: 768px) {
    /* Карточка 6c: клиент и сумма первой строкой, ниже — номер, скидка,
       дата и срок. Данные те же, что на десктопе, меняется раскладка. */
    .list--proposal .list__row {
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas:
            "company  amount"
            "number   discount"
            "date     valid";
        gap: var(--sp-2) var(--sp-1);
    }

    .list--proposal .list__cell--discount,
    .list--proposal .list__cell--valid { text-align: right; }
}

/* --- список заявок (макет 8a на десктопе, 8c на мобильном) ---
   Свой набор колонок поверх общего компонента .list. Вместо «сообщения»
   с макета — товар, со страницы которого пришла заявка (спека модуля,
   раздел 0.3); шеврона в конце строки нет — скилл verstka. */

.list--request .list__head,
.list--request .list__row {
    grid-template-columns: var(--w-col-date) var(--w-col-phone) minmax(0, 1fr)
                           minmax(0, 1.3fr) auto;
    grid-template-areas: "date phone name item tags";
}

/* Трубка у номера — декор с макетов 8a и 8g: телефон в списке остаётся
   текстом, ссылкой tel: он не становится. Компонент общий для заявок
   и заказов — второй копии не заводим. */
.list--request .list__cell--phone,
.list--order .list__cell--phone {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}

.list--request .list__cell--phone svg,
.list--order .list__cell--phone svg { color: var(--color-text-muted); }

/* Телефон в модалке заявки: та же трубка-декор, что в строке списка,
   но своим блоком — ячейки сетки в модалке нет */
.request-phone {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-md);
}

.request-phone svg { color: var(--color-text-muted); }

/* --- блок компании в модалке заявки ---
   Своего макета у блока нет: его требуют pages.md и спека модуля, раздел 6.
   Одна и та же строка служит привязанной компании и каждому результату
   поиска — у результата она сама является формой привязки. */

.request-company {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-3);
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    background: var(--color-surface-2);
}

.request-company__body { flex: 1; min-width: 0; }

.request-company__name { font-weight: var(--fw-semibold); }

.request-company__sub {
    font-size: var(--fs-base);
    color: var(--color-text-muted);
}

/* Кнопка строки не тянется и не сжимается: место уступает название */
.request-company .btn { flex: none; }

.request-found {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
}

/* Ссылка на форму базы данных стоит отдельным действием и по ширине
   поля не растягивается */
.request-create { align-self: flex-start; }

.list__cell--item {
    grid-area: item;
    min-width: 0;
    color: var(--color-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

@media (max-width: 768px) {
    /* Карточка 8c: имя, ниже «дата · телефон», ниже товар, пилюля справа.
       Данные те же, что на десктопе, — меняется только раскладка. */
    .list--request .list__row {
        grid-template-columns: auto minmax(0, 1fr) auto;
        grid-template-areas:
            "name  name  tags"
            "date  phone tags"
            "item  item  item";
        gap: var(--sp-1) var(--sp-2);
    }

    .list--request .list__cell--phone::before { content: '· '; }
    .list--request .list__cell--tags { justify-content: flex-end; }
}

/* --- список заказов (макет 8g на десктопе; мобильного макета нет) ---
   Колонки те же, что у заявок, только вместо товара — сумма к оплате
   (спека модуля, раздел 5). Шеврона в конце строки нет — скилл verstka. */

/* Пол у имени и сжимаемые соседи — по той же причине, что в списке КП:
   иначе на 1024px длинная фамилия наезжала бы на сумму */
.list--order .list__head,
.list--order .list__row {
    grid-template-columns: minmax(0, var(--w-col-date))
                           minmax(0, var(--w-col-phone))
                           minmax(var(--w-col-name-min), 1fr)
                           minmax(0, var(--w-col-price)) auto;
    grid-template-areas: "date phone name sum tags";
}

/* Сумма здесь — колонка среди других, а не итог таблицы позиций:
   прижимать её вправо не к чему */
.list--order .list__cell--sum { text-align: left; }

@media (max-width: 768px) {
    /* Карточка по образцу 8c: имя и пилюля первой строкой, ниже
       «дата · телефон», ниже сумма. Данные те же, что на десктопе, —
       меняется только раскладка. */
    .list--order .list__row {
        grid-template-columns: auto minmax(0, 1fr) auto;
        grid-template-areas:
            "name  name  tags"
            "date  phone tags"
            "sum   sum   sum";
        gap: var(--sp-1) var(--sp-2);
    }

    .list--order .list__cell--phone::before { content: '· '; }
    .list--order .list__cell--tags { justify-content: flex-end; }
}

/* --- карточка заказа (макет 8e на десктопе, 8i на мобильном) ---
   Разметка одна на обе ширины, порядок блоков в ней — как на 8i. Колонки
   десктопа собираются здесь: слева состав и отработка, справа покупатель
   и компания (макет 8e). Пропорции те же, что у карточки компании. */

.order-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
    grid-template-areas:
        "items buyer"
        "done  company";
    gap: var(--sp-5);
    align-items: start;
}

.order-items   { grid-area: items; }
.order-buyer   { grid-area: buyer; }
.order-company { grid-area: company; }
.order-done    { grid-area: done; }

/* Таблица позиций стоит внутри карточки, и рамку с отступами по краям даёт
   уже карточка — второй раз компонент .list их не рисует (макет 8e).
   На 768px строки списка обычно становятся отдельными карточками; здесь
   они остаются строками, потому что карточка вокруг них одна. */
.order-items .list {
    background: none;
    border: none;
    border-radius: 0;
    box-shadow: none;
    overflow: visible;
    gap: 0;
}

.order-items .list__head,
.order-items .list__row {
    padding-left: 0;
    padding-right: 0;
    background: none;
    border: none;
    border-bottom: 1px solid var(--color-border);
    border-radius: 0;
    box-shadow: none;
}

.order-items .list__row:last-child { border-bottom: none; }

/* Свои колонки состава. У КП та же таблица занимает страницу целиком,
   а здесь карточка делит строку с покупателем, и 150px под каждую цену
   съедали название товара.

   Селектор длиннее общего .list--pitem намеренно: так сюда не достаёт
   планшетная раскладка компонента. На 1024px .order-grid уже в одну
   колонку, карточка растянута во всю ширину, и складывать строку
   в карточку рано — это происходит ниже, на 768px, вместе с шапкой
   колонок. */
.order-items .list--pitem .list__head,
.order-items .list--pitem .list__row {
    grid-template-columns: minmax(0, 1.6fr) var(--w-pitem-price)
                           var(--w-pitem-qty) var(--w-pitem-price);
    grid-template-areas: "name price qty sum";
    gap: var(--sp-3);
}

/* Количество прижато влево, как на десктопе: правое выравнивание
   компонент включает вместе с планшетной раскладкой, а её здесь нет */
.order-items .list--pitem .list__cell--qty { text-align: left; }

/* Числа не разрываются: у ячеек min-width: 0, и сумма, не влезшая
   в колонку, сложилась бы пополам — «1 540» и «000 ₽» отдельными
   строками читаются как два разных числа */
.order-items .list--pitem .list__cell--price,
.order-items .list--pitem .list__cell--qty,
.order-items .list--pitem .list__cell--sum { white-space: nowrap; }

/* Миниатюра слева от названия — тот же компонент, что в каталоге КП
   (макет 6e). Картинка не сжимается, длинное название переносится рядом
   с ней, а не под неё. */
.order-items .list__cell--name {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-width: 0;
}

/* Место под картинку размечено до её загрузки: у ленивой миниатюры своей
   высоты нет, и без бокса строка прыгала бы после подгрузки. Пропорции
   картинки при этом сохраняются — object-fit вписывает её в квадрат. */
.order-items .pitem__img {
    flex: none;
    width: var(--sz-thumb);
    height: var(--sz-thumb);
}

.order-items .pitem__img img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

/* Отступ пометке даёт уже gap строки */
.order-items .pitem__gone { margin-left: 0; }

/* Пары «подпись — значение» столбиком: колонок, как в карточке компании,
   блоку покупателя не нужно — он и так стоит в узкой колонке */
.order-fields {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}

/* Футер отработки: удаление слева, основное действие справа — как на 8i */
.order-foot {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}

.order-foot .btn--danger { margin-right: auto; }

@media (max-width: 1024px) {
    /* Одна колонка в порядке разметки (макет 8i): состав, покупатель,
       компания, отработка. Состав данных тот же, что на десктопе. */
    .order-grid {
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas: none;
        gap: var(--sp-4);
    }

    .order-items,
    .order-buyer,
    .order-company,
    .order-done { grid-area: auto; }
}

@media (max-width: 768px) {
    /* Карточка состава (макет 8i): название с миниатюрой первой строкой,
       ниже цена, количество и сумма одной строкой — как в чеке. Ничего
       не прячется, меняется только раскладка. */
    .order-items .list--pitem .list__row {
        grid-template-columns: auto auto minmax(0, 1fr);
        grid-template-areas:
            "name  name name"
            "price qty  sum";
        gap: var(--sp-1) var(--sp-2);
    }
}

/* --- список звонков (своего макета нет: таблица по образцу 10a, колонка
   менеджера и пагинация — как в 2a; спека модуля, раздел 5) ---
   Свой набор колонок поверх общего компонента .list. Колонок «Оценка»
   и «Краткий итог» с 10a здесь нет — ИИ-обработки в этой итерации
   не существует. Шеврона в конце строки нет — скилл verstka. */

.list--call .list__head,
.list--call .list__row {
    grid-template-columns: minmax(0, var(--w-col-date))
                           minmax(0, var(--w-col-direction))
                           minmax(var(--w-col-name-min), 1fr)
                           minmax(var(--w-col-name-min), var(--w-col-manager))
                           minmax(0, var(--w-col-duration))
                           auto;
    grid-template-areas: "date dir company manager dur tags";
}

/* Стрелка направления — часть значения, а не декор рядом с ним:
   ↙ и ↗ читаются быстрее слова */
.list__cell--dir {
    grid-area: dir;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    color: var(--color-text-secondary);
}

.list__cell--dir svg { flex: none; }

.list__cell--dur {
    grid-area: dur;
    min-width: 0;
    color: var(--color-text-secondary);
    font-variant-numeric: tabular-nums;
}

/* Номер непривязанного звонка и кнопка «Привязать» стоят в одной ячейке */
.list--call .list__cell--company {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}

/* «Привязать» ведёт на карточку звонка — туда же, куда и сама строка (0.12).
   position: relative поднимает ссылку над растяжкой .list__link::after,
   иначе клик по ней перехватывала бы строка. Кнопка ниже ростом обычной:
   строка списка не должна расти под неё. */
.call-pin {
    position: relative;
    flex: none;
    height: var(--h-control-sm);
    padding: 0 var(--sp-3);
    font-size: var(--fs-base);
}

/* Недозвон: длительности у него нет, и «не отвечен» стоит вместо неё
   приглушённым (спека модуля, раздел 5) */
.call-miss { color: var(--color-text-muted); }

@media (max-width: 768px) {
    /* Карточка звонка в списке: направление и компания-или-номер первой
       строкой, «Сегодня 11:24 · 3:24» второй, менеджер третьей, пилюля
       справа. Данные те же, что на десктопе, — меняется только раскладка. */
    .list--call .list__row {
        grid-template-columns: auto minmax(0, 1fr) auto;
        grid-template-areas:
            "dir     company  tags"
            "date    dur      tags"
            "manager manager  manager";
        gap: var(--sp-1) var(--sp-2);
    }

    .list--call .list__cell--dur::before { content: '· '; }
    .list--call .list__cell--tags { justify-content: flex-end; }
}

/* --- карточка звонка (макет 3e на десктопе, 3g на мобильном) ---
   Разметка одна на обе ширины, порядок блоков в ней — как на 3g: плеер,
   метаданные, компания, транскрипт. Колонки десктопа собираются здесь:
   слева плеер и транскрипт, справа метаданные и компания (макет 3e).
   Пропорции те же, что у карточки компании. */

.call-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
    grid-template-areas:
        "audio      meta"
        "transcript company"
        "transcript .";
    gap: var(--sp-5);
    align-items: start;
}

.call-audio   { grid-area: audio; }
.call-meta    { grid-area: meta; }
.call-company { grid-area: company; }
.call-talk    { grid-area: transcript; }

/* Нативный плеер вместо рисованной волны с макета 3e (спека модуля, 0.14).
   Своей ширины у него нет — растягивается на карточку целиком. */
.call-audio__player { width: 100%; }

/* «Запись удалена», «Записи нет», «Транскрипта нет» — одно и то же по смыслу
   место без содержимого, и текст в нём приглушён одинаково */
.call-audio__none { color: var(--color-text-muted); }

/* Пары «подпись — значение» блока метаданных: подпись слева, значение
   справа — на обеих ширинах, как на 3e. Сами пары — те же .co-field,
   что в карточке компании, вторых стилей не заводим. */
.call-meta__list {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}

.call-meta__list .co-field {
    display: grid;
    grid-template-columns: var(--w-field-label) minmax(0, 1fr);
    gap: var(--sp-3);
    align-items: baseline;
}

.call-meta__list .co-field__label { margin-bottom: 0; }

/* Реплика транскрипта: подпись стороны и пузырь под ней. Таймкодов
   и аватарок с макета 3e нет (спека модуля, 0.11). */
.call-talk__side {
    margin-bottom: var(--sp-1);
    font-size: var(--fs-base);
    color: var(--color-text-muted);
}

.call-talk__text {
    background: var(--color-surface-2);
    border-radius: var(--r-lg);
    border-top-left-radius: var(--r-sm);
    padding: var(--sp-3) var(--sp-4);
    line-height: 1.5;
    overflow-wrap: anywhere;
}

/* Стороны различаются фоном пузыря, как на 3e: менеджер на сером,
   клиент на акцентном */
.call-talk__row--client .call-talk__text { background: var(--color-accent-soft); }

@media (max-width: 768px) {
    /* Одна колонка в порядке разметки (макет 3g): плеер, метаданные,
       компания, транскрипт. Состав данных тот же, что на десктопе. */
    .call-grid {
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas: none;
        gap: var(--sp-4);
    }

    .call-audio,
    .call-meta,
    .call-company,
    .call-talk { grid-area: auto; }
}

/* --- строка фильтров списка (макет 2a) ---
   Поиск, город и переключатель менеджера. Значения живут в адресе,
   поэтому это обычная GET-форма. Кнопки «Показать» на макете нет:
   селекты отправляют форму сами (data-auto-submit в app.js). */

.filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-3);
}

/* В строке фильтров контролы ниже, чем в формах: 38px по макету */
.filters .input { height: var(--h-control); background: var(--color-surface); }

.filters__search {
    position: relative;
    display: flex;
    align-items: center;
    width: var(--w-search);
    max-width: 100%;
}

.filters__search svg {
    position: absolute;
    left: var(--sp-3);
    color: var(--color-text-muted);
    pointer-events: none;
}

.filters__search .input { padding-left: var(--sp-8); }

.filters__select { width: auto; }

/* Длинные названия регионов не расширяют страницу. */
.filters--geography .filters__select {
    max-width: 100%;
    min-width: 0;
}

@media (min-width: 769px) {
    .filters--geography { flex-wrap: nowrap; }
    .filters--geography .filters__search { flex: 1; min-width: 0; }
    .filters--geography .filters__select {
        width: var(--w-geography-select);
        flex: 0 1 var(--w-geography-select);
    }
    .filters--geography .seg,
    .filters--geography .filters__new { flex-shrink: 0; }
}

/* Две точки входа в добавление, видна всегда ровно одна: на десктопе кнопка
   с текстом правым краем этой же строки, на телефоне квадратная «+» рядом
   с поиском (макет 2c). Вправо .filters__new уводит margin-right: auto у .seg. */
.filters__add { display: none; }

@media (max-width: 768px) {
    /* Сетка вместо колонки: поиск и «+» стоят в одну строку (макет 2c),
       город и переключатель занимают всю ширину каждый. */
    .filters {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        align-items: center;
    }

    /* Там, где кнопки «+» нет вовсе (Клиенты: клиент заводится на карточке
       компании), вторая колонка не нужна — иначе её пустой трек съедает
       у поиска ширину зазора и правый край не сходится с карточками */
    .filters:not(:has(.filters__add)) { grid-template-columns: minmax(0, 1fr); }

    .filters__search { width: auto; }
    .filters__select { width: 100%; }
    .filters__select, .seg { grid-column: 1 / -1; }

    .filters__add {
        display: inline-flex;
        width: var(--h-control);
        padding: 0;
        font-size: var(--fs-lg);
    }

    .filters__new { display: none; }
}

/* --- сегментированный переключатель (макет 2a) ---
   Пилюли в общей рамке: «Все», «Не привязанные» и селект менеджера.
   Тем же компонентом дальше верстаются табы разделов. */

.seg {
    display: flex;
    align-items: stretch;
    gap: var(--sp-1);
    height: var(--h-control);
    padding: var(--sp-1);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--r-lg);
}

.seg__item {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: 0 var(--sp-3);
    border-radius: var(--r-sm);
    color: var(--color-text-secondary);
    font-size: var(--fs-base);
    font-weight: var(--fw-medium);
    text-decoration: none;
    white-space: nowrap;
    transition: background var(--transition), color var(--transition);
}

a.seg__item:hover { background: var(--color-surface-2); }

.seg__item.is-active {
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-weight: var(--fw-semibold);
}

/* Счётчик у таба (макет 3a). Отступ от подписи даёт gap самого .seg__item */
.seg__count { color: var(--color-text-muted); }

/* На акцентном фоне активного таба приглушённый цвет нечитаем,
   поэтому там счётчик отступает прозрачностью, а не цветом */
.seg__item.is-active .seg__count {
    color: inherit;
    opacity: .7;
}

/* Табы стоят слева, поиск уходит к правому краю строки (макет 3a).
   В базе (макет 2a) сегмент идёт последним и свободное место и так справа —
   там правило ничего не меняет. */
.filters .seg { margin-right: auto; }

/* Пилюля-селект: системная рамка и стрелка убраны, шеврон рисуем сами.
   Он позиционирован абсолютно, по вертикали его центрирует align-items. */
/* Класс идёт вместе с .seg__item, поэтому и селектор из двух — иначе
   мобильные отступы .seg__item перебили бы обнуление */
.seg__item.seg__select {
    position: relative;
    padding: 0;
}

.seg__select:focus-within { box-shadow: var(--focus-ring); }

.seg__select select {
    appearance: none;
    height: 100%;
    padding: 0 var(--sp-6) 0 var(--sp-3);
    border: 0;
    border-radius: var(--r-sm);
    background: transparent;
    color: inherit;
    font: inherit;
    cursor: pointer;
    outline: none;
}

.seg__select svg {
    position: absolute;
    right: var(--sp-3);
    color: var(--color-text-muted);
    pointer-events: none;
}

/* Сегмент выбора внутри формы: тип события (макеты 4e, 4i). Пункт — <label>
   с radio внутри, а не ссылка, поэтому и модификатор: в поле формы сегмент
   тянулся бы во всю строку, а на макете он ровно по трём пилюлям. */
.seg--pick {
    width: fit-content;
    max-width: 100%;
}

.seg--pick .seg__item {
    position: relative;
    cursor: pointer;
}

/* Radio не прячется display:none и не уходит из потока табуляции: тип
   события должен выбираться и с клавиатуры — то же правило, что
   у .switcher--pick */
.seg--pick .seg__item input {
    position: absolute;
    width: 0;
    height: 0;
    margin: 0;
    opacity: 0;
}

/* Выбранный пункт — тем же :has(input:checked), что у .switcher--pick.
   Оформление повторяет .seg__item.is-active: сегмент в форме и сегмент
   табов выглядят одинаково. */
.seg--pick .seg__item:has(input:checked) {
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-weight: var(--fw-semibold);
}

.seg--pick .seg__item:has(input:focus-visible) { box-shadow: var(--focus-ring); }

@media (max-width: 768px) {
    /* Сегмент остаётся единым блоком: не разваливается на кнопки и не скроллится */
    .seg { height: var(--h-control-lg); }

    .seg__item {
        flex: 1;
        justify-content: center;
        padding: 0 var(--sp-2);
    }

    .seg__select select { width: 100%; }

    /* В строке фильтров сегмент занимает всю ширину: на десктопе его двигает
       вправо margin-right: auto, а в сетке автоотступ отменяет растяжение
       ячейки — селект менеджера обрезается правым краем */
    .filters .seg { margin-right: 0; }

    /* В форме сегмент встаёт во всю ширину поля: на 390px три пилюли
       по содержимому оставляют рваный край рядом с остальными полями */
    .seg--pick { width: 100%; }

    /* Шесть табов воронки со счётчиками (Клиенты, макет 3c) в строку 390px
       не помещаются. Сегмент остаётся единым блоком и переносит пилюли
       на вторую строку: ни горизонтального скролла, ни отдельных кнопок,
       ни спрятанных табов — скилл verstka. В базе (макет 2a) сегмент
       короткий, поэтому правило под модификатором, а не в .seg. */
    .seg--wrap {
        height: auto;
        flex-wrap: wrap;
    }

    /* На 390px поиск стоит над табами (макет 3c), а на десктопе они в одной
       строке: табы слева, поиск справа (3a). Порядок в разметке десктопный,
       мобильный доворачивается order — переставлять элементы местами
       в разметке ради этого незачем.

       Правило под .filters, а не в самом .seg--wrap: в Заявках (макет 8c)
       тот же сегмент стоит прямо в .content, и order там утащил бы табы
       под список. */
    .filters .seg--wrap { order: 1; }

    .seg--wrap .seg__item {
        flex: 1 0 auto;
        height: var(--h-control);
    }
}

/* --- пагинация (макеты 2a, 2c) --- */

.pager {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--sp-2);
}

.pager__info {
    flex: 1;
    font-size: var(--fs-base);
    color: var(--color-text-muted);
}

.pager__item {
    min-width: var(--h-control-sm);
    height: var(--h-control-sm);
    padding: 0 var(--sp-2);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    background: var(--color-surface);
    color: var(--color-text-secondary);
    font-size: var(--fs-base);
    transition: background var(--transition), color var(--transition);
}

.pager__item:hover { background: var(--color-surface-2); color: var(--color-text); }

.pager__item.is-active {
    border-color: var(--color-accent);
    background: var(--color-accent);
    color: var(--color-on-accent);
    font-weight: var(--fw-semibold);
}

/* Стрелка на краю списка — не ссылка, а заглушка */
.pager__item--off { color: var(--color-text-disabled); }
.pager__item--off:hover { background: var(--color-surface); color: var(--color-text-disabled); }

.pager__gap { color: var(--color-text-muted); }

@media (max-width: 768px) {
    /* Пагинация на мобильном остаётся видимой — скилл verstka */
    .pager { justify-content: center; }
    .pager__info { flex: none; width: 100%; text-align: center; }
}

/* --- карточка компании (макет 2f на десктопе, 2d на мобильном) ---
   Две колонки: слева основные поля, справа комментарий и контактные лица.
   Блок юрлиц макета не имеет и верстается теми же классами. */

.co-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
    gap: var(--sp-5);
    align-items: start;
}

.co-col {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: var(--sp-5);
}

/* Слот менеджера в шапке. ФИО выводится целиком и на мобильном — скилл verstka,
   поэтому обрезки по ширине здесь нет. */
.co-manager {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    color: var(--color-text-secondary);
}

/* Слот бывает переключателем: тогда это кнопка, но выглядит она так же —
   рамки и заливки у неё нет, есть только шеврон в конце строки. */
button.co-manager {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    text-align: left;
}

.co-fields {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--sp-4) var(--sp-6);
}

.co-field { min-width: 0; }

.co-field__label {
    margin-bottom: var(--sp-1);
    color: var(--color-text-muted);
}

/* Значение поля: телефон и WhatsApp идут с иконкой, поэтому строка,
   а не блок. Длинный адрес или email переносится, а не растягивает колонку. */
.co-field__value {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    font-weight: var(--fw-medium);
    overflow-wrap: anywhere;
}

.co-field__value svg { flex: none; }

/* Комментарий: переносы строк приходят из БД через nl2br() */
.co-text {
    color: var(--color-text-secondary);
    line-height: 1.55;
}

.co-person {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
}

.co-person__body { flex: 1; min-width: 0; }

.co-person__name {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
}

.co-person__sub {
    font-size: var(--fs-base);
    color: var(--color-text-muted);
}

.co-person__contacts {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--sp-1);
    text-align: right;
}

/* Реквизиты юрлиц стоят в блоке основных полей и свёрнуты: разворачивает
   их кнопка внизу блока, состояние держит класс на самой карточке.
   Поля внутри — те же .co-fields, поэтому раскладка у них общая с контактами:
   два столбца на десктопе, один на мобильном. */
.co-reqs {
    display: none;
    flex-direction: column;
    gap: var(--sp-4);
    padding-top: var(--sp-4);
    border-top: 1px solid var(--color-border);
}

.co-main.is-reqs-open .co-reqs { display: flex; }

/* Переключатель раскрытия: не действие формы, а раскрытие блока, поэтому
   выглядит ссылкой со шевроном, а не кнопкой из UI-кита. */
.co-reqs__btn {
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-weight: var(--fw-semibold);
    color: var(--color-accent);
    cursor: pointer;
}

.co-reqs__btn:hover { color: var(--color-accent-hover); }

/* Шеврон смотрит вниз у свёрнутого блока и вверх у раскрытого */
.co-main.is-reqs-open .co-reqs__btn svg { transform: rotate(180deg); }

/* Обе подписи лежат в разметке, видна та, что отвечает состоянию блока */
.co-reqs__hide { display: none; }
.co-main.is-reqs-open .co-reqs__show { display: none; }
.co-main.is-reqs-open .co-reqs__hide { display: inline; }

/* Юрлицо: название строкой и свой набор полей под ним */
.co-legal + .co-legal {
    padding-top: var(--sp-4);
    border-top: 1px solid var(--color-border);
}

.co-legal__head {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    margin-bottom: var(--sp-3);
}

.co-legal__name { font-weight: var(--fw-semibold); }

@media (max-width: 768px) {
    .co-grid { grid-template-columns: 1fr; gap: var(--sp-4); }
    .co-col  { gap: var(--sp-4); }

    /* Макет 2d: подпись слева, значение справа. Состав полей тот же,
       что на десктопе, — меняется только раскладка. */
    .co-fields { grid-template-columns: 1fr; gap: var(--sp-3); }

    .co-field {
        display: grid;
        grid-template-columns: var(--w-field-label) minmax(0, 1fr);
        gap: var(--sp-3);
        align-items: baseline;
    }

    .co-field__label { margin-bottom: 0; }
}

/* --- блоки активности на карточке компании (макет 2f, нижний ряд) ---
   Два ряда по два равных блока: КП и счета, под ними звонки и заказы.
   Ряд — свой .co-act, промежуток между рядами держит .content. */

.co-act {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sp-5);
    align-items: start;
}

/* Заказов с сайта у компании может не быть вовсе: звонки тогда остаются
   в ряду одни и занимают его целиком, а не половину с дырой справа */
.co-act > :only-child { grid-column: 1 / -1; }

/* Строки идут вплотную: делит их линия, а не промежуток. Лента выходит
   за боковые отступы карточки — подсветка строки при наведении должна идти
   до её краёв, как в лентах разделов (.dash-list). */
.co-act__list {
    display: flex;
    flex-direction: column;
    margin: 0 calc(var(--sp-5) * -1);
}

/* Вся строка — ссылка (скилл verstka), шеврона справа нет. Расшифровка
   звонка переносится второй строкой сама: .co-act__note занимает всю ширину */
.co-act__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-5);
    border-top: 1px solid var(--color-border);
    color: var(--color-text);
    transition: background var(--transition);
}

.co-act__row:hover {
    background: var(--color-surface-2);
    color: var(--color-text);
}

.co-act__when {
    flex: none;
    color: var(--color-text-muted);
}

/* Главное в строке: направление звонка со стрелкой, номер КП или счёта */
.co-act__main {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    font-weight: var(--fw-semibold);
    overflow: hidden;
}

.co-act__main svg { flex: none; }

/* Длительность, сумма, оценка — приглушённый хвост строки */
.co-act__meta {
    flex: none;
    color: var(--color-text-secondary);
    font-variant-numeric: tabular-nums;
}

/* Расшифровка звонка: длинная не растягивает столбец */
.co-act__note {
    flex-basis: 100%;
    min-width: 0;
    color: var(--color-text-secondary);
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

@media (max-width: 768px) {
    /* Столбцы становятся одним: состав строк тот же, что на десктопе */
    .co-act { grid-template-columns: 1fr; gap: var(--sp-4); }
}

/* --- страница правки компании (макет 2g) ---
   Раскладка та же, что на просмотре: .co-grid, .card, .co-fields — только
   вместо значений в них стоят поля формы. Своё здесь одно: строка-запись
   с шапкой действий. Тем же блоком на шаге реквизитов верстаются юрлица —
   отличается он лишь числом полей внутри. */

/* Вся страница — одна форма, и для .content она единственный блок.
   Вертикальный ритм страницы держит поэтому она сама. */
.co-form {
    display: flex;
    flex-direction: column;
    gap: var(--sp-5);
}

.co-rows {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}

.co-row {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    padding: var(--sp-3);
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
}

/* Шапка записи: чьи это данные и что с ними можно сделать. Кнопки —
   ссылки на подтверждение: вложенная форма внутри формы недопустима. */
.co-row__head {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}

.co-row__title {
    flex: 1;
    min-width: 0;
    font-weight: var(--fw-semibold);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.co-row__actions {
    flex: none;
    display: flex;
    gap: var(--sp-2);
}

.co-row__fields {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--sp-3);
}

/* Название юрлица и юр. адрес в половину строки не помещаются */
.co-row__fields .field--wide { grid-column: 1 / -1; }

/* Добавление строки: пунктирная кнопка во всю ширину блока */
.co-add {
    width: 100%;
    border: 1px dashed var(--color-border);
    background: none;
    color: var(--color-accent);
    padding: var(--sp-2);
    border-radius: var(--r-sm);
}

.co-add:hover { background: var(--color-surface-2); color: var(--color-accent); }

@media (max-width: 768px) {
    /* Та же форма в одну колонку — спека, чек-лист 25 */
    .co-form { gap: var(--sp-4); }
    .co-row__fields { grid-template-columns: 1fr; }
}

/* --- дропдаун (макет 2f: слот менеджера и статус в шапке карточки) ---
   Меню всегда в разметке, его рисует сервер; app.js только ставит .is-open.
   Пункты — кнопки отправки формы, поэтому каждый компонент несёт свой <form>. */

.drop { position: relative; }

.drop__toggle { cursor: pointer; }

.drop__toggle:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.drop__menu {
    display: none;
    position: absolute;
    z-index: 20;
    top: calc(100% + var(--sp-2));
    right: 0;
    min-width: var(--w-drop);
    padding: var(--sp-1);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    box-shadow: var(--shadow-md);
}

.drop.is-open .drop__menu { display: block; }

.drop__item {
    width: 100%;
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3);
    border: 0;
    border-radius: var(--r-sm);
    background: none;
    color: var(--color-text);
    font-family: var(--font);
    font-size: var(--fs-base);
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--transition);
}

.drop__item:hover { background: var(--color-surface-2); color: var(--color-text); }

.drop__item.is-active {
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-weight: var(--fw-semibold);
}

.drop__item--danger { color: var(--color-danger); }
.drop__item--danger:hover { background: var(--color-danger-soft); color: var(--color-danger); }

/* Отделяет «Освободить» от списка сотрудников */
.drop__sep {
    margin: var(--sp-1) 0;
    border-top: 1px solid var(--color-border);
}

@media (max-width: 768px) {
    /* На узком экране меню не должно уезжать за край */
    .drop__menu { min-width: 0; max-width: 78vw; }
}

/* --- модалка (макет 11c на десктопе, 11e снизу на мобильном) ---
   Один компонент на весь проект. Состояние держит сервер: попап живёт
   в адресе страницы, поэтому классов «открыто/закрыто» здесь нет.
   Закрытие — ссылками на страницу без параметра плюс Esc в app.js. */

.modal {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: var(--sp-8) var(--sp-4);
    overflow-y: auto;
}

.modal__backdrop {
    position: fixed;
    inset: 0;
    background: var(--color-backdrop);
}

.modal__window {
    position: relative;
    width: 100%;
    max-width: var(--w-modal);
    background: var(--color-surface);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-lg);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* «Ручка» нижнего листа — только на мобильном */
.modal__handle { display: none; }

.modal__head {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-5);
    border-bottom: 1px solid var(--color-border);
}

.modal__title {
    flex: 1;
    font-size: var(--fs-lg);
    font-weight: var(--fw-bold);
}

/* Вторая строка заголовка — дата и пилюля статуса заявки (макет 8a).
   Начертание своё: подзаголовок не спорит с названием окна. */
.modal__sub {
    display: block;
    margin-top: var(--sp-1);
    font-size: var(--fs-base);
    font-weight: var(--fw-regular);
    color: var(--color-text-muted);
}

.modal__close {
    display: flex;
    color: var(--color-text-muted);
    transition: color var(--transition);
}

.modal__close:hover { color: var(--color-text); }

.modal__body {
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
    padding: var(--sp-5);
}

/* Вторая половина тела, отрезанная формой: у блока компании в заявке свои
   формы, а вложенных <form> не бывает. Верх уже отбит нижним отступом
   первой половины, второй раз отбивать не нужно. */
.modal__body--next { padding-top: 0; }

.modal__foot {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-5);
    border-top: 1px solid var(--color-border);
}

/* Действие, уводящее влево от «Отмены»: удаление пользователя */
.modal__foot-start { margin-right: auto; }

.modal__text {
    font-size: var(--fs-md);
    line-height: 1.5;
}

/* Предупреждение внутри подтверждения (макет 11e) */
.modal__warning {
    border-radius: var(--r-md);
    padding: var(--sp-3) var(--sp-4);
    background: var(--color-danger-soft);
    color: var(--color-danger);
    font-size: var(--fs-base);
    line-height: 1.5;
}

@media (max-width: 768px) {
    .modal {
        align-items: flex-end;
        padding: 0;
    }

    .modal__window {
        max-width: none;
        border-radius: var(--r-lg) var(--r-lg) 0 0;
        border-bottom: none;
    }

    .modal__handle {
        display: block;
        width: var(--sp-8);
        height: var(--sp-1);
        margin: var(--sp-2) auto 0;
        border-radius: var(--r-full);
        background: var(--color-text-disabled);
    }

    .modal__foot .btn { flex: 1; height: var(--h-control-lg); }
    .modal__foot-start { margin-right: 0; }
}

/* --- блок «Найдены похожие компании» (макеты 2h, 2e) ---
   Предупреждение внутри формы создания: запрета на сохранение нет,
   но до отметки флажка кнопка недоступна — это состояние макета. */

.dup {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    padding: var(--sp-4);
    border: 1px solid var(--color-warning);
    border-radius: var(--r-lg);
    background: var(--color-warning-soft);
}

.dup__head {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    color: var(--color-warning);
    font-size: var(--fs-md);
    font-weight: var(--fw-bold);
}

.dup__mark {
    width: var(--sp-6);
    height: var(--sp-6);
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--r-full);
    background: var(--color-warning);
    color: var(--color-on-accent);
}

.dup__item {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-3);
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    background: var(--color-surface);
}

.dup__body { flex: 1; min-width: 0; }

.dup__name { font-weight: var(--fw-semibold); }

.dup__sub { color: var(--color-text-muted); }

/* Подтверждение занимает две строки, флажок держится за первую */
.dup__confirm { align-items: flex-start; }

/* --- лента менеджеров (макет 3a) ---
   Компонент общий для шести разделов, поэтому классы не клиентские.
   Пункты — обычные ссылки, JS лента не требует.
   Лента не карточка: ни подложки, ни рамки, ни отступов — стоит прямо
   на фоне страницы. На макетах 3a, 4a, 6a и 7a она нарисована карточкой,
   отступление осознанное и записано в docs/frontend/pages.md. Сбросы
   оставлены явными: класс card на ленте висел на всех семи страницах,
   и без них случайно вернувшийся card снова её поднимет. */

.switcher {
    display: flex;
    align-items: center;
    gap: var(--sp-6);
    padding: 0;
    background: none;
    border: none;
    box-shadow: none;
}

.switcher__label {
    flex: none;
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    color: var(--color-text-secondary);
}

.switcher__list {
    display: flex;
    gap: var(--sp-5);
    padding-top: 3px;
}

.switcher__item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--sp-2);
    width: var(--w-switcher-item);
    flex: none;
    color: inherit;
    text-decoration: none;
}

.switcher__avatar {
    width: var(--sz-avatar-switcher);
    height: var(--sz-avatar-switcher);
    font-size: var(--fs-md);
}

/* Выбранный отмечен кольцом вокруг аватара, а не заливкой всего пункта.
   Кольцо — тот же акцентный контур, что у фокуса. Подложка и цвет нужны
   только заглушке с инициалами: под фото их не видно, но и мешать нечему,
   а разбивать правило на два селектора ради этого незачем. */
.switcher__item.is-active .switcher__avatar {
    box-shadow: var(--focus-ring);
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

.switcher__name {
    max-width: 100%;
    font-size: var(--fs-sm);
    color: var(--color-text-secondary);
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.switcher__item:hover .switcher__name { color: var(--color-text); }

.switcher__item.is-active .switcher__name {
    color: var(--color-accent);
    font-weight: var(--fw-semibold);
}

@media (max-width: 768px) {
    /* Подпись «Менеджер:» на узком экране не выводится (макет 3c): ленту
       и так видно, а место дороже. Сама лента прокручивается вбок, но
       ничего не прячет — в ней все, кого отдал user_switcher_for(). */
    .switcher__label { display: none; }

    .switcher__list {
        gap: var(--sp-4);
        overflow-x: auto;
    }

    .switcher__item { width: var(--w-switcher-item-sm); }

    .switcher__avatar {
        width: var(--sz-avatar-switcher-sm);
        height: var(--sz-avatar-switcher-sm);
        font-size: var(--fs-base);
    }

    /* Подпись короче десктопной, но не обрезанная: ФИО и так в одну строку
       с многоточием, а на 64px помещается «Дмитрий О.» с макета */
    .switcher__name { font-size: var(--fs-xs); }
}

/* --- лента менеджеров и кнопка действия одной строкой (Календарь) ---
   Кнопка всегда прижата к правому краю, лента забирает остаток ширины.
   Каждая часть выводится по своему условию, поэтому кнопку двигает
   margin-left, а не порядок в разметке. */

.switcher-row {
    display: flex;
    align-items: center;
    gap: var(--sp-4);
}

/* min-width: 0 обязателен: без него flex-элемент не сжимается ниже
   содержимого и лента вместо прокрутки распирает страницу */
.switcher-row .switcher {
    flex: 1;
    min-width: 0;
}

/* Лента длиннее строки прокручивается вбок и ничего не прячет — в ней
   все, кого отдал user_switcher_for() */
.switcher-row .switcher__list { overflow-x: auto; }

.switcher-row__add {
    flex: none;
    margin-left: auto;
}

/* Пара к .switcher-row__add: видна всегда ровно одна из двух */
.switcher-row__plus { display: none; }

@media (max-width: 768px) {
    .switcher-row__add { display: none; }

    .switcher-row__plus {
        display: inline-flex;
        flex: none;
        margin-left: auto;
        width: var(--h-control);
        padding: 0;
        font-size: var(--fs-lg);
    }
}

/* В Документах кнопка действия — выпадающий список, и к правому краю
   прижимается его обёртка: сами кнопки лежат внутри неё, а не в строке */
.switcher-row__actions {
    flex: none;
    margin-left: auto;
}

/* --- плитки исполнителей в форме задачи (макет 5g) ---
   Тот же компонент, что лента менеджеров: аватар, ширина пункта, подпись
   и поведение на 768px достаются даром. Отличие одно — пункт это <label>
   с radio внутри. */

/* Плиток бывает больше, чем помещается в строку модалки: лента менеджеров
   стоит во всю ширину страницы, а эта — в окне 560px. */
.switcher--pick .switcher__list { flex-wrap: wrap; }

.switcher--pick .switcher__item {
    position: relative;
    cursor: pointer;
}

/* Radio не прячется display:none и не уходит из потока табуляции:
   выбор исполнителя должен работать и с клавиатуры. */
.switcher--pick .switcher__item input {
    position: absolute;
    width: 0;
    height: 0;
    margin: 0;
    opacity: 0;
}

/* Кольцо выбранного — тем же :has(input:checked), что у .radio-card.
   Заливка и цвет из .is-active здесь не нужны: у аватара свой цвет,
   и на макете выбранная плитка отличается только кольцом и подписью. */
.switcher--pick .switcher__item:has(input:checked) .switcher__avatar {
    box-shadow: var(--focus-ring);
}

.switcher--pick .switcher__item:has(input:focus-visible) .switcher__avatar {
    box-shadow: var(--focus-ring);
}

.switcher--pick .switcher__item:has(input:checked) .switcher__name {
    color: var(--color-accent);
    font-weight: var(--fw-semibold);
}

/* --- карточка клиента (макеты 3a, 3c) ---
   Список клиентов — сетка карточек, а не таблица: значений у строки четыре,
   в колонки они не выстраиваются. */

.clients {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--sp-4);
}

/* Карточка целиком — ссылка на компанию, шеврона справа нет */
.client-card {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    padding: var(--sp-5);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-sm);
    color: inherit;
    text-decoration: none;
    transition: border-color var(--transition);
}

.client-card:hover { border-color: var(--color-accent); }

.client-card__head {
    display: flex;
    align-items: flex-start;
    gap: var(--sp-3);
}

.client-card__body { flex: 1; min-width: 0; }

.client-card__name {
    font-size: var(--fs-md);
    font-weight: var(--fw-bold);
}

.client-card__city {
    font-size: var(--fs-base);
    color: var(--color-text-muted);
}

/* Нижняя строка прижата к низу: карточки в ряду одной высоты, а контакт
   с датой стоят на одном уровне независимо от длины названия */
.client-card__foot {
    display: flex;
    align-items: baseline;
    gap: var(--sp-3);
    margin-top: auto;
    font-size: var(--fs-base);
    color: var(--color-text-secondary);
}

.client-card__contact {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Контактов нет — строка всё равно есть, иначе карточка схлопывается */
.client-card__contact--none { color: var(--color-text-muted); }

.client-card__date {
    flex: none;
    color: var(--color-text-muted);
}

@media (max-width: 768px) {
    /* Одна колонка (макет 3c). Карточка не разбирается на строки и не теряет
       ни бейджа, ни контакта, ни даты — меняется только раскладка сетки. */
    .clients { grid-template-columns: minmax(0, 1fr); }

    .client-card { padding: var(--sp-4); }
}

/* --- строка задачи (макеты 5a, 5c) ---
   Список задач — стопка строк в одной карточке: значения выстраиваются
   в колонки, поэтому карточек-плиток, как у клиентов, здесь нет. */

/* Строки лежат вплотную и разделяются линиями, поэтому отступ карточки
   снимается: подсветка строки идёт от края до края */
.tasks {
    padding: 0;
    overflow: hidden;
}

/* Вся строка — ссылка в модалку задачи, шеврона справа нет */
.task-row {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-5);
    border-bottom: 1px solid var(--color-border);
    color: inherit;
    text-decoration: none;
    transition: background var(--transition);
}

.task-row:last-child { border-bottom: none; }
.task-row:hover { background: var(--color-surface-2); }

/* Строка — главная ссылка списка, и с клавиатуры по ней должно быть видно,
   где стоишь. Кольцо рисуется внутрь, а не наружу как у кнопок: строка идёт
   от края до края карточки, у которой overflow: hidden, — внешнее его срежет. */
.task-row:focus-visible {
    outline: none;
    box-shadow: inset var(--focus-ring);
}

.task-row__body { flex: 1; min-width: 0; }

.task-row__title {
    display: block;
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
}

.task-row__sub {
    display: block;
    font-size: var(--fs-base);
    color: var(--color-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Пилюли, компания и исполнитель — одной группой: на десктопе она стоит
   в той же строке, на мобильном целиком уезжает во вторую */
.task-row__meta {
    flex: none;
    display: flex;
    align-items: center;
    gap: var(--sp-3);
}

.task-row__company,
.task-row__none {
    font-size: var(--fs-base);
    color: var(--color-text-muted);
    white-space: nowrap;
}

/* Ширина фиксированная, как у колонки менеджера в списке компаний: иначе
   длинное имя сдвигает аватары и они перестают стоять шагом */
.task-row__who {
    flex: none;
    width: var(--w-col-manager);
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}

.task-row__name {
    min-width: 0;
    font-size: var(--fs-base);
    color: var(--color-text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Квадрат слева — индикатор, а не форма: он ведёт в ту же модалку.
   Рамка красная у просроченной, заливка у выполненной. */
.task-check {
    flex: none;
    width: var(--sp-5);
    height: var(--sp-5);
    border: 1px solid var(--color-text-muted);
    border-radius: var(--r-sm);
    display: flex;
    align-items: center;
    justify-content: center;
}

.task-check--over { border-color: var(--color-danger); }

.task-check--done {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: var(--color-on-accent);
}

/* Пилюли и подписи в модалке задачи (макеты 5e, 5i): дедлайн, автор,
   исполнитель, компания. От ряда в строке списка отличается переносом:
   в модалке ФИО выводятся целиком, и на 390px они в строку не помещаются. */
.task-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-base);
    color: var(--color-text-muted);
}

@media (max-width: 768px) {
    /* Строка становится карточкой (макет 5c): чекбокс с заголовком первой
       строкой, пилюли с исполнителем второй. Данные те же, что на десктопе. */
    .tasks {
        background: none;
        border: none;
        box-shadow: none;
        display: flex;
        flex-direction: column;
        gap: var(--sp-2);
        overflow: visible;
    }

    .task-row {
        display: grid;
        grid-template-columns: auto minmax(0, 1fr);
        /* Вторая строка начинается в колонке заголовка, а не под чекбоксом
           (макет 5c): пилюли выровнены по тексту, а не по индикатору */
        grid-template-areas:
            "check body"
            ".     meta";
        align-items: start;
        gap: var(--sp-2) var(--sp-3);
        background: var(--color-surface);
        border: 1px solid var(--color-border);
        border-radius: var(--r-lg);
        box-shadow: var(--shadow-sm);
        padding: var(--sp-3) var(--sp-4);
    }

    .task-row:last-child { border-bottom: 1px solid var(--color-border); }

    .task-check     { grid-area: check; }
    .task-row__body { grid-area: body; }

    .task-row__meta {
        grid-area: meta;
        flex-wrap: wrap;
        gap: var(--sp-2);
    }

    /* Ширины колонки на мобильном нет — исполнитель встаёт в общий ряд
       пилюль последним и занимает столько, сколько ему нужно */
    .task-row__who { width: auto; }
}

/* --- календарь: карточки «Сегодня» и «Завтра» (макеты 4a, 4c) ---
   Две карточки в ряд на десктопе, в колонку на телефоне. Состав данных
   одинаковый, меняется только раскладка. */

.cal-cards {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--sp-5);
    align-items: start;
}

.cal-card {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}

.cal-card__head {
    display: flex;
    align-items: baseline;
    gap: var(--sp-2);
}

/* Одна и та же подпись в двух написаниях: «понедельник, 20 июля» на десктопе
   и «пн, 20 июля» на 390px (спека, раздел 5). Сокращать слово CSS не умеет,
   поэтому в разметке их два, и лишнее гасится ниже. Данные это не прячет —
   написания равнозначны. */
.cal-card__date,
.cal-card__date-sm {
    font-size: var(--fs-base);
    color: var(--color-text-muted);
}

.cal-card__date-sm { display: none; }

.cal-card__none {
    font-size: var(--fs-base);
    color: var(--color-text-muted);
}

/* --- строка события (макеты 4a, 4c, 4g) ---
   Одна и та же строка в карточках дней и в модалке дня, партиал
   public/event-row.inc.php. Вся строка — ссылка в карточку события. */

.ev-row {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-3);
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    color: inherit;
    text-decoration: none;
    transition: background var(--transition);
}

.ev-row:hover { background: var(--color-surface-2); }

.ev-row:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}

.ev-row__icon {
    flex: none;
    width: var(--h-control-sm);
    height: var(--h-control-sm);
    border-radius: var(--r-md);
    display: flex;
    align-items: center;
    justify-content: center;
}

.ev-row__body { flex: 1; min-width: 0; }

/* Длинный текст события обрывается многоточием, а не переносится:
   строк в карточке дня бывает много (спека, раздел 6) */
.ev-row__title {
    display: block;
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ev-row__sub {
    display: block;
    font-size: var(--fs-base);
    color: var(--color-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Выполненное событие (decisions.md п. 9): текст, подпись компании и подложка
   иконки гаснут, слева от времени зелёная галочка. Зачёркивания нет — на
   макетах этого состояния нет вовсе, вид принят решением владельца проекта.
   Подложка гасится после .ev-row__icon.ev--*: те объявлены ниже, поэтому
   селектор здесь длиннее на один класс. */
.ev-row--done .ev-row__title,
.ev-row--done .ev-row__sub { color: var(--color-text-muted); }

.ev-row--done .ev-row__icon.ev--call,
.ev-row--done .ev-row__icon.ev--meeting,
.ev-row--done .ev-row__icon.ev--event {
    background: var(--color-surface-2);
    color: var(--color-text-muted);
}

.ev-row__done {
    display: inline-flex;
    vertical-align: middle;
    margin-right: var(--sp-1);
    color: var(--color-success);
}

/* Цвет типа события: звонок синий, встреча зелёная, событие оранжевое
   (спека, раздел 6). Класс задаёт только цвет — им красятся и иконка
   строки, и точка в сетке. */
.ev--call    { color: var(--color-accent); }
.ev--meeting { color: var(--color-success); }
.ev--event   { color: var(--color-warning); }

/* Подложка под иконкой — та же тройка, но приглушённая */
.ev-row__icon.ev--call    { background: var(--color-accent-soft); }
.ev-row__icon.ev--meeting { background: var(--color-success-soft); }
.ev-row__icon.ev--event   { background: var(--color-warning-soft); }

/* Пилюля типа в карточке события — та же тройка, что у иконки.
   Цвет текста приходит от .ev--*: оно объявлено ниже .tag и потому сильнее */
.tag.ev--call    { background: var(--color-accent-soft); }
.tag.ev--meeting { background: var(--color-success-soft); }
.tag.ev--event   { background: var(--color-warning-soft); }

/* --- модалка дня и карточка события (макеты 4a popup, 4g) --- */

/* Список дня внутри модалки: строки стоят плотнее, чем блоки .modal__body,
   иначе день из пяти событий не помещается на 390px */
.ev-list {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}

/* Тот же список без рамок вокруг строк — блок событий дашборда (макет 1c):
   карточка блока уже нарисована вокруг, и вторая рамка внутри читается
   как второй уровень, которого на макете нет.

   Список тянется за поля карточки, а строка забирает их себе отступами:
   подсветка при наведении идёт от края до края, как у задач и звонков,
   а текст остаётся вровень с заголовком блока. */
.ev-list--plain { margin: 0 calc(var(--sp-5) * -1); }

.ev-list--plain .ev-row {
    padding: var(--sp-2) var(--sp-5);
    border: none;
    border-radius: 0;
}

/* «+ Событие на этот день» (макет 4g): пунктиром во всю ширину — это
   продолжение списка, а не главное действие окна */
.ev-add {
    width: 100%;
    height: var(--h-control-lg);
    border: 1px dashed var(--color-border);
    background: none;
    color: var(--color-accent);
}

.ev-add:hover { background: var(--color-surface-2); color: var(--color-accent); }

/* Тип и компания в карточке события. Свой класс, а не .task-meta:
   ряд тот же, но модули разметку друг у друга не занимают */
.ev-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-base);
    color: var(--color-text-muted);
}

/* --- сетка месяца (макеты 4a, 4c) ---
   Недели с понедельника по воскресенье, 35 или 42 клетки. Клетка — ссылка
   в модалку дня, дни соседних месяцев приглушены, но кликабельны. */

.cal {
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
}

.cal__head {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
}

.cal__title { flex: 1; }

.cal__nav {
    flex: none;
    width: var(--h-control-sm);
    height: var(--h-control-sm);
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-text-secondary);
    text-decoration: none;
    transition: background var(--transition);
}

.cal__nav:hover { background: var(--color-surface-2); color: var(--color-text); }

.cal__dow {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: .4px;
}

.cal__dow > span { padding: var(--sp-1) var(--sp-3); }

/* Суббота и воскресенье красные (макет 4a). Они всегда шестым и седьмым
   столбцом — отдельного класса это не требует. */
.cal__dow > span:nth-child(6),
.cal__dow > span:nth-child(7) { color: var(--color-danger); }

/* Линии рисуются клетками: сетке остаются верхняя и левая, иначе
   у крайних клеток граница вышла бы двойной */
.cal__grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    border-top: 1px solid var(--color-border);
    border-left: 1px solid var(--color-border);
}

.cal__day {
    display: block;
    min-height: var(--h-cal-day);
    padding: var(--sp-2) var(--sp-3);
    border-right: 1px solid var(--color-border);
    border-bottom: 1px solid var(--color-border);
    font-size: var(--fs-base);
    color: var(--color-text);
    text-decoration: none;
    transition: background var(--transition);
}

.cal__day:hover { background: var(--color-surface-2); }

/* Кольцо внутрь, а не наружу: клетки стоят вплотную, внешнее срезалось бы
   соседней — то же правило, что у строки задачи */
.cal__day:focus-visible {
    outline: none;
    box-shadow: inset var(--focus-ring);
}

.cal__day--out .cal__num { color: var(--color-text-disabled); }

.cal__day.is-active { background: var(--color-accent-soft); }

.cal__day.is-active .cal__num {
    color: var(--color-accent);
    font-weight: var(--fw-bold);
}

.cal__num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* Сегодняшнее число — синий круг (макет 4a). Правило стоит после .is-active:
   у открытого сегодняшнего дня побеждает круг, а не заливка клетки.
   Отрицательные отступы возвращают круг на место числа. */
.cal__day--today .cal__num {
    width: var(--sz-cal-today);
    height: var(--sz-cal-today);
    margin: calc(var(--sp-1) * -1) 0 0 calc(var(--sp-1) * -1);
    border-radius: var(--r-full);
    background: var(--color-accent);
    color: var(--color-on-accent);
    font-weight: var(--fw-bold);
}

.cal__dots {
    display: flex;
    gap: var(--sp-1);
    margin-top: var(--sp-1);
}

/* Цвет точки берётся у .ev--*: типы красятся одними и теми же классами */
.cal__dot {
    width: var(--sz-cal-dot);
    height: var(--sz-cal-dot);
    border-radius: var(--r-full);
    background: currentColor;
}

@media (max-width: 768px) {
    /* Карточки дней в одну колонку (макет 4c), состав данных тот же */
    .cal-cards {
        grid-template-columns: 1fr;
        gap: var(--sp-4);
    }

    .cal-card__date    { display: none; }
    .cal-card__date-sm { display: inline; }

    /* Все семь колонок остаются: сетка ужимается по ширине, число встаёт
       по центру клетки, а не в её левый верхний угол */
    .cal__dow > span {
        padding: var(--sp-1);
        text-align: center;
    }

    .cal__day {
        min-height: var(--h-control-lg);
        padding: var(--sp-2) var(--sp-1);
        text-align: center;
    }

    .cal__day--today .cal__num { margin: 0; }

    .cal__dots { justify-content: center; }

    /* Футер карточки события несёт до четырёх кнопок («Удалить», «Отмена»,
       «Изменить», «Вернуть в работу»), а .modal__foot .btn на мобильном даёт
       каждой flex: 1 — вчетвером они в строку не встают. Уходит «Отмена»:
       это дублирующий способ закрытия, окно закрывается ещё крестиком, фоном,
       Esc и ручкой нижнего листа. Прячется действие, а не данные, — правило
       скилла verstka про состав данных на 390px не нарушено. Класс вешается
       только когда в футере есть другие кнопки: у постороннего с правом
       просмотра «Закрыть» единственная, и она остаётся. */
    .ev-cancel { display: none; }
}

/* --- конструктор КП (макет 6e на десктопе, 6g на мобильном) ---
   Две колонки: каталог слева, сводка справа липкой панелью. На 768px
   колонки становятся друг под другом, сводка перестаёт быть липкой —
   иначе она закрыла бы половину экрана телефона. */

.pnew {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--w-summary);
    gap: var(--sp-5);
    align-items: start;
}

.pnew__main {
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
    min-width: 0;
}

/* --- каталог сайта аккордеоном (макет 6e) ---
   Категории и подкатегории — <details>: раскрытие работает и без JS,
   с клавиатуры тоже. Строки товаров лежат вплотную и разделяются линиями,
   поэтому отступ карточки снимается, как у .tasks. */

.pcat { padding: 0; }

.pcat__search { padding: var(--sp-5) var(--sp-5) var(--sp-4); }

.pcat__empty {
    padding: 0 var(--sp-5) var(--sp-5);
    color: var(--color-text-muted);
}

.pcat__head {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-5);
    border-top: 1px solid var(--color-border);
    background: var(--color-surface-2);
    cursor: pointer;
    font-weight: var(--fw-bold);
    list-style: none;
}

/* Своя стрелка вместо системного треугольника: он рисуется по-разному
   в разных браузерах и не красится токеном */
.pcat__head::-webkit-details-marker { display: none; }

.pcat__head::before {
    content: '';
    width: 0;
    height: 0;
    flex: none;
    border-left: 5px solid currentColor;
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    color: var(--color-text-muted);
    transition: transform var(--transition);
}

.pcat__cat1[open] > .pcat__head::before,
.pcat__cat2[open] > .pcat__head::before {
    transform: rotate(90deg);
    color: var(--color-accent);
}

/* Подкатегория лежит на общем фоне и с отступом: вложенность видна
   отступом, а не рамкой */
.pcat__head--sub {
    padding-left: var(--sp-8);
    background: none;
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
}

.pcat__title { flex: 1; min-width: 0; }

.pcat__count {
    font-size: var(--fs-base);
    font-weight: var(--fw-regular);
    color: var(--color-text-muted);
}

/* Строка товара. Миниатюры нет: она живёт только в PDF (спека модуля,
   раздел 11), а повторять одну заглушку в каждой строке — шум. */
.pitem {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--w-col-price) auto;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-5) var(--sp-3) var(--sp-12);
    border-top: 1px solid var(--color-border);
}

.pitem__body {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
}

.pitem__img img {
    max-width: var(--sz-thumb);
    max-height: var(--sz-thumb);
}
.pitem__title { font-weight: var(--fw-semibold); }

.pitem__info { min-width: 0; }
.pitem__reference {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-1) var(--sp-2);
    margin-top: var(--sp-1);
    font-size: var(--fs-base);
    color: var(--color-text-muted);
}
.pitem__reference > span { white-space: nowrap; }
.pitem__reference-dhz::before {
    content: '/';
    margin-right: var(--sp-2);
    color: var(--color-border);
}
.pitem__reference-value {
    font-weight: var(--fw-medium);
    font-variant-numeric: tabular-nums;
}

/* Характеристики приходят из item.descr и бывают длиной в абзац —
   в строку каталога они встают одной строкой с многоточием */
.pitem__sub {
    font-size: var(--fs-base);
    color: var(--color-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.pitem__price {
    font-weight: var(--fw-semibold);
    text-align: right;
}

.pitem__price-button,
.pitem__price-input {
    width: 100%;
    min-width: 0;
    font: inherit;
    color: inherit;
    text-align: inherit;
    padding: var(--sp-1);
    border: 1px solid transparent;
    border-radius: var(--r-md);
    background: transparent;
}

.pitem__price-button { cursor: pointer; }
.pitem__price-button:hover { color: var(--color-accent); }
.pitem__price-input { border-color: var(--color-border); }
.pitem__price-button:focus-visible,
.pitem__price-input:focus { outline: 1px solid var(--color-accent); }

/* Пометка вместо ссылки: товар удалён из каталога сайта, но КП цело */
.pitem__gone {
    margin-left: var(--sp-2);
    font-size: var(--fs-base);
    font-weight: var(--fw-regular);
    color: var(--color-text-muted);
}

/* --- степпер количества (макет 6e) ---
   Рамка акцентная у выбранной позиции — состояние показывает :has(),
   отдельного класса от сервера не требуется. */

.stepper {
    display: flex;
    align-items: center;
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    overflow: hidden;
}

.stepper__btn {
    width: var(--h-control-sm);
    height: var(--h-control-sm);
    border: none;
    background: none;
    color: var(--color-accent);
    font: inherit;
    font-size: var(--fs-lg);
    cursor: pointer;
}

.stepper__value {
    width: var(--w-stepper-value);
    height: var(--h-control-sm);
    border: none;
    border-left: 1px solid var(--color-border);
    border-right: 1px solid var(--color-border);
    background: none;
    color: var(--color-text-muted);
    font: inherit;
    font-weight: var(--fw-semibold);
    text-align: center;
}

.stepper__value:focus { outline: none; box-shadow: var(--focus-ring); }

/* Выбранная позиция обведена акцентом. Класс вешает proposal.js: значение
   поля меняется свойством, и селектор по атрибуту за ним не поспевает. */
.stepper.is-on { border-color: var(--color-accent); }
.stepper.is-on .stepper__value { color: var(--color-accent); }

/* --- произвольные позиции (decisions.md п. 6) --- */

.pcustom__head {
    font-size: var(--fs-md);
    font-weight: var(--fw-bold);
}

.pcustom__row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--w-col-price) var(--w-col-discount);
    gap: var(--sp-2);
}

/* --- сводка КП и итог сохранённого КП (макеты 6e, 6i) ---
   Один компонент на обе страницы: в конструкторе он считается на лету,
   в карточке приходит готовым от сервера. */

.summary {
    position: sticky;
    top: var(--sp-6);
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
}

.summary__head {
    font-size: var(--fs-md);
    font-weight: var(--fw-bold);
}

.summary__list {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
}

.summary__none { color: var(--color-text-muted); }

.summary__item {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}

.summary__item-name { flex: 1; min-width: 0; }
.summary__item-qty  { color: var(--color-text-muted); }

.summary__item-sum {
    font-weight: var(--fw-semibold);
    white-space: nowrap;
}

.summary__totals {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    padding-top: var(--sp-3);
    border-top: 1px solid var(--color-border);
}

.summary__row {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}

.summary__label { flex: 1; min-width: 0; color: var(--color-text-secondary); }

.summary__row--muted { font-size: var(--fs-base); color: var(--color-text-muted); }
.summary__row--muted .summary__label { color: inherit; }

/* Скидка — выигрыш клиента, поэтому цвет успеха (макет 6e) */
.summary__row--cut { font-size: var(--fs-base); color: var(--color-success); }
.summary__row--cut .summary__label { color: inherit; }

.summary__row--total { align-items: baseline; }
.summary__row--total .summary__label { font-weight: var(--fw-bold); color: var(--color-text); }

.summary__total {
    font-size: var(--fs-2xl);
    font-weight: var(--fw-black);
}

.summary__field {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}

.summary__field .input {
    width: var(--w-discount-field);
    height: var(--h-control-sm);
    text-align: right;
}

.summary__unit { color: var(--color-text-secondary); }

/* Скидка сохранённого КП — та же строка сводки, но форма с кнопкой:
   на 6i кнопка живёт в шапке, на 6k — рядом с полем (спека, раздел 11) */
.summary__save {
    height: var(--h-control-sm);
    padding: 0 var(--sp-3);
    font-size: var(--fs-base);
}

/* --- сохранённое КП (макет 6i на десктопе, 6k на мобильном) --- */

.pcard {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--w-summary);
    gap: var(--sp-5);
    align-items: start;
}

/* Подпись под заголовком: дата, автор, контакт, срок действия */
.page-head__text { min-width: 0; }

.page-head__sub {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
    margin-top: var(--sp-1);
    font-size: var(--fs-base);
    color: var(--color-text-muted);
}

.page-head__sub > span + span::before { content: '· '; }

/* Таблица позиций поверх общего компонента .list: свой набор колонок,
   на 768px строка складывается в карточку тем же способом, что везде. */

.list--pitem .list__head,
.list--pitem .list__row {
    grid-template-columns: minmax(var(--w-col-name-min), 1.6fr)
                           minmax(0, var(--w-col-price))
                           minmax(0, var(--w-col-discount))
                           minmax(0, var(--w-col-price));
    grid-template-areas: "name price qty sum";
}

.list--pitem .list__cell--name { font-weight: var(--fw-semibold); }

.list__cell--price { grid-area: price; min-width: 0; color: var(--color-text-secondary); }
.list__cell--qty   { grid-area: qty;   min-width: 0; color: var(--color-text-secondary); }

.list__cell--sum {
    grid-area: sum;
    min-width: 0;
    font-weight: var(--fw-semibold);
    text-align: right;
}

/* Строки таблицы никуда не ведут — подсветка наведения только сбивала бы
   с толку, кликать в них нечего, кроме ссылки на товар */
.list--pitem .list__row:hover { background: none; }

@media (max-width: 1024px) {
    /* Обе страницы КП в одну колонку (макеты 6g, 6k): сводка встаёт под
       каталогом и перестаёт быть липкой, состав данных тот же */
    .pnew,
    .pcard { grid-template-columns: minmax(0, 1fr); }

    .summary { position: static; }

    .pcat__head     { padding: var(--sp-3) var(--sp-4); }
    .pcat__head--sub { padding-left: var(--sp-6); }

    /* Строка товара столбиком: название, ниже цена и степпер в одну строку */
    .pitem {
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas:
            "body  body"
            "price qty";
        padding: var(--sp-3) var(--sp-4);
    }

    .pitem__body  { grid-area: body; }
    .pitem__price { grid-area: price; text-align: left; }
    .pitem        > .stepper { grid-area: qty; }

    /* Название на всю ширину, цена и количество под ним */
    .pcustom__row {
        grid-template-columns: minmax(0, 1fr) var(--w-col-discount);
        grid-template-areas:
            "name  name"
            "price qty";
    }

    .pcustom__name  { grid-area: name; }
    .pcustom__price { grid-area: price; }
    .pcustom__qty   { grid-area: qty; }
}

@media (max-width: 768px) {
    /* Карточка 6k: позиция и сумма первой строкой, ниже цена и количество.
       Мобильная точка перелома одна на проект (скилл verstka), и это та же ширина,
       на которой .list прячет шапку колонок: раньше строки складывались
       в карточки уже на 1024px, а шапка над ними оставалась табличной. */
    .list--pitem .list__row {
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas:
            "name  sum"
            "price qty";
        gap: var(--sp-2) var(--sp-1);
    }

    .list--pitem .list__cell--qty { text-align: right; }
}

/* --- списки документов (макеты 7a и 7e на десктопе, 7i на мобильном) ---
   Два набора колонок поверх общего компонента .list: у счёта есть статус
   и дата оплаты, у договора их нет. Колонки «Клиент», «Сумма», «№» и «Дата»
   переиспользуются из списка КП. Таба «Акты» нет: акты — v5. */

/* Семь колонок — самый плотный список в проекте: жёсткие ширины плюс
   пилюля статуса и кнопка акта на 1024px не помещались вовсе, и клиент
   оставался без единого пикселя. Пол у клиента, остальные уступают.
   Ширина по содержимому (auto) здесь не годится: шапка и строки — две
   независимые сетки, и в шапке нет ни пилюли, ни иконок, поэтому её
   колонки выходили уже и заголовки уезжали правее своих данных. */
.list--invoice .list__head,
.list--invoice .list__row {
    grid-template-columns: minmax(0, var(--w-col-docnum))
                           minmax(var(--w-col-name-min), 1fr)
                           minmax(0, var(--w-col-price))
                           minmax(0, var(--w-col-date))
                           minmax(0, var(--w-col-docstatus))
                           minmax(0, var(--w-col-date))
                           var(--w-col-docacts);
    grid-template-areas: "number company amount date status paid act";
}

/* У договора колонок меньше, и на 1024px клиенту места пока хватает,
   но схема та же, что у счёта: разъезжаться они должны одинаково */
.list--contract .list__head,
.list--contract .list__row {
    grid-template-columns: minmax(0, var(--w-col-contractnum))
                           minmax(var(--w-col-name-min), 1fr)
                           minmax(0, var(--w-col-price))
                           minmax(0, var(--w-col-date))
                           var(--w-col-docacts);
    grid-template-areas: "number company amount date act";
}

/* Ячейка статуса поднимается над растянутой ссылкой строки — так же, как
   .list__actions: пилюля внутри неё ведёт на подтверждение, а не
   на скачивание PDF */
.list__cell--status {
    grid-area: status;
    position: relative;
    min-width: 0;
}

.list__cell--paid { grid-area: paid; min-width: 0; color: var(--color-text-secondary); }

/* Номер документа — единственная ссылка строки, но кликабельна вся строка
   целиком, поэтому цвет у него общий для ячейки, а не акцентный */
.list--invoice .list__link,
.list--contract .list__link { font-weight: var(--fw-regular); color: inherit; }

/* Значок «№» перед номером нужен только карточке, где нет шапки колонок */
.list__num-sign { display: none; }

@media (max-width: 768px) {
    /* Карточка 7i: номер и клиент первой строкой, ниже сумма и дата, под ними
       статус с датой оплаты и действия. Данные те же, что на десктопе.

       Колонки три, а не две: крайняя левая держит только номер, сумма
       и статус растягиваются на первые две. Иначе ширину левой колонки
       задавала бы пилюля статуса и название клиента уезжало бы к середине
       карточки. Правая колонка — по дате, самому длинному значению справа. */
    .list--invoice .list__row {
        grid-template-columns: auto minmax(0, 1fr) auto;
        grid-template-areas:
            "number company company"
            "amount amount  date"
            "status status  paid"
            "act    act     act";
        gap: var(--sp-2);
    }

    /* Мобильного макета списка договоров нет — верстается по 7i без статуса
       и без корзины (спека, раздел 12) */
    .list--contract .list__row {
        grid-template-columns: auto minmax(0, 1fr) auto;
        grid-template-areas:
            "number company company"
            "amount amount  date"
            "act    act     act";
        gap: var(--sp-2);
    }

    /* На десктопе номер стоит под заголовком «№», на карточке заголовков нет
       и голая цифра рядом с названием читается как часть названия */
    .list__num-sign { display: inline; }

    .list--invoice .list__cell--date,
    .list--contract .list__cell--date,
    .list--invoice .list__cell--paid { text-align: right; }

    .list--invoice .list__actions,
    .list--contract .list__actions { justify-content: flex-end; }
}

/* --- пилюля статуса счёта (макеты 7a, 7i) ---
   Значений у статуса два, поэтому дропдауна с макета здесь нет: у кого есть
   право сменить статус — это ссылка на подтверждение, у остальных текст. */

.doc-status {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    height: var(--h-control-sm);
    padding: 0 var(--sp-3);
    border: 1px solid var(--color-border);
    border-radius: var(--r-sm);
    background: var(--color-surface);
    color: var(--color-text-secondary);
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    text-decoration: none;
    white-space: nowrap;
    transition: border-color var(--transition);
}

/* Точка слева — того же цвета, что подпись: серая у созданного счёта,
   зелёная у оплаченного */
.doc-status::before {
    content: '';
    width: var(--sz-doc-dot);
    height: var(--sz-doc-dot);
    flex: none;
    border-radius: var(--r-full);
    background: currentColor;
}

a.doc-status:hover { border-color: var(--color-accent); }

/* Кольцо фокуса такое же, как у .btn: пилюля — ссылка на подтверждение,
   и с клавиатуры по строкам списка должно быть видно, где стоишь */
a.doc-status:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.doc-status--paid { color: var(--color-success); }

/* --- пилюли фильтра (макет 7i, фильтр по оплате) ---
   Значение живёт в адресе, поэтому это обычные ссылки, а не кнопки формы.
   От табов отличаются формой: таб выбирает раздел, пилюля — отбор внутри него. */

.pills {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
}

.pill {
    padding: var(--sp-2) var(--sp-4);
    border: 1px solid var(--color-border);
    border-radius: var(--r-full);
    background: var(--color-surface);
    color: var(--color-text-secondary);
    font-size: var(--fs-base);
    text-decoration: none;
    white-space: nowrap;
    transition: background var(--transition), color var(--transition);
}

.pill:hover { background: var(--color-surface-2); color: var(--color-text); }

.pill:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.pill.is-active {
    border-color: var(--color-accent);
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-weight: var(--fw-semibold);
}

/* --- реквизиты юрлица только для чтения (макеты 7c, 7g) ---
   На макетах они нарисованы редактируемыми полями и одной строкой — делаем
   нормально: каждый реквизит своим полем, правка живёт на карточке компании
   (decisions.md п. 5). Блоков на странице столько, сколько юрлиц у клиентов;
   показывает нужный documents.js. */

.doc-req {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--sp-3);
    padding: var(--sp-3);
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    background: var(--color-surface-2);
}

.doc-req__field { min-width: 0; }

/* Название юрлица и юр. адрес в четверть строки не помещаются */
.doc-req__field--wide { grid-column: span 2; }

.doc-req__label {
    font-size: var(--fs-sm);
    color: var(--color-text-muted);
}

.doc-req__value {
    font-size: var(--fs-base);
    color: var(--color-text);
    overflow-wrap: anywhere;
}

/* Поле только для чтения в форме договора (макет 7g): у чужого документа
   те же поля показываются текстом, но остаются на своих местах — форма
   не должна перестраиваться от того, кто её открыл. */
.doc-view {
    display: flex;
    align-items: center;
    min-height: var(--h-control-lg);
    padding: var(--sp-2) var(--sp-4);
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    background: var(--color-surface-2);
    color: var(--color-text);
    font-size: var(--fs-md);
    overflow-wrap: anywhere;
}

/* Повтор номера — предупреждение, а не запрет (decisions.md п. 7): кнопка
   сохранения рядом меняет подпись на «Сохранить всё равно». Одно на обе формы
   документов — счёт и договор спрашивают об одном и том же. */
.doc-warn {
    padding: var(--sp-3);
    border: 1px solid var(--color-warning);
    border-radius: var(--r-md);
    background: var(--color-warning-soft);
    color: var(--color-warning);
    font-size: var(--fs-base);
}

/* --- форма счёта (макеты 7c, 7k) --- */

/* Шапка блока «Счёт»: заголовок слева, номер и дата справа в одну строку */
.inv-head {
    display: flex;
    align-items: flex-end;
    gap: var(--sp-3);
}

.inv-head__title { flex: 1; min-width: 0; }

.inv-head__field { flex: none; width: var(--w-inv-number); }

/* Дата шире номера: «дд.мм.гггг» и иконка календаря браузера */
.inv-head__field + .inv-head__field { width: var(--w-col-date); }

.inv-rows {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
}

.inv-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--w-inv-qty) var(--w-inv-sum) var(--w-inv-del);
    gap: var(--sp-2);
    align-items: center;
}

/* Подписи колонок — та же сетка, что у строк, поэтому это не таблица,
   а первая строка того же грида */
.inv-row--head {
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: .4px;
}

.inv-row__num { text-align: right; }

/* Итог и кнопки одним блоком: число в форме одно на обе раскладки, дублировать
   его в липкой панели мобильного (макет 7k) незачем */
.inv-foot {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-4);
    border: 1px solid var(--color-border);
    border-radius: var(--r-lg);
    background: var(--color-surface);
    box-shadow: var(--shadow-sm);
}

.inv-foot__total { flex: 1; min-width: 0; }

.inv-foot__label {
    font-size: var(--fs-sm);
    color: var(--color-text-muted);
}

.inv-foot__sum {
    font-size: var(--fs-2xl);
    font-weight: var(--fw-black);
    color: var(--color-text);
}

/* Подпись кнопки сохранения в двух написаниях: полная (макет 7c) и короткая
   для липкой панели на 390px (макет 7k) — с полной там не остаётся места
   под итог. Данные это не прячет: написания равнозначны */
.inv-foot__save-sm { display: none; }

@media (max-width: 768px) {
    /* Реквизиты столбиком по одному полю (макет 7k): состав данных тот же */
    .doc-req { grid-template-columns: minmax(0, 1fr); }
    .doc-req__field--wide { grid-column: span 1; }

    /* Номер и дата под заголовком, в две колонки */
    .inv-head {
        flex-wrap: wrap;
        gap: var(--sp-2) var(--sp-3);
    }

    .inv-head__title { flex: 1 0 100%; }

    .inv-head__field,
    .inv-head__field + .inv-head__field { flex: 1; width: auto; }

    /* Строка счёта столбиком: наименование во всю ширину, ниже количество,
       сумма и корзина */
    .inv-row {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) var(--w-inv-del);
        grid-template-areas:
            "name name name"
            "qty  sum  del";
        gap: var(--sp-2);
        padding: var(--sp-3);
        border: 1px solid var(--color-border);
        border-radius: var(--r-md);
    }

    .inv-row > :nth-child(1) { grid-area: name; }
    .inv-row > :nth-child(2) { grid-area: qty; }
    .inv-row > :nth-child(3) { grid-area: sum; }
    .inv-row > :nth-child(4) { grid-area: del; justify-self: end; }

    /* Подписи колонок карточке не нужны — у каждого поля свой placeholder */
    .inv-row--head { display: none; }

    /* Липкая нижняя панель: итог и кнопка всегда под рукой (макет 7k) */
    .inv-foot {
        position: sticky;
        bottom: 0;
        flex-wrap: wrap;
        border-radius: var(--r-lg) var(--r-lg) 0 0;
        box-shadow: var(--shadow-md);
    }

    .inv-foot__save    { display: none; }
    .inv-foot__save-sm { display: inline; }

    /* В строке панели остаются итог и сохранение, как на макете 7k, а «Отмена»
       уходит под них: втроём они на 390px не помещаются, но и убирать действие
       нельзя — на мобильном тот же состав, что на десктопе (скилл verstka) */
    .inv-foot .btn--ghost {
        order: 1;
        flex: 1 0 100%;
    }
}

/* --- форма договора (макеты 7g, 7q) --- */

/* Номер, дата, сумма и срок поставки в одну строку (макет 7g), на телефоне —
   по два в ряд (макет 7q): состав полей тот же. Адрес доставки и КП идут
   ниже во всю ширину — они длиннее остальных. */
.ctr-terms {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--sp-3);
}

/* Кнопки внизу формы. Итога у договора нет — сумма вводится руками,
   и панель держит одни действия, в отличие от .inv-foot у счёта. */
.ctr-foot {
    display: flex;
    justify-content: flex-end;
    gap: var(--sp-3);
}

/* --- версии договора (макета нет) ---
   Блок виден одному Владельцу (decisions.md п. 7). Каждая строка — ссылка
   на свой файл: версии не удаляются никогда, и список растёт вниз. */

.ctr-versions {
    display: flex;
    flex-direction: column;
}

.ctr-version {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-3) 0;
    border-top: 1px solid var(--color-border);
    color: var(--color-text);
    text-decoration: none;
}

.ctr-version:first-child { border-top: 0; }

.ctr-version:hover { color: var(--color-accent); }

.ctr-version__date { flex: none; font-weight: var(--fw-semibold); }

.ctr-version__user {
    flex: 1;
    min-width: 0;
    color: var(--color-text-secondary);
}

.ctr-version__act { flex: none; color: var(--color-accent); }

@media (max-width: 768px) {
    .ctr-terms { grid-template-columns: repeat(2, minmax(0, 1fr)); }

    /* Липкая нижняя панель: кнопка сохранения всегда под рукой (макет 7q) */
    .ctr-foot {
        position: sticky;
        bottom: 0;
        flex-wrap: wrap;
        gap: var(--sp-2);
        padding: var(--sp-3);
        border: 1px solid var(--color-border);
        border-radius: var(--r-lg) var(--r-lg) 0 0;
        background: var(--color-surface);
        box-shadow: var(--shadow-md);
    }

    /* Каждая кнопка во всю ширину: «Сохранить · сформировать PDF» нарисована
       на 7q именно так, а в половину строки её подпись переносится и вылезает
       из фиксированной высоты .btn */
    .ctr-foot .btn { flex: 1 0 100%; }

    /* Автор версии уходит на свою строку: дата и ссылка остаются в одной */
    .ctr-version { flex-wrap: wrap; }

    .ctr-version__user {
        flex: 1 0 100%;
        order: 3;
    }
}

/* --- дашборд (макеты 1c, 1e, 1f, 1g) ---
   Строки лент дашборд берёт у разделов (.task-row, .ev-row, .list--call
   и прочие): вторых стилей для тех же сущностей здесь нет. Своё у него —
   раскладка рядов, карточка-цифра и диаграмма поступлений. */

/* Ряды идут столбиком с тем же шагом, что между блоками .content: страница
   держит свою раскладку сама и не полагается на gap каркаса. */
.dash {
    display: flex;
    flex-direction: column;
    gap: var(--sp-5);
}

/* Ряд блоков. Сетка макета 1c — три колонки на всю ширину: в ней стоят
   задачи, события и звонки, а широкие блоки занимают по две колонки
   модификаторами ниже. */
.dash-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--sp-5);
}

/* Первый ряд: карточка счетов уже карточки поступлений. Рядом с суммой там
   стоит диаграмма восьми месяцев, и в трети ряда она вырождается
   в частокол — пропорция взята с макета 1c. */
.dash-grid--stat { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }

/* Последний ряд: заказы и заявки пополам. Макет 1c отдаёт заказам две
   колонки из трёх, но по решению владельца от 23.08.2026 блоки одной
   ширины — таблица заказов в половину ряда помещается. */
.dash-grid--half { grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* Тот же ряд, когда блок в нём один: пустых колонок ряд не держит */
.dash-grid--one { grid-template-columns: minmax(0, 1fr); }

/* Карточка-цифра: подпись, крупная сумма, подстрока */
.dash-stat {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    color: var(--color-text);
}

/* Подсветка рамкой у карточки-ссылки (макет 1c). Правило висит на теге:
   поступления — такая же карточка, но никуда не ведут, и подсвечивать
   её нечему. */
a.dash-stat:hover { border-color: var(--color-accent); color: var(--color-text); }

.dash-stat__label {
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    color: var(--color-text-secondary);
}

.dash-stat__sum {
    font-size: var(--fs-2xl);
    font-weight: var(--fw-black);
    letter-spacing: -.5px;
}

.dash-stat__sub { color: var(--color-text-muted); }

/* Та же карточка с диаграммой: цифры слева, столбцы справа (макет 1c) */
.dash-stat--chart {
    flex-direction: row;
    align-items: center;
    gap: var(--sp-6);
}

.dash-stat__body {
    flex: none;
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
}

/* Диаграмма поступлений: восемь месяцев, текущий последний и акцентом.
   Высота столбца приходит из PHP процентом от максимума — единственное
   значение страницы, которого CSS знать не может. */
.dash-bars {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: flex-end;
    gap: var(--sp-2);
    height: var(--h-dash-bars);
}

.dash-bar {
    position: relative;
    flex: 1;
    min-width: 0;
    /* Месяц без оплат — плоская полоска, а не пустое место: иначе в ряду
       появляется дырка и восьми столбцов уже не видно */
    min-height: var(--h-dash-bar-min);
    border-radius: var(--r-sm) var(--r-sm) 0 0;
    background: var(--color-accent-soft);
}

.dash-bar--now { background: var(--color-accent); }

/* Подсказка «Июль 2026 — 12 640 000 ₽» по наведению, без JS. Тот же текст
   продублирован атрибутом title: по тапу и в читалках подсказки на :hover
   не существует. */
.dash-bar__tip {
    position: absolute;
    left: 50%;
    bottom: 100%;
    z-index: 10;
    display: none;
    margin-bottom: var(--sp-2);
    padding: var(--sp-1) var(--sp-2);
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    background: var(--color-surface);
    box-shadow: var(--shadow-md);
    color: var(--color-text);
    font-size: var(--fs-sm);
    white-space: nowrap;
    transform: translateX(-50%);
}

.dash-bar:hover .dash-bar__tip { display: block; }

/* Шапка блока-ленты: заголовок слева, ссылка «Все …» справа (макет 1c).
   Растягивать .card__title нельзя — он общий на все карточки, поэтому
   ширину забирает правило внутри шапки. */
.dash-block__head {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
}

.dash-block__head .card__title { flex: 1; min-width: 0; }

.dash-block__head a {
    flex: none;
    font-weight: var(--fw-semibold);
}

/* Лента задач идёт во всю ширину карточки: боковые отступы у .task-row свои,
   и второй раз их добавлять нельзя — текст строк уехал бы от заголовка блока.
   События так не тянутся: у .ev-row в блоке боковых отступов нет вовсе
   (.ev-list--plain), и края карточки ей брать не с чего. */
.dash-list { margin: 0 calc(var(--sp-5) * -1); }

/* Блок без строк остаётся на месте с приглушённой подписью, вместе со ссылкой
   «Все …»: исчезающие блоки ломают привычную раскладку (спека, 0.13) */
.dash-block__none { color: var(--color-text-muted); }

/* Задачи в блоке идут без разделителей: на 1c строки отделены только
   промежутком, а вертикальных отступов у .task-row на это хватает.
   В разделе линии остаются — там лента длинная и без них рябит. */
.dash-list .task-row { border-bottom: none; }

/* Ленты разделов внутри карточки блока: рамку, тень и скругление карточка
   уже нарисовала вокруг, второй раз они не нужны. Сами строки, пилюли
   и растянутая ссылка остаются те же, что в разделах. */
.dash-list .list {
    border: none;
    border-radius: 0;
    box-shadow: none;
    background: none;
}

/* Звонки стоят третьим блоком ряда, в трети ширины (макет 1c), и колонки
   раздела туда не встают: строка складывается карточкой — компания, ниже
   время и оценка, пилюля справа. Направления и длительности в ней нет
   вовсе: они в карточке звонка (спека, блок 5). */
.dash-list .list--call .list__row {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
        "company company tags"
        "date    score   tags";
    gap: var(--sp-1) var(--sp-2);
}

/* У Владельца и Руководителя добавляется третья строка — сотрудник.
   Отдельным правилом, а не пустой областью в раскладке выше: пустая
   строка сетки всё равно забрала бы себе row-gap. У Менеджера
   и Партнёра в области он один, свой, и строки нет вовсе. */
.dash-list .list--call-mgr .list__row {
    grid-template-areas:
        "company company tags"
        "date    score   tags"
        "manager manager manager";
}

/* Точка отделяет оценку от времени: подписей колонок в строке нет */
.dash-list .list--call .list__cell--score::before { content: '· '; }

/* Оценка модели: до ИИ-обработки везде «—» (спека, 0.15). Разряды
   выравниваются по ширине — столбец узкий, и прыгающие цифры в нём заметны. */
.list__cell--score {
    grid-area: score;
    min-width: 0;
    color: var(--color-text-secondary);
    font-variant-numeric: tabular-nums;
}

/* Недозвон приглушается целиком: разговора не было, и читать в строке
   нечего (спека, блок 5) */
.dash-list .list__row--miss { color: var(--color-text-secondary); }

/* Заказы остаются таблицей, как на 1c: номер, клиент, сумма, статус,
   дата — с подписями колонок сверху. Колонки раздела заказов сюда
   не годятся: там первыми идут дата и телефон, а в блоке телефона нет
   вовсе (спека, блок 6).
   Все три числовые колонки — minmax от нуля: в половине ряда на узком
   ноутбуке ширины с макета не встают, и сжиматься должны они, а не имя
   клиента. Переполнения карточки при этом не бывает ни на какой ширине. */
.dash-list .list--order .list__head,
.dash-list .list--order .list__row {
    grid-template-columns: minmax(0, var(--w-dash-num))
                           minmax(0, 1fr)
                           minmax(0, var(--w-dash-sum))
                           minmax(0, var(--w-dash-status))
                           minmax(0, var(--w-dash-date));
    grid-template-areas: "number name sum tags date";
    gap: var(--sp-2);
}

/* Заявки стоят в одной колонке ряда, и пять колонок в неё не встают:
   строка складывается карточкой — той же, какой эта лента становится
   на мобильном. Данные в ней те же, меняется только раскладка. */
.dash-list .list--request .list__row {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
        "name name  tags"
        "date phone tags"
        "item item  item";
    gap: var(--sp-1) var(--sp-2);
}

/* Точка отделяет второе значение строки от первого: колонок с заголовками
   у заявок нет, и без неё «19 июл +7 917 …» читается одной строкой */
.dash-list .list--request .list__cell--phone::before { content: '· '; }

/* Пилюля статуса стоит у правого края строки, как в тех же лентах
   на мобильном */
.dash-list .list__cell--tags { justify-content: flex-end; }

@media (max-width: 768px) {
    /* Все блоки столбиком в порядке разметки (макеты 1f, 1g): счета,
       поступления, задачи, события, звонки, заказы, заявки. Ничего
       не прячется и строк в лентах те же пять — см. спеку, 0.12. */
    .dash-grid,
    .dash-grid--stat,
    .dash-grid--half { grid-template-columns: minmax(0, 1fr); }

    /* Диаграмма встаёт под сумму: рядом с ней в 390px восемь столбцов
       вырождаются в частокол, а «12 640 000 ₽» переносится и вылезает
       из карточки. Данные те же — меняется только раскладка. */
    .dash-stat--chart {
        flex-direction: column;
        align-items: stretch;
        gap: var(--sp-3);
    }

    /* Лента больше не тянется за поля карточки: на мобильном строки
       разделов сами становятся карточками с рамкой и отступами,
       и вторая рамка внутри блока лишняя (макет 1f) */
    .dash-list { margin: 0; }

    /* Строки лент остаются строками с разделителем: карточка блока
       вокруг них уже нарисована, и вложенные карточки в ней читаются
       как второй уровень, которого на макете нет. Фон не трогаем —
       подсветка при наведении остаётся за строкой. */
    .dash-list .list { gap: 0; }

    .dash-list .list__row {
        border: none;
        border-bottom: 1px solid var(--color-border);
        border-radius: 0;
        box-shadow: none;
        padding: var(--sp-3) 0;
    }

    .dash-list .list__row:last-child { border-bottom: none; }
    
    .dash-list .task-row {
        padding: var(--sp-2) 0;
        border: none;
        box-shadow: none;
        display: flex;
    }

    /* Событиям — то же, что .dash-list: на 390px ленты не тянутся
       за поля карточки, и подсветка строки идёт вровень с остальными */
    .ev-list--plain { margin: 0; }
    .ev-list--plain .ev-row { padding: var(--sp-2) 0; }

    /* Таблица заказов складывается карточкой: пять колонок в 390px
       не встают, а подписи колонок прячет общее правило .list__head.
       Данные те же — меняется только раскладка (макет 1f). */
    .dash-list .list--order .list__row {
        grid-template-columns: auto minmax(0, 1fr) auto;
        grid-template-areas:
            "number name tags"
            "date   sum  tags";
        gap: var(--sp-1) var(--sp-2);
    }

    /* Точка отделяет сумму от даты: подписи колонок на мобильном скрыты,
       и без неё «19 июл 2 140 000 ₽» читается одним числом */
    .dash-list .list--order .list__cell--sum::before { content: '· '; }
}

/* --- пустое состояние --- */

.empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-12) var(--sp-6);
    color: var(--color-text-muted);
    text-align: center;
}

/* --- почта: три панели (макет 9a), три экрана на мобильном (9e, 9g) ---
   Одна обёртка .mail на все три адреса раздела. На десктопе видны все панели,
   на узком экране — та, которую назвал класс режима (спека mail.md 0.19). */

.mail {
    display: grid;
    grid-template-columns: var(--w-mail-folders) var(--w-mail-list) minmax(0, 1fr);
    min-height: var(--h-mail);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}

/* --- панель папок --- */

.mail__folders {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    padding: var(--sp-3) var(--sp-2);
    border-right: 1px solid var(--color-border);
    background: var(--color-surface-2);
}

/* «Написать» и «Обновить» стоят в одну строку: обновление — только иконка
   фиксированной ширины, «Написать» занимает всю оставшуюся */
.mail__actions {
    display: flex;
    gap: var(--sp-2);
    margin: 0 var(--sp-1) var(--sp-3);
}

.mail__compose { flex: 1; min-width: 0; }

.mail__sync { flex: none; }

.mail__sync-btn { width: var(--h-control); padding: 0; }

/* Плюс на макете крупнее подписи и без своего интерлиньяжа, иначе кнопка
   становится выше остальных */
.mail__plus { font-size: var(--fs-xl); line-height: 1; }

/* flex: 1 — чтобы футер панели прижался к её низу, а не встал сразу
   под последней папкой */
.mail-folders {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
}

.mail-folder {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-3);
    border-radius: var(--r-md);
    color: var(--color-text-secondary);
    font-size: var(--fs-md);
    font-weight: var(--fw-medium);
    transition: background var(--transition), color var(--transition);
}

.mail-folder:hover { background: var(--color-bg); color: var(--color-text); }

.mail-folder.is-active {
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-weight: var(--fw-semibold);
}

.mail-folder svg { flex: none; }

.mail-folder__name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Число черновиков — не счётчик непрочитанного: заливкой оно спорило бы
   с «Входящими», у которых на макете 9a бейдж один на всю панель */
.mail-folder__count { flex: none; color: var(--color-text-muted); font-size: var(--fs-sm); }

/* Футер панели папок: ручное обновление и отключение ящика. В макете 9a его
   нет — блок добавлен по решению владельца (спека mail.md, раздел 11).
   Отрицательные поля растягивают линию на всю ширину панели, а собственные
   отступы возвращают текст на место. */
.mail__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2);
    margin: auto calc(var(--sp-2) * -1) calc(var(--sp-3) * -1);
    padding: var(--sp-3) var(--sp-3) var(--sp-3);
    border-top: 1px solid var(--color-border);
}

/* Один класс на ссылку и на кнопку формы: выглядят они одинаково, а рамку
   и шрифт кнопке приходится снимать — общего сброса для button в проекте нет */
.mail__foot-link {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    padding: 0;
    border: 0;
    background: none;
    color: var(--color-text-secondary);
    font-family: var(--font);
    font-size: var(--fs-base);
    font-weight: var(--fw-medium);
    cursor: pointer;
    transition: color var(--transition);
}

.mail__foot-link:hover { color: var(--color-text); }

.mail__foot-link--danger { color: var(--color-danger); }
.mail__foot-link--danger:hover { color: var(--color-danger); opacity: .8; }

/* --- панель списка --- */

.mail__list {
    display: flex;
    flex-direction: column;
    min-width: 0;
    border-right: 1px solid var(--color-border);
}

/* Заголовок папки со стрелкой назад нужен только на экране списка (9e справа):
   на десктопе и папка, и счётчик непрочитанного видны в соседней панели,
   и данные при этом не теряются */
.mail__list-head {
    display: none;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--color-border);
}

.mail__list-title {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--fs-xl);
    font-weight: var(--fw-bold);
}

.mail__back { display: flex; flex: none; color: var(--color-text); }
.mail__back:hover { color: var(--color-accent); }

.mail-search {
    position: relative;
    display: flex;
    align-items: center;
    padding: var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--color-border);
}

.mail-search svg {
    position: absolute;
    left: calc(var(--sp-4) + var(--sp-3));
    color: var(--color-text-muted);
    pointer-events: none;
}

.mail-search .input {
    height: var(--h-control);
    padding-left: var(--sp-8);
    border-color: transparent;
}

.mail-search .input:focus { border-color: var(--color-accent); }

.mail-rows { display: flex; flex-direction: column; }

/* Вся строка — ссылка на письмо (скилл verstka), шеврона справа нет */
.mail-row {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    padding: var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--color-border);
    color: var(--color-text-secondary);
    transition: background var(--transition);
}

.mail-row:hover { background: var(--color-surface-2); color: var(--color-text-secondary); }

.mail-row__top { display: flex; align-items: baseline; gap: var(--sp-2); }

.mail-row__who {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--fs-md);
}

.mail-row__time { flex: none; font-size: var(--fs-sm); color: var(--color-text-muted); }

.mail-row__subject {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    min-width: 0;
}

.mail-row__text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.mail-row__clip { flex: none; color: var(--color-text-muted); }

.mail-row__snippet {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--color-text-muted);
}

/* Непрочитанное — жирным, время акцентом (макет 9a) */
.mail-row.is-unread { color: var(--color-text); }
.mail-row.is-unread:hover { color: var(--color-text); }
.mail-row.is-unread .mail-row__who,
.mail-row.is-unread .mail-row__subject { font-weight: var(--fw-bold); }
.mail-row.is-unread .mail-row__time { color: var(--color-accent); font-weight: var(--fw-semibold); }

/* Открытое письмо — подложкой акцента (макет 9a) */
.mail-row.is-active,
.mail-row.is-active:hover { background: var(--color-accent-soft); }

.mail__list .pager { padding: var(--sp-3) var(--sp-4); }

/* --- панель письма --- */

.mail__msg {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

/* Шапка экрана письма (9g): назад в список, название папки, удаление.
   На десктопе папка видна в соседней панели, а удаление стоит в ряду
   действий — там эта шапка не нужна */
.mail__msg-head {
    display: none;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--color-border);
}

.mail-msg {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.mail-msg__actions {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-5);
    border-bottom: 1px solid var(--color-border);
}

/* Пустое место между ответом и удалением: опасное действие стоит поодаль
   от обычных (макет 9a) */
.mail-msg__gap { flex: 1; }

/* Удаление иконкой — только в шапке экрана письма (9g) */
.mail-msg__icon {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: var(--h-control-sm);
    height: var(--h-control-sm);
    border-radius: var(--r-md);
    background: var(--color-danger-soft);
    color: var(--color-danger);
    border: none;
}

.mail-msg__icon:hover { color: var(--color-danger); opacity: .9; }

/* Тело письма не дошло — ящик не ответил. Заголовки при этом на месте:
   они из кэша (спека mail.md 0.9) */
.mail-msg__notice {
    padding: var(--sp-3) var(--sp-5);
    border-bottom: 1px solid var(--color-border);
    color: var(--color-danger);
    font-weight: var(--fw-medium);
}

.mail-msg__body {
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
    min-width: 0;
    padding: var(--sp-5);
}

.mail-msg__subject {
    font-size: var(--fs-xl);
    font-weight: var(--fw-bold);
    line-height: 1.35;
}

.mail-msg__author {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
}

.mail-msg__who { flex: 1; min-width: 0; }

/* Адрес отправителя и список получателей длиннее панели: они переносятся
   по любому месту, иначе тянут раскладку вширь */
.mail-msg__name {
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
    overflow-wrap: anywhere;
}

.mail-msg__email { color: var(--color-text-muted); font-weight: var(--fw-regular); }

.mail-msg__meta {
    font-size: var(--fs-base);
    color: var(--color-text-muted);
    overflow-wrap: anywhere;
}

/* Тело письма пришло из интернета: своей вёрстки у него нет, а раздвинуть
   панель оно не должно — картинки и таблицы ужимаются по месту */
.mail-msg__text {
    min-width: 0;
    line-height: 1.65;
    overflow-wrap: anywhere;
}

.mail-msg__text img { max-width: 100%; height: auto; }

.mail-msg__text table {
    max-width: 100%;
    border-collapse: collapse;
}

.mail-msg__text td,
.mail-msg__text th { padding: var(--sp-1) var(--sp-2); }

.mail-msg__text ul,
.mail-msg__text ol { padding-left: var(--sp-5); }

.mail-msg__text a { color: var(--color-accent); }

.mail-msg__text blockquote {
    margin: var(--sp-3) 0;
    padding-left: var(--sp-3);
    border-left: 2px solid var(--color-border);
    color: var(--color-text-secondary);
}

/* --- вложения (макеты 9a, 9g) --- */

.mail-msg__files { display: flex; flex-wrap: wrap; gap: var(--sp-3); }

.mail-file {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    max-width: 100%;
    padding: var(--sp-2) var(--sp-3);
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    background: var(--color-surface-2);
    color: var(--color-text);
    transition: border-color var(--transition);
}

.mail-file:hover { border-color: var(--color-accent); color: var(--color-text); }

/* Расширение файла вместо иконки типа: файл в письме бывает любой,
   а набор иконок под все типы не нарисован */
.mail-file__ext {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: var(--sz-mail-ext);
    height: var(--sz-mail-ext);
    border-radius: var(--r-sm);
    background: var(--color-danger-soft);
    color: var(--color-danger);
    font-size: var(--fs-xs);
    font-weight: var(--fw-bold);
}

.mail-file__who { min-width: 0; }

.mail-file__name {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
}

.mail-file__size {
    display: block;
    font-size: var(--fs-sm);
    color: var(--color-text-muted);
}

/* --- плашка «ящик не отвечает» и форма подключения --- */

.mail-notice {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--sp-3);
    border-color: var(--color-danger);
    color: var(--color-danger);
}

.mail-notice svg { flex: none; }

.mail-notice__text {
    flex: 1;
    min-width: var(--w-col-name-min);
    font-weight: var(--fw-medium);
}

.mail-notice__actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

.mail-connect { max-width: var(--w-modal); }

.mail-connect__form {
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
}

/* --- мобильная версия (макеты 9e, 9f) --- */

/* Кнопка «＋» на экране списка. На десктопе её место занимает «Написать»
   в панели папок, поэтому вне мобильной версии она не показывается */
.mail__fab { display: none; }

@media (max-width: 768px) {
    /* Панели перестают быть колонками одной карточки и становятся экранами.
       Какой из них показан, решает класс режима: три адреса — три экрана,
       и это решение записано в спеке модуля (mail.md 0.19), а не придумано
       вёрсткой ради экономии места */
    .mail {
        display: block;
        min-height: 0;
        border: 0;
        border-radius: 0;
        background: none;
        box-shadow: none;
        overflow: visible;
    }

    .mail__folders,
    .mail__list,
    .mail__msg {
        border: 1px solid var(--color-border);
        border-radius: var(--r-lg);
        background: var(--color-surface);
        box-shadow: var(--shadow-sm);
    }

    .mail--folders .mail__list,
    .mail--folders .mail__msg,
    .mail--list .mail__folders,
    .mail--list .mail__msg,
    .mail--message .mail__folders,
    .mail--message .mail__list { display: none; }

    .mail__folders { padding: var(--sp-2); }

    /* На экране папок строка кнопок стоит под списком папок (9e слева),
       контролы — в мобильную высоту */
    .mail__actions {
        order: 2;
        margin: var(--sp-3) var(--sp-1) var(--sp-1);
    }

    .mail__compose,
    .mail__sync-btn { height: var(--h-control-lg); }

    .mail__sync-btn { width: var(--h-control-lg); }

    .mail-folder { padding: var(--sp-3); font-size: var(--fs-lg); }

    /* Кнопка «Написать» стоит под папками, футер — под ней: на экране папок
       он остаётся последним блоком колонки. Поля пересчитаны под padding
       панели, который на узком экране меньше десктопного */
    .mail__foot {
        order: 3;
        margin: var(--sp-2) calc(var(--sp-2) * -1) calc(var(--sp-2) * -1);
        padding: var(--sp-3) var(--sp-3) var(--sp-2);
    }

    .mail__list-head,
    .mail__msg-head { display: flex; }

    /* Действия письма уходят вниз, под текст и вложения (макет 9g) */
    .mail-msg__actions {
        order: 3;
        padding: 0 var(--sp-4) var(--sp-4);
        border-bottom: 0;
    }

    .mail-msg__actions .btn { flex: 1; height: var(--h-control-lg); }

    .mail-msg__gap { display: none; }

    /* Удаление на этом экране не пропадает, а переезжает в шапку иконкой
       (макет 9g): третья кнопка в ряду не поместилась бы */
    .mail-msg__del { display: none; }

    .mail-msg__body { padding: var(--sp-4); }

    .mail--list .mail__fab {
        display: flex;
        position: fixed;
        right: var(--sp-4);
        bottom: var(--sp-4);
        z-index: 5;
        width: var(--sz-fab);
        height: var(--sz-fab);
        align-items: center;
        justify-content: center;
        border-radius: var(--r-full);
        background: var(--color-accent);
        color: var(--color-on-accent);
        font-size: var(--fs-2xl);
        box-shadow: var(--shadow-lg);
    }

    .mail--list .mail__fab:hover { background: var(--color-accent-hover); color: var(--color-on-accent); }
}

/* ======================================================================
   Модалка «Написать письмо» — макет 9c
   ====================================================================== */

/* Окно шире обычного: внутри поле текста письма, и на 480 оно становится
   колодцем в шесть слов шириной */
.modal__window--mail { max-width: var(--w-mail-modal); }

.mail-compose { display: flex; flex-direction: column; }

/* «Кому» и «Тема»: подпись слева, поле во всю оставшуюся ширину.
   Рамок у полей нет — их роль играет линия под строкой */
.mail-compose__row {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: 0 var(--sp-5);
    border-bottom: 1px solid var(--color-border);
}

/* Фокус виден по линии строки: своих рамок у полей нет, а невидимый фокус
   означает, что с клавиатуры поле не найти */
.mail-compose__row:focus-within { border-bottom-color: var(--color-accent); }

.mail-compose__label {
    flex: none;
    width: var(--w-mail-label);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--color-text-muted);
}

.mail-compose__input,
.mail-compose__body {
    border: 0;
    background: none;
    color: var(--color-text);
    font: inherit;
    outline: none;
}

.mail-compose__input {
    flex: 1;
    min-width: 0;
    height: var(--h-control);
    font-size: var(--fs-md);
}

.mail-compose__body:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: -2px;
}

.mail-compose__body {
    min-height: var(--h-mail-body);
    padding: var(--sp-4) var(--sp-5);
    font-size: var(--fs-md);
    line-height: 1.65;
    resize: vertical;
}

/* Поле «Кому» с пилюлями: они стоят перед полем ввода и переносятся
   на вторую строку, когда получателей много */
.mail-to {
    display: flex;
    flex: 1;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2);
    min-width: 0;
    padding: var(--sp-2) 0;
}

.mail-pill {
    display: flex;
    align-items: center;
    gap: var(--sp-1);
    padding: var(--sp-1) var(--sp-2);
    border-radius: var(--r-full);
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
}

.mail-pill__x,
.mail-file__x {
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    line-height: 1;
    opacity: .6;
    cursor: pointer;
}

.mail-pill__x:hover,
.mail-file__x:hover { opacity: 1; }

.mail-to .mail-compose__input { width: auto; }

/* Выбранные файлы. Пустой блок отступа не занимает: чаще всего вложений нет */
.mail-compose__files {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
    padding: 0 var(--sp-5) var(--sp-4);
}

.mail-compose__files:empty { display: none; }

/* Файлы черновика сервер рисует теми же карточками, но сама карточка тут
   не ссылка: рядом с ней стоит крестик, снимающий файл с письма, —
   скачивается файл по имени */
.mail-compose__files .mail-file__name:hover { color: var(--color-accent); }

.mail-file__x { flex: none; margin-left: var(--sp-2); font-size: var(--fs-md); }

.mail-compose__foot {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-5);
    border-top: 1px solid var(--color-border);
}

/* Отправка и скрепка слева, черновик — у правого края (макет 9c) */
.mail-compose__gap { flex: 1; }

/* Скрепка с макета: сам input спрятан под ней, кликом по подписи браузер
   открывает выбор файла сам — обработчик для этого не нужен */
.mail-attach {
    position: relative;
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: var(--h-control);
    height: var(--h-control);
    border: 1px solid var(--color-border);
    border-radius: var(--r-md);
    color: var(--color-text-secondary);
    cursor: pointer;
    transition: border-color var(--transition), color var(--transition);
}

.mail-attach:hover { border-color: var(--color-accent); color: var(--color-accent); }

/* Не display:none: скрытое так поле уходит из обхода по Tab, и вложить
   файл с клавиатуры становится нечем */
.mail-attach input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
}

@media (max-width: 768px) {
    /* Крупнее становятся только контролы. Высота текста письма остаётся
       прежней: нижний лист её не раздаёт, и поле схлопнулось бы в строку */
    .mail-compose__foot .btn { height: var(--h-control-lg); }

    .mail-attach { width: var(--h-control-lg); height: var(--h-control-lg); }

    /* У черновика кнопок в ряду четыре, и на 390 они не помещаются:
       «Удалить» и «Сохранить черновик» уходят на вторую строку, а распорка
       между рядами кнопок перестаёт быть нужной */
    .mail-compose__foot { flex-wrap: wrap; }

    .mail-compose__gap { display: none; }
}
