/* ==============================================================================================
   Дизайн-система кабинета.

   Поверхности сплошные, а не полупрозрачные: полупрозрачный токен «осветляет то, на чём лежит»,
   поэтому один и тот же цвет работал и как фон панели, и как наведение поверх неё — на сплошном
   цвете так нельзя, наведение обязано брать следующую ступень (--surface-2).

   Старые имена (--border, --muted, --radius, --brand-3 …) оставлены псевдонимами: на них ссылается
   полторы тысячи строк ниже, и переименовывать их разом — значит менять всё сразу и ничего не
   проверить. Новые правила пишутся уже новыми именами.
   ============================================================================================== */
:root{
  --bg:#0E1320;            /* страница */
  --surface:#151B2B;       /* карточки, панели, сайдбар */
  --surface-2:#1C2436;     /* наведение и вложенные поверхности — на ступень СВЕТЛЕЕ карточки */
  --line:#26304a;
  --line-2:#33405f;        /* граница посильнее: наведение, фокус, подчёркивание ссылок */

  --text:#EAEFFA;
  --text-2:#98A6C4;        /* подписи, мета */
  --text-3:#7683A0;        /* приглушённое, неактивное — но читаемое: 4.5:1 на карточке */

  --brand:#3A72FF;         /* тот же синий, что в логотипе */
  --brand-hi:#5A8AFF;      /* наведение на синем — СВЕТЛЕЕ, а не темнее */
  --brand-ink:#8FB2FF;     /* синий как ТЕКСТ на тёмном */
  --brand-wash:rgba(58,114,255,.12);   /* единственная синяя подложка на весь кабинет */
  --on-brand:#FFFFFF;

  --ok:#37D39B;
  --warn:#E0A33A;          /* «оформляется», «ждёт оплаты» — раньше это было синим или тремя жёлтыми */
  --bad:#F1707A;

  --shadow:rgba(4,8,18,.5);

  /* Шкала, а не подбор на глаз. */
  --t-xs:11px; --t-s:13px; --t-m:15px; --t-l:19px; --t-xl:24px; --t-2xl:30px;
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px; --sp-6:32px; --sp-7:48px;
  --r-s:8px; --r:12px; --r-l:16px;

  --page:1180px;
  --rail:60px;             /* высота верхней полосы; от неё считаются липкие смещения */
  --sidebar-w:264px;

  --font-body:'Golos Text',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  --font-mono:'IBM Plex Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
  font-family:var(--font-body);

  /* ---- псевдонимы старых имён ---- */
  --bg-2:var(--surface-2);
  --surface-solid:var(--bg);
  --popover:var(--surface-2);
  --border:var(--line);
  --border-strong:var(--line-2);
  --muted:var(--text-2);
  --muted-2:var(--text-3);
  --brand-2:var(--brand);
  --brand-3:var(--brand-ink);
  --accent:var(--ok);
  --danger:var(--bad);
  --radius:var(--r-l);
  --radius-sm:var(--r);
  /* Заголовки набирались Space Grotesk, в котором нет кириллицы, — то есть все русские
     заголовки и так рисовались системным шрифтом. Теперь это честно один Golos Text. */
  --font-display:var(--font-body);
}

/* ================= СВЕТЛАЯ ТЕМА =================
   Раньше здесь лежало два десятка точечных переопределений — потому что цвета были зашиты
   в правила литералами. Теперь всё, что различается между темами, различается ЗДЕСЬ, а ниже
   по файлу правила знают только имена. Осталось одно исключение — логотип: его цвета заданы
   атрибутами внутри SVG, и перебить их можно только правилом. */
html[data-theme="light"]{
  /* Страница на ступень ниже карточек намеренно: при #f5f8fd белые карточки отличались от неё
     на 1.06:1, и весь экран читался одним бледным полем, разделённым только волосяными
     линиями. Шаг вниз даёт карточкам от чего оторваться, не трогая контраст текста. */
  --bg:#ECEFF6;
  --surface:#FFFFFF;
  --surface-2:#F4F6FB;
  --line:rgba(20,40,80,.12);
  --line-2:rgba(20,40,80,.22);

  --text:#0E1B30;
  --text-2:#53627D;
  --text-3:#657189;        /* светлая тема: 4.9:1 на карточке, 4.3:1 на странице */

  --brand:#1858E0;
  --brand-hi:#1149BE;      /* на светлом «ярче» — это темнее, иначе кнопка исчезает */
  --brand-ink:#1449B4;
  --brand-wash:rgba(24,88,224,.09);

  --ok:#0B7A52;
  --warn:#A9690F;
  --bad:#C01F38;

  --shadow:rgba(20,40,90,.16);
}
/* Логотип: цвета заданы атрибутами в самом SVG, поэтому только правило их и перебьёт. */
html[data-theme="light"] .otpbox-logo .logo-otp{fill:#1858e0}
html[data-theme="light"] .otpbox-logo .logo-box{fill:#0e3a8f}

*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{
  background:var(--bg);
  color:var(--text);
  font-size:var(--t-m);
  line-height:1.55;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  min-height:100vh;
}
/* Зерна и двух размытых пятен на фоне больше нет. Пятна были слоями 620×620 и 560×560 с
   filter:blur(120px): браузер пересобирал их каждый кадр — на телефоне это грелось впустую,
   а на сплошных поверхностях их всё равно не видно. */
svg{display:block}
a{color:inherit;text-decoration:none}
/* Кнопка без класса — это кнопка, а не серая коробка. Без этого сброса браузер рисует
   ей собственную рамку, фон и системный шрифт: ровно так «Отметить все» и «Снять все»
   на странице предпочтений выглядели чужими на фоне всего остального. Всё, чему нужен
   вид кнопки, задаёт его само — .btn, .icon-btn, .chip и прочие. */
button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:pointer}

/* Цифры одинаковой ширины: номер, код, сумма, таймер. Иначе строка дёргается на каждой
   секунде таймера и при каждом обновлении баланса. Класс точечный — .mono (табличные цифры
   основным шрифтом) остаётся как был, потому что моноширинный шрифт шире и вылезет из
   фиксированных ячеек таблиц. */
.num{font-family:var(--font-mono);font-variant-numeric:tabular-nums;letter-spacing:-.01em}

/* Фокус с клавиатуры виден везде и одинаково. Намеренно НЕ синий: синие обводки по кабинету
   убраны отдельным решением, и возвращать их через глобальное правило было бы отменой этого
   решения втихую. */
:focus-visible{outline:2px solid var(--line-2);outline-offset:2px;border-radius:4px}

/* ---------- brand mark ---------- */
.brand{display:flex;align-items:center;gap:11px;font-weight:600;font-size:var(--t-l);letter-spacing:-.02em;color:var(--text)}
.brand .otpbox-logo{height:32px;width:auto;display:block}
.auth-card .brand .otpbox-logo{height:38px}
.sidebar .brand{padding:6px 8px 12px}
/* user chip becomes a link to the profile; image avatars */
a.user-chip{cursor:pointer;text-decoration:none;transition:opacity .15s ease}
a.user-chip:hover{opacity:.85}
.avatar-img{object-fit:cover}
/* profile page */
.profile-top{display:flex;align-items:flex-start;gap:22px;flex-wrap:wrap}
.profile-avatar{width:88px;height:88px;border-radius:var(--r-l);overflow:hidden;flex:none;
  display:grid;place-items:center;border:1px solid var(--line);
  background:var(--brand)}
.profile-avatar img{width:100%;height:100%;object-fit:cover}
.profile-avatar-letter{font-weight:600;font-size:36px;color:var(--on-brand)}
.profile-id{display:flex;flex-direction:column;gap:12px;min-width:0}
.profile-username{font-size:var(--t-l);font-weight:600;color:var(--text)}
.profile-handle{color:var(--text-2);font-size:var(--t-s);margin-top:-6px}
.profile-stats{display:flex;flex-direction:column;gap:10px;margin-top:16px}
.profile-stat{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:14px;padding:13px 18px;border-radius:var(--r);
  background:var(--surface-2);border:1px solid var(--line)}
.profile-stat .ps-label{font-size:var(--t-xs);color:var(--text-3)}
.profile-stat .ps-value{font-size:var(--t-m);font-weight:600;color:var(--text);text-align:right;white-space:nowrap}
/* Кнопки работы с фото стоят в одну строку, подсказка о форматах — под обеими. Без этой строки
   .avatar-actions оставался обычным блоком, формы вставали одна под другой, и подсказка,
   лежавшая внутри первой из них, оказывалась зажата между двумя кнопками. */
.avatar-actions{display:flex;align-items:center;flex-wrap:wrap;gap:10px}
.avatar-form{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.avatar-file{position:absolute;width:1px;height:1px;opacity:0;overflow:hidden}
.avatar-hint{flex-basis:100%;font-size:var(--t-xs);color:var(--text-3)}

/* ---------- buttons ---------- */
/* Кнопка — плоская заливка. Градиент под наклоном 135° и подъём на пиксель при наведении
   были в кабинете на каждой кнопке; вместе с тенью это давало «объём» там, где ничего не
   выступает. Наведение теперь просто светлее. */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-2);
  font-weight:600;font-size:var(--t-s);
  padding:0 var(--sp-5);height:44px;border-radius:var(--r);cursor:pointer;border:1px solid transparent;
  transition:background .16s,border-color .16s,color .16s}
.btn svg{width:17px;height:17px;flex:none}
.btn-primary{background:var(--brand);border-color:var(--brand);color:var(--on-brand)}
.btn-primary:hover{background:var(--brand-hi);border-color:var(--brand-hi)}
.btn:disabled{background:var(--surface-2);border-color:var(--line);color:var(--text-3);cursor:not-allowed}
.btn-ghost{background:none;border-color:var(--line);color:var(--text-2)}
.btn-ghost:hover{background:var(--surface-2);border-color:var(--line-2);color:var(--text)}
.btn-lg{height:50px;padding:0 var(--sp-5);font-size:var(--t-m)}
.btn-block{width:100%}

/* ---------- forms ---------- */
.field{display:flex;flex-direction:column;gap:8px;margin-bottom:18px}
/* Shrink the caption to its text. `.field` is a column flex container, so a label stretched to
   the full field width by default — a 44px word sitting in a 220px click target. And a <label>
   forwards clicks to its input, so clicking the empty space beside the caption put the caret in
   a field the user was not aiming at, with nothing on screen suggesting that area was live. */
.field label{font-size:var(--t-s);font-weight:400;color:var(--text-2);align-self:flex-start}
.input{
  width:100%;
  background:var(--surface-2);
  border:1px solid var(--line);
  border-radius:var(--r);
  color:var(--text);
  font-family:var(--font-body);
  font-size:var(--t-m);
  padding:12px 15px;
  transition:border-color .16s;
}
.input::placeholder{color:var(--text-3)}
.input:hover{border-color:var(--line-2)}
/* Focus is shown, not advertised. The border brightens to the same neutral tone hover uses —
   enough to find the caret with the Tab key, with no blue and no halo. Removing it outright is
   not an option: `outline:none` above means this border IS the focus indicator, and there is no
   :focus-visible fallback anywhere in this file. */
.input:focus{
  outline:none;
  border-color:var(--border-strong);
}

/* ---------- alerts ---------- */
.alert{display:flex;align-items:flex-start;gap:var(--sp-3);font-size:var(--t-s);line-height:1.45;
  padding:var(--sp-3) var(--sp-4);border-radius:var(--r);margin-bottom:var(--sp-4);
  background:var(--surface-2);border:1px solid var(--line)}
.alert svg{width:18px;height:18px;flex:none;margin-top:1px}
.alert-error{color:var(--bad)}
.alert-error svg{color:var(--bad)}
.alert-success{color:var(--ok)}
.alert-success svg{color:var(--ok)}

/* ================= AUTH (login / register) ================= */
.auth-wrap{position:relative;z-index:1;min-height:100vh;display:grid;place-items:center;padding:40px 20px}
.auth-card{
  width:100%;max-width:430px;
  background:var(--surface);
  border:1px solid var(--line-2);
  border-radius:var(--r-l);
  padding:var(--sp-6) var(--sp-6);
  box-shadow:0 30px 60px -30px var(--shadow);
}
.auth-card .brand{justify-content:center;margin-bottom:8px}
.auth-head{text-align:center;margin-bottom:28px}
.auth-head h1{font-size:var(--t-xl);font-weight:600;letter-spacing:-.02em}
.auth-head p{color:var(--text-2);font-size:var(--t-s);margin-top:7px}
.auth-foot{text-align:center;margin-top:22px;font-size:var(--t-s);color:var(--text-2)}
.auth-foot a{color:var(--brand-ink);font-weight:500;transition:color .16s}
.auth-foot a:hover{color:var(--text)}

/* ================= APP SHELL ================= */
.app{position:relative;z-index:1;display:flex;min-height:100vh}

/* ---------- sidebar ---------- */
.sidebar{
  position:fixed;top:0;left:0;bottom:0;width:var(--sidebar-w);
  /* Прокрутка на случай, когда пунктов больше, чем высоты окна: у фиксированного блока её нет
     по умолчанию, и «Выйти» просто рисовалась ниже края окна — достать её было нечем. */
  overflow-y:auto;overscroll-behavior:contain;
  display:flex;flex-direction:column;
  padding:var(--sp-5) 18px;
  background:var(--surface);
  border-right:1px solid var(--line);
}
.sidebar .brand{padding:6px 8px 26px}
.nav{display:flex;flex-direction:column;gap:6px;flex:1}
.nav-bottom{display:flex;flex-direction:column;gap:6px;margin-top:auto;padding-top:6px}
.nav-label{font-size:var(--t-xs);font-weight:600;letter-spacing:.16em;
  text-transform:uppercase;color:var(--text-3);padding:6px 12px 8px}
.nav-item{
  display:flex;align-items:center;gap:var(--sp-3);
  font-size:var(--t-m);font-weight:500;
  color:var(--text-2);
  padding:11px 13px;border-radius:var(--r);
  border:1px solid transparent;
  width:100%;background:none;cursor:pointer;text-align:left;
  transition:color .16s,background .16s,border-color .16s;
}
.nav-item svg{width:20px;height:20px;flex:none}
/* Наведение обязано брать СЛЕДУЮЩУЮ ступень: сайдбар сам залит var(--surface), и наведение
   тем же токеном было бы невидимым. */
.nav-item:hover{color:var(--text);background:var(--surface-2)}
/* Активный пункт — указатель «ты здесь». Заливка и есть весь признак: рамка, градиент и
   внутренний блик поверх неё ничего не добавляли, кроме трёх лишних слоёв. */
.nav-item.active{color:var(--text);background:var(--brand-wash);border-color:transparent}
.nav-item.active svg{color:var(--brand-ink)}
.sidebar-foot{border-top:1px solid var(--line);padding-top:14px;margin-top:14px}
.logout-form{margin:0}
.nav-item.logout:hover{color:var(--bad);background:var(--surface-2)}

/* ---------- main column ---------- */
.main{flex:1;margin-left:var(--sidebar-w);display:flex;flex-direction:column;min-width:0}

/* ---------- top bar ---------- */
.topbar{
  position:sticky;top:0;z-index:30;
  display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);
  height:var(--rail);padding:0 var(--sp-5);
  background:var(--bg);
  border-bottom:1px solid var(--line);
}
.topbar-left{display:flex;align-items:center;gap:var(--sp-3);min-width:0}
.topbar-right{display:flex;align-items:center;gap:var(--sp-2)}
/* mobile-only chrome: bottom tab bar, topbar logo, extra profile-menu items.
   Hidden on desktop; revealed in the <=900px responsive block. */
.tabbar{display:none}
.mobile-brand{display:none}
.user-dd-mobile{display:none}

/* Чип баланса. Синяя капсула с иконкой карты весила больше, чем сама цифра; теперь это
   обычная поверхность, а выделяется в ней ровно то, ради чего на неё смотрят, — сумма. */
.balance-chip{
  /* Высота задана числом, а не отступами: при padding'ах она зависела от строчного интервала
     и выходила 37px против 34px у колокольчика и аватарки — вся полоса становилась выше их
     обоих, и они переставали стоять с чипом на одной линии. */
  /* По центру, а не по базовой линии: при фиксированной высоте базовая линия прижимает
     содержимое к верху чипа — подпись и сумма уезжали вверх относительно соседей. */
  display:inline-flex;align-items:center;gap:7px;height:34px;
  font-size:var(--t-s);
  padding:0 12px;border-radius:var(--r-s);
  background:var(--surface);
  border:1px solid var(--line);
  color:var(--text);
}
.balance-lbl{font-size:var(--t-xs);color:var(--text-3)}
.balance-chip .val{font-weight:600}
.balance-chip .cur{color:var(--text-3)}
a.balance-chip,button.balance-chip{cursor:pointer;transition:border-color .16s}
a.balance-chip:hover,button.balance-chip:hover{border-color:var(--line-2)}
/* Обёртка меню — сама элемент полосы, и без выравнивания она растягивалась на всю её
   высоту, а кнопка с аватаркой прижималась к верхнему краю: аватарка стояла выше
   соседей на три пикселя. */
.user-menu{position:relative;display:flex;align-items:center}
.user-chip{display:inline-flex;align-items:center;gap:9px;
  font-size:var(--t-s);color:var(--text);background:none;border:0;cursor:pointer;
  padding:0;border-radius:var(--r-s)}
/* В шапке остаётся только аватар: имя пользователя дублировало то, что и так написано на
   странице профиля, а шеврон объяснял очевидное. Узлы оставлены в разметке — на них
   ссылается перевод и подмена шапки при переходах. */
.user-chip .uname,.user-chev{display:none}
.user-dropdown{position:absolute;top:calc(100% + 8px);right:0;z-index:70;min-width:210px;
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r);padding:6px;
  box-shadow:0 20px 44px -20px var(--shadow);display:flex;flex-direction:column;gap:2px}
.user-dropdown[hidden]{display:none}
.user-dd-item{display:flex;align-items:center;gap:10px;width:100%;padding:9px 11px;border-radius:var(--r-s);
  border:0;background:none;color:var(--text-2);cursor:pointer;font-family:var(--font-body);font-size:var(--t-s);
  font-weight:400;text-align:left;transition:background .14s,color .14s}
.user-dd-item:hover{background:var(--surface-2);color:var(--text)}
.user-dd-item svg{width:16px;height:16px;color:currentColor;flex:none}
.user-dd-sep{height:1px;background:var(--line);margin:5px 4px}
.user-dd-logout-form{margin:0}
.user-dd-logout,.user-dd-logout svg{color:var(--bad)}
.avatar{width:34px;height:34px;border-radius:var(--r-s);flex:none;display:grid;place-items:center;
  font-weight:600;font-size:var(--t-s);color:var(--on-brand);background:var(--brand)}

/* ---------- topbar icon buttons: theme + language ---------- */
.icon-btn{display:inline-grid;place-items:center;width:34px;height:34px;border-radius:var(--r-s);
  border:1px solid var(--line);background:var(--surface);color:var(--text-2);cursor:pointer;
  transition:border-color .16s,color .16s}
