/* assets/css/admin.css — компонентная библиотека mp-* для админки Centers.
 *
 * ДОСЛОВНАЯ копия modules/multipage/public/assets/css/admin.css из
 * multipage-node (владелец, 2026-08-06: "что можно — скопировать с основной
 * системы, чтобы лишний раз не делать"). Имена классов mp-* сохранены
 * специально: куски разметки (карточки, таблицы, формы, пилюли, savebar)
 * переносятся между multipage-node и Centers 1:1 без переименований.
 * Требует токены из shell.css (подключается ПЕРЕД этим файлом).
 * При обновлении исходника в multipage-node этот файл можно просто
 * перезаписать свежей копией + вернуть эту шапку. */

.mp-admin {
    --mp-ease: cubic-bezier(.22, 1, .36, 1);
}

/* ── Заголовок раздела (см. mp_render_page_header(), admin_helpers.php) ── */
.mp-page-header {
    margin-bottom: 20px;
    animation: mp-fade-up .35s var(--mp-ease) both;
}

.mp-page-header__eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    font-weight: 700;
    color: var(--muted);
    text-decoration: none;
    text-transform: uppercase;
    letter-spacing: .05em;
    margin-bottom: 4px;
    transition: color .15s var(--mp-ease);
}

.mp-page-header__eyebrow:hover {
    color: var(--main);
}

.mp-page-header__title {
    margin: 0;
    font-size: 22px;
    font-weight: 900;
    color: var(--text);
}

/* ── Карточка ── */
.mp-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--space, 16px);
    transition: border-color .2s var(--mp-ease), transform .2s var(--mp-ease), box-shadow .2s var(--mp-ease);
}

.mp-card--hover:hover {
    border-color: var(--main-glass);
    transform: translateY(-2px);
    box-shadow: var(--card-shadow);
}

.mp-card + .mp-card {
    margin-top: 12px;
}

/* ── Двухколоночная раскладка "список + боковая форма" (categories.php:
 * список категорий слева + карточка "Добавить категорию" справа, 320px).
 * Раньше это был голый инлайн-style="display:grid;grid-template-columns:
 * 1fr 320px" БЕЗ единого media-запроса — на 375px .mp-card слева (overflow:
 * hidden) схлопывался почти до 0 и физически пропадал с экрана, виден был
 * только правый блок (независимый повторный аудит UX/UI, находка 1,
 * Critical). 1fr+320px реально нуждается в разумной ширине контентной
 * области — ниже брейкпоинта уходим в одну колонку, форма добавления
 * съезжает под список (тот же max-width-паттерн, что и у .mp-table ниже,
 * для консистентности между всеми media-запросами этого файла). */
.mp-cat-layout {
    display: grid;
    grid-template-columns: 1fr 320px;
    gap: 20px;
    align-items: start;
}

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

/* ── Статистические плашки (дашборд) ── */
.mp-stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 12px;
    margin-bottom: 20px;
}

.mp-stat-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 16px 18px;
    position: relative;
    overflow: hidden;
    transition: transform .2s var(--mp-ease), box-shadow .2s var(--mp-ease);
}

.mp-stat-card::before {
    content: '';
    position: absolute;
    inset: 0 auto 0 0;
    width: 4px;
    background: var(--mp-accent, var(--main));
}

.mp-stat-card:hover {
    transform: translateY(-2px);
    box-shadow: var(--card-shadow);
}

.mp-stat-card__value {
    font-size: 26px;
    font-weight: 900;
    color: var(--text);
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}

.mp-stat-card__label {
    font-size: 12px;
    font-weight: 700;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .04em;
    margin-top: 4px;
}

/* ── Бейджи статуса ── */
.mp-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    line-height: 1.5;
    white-space: nowrap;
}

.mp-badge--success { background: color-mix(in srgb, var(--success) 16%, transparent); color: var(--success); }
.mp-badge--danger  { background: color-mix(in srgb, var(--danger) 16%, transparent); color: var(--danger); }
.mp-badge--warning { background: color-mix(in srgb, var(--warning) 16%, transparent); color: var(--warning); }
.mp-badge--info    { background: color-mix(in srgb, var(--info) 16%, transparent); color: var(--info); }
.mp-badge--main    { background: var(--main-glass); color: var(--main); }
.mp-badge--muted   { background: color-mix(in srgb, var(--muted) 16%, transparent); color: var(--muted); }

/* ── Кнопки-действия ── */
.mp-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 44px;
    padding: 8px 18px;
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: var(--surface-2);
    color: var(--text);
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
    text-decoration: none;
    transition: background .15s var(--mp-ease), border-color .15s var(--mp-ease), transform .1s var(--mp-ease), box-shadow .15s var(--mp-ease);
}

.mp-btn:hover {
    transform: translateY(-1px);
    box-shadow: 0 4px 14px rgba(0, 0, 0, .12);
}

.mp-btn:active {
    transform: translateY(0);
}

.mp-btn:focus-visible {
    outline: 2px solid var(--main);
    outline-offset: 2px;
}

.mp-btn--primary { background: var(--main); border-color: var(--main); color: var(--main-contrast); }
.mp-btn--primary:hover { background: var(--main-dark); border-color: var(--main-dark); }

