/* Шрифты — локальные, из /static/fonts. Раньше здесь стоял @import с
   fonts.googleapis.com: на машине разработчика (127.0.0.1) он проходил, а с
   портала в корпоративной сети (10.20.2.83) — нет, и весь интерфейс
   отрисовывался системным шрифтом. Подключаются отдельным <link> в index.html,
   а не @import: @import внутри CSS блокирует отрисовку до своей загрузки и
   не поддаётся автоверсионированию (оно правит только src/href в index.html). */

/* ═══════════════════════════════════════════════════════════
   UNIPORTAL · redesigned UI  —  "Refined corporate dark-tech"
   All selectors & CSS variables preserved 1:1 — JS untouched.
═══════════════════════════════════════════════════════════ */

/* ── DARK THEME (default) ───────────────────────────────── */
:root, [data-theme="dark"] {
  --bg:          #0a0d14;
  --bg2:         #11151f;
  --bg3:         #181d2b;
  --bg4:         #222a3d;
  --border:      #232a3c;
  --border2:     #34405c;
  --text:        #e9eef8;
  --text2:       #94a6c8;
  --text3:       #5a6b8c;
  --accent:      #4d8df0;
  --accent-h:    #7fb0ff;
  --accent-dark: #2f5fb0;
  --accent-dim:  rgba(77,141,240,.14);
  --green:       #34d399;
  --green-dim:   #0c241c;
  --gold:        #fbbf3d;
  --gold-dim:    #2a2008;
  --red:         #f0556b;
  --shadow:      0 24px 70px -12px rgba(0,0,0,.78), 0 8px 24px rgba(0,0,0,.5);
  --card-shadow: 0 1px 2px rgba(0,0,0,.5), 0 6px 18px -6px rgba(0,0,0,.55);
  --ring:        0 0 0 3px rgba(77,141,240,.22);
  --glow:        0 0 18px rgba(77,141,240,.55), 0 0 36px rgba(77,141,240,.28);
  --grid-line:   rgba(255,255,255,.022);
  --ambient:     radial-gradient(900px 520px at 82% -8%, rgba(77,141,240,.10), transparent 65%),
                 radial-gradient(700px 480px at 8% 104%, rgba(52,211,153,.05), transparent 60%);
  color-scheme: dark;
}

/* ── LIGHT THEME — "soft daylight" ─────────────────────── */
[data-theme="light"] {
  --bg:          #eef2f8;
  --bg2:         #ffffff;
  --bg3:         #f1f5fb;
  --bg4:         #e4ebf5;
  --border:      #e1e8f2;
  --border2:     #ccd8e8;
  --text:        #0e1a2e;
  --text2:       #4a5d7d;
  --text3:       #8b9bb5;
  --accent:      #2f6fe0;
  --accent-h:    #1f5cc8;
  --accent-dark: #1c4ba0;
  --accent-dim:  rgba(47,111,224,.10);
  --green:       #0f9d63;
  --green-dim:   #d6f4e6;
  --gold:        #c6850a;
  --gold-dim:    #fdefc8;
  --red:         #d63050;
  --shadow:      0 24px 60px -16px rgba(30,52,92,.28), 0 6px 18px rgba(30,52,92,.12);
  --card-shadow: 0 1px 2px rgba(30,52,92,.07), 0 6px 16px -8px rgba(30,52,92,.14);
  --ring:        0 0 0 3px rgba(47,111,224,.16);
  --glow:        0 0 18px rgba(47,111,224,.4), 0 0 36px rgba(47,111,224,.2);
  --grid-line:   rgba(30,52,92,.045);
  --ambient:     radial-gradient(900px 520px at 84% -10%, rgba(47,111,224,.08), transparent 62%),
                 radial-gradient(640px 440px at 4% 106%, rgba(15,157,99,.05), transparent 60%);
  color-scheme: light;
}

/* ── СЛОИ (z-index) ─────────────────────────────────────────
   Единой шкалы в портале исторически не было: числа от 200 до 2147483646
   расставлены по десятку файлов, и каждый новый слой подбирался «чтобы
   сейчас было видно». Так и появился баг: панель «Администрирование» в
   шапке имела 400, а страница профиля — 8500, и меню открывалось ПОД
   страницей. Ниже — шкала, к которой стоит приводить всё новое.

     --z-header      шапка портала
     --z-page        полноэкранная страница поверх обычного вида (профиль)
     --z-dropdown    меню и выпадающие панели шапки: обязаны быть выше
                     страницы, но ниже модалок
     --z-modal       модальные окна
     --z-tooltip     подсказки: выше модалок, иначе не видны в формах
     --z-toast       тосты и системные диалоги — поверх всего

   Переменные объявлены на :root (а не в теме), поэтому одинаковы в тёмной
   и светлой. Старые числа в JS не трогаем: их правка вслепую — отдельная
   работа с полным обходом экранов. */
:root{
  --z-header:   200;
  --z-page:     8500;
  --z-dropdown: 8600;
  --z-modal:    9500;
  --z-tooltip:  10600;
  --z-toast:    30000;
}

/* ── RESET ──────────────────────────────────────────────── */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
html,body{height:100%;overflow:hidden;}
body{
  font-family:'Inter Tight','Sora',sans-serif;
  background:var(--bg);
  background-image:var(--ambient);
  background-attachment:fixed;
  color:var(--text);font-size:13px;line-height:1.55;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  transition:background .25s,color .25s;
  /* По запросу Paul-а 18.06: запрет копирования текста на портале ВЕЗДЕ,
     кроме модуля заявок (#tickets-view-wrap) и текстовых полей. Это сужает
     утечку чувствительных данных через выделение мышью. Выделение всё ещё
     работает в input/textarea и внутри карточки/листа заявок. */
  user-select:none;
  -webkit-user-select:none;
}
/* В заявках копирование разрешено — оно нужно для номеров заявок, кодов
   ошибок, переписки между исполнителем и автором. */
#tickets-view-wrap,
#tickets-view-wrap *{
  user-select:text;
  -webkit-user-select:text;
}
/* Полю ввода всегда можно — пишут пользователи. */
input,textarea,select,[contenteditable="true"]{
  user-select:text;
  -webkit-user-select:text;
}
button{font-family:inherit;cursor:pointer;}
input,select,textarea{font-family:inherit;}
a{color:inherit;text-decoration:none;}
.dim{color:var(--text3);}
.mt6{margin-top:6px;}.mt8{margin-top:8px;}

/* Global scrollbars */
*::-webkit-scrollbar{width:9px;height:9px;}
*::-webkit-scrollbar-track{background:transparent;}
*::-webkit-scrollbar-thumb{background:var(--border2);border-radius:6px;border:2px solid transparent;background-clip:padding-box;}
*::-webkit-scrollbar-thumb:hover{background:var(--accent-dark);background-clip:padding-box;}
*{scrollbar-width:thin;scrollbar-color:var(--border2) transparent;}

::selection{background:var(--accent-dim);color:var(--accent-h);}

/* ═══════════════════════════════════════
   HEADER
═══════════════════════════════════════ */
.header{
  position:fixed;top:0;left:0;right:0;height:58px;
  background:color-mix(in srgb, var(--bg2) 80%, transparent);
  -webkit-backdrop-filter:blur(16px) saturate(160%);
  backdrop-filter:blur(16px) saturate(160%);
  border-bottom:1px solid var(--border);
  display:flex;align-items:center;padding:0 18px;gap:14px;z-index:var(--z-header);
  box-shadow:0 1px 0 rgba(255,255,255,.02), 0 8px 30px -12px rgba(0,0,0,.6);
  transition:background .25s,border-color .25s;
}
.logo{display:flex;align-items:center;gap:11px;cursor:pointer;flex-shrink:0;
  padding:5px 8px;border-radius:11px;transition:background .15s;}
.logo:hover{background:var(--bg3);}
.logo-img{height:34px;width:34px;border-radius:9px;object-fit:cover;
  box-shadow:0 2px 8px rgba(0,0,0,.35), inset 0 0 0 1px rgba(255,255,255,.06);}
.logo-text{
  font-family:'Sora',sans-serif;font-size:17px;font-weight:800;letter-spacing:-.2px;
  background:linear-gradient(120deg,var(--text) 30%,var(--accent-h));
  -webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;
  white-space:nowrap;
}
/* min-width:0 обязателен: без него flex-элемент не даёт себя сжать меньше
   содержимого, строка вкладок распирает шапку и выдавливает переключатель
   темы с блоком пользователя за край. overflow:hidden — страховка на тот
   случай, если JS-раскладка (navbar.js) не успела отработать. */
/* position:relative — точка отсчёта для бегущей плашки .nav-ind (она внутри). */
.header-nav{display:flex;gap:3px;flex:1;min-width:0;overflow:hidden;position:relative;}

/* Текст, который пишет пользователь: описания и комментарии заявок, посты,
   истории правок. Один класс на все такие места, чтобы правило не разъезжалось.

   Зачем. Вставленная в комментарий ссылка вида
   https://.../%D0%A0%D1%83%D0%BA%D0%BE... — это одно «слово» на несколько
   сотен символов: пробелов в процентной кодировке нет, переносить браузеру
   не по чему. Такое слово распирало карточку, за ней колонку, за ней всю
   страницу — правый столбец с информацией о заявке уезжал за экран, и читать
   можно было только уменьшив масштаб браузера.

   Почему именно так:
   • word-break:break-word — рвёт ТОЛЬКО то, что иначе не помещается; обычные
     слова остаются целыми. break-all рвал бы и их — читать было бы нельзя.
   • overflow-wrap:anywhere — то же самое по буквам стандарта, но его
     учитывают при расчёте минимальной ширины блока. Два свойства рядом не
     дублируют друг друга: разные браузеры поддерживают разные, а нам нужно,
     чтобы сжималось везде.
   • overflow:hidden сюда добавлять НЕЛЬЗЯ: это переписка, обрезанный текст
     означает потерянный текст. Только перенос. */
/* Общая страховка на весь портал. overflow-wrap наследуется, поэтому одно
   объявление на body закрывает любое место, где текст лежит в блоке с уже
   известной шириной: длинное «слово» переносится, а не торчит за край.
   Именно break-word, а НЕ anywhere: break-word не участвует в расчёте
   минимальной ширины блока, поэтому не может неожиданно сжать колонку,
   кнопку или ячейку таблицы — раскладка везде остаётся прежней.
   Там, где ширина блока сама зависит от содержимого (колонка грида 1fr,
   flex-элемент), одного этого мало — там дополнительно стоит .u-text. */
body{overflow-wrap:break-word;}
/* nowrap и обрезание с многоточием сильнее переноса, поэтому места вроде
   строки вкладок или названия в списке заявок ведут себя как раньше. */

.u-text, .mf-value{word-break:break-word;overflow-wrap:anywhere;}
/* Внутри такого текста бывает код в тройных кавычках. У <pre> собственный
   white-space:pre и своя прокрутка — там перенос не нужен и не должен
   включаться от родителя. */
