/* Vibe Chess Mini App — токены и компоненты.
   Тёмная база по дизайну; из themeParams Telegram берём фон/текст, если клиент
   их отдаёт, чтобы приложение оставалось родным в любой теме. */

:root {
  --bg:        #1A1A1A;
  --card:      #21201F;
  --row:       #2B2A28;
  --hero:      #FFEC42;
  --action:    #FFCC00;
  --action-2:  #FFDB4D;
  --success:   #00A046;
  --success-t: #3FBF74;
  --danger:    #FF3333;
  --ink:       #21201F;

  --t1: #FFFFFF;
  --t2: rgba(255,255,255,.62);
  --t3: rgba(255,255,255,.42);
  --line: rgba(255,255,255,.08);
  --line-2: rgba(255,255,255,.16);

  /* Единый вертикальный шаг между блоками — вся раскладка приложения меряется
     им. Правится в одном месте: раньше отступы разъезжались по экранам (14px в
     ленте, 6px между секциями, 8px под заголовком, 6px в профиле). */
  --gap: 10px;
  /* Отступ НАД заголовком секции. Заголовок начинает новый смысловой блок,
     поэтому сверху воздуха втрое больше, чем снизу: так видно, что он относится
     к тому, что ниже, а не к тому, что выше. Правится тут, на всё приложение. */
  --gap-head: 30px;

  --r-card: 22px;
  --r-row: 16px;
  --r-btn: 14px;
  --r-pill: 9999px;

  --sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --mono: ui-monospace, Menlo, Consolas, monospace;

  /* Перезаписываются из JS значениями Telegram (safeArea + contentSafeArea).
     В самих отступах ниже стоит max() с env(), чтобы на старых клиентах,
     где Telegram не отдаёт эти величины, работал системный вырез. */
  --safe-top: 0px;
  --safe-bottom: 0px;

  /* Поля шапки под кнопки Telegram («Закрыть» слева, «⌄ …» справа).
     Замерено по скриншоту iPhone: «Закрыть» заканчивается на ~108pt,
     берём 116px — с запасом и на более широкие локали. */
  --tb-side: 116px;
}

* { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; }

/* `hidden` должен прятать ЧТО УГОДНО. Браузерное правило для него —
   `display: none` с нулевой специфичностью, поэтому любой наш `display: flex`
   его перебивает. На этом уже дважды обожглись: вкладки оставались
   кликабельными на экране входа (тап уводил на экран с авторизацией, и человек
   видел голое «Не авторизовано»), а блок с кодом входа показывался до нажатия
   кнопки. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  /* Страница не прокручивается вовсе — ни вбок, ни вниз. Прокрутка живёт
     внутри #screen, а высота приложения привязана к видимой области. Стоит
     разрешить прокрутку странице, и iOS при фокусе в поле «доскролливает» до
     него сам: наша шапка уезжала под кнопки Telegram, а форма выглядела
     обрезанной, пока экран не потянут пальцем. */
  overflow: hidden;
  background: var(--bg);
  color: var(--t1);
  font-family: var(--sans);
  font-size: 19px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}

button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
a { color: var(--action); text-decoration: none; }

/* --app-h ставит JS по visualViewport (см. applyViewportHeight): при открытой
   клавиатуре приложение ужимается, а не уезжает вместе со страницей. */
#root { position: relative; display: flex; flex-direction: column; height: var(--app-h, 100%); }

/* ---------- Веб-версия на широком экране ----------
   Интерфейс рисовался под телефон: во всю ширину монитора строки растягиваются
   на пол-экрана и читать их неудобно. Поэтому на широких экранах зажимаем
   приложение в колонку телефонных пропорций (≈393×852 у iPhone, здесь 1:2.1).

   Порог 560px намеренно выше телефонной ширины: и в Telegram на телефоне, и в
   десктопном Telegram окно мини-аппа узкое, туда эти правила не попадают и
   приложение остаётся во весь экран. */
@media (min-width: 560px) {
  body {
    display: flex; align-items: center; justify-content: center;
    background: #0E0E0E;            /* поле вокруг «телефона» */
  }
  #root {
    /* Ширину тоже ограничиваем высотой окна: иначе на низком окне (ноутбук,
       открытая консоль) колонка осталась бы в 420px и стала приземистой —
       пропорции телефона потерялись бы. Делитель 2.1 ≈ 852/393 у iPhone. */
    width: min(420px, calc((var(--app-h, 100vh) - 32px) / 2.1));
    /* Высота — по экрану, но не выше телефонных пропорций: на высоком мониторе
       колонка иначе вытянулась бы в неестественно узкую полосу. */
    height: min(var(--app-h, 100%) - 32px, 880px);
    border: 1px solid var(--line);
    border-radius: 28px;
    overflow: hidden;               /* скругление должно резать и шапку, и вкладки */
    box-shadow: 0 24px 60px rgba(0, 0, 0, .55);
  }
}

/* ---------- Шапка ---------- */
/* Заголовок строго по центру. Боковые поля (--tb-side) зарезервированы под
   кнопки Telegram, которые лежат поверх страницы: слева «Закрыть», справа
   «⌄ …». Благодаря им длинный заголовок обрежется многоточием, а не заедет
   под кнопки. */
#topbar {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  padding-top: calc(10px + max(env(safe-area-inset-top, 0px), var(--safe-top)));
  padding-right: var(--tb-side); padding-bottom: 10px; padding-left: var(--tb-side);
  border-bottom: 1px solid var(--line);
  flex: none;
}
/* Своя кнопка «назад» — только в браузере: в Telegram её рисует сам клиент.
   Абсолютным позиционированием, чтобы заголовок остался ровно по центру. */
