/* ============================================================
   TisOS · Сдвижное боковое меню — ОБЩИЙ механизм (Волна 10.9)
   Пара к public/js/side-menu.js. Подключается ЛЮБОЙ страницей, у которой есть
   разметка `.layout > aside.sidebar + main.main` (сегодня — только admin.html;
   панель врача смонтирует позже, отдельной сессией).
   ============================================================

   ПОЧЕМУ ПЕРЕМЕННОЙ НЕТ В РАЗВЁРНУТОМ СОСТОЯНИИ. Критерий приёмки волны — вычисленные
   стили ВСЕХ существующих элементов до и после волны совпадают до последнего свойства.
   Пользовательское свойство, объявленное на :root, наследуется и попадает в
   getComputedStyle КАЖДОГО элемента страницы — замер показал бы расхождение на всех
   58 596 узлах. Поэтому --sb-w НЕ ОБЪЯВЛЕНА нигде, пока состояние по умолчанию:
   `.layout` читает её как var(--sb-w, 240px) и берёт запасное значение. Объявляется она
   только тогда, когда человек СОЗНАТЕЛЬНО ушёл от умолчания — правилом ниже (свёрнуто)
   или строчным стилем на <html> из JS (своя ширина после перетаскивания). Побочная
   польза: не загрузился этот файл — меню осталось ровно таким, каким было до волны.

   ШИРИНА СВЁРНУТОГО 77 px — не «на глаз», а замер:
     16 px (padding-left у .sidebar) + 44 px (тач-цель проекта) + 16 px (padding-right)
     + 1 px (border-right у .sidebar; в проекте box-sizing:border-box, styles.css:34 —
       рамка съедает ширину изнутри).
   44 px — та же тач-цель, что у .btn (styles.css) и у тулбара сетки (Волна 10.8, блок 3.2).
   Первый заход волны взял 76 px «по арифметике без рамки» — замер показал пункт 43 px,
   то есть на пиксель УЖЕ обещанного. Число исправлено по замеру, а не наоборот.
   Замер существующего пункта до волны: .side-item = 207 × 46 px, иконка 24 × 24 px;
   в свёрнутом пункт ровно 44 × 44 px.

   ПОЧЕМУ ВЕСЬ СВЁРНУТЫЙ ВИД — ВНУТРИ @media (min-width: 761px). До 760 px меню и так
   превращается в выдвижной ящик поверх содержимого (admin.css: position:fixed +
   transform), и ширина ящика ничего не отнимает у экрана — там роль «свёрнутого»
   играет сам гамбургер. А transform на ящике делает его containing block для
   position:fixed — выезжающие панели разделов считались бы от ящика, а не от окна,
   и уехали бы. Поэтому на узком экране механизм просто не включается: ящик всегда
   с подписями, и новые правила не дерутся со старыми. */