.u-text pre{overflow-wrap:normal;word-break:normal;}
.nav-btn{
  /* Паддинг вынесен в переменные, потому что на узких экранах база другая
     (см. медиазапрос в конце файла), и любое состояние кнопки должно уметь
     сослаться на ту же базу, а не хардкодить десктопные числа. */
  --nb-py:8px; --nb-px:14px;
  /* nowrap обязателен. Без него подпись переносится на вторую строку раньше,
     чем navbar.js успевает убрать лишние вкладки в «Ещё»: он меряет
     offsetWidth уже сжатой кнопки и считает, что места хватает. А перенос в
     одной кнопке растягивает по высоте ВСЕ соседние (flex по умолчанию
     stretch), и вся строка скачет с 32px на 46px. Причём немонотонно — на
     780/880/900/960/980/1080/1100px ломалось, а на соседних ширинах нет,
     то есть при плавном изменении ширины окна шапка дёргалась несколько раз.
     В мобильном медиазапросе nowrap стоял давно; на обычных ширинах его не
     было. Стережёт check_wrap.py. */
  white-space:nowrap;
  position:relative;padding:var(--nb-py) var(--nb-px);border-radius:8px;border:1px solid var(--border);
  background:var(--bg3);color:var(--text2);font-size:12.5px;font-weight:600;
  letter-spacing:-.1px;
  /* ЦВЕТ ПОДПИСИ МЕНЯЕТСЯ МГНОВЕННО, и это не экономия, а необходимость.
     Заливка кнопки — радиальный градиент, а background-image переходами не
     анимируется в принципе: он появляется и исчезает кадром. Пока цвет ехал
     свои 0,14с, подпись оказывалась в промежуточном сером НА УЖЕ ГОЛУБОЙ
     заливке (или наоборот, белой на уже пропавшей) — в светлой теме контраст
     в этот момент падал до 1,0:1, буквы буквально исчезали. Замерено на всех
     четырёх переключениях: наведение, снятие наведения, вкладка стала
     активной, вкладка перестала быть активной.
     Остальные свойства переходами оставлены: рамка и тень действительно
     анимируются и ни с чем не рассинхронизированы. */
  transition:background .14s,border-color .14s,box-shadow .18s;
}
/* ПОЧЕМУ ПРИ СМЕНЕ ТЕМЫ ПОДПИСЬ ВСЁ-ТАКИ ПРЫГАЕТ, И ЭТО ОСОЗНАННО.
   При смене темы фон кнопки не меняется, отставать цвету не от чего, и
   плавный переход тут был бы уместен: body и шапка едут свои 0,25с, а подписи
   навбара переключаются кадром. Я это чинил — вешал на <html> метку на время
   смены темы и по ней возвращал `transition:color`. Пришлось откатить: метка
   действует на ВСЕ состояния кнопки, а не только на покой, и все 0,32с её
   жизни любое наведение/расфокус/смена активной вкладки снова получали тот
   самый рассинхрон с мгновенной заливкой — контраст падал до 1,4:1. Сузить
   исключение не выходит: `:not(:hover)` чинит вход в наведение и ломает выход
   из него (элемент снова начинает подходить под правило, а заливка уже
   исчезла кадром). Мелкая несогласованность на разовом действии дешевле, чем
   дыра в читаемости на каждом наведении в эти доли секунды.
   ЧТО ЭТО СТОИТ, ЧИСЛОМ, И ЧЕСТНО.
   Подпись переключается кадром, а фон шапки едет свои 0,25с и по дороге
   проходит РОВНО через её яркость. Замерено перемоткой перехода по прогрессу
   (а не гонкой со скриншотами): контраст падает до 1,0–1,1:1 примерно на 16%
   пути и держится ниже 2:1 на ~31% пути — то есть около 78мс из 250.
   Раскладывать это по направлениям (столько-то в светлую, столько-то в
   тёмную) бессмысленно: перемотка детерминирована, а вот съём пикселей после
   неё — нет, и от прогона к прогону направления меняются местами в пределах
   тех же сотых. Обе стороны проваливаются одинаково. Буквы в этот момент практически исчезают, в обе
   стороны переключения примерно одинаково. Числа печатает check_themedip; он
   же сторожит, чтобы окно не стало шире. Так же ведёт себя логотип: у него
   текст залит градиентом, заливка тоже переключается кадром.
   Это НЕ лечится укорочением перехода шапки. Я пробовал 0,12с и сначала решил,
   что помогло: замеры давали 4–5:1. Оказалось, что «помогло» только замеру —
   он гонялся со скриншотами за узким окном и просто перестал в него попадать.
   Перемотка по прогрессу показывает те же 1,0:1 и при 0,12с, и при 0,25с:
   глубина определяется тем, что кривая фона пересекает яркость подписи, а от
   длительности зависит только ширина окна. Зато шов между шапкой и страницей
   от расхождения длительностей появляется настоящий: шапка приезжает, страница
   ещё едет. Поэтому 0,25с возвращены.
   Настоящее лечение — синхронизировать заливку кнопки с цветом подписи, а для
   этого заливку надо перенести с самого фона кнопки на псевдоэлемент, чтобы
   она умела плавно проявляться (градиент переходами не анимируется, а
   прозрачность псевдоэлемента — умеет). Это переделка «шкуры», которая
   отлаживалась много кругов, поэтому отдельной работой, а не походя.
   Замеряет глубину check_themedip.py — он перематывает переход по прогрессу
   через getAnimations(), поэтому не зависит ни от длительности, ни от скорости
   машины. */

/* ── Вкладки: бегущая плашка под активной ─────────────────────────────────

   «Свет за стеклом»: внутри вкладки медленно ездит мягкое световое пятно —
   как лампа за матовым стеклом. Полос и границ у градиента нет вообще, только
   светлее-темнее, поэтому движение не мельтешит.

   Самая светлая точка — центр пятна, и это фирменный голубой БЕЗ подмешанного
   белого. В прототипе центр был высветлен на 10% белым, и контраст с подписью
   падал до 4,0:1 при норме 4,5:1 — читать было бы уже тяжело. Свечение здесь
   даёт не яркость центра, а глубина фона вокруг: края уходят в тёмно-синий,
   и на их фоне тот же голубой читается как свет.

   Почему не чистый --accent: в тёмной теме это #4d8df0, контраст 3,3:1.
   Берём тот же голубой на пару тонов глубже — фирменным быть не перестаёт.

   ПОД КУРСОРОМ пятно приглушено (--nav-h*), у активной вкладки — в полную
   силу. Иначе, пока ведёшь мышью по шапке, каждая вкладка по очереди светится
   ровно как выбранная, и на секунду теряется ответ на вопрос «где я сейчас»:
   разница только в начертании 700 против 600, а на 12,5px её не видно.       */
:root{
  --nav-g1:#346cbd;   /* центр пятна: фирменный голубой, приглушён до читаемого */
  --nav-g2:#27508f;   /* полутень */
  /* Край. В тёмной теме он не может быть по-настоящему тёмным: фон шапки сам
     почти чёрный (rgb(15,19,29)), и на #16294a край кнопки давал к нему
     контраст всего 1,28:1 — широкая вкладка теряла форму, дальняя половина
     растворялась в шапке.
     Это компромисс, а не свободный выбор: чем светлее край, тем ровнее форма и
     тем слабее перелив. Замерено на плашке 120px по реальным пикселям:
        #16294a → край 1,28:1, размах яркости 70%
        #1e3a68 → край 1,64:1, размах 60%   ← взяли это
        #26477e → край 2,02:1, размах 52%
     Ориентир WCAG 1.4.11 (3:1 для границ элементов управления) тут сознательно
     не выдержан, и НЕ потому, что он недостижим: фон шапки имеет яркость
     0,0066, для 3:1 краю нужна яркость 0,120, а это ниже яркости центра пятна
     (--nav-g1, 0,151) — арифметически такой край существует. Причина другая:
     0,120 ВЫШЕ полутени --nav-g2 (0,082), то есть край стал бы светлее середины
     и затухание центр→край перевернулось бы — вместо пятна получилось бы
     кольцо. Сам критерий 1.4.11 здесь и не про это: кнопку опознают по белой
     полужирной подписи, её контраст (5,149:1) проверяется отдельно, граница
     плашки опознавательной нагрузки не несёт.
     Обводкой форму не подпираем — заказчик её снял отдельной правкой.
     Четвёртый, светлый стоп у самого ободка тоже пробовали: края кнопки в
     середине цикла всё равно попадают в тёмную зону, край так и остаётся
     1,3:1 — приём не работает, поэтому его нет. */
  --nav-g3:#1e3a68;
  --nav-h1:#264f8a;   /* то же самое под курсором, тише активной вкладки */
  --nav-h2:#204175;
  /* Насколько тише: пик наведённой — это --nav-h1, пик активной — --nav-g1, и
     сравнивать надо именно пики. От фазы они зависят слабо (замер по полосе
     НАД подписью даёт разброс 17–23% — сама полоса лежит выше вертикального
     хода пятна, поэтому чистый --nav-g1 в неё не попадает никогда), а фазы у
     плашки и ховера независимы (8с против 5с) и перебирают все комбинации.
     Поэтому проверка не считает пик константой, а честно перебирает 13 фаз и
     берёт худшее сочетание: впадину пика активной против вершины пика
     наведённой. Запас худшей пары: +47% в тёмной теме, +27% в светлой —
     фазовый разброс он перекрывает с большим запасом.
     ОГОВОРКА ПРО МЕТРИКУ, чтобы её не переизобретали: сначала сравнивались
     СРЕДНИЕ яркости, и по ним выходило «пересечение» — узкая наведённая
     вкладка якобы перебивала широкую активную. Собранный кадр показал
     обратное: средняя обманывает, у широкой плашки в неё входит много тёмного
     края. Глаз цепляется за самую светлую точку, метрика — тоже.
     Своего тёмного тона у ховера НЕТ: он берёт --nav-g3. Раньше был отдельный
     и более тёмный — на 18% по каналам RGB и на 31% по яркости, но считать
     надо от ТОГДАШНЕГО --nav-g3 (#16294a): край подняли до #1e3a68 тем же
     коммитом, которым лишний тон убрали. И получалось, что именно под
     курсором край кнопки хуже всего отличался от шапки —
     приглушение топило её там, где и так было туго. Приглушение читается по
     яркому центру, тёмному краю темнеть незачем. */
  /* Размер пятна — в пикселях, а не в процентах, поэтому вынесен в переменные:
     на узкой мобильной шапке кнопка 58px, и крупное пятно накрывало бы её
     целиком, превращая «свет» в ровную заливку. Правка размера — в одном месте.
     Проценты тут не помогли бы, а min()/clamp() с процентом внутри радиуса
     градиента Chrome вообще не принимает — проверено, объявление отваливается
     целиком. Поэтому размер фиксированный, а под узкий экран — медиазапрос. */
  --nav-spot-w:86px;
  --nav-spot-h:60px;
  /* Под курсором пятно ещё и меньше, не только тише — наведение должно быть
     сигналом полегче, чем выбор. Проверено, что это ничего не стоит: размах
     яркости у наведённой кнопки при 64x48 даже чуть выше, чем при 86x60
     (33% против 32% в тёмной теме, 59% против 54% в светлой) — пятно меньше,
     зато тёмного поля вокруг него больше. */
  --nav-spot-w-hover:64px;
  --nav-spot-h-hover:48px;
  --nav-flow-dur:8s;          /* полный проход пятна туда и обратно */
  --nav-flow-dur-hover:5s;    /* под курсором — живее, но так же спокойно */
  --nav-shadow:0 2px 8px -3px rgba(0,0,0,.55);
}
[data-theme="light"]{
  --nav-g1:#3070de;   /* потолок по контрасту с белой подписью, см. ниже */
  --nav-g2:#2a60bf;
  /* Светлая тема осветлена целиком: на белой шапке прежняя палитра читалась
     тёмными пятнами. Ограничитель тут один и жёсткий — белая подпись. Её
     контраст считается с САМОЙ СВЕТЛОЙ точкой пятна, поэтому центр нельзя
     поднимать выше #3070de: там ровно 4,67:1 при норме 4,5, следующий шаг
     (#3374e0) даёт уже 4,45 и норму не проходит.
     Зато край поднят сильно: с #112c53 (яркость 0,026) до #22509f (0,086),
     втрое. Именно тёмный край и делал кнопку «пятном» — центр-то был почти
     на месте. Форма при этом не теряется. Арифметика по самому цвету:
     #22509f к чистому белому 7,73:1, к фону шапки rgb(249,251,254) — 7,46:1.
     На экране край чуть светлее самого цвета — пятно дрейфует, и крайний
     пиксель берётся из градиента: check_edge меряет отрисованное и по всем
     девяти фазам дрейфа даёт не ниже 7,03:1 при пороге 2,5. Числа расходятся
     не от ошибки: одно — про цвет, другое — про пиксель.
     Размах пятна 51% — меньше прежних 83%, это цена осветления. */
  --nav-g3:#22509f;
  --nav-h1:#2a5cb0;
  --nav-h2:#26559f;
  --nav-shadow:0 2px 10px -4px rgba(47,111,224,.5);
}
/* Мобильная шапка: кнопки вдвое уже, пятно уменьшаем пропорционально — иначе
   эффект на них не читается. Переопределение стоит ПОСЛЕ обоих блоков выше и
   тему не трогает: --nav-spot-* от темы не зависят. */
@media (max-width:768px){
  :root{--nav-spot-w:58px;--nav-spot-h:44px;
        --nav-spot-w-hover:44px;--nav-spot-h-hover:34px;}
}

/* Единственная анимация навбара: пятно проходит кнопку справа налево и
   возвращается. Проценты background-position считаются от РАЗНИЦЫ размеров
   картинки и кнопки, а картинка вдвое больше кнопки, поэтому положение центра
   пятна = ширина × (1 − позиция): 18% → центр на 82% ширины, 82% → на 18%.
   Итого пятно реально проходит около двух третей кнопки, а не топчется в углу.
   ease-in-out — у краёв движение почти останавливается, поэтому глаз не ловит
   ни старта, ни разворота. Длительности живут в переменных выше — иначе их
   приходится править в трёх местах и они разъезжаются. */
@keyframes navDrift{
  0%   {background-position:18% 38%;}
  50%  {background-position:82% 62%;}
  100% {background-position:18% 38%;}
}

/* Вкладки в строке — прозрачные: фон им рисует плашка. В выпадающих панелях
   .nav-btn остаётся обычной «таблеткой». */
.header-nav > .nav-btn{background:transparent;border-color:transparent;z-index:1;}
/* «Администрирование ▾» и «Ещё ▾» — не вкладки, а кнопки-меню: под ними плашка
   не ездит, и без собственного фона они превращались в голый текст. */
.header-nav > .nav-btn.nav-dd-trigger{background:var(--bg3);border-color:var(--border);}
.header-nav > .nav-btn.nav-dd-trigger.open{background:var(--bg4);border-color:var(--accent);}
/* ВАЖЕН ПОРЯДОК: этот блок обязан стоять ДО «шкуры» ниже. У `.open` и у
   `.nav-dd-trigger:hover` из «шкуры» специфичность одинаковая (0,0,4,0), и
   спор решается порядком в файле. Поставь этот блок после — и наведение на
   УЖЕ ОТКРЫТЫЙ триггер теряло бы заливку, оставляя крутиться анимацию по
   пустому фону. */

/* Общая «шкура»: и плашка, и наведённая кнопка, и активный пункт выпадающей
   панели. Селекторы с префиксом `.header-nav >` перечислены отдельно
   намеренно: без них правила прозрачных вкладок ниже идут с той же
   специфичностью, но позже по файлу, и гасили бы заливку при наведении. */
