/* Брони — из макета «Клуб - Брони». Числа без округлений: сверять рендер с
   макетом можно только когда они те же.

   Своим файлом, а не разделом в screens.css: экраны четвёртого этапа
   переносятся в несколько рук, и общий файл был бы одной точкой столкновения.

   ПРЕФИКС `bo`, А НЕ `bk`. `bk*` в screens.css уже занят — это шторка брони на
   странице вечера (`.bkSheet`, `.bkTicket`, `.bkQr`). Совпади имена, одна
   правка перекрасила бы другую. `bg` не взят нарочно: в CSS две эти буквы
   читаются как background, и `.bgRow` через месяц выглядит опечаткой.

   ОДИН КЕЙФРЕЙМ ЗДЕСЬ СВОЙ, И ЭТО НЕ ДУБЛИРОВАНИЕ.
     `boRowIn` — движение то же, что у `rowIn` в anim.css (translateY(14px)), и
       длительность из макета (.55s) стояла бы в правиле, а не в кейфрейме. Но
       `rowIn` кончается на ЖЁСТКОЙ единице прозрачности, а прошедшая строка
       обязана остаться на .72 (макет гасит её цветом). С `both` последний кадр
       анимации сильнее любого правила, и строка после проявки вспыхивала бы в
       полную силу. Поэтому конец здесь — `var(--o)`.
   `sweep` (7s) и `pulse` (точка у отсчёта) совпали и взяты из anim.css:
   различается только длительность точки (1.9s против 1.8s), а она стоит в
   правиле. `fadeIn` НЕ подошёл: он едет на 8px вверх, а подложке карточки кода
   в макете задана чистая прозрачность — отсюда `boFade`.

   ЧЕГО В МАКЕТЕ НЕТ, А ЗДЕСЬ ЕСТЬ, И ПОЧЕМУ (каждое помечено по месту):
   цвет двух кнопок в подвале пропуска, внутренние отступы в плашках и
   кнопках-таблетках, межстрочный интервал в двух подписях. Это места, где
   исходник числа не называл, а без них блок не собирается. */

/* ══ ВЕРХ ════════════════════════════════════════════════════════════════
   Шапка этого экрана НЕ `.csHead`: она едет вместе с содержимым внутри цветной
   подложки, а не липнет к верху. Так в макете — и правильно: липкой здесь
   назначена строка вкладок, а две липкие полосы одна под другой съели бы треть
   экрана телефона.

   Подложка и содержимое разведены по двум узлам нарочно: на широком экране
   содержимое встаёт колонкой по центру, а свечение обязано остаться во всю
   ширину. Тем же порядком разведены `.dnTop` / `.dnTopIn` у сыгранного вечера. */
.boTop { background: radial-gradient(120% 70% at 18% 0%, var(--glow), var(--bg2) 58%, var(--bg)); }
/* Сверху `--cs-top`, а НЕ 58px макета. Пятьдесят восемь в исходнике — это
   высота нарисованной в макете строки состояния телефона: часы, сеть,
   батарея. У нас её рисует сам телефон, а в браузере над страницей стоит его
   собственная панель, и перенесённое число оставляло полосу пустоты, которой
   нет больше нигде на сайте: соседние экраны начинали заголовок сразу, этот —
   на 58px ниже.

   Добавка на «чёлку» здесь СВОЯ, хотя токен общий: шапка этого экрана едет в
   потоке, и отступить под вырез ей нечем — на соседних экранах это делает за
   них фиксированная шапка. */
.boTopIn { padding: var(--cs-top) 20px 0; padding-top: calc(var(--cs-top) + env(safe-area-inset-top, 0px)); }

.boHead { display: flex; align-items: center; gap: 11px; }
.boTitle {
  margin: 0;
  font-family: var(--f-head); font-size: 20px; font-weight: 800;
  letter-spacing: -.6px; line-height: 1.5; color: var(--ink);
}
/* Отсчёт до ближайшей брони. Те же числа, что у «ближайший через» на
   «Турнирах» (`.tNext`): у дизайнера это буквально один элемент. Скопирован, а
   не переиспользован, — чужой экран не должен уметь перекрасить этот. */
