/* ---------------------------------------------------------------------
   Оформление мини-аппа.

   Вся палитра — в блоке :root ниже. Перекрасить приложение целиком
   значит поправить эти двадцать строк, по остальному файлу цвета
   не раскиданы.

   Тему берём свою, а не из Telegram. Переменные Telegram
   (--tg-theme-bg-color и прочие) в разных клиентах заполнены
   по-разному, и приложение на них выглядит каждый раз иначе.
   --------------------------------------------------------------------- */
:root {
  /* Зима. Фон не нейтрально-чёрный, а с синевой: на нём белый снег
     читается как снег, а не как пыль на экране. */
  --bg:          #060B12;
  --bg-deep:     #04070D;
  --surface:     #0F1621;
  --surface-2:   #16202D;
  --surface-3:   #1E2A3A;
  --line:        rgba(255, 255, 255, .065);
  --line-2:      rgba(255, 255, 255, .13);

  /* Стекло: плашка, сквозь которую видно фон приложения. Держит её не
     заливка, а волосяная обводка и блик по верхнему краю. На нём живут
     карточки «Топ результатов», «Прибыли» и «Медиа» — набором, а не
     порознь по файлу: иначе одинаковые на вид карточки разъедутся по
     оттенку при первой же правке. */
  --glass:       linear-gradient(150deg, rgba(14, 30, 52, .5), rgba(6, 14, 26, .36));
  --glass-line:  rgba(140, 200, 255, .18);
  --glass-edge:  0 14px 34px -26px #000, inset 0 1px 0 rgba(255, 255, 255, .07);

  /* Плашка с подложкой: счётчики, услуги, инструменты и прочие
     карточки, которые стоят на сплошной заливке. Сквозь неё ничего не
     просвечивает — небо лежит внутри самой плашки, своей картинкой,
     и видно его еле-еле. Так же, как узор у карточек участников:
     картинка в карточке, а не дыра в ней.

     Слои сверху вниз: завеса, небо, цвет плашки. Заметность неба —
     четвёртым разрядом в завесе: ближе к единице глуше, ближе к нулю
     ярче. Адрес картинки приезжает из index.html: в css он остался бы
     без отпечатка статики, а /static/ отдаётся immutable на год. */
  --plate:       linear-gradient(rgba(15, 22, 33, .58), rgba(15, 22, 33, .58)),
                 var(--zvezdy, none) center / cover no-repeat,
                 var(--surface);

  /* текст */
  --ink:         #EEF4FA;
  --ink-dim:     #8FA3B8;
  --ink-faint:   #5A6B7E;

  /* акцент: лёд и морозная дымка */
  --brand:       #58AEFF;
  --brand-2:     #7FE3F5;
  --brand-soft:  rgba(88, 174, 255, .14);
  --brand-line:  rgba(88, 174, 255, .34);

  /* состояния */
  --ok:          #3ED9A8;
  --ok-soft:     rgba(62, 217, 168, .13);
  --warn:        #FFC06A;
  --warn-soft:   rgba(255, 192, 106, .13);
  --bad:         #FF6B7E;
  --bad-soft:    rgba(255, 107, 126, .13);

  /* Завеса над фоновой картинкой. Картинка яркая и с крупным логотипом
     посередине — без затемнения карточки на ней не читаются. Вверху
     тоньше, чтобы фон был виден, внизу плотнее: там панель и длинные
     списки. Крутить яркость фона — этими двумя числами, четвёртый
     разряд ближе к единице делает темнее. */
  --veil-top:    rgba(6, 11, 18, .84);
  --veil-bottom: rgba(4, 7, 13, .94);

  /* Лёд витрины. Карточки главной рисуются стилями целиком, без единой
     картинки: силуэт гряды — строка svg, снежная крошка — точки
     градиентом, свечение — обычные тени. Положить настоящие фотографии
     можно позже и не трогая вёрстку: см. --art у .door__art ниже. */
  --ice-line:    rgba(140, 200, 255, .24);
  --ice-lip:     rgba(255, 255, 255, .14);
  /* Две гряды, дальняя светлее ближней — так в карточке появляется
     глубина. preserveAspectRatio='none': картинку тянем по ширине
     карточки, горы от этого только разнообразнее. */
  --ridge-far:   url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 90' preserveAspectRatio='none'><path d='M0 90V70L36 48L64 64L96 40L130 66L164 48L198 70L232 52L266 72L300 56L320 66V90Z' fill='%230E2742'/></svg>");
  --ridge-near:  url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 90' preserveAspectRatio='none'><path d='M0 90V58L28 32L48 50L74 20L104 54L128 36L156 64L182 42L212 68L236 52L268 76L292 58L320 78V90Z' fill='%23061322'/></svg>");
  /* Крупа на ледяной кайме. Фильтр svg рисует шум прямо в строке
     адреса — отдельного файла под фактуру заводить не из-за чего.
     Без неё кайма выглядит гладкой полоской пластика. */
  --frost: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='90' height='90'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.86  0 0 0 0 0.95  0 0 0 0 1  0 0 0 0.5 0'/></filter><rect width='90' height='90' filter='url(%23n)'/></svg>");

  /* Крошка расставлена руками, а не плиткой: повтор на карточке в
     полторы сотни точек читается как сетка, а не как снег. */
  --dust:
    radial-gradient(1.4px 1.4px at 14% 22%, rgba(255, 255, 255, .62), transparent),
    radial-gradient(1px   1px   at 33% 64%, rgba(255, 255, 255, .40), transparent),
    radial-gradient(1.6px 1.6px at 58% 17%, rgba(255, 255, 255, .50), transparent),
    radial-gradient(1px   1px   at 72% 47%, rgba(255, 255, 255, .38), transparent),
    radial-gradient(1.2px 1.2px at 88% 29%, rgba(255, 255, 255, .52), transparent),
    radial-gradient(1px   1px   at 46% 83%, rgba(255, 255, 255, .30), transparent),
    radial-gradient(1.3px 1.3px at 24% 45%, rgba(255, 255, 255, .34), transparent),
    radial-gradient(1px   1px   at 92% 69%, rgba(255, 255, 255, .30), transparent);

  /* геометрия */
  --r-sm:        12px;
  /* Верхний отступ экрана. Названо, потому что на него же вылезает
     баннер отрицательным margin — два места должны меняться вместе. */
  --screen-top:  16px;
  --r:           18px;
  --r-lg:        24px;
  --pad:         16px;

  /* движение */
  --ease:        cubic-bezier(.22, .78, .24, 1);
  /* Своя кривая на появление. У --ease длинный хвост: последняя треть
     времени уходит на последние проценты пути, и блок доползает к месту
     долями точки за кадр. Глазу это читается не как торможение, а как
     рывок перед остановкой — ровно то место, где движение «рвалось».
     Здесь хвост короче, блок доезжает и встаёт. */
  --ease-up:     cubic-bezier(.21, .62, .3, 1);
  --fast:        .18s;
  --slow:        .36s;
  --rise:        .3s;

  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI Variable Text",
          "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* Отступы под вырез и домашнюю полоску. Telegram с версии 8.0 отдаёт
     свои значения; где их нет — берём системные env(). */
  --safe-top:    max(env(safe-area-inset-top), var(--tg-safe-area-inset-top, 0px));
  --safe-bottom: max(env(safe-area-inset-bottom), var(--tg-safe-area-inset-bottom, 0px));
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  /* Скроллим не страницу, а .scroll внутри: иначе вебвью Telegram
     закрывает мини-апп на оттяжке вниз. */
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
}

/* Фон висит на html, а не на body, и это важно. Снег и подсветка лежат
   на отрицательных слоях (z-index -1 и -2), а фон body по порядку
   отрисовки идёт ПОЗЖЕ них — непрозрачный body просто закрыл бы собой
   и то и другое. */
html { background: var(--bg); }

body {
  margin: 0;
  background: transparent;
  color: var(--ink);
  font: 400 15px/1.45 var(--font);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  touch-action: manipulation;
}

/* Фон складывается из трёх вещей, сверху вниз: два холодных пятна по
   углам, завеса и сама картинка. Фиксированный, при скролле не едет.
   z-index -2, чтобы снег (-1) шёл поверх фона, но под содержимым.

   Размытия нет намеренно: слой во весь экран с filter: blur пересчитывался
   бы на каждом кадре снега, а это ровно тот случай, когда на телефоне
   начинает дёргаться прокрутка. Картинку глушит завеса, не размытие.

   --fon приезжает из index.html: там в адрес подставляется отпечаток
   статики. Задай мы его здесь, адрес остался бы без версии, а /static/
   отдаётся с immutable на год — подменённый файл не доехал бы до
   телефонов до следующего года. */
body::before {
  content: "";
  position: fixed; inset: 0; z-index: -2; pointer-events: none;
  background:
    radial-gradient(90% 55% at 12% -8%,  rgba(88, 174, 255, .20), transparent 60%),
    radial-gradient(80% 50% at 95% 4%,   rgba(127, 227, 245, .11), transparent 62%),
    linear-gradient(180deg, var(--veil-top) 0%, var(--veil-bottom) 55%),
    /* Без background-attachment: fixed — слой и так на fixed-элементе,
       а на iOS это свойство и тормозит, и местами просто игнорируется. */
    var(--fon, none) center / cover no-repeat,
    var(--bg);
}

/* Снег идёт за всем содержимым и не скроллится. Карточки его закрывают,
   видно в промежутках — как за сообщениями в Telegram. */
.snow {
  position: fixed; inset: 0; z-index: -1;
  width: 100%; height: 100%;
  pointer-events: none;
}
.snow[hidden] { display: none; }

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

svg:not(.sprite) {
  width: 20px; height: 20px;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
  flex: none;
}

h1, h2, h3, p, ol, ul { margin: 0; }
b, strong { font-weight: 650; }
button { font: inherit; color: inherit; border: 0; background: none; cursor: pointer; }

.num { font-variant-numeric: tabular-nums; }


/* ——— заставка ———————————————————————————————————————————— */
.splash {
  position: fixed; inset: 0; z-index: 50;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 22px;
  background: var(--bg);
  transition: opacity var(--slow) var(--ease);
}
.splash.is-off { opacity: 0; pointer-events: none; }
.splash__note { color: var(--ink-faint); font-size: 13.5px; }

/* ——— знак с логотипом ————————————————————————————————————— */
/* Один вид на заставке и на заслонке: это те два экрана, где человек
   видит сервис до того, как увидит его содержимое. Фон логотипа чёрный
   и совпадает с нашим — шва на скруглении не видно. */
.mark {
  position: relative;
  width: 92px; height: 92px; border-radius: 30px;
  background: var(--surface-2);
  box-shadow: 0 20px 50px -18px rgba(88, 174, 255, .6);
  animation: rise var(--rise) var(--ease-up) both;
}
.mark--big { width: 108px; height: 108px; border-radius: 34px; }

.mark__img {
  display: block; width: 100%; height: 100%;
  border-radius: inherit; object-fit: cover;
}

/* кольцо вокруг знака медленно дышит */
.mark::after {
  content: ""; position: absolute; inset: -14px;
  border-radius: 42px; border: 1px solid var(--brand-line);
  animation: breathe 3.4s var(--ease) infinite;
}
@keyframes breathe {
  0%, 100% { transform: scale(1);    opacity: .7;  }
  50%      { transform: scale(1.07); opacity: .25; }
}


/* ——— заслонка: открыли не из Telegram ————————————————————— */
/* Показывается вместо всего остального. Фронт при этом не грузится:
   boot.js останавливается до api.js и app.js. */
.gate {
  position: fixed; inset: 0; z-index: 55;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 13px; padding: 36px 28px;
  padding-top: calc(36px + var(--safe-top));
  padding-bottom: calc(36px + var(--safe-bottom));
  background: var(--bg); text-align: center;
}
.gate[hidden] { display: none; }

.gate .mark { margin-bottom: 6px; }
.gate__title {
  font-size: 21px; font-weight: 680; letter-spacing: -.02em;
  animation: rise var(--rise) var(--ease-up) both; animation-delay: 60ms;
}
.gate__text {
  color: var(--ink-dim); font-size: 14.5px; line-height: 1.5; max-width: 32ch;
  animation: rise var(--rise) var(--ease-up) both; animation-delay: 105ms;
}
.gate .btn {
  text-decoration: none;
  animation: rise var(--rise) var(--ease-up) both; animation-delay: 150ms;
}
.gate .btn[hidden] { display: none; }
.gate__note {
  margin-top: 2px; color: var(--ink-faint); font-size: 12.5px; max-width: 30ch;
  animation: rise var(--rise) var(--ease-up) both; animation-delay: 195ms;
}

/* ——— экран ошибки ————————————————————————————————————————— */
.fatal {
  position: fixed; inset: 0; z-index: 60;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 12px; padding: 32px 26px;
  padding-bottom: calc(32px + var(--safe-bottom));
  background: var(--bg); text-align: center;
}
.fatal[hidden] { display: none; }
.fatal__ico { width: 44px; height: 44px; color: var(--bad); stroke-width: 1.5; }
.fatal__title { font-size: 19px; font-weight: 650; }
.fatal__text { color: var(--ink-dim); font-size: 14.5px; max-width: 30ch; }
.fatal__raw {
  max-width: 100%; max-height: 32vh; overflow: auto; margin: 4px 0 0;
  padding: 10px 12px; border: 1px solid var(--line);
  border-radius: var(--r-sm); background: var(--surface);
  color: var(--ink-faint); font: 400 11.5px/1.5 var(--mono);
  text-align: left; white-space: pre-wrap; word-break: break-word;
}