@media (min-width: 761px) {

  html.sb-collapsed { --sb-w: 77px; }

  /* ── Свёрнутое состояние ───────────────────────────────────────────────────
     Все правила включаются ТОЛЬКО классом sb-collapsed на <html>. В развёрнутом
     состоянии ни одно из них не применяется — вычисленных стилей они не трогают. */

  /* Логотип. Слово «TisOS» в 44 px не помещается ни при каком кегле, поэтому в свёрнутом
     остаётся монограмма «T» — ссылка на главную работает так же, обрезки посреди буквы
     нет. font-size:0 гасит текст, не убирая узел (текст лежит голым, без обёртки —
     display:none к нему не применить); монограмму рисует ::before, узлов не добавляя. */
  html.sb-collapsed .sidebar-logo { font-size: 0; padding: 0 0 10px; text-align: center; }
  html.sb-collapsed .sidebar-logo::before { content: 'T'; font-size: 26px; line-height: 1.6; }

  /* Пункты и головы разделов: только иконка по центру. Подпись — тем же приёмом
     font-size:0 (у .side-item подпись тоже голый текстовый узел). Тач-цель — min-height. */
  html.sb-collapsed .sidebar .side-item,
  html.sb-collapsed .sidebar .side-group-head {
    justify-content: center; gap: 0; padding: 10px; min-height: 44px; font-size: 0;
  }
  html.sb-collapsed .sidebar .side-group-head > span,
  html.sb-collapsed .sidebar .side-chevron { display: none; }
  /* Иконки не должны съёжиться вслед за font-size:0 — размер задаём явно. */
  html.sb-collapsed .sidebar .side-item > svg,
  html.sb-collapsed .sidebar .side-item > i,
  html.sb-collapsed .sidebar .side-group-head > svg:first-child,
  html.sb-collapsed .sidebar .side-group-head > i:first-child { width: 24px; height: 24px; flex: none; }
  html.sb-collapsed .side-nav { overflow-x: hidden; }

  /* 🔴 БЕЗ ЭТОЙ СТРОКИ РЕЖИМ ИКОНОК НЕ РАБОТАЕТ, и это не украшение — замер блока 2.
     .sidebar стоит на position:sticky, а sticky САМ ПО СЕБЕ создаёт контекст наложения
     с z-index:auto (=0). Всё, что внутри меню, — и подсказки, и выезжающие панели —
     заперто в этом контексте: их собственные z-index 300/320 упорядочивают их лишь
     ДРУГ ОТНОСИТЕЛЬНО ДРУГА, но поднять над содержимым справа не могут. А справа есть
     липкая шапка сетки (.sg-head, z-index 5) и позиционированные колонки (.sg-col).
     Замер: панель выезжала, но document.elementFromPoint в её точке отдавал .sg-col —
     то есть курсор попадал в СЕТКУ, панель закрывалась, и 19 из 22 пунктов оказывались
     недостижимы. Поднимаем весь sidebar: 100 — выше всего содержимого страницы
     (максимум 40 у .topbar) и ниже всех модалок и тостов (1000, 1100, 1300).
     Только в свёрнутом: в развёрнутом ничего никуда не выезжает. */
  html.sb-collapsed .sidebar { z-index: 100; }

  /* ── Кнопка сворачивания ───────────────────────────────────────────────────
     ЕДИНСТВЕННЫЙ новый узел разметки за всю волну; создаёт её JS, чтобы страницам не
     пришлось её дублировать. В РАЗВЁРНУТОМ состоянии кнопка вынута из потока
     (position:absolute) — иначе она сдвинула бы .side-nav вниз на 48 px и порушила
     критерий «до = после». Место есть: логотип «TisOS» занимает левые ~100 px из 207.
     .sidebar уже позиционирован (sticky), отдельного position:relative не нужно.
     В свёрнутом кнопка встаёт В ПОТОК отдельной строкой под монограммой. В обоих
     состояниях она вне .side-nav — прокрутка меню её не уносит. */
  html.sb-collapsed .sb-toggle { position: static; align-self: center; margin-bottom: 2px; }
  html.sb-collapsed .sb-toggle svg { transform: rotate(180deg); }

  /* ── Подсказка при наведении и при фокусе с клавиатуры ─────────────────────
     Это не украшение: «Уровни лояльности», «Права доступа», «Призраки», «Журнал»,
     «Контроль админов» по иконке не различимы. Текст берётся из data-tip, который JS
     проставляет из самой подписи пункта — расходиться им негде.
     position:fixed, потому что .side-nav прокручиваемый: обычная абсолютная подсказка
     обрезалась бы его границей. Координаты кладёт JS в --sb-tip-x/y на <html>
     (одна подсказка на экране за раз), поэтому переменных не плодим. */
  html.sb-collapsed .sidebar .side-item[data-tip]:hover::after,
  html.sb-collapsed .sidebar .side-item[data-tip]:focus-visible::after {
    content: attr(data-tip); position: fixed;
    left: var(--sb-tip-x, 84px); top: var(--sb-tip-y, 0px); transform: translateY(-50%);
    z-index: 320; white-space: nowrap; pointer-events: none;
    background: var(--text); color: #fff; border-radius: 6px; padding: 6px 10px;
    font-family: var(--font-head); font-weight: 600; font-size: 12px; line-height: 1.3;
    box-shadow: var(--shadow-lg);
  }

  /* ── Выезжающие подразделы ─────────────────────────────────────────────────
     Панель — ТОТ ЖЕ узел .side-group-items, что и в развёрнутом меню, а не копия:
     значит, обработчики кликов, подсветка активного и порядок пунктов везде одни.
     В свёрнутом он вынут из потока (position:fixed) — иначе подпункты с отступом 42 px
     не влезли бы в 44-пиксельную полосу и были бы обрезаны прокруткой .side-nav.
     visibility:hidden в закрытом виде обязательна: без неё подпункты ловили бы фокус
     с клавиатуры, будучи невидимыми. Координаты ставит JS от иконки раздела — поэтому
     панель стоит напротив своей иконки даже у прокрученного меню.
     Переходы здесь выключены намеренно: JS измеряет высоту панели сразу после
     открытия, чтобы прижать её к нижнему краю экрана, — на анимации он измерил бы ноль. */
  html.sb-collapsed .side-group > .side-group-items {
    position: fixed; left: 0; top: 0; width: 240px; z-index: 300;
    max-height: 0; opacity: 0; visibility: hidden; margin-top: 0; padding: 0;
    transition: none; background: var(--card); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: var(--shadow-xl);
  }
  /* Потолок 600 px тот же, что у развёрнутого аккордеона (admin.css), плюс защита от
     нижнего края окна. Что не поместилось — ПРОКРУЧИВАЕТСЯ, а не обрезается молча. */
  html.sb-collapsed .side-group.sb-fly > .side-group-items {
    max-height: min(600px, calc(100vh - 16px)); opacity: 1; visibility: visible;
    overflow-y: auto; padding: 6px;
  }
  /* Шапка панели = имя раздела: иначе, выехав сбоку, список висит без подписи. */
  html.sb-collapsed .side-group.sb-fly > .side-group-items::before {
    content: attr(data-tip); display: block; padding: 6px 10px 8px;
    font-family: var(--font-head); font-weight: 700; font-size: 11px; letter-spacing: .04em;
    text-transform: uppercase; color: var(--text-muted);
  }
  /* Внутри панели пункты выглядят как в развёрнутом меню: подпись слева от иконки. */
  /* min-height 44 px, а не 41 px как у подпунктов развёрнутого аккордеона: в режиме
     иконок выезжающая панель — ЕДИНСТВЕННЫЙ способ попасть в подпункт, и тач-цель здесь
     обязана быть полной. */
  html.sb-collapsed .side-group.sb-fly > .side-group-items .side-item {
    justify-content: flex-start; gap: 12px; padding: 10px 12px; font-size: 13.5px; min-height: 44px;
  }
  html.sb-collapsed .side-group.sb-fly > .side-group-items .side-item > svg,
  html.sb-collapsed .side-group.sb-fly > .side-group-items .side-item > i { width: 16px; height: 16px; }
  /* Подсказка внутри панели не нужна — там и так подпись. */
  html.sb-collapsed .side-group.sb-fly > .side-group-items .side-item[data-tip]:hover::after,
  html.sb-collapsed .side-group.sb-fly > .side-group-items .side-item[data-tip]:focus-visible::after { content: none; }

  /* ── Активный раздел виден и в свёрнутом ───────────────────────────────────
     Сам активный пункт красит .side-item.active (admin.css). Но если он лежит ВНУТРИ
     раздела, в полосе видна только голова раздела — её и подсвечиваем. */
  html.sb-collapsed .side-group:has(.side-item.active) > .side-group-head {
    color: var(--primary); background: var(--card-tint);
  }

}