.boNext { display: flex; align-items: center; gap: 5px; font-size: 11px; color: rgba(255, 255, 255, .5); }
.boNext .cs-live { width: 6px; height: 6px; animation-duration: 1.9s; }
.boHead > .cs-round { margin-left: auto; }

/* ── пропуск ─────────────────────────────────────────────────────────────── */
.boPass {
  position: relative;
  border-radius: 24px;
  overflow: hidden;
  background: linear-gradient(180deg, var(--tile), var(--tileHi));
  border: 1px solid var(--accSoft);
  box-shadow: 0 14px 32px rgba(0, 0, 0, .5);
  margin-top: 14px;
}
.boSweep {
  position: absolute; top: 0; bottom: 0; width: 60px;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .08), transparent);
  animation: sweep 7s ease-in-out infinite;
}

/* Перенос строки здесь работает: код и текст встают рядом, а «Показать QR»
   шириной во всю карточку сам уезжает на вторую строку. */
.boPassBody { position: relative; display: flex; flex-wrap: wrap; gap: 14px; padding: 16px; }

.boPassQr { flex: none; width: 104px; height: 104px; border-radius: 16px; background: #fff; padding: 8px; }
/* `qr()` пишет размер инлайном — перебить его можно только так. */
.boPassQr .fd-qr { width: 100% !important; height: 100% !important; }
.boPassQr svg { display: block; width: 100%; height: 100%; }

.boPassText { flex: 1; min-width: 132px; display: flex; flex-direction: column; justify-content: center; }

/* Плашка режима на пропуске. «В клубе» золотом, а не акцентом клуба: это не
   бренд зала, а то, что место за живым столом. Тем же цветом помечен вечер на
   итогах (`.dnWhere`) — числа совпадают до десятой доли. */
.boWhere {
  align-self: flex-start;
  display: flex; align-items: center; gap: 4px;
  font-size: 9.5px; font-weight: 800;
  letter-spacing: .9px; text-transform: uppercase;
  color: #F0B84A;
  background: rgba(240, 184, 74, .14);
  border: 1px solid rgba(240, 184, 74, .42);
  border-radius: 14px;
  padding: 4px 8px 4px 7px;
}
.boWhere i { font-size: 12px; }

.boPassName {
  margin-top: 9px;
  font-family: var(--f-head); font-size: 15px; font-weight: 800;
  letter-spacing: -.4px; line-height: 1.3; color: #fff;
}
.boPassWhen { margin-top: 6px; font-size: 11.5px; color: rgba(255, 255, 255, .55); }
.boPassCode { margin-top: 3px; font-size: 11px; color: rgba(255, 255, 255, .4); }

.boShow {
  width: 100%;
  display: flex; align-items: center; justify-content: center; gap: 7px;
  border-radius: 14px;
  background: var(--acc); color: var(--accInk);
  padding: 12px;
  font-size: 12.5px; font-weight: 800;
}
.boShow i { font-size: 16px; }

.boPassFoot { position: relative; display: flex; border-top: 1px dashed rgba(255, 255, 255, .16); }
/* Цвет первых двух кнопок макет не называет (назван только акцент у третьей).
   Взят тот же, что у второй кнопки в строке списка, — иначе одинаковые по роли
   действия на одном экране разошлись бы тоном. */
.boFootBtn {
  flex: 1;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  padding: 12px 0;
  font-size: 11.5px; font-weight: 750;
  color: rgba(255, 255, 255, .75);
}
/* Кромка у ВТОРОЙ и ТРЕТЬЕЙ. Соседским селектором, а не по номеру: «Отменить»
   рисуется не всегда (отмена бывает уже невозможна), и по номеру разделитель
   повис бы у крайней кнопки. */
.boFootBtn + .boFootBtn { border-left: 1px solid var(--line); }
.boFootBtn i { font-size: 15px; }
.boFootBtn.acc { color: var(--acc); }

/* ══ ЛИПКИЕ ВКЛАДКИ ══════════════════════════════════════════════════════ */
.boTabsWrap {
  position: sticky; top: 0; z-index: 20;
  padding: 14px 20px 10px;
  background: linear-gradient(180deg, var(--bg) 78%, transparent);
}
.boTabs {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 5px;
  background: rgba(255, 255, 255, .05);
  border: 1px solid var(--line);
  border-radius: 18px;
  padding: 4px;
}
.boTab {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  border-radius: 14px;
  padding: 10px 0;
  font-size: 12px; font-weight: 800;
  background: transparent; color: rgba(255, 255, 255, .62);
}
.boTab.on { background: #fff; color: #12100F; }
.boTabN { font-size: 10.5px; opacity: .6; }

/* ══ СПИСОК ══════════════════════════════════════════════════════════════
   Низ — `--nav-gap`, а не 120px макета: у нас под экраном живёт общая таблетка
   навигации на пять кнопок, и её высота объявлена токеном. */
.boList { padding: 6px 20px var(--nav-gap); display: flex; flex-direction: column; gap: 10px; }

.boRow {
  position: relative;
  display: flex;
  border-radius: 20px;
  overflow: hidden;
  background: var(--tile);
  border: 1px solid var(--line);
  box-shadow: 0 8px 20px rgba(0, 0, 0, .35);
}
.boBar { position: absolute; left: 0; top: 0; bottom: 0; width: 4px; }

/* Цвет состояния — кромкой строки. Пятёрка цветов макета здесь неполная:
   «ждёт подтверждения» и «лист ожидания» не заведены, потому что за ними нет
   состояний в модели (см. шапку src/screens/site/bookings.js). Правило,
   которое никогда не сработает, через месяц читается как поломка. */
.boRow.ok { border-color: rgba(99, 214, 159, .4); }
.boRow.visited { border-color: rgba(255, 255, 255, .14); }
.boRow.noshow { border-color: rgba(232, 83, 107, .35); }
/* Отменённая своего цвета в макете не получила — берёт тот же приглушённый,
   что и посещённая: вкладка называет её сама. */
.boRow.off { border-color: rgba(255, 255, 255, .14); }

/* Полоса слева красится состоянием — и своя она ровно у одного: всё прошедшее
   макет гасит одним приглушённым тоном. «Посещена», «не пришли» и «отменена»
   иначе как прошедшими не бывают, поэтому правил здесь два, а не пять: цвета
   остальных состояний живут в кромке строки и в плашке. */
.boRow.ok .boBar { background: #63D69F; }

/* Прошедшая строка тише: гаснет целиком, полоса слева обесцвечивается, время
   перестаёт быть белым. Прозрачность идёт через `--o`, а не прямо: её же
   держит последний кадр проявки (см. `boRowIn` внизу файла), и двумя разными
   правилами на одно свойство они бы спорили. */
.boRow { --o: 1; }
.boRow.past { --o: .72; }
.boRow.past .boBar { background: rgba(255, 255, 255, .14); }
.boRow.past .boTime { color: rgba(255, 255, 255, .45); }

.boDate {
  flex: none; width: 74px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  background: rgba(0, 0, 0, .28);
  padding: 14px 6px;
}
.boDow, .boMon {
  font-size: 9.5px; font-weight: 800;
  letter-spacing: 1.1px; text-transform: uppercase;
  color: rgba(255, 255, 255, .45);
}
.boDay {
  font-family: var(--f-head); font-size: 23px; font-weight: 800;
  letter-spacing: -1px; line-height: 1.2; color: var(--ink);
}
.boTime { margin-top: 6px; font-size: 12px; font-weight: 700; color: #fff; }

.boMain { flex: 1; min-width: 0; display: flex; flex-direction: column; padding: 13px 14px; }
.boChips { display: flex; align-items: center; gap: 7px; }

/* Плашка состояния стоит только у «посещена» и «не пришли» — так в макете. У
   предстоящей её работу делает полоса слева, а в 360px она отжимала бы режим
   на вторую строку. Отступ между значком и словом макет не называет — взят тот
   же, что в плашке режима. */
.boSt {
  display: flex; align-items: center; gap: 4px;
  font-size: 9.5px; font-weight: 700;
  letter-spacing: .9px; text-transform: uppercase;
  border: 1px solid transparent; border-radius: 13px;
  padding: 3px 8px;
}
.boSt i { font-size: 12px; }
.boSt.visited { color: rgba(255, 255, 255, .5); border-color: rgba(255, 255, 255, .14); }
.boSt.noshow { color: #E8536B; border-color: rgba(232, 83, 107, .35); }

/* Плашка режима в строке — та же, что на пропуске, но мельче: другие радиус,
   отступы и трекинг. Сводить их значило бы править макет. */
.boMode {
  display: flex; align-items: center; gap: 4px;
  font-size: 9.5px; font-weight: 800;
  letter-spacing: .8px; text-transform: uppercase;
  color: #F0B84A;
  background: rgba(240, 184, 74, .14);
  border: 1px solid rgba(240, 184, 74, .42);
  border-radius: 13px;
  padding: 3px 7px;
}
.boMode i { font-size: 11px; }

.boName { margin-top: 9px; font-size: 14.5px; font-weight: 800; color: #fff; }
.boSub { margin-top: 4px; font-size: 11px; color: rgba(255, 255, 255, .5); }

.boActs { display: flex; align-items: center; gap: 8px; margin-top: 11px; }
/* Кромка прозрачная, а не отсутствующая: у второй кнопки она есть, и без неё
   таблетки в одной строке разошлись бы на два пикселя ростом. Отступ между
   значком и словом макет не называет — взят общий для кнопок сайта. */
.boQr {
  display: flex; align-items: center; gap: 6px;
  border-radius: 13px;
  border: 1px solid transparent;
  background: var(--acc); color: var(--accInk);
  padding: 8px 11px;
  font-size: 11.5px; font-weight: 800;
}
.boQr.ghost { background: rgba(255, 255, 255, .06); color: rgba(255, 255, 255, .6); border-color: var(--line); }
.boAct {
  display: flex; align-items: center; gap: 6px;
  border-radius: 13px;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, .06);
  color: rgba(255, 255, 255, .75);
  padding: 8px 11px;
  font-size: 11.5px; font-weight: 750;
}
.boQr i, .boAct i { font-size: 15px; }
.boCode { margin-left: auto; font-size: 10px; color: rgba(255, 255, 255, .35); }

/* ── пусто ───────────────────────────────────────────────────────────────
   Межстрочный интервал у пояснения макет не называет; взят тот же 1.5, что у
   общего `.cs-empty`, — без него фраза в две строки слипается. */
.boEmpty {
  border-radius: 20px;
  background: var(--tile);
  border: 1px dashed var(--line);
  padding: 26px 18px;
  text-align: center;
}
.boEmpty > i { font-size: 26px; color: rgba(255, 255, 255, .35); }
.boEmptyT { margin-top: 10px; font-size: 14px; font-weight: 800; color: #fff; }
.boEmptyS { margin-top: 5px; font-size: 12px; font-weight: 650; line-height: 1.5; color: rgba(255, 255, 255, .5); }
.boEmptyBtn {
  margin-top: 14px;
  border-radius: 14px;
  background: var(--acc); color: var(--accInk);
  padding: 11px 18px;
  font-size: 12.5px; font-weight: 800;
}

/* Гость. В макете его нет — там человек уже вошёл; блок собран из общих
   кирпичей сайта (`.cs-empty`, `.cs-btn`), как у бонусов и статистики. */
.boGuest { margin-top: 14px; }
.boGuestBtn { margin: 14px 0 var(--nav-gap); }

/* ══ КАРТОЧКА КОДА ═══════════════════════════════════════════════════════
   НЕ шторка снизу: карточка по центру и белая.

   ЦЕНТРИРУЕТ ПОДЛОЖКА, а не `top:50%` + `translateY(-50%)` на самой карточке,
   как в макете. Общий слой смахивания (ui/shell.js) пишет `[data-sheet]`
   `transform` прямо в стиль — свой `translateY(-50%)` карточка под пальцем
   потеряла бы и прыгнула на пол-экрана. Геометрия та же, появление макетное. */
.boBack {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, .72);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  animation: boFade .3s ease both;
}
.boCard {
  width: calc(100% - 40px);
  border-radius: 28px;
  background: #fff;
  padding: 22px;
  box-shadow: 0 30px 70px rgba(0, 0, 0, .6);
  animation: boCardIn .34s cubic-bezier(.34, 1.4, .5, 1) both;
}
.boCardHead { display: flex; align-items: center; gap: 10px; }
.boCardText { flex: 1; min-width: 0; }
.boCardName {
  font-family: var(--f-head); font-size: 17px; font-weight: 800;
  letter-spacing: -.4px; line-height: 1.35; color: #12100F;
}
.boCardSub { margin-top: 4px; font-size: 11.5px; color: rgba(0, 0, 0, .55); }
.boCardX {
  flex: none;
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px;
  border-radius: 12px;
  background: rgba(0, 0, 0, .07); color: #12100F;
}
.boCardX i { font-size: 18px; }

/* Код НАСТОЯЩИЙ (`#shared/lib/qr.js`) — тот же, что читает сканер на входе.
   Квадрат держит `aspect-ratio`, а не подобранная высота: ширина карточки
   зависит от экрана. */
.boCardQr { width: 100%; aspect-ratio: 1; margin-top: 16px; }
.boCardQr .fd-qr { width: 100% !important; height: 100% !important; }
.boCardQr svg { display: block; width: 100%; height: 100%; }

.boCardCode { margin-top: 14px; text-align: center; font-size: 13px; font-weight: 700; letter-spacing: 2px; color: #12100F; }
.boCardNote { margin-top: 6px; text-align: center; font-size: 11.5px; font-weight: 650; line-height: 1.5; color: rgba(0, 0, 0, .5); }

/* ══ ПРОЯВКА ═════════════════════════════════════════════════════════════
   Состояния ДВА, и это не роскошь. `go` — анимация; её вешает первый рендер
   вкладки. `on` — тот же конец без движения; его получают все последующие,
   потому что снимок витрины приходит сам по себе несколько раз в минуту, и
   список, въезжающий заново от чужой брони в соседнем вечере, читается как
   поломка. Ставит классы сам экран, ПОСЛЕ записи разметки: в строке они меняли
   бы её от рендера к рендеру (screens/site/bookings.js). */
.boRw { opacity: 0; }
.boRw.on { opacity: var(--o, 1); }
.boRw.go { animation: boRowIn .55s var(--ease) var(--d, 0s) both; }

/* ── свои кейфреймы ──────────────────────────────────────────────────────── */

/* То же движение, что у `rowIn` в anim.css, но конец — `var(--o)`: прошедшая
   строка обязана остаться приглушённой, а `both` держит последний кадр
   сильнее любого правила. */
@keyframes boRowIn {
  0% { opacity: 0; transform: translateY(14px) }
  100% { opacity: var(--o, 1); transform: none }
}

/* Подложка карточки кода появляется ЧИСТОЙ прозрачностью. `fadeIn` из anim.css
   не подошёл: он едет на 8px вверх, и подложка во весь экран от этого
   дёргается. */
@keyframes boFade { 0% { opacity: 0 } 100% { opacity: 1 } }
@keyframes boCardIn { 0% { transform: scale(.92) } 100% { transform: none } }

/* ══ ШИРОКИЙ ЭКРАН ═══════════════════════════════════════════════════════
   Колонка по центру — то же единственное правило, что в desktop.css; `--cs-col`
   объявлен там же и в том же медиа-запросе. Держим здесь, чтобы не трогать
   общий файл ради одного экрана.

   Карточка кода в колонку 620px не встаёт: код у неё квадратный, и на всю
   ширину это был бы QR ростом с пол-окна. 350px — ширина той же карточки на
   телефоне макета (390 − 20 − 20), то есть её собственный размер. */
@media (min-width: 768px) {
  .boTopIn, .boTabs, .boList {
    max-width: var(--cs-col);
    margin-left: auto;
    margin-right: auto;
  }
  .boCard { max-width: 350px; }
}

/* ══ МЕНЬШЕ ДВИЖЕНИЯ ═════════════════════════════════════════════════════
   Блок из макета. Он здесь, а не в общем anim.css, потому что у сайта клуба
   такого правила пока нет вовсе — каталожный base.css на клубном имени не
   подключается. Завести его на весь сайт стоит отдельной строкой, и это не
   работа этого экрана. */
@media (prefers-reduced-motion: reduce) {
  .boTopIn *, .boList *, .boBack, .boBack * {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}
