* { box-sizing: border-box; }

/* Дизайн страницы рассчитан только на светлую тему. Без этого браузер
   при тёмной теме ОС/браузера рисует нативные элементы (выпадающий
   список <datalist> — например, у поля "Тип" в Телефонах, стрелочки
   <select>, системные попапы даты и т.п.) в тёмных цветах поверх
   светлой страницы — тёмный фон, плохо читаемый текст. */
:root {
    color-scheme: light;
}

body {
    font-family: system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
    background: #f5f6f8;
    color: #1f2328;
    margin: 0;
    padding: 0 1.5rem 0.5rem;
}

@media (max-width: 480px) {
    /* На узком экране телефона отступы по бокам страницы отъедают
       заметную долю и без того небольшой ширины — сокращаем их, чтобы
       освободить место под поля формы. */
    body {
        padding: 0 0.6rem 1.5rem;
    }
}

header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.5rem;
}

header h1 {
    font-size: 1.4rem;
    margin: 0.6rem 0;
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.header-left {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}

/* Страницы с меньшим количеством контента (например, импорт подписок,
   открываемый в маленьком окне расширения) — минимум пустого места
   сверху, чтобы не возникала вертикальная прокрутка. */
body.compact-page header h1 {
    margin: 0.6rem 0;
}

body.compact-page main > h2:first-child {
    margin: 0.4rem 0;
}

.page-title-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin: 0.4rem 0;
}

.page-title-row h2 {
    margin: 0;
}

.page-title-row-actions {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

body.compact-page main > p {
    margin: 0.3rem 0;
}

header h1 a {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.app-icon {
    width: 32px;
    height: 32px;
}

.app-icon-glyph {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.7rem;
    line-height: 1;
    color: #1f2328;
}

.app-icon.spinning {
    animation: dialog-save-spin 0.9s linear infinite;
}

.user-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: flex-end;
    gap: 0.6rem;
    font-size: 0.85rem;
    color: #6b7280;
    position: relative;
}

.header-actions-list {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: flex-end;
    gap: 0.6rem;
}

.resource-header-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
}

.user-account-block {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.2rem;
}

.user-account-buttons {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.user-bar a {
    color: inherit;
}

.user-bar form {
    display: inline;
}

header h1 a {
    color: inherit;
    text-decoration: none;
}

.channel-count {
    color: #4b5157;
    font-size: 0.8em;
}

.rights-reserved {
    color: #9aa0a6;
    font-size: 1.3rem;
    line-height: 1;
    white-space: nowrap;
    cursor: default;
}

@media (max-width: 600px) {
    /* На узком экране (телефон) в шапке и так тесно (заголовок + счётчик +
       3 кнопки справа) — значок ® не несёт функциональной нагрузки,
       прячем его первым. */
    .rights-reserved {
        display: none;
    }
}

.header-version-text {
    color: #9aa0a6;
    font-size: 0.75rem;
    white-space: nowrap;
    /* Показываем версию текстом, только если реально есть место — на
       узких экранах она первой уходит под нож, ширины и так впритык. */
    display: none;
}

@media (min-width: 600px) {
    .header-version-text {
        display: inline;
    }
}

.filters {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
    margin-bottom: 1rem;
}

.filters input[type="text"],
.filters select {
    padding: 0.4rem 0.6rem;
    border: 1px solid #ccd1d9;
    border-radius: 6px;
    font-size: 0.9rem;
}

.search-wrap {
    position: relative;
    display: inline-block;
    flex: 1 1 160px;
    min-width: 160px;
}

.search-wrap input[type="text"] {
    padding-left: 2rem;
    padding-right: 2rem;
    width: 100%;
}

.search-icon-left {
    position: absolute;
    left: 0.6rem;
    top: 50%;
    transform: translateY(-50%);
    font-size: 0.85rem;
    opacity: 0.6;
    border: none;
    background: transparent;
    padding: 0;
    line-height: 1;
    cursor: pointer;
}

.search-icon-left:hover {
    opacity: 1;
    background: transparent;
}

.search-clear {
    position: absolute;
    right: 0.4rem;
    top: 50%;
    transform: translateY(-50%);
    border: none;
    background: transparent;
    cursor: pointer;
    font-size: 1.1rem;
    line-height: 1;
    color: #6b7280;
    padding: 0.1rem 0.3rem;
    border-radius: 50%;
}

.search-clear:hover {
    background: #f0f2f5;
    color: #1f2328;
}

button, .button-link {
    padding: 0.4rem 0.8rem;
    border-radius: 6px;
    border: 1px solid #ccd1d9;
    background: #fff;
    color: #1f2328;
    text-decoration: none;
    font-size: 0.9rem;
    cursor: pointer;
    display: inline-block;
}

button.primary, .button-link.primary {
    background: #1a73e8;
    border-color: #1a73e8;
    color: #fff;
}

.btn-icon-img {
    height: 1.2rem;
    width: auto;
    display: block;
    vertical-align: middle;
}

.filters-actions {
    display: flex;
    gap: 0.5rem;
    margin-left: auto;
    position: relative;
}

/* Специфичность выше, чем у .icon-filter-btn (display: inline-flex ниже
   по файлу) — иначе при равной специфичности побеждало бы более позднее
   правило и бургер был бы виден всегда, даже когда все кнопки помещаются
   и он не нужен. */
.icon-filter-btn.burger-menu-btn {
    display: none;
}

.filters-btn.secondary-page-back-btn {
    /* .filters-btn+класс (не просто .secondary-page-back-btn) — иначе
       .icon-filter-btn{display:inline-flex} ниже по файлу с той же
       специфичностью (0,1,0) перебивал этот display:none, и кнопка была
       видна всегда, а не только в @media (max-width: 700px) ниже. */
    display: none;
}

@media (max-width: 700px) {
    /* "Сервис"/"Тарифы"/"Политика"/"О приложении" — отдельные страницы
       (не попапы, см. .user-bar-secondary-page в base.html), на узком
       экране вместо бургера сразу кнопка возврата в список ресурсов/
       каналов — там особо нечему сворачиваться, кроме той же ссылки. */
    .user-bar-secondary-page .secondary-page-back-btn {
        display: inline-flex;
    }

    .user-bar-secondary-page .icon-filter-btn.burger-menu-btn {
        display: none !important;
    }
}

.filters-actions-list {
    display: flex;
    gap: 0.5rem;
}

/* "Бургер" в шапке и над таблицей каналов появляется не по ширине экрана,
   а только когда ряд кнопок реально не помещается на своей строке —
   класс "burger-active" ставит/снимает window.initOverflowBurger()
   (base.html) по факту измерения, см. там же. */
.filters-actions.burger-active .burger-menu-btn,
.user-bar.burger-active .burger-menu-btn {
    display: inline-flex;
}

.filters-actions.burger-active .filters-actions-list,
.user-bar.burger-active .header-actions-list {
    display: none;
}

.filters-actions.burger-active .filters-actions-list.open,
.user-bar.burger-active .header-actions-list.open {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.3rem;
    position: absolute;
    top: calc(100% + 0.4rem);
    right: 0;
    background: #fff;
    border: 1px solid #dfe1e5;
    border-radius: 8px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
    padding: 0.5rem;
    z-index: 20;
    min-width: 12rem;
}

.filters-actions-list.open .filters-btn,
.header-actions-list.open .filters-btn {
    width: 100%;
    justify-content: flex-start;
    /* Некоторые кнопки в обычном виде — с белым фоном (например, кнопка
       импорта Google, чтобы был виден цветной значок "G"), из-за чего
       белый текст подписи (::after) в выпадающем меню становится
       нечитаемым. В самом меню фон/цвет всегда синий/белый одинаково для
       всех пунктов, вне зависимости от их обычного вида. */
    background: #1a73e8;
    border-color: #1a73e8;
    color: #fff;
}

.filters-actions-list.open .th-menu-trigger.filters-btn {
    display: flex;
}

.filters-actions-list.open .icon-filter-btn img,
.filters-actions-list.open .icon-filter-btn svg,
.filters-actions-list.open .icon-filter-btn .btn-icon-glyph,
.header-actions-list.open .icon-filter-btn img,
.header-actions-list.open .icon-filter-btn svg,
.header-actions-list.open .icon-filter-btn .btn-icon-glyph {
    /* В развёрнутом (узкий экран) виде пункты меню — только текст
       подписи (title, через ::after ниже), без значков — иконки имеют
       смысл как компактная замена подписи в узком ряду кнопок, а тут
       место уже не экономим, и подпись читается сама по себе. */
    display: none;
}

/* "My"/"USD" — короткие подписи для тесного ряда кнопок над таблицей;
   в развёрнутом бургер-меню места достаточно — показываем длинный
   вариант ("Мои каналы"/"Доходы") вместо сокращённого. */
.btn-text-full {
    display: none;
}

.filters-actions-list.open .btn-text-compact,
.header-actions-list.open .btn-text-compact {
    display: none;
}

.filters-actions-list.open .btn-text-full,
.header-actions-list.open .btn-text-full {
    display: inline;
}

.filters-actions-list.open .icon-filter-btn::after,
.header-actions-list.open .icon-filter-btn::after {
    content: attr(title);
}

.filters-actions-list.open .th-menu {
    position: static;
    margin-top: 0.3rem;
    box-shadow: none;
    border-left: 2px solid #dfe1e5;
    border-radius: 0;
}

.header-actions-list.open .user-account-block {
    align-items: stretch;
    width: 100%;
}

.header-actions-list.open .user-account-buttons {
    flex-direction: column;
    align-items: stretch;
    width: 100%;
}

.filters-btn {
    padding: 0.4rem 0.8rem;
    border-radius: 6px;
    border: 1px solid #1a73e8;
    background: #1a73e8;
    color: #fff;
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: normal;
    cursor: pointer;
    display: inline-block;
    white-space: nowrap;
}

/* Часть этих кнопок — настоящие ссылки <a> (например, "?" О приложении),
   а не <button> — без явного цвета для :visited браузер подкрашивает
   пройденную ссылку в свой стандартный тёмно-фиолетовый, из-за чего
   именно эта кнопка выглядит темнее остальных. */
a.filters-btn:visited,
a.filters-btn:hover,
a.filters-btn:active {
    color: #fff;
}

.icon-filter-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.icon-filter-btn-outline {
    /* Цветная многоцветная иконка (лого My YouTube — синий/красный/белый)
       на сплошном синем фоне сливается и теряет контраст, поэтому для
       этой кнопки фон белый с синей обводкой вместо обычной заливки. */
    background: #fff;
    border-color: #1a73e8;
}

.google-import-btn,
#export-excel-btn {
    background: #fff;
    border: 1px solid #1a73e8;
}

.icon-filter-btn img {
    height: 1.1rem;
    width: auto;
    display: block;
}

.icon-filter-btn.active {
    background: #0d47a1;
    border-color: #0d47a1;
}

.lang-switch-btn {
    font-weight: 600;
    font-size: 0.78rem;
    letter-spacing: 0.02em;
}

