/* ===========================================================================
   admin.css — оболочка админки, экран входа, экран отказа и те примитивы,
   на которых стоит всё остальное: кнопки, поля, карточка с риббоном.
   TASKS P5-1 … P5-13. Дизайн — design/README.md §Admin, админский
   экран прототипа design/CFA Site.dc.html и разделы «Buttons» и
   «Forms (admin)» стайлгайда.

   Подключается последним, после tokens.css и base.css. На публичной
   странице не подключается никогда, и наоборот: layout.css и
   components.css в админку не едут. Единственный общий с сайтом файл —
   content.css: его подключает экран редактора (P5-4), потому что
   поверхность редактирования — тот же самый `.cfa-rt`, и это условие
   «WYSIWYG = live». Правил `.cfa-rt` в этом файле нет ни одного, и
   появиться они здесь не должны — см. шапку content.css.

   ---------------------------------------------------------------------------
   ЧЕМ ЭТОТ ФАЙЛ ОТЛИЧАЕТСЯ ОТ ОСТАЛЬНОГО CSS ПРОЕКТА

   DESIGN §10, преамбула: «Админка — отдельная история: ей достаточно ярус A
   (ею пользуются 2–4 человека), декоративные фолбэки там не нужны». Из этого
   следуют три поблажки, и они же прописаны в профиле `admin.css` у линтера
   (SKIP в tools/css-lint.php):

   1. `gap` разрешён. Он здесь и используется — топбар, строки форм, группы
      кнопок. В ярусе A (Chrome 83+, Safari 13.1+) flex-gap есть везде, а
      изобретать margin-схемы ради браузера, в котором админку не откроют,
      смысла нет. На публичной странице `gap` по-прежнему запрещён.
   2. Пара «статика + var()» не нужна. Токен пишется одной строкой.
   3. `clamp()`/`min()` можно писать без статической страховки.

   Что НЕ поблажка и соблюдается здесь так же строго:
   — хексов в файле нет ни одного, все значения из tokens.css (туда же
     уехали админские: `--g-adm-top`, `--sh-btn-*`, `--ts-*` и прочие);
   — запрещённый синтаксис из §10 п.13 запрещён и тут: ни `:has()`, ни
     `:is()`/`:where()`, ни вложенности, ни `inset`, ни логических свойств,
     ни `aspect-ratio`, ни `display:contents`;
   — `position:sticky` только у топбара и у полосы тулбара в редакторе,
     и потеря прилипания безвредна в обоих случаях (§10 п.10).

   ---------------------------------------------------------------------------
   ПОРЯДОК РАЗДЕЛОВ
     1. Примитивы: кнопки, поля, карточка
     2. Оболочка: топбар, табы, чипы, полоса контента
     3. Экран входа
     4. Экран «Admins only»
     5. Таблицы: общая раскладка строк и шапки
     6. Список страниц
     7. Пользователи
     8. Экран подтверждения удаления
     9. Редактор страницы: два режима, панель настроек, тост
    10. Настройки сайта: сетка карточек, ряды, тумблер, палитра
    11. Заглушки нереализованных экранов (уйдут вместе с ними)
   =========================================================================== */


/* ===========================================================================
   1. ПРИМИТИВЫ

   Кнопки собраны как база + тема. База — геометрия и типографика, тема —
   борт, градиент, тень и контур текста. Четыре темы из стайлгайда: золото
   (CTA), небо (primary), хром (secondary), красный (danger).

   Состояния у всех тем одинаковые по смыслу и разные по значению: при
   наведении кнопка светлеет и поднимается на 1px, при нажатии опускается
   на 3–4px, а сплошная тень снизу сжимается в 1px — из этого и получается
   ощущение физической кнопки, на котором держится весь дизайн 2010-х.
   =========================================================================== */

.adm-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;

    font-family: var(--f-display);
    font-weight: 800;
    font-style: italic;
    font-size: 16px;
    line-height: 1.2;
    text-transform: uppercase;
    white-space: nowrap;

    padding: 10px 20px;
    border-radius: var(--r-md);
    border: 2px solid transparent;

    /* Кнопка бывает и ссылкой (`<a class="adm-btn">`), а base.css красит
       ссылки в синий и подчёркивает. Цвет ставит тема, подчёркивание
       снимается здесь — иначе оно осталось бы поверх глянца. */
    text-decoration: none;

    transition: filter .12s, transform .08s, box-shadow .08s;
}

/* Ссылка-кнопка не должна менять цвет по правилу `a:hover` из base.css:
   у глянцевой кнопки цвет текста — часть темы, а не состояние. */
.adm-btn:hover,
.adm-btn:focus {
    text-decoration: none;
}

@media (prefers-reduced-motion: reduce) {
    .adm-btn {
        transition: none;
    }
}

/* --- Золото: главное действие на экране (Save, Log in, + New) ------------ */

.adm-btn--gold {
    color: var(--c-chrome-0);
    border-color: var(--c-gold-border);
    background: var(--g-gold);
    box-shadow: var(--sh-btn-gold);
    text-shadow: var(--ts-gold);
}

.adm-btn--gold:hover,
.adm-btn--gold:focus {
    color: var(--c-chrome-0);
    filter: brightness(1.1) saturate(1.1);
    transform: translateY(-1px);
}

.adm-btn--gold:active {
    transform: translateY(4px);
    box-shadow: var(--sh-btn-gold-on);
}

/* --- Небо: обычное действие ---------------------------------------------- */

.adm-btn--sky {
    color: var(--c-chrome-0);
    border-color: var(--c-outline);
    background: var(--g-sky);
    box-shadow: var(--sh-btn-sky);
    text-shadow: var(--ts-sky);
}

.adm-btn--sky:hover,
.adm-btn--sky:focus {
    color: var(--c-chrome-0);
    filter: brightness(1.1);
    transform: translateY(-1px);
}

.adm-btn--sky:active {
    transform: translateY(3px);
    box-shadow: var(--sh-btn-sky-on);
}

/* --- Хром: отмена, второстепенное --------------------------------------- */

.adm-btn--chrome {
    color: var(--c-ink);
    border-color: var(--c-frame);
    background: var(--g-chrome);
    box-shadow: var(--sh-btn-chrome);
}

.adm-btn--chrome:hover,
.adm-btn--chrome:focus {
    color: var(--c-ink);
    filter: brightness(1.05);
    transform: translateY(-1px);
}

.adm-btn--chrome:active {
    transform: translateY(3px);
    box-shadow: var(--sh-btn-chrome-on);
}

/* --- Красный: выход и удаление ------------------------------------------ */

.adm-btn--red {
    color: var(--c-chrome-0);
    border-color: var(--c-red-border);
    background: var(--g-red);
    box-shadow: var(--sh-btn-red);
    text-shadow: var(--ts-red);
}

.adm-btn--red:hover,
.adm-btn--red:focus {
    color: var(--c-chrome-0);
    filter: brightness(1.1);
    transform: translateY(-1px);
}

.adm-btn--red:active {
    transform: translateY(3px);
    box-shadow: var(--sh-btn-red-on);
}

/* --- Размеры ------------------------------------------------------------- */

/* Крупная: единственная на экране входа, и «Download» в стайлгайде. */
.adm-btn--lg {
    font-weight: 900;
    font-size: 22px;
    letter-spacing: .04em;
    padding: 14px 30px;
    border-radius: var(--r-lg);
}

/* Мелкая: в риббонах и строках таблиц. Не курсивная и не глянцево-объёмная —
   у неё тень в 2px, иначе ряд одинаковых кнопок в таблице начинает рябить. */
.adm-btn--sm {
    font-style: normal;
    font-size: var(--fs-small);
    letter-spacing: .06em;
    padding: 6px 12px;
    border-radius: var(--r-sm);
    border-width: 1px;
}

.adm-btn--sm.adm-btn--sky {
    background: var(--g-sky-soft);
    box-shadow: var(--sh-btn-sm);
    text-shadow: none;
}

.adm-btn--sm.adm-btn--chrome {
    background: var(--g-chrome-soft);
    box-shadow: none;
}

.adm-btn--sm.adm-btn--chrome:hover,
.adm-btn--sm.adm-btn--chrome:focus {
    color: var(--c-deep-sky);
}

/* Мелкая красная — не глянцевая, а контурная: в строке таблицы заливка
   кричала бы громче самой страницы, которую удаляют. Так и в прототипе. */
.adm-btn--sm.adm-btn--red {
    color: var(--c-red-dark);
    border-color: var(--c-red);
    background: var(--c-surface);
    box-shadow: none;
    text-shadow: none;
}

.adm-btn--sm.adm-btn--red:hover,
.adm-btn--sm.adm-btn--red:focus {
    color: var(--c-red-dark);
    background: var(--c-bad-bg);
    filter: none;
}

.adm-btn--sm:active {
    transform: translateY(1px);
}

/* Выключенная: ни глянца, ни тени — она не нажимается, и это должно быть
   видно, а не угадываться по курсору. */
.adm-btn[disabled],
.adm-btn[aria-disabled='true'] {
    cursor: not-allowed;
    color: var(--c-off-ink);
    border-color: var(--c-off-border);
    background: var(--g-off);
    box-shadow: none;
    text-shadow: none;
    filter: none;
    transform: none;
}

/* --- Поля ввода --------------------------------------------------------- */

