﻿/* ============================================================================
   [DESIGN TOKENS] ПЕРЕЕХАЛИ в tokens.css (DS9 волна 1, 2026-07-09).
   ----------------------------------------------------------------------------
   Все токены :root (--c-*, --radius-*, --space-*, --fs-*, --fw-*, --shadow-*,
   --control-h, --tab-bar-h) и их @media-dark переопределения живут в tokens.css,
   который подключается ПЕРВЫМ <link> во всех HTML (index/login/profile) — ДО
   этого файла. Здесь токены НЕ объявляются.
   ПРАВИЛО прежнее: новый CSS НЕ хардкодит цвета/радиусы/шрифты/тени —
   только var(--*) из tokens.css (+ per-theme --th-* из themes.css).
   ============================================================================ */

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    background-color: var(--tg-theme-bg-color, #ffffff);
    color: var(--tg-theme-text-color, #000000);
    margin: 0;
    padding: var(--space-4);   /* [DS8] 15px → шкала отступов */
    box-sizing: border-box;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
}

h2,
h3 {
    margin-top: 0;
    font-size: var(--fs-lg);
    font-weight: var(--fw-medium);   /* [DS8] literal 500 → токен (визуально без изменений) */
    color: var(--tg-theme-text-color, #000000);
}

.hidden {
    display: none !important;
}

#subscribe-btn {
    min-width: 32px;
    min-height: 32px;
    padding: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    -webkit-appearance: none;
    appearance: none;
}

#subscribe-btn.hidden {
    display: none !important;
}

.top-nav {
    display: flex;
    align-items: center;
    min-height: var(--control-h);
    margin-bottom: var(--space-5);
}

/* [DS] Кнопка «назад» — обычный flex-ребёнок шапки (.top-nav/.schedule-header-inner),
   центрируется по вертикали вместе с заголовком. Раньше была position:fixed в углу
   вьюпорта → визуально «висела» выше текста. */
.nav-back-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
    margin-left: -4px;
    padding: 4px 12px 4px 4px;
    z-index: 200;
    background: none;
    border: none;
    font-size: var(--fs-xl);
    line-height: 1;
    cursor: pointer;
    color: var(--th-link-accent, var(--c-accent));
    -webkit-tap-highlight-color: transparent;
}

.nav-title {
    margin: 0;
    font-size: var(--fs-lg);
    font-weight: var(--fw-semibold);
    flex-grow: 1;
}

/* --- [DS10] Скрывающаяся по скроллу шапка страницы поиска графиков (#all-schedules-view) ---
   .top-nav сам по себе — общий класс на десятках страниц (не трогаем); здесь свой scoped
   контейнер, оборачивающий .top-nav + строку поиска, чтобы прятать/показывать их ОДНИМ
   transform'ом по направлению скролла (см. _onAsHeaderScroll в app_events.js). */
.as-sticky-header {
    position: sticky;
    top: 0;
    z-index: 55;
    background: var(--tg-theme-bg-color, #fff);
    transition: transform 0.22s ease;
    will-change: transform;
}
.as-header--hidden {
    transform: translateY(-100%);
}

/* Обёртка кнопки-иконки в шапке + её попап (геолокация/раздел на "Графики") — якорь
   position:relative для _toggleDropdown, по образцу settings-wrap/user-avatar-wrap. */
.icon-dropdown-wrap {
    position: relative;
    flex-shrink: 0;
}
/* Небольшой отступ от правого края шапки (после иконки раздела обычно ничего, кроме
   скрытого #moderation-btn) — иконки не должны липнуть к самому краю экрана. */
#as-cat-wrap {
    margin-right: 8px;
}

.form-group {
    margin-bottom: var(--space-4);
}