.nav-ind,
.nav-btn:hover,.nav-btn:focus-visible,
.header-nav > .nav-btn:hover,.header-nav > .nav-btn:focus-visible,
.header-nav > .nav-btn.nav-dd-trigger:hover,
.header-nav > .nav-btn.nav-dd-trigger:focus-visible,
.nav-dd-panel .nav-btn.active{
  /* Радиусы заданы в пикселях, а не в процентах: у вкладок ширина от «Цели»
     (~60px) до «Переговорные» (~150px), и процентные радиусы растянули бы
     пятно на широкой кнопке в горизонтальную полосу — один и тот же приём
     выглядел бы на соседних вкладках по-разному. В пикселях пятно везде
     одного размера, как настоящий источник света.

     `at 50% 50%` — центр пятна в середине КАРТИНКИ (она вдвое больше кнопки),
     а не в её углу. Со смещённым центром половину цикла пятно уезжало за левый
     край кнопки, и вместо «свет едет» получалось «угол то загорается, то
     гаснет»; правая треть широких кнопок не светилась вообще никогда. */
  /* Стопы сдвинуты к центру (было 0/46/88): при прежней раскладке пятно было
     шире любой кнопки, и до тёмного края --nav-g3 дело не доходило нигде —
     на экране работала треть палитры, а яркость в неподвижной точке за цикл
     менялась всего на 12% (узкая вкладка) и 27% (широкая).
     Сейчас размах у плашки 42–53% в тёмной теме; в светлой он меньше — там
     край подняли отдельно, ради общей светлоты (см. блок про светлую тему),
     и по расчёту яркостей размах вышел 51% против 83% прежних. Разброс внутри
     темы — это разница между узкой вкладкой и широкой. Это и есть та
     «выраженность», которой не хватало, без нового цвета и без ускорения. */
  background:radial-gradient(var(--nav-spot-w) var(--nav-spot-h) at 50% 50%,
             var(--nav-g1) 0%, var(--nav-g2) 32%, var(--nav-g3) 58%);
  background-size:200% 200%;
  /* Якорь = первый кадр анимации. Шорткод `background` выше сбрасывает позицию
     в 0% 0%, куда анимация не заходит никогда, и при её выключении (кнопка в
     шапке) пятно прыгало в чужой угол. С якорем «выключено» совпадает с местом
     естественной остановки, а включение обратно проходит вообще без скачка. */
  background-position:18% 38%;
  border-color:transparent;
  animation:navDrift var(--nav-flow-dur) ease-in-out infinite;
}

.nav-ind{
  position:absolute;left:0;top:0;z-index:0;pointer-events:none;
  border-radius:9px;border:0;opacity:0;
  /* Переезд между вкладками — без пружины: заказчик просил плавнее, а
     обратный ход в конце как раз и читался как «дёрнулось». */
  transition:transform .5s cubic-bezier(.32,.72,0,1),
             width .5s cubic-bezier(.32,.72,0,1),
             height .5s cubic-bezier(.32,.72,0,1),
             opacity .22s linear;
}
.nav-ind.on{opacity:1;}
/* Первая установка позиции — без анимации, иначе плашка приезжает из левого
   угла шапки при каждой загрузке страницы. */
.nav-ind.nav-ind-noanim{transition:none;}

/* Клавиатурный фокус ведёт себя как наведение: иначе человек, идущий по шапке
   табом, видел бы только системную обводку. :focus-visible, а не :focus —
   чтобы заливка не вспыхивала от каждого клика мышью. Оговорка: после клика,
   открывшего меню, и последующего Escape браузер оставляет фокус на кнопке и
   считает его «клавиатурным» — заливка там останется до ухода фокуса, и это
   правильное поведение: фокус действительно на ней. */
/* `:not(.active)` обязателен: без него наведение на УЖЕ активный пункт внутри
   выпадающей панели глушило его до яркости обычного ховера, и активный пункт
   переставал отличаться от соседей — ровно та неразличимость, ради которой
   приглушение и вводилось. Вкладок в строке это не касается: у активной фон
   рисует плашка позади, а сама кнопка прозрачная. */
.nav-btn:hover:not(.active),.nav-btn:focus-visible:not(.active),
.header-nav > .nav-btn:hover,.header-nav > .nav-btn:focus-visible,
.header-nav > .nav-btn.nav-dd-trigger:hover,
.header-nav > .nav-btn.nav-dd-trigger:focus-visible{
  /* Подмена переменных, а не отдельного градиента: правило «шкуры» читает
     --nav-g1/g2 уже на самом элементе, поэтому достаточно переопределить их
     тут (--nav-g3 намеренно не трогаем — см. комментарий к нему).
     Темы разведены через --nav-h* выше — иначе весь список селекторов пришлось
     бы дублировать ещё раз под [data-theme="light"]. */
  --nav-g1:var(--nav-h1); --nav-g2:var(--nav-h2);
  --nav-spot-w:var(--nav-spot-w-hover); --nav-spot-h:var(--nav-spot-h-hover);
  color:#fff;
  /* Селекторы продублированы с тем же префиксом, что и у «шкуры» выше, —
     иначе она перебивала бы длительность своей специфичностью. */
  animation-duration:var(--nav-flow-dur-hover);
}

/* Активная вкладка в строке. Белым текст становится ТОЛЬКО когда плашка под
   ним действительно есть: до инициализации navbar.js (и когда активная вкладка
   уехала в свёрнутую панель) плашки нет, и белое на прозрачном было бы
   невидимым. Поэтому запасной цвет — акцентный. */
.header-nav > .nav-btn.active{color:var(--accent-h);font-weight:700;background:transparent;
  border-color:transparent;box-shadow:none;}