.icon-btn:hover{border-color:var(--line-2);color:var(--text)}
.icon-btn svg{width:17px;height:17px;display:block}

/* ---------- content ---------- */
/* Контент центрируется. Раньше max-width стоял без margin:0 auto — на широком мониторе всё
   было прижато к левому краю, а справа оставалось пустое поле в половину экрана. */
.content{padding:var(--sp-6) var(--sp-5) 90px;max-width:var(--page);width:100%;margin:0 auto}
.page-head{margin-bottom:var(--sp-5)}
.page-head h1{font-size:var(--t-2xl);font-weight:600;letter-spacing:-.02em;line-height:1.2}
.page-head p{color:var(--text-2);font-size:var(--t-s);margin-top:var(--sp-1)}

/* ---------- generic panel/card ---------- */
.panel{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-l);padding:var(--sp-5)}
.panel + .panel{margin-top:var(--sp-3)}
.panel-head{display:flex;align-items:flex-start;gap:var(--sp-3);margin-bottom:6px}
.panel-head h2{font-size:var(--t-l);font-weight:600;letter-spacing:-.02em}
.panel-sub{color:var(--text-2);font-size:var(--t-s);margin-bottom:var(--sp-4)}

/* ---------- balance card ---------- */
.balance-card{
  position:relative;overflow:hidden;
  border:1px solid var(--line);border-radius:var(--r-l);padding:var(--sp-5);
  background:var(--surface);
}
.balance-card .label{font-size:var(--t-xs);color:var(--text-3)}
.balance-card .amount{font-size:var(--t-2xl);font-weight:600;letter-spacing:-.02em;line-height:1.1;margin:6px 0 4px}
.balance-card .amount small{font-size:var(--t-l);font-weight:500;color:var(--text-3);margin-left:4px}
.balance-card .hint{color:var(--text-2);font-size:var(--t-s);margin-bottom:var(--sp-4)}
.balance-card .actions{display:flex;gap:var(--sp-2);flex-wrap:wrap}

/* ---------- empty state ---------- */
/* Синей плитки с иконкой здесь больше нет: пустой экран объясняется словами, а плитка
   только делала пустоту нарядной. */
.empty{text-align:center;padding:var(--sp-7) var(--sp-4)}
.empty .empty-mascot{display:block;margin:0 auto 12px;width:clamp(104px,32vw,140px);height:auto}
.empty h3{font-size:var(--t-l);font-weight:600;letter-spacing:-.02em}
.empty p{color:var(--text-2);font-size:var(--t-s);margin:var(--sp-2) auto var(--sp-4);max-width:46ch}
/* small hint line under an empty-state title (e.g. "SMS appears here automatically") */
.empty .empty-sub{color:var(--text-3);font-size:var(--t-s);margin:6px auto 0;max-width:280px}
/* breathing room when the CTA follows the title directly (no descriptive paragraph) */
.empty h3 + .btn{margin-top:20px}

.form-grid{max-width:480px}
.form-actions{margin-top:6px}

/* ---------- bottom tab bar (mobile) ---------- */
/* Колонки не пересчитываются вручную: grid-auto-columns:1fr раскладывает столько вкладок,
   сколько их есть в разметке. Раньше здесь стояло repeat(5,1fr), и удаление одного пункта
   меню оставляло пустую колонку. */
.tabbar{
  grid-auto-flow:column;grid-auto-columns:1fr;align-items:stretch;
  position:fixed;left:0;right:0;bottom:0;z-index:45;
  padding:0 0 env(safe-area-inset-bottom);
  background:var(--bg);
  border-top:1px solid var(--line);
}
.tab-item{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
  padding:9px 4px 8px;color:var(--text-3);min-height:50px;text-align:center;
  font-weight:500;font-size:10.5px;
  transition:color .16s}
.tab-item svg{width:20px;height:20px;flex:none}
.tab-item span{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tab-item.active{color:var(--brand-ink)}
.tab-item.active svg{color:var(--brand-ink)}

/* ================= RESPONSIVE ================= */
@media(max-width:900px){
  .sidebar{display:none}
  .main{margin-left:0}
  .tabbar{display:grid}
  .mobile-brand{display:flex;align-items:center}
  .mobile-brand .otpbox-logo{height:27px;width:auto;display:block}
  .user-dd-mobile{display:flex}
  /* notifications panel: pin BOTH edges so it always stays on-screen. Pinning left+right
     (instead of right+width) is bulletproof regardless of the containing block. Higher specificity
     (`.notif-menu .notif-dropdown` = 0,2,0) beats the base `.notif-dropdown` (0,1,0) which
     lives later in this file (media queries don't add specificity). */
  .notif-menu .notif-dropdown{position:fixed;top:calc(var(--rail) + 6px);left:8px;right:8px;width:auto;max-width:none}
  .topbar{padding:0 var(--sp-4)}
  .content{padding:var(--sp-5) var(--sp-4) calc(72px + env(safe-area-inset-bottom))}
  .balance-lbl{display:none}
  .balance-card{padding:var(--sp-4)}
}
@media(max-width:560px){
  .topbar-right{gap:6px}
  .balance-chip{padding:6px 10px}
  .balance-card .actions .btn{width:100%}
  .auth-card{padding:30px 22px}
}
@media(max-width:400px){
  .topbar{padding:0 12px}
  .topbar-right{gap:7px}
  .mobile-brand .otpbox-logo{height:24px}
  .balance-chip{padding:6px 10px}
  .tab-item{font-size:10px;gap:3px}
  .tab-item svg{width:22px;height:22px}
}

/* ---------- data table ---------- */
.table-wrap{overflow-x:auto;border:1px solid var(--line);border-radius:var(--r);background:var(--surface)}
.data-table{width:100%;border-collapse:collapse;font-size:var(--t-s);min-width:560px}
.data-table thead th{
  font-size:var(--t-xs);font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  color:var(--text-3);text-align:left;padding:13px 16px;border-bottom:1px solid var(--line)
}
.data-table tbody td{padding:14px 16px;border-bottom:1px solid var(--border);color:var(--text);vertical-align:middle}
.data-table tbody tr:last-child td{border-bottom:none}
.data-table tbody tr:hover{background:var(--surface-2)}
.data-table .mono{font-variant-numeric:tabular-nums;font-feature-settings:"tnum"}
.data-table .muted-cell{color:var(--muted)}

/* ---------- badges ---------- */
/* Status, as a word in a colour. It used to be a tinted, bordered capsule — a lot of chrome
   around one word whose colour was already saying the same thing twice. */
.badge{display:inline-flex;align-items:center;font-size:var(--t-s);
  font-weight:600;color:var(--text-2)}
.badge-ok{color:var(--ok)}
/* A rental that ran its full term and ended is not a failure. Red is this product's colour for
   things going wrong — spend it there, or it stops meaning anything when it matters. */
.badge-finished{color:var(--text-3)}
.badge-muted{color:var(--text-3)}
.badge-soon{color:var(--warn)}

/* ---------- "soon" card ---------- */
.soon-card{border:1px dashed var(--line-2);border-radius:var(--r);
  background:var(--surface-2);padding:18px 20px}
.soon-row{display:flex;align-items:center;gap:16px}
.soon-icon{width:40px;height:40px;border-radius:var(--r-s);flex:none;display:grid;place-items:center;color:var(--brand-ink);
  background:var(--brand-wash)}
.soon-icon svg{width:21px;height:21px}
.soon-text{display:flex;flex-direction:column;gap:3px;flex:1;min-width:0}
.soon-text strong{font-weight:600;font-size:var(--t-m)}
.soon-text span{color:var(--text-2);font-size:var(--t-s);line-height:1.45}

/* ---------- catalog (rent) ---------- */
.search-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:18px 0 22px;position:relative}
.search-bar .search-ico{position:absolute;left:14px;top:50%;transform:translateY(-50%);color:var(--muted-2);pointer-events:none}
.search-bar .search-ico svg{width:18px;height:18px}
.search-input{flex:1;min-width:200px;padding-left:42px}
.search-bar .btn{flex:none}
.catalog-meta{font-size:var(--t-s);color:var(--text-3);margin-bottom:14px}
.catalog-hint{color:var(--text-3)}
.updating-dots{display:flex;gap:7px;justify-content:center;margin-top:16px}
.updating-dots span{width:6px;height:6px;border-radius:50%;background:var(--text-3);opacity:.4;
  animation:updating-dot 1.1s ease-in-out infinite}
.updating-dots span:nth-child(2){animation-delay:.18s}
.updating-dots span:nth-child(3){animation-delay:.36s}
@keyframes updating-dot{0%,100%{opacity:.3;transform:translateY(0)}50%{opacity:1;transform:translateY(-4px)}}
.catalog-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:var(--sp-3);transition:opacity .15s ease}
[data-rent-results].is-loading{opacity:.5;pointer-events:none;transition:opacity .15s ease}
/* whole card is clickable now (no per-card button) */
a.service-card{display:flex;align-items:center;gap:var(--sp-3);padding:12px var(--sp-4);border:1px solid var(--line);
  border-radius:var(--r);background:var(--surface);cursor:pointer;
  transition:border-color .16s,background .16s}
/* Карточка не всплывает и не подсвечивается синим: она никуда не двигается, когда на неё
   наводят мышь, — меняется только граница и фон. */
a.service-card:hover{border-color:var(--line-2);background:var(--surface-2)}
/* flat, non-glowing icon background (the bright gradient + halo blinded the eyes) */
.service-mark{width:34px;height:34px;border-radius:var(--r-s);flex:none;display:grid;place-items:center;overflow:hidden;
  font-weight:600;font-size:var(--t-s);color:var(--on-brand);
  background:var(--brand)}