/* ——— каркас ——————————————————————————————————————————————— */
/* position: relative — чтобы плавающая нижняя панель считала свои
   края от окна приложения, а не от страницы целиком. */
.app { position: relative; display: flex; flex-direction: column; height: 100%; }
.app[hidden] { display: none; }


/* Шапки над ним нет, поэтому вырез обходит сам список: баннер уходит
   под него только на тех телефонах, где Telegram не закрывает вырез
   своей полосой. Обычно здесь ноль. */
.scroll {
  flex: 1; min-height: 0;
  padding-top: var(--safe-top);
  overflow-y: auto; overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  /* Полоски прокрутки нет. На телефоне её и так не видно, а в
     десктопном Telegram и в браузере она отъедает край у баннера и
     карточек — те идут во всю ширину, и полоска ложится прямо на них. */
  scrollbar-width: none;
}
.scroll::-webkit-scrollbar { width: 0; height: 0; }

.screen {
  display: flex; flex-direction: column; gap: 10px;
  padding: var(--screen-top) var(--pad) 0;
  /* место под плавающую панель: её высота плюс отступ снизу */
  padding-bottom: calc(100px + var(--safe-bottom));
}
.screen[hidden] { display: none; }
/* Каждый экран проявляется сам. Отдельного класса на переход не нужно:
   возврат из display: none перезапускает анимацию, а переключаем мы
   экраны именно так. Без этого подэкраны админки подменялись
   щелчком — содержимое менялось, а хода не было видно вовсе.

   Сдвига у экрана нет намеренно, только прозрачность. Внутри едут свои
   блоки (.rise), и два хода друг в друге — это два слоя, вложенных один
   в другой: в тот кадр, когда заканчивается внешний, браузер убирает
   его слой и перекладывает всё, что лежало внутри. Блоки, которые ещё
   едут, в этот кадр дёргаются. Так и выглядел разрыв между концом
   анимации и местом, куда блок встаёт. */
.screen:not([hidden]) { animation: screen-in var(--fast) linear both; }
@keyframes screen-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
/* На витрине блоки крупнее, и десяти точек между ними мало: карточки
   начинают читаться как один слипшийся столбик. Поля по бокам, наоборот,
   уже обычных: при шестнадцати точках на карточку остаётся полторы сотни,
   и подпись из трёх слов расползается на четыре строки. --pad правит
   сразу и отступы экрана, и вылет баннера за них. */
.screen--hub { gap: 13px; }
/* Узкие поля. Карточкам здесь важна каждая точка ширины: на витрине в
   них подпись из трёх слов, в результатах — название банка и сумма
   в одной строке. */
.screen--wide { --pad: 10px; }

.head {
  margin: 12px 2px 2px;
  font-size: 12.5px; font-weight: 650;
  letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-faint);
}


/* ——— главная: баннер ——————————————————————————————————————— */
/* Пропорции задаёт сам файл (1280×427), а не наоборот: баннер — готовая
   композиция, фонари по краям и снеговик по центру. Обрезка под более
   высокий прямоугольник отрезала бы края.

   Во всю ширину — значит вылезти из отступов .screen: вверх на padding-top,
   по бокам на --pad. Поэтому скруглений и боковой обводки нет, они висели
   бы за краем экрана; от прежней рамки осталась нижняя волосяная линия —
   она отделяет картинку от содержимого. */
.hero {
  position: relative; overflow: hidden;
  margin: calc(var(--screen-top) * -1) calc(var(--pad) * -1) 2px;
  aspect-ratio: 1280 / 431;
  border-bottom: 1px solid var(--brand-line);
  /* цвет взят из самого баннера: пока картинка едет, на её месте
     не дыра, а тёмно-синее поле ровно того же тона */
  background: linear-gradient(170deg, #0C1B33, #061020);
  box-shadow: 0 18px 44px -26px rgba(88, 174, 255, .55);
}
.hero__img {
  display: block; width: 100%; height: 100%;
  /* страховка: при дробной ширине экрана коробка округлится, картинка нет */
  object-fit: cover;
}
/* ——— главная: витрина разделов ————————————————————————————— */
/*
   Карточка — это две коробки. Внешняя (.door / .lane) рисует ледяную
   кайму: светлый полупрозрачный градиент, волосяная обводка и свечение
   наружу. Внутренняя (.door__in / .lane__in) режет фотографию по своему,
   меньшему радиусу. Одной коробкой это не делается: кайма должна быть
   светлее снимка и выходить за его край свечением.

   Фон внутри собирается слоями, сверху вниз:

     1. --art            фотография раздела, адрес приезжает из
                         index.html (там к нему подставляется отпечаток
                         статики, иначе подменённый файл не доехал бы
                         до телефонов: /static/ отдаётся immutable)
     2. --dust           снежная крошка
     3. две гряды гор    --ridge-near и --ridge-far, строкой svg
     4. свечение из угла
     5. подложка         от --art-a к общему тёмному низу

   Слои 2–5 — запасной вид на случай, если фотографии нет: она лежит
   выше и непрозрачна, так что при наличии снимка их не видно. Поэтому
   новый раздел можно добавить без картинки, он не будет выглядеть
   поломанным, а фотографию дослать потом одной строкой.
*/
.doors { display: grid; grid-template-columns: 1fr 1fr; gap: 11px; }
.lanes { display: flex; flex-direction: column; gap: 11px; }

/* Кайма в полторы точки. Толстая съедала карточку и читалась как
   рамка для фото, а не как намёрзший лёд. Поверх градиента лежит
   крупа --frost: сама по себе гладкая полоска выглядит пластиком. */
.door, .lane, .bay {
  position: relative;
  display: block; padding: 1.5px; width: 100%;
  border-radius: 21px;
  text-align: left; color: inherit; font: inherit; cursor: pointer;
  background:
    var(--frost),
    linear-gradient(150deg,
      rgba(206, 243, 255, .72) 0%,
      rgba(110, 182, 236, .32) 34%,
      rgba(158, 220, 255, .36) 63%,
      rgba(218, 247, 255, .68) 100%);
  box-shadow:
    0 0 0 1px rgba(176, 226, 255, .34),
    0 0 22px -6px rgba(76, 172, 255, .5),
    0 18px 38px -24px #000;
  transition: box-shadow var(--fast) var(--ease);
}
.door:active, .lane:active, .bay:active {
  box-shadow:
    0 0 0 1px rgba(214, 242, 255, .7),
    0 0 30px -4px rgba(96, 190, 255, .8),
    0 18px 38px -24px #000;
}
.door:focus-visible, .lane:focus-visible, .bay:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 3px;
}

.door__in, .lane__in, .bay__in {
  position: relative; isolation: isolate; overflow: hidden;
  border-radius: 19.5px;
  background: #060E1A;
}
.door__in {
  display: flex; flex-direction: column; justify-content: flex-end;
  min-height: 86px; padding: 10px 12px;
}
.lane__in {
  display: flex; align-items: center; gap: 13px;
  min-height: 80px; padding: 11px 13px;
}

