/* Атрибут hidden всегда скрывает элемент, даже если у него задан display (flex и т.п.) */
[hidden] { display: none !important; }

/* Фирменный шрифт продукта — Golos Text. Лежит локально в /fonts: без обращения к
   Google Fonts, чтобы интерфейс открывался одинаково и из Беларуси, и из-за границы. */
@font-face{font-family:'Golos Text';font-style:normal;font-display:swap;font-weight:400;src:url(/fonts/golos-text-cyrillic-400-normal.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:'Golos Text';font-style:normal;font-display:swap;font-weight:400;src:url(/fonts/golos-text-latin-400-normal.woff2) format('woff2');unicode-range:U+0000-00FF,U+2000-206F,U+20AC,U+2122}
@font-face{font-family:'Golos Text';font-style:normal;font-display:swap;font-weight:500;src:url(/fonts/golos-text-cyrillic-500-normal.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:'Golos Text';font-style:normal;font-display:swap;font-weight:500;src:url(/fonts/golos-text-latin-500-normal.woff2) format('woff2');unicode-range:U+0000-00FF,U+2000-206F,U+20AC,U+2122}
@font-face{font-family:'Golos Text';font-style:normal;font-display:swap;font-weight:600;src:url(/fonts/golos-text-cyrillic-600-normal.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:'Golos Text';font-style:normal;font-display:swap;font-weight:600;src:url(/fonts/golos-text-latin-600-normal.woff2) format('woff2');unicode-range:U+0000-00FF,U+2000-206F,U+20AC,U+2122}
@font-face{font-family:'Golos Text';font-style:normal;font-display:swap;font-weight:700;src:url(/fonts/golos-text-cyrillic-700-normal.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:'Golos Text';font-style:normal;font-display:swap;font-weight:700;src:url(/fonts/golos-text-latin-700-normal.woff2) format('woff2');unicode-range:U+0000-00FF,U+2000-206F,U+20AC,U+2122}

/* ⚠ ПАЛИТРА — брендбук продукта, версия 1.0, направление «Хвоя» (утверждено 10.09.2026).
   Менять цвета ТОЛЬКО здесь. Смысл каждого:
     --brand        заливка главного действия и активного пункта, цвет ссылок;
     --brand-light  светлая подложка под тем же смыслом («идёт как надо»);
     --warn         требует решения (не оплачено, расхождение, просрочка);
     --danger       ошибка, отмена, необратимое действие.
   Зелёным НЕЛЬЗЯ красить предупреждения, ошибки и декоративные элементы.
   Старые имена (--yellow, --teal, --black, --grey, --red, --green, --border) оставлены
   псевдонимами: на них завязано больше сотни правил, переписывать их разом незачем. */
:root {
  --brand:       #2F6B4F;
  --brand-dark:  #245640;
  --brand-light: #E4EDE6;
  --ink:         #1F211D;
  --sand:        #F5F2EB;
  --surface:     #FFFDF9;
  --row-alt:     #FAF7F0;
  --muted:       #63665D;
  --line:        #E3DED2;
  --ok:          #2F6B4F;
  --warn:        #8A5B10;
  --warn-bg:     #F6EBD8;
  --danger:      #A8412F;
  --danger-bg:   #F7E6E2;
  /* Цвета типов записи — это НЕ статусы: они живут полоской слева, а не плашкой. */
  --cat-dr:      #8C6A3F;
  --cat-unl:     #7A5A8C;

  --yellow: var(--brand);
  --black: var(--ink);
  --teal: var(--brand);
  --teal-light: var(--brand-light);
  --red: var(--danger);
  --green: var(--ok);
  --grey: var(--sand);
  --border: var(--line);

  /* ⚠ Одна сетка на шапку колонок и на строки. Раньше .col-head и .bk были описаны разными
     наборами колонок и со временем разъехались (найдено 27.07): заголовки «СУММА» и «ДЕЙСТВИЯ»
     стояли не над своими столбцами. Менять ширины — ТОЛЬКО здесь. */
  --day-cols: 40px minmax(0,1.5fr) minmax(0,1fr) 56px 80px minmax(0,1.15fr) 132px 236px;
  --day-cols-narrow: 40px minmax(0,1.5fr) minmax(0,1fr) 56px 80px minmax(0,1.15fr) 124px;
}
* { box-sizing: border-box; }
[hidden] { display: none !important; } /* hidden сильнее любых display-правил */
body {
  margin: 0; font-family: 'Golos Text', system-ui, sans-serif;
  background: var(--grey); color: var(--black);
}
/* Логотип ПЛОЩАДКИ (арендатора), а не программы: смена и гость должны видеть имя своего
   центра. Знак самого продукта живёт только на экране входа и в мастере настройки. */
.logo { font-weight: 700; font-size: 22px; letter-spacing: -0.022em; }
.logo span { color: var(--brand); }
.logo em { font-style: normal; color: var(--muted); font-weight: 500; font-size: 14px; margin-left: 6px; }
.logo.sm { font-size: 18px; }

/* Вход */
.login-wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: var(--black); }
.login-card { background: var(--surface); border-radius: 20px; padding: 32px; width: 340px; display: flex; flex-direction: column; gap: 14px; }
.login-card label { display: flex; flex-direction: column; gap: 4px; font-weight: 700; font-size: 13px; }

/* Шапка */
.topbar {
  display: flex; align-items: center; gap: 6px; row-gap: 6px; padding: 8px 14px;
  background: var(--surface); color: var(--ink); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 5;
  flex-wrap: wrap; /* при узком окне/крупном масштабе кнопки переносятся, ничего не обрезается */
}
/* 27.07 (решение владельца): все разделы остаются на виду, ничего не прячем под «Ещё» —
   вместо этого делаем кнопки шапки компактнее. На 1440 это укладывает их в два ряда вместо
   трёх и возвращает экрану ~60 px. Кнопки основного экрана (в окнах) остаются прежними. */
.topbar .btn { padding: 6px 10px; font-size: 13px; border-radius: 12px; }
.topbar .btn-primary { padding: 7px 14px; }   /* «+ Запись» — главное действие, чуть крупнее */
/* Разделители между смысловыми группами: деньги / товар / справочники — без пряток и меню */
.topbar .grp { width: 1px; align-self: stretch; background: var(--line); margin: 2px 4px; }
@media (max-width: 1100px) { .topbar .grp { display: none; } }
.topbar select { max-width: 240px; }
.topbar #sel-arena { text-overflow: ellipsis; }
.whoami span { white-space: nowrap; max-width: 160px; overflow: hidden; text-overflow: ellipsis; }
.topbar .logo span { color: var(--brand); }
.topbar select, .topbar input[type="date"] { padding: 7px 10px; border-radius: 12px;
  border: 1px solid var(--line); background: var(--surface); font-family: inherit; font-weight: 600; }
.spacer { flex: 1; }
.whoami { display: flex; align-items: center; gap: 6px; font-weight: 700; margin-left: 10px;
  padding-left: 10px; border-left: 1px solid var(--line); }

/* Кнопки и поля */
.btn { border: none; border-radius: 14px; padding: 9px 16px; font-family: inherit; font-weight: 700; font-size: 14px; cursor: pointer; }
.btn-primary { background: var(--brand); color: var(--surface); }
.btn-primary:hover { background: var(--brand-dark); }
.btn-ghost { background: transparent; color: inherit; border: 1px solid var(--border); }
.btn-danger { background: var(--red); color: var(--surface); }
.btn-danger:hover { filter: brightness(1.05); }
.btn-danger-ghost { color: var(--red); border-color: var(--red); }
.topbar .btn-ghost { color: var(--ink); border-color: var(--line); background: var(--surface); }
.topbar .btn-ghost:hover { border-color: #C9C2B0; }
input, select { padding: 8px 10px; border: 1px solid var(--border); border-radius: 12px; font-family: inherit; font-size: 14px; }
/* Фокус — фирменный. Без этого Chrome рисует свою оранжевую рамку, а в мягкой палитре
   оранжевый читается как предупреждение: человек ищет ошибку там, где её нет. */
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px rgba(47,107,79,.15);
}
.btn:focus-visible, button:focus-visible, a:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 2px;
}
.err { color: var(--red); font-weight: 700; font-size: 13px; min-height: 18px; }
/* Тот же класс на поле ввода — красная рамка. ⚠ 28.08: без неё отказ сервера был только
   текстом внизу окна, и человек не понимал, КАКОЕ поле от него хотят. */
input.err, select.err, textarea.err {
  border-color: var(--red); box-shadow: 0 0 0 3px rgba(168,65,47,.15);
}

/* Логотип из брендбука */
.logo-img { height: 40px; align-self: center; }
.logo-img.sm { height: 26px; }
.login-card .logo-img { height: 48px; max-width: 100%; object-fit: contain; margin-bottom: 6px; } /* 19.07: логотип вылазил за карточку входа */

/* Сетка дня */
main { padding: 16px; max-width: 1200px; margin: 0 auto; }
.day-grid { display: flex; flex-direction: column; gap: 10px; }
.hour-block { background: var(--surface); border-radius: 18px; overflow: hidden; border: 1px solid var(--border); }
.hour-head {
  display: flex; align-items: center; gap: 12px; padding: 8px 14px;
  background: var(--brand-light); color: var(--ink); font-weight: 600;
}
.hour-head .load { margin-left: auto; font-size: 13px; font-weight: 500; color: var(--muted); }
.hour-head .load.warn { color: var(--warn); }
.hour-head .load.full { color: var(--danger); }
.hour-body { display: flex; flex-direction: column; }
.bk {
  display: grid;
  grid-template-columns: var(--day-cols);
  gap: 8px;
  padding: 8px 14px; border-top: 1px solid var(--line); align-items: center; font-size: 14px;
}
.bk > div { min-width: 0; } /* колонки могут сжиматься — контент не распирает строку */
.bk .phone { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 27.07 (замечание аудита): метка комнаты обрезалась вместе с именем («Большая ко…»).
   Сжимается только имя гостя, метка и иконки остаются целыми. */
/* ⚠ .name — грид-ячейка: без overflow:hidden её содержимое вылезает на соседнюю колонку
   (27.07: длинная подпись «безлимит · с 12:00 до 15:00» наезжала на телефон). Сжимаются
   имя и подпись, метка комнаты остаётся целой. */
.bk .name { display: flex; align-items: center; gap: 4px; min-width: 0; overflow: hidden; white-space: nowrap; }
.bk .name .nm-text { overflow: hidden; text-overflow: ellipsis; min-width: 0; flex: 0 1 auto; }
.bk .name .cont-tag { overflow: hidden; text-overflow: ellipsis; min-width: 0; flex: 0 1 auto; }
/* В строке-продолжении полезнее подпись «с 12:00 до 15:00», чем полное имя — сжимаем имя. */
.bk.cont .name .nm-text { flex: 0 1 auto; max-width: 62%; }
.bk.cont .name .cont-tag { flex: 1 1 auto; }
.bk .name .room-tag { flex: none; }
/* 26.07: акция раньше обрезалась многоточием («… · Им...»). Теперь билет и акция — двумя
   строками: название билета сокращается, а акция видна целиком отдельной плашкой. */
.bk .tkt { overflow: hidden; }
.bk .tkt-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bk .tkt-disc { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 2px; }
/* Плашка акции лежит и на белых, и на бирюзовых строках (продолжение брони, ДР), поэтому
   фон у неё БЕЛЫЙ с рамкой, а не светло-бирюзовый — иначе на бирюзовом она сливалась. */
.disc-tag { background: var(--surface); color: var(--teal); border: 1px solid #CFE0D4; border-radius: 8px;
  padding: 0 6px; font-size: 11px; font-weight: 700; line-height: 1.6; }
.disc-tag.promo { background: #F6EBD8; border-color: #C9A24A; color: #6B4A11; }
/* Сумма: методы оплаты («нал 46,00 + карта 25») переносятся, а не обрезаются */
.bk .sum { overflow: hidden; }
.bk .sum > :first-child, .bk .sum { white-space: normal; }
.rowact { display: flex; gap: 4px; justify-content: flex-end; flex-wrap: wrap; }
.rowact .btn-mini { font-size: 11px; padding: 4px 8px; white-space: nowrap; }
@media (max-width: 1150px) {
  .bk { grid-template-columns: var(--day-cols-narrow); }
  .rowact { grid-column: 1 / -1; justify-content: flex-end; padding-top: 4px; }
}
/* Строка-продолжение идёт по ТОЙ ЖЕ сетке, что и основная: своя сетка ломала выравнивание
   телефона и статуса относительно колонок дня (27.07). Подпись сокращена вместо этого. */
.bk.cont { background: #FAF7F0; color: #93907F; font-size: 13px; }
.bk.cont .name { font-weight: 400; }
.cont-tag { color: #A5A294; font-size: 11px; }
.bk.flash { animation: flash 0.5s ease-in-out 2; }
@keyframes flash { 0%,100% { background: inherit; } 50% { background: var(--brand-light); } }
.dr-mode { display: flex; gap: 18px; padding: 4px 0; }
.radio { display: flex; align-items: center; gap: 6px; font-weight: 700; font-size: 13px; }
.dr-info { font-weight: 700; color: var(--teal); }
.e-info { display: flex; flex-direction: column; gap: 4px; font-size: 14px; background: var(--teal-light); border-radius: 14px; padding: 10px 14px; }
.e-log { max-height: 30vh; overflow: auto; font-size: 12px; display: flex; flex-direction: column; gap: 3px; }
.e-log-row { padding: 4px 8px; background: var(--grey); border-radius: 8px; }
.h-row.inactive { opacity: 0.5; }
/* 02.09: уволенные в списке сотрудников приглушаются ЦВЕТОМ, а не прозрачностью — иначе
   кнопка «📄 расчёт» выглядела неактивной, хотя работала. Кнопки остаются обычными. */
.h-row.st-inactive { color: #A5A294; }
.h-row.st-inactive .btn { color: inherit; }
.h-row.st-inactive .btn[data-sdism] { color: var(--ink); font-weight: 700; }
.bar-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; max-height: 38vh; overflow: auto; }
/* 18.07: категорий стало 6 (…Прокат) — строка категорий переносится, а не обрезается */
#bar-cats { flex-wrap: wrap; gap: 8px 18px; }
/* 18.07 №2: кнопки владельца (🌐/✏) были ОТДЕЛЬНЫМИ ячейками сетки и ломали колонки —
   теперь товар и его админ-строка живут в одной ячейке (.bar-cell) */
.bar-cell { display: flex; flex-direction: column; gap: 2px; }
.bar-cell .bar-item { width: 100%; }
.bar-admin { display: flex; align-items: center; gap: 6px; font-size: 10px; color: #93907F; font-weight: 700; }
.bar-admin input { width: 14px; height: 14px; }
.bar-admin .btn-mini { font-size: 10px; padding: 2px 6px; }
#bar-staff-sel { max-width: 160px; }
/* PIN-поля: маскируем точками сами — Chrome больше не считает их паролем и не предлагает сохранить */
.pinmask { -webkit-text-security: disc; }
/* Чем оплачено — тем же шрифтом, что штампы «подтв./пришёл/завершён» */
.pay-methods { color: #93907F; font-size: 11px; font-weight: 400; white-space: normal; line-height: 1.3; }
/* Шапка колонок: «ВРЕМЯ»/«ЗАПИСАНО» не влезали — мельче и плотнее */
.col-head { font-size: 10px !important; letter-spacing: .2px !important; }
/* Форма «Добавить сотрудника»: поля переносятся, а не сжимаются в кашу */
#st-form .row { flex-wrap: wrap; }
#st-form .row > label { min-width: 170px; }
/* Допродажа в окне оплаты (18.07): чипы размеров/товаров внутри строк-вопросов */
.up-chips { display: inline-flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 2px; }
.up-chip { padding: 7px 12px; border: 2px solid var(--border); border-radius: 14px; background: var(--surface);
  font-family: inherit; font-weight: 700; font-size: 13px; cursor: pointer; }
.up-chip:hover { border-color: var(--brand); background: #EFF4F0; }
.up-chip.on { border-color: var(--brand-dark); background: var(--brand); color: var(--surface); }
.up-chip.minus { padding: 7px 9px; color: #A8412F; }
.bar-item { padding: 10px 6px; border: 2px solid var(--border); border-radius: 14px; background: var(--surface);
  font-family: inherit; font-weight: 700; font-size: 12px; cursor: pointer; }
.bar-item:hover { border-color: var(--brand); background: #EFF4F0; }
.bar-item small { display: block; color: var(--teal); font-weight: 700; }

/* Касса */
.sum.paid { color: var(--green); }
.sum.partial { color: #8A5B10; }
/* 24.08 (замечание контроля качества): по записи оформлен возврат — это не долг и не оплата,
   а закрытый расчёт. Оранжевый здесь читался как «иди и добери деньги». */
.sum.refunded { color: #5A6E8C; }
.denom-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px 12px; }
.denom { display: flex; align-items: center; gap: 6px; font-weight: 700; font-size: 13px; }
.denom span { min-width: 38px; text-align: right; }
.denom input { width: 64px; padding: 4px 6px; }
.neg { color: var(--red); }
.pos { color: var(--green); }

/* Волна 3: типы записей и комнаты — цветовая навигация */
.form label.svc { flex-direction: row; align-items: center; justify-content: space-between; }
/* ⚠ .form label задаёт колонку и перебивает .radio (специфичность выше) — из-за этого галочки
   в окнах рисовались «квадратик сверху, подпись снизу» вместо строки (найдено 27.07). */
.form label.radio { flex-direction: row; align-items: center; gap: 6px; min-width: 0; flex: 0 1 auto; }
.form label.radio input { flex: none; }
.dr-mode label.radio { flex-direction: row; align-items: center; }
.bk.dr { border-left: 5px solid var(--cat-dr); }          /* цвет комнаты подставляется инлайн */
.bk.unl { border-left: 5px solid var(--cat-unl); }               /* безлимит — фиолетовый */
.bk.pt { border-left: 5px solid var(--teal); }            /* тренировка — бирюзовый */
.bk.rent { border-left: 5px solid var(--black); background: repeating-linear-gradient(45deg, #F6EBD8, #F6EBD8 14px, #FDFBF5 14px, #FDFBF5 28px); font-weight: 700; }
.bk.conflict { background: #F7E6E2; box-shadow: inset 0 0 0 2px var(--red); }
.room-tag { max-width: none;  padding: 1px 8px; border-radius: 12px; font-size: 12px; font-weight: 700; color: var(--surface); text-shadow: 0 1px 2px rgba(0,0,0,.25); }
.e-photos { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.e-photos img { height: 64px; border-radius: 12px; cursor: zoom-in; border: 1px solid var(--border); }

/* Шапка колонок таблицы дня */
.col-head {
  display: grid;
  grid-template-columns: var(--day-cols);
  gap: 8px; padding: 6px 14px; margin-bottom: 4px;
  font-size: 11px; font-weight: 700; color: #93907F; text-transform: uppercase; letter-spacing: .4px;
}
/* «ЗАПИСАНО» и «СТАТУС» слипались на узких экранах (аудит 14.07) — не даём тексту вылезать за колонку */
.col-head div { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.col-head div:nth-child(7) { text-align: right; }
.col-head div:last-child { text-align: right; }
@media (max-width: 1150px) {
  .col-head { grid-template-columns: var(--day-cols-narrow); }
  .col-head div:last-child { display: none; }
}
/* Конфликт с арендой — красный фон побеждает зебру */
.bk.conflict, .bk.conflict:nth-child(even) { background: #F7E6E2 !important; box-shadow: inset 0 0 0 2px var(--red); }
.bk.cont.rent { background: repeating-linear-gradient(45deg, #F6EBD8, #F6EBD8 14px, #FDFBF5 14px, #FDFBF5 28px) !important; color: #4A4D45; }
.bk:nth-child(even) { background: var(--row-alt); }
.bk .name { font-weight: 700; }
.bk .phone { color: #63665D; }
.bk .sum { font-weight: 700; text-align: right; }
.bk .badge { font-size: 12px; padding: 2px 8px; border-radius: 20px; font-weight: 700; text-align: center; }
.badge.new { background: transparent; color: var(--muted); box-shadow: inset 0 0 0 1px var(--line); }
.badge.confirmed { background: #E3E8EF; }
.badge.arrived { background: #E4EDE6; color: var(--green); }
.badge.done { background: #E3DED2; color: #93907F; }
.badge.no_show { background: #F7E6E2; color: var(--red); }
/* 23.08 (аудит): у «отменена» не было цвета вообще — единственный статус без заливки,
   терялся среди цветных. «Не пришёл» и «отменена» — разные вещи, красный тут не подходит. */
.badge.cancelled { background: #E3DED2; color: #93907F; text-decoration: line-through; }
.bk .actions { text-align: right; }
.bk .actions button { font-size: 12px; padding: 4px 10px; }
.bk.dr { border-left: 4px solid var(--cat-dr); }
.empty-hour { padding: 8px 14px; color: #A5A294; font-size: 13px; }

/* Модалки */
dialog { border: none; border-radius: 20px; padding: 0; width: min(680px, 94vw); }
dialog::backdrop { background: rgba(0,0,0,.55); }
/* Фото сотрудника в списке: маленькое кружком, по клику — крупное. */
.st-ph { width: 34px; height: 34px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; cursor: pointer; background: #E3DED2; }
.st-ph-no { display: inline-flex; align-items: center; justify-content: center; font-size: 18px; cursor: default; opacity: .55; }
.st-gap { display: inline-block; margin-left: 6px; font-size: 12px; color: #8A5B10; background: #F6EBD8; border-radius: 12px; padding: 1px 7px; }

/* Прайс: сетка «название + колонки цен». Раньше поля стояли сразу за названием, поэтому
   съезжали по горизонтали (сравнить цены по вертикали было нельзя), длинные названия наезжали
   на первое поле, а какая колонка что означает — было видно только по всплывающей подсказке. */
.pr-row { display: flex; align-items: center; gap: 10px; padding: 5px 8px; border-bottom: 1px solid #E3DED2; }
.pr-row .pr-name { flex: 1 1 auto; min-width: 0; }
.pr-row .pr-cells { flex: 0 0 auto; display: flex; gap: 8px; }
.pr-row .pr-cells > * { width: 92px; box-sizing: border-box; text-align: center; }
.pr-head { border-bottom: 0; padding-bottom: 0; }
.pr-head .pr-cells > span { font-size: 12px; font-weight: 600; color: #63665D; text-transform: uppercase; letter-spacing: .02em; }
@media (max-width: 700px) {
  /* На телефоне название и цены не помещаются в одну строку — цены уходят под название.
     Шапка при этом остаётся над своими колонками: у неё название пустое. */
  .pr-row { flex-wrap: wrap; gap: 4px 8px; }
  .pr-row .pr-name { flex: 1 0 100%; font-size: 14px; }
  /* min-width:0 обязателен: у flex-элемента по умолчанию auto, и полоса полей отказывалась
     сжиматься — третья колонка уезжала за правый край экрана. */
  .pr-row .pr-cells { flex: 1 1 100%; min-width: 0; }
  .pr-row .pr-cells > * { width: auto; flex: 1 1 0; min-width: 0; }
  .pr-head .pr-name { display: none; }
  .pr-head .pr-cells > span { font-size: 11px; }
}

/* Отчёт по акциям — семь столбцов, в 680px числа переносятся на две строки.
   ⚠ Только для широкого экрана: правило по id сильнее мобильного `dialog { width: 100vw }`
   в конце файла и молча вылезло бы за экран телефона. */
@media (min-width: 901px) { #dlg-zonefix, #dlg-freehour { width: min(1040px, 96vw); } }
/* ⚠ 28.08, третий заход по жалобе владельца «отзывы всё ещё криво». Причина была не в сетке
   строки, а в ШИРИНЕ окна: отзывы жили в стандартных 680px, из которых половину съедали
   оценка, дата и гость. Тексту оставалось ~250px — и «Очень приветливый коллектив» лез
   в три строки, а строка отзыва вырастала в блок на пол-экрана. */
/* #dlg-reviews теперь в полноэкранной раме (.dlg-full) — своя ширина не нужна */
/* Широкая таблица на телефоне прокручивается вбок сама, а не растягивает окно. */
.tblwrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.tblwrap .bd-cats { min-width: 560px; }
/* На телефоне таблица акций режется до главного: название, применения, гости, выручка, скидка.
   «Тип» повторяется блоком «По типам», «Чек/гость» считается из выручки и гостей — оба
   уводили самое нужное число (скидку) за правый край экрана. */
@media (max-width: 700px) {
  .ak-t-disc th:nth-child(2), .ak-t-disc td:nth-child(2),
  .ak-t-disc th:nth-child(7), .ak-t-disc td:nth-child(7) { display: none; }
  /* В списке непогашенных на телефоне прячем арену: в списке обычно своя же смена. */
  .ak-t-nored th:nth-child(2), .ak-t-nored td:nth-child(2) { display: none; }
  .tblwrap .bd-cats { min-width: 0; font-size: 13px; }
  /* «, BYN» в шапке — на телефоне лишние 60px; валюта названа в сводке выше. */
  .tblwrap .cur { display: none; }
  /* В хвостах на телефоне арена не нужна: смена всё равно смотрит свою. ⚠ 16.08: правило
     было привязано к номеру колонки и работало только в первой таблице. В «недоплачено»
     арена оставалась, занимала три строки и выдавливала колонку «Долг» за правый край —
     самое нужное число было не видно. Теперь колонка помечена классом во всех таблицах. */
  .ls-arena { display: none; }
  /* Подпись к дате — своей строкой: рядом с полем она не влезала и рвалась пополам,
     обе половинки с жёлтым фоном. */
  #dlg-loose .row .hint { display: block; margin-top: 6px; }
  .tblwrap .bd-cats th, .tblwrap .bd-cats td { padding-left: 4px; padding-right: 4px; }
}
.form { padding: 24px; display: flex; flex-direction: column; gap: 12px; }
.form h3 { margin: 0 0 6px; }
.form .row { display: flex; gap: 10px; flex-wrap: wrap; }
.form label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; font-weight: 700; flex: 1; min-width: 130px; }
.quote { background: var(--teal-light); border-radius: 14px; padding: 10px 14px; font-weight: 700; font-size: 16px; }
.actions { display: flex; justify-content: flex-end; gap: 10px; }

/* Правки 07.07 */
.day-badge { font-weight: 600; font-size: 14px; color: var(--ink); padding: 4px 10px; border-radius: 20px; background: #EDE9DF; }
.day-badge.holiday { background: var(--brand-light); color: var(--brand-dark); }
.hint { background: #F6EBD8; border-radius: 12px; padding: 8px 12px; font-size: 13px; font-weight: 700; }
.attn { margin-right: 4px; }
.subrow { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding-top: 4px; font-size: 12px; }
.subrow:empty { display: none; }
.stamps { color: #93907F; font-size: 11px; }
.btn-mini { font-size: 12px; padding: 4px 10px; }
.btn-mini.need, .pcode.need { border: 2px solid var(--red); }
.pcode { width: 130px; padding: 4px 8px; font-size: 12px; }
/* На телефоне поле ввода мельче 16px заставляет iOS зумить страницу при фокусе и не
   возвращать обратно (грабля 7 в CLAUDE.md). Ширину тоже отпускаем: 130px мало под код. */
@media (max-width: 760px) { .pcode { font-size: 16px; width: 100%; } }
.entered { display: flex; align-items: center; gap: 4px; font-weight: 700; }
.plink { color: var(--teal); font-weight: 700; }
.svc-title { font-weight: 700; font-size: 13px; margin-top: 4px; }
.svc-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 14px; }
.svc { display: flex; justify-content: space-between; align-items: center; gap: 8px; font-size: 13px; }
.svc input { width: 60px; padding: 4px 6px; }
/* 23.08 (владелец): что уже входит в выбранный пакет — видно прямо в списке услуг,
   чтобы тренера не продавали вторым разом. Строка подсвечена, рядом «уже в пакете: N». */
.svc-included { background: #F6EBD8; border-radius: 8px; padding: 2px 6px; margin: 0 -6px; }
.svc-inc { display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 999px;
  background: var(--ink); color: var(--surface); font-size: 11px; font-weight: 700; white-space: nowrap; }
.h-list { max-height: 50vh; overflow: auto; display: flex; flex-direction: column; gap: 4px; }
.h-row { padding: 8px 10px; border: 1px solid var(--border); border-radius: 12px; font-size: 13px; cursor: pointer; }
.h-row:hover { background: var(--teal-light); }
.h-when { display: block; color: #93907F; font-size: 11px; }
.bk.pt { border-left: 4px solid var(--teal); }

/* 19.07: бегущий маскот — индикатор загрузки (правка владельца п.4/18) */
#hp-loader { position: fixed; right: 14px; bottom: 14px; z-index: 2000; background: var(--ink); color: var(--surface);
  font-weight: 700; font-size: 13px; padding: 8px 14px; border-radius: 20px; display: none;
  box-shadow: 0 2px 10px rgba(0,0,0,.35); animation: hpLoaderBounce .7s ease-in-out infinite; pointer-events: none; }
@keyframes hpLoaderBounce { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
/* 19.07: вёрстка сертификатов/абонементов (п.22) — поля переносятся, диалог шире */
#dlg-certs { width: min(980px, 96vw); }
#ct-new-cert .row, #ct-new-sub .row { flex-wrap: wrap; }
#ct-new-cert .row > label, #ct-new-sub .row > label { min-width: 180px; flex: 1 1 180px; }
#ct-list .h-row { line-height: 1.5; }

/* Выбор человека нажатием (кто вёл праздник) — вместо ввода номера в системном окне */
.pick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 8px; margin: 6px 0; }
.pick-grid .pick {
  display: flex; align-items: center; gap: 10px; padding: 10px 12px; cursor: pointer;
  background: var(--surface); border: 2px solid var(--line); border-radius: 12px;
  font-family: inherit; font-weight: 700; font-size: 14px; text-align: left;
}
.pick-grid .pick:hover { border-color: var(--teal); background: #EFF4F0; }
.pick-grid .pick img, .pick-grid .pick .noph {
  width: 40px; height: 40px; border-radius: 14px; object-fit: cover; flex: none;
  background: #E3DED2; display: flex; align-items: center; justify-content: center; font-size: 18px;
}

/* Увольнение: расчёт и проверки. Блок читают перед необратимым действием, поэтому
   ключевые числа крупные, а пояснения — мелкие и серые. */
.fire-calc { background: var(--teal-light); border: 1px solid #CFE0D4; border-radius: 12px; padding: 10px 12px; margin: 10px 0; }
.fire-calc .fc-row { display: flex; justify-content: space-between; gap: 10px; padding: 3px 0; font-size: 14px; }
.fire-calc .fc-row b { white-space: nowrap; margin-left: auto; }   /* при переносе сумма всё равно справа */
.fire-calc .fc-total { border-top: 2px solid var(--teal); margin-top: 6px; padding-top: 7px; font-size: 16px; }
.fire-calc .fc-note { font-size: 12px; color: #63665D; margin-top: 6px; line-height: 1.4; }
.fire-check { border-radius: 14px; padding: 8px 10px; margin: 6px 0; font-size: 13px; line-height: 1.4; }
.fire-check.warn { background: #F6EBD8; border: 1px solid #E0C48A; color: #6B4A11; }
.fire-check.block { background: #F7E6E2; border: 1px solid var(--red); color: #7E3324; }
.fire-check .fc-fix { display: block; opacity: .8; margin-top: 2px; }
.fire-list { border: 1px solid var(--border); border-radius: 12px; padding: 8px 12px; margin: 4px 0 2px; font-size: 14px; }
.fire-list b { display: block; margin-bottom: 4px; font-size: 13px; }
.fire-list .radio { display: flex; align-items: center; gap: 8px; padding: 2px 0; }
.st-fired { color: var(--red); font-weight: 600; }
/* Запасной путь «просто закрыть доступ» намеренно неприметный: это исключение, а не второй
   равноправный способ уволить. На .hint его класть нельзя — в диалогах у подсказки жёлтый фон,
   и ссылка читалась как предупреждение. */
/* Строка товара на складе: название держит ширину, кнопки переносятся сами.
   ⚠ Без этого шесть кнопок сжимали «Bonaqua газ 0,5» в столбик по одному слову. */
.h-row.st-prod { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; }
.st-prod-name { flex: 1 1 240px; min-width: 180px; }
.st-prod-acts { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; }

/* Имя арены в заголовке окна: не «украшение», а ответ на вопрос «куда я сейчас ввожу». */
.dlg-arena { font-size: 14px; font-weight: 700; color: var(--teal); }

/* Миниатюра фото документа с кнопкой «переснять» в углу. */
.e-photo { position: relative; display: inline-block; }
.e-photo button {
  position: absolute; top: -6px; right: -6px; width: 22px; height: 22px; line-height: 1;
  border-radius: 50%; border: 1px solid var(--border); background: var(--surface); cursor: pointer;
  font-size: 13px; padding: 0;
}
.e-photo button:hover { background: var(--teal); color: var(--surface); border-color: var(--teal); }

/* Снимки, приложенные к закрытию смены: сетка миниатюр с крестиком «убрать». */
.so-prevs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.so-thumb { position: relative; display: inline-block; }
.so-thumb img { width: 84px; height: 84px; border-radius: 14px; object-fit: cover; display: block; }
.so-thumb button {
  position: absolute; top: -6px; right: -6px; width: 22px; height: 22px; line-height: 1;
  border-radius: 50%; border: 1px solid var(--border); background: var(--surface); cursor: pointer;
  font-size: 13px; padding: 0;
}
.so-thumb button:hover { background: var(--red); color: var(--surface); border-color: var(--red); }

/* Погашенная кнопка должна ВЫГЛЯДЕТЬ погашенной: без этого «Оформить увольнение» оставалась
   ярко-красной, человек жал и ничего не происходило — хуже, чем понятный отказ. */
#dlg-fire .btn:disabled { opacity: .4; cursor: not-allowed; filter: none; }
.fire-plain { text-align: center; margin-top: 6px; }
.fire-plain a { color: #93907F; font-size: 12px; text-decoration: underline; }
.fire-plain a:hover { color: var(--red); }

/* =========================================================================
   АДАПТИВ ПОД ТЕЛЕФОН И ПЛАНШЕТ (26.07.2026)
   Решение владельца: одна админка на все экраны вместо второго мобильного фронта.
   Правило блока: НИЧЕГО выше по файлу не переопределяем без media-запроса —
   на широком экране админка обязана остаться такой же, как была.
   ========================================================================= */

/* Порядок в шапке: #topnav теперь оборачивает кнопки, и «+ Запись» с «кто я»
   должны остаться справа, как раньше. На десктопе #topnav — display:contents,
   поэтому его дети участвуют во flex-раскладке .topbar напрямую. */
#topnav { display: contents; }
#btn-new { order: 1; }
.whoami { order: 2; }
#btn-burger { display: none; }   /* показывается только media-запросом ниже */
/* PIN вводится вразрядку, но подсказка «4–6 цифр» должна читаться нормально */
#login-pin { letter-spacing: 6px; }
#login-pin::placeholder { letter-spacing: normal; }
/* Номер гостя — ссылка «позвонить»; в таблице дня выглядит как обычный текст */
.bk .tel { color: inherit; text-decoration: none; }
.bk .tel:hover { text-decoration: underline; }

@media (max-width: 900px) {
  /* Шапка: всё, кроме арены/даты/«+ Запись», уезжает под ☰ */
  #btn-burger { display: block; font-size: 18px; padding: 9px 14px; }
  #topnav {
    display: none;
    position: fixed; top: 96px; right: 8px; left: 8px; z-index: 30;
    background: var(--surface); border: 1px solid var(--line); border-radius: 18px;
    padding: 10px; gap: 8px; max-height: 78vh; overflow: auto;
    box-shadow: 0 12px 32px rgba(0,0,0,.45);
  }
  #topnav.open { display: grid; grid-template-columns: 1fr 1fr; }
  #topnav .btn { min-height: 46px; font-size: 14px; }
  #topnav .whoami { grid-column: 1 / -1; flex-wrap: wrap; border-top: 1px solid var(--line); padding-top: 8px; }
  #topnav .whoami span { max-width: none; }
  /* Порядок в меню. На десктопе «частое — правее, ближе к + Запись», и в вертикальном
     списке то же самое читается наоборот: Касса и Операции оказывались в самом низу,
     а Сотрудники и Акции — наверху. Переворачиваем ГРУППАМИ через order (разметку не трогаем,
     десктопный порядок остаётся прежним). */
  #btn-cash, #btn-ops, #btn-prepays { order: 10; }                      /* деньги */
  #btn-bar, #btn-stock, #topnav a[href="/scan.html"] { order: 20; }     /* товар */
  #btn-certs, #btn-history, #btn-safety, #btn-reports { order: 30; }    /* документы */
  #btn-staff, #btn-disc, #btn-promo, #btn-game, #btn-settings { order: 40; } /* справочники */
  #topnav .whoami { order: 50; }
  /* Разделители групп: на десктопе вертикальные линии, в меню — горизонтальные на всю ширину.
     display возвращаем явно: на ширинах до 1100 px они спрятаны, а в меню как раз нужны. */
  #topnav .grp { display: block; width: auto; height: 1px; grid-column: 1 / -1; align-self: auto; margin: 2px 0; }
  /* Каждый разделитель — в своей позиции МЕЖДУ группами (15/25/35), иначе он попадал
     в один слот с группой и всплывал над первым разделом. */
  #topnav .grp:nth-of-type(1) { order: 15; }
  #topnav .grp:nth-of-type(2) { order: 25; }
  #topnav .grp:nth-of-type(3) { order: 35; }

  /* Шапка в две строки: [марка] [арена] [☰] / [дата] [+ Запись].
     Горизонтальный логотип-надпись шириной ~155 px на 375-м экране съедал половину
     строки — подменяем его квадратной маркой (в CSS, чтобы не трогать разметку). */
  .topbar { flex-wrap: wrap; gap: 6px; padding: 8px 10px; }
  /* ⚠ Только шапка админки: у reports.html свой логотип текстом, и общее правило рисовало
     на той странице ВТОРУЮ марку рядом с ним. */
  #view-main .topbar .logo-img.sm { display: none; }
  #view-main .topbar::before {
    content: ''; width: 28px; height: 28px; flex: none; order: 0;
    background: url('/img/favicon.png?v=3') center/contain no-repeat; border-radius: 7px;
  }
  /* Базисы в px, а не в %: на 375 px дата с «+ Запись» переносятся во вторую строку,
     на планшете 768 всё помещается в одну. С процентами планшет разъезжался. */
  .topbar #sel-arena { order: 1; flex: 1 1 120px; min-width: 0; max-width: none; }
  #btn-burger { order: 2; }
  .topbar #sel-date { order: 3; flex: 1 1 150px; }
  #btn-new { order: 4; flex: 0 0 auto; padding: 9px 14px; }
  .topbar .spacer { display: none; }
  #day-badge { display: none; }          /* день недели виден в самой дате */

  main { padding: 10px; }

  /* Таблица дня → карточки. Разметку не трогаем: те же дети, только flex-раскладка */
  .col-head { display: none; }
  .bk {
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px;
    padding: 12px; font-size: 15px;
  }
  .bk > div:nth-child(1) { font-size: 18px; font-weight: 700; }   /* время */
  .bk .name { flex: 1 1 auto; font-size: 16px; white-space: normal; }
  .bk .sum { margin-left: auto; text-align: right; font-size: 16px; }
  .bk .phone { flex-basis: 100%; font-size: 14px; }               /* телефон — со своей строки */
  .bk .tkt { flex: 1 1 100%; white-space: normal; font-size: 13px; color: #4A4D45; }
  .bk .rowact { flex: 1 1 100%; justify-content: flex-start; gap: 6px; padding-top: 4px; }
  .bk .rowact .btn-mini { font-size: 14px; padding: 10px 14px; }  /* палец, а не мышь */
  .bk .subrow { flex: 1 1 100%; font-size: 13px; }
  .bk.cont { font-size: 13px; }
  .hour-head { padding: 10px 12px; }
  /* Имя с меткой комнаты переносится по словам: из-за nowrap блок целиком уезжал на свою
     строку, и у ДР время оказывалось отдельно от имени, а у обычных записей — вместе. */
  /* На телефоне строка не сетка, а flex — обрезать нечего, содержимое переносится по словам.
     overflow:hidden из десктопных правил здесь резал бы перенесённую вторую строку. */
  .bk .name { white-space: normal; flex-wrap: wrap; overflow: visible; }
  .bk .name .nm-text, .bk .name .cont-tag { overflow: visible; text-overflow: clip; max-width: none; }
  /* Граница между гостями: поле «код партнёра» предыдущей записи прилипало к следующей */
  .bk + .bk { border-top: 2px solid var(--border); }

  /* ЗАВЕРШЁННЫЕ записи сворачиваем. За день их 61 из 66, и каждая занимала ~490 px —
     весь день превращался в 40 экранов прокрутки. Админу с телефоном нужны те, кто ещё
     не пришёл и не заплатил; завершённые остаются строкой «время · имя · сумма»,
     подробности открываются тапом по записи. */
  .bk.st-done .stamps,
  .bk.st-done .rowact,
  .bk.st-done .tkt,
  .bk.st-done .phone { display: none; }
  .bk.st-done { padding: 8px 12px; opacity: .75; }
  .bk.st-done .name { font-size: 15px; }
  /* Подстрока (код партнёра, фото документа) у завершённой записи прячется, только если
     там НЕ осталось незакрытых требований — «.need» ставится как раз на них. */
  .bk.st-done .subrow:not(:has(.need)) { display: none; }

  /* Окна — листом снизу: во всю ширину, но по ВЫСОТЕ СОДЕРЖИМОГО. Раньше стояла жёсткая
     height:100dvh, и короткие окна (закрытая касса — три строки) показывали пол-экрана пустоты. */
  dialog {
    width: 100vw; max-width: 100vw; height: auto; max-height: 92dvh;
    margin: auto 0 0; border-radius: 20px 16px 0 0;
  }
  .form { padding: 14px; gap: 10px; }
  .form .row { gap: 8px; }
  /* Поля формы — по одному в строку. ⚠ Кроме .radio: это чипы-переключатели (категории бара,
     режимы склада, «пакет/калькулятор»), и растягивание каждого на всю ширину разносило их
     по одному на строку — окно бара начиналось с экрана одних категорий. */
  .form label:not(.radio) { min-width: 100%; font-size: 13px; }
  .form label.radio { min-width: 0; flex: 0 0 auto; }
  /* ⚠ 16.08. В настройках .radio — это чекбоксы с ДЛИННЫМИ подписями («клубная карта включена»,
     «показывать гостям кнопки онлайн-оплаты»), а не чипы-переключатели. С flex:0 0 auto подпись
     не переносилась и уходила за правый край экрана. Правило точечное: в баре и на складе
     .radio по-прежнему чипы, и растягивать их нельзя. */
  #dlg-settings label.radio { flex: 1 1 100%; align-items: flex-start; }
  /* ⚠ Аудит 29.07, две беды сразу.
     1) Панель кнопок не переносилась: в карточке записи их пять («Журнал правок», «Отменить»,
        «Закрыть», «Сохранить», «🗑») — «Сохранить правки» уезжало за правый край на 375.
     2) В одном окне таких панелей бывает несколько (в кассе три, в сертификатах четыре),
        и все они липли к низу, наезжая друг на друга: сверху оказывалась первая по разметке,
        то есть «Закрыть». Липкой оставляем ТОЛЬКО последнюю панель окна — она с главной
        кнопкой; остальные едут вместе с содержимым, как обычные. */
  .actions { background: var(--surface); padding: 10px 0 4px; flex-wrap: wrap; }
  /* ⚠ 25.08 (нашёл аудит связки). Правило целилось в `form.form`, а таких элементов в админке
     НЕТ ни одного — там 28 штук `div.form`; второе правило доставало лишь девять редких окон.
     Плюс `.actions` почти нигде не последний ребёнок: после него идёт блок ошибки (#p-err,
     #bar-err, #e-err). Итог: «Продать», «Принять», «Сохранить», «Закрыть смену» приходилось
     доскроллить донизу, а в баре после шестидесяти товаров это квест.
     Убираем `:last-child` и целимся в оба вида контейнера. */
  .dlg > .actions, .form > .actions { position: sticky; bottom: 0; z-index: 5;
    background: var(--card, var(--surface)); padding-top: 6px; }
  .actions .btn { flex: 1 1 44%; min-height: 48px; }
  .svc-grid, .denom-grid, .bar-grid { grid-template-columns: 1fr 1fr; }
  .bar-grid { max-height: none; }
  .h-list, .e-log { max-height: none; }
  .pcode { width: 100%; }
  /* Строки списков (сотрудники, операции, история, сертификаты) свёрстаны ИНЛАЙНОВЫМ flex
     прямо в app.js — на телефоне они уезжали вбок на 500 px. Перебить инлайн можно только
     через !important, и делаем это исключительно внутри мобильного media-запроса. */
  .h-row { flex-wrap: wrap !important; gap: 6px 8px !important; }
  .h-row > span { flex-wrap: wrap; min-width: 0; }
  .h-row .btn-mini { font-size: 13px; padding: 8px 12px; }
  /* Ряды переключателей режима (склад: остатки/приход/инвентаризация/рецепты/история,
     ДР: пакет/калькулятор) не переносились и распирали окно на 400 px вправо.
     ⚠ Категории бара (#bar-cats) используют ТОТ ЖЕ класс .dr-mode, и это правило разносило
     их по одной на строку с огромными отступами — см. отдельное правило ниже. */
  .dr-mode { flex-wrap: wrap; gap: 8px 14px; row-gap: 8px; }
  .dr-mode .radio { padding: 6px 0; }

  /* Категории бара — компактные строки-чипы, а не разреженный список на весь экран */
  #bar-cats { gap: 6px 10px !important; row-gap: 6px !important; }
  #bar-cats .radio { padding: 0; }
  /* Чек бара липнет к низу окна: после ~60 товаров кнопка «Продать» была недосягаема */
  #dlg-bar .actions { flex-wrap: wrap; }
  #dlg-bar .actions .btn { flex: 1 1 46%; min-height: 46px; font-size: 14px; padding: 8px 10px; }
  /* Служебные переключатели товара (🌐 продаётся онлайн, ✏ цена) — не для работы с телефона */
  .bar-admin { display: none; }

  /* Цена держится над кнопками, пока листаешь форму: в форме ДР она была в самом низу,
     а меняется от каждого поля — выбирая пакет, админ должен видеть сумму. */
  .quote { position: sticky; bottom: 62px; z-index: 4; box-shadow: 0 -4px 12px rgba(0,0,0,.06); }
  .actions { z-index: 5; }

  /* Операции: «сменить способ» вставала то в строку, то на свою — зависело от длины имени */
  #ops-list .btn-mini { display: block; width: 100%; margin-top: 6px; }
  /* Плашки-пояснения в окнах: на телефоне занимали больше половины первого экрана */
  dialog .hint { font-size: 12px; padding: 6px 10px; line-height: 1.35; }

  /* iOS зумит страницу, если у поля шрифт меньше 16px. Только поля ввода. */
  input, select, textarea { font-size: 16px; }
  /* Высота под палец — текстовым полям и спискам. Галочкам и радиокнопкам она не нужна:
     от неё они раздувались до 44 px и рвали строку переключателей. */
  .form label input:not([type="checkbox"]):not([type="radio"]),
  .form label select { min-height: 44px; }
}

@media (max-width: 560px) {
  .svc-grid { grid-template-columns: 1fr; }
  .denom-grid { grid-template-columns: repeat(3, 1fr); }
  /* Товары бара остаются в ДВЕ колонки и на 375 px: в одну помещалось три позиции на экран,
     а их около шестидесяти. Названия короткие — двух колонок хватает. */
  .bar-grid { grid-template-columns: 1fr 1fr; }
  /* меню шапки остаётся в две колонки и на 375 px: названия короткие, зато панель вдвое ниже */

  /* ⚠ 11.08 (аудит вёрстки: 21 раздел × две ширины, зацепился один — «Отчёт по акциям»).
     Выпадающий список подстраивается под САМЫЙ ДЛИННЫЙ пункт: у акций названия вроде
     «Многодетная семья — 25%», и селект распирал строку до 381 px при экране 375. Итог —
     горизонтальная прокрутка всего окна: пальцем экран уезжает вбок, и человек думает,
     что сломалось. Разрешаем полям сжиматься: min-width:0 снимает распорку от содержимого. */
  /* ⚠ 28.08. Это правило (11.08, «селект акций распирал строку») своим min-width:0 ПЕРЕБИВАЛО
     более раннее «поля формы — по одному в строку» (@media 900, min-width:100%): специфичность
     равная, каскадом побеждал этот блок — и на телефоне поля жались в одну строку по 61px
     (нашлось в VIP-картах: пять полей в строке шириной 375). Сжатие оставляем, но вместе
     с ним возвращаем «одна строка — одно поле»; .radio-чипы не трогаем, как и в правиле 900. */
  .dlg .row > label:not(.radio), .form .row > label:not(.radio) { min-width: 0; flex: 1 1 100%; }
  .dlg .row > label.radio, .form .row > label.radio { min-width: 0; }
  .dlg .row select, .dlg .row input, .form .row select, .form .row input { max-width: 100%; min-width: 0; }
}

/* Тач-устройство: подсказка вместо двойного клика, который пальцем не сделать */
@media (pointer: coarse) {
  .bk[data-bid] { cursor: pointer; }
}

/* 29.07: в строке сотрудника кнопок стало восемь (добавились «🗝 все арены» и «✉ вход по
   email»). Инлайновый flex без переноса выдавливал имя за левый край карточки — текст
   уезжал под соседние блоки. Кнопки переносятся, имя не сжимается ниже читаемого. */
.st-row { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; flex-wrap: wrap; }
.st-who { flex: 1 1 260px; min-width: 0; overflow-wrap: anywhere; }
.st-acts { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; flex: 0 1 auto; }
@media (max-width: 900px) { .st-acts { justify-content: flex-start; } }

/* Разбивка продаж бара по группам (просьба управляющей площадкой 30.07): её переписывают
   в таблицу выручки, поэтому цифры должны читаться столбиком и без лишнего декора. */
/* Таблица бара И отчёта по акциям — один стиль на обе: вторая копия правил разъехалась бы. */
.bd-cats { width: 100%; border-collapse: collapse; margin: 8px 0 12px; font-size: 14px; }
.bd-cats th { background: var(--brand-light); color: var(--ink); font-weight: 600; text-align: right; padding: 6px 8px; }
.bd-cats th:first-child { text-align: left; }
.bd-cats td { padding: 5px 8px; text-align: right; border-bottom: 1px solid #E3DED2; font-variant-numeric: tabular-nums; }
.bd-cats td:first-child { text-align: left; }
.bd-cats tr:nth-child(even) td { background: #F1EEE6; }
/* Хвосты. Комментарий записи — отдельная строка во всю ширину под ней: слева направо, обычным
   текстом. Раньше он лежал в ячейке суммы и растягивал строку на несколько этажей.
   nowrap на ячейках нужен там же и по той же причине: длинное имя арены ломалось на три
   строки и тянуло за собой высоту всей таблицы. Комментарию перенос, наоборот, разрешён. */
.ls-t td, .ls-t th { white-space: nowrap; }
/* ⚠ 16.08. В хвостах текст равняется ВЛЕВО, а числа — вправо. В общей таблице (.bd-cats)
   вправо прижато всё, кроме первой колонки: там это правильно — она про деньги. Здесь же
   в середине стоят имя гостя и арена, и от выравнивания вправо имя слипалось с ценой
   («Имя Фамилия 40,00» читалось как одна ячейка). */
.ls-tbl th, .ls-tbl td { text-align: left; }
.ls-tbl th.num, .ls-tbl td.num { text-align: right; }
/* Текстовые колонки — по содержимому, свободное место достаётся колонкам с деньгами.
   Иначе таблица растягивала текст по всей ширине и «10.08 … длинное имя арены … имя
   гостя» стояло тремя островками с провалами между ними. */
.ls-tbl td:not(.num), .ls-tbl th:not(.num) { white-space: nowrap; width: 1%; }
.ls-tbl tr.ls-note td { width: auto; }
/* Таблица без денежных колонок: остаток ширины забирает последняя, иначе три коротких
   колонки расползаются по всей строке островками. */
.ls-plain td:last-child, .ls-plain th:last-child { width: auto; }
/* Реестр уведомлений: кнопка и пояснение к ней. ⚠ В диалогах .row сам по себе НЕ flex (флексом
   он становится только внутри .form), поэтому пояснение текло обычным текстом и его жёлтая
   плашка заезжала на строку под кнопкой. */
#dlg-notify .row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
#dlg-notify .row .hint { flex: 1 1 260px; }
/* Отчёт «Онлайн-записи». Та же грабля, что у реестра уведомлений: в диалоге `.row` сам по себе
   НЕ flex, поэтому и строка фильтров, и плитки сводки текли одна под другой во всю ширину. */
#dlg-online .row, #dlg-zonefix .row, #dlg-viol .row, #dlg-freehour .row { display: flex; gap: 10px; flex-wrap: wrap; }
#dlg-online h4, #dlg-viol h4, #dlg-freehour h4 { margin: 0 0 6px; font-size: 15px; font-weight: 600; }
/* Полоса прокрутки на телефоне рисуется поверх содержимого и срезала правый край чисел
   («+4» превращалось в «+»). Отступ отдаём ей, а не цифрам. */
#on-body, #vi-body, #zf-body { padding-right: 8px; }
.bd-cats tr.ls-note td { text-align: left; white-space: normal; padding: 0 8px 7px;
  border-bottom: 1px solid #E3DED2; font-size: 13px; color: #4A4D45; }
.bd-cats.has-notes tr:nth-child(even) td { background: transparent; }
.bd-cats.has-notes tr td { border-bottom: 0; }
.bd-cats.has-notes tr.ls-note td { border-bottom: 1px solid #E3DED2; }
/* Запись без комментария сама себе подводит черту — иначе строки слипаются. */
.bd-cats.has-notes tr:not(.ls-note):has(+ tr:not(.ls-note)) td { border-bottom: 1px solid #E3DED2; }
.bd-cats.has-notes tr:not(.ls-note):last-child td { border-bottom: 1px solid #E3DED2; }
.bd-cats .bd-total td { font-weight: 600; border-top: 2px solid var(--ink); border-bottom: 0; background: var(--surface); }

/* Запись на арену своего города (09.08). Полоса заметная, но не тревожная: администратор
   здесь ничего не нарушает — просто должен помнить, что касса у этой арены своя. */
.alien-note { margin: 6px 0; padding: 7px 10px; border-radius: 12px; font-size: 13px;
  background: #F6EBD8; border: 1px solid #E0C48A; color: #6B4A11; }

/* Напоминание смене. Три состояния: «скоро» — спокойное, «пора» — жёлтое, «просрочено» —
   красное. Плашка кликабельна целиком: на телефоне попасть в мелкую кнопку сложно. */
.rem-bar { margin: 8px 0; padding: 10px 12px; border-radius: 14px; cursor: pointer;
  display: flex; gap: 10px; align-items: center; line-height: 1.35; }
.rem-bar .rem-ic { font-size: 22px; flex: 0 0 auto; }
.rem-bar .rem-body { flex: 1 1 auto; min-width: 0; }
.rem-bar .rem-go { flex: 0 0 auto; align-self: center; white-space: nowrap; }
.rem-how { margin-top: 6px; }
.rem-how summary { cursor: pointer; font-size: 13px; font-weight: 600; opacity: .8; }
.rem-how ol { margin: 6px 0 0; padding-left: 20px; font-size: 13px; }
.rem-how li { margin: 3px 0; }
.rem-ask { font-size: 12.5px; margin-top: 6px; opacity: .85; }
.rem-bar b { display: block; font-size: 15px; }
.rem-bar .rem-hint { font-size: 12.5px; opacity: .85; }
.rem-bar.rem-soon { background: #EEF1F6; border: 1px solid #CFD8E4; color: #3B4A66; }
.rem-bar.rem-due  { background: #F6EBD8; border: 1px solid #E0C48A; color: #6B4A11; }
.rem-bar.rem-late { background: #F7E6E2; border: 1px solid #EBCFC8; color: #7E3324; }

/* Увольнение на телефоне: каждое поле своей строкой. Иначе дата и основание делят 375 px,
   и в поле даты видно «13/08/20» без последних цифр года. */
@media (max-width: 600px) {
  /* ⚠ 20.08. В ряду карточки праздника стало ЧЕТЫРЕ поля: комната, часы, пакет, акция.
     Общее правило `min-width: 0` (11.08, чтобы селект акций не распирал строку) позволяет им
     сжиматься до 81 px — на телефоне читалось «Нижняя…», «— без п…», и выбрать пакет вслепую
     нельзя. Ставим по два поля в строку: и читаемо, и горизонтальной прокрутки по-прежнему нет. */
  #e-room-row > label { flex: 1 1 45%; }
  #dlg-fire .row { flex-direction: column; gap: 8px; }
  #dlg-fire .row label { flex: 1 1 auto; width: 100%; }
  /* ⚠ Галочки чек-листа исключены намеренно: с width:100% каждый квадратик растягивался
     на всю строку и выталкивал подпись за край окна (поймано замером scrollWidth). */
  #dlg-fire input:not([type="checkbox"]), #dlg-fire select { width: 100%; }
  /* ⚠ То же и на складе: поле «Остатки на дату» делило строку с кнопкой Excel, и от даты
     было видно «13/0». Дата — не украшение: по ней выгружают остатки на конец дня. */
  #dlg-stock input[type="date"] { width: 100%; }
  #dlg-stock .row > label { flex: 1 1 100%; }
  .fire-calc .fc-row { flex-wrap: wrap; }
}

/* Результат проверки шлюза сообщений — виден сразу, не теряется внизу формы */
.hint.ok{background:#E4EDE6;border:1px solid #CFE0D4;color:#245640}
.hint.bad{background:#F7E6E2;border:1px solid #EBCFC8;color:#7E3324}


/* ⚠ 23.08. График смен: сетка «сотрудники × дни месяца». Ширины фиксированные, чтобы 31 колонка
   не расползалась; первая колонка липкая — иначе при прокрутке вправо теряются имена. */
.dlg-wide { max-width: min(96vw, 1500px); }
.sc-wrap { overflow: auto; max-height: 62vh; border: 1px solid var(--line, var(--line)); border-radius: 12px; }
.sc-tbl { border-collapse: separate; border-spacing: 0; font-size: 13px; }
.sc-tbl th, .sc-tbl td { border-bottom: 1px solid #E3DED2; border-right: 1px solid #EDE9DF; padding: 0; }
.sc-tbl thead th { position: sticky; top: 0; background: var(--brand-light); color: var(--ink); font-weight: 600;
  z-index: 3; padding: 6px 4px; text-align: center; min-width: 34px; }
.sc-tbl thead th.sc-wknd { background: #D9D3C6; }
.sc-name { position: sticky; left: 0; background: var(--surface); z-index: 2; min-width: 190px; max-width: 220px;
  padding: 6px 10px !important; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sc-tbl thead th.sc-name { z-index: 4; background: var(--brand-light); }
.sc-cell { width: 34px; height: 30px; text-align: center; cursor: pointer; user-select: none; }
.sc-cell:hover { outline: 2px solid var(--brand); outline-offset: -2px; }
.sc-full { background: var(--brand-light); font-weight: 600; }
.sc-morning { background: #F6EBD8; }
.sc-evening { background: #E4EDE6; }
/* ⚠ 27.08: подписи ТБ собраны не со всех гостей. Не запрет, а видимость — решение владельца. */
.tb-warn { color:var(--danger); font-weight: 600; font-size:11.5px; white-space:nowrap; }
/* смена со своим временем: вносится из графика управляющих, отличается от шаблонных */
.sc-custom { background: #EBE4F2; font-weight: 700; }
.sc-fact { box-shadow: inset 0 -3px 0 var(--ok); }        /* человек реально работал в этот день */
.sc-miss { box-shadow: inset 0 -3px 0 var(--danger); }        /* в графике был, а факта нет */
.sc-total { font-weight: 700; background: #FAF7F0; min-width: 54px; text-align: center; }
.sc-sum td { border-top: 2px solid #D9D3C6; font-weight: 700; background: #FAF7F0; }


/* Утренний обход: строка на зону, три части (мат, сетка, пружины) с тремя состояниями.
   По умолчанию не отмечено ничего — это и значит «цело». */
.zc-tbl { width: 100%; border-collapse: collapse; font-size: 14px; }
.zc-tbl thead th { position: sticky; top: 0; background: var(--brand-light); color: var(--ink); text-align: left;
  padding: 7px 10px; font-family: 'Golos Text', sans-serif; font-weight: 700; z-index: 2; }
.zc-tbl td { border-bottom: 1px solid #E3DED2; padding: 5px 10px; }
.zc-zone { font-weight: 600; white-space: nowrap; }
.zc-part { display: inline-flex; gap: 4px; align-items: center; }
.zc-btn { border: 1px solid #D9D3C6; background: var(--surface); border-radius: 999px; padding: 2px 10px;
  cursor: pointer; font-size: 13px; }
.zc-btn.on-warn { background: #F6EBD8; border-color: #C9A24A; font-weight: 700; }
.zc-btn.on-bad { background: #F7E6E2; border-color: var(--danger); color: #7E3324; font-weight: 700; }
.zc-row-bad { background: #FCF3F1; }

/* 23.08: поле формы в модальных окнах — подпись СВЕРХУ. Без этого длинная подпись и поле
   встают в строку, и на телефоне подпись оказывается под полем (окно замечания обхода). */
.dlg label.fld { display: flex; flex-direction: column; gap: 4px; font-size: 13px; font-weight: 700; }
/* 30.08 (владелец: «почти все выкатки отдельных разделов с проблемами по вёрстке»).
   Модальные окна, собранные на <div class="dlg"> вместо <div class="form">, оставались
   БЕЗ раскладки вовсе: контейнер не имел ни отступов, ни колонки, и подписи с полями текли
   одной строкой (разбор отзыва, контакты сотрудника, старый бланк, смена должности).
   Даём .dlg в модальном окне ту же базу, что у .form; полноэкранные разделы (.dlg-full)
   не трогаем — у них своя рама. Плюс сторож в smoke.mjs: голый label с полем вне .form/.dlg
   валит сборку. */
dialog:not(.dlg-full) > .dlg { padding: 24px; display: flex; flex-direction: column; gap: 12px; }
dialog:not(.dlg-full) > .dlg .row { display: flex; gap: 10px; flex-wrap: wrap; }
dialog:not(.dlg-full) > .dlg label:not(.radio):not(.svc) { display: flex; flex-direction: column;
  gap: 4px; font-size: 12px; font-weight: 700; flex: 1; min-width: 130px; }
dialog:not(.dlg-full) > .dlg label.radio { display: flex; align-items: center; gap: 6px; }

/* ── Полноэкранные разделы ────────────────────────────────────────────
   28.08 (решение владельца: «давай второй вариант — на полный экран с кнопкой назад»).
   Тяжёлые разделы (Прайс, Нарушения, Онлайн-записи, Отчёт по акциям, Хвосты, Отзывы)
   жили в модалке 680px и по мере наполнения данными превратились в бесконечный скролл.
   Рама одна на всех: раздел раскрывается на весь экран, сверху липкая шапка с «← Назад»,
   контент центрован и ограничен читаемой шириной. Модалка при этом остаётся модалкой
   (dialog.showModal) — состояние арены и даты в основной шапке никуда не девается. */
dialog.dlg-full {
  width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh;
  margin: 0; border-radius: 0; padding: 0;
}
dialog.dlg-full .dlg, dialog.dlg-full .form { max-width: 1240px; margin: 0 auto; padding: 0 24px 32px; }
dialog.dlg-full h3 {
  position: sticky; top: 0; z-index: 30; background: var(--surface);
  display: flex; align-items: center; gap: 14px;
  margin: 0 -24px 12px; padding: 12px 24px; border-bottom: 1px solid #E3DED2;
}
dialog.dlg-full h3 .btn-back { flex: none; }
/* Низ раздела: кнопки действий прилипают, чтобы «Сохранить» не надо было искать в конце простыни. */
dialog.dlg-full .actions {
  position: sticky; bottom: 0; z-index: 30; background: var(--surface);
  margin: 12px -24px 0; padding: 10px 24px;
  border-top: 1px solid #E3DED2; display: flex; gap: 10px; justify-content: flex-end;
}
@media (max-width: 700px) {
  dialog.dlg-full .dlg, dialog.dlg-full .form { padding: 0 12px 20px; }
  /* Селект с длинной опцией («через сайт — Viber + SMS (рекомендуется)») не ужимается
     max-width'ом — его intrinsic-ширина побеждает, и Настройки уезжали на 2px вбок.
     На телефоне селекты в раме занимают строку целиком. */
  dialog.dlg-full select { width: 100%; max-width: 100%; min-width: 0; box-sizing: border-box; }
  dialog.dlg-full h3 { margin: 0 -12px 10px; padding: 10px 12px; }
  dialog.dlg-full .actions { margin: 10px -12px 0; padding: 8px 12px; }
}

/* ── Прайс в полноэкранной раме ── */
/* Города рядом на широком экране: сравнивать площадки разных городов — частый сценарий владельца. */
.pr-cities { display: grid; grid-template-columns: 1fr; gap: 8px 40px; align-items: start; }
/* min-width:0 обязателен: у грид-ячейки по умолчанию auto, и колонка города отказывалась
   сжиматься под телефон — прайс уезжал за правый край (631px на экране 375). */
.pr-cities > .pr-city { min-width: 0; }
@media (min-width: 1100px) { .pr-cities { grid-template-columns: 1fr 1fr; } }
.pr-city > .svc-title {
  position: sticky; top: 52px; z-index: 20; background: var(--brand-light);
  padding: 8px 12px; border-radius: 14px; margin-top: 8px;
}
.pr-in { font-variant-numeric: tabular-nums; }

/* ── Отчётные разделы (Нарушения, Онлайн-записи…) — единая система ──────────
   28.08, этап 2 полноэкранных разделов. До этого каждая цифра и полоска несли
   свои инлайн-стили — отсюда разнобой размеров, на который жаловался владелец.
   Теперь плитки, полосы и сетки одни на все отчёты. */
.rep-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 10px; margin: 12px 0; }
.rep-card { background: #FAF7F0; border-radius: 18px; padding: 12px 16px; min-width: 0; }
.rep-card b { display: block; font-size: 27px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.15; }
.rep-card span { font-size: 12px; color: #63665D; }
.rep-grid { display: grid; grid-template-columns: 1fr; gap: 4px 36px; align-items: start; }
@media (min-width: 1000px) { .rep-grid { grid-template-columns: 1fr 1fr; } }
.rep-grid > div { min-width: 0; }
.rep-h { margin: 14px 0 6px; font-size: 15px; font-weight: 700; }
/* Строка с полосой: подпись и число на одной линии, полоса под ними на всю ширину.
   Вторая, серая полоса — прошлый период: сравнение видно без чтения чисел. */
.bar-row { margin: 5px 0; font-size: 13px; }
.bar-row .cap { display: flex; justify-content: space-between; gap: 12px; }
.bar-row .cap b { font-variant-numeric: tabular-nums; white-space: nowrap; }
.bar-row .track { height: 6px; background: #E3DED2; border-radius: 3px; margin-top: 2px; }
.bar-row .track i { display: block; height: 6px; background: var(--brand); border-radius: 3px; }
.bar-row .track.prev { height: 4px; margin-top: 2px; }
.bar-row .track.prev i { height: 4px; background: #D9D3C6; }

/* ── Окно «Отзывы гостей» ─────────────────────────────────────────────
   ⚠ 27.08, третья итерация по скринам владельца: грид на всём списке разом давал строки
   без общей линии — «криво». Каждая строка — свой грид с одной нижней границей. */
.rv-row {
  display: grid; grid-template-columns: 104px 132px 186px minmax(0, 1fr);
  gap: 2px 18px; padding: 10px 2px; border-bottom: 1px solid #E3DED2; align-items: baseline;
}
.rv-row:hover { background: #FAF7F0; }
.rv-row > div { min-width: 0; }
.rv-when { font-size: 12px; color: #63665D; margin-top: 1px; }
.rv-stars { font-size: 15px; letter-spacing: .5px; line-height: 1.2; }
/* Гость пишет с переносами строк («Всё отлично 👍\nВот только долго искали») — без pre-line
   строки слипались в «👍Вот». */
.rv-text { line-height: 1.4; white-space: pre-line; }
/* Разбивка по вопросам — одной строкой под текстом; не даём ей рвать слова пополам. */
.rv-marks { font-size: 12.5px; color: #63665D; margin-top: 2px; }
.rv-marks span { white-space: nowrap; }
.rv-meta { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; margin-top: 3px; font-size: 12px; }
/* 🔧 Монтажные ночи (02.09): лента ночей сверху, дальше две колонки — ночь и «висят». */
.in-strip { display: flex; gap: 6px; flex-wrap: wrap; margin: 8px 0 12px; }
.in-night { border: 1px solid #E3DED2; border-radius: 14px; padding: 6px 10px; background: var(--surface); cursor: pointer;
  font: inherit; text-align: left; line-height: 1.25; }
.in-night b { display: block; font-size: 13px; }
.in-night span { font-size: 12px; color: #63665D; }
.in-night.sel { border-color: var(--ink); background: #F6EBD8; }
.in-grid { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 16px; align-items: start; }
.in-person { display: flex; gap: 10px; flex-wrap: wrap; align-items: baseline; padding: 6px 0; border-bottom: 1px dashed #E3DED2; }
.in-person b { min-width: 170px; }
.in-seg { white-space: nowrap; }
.in-seg i { color: #63665D; font-style: normal; }
.in-total { margin-left: auto; font-weight: 600; white-space: nowrap; }
.in-side .h-row { background: #F6EBD8; }
.in-age { color: #A8412F; font-weight: 600; }
@media (max-width: 900px) {
  /* На телефоне в две колонки помещается только «оценка + когда» и «дата визита».
     Имя гостя и текст отзыва идут во всю ширину — иначе «Пашкевич анастасия»
     переносится по слогам в колонке шириной 104px. */
  .rv-row { grid-template-columns: 104px minmax(0, 1fr); gap: 2px 12px; }
  .rv-row > div:nth-child(3) { grid-column: 1 / -1; margin-top: 3px; }
  .rv-row > div:last-child { grid-column: 1 / -1; margin-top: 2px; }
}

/* Опрос гостя по телефону: ивент задаёт ТЕ ЖЕ вопросы, что видит гость по ссылке, и жмёт
   цифру от 1 до 5. Кнопки крупные — окно открывают с телефона, держа трубку. */
.rvm-script { background: #F6EBD8; border: 1px solid #E6D3AE; border-radius: 12px;
  padding: 10px 12px; margin: 10px 0; font-size: 13px; line-height: 1.45; }
.rvm-q { padding: 10px 0; border-top: 1px solid #E3DED2; }
.rvm-qt { font-weight: 700; font-size: 15px; }
.rvm-hint { color: #93907F; font-size: 12px; margin: 2px 0 8px; }
.rvm-btns { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.rvm-btns button { width: 46px; height: 46px; border-radius: 12px; border: 1.5px solid #E3DED2;
  background: var(--surface); font-weight: 600; font-size: 17px; cursor: pointer; }
.rvm-btns button.on { background: var(--brand); border-color: var(--brand-dark); color: var(--surface); }
.rvm-caps { color: #93907F; font-size: 11px; margin-left: 6px; }
.rvm-lbl { font-weight: 700; font-size: 13px; margin: 14px 0 4px; }
#rvm-text { width: 100%; box-sizing: border-box; font: inherit; font-size: 16px; }
.rvm-promo { background: var(--brand-light); border-radius: 12px; padding: 10px 12px; margin-top: 10px;
  font-weight: 700; font-size: 14px; }

/* Комментарий к записи виден прямо в списке дня (в него пишут номер шкафчика и залог за ключ).
   Отдельная строка под записью — в саму строку его класть нельзя, там grid по --day-cols. */
.bk-note { margin: -2px 0 6px 44px; padding: 3px 8px; font-size: 13px; line-height: 1.35;
  color: #43463E; background: #EFF4F0; border-left: 3px solid var(--brand); border-radius: 0 8px 8px 0;
  cursor: pointer; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
@media (max-width: 900px) { .bk-note { margin-left: 8px; font-size: 12px; } }
@media (max-width: 900px) {
  /* Монтажные ночи на телефоне: «висят» под ночью, не справа; итог человека — новой строкой. */
  .in-grid { grid-template-columns: minmax(0, 1fr); }
  .in-person b { min-width: 0; width: 100%; }
  .in-total { margin-left: 0; width: 100%; }
  .in-seg { white-space: normal; }
}

/* ---------- 03.09: сетка «Комнаты × время» (запись праздников по телефону) ---------- */
.rooms-wrap { margin-top: 8px; }
.rooms-grid { display: grid; grid-template-columns: 190px 1fr; gap: 6px 0; align-items: center; }
.rm-label { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 14px; padding: 6px 10px; }
.rm-label.rm-head { font-size: 12px; color: #63665D; }
.rm-dot { width: 12px; height: 12px; border-radius: 50%; flex: none; }
.rm-row { position: relative; height: 52px; background: var(--surface); border: 1px solid var(--line); border-radius: 14px; overflow: hidden; cursor: cell; }
.rm-row.rm-rent { background: #FAF7F0; cursor: default; }
.rm-row > i { position: absolute; top: 0; bottom: 0; border-left: 1px dashed var(--line); }
.rm-row > i.rm-now { border-left: 2px solid var(--red); z-index: 3; }
.rm-row.rm-head { height: 20px; background: transparent; border: none; cursor: default; overflow: visible; }
.rm-row.rm-head span { position: absolute; top: 0; transform: translateX(-50%); font-size: 12px; color: #63665D; font-weight: 700; }
.rm-bar { position: absolute; top: 5px; bottom: 5px; z-index: 2; border-radius: 12px; padding: 3px 8px; font-size: 12px; line-height: 1.2;
  overflow: hidden; white-space: nowrap; cursor: pointer; border: 1px solid rgba(0,0,0,.08); display: flex; flex-direction: column; justify-content: center; }
.rm-bar b, .rm-bar span { overflow: hidden; text-overflow: ellipsis; }
.rm-bar.st-new { background: #EDE9DF; }
.rm-bar.st-confirmed { background: #E3E8EF; }
.rm-bar.st-arrived { background: #E4EDE6; }
.rm-bar.st-done { background: #E3DED2; color: #93907F; }
.rm-clean, .rm-legend-clean { background: repeating-linear-gradient(135deg, #E3DED2 0 4px, var(--surface) 4px 8px); }
.rm-blocked, .rm-legend-blocked { background: repeating-linear-gradient(135deg, #EBCFC8 0 5px, #F7E6E2 5px 10px); }
.rm-blocked { position: absolute; top: 0; bottom: 0; z-index: 1; cursor: not-allowed; border-left: 2px solid var(--red); border-right: 2px solid var(--red); }
.rm-window { position: absolute; top: 3px; bottom: 3px; z-index: 1; border: 2px dashed var(--ok); border-radius: 12px; background: rgba(47,107,79,.07); cursor: pointer; }
.rm-window span { position: absolute; right: 4px; bottom: 2px; font-size: 11px; font-weight: 600; color: var(--ok); }
.rm-legend-window { border: 2px dashed var(--ok) !important; background: rgba(47,107,79,.07); }
.rm-window.pin, .rm-legend-window.pin { border-color: #8A5B10 !important; background: rgba(138,91,16,.08); }
.rm-window.pin span { color: #8A5B10; }
.rm-clean.short { background: repeating-linear-gradient(135deg, #E6D3AE 0 4px, var(--surface) 4px 8px); }
.rm-clean { position: absolute; top: 5px; bottom: 5px; z-index: 1; border-radius: 0 8px 8px 0; }
.rm-legend { grid-column: 1 / -1; font-size: 12px; color: #63665D; display: flex; gap: 14px; flex-wrap: wrap; padding: 6px 10px; }
.rm-legend b { display: inline-block; width: 14px; height: 14px; border-radius: 6px; vertical-align: -3px; margin-right: 4px; border: 1px solid rgba(0,0,0,.08); }
#btn-rooms.on { background: var(--brand); color: var(--surface); }
@media (max-width: 900px) {
  /* Сетке нужна ширина: 12 часов в 250px не читаются. Скроллится сама, страница — нет. */
  .rooms-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .rooms-grid { min-width: 760px; grid-template-columns: 130px 1fr; }
  .rm-label { font-size: 12px; padding: 4px 6px; }
}

/* 07.09: вкладки раздела ⚙ Настройки (группы блоков по заголовкам, см. settingsTabs в app.js) */
.st-tabs { display: flex; flex-wrap: wrap; gap: 6px; position: sticky; top: 0; z-index: 2; background: var(--surface); padding: 6px 0 10px; border-bottom: 1px solid #E3DED2; margin-bottom: 8px; }
.st-tab { border: 1px solid #D9D3C6; background: #F1EEE6; border-radius: 999px; padding: 6px 12px; font-size: 14px; cursor: pointer; }
.st-tab.active { background: #2F3B33; color: var(--surface); border-color: #2F3B33; }
.st-hide { display: none !important; }
@media (max-width: 600px) { .st-tab { font-size: 13px; padding: 6px 10px; } }

/* 08.09 (аудит, этап 3): общая форма askForm() вместо системных prompt() для денег, ставок,
   процентов и остатков. Узкая — в ней одно-три поля. ⚠ Только на широком экране: класс
   сильнее мобильного `dialog { width: 100vw }`, и на телефоне лист прижимался к левому краю. */
@media (min-width: 901px) { .dlg-ask { width: min(460px, 94vw); } }
.dlg-ask .form label { min-width: 0; }
.dlg-ask .form input, .dlg-ask .form select, .dlg-ask .form textarea { font-size: 16px; }
.dlg-ask textarea { padding: 8px 10px; border: 1px solid var(--border); border-radius: 12px; font-family: inherit; resize: vertical; }
.ask-num { display: flex; align-items: center; gap: 8px; }
.ask-num input { flex: 1 1 auto; min-width: 0; }
.ask-num b { flex: 0 0 auto; color: #63665D; font-size: 13px; }
.ask-hint { font-weight: 400; color: #63665D; font-size: 12px; line-height: 1.35; }

/* 09.09 (продукт): экран «⚙ Тенант» — профиль площадки и реквизиты (index.html, .tn-only;
   логика — tenantSettings() в конце app.js). Только новые классы, чужие правила не трогаем. */
.tn-field { display: flex; flex-direction: column; gap: 4px; font-size: 12px; font-weight: 700; flex: 1; min-width: 130px; } /* как .form label, но div — внутри свои label-чипы */
.tn-sub { font-weight: 400; color: #63665D; font-size: 12px; line-height: 1.35; }             /* подсказка под полем: «сейчас +03:00», «белорусский рубль» */
.tn-week { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
.tn-week label.radio { border: 1px solid #D9D3C6; border-radius: 999px; padding: 6px 10px; background: #F1EEE6; cursor: pointer; font-size: 13px; user-select: none; }
.tn-week label.radio:has(input:checked) { background: #2F3B33; color: var(--surface); border-color: #2F3B33; } /* выбранный день — как активная вкладка ⚙ */
.tn-actions { align-items: center; }
.tn-status { font-size: 13px; font-weight: 700; color: #245640; }
.tn-status.bad { color: var(--red); }
@media (max-width: 900px) {
  /* Телефон: чипы дней под палец (44 px), поле — на всю ширину; шрифт 16px даёт общее правило выше */
  .tn-field { min-width: 100%; font-size: 13px; }
  /* ⚠ Селектор с #dlg-settings: правило 16.08 «#dlg-settings label.radio { flex: 1 1 100% }» растягивает
     чекбоксы настроек на всю строку (у них длинные подписи). Дни недели — короткие чипы, им нужна строка. */
  #dlg-settings .tn-week label.radio { flex: 0 0 auto; min-height: 44px; display: inline-flex; align-items: center; padding: 6px 12px; }
  .tn-actions { flex-direction: column; align-items: stretch; }
  .tn-actions .btn { width: 100%; min-height: 44px; }
}

/* 09.09 (продукт): «Ключи агента (MCP)» — подблок вкладки «⚙ Тенант» (index.html, классы .ak-*;
   логика — блок agentKeys в конце app.js). Только новые классы, чужие правила не трогаем. */
.ak-head { font-size: 15px; font-weight: 600; margin-top: 18px; padding-top: 14px; border-top: 1px dashed #D9D3C6; }
.ak-issue { align-items: flex-end; }
.ak-issue .btn { flex: 0 0 auto; }
.ak-fresh { border: 2px solid #8A5B10; background: #F6EBD8; border-radius: 14px; padding: 10px 12px; margin: 6px 0 10px; display: flex; flex-direction: column; gap: 6px; }
.ak-fresh-title { font-weight: 600; color: #8A5B10; }
.ak-fresh-row { display: flex; gap: 8px; align-items: center; }
.ak-fresh-row input { flex: 1 1 auto; min-width: 0; font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 14px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 12px; }
.ak-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.ak-row { display: grid; grid-template-columns: 1fr auto; gap: 2px 12px; align-items: center; border: 1px solid var(--border); border-radius: 14px; padding: 8px 12px; }
.ak-row .ak-main { grid-column: 1; min-width: 0; overflow-wrap: anywhere; }
.ak-row .ak-meta { grid-column: 1; font-size: 12px; color: #63665D; }
.ak-row .ak-revoke { grid-column: 2; grid-row: 1 / span 2; }
.ak-main code { background: #F1EEE6; border-radius: 8px; padding: 1px 6px; margin: 0 2px 0 6px; }
.ak-scope { font-size: 12px; color: #245640; background: #E4EDE6; border-radius: 999px; padding: 1px 8px; margin-left: 4px; }
.ak-empty { color: #63665D; font-size: 13px; padding: 6px 0; }
.ak-empty.bad { color: var(--red); }
@media (max-width: 900px) {
  /* Телефон: поле и кнопки на всю ширину, под палец (44 px); строка ключа — в один столбец */
  .ak-issue { flex-direction: column; align-items: stretch; }
  .ak-issue .btn { width: 100%; min-height: 44px; }
  .ak-fresh-row { flex-direction: column; align-items: stretch; }
  .ak-fresh-row .btn { min-height: 44px; }
  .ak-row { grid-template-columns: 1fr; }
  .ak-row .ak-revoke { grid-column: 1; grid-row: auto; min-height: 44px; margin-top: 4px; }
}

/* 09.09 (продукт): «Подключённые приложения (OAuth)» — подблок вкладки «⚙ Тенант» под ключами
   агента (index.html, #oc-list; логика — блок oauthClients в конце app.js). Строки переиспользуют .ak-row;
   свои классы — только бейдж «доступ истёк» и приглушение такой строки. Чужие правила не трогаем. */
.oc-badge { font-size: 12px; color: #8A5B10; background: #F6EBD8; border: 1px solid #E6D3AE; border-radius: 999px; padding: 1px 8px; margin-left: 6px; }
.oc-row.oc-stale .ak-main b { color: #63665D; }

/* 09.09 (продукт, пакет «Детский центр»): раздел «🎨 Занятия» — расписание на неделю,
   карточка занятия с отметкой посещаемости, группы, абонементы, справочники.
   Разметка — #dlg-classes и #dlg-cl-session в конце index.html, логика — блок «ЗАНЯТИЯ»
   в конце app.js. Только новые классы .cl-*, чужие правила не трогаем.
   ⚠ Экран отметки посещаемости считается по ТЕЛЕФОНУ педагога: это главный сценарий раздела.
   Поэтому кнопки «был / не был / уваж.» на узком экране растягиваются на всю ширину и держат
   44 px высоты — промах пальцем здесь стоит ребёнку занятия. */
.cl-navrow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 10px 0; }
.cl-week { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px; align-items: start; }
.cl-day { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.cl-dhead { font-weight: 600; font-size: 13px; padding: 4px 6px; background: var(--grey); border-radius: 12px; text-align: center; }
.cl-day.cl-now .cl-dhead { background: var(--brand-light); }
.cl-empty { color: #A5A294; text-align: center; font-size: 12px; padding: 4px 0; }
.cl-ses { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; text-align: left;
  border: 1px solid var(--border); border-left: 4px solid var(--teal); border-radius: 14px;
  background: var(--surface); padding: 8px 10px; cursor: pointer; font: inherit; width: 100%; }
.cl-ses:hover { background: var(--teal-light); }
.cl-ses.cl-held { border-left-color: var(--green); }
.cl-ses.cl-off { border-left-color: var(--red); opacity: .65; }
.cl-time { font-weight: 600; }
.cl-prog { font-weight: 700; overflow-wrap: anywhere; }
.cl-meta { font-size: 12px; color: #63665D; overflow-wrap: anywhere; }
.cl-warn { color: var(--red); font-weight: 700; margin: 4px 0; }
.cl-shead { color: #43463E; margin: 4px 0 10px; }
/* Список детей на занятии */
.cl-kids { display: flex; flex-direction: column; gap: 8px; }
.cl-kid { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 6px 12px; align-items: center;
  border: 1px solid var(--border); border-radius: 14px; padding: 10px 12px; background: var(--surface); }
.cl-kname { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cl-pass { font-size: 12px; color: var(--teal); font-weight: 700; }
.cl-pass.cl-nopass { color: #8A5B10; }
.cl-mks { display: flex; gap: 6px; }
.cl-mk { border: 1px solid var(--border); background: var(--surface); border-radius: 999px; padding: 8px 14px;
  font: inherit; font-size: 14px; cursor: pointer; min-height: 40px; }
.cl-mk:disabled { opacity: .5; cursor: default; }
.cl-mk-present.on { background: var(--green); border-color: var(--green); color: var(--surface); font-weight: 600; }
.cl-mk-absent.on { background: var(--red); border-color: var(--red); color: var(--surface); font-weight: 600; }
.cl-mk-excused.on { background: var(--teal); border-color: var(--teal); color: var(--surface); font-weight: 600; }
/* Списки групп, детей, абонементов */
.cl-list { display: flex; flex-direction: column; gap: 6px; margin: 10px 0; }
.cl-grow { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; text-align: left;
  border: 1px solid var(--border); border-radius: 14px; background: var(--surface); padding: 10px 12px;
  font: inherit; cursor: pointer; width: 100%; }
.cl-grow:hover { background: var(--teal-light); }
.cl-grow.cl-static { cursor: default; }
.cl-grow.cl-static:hover { background: var(--surface); }
.cl-genrow { align-items: flex-end; margin: 10px 0; }
.cl-sell { border: 1px dashed var(--border); border-radius: 12px; padding: 10px 14px; margin: 14px 0; }
.cl-days { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 6px; }
.cl-day-chk { display: inline-flex; align-items: center; gap: 4px; }
.cl-sacts { gap: 8px; flex-wrap: wrap; }

@media (max-width: 900px) {
  /* Телефон: неделя — не сетка из семи колонок (в 375 px они шириной в палец), а список по дням. */
  .cl-week { grid-template-columns: 1fr; gap: 12px; }
  .cl-dhead { text-align: left; }
  /* Отметка посещаемости: ребёнок сверху, три кнопки во всю ширину под ним. Это единственный
     экран раздела, который сотрудник открывает стоя, — цель кнопки должна быть крупной. */
  .cl-kid { grid-template-columns: 1fr; }
  .cl-mks { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
  .cl-mk { min-height: 44px; padding: 10px 6px; }
  .cl-genrow { flex-direction: column; align-items: stretch; }
  .cl-genrow .btn { width: 100%; min-height: 44px; }
  /* 16 px минимум: меньше — и iOS зумит страницу при фокусе и обратно не возвращает (грабля 8). */
  .cl-genrow input, .cl-genrow select { font-size: 16px; }
  .cl-sacts .btn { width: 100%; min-height: 44px; }
}

/* 09.09 (продукт, слой ЗАПИСИ): права ключа агента (.ak-scopes-*) и «Журнал действий
   агентов» (.mo-*) — подблоки вкладки «⚙ Тенант» (index.html, логика в конце app.js).
   Только новые классы; строки ключей и приложений переиспользуют .ak-row выше. */
.ak-scopes-box { margin: 6px 0 10px; }
.ak-scopes-title { font-size: 13px; font-weight: 600; margin-bottom: 6px; }
.ak-scopes { display: flex; flex-direction: column; gap: 4px; }
.ak-scopes label.radio { border: 1px solid #E3DED2; border-radius: 12px; padding: 6px 10px; background: #FAF7F0; font-size: 13px; font-weight: 700; cursor: pointer; }
.ak-scopes label.radio:has(input:checked) { border-color: #2F3B33; background: var(--brand-light); }
.ak-scopes label.ak-scope-read { cursor: default; opacity: .75; }
.ak-sc-hint { font-weight: 400; color: #63665D; }
/* Право на изменение видно с первого взгляда: чтение — зелёное, запись — оранжевая */
.ak-scope-w { color: #8A5B10; background: #F6EBD8; }
.mo-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.mo-row { display: grid; grid-template-columns: 1fr auto; gap: 2px 12px; align-items: center; border: 1px solid var(--border); border-radius: 14px; padding: 8px 12px; }
.mo-row .mo-main { grid-column: 1; min-width: 0; overflow-wrap: anywhere; }
.mo-row .mo-meta { grid-column: 1; font-size: 12px; color: #63665D; }
.mo-row .mo-undo { grid-column: 2; grid-row: 1 / span 2; }
.mo-row .mo-hint { grid-column: 2; grid-row: 1 / span 2; max-width: 260px; font-size: 12px; color: #93907F; text-align: right; }
.mo-badge { color: #63665D; }
.mo-row.mo-undone .mo-main b { color: #93907F; text-decoration: line-through; }
@media (max-width: 900px) {
  /* Телефон: одна колонка, кнопка под палец (44 px), подсказка под текстом операции */
  .mo-row { grid-template-columns: 1fr; }
  .mo-row .mo-undo { grid-column: 1; grid-row: auto; min-height: 44px; margin-top: 4px; }
  .mo-row .mo-hint { grid-column: 1; grid-row: auto; max-width: none; text-align: left; margin-top: 2px; }
  /* ⚠ Именно block, а не flex: во flex-строке подпись и пояснение становятся отдельными
     колонками, и на 375 «Смены — Смены и часы сотрудников» разъезжается в две колонки.
     Блок даёт обычный перенос текста, а padding держит высоту под палец (44 px). */
  #dlg-settings .ak-scopes label.radio { flex: 1 1 100%; display: block; padding: 11px 12px; }
}

/* 09.09 (продукт): ЦЕНОВЫЕ ЗОНЫ — вкладка ⚙ «🗺 Зоны». Разметка — .pz-* в index.html,
   логика — блок «ЦЕНОВЫЕ ЗОНЫ» в конце app.js. Строка зоны и строка площадки свёрстаны одинаково
   (как .ak-row): слева описание, справа действия — так глазами видно, что это один список. */
.pz-list, .pz-arenas { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.pz-row { display: flex; align-items: center; gap: 8px; border: 1px solid var(--border); border-radius: 14px; padding: 8px 12px; }
.pz-row .pz-main { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.pz-row .pz-sub { display: block; font-size: 12px; color: #63665D; }
.pz-row.inactive { opacity: .6; }
.pz-row code { background: #F1EEE6; border-radius: 8px; padding: 1px 6px; }
.pz-row .pz-zone { flex: 0 0 auto; max-width: 45%; }
/* Кнопка «Выключить» у зоны с площадками недоступна: показываем это, иначе человек жмёт её
   и не понимает, почему ничего не происходит (подсказка «почему» — в title кнопки). */
.pz-row .btn[disabled] { opacity: .45; cursor: not-allowed; }
.pz-head { font-size: 15px; font-weight: 600; margin-top: 18px; padding-top: 14px; border-top: 1px dashed #D9D3C6; }
@media (max-width: 900px) {
  /* Телефон: одна колонка, кнопки под палец (44 px) — иначе «Переименовать» и «Выключить»
     сжимаются в две узкие полоски и промахнуться мимо них проще, чем попасть. */
  .pz-row { flex-wrap: wrap; }
  .pz-row .pz-main { flex: 1 1 100%; }
  .pz-row .btn, .pz-row .pz-zone { flex: 1 1 auto; min-height: 44px; max-width: none; }
}

/* 09.09 (ДВА БРЕНДА): подпись продукта на экране входа (.login-product) и подблок «🏷 О продукте»
   вкладки ⚙ «Тенант» (index.html, поля #pd-*; логика — блок в конце app.js).
   ⚠ Бренд площадки на входе остаётся главным — логотип и название вверху карточки; имя продукта
   идёт мелкой серой строкой внизу и не должно с ним спорить. Гостевые экраны этой подписи не
   имеют вовсе: гость видит площадку, а не то, на чём она работает.
   Своих классов у полей блока нет — они переиспользуют .row/.tn-sub/.tn-status профиля площадки,
   поэтому и на 1440, и на 375 ведут себя так же, как поля выше. */
.login-product { text-align: center; font-size: 12px; color: #93907F; margin-top: 2px; }
@media (max-width: 900px) {
  /* Телефон: карточка входа сжата, подпись не должна отъедать высоту у кнопки «Войти по PIN» */
  .login-product { font-size: 11px; margin-top: 0; }
  /* ⚠ Подписи полей блока короткие («Имя продукта», «Сайт продукта»), поэтому на 375 строка
     НЕ переносится сама, как переносятся соседние поля с длинными подписями, — и в половину
     ширины не влезает подсказка «задаётся при установке». Ставим по одному полю в строку. */
  #dlg-settings .row > label:has(#pd-name), #dlg-settings .row > label:has(#pd-site) { min-width: 100%; }
}