.service-mark img{width:22px;height:22px;display:block;object-fit:contain}
.service-name{font-weight:500;font-size:var(--t-m);flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.service-go{flex:none;display:grid;place-items:center;color:var(--text-3);transition:color .16s}
.service-go svg{width:16px;height:16px}
a.service-card:hover .service-go{color:var(--text-2)}
.btn[disabled],.btn.is-disabled{opacity:.5;cursor:not-allowed;pointer-events:none;transform:none;box-shadow:none}

/* ---------- pager ---------- */
.pager{display:flex;align-items:center;justify-content:center;gap:14px;margin-top:24px}
.pager-info{font-size:var(--t-s);color:var(--text-2)}
.catalog-note{display:flex;align-items:flex-start;gap:9px;margin-top:20px;color:var(--text-3);font-size:var(--t-s);line-height:1.5}
.catalog-note svg{width:17px;height:17px;flex:none;margin-top:1px;color:var(--text-3)}

/* ---------- profile / security ---------- */
.profile-meta{color:var(--text-3);font-size:var(--t-s);margin:2px 0 4px}
.panel-head .tag{margin-left:auto;flex:none;font-size:var(--t-s);font-weight:600}
.tag-on{color:var(--ok)}
.tag-off{color:var(--text-3)}
.input-code{font-family:var(--font-mono);letter-spacing:.22em;font-size:var(--t-xl);text-align:center;font-weight:500}

.btn-danger{background:none;border-color:var(--line);color:var(--bad)}
.btn-danger:hover{background:var(--surface-2);border-color:var(--bad);color:var(--bad)}

/* numbered guide */
.totp-guide{list-style:none;counter-reset:step;display:flex;flex-direction:column;gap:14px;margin:6px 0 22px}
.totp-guide li{counter-increment:step;position:relative;padding-left:38px;min-height:24px}
.totp-guide li::before{content:counter(step);position:absolute;left:2px;top:1px;width:24px;height:24px;border-radius:50%;
  display:grid;place-items:center;font-size:var(--t-xs);color:var(--text-2);
  background:var(--surface-2);border:1px solid var(--line)}
.totp-guide li b{display:block;font-weight:500;font-size:var(--t-m)}
.totp-guide li span{display:block;color:var(--text-2);font-size:var(--t-s);line-height:1.45;margin-top:2px}

/* setup wizard */
.totp-setup{border-top:1px solid var(--border);padding-top:22px;margin-top:6px}
.totp-pair{display:flex;gap:24px;flex-wrap:wrap;align-items:center;margin-bottom:22px}
.totp-qr{background:#fff;padding:12px;border-radius:var(--r);flex:none;line-height:0}
.totp-qr img{width:172px;height:172px;display:block}
.totp-key{flex:1;min-width:220px}
.totp-key label{font-size:var(--t-s);color:var(--text-2)}
.totp-secret{display:block;margin:8px 0;font-family:var(--font-mono);font-size:var(--t-m);letter-spacing:.06em;
  word-break:break-all;color:var(--text);background:var(--surface-2);border:1px solid var(--line);
  border-radius:var(--r-s);padding:var(--sp-2) var(--sp-3)}
.totp-hint{color:var(--text-3);font-size:var(--t-xs);line-height:1.45}
.totp-enable-form{max-width:320px}
.totp-enable-form .form-actions{display:flex;gap:10px}

/* enabled status */
.totp-status{border-top:1px solid var(--border);padding-top:20px;margin-top:6px;display:flex;
  justify-content:space-between;align-items:center;gap:18px;flex-wrap:wrap}
.totp-status-row{display:flex;align-items:center;gap:13px}
.totp-status-row svg{width:24px;height:24px;color:var(--ok);flex:none}
.totp-status-row strong{display:block;font-weight:500;font-size:var(--t-m)}
.totp-status-row span{color:var(--text-2);font-size:var(--t-s)}
.totp-actions{display:flex;gap:10px;flex-wrap:wrap}

/* backup codes */
.backup-block{border:1px solid var(--line);background:var(--surface-2);
  border-radius:var(--r);padding:20px;margin:6px 0 22px}
.backup-head{display:flex;align-items:center;gap:9px;color:var(--ok);font-size:var(--t-m);font-weight:600}
.backup-head svg{width:19px;height:19px;flex:none}
.backup-note{color:var(--text-2);font-size:var(--t-s);line-height:1.5;margin:8px 0 16px}
.backup-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px;margin-bottom:16px}
.backup-code{font-family:var(--font-mono);font-size:var(--t-m);letter-spacing:.06em;text-align:center;
  color:var(--text);background:var(--surface);border:1px solid var(--line);border-radius:var(--r-s);padding:10px}

/* ---------- tariffs (rent detail) ---------- */
.back-link{display:inline-flex;align-items:center;gap:5px;font-size:var(--t-s);
  color:var(--text-3);margin-bottom:14px;transition:color .16s}
.back-link:hover{color:var(--text)}
.back-link svg{width:16px;height:16px}
.rent-paynote{display:flex;align-items:center;gap:10px;color:var(--text-2);font-size:var(--t-s);line-height:1.45;
  padding:12px var(--sp-4);border:1px solid var(--line);background:var(--surface-2);
  border-radius:var(--r);margin-bottom:20px}
.rent-paynote svg{width:18px;height:18px;flex:none;color:var(--text-3)}
.tariff-list{display:flex;flex-direction:column;gap:10px}
.tariff-row{display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:12px var(--sp-4);border:1px solid var(--line);border-radius:var(--r);background:var(--surface);
  transition:border-color .16s,background .16s}
.tariff-row:hover{border-color:var(--line-2);background:var(--surface-2)}
.tariff-info{display:flex;flex-direction:column;gap:3px;min-width:0;flex:1 1 auto;overflow:hidden}
.tariff-country{font-weight:500;font-size:var(--t-m)}
.tariff-duration{color:var(--text-2);font-size:var(--t-s)}
.tariff-buy{display:flex;align-items:center;gap:14px;flex:none}
.tariff-price{font-family:var(--font-mono);font-weight:500;font-size:var(--t-l);color:var(--text);white-space:nowrap;font-variant-numeric:tabular-nums}
.tariff-term{font-weight:500;font-size:var(--t-m)}
.tariff-sub{color:var(--text-3);font-size:var(--t-xs);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ---------- country accordion (rent purchase) ---------- */
.country-accordion{display:flex;flex-direction:column;gap:var(--sp-2);max-width:640px}
.country-spoiler{border:1px solid var(--line);border-radius:var(--r);background:var(--surface);overflow:hidden;
  transition:border-color .16s}
.country-spoiler:hover{border-color:var(--line-2)}
/* Open already reads three ways: the body expands, the chevron turns, and the disc fills with a
   check. The blue frame around the whole card was a fourth. */
.country-spoiler.is-open{border-color:var(--line-2)}
.country-head{display:flex;align-items:center;gap:var(--sp-3);width:100%;padding:12px var(--sp-4);background:none;border:0;
  cursor:pointer;text-align:left;font-family:inherit;color:var(--text);transition:background .16s}
.country-head:hover{background:var(--surface-2)}
/* radio-style picker: empty ring -> filled disc with a check when its country is open */
.country-pick{flex:none;width:20px;height:20px;border-radius:50%;border:1.5px solid var(--line-2);
  background:none;display:grid;place-items:center;
  transition:border-color .16s,background .16s}
/* The filled disc with its white check is the affordance and stays; the 4px bloom around it was
   not telling anyone anything. */
.country-spoiler.is-open .country-pick{border-color:var(--brand);background:var(--brand)}
.country-check{width:11px;height:11px;color:var(--on-brand);opacity:0;transform:scale(.4);
  transition:opacity .2s ease,transform .2s ease}
.country-spoiler.is-open .country-check{opacity:1;transform:none}
.country-flag{width:27px;height:20px;border-radius:4px;object-fit:cover;flex:none;box-shadow:0 0 0 1px rgba(0,0,0,.16)}
.country-flag.flag-missing{display:none}
.country-name{font-weight:500;font-size:var(--t-m);flex:1;min-width:0}
.country-from{color:var(--text-3);font-size:var(--t-s);white-space:nowrap}
.country-from b{color:inherit;font-weight:500;font-family:var(--font-mono)}
.country-chevron{width:16px;height:16px;color:var(--text-3);flex:none;transition:transform .25s ease,color .16s}
.country-spoiler.is-open .country-chevron{transform:rotate(90deg)}
/* smooth height: collapse via grid-template-rows 0fr -> 1fr (no fixed max-height guess) */
.country-body{display:grid;grid-template-rows:0fr;transition:grid-template-rows .28s ease}
.country-spoiler.is-open .country-body{grid-template-rows:1fr}
.country-body-inner{overflow:hidden;min-height:0}
.country-body .tariff-list{padding:var(--sp-2);gap:0;border-top:1px solid var(--line)}
.country-body .tariff-row{background:none;border-color:transparent;border-radius:var(--r-s)}
.country-body .tariff-row:hover{background:var(--surface-2);border-color:transparent}

/* ---------- top-up (balance) ---------- */
.topup-form{max-width:560px}
/* Ссылка внутри текста. Класс уже стоял в разметке (ссылка на бота на странице безопасности),
   а правила у него не было — выглядела она как обычный текст. */
.link{color:var(--brand-ink);font-weight:500}
.link:hover{text-decoration:underline}

/* ---------- «Счёт создан»: транзитный экран без скрипта ---------- */
.topup-created{max-width:460px}
/* Сумма читается как чек: крупное число, рядом — способ оплаты. Тем же приёмом набран ключ
   оповещений на странице безопасности, поэтому экран не выглядит пришлым. */
.topup-created-sum{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:18px}
.topup-created-sum .num{font-size:30px;font-weight:600;letter-spacing:-.01em}
.topup-created-method{color:var(--text-2);font-size:var(--t-s)}
.topup-created-note{margin-top:14px}
.topup-created-back{display:inline-block;margin-top:14px;font-size:var(--t-s)}
.method-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px}
.method-option{position:relative;display:block;cursor:pointer}
.method-option input{position:absolute;opacity:0;width:0;height:0}
.method-card{display:flex;align-items:center;gap:11px;padding:var(--sp-4);border:1px solid var(--line);
  border-radius:var(--r);background:var(--surface-2);transition:border-color .16s}
.method-option:hover .method-card{border-color:var(--line-2)}
/* The check badge scales in on selection; the border and tint say it twice more. Keeping the
   border, dropping the third. */
.method-option input:checked + .method-card{border-color:var(--brand)}
.method-ico{font-size:22px;line-height:1;flex:none}
.method-logo{width:30px;height:30px;border-radius:50%;flex:none;display:block;object-fit:cover;
  border:1px solid var(--line)}
.method-option input:checked + .method-card .method-logo{border-color:rgba(122,163,255,.5)}
.balance-inline{color:var(--text);font-weight:600;font-family:var(--font-mono)}
.method-title{font-weight:600;font-size:var(--t-m);flex:1;min-width:0}
.method-check{width:20px;height:20px;border-radius:50%;flex:none;display:grid;place-items:center;color:var(--on-brand);
  background:var(--brand);opacity:0;transform:scale(.6);transition:opacity .18s ease,transform .18s ease}
.method-check svg{width:13px;height:13px}
.method-option input:checked + .method-card .method-check{opacity:1;transform:scale(1)}
.topup-error{color:var(--bad);font-size:var(--t-xs);min-height:17px;margin:-4px 0 14px}
.topup-chips{display:flex;gap:8px;flex-wrap:wrap;margin:-6px 0 20px}
.chip{font-weight:500;font-size:var(--t-s);color:var(--text-2);cursor:pointer;
  padding:9px 13px;border-radius:var(--r-s);border:1px solid var(--line);background:var(--surface-2);
  transition:border-color .16s,color .16s}
.chip:hover{border-color:var(--line-2);color:var(--text)}
.chip.is-on{border-color:var(--brand);color:var(--text)}
.topup-history{display:flex;flex-direction:column;gap:9px}
.thist-row{display:flex;align-items:center;gap:var(--sp-3);padding:12px var(--sp-4);border:1px solid var(--line);
  border-radius:var(--r);background:var(--surface);transition:border-color .16s,background .16s}
.thist-row:hover{border-color:var(--line-2);background:var(--surface-2)}
.thist-logo{flex:none}
.thist-logo img{width:36px;height:36px;border-radius:50%;display:block;object-fit:cover;
  border:1px solid var(--line)}
.thist-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.thist-amount{font-family:var(--font-mono);font-weight:600;font-size:var(--t-m)}
.thist-amount.is-up{color:var(--ok)}
/* Трата — это не ошибка. Красным в кабинете помечается то, что пошло не так; если красить
   им каждую покупку, цвет перестаёт что-либо значить там, где он действительно нужен. */
.thist-amount.is-down{color:var(--text)}
.thist-balance{font-family:var(--font-mono);font-size:var(--t-s);color:var(--text-3)}
/* Плитка на наведённой строке переключается на цвет карточки, иначе она в ней растворяется. */
.thist-row:hover .op-ico{background:var(--surface)}
.thist-meta{color:var(--text-2);font-size:var(--t-s);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.thist-side{display:flex;align-items:center;gap:10px;flex:none}
/* operations: external invoice id chip + copy button, and the unpaid-invoice row */
.thist-ext{display:flex;align-items:center;gap:6px;margin-top:2px;min-width:0}
.thist-ext-label{color:var(--text-3);letter-spacing:.04em;font-size:var(--t-xs);flex:none}
.thist-ext code{font-family:var(--font-mono);font-size:var(--t-xs);
  color:var(--text-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:190px}
.thist-copy{flex:none;display:grid;place-items:center;width:22px;height:22px;padding:0;border:1px solid var(--line);
  border-radius:6px;background:transparent;color:var(--text-3);cursor:pointer;
  transition:color .16s,border-color .16s,background .16s}
.thist-copy svg{width:12px;height:12px}
.thist-copy:hover{color:var(--text);border-color:var(--line-2);background:var(--surface-2)}
.thist-copy.copied{color:var(--ok);border-color:var(--ok)}
.thist-row.is-unpaid{border-color:var(--warn);background:var(--surface-2)}
/* Внутри панели это список, а не стопка карточек: своя рамка у каждой строки внутри уже
   обведённой панели давала рамку в рамке. Снаружи панели (если список окажется там)
   строка остаётся карточкой — правило действует только на вложенный случай. */
.topup-pending .topup-history,.panel .topup-history{gap:0}
.topup-pending .topup-history .thist-row,
.panel .topup-history .thist-row{border-color:transparent;border-radius:var(--r-s);background:none}
.topup-pending .topup-history .thist-row + .thist-row,
.panel .topup-history .thist-row + .thist-row{border-top-color:var(--line);border-radius:0}
.topup-pending .topup-history .thist-row:hover,
.panel .topup-history .thist-row:hover{border-color:transparent;background:var(--surface-2)}
.topup-pending .topup-history .thist-row + .thist-row:hover,
.panel .topup-history .thist-row + .thist-row:hover{border-top-color:var(--line)}
/* Неоплаченный счёт остаётся выделенным, но полосой слева, а не рамкой по кругу. */
.topup-pending .topup-history .thist-row.is-unpaid,
.panel .topup-history .thist-row.is-unpaid{border-left:3px solid var(--warn);background:var(--surface-2)}
/* topbar balance chip bump when a top-up lands */
/* Неоплаченные счета внутри панели пополнения. Это контекст для решения, а не само решение:
   заголовок мельче панельного, подпись тихая, а черта снизу отделяет их от формы — иначе список
   читался бы как часть формы и спорил с кнопкой «Перейти к оплате» за главное действие. */
.topup-pending{margin-bottom:var(--sp-4);padding-bottom:var(--sp-4);border-bottom:1px solid var(--line)}
.topup-pending-head{display:flex;flex-direction:column;gap:2px;margin-bottom:10px}
.topup-pending-head h3{font-size:var(--t-m);font-weight:600;letter-spacing:-.01em}
.topup-pending-head p{color:var(--text-2);font-size:var(--t-s)}
/* Счёт живёт сутки, а потолок отпускает через час — значит висящих счетов бывает больше трёх, и
   все они стояли ПЕРЕД формой пополнения. Список выше трёх строк прокручивается внутри себя:
   форма остаётся на экране, а счета никуда не пропадают. */
.topup-pending .topup-history{max-height:236px;overflow-y:auto;overscroll-behavior:contain}
/* В истории операций блок — вся панель целиком, и вторая черта там лишняя. Метка на панели, а
   не «прямой потомок»: на «Балансе» список тоже прямой потомок панели, и правило по потомку
   съедало черту, которой он отделён от формы пополнения. */
.panel-pending-only > .topup-pending{margin-bottom:0;padding-bottom:0;border-bottom:0}
/* Ключ 2FA и кнопка «скопировать» — в одну строку: ключ занимает всё место, кнопка не жмётся. */
.totp-secret-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.totp-secret-row .totp-secret{flex:1;min-width:180px}
/* Счётчик непрочитанных в меню: тот же смысл, что у точки на колокольчике, но в строке
   пункта — поэтому цифра прижата к правому краю и не растягивает пункт. */
.nav-badge{margin-left:auto;min-width:20px;height:20px;padding:0 6px;display:inline-flex;
  align-items:center;justify-content:center;border-radius:999px;background:var(--brand);
  color:#fff;font-size:var(--t-xs);font-weight:600;font-variant-numeric:tabular-nums}
.nav-badge[hidden]{display:none}
.balance-chip.bumped{animation:chip-bump .6s ease}
@keyframes chip-bump{0%{transform:none}25%{transform:scale(1.1);border-color:var(--ok)}100%{transform:none}}

/* ---------- top navigation loading bar (pjax) ---------- */
.nav-progress{position:fixed;top:0;left:0;right:0;height:3px;z-index:9999;pointer-events:none;
  opacity:0;transition:opacity .25s ease}
.nav-progress.on{opacity:1}
.nav-progress-fill{display:block;height:100%;width:0;border-radius:0 3px 3px 0;
  background:var(--brand);
  transition:width .18s ease}

/* ---------- ban page ---------- */
.ban-wrap{min-height:100vh;display:grid;place-items:center;padding:24px;position:relative;z-index:1}
.ban-card{width:100%;max-width:440px;text-align:center;background:var(--surface);border:1px solid var(--line-2);
  border-radius:var(--r-l);padding:var(--sp-6) var(--sp-5);box-shadow:0 30px 60px -30px var(--shadow)}
.ban-icon{width:56px;height:56px;border-radius:var(--r);margin:0 auto 22px;display:grid;place-items:center;
  color:var(--bad);background:var(--surface-2);border:1px solid var(--line)}
.ban-icon svg{width:26px;height:26px}
.ban-card h1{font-size:var(--t-xl);font-weight:600;letter-spacing:-.02em;margin-bottom:8px}
.ban-sub{color:var(--text-2);font-size:var(--t-s);margin-bottom:22px}
.ban-reason{display:flex;flex-direction:column;gap:5px;text-align:left;background:var(--surface-2);
  border:1px solid var(--line);border-radius:var(--r);padding:14px var(--sp-4);margin-bottom:20px}
.ban-reason-label{font-size:var(--t-xs);color:var(--text-3)}
.ban-reason-text{font-size:var(--t-s);color:var(--text);line-height:1.5;word-break:break-word}
.ban-note{color:var(--text-3);font-size:var(--t-s);margin-bottom:22px}

/* ---------- notifications bell ---------- */
.notif-menu{position:relative}
.notif-bell{position:relative;overflow:visible}
.notif-badge{position:absolute;top:-5px;right:-5px;min-width:18px;height:18px;padding:0 5px;border-radius:999px;
  background:var(--bad);color:var(--on-brand);font-size:var(--t-xs);font-weight:600;line-height:18px;
  text-align:center;box-shadow:0 0 0 2px var(--bg)}
.notif-dropdown{position:absolute;top:calc(100% + 8px);right:0;z-index:70;width:340px;max-width:calc(100vw - 32px);
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r);padding:6px;
  box-shadow:0 20px 44px -20px var(--shadow)}
.notif-dropdown[hidden]{display:none}
.notif-head{display:flex;align-items:center;justify-content:space-between;padding:8px 10px 10px;
  font-weight:600;font-size:var(--t-s);color:var(--text)}
.notif-head-link{color:var(--text);text-decoration:none}
.notif-head-link:hover{text-decoration:underline}
.notif-readall{background:none;border:0;color:var(--text-2);font-size:var(--t-xs);cursor:pointer;font-family:var(--font-body)}
.notif-readall:hover{text-decoration:underline}
.notif-list{display:flex;flex-direction:column;gap:2px;max-height:min(60vh,420px);overflow-y:auto}
/* custom thin themed scrollbars (kept visible, just styled) */
.notif-list,.chat-thread{scrollbar-width:thin;scrollbar-color:var(--border-strong) transparent}
.notif-list::-webkit-scrollbar,.chat-thread::-webkit-scrollbar{width:10px}
.notif-list::-webkit-scrollbar-track,.chat-thread::-webkit-scrollbar-track{background:transparent}
.notif-list::-webkit-scrollbar-thumb,.chat-thread::-webkit-scrollbar-thumb{background:var(--border-strong);
  border-radius:999px;border:3px solid transparent;background-clip:padding-box}
.notif-list::-webkit-scrollbar-thumb:hover,.chat-thread::-webkit-scrollbar-thumb:hover{background:var(--muted-2);
  background-clip:padding-box;border:3px solid transparent}
.notif-empty{padding:24px 12px;text-align:center;color:var(--text-3);font-size:var(--t-s)}
.notif-item{display:flex;gap:11px;padding:10px 11px;border-radius:var(--r-s);cursor:pointer;text-align:left;
  transition:background .15s ease}
.notif-item:hover{background:var(--surface-2)}
.notif-item.is-static{cursor:default}
.notif-item.is-static:hover{background:transparent}
.notif-item.is-unread{background:var(--brand-wash)}
.notif-item.is-unread:hover{background:var(--brand-wash)}
.notif-item.is-static.is-unread:hover{background:var(--brand-wash)}
.notif-ico{width:34px;height:34px;border-radius:var(--r-s);flex:none;display:grid;place-items:center;color:var(--text-2);
  background:var(--surface-2);border:1px solid var(--line)}
.notif-ico svg{width:18px;height:18px}
.notif-body{display:flex;flex-direction:column;gap:2px;min-width:0}
.notif-title{font-weight:600;font-size:var(--t-s);color:var(--text)}
.notif-text{font-size:var(--t-s);color:var(--text-2);line-height:1.4}
.notif-time{font-size:var(--t-xs);color:var(--text-3);margin-top:1px}
.notifications-page-panel{padding:10px}
.notifications-page-list{display:flex;flex-direction:column;gap:4px}
.notifications-date-heading{padding:17px 18px 8px;color:var(--text);
  font-size:var(--t-s);font-weight:600;border-bottom:1px solid var(--line)}
.notifications-page-item{padding:16px 18px;border:1px solid transparent}
.notifications-page-item + .notifications-page-item{border-top-color:var(--line)}
.notifications-page-item .notif-ico{width:40px;height:40px;border-radius:var(--r-s)}
.notifications-page-item .notif-ico svg{width:21px;height:21px}
.notifications-page-item .notif-title{font-size:var(--t-m)}
.notifications-page-item .notif-text{font-size:var(--t-s)}
.number-pager{display:flex;align-items:center;justify-content:center;gap:7px;flex-wrap:wrap;padding:22px 10px 12px}
.number-pager-btn{min-width:34px;height:34px;padding:0 10px;border:1px solid var(--line);border-radius:var(--r-s);
  display:grid;place-items:center;color:var(--text-2);background:none;
  font-size:var(--t-s);text-decoration:none;transition:border-color .16s,color .16s}
.number-pager-btn:hover{border-color:var(--line-2);color:var(--text)}
.number-pager-btn.active{background:var(--brand);border-color:var(--brand);color:var(--on-brand);cursor:default}
.number-pager-gap{min-width:24px;text-align:center;color:var(--text-3)}

/* ---------- my numbers ---------- */
.num-list{display:flex;flex-direction:column;gap:var(--sp-3)}
.num-card{border:1px solid var(--line);border-radius:var(--r-l);background:var(--surface);padding:var(--sp-4) var(--sp-5)}
.num-row{display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap}
.num-main{min-width:0}
.num-phone{font-family:var(--font-mono);font-weight:500;font-size:var(--t-xl);color:var(--text);font-variant-numeric:tabular-nums}
.num-side{display:flex;align-items:center;gap:12px;flex:none}
.num-dates{font-size:var(--t-s);color:var(--text-2)}
.num-transfer{margin-top:12px;border-top:1px solid var(--line);padding-top:12px}
.num-transfer>summary{display:inline-flex;align-items:center;gap:8px;cursor:pointer;list-style:none;
  font-size:var(--t-s);color:var(--text-2);user-select:none}
.num-transfer>summary::-webkit-details-marker{display:none}
.num-transfer>summary svg{width:16px;height:16px}
.num-transfer>summary:hover{color:var(--text)}
.num-transfer[open]>summary{margin-bottom:12px}
.num-transfer-form{display:flex;gap:10px;flex-wrap:wrap}
.num-transfer-form .input{flex:1;min-width:200px}
.num-transfer-note{font-size:var(--t-xs);color:var(--text-3);margin-top:9px}

/* ---------- support / tickets ---------- */
.support-tg{display:flex;align-items:center;gap:11px;font-size:var(--t-s);line-height:1.5;
  padding:12px var(--sp-4);border-radius:var(--r);margin-bottom:20px;color:var(--text-2);
  background:var(--surface-2);border:1px solid var(--line)}
.support-tg svg{width:20px;height:20px;color:var(--brand-ink);flex:none}
.support-tg a{color:var(--brand-ink);font-weight:500}
.support-tg a:hover{text-decoration:underline}
.ticket-list{display:flex;flex-direction:column;gap:10px}
.ticket-row{display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:12px var(--sp-4);border:1px solid var(--line);border-radius:var(--r);background:var(--surface);
  transition:border-color .16s,background .16s}
.ticket-row:hover{border-color:var(--line-2);background:var(--surface-2)}
.ticket-main{min-width:0;display:flex;flex-direction:column;gap:4px}
.ticket-subject{font-weight:500;font-size:var(--t-m);color:var(--text);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ticket-preview{font-size:var(--t-s);color:var(--text-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:46ch}
.ticket-side{display:flex;flex-direction:column;align-items:flex-end;gap:6px;flex:none}
.ticket-time{font-size:var(--t-xs);color:var(--text-3)}
.ticket-perpage{margin-left:auto}
.cab-select{appearance:none;-webkit-appearance:none;-moz-appearance:none;
  background-color:var(--surface-2);border:1px solid var(--line);color:var(--text);
  font-family:var(--font-body);font-size:var(--t-s);padding:8px 32px 8px 13px;border-radius:var(--r-s);cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236C7A99' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 10px center;background-size:14px;
  transition:border-color .2s ease,background-color .2s ease}
.cab-select:hover{border-color:var(--line-2)}
.cab-select:focus{outline:none;border-color:var(--line-2)}
.cab-select option{background:var(--surface);color:var(--text)}
.tk-pill{display:inline-flex;align-items:center;font-weight:600;font-size:var(--t-s);white-space:nowrap}
.tk-open{color:var(--ok)}
.tk-awaiting_client{color:var(--warn)}
.tk-closed{color:var(--text-3)}
.ticket-id-line{font-family:var(--font-mono);font-size:var(--t-xs);letter-spacing:.06em;
  color:var(--text-3);margin:2px 0 5px}
.ticket-head-row{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-top:2px}
.ticket-head-row h1.ticket-title{font-size:var(--t-2xl);line-height:1.2;word-break:break-word}
.ticket-close{margin:0 0 0 auto}
.ticket-close-btn{display:inline-flex;align-items:center;gap:7px;padding:8px 14px;border-radius:var(--r-s);cursor:pointer;
  font-size:var(--t-s);font-weight:600;
  color:var(--bad);background:none;border:1px solid var(--line);
  transition:background .16s,border-color .16s}
.ticket-close-btn:hover{background:var(--surface-2);border-color:var(--bad)}
.ticket-close-btn svg{width:15px;height:15px}
.chat-day{align-self:center;margin:6px auto 2px;padding:var(--sp-1) var(--sp-3);border-radius:var(--r-s);
  font-size:var(--t-xs);color:var(--text-2);background:var(--surface);border:1px solid var(--line)}
/* Свои отступы теперь у шапки, ленты и поля ввода: панель их больше не держит, иначе
   лента не доходила бы до краёв и её собственный фон читался бы как вставка. */
.chat-panel{display:flex;flex-direction:column;padding:0;overflow:hidden}
.chat-header{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--line);position:relative}
.chat-support-avatar{width:42px;height:42px;border-radius:50%;flex:none;overflow:hidden;display:grid;place-items:center;
  background:var(--brand-wash);border:1px solid var(--line-2)}
.chat-support-avatar img{width:100%;height:100%;object-fit:cover;display:block}
.chat-header-info{min-width:0;display:flex;flex-direction:column;gap:2px}
.chat-header-title{font-size:var(--t-l);font-weight:600;letter-spacing:-.02em;line-height:1.2}
.chat-header-sub{font-size:var(--t-s);color:var(--text-2);line-height:1.35}
.chat-warning{margin-left:auto;flex:none;width:34px;height:34px;border-radius:var(--r-s);display:grid;place-items:center;
  background:var(--surface-2);border:1px solid var(--line);color:var(--warn);cursor:pointer;
  transition:border-color .16s,background .16s}
.chat-warning:hover{border-color:var(--line-2);background:var(--surface-2)}
.chat-warning svg{width:20px;height:20px}
.chat-warning-pop{position:absolute;top:calc(100% - 2px);right:14px;z-index:30;max-width:min(280px,calc(100vw - 32px));
  background:var(--surface-2);border:1px solid var(--line-2);border-radius:var(--r);padding:10px 13px;
  font-size:var(--t-s);line-height:1.45;color:var(--text);box-shadow:0 20px 44px -20px var(--shadow);
  opacity:0;visibility:hidden;transform:translateY(-6px) scale(.97);transform-origin:top right;
  transition:opacity .16s ease,transform .16s cubic-bezier(.2,.9,.3,1),visibility .16s}
.chat-warning-pop.is-open{opacity:1;visibility:visible;transform:none}
.chat-warning-pop a{color:var(--brand-ink);font-weight:500}
.chat-warning-pop a:hover{text-decoration:underline}
.chat-warning-pop[hidden]{display:none}
/* back button — only in mobile fullscreen */
.chat-back{display:none;flex:none;width:36px;height:36px;place-items:center;border:0;background:none;
  color:var(--text);cursor:pointer;border-radius:10px;margin-left:-4px;text-decoration:none}
.chat-back svg{width:24px;height:24px}
.chat-back:active{background:var(--surface-2)}
/* mobile: support chat goes fullscreen over everything, like a real messenger */
@media(max-width:560px){
  body.chat-open{overflow:hidden}
  .chat-panel.chat-fullscreen{position:fixed;left:0;right:0;top:0;height:100vh;height:100dvh;z-index:100;
    margin:0;border:0;border-radius:0;background:var(--bg);display:flex;flex-direction:column;
    padding:env(safe-area-inset-top) 0 0;box-shadow:none}
  .chat-fullscreen .chat-back{display:grid}
  .chat-fullscreen .chat-header{flex:none;padding:11px 12px}
  .chat-fullscreen .chat-thread{flex:1;max-height:none;min-height:0;padding:14px 14px 16px}
  .chat-fullscreen .chat-compose{flex:none;padding:10px 12px calc(10px + env(safe-area-inset-bottom));
    border-top:1px solid var(--line);background:var(--surface)}
  .chat-fullscreen .chat-closed-note{margin:12px}
}
.chat-thread{display:flex;flex-direction:column;gap:var(--sp-2);max-height:min(56vh,560px);overflow-y:auto;
  overscroll-behavior:contain;padding:var(--sp-4);margin-bottom:0;background:var(--surface-2)}
.chat-msg{display:flex;flex-direction:column;gap:4px;max-width:78%}
.chat-msg.is-me{align-self:flex-end;align-items:flex-end}
.chat-msg.is-support{align-self:flex-start;align-items:flex-start}
.chat-bubble{padding:var(--sp-2) var(--sp-3);border-radius:var(--r);font-size:var(--t-m);line-height:1.5;white-space:pre-wrap;word-break:break-word;border:1px solid var(--line);background:var(--surface)}
.is-me .chat-bubble{background:var(--brand-wash);color:var(--text);border-bottom-right-radius:var(--r-s)}
.is-support .chat-bubble{border-bottom-left-radius:var(--r-s)}
.chat-meta{display:flex;align-items:center;gap:1px;font-size:var(--t-xs);color:var(--text-3);padding:0 4px}
.chat-typing{align-self:flex-start;max-width:78%}
.typing-bubble{display:inline-flex;align-items:center;gap:4px;padding:var(--sp-3) var(--sp-4);border-radius:var(--r);border-bottom-left-radius:var(--r-s);
  background:var(--surface);border:1px solid var(--line)}
.typing-bubble span{width:7px;height:7px;border-radius:50%;background:var(--text-2);opacity:.45;animation:typing-bounce 1.25s infinite ease-in-out}
.typing-bubble span:nth-child(2){animation-delay:.16s}
.typing-bubble span:nth-child(3){animation-delay:.32s}
@keyframes typing-bounce{0%,65%,100%{transform:translateY(0);opacity:.35}30%{transform:translateY(-5px);opacity:1}}
.chat-closed-note{display:flex;align-items:center;justify-content:center;gap:9px;font-size:var(--t-s);color:var(--text-2);
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r);padding:11px 14px;margin:var(--sp-3)}
.chat-closed-note svg{width:17px;height:17px;color:var(--text-3);flex:none}
.chat-closed-note a{color:var(--brand-ink);font-weight:500}
.chat-closed-note a:hover{text-decoration:underline}
.chat-closed-note[hidden]{display:none}
.chat-compose[hidden]{display:none}
.chat-compose{display:flex;align-items:flex-end;gap:var(--sp-2);padding:var(--sp-3) var(--sp-4);border-top:1px solid var(--line)}
.chat-input{flex:1;resize:none;min-height:40px;max-height:160px;line-height:1.45;overflow-y:hidden;padding:9px 13px}
.chat-send{flex:none;width:40px;height:40px;padding:0;display:grid;place-items:center;border-radius:var(--r-s);
  border:1px solid transparent;background:var(--brand);color:var(--on-brand);cursor:pointer;
  transition:background .16s,color .16s,border-color .16s}
.chat-send:hover{background:var(--brand-hi)}
.chat-compose.is-empty .chat-send{background:var(--surface-2);border-color:var(--line);color:var(--text-3);cursor:not-allowed}
.chat-send svg{width:17px;height:17px}
.chat-close-form{padding:0 var(--sp-4) var(--sp-3);text-align:center}
.chat-close-btn{background:none;border:0;color:var(--text-3);font-size:var(--t-s);cursor:pointer;font-family:var(--font-body);
  text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--line-2)}
.chat-close-btn:hover{color:var(--bad)}

/* prevent iOS auto-zoom on focus */
@media(max-width:768px){
  input,select,textarea,.input{font-size:16px}
}

/* support send throttle hint */
.support-cooldown-hint{position:fixed;left:50%;bottom:88px;transform:translateX(-50%) translateY(8px);background:var(--surface);border:1px solid var(--line-2);color:var(--text);padding:10px 16px;border-radius:var(--r);font-size:var(--t-s);box-shadow:0 20px 44px -20px var(--shadow);opacity:0;pointer-events:none;transition:opacity .2s,transform .2s;z-index:1200;}
.support-cooldown-hint.show{opacity:1;transform:translateX(-50%) translateY(0);}

/* support: image attachments + attach button */
.chat-image-link{display:inline-block;max-width:min(280px,72%);border-radius:var(--r);overflow:hidden;line-height:0;border:1px solid var(--line);}
.chat-image{display:block;width:100%;height:auto;cursor:zoom-in;}
.chat-msg.is-me .chat-image-link{margin-left:auto;}
.chat-attach{flex:none;width:40px;height:40px;display:grid;place-items:center;border:1px solid var(--line);background:var(--surface-2);color:var(--text-2);border-radius:var(--r-s);cursor:pointer;transition:border-color .16s,color .16s;}
.chat-attach:hover{border-color:var(--line-2);color:var(--text);}
.chat-attach svg{width:18px;height:18px;}
.chat-attach.is-busy{opacity:.5;pointer-events:none;}

/* nice slim scrollbar for the ticket compose box (was the ugly default arrows) */
.chat-input{scrollbar-width:thin;scrollbar-color:rgba(140,160,200,.45) transparent;}
.chat-input::-webkit-scrollbar{width:9px;}
.chat-input::-webkit-scrollbar-track{background:transparent;}
.chat-input::-webkit-scrollbar-thumb{background:rgba(140,160,200,.4);border-radius:8px;border:2px solid transparent;background-clip:content-box;}
.chat-input::-webkit-scrollbar-thumb:hover{background:rgba(140,160,200,.65);border:2px solid transparent;background-clip:content-box;}
.chat-input::-webkit-scrollbar-button{display:none;height:0;width:0;}

/* ===== image send confirmation (preview) ===== */
.img-confirm-backdrop{position:fixed;inset:0;z-index:1200;background:rgba(0,0,0,.6);display:flex;align-items:center;justify-content:center;padding:24px;}
.img-confirm-backdrop[hidden]{display:none;}
.img-confirm{background:var(--bg);border:1px solid var(--line-2);border-radius:var(--r-l);padding:18px;max-width:min(460px,92vw);box-shadow:0 30px 60px -30px var(--shadow);}
.img-confirm-title{font-weight:600;font-size:var(--t-l);margin-bottom:14px;text-align:center;}
.img-confirm-preview{display:grid;place-items:center;background:var(--surface-2);border-radius:var(--r);padding:10px;margin-bottom:16px;}
.img-confirm-preview img{max-width:100%;max-height:52vh;border-radius:8px;display:block;}
.img-confirm-actions{display:flex;justify-content:flex-end;gap:10px;}

/* country flag icon in tariff rows (emoji flags don't render on Windows) */
.tariff-flag{width:20px;height:15px;border-radius:3px;object-fit:cover;margin-right:8px;vertical-align:-2px;box-shadow:0 0 0 1px rgba(0,0,0,.12);}

/* tariff country: flag + name aligned; ToS agreement note */
.tariff-country{display:inline-flex;align-items:center;gap:8px;}
.tariff-flag{width:22px;height:16px;border-radius:3px;object-fit:cover;flex:none;box-shadow:0 0 0 1px rgba(0,0,0,.14);}
.tariff-flag.flag-missing{display:none;}
.tariff-country-name{font-weight:600;}
.rent-tos-note{margin-top:var(--sp-5);color:var(--text-3);font-size:var(--t-s);line-height:1.5;max-width:640px;}
.rent-tos-note a{color:var(--brand-ink);text-decoration:underline;text-underline-offset:3px;}

/* cabinet corner toast (2FA + general) */
/* Тост встаёт ПОД шапкой, а не поверх неё: на 18px от верха он накрывал колокольчик, аватар и
   баланс — и, не имея pointer-events:none, ещё и не давал по ним щёлкнуть все 4.2 секунды. */
.cab-toast-wrap{position:fixed;top:calc(var(--rail) + 10px);right:18px;z-index:1400;display:flex;
  flex-direction:column;gap:10px;max-width:340px;pointer-events:none;}
.cab-toast-wrap>*{pointer-events:auto;}
/* Открытый диалог держит страницу: под затемнением она прокручивалась колесом и клавишами, и
   человек уезжал от того, что подтверждает. Класс ставит cabinetModalHold. */
body.modal-open{overflow:hidden}
/* Тост — поверхность с цветной левой полосой. Три сплошные заливки (зелёная, красная, синяя)
   были тремя разными самыми яркими пятнами на экране ради сообщения, которое живёт 4 секунды. */
.cab-toast{padding:12px var(--sp-4);border-left:3px solid var(--line-2);border-radius:var(--r-s);
  font-size:var(--t-s);color:var(--text);
  box-shadow:0 20px 44px -20px var(--shadow);opacity:0;transform:translateX(20px);transition:opacity .25s,transform .25s;
  background:var(--surface);border-top:1px solid var(--line);border-right:1px solid var(--line);border-bottom:1px solid var(--line);}
.cab-toast.in{opacity:1;transform:none;}
.cab-toast-success{border-left-color:var(--ok);}
.cab-toast-error{border-left-color:var(--bad);}
.cab-toast-info{border-left-color:var(--brand);}
/* 6-digit code field: roomy, segmented feel */
.input-code{text-align:center;letter-spacing:.22em;font-size:var(--t-xl);font-family:var(--font-mono);font-weight:500;padding-left:.22em;}
.input-code::placeholder{letter-spacing:.22em;opacity:.4;}

/* prevent the Russian-then-English flash: hide app content for EN until the
   client-side i18n pass has run (revealed by the .i18n-ready class) */
html[data-lang="en"]:not(.i18n-ready) .app,
html[data-lang="en"]:not(.i18n-ready) .auth-wrap,
html[data-lang="en"]:not(.i18n-ready) .ban-wrap{visibility:hidden;}

/* ===== my numbers: per-number management ===== */
/* The duration is a label, not a link — there is nothing to click and nothing to warn about,
   so it takes the text colour like everything else on the line. */
.num-tariff{font-weight:600;}
.num-card [hidden]{display:none!important}
.num-actions{display:flex;align-items:center;flex-wrap:wrap;gap:var(--sp-3);margin-top:var(--sp-4);padding-top:var(--sp-3);border-top:1px solid var(--line);}
/* When the bar carries only the auto-renew block there is nothing to separate it FROM and
   nothing to sit opposite: the rule drew a full-width line under a header and `margin-left:auto`
   parked the switch at the far edge, leaving a wide empty band across the card. */
.num-toggle{display:inline-flex;align-items:center;gap:9px;cursor:pointer;user-select:none;margin-left:auto;}
.num-toggle input{position:absolute;opacity:0;width:0;height:0;}
.num-toggle-track{position:relative;width:36px;height:20px;border-radius:11px;background:var(--surface-2);border:1px solid var(--line);transition:background .18s,border-color .18s;flex:none;}
.num-toggle-track::after{content:"";position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;background:var(--text-3);transition:transform .18s cubic-bezier(.2,.9,.3,1),background .18s;}
.num-toggle input:checked + .num-toggle-track{background:var(--brand-wash);border-color:var(--brand);}
.num-toggle input:checked + .num-toggle-track::after{transform:translateX(16px);background:var(--brand);}
.num-toggle input:focus-visible + .num-toggle-track{outline:2px solid var(--line-2);outline-offset:2px;}
.num-toggle-label{font-size:var(--t-s);color:var(--text-2);}
.num-note-preview{color:var(--text-3);}
.num-note-edit{margin-top:8px;}
.num-note-input{width:100%;min-height:84px;resize:vertical;}
.num-note-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:8px;}
.num-note-count{font-size:var(--t-xs);color:var(--text-3);}
.num-note-save{height:36px;padding:0 var(--sp-4);font-size:var(--t-s);}


/* ===== custom confirm modal ===== */
/* Прокрутка — у затемнения, предел высоты — у окна. Без этого диалог, который выше экрана,
   становился недостижимым: страницу под ним держит замок, а внутри прокручивать было нечего —
   у диалога пополнения с тремя счетами кнопка «Перейти к оплате» уезжала ниже края.
   align-items:flex-start вместо center — иначе высокое окно обрезается сверху, и крестик
   оказывается выше нуля прокрутки. */
.cab-modal-back{position:fixed;inset:0;z-index:1200;display:flex;align-items:flex-start;
  justify-content:center;padding:20px;background:rgba(6,10,20,.6);opacity:0;
  transition:opacity .2s ease;overflow:auto;overscroll-behavior:contain;}
/* Короткие диалоги (подтверждения) остаются по центру: у них своя высота меньше экрана. */
.cab-modal-back>.cab-modal{margin:auto;}
.cab-modal-back.in{opacity:1;}
.cab-modal{width:100%;max-width:420px;background:var(--bg);border:1px solid var(--line-2);border-radius:var(--r-l);box-shadow:0 30px 60px -30px var(--shadow);padding:var(--sp-5);transform:translateY(12px) scale(.97);opacity:0;transition:transform .22s cubic-bezier(.2,.9,.3,1),opacity .22s;}
.cab-modal-back.in .cab-modal{transform:none;opacity:1;}
.cab-modal-title{margin:0 0 8px;font-size:var(--t-l);font-weight:600;color:var(--text);}
.cab-modal-text{margin:0 0 20px;font-size:var(--t-s);line-height:1.5;color:var(--text-2);}
.cab-modal-foot{display:flex;gap:10px;justify-content:flex-end;}
.cab-modal-foot .btn{height:40px;padding:0 var(--sp-4);}
/* Corner dismiss for dialogs whose "no thanks" is not a decision worth a button of its own —
   the price-changed notice, where closing just means "seen it". Sits inside the padding so the
   title can run its full width; the hit area is deliberately larger than the glyph. */
.cab-modal-priced{position:relative;}
.cab-modal-x{position:absolute;top:12px;right:12px;width:32px;height:32px;display:flex;
  align-items:center;justify-content:center;padding:0;border:0;border-radius:var(--r-s);
  background:transparent;color:var(--text-3);cursor:pointer;transition:background .15s,color .15s;}
.cab-modal-x:hover{background:var(--surface-2);color:var(--text);}
.cab-modal-x svg{width:17px;height:17px;}
.cab-modal-priced .cab-modal-title{padding-right:34px;}
.btn-danger-solid{background:var(--bad);border-color:var(--bad);color:#fff;}
.btn-danger-solid:hover{filter:brightness(1.08);}

/* ===== number card: refund removal animation ===== */
.num-card{transition:opacity .4s ease,transform .4s ease,max-height .45s ease,margin .4s ease,padding .4s ease;}
/* overflow:hidden belongs to the collapse ONLY. On the resting card it clipped every popup that
   reached past an edge — the auto-renew hint sits low in the card and opens upward, so it lost
   its top line to the card border and became unreadable exactly where it was needed. */
.num-card.num-removing{overflow:hidden;opacity:0;transform:translateX(40px) scale(.96);max-height:0!important;margin-top:0!important;margin-bottom:0!important;padding-top:0!important;padding-bottom:0!important;border-width:0!important;}

/* ===== activate pill / availability countdown ===== */
.num-avail{display:inline-flex;align-items:center;gap:7px;height:36px;padding:0 var(--sp-4);font-size:var(--t-s);font-weight:600;border-radius:var(--r);background:none;border:1px solid var(--line);color:var(--ok);}
.num-avail svg{width:16px;height:16px;}

/* ===== auto-renew info tooltip ===== */
.num-hint{position:relative;display:inline-flex;align-items:center;margin-left:6px;color:var(--text-3);}
.num-hint svg{width:15px;height:15px;}
.num-hint:hover{color:var(--text-2);}
.num-hint .num-hint-pop{position:absolute;bottom:calc(100% + 9px);right:-8px;width:240px;padding:10px 12px;font-size:12.5px;line-height:1.45;font-weight:400;color:var(--text);background:var(--surface-2);border:1px solid var(--line-2);border-radius:var(--r);box-shadow:0 20px 44px -20px var(--shadow);opacity:0;visibility:hidden;transform:translateY(-7px);transition:opacity .22s ease,transform .22s ease,visibility .22s;z-index:30;}
.num-hint:hover .num-hint-pop,.num-hint:focus .num-hint-pop{opacity:1;visibility:visible;transform:none;}
.num-hint .num-hint-pop::after{content:"";position:absolute;top:100%;right:12px;border:6px solid transparent;border-top-color:var(--surface-2);}

/* ===== number card: copy, ⋮ menu, renew-next ===== */
.num-phone-line{display:flex;align-items:center;gap:8px}
.num-copy{display:inline-flex;width:28px;height:28px;align-items:center;justify-content:center;border:1px solid var(--line);border-radius:var(--r-s);background:transparent;color:var(--text-3);cursor:pointer;transition:color .16s,border-color .16s,background .16s}
.num-copy:hover{color:var(--text);border-color:var(--line-2);background:var(--surface-2)}
.num-copy:active{transform:scale(.94)}
.num-copy.copied{color:var(--ok);border-color:var(--ok)}
.num-copy svg{width:14px;height:14px}
.num-renew{margin-left:auto;display:flex;flex-direction:column;align-items:flex-end;gap:5px}
.num-renew-row{display:flex;align-items:center;gap:9px}
.num-renew-row .num-toggle{margin-left:0}
.num-renew-next{font-size:var(--t-xs);color:var(--text-2)}
.num-renew-next[hidden]{display:none}
/* Одна строка вместо двух: «Продление оплачено» + «Номер будет продлён после …» говорили
   одно и то же дважды и занимали высоту, которой в плотной карточке нет. Цвет сообщения —
   --ok, но смысл держит не он, а галочка: соседнее состояние в этом же слоте («Автопродление
   недоступно») отличается от этого ТОЛЬКО цветом, а этого мало. */
.num-renew-pending{display:flex;align-items:center;gap:5px;font-size:var(--t-xs);color:var(--ok)}
.num-renew-ok{width:13px;height:13px;flex:none}
.num-renew-pending[hidden]{display:none}
/* Last 12h of the term: auto-renew is deliberately closed. Same right-aligned slot as the
   "paid & pending" notice, but muted — it states a limit, it is not good news. */
.num-renew-closed{display:flex;align-items:center;gap:2px;font-size:var(--t-xs);color:var(--text-2)}
.num-renew-closed[hidden]{display:none}
.num-renew-closed .num-hint{margin-left:4px}
.num-renew-pending .num-hint{margin-left:1px;color:var(--text-3)}
.num-renew-pending .num-hint:hover{color:var(--text-2)}
.num-avail-load{color:var(--text-3)!important;background:var(--surface-2)!important;border-color:var(--line)!important}

/* ---------- waiting for the first SMS ----------
   The most common moment in the product: the number is bought and the user is staring at the
   card. A collapsed spoiler is the wrong shape for it — there is nothing to unfold — so until
   something arrives the card shows this strip instead, and the spoiler appears with the first
   message. Calm on purpose: one slow pulse, no spinner. A spinner reads as "request in flight,
   might fail"; a pulse reads as "alive". */
.num-wait{margin-top:var(--sp-4);padding-top:var(--sp-4);border-top:1px solid var(--line)}
.num-wait[hidden]{display:none}
.num-wait-row{display:flex;align-items:center;gap:var(--sp-3)}
/* Один проход полосы — это один такт опроса кабинета, пять секунд. Он подтверждает, что связь
   жива. Крутилка вместо него читалась бы как «запрос висит и может отвалиться». */
.num-wait-dot{position:relative;width:64px;height:2px;border-radius:2px;background:var(--surface-2);flex:none;overflow:hidden}
.num-wait-dot::after{content:"";position:absolute;inset:0 auto 0 0;width:22px;border-radius:2px;
  background:var(--brand);animation:num-wait-pulse 5s cubic-bezier(.4,0,.6,1) infinite}
.num-wait-title{font-size:var(--t-s);color:var(--text-2)}
.num-wait-timer{margin-left:auto;font-family:var(--font-mono);font-size:var(--t-xs);color:var(--text-3);font-variant-numeric:tabular-nums}
.num-wait-sub{margin-top:4px;font-size:var(--t-s);color:var(--text-3);line-height:1.45}
.num-wait-help{margin-top:4px;font-size:var(--t-s);color:var(--text-3);line-height:1.45;animation:num-wait-in .3s ease}
.num-wait-help a{color:var(--brand-ink);text-decoration:underline;text-underline-offset:3px}
@keyframes num-wait-pulse{from{transform:translateX(-24px)}to{transform:translateX(66px)}}
@keyframes num-wait-in{from{opacity:0}to{opacity:1}}
/* Someone who set "reduce motion" asked not to be pulsed at. Keep the dot, drop the animation. */
@media (prefers-reduced-motion:reduce){
  .num-wait-dot::after{animation:none}
  .num-wait-help{animation:none}
}

/* ---------- per-number SMS spoiler ---------- */
/* The message that just landed, marked briefly so the eye finds it without hunting. Fades out
   on its own — a permanent marker would just become part of the furniture. */
/* "Показаны последние 5 · Все сообщения →". Rendered only when the card is actually hiding
   something: a user who received 7 and counts 5 would otherwise think messages were lost. */

/* ===== generic modal body + rental history ===== */
.cab-modal-body{margin-bottom:18px}
.cab-modal-wide{max-width:640px}

/* notes summary: a clearer call-to-action when empty */

/* support: a touch more air before the first ticket */
.ticket-list-spaced{margin-top:16px}

/* ===== confirm modal: styled hint sub-note ===== */
/* Счёт в окне подтверждения автопродления. Сумма, срок и остаток на балансе стоят друг под
   другом одной колонкой цифр: человек читает их сверху вниз и сам видит, хватает ли денег,
   ещё до того как согласится платить. Цифры моноширинные — иначе колонка «пляшет». */
.renew-sum{margin:0 0 16px;border:1px solid var(--line);border-radius:var(--r-s);background:var(--surface-2);}
.renew-sum-row{display:flex;align-items:baseline;justify-content:space-between;gap:14px;padding:10px 13px;font-size:var(--t-s);color:var(--text-2);}
.renew-sum-row + .renew-sum-row{border-top:1px solid var(--line);}
.renew-sum-row b{font-weight:600;color:var(--text);font-size:var(--t-m);}
/* Нехватку баланса красным здесь мало: рядом идёт отдельная строка словами, потому что одним
   цветом смысл передавать нельзя. */
.renew-sum-row b.is-short{color:var(--danger,#e5484d);}
.cab-modal-hint{display:flex;align-items:flex-start;gap:9px;margin:0 0 18px;padding:11px 13px;border-radius:var(--r-s);background:var(--surface-2);border:1px solid var(--line);color:var(--text-2);font-size:var(--t-s);line-height:1.45;}
.cab-modal-hint svg{width:17px;height:17px;flex:none;margin-top:1px;color:var(--text-3);}

/* ===== tidy dark scrollbars for cabinet textareas/scroll areas ===== */
.num-note-input,textarea.input,.cab-modal-body{scrollbar-width:thin;scrollbar-color:var(--border-strong) transparent;}
.num-note-input::-webkit-scrollbar,textarea.input::-webkit-scrollbar,-webkit-scrollbar,.cab-modal-body::-webkit-scrollbar{width:10px;height:10px;}
.num-note-input::-webkit-scrollbar-track,textarea.input::-webkit-scrollbar-track,-webkit-scrollbar-track,.cab-modal-body::-webkit-scrollbar-track{background:transparent;}
.num-note-input::-webkit-scrollbar-thumb,textarea.input::-webkit-scrollbar-thumb,-webkit-scrollbar-thumb,.cab-modal-body::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:8px;border:3px solid transparent;background-clip:content-box;}
.num-note-input::-webkit-scrollbar-thumb:hover,textarea.input::-webkit-scrollbar-thumb:hover,-webkit-scrollbar-thumb:hover,.cab-modal-body::-webkit-scrollbar-thumb:hover{background:var(--muted-2);background-clip:content-box;}
.num-note-input::-webkit-scrollbar-corner,textarea.input::-webkit-scrollbar-corner{background:transparent;}

/* 2FA disable: password re-auth inline with the button */
.totp-disable-form{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.totp-disable-pass{max-width:210px}

/* =====================================================================
   MOBILE OVERRIDES FOR LATE-DEFINED COMPONENTS
   The central @media block (≈line 435) sits BEFORE these components, so an
   override placed there would lose on source order (media queries don't add
   specificity). Keep mobile rules for anything defined after that block HERE,
   at the end of the file, so they always win. See [project_cabinet_static_cache].
   ===================================================================== */
@media(max-width:640px){
  /* ----- messages: 6-column table -> stacked labelled cards ----- */
  .msg-field{width:100%}
}
@media(max-width:560px){
  /* ----- my numbers: stack the card so the renew block / actions don't crowd ----- */
  .num-card{padding:var(--sp-4)}
  .num-row{flex-direction:column;align-items:stretch;gap:9px}
  .num-side{margin-left:0;gap:10px;flex-wrap:wrap}
  .num-actions{gap:12px}
  .num-avail{width:100%;justify-content:center}
  .num-renew{margin-left:0;width:100%;align-items:stretch}          /* was right-floated via margin-left:auto */
  .num-renew-next{text-align:left}
  .num-renew-pending{align-items:center}
  .num-hint .num-hint-pop{width:min(240px,calc(100vw - 40px));right:0}   /* keep tooltip on-screen */
  .num-transfer-form .input{min-width:0}
}
@media(max-width:560px){
  /* ----- Stage 4: secondary pages polish ----- */
  /* catalog search: input fills the row, button (if any) wraps full-width below */
  .search-input{min-width:0}
  .search-bar .btn{flex:1 1 100%}
  /* balance: one payment method per row reads cleaner on a phone */
  .method-grid{grid-template-columns:1fr}
  /* operations / top-up history: keep the after-balance from squeezing the amount */
  .thist-row{flex-wrap:wrap;row-gap:6px}
  .thist-side{margin-left:auto}
  /* description wraps to 2 lines instead of truncating mid-word on a phone */
  .thist-meta{white-space:normal;overflow:visible;text-overflow:clip;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
  /* support tickets list: stack subject above the time/status meta */
  .ticket-row{flex-direction:column;align-items:stretch;gap:8px}
  .ticket-side{flex-direction:row;align-items:center;gap:10px}
  .ticket-preview{max-width:none}
  /* support chat: slightly wider bubbles on a narrow screen */
  .chat-msg{max-width:88%}
  /* profile: keep avatar LEFT + info RIGHT (don't wrap the column under the avatar) */
  .profile-top{flex-wrap:nowrap;gap:15px}
  .profile-avatar{width:64px;height:64px;border-radius:var(--r)}
  .profile-avatar-letter{font-size:26px}
  .profile-id{flex:1;min-width:0}
  .profile-stat{padding:12px 16px}
  /* empty states: compact so the CTA stays above the fold (no scroll) on a phone */
  .page-head{margin-bottom:18px}
  .empty{padding:12px 18px 12px}
  .empty .empty-mascot{width:92px;margin-bottom:6px}
  .panel-empty{padding-top:16px;padding-bottom:18px}
  .empty h3{font-size:var(--t-l)}
  .empty p{margin:7px auto 14px;font-size:var(--t-s)}
  /* catalog: 2-up compact grid (was one tall card per row → very long scroll) */
  .catalog-grid{grid-template-columns:repeat(2,1fr);gap:10px}
  a.service-card{gap:9px;padding:10px}
  .service-mark{width:30px;height:30px;border-radius:var(--r-s);font-size:var(--t-xs)}
  .service-mark img{width:18px;height:18px}
  .service-name{font-size:var(--t-s)}
  .service-go{display:none}                 /* whole card is tappable; chevron just eats width */
  /* keep the search reachable while scrolling 1000+ services. The bg/shadow appear ONLY
     once it's actually pinned (.is-stuck — toggled by an IntersectionObserver in rent.html),
     so unpinned it looks exactly like a normal search field, not a dark slab. */
  .search-bar{position:sticky;top:var(--rail);z-index:20;padding:6px 0;margin:14px 0 10px}
  /* when pinned: span full screen width (full-bleed out of the panel's padding) so it
     reads as a header toolbar, not an inset box. -44px = content(16) + panel(28) padding;
     padding compensates so the input itself doesn't move. */
  /* -40px = отступ контента (16) + отступ панели (24); паддинг компенсирует, чтобы само
     поле не сдвинулось. Числа выведены из текущих отступов, а не подобраны. */
}

/* ---------- purchase: mobile tap-to-select tariffs + sticky checkout bar ---------- */
.checkout-bar{display:none}
/* A 30px blue bloom under the main button was the loudest thing on the screen and the least
   informative — the bar slides in and the button is already filled. */
.checkout-buy{width:100%}
@media(max-width:900px){
  /* hide per-row buy; the whole row becomes a radio-style choice */
  .country-body .tariff-buy form[data-buy-form]{display:none}
  .country-body .tariff-row{cursor:pointer;position:relative;padding-right:42px;gap:10px}
  .country-body .tariff-list{padding-left:10px;padding-right:10px}
  .country-body .tariff-price{font-size:var(--t-m)}
  .country-body .tariff-row::after{content:"";position:absolute;right:15px;top:50%;transform:translateY(-50%);
    width:20px;height:20px;border-radius:50%;border:1.5px solid var(--line-2);background:none;
    transition:border-color .16s,background .16s}
  /* Tint plus filled dot is enough; the inset ring and the dot's halo were two more. */
  .country-body .tariff-row.is-selected{background:var(--brand-wash)}
  .country-body .tariff-row.is-selected::after{border-color:var(--brand);background:var(--brand)}
  /* sticky bar sits just above the bottom tab-bar (--cb-bottom measured in JS) */
  .checkout-bar{display:block;position:fixed;left:0;right:0;bottom:var(--cb-bottom,62px);z-index:46;
    padding:10px 14px;transform:translateY(180%);transition:transform .26s cubic-bezier(.2,.9,.3,1);pointer-events:none}
  .checkout-bar.is-active{transform:none;pointer-events:auto}
  body.checkout-open .content{padding-bottom:calc(150px + env(safe-area-inset-bottom))}
}

/* ---------- аватарка ---------- */
.avatar{position:relative;overflow:hidden}
.profile-avatar{position:relative}
.profile-avatar img{position:absolute;inset:0}

/* ---------- profile ID (copyable) ---------- */
.profile-idline{display:inline-flex;align-items:center;gap:8px;
  padding:5px 6px 5px 11px;border-radius:var(--r-s);background:var(--surface-2);border:1px solid var(--line);width:fit-content}
.profile-idlabel{font-size:var(--t-xs);color:var(--text-3)}
.profile-idval{font-family:var(--font-mono);font-size:var(--t-s);font-weight:500;color:var(--text)}
.profile-idcopy{display:inline-flex;width:26px;height:26px;align-items:center;justify-content:center;flex:none;
  border:1px solid var(--line);border-radius:var(--r-s);background:transparent;color:var(--text-3);cursor:pointer;
  transition:color .16s,border-color .16s,background .16s}
.profile-idcopy:hover{color:var(--text);border-color:var(--line-2);background:var(--surface)}
.profile-idcopy.copied{color:var(--ok);border-color:var(--ok)}
.profile-idcopy svg{width:14px;height:14px}

/* ---------- предпочтения: оформление, язык, оповещения ----------------------------------------
   Оповещения — таблица, а не столбик карточек. Подпись канала стоит один раз в шапке, а не
   шестнадцать раз под каждым событием: одинаковая строка, повторённая шестнадцать раз, — это
   шестнадцать строк текста, которые никто не читает, и вдвое более длинная прокрутка.
   На узком экране шапка прячется, а подпись возвращается внутрь самой галочки. */
.prefs{display:flex;flex-direction:column;gap:var(--sp-3);max-width:940px}

/* Тема и язык рядом: это один вопрос — как кабинет выглядит и на каком он языке. */
.pref-pair{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-5)}
.pref-block-title{font-size:var(--t-xs);color:var(--text-3);margin-bottom:var(--sp-2)}
.pref-choices{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-2)}
.pref-choice{
  display:flex;align-items:center;gap:var(--sp-2);text-align:left;
  padding:10px 12px;border:1px solid var(--line);border-radius:var(--r);
  background:var(--surface-2);cursor:pointer;transition:border-color .16s,background .16s;
}
.pref-choice:hover{border-color:var(--line-2)}
.pref-choice-mark{
  flex:none;width:18px;height:18px;border-radius:50%;
  border:1.5px solid var(--line-2);transition:border-color .16s,background .16s,box-shadow .16s;
}
.pref-choice-name{font-size:var(--t-s);color:var(--text)}
.pref-note{margin-top:var(--sp-2);font-size:var(--t-xs);color:var(--text-3)}
/* Выбранное состояние читается из атрибутов <html>, которые и так ставят переключатели темы
   и языка, — отдельного «выбрано» в разметке нет, и рассинхронизироваться нечему. */
html[data-theme="dark"] .pref-choice[data-theme-set="dark"],
html[data-theme="light"] .pref-choice[data-theme-set="light"],
html[data-lang="ru"] .pref-choice[data-lang-set="ru"],
html[data-lang="en"] .pref-choice[data-lang-set="en"]{border-color:var(--brand);background:var(--brand-wash)}
html[data-theme="dark"] .pref-choice[data-theme-set="dark"] .pref-choice-mark,
html[data-theme="light"] .pref-choice[data-theme-set="light"] .pref-choice-mark,
html[data-lang="ru"] .pref-choice[data-lang-set="ru"] .pref-choice-mark,
html[data-lang="en"] .pref-choice[data-lang-set="en"] .pref-choice-mark{
  border-color:var(--brand);background:var(--brand);box-shadow:inset 0 0 0 3px var(--surface);
}

/* ---- шапка карточки оповещений ---- */
.prefs-head{align-items:center}
.prefs-tools{margin-left:auto;display:flex;align-items:center;gap:var(--sp-3);flex-wrap:wrap}
.prefs-tool{
  font-size:var(--t-s);color:var(--text-3);cursor:pointer;
  text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--line-2);
  transition:color .16s;
}
.prefs-tool:hover{color:var(--text)}
/* Место под ответ держим всегда: строка, появляющаяся при сохранении, иначе сдвигала бы кнопки. */
.prefs-state{font-size:var(--t-xs);color:var(--text-3);min-width:9ch;text-align:right}
.prefs-state.is-ok{color:var(--ok)}
.prefs-state.is-bad{color:var(--bad)}

/* ---- сама таблица ---- */
.prefs-form{margin-top:var(--sp-4)}
/* Шапка столбцов липнет под верхнюю полосу. Строк здесь под два десятка, и без этого человек,
   прокрутивший до «Аккаунта и поддержки», видит два одинаковых столбца галочек и не знает, какой
   из них колокольчик, а какой Telegram. Смещение считается от --rail: полоса сама липкая и стоит
   выше по z, поэтому шапка, вставшая в 0, ушла бы под неё. Фон непрозрачный и растянут в поля
   панели отрицательными отступами — иначе строки просвечивали бы по краям, проезжая под шапкой. */
.pref-cols{
  position:sticky;top:var(--rail);z-index:5;
  display:flex;align-items:flex-end;gap:var(--sp-3);
  margin:0 calc(var(--sp-5) * -1);
  padding:var(--sp-2) var(--sp-5);
  background:var(--surface);
  border-bottom:1px solid var(--line);
}
.pref-cols>span:first-child{flex:1;min-width:0}
.pref-col{flex:none;width:210px;text-align:center;font-size:var(--t-xs);color:var(--text-3);line-height:1.3}

.pref-group + .pref-group{margin-top:var(--sp-4)}
.pref-group-title{
  font-size:var(--t-s);font-weight:600;color:var(--text-2);
  padding:var(--sp-3) 0 var(--sp-2);border-top:1px solid var(--line);
}
.pref-row{display:flex;align-items:center;gap:var(--sp-3)}
.pref-row + .pref-row{border-top:1px solid var(--line)}
.pref-row-name{flex:1;min-width:0;font-size:var(--t-s);color:var(--text-2)}

.pref-cell{
  flex:none;width:210px;display:flex;align-items:center;justify-content:center;
  padding:10px 0;border-radius:var(--r-s);cursor:pointer;user-select:none;
  transition:background .16s;
}
.pref-cell:hover{background:var(--surface-2)}
/* Настоящий чекбокс остаётся в разметке и фокусируемым — он просто не виден. Форма отправляет
   именно его, и клавиатура работает без единой строки скрипта. */
.pref-cell input{position:absolute;opacity:0;width:0;height:0}
.pref-mark{
  flex:none;width:18px;height:18px;border-radius:5px;display:grid;place-items:center;
  border:1.5px solid var(--line-2);color:transparent;
  transition:background .16s,border-color .16s,color .16s;
}
.pref-mark svg{width:12px;height:12px}
.pref-cell input:checked + .pref-mark{background:var(--brand);border-color:var(--brand);color:var(--on-brand)}
.pref-cell input:focus-visible + .pref-mark{outline:2px solid var(--line-2);outline-offset:2px}
/* На широком экране подпись канала уже стоит в шапке столбца. Прячем её так, чтобы она
   осталась доступной программе чтения с экрана: display:none отнял бы у галочки имя. */
.pref-cell-lbl{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;
}

.prefs-foot{margin-top:var(--sp-5);padding-top:var(--sp-4);border-top:1px solid var(--line)}
.prefs-foot[hidden]{display:none}

@media(max-width:760px){
  .pref-pair{grid-template-columns:1fr;gap:var(--sp-4)}
  /* Столбцов больше нет — подпись возвращается в саму галочку. */
  .pref-cols{display:none}
  .pref-row{flex-direction:column;align-items:stretch;gap:var(--sp-2);padding:var(--sp-3) 0}
  .pref-cell{
    width:auto;justify-content:flex-start;gap:var(--sp-2);padding:8px 12px;
    border:1px solid var(--line);background:var(--surface-2);
  }
  .pref-cell-lbl{
    position:static;width:auto;height:auto;margin:0;overflow:visible;
    clip-path:none;white-space:normal;font-size:var(--t-s);color:var(--text-2);
  }
  .pref-cell input:checked ~ .pref-cell-lbl{color:var(--text)}
}

/* ==================== История операций ====================================================
   Раньше это была стопка отдельных карточек внутри уже обведённой панели — рамка в рамке,
   в которой глазу не за что зацепиться. Теперь одна панель со строками: колонки стоят друг
   под другом, и суммы сравниваются взглядом, а не перечитыванием каждой строки.
   Блок неоплаченных счетов (.topup-history / .thist-*) намеренно остался прежним: он про
   действие «оплати», а не про историю, и живёт отдельной панелью выше. */

/* ---------- полоса итогов ---------- */
/* Одна карточка, а не три в ряд: три одинаковые плитки — самый заметный признак макета,
   собранного из плиток. Ячейки разделены линиями внутри общей рамки. */
.ops-summary{display:grid;grid-template-columns:repeat(3,1fr);
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-l);
  overflow:hidden;margin:var(--sp-4) 0}
.ops-cell{padding:var(--sp-4);min-width:0}
.ops-cell + .ops-cell{border-left:1px solid var(--line)}
.ops-cell-lbl{display:block;font-size:var(--t-xs);color:var(--text-3)}
.ops-cell-val{display:block;font-size:var(--t-l);font-weight:600;white-space:nowrap}

/* ---------- чипы-фильтры ---------- */
/* Переносом, а не горизонтальной прокруткой: на телефоне четыре чипа встают в две строки,
   и ни один не оказывается за краем экрана. */
.ops-filters{display:flex;flex-wrap:wrap;gap:var(--sp-2);margin-bottom:var(--sp-4)}
.ops-chip{display:inline-flex;align-items:center;gap:var(--sp-2);
  padding:var(--sp-2) var(--sp-3);border:1px solid var(--line);border-radius:var(--r-s);
  background:var(--surface);color:var(--text-2);font-size:var(--t-s);
  transition:color .16s,background .16s,border-color .16s}
.ops-chip:hover{color:var(--text);border-color:var(--line-2)}
.ops-chip-n{font-size:var(--t-xs);color:var(--text-3)}
.ops-chip.is-on{border-color:var(--brand);color:var(--text)}
.ops-chip.is-on:hover{color:var(--brand-ink);border-color:var(--brand)}
.ops-chip.is-on .ops-chip-n{color:var(--brand-ink)}

/* ---------- список ---------- */
/* Панель отдаёт поля строкам: разделительная линия обязана идти от края до края, иначе она
   читается как ещё одна рамка внутри рамки — ровно то, от чего экран и лечится. */
.panel.ops-panel{padding:0;overflow:hidden}

/* Одна сетка на шапку колонок и на строки — иначе они разъезжаются при первой же правке. */
.ops-cols,.ops-row{display:grid;
  grid-template-columns:36px minmax(0,1fr) 150px 110px 130px 16px;
  grid-template-areas:"tile title date amount balance";
  align-items:center;gap:var(--sp-3)}
.ops-cols{padding:var(--sp-2) var(--sp-4);border-bottom:1px solid var(--line)}
.ops-cols span{font-size:var(--t-xs);font-weight:400;color:var(--text-3)}
.ops-row{padding:var(--sp-3) var(--sp-4);transition:background .14s}
.ops-row + .ops-row{border-top:1px solid var(--line)}
.ops-row:hover{background:var(--surface-2)}

.ops-tile{grid-area:tile;width:36px;height:36px;border-radius:var(--r-s);
  display:grid;place-items:center;color:var(--text-2);
  background:var(--surface-2);border:1px solid var(--line);transition:background .14s}
/* На наведённой строке фон плитки совпал бы с фоном строки — плитка растворилась бы. */
.ops-row:hover .ops-tile{background:var(--surface)}
.ops-tile.is-in{color:var(--ok)}
.ops-tile svg{width:17px;height:17px}
/* Логотип платёжной системы занимает место плитки и повторяет её форму: столбец слева должен
   читаться одной колонкой, а не чередованием кружков и квадратов. */
.ops-logo{grid-area:tile;width:36px;height:36px;border-radius:var(--r-s);display:block;
  object-fit:cover;border:1px solid var(--line);background:var(--surface-2)}

.ops-title{grid-area:title;min-width:0}
.ops-name{display:block;font-size:var(--t-m);font-weight:500;color:var(--text);
  overflow-wrap:anywhere}
.ops-note{display:block;font-size:var(--t-s);color:var(--text-2);overflow-wrap:anywhere}
.ops-ext{display:flex;align-items:center;gap:var(--sp-2);min-width:0}
.ops-ext-label{flex:none;font-size:var(--t-xs);color:var(--text-3);letter-spacing:.04em}
.ops-ext code{font-family:var(--font-mono);font-size:var(--t-xs);color:var(--text-3);
  max-width:190px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ops-copy{flex:none;display:grid;place-items:center;width:22px;height:22px;
  border:1px solid var(--line);border-radius:var(--r-s);background:transparent;
  color:var(--text-3);cursor:pointer;transition:color .16s,border-color .16s,background .16s}
.ops-copy svg{width:12px;height:12px}
.ops-copy:hover{color:var(--text);border-color:var(--line-2);background:var(--surface-2)}
/* Подтверждение копирования — цветом на полторы секунды, без всплывающих подписей. */
.ops-copy.copied{color:var(--ok);border-color:var(--ok)}

.ops-date{grid-area:date;font-size:var(--t-s);color:var(--text-2);white-space:nowrap}

/* Сумма прижата вправо и набрана табличными цифрами (.num): разряды стоят друг под другом,
   и колонку можно просматривать сверху вниз, не читая её. */
.ops-amount{grid-area:amount;text-align:right;font-size:var(--t-m);font-weight:600;
  color:var(--text);white-space:nowrap}
/* Приход — зелёный. Трата остаётся обычным цветом текста: красным в этом продукте помечается
   то, что пошло не так, и если красить им каждую покупку, цвет перестанет что-либо значить
   там, где он действительно нужен. */
.ops-amount.is-in{color:var(--ok)}

.ops-after{grid-area:balance;text-align:right;font-size:var(--t-s);color:var(--text-3);
  white-space:nowrap}
/* Подпись «баланс» нужна только там, где нет шапки колонок, — то есть на телефоне. */
.ops-after-lbl{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}

/* Счётчик и пагинация делят одну полосу: разведённые по двум, они дали бы две волосяные
   линии подряд под списком, который и так весь из линий. */
.ops-foot{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;
  gap:var(--sp-3);padding:var(--sp-2) var(--sp-4);border-top:1px solid var(--line);
  font-size:var(--t-xs);color:var(--text-3)}
.ops-foot .pager{margin:0}
/* Пустая выборка — это не пустая история: человек сам сузил список, и он должен об этом
   прочитать, а не решить, что операции пропали. */
.ops-empty{padding:var(--sp-6) var(--sp-4);text-align:center;color:var(--text-2);
  font-size:var(--t-s)}

/* ---------- телефон ---------- */
@media(max-width:760px){
  /* Дата уходит под название, сумма остаётся справа. Плитка тянется на обе строки, поэтому
     высота строки не зависит от того, во сколько строк развернулось название. */
  .ops-cols{display:none}
  .ops-row{grid-template-columns:36px minmax(0,1fr) auto 16px;
    grid-template-areas:
      "tile title amount"
      "tile date  balance";
    align-items:start;column-gap:var(--sp-3);row-gap:var(--sp-1)}
  .ops-tile,.ops-logo{align-self:center}
  .ops-date{font-size:var(--t-xs)}
  .ops-after{font-size:var(--t-xs)}
  .ops-after-lbl{position:static;width:auto;height:auto;margin:0;overflow:visible;clip-path:none}
  .ops-foot{justify-content:center}
  .ops-cell{padding:var(--sp-3)}
  .ops-cell-val{font-size:var(--t-m)}
}
@media(max-width:560px){
  /* Три колонки итогов на узком экране: сумма важнее своего кегля — она обязана поместиться
     целиком, иначе полоса итогов начнёт врать обрезанным числом. */
  .ops-cell-val{font-size:var(--t-s)}
}

/* ==============================================================================================
   «Мои номера»: структура карточки по концепту.

   Карточка читается сверху вниз в том же порядке, в каком её читает человек: номер, под ним
   строка «сервис · срок · заметка», затем последнее SMS с кодом, и только в самом низу — чем
   номером можно распорядиться. Раньше управление стояло вторым, между номером и сообщением,
   то есть между читателем и единственным, ради чего он открыл страницу.

   Блок дописан в КОНЕЦ файла намеренно: он перекрывает более ранние правила .num-* порядком
   следования, а не весом селекторов. По той же причине мобильные переопределения тех же
   селекторов лежат здесь же, ниже, — иначе поздние обычные правила побеждали бы ранний @media.
   ============================================================================================== */

/* Разделитель — кружок, а не символ «·» внутри текста. Текстовая точка склеивала название
   сервиса и срок в ОДИН текстовый узел, а словарь переводчика ищет узел целиком: «Bolt · 3 дня»
   не совпадало ни с одним ключом, и срок оставался русским на английской версии. */
.num-sep{width:3px;height:3px;border-radius:50%;background:var(--text-3);flex:none}

/* ---------- шапка карточки ---------- */
.num-row{display:flex;align-items:flex-start;justify-content:flex-start;gap:var(--sp-4);flex-wrap:wrap}
.num-side{margin-left:auto}
.num-sub{display:flex;align-items:center;flex-wrap:wrap;gap:var(--sp-2);margin-top:5px;
  font-size:var(--t-s);color:var(--text-2)}
/* Срок — такая же подпись, как сервис рядом с ним. Полужирным он перетягивал взгляд с номера. */
.num-tariff{font-weight:400}
.num-sub-note{display:inline-flex;align-items:center;gap:var(--sp-2);min-width:0}

/* ---------- последнее сообщение: сразу в карточке, без спойлера ----------
   Код — единственная причина, по которой эту страницу открывают, и лишний клик здесь стоит
   дороже, чем где угодно ещё. Отправитель и время мелким, текст в две строки, справа — сам код. */
.num-last{display:flex;align-items:flex-start;gap:var(--sp-3);
  margin-top:var(--sp-4);padding-top:var(--sp-4);border-top:1px solid var(--line)}
.num-last-body{flex:1;min-width:0}
.num-last-meta{display:flex;align-items:center;gap:var(--sp-2);font-size:var(--t-xs);color:var(--text-3)}
.num-last-text{margin-top:2px;font-size:var(--t-s);line-height:1.45;color:var(--text-2);
  word-break:break-word;overflow:hidden;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.num-last-code{display:flex;align-items:center;gap:var(--sp-2);flex:none;margin-left:auto;
  min-height:40px;padding:var(--sp-2) var(--sp-3);border-radius:var(--r-s);cursor:pointer;
  border:1px solid transparent;background:var(--brand-wash);transition:border-color .16s}
.num-last-code:hover{border-color:var(--brand)}
.num-last-code-val{font-size:var(--t-l);font-weight:600;color:var(--text)}
.num-last-code-cap{font-family:var(--font-body);font-size:var(--t-xs);color:var(--brand-ink)}
/* Подпись переключается тем же классом .copied, который вешает общий обработчик копирования, —
   отдельный скрипт ради одного слова не понадобился. */
.num-last-cap-done{display:none}
.num-last-code.copied .num-last-cap-idle{display:none}
.num-last-code.copied .num-last-cap-done{display:inline}
.num-last-code.copied .num-last-code-cap{color:var(--ok)}
/* ---------- смена последнего SMS ------------------------------------------------------------
   Эту страницу держат открытой ради одного — увидеть НОВЫЙ код. Раньше содержимое блока
   затиралось в том же кадре, в котором приходил ответ опроса: человек, отвернувшийся на
   секунду, не мог отличить пришедший код от того, который он уже вставил и который не подошёл.
   Поэтому смена идёт движением: старое уходит ВВЕРХ, новое приходит СНИЗУ — тот же порядок,
   в каком растёт лента переписки.
   Двигаются только содержимое (отправитель, время, текст) и сами цифры кода. Синяя плашка
   кнопки остаётся на месте: на плоских поверхностях ездящая поверхность читается громче
   события, ради которого всё затевалось.
   Кривая — та же, что у модалки, тумблера и тоста кабинета: своей здесь заводить не за что. */
.num-last-body,
.num-last-code-val{
  /* Базовое правило описывает ВОЗВРАТ в покой, то есть приход нового сообщения. */
  transition:opacity .19s cubic-bezier(.2,.9,.3,1),transform .19s cubic-bezier(.2,.9,.3,1)}
/* Цифры приходят на 40 мс позже текста: взгляд идёт слева направо и заканчивает движение ровно
   на том, что нужно скопировать. */
.num-last-code-val{transition-delay:.04s}
/* Фаза 1 — уходит старое: вверх и в прозрачность, обе половины одновременно. */
.num-last.is-sms-out .num-last-body,
.num-last.is-sms-out .num-last-code-val{
  opacity:0;transform:translateY(calc(var(--sp-2) * -1));transition-duration:.12s;transition-delay:0s}
/* Фаза 2 — стартовая точка прихода. Переход выключен намеренно: это не движение, а
   перестановка на невидимом; само движение начинается со снятия класса. */
.num-last.is-sms-in .num-last-body,
.num-last.is-sms-in .num-last-code-val{
  opacity:0;transform:translateY(var(--sp-2));transition-duration:0s;transition-delay:0s}

/* Высота текста зафиксирована двумя строками — ровно столько же показывает обрезка
   -webkit-line-clamp:2 выше по файлу. Иначе однострочное сообщение, сменяясь двустрочным,
   дёргало бы вверх всё, что ниже: строку действий с «Отмена и возврат» прямо под курсором, а в
   списке — и все карточки под этой. 2.9 = 2 строки × line-height 1.45 при том же --t-s, так
   что значение не разъедется с правилом, которое эту обрезку задаёт. */
.num-last-text{min-height:calc(var(--t-s) * 2.9)}

/* Пришедшее только что: граница кнопки на ступень выше — этого хватает, чтобы глаз нашёл новое
   сам, не сравнивая карточку с тем, что он помнит. Ореола (box-shadow) здесь больше нет: на
   плоских поверхностях кабинета он читался как кольцо фокуса, то есть сообщал не то. */
.num-last.is-new .num-last-code{border-color:var(--brand)}

/* Кто попросил убрать движение, просил убрать ДВИЖЕНИЕ, а не событие: подмена обязана остаться
   заметной, иначе новый код не отличить от старого — ровно та беда, от которой всё это и
   делалось. Остаётся прозрачность (и рамка кнопки), обе фазы короче. */
@media (prefers-reduced-motion:reduce){
  .num-last-body,
  .num-last-code-val{transition-duration:.14s;transition-delay:0s}
  .num-last.is-sms-out .num-last-body,
  .num-last.is-sms-out .num-last-code-val{transform:none;transition-duration:.1s}
  .num-last.is-sms-in .num-last-body,
  .num-last.is-sms-in .num-last-code-val{transform:none}
}

/* ---------- спойлер с перепиской: ниже и тише ---------- */
/* Счётчик был синей капсулой вокруг одной цифры — рама вокруг числа, которое читается и так. */
/* Код внутри спойлера повторяет тот, что уже показан выше, поэтому он на ступень тише. */

/* ---------- заметки ---------- */
.num-note-preview{max-width:24ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ---------- строка действий: слева продление, справа действия ---------- */
.num-actions{gap:var(--sp-4)}
.num-renew{margin-left:0;align-items:flex-start}
.num-renew-pending{align-items:center}
.num-renew-next{text-align:left}
/* Блок продления стоит теперь СЛЕВА, поэтому в одиночестве он больше не оставляет пустую полосу
   через всю карточку — ту самую, ради которой этот класс и появился. Скрипт продолжает его
   выставлять, менять его логику не потребовалось, но линию строка сохраняет в обоих случаях. */
/* Подсказки блока продления открываются от ЛЕВОГО КРАЯ СВОЕЙ СТРОКИ, а не от значка.
   Привязка к значку здесь не работает принципиально: значок стоит там, куда его сдвинул текст
   перед ним, а текст меняется от языка и от данных. Замерено на трёх вариантах текста при
   ширинах 320–1920: с раскрытием влево «Продление оплачено» вылетала на 3 пикселя за левый край
   на английском (там «The number renews» длиннее русского), с раскрытием вправо тот же попап
   уезжал за правый край на 66 пикселей вместе с горизонтальной прокруткой страницы, стоило
   тексту стать длиннее. От края строки запас не зависит ни от текста, ни от языка вообще:
   33–41 пиксель на всех ширинах, потому что левый край строки — это левый край карточки.
   Ширину берём из базового правила .num-hint-pop (240px).

   Плата за устойчивость — хвостик-указатель: он крепится к попапу, а расстояние до значка
   меняется вместе с текстом, поэтому указывать на значок он больше не может. Убран, как и в
   мобильной плашке ниже; связь попапа со значком видна из того, что попап появляется по
   наведению на него. */
.num-renew,.num-renew-pending,.num-renew-closed,.num-renew-row{position:relative}
.num-renew .num-hint{position:static}
.num-renew .num-hint .num-hint-pop{left:0;right:auto}
.num-renew .num-hint .num-hint-pop::after{display:none}

/* Живой отсчёт «Отмена HH:MM:SS» внутри красной кнопки и откат переключателя продления.
   Раньше жили в <style> самой страницы и брали цвет мимо токенов. */
.num-cooldown{font-size:var(--t-xs);color:var(--text-2);white-space:nowrap;align-self:center}

@media(max-width:560px){
  /* Повтор мобильных правил для тех же селекторов: базовые правила выше идут ПОЗЖЕ прежнего
     @media-блока файла и иначе перебивали бы его порядком следования. */
  .num-row{flex-direction:column;align-items:stretch;gap:var(--sp-3)}
  .num-side{margin-left:0;gap:var(--sp-3);flex-wrap:wrap}
  /* Код уходит под текст и занимает всю ширину — на телефоне это самая нажимаемая кнопка. */
  .num-last{flex-direction:column;gap:var(--sp-3)}
  .num-last-code{margin-left:0;width:100%;justify-content:space-between}
  .num-actions{gap:var(--sp-3)}
  .num-renew{margin-left:0;width:100%;align-items:stretch}
  .num-renew-next{text-align:left}
  .num-renew-pending{align-items:center}
  /* На телефоне подсказка занимает всю ширину строки, а не 240 фиксированных пикселей: строка
     здесь во всю карточку, и узкая колонка текста рядом с широкой пустотой читается хуже.
     Привязка к краю строки и убранный хвостик — из базовых правил выше (замерено: без них
     вылет 40–45 пикселей за край экрана на всех ширинах до 560). */
  .num-renew .num-hint .num-hint-pop{left:0;right:0;width:auto}
}

/* ===== Сообщения: лента строк-карточек ========================================================
   Таблица здесь жила в двух видах сразу: на десктопе шесть колонок, из которых росла только
   одна (текст SMS), а на телефоне те же ячейки разбирались в подписанные строки — то есть
   вёрстка экрана зависела от ширины окна принципиально, а не в мелочах. Строка-карточка
   одна и та же везде: на широком экране её колонки стоят рядом, на узком складываются.

   ВАЖНО: блок должен лежать В КОНЦЕ cabinet.css — его медиазапросы обязаны выигрывать
   у секции MOBILE OVERRIDES выше по файлу (медиазапрос специфичности не добавляет). */

.msg-spin{display:inline-block;width:14px;height:14px;border:2px solid var(--line);
  border-top-color:var(--text-3);border-radius:50%;animation:msgspin .7s linear infinite;
  vertical-align:-2px;margin-right:7px}
@keyframes msgspin{to{transform:rotate(360deg)}}
.msg-loading{padding:var(--sp-6) var(--sp-1);color:var(--text-2);text-align:center}

/* ---------- фильтры ---------- */

.msg-filters{display:flex;align-items:flex-end;flex-wrap:wrap;gap:var(--sp-3);margin-bottom:var(--sp-5)}
/* flex-basis 200px и перенос: на узком экране поля сами встают друг под друга,
   отдельного правила в медиазапросе для этого не нужно. */
.msg-field{flex:1 1 200px;max-width:260px;margin:0;gap:var(--sp-2)}
.msg-field label{font-size:var(--t-xs);color:var(--text-3)}
.msg-filters .cab-select,.msg-filters .input{display:flex;align-items:center;
  width:100%;min-width:0;height:40px;box-sizing:border-box;
  padding:0 var(--sp-3);font-size:var(--t-s);border-radius:var(--r-s)}
.msg-filters .cab-select{padding-right:var(--sp-6)}
.msg-count{margin:0 0 var(--sp-3) auto;font-size:var(--t-s);color:var(--text-3)}
.msg-count[hidden]{display:none}

/* ---------- лента ---------- */

.msg-list{display:grid;gap:var(--sp-3)}

/* Шапка и строка — разные гриды, поэтому числа колонок продублированы: разъедутся —
   подписи перестанут стоять над своими колонками. */
.msg-head{display:grid;grid-template-columns:180px minmax(0,1fr) 140px 104px;gap:var(--sp-4);
  padding:0 var(--sp-5);font-size:var(--t-xs);color:var(--text-3)}
.msg-head span:nth-child(3){text-align:center}
.msg-head span:last-child{text-align:right}

.msg-row{display:grid;grid-template-columns:180px minmax(0,1fr) 140px 104px;gap:var(--sp-4);
  grid-template-areas:"who body code at";align-items:center;
  padding:var(--sp-4) var(--sp-5);
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-l);
  transition:border-color .16s}
.msg-row:hover{border-color:var(--line-2)}

.msg-who{grid-area:who;min-width:0}
.msg-svc{display:block;font-size:var(--t-m);font-weight:500;color:var(--text);overflow-wrap:anywhere}
.msg-phone{margin-top:var(--sp-1);font-size:var(--t-s);color:var(--text-2)}

.msg-body{grid-area:body;min-width:0}
.msg-from{font-size:var(--t-xs);color:var(--text-3);overflow-wrap:anywhere}
.msg-text{margin:var(--sp-1) 0 0;max-width:none;font-size:var(--t-s);line-height:1.45;
  color:var(--text-2);overflow-wrap:anywhere}
/* Подсветка кода в тексте — тем же моноширинным, что и в кнопке: одна и та же цифра
   не должна выглядеть двумя разными. */
.msg-mark{background:var(--brand-wash);color:var(--text);border-radius:var(--r-s);
  padding:0 var(--sp-1);font-family:var(--font-mono);font-weight:500}

/* ---------- код ---------- */

/* Ради кода страницу и открывают: он крупнее всего остального в строке, и кнопкой служит
   вся ячейка — целиться в иконку рядом со значением не нужно. Цвет значения обычный
   текстовый: синим здесь красится подложка, и синие цифры на ней читались бы хуже. */
.msg-copy{grid-area:code;width:100%;display:grid;justify-items:center;gap:var(--sp-1);
  padding:var(--sp-2) var(--sp-3);border:1px solid transparent;border-radius:var(--r-s);
  background:var(--brand-wash);font-family:var(--font-body);text-align:center;cursor:pointer;
  transition:border-color .16s}
.msg-copy:hover{border-color:var(--brand)}
.msg-code{display:block;font-size:var(--t-xl);font-weight:600;line-height:1.15;color:var(--text)}
.msg-copy-hint,.msg-copy-done{display:inline-flex;align-items:center;gap:var(--sp-1);
  font-size:var(--t-xs);color:var(--text-3);transition:color .16s}
.msg-copy-hint svg,.msg-copy-done svg{width:12px;height:12px;flex:none}
.msg-copy:hover .msg-copy-hint{color:var(--brand-ink)}
/* Подтверждение подменяет подпись на месте, а не всплывает рядом: взгляд уже там, куда нажали. */
.msg-copy-done{display:none;color:var(--brand-ink)}
.msg-copy.copied{border-color:var(--brand)}
.msg-copy.copied .msg-copy-hint{display:none}
.msg-copy.copied .msg-copy-done{display:inline-flex}

.msg-nocode{grid-area:code;text-align:center;font-size:var(--t-s);color:var(--text-3)}
.msg-at{grid-area:at;text-align:right;font-size:var(--t-xs);color:var(--text-3);white-space:nowrap}

.msg-reset{margin-top:var(--sp-4)}

/* ---------- узкий экран ---------- */

/* Порог считается по КОЛОНКЕ КОНТЕНТА, а не по окну: сайдбар забирает 264px, поэтому уже
   при 1080px окна под ленту остаётся меньше 770px — четыре колонки сжимают текст SMS
   до полутора слов в строке. Ниже порога сайдбара нет и места снова хватает, но карточка
   в столбик там и нужна: это планшет и телефон. */
@media(max-width:1080px){
  .msg-head{display:none}                    /* подписи без колонок не нужны */
  .msg-row{grid-template-columns:minmax(0,1fr) auto;
    grid-template-areas:
      "who  at"
      "body body"
      "code code";
    align-items:start;gap:var(--sp-3);padding:var(--sp-4)}
  .msg-at{align-self:start}
  /* Код ложится под текст отдельной полосой. Ширину ограничиваем: на планшете карточка
     под 700px, и растянутая на неё подложка читается баннером, а не кнопкой. */
  .msg-copy{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);
    justify-self:start;max-width:320px;padding:var(--sp-3) var(--sp-4)}
  .msg-nocode{text-align:left}
}
@media(max-width:640px){
  /* Каждое поле — своя строка, счётчик под ними: иначе они делят строку по остатку
     ширины и встают уступом. */
  .msg-field{flex:1 1 100%;max-width:none}
  .msg-count{flex:1 1 100%;margin:0}
  /* На телефоне кнопка снова во всю ширину: по такой попадают большим пальцем не глядя. */
  .msg-copy{max-width:none}
}

/* ---------- каталог сервисов: цена «от» на карточке ------------------------------------------
   Блок дописан в КОНЕЦ файла, а значит идёт после медиазапроса на 900px выше. Своей
   специфичности у медиазапроса нет, поэтому всё, что он задавал этим же селекторам, повторено
   ниже в собственном @media — иначе десктопное правило победило бы на телефоне. */

/* Правый край карточки занял ценник, и ему нужна ширина: при колонке в 190px от названия
   оставалось два слога. 210px — та точка, где ряд из пяти тесных карточек превращается в ряд
   из четырёх нормальных. */
.catalog-grid{grid-template-columns:repeat(auto-fill,minmax(210px,1fr))}
a.service-card{padding:var(--sp-3) var(--sp-4)}
/* Название на ступень мельче: в одной строке с ценой 15px и 13px спорили за внимание. */
.service-name{font-size:var(--t-s)}
/* Цена — подпись, а не заголовок: список читают по названиям, цена лишь задаёт порядок суммы,
   точную выбирают на экране тарифов. Не переносится, цифры моноширинные (класс .num в
   разметке) — иначе колонка цен пляшет от строки к строке. */
.service-price{flex:none;font-size:var(--t-s);color:var(--text-3);white-space:nowrap}

/* Поле поиска и подпись над списком — по шкале отступов (было 18/22 и 14). */
.search-bar{margin:var(--sp-4) 0}
.catalog-meta{margin-bottom:var(--sp-3)}

/* Пустой экран каталога — карточка, а не текст посреди пустоты. Фон берёт СЛЕДУЮЩУЮ ступень
   (--surface-2): панель вокруг уже залита --surface, и карточка тем же цветом не читалась бы.
   Правило намеренно сужено до каталога — .empty общий для половины кабинета. */
[data-rent-results] .empty{
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-l);
  padding:var(--sp-7) var(--sp-4);
}

@media(max-width:900px){
  /* Повтор правил верхнего медиазапроса — см. комментарий в начале блока. */
  .catalog-grid{grid-template-columns:repeat(2,1fr);gap:var(--sp-2)}
  a.service-card{gap:var(--sp-2);padding:var(--sp-3)}
  .search-bar{margin:var(--sp-3) 0 var(--sp-2)}
  [data-rent-results] .empty{padding:var(--sp-5) var(--sp-4)}
  /* Липкий поиск: сплошной фон страницы и одна нижняя граница. Размытия тут нет — слово
     «фростед-гласс» осталось только в комментарии выше по файлу, его надо удалить. Правило
     повторено дословно, потому что этот блок лежит ниже и иначе смысл читался бы в двух
     местах сразу. Отступы .is-stuck (полноширинный вылет из панели) не трогаются. */
  .search-bar.is-stuck{background:var(--bg);border-bottom:1px solid var(--line)}
}
@media(max-width:640px){
  /* В двух колонках на телефоне ценник отбирает у названия последние пиксели, а точную цену
     всё равно выбирают на следующем экране. */
  .service-price{display:none}
}

/* ==============================================================================================
   «Мои номера»: карточка по концепту — ОДНА нижняя строка вместо меню ⋮ и двух отдельных полос.

   Распорядиться номером можно было из трёх мест сразу: меню ⋮ в правом верхнем углу, строка
   заметки и строка автопродления. Три места, в которых человек искал одно и то же. Теперь это
   одна полоса внизу карточки: слева состояние продления (у завершённой аренды — сам факт её
   конца), справа текстовые действия.

   Блок дописан в КОНЕЦ файла намеренно: он перекрывает более ранние правила .num-* порядком
   следования, а не весом селекторов. По той же причине мобильные переопределения тех же
   селекторов лежат здесь же, ниже, — иначе поздние обычные правила побеждали бы ранний @media.
   ============================================================================================== */

/* Завершённая аренда отличается от живой бейджем «завершён», строкой «Срок аренды закончился»
   и отсутствием кнопок действий — этого хватает. Прежде она была приглушена ЦЕЛИКОМ
   (opacity: .7), и вместе с ней гас текст SMS: 3.17:1 в светлой теме против нормы 4.5. А ведь
   именно за этим текстом в закрытую карточку и заходят — посмотреть код, который приходил. */
.num-card.is-done{opacity:1}

/* ---------- шапка: номер, статус, срок ---------- */
/* Номер показан группами («+1 412 696-6474»), а в буфер уходит сплошная строка цифр: читают и
   вставляют разные вещи. Разбивку считает web_cabinet/services/rent_display.py. */
.num-phone-line{gap:var(--sp-2)}
.num-side{gap:var(--sp-3)}
/* Срок — обычная подпись рядом со статусом: «осталось 2 дн 22 ч», у завершённой «до 09.08.2026».
   Пунктирного подчёркивания и всплывающей точной даты здесь больше нет: пунктир под каждой
   карточкой читался как ссылка, а подсказка обещала подробность, которой в компактной строке и
   так почти не осталось. Моноширинными идут только цифры — слово набрано обычным шрифтом,
   иначе строка выглядит куском кода. */
.num-dates{font-size:var(--t-s);line-height:1.25;color:var(--text-2)}

/* ---------- ожидание первого SMS ----------
   Полоса-пульс и обещание — одна мысль, поэтому и одна строка: разнесённые на две, они читались
   как заголовок с пояснением, хотя пояснять там нечего. Подсказка «код обычно приходит за 1-2
   минуты» появляется ниже и только после трёх минут тишины. */
.num-wait-text{flex:1;min-width:0;font-size:var(--t-s);line-height:1.45;color:var(--text-2)}
.num-wait-title{color:var(--text-2)}
.num-wait-dash{color:var(--text-3)}
.num-wait-sub{display:inline;margin-top:0;font-size:inherit;color:var(--text-3)}

/* ---------- нижняя строка действий ---------- */
.num-actions{display:flex;align-items:center;flex-wrap:wrap;gap:var(--sp-4);
  margin-top:var(--sp-4);padding-top:var(--sp-3);border-top:1px solid var(--line)}
.num-renew{margin-left:0;align-items:flex-start;gap:var(--sp-1)}
.num-renew-row{gap:var(--sp-2)}
.num-renew-row .num-toggle{margin-left:0;gap:var(--sp-2)}
.num-links{margin-left:auto;display:flex;align-items:center;flex-wrap:wrap;gap:var(--sp-4)}
/* Действия — текстовые ссылки, а не кнопки: кнопка обещает поступок, а три пункта из четырёх
   просто открывают окно. Подчёркивание тише самого текста, цвет добирается по наведению. */
.num-link{padding:0;border:0;background:none;cursor:pointer;
  font-family:inherit;font-size:var(--t-s);line-height:1.35;color:var(--text-3);
  text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--line-2);
  transition:color .16s}