.lang-select {
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    font-weight: 600;
    font-size: 0.78rem;
    letter-spacing: 0.02em;
    padding: 0.4rem 1.6rem 0.4rem 0.7rem;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='14' height='14'><path fill='white' d='M7 10l5 5 5-5z'/></svg>");
    background-repeat: no-repeat;
    background-position: right 0.4rem center;
    background-size: 14px;
}

.lang-select option {
    color: #1f2328;
    background: #fff;
}

.filters-btn:disabled {
    background: #c2c8d0;
    border-color: #c2c8d0;
    cursor: default;
    opacity: 0.7;
}

.filters-btn:disabled:hover {
    background: #c2c8d0;
    border-color: #c2c8d0;
}

.filters-btn:hover {
    background: #1558b0;
    border-color: #1558b0;
}

button.danger {
    background: #fff;
    border-color: #d93025;
    color: #d93025;
}

.icon-btn {
    width: 1.9rem;
    height: 1.9rem;
    padding: 0;
    line-height: 1;
    font-size: 1.15rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
}

.icon-btn:hover {
    background: #fce8e6;
}

button.danger.icon-btn,
.button-link.icon-btn {
    background: transparent;
    border: none;
    border-radius: 0;
}

button.danger.icon-btn:hover {
    background: transparent;
    color: #a50e0e;
}

.button-link.icon-btn:hover {
    background: transparent;
    color: #1a73e8;
}

.icon-btn-lg {
    width: 2.3rem;
    height: 2.3rem;
    font-size: 1.5rem;
}

.table-scroll {
    max-height: var(--channels-table-height, calc(100vh - 230px));
    overflow-y: auto;
    overflow-x: auto;
    border-radius: 8px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}

.admin-table-scroll {
    max-height: calc(100vh - 200px);
    overflow-y: auto;
    overflow-x: hidden;
    border-radius: 8px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
    -webkit-overflow-scrolling: touch;
    touch-action: pan-y;
    overscroll-behavior: contain;
}

.admin-table-scroll table {
    width: 100%;
    table-layout: fixed;
    font-size: 1.05rem;
}