.mp-btn--danger { background: color-mix(in srgb, var(--danger) 12%, transparent); border-color: color-mix(in srgb, var(--danger) 35%, transparent); color: var(--danger); }
.mp-btn--danger:hover { background: var(--danger); border-color: var(--danger); color: #fff; }

.mp-btn--success { background: color-mix(in srgb, var(--success) 12%, transparent); border-color: color-mix(in srgb, var(--success) 35%, transparent); color: var(--success); }
.mp-btn--success:hover { background: var(--success); border-color: var(--success); color: #fff; }

/* Ядро (assets/css/admin-shell.css, core-файл, ВНЕ modules/multipage/) даёт
 * ЛЮБОЙ <button type="submit"> без класса .del-btn/.nav-logout-btn
 * собственный !important-цвет var(--main) — задумано как "кнопка сохранения
 * по умолчанию красится в акцент темы без явной разметки" для остальной
 * админки, но у mp_render_action_button() (admin_helpers.php) ВСЕ кнопки —
 * <button type="submit"> (модерация товаров, удаление категории и т.п.), и
 * это ядерное правило молча перекрашивало .mp-btn--success/--danger обратно
 * в один и тот же var(--main) поверх наших цветов — "Скрыть" (danger) и
 * "Показать" (success) выглядели ИДЕНТИЧНО, отличить нельзя было вообще
 * (обнаружено визуальной проверкой, не текстовыми тестами — screenshot,
 * 2026-08-02). Не трогаем сам core-файл (правка ушла бы в core_patch/,
 * 9-й файл вместо 8 — непропорционально ради чисто косметического фикса):
 * специфичность .mp-admin button.mp-btn.mp-btn--X (0,3,1) РОВНО равна
 * специфичности ядерного селектора (0,3,1) — при точном совпадении и
 * !important с обеих сторон побеждает порядок подключения, а
 * modules/multipage/public/assets/css/admin.css грузится ПОСЛЕ
 * assets/css/admin-shell.css (см. modules/multipage/admin.php) — .mp-admin
 * в начале селектора не просто "для специфичности", а реальный родитель
 * (весь контент модуля рендерится внутри него). */
.mp-admin button.mp-btn.mp-btn--primary { background: var(--main) !important; border-color: var(--main) !important; color: var(--main-contrast) !important; }
.mp-admin button.mp-btn.mp-btn--danger { background: color-mix(in srgb, var(--danger) 12%, transparent) !important; border-color: color-mix(in srgb, var(--danger) 35%, transparent) !important; color: var(--danger) !important; }
.mp-admin button.mp-btn.mp-btn--danger:hover { background: var(--danger) !important; border-color: var(--danger) !important; color: #fff !important; }
.mp-admin button.mp-btn.mp-btn--success { background: color-mix(in srgb, var(--success) 12%, transparent) !important; border-color: color-mix(in srgb, var(--success) 35%, transparent) !important; color: var(--success) !important; }
.mp-admin button.mp-btn.mp-btn--success:hover { background: var(--success) !important; border-color: var(--success) !important; color: #fff !important; }

/* Компактный вариант — списки/таблицы, где полноразмерная кнопка 44px
 * визуально слишком тяжёлая (несколько кнопок подряд в одной строке
 * таблицы), но touch-target по высоте всё равно не должен проваливаться
 * ниже разумного минимума. */
.mp-btn--sm {
    min-height: 34px;
    padding: 6px 12px;
    font-size: 12px;
}

/* Тот же приём, что и у трёх правил .mp-admin button.mp-btn.mp-btn--X чуть
 * выше (см. докблок там): ядерное button[type="submit"]:not(.del-btn):not
 * (.nav-logout-btn) (admin-shell.css:95) задаёт padding:15px 25px БЕЗ
 * !important, но его специфичность (0,3,1) РОВНО равна специфичности
 * голого .mp-btn--sm (0,1,0) < (0,3,1) — то есть ядерное правило побеждало
 * не через !important, а просто за счёт большей специфичности, и компактный
 * паддинг/min-height у <button class="mp-btn mp-btn--sm"> (без .del-btn/
 * .nav-logout-btn, например кнопка "Удалить" в categories.php) реально не
 * применялся. Поднимаем специфичность до той же (0,3,1) — .mp-admin
 * button.mp-btn.mp-btn--sm — и, как и у соседних правил, admin.css грузится
 * ПОСЛЕ admin-shell.css, так что порядок подключения решает при точном
 * совпадении специфичности и !important с обеих сторон. */
.mp-admin button.mp-btn.mp-btn--sm { padding: 6px 12px !important; min-height: 34px !important; }

/* ── Плавное появление списков/таблиц (замена прежнему "просто появилось
 * после перезагрузки страницы") ── MPA-архитектура всего проекта (полная
 * перезагрузка на каждое действие) — намеренная, не мини-SPA ради одной
 * анимации; .mp-stagger даёт ощущение "обновилось" на самой перерисовке. */
.mp-stagger > * {
    animation: mp-fade-up .3s var(--mp-ease) both;
}

.mp-stagger > *:nth-child(1) { animation-delay: .02s; }
.mp-stagger > *:nth-child(2) { animation-delay: .05s; }
.mp-stagger > *:nth-child(3) { animation-delay: .08s; }
.mp-stagger > *:nth-child(4) { animation-delay: .11s; }
.mp-stagger > *:nth-child(5) { animation-delay: .14s; }
.mp-stagger > *:nth-child(6) { animation-delay: .17s; }
.mp-stagger > *:nth-child(n+7) { animation-delay: .2s; }

@keyframes mp-fade-up {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
    .mp-page-header,
    .mp-stagger > *,
    .mp-card,
    .mp-stat-card,
    .mp-btn {
        animation: none !important;
        transition: none !important;
    }
}

/* ── Баннер self-detect (см. core_patch/reapply.php + mp_render_core_patch_banner(),
 * admin_helpers.php) — показывается, когда sync_core()/update_core() ядра
 * (admin/modules/settings/sync_core.php) стёр наши правки в 8 core-файлах
 * (helpers/module_registry.php, helpers/nav_layout.php и т.д. — см.
 * core_patch/README.md). Намеренно НЕ использует .mp-card — этот блок
 * обязан оставаться заметным на фоне обычных карточек страницы, не
 * теряться визуально среди них. */
.mp-alert {
    display: flex;
    gap: 14px;
    align-items: flex-start;
    padding: 16px 18px;
    border-radius: var(--radius-lg);
    border: 1px solid;
    margin-bottom: 20px;
    animation: mp-fade-up .35s var(--mp-ease) both;
}

.mp-alert--warning {
    background: color-mix(in srgb, var(--warning) 10%, var(--surface));
    border-color: color-mix(in srgb, var(--warning) 35%, transparent);
}

.mp-alert--success {
    background: color-mix(in srgb, var(--success) 10%, var(--surface));
    border-color: color-mix(in srgb, var(--success) 35%, transparent);
}

.mp-alert--danger {
    background: color-mix(in srgb, var(--danger) 10%, var(--surface));
    border-color: color-mix(in srgb, var(--danger) 35%, transparent);
}

.mp-alert__icon {
    flex-shrink: 0;
    width: 22px;
    height: 22px;
    margin-top: 1px;
}

.mp-alert--warning .mp-alert__icon { color: var(--warning); }
.mp-alert--success .mp-alert__icon { color: var(--success); }
.mp-alert--danger .mp-alert__icon { color: var(--danger); }

.mp-alert__body {
    flex: 1;
    min-width: 0;
}

.mp-alert__title {
    font-size: 14px;
    font-weight: 800;
    color: var(--text);
    margin-bottom: 3px;
}

.mp-alert__text {
    font-size: 13px;
    color: var(--muted);
    line-height: 1.5;
}

.mp-alert__actions {
    margin-top: 12px;
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}

.mp-alert__report {
    margin-top: 12px;
    padding: 10px 12px;
    background: var(--surface-2);
    border-radius: var(--radius);
    font-size: 12px;
    font-family: monospace;
    max-height: 220px;
    overflow-y: auto;
}

.mp-alert__report-line {
    padding: 3px 0;
    display: flex;
    gap: 8px;
    align-items: baseline;
}

.mp-alert__report-line + .mp-alert__report-line {
    border-top: 1px solid var(--border);
}

/* ── Пустое состояние ── */
.mp-empty {
    text-align: center;
    padding: 48px 20px;
    color: var(--muted);
}

.mp-empty__icon {
    width: 44px;
    height: 44px;
    margin: 0 auto 12px;
    color: var(--muted);
    opacity: .6;
}

.mp-empty__title {
    font-size: 15px;
    font-weight: 700;
    color: var(--text);
    margin-bottom: 4px;
}

/* ── Форма ── */
.mp-field {
    margin-bottom: 12px;
}

.mp-field label {
    display: block;
    font-size: 12px;
    color: var(--muted);
    margin-bottom: 4px;
    font-weight: 600;
}

.mp-field input,
.mp-field select,
.mp-field textarea {
    width: 100%;
    padding: 9px 11px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--input);
    color: var(--text);
    box-sizing: border-box;
    font-size: 14px;
    transition: border-color .15s var(--mp-ease), box-shadow .15s var(--mp-ease);
}

.mp-field input:focus,
.mp-field select:focus,
.mp-field textarea:focus {
    outline: none;
    border-color: var(--main);
    box-shadow: 0 0 0 3px var(--main-glass);
}

/* ── Таблица (когда собственный .table-responsive ядра недоступен/не
 * подходит контексту) — держит те же системные 14px, что и весь остальной
 * админ-интерфейс, вместо разрозненных 13px/11px по разным страницам
 * модуля. */
/* Обёртка списочных таблиц (categories/products/orders/pages.php): раньше
 * <table class="mp-table"> лежала прямо внутри <div class="mp-card"
 * style="padding:0;overflow:hidden;"> — overflow:hidden там нужен ТОЛЬКО
 * чтобы таблица не вылезала за скруглённые углы карточки, но в диапазоне
 * ~641-900px (между брейкпоинтом мобильной card-трансформации .mp-table
 * ниже, 640px, и шириной, где таблица целиком помещается) он же молча
 * ОБРЕЗАЛ содержимое, которое не влезло, вместо прокрутки — на products.php
 * это резало кнопки "Скрыть"/"Закрепить" в колонке действий, на orders.php
 * — <select name="status"> (виден был только "Нов" вместо "Новый",
 * независимый повторный аудит UX/UI, находка 2, High). Разделяем
 * ответственность на два вложенных контейнера: .mp-table-card держит
 * overflow:hidden ТОЛЬКО ради border-radius, а .mp-table-scroll вокруг
 * самой <table> даёт горизонтальный скролл именно там, где он нужен. */
.mp-table-card {
    padding: 0;
    overflow: hidden;
}

/* Индикатор горизонтального скролла (независимый повторный аудит UX/UI,
 * раунд 2, находка 2, Medium): overflow-x:auto сам по себе не подсказывает,
 * что таблицу можно проскроллить — на macOS с "показывать скроллбары только
 * во время скролла" нативный скроллбар не виден вообще, пока палец/курсор не
 * коснулся области. Классический CSS-приём "scroll shadow" без единой
 * строчки JS (4 фоновых слоя): два "крышечных" градиента цвета фона карточки
 * с background-attachment:local — они физически часть скроллящегося
 * содержимого, поэтому у текущего края скролла крышка перекрывает тень под
 * собой; и два градиента-тени с background-attachment:scroll — они, наоборот,
 * прибиты к вьюпорту .mp-table-scroll и никогда не двигаются. Итог: тень
 * видна только у того края, куда ЕЩЁ есть куда скроллить, и пропадает сама,
 * когда доскроллили до конца. Если контент вообще не переполняет контейнер
 * (нет скролла), обе крышки перекрывают обе тени целиком — эффекта не видно,
 * ничего вручную проверять не нужно. */
.mp-table-scroll {
    overflow-x: auto;
    background-color: var(--surface);
    background-repeat: no-repeat;
    background-image:
        linear-gradient(to right, var(--surface) 30%, transparent),
        linear-gradient(to left, var(--surface) 30%, transparent),
        linear-gradient(to right, color-mix(in srgb, var(--text) 18%, transparent), transparent),
        linear-gradient(to left, color-mix(in srgb, var(--text) 18%, transparent), transparent);
    background-position: 0 0, 100% 0, 0 0, 100% 0;
    background-size: 28px 100%, 28px 100%, 14px 100%, 14px 100%;
    background-attachment: local, local, scroll, scroll;
}

/* Раунд 3 аудита UX/UI (находка 2, Low): на тёмной теме 18%-примесь
 * var(--text) к transparent еле заметна — сама тень нужна только как слабая
 * подсказка "тут есть куда скроллить", но на тёмной теме она уходила ниже
 * порога, где эту подсказку вообще можно разглядеть. Тёмная/светлая тема
 * этого проекта переключается ЦЕЛИКОМ на сервере (admin/index.php,
 * $dark_mode из настроек) — активная тема попадает в CSS только готовым
 * набором var(--...) значений в инлайн :root, без класса/data-атрибута на
 * <html>/<body>, за который можно было бы зацепиться селектором здесь.
 * prefers-color-scheme — единственный сигнал тёмного/светлого режима,
 * доступный чисто в CSS без правки разметки; он завязан на предпочтение ОС/
 * браузера, а не на переключатель $dark_mode внутри самого приложения, но
 * это тот же компромисс, который прямо допускала находка аудита. Светлую
 * тему (ветка else — предпочтение ОС "light" или его отсутствие) этот блок
 * не трогает, он только переопределяет background-image поверх правила
 * выше. 40% — больше чем вдвое против базовых 18%, в том же порядке, что и
 * border-color: color-mix(... 35%, transparent) у .mp-btn--danger/--success
 * выше по файлу (там та же задача: примесь var(--...) должна остаться
 * читаемым тонким акцентом, а не сплошной заливкой), подтверждено визуально
 * скриншотом на тёмной теме ниже. */
@media (prefers-color-scheme: dark) {
    .mp-table-scroll {
        background-image:
            linear-gradient(to right, var(--surface) 30%, transparent),
            linear-gradient(to left, var(--surface) 30%, transparent),
            linear-gradient(to right, color-mix(in srgb, var(--text) 40%, transparent), transparent),
            linear-gradient(to left, color-mix(in srgb, var(--text) 40%, transparent), transparent);
    }
}

.mp-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}

/* Колонка "Название"/"Клиент" и т.п. — переменной длины текст без
 * собственной границы (независимый повторный аудит UX/UI, раунд 2, находка
 * 1, High): правило `.mp-table-scroll .mp-table { min-width: max-content; }`
 * ниже не имеет верхней границы само по себе — при реалистично длинном
 * названии товара (проверено: 89-символьное название) max-content-ширина
 * ЭТОЙ ОДНОЙ ячейки растягивает ВСЮ таблицу настолько, что колонки
 * Статус/Посл.заказ/Действия уезжают за край контейнера даже на 1440px
 * десктопе — кнопки модерации становятся недостижимы без горизонтального
 * скролла, которого на десктопе никто не ждёт. max-width здесь ограничивает
 * вклад именно этой ячейки в max-content-ширину таблицы (стандартное
 * поведение table-layout:auto — max-width на ячейке клэмпит её intrinsic-
 * ширину и включает перенос), остальные колонки остаются nowrap/компактными.
 * Перенос (white-space:normal вместо ellipsis) выбран сознательно — сама
 * находка просит решить по месту: обрезание с title-атрибутом прячет
 * часть названия товара за наведением, а перенос в 2-3 строки держит его
 * полностью читаемым сразу, ценой чуть большей высоты строки — это тот же
 * компромисс, что уже принят для .mp-alert__text/.mp-empty выше по файлу. */
.mp-table td.mp-col-wrap {
    max-width: 320px;
    white-space: normal;
    overflow-wrap: anywhere;
}

@media (min-width: 641px) {
    /* overflow-x:auto на .mp-table-scroll САМ ПО СЕБЕ ничего не решает,
     * пока таблица держит width:100% — table-layout:auto (умолчание) в
     * этом случае просто СЖИМАЕТ колонки, включая <select>/<button> внутри
     * них, ниже их естественной ширины, чтобы уложиться ровно в контейнер
     * (браузер это делает молча, без горизонтального overflow вообще) —
     * проверено вживую (orders.php, 768px): scrollWidth контейнера был
     * РАВЕН clientWidth, скролла не было, а <select name="status"> всё
     * равно показывал обрезанный текст ("Новь" вместо "Новый"), потому что
     * сам браузер сжал select ниже contentwidth его текста. min-width:
     * max-content запрещает таблице сжиматься ниже её естественной ширины
     * — именно тогда она превышает .mp-table-scroll и появляется реальный
     * горизонтальный скролл вместо тихого сжатия контента. Только для
     * >640px — ниже вступает мобильная card-трансформация того же файла
     * (thead:none, td:flex), где ширина самой <table> как элемента больше
     * не имеет значения. */
    .mp-table-scroll .mp-table {
        min-width: max-content;
    }
}

.mp-table th {
    text-align: left;
    font-size: 11px;
    font-weight: 700;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .04em;
    padding: 8px 10px;
    border-bottom: 1px solid var(--border);
}

.mp-table td {
    padding: 10px;
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
}

.mp-table tr:last-child td {
    border-bottom: none;
}

.mp-table tr {
    transition: background .15s var(--mp-ease);
}

.mp-table tbody tr:hover {
    background: var(--surface-2);
}

/* Раунд 3 аудита UX/UI (находка 1, High): все селекторы этого блока ниже —
 * ПРЯМЫЕ дочерние комбинаторы (.mp-table > tbody > tr > td), а не descendant
 * (.mp-table td). Причина: orders.php рендерит состав заказа как <table> БЕЗ
 * класса ВНУТРИ <td colspan="8"> строки заказа — а та строка сама лежит
 * внутри <table class="mp-table">. descendant-селектор `.mp-table td` не
 * видит границу вложенности, он совпадает с ЛЮБОЙ ячейкой на любой глубине
 * внутри .mp-table, включая ячейки этой вложенной таблицы товаров. Итог на
 * ≤640px (card-трансформация ниже): название/кол-во/цена товара
 * разваливались на 3 отдельных блока на всю ширину карточки вместо одной
 * компактной строки, а при заказе с 2+ позициями цена товара №1 шла сразу
 * перед названием товара №2 без визуального разделения — разобрать, где
 * чья цена, было тяжело (orders.php — самая операционно важная страница
 * модуля, её часто смотрят с телефона). На 768/1440px бага не было —
 * инлайн-стили самой вложенной таблицы (padding:3px 0 и т.п.) побеждают
 * class-based правила по специфичности, но `display` инлайном не задан, а
 * именно `display:flex` ломал раскладку на мобильных. Дочерний комбинатор
 * требует, чтобы ячейка была ПРЯМЫМ потомком строки — прямого потомка
 * tbody — прямого потомка самой .mp-table; у вложенной таблицы товаров
 * цепочка длиннее (ещё через <div>/саму вложенную <table>/её собственный
 * tbody), поэтому она перестаёт совпадать с этими селекторами вообще и
 * остаётся обычной HTML-таблицей — компактной по умолчанию, без ручной
 * стилизации "строка = карточка", как и было на десктопе. */
@media (max-width: 640px) {
    .mp-table > thead {
        display: none;
    }

    .mp-table > tbody > tr {
        display: block;
        border-bottom: 1px solid var(--border);
        padding: 10px 0;
    }

    /* Раньше — display:flex;justify-content:space-between в ОДНУ строку
     * (label слева, значение справа). При длинном SEO-заголовке/URL-слаге
     * значение не помещалось и обрезалось справа (кнопка "Редактировать"
     * читалась как "Редактиров…"), а .mp-table-scroll вокруг таблицы давал
     * обходной горизонтальный скролл — но скроллилась ВСЯ строка целиком,
     * включая label, так что после скролла подпись обрезалась до хвоста
     * ("ИЦА" вместо "СТРАНИЦА") — либо видна подпись без значения, либо
     * значение без подписи, никогда оба сразу (независимый повторный аудит
     * UX/UI, раунд 2, находка 3, High). Смысл всей card-трансформации —
     * вертикальная раскладка label→value БЕЗ горизонтальной прокрутки, а
     * прокрутка внутри одной ячейки эту семантику ломала. flex-direction:
     * column — label сверху, значение снизу, оба на всю ширину карточки;
     * overflow-wrap:anywhere переносит значение (в т.ч. слитную строку без
     * пробелов вроде slug/URL) вместо того, чтобы толкать ячейку за край. */
    .mp-table > tbody > tr > td {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 3px;
        border-bottom: none;
        padding: 6px 0;
        overflow-wrap: anywhere;
    }

    .mp-table > tbody > tr > td::before {
        content: attr(data-label);
        font-size: 11px;
        font-weight: 700;
        color: var(--muted);
        text-transform: uppercase;
        letter-spacing: .03em;
    }

    /* Пустой data-label (например колонка-превью с картинкой в
     * products.php, data-label="") не должен оставлять пустую "строку
     * подписи" перед значением — content:attr() с пустой строкой рендерит
     * ::before нулевой ширины/высоты, но всё равно как отдельный flex-item;
     * без этого исключения между ним и значением остаётся паразитный gap. */
    .mp-table > tbody > tr > td[data-label=""]::before {
        display: none;
    }

    .mp-table > tbody > tr > td[data-label=""] {
        gap: 0;
    }

    /* Вложенная таблица состава заказа (orders.php, .mp-order-items) теперь
     * вне досягаемости правил выше (см. комментарий к блоку) и остаётся
     * обычной table/table-row/table-cell раскладкой — но её колонки
     * названы под десктопную ширину карточки (100/60px под цену/кол-во).
     * На узкой мобильной карточке (~300px после паддингов) длинное
     * название товара может упереться в эти фиксированные колонки —
     * overflow-x:auto тут страхует от обрезания, ничего не обрезая молча,
     * тем же приёмом, что и .mp-table-scroll выше по файлу. */
    .mp-order-items-wrap {
        overflow-x: auto;
    }
}

/* ── Строка поиска (products.php: input[name=q] + select[name=filter] +
 * кнопка) ── На десктопе — обычный ряд. select был уже частично
 * подправлен раньше (flex:0 0 auto;width:auto вместо растягивания), но
 * width:auto считается по самому длинному русскому option ("Закреплено") —
 * на 375px это всё равно съедало большую часть строки, оставляя input
 * ~58px шириной, placeholder "Поиск..." обрезался до "По…" (независимый
 * повторный аудит UX/UI, находка 3, Medium). На мобильной ширине (тот же
 * брейкпоинт 640px, что и у .mp-table выше) переключаем ряд на перенос —
 * input и select уходят каждый на свою полную строку вместо дележа одной.
 * !important на дочерних селекторах ниже обязателен: инлайн-style="flex:1"
 * / "flex:0 0 auto;width:auto" прямо на <input>/<select> в products.php
 * (специфичность инлайн-style побеждает ЛЮБОЙ внешний селектор без
 * !important, независимо от классов). */
.mp-search-row {
    display: flex;
    gap: 8px;
    margin-bottom: 14px;
}

@media (max-width: 640px) {
    .mp-search-row {
        flex-wrap: wrap;
    }

    .mp-search-row input[type="text"],
    .mp-search-row select {
        flex: 1 1 100% !important;
        width: 100% !important;
    }
}

/* ============================================================
 * ── sources.php — карточная сетка "Dense operational grid" ──
 * (редизайн 2026-08-05, см. докблок modules/multipage/admin_pages/
 * sources.php). Раньше страница была голой <table> с двумя <select> в
 * инлайн-стилях — ничего в самой строке не сигналило "эта нода
 * переопределена", владельцу приходилось сравнивать оба select'а с
 * дефолтным текстом построчно. Карточка делает это ПЕРВЫМ визуальным
 * сигналом (акцент сверху + пульсирующий бейдж + тонированный аватар) —
 * тот же язык, что уже одобрен владельцем на .nm-card/.nm-badge в
 * master_core/network_manager.php (карточки нод сети), не копия, а тот же
 * "семейный" приём под другой домен данных (свежесть+категория, не
 * онлайн/офлайн).
 *
 * Namespace mp-src-* — ПОЛНОСТЬЮ локален этой одной странице, ни один
 * класс отсюда не переиспользуется на других 5 страницах модуля. Где
 * смысл совпадает с уже существующими компонентами (цвет бейджа, вход
 * .mp-stagger) — переиспользуются реальные классы admin.css напрямую в
 * разметке (mp-badge--main/--muted, mp-stagger), они здесь не
 * переопределяются.
 *
 * Свежесть — сегментированный ряд pill'ов поверх ОБЫЧНЫХ <input
 * type="radio">, не JS-кнопок: раскладка "невидимый input поверх
 * подписанного label" (тот же приём, что уже ниже у .mp-src-switch) даёт
 * рабочую форму БЕЗ единой строчки JS — <script> внизу sources.php только
 * подсвечивает состояние карточки (акцент/бейдж/кнопка "сбросить") и
 * фильтрует список по поиску/переключателю, ничего из этого не требуется
 * для самого сохранения (overrides[nid][days]/[category_id] — обычные
 * именованные поля формы, POST-хендлер в sources.php их не касается).
 * ============================================================ */

.mp-src-count {
    font-size: 13px;
    color: var(--muted);
    margin: 0 0 18px;
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.mp-src-count .dot {
    width: 3px;
    height: 3px;
    border-radius: 50%;
    background: var(--muted);
    flex-shrink: 0;
}

.mp-src-count .override-count {
    color: var(--main);
    font-weight: 800;
}

.mp-src-hint {
    font-size: 12px;
    color: var(--muted);
    margin: 0 0 16px;
    max-width: 640px;
    line-height: 1.5;
}

/* ── строка-контекст: глобальное умолчание свежести остаётся видимым на
 * уровне страницы, а не теряется среди per-node карточек ниже (явное
 * требование ТЗ редизайна). ── */
.mp-src-context {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 11px 16px;
    margin-bottom: 20px;
    font-size: 12.5px;
    animation: mp-fade-up .35s var(--mp-ease) both;
}

.mp-src-context svg {
    flex-shrink: 0;
    color: var(--muted);
}

.mp-src-context a {
    color: var(--main);
    text-decoration: none;
    font-weight: 700;
    margin-left: auto;
    white-space: nowrap;
}

.mp-src-context a:hover {
    text-decoration: underline;
}

/* ── тулбар: поиск + переключатель "только переопределённые" — то, что
 * держит сетку читаемой за пределами ~8-10 нод (тот же .nm-toolbar-приём,
 * что уже одобрен в network_manager.php). ── */
.mp-src-toolbar {
    display: flex;
    gap: 10px;
    margin-bottom: 16px;
    flex-wrap: wrap;
    align-items: center;
}

.mp-src-search-wrap {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1;
    min-width: 200px;
    background: var(--input);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0 14px;
    transition: border-color .2s var(--mp-ease), box-shadow .2s var(--mp-ease);
}

/* box-shadow glow — тот же паттерн, что .mp-src-select:focus/.mp-field
 * input:focus чуть выше по файлу, а не только смена border-color (был
 * скопирован из network_manager.php's .nm-search-wrap:focus-within без
 * этой части паттерна) — независимый повторный аудит доступности,
 * 2026-08-05: единственный видимый фокус-индикатор строки поиска был
 * тоньше, чем у всех остальных новых полей в этом же редизайне. */
.mp-src-search-wrap:focus-within {
    border-color: var(--main);
    box-shadow: 0 0 0 3px var(--main-glass);
}

.mp-src-search-wrap svg {
    flex-shrink: 0;
    color: var(--muted);
}

.mp-src-search-wrap input {
    flex: 1;
    padding: 10px 0;
    background: transparent;
    border: none;
    outline: none;
    color: var(--text);
    font-size: 13px;
    min-width: 0;
}

.mp-src-toggle {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 9px 14px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font-size: 12.5px;
    color: var(--muted);
    cursor: pointer;
    white-space: nowrap;
    user-select: none;
    transition: border-color .15s var(--mp-ease), color .15s var(--mp-ease);
}

.mp-src-toggle:hover {
    color: var(--text);
}

.mp-src-switch {
    position: relative;
    flex-shrink: 0;
    width: 32px;
    height: 19px;
}

.mp-src-switch input {
    position: absolute;
    opacity: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    cursor: pointer;
}

.mp-src-switch-track {
    position: absolute;
    inset: 0;
    background: var(--input);
    border: 1px solid var(--border);
    border-radius: 999px;
    transition: background .2s var(--mp-ease), border-color .2s var(--mp-ease);
}

.mp-src-switch-track::before {
    content: '';
    position: absolute;
    top: 1px;
    left: 1px;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    background: var(--muted);
    transition: transform .2s var(--mp-ease), background .2s var(--mp-ease);
}

.mp-src-switch input:checked ~ .mp-src-switch-track {
    background: var(--main-glass);
    border-color: var(--main);
}

.mp-src-switch input:checked ~ .mp-src-switch-track::before {
    transform: translateX(13px);
    background: var(--main);
}

.mp-src-switch input:focus-visible ~ .mp-src-switch-track {
    outline: 2px solid var(--main);
    outline-offset: 2px;
}

/* ── сетка карточек ── */
.mp-src-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 16px;
    /* Резерв места под .mp-src-savebar (sticky, bottom:16px, ~56px высоты
     * содержимого + 28px паддингов) — без этого запаса у sticky-элемента
     * физически нет "свободного" пространства прокрутки под последней
     * строкой сетки, и панель сохранения наплывает на карточки последнего
     * ряда вместо того, чтобы просто плавать над пустым местом (обнаружено
     * визуальной проверкой скриншотом, 2026-08-05). */
    margin-bottom: 96px;
}

.mp-src-empty-search {
    grid-column: 1 / -1;
    text-align: center;
    padding: 36px 20px;
    color: var(--muted);
    font-size: 13px;
    border: 1px dashed var(--border);
    border-radius: var(--radius-lg);
}

/* ── карточка ── */
.mp-src-card {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    transition: border-color .2s var(--mp-ease), transform .18s var(--mp-ease), box-shadow .2s var(--mp-ease);
}

.mp-src-card:hover {
    border-color: color-mix(in srgb, var(--main) 45%, var(--border));
    transform: translateY(-2px);
    box-shadow: var(--card-shadow);
}

.mp-src-card__accent {
    height: 3px;
    flex-shrink: 0;
    background: var(--border);
    transition: background .25s var(--mp-ease), box-shadow .25s var(--mp-ease);
}

.mp-src-card--override .mp-src-card__accent {
    background: linear-gradient(90deg, var(--main), var(--main-dark) 70%);
    box-shadow: 0 0 12px -2px var(--main-glass);
}

.mp-src-card__body {
    padding: 18px;
    display: flex;
    flex-direction: column;
    gap: 14px;
    flex: 1;
}

.mp-src-card__head {
    display: flex;
    align-items: flex-start;
    gap: 11px;
}

.mp-src-avatar {
    width: 38px;
    height: 38px;
    border-radius: 11px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    font-weight: 900;
    letter-spacing: -.4px;
    background: var(--surface);
    color: var(--muted);
    border: 1px solid var(--border);
    transition: background .2s var(--mp-ease), color .2s var(--mp-ease), border-color .2s var(--mp-ease);
}

.mp-src-card--override .mp-src-avatar {
    background: var(--main-glass);
    color: var(--main);
    border-color: color-mix(in srgb, var(--main) 40%, transparent);
}

.mp-src-meta {
    flex: 1;
    min-width: 0;
}

.mp-src-name {
    font-size: 14.5px;
    font-weight: 800;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.mp-src-sub {
    font-size: 11px;
    color: var(--muted);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    margin-top: 2px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ── бейдж "Авто"/"Переопределено" — компонует ЦВЕТ из уже существующих
 * .mp-badge--main/--muted (см. разметку sources.php), форма/точка —
 * собственные, компактнее базового .mp-badge под плотность карточки. ── */
.mp-src-badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    flex-shrink: 0;
    padding: 4px 9px 4px 8px;
    border-radius: 999px;
    font-size: 10px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .4px;
    white-space: nowrap;
    transition: background .2s var(--mp-ease), color .2s var(--mp-ease);
}

.mp-src-badge-dot {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}

.mp-src-card--override .mp-src-badge-dot {
    animation: mp-src-pulse 2s infinite;
}

@keyframes mp-src-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: .3; }
}

.mp-src-field {
    display: flex;
    flex-direction: column;
    gap: 7px;
}

.mp-src-field__label {
    display: block;
    font-size: 10.5px;
    font-weight: 800;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .05em;
}

/* ── pill'ы свежести — невидимый <input type="radio"> поверх подписанного
 * <label> (см. докблок секции выше) — родные radio-семантика/клавиатура,
 * никакого JS не требуется для самой работы формы. ── */
.mp-src-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
}