.header-nav > .nav-ind.on ~ .nav-btn.active{color:#fff;}
/* Пока плашка едет, под новой активной вкладкой её ещё нет, а белой подпись
   уже стала — правило выше срабатывает от класса .on, а не от факта «плашка
   доехала». В светлой теме это белое на белой шапке: на дальних перегонах
   подпись пропадала совсем примерно на 0,15с при каждом переходе.
   Цвет подписи при этом НЕ меняем. Пробовал: тёмный текст отлично читается на
   белой шапке, но плашка подъезжает под него раньше, чем заканчивается
   переезд, и тёмное на голубом даёт уже 2,3:1. Никакой один цвет не годится
   сразу для белой шапки и для голубой плашки — на белом нужен тёмный, на
   плашке светлый, а плашка в этот момент наполовину под подписью.
   Поэтому подпись остаётся белой, а на время переезда под неё подкладывается
   тёмный ореол: на белой шапке он очерчивает буквы, на плашке почти не виден,
   потому что того же цвета, что её край. В покое ореола нет вовсе — он и
   раньше был отвергнут именно за то, что смягчал края букв в постоянном
   состоянии; здесь он живёт треть секунды. */
.header-nav > .nav-ind.on.nav-ind-moving ~ .nav-btn.active{
  text-shadow:0 0 2px var(--nav-g3),0 0 4px var(--nav-g3),0 0 6px var(--nav-g3);}
/* И «прохожим» вкладкам — тем, ПОД которыми плашка пролетает по дороге, — но
   ТОЛЬКО в светлой теме, и вот почему. Подпись у них приглушённого цвета, а
   под ней проезжает яркое пятно. В светлой теме яркость подписи (0,108) лежит
   ВНУТРИ диапазона пятна (0,086…0,175): где-то на пятне непременно найдётся
   точка ровно такой же яркости, и буквы там исчезают — замерено 1,02:1.
   В тёмной теме подпись (0,377) ярче всего пятна целиком, и хуже 2,12:1 не
   становится: буквы приглушаются, но остаются видны. Проверял ореол и там —
   по замеру локальной читаемости выходит чуть хуже, чем без него, и глазами
   разницы не видно, так что в тёмной теме его нет.
   Ореол цвета ФОНА ШАПКИ, а не края пятна: он возвращает буквам их обычное
   отношение «фигура на своём фоне». Пока плашка далеко, ореол невидим — он
   того же цвета, что и то, на чём буквы и так лежат.
   Кнопки-меню («Администрирование ▾», «Ещё ▾») исключены: плашка под них не
   заезжает никогда, а фон у них свой (--bg3, не --bg2), и ореол цвета шапки
   давал бы на них видимое светлое пятно вокруг подписи.
   ВКЛАДКА ПОД КУРСОРОМ И ПОД КЛАВИАТУРНЫМ ФОКУСОМ ИСКЛЮЧЕНА по той же причине,
   и это не теория: человек держит курсор на одной вкладке, а переключается
   клавиатурой или ссылкой из содержимого — и пока плашка летит мимо, на синей
   заливке наведения вокруг белой подписи вспыхивал белый ореол. Обоснование
   выше («ореол того же цвета, что и фон под буквами») для наведённой вкладки
   неверно: под её буквами не шапка, а собственное синее пятно, и там подпись
   и без ореола контрастна — это отдельно стережёт check_hierarchy. */
[data-theme="light"] .header-nav > .nav-ind.on.nav-ind-moving ~ .nav-btn:not(.active):not(.nav-dd-trigger):not(:hover):not(:focus-visible){
  text-shadow:0 0 2px var(--bg2),0 0 3px var(--bg2),0 0 5px var(--bg2),0 0 7px var(--bg2);}
/* ЧТО ОСТАЛОСЬ ОТ ОГРАНИЧЕНИЯ ПЕРЕЕЗДА.
   Плашка едет ПОД вкладками полсекунды, и подписи тех, мимо кого она проходит,
   остаются приглушённого цвета. Это давало в светлой теме 1,02:1 — буквы
   исчезали. Починено ореолом (см. блок выше): и у активной вкладки, и у
   прохожих. Замеры на действующих параметрах ореола (числа обновлены после
   того, как ореол усилили, — прежние 4,61 / 7,28 / 1,70 относились к более
   слабому варианту): подпись активной различима весь перегон через всю строку,
   размах яркости в полосе по прогонам — 5,6–5,8:1 в светлой теме и 7,1–8,0:1
   в тёмной (шаг 40мс; разброс от того, что кадры браузера идут не по
   расписанию, поэтому здесь диапазон, а не «не ниже X»: любое одно число тут
   было бы либо занижено, либо неверно. Порог проверки — 4,5:1, запас большой
   в любом прогоне), у прохожей локальная читаемость под
   плашкой 2,14 против 1,12 без ореола — это число стабильно, там момент
   берётся не по времени, а по геометрии. Мерят check_label (L1) и check_passing (P2) — если правишь
   text-shadow выше, перегенерируй числа ими, а не на глаз.
   ЧЕСТНО ПРО ОСТАТОК: ореол — подпорка, а не полное решение. Буквы прохожей
   вкладки под самым ярким местом пятна всё равно приглушены сильнее, чем в
   покое; читаются, но хуже. Полностью это снимается только сменой самого
   приёма перехода: плашка должна не ехать под вкладками, а гаснуть на старом
   месте и проявляться на новом. Это отдельная работа с отдельным
   согласованием — здесь её нет.
   Промежуточный вариант «приглушать плашку на время полёта» пробовал: он
   чинил обе беды, но ломал системную настройку «уменьшить движение» и
   добавлял рывок при остановке, и был откачен. */
/* Наведение на УЖЕ активную вкладку: фон ей рисует плашка позади, сама кнопка
   прозрачная, анимировать нечего. Второй заливки поверх первой тут и не может
   возникнуть: `background:transparent` из правила выше имеет ту же
   специфичность (0,0,3,0), что и «шкура», но стоит позже по файлу и потому
   всегда выигрывает. Правило ниже нужно не для этого, а чтобы не гонять
   анимацию по невидимому фону 8 секунд в цикле. */
.header-nav > .nav-btn.active:hover,
.header-nav > .nav-btn.active:focus-visible{animation:none;}

/* В выпадающей панели плашки нет — активному пункту нужна своя заливка. */
/* Начертание 700, как у активной вкладки в строке: правило для строки требует
   прямого потомка .header-nav и на пункты панели не распространяется, а без
   него у активного пункта не остаётся ничего, кроме яркости. */
.nav-dd-panel .nav-btn.active{color:#fff;font-weight:700;box-shadow:var(--nav-shadow);}
/* И своё пятно: пункт панели 251x32, а вкладка в строке 61–120x32. Пятно,
   подобранное под вкладку (86px), на пункте выглядит компактной кляксой,
   ползающей по тёмной полосе, — как skeleton-заглушка на загрузке, а не как
   свет за стеклом. Держим ту же пропорцию «пятно примерно вдвое шире кнопки»,
   что и в строке, и приём читается одинаково в обоих местах. */
.nav-dd-panel .nav-btn{--nav-spot-w:230px;--nav-spot-h:56px;
                       --nav-spot-w-hover:170px;--nav-spot-h-hover:42px;}

/* Пауза для невидимой плашки. Отдельным правилом, потому что в «шкуре» стоит
   шорткод `animation`, сбрасывающий animation-play-state в running. Побеждает
   тут специфичность, а не порядок: `.nav-ind:not(.on)` — это два класса против
   одного, так что правило можно двигать по файлу свободно. */
.nav-ind:not(.on){animation-play-state:paused;}

/* ── Движение и как оно выключается ───────────────────────────────────────
   Движение тут двух сортов, и раньше они гасились по-разному; теперь оба
   гасятся одной кнопкой (почему — ниже, в блоке «ЧЕМ УПРАВЛЯЕТСЯ»).
     • ПЕРЕМЕЩЕНИЕ — переезд плашки между вкладками и появление разделов.
       Объекты действительно едут по экрану.
     • ДРЕЙФ СВЕТА на месте — границы не двигаются. Здесь нет повторяющегося
       узора, который «пробегает» мимо точки: за цикл яркость в любой точке
       один раз плавно нарастает и один раз спадает. Частота = одно колебание
       за 8с = 0,125 Гц у активной вкладки и 0,2 Гц под курсором — в 24 и 15
       раз ниже порога WCAG 2.3.1 (три вспышки в секунду). Строго говоря это
       вообще не «вспышки»: порог 2.3.1 описан для резких переключений, а тут
       непрерывное изменение по четыре секунды в каждую сторону.
       При правке длительности пересчитывать как 1/длительность.
       ЧЕСТНАЯ ОГОВОРКА: настройка «уменьшить движение» существует не из-за
       мельканий, а из-за вестибулярных реакций и отвлекающего движения, и
       расчёт частоты её не отменяет.
   ЧЕМ УПРАВЛЯЕТСЯ. Единственный выключатель — кнопка в шапке: она ставит
   data-anim="off" на <html> и гасит ВСЁ разом (перелив, переезд подсветки,
   появление разделов), выбор запоминается. Системная настройка ОС
   «уменьшить движение» больше не гасит ничего сама по себе — это осознанное
   решение заказчика, принятое явно.
   Почему так. Windows поднимает этот флаг не только когда человек сознательно
   просит убрать анимацию: его же ставят экономия батареи и «настройки
   быстродействия» в корпоративных образах. На машине заказчика флаг оказался
   поднят именно так, и портал у него выглядел рвано, хотя никто движение не
   отключал. Требование WCAG 2.2.2 (дать способ остановить движение) закрывает
   кнопка в шапке — она на виду, рядом с переключателем темы.
   Цена решения названа честно: человек, поставивший настройку ОС осознанно,
   при первом заходе увидит движение и должен будет нажать кнопку. Обратный
   размен — тот, что был раньше: у всех с флагом от батареи портал выглядел
   сломанным.                                                                */
/* Селекторы повторяют ВСЕ варианты из «шкуры», включая префиксные: без них
   `[data-anim="off"] .nav-btn:hover` (0,0,3,0) проигрывает по специфичности
   правилу `.header-nav > .nav-btn.nav-dd-trigger:hover` (0,0,4,0), и на
   кнопках-меню перелив продолжал бы идти при выключенной анимации. */
/* Именно ПАУЗА, а не `animation:none`. Снятие анимации возвращает свойству
   значение из правила — пятно скачком уезжает к якорю, то есть нажатие кнопки
   «выключить движение» само по себе давало бы резкое движение. С паузой пятно
   замирает ровно там, где его застали, а повторное включение продолжает ход с
   того же места. Перерисовок у паузы нет, кадры не тикают.
   (Переезды плашки и появление разделов гасятся ниже по-старому, через
   `animation:none` и снятие transition: там замораживать нечего.) */
[data-anim="off"] .nav-ind,
[data-anim="off"] .nav-btn:hover,
[data-anim="off"] .nav-btn:focus-visible,
[data-anim="off"] .header-nav > .nav-btn:hover,
[data-anim="off"] .header-nav > .nav-btn:focus-visible,
[data-anim="off"] .header-nav > .nav-btn.nav-dd-trigger:hover,
[data-anim="off"] .header-nav > .nav-btn.nav-dd-trigger:focus-visible,
[data-anim="off"] .nav-dd-panel .nav-btn.active{animation-play-state:paused;}
[data-anim="off"] .nav-ind{transition:opacity .22s linear;}

/* Переключатель анимации в шапке — рядом с переключателем темы, тем же видом. */
.anim-btn{display:inline-flex;align-items:center;justify-content:center;
  width:38px;height:38px;flex-shrink:0;border-radius:11px;cursor:pointer;
  border:1px solid var(--border2);background:var(--bg3);color:var(--text2);
  transition:color .15s,border-color .15s,background .15s;}
.anim-btn:hover{color:var(--text);border-color:var(--accent);background:var(--bg4);}
.anim-btn[aria-pressed="false"]{opacity:.65;}

/* ── Свёрнутые вкладки: «Администрирование ▾» и «Ещё ▾» ────────────────── */
.nav-dd-trigger{display:inline-flex;align-items:center;gap:5px;flex-shrink:0;white-space:nowrap;}
.nav-dd-caret{font-size:9px;line-height:1;opacity:.7;transition:transform .15s;}
.nav-dd-trigger.open .nav-dd-caret{transform:rotate(180deg);}
/* Точка у триггера, если активная вкладка спрятана внутри него — иначе
   пользователь не видит, где он находится. */
/* Префикс `.header-nav >` тут для надёжности, а не по расчёту специфичности:
   без него у рамки-подсказки было бы (0,0,3,0) — ровно столько же, сколько у
   правила фона триггера выше, и спор решался бы порядком в файле. Сейчас
   порядок случайно на нашей стороне (подсказка стоит позже), но это хрупко:
   перестановка блоков молча погасила бы сигнал «активная вкладка спрятана
   здесь», оставив одну точку в углу. С префиксом (0,0,4,0) порядок ни на что
   не влияет. Оба триггера navbar.js кладёт прямыми потомками .header-nav,
   так что селектор их накрывает всегда. */
.header-nav > .nav-btn.nav-dd-trigger.has-active{border-color:var(--accent);color:var(--text);}
/* …но под курсором рамка и цвет текста отдаются заливке. Про цвет забыть
   нельзя: правило выше (0,0,4,0) стоит ПОЗЖЕ «шкуры» с её `color:#fff` той же
   специфичности и перекрашивало подпись в тёмный — на синей заливке в светлой
   теме это давало контраст 1,25:1, то есть нечитаемо. Здесь 0,0,5,0. */
.header-nav > .nav-btn.nav-dd-trigger.has-active:hover,
.header-nav > .nav-btn.nav-dd-trigger.has-active:focus-visible{
  border-color:transparent;color:#fff;}
.nav-dd-trigger.has-active::after{
  content:'';position:absolute;top:5px;right:5px;width:5px;height:5px;
  border-radius:50%;background:var(--accent);
}
.nav-dd-panel{
  /* Выше страницы профиля (--z-page): панель шапки открывается вниз, прямо
     в область, которую страница профиля закрывает собой. При z-index:400
     меню было технически открыто, но невидимо — уезжало под страницу. */
  position:fixed;z-index:var(--z-dropdown);min-width:210px;padding:6px;
  background:var(--bg2);border:1px solid var(--border2);border-radius:12px;
  box-shadow:0 18px 48px -12px rgba(0,0,0,.55);
  display:flex;flex-direction:column;gap:3px;overflow:auto;
}
.nav-dd-panel .nav-btn{width:100%;text-align:left;flex-shrink:0;white-space:nowrap;}

.header-right{display:flex;align-items:center;gap:9px;flex-shrink:0;}
.office-select{
  padding:8px 30px 8px 12px;border-radius:10px;border:1px solid var(--border2);
  background:var(--bg3);color:var(--text);font-size:12.5px;font-weight:600;outline:none;
  appearance:none;cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M5 6L0 0h10z' fill='%2394a6c8'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 11px center;transition:border-color .15s,box-shadow .15s;
}
.office-select:hover{border-color:var(--accent-dark);}
.office-select:focus{border-color:var(--accent);box-shadow:var(--ring);}
.theme-btn{
  width:38px;height:38px;border-radius:11px;border:1px solid var(--border2);
  background:var(--bg3);color:var(--text2);font-size:16px;line-height:1;
  display:flex;align-items:center;justify-content:center;
  transition:transform .2s cubic-bezier(.34,1.56,.64,1),border-color .15s,background .15s;
}
.theme-btn:hover{border-color:var(--accent);background:var(--accent-dim);transform:rotate(-18deg) scale(1.05);}

/* ═══════════════════════════════════════
   LAYOUT
═══════════════════════════════════════ */
.root-layout{position:fixed;top:58px;left:0;right:0;bottom:0;}
.view{display:none;width:100%;height:100%;}
/* ВНИМАНИЕ на будущее: `transform` в этой анимации делает `.view` содержащим
   блоком для потомков с `position:fixed` — модалки, тосты и выпадашки внутри
   раздела позиционировались бы от него, а не от окна. Поэтому всё плавающее в
   портале кладётся в document.body, и так и должно остаться.

   Появление раздела. Только opacity и небольшой вертикальный сдвиг — обе
   величины композитные, layout на каждый кадр не пересчитывается.
   Горизонтального сдвига и масштаба здесь быть не должно: у .root-layout нет
   overflow:hidden, и они дали бы артефакты по краям.
   Гасится ТОЛЬКО переключателем в шапке. Системной настройкой «уменьшить
   движение» — уже нет: медиазапрос убран по явному решению заказчика, причина
   расписана в секции навбара, в блоке «ЧЕМ УПРАВЛЯЕТСЯ». */
/* Уходящий раздел: лежит ПОД приходящим (z-index 0 против 1) и угасает. Именно
   под: приходящий проявляется поверх него из прозрачности, и пока он ещё
   полупрозрачен, сквозь него виден уходящий — это и даёт переход вместо
   подмены. Если положить уходящий сверху, он на четверть секунды перекроет уже
   проявившийся новый раздел. Класс вешает и снимает
   showView (app.js). display:flex перебивает базовое `.view{display:none}`
   обычной специфичностью (два класса против одного), !important тут не нужен —
   он стоял по недосмотру и молча выигрывал бы у будущих правил. Инлайновых
   display у разделов в index.html нет (проверено), перебивать нечего.
   pointer-events:none — по угасающему разделу нельзя кликать, иначе на четверть
   секунды кликабельны оказываются две страницы сразу.
   ПОЧЕМУ fixed, А НЕ absolute. Разделы лежат в двух разных местах вёрстки:
   обычные — внутри .root-layout (она fixed, top:58px), а «Корзина», «Лог»,
   «Бот», «Карты», «Техника», «Админ-панель», «СКУД» и «Заявки» — соседями в
   body, и коробку им задаёт правило `#view-….active{position:fixed;top:58px}`.
   При уходе .active снимается раньше, чем вешается .view-leaving, и с
   `position:absolute;top:0` эти восемь разделов на все 0,2с уезжали вверх под
   шапку — прямо во время угасания, то есть видимым рывком. Fixed с тем же
   отступом сверху даёт обеим группам ровно ту коробку, что была: у .root-layout
   она такая же. Мобильное значение 56px повторяет `.root-layout{top:56px}` из
   медиазапроса внизу файла — если там когда-нибудь поменяют высоту шапки, эти
   два числа надо править вместе.
   ОГОВОРКА: часть разделов при уходе разбирает своё содержимое (Home.destroy и
   подобные вызовы ниже по showView). Такой раздел угаснет уже пустым — не
   красивее прежнего мгновенного исчезновения, но и не хуже. */
.view.view-leaving{display:flex;position:fixed;top:58px;left:0;right:0;bottom:0;
  /* height:auto обязателен: базовое `.view{height:100%}` при position:fixed
     считает 100% от ОКНА, а не от коробки top/bottom, и коробка получается
     переопределённой — высота выигрывает у bottom, уходящий раздел свисает на
     58px ниже экрана. С auto высоту задаёт пара top/bottom, как и задумано. */
  height:auto;
  /* ВНИМАНИЕ: 0,07с продублировано у #mob-map-toggle внизу файла — плавающая
     кнопка схемы гаснет своим переходом ровно этой длительности, потому что
     лежит рядом с разделами и их прозрачности не наследует. Меняешь здесь —
     поменяй и там. */
  pointer-events:none;z-index:0;animation:viewOut .07s ease-out forwards;}
@media (max-width:768px){ .view.view-leaving{top:56px;} }
/* У восьми «спец-разделов» коробка в активном состоянии переопределена:
   `position:fixed;top:58px;bottom:0` плюс унаследованное `height:100%`, и
   высота выигрывает у bottom — раздел на 58px выше видимой области, его низ
   уходит за край экрана (внутри он скроллится сам). Это давнее поведение
   портала, и уходящий раздел должен выглядеть ровно так же, иначе в момент
   ухода картинка скакнёт. Поэтому им — height:100%, а не auto. */
#view-bot.view-leaving,#view-trash.view-leaving,#view-log.view-leaving,
#view-cards.view-leaving,#view-admin.view-leaving,#view-tickets.view-leaving,
#view-acs.view-leaving,#view-storage.view-leaving{height:100%;}
/* И отступ сверху им тоже свой. У .root-layout на узком экране 56px, у этих
   восьми — по-прежнему 58px: их правило `#view-….active{top:58px!important}`
   мобильного переопределения не имеет (это давнее поведение портала, не наше).
   Общий мобильный сдвиг .view-leaving на 56px делал бы уходящий раздел на 2px
   выше активного — мелочь, но ровно того же рода, что чинили в этом круге. */
@media (max-width:768px){
  #view-bot.view-leaving,#view-trash.view-leaving,#view-log.view-leaving,
  #view-cards.view-leaving,#view-admin.view-leaving,#view-tickets.view-leaving,
  #view-acs.view-leaving,#view-storage.view-leaving{top:58px;}
}
/* Первый кадр — переменная, а не константа. Обычный переход её не задаёт, и
   работает подстановка по умолчанию (1 для угасания, 0 для появления). Если же
   предыдущий переход не успел доиграть, showView кладёт в --v-from фактическую
   непрозрачность, и новая анимация продолжает с неё вместо скачка. */
@keyframes viewOut{from{opacity:var(--v-from,1);}to{opacity:0;}}
.view.active{z-index:1;}
[data-anim="off"] .view.view-leaving{animation:none;display:none!important;}

/* Появление раздела. Сдвиг намеренно маленький (12px) и без масштабирования:
   пока анимация идёт, у .view ненулевой transform, а это делает её локальной
   системой координат для любых position:fixed внутри — модалок, липких шапок.
   Чем больше сдвиг, тем заметнее они дёрнутся на эти полсекунды. Раньше было
   8px/0,38с, стало 12px/0,46с с более мягким выходом: переход читается как
   переход, а не как подмена, но остаётся в том же безопасном классе. */
/* Две анимации, а не одна: прозрачность набирается быстро, а подъём тянется
   дольше и мягко тормозит. Одной анимацией так не сделать — у неё одна кривая
   на все свойства, и либо подъём получается резким, либо раздел долго остаётся
   невидимым.
   ПОЧЕМУ ИМЕННО 0,07 / 0,01 / 0,10, А ПОДЪЁМ ПРИ ЭТОМ ДОЛГИЙ. Уходящий и
   приходящий слои обязаны пересечься: пока один гаснет, а другой проявляется,
   где-то посередине видно оба, и на паре разделов с заголовком в одном месте
   это читается как двойная экспозиция. Пиковое перекрытие уменьшить нельзя —
   оно арифметически равно самому тёмному кадру перехода (в точке пересечения
   обе прозрачности одинаковы), и «меньше двоения» означало бы ровно настолько
   же «темнее кадр». А вот ВРЕМЯ, которое оба слоя остаются читаемыми,
   сокращается укорочением обеих анимаций: чем они короче, тем круче проходят
   точку пересечения. Считано по кривым ease-out, окно «обе прозрачности не
   ниже 0,10» (на этом уровне текст ещё читается):
     0,18 / 0,05 / 0,20 — 71мс, самый тёмный кадр 0,32;
     0,12 / 0,03 / 0,16 — 49мс, 0,31;
     0,07 / 0,01 / 0,10 — 35мс, 0,35  ← здесь лучше ОБА числа сразу.
   Растворение получилось быстрым, но «переходом, а не подменой» его делает не
   оно, а подъём: он идёт свои 0,46с и после того, как прозрачность улеглась.
   Проверено перебором длительностей: короче этого окно не сделать, не уронив
   кадр ниже 0,30. Мерит check_view.
   ЗАДЕРЖКА и `both` — против двоения текста. Симметричный кросс-фейд без
   задержки (оба слоя гаснут и проявляются одновременно) держит их в середине
   перехода примерно поровну, и если у соседних разделов контент стоит в одних
   местах — а заголовок и сетка карточек стоят, — читаются оба набора слов
   сразу. С нынешней раскладкой пик перекрытия 0,35 — ровно
   столько же, сколько самый тёмный кадр перехода, и это не совпадение, а то
   самое равенство из абзаца выше. Числа тут намеренно не дублируются второй
   раз: единственный их источник — таблица выше и check_view.
   `both` обязателен: без него приходящий раздел все 0,01с задержки виден
   полностью, и вся затея теряет смысл. Задержка с тех пор укоротилась
   (0,05 -> 0,03 -> 0,01с), но довод от этого не изменился: без `both` первый
   кадр перехода показывал бы новый раздел непрозрачным. */
/* ПОЧЕМУ ОТДЕЛЬНЫЙ КЛАСС, А НЕ ПРОСТО .active. Пока анимация висела на .active,
   она была привязана к состоянию «раздел открыт», а не к событию «раздел
   открыли». Любая правка правил под этим состоянием заставляла её проиграться
   заново: достаточно было нажать в шапке «выключить анимацию» и тут же
   «включить», ничего никуда не переключая, — и вся спокойно стоявшая страница
   проваливалась в ноль и снова всплывала. То же самое ломало и уход: раздел,
   которому выключили и вернули анимацию на полпути, вспыхивал на полную
   непрозрачность и гас заново. Класс вешает showView на время самой анимации и
   снимает по её окончании; в покое анимации на разделе нет вовсе — и
   перезапускать нечего. */
.view.active{display:flex;}
.view.active.view-entering{
  animation:viewFade .10s ease-out .01s both, viewRise .46s cubic-bezier(.16,.84,.28,1);}
@keyframes viewFade{from{opacity:var(--v-from,0);}to{opacity:1;}}
/* Первый кадр подъёма — тоже переменная: раздел, которому переход прервали на
   полпути, обязан продолжить с той высоты, где его застали, а не прыгнуть на
   полные 12px. Обычный переход её не задаёт и берёт значение по умолчанию. */
@keyframes viewRise{from{transform:translateY(var(--v-from-y,12px));}to{transform:translateY(0);}}
/* Гасилка появления раздела. Раньше их было две — эта и медиазапрос системной
   настройки «уменьшить движение»; вторую убрали по решению заказчика (причина
   расписана в секции навбара, блок «ЧЕМ УПРАВЛЯЕТСЯ»), осталась одна.
   Порядок в файле для неё не важен: `[data-anim="off"] .view.active
   .view-entering` — это атрибут плюс три класса против двух классов у самого
   правила анимации, специфичность выше, и гасилка выигрывает где бы ни стояла.
   Проверено отдельной страницей, где она объявлена раньше: всё равно гасит. */
[data-anim="off"] .view.active.view-entering{animation:none;}
#view-bot.active, #view-trash.active, #view-log.active, #view-cards.active, #view-admin.active, #view-tickets.active, #view-acs.active {
  display:block !important;position:fixed !important;top:58px !important;
  left:0 !important;right:0 !important;bottom:0 !important;
  overflow-y:auto !important;overflow-x:hidden !important;z-index:10;
}
#view-storage.active {
  display:flex !important;flex-direction:column !important;position:fixed !important;
  top:58px !important;left:0 !important;right:0 !important;bottom:0 !important;
  overflow:hidden !important;z-index:10;
}

/* ═══════════════════════════════════════
   SIDEBAR
═══════════════════════════════════════ */
.sidebar{
  width:296px;min-width:296px;background:var(--bg2);
  border-right:1px solid var(--border);
  display:flex;flex-direction:column;overflow:hidden;transition:background .25s;
}
.sidebar-block{padding:16px 14px;border-bottom:1px solid var(--border);flex-shrink:0;}
.block-title{
  font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:1.4px;
  color:var(--text3);margin-bottom:11px;display:flex;align-items:center;gap:7px;
}
.block-title::before{
  content:"";width:14px;height:2px;border-radius:2px;
  background:linear-gradient(90deg,var(--accent),transparent);
}
.search-input{
  width:100%;padding:9px 12px;background:var(--bg3);border:1px solid var(--border);
  border-radius:10px;color:var(--text);font-size:12.5px;outline:none;
  transition:border-color .15s,box-shadow .15s,background .15s;
}
.search-input::placeholder{color:var(--text3);}
.search-input:hover{border-color:var(--border2);}
.search-input:focus{border-color:var(--accent);box-shadow:var(--ring);background:var(--bg2);}

/* Employee list */
.emp-list{flex:1;overflow-y:auto;overflow-x:hidden;}
.emp-list::-webkit-scrollbar{width:5px;}
.emp-list::-webkit-scrollbar-thumb{background:var(--border2);border-radius:3px;}
.emp-item{
  padding:11px 14px;border-bottom:1px solid var(--border);cursor:pointer;
  transition:background .14s,border-color .14s;border-left:2.5px solid transparent;
}
.emp-item:hover{background:var(--bg3);}
.emp-item.highlighted{
  border-left-color:var(--accent);
  background:linear-gradient(90deg,var(--accent-dim),transparent 80%);
}
.emp-item.emp-empty{padding:18px 14px;color:var(--text3);font-size:12px;cursor:default;}
.emp-name{font-weight:600;font-size:12.5px;color:var(--text);line-height:1.35;}
.emp-email{font-size:11px;color:var(--accent-h);margin-top:2px;}
.emp-meta{font-size:11px;color:var(--text3);font-family:'JetBrains Mono',monospace;margin-top:1px;}
.emp-actions{display:flex;gap:6px;margin-top:8px;}
.emp-btn{
  flex:1;padding:6px 6px;font-size:11px;font-weight:600;border-radius:8px;
  border:1px solid var(--border2);background:var(--bg4);color:var(--text2);transition:all .14s;
}
.emp-btn:hover{background:var(--accent);color:#fff;border-color:var(--accent);box-shadow:var(--glow);}

/* Avatar in sidebar */
.emp-list-avatar{width:34px;height:34px;border-radius:10px;object-fit:cover;flex-shrink:0;border:1px solid;cursor:zoom-in;
  box-shadow:0 2px 6px rgba(0,0,0,.3);}
.emp-list-initials{display:inline-flex;align-items:center;justify-content:center;
  width:34px;height:34px;border-radius:10px;font-size:11px;font-weight:800;flex-shrink:0;border:1px solid;}

/* Birthday */
.bday-item{
  background:linear-gradient(135deg,var(--bg3),var(--bg2));
  border:1px solid var(--border2);border-radius:12px;padding:10px 12px;margin-top:8px;
  box-shadow:var(--card-shadow);position:relative;overflow:hidden;
}
.bday-item::before{
  content:"";position:absolute;top:0;left:0;width:3px;height:100%;
  background:linear-gradient(180deg,var(--gold),transparent);
}
.bday-name{font-weight:700;font-size:12px;}
.bday-date{font-size:11px;color:var(--text3);margin-top:2px;}
.bday-send-btn{
  margin-top:9px;width:100%;padding:6px 0;border-radius:9px;
  border:1px solid var(--accent-h);background:transparent;color:var(--accent-h);
  font-size:11px;font-weight:700;cursor:pointer;font-family:inherit;transition:all .16s;
}
.bday-send-btn:hover{background:var(--accent-h);color:#fff;box-shadow:var(--glow);}

.trash-item{
  background:var(--bg2);border:1px solid var(--border);border-radius:14px;
  padding:15px 17px;margin-bottom:9px;display:flex;align-items:center;gap:13px;
  box-shadow:var(--card-shadow);transition:transform .14s,border-color .14s;
}
.trash-item:hover{transform:translateY(-1px);border-color:var(--border2);}
.trash-item-info{flex:1;min-width:0;}
.trash-item-name{font-weight:700;font-size:13px;color:var(--text);}
.trash-item-meta{font-size:11px;color:var(--text3);margin-top:3px;}
.trash-restore-btn{
  padding:7px 15px;border-radius:9px;border:1px solid var(--green);background:transparent;
  color:var(--green);font-size:12px;font-weight:600;cursor:pointer;font-family:inherit;
  transition:background .16s,color .16s,box-shadow .18s;white-space:nowrap;
}
.trash-restore-btn:hover{
  background:var(--green);color:#fff;
  box-shadow:0 0 18px rgba(52,211,153,.55),0 0 36px rgba(52,211,153,.28);
}
.trash-purge-btn{
  padding:7px 11px;border-radius:9px;border:1px solid var(--border2);background:transparent;
  color:var(--text3);font-size:12px;cursor:pointer;font-family:inherit;
  transition:background .16s,color .16s,border-color .16s,box-shadow .18s;margin-left:6px;
}
.trash-purge-btn:hover{
  border-color:var(--red);color:var(--red);background:rgba(240,85,107,.07);
  box-shadow:0 0 0 1px var(--red),0 0 16px rgba(240,85,107,.5);
}
[data-theme="light"] .trash-restore-btn:hover{box-shadow:0 0 18px rgba(15,157,99,.42),0 0 36px rgba(15,157,99,.22);}
[data-theme="light"] .trash-purge-btn:hover{box-shadow:0 0 0 1px var(--red),0 0 16px rgba(214,48,80,.35);}

.log-item{
  display:grid;grid-template-columns:140px 90px 80px 1fr;gap:8px;align-items:center;
  padding:11px 16px;border-bottom:1px solid var(--border);font-size:12px;transition:background .12s;
}
.log-item:hover{background:var(--bg2);}
.log-action-delete{color:var(--red);font-weight:600;}
.log-action-restore{color:var(--green);font-weight:600;}
.log-action-edit{color:var(--accent-h);font-weight:600;}
.log-action-add{color:var(--gold);font-weight:600;}
.log-action-purge{color:var(--text3);font-weight:600;}

/* Services */
.services-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px;}
.svc{
  padding:11px 9px;border-radius:11px;font-size:12px;font-weight:700;border:1px solid;
  transition:transform .15s,box-shadow .18s,background .15s,color .15s,border-color .15s;text-align:center;
  position:relative;
}
.svc:hover{transform:translateY(-2px);}
.s-blue {background:var(--accent-dim);color:var(--accent-h);border-color:var(--border2);}
.s-blue:hover{
  background:var(--accent);color:#fff;border-color:var(--accent);
  box-shadow:0 0 18px rgba(77,141,240,.6),0 0 36px rgba(77,141,240,.3);
}
.s-green{background:var(--green-dim);color:var(--green);border-color:var(--border2);}
.s-green:hover{
  background:var(--green);color:#fff;border-color:var(--green);
  box-shadow:0 0 18px rgba(52,211,153,.55),0 0 36px rgba(52,211,153,.28);
}
.s-gray {background:var(--bg3);color:var(--text2);border-color:var(--border2);}
.s-gray:hover{
  background:var(--bg4);color:var(--text);border-color:var(--text3);
  box-shadow:0 0 0 1px var(--text3),0 0 16px rgba(148,166,200,.32);
}
.s-red  {background:rgba(240,85,107,.10);color:var(--red);border-color:var(--border2);}
.s-red:hover{
  background:var(--red);color:#fff;border-color:var(--red);
  box-shadow:0 0 18px rgba(240,85,107,.55),0 0 36px rgba(240,85,107,.28);
}
[data-theme="light"] .s-blue:hover {box-shadow:0 0 18px rgba(47,111,224,.42),0 0 36px rgba(47,111,224,.22);}
[data-theme="light"] .s-green:hover{box-shadow:0 0 18px rgba(15,157,99,.42),0 0 36px rgba(15,157,99,.22);}
[data-theme="light"] .s-gray:hover {box-shadow:0 0 0 1px var(--text3),0 0 16px rgba(139,155,181,.28);}
[data-theme="light"] .s-red:hover  {box-shadow:0 0 18px rgba(214,48,80,.42),0 0 36px rgba(214,48,80,.22);}

.sidebar-footer{margin-top:auto;padding:12px 14px;border-top:1px solid var(--border);
  display:flex;align-items:center;gap:7px;flex-wrap:wrap;}
.footer-dot{width:7px;height:7px;background:var(--green);border-radius:50%;flex-shrink:0;
  box-shadow:0 0 0 3px rgba(52,211,153,.18);animation:pulse 2.4s ease-in-out infinite;}
@keyframes pulse{0%,100%{box-shadow:0 0 0 3px rgba(52,211,153,.18);}50%{box-shadow:0 0 0 6px rgba(52,211,153,0);}}
.footer-link{color:var(--accent);font-size:11px;margin-left:auto;cursor:pointer;font-weight:600;}
.footer-link:hover{text-decoration:underline;}

/* ═══════════════════════════════════════
   MAP VIEW
═══════════════════════════════════════ */
.map-area{
  flex:1;position:relative;overflow:hidden;background:var(--bg);transition:background .25s;
  background-image:
    linear-gradient(var(--grid-line) 1px,transparent 1px),
    linear-gradient(90deg,var(--grid-line) 1px,transparent 1px);
  background-size:32px 32px;
}
.map-label{
  position:absolute;top:16px;left:18px;font-size:12.5px;font-weight:700;
  color:var(--text);pointer-events:none;z-index:5;
  background:color-mix(in srgb,var(--bg2) 86%,transparent);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  padding:6px 13px;border-radius:9px;border:1px solid var(--border);box-shadow:var(--card-shadow);
}
.map-stage{width:100%;height:100%;overflow:hidden;position:relative;}
.map-svg{display:block;transform-origin:0 0;user-select:none;}
.zoom-bar{position:absolute;right:16px;top:16px;display:flex;flex-direction:column;gap:5px;z-index:10;}
.zoom-btn{
  width:36px;height:36px;background:color-mix(in srgb,var(--bg2) 88%,transparent);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  border:1px solid var(--border2);border-radius:10px;color:var(--text2);font-size:17px;
  display:flex;align-items:center;justify-content:center;
  transition:all .14s;box-shadow:var(--card-shadow);
}
.zoom-btn:hover{background:var(--accent);color:#fff;border-color:var(--accent);transform:scale(1.06);}
.zoom-btn:active{transform:scale(.94);}
.pdf-btn{
  position:absolute;bottom:16px;left:16px;padding:9px 17px;
  background:color-mix(in srgb,var(--bg2) 88%,transparent);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  border:1px solid var(--border2);border-radius:10px;color:var(--text2);
  font-size:12px;font-weight:600;transition:all .16s;z-index:10;box-shadow:var(--card-shadow);
}
.pdf-btn:hover{border-color:var(--accent);color:var(--accent-h);background:var(--bg3);}

/* ═══════════════════════════════════════
   ORG VIEW
═══════════════════════════════════════ */
.org-main{flex:1;display:flex;flex-direction:column;overflow:hidden;}
.org-toolbar{
  padding:13px 18px;border-bottom:1px solid var(--border);background:var(--bg2);
  display:flex;align-items:center;gap:9px;flex-shrink:0;flex-wrap:wrap;transition:background .25s;
}
.org-dept-name{font-family:'Sora',sans-serif;font-size:15px;font-weight:700;color:var(--text);letter-spacing:-.2px;}
.org-count{font-size:11px;color:var(--text3);font-family:'JetBrains Mono',monospace;}
.org-add-btn{
  padding:8px 14px;background:var(--accent);
  border:1px solid var(--accent);border-radius:8px;color:#fff;font-size:12px;font-weight:700;
  cursor:pointer;transition:background .14s,border-color .14s,box-shadow .18s,color .14s;display:flex;align-items:center;gap:6px;
}
.org-add-btn:hover{
  background:var(--accent-h);border-color:var(--accent-h);
  box-shadow:0 0 18px rgba(77,141,240,.65),0 0 36px rgba(77,141,240,.35);
}
[data-theme="light"] .org-add-btn:hover{
  box-shadow:0 0 18px rgba(47,111,224,.45),0 0 36px rgba(47,111,224,.25);
}

/* Export buttons — secondary look with accent glow on hover */
.org-add-btn.export-btn{
  background:var(--bg3)!important;border-color:var(--border2)!important;color:var(--text2)!important;
}
.org-add-btn.export-btn:hover{
  background:var(--bg3)!important;border-color:var(--accent)!important;color:var(--accent-h)!important;
  box-shadow:0 0 0 1px var(--accent),0 0 18px rgba(77,141,240,.65),0 0 36px rgba(77,141,240,.35);
}
[data-theme="light"] .org-add-btn.export-btn:hover{
  box-shadow:0 0 0 1px var(--accent),0 0 18px rgba(47,111,224,.45),0 0 36px rgba(47,111,224,.25);
}

.org-canvas{overflow:auto;padding:0!important;display:flex;flex-direction:column;flex:1;
  background:var(--bg);transition:background .25s;}
.org-hint{font-size:11px;color:var(--text3);line-height:1.5;margin-bottom:2px;}
.dept-list{display:flex;flex-direction:column;gap:3px;}
.dept-item{
  display:flex;align-items:center;gap:9px;padding:8px 11px;border-radius:10px;
  cursor:pointer;border:1px solid transparent;transition:background .14s,border-color .14s;
}
.dept-item:hover{background:var(--bg3);}
.dept-item.active{background:var(--accent-dim);border-color:var(--border2);}
.dept-dot{width:9px;height:9px;border-radius:50%;flex-shrink:0;box-shadow:0 0 0 3px color-mix(in srgb,currentColor 16%,transparent);}
.dept-name{font-size:11.5px;font-weight:600;flex:1;line-height:1.35;color:var(--text);}
.dept-num{font-size:10.5px;font-family:'JetBrains Mono',monospace;color:var(--text3);
  background:var(--bg3);padding:2px 7px;border-radius:8px;}

.org-mode-toggle{display:flex;gap:3px;background:var(--bg3);border:1px solid var(--border);
  border-radius:10px;padding:3px;}
.org-mode-btn{
  display:flex;align-items:center;gap:5px;padding:6px 11px;border:none;background:transparent;
  color:var(--text3);font-size:11.5px;font-weight:600;border-radius:7px;cursor:pointer;
  transition:background .14s,color .14s,box-shadow .18s;white-space:nowrap;
}
.org-mode-btn.active{
  background:var(--accent);color:#fff;
  box-shadow:0 0 14px rgba(77,141,240,.5),0 0 28px rgba(77,141,240,.25);
}
.org-mode-btn:not(.active):hover{
  background:var(--bg4);color:var(--text);
  box-shadow:0 0 0 1px var(--accent),0 0 14px rgba(77,141,240,.4);
}
[data-theme="light"] .org-mode-btn.active{
  box-shadow:0 0 14px rgba(47,111,224,.4),0 0 28px rgba(47,111,224,.2);
}
[data-theme="light"] .org-mode-btn:not(.active):hover{
  box-shadow:0 0 0 1px var(--accent),0 0 14px rgba(47,111,224,.3);
}

/* ═══════════════════════════════════════
   TABLE v3
═══════════════════════════════════════ */
.emp-table-outer{display:flex;flex-direction:column;flex:1;overflow:hidden;position:relative;}
.emp-table-wrap{overflow-x:auto;overflow-y:auto;display:block;flex:1;scrollbar-width:none;-ms-overflow-style:none;}
.emp-table-wrap::-webkit-scrollbar{height:0;width:6px;display:none;}
.emp-table-head{
  display:grid;padding:11px 16px;background:var(--bg2);
  border-bottom:1px solid var(--border2);position:sticky;top:0;z-index:10;
  box-shadow:0 4px 14px -8px rgba(0,0,0,.5);flex-shrink:0;
  width:max-content;min-width:100%;
}
.emp-table-body{flex:1;}
.emp-table-body::-webkit-scrollbar{width:6px;}
.emp-table-body::-webkit-scrollbar-thumb{background:var(--border2);border-radius:4px;}
.emp-table-row{
  display:grid;padding:10px 16px;border-bottom:1px solid var(--border);
  align-items:center;transition:background .12s;cursor:pointer;
  width:max-content;min-width:100%;
}
.emp-table-row.selected{
  background:var(--accent-dim)!important;
  border-left:3px solid var(--accent);padding-left:13px;
}
.emp-table-row.dragging{opacity:.4;}
.emp-table-row.drag-over{border-top:2px solid var(--accent);}
.h-scroll-wrap{height:14px;overflow-x:auto;overflow-y:hidden;flex-shrink:0;
  background:var(--bg2);border-top:1px solid var(--border);}
.h-scroll-wrap::-webkit-scrollbar{height:8px;}
.h-scroll-wrap::-webkit-scrollbar-thumb{background:var(--border2);border-radius:4px;}
.h-scroll-wrap::-webkit-scrollbar-thumb:hover{background:var(--accent-dark);}
.h-scroll-inner{height:1px;}
.col-resize-handle{position:absolute;right:0;top:0;bottom:0;width:6px;cursor:col-resize;
  background:transparent;transition:background .12s;border-radius:3px;z-index:10;}
.col-resize-handle:hover,.col-resize-handle:active{background:var(--accent);opacity:.7;}
.etc{display:flex;align-items:center;gap:6px;overflow:hidden;color:var(--text3);
  font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.9px;}
.etc-drag{justify-content:center;}
.etc-check{justify-content:center;}
.etc-pos{overflow:hidden;flex-wrap:wrap;height:auto;padding-right:12px;}
.etc-num{color:var(--text3);font-family:'JetBrains Mono',monospace;font-size:11px;}
.etc-name{gap:10px;}
.etc-contact{font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--text2);
  text-transform:none;letter-spacing:0;}
.etc-actions{gap:3px;justify-content:flex-end;padding-right:6px;}
.drag-handle{cursor:grab;color:var(--text3);font-size:14px;line-height:1;padding:2px;
  border-radius:5px;user-select:none;transition:color .12s;}
.drag-handle:hover{color:var(--accent-h);}
.etr-avatar{width:34px;height:34px;border-radius:10px;display:flex;align-items:center;
  justify-content:center;font-size:11px;font-weight:800;flex-shrink:0;border:1px solid;
  box-shadow:0 2px 6px rgba(0,0,0,.25);}
.etr-name{font-size:12.5px;font-weight:600;color:var(--text);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;}
.etr-sub{font-size:11px;color:var(--text3);margin-top:1px;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;}
.etr-badge{display:inline-block;padding:3px 9px;background:var(--accent-dim);
  color:var(--accent-h);border-radius:20px;font-size:11px;font-weight:600;white-space:normal;
  word-break:break-word;overflow:visible;text-overflow:unset;max-width:none;
  text-transform:none;letter-spacing:0;line-height:1.4;}
.etr-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;display:inline-block;margin-right:5px;}
.etr-mono{font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--text2);
  text-transform:none;letter-spacing:0;}