.tb-back {
  position: absolute; left: 6px; bottom: 4px;
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; padding: 0;
  border: 0; background: none; color: var(--text); cursor: pointer;
  border-radius: 50%;
}
.tb-back:active { background: var(--line); }
.tb-back svg { width: 22px; height: 22px; }

.tb-title {
  font-size: 18px; font-weight: 600; letter-spacing: -.01em; text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}

/* Логотип Vibe Chess. Исходник — светлый PNG на прозрачном фоне:
   на тёмных подложках идёт как есть, на светлых затемняем фильтром
   (brightness(0) делает его чёрным — контраст как в дизайне: ink на yellow). */
.logo-img { display: block; width: auto; object-fit: contain; }
/* Аватарка Telegram — заполняет круг целиком */
.ava-img { display: block; width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
.empty-icon .logo-img { height: 34px; opacity: .55; }
.opp-ava { display: flex; align-items: center; justify-content: center;
  font: 600 18px/1 var(--sans); color: rgba(33,32,31,.55); overflow: hidden; }
.prof-ava { display: flex; align-items: center; justify-content: center;
  font: 600 26px/1 var(--sans); color: var(--t2); overflow: hidden; }

/* ---------- Контент ---------- */
#screen { position: relative; flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch;
  scrollbar-width: none; padding: 14px 16px 20px; }
#screen::-webkit-scrollbar { display: none; }
#screen.flush { padding: 0; }

.stack { display: flex; flex-direction: column; gap: var(--gap); }
/* Один и тот же шаг по вертикали для всего экрана — и между группами, и внутри
   них. В профиле подряд шли три разных отступа (10px между плитками
   статистики, 14px до «Редактировать профиль», 6px между строками), и список
   выглядел сбитым. Значение то же, что у плиток статистики. */
.stack--even { gap: 10px; }

/* ---------- Секции ---------- */
.sec-head {
  display: flex; align-items: baseline; justify-content: space-between;
  font-size: 16px; font-weight: 500; letter-spacing: .06em; text-transform: uppercase;
  color: var(--t3);
  /* Снизу отступ даёт шаг стека (--gap), поэтому здесь добавляем только
     разницу — иначе над заголовком вышло бы 40px вместо 30. */
  margin-top: calc(var(--gap-head) - var(--gap));
}
.sec-head .link { color: var(--action); text-transform: none; letter-spacing: 0;
  font-size: 17px; font-weight: 500; }

/* ---------- Карточка события ---------- */
.card {
  background: var(--card); border-radius: var(--r-card); padding: 16px;
  display: flex; flex-direction: column; gap: 12px; width: 100%; text-align: left;
  border: 1px solid transparent; transition: border-color .15s, transform .1s;
}
.card:active { transform: scale(.99); }

/* Карточка-выбор (экран «Что создаём»): та же рамка в 1px, что у .btn--ghost.
   Без неё три варианта выглядели как справочные блоки, а не как то, из чего
   нужно выбрать. */
.card--choice { border-color: var(--line); }

/* Ссылка-адрес: подчёркивание прерывистое — так видно, что это ссылка, но
   строка не спорит по весу с остальным текстом карточки. */
.linkish {
  align-self: flex-start; padding: 0;
  font-size: 17px; font-weight: 500; color: var(--action);
  text-decoration: underline dashed; text-underline-offset: 4px;
  text-decoration-thickness: 1px;
  text-decoration-color: rgba(255, 204, 0, .55);
}
.linkish:active { opacity: .6; }

/* Правка события: по виду ссылка, по разметке кнопка — открывает экран внутри
   приложения, а не ведёт по адресу (с <a href="#"> в адресе остался бы якорь).
   По центру и без подчёркивания: это отдельное действие под карточкой, а не
   часть текста. */
.link-edit {
  align-self: center; padding: 4px 0;
  font-size: 17px; font-weight: 500; color: var(--danger);
  text-decoration: none;
}
.link-edit:active { opacity: .6; }

/* Карточка с фото площадки: снимок идёт вровень с краями карточки, поэтому
   гасим её отступы отрицательными полями и скругляем только верх. overflow
   на самой карточке не годится — она даёт scale(.99) при нажатии. */
.card--photo { padding-top: 0; }
.card-photo {
  display: block;
  /* Компенсируем и padding (16px), и прозрачную рамку карточки (1px) — иначе
     по бокам и сверху оставалась полоска фона в пиксель. */
  width: calc(100% + 34px); margin: -1px -17px 0;
  height: 132px; object-fit: cover;
  border-radius: var(--r-card) var(--r-card) 0 0;
  background: var(--row);
}
.card--live { border-color: rgba(255,51,51,.4); }
.card--accent { background: var(--hero); color: var(--ink); }

.card-top { display: flex; align-items: flex-start; gap: 10px; }
.card-title { flex: 1; font-size: 20px; font-weight: 600; line-height: 1.25;
  letter-spacing: -.01em; min-width: 0; }
/* align-items: center обязателен. По умолчанию элементы растягиваются на всю
   высоту строки, и обычный текст прижимается к её верху, а внутри плашки
   («10», «5+3») он отцентрован — из-за этого подписи и плашка шли вразнобой.
   С центрированием текст в строке лежит на одной линии. */
.card-meta { display: flex; flex-wrap: wrap; align-items: center;
  gap: 6px 12px; font-size: 17px; color: var(--t2); }
.card--accent .card-meta { color: rgba(33,32,31,.62); }
.card-meta b { color: var(--t1); font-weight: 500; }
.card--accent .card-meta b { color: var(--ink); }