/* Подпись и поле — один `<label>` колонкой: подпись Exo 2, поле Titillium.
   Так в прототипе и в стайлгайде, и так клик по подписи попадает в поле без
   `for`/`id` на каждой строке. */
.adm-field {
    display: flex;
    flex-direction: column;
    gap: 6px;

    font-family: var(--f-display);
    font-weight: 800;
    font-size: 14px;
    color: var(--c-navy);
}

.adm-input,
.adm-select,
.adm-textarea {
    font-family: var(--f-body);
    font-weight: 400;
    font-size: var(--fs-body);
    color: var(--c-ink);

    padding: 10px 12px;
    border-radius: var(--r-md);
    border: 2px solid var(--c-input-border);
    background: var(--g-input);
    box-shadow: var(--sh-input);
}

/* Логин набирается монотипным: в нём значимы точки, дефисы и подчёркивания,
   и в пропорциональном шрифте «misaki_1» от «misaki.1» не отличить. */
.adm-input--mono {
    font-family: var(--f-mono);
    font-size: 14px;
}

.adm-select {
    /* У выпадающего списка свой, более светлый градиент: у него справа
       рисуется системная стрелка, и тёмный верх под ней выглядит грязно. */
    background: var(--g-select);
    box-shadow: none;
}

.adm-textarea {
    min-height: 120px;
    line-height: var(--lh-body);
}

/* Фокус виден обводкой в 3px, а не системным outline: системный рисуется
   вплотную к рамке и на 2px-рамке почти не читается. Снимать его без замены
   нельзя — это единственный ориентир при работе с клавиатуры. */
.adm-input:focus,
.adm-select:focus,
.adm-textarea:focus {
    outline: none;
    border-color: var(--c-sky);
    box-shadow: var(--sh-focus);
}

/* Поле, которое не прошло проверку. Рамка И фон: только по цвету рамки
   ошибку не заметит тот, кто не различает красный и синий. */
.adm-input.is-bad,
.adm-select.is-bad,
.adm-textarea.is-bad {
    border-color: var(--c-red);
    background: var(--c-bad-field);
    box-shadow: none;
}

/* Несколько сообщений в одном блоке: маркеры у края, без лишних отступов
   списка по умолчанию. */
.adm-error-list {
    margin: 0;
    padding-left: 20px;
}

/* Пояснение под полем: обычным весом, 13px, красным. */
.adm-field-note {
    font-family: var(--f-body);
    font-weight: 600;
    font-size: var(--fs-small);
    color: var(--c-red-dark);
}

/* --- Блок ошибки -------------------------------------------------------- */

/* Один и тот же блок на входе и в формах админки: 2px красная рамка,
   светло-красный фон, красный жирный текст. */
.adm-error {
    margin: 0;
    padding: 8px 12px;
    border-radius: var(--r-md);
    border: 2px solid var(--c-red);
    background: var(--c-bad-bg);
    color: var(--c-red-dark);
    font-weight: 700;
    font-size: 14px;
}

/* --- Карточка с риббоном ------------------------------------------------ */

/* Основная коробка всех экранов админки: белое тело, синяя рамка, белый
   гало-ореол в тени и небесный риббон-заголовок сверху. overflow:hidden
   нужен, чтобы риббон обрезался скруглением карточки. */
.adm-card {
    background: var(--c-surface);
    border: 1px solid var(--c-card-border);
    border-radius: var(--r-xl);
    box-shadow: var(--sh-card-adm);
    overflow: hidden;
}

.adm-card-head {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: center;
    justify-content: space-between;

    padding: 10px 18px;
    background: var(--g-sky);
    border-bottom: 2px solid var(--c-outline);
    box-shadow: var(--sh-gloss-2);
}

.adm-card-title {
    font-family: var(--f-display);
    font-weight: 900;
    font-style: italic;
    font-size: 24px;
    line-height: 1.2;
    text-transform: uppercase;
    color: var(--c-chrome-0);
    text-shadow: var(--ts-ribbon);
}

/* Заголовок со счётчиком — одна группа в риббоне: без неё
   justify-content:space-between растащил бы их по краям. */
.adm-card-titles {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: baseline;
}

/* Счётчик рядом с заголовком («12 pages»): мелкий, без контура. */
.adm-card-count {
    font-family: var(--f-body);
    font-weight: 700;
    font-size: 14px;
    color: var(--c-chrome-0);
    text-shadow: 0 1px 0 var(--c-outline);
}

/* Риббон бывает трёх цветов, и цвет означает роль экрана: небо — список,
   золото — форма, в которой что-то создают или правят, красный — отказ или
   подтверждение удаления. Текст на всех трёх белый, меняются борт, градиент
   и контур. */
.adm-card-head--gold {
    background: var(--g-gold);
    border-bottom-color: var(--c-gold-border);
}

.adm-card-head--gold .adm-card-title {
    font-size: 18px;
    text-shadow: var(--ts-gold);
}

.adm-card-head--red {
    background: var(--g-red);
    border-bottom-color: var(--c-red-border);
}

.adm-card-head--red .adm-card-title {
    text-shadow: var(--ts-red);
}

.adm-card-body {
    padding: 26px;
}

/* Карточка на полэкрана: подтверждение удаления. Не во всю полосу, потому
   что в ней три строки текста и две кнопки. */
.adm-card--narrow {
    max-width: 620px;
    margin: 20px auto;
}

/* --- Две колонки: таблица и боковая форма -------------------------------- */

/* Экран пользователей (и позже настроек): слева список, справа форма. Обе
   колонки — flex-элементы с базисом, поэтому на узком экране форма уезжает
   под таблицу сама, без медиа-запроса. */
.adm-split {
    display: flex;
    flex-wrap: wrap;
    gap: 22px;
    align-items: flex-start;
}

.adm-split-main {
    flex: 2 1 560px;
    /* min-width:0 обязателен: без него длинный `code` внутри таблицы не даёт
       колонке сжаться, и флекс-базис 560px превращается в «не меньше
       содержимого». */
    min-width: 0;
}

.adm-split-side {
    flex: 1 1 300px;
    max-width: 100%;
    display: flex;
    flex-direction: column;
    gap: 18px;
}


/* ===========================================================================
   2. ОБОЛОЧКА

   Топбар липкий, под ним полоса контента шириной 1240px. Фон страницы —
   сплошное небо: ни «осколков», ни шотландки, в отличие от публичной
   страницы. Так в прототипе, и это осмысленно: админка — рабочий экран,
   текстура под таблицами мешала бы читать.
   =========================================================================== */

.adm {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    background: var(--c-page-0);
}

.adm-top {
    position: sticky;
    top: 0;
    z-index: 20;

    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 18px;

    padding: 8px 20px;
    background: var(--c-adm-top);
    background: var(--g-adm-top);
    border-bottom: 3px solid var(--c-sky);
    box-shadow: var(--sh-adm-top);
}

/* Логотип и чип «ADMIN» — одна ссылка на сайт. */
.adm-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
}

.adm-brand-logo {
    height: 30px;
    width: auto;
    /* base.css ставит медиа max-width:100%; у логотипа в полосе ширина
       считается от высоты, и ограничение по ширине контейнера ему мешает. */
    max-width: none;
    filter: var(--dr-foot-logo);
}

/* --- Чипы --------------------------------------------------------------- */

.adm-chip {
    font-family: var(--f-display);
    font-weight: 900;
    font-style: italic;
    letter-spacing: .1em;
    color: var(--c-chrome-0);
    white-space: nowrap;
}

/* Чип у логотипа: 13px, радиус 3px — чуть крупнее чипа роли. */
.adm-chip--brand {
    font-size: var(--fs-small);
    padding: 2px 10px;
    border-radius: 3px;
    border: 1px solid var(--c-red-border);
    background: var(--g-red-chip);
}

/* Чип роли вошедшего. Красный — admin, фиолетовый — manager; роль видно
   всегда, а не только на странице пользователей. */
.adm-chip--role {
    font-size: 11px;
    padding: 1px 7px;
    border-radius: 3px;
    border: 1px solid transparent;
}

.adm-chip--admin {
    border-color: var(--c-red-border);
    background: var(--g-red-chip);
}

.adm-chip--manager {
    border-color: var(--c-manager-border);
    background: var(--g-purple-chip);
}

/* --- Табы --------------------------------------------------------------- */

.adm-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

.adm-tab {
    font-family: var(--f-display);
    font-weight: 900;
    font-style: italic;
    font-size: 15px;
    text-transform: uppercase;
    text-decoration: none;

    padding: 8px 16px;
    border-radius: var(--r-md);
    border: 1px solid transparent;
    color: var(--c-on-navy);
}

.adm-tab:hover,
.adm-tab:focus {
    color: var(--c-chrome-0);
    text-decoration: none;
    background: rgba(255, 255, 255, .12);
}

/* Активный таб — хромовая пилюля: единственное светлое пятно в полосе,
   поэтому «где я» читается без чтения. */
.adm-tab.is-active,
.adm-tab.is-active:hover,
.adm-tab.is-active:focus {
    color: var(--c-navy);
    background: var(--g-chrome);
    border-color: var(--c-frame);
}

/* --- Правая часть полосы ------------------------------------------------ */

/* Имя, «View site» и «Log out» — одна группа, прижатая вправо автоотступом.
   Группой, а не тремя элементами полосы, по одной причине: при переносе
   строки `flex-wrap` рвёт контейнер между любыми двумя детьми, и «Log out»
   уезжал на свою строку в одиночку. Автоотступ вместо пустой распорки —
   он же прижимает группу вправо и когда она оказалась на своей строке. */