/* Какой карточке какой снимок. Сами адреса — в index.html. */
.door--manuals { --art: var(--art-manuals); --art-a: #1B4F8C; }
.door--connect { --art: var(--art-connect); --art-a: #14416E; }
.door--draw    { --art: var(--art-draw);    --art-a: #1D5A82; }
.door--media   { --art: var(--art-media);   --art-a: #17487E; }
.lane--results { --art: var(--art-results); --art-a: #1C5590; }
.lane--profit  { --art: var(--art-profit);  --art-a: #164A7D; }
.lane--support { --art: var(--art-support); --art-a: #1A5176; }

/* Подложка — она же запасной вид, если снимка нет. */
.door__art, .lane__art, .bay__art {
  position: absolute; inset: 0; z-index: 0;
  background:
    var(--dust),
    var(--ridge-near) bottom / 100% 54% no-repeat,
    var(--ridge-far)  bottom / 100% 68% no-repeat,
    radial-gradient(120% 92% at 78% 6%, rgba(120, 200, 255, .34), transparent 62%),
    linear-gradient(165deg, var(--art-a, #17457A), #050D1A);
}

/* У квадратной карточки снимок занимает её целиком и ложится поверх
   подложки первым слоем. */
.door__art {
  background:
    var(--art, none) center / cover no-repeat,
    var(--dust),
    var(--ridge-near) bottom / 100% 54% no-repeat,
    var(--ridge-far)  bottom / 100% 68% no-repeat,
    radial-gradient(120% 92% at 78% 6%, rgba(120, 200, 255, .34), transparent 62%),
    linear-gradient(165deg, var(--art-a, #17457A), #050D1A);
}

/* А у полосы — нет. В макете фотография не растянута на всю кнопку:
   левая четверть остаётся чистым градиентом, снимок лежит справа и
   растворяется к середине. Поэтому он отдельным слоем с маской, а не
   фоном всей коробки. */
.lane__art::before {
  content: ""; position: absolute; inset: 0 0 0 26%;
  background: var(--art, none) right center / cover no-repeat;
  -webkit-mask-image: linear-gradient(90deg, transparent 0%,
                                      rgba(0, 0, 0, .55) 20%, #000 46%);
          mask-image: linear-gradient(90deg, transparent 0%,
                                      rgba(0, 0, 0, .55) 20%, #000 46%);
}

/* Затемнение поверх снимка. У квадратной карточки текст внизу — гасим
   низ; у полосы текст слева — гасим левый край. Без этого белые буквы
   попадают то на снег, то на фонарь, и читаются через раз. */
.door__art::after, .lane__art::after, .bay__art::after {
  content: ""; position: absolute; inset: 0;
}
.door__art::after {
  background: linear-gradient(180deg,
    rgba(4, 9, 18, .10) 0%, rgba(4, 9, 18, .32) 44%, rgba(3, 8, 16, .92) 88%);
}
.lane__art::after {
  background: linear-gradient(90deg,
    rgba(4, 9, 18, .80) 0%, rgba(4, 9, 18, .52) 38%, rgba(4, 9, 18, 0) 68%);
}

.door__body, .lane__body, .door__go, .lane__go, .lane__ico,
.bay__body, .bay__go, .bay__ico {
  position: relative; z-index: 1;
}

/* Белые буквы со свечением, как в макете: градиентная заливка на
   кегле в шестнадцать точек превращается в грязь, а снимок под
   текстом и так холодный. */
.door__title, .lane__title, .bay__title {
  color: #FFFFFF; font-weight: 700; letter-spacing: -.02em;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .85), 0 0 18px rgba(96, 184, 255, .55);
}

/* Место под кружок со стрелкой отводит подпись, а не весь блок:
   «Подключиться» — одно слово, переносить его негде, и в остаток
   ширины оно не влезало. Над стрелкой заголовку никто не мешает. */
.door__body {
  display: flex; flex-direction: column; gap: 3px; min-width: 0;
}
.door__title {
  font-size: 16px; line-height: 1.15;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Ровно две строки у всех четырёх карточек, не больше и не меньше.
   Иначе подписи разной длины разгоняют текст по высоте, и в ряду
   заголовки стоят на разном уровне — видно сразу. Длинная подпись
   режется многоточием: править её в разметке, а не здесь. */
.door__hint {
  padding-right: 40px;
  color: rgba(214, 231, 246, .82); font-size: 9.8px; line-height: 1.3;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .8);
  height: 2.6em;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Кольцо со стрелкой: тонкая светлая линия, прозрачная середина и
   свечение по краю — внутри него виден сам снимок. */
.door__go, .lane__go, .bay__go {
  position: absolute;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1.5px solid rgba(214, 240, 255, .82);
  background: rgba(8, 20, 38, .3);
  box-shadow: 0 0 14px -2px rgba(120, 200, 255, .65),
              inset 0 0 12px -5px rgba(150, 215, 255, .9);
  color: #EAF7FF;
}
.door__go { right: 9px; bottom: 9px; width: 34px; height: 34px; }
.door__go svg { width: 16px; height: 16px; stroke-width: 2; }

/* ——— главная: широкие полосы ———————————————————————————————— */
/* Значок в ледяной обойме. Светится так же, как кайма карточки —
   иначе он выглядит наклейкой поверх чужого снимка. */
.lane .lane__ico {
  width: 54px; height: 54px; flex: none; border-radius: 17px;
  display: grid; place-items: center;
  border: 1.5px solid rgba(190, 232, 255, .72);
  background: linear-gradient(160deg, rgba(42, 112, 192, .52), rgba(8, 22, 44, .74));
  box-shadow: 0 0 16px -4px rgba(110, 195, 255, .75),
              inset 0 0 14px -6px rgba(170, 225, 255, .9);
  color: #F0FAFF;
}
.lane__ico svg { width: 26px; height: 26px; stroke-width: 1.8; }

.lane__body {
  display: flex; flex-direction: column; gap: 2px;
  flex: 1; min-width: 0; padding-right: 42px;
}
/* Надпись над названием. Одна и та же на всех полосах — она не
   поясняет раздел, а держит их вместе как один блок. */
.lane__kick {
  font-size: 8.5px; font-weight: 650; letter-spacing: .2em;
  text-transform: uppercase; color: rgba(184, 214, 240, .82);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .8);
}
.lane__title { font-size: 19.5px; line-height: 1.14; letter-spacing: -.025em; }
.lane__hint {
  color: rgba(206, 224, 242, .76); font-size: 10.5px; line-height: 1.3;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .8);
}

.lane__go {
  right: 11px; top: 50%; transform: translateY(-50%);
  width: 40px; height: 40px;
}
.lane__go svg { width: 18px; height: 18px; stroke-width: 2; }


/* ——— команда: входы ————————————————————————————————————————— */
/*
   Та же карточка, что на витрине, только во всю ширину и лежащая:
   значок слева, текст посередине, кольцо со стрелкой справа. Отсюда
   и общие правила с .door и .lane выше — кайма, обрезка снимка,
   свечение и подпись у них одни на троих.

   Снимки сняты с присланных кадров изнутри: в каждом из них уже
   нарисована своя светящаяся рамка, и под нашей каймой рамок было бы
   две (см. INSIDE в tools/make_art.py).

   Карточка тимлида золотая, а не ледяная, и это не каприз: три
   одинаковых входа подряд взгляду не за что различить. Золото
   отделяет вход тимлида от входа трейдера быстрее любой подписи —
   и совпадает с короной на самом снимке.
*/
.bays { display: flex; flex-direction: column; gap: 11px; }

.bay__in {
  display: flex; align-items: center; gap: 13px;
  min-height: 94px; padding: 12px 13px;
}

.bay--join   { --art: var(--art-join);   --art-a: #15457C; }
.bay--trader { --art: var(--art-trader); --art-a: #11457E; }
.bay--lead   { --art: var(--art-lead);   --art-a: #45350F; }

/* Снимок занимает карточку целиком и лежит поверх подложки первым
   слоем — как у квадратной карточки витрины. */
.bay__art {
  background:
    var(--art, none) center / cover no-repeat,
    var(--dust),
    var(--ridge-near) bottom / 100% 54% no-repeat,
    var(--ridge-far)  bottom / 100% 68% no-repeat,
    radial-gradient(120% 92% at 78% 6%, rgba(120, 200, 255, .34), transparent 62%),
    linear-gradient(165deg, var(--art-a, #17457A), #050D1A);
}
/* Завеса плотная слева, где текст, и почти прозрачная справа — там
   кольцо со стрелкой, и сквозь него должен быть виден сам снимок. */
.bay__art::after {
  background: linear-gradient(90deg,
    rgba(4, 9, 18, .88) 0%, rgba(4, 9, 18, .7) 44%, rgba(4, 9, 18, .18) 100%);
}

.bay__ico {
  width: 50px; height: 50px; flex: none; border-radius: 16px;
  display: grid; place-items: center;
  border: 1.5px solid rgba(190, 232, 255, .72);
  background: linear-gradient(160deg, rgba(42, 112, 192, .52), rgba(8, 22, 44, .74));
  box-shadow: 0 0 16px -4px rgba(110, 195, 255, .75),
              inset 0 0 14px -6px rgba(170, 225, 255, .9);
  color: #F0FAFF;
}
.bay__ico svg { width: 24px; height: 24px; stroke-width: 1.8; }

/* Место под кольцо со стрелкой отводит текст, а не вся коробка:
   кольцо сидит в одиннадцати точках от края и само в сорок, и
   подпись в сорок четыре заезжала под него второй строкой. */
.bay__body {
  display: flex; flex-direction: column; gap: 3px;
  flex: 1; min-width: 0; padding-right: 51px;
}
.bay__title { font-size: 17px; line-height: 1.16; letter-spacing: -.025em; }
.bay__hint {
  color: rgba(206, 224, 242, .78); font-size: 11px; line-height: 1.32;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .8);
}
/* Ярлык готовности. Мелкий и в разрядку: он не читается словами,
   а показывает, что вход живой. */
.bay__tag {
  align-self: flex-start; margin-top: 4px;
  padding: 3px 8px; border-radius: 7px;
  border: 1px solid var(--brand-line); background: rgba(88, 174, 255, .16);
  color: #BFE2FF;
  font-size: 8px; font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; white-space: nowrap;
}

.bay__go {
  right: 11px; top: 50%; transform: translateY(-50%);
  width: 40px; height: 40px;
}
.bay__go svg { width: 18px; height: 18px; stroke-width: 2; }

/* Золото тимлида: кайма, обойма значка, кольцо стрелки и ярлык. */
.bay--lead {
  background:
    var(--frost),
    linear-gradient(150deg,
      rgba(255, 240, 206, .74) 0%,
      rgba(226, 176, 92, .34) 34%,
      rgba(246, 212, 146, .38) 63%,
      rgba(255, 244, 216, .7) 100%);
  box-shadow:
    0 0 0 1px rgba(255, 223, 158, .34),
    0 0 22px -6px rgba(255, 186, 84, .5),
    0 18px 38px -24px #000;
}
.bay--lead:active {
  box-shadow:
    0 0 0 1px rgba(255, 238, 196, .7),
    0 0 30px -4px rgba(255, 199, 104, .8),
    0 18px 38px -24px #000;
}
.bay--lead .bay__ico {
  border-color: rgba(255, 226, 170, .72);
  background: linear-gradient(160deg, rgba(186, 136, 46, .54), rgba(40, 27, 7, .76));
  box-shadow: 0 0 16px -4px rgba(255, 200, 110, .75),
              inset 0 0 14px -6px rgba(255, 236, 192, .9);
  color: #FFF4E0;
}
.bay--lead .bay__go {
  border-color: rgba(255, 232, 186, .82);
  box-shadow: 0 0 14px -2px rgba(255, 205, 120, .65),
              inset 0 0 12px -5px rgba(255, 226, 170, .9);
  color: #FFF6E6;
}
.bay--lead .bay__title {
  text-shadow: 0 1px 3px rgba(0, 0, 0, .85), 0 0 18px rgba(255, 196, 102, .5);
}
.bay--lead .bay__tag {
  border-color: rgba(255, 192, 106, .4); background: rgba(255, 192, 106, .15);
  color: #FFD79B;
}


/* ——— команда: ставки ———————————————————————————————————————— */
/*
   Фон прозрачный — сквозь плашку виден фон приложения, держат её
   обводка и блик по верхнему краю, как у карточек «Топ результатов».
   Цифры стоят в разметке: это условия работы, они меняются не чаще
   самого текста приложения, и заводить под них экран в админке
   незачем.
*/
.rates {
  display: flex; flex-direction: column; gap: 11px;
  margin-top: 3px; padding: 13px 13px 14px;
  border-radius: 20px;
  border: 1px solid var(--glass-line);
  background: var(--glass);
  box-shadow: var(--glass-edge);
}
.rates__top {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.rates__top strong { font-size: 18px; font-weight: 700; letter-spacing: -.02em; }
.rates__live {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  padding: 4px 10px; border-radius: 999px;
  border: 1px solid rgba(62, 217, 168, .34); background: var(--ok-soft);
  color: var(--ok); font-size: 10px; font-weight: 650;
}
.rates__live::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: var(--ok); box-shadow: 0 0 8px rgba(62, 217, 168, .9);
}

/* Четыре доли в ряд. Кегль мелкий нарочно: на 360 точках каждой
   достаётся около восьмидесяти, и «Штатный ТЛ» в них помещается
   только так. */
.rates__roles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.role {
  display: flex; flex-direction: column; gap: 1px; min-width: 0;
  padding: 8px 7px 7px; border-radius: 13px;
  border: 1px solid var(--line); background: rgba(8, 17, 31, .5);
}
.role svg { width: 15px; height: 15px; margin-bottom: 3px; }
.role b { font-size: 13px; font-weight: 700; letter-spacing: -.02em; }
.role i {
  font-style: normal; font-size: 8.5px; color: var(--ink-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.role--lead   { border-color: rgba(88, 174, 255, .3); }
.role--staff  { border-color: rgba(127, 227, 245, .3); }
.role--mentor { border-color: rgba(176, 160, 255, .3); }
.role--head   { border-color: rgba(62, 217, 168, .32); }
.role--lead   svg, .role--lead   b { color: var(--brand); }
.role--staff  svg, .role--staff  b { color: var(--brand-2); }
.role--mentor svg, .role--mentor b { color: #B6A6FF; }
.role--head   svg, .role--head   b { color: var(--ok); }

/* Три столбца: сумма тянется, проценты стоят колонками постоянной
   ширины — иначе «0–1%» и «5%» разъезжаются по строкам. */
.rates__grid {
  display: grid; grid-template-columns: 1fr 58px 58px;
  column-gap: 10px;
  padding: 0 2px;
}
.rates__grid > * {
  padding: 9px 0; border-bottom: 1px solid var(--line);
  font-size: 12.5px;
}
/* Последняя строка — без черты: под ней край плашки. */
.rates__grid > :nth-last-child(-n+3) { border-bottom: 0; padding-bottom: 0; }
.rates__th {
  padding-top: 0;
  font-size: 8.5px; font-weight: 650; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-faint);
}
.rates__th:not(:first-child), .rates__in, .rates__out { text-align: right; }
.rates__sum { color: var(--ink-dim); }
.rates__in  { color: var(--brand-2); font-weight: 700; }
.rates__out { color: var(--ok); font-weight: 700; }

.rates__foot {
  margin: 4px 0 0; text-align: center;
  font-size: 8.5px; font-weight: 650; letter-spacing: .2em;
  text-transform: uppercase; color: var(--ink-faint);
}

/* ——— подпись-разделитель с линией ——————————————————————————— */
.rail {
  display: flex; align-items: center; gap: 10px;
  margin: 6px 2px -2px;
  color: var(--ink-faint);
  font-size: 10px; font-weight: 650; letter-spacing: .22em; text-transform: uppercase;
}
.rail::after {
  content: ""; flex: 1; height: 1px;
  background: linear-gradient(90deg, var(--brand-line), transparent);
}

/* ——— топ результаты ————————————————————————————————————————— */
/*
   Карточки здесь почти прозрачные: под ними виден фон приложения, и
   держит их не заливка, а волосяная обводка и узор справа. Сама
   прозрачность — общая, из --glass в :root; узор дальше по файлу
   только у этих двух.

   Узор — присланная картинка, но не как картинка. Из неё собрана маска
   (белое полотно, прозрачность по яркости — см. tools/make_pattern.py),
   а цвет задаёт градиент под ней. Поэтому перекрасить ленты можно
   здесь, не трогая файл, и они всегда в тон остальному льду.
*/
.lead, .mate {
  position: relative; isolation: isolate; overflow: hidden;
  border-radius: 20px;
  border: 1px solid var(--glass-line);
  background: var(--glass);
  box-shadow: var(--glass-edge);
}

/* Лента занимает правые две трети и растворяется влево — за это
   отвечает градиент, а не вторая маска: складывать маски умеют не все
   вебвью, а прозрачный край градиента работает везде одинаково. */
.lead__uzor, .mate__uzor {
  position: absolute; inset: 0 0 0 30%; z-index: 0; pointer-events: none;
  background: linear-gradient(100deg,
    transparent 0%, rgba(118, 196, 255, .26) 40%, rgba(166, 224, 255, .5) 100%);
  -webkit-mask: var(--uzor) right center / cover no-repeat;
          mask: var(--uzor) right center / cover no-repeat;
}

.lead {
  display: flex; flex-direction: column; gap: 2px;
  padding: 15px 16px 16px;
}
.lead > *, .mate__top, .mate__reqs { position: relative; z-index: 1; }

.lead__kick, .mate__role {
  font-size: 8.5px; font-weight: 650; letter-spacing: .2em;
  text-transform: uppercase; color: rgba(150, 196, 235, .76);
}
.lead__title {
  font-size: 21px; font-weight: 700; letter-spacing: -.025em; line-height: 1.14;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .8);
}
.lead__hint {
  padding-right: 54px;
  color: rgba(197, 218, 238, .66); font-size: 10.5px; line-height: 1.3;
}

.team { display: flex; flex-direction: column; gap: 11px; }

.mate { display: flex; flex-direction: column; gap: 11px; padding: 13px; }
.mate__top { display: flex; align-items: center; gap: 12px; }
.mate__face {
  width: 58px; height: 58px; flex: none; border-radius: 17px; overflow: hidden;
  display: grid; place-items: center;
  border: 1px solid rgba(165, 215, 255, .35);
  background: linear-gradient(160deg, rgba(88, 174, 255, .28), rgba(10, 24, 44, .64));
  color: #CFE9FF; font-size: 23px; font-weight: 700;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18);
}
.mate__face img { display: block; width: 100%; height: 100%; object-fit: cover; }
.mate__who { display: flex; flex-direction: column; min-width: 0; }
.mate__nick {
  font-size: 22px; font-weight: 700; letter-spacing: -.03em; line-height: 1.12;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .8);
}
.mate__label {
  margin-top: 4px;
  font-size: 7.5px; font-weight: 650; letter-spacing: .2em;
  text-transform: uppercase; color: rgba(140, 180, 218, .58);
}

/* Две колонки и всё в одну строку: значок, название, сумма. Места в
   обрез, поэтому и кегль мелкий, и отступы узкие — название ужимается
   первым, сумму резать нельзя. */
.mate__reqs { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.req {
  display: flex; align-items: center; gap: 6px; min-width: 0;
  padding: 6px 7px; border-radius: 11px;
  border: 1px solid rgba(255, 255, 255, .07);
  background: rgba(8, 17, 31, .62);
}
.req__ico { width: 20px; height: 20px; flex: none; border-radius: 6px; object-fit: cover; }
.req__name {
  flex: 1; min-width: 0; font-size: 9.5px; color: var(--ink-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.req__sum {
  flex: none; font-size: 9.5px; font-weight: 650;
  white-space: nowrap; font-variant-numeric: tabular-nums;
}


/* ——— рейтинг трейдеров ——————————————————————————————————————— */
/*
   Подиум и список под ним. От того, что у конкурентов, нарочно
   отличается тремя вещами: ступени не золото-серебро-бронза, а лёд
   разной яркости; сверху на каждой лежит снежная шапка; номер места
   в строке — скруглённый квадрат, как и всё остальное в приложении,
   а не кружок.
*/
.rank { display: flex; flex-direction: column; gap: 11px; }
.rank__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; margin: 2px 3px 0;
}
.rank__head strong { font-size: 17px; font-weight: 700; letter-spacing: -.02em; }
.rank__head span { flex: none; font-size: 10.5px; color: var(--ink-faint); }

.podium {
  position: relative; overflow: hidden;
  display: grid; grid-template-columns: 1fr 1.14fr 1fr;
  align-items: end; gap: 8px;
  padding: 14px 12px 0;
  border-radius: 20px;
  border: 1px solid rgba(140, 200, 255, .18);
  background: linear-gradient(170deg, rgba(14, 30, 52, .5), rgba(6, 14, 26, .36));
}
.step { display: flex; flex-direction: column; align-items: center; gap: 5px; min-width: 0; }
.step__sum {
  font-size: 12px; font-weight: 700; white-space: nowrap;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .8);
}
.step__nick {
  max-width: 100%; font-size: 11px; color: var(--ink-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.step__pillar {
  position: relative; width: 100%; margin-top: 3px;
  display: grid; place-items: center;
  border-radius: 14px 14px 0 0;
  border: 1px solid rgba(255, 255, 255, .1); border-bottom: 0;
  font-size: 30px; font-weight: 700; letter-spacing: -.03em;
}
/* Снежная шапка. Лежит поверх верхнего края и выходит за него —
   поэтому у .podium нет нижнего отступа, ступени стоят на дне. */
.step__pillar::before {
  content: ""; position: absolute; left: 5px; right: 5px; top: -3px; height: 8px;
  border-radius: 8px;
  background: linear-gradient(180deg, #FFFFFF, #C9E3F8);
  box-shadow: 0 0 13px -2px rgba(190, 230, 255, .85);
}
.step--1 .step__pillar {
  height: 118px; color: #EAF7FF;
  background: linear-gradient(180deg, rgba(126, 210, 255, .46), rgba(26, 78, 130, .26));
}
.step--2 .step__pillar {
  height: 86px; color: #DCE8F4;
  background: linear-gradient(180deg, rgba(196, 214, 230, .3), rgba(40, 58, 78, .24));
}
.step--3 .step__pillar {
  height: 70px; color: #C6D8EA;
  background: linear-gradient(180deg, rgba(120, 160, 200, .28), rgba(26, 44, 66, .24));
}

/* Отступ здесь, а не в gap у .rank: подиум и список лежат в одной
   коробке, которую наполняет скрипт, и промежутка между ними иначе
   не выходит — они встают впритык. */
.rank__list { display: flex; flex-direction: column; gap: 7px; margin-top: 11px; }
/* Полоска идёт под обеими подписями, а не только под ником: так
   видно, насколько строка отстала, не сравнивая числа глазами. */
.rline {
  display: grid; grid-template-columns: auto 1fr auto;
  align-items: center; column-gap: 10px; row-gap: 6px;
  padding: 9px 11px; border-radius: 15px;
  border: 1px solid var(--line); background: rgba(10, 20, 36, .5);
}
.rline__no {
  grid-row: span 2; width: 32px; height: 32px; border-radius: 10px;
  display: grid; place-items: center;
  background: rgba(88, 174, 255, .14); color: #9FD2FF;
  font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.rline__nick {
  min-width: 0; font-size: 13.5px; font-weight: 650;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rline__sum {
  flex: none; font-size: 12.5px; font-weight: 650;
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.rline__bar {
  grid-column: 2 / -1; height: 4px; border-radius: 3px; overflow: hidden;
  background: rgba(255, 255, 255, .07);
}
.rline__bar i {
  display: block; height: 100%; border-radius: 3px;
  background: linear-gradient(90deg, #3E86D8, #7FE3F5);
}


/* ——— медиа: список роликов ——————————————————————————————————— */
/*
   Блок во всю ширину: сверху превью 16:9, под ним название и кнопка,
   разворачивающая описание. Нет описания — нет и кнопки: пустая
   стрелка, которая ничего не открывает, хуже её отсутствия.

   Ни одного байта самого ролика здесь не качается. В списке только
   превью (webp 640×360, ленивая загрузка), а видео появляется в плеере
   и только после нажатия — см. окно .reel ниже.
*/
/* Переключатель двух половин «Медиа». Половин ровно две, поэтому
   бегунок уезжает трансформом на свою же ширину, а ширину ему даёт
   колонка сетки: мерить кнопки скриптом, как у нижней панели,
   здесь не из-за чего. */
.duo {
  position: relative;
  display: grid; grid-template-columns: 1fr 1fr;
  padding: 4px; border-radius: 17px;
  border: 1px solid var(--line-2); background: var(--surface);
}
.duo[hidden] { display: none; }
.duo__pill {
  grid-area: 1 / 1; border-radius: 13px;
  background: linear-gradient(180deg, rgba(62, 148, 236, .5),
                                      rgba(24, 70, 134, .32));
  border: 1px solid rgba(140, 200, 255, .34);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18),
              0 0 16px -6px rgba(90, 180, 255, .9);
  transition: transform var(--slow) var(--ease);
}
.duo.is-second .duo__pill { transform: translateX(100%); }
.duo__one {
  position: relative; z-index: 1; min-width: 0;
  display: flex; align-items: center; justify-content: center; gap: 7px;
  padding: 10px 6px; border: 0; border-radius: 13px; background: none;
  color: var(--ink-faint); font: inherit; font-size: 13px; font-weight: 650;
  letter-spacing: -.01em; cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: color var(--fast) var(--ease);
}
.duo__one:nth-of-type(1) { grid-area: 1 / 1; }
.duo__one:nth-of-type(2) { grid-area: 1 / 2; }
.duo__one.is-on { color: var(--ink); }
.duo__one svg { width: 16px; height: 16px; flex: none; }

/* Ссылки раздела. Строкой, а не блоком с картинкой: у ссылки нет
   кадра, который стоило бы показывать во всю ширину, — есть значок,
   имя и, если завели, строка пояснения. */
.rows { display: flex; flex-direction: column; gap: 9px; }
.rows[hidden] { display: none; }
.wrow {
  display: flex; align-items: center; gap: 12px;
  padding: 12px; border-radius: var(--r);
  border: 1px solid var(--line); background: var(--plate);
  color: inherit; text-decoration: none;
  transition: border-color var(--fast) var(--ease);
}
.wrow:active { border-color: var(--brand-line); }
.wrow__ico {
  width: 44px; height: 44px; flex: none; overflow: hidden;
  border-radius: 14px; display: grid; place-items: center;
  background: var(--brand-soft); color: var(--brand);
}
.wrow__ico img { display: block; width: 100%; height: 100%; object-fit: cover; }
.wrow__ico svg { width: 20px; height: 20px; }
.wrow__text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.wrow__name {
  font-size: 14.5px; font-weight: 650; letter-spacing: -.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.wrow__sub { font-size: 12px; color: var(--ink-faint); line-height: 1.4; }
.wrow__go { width: 18px; height: 18px; flex: none; color: var(--ink-faint); }

.reels { display: flex; flex-direction: column; gap: 12px; }
/* Половины переключаются через hidden, а display: flex его сам по себе
   не слушает: без этой строки под ссылками остались бы и ролики. */
.reels[hidden] { display: none; }

.clip {
  position: relative; overflow: hidden;
  border-radius: 20px;
  border: 1px solid var(--glass-line);
  background: var(--glass);
  box-shadow: var(--glass-edge);
}
.clip__shot {
  position: relative; display: block; width: 100%;
  aspect-ratio: 16 / 9; overflow: hidden;
  border-radius: 19px 19px 0 0;
  /* Пока превью едет (или его вовсе нет), на месте картинки не дыра,
     а тёмно-синее поле в тон остальному льду. */
  background: linear-gradient(165deg, #11253F, #060E1A);
}
.clip__img { display: block; width: 100%; height: 100%; object-fit: cover; }
.clip__play {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 54px; height: 54px; border-radius: 50%;
  display: grid; place-items: center;
  border: 1px solid rgba(190, 230, 255, .5);
  background: rgba(8, 18, 32, .52);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  color: #EAF7FF;
  box-shadow: 0 0 24px -4px rgba(76, 172, 255, .65);
  transition: transform var(--fast) var(--ease);
}
/* Треугольник оптически тяжелее слева: без сдвига он кажется
   прижатым к левому краю кружка. */
.clip__play svg { width: 21px; height: 21px; margin-left: 3px; }
.clip__shot:active .clip__play { transform: translate(-50%, -50%) scale(.92); }
.clip__secs {
  position: absolute; right: 9px; bottom: 9px;
  padding: 3px 7px; border-radius: 8px;
  background: rgba(4, 9, 16, .76);
  color: #D6E7F6; font-size: 10.5px; font-weight: 650;
  font-variant-numeric: tabular-nums;
}

.clip__bar { display: flex; align-items: center; gap: 10px; padding: 11px 12px; }
.clip__title {
  flex: 1; min-width: 0;
  font-size: 14.5px; font-weight: 650; letter-spacing: -.01em; line-height: 1.3;
}
.clip__more {
  width: 32px; height: 32px; flex: none; border-radius: 10px;
  display: grid; place-items: center;
  background: rgba(88, 174, 255, .12); color: #9FD2FF;
}
.clip__more svg {
  width: 16px; height: 16px; transform: rotate(90deg);
  transition: transform var(--fast) var(--ease);
}
.clip.is-open .clip__more svg { transform: rotate(-90deg); }
/* Коробка, которая и разворачивается. Высота у текста своя, а css
   без числа её не анимирует — число ставит скрипт (fold в app.js),
   здесь только ход и обрезка по дороге. */
.clip__fold {
  height: 0; overflow: hidden;
  transition: height var(--slow) var(--ease);
}
/* Описание — тоже кнопка: свернуть его можно нажатием по самому
   тексту, не целясь в стрелку. */
.clip__about {
  display: block; width: 100%; padding: 0 12px 12px;
  color: var(--ink-dim); font: inherit; font-size: 12.5px; line-height: 1.5;
  text-align: left; white-space: pre-line;
  opacity: 0; transition: opacity var(--fast) var(--ease);
}
/* Текст проявляется чуть позже коробки, иначе он читается прежде,
   чем ему хватило места, и мелькает обрезанным. */
.clip.is-open .clip__about { opacity: 1; transition-delay: 80ms; }
.clip__about::before {
  content: ""; display: block; height: 1px; margin-bottom: 10px;
  background: var(--line);
}

/* Замок: раздел закрыт, пока человек не в команде. Кнопка ведёт
   в «Команду» — там набор, после него раздел откроется. */
.locked {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 34px 22px 28px; border-radius: 20px; text-align: center;
  border: 1px dashed rgba(140, 200, 255, .26);
  background: linear-gradient(170deg, rgba(14, 30, 52, .46), rgba(6, 14, 26, .3));
}
.locked > svg { width: 30px; height: 30px; color: #7FB7E8; stroke-width: 1.5; }
.locked b { font-size: 16px; }
.locked span { max-width: 30ch; color: var(--ink-dim); font-size: 12.5px; line-height: 1.45; }


/* ——— медиа: плеер ———————————————————————————————————————————— */
/*
   Окно поверх раздела. Кнопки свои, а не controls браузера: родные
   в каждом вебвью выглядят по-своему — серая полоса, чужие шрифты,
   и меню из трёх точек со «Скачать» тоже чужое.

   Полоса прячется через секунду после начала и возвращается по
   касанию: на 16:9 она закрывает нижнюю восьмую кадра.
*/
.reel {
  position: absolute; inset: 0; z-index: 45;
  display: flex; align-items: center; justify-content: center;
  padding: calc(var(--safe-top) + 14px) 13px calc(var(--safe-bottom) + 14px);
  background: rgba(3, 6, 11, .84);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
.reel[hidden] { display: none; }
.reel__sheet {
  position: relative; width: 100%; max-width: 520px;
  display: flex; flex-direction: column; gap: 8px;
  padding: 15px 15px 13px; border-radius: 22px;
  border: 1px solid rgba(140, 200, 255, .22);
  background: linear-gradient(165deg, #101E31, #070E19 70%);
  box-shadow: 0 30px 70px -30px #000, inset 0 1px 0 rgba(255, 255, 255, .08);
  animation: rise var(--rise) var(--ease-up) both;
}
.reel__x {
  position: absolute; right: 11px; top: 12px; z-index: 2;
  width: 32px; height: 32px; border-radius: 11px;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, .07); color: var(--ink-dim);
}
.reel__x svg { width: 16px; height: 16px; }
.reel__kick {
  padding-right: 42px;
  font-size: 8.5px; font-weight: 650; letter-spacing: .2em;
  text-transform: uppercase; color: rgba(150, 196, 235, .76);
}
.reel__title {
  padding-right: 42px; margin-top: -4px;
  font-size: 17px; font-weight: 700; letter-spacing: -.02em; line-height: 1.26;
}

.reel__stage {
  position: relative; overflow: hidden;
  aspect-ratio: 16 / 9; border-radius: 15px; background: #04070C;
}
/* На весь экран коробка перестаёт быть 16:9: соотношение держит сам
   ролик, а коробка занимает всё, что есть. */
.reel__stage:fullscreen { aspect-ratio: auto; width: 100%; height: 100%; border-radius: 0; }
.reel__stage:-webkit-full-screen { aspect-ratio: auto; width: 100%; height: 100%; border-radius: 0; }
.reel__stage::backdrop { background: #04070C; }
.reel__video {
  display: block; width: 100%; height: 100%;
  object-fit: contain; background: #04070C;
}
/* Кружок пуска сидит выше середины кадра: полоса внизу занимает
   шестую часть, и по центру он касался бы её нижним краем. Подъём
   даёт нижнее поле — в сетке с place-items: center оно смещает
   кружок вверх ровно на половину. */
.reel__start {
  position: absolute; inset: 0; width: 100%;
  display: grid; place-items: center; padding-bottom: 30px;
  background: rgba(4, 9, 16, .32); color: #EAF7FF;
  animation: fade var(--fast) var(--ease) both;
}
.reel__start[hidden] { display: none; }
.reel__start svg {
  /* box-sizing: border-box — 54 это весь кружок, а поля внутри
     оставляют треугольнику девятнадцать точек. Слева поле больше:
     треугольник оптически тяжелее слева и без сдвига кажется
     прижатым к краю. */
  width: 54px; height: 54px; padding: 17px 16px 18px 19px;
  border-radius: 50%;
  border: 1px solid rgba(190, 230, 255, .5);
  background: rgba(8, 18, 32, .55);
  box-shadow: 0 0 22px -4px rgba(76, 172, 255, .7);
  transition: transform var(--fast) var(--ease);
}
.reel__start:active svg { transform: scale(.93); }
.reel__wait {
  position: absolute; left: 50%; top: 50%; z-index: 1;
  width: 32px; height: 32px; margin: -16px 0 0 -16px;
  border-radius: 50%;
  border: 2px solid rgba(190, 230, 255, .22); border-top-color: #9FD2FF;
  animation: spin .8s linear infinite;
}
.reel__wait[hidden] { display: none; }
@keyframes spin { to { transform: rotate(360deg); } }

/* Две строки: полоса сверху во всю ширину кадра, кнопки под ней.
   В один ряд с четырьмя кнопками и часами полосе на 360 точках
   остаётся около сотни — в нужную секунду пальцем не попасть. */
.reel__bar {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; flex-direction: column; gap: 1px;
  padding: 26px 10px 7px;
  background: linear-gradient(180deg, transparent, rgba(3, 7, 13, .92) 55%);
  transition: opacity var(--fast) var(--ease);
}
.reel__row { display: flex; align-items: center; gap: 2px; }
.reel.is-idle .reel__bar { opacity: 0; pointer-events: none; }
.reel__btn {
  width: 33px; height: 33px; flex: none; border-radius: 10px;
  display: grid; place-items: center; color: #DCEAF7;
}
.reel__btn:active { background: rgba(255, 255, 255, .13); }
.reel__btn svg { width: 17px; height: 17px; }
/* Часы уходят влево от правой пары кнопок: «Ещё» и «На весь экран»
   должны стоять у края, иначе большой палец ищет их в середине. */
.reel__time {
  flex: none; margin: 0 auto 0 5px;
  font-size: 11.5px; color: #AFC6DA;
}

/* Полоса перемотки. Коробка высокая, а сама линия тонкая: пальцем
   в три точки не попасть, поэтому нажатие ловит вся высота. */
.reel__line {
  position: relative; width: 100%; height: 22px;
  display: flex; align-items: center; cursor: pointer; touch-action: none;
}
.reel__line::before {
  content: ""; position: absolute; left: 0; right: 0; height: 4px;
  border-radius: 2px; background: rgba(255, 255, 255, .2);
}
.reel__buf, .reel__fill {
  position: absolute; left: 0; width: 0; height: 4px; border-radius: 2px;
}
.reel__buf { background: rgba(255, 255, 255, .3); }
.reel__fill { background: linear-gradient(90deg, #3E86D8, #7FE3F5); }
.reel__fill i {
  position: absolute; right: -6px; top: 50%; transform: translateY(-50%);
  width: 12px; height: 12px; border-radius: 50%;
  background: #EAF7FF; box-shadow: 0 0 9px rgba(127, 227, 245, .9);
}

/* Меню трёх точек: скорость и картинка в картинке.

   Скорости стоят строкой, а не столбиком, и это не украшение: меню
   лежит внутри кадра (иначе на полном экране его бы не было видно —
   туда попадает только содержимое развёрнутой коробки), а кадр 16:9
   на телефоне — около двухсот точек в высоту. Пять строк со списком
   просто не поместились бы. */
.reel__menu {
  position: absolute; left: 10px; right: 10px; bottom: 66px; z-index: 2;
  padding: 5px 8px 6px; border-radius: 14px;
  border: 1px solid var(--line-2);
  background: rgba(10, 18, 30, .96);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  box-shadow: 0 18px 40px -20px #000;
  animation: pop var(--fast) var(--ease) both;
}
.reel__menu[hidden] { display: none; }
@keyframes pop {
  from { opacity: 0; transform: translate3d(0, 6px, 0); }
  to   { opacity: 1; transform: none; }
}
.reel__head {
  padding: 2px 2px 3px;
  font-size: 9.5px; font-weight: 650; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-faint);
}
.reel__rates { display: flex; gap: 5px; }
.reel__rate {
  flex: 1; min-width: 0; padding: 7px 0; border-radius: 9px;
  background: rgba(255, 255, 255, .07); color: var(--ink-dim);
  font: inherit; font-size: 12px; font-weight: 650; text-align: center;
  font-variant-numeric: tabular-nums;
}
.reel__rate.is-on { background: var(--brand-soft); color: var(--brand); }
.reel__item {
  display: flex; align-items: center; gap: 8px; width: 100%;
  margin-top: 5px; padding: 7px 9px; border-radius: 10px;
  background: rgba(255, 255, 255, .05);
  color: var(--ink); font: inherit; font-size: 13px; text-align: left;
}
.reel__item:active { background: rgba(255, 255, 255, .08); }
/* Галочка у выбранной скорости: она здесь единственный значок,
   поэтому прижимается вправо прямо в правиле для svg. */
.reel__item svg {
  width: 15px; height: 15px; flex: none;
  margin-left: auto; color: var(--brand);
}
.reel__note { color: var(--ink-faint); font-size: 10.5px; text-align: center; }


/* ——— админка: тройка лидеров ————————————————————————————————— */
.lrow {
  display: flex; align-items: center; gap: 9px;
  padding: 10px; border-radius: var(--r);
  border: 1px solid var(--line); background: var(--plate);
}
.lrow + .lrow { margin-top: 8px; }
.lrow__no {
  width: 30px; height: 30px; flex: none; border-radius: 10px;
  display: grid; place-items: center;
  background: rgba(88, 174, 255, .14); color: #9FD2FF;
  font-size: 12.5px; font-weight: 700;
}
.lrow input {
  flex: 1 1 0; min-width: 0; padding: 9px 10px; border-radius: 11px;
  border: 1px solid var(--line-2); background: var(--bg-deep);
  color: var(--ink); font: inherit; font-size: 13.5px;
}
.lrow input:focus { outline: none; border-color: var(--brand-line); }
.lrow__sum { text-align: right; font-variant-numeric: tabular-nums; }

.note { margin: 0 3px; color: var(--ink-faint); font-size: 11.5px; line-height: 1.4; }


/* ——— админка: список участников ————————————————————————————— */
.crew { display: flex; flex-direction: column; gap: 9px; }
.crew__one {
  display: flex; align-items: center; gap: 11px;
  padding: 10px 11px; border-radius: var(--r);
  border: 1px solid var(--line); background: var(--plate);
}
.crew__face {
  width: 42px; height: 42px; flex: none; border-radius: 13px; overflow: hidden;
  display: grid; place-items: center;
  background: linear-gradient(160deg, rgba(88, 174, 255, .26), rgba(10, 24, 44, .6));
  color: #CFE9FF; font-size: 17px; font-weight: 650;
}
.crew__face img { display: block; width: 100%; height: 100%; object-fit: cover; }
.crew__text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.crew__nick {
  font-size: 14.5px; font-weight: 650; letter-spacing: -.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.crew__sub {
  font-size: 11.5px; color: var(--ink-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.crew__btn {
  width: 32px; height: 32px; flex: none; border-radius: 10px;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, .045); color: var(--ink-faint);
}
.crew__btn:active { background: rgba(255, 255, 255, .1); color: var(--ink); }
.crew__btn svg { width: 16px; height: 16px; }
.crew__btn--up svg { transform: rotate(-90deg); }
.crew__btn--down svg { transform: rotate(90deg); }

/* Та же строка, но у ролика вместо квадратной аватарки превью 16:9. */
.crew__shot {
  width: 64px; height: 36px; flex: none; border-radius: 9px; overflow: hidden;
  display: grid; place-items: center;
  background: linear-gradient(160deg, rgba(88, 174, 255, .24), rgba(10, 24, 44, .6));
  color: #9FD2FF;
}
.crew__shot img { display: block; width: 100%; height: 100%; object-fit: cover; }
.crew__shot svg { width: 16px; height: 16px; }
/* У ссылки значок квадратный: 16:9, как у ролика, обрезало бы его
   по бокам. */
.crew__ico { width: 38px; height: 38px; border-radius: 12px; }

/* Подпись файла мануала правится прямо в строке: поле одно, и
   отдельный экран ради него был бы длиннее самой правки. Поэтому
   поле выглядит как обычное название строки, пока в него не зашли:
   ни рамки, ни заливки. Подсказка — имя файла: его и увидит
   читатель, если подпись оставить пустой. */
.crew__edit {
  width: 100%; padding: 0;
  border: 0; background: none; color: var(--ink); font: inherit;
  font-size: 14.5px; font-weight: 650; letter-spacing: -.01em;
}
.crew__edit:focus { outline: none; }
.crew__edit::placeholder {
  color: var(--ink-faint); font-weight: 400; opacity: 1;
}

/* Значок ссылки в её карточке. Квадрат и небольшой: он необязателен,
   и плашка во всю ширину обещала бы картинку, которой может не быть. */
.pic { display: flex; align-items: center; gap: 13px; }
.pic__box {
  position: relative; overflow: hidden;
  width: 76px; height: 76px; flex: none;
  border-radius: 21px; display: grid; place-items: center;
  border: 1px dashed var(--line-2); background: var(--surface);
  color: var(--ink-faint);
}
.pic__box.is-set { border-style: solid; }
.pic__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.pic__face { position: relative; display: grid; place-items: center; }
.pic__face svg { width: 24px; height: 24px; }
.pic__box.is-set .pic__face { display: none; }
.pic__say { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.pic__say b { font-size: 14px; font-weight: 650; }
.pic__say span { font-size: 12px; color: var(--ink-faint); line-height: 1.4; }
.pic__say .link { align-self: flex-start; margin-top: 3px; }


/* ——— формы админки ——————————————————————————————————————————— */
/* display задан явно, поэтому и прятать форму нужно явно: правило
   браузера для [hidden] слабее любого нашего. */
.form { display: flex; flex-direction: column; gap: 11px; }
.form[hidden] { display: none; }

/* Аватарка выбирается нажатием на квадрат: свой вид у input[type=file]
   в каждом браузере свой, и ни один не похож на остальное приложение. */
.face {
  position: relative; overflow: hidden;
  align-self: center; width: 96px; height: 96px; border-radius: 28px;
  display: grid; place-items: center;
  border: 1px dashed var(--line-2); background: var(--surface);
  color: var(--ink-faint);
}
.face__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.face .face__ico { width: 26px; height: 26px; }

.link {
  align-self: center; padding: 6px 10px; border-radius: 10px;
  background: none; border: 0; color: var(--brand); font: inherit; font-size: 12.5px;
}
.link--bad { color: var(--bad); }

.field { display: flex; flex-direction: column; gap: 5px; }
.field > span { padding-left: 3px; font-size: 11.5px; color: var(--ink-faint); }
.field input, .field textarea {
  width: 100%; padding: 12px 13px; border-radius: 14px;
  border: 1px solid var(--line-2); background: var(--surface);
  color: var(--ink); font: inherit; font-size: 14.5px;
}
.field input:focus, .field textarea:focus {
  outline: none; border-color: var(--brand-line);
}
/* Подсказка в поле — это пример, а не значение. По умолчанию браузер
   красит её почти как текст, и в анкете на тринадцать полей половина
   выглядела бы уже заполненной. */
.field input::placeholder, .field textarea::placeholder {
  color: var(--ink-faint); opacity: 1;
}
/* Полоски прокрутки нет и здесь: в остальном приложении её тоже нет,
   а в описании ролика она ложится прямо на текст. */
.field textarea {
  min-height: 94px; line-height: 1.45; resize: vertical;
  scrollbar-width: none;
}
.field textarea::-webkit-scrollbar { width: 0; height: 0; }


/* ——— форма ролика ———————————————————————————————————————————— */
/* Превью во всю ширину и 16:9 — ровно тем прямоугольником, каким оно
   встанет в раздел. Квадратная плашка, как у аватарки, врала бы
   про обрезку. */
.shot {
  position: relative; overflow: hidden;
  width: 100%; aspect-ratio: 16 / 9; border-radius: var(--r);
  display: grid; place-items: center;
  border: 1px dashed var(--line-2); background: var(--surface);
  color: var(--ink-faint);
}
.shot__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Сам ролик в плашке: с него и снимается кадр. Видно, что берём,
   до того как взяли. Нажатие проходит мимо него на кнопку —
   плашка по-прежнему открывает галерею. */
.shot__probe {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; background: #05090F; pointer-events: none;
}
.shot__probe[hidden] { display: none; }
.shot__face {
  position: relative; display: flex; flex-direction: column;
  align-items: center; gap: 7px; font-size: 12px;
}
.shot .shot__ico { width: 25px; height: 25px; }
.shot.is-set .shot__face { display: none; }
.shot.is-set, .shot.is-live { border-style: solid; }
/* Поверх живого кадра подпись нужна читаемой, а сама плашка —
   по-прежнему нажимаемой: прячем её только когда превью уже выбрано. */
.shot.is-live .shot__face {
  gap: 5px; padding: 9px 13px; border-radius: 999px; font-size: 11.5px;
  background: rgba(5, 10, 18, .66); color: var(--ink-dim);
  -webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px);
}
.shot.is-live .shot__ico { width: 18px; height: 18px; }

/* Кадр из ролика: ползунок по длительности и кнопка «взять». */
.grab { display: flex; align-items: center; gap: 9px; }
.grab[hidden] { display: none; }
.grab input[type="range"] {
  flex: 1; min-width: 0; height: 22px; accent-color: var(--brand);
  background: none;
}
.grab__at { flex: none; font-size: 12px; color: var(--ink-dim); }
.grab .btn { flex: none; padding: 9px 13px; font-size: 13px; }

/* Выбор файла: свой вид у input[type=file] в каждом браузере свой. */
.file {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 11px; border-radius: var(--r);
  border: 1px solid var(--line-2); background: var(--surface);
  color: inherit; font: inherit; text-align: left;
}
.file:active { border-color: var(--brand-line); }
.file__ico {
  width: 38px; height: 38px; flex: none; border-radius: 12px;
  display: grid; place-items: center;
  background: var(--brand-soft); color: var(--brand);
}
.file__text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.file__title {
  font-size: 14px; font-weight: 650;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.file__hint {
  font-size: 11.5px; color: var(--ink-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.file__go { width: 16px; height: 16px; flex: none; color: var(--ink-faint); }

.load { height: 4px; border-radius: 3px; overflow: hidden; background: rgba(255, 255, 255, .08); }
.load[hidden] { display: none; }
.load i {
  display: block; width: 0; height: 100%; border-radius: 3px;
  background: linear-gradient(90deg, #3E86D8, #7FE3F5);
  transition: width .15s linear;
}

/* Строка реквизита в две полосы: банк сверху во всю ширину, суммы под
   ним. В одну строку всё это не помещается уже на 375 точках. */
.brow {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 10px; border-radius: var(--r);
  border: 1px solid var(--line); background: var(--surface);
}
.brow + .brow { margin-top: 8px; }
.brow__pick {
  flex: 1 1 100%; min-width: 0;
  display: flex; align-items: center; gap: 9px;
  padding: 8px 9px; border-radius: 11px;
  background: rgba(255, 255, 255, .04); color: inherit; font: inherit;
  text-align: left;
}
.brow__pick:active { background: rgba(255, 255, 255, .09); }
.brow__ico { width: 24px; height: 24px; flex: none; border-radius: 8px; object-fit: cover; }
.brow__name {
  flex: 1; min-width: 0; font-size: 13px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.brow__name--empty { color: var(--ink-faint); }
.brow__pick svg { width: 15px; height: 15px; flex: none; color: var(--ink-faint); }
.brow__sum {
  flex: 1 1 0; min-width: 0; padding: 9px 10px; border-radius: 11px;
  border: 1px solid var(--line-2); background: var(--bg-deep);
  color: var(--ink); font: inherit; font-size: 13.5px;
  font-variant-numeric: tabular-nums;
}
.brow__sum:focus { outline: none; border-color: var(--brand-line); }
.brow__x {
  width: 34px; height: 34px; flex: none; border-radius: 11px;
  display: grid; place-items: center;
  background: rgba(255, 107, 126, .1); color: var(--bad);
}
.brow__x svg { width: 16px; height: 16px; }
/* Как сумма будет выглядеть в карточке — видно сразу, не выходя
   из формы: вводят рубли, а на витрине стоит «12,1кк». */
.brow__look {
  flex: 1 1 100%; padding-left: 3px;
  font-size: 11px; color: var(--ink-faint);
}

/* ——— мануалы ——————————————————————————————————————————————— */
/* Раздел мануалов — это банк, поэтому список плоский: строки банков,
   внутри банка строки тем. Зазор чуть плотнее, чем у роликов: строка
   низкая, и двенадцать точек между ними рвут список. */
.mrows { display: flex; flex-direction: column; gap: 9px; }

/* Строка банка и строка темы — та же .wrow, что у ссылок, только
   ведёт внутрь приложения. Кнопке возвращаем то, что браузер у неё
   забирает: ширину, цвет, шрифт и выключку. */
button.wrow {
  width: 100%; color: inherit; font: inherit; text-align: left;
  cursor: pointer;
}

/* Название темы — это фраза, а не ярлык: у ссылок строка обрывается
   многоточием, здесь так нельзя. Ради неё сюда и заходят, а закрытый
   человек по ней только и поймёт, что внутри. То же и в админке,
   где темы правят. */
.wrow--long .wrow__name,
.crew__one--long .crew__nick {
  white-space: normal; overflow: visible; overflow-wrap: anywhere;
}

/* Текст мануала. Межстрочник шире обычного и строка длиннее: его
   читают подряд, а не выхватывают взглядом, как подписи в карточках.
   Своей прокрутки у него нет — страница и так одна, а две прокрутки
   на телефоне цепляются друг за друга. */
/* Текст лежит на плашке, а не прямо на фоне: читают его минутами,
   и звёзды с логотипом за строками на этом сроке начинают мешать. */
.read {
  display: block; margin: 4px 0 0;
  padding: 15px 16px 16px;
  border-radius: var(--r);
  border: 1px solid var(--line); background: var(--plate);
  font-size: 15px; line-height: 1.62; color: var(--ink-dim);
}
.read p {
  margin: 0 0 13px;
  /* Одиночный перенос остаётся переносом: шаги в мануалах пишут
     строками, и склеивать их в абзац нельзя. */
  white-space: pre-line;
  overflow-wrap: anywhere;
}
.read p:last-child { margin-bottom: 0; }

/* ——— выбор банка ——————————————————————————————————————————— */
/* На весь экран и с поиском: банков больше двух десятков, в выпадающем
   списке их не перебрать. */
.pick {
  position: absolute; inset: 0; z-index: 40;
  display: flex; flex-direction: column;
  background: rgba(6, 11, 18, .97);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
}
.pick[hidden] { display: none; }
.pick__bar {
  display: flex; align-items: center; gap: 9px; flex: none;
  padding: calc(var(--safe-top) + 12px) 12px 12px;
  border-bottom: 1px solid var(--line);
}
.pick__ico { width: 18px; height: 18px; flex: none; color: var(--ink-faint); }
.pick__bar input {
  flex: 1; min-width: 0; padding: 10px 0;
  border: 0; background: none; color: var(--ink); font: inherit; font-size: 15px;
}
.pick__bar input:focus { outline: none; }
.pick__x {
  width: 34px; height: 34px; flex: none; border-radius: 11px;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, .05); color: var(--ink-dim);
}
.pick__list {
  flex: 1; min-height: 0; overflow-y: auto;
  padding: 8px 12px calc(16px + var(--safe-bottom));
  scrollbar-width: none;
}
.pick__list::-webkit-scrollbar { width: 0; }
.pick__one {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 10px 11px; border-radius: 14px;
  color: inherit; font: inherit; font-size: 14.5px; text-align: left;
}
.pick__one + .pick__one { margin-top: 2px; }
.pick__one:active { background: rgba(255, 255, 255, .07); }
.pick__one img { width: 32px; height: 32px; flex: none; border-radius: 10px; }


/* ——— счётчики под баннером ——————————————————————————————————— */
.tally {
  border-radius: var(--r);
  border: 1px solid var(--line);
  background: var(--plate);
  overflow: hidden;
}
.tally__row { display: flex; gap: 10px; padding: 13px 14px; }
.tally__cell { display: flex; flex-direction: column; gap: 3px; flex: 1; min-width: 0; }
.tally__cell i { color: var(--ink-faint); font-size: 11.5px; font-style: normal; }
.tally__cell b {
  font-size: 19px; font-weight: 650; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
/* Статус — отдельной строкой, а не третьей колонкой: подписи вроде
   «ждём второй файл» в треть ширины не помещаются. */
.tally__now {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 14px;
  border-top: 1px solid var(--line);
  background: rgba(255, 255, 255, .02);
}
.tally__now span { color: var(--ink-faint); font-size: 12px; }
.tally__now b {
  min-width: 0; font-size: 13px; font-weight: 600; color: var(--ink-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}


/* ——— сетка услуг ——————————————————————————————————————————— */
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }

.svc {
  display: flex; flex-direction: column; gap: 7px;
  padding: 14px; text-align: left;
  background: var(--plate); border: 1px solid var(--line);
  border-radius: var(--r);
  transition: border-color var(--fast) var(--ease),
              background var(--fast) var(--ease),
              transform var(--fast) var(--ease);
}
.svc:active { transform: scale(.975); border-color: var(--brand-line); }
.svc__ico {
  width: 36px; height: 36px; border-radius: 11px;
  display: grid; place-items: center;
  background: var(--brand-soft); color: var(--brand);
}
.svc__ico svg { width: 19px; height: 19px; }
.svc__text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.svc__title { font-size: 14px; font-weight: 600; letter-spacing: -.01em; }
.svc__hint { color: var(--ink-faint); font-size: 11.5px; line-height: 1.35; }
.svc__price {
  margin-top: auto; padding-top: 4px;
  font-size: 13px; font-weight: 650; color: var(--brand);
  font-variant-numeric: tabular-nums;
}
.svc--admin .svc__ico { background: var(--warn-soft); color: var(--warn); }

/* Клиентский товар один, и он занимает обе колонки: значок слева,
   цена справа, остальное посередине. Админские инструменты под ним
   так и ложатся парами. */
.svc--wide {
  grid-column: 1 / -1;
  flex-direction: row; align-items: center; gap: 14px;
  padding: 15px 16px;
}
.svc--wide .svc__ico { width: 46px; height: 46px; border-radius: 15px; }
.svc--wide .svc__ico svg { width: 24px; height: 24px; }
.svc--wide .svc__text { flex: 1; }
.svc--wide .svc__price {
  margin: 0; padding: 0; flex: none;
  font-size: 15px; white-space: nowrap;
}


/* ——— шаги ————————————————————————————————————————————————— */
.steps {
  counter-reset: step; list-style: none; padding: 4px 0; margin: 0;
  background: var(--plate); border: 1px solid var(--line);
  border-radius: var(--r);
}
.steps li {
  position: relative;
  display: flex; flex-direction: column; gap: 1px;
  padding: 11px 14px 11px 50px;
}
.steps li + li { border-top: 1px solid var(--line); }
.steps li::before {
  counter-increment: step; content: counter(step);
  position: absolute; left: 14px; top: 11px;
  width: 24px; height: 24px; border-radius: 8px;
  display: grid; place-items: center;
  background: var(--brand-soft); color: var(--brand);
  font-size: 12px; font-weight: 650;
}
.steps li b { font-size: 14px; }
.steps li span { color: var(--ink-faint); font-size: 12.5px; }



/* ——— плитки сводки ————————————————————————————————————————— */
/* ——— кнопки-строки в админке ———————————————————————————————— */
/* Группы разделены воздухом, а не заголовками: «Управление» над тремя
   кнопками ничего не добавляет к тому, что на них написано. */
.acts { display: flex; flex-direction: column; gap: 10px; }
/* 18 сверху плюс 10 от .screen — между группами выходит 28 точек против
   10 внутри группы. Меньше — и разрыв читается как случайный отступ. */
.acts--sep { margin-top: 18px; }

.act {
  display: flex; align-items: center; gap: 13px;
  padding: 13px 15px; text-align: left;
  background: var(--plate); border: 1px solid var(--line);
  border-radius: var(--r);
  color: inherit; font: inherit; cursor: pointer;
  transition: border-color var(--fast) var(--ease);
}
.act:active { border-color: var(--brand-line); }
.act__ico {
  width: 38px; height: 38px; border-radius: 12px; flex: none;
  display: grid; place-items: center;
  background: var(--warn-soft); color: var(--warn);
}
.act__ico svg { width: 20px; height: 20px; }
.act__title {
  flex: 1; min-width: 0;
  font-size: 14.5px; font-weight: 600; letter-spacing: -.01em;
}
.act__go { color: var(--ink-faint); width: 18px; height: 18px; }
/* Чаты стоят отдельной группой — и цветом тоже, чтобы воздух между
   группами читался как граница, а не как случайный отступ. */
.act--talk .act__ico { background: var(--brand-soft); color: var(--brand); }

/* ——— назад с подэкрана ——————————————————————————————————————— */
.back {
  display: inline-flex; align-items: center; gap: 4px;
  align-self: flex-start;
  margin: 0 0 2px -6px; padding: 6px;
  border: 0; border-radius: 10px; background: none;
  color: var(--ink-dim); font: inherit; font-size: 13.5px; cursor: pointer;
  transition: color var(--fast) var(--ease);
}
.back:active { color: var(--ink); }
.back svg { width: 17px; height: 17px; transform: rotate(180deg); }

.tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.tile {
  padding: 14px;
  background: var(--plate); border: 1px solid var(--line);
  border-radius: var(--r);
}
.tile i {
  display: block; font-style: normal; margin-bottom: 4px;
  color: var(--ink-faint); font-size: 11.5px;
}
.tile b {
  display: block;
  font-size: 23px; font-weight: 700; letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
}
.tile small { color: var(--ink-faint); font-size: 11.5px; }
.tile--accent { border-color: var(--brand-line); background: var(--brand-soft); }
.tile--accent b { color: #B9C6FF; }


/* ——— список заявок ————————————————————————————————————————— */
/* Карточка о двух частях: строка и разворот под ней. Разворот есть
   не у всех — у клиента только у закрытого заказа с готовым паком, —
   поэтому стрелка рисуется из скрипта, а не стоит в разметке. */
.job {
  display: flex; flex-direction: column;
  background: var(--plate); border: 1px solid var(--line);
  border-radius: var(--r);
}
.job + .job { margin-top: 8px; }
.job__row {
  display: flex; align-items: center; gap: 12px;
  padding: 13px 14px;
}
.job__ico { position: relative; width: 46px; height: 46px; flex: none; }

/* Углы режем обрезкой, а не подложкой: значок банка может прийти и
   квадратным, и уже скруглённым. Прозрачные углы так и остаются
   прозрачными, прямые — обрезаются. */
.job__bank {
  display: block; width: 100%; height: 100%;
  border-radius: 15px; object-fit: cover;
}
/* Банк незнакомый — домик в такой же плитке. Отступ и толщину линии
   задаём явно: иконка нарисована под 20 точек, а здесь ей двадцать
   четыре, и тонкая линия на сером фоне просто пропадала. */
.job__ico > svg {
  width: 100%; height: 100%; padding: 11px; border-radius: 15px;
  background: var(--surface-3); color: var(--ink-dim);
  stroke-width: 1.5;
}

/* Состояние заявки — кружком поверх правого нижнего угла, как метка
   у аватара. Обводка цветом карточки вырезает его из значка. */
.job__state {
  position: absolute; right: -3px; bottom: -3px;
  width: 21px; height: 21px; border-radius: 50%;
  display: grid; place-items: center;
  border: 2px solid var(--surface);
  background: var(--surface-3); color: var(--ink-dim);
}
.job__state svg { width: 12px; height: 12px; }
.job--done .job__state { background: var(--ok);   color: #05241B; }
.job--wait .job__state { background: var(--warn); color: #3B2604; }
.job--dead .job__state { background: var(--bad);  color: #320811; }

.job__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.job__title {
  font-size: 15px; font-weight: 600; letter-spacing: -.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.job__meta {
  color: var(--ink-faint); font-size: 12px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.job__price {
  font-size: 14px; font-weight: 650; flex: none;
  font-variant-numeric: tabular-nums;
}

/* Разворачивается коробка вокруг содержимого, а не само содержимое:
   высоту css анимирует только у того, что умеет быть обрезанным.
   Число ей ставит скрипт и по концу хода отпускает в auto. */
.job__more {
  width: 32px; height: 32px; flex: none; border-radius: 10px;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, .045); color: var(--ink-faint);
  transition: transform var(--fast) var(--ease), background var(--fast) var(--ease);
}
.job__more svg { width: 16px; height: 16px; transform: rotate(90deg); }
.job__more:active { background: rgba(255, 255, 255, .1); color: var(--ink); }
.job.is-open .job__more { transform: rotate(180deg); }
.job__fold {
  height: 0; overflow: hidden;
  transition: height var(--slow) var(--ease);
}
.job__in {
  display: flex; flex-direction: column; gap: 10px;
  padding: 2px 14px 14px;
  border-top: 1px solid var(--line);
  opacity: 0; transition: opacity var(--fast) var(--ease) .08s;
}
.job.is-open .job__in { opacity: 1; }
/* Отступ сверху даёт сама линия, поэтому у первой строки его нет:
   иначе над «ID клиента» висело бы двадцать точек воздуха. */
.job__in > :first-child { margin-top: 10px; }

/* Строки «подпись — значение». Подпись слева серым, значение справа
   и жирным: так читается сверху вниз, а не слева направо. */
.facts { display: flex; flex-direction: column; }
.facts__one {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; padding: 7px 0;
}
.facts__one + .facts__one { border-top: 1px solid var(--line); }
.facts__one i {
  font-style: normal; color: var(--ink-faint); font-size: 12px;
  flex: none;
}
.facts__one b {
  min-width: 0; font-size: 13.5px; font-weight: 600; text-align: right;
  overflow-wrap: anywhere;
}
/* Счёт: последняя строка — итог, и она заметнее остальных. */
.facts--sum .facts__one:last-child b {
  font-size: 16px; font-weight: 700; color: var(--brand);
}

/* Файл с двумя кнопками: на устройство и в чат бота. Две, а не одна:
   в вебвью скачанный файл ещё надо найти, а в переписке он остаётся
   и пересылается. */
.grab2 {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 10px 11px; border-radius: var(--r-sm);
  background: rgba(255, 255, 255, .03); border: 1px solid var(--line);
}
.grab2__text { flex: 1 1 100%; display: flex; flex-direction: column; }
.grab2__text b { font-size: 13.5px; font-weight: 650; }
.grab2__text span { font-size: 11.5px; color: var(--ink-faint); }

.pill {
  display: inline-flex; align-items: center; gap: 6px; flex: 1 1 0;
  justify-content: center;
  padding: 9px 12px; border-radius: 11px;
  background: var(--brand-soft); border: 1px solid var(--brand-line);
  color: var(--brand); font: inherit; font-size: 12.5px; font-weight: 600;
}
.pill:disabled { opacity: .5; }
.pill svg { width: 15px; height: 15px; }

/* Решение по заявке: отказ и одобрение в одну строку. Отказ слева и
   не зовёт нажать; одобрение справа, обычной кнопкой. */
.deal { display: flex; flex-wrap: wrap; gap: 8px; }
.deal .btn {
  flex: 1 1 0; min-width: 140px; margin: 0; padding: 11px 14px;
  font-size: 13.5px;
}
.btn--bad { border-color: rgba(255, 107, 126, .4); color: var(--bad); }
.btn--bad.is-armed {
  background: var(--bad-soft); border-color: var(--bad); color: #FFC9D1;
}

.pickbox { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.pickbox .btn--wide { margin: 0; max-width: none; }
.pickbox__birth { display: flex; flex-direction: column; gap: 8px; }
.pickbox__birth[hidden] { display: none; }

/* ——— порог учёта транзакций ————————————————————————————————— */
/* Последний шаг выдачи. Число вводят крупно — оно здесь главное, —
   а под ним на ходу пересчитывается, во сколько страниц уложится пак. */
.cut { display: flex; flex-direction: column; gap: 9px; width: 100%; }
.cut .btn--wide { margin: 0; max-width: none; }

.cut__sum {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px; border-radius: 14px;
  border: 1px solid var(--line-2); background: var(--bg-deep);
}
.cut__sum:focus-within { border-color: var(--brand-line); }
.cut__in {
  flex: 1; min-width: 0; padding: 0;
  border: 0; background: none; color: var(--ink);
  font: inherit; font-size: 22px; font-weight: 700; letter-spacing: -.02em;
}
.cut__in:focus { outline: none; }
.cut__rub { flex: none; color: var(--ink-faint); font-size: 15px; }

/* Готовые числа строкой: пороги круглые, и чаще всего нужен один из них,
   а не своё значение с клавиатуры. */
.cut__chips { display: flex; flex-wrap: wrap; gap: 6px; }
.cut__chip {
  padding: 7px 11px; border-radius: 11px;
  border: 1px solid var(--line); background: rgba(255, 255, 255, .04);
  color: var(--ink-dim); font: inherit; font-size: 12.5px;
  font-variant-numeric: tabular-nums; cursor: pointer;
  transition: border-color var(--fast) var(--ease),
              background var(--fast) var(--ease),
              color var(--fast) var(--ease);
}
.cut__chip.is-on {
  border-color: var(--brand-line); background: var(--brand-soft);
  color: var(--brand);
}

/* Итог пересчёта. Страницы — то, ради чего сюда и пришли, поэтому
   нижняя строка заметнее верхней. */
.cut__facts { padding: 2px 2px 0; }
.cut__facts .facts__one:last-child b { font-size: 15px; color: var(--brand); }

/* Порог срезал всё — пак вышел бы пустым, и кнопка выдачи гаснет. */
.cut__warn {
  margin: 0; padding: 9px 11px; border-radius: 12px;
  border: 1px solid rgba(255, 192, 106, .32); background: var(--warn-soft);
  color: var(--warn); font-size: 12px; line-height: 1.35;
}
.cut__warn[hidden] { display: none; }

/* Поиск по заявкам. Строка во всю ширину: id и ник вводят целиком,
   а не выбирают из списка. */
.find {
  display: flex; align-items: center; gap: 9px;
  margin-bottom: 10px; padding: 0 12px;
  border-radius: 14px;
  border: 1px solid var(--line-2); background: var(--surface);
}
.find__ico { width: 17px; height: 17px; flex: none; color: var(--ink-faint); }
.find input {
  flex: 1; min-width: 0; padding: 11px 0;
  border: 0; background: none; color: var(--ink); font: inherit; font-size: 14px;
}
.find input:focus { outline: none; }
.find input::-webkit-search-cancel-button { display: none; }
.find__x {
  width: 28px; height: 28px; flex: none; border-radius: 9px;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, .05); color: var(--ink-dim);
}
.find__x[hidden] { display: none; }
.find__x svg { width: 14px; height: 14px; }


/* ——— инструменты админа ——————————————————————————————————— */
/* Последний собранный файл. Стоит выше анкеты: чаще всего инструмент
   открывают, чтобы забрать готовое, а не собрать новое. */
.kept {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  padding: 12px 13px; border-radius: var(--r);
  border: 1px solid var(--brand-line); background: var(--brand-soft);
}
.kept__text { flex: 1 1 100%; display: flex; flex-direction: column; gap: 2px; }
.kept__text b {
  font-size: 14px; font-weight: 650;
  overflow-wrap: anywhere;
}
.kept__text span { font-size: 11.5px; color: var(--ink-dim); }
.kept__acts { display: flex; gap: 8px; width: 100%; }

/* Подпись работника. Плашка низкая и широкая: росчерк лежит поперёк
   кадра, и квадрат обрезал бы ему хвост. Фон светлый — чернила
   вырезаны в чёрный, на тёмном их не видно. */
.hand {
  position: relative; overflow: hidden;
  width: 100%; height: 108px; border-radius: var(--r);
  display: grid; place-items: center;
  border: 1px dashed var(--line-2); background: var(--surface);
  color: var(--ink-faint);
}
.hand.is-set {
  border-style: solid;
  background: #F3F7FB;
}
.hand__img {
  max-width: 100%; max-height: 100%;
  object-fit: contain;
}
.hand__face {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 0 24px; text-align: center; font-size: 11.5px; line-height: 1.35;
}
.hand__face svg { width: 22px; height: 22px; }

/* Поле с ошибкой. Текст причины — под полем, а не окном поверх всего:
   ошибся он в одном поле, а не во всей анкете. */
.field__bad {
  display: none; padding-left: 3px;
  color: var(--bad); font-size: 11.5px; line-height: 1.35;
}
.field.is-bad > span:first-child { color: var(--bad); }
.field.is-bad input, .field.is-bad textarea { border-color: var(--bad); }
.field.is-bad .field__bad { display: block; }

/* Пока собирается документ — заголовок дышит. Полосы здесь нет
   нарочно: процентов у сборки нет, а поддельная полоса врёт. */
.head.is-busy { animation: breathe 1.4s ease-in-out infinite; }
@keyframes breathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: .45; }
}


/* ——— пак скриншотов у клиента ————————————————————————————— */
.card {
  display: flex; flex-direction: column; gap: 11px;
  padding: 14px; border-radius: var(--r);
  border: 1px solid var(--line); background: var(--plate);
}
.card + .card { margin-top: 10px; }
.card__head { font-size: 15.5px; font-weight: 650; letter-spacing: -.01em; }
.card .btn--wide { margin: 2px 0 0; max-width: none; }
.card .file { background: var(--bg-deep); }

/* Условия сделки. Мелким, но не серым до невидимости: их человек
   должен прочитать до оплаты, а не после. */
.terms {
  padding: 11px 12px; border-radius: var(--r-sm);
  background: rgba(255, 255, 255, .03);
  color: var(--ink-dim); font-size: 11.5px; line-height: 1.45;
}

/* Полоса сборки. Проценты настоящие: их пишет тот, кто рисует. */
.task {
  display: flex; flex-direction: column; gap: 8px;
  padding: 13px; border-radius: var(--r-sm);
  background: var(--bg-deep); border: 1px solid var(--brand-line);
}
.task__top {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; font-size: 13px; color: var(--ink-dim);
}
.task__top b { font-size: 15px; color: var(--ink); }
.task__line {
  height: 8px; border-radius: 5px; overflow: hidden;
  background: rgba(255, 255, 255, .07);
}
.task__line i {
  display: block; height: 100%; border-radius: 5px;
  background: linear-gradient(90deg, #3E86D8, #7FE3F5);
  transition: width .4s var(--ease);
}
.task--bad { border-color: rgba(255, 107, 126, .4); }
.task--bad .task__line i { background: var(--bad); }


/* ——— пустые состояния и скелетоны ————————————————————————— */
.empty {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 44px 24px; text-align: center;
  border: 1px dashed var(--line-2); border-radius: var(--r);
  color: var(--ink-faint);
}
.empty svg { width: 30px; height: 30px; color: var(--ink-faint); stroke-width: 1.4; }
.empty b { color: var(--ink-dim); font-size: 14.5px; }
.empty span { font-size: 12.5px; max-width: 28ch; }

/* Блик бежит сдвигом, а не положением фона. Положение фона браузер
   перерисовывает целиком каждый кадр, и три скелетона на экране
   съедали столько же, сколько всё появление вместе взятое — в те же
   полсекунды, когда экран и въезжает. Сдвиг считает видеокарта. */
.skel {
  position: relative; overflow: hidden;
  border-radius: var(--r); height: 66px;
  background: var(--surface);
}
.skel::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 18%, var(--surface-2) 50%,
                                      transparent 82%);
  animation: shimmer 1.3s linear infinite;
  will-change: transform;
}
.skel + .skel { margin-top: 8px; }
@keyframes shimmer {
  from { transform: translate3d(-105%, 0, 0); }
  to   { transform: translate3d(105%, 0, 0); }
}


/* ——— кнопки ———————————————————————————————————————————————— */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 13px 20px; border-radius: 14px;
  background: linear-gradient(145deg, var(--brand), #4ED4EE);
  color: #fff; font-size: 15px; font-weight: 620;
  box-shadow: 0 10px 26px -12px rgba(88, 174, 255, .9);
}
/* display у кнопки задан явно, поэтому и прятать её нужно явно:
   правило браузера для [hidden] слабее любого нашего. */
.btn[hidden] { display: none; }
.btn--wide { width: 100%; max-width: 300px; margin-top: 12px; align-self: center; }
/* Второстепенная кнопка: одного вида с основной, но не зовёт нажать.
   Стоит после .btn нарочно — вес у правил одинаковый, решает порядок. */
.btn--ghost {
  background: none; border: 1px solid var(--brand-line);
  color: var(--brand); box-shadow: none;
}
.btn__ico { width: 18px; height: 18px; stroke-width: 2; }
.tap { transition: transform var(--fast) var(--ease), opacity var(--fast) var(--ease); }
.tap:active { transform: scale(.96); opacity: .9; }


/* ——— нижняя панель ————————————————————————————————————————— */
/* Плавающая таблетка поверх содержимого. Подписи нужны: «Прибыль» от
   «Результатов» одними значками не отличить, а разделов теперь шесть.
   Место под панель отведено снизу в .screen.

   При прокрутке вниз панель поджимается — так она меньше закрывает
   список. Возвращается, когда человек почти наверху: один порог на
   сжатие и другой на возврат, иначе на границе она дрожала бы.      */
.tabs {
  position: absolute; z-index: 10;
  left: 10px; right: 10px;
  bottom: calc(9px + var(--safe-bottom));
  display: flex; padding: 5px 4px;
  border-radius: 24px;
  background: rgba(13, 25, 42, .86);
  -webkit-backdrop-filter: blur(22px) saturate(160%);
          backdrop-filter: blur(22px) saturate(160%);
  border: 1px solid var(--ice-line);
  box-shadow: 0 18px 44px -20px rgba(0, 0, 0, .95),
              inset 0 1px 0 var(--ice-lip);
  transform-origin: bottom center;
  transition: transform .34s var(--ease);
}
.tabs.is-small { transform: scale(.88); }

/* Подложка активной вкладки едет одним элементом — так переход читается
   как движение, а не как перекраска двух кнопок. */
.tabs__pill {
  position: absolute; top: 5px; left: 0;
  height: 50px; border-radius: 16px;
  background: linear-gradient(180deg, rgba(62, 148, 236, .52), rgba(24, 70, 134, .34));
  border: 1px solid rgba(140, 200, 255, .34);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .18),
              0 0 18px -6px rgba(90, 180, 255, .9);
  transition: transform var(--slow) var(--ease), width var(--slow) var(--ease),
              opacity var(--fast) var(--ease);
  pointer-events: none;
}
/* Светлая черта по верхнему краю подложки — в макете именно она
   показывает, где стоишь, а не сама заливка. */
.tabs__pill::before {
  content: ""; position: absolute; left: 24%; right: 24%; top: -1px;
  height: 3px; border-radius: 0 0 3px 3px;
  background: #8FD9FF;
  box-shadow: 0 0 10px rgba(143, 217, 255, .95);
}
.tab {
  position: relative; z-index: 1;
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px;
  height: 50px; padding: 0 1px; border-radius: 17px;
  color: var(--ink-faint);
  transition: color var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.tab[hidden] { display: none; }
.tab:active { transform: scale(.9); }
.tab:focus-visible { outline: 2px solid var(--brand); outline-offset: -3px; }
.tab__ico { width: 20px; height: 20px; }
/* Подпись в девять точек — ровно на грани читаемости, поэтому без
   межбуквенного разрежения и с многоточием: у админа вкладок семь,
   и «Подключиться» в свою долю ширины уже не помещается. */
.tab__name {
  max-width: 100%;
  font-size: 8.5px; font-weight: 600; line-height: 1; letter-spacing: -.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tab.is-on { color: var(--ink); }
.tab.is-on .tab__ico {
  stroke-width: 2;
  filter: drop-shadow(0 0 7px rgba(88, 174, 255, .8));
}


/* ——— появление ————————————————————————————————————————————— */
/*
   Блок въезжает снизу. Три вещи здесь важнее, чем кажется, и каждая
   лечит свой рывок:

   1. Конечный кадр — translate3d(0,0,0), а не none. Пока у блока
      трёхмерный сдвиг, он лежит на своём слое; «none» снимает слой
      прямо в последнем кадре хода, и блок перерисовывается заново
      ровно в тот момент, когда должен тихо встать на место.
   2. will-change поднимает слой заранее. Без подсказки браузер
      заводит его на первом кадре анимации — и этот кадр пропадает.
   3. Подсказку снимаем по концу хода (класс is-in вешает app.js).
      Слой под каждой строкой списка живёт до конца сеанса и отъедает
      память; is-in гасит анимацию целиком, и блок становится обычным
      неподвижным блоком — в кадре, где ничего не движется.
*/
.rise {
  animation: rise var(--rise) var(--ease-up) both;
  animation-delay: calc(var(--i, 0) * 40ms);
  will-change: opacity, transform;
  -webkit-backface-visibility: hidden;
          backface-visibility: hidden;
}
/* Сдвиг здесь не обнуляется нарочно: без анимации он и так свой,
   обычный. Поставь мы transform: none — правило легло бы поверх
   .tap:active, и строка перестала бы поджиматься под пальцем. */
.rise.is-in {
  animation: none;
  will-change: auto;
}
@keyframes rise {
  from { opacity: 0; transform: translate3d(0, 12px, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}
@keyframes fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Кому движение мешает — отдаём статику. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* Планшет и широкий десктоп Telegram: колонка по центру, а не растянутая
   на всю ширину — две плитки в ряд на 1200px выглядели бы абсурдно. */
@media (min-width: 560px) {
  .scroll > .screen { max-width: 520px; margin: 0 auto; width: 100%; }
  .top { max-width: 520px; margin: 0 auto; width: 100%; }
  /* Панель лежит абсолютно, поэтому центрируется нулевыми краями
     и автополями, а не transform: его занимает сжатие при прокрутке. */
  .tabs { left: 0; right: 0; width: 496px; margin-inline: auto; }
  /* Полоса ввода лежит так же абсолютно и центруется тем же приёмом.
     488 — это 520 колонки минус поля экрана с двух сторон. */
  .dock { left: 0; right: 0; width: 488px; margin-inline: auto; }
  .grid, .tiles { grid-template-columns: repeat(3, 1fr); }
}

/* ---------------------------------------------------------------------
   Переписка с поддержкой.

   Два экрана на одной вёрстке: своя переписка у человека и чужая
   у админа. Разница одна — класс is-mine на строке: от него зависит,
   с какой стороны встанет облачко и какого оно цвета.

   Лента едет в общей прокрутке страницы, а не в своём окне: вложенная
   прокрутка в вебвью Telegram то перехватывает жест у страницы, то
   отдаёт его ей на середине списка.

   Полоса ввода из потока ленты вынута совсем. Sticky здесь не работал:
   пока переписка короче экрана, прилипать не к чему — полоса просто
   стояла там, куда её поставил поток, то есть под лентой где-то посреди
   экрана, и переезжала вниз только когда сообщений набиралось на
   прокрутку. Теперь это .dock — абсолютный блок с краями от .app
   (прокрутка .scroll его не везёт и не обрезает: его содержащий блок
   лежит выше неё), и стоит он на одном месте всегда.
   --------------------------------------------------------------------- */
/* --say-h — высота полосы ввода; её ведёт js, когда поле растёт под
   текст. Из неё считается низ ленты, чтобы последнее облачко не
   уходило под полосу. */
.screen--talk {
  gap: 8px;
  --say-h: 52px;
  padding-bottom: calc(86px + var(--say-h) + var(--safe-bottom));
}

.talk {
  display: flex; flex-direction: column; gap: 7px;
  padding: 2px 0 6px;
}

/* Дата между днями. Одна на день, по центру: время в облачке
   показывает часы, а какой это был день — только эта строка. */
.talk__day {
  align-self: center; margin: 6px 0 2px;
  padding: 3px 10px; border-radius: 9px;
  background: rgba(255, 255, 255, .05);
  color: var(--ink-faint); font-size: 10.5px; letter-spacing: .04em;
}

.talk__one { display: flex; }
.talk__one.is-mine { justify-content: flex-end; }

/* Облачко, нарисованное до ответа сервера. Приглушено, но читаемо:
   написанное уже на экране, а подтверждения ещё нет. Время в нём тоже
   своё, местное, — сервер пришлёт своё и облачко подменится. */
.talk__one.is-wait .bub { opacity: .62; }

.bub {
  position: relative;
  display: flex; flex-direction: column; gap: 5px;
  max-width: 84%; min-width: 72px;
  padding: 9px 11px 7px;
  border-radius: 16px 16px 16px 5px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  box-shadow: 0 10px 24px -22px #000;
}
/* Своё облачко — холодной заливкой и с загнутым другим углом: в ленте
   сторона читается раньше, чем прочитан текст. */
.is-mine .bub {
  border-radius: 16px 16px 5px 16px;
  background: linear-gradient(170deg, rgba(62, 148, 236, .3),
                                      rgba(24, 70, 134, .22));
  border-color: var(--brand-line);
}

.bub__say {
  /* pre-wrap — абзацы человека остаются абзацами; break-word —
     ссылка в тридцать знаков не растягивает облачко за край экрана. */
  white-space: pre-wrap; overflow-wrap: break-word; word-break: break-word;
  font-size: 14.5px; line-height: 1.38; color: var(--ink);
}

.bub__at {
  align-self: flex-end;
  font-size: 10px; color: var(--ink-faint); font-variant-numeric: tabular-nums;
}

.bub__shot {
  display: block; width: 100%; max-height: 320px;
  border-radius: 11px; object-fit: cover;
  background: var(--surface-3);
  cursor: pointer;
}

/* Вложение, которое не картинка. Нажатие — скачивание, и стрелка
   справа говорит об этом раньше, чем человек нажмёт. */
.bub__doc {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 9px; border-radius: 11px;
  background: rgba(255, 255, 255, .05);
  border: 1px solid var(--line);
  cursor: pointer;
}
.bub__doc-ico { width: 20px; height: 20px; flex: none; color: var(--brand); }
.bub__doc-text {
  display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1;
}
.bub__doc-text b {
  font-size: 13px; font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bub__doc-text span { font-size: 10.5px; color: var(--ink-faint); }
.bub__doc-go { width: 16px; height: 16px; flex: none; color: var(--ink-dim); }

/* --- полоса ввода --------------------------------------------------- */
/* Стоит над плавающей панелью: 71 точку занимает сама панель (50 на
   вкладку, её поля и отступ от низа), остальное — воздух.

   Края берутся от --pad, а не от нуля: полоса должна стоять ровно по
   бокам ленты, а --pad у экрана и правит эти поля. */
.dock {
  position: absolute; z-index: 5;
  left: var(--pad); right: var(--pad);
  bottom: calc(78px + var(--safe-bottom));
  display: flex; flex-direction: column; gap: 6px;
}

.say {
  display: flex; align-items: flex-end; gap: 7px;
  padding: 7px; border-radius: 22px;
  background: rgba(13, 25, 42, .9);
  -webkit-backdrop-filter: blur(20px) saturate(150%);
          backdrop-filter: blur(20px) saturate(150%);
  border: 1px solid var(--ice-line);
  box-shadow: 0 16px 38px -22px rgba(0, 0, 0, .95),
              inset 0 1px 0 var(--ice-lip);
}

.say__text {
  flex: 1; min-width: 0;
  /* Высоту ведёт js под число строк; resize убран — тянуть угол
     в вебвью всё равно нечем, а полоска в нём рисуется. */
  height: 38px; max-height: 108px; resize: none;
  padding: 9px 2px; border: 0; background: none;
  color: var(--ink); font: inherit; font-size: 14.5px; line-height: 1.3;
}
.say__text::placeholder { color: var(--ink-faint); }
.say__text:focus { outline: none; }

.say__clip, .say__go {
  flex: none;
  display: grid; place-items: center;
  width: 38px; height: 38px; border-radius: 50%;
  border: 1px solid var(--line-2); background: rgba(255, 255, 255, .05);
  color: var(--ink-dim);
}
.say__clip svg, .say__go svg { width: 18px; height: 18px; }
.say__go {
  background: linear-gradient(180deg, rgba(62, 148, 236, .6),
                                      rgba(24, 70, 134, .4));
  border-color: var(--brand-line); color: #EAF7FF;
}
.say__clip:disabled, .say__go:disabled { opacity: .45; }

/* --- список чатов у админа ------------------------------------------- */
/* Строка собрана из блока состава (.crew__one): это тот же список
   людей, и расходиться по виду им незачем. Своё здесь только число
   непрочитанного и галочка справа. */
.chat__new {
  flex: none;
  min-width: 20px; height: 20px; padding: 0 6px;
  display: grid; place-items: center;
  border-radius: 10px;
  background: var(--brand); color: #04101D;
  font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.chat__go { flex: none; width: 18px; height: 18px; color: var(--ink-faint); }

/* Число непрочитанного на кнопке «Чаты поддержки» в сводке. */
.act__tag {
  margin-left: auto;
  min-width: 20px; height: 20px; padding: 0 6px;
  display: grid; place-items: center;
  border-radius: 10px;
  background: var(--brand); color: #04101D;
  font-size: 11px; font-weight: 700;
}
.act__tag[hidden] { display: none; }