.etr-link a{color:var(--accent-h);font-size:11px;}
.etr-btn{padding:4px 6px;border-radius:7px;border:1px solid var(--border2);background:var(--bg3);
  font-size:13px;cursor:pointer;transition:all .12s;color:var(--text);display:inline-flex;
  align-items:center;justify-content:center;flex-shrink:0;}
.etr-btn:hover{background:var(--accent);border-color:var(--accent);color:#fff;}
.etr-btn-del:hover{background:rgba(240,85,107,.16)!important;border-color:var(--red)!important;color:var(--red)!important;}

/* Bulk bar */
.bulk-bar{display:none;align-items:center;gap:9px;padding:9px 16px;
  background:linear-gradient(90deg,var(--accent-dim),transparent);
  border-bottom:1px solid var(--border2);border-left:3px solid var(--accent);
  flex-shrink:0;flex-wrap:wrap;}
.bulk-count{font-size:12px;font-weight:700;color:var(--accent-h);white-space:nowrap;}
.bulk-btn{padding:6px 13px;border-radius:9px;border:1px solid var(--border2);background:var(--bg3);
  color:var(--text2);font-size:12px;font-weight:600;cursor:pointer;transition:background .12s,color .12s,border-color .12s,box-shadow .18s;white-space:nowrap;}
.bulk-btn:hover{
  background:var(--accent);color:#fff;border-color:var(--accent);
  box-shadow:0 0 16px rgba(77,141,240,.55),0 0 32px rgba(77,141,240,.3);
}
.bulk-btn-del:hover{
  background:var(--red)!important;color:#fff!important;border-color:var(--red)!important;
  box-shadow:0 0 16px rgba(240,85,107,.55),0 0 32px rgba(240,85,107,.3)!important;
}
[data-theme="light"] .bulk-btn:hover{
  box-shadow:0 0 16px rgba(47,111,224,.4),0 0 32px rgba(47,111,224,.22);
}
[data-theme="light"] .bulk-btn-del:hover{
  box-shadow:0 0 16px rgba(214,48,80,.4),0 0 32px rgba(214,48,80,.22)!important;
}

/* Плитка вложения заявки + кнопка удаления в углу.
   Кнопка появляется по наведению (на тач-устройствах — всегда видна, см. media
   ниже: там hover не срабатывает и кнопка осталась бы недостижимой). */
.tk-att-tile{position:relative;display:inline-flex;line-height:0;}
.tk-att-del{position:absolute;top:-6px;right:-6px;width:20px;height:20px;padding:0;
  display:flex;align-items:center;justify-content:center;
  border:1px solid var(--border2);border-radius:50%;background:var(--bg3);
  color:var(--text2);font-size:14px;line-height:1;cursor:pointer;
  opacity:0;transform:scale(.85);transition:opacity .12s,transform .12s,background .12s,color .12s;
  z-index:2;}
.tk-att-tile:hover .tk-att-del,
.tk-att-del:focus-visible{opacity:1;transform:scale(1);}
.tk-att-del:hover{background:var(--red);color:#fff;border-color:var(--red);}
@media (hover:none){.tk-att-del{opacity:1;transform:scale(1);}}

/* Custom checkboxes */
.row-check{appearance:none;-webkit-appearance:none;width:17px;height:17px;
  border:1.5px solid var(--border2);border-radius:5px;background:var(--bg3);cursor:pointer;
  flex-shrink:0;transition:all .14s;position:relative;display:inline-flex;
  align-items:center;justify-content:center;}
.row-check:hover{border-color:var(--accent);background:var(--accent-dim);}
.row-check:checked{background:var(--accent);border-color:var(--accent);}
.row-check:checked::after{content:'';display:block;width:4px;height:8px;border:2px solid #fff;
  border-top:none;border-left:none;transform:rotate(45deg) translateY(-1px);margin-top:1px;}
.row-check:indeterminate{background:var(--accent-dim);border-color:var(--accent);}
.row-check:indeterminate::after{content:'';display:block;width:8px;height:2px;
  background:var(--accent-h);border-radius:1px;}

/* ═══════════════════════════════════════
   EDITOR VIEW
═══════════════════════════════════════ */
.editor-main{
  flex:1;position:relative;overflow:hidden;cursor:default;background:var(--bg);
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: 22px 22px;
}
.editor-svg{display:block;transform-origin:0 0;position:absolute;top:0;left:0;}
.editor-hint{position:absolute;top:16px;left:50%;transform:translateX(-50%);
  padding:8px 18px;background:color-mix(in srgb,var(--bg2) 90%,transparent);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  border:1px solid var(--border2);border-radius:22px;font-size:12px;color:var(--text3);
  pointer-events:none;z-index:10;white-space:nowrap;box-shadow:var(--card-shadow);}
.editor-zoom-bar{position:absolute;right:16px;top:16px;display:flex;flex-direction:column;gap:5px;z-index:10;}
.tool-group{display:flex;flex-direction:column;gap:5px;}
.tool-btn{
  display:flex;align-items:center;gap:9px;padding:9px 13px;border-radius:10px;
  border:1px solid var(--border);background:var(--bg3);color:var(--text2);
  font-size:12.5px;font-weight:600;transition:all .14s;text-align:left;
}
.tool-btn:hover{background:var(--bg4);color:var(--text);border-color:var(--border2);}
.tool-btn.active{background:var(--accent-dim);color:var(--accent-h);
  border-color:var(--accent);box-shadow:inset 0 0 0 1px rgba(77,141,240,.2);}
.btn-primary{
  width:100%;padding:11px;background:var(--accent);
  border:1px solid var(--accent);border-radius:10px;color:#fff;font-size:13px;font-weight:700;
  transition:background .14s,border-color .14s,box-shadow .18s;cursor:pointer;
}
.btn-primary:hover{
  background:var(--accent-h);border-color:var(--accent-h);
  box-shadow:0 0 18px rgba(77,141,240,.6),0 0 36px rgba(77,141,240,.3);
}
.btn-secondary{
  width:100%;padding:10px;background:var(--bg3);border:1px solid var(--border2);
  border-radius:11px;color:var(--text2);font-size:12.5px;font-weight:600;
  transition:background .14s,color .14s,border-color .14s,box-shadow .18s;cursor:pointer;
}
.btn-secondary:hover{
  background:var(--bg4);color:var(--text);border-color:var(--accent);
  box-shadow:0 0 0 1px var(--accent),0 0 16px rgba(77,141,240,.45);
}
[data-theme="light"] .btn-primary:hover{box-shadow:0 0 18px rgba(47,111,224,.42),0 0 36px rgba(47,111,224,.22);}
[data-theme="light"] .btn-secondary:hover{box-shadow:0 0 0 1px var(--accent),0 0 16px rgba(47,111,224,.32);}
.prop-row{margin-bottom:10px;}
.prop-label{font-size:11px;color:var(--text3);margin-bottom:5px;font-weight:600;}
.prop-input{width:100%;padding:8px 11px;background:var(--bg3);border:1px solid var(--border);
  border-radius:9px;color:var(--text);font-size:12px;outline:none;transition:border-color .15s,box-shadow .15s;}
.prop-input:focus{border-color:var(--accent);box-shadow:var(--ring);}
/* Подсказка «когда выбирать этот приоритет» под селектором в форме создания
   заявки. Отдельным блоком, а не всплывающей подсказкой: форма открывается
   в модальном окне, а на телефоне наведения мышью нет вовсе. */
.tk-prio-hint{margin-top:7px;max-width:520px;padding:9px 12px;border-radius:9px;
  background:var(--bg3);border:1px solid var(--border);border-left:3px solid var(--accent);
  font-size:12px;color:var(--text2);line-height:1.5;white-space:pre-line;}
.prop-select{appearance:none;cursor:pointer;}
.prop-input[type="range"]{padding:0;height:6px;accent-color:var(--accent);
  background:var(--bg3);border:none;border-radius:3px;cursor:pointer;width:100%;}
.prop-input[type="color"]{padding:2px 4px;height:34px;cursor:pointer;}

/* ═══════════════════════════════════════
   MODALS
═══════════════════════════════════════ */
.modal-bg{position:fixed;inset:0;background:rgba(6,9,16,.62);
  -webkit-backdrop-filter:blur(10px) saturate(120%);backdrop-filter:blur(10px) saturate(120%);
  z-index:1000;display:none;align-items:center;justify-content:center;}
.modal-bg.open{display:flex;animation:bgFade .2s ease;}
@keyframes bgFade{from{opacity:0;}to{opacity:1;}}
.modal{
  background:var(--bg2);border:1px solid var(--border2);border-radius:18px;
  width:540px;max-width:95vw;max-height:90vh;overflow-y:auto;padding:26px;position:relative;
  box-shadow:var(--shadow);animation:modalIn .26s cubic-bezier(.16,1,.3,1);transition:background .25s;
}
.modal::-webkit-scrollbar{width:5px;}
.modal::-webkit-scrollbar-thumb{background:var(--border2);border-radius:3px;}
@keyframes modalIn{from{opacity:0;transform:scale(.96) translateY(14px);}to{opacity:1;transform:scale(1) translateY(0);}}
.modal-x{position:absolute;top:18px;right:18px;width:30px;height:30px;background:var(--bg3);
  border:1px solid var(--border2);border-radius:50%;color:var(--text2);font-size:13px;
  display:flex;align-items:center;justify-content:center;transition:all .14s;cursor:pointer;}
.modal-x:hover{background:var(--red);color:#fff;border-color:var(--red);transform:rotate(90deg);}
.modal-top{display:flex;gap:20px;align-items:flex-start;margin-bottom:24px;}
.modal-avatar{
  width:100px;height:100px;border-radius:16px;
  background:linear-gradient(135deg,var(--accent-dim),var(--bg3));
  border:1px solid var(--border2);flex-shrink:0;display:flex;align-items:center;
  justify-content:center;font-size:32px;font-weight:800;color:var(--accent-h);overflow:hidden;
  box-shadow:0 8px 22px -8px rgba(0,0,0,.5);
}
.modal-name{font-family:'Sora',sans-serif;font-size:18px;font-weight:700;line-height:1.2;
  color:var(--text);letter-spacing:-.3px;}
.modal-pos{font-size:12px;color:var(--text2);margin-top:6px;}
.modal-status{display:inline-block;margin-top:9px;padding:4px 12px;border-radius:20px;
  font-size:11px;font-weight:700;}

/* Light theme org table fixes */
[data-theme="light"] .emp-table-head { background: var(--bg2); }
[data-theme="light"] .emp-table-row:hover { background: var(--bg3); }
[data-theme="light"] .etr-name { color: var(--text); }
[data-theme="light"] .etr-sub  { color: var(--text3); }
[data-theme="light"] .etc-contact { color: var(--text2); }
[data-theme="light"] #org-svg text { fill: var(--text) !important; }
[data-theme="light"] #org-svg .org-link { stroke: var(--border2) !important; }

/* Tree theme transition */
#org-svg rect { transition: fill .15s, stroke .15s; }

/* Schedule statuses */
.status-active   {background:rgba( 52,211,153,.16);color:var(--green);}
.status-hybrid   {background:rgba( 77,141,240,.16);color:var(--accent-h);}
.status-remote   {background:rgba(251,191, 61,.16);color:var(--gold);}
.status-maternity{background:rgba(244,130,200,.16);color:#f482c8;}
.status-visiting {background:rgba(170,178,200,.16);color:#aab2c8;}

.modal-fields{display:grid;grid-template-columns:140px minmax(0,1fr);gap:11px 0;}
.mf-label{font-size:11px;color:var(--text3);align-self:center;font-weight:600;}
/* Должность и прочие поля карточки никем не ограничены по «словности»:
   одно длинное слово распирало модалку и текст обрезался по краю, поэтому
   .mf-value подключён к тому же правилу переноса, что и .u-text. */
.mf-value{font-size:12.5px;color:var(--text);align-self:center;line-height:1.45;}
.mf-link:hover{text-decoration:underline;}
.mf-divider{grid-column:1/-1;height:1px;background:var(--border);margin:5px 0;}
.mf-mono{font-family:'JetBrains Mono',monospace;font-size:12px;}
.mf-btn{display:inline-flex;align-items:center;gap:4px;padding:4px 11px;border-radius:8px;
  border:1px solid var(--border2);background:var(--bg3);color:var(--text2);font-size:11px;
  margin-left:6px;cursor:pointer;transition:all .12s;}
.mf-btn:hover{background:var(--accent);color:#fff;border-color:var(--accent);}

/* Add/edit form */
.form-title{font-family:'Sora',sans-serif;font-size:17px;font-weight:700;color:var(--text);
  margin-bottom:22px;padding-bottom:14px;border-bottom:1px solid var(--border);
  display:flex;align-items:center;gap:9px;letter-spacing:-.3px;}
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;}
.form-row{margin-bottom:0;}
.form-label{font-size:10.5px;color:var(--text3);margin-bottom:6px;font-weight:700;
  text-transform:uppercase;letter-spacing:.9px;}
.form-input{width:100%;padding:10px 13px;background:var(--bg3);border:1px solid var(--border);
  border-radius:10px;color:var(--text);font-size:13px;outline:none;
  transition:border-color .15s,box-shadow .15s,background .15s;}
.form-input:hover{border-color:var(--border2);}
.form-input:focus{border-color:var(--accent);box-shadow:var(--ring);background:var(--bg2);}

#f-photo-preview{width:80px;height:80px;border-radius:12px;object-fit:cover;
  border:1px solid var(--border2);display:none;margin-top:6px;box-shadow:var(--card-shadow);}

/* Assign modal */
.assign-list{max-height:300px;overflow-y:auto;margin-top:10px;border:1px solid var(--border);
  border-radius:12px;}
.assign-list::-webkit-scrollbar{width:5px;}
.assign-list::-webkit-scrollbar-thumb{background:var(--border2);border-radius:3px;}
.assign-item{padding:11px 13px;border-bottom:1px solid var(--border);cursor:pointer;
  transition:background .12s;display:flex;align-items:center;gap:11px;}
.assign-item:last-child{border-bottom:none;}
.assign-item:hover{background:var(--bg3);}
.assign-item.current{background:var(--accent-dim);border-left:2px solid var(--accent);}
.ai-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;}
.ai-name{font-size:12.5px;font-weight:600;color:var(--text);}
.ai-pos{font-size:11px;color:var(--text3);margin-top:1px;}

/* ═══════════════════════════════════════
   TOOLTIP
═══════════════════════════════════════ */
.tooltip{position:fixed;background:var(--bg2);border:1px solid var(--border2);
  word-break:break-word;overflow-wrap:anywhere;
  border-radius:10px;padding:9px 13px;font-size:12px;color:var(--text);pointer-events:none;
  /* Выше модалок: подсказка нужна в том числе в формах внутри модальных
     окон, а при z-index:500 она пряталась за их подложкой. */
  z-index:var(--z-tooltip);display:none;max-width:240px;box-shadow:var(--shadow);line-height:1.5;}
.tooltip.show{display:block;animation:ttIn .14s ease;}
@keyframes ttIn{from{opacity:0;transform:translateY(4px);}to{opacity:1;transform:translateY(0);}}
.tt-name{font-weight:700;color:var(--accent-h);}
.tt-sub{font-size:11px;color:var(--text3);}

kbd{display:inline-block;padding:2px 6px;background:var(--bg3);border:1px solid var(--border2);
  border-radius:6px;font-family:'JetBrains Mono',monospace;font-size:10px;color:var(--text2);
  box-shadow:0 1px 0 var(--border);}

@keyframes fadeIn{from{opacity:0;transform:translateY(8px);}to{opacity:1;transform:translateY(0);}}

@media print{.header,.sidebar,.zoom-bar,.pdf-btn,.editor-zoom-bar{display:none!important;}
  .view.active{position:absolute;inset:0;}body{background-image:none;}}

/* ── Sort buttons in sidebar ── */
.sort-btn {
  flex: 1;display: flex; align-items: center; justify-content: center; gap: 4px;
  padding: 6px 4px;border: 1px solid var(--border);background: var(--bg3);
  color: var(--text3);font-size: 10.5px; font-weight: 700;border-radius: 9px;
  cursor: pointer;transition: background .14s,color .14s,border-color .14s,box-shadow .18s;white-space: nowrap;
}
.sort-btn:hover {
  background: var(--bg4); color: var(--text); border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent),0 0 14px rgba(77,141,240,.45);
}
.sort-btn.active {
  background: var(--accent-dim); color: var(--accent-h); border-color: var(--accent);
  box-shadow: 0 0 14px rgba(77,141,240,.5),0 0 28px rgba(77,141,240,.25);
}
[data-theme="light"] .sort-btn:hover { box-shadow: 0 0 0 1px var(--accent),0 0 14px rgba(47,111,224,.32); }
[data-theme="light"] .sort-btn.active { box-shadow: 0 0 14px rgba(47,111,224,.35),0 0 28px rgba(47,111,224,.2); }
.sort-btn svg    { flex-shrink: 0; }

/* ── Viewer-mode ── */
/* Скрываем стандартные «add»-кнопки. Tickets-кнопки получают доп. класс
   .tickets-btn — он сюда не попадает, права для них проверяются в JS. */
[data-role="viewer"] .org-add-btn:not(.tickets-btn),
[data-role="viewer"] #csv-import-btn,
[data-role="viewer"] .etr-btn[title="Редактировать"],
[data-role="viewer"] .etr-btn[title="Удалить"],
[data-role="viewer"] .bulk-btn:not(.tickets-btn):not([onclick*="clearSelection"]):not([onclick*="backToList"]):not([onclick*="reload"]):not([onclick*="resetFilters"]):not([onclick*="ExtraFilters"]),
[data-role="viewer"] #modal-footer label,
[data-role="viewer"] #office-manage-btn {
  display: none !important;
}

/* Кнопка «Назад» в шапке заявки видна ВСЕМ ролям без исключения */
#tk-back-btn { display: inline-flex !important; }

/* Кнопки тикет-модуля (взять/закрыть/согласовать/внутренний коммент и т.п.)
   управляются логикой прав в JS и НЕ должны скрываться viewer-правилами.
   Высокая специфичность + !important перебивают любое [data-role] правило. */
[data-role="viewer"] button.tickets-btn,
[data-role="viewer"] .bulk-btn.tickets-btn,
button.tickets-btn {
  display: inline-flex !important;
}

/* ═══════════════════════════════════════════════════════════
   MOBILE (≤ 768px)
═══════════════════════════════════════════════════════════ */
@media (max-width: 768px) {
  .header { padding: 0 10px; height: 56px; gap: 4px;
    overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .header::-webkit-scrollbar { display: none; }
  .logo-text { display: none; }
  /* Только переменные: сами padding/border остаются на правилах выше — так
     любое состояние кнопки считается от той же базы, что и покой. */
  /* white-space:nowrap отсюда убран: он теперь стоит в базовом правиле
     .nav-btn безусловно, и дублировать его здесь незачем. */
  .nav-btn   { --nb-py: 6px; --nb-px: 11px; font-size: 12px; flex-shrink: 0; }
  .header > div:last-child { flex-shrink: 0; }
  .header .user-name { display: none; }

  .root-layout { top: 56px; }

  #view-map { flex-direction: column !important; }
  #view-map .sidebar {
    width: 100% !important; min-width: unset !important; height: auto;
    max-height: 50vh; border-right: none;
    border-bottom: 1px solid var(--border); flex-shrink: 0; overflow-y: auto;
  }
  #view-map .map-area { flex: 1; min-height: 0; }

  #view-org { flex-direction: column !important; }
  .org-sidebar {
    width: 100% !important; min-width: unset !important; height: auto !important;
    max-height: 180px; border-right: none !important;
    border-bottom: 1px solid var(--border); flex-shrink: 0; overflow-y: auto;
  }
  .org-main { flex: 1; min-height: 0; overflow: auto; }
  .dept-item { padding: 11px 13px !important; font-size: 13px !important; }

  .etr-row { font-size: 12px; }
  .etr-head { font-size: 11px; }

  .modal {
    width: 100% !important; max-width: 100% !important; max-height: 92vh !important;
    border-radius: 20px 20px 0 0 !important; padding: 18px !important; margin: 0 !important;
  }
  .modal-overlay { align-items: flex-end !important; padding: 0 !important; }
  .modal-form-row { grid-template-columns: 1fr !important; }
  .form-grid { grid-template-columns: 1fr !important; }

  /* Корзина и Лог отсюда убраны намеренно: они теперь лежат в «Администрировании»,
     строку вкладок не удлиняют, а их сокрытие правилом CSS ломало бы подсчёт
     доступных пунктов в navbar.js (он ориентируется на inline-стиль от auth.js).

     Редактор схемы на телефоне тоже бесполезен, но прячется он ТЕПЕРЬ НЕ
     ЗДЕСЬ, а в auth.js — тем же inline-стилем, что и всё остальное. Правило
     отсюда обманывало навбар ровно так, как предостерегает абзац выше:
     кнопка считалась доступной, попадала в «Ещё» и в окно настройки шапки,
     человек раскрывал «Ещё» и видел пустоту. Случай нашёлся при разборе. */

  #view-storage.active { flex-direction: column !important; }
  #storage-toolbar { padding: 8px 12px !important; }
  #storage-content { padding: 10px !important; }
}

/* Mob sidebar toggle */
#mob-map-toggle {
  position: fixed; bottom: 18px; right: 18px; z-index: 200;
  width: 52px; height: 52px; border-radius: 50%;
  background: linear-gradient(180deg,var(--accent),var(--accent-dark));
  color: #fff; border: none; cursor: pointer; font-size: 22px;
  box-shadow: var(--glow), 0 6px 20px rgba(0,0,0,.4); display: none;
  align-items: center; justify-content: center;
  /* 0,07с у прозрачности — ровно столько же, сколько угасает уходящий раздел
     (`.view.view-leaving{animation:viewOut .07s}` выше). Кнопка лежит в
     разметке РЯДОМ с разделами, а не внутри, и их прозрачности не наследует:
     без своего перехода она либо пропадала рывком в первом кадре, либо ещё
     четверть секунды висела одна поверх уже открывшегося другого раздела.
     Если менять длительность ухода раздела — менять и здесь. */
  transition: transform .18s cubic-bezier(.34,1.56,.64,1), opacity .07s ease-out;
}
#mob-map-toggle.fab-leaving { opacity: 0; pointer-events: none; }
#mob-map-toggle:active { transform: scale(.9); }
@media (max-width: 768px) {
  #mob-map-toggle { display: flex; }
}