.admin-table-scroll th,
.admin-table-scroll td {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.channels-table {
    width: 100%;
    /* border-collapse: collapse ломает border/box-shadow у залипающей
       (position: sticky) шапки при скролле — известная особенность этой
       модели границ в паре со sticky. separate + border-spacing: 0 даёт
       тот же внешний вид (ячейки всё равно вплотную), но без этого бага. */
    border-collapse: separate;
    border-spacing: 0;
    background: #fff;
}

.channels-table th, .channels-table td {
    padding: 0.4rem 0.45rem;
    text-align: left;
    border-bottom: 1px solid #eceff2;
    font-size: 0.82rem;
    white-space: nowrap;
}

.channels-table th {
    white-space: normal;
    vertical-align: middle;
    min-width: 3.2rem;
}

.channels-table tbody tr:nth-of-type(even) {
    background: #f5f6f8;
}

.channels-table tbody tr.link-broken-row,
.channels-table tbody tr.link-broken-row:nth-of-type(even) {
    background: #fce4ec;
}

.channels-table tbody tr.duplicate-row,
.channels-table tbody tr.duplicate-row:nth-of-type(even) {
    background: #fce4ec;
}

.channels-table tbody tr.row-selected {
    outline: 1.5px solid #1a73e8;
    outline-offset: -1.5px;
}

.channels-table tbody tr.just-saved-row,
.channels-table tbody tr.just-saved-row:nth-of-type(even) {
    background: #e8f0fe;
    transition: background 1.2s ease;
}

/* Закреплённая строка ("📌") залипает сразу под шапкой (2-й строкой) —
   top считает JS (updatePinnedRowOffsets), т.к. высота шапки не постоянна. */
.channels-table tbody tr.pinned-row,
.channels-table tbody tr.pinned-row:nth-of-type(even) {
    background: #fff8e1;
}

.channels-table tbody tr.pinned-row {
    position: sticky;
    z-index: 4;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.pin-channel-btn {
    opacity: 0.4;
    filter: grayscale(1);
}

.pin-channel-btn img {
    width: 1.05rem;
    height: 1.05rem;
    display: block;
}

.pin-channel-btn:hover {
    opacity: 0.75;
}

.pin-channel-btn.active {
    opacity: 1;
    filter: none;
}

.channels-table th.income, .channels-table td.income,
.channels-table th.numeric, .channels-table td.numeric {
    text-align: right;
}

.admin-table-scroll .channels-table th,
.admin-table-scroll .channels-table td {
    font-size: 1rem;
    padding-top: 0.2rem;
    padding-bottom: 0.2rem;
}

.admin-table-scroll .channels-table input,
.admin-table-scroll .channels-table select {
    font-size: 1rem;
    width: 100%;
    box-sizing: border-box;
    padding-top: 0.15rem;
    padding-bottom: 0.15rem;
}

.admin-table-scroll .channels-table input.date-input-compact {
    width: fit-content;
}

.email-copy-cell {
    cursor: pointer;
}

.email-copy-cell:hover {
    background: #e8f0fe;
}

.email-copy-cell.email-copy-flash {
    background: #e6f4ea;
}

.channels-table th.views-header {
    text-align: center;
}

.channels-table td.views-cell {
    text-align: right;
}

.channel-name {
    max-width: 175px;
}

.category-cell {
    /* Длинная категория переносится на 2 строки, а не наезжает на соседнюю
       колонку; если и в 2 строки не влезает — обрезается многоточием,
       полное название — в title (подсказка при наведении). */
    max-width: 175px;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-clamp: 2;
    text-overflow: ellipsis;
    white-space: normal;
    word-break: break-word;
}

.channel-name .popup-trigger {
    max-width: 160px;
    vertical-align: bottom;
    cursor: pointer;
}

.channel-name .name-text {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: bottom;
}

/* iPad 10.2" (модель MK2K3RU/A, CSS-ширина 810px в портрете) — Аудитория
   и Рейтинг убраны из таблицы совсем (по просьбе пользователя), Дата
   создания сжата до MM-YY, но и оставшихся 12 колонок на 810px впритык —
   сжимаем padding/шрифт и ширину, чтобы уложиться без горизонтального
   скролла хотя бы в портретной ориентации. */
@media (max-width: 850px) {
    .channels-table th, .channels-table td {
        padding: 0.25rem 0.25rem;
        font-size: 0.78rem;
    }
    .channel-name {
        max-width: 100px;
    }
    .category-cell {
        max-width: 80px;
    }
    .channel-name .popup-trigger {
        max-width: 85px;
    }
    .channels-table th.numeric, .channels-table td.numeric {
        max-width: 50px;
    }
    .resource-table-frame .channels-table th.income, .resource-table-frame .channels-table td.income {
        max-width: 75px;
    }
    .screenshot-thumb, .screenshot-placeholder {
        width: 36px;
        height: 20px;
    }
    .actions {
        width: 4.4rem;
        gap: 0.2rem;
    }
    .vertical-header {
        width: 1.1rem;
        padding: 0.3rem 0.15rem;
    }
}

/* Колонка "Подпис." — с сокращённым заголовком и округлёнными числами
   (без десятых после 9,9 млн) значения короткие, поэтому сама колонка
   всегда уже остальных числовых, а не только на узких экранах. */
.channels-table th.subscriber-header, .channels-table td.subscriber-cell {
    max-width: 3.6rem;
}

.th-label-narrow, .td-value-narrow {
    display: none;
}

@media (max-width: 600px) {
    /* На узком экране (смартфон) колонка CPM не так важна, как остальные,
       и первая освобождает место в и без того тесной таблице. */
    .channels-table .cpm-col {
        display: none;
    }

    /* Подписчики — короткая подпись и число в миллионах вместо полного,
       чтобы колонка не была самой широкой в и без того тесной таблице.
       Точное число остаётся в title (всплывающая подсказка). */
    .th-label-wide, .td-value-wide {
        display: none;
    }

    .th-label-narrow, .td-value-narrow {
        display: inline;
    }
}

.channels-table th.center, .channels-table td.center {
    text-align: center;
}

.country-cell {
    cursor: pointer;
}

.vertical-header {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    white-space: nowrap;
    padding: 0.5rem 0.3rem;
    min-width: auto;
    width: 1.6rem;
}

/* Эта ячейка повёрнута на 180deg — обычная граница снизу вместе с ней
   переворачивается и оказывается визуально сверху. Ставим границу сверху
   (в её собственных, "неповёрнутых" координатах), чтобы после разворота
   линия оказалась внизу, как у остальных ячеек. */
.channels-table thead th.vertical-header {
    border-bottom: none;
    border-top: 2px solid #9aa0a6;
}

.channels-table thead th {
    background: #dfe1e5;
    font-weight: 600;
    position: sticky;
    top: 0;
    z-index: 5;
    border-bottom: 2px solid #9aa0a6;
}

/* Каждая липкая <th> — свой стек наложения (position: sticky + z-index).
   Внутренний .th-menu (выпадающее меню сортировки) с z-index:15 не может
   пробить z-index соседней <th> (тоже 5, но позже в DOM — при равенстве
   побеждает более поздний элемент), из-за чего меню визуально "проваливалось"
   под соседний заголовок. Поднимаем z-index именно у самой <th>, пока в
   ней открыто меню — тогда весь её стек оказывается выше соседей. */
.channels-table thead th:has(.th-menu-trigger:hover) {
    z-index: 20;
}

.th-menu-trigger {
    position: relative;
    display: inline-block;
    cursor: pointer;
    text-align: center;
}


.th-menu {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    background: #fff;
    border: 1px solid #eceff2;
    border-radius: 8px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
    z-index: 15;
    min-width: 180px;
    padding: 0.5rem 0.3rem 0.3rem;
}

.th-menu-trigger:hover .th-menu {
    display: block;
}

.th-menu a {
    display: block;
    padding: 0.45rem 0.6rem;
    border-radius: 6px;
    color: #1f2328;
    text-decoration: none;
    font-weight: normal;
    font-size: 0.85rem;
    white-space: nowrap;
}

.th-menu a:hover {
    background: #f0f2f5;
}

.th-menu-trigger.icon-filter-btn {
    display: inline-flex;
}

.del-menu {
    left: auto;
    right: 0;
}

.th-menu-right {
    left: auto;
    right: 1rem;
}

.cpm-tooltip {
    white-space: normal;
    width: 220px;
    color: #1f2328;
    font-size: 0.85rem;
    line-height: 1.4;
    cursor: default;
}

.del-menu button {
    display: block;
    width: 100%;
    padding: 0.45rem 0.6rem;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: #1f2328;
    text-align: left;
    font-weight: normal;
    font-size: 0.85rem;
    white-space: nowrap;
    cursor: pointer;
}

.del-menu button:hover {
    background: #f0f2f5;
}

.th-menu-sep {
    margin-top: 0.3rem;
    padding-top: 0.55rem;
    border-top: 1px solid #eceff2;
}

.actions {
    display: flex;
    gap: 0.3rem;
    align-items: center;
    /* max-width (не просто width) — раньше колонка "распухала" за счёт
       свободного места, которое table-layout:auto перераспределял сюда;
       значение чуть больше минимума под 3 иконки (1.9 + 1.9 + 2.3rem +
       2 зазора по 0.3rem = 6.7rem), чтобы не обрезать их. Отступы ячейки
       тоже урезаны отдельно — общие 0.45rem с каждой стороны у этой
       колонки не нужны. */
    max-width: 6.8rem;
}

.channels-table td.actions,
.channels-table td.edit-cell {
    padding-left: 0.2rem;
    padding-right: 0.2rem;
}

.refresh-channel-btn img {
    width: 1.05rem;
    height: 1.05rem;
    display: block;
}

.refresh-channel-btn img.spinning {
    animation: dialog-save-spin 0.9s linear infinite;
}

.actions form {
    display: inline;
}

.empty {
    text-align: center;
    color: #6b7280;
    padding: 1.5rem;
}

.link-arrow {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-left: 0.3rem;
    background: transparent;
    border: none;
    padding: 0;
    color: #1a73e8;
    text-decoration: none;
    font-size: 1.1rem;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
}

.calculator-link {
    margin-left: 0.3rem;
    margin-right: 0;
    vertical-align: middle;
}

.calculator-link img {
    width: 0.95rem;
    height: 0.95rem;
    display: block;
}

/* Поле "Категория" в попапе импорта подписок Google — без явной ширины
   растягивалось на весь доступный размер обычного текстового поля
   браузера, из-за чего колонка становилась непропорционально широкой. */
.category-input {
    width: 100%;
    max-width: 8rem;
    box-sizing: border-box;
}


.link-arrow:hover {
    color: #1558b0;
}

.link-arrow:disabled {
    color: #cbd0d6;
    cursor: default;
}

.label-with-arrow {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

.channel-url-copy-label {
    cursor: pointer;
}

.channel-form {
    max-width: 480px;
    background: #fff;
    padding: 1.25rem;
    border-radius: 8px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    /* <fieldset> по умолчанию имеет min-width:min-content — без сброса
       длинная строка без пробелов (например, JWT-токен) в Описании/Адресе
       распирала всю форму, даже с overflow-wrap:break-word (это значение
       не уменьшает min-content, в отличие от overflow-wrap:anywhere — см.
       .rich-text-editable). */
    min-width: 0;
}

.channel-form label {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    font-size: 0.9rem;
    font-weight: 500;
}

.channel-form input, .channel-form select, .channel-form textarea {
    padding: 0.35rem 0.5rem;
    border: 1px solid #ccd1d9;
    border-radius: 6px;
    font-size: 0.9rem;
    font-weight: normal;
    font-family: inherit;
    resize: vertical;
}

.count-input {
    width: 50%;
}

/* Поля, которые заполняются только автоматически ("Обновить"/
   "Автозаполнить") — вручную не редактируются: без контура/фона, значение
   выровнено по правому краю и жирным, как обычный текст, а не поле ввода. */
.count-input[readonly] {
    background: transparent;
    border: none;
    color: #202124;
    font-weight: 700;
    text-align: right;
    cursor: default;
}

.thousands-input {
    text-align: right;
}

@media (max-width: 600px) {
    .count-input {
        width: 100%;
    }

    /* Попап "Изменить/Добавить канал": заголовок поля и его значение
       должны оставаться в одну строку даже на узком экране (кроме
       "Ссылка на канал" — она и так не field-row, подпись у неё уже
       отдельной строкой над полем). Сама ширина инпутов теперь ещё и
       по значению (см. channel-form.js) — здесь только подстраховка,
       чтобы подпись не проталкивала строку на перенос. */
    .channel-form label.field-row {
        flex-wrap: nowrap;
    }

    .channel-form label.field-row .field-label-text {
        font-size: 0.82rem;
    }

    .channel-form textarea[name="description"],
    .channel-form .short-name-input {
        width: 100%;
        box-sizing: border-box;
    }

    /* Отступы попапа "Изменить/Добавить канал" — вдвое меньше, чем на
       широком экране, чтобы больше полей помещалось без прокрутки. */
    .channel-form {
        padding: 0.625rem 0.3125rem;
        gap: 0.425rem;
    }

    .channel-form label {
        gap: 0.15rem;
    }

    .channel-form input,
    .channel-form select,
    .channel-form textarea {
        padding: 0.225rem 0.3rem;
    }

    .channel-form label.field-row {
        gap: 0.3rem;
    }

    /* Подстраховка от выезда строки за край экрана: если даже сокращённая
       подпись поля вместе со значением не помещаются, подпись переносится
       по словам, а не толкает строку за пределы попапа. */
    .channel-form label.field-row .field-label-text {
        flex-shrink: 1;
        min-width: 0;
        overflow-wrap: break-word;
    }

    .channel-form label.field-row > input,
    .channel-form label.field-row > select {
        min-width: 0;
    }
}

.channel-form label.field-row {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

.field-label-text {
    flex-shrink: 0;
}

.channel-form label.field-row > input,
.channel-form label.field-row > select {
    width: auto;
    flex: 0 1 140px;
}

.channel-form label.field-row > input[name="cpm"] {
    flex-basis: 70px;
}

.short-name-input {
    resize: none;
    overflow-wrap: break-word;
    line-height: 1.3;
}

.url-with-button {
    display: flex;
    gap: 0.5rem;
}

.url-with-button input {
    flex: 1;
}

.autofill-btn {
    flex-shrink: 0;
    white-space: nowrap;
}

.autofill-status {
    display: block;
    font-size: 0.8rem;
    font-weight: normal;
    margin-top: 0.3rem;
}

.autofill-status.ok { color: #137333; }
.autofill-status.error { color: #c5221f; }

.channel-dialog {
    position: relative;
    border: none;
    border-radius: 10px;
    padding: 0;
    box-shadow: 6px 6px 20px rgba(0, 0, 0, 0.25);
    max-width: 560px;
    width: 90vw;
    max-height: 85vh;
    overflow: hidden;
    background: #fff;
}

/* Для попапов с широким содержимым (например, таблица импорта подписок) —
   тот же вид попапа, что и у остальных диалогов, но без узкого max-width. */
.channel-dialog.wide-dialog {
    max-width: 1400px;
    width: 95vw;
}

/* Тарифы/Политика/Сервис/О приложении открываются в этом попапе через
   <iframe src="...?embed=1"> (см. data-open-page-modal в base.html) —
   сама страница внутри iframe прячет свою шапку через .is-embed-page
   ниже, здесь только даём попапу и iframe достаточно места. */
.page-modal-dialog {
    height: 90vh;
}

/* "Политика" — узкий текст (max-width:760px на самой странице), общий
   wide-dialog (1400px) вокруг нужен для "Сервиса" (широкая таблица), но
   для Политики оставляет слишком много пустого места по бокам. */
.page-modal-dialog.page-modal-dialog-narrow {
    max-width: 820px;
    width: 90vw;
    height: 85vh;
}

.page-modal-body {
    padding: 0;
    flex: 1 1 auto;
    min-height: 0;
}

.page-modal-iframe {
    display: block;
    width: 100%;
    height: 100%;
    border: none;
}

/* Страница загружена внутри #page-modal-dialog (см. выше) как embed —
   без собственной шапки (кнопка возврата в БД/язык/выход не нужны,
   они уже есть на странице ПОД попапом), см. base.html. */
.is-embed-page header {
    display: none;
}

/* Заголовок "Политики" в узком попапе (см. .page-modal-dialog-narrow
   выше) — обычный размер <h1> в 760px-контейнере местами не помещался
   по ширине в одну строку. */
.is-embed-page .privacy-page h1 {
    font-size: 1.5rem;
}

.channel-dialog[open] {
    display: flex;
    flex-direction: column;
}

.dialog-header {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    background: #fff;
    padding: 1.1rem 0.6rem 1.1rem 1.5rem;
    border-bottom: 1px solid #eceff2;
    border-radius: 10px 10px 0 0;
}

.dialog-body {
    overflow-y: auto;
    flex: 1 1 auto;
}

.dialog-header-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-shrink: 0;
}

.dialog-close {
    width: 2rem;
    height: 2rem;
    padding: 0;
    margin-left: 0.5rem;
    border: none;
    background: transparent;
    color: #6b7280;
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
    border-radius: 50%;
    flex-shrink: 0;
}

.dialog-close:hover {
    background: #f0f2f5;
    color: #1f2328;
}

.channel-dialog h2 {
    margin: 0;
    font-size: 1.15rem;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.logout-dialog {
    background: #fff;
    max-width: 420px;
}

.logout-dialog .dialog-header {
    justify-content: flex-end;
    padding: 0.6rem 0.6rem 0;
    border-bottom: none;
}

.logout-dialog-body {
    padding: 0 1.75rem 2rem;
    text-align: center;
}

.logout-dialog-message {
    margin: 0 0 1.5rem;
    font-size: 1.15rem;
    font-weight: 700;
    color: #1f2328;
}

.logout-dialog-actions {
    display: flex;
    justify-content: center;
    gap: 0.75rem;
}

.logout-dialog-actions button {
    flex: 1 1 0;
}

.logged-out-message {
    text-align: center;
    color: #6b7280;
    font-size: 1rem;
    margin-top: 4rem;
}

.logged-out-actions {
    display: flex;
    justify-content: center;
    margin-top: 1rem;
}

.dialog-save-icon {
    display: none;
    width: 1.2em;
    height: 1.2em;
}

.dialog-save-icon.spinning {
    display: inline-block;
    animation: dialog-save-spin 0.9s linear infinite;
}

.description-save-icon {
    /* В этом попапе иконка — не только индикатор сохранения, но и просто
       логотип рядом с названием канала, поэтому видна всегда, а не
       только во время вращения (в отличие от остальных dialog-save-icon). */
    display: inline-block;
}

@keyframes dialog-save-spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

.filters-btn.blinking {
    animation: save-btn-blink 1s ease-in-out infinite;
}

@keyframes save-btn-blink {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.35; }
}

.channel-dialog::backdrop {
    background: rgba(0, 0, 0, 0.4);
}

.channel-dialog.auth-dialog {
    max-width: 340px;
}

.channel-dialog.auth-dialog::backdrop {
    /* Форма входа/регистрации — по сути тот же попап, что и в
       расширении, просто на сайте; тяжёлая тёмная подложка модалки
       здесь неуместна, оставляем лёгкое затемнение. */
    background: rgba(0, 0, 0, 0.15);
}

.channel-dialog .channel-form {
    max-width: none;
    box-shadow: none;
    background: transparent;
    padding: 0.4rem 1.5rem 1.5rem;
    gap: 0.25rem;
}

.form-actions {
    display: flex;
    justify-content: space-between;
    gap: 0.6rem;
    margin-top: 0.5rem;
}

.screenshot-thumb {
    width: 64px;
    height: 36px;
    object-fit: cover;
    border-radius: 4px;
    border: 1px solid #ccd1d9;
    display: block;
    cursor: pointer;
}

.screenshot-thumb.copied {
    outline: 2px solid #137333;
    outline-offset: 1px;
}

.screenshot-hover-wrap {
    position: relative;
    display: inline-block;
}

.screenshot-preview {
    /* Без зазора от миниатюры (left:100%, не +0.5rem) — иначе курсор
       при движении вправо выходит из наведённой зоны раньше, чем
       попадает на увеличенное изображение, и попап закрывается. */
    display: none;
    position: absolute;
    left: 100%;
    top: 50%;
    transform: translateY(-50%);
    z-index: 50;
    width: 240px;
    height: auto;
    object-fit: contain;
    border-radius: 8px;
    border: 2px solid #9aa0a6;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
    background: #fff;
}

.screenshot-hover-wrap:hover .screenshot-preview {
    display: block;
}

.popup-trigger {
    position: relative;
    display: inline-block;
    white-space: nowrap;
}

.channel-name {
    white-space: nowrap;
}

.screenshot-popup {
    display: none;
    position: absolute;
    top: 50%;
    /* left:100% отсчитывался от всего .popup-trigger, а он шире, чем
       текст-триггер .name-text (в него ещё входит стрелочка "↗") — между
       концом наведённого текста и левым краем попапа получался зазор,
       и курсор при переходе к попапу успевал "выйти из hover" и попап
       закрывался раньше, чем до него доезжали. Сдвигаем левее (примерно
       на ширину стрелочки), чтобы зазора почти не оставалось. */
    left: calc(100% - 1.4rem);
    transform: translateY(-50%);
    width: 350px;
    max-height: min(360px, calc(100vh - 4rem));
    overflow-y: auto;
    white-space: normal;
    background: #fff;
    color: #1f2328;
    padding: 0.5rem 0.6rem;
    border-radius: 8px;
    border: 1px solid #eceff2;
    font-size: 0.8rem;
    line-height: 1.35;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
    z-index: 20;
}

/* Попап с описанием открывается только при наведении на само название
   (а не на всю обёртку .popup-trigger, куда входит и стрелочка "Открыть в
   Ютубе" — у неё своя всплывающая подсказка, а не этот попап). Правило
   ":hover" на самом попапе держит его открытым, пока курсор переходит с
   названия на содержимое попапа (описание/копирование). */
.name-text:hover ~ .screenshot-popup,
.screenshot-popup:hover {
    display: block;
}

.popup-name-row {
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
}

.popup-name {
    display: inline-block;
    white-space: pre-line;
}

.popup-description-scroll,
.popup-description {
    /* Описания каналов из YouTube часто содержат переносы строк (нумерованные
       пункты, абзацы) — без white-space они схлопывались в один сплошной
       текст, хотя сам перенос в данных сохранялся. */
    white-space: pre-line;
}

.popup-description-scroll {
    /* Описание может быть длинным — своя прокрутка внутри попапа, а не
       растягивание попапа на весь экран (имя/стрелка и подсказка снизу
       остаются видны всегда). */
    display: block;
    max-height: 160px;
    overflow-y: auto;
    margin: 0.5rem 0;
    padding-right: 0.3rem;
    cursor: pointer;
}

.popup-description-scroll:hover {
    background: #f0f2f5;
    border-radius: 4px;
}

.popup-add-description {
    display: block;
    width: auto;
    margin: 0.5rem 0;
    padding: 0;
    border: none;
    background: transparent;
    color: #1a73e8;
    font-size: 0.8rem;
    text-decoration: underline;
    cursor: pointer;
}

.popup-add-description:hover {
    background: transparent;
    color: #1558b0;
}

.popup-translate-btn {
    display: block;
    width: auto;
    margin: 0.35rem 0 0;
    padding: 0;
    border: none;
    background: transparent;
    color: #1a73e8;
    font-size: 0.75rem;
    text-decoration: underline;
    cursor: pointer;
}

.popup-translate-btn:hover:not(:disabled) {
    background: transparent;
    color: #1558b0;
}

.popup-translate-btn:disabled {
    color: #9aa0a6;
    text-decoration: none;
    cursor: default;
}

.popup-hint {
    display: block;
    margin-top: 0.5rem;
    color: #9aa0a6;
    font-size: 0.75rem;
}

/* Попап-редактор описания (перетаскиваемый, dialog-header — ручка
   перетаскивания через initDialogDrag из import-subscriptions.js). */
.description-dialog .dialog-header {
    cursor: move;
    user-select: none;
}

.description-dialog .dialog-body {
    padding: 1rem 1.25rem 1.25rem;
    box-sizing: border-box;
}

.description-edit-textarea {
    width: 100%;
    min-height: 220px;
    max-height: 55vh;
    overflow-y: auto;
    box-sizing: border-box;
    resize: vertical;
    font-family: inherit;
}

.description-save-confirm {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
    margin-top: 0.6rem;
}

.description-save-confirm.visible {
    display: flex;
}

.description-save-confirm button {
    width: auto;
}

.name-text.copied {
    background: #e6f4ea;
    color: #137333;
    border-radius: 4px;
}

.field-copy-flash {
    background: #e6f4ea !important;
    transition: background 0.5s ease;
}

/* Поле, в котором нашёлся поисковый запрос (сейчас — только Описание,
   многострочный textarea, где точную позицию строкой мерить ненадёжно) —
   подсвечивается целиком. Снимается первым же кликом где угодно на
   странице (см. resources.html). */
.search-match {
    background: #7ee2a0 !important;
}

/* Однострочные поля (Название/Ссылки/Телефоны/Emails, в т.ч. список
   ресурсов слева) — вместо целого поля подсвечивается точно найденная
   подстрока: JS рисует .search-match-outline поверх нужных пикселей, а
   у самого поля временно снимается фон (.search-match-host), чтобы
   подложка была видна сквозь текст. */
.search-match-host {
    /* Позиционированные элементы (сам прямоугольник ниже) по умолчанию
       рисуются поверх статичных, независимо от порядка в DOM — без
       position+z-index здесь текст поля оказался бы под зелёной
       подложкой, а не поверх неё. */
    position: relative;
    z-index: 1;
    background: transparent !important;
}

.search-match-outline {
    position: absolute;
    background: #7ee2a0;
    border-radius: 3px;
    pointer-events: none;
    z-index: 0;
}

.screenshot-placeholder {
    display: block;
    width: 64px;
    height: 36px;
    border: 1px dashed #ccd1d9;
    border-radius: 4px;
}

.screenshot-preview img {
    border-radius: 6px;
    border: 1px solid #ccd1d9;
}

.name-with-preview {
    display: flex;
    align-items: flex-end;
    gap: 0.75rem;
}

.name-with-preview .name-field {
    flex: 1;
    min-width: 0;
}

.name-with-preview .screenshot-preview {
    flex-shrink: 0;
}

.name-with-preview .screenshot-preview img {
    display: block;
    height: 3.3rem;
    width: 3.3rem;
    object-fit: cover;
}

/* Страница "Ресурсы": слева список сайтов, справа форма выбранного. */
.resources-layout {
    display: flex;
    gap: 0.5rem;
    align-items: flex-start;
    margin-top: 0;
}

.resources-burger-btn {
    /* На широких экранах список ресурсов всегда пристыкован слева (не
       всплывающая панель) — кнопка открытия/закрытия не нужна. На узких
       (телефон) список не помещается рядом с формой — становится попапом,
       кнопка показывается через @media (max-width: 850px) ниже. */
    display: none;
}

.resources-sidebar-close {
    /* Видна только в попап-режиме (узкий экран, .burger-open) — см.
       @media (max-width: 850px) ниже. На широком экране список не
       закрывается (всегда пристыкован), кнопка не нужна. */
    display: none;
    position: absolute;
    top: 0.4rem;
    right: 0.4rem;
    z-index: 1;
    width: 1.7rem;
    height: 1.7rem;
    padding: 0;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 50%;
    background: transparent;
    color: #6b7280;
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
}

.resources-sidebar-close:hover {
    background: #e4e6ea;
    color: #1f2328;
}

.resources-sidebar-drag-handle {
    /* Видна только в попап-режиме (узкий экран, .burger-open) — см.
       @media (max-width: 850px) ниже, тот же принцип, что и у
       .resources-sidebar-close выше. */
    display: none;
    align-items: center;
    justify-content: center;
    height: 1.2rem;
    letter-spacing: 0.15em;
    color: #9aa0a6;
    cursor: grab;
    touch-action: none;
    user-select: none;
    font-size: 0.9rem;
}

.resources-sidebar-drag-handle:active {
    cursor: grabbing;
    color: #6b7280;
}

.resources-sidebar-col {
    flex: 0 0 240px;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    height: var(--resources-panel-height, calc(100vh - 140px));
    margin-top: 0;
}

/* На узких экранах (телефон) список ресурсов и панель ресурса рядом не
   помещаются — раньше список был всплывающей панелью (кнопка-бургер
   слева от основного бургера шапки открывала попап), потом от этого
   отказались в пользу "всегда пристыкован слева" (см. комментарии у
   .resources-burger-btn/.resources-sidebar-close/.resources-sidebar-drag-handle
   выше) — это работает на широких экранах, но на телефоне список и
   панель наезжают друг на друга. Возвращаем попап именно для узких
   экранов, тот же JS (openSidebarPopup/burger-open, resources.html)
   всё это время был на месте и рабочий — не хватало только этого CSS.
   ВАЖНО: этот блок должен идти ПОСЛЕ базового правила .resources-sidebar-col
   выше — при равной специфичности (.resources-sidebar-col в обоих местах)
   побеждает то, что позже по тексту файла, а не то, что "внутри @media".
   Раньше этот блок стоял до базового правила, и display:flex из базового
   правила молча забивал display:none отсюда на любом экране. */
@media (max-width: 850px) {
    .resources-burger-btn {
        display: inline-flex;
    }

    .resources-sidebar-col {
        display: none;
    }

    .resources-sidebar-col.burger-open {
        display: flex;
        position: fixed;
        z-index: 100;
        width: min(92vw, 22rem);
        max-height: 80vh;
        background: #fff;
        border: 1px solid #dfe1e5;
        border-radius: 10px;
        box-shadow: 0 8px 28px rgba(0, 0, 0, 0.22);
        padding: 0.6rem;
        overflow: auto;
        box-sizing: border-box;
    }

    /* Начальное положение — по центру экрана. Как только пользователь
       один раз перетащит попап за ручку ⠿⠿⠿ (resources.html,
       sidebarDragHandle), JS проставит style.left/style.top в px — это
       правило (через :not) перестаёт действовать, и дальнейшие открытия
       помнят последнюю позицию вместо того, чтобы центрировать заново. */
    .resources-sidebar-col.burger-open:not([style*="left"]) {
        left: 50%;
        top: 4rem;
        translate: -50% 0;
    }

    /* Ручка перетаскивания попапа (⠿⠿⠿) в вертикальном/узком режиме не
       нужна — попап и так открывается по центру. Явно НЕ показываем её
       здесь (остаётся display:none по умолчанию), в отличие от
       .resources-sidebar-close ниже. */

    .resources-sidebar-col.burger-open .resources-sidebar-close {
        display: flex;
        /* Без ручки перетаскивания крестик теперь в одной строке с полем
           поиска (оно — первое, что видно сверху попапа), а не под ней. */
        top: 0.6rem;
        right: 0.6rem;
    }

    .resources-sidebar-col.burger-open .resources-filters {
        /* Место под крестик закрытия справа, чтобы поле поиска не
           заезжало под него — оно же "чуть уже". */
        padding-right: 2.2rem;
    }
}

.resources-filters {
    flex-direction: column;
    align-items: stretch;
    margin-bottom: 0;
}

.resources-filters .search-wrap,
.resources-filters select {
    width: 100%;
    box-sizing: border-box;
    /* .search-wrap несёт flex: 1 1 160px из расчёта на горизонтальный ряд
       (растяжение по ширине). В этой колонке (.resources-filters —
       flex-direction: column) тот же flex-basis трактуется как высота —
       поле поиска раздувалось по вертикали и отнимало место у списка
       названий ниже, а лупа (center: top 50%) уезжала от инпута. */
    flex: 0 0 auto;
}

.resources-sidebar {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    /* Рамка — здесь, а не на fieldset ниже: тот же баг Chromium, что и
       у #resource-form (см. большой комментарий там) — fieldset с
       растянутым по высоте, но коротким по контенту списком (мало
       ресурсов) рисует border по заниженной высоте, обрубая его задолго
       до реального низа блока, хотя по факту (getBoundingClientRect)
       высота посчитана верно. overflow:hidden — чтобы квадратные углы
       #resource-name-list (border-radius:inherit от уже безрамочного
       fieldset ниже, т.е. 0) не торчали из-под скруглённых углов рамки. */
    border: 2px solid #9aa0a6;
    border-radius: 6px;
    overflow: hidden;
}

.resources-sidebar .resource-table-frame {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    /* См. комментарий у .resources-sidebar выше — рамка переехала туда. */
    border: none;
    border-radius: 0;
}

/* <legend> не всегда честно уважает display:flex в Safari/WebKit (в т.ч.
   на iPad) — сам UA-стиль легенды может тихо игнорировать это, из-за чего
   разметка внутри съезжает и клики по кнопкам ▲▼✳ не попадают туда, куда
   визуально ожидается. Поэтому flex вешаем не на сам <legend>, а на
   вложенный <span class="resource-table-legend-nav-inner"> — на span это
   ограничение не действует. */
.resource-table-legend-nav {
    width: calc(100% - 1rem);
    box-sizing: border-box;
    /* Рамка теперь рисуется на .resources-sidebar (см. комментарий там), а
       не на этом fieldset — из-за этого <legend> потерял стандартное
       поведение браузера "врезаться" в верхнюю линию рамки и просто лежит
       обычным содержимым сразу под ней. Поднимаем строку заголовка вверх,
       чтобы она снова визуально перекрывала эту линию. */
    position: relative;
    margin-top: -0.7rem;
}

.resources-sidebar .resource-table-legend-nav {
    /* Тот же приём, что выше, но .resources-sidebar (родитель) сам имеет
       overflow:hidden (нужен для скруглённых углов списка, см. комментарий
       у .resources-sidebar) — сдвиг на -0.7rem там просто обрезался этим
       overflow, "Название" визуально вообще не вылезал на линию рамки.
       Хватает совсем небольшого сдвига (по толщине самой рамки), чтобы
       текст лёг на линию, не вылезая за пределы обрезаемой области. */
    margin-top: -2px;
}

/* Таблица каналов: кнопки ▲▼✳ теперь отдельным плавающим элементом (не
   внутри <legend>), поэтому легенде больше не нужно растягиваться на всю
   ширину рамки — наоборот, широкая легенда потом перекрывала эти кнопки
   и съедала почти всю верхнюю линию рамки под вырез. Другие таблицы
   (Пользователи, Ресурсы) кнопки внутри <legend> ещё используют — их эта
   правка не трогает. */
.resource-table-legend-nav.resource-table-legend-nav-narrow {
    width: auto;
}

.resource-table-legend-nav-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    gap: 0.5rem;
}

/* Кнопки ▲▼✳ таблицы каналов вынесены из <legend> отдельным элементом
   (см. index.html) — в Safari/iOS интерактивные элементы внутри <legend>
   ненадёжно получают тапы. Держим их ВНУТРИ рамки (не наезжая на саму
   линию границы) — если пытаться посадить их поверх border через
   отрицательный сдвиг, их обрезает overflow:auto самой рамки (она же
   .table-scroll, с внутренним скроллом таблицы). Так верхняя линия рамки
   остаётся целой почти по всей ширине, кнопки — маленькой панелькой
   в правом верхнем углу изнутри. */
.resource-legend-nav-floating {
    position: absolute;
    top: 0.35rem;
    right: 0.6rem;
    display: flex;
    align-items: center;
    gap: 0.3rem;
    z-index: 3;
}

@media (max-width: 850px) {
    .resource-legend-nav-floating .resource-nav-btn {
        width: 2.2rem;
        height: 2.2rem;
        font-size: 1rem;
    }
}

.resource-legend-nav {
    display: flex;
    gap: 0.25rem;
    flex-shrink: 0;
}

.resource-legend-bridge {
    flex: 1 1 auto;
    align-self: stretch;
    position: relative;
    min-width: 0.5rem;
}

.resource-legend-bridge::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    border-top: 2px solid #9aa0a6;
}

.resource-legend-nav .resource-nav-btn {
    width: 1.35rem;
    height: 1.35rem;
    font-size: 0.65rem;
}

#phone-list-header,
#email-list-header {
    cursor: default;
}

.list-header-preview-popup {
    display: none;
    position: fixed;
    z-index: 2000;
    max-width: 320px;
    max-height: 280px;
    overflow-y: auto;
    background: #fff;
    border: 1px solid #dadce0;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
    padding: 0.4rem 0;
    font-size: 0.82rem;
    font-family: inherit;
}

.list-header-preview-line {
    padding: 0.3rem 0.7rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.list-header-preview-line:not(:last-child) {
    border-bottom: 1px solid #eee;
}

.resource-list-header {
    display: block;
    /* Тот же шрифт, что у обычной <legend> ("Ссылки"/"Телефоны") — раньше
       был мельче/светлее и выделялся среди одинаковых по смыслу
       заголовков таблиц. */
    font-size: 0.9rem;
    font-weight: 500;
    color: #1f2328;
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    /* Белая подложка — чтобы линия рамки (см. .resource-table-legend-nav
       выше) визуально "прерывалась" под текстом, как у обычной <legend>. */
    background: #fff;
    padding: 0 0.3rem;
    border-radius: 3px;
    white-space: nowrap;
}

.resource-list-header-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
}

.mail-settings-open-btn {
    background: transparent;
    border: none;
    padding: 0;
    color: #1a73e8;
    font-size: 0.78rem;
    text-decoration: underline;
    cursor: pointer;
    white-space: nowrap;
}

.mail-settings-open-btn:hover {
    color: #1558b0;
}

@media (max-width: 600px) {
    /* На узком экране в строке заголовка "Emails" и так тесно (название +
       кнопки ▲▼✳✕) — убираем эту кнопку, настройки шаблона письма на
       телефоне не первоочередная функция. */
    .mail-settings-open-btn {
        display: none;
    }
}

.resource-sort-menu {
    position: relative;
    display: block;
    min-width: 0;
}

.resource-sort-popup {
    /* Без зазора от заголовка (margin-top:0) — иначе курсор при движении
       вниз выходит из наведённой зоны раньше, чем попадает в попап, и тот
       закрывается, не успев открыться. */
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 20;
    min-width: 9.5rem;
    background: #fff;
    border: 1px solid #ccd1d9;
    border-radius: 6px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    padding: 0.3rem 0;
    margin-top: 0;
}

.resource-sort-menu:hover .resource-sort-popup {
    display: block;
}

.resource-sort-option {
    display: block;
    padding: 0.4rem 0.7rem;
    font-size: 0.82rem;
    font-weight: 400;
    color: #1f2328;
    text-decoration: none;
    white-space: nowrap;
}

.resource-sort-option:hover {
    background: #f0f2f5;
}

.resource-sort-option.active {
    color: #1a73e8;
    font-weight: 600;
}

#resource-name-list {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
}

.resource-name-input {
    cursor: pointer;
    background: transparent;
    font-size: 0.98rem;
    text-align: left;
}

.resource-name-input:hover {
    background: #f0f2f5;
}

.resource-name-input.active {
    background: transparent;
    color: #1a73e8;
    font-weight: 500;
    outline: 1.5px solid #1a73e8;
    outline-offset: -1.5px;
}

.resource-name-input.active:hover {
    background: #f0f2f5;
}

.resource-list-empty-text {
    display: block;
    padding: 0.5rem 0.6rem;
    color: #6b7280;
    font-size: 0.85rem;
}

.resource-list-empty-add {
    display: block;
    padding: 0 0.6rem 0.6rem;
    color: #1a73e8;
    font-size: 0.85rem;
    text-decoration: none;
}

.resource-list-empty-add:hover {
    text-decoration: underline;
}

.resource-nav-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.7rem;
    height: 1.7rem;
    flex-shrink: 0;
    border: 1px solid #ccd1d9;
    border-radius: 4px;
    background: #fff;
    color: #1f2328;
    text-decoration: none;
    font-size: 0.8rem;
    line-height: 1;
}