.num-link:hover{color:var(--text)}
/* Возврат краснеет ТОЛЬКО под курсором. Раньше это была сплошная красная кнопка — отдельное
   требование заказчика, снятое им же вместе с переносом концепта: постоянный красный кричал на
   каждой живой карточке об исходе, который выбирают единицы, и обесценивал красный там, где он
   действительно значит «осторожно». */
.num-link.danger:hover{color:var(--bad)}
.num-link[disabled]{opacity:.6;cursor:default}
.num-link[disabled]:hover{color:var(--text-3)}
/* Живой отсчёт до закрытия окна возврата дописывается прямо в ссылку. */
.num-refund-left{font-variant-numeric:tabular-nums;opacity:.85}
/* «Срок аренды закончился» — не действие, поэтому и не выглядит нажимаемым. */
.num-inert{font-size:var(--t-s);color:var(--text-3)}

/* Редактор заметки раскрывается ссылкой «Заметка» и живёт прямо над строкой действий: подпись
   спойлера занимала отдельную строку карточки, а её превью слово в слово повторяло заметку,
   которая и так стоит в строке под номером. */

@media(max-width:560px){
  .num-actions{gap:var(--sp-3)}
  /* Ссылки занимают строку целиком и расходятся по краям — единственный способ дать им площадь
     под палец, не превращая обратно в кнопки. */
  .num-links{margin-left:0;width:100%;justify-content:space-between;gap:var(--sp-3)}
  .num-renew{margin-left:0;width:100%;align-items:stretch}
}