/* ---------- Бейджи ---------- */
.badge {
  display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px;
  border-radius: var(--r-pill); font-size: 16px; font-weight: 500; flex: none;
}
.badge--live   { background: var(--danger); color: #fff; }
.badge--open   { background: var(--action); color: var(--ink); }
.badge--closed { background: var(--row); color: var(--t2); }
.badge--done   { background: rgba(0,160,70,.18); color: var(--success-t); }
.badge--draft  { background: var(--row); color: var(--t3); border: 1px dashed var(--line-2); }
.dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor;
  animation: pulse 2s ease-in-out infinite; }
@keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: .5 } }

.tc { display: inline-flex; align-items: center; height: 28px; padding: 0 10px;
  border-radius: 8px; background: var(--row); color: var(--t1);
  font: 500 16px/1 var(--mono); flex: none; }
.card--accent .tc { background: rgba(33,32,31,.1); color: var(--ink); }

/* Тип события рядом с контролем времени. Отдельно от .tc: там моноширинный
   шрифт с разрядкой — он хорош для «10+5», но слова в нём выглядят разорванными. */
/* Чуть мельче .tc — чтобы «рапид · 10 мин» и «свободная встреча» помещались
   в одну строку на телефоне шириной 393pt. На более узких перенос сработает
   сам, ряд для этого и flex-wrap. */
.kind { display: inline-flex; align-items: center; height: 28px; padding: 0 8px;
  border-radius: 8px; background: var(--row); color: var(--t2);
  font: 500 15px/1 var(--sans); flex: none; }

/* Разделители «·» между значениями в строке. Псевдоэлемент, а не символ в
   разметке: при переносе строки лишняя точка не повиснет в начале. */
.card-meta--dots span + span::before {
  content: '·'; color: var(--t3); margin-right: 12px;
}
/* Описание события — сплошной текст, а не строка меток: даём ему нормальную
   межстрочную высоту и переносы по абзацам, как их набрал организатор. */
.card-desc { display: block; line-height: 1.45; white-space: pre-line; }
.card-meta--dots { gap: 6px 8px; }

/* ---------- Окно с QR-кодом ---------- */
.qr-wrap {
  position: fixed; inset: 0; z-index: 200; display: flex;
  align-items: center; justify-content: center; padding: 24px;
  background: rgba(0, 0, 0, .72);
}
.qr-card {
  background: var(--card); border-radius: var(--r-card); padding: 20px;
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  max-width: 340px; width: 100%;
}
.qr-title { font: 600 19px/1.25 var(--sans); text-align: center; }
/* Белая подложка обязательна: сканеры плохо читают тёмный QR, а картинка
   приходит с прозрачным фоном. */
.qr-img { width: 100%; max-width: 260px; border-radius: 12px; background: #fff;
  padding: 10px; }
.qr-note { font-size: 16px; line-height: 1.4; color: var(--t2); text-align: center; }

/* Число досок. Своя окраска, а не общий «призрак»: кнопка сообщает состояние —
   хватает досок или часть пар будет ждать, — и на фоне соседних кнопок она
   иначе терялась. Зелёная = всё сходится, жёлтая = обратите внимание. */
/* Кнопка числа досок. Колонкой: подпись под числом блочная и в общей строке
   разрывала «2 доски» пополам. Ростом с карточку, а не с обычную кнопку —
   она несёт две строки и читается как отдельный блок настройки. */
.btn--tables {
  flex-direction: column; gap: 5px; padding: 18px 16px; min-height: 78px;
  border-radius: var(--r-row); line-height: 1.3;
  background: var(--card); border: 1px solid var(--line-2);
  color: var(--t1); font-size: 21px;
}
.btn--tables .row-hint {
  margin-top: 0; font-weight: 400; font-size: 16px; color: var(--success-t);
}
.btn--tables-warn .row-hint { color: var(--action); }

/* Отбивка перед элементом: к общему шагу стека добавляется столько, чтобы
   вышло --gap-head. Тем же значением, что у заголовков секций, — так по всему
   приложению одна мера «большого» промежутка. */
.gap-before { margin-top: calc(var(--gap-head) - var(--gap)); }

/* Сетка выбора числа: цифры крупные, чтобы попадать пальцем. */
.pick-grid { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.pick {
  min-width: 56px; height: 52px; padding: 0 14px; border-radius: var(--r-btn);
  background: var(--row); color: var(--t1); font: 600 20px/1 var(--sans);
  border: 1px solid var(--line);
}
.pick--on { background: var(--action); color: var(--ink); border-color: transparent; }
.pick:active { transform: scale(.97); }

/* ---------- Тумблер и подпись под пунктом ---------- */
.row-hint { display: block; font-size: 15px; line-height: 1.35; color: var(--t3);
  margin-top: 3px; }
.sw {
  flex: none; width: 52px; height: 31px; border-radius: 9999px;
  background: var(--row); position: relative; transition: background .18s;
}
.sw i {
  position: absolute; top: 3px; left: 3px; width: 25px; height: 25px;
  border-radius: 50%; background: #fff; transition: transform .18s;
}
.sw--on { background: var(--success); }
.sw--on i { transform: translateX(21px); }

/* Кнопка «Написать» — в цветах Telegram: действие происходит там, и синий
   говорит об этом раньше, чем подпись. */
.btn-tg {
  flex: none; background: #2AABEE; color: #fff;
  border-radius: var(--r-btn); padding: 11px 18px; font: 600 17px/1 var(--sans);
}
.btn-tg:active { opacity: .85; }
/* Та же кнопка, но главным действием на экране профиля. */
.btn-tg--wide { display: flex; align-items: center; justify-content: center;
  width: 100%; padding: 16px; font-size: 19px; border-radius: var(--r-btn); }

/* Строка контакта кликабельна целиком, поэтому ведёт себя как кнопка. */
.plr[data-go] { text-align: left; cursor: pointer; }
.plr[data-go]:active { transform: scale(.99); }

/* ---------- История партий в профиле ---------- */
.hist {
  display: flex; align-items: center; gap: 12px;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-row); padding: 12px 14px;
}
/* Результат — крупной цифрой слева: по колонке 1/0/½ история читается
   быстрее, чем по словам. */