.resource-nav-btn:hover {
    background: #f0f2f5;
}

.resource-nav-btn.disabled {
    pointer-events: none;
    opacity: 0.35;
}

.resource-nav-new {
    border-color: #1a73e8;
    color: #1a73e8;
}

.resource-nav-danger {
    border-color: #d33;
    color: #d33;
}

/* Активная (доступная для клика) кнопка удаления выделения — заметно ярче
   обычной бледной обводки, чтобы явно показать, что выделение подхвачено
   и кнопка готова к действию. */
.resource-nav-danger:not(:disabled) {
    background: #d93025;
    border-color: #d93025;
    color: #fff;
    font-weight: 700;
    font-size: 1rem;
}

.resource-nav-danger:not(:disabled):hover {
    background: #a50e0e;
    border-color: #a50e0e;
}

.resource-nav-btn:disabled {
    pointer-events: none;
    opacity: 0.35;
    cursor: default;
}

.resources-panel {
    position: relative;
    flex: 1 1 auto;
    /* Раньше 1200px — на широких окнах справа оставалась большая пустая
       область (колонка списка слева фиксированная 240px и не растёт).
       1600px даёт панели расти вместе с окном, но не бесконечно (иначе
       поля растягивались бы неоправданно широко на сверхширoких мониторах). */
    max-width: 1600px;
    min-width: 0;
    height: var(--resources-panel-height, calc(100vh - 140px));
    margin-top: 0;
    /* Сама панель больше не скроллится (см. #resource-form ниже) — только
       задаёт доступную высоту, чтобы детям было что заполнять/от чего
       обрезаться. */
}