.adm-top-right {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 18px;
    margin-left: auto;
}

.adm-me {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    color: var(--c-on-navy);
}

.adm-me-name {
    font-weight: 600;
}

/* Логин под именем: при двух «Dmitrii» в базе иначе не понять, под кем
   вошли. В прототипе этой строки нет — он знает только одно имя. */
.adm-me-login {
    font-size: var(--fs-micro);
    color: var(--c-foot-dim);
}

.adm-view {
    font-size: 14px;
    color: var(--c-sky-pale);
    text-decoration-color: var(--c-foot-link-rule);
}

.adm-view:hover,
.adm-view:focus {
    color: var(--c-chrome-0);
    text-decoration-color: var(--c-chrome-0);
}

/* Выход — форма с POST и CSRF-токеном, а не ссылка: GET-выход вылетал бы
   от любой чужой картинки `<img src="/admin/logout">`. */
.adm-logout {
    margin: 0;
}

.adm-logout-btn {
    font-style: normal;
    font-size: var(--fs-small);
    letter-spacing: 0;
    padding: 6px 12px;
    border-radius: var(--r-sm);
    border-width: 1px;
}

/* --- Полоса контента ---------------------------------------------------- */

.adm-main {
    flex: 1 1 auto;
    width: 100%;
    max-width: 1240px;
    margin: 0 auto;
    padding: 26px 20px 60px;
}


/* ===========================================================================
   3. ЭКРАН ВХОДА (P5-2)

   Двухпанельная карточка: слева лучи и персонаж, справа форма. Лучи —
   `repeating-conic-gradient`, и это единственное место в файле со
   статической страховкой перед значением: без поддержки conic-gradient
   остался бы прозрачный фон, а не «просто без лучей» (DESIGN §10 п.5).
   Правило из §10 здесь не обязательно, но стоит оно одну строку.
   =========================================================================== */

.adm-login {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 30px 16px;

    background: var(--c-page-0);
    background:
        repeating-conic-gradient(from 0deg at 50% 50%,
            rgba(255, 255, 255, .12) 0deg, rgba(255, 255, 255, .12) 6deg,
            transparent 6deg, transparent 14deg),
        var(--c-page-0);
}

.login-card {
    width: 820px;
    max-width: 100%;

    display: flex;
    flex-wrap: wrap;

    border-radius: var(--r-2xl);
    border: 2px solid var(--c-outline);
    box-shadow: var(--sh-login);
    background: var(--c-surface);
    overflow: hidden;
}

/* Левая панель. Персонаж обрезается её границами, поэтому overflow:hidden
   обязателен: без него арт вылез бы на 120px ниже карточки.

   ПО УМОЛЧАНИЮ здесь УЗКИЙ случай — панели сложены в колонку, и персонажа
   нет. Так пришлось сделать после замера на 320px: арт у Кая вертикальный
   (1043×1508), прижат к низу и торчит из панели на 120px, и в сложенной
   панели высотой 320px от него остаётся туловище — голова обрезана верхней
   кромкой, а логотип ложится поверх лица. Масштабировать не помогает: чтобы
   голова влезла, панель должна быть почти во всю высоту арта, и тогда экран
   входа на телефоне начинается с трёхсотпиксельной картинки. Поэтому ниже
   порога переноса остаётся короткая полоса с лучами и логотипом — ровно то,
   что и так читается на телефоне.

   Медиа-запрос ниже — `min-width`, как требует DESIGN §10 п.12, поэтому
   широкий случай и есть исключение, а не наоборот. */
.login-art {
    position: relative;
    flex: 1 1 300px;
    min-height: 150px;
    overflow: hidden;

    /* В колонке панель стоит НАД формой, и граница у неё нижняя. Боковую
       ставит медиа-запрос вместе с остальным широким случаем. */
    border-bottom: 3px solid var(--c-outline);
    background: var(--g-sky-soft);
    background:
        repeating-conic-gradient(from 0deg at 50% 100%,
            rgba(255, 255, 255, .25) 0deg, rgba(255, 255, 255, .25) 6deg,
            transparent 6deg, transparent 14deg),
        var(--g-hero);
}

.login-art-char {
    display: none;

    position: absolute;
    left: 50%;
    bottom: -120px;
    transform: translateX(-50%);
    height: 520px;
    width: auto;
    /* Тот же случай, что у логотипа в полосе: ширина считается от высоты,
       и max-width:100% из base.css сплющил бы персонажа. */
    max-width: none;
}

.login-art-logo {
    position: absolute;
    left: 16px;
    top: 16px;
    width: 200px;
    height: auto;
    filter: drop-shadow(0 3px 0 var(--c-navy-shade));
}

/* 680px — измеренный порог, на котором панели встают рядом: карточка это
   `flex:1 1 300px` + 3px границы + `flex:1 1 340px` = 643px содержимого,
   плюс 4px её рамки и 32px боковых отступов экрана. Круглое «768px» сюда
   ставить нельзя: между 680 и 768 панели уже рядом, и панель должна стать
   боковой ровно там, где это произошло. */
@media (min-width: 680px) {
    .login-art {
        min-height: 320px;
        border-bottom: 0;
        border-right: 3px solid var(--c-outline);
    }
}

/* Персонаж возвращается позже, чем панель становится боковой, и вот почему.
   Арт при высоте 520px имеет ширину 360px, а панель уже панели: до 852px
   она уже 360px, и персонаж выходит за её края — так и задумано, его режет
   `overflow:hidden`. Но пока карточка прижата к краям экрана, этот вылет
   пересекает ещё и край ВЬЮПОРТА. Горизонтального скролла не даёт (режет
   панель), однако `./dev.sh viewport` такое честно считает нарушением, и
   отличать «вылез и обрезан» от «вылез и утащил страницу» глазами каждый
   раз никто не станет. Замерено: край вьюпорта перестаёт пересекаться на
   ~726px, 740 взято с запасом. */
@media (min-width: 740px) {
    .login-art-char {
        display: block;
    }
}

/* Правая панель: форма. Все её дети — строки в колонку с зазором 16px. */
.login-form {
    flex: 1 1 340px;
    display: flex;
    flex-direction: column;
    gap: 16px;
    padding: 32px 30px;
}

/* Скошенный тег «ADMIN». В своём flex-контейнере, иначе растянулся бы на
   всю ширину формы: `align-self` сюда не годится — тег должен держать
   строку, в которую в будущем может приехать что-то ещё. */
.login-tags {
    display: flex;
}

.login-tag {
    font-family: var(--f-display);
    font-weight: 900;
    font-style: italic;
    font-size: 14px;
    letter-spacing: .1em;
    color: var(--c-chrome-0);

    padding: 3px 14px;
    border-radius: var(--r-tag);
    border: 1px solid var(--c-red-border);
    background: var(--g-red-chip);
    transform: skewX(-10deg);
}

.login-title {
    font-family: var(--f-display);
    font-weight: 900;
    font-style: italic;
    font-size: 34px;
    line-height: 1;
    text-transform: uppercase;
    color: var(--c-navy);
}

.login-lead {
    font-size: 15px;
    color: var(--c-caption);
}

/* Капча. Виджет Turnstile рисует свой iframe фиксированной ширины; на узкой
   форме он мог бы вылезти, поэтому ограничиваем и даём прокрутку контейнеру
   вместо страницы. */
.login-captcha {
    max-width: 100%;
    overflow-x: auto;
}

.login-foot {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: space-between;
    font-size: var(--fs-small);
    color: var(--c-steel-light);
}

/* Предупреждение «капча выключена». Не ошибка — но на проде её увидеть
   нужно до того, как это обнаружит кто-то другой. */
.login-warn {
    margin: 0;
    padding: 8px 12px;
    border-radius: var(--r-md);
    border: 2px solid var(--c-gold);
    background: var(--c-gold-pale);
    color: var(--c-gold-ink);
    font-weight: 600;
    font-size: var(--fs-small);
}


/* ===========================================================================
   4. ЭКРАН «ADMINS ONLY» (P5-13)

   Отказ, а не скрытая кнопка (DESIGN §7). Маскот слева, объяснение и путь
   назад справа. Штриховка 45° в красном — тот же приём, что у нотисов
   `.rt-notice.warn` в контенте, поэтому «что-то не так» читается до текста.
   =========================================================================== */

.adm-denied {
    max-width: 560px;
    margin: 40px auto;

    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    align-items: center;

    padding: 24px;
    border-radius: var(--r-xl);
    border: 2px solid var(--c-red);
    background: var(--c-bad-bg);
    background: var(--tex-denied);
}

.adm-denied-art {
    width: 100px;
    height: auto;
    filter: var(--dr-mascot);
}