/* ── Кнопка сворачивания: общий вид (нужен и до 761 px, где она скрыта) ────── */
.sb-toggle {
  position: absolute; top: 20px; right: 16px; z-index: 3;
  width: 44px; height: 44px;                 /* тач-цель проекта, не меньше */
  display: flex; align-items: center; justify-content: center;
  border: 1px solid transparent; background: transparent; border-radius: var(--radius-sm);
  color: var(--text-muted); cursor: pointer; padding: 0;
}
.sb-toggle:hover { background: var(--bg); color: var(--primary); border-color: var(--border); }
/* Одна иконка на оба состояния: разворачиваем поворотом, а не подменой узла —
   иначе после каждого нажатия пришлось бы заново звать lucide.createIcons(). */
.sb-toggle svg, .sb-toggle i { width: 20px; height: 20px; transition: transform .2s ease; }
/* До 760 px сворачивать нечего (см. объяснение в шапке), кнопку не показываем. */
@media (max-width: 760px) { .sb-toggle { display: none; } }

/* ── Ручка перетаскивания ────────────────────────────────────────────────────
   ВИДИМАЯ линия — собственная рамка меню (border-right, 1 px), она была и до волны.
   ЛОВИТСЯ полоса шире: 10 px поверх границы (по 5 px в каждую сторону) — в 1 px мышью
   не попасть. Ручка — псевдоэлемент, новых узлов разметки волна не заводит; курсор
   меняется сам, потому что псевдоэлемент принимает указатель.
   В покое ручка ПРОЗРАЧНА: критерий «до = после» запрещает менять вид развёрнутого
   меню. Подсвечивается она только пока тянут — и не всей полосой, а линией в 2 px
   по центру зоны захвата, чтобы не выглядеть жирнее самой границы. */