/* ── Строка списка заявок ───────────────────────────────────────────
   Подсветку наведения держит БРАУЗЕР, а не обработчики. Прежде строка
   красилась из onmouseenter, а гасилась из onmouseleave, и подсветка
   оставалась висеть: уход указателя можно не получить вовсе — строку
   перерисовали под курсором, окно потеряло фокус, страница уехала
   прокруткой. :hover пересчитывается от того, где указатель НА САМОМ
   ДЕЛЕ, и «залипнуть» не может по устройству.

   Выбранная строка сильнее наведения: иначе отметка галочкой пропадала
   бы под мышью. */
.tk-row { transition: background .12s; }
.tk-row:hover { background: var(--bg3); }
.tk-row.is-sel, .tk-row.is-sel:hover { background: var(--accent-dim); }

/* ── Фон шапки личной страницы ──────────────────────────────────────
   Плёнка поверх картинки: без неё тёмный текст на светлой фотографии
   (и светлый на тёмной) не читается, а фон человек выбирает любой.
   Цвет плёнки берём из темы, поэтому она работает и в светлой, и в
   тёмной. */
.pf-head-cover { background-color: var(--bg2); }
.pf-head-film {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to right,
    color-mix(in srgb, var(--bg2) 88%, transparent),
    color-mix(in srgb, var(--bg2) 62%, transparent));
}
/* Содержимое шапки — поверх плёнки. */
.pf-head-cover > *:not(.pf-head-film) { position: relative; z-index: 1; }
/* Колонка действий в шапке профиля. У администратора в ней до семи кнопок,
   и развёрнутой она закрывает фон и половину шапки — поэтому свёрнута по
   умолчанию и раскрывается кнопкой «Действия». */