label {
    display: block;
    font-weight: var(--fw-semibold);
    margin-bottom: var(--space-1);
    color: var(--tg-theme-text-color, #000000);
}

input[type="text"],
input[type="number"],
input[type="email"],
input[type="password"],
input[type="tel"],
input[type="date"],
input[type="time"],
select,
textarea {
    width: 100%;
    padding: var(--space-3);
    border: 1px solid var(--c-hint);
    border-radius: var(--radius-sm);
    box-sizing: border-box;
    background-color: var(--c-bg);
    color: var(--c-text);
    font-size: var(--fs-md);
    font-family: inherit;
}

textarea {
    resize: none;
    overflow: hidden;
    min-height: var(--control-h);
}

/* --- INPUT STATES (DS7) --- */
/* Глобальные состояния инпутов: фокус, disabled, ошибка. Применяется на ВСЕХ ширинах. */
input[type="text"]:focus,
input[type="number"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
input[type="tel"]:focus,
input[type="date"]:focus,
input[type="time"]:focus,
select:focus,
textarea:focus {
    border-color: var(--th-focus, var(--c-accent));
    outline: none;
    box-shadow: 0 0 0 3px var(--th-accent-glow, var(--c-accent-glow));
}
input:disabled, select:disabled, textarea:disabled {
    opacity: .55;
    cursor: not-allowed;
}
input.error, select.error, textarea.error {
    border-color: var(--c-danger) !important;
}
input.error:focus, textarea.error:focus {
    box-shadow: 0 0 0 3px var(--th-danger-glow, var(--c-danger-glow));
}

.grid-checkboxes {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
    gap: 10px;
}

.checkbox-btn {
    display: block;
    text-align: center;
    padding: 10px;
    border-radius: var(--radius-sm);
    background-color: var(--tg-theme-secondary-bg-color, #efeff4);
    cursor: pointer;
    color: var(--tg-theme-text-color, #000);
    font-weight: 500;
    border: 2px solid transparent;
    /* [QA 2026-08-25] Длинные однословные подписи пресетов («Парикмахерская») шире
       минимальной ширины колонки (80px) и без переноса обрезались фоном соседней
       плитки (overflow:visible рисует хвост слова поверх следующего grid-item).
       break-word переносит слово по слогам на вторую строку — как у «Аренда корта». */
    overflow-wrap: break-word;
}

.checkbox-btn.active {
    background-color: var(--th-focus, var(--c-accent));
    color: var(--c-on-accent);
}

/* --- [CATEGORIES] Разделы графика --- */
/* Раздел выбирается компактным дропдауном (DS10): в формах (мастер/правка) —
   кнопка-триггер .cat-select-trigger, разворачивающая .dropdown-menu.cat-dropdown-list
   на всю ширину поля; на странице поиска — иконка .nav-icon-btn в шапке, тот же
   .cat-dropdown-list как узкий попап (стили пункта — у канона .dropdown-item). Исходный
   <select> остаётся в DOM скрытым — хранилище состояния и i18n-якорь для <option>. */
.cat-select-trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    width: 100%;
    padding: var(--space-3);
    border: 1px solid var(--c-hint);
    border-radius: var(--radius-sm);
    box-sizing: border-box;
    background-color: var(--c-bg);
    color: var(--c-text);
    font-size: var(--fs-md);
    font-family: inherit;
    cursor: pointer;
    text-align: left;
}
.cat-select-trigger:hover { border-color: var(--th-focus, var(--c-accent)); }
.cat-select-trigger:focus-visible { border-color: var(--th-focus, var(--c-accent)); outline: none; }
.cat-select-trigger:active { opacity: 0.9; }
.cat-select-trigger:disabled { opacity: .5; cursor: default; pointer-events: none; }
.cat-select-trigger--placeholder { color: var(--tg-theme-hint-color, #8e8e93); }

.cat-select-wrap { position: relative; }
.cat-select-wrap .dropdown-menu {
    left: 0;
    right: auto;
    top: 100%;
    margin-top: 4px;
    width: 100%;
    box-sizing: border-box;
}

/* Попап геолокации/раздела в шапке страницы поиска — кнопка стоит у ПРАВОГО края экрана
   (после неё в потоке только скрытый #moderation-btn), поэтому попап растёт ВЛЕВО от
   правого края обёртки (right:0), а не от левого — иначе на узком экране список уезжает
   за правый край viewport и часть пунктов физически недоступна тапом. Канон .dropdown-menu
   даёт свои left/top для другого случая (угловая кнопка настроек) — здесь свои значения. */
#as-geo-wrap .dropdown-menu,
#as-cat-wrap .dropdown-menu {
    left: auto;
    right: 0;
    top: 100%;
    margin-top: 6px;
}
.geo-popover { width: 280px; max-width: calc(100vw - 2 * var(--space-4)); }
.cat-dropdown-list { max-width: calc(100vw - 2 * var(--space-4)); }

.day-timeline-container {
    margin-top: var(--space-4);
    padding: var(--space-3);
    border-radius: var(--radius-md);
    background-color: var(--tg-theme-secondary-bg-color, #efeff4);
}

.day-title {
    font-weight: var(--fw-bold);
    margin-bottom: var(--space-3);
    color: var(--th-link-accent, var(--c-accent));
}

/* --- [COPY DAY] Заголовок блока дня с кнопкой «На все дни» — та же .day-title внутри,
   просто в строке рядом с кнопкой, поэтому у неё margin-bottom снят (перенесён на строку). --- */
.day-title-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
}

.day-title-row .day-title {
    margin-bottom: 0;
}

.day-copy-btn {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px var(--space-2);
    border: none;
    border-radius: var(--radius-sm);
    background-color: var(--tg-theme-bg-color, #ffffff);
    color: var(--th-link-accent, var(--c-accent));
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
    cursor: pointer;
}
.day-copy-btn:hover    { background-color: var(--tg-theme-bg-color, #ffffff); opacity: .85; }
.day-copy-btn:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 1px; }
.day-copy-btn:active   { opacity: .6; }

/* --- [CYCLIC MODE] Переключатель режима / календарь дат / маркер петли --- */
.schedule-mode-toggle {
    grid-template-columns: 1fr 1fr;
}

.dates-calendar {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 5px;
    max-height: 300px;
    overflow-y: auto;
    padding: 8px;
    border-radius: var(--radius-md);
    background-color: var(--tg-theme-secondary-bg-color, #efeff4);
}

/* --- [DATES CALENDAR WRAP] Обёртка помесячного календаря выбора дат ---
   Внутри — по одному блоку `.nav-cal-month` + `.dates-calendar` на КАЖДЫЙ месяц
   (тот же вид, что у nav-календаря панели графика).
   ⚠ СВОЕЙ прокрутки здесь НЕТ намеренно: раньше календарь скроллился внутри себя, а
   страница — снаружи, и две независимые прокрутки в одном экране путали (репорт
   2026-08-05). Блок растёт по высоте, а прокрутка на весь шаг мастера остаётся одна. */
.dates-calendar-wrap {
    border-radius: var(--radius-md);
    background-color: var(--tg-theme-secondary-bg-color, #efeff4);
    padding: 4px 0 8px;
}
.dates-calendar-wrap .dates-calendar {
    max-height: none;
    overflow: visible;   /* см. выше: единственная прокрутка — у страницы */
    background: none;
    padding: 0 8px;
}
.dates-calendar-wrap .nav-cal-month { margin: 12px 12px 8px; }
.dates-calendar-wrap .nav-cal-month:first-child { margin-top: 6px; }

/* Дни вне доступного горизонта (прошлое / дальше 60 дней) — видны, но не выбираются. */
.date-cell.date-cell-disabled {
    opacity: 0.32;
    pointer-events: none;
}

/* Шапка дней недели (Пн..Вс) */
.date-cal-head {
    text-align: center;
    font-size: 0.72em;
    font-weight: var(--fw-bold);
    opacity: 0.65;
    padding: 2px 0;
}

/* Пустая ячейка-заполнитель — выравнивание первой недели по колонкам */
.date-cell-filler {
    visibility: hidden;
}

.date-cell {
    padding: 8px 2px;
    line-height: 1.1;
    /* [DS9] Цифры дат по центру и в nav-календаре (в календаре создания графика
       центрирование давал только сосед-класс .checkbox-btn) */
    text-align: center;
}

.date-cell-num {
    display: block;
    font-weight: var(--fw-bold);
    font-size: 1em;
}

/* Воскресенья — красным (у выбранного — белый текст на акценте, не переопределяем) */
.date-cell-sunday:not(.active) {
    color: var(--c-danger);
}

/* --- [NAV-КАЛЕНДАРЬ] Навигация по дням графика из синей полосы --- */
/* Календарь открыт: прячем все слоты, но синяя полоса (today-divider) с нажатой кнопкой остаётся */
#schedule-list.nav-cal-open > *:not(#today-divider) {
    display: none !important;
}
#schedule-nav-calendar {
    padding: 8px 0 16px;
}

/* Разделитель месяца */
.nav-cal-month {
    font-weight: var(--fw-bold);
    font-size: var(--fs-md);
    color: var(--tg-theme-hint-color, #8e8e93);
    margin: 14px 4px 8px;
    padding-bottom: 4px;
    border-bottom: 1px solid var(--tg-theme-secondary-bg-color, #efeff4);
}
#schedule-nav-calendar .nav-cal-month:first-child { margin-top: 4px; }

/* День со слотами — кликабельная отметка. [DS9] Тихая ячейка: вместо полной акцентной
   заливки только синяя НИЖНЯЯ граница (inset-тень — не сдвигает сетку по высоте),
   текст остаётся цветом темы (воскресенья — красными). Ховер — лёгкий акцентный фон. */
.date-cell.nav-cal-has-slots {
    box-shadow: inset 0 -3px 0 var(--th-grid-selected, var(--c-accent));
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background 0.15s;
}
.date-cell.nav-cal-has-slots:hover { background: var(--th-accent-glow, var(--c-accent-glow)); }
.date-cell.nav-cal-has-slots:active { opacity: 0.8; }

/* День без слотов — приглушён, не кликается */
.date-cell.nav-cal-empty {
    opacity: 0.4;
    pointer-events: none;
}

/* Маркер начала следующего круга — оранжевый акцент (как admin-only зона) */
.day-timeline-container.cycle-marker {
    background-color: rgba(255, 149, 0, 0.12);
    border-left: 3px solid var(--th-warning, var(--c-warning));
}

.cycle-marker-title {
    color: var(--th-warning, var(--c-warning));
    margin-bottom: 0;
}

.time-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    touch-action: pan-y;
}

.time-cell {
    flex: 1 1 calc(25% - 8px);
    min-width: 65px;
    padding: 12px 0;
    text-align: center;
    border-radius: var(--radius-sm);
    font-size: var(--fs-sm);
    font-weight: 500;
    background-color: var(--tg-theme-bg-color, #ffffff);
    border: 1px solid var(--tg-theme-hint-color, #ccc);
    cursor: pointer;
    box-sizing: border-box;
}

.time-cell.selected {
    background-color: var(--th-grid-selected, var(--c-accent));
    color: var(--c-on-accent);
    border-color: var(--th-grid-selected, var(--c-accent));
}

.time-cell.selected.merge-right {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
    margin-right: -8px;
    padding-right: 8px;
    border-right: 1px solid rgba(255, 255, 255, 0.4);
    z-index: 1;
    position: relative;
}

.time-cell.selected.merge-left {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
    border-left-color: transparent;
}

.date-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: var(--fs-md);
    font-weight: bold;
    margin-top: 20px;
    margin-bottom: 10px;
    color: var(--tg-theme-hint-color, #999);
    border-bottom: 1px solid var(--tg-theme-secondary-bg-color, #efeff4);
    padding-bottom: 5px;
}

/* --- [DAY CARD → DAY ROW, DS9 W2] День — тихая строка-разделитель вместо цветной карточки:
   иконка+дата приглушённым капсом, гибкая линия до правых инструментов (⋮ админа + шеврон).
   Линия — ::after (flex-item через order; кнопкам задан order:2, линия order:1).
   Токены --th-day-bg/-accent удалены из палитр ([DS9 W5]). --- */
.date-header.day-card {
    background: transparent;
    border: none;
    border-radius: 0;
    padding: 4px 2px;
    margin-top: 16px;
    margin-bottom: 8px;
    color: var(--tg-theme-hint-color, #8e8e93);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    gap: 8px;
    justify-content: flex-start;   /* базовый .date-header даёт space-between — здесь линия сама распирает */
    position: relative;
    box-shadow: none;
}
.date-header.day-card::after {
    content: '';
    order: 1;
    flex: 1 1 auto;
    height: 1px;
    background: var(--th-border-subtle, var(--c-border-subtle));
    margin: 0 2px;
}
.date-header.day-card > button { order: 2; }

/* [DS9 W2] Слот — нейтральная строка на surface-1; СТАТУС несёт полоска 3px (::before,
   цвет из --st-strip по классу состояния) + чип-счётчик-«батарея» (.slot-count). */
.slot-container {
    display: flex;
    flex-direction: column;
    background-color: var(--th-surface-1, var(--c-surface-1));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    box-shadow: var(--th-slot-shadow, var(--shadow-1));   /* [DS9 W5] тень-1 из базы (легаси-блоки сжаты) */
    padding: 11px 12px;
    border-radius: var(--radius-md);
    margin-bottom: 10px;
    overflow: hidden;
    position: relative;
    /* цвет статус-полоски по умолчанию (перекрывается классами состояния ниже) */
    --st-strip: var(--th-slot-acc-full, var(--c-slot-acc-full));
}

/* --- [SLOT LEFT ACCENT → STATUS STRIP, DS9 W2] Левая вертикаль 3px цветом СТАТУСА
   (--th-slot-acc-* из палитр; --st-strip назначается классом состояния на .slot-container).
   Старый токен --th-slot-accent удалён из палитр ([DS9 W5]). --- */
.slot-container::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: var(--radius-md);     /* совпадает с радиусом карточки */
    border-left: 3px solid var(--st-strip);
    pointer-events: none;
}
.slot-container.slot-free    { --st-strip: var(--th-slot-acc-free, var(--c-slot-acc-free)); }
.slot-container.slot-partial { --st-strip: var(--th-slot-acc-partial, var(--c-slot-acc-partial)); }
.slot-container.slot-busy    { --st-strip: var(--th-slot-acc-busy, var(--c-slot-acc-busy)); }
.slot-container.expired      { --st-strip: var(--c-expired); }
.slot-container.admin-only   { --st-strip: var(--th-warning, var(--c-warning)); }
/* [ЗАМОРОЗКА, 2026-08-26] Раньше .frozen ДАВАЛ СВОЙ ФИКСИРОВАННЫЙ --st-strip и перебивал
   занятость (см. историю ниже) — все замороженные слоты выглядели одинаково. Теперь
   занятость несёт та же полоска, что и вне заморозки (классы .slot-free/partial/busy/other
   ставятся всегда, buildSlotCard), а .frozen только приглушает opacity (см. ниже). Место,
   освободившееся ИМЕННО во время заморозки (см. reopenedSpot), получает СВОЙ акцент поверх —
   тот же голубой токен, что раньше означал «заморожено», теперь значит «нужно решение». */
.slot-container.reopened     { --st-strip: var(--th-frozen, var(--c-frozen)); }

.slot-row {
    display: flex;
    align-items: stretch;
    gap: 0;
}

/* --- [SLOT STRIP] Полоса действий слота. Цветной прямоугольник убран — инструменты лежат
   прямо на тинте слота (его задаёт .slot-container.slot-free/busy/other, см. ниже).
   Кнопка действия прижата влево, ⋮/шеврон уходят вправо. --- */
.slot-actions {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 6px;
    align-self: center;
    min-width: 0;
}

.slot-actions .btn-inline {
    flex: 0 1 auto;          /* может сжиматься (…) — чтобы счётчик/⋮/шеврон всегда влезали */
    min-width: 0;
    justify-content: center; /* глагол — по центру кнопки (равные отступы слева/справа) */
}
/* Левая часть полосы слота — ОДНА колонка на все состояния: и кнопка действия
   («Записаться»/«Отменить»), и подписи-плашки .btn-slot-plain («Занято», «Закрыто»,
   «Записан» в режиме автораспределения, замок admin-only) занимают всю свободную ширину и
   центрируют текст (justify-content:center у .btn-inline выше). Ширину освободил
   чип-счётчик, лишившийся отдельной полоски-прогресса.
   ⚠ Раньше растягивалась ТОЛЬКО .btn-slot-action, а плашки прижимались к времени слева
   («они не кнопки») — в столбце слотов каждый занятый/закрытый выбивался из общей вертикали.
   Плашка остаётся некликабельной (cursor:default, без фона и рамки — см. .btn-slot-plain),
   растягивается только её ячейка. */
.slot-actions .btn-slot-action,
.slot-actions .btn-slot-plain {
    flex: 1 1 auto;
}
/* [FIX] Текст глагола — отдельный span: центрируется, когда слово влезает; при нехватке
   места режется ТОЛЬКО справа многоточием (левый край не обрезается). Без span обрезка
   flex-контента при justify-content:center «съедала» слово с ОБОИХ концов. */
.slot-actions .btn-inline .slot-action-label {
    display: block;
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.slot-actions .chevron-btn {
    opacity: 0.7;
}

/* --- [SLOT ACTIONS RIGHT] Правый кластер инструментов слота (право→лево: шеврон · ⋮ · 1/2).
   margin-left:auto прижимает кластер к правому краю, кнопка действия занимает левую часть и
   при нехватке места режется многоточием (.btn-inline). Каждая .sa-cell имеет фиксированную
   ширину и держит место, даже когда её элемент отсутствует — инструменты не «прыгают». --- */
.slot-actions-right {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-left: auto;
    flex: 0 0 auto;
}
.sa-cell {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
}
.sa-count   { width: auto; min-width: 30px; }     /* [DS9 W2] чип с прогрессом шире фикс-30px */
.sa-menu    { width: 26px; }                      /* ⋮ / 🗑 (или пусто-плейсхолдер) */
.sa-chevron { width: 30px; margin-right: -4px; }  /* шеврон чуть правее — ближе к краю слота */

/* --- SLOTS BUSY LOCK --- */
/* Пока идёт запрос записи/отмены — кнопка действия погашена и не кликается.
   Класс slots-busy вешается на #schedule-list из app_realtime.js на время round-trip. */
#schedule-list.slots-busy .slot-actions .btn-inline {
    pointer-events: none;
    opacity: 0.55;
}

/* --- [SLOT TINT] Весь слот наследует цвет состояния (база / Системная тема).
   Для явных тем переопределяется через --th-slot-* в themes.css ([SKIN]).
   expired/admin-only/frozen имеют собственные тинты ниже и сюда не входят. --- */
.slot-container.slot-free    { background: var(--c-slot-free-bg); }    /* пусто (есть места, нет участников) */
.slot-container.slot-partial { background: var(--c-slot-partial-bg); } /* есть участники, есть свободные места */
.slot-container.slot-other   { background: var(--c-slot-full-bg); }    /* мест нет = заполнен */
.slot-container.slot-busy    { background: var(--c-slot-busy-bg); }    /* я записан */

/* --- [SLOT FILL BAR → CAP CHIP] Индикатор заполнения — это САМ чип-счётчик: цифры лежат
   на заливке-«заряде» (.slot-count::after, см. [SLOT COUNT]). Ни полосы снизу, ни отдельной
   мини-полоски рядом с цифрами больше нет. Класс .has-fill и переменная --slot-fill ставятся
   JS как раньше (buildSlotCard) — логика не менялась. --- */

.slot-participant {
    display: flex;
    align-items: center;
    gap: 8px;
    padding-top: 8px;
    border-top: 1px solid rgba(128, 128, 128, 0.15);
    min-height: 44px;
    position: relative;
}

/* --- [SLOT PARTICIPANTS DIVIDER] Чёткая разделительная полоса МЕЖДУ участниками в развёрнутом
   слоте (селектор + срабатывает только когда участников несколько). Первый ряд сохраняет блёклую
   границу от строки слота; цвет — тематический токен (без хардкода). --- */
.slot-participant + .slot-participant {
    border-top-color: var(--th-divider, var(--c-hint));
}

.slot-avatar {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background-color: var(--th-avatar-bg, var(--c-accent));
    color: white;
    font-size: var(--fs-xs);
    font-weight: 600;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

/* --- [AVATARS задача 3] фото-аватар участника в записи слота --- */
.slot-avatar-img {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

/* --- [MULTI-ROT POSITION BADGE] Номер позиции участника при скользящей ротации --- */
/* Чистая цифра без фона: белая в тёмной теме, чёрная в светлой (через --tg-theme-text-color) */
.slot-position-badge {
    font-size: var(--fs-xs);
    font-weight: 700;
    line-height: 1;
    color: var(--tg-theme-text-color, #000);
    flex-shrink: 0;
    min-width: 14px;
    opacity: 0.6;
}

/* --- [ИНКОГНИТО — СТИЛИ] --- */
.slot-avatar-incognito {
    background-color: var(--tg-theme-hint-color, #8e8e93);
    font-size: var(--fs-sm);
}

.slot-participant-name {
    font-size: var(--fs-sm);
    color: var(--tg-theme-text-color, #000);
    flex: 1;
    min-width: 0;
}

.incognito-badge {
    font-size: var(--fs-2xs);
    opacity: 0.7;
}

/* --- [КОНТАКТ ИЗ ТЕЛЕФОНА] Запись контакта, который не является пользователем приложения,
   маркируется ИНАЧЕ, чем обычная (бриф). Переиспользуем токен «мягкого предупреждения»
   (--th-warn-soft-*, уже во всех 4 палитрах themes.css) вместо нового: тот же смысл — «не
   обычный случай, обрати внимание» — правило 2 CLAUDE.md (не изобретать компонент/токен,
   если есть подходящий готовый). --- */
.slot-participant-external {
    background: var(--th-warn-soft-bg, var(--c-warn-soft-bg));
    border-left: 3px solid var(--th-warn-soft-text, var(--c-warn-soft-text));
    padding-left: 6px;
    border-radius: var(--radius-xs);
}

/* --- [КОНТАКТ ИЗ ТЕЛЕФОНА] Звонилка в правом краю ячейки — тот же образец, что telBtn
   контактного телефона графика (app_schedule.js, кнопка «Позвонить» на синей полосе). --- */
.slot-contact-call-btn {
    background: none;
    border: none;
    font-size: var(--fs-md);
    cursor: pointer;
    color: var(--th-link-accent, var(--c-accent));
    opacity: 0.85;
    padding: 2px 6px;
    flex-shrink: 0;
}
.slot-contact-call-btn:hover   { opacity: 1; }
.slot-contact-call-btn:active  { transform: scale(0.94); }
.slot-contact-call-btn:focus-visible { outline: 2px solid var(--th-link-accent, var(--c-accent)); outline-offset: 1px; }

/* --- [REMINDER CONFIRMATION] Галочка подтверждения записи участником на полосе слота --- */
.slot-confirmed-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--c-success);
    color: var(--c-on-accent);
    font-size: var(--fs-xs);
    font-weight: 700;
    line-height: 1;
    margin-left: 4px;
    flex-shrink: 0;
}

.slot-part-menu-btn {
    background: none;
    border: none;
    font-size: var(--fs-xl);
    font-weight: 900;
    cursor: pointer;
    color: var(--tg-theme-text-color, #000);
    opacity: 0.55;
    padding: 2px 6px;
    line-height: 1;
    flex-shrink: 0;
    margin-left: auto;
    border-radius: var(--radius-sm);
    -webkit-tap-highlight-color: transparent;
    transition: opacity 0.15s;
}

.slot-part-menu-btn:active {
    opacity: 1;
}

/* --- [ДАННЫЕ УЧАСТНИКА — ЗНАЧОК] --- */
.slot-part-data-btn {
    background: none;
    border: none;
    font-size: var(--fs-md);
    cursor: pointer;
    padding: 2px 5px;
    margin-left: 2px;
    opacity: 0.65;
    flex-shrink: 0;
    -webkit-tap-highlight-color: transparent;
    transition: opacity 0.15s;
}

.slot-part-data-btn:active {
    opacity: 1;
}


/* --- [АКТИВНЫЕ ЗАПИСИ 2026-08-27] Кнопка-календарик со счётчиком на карточке участника
   и контакта (окно «Участники графика», раздел «Контакты», окно поиска). Декор взят у
   соседей той же строки — .slot-part-data-btn (прозрачный фон, тап-прозрачность) плюс
   акцентный цвет ссылок --th-link-accent, как у .slot-contact-call-btn: строка карточки
   уже имеет свой ритм, и новый компонент в ней быть НЕ должен (правило 2). --- */
.person-bookings-btn {
    background: none;
    border: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 2px 5px;
    margin-left: auto;
    flex-shrink: 0;
    color: var(--th-link-accent, var(--c-accent));
    font-size: var(--fs-md);
    font-family: inherit;
    opacity: 0.9;
    -webkit-tap-highlight-color: transparent;
    transition: opacity 0.15s;
}

.person-bookings-btn:hover,
.person-bookings-btn:focus-visible {
    opacity: 1;
}

.person-bookings-btn:active {
    opacity: 1;
    transform: scale(0.94);
}

.person-bookings-btn:disabled {
    opacity: 0.4;
    cursor: default;
}

.person-bookings-count {
    font-size: var(--fs-2xs);
    font-weight: 700;
    line-height: 1;
}

/* Стрелка «перейти» в строке списка записей человека */
.person-bookings-go {
    color: var(--th-link-accent, var(--c-accent));
    flex-shrink: 0;
    margin-left: auto;
    opacity: 0.7;
}

/* --- [АКТИВНЫЕ ЗАПИСИ] Подсветка карточки, к которой перенесли график по тапу на запись.
   Временная (снимается таймером в focusScheduleBooking): постоянная метка на чужой
   строке при следующем открытии графика читалась бы как состояние самой записи.
   Ореол --th-accent-glow уже используется для «подсвеченного пункта» — тот же смысл. --- */
.slot-participant--focus {
    background: var(--th-accent-glow, rgba(51, 144, 236, .18));
    border-radius: var(--radius-xs);
    box-shadow: 0 0 0 2px var(--th-link-accent, var(--c-accent));
    transition: background 0.2s ease, box-shadow 0.2s ease;
}

/* --- [СЛОТ — КНОПКА МЕНЮ АДМИНА] --- */
.slot-admin-menu-btn {
    background: none;
    border: none;
    font-size: var(--fs-lg);
    cursor: pointer;
    padding: 0 4px;
    opacity: 0.85;
    flex-shrink: 0;
    color: var(--tg-theme-text-color, #000);
    -webkit-tap-highlight-color: transparent;
    transition: opacity 0.15s;
}

.slot-admin-menu-btn:active {
    opacity: 1;
}

/* --- [УДАЛЕНИЕ ЗАПИСИ ИЗ СЛОТА — ЗНАЧОК] --- */
.slot-part-remove-btn {
    background: none;
    border: none;
    font-size: var(--fs-md);
    cursor: pointer;
    padding: 2px 5px;
    margin-left: 2px;
    opacity: 0.55;
    flex-shrink: 0;
    -webkit-tap-highlight-color: transparent;
    transition: opacity 0.15s;
}

.slot-part-remove-btn:active {
    opacity: 1;
}

/* --- [REVIEW REQUEST — ЗНАЧОК] Запрос отзыва у участника прошедшей записи (админ).
   Геометрия и поведение — канон .slot-part-remove-btn выше. Состояние «уже запрошено»
   выражено ТОЛЬКО прозрачностью (пользователь: «кнопка должна тускнеть и больше не
   нажиматься») — новых цветовых токенов не заводим, палитры тем не трогаем. --- */
.slot-review-req-btn {
    background: none;
    border: none;
    font-size: var(--fs-md);
    cursor: pointer;
    padding: 2px 5px;
    margin-left: 2px;
    opacity: 0.6;
    flex-shrink: 0;
    -webkit-tap-highlight-color: transparent;
    transition: opacity 0.15s;
}

.slot-review-req-btn:hover { opacity: 0.85; }
.slot-review-req-btn:active { opacity: 1; }
.slot-review-req-btn:focus-visible {
    outline: 2px solid var(--th-focus, var(--c-accent));
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

.slot-review-req-btn.is-done,
.slot-review-req-btn:disabled {
    opacity: 0.22;
    cursor: default;
    pointer-events: none;
}

/* --- [РОКИРОВКА — СТИЛИ] --- */
.swap-circle {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    border: 2px solid var(--th-brand, var(--c-brand));   /* фирменный голубой */
    flex-shrink: 0;
    margin-left: auto;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-sm);
    font-weight: 700;
    color: transparent;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
    -webkit-tap-highlight-color: transparent;
}

.swap-circle.is-selected {
    background: var(--th-avatar-bg, var(--c-accent));
    border-color: var(--th-avatar-bg, var(--c-accent));
    color: var(--tg-theme-button-text-color, #fff);
}

/* --- [РУЧНОЙ ОТБОР — КРУЖКИ] Минималистичный чекбокс выбора участника в очередь (Задача 3) --- */
.pick-circle {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    border: 2px solid var(--tg-theme-hint-color, #aaa);
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-sm);
    font-weight: 700;
    color: transparent;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
    -webkit-tap-highlight-color: transparent;
}

.pick-circle.is-selected {
    background: var(--th-avatar-bg, var(--c-accent));
    border-color: var(--th-avatar-bg, var(--c-accent));
    color: var(--tg-theme-button-text-color, #fff);
}

.swap-action-bar {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 9990;
    background: var(--tg-theme-bg-color, #fff);
    border-top: 1px solid rgba(128, 128, 128, 0.2);
    padding: 10px 14px calc(10px + env(safe-area-inset-bottom, 0px)) 14px;
    display: flex;
    align-items: center;
    gap: 10px;
    /* [ANTI-OVERFLOW] На широком рендере (браузер Windows) + длинных локализациях кнопки
       не помещаются в одну строку — разрешаем перенос, иначе уходят за правый край вьюпорта. */
    flex-wrap: wrap;
    box-shadow: 0 -3px 16px rgba(0, 0, 0, 0.12);
}

.swap-count {
    flex: 1;
    /* min-width:0 — лейбл может сжаться ниже своего min-content и отдать место кнопкам. */
    min-width: 0;
    font-size: var(--fs-sm);
    color: var(--tg-theme-text-color, #000);
    opacity: 0.7;
}

.swap-btn-cancel,
.swap-btn-ok {
    border: none;
    border-radius: var(--radius-md);
    /* [DS8] размеры/вес — на токены (высота компактного контрола вместо ad-hoc паддинга) */
    min-height: var(--control-h-compact);
    padding: 0 var(--space-4);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    /* Кнопки не сжимаются и текст не ломается; при нехватке ширины переносятся целиком (flex-wrap). */
    flex-shrink: 0;
    white-space: nowrap;
}

.swap-btn-cancel {
    background: var(--tg-theme-secondary-bg-color, #f2f2f7);
    color: var(--tg-theme-text-color, #000);
}

.swap-btn-ok {
    background: var(--th-btn-bg, var(--c-accent));   /* заливка кнопки — общий токен с .btn-primary */
    color: var(--tg-theme-button-text-color, #fff);
}

.swap-btn-ok:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.slot-part-dropdown {
    display: none;
    position: absolute;
    right: 0;
    top: 100%;
    z-index: 200;
    min-width: 200px;
    margin-top: 2px;
}

.time-badge {
    flex-shrink: 0;
    font-weight: bold;
    font-size: var(--fs-md);
    align-self: center;
    margin-right: 10px;
}

.chevron-btn {
    background: none;
    border: none;
    cursor: pointer;
    opacity: 0.6;
    flex-shrink: 0;
    -webkit-tap-highlight-color: transparent;
    outline: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
}

.chevron-btn::after {
    content: '';
    display: block;
    width: 8px;
    height: 8px;
    border-right: 2.5px solid currentColor;
    border-bottom: 2.5px solid currentColor;
    transform: rotate(-135deg) translateY(-1px);
    transition: transform 0.22s ease;
}

.chevron-btn.is-collapsed::after {
    transform: rotate(45deg) translateY(-1px);
}

.chevron-btn:active {
    opacity: 1;
}

.date-header .chevron-btn {
    opacity: 0.75;
    margin-right: 8px;
}
/* --- [DAY CHEVRON ALIGN] Только полоса даты (day-card): как .sa-chevron в слоте — чуть за
   внутренний край (margin-right:-4px) + 4px зазор от ⋮ → шеврон и ⋮ полосы даты встают в одну
   вертикаль с шевронами/⋮ слотов ниже. Прочие заголовки (today/admin/expired) не трогаем. --- */
.date-header.day-card .chevron-btn {
    margin-right: -4px;
    margin-left: 4px;
}

.date-header.today-marker .chevron-btn {
    opacity: 0.85;
}

.date-header.admin-only-marker .chevron-btn {
    opacity: 0.85;
}

.section-collapsed {
    display: none !important;
}

.btn {
    border: none;
    padding: 0 var(--space-4);
    min-height: var(--control-h);
    border-radius: var(--radius-sm);
    font-weight: var(--fw-bold);
    cursor: pointer;
    width: 100%;
    font-size: var(--fs-md);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
}

.btn-inline {
    width: auto;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.btn-primary {
    /* Заливка ОСНОВНОЙ кнопки — отдельный токен --th-btn-bg (разнесён с акцентом
       --c-accent, чтобы кнопки и активная вкладка красились независимо). Фолбэк на
       акцент → системная тема и старые палитры без переменной выглядят как раньше. */
    background-color: var(--th-btn-bg, var(--c-accent));
    color: var(--c-on-accent);
    margin-top: var(--space-4);
}

/* --- BTN STATES (DS7) --- */
/* Состояния кнопок: hover/active/disabled/focus/danger.
   Базовый .btn-secondary добавлен здесь (вне @media) — для Системной светлой темы.
   @media-блоки переопределяют его позже каскадом — это правильно. */
.btn-primary:hover { filter: brightness(1.06); }
.btn-primary:active { filter: brightness(0.94); transform: scale(0.99); }
.btn:disabled, .btn.btn-loading:disabled { opacity: .5; cursor: default; pointer-events: none; }
.btn:focus-visible { outline: 2px solid var(--th-focus, var(--c-accent)); outline-offset: 2px; }
/* [AUDIT 2026-08-27] .btn-other заявлен каноническим (CLAUDE.md), но правила не имел вообще:
   6 употреблений рендерились дефолтной кнопкой браузера поверх любой темы. По смыслу это
   вторичная кнопка — делим правило с .btn-secondary (и в [SKIN] themes.css). */
.btn-secondary,
.btn-other {
    background: var(--th-btn2-bg, var(--c-surface));
    color: var(--c-text);
    border: 1px solid var(--th-btn2-border, var(--c-hint));
}
.btn-danger { background: var(--c-danger); color: var(--c-on-accent); }
.btn-danger:hover { filter: brightness(1.06); }
.btn-danger:active { filter: brightness(0.94); transform: scale(0.99); }

/* --- [SLOT BTN] Статус слота несёт его ФОН. Кнопка действия лишь слегка выделяется на нём:
   компактная, со своим приглушённым фоном (чуть ТЕМНЕЕ слота в светлой теме / чуть СВЕТЛЕЕ в
   тёмной) + тонкий контур. Текст — цвета темы. Счётчик мест в кнопку НЕ входит (см. .slot-count).
   Ширина — flex-grow (см. [SLOT STRIP]): кнопка занимает всю свободную часть строки слота. */
.btn-slot-action {
    min-height: var(--control-h-compact);
    padding: 0 12px;
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--tg-theme-text-color);
    border-radius: var(--radius-sm);
}
.btn-free {
    background-color: var(--th-slot-btn-free-bg, var(--c-slot-btn-free-bg));
    border: 1px solid var(--th-slot-btn-free-border, var(--c-slot-btn-free-border));
}
.btn-busy {
    background-color: var(--th-slot-btn-busy-bg, var(--c-slot-btn-busy-bg));
    border: 1px solid var(--th-slot-btn-busy-border, var(--c-slot-btn-busy-border));
    /* [DS9 W2] контурная кнопка отмены — текст цветом статуса «я записан» */
    color: var(--th-slot-acc-busy, var(--c-slot-acc-busy));
}
.btn-slot-action:hover { filter: brightness(1.05); }
.btn-slot-action:active { filter: brightness(0.93); transform: scale(0.98); }

/* --- [SLOT LABEL PLAIN] Когда действие недоступно (Занято / Закрыто / истёкший / только админ) —
   это НЕ кнопка, а просто надпись: без фона, рамки и тени, приглушённым цветом. --- */
.btn-slot-plain {
    min-height: auto;
    padding: 0;
    background: none !important;
    border: none !important;
    box-shadow: none !important;
    color: var(--tg-theme-hint-color);
    font-weight: var(--fw-semibold);
    cursor: default;
}

/* --- [SLOT COUNT → CAP CHIP] Счётчик «занято/всего» — сам себе индикатор заполненности,
   по принципу индикатора заряда батареи: корпус-чип = дорожка (--*-slot-fill-track),
   внутри «заряд» (::after, ширина = --slot-fill, цвет = статус-полоска --st-strip),
   цифры лежат ПОВЕРХ заливки. Отдельной полоски-прогресса рядом с цифрами больше нет —
   освободившаяся ширина отдана кнопке действия (.btn-slot-action, flex-grow).
   Живёт в ячейке .sa-count правого кластера (ширина auto). --- */
.slot-count {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    padding: 3px 9px;
    border-radius: var(--radius-xs);
    background: var(--th-slot-fill-track, var(--c-slot-fill-track));   /* «корпус» = пустая часть */
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    font-variant-numeric: tabular-nums;
    /* [AUDIT 2026-08-27] Основной текст, а не hint: у чипа своя дорожка (--th-slot-fill-track)
       и «заряд» поверх неё, на них серый давал 2.92:1, а поверх заливки — 2.01:1. */
    color: var(--tg-theme-text-color);
    white-space: nowrap;
    /* z-index:0 создаёт stacking context — без него ::after с z-index:-1 уехал бы ЗА фон чипа
       и заливка пропала бы совсем. overflow обрезает «заряд» по форме чипа: своего
       border-radius у ::after нет, скругление он наследует отсюда. */
    position: relative;
    z-index: 0;
    overflow: hidden;
}
/* «Заряд» — только когда JS повесил .has-fill (не у истёкших/замороженных/админ).
   z-index:-1 держит заливку НАД фоном чипа, но ПОД цифрами — иначе цифры замыливаются. */
.slot-container.has-fill .slot-count::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    width: var(--slot-fill, 0%);
    background: var(--st-strip);
    opacity: 0.28;
    pointer-events: none;
}
/* Счётчик-подсказка при >9: метка «9+», кликабельна и открывает попап с реальным числом. */
.slot-count.slot-count-hint {
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
.slot-count.slot-count-hint:active { opacity: 0.6; }

/* --- [HOME HEADER] Логотип + бренд --- */
.home-header {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    margin-bottom: var(--space-4);
    margin-top: var(--space-1);
}

.home-logo {
    /* [DS9 W4] Компактный логотип 38px — бренд несёт сам знак, не размер (прототип) */
    width: 38px;
    height: 38px;
    border-radius: var(--radius-sm);
    flex-shrink: 0;
    display: block;
}

.home-brand {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 2px;
}

.home-brand-name {
    /* [DS9 W4] Вордмарк цветом текста — синий бренд-акцент несёт логотип, не подпись */
    font-size: var(--fs-lg);
    font-weight: var(--fw-semibold);
    line-height: 1.1;
    color: var(--tg-theme-text-color);
    letter-spacing: -0.3px;
}

/* [DS9 W5] .home-brand-url удалён — разметки под него нет (мёртвый класс) */

/* --- [USER AVATAR] Кнопка аватара справа в шапке + выпадающее меню --- */
.user-avatar-wrap {
    margin-left: auto;
    position: relative;
    flex-shrink: 0;
}

.user-avatar-btn {
    width: 38px;
    height: 38px;
    border-radius: 50%;
    border: 2px solid transparent;
    background: transparent;
    cursor: pointer;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: border-color 0.18s;
    -webkit-tap-highlight-color: transparent;
}

.user-avatar-btn:hover,
.user-avatar-btn:active {
    border-color: var(--th-avatar-bg, var(--c-accent));
}

.user-avatar-inner {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    overflow: hidden;
    background: var(--th-avatar-bg, var(--c-accent));
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-sm);
    font-weight: 700;
    color: var(--c-on-accent);
    flex-shrink: 0;
}

.user-avatar-inner img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* --- [GUEST LOGIN BTN, DS9 W4] Гость: акцентная кнопка «Войти» на месте аватара.
   Классы вместо инлайн-стилей (app_navigation.js::initUserAvatar). Единственная
   акцентная заливка на экране гостя — CTA входа (прототип DS9, секция «Главная»). --- */
.user-avatar-btn.is-login {
    width: auto;
    height: auto;
    padding: 7px 14px;
    border: none;
    border-radius: var(--radius-sm);
    background: var(--th-btn-bg, var(--c-accent));
    transition: opacity 0.15s;
}
.user-avatar-btn.is-login:hover,
.user-avatar-btn.is-login:active {
    border-color: transparent;
    opacity: 0.85;
}
.user-avatar-btn.is-login .user-avatar-inner {
    width: auto;
    height: auto;
    padding: 0;
    background: none;
    border-radius: 0;
    overflow: visible;
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--c-on-accent);
    white-space: nowrap;
}

.user-menu-dropdown {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    /* [DS9 W3] Меню = surface-3 + рамка border-subtle + тень-2 (как .dropdown-menu) */
    background: var(--th-surface-3, var(--c-surface-3));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    border-radius: var(--radius-md);
    box-shadow: var(--th-shadow-2, var(--shadow-2));
    padding: 6px;
    min-width: 190px;
    z-index: 500;
    overflow: hidden;
    animation: _umFadeIn 0.15s ease;
}

@keyframes _umFadeIn {
    from {
        opacity: 0;
        transform: translateY(-6px) scale(0.97);
    }

    to {
        opacity: 1;
        transform: translateY(0) scale(1);
    }
}

.user-menu-name {
    padding: 12px 16px 8px;
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--tg-theme-hint-color, #8e8e93);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 190px;
}

.user-menu-divider {
    height: 1px;
    background: rgba(120, 120, 128, 0.2);
    margin: 2px 8px;   /* [DS9 W3] инсет под скруглённые пункты */
}

.user-menu-item {
    display: flex;
    align-items: center;
    width: 100%;
    padding: 11px 12px;
    background: none;
    border: none;
    border-radius: var(--radius-sm);   /* [DS9 W3] скруглённый пункт внутри padding-контейнера */
    color: var(--tg-theme-text-color, #000);
    font-size: var(--fs-md);
    text-align: left;
    cursor: pointer;
    text-decoration: none;
    transition: background 0.1s;
    -webkit-tap-highlight-color: transparent;
}

.user-menu-item:hover {
    background: rgba(120, 120, 128, 0.12);
}

.user-menu-logout {
    color: var(--c-danger);
}

/* --- [HOME STICKY TOP] Неподвижный блок: шапка + описание + «Мои записи» --- */
.home-sticky-top {
    position: sticky;
    top: 0;
    z-index: 55;
    background: var(--tg-theme-bg-color, #fff);
    /* Компенсируем боковые отступы body чтобы фон прокрашивался до краёв */
    margin-left: -15px;
    margin-right: -15px;
    padding-left: 15px;
    padding-right: 15px;
    padding-bottom: 0;
}

/* --- [HOME DESC BLOCK] Описание + «Подробнее» на той же строке --- */
.home-desc-block {
    position: relative;
    margin-bottom: 2px;
}
/* --- [HOME DESC] Краткое описание --- */
.home-desc {
    font-size: var(--fs-sm);
    line-height: 1.5;
    color: var(--tg-theme-hint-color, #8e8e93);
    margin: 0;
    padding: 0;
}
.home-desc-clamped {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    /* Резервируем место в конце 3-й строки для кнопки «Подробнее» */
    padding-right: 72px;
}
.home-more-btn {
    position: absolute;
    bottom: 0;
    right: 0;
    background: none;
    border: none;
    padding: 0;
    color: var(--th-link-accent, var(--tg-theme-button-color, #2ea6ff));
    font-size: var(--fs-sm);
    line-height: 1.5;
    cursor: pointer;
    font-family: inherit;
}

/* --- [HOME SECTION HEADER] «Мои записи» + ссылка «Все мои записи» (одна строка, DS9 W4) --- */
.home-section-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-2);
    padding: 10px 0 4px;
}
.home-section-title {
    /* [DS9 W4] Секционный заголовок = тихий eyebrow (капс вторичным цветом), не конкурирует с CTA */
    margin: 0;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    color: var(--tg-theme-hint-color, #8e8e93);
    text-transform: uppercase;
    letter-spacing: 0.08em;
}
.home-section-divider {
    height: 1px;
    background: var(--tg-theme-secondary-bg-color, #efeff4);
    margin: 14px 0 8px;
}

/* ============================================================================
   --- [HOME PRESENTATION / PITCH] Онбординг-презентация: встроенная панель со СВОИМ
   скроллом (страница/шапка не двигаются). Тот же контент (renderPitchHTML) рендерится
   и на странице «О приложении» (#about-pitch), но там скроллится сама страница.
   ============================================================================ */
.home-presentation {
    position: relative;
    display: flex;
    flex-direction: column;
    min-height: 0;
}
.presentation-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    /* Фолбэк для обычного потока (браузер без таб-бара): внутренний скролл, а не у всей
       страницы. В flex-колонке (body.has-tab-bar) высоту даёт родитель — см. ниже. */
    max-height: calc(100vh - 120px);
    max-height: calc(100dvh - 120px);
    padding: 2px 0 16px;
    scroll-snap-type: y proximity;
}
/* Закрыть (✕) — только авторизованным; абсолют поверх контента панели. */
.presentation-close-btn {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 5;
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--tg-theme-bg-color, #fff);
    border: 1px solid var(--tg-theme-secondary-bg-color, #efeff4);
    border-radius: 50%;
    color: var(--tg-theme-text-color, #1c1c1e);
    font-size: var(--fs-md);
    line-height: 1;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    box-shadow: var(--th-shadow-1, var(--shadow-1));   /* [DS9 W4] ступень-1 вместо литерала */
}
.presentation-close-btn:hover { opacity: 0.7; }

/* --- Питч (слайды): hero + карточки. Используется и в #presentation-scroll, и в #about-pitch ---
   [DS9 W4] Hero = нейтральная карточка surface-1, выравнивание влево, без градиента и лого
   (лого несёт шапка главной). Разметка — renderPitchHTML (app_navigation.js). --- */
.pitch-hero {
    display: flex;
    flex-direction: row;
    align-items: flex-end;
    text-align: left;
    gap: var(--space-3);
    padding: 18px 16px;
    margin-bottom: 16px;
    border-radius: var(--radius-lg);
    background: var(--th-surface-1, var(--c-surface-1));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
    scroll-snap-align: start;
}
.pitch-hero-title {
    font-size: var(--fs-lg);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-tight);
    color: var(--tg-theme-text-color, #000);
}
.pitch-hero-sub {
    margin-top: 6px;
    font-size: var(--fs-sm);
    line-height: var(--lh-normal);
    color: var(--tg-theme-hint-color, #8e8e93);
    /* max-width:34ch снят: ширину строки теперь задаёт колонка .pitch-hero-copy */
}
/* Hero стал двухколоночным: текст + та же фигура, что в карточке CTA (стили <img> —
   в секции [HOME CTA]). */
.pitch-hero-copy {
    flex: 1 1 auto;
    min-width: 0;
}
.pitch-hero-art {
    flex: 0 0 auto;
    width: 96px;
    line-height: 0;
}
/* Питч рендерится в ДВА контейнера (renderPitchHTML → #presentation-scroll и #about-pitch).
   На главной презентация видна одновременно с карточкой #home-cta, где фигура уже есть, —
   два одинаковых персонажа на одном экране. Поэтому в презентации фигуру гасим: в питче
   она живёт только на странице «О приложении». */
#presentation-scroll .pitch-hero-art { display: none; }
/* .pitch-card наследует фон/радиус/паддинги .about-section; добавляем только «слайдовый» снап. */
.pitch-card { scroll-snap-align: start; }

/* В flex-колонке главной (body.has-tab-bar) панель занимает зону контента вместо «Моих записей»;
   высота приходит от родителя → фолбэк max-height у скролла снимаем. */
body.has-tab-bar #home-view:not(.hidden) .home-presentation {
    flex: 1 1 auto;
    min-height: 0;
}
body.has-tab-bar .presentation-scroll { max-height: none; }
/* --- [MY-EVENTS SUBSCRIPTIONS] Секция подписок в табе Графики ---
   [DS9 W4] Единый секционный eyebrow (как .home-section-title / .about-section-title) */
.my-events-subs-header {
    padding: 4px 0 8px;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    color: var(--tg-theme-hint-color, #8e8e93);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    margin: 0;
}

/* --- [HOME GRID] Меню --- */
.home-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 15px;
    margin-top: 15px;
}

/* --- [ANDROID INSTALL STRIP] Кнопка установки (браузер) --- */
.android-install-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    padding: 0;
    background: var(--tg-theme-secondary-bg-color, #efeff4);
    color: var(--tg-theme-hint-color, #8e8e93);
    border: none;
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: opacity 0.15s;
}
.android-install-btn:active { opacity: 0.6; }

/* --- [HOME SHARE BTN S23] Иконка «Поделиться» слева от APK-кнопки на главной.
   Тот же визуальный стиль, что у .android-install-btn — единая палитра home-strip.
   .home-tg-btn (вход в мини-приложение через Telegram) стоит левее и делит те же правила:
   полоса должна читаться одним рядом одинаковых кнопок. --- */
.home-share-btn,
.home-tg-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    padding: 0;
    margin-right: 8px;
    background: var(--tg-theme-secondary-bg-color, #efeff4);
    color: var(--tg-theme-hint-color, #8e8e93);
    border: none;
    border-radius: var(--radius-md);
    cursor: pointer;
    vertical-align: middle;
    transition: opacity 0.15s;
}
.home-share-btn:active,
.home-tg-btn:active { opacity: 0.6; }

/* --- [APK UPDATE DOT S23] Красная лампочка на иконке скачивания: подсветка наличия
   обновлённого билда (server mtime > localStorage.apk_last_downloaded_mtime). --- */
.android-install-btn { position: relative; vertical-align: middle; }
.apk-update-dot {
    position: absolute;
    top: 4px;
    right: 4px;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--c-danger);
    border: 2px solid var(--tg-theme-secondary-bg-color, #efeff4);
    box-sizing: content-box;
    box-shadow: 0 0 0 1px var(--th-border-subtle, var(--c-border-subtle));   /* [DS9 W5] хайрлайн из токена */
    pointer-events: none;
}
.apk-update-dot[hidden] { display: none; }

.android-install-qr {
    width: 180px;
    height: 180px;
    border-radius: var(--radius-sm);
    display: block;
    margin: 0 auto 8px;
    background: #fff;
}

.android-install-apk-btn {
    display: block;
    width: 100%;
    box-sizing: border-box;
    text-decoration: none;
    text-align: center;
    margin: 0 0 10px;
}

.android-install-close-btn {
    background: var(--tg-theme-secondary-bg-color, #efeff4);
    color: var(--tg-theme-text-color, #000);
    width: 100%;
    margin: 0;
}

/* --- [ABOUT VIEW] Страница «О приложении» --- */
.about-body {
    padding-bottom: 20px;
}

.about-logo-block {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-bottom: 20px;
    padding: 16px;
    /* [DS9 W4] = поверхность карточки (как .about-section) */
    background: var(--th-surface-1, var(--c-surface-1));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
    border-radius: var(--radius-lg);
}

.about-logo {
    width: 54px;
    height: 54px;
    border-radius: var(--radius-lg);
    flex-shrink: 0;
    display: block;
}

.about-section {
    margin-bottom: 16px;
    padding: 14px 16px;
    /* [DS9 W4] Карточка контента = surface-1 + рамка + тень-1 (как .event-card) */
    background: var(--th-surface-1, var(--c-surface-1));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
    border-radius: var(--radius-lg);
}

.about-section-title {
    /* [DS9 W4] Заголовок секции = тихий eyebrow вторичным цветом (единый с .home-section-title) */
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    color: var(--tg-theme-hint-color, #8e8e93);
    margin-bottom: 8px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.about-text {
    font-size: var(--fs-md);
    line-height: 1.55;
    color: var(--tg-theme-text-color, #000);
    margin: 0;
}

.about-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    /* C.4 Фикс: контейнер не должен расширяться за пределы карточки */
    min-width: 0;
    overflow: hidden;
}

.about-list-item {
    font-size: var(--fs-sm);
    line-height: 1.45;
    color: var(--tg-theme-text-color, #000);
    /* C.4 Фикс переполнения: текст переносится, иконка не «выпрыгивает» за карточку */
    overflow-wrap: break-word;
    word-break: break-word;
    min-width: 0;
}

.about-steps {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.about-step {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    font-size: var(--fs-sm);
    line-height: 1.45;
    color: var(--tg-theme-text-color, #000);
}

.about-step-num {
    flex-shrink: 0;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: var(--th-chip, var(--c-accent));
    color: var(--c-on-accent);
    font-size: var(--fs-sm);
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 1px;
}

@media (prefers-color-scheme: dark) {
    .home-brand-name {
        color: var(--c-brand);
    }
}

.menu-card {
    background-color: var(--tg-theme-secondary-bg-color, #efeff4);
    border-radius: var(--radius-lg);
    padding: 20px 10px;
    text-align: center;
    cursor: pointer;
    box-shadow: var(--th-shadow-1, var(--shadow-1));   /* [DS9 W5] ступень-1 вместо старой шкалы */
    border: 1px solid var(--th-card-border, var(--c-border-subtle));   /* [DS9 W5] рамка из базы (легаси-блоки сжаты) */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 100px;
}

.menu-card:active {
    transform: scale(0.96);
    background-color: var(--th-menu-active, var(--c-accent));
    color: var(--tg-theme-button-text-color, #ffffff);
}

/* --- [ICON SVG] Штриховые иконки (Lucide), DS1 ----------------------------- */
/* {icon:NAME} → <span class="icn"><svg…stroke="currentColor"></span> (см. app_i18n
   injectIconsInTextNode). Размер наследуется от font-size родителя (1em), цвет —
   от currentColor (тема/акцент/белый на цветных кнопках слотов). */
.icn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    vertical-align: -0.15em;
}
.icn svg {
    width: 1em;
    height: 1em;
    display: block;
}
/* [DS] Иконка слева от подписи (пункты меню, кнопки с иконкой+текстом) — отступ между
   иконкой и текстом. Класс icn-lead навешивает injectIconsInTextNode только когда за иконкой
   идёт текст, поэтому иконки-одиночки (напр. «назад», ⋮) отступа НЕ получают. */
.icn-lead {
    margin-inline-end: 0.4em;
}

.menu-icon {
    font-size: 32px;
    margin-bottom: 10px;
}
/* Иконка карточки меню крупнее текста — фиксируем явный размер SVG. */
.menu-icon .icn svg {
    width: 30px;
    height: 30px;
}

.menu-text {
    font-size: var(--fs-sm);
    font-weight: 600;
    color: inherit;
}

.event-card {
    /* [DS9 W4] Карточка списка = surface-1 + рамка + тень-1 (нейтральная поверхность, эталон прототипа) */
    background: var(--th-surface-1, var(--c-surface-1));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
    padding: var(--space-4);           /* [DS8] 15px → шкала отступов */
    border-radius: var(--radius-md);
    margin-bottom: var(--space-3);     /* [DS8] единый ритм списков карточек */
    cursor: pointer;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
    position: relative;
    transition: background-color 0.1s;
}

.event-card:active {
    background-color: var(--th-surface-2, var(--c-surface-2));
}

/* [DS9 W4] Недоступный график (приватный без подписки) — класс вместо инлайн-стиля */
.event-card--locked {
    cursor: not-allowed;
    opacity: 0.7;
}

/* --- [CARD 3 ROWS 2026-08-14: EVENT CARD LAYOUT] Контент карточки = ТРИ ряда в колонку.
   Отдельной колонки бейджей справа больше нет: рейтинг и значки статуса переехали в конец
   второго ряда, значок видимости — в конец третьего. Смысл — отдать первый ряд названию
   целиком (требование владельца), чтобы оно обрезалось как можно позже.
   ⚠ `align-items: center` у самого .event-card НЕ трогаем: этот класс — база у
   .booking-card (карточки записей на главной), смена выравнивания уехала бы туда. --- */
.event-card-main {
    flex: 1;
    min-width: 0;
    margin: 0 0 0 var(--space-3);
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.event-card-row {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}
/* Правый край рядов держим на margin-left:auto, а НЕ на justify-content: space-between:
   когда левая часть ряда пуста (у графика нет свободного времени или не указан город),
   space-between прижал бы значки к ЛЕВОМУ краю. */
.event-card-row > .event-card-badges,
.event-card-row > .event-card-vis {
    margin-left: auto;
}

/* Значок раздела перед названием (первый ряд). Здесь ЭМОДЗИ, а не штриховая иконка из
   ICONS_SVG: раздел уже цветной сам по себе в списке разделов (внутри <option> разметка
   не рендерится, и там всегда эмодзи), и карточка обязана совпадать с ним один в один.
   Поэтому ни color, ни палитры: цвет несёт сам глиф. line-height жёсткий — эмодзи выше
   строчных букв и без него растягивал бы первый ряд. */
.event-card-cat {
    flex-shrink: 0;
    font-size: var(--fs-sm);
    line-height: 1;
}

.event-card-title {
    flex: 1 1 auto;
    min-width: 0;
    font-weight: var(--fw-semibold);
    color: var(--tg-theme-text-color);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.event-card-badges {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    color: var(--tg-theme-hint-color, #8e8e93);   /* служебный слой — тихие иконки */
}

/* Иконка видимости (глобус/замок) — правый край третьего ряда.
   [2026-08-25, повторный проход] Раньше рендерилась ЭМОДЗИ (window.ICONS.language/
   lock_closed) ради «естественного» цвета символа — но цвет эмодзи задаёт шрифт ОС, а не
   CSS: тему игнорировал целиком, и на тёмном фоне выходило слишком контрастно, без
   способа приглушить. Вернули штриховую SVG ({icon:…}, currentColor, app_events.js) +
   свой приглушённый акцент на статус через палитру (--th-icon-public/-private,
   themes.css, все 4 темы) — так же, как уже тонированы .sched-badge--sub/-mod-pending
   ниже, только для НОВОЙ пары значков, а не одного общего hint-цвета. */
.event-card-vis {
    display: inline-flex;
    flex-shrink: 0;
}
.event-card-vis--public  { color: var(--th-icon-public,  var(--c-icon-public)); }
.event-card-vis--private { color: var(--th-icon-private, var(--c-icon-private)); }

.event-info {
    flex-grow: 1;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.event-title {
    font-weight: 600;
    font-size: var(--fs-md);
    margin-bottom: 5px;
    color: var(--tg-theme-text-color);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.event-desc {
    font-size: var(--fs-sm);
    color: var(--tg-theme-hint-color);
    word-break: break-word;
}

/* --- [СЛЕЖЕНИЕ ЗА СЛОТАМИ] --- */
.watch-badge {
    font-size: var(--fs-sm);
    margin-left: 4px;
}

/* --- Значки «Мой» / «Подписан» на карточке графика ---
   [DS9 W4] Служебный слой: тихие иконки цветом hint (наследуют .event-card-badges),
   без собственных цветов — не спорят с контентом (принцип 3 прототипа). */
/* [2026-08-24/25] Базовый цвет — accent, им остаётся ТОЛЬКО .watch-badge (колокол
   слежения, всё ещё {icon:bell}-SVG без своего модификатора). Остальные .sched-badge--*
   переопределяют цвет точечно (тот же порядок каскада, что у --sub/--pending/
   --rejected/--mod-pending). */
.sched-badge {
    font-size: 15px;
    line-height: 1;
    cursor: default;
    flex-shrink: 0;
    -webkit-tap-highlight-color: transparent;
    color: var(--c-accent);
}

/* [2026-08-24] Статус заявки на подписку («Мои графики» → подписки) — сознательно НЕ
   тихий, в отличие от .sched-badge выше: в отличие от «Мой»/«Подписан» (нейтральная
   информация), «на рассмотрении»/«отклонено» — то, ради чего владелец вообще открыл
   этот список, тот же принцип, что у .mod-item-date--warn/--danger в очереди модерации. */
.sched-badge--pending  { color: var(--th-warning, var(--c-warning)); }
.sched-badge--rejected { color: var(--c-danger); }

/* [2026-08-24] Остальные значки статуса графика в карточках — тоже цветные по смыслу
   (запрос владельца). «Подписан» — подтверждённое, позитивное состояние → success.
   «На модерации» (карточка СВОЕГО графика, «Мои графики») → тот же warning, что у
   pending выше. Значок «ближайшее свободное время сегодня» (.event-card-next--today)
   НЕ трогаем — он уже зелёный и работает штатно. */
.sched-badge--sub         { color: var(--c-success); }
.sched-badge--mod-pending { color: var(--th-warning, var(--c-warning)); }
/* [2026-08-25, повторный проход] Корона «Мой график» вернулась на штриховую SVG
   ({icon:crown}) — раньше рендерилась эмодзи ради «естественного золотого», но это тот же
   контраст/игнор-темы, что у глобуса/замка выше. Свой приглушённый акцент — через палитру. */
.sched-badge--mine        { color: var(--th-icon-mine, var(--c-icon-mine)); }

/* Отступ сверху перед НОВОЙ секцией статусов внутри одного списка подписок —
   .contact-group-header сам по себе рассчитан на первую позицию в своём списке. */
.sub-status-header {
    margin-top: var(--space-4);
}

/* .event-menu-btn удалён (аудит 2026-08-11, находка E6): кнопки контекстного меню
   карточки в разметке нет, вместе с ней снят и её обработчик toggleEventMenu. */

/* --- [BOT OWNER] Кнопка удаления графика в списке «Все графики» --- */
/* [DS9] Минимализм: тихая icon-кнопка без контура; деструктив несёт цвет текста,
   ховер — лёгкий danger-фон вместо сплошной заливки. */
.owner-delete-btn {
    flex-shrink: 0;
    margin-left: 10px;
    padding: 4px 8px;
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    line-height: 1;
    color: var(--c-danger);
    background: transparent;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    outline: none;
    transition: background 0.15s;
}

.owner-delete-btn:hover {
    background: var(--th-danger-glow, var(--c-danger-glow));
}

.owner-delete-btn:active {
    opacity: 0.7;
}

.dropdown-menu {
    position: absolute;
    right: 15px;
    top: 45px;
    /* [DS9 W3] Дропдаун = surface-3 + рамка border-subtle + тень-2 (эталон прототипа) */
    background: var(--th-surface-3, var(--c-surface-3));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    border-radius: var(--radius-md);
    box-shadow: var(--th-shadow-2, var(--shadow-2));
    padding: 6px;
    z-index: 100;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    min-width: 180px;
}

.dropdown-item {
    padding: var(--space-3);
    border: none;
    background: transparent;
    text-align: left;
    font-size: var(--fs-sm);
    cursor: pointer;
    color: var(--tg-theme-text-color, #000);
    width: 100%;
    box-sizing: border-box;
    /* [DS9 W3] Разделители-подчёркивания убраны — скруглённые пункты внутри
       контейнера с padding (паттерн прототипа DS9) */
    border-radius: var(--radius-sm);
    font-family: inherit;
    outline: none;
}

.dropdown-item:active {
    background-color: var(--tg-theme-secondary-bg-color, #efeff4);
}
/* [DS9 W5] Hover пункта в режиме «Системная» (раньше жил в легаси-блоке тёмной);
   темы перекрывают через [SKIN] (--th-hover). */
.dropdown-item:hover {
    background-color: var(--c-surface-2);
}

/* --- [LINK TYPE MENU] Голубая окантовка пунктов меню выбора типа ссылки (Поделиться/QR) --- */
#link-type-dropdown-portal {
    padding: 6px;
}
#link-type-dropdown-portal .dropdown-item {
    border: 1.5px solid var(--th-brand, var(--c-brand));   /* фирменный голубой */
    border-radius: var(--radius-sm);
    width: 100%;
    box-sizing: border-box;
}
#link-type-dropdown-portal .dropdown-item + .dropdown-item {
    margin-top: 6px;   /* зазор между пилюлями, чтобы окантовка читалась со всех сторон */
}
#link-type-dropdown-portal .dropdown-item:hover {
    border-color: var(--th-menu-active, var(--c-accent));
    background: var(--th-accent-glow, var(--c-accent-glow));
}

.dropdown-item.danger {
    color: var(--c-danger);
}

/* --- [CATEGORIES DS10] Компактный список разделов графика — miniature-вариант канона
   .dropdown-item (уже, meньше отступы) + "выбрано" рамкой/glow, БЕЗ отдельного значка-
   галочки (тот же приём, что #link-type-dropdown-portal выше и .settings-picker-item.is-active). --- */
.cat-dropdown-list .dropdown-item {
    padding: var(--space-2) var(--space-3);
    font-size: var(--fs-sm);
    border: 1.5px solid transparent;   /* резерв места — пункт не «прыгает» при выборе */
}
.cat-dropdown-list .dropdown-item.is-selected {
    border-color: var(--th-menu-active, var(--c-accent));
    background: var(--th-accent-glow, var(--c-accent-glow));
}

.modal-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    /* [DS9 W3] Единая подложка модалок — токен overlay (тёмная: .65, светлая: .50) */
    background: var(--th-overlay, var(--c-overlay));
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
}

.modal-content {
    /* [DS9 W3] Диалог = верхняя ступень elevation: surface-3 + shadow-3 */
    background: var(--th-surface-3, var(--c-surface-3));
    width: 90%;
    max-width: 400px;
    /* [FIX] Без ограничения высоты длинная форма (напр. #edit-modal с блоком локации)
       выходит за границы вьюпорта поровну вверх/вниз (overlay центрирует flex'ом), и
       .modal-actions с «Сохранить»/«Отмена» уезжает за нижний край недостижимо — у
       overlay нет своего скролла. Кап + внутренний скролл держат кнопки в пределах окна. */
    max-height: 90vh;
    max-height: 90dvh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: var(--space-5);
    border-radius: var(--radius-lg);
    box-shadow: var(--th-shadow-3, var(--shadow-3));
    box-sizing: border-box;
    animation: _modalIn 0.16s ease;
}

/* [DS9 W5] Ширины диалогов сведены к ДВУМ значениям: база 400px (выше) и компакт 320px
   (= .tg-dialog / .hint-popup). Инлайновые max-width 300/320/340/380 в index.html заменены
   этими классами — новые модалки НЕ задают ширину инлайном. */
.modal-content--compact { max-width: 320px; }
.modal-content--centered { text-align: center; }

/* --- [POST-CREATE INFO] Сводка после создания графика (#post-create-info-modal) ---
   Было: сплошные абзацы hint-цветом, последний — одной строкой с «• »-буллетами внутри.
   Владелец (2026-08-25): «мрачно». Стало: у каждой подсказки СВОЙ значок в мягкой плашке
   и свой вертикальный ритм, текст — основным цветом (hint остаётся только вводной строке).
   Разметку собирает app_schedule.js::showPostCreateInfoModal. */
.post-create-body {
    text-align: left;
    font-size: var(--fs-sm);
    line-height: 1.45;
    color: var(--tg-theme-text-color, var(--c-text));
    margin: 0 0 var(--space-4);
}

.post-create-intro {
    margin: 0 0 var(--space-4);
    color: var(--tg-theme-hint-color, var(--c-hint));
}

.post-create-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);          /* тот самый «отступ между строками» */
    margin: 0 0 var(--space-4);
}

.post-create-item {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
}

/* [2026-08-25, второй проход] Плашка-подложка под значком убрана: владелец назвал её
   «огранкой не в тему», и на светлой модалке она к тому же гасила сам значок. Осталась
   только фиксированная ширина — она держит левый край текста ровным, когда строка
   переносится. Высота = строке текста (14px × 1.45), поэтому значок встаёт вровень с
   ПЕРВОЙ строкой, а не по центру абзаца. */
.post-create-item-icon {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 20px;
    font-size: 18px;              /* .icn svg наследует размер от font-size — значок 18px */
    line-height: 1;
    color: var(--th-icon-public, var(--c-icon-public));
}

/* Цвет значка несёт смысл, а не украшает: оранжевый — ждём (график на модерации),
   синий (по умолчанию) — кто увидит график и кого пускаем, зелёный — работает на вас. */
.post-create-item--wait .post-create-item-icon { color: var(--th-warning, var(--c-warning)); }
.post-create-item--go   .post-create-item-icon { color: var(--th-success, var(--c-success)); }

.post-create-item-text {
    flex: 1;
    min-width: 0;
}

/* Сноска под списком возможностей: единственное, чего список о себе не говорит, — ГДЕ их
   включать. Заголовка над списком нет намеренно (владелец: «раздел лишний»). */
.post-create-note {
    margin: 0;
    font-size: var(--fs-2xs);
    color: var(--tg-theme-hint-color, var(--c-hint));
}

/* Подписи «Настройки» и «Участники» в сноске — не текст, а ссылки в те же окна.
   Кнопка, раздетая до текста: семантика нажатия и клавиатура достаются даром, а выглядит
   она частью предложения. Канон цвета — тот же --th-link-accent, что у .desc-more-btn. */
.post-create-note-link {
    display: inline;
    margin: 0;
    padding: 0;
    background: none;
    border: none;
    font: inherit;
    color: var(--th-link-accent, var(--c-accent));
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.post-create-note-link:hover  { opacity: .85; }
.post-create-note-link:active { opacity: .7; }
.post-create-note-link:focus-visible {
    outline: 2px solid var(--th-focus, var(--c-accent));
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}
.post-create-note-link:disabled { opacity: .5; cursor: default; text-decoration: none; }

/* [DS9 W3] Полноэкранные модалки-страницы (contacts/chat/subscribe и т.п.) —
   фон страницы, не «приподнятая» поверхность диалога */
.modal-content.contacts-modal-fullscreen {
    background: var(--c-bg);
    box-shadow: none;
}

/* --- [MODAL ENTRANCE] Единая анимация появления для всех модалок/поповеров --- */
@keyframes _modalIn {
    from { opacity: 0; transform: translateY(8px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* --- [DS9 W3] MODAL ACTIONS — единая зона кнопок внизу модалки.
   Ряд равношироких кнопок: нейтральная/отмена СЛЕВА, primary/danger СПРАВА
   (исторический источник паттерна — ряд кнопок фото-модалки, теперь тоже на этом
   классе). Использовать вместо самодельных flex-рядов. --- */
.modal-actions {
    display: flex;
    gap: var(--space-2);
    margin-top: var(--space-4);
}

.modal-actions .btn {
    flex: 1;
    min-width: 0;
    margin-top: 0;
}

/* --- [DS9 W3] MODAL NOTE — сервисная строка модалки (загрузка / пусто / ошибка).
   Разметка в app_modals.js / app_modals_actions.js. --- */
.modal-note {
    text-align: center;
    color: var(--tg-theme-hint-color, #8e8e93);
    font-size: var(--fs-sm);
    padding: var(--space-5) 0;
    margin: 0;
}

.modal-note--error {
    color: var(--c-danger);
}

/* Участники в стиле карточек */
.contact-item {
    display: flex;
    align-items: center;
    padding: 12px 15px;
    background: var(--tg-theme-secondary-bg-color, #efeff4);
    border-radius: var(--radius-md);
    margin-bottom: 8px;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.contact-avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background-color: var(--th-avatar-bg, var(--c-accent));
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    margin-right: 12px;
}

.contact-avatar-img {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    object-fit: cover;
    margin-right: 12px;
}

.contact-info {
    flex-grow: 1;
}

.contact-name {
    font-weight: 600;
    color: var(--tg-theme-text-color, #000);
    margin-bottom: 2px;
}

.contact-username {
    font-size: var(--fs-xs);
    color: var(--tg-theme-hint-color, #999);
}

/* Полноэкранное модальное окно участников */
.contacts-modal-fullscreen {
    width: 100% !important;
    max-width: 100% !important;
    /* [FIX 2026-08-22] `height:100%` у position:fixed резолвится в initial containing block —
       ТОТ ЖЕ «большой» вьюпорт, что и 100vh/100dvh, а в установленном standalone-приложении на
       Android он включает зону системной жест-навигации (см. --app-vh в app_navigation.js:
       _publishAppVh, замер там же — innerHeight=753 против 100svh≈810, +57px навигации). Низ
       окна (полоса поиска вкладки «Контакт») рисовался ПОД этой зоной — физически на экране,
       но не в области, куда попадают тапы/видно контент. Фолбэк 100% — только до первого
       JS-замера (--app-vh публикуется до готовности DOM, поэтому окно не открыть раньше него). */
    height: 100% !important;
    height: var(--app-vh, 100%) !important;
    max-height: 100% !important;
    max-height: var(--app-vh, 100%) !important;
    margin: 0 !important;
    padding: 0 !important;
    border-radius: 0 !important;
    display: flex;
    flex-direction: column;
    /* .modal-overlay центрирует детей (align-items:center) — сама подложка по-прежнему высотой
       в «большой» вьюпорт (та же инфляция, что чинит --app-vh выше, но у overlay это фон, а не
       контент, трогать незачем). Раз окно теперь НИЖЕ подложки, без flex-start его вертикально
       центрировало бы — сверху появлялся бы паразитный зазор с подложкой вместо страницы. */
    align-self: flex-start;
}

.contacts-modal-body {
    padding: 0 20px 20px 20px;
    overflow-y: auto;
    flex-grow: 1;
    /* min-height:0 — без него flex-ребёнок не даёт себя сжать ниже размера контента, и
       внутренние колоночные раскладки (вкладка «Контакт») теряют высоту. */
    min-height: 0;
    -webkit-overflow-scrolling: touch;
}

/* [FIX 2026-08-22] Шапка и ряд «Участники / Контакт» — НЕСЖИМАЕМЫЕ. .contacts-modal-fullscreen
   это flex-колонка, а flex-shrink по умолчанию 1: при длинном списке браузер сжимал ряд вкладок
   почти в ноль, и кнопки «то появлялись, то исчезали». Уехать они не могли — лежат ВНЕ
   прокручиваемой .contacts-modal-body, — именно сжимались. */
.contacts-modal-fullscreen > .owner-tabs,
.contacts-modal-fullscreen > div:first-child {
    flex-shrink: 0;
}

.contacts-list-scrollable {
    flex: 1;
    min-height: 200px;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    padding-right: 5px;
}

/* Блокировка прокрутки фона при открытом модальном окне */
body.modal-open {
    overflow: hidden;
    position: fixed;
    width: 100%;
}

/* Кнопка обновления ссылки (круглая иконка) */
.btn-regenerate {
    background: transparent;
    border: none;
    border-radius: 50%;
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    color: var(--c-success-soft);
    font-size: 30px;
    padding: 0;
    transition: all 0.2s;
    flex-shrink: 0;
}

.btn-regenerate:active {
    background: rgba(76, 217, 100, 0.1);
    transform: rotate(180deg) scale(0.9);
}

.btn-regenerate.animating {
    animation: spin-pulse 0.5s ease;
}

@keyframes spin-pulse {
    0% {
        transform: rotate(0) scale(1);
    }

    50% {
        transform: rotate(180deg) scale(0.8);
    }

    100% {
        transform: rotate(360deg) scale(1);
    }
}

/* Переключатели (Toggle Switches) в стиле iOS/Telegram */
.switch {
    position: relative;
    display: inline-block;
    width: 50px;
    height: 28px;
    flex-shrink: 0;
}

.switch input {
    opacity: 0;
    width: 0;
    height: 0;
}

.slider {
    position: absolute;
    cursor: pointer;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    /* Выкл. трек: тема приходит ЗНАЧЕНИЕМ токена (--th-slider), а не своим правилом —
       иначе body[data-theme] .slider (0-2-1) перебивал бы ВКЛ-состояние ниже. */
    background-color: var(--th-slider, var(--c-slider));
    /* [AUDIT 2026-08-27] Выключенный трек почти сливался с фоном (1.25-1.27:1). Затемнять его
       нельзя — прочитается как включённый, поэтому контур. inset-тень вместо border: не
       меняет геометрию (элемент растянут по inset) и не сдвигает кружок. */
    box-shadow: inset 0 0 0 1px var(--th-input-border, var(--c-border-strong));
    transition: .3s;
    border-radius: var(--radius-pill);
}

.slider:before {
    position: absolute;
    content: "";
    height: 24px;
    width: 24px;
    left: 2px;
    bottom: 2px;
    background-color: white;
    transition: .3s;
    border-radius: 50%;
    box-shadow: var(--th-shadow-1, var(--shadow-1));   /* [DS9 W5] ступень-1 вместо старой шкалы */
}

input:checked+.slider {
    background-color: var(--th-toggle-on, var(--c-accent));
    box-shadow: none;   /* включённый трек читается заливкой — контур лишний */
}

input:checked+.slider:before {
    transform: translateX(22px);
}

/* Выкл. трек во ВСЕХ темах — токен --th-slider/--c-slider в правиле .slider выше
   (Системная тёмная — @media в tokens.css). Отдельных правил на тему/prefers-dark
   быть НЕ должно: они равны по специфичности input:checked+.slider и гасят синий ВКЛ. */

/* --- [SEO PENDING] Тумблер включён, но ещё не действует ---------------------
   Согласие владельца уже дано, а страница появится только после модерации. Тумблер
   ОСТАЁТСЯ нажимаемым (согласие можно и отозвать) — приглушается лишь вид, поэтому
   opacity на .slider, а не disabled на input и не pointer-events:none.
   Цвет не трогаем: он приходит токеном из палитры темы (правило 3), гасится прозрачностью. */
.switch.is-pending .slider {
    opacity: 0.45;
}
.switch.is-pending .slider:before {
    box-shadow: none;   /* приподнятость гасим вместе с цветом — иначе выглядит активным */
}

/* Статичная шапка страницы графика */
/* [ЗАДАЧА 3] Окно вписано строго во вьюпорт за вычетом вертикальных паддингов body (15px+15px),
   иначе документ выше вьюпорта и скроллится весь body — шапка уезжала вверх. dvh учитывает
   адресную строку мобильных браузеров. Таб-бара на этом окне нет (schedule-view не в _TAB_ROOTS).
   Внутри скроллится только #schedule-list-container, шапка остаётся неподвижной. */
#schedule-view {
    display: flex;
    flex-direction: column;
    height: calc(100vh - 30px);   /* fallback для движков без dvh */
    height: calc(100dvh - 30px);
    overflow: hidden;
}

/* Шапка — такая же скруглённая карточка, как всё под ней (.date-header.today-marker, слоты):
   полная рамка по периметру + --radius-md, в полях body. Раньше была полосой край-в-край
   (margin -15px) с одной линией снизу — единственный острый прямоугольник на экране.
   ВАЖНО: цвет рамки задаётся через border-color (не border-bottom-color) — так же в темах
   (themes.css) и в двух media-блоках Системной темы ниже, иначе покрасится только низ.
   Токены --th-header-* заведены во всех палитрах; фолбэк рамки — border-subtle, а не
   --tg-theme-hint-color (в светлой это #5e6470 — почти чёрная черта).
   position:sticky тут фактически мёртвый (шапка — сестра #schedule-list-container, а не его
   потомок), но z-index под ним нужен выпадающему меню — оставлено намеренно. */
.schedule-header {
    position: sticky;
    top: 0;
    z-index: 100;
    background-color: var(--th-header-bg, var(--tg-theme-bg-color, #ffffff));
    border: 1px solid var(--th-header-border, var(--c-border-subtle));
    border-radius: var(--radius-md);
    box-shadow: var(--th-header-shadow, none);
    margin: 0 0 var(--space-2);
    padding: var(--space-2) var(--space-3);
}

/* --- [ШАПКА ГРАФИКА — ВНУТРЕННИЙ LAYOUT] ---
   Все четыре элемента (стрелка · фото · заголовок+описание · ⋮) стоят на ОДНОЙ оптической оси:
   align-items:center. Раньше стрелка и ⋮ жались к верху, а фото центрировалось — три разные оси
   на высокой шапке. Высота текстового блока (заголовок + 2 строки описания) теперь сопоставима
   с квадратом фото 56px, поэтому центрирование даёт ровную композицию. */
.schedule-header-inner {
    display: flex;
    align-items: center;
    position: relative;
    gap: 8px;
}

.schedule-header-left {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.schedule-title {
    margin: 0 0 2px 0;
    font-size: var(--fs-lg);
    font-weight: var(--fw-semibold);   /* [DS8] как .nav-title — единый вес заголовков экранов */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* --- [OWNER MODERATION] Строка заголовка графика: название + значок статуса --- */
/* Обёртка нужна потому, что .schedule-header-left — колонка: без неё значок уехал бы
   на отдельную строку под названием. min-width:0 сохраняет ellipsis у длинного названия. */
.schedule-title-row {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    min-width: 0;
}
.schedule-title-row .schedule-title { min-width: 0; }

/* Значок состояния модерации: виден только администрации графика (показ решает JS).
   Цвета берём из существующих токенов тем — своих заводить не пришлось:
   одобрен = «успех», на модерации = «мягкое предупреждение». */
.sched-mod-status {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    font-size: var(--fs-sm);
    line-height: 1;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
.sched-mod-status.is-approved { color: var(--th-success, var(--c-success)); }
.sched-mod-status.is-pending  { color: var(--th-warn-soft-text, var(--c-warn-soft-text)); }
.sched-mod-status:active { opacity: 0.7; }

.sched-back-btn {
    /* Стрелка — в УГЛУ панели, как на всех остальных экранах. На низких шапках (.top-nav,
       min-height 44px) угол и центр совпадают, а здесь шапка высокая (фото 56px + заголовок +
       описание), и общее центрирование уводило стрелку в середину левой области — она
       переставала читаться как навигация экрана. Остальные элементы остаются по центру.
       Вертикальный padding обнулён, чтобы глиф не раздувал строку — тап-зону 44px держит
       .sched-back-btn::before = max(100%, --control-h). */
    align-self: flex-start;
    flex-shrink: 0;
    padding-top: 0;
    padding-bottom: 0;
    /* Правый padding базовой .nav-back-btn (12px) + gap 8px давали 20px пустоты между стрелкой
       и квадратом фото — фото «отплывало» в середину. Сужаем до 4px: фото встаёт вплотную к
       навигации, тап-зона 44px не страдает (её держит ::before, а не padding). */
    padding-right: var(--space-1);
}

.schedule-desc {
    --desc-lh: 1.4;                 /* один источник правды для line-height и расчёта высоты */
    font-size: var(--fs-sm);
    color: var(--tg-theme-hint-color, #8e8e93);
    line-height: var(--desc-lh);
    /* [ВЫСОТА] Только потолок в 2 строки, без min-height: график БЕЗ описания схлопывает блок
       в 0 и не держит пустоту в шапке. Раньше высота считалась по 13px при шрифте 14px —
       третья строка подрезалась по горизонту. Полный текст — тапом (открывает карточку). */
    max-height: calc(var(--fs-sm) * var(--desc-lh) * 2);
    overflow: hidden;
}

/* Непустое описание — тап-цель: открывает карточку графика (состояния как у квадрата
   фото .sched-header-thumb). Класс вешает setupDescReadMore (app_schedule.js). */
.schedule-desc.is-tappable {
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.schedule-desc.is-tappable:active {
    opacity: 0.8;
}

.desc-more-btn {
    cursor: pointer;
    color: var(--th-link-accent, var(--c-accent));
    font-weight: bold;
    -webkit-tap-highlight-color: transparent;
}

.schedule-header-right {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    align-items: center;
    flex-shrink: 0;
    /* Оптическая симметрия с левым краем: слева глиф стрелки стоит в 12px от рамки шапки
       (margin-left:-4px + padding-left:4px при padding шапки 12px), справа же собственный
       padding кнопки ⋮ (8px) уводил глиф на 20px. Сдвигаем колонку на 8px — оба глифа
       оказываются на одинаковом расстоянии от рамки. */
    margin-right: -8px;
}

/* Стрелка «наверх» появляется при прокрутке списка ниже 200px (updateScrollTopBtn вешает .hidden).
   С display:none колонка пере-центрировалась бы и ⋮ прыгал на ~13px при каждом пересечении
   порога, поэтому место резервируем всегда: гасим видимостью, а не выкидыванием из потока.
   visibility:hidden убирает кнопку и из a11y-дерева. Прецедент оверрайда .hidden — #subscribe-btn. */
.schedule-header-right .sched-scrolltop-btn.hidden {
    display: inline-flex !important;
    visibility: hidden;
    pointer-events: none;
}

.sched-menu-btn {
    background: none;
    border: none;
    font-size: var(--fs-2xl);
    cursor: pointer;
    color: var(--tg-theme-text-color, #000);
    padding: 0 var(--space-2);
    line-height: 1;
    -webkit-tap-highlight-color: transparent;
}

/* --- [SCROLL-TOP] Стрелка «наверх» под сэндвич-кнопкой (появляется при прокрутке) --- */
/* Вид как у кнопки «назад» (.nav-back-btn): акцентный цвет, тот же размер; иконка — стрелка вверх. */
.sched-scrolltop-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    margin-top: var(--space-1);
    padding: 4px;
    background: none;
    border: none;
    font-size: var(--fs-xl);
    line-height: 1;
    cursor: pointer;
    color: var(--th-link-accent, var(--c-accent));
    -webkit-tap-highlight-color: transparent;
}

#schedule-list-container {
    flex: 1;
    overflow-y: auto;
    /* [ЗАЗОР ШАПКА↔ПАНЕЛЬ] Верхний padding убран: он складывался с margin-bottom шапки (8px)
       и margin-top панели действий (14px) — между двумя карточками зияло 32px. Отбивку сверху
       держит только шапка (var(--space-2)); первый элемент истории (.date-header.expired)
       свой margin-top 20px не потерял. */
    padding-top: 0;
    -webkit-overflow-scrolling: touch;
}

/* [DS9 W2] Панель действий графика (бывш. «синяя полоса»): нейтральная surface-2 с тонкой
   рамкой; единственный акцент — CTA «Записаться» (booknear). Иконки — приглушённые (hint).
   Градиент читает прежние токены --th-todaybar-* — в палитрах light/dark их значения
   выровнены в surface-2 (плоский «градиент»); custom1/2 сохраняют свой цвет. */
.date-header.today-marker {
    background: linear-gradient(90deg, var(--th-todaybar-from, var(--c-surface-2)) 0%, var(--th-todaybar-to, var(--c-surface-2)) 100%);
    color: var(--tg-theme-hint-color, #8e8e93);
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    padding: 7px 10px;
    margin: 14px 0;
    border-radius: var(--radius-md);
    font-weight: 600;
    box-shadow: none;
    /* [ЗАДАЧА 3/4] Без текста даты единственный flex-ребёнок (actions) держим справа; */
    /* position:relative — контекст для абсолютно центрированной кнопки записи. */
    justify-content: flex-end;
    position: relative;
}

/* [ЗАЗОР ШАПКА↔ПАНЕЛЬ] Когда история свёрнута (обычное состояние экрана), панель действий —
   первый видимый элемент списка, и её верхний margin превращался в пустую полосу под шапкой.
   Гасим его именно в этом случае; при развёрнутой истории (класс .section-collapsed снят
   через toggleSectionInstant) 14px возвращаются как отбивка от последней карточки истории. */
#expired-section.section-collapsed + .date-header.today-marker {
    margin-top: 0;
}

/* --- [ЗАДАЧА 1] Кнопка «Быстрая запись» — слева на синей полосе --- */
/* Полноценная кнопка-пилюля шириной ~3-4 значка (иконка молнии + подпись). Тёмно-синяя */
/* заливка (темнее полосы — слегка выделяется), белая подпись. Прижата влево как обычный */
/* флекс-ребёнок; правый блок actions уходит вправо через margin-left:auto. */
/* Цвета — из палитры тем: var(--th-booknear-*, var(--c-booknear-*)). */
.today-book-nearest-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    /* [ЗАДАЧА 2] Кнопка прижата к левому краю полосы (margin-left компенсирует левый padding .today-marker = 10px, DS9 W2). */
    margin-left: -2px;
    /* [OVERFLOW] Не сжимается и не выталкивается значками действий — иначе «выпирает» за экран. */
    flex-shrink: 0;
    /* Ширина одинакова при любом языке = как с русским «Записаться» (~111px + запас на шрифты устройств); */
    /* короткие переводы («Book», «Rezervă») центрируются в той же ширине, а не сужают кнопку. */
    min-width: 116px;
    padding: 6px 14px;
    background: var(--th-booknear-bg, var(--c-booknear-bg));
    border: 1px solid var(--th-booknear-border, var(--c-booknear-border));
    border-radius: var(--radius-md);
    color: var(--th-booknear-icon, var(--c-booknear-icon));
    font-weight: 600;
    font-size: var(--fs-sm);
    white-space: nowrap;
    cursor: pointer;
    box-shadow: var(--th-shadow-1, var(--shadow-1));   /* [DS9 W5] ступень-1 вместо старой шкалы */
    -webkit-tap-highlight-color: transparent;
    transition: filter 0.15s, transform 0.05s;
}
.today-book-nearest-btn:hover { filter: brightness(1.12); }
.today-book-nearest-btn:active { transform: scale(0.96); }
/* Молния — залитая (fill: currentColor перебивает атрибут fill="none" обёртки иконки). */
.today-book-nearest-btn svg { width: 18px; height: 18px; display: block; fill: currentColor; flex-shrink: 0; }

/* --- [TODAY-MARKER ДЕЙСТВИЯ] Контейнер кнопок справа: календарь | ёлочка | шеврон истории --- */
/* margin-left:auto прижимает блок вправо независимо от наличия кнопки «Быстрая запись» слева. */
.today-marker-actions {
    display: flex;
    align-items: center;
    gap: 16px;                       /* [ЗАДАЧА 3] больше зазор между монитором/календарём/ёлочкой/шевроном */
    margin-left: auto;
    /* [OVERFLOW] Ряд не сжимается: offsetWidth = честная контентная ширина (нужно для расчёта
       переполнения в layoutTodayActionsOverflow). Лишние значки прячем JS-логикой, а не сжатием. */
    flex-shrink: 0;
}

/* [ЗАДАЧА 4 / OVERFLOW] Скрытые (непоместившиеся или наоборот поместившиеся) значки полосы.
   Специфичность .today-marker-actions .tm-hidden > .today-cal-btn — перебивает inline-flex кнопок. */
.today-marker-actions .tm-hidden { display: none; }
/* [OVERFLOW / ЭШЕЛОН 2] Во втором эшелоне (шеврон нажат) «Записаться» уходит с полосы,
   освобождая свои ~116px под значки — иначе спрятанные иконки не влезали. Селектор от
   .date-header.today-marker: кнопка — сосед .today-marker-actions, а не её потомок. */
.date-header.today-marker .today-book-nearest-btn.tm-book-hidden { display: none; }
/* Overflow-шеврон: та же кнопка полосы; в открытом состоянии повёрнут (указывает «обратно»). */
.today-marker-actions .tm-overflow-btn svg { transition: transform 0.15s; }
.today-marker-actions .tm-overflow-btn.tm-overflow-open svg { transform: rotate(180deg); }

/* Кнопка-календарь (открыть навигацию по дням). Белая на синем (color: inherit от маркера). */
.today-cal-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 3px 5px;
    background: none;
    border: none;
    border-radius: var(--radius-sm);
    color: inherit;
    cursor: pointer;
    opacity: 0.9;
    -webkit-tap-highlight-color: transparent;
    transition: background 0.15s, opacity 0.15s;
}
.today-cal-btn:hover { opacity: 1; }
.today-cal-btn:active { opacity: 1; }
/* Нажатое (активное) состояние — календарь открыт ([DS9 W2] нейтральный ховер-фон) */
.today-cal-btn.active {
    background: var(--th-hover, rgba(128, 128, 128, 0.18));
    opacity: 1;
}
.today-cal-btn svg { width: 18px; height: 18px; display: block; }

/* --- [ЗАДАЧА 4] Кнопка-монитор — состояние меняет САМ значок (закрытый/открытый глаз), --- */
/* без смены яркости и без фона. Глаз чуть крупнее остальных значков полосы. */
.today-monitor-btn svg { width: 22px; height: 22px; }

/* Ёлочка (gcb) в синей полосе — белая, перекрывает тёмные тем-оверрайды (.gcb-btn в themes.css) */
.date-header.today-marker .gcb-btn { color: inherit; opacity: 0.9; }

/* [DS9 W2] Разделитель зоны админа — тихая строка в стиле дня: warning-текст + линия. */
.date-header.admin-only-marker {
    background: transparent;
    color: var(--th-warning, var(--c-warning));
    padding: 4px 2px;
    margin: 14px 0 8px;
    border-radius: 0;
    font-weight: var(--fw-semibold);
    box-shadow: none;
    gap: 8px;
    justify-content: flex-start;
}
.date-header.admin-only-marker::after {
    content: '';
    order: 1;
    flex: 1 1 auto;
    height: 1px;
    background: var(--th-border-subtle, var(--c-border-subtle));
    margin: 0 2px;
}
.date-header.admin-only-marker > button { order: 2; }

/* [DS9 W2] Слоты «только админ» — нейтральная строка; статус несёт оранжевая полоска
   (--st-strip: --c-warning, см. [STATUS STRIP]) + замок в кнопке-надписи. */
.slot-container.admin-only {
    background: var(--th-surface-1, var(--c-surface-1));
}

.date-header.admin-only {
    color: var(--th-warning, var(--c-warning));
    opacity: 0.9;
}

/* --- GLOBAL COLLAPSE BUTTON (ЁЛОЧКА) --- */
.gcb-btn {
    background: none;
    border: none;
    cursor: pointer;
    padding: 2px 4px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    -webkit-tap-highlight-color: transparent;
    outline: none;
    color: var(--tg-theme-text-color, #000);
    opacity: 0.8;
}

.gcb-btn:active {
    opacity: 1;
}

.gcb-i {
    display: block;
    width: 4px;
    height: 4px;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transition: transform 0.22s ease;
}

/* [ЗАДАЧА 6] Геометрия gcb-i (border-right+border-bottom): rotate(45deg)=∨ вниз,
   rotate(-45deg)=▶ вправо, rotate(-135deg)=∧ вверх. */
.gcb-btn.gcb-state-0 .gcb-i {
    transform: rotate(45deg);
}

/* ∨ всё развёрнуто */
.gcb-btn.gcb-state-1 .gcb-i {
    transform: rotate(-45deg);
}

/* ▶ дни свёрнуты */
.gcb-btn.gcb-state-2 .gcb-i {
    transform: rotate(-135deg);
}

/* ∧ дни видны, участники скрыты (старт) */
@media (prefers-color-scheme: dark) {
    .gcb-btn {
        color: rgba(255, 255, 255, 1);
    }
}

/* --- DARK THEME CHEVRONS --- */
@media (prefers-color-scheme: dark) {
    .chevron-btn {
        color: rgba(255, 255, 255, 0.8) !important;
    }

    .date-header.today-marker .chevron-btn,
    .date-header.admin-only-marker .chevron-btn {
        color: rgba(255, 255, 255, 0.9) !important;
    }

}

/* [AUDIT 2026-08-27] Подпись зоны админа: правило жило внутри @media (prefers-color-scheme: dark),
   то есть следовало за ОС, а не за выбранной темой, и парного body[data-theme] не имело.
   В светлой теме на тёмной ОС давало #ffcc80 на белом слоте — 1.48:1. Теперь токен палитры. */
.slot-actions-admin-only {
    color: var(--th-warning, var(--c-warning));
}

/* [DS9 W2] Истёкшие — нейтральная строка, приглушение + серая статус-полоска.
   [AUDIT 2026-08-27] Приглушение даёт ЦВЕТ, а не opacity: сплошная прозрачность роняла
   контраст текста ниже нормы (в светлой 3.76 у основного и 1.98 у серого) и, создавая
   группу, тянула за собой всех детей. --th-expired-text приглушён ровно до порога 4.5:1. */
.slot-container.expired {
    background: var(--th-surface-1, var(--c-surface-1));
    opacity: 1;
    color: var(--th-expired-text, var(--c-expired-text));
}
/* Дети, задающие свой цвет сами, — на тот же приглушённый токен. */
.slot-container.expired .btn-slot-plain,
.slot-container.expired .slot-count {
    color: var(--th-expired-text, var(--c-expired-text));
}
/* У истёкшего слота чип никогда не получает .has-fill (см. комментарий у .slot-count), то есть
   дорожка ничего не показывает — только затемняет фон под цифрами и роняет их до 3.7-3.9:1
   (поймано живым замером getComputedStyle, а не расчётом по токенам). Снимаем. */
.slot-container.expired .slot-count {
    background: transparent;
}

.date-header.expired {
    color: var(--th-expired-text, var(--c-expired-text));
    opacity: 1;
}

/* --- [ЗАМОРОЗКА ЗАПИСИ, DS9 W2] Нейтральная строка, занятость несёт полоска (см. выше);
   лёгкое приглушение — «запись сейчас закрыта». --- */
.slot-container.frozen {
    background: var(--th-surface-1, var(--c-surface-1));
    opacity: 0.88;
}

/* [ЗАМОРОЗКА — ОСВОБОДИВШЕЕСЯ МЕСТО, 2026-08-26] Требует действия (записаться самому или
   решение админа) — НЕ приглушаем, в отличие от обычного «закрыто до конца заморозки». */
.slot-container.frozen.reopened {
    opacity: 1;
}

/* --- [ЗАМОРОЗКА — СТРОКА-МАРКЕР ОСВОБОДИВШЕГОСЯ МЕСТА] Переиспользует готовый токен
   «мягкого предупреждения» (DS8, auth-модалка) — тот же визуальный язык «требует внимания»,
   новых цветовых токенов не заводим (правило 3 CLAUDE.md). --- */
.slot-vacated-notice {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    padding: 7px 10px;
    border-radius: var(--radius-sm);
    background: var(--th-warn-soft-bg, var(--c-warn-soft-bg));
    color: var(--th-warn-soft-text, var(--c-warn-soft-text));
    font-size: var(--fs-xs);
    line-height: var(--lh-tight);
}
.slot-vacated-notice-icon {
    flex-shrink: 0;
    display: flex;
}
.slot-vacated-notice-text {
    flex: 1 1 auto;
    min-width: 0;
}
.slot-vacated-lock-btn {
    flex-shrink: 0;
    background: none;
    border: none;
    font-size: var(--fs-md);
    cursor: pointer;
    padding: 2px 4px;
    color: inherit;
    opacity: 0.75;
    -webkit-tap-highlight-color: transparent;
    transition: opacity 0.15s;
}
.slot-vacated-lock-btn:hover { opacity: 1; }
.slot-vacated-lock-btn:active { opacity: 0.55; }
.slot-vacated-lock-btn:focus-visible {
    outline: 2px solid var(--th-focus, var(--c-accent));
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

/* --- [HINT BTN] --- */
.label-with-hint {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 5px;
    font-weight: 600;
    color: var(--tg-theme-text-color, #000);
}

.hint-btn {
    background: none;
    border: 1.5px solid var(--tg-theme-hint-color, #999);
    border-radius: 50%;
    width: 18px;
    height: 18px;
    min-width: 18px;
    font-size: var(--fs-2xs);
    font-weight: 700;
    line-height: 16px;
    text-align: center;
    cursor: pointer;
    color: var(--tg-theme-hint-color, #999);
    padding: 0;
    flex-shrink: 0;
    margin-left: 6px;
    opacity: 0.75;
    -webkit-tap-highlight-color: transparent;
}

.hint-btn:active {
    opacity: 1;
    transform: scale(0.92);
}

/* --- [DS6] ТАЧ-ЦЕЛИ 44px (WCAG 2.5.5): невидимая hit-area через ::before.
   Визуальный размер иконки НЕ меняется — расширяется только кликабельная область. --- */
.sched-menu-btn,
.event-menu-btn,
.bell-btn,
.user-avatar-btn,
.hint-btn,
#subscribe-btn {
    position: relative;
}

.nav-back-btn::before,
.sched-back-btn::before,
.sched-menu-btn::before,
.event-menu-btn::before,
.bell-btn::before,
.user-avatar-btn::before,
.hint-btn::before,
#subscribe-btn::before {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: max(100%, var(--control-h));
    height: max(100%, var(--control-h));
    transform: translate(-50%, -50%);
}

/* --- [HINT POPUP] --- */
#hint-popup-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: var(--th-overlay, var(--c-overlay));   /* [DS9 W3] единый оверлей */
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}

.hint-popup {
    background: var(--th-surface-3, var(--c-surface-3));   /* [DS9 W3] поверхность диалога */
    color: var(--c-text);
    border-radius: var(--radius-md);
    box-shadow: var(--th-shadow-3, var(--shadow-3));
    padding: var(--space-5);
    max-width: 320px;
    width: 100%;
    animation: _modalIn 0.16s ease;
}

.hint-popup-title {
    font-weight: 700;
    font-size: var(--fs-md);
    margin: 0 0 10px 0;
}

.hint-popup-text {
    font-size: var(--fs-sm);
    line-height: 1.55;
    white-space: pre-line;
    margin: 0 0 14px 0;
}

.hint-popup-close {
    display: block;
    margin: 0 auto;
    padding: 5px 18px;
    background: none;
    color: var(--tg-theme-hint-color, #999);
    border: 1px solid var(--tg-theme-hint-color, #ccc);
    border-radius: var(--radius-sm);
    font-size: var(--fs-sm);
    font-weight: 600;
    cursor: pointer;
}

/* --- [DS6] INLINE-TOOLTIP подсказки (десктоп/планшет ≥600px).
   На мобиле (<600px) showFieldHint показывает прежний sheet-модал. Координаты задаёт JS. --- */
.hint-inline-tip {
    position: fixed;
    z-index: 200;
    width: max-content;
    max-width: min(280px, 92vw);
    box-sizing: border-box;
    text-align: left;
    font-size: var(--fs-sm);
    font-weight: 400;
    line-height: 1.5;
    background: var(--c-surface);
    color: var(--c-text);
    border: 1px solid var(--c-hint);
    border-radius: var(--radius-md);
    box-shadow: var(--th-shadow-2, var(--shadow-2));   /* [DS9 W5] поповер = ступень-2 (дропдаун) */
    padding: var(--space-3);
    cursor: default;
    animation: _modalIn 0.14s ease;
}

.hint-inline-title {
    font-weight: 700;
    font-size: var(--fs-sm);
    margin-bottom: var(--space-1);
    color: var(--c-text);
}

.hint-inline-text {
    font-size: var(--fs-sm);
    line-height: 1.5;
    white-space: pre-line;
    color: var(--c-text);
}

/* --- [DS6] МАСТЕР СОЗДАНИЯ ГРАФИКА (3 шага) --- */
.wizard-progress {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--space-5);
}

.wizard-dots {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.wizard-dot {
    width: 8px;
    height: 8px;
    border-radius: var(--radius-pill);
    background: var(--c-hint);
    opacity: 0.4;
    transition: opacity 0.18s, background 0.18s, width 0.18s;
}

.wizard-dot-done {
    opacity: 1;
    background: var(--th-chip, var(--c-accent));
}

.wizard-dot-active {
    opacity: 1;
    width: 22px;
    background: var(--th-chip, var(--c-accent));
}

.wizard-step-counter {
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--c-hint);
}

.wizard-step-title {
    margin: 0 0 var(--space-4) 0;
    font-size: var(--fs-lg);
    font-weight: var(--fw-semibold);
    color: var(--c-text);
}

.wizard-nav {
    display: flex;
    gap: var(--space-3);
    margin-top: var(--space-5);
}

.wizard-nav .wizard-back {
    flex: 0 0 auto;
    width: auto;
}

.wizard-nav .wizard-next,
.wizard-nav .wizard-create {
    flex: 1;
}

/* --- [ШАПКА СТРАНИЦЫ МОИ ЗАПИСИ — STICKY] --- */

/* Прилипающая шапка: остаётся видна при прокрутке */
.top-nav-sticky {
    position: sticky;
    top: 0;
    z-index: 100;
    background: var(--tg-theme-bg-color, #fff);
    padding-top: var(--space-1);
    padding-bottom: var(--space-2);
    margin-bottom: var(--space-3);
    border-bottom: 1px solid var(--tg-theme-secondary-bg-color, #efeff4);
}

/* Кнопка колокольчика в шапке */
.bell-btn {
    background: none;
    border: none;
    cursor: pointer;
    padding: 4px 2px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--tg-theme-text-color, #000);
    flex-shrink: 0;
    transition: opacity 0.2s;
    -webkit-tap-highlight-color: transparent;
}

.bell-btn:active {
    transform: scale(0.88);
}

/* Размер SVG колокольчика */
.bell-icon {
    width: 22px;
    height: 22px;
}

/* Уведомления включены */
.bell-btn.bell-active {
    opacity: 1;
}

/* Уведомления отключены — перечёркнутая линия поверх иконки */
.bell-btn.bell-muted {
    opacity: 1;
    position: relative;
}
.bell-btn.bell-muted::after {
    content: '';
    position: absolute;
    width: 2px;
    height: 70%;
    background: var(--c-danger);
    border-radius: 2px;
    transform: rotate(-45deg);
    top: 15%;
    left: calc(50% - 1px);
    pointer-events: none;
    z-index: 1;
}

/* --- [НАПОМИНАНИЯ ГРАФИКА — МОДАЛ] --- */

/* Строка существующего напоминания в списке */
.reminder-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 15px;
    background: var(--tg-theme-secondary-bg-color, #efeff4);
    border-radius: var(--radius-md);
    margin-bottom: 8px;
    font-size: var(--fs-sm);
    color: var(--tg-theme-text-color, #000);
}

/* [DS9 W3] Правый кластер строки напоминания (✓-подтверждение + ✕-удаление) */
.reminder-item-controls {
    display: flex;
    align-items: center;
    gap: 6px;
}

/* Кнопка удаления напоминания */
.reminder-delete-btn {
    background: none;
    border: none;
    cursor: pointer;
    font-size: var(--fs-md);
    color: var(--tg-theme-hint-color, #999);
    padding: 0 4px;
    flex-shrink: 0;
    -webkit-tap-highlight-color: transparent;
}

.reminder-delete-btn:active {
    opacity: 0.5;
}

/* Галочка "требовать подтверждение записи" — слева от ✕ */
.reminder-confirm-toggle {
    background: none;
    border: 1.5px solid var(--tg-theme-hint-color, #999);
    color: var(--tg-theme-hint-color, #999);
    cursor: pointer;
    font-size: var(--fs-sm);
    line-height: 1;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    padding: 0;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    -webkit-tap-highlight-color: transparent;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
}

.reminder-confirm-toggle.active {
    background: var(--c-success);
    border-color: var(--c-success);
    color: var(--c-on-accent);
}

.reminder-confirm-toggle:active {
    opacity: 0.6;
}

/* Блок "тихие часы" */
.quiet-hours-section {
    background: var(--tg-theme-secondary-bg-color, #efeff4);
    border-radius: var(--radius-md);
    padding: 12px 14px;
    margin-bottom: 12px;
}

.quiet-hours-section select {
    background: var(--c-bg);
    color: var(--c-text);
    border: 1px solid var(--c-hint);
    border-radius: var(--radius-sm);
}

/* Полоса выбора времени напоминания */
.reminder-picker {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    padding: 14px 0 6px;
    justify-content: center;
}

.reminder-picker.hidden {
    display: none;
}

/* Чипсы-кнопки выбора времени */
.reminder-chip {
    background: var(--th-chip, var(--c-accent));
    color: var(--tg-theme-button-text-color, #fff);
    border: none;
    border-radius: var(--radius-pill);
    padding: 8px 18px;
    font-size: var(--fs-sm);
    font-weight: 600;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: opacity 0.15s;
}

.reminder-chip:active {
    opacity: 0.75;
    transform: scale(0.95);
}

/* --- [TEXTAREA НАПОМИНАНИЯ] --- */
.reminder-text-section {
    margin-bottom: 15px;
}

.reminder-text-input {
    resize: vertical;
    overflow: auto !important;
    min-height: 90px;
    max-height: 260px;
    font-size: var(--fs-sm);
    line-height: 1.5;
}

.reminder-text-footer {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-top: 4px;
    gap: 8px;
}

.reminder-text-hint {
    font-size: var(--fs-2xs);
    color: var(--tg-theme-hint-color, #999);
    margin: 0;
    flex: 1;
}

.reminder-text-counter {
    font-size: var(--fs-2xs);
    color: var(--tg-theme-hint-color, #999);
    flex-shrink: 0;
    white-space: nowrap;
}

.reminder-text-counter--warn {
    color: var(--th-warning, var(--c-warning));
}

/* --- [ЧЕКБОКС ЯРЛЫКА ПОДПИСКИ] --- */
.subscribe-shortcut-label {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0 0 16px 0;
    padding: 10px 12px;
    background: var(--tg-theme-secondary-bg-color, #efeff4);
    border-radius: var(--radius-sm);
    cursor: pointer;
    user-select: none;
    text-align: left;
    transition: opacity 0.15s;
}

.subscribe-shortcut-label:active {
    opacity: 0.7;
}

.subscribe-shortcut-checkbox {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    cursor: pointer;
    accent-color: var(--th-focus, var(--c-accent));
    margin: 0;
}

.subscribe-shortcut-text {
    font-size: var(--fs-sm);
    color: var(--tg-theme-text-color, #000);
    line-height: 1.3;
}

/* --- [ПОВЕРХ МОДЕРАЦИИ] Модал данных участника должен быть выше модала модерации --- */
#participant-data-modal {
    z-index: 1010;
}

/* --- [S7 ПОВЕРХ ЛЕНТЫ ОТЗЫВОВ] Окно жалобы открывается ИЗ ленты отзывов, но лежит в
   разметке РАНЬШЕ неё — при равном z-index лента перекрыла бы его. Тот же приём, что
   у #participant-data-modal выше. --- */
#report-modal {
    z-index: 1010;
}

/* --- [МОДЕРАЦИЯ] Шапка модалки ([DS9] токены вместо хардкода + «тихие» icon-кнопки,
   как шапка «Уведомления»: колокольчик-toggle и крестик = .nav-icon-btn) --- */
.mod-modal-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--th-border-subtle, var(--c-border-subtle));
}
.mod-modal-title {
    margin: 0;
    font-size: var(--fs-lg);
    font-weight: var(--fw-semibold);
    color: var(--tg-theme-text-color, var(--c-text));
}
.mod-modal-head-actions {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}
/* Колокольчик-toggle: базово приглушён (.nav-icon-btn = hint), включён — акцент */
#mod-notify-btn.is-active {
    color: var(--th-link-accent, var(--c-accent));
}

/* --- [МОДЕРАЦИЯ] Вкладки фильтра ([DS9 W5] сегмент-контрол как .inbox-tabs:
   трек surface-2 + рамка, активная вкладка = surface-1 + shadow-1) --- */
.mod-tabs {
    display: flex;
    gap: var(--space-1);
    margin-bottom: var(--space-3);
    padding: var(--space-1);
    background: var(--th-surface-2, var(--c-surface-2));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    border-radius: var(--radius-md);
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}
.mod-tab {
    flex: 1 1 auto;
    padding: 6px 10px;
    border: none;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--tg-theme-hint-color, var(--c-hint));
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    cursor: pointer;
    font-family: inherit;
    white-space: nowrap;
    transition: background 0.18s, color 0.18s, box-shadow 0.18s;
    -webkit-tap-highlight-color: transparent;
}

.mod-tab-active {
    background: var(--th-surface-1, var(--c-surface-1));
    color: var(--tg-theme-text-color, var(--c-text));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
}

/* --- [КАРТОЧКА ЗАЯВКИ] [DS9] Нейтральная карточка surface-1 + рамка + тень-1 (как .inbox-item) --- */
.mod-item {
    background: var(--th-surface-1, var(--c-surface-1));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
    border-radius: var(--radius-md);
    padding: 12px 14px;
    margin-bottom: 8px;
}

.mod-item:last-child {
    margin-bottom: 0;
}

/* Шапка карточки: аватар (margin-right у .contact-avatar) + имя/мета + дата справа */
.mod-item-head {
    display: flex;
    align-items: center;
}

.mod-item-info {
    flex: 1;
    min-width: 0;
}

.mod-item-name {
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--tg-theme-text-color, var(--c-text));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.mod-item-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 2px 10px;
    font-size: var(--fs-xs);
    color: var(--tg-theme-hint-color, var(--c-hint));
    margin-top: 2px;
}

.mod-item-time {
    white-space: nowrap;
}

.mod-item-date {
    flex-shrink: 0;
    margin-left: 8px;
    font-size: var(--fs-2xs);
    color: var(--tg-theme-hint-color, var(--c-hint));
    text-align: right;
}

/* [A3] Возраст заявки относительно TTL автопротухания (24ч — tasks.BOOKING_REQUEST_TTL_SECONDS).
   Только цвет, без цифр/обратного отсчёта — решение владельца. */
.mod-item-date--warn {
    color: var(--th-warning, var(--c-warning));
    font-weight: 600;
}

.mod-item-date--danger {
    color: var(--c-danger);
    font-weight: 600;
}

/* --- [МОДЕРАЦИЯ ЗАЯВОК → ЧАТ] Icon-кнопка «Написать пользователю» (паттерн .owner-sched-chat-btn) --- */
.mod-chat-btn {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    margin-left: 6px;
    padding: 0;
    border: none;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--th-link-accent, var(--c-accent));
    cursor: pointer;
    transition: background 0.15s;
}
.mod-chat-btn:hover { background: var(--th-accent-glow, var(--c-accent-glow)); }
.mod-chat-btn:active { opacity: 0.85; }
.mod-chat-btn svg { width: 18px; height: 18px; }

/* --- [РАЗДЕЛИТЕЛЬ ДНЯ] Центрированный заголовок группы по дню подачи (как в чате) --- */
.mod-date-header {
    text-align: center;
    margin: 4px 0 10px;
}

.mod-date-header span {
    display: inline-block;
    padding: 3px 12px;
    background: var(--th-surface-2, var(--c-surface-2));
    border-radius: var(--radius-pill);
    font-size: var(--fs-2xs);
    color: var(--tg-theme-hint-color, var(--c-hint));
}

/* --- [S5 JOURNAL] Строка журнала событий графика.
   Карточка/типографика — целиком канон .mod-item* (окно модерации); своё здесь только
   место аватара: у события нет лица, поэтому там глиф действия в тихом кружке
   (размер и отступ = .contact-avatar, чтобы колонки текста совпадали с модерацией). --- */
.jrn-icon {
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    margin-right: 12px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--th-surface-2, var(--c-surface-2));
    color: var(--tg-theme-hint-color, var(--c-hint));
    /* Размер глифа: .icn svg = 1em, поэтому задаём его font-size'ом контейнера —
       так масштабируются и SVG-ядро, и имена, оставшиеся эмодзи. */
    font-size: 18px;
}

/* --- [ЧИП ДОП. ДАННЫХ] Простая кнопка БЕЗ обвода — стоит в строке мета, справа от типа заявки --- */
.mod-data-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 4px;
    background: transparent;
    border: none;
    border-radius: var(--radius-pill);
    color: var(--th-link-accent, var(--c-accent));
    font-size: var(--fs-xs);
    font-family: inherit;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: opacity 0.15s;
}

.mod-data-chip:active {
    opacity: 0.6;
}

.mod-item-actions {
    display: flex;
    gap: 8px;
    margin-top: 12px;
}

/* --- [КНОПКИ РЕШЕНИЯ] Контурные, компактные, неяркие; заливка только на :active --- */
.mod-btn-approve,
.mod-btn-reject {
    flex: 0 1 auto;
    /* [DS8] размеры/вес — на токены */
    min-height: var(--control-h-compact);
    padding: 0 var(--space-4);
    background: transparent;
    border: 1px solid;
    border-radius: var(--radius-sm);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    cursor: pointer;
    font-family: inherit;
    -webkit-tap-highlight-color: transparent;
    transition: background 0.15s, color 0.15s;
}

.mod-btn-approve { color: var(--c-success); border-color: var(--c-success); }
.mod-btn-reject  { color: var(--c-danger);  border-color: var(--c-danger);  }

.mod-btn-approve:active { background: var(--c-success); color: var(--c-on-accent); }
.mod-btn-reject:active  { background: var(--c-danger);  color: var(--c-on-accent); }

/* [ПОРЯДОК МОДЕРАЦИИ 2026-08-24] «Принять» у записи блокируется, пока у того же человека
   pending подписка (renderModerationList) — не серый безликий disabled, а приглушённый
   контур, чтобы кнопка не читалась как «не работает вовсе», title объясняет причину. */
.mod-btn-approve:disabled,
.mod-btn-reject:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}
.mod-btn-approve:disabled:active { background: transparent; color: var(--c-success); }

/* --- [BULK] Кнопка «Подтвердить все»: тот же компактный контурный вид, что был в шапке,
   просто вынесена плавающей в правый нижний угол (фикс., поверх списка). Лёгкая тень + фон
   окна — чтобы читалась над карточками; показывается только при наличии заявок. --- */
.mod-approve-all {
    position: fixed;
    right: 16px;
    bottom: calc(16px + env(safe-area-inset-bottom, 0px));
    z-index: 10;
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 3px 9px;
    background: var(--c-bg);
    border: 1px solid var(--c-success);
    border-radius: var(--radius-pill);
    color: var(--c-success);
    box-shadow: var(--th-shadow-1, var(--shadow-1));   /* [DS9 W5] ступень-1 вместо старой шкалы */
    font-size: var(--fs-2xs);
    font-weight: 600;
    cursor: pointer;
    font-family: inherit;
    -webkit-tap-highlight-color: transparent;
    transition: background 0.15s, color 0.15s;
}

.mod-approve-all:active {
    background: var(--c-success);
    color: var(--c-on-accent);
}

/* Запас снизу у списка, чтобы плавающая кнопка не перекрывала действия последней карточки */
#moderation-list {
    padding-bottom: 56px;
}

/* --- [СТРАНИЦА МОИ ЗАПИСИ] --- */

/* Заголовок карточки записи — занимает свободное место, overflow для ellipsis */
/* ============================================================================
   --- [BOOKING CARD] Карточка записи: ВРЕМЯ слева, название+детали справа.
   Базовые стили (фон/паддинг/радиус/:active) — от .event-card; .booking-card задаёт
   раскладку колонок. Дата — отдельный разделитель .date-header (как на графике). --- */
.booking-card {
    align-items: stretch;   /* колонка времени тянется на высоту карточки */
    gap: 14px;
}

/* Левая колонка — время записи одной строкой «ЧЧ:ММ – ЧЧ:ММ» (как .time-badge слота на графике) */
.booking-card-time {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    flex-shrink: 0;
    line-height: 1.2;
}
.bc-time-range {
    font-size: var(--fs-md);
    font-weight: 700;
    color: var(--tg-theme-text-color, #000);
    white-space: nowrap;
}

/* Правый блок — название записи.
   ⚠ justify-content: center ОБЯЗАТЕЛЕН и парен к такому же у .booking-card-time.
   Карточка — flex-строка с align-items: stretch, то есть обе колонки тянутся на её высоту.
   Время центрируется, а название без этой строки прижималось к ВЕРХУ. Пока карточка ровно
   в одну строку, высоты совпадали и расхождения не было видно; как только строка
   становится выше — кнопка «Добавить в календарь»/«Записаться снова» (34px) или запас под
   чип «Идёт сейчас», — название уезжало на 6.4px выше времени (замерено по базовым линиям).
   Именно этот случай и виден авторизованному пользователю: у гостя кнопок нет. */
.booking-card-body {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
    flex: 1;
    min-width: 0;
    overflow: hidden;
}
/* --- [S3 ICS] Кнопка «Добавить в календарь» в карточке записи («Мои записи»/today-окно).
   База — канон .nav-icon-btn (тихая icon-кнопка 34px, hover-подложка); здесь только
   посадка flex-item'ом в карточку. --- */
/* --- [S5 ПОВТОР] Кнопка «Записаться снова» у ПРОШЕДШЕЙ записи — та же посадка,
   что у кнопки календаря (у актуальной записи её место занимает {icon:calendar},
   вместе они не встречаются). --- */
.bc-ics-btn,
.bc-repeat-btn {
    align-self: center;
    margin-left: 0;   /* зазор между колонками даёт gap самой .booking-card */
}
.bc-ics-btn:focus-visible,
.bc-repeat-btn:focus-visible { outline: 2px solid var(--th-focus, var(--c-accent)); outline-offset: 1px; }
/* Единственное активное действие прошедшей карточки выделяем ЦВЕТОМ, а не яркостью.
   [AUDIT 2026-08-27] Раньше карточка гасилась целиком (opacity: .5) и текст падал до 2.9:1;
   теперь приглушает цвет (--th-expired-text), группы прозрачности больше нет.
   Селектор с родителем — не украшение: одноклассовый .bc-repeat-btn проиграл бы по
   порядку следования базовому .nav-icon-btn (объявлен ниже) при равной специфичности. */
.booking-card .bc-repeat-btn {
    color: var(--th-link-accent, var(--c-accent));
}
/* [ICS SAVED] Запись уже выгружена в календарь с этого устройства (localStorage 'ics_saved').
   Селектор с родителем — по той же причине, что у .bc-repeat-btn выше: иначе базовый
   .nav-icon-btn (объявлен ниже) перебьёт цвет при равной специфичности. */
.booking-card .bc-ics-btn.is-saved {
    color: var(--th-success, var(--c-success));
}

/* Название записи — СТРОГО одна строка с троеточием.
   [BOOKING NOW] Раньше был -webkit-line-clamp: 2. Чип «Идёт сейчас» стоял flex-item'ом
   в той же строке (flex-shrink: 0) и отъедал ширину у .booking-card-body → длинное
   название переносилось на второй ряд. Чип уведён из потока (см. .bc-now-chip ниже,
   position: absolute), ширина больше не отбирается, а перенос запрещён явно. */
.booking-title-ellipsis {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    color: var(--tg-theme-text-color);
    font-size: var(--fs-md);
}

/* Истекшая запись — приглушена цветом (см. [AUDIT] выше: opacity роняла контраст текста) */
.booking-card.expired {
    opacity: 1;
    color: var(--th-expired-text, var(--c-expired-text));
}
.booking-card.expired .booking-title-ellipsis {
    color: inherit;
}

/* --- [BOOKING NOW] Текущая (идущая прямо сейчас) запись.
   [DS9 W4] Статус ≠ заливка: нейтральный фон + зелёная статус-полоска 3px (паттерн
   .slot-container::before) + чип «Идёт сейчас». Токен --th-booking-now-bg палитрами
   удалён из палитр ([DS9 W5]). --- */
.event-card.booking-card-now::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: var(--radius-md);     /* совпадает с радиусом карточки */
    border-left: 3px solid var(--th-slot-acc-partial, var(--c-slot-acc-partial));
    pointer-events: none;
}
/* Чип «Идёт сейчас» — ЧИСТЫЙ ТЕКСТ (без рамки и фона) в нижнем правом углу карточки.
   position: absolute → НЕ flex-item: ширину у .booking-card-body не отбирает, название
   получает всю строку. Якорь — .event-card { position: relative }. С полоской
   .booking-card-now::before (inset: 0, только border-left, pointer-events: none)
   не конфликтует. Вертикальный зазор под чип даёт padding-bottom карточки (ниже) —
   без него чип лёг бы на название. */
.bc-now-chip {
    position: absolute;
    right: var(--space-4);      /* = padding карточки → чип по правому краю контента */
    bottom: var(--space-2);
    color: var(--th-slot-acc-partial, var(--c-slot-acc-partial));
    font-size: var(--fs-3xs);
    font-weight: var(--fw-semibold);
    line-height: 1;
    white-space: nowrap;
    pointer-events: none;       /* карточка кликабельна целиком — чип не перехватывает тап */
}
/* Резерв высоты под чип — только у карточки «идёт сейчас».
   Два класса перебивают padding: var(--space-4) у .event-card по специфичности. */
.event-card.booking-card-now {
    padding-bottom: calc(var(--space-4) + var(--space-2));
}

/* --- [HOME BOOKINGS ALL LINK] Ссылка на полную страницу «Мои записи».
   [DS9 W4] Переехала из футера today-окна в строку заголовка «Мои записи» (прототип). --- */
.home-bookings-all-link {
    display: inline-block;
    width: auto;
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    flex-shrink: 0;
    color: var(--th-link-accent, var(--tg-theme-button-color, #2ea6ff));
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    font-family: inherit;
    text-align: right;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
.home-bookings-all-link:hover,
.home-bookings-all-link:active {
    opacity: 0.7;
}

/* Спейсер в конце окна записей на сегодня: переполняет окно, чтобы актуальную запись/надпись
   можно было прокруткой поднять к самому верху (прошедшие уходят за верхний край) даже при
   коротком списке. Высота ≥ высоты окна (= max-height #home-bookings-section). */
.home-bookings-spacer {
    min-height: 33vh;
    min-height: 33dvh;
    flex-shrink: 0;
}

/* --- [МОИ ЗАПИСИ] Неподвижная шапка страницы: top-nav залипает вверху, скроллится только
   список записей. Паттерн как у .home-sticky-top — фон протягивается до краёв экрана через
   отрицательные боковые margin (body padding = 15px), z-index перекрывает карточки. --- */
#my-bookings-view .top-nav {
    position: sticky;
    top: 0;
    z-index: 55;
    background: var(--tg-theme-bg-color, #fff);
    margin-left: -15px;
    margin-right: -15px;
    padding: 6px 15px;
}

/* --- [МОИ ЗАПИСИ] header nav controls: кнопка-календарь + стрелка-возврат --- */
/* Иконки в шапке: прозрачный фон, акцентный цвет (как .sched-scrolltop-btn). */
.mb-cal-btn,
.mb-scrolltop-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    padding: 4px 6px;
    margin-left: 4px;
    background: none;
    border: none;
    border-radius: var(--radius-sm);
    color: var(--th-link-accent, var(--c-accent));
    cursor: pointer;
    transition: background 0.15s, opacity 0.15s;
    -webkit-tap-highlight-color: transparent;
}
.mb-cal-btn svg,
.mb-scrolltop-btn svg { width: 20px; height: 20px; display: block; }
.mb-cal-btn:hover,
.mb-scrolltop-btn:hover { opacity: 0.85; }
.mb-cal-btn:active,
.mb-scrolltop-btn:active { transform: scale(0.92); }
/* Активное состояние — календарь открыт (тематический фон, без хардкода цвета) */
.mb-cal-btn.active {
    background: var(--th-surface, var(--c-surface));
}

/* Контейнер nav-календаря «Мои записи» — зеркало #schedule-nav-calendar */
#my-bookings-nav-calendar {
    padding: 8px 0 16px;
}
#my-bookings-nav-calendar .nav-cal-month:first-child { margin-top: 4px; }

/* Синяя разделительная полоса между прошедшими и актуальными записями */
.bookings-divider {
    width: 100%;
    height: 5px;
    border-radius: var(--radius-pill);
    background-color: var(--th-chip, var(--c-accent));
    margin: var(--space-3) 0;
    flex-shrink: 0;
}

/* --- [КНОПКИ СОЗДАТЬ НА СТРАНИЦЕ МОИ ГРАФИКИ] --- */

/* Кнопка рядом с заголовком */
.btn-create-header {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    background: none;
    border: none;
    color: var(--th-link-accent, var(--c-accent));
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);   /* [DS8] вес — на токен */
    cursor: pointer;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    margin-left: auto;
    -webkit-tap-highlight-color: transparent;
}

.btn-create-header:active {
    background-color: var(--tg-theme-secondary-bg-color, #e5e5ea);
}

.btn-create-icon {
    font-size: var(--fs-xl);           /* [DS8] 20px → токен шкалы */
    font-weight: var(--fw-regular);
    line-height: 1;
}

.btn-create-text {
    line-height: 1;
}

/* Плавающая кнопка в нижнем правом углу.
   [DS9 W4] Круг 56px → квадрат 48px со скруглением и тенью-3 (главное действие экрана,
   эталон прототипа «Поиск/Мои графики»). */
.fab-create-btn {
    position: fixed;
    bottom: 20px;
    right: 20px;
    width: 48px;
    height: 48px;
    border-radius: var(--radius-md);
    border: none;
    background-color: var(--th-fab, var(--c-accent));
    color: var(--tg-theme-button-text-color, #fff);
    font-size: 28px;
    font-weight: 300;
    line-height: 1;
    cursor: pointer;
    box-shadow: var(--th-shadow-3, var(--shadow-3));
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    -webkit-tap-highlight-color: transparent;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.fab-create-btn:active {
    transform: scale(0.92);
    box-shadow: var(--th-shadow-2, var(--shadow-2));   /* [DS9 W5] просадка 3→2 при нажатии */
}

/* --- [КНОПКА УДАЛЕНИЯ ОРАНЖЕВОГО СЛОТА] --- */
.btn-admin-slot-delete {
    background: none;
    border: none;
    font-size: var(--fs-md);
    cursor: pointer;
    padding: 2px 4px;
    opacity: 0.55;
    -webkit-tap-highlight-color: transparent;
    transition: opacity 0.15s;
    flex-shrink: 0;
}

.btn-admin-slot-delete:hover {
    opacity: 1;
}

.btn-admin-slot-delete:active {
    transform: scale(0.88);
}

/* --- [КНОПКА УДАЛЕНИЯ ОРАНЖЕВОГО ДНЯ] --- */
.btn-admin-day-delete {
    background: none;
    border: none;
    font-size: var(--fs-md);
    cursor: pointer;
    padding: 2px 6px;
    opacity: 0.5;
    margin-left: auto;
    -webkit-tap-highlight-color: transparent;
    transition: opacity 0.15s;
    flex-shrink: 0;
}

.btn-admin-day-delete:hover {
    opacity: 1;
}

.btn-admin-day-delete:active {
    transform: scale(0.88);
}

/* --- [DAY ADD MENU] Кнопка ⋮ в полосе активного дня --- */
.btn-day-menu {
    /* --- [DAY MENU ALIGN] ⋮ полосы даты — фикс-бокс с flex-центровкой, как ⋮ в слоте (.sa-menu):
       так глиф вертикально совпадает с шевроном и оба встают в одну колонку со слотами ниже. --- */
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;                                   /* = ширина .sa-menu в правом кластере слота */
    height: 30px;                                  /* = высота .chevron-btn → ⋮ и шеврон на одной линии */
    background: none;
    border: none;
    font-size: var(--fs-lg);
    cursor: pointer;
    padding: 0;
    opacity: 0.9;                                  /* контрастнее — раньше 0.55 был почти не виден */
    color: var(--tg-theme-text-color, #333);       /* текстовый цвет вместо приглушённого hint */
    -webkit-tap-highlight-color: transparent;
    transition: opacity 0.15s;
    flex-shrink: 0;
    margin-left: auto;
}

.btn-day-menu:hover {
    opacity: 1;
}

.btn-day-menu:active {
    transform: scale(0.88);
}

/* --- [КНОПКА РЕДАКТИРОВАНИЯ СЛОТОВ] Заголовок оранжевой секции --- */
.btn-edit-slots {
    background: none;
    border: none;
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--tg-theme-text-color, #333);
    cursor: pointer;
    padding: 2px 6px 2px 0;
    font-family: inherit;
    -webkit-tap-highlight-color: transparent;
    transition: opacity 0.15s;
}

.btn-edit-slots:hover {
    opacity: 0.7;
}

.btn-edit-slots:active {
    opacity: 0.5;
}



/* ==========================================
   [LIGHT THEME CONTRAST] Контрастность светлой темы
   ========================================== */

/* Переменные-дефолты для браузерного просмотра ПЕРЕЕХАЛИ в tokens.css
   [SYSTEM LIGHT CORE] ([DS9 W5]) — единое место для index/login/profile. */

/* --- [SYSTEM DARK PALETTE] Палитра режима «Системная» (тёмная ОС, без data-theme).
   [DS9 W5] СЖАТО: ядро — переопределение --tg-theme-* (его подхватывают токены --c-*
   и все компонентные правила на var()). Ниже остались ТОЛЬКО правила, чьи тёмные
   значения не выводятся из токенов (поверхность инпутов/шапки/кнопок темнее базы).
   Дубли значений компонентов (event-card, слоты, модалки, дропдауны…) удалены —
   их тёмный вид дают --c-surface-1/2/3, --c-border-subtle/strong, --shadow-1/2/3
   из tokens.css @media (prefers-color-scheme: dark). --- */
@media (prefers-color-scheme: dark) {
    /* Ядро (--tg-theme-* + color-scheme) — в tokens.css [SYSTEM DARK CORE] ([DS9 W5]) */

    /* Поля ввода: поверхность (не фон страницы) + рамка border-strong */
    input[type="text"],
    input[type="number"],
    input[type="email"],
    input[type="password"],
    input[type="tel"],
    input[type="date"],
    input[type="time"],
    select,
    textarea {
        background-color: var(--c-surface);
        border-color: var(--c-border-strong);
        color: var(--c-text);
    }

    input[type="text"]::placeholder,
    textarea::placeholder {
        color: var(--c-hint);
    }

    /* Шапка графика: surface-1 (светлее фона страницы) + направленная тень.
       border-color (не -bottom-): рамка идёт по всему периметру скруглённой карточки.
       Оттенок subtle: strong давал слишком тяжёлую линию. */
    .schedule-header {
        background: var(--c-surface-1);
        border-color: var(--c-border-subtle);
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
    }

    /* .modal-body — базового правила фона нет (в отличие от .modal-content) */
    .modal-body {
        background: var(--c-surface-3);
    }

    /* Separator / divider / hr */
    .modal-separator,
    .settings-separator {
        border-color: var(--c-divider);
    }
    hr {
        border-color: var(--c-divider);
    }

    /* Кнопки: вторичная = surface-2 + рамка border-strong */
    .btn-secondary,
    .secondary-btn {
        background: var(--c-surface-2);
        color: var(--c-text);
        border-color: var(--c-border-strong);
    }

    /* Переключатель (выкл.) — переехал в токен --c-slider (tokens.css, @media dark) */

    /* Offline banner */
    #offline-banner {
        background: var(--c-surface-2);
        color: var(--c-text);
        border-bottom-color: var(--c-border-strong);
    }
}

/* ==========================================
   [FORCED THEME] Принудительные темы переключателя вынесены в ОТДЕЛЬНЫЙ файл.
   ------------------------------------------
   Палитры тем (Светлая/Тёмная + шаблоны-копии) и общая «обшивка» компонентов
   теперь живут в themes.css (подключается после style.css в index.html/login.html).
   Там же — инструкция, как добавить свою тему. Здесь оставлены только @media-блоки
   режима «Системная» (см. выше по файлу) и стили UI переключателя (ниже).
   ========================================== */

/* --- [SETTINGS PICKER MODALS] Стили модалок выбора темы и языка --- */

.settings-picker-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 12px 0;
}

.settings-picker-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
    border: 1px solid var(--tg-theme-hint-color, #c0c9d8);
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--tg-theme-text-color, #0d1117);
    cursor: pointer;
    font-size: var(--fs-md);
    text-align: left;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.settings-picker-item:hover {
    border-color: var(--th-menu-active, var(--c-accent));
}

.settings-picker-item.is-active {
    border-color: var(--th-menu-active, var(--c-accent));
    background: var(--th-accent-glow, var(--c-accent-glow)); /* хардкод → токен DS7 */
}

.settings-picker-item .spi-radio {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    border: 2px solid var(--tg-theme-hint-color, #b4bdcf);
    flex-shrink: 0;
    position: relative;
}

.settings-picker-item.is-active .spi-radio {
    border-color: var(--th-menu-active, var(--c-accent));
}

.settings-picker-item.is-active .spi-radio::after {
    content: '';
    position: absolute;
    inset: 3px;
    border-radius: 50%;
    background: var(--th-menu-active, var(--c-accent));
}

/* [GCB FIX] Ёлочка тёмная в светлой теме — перебивает dark-media-query если Telegram light + ОС dark */
@media (prefers-color-scheme: light) {
    .gcb-btn {
        color: var(--tg-theme-text-color, #0d1117);
    }
}

/* Принудительная светлая тема для .gcb-btn вынесена в themes.css (extras темы light). */

/* Страховка на случай, когда data-theme ещё/вообще не проставлен (JS не отработал:
   недоступный localStorage, момент до boot-скрипта). В штатном режиме атрибут стоит
   ВСЕГДА — «Системная» разрешается в light/dark, — и работают extras темы из themes.css. */
body:not([data-theme]) .gcb-btn {
    color: var(--tg-theme-text-color, #111827);
}

/* --- [SYSTEM LIGHT PALETTE] Палитра режима «Системная» (светлая ОС, без data-theme).
   [DS9 W5] СЖАТО: карточки/слоты/модалки/дропдауны берут светлые значения из базовых
   правил на токенах (--c-surface-*, --c-border-*, --shadow-1/2/3 в tokens.css :root).
   Осталось только то, что отличается от базы: рамка инпутов и тень шапки графика. --- */
@media (prefers-color-scheme: light) {
    input[type="text"],
    input[type="number"],
    input[type="email"],
    input[type="password"],
    input[type="tel"],
    input[type="date"],
    input[type="time"],
    select,
    textarea {
        border-color: var(--c-border-strong);
        background-color: var(--c-bg);
    }

    .schedule-header {
        border-color: var(--c-border-subtle);   /* по периметру; strong — слишком тяжело */
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.07);
    }
}


/* ==========================================
   [DESKTOP VIEW] Удобный вид в браузере на ПК / Windows
   ========================================== */
@media (min-width: 600px) {

    /* Градиентный фон, видимый по бокам от «приложения».
       [AUDIT 2026-08-27] Значение — токен (Системная тема; тёмный вариант — @media в tokens.css).
       Выбранную тему накладывают правила html[data-theme] в themes.css. */
    html {
        background: var(--c-desktop-bg);
        min-height: 100vh;
    }

    /* Тело как «телефонный экран» по центру */
    body {
        max-width: 460px;
        margin: 0 auto;
        min-height: 100vh;
        /* Тонкие белые полосы по краям + глубокая тень — эффект «телефона на рабочем столе» */
        box-shadow:
            -1px 0 0 rgba(255, 255, 255, 0.13),
            1px 0 0 rgba(255, 255, 255, 0.13),
            0 0 70px rgba(0, 0, 0, 0.45);
    }

    /* --- [DESKTOP FIXED] Фикс-элементы привязаны к вьюпорту → на десктопе удерживаем их
       в пределах 460px-«телефона», иначе вылезают за боковые границы рамки.
       Селектор body.has-tab-bar .tab-bar (0,2,0) специфичнее базового .tab-bar (0,1,0) —
       иначе базовые left:0/right:0 (ниже в файле) перебили бы наши, а transform остался бы
       и сдвинул бар влево. --- */
    body.has-tab-bar .tab-bar {
        left: 50%;
        right: auto;
        width: 100%;
        max-width: 460px;
        transform: translateX(-50%);   /* центрируем по той же колонке, что и body */
    }
    /* --- [DESKTOP FIX] Панель рокировки (#swap-action-bar) — та же привязка к 460px-«телефону».
       Без неё базовые left:0/right:0 растягивают панель на всё окно браузера (Windows), и кнопки
       подтверждения уходят к правому краю окна, ВНЕ рамки приложения. База .swap-action-bar
       объявлена ВЫШЕ по файлу → этот блок (позже) перебивает её по порядку при равной специфичности. */
    .swap-action-bar {
        left: 50%;
        right: auto;
        width: 100%;
        max-width: 460px;
        transform: translateX(-50%);
    }
    /* FAB «+»: правый край рамки = (50% − полуширина 230px), + 20px внутренний отступ */
    .fab-create-btn {
        right: calc(50% - 210px);
    }

    /* --- Hover-состояния для мыши --- */
    .menu-card {
        transition: transform 0.17s ease, box-shadow 0.17s ease;
    }

    .menu-card:hover {
        transform: translateY(-2px);
        box-shadow: var(--th-shadow-2, var(--shadow-2));   /* [DS9 W5] ступень-2 вместо литерала */
    }

    .event-card {
        transition: box-shadow 0.15s ease;
    }

    .event-card:hover {
        box-shadow: var(--th-shadow-2, var(--shadow-2));   /* [DS9 W4] ступень-2 вместо литерала */
    }

    /* .btn-primary и .btn-danger используют brightness-hover (BTN STATES DS7) — исключаем их */
    :where(.btn:not(.btn-primary):not(.btn-danger)):not(:disabled):hover {
        opacity: 0.88;
    }

    .nav-back-btn:hover,
    .sched-back-btn:hover {
        opacity: 0.7;
    }

    .dropdown-item:hover {
        background-color: var(--tg-theme-secondary-bg-color, #efeff4);
    }

    .checkbox-btn:hover {
        opacity: 0.82;
    }

    .contact-item:hover {
        opacity: 0.85;
    }

    /* --- Фокус на полях ввода — правила перенесены в глобальный блок INPUT STATES (DS7) --- */

    /* --- Полноэкранные модалы — ограничиваем шириной «телефона» --- */

    /* contacts-modal-fullscreen использует !important, поэтому перебиваем тоже */
    .contacts-modal-fullscreen {
        max-width: 460px !important;
        width: 100% !important;
        height: 90vh !important;
        max-height: 90vh !important;
        border-radius: var(--radius-lg) !important;
    }
}

/* [AUDIT 2026-08-27] Отдельный desktop-dark блок удалён: тёмный вариант приходит
   значением --c-desktop-bg (tokens.css, @media dark), а выбранная тема — html[data-theme]. */

/* ==========================================
   [INBOX UI S19] Bell + modal + tabs
   ========================================== */

/* --- Bell button: УДАЛЁН вместе с колокольчиком шапки (вход в inbox = вкладка таб-бара).
   Правила #bell-btn (сам круг, :hover/:active и `+ .user-avatar-wrap`) снесены 2026-08-07
   как мёртвые: элемента с таким id в разметке нет ни на одной странице.
   ⚠ Класс .bell-btn ЖИВ и к этому отношения не имеет — это #bookings-bell-btn
   («отключить уведомления» в «Моих записях»), его правила выше по файлу. --- */

/* --- [SETTINGS BTN] Шестерёнка возле логотипа --- */
.settings-btn {
    background: none;
    border: none;
    cursor: pointer;
    padding: 4px;
    /* [DS9 W4] Служебная иконка — тихий вторичный цвет (был фирменный --c-brand) */
    color: var(--tg-theme-hint-color, #8e8e93);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-left: auto;
    flex-shrink: 0;
    transition: opacity 0.2s;
    -webkit-tap-highlight-color: transparent;
}
.settings-btn:active {
    transform: scale(0.88);
}
.settings-btn svg {
    width: 20px;
    height: 20px;
}

.bell-badge {
    position: absolute;
    top: -2px;
    right: -2px;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: var(--radius-sm);
    background: var(--c-danger);
    color: var(--c-on-accent);
    font-size: var(--fs-2xs);
    font-weight: 700;
    line-height: 18px;
    text-align: center;
    box-sizing: border-box;
    box-shadow: 0 0 0 2px var(--tg-theme-bg-color, #fff);
}
.bell-badge.hidden {
    display: none;
}

/* ============================================================================
   --- [BOTTOM TAB BAR (DS4)] Нижний таб-бар: Главная · Мои графики · Поиск · Уведомления ---
   Фиксирован внизу, виден только на 4 корневых экранах (управляется syncTabBar()
   в app_navigation.js через класс .hidden + body.has-tab-bar). Только токены.
   ============================================================================ */
.tab-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 90;                 /* выше контента, ниже модалок (1000+) и чата (9999) */
    display: flex;
    align-items: stretch;
    background: var(--c-bg);
    border-top: 1px solid var(--c-surface);
    padding-bottom: env(safe-area-inset-bottom, 0px);  /* iPhone X+ / Android gesture nav */
}
.tab-bar.hidden { display: none; }
/* [DS10] Экранная клавиатура для ввода текста — таб-бар прячем целиком, а не просто
   задвигаем под неё (детектор — _evalKeyboardOpen в app_navigation.js). */
body.keyboard-open .tab-bar { display: none; }

.tab-btn {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    /* [DS] flex-start (а не center): иконки выравниваются по ВЕРХУ. Бар по высоте тянется
       к самой высокой кнопке (align-items:stretch родителя), поэтому при переносе подписи
       на 2-ю строку у длинной локали все иконки остаются на одном уровне — симметрия не рвётся. */
    justify-content: flex-start;
    gap: 2px;
    min-height: var(--control-h);    /* 44px тач-цель (WCAG 2.5.5) */
    padding: 6px 2px;
    background: none;
    border: none;
    cursor: pointer;
    color: var(--c-hint);            /* неактивный — приглушённый */
    font-family: inherit;
    -webkit-tap-highlight-color: transparent;
    transition: color 0.15s, transform 0.1s;
}
.tab-btn:active { transform: scale(0.94); }
.tab-btn.tab-btn-active { color: var(--th-tab-active, var(--c-accent)); }   /* активный — акцент */

/* Обёртка иконки: position:relative — якорь для бейджа непрочитанных */
.tab-ico {
    position: relative;
    display: inline-flex;
    line-height: 1;
}
.tab-ico .icn svg { width: 24px; height: 24px; }

.tab-lbl {
    font-size: var(--fs-2xs);
    font-weight: var(--fw-medium);
    line-height: 1.1;
    text-align: center;   /* перенос на 2-ю строку центрируется под иконкой */
}

/* Бейдж непрочитанных на вкладке «Уведомления» (визуально как прежний .bell-badge) */
.tab-badge {
    position: absolute;
    top: -5px;
    right: -9px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: var(--radius-pill);
    background: var(--c-danger);
    color: var(--c-on-accent, #fff);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    line-height: 16px;
    text-align: center;
    box-sizing: border-box;
    box-shadow: 0 0 0 2px var(--c-bg);
}
.tab-badge.hidden { display: none; }

/* Контент не должен прятаться под фиксированным таб-баром (класс ставит syncTabBar).
   --tab-bar-h — плоский px; реальную высоту с safe-area публикует JS (_publishTabBarHeight,
   offsetHeight включает padding-bottom env). Без вложенного calc — иначе свойство сбросится. */
body.has-tab-bar {
    padding-bottom: calc(var(--tab-bar-h) + 12px);
}
/* FAB «+» на «Мои графики» поднимается над таб-баром, чтобы не перекрывался */
body.has-tab-bar .fab-create-btn {
    bottom: calc(20px + var(--tab-bar-h));
}
/* [FAB CLEARANCE] Свободная полоса под списком «Мои графики». Без неё последняя карточка
   (и кнопки на ней) упирается в нижний край прокрутки ровно там, где висит плавающий «+»,
   и её не прочитать: padding-bottom у body рассчитан только на таб-бар, про FAB он не знает.
   76px = 20 (отступ FAB от низа) + 48 (высота FAB) + 8 (воздух). Работает в обоих режимах:
   с таб-баром запас отсчитывается от уже поднятого края, без него — от padding body. */
#my-events-view {
    padding-bottom: 76px;
}

/* ============================================================================
   --- [HOME LAYOUT] Главный экран = фикс-высоты flex-колонка от верха страницы РОВНО до
   верхней границы таб-бара (gap 0 — зоны граничат). Высота = 100dvh минус верхний padding
   body минус высота таб-бара. БЕЗ вложенных calc: --tab-bar-h — плоский px (JS публикует
   реальную высоту с safe-area), иначе calc-внутри-calc через var сбрасывался и strip уезжал
   под таб-бар. Три зоны со своим пространством, которые НЕ накладываются и граничат друг с другом:
     1) .home-sticky-top      — неподвижная шапка (лого/описание/«Мои записи»), не сжимается;
     2) #home-bookings-section — окно записей НА СЕГОДНЯ: ≈ половина свободного пространства
        (кап 33dvh), размер по контенту, своя зона скролла при переполнении (прошедшие за верхом);
     3) #android-install-strip — иконки Поделиться/Android, прижаты к низу = граничат с таб-баром.
     4) #home-cta            — карточка первичного действия «Создать график», прижата к низу.
   Свободное место колонки забирает ИМЕННО #home-cta (margin-top:auto), а strip идёт следом
   без auto — два auto-марджина в одной flex-колонке делят остаток ПОПОЛАМ и раскрыли бы дыру
   между ними. --- */
body.has-tab-bar #home-view:not(.hidden) {
    display: flex;
    flex-direction: column;
    /* Высота = РЕАЛЬНАЯ видимая высота − верхний padding(15px) − высота таб-бара.
       [КЛЮЧЕВОЕ] В установленном PWA (standalone) на Android CSS-юниты 100svh/dvh/vh включают зону
       системной жест-навигации: замерено на устройстве window.innerHeight=753, а 100svh≈810 (+57px
       = высота нав-панели). Фикс. таб-бар (position:fixed; bottom:0) визуально привязан к innerHeight
       (753), поэтому колонка на svh/dvh перерастала на 57px и strip Поделиться/Android уезжал ПОД бар
       («иконки исчезают в приложении, но не во вкладке» — в браузерной вкладке dvh≈innerHeight).
       Решение: берём видимую высоту из JS — --app-vh = window.innerHeight (публикует _publishAppVh
       на старте/resize/orientation/visualViewport). Фолбэк 100dvh — только до первого замера JS
       (на десктопе/во вкладке dvh≈innerHeight, там и так корректно). */
    height: calc(100dvh - 15px - var(--tab-bar-h));                  /* фолбэк до JS-замера --app-vh */
    height: calc(var(--app-vh, 100dvh) - 15px - var(--tab-bar-h));   /* реальная видимая высота (standalone-safe) */
}
/* Главная фикс-высоты и не растекается под таб-бар → её ветке body нижний резерв НЕ нужен,
   иначе документ стал бы выше вьюпорта и появился бы паразитный скролл. Остальные корневые
   экраны (Мои графики/Поиск/Уведомления) скроллятся обычным потоком — им резерв сохраняется. */
body.has-tab-bar.home-active {
    padding-bottom: 0;
}
#home-view .home-sticky-top {
    flex: 0 0 auto;            /* шапка не сжимается — её зона приоритетна */
}
#home-bookings-section {
    flex: 0 1 auto;            /* размер по контенту (короткий список → маленькое окно), не растягивается */
    min-height: 0;             /* критично: позволяет flex-элементу сжиматься и скроллиться */
    max-height: 33vh;          /* fallback для движков без dvh */
    max-height: 33dvh;         /* окно записей на сегодня — ≈ половина свободного пространства */
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}
#android-install-strip {
    flex: 0 0 auto;
    margin-top: 0;             /* к низу группу прижимает #home-cta (см. [HOME CTA]), не strip */
    text-align: right;         /* иконки Поделиться/Android — по правому краю (как аватар/колокольчик) */
    padding: 6px 0 8px;        /* справа 0 → на одной вертикали с шапкой; снизу 8px — зазор над таб-баром */
}

/* ============================================================================
   --- [HOME CTA] Карточка первичного действия главной: «Создать график».
   Поверхность/рамка/тень — как у .about-section и .pitch-card (единая карточка контента DS9).
   margin-top:auto забирает всё свободное место колонки, поэтому группа [CTA + strip] прижата
   к низу — зона большого пальца. auto живёт ИМЕННО здесь, а не на strip: strip может быть
   скрыт (display:none до showHomeView), и тогда прижимать группу было бы нечем.
   Вне body.has-tab-bar (#home-view — обычный блок) auto в блочном потоке = 0, карточка просто
   идёт в потоке. --- */
#home-cta {
    flex: 0 0 auto;
    margin-top: auto;
    margin-bottom: var(--space-3);
    padding: 14px 16px;
    background: var(--th-surface-1, var(--c-surface-1));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
    border-radius: var(--radius-lg);
}

/* Заголовок-обещание: называет результат и его цену во времени, а не описывает продукт
   (это делает презентация выше). */
.home-cta-title {
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-tight);
    color: var(--tg-theme-text-color, #000);
}

/* Микрокопия снятия барьера: перечисляет ровно три поля и обещает обратимость —
   главный страх новичка не «сложно», а «вдруг сделаю не так». */
.home-cta-sub {
    margin-top: 4px;
    font-size: var(--fs-sm);
    line-height: var(--lh-normal);
    color: var(--tg-theme-hint-color, #8e8e93);
}

/* --- Строка [текст | иллюстрация] карточки CTA -------------------------------
   align-items:flex-end — фигура «стоит» на одной базовой линии с последней строкой
   микрокопии, а не висит по центру: она обрезана по бёдра и низ должен быть опорным. */
.home-cta-row {
    display: flex;
    align-items: flex-end;
    gap: var(--space-3);
}
.home-cta-copy {
    flex: 1 1 auto;
    min-width: 0;              /* иначе длинное слово распирает колонку и жмёт иллюстрацию */
}
/* Ширина фиксирована в px, а не в %: карточка обязана иметь постоянную высоту, иначе фигура
   поедет при смене состояния главной. 100px при пропорции исходника 0.626 → 160px высоты. */
.home-cta-art {
    flex: 0 0 auto;
    width: 100px;
    line-height: 0;            /* снимает нижний inline-зазор под <img> */
}
.home-cta-art img,
.pitch-hero-art img {
    display: block;
    width: 100%;
    height: auto;
    /* Низ фигуры растворяем намеренно: исходник обрезан по бёдра (иначе читается как обрубок),
       а её светлые брюки на светлой теме сливаются с фоном. Маска — не цвет, палитра тем
       (--th-*) здесь не нужна и работает одинаково во всех четырёх темах. */
    -webkit-mask-image: linear-gradient(to top, transparent 0, #000 14%);
    mask-image: linear-gradient(to top, transparent 0, #000 14%);
}
/* Низкие экраны: карточка не должна вытеснять зону контента. Размер фиксирован для
   устройства и не меняется в ходе работы — «скачков» это не создаёт. */
@media (max-height: 640px) {
    .home-cta-art { width: 72px; }
}
/* Очень узкие (≤340px): без этого микрокопии остаётся 142px и она уходит в 4 строки. */
@media (max-width: 340px) {
    .home-cta-art  { width: 84px; }
    .pitch-hero-art { width: 80px; }
}

.home-cta-btn {
    margin-top: var(--space-3);   /* перебивает margin-top:--space-4 из .btn-primary */
    min-height: var(--control-h);
    /* Своя заливка (перебивает --th-btn-bg из .btn-primary): главная точка входа
       регулируется в редакторе тем отдельной строкой «Кнопка "Создать график"». */
    background-color: var(--th-cta-create, var(--c-cta-create));
}

/* --- [S23] Inbox: «Очистить» в шапке top-nav (вместо ✓ Все / ✕ из модалки).
   [DS9] Минимализм: тихая текстовая кнопка-действие без контура (акцентный текст,
   ховер — лёгкий акцентный фон) — как icon-кнопки шапки «Мои записи» (.mb-cal-btn). --- */
.inbox-clear-btn {
    background: transparent;
    border: none;
    color: var(--th-link-accent, var(--c-accent));
    border-radius: var(--radius-sm);
    padding: 6px 10px;
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    cursor: pointer;
    transition: background 0.15s;
    -webkit-tap-highlight-color: transparent;
}
.inbox-clear-btn:hover {
    background: var(--th-accent-glow, var(--c-accent-glow));
}
.inbox-clear-btn:active { opacity: 0.85; }
.inbox-clear-btn:disabled {
    opacity: 0.4;
    cursor: default;
}

/* --- [INBOX STICKY NAV] Неподвижная шапка страницы уведомлений при прокрутке списка —
   канон #owner-members-view .top-nav (sticky + фон страницы + компенсация бокового
   паддинга отрицательными margin, чтобы строки не выглядывали по краям). --- */
#inbox-view .top-nav {
    position: sticky;
    top: 0;
    z-index: 55;
    background: var(--tg-theme-bg-color, #fff);
    margin-left: calc(-1 * var(--space-4));
    margin-right: calc(-1 * var(--space-4));
    padding: var(--space-2) var(--space-4);
    margin-bottom: var(--space-2);
}

/* --- [NAV ICON BTN] Компактная icon-кнопка в шапке (например «Заблокированные» в уведомлениях) --- */
.nav-icon-btn {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    /* [BLOCKED] Кнопка — крайняя справа в шапке (после «Очистить»), поэтому зазор слева, а не справа,
       чтобы она стояла ровно у правого края во ВСЕХ режимах (в т.ч. когда «Очистить» скрыта). */
    margin-left: 6px;
    padding: 0;
    border: none;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--tg-theme-hint-color, #8e8e93);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background 0.15s, color 0.15s;
}
.nav-icon-btn:hover { background: var(--th-surface, var(--c-surface)); }
.nav-icon-btn:active { opacity: 0.85; }
.nav-icon-btn svg { width: 20px; height: 20px; }

/* [NAV ICON BTN — EDGE] Модификатор для icon-кнопок наравне с соседними текстовыми
   действиями шапки (напр. «Заблокированные» рядом с «Очистить»): [DS9] тот же тихий
   акцент без контура, что и .inbox-clear-btn — пара читается едино. */
.nav-icon-btn--edge {
    color: var(--th-link-accent, var(--c-accent));
}
.nav-icon-btn--edge:hover {
    background: var(--th-accent-glow, var(--c-accent-glow));
}
.nav-icon-btn--edge:active { opacity: 0.85; }

/* [DS10] Активный фильтр (радиус > 0 / выбран конкретный раздел) — акцентная подложка
   на самой иконке шапки поиска, тем же токеном glow, что и .cat-dropdown-list.is-selected. */
.nav-icon-btn--active {
    color: var(--th-menu-active, var(--c-accent));
    background: var(--th-accent-glow, var(--c-accent-glow));
}
.nav-icon-btn--active:hover {
    background: var(--th-accent-glow, var(--c-accent-glow));
}

/* [BLOCKED USERS ГЛИФ] «user-x»: силуэт пользователя + ✕. [DS9] Монохром —
   stroke="currentColor" в самом SVG (цвет задаёт кнопка), CSS только размер. */
.bu-glyph { width: 22px; height: 22px; }

/* --- [BLOCKED USERS] Строки экрана личного списка блокировок --- */
.blocked-user-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--th-divider, var(--tg-theme-secondary-bg-color, #efeff4));
}
.blocked-user-ava {
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    object-fit: cover;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--th-surface, var(--c-surface));
    color: var(--tg-theme-hint-color, #8e8e93);
}
.blocked-user-ava-ph svg { width: 22px; height: 22px; }
.blocked-user-info {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex-grow: 1;
}
.blocked-user-name {
    font-size: var(--fs-md);
    font-weight: var(--fw-medium);
    color: var(--tg-theme-text-color);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.blocked-user-sub {
    font-size: var(--fs-xs);
    color: var(--tg-theme-hint-color, #8e8e93);
}
.blocked-user-unblock {
    flex-shrink: 0;
    background: transparent;
    border: 1px solid var(--th-fab, var(--c-accent));
    color: var(--th-fab, var(--c-accent));
    border-radius: var(--radius-sm);
    padding: 6px 12px;
    font-size: var(--fs-sm);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
.blocked-user-unblock:hover { background: var(--th-fab, var(--c-accent)); color: var(--c-on-accent); }
.blocked-user-unblock:active { opacity: 0.85; }
.blocked-user-unblock:disabled { opacity: 0.4; cursor: default; }

/* --- Tabs ([DS9 W5] сегмент-контрол: трек surface-2, активная = surface-1 + shadow-1) --- */
.inbox-tabs {
    display: flex;
    gap: var(--space-1);
    margin: var(--space-2) var(--space-3);
    padding: var(--space-1);
    background: var(--th-surface-2, var(--c-surface-2));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    border-radius: var(--radius-md);
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}
.inbox-tab {
    flex: 1 1 auto;
    background: transparent;
    border: none;
    color: var(--tg-theme-hint-color, var(--c-hint));
    padding: 6px 10px;
    border-radius: var(--radius-sm);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    cursor: pointer;
    transition: background 0.18s, color 0.18s, box-shadow 0.18s;
    white-space: nowrap;
    -webkit-tap-highlight-color: transparent;
}
.inbox-tab-active {
    background: var(--th-surface-1, var(--c-surface-1));
    color: var(--tg-theme-text-color, var(--c-text));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
}

/* [ЗАДАЧА 2] Точка-индикатор «есть непрочитанные в этом табе».
   Ставится из app_inbox.js::_updateTabDots при открытии inbox-view и по SSE.
   На активном табе не показывается (он уже открыт и помечается прочитанным). */
.inbox-tab {
    position: relative;
}
.inbox-tab-has-unread::after {
    content: '';
    position: absolute;
    top: 4px;
    right: 6px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--tg-theme-destructive-text-color, var(--c-danger));
    box-shadow: 0 0 0 2px var(--th-surface-2, var(--c-surface-2));
    pointer-events: none;
}

/* --- List --- */
.inbox-list {
    padding: 10px 14px 20px;
    overflow-y: auto;
    flex-grow: 1;
    -webkit-overflow-scrolling: touch;
}
/* Старый .inbox-empty объединён с .empty-state (DS7) — см. ниже */

/* [DS9 W5] Строка уведомления = нейтральная карточка surface-1 + рамка + тень-1.
   Непрочитанное — ТОЧКА слева (::before), а не цветная кайма/заливка. */
.inbox-item,
.inbox-chat-item {
    position: relative;
    background: var(--th-surface-1, var(--c-surface-1));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
    border-radius: var(--radius-md);
    padding: 10px 12px 10px 28px;
    margin-bottom: 8px;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}
/* Точка статуса: у всех строк (прочитанная — пустой контур, место сохраняется) */
.inbox-item::before,
.inbox-chat-item::before {
    content: '';
    position: absolute;
    left: 11px;
    top: 16px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    border: 1px solid var(--th-border-strong, var(--c-border-strong));
    pointer-events: none;
}
/* --- [CHAT AVATAR] Картинка слева в карточке чата: аватар собеседника или обложка графика.
   Флексом становится ТОЛЬКО чат-карточка — .inbox-item на вкладках «Другие»/«Напоминания»
   остаётся вертикальным потоком. Крестик .inbox-item-del, бейдж .inbox-chat-unread и точка
   статуса ::before позиционируются абсолютно от .inbox-chat-item, смена потока их не трогает. */
.inbox-chat-item {
    display: flex;
    align-items: center;
    gap: 10px;
}
.inbox-chat-main {
    flex: 1 1 auto;
    min-width: 0;   /* без этого ellipsis у заголовка не сработает: flex-элемент не сжимается */
}
/* Круг = человек (личный чат). Размеры и палитра — как у канонического .contact-avatar. */
.inbox-chat-ava,
.inbox-chat-ava-img {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    flex-shrink: 0;
}
.inbox-chat-ava {
    background-color: var(--th-avatar-bg, var(--c-accent));
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
}
.inbox-chat-ava-img {
    object-fit: cover;
    display: block;
}
/* Квадрат = график (общий чат графика и его поддержка): тот же .event-card-thumb, но 40px. */
.inbox-chat-thumb {
    width: 40px;
    height: 40px;
}

/* --- [S23] Swipe-to-delete wrapper: красный фон-«мусорка» под карточкой ---
   Карточка лежит в .inbox-row, переезжает translateX при свайпе вправо. */
.inbox-row {
    position: relative;
    margin-bottom: 8px;
    overflow: hidden;
    border-radius: var(--radius-md);
    /* [DS9 W5] В покое подложка ПРОЗРАЧНА: постоянный красный фон просвечивал через
       полупрозрачную рамку/скругления карточки розовым ореолом (светлая тема).
       Красный — только когда он функционально виден: во время свайпа и удаления. */
    background: transparent;
    /* Тень-1 несёт врапер (не карточка) — иначе тень ложилась бы на красную подложку */
    box-shadow: var(--th-shadow-1, var(--shadow-1));
}
/* [FIX МОРГАНИЕ] Красный держится ОДНИМ непрерывным слоем: от касания и до схлопывания
   строки. Раньше красили .inbox-row-swiping/.inbox-row-removing, а между ними класс
   снимался на pointerup — пока шёл запрос удаления, подложка была прозрачной, и красный
   успевал погаснуть и зажечься снова («моргал»). Теперь заливку несёт отдельный
   .inbox-row-armed (живёт весь жест), а .inbox-row-swiping отвечает ТОЛЬКО за
   отключение transition на время следования за пальцем. */
.inbox-row.inbox-row-armed,
.inbox-row.inbox-row-removing {
    background: var(--c-danger);
}
.inbox-row::before {
    content: '🗑 Удалить';
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    padding-left: 18px;
    color: var(--c-on-accent);
    font-weight: 600;
    font-size: var(--fs-sm);
    letter-spacing: 0.2px;
    pointer-events: none;
}
.inbox-row > .inbox-item,
.inbox-row > .inbox-chat-item,
.inbox-row > .owner-sched-card {   /* [ЗАДАЧА 4] свайп-удаление закрытых жалоб */
    margin-bottom: 0;
    box-shadow: none;   /* [DS9 W5] тень несёт .inbox-row — см. комментарий выше */
    touch-action: pan-y;
    transition: transform 0.18s ease-out;
    will-change: transform;
}
.inbox-row.inbox-row-swiping > .inbox-item,
.inbox-row.inbox-row-swiping > .inbox-chat-item,
.inbox-row.inbox-row-swiping > .owner-sched-card {
    transition: none;
}
.inbox-row.inbox-row-removing {
    transition: max-height 0.22s ease-out, margin 0.22s ease-out, opacity 0.22s ease-out;
    max-height: 200px;
    opacity: 1;
}
.inbox-row.inbox-row-removing.inbox-row-removed {
    max-height: 0;
    margin-bottom: 0;
    opacity: 0;
}
/* --- [S23] Крестик удаления в правом верхнем углу карточки ---
   Глиф вдвое крупнее базового (calc(--fs-md * 2)), сам бокс прижат вплотную к углу
   (top/right: 0) — центр «×» смещён к углу карточки. Цвет — отдельный токен палитры
   --th-card-del: почти сливается с фоном карточки, чтобы не перетягивать внимание;
   контраст поднимается только на :hover/:focus. */
.inbox-item-del {
    position: absolute;
    top: 0;
    right: 0;
    width: 30px;
    height: 30px;
    border: none;
    background: transparent;
    color: var(--th-card-del, var(--c-card-del));
    font-size: calc(var(--fs-md) * 2);
    line-height: 1;
    cursor: pointer;
    border-radius: 50%;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    -webkit-tap-highlight-color: transparent;
    transition: color 0.15s ease-out, background 0.15s ease-out;
    z-index: 2;
}
.inbox-item-del:hover,
.inbox-item-del:focus {
    background: var(--th-hover, rgba(0, 0, 0, 0.08));
    color: var(--tg-theme-text-color, #1c1c1e);
}
.inbox-item-del:active {
    color: var(--tg-theme-text-color, #1c1c1e);
    transform: scale(0.92);
}
.inbox-item-unread::before,
.inbox-chat-item-unread::before {
    background: var(--th-unread, var(--c-accent));
    border-color: var(--th-unread, var(--c-accent));
}
/* --- [INBOX DEEP-LINK FLASH] Подсветка карточки, на которую привёл тап по push-уведомлению
   (Android: кнопок в системном уведомлении нет → ведём на карточку с кнопками accept/decline).
   Inset-кольцо — чтобы не клипалось overflow:hidden у .inbox-row; цвет — токен темы. --- */
.inbox-row-flash > .inbox-item {
    animation: inboxFlash 1.8s ease-out;
}
@keyframes inboxFlash {
    0%, 60% { box-shadow: inset 0 0 0 2px var(--th-focus, var(--c-accent)); }
    100%    { box-shadow: inset 0 0 0 0 transparent; }
}

.inbox-item-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 8px;
    margin-bottom: 4px;
}
/* --- [S23] Уведомления и напоминания имеют крестик «×» в правом верхнем углу
   (.inbox-item-del). Резервируем место в первой строке, чтобы time-tag не лез под него. */
.inbox-item .inbox-item-row,
.inbox-chat-item .inbox-item-row {
    padding-right: 30px;
}
.inbox-item-title {
    font-weight: 600;
    font-size: var(--fs-sm);
    color: var(--tg-theme-text-color, #000);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1 1 auto;
    min-width: 0;
}
.inbox-item-time {
    font-size: var(--fs-2xs);
    color: var(--tg-theme-hint-color, #8e8e93);
    flex-shrink: 0;
}
/* [FIX] Карточка чата раздувалась по высоте, если последнее сообщение было длинным
   (текст рос произвольно). Клэмп на 2 строки — как .home-desc-clamped — держит
   ОДИНАКОВУЮ высоту у всех карточек колокольчика независимо от длины текста. */
.inbox-item-body {
    font-size: var(--fs-sm);
    color: var(--tg-theme-text-color, #1c1c1e);
    line-height: 1.35;
    word-wrap: break-word;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
/* [S23] .inbox-item-kind — технический бейдж "swap_request" / "reminder_*" — скрыт
   от пользователя. JS больше не рендерит этот узел, селектор оставлен на случай,
   если где-то остался старый кэшированный fragment. */
.inbox-item-kind { display: none !important; }

/* [S23 ACTIONS] Кнопки ✅/❌ под телом уведомления для notif'ов с payload.actions
   (swap_request, reminder_confirm_request). Логика клика — app_inbox.js::_handleInboxAction. */
.inbox-item-actions {
    margin-top: 10px;
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}
.inbox-item-action {
    flex: 1 1 auto;
    min-width: 96px;
    padding: 8px 12px;
    /* [DS9] DS9-токены вместо легаси tg-theme-* (surface-2 + рамка-subtle) */
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    border-radius: var(--radius-sm);
    background: var(--th-surface-2, var(--c-surface-2));
    color: var(--tg-theme-text-color, #1c1c1e);
    font-size: var(--fs-sm);
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s, opacity 0.15s, transform 0.05s;
}
.inbox-item-action:hover  { background: var(--th-fab, var(--c-accent)); color: var(--c-on-accent); border-color: transparent; }
.inbox-item-action:active { transform: scale(0.97); }
.inbox-item-action:disabled,
.inbox-item-action[disabled] { opacity: 0.5; cursor: default; }

/* [ЗАДАЧА 1] «Выбранная» кнопка — единственная по центру карточки взамен пары
   actions[]. Рендерится из _handleInboxAction (мгновенно после клика) и при
   повторном открытии inbox (бэкенд хранит payload.action_chosen). */
.inbox-item-actions-chosen {
    justify-content: center;
}
.inbox-item-action-chosen {
    flex: 0 1 auto;
    min-width: 120px;
    background: var(--th-fab, var(--c-accent));
    color: var(--tg-theme-button-text-color, #fff);
    border-color: transparent;
    opacity: 1;          /* перекрываем :disabled opacity:0.5 — кнопка выглядит как принятый ответ */
    cursor: default;
}
.inbox-item-action-chosen:hover {
    background: var(--th-fab, var(--c-accent));
    color: var(--tg-theme-button-text-color, #fff);
    border-color: transparent;
}
.inbox-item-action-chosen:disabled,
.inbox-item-action-chosen[disabled] {
    opacity: 1;
}
.inbox-chat-unread {
    /* [FIX] Раньше badge лежал в top-right поверх крестика «×» и блокировал клики/свайп.
       Сейчас сдвинут в нижний правый угол + pointer-events: none, чтобы тапы и pan-жесты
       свободно доходили до карточки и кнопки удаления. */
    position: absolute;
    bottom: 10px;
    right: 10px;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: var(--radius-sm);
    background: var(--th-unread, var(--c-accent));
    color: var(--c-on-accent);
    font-size: var(--fs-2xs);
    font-weight: 700;
    line-height: 18px;
    text-align: center;
    box-sizing: border-box;
    pointer-events: none;
}

/* ==========================================
   [CHAT UI S20] Полноэкранный chat-view (открытие thread'а)
   ========================================== */

/* --- Контейнер chat-модалки: flex column, занимает весь viewport, безопасные зоны на mobile --- */
.chat-modal-content.contacts-modal-fullscreen {
    display: flex;
    flex-direction: column;
    height: 100%;
    overflow: hidden;
    padding: 0;
}

/* --- Шапка ([DS9 W5] surface-1 + тонкая рамка — как шапка графика) --- */
.chat-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    background: var(--th-surface-1, var(--c-surface-1));
    flex-shrink: 0;
}
.chat-back-btn,
.chat-menu-btn {
    background: none;
    border: none;
    font-size: 20px;
    color: var(--tg-theme-text-color, #1c1c1e);
    cursor: pointer;
    padding: 6px 8px;
    line-height: 1;
    -webkit-tap-highlight-color: transparent;
}
/* [DS] Стрелка «назад» в чате — синяя, как .nav-back-btn в остальных окнах */
.chat-back-btn {
    color: var(--th-link-accent, var(--c-accent));
}
.chat-back-btn:hover {
    opacity: 0.7;
}
.chat-menu-btn:hover {
    color: var(--th-link-accent, var(--c-accent));
}
.chat-header-info {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.chat-title {
    margin: 0;
    font-size: var(--fs-md);
    font-weight: 600;
    color: var(--tg-theme-text-color, #000);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.chat-subtitle {
    font-size: var(--fs-xs);
    color: var(--tg-theme-hint-color, #8e8e93);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.chat-header-actions {
    display: flex;
    align-items: center;
    flex-shrink: 0;
    position: relative;
}
/* --- Дропдаун в шапке чата --- */
.chat-header-actions .dropdown-menu {
    right: 0;
    top: calc(100% + 4px);
}

/* --- Лента сообщений ([DS9 W5] фон страницы; пузыри несут поверхность сами) --- */
.chat-messages {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 12px 14px;
    background: var(--tg-theme-bg-color, var(--c-bg));
    -webkit-overflow-scrolling: touch;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
/* --- CHAT: сообщения прижаты к нижнему краю (Telegram-style). Первый реальный
   блок съедает свободное место сверху через margin-top:auto — при малом числе
   сообщений они висят внизу, при переполнении верх прокручивается без обрезки
   (в отличие от justify-content:flex-end). .chat-empty не затронут — свой margin:auto. */
.chat-messages > .chat-day-divider:first-child,
.chat-messages > .chat-message:first-child {
    margin-top: auto;
}
.chat-empty {
    margin: auto;
    text-align: center;
    color: var(--tg-theme-hint-color, #8e8e93);
    font-size: var(--fs-sm);
    padding: 30px 12px;
    line-height: 1.5;
}

/* --- Day divider ([DS9 W5] чип surface-2 + рамка — вместо полупрозрачного хардкода) --- */
.chat-day-divider {
    align-self: center;
    background: var(--th-surface-2, var(--c-surface-2));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    color: var(--tg-theme-hint-color, #8e8e93);
    font-size: var(--fs-2xs);
    font-weight: 600;
    padding: 3px 10px;
    border-radius: var(--radius-pill);
    margin: 10px 0 4px;
    text-transform: uppercase;
    letter-spacing: 0.4px;
}

/* --- Сообщение (контейнер ряда) --- */
.chat-message {
    display: flex;
    flex-direction: column;
    max-width: 78%;
    margin: 2px 0;
}
.chat-message-mine {
    align-self: flex-end;
    align-items: flex-end;
}
.chat-message-other {
    align-self: flex-start;
    align-items: flex-start;
}
.chat-message-name {
    font-size: var(--fs-2xs);
    font-weight: 600;
    color: var(--th-chat-accent, var(--c-accent));
    margin: 0 12px 2px;
}

/* --- CHAT GROUP AVATARS (общий чат графика: аватар слева от пузырей, как Telegram) --- */
/* Только для чужих сообщений group-чата (класс chat-message-group ставится в _renderMessages).
   Аватар и имя автора — у ПОСЛЕДНЕГО сообщения серии (_applyGroupSeriesTails); на остальных
   пустая колонка держит выравнивание. Имя лежит последним в .chat-msg-content, а обе колонки
   выровнены по низу — поэтому аватар и имя оказываются на одной линии, под пузырём. */
.chat-message-other.chat-message-group {
    flex-direction: row;
    align-items: flex-end;      /* аватар прижат к нижнему пузырю блока — как в мессенджерах */
    gap: 6px;
}
.chat-msg-avatar-col {
    flex: 0 0 28px;
    width: 28px;
    display: flex;
    align-items: flex-end;
}
.chat-msg-content {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    min-width: 0;               /* длинный текст переносится, а не распирает строку */
}
/* Имя стоит ПОД пузырём → отступ сверху, а не снизу (базовый .chat-message-name — наоборот,
   в личном чате имя остаётся над сообщением). */
.chat-message-group .chat-message-name { margin: 3px 0 0 4px; }
.chat-msg-avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background-color: var(--th-avatar-bg, var(--c-accent));  /* fallback: buildAvatarHtml задаёт per-user цвет inline */
    color: var(--c-on-accent, #fff);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--fs-2xs);
    font-weight: 600;
    flex-shrink: 0;
}
.chat-msg-avatar-img {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

/* --- Bubble --- */
.chat-bubble {
    position: relative;
    border-radius: var(--radius-lg);
    padding: 8px 12px 8px;
    font-size: var(--fs-sm);
    line-height: 1.4;
    word-wrap: break-word;
    overflow-wrap: anywhere;
    white-space: pre-wrap;
}
.chat-message-mine .chat-bubble {
    background: var(--th-chat-accent, var(--c-accent));
    color: var(--c-on-accent);
    border-bottom-right-radius: 4px;
}
/* [DS9 W5] Пузырь собеседника = surface-1 + тонкая рамка (лента лежит на фоне страницы) */
.chat-message-other .chat-bubble {
    background: var(--th-surface-1, var(--c-surface-1));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    color: var(--tg-theme-text-color, #000);
    border-bottom-left-radius: 4px;
}
.chat-time {
    display: inline-block;
    margin-left: 8px;
    font-size: var(--fs-2xs);
    opacity: 0.7;
    vertical-align: baseline;
    white-space: nowrap;
}
.chat-message-mine .chat-time { color: rgba(255, 255, 255, 0.85); }
.chat-message-other .chat-time { color: var(--tg-theme-hint-color, #8e8e93); }

/* --- [READ RECEIPTS задача 3] Галочки доставки/прочтения на МОИХ сообщениях --- */
/* ✓ (одна) — доставлено; ✓✓ (две, акцент) — прочитано. Только в .chat-message-mine. */
.chat-check {
    display: inline-flex;
    align-items: center;
    margin-left: 4px;
    vertical-align: baseline;
    /* «Мой» пузырь = var(--c-accent) с белым текстом. Доставлено — приглушённая белая
       галочка; прочитано — двойная и полностью непрозрачная (контраст вместо хардкода цвета). */
    color: rgba(255, 255, 255, 0.6);
}
.chat-check-svg {
    width: 1.05em;
    height: 0.85em;
}
/* Прочитано всеми — спокойный мятный из палитры темы (--th-chat-check-read, [DS9 W5]),
   с откатом на базовый токен для Системной темы. Цвет — только в палитрах (см. CLAUDE.md §1). */
.chat-check.chat-check-read {
    color: var(--th-chat-check-read, var(--c-chat-check-read));
}

/* --- [MSG META + EDIT задача 1] Метка «изменено» + контекст-меню сообщения --- */
.chat-meta {
    display: inline;
    white-space: nowrap;
}
.chat-edited {
    font-size: var(--fs-2xs);
    opacity: 0.7;
    margin-left: 8px;
    font-style: italic;
}
.chat-message-mine .chat-edited { color: rgba(255, 255, 255, 0.85); }
.chat-message-other .chat-edited { color: var(--tg-theme-hint-color, #8e8e93); }

/* Long-press на «моём» пузыре открывает наше меню — гасим нативные выделение/callout. */
.chat-message-mine .chat-bubble {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
}
/* Контекст-меню сообщения переиспользует .dropdown-menu; чуть уже. */
.chat-msg-actions { min-width: 160px; }
/* [CHAT MODERATION] Имя автора шапкой меню над ЧУЖИМ сообщением: два пункта из трёх
   бьют по человеку, а не по сообщению, — адресат должен быть виден. Не кнопка:
   ни hover'а, ни курсора, тихий hint-слой над списком действий. */
.chat-msg-actions-head {
    padding: var(--space-2) var(--space-3);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    color: var(--tg-theme-hint-color);
    border-bottom: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    margin-bottom: 4px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* --- [EDIT BAR задача 1] Полоска «Редактирование» над полем ввода --- */
.chat-edit-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 8px 14px;
    /* [DS9 W5] Вложенная панель = surface-2 + тонкая рамка */
    background: var(--th-surface-2, var(--c-surface-2));
    border-top: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    border-left: 3px solid var(--th-chat-accent, var(--c-accent));
}
.chat-edit-bar-label {
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--th-chat-accent, var(--c-accent));
}
.chat-edit-bar-cancel {
    border: none;
    background: transparent;
    color: var(--tg-theme-hint-color, #8e8e93);
    font-size: var(--fs-md);
    cursor: pointer;
    padding: 4px;
    line-height: 1;
    display: flex;
    align-items: center;
    -webkit-tap-highlight-color: transparent;
}
.chat-edit-bar-cancel:active { opacity: 0.6; }

/* --- Read-only banner (задача 7.1) ([DS9 W5] surface-2 + тонкая рамка) --- */
.chat-readonly-banner {
    padding: 12px 16px;
    text-align: center;
    background: var(--th-surface-2, var(--c-surface-2));
    color: var(--tg-theme-hint-color, #999);
    font-size: var(--fs-sm);
    border-top: 1px solid var(--th-border-subtle, var(--c-border-subtle));
}

/* --- Input bar ([DS9 W5] бар surface-1, поле surface-2 — elevation вместо тонов TG) --- */
.chat-input-wrap {
    display: flex;
    align-items: flex-end;
    gap: 8px;
    padding: 8px 10px calc(8px + env(safe-area-inset-bottom, 0px));
    border-top: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    background: var(--th-surface-1, var(--c-surface-1));
    flex-shrink: 0;
}
.chat-input {
    flex: 1 1 auto;
    min-height: 40px;
    /* [FIX] Было 140px (~4-5 строк) — поле разрасталось на весь экран при длинном
       сообщении. Ограничено 2 строками: 40px (1 строка + паддинги, см. min-height) +
       ~20px на вторую строку (line-height 1.4 * fs-sm 14px). Тот же max — в JS _autosize
       (app_chat.js), числа держать синхронно. */
    max-height: 64px;
    /* [FIX 2026-08-25] Глобальный сброс `textarea { overflow:hidden }` (строка ~117) не
       переопределялся здесь: коробка честно упиралась в max-height, но текст СВЕРХ него
       становился невидимым и недоступным для прокрутки — юзер не мог перечитать начало
       длинного сообщения. auto возвращает собственный скролл поля после упора в потолок. */
    overflow-y: auto;
    padding: 10px 12px;
    font-size: var(--fs-sm);
    /* [РАДИУС] --radius-pill (999px) на textarea с box-sizing:border-box — браузер сам
       клэмпит эффективный радиус угла к half(height) (CSS overlapping-corners алгоритм):
       на 1 строке (40px) угол ≈20px («таблетка»), на 2 строках (64px) ≈32px — то есть
       чем выше поле растёт, тем СИЛЬНЕЕ (в px) скругляются углы, само собой, без JS. */
    border-radius: var(--radius-pill);
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    background: var(--th-surface-2, var(--c-surface-2));
    color: var(--tg-theme-text-color, #000);
    resize: none;
    line-height: 1.4;
    outline: none;
    font-family: inherit;
    box-sizing: border-box;
}
.chat-input:focus {
    border-color: var(--th-focus, var(--c-accent));
}
.chat-input-side {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    flex-shrink: 0;
}
.chat-counter {
    font-size: var(--fs-2xs);
    color: var(--tg-theme-hint-color, #8e8e93);
    line-height: 1;
}
.chat-send-btn {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: none;
    background: var(--th-chat-accent, var(--c-accent));
    color: var(--c-on-accent);
    font-size: var(--fs-md);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    -webkit-tap-highlight-color: transparent;
    transition: opacity 0.18s, background 0.18s;
}
.chat-send-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}
.chat-send-btn:not(:disabled):hover {
    background: var(--th-chat-accent, var(--c-accent));
    filter: brightness(1.08);
}

/* --- Узкие экраны: уменьшаем боковые отступы --- */
@media (max-width: 480px) {
    .chat-messages { padding: 10px 10px; }
    .chat-message { max-width: 85%; }
}

/* --- BOOTLOADER OVERLAY (S22) --- */
/* Покрывает страницу до выставления html.app-ready (JS снимает класс после auth+рендера).
   ВАЖНО: background задаётся ИНЛАЙНОВЫМ блоком <style> в <head> каждой HTML-страницы
   через @media (prefers-color-scheme), а не здесь. Причина — если ставить тут через
   var(--tg-theme-bg-color), значение переменной появляется только после загрузки
   Telegram WebApp (в браузере) или style.css (в Capacitor), что само даёт мини-вспышку
   при первом её появлении. Inline @media срабатывает синхронно при парсе <head>. */
#app-bootloader {
    position: fixed;
    inset: 0;
    z-index: 2147483647;
    opacity: 1;
    transition: opacity 140ms ease-out;
    pointer-events: auto;
}
html.app-ready #app-bootloader {
    opacity: 0;
    pointer-events: none;
}

/* --- ADMIN OPEN CHATS (кнопка «Открытых чатов» в окне участников) --- */
.btn-admin-open-chats {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 7px 14px;
    background: var(--th-fab, var(--c-accent));
    color: var(--tg-theme-button-text-color, #fff);
    border: none;
    border-radius: var(--radius-pill);
    font-size: var(--fs-sm);
    font-family: inherit;
    cursor: pointer;
    transition: opacity 0.15s;
}
.btn-admin-open-chats:hover { opacity: 0.85; }
.btn-admin-open-chats:active { opacity: 0.7; }

.admin-open-chats-count {
    font-weight: 700;
    min-width: 14px;
    display: inline-block;
    text-align: center;
}

/* --- OPEN CHATS MODAL ITEMS --- */
.open-chats-section-hdr {
    padding: 12px 4px 4px;
    font-size: var(--fs-2xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--tg-theme-hint-color, #999);
}

/* Статусная пилюля (Ожидает / В работе) внутри inbox-chat-item */
.open-chat-status-pill {
    display: inline-block;
    font-size: var(--fs-2xs);
    font-weight: 500;
    padding: 2px 8px;
    border-radius: var(--radius-pill);
    margin-bottom: 4px;
    line-height: 1.4;
}
/* [AUDIT 2026-08-27] Подложки были литералами rgba и темой не управлялись, а текст «закрыт»
   брал общий hint и на своей подложке давал 2.93:1. Всё три пары — токены палитры. */
.open-chat-status-waiting {
    background: var(--th-chat-pill-waiting-bg, var(--c-chat-pill-waiting-bg));
    color: var(--th-chat-status-waiting, var(--c-chat-status-waiting));
}
.open-chat-status-active {
    background: var(--th-chat-pill-active-bg, var(--c-chat-pill-active-bg));
    color: var(--th-chat-status-active, var(--c-chat-status-active));
}
.open-chat-status-closed {
    background: var(--th-chat-pill-closed-bg, var(--c-chat-pill-closed-bg));
    color: var(--th-chat-status-closed, var(--c-chat-status-closed));
}
/* Карточка закрытого участником чата — приглушённая. */
.inbox-chat-item-closed {
    opacity: 0.65;
}
.inbox-chat-item-closed .inbox-item-title,
.inbox-chat-item-closed .inbox-item-body {
    text-decoration: line-through;
    text-decoration-color: var(--th-chat-status-closed, var(--c-chat-status-closed));
}
.open-chat-admin-label {
    font-size: var(--fs-2xs);
    color: var(--tg-theme-hint-color, #8e8e93);
    margin-top: 3px;
}

/* --- [FIX-7 BUTTON LOCK] Визуальный индикатор для кнопок во время мутирующего fetch'а. ---
   Применяется через lockButton(btn) из app_api.js. opacity:0.6 + cursor:wait +
   pointer-events:none гарантирует, что повторный клик не попадёт. CSS-переменные не нужны —
   класс работает поверх существующих стилей кнопки. */
.btn-loading {
    opacity: 0.6 !important;
    cursor: wait !important;
    pointer-events: none !important;
}


/* ============================================================================
   --- THEME EDITOR --- Редактор тем владельца (app_theme_editor.js) + пикер
   (app_color_picker.js). Только токены/--th-*/var() — без хардкода цветов
   (CLAUDE.md §1, правило 4). ИСКЛЮЧЕНИЕ: радуга/градиенты внутри пикера —
   это его рабочая функция (выбор тона), а не «обшивка» темы; такие цвета
   допустимы только здесь и в самом канвасе пикера.
   ============================================================================ */

/* --- Список тем --- */
.te-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin: var(--space-2) 0;
    max-height: 50vh;
    overflow-y: auto;
}
.te-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
.te-row-name {
    flex: 1 1 auto;
    text-align: left;
    padding: var(--space-3);
    background: var(--th-surface, var(--c-surface));
    border: 1px solid var(--th-card-border, var(--c-hint));
    border-radius: var(--radius-sm);
    color: var(--tg-theme-text-color);
    font: inherit;
    font-size: var(--fs-sm, 14px);
    cursor: pointer;
}
.te-row-name:hover { background: var(--th-hover, rgba(0, 0, 0, 0.04)); }
.te-row-name:active { transform: scale(0.99); }
.te-row-rename {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    border: 1px solid var(--th-card-border, var(--c-hint));
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--c-accent);
    font-size: var(--fs-md);
    cursor: pointer;
}
.te-row-rename:hover { background: var(--th-hover, rgba(0, 0, 0, 0.04)); }
.te-rename-input {
    flex: 1 1 auto;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    font: inherit;
    font-size: var(--fs-sm, 14px);
}
.te-add {
    width: 100%;
    margin-top: var(--space-2);
}

/* --- Палитра темы (список переменных) --- */
.te-vars {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    max-height: 60vh;
    overflow-y: auto;
    margin: var(--space-2) 0;
    padding-right: var(--space-1);
}
/* Заголовок группы цветов в палитре — единый секционный eyebrow
   (как .my-events-subs-header / .home-section-title): капс вторичным цветом. */
.te-section {
    padding: var(--space-3) 0 var(--space-1);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    color: var(--tg-theme-hint-color, #8e8e93);
    text-transform: uppercase;
    letter-spacing: 0.08em;
}
.te-section:first-child { padding-top: 0; }

.te-var {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) 0;
    border-bottom: 1px solid var(--th-divider, var(--c-hint));
}
.te-var:last-child { border-bottom: none; }
.te-swatch {
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--th-input-border, var(--c-hint));
    padding: 0;
    cursor: pointer;
    /* Шахматка под прозрачными цветами, чтобы alpha было видно. */
    background-image:
        linear-gradient(45deg, rgba(128, 128, 128, 0.4) 25%, transparent 25%),
        linear-gradient(-45deg, rgba(128, 128, 128, 0.4) 25%, transparent 25%),
        linear-gradient(45deg, transparent 75%, rgba(128, 128, 128, 0.4) 75%),
        linear-gradient(-45deg, transparent 75%, rgba(128, 128, 128, 0.4) 75%);
    background-size: 10px 10px;
    background-position: 0 0, 0 5px, 5px -5px, -5px 0;
}
/* Свотч поверх шахматки красится inline-стилем background (с приоритетом). */
.te-swatch[style*="background"] { background-image: none; }
.te-swatch-na {
    cursor: default;
    background: var(--th-surface, var(--c-surface));
    background-image: none;
}
.te-var-info {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.te-var-comment {
    font-size: var(--fs-sm, 14px);
    color: var(--tg-theme-text-color);
    line-height: 1.2;
}
.te-var-name {
    font-size: var(--fs-2xs, 11px);
    color: var(--tg-theme-hint-color);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.te-var-value {
    flex: 0 0 auto;
    max-width: 130px;
}
.te-hex {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: var(--fs-xs, 12px);
    color: var(--tg-theme-hint-color);
}
.te-textval {
    width: 124px;
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-sm);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: var(--fs-2xs, 11px);
}
.te-status {
    min-height: 18px;
    font-size: var(--fs-xs, 12px);
    color: var(--c-accent);
    text-align: center;
    margin-top: var(--space-1);
}

/* --- Пикер цвета (app_color_picker.js) --- */
.cp-content { max-width: 300px; }
.cp-sv-wrap {
    position: relative;
    width: 260px;
    max-width: 100%;
    margin: var(--space-2) auto var(--space-3);
    touch-action: none;            /* drag не скроллит страницу */
}
.cp-sv {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--radius-sm);
    border: 1px solid var(--th-input-border, var(--c-hint));
    cursor: crosshair;
}
.cp-sv-cursor {
    position: absolute;
    width: 16px;
    height: 16px;
    margin: -8px 0 0 -8px;
    border: 3px solid #fff;
    border-radius: 50%;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);
    pointer-events: none;
}
.cp-hue-wrap {
    position: relative;
    width: 260px;
    max-width: 100%;
    margin: 0 auto var(--space-3);
    touch-action: none;
}
.cp-hue {
    display: block;
    width: 100%;
    height: 22px;
    border-radius: var(--radius-pill);
    border: 1px solid var(--th-input-border, var(--c-hint));
    cursor: ew-resize;
}
.cp-hue-cursor {
    position: absolute;
    top: -2px;
    width: 6px;
    height: 26px;
    margin-left: -3px;
    border: 2px solid #fff;
    border-radius: var(--radius-sm);
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);
    pointer-events: none;
}
.cp-bottom {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
.cp-preview {
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--th-input-border, var(--c-hint));
}
.cp-hex {
    flex: 1 1 auto;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: var(--fs-sm, 14px);
    text-transform: uppercase;
}

/* --- TOAST (DS7) --- */
/* Контейнер-хост тостов: фиксированный, по центру над таб-баром */
.toast-host {
    position: fixed;
    left: 50%;
    bottom: calc(var(--tab-bar-h, 44px) + 24px);
    transform: translateX(-50%);
    z-index: 100001;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    pointer-events: none;
    max-width: 92vw;
}
/* Отдельный тост */
.toast {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 10px 18px;
    border-radius: var(--radius-pill);
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    /* [DS9 W5] Тост = плавающая поверхность: surface-3 + ступень-2 + тонкая рамка */
    background: var(--th-surface-3, var(--c-surface-3));
    color: var(--c-text);
    box-shadow: var(--th-shadow-2, var(--shadow-2));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    opacity: 0;
    transform: translateY(8px);
    transition: opacity .18s ease-out, transform .18s ease-out;
    max-width: 100%;
}
.toast.show { opacity: 1; transform: translateY(0); }
.toast-success { border-left: 3px solid var(--c-success); }
.toast-error   { border-left: 3px solid var(--c-danger); }
.toast-info    { border-left: 3px solid var(--c-accent); }

/* --- EMPTY STATE (DS7) --- */
/* Универсальное пустое состояние; .inbox-empty переиспользует тот же layout */
.empty-state,
.inbox-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    color: var(--c-hint);
    padding: 48px 20px;
    gap: var(--space-2);
}
.empty-state .empty-icon {
    line-height: 1;
    opacity: .65;
}
.empty-state .empty-icon svg,
.empty-state .empty-icon .icn svg {
    width: 40px;
    height: 40px;
}
.empty-state .empty-title {
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
    color: var(--c-text);
}
.empty-state .empty-text {
    font-size: var(--fs-sm);
    max-width: 280px;
}
/* Компактный вариант для встроенных пустых состояний (напр. поиск внутри списка) */
.empty-state--compact {
    padding: 20px;
}

/* --- SKELETON (DS7) --- */
/* Базовый класс-скелетон с shimmer-анимацией */
.skeleton {
    position: relative;
    overflow: hidden;
    background: var(--th-surface, var(--c-surface));
    border-radius: var(--radius-md);
}
.skeleton::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, var(--th-hover, rgba(0,0,0,.05)), transparent);
    transform: translateX(-100%);
    animation: skeletonShimmer 1.3s infinite;
}
@keyframes skeletonShimmer { to { transform: translateX(100%); } }
.skeleton-card {
    height: 64px;
    margin-bottom: var(--space-2);
}
.skeleton-line {
    height: 12px;
    border-radius: var(--radius-sm);
    margin: 10px 0;
}
@media (prefers-reduced-motion: reduce) {
    .skeleton::after { animation: none; }
}

/* ============================================================ */
/* [OWNER PANEL] Панель владельца приложения (app_owner.js)     */
/* Только переменные тем (правило 4) + состояния (правило 5).   */
/* ============================================================ */

/* --- ХАБ настроек владельца --- */
.owner-hub { padding: 12px 16px 24px; }
.owner-hub-item {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 15px;
    margin-bottom: 10px;
    /* [DS9] surface-1 + рамка + тень-1 (как .owner-sched-card / .inbox-item) — уход от плоской цветной заливки */
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    border-radius: var(--radius-md);
    background: var(--th-surface-1, var(--c-surface-1));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
    color: var(--tg-theme-text-color);
    font-size: var(--fs-md);
    font-weight: 600;
    text-align: left;
    cursor: pointer;
    transition: background-color 0.12s, transform 0.05s;
}
.owner-hub-item:hover { background: var(--th-menu-active, var(--c-accent-glow)); }
.owner-hub-item:active { transform: scale(0.99); }
.owner-hub-main { flex: 1; min-width: 0; }
.owner-hub-arrow { flex-shrink: 0; color: var(--th-link-accent, var(--c-accent)); font-size: var(--fs-md); }

/* --- Вкладки (модерация графиков) — унифицированы со стилем уведомлений (.inbox-tab):
   [DS9 W5] сегмент-контрол — трек surface-2, активная = surface-1 + shadow-1 --- */
.owner-tabs {
    display: flex;
    gap: var(--space-1);
    margin: var(--space-2) var(--space-3);
    padding: var(--space-1);
    background: var(--th-surface-2, var(--c-surface-2));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    border-radius: var(--radius-md);
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}
.owner-tab {
    flex: 1 1 auto;
    padding: 6px 10px;
    border: none;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--tg-theme-hint-color, var(--c-hint));
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.18s, color 0.18s, box-shadow 0.18s;
    -webkit-tap-highlight-color: transparent;
}
.owner-tab.is-active {
    background: var(--th-surface-1, var(--c-surface-1));
    color: var(--tg-theme-text-color, var(--c-text));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
}

/* --- Карточка графика (модерация) --- */
#owner-schedules-list { padding: 8px 16px 24px; }
.owner-sched-card {
    /* [DS9 W5] Карточка модерации = surface-1 + рамка + тень-1 (как строки inbox) */
    background: var(--th-surface-1, var(--c-surface-1));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
    border-radius: var(--radius-md);
    padding: 14px;
    margin-bottom: 10px;
    position: relative;   /* [ЗАДАЧА 4] якорь для крестика удаления .inbox-item-del (закрытые жалобы) */
}
/* [ЗАДАЧА 4] Резерв справа под крестик, чтобы длинный заголовок жалобы не заезжал под него. */
.inbox-row > .owner-sched-card .owner-sched-head { padding-right: 30px; }
.owner-sched-head {
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
}
.owner-sched-head:hover .owner-sched-title { color: var(--th-link-accent, var(--c-accent)); }
.owner-sched-vis { font-size: var(--fs-md); flex-shrink: 0; }
.owner-sched-title {
    font-weight: 600;
    font-size: var(--fs-md);
    color: var(--tg-theme-text-color);
    flex: 1; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.owner-sched-meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
    margin: 8px 0 12px;
    font-size: var(--fs-xs);
    color: var(--tg-theme-hint-color);
    flex-wrap: wrap;
}
.owner-sched-author-wrap { display: flex; align-items: center; gap: 6px; min-width: 0; }
.owner-sched-author { cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.owner-sched-author:hover { color: var(--th-link-accent, var(--c-accent)); text-decoration: underline; }
.owner-sched-counts { flex-shrink: 0; }
.owner-sched-actions { display: flex; flex-wrap: wrap; gap: 8px; }

/* --- [МОДЕРАЦИЯ → ЧАТ С АВТОРОМ] Компактная icon-кнопка «Написать автору» рядом с именем --- */
.owner-sched-chat-btn {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: none;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--th-link-accent, var(--c-accent));
    cursor: pointer;
    transition: background 0.15s;
}
.owner-sched-chat-btn:hover { background: var(--th-accent-glow, var(--c-accent-glow)); }
.owner-sched-chat-btn:active { opacity: 0.85; }
.owner-sched-chat-btn svg { width: 18px; height: 18px; }

/* --- [ЖАЛОБЫ] Карточка жалобы (база = .owner-sched-card; здесь — только дополнения) --- */
#owner-complaints-list { padding: 8px 16px 24px; }
.owner-complaint-row { margin-top: 6px; }
.owner-complaint-head--deleted { cursor: default; }
.owner-complaint-head--deleted .owner-sched-title { color: var(--tg-theme-hint-color, #999); }
.owner-complaint-head--deleted:hover .owner-sched-title { color: var(--tg-theme-hint-color, #999); }
.owner-complaint-date {
    flex-shrink: 0;
    font-size: var(--fs-2xs);
    color: var(--tg-theme-hint-color, #999);
}
.owner-complaint-text {
    margin: 10px 0;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    background: var(--th-surface, var(--tg-theme-bg-color, #fff));
    font-size: var(--fs-sm);
    color: var(--tg-theme-text-color, #000);
    white-space: pre-wrap;
    word-break: break-word;
}

/* ============================================================================
   [ПОЧТА] Раздел переписки админов приложения по e-mail (owner-mail-*)
   ============================================================================
   База — карточки модерации (.owner-sched-card) и «конверт» текста жалобы
   (.owner-complaint-text): раздел живёт в той же панели и обязан выглядеть её
   частью, а не отдельным приложением. Собственного здесь ровно три вещи:
   бейдж непрочитанных, лента писем с разделением «мы / они» и строка состояния
   канала. Цвета — только токены тем (правило 3 CLAUDE.md). */

#owner-mail-list { padding: 8px 16px 24px; }

/* Строка состояния канала: адрес отправки + предупреждение о ненастроенном приёме. */
.owner-mail-channel { padding: 10px 16px 0; }
.owner-mail-addr {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: var(--fs-sm);
    color: var(--tg-theme-hint-color, var(--c-hint));
    word-break: break-all;
}
.owner-mail-warn {
    margin-top: 8px;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    border: 1px dashed var(--th-border-subtle, var(--c-border-subtle));
    background: var(--th-surface, var(--c-surface));
    font-size: var(--fs-xs);
    color: var(--tg-theme-text-color, var(--c-text));
}

/* Счётчик непрочитанных в пункте хаба. */
.owner-mail-badge {
    flex-shrink: 0;
    min-width: 20px;
    padding: 0 6px;
    border-radius: var(--radius-pill);
    /* Тот же токен, что у точки непрочитанного в уведомлениях и чатах: смысл
       идентичен, а новый цвет в палитре потребовал бы правки всех четырёх тем. */
    background: var(--th-unread, var(--c-accent));
    color: var(--c-on-accent, #fff);
    font-size: var(--fs-2xs);
    line-height: 20px;
    text-align: center;
}

/* Карточка ветки: тема сверху, собеседник и дата — под ней. */
.owner-mail-card { cursor: pointer; }
.owner-mail-card:hover { border-color: var(--th-link-accent, var(--c-accent)); }
.owner-mail-card.is-unread { border-left: 3px solid var(--th-unread, var(--c-accent)); }
.owner-mail-meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-top: 6px;
}
.owner-mail-peer {
    font-size: var(--fs-sm);
    color: var(--tg-theme-text-color, var(--c-text));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.owner-mail-peer-addr {
    margin-top: 2px;
    font-size: var(--fs-2xs);
    color: var(--tg-theme-hint-color, var(--c-hint));
    word-break: break-all;
}
/* Кликабельный вариант — только в шапке открытой переписки (клик → mailto:, openOwnerMailAddr).
   В карточке списка (owner-mail-peer-addr без модификатора) остаётся просто текстом. */
.owner-mail-peer-addr--link {
    cursor: pointer;
    width: fit-content;
    text-decoration: underline;
    text-decoration-style: dotted;
    text-underline-offset: 2px;
}
.owner-mail-peer-addr--link:hover,
.owner-mail-peer-addr--link:focus-visible {
    color: var(--tg-theme-link-color, var(--c-link));
}
.owner-mail-peer-addr--link:active { opacity: 0.7; }
/* Бейдж «логической почты» (support/sales/...) перед адресом собеседника — виден
   даже без активного фильтра чипов, чтобы сразу было ясно, через какой адрес пришло. */
.owner-mail-box {
    display: inline-block;
    margin-right: 6px;
    padding: 1px 6px;
    border-radius: var(--radius-pill);
    background: var(--th-chip, var(--c-accent));
    color: var(--c-on-accent, #fff);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    text-transform: uppercase;
    vertical-align: middle;
}

/* Шапка ветки: адрес собеседника + группа кнопок (закрыть/блок/удалить). */
.owner-mail-thread-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    padding: 10px 16px;
}
/* Группа кнопок справа от адреса — закрыть/переоткрыть, заблокировать, удалить.
   Отдельный флекс-контейнер, а не голые кнопки: на узком экране переносится
   ЦЕЛИКОМ под адрес, а не рвётся на кнопки вперемешку с текстом адреса. */
.owner-mail-thread-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* Ссылка на бан-лист в списке веток — канон .owner-hub-item, но без обёртки .owner-hub
   (та даёт внешние отступы страницы) — задаём их точечно, как у остального контента раздела. */
.owner-mail-blocked-link { width: calc(100% - 32px); margin: 4px 16px 10px; }

/* Форма ручной блокировки адреса (owner-mail-blocked-view) — тот же приём отступов,
   что у формы ответа в ветке (.owner-mail-reply). */
.owner-mail-block-form { padding: 12px 16px 20px; }
.owner-mail-block-form .btn { width: 100%; margin-top: 4px; }
#owner-mail-blocked-list { padding: 0 16px 24px; }
/* Причина блокировки — под адресом в карточке, тем же приёмом, что owner-mail-peer-addr. */
.owner-mail-blocked-reason {
    margin-top: 4px;
    font-size: var(--fs-xs);
    color: var(--tg-theme-hint-color, var(--c-hint));
}

/* Лента писем. Своё письмо прижато вправо и подсвечено акцентом — тот же приём
   различения сторон, что в чате приложения. */
#owner-mail-messages { padding: 8px 16px 0; }
.owner-mail-msg {
    max-width: 88%;
    margin-bottom: 10px;
    padding: 10px 12px;
    border-radius: var(--radius-md);
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    background: var(--th-surface-1, var(--c-surface-1));
}
.owner-mail-msg.is-out {
    margin-left: auto;
    background: var(--th-menu-active, var(--c-accent-glow));
}
.owner-mail-msg-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 6px;
}
.owner-mail-msg-from {
    font-size: var(--fs-2xs);
    color: var(--tg-theme-hint-color, var(--c-hint));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.owner-mail-msg-body {
    font-size: var(--fs-sm);
    color: var(--tg-theme-text-color, var(--c-text));
    word-break: break-word;
}
.owner-mail-msg-att {
    margin-top: 8px;
    font-size: var(--fs-2xs);
    color: var(--tg-theme-hint-color, var(--c-hint));
}

/* Поле ответа и кнопка «Написать письмо». */
.owner-mail-reply { padding: 12px 16px 24px; }
.owner-mail-reply .btn { width: 100%; margin-top: 10px; }
.owner-mail-compose-btn { display: block; width: calc(100% - 32px); margin: 0 16px 24px; }

@media (max-width: 480px) {
    .owner-mail-msg { max-width: 100%; }
}

/* --- [S7] Блок отзыва внутри карточки жалобы: тот же «конверт», что и текст жалобы
   (.owner-complaint-text), но с шапкой «Отзыв + звёзды + автор», чтобы модератор
   отличал предмет жалобы от её текста. --- */
.owner-complaint-review {
    margin: 10px 0;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    border: 1px dashed var(--th-border-subtle, var(--c-border-subtle));
    background: var(--th-surface, var(--tg-theme-bg-color, #fff));
}
.owner-complaint-review.is-hidden { opacity: 0.6; }
.owner-complaint-review.is-gone {
    font-size: var(--fs-sm);
    color: var(--tg-theme-hint-color, var(--c-hint));
}
.owner-complaint-review-head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 2px 8px;
}
.owner-complaint-review-label {
    font-size: var(--fs-2xs);
    font-weight: var(--fw-medium);
    color: var(--tg-theme-hint-color, var(--c-hint));
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.owner-complaint-review-author {
    font-size: var(--fs-2xs);
    color: var(--tg-theme-hint-color, var(--c-hint));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.owner-complaint-review-status {
    font-size: var(--fs-2xs);
    color: var(--c-danger);
}
.owner-complaint-review-text {
    margin-top: 6px;
    font-size: var(--fs-sm);
    color: var(--tg-theme-text-color, #000);
    white-space: pre-wrap;
    word-break: break-word;
}

/* --- Кнопки действий в карточках владельца --- */
.owner-card-btn {
    padding: 7px 12px;
    border: 1px solid var(--th-divider, var(--c-hint));
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--tg-theme-text-color);
    font-size: var(--fs-sm);
    font-weight: 500;
    cursor: pointer;
    transition: filter 0.12s, background-color 0.12s, opacity 0.12s;
}
.owner-card-btn:hover { filter: brightness(0.96); }
.owner-card-btn:active { opacity: 0.85; }
.owner-card-btn--ok {
    background: var(--th-accent-glow, var(--c-accent-glow));
    border-color: transparent;
    color: var(--c-accent);
}
.owner-card-btn--muted { color: var(--tg-theme-hint-color); }
.owner-card-btn--danger {
    background: var(--th-danger-glow, var(--c-danger-glow));
    border-color: transparent;
    color: var(--c-danger);
}

/* --- Лимиты данных --- */
.owner-limits-body { padding: 14px 16px 32px; }
.owner-limit-group {
    /* [DS9] surface-1 + рамка + тень-1 — единый card-стиль панели «Управление» */
    background: var(--th-surface-1, var(--c-surface-1));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
    border-radius: var(--radius-md);
    padding: 12px 14px;
    margin-bottom: 10px;
}
.owner-limit-label {
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--tg-theme-text-color);
    margin-bottom: 8px;
}
.owner-limit-row { display: flex; gap: 10px; }
.owner-limit-cell { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.owner-limit-sub {
    font-size: var(--fs-xs);
    color: var(--tg-theme-hint-color);
}
.owner-limit-input { width: 100%; }
.owner-limits-hint {
    font-size: var(--fs-xs);
    color: var(--tg-theme-hint-color);
    margin: 6px 2px 16px;
}
/* [ЛИМИТЫ] Пояснение к потолку, дальше которого значение не примет БД (CHECK-констрейнт).
   Без него отказ сохранения выглядел бы необъяснимым. */
.owner-limit-caphint {
    font-size: var(--fs-xs);
    color: var(--tg-theme-hint-color);
    margin-top: 6px;
}
/* [ЛИМИТЫ] Секции формы — тот же eyebrow, что в редакторе тем (.te-section),
   отступ сверху крупнее: здесь секции разделяют карточки, а не строки списка. */
.owner-limits-body .te-section { padding-top: var(--space-4); }
.owner-limits-body .te-section:first-child { padding-top: 0; }
/* [ТАРИФЫ] Строка тарифа переиспользует раскладку .owner-limit-row — на узких
   экранах три числовых поля остаются в ряд, как у диапазонов выше. */
.owner-tier-input { width: 100%; }
/* [OWNER: МОДЕРАЦИЯ] Строка тумблера (лейбл + .switch), как loc-auto-row */
.owner-mod-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin-top: 10px;
}
.owner-mod-lbl {
    font-size: var(--fs-sm);
    color: var(--tg-theme-text-color);
    line-height: 1.35;
    /* [ХИНТЫ] Лейбл забирает всё свободное место, чтобы кнопка «?» встала вплотную к
       тумблеру, а не повисла посередине строки (justify-content: space-between развёл бы
       три элемента по краям). */
    flex: 1;
    min-width: 0;
}
.owner-limits-save { width: 100%; }

/* --- Заблокированные + участники (карточка пользователя) --- */
#owner-blocked-list, #owner-members-list { padding: 8px 16px 24px; }
.owner-blocked-card, .owner-mem-card {
    display: flex;
    align-items: center;
    gap: 12px;
    /* [DS9] surface-1 + рамка + тень-1 (как .owner-sched-card / .inbox-item) — уход от плоской цветной заливки */
    background: var(--th-surface-1, var(--c-surface-1));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
    border-radius: var(--radius-md);
    padding: 12px 14px;
    margin-bottom: 10px;
}
.owner-mem-card { cursor: pointer; transition: background-color 0.12s; -webkit-tap-highlight-color: transparent; }
/* Кнопка ⋮ действий участника (модератор / блокировка) — прижата вправо. */
.owner-mem-menu-btn {
    flex-shrink: 0; margin-left: auto;
    background: transparent; border: none; cursor: pointer;
    color: var(--tg-theme-hint-color); font-size: 20px; line-height: 1;
    padding: 4px 10px; border-radius: var(--radius-sm);
    -webkit-tap-highlight-color: transparent;
    transition: background-color 0.12s, color 0.12s;
}
/* Hover-подсветка ТОЛЬКО на устройствах с реальным указателем (мышь) — иначе на тач
   :hover «залипает» после тапа и карточка/кнопка остаются синими до следующего касания. */
@media (hover: hover) and (pointer: fine) {
    .owner-mem-card:hover { background: var(--th-menu-active, var(--c-accent-glow)); }
    .owner-mem-menu-btn:hover { background: var(--th-menu-active, var(--c-accent-glow)); color: var(--c-accent); }
}
/* Бейджи статуса участника в подписи. */
.owner-mem-tag { font-weight: 600; }
.owner-mem-tag--mod { color: var(--th-link-accent, var(--c-accent)); }
.owner-mem-tag--blocked { color: var(--c-danger); }

/* --- [OWNER: СПИСОК УЧАСТНИКОВ] Неподвижная шапка: top-nav залипает вверху, скроллится только
   список участников. Паттерн как у #my-bookings-view .top-nav — фон протягивается до краёв
   экрана через отрицательные боковые margin (body padding = var(--space-4)). --- */
#owner-members-view .top-nav {
    position: sticky;
    top: 0;
    z-index: 55;
    background: var(--tg-theme-bg-color, #fff);
    margin-left: calc(-1 * var(--space-4));
    margin-right: calc(-1 * var(--space-4));
    padding: var(--space-2) var(--space-4);
    margin-bottom: var(--space-2);
}
.owner-mem-avatar, .owner-mem-avatar-img {
    width: 40px; height: 40px;
    border-radius: 50%;
    flex-shrink: 0;
    object-fit: cover;
    display: flex; align-items: center; justify-content: center;
    color: #fff; font-weight: 600; font-size: var(--fs-md);
}
.owner-blocked-info, .owner-mem-info {
    flex: 1; min-width: 0;
    display: flex; flex-direction: column; gap: 2px;
}
.owner-blocked-name, .owner-mem-name {
    font-weight: 600;
    font-size: var(--fs-sm);
    color: var(--tg-theme-text-color);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.owner-blocked-name { cursor: pointer; }
.owner-blocked-name:hover { color: var(--th-link-accent, var(--c-accent)); text-decoration: underline; }
.owner-blocked-sched, .owner-mem-sub {
    font-size: var(--fs-xs);
    color: var(--tg-theme-hint-color);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.owner-mem-arrow { flex-shrink: 0; color: var(--th-link-accent, var(--c-accent)); font-size: var(--fs-md); }

@media (max-width: 400px) {
    .owner-hub { padding: 10px 12px 20px; }
    #owner-schedules-list, #owner-blocked-list, #owner-members-list { padding: 6px 12px 20px; }
    .owner-limits-body { padding: 12px 12px 28px; }
}
/* ==========================================
   --- [EVENT IMAGE + GEO] Фото графика (квадрат/crop-модалка) + локация + радиус-поиск ---
   ========================================== */

/* --- [EVENT CARD THUMB] Квадрат-миниатюра в карточках списков и шапке графика --- */
.event-card-thumb {
    width: 54px;
    height: 54px;
    flex-shrink: 0;
    border-radius: var(--radius-sm);
    overflow: hidden;
    background: var(--th-surface, var(--c-surface));
}
.event-card-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
/* Квадрат в карточке списка как тап-цель (Поиск / Мои графики / Подписки): открывает
   карточку графика, а не входит в него. Класс вешает renderEventImageSquare(opts.tap). */
.event-card-thumb.is-tappable {
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.event-card-thumb.is-tappable:active {
    opacity: 0.8;
}

/* Квадрат в шапке графика — крупнее, кликабельный.
   align-self не нужен: .schedule-header-inner центрирует всю строку целиком. */
.event-card-thumb.sched-header-thumb {
    width: 56px;
    height: 56px;
    cursor: pointer;
}
.event-card-thumb.sched-header-thumb:active { opacity: 0.8; }

/* --- [CARD LOCATION] «флаг Город · ~N км» — левая часть третьего ряда карточки --- */
.event-card-loc {
    flex: 1 1 auto;
    min-width: 0;   /* без этого ellipsis не сработает: flex-элемент не сжимается */
    font-size: var(--fs-xs);
    color: var(--tg-theme-hint-color);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* --- [S1 NEXT FREE] Ближайшее свободное время — первый элемент мета-строки карточки
   (порядок: строка nowrap+ellipsis, обрезаться должен город, а не доступность).
   Цвет НЕ задаём: наследуется от .event-card-loc (тихий hint-слой). Отличаем только
   насыщенностью шрифта — это самая полезная часть строки, но не отдельный акцент. */
.event-card-next {
    flex: 0 1 auto;
    min-width: 0;
    font-size: var(--fs-xs);
    color: var(--tg-theme-hint-color);
    font-weight: var(--fw-medium);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* [ЗЕЛЁНЫЕ ЧАСЫ] Свободное время есть уже СЕГОДНЯ. Красим ТОЛЬКО значок (иконки берут
   цвет из currentColor, см. обёртку s() в language/_icons.js) — текст остаётся тихим
   hint-слоем, иначе второй ряд начинает спорить с названием.
   Прецедент точечной покраски одной иконки — .booking-card .bc-ics-btn.is-saved. */
.event-card-next--today .icn {
    color: var(--th-success, var(--c-success));
}

/* --- [SCHED HEADER LOC] Локация в шапке графика (под описанием) --- */
.schedule-loc {
    font-size: var(--fs-xs);
    color: var(--tg-theme-hint-color);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* --- [RANGE SLIDER] Общий стиль ползунков (zoom кропа, радиус поиска).
   Трек — токен тумблера --th-slider, бегунок — токен кнопки --th-btn-bg. --- */
input[type="range"] {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 4px;
    border-radius: var(--radius-pill);
    background: var(--th-slider, var(--c-hint));
    outline: none;
}
input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--th-btn-bg, var(--c-accent));
    border: none;
    cursor: pointer;
}
input[type="range"]::-moz-range-thumb {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--th-btn-bg, var(--c-accent));
    border: none;
    cursor: pointer;
}
input[type="range"]:focus-visible {
    outline: 2px solid var(--th-focus, var(--c-accent));
    outline-offset: 2px;
}
input[type="range"]:disabled { opacity: .5; }

/* --- [SEARCH RADIUS] Содержимое попапа геолокации (#as-geo-popover) в шапке вкладки
   «Поиск» — id/логика ползунка не менялись. [DS10] Метка и значение — отдельной строкой
   НАД ползунком (а не в одном ряду с ним): в узком попапе одна строка почти не оставляла
   ползунку места для перетаскивания («ползунок очень короткий»). --- */
.search-radius-row {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: 4px 16px 8px;
    font-size: var(--fs-sm);
    color: var(--tg-theme-text-color);
}
.search-radius-row-top {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
/* [DS10] --th-slider почти сливался с фоном попапа (--th-surface-3) в тёмной теме — оба
   близки по тону (#24282e vs #262a31), дорожка визуально пропадала. --c-hint (тот же токен,
   что и у соседней подписи км) даёт устойчивый контраст на любой поверхности всех тем. */
.search-radius-row input[type="range"] {
    width: 100%;
    background: var(--c-hint);
}
.search-radius-label {
    margin-left: auto;
    color: var(--tg-theme-hint-color);
    font-size: var(--fs-xs);
    white-space: nowrap;
}

/* --- [LOCATION BLOCK] Блок «Местоположение» в мастере создания и edit-модалке --- */
.loc-block { margin-top: var(--space-3); }
.loc-row {
    display: flex;
    gap: var(--space-2);
    margin-top: var(--space-2);
}
.loc-row > * { flex: 1; min-width: 0; }
.loc-geo-btn { margin-top: var(--space-2); width: 100%; }
/* Тумблер «Определять адрес автоматически» */
.loc-auto-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin-top: var(--space-2);
    font-size: var(--fs-sm);
    color: var(--tg-theme-text-color);
}
/* Read-only координаты (только кнопкой GPS) + заблокированные при автоопределении поля адреса */
.loc-row input[readonly],
.loc-row select:disabled,
.loc-row input:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

/* --- [EVIMG MODAL] Crop-модалка фото графика --- */
.evimg-content {
    width: min(340px, calc(100vw - 32px));
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}
.evimg-title { margin: 0; font-size: var(--fs-md, 16px); }
.evimg-canvas-wrap {
    position: relative;
    width: 280px;
    height: 280px;
    max-width: 100%;
    margin: 0 auto;
    border-radius: var(--radius-md);
    overflow: hidden;
    background: var(--th-surface, var(--c-surface));
}
.evimg-canvas-wrap canvas {
    width: 100%;
    height: 100%;
    display: block;
    touch-action: none; /* drag пальцем не должен скроллить страницу */
    cursor: grab;
}
.evimg-canvas-wrap canvas:active { cursor: grabbing; }
.evimg-placeholder {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}
.evimg-placeholder img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.evimg-zoom-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--fs-sm);
    color: var(--tg-theme-hint-color);
}
.evimg-zoom-row input[type="range"] { flex: 1; }
/* [DS9] Ряды кнопок фото-модалки — канонический .modal-actions (самодельный
   .evimg-btn-row удалён); ритм между рядами задаёт gap контейнера .evimg-content.
   Модалка узкая (340px) — паддинг кнопок ужат, чтобы «Выбрать фото» не переносился
   (длинные i18n-строки при этом переносятся штатно). */
.evimg-content .modal-actions { margin-top: 0; }
.evimg-content .modal-actions .btn { padding: 0 var(--space-2); }
/* --- [EVIMG FULLSCREEN] Полноэкранный просмотр фото --- */
.evimg-fullscreen {
    z-index: 100000;
    cursor: zoom-out;
}
.evimg-fullscreen img {
    max-width: min(92vw, 560px);
    max-height: 80vh;
    border-radius: var(--radius-md);
    object-fit: contain;
}

/* --- [EVIMG DETAIL] Просмотр фото графика: фото на всю ширину + название/описание + редактирование --- */
.evimg-detail {
    z-index: 100000;
    align-items: flex-start;
    justify-content: center;
    overflow-y: auto;
    background: var(--tg-theme-bg-color);
}
.evimg-detail-content {
    width: 100%;
    max-width: 560px;
    min-height: 100%;
    background: var(--tg-theme-bg-color);
    display: flex;
    flex-direction: column;
}
.evimg-detail-top {
    position: sticky;
    top: 0;
    padding: var(--space-2);
    background: var(--tg-theme-bg-color);
    z-index: 1;
}
.evimg-detail-img {
    width: 100%;
    height: auto;
    display: block;
    object-fit: contain;
    background: var(--th-surface, var(--c-surface));
}
.evimg-detail-body {
    padding: var(--space-3) var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}
.evimg-detail-title {
    margin: 0;
    font-size: var(--fs-md);
    color: var(--tg-theme-text-color);
}
.evimg-detail-desc {
    margin: 0;
    font-size: var(--fs-sm);
    color: var(--tg-theme-hint-color);
    white-space: pre-wrap;
    word-break: break-word;
}
.evimg-detail-edit { margin-top: var(--space-2); }

/* ============================================================================ */
/* --- [DS8] AUTH MODAL + TG DIALOGS (перенос инлайн-стилей app_globals.js) --- */
/* ============================================================================ */
/* Единые классы для JS-генерируемых окон: showAuthModal / showRegisterModal /
   showForgotModal (bottom-sheet) и tg.showConfirm / tg.showAlert (диалоги вне TG).
   Разметка создаётся в app_globals.js — здесь ТОЛЬКО стили (токены, без хардкода). */

/* --- Оверлеи --- */
.tg-dialog-overlay {
    position: fixed;
    inset: 0;
    z-index: 999999;
    background: var(--th-overlay, var(--c-overlay));   /* [DS9 W3] единый оверлей */
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-5);
    box-sizing: border-box;
}
.auth-sheet-overlay {
    position: fixed;
    inset: 0;
    z-index: 100000;
    background: var(--th-overlay, var(--c-overlay));   /* [DS9 W3] единый оверлей */
    display: flex;
    align-items: flex-end;
    justify-content: center;
}
.auth-sheet-overlay--stacked { z-index: 100001; }   /* регистрация/сброс поверх основного */

/* --- Диалог tg.showConfirm/showAlert --- */
.tg-dialog {
    background: var(--th-surface-3, var(--c-surface-3));   /* [DS9 W3] поверхность диалога */
    box-shadow: var(--th-shadow-3, var(--shadow-3));
    border-radius: var(--radius-lg);
    padding: var(--space-6) var(--space-5);
    width: 100%;
    max-width: 320px;
    box-sizing: border-box;
}
.tg-dialog-text {
    font-size: var(--fs-md);
    color: var(--c-text);
    margin-bottom: var(--space-5);
    line-height: 1.5;
    text-align: center;
}
.tg-dialog-btns { display: flex; gap: var(--space-3); }
.tg-dialog-btn {
    flex: 1;
    min-height: var(--control-h-compact);
    padding: var(--space-3);
    border: none;
    border-radius: var(--radius-sm);
    font-size: var(--fs-md);
    cursor: pointer;
    transition: filter 0.15s;
}
.tg-dialog-btn:active { filter: brightness(0.94); }
.tg-dialog-btn--secondary {
    background: var(--th-btn2-bg, rgba(120, 120, 128, 0.2));
    color: var(--c-text);
}
.tg-dialog-btn--primary {
    background: var(--th-btn-bg, var(--c-accent));
    color: var(--c-on-accent);
    font-weight: var(--fw-semibold);
}
.tg-dialog-btn--full { width: 100%; flex: none; }

/* --- Bottom-sheet входа/регистрации/сброса --- */
.auth-sheet {
    background: var(--th-surface-3, var(--c-surface-3));   /* [DS9 W3] поверхность диалога */
    box-shadow: var(--th-shadow-3, var(--shadow-3));
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    padding: var(--space-6) var(--space-5) 40px;   /* 40px снизу — запас под home-indicator */
    width: 100%;
    max-width: 480px;
    text-align: center;
}
.auth-sheet-grip {
    width: 36px;
    height: 4px;
    background: var(--c-hint);
    opacity: 0.4;
    border-radius: var(--radius-pill);
    margin: 0 auto var(--space-5);
}
.auth-sheet-title {
    font-size: var(--fs-lg);
    font-weight: var(--fw-semibold);
    color: var(--c-text);
    margin-bottom: var(--space-2);
}
.auth-sheet-sub {
    font-size: var(--fs-sm);
    color: var(--c-hint);
    margin-bottom: var(--space-6);
}

/* --- Поля ввода (общие для всех трёх модалок: _auth-/_reg-/_forgot-) --- */
._auth-input, ._reg-input, ._forgot-input {
    width: 100%;
    padding: 13px 14px;
    margin-bottom: var(--space-3);
    border-radius: var(--radius-md);
    border: 1px solid var(--th-input-border, #d2d5db);
    background: var(--c-bg);
    color: var(--c-text);
    font-size: var(--fs-md);
    box-sizing: border-box;
    -webkit-appearance: none;
    transition: border-color 0.15s, box-shadow 0.15s;
}
._auth-input::placeholder, ._reg-input::placeholder, ._forgot-input::placeholder {
    color: var(--c-hint);
}
._auth-input:focus, ._reg-input:focus, ._forgot-input:focus {
    outline: none;
    border-color: var(--c-accent);
    box-shadow: 0 0 0 3px var(--th-accent-glow, var(--c-accent-glow));
}

/* --- Основная кнопка sheet'а (вход / регистрация / отправить ссылку) --- */
._auth-primary {
    width: 100%;
    min-height: var(--control-h);
    padding: var(--space-3);
    margin-bottom: var(--space-2);
    border-radius: var(--radius-md);
    border: none;
    background: var(--th-btn-bg, var(--c-accent));
    color: var(--c-on-accent);
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
    cursor: pointer;
    transition: filter 0.15s;
}
._auth-primary:active { filter: brightness(0.94); }
._auth-primary:disabled { opacity: 0.6; cursor: default; }

/* --- Служебные строки sheet'а --- */
._auth-linkrow { display: flex; justify-content: space-between; margin-bottom: var(--space-4); }
._auth-link {
    background: none;
    border: none;
    color: var(--c-link);
    font-size: var(--fs-xs);
    cursor: pointer;
    padding: 2px 0;
}
._auth-status, ._reg-status, ._forgot-status {
    display: none;
    font-size: var(--fs-xs);
    color: var(--c-danger);
    margin-bottom: var(--space-3);
    text-align: left;
    line-height: 1.4;
}
._reg-status, ._forgot-status { margin-top: 2px; }
/* [TURNSTILE] Контейнер капчи в модалке регистрации. Пустой, пока виджет не отрисован
   (в Telegram WebApp и в APK он не показывается вовсе) — поэтому :empty без отступов,
   иначе в этих средах между полем пароля и кнопкой висела бы пустая полоса. */
._reg-captcha {
    display: flex;
    justify-content: center;
    margin-bottom: var(--space-3);
}
._reg-captcha:empty { display: none; }
._auth-divider {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    color: var(--c-hint);
    font-size: var(--fs-2xs);
    margin-bottom: var(--space-4);
}
._auth-divider::before, ._auth-divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--th-divider, rgba(128, 128, 128, 0.2));
}

/* --- Кнопки провайдеров (Google — белая по бренд-гайду, Telegram — фирменный голубой) --- */
.auth-provider-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    width: 100%;
    min-height: var(--control-h);
    padding: var(--space-3);
    margin-bottom: var(--space-3);
    border-radius: var(--radius-md);
    border: none;
    font-size: var(--fs-md);
    font-weight: var(--fw-medium);
    cursor: pointer;
    transition: filter 0.15s;
}
.auth-provider-btn:active { filter: brightness(0.94); }
.auth-provider-btn--google {
    background: var(--c-brand-google-bg);
    color: var(--c-brand-google-text);
    /* тонкий контур — белая кнопка не сливается со светлой поверхностью sheet'а */
    box-shadow: 0 0 0 1px var(--th-divider, rgba(128, 128, 128, 0.2));
}
.auth-provider-btn--telegram {
    background: var(--c-brand-telegram);
    color: #ffffff;   /* бренд-константа: белый по гайдлайну Telegram, от темы не зависит */
    margin-bottom: var(--space-2);
}

/* --- Отмена + мягкое предупреждение --- */
.auth-cancel-btn {
    width: 100%;
    min-height: var(--control-h-compact);
    padding: var(--space-2);
    border-radius: var(--radius-md);
    border: none;
    background: transparent;
    color: var(--c-hint);
    font-size: var(--fs-sm);
    cursor: pointer;
}
.auth-warn-note {
    margin-top: var(--space-3);
    padding: var(--space-3) 14px;
    background: var(--th-warn-soft-bg, var(--c-warn-soft-bg));
    color: var(--th-warn-soft-text, var(--c-warn-soft-text));
    border-radius: var(--radius-sm);
    font-size: var(--fs-xs);
    text-align: left;
}
/* --- [LEGAL] Политика и условия в подвале модалок входа/регистрации
   (разметку строит app_globals.js::legalLinksHtml; тот же вид, что у
   .login-policy-links в login.html) --- */
.auth-sheet-legal {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    margin-top: var(--space-2);
    flex-wrap: wrap;
}
.auth-sheet-legal-link {
    padding: var(--space-1) 0;
    border: none;
    background: transparent;
    color: var(--c-hint);
    font-size: var(--fs-xs);
    text-decoration: underline;
    cursor: pointer;
}
.auth-sheet-legal-link:hover,
.auth-sheet-legal-link:focus-visible {
    color: var(--th-link-accent, var(--c-link));
}
.auth-sheet-legal-link:focus-visible {
    outline: 2px solid var(--th-link-accent, var(--c-link));
    outline-offset: 2px;
    border-radius: var(--radius-xs, 4px);
}
.auth-sheet-legal-link:active { opacity: 0.7; }
.auth-sheet-legal-sep {
    color: var(--c-hint);
    font-size: var(--fs-xs);
}

/* ============================================================================ */
/* --- [DS9 W3] МОДАЛКИ/ДРОПДАУНЫ: перенос инлайн-стилей app_modals*.js в классы --- */
/* ============================================================================ */

/* --- [FIELD TYPE MENU] Popup выбора типа доп. поля (разметка в app_modals.js::_openFieldTypeMenu;
   направление вверх/вниз и max-height ставит JS по месту под клавиатурой) --- */
.field-type-menu {
    position: absolute;
    right: 4px;
    top: calc(100% + 4px);
    z-index: 50;
    background: var(--th-surface-3, var(--c-surface-3));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    border-radius: var(--radius-md);
    box-shadow: var(--th-shadow-2, var(--shadow-2));
    padding: 6px;
    min-width: 160px;
    max-height: 60vh;
    overflow-y: auto;
}
.field-type-menu-title {
    font-size: var(--fs-2xs);
    color: var(--tg-theme-hint-color, #8e8e93);
    padding: 4px 8px 6px;
    text-transform: uppercase;
    letter-spacing: 0.4px;
}
.field-type-opt {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    width: 100%;
    padding: 9px 10px;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: var(--fs-sm);
    font-family: inherit;
    text-align: left;
    background: transparent;
    color: var(--tg-theme-text-color, #000);
    -webkit-tap-highlight-color: transparent;
}
.field-type-opt.is-active {
    background: var(--th-hover, var(--tg-theme-secondary-bg-color, #efeff4));
}
.field-type-opt-icon {
    font-size: var(--fs-md);
    width: 20px;
    text-align: center;
    flex-shrink: 0;
}

/* --- [SUBSCRIBE CUSTOM FIELDS] Доп. поля при подписке (разметка в
   app_modals_actions.js::openSubscribeModal; инпуты — глобальные правила input[...]).
   [ВЫДЕЛЕНИЕ] Раньше блок ничем не отличался от окружающего текста модалки и терялся
   на фоне вступительных абзацев — по просьбе владельца оформлен как отдельная панель:
   surface-2 (уже используется для «вложенных блоков») + акцентная полоса слева 3px —
   тот же приём, что у .toast-info/.owner-mail-card.is-unread, а не новый компонент. --- */
#subscribe-custom-fields.subscribe-fields-panel {
    background: var(--th-surface-2, var(--c-surface-2));
    border-left: 3px solid var(--th-accent, var(--c-accent));
    border-radius: var(--radius-sm);
    padding: var(--space-4);
}

/* [A4] Постоянный статус-баннер записи участника на графике (#schedule-view, сразу под
   .schedule-header) — тот же приём выделения (surface-2 + акцентная полоса слева), что у
   .subscribe-fields-panel/.toast-info выше, но остаётся на экране, пока статус актуален:
   не toast (не исчезает через 2.6с) и не alert (не требует тапа «ОК»). Видимость решает
   loadSchedule() по currentSubscriptionPending (app_schedule.js). */
.view-status-banner {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0 0 var(--space-2);
    padding: var(--space-3) var(--space-4);
    background: var(--th-surface-2, var(--c-surface-2));
    border-left: 3px solid var(--th-accent, var(--c-accent));
    border-radius: var(--radius-sm);
    font-size: var(--fs-sm);
    color: var(--tg-theme-text-color, var(--c-text));
}
.view-status-banner-icon {
    flex-shrink: 0;
    font-size: var(--fs-md);
}
.subscribe-fields-title {
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--th-accent, var(--c-accent));
    margin: 0 0 var(--space-3);
}
.subscribe-field {
    margin-bottom: var(--space-3);
}
.subscribe-field:last-child {
    margin-bottom: 0;
}
.subscribe-field-label {
    display: block;
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
    color: var(--c-text);
    margin-bottom: var(--space-1);
}

/* --- [PDATA] Данные участника в модалке админа (разметка в
   app_modals_actions.js::openParticipantDataModal) --- */
.pdata-row {
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--tg-theme-secondary-bg-color, #efeff4);
}
.pdata-key {
    display: block;
    font-size: var(--fs-xs);
    color: var(--tg-theme-hint-color, #8e8e93);
    margin-bottom: 3px;
}
.pdata-val {
    font-size: var(--fs-md);
    font-weight: var(--fw-medium);
}
.pdata-val--tel {
    cursor: pointer;
    color: var(--th-link-accent, var(--c-link));
    -webkit-tap-highlight-color: transparent;
}
.pdata-copy-btn {
    font-size: var(--fs-xs);
    padding: 3px 8px;
    border: 1px solid var(--tg-theme-hint-color, #8e8e93);
    border-radius: var(--radius-sm);
    background: none;
    color: var(--tg-theme-hint-color, #8e8e93);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    margin-left: 4px;
    vertical-align: middle;
}

/* ============================================================================
   --- [S6 REVIEWS] Отзывы: ряд звёзд, шапка ленты, строка отзыва ---
   ============================================================================ */

/* --- РЯД ЗВЁЗД ---
   Единственный компонент S6 без аналога в проекте (согласован с пользователем).
   Различие состояний — И формой, И цветом: выбранная звезда ЗАЛИТА (fill:currentColor
   поверх контурного Lucide-глифа) и красится акцентом, невыбранная остаётся контуром
   цветом hint. Новых цветовых токенов не заводим — только существующие --th-chip и
   --tg-theme-hint-color, поэтому 4 палитры themes.css править не пришлось. */
.rating-stars {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    color: var(--tg-theme-hint-color, var(--c-hint));
    line-height: 1;
}

.rating-star {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--tg-theme-hint-color, var(--c-hint));
    font-size: var(--fs-md);
    line-height: 1;
}

.rating-star.is-on {
    color: var(--th-chip, var(--c-accent));
}

/* Заливка: глиф star в ICONS_SVG — контурный (fill:none, stroke=currentColor),
   поэтому «полная» звезда получается одним правилом, без второй иконки в реестре. */
.rating-star.is-on svg {
    fill: currentColor;
}

/* Компактный ряд — шапка ленты и строка отзыва */
.rating-stars--sm .rating-star {
    font-size: var(--fs-sm);
}

/* --- ЗВЁЗДЫ-КНОПКИ (композитор) --- */
.rating-star-btn {
    width: 44px;              /* палец: минимальная тап-цель, проверено на 360px */
    height: 44px;
    padding: 0;
    border: none;
    background: transparent;
    border-radius: var(--radius-sm);
    font-size: var(--fs-xl);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: transform 0.12s, background 0.15s, color 0.15s;
}

.rating-star-btn:hover {
    background: var(--th-accent-glow, var(--c-accent-glow));
}

.rating-star-btn:focus-visible {
    outline: 2px solid var(--th-chip, var(--c-accent));
    outline-offset: 2px;
}

.rating-star-btn:active {
    transform: scale(0.92);
}

.rating-star-btn:disabled {
    opacity: 0.5;
    cursor: default;
}

.rating-stars-label {
    margin: 0 0 4px 0;
}

/* --- ШАПКА ЛЕНТЫ: крупная средняя оценка + звёзды + счётчик --- */
.reviews-summary {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    margin-bottom: 12px;
    background: var(--th-surface-1, var(--c-surface-1));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
    border-radius: var(--radius-md);
}

.reviews-summary-score {
    font-size: var(--fs-xl);
    font-weight: 600;
    color: var(--tg-theme-text-color, var(--c-text));
    line-height: 1;
}

.reviews-summary-side {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}

.reviews-summary-count {
    font-size: var(--fs-xs);
    color: var(--tg-theme-hint-color, var(--c-hint));
}

/* Кнопка композитора / объяснение, почему её нет */
.reviews-compose-row {
    margin-bottom: 12px;
}

.reviews-compose-row .btn {
    width: 100%;
}

/* --- СТРОКА ОТЗЫВА — карточка/типографика целиком канон .mod-item* (окно модерации) --- */
.review-item {
    background: var(--th-surface-1, var(--c-surface-1));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
    border-radius: var(--radius-md);
    padding: 12px 14px;
    margin-bottom: 8px;
}

.review-item:last-child {
    margin-bottom: 0;
}

/* Свой отзыв — тихая акцентная кайма слева, без заливки (принцип «служебный слой молчит») */
.review-item--mine {
    border-left: 3px solid var(--th-chip, var(--c-accent));
}

/* Скрытый модератором (S7): виден только автору, поэтому приглушён и обведён пунктиром */
.review-item--hidden {
    opacity: 0.6;
    border-style: dashed;
}

.review-head {
    display: flex;
    align-items: center;
}

.review-head-info {
    flex: 1;
    min-width: 0;
}

.review-author {
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--tg-theme-text-color, var(--c-text));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.review-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 2px 8px;
    margin-top: 2px;
}

.review-date {
    font-size: var(--fs-2xs);
    color: var(--tg-theme-hint-color, var(--c-hint));
    white-space: nowrap;
}

.review-text {
    margin-top: 8px;
    font-size: var(--fs-sm);
    color: var(--tg-theme-text-color, var(--c-text));
    word-break: break-word;
    white-space: pre-wrap;
}

/* --- [S7] Жалоба на отзыв: тихая icon-кнопка справа в шапке строки.
   Базовый вид/состояния — от .nav-icon-btn; здесь только компактность внутри строки
   (штатные 34px рядом с аватаром и звёздами смотрелись бы кнопкой действия). --- */
.review-report-btn {
    width: 28px;
    height: 28px;
    margin-left: var(--space-2);
}
.review-report-btn svg { width: 16px; height: 16px; }

/* Подпись у скрытого модератором отзыва (виден только автору, .review-item--hidden) */
.review-hidden-note {
    margin-top: 6px;
    font-size: var(--fs-2xs);
    color: var(--tg-theme-hint-color, var(--c-hint));
}

/* --- БЕЙДЖ РЕЙТИНГА В КАРТОЧКЕ СПИСКА --- */
/* [2026-08-25, повторный проход] Звезда вернулась на штриховую {icon:star}-SVG: владелец
   просил «залить только контуры синим», а эмодзи ⭐ красит шрифт ОС — CSS до него не
   достаёт. Цвет несёт ТОЛЬКО значок (.sched-badge-star); само число остаётся тихим —
   иначе бейдж перебивал бы название графика. Для этого у .sched-badge--rating есть свой
   color: базовый .sched-badge красит всё содержимое акцентом. */
.sched-badge--rating {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: var(--fs-2xs);
    white-space: nowrap;
    color: var(--tg-theme-hint-color, var(--c-hint));
}

.sched-badge-star {
    display: inline-flex;
    font-size: var(--fs-sm);   /* контур мельче 11px читается плохо */
    color: var(--th-icon-rating, var(--c-icon-rating));
}

/* --- [ИСТОЧНИКИ КОНТАКТОВ 2026-08-22] Ряд «Избранное / Журнал / Контакты / Группы» в окне
   записи контакта. Родня .owner-tabs (тот же контейнер-подложка и та же активная плашка).
   Ширина строго 4×25%, без горизонтальной прокрутки: ряд обязан помещаться целиком.
   [UPD 2026-08-23] Иконки над подписями убраны по просьбе владельца — кнопки однострочные. --- */
.contact-src-tabs {
    display: flex;
    gap: var(--space-1);
    margin: 0 0 var(--space-2) 0;
    padding: var(--space-1);
    background: var(--th-surface-2, var(--c-surface-2));
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    border-radius: var(--radius-md);
    /* [FIX 2026-08-22] Ряд прячется при прокрутке списка вниз и возвращается при прокрутке
       вверх (класс .is-collapsed вешает _bindAdminBookScrollHide). Анимируем max-height и
       отступы, а не display: display не анимируется, и ряд бы моргал. */
    overflow: hidden;
    max-height: 72px;
    transition: max-height 0.22s ease, opacity 0.18s ease,
                margin-bottom 0.22s ease, padding 0.22s ease, border-width 0.18s ease;
}
.contact-src-tabs.is-collapsed {
    max-height: 0;
    opacity: 0;
    margin-bottom: 0;
    padding-top: 0;
    padding-bottom: 0;
    border-width: 0;
}
.contact-src-tab {
    flex: 1 1 25%;
    min-width: 0;                 /* иначе длинная подпись распирает колонку шире 25% */
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 9px 2px;
    border: none;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--tg-theme-hint-color, var(--c-hint));
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    line-height: 1.15;
    cursor: pointer;
    transition: background 0.18s, color 0.18s, box-shadow 0.18s;
    -webkit-tap-highlight-color: transparent;
}
.contact-src-tab span {
    display: block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.contact-src-tab:hover { color: var(--tg-theme-text-color, var(--c-text)); }
.contact-src-tab:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 1px; }
.contact-src-tab:active { transform: scale(0.97); }
.contact-src-tab:disabled { opacity: 0.5; cursor: default; }
.contact-src-tab.is-active {
    background: var(--th-surface-1, var(--c-surface-1));
    color: var(--tg-theme-text-color, var(--c-text));
    box-shadow: var(--th-shadow-1, var(--shadow-1));
}

/* [FIX 2026-08-22] Вкладка «Контакт» — колонка на всю высоту тела модалки: ряд источников
   сверху, список посередине (единственный прокручиваемый), полоса поиска снизу.
   ПОЧЕМУ ТАК, А НЕ sticky: раньше поиск был последним ребёнком обычного блока с
   `position:sticky; bottom:0` — и не показывался вообще. Sticky работает только В ПРЕДЕЛАХ
   родителя, а у последнего ребёнка блока, растущего по контенту, «пределов» ниже уже нет.
   :not(.hidden) обязателен — иначе display:flex подрался бы с классом .hidden. */
#admin-book-tab-contact:not(.hidden) {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
}
#admin-book-tab-contact > .contact-src-tabs,
#admin-book-tab-contact > .contact-search-bottom {
    flex: 0 0 auto;
}
#admin-book-tab-contact > #admin-book-contact-list {
    flex: 1 1 auto;
    min-height: 0;       /* иначе список не сожмётся и вытолкнет поиск за край окна */
}

/* Полоса поиска — нижняя строка колонки (см. index.html → admin-book-tab-contact). */
.contact-search-bottom {
    padding-top: var(--space-2);
    background: var(--th-surface-1, var(--c-surface-1));
}

/* [УБРАНО 2026-08-23] Текст-пояснение над списком контактов убран из разметки целиком по
   просьбе владельца (не просто прячется при скролле, как раньше, — его больше нет в DOM) —
   правила #admin-book-contact-hint удалены за ненадобностью. */

/* [ПОИСК: ИКОНКА-ЛУПА 2026-08-23] Обёртка позиционирует {icon:search} слева внутри поля —
   сам инпут получает левый отступ ТОЛЬКО в этом контексте (общий .form-input не трогаем,
   он используется и без иконки в других местах приложения). */
.contact-search-input-wrap {
    position: relative;
    display: flex;
    align-items: center;
}
.contact-search-input-wrap .contact-search-icon {
    position: absolute;
    left: var(--space-2);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--tg-theme-hint-color, var(--c-hint));
    pointer-events: none;
}
.contact-search-input-wrap .contact-search-icon svg { width: 16px; height: 16px; }
.contact-search-input-wrap input.form-input {
    padding-left: 34px;
}

/* Заголовок группы контактов — тот же разделитель секции, что в журнале (.mod-date-header). */
.contact-group-header {
    padding: var(--space-2) var(--space-1) var(--space-1);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    color: var(--tg-theme-hint-color, var(--c-hint));
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* --- [КАТЕГОРИИ КОНТАКТОВ 2026-08-22] Вкладка «Группы» — двухуровневая: список категорий,
   тап по категории → её контакты. Строка категории построена на канонической .contact-item
   (правило 2), своё — только круглый значок вместо аватара. --- */
.contact-cat-icon {
    width: 40px;
    height: 40px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--th-surface-2, var(--c-surface-2));
    color: var(--tg-theme-hint-color, var(--c-hint));
}
.contact-cat-icon svg { width: 20px; height: 20px; }

/* Возврат из категории к списку категорий. Отдельная строка, а не кнопка в шапке модалки:
   шапку занимает закрытие всего окна, и два «назад» рядом путали бы. */
.contact-cat-back {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    width: 100%;
    padding: var(--space-2) var(--space-1);
    border: none;
    background: transparent;
    color: var(--th-accent, var(--c-accent));
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    cursor: pointer;
    text-align: left;
}
.contact-cat-back:hover { text-decoration: underline; }
.contact-cat-back:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 1px; }
.contact-cat-back:active { opacity: 0.7; }
.contact-cat-back svg { width: 16px; height: 16px; }

/* ============================================================================
   [OWNER: УПРАВЛЕНИЕ ПРОЦЕССОМ] Блок кнопок «Перезагрузка» / «Выключение»
   ============================================================================
   Отделён от списка разделов панели: это не навигация, а действия над самим
   процессом. Поверхность и рамка — те же токены, что у .owner-hub-item, чтобы
   блок читался частью панели, а не чужой врезкой.

   ⚠ ФОРМА КНОПОК — КОПИЯ .tab-btn нижней навигации (значок сверху, слово под ним).
   Первая версия была двумя широкими кнопками во всю строку, одна из них — залитая
   красным .btn-danger; владелец указал, что они слишком крупные и перетягивают
   внимание. Это редкие служебные действия: они обязаны быть спокойными, а вес
   предупреждения несёт модалка подтверждения, а не сама кнопка.
   Цвета — только из палитры (правило 3). */
.owner-process-block {
    margin-top: var(--space-6);
    padding: var(--space-4);
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    border-radius: var(--radius-md);
    background: var(--th-surface-1, var(--c-surface-1));
}

.owner-process-title {
    margin: 0 0 var(--space-2);
    font-size: var(--fs-md);
}

/* Кнопки НЕ растягиваются во всю ширину: фиксированная колонка держит их
   компактными, а лишнее место остаётся пустым — так блок не выглядит панелью
   основных действий. */
.owner-process-actions {
    display: flex;
    gap: var(--space-2);
}

.owner-proc-btn {
    flex: 0 0 auto;
    width: 88px;
    display: flex;
    flex-direction: column;
    align-items: center;
    /* flex-start, как у .tab-btn: при длинной локали подпись уходит на 2-ю строку,
       а значки остаются на одном уровне. */
    justify-content: flex-start;
    gap: var(--space-1);
    min-height: var(--control-h);   /* 44px тач-цель (WCAG 2.5.5) */
    padding: var(--space-2) var(--space-1);
    border: 1px solid var(--th-border-subtle, var(--c-border-subtle));
    border-radius: var(--radius-sm);
    background: none;
    color: var(--tg-theme-text-color);
    cursor: pointer;
    transition: background-color .15s ease, border-color .15s ease;
}

.owner-proc-ico {
    display: inline-flex;
    line-height: 1;
}
.owner-proc-ico .icn svg { width: 22px; height: 22px; }

.owner-proc-lbl {
    font-size: var(--fs-2xs);
    font-weight: var(--fw-medium);
    line-height: 1.1;
    text-align: center;
}

/* Состояния (правило 4). */
.owner-proc-btn:hover  { background: var(--th-surface-2, var(--c-surface-2)); }
.owner-proc-btn:focus-visible {
    outline: 2px solid var(--c-accent);
    outline-offset: 2px;
}
.owner-proc-btn:active { transform: scale(0.94); }
.owner-proc-btn:disabled {
    opacity: .5;
    cursor: default;
    transform: none;
}

/* Опасность помечена ТОЛЬКО цветом значка — заливки нет намеренно (см. шапку). */
.owner-proc-btn--danger .owner-proc-ico { color: var(--c-danger); }
.owner-proc-btn--danger:hover { border-color: var(--c-danger); }