.mp-src-pill-wrap {
    position: relative;
    display: inline-flex;
}

.mp-src-pill-input {
    position: absolute;
    inset: 0;
    opacity: 0;
    margin: 0;
    cursor: pointer;
}

.mp-src-pill {
    font-size: 11.5px;
    font-weight: 700;
    line-height: 1;
    padding: 7px 11px;
    border-radius: 999px;
    cursor: pointer;
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--muted);
    user-select: none;
    /* pointer-events:none обязателен: transform в :active-правиле ниже
     * создаёт stacking context, label в момент нажатия всплывает НАД
     * absolute-инпутом и перехватывает mouseup — mousedown/mouseup попадают
     * в разные элементы, click уходит в обёртку и radio вообще не
     * выбирается (подтверждено Playwright-репро: checked=false при любом
     * темпе клика). Все события должны всегда идти в сам инпут, который
     * покрывает пилюлю целиком (inset:0). */
    pointer-events: none;
    transition: background .15s var(--mp-ease), border-color .15s var(--mp-ease), color .15s var(--mp-ease);
}

.mp-src-pill-input:hover + .mp-src-pill {
    color: var(--text);
    border-color: color-mix(in srgb, var(--main) 35%, var(--border));
}

.mp-src-pill-input:active + .mp-src-pill {
    transform: scale(.96);
}