/* Форма-обёртка ".resource-form" — <fieldset> внутри простого
   <form id="resource-form">. РАМКА/ФОН/ОТСТУПЫ и СКРОЛЛ — на самом
   <form>, а не на fieldset — найденный through-тестом (offline-
   репродукция в Playwright: growing #phone-list/#email-list за пределы
   обычной высоты и снятие getBoundingClientRect() у каждого предка) баг
   Chromium: fieldset, будучи flex-контейнером с растянутыми по высоте
   детьми (align-items:stretch у .resources-columns заставляет правую
   колонку растягивать левую и наоборот), правильно вычисляет свою
   итоговую высоту (geometry/getBoundingClientRect совпадает с суммой
   контента), но РИСУЕТ border по заниженной, "доflex-growной" высоте —
   контент виден полностью, а сам border закрывается раньше и как бы
   "обрубает" рамку до того, как показал всё содержимое. У ОБЫЧНОГО
   <form> (не fieldset) такого бага нет — блочный элемент с фиксированной
   высотой и overflow-y:auto корректно и рисует border по своей границе, и
   скроллит содержимое внутри неё, что бы там ни лежало. Поэтому в
   fieldset нарочно обнулены border/background/padding, а высота, рамка
   и единственный скролл всего блока — на #resource-form. Высота у
   fieldset и #resource-form при этом жёстко равна высоте панели (а не
   auto) — иначе Ссылкам/Телефонам/Emails (flex:1 1 auto, см. ниже) было
   бы нечего "доедать", когда ресурс короткий, и снизу оставалась бы
   пустая область. <legend> внутри fieldset по-прежнему показывает текущий
   аккаунт с разрывом линии — сам разрыв рисует граница на #resource-form,
   fieldset остаётся чисто структурным (flex-раскладка + legend), без
   своего оформления. */
