/* MIRZABAN — Личный кабинет / Telegram Mini App.
   Ревизия 4 — «Private Digital Archive»: премиальный компактный дизайн
   (архив/сейф/личное дело как визуальная метафора вокруг реального
   VPN-кабинета), единый набор line-иконок вместо эмодзи, микроанимации.

   Важно: карточка VPN на главной показывает ТОЛЬКО то, что реально отдаёт
   /api/home (статус доступа, трафик, дни, устройства, ссылка подписки).
   В боте нет in-app тумблера подключения и нет данных о сервере/пинге —
   поэтому здесь нет фейковой кнопки "Подключить/Отключить" и выбора
   сервера: главное действие — открыть настоящую ссылку подписки. */
:root{
  /* Тёмная тема (по эскизу пользователя) — раньше здесь была светлая
     "кремовая" палитра ("Private Digital Archive"), теперь карточки и фон
     тоже тёмные, акценты (золото/зелёный/красный) стали ярче, чтобы читаться
     на тёмном фоне. Имена переменных не менялись специально — их использует
     весь остальной файл + inline-стили в app.js, поэтому смена темы — это
     смена ТОЛЬКО значений в этом блоке (+ несколько мест ниже с "зашитыми"
     rgb-триплетами старых акцентных цветов, см. комментарии по тексту). */
  --bg: #1E1A15;
  --ink: #F2EBDC;
  --ink-dim: #9C9280;
  --brown-dark: #14110C;
  --brown: #D9B876;
  --card: #2A251D;
  --card-soft: #221E17;
  --border: rgba(255,255,255,.09);
  --green: #6FA057;
  --gold: #C9A25C;
  --red: #D2695A;
  --gold-tint: rgba(201,162,92,.16);
  --green-tint: rgba(111,160,87,.16);
  --red-tint: rgba(210,105,90,.16);
  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --radius-s: 12px;
  --radius-m: 14px;
  --radius-l: 16px;
  --shadow-card: 0 1px 2px rgba(0,0,0,.25), 0 8px 20px rgba(0,0,0,.35);
  --shadow-btn: 0 4px 14px rgba(0,0,0,.4);
  --shadow-float: 0 14px 34px rgba(0,0,0,.55);

  /* ---- Design tokens (редизайн под "Premium Technology VPN app", см.
     просьбу "профессиональный UX/UI редизайн... сформируй новую дизайн-
     систему") — палитра/логотип/фирменный характер НЕ меняются (просили
     сохранить), тут только СИСТЕМАТИЗАЦИЯ того, что раньше было раскидано
     магическими числами по всему файлу: единая шкала отступов, длительности
     анимаций, z-index. Использовать эти переменные в НОВОМ коде вместо
     очередных "случайных" px — старые правила по всему файлу оставлены как
     есть (масштаб файла не позволяет безопасно переписать 1400+ строк за
     один проход), но каждая последующая правка должна брать значения отсюда. */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 20px; --sp-6: 24px; --sp-8: 32px;
  --dur-fast: 150ms; --dur-base: 220ms; --dur-slow: 320ms;
  --ease-spring: cubic-bezier(.22,.9,.32,1);
  --ease-pop: cubic-bezier(.34,1.56,.64,1);
  --z-nav: 20; --z-topbar: 25; --z-drawer: 60; --z-modal: 70; --z-toast: 80;
  /* см. живой фидбег со скриншотом "смотри какие огромные кнопки растянулись
     и так почти везде" — раньше #app/.topbar/.navbar/.drawer были без
     потолка вообще (см. историю коммента у #app ниже: "убрали потолок
     совсем"), из расчёта на Telegram-клиент, где окно физически не бывает
     шире ~500-600px. На реальном широком браузере (см. скриншот, ~1900px)
     .btn{width:100%} внутри такого контейнера растягивается на всю ширину
     экрана — те самые "огромные кнопки". Возвращаем разумный потолок и
     центрируем контент — на мобильном/в Telegram он всё равно у́же этого
     потолка и НИКАК не ограничивается (клампы ниже продолжают работать как
     раньше), а на широком окне контент теперь остаётся комфортной колонкой
     по центру, а не тянется край-в-край. */
  --content-max: 640px;
}
*{ box-sizing:border-box; -webkit-tap-highlight-color: transparent; }
html,body{ height:100%; }
body{
  margin:0; padding:58px 0 calc(84px + var(--tg-chrome-bottom, 0px) + env(safe-area-inset-bottom));
  background: var(--brown-dark);
  font-family: var(--font);
  font-size:15px; line-height:1.4;
  color: var(--ink);
  min-height:100vh;
  color-scheme: dark; /* нативные элементы (input/select, полосы прокрутки) сами подстраиваются под тёмный фон */
}
/* Блокировка прокрутки фона, пока открыта модалка/шторка меню (см. app.js
   lockBodyScroll/unlockBodyScroll) — position:fixed на body "замораживает"
   документ на месте (top уже выставлен из JS на -scrollY), overflow:hidden
   страхует от десктопного скролла колесом. Без этого фон под оверлеем
   листался тачем даже при position:fixed на самом оверлее — известная
   особенность iOS/WebView. */
body.modal-open{ position:fixed; left:0; right:0; width:100%; overflow:hidden; }
/* --tg-chrome-bottom — доп. запас снизу поверх фикс. навбара (84px) на случай,
   если сама оболочка Telegram (нижняя плашка "..."/индикатор свайпа) занимает
   больше места, чем сообщает env(safe-area-inset-bottom) — тогда даже без
   этой переменной 84px должно хватать, но она подстраховывает конкретное
   устройство (см. app.js: считается из tg.viewportStableHeight). Из-за этого
   раньше на некоторых устройствах обрезалась последняя строка на экране
   "Подписка" (текст про способы оплаты) — контент был, просто фикс. навбар
   Telegram перекрывал последние ~20-40px сверх зарезервированных 84px. */
/* Плавная ширина колонки вместо резких скачков между фикс. точками (см.
   живой фидбег "при увеличении окна кнопки/блоки не растягиваются плавно,
   не следуют за границами, как у Кошелька") — clamp() пересчитывает ширину
   на каждый кадр вместе с окном, а transition на max-width сглаживает сам
   момент программного изменения (например, когда Telegram анимированно
   разворачивает мини-апп до полного экрана). Медиа-запросы ниже по файлу
   остались только там, где меняется РЕАЛЬНО другое (шрифт, число колонок в
   сетке) — сама ширина теперь везде считается через этот clamp. */
/* Полный пересмотр (см. подробный разбор проблемы "контент в узком
   фиксированном контейнере, огромные пустые поля по бокам") — предыдущая
   попытка (clamp с потолком 960px, потом 1400px) снова упёрлась в потолок на
   действительно широких окнах (1920px и шире). Причина архитектурная: у
   #app/.topbar/.navbar был max-width вообще — раз он есть, он рано или
   поздно на каком-то экране окажется у́же окна. Правильное решение — убрать
   потолок совсем (max-width:none), а ширину отдавать под responsive
   ГОРИЗОНТАЛЬНЫЙ padding (узкий на мобильном, шире на десктопе) — контент
   растёт вместе с окном без верхней границы, а не наоборот. */
/* См. живой фидбег "убери подложку, оставь только блоки" — у #app был свой
   фон (var(--bg), на тон светлее body) под всем контентом целиком, из-за
   чего на просвет получался едва заметный прямоугольник "подложки" под
   карточками, отдельный от общего фона страницы. Убираем — фон теперь
   ЕДИНЫЙ (тот же var(--brown-dark), что у body), а различимыми "блоками"
   остаются только сами .paper-карточки со своим (более светлым) фоном —
   именно то разделение, которое и должно быть видно. */
#app{
  width:100%; max-width:var(--content-max); margin:0 auto;
  padding: 10px clamp(12px, 3vw, 32px) 4px;
  background:var(--brown-dark); min-height:calc(100vh - 142px);
  transition: transform .34s cubic-bezier(.22,.9,.32,1), opacity .3s ease;
}
/* см. openDrawer/closeDrawer в app.js — класс включается ровно тогда же,
   когда .drawer-overlay получает .open, чтобы фон "уезжал" синхронно с тем,
   как панель меню выезжает снизу (см. .drawer-overlay.open .drawer выше). */
#app.app-receded{ transform: translateY(22px) scale(.965); opacity:.45; }

::selection{ background: var(--gold-tint); }