.mp-src-pill--auto {
    border-style: dashed;
}

.mp-src-pill-input:checked + .mp-src-pill--auto {
    background: color-mix(in srgb, var(--muted) 16%, transparent);
    border-color: var(--muted);
    border-style: solid;
    color: var(--text);
}

.mp-src-pill-input:checked + .mp-src-pill:not(.mp-src-pill--auto) {
    background: var(--main);
    border-color: var(--main);
    color: var(--main-contrast);
    box-shadow: 0 2px 9px -2px var(--main-glass);
}

.mp-src-pill-input:focus-visible + .mp-src-pill {
    outline: 2px solid var(--main);
    outline-offset: 2px;
}

/* ── категория — обычный <select>, стилизованный под систему (список
 * открытый, 20+ категорий — сегментированный ряд, как у свежести, здесь
 * перестал бы масштабироваться ровно там, где pill'ы ещё держатся). Тот же
 * inline-SVG data-URI со стрелкой (stroke='%23888'), что уже применяет
 * .nm-filter в master_core/network_manager.php — единственное место файла,
 * где цвет не идёт через var(--...): встроенный в data-URI SVG физически
 * не может ссылаться на CSS custom property, тот же обоснованный
 * прецедент, что и в справочном файле. ── */
.mp-src-select-wrap {
    position: relative;
}