/* ---------- история операций: раскрывающаяся строка -------------------------------------------
   Номер операции и выход «повторить то же самое» нужны редко, а места в строке под них нет.
   Раньше номер счёта висел третьей строкой в описании у каждого пополнения и растягивал
   строку вдвое ради значения, которое переписывают раз в месяц и только в поддержку. */
.ops-row{width:100%;text-align:left;background:none;border:0;cursor:pointer;font:inherit;color:inherit}
/* Шапка столбцов не кликается, поэтому шестая ячейка у неё пустая — но она нужна, иначе
   подписи разъедутся с содержимым на ширину шеврона. */
.ops-cols::after{content:""}
.ops-chev{width:16px;height:16px;color:var(--text-3);flex:none;transition:transform .2s,color .16s}
.ops-row:hover .ops-chev{color:var(--text-2)}
.ops-row.is-open .ops-chev{transform:rotate(180deg)}

.ops-details{
  display:flex;align-items:center;flex-wrap:wrap;gap:var(--sp-3) var(--sp-5);
  padding:var(--sp-3) var(--sp-4) var(--sp-4) 60px;
  background:var(--surface-2);border-top:1px solid var(--line);
}
.ops-details[hidden]{display:none}
.ops-field{display:flex;align-items:baseline;gap:var(--sp-2);min-width:0}
.ops-field-lbl{font-size:var(--t-xs);color:var(--text-3);flex:none}
.ops-field-val{display:inline-flex;align-items:center;gap:var(--sp-2);min-width:0;
  font-size:var(--t-s);color:var(--text)}