.hist-res {
  flex: none; width: 34px; height: 34px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font: 600 18px/1 var(--sans);
}
.hist-res--win  { background: rgba(0,160,70,.18); color: var(--success-t); }
.hist-res--loss { background: rgba(255,51,51,.16); color: #FF6B6B; }
.hist-res--draw { background: var(--row); color: var(--t2); }
.hist-mid { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.hist-name { font: 600 18px/1.25 var(--sans); overflow-wrap: anywhere; }
.hist-when { font-size: 16px; color: var(--t3); margin-top: 2px; }
.hist-delta { flex: none; font: 600 17px/1 var(--sans); font-variant-numeric: tabular-nums; }
.hist-delta.up { color: var(--success-t); }
.hist-delta.down { color: #FF6B6B; }

/* ---------- Строка участника встречи ---------- */
.plr {
  display: flex; align-items: center; gap: 12px;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-row); padding: 12px 14px;
}
.plr-ava {
  width: 44px; height: 44px; border-radius: 50%; flex: none; overflow: hidden;
  background: var(--row); display: flex; align-items: center; justify-content: center;
  font: 600 17px/1 var(--sans); color: var(--t2);
}
/* Имя с аватаркой открывают профиль, «Выбрать» рядом — отдельное действие.
   Поэтому кликабельна не вся строка, а только эта её часть. */
.plr-open {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 12px;
  text-align: left; padding: 0;
}
.plr-open:active { opacity: .7; }

.plr-mid { flex: 1; min-width: 0; }
.plr-name { font: 600 19px/1.25 var(--sans); overflow-wrap: anywhere; }
.plr-sub { font-size: 17px; color: var(--t2); margin-top: 2px; }
.plr-pick {
  flex: none; background: var(--action); color: var(--ink);
  border-radius: var(--r-btn); padding: 11px 20px; font: 600 18px/1 var(--sans);
}
.plr-pick:active { transform: scale(.98); }

/* «Стол ожидается» — главный вопрос игрока, пока пары не расставлены.
   Крупно и над блоком партии, чтобы читалось с расстояния вытянутой руки. */
.table-wait {
  text-align: center; padding: 14px 12px; border-radius: var(--r-row);
  background: rgba(255, 204, 0, .12); border: 1px solid rgba(255, 204, 0, .4);
  color: var(--action); font: 600 22px/1.2 var(--sans);
  letter-spacing: .02em; text-transform: uppercase;
}
/* Назначенный стол — жирным: это то, что ищут глазами. */
.duel-table { color: var(--t1); font-weight: 700; }
/* Пешка перед именем в списке пар: сразу видно, кто какими играет.
   Тот же силуэт, что в чипах цвета выше, — фигура в приложении одна. */
.pawn { display: inline-block; width: 13px; height: 13px; margin-right: 7px;
  vertical-align: -1px; }
.pawn svg { width: 100%; height: 100%; display: block; }
/* Через .pair-names: у строки пары есть правило `.pair-names span`, и по
   специфичности оно перебивало цвет пешки — обе выходили одинаковыми. */
.pair-names .pawn--w { color: #fff; }
.pair-names .pawn--b { color: rgba(255, 255, 255, .38); }

/* Пометка гостя в списке пар: приписка к имени, а не плашка — строка узкая. */
.guest-mark { color: var(--t3); font-weight: 400; font-size: 15px; margin-left: 6px; }

/* ---------- Блок идущей партии «дуэль» (макет 2a) ----------
   Раскладка и цвета — из макета; блок задуман самостоятельным островом на
   тёмном фоне, поэтому здесь конкретные значения, а не токены.
   Размеры шрифта подняты до общего минимума приложения (16-17px): в макете
   они были от 11px, а это заметно мельче всего остального интерфейса. */
.duel {
  background: var(--card); border: 1px solid rgba(255,255,255,.08);
  border-radius: 20px; padding: 18px 16px 16px;
  display: flex; flex-direction: column; gap: 16px;
}
.duel-top { display: flex; align-items: center; justify-content: space-between; }
.duel-lbl { font: 500 16px/1.2 var(--sans); letter-spacing: .06em;
  text-transform: uppercase; color: rgba(255,255,255,.5); }
.duel-live {
  display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px;
  border-radius: var(--r-pill); background: rgba(255,51,51,.16); color: #FF6B6B;
  font: 500 16px/1 var(--sans);
}
.duel-dot { width: 5px; height: 5px; border-radius: 50%; background: #FF3333;
  animation: duel-pulse 2s ease-in-out infinite; }
@keyframes duel-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .25 } }

.duel-row { display: flex; align-items: flex-start; gap: 8px; }
.duel-side { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 10px;
  min-width: 0; }
.duel-ava {
  width: 64px; height: 64px; border-radius: 50%; background: var(--row); flex: none;
  display: flex; align-items: center; justify-content: center;
  font: 600 22px/1 var(--sans); color: rgba(255,255,255,.75);
  overflow: hidden;
}
/* Своё кольцо — единственная пометка «это вы»: подписи одинаковой длины, и без
   кольца сторону приходилось бы вычитывать. */
.duel-ava--mine { box-shadow: 0 0 0 2px var(--action); color: var(--action); }
.duel-name { text-align: center; min-width: 0; }
.duel-nm { font: 600 19px/1.25 var(--sans); overflow-wrap: anywhere; }
.duel-role { font: 400 17px/1.3 var(--sans); color: rgba(255,255,255,.45); margin-top: 2px; }
.duel-vs { display: flex; flex-direction: column; align-items: center; padding-top: 20px;
  font: 600 17px/1 var(--sans); color: rgba(255,255,255,.32); letter-spacing: .04em; }

.duel-chip {
  display: inline-flex; align-items: center; gap: 7px; height: 34px; padding: 0 14px;
  border-radius: var(--r-pill); font: 600 17px/1 var(--sans); flex: none;
}
.duel-chip svg { width: 15px; height: 15px; flex: none; }
.duel-chip--w { background: #fff; color: #21201F; }                    /* играет белыми */
.duel-chip--b { background: var(--row); color: rgba(255,255,255,.85);  /* играет чёрными */
  border: 1px solid rgba(255,255,255,.12); }

/* ---------- Окно подтверждения результата (кольцо отсчёта) ---------- */
/* Заполнение кольца задаёт --p из JS. conic-gradient, а не SVG: одна строка,
   и не нужно считать длину окружности. */
.conf-ring {
  --p: 0%;
  /* Воздух вокруг кольца. Экран короткий, всё содержимое жалось к шапке —
     кольцо тут главное, и ему нужен запас сверху и снизу. К нижнему полю
     добавляется ещё общий шаг стека. */
  margin: 34px 0 18px;
  position: relative; width: 172px; height: 172px; align-self: center;
  border-radius: 50%;
  background: conic-gradient(var(--action) var(--p), rgba(255,255,255,.1) 0);
  transition: background .3s linear;
}
.conf-ring--done { background: var(--success); }
.conf-ring-in {
  position: absolute; inset: 10px; border-radius: 50%; background: var(--bg);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
}
.conf-num { font: 600 40px/1 var(--sans); font-variant-numeric: tabular-nums; }
.conf-sub { font-size: 14px; color: var(--t3); }
.conf-head { font: 600 21px/1.3 var(--sans); text-align: center; }
.conf-note { font-size: 17px; line-height: 1.5; color: var(--t2); text-align: center;
  max-width: 320px; align-self: center; }

/* ---------- Живой турнир: героическая плашка ---------- */
.hero {
  background: var(--hero); color: var(--ink); border-radius: var(--r-card);
  padding: 18px; display: flex; flex-direction: column; gap: 14px;
}
/* Блок идущей партии на встрече. Жёлтый фон .hero там спорил с жёлтыми же
   кнопками «Открыть таймер» и «Записать результат» — плашка читалась как ещё
   одна кнопка. Светло-серый: заметно, но не соперничает с действиями. */
.hero--calm { background: #D9D7D2; }
.hero--calm .hero-lbl { color: rgba(33,32,31,.6); }

.hero-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.hero-lbl { font-size: 16px; font-weight: 500; letter-spacing: .06em;
  text-transform: uppercase; color: rgba(33,32,31,.55); }
.hero-table { font-size: 72px; font-weight: 600; line-height: .9; letter-spacing: -.03em;
  font-variant-numeric: tabular-nums; margin-top: 6px; }
.hero-color { display: flex; flex-direction: column; align-items: center; gap: 7px; flex: none; }
/* Кружок цвета: фон противоположный, пешка внутри — цвета игрока (см.
   colorChip). currentColor задаёт заливку фигуры, поэтому SVG один на оба
   варианта. */
.chip { width: 54px; height: 54px; border-radius: 50%; border: 2px solid var(--ink);
  display: flex; align-items: center; justify-content: center; flex: none; }
.chip svg { width: 60%; height: 60%; display: block; }
.chip--w { background: #fff; color: #21201F; }        /* играет чёрными */
.chip--b { background: #0D0D0D; color: #fff; }        /* играет белыми */
.hero-color b { font-size: 18px; font-weight: 600; }

.opp {
  display: flex; align-items: center; gap: 12px;
  background: rgba(33,32,31,.08); border-radius: var(--r-row); padding: 11px 13px;
}
.opp-ava { width: 38px; height: 38px; border-radius: 50%; flex: none;
  background: rgba(33,32,31,.12); display: flex; align-items: center;
  justify-content: center; font-size: 21px; overflow: hidden; }
.opp-name { font-size: 18px; font-weight: 600; line-height: 1.25;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.opp-sub { font-size: 16px; color: rgba(33,32,31,.6); }

/* ---------- Переключатель туров ---------- */
.rounds { display: flex; gap: 6px; }
.rounds button {
  flex: 1; height: 38px; border-radius: 11px; border: 1px solid var(--line);
  font-size: 17px; font-weight: 500; color: var(--t1);
  display: flex; align-items: center; justify-content: center;
}
.rounds button.on { background: var(--row); border-color: var(--line-2); }
.rounds button.future { color: var(--t3); }

/* ---------- Список пар ---------- */
.pair {
  display: flex; gap: 12px; align-items: center; padding: 10px 12px;
  border-radius: var(--r-row); border: 1px solid transparent;
}
.pair.mine { background: rgba(255,204,0,.1); border-color: rgba(255,204,0,.35); }
.pair-no { width: 26px; font-size: 18px; font-weight: 600; color: var(--t3);
  font-variant-numeric: tabular-nums; flex: none; }
.pair.mine .pair-no { color: var(--action); }
/* Аватарка участника. Размер фиксирован и flex: none — иначе при длинном имени
   кружок сплющивался бы в овал. */
.pair-ava {
  width: 34px; height: 34px; border-radius: 50%; flex: none; overflow: hidden;
  background: var(--row); display: flex; align-items: center; justify-content: center;
}
/* Инициалы-заглушка: размер подобран под кружок, чтобы две буквы не жались
   к краям. Цвет приглушён — это подпись, а не имя. */
.pair-ava { font: 600 15px/1 var(--sans); color: var(--t2); }

.pair-names { flex: 1; font-size: 18px; line-height: 1.4; min-width: 0; }
.pair-names span { color: var(--t2); }
.pair-res { font-size: 17px; font-weight: 500; font-variant-numeric: tabular-nums;
  color: var(--t3); flex: none; }
.pair-res.set { color: var(--t1); }

/* ---------- Таблица ---------- */
.tbl { display: flex; flex-direction: column; }
.tbl-head, .tbl-row {
  display: grid; grid-template-columns: 32px 1fr 52px 52px; gap: 8px;
  align-items: center; padding: 10px 12px;
}
.tbl-head { font-size: 16px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--t3); position: sticky; top: 0; background: var(--bg); z-index: 2; }
.tbl-row { border-radius: var(--r-row); border: 1px solid transparent; }
.tbl-row.me { background: rgba(255,204,0,.1); border-color: rgba(255,204,0,.35); }
.tbl-rank { font-weight: 600; font-variant-numeric: tabular-nums; color: var(--t2); }
.tbl-row.me .tbl-rank { color: var(--action); }
.tbl-name { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-size: 18px; }
.tbl-pts { text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; }
.tbl-bh { text-align: right; font-size: 17px; color: var(--t3);
  font-variant-numeric: tabular-nums; }

/* ---------- Ввод результата ---------- */
.res-opts { display: flex; flex-direction: column; gap: var(--gap); }
/* Тот же набор в живом турнире — сеткой 2×2: там он стоит прямо под партией,
   и четыре строки во всю ширину отодвинули бы список пар за экран. */
.res-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.res-grid .res-btn { font-size: 17px; padding: 13px 12px; gap: 8px;
  justify-content: center; text-align: center; }
.res-grid .res-btn .k { width: auto; font-size: 15px; }
.res-btn {
  display: flex; align-items: center; gap: 12px; width: 100%;
  background: var(--card); border-radius: var(--r-row); padding: 15px 16px;
  border: 1px solid var(--line); font-size: 19px; font-weight: 500; text-align: left;
}
.res-btn:active { transform: scale(.99); }
.res-btn .k { font: 600 18px/1 var(--mono); color: var(--t3); flex: none; width: 34px; }
.res-btn--win  { border-color: rgba(0,160,70,.35); }
.res-btn--draw { border-color: var(--line-2); }
.res-btn--loss { border-color: rgba(255,255,255,.12); }
.res-btn--nos  { border-color: rgba(255,51,51,.3); color: var(--t2); }

.wait {
  display: flex; align-items: center; gap: 12px; background: var(--card);
  border: 1px solid rgba(255,204,0,.35); border-radius: var(--r-row); padding: 12px 14px;
}
.wait-ring { position: relative; width: 34px; height: 34px; flex: none; }
.wait-ring i { position: absolute; inset: 0; border-radius: 50%; }
.wait-ring i:last-child { inset: 4px; background: var(--card); }
.wait-txt { flex: 1; font-size: 17px; line-height: 1.35; color: var(--t2); }
.wait-txt b { color: var(--t1); font-weight: 600; font-variant-numeric: tabular-nums; }

/* ---------- Профиль ---------- */
.prof-head { display: flex; align-items: center; gap: 14px; }
.prof-ava { width: 64px; height: 64px; border-radius: 50%; background: var(--row);
  display: flex; align-items: center; justify-content: center; font-size: 32px; flex: none;
  overflow: hidden; }
.prof-name { font-size: 23px; font-weight: 600; letter-spacing: -.01em; }
.prof-sub { font-size: 17px; color: var(--t2); }

.stats { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--gap); }
.stat { background: var(--card); border-radius: var(--r-row); padding: 14px; }
.stat b { display: block; font-size: 30px; font-weight: 600; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums; }
.stat span { font-size: 16px; color: var(--t2); }

/* min-width: 0 обязателен: у нативных input[type=date/time] большая
   минимальная ширина (формат + системная иконка), и во flex-ряду они
   отказывались сжиматься — поле времени выезжало и наезжало на дату. */
.field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.field label { font-size: 16px; color: var(--t2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.field input, .field textarea, .field select {
  width: 100%; max-width: 100%; background: var(--card); border: 1px solid var(--line);
  border-radius: 12px; padding: 13px 14px; color: var(--t1);
  font-family: inherit; font-size: 19px;
}
.field input:focus, .field textarea:focus, .field select:focus { outline: none; border-color: var(--line-2); }
.field textarea { resize: vertical; min-height: 80px; }
/* select в тёмной теме: убираем системную стрелку и рисуем свою */
.field select { appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%23ffffff88' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 16px center;
  padding-right: 40px; }
.field select option { background: #21201F; color: #fff; }

/* Дата и время — ОДНО поле datetime-local, а не два в ряд.
   Раньше стояли рядом, и на iOS нативные виджеты шире, чем в десктопном
   Chrome: поле времени наезжало на дату. Одно поле убирает саму возможность
   разъехаться, заодно на телефоне открывается единый нативный выбор. */
/* Дата рождения в профиле — то же поведение, но без времени. */
.field input[type="datetime-local"], .field input[type="date"] {
  font-size: 18px;
  -webkit-appearance: none; appearance: none;   /* iOS иначе навязывает свою ширину */
  min-height: 52px;
}
.field input[type="datetime-local"]::-webkit-calendar-picker-indicator,
.field input[type="date"]::-webkit-calendar-picker-indicator {
  filter: invert(1); opacity: .45;
}
.field input[type="datetime-local"]::-webkit-date-and-time-value,
.field input[type="date"]::-webkit-date-and-time-value { text-align: left; }

/* Плашка отладки — видна только админу (см. is_admin в /api/me).
   По ней вы называете экран, а я по коду нахожу нужный SCREENS.*

   Намеренно обычный элемент потока (не position: fixed): фиксированная плашка
   наезжала на главную кнопку, а вычислять её отступ под каждый экран — лишняя
   хрупкость. В потоке она просто занимает свою строку над вкладками. */
#dm-badge {
  /* Поверх содержимого, у правого нижнего угла. Отступ снизу ставит JS
     (placeDmBadge): он зависит от того, видны ли сейчас вкладки и главная
     кнопка. */
  position: absolute; right: 10px; z-index: 3; margin: 0;
  display: flex; align-items: center; gap: 6px;
  height: 26px; padding: 0 10px; border-radius: var(--r-pill);
  background: rgba(20, 20, 20, .78); border: 1px solid rgba(255, 204, 0, .45);
  color: var(--action); font: 600 13px/1 var(--mono);
  letter-spacing: .02em; opacity: .85;
}
#dm-badge:active { opacity: 1; }

#dm-badge .dm-scr { font: 500 12px/1 var(--sans); color: var(--t2); }

.rowlink {
  display: flex; align-items: center; gap: 12px; width: 100%;
  background: var(--card); border-radius: var(--r-row); padding: 14px 16px;
  font-size: 19px; text-align: left;
}
.rowlink .chev { color: var(--t3); font-size: 23px; }
.rowlink span.val { color: var(--t2); font-size: 18px; }

/* ---------- Пустое состояние ---------- */
.empty { display: flex; flex-direction: column; align-items: center; gap: 12px;
  text-align: center; padding: 32px 20px; }
.empty-icon { width: 64px; height: 64px; border-radius: 50%; background: var(--card);
  display: flex; align-items: center; justify-content: center; font-size: 34px;
  color: var(--t3); }
.empty h3 { font-size: 21px; font-weight: 600; letter-spacing: -.01em; }
.empty p { font-size: 18px; color: var(--t2); max-width: 300px; }

/* ---------- Кнопки ---------- */
.btn {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  height: 54px; border-radius: var(--r-btn); font-size: 19px; font-weight: 600;
  width: 100%;
}
.btn:active { transform: scale(.99); }
.btn--primary { background: var(--action); color: var(--ink); }
.btn--ghost { background: var(--card); color: var(--t1); border: 1px solid var(--line); }
/* Кнопка-действие организатора: обычная по форме, но подпись жёлтая — по ней
   видно, что это его инструмент, а не общий пункт. */
.btn--accent { color: var(--action); }
.btn--danger { background: transparent; color: var(--danger);
  border: 1px solid rgba(255,51,51,.3); }

#mainbtn-wrap { flex: none; padding: 8px 16px 10px; }
#mainbtn { display: flex; align-items: center; justify-content: center;
  width: 100%; height: 54px; border-radius: var(--r-btn);
  background: var(--action); color: var(--ink); font-size: 19px; font-weight: 600; }
#mainbtn:disabled { opacity: .5; }

/* ---------- Нижние вкладки ---------- */
#tabs {
  display: flex; flex: none; border-top: 1px solid var(--line);
  /* Снизу телефон резервирует полосу под жест «домой» (на айфонах это 34px).
     Целиком её отдавать не за чем: сама чёрточка занимает пять пикселей, а
     остальное — воздух, из-за которого значки прижимались к верху полосы и под
     ними оставалась пустота. Берём половину: значки опускаются ниже, до
     чёрточки всё равно остаётся запас, и полоса не растёт.
     Сверху отступ больше нижнего ровно на эту же величину — так значки стоят
     по центру видимой части. */
  padding: calc(8px + max(env(safe-area-inset-bottom, 0px), var(--safe-bottom)) * .25)
           4px
           calc(8px + max(env(safe-area-inset-bottom, 0px), var(--safe-bottom)) * .5);
  background: var(--bg);
}

/* Полоса ужата до 50px: значок 20, подпись 14, отступы по три пикселя. Меньше
   уже нельзя — цель для пальца должна остаться не меньше сорока пикселей по
   высоте, а подпись читаемой. */
.tab { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 3px 0 0; color: var(--t3); }
.tab svg { width: 20px; height: 20px; }
/* Интерлиньяж прижат к размеру шрифта: при обычных 1.5 под подписью оставалось
   почти четыре пикселя пустоты, и полоса выглядела выше, чем есть. */
.tab span { font: 500 14px/1.1 var(--sans); white-space: nowrap; }
.tab.on { color: var(--action); }

/* ---------- Часы (встроенные) ---------- */
#clockframe { width: 100%; height: 100%; border: 0; display: block; }

/* ---------- Скелетон / загрузка ---------- */
.loader { display: flex; gap: 6px; justify-content: center; padding: 48px 0; }
.loader span { width: 8px; height: 8px; border-radius: 50%; background: var(--t3);
  animation: bounce 1.2s ease-in-out infinite; }
.loader span:nth-child(2) { animation-delay: .15s }
.loader span:nth-child(3) { animation-delay: .3s }
@keyframes bounce { 0%,80%,100% { opacity: .3; transform: translateY(0) }
                    40% { opacity: 1; transform: translateY(-5px) } }

.skel { height: 14px; border-radius: 7px; background: var(--row); }

/* ---------- Список городов ---------- */
.city-list { display: flex; flex-direction: column; gap: var(--gap); }
.city-item { display: flex; align-items: center; justify-content: space-between;
  background: var(--card); border-radius: 12px; padding: 14px 16px;
  font-size: 19px; width: 100%; text-align: left; }
.city-item.on { border: 1px solid var(--action); }

/* ---------- Тост ---------- */
/* Полоса уведомления между шапкой и содержимым. Не position: fixed —
   наложением она накрывала то главную кнопку, то текст карточки. */
#toast {
  flex: none;
  background: var(--action); color: var(--ink);
  padding: 12px 16px; font-size: 17px; font-weight: 500; text-align: center;
  animation: toast-in .18s ease;
}
@keyframes toast-in {
  from { opacity: 0; transform: translateY(-100%) }
  to   { opacity: 1; transform: translateY(0) }
}

.err { color: var(--danger); font-size: 18px; text-align: center; padding: 24px 0; }

/* ---------- Фото турнира ---------- */
.tour-photo {
  display: block; width: 100%; max-height: 220px; object-fit: cover;
  border-radius: var(--r-card); background: var(--card);
}
.photo-box { display: flex; flex-direction: column; gap: var(--gap); }
.photo-empty {
  display: flex; align-items: center; justify-content: center;
  height: 120px; border-radius: var(--r-card);
  background: var(--card); border: 1px dashed var(--line-2);
  color: var(--t3); font-size: 16px;
}

/* Строка пары, по которой организатор может проставить счёт. Подсказка тихая —
   стрелка справа: для игроков список остаётся просто списком. */
.pair--set { cursor: pointer; }
.pair--set:active { background: rgba(255, 255, 255, .06); }
.pair--set .pair-res::after { content: ' ›'; color: var(--t3); }

/* Окно счёта от организатора. Варианты — строки одного списка: значок в
   колонке фиксированной ширины, поэтому подписи начинаются на одной линии
   независимо от того, есть у варианта пешка или нет. */
.score-list { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.score-opt {
  display: flex; align-items: center; width: 100%; gap: 10px;
  padding: 13px 14px; border-radius: var(--r-btn); text-align: left;
  background: var(--row); border: 1px solid var(--line); color: var(--t1);
  font: 600 17px/1.25 var(--sans);
}
.score-opt:active { opacity: .85; }
.score-ico { flex: none; width: 16px; display: flex; justify-content: center; }
.score-ico .pawn { width: 16px; height: 16px; margin: 0; }
.score-ico .pawn--w { color: #fff; }
.score-ico .pawn--b { color: rgba(255, 255, 255, .45); }
.score-txt { flex: 1; min-width: 0; }
/* Откуда взялась отметка — строкой ПОД названием, а не сбоку: сбоку она
   отжимала имя и длинные имена ломались на две строки. */
.score-mark { display: block; margin-top: 3px;
  font: 400 14px/1.2 var(--sans); color: var(--t2); }

/* Уже проставленный результат подсвечен, и цвет говорит, кто его поставил:
   переписывать запись игроков — не то же самое, что вносить первую. */
.score-opt--on { border-width: 2px; padding: 12px 13px; }
/* У отмеченного варианта две строки — пешку держим у имени, а не по центру
   блока, иначе она оказывается напротив пояснения. */
.score-opt--on .score-ico { align-self: flex-start; margin-top: 3px; }
.score-opt--mine { border-color: var(--action); }
.score-opt--mine .score-mark { color: var(--action); }
.score-opt--players { border-color: var(--success-t); }
.score-opt--players .score-mark { color: var(--success-t); }
.score-opt--pending { border-color: rgba(255, 204, 0, .5); background: rgba(255, 204, 0, .08); }

/* ---------- Экран входа (DM10190) ----------
   Вкладок на нём нет, поэтому содержимое центрируем по высоте: прижатая к
   верху колонка на пустом экране выглядит забытой. */
.login {
  min-height: 100%; display: flex; flex-direction: column; justify-content: center;
  align-items: center; text-align: center; gap: 14px; padding: 24px 4px;
}
.login-logo {
  width: 84px; height: 84px; border-radius: 50%; background: var(--card);
  display: flex; align-items: center; justify-content: center; margin-bottom: 2px;
}
.login-logo img { width: 52px; height: 52px; object-fit: contain; }
.login-head { font: 600 24px/1.2 var(--sans); letter-spacing: -.01em; }
.login-text { font-size: 18px; line-height: 1.45; color: var(--t2); max-width: 320px; }

/* Кнопка в фирменном синем Telegram: вход идёт через него, и это должно быть
   видно раньше, чем прочитан текст. */
.btn-tg-wide {
  width: 100%; margin-top: 6px; gap: 10px;
  background: #2AABEE; color: #fff; border: 0;
}
.btn-tg-wide:active { opacity: .88; }

.login-wait { width: 100%; display: flex; flex-direction: column; gap: var(--gap); }
/* Код сверяют глазами с тем, что показал бот, — поэтому крупно и вразрядку. */
.login-code {
  font: 700 38px/1.15 var(--mono); letter-spacing: .18em; text-align: center;
  color: var(--action); margin: 16px 0 10px; padding-left: .18em;
}
.login-status { color: var(--t2); }

/* ---------- Знакомство (DM10270) ---------- */
.onb-step { font: 500 15px/1 var(--sans); color: var(--t3); letter-spacing: .04em;
  text-transform: uppercase; }
.onb-head { font: 600 24px/1.2 var(--sans); letter-spacing: -.01em; }
.onb-hint { font-size: 17px; line-height: 1.45; color: var(--t2); }