.pf-acts { display: flex; flex-direction: column; gap: 8px; align-items: stretch; }
.pf-acts-t {
  display: flex; align-items: center; gap: 6px; justify-content: center;
  padding: 8px 16px; border-radius: 8px; font-size: 12.5px; font-weight: 600;
  background: var(--bg3); color: var(--text2); border: 1px solid var(--border2);
  cursor: pointer;
}
.pf-acts-t:hover { background: var(--bg2); color: var(--text); }
/* Свёрнутое состояние прячем display:none, а не высотой: спрятанная
   высотой кнопка остаётся доступной с клавиатуры по Tab, и человек
   проваливается фокусом в невидимое. */
.pf-acts-body { display: none; flex-direction: column; gap: 8px; }
.pf-acts-open .pf-acts-body { display: flex; }

/* ── Окно настройки шапки ───────────────────────────────────────────
   Подложка НЕ закрывает окно по клику: человек переставляет кнопки, и
   промах мимо стрелки не должен стоить ему всей работы. */
.nav-cfg-bg{position:fixed;inset:0;z-index:11000;background:rgba(0,0,0,.45);
  display:flex;align-items:center;justify-content:center;padding:20px}
.nav-cfg{background:var(--bg2);border:1px solid var(--border);border-radius:14px;
  width:min(560px,100%);max-height:min(82vh,760px);display:flex;flex-direction:column;
  box-shadow:0 18px 50px -12px rgba(0,0,0,.5)}