.ops-field-val code{font-family:var(--font-mono);font-size:var(--t-xs);color:var(--text-2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:24ch}
.ops-go{margin-left:auto;flex:none;font-size:var(--t-s);color:var(--brand-ink);
  text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--line-2)}
.ops-go:hover{text-decoration-color:currentColor}

@media(max-width:760px){
  .ops-details{padding-left:var(--sp-4)}
  .ops-go{margin-left:0;width:100%}
}

/* ---------- редактор заметки: раскрывается ПОД строкой действий ------------------------------
   Панель стоит после ссылки, которая её открывает. Раскрываясь выше, она сдвигала строку
   действий вниз на свою высоту — ссылка уходила из-под пальца, и второе нажатие («свернуть»)
   попадало уже в пустоту.
   Высоту ведёт grid-template-rows 0fr->1fr — тем же приёмом, что и аккордеон стран (.country-body
   выше): раскрытие и закрытие идут ОДНИМ движением, и угадывать max-height не нужно. Раньше
   плавной была только вторая половина: закрытие гнало inline max-height к нулю, а открытие
   просто снимало hidden — из display:none анимировать нечего, и поле выпрыгивало.
   Свёрнутая панель ещё и невидима: иначе поле ввода остаётся в порядке обхода Tab, и курсор
   уезжает в невидимую точку карточки. Видимость снимается ПОСЛЕ движения, а возвращается сразу,
   иначе закрытие шло бы рывком. */