/* ================= splash: папка → открытие → интерфейс ================= */
.splash{
  position:fixed; inset:0; z-index:999; background: var(--brown-dark);
  display:flex; align-items:center; justify-content:center; flex-direction:column; gap:14px;
  transition: opacity .35s ease, visibility .35s ease;
}
.splash.hide{ opacity:0; visibility:hidden; pointer-events:none; }
.splash-folder{ width:64px; height:64px; position:relative; }
.splash-folder svg{ width:100%; height:100%; color:#D9B876; }
.splash-folder .lid{ transform-origin:6px 9px; animation: lidOpen .6s cubic-bezier(.22,.9,.32,1) .15s both; }
@keyframes lidOpen{ 0%{ transform:rotate(0deg);} 55%{ transform:rotate(-22deg);} 100%{ transform:rotate(-8deg);} }
.splash-word{ color:#F1E4C4; font-size:12.5px; font-weight:700; letter-spacing:3px; opacity:0; animation: splashWordIn .4s ease .35s forwards; }
@keyframes splashWordIn{ from{ opacity:0; transform:translateY(4px);} to{ opacity:1; transform:translateY(0);} }

/* ================= верхняя шапка ================= */
/* См. живой фидбег: "сделай вообще прозрачную или вообще убери" — полупрозрачный
   blur всё равно оставлял лёгкую пелену поверх контента, убрали совсем. Сам
   контейнер .topbar остаётся (в нём живут кнопки меню/подарка/колокольчика
   и логика их позиционирования/safe-area), но фона у него больше нет —
   контент, проскроллившийся под шапку, виден как есть, без затемнения. */
.topbar{
  position:fixed; top:0; left:0; right:0; z-index:25;
  display:flex; align-items:center; justify-content:space-between;
  width:100%; max-width:var(--content-max); margin:0 auto;
  background: transparent;
  padding:10px clamp(12px, 3vw, 32px); padding-top: max(10px, env(safe-area-inset-top));
}
/* Группа справа (подарок+колокольчик) — обёртка нужна, чтобы оба остались
   рядом друг с другом при justify-content:space-between на 3 детях топбара
   (меню/бренд/группа), а не разлетелись равными промежутками как 4
   отдельных ребёнка — см. просьбу "иконку в виде подарка... рядом с
   колокольчиком". */
.topbar-actions{ display:flex; align-items:center; gap:8px; }
/* См. просьбу "Меню Папку возьми в рамочку так же возьми в рамочку подарок и
   колокольчик и сделай их чуть больше" — раньше кнопки были совсем без
   фона/рамки (см. историю правки выше про "убери подложку вообще") — теперь
   у всех трёх тонкая видимая рамка + чуть более крупная область (36px→42px),
   иконки внутри тоже подросли, чтобы не "тонуть" в увеличившейся рамке. */
.menu-btn, .bell-btn, .gift-btn{
  position:relative; background:rgba(255,255,255,.06); border:1.5px solid rgba(255,255,255,.16); border-radius:13px;
  width:42px; height:42px; cursor:pointer; color:#F1E4C4;
  display:flex; align-items:center; justify-content:center; flex-direction:column; gap:4px;
  transition: transform var(--dur-fast) ease, background var(--dur-fast) ease, border-color var(--dur-fast) ease;
}
.menu-btn:active, .bell-btn:active, .gift-btn:active{ transform:scale(.88); background:rgba(255,255,255,.14); border-color:rgba(255,255,255,.28); }
/* Папка вместо гамбургера (см. живой фидбег "Меню в место чёрточек сделай
   Папку") — та же двухцветная иконка, что и в заставке при запуске
   (.splash-folder), для фирменной узнаваемости. */
.icon-folder{ width:29px; height:29px; }
/* См. живой фидбег "Букву М такую же сделать в место папки меню" — папку в
   кнопке меню сменили на фирменную золотую букву «M», тот же приём
   (градиент-заливка текста + переливающийся shine), что и .brand-badge span
   на странице входа (login.html) — единый узнаваемый символ MIRZABAN. */
.menu-btn-letter{
  font-weight:800; font-size:21px; line-height:1; font-family:inherit;
  background:linear-gradient(100deg, #8a6a30 0%, var(--gold) 25%, #fff3d6 50%, var(--gold) 75%, #8a6a30 100%);
  background-size:220% 100%; -webkit-background-clip:text; background-clip:text; color:transparent;
  animation: menuLetterShine 3.2s linear infinite;
}
@keyframes menuLetterShine{ 0%{ background-position:0% 0; } 100%{ background-position:220% 0; } }
/* Настоящие цветные SVG вместо эмодзи (см. живой фидбег "сделай нормальный
   колокольчик и нормальную коробочку, они должны быть цветными и
   анимированными" — эмодзи-глиф зависит от шрифта эмодзи конкретного
   устройства/WebView и у пользователя не отрисовался как ожидалось; свой
   SVG рисуется одинаково всегда, и цвет, и анимация полностью под нашим
   контролем). Обеим иконкам добавлено вечное лёгкое покачивание — "живые",
   а не статичные — а более заметная анимация (bellRing/giftBounce) на
   реальное событие просто на время ЗАМЕНЯЕТ его более резким движением. */
.icon-bell{ width:27px; height:27px; transform-origin:50% 6%; animation: bellIdleSwing 4.4s ease-in-out infinite; }
@keyframes bellIdleSwing{ 0%,100%{ transform:rotate(0deg);} 50%{ transform:rotate(5deg);} }
.icon-gift{ width:27px; height:27px; transform-origin:50% 85%; animation: giftIdleBounce 3.6s ease-in-out infinite; }
@keyframes giftIdleBounce{ 0%,100%{ transform:translateY(0) rotate(0deg);} 50%{ transform:translateY(-1.5px) rotate(-3deg);} }
/* Пока есть непрочитанные ответы персонала — колокольчик "дышит" заметнее
   постоянно, а в момент, когда приходит НОВЫЙ ответ (см. app.js.setBellUnread),
   на него на секунду накладывается более резкое покачивание "ring" (по просьбе
   "колокольчик можно сделать анимированным ... онлайн оповещение") — теперь
   ПОКА есть непрочитанное, сама кнопка (не только иконка) постоянно светится
   золотой пульсирующей обводкой, а бейдж-число — красной "рябью", чтобы
   колокольчик было видно боковым зрением, а не только при внимательном
   взгляде на топбар. */
.bell-btn.has-unread{ background:rgba(201,162,92,.16); animation: bellGlow 2.2s ease-in-out infinite; }
@keyframes bellGlow{
  0%,100%{ box-shadow:0 0 0 0 rgba(201,162,92,.45); }
  50%{ box-shadow:0 0 0 7px rgba(201,162,92,0); }
}
.bell-btn.has-unread .icon-bell{ animation: bellBreathe 2.2s ease-in-out infinite; }
@keyframes bellBreathe{ 0%,100%{ transform:rotate(0deg);} 50%{ transform:rotate(9deg);} }
.bell-btn.ring .icon-bell{ animation: bellRing .6s cubic-bezier(.36,.07,.19,.97) 2; }
@keyframes bellRing{
  0%,100%{ transform:rotate(0deg); }
  15%{ transform:rotate(18deg); } 30%{ transform:rotate(-16deg); }
  45%{ transform:rotate(12deg); } 60%{ transform:rotate(-9deg); }
  75%{ transform:rotate(5deg); } 90%{ transform:rotate(-2deg); }
}
.bell-badge{
  position:absolute; top:-3px; right:-3px; min-width:16px; height:16px; padding:0 4px;
  border-radius:10px; background:var(--red); color:#fff; font-size:9px; font-weight:800;
  display:flex; align-items:center; justify-content:center; border:2px solid var(--brown-dark);
}
.bell-badge.hidden{ display:none; }
.bell-badge:not(.hidden){ animation: badgePop .4s cubic-bezier(.34,1.56,.64,1), badgeRipple 1.8s ease-out .45s infinite; }
@keyframes badgePop{ 0%{ transform:scale(0);} 70%{ transform:scale(1.2);} 100%{ transform:scale(1);} }
@keyframes badgeRipple{
  0%{ box-shadow:0 0 0 0 rgba(210,105,90,.6); }
  70%{ box-shadow:0 0 0 6px rgba(210,105,90,0); }
  100%{ box-shadow:0 0 0 0 rgba(210,105,90,0); }
}
/* Иконка-подарок (Рефералы) — см. requirements п.9 "анимация подарка ТОЛЬКО
   когда реально пришёл новый реферал, одноразовый bounce/sparkle, НЕ
   бесконечная/постоянная" — в отличие от колокольчика (который "дышит" всё
   время, пока есть непрочитанное) подарок ведёт себя по-другому: в момент
   обнаружения роста реального totals.invited (см. noteReferralCount в
   app.js, сравнение с последним просмотренным значением в localStorage) —
   один раз проигрывается bounce, а маленькая золотая точка остаётся
   статичной (без анимации) до тех пор, пока пользователь не откроет раздел
   "Рефералы" — так видно, что есть что-то новое, но это не мигает вечно. */
.gift-btn .gift-dot{
  position:absolute; top:1px; right:2px; width:9px; height:9px; border-radius:50%;
  background:var(--gold); border:2px solid var(--brown-dark);
  opacity:0; transform:scale(0); transition: transform var(--dur-base) var(--ease-pop), opacity var(--dur-base) ease;
}
.gift-btn.has-new .gift-dot{ opacity:1; transform:scale(1); }
.gift-btn.gift-bounce .icon-gift{ animation: giftBounce .7s cubic-bezier(.34,1.56,.64,1) 1; }
.gift-btn.gift-bounce .gift-dot{ animation: badgePop .4s cubic-bezier(.34,1.56,.64,1); }
@keyframes giftBounce{
  0%,100%{ transform:scale(1) rotate(0deg); }
  30%{ transform:scale(1.22) rotate(-10deg); }
  55%{ transform:scale(.92) rotate(7deg); }
  80%{ transform:scale(1.08) rotate(-3deg); }
}

/* ================= меню ================= */
/* Второй виток (живой фидбег по скриншотам Wallet): "Меню открывается как
   обычная вкладка... возврат стрелочкой назад как в Кошельке" — раньше это
   была узкая (78% ширины) боковая шторка-оверлей с затемнённым фоном позади.
   Теперь это полноэкранный экран, "наезжающий" слева-направо ПОВЕРХ всего
   интерфейса (тот же приём, что у Telegram при переходе в настройки), а не
   узкая панель рядом с видимым куском старого экрана. Список пунктов/иконок
   ниже НЕ трогали — только контейнер и способ его закрытия (раньше — "Х" в
   углу, теперь — стрелка "←" слева в заголовке, привычная навигация
   Telegram). */
.drawer-overlay{
  position:fixed; inset:0; z-index:var(--z-drawer);
  opacity:0; pointer-events:none; transition: opacity var(--dur-fast) ease;
}
.drawer-overlay.open{ opacity:1; pointer-events:auto; }
.drawer-overlay.hidden{ display:none; }
/* см. просьбу "когда открываешь меню меню снизу... плавно убиралась в низ, а
   при закрытии возвращалась" — раньше .drawer открывался ПРОСТЫМ fade
   (см. историю комментария выше), без своего движения. Теперь сама панель
   выезжает СНИЗУ ВВЕРХ (translateY 100% → 0), а фон-контент под ней (#app —
   см. .app-receded в style.css/openDrawer в app.js) синхронно "уезжает" вниз
   и слегка уменьшается/гаснет — тот же приём, что у системных модальных
   шторок iOS/Telegram, где фоновый экран визуально "отступает" под
   всплывающей панелью, а не просто мгновенно скрывается. */
.drawer{ transform: translateY(100%); transition: transform .34s cubic-bezier(.22,.9,.32,1); }
.drawer-overlay.open .drawer{ transform: translateY(0); }
/* См. живой фидбег "меню не должно появляться слева/справа, просто должно
   открыться окно" — раньше .drawer ещё и сам "выезжал" через translateX
   ПОВЕРХ затухания .drawer-overlay (двойная анимация). Теперь у самого
   .drawer нет своего transform/transition — открытие/закрытие целиком
   держится на fade самого .drawer-overlay выше, никакого сдвига по X. */
.drawer{
  position:absolute; top:0; left:0; right:0; bottom:0; width:100%; max-width:none;
  background: var(--brown-dark);
  padding:0 0 env(safe-area-inset-bottom);
  overflow-y:auto;
}
/* Нижний плавающий навбар лежит ПОВЕРХ #app как отдельный fixed-элемент —
   пока открыто полноэкранное Меню, он должен быть скрыт (см. openDrawer/
   closeDrawer в app.js), а не просвечивать через/под шторкой. */
.navbar.hidden-by-drawer{ opacity:0; pointer-events:none; transition: opacity var(--dur-fast) ease; }
/* Заголовок экрана меню — та же логика, что у нативной шапки Telegram
   (стрелка назад слева + заголовок), чтобы переход в "Меню" и обратно
   ощущался как обычная навигация, а не как выпадающая панель. */
.drawer-header{
  position:sticky; top:0; z-index:1; background:var(--brown-dark);
  display:flex; align-items:center; gap:10px;
  max-width:var(--content-max); margin-left:auto; margin-right:auto;
  padding:12px clamp(12px, 3vw, 32px); padding-top:max(12px, env(safe-area-inset-top));
}
/* См. живой фидбег "убери слово Меню и стрелочку" — внутри настоящего
   Telegram эта строка скрывается через JS (см. HAS_NATIVE_BACK в app.js),
   потому что там уже есть штатная нативная кнопка "назад" в шапке самого
   Telegram. Остаётся видимой только в веб-версии кабинета вне Telegram
   (там своего "назад" нет вообще). */
.drawer-header.hidden{ display:none; }
.drawer-header-title{ font-size:15.5px; font-weight:800; }
.drawer-close{
  background:rgba(255,255,255,.09); border:1px solid rgba(255,255,255,.08); border-radius:10px; width:34px; height:34px;
  cursor:pointer; color:var(--ink); display:flex; align-items:center; justify-content:center; flex-shrink:0;
  transition: transform var(--dur-fast) var(--ease-pop), background var(--dur-fast) ease;
}
.drawer-close:active{ transform:scale(.88); background:rgba(255,255,255,.14); }
.drawer-close svg{ width:18px; height:18px; }
/* См. живой фидбег "разделы возьми в блок" — раньше пункты меню лежали
   прямо на фоне .drawer (плоским списком), теперь каждая логическая группа
   (профиль / основное меню / поддержка / правовая информация) — отдельная
   карточка со своим фоном var(--card), тем же приёмом, что и .paper на
   обычных экранах. Горизонтальный отступ карточки от края — тот же
   responsive clamp, что и у #app/.topbar, чтобы всё было на одной сетке. */
.drawer-group{
  background: var(--card); border-radius: var(--radius-l);
  /* См. живой фидбег со скриншотом "нет расстояния слева и справа как везде
     в приложении" — раньше боковой инсет держался на margin-left/right:auto
     ПОВЕРХ margin: 0 clamp(...) в шорткарте выше, а width оставался auto.
     При такой комбинации (width:auto + оба margin:auto) CSS обнуляет
     авто-маржины и отдаёт всю ширину блоку — отступы пропадали на любом
     экране, где max-width ещё не "упирался" (то есть почти всегда на
     мобильном). Явная width ниже убирает эту неоднозначность: авто-маржины
     теперь честно делят ОСТАВШЕЕСЯ место (=clamp с каждой стороны на
     мобильном, а на широких экранах — центрирование в рамках max-width),
     та же responsive-сетка, что у #app/.topbar/.drawer-header. */
  width: calc(100% - 2 * clamp(12px, 3vw, 32px));
  max-width:calc(var(--content-max) - 2 * clamp(12px, 3vw, 32px));
  margin: 0 auto var(--sp-3);
  padding: var(--sp-2) 0;
  box-shadow: 0 1px 2px rgba(0,0,0,.22);
}
.drawer-group:first-of-type{ margin-top: var(--sp-3); }
.drawer-profile{ padding:10px 14px; display:flex; align-items:center; gap:12px; }
.drawer-avatar{
  width:46px; height:46px; border-radius:13px; flex-shrink:0; display:flex; align-items:center; justify-content:center;
  background: linear-gradient(135deg, #6C7BF7, #4C5FD6); color:#fff; font-size:18px; font-weight:700;
}
.drawer-name{ font-size:14.5px; font-weight:700; }
.drawer-id{ font-size:11px; color:var(--ink-dim); margin:2px 0 4px; }
/* Email в профиле-«визитке» меню — см. живой фидбег "добавь в профиль
   пользовательскую почту если она у него есть". Реальные данные из
   /api/profile/email (см. updateDrawerProfile в app.js) — если почта не
   привязана, элемент остаётся с классом hidden и просто не рендерится. */
.drawer-email{ font-size:11.5px; color:var(--ink-dim); margin-bottom:6px; display:flex; align-items:center; gap:5px; }
.drawer-email.hidden{ display:none; }
.drawer-email svg{ width:12px; height:12px; flex-shrink:0; }
.drawer-nav{ display:flex; flex-direction:column; gap:2px; padding:0 clamp(6px, 2vw, 20px); }
/* Крупнее (см. живой фидбег "разделы сделай крупнее") — было font-size:14.5px/
   иконка 19px/padding 13px 12px/min-height 48px, стало заметно щедрее. */
.drawer-nav-item{
  display:flex; align-items:center; gap:13px; width:100%; text-align:left; background:none; border:none;
  font-family:var(--font); font-size:15.5px; font-weight:600; color:var(--ink); padding:15px 12px; min-height:54px;
  border-radius:var(--radius-s); cursor:pointer; transition: background var(--dur-fast) ease, transform var(--dur-fast) var(--ease-pop);
}
.drawer-nav-item svg{ width:21px; height:21px; color:var(--ink-dim); flex-shrink:0; }
.drawer-nav-item:active{ background:rgba(255,255,255,.07); transform:scale(.98); }
.drawer-nav-item.active{ background: var(--gold-tint); color: var(--brown); }
.drawer-nav-item.active svg{ color: var(--gold); }
/* Правовая информация — визуально те же .drawer-nav-item, что и остальные
   пункты меню (см. живой фидбек "текст по размеры не такой иконки нету" —
   раньше тут были мелкий текст без иконки слева и один только шеврон;
   теперь размер текста/иконка слева единые со всем меню, шеврон справа —
   единственное отличие, как у ссылок в подвале Wallet). */
.drawer-legal{ padding: var(--sp-1) clamp(6px, 2vw, 20px); display:flex; flex-direction:column; }
.drawer-legal .drawer-nav-item{ justify-content:space-between; }
.drawer-legal-label{ flex:1; min-width:0; }
.drawer-legal-chev{ width:16px; height:16px; color:var(--ink-dim); flex-shrink:0; }
/* user-only/staff-only — переключаются в updateDrawerProfile() по роли из
   /api/home (та же роль, что проверяет сервер в get_current_staff): у
   персонала свой личный раздел "Поддержка" скрыт целиком (см. просьбу "убери
   у Администратора/Техподдержки/Бухгалтера раздел Поддержка"), вместо него —
   "Тикеты", и наоборот для обычного пользователя. Одно и то же правило и для
   пункта шторки, и для нижнего меню (см. .navbar .nav-item.hidden ниже). */
.drawer-nav-item.hidden{ display:none; }
.drawer-nav-item .dn-badge{
  margin-left:auto; min-width:18px; height:18px; padding:0 5px; border-radius:9px;
  background:var(--red); color:#fff; font-size:10px; font-weight:800;
  display:flex; align-items:center; justify-content:center; flex-shrink:0;
}
.drawer-nav-item .dn-badge.hidden{ display:none; }

/* ================= экраны ================= */
.screen{ display:none; }
.screen.active{ display:block; animation: fadeUp .28s cubic-bezier(.22,.9,.32,1); }
@keyframes fadeUp{ from{ opacity:0; transform:translateY(8px);} to{ opacity:1; transform:translateY(0);} }

.stagger{ opacity:0; animation: staggerIn .34s cubic-bezier(.22,.9,.32,1) forwards; animation-delay: calc(var(--i, 0) * 50ms); }
@keyframes staggerIn{ from{ opacity:0; transform:translateY(8px);} to{ opacity:1; transform:translateY(0);} }

/* Раньше — чёткая рамка+тень+более светлый фон (var(--card)) на каждой
   секции, из-за чего экран выглядел "стопкой отдельных коробок" (см.
   просьбу "у нас все в блоках", сравнение с Telegram Wallet — там секции
   просто на общем фоне с разделителями, без рамок). Теперь фон совпадает с
   фоном страницы (полностью сливается — рамка/тень убраны), секции отделяет
   отступ margin-bottom + тонкий верхний разделитель у .screen-header
   (см. ниже) — то же ощущение "плоского списка секций", что у Wallet, а не
   набор отдельных карточек. Скругление радиуса оставлено (Wallet тоже
   скругляет отдельные интерактивные строки/плитки внутри секций), но само
   по себе на невидимой рамке не даёт "боксового" эффекта. */
/* ИСПРАВЛЕНИЕ по живому фидбеку (скриншот реального приложения): полностью
   прозрачный .paper оказался перебором — у Wallet карточки тоже без рамки/
   тени, но у НИХ фон карточки заметно светлее фона страницы (см. скриншот
   Wallet: строки "Доллары"/"Фьючерсы"/крипто лежат на явно более светлой
   подложке, чем чёрный фон экрана). У нас же и #app, и .paper были почти
   одного оттенка — от этого "всё сливается, плохо видно", что и было
   отмечено. Возвращаем видимый фон карточки (var(--card) — он и так на
   ступеньку светлее var(--bg), см. :root), но БЕЗ рамки и БЕЗ тяжёлой тени —
   контраст только через цвет, как просили изначально. */
.paper{
  background: var(--card); border-radius: var(--radius-l);
  padding: var(--sp-4) var(--sp-4) var(--sp-5); position:relative; overflow:hidden;
  margin-bottom: var(--sp-3); border:none;
  box-shadow: 0 1px 2px rgba(0,0,0,.22);
}

.screen-header{ display:flex; justify-content:space-between; align-items:flex-start; margin-bottom:14px; gap:10px; }
.screen-header .sh-text b{ display:block; font-family:var(--mono); font-size:10px; text-transform:uppercase; letter-spacing:1.2px; color:var(--ink-dim); font-weight:600; margin-bottom:4px; }
.screen-header .sh-text .sh-title{ font-size:18px; font-weight:800; line-height:1.25; letter-spacing:-.2px; }
.screen-header .sh-text .sh-sub{ font-size:11.5px; color:var(--ink-dim); margin-top:3px; }
.screen-close-btn{
  width:32px; height:32px; border-radius:10px; border:none; background:rgba(255,255,255,.07);
  color:var(--ink-dim); cursor:pointer; display:flex; align-items:center; justify-content:center; flex-shrink:0;
  transition: transform .15s ease, background .15s ease;
}
.screen-close-btn:active{ transform:scale(.88); background:rgba(255,255,255,.14); }
.screen-close-btn svg{ width:15px; height:15px; }

.status-pill{
  display:inline-flex; align-items:center; gap:5px; font-size:10px; font-weight:700; letter-spacing:.3px;
  text-transform:uppercase; padding:5px 10px; border-radius:20px; white-space:nowrap; flex-shrink:0;
  background: rgba(156,146,128,.16); color:var(--ink-dim);
}
.status-pill::before{ content:""; width:5px; height:5px; border-radius:50%; background:currentColor; flex-shrink:0; }
.status-pill.green{ background:var(--green-tint); color:var(--green); }
.status-pill.green::before{ animation: dotPulse 1.8s ease-in-out infinite; }
.status-pill.red{ background:var(--red-tint); color:var(--red); }
.status-pill.amber{ background:var(--gold-tint); color:var(--gold); }
@keyframes dotPulse{ 0%,100%{ box-shadow:0 0 0 0 rgba(111,160,87,.45);} 50%{ box-shadow:0 0 0 4px rgba(111,160,87,0);} }

/* ================= точка статуса пользователя (раздел "Пользователи" —
   см. requirements UX-аудита "онлайн — зелёная точка, отключён — красная,
   активен-но-офлайн — нейтральная, не путать эти состояния"). Список и
   карточка используют один и тот же status_key с сервера (см.
   webapp/api/routers/staff_admin.py._status_key) — тут только цвет. ========= */
.status-dot{ display:inline-block; width:8px; height:8px; border-radius:50%; flex-shrink:0; background:var(--ink-dim); }
.status-dot.green{ background:var(--green); animation: dotPulse2 1.8s ease-in-out infinite; }
.status-dot.red{ background:var(--red); }
/* Офлайн (см. просьбу "офлайн красная мерцающая кнопка") — мигает так же,
   как зелёная "онлайн" (dotPulse2), просто красным свечением. Отдельный
   класс от .status-dot.red выше НАМЕРЕННО: тот используется и для "отключён/
   забанен" — это постоянная блокировка, а не живой статус, ей мигать не за чем. */
.status-dot.red-pulse{ background:var(--red); animation: dotPulseRed 1.8s ease-in-out infinite; }
.status-dot.amber{ background:var(--gold); }
.status-dot.grey{ background:var(--ink-dim); }
.status-inline-dot{ display:inline-flex; align-items:center; gap:6px; font-size:11px; font-weight:700; color:var(--ink-dim); white-space:nowrap; }
@keyframes dotPulseRed{ 0%,100%{ box-shadow:0 0 0 0 rgba(210,105,90,.5);} 50%{ box-shadow:0 0 0 4px rgba(210,105,90,0);} }

/* ================= фильтры-чипы в горизонтальной прокрутке (раздел
   "Пользователи" — см. requirements UX-аудита "Все/Онлайн/Офлайн/Отключённые/
   Активные/Без тарифа/Истекает скоро/Истёк") — отдельно от .chips (та —
   фиксированная grid-сетка для 2-4 вариантов, тут набор длиннее и должен
   помещаться на маленьком экране без переноса, поэтому scroll, а не wrap. ==== */
.filter-chip-row{ display:flex; gap:7px; overflow-x:auto; overflow-y:hidden; margin:0 -1px 12px; padding:1px 1px 4px; -webkit-overflow-scrolling:touch; scrollbar-width:none; }
.filter-chip-row::-webkit-scrollbar{ display:none; }
.filter-chip{
  flex-shrink:0; border:1.5px solid var(--border); border-radius:20px; padding:9px 14px; min-height:36px;
  font-family:var(--font); font-size:12.5px; font-weight:700; background:var(--card-soft); color:var(--ink-dim);
  cursor:pointer; white-space:nowrap; transition: transform .15s cubic-bezier(.34,1.4,.64,1), background .12s ease, color .12s ease;
}
.filter-chip:active{ transform:scale(.95); }
.filter-chip.active{ background:var(--brown-dark); color:#F5EAD0; border-color:var(--brown-dark); }
/* "Онлайн"/"Офлайн" — выбранное состояние "горит" соответствующим цветом
   (тем же, что и .status-dot.green/.red-pulse), а не обычным тёмным — чтобы
   кнопка фильтра визуально совпадала с точкой статуса в списке (см. просьбу
   "онлайн зелёная мерцающая кнопка, офлайн красная мерцающая кнопка"). */
.filter-chip-green.active{ background:var(--green); color:#0E1A0A; border-color:var(--green); animation: chipPulseGreen 1.8s ease-in-out infinite; }
.filter-chip-red.active{ background:var(--red); color:#fff; border-color:var(--red); animation: chipPulseRed 1.8s ease-in-out infinite; }
@keyframes chipPulseGreen{ 0%,100%{ box-shadow:0 0 0 0 rgba(111,160,87,.5);} 50%{ box-shadow:0 0 0 5px rgba(111,160,87,0);} }
@keyframes chipPulseRed{ 0%,100%{ box-shadow:0 0 0 0 rgba(210,105,90,.5);} 50%{ box-shadow:0 0 0 5px rgba(210,105,90,0);} }

/* ================= Главная: карточка "Личное дело" (строки-записи) ========= */
.status-pulse{ animation: dotPulse2 1.8s ease-in-out infinite; }
@keyframes dotPulse2{ 0%,100%{ box-shadow:0 0 0 0 rgba(111,160,87,.45);} 50%{ box-shadow:0 0 0 4px rgba(111,160,87,0);} }

.record-label{ font-family:var(--mono); font-size:10px; text-transform:uppercase; letter-spacing:1.2px; color:var(--ink-dim); font-weight:600; margin-bottom:10px; }
.identity-row{ display:flex; align-items:flex-start; gap:11px; margin-bottom:14px; }
.identity-avatar{
  width:44px; height:44px; border-radius:13px; flex-shrink:0; display:flex; align-items:center; justify-content:center;
  background: linear-gradient(135deg, #6C7BF7, #4C5FD6); color:#fff; font-size:17px; font-weight:700;
}
.identity-text{ flex:1; min-width:0; }
.identity-name{ font-size:15.5px; font-weight:800; }
.identity-sub{ font-size:11.5px; color:var(--ink-dim); margin-top:2px; line-height:1.5; }
.identity-right{ flex-shrink:0; text-align:right; }
.identity-days{ font-size:10px; color:var(--ink-dim); margin-top:6px; font-weight:600; }
.status-pill.gold{ background: var(--gold-tint); color:var(--gold); }
.status-pill.gold::before{ display:none; }
.status-pill.gold svg{ width:10px; height:10px; }

.record-divider{ border-top:1px solid var(--border); margin:0; }
.record-row{ display:flex; align-items:center; justify-content:space-between; gap:10px; padding:13px 0; cursor:default; }
.record-row.clickable{ cursor:pointer; transition: opacity .12s ease; }
.record-row.clickable:active{ opacity:.6; }
.record-row .rr-label{ font-size:10.5px; color:var(--ink-dim); text-transform:uppercase; letter-spacing:.4px; font-weight:600; margin-bottom:4px; }
.record-row .rr-value-lg{ font-size:15.5px; font-weight:800; }
.record-row .rr-sub{ font-size:11px; color:var(--ink-dim); margin-top:1px; }
.record-row .rr-value{ font-size:14px; font-weight:800; font-variant-numeric:tabular-nums; }
.record-row .rr-link{ display:flex; align-items:center; gap:6px; font-size:12.5px; color:var(--brown); font-weight:600; word-break:break-all; }
.record-row .rr-link svg{ width:13px; height:13px; flex-shrink:0; color:var(--ink-dim); }
.record-row svg.chev{ width:16px; height:16px; color:var(--ink-dim); flex-shrink:0; }
/* Базовый размер для ЛЮБОЙ иконки без более специфичного правила — без этого
   голый <svg class="ic"> (например, chevron в .pay-method на экране "Баланс")
   рендерится вintrinsic-размере браузера (сотни px). Специфичность класса
   ниже, чем у "X svg"-правил выше/ниже по файлу, так что нигде их не перебьёт. */
svg.ic{ width:16px; height:16px; flex-shrink:0; }
/* Та же защита, что и у svg.ic выше, но для растровых иконок способов оплаты
   (PAYMENT_METHOD_ICON в app.js, файлы bot/assets/payment_method_*.webp) —
   без базового ограничения голый <img class="payment-method-icon"> без
   более специфичной обёртки (.icon-badge img/.transfer-qr-big img — у них
   своя специфичность и она перебивает это правило) рендерится в родном
   разрешении файла (320×320) и раздувает layout, как было с Stars в шторке
   пополнения баланса до этой правки. */
.payment-method-icon{ width:22px; height:22px; object-fit:contain; display:block; }
.record-stats-2col{ display:grid; grid-template-columns:1fr 1fr; gap:8px; padding-bottom:13px; text-align:left; }
.record-stats-2col b{ display:block; font-size:14.5px; font-weight:800; font-variant-numeric:tabular-nums; }
.record-stats-2col span{ font-size:10px; color:var(--ink-dim); text-transform:uppercase; letter-spacing:.3px; font-weight:600; }

/* ================= Главная: премиальный блок "Тариф" ================= */
/* См. просьбу "Тариф блок крупнее, осталось дней крупнее" (раньше), а затем
   "сделай функциональней и премиальное, Название тарифа и описание, число
   дней перенеси в правую сторону и возьми в блок, оформи как то поинтересней
   и нагляднее" — один ряд: слева название + описание (трафик/устройства),
   справа — цветная плашка-"чип" с крупной цифрой дней, а не отдельная строка
   под названием. premium-gold-card даёт золотое свечение в углу (см. её
   докстринг выше) — контенту нужен z-index, чтобы быть поверх этого свечения. */
.tariff-status-card{ padding-bottom:14px; }
.tariff-status-card > *{ position:relative; z-index:1; }
.tariff-hero-row{ display:flex; align-items:center; justify-content:space-between; gap:14px; }
.tariff-hero-main{ min-width:0; flex:1; }
.tariff-name-lg{ font-size:19px; font-weight:800; letter-spacing:-.2px; line-height:1.25; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.tariff-desc{ font-size:11.5px; color:var(--ink-dim); font-weight:600; margin-top:5px; }
.tariff-expire{ font-size:11px; color:var(--ink-dim); opacity:.75; margin-top:3px; }
/* Чип с днями — компактная цветная "капсула" справа от названия тарифа,
   цвет меняется по срочности (см. .ok/.warn/.danger — те же tint-переменные,
   что и у status-pill/badges по всему кабинету, ничего нового не вводим). */
.tariff-days-chip{
  flex-shrink:0; text-align:center; border-radius:16px; padding:9px 15px; min-width:70px;
  background:var(--card-soft); border:1.5px solid var(--border);
}
.tariff-days-chip-num{ font-size:27px; font-weight:800; font-variant-numeric:tabular-nums; letter-spacing:-.6px; line-height:1; }
.tariff-days-chip-label{ font-size:9.5px; font-weight:700; text-transform:uppercase; letter-spacing:.3px; margin-top:3px; color:var(--ink-dim); }
.tariff-days-chip.ok{ background:var(--green-tint); border-color:rgba(111,160,87,.4); }
.tariff-days-chip.ok .tariff-days-chip-num{ color:var(--green); }
.tariff-days-chip.warn{ background:var(--gold-tint); border-color:rgba(201,162,92,.4); }
.tariff-days-chip.warn .tariff-days-chip-num{ color:var(--gold); }
.tariff-days-chip.danger{ background:var(--red-tint); border-color:rgba(210,105,90,.4); }
.tariff-days-chip.danger .tariff-days-chip-num{ color:var(--red); }
/* см. просьбу "с начало дни потом время всё в том же квадратике только
   красиво и понятно... не мелко чтобы было видно" — в последние сутки чип
   шире (нужно место под "23:59:59") и цифры крупнее моноширинным начертанием,
   чтобы секунды не "прыгали" по ширине на каждый тик (см. _tickTrialChip в
   app.js). */
.tariff-days-chip.countdown-live{ min-width:122px; padding:9px 13px; animation: dotPulseRed 1.8s ease-in-out infinite; }
.tariff-days-chip.countdown-live .tariff-days-chip-num{ font-size:20px; letter-spacing:0; font-family:var(--mono); font-variant-numeric:tabular-nums; }
.tariff-days-chip.countdown-live .tariff-days-chip-label{ font-size:8.5px; }

/* Общая "премиальная" поверхность — тёмный градиент + золотое свечение в
   углу (НЕ вариант .balance-hero — см. её докстрин выше: та карточка
   единственная намеренно светлая во всём приложении, эту трогать нельзя).
   Используется и картой VPN-подписки (.vpn-premium-card, экран Подписка), и
   картой "Отправить чек" (.send-receipt-card, экран Финансы/Баланс) — общий
   визуальный язык "премиальности" вместо копирования одних и тех же
   градиент/glow-правил в обоих местах. */
/* Смягчено при плоском редизайне (см. .paper выше) — раньше отдельная
   рамка+тень выделяли эту карточку из общего плоского фона резче, чем
   остальные секции. Лёгкий золотой тон/градиент оставлен намеренно (это
   ЕДИНСТВЕННЫЙ акцент под действительно важный контент — подписка/баланс/
   рефералка, как выделены отдельные строки "Фьючерсы"/крипто в Wallet), но
   без чёткого контура коробки — просто тёплый тон поверх обычного фона. */
.premium-gold-card{
  position:relative; overflow:hidden;
  background: linear-gradient(150deg, #2E2717 0%, var(--card) 55%, #241F17 100%);
  border:none;
  box-shadow: 0 1px 2px rgba(0,0,0,.22);
}
.premium-gold-card::before{
  /* Раньше ширина/высота были в % от размера самой карточки (65%/150%) —
     на невысоких карточках это была аккуратная круглая подсветка в углу, но
     на длинных карточках (например "Обзор системы" на Главной с кучей строк
     статистики и графиком) те же проценты растягивали круг в вытянутый
     эллипс на всю высоту — визуально это читалось как светлая диагональная
     "полоса"/жгут через всю карточку (см. скриншот с обводкой). Фиксированный
     размер в px не зависит от высоты содержимого — круг остаётся кругом и
     свечением только в самом углу, при любой длине карточки. */
  content:""; position:absolute; top:-70px; right:-60px; width:220px; height:220px; border-radius:50%;
  background: radial-gradient(circle, rgba(201,162,92,.16), transparent 70%);
  pointer-events:none;
}

/* "Премиальная" карточка VPN-подписки на экране Подписка (см. loadSubscription
   в app.js) — компактная замена прежнего длинного ряда с голым текстом URL:
   тариф + оставшееся время дней видны сразу, ссылка в одну строку с
   копированием по тапу, QR — маленькая тап-миниатюра, а не картинка на всю
   ширину. */

/* Лёгкая "дышащая" золотая подсветка рамки карточки — привлекает внимание к
   VPN-подписке БЕЗ анимированной подложки/декоративного блока (эту концепцию
   убрали ранее целиком, см. "давай уберем везде анимированную подложку она
   занимает место"): box-shadow рисуется поверх контента и не влияет на
   layout/высоту карточки — места не занимает, но карточка заметно "живая".
   Период медленный и амплитуда небольшая — тот же принцип, что и в
   отменённой подложке ("не особо заметная и не напрягающая"). */
.vpn-premium-card{ animation: vpnPremiumGlow 3.6s ease-in-out infinite; }
@keyframes vpnPremiumGlow{
  0%, 100%{ box-shadow: inset 0 0 0 1px rgba(201,162,92,.05), var(--shadow-card), 0 0 0 0 rgba(212,175,90,0); }
  50%{ box-shadow: inset 0 0 0 1px rgba(201,162,92,.05), var(--shadow-card), 0 0 24px 3px rgba(212,175,90,.32); }
}
/* Короткая подсказка под ссылкой — поясняет назначение блока (то, что нужно
   вставить в VPN-приложение), тон/формулировка перекликаются с текстами бота
   (bot/texts.py: LINK_FILE_CONTENT/QR_CAPTION — "…в приложение для
   подключения"). */
.vpn-premium-hint{ font-size:11.5px; line-height:1.4; color:var(--ink-dim); margin:0 0 12px; position:relative; z-index:1; }
.vpn-premium-top{ display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:12px; position:relative; z-index:1; }
.vpn-premium-label{ font-size:10.5px; font-weight:700; letter-spacing:1.1px; text-transform:uppercase; color:var(--gold); }
.vpn-premium-mid{ display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:12px; position:relative; z-index:1; }
.vpn-premium-name{ font-size:17px; font-weight:800; letter-spacing:-.2px; margin-bottom:3px; }
.vpn-premium-days{ font-size:12px; font-weight:600; color:var(--ink-dim); }
.vpn-premium-days b{ font-size:15px; font-weight:800; color:var(--ink); font-variant-numeric:tabular-nums; }
.vpn-premium-days.warn b{ color:var(--gold); }
.vpn-premium-days.danger b{ color:var(--red); }
.vpn-premium-expire{ font-size:11px; font-weight:500; color:var(--ink-dim); opacity:.8; margin-top:1px; }
.vpn-premium-qr-wrap{ flex-shrink:0; width:56px; height:56px; border-radius:12px; background:#fff; padding:5px; cursor:pointer; }
.vpn-premium-qr-img{ width:100%; height:100%; object-fit:contain; display:block; }
.vpn-premium-link{
  display:flex; align-items:center; gap:9px; background:rgba(0,0,0,.24); border:1px solid rgba(255,255,255,.08);
  border-radius:12px; padding:10px 11px; cursor:pointer; position:relative; z-index:1; margin-bottom:12px;
  transition: background .12s ease, transform .15s cubic-bezier(.34,1.4,.64,1);
}
.vpn-premium-link:active{ transform:scale(.98); }
.vpn-premium-link .icon-badge{ margin:0; flex-shrink:0; width:32px; height:32px; }
.vpn-premium-link .icon-badge svg{ width:15px; height:15px; }
.vpn-premium-link-text{ flex:1; min-width:0; font-family:var(--mono); font-size:11px; color:var(--ink-dim); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.vpn-premium-link .copy-ic{ flex-shrink:0; transition: transform .25s cubic-bezier(.34,1.56,.64,1), color .2s ease; }
.vpn-premium-link.copied{ border-color: var(--green); }
.vpn-premium-link.copied .copy-ic{ color:var(--green); transform: scale(1.35) rotate(-8deg); }
.vpn-premium-card .btn-row{ position:relative; z-index:1; margin-bottom:0; }
.vpn-premium-card .btn-row .btn{ margin-bottom:0; }

/* Карточка «Отправить чек» на экране Финансы/Баланс (по просьбе "только для
   пользователей и технической поддержки... сделай более премиальный вид
   карточки") — та же .premium-gold-card поверхность, что и у VPN-подписки:
   иконка + заголовок + короткое описание + кнопка, ничего лишнего. */
.send-receipt-card{ display:flex; flex-direction:column; }
.send-receipt-card .icon-badge{ background:var(--gold-tint); color:var(--gold); position:relative; z-index:1; margin-bottom:10px; }
.send-receipt-card .sr-title{ font-size:15.5px; font-weight:800; margin-bottom:5px; position:relative; z-index:1; }
.send-receipt-card .sr-desc{ font-size:12px; color:var(--ink-dim); line-height:1.55; margin-bottom:13px; position:relative; z-index:1; }
.send-receipt-card .btn{ position:relative; z-index:1; margin-bottom:0; }

/* ================= блоки данных ================= */
/* Светлая "визитка" баланса на экране Финансы (см. эскиз пользователя) —
   единственное место в приложении с осознанно светлой карточкой на тёмном
   фоне, поэтому отдельный компонент, а не вариант .case-box (который остаётся
   тёмным везде — Рефералы/Поддержка/Подписка и т.д., трогать его нельзя). */
.balance-hero{
  position:relative; overflow:hidden; display:flex; align-items:center; gap:13px;
  background: linear-gradient(135deg, #F5EEDD, #E7D9B8); color:var(--brown-dark);
  border-radius: var(--radius-l); padding:16px 18px; margin-bottom:8px; border:1px solid rgba(20,17,12,.08);
}
.balance-hero .icon-badge{ background:var(--brown-dark); color:#F1E4C4; width:44px; height:44px; border-radius:13px; flex-shrink:0; margin:0; }
.balance-hero .bh-text{ flex:1; min-width:0; position:relative; z-index:1; }
.balance-hero .bh-label{ font-size:11.5px; color:rgba(20,17,12,.6); font-weight:700; margin-bottom:3px; }
.balance-hero .bh-value{ font-size:25px; font-weight:800; letter-spacing:-.4px; font-variant-numeric:tabular-nums; }
.balance-hero .bh-coins{ position:absolute; right:-8px; top:50%; transform:translateY(-50%); opacity:.95; pointer-events:none; }
.balance-hint-outside{ font-size:11px; color:var(--ink-dim); margin:0 2px 12px; }

/* Экран "Рефералы" для пользователя — приглашающий баннер (см. живой фидбег
   со скриншотом раздела "Пригласить друзей" у Wallet: крупная иконка,
   заголовок, кнопка "Поделиться" сразу под ним). Текст заголовка целиком
   собран из реальных настроек программы (см. rewardHeadline в app.js) — тут
   только вёрстка. */
.ref-hero{ text-align:center; padding-top: var(--sp-6); padding-bottom: var(--sp-5); }
.ref-hero-icon{
  width:56px; height:56px; margin:0 auto 14px; border-radius:16px;
  background:var(--gold-tint); color:var(--gold);
  display:flex; align-items:center; justify-content:center;
}
.ref-hero-icon svg{ width:28px; height:28px; }
/* см. просьбу "убери [плоскую коробочку] сделай премиальную коробочку
   цветную красивую с бантиком и анимированную" — многоцветный "картинный"
   подарок (GIFT_PICTURE_SVG в app.js — та же коробка+бант, что и на плитке
   "Рефералы" на Главной) вместо плоской одноцветной line-иконки, крупнее,
   с мягким золотым свечением позади и вечным лёгким покачиванием/наклоном —
   "живая" премиальная деталь по центру, а не статичный плоский квадрат. */
.ref-hero-icon.gift-hero{
  width:92px; height:92px; background:none; position:relative;
  animation: giftHeroFloat 3.4s ease-in-out infinite;
}
.ref-hero-icon.gift-hero::before{
  content:""; position:absolute; inset:-10px; border-radius:50%;
  background: radial-gradient(circle, rgba(201,162,92,.35), transparent 72%);
}
.ref-hero-icon.gift-hero svg{ width:64px; height:64px; position:relative; z-index:1; filter: drop-shadow(0 8px 16px rgba(0,0,0,.4)); }
@keyframes giftHeroFloat{ 0%,100%{ transform:translateY(0) rotate(0deg);} 50%{ transform:translateY(-6px) rotate(-4deg);} }
.ref-hero-title{ font-size:18px; font-weight:800; line-height:1.35; margin-bottom:6px; }
.ref-hero-sub{ font-size:12px; color:var(--ink-dim); margin-bottom:16px; }
.ref-hero .ref-link{ text-align:left; }
/* "Как это работает" — пронумерованные шаги вместо одного абзаца в
   .dashed-box (тот же реальный текст, просто разбит по шагам, как в Wallet). */
.ref-step{ display:flex; align-items:flex-start; gap:12px; padding:9px 0; font-size:13px; line-height:1.5; }
.ref-step + .ref-step{ border-top:1px solid var(--border); }
.ref-step-num{
  flex-shrink:0; width:24px; height:24px; border-radius:50%; margin-top:1px;
  background:var(--gold-tint); color:var(--gold); font-weight:800; font-size:11.5px;
  display:flex; align-items:center; justify-content:center;
}
.case-box{ border-radius: var(--radius-m); padding:15px 16px; margin-bottom:12px; background:var(--card-soft); border:1px solid var(--border); position:relative; overflow:hidden; }
.case-box .cb-icon{ position:absolute; top:14px; right:14px; width:38px; height:38px; border-radius:11px; background:rgba(201,162,92,.16); color:var(--gold); display:flex; align-items:center; justify-content:center; }
.case-box .cb-icon svg{ width:19px; height:19px; }
.case-box .cb-hint{ font-size:11px; color:var(--ink-dim); margin-top:9px; }
.case-id{ font-family:var(--mono); font-size:10px; color:var(--ink-dim); letter-spacing:1px; text-transform:uppercase; margin-bottom:7px; font-weight:600; }
/* Самая крупная цифра на экране (сумма реф. начислений, баланс и т.п.) —
   единственное место, где реально применяем clamp() (см. requirements п.19
   "типографика с fluid-размерами через clamp() где оправдано"): на очень
   узких экранах (<360px) с длинными суммами в рублях это единственная
   надпись, которая рискует не влезть в ширину карточки. Остальной текст
   и так укладывается в дискретные max-width брейкпоинты выше — плодить
   clamp() по всему файлу без реальной необходимости не стали. */
.big-num{ font-size:clamp(22px, 7vw, 28px); font-weight:800; color:var(--ink); font-variant-numeric:tabular-nums; letter-spacing:-.5px; }
.big-num small{ font-size:13px; font-weight:500; color:var(--ink-dim); }
.bar{ height:7px; border-radius:5px; background:rgba(255,255,255,.08); margin-top:10px; overflow:hidden; }
.bar > div{ height:100%; border-radius:5px; background: var(--green); transition: width .7s cubic-bezier(.22,.9,.32,1); }

/* ================= Профиль: «Настройки уведомлений» (переключатели-строки
   + iOS-style switch) — см. просьбу "Настройки уведомлений: Окончание
   подписки/Предупреждение о трафике/Низкий баланс/Новости/Промо-предложения",
   webapp/frontend/app.js._profileNotificationsBlockHtml. ================= */
/* см. просьбу "Блок настройка уведомлений Сделай компактней" — уменьшены
   вертикальные отступы строк/переключателей и межстрочный набор подписи,
   сам переключатель тоже чуть меньше (36x21 вместо 42x25). */
.pref-row{ display:flex; align-items:center; justify-content:space-between; gap:12px; padding:8px 0; }
.pref-text b{ display:block; font-size:12.5px; font-weight:700; line-height:1.25; }
.pref-text span{ display:block; font-size:10.5px; color:var(--ink-dim); margin-top:1px; line-height:1.3; }
.pref-row + .field-label{ margin-top:-2px; }
.switch{ position:relative; display:inline-block; width:36px; height:21px; flex-shrink:0; cursor:pointer; }
.switch input{ position:absolute; opacity:0; width:100%; height:100%; margin:0; cursor:pointer; }
.switch-slider{ position:absolute; inset:0; background:var(--card-soft); border:1.5px solid var(--border); border-radius:21px; transition:background .15s ease, border-color .15s ease; pointer-events:none; }
.switch-slider::before{ content:""; position:absolute; width:14px; height:14px; left:2.5px; top:2.5px; background:var(--ink-dim); border-radius:50%; transition:transform .15s cubic-bezier(.34,1.4,.64,1), background .15s ease; }
.switch input:checked ~ .switch-slider{ background:var(--gold-tint); border-color:var(--gold); }
.switch input:checked ~ .switch-slider::before{ transform:translateX(15px); background:var(--gold); }
/* компактный вариант .filter-chip-row (см. _profileNotificationsChipRow в
   app.js) — те же чипы, но ниже и с меньшим отступом снизу, специально для
   плотного блока "Настройки уведомлений". */
.pref-chip-row{ margin-bottom:4px; }
.pref-chip-row .filter-chip{ padding:6px 12px; min-height:29px; font-size:12px; }

/* ================= раскрывающийся блок "кнопка -> контент" (тарифы на
   Подписке, история операций на Балансе — по просьбе не показывать список
   сразу, а прятать его за кнопкой) ================= */
.toggle-row{ display:flex; align-items:center; justify-content:space-between; }
.toggle-row .toggle-chev{ margin-left:auto; transition: transform .28s cubic-bezier(.22,.9,.32,1); }
.toggle-row.open .toggle-chev{ transform: rotate(90deg); }
.collapse-body{ max-height:0; overflow:hidden; transition: max-height .32s cubic-bezier(.22,.9,.32,1); }
.collapse-body-inner{ padding-top:11px; }

/* ================= крупные карточки статистики (Рефералы) ================= */
/* auto-fit вместо жёсткого repeat(3,1fr) — на обычном экране это те же 3
   равные колонки (растягиваются на всю ширину контейнера, как и раньше),
   но на очень узком экране (<~380px, где 3×120px не помещаются) колонки
   сами перетекут на следующую строку вместо того, чтобы сжиматься до
   нечитаемой ширины — без отдельного media-query-костыля под каждый размер. */
.stat-grid{ display:grid; grid-template-columns:repeat(auto-fit, minmax(112px,1fr)); gap:9px; margin-bottom:12px; }
.stat-card{
  border-radius: var(--radius-m); padding:16px 8px 14px; background:var(--card-soft); border:1px solid var(--border);
  text-align:center; transition: transform .15s cubic-bezier(.34,1.4,.64,1), background .12s ease;
}
.stat-card:active{ transform:scale(.94); }
.stat-card .icon-badge{ margin:0 auto 9px; }
.stat-card b{ display:block; font-size:19px; font-weight:800; font-variant-numeric:tabular-nums; }
.stat-card span{ font-size:9px; color:var(--ink-dim); text-transform:uppercase; letter-spacing:.2px; font-weight:600; display:block; margin-top:3px; }
.stat-card.green{ background:var(--green-tint); border-color: rgba(111,160,87,.3); }
.stat-card.gold{ background:var(--gold-tint); border-color: rgba(201,162,92,.32); }
.grid3{ display:grid; grid-template-columns:repeat(3,1fr); gap:8px; text-align:center; margin-top:13px; }
.grid4{ display:grid; grid-template-columns:repeat(4,1fr); gap:6px; text-align:center; margin-bottom:12px; }
.grid3 .item b, .grid4 .item b{ display:block; font-size:15px; font-weight:800; font-variant-numeric:tabular-nums; }
.grid3 .item span, .grid4 .item span{ font-size:8.5px; color:var(--ink-dim); text-transform:uppercase; letter-spacing:.2px; font-weight:600; display:block; margin-top:2px; }
.grid4 .item{ border-radius:var(--radius-s); padding:10px 4px; background:var(--card-soft); border:1px solid var(--border); }
@media (max-width:374px){ .grid4{ grid-template-columns:repeat(2,1fr); } }

/* Дашборд персонала на Главной (см. просьбу "добавь информацию дашборд как
   в боте сделай премиальный вывод по блокам если возможно с графиком") —
   .premium-gold-card поверхность (та же, что у Подписки/Прав доступа),
   ячеек может быть от 6 до 10 в зависимости от роли, поэтому auto-fill
   вместо жёсткого числа колонок — само подстраивается под ширину экрана. */
.dash-card > *{ position:relative; z-index:1; }
.dash-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(84px,1fr)); gap:7px; text-align:center; margin-bottom:2px; }
.dash-grid .item{ border-radius:var(--radius-s); padding:10px 6px; background:rgba(0,0,0,.2); border:1px solid rgba(255,255,255,.07); }
.dash-grid .item b{ display:block; font-size:14.5px; font-weight:800; font-variant-numeric:tabular-nums; }
.dash-grid .item span{ font-size:8.5px; color:var(--ink-dim); text-transform:uppercase; letter-spacing:.2px; font-weight:600; display:block; margin-top:3px; line-height:1.25; }
/* см. просьбу "в дашборде должен смениться цвет на блоке новая заявка или
   стать как-то заметней, при нажатии попадаем..." — плитка "Новых заявок"
   (см. loadStaffHomeGlance в app.js) получает тревожный красный тон +
   мигание (та же dotPulseRed, что и у "офлайн" статус-точки), когда значение
   > 0, и становится кликабельной (переход на вкладку "Тикеты", где уже живёт
   блок «📨 Заявки на вступление» — см. _staffRequestsBlockHtml). */
.dash-grid .item-alert{ background: var(--red-tint); border-color: rgba(210,105,90,.4); animation: dotPulseRed 1.8s ease-in-out infinite; }
.dash-grid .item-alert b{ color: var(--red); }
.dash-grid .item-clickable{ cursor:pointer; transition: transform .15s cubic-bezier(.34,1.4,.64,1); }
.dash-grid .item-clickable:active{ transform:scale(.94); }
/* Ручной SVG-бар-чарт (тот же приём, что и hand-crafted иконки в app.js —
   без сторонних chart-библиотек) — доход за последние 7 дней, сегодняшний
   столбик выделен золотым. */
.dash-chart-wrap{ margin-top:12px; padding-top:11px; border-top:1px dashed rgba(255,255,255,.1); }
.dash-chart-label{ font-size:10.5px; color:var(--ink-dim); font-weight:600; margin-bottom:2px; }
.dash-chart-svg{ width:100%; height:74px; display:block; }

/* ================= «📊 Статистика» (Администратор/Техподдержка, см.
   webapp/api/routers/staff_stats.py) — тот же набор данных, что "📈
   Статистика" в постоянном нижнем меню бота, премиальные карточки вместо
   голого текста (см. просьбу "сделано премиально и удобно для пользования, с
   анимацией, кнопками закрытия и крестиком"). ================= */

/* Полоска переключения серверов (см. bot/handlers/stats.py "🌐 Серверы") —
   показывается только когда серверов больше одного, обычные .chip делаются
   тесноватыми в горизонтальном скролле, поэтому свой узкий вариант. */
.stats-server-strip{ display:flex; gap:7px; overflow-x:auto; padding-bottom:2px; margin-bottom:12px; scrollbar-width:none; }
.stats-server-strip::-webkit-scrollbar{ display:none; }
.stats-server-chip{ flex-shrink:0; border:1.5px solid var(--border); border-radius:var(--radius-s); padding:8px 13px; font-size:12px; font-weight:700; background:var(--card-soft); cursor:pointer; white-space:nowrap; transition: transform .15s cubic-bezier(.34,1.4,.64,1), background .12s ease; }
.stats-server-chip:active{ transform:scale(.95); }
.stats-server-chip.active{ background: var(--brown-dark); color:#F5EAD0; border-color: var(--brown-dark); }

/* Строка нагрузки (CPU/RAM/Диск) — визуальная замена текстовых "████░░ 42%"
   ползунков бота (bot/utils/stats_view.py.bar()), тот же принцип: цвет
   меняется по порогу (см. _statLoadColor в app.js — 🟢/🟡/🔴, как и
   _level_emoji в боте), просто нарисовано, а не эмодзи-блоками. */
.stat-load-row{ margin-bottom:12px; }
.stat-load-row:last-child{ margin-bottom:0; }
.stat-load-head{ display:flex; align-items:center; justify-content:space-between; font-size:11.5px; color:var(--ink-dim); font-weight:600; margin-bottom:5px; }
.stat-load-head b{ color:var(--ink); font-size:13.5px; font-variant-numeric:tabular-nums; }
.stat-load-track{ height:7px; border-radius:5px; background:rgba(0,0,0,.22); overflow:hidden; }
.stat-load-fill{ height:100%; border-radius:5px; transition: width .5s cubic-bezier(.22,.9,.32,1); }
.stat-load-fill.ok{ background: linear-gradient(90deg, var(--green), #8fc27a); }
.stat-load-fill.warn{ background: linear-gradient(90deg, var(--gold), #e0c583); }
.stat-load-fill.crit{ background: linear-gradient(90deg, var(--red), #e08a7a); }

/* Вкладки разделов (Обзор/VPN/Безопасность/Логи/Графики) — те же .chip/.chips,
   что и у "Оповещения" (notif-section-chip), просто больше колонок и мельче
   шрифт под 5 пунктов вместо 4 (см. app.js openNotificationEditModal). */
.stats-tabs{ display:grid; gap:6px; margin-bottom:12px; }
.stats-tab{ border:1.5px solid var(--border); border-radius:var(--radius-s); padding:9px 2px; font-size:10px; font-weight:700; background:var(--card-soft); cursor:pointer; text-align:center; transition: transform .15s cubic-bezier(.34,1.4,.64,1), background .12s ease; }
.stats-tab:active{ transform:scale(.95); }
.stats-tab.active{ background: var(--brown-dark); color:#F5EAD0; border-color: var(--brown-dark); }

/* Переключатель метрика/период на "Графиках" — компактные пилюли в строку. */
.stats-period-row{ display:flex; gap:6px; margin:10px 0 4px; flex-wrap:wrap; }
.stats-period-pill{ border:1.5px solid var(--border); border-radius:999px; padding:6px 12px; font-size:11px; font-weight:700; background:var(--card-soft); cursor:pointer; transition: transform .15s cubic-bezier(.34,1.4,.64,1), background .12s ease; }
.stats-period-pill:active{ transform:scale(.94); }
.stats-period-pill.active{ background: var(--gold-tint); border-color: var(--gold); color: var(--gold); }

/* Сам линейный график (см. app.js._statsLineChartSvg) — тот же ручной SVG
   приём, что и .dash-chart-svg (доход за 7 дней), просто с точками по
   времени и опциональной второй серией (⬇/⬆, см. staff_stats.py.metric
   speed/traffic). */
.stats-chart-wrap{ margin-top:6px; }
.stats-chart-svg{ width:100%; height:150px; display:block; }
.stats-chart-legend{ display:flex; gap:14px; margin-top:8px; font-size:10.5px; color:var(--ink-dim); font-weight:600; }
.stats-chart-legend span{ display:inline-flex; align-items:center; gap:5px; }
.stats-chart-legend i{ width:10px; height:10px; border-radius:3px; display:inline-block; }

/* Экспорт (только Администратор, см. staff_stats.py.export_stats) — три
   компактные кнопки в ряд вместо трёх .btn.outline друг под другом. */
.stats-export-row{ display:grid; grid-template-columns:repeat(3,1fr); gap:8px; margin-top:4px; }
.stats-export-row .btn{ padding:10px 4px; font-size:11.5px; }

@media (max-width:374px){ .stats-tabs{ grid-template-columns:repeat(3,1fr) !important; } }

/* ================= иконки-бейджи ================= */
.icon-badge{
  width:38px; height:38px; border-radius:11px; display:flex; align-items:center; justify-content:center;
  flex-shrink:0; background: var(--gold-tint); color: var(--brown);
  transition: background .25s ease, color .25s ease;
}
.icon-badge svg{ width:18px; height:18px; }
.icon-badge.green{ background: var(--green-tint); color: var(--green); }
.icon-badge.red{ background: var(--red-tint); color: var(--red); }
.icon-badge.dark{ background: var(--brown-dark); color:#F1E4C4; }
/* Нейтральный светлый кружок под цветной значок СБП (см. SBP_BADGE_SVG в
   app.js) — если посадить цветной лого на золотую/зелёную тонировку, как у
   остальных icon-badge, цвета забьются, поэтому у этого варианта фон всегда
   светлый, независимо от темы. */
.icon-badge.plain{ background:#F2EBDC; }
.icon-badge.plain svg{ width:20px; height:20px; }

/* Тоже auto-fit — на мобильном ширины хватает ровно на 2 колонки (как и
   было), а дальше число колонок растёт САМО вместе с шириной контейнера
   (без фиксированного "включить 3-ю колонку только после 1200px" скачка). */
.shortcut-grid{ display:grid; grid-template-columns:repeat(auto-fit, minmax(148px,1fr)); gap:10px; margin-bottom:12px; }
.shortcut-card{
  border-radius: var(--radius-m); padding:16px 14px; background:var(--card-soft); border:1px solid var(--border);
  cursor:pointer; text-align:left; font-family:var(--font); color:var(--ink);
  transition: transform .15s cubic-bezier(.34,1.4,.64,1), background .15s ease;
}
.shortcut-card:active{ transform:scale(.96); }
.shortcut-card.tint-green{ background: var(--green-tint); border-color: rgba(111,160,87,.3); }
.shortcut-card.tint-green:active{ background: rgba(111,160,87,.24); }
.shortcut-card.tint-gold{ background: var(--gold-tint); border-color: rgba(201,162,92,.32); }
.shortcut-card.tint-gold:active{ background: rgba(201,162,92,.24); }
/* Круглая "картинка" вместо одноцветной line-иконки (см. эскиз — WALLET_PICTURE_SVG/
   GIFT_PICTURE_SVG в app.js, многоцветные, поэтому без .icon-badge svg{color:currentColor}). */
.shortcut-card .icon-badge{ margin:0 0 11px; width:46px; height:46px; border-radius:50%; background:rgba(255,255,255,.14); }
.shortcut-card .sc-label{ font-size:10.5px; color:var(--ink-dim); font-weight:700; text-transform:uppercase; letter-spacing:.3px; display:block; margin-bottom:3px; }
.shortcut-card b{ display:block; font-size:17px; font-weight:800; font-variant-numeric:tabular-nums; letter-spacing:-.3px; }
.shortcut-card .sc-sub{ font-size:10.5px; color:var(--ink-dim); font-weight:600; display:block; margin-top:1px; }

/* ================= кнопки ================= */
.btn{
  display:flex; align-items:center; justify-content:center; gap:7px; width:100%; text-align:center;
  font-family:var(--font); font-weight:700; font-size:14px; letter-spacing:.1px;
  padding:14px; min-height:48px; border-radius: var(--radius-m); border:none;
  background: var(--brown-dark); color:#F5EAD0; cursor:pointer; margin-bottom:9px;
  transition: transform .15s cubic-bezier(.34,1.4,.64,1), filter .12s ease, box-shadow .15s ease;
  box-shadow: var(--shadow-btn);
  /* .btn часто рисуется на <a href="..."> (например "Открыть подписку" на
     экране "Подписка"), а не только на <button> — без явного сброса браузер
     подчёркивает такие ссылки своим дефолтным стилем, что выбивается из
     премиального вида кнопки (см. просьбу "убери подчеркивание ссылки
     Открыть подписку"). Сброс тут, на самом .btn, а не отдельным правилом —
     чинит разом ВСЕ <a class="btn ...">, включая QR-модалки/"Скачать QR". */
  text-decoration:none;
}
.btn:active{ transform:scale(.97); filter:brightness(1.12); }
.btn.outline{ background:transparent; color:var(--ink); border:1.5px solid var(--border); box-shadow:none; }
.btn.gold{ background: var(--gold); color:#2c2109; box-shadow: 0 4px 14px rgba(201,162,92,.4); }
.btn.danger{ background: var(--red); color:#fff; box-shadow: 0 4px 14px rgba(210,105,90,.35); }
.btn.small{ width:auto; display:inline-flex; padding:9px 15px; min-height:auto; font-size:12px; margin:0; }
/* Крупная кнопка — см. просьбу "сделай раздел поддержка более функциональней
   и удобней крупней" (главное действие экрана "Тикеты"). */
.btn.btn-lg{ min-height:56px; font-size:15px; }
.btn:disabled{ opacity:.4; cursor:not-allowed; transform:none; box-shadow:none; }
.btn svg{ width:15px; height:15px; flex-shrink:0; }
/* Два действия в ряд, поровну — используется мастером диагностики
   подключения (Да/Нет) в разделе "Поддержка", см. loadHelp в app.js. */
.btn-row-2{ display:flex; gap:10px; margin-bottom:8px; }
.btn-row-2 .btn{ flex:1; margin:0; }

.dashed-box{ border-radius: var(--radius-m); padding:12px 14px; font-size:12px; color:var(--ink-dim); margin-bottom:12px; line-height:1.6; background:var(--card-soft); border:1px dashed var(--border); }
.error-banner{ color:var(--red); border-radius: var(--radius-m); padding:12px 14px; font-size:12px; margin-bottom:12px; background:var(--red-tint); font-weight:500; }
.spinner-text{ text-align:center; padding:30px 0; color:var(--ink-dim); font-size:12.5px; }
.spinner-text::before{
  content:""; display:block; width:22px; height:22px; margin:0 auto 11px;
  border:2.5px solid var(--border); border-top-color:var(--gold); border-radius:50%;
  animation: spin .75s linear infinite;
}
@keyframes spin{ to{ transform:rotate(360deg);} }

.skeleton{ background: linear-gradient(100deg, var(--card-soft) 30%, #3A3226 50%, var(--card-soft) 70%); background-size:200% 100%; animation: shimmer 1.3s ease-in-out infinite; border-radius:11px; }
@keyframes shimmer{ from{ background-position:150% 0;} to{ background-position:-50% 0;} }
.skeleton-block{ height:110px; margin-bottom:12px; }
.skeleton-line{ height:14px; margin-bottom:8px; width:70%; }

/* ================= empty state ================= */
.empty-state{ text-align:center; padding:30px 16px; }
.empty-state .es-icon{ width:44px; height:44px; margin:0 auto 12px; border-radius:14px; background:var(--card-soft); border:1px solid var(--border); display:flex; align-items:center; justify-content:center; color:var(--ink-dim); }
.empty-state .es-icon svg{ width:20px; height:20px; }
.empty-state .es-title{ font-size:14px; font-weight:800; margin-bottom:4px; }
.empty-state .es-sub{ font-size:12px; color:var(--ink-dim); line-height:1.55; max-width:260px; margin:0 auto 14px; }

/* ================= статус доступа (pending_approval/banned) =================
   см. просьбу "сделай текст крупнее и информативнее, выведи время до
   проверки, не уходите со страницы" — отдельный, заметно более крупный блок,
   чем .empty-state выше (тот рассчитан на мелкие плейсхолдеры пустых
   списков внутри вкладок, а не на главный экран ожидания). */
.access-status{ text-align:center; padding:40px 20px 32px; }
.access-status .as-icon{
  width:76px; height:76px; margin:0 auto 18px; border-radius:22px;
  background:var(--gold-tint); border:1px solid var(--border); color:var(--gold);
  display:flex; align-items:center; justify-content:center;
  animation:as-pulse 2.4s ease-in-out infinite;
}
.access-status.is-banned .as-icon{ background:var(--red-tint); color:var(--red); animation:none; }
.access-status.is-expired .as-icon{ background:var(--card-soft); color:var(--ink-dim); animation:none; }
.access-status .as-icon svg{ width:36px; height:36px; }
@keyframes as-pulse{
  0%, 100%{ box-shadow:0 0 0 0 rgba(201,162,92,.35); }
  50%{ box-shadow:0 0 0 12px rgba(201,162,92,0); }
}
.access-status .as-title{ font-size:21px; font-weight:800; margin-bottom:10px; letter-spacing:-.2px; }
.access-status .as-text{ font-size:14.5px; color:var(--ink-dim); line-height:1.6; max-width:380px; margin:0 auto 20px; }
.access-status .as-timer{
  display:inline-flex; align-items:center; gap:12px; background:var(--card-soft);
  border:1px solid var(--border); border-radius:var(--radius-m); padding:12px 22px; margin:0 auto 22px;
}
.access-status .as-timer-label{ font-size:12px; color:var(--ink-dim); }
.access-status .as-timer-value{ font-family:var(--mono); font-size:19px; font-weight:700; color:var(--gold); font-variant-numeric:tabular-nums; }
.access-status .as-note{
  display:flex; align-items:center; justify-content:center; gap:8px;
  font-size:12.5px; color:var(--ink-dim); line-height:1.55; max-width:340px; margin:0 auto 18px;
}
.access-status .as-note-dot{ width:7px; height:7px; border-radius:50%; background:var(--green); flex:none; animation:as-blink 1.6s ease-in-out infinite; }
@keyframes as-blink{ 0%, 100%{ opacity:1; } 50%{ opacity:.25; } }
.access-status .as-support{ margin-top:10px; font-size:11.5px; color:var(--ink-dim); }

/* ================= карточка пробного тарифа ================= */
.trial-card{
  border-radius: var(--radius-l); padding:18px 16px; margin-bottom:12px;
  background: var(--green-tint); border:1px solid rgba(111,160,87,.32);
  cursor:pointer; transition: transform .15s cubic-bezier(.34,1.4,.64,1); position:relative; overflow:hidden;
}
.trial-card:active{ transform:scale(.98); }
.trial-card .tc-badge{
  display:inline-flex; align-items:center; gap:5px; font-size:10px; font-weight:700; letter-spacing:.3px;
  text-transform:uppercase; background: var(--green); color:#fff; padding:5px 10px; border-radius:20px; margin-bottom:10px;
}
.trial-card .tc-badge svg{ width:11px; height:11px; }
.trial-card .tc-title{ font-size:16.5px; font-weight:800; margin-bottom:3px; }
.trial-card .tc-sub{ font-size:12px; color:#B9D6A6; margin-bottom:13px; line-height:1.5; }
.trial-card .tc-stats{ display:flex; gap:18px; margin-bottom:14px; }
.trial-card .tc-stats b{ display:block; font-size:18px; font-weight:800; }
.trial-card .tc-stats span{ font-size:9.5px; color:#B9D6A6; text-transform:uppercase; letter-spacing:.4px; font-weight:700; }
.trial-card .btn{ margin-bottom:0; background: var(--green); box-shadow:0 4px 14px rgba(111,160,87,.4); }
.trial-card.used{ background: var(--card-soft); border-color: var(--border); cursor:default; }
.trial-card.used .tc-badge{ background: var(--red); }
.trial-card.used .tc-sub, .trial-card.used .tc-stats span{ color: var(--ink-dim); }

.sub-balance-row{
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  border-radius: var(--radius-m); padding:13px 15px; margin-bottom:12px; background:var(--card-soft); border:1px solid var(--border);
}
.sub-balance-row .sb-label{ font-family:var(--mono); font-size:10px; color:var(--ink-dim); text-transform:uppercase; letter-spacing:.6px; margin-bottom:3px; font-weight:600; }
.sub-balance-row .sb-value{ font-size:19px; font-weight:800; font-variant-numeric:tabular-nums; }
.sub-balance-row .btn{ width:auto; margin:0; padding:11px 16px; font-size:12px; flex-shrink:0; }

.info-chip-row{ display:flex; align-items:center; gap:9px; border-radius: var(--radius-m); padding:13px 14px; margin-bottom:12px; background:var(--card-soft); border:1px solid var(--border); }
.info-chip-row .icon-badge{ flex-shrink:0; }
.info-chip-row .ic-cols{ flex:1; min-width:0; display:grid; grid-template-columns:1fr 1fr; gap:8px; }
.info-chip .ic-title{ font-family:var(--mono); font-size:9.5px; color:var(--ink-dim); text-transform:uppercase; letter-spacing:.3px; font-weight:600; }
.info-chip .ic-value{ font-size:13.5px; font-weight:800; margin-top:2px; }

/* "Приглашён пользователем X" — заметный бейдж в карточке "Пользователь"
   (персонал), сразу под ID (см. просьбу "если пользователя кто-то пригласил
   то должно отображаться" — раньше это была рядовая строка среди дат
   регистрации/тикетов в самом низу карточки, теперь видно сразу). Тот же
   .icon-badge + gold-tint фон, что и остальные премиальные акценты карточки. */
.referrer-badge{
  display:flex; align-items:center; gap:9px; font-size:12px; color:var(--ink-dim);
  background: var(--gold-tint); border-radius: var(--radius-s); padding:8px 11px; margin:8px 0 4px;
}
.referrer-badge .icon-badge{ width:26px; height:26px; margin:0; flex-shrink:0; }
.referrer-badge .icon-badge svg{ width:14px; height:14px; }
.referrer-badge b{ color: var(--gold); font-weight:700; }

.tariff-card{ border-radius: var(--radius-m); padding:14px 15px; margin-bottom:9px; background:var(--card-soft); border:1px solid var(--border); position:relative; cursor:pointer; transition: transform .15s cubic-bezier(.34,1.4,.64,1);}
.tariff-card:active{ transform:scale(.98); }
.tariff-card.current{ background: var(--green-tint); box-shadow:inset 0 0 0 1.5px var(--green); border-color:transparent; }
.tariff-card .name{ font-size:14.5px; font-weight:800; margin-bottom:3px; padding-right:70px; }
.tariff-card .meta{ font-size:11px; color:var(--ink-dim); font-weight:500; }
.tariff-card .price{ font-size:19px; font-weight:800; color:var(--ink); white-space:nowrap; display:flex; align-items:center; gap:4px; margin-top:10px; }
.tariff-card .price svg{ width:15px; height:15px; color:var(--ink-dim); margin-left:auto; }
.tag{ position:absolute; top:-7px; right:14px; font-size:9px; font-weight:700; background:var(--red); color:#fff; padding:4px 9px; border-radius:20px; letter-spacing:.3px;}
/* "Своя конфигурация" в списке тарифов (см. openCustomTariffLevel в app.js,
   просьба "настроить любую конфигурацию тарифа") — та же карточка, что и
   обычный тариф каталога, но пунктирная золотая рамка вместо сплошной,
   чтобы на глаз читалась как "не из каталога, свободный ввод". */
/* см. просьбу "Тарифы по удобней блоками квадратиками в 2-3 блока", а затем
   "теперь не влезают... напиши больше информации" — .tariffs-grid/.grid-tile
   используются ТОЛЬКО каталогом на экране "Тарифы" (см. loadTariffs в
   app.js); обычный .tariff-card (без .grid-tile) в модалках выдачи/продления
   тарифа персоналом остаётся строкой-списком, как раньше — там узкая
   модалка, сетка там не нужна и не помогала бы. Колонок ВСЕГДА 2 (без
   бампа до 3 на среднем экране, как было раньше) — с возросшим объёмом
   текста (срок+трафик, устройства, цена, цена/день) трём узким колонкам уже
   не хватало места, отсюда и "не влезают"; двум колонкам в наших же
   --content-max=640px хватает места стабильно, независимо от ширины окна. */
.tariffs-grid{ display:grid; grid-template-columns:repeat(2, 1fr); gap:10px; margin-bottom:4px; }
.tariff-card.grid-tile{
  display:flex; flex-direction:column; align-items:stretch; gap:6px;
  min-height:168px; padding:15px 13px; margin-bottom:0;
}
.tariff-card.grid-tile .tc-head{ flex-direction:column; align-items:flex-start; gap:8px; margin-bottom:2px; }
.tariff-card.grid-tile .name{ padding-right:0; font-size:13.5px; line-height:1.28; }
.tariff-card.grid-tile .meta{ font-size:10.5px; line-height:1.5; }
.tariff-card.grid-tile .price{ margin-top:auto; font-size:15.5px; flex-wrap:wrap; }
/* см. просьбу "напиши больше информации о тарифе" — цена в пересчёте на
   день (t.duration_days > 0 && p1.price_rub != null, см. perDayStr в
   loadTariffs) — помогает сравнить тарифы между собой быстрее, чем просто
   итоговая сумма за весь срок. */
.tc-per-day{ font-size:10px; color:var(--ink-dim); font-weight:600; margin-top:-2px; }
.custom-tariff-card{ background:transparent; border:1.5px dashed rgba(201,162,92,.55); }
.custom-tariff-card .name{ color:var(--gold); display:flex; align-items:center; gap:6px; padding-right:0; }
.custom-tariff-card .name svg{ width:16px; height:16px; }
.custom-tariff-card .price{ font-size:13px; color:var(--gold); }
.custom-tariff-card .price svg{ color:var(--gold); }

/* Пресет-чипы суммы баланса (см. renderBalanceAdjustModal в app.js, просьба
   "администратор может пополнить баланс пользователя") — переиспользует
   .chip, только с зелёным/красным акцентом на выбранном пресете вместо
   золотого (сумма поступления/списания — понятнее цветом, чем нейтральным
   золотом, как у обычных фильтров). */
.bal-preset-chip.active{ background: var(--green-tint); border-color:var(--green); color:var(--green); font-weight:700; }
.bal-preset-chip.negative.active{ background: var(--red-tint); border-color:var(--red); color:var(--red); }
.tag.green{ background: var(--green); }
.tag.gold{ background: var(--gold); color:#2c2109; }

/* ================= раскраска карточек тарифов (экран "Тарифы" — см.
   requirements "раскрась, сделай привлекательней") — цветная левая полоска +
   иконка-бейдж слева от названия, в рамках уже принятой палитры (gold/green),
   без новых чужеродных цветов. .colorful — модификатор поверх обычного
   .tariff-card (используется ТОЛЬКО на каталоге тарифов, не в служебной
   выдаче тарифа персоналом — там достаточно нейтрального списка). ========= */
.tariff-card.colorful{ border-left:3px solid var(--border); padding-left:13px; }
.tariff-card.colorful.accent-green{ border-left-color: var(--green); }
.tariff-card.colorful.accent-gold{ border-left-color: var(--gold); }
.tariff-card.colorful.accent-warm{ border-left-color: var(--brown); }
.tariff-card.colorful.current{ border-left-color: var(--green); }
/* см. просьбу "Сделай карточки по блокам и разными цветами, тоже все
   премиальные" — тот же приём цветной полоски слева, что и у карточек
   тарифов выше (.tariff-card.colorful), теперь как универсальный модификатор
   для ЛЮБОЙ .paper-карточки — разные смысловые блоки на одном экране
   (Профиль/Подписка и т.п.) отличаются на глаз цветом, а не только
   заголовком, оставаясь на той же тёмной премиальной поверхности. */
.paper.card-accent-blue{ border-left:3px solid #6C7BF7; padding-left:14px; }
.paper.card-accent-purple{ border-left:3px solid #D67CC2; padding-left:14px; }
.paper.card-accent-teal{ border-left:3px solid #5FB8AE; padding-left:14px; }
.paper.card-accent-rose{ border-left:3px solid var(--red); padding-left:14px; }
.tariff-card .tc-head{ display:flex; align-items:center; gap:11px; margin-bottom:2px; }
.tariff-card .tc-head .icon-badge{ margin:0; flex-shrink:0; }
.tariff-card .tc-head .name{ margin-bottom:2px; }
.tariff-card.colorful .name{ padding-right:60px; }

/* Баланс над каталогом тарифов (см. просьбу "добавь баланс что бы
   пользователь видел сколько у него денег") — компактная полоска, не
   отдельная карточка, чтобы не удлинять экран: иконка + подпись + сумма +
   "Пополнить" в один ряд, перенос на мобильных при нехватке места. */
.tariffs-balance-strip{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap; border-radius: var(--radius-m); padding:11px 13px;
  margin-bottom:12px; background:var(--card-soft); border:1px solid var(--border);
}
.tariffs-balance-strip .icon-badge{ margin:0; flex-shrink:0; }
.tariffs-balance-strip .tb-text{ flex:1; min-width:110px; }
.tariffs-balance-strip .tb-text b{ display:block; font-size:12.5px; font-weight:700; }
.tariffs-balance-strip .tb-text span{ display:block; font-size:10.5px; color:var(--ink-dim); margin-top:1px; }
.tariffs-balance-strip .tb-amount{ font-size:16px; font-weight:800; color:var(--gold); font-family:var(--mono); flex-shrink:0; }
.tariffs-balance-strip .btn{ width:auto; flex-shrink:0; }

/* ================= формы ================= */
input[type=text], input[type=number], input[type=email]{
  width:100%; font-family:var(--font); font-size:14px; padding:13px; min-height:46px; border-radius: var(--radius-m);
  border:1.5px solid var(--border); background:var(--card-soft); color:var(--ink); margin-bottom:9px;
  transition: border-color .15s ease, background .15s ease;
  -webkit-appearance:none; appearance:none; /* email-инпут в некоторых WebView получает системную "капсулу" без этого */
}
input[type=text]:focus, input[type=number]:focus, input[type=email]:focus{ outline:none; border-color:var(--gold); background:var(--card); }
textarea{
  width:100%; font-family:var(--font); font-size:14px; padding:12px 13px; border-radius: var(--radius-m);
  border:1.5px solid var(--border); background:var(--card-soft); color:var(--ink); margin-bottom:9px;
  resize:vertical; min-height:84px; transition: border-color .15s ease, background .15s ease;
}
textarea:focus{ outline:none; border-color:var(--gold); background:var(--card); }
input[type=file]{ position:absolute; width:1px; height:1px; opacity:0; overflow:hidden; pointer-events:none; }
.file-upload-btn{
  display:flex; align-items:center; gap:12px; width:100%; box-sizing:border-box;
  border:1.5px dashed var(--border); border-radius: var(--radius-l); padding:15px 14px; margin-bottom:10px;
  background:var(--card-soft); cursor:pointer; transition: transform .12s ease, background .12s ease, border-color .12s ease;
}
.file-upload-btn:active{ transform:scale(.98); }
.file-upload-btn svg{ width:22px; height:22px; flex-shrink:0; color:var(--ink-dim); }
.file-upload-btn .fu-text{ font-size:13px; font-weight:700; line-height:1.4; }
.file-upload-btn .fu-sub{ display:block; font-size:10px; font-weight:500; color:var(--ink-dim); margin-top:2px; }
.file-upload-btn.has-file{ border-style:solid; border-color:var(--green); background: var(--green-tint); }
.file-upload-btn.has-file svg{ color:var(--green); }
.field-row{ display:flex; gap:8px; }
.field-row input{ flex:1; }
.field-row .btn{ width:auto; flex-shrink:0; padding:0 16px; margin-bottom:9px; font-size:12px;}
.btn-row{ display:flex; gap:8px; }
.btn-row .btn{ margin-bottom:9px; }
.chips{ display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-bottom:10px; }
.chip{ border:1.5px solid var(--border); border-radius: var(--radius-s); padding:12px 15px; font-size:13.5px; font-weight:700; background:var(--card-soft); cursor:pointer; text-align:center; transition: transform .15s cubic-bezier(.34,1.4,.64,1), background .12s ease;}
.chip:active{ transform:scale(.96); }
.chip.active{ background: var(--brown-dark); color:#F5EAD0; border-color: var(--brown-dark); }
.period-row{ flex-wrap:nowrap; }
.period-row .chip{ flex:1; padding:10px 2px; font-size:11.5px; }

.pay-method, .method-btn{ border-radius: var(--radius-m); padding:12px 13px; margin-bottom:8px; font-size:12.5px; display:flex; gap:12px; align-items:center; width:100%; background:var(--card-soft); border:1.5px solid var(--border); cursor:pointer; text-align:left; font-family:var(--font); min-height:48px; transition: transform .12s ease, background .12s ease, border-color .12s ease;}
.pay-method > div, .pay-method > span:last-child, .method-btn > span:last-child{ flex:1; min-width:0; }
.pay-method .icon-badge, .method-btn .icon-badge{ margin:0; }
.pay-method:active, .method-btn:active{ transform:scale(.98); }
.method-btn:disabled{ opacity:.4; cursor:not-allowed; transform:none; }
.pay-method b, .method-btn b{ display:block; font-size:13.5px; margin-bottom:2px; font-weight:700; }
.pay-method span, .method-btn span{ color:var(--ink-dim); font-size:10.5px; line-height:1.4; display:block; font-weight:500;}

/* "Премиальный" вид строки способа оплаты (по просьбе "сделай более
   премиальный вид способа оплаты. Добавь иконки Telegram Stars/Ozon Банк/
   СБП") — крупнее бейдж значка + тонкая фирменная цветная полоска слева
   (золото Stars / синий Ozon / фиолетово-бирюзовый СБП, см.
   TELEGRAM_STARS_BADGE_SVG/OZON_BADGE_SVG/SBP_BADGE_SVG в app.js), вместо
   однотипных плоских серых строк без акцента. */
.pay-method, .method-btn{ position:relative; overflow:hidden; padding-left:16px; }
.pay-method::before, .method-btn::before{
  content:""; position:absolute; left:0; top:0; bottom:0; width:3px; background:var(--border);
}
.pay-method[data-method="stars"]::before, .method-btn[data-method="stars"]::before{ background: linear-gradient(180deg,#FFD874,#F0A93B); }
.pay-method[data-method="ozon"]::before, .method-btn[data-method="ozon"]::before{ background:#005BFF; }
.pay-method[data-method="sbp"]::before, .method-btn[data-method="sbp"]::before{ background: linear-gradient(180deg,#7C5CFC,#22B8CF); }
.pay-method .icon-badge, .method-btn .icon-badge{ width:42px; height:42px; }
.pay-method .icon-badge svg, .method-btn .icon-badge svg{ width:22px; height:22px; }
/* см. просьбу "иконки должны вделать в квадратики и быть четкими и
   привлекающими" — реальные лого (Stars/Ozon/СБП, уже сами по себе цельные
   квадратные картинки с непрозрачным фоном, см. bot/assets/payment_method_*.
   webp) заполняют ВЕСЬ бейдж (object-fit:cover, без внутренних отступов) со
   скруглением самого бейджа, а не плавают мелкой картинкой на отдельной
   бежевой подложке, как было раньше (.icon-badge.plain — второй, "внешний"
   квадрат под ними больше не нужен для этих трёх, см. ниже). */
.pay-method .icon-badge img, .method-btn .icon-badge img{ width:100%; height:100%; object-fit:cover; display:block; border-radius:inherit; }
.pay-method .icon-badge.plain, .method-btn .icon-badge.plain{ background:transparent; overflow:hidden; box-shadow:0 0 0 1px rgba(255,255,255,.08) inset; }
.qr-box{ width:40px; height:40px; flex-shrink:0; border-radius:10px; display:flex; align-items:center; justify-content:center; color:var(--brown); text-align:center; overflow:hidden; cursor:pointer; background: var(--gold-tint);}
.qr-box svg{ width:18px; height:18px; }
.qr-box img{ width:100%; height:100%; object-fit:contain; background:#fff; }
.qr-tap-hint{ font-size:9.5px; color:var(--ink-dim); text-align:center; margin-top:4px; }

.ref-link{ font-size:11.5px; word-break:break-all; background:var(--card-soft); border:1.5px solid var(--border); border-radius:12px; padding:11px 12px; margin-bottom:10px; color:var(--ink-dim); cursor:pointer; transition: background .12s ease, border-color .2s ease, transform .15s cubic-bezier(.34,1.4,.64,1); display:flex; align-items:center; justify-content:space-between; gap:8px;}
.ref-link:active{ background: var(--border); transform:scale(.98); }
.ref-link svg{ width:14px; height:14px; flex-shrink:0; color:var(--ink-dim); }
/* Крупный вариант (реф. ссылка / ссылка на кабинет на "Рефералах") — с
   иконкой-бейджем и подписью сверху, вместо голой строки текста (по просьбе
   "сделай крупнее и удобней"). */
.ref-link.big{ padding:14px; border-radius: var(--radius-m); }
.ref-link.big b{ display:block; font-size:12.5px; font-weight:700; color:var(--ink); margin-bottom:2px; }
.ref-link.big span:not(.icon-badge){ font-size:11px; word-break:break-all; color:var(--ink-dim); }
.ref-link .copy-ic{ transition: transform .25s cubic-bezier(.34,1.56,.64,1), color .2s ease; }
.ref-link.copied{ border-color: var(--green); }
.ref-link.copied .copy-ic{ color: var(--green); transform: scale(1.35) rotate(-8deg); }

.ticket{ border-radius: var(--radius-m); background:var(--card-soft); border:1px solid var(--border); padding:12px 13px; margin-bottom:8px; font-size:12.5px; cursor:pointer; transition: transform .12s ease;}
.ticket:active{ transform:scale(.98); }
.ticket.closed{ opacity:.55; }
.ticket-top{ display:flex; justify-content:space-between; align-items:center; gap:8px; margin-bottom:3px; }
.ticket b{ font-size:13px; font-weight:700; }
.ticket .ticket-preview{ display:block; color:var(--ink-dim); font-size:11.5px; }
.ticket .ticket-when{ color:var(--ink-dim); font-size:10.5px; white-space:nowrap; }
/* Крупный вариант карточки тикета — иконка темы + чуть больше "воздуха",
   чем у базового .ticket (см. просьбу "сделай раздел поддержка более
   функциональней и удобней крупней"). Переиспользует .icon-badge/.chev,
   как и .staff-ticket-row у персонала — тот же визуальный язык. */
.ticket.ticket-lg{ display:flex; align-items:center; gap:12px; padding:13px 14px; }
.ticket.ticket-lg .icon-badge{ margin:0; }
.ticket-lg-main{ flex:1; min-width:0; }
.ticket-lg-main .ticket-preview{ margin:2px 0; }
/* Пульсирующая цветная "кнопка"-статус вместо серой точки+текста и стрелочки
   (см. просьбу "закрытый тикет должно быть пульсирующая красная кнопка
   открытый тикет пульсирующая зеленая кнопка и убери там лишние серые точки
   и стрелочки") — .ticket.closed больше не гасит всю строку (opacity), иначе
   красная "кнопка" тоже потускнеет и перестанет быть заметной. */
.ticket.ticket-lg.closed{ opacity:1; }
.ticket-status-pulse{
  flex-shrink:0; font-size:9.5px; font-weight:800; text-transform:uppercase; letter-spacing:.3px;
  padding:7px 12px; border-radius:20px; color:#fff; white-space:nowrap;
}
.ticket-status-pulse.green{ background:var(--green); animation: ticketPulseGreen 1.8s ease-out infinite; }
.ticket-status-pulse.red{ background:var(--red); animation: ticketPulseRed 1.8s ease-out infinite; }
/* см. просьбу "то же самое нужно сделать по закрытии Тикетов" — крестик
   рядом с красной "Закрыт"-кнопкой (см. hideTicketCard в app.js), только у
   уже закрытых обращений. Тот же .icon-only-btn, что и везде в кабинете —
   просто ещё один элемент в существующем flex-ряду .ticket.ticket-lg,
   отдельного position:absolute тут не нужно (в отличие от .inbox-entry). */
.ticket.ticket-lg .ticket-hide-btn{ margin-left:2px; }
/* Та же "усадка" при удалении, что и у .inbox-entry.removing (см. ниже,
   @keyframes inboxEntryRemove) — переиспользуем анимацию, а не дублируем. */
.ticket.removing{ animation: inboxEntryRemove .2s ease forwards; pointer-events:none; }
@keyframes ticketPulseGreen{
  0%{ box-shadow:0 0 0 0 rgba(111,160,87,.55); }
  70%{ box-shadow:0 0 0 9px rgba(111,160,87,0); }
  100%{ box-shadow:0 0 0 0 rgba(111,160,87,0); }
}
@keyframes ticketPulseRed{
  0%{ box-shadow:0 0 0 0 rgba(210,105,90,.55); }
  70%{ box-shadow:0 0 0 9px rgba(210,105,90,0); }
  100%{ box-shadow:0 0 0 0 rgba(210,105,90,0); }
}
.status-inline{ display:inline-flex; align-items:center; gap:5px; font-size:10.5px; font-weight:700; white-space:nowrap; flex-shrink:0; }
.status-inline::before{ content:""; width:6px; height:6px; border-radius:50%; background:currentColor; }
.status-inline.open{ color:var(--red); }
.status-inline.answered{ color:var(--gold); }
.status-inline.closed{ color:var(--ink-dim); }
.status{ font-size:9.5px; font-weight:700; text-transform:uppercase; padding:4px 8px; border-radius:20px; white-space:nowrap; display:inline-block;}
.status.open{ background: var(--red-tint); color:var(--red); }
.status.answered{ background: var(--gold-tint); color:var(--gold); }
.status.closed{ background: var(--green-tint); color:var(--green); }

.thread-msg{ border-radius: var(--radius-m); background:var(--card-soft); border:1px solid var(--border); padding:10px 12px; margin-bottom:8px; font-size:12.5px; }
.thread-msg .who{ font-weight:700; font-size:10.5px; text-transform:uppercase; margin-bottom:4px; letter-spacing:.3px; }
.thread-msg.user .who{ color:var(--red); }
.thread-msg.staff .who{ color:var(--green); }
.thread-msg .when{ color:var(--ink-dim); font-size:10px; float:right; font-weight:500; text-transform:none;}

.topic-choice{ display:flex; flex-direction:column; gap:8px; margin-bottom:10px; }
.topic-choice button.active{ background: var(--brown-dark); color:#F5EAD0; border-color: var(--brown-dark); }

.history-list{ margin-bottom:12px; }
.history-item{ display:flex; align-items:center; border-radius: var(--radius-m); background:var(--card-soft); border:1px solid var(--border); padding:11px 13px; margin-bottom:7px; font-size:12px; gap:10px;}
.history-item .hi-amount{ font-weight:800; font-size:13.5px; white-space:nowrap; font-variant-numeric:tabular-nums; flex-shrink:0; }
.history-item .hi-amount.pos{ color:var(--green); }
.history-item .hi-amount.neg{ color:var(--red); }
.history-item .hi-main{ flex:1; min-width:0; }
.history-item .hi-reason{ font-weight:700; font-size:12.5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.history-item .hi-comment{ color:var(--ink-dim); font-size:10px; margin-top:2px; }
.history-item .hi-when{ color:var(--ink-dim); font-size:10px; flex-shrink:0; white-space:nowrap; }

.footer-doc{ margin-top:4px; padding-top:12px; border-top:1px solid var(--border); font-size:10.5px; color:var(--ink-dim); }

/* ================= нижняя навигация ================= */
/* См. просьбу "сделай футер одного цвета, цвет такой же где располагаются
   кнопки, и сделай кнопки побольше премиального цвета" — .navbar и body уже
   были одним и тем же var(--brown-dark), реальный "стык двух оттенков" на
   скриншоте был между этим цветом и цветом СИСТЕМНОГО фона Telegram под
   safe-area (см. app.js: tg.setHeaderColor/setBackgroundColor/
   setBottomBarColor — раньше #30291f, теперь тот же #14110C, что и здесь).
   Тонкая золотая линия сверху — граница навбара, чтобы даже при полностью
   одинаковом фоне сохранялось лёгкое отделение от контента. */
/* Второй виток редизайна (см. профессиональный UX/UI-бриф, п.15 "Footer" —
   "не тяжёлая тёмная полоса, а floating navigation... кнопки поверх
   основного фона, аккуратная плавающая панель") — навбар больше не приклеен
   на всю ширину к нижнему краю: это компактная плавающая панель со
   скруглением и лёгким размытием фона (умеренный glassmorphism — просили
   "не превращать в стеклянные карточки", поэтому blur лёгкий и только тут,
   а не по всему приложению), приподнятая над safe-area на небольшой отступ.
   Активный пункт — просто золотой цвет иконки/подписи, без отдельного
   бокса (как и в предыдущей правке). */
/* left/right теперь тот же responsive-отступ, что и горизонтальный padding
   #app/.topbar (см. их правку выше) — раньше тут был max-width с потолком,
   который на достаточно широком окне снова превращался в "остров" посреди
   пустоты (см. разбор архитектурной причины). Без потолка панель растёт
   вместе с окном 1:1, просто с тем же аккуратным отступом от края, что и у
   карточек контента. */
.navbar{
  position:fixed; left: clamp(12px, 3vw, 32px); right: clamp(12px, 3vw, 32px);
  max-width:var(--content-max); margin:0 auto;
  bottom:calc(10px + env(safe-area-inset-bottom));
  z-index:var(--z-nav);
  display:flex; justify-content:space-around; align-items:stretch;
  background: rgba(35,30,23,.78);
  backdrop-filter: blur(18px) saturate(1.3); -webkit-backdrop-filter: blur(18px) saturate(1.3);
  border:1px solid rgba(255,255,255,.07);
  border-radius: 20px;
  box-shadow: var(--shadow-float);
  padding:6px 4px;
}
.navbar .nav-item{
  font-size:10.5px; color:#A99B7C; text-align:center; letter-spacing:.1px;
  cursor:pointer; flex:1; padding:8px 2px 7px; margin:0 1px; font-weight:600; min-height:48px;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; border-radius:14px;
  transition: color var(--dur-fast) ease, transform var(--dur-fast) var(--ease-pop), background var(--dur-fast) ease;
}
.navbar .nav-item:active{ transform:scale(.9); background:rgba(255,255,255,.06); }
.navbar .nav-item.active{ color:var(--gold); font-weight:700; }
.navbar .nav-item svg{ width:22px; height:22px; transition: transform .2s cubic-bezier(.34,1.4,.64,1), color .15s ease; }
.navbar .nav-item.active svg{ color: var(--gold); transform: scale(1.1); }
.navbar .nav-item.hidden{ display:none; }

/* ================= панель персонала: "Тикеты" ================= */
.staff-role-pill{ font-family:var(--mono); font-size:9.5px; letter-spacing:.3px; }
.staff-ticket-row{
  border-radius: var(--radius-m); padding:12px 13px; margin-bottom:8px; background:var(--card-soft);
  border:1.5px solid var(--border); display:flex; gap:12px; align-items:center; cursor:pointer;
  transition: transform .12s ease, border-color .12s ease;
}
.staff-ticket-row:active{ transform:scale(.98); }
.staff-ticket-row.closed{ opacity:.55; }
.staff-ticket-row.awaiting{ border-color: rgba(210,105,90,.4); }
.staff-ticket-row .icon-badge{ margin:0; }
.staff-ticket-row .st-main{ flex:1; min-width:0; }
.staff-ticket-row .st-main b{ display:block; font-size:13.5px; font-weight:700; margin-bottom:2px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.staff-ticket-row .st-main span{ color:var(--ink-dim); font-size:10.5px; line-height:1.4; display:block; font-weight:500; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.staff-ticket-row .st-side{ flex-shrink:0; text-align:right; display:flex; flex-direction:column; align-items:flex-end; gap:4px; }
.staff-profile-card{
  display:flex; align-items:center; gap:11px; border-radius: var(--radius-m); padding:11px 13px;
  background:var(--card-soft); border:1px solid var(--border); margin-bottom:11px;
}
.staff-profile-card .icon-badge{ margin:0; }
.staff-profile-card .sp-main{ flex:1; min-width:0; font-size:11.5px; color:var(--ink-dim); line-height:1.5; }
.staff-profile-card .sp-main b{ display:block; font-size:13.5px; color:var(--ink); font-weight:700; margin-bottom:1px; }
.staff-flags{ display:flex; gap:6px; flex-wrap:wrap; margin-top:5px; }
.staff-flag{ font-size:9.5px; font-weight:700; padding:2px 7px; border-radius:8px; background:var(--red-tint); color:var(--red); }

/* ================= «Поддержка» — справочный центр (см. loadHelp/
   renderHelpHub в app.js, database/models.py.HelpArticle) — списки хаба/
   категорий FAQ/статей переиспользуют .staff-ticket-row/.icon-badge/.st-main
   выше (та же "премиальная" карточная строка, что и у персонала), здесь —
   только специфика этого раздела: счётчик на плитке хаба, кнопки
   редактирования у администратора и типографика самой статьи. ================= */
.help-count-pill{ flex-shrink:0; font-size:11px; font-weight:800; color:var(--gold); background:rgba(201,162,92,.14); border-radius:20px; padding:3px 9px; }
.help-row-actions{ display:flex; gap:6px; flex-shrink:0; }
.icon-only-btn{
  width:30px; height:30px; border-radius:9px; flex-shrink:0; display:flex; align-items:center; justify-content:center;
  background:rgba(255,255,255,.05); border:1px solid var(--border); color:var(--ink-dim); cursor:pointer; transition: transform .12s ease;
}
.icon-only-btn:active{ transform:scale(.92); }
.icon-only-btn svg{ width:14px; height:14px; }
/* Текст статьи — длиннее, чем обычные карточки кабинета (инструкции/FAQ-
   ответы/правила бывают в несколько абзацев), поэтому чуть крупнее
   line-height, чем у .changelog-text. */
.help-article-body{ font-size:13.5px; line-height:1.65; color:var(--ink); }
.help-article-body b{ color:var(--gold); }
.help-article-body code{ background:rgba(255,255,255,.08); padding:1px 5px; border-radius:5px; font-family:var(--mono); font-size:12px; }
/* "Скачать приложения" — рабочие ссылки как блоки-кнопки, а не голый текст
   (см. просьбу "сделай рабочие ссылки как блоки кнопки"), тот же визуальный
   язык, что и .vpn-premium-link/.staff-ticket-row по всему кабинету. */
.help-app-links{ display:flex; flex-direction:column; gap:8px; margin-top:4px; }
.help-app-link-btn{
  display:flex; align-items:center; gap:10px; padding:11px 13px; border-radius: var(--radius-m);
  background:var(--card-soft); border:1px solid var(--border); text-decoration:none; color:var(--ink);
  transition: transform .12s ease, border-color .12s ease;
}
.help-app-link-btn:active{ transform:scale(.98); border-color:rgba(201,162,92,.4); }
.help-app-link-btn .icon-badge{ margin:0; flex-shrink:0; }
.help-app-link-text{ flex:1; min-width:0; font-size:12.5px; font-weight:700; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.help-app-link-btn svg.ext{ width:14px; height:14px; color:var(--ink-dim); flex-shrink:0; }

/* ================= Права доступа (см. renderPermissionsBody в app.js) =================
   .perm-card — та же .premium-gold-card поверхность, что у Подписки/«Отправить
   чек»: тёмная карточка с золотым свечением в углу. Свечение — абсолютно
   позиционированный ::before без z-index, поэтому обычный контент карточки
   рисуется НАД ним только если у него самого задан свой стек — проще всего
   поднять все прямые дети карточки разом. */
.perm-card > *{ position:relative; z-index:1; }
/* Чипы ролей — 5 штук, поэтому auto-fit вместо жёстких 2-3 колонок (иначе
   последняя строка "разъезжается" пусто); под названием роли — счётчик
   "включено/всего", чтобы не переключаться по очереди ради общей картины. */
.perm-role-chips{ grid-template-columns:repeat(auto-fit,minmax(82px,1fr)); }
.chip .chip-count{ display:block; font-size:9.5px; font-weight:600; opacity:.6; margin-top:3px; letter-spacing:.2px; }
.chip.active .chip-count{ opacity:.8; }
/* Заголовок категории-аккордеона (см. .toggle-row/.collapse-body — тот же
   приём, что у "Истории операций" на Балансе) — иконка + название + счётчик
   включённых прав + шеврон. */
.perm-cat-toggle{ margin-bottom:8px; }
.perm-cat-toggle .icon-badge{ margin:0; }
.perm-cat-label{ flex:1; min-width:0; text-align:left; }
.perm-cat-label b{ display:block; font-size:13px; font-weight:700; }
.perm-cat-label span{ display:block; font-size:10.5px; color:var(--ink-dim); font-weight:500; margin-top:1px; }
/* Строка одного права — сама больше не кликабельна целиком (клик только по
   тумблеру), поэтому без cursor:pointer/press-анимации родительского
   .staff-ticket-row. Пилюля "Активно"/"Скоро" — компактнее обычного
   .status-pill, чтобы поместиться рядом с тумблером в узком .st-side. */
.perm-row{ cursor:default; }
.perm-row:active{ transform:none; }
.perm-status-pill{ font-size:8.5px; padding:3px 8px; }

/* ================= модалка ================= */
/* Все модальные окна открываются по центру экрана (см. просьбу "все окна
   должны открываться по центру, задний фон не должен двигаться") — раньше
   это была нижняя шторка (align-items:flex-end + translateY), теперь
   центрированный диалог: align-items:center + масштаб/прозрачность вместо
   слайда снизу, скругление со всех сторон.
   См. также просьбу "окна сильно сливаются друг с другом когда накладываются" —
   раньше фон затемнялся всего на 55% без блюра, а сама модалка (var(--bg),
   тот же тон, что и страница под ней) визуально не отделялась от подложки.
   Теперь: непрозрачнее затемнение + backdrop-filter blur (страница под
   модалкой размывается — модалка читается как отдельный слой), сама
   карточка на тон светлее страницы (var(--card) вместо var(--bg)) и с
   заметно более сильной тенью/светлой окантовкой. */
.modal-overlay{
  position:fixed; inset:0; background:rgba(15,12,8,.78); display:flex; align-items:center; justify-content:center;
  z-index:50; opacity:0; pointer-events:none; transition: opacity .2s ease; padding:28px 18px;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.modal-overlay.open{ opacity:1; pointer-events:auto; }
.modal-overlay.hidden{ display:none; }
/* Обёртка — общий "жёсткий" блок для крестика-закрытия (index.html:
   .modal-x-close — сосед #modal-sheet, а не его часть, чтобы пережить
   sheet.innerHTML при перерисовке) и для scale/opacity-анимации входа: оба
   элемента должны двигаться единым целым, иначе во время короткой анимации
   крестик на миг "отклеивается" от угла карточки. */
.modal-sheet-wrap{ position:relative; width:100%; max-width:480px; transform: scale(.94); opacity:0; transition: transform .22s cubic-bezier(.22,.9,.32,1), opacity .18s ease; }
.modal-overlay.open .modal-sheet-wrap{ transform: scale(1); opacity:1; }
/* см. просьбу "Все модальные окна приведи в порядок сделай примиальными и
   удобными" — тот же тёплый градиент + золотое свечение в углу, что и у
   .premium-gold-card карточек по всему кабинету (Тариф/Обзор системы/
   Новости), только тут это САМА подложка модалки — единый визуальный язык,
   а не отдельный "плоский" стиль только для диалогов. */
/* см. просьбу "модальные окна... чуть прозрачными и премиальными с
   отступами" — фон теперь полупрозрачный (rgba вместо сплошных hex) со
   своим backdrop-filter blur: сквозь модалку слегка проглядывает
   затемнённый/размытый фон позади (тот же приём "стекла", что и у навбара
   снизу — .navbar тоже rgba+blur), а не сплошная непрозрачная плита. Плюс
   увеличены внешние (.modal-overlay) и внутренние отступы — меньше ощущения
   "впритык к краям экрана". */
.modal-sheet{
  background: linear-gradient(150deg, rgba(46,39,23,.92) 0%, rgba(42,37,29,.92) 55%, rgba(36,31,23,.92) 100%);
  -webkit-backdrop-filter: blur(22px) saturate(1.35); backdrop-filter: blur(22px) saturate(1.35);
  width:100%; max-height:min(86vh, 720px); overflow-y:auto; overflow-x:hidden; border-radius:24px;
  padding:22px 18px calc(22px + env(safe-area-inset-bottom));
  border:1px solid rgba(255,255,255,.1); position:relative;
  box-shadow: 0 24px 60px rgba(0,0,0,.65), 0 0 0 1px rgba(201,162,92,.08);
}
.modal-sheet::before{
  content:""; position:absolute; top:-70px; right:-60px; width:220px; height:220px; border-radius:50%;
  background: radial-gradient(circle, rgba(201,162,92,.14), transparent 70%);
  pointer-events:none;
}
.modal-sheet > *{ position:relative; z-index:1; }
.modal-sheet h3{
  margin:0 0 14px; padding-bottom:12px; border-bottom:1px solid rgba(255,255,255,.08);
  font-size:15.5px; font-weight:800; display:flex; align-items:center; gap:8px; flex-wrap:wrap; padding-right:32px;
}
.modal-sheet.qr-modal{ text-align:center; }
.modal-sheet.qr-modal img{ width:100%; max-width:260px; border-radius:14px; margin:0 auto 13px; display:block; background:#fff; }
/* Постоянный крестик-закрытие поверх ЛЮБОЙ модалки (см. app.js — живёт вне
   #modal-sheet, поэтому есть всегда, включая скелетоны загрузки и модалки,
   где по невнимательности забыли добавить свою кнопку "Закрыть"/"Отмена"). */
.modal-x-close{
  position:absolute; top:10px; right:10px; z-index:3; width:30px; height:30px; border-radius:50%;
  border:1px solid rgba(255,255,255,.1); background:rgba(0,0,0,.32); color:#F2EBDC;
  display:flex; align-items:center; justify-content:center; cursor:pointer;
  transition: transform .15s ease, background .15s ease;
}
.modal-x-close:active{ transform:scale(.88); background:rgba(0,0,0,.5); }
.modal-x-close svg{ width:14px; height:14px; }

.transfer-qr-big{ width:170px; height:170px; margin:4px auto 12px; border-radius: var(--radius-l); background:#fff; border:1px solid var(--border); display:flex; align-items:center; justify-content:center; overflow:hidden; cursor:pointer; }
.transfer-qr-big img{ width:100%; height:100%; object-fit:contain; }
.transfer-qr-big svg{ width:40px; height:40px; color:var(--ink-dim); }
/* см. просьбу "в пополнении Telegram Stars выровнять иконку по центру и
   сделать одинаковой по размеру с QRCode" — тот же .transfer-qr-big блок
   (170×170, центрирован), что и у QR переводов, просто своя подложка: белый
   фон нужен только сканеру QR-кода, звезде он ни к чему и смотрится тускло —
   вместо него premium тёмно-золотой градиент с мягким свечением, без
   курсора "клик" (в отличие от QR тут нечего открывать по тапу). */
.transfer-qr-big.stars-hero{
  background: radial-gradient(circle at 50% 38%, rgba(240,169,59,.22), transparent 68%), linear-gradient(155deg,#2c2313,#1c150b);
  border-color: rgba(240,169,59,.32); cursor:default;
  box-shadow: 0 0 0 1px rgba(240,169,59,.12) inset, 0 10px 28px rgba(240,169,59,.16);
}
.transfer-qr-big.stars-hero img{ width:72%; height:72%; object-fit:contain; filter: drop-shadow(0 6px 14px rgba(0,0,0,.35)); }
/* Лёгкое "появление" героя-блока при открытии модалки (по просьбе "добавь
   немного анимации") — сдержанный pop-in, не карусель и не бесконечный
   лоуп, один раз при рендере шторки. */
@keyframes heroPopIn{ 0%{ transform:scale(.86); opacity:0; } 100%{ transform:scale(1); opacity:1; } }
.hero-pop-in{ animation: heroPopIn .32s cubic-bezier(.22,.9,.32,1) both; }
.transfer-amount{ text-align:center; margin-bottom:4px; }
.transfer-amount .ta-label{ font-family:var(--mono); font-size:10px; color:var(--ink-dim); text-transform:uppercase; letter-spacing:1px; font-weight:600; }
.transfer-amount .ta-value{ font-size:26px; font-weight:800; margin-top:2px; }
.transfer-hint{ text-align:center; font-size:12px; color:var(--ink-dim); margin:6px 0 14px; }

.quote-header{ display:flex; align-items:flex-start; justify-content:space-between; gap:10px; padding:13px 15px; margin-bottom:11px; border-radius: var(--radius-m); background:var(--card-soft); border:1px solid var(--border); }
.quote-header .qh-name{ font-size:15px; font-weight:800; margin-bottom:3px; }
.quote-header .qh-meta{ font-size:11px; color:var(--ink-dim); }
.quote-header .qh-price{ font-size:15px; font-weight:800; white-space:nowrap; }

.stepper-row{ display:flex; align-items:center; justify-content:space-between; gap:10px; padding:11px 15px; margin-bottom:11px; border-radius: var(--radius-m); background:var(--card-soft); border:1px solid var(--border); }
.stepper-row .sr-label{ font-size:12.5px; font-weight:700; }
.stepper-row .sr-max{ font-size:10.5px; color:var(--ink-dim); }
.stepper{ display:flex; align-items:center; gap:12px; }
.stepper button{ width:32px; height:32px; border-radius:50%; border:1px solid var(--border); background:var(--card); font-size:15px; font-weight:700; cursor:pointer; font-family:var(--font); transition: transform .15s cubic-bezier(.34,1.4,.64,1);}
.stepper button:active{ transform:scale(.88); }
.stepper button:disabled{ opacity:.3; cursor:not-allowed; }
.stepper .count{ font-size:16px; font-weight:800; min-width:20px; text-align:center; font-variant-numeric:tabular-nums; }

/* Добавление устройств прямо в "Архиве доступа" (см. просьбу "к тарифу
   который приобрёл пользователь добавить кнопки по добавлению устройств, в
   зависимости меняется цена и можно доплатить сразу") — степпер сверху
   переиспользует .stepper-row как есть, ниже — своя строка с живой ценой. */
.archive-devices-block{ margin-top:2px; }
.archive-devices-block .stepper-row{ margin-bottom:8px; }
.adv-price-row{ display:flex; align-items:center; justify-content:space-between; gap:10px; padding:0 2px; margin-bottom:10px; font-size:12px; color:var(--ink-dim); }
.adv-price{ font-size:15px; font-weight:800; color:var(--ink); font-variant-numeric:tabular-nums; }
.adv-price.refund{ color:var(--green); }

.quote-box{ border-radius: var(--radius-m); padding:13px 15px; margin:12px 0; font-size:12.5px; line-height:1.8; background:var(--card-soft); border:1px solid var(--border); }
.quote-row{ display:flex; justify-content:space-between; }
.quote-row.total{ font-weight:800; font-size:15px; border-top:1px solid var(--border); margin-top:7px; padding-top:7px; color:var(--ink); }

.toast{ position:fixed; bottom:86px; left:50%; transform:translateX(-50%) translateY(10px); background: var(--brown-dark); color:#F5EAD0; font-family:var(--font); font-weight:600; font-size:12.5px; padding:12px 16px; border-radius:16px; z-index:100; opacity:0; pointer-events:none; transition:opacity .3s cubic-bezier(.22,.9,.32,1), transform .3s cubic-bezier(.22,.9,.32,1); max-width:88vw; text-align:left; box-shadow: var(--shadow-float); display:flex; align-items:center; gap:10px;}
.toast.show{ opacity:1; transform:translateX(-50%) translateY(0); }
.toast .toast-icon{ width:26px; height:26px; border-radius:50%; background:rgba(111,160,87,.4); color:#B9D6A6; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.toast .toast-icon svg{ width:14px; height:14px; }
.toast .toast-title{ font-weight:700; font-size:12.5px; }
.toast .toast-sub{ font-weight:500; font-size:10.5px; color:#C9BFA8; margin-top:1px; }

/* ================= переписка по тикету ================= */
/* Переписка светлее/крупнее/удобнее (см. просьбу пользователя со скрином
   диалога в "Тикетах" — тёмные, мелкие пузыри плохо читались): чужие
   сообщения — светлее card-soft (var(--card), с контрастной обводкой), свои —
   вместо почти чёрного brown-dark сплошной тёплый gold с тёмным текстом,
   как акцентный цвет остального интерфейса, а не "провал в черноту". */
.chat-msg{ display:flex; gap:10px; margin-bottom:14px; max-width:90%; }
.chat-msg.user{ margin-left:auto; flex-direction:row-reverse; }
.chat-avatar{ width:34px; height:34px; border-radius:50%; background:var(--brown-dark); color:#F1E4C4; font-size:13.5px; font-weight:700; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.chat-bubble{ border-radius: var(--radius-m); padding:11px 14px; font-size:14px; line-height:1.55; background:var(--card); border:1px solid var(--border); word-break:break-word; }
.chat-msg.user .chat-bubble{ background: var(--gold); color:var(--brown-dark); border-color:transparent; font-weight:500; }
.chat-who{ font-size:11px; font-weight:700; color:var(--ink-dim); margin-bottom:4px; }
.chat-msg.user .chat-who{ text-align:right; color:var(--gold); }
.chat-when{ font-size:10px; color:var(--ink-dim); margin-top:4px; }
.chat-msg.user .chat-when{ text-align:right; }
.chat-input-row{ display:flex; align-items:center; gap:9px; margin-top:8px; }
.chat-input-row input{ flex:1; margin-bottom:0; font-size:14px; padding:13px 15px; min-height:48px; }
.chat-send-btn{ width:48px; height:48px; border-radius:50%; border:none; background: var(--green); color:#fff; display:flex; align-items:center; justify-content:center; cursor:pointer; flex-shrink:0; transition: transform .15s cubic-bezier(.34,1.4,.64,1);}
.chat-send-btn:active{ transform:scale(.9); }
.chat-send-btn svg{ width:19px; height:19px; }
.chat-send-btn:disabled{ opacity:.4; cursor:not-allowed; }

select.field-select{
  width:100%; font-family:var(--font); font-size:14px; padding:13px; min-height:46px; border-radius: var(--radius-m);
  border:1.5px solid var(--border); background:var(--card-soft); color:var(--ink); margin-bottom:9px; appearance:none;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239C9280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m6 9 6 6 6-6'/></svg>");
  background-repeat:no-repeat; background-position:right 13px center; background-size:16px;
}
select.field-select:focus{ outline:none; border-color:var(--gold); background-color:var(--card); }
.field-label{ font-size:11.5px; font-weight:700; margin-bottom:6px; color:var(--ink); }

@media (max-width:359px){
  .grid3 .item b, .grid4 .item b{ font-size:15px; }
  .vpn-shield svg{ width:46px; height:46px; }
  /* До 6 пунктов нижней навигации одновременно у Администратора/Техподдержки
     (Главная/Подписка/Финансы/Рефералы/Пользователи/Тикеты) — на самых узких
     экранах чуть сжимаем подписи/иконки, чтобы ни одна не обрезалась и не
     перенеслась в 2 строки (см. requirements "нижняя навигация не должна
     перекрывать/обрезать контент"). */
  .navbar .nav-item{ font-size:9.5px; padding:8px 1px 7px; margin:0 1px; border-radius:12px; }
  .navbar .nav-item svg{ width:19px; height:19px; }
}

/* ================= адаптивность: планшеты / Telegram Desktop / широкие окна
   (см. requirements UX-аудита п.2 "проверить маленькие/обычные/большие
   смартфоны, планшеты, Telegram Desktop, узкие и широкие окна"). Раньше
   #app/.topbar/.navbar были жёстко ограничены max-width:480px на ЛЮБОМ экране —
   на десктопе/планшете это давало узкую колонку с гигантскими пустыми полями
   по бокам вместо использования пространства. Контент остаётся
   один-в-один тем же (без "растягивания" карточек в нелепую ширину — просто
   более щедрая, читаемая колонка), список -> карточки (а не таблицы) не
   меняется на любом размере: это уже мобильная модель, которая одинаково
   хорошо работает и на широком окне. ========= */
/* см. баг-репорт "можно прокрутить вниз до бесконечности" — внутри
   Telegram-WebView (особенно iOS) резиновый bounce-скролл за пределы контента
   визуально выглядит как бесконечная пустая область, потому что там нечего
   останавливать прокрутку. overscroll-behavior запрещает именно этот bounce
   (обычный внутренний скролл модалок/списков не затрагивает — тот работает
   через собственный overflow-y на конкретных элементах, см. .modal-sheet/
   .drawer выше). */
html, body{ overflow-x:hidden; overscroll-behavior-y:none; }
/* Раньше тут были фиксированные max-width:600/680/960px на каждой точке —
   именно это давало "скачки"/потолок при живом растягивании окна вместо
   непрерывного роста, как у Wallet (см. живой фидбег, дважды). Боковой
   padding у #app/.topbar/.navbar/.drawer теперь везде один и тот же
   clamp(12px, 3vw, 32px) прямо в базовом правиле (без потолка ширины) — он
   САМ растёт плавно с шириной окна, поэтому здесь фиксированный
   переопределяющий padding-left/right больше не нужен и не добавляется —
   остаются только вещи, для которых ширина не при чём (gap сетки, шрифт,
   число колонок в плитках). */
@media (min-width:600px){
  .record-stats-2col, .stat-grid, .shortcut-grid{ gap:12px; }
}
@media (min-width:900px){
  body{ font-size:15.5px; }
}
/* .shortcut-grid теперь сам решает число колонок через auto-fit (см. выше) —
   отдельный "включить 3-ю колонку после 1200px" уже не нужен. .dash-grid
   на широких экранах просто даёт плиткам чуть больше минимальной ширины. */
@media (min-width:1200px){
  .dash-grid{ grid-template-columns:repeat(auto-fill,minmax(104px,1fr)); }
}
/* Комфортная зона нажатия (см. requirements п.2 "минимум ~44px для основных
   интерактивных элементов") — точечные догрузки для мест, где кнопка была
   компактнее по дизайну (мелкие "small"-кнопки в шапках экранов, фильтры). */
.btn.small{ min-height:40px; }
.screen-close-btn{ width:38px; height:38px; }
/* См. живой фидбег "чуть больше" — было 40px, кнопки чуть подросли вместе с
   иконками внутри (см. .icon-folder/.icon-bell/.icon-gift выше). */
.menu-btn, .bell-btn, .gift-btn{ width:43px; height:43px; }

/* ================= «История изменений» (см. просьбу "у Всех в главном меню
   должно быть раздел История изменений... ставить лайки или дизлайки") ================= */
.changelog-entry{ margin-top:10px; }
/* Подсветка конкретной записи при переходе сюда из карточки-оповещения в
   разделе "Оповещения" (см. app.js.focusChangelogEntry/goToChangelogEntry —
   просьба "нажимаешь на оповещение из Истории изменений — попадаешь именно на
   эту запись") — короткое золотое "дыхание" рамки, само гаснет через ~2с. */
.changelog-entry.entry-highlight-pulse{ animation: entryHighlightPulse 1.1s ease-out 2; border-radius:16px; }
@keyframes entryHighlightPulse{
  0%{ box-shadow: 0 0 0 0 rgba(212,175,55,.55); }
  70%{ box-shadow: 0 0 0 10px rgba(212,175,55,0); }
  100%{ box-shadow: 0 0 0 0 rgba(212,175,55,0); }
}
.changelog-meta{ display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:8px; }
.changelog-meta-left{ display:flex; align-items:center; gap:10px; min-width:0; flex-wrap:wrap; }
.changelog-date{ font-family:var(--mono); font-size:10.5px; color:var(--ink-dim); }
/* Бейдж роли автора ("Администратор"/"Бухгалтерия") — см. просьбу "слово
   Администратор не влезает, блок не влезает": НЕ .tag (тот position:absolute,
   рассчитан на карточки БЕЗ overflow:hidden, см. .tariff-card), а обычный
   инлайн-элемент в флекс-ряду — та же расцветка (.tag.gold), но ничего не
   обрезается родительским .paper{overflow:hidden}. flex-wrap на родителе
   выше — если роль+дата всё равно не помещаются в одну строку на узком
   экране, переносится целиком, а не обрезается. */
.changelog-role-tag{
  font-size:9px; font-weight:700; letter-spacing:.3px; padding:4px 9px; border-radius:20px;
  background: var(--gold); color:#2c2109; flex-shrink:0; white-space:nowrap;
}
.changelog-text{ font-size:13.5px; line-height:1.55; white-space:pre-wrap; }
.changelog-gallery{ display:flex; gap:8px; flex-wrap:wrap; margin-top:10px; }
.changelog-img{ width:84px; height:84px; object-fit:cover; border-radius:10px; cursor:pointer; border:1px solid var(--border); }
.changelog-reactions{ display:flex; gap:8px; margin-top:12px; }
.changelog-react-btn{
  display:flex; align-items:center; gap:6px; font-family:var(--font); font-size:12.5px; font-weight:700;
  color:var(--ink-dim); background:var(--card-soft); border:1.5px solid var(--border); border-radius:12px;
  padding:8px 13px; min-height:38px; cursor:pointer; transition: transform .12s ease, background .12s ease, border-color .12s ease, color .12s ease;
}
.changelog-react-btn:active{ transform:scale(.94); }
.changelog-react-btn svg{ width:16px; height:16px; }
.changelog-react-btn.active-like{ color:var(--green); border-color:var(--green); background:var(--green-tint); }
.changelog-react-btn.active-dislike{ color:var(--red); border-color:var(--red); background:var(--red-tint); }
/* Кнопка "редактировать" на карточке записи — видна только can_manage
   (Админ/Бухгалтерия), см. openChangelogEditModal. */
.changelog-edit-btn{
  display:flex; align-items:center; justify-content:center; flex-shrink:0; width:30px; height:30px;
  border-radius:9px; background:var(--card-soft); border:1px solid var(--border); color:var(--ink-dim); cursor:pointer;
}
.changelog-edit-btn:active{ transform:scale(.92); }
.changelog-edit-btn svg{ width:14px; height:14px; }
/* Кнопка удаления записи "Новостей" (см. renderNewsEntry/wireNewsEntries) —
   та же геометрия, что и .changelog-edit-btn выше, только в красном (действие
   деструктивное, в отличие от карандаша редактирования рядом). */
.changelog-edit-btn.news-del-btn{ color:var(--red); border-color:rgba(210,105,90,.4); background:var(--red-tint); }

/* Бейдж категории записи ("Новости"/"Акции"/"Поздравления"/"Праздники") —
   см. просьбу "На главной должно появится новой блок в него будет входить
   Новости, Акции, Поздравления, Праздники" — та же геометрия, что и
   .changelog-role-tag выше, свой цвет на категорию, чтобы лента легко
   сканировалась взглядом без чтения текста. */
.news-cat-tag{
  display:inline-flex; align-items:center; gap:4px; font-size:9px; font-weight:700; letter-spacing:.3px;
  padding:4px 9px; border-radius:20px; flex-shrink:0; white-space:nowrap;
}
.news-cat-tag svg{ width:11px; height:11px; }
.news-cat-tag.news-cat-news{ background:var(--gold-tint); color:var(--gold); }
.news-cat-tag.news-cat-promo{ background:rgba(111,160,87,.18); color:var(--green); }
.news-cat-tag.news-cat-greeting{ background:rgba(201,120,180,.18); color:#D67CC2; }
.news-cat-tag.news-cat-holiday{ background:var(--red-tint); color:var(--red); }

/* Компакт-лента на Главной (см. loadHomeNewsSlot) — миниатюра/иконка слева,
   категория + обрезанный текст справа, вся строка кликабельна (открывает
   полный раздел "Новости"). См. просьбу "должны быть в блоке и красиво
   оформлены в премиум стиле" — сама карточка теперь .premium-gold-card, та
   же тёмная градиентная поверхность с золотым свечением в углу, что у
   карточки "Тариф"/"Обзор системы" (единый премиальный визуальный язык
   Главной), а не обычный плоский .paper, как раньше. */
.home-news-card{ padding-bottom:8px; }
.home-news-card > *{ position:relative; z-index:1; }
.home-news-card .screen-header{ margin-bottom:8px; }
.home-news-item{ display:flex; align-items:center; gap:11px; padding:10px 2px; cursor:pointer; border-top:1px solid var(--border); transition: opacity .12s ease, transform .12s cubic-bezier(.34,1.4,.64,1); }
.home-news-item:first-of-type{ border-top:none; padding-top:3px; }
.home-news-item:active{ opacity:.7; transform:scale(.985); }
.home-news-thumb{ width:46px; height:46px; border-radius:12px; object-fit:cover; flex-shrink:0; border:1px solid var(--border); }
.home-news-thumb-icon{ display:flex; align-items:center; justify-content:center; border-radius:12px; }
.home-news-thumb-icon svg{ width:19px; height:19px; }
/* Тонировка иконки-миниатюры цветом категории — тот же принцип, что и у
   .news-cat-tag выше, чтобы лента считывалась по цвету даже без картинки. */
.home-news-thumb-icon.news-cat-news{ background:var(--gold-tint); color:var(--gold); }
.home-news-thumb-icon.news-cat-promo{ background:rgba(111,160,87,.18); color:var(--green); }
.home-news-thumb-icon.news-cat-greeting{ background:rgba(201,120,180,.18); color:#D67CC2; }
.home-news-thumb-icon.news-cat-holiday{ background:var(--red-tint); color:var(--red); }
.home-news-item-text{ min-width:0; flex:1; }
.home-news-item-body{ font-size:12.5px; line-height:1.4; color:var(--ink); margin-top:5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.home-news-item svg.chev{ width:15px; height:15px; color:var(--ink-dim); flex-shrink:0; }

/* Мини-пикер эмодзи в форме добавления/редактирования "Новостей" (см.
   просьбу "Так же подгружать файлы и эмодзи") — просто вставляет символ в
   textarea по клику, никакой отдельной обработки на backend не требуется
   (эмодзи — обычный unicode в тексте). */
.emoji-picker{ display:flex; gap:5px; flex-wrap:wrap; margin:8px 0 4px; }
.emoji-pick-btn{
  font-size:18px; line-height:1; padding:6px 8px; border-radius:9px; background:var(--card-soft);
  border:1px solid var(--border); cursor:pointer;
}
.emoji-pick-btn:active{ transform:scale(.9); background:var(--gold-tint); }

/* Кнопка удаления записи в реестре "Базы и блокировки" (см.
   renderTrialClaimRow) — тот же размер/геометрия, что и .changelog-edit-btn
   выше, но в красном (действие деструктивное — снимает used_trial с
   пользователя). */
.trial-claim-delete-btn{
  display:flex; align-items:center; justify-content:center; flex-shrink:0; width:30px; height:30px;
  border-radius:9px; background:var(--red-tint); border:1px solid transparent; color:var(--red); cursor:pointer;
}
.trial-claim-delete-btn:active{ transform:scale(.92); }
.trial-claim-delete-btn svg{ width:14px; height:14px; }
#cl-images{ margin-bottom:4px; }
/* Миниатюры уже загруженных картинок в модалке редактирования — клик
   переключает "удалить"/"оставить" (см. openChangelogEditModal), крестик
   поверх — визуальный маркер пометки на удаление. */
.cl-edit-gallery{ display:flex; gap:8px; flex-wrap:wrap; margin:6px 0 10px; }
.cl-edit-thumb{ position:relative; width:72px; height:72px; border-radius:10px; cursor:pointer; }
.cl-edit-thumb img{ width:100%; height:100%; object-fit:cover; border-radius:10px; border:1px solid var(--border); display:block; transition: opacity .12s ease; }
.cl-edit-thumb.removed img{ opacity:.32; }
.cl-edit-thumb-x{
  position:absolute; inset:0; display:none; align-items:center; justify-content:center;
  color:var(--red); background:rgba(0,0,0,.35); border-radius:10px; font-size:22px; font-weight:800;
}
.cl-edit-thumb.removed .cl-edit-thumb-x{ display:flex; }

/* ================= «Раздел оповещения» — личный инбокс пользователя (см.
   просьбу "Все оповещения должны приходить пользователям в Раздел оповещения
   в главном меню, так же вывести всплывающие оповещения на Главной с
   возможностью закрыть блок, так же оповещение должно показываться в
   колокольчике") — карточки экрана списка, та же геометрия, что и
   .changelog-entry выше, но с иконкой-эмодзи слева вместо тега роли
   (у оповещений нет "автора", есть только тип/код). ================= */
.inbox-entry{ position:relative; margin-top:10px; display:flex; gap:12px; align-items:flex-start; transition: opacity .18s ease, transform .18s ease; }
.inbox-entry-icon{
  width:36px; height:36px; border-radius:11px; background:var(--gold-tint,rgba(212,175,55,.14));
  display:flex; align-items:center; justify-content:center; flex-shrink:0; font-size:17px;
}
.inbox-entry-body{ min-width:0; flex:1; padding-right:26px; }
.inbox-entry-title{ font-size:13.5px; font-weight:700; line-height:1.4; }
.inbox-entry-text{ font-size:12.5px; color:var(--ink-dim); line-height:1.5; margin-top:3px; white-space:pre-wrap; }
.inbox-entry-date{ font-family:var(--mono); font-size:10px; color:var(--ink-dim); margin-top:6px; }
/* см. просьбу "при нажатии попадаем в оповещения и там Новая заявка..." —
   записи о заявках/сообщениях, адресованные персоналу (см.
   INBOX_STAFF_ACTIONABLE_CODES в app.js), кликабельны и ведут на вкладку
   "Тикеты", где уже готовы действия. */
.inbox-entry.clickable{ cursor:pointer; transition: opacity .12s ease; }
.inbox-entry.clickable:active{ opacity:.6; }
/* см. просьбу "закрывать оповещения крестиком... по одному за раз" —
   крестик в углу карточки, тот же .screen-close-btn, что и на баннере
   оповещений Главной (.home-inbox-banner .screen-close-btn), просто
   компактнее (карточек в списке много, баннер — разовый элемент). Клик
   останавливает всплытие (stopPropagation в app.js), чтобы не задеть
   data-inbox-goto-staff у кликабельных карточек рядом. */
.inbox-entry .screen-close-btn{ position:absolute; top:8px; right:8px; width:26px; height:26px; border-radius:9px; z-index:2; }
.inbox-entry .screen-close-btn svg{ width:12px; height:12px; }
/* Короткая "усадка" карточки при удалении (по просьбе "добавь анимации") —
   deleteInboxEntry в app.js ставит класс, ждёт конец анимации и только потом
   убирает запись из списка/перерисовывает. */
.inbox-entry.removing{ animation: inboxEntryRemove .2s ease forwards; pointer-events:none; }
@keyframes inboxEntryRemove{ to{ opacity:0; transform: scale(.95) translateX(10px); margin-top:0; } }
/* Кнопка "Очистить всё" в заголовке раздела — см. .screen-header (уже
   space-between), тут просто прижимается вправо вторым child'ом. */
.inbox-header-actions{ flex-shrink:0; }
.inbox-header-actions .btn{ margin:0; }

/* Всплывающий баннер непрочитанных оповещений на Главной — .paper с золотым
   акцентом слева (perceptually "новое, требует внимания"), крестик — просто
   ЗАКРЫТЬ (и заодно пометить прочитанным, см. loadHomeInboxBanner), сам
   баннер не блокирует остальной контент Главной. */
.home-inbox-banner{
  position:relative; border-left:3px solid var(--gold); padding-right:38px; margin-bottom:12px;
}
.home-inbox-banner .screen-close-btn{ position:absolute; top:10px; right:10px; }
.home-inbox-banner-head{ display:flex; align-items:center; gap:8px; font-size:12.5px; font-weight:700; margin-bottom:2px; }
.home-inbox-banner-item{ margin-top:8px; }
.home-inbox-banner-item:first-child{ margin-top:10px; }
.home-inbox-banner-item .inbox-entry-title{ font-size:12.5px; }
.home-inbox-banner-item .inbox-entry-text{ font-size:12px; -webkit-line-clamp:2; display:-webkit-box; -webkit-box-orient:vertical; overflow:hidden; }
.home-inbox-banner-more{ font-size:11px; color:var(--ink-dim); margin-top:8px; }

/* см. просьбу "в блок Личное дело вывести QRCode подписки по центру в
   открытом виде" — белый квадрат нужен для контраста/сканируемости QR на
   тёмном фоне (тот же приём, что и .transfer-qr-big у QR оплаты выше). */
.identity-qr-wrap{ display:flex; justify-content:center; padding-top:4px; }
.identity-qr-img{ width:176px; height:176px; background:#fff; border-radius:var(--radius-l); padding:12px; object-fit:contain; }

/* Скрин чека оплаты переводом (см. openStaffReceiptModal, раздел Тикеты ->
   "Заявки и чеки") — в отличие от QR выше, произвольные пропорции (обычный
   скриншот с телефона), поэтому не фиксированный квадрат: ограничение по
   высоте, ширина по контейнеру модалки. */
.receipt-preview-wrap{ display:flex; justify-content:center; padding-top:4px; }
.receipt-preview-img{ max-width:100%; max-height:340px; background:#fff; border-radius:var(--radius-l); padding:6px; object-fit:contain; }