.sidebar::after {
  content: ''; position: absolute; top: 0; bottom: 0; right: -5px; width: 10px;
  z-index: 4; cursor: col-resize; touch-action: none;
}
@media (max-width: 760px) { .sidebar::after { display: none; } }
html.sb-dragging { cursor: col-resize; user-select: none; -webkit-user-select: none; }
html.sb-dragging .sidebar::after {
  background: linear-gradient(to right, transparent 4px, var(--primary) 4px, var(--primary) 6px, transparent 6px);
}

/* ── Выдвижной ящик на узком экране: задник и доступный гамбургер (Волна 10.10) ──
   Пара к разделу «Выдвижной ящик» в public/js/side-menu.js. Всё ниже включается ТOЛЬКO
   классом sb-drawer-open на <html>, который JS ставит на время открытого ящика. В покое
   не применяется НИ OДНO правило — вычисленные стили страницы с закрытым ящиком остаются
   доволновыми до последнего свойства (критерий блока 2.5, замер: 5181 узел, 0 расхождений).

   ЗAЧЕМ ПOДНИМAЕМ ШAПКУ, A НЕ КНOПКУ. `.topbar` стоит на position:sticky с z-index 40 и
   САМ создаёт контекст наложения. Любой z-index, выданный кнопке внутри шапки, упорядочит
   её лишь среди детей шапки — над ящиком (z-index 200, admin.css:332) не поднимет ни при
   каком значении. Замер блока 2 это подтвердил: с z-index 210 на самой кнопке
   document.elementFromPoint в её точке по-прежнему отдавал a.logo.sidebar-logo. Поэтому
   поднимается КOНТЕКСТ — вся шапка целиком. Побочное следствие названо честно: пока ящик
   открыт, белая полоса шапки высотой 72 px накрывает верх ящика, где лежит логотип; пункты
   меню начинаются ниже (замер на 390: первый пункт на y = 85) и видны все.

   Задник — 190: ниже ящика (200), чтобы клик по самому меню доходил до пунктов, и выше
   всего содержимого (максимум 40 у .topbar в покое). */
@media (max-width: 760px) {
  .sb-backdrop {
    position: fixed; inset: 0; z-index: 190;
    background: rgba(16, 28, 44, .45);
    /* Задник обязан ЛOВИТЬ указатель: тап мимо ящика — один из трёх способов закрытия.
       Прятать его ради прохождения проверки запрещено (блок 2.5). */
    pointer-events: auto;
  }
  html.sb-drawer-open .topbar { z-index: 210; }
}
/* Атрибут hidden прячет задник при закрытом ящике: скрытый узел указателя не ловит, и
   клик в середину экрана доходит в содержимое (блок 2.2, проверяется замером).
   Правило вне медиазапроса: если окно расширили при открытом ящике, JS снимает hidden
   не мгновенно, а задник обязан исчезнуть в ту же секунду. */
.sb-backdrop[hidden] { display: none; }

/* ── Плавность ───────────────────────────────────────────────────────────────
   Переход навешивается ТОЛЬКО на время анимации (класс sb-anim ставит и снимает JS).
   Если оставить его в покое, у .layout изменилось бы вычисленное свойство transition —
   то самое «до = после», которое волна обязана удержать.
   prefers-reduced-motion в проекте уже гасит все переходы глобально (styles.css),
   но здесь сказано явно: правило волны не должно зависеть от чужого правила. */
html.sb-anim .layout { transition: grid-template-columns .2s ease; }
@media (prefers-reduced-motion: reduce) {
  html.sb-anim .layout { transition: none; }
}