#resource-form {
    display: block;
    height: 100%;
    overflow-y: auto;
    max-width: 1600px;
    background: #f5f6f8;
    border: 2px solid #9aa0a6;
    border-radius: 8px;
    margin-top: 0;
    /* Тот же зазор 0.5rem везде: отступ от края панели, между колонками,
       между строками формы — раньше были три разных значения (0.6/1/0.75
       и gap 0.55rem), из-за чего отступы визуально не совпадали. */
    padding: 0.5rem;
    /* На iPad/iOS палец должен только вертикально скроллить блок полей
       (как колесо мыши на десктопе), не задевая горизонтальные жесты
       браузера (свайп назад/вперёд) и с плавной инерцией прокрутки. */
    -webkit-overflow-scrolling: touch;
    touch-action: pan-y;
    overscroll-behavior: contain;
}

.resources-panel .resource-form {
    /* Только раскладка (см. большой комментарий выше про баг fieldset) —
       никакого border/background/padding/скролла здесь, они теперь на
       #resource-form. height:100% (не auto) — чтобы детям (Ссылки/
       Телефоны/Emails, flex:1 1 auto, см. ниже) было что заполнять, когда
       ресурс короткий; если содержимое всё равно не влезает — обрезает и
       скроллит #resource-form (единственный скролл на весь блок). */
    height: 100%;
    max-width: none;
    background: transparent;
    border: none;
    border-radius: 0;
    box-shadow: none;
    margin-top: 0;
    padding: 0;
    gap: 0.5rem;
}

@media (max-width: 480px) {
    #resource-form {
        padding: 0.6rem 0.6rem 0.75rem;
    }
}

.resource-form-legend {
    /* Раньше был <legend> внутри #resource-form (border: 2px solid у
       #resource-form, а не у fieldset — см. .resources-panel .resource-form
       ниже) — сдвиг вверх через margin-top упирался в overflow-y:auto у
       #resource-form (свой скролл блока полей): либо не долетал до
       границы, либо обрезался наполовину, что и давало то видимую линию
       поперёк текста, то текст, вообще не перекрывающий рамку. Теперь это
       <span>, вынесенный из #resource-form наружу, в .resources-panel
       (см. resources.html) — здесь ничего не обрезает, поэтому просто
       кладём его абсолютно поверх верхней границы #resource-form.
       .resources-panel — position:relative, #resource-form начинается
       вплотную к её верху (margin-top:0), поэтому top:0 здесь = верхняя
       граница #resource-form. */
    position: absolute;
    top: 0;
    left: 1rem;
    transform: translateY(-50%);
    z-index: 1;
    padding: 0 0.5rem;
    font-size: 0.85rem;
    font-weight: 500;
    color: #6b7280;
    background: #f5f6f8;
}

.resource-form-legend:empty {
    display: none;
}

.resource-form-legend a {
    color: inherit;
}

html:has(.resources-layout),
body:has(.resources-layout),
html:has(.admin-page-layout),
body:has(.admin-page-layout) {
    height: 100%;
    background: #fff;
    /* Колонки страницы скроллятся сами внутри себя (высота посчитана
       точно через JS) — внешний скролл вкладки не нужен. Без height:100%
       overflow:hidden ни на что не влияет, т.к. body просто растягивается
       под контент. */
    overflow: hidden;
}

.resource-toolbar-danger {
    background: #fff;
    border-color: #d93025;
    color: #d93025;
}

.resource-toolbar-danger:hover {
    background: #fce8e6;
    border-color: #d93025;
    color: #a50e0e;
}

.resource-name-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.5rem;
}

.resource-name-field {
    /* Ширина как у поля "Описание" — оно занимает всю левую колонку
       из .resources-columns (две равные колонки с зазором 0.75rem). */
    flex: 0 1 calc(50% - 0.375rem);
}

.channel-form .resource-name-field.field-row > input {
    /* Заголовок "Название" — в одну строку с полем, а не над ним:
       поле занимает остаток строки внутри и так уже ограниченной
       по ширине .resource-name-field (не на всю страницу). */
    flex: 1;
    width: auto;
}

.resource-created-date {
    flex: 0 0 auto;
    width: calc(10.5ch + 1.2rem);
}

.resource-country-field {
    flex: 1;
    min-width: 110px;
}

.channel-form label.resource-country-field > input {
    flex: 1;
    width: auto;
}

.resource-address-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}

.resource-postal-code {
    flex: 0 0 auto;
    width: 6rem;
}

.resource-address-heading {
    font-size: 0.9rem;
    font-weight: 500;
    cursor: pointer;
}

.resource-city-field {
    flex: 1;
    min-width: 160px;
}

.channel-form label.resource-city-field > input {
    flex: 1;
    width: auto;
}