.mp-src-select {
    width: 100%;
    appearance: none;
    -webkit-appearance: none;
    padding: 9px 32px 9px 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--input);
    color: var(--text);
    font: inherit;
    font-size: 12.5px;
    cursor: pointer;
    transition: border-color .15s var(--mp-ease), box-shadow .15s var(--mp-ease);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2.5' stroke-linecap='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 11px center;
}

.mp-src-select:focus {
    outline: none;
    border-color: var(--main);
    box-shadow: 0 0 0 3px var(--main-glass);
}

.mp-src-select.is-set {
    border-color: color-mix(in srgb, var(--main) 45%, var(--border));
}

.mp-src-foot {
    display: flex;
    align-items: center;
    min-height: 16px;
    margin-top: auto;
    padding-top: 2px;
}

.mp-src-reset {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    font-size: 11.5px;
    font-weight: 700;
    color: var(--muted);
    cursor: pointer;
    transition: color .15s var(--mp-ease);
}

.mp-src-reset:hover {
    color: var(--danger);
}

.mp-src-reset[hidden] {
    display: none;
}

/* ── панель сохранения — один submit на всю форму, то же поведение
 * сохранения, что и раньше у одной кнопки под таблицей. ── */
.mp-src-savebar {
    position: sticky;
    bottom: 16px;
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    margin-top: 22px;
    padding: 14px 18px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--card-shadow);
}