.num-note-edit{display:grid;grid-template-rows:0fr;margin-top:0;visibility:hidden;
  transition:grid-template-rows .28s ease,visibility 0s linear .28s}
.num-note-edit.is-open{grid-template-rows:1fr;visibility:visible;
  transition:grid-template-rows .28s ease,visibility 0s}
.num-note-edit-inner{overflow:hidden;min-height:0}
/* Отступ до поля задан МАРГИНОМ внутри клипующего блока, а не паддингом на нём: паддинг входит
   в его собственную рамку, и свёрнутая панель оставалась бы на 12px выше нуля — тонкая полоса
   пустоты под каждой карточкой. */
.num-note-edit .num-note-input{margin-top:var(--sp-3)}
@media (prefers-reduced-motion:reduce){
  .num-note-edit,.num-note-edit.is-open{transition:none}
}

/* ===== Поддержка: чат во всю доступную высоту ================================================
   Панель высоты не имела вовсе, ограничена была только лента (max-height:min(56vh,560px)):
   переписка обрывалась примерно на 430px, а под ней оставалась пустая половина экрана. Высоту
   задаёт САМА панель, а лента внутри тянется — тогда прокручивается только переписка, а поле
   ввода всегда стоит на месте.

   Вычитаем занятое вокруг: рейка --rail 60px + верхний отступ .content (--sp-6 = 32px) + её
   нижний отступ 90px = 182px. dvh, а не vh: на телефоне адресная строка съезжает, и vh врёт
   ровно на её высоту; строка с vh стоит первой — для браузеров, которые dvh ещё не знают.

   :not(.chat-fullscreen) — на телефоне чат разворачивается на весь экран своим правилом
   (height:100dvh), и эти два правила не должны спорить, в каком бы порядке ни лежали в файле. */