.resource-top-row {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.resource-top-row.resource-login-row {
    gap: 0.5rem;
}

/* Категории и блок Логин+Пароль — два прямых потомка строки, только они
   участвуют в этом правиле (селектор ">"), поэтому оно не конфликтует с
   правилами для Логина/Пароля внутри .resource-login-password-group —
   те вложены на уровень глубже и селектором ">" сюда не попадают.
   Реальный (не min-width:0) минимум — иначе поля просто сжимаются как
   угодно узко вместо переноса на новую строку, и текст одного поля
   визуально наезжает на соседнее. */
.resource-top-row.resource-login-row > .resource-category-field,
.resource-top-row.resource-login-row > .resource-login-password-group {
    flex: 1 1 280px;
    min-width: 260px;
}

.resource-login-row label.field-row > input,
.resource-login-row .resource-password-wrap,
.resource-login-row .resource-login-wrap {
    flex: 1;
    width: auto;
}

.resource-login-password-group {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

/* Селектор длиной в 4 класса — гарантированно перебивает общее
   ".resource-top-row .field-row" (2 класса) независимо от порядка правил
   в файле: при равной специфичности выигрывает более позднее правило,
   а не более понятное — раньше это уже приводило к неожиданному
   перекрытию Логина/Пароля, поэтому здесь специфичность делаем заведомо
   выше, а не полагаемся на порядок. */
.resource-top-row.resource-login-row .resource-login-password-group .field-row {
    flex: 1 1 190px;
    min-width: 190px;
}

.resource-login-wrap {
    position: relative;
    flex: 1;
    width: auto;
}

.resource-login-wrap input {
    width: 100%;
    padding-left: 1.7rem;
    box-sizing: border-box;
}

.resource-login-menu-btn {
    display: flex;
    position: absolute;
    left: 0.15rem;
    top: 50%;
    transform: translateY(-50%);
    width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    color: #6b7280;
    font-size: 0.9rem;
    line-height: 1;
    cursor: pointer;
}

.resource-login-menu-btn:hover {
    background: transparent;
    color: #1f2328;
}

.resource-top-row .field-row {
    flex: 1;
    min-width: 200px;
}

.channel-form label.resource-category-field > button {
    flex: 1;
    width: auto;
}

.resource-category-btn {
    flex: 1;
    text-align: left;
    padding: 0.45rem 0.6rem;
    border: 1px solid #ccd1d9;
    border-radius: 6px;
    background: #fff;
    font-size: 0.9rem;
    font-weight: normal;
    color: #1f2328;
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.resource-category-btn:hover {
    border-color: #9aa0a6;
}

.category-picker-dialog {
    max-width: 340px;
}

.category-picker-actions {
    justify-content: center;
}

.category-picker-body {
    padding: 0 1.5rem 1.5rem;
}

.category-picker-search {
    display: block;
    width: 100%;
    box-sizing: border-box;
    margin-bottom: 0.6rem;
    padding: 0.35rem 0.5rem;
    border: 1px solid #ccd1d9;
    border-radius: 6px;
    font-size: 0.9rem;
    font-family: inherit;
}

.category-picker-search:focus {
    outline: 2px solid #1a73e8;
    outline-offset: -1px;
}

.category-name-input:focus {
    outline: 2px solid #1a73e8;
    outline-offset: -1px;
}

/* Список категорий в попапе "Фильтр" — теперь такая же рамка+заголовок+
   ▲▼× как у Ссылок/Телефонов/Emails (строки — .url-row, тот же класс),
   удаление действует сразу для всех ресурсов (см. JS, как и в попапе
   "Категории" на форме ресурса). Один столбец (не 2 колонки) — иначе
   сплошной алфавитный список из БД (ORDER BY UPPER(category)) визуально
   "рвётся" на две колонки и кажется неотсортированным; прокрутка —
   обычным скроллбаром (max-height/overflow). */
.category-checkbox-list {
    display: flex;
    flex-direction: column;
    max-height: 40vh;
    overflow-y: auto;
}

.filter-category-frame {
    margin-bottom: 1rem;
}

.category-checkbox-label {
    flex: 1;
    display: flex;
    align-items: center;
    padding: 0.3rem 0.5rem 0.3rem 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: pointer;
}

/* Попап "Категории" на форме ресурса — таблица как Ссылки/Телефоны:
   чекбокс + редактируемое название + крестик удаления, один столбец со
   своим скроллом. */
#category-checkbox-list {
    max-height: 40vh;
    overflow-y: auto;
}

.url-row input.category-row-checkbox {
    flex: 0 0 auto;
    align-self: center;
    width: 1rem;
    height: 1rem;
    margin: 0 0.7rem;
    accent-color: #1a73e8;
}

.filter-picker-dialog {
    max-width: 460px;
}

.share-dialog-body {
    padding: 1.1rem 1.5rem 1.5rem;
    display: flex;
    flex-direction: column;
}

.about-btn-glyph {
    font-weight: 700;
}

.share-dialog-body label {
    font-size: 0.85rem;
    font-weight: 500;
    margin-bottom: 0.4rem;
}

.share-dialog-body label:not(:first-child) {
    margin-top: 0.8rem;
}

.share-dialog-body input,
.share-dialog-body textarea {
    width: 100%;
    box-sizing: border-box;
    padding: 0.45rem 0.6rem;
    border: 1px solid #ccd1d9;
    border-radius: 6px;
    font-size: 0.9rem;
    font-family: inherit;
    resize: vertical;
}

.filter-picker-body {
    padding: 1.1rem 1.5rem 1.5rem;
    display: flex;
    flex-direction: column;
}

.mail-settings-body {
    padding: 1.1rem 1.5rem 1.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.mail-settings-body textarea {
    width: 100%;
    box-sizing: border-box;
    resize: vertical;
}

.filter-picker-label {
    font-size: 0.85rem;
    font-weight: 500;
    margin: 0 0 0.25rem;
}

.filter-picker-body select {
    width: 100%;
    box-sizing: border-box;
    margin-bottom: 0.5rem;
}

.filter-logic-radio-list {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    margin-bottom: 0.9rem;
}

.filter-logic-radio-row {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    font-size: 0.9rem;
    font-weight: normal;
    cursor: pointer;
}

.filter-logic-radio-row input {
    flex: 0 0 auto;
    width: 1.05rem;
    height: 1.05rem;
    accent-color: #1a73e8;
}

.filter-picker-checkbox-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.9rem;
    margin-bottom: 0.35rem;
}

.filter-picker-checkbox-row input {
    flex: 0 0 auto;
    width: 1.1rem;
    height: 1.1rem;
}

.category-add-row input {
    flex: 1;
}

.confirm-popup {
    position: fixed;
    z-index: 1000;
    background: #fff;
    border-radius: 8px;
    box-shadow: 4px 4px 14px rgba(0, 0, 0, 0.25);
    padding: 0.75rem;
    font-size: 0.85rem;
    max-width: 240px;
    display: none;
}

.confirm-popup.visible {
    display: block;
}

.confirm-popup p {
    margin: 0 0 0.6rem;
    color: #1f2328;
}

.confirm-popup-actions {
    display: flex;
    gap: 0.5rem;
    justify-content: flex-end;
}

.confirm-popup-actions button {
    padding: 0.3rem 0.7rem;
    font-size: 0.8rem;
    width: auto;
}

.resources-columns {
    display: flex;
    gap: 0.5rem;
    align-items: stretch;
    flex: 1;
    min-height: 0;
}

.resources-columns .resources-col {
    flex: 1;
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

/* Ссылки/Телефоны/Emails растягиваются, заполняя всю доступную высоту
   колонки, даже когда записей мало или нет вовсе (см. #resource-form —
   он снова жёстко ограничен высотой панели, поэтому свободное место
   реально есть и его есть чем заполнять; единственный скролл — на самом
   #resource-form, см. там). Ручная высота (перетаскивание за ручку — см.
   JS) задаёт лишь стартовую высоту (flex-basis), flex-grow добирает
   остальное. */
/* Ссылки/Телефоны/Emails — высота ручная (перетаскивание за ручку внизу
   рамки, см. #url-list-resize-handle/#phone-list-resize-handle/
   #email-list-resize-handle в resources.html и JS) — не растягиваются на
   всю высоту колонки сами по себе, только по содержимому + вручную
   заданная высота. "Описание"/"Адрес" (см. ниже) — сами тянущиеся поля,
   занимают всё, что осталось в колонке после этого. */
.resources-columns .resources-col .resource-table-frame {
    flex: 0 0 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.resources-columns .resources-col > .resource-address-row {
    flex: 0 0 auto;
}

/* "Описание"/"Адрес" — занимают всё, что осталось в колонке после
   вручную заданной высоты Ссылок/Телефонов/Emails (перетаскивание — см.
   JS): тянуть границу вверх/вниз сразу меняет и высоту соседнего
   текстового поля. */
.resources-columns .resources-col > label:has(#resource-description-input),
.resources-columns .resources-col > label:has(#resource-address-input) {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
}

.resources-columns .resources-col > label textarea {
    flex: 1 1 auto;
    /* Не сжимается меньше 2 строк текста, даже когда свободного места в
       колонке почти не осталось. */
    min-height: 3.4rem;
    /* Высота теперь всегда подстраивается сама через flex — ручной resize
       только мешал бы и ломал выравнивание с соседней колонкой. */
    resize: none;
}

/* Настоящее редактируемое поле (contenteditable) для "Описание"/"Адрес" —
   textarea с тем же именем остаётся в DOM скрытой, как источник значения
   для автосохранения/поиска/сабмита формы (см. JS), а редактирует
   пользователь именно этот div — жирный/курсивный текст виден сразу,
   без переключения между "сырым" и "показанным" видом. */
.rich-text-editable {
    flex: 1 1 auto;
    /* Без min-width:0 длинная строка без пробелов (например,
       percent-encoded ссылка) распирает flex-родителей на всю свою
       ширину вместо переноса — классическая ловушка flexbox: у элемента
       есть неявный min-width:auto по контенту, который min-width:0
       снимает. overflow-wrap:break-word переносит обычный текст по
       пробелам/словам и ломает середину слова только если оно само по
       себе не помещается на строку (длинная ссылка без пробелов) —
       word-break:break-all этого не различал и рвал по буквам даже
       обычные слова, поэтому его убрали. */
    min-width: 0;
    min-height: 3.4rem;
    padding: 0.35rem 0.5rem;
    border: 1px solid #ccd1d9;
    border-radius: 6px;
    font-size: 0.9rem;
    font-family: inherit;
    background: #fff;
    white-space: pre-wrap;
    /* anywhere (не break-word) — иначе браузер не считает такой перенос
       при расчёте min-content, и длинная строка без пробелов всё равно
       распирает flex-родителей (см. min-width:0 у .channel-form). */
    overflow-wrap: anywhere;
    overflow-y: auto;
    overflow-x: hidden;
    outline: none;
}

.rich-text-editable:focus {
    border-color: #1a73e8;
}

.rich-text-editable:empty:before {
    content: attr(data-placeholder);
    color: #9aa0a6;
}

.rich-text-editable b {
    font-weight: 700;
}

.rich-text-editable i {
    font-style: italic;
}

@media (max-width: 1150px) {
    .resources-columns {
        flex-direction: column;
    }

    /* .resources-col (flex:1 по умолчанию) в widescreen-варианте делит
       высоту РЯДА строго пополам с соседней колонкой — правильно, когда
       они стоят бок о бок. Тут, при переносе в один столбец, "пополам"
       уже не имеет смысла: если в одной колонке (например, "Описание")
       текста больше, чем помещается в её половину, он просто вылезал за
       рамку прямо на соседнюю колонку снизу ("Телефоны" и т.п.). Каждая
       колонка теперь высотой по своему содержимому — общий вертикальный
       скролл уже даёт #resource-form (overflow-y: auto). */
    .resources-columns .resources-col {
        flex: 0 0 auto;
    }
}

.resource-table-frame {
    position: relative;
    border: 2px solid #9aa0a6;
    border-radius: 6px;
    padding: 0;
    margin: 0;
    min-width: 0;
}

.resource-table-frame legend {
    padding: 0 0.5rem;
    /* Отступ одинаковый с обеих сторон (легенда уже сама уже фиксированной
       ширины через calc() выше) — иначе один из верхних углов рамки
       рендерится без видимого скругления. */
    margin: 0 0.5rem;
    font-size: 0.9rem;
    font-weight: 500;
    color: #1f2328;
}

.url-list {
    display: flex;
    flex-direction: column;
    gap: 0;
    border-radius: inherit;
    overflow: hidden;
}

/* Телефоны — высота теперь ручная (см. #phone-list-resize-handle в
   resources.html), минимум — 3 строки, по умолчанию тоже 3 строки
   (min-height ниже). Своя ручка меняет высоту за счёт соседней Emails
   (см. JS) — в отличие от Ссылок/Emails, у которых при перетаскивании
   растёт вся колонка (и скроллится панель целиком), а не соседний список. */
#phone-list {
    flex: 0 0 auto !important;
    min-height: 4.4rem;
    overflow-y: auto !important;
    /* Место под полосу прокрутки резервируется всегда (даже когда строк
       мало и скролл не нужен) — иначе при появлении/исчезновении скролла
       (после добавления/удаления строки) вторая колонка ("Тип") дёргалась
       вбок на ширину полосы. */
    scrollbar-gutter: stable;
}

/* Ссылки/Emails — высота теперь ручная (см. #url-list-resize-handle/
   #email-list-resize-handle в resources.html): пользователь тянет границу
   вниз/вверх, минимум — 2 строки (принудительно через JS при
   перетаскивании), по умолчанию — 3 строки (min-height ниже), без
   верхнего предела — если места не хватает, скроллится вся панель
   ресурса целиком. */
#url-list,
#email-list {
    flex: 0 0 auto !important;
    min-height: 3rem;
    overflow-y: auto !important;
    /* См. комментарий у #phone-list выше — та же причина. */
    scrollbar-gutter: stable;
}

/* Ручка для перетаскивания высоты таблицы Ссылки (см. JS) — тонкая
   полоска внизу рамки, с маленьким "грипом" по центру для наглядности. */
.resource-list-resize-handle {
    flex: 0 0 auto;
    height: 9px;
    cursor: row-resize;
    position: relative;
    touch-action: none;
}

.resource-list-resize-handle::after {
    content: '';
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 32px;
    height: 3px;
    border-radius: 2px;
    background: #c4c7cc;
}

.resource-list-resize-handle:hover::after {
    background: #1a73e8;
}

.url-row {
    display: flex;
    align-items: stretch;
    gap: 0;
}

/* Выделение нескольких строк Shift+стрелками (Ссылки/Телефоны/Emails) —
   голубой фон, без рамки/контура. У самих полей ввода свой непрозрачный
   фон (обычно белый), поэтому красим и их тоже — иначе подсветку было бы
   видно только в узких зазорах между полями, а не по всей строке. */
.url-row.row-multi-selected,
.url-row.row-multi-selected input {
    background: #e8f0fe;
}

.channels-table tbody tr.row-multi-selected,
.channels-table tbody tr.row-multi-selected td {
    background: #e8f0fe;
}

.url-row:not(:last-child) {
    border-bottom: 1px solid #dfe1e5;
}

/* Последняя (пустая) строка — только для ввода нового значения, крестик
   удаления там не нужен (и всё равно не работает — см. JS-обработчик). */
/* visibility:hidden (не display:none) — крестик не виден, но место под
   него остаётся, иначе поля в последней (пустой) строке съезжают шире
   остальных и колонки перестают совпадать по ширине между строками. */
.url-row:last-child [data-remove-row] {
    visibility: hidden;
}

.url-row input,
.url-row .link-arrow,
.url-row .icon-btn {
    border: none;
    border-radius: 0;
    margin-left: 0;
}

.url-row > *:not(:last-child) {
    border-right: 1px solid #dfe1e5;
}

/* Ссылки: стрелка "открыть" и крестик "удалить" заменены на popup-меню по
   наведению (см. JS) — сами кнопки остаются в DOM (нужны существующим
   обработчикам клика), но не показываются напрямую. */
.url-row-hidden-action {
    display: none;
}

.url-row-hover-menu {
    display: none;
    position: fixed;
    z-index: 2000;
    flex-direction: column;
    min-width: 150px;
    max-width: 320px;
    background: #fff;
    border: 1px solid #dadce0;
    border-radius: 8px;
    box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2);
    overflow: hidden;
    font-family: inherit;
}

/* Подсказки к заголовкам "Описание"/"Адрес"/"Ссылки" (Ctrl+B/I/R) —
   тот же внешний вид, что у popup-меню (белая карточка с тенью), вместо
   системного плоского title. */
.custom-hint-tooltip {
    display: none;
    position: fixed;
    z-index: 2000;
    max-width: 280px;
    padding: 0.5rem 0.7rem;
    background: #fff;
    border: 1px solid #dadce0;
    border-radius: 8px;
    box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2);
    font-family: inherit;
    font-size: 0.8rem;
    line-height: 1.4;
    color: #202124;
    white-space: pre-line;
    pointer-events: none;
}

.url-row-hover-menu-header {
    padding: 0.5rem 0.8rem;
    font-size: 0.8rem;
    color: #5f6368;
    background: #f8f9fa;
    border-bottom: 1px solid #eee;
    word-break: break-all;
    cursor: pointer;
    max-height: 8rem;
    overflow-y: auto;
}

.url-row-hover-menu-header:empty {
    display: none;
}

.url-row-hover-menu button {
    width: auto;
    padding: 0.5rem 0.8rem;
    border: none;
    border-radius: 0;
    background: #fff;
    color: #202124;
    font-size: 0.85rem;
    text-align: left;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.url-row-hover-menu-icon {
    flex: 0 0 auto;
    font-size: 0.9rem;
    line-height: 1;
}

img.url-row-hover-menu-icon {
    width: 1rem;
    height: 1rem;
    object-fit: contain;
}

svg.url-row-hover-menu-icon {
    width: 1rem;
    height: 1rem;
    color: #5f6368;
}

.url-row-hover-menu button.danger svg.url-row-hover-menu-icon {
    color: #c5221f;
}

.url-row-hover-menu-primary {
    font-weight: 700;
}

.url-row-hover-menu button[data-menu-action="description"].has-description {
    font-weight: 700;
}

.url-row-hover-menu button:not(:last-child) {
    border-bottom: 1px solid #eee;
}

.url-row-hover-menu button:hover {
    background: #f0f2f5;
}

.url-row-hover-menu button.danger {
    color: #c5221f;
}

/* Строка с сохранённым значением (логин/email, тип телефона) + крестик
   удаления ЭТОГО значения из списка — value-кнопка и крестик теперь два
   разных элемента внутри общей строки-обёртки, а не один сплошной button
   (см. renderMenu в resources.html). */
.url-row-hover-menu-item {
    display: flex;
    align-items: stretch;
}

.url-row-hover-menu-item:not(:last-child) {
    border-bottom: 1px solid #eee;
}

.url-row-hover-menu-item button[data-menu-value] {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.url-row-hover-menu-item .url-row-hover-menu-delete {
    flex: 0 0 auto;
    width: 1.8rem;
    padding: 0;
    border: none;
    border-radius: 0;
    background: transparent;
    color: #9aa0a6;
    font-size: 0.95rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

.url-row-hover-menu-item .url-row-hover-menu-delete:hover {
    background: #fce8e6;
    color: #c5221f;
}


.url-row input {
    flex: 1;
    padding-top: 0.15rem;
    padding-bottom: 0.15rem;
    text-align: left;
}

/* Ссылка с описанием (см. url_descriptions) — светло-синий шрифт,
   чтобы её было видно среди остальных без открытия меню "Описание". */
#url-list input[name="urls"].has-description {
    color: #5b9bd5;
}

.url-row .phone-type-wrap {
    position: relative;
    flex: 0 0 112px;
}

/* Стрелка "открыть чат" сразу в строке телефона, после самого значения —
   видна только когда Тип — WhatsApp/Telegram (см. JS: updatePhoneOpenChatBtn),
   у остальных типов её тут нет (открывать нечего). */
.phone-open-chat-btn {
    flex: 0 0 auto;
    width: 1.6rem;
    padding: 0;
    border: none;
    background: transparent;
    color: #1a73e8;
    font-size: 0.9rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

.phone-open-chat-btn:hover {
    background: #e8f0fe;
    color: #174ea6;
}

.phone-type-wrap input.phone-type-input {
    width: 100%;
    box-sizing: border-box;
    padding-left: 1.3rem;
}

.phone-type-menu-btn {
    display: flex;
    position: absolute;
    left: 0.1rem;
    top: 50%;
    transform: translateY(-50%);
    width: 1.1rem;
    height: 1.1rem;
    padding: 0;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    color: #6b7280;
    font-size: 0.75rem;
    line-height: 1;
    cursor: pointer;
}

.phone-type-menu-btn:hover {
    background: transparent;
    color: #1f2328;
}

.url-row .link-arrow,
.url-row .icon-btn {
    flex: 0 0 auto;
    width: 2.4rem;
    height: auto;
    align-self: stretch;
}

.local-admin-users-table th {
    text-align: center;
}

/* Таблица пользователей в "Сервисе" (admin_users.html) на узком экране —
   полных подписей и обычной ширины столбцов не хватает. */
.th-text-compact {
    display: none;
}

@media (max-width: 700px) {
    .local-admin-users-table {
        font-size: 0.78rem;
    }

    /* Заголовок/значение сам НЕ поворачиваем (у <th>/<td> при
       writing-mode:vertical-rl "по центру" означает центр по
       вертикальной, а не горизонтальной оси — из-за этого поворотный
       текст и обычные (неповоротные) соседние элементы в столбце не
       совпадали по общей оси). Вместо этого поворачиваем только
       ВНУТРЕННИЙ элемент (span) — тогда text-align:center на самой
       ячейке (обычный, горизонтальный writing-mode) центрирует его
       как любой другой инлайновый элемент, и заголовок с значением
       в одном столбце оказываются на одной оси. */
    .local-admin-users-table th {
        vertical-align: bottom;
        padding-bottom: 0.4rem;
    }

    .local-admin-users-table td.col-resources-count {
        /* Сама ячейка НЕ поворачивается (см. комментарий у th/td.col-date
           выше про ось центрирования) — поворачивается вложенный
           .th-vertical-text (правило см. рядом с th .th-vertical-text). */
        text-align: center;
        padding-top: 0.35rem;
        padding-bottom: 0.35rem;
    }

    .local-admin-users-table td.col-date {
        text-align: center;
        /* Небольшой зазор сверху/снизу — иначе повёрнутый текст даты
           упирается прямо в границы ячейки. .date-compact-label — сам
           position:absolute с top/left в процентах, но это всё равно
           работает: проценты для абсолютно спозиционированного элемента
           отсчитываются от padding-box предка (position:relative), так
           что добавленный здесь padding автоматически "поджимает" зону
           центрирования и для него тоже, без отдельного правила. */
        padding-top: 0.35rem;
        padding-bottom: 0.35rem;
    }

    .local-admin-users-table th .th-text-full,
    .local-admin-users-table th .th-text-compact,
    .local-admin-users-table th .th-vertical-text,
    .local-admin-users-table td.col-date .th-text-full,
    .local-admin-users-table td.col-date .th-text-compact,
    .local-admin-users-table td.col-resources-count .th-vertical-text {
        /* display НЕ трогаем здесь — его переключают .th-text-full/
           .th-text-compact ниже (полный/сокращённый вариант), а это
           правило (выше по специфичности из-за .local-admin-users-table)
           раньше молча забивало display:none у скрытого варианта, и оба
           текста показывались одновременно склеенными. */
        writing-mode: vertical-rl;
        transform: rotate(180deg);
        white-space: nowrap;
    }

    /* "Доступ до" — ячейка с настоящим <input type="date"> внутри: саму
       ячейку (и вместе с ней инпут) не поворачиваем, чтобы не получить
       перевёрнутое поле ввода — поворачиваем только видимую наклейку
       с текстом поверх (см. .date-compact-label в HTML/JS). */
    .date-compact-wrap .date-compact-label {
        writing-mode: vertical-rl;
        transform: translate(-50%, -50%) rotate(180deg);
        left: 50%;
        top: 50%;
    }

    .th-text-full {
        display: none;
    }

    .th-text-compact {
        display: inline-block;
    }

    .local-admin-users-table .col-num {
        display: none;
    }

    /* Email — единственная колонка, которой реально нужна ширина (адреса
       длинные); остальные (кроме Email) сжимаются до содержимого — они
       все теперь узкие/повёрнутые — и освобождают место для Email.
       width — inline-стиль на <col> (см. admin_users.html), поэтому
       здесь нужен !important, чтобы его перебить.
       У этой таблицы table-layout: fixed (.admin-table-scroll table) —
       в этом режиме ширины ("auto"/"1%"/проценты) не подстраиваются под
       контент, как в обычном (auto) layout, а жёстко режут его: "1%"
       здесь означало буквально пару пикселей, и содержимое остальных
       колонок оказывалось попросту невидимым (не просто узким).
       Поэтому — не "1%", а конкретный минимальный размер в rem, которого
       хватает под повёрнутый текст/чекбокс, а Email забирает всё, что
       останется. */
    .local-admin-users-table colgroup col.col-email {
        width: auto !important;
    }

    .local-admin-users-table td.email-copy-cell {
        /* Не обрезаем многоточием — переносим как есть (можно посреди
           слова/адреса), высота строки/ячейки растёт под текст. */
        white-space: normal;
        overflow-wrap: anywhere;
    }

    .local-admin-users-table colgroup col:not(.col-num):not(.col-email) {
        width: 2.1rem !important;
    }

    .date-compact-wrap {
        position: relative;
    }

    .date-compact-wrap .date-input-compact {
        /* Сам инпут остаётся кликабельным на всю ячейку (открывает
           нативный календарь) и функционально на месте — но визуально
           полностью скрыт (не только текст, но и рамка/фон/стрелка
           календаря) — виден только .date-compact-label поверх, в нужном
           формате и повёрнутый вертикально. */
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        border: none;
        background: transparent;
        color: transparent;
        opacity: 0;
        cursor: pointer;
    }

    .date-compact-label {
        position: absolute;
        left: 0.4rem;
        top: 50%;
        transform: translateY(-50%);
        pointer-events: none;
        color: #1f2328;
        font-size: inherit;
        white-space: nowrap;
    }

    /* "Тариф" (select) — тот же приём, что у "Доступ до": сам select не
       поворачиваем (нативная стрелка/список повернулись бы криво в
       разных браузерах) — прячем его текст и показываем наклейку
       "P"/"F" повёрнутой вертикально поверх, по центру ячейки. */
    .tariff-compact-wrap {
        position: relative;
        text-align: center;
    }

    .tariff-compact-wrap select {
        color: transparent;
    }

    .tariff-compact-label {
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%) rotate(180deg);
        writing-mode: vertical-rl;
        pointer-events: none;
        color: #1f2328;
        font-size: inherit;
        white-space: nowrap;
    }
}