.nav-cfg-h{display:flex;align-items:center;gap:10px;padding:14px 16px;
  border-bottom:1px solid var(--border);font-size:14px;color:var(--text)}
.nav-cfg-h b{flex:1}
.nav-cfg-h button{background:none;border:0;color:var(--text3);font-size:20px;
  line-height:1;cursor:pointer;padding:0 4px}
.nav-cfg-hint{padding:12px 16px;color:var(--text3);font-size:12px;line-height:1.5;
  border-bottom:1px solid var(--border)}
.nav-cfg-list{overflow-y:auto;padding:6px 8px;min-height:0}
.nav-cfg-row{display:flex;align-items:center;gap:8px;padding:7px 8px;border-radius:8px}
.nav-cfg-row:hover{background:var(--bg3)}
.nav-cfg-off{opacity:.5}
.nav-cfg-n{width:22px;text-align:right;color:var(--text3);font-size:11.5px}
.nav-cfg-t{flex:1;min-width:0;color:var(--text);font-size:13px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.nav-cfg-t i{color:var(--text3);font-size:11.5px;font-style:italic}
.nav-cfg-hid{display:flex;align-items:center;gap:5px;color:var(--text3);
  font-size:11.5px;white-space:nowrap;cursor:pointer}
.nav-cfg-row>button{background:var(--bg3);border:1px solid var(--border2);
  color:var(--text2);border-radius:7px;width:28px;height:26px;cursor:pointer;font-size:13px}
.nav-cfg-row>button:disabled{opacity:.35;cursor:default}
.nav-cfg-f{display:flex;align-items:center;gap:8px;padding:12px 16px;
  border-top:1px solid var(--border)}
.nav-cfg-sec{background:var(--bg3);border:1px solid var(--border2);color:var(--text2);
  padding:7px 14px;border-radius:8px;font-size:12.5px;font-weight:600;cursor:pointer}
.nav-cfg-ok{background:var(--accent);border:1px solid var(--accent);color:#fff;
  padding:7px 16px;border-radius:8px;font-size:12.5px;font-weight:700;cursor:pointer}