.mp-src-savebar__hint {
    font-size: 11.5px;
    color: var(--muted);
    flex: 1;
    min-width: 180px;
}

@media (prefers-reduced-motion: reduce) {
    .mp-src-card,
    .mp-src-context,
    .mp-src-badge-dot,
    .mp-src-pill,
    .mp-src-switch-track,
    .mp-src-switch-track::before,
    .mp-src-select {
        animation: none !important;
        transition: none !important;
    }
}

@media (max-width: 640px) {
    .mp-src-grid {
        grid-template-columns: 1fr;
    }

    .mp-src-savebar {
        flex-direction: column;
        align-items: stretch;
    }

    .mp-src-savebar__hint {
        order: 2;
        text-align: center;
    }
}

/* ============================================================
 * ── orders.php — точечный полироль (2026-08-05) ──
 * Не редизайн: список остаётся <table> (100 последних заказов — таблица
 * остаётся верным примитивом на этом объёме, тот же вывод, что и у
 * карточной сетки sources.php чуть выше по файлу). Три точечных изменения:
 * счётчик на пилюлях фильтра, busy-индикатор у select'а смены статуса,
 * лёгкая связка колонок "Статус"/"Статус в сети".
 *
 * Namespace mp-ord-* — локален этой одной странице, тот же принцип
 * изоляции, что и у mp-src-* выше. Цвет пилюли/акцента идёт через
 * var(--mp-ord-accent), заданную инлайн в orders.php как var(--success) и
 * т.п. — тот же приём инлайн-custom-property, что уже применяет
 * mp_render_stat_card() через --mp-accent (admin_helpers.php), не
 * изобретаем параллельный способ прокинуть цвет темы в разметку.
 * ============================================================ */