.adm-denied-text {
    flex: 1 1 240px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.adm-denied-title {
    font-family: var(--f-display);
    font-weight: 900;
    font-style: italic;
    font-size: 22px;
    line-height: 1.2;
    text-transform: uppercase;
    color: var(--c-red-dark);
}

.adm-denied-lead {
    font-size: 15px;
}


/* ===========================================================================
   5. ТАБЛИЦЫ (P5-3, P5-12)

   Строка — CSS-grid, как в прототипе, а набор столбцов задаёт экран
   (`.adm-table--pages`, `--users`): одинаковый `grid-template-columns` у
   шапки и у строк живёт в одном месте, иначе они разъезжаются на первой же
   правке ширины.

   Разметка при этом НЕ `<table>`, а div-ы с ARIA-ролями (role="table" /
   "row" / "columnheader" / "cell"). Причина не в стилистике: раскладка
   прототипа — сетка, а `display:grid` на `<tr>`/`<td>` стирает табличную
   семантику в дереве доступности, и роли пришлось бы возвращать теми же
   атрибутами — только уже поверх лишней разметки. Описание — в шапке
   app/templates/admin-pages.php.

   Горизонтальная прокрутка — у обёртки, а не у страницы: `min-width` стоит
   на самой сетке, и на узком экране карточка прокручивается внутри себя
   (design/README.md §Responsive: «Admin tables scroll horizontally inside
   their card»).
   =========================================================================== */

.adm-table-scroll {
    overflow-x: auto;
}

.adm-tr {
    display: grid;
    gap: 12px;
    align-items: center;
    padding: 12px 18px;
    border-bottom: 1px dotted var(--c-border-dot);
    font-size: 15px;
}

.adm-tr:hover {
    background: var(--c-row-hover);
}

/* Шапка: лёд, подписи Exo 2 в разрядку. Последняя строка таблицы и шапка
   не должны давать двойную линию, поэтому у шапки граница сплошная и своя. */
.adm-th {
    padding: 10px 18px;
    background: var(--c-ice);
    border-bottom: 1px solid var(--c-border-soft);
    font-family: var(--f-display);
    font-weight: 800;
    font-size: var(--fs-micro);
    letter-spacing: .1em;
    color: var(--c-thead-ink);
}

.adm-th:hover {
    background: var(--c-ice);
}

/* Последняя строка: рамку карточки и без неё видно. */
.adm-tr:last-child {
    border-bottom: 0;
}

/* Ячейка, в которой лежит длинный текст (заголовок, слаг): обрезается
   многоточием, а не растягивает колонку. */
.adm-cell-tight {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.adm-cell-dim {
    font-size: var(--fs-small);
    color: var(--c-caption);
}

/* Монотипный слаг или логин в строке. */
.adm-code {
    display: block;
    font-family: var(--f-mono);
    font-size: var(--fs-small);
    color: var(--c-caption);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Группа действий в конце строки, прижата вправо. */
.adm-acts {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    justify-content: flex-end;
}

/* Форма внутри ячейки — обёртка одной кнопки (удаление, стрелки порядка).
   Своего места в раскладке занимать не должна. */
.adm-act-form {
    margin: 0;
}

/* Пустая колонка под действия в шапке: пустой span иначе схлопывается и
   сетка шапки считается по пяти детям вместо шести. */
.adm-th-blank {
    min-height: 1px;
}


/* ===========================================================================
   6. СПИСОК СТРАНИЦ (P5-3)
   =========================================================================== */

/* 70px стрелки · заголовок · адрес · меню · дата · действия.
   720px — измеренный минимум, при котором действия не наезжают на дату. */
.adm-table--pages {
    min-width: 720px;
}

.adm-table--pages .adm-tr {
    grid-template-columns: 70px minmax(0, 2fr) minmax(0, 1.4fr) 110px 120px 210px;
}

/* Стрелки порядка: две мелкие хромовые кнопки 26×26. Не .adm-btn — у той
   типографика Exo 2 и паддинги под текст, а здесь глиф в квадрате. */
.adm-ord {
    display: flex;
    gap: 4px;
}

.adm-ord-btn {
    width: 26px;
    height: 26px;
    border-radius: var(--r-xs);
    border: 1px solid var(--c-frame);
    background: var(--g-chrome-soft);
    color: var(--c-navy);
    font-size: 12px;
    line-height: 1;
}

.adm-ord-btn:hover {
    filter: brightness(1.05);
}

.adm-ord-btn[disabled] {
    color: var(--c-off-ink);
    border-color: var(--c-off-border);
    background: var(--g-off);
    cursor: not-allowed;
    filter: none;
}

/* Заголовок страницы: чип темы + ссылка в редактор. */
.adm-page-title {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.adm-page-link {
    font-family: var(--f-display);
    font-weight: 800;
    font-style: italic;
    font-size: 16px;
    text-decoration: none;
    color: var(--c-navy);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.adm-page-link:hover,
.adm-page-link:focus {
    color: var(--c-deep-sky);
}

/* Чип темы баннера. Фон сплошной, не градиент: в прототипе у него именно
   плоский цвет, в отличие от чипов роли. */
.adm-chip--theme {
    flex: none;
    font-size: 11px;
    padding: 1px 7px;
    border-radius: 3px;
}

.adm-chip--cfa {
    background: var(--c-deep-sky);
}

.adm-chip--bfa {
    background: var(--c-orange-dark);
}

/* «● In menu» / «Hidden». Разница не только в цвете: у включённого есть
   точка, и состояние читается без различения зелёного и серого. */
.adm-state {
    font-size: var(--fs-small);
    font-weight: 700;
    color: var(--c-steel-light);
}

.adm-state--on {
    color: var(--c-in-menu);
}

/* Пометка главной страницы: она публикуется как «/» и её нельзя удалить. */
.adm-home-mark {
    font-family: var(--f-display);
    font-weight: 800;
    font-size: 10px;
    letter-spacing: .08em;
    color: var(--c-gold-ink);
    padding: 0 5px;
    border: 1px solid var(--c-gold);
    border-radius: 3px;
    background: var(--c-gold-pale);
    white-space: nowrap;
}

/* Черновик: статус есть в базе с фазы 1, а редактора ещё нет — пометка
   нужна, чтобы незаметная для публикации страница не выглядела живой. */
.adm-draft-mark {
    font-family: var(--f-display);
    font-weight: 800;
    font-size: 10px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--c-chrome-0);
    padding: 1px 6px;
    border-radius: 3px;
    background: var(--c-steel);
    white-space: nowrap;
}

/* Пустой список: карточка без строк выглядела бы как сломанный экран. */
.adm-empty {
    padding: 26px;
    font-size: var(--fs-body);
    color: var(--c-caption);
}

/* --- История правок страницы (P5-10) --------------------------------------
   сохранено · автор · что изменилось · Restore. Пометка CURRENT — та же
   плашка, что HOME у списка страниц: обе говорят «эта строка особая». */
.adm-table--revs {
    min-width: 600px;
}

.adm-table--revs .adm-tr {
    grid-template-columns: 230px minmax(0, 1fr) minmax(0, 2fr) 100px;
}

.adm-rev-when {
    display: flex;
    align-items: center;
    gap: 8px;
}

.adm-rev-date {
    font-weight: 700;
    color: var(--c-navy);
    white-space: nowrap;
}

/* Список изменений переносится, а не режется многоточием: обрезанное
   «Text −1200» как раз то, что прочитать нужно. */
.adm-rev-changes {
    overflow-wrap: break-word;
}


/* ===========================================================================
   7. ПОЛЬЗОВАТЕЛИ (P5-12)
   =========================================================================== */

/* пользователь · роль · дата · действия */
.adm-table--users {
    min-width: 560px;
}

.adm-table--users .adm-tr {
    grid-template-columns: minmax(0, 2fr) 100px 110px 150px;
}

.adm-user {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
}

/* Плитка с инициалом: цвет по роли, то есть роль видна дважды — плиткой и
   чипом. Инициал декоративен, имя рядом написано текстом. */
.adm-avatar {
    flex: none;
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    border-radius: var(--r-lg);
    border: 2px solid var(--c-chrome-0);
    box-shadow: 0 0 0 1px var(--c-outline);
    font-family: var(--f-display);
    font-weight: 900;
    font-style: italic;
    font-size: 18px;
    color: var(--c-chrome-0);
    text-shadow: 0 1px 0 rgba(0, 0, 0, .4);
}

.adm-avatar--admin {
    background: var(--g-avatar-admin);
}

.adm-avatar--manager {
    background: var(--g-avatar-manager);
}

.adm-user-text {
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.adm-user-name {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    font-family: var(--f-display);
    font-weight: 800;
    font-style: italic;
    font-size: 16px;
    color: var(--c-navy);
}

/* «YOU» рядом со своим именем: себя нельзя удалить и нельзя себе сменить
   роль, и обе кнопки объясняются этой пометкой. */
.adm-you {
    flex: none;
    font-family: var(--f-body);
    font-weight: 700;
    font-size: 11px;
    color: var(--c-deep-sky);
    padding: 0 6px;
    border: 1px solid var(--c-deep-sky);
    border-radius: 3px;
}

/* Отключённая учётка: пароль верный, а вход не пускает (DESIGN §9.2).
   Без пометки это выглядело бы как «вход сломался». */
.adm-user-off {
    flex: none;
    font-family: var(--f-body);
    font-weight: 700;
    font-size: 11px;
    color: var(--c-red-dark);
    padding: 0 6px;
    border: 1px solid var(--c-red);
    border-radius: 3px;
}

.adm-chip--cell {
    justify-self: start;
    font-size: 11px;
    letter-spacing: .06em;
    padding: 3px 9px;
    border-radius: var(--r-tag);
    border: 1px solid transparent;
    text-shadow: 0 1px 0 rgba(0, 0, 0, .35);
}

/* --- Форма пользователя ------------------------------------------------- */

.adm-form {
    margin: 0;
}

.adm-form-body {
    padding: 16px;
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.adm-form-acts {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: flex-end;
}

.adm-hint {
    font-family: var(--f-body);
    font-weight: 400;
    font-size: var(--fs-micro);
    color: var(--c-steel-light);
}

/* --- Карточки ролей ----------------------------------------------------- */

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

   Ободок выбранной карточки ставит класс `is-on` ОТ СЕРВЕРА, а не CSS:
   выбрать родителя по состоянию ребёнка умеет только :has(), который
   запрещён (DESIGN §10 п.2 — «нужное условие ставит рендерер классом»).
   Поэтому после клика без перезагрузки переезжает точка, а рамка остаётся
   на сохранённой роли. Это не рассинхрон, а ровно то, что есть: рамкой
   помечено текущее значение в базе, точкой — выбранное в форме. */
.adm-roles {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.adm-role {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    padding: 10px 12px;
    border-radius: var(--r-md);
    border: 2px solid var(--c-ring-idle);
    background: var(--c-surface);
    font-weight: 400;
}

.adm-role-radio {
    flex: none;
    margin: 3px 0 0;
}

.adm-role.is-on {
    border-color: var(--c-navy);
}

.adm-role--admin.is-on {
    background: var(--g-role-admin);
}

.adm-role--manager.is-on {
    background: var(--g-role-manager);
}

.adm-role-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.adm-role-name {
    font-family: var(--f-display);
    font-weight: 900;
    font-style: italic;
    font-size: 15px;
    color: var(--c-navy);
}

.adm-role-desc {
    font-family: var(--f-body);
    font-weight: 400;
    font-size: var(--fs-small);
    color: var(--c-caption);
}

/* Роль нельзя менять (свою или последнего админа) — карточки гаснут, и
   рядом написано почему. */
.adm-role--locked {
    border-color: var(--c-off-border);
    background: var(--g-off);
}

.adm-role-lock {
    font-family: var(--f-body);
    font-weight: 600;
    font-size: var(--fs-micro);
    color: var(--c-gold-border);
}

/* --- Памятка о ролях ---------------------------------------------------- */

.adm-legend {
    padding: 14px 16px;
    border-radius: var(--r-xl);
    border: 2px solid var(--c-deep-sky);
    background: var(--c-ice);
    background: var(--tex-legend);
    font-size: 14px;
    line-height: 1.55;
    color: var(--c-legend-ink);
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.adm-legend-row {
    margin: 0;
}

.adm-legend-note {
    margin: 0;
    font-size: var(--fs-small);
    color: var(--c-caption);
}


/* ===========================================================================
   8. ПОДТВЕРЖДЕНИЕ УДАЛЕНИЯ (P5-3, P5-12)

   Отдельный экран, а не `confirm()` в браузере: CSP админки запрещает
   инлайновый скрипт (`script-src 'self'`, app/src/http.php), поэтому
   `onsubmit="return confirm(…)"` не выполнится вообще — и удаление
   происходило бы с первого клика. Этот же экран показывает отказ, когда
   удалять нельзя: тогда у него нет красной кнопки, только «Cancel».
   =========================================================================== */

.adm-confirm {
    display: flex;
    flex-direction: column;
    gap: 14px;
    font-size: var(--fs-body);
    color: var(--c-ink);
}

.adm-confirm-lead {
    margin: 0;
}

.adm-confirm-facts {
    margin: 0;
    padding: 12px 16px;
    list-style: none;
    border-radius: var(--r-md);
    border: 1px solid var(--c-border-soft);
    background: var(--c-ice);
    font-size: var(--fs-small);
    color: var(--c-caption);
}

.adm-confirm-fact {
    margin: 0;
    padding: 2px 0;
}

.adm-confirm-key {
    font-family: var(--f-display);
    font-weight: 800;
    color: var(--c-navy);
}

.adm-confirm-acts {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-items: center;
    justify-content: flex-end;
}


/* ===========================================================================
   9. РЕДАКТОР СТРАНИЦЫ

   TASKS P5-4 (каркас) и P5-8 (панель настроек). Слева заголовок и карточка
   редактора, справа «Page settings» — та же `.adm-split`, что у экрана
   пользователей.

   ---------------------------------------------------------------------------
   ДВА РЕЖИМА И ПОЧЕМУ ИМИ УПРАВЛЯЕТ CSS, А НЕ ТОЛЬКО JS

   Поле формы — textarea `.adm-ed-code`; `contenteditable`-поверхность
   `.adm-ed-surface` её зеркало (см. шапку app/templates/admin-editor.php).
   Поэтому ИСХОДНОЕ состояние, то, которое отдал сервер, — это режим кода:
   без скрипта человек видит сырой HTML и может его сохранить.

   Переключает режимы пара классов:
     `has-js` на <html>    — скрипт загрузился; показываем поверхность;
     `is-code` на карточке — кнопка «</> HTML» нажата; снова показываем код.

   `has-js` ставится первой строкой admin-editor.js, который подключён БЕЗ
   `defer`, то есть до разбора body. Поэтому сырой HTML не успевает
   мигнуть, а без скрипта экран остаётся рабочим.
   =========================================================================== */

/* --- Шапка экрана: крошки и кнопки --------------------------------------- */

.adm-ed-top {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 14px;
}

.adm-ed-crumbs {
    margin: 0;
    font-size: var(--fs-small);
    font-weight: 700;
    color: var(--c-chrome-0);
    text-shadow: var(--ts-crumb);
}

.adm-ed-crumb,
.adm-ed-crumb:visited {
    color: var(--c-chrome-0);
}

.adm-ed-crumb-sep {
    margin: 0 4px;
}

.adm-ed-crumb-here {
    font-weight: 800;
}

.adm-ed-acts {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

/* --- Заголовок страницы: самое крупное поле на экране --------------------- */

.adm-ed-title {
    display: block;
    width: 100%;
    margin-bottom: 14px;

    font-family: var(--f-display);
    font-weight: 900;
    font-style: italic;
    font-size: 30px;
    text-transform: uppercase;
    color: var(--c-navy);

    padding: 12px 16px;
    border: 2px solid var(--c-card-border);
    border-radius: var(--r-xl);
    background: var(--c-surface);
    box-shadow: var(--sh-ring-white);
}

.adm-ed-title:focus {
    outline: none;
    border-color: var(--c-sky);
}

.adm-ed-title.is-bad {
    border-color: var(--c-red);
    background: var(--c-bad-field);
}

/* --- Карточка редактора --------------------------------------------------- */

.adm-ed-card {
    border: 1px solid var(--c-card-border);
    border-radius: var(--r-xl);
    background: var(--c-surface);
    box-shadow: var(--sh-card-adm);
}

/* Полоса инструментов. Липкая, и отступ сверху — высота топбара: иначе
   она прилипала бы ПОД ним и уезжала из виду (§10 п.10 — потеря
   прилипания безвредна, полоса просто уедет вверх со страницей). */
.adm-ed-bar {
    position: sticky;
    top: 58px;
    z-index: 5;

    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;

    padding: 8px 10px;
    border-bottom: 2px solid var(--c-frame);
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    background: var(--g-adm-bar);
}

/* Кнопки форматирования (P5-5 … P5-7). Место под пикер картинок (P5-14)
   добавится в группу `--links`. */
/* Базис 0, а не auto: при `auto` группа требует ширину всего своего
   содержимого (а она переносится внутри себя, то есть max-content — это
   все кнопки в одну строку), не помещается рядом с «</> HTML» и сбрасывает
   его на отдельную строку. С нулевым базисом она занимает ровно остаток
   полосы, а кнопки переносятся внутри неё. */
.adm-ed-tools {
    flex: 1 1 0;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
    min-width: 0;
    min-height: 32px;
}

/* --- Группы кнопок ------------------------------------------------------- */

.adm-ed-group {
    display: flex;
    flex-wrap: wrap;
    gap: 2px;
    align-items: center;

    padding: 2px;
    border: 1px solid var(--c-tool-border);
    border-radius: 7px;
    background: var(--c-tool-bg);
}

/* У палитры кнопка своя и нарисована сама по себе — подложка группы под
   ней выглядела бы как рамка вокруг рамки. Геометрия при этом та же:
   padding и border остаются на месте, меняется только видимость. */
.adm-ed-group--colour {
    border-color: transparent;
    background: transparent;
}

.adm-ed-tool {
    min-width: 32px;
    height: 32px;
    padding: 0 8px;

    font-family: var(--f-display);
    font-weight: 800;
    font-size: var(--fs-small);
    color: var(--c-navy);

    border: 1px solid transparent;
    border-radius: var(--r-xs);
    background: transparent;
    cursor: pointer;
}

.adm-ed-tool:hover,
.adm-ed-tool:focus {
    color: var(--c-deep-sky);
    border-color: var(--c-input-border);
    background: var(--c-surface);
}

/* Погашены в режиме `</> HTML` (и до загрузки скрипта — так их отдаёт
   сервер, см. шапку app/templates/admin-editor.php). Курсор обычный:
   «ничего не произойдёт» должно быть видно до нажатия. */
.adm-ed-tool:disabled,
.adm-ed-color:disabled {
    opacity: .45;
    cursor: default;
}

.adm-ed-tool:disabled:hover,
.adm-ed-color:disabled:hover {
    color: var(--c-navy);
    border-color: transparent;
    background: transparent;
}

/* --- Кнопка палитры ------------------------------------------------------ */

.adm-ed-color {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 36px;
    padding: 0 10px;

    font-family: var(--f-display);
    font-weight: 900;
    font-size: 15px;
    color: var(--c-navy);

    border: 1px solid var(--c-tool-border);
    border-radius: 7px;
    background: var(--c-surface);
    cursor: pointer;
}

.adm-ed-color:hover,
.adm-ed-color:focus {
    border-color: var(--c-input-border);
}

.adm-ed-color-a {
    display: flex;
    flex-direction: column;
    align-items: center;
    line-height: 1;
}

/* Радуга под «A» — единственное, что объясняет кнопку без подписи. */
.adm-ed-color-bar {
    width: 16px;
    height: 4px;
    margin-top: 2px;
    border-radius: 2px;
    background: var(--g-tool-rainbow);
}

/* --- Всплывающие панели -------------------------------------------------- */

/* Прячет панель атрибут `hidden`, а не класс: состояние «скрыта» нужно и
   скринридеру, а `display:none` из CSS он не видит — видит именно
   атрибут. Правило ниже нужно потому, что `display:flex` здесь перебил
   бы `hidden` (у него специфичность автора, а не браузера).

   ОТСЧЁТ ОТ ПОЛОСЫ, А НЕ ОТ КНОПКИ. В разметке панель лежит внутри своей
   группы (так её находит скринридер и так она закрывается вместе с
   тулбаром), но позиционируется от `.adm-ed-bar` — у групп нет
   `position`, и ближайший предок с ним это полоса (она `sticky`).
   Причина простая: кнопки в полосе переносятся по flex-wrap, и панель
   шириной 260px, открытая от кнопки у правого края, вылезла бы за
   карточку, а на узком экране — за вьюпорт. От левого края полосы она
   на месте при любой ширине, и считать ничего не нужно. `left` равен
   padding-у полосы — панель встаёт под её первую кнопку. */
.adm-ed-pop {
    position: absolute;
    top: 100%;
    left: 10px;
    z-index: 10;

    display: flex;
    flex-direction: column;
    gap: 10px;

    width: 260px;
    margin-top: 6px;
    padding: 12px;

    border: 2px solid var(--c-card-border);
    border-radius: var(--r-lg);
    background: var(--c-surface);
    box-shadow: var(--sh-pop);
}

.adm-ed-pop[hidden] {
    display: none;
}

.adm-ed-pop-label {
    margin: 0;

    font-family: var(--f-display);
    font-weight: 800;
    font-size: 11px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--c-steel-light);
}

.adm-ed-pop-foot {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    justify-content: space-between;

    padding-top: 8px;
    border-top: 1px dotted var(--c-border-dot);
    font-size: var(--fs-small);
}

/* Текстовая кнопка: «Default colour» и «Cancel». Выглядит ссылкой, потому
   что стоит в паре с настоящей ссылкой «Manage custom…». */
.adm-ed-reset {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-weight: 700;
    color: var(--c-navy);
    text-decoration: underline;
    cursor: pointer;
}

/* --- Свотчи палитры ------------------------------------------------------ */

.adm-ed-swatches {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 6px;
}

/* Высота задана числом, а не `aspect-ratio` (DESIGN §10 п.7). Рамка белая
   и отделяет свотчи друг от друга, а контур поверх цвета даёт тень
   --sh-swatch: границей это сделать нечем, она уже занята. */
.adm-ed-swatch {
    height: 34px;
    border: 2px solid var(--c-surface);
    border-radius: var(--r-sm);
    box-shadow: var(--sh-swatch);
    cursor: pointer;
}

.adm-ed-swatch:hover,
.adm-ed-swatch:focus {
    transform: scale(1.1);
}

/* --- Панель «Image» (P5-14) ---------------------------------------------- */

/* Шире остальных: четыре плитки в ряд читаются как превью, пять при 260px
   — уже как значки. На узкой полосе панель ужимается до неё самой
   (`left` с обеих сторон — тот же padding полосы, что у `.adm-ed-pop`). */
.adm-ed-pop--image {
    width: 340px;
    max-width: calc(100% - 20px);
}

.adm-ed-img-file {
    max-width: 100%;
    font-size: var(--fs-small);
}

.adm-ed-img-status,
.adm-ed-img-none {
    margin: 0;
    font-size: var(--fs-small);
    color: var(--c-steel);
}

.adm-ed-img-status.is-bad {
    font-weight: 700;
    color: var(--c-red-dark);
}

/* Высота ограничена: пикер — выбор из недавнего (UP_PICK_LIMIT), и
   полсотни плиток не должны выталкивать поля alt и подписи за экран. */
.adm-ed-imgs {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 6px;
    max-height: 216px;
    overflow-y: auto;
}

/* Плитка — квадрат числом, а не `aspect-ratio` (DESIGN §10 п.7); картинка
   вписана целиком (`contain`), иначе у длинного скриншота был бы виден
   только кусок середины, и выбирать пришлось бы наугад. */
.adm-ed-img {
    height: 70px;
    padding: 3px;
    border: 2px solid var(--c-tool-border);
    border-radius: var(--r-sm);
    background: var(--c-tool-bg);
    cursor: pointer;
}

.adm-ed-img img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.adm-ed-img:hover,
.adm-ed-img:focus {
    border-color: var(--c-input-border);
}

.adm-ed-img[aria-pressed="true"] {
    border-color: var(--c-sky-dark);
    box-shadow: var(--sh-focus);
}

/* Кнопка «</> HTML». Моноширинная и тёмная в нажатом состоянии — она
   показывает не действие, а РЕЖИМ, и выглядеть должна как тумблер, а не
   как ещё одна кнопка в группе. */
.adm-ed-html {
    flex: none;
    /* Полоса умеет быть в две строки, а кнопка остаётся одна: по центру
       она висела бы МЕЖДУ рядами кнопок. Верх к верху первого ряда. */
    align-self: flex-start;
    height: 32px;
    padding: 0 10px;

    font-family: var(--f-mono);
    font-size: var(--fs-small);
    font-weight: 700;
    color: var(--c-navy);

    border: 1px solid var(--c-navy);
    border-radius: var(--r-sm);
    background: var(--c-surface);
    cursor: pointer;
}

.adm-ed-html:hover,
.adm-ed-html:focus {
    background: var(--c-ice);
}

.adm-ed-html[aria-pressed='true'] {
    color: var(--c-code-ink);
    background: var(--c-code-bg);
}

/* --- Две поверхности ------------------------------------------------------ */

/* Поверхность `contenteditable`. Остальное её оформление — content.css, тот
   же файл, что на публичной странице: это условие «WYSIWYG = live», и
   копировать сюда правила `.cfa-rt` нельзя (см. шапку content.css).

   `display:none` здесь, а не ниже: исходное состояние экрана — режим
   кода, и поверхность в нём скрыта. Включают её правила с `.has-js`. */
.adm-ed-surface {
    display: none;
    min-height: 520px;
    padding: 26px 28px;
}

.adm-ed-surface:focus {
    outline: none;
}

/* Сырая разметка. Тёмный фон и небесный текст — из прототипа. */
.adm-ed-code {
    display: block;
    width: 100%;
    min-height: 520px;
    padding: 18px;
    resize: vertical;

    font-family: var(--f-mono);
    font-size: var(--fs-small);
    line-height: 1.55;
    color: var(--c-code-ink);

    border: 0;
    border-radius: 0 0 var(--r-xl) var(--r-xl);
    background: var(--c-code-bg);
}

.adm-ed-code:focus {
    outline: none;
}

/* Четыре правила ниже — тот самый порядок из шапки раздела: сначала
   скрипт отменяет режим кода, потом кнопка «</> HTML» его возвращает.
   Специфичность у пар одинаковая, и работает именно порядок. */
.has-js .adm-ed-surface {
    display: block;
}

.has-js .adm-ed-code {
    display: none;
}

.has-js .adm-ed-card.is-code .adm-ed-surface {
    display: none;
}

.has-js .adm-ed-card.is-code .adm-ed-code {
    display: block;
}

/* ===========================================================================
   Панель «Page settings» (P5-8)
   =========================================================================== */

.adm-ed-side {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

/* Слаг: нарисованный «/» и поле в одной рамке */
.adm-ed-slug {
    display: flex;
    align-items: center;
    border: 2px solid var(--c-input-border);
    border-radius: var(--r-md);
    background: var(--c-surface);
    overflow: hidden;
}

.adm-ed-slug-pre {
    align-self: stretch;
    display: flex;
    align-items: center;
    padding: 0 8px;
    font-family: var(--f-mono);
    color: var(--c-steel-light);
    background: var(--c-ice);
}

.adm-ed-slug-in {
    flex: 1;
    /* Без него моноширинный слаг не даёт боковой колонке сжаться. */
    min-width: 0;
    padding: 8px 10px;
    font-family: var(--f-mono);
    color: var(--c-ink);
    border: 0;
    background: transparent;
}

.adm-ed-slug-in:focus {
    outline: none;
}

.adm-ed-slug-in.is-bad {
    background: var(--c-bad-field);
}

/* --- Тема баннера: две плашки-радио -------------------------------------- */

.adm-ed-themes {
    display: flex;
    gap: 6px;
}

/* Радио скрыто, но доступно с клавиатуры: это не `display:none`, иначе
   поле выпало бы и из порядка табуляции, и из дерева доступности. Тот же
   приём, что у карточек ролей (.adm-role-radio). */
.adm-ed-theme-radio {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    clip: rect(0 0 0 0);
    overflow: hidden;
    white-space: nowrap;
}

.adm-ed-theme {
    flex: 1;
    position: relative;
    display: block;
    padding: 8px;
    text-align: center;
    cursor: pointer;

    font-family: var(--f-display);
    font-weight: 900;
    font-style: italic;
    font-size: var(--fs-small);
    color: var(--c-chrome-0);
    text-shadow: var(--ts-crumb);

    border: 2px solid var(--c-ring-idle);
    border-radius: var(--r-md);
}

.adm-ed-theme--cfa {
    background: var(--g-banner-cfa);
}

.adm-ed-theme--bfa {
    background: var(--g-banner-bfa);
}

/* Ободок выбранной ставит сервер классом `is-on`: выбрать родителя по
   состоянию ребёнка умеет только `:has()` (§10 п.2). Пока JS не
   перерисовал ничего, выбранной остаётся та, что пришла с сервера, —
   а браузер рисует фокус на самом радио, и видно его благодаря
   `:focus` ниже. */
.adm-ed-theme.is-on {
    border-color: var(--c-navy);
}

.adm-ed-theme-radio:focus + .adm-ed-theme-text {
    outline: 2px solid var(--c-chrome-0);
    outline-offset: 2px;
}

/* --- Мини-превью баннера ------------------------------------------------- */

/* Та же пара классов `is-cfa`/`is-bfa`, что у баннера на сайте
   (RENDER_THEMES в app/src/render.php), и тот же токен градиента —
   правила `.page-banner` при этом не переиспользуются: у полоски 120px
   своя геометрия, а components.css в админку не подключается вообще. */
.adm-ed-preview {
    position: relative;
    height: 120px;
    overflow: hidden;
    border: 2px solid var(--c-outline);
    border-radius: var(--r-lg);
}

.adm-ed-preview.is-cfa {
    background: repeating-conic-gradient(from 0deg at 85% 120%, rgba(255, 255, 255, .16) 0deg 6deg, transparent 6deg 14deg),
                var(--g-banner-cfa);
}

.adm-ed-preview.is-bfa {
    background: repeating-conic-gradient(from 0deg at 85% 120%, rgba(255, 255, 255, .16) 0deg 6deg, transparent 6deg 14deg),
                var(--g-banner-bfa);
}

/* Арт. `background-image` приходит инлайном из шаблона, его же меняет JS
   при выборе в списке; пустое значение — просто нет картинки. */
.adm-ed-preview-art {
    position: absolute;
    right: 8px;
    bottom: -50px;
    width: 45%;
    height: 170px;

    background-position: center bottom;
    background-repeat: no-repeat;
    background-size: contain;
}

.adm-ed-preview-title {
    position: absolute;
    left: 12px;
    right: 40%;
    bottom: 10px;

    font-family: var(--f-display);
    font-weight: 900;
    font-style: italic;
    font-size: 17px;
    line-height: 1.05;
    text-transform: uppercase;
    color: var(--c-chrome-0);
    text-shadow: var(--ts-banner-title);
}

/* --- Остальное панели ---------------------------------------------------- */

.adm-ed-check {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: var(--fs-body);
    cursor: pointer;
}

.adm-ed-check-box {
    width: 18px;
    height: 18px;
    accent-color: var(--c-deep-sky);
}

/* Галочка-уточнение под полем (редирект со старого слага, P5-10): мельче
   основных и в две строки, поэтому коробка прижата к первой строке. */
.adm-ed-check--sub {
    align-items: flex-start;
    font-size: var(--fs-small);
    color: var(--c-caption);
}

.adm-ed-check--sub .adm-ed-check-box {
    flex: none;
    margin: 1px 0 0;
}

/* Слаг внутри фразы: у .adm-code по умолчанию блок с многоточием. */
.adm-code--inline {
    display: inline;
    white-space: normal;
}

.adm-ed-meta {
    /* Описание для поиска длиннее строки, но вертикальная тяга панели
       важнее: растягивать её руками человек не должен. */
    min-height: 68px;
}

.adm-ed-view {
    margin: 0;
    font-size: var(--fs-small);
}

.adm-legend-lead {
    font-family: var(--f-display);
    font-style: italic;
    color: var(--c-card-border);
}

/* --- Тост «✓ Page saved» (P5-9) ------------------------------------------ */

/* Снимает его скрипт через 2.2 с. Без скрипта он остаётся на экране —
   это не мешает: сообщение правдивое, а перекрывает он нижние 24px
   полосы, где ничего интерактивного нет. */
.adm-toast {
    position: fixed;
    left: 50%;
    bottom: 24px;
    z-index: 50;
    /* Сдвиг на половину себя: ширина тоста зависит от текста, и центр
       иначе не посчитать. transform есть во всём ярусе A. */
    transform: translateX(-50%);

    margin: 0;
    padding: 12px 22px;

    font-family: var(--f-display);
    font-weight: 900;
    font-style: italic;
    font-size: var(--fs-body);
    color: var(--c-chrome-0);
    text-shadow: var(--ts-green);

    border: 2px solid var(--c-success-border);
    border-radius: var(--r-lg);
    background: var(--g-toast);
    box-shadow: var(--sh-toast);
}


/* ===========================================================================
   10. НАСТРОЙКИ САЙТА (P5-11)

   Весь экран — ОДНА форма, а карточки внутри неё лежат автосеткой. Отсюда
   две особенности раскладки:

   — у сетки нет медиа-запроса. `repeat(auto-fit, minmax(…))` сам решает,
     сколько колонок влезло, и на узком экране карточки встают в одну —
     как `.adm-split` на экранах списков;
   — ряды полей (ссылка сайдбара, кастомный цвет) — flex с `flex-wrap`, и
     поля в них переносятся по базису. Поэтому ряд не ломается ни на 320px,
     ни рядом с длинным адресом.
   =========================================================================== */

.adm-set {
    display: flex;
    flex-direction: column;
    gap: 22px;
}

/* Полоса заголовка: слева H1, справа золотая кнопка. На узком экране
   кнопка уезжает под заголовок сама. */
.adm-set-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: center;
    justify-content: space-between;
}

/* Единственный <h1> в админке: на остальных экранах заголовок играет
   риббон карточки, а здесь он есть в дизайне. Белый с контуром — он стоит
   на небесной полосе контента, а не на белой карточке. */
.adm-set-h1 {
    margin: 0;
    font-family: var(--f-display);
    font-weight: 900;
    font-style: italic;
    font-size: 34px;
    line-height: 1;
    text-transform: uppercase;
    color: var(--c-chrome-0);
    text-shadow: var(--ts-h1-adm);
}

/* Блок ошибки и пометка о несохранённом стоят ПОД полосой заголовка и во
   всю ширину: они относятся ко всей форме, а не к одной карточке. */
.adm-set-error {
    font-size: var(--fs-body);
}

/* «Unsaved changes»: ▲▼ и «+ Add» перестраивают форму, ничего не сохраняя,
   и человек должен видеть, что правка ещё не в базе. Жёлтая плашка, не
   красная: это не ошибка. */
.adm-set-dirty {
    margin: 0;
    padding: 8px 12px;
    border-radius: var(--r-md);
    border: 2px solid var(--c-gold-border);
    background: var(--c-add-bg);
    font-family: var(--f-display);
    font-weight: 800;
    font-size: 14px;
    color: var(--c-navy);
}

.adm-set-grid {
    display: grid;
    /* minmax(min(100%, 520px), 1fr): без min() колонка в 520px не сжимается
       на экране уже 520px и даёт горизонтальную прокрутку страницы. */
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 520px), 1fr));
    gap: 22px;
    align-items: start;
}

/* Тело карточки настроек. Своё, а не .adm-card-body: там 26px, а здесь
   карточек шесть, и воздух в каждой складывается в лишний экран прокрутки.
   Двух классов на одном элементе нет намеренно — одинаковая специфичность
   двух `padding` решалась бы порядком строк в файле. */
.adm-set-fields,
.adm-set-rows {
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: 16px;
}

/* Два поля в строке, пока хватает места (invite + server ID). */
.adm-set-pair {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
}

.adm-set-pair .adm-field {
    flex: 1 1 200px;
    min-width: 0;
}

/* Описание сайта — короткое поле: это meta description, а не текст. */
.adm-set-meta {
    min-height: 0;
}

/* --- Ряд с полями: ссылка сайдбара и кастомный цвет ---------------------- */

.adm-set-row {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;

    padding: 8px;
    border-radius: var(--r-md);
    border: 1px solid var(--c-set-row-edge);
    background: var(--c-set-row-bg);
}

/* Подпись и адрес делят строку в пропорции 1:2, обе сжимаются до нуля.
   min-width:0 обязателен: без него длинный адрес не даёт полю сжаться. */
.adm-set-row-label {
    flex: 1 1 150px;
    min-width: 0;
    font-weight: 700;
}

.adm-set-row-url {
    flex: 2 1 180px;
    min-width: 0;
}

/* Ряд палитры: свотч, название, hex и ✕. Фона и рамки у него нет — свотч
   сам достаточно заметен, а ледяная подложка под цветом врала бы о нём. */
.adm-set-row--color {
    padding: 0;
    border: 0;
    background: none;
}

.adm-set-empty {
    margin: 0;
    font-family: var(--f-body);
    font-size: var(--fs-small);
    color: var(--c-steel-light);
}

/* --- Пунктирная «+ Add» -------------------------------------------------- */

/* Не .adm-btn: та глянцевая и объёмная, то есть обещает действие над
   сайтом. Здесь добавляется пустая строка в форму, и выглядеть это должно
   как место, куда ещё можно что-то положить. */
.adm-add {
    align-self: flex-start;

    font-family: var(--f-display);
    font-weight: 800;
    font-size: var(--fs-small);
    text-transform: uppercase;
    color: var(--c-deep-sky);

    padding: 7px 14px;
    border-radius: var(--r-sm);
    border: 2px dashed var(--c-deep-sky);
    background: var(--c-add-bg);
}

.adm-add:hover,
.adm-add:focus {
    background: var(--c-add-bg-on);
}

.adm-add[disabled] {
    cursor: not-allowed;
    color: var(--c-off-ink);
    border-color: var(--c-off-border);
    background: var(--g-off);
}

/* Красная ✕ в ряду полей. Остальное наследуется от .adm-ord-btn — это тот
   же примитив, что у стрелок порядка страниц. */
.adm-ord-btn--red {
    border-color: var(--c-red);
    background: var(--c-surface);
    color: var(--c-red-dark);
    font-weight: 900;
}

.adm-ord-btn--red:hover {
    background: var(--c-bad-bg);
    filter: none;
}

/* --- Тумблер ------------------------------------------------------------- */

/* Настоящий чекбокс плюс нарисованная дорожка. Чекбокс не прячется
   `display:none` — так он перестал бы получать фокус; он прозрачен и лежит
   поверх дорожки, то есть кликабельна она целиком. */
.adm-switch {
    position: relative;
    display: inline-flex;
    gap: 8px;
    align-items: center;
    cursor: pointer;

    font-family: var(--f-display);
    font-weight: 800;
    font-size: 14px;
    color: var(--c-chrome-0);
    text-shadow: var(--ts-sky);
}

/* Тот же тумблер внутри тела карточки, а не в риббоне: там фон белый, и
   белый текст с небесным контуром на нём не читается. */
.adm-switch--block {
    color: var(--c-navy);
    text-shadow: none;
}

.adm-switch-box {
    position: absolute;
    top: 0;
    left: 0;
    width: 52px;
    height: 28px;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.adm-switch-track {
    position: relative;
    flex: none;
    width: 52px;
    height: 28px;
    border-radius: 14px;
    border: 2px solid var(--c-switch-edge);
    background: var(--g-switch-off);
}

.adm-switch-knob {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: 1px solid var(--c-frame);
    background: var(--g-switch-knob);
    transition: left .15s;
}

/* Состояние ставит сам браузер соседним селектором — ни класса от сервера,
   ни скрипта для этого не нужно. */
.adm-switch-box:checked ~ .adm-switch-track {
    background: var(--g-switch-on);
}

.adm-switch-box:checked ~ .adm-switch-track .adm-switch-knob {
    left: 26px;
}

.adm-switch-box:focus ~ .adm-switch-track {
    box-shadow: var(--sh-focus);
}

@media (prefers-reduced-motion: reduce) {
    .adm-switch-knob {
        transition: none;
    }
}

/* --- Карточка Discord ---------------------------------------------------- */

.adm-card-head--discord {
    background: var(--g-discord);
    border-bottom-color: var(--c-discord-border);
}

.adm-card-head--discord .adm-card-title {
    text-shadow: 0 1px 0 var(--c-discord-border);
}

/* Кнопка «Test connection»: пятая тема кнопки, и она единственная завязана
   на чужой бренд — поэтому живёт здесь, а не среди примитивов. */
.adm-btn--discord {
    font-size: 14px;
    color: var(--c-chrome-0);
    border-color: var(--c-discord-border);
    background: var(--g-discord);
    box-shadow: 0 3px 0 var(--c-discord-border);
    text-shadow: 0 1px 0 var(--c-discord-border);
}

.adm-btn--discord:hover,
.adm-btn--discord:focus {
    color: var(--c-chrome-0);
    filter: brightness(1.1);
    transform: translateY(-1px);
}

.adm-btn--discord:active {
    transform: translateY(2px);
    box-shadow: 0 1px 0 var(--c-discord-border);
}

.adm-set-test {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: center;
}

.adm-set-test-out {
    font-family: var(--f-body);
    font-weight: 700;
    font-size: 14px;
    color: var(--c-in-menu);
}

/* Ответ с ошибкой красится классом от скрипта: цвет здесь означает исход
   запроса, а не оформление. */
.adm-set-test-out.is-bad {
    color: var(--c-red-dark);
}

/* --- Палитра ------------------------------------------------------------- */

/* Подпись группы («SYSTEM · BUILT IN»): разрядка и мелкий кегль. */
.adm-set-label {
    margin: 0;
    font-family: var(--f-display);
    font-weight: 800;
    font-size: var(--fs-micro);
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--c-steel-light);
}

.adm-set-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

/* Системный цвет — читаемый чип, а не безымянный квадратик: названия из
   PAGE_SYS_COLORS это те же названия, что в палитре редактора. */
.adm-set-chip {
    display: inline-flex;
    gap: 6px;
    align-items: center;
    padding: 3px 9px 3px 4px;
    border-radius: 14px;
    border: 1px solid var(--c-set-row-edge);
    background: var(--c-set-row-bg);
    font-family: var(--f-body);
    font-weight: 700;
    font-size: var(--fs-small);
}

.adm-set-dot {
    flex: none;
    width: 18px;
    height: 18px;
    border-radius: 50%;
}

/* Свотч кастомного цвета: квадрат с белой рамкой и контуром, а внутри —
   нативный `input[type=color]` в нулевой прозрачности во всю площадь.
   Свой вид у пикера в каждом браузере свой; наш — вот этот квадрат. */
.adm-set-swatch {
    position: relative;
    flex: none;
    width: 38px;
    height: 38px;
    border-radius: var(--r-md);
    border: 2px solid var(--c-chrome-0);
    box-shadow: var(--sh-swatch);
    cursor: pointer;
}

.adm-set-picker {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    opacity: 0;
    cursor: pointer;
}

.adm-set-picker:focus {
    /* Прозрачное поле обводкой не покажешь — её рисует рамка свотча.
       Поэтому фокус виден на РОДИТЕЛЕ, а не на самом поле. */
    outline: none;
}

.adm-set-picker:focus ~ .adm-set-code {
    text-decoration: underline;
}

/* Цвет, который не разобрался как #RRGGBB: вместо пикера текстовое поле с
   тем, что лежит в базе. Узкое — в нём семь символов. */
.adm-set-hex {
    flex: none;
    width: 110px;
}

.adm-set-code {
    flex: none;
    font-size: var(--fs-small);
    color: var(--c-steel);
}

/* --- Режим и публикация -------------------------------------------------- */

.adm-set-acts {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}


/* ===========================================================================
   11. КАРТИНКИ И ЭКРАН ОТКАЗА (P5-14)

   Экран «Images»: сетка загруженного слева и форма загрузки справа, на
   тех же `.adm-split` и `.adm-card`, что пользователи. Панель «Image» в
   редакторе — в разделе 9, рядом с остальными панелями тулбара.

   `.adm-result` — тело экрана отказа Rebuild/Purge (admin-result.php).
   Раньше этим же оформлением рисовались заглушки фазы 5; они ушли с
   последней из них, а экран отказа остался.
   =========================================================================== */

.adm-result {
    display: flex;
    flex-direction: column;
    gap: 12px;
    font-size: var(--fs-body);
    color: var(--c-ink);
}

.adm-img-empty {
    margin: 0;
    font-size: var(--fs-body);
    color: var(--c-steel);
}

/* Колонки — по ширине, а не числом: карточка живёт в `.adm-split-main`,
   ширина которой зависит от экрана, и `auto-fill` сам решает, сколько
   плиток в ряд. 170px — чтобы путь вида /uploads/2026/10/<16 hex>.png
   помещался в две строки. */
.adm-imgs {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
    gap: 14px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.adm-img {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
    padding: 8px;
    border: 2px solid var(--c-tool-border);
    border-radius: var(--r-md);
    background: var(--c-surface);
}

/* Только что загруженная — подсвечена: её путь человек пришёл копировать. */
.adm-img.is-fresh {
    border-color: var(--c-sky-dark);
    box-shadow: var(--sh-focus);
}

.adm-img-thumb {
    display: block;
    height: 120px;
    margin-bottom: 4px;
    border-radius: var(--r-sm);
    background: var(--c-tool-bg);
}

.adm-img-thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.adm-img-name,
.adm-img-meta {
    margin: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.adm-img-name {
    font-weight: 700;
    font-size: 14px;
    color: var(--c-navy);
}

.adm-img-meta {
    font-size: var(--fs-small);
    color: var(--c-steel);
}

/* Путь целиком переносится, а не обрезается многоточием: его копируют, и
   обрезанный путь выделился бы обрезанным. `user-select: all` выделяет
   его одним кликом — без скрипта и без кнопки «Copy». */
.adm-img-path {
    font-family: var(--f-mono);
    font-size: var(--fs-small);
    color: var(--c-caption);
    overflow-wrap: anywhere;
    -webkit-user-select: all;
    user-select: all;
}

.adm-img-form {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.adm-img-file {
    padding: 8px;
}

.adm-img-hint {
    margin: 0;
    font-size: var(--fs-small);
    color: var(--c-steel);
}