.chat-panel:not(.chat-fullscreen){height:calc(100vh - 182px);height:calc(100dvh - 182px);min-height:460px}
/* min-height:0 обязателен: flex-элемент по умолчанию не сжимается ниже своего содержимого, и
   прокрутка вместо ленты уехала бы на страницу. max-height снимаем — высоту ленты теперь
   определяет остаток панели, а не доля экрана. */
.chat-panel .chat-thread{flex:1;min-height:0;max-height:none}
/* Высоту отдаёт лента, и только она: шапка и строка ввода остаются целыми. */
.chat-panel .chat-header,.chat-panel .chat-compose,.chat-panel .chat-closed-note{flex:none}

@media(max-width:900px){
  /* 60px рейка + 24px верхний отступ .content + 72px полоса вкладок = 156px. */
  .chat-panel:not(.chat-fullscreen){
    height:calc(100vh - 156px - env(safe-area-inset-bottom,0px));
    height:calc(100dvh - 156px - env(safe-area-inset-bottom,0px));
    min-height:380px}
  /* Узкому экрану пузырь нужен шире: 78% от 360px — это 280px, в которых переносится каждое
     второе слово. */
  .chat-panel .chat-msg,.chat-panel .chat-typing{max-width:88%}
}

/* Подпись у кнопки отправки: на широком экране рядом со значком есть место для слова, и
   действие перестаёт быть догадкой по картинке. На узком остаётся один значок — подпись
   съела бы половину строки ввода. */
.chat-send-lbl{display:none}
@media(min-width:901px){
  .chat-compose .chat-send{display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-2);
    width:auto;padding:0 var(--sp-4);font-family:var(--font-body);font-size:var(--t-s);font-weight:500}
  .chat-send-lbl{display:inline}
}

/* ===== Выпадающий список с поиском (фильтр «Сервис») =========================================
   Системное меню <select> рисует операционная система: на тёмной странице это чужая синяя
   подсветка, чужой шрифт и никакого поиска — а сервисов у аккаунта бывает несколько десятков.
   Разметку списка строит скрипт, здесь только оформление. Настоящий <select> остаётся под ним
   и прячется ровно тогда, когда замена собрана: класс is-combo ставит тот же скрипт. */
.msg-field.is-combo .cab-select{display:none}
.combo{position:relative;width:100%}
.combo-btn{display:flex;align-items:center;gap:var(--sp-2);width:100%;height:40px;padding:0 var(--sp-3);
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-s);
  color:var(--text);font-family:var(--font-body);font-size:var(--t-s);text-align:left;cursor:pointer;
  transition:border-color .16s}
.combo-btn:hover{border-color:var(--line-2)}
/* Раскрытый список — состояние кнопки, а не одной только панели: иначе на самой кнопке ничто
   не говорит, что она сейчас что-то держит открытым. */
.combo.is-open .combo-btn{border-color:var(--line-2)}
.combo-val{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.combo-chev{flex:none;width:14px;height:14px;color:var(--text-3);transition:transform .16s}
.combo.is-open .combo-chev{transform:rotate(180deg)}
.combo-pop{position:absolute;z-index:40;top:calc(100% + var(--sp-1));left:0;right:0;
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r);
  box-shadow:0 20px 44px -20px var(--shadow);overflow:hidden}
.combo-pop[hidden]{display:none}
.combo-search{position:relative;padding:var(--sp-2);border-bottom:1px solid var(--line)}
.combo-find{position:absolute;left:calc(var(--sp-2) + var(--sp-3));top:50%;transform:translateY(-50%);
  width:14px;height:14px;color:var(--text-3);pointer-events:none}
.combo-input{width:100%;height:36px;padding:0 var(--sp-3) 0 var(--sp-6);
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-s);
  color:var(--text);font-family:var(--font-body);font-size:var(--t-s);transition:border-color .16s}
.combo-input::placeholder{color:var(--text-3)}
/* Как у остальных полей кабинета: фокус показывает рамка, без синего ореола. */
.combo-input:focus{outline:none;border-color:var(--line-2)}
/* position:relative — чтобы offsetTop пунктов считался от самого списка: скрипт докручивает
   его к клавиатурному курсору по этим числам. */
.combo-list{position:relative;max-height:240px;overflow-y:auto;overscroll-behavior:contain;
  padding:var(--sp-1);list-style:none;scrollbar-width:thin;scrollbar-color:var(--line-2) transparent}
.combo-opt{display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-2);border-radius:var(--r-s);
  font-size:var(--t-s);color:var(--text-2);cursor:pointer}
/* Пункты не удаляются, а прячутся, — значит display:none обязан победить display:flex выше. */
.combo-opt[hidden]{display:none}
/* Наведение мышью и курсор с клавиатуры подсвечиваются одинаково: это одно и то же место. */
.combo-opt:hover,.combo-opt.is-active{background:var(--surface-2);color:var(--text)}
.combo-opt[aria-selected="true"]{color:var(--text)}
/* Галочка занимает место всегда: иначе выбор пункта сдвигал бы весь список на её ширину. */
.combo-check{flex:none;width:14px;height:14px;color:var(--brand-ink);visibility:hidden}
.combo-opt[aria-selected="true"] .combo-check{visibility:visible}
.combo-opt-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.combo-none{padding:var(--sp-3);text-align:center;font-size:var(--t-s);color:var(--text-3)}
.combo-none[hidden]{display:none}
@media(max-width:900px){
  /* Палец крупнее курсора: строке списка нужна высота, за которую не промахиваются. */
  .combo-opt{padding:var(--sp-3) var(--sp-2)}
  .combo-list{max-height:280px}
}

/* Название сервиса приходит из каталога, а не пишется нами, и оно может оказаться длиннее даты и
   суммы, ради которых строка задумывалась. Значению разрешено сжиматься и обрезаться: без этого
   nowrap выше распирает карточку по ширине экрана на телефоне. */
.profile-stat .ps-value{min-width:0;overflow:hidden;text-overflow:ellipsis}

/* ===== чат поддержки: отметки о судьбе сообщения ==============================================
   Три состояния своего сообщения различались только цветом: одна и та же двойная галочка
   красилась в серый («доставлено») и в синий («прочитано»), а состояния «уходит» не было вовсе.
   Разницу двух приглушённых тонов на телефоне не видно, поэтому состояния различает ФОРМА —
   часы, одна галочка, две галочки, восклицательный знак. Цвет остался, но в одиночку он больше
   ничего не значит. Значки ставит скрипт (support.html), здесь только их вид. */
.chat-ticks{display:inline-flex;align-items:center;margin-left:var(--sp-1);color:var(--text-3);
  transition:color .2s ease}
.chat-ticks svg{display:block;width:15px;height:14px}
/* Часы и восклицательный знак нарисованы кругом: неравные ширина и высота сплющили бы его. */
.chat-ticks[data-state="sending"] svg,
.chat-ticks[data-state="failed"] svg{width:14px;height:14px}
/* Одной галочке ширина двух не нужна: иначе разница в форме читалась бы как лишний отступ. */
.chat-ticks[data-state="sent"] svg{width:13px;height:14px}
.chat-ticks[data-state="read"]{color:var(--brand-ink)}
.chat-ticks[data-state="failed"]{color:var(--bad)}
/* Пока запрос в пути, часы медленно дышат: это признак «идёт», а не «замерло». */
.chat-ticks[data-state="sending"]{animation:tick-wait 1.6s ease-in-out infinite}
@keyframes tick-wait{0%,100%{opacity:.4}50%{opacity:1}}
@media(prefers-reduced-motion:reduce){
  .chat-ticks[data-state="sending"]{animation:none;opacity:.7}
}
/* Судьба сообщения видна и по самому пузырю: значок в мете мелкий, а сообщений на экране много. */
.chat-msg.is-pending .chat-bubble{opacity:.72}
.chat-msg.is-failed .chat-bubble{border-color:var(--bad)}
/* «Повторить» — кнопка, по которой попадают пальцем, а не подчёркнутое слово в 11px. Красным
   она НЕ красится: красное на этом пузыре уже занято — им сказано, что случилось. Кнопка
   говорит, что с этим делать, и должна читаться как действие, а не как вторая тревога. */
.chat-retry{margin-left:var(--sp-2);padding:var(--sp-1) var(--sp-2);border:1px solid var(--line);
  border-radius:var(--r-s);background:var(--surface);color:var(--text-2);cursor:pointer;
  font-family:var(--font-body);font-size:var(--t-xs);line-height:1.2;
  transition:border-color .16s,background .16s,color .16s}
.chat-retry:hover{border-color:var(--line-2);background:var(--surface-2);color:var(--text)}

/* ===== чат поддержки: скрепка, поле и «Отправить» одной высоты ================================
   Ряд стоял неровно: кнопки были прибиты к 40px, а высоту поля определял его собственный текст —
   15px × 1.45 плюс отступы давали 41.75px на десктопе и 43.19px на телефоне, где шрифт полей
   поднят до 16px, чтобы iOS не увеличивал страницу при фокусе. Полтора-три пикселя расхождения и
   есть та самая «неровность»: значки сидели ниже середины поля.

   Теперь высота ряда — одно число, а внутренние отступы поля выводятся ИЗ него; строка
   зафиксирована в пикселях, поэтому арифметика не зависит от того, каким шрифтом набран текст.
   Вторую половину чинит скрипт: он добавляет к scrollHeight рамки, которых тот не считает, —
   без этого поле садилось на два пикселя на первом же нажатии. */
.chat-compose{--chat-ctl:40px;--chat-line:22px}
.chat-attach,.chat-send{width:var(--chat-ctl);height:var(--chat-ctl)}
.chat-input{min-height:var(--chat-ctl);line-height:var(--chat-line);
  padding:calc((var(--chat-ctl) - var(--chat-line))/2 - 1px) var(--sp-3)}
/* На телефоне по этому ряду попадают пальцем, а не курсором: 44px — привычный минимум, ниже
   которого промахиваются мимо скрепки. Отступы поля пересчитаются сами. */
@media(max-width:560px){
  .chat-fullscreen .chat-compose{--chat-ctl:44px}
}

/* ===== «Продлеваем…» рядом со сроком =========================================================
   Между концом прежнего срока и появлением нового это поле пустовало: «осталось» уже
   нечего, а статус остаётся «активен». Пустота читалась как поломка, поэтому там стоит слово и
   значок-подсказка — та же самая .num-hint, что у переключателя автопродления. Своих цветов,
   размеров и анимаций у неё нет и быть не должно: всё берётся из правил .num-hint выше.

   Обёртка нужна ровно для одного: держать слово и значок вместе. У .num-side своё расстояние
   между детьми (--sp-3), и без обёртки значок отходил бы от слова на 12px + собственные 6px, а на
   узком экране (.num-side разрешает перенос) мог уехать на строку ниже — подсказка отдельно от
   того, к чему она относится. */
.num-dates-wrap{display:inline-flex;align-items:center;min-width:0}
/* На телефоне .num-row встаёт колонкой, и значок оказывается ПОСЕРЕДИНЕ строки: слева от него
   статус и слово «Продлеваем…», справа — пустое место до края карточки. Ни одна из двух готовых
   привязок туда не годится: базовая (right:-8px, вверх-влево) уводит 240px за левый край экрана,
   зеркальная от .num-renew (left:-8px) — за правый. Измерено в браузере на 390px: 428px правого
   края против 390px экрана.
   Поэтому попап центрируется по самому значку — тогда он не зависит от того, какой длины слева
   оказались статус и слово (а по-английски они короче). Движение и его длительность те же:
   подъём на 7px остаётся, к нему добавлен только сдвиг на половину ширины, иначе transform
   перебил бы анимацию показа. Стрелка съезжает под центр — она и должна смотреть на значок. */
@media(max-width:560px){
  .num-side .num-hint .num-hint-pop{width:min(240px,calc(100vw - 40px));left:50%;right:auto;
    transform:translate(-50%,-7px)}
  .num-side .num-hint:hover .num-hint-pop,
  .num-side .num-hint:focus .num-hint-pop{transform:translate(-50%,0)}
  .num-side .num-hint .num-hint-pop::after{left:calc(50% - 6px);right:auto}
}

/* ===== Уведомления в Telegram: билет с ключом ==================================================
   Ключ показывается ровно один раз — в ответе на нажатие, — и это единственное место в кабинете,
   где человеку дают код для переноса в другое приложение. Поэтому он и выглядит как отрывной
   билет: крупная моноширинная строка с широким трекингом на своей подложке, чтобы его можно было
   прочитать с экрана и набрать в телефоне не глядя. Отрывная кромка — насечка снизу, она же
   отделяет ключ от инструкции. Никаких других вольностей блок себе не позволяет: он живёт в той
   же системе панелей, что и остальная страница. */
.notify-ticket{margin-top:14px;border:1px solid var(--line-2);border-radius:var(--r);
  background:var(--surface-2);overflow:hidden}
.notify-ticket-key{padding:22px 18px 20px;text-align:center;font-size:30px;font-weight:600;
  letter-spacing:.24em;color:var(--text);background:
    radial-gradient(circle at 50% 100%, transparent 6px, var(--surface-3, var(--surface-2)) 6px) 0 100%/22px 12px repeat-x,
    var(--surface-3, var(--surface-2));
  border-bottom:1px dashed var(--line-2);user-select:all}
.notify-ticket-body{padding:14px 18px 16px}
.notify-steps{margin:0;padding-left:18px;color:var(--text-2);font-size:var(--t-sm);line-height:1.7}
.notify-steps li{margin:0}
.notify-ticket-foot{display:flex;align-items:center;gap:12px;margin-top:12px;flex-wrap:wrap}
.notify-ticket-actions{margin-left:auto;display:flex;gap:8px}
.btn-sm{padding:6px 12px;font-size:var(--t-sm)}
.notify-pending{margin-top:10px}

@media(max-width:560px){
  /* На телефоне ключ переносится по группам, а не режется пополам: набирать его будут именно там. */
  .notify-ticket-key{font-size:24px;letter-spacing:.18em;word-break:break-word}
  .notify-ticket-actions{margin-left:0;width:100%}
  .notify-ticket-actions .btn{flex:1}
}