.mp-ord-filters {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.mp-ord-pill-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 17px;
    height: 17px;
    padding: 0 5px;
    border-radius: 999px;
    font-size: 10.5px;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    background: color-mix(in srgb, var(--mp-ord-accent, var(--muted)) 18%, transparent);
    color: var(--mp-ord-accent, var(--muted));
    transition: background .15s var(--mp-ease), color .15s var(--mp-ease);
}

/* Активная (текущий фильтр) пилюля стоит на сплошном var(--main) фоне
 * (.mp-btn--primary) — цветной счётчик поверх него читался бы плохо
 * (недостаточный контраст warning/success-оттенка на фоне var(--main)),
 * инвертируем в var(--main-contrast), тот же приём, что уже применён к
 * активному .mp-src-pill (не .mp-src-pill--auto) выше по файлу. */
.mp-ord-pill.mp-btn--primary .mp-ord-pill-count {
    background: color-mix(in srgb, var(--main-contrast) 25%, transparent);
    color: var(--main-contrast);
}

/* ── select статуса в колонке "Действия" — раньше padding/border/etc. были
 * инлайн-стилем прямо на orders.php, здесь вынесены в класс + добавлен
 * focus-стейт, которого не было вообще (клавиатурная навигация падала на
 * голый браузерный дефолт — ни одно другое поле ввода в этой системе так
 * не выглядит, см. .mp-field input:focus/.mp-src-select:focus выше). ── */
.mp-ord-status-wrap {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.mp-ord-status-select {
    padding: 7px 10px;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--input);
    color: var(--text);
    font: inherit;
    font-size: 12.5px;
    cursor: pointer;
    transition: border-color .15s var(--mp-ease), box-shadow .15s var(--mp-ease), opacity .15s var(--mp-ease);
}

.mp-ord-status-select:focus {
    outline: none;
    border-color: var(--main);
    box-shadow: 0 0 0 3px var(--main-glass);
}

/* ── лёгкий busy-индикатор на время POST-сабмита (реальная перезагрузка
 * страницы в этой MPA-архитектуре, не AJAX) — на медленной сети админ
 * раньше не видел никакого отклика между кликом на статус и завершением
 * перезагрузки, что читалось как "зависло". pointer-events:none на
 * ОБЁРТКЕ (не на самом <select>) — значение статуса, уже захваченное в
 * этот submit(), не теряется (disabled-поля не уходят в POST), просто
 * блокируется повторный клик до конца перезагрузки. ── */
.mp-ord-status-spinner {
    display: none;
    width: 13px;
    height: 13px;
    flex-shrink: 0;
    border-radius: 50%;
    border: 2px solid var(--border);
    border-top-color: var(--main);
    animation: mp-ord-spin .6s linear infinite;
}

.mp-ord-status-wrap.is-saving .mp-ord-status-select {
    opacity: .6;
    pointer-events: none;
}

.mp-ord-status-wrap.is-saving .mp-ord-status-spinner {
    display: inline-block;
}

@keyframes mp-ord-spin {
    to { transform: rotate(360deg); }
}

/* ── связка "Статус" → "Статус в сети": маленькая мьютированная стрелка
 * перед бейджем сети читает колонку как "то, во что превратился локальный
 * статус на мастере", а не два несвязанных значения рядом. Рендерится
 * ТОЛЬКО когда есть реальный бейдж сети (см. $_net_label в orders.php) —
 * перед прочерком "—" (мастер недоступен/нет строки) стрелка смысла не
 * добавляет. ── */
.mp-ord-net-link {
    display: inline-flex;
    color: var(--muted);
    opacity: .55;
    margin-right: 3px;
    vertical-align: middle;
}

@media (prefers-reduced-motion: reduce) {
    .mp-ord-pill-count,
    .mp-ord-status-select,
    .mp-ord-status-spinner {
        animation: none !important;
        transition: none !important;
    }
}

/* ============================================================
 * ── admin.php — строка живого контекста шапки-обёртки ──
 * (mp_render_wrap_context(), admin_helpers.php, 2026-08-05) — последний
 * прогон синка + счётчик открытых конфликтов, если есть. Рендерится
 * admin.php сразу под .mp-page-header на всех 7 под-страницах модуля (на
 * dashboard.php — только часть про синк, без счётчика конфликтов: там уже
 * есть собственная полноразмерная карточка с тем же списком моделей, см.
 * докблок mp_render_wrap_context()).
 *
 * Сознательно НЕ .mp-card/.mp-alert — это ambient-метаданные, а не
 * отдельный содержательный блок, вес карточки/алерта здесь конкурировал бы
 * с самим заголовком раздела прямо над ним. Ближе по весу к обычному
 * подзаголовку: мелкий шрифт, var(--muted), без фона/рамки.
 * ============================================================ */
.mp-wrap-context {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    font-size: 12px;
    color: var(--muted);
    margin: -10px 0 20px;
    animation: mp-fade-up .35s var(--mp-ease) both;
    animation-delay: .05s;
}

.mp-wrap-context__item {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

.mp-wrap-context__sep {
    color: var(--border);
}

a.mp-wrap-context__item--warn {
    color: var(--warning);
    text-decoration: none;
    font-weight: 700;
}

a.mp-wrap-context__item--warn:hover {
    text-decoration: underline;
}

@media (prefers-reduced-motion: reduce) {
    .mp-wrap-context {
        animation: none !important;
    }
}

/* ============================================================
 * Настройки модуля (admin_pages/settings.php) — редизайн 2026-08-05
 * (владелец: настройки модуля живут в ОДНОМ месте — здесь, в группе
 * МНОГОСТРАНИЧНИК; вкладка "Модули" общих настроек ноды упразднена).
 * Та же визуальная система, что "Dense operational grid" источников:
 * сетка карточек-секций, цветные иконко-чипы, savebar, .mp-stagger.
 * ============================================================ */

.mp-set-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 20px;
    align-items: start;
    margin-bottom: 20px;
}

.mp-set-card {
    display: flex;
    flex-direction: column;
}

.mp-set-card__head {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    margin-bottom: 16px;
    padding-bottom: 14px;
    border-bottom: 1px solid var(--border);
}

.mp-set-card__icon {
    flex-shrink: 0;
    width: 34px;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 10px;
    background: var(--main-glass);
    color: var(--main);
}

.mp-set-card__icon svg {
    width: 17px;
    height: 17px;
}

.mp-set-card__titles h3 {
    margin: 0;
    font-size: 14.5px;
    font-weight: 800;
    letter-spacing: .2px;
}

.mp-set-card__titles p {
    margin: 4px 0 0;
    font-size: 11.5px;
    line-height: 1.45;
    color: var(--muted);
}

.mp-set-sync-label {
    font-size: 12px;
    color: var(--muted);
    font-weight: 600;
    margin-bottom: 8px;
}

.mp-set-note {
    font-size: 11.5px;
    line-height: 1.5;
    color: var(--muted);
    margin: 12px 0 0;
}

.mp-set-link {
    display: inline-block;
    margin-top: 10px;
    font-size: 12px;
    font-weight: 700;
    color: var(--main);
    text-decoration: none;
    transition: opacity .15s var(--mp-ease);
}

.mp-set-link:hover {
    opacity: .75;
    text-decoration: underline;
}

/* Статус-карточка мастера: тонкая цветовая подводка слева по состоянию —
 * ok/подключено или danger/не подключено видно с расстояния, без чтения. */
.mp-set-card--status {
    border-left: 3px solid var(--success);
}

.mp-set-card--status.is-bad {
    border-left-color: var(--danger);
}

.mp-set-master-status {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.mp-set-master-url {
    font-size: 12px;
    color: var(--text);
    font-weight: 600;
    word-break: break-all;
}

.mp-set-master-src {
    font-size: 11px;
    color: var(--muted);
}

.mp-set-token-badge {
    margin-left: 6px;
    font-size: 10px;
    padding: 1px 7px;
    vertical-align: 1px;
}

@media (max-width: 640px) {
    .mp-set-grid {
        grid-template-columns: 1fr;
    }
}
