/* Атрибут 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;

  /* ⚠ 11.09 (решение владельца): оформление — настройка площадки. Ниже значения темы «Хвоя»
     по умолчанию; тема площадки переопределяет их вставкой <style id="tenant-theme"> в страницу
     (src/theme.js). Держать в согласии с PRESETS.hvoya — за этим следит test/theme.mjs.
     Роли разведены намеренно: у первой площадки жёлтый был ЗАЛИВКОЙ, а бирюзовый — ТЕКСТОМ,
     и с одним --brand на оба её подписи стали бы жёлтым текстом по белому. */
  --on-brand:      #FFFDF9;
  --accent-ink:    #2F6B4F;
  --accent-tint:   #E4EDE6;
  --topbar-bg:     #FFFDF9;
  --topbar-ink:    #1F211D;
  --topbar-line:   #E3DED2;
  --topbar-btn-bg: #FFFDF9;
  --topbar-hover:  #C9C2B0;
  --hour-bg:       #E4EDE6;
  --hour-ink:      #1F211D;
  --hour-muted:    #63665D;
  --hour-warn:     #8A5B10;
  --hour-full:     #A8412F;
  --font:          'Golos Text', system-ui, sans-serif;

  --yellow: var(--brand);
  --black: var(--ink);
  --teal: var(--accent-ink);
  --teal-light: var(--accent-tint);
  --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: var(--font);
  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(--topbar-bg); color: var(--topbar-ink); border-bottom: 1px solid var(--topbar-line);
  position: sticky; top: 0; z-index: 5;
  flex-wrap: wrap; /* при узком окне/крупном масштабе кнопки переносятся, ничего не обрезается */
}
/* (Отменено 11.09 — меню по темам, см. ниже.) 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(--topbar-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(--topbar-line); background: var(--topbar-btn-bg); color: var(--topbar-ink);
  font-family: inherit; font-weight: 600; }
/* ⚠ 12.09 (владелец: «дату где выбираешь, непонятно, что нажать, чтобы календарь открыть»). В тёмной шапке браузер
   рисовал значок календаря тёмным на тёмном. Теперь поле целиком открывает календарь, а в тёмной шапке у поля тёмная
   схема и значок светлый (app.js, по цвету шапки). */
.topbar input[type="date"] { cursor: pointer; }
.topbar input[type="date"]::-webkit-calendar-picker-indicator { opacity: 1; cursor: pointer; }
.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(--topbar-line); }
/* ⚠ 11.09 (владелец: «на главной у руководителей слишком хаотично открыты все пункты в шапке»; «да» — меню
   по темам, отменяет решение 27.07 выше). У владельца было 40 кнопок в 4 ряда. Кнопки те же — только разложены
   по выпадающим меню; каждодневное (касса, операции, предоплаты, отметка смены) остаётся на виду. Пустое меню
   у должности скрывается, меню из одного пункта показывает сам пункт (классы ставит app.js). */
.nav-grp { position: relative; display: flex; align-items: center; }
.nav-menu { display: none; position: absolute; top: calc(100% + 6px); left: 0; z-index: 40; min-width: 220px;
  flex-direction: column; align-items: stretch; gap: 4px; padding: 8px; border-radius: 16px;
  background: var(--topbar-bg); border: 1px solid var(--topbar-line); box-shadow: 0 12px 32px rgba(0,0,0,.35); }
.nav-menu-right { left: auto; right: 0; }
.nav-menu-flip { left: auto; right: 0; }   /* меню у правого края экрана разворачивается влево (app.js) */
.nav-grp.open > .nav-menu { display: flex; }
.nav-menu > .btn { text-align: left; white-space: nowrap; }
.nav-menu > [hidden] { display: none !important; }
.nav-grp--empty { display: none; }
.nav-grp--single > .nav-grp-btn { display: none; }
.nav-grp--single > .nav-menu { display: contents; }
.nav-grp-btn[aria-expanded="true"] { border-color: var(--topbar-hover); }

/* Кнопки и поля */
.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(--on-brand); }
.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(--topbar-ink); border-color: var(--topbar-line); background: var(--topbar-btn-bg); }
.topbar .btn-ghost:hover { border-color: var(--topbar-hover); }
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(--hour-bg); color: var(--hour-ink); font-weight: 600;
}
.hour-head .load { margin-left: auto; font-size: 13px; font-weight: 500; color: var(--hour-muted); }
.hour-head .load.warn { color: var(--hour-warn); }
.hour-head .load.full { color: var(--hour-full); }
.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; }
/* 06.10: незакрытая смена более раннего дня — заметнее справки, но не красная ошибка */
.e-warn { font-size: 14px; background: #FFF4D6; border: 1px solid #F2C94C; border-radius: 14px; padding: 10px 14px; line-height: 1.5; }
.e-warn .btn { margin-top: 6px; }
/* 06.10: пробная печать — ответ кассы по каждой кассе: причина словами и кнопка, которая её убирает */
.cpx-rows { display: grid; gap: 6px; margin-top: 8px; font-size: 14px; font-weight: 400; line-height: 1.45; }
.cpx-row { background: var(--surface); border: 1px solid var(--line); border-radius: 10px; padding: 6px 10px; overflow-wrap: anywhere; }
.cpx-row.bad { background: var(--warn-bg); border-color: #E7CFA3; }
.cpx-row .btn { display: block; margin-top: 6px; white-space: normal; text-align: left; max-width: 100%; }
.cpx-why { margin-top: 6px; font-size: 13px; font-weight: 400; color: var(--warn); }
.cpx-pick { display: flex; flex-wrap: wrap; gap: 6px 16px; align-items: center; margin-top: 8px; font-size: 14px; }
/* сильнее .form label (колонка, flex:1, min-width 130px) — иначе галочка встаёт над именем кассы */
.form .cpx-pick label { display: flex; flex-direction: row; gap: 6px; align-items: center; margin: 0; flex: 0 1 auto; min-width: 0; font-size: 14px; font-weight: 600; }
.form .cpx-pick input { width: auto; margin: 0; }
/* 26.09: две большие кнопки способа оплаты — без выбора оплату не провести */
.p-quick { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 10px 0 4px; }
.p-quick-btn { font-size: 17px; padding: 14px 10px; border-width: 2px; }
.p-quick-btn.on { background: var(--brand); color: var(--on-brand); border-color: var(--brand-dark, var(--brand)); }
#btn-p-save:disabled { opacity: .45; cursor: not-allowed; }
.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(--on-brand); }
.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; }
/* ⚠ 20.09 (проверка окон на 375 и 1440). Перенос кнопок — в БАЗОВОМ правиле, а не только
   в мобильном media-запросе (строка 660). Было так: до 900 px ряд переносился, шире — никогда,
   и на 375 всё держалось на одном этом правиле плюс точечные заплатки (`#dlg-bar .actions`).
   Ряд кнопок в модалке при нехватке места ломается особенно неприятно: из-за
   justify-content:flex-end он вылезает ВЛЕВО, и обрезается ПЕРВАЯ кнопка, а не последняя.
   Пока кнопки помещаются в строку, перенос не меняет ни одного окна — проверено на всех 72. */
.actions { display: flex; flex-wrap: wrap; 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; }
/* ⚠ 12.09 (владелец: «странное сочетание цветов на надписи „суббота“»). Было светлый оттенок фирменного цвета фоном
   и тёмный — текстом: у жёлтой темы это бледно-жёлтое на светло-жёлтом. Фирменный цвет и цвет текста на нём тема
   держит контрастными (--on-brand, не ниже 4,5:1). */
.day-badge.holiday { background: var(--brand); color: var(--on-brand); }
.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-calc .fc-unknown { color: #63665D; }   /* 09.10: строка «прошлый месяц», когда CRM не знает, выплачено ли — в итог не входит */
.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; }
/* 05.10: учёт склада — директору арены и выше; у администратора вместо числа только «на исходе» */
.st-nobook:empty { display: none; }
.st-nobook.low { color: var(--red); font-weight: 900; white-space: nowrap; }

/* Имя арены в заголовке окна: не «украшение», а ответ на вопрос «куда я сейчас ввожу». */
.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(--topbar-bg); border: 1px solid var(--topbar-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; }
  /* ⚠ 11.09 (меню по темам): в ☰ те же группы, что на широком экране, — подзаголовками вместо выпадающих
     списков. Сверху каждодневное (касса, операции, предоплаты, отметка смены), ниже темы, «мой профиль» —
     последним. Кнопка группы здесь не нужна: содержимое меню встаёт прямо в сетку. Старые ручные order у
     отдельных кнопок и разделители .grp убраны вместе с разметкой, которой больше нет. */
  #topnav .nav-grp { display: contents; }
  #topnav .nav-grp-btn { display: none; }
  #topnav .nav-menu { position: static; display: grid; grid-template-columns: 1fr 1fr; grid-column: 1 / -1;
    gap: 8px; min-width: 0; padding: 8px 0 0; border: 0; border-top: 1px solid var(--topbar-line);
    border-radius: 0; box-shadow: none; background: none; order: 20; }
  #topnav .nav-menu::before { content: attr(data-title); grid-column: 1 / -1; font-size: 12px; font-weight: 800;
    letter-spacing: .04em; text-transform: uppercase; opacity: .7; }
  #topnav .nav-grp--empty .nav-menu { display: none; }
  #topnav .nav-menu > .btn { text-align: center; white-space: normal; }
  #topnav .nav-menu > a.btn { display: flex; align-items: center; justify-content: center; }   /* ссылка — как кнопка */
  #btn-cash, #btn-ops, #btn-prepays, #btn-myshift, #btn-mystint, #btn-itasks-hdr { order: 10; }
  #topnav .whoami .nav-menu { order: 50; }

  /* Шапка в две строки: [марка] [арена] [☰] / [дата] [+ Запись].
     Горизонтальный логотип-надпись шириной ~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; }
  /* ⚠ 20.09. В ряду кнопок бывает не только кнопка: в «Услугах ДР» первым лежит пояснение
     («Убрать услугу — „Выключить“ в списке…»). На телефоне оно делило строку с двумя кнопками,
     сжималось до 103 px — узкий столбик по слову в строке — и растягивало весь ряд до 142 px
     в высоту. Пояснению — своя строка, кнопки под ним. `!important` тут вынужденный: ширина
     задана ИНЛАЙНОВЫМ `style="flex:1"` прямо в разметке, а инлайн обычным правилом не перебить;
     как и соседние такие случаи, делаем это только внутри мобильного запроса. */
  .actions > .hint { flex: 1 1 100% !important; }
  .svc-grid, .denom-grid, .bar-grid { grid-template-columns: 1fr 1fr; }
  .bar-grid { max-height: none; }
  .h-list, .e-log { max-height: none; }
  /* ⚠ 12.09 (аудит вёрстки): у части списков высота задана прямо в разметке (style="max-height:…vh"), и правило
     выше её не перебивало — на телефоне «Акции» и «Рулетка» оставались окошком с прокруткой внутри прокрутки. */
  .h-list[style*="max-height"], .e-log[style*="max-height"] { max-height: none !important; }
  .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; }
  /* ⚠ 20.09 (жалоба владельца: «название наезжает на кнопку „цена“»). Оборотная сторона
     `min-width: 0` строкой выше: название может сжаться УЖЕ своего самого длинного слова,
     а слово не переносится и рисуется ЗА рамкой — поверх соседней кнопки. Рамки при этом
     не пересекаются, поэтому обычная проверка переполнения такое не видит; поймалось замером
     самого текста (Range.getClientRects): 6 услуг из 12, нахлёст до 32 px. В «Услугах ДР»
     колонка названия на телефоне узкая (три кнопки в строке), поэтому именно там. Лечим точечно:
     название занимает строку целиком, кнопки встают под ним. `!important` — против инлайнового
     `style="flex:1"` в разметке. Остальные списки (склад, сотрудники, операции, сертификаты)
     проверены замером — там нахлёста нет, их не трогаем. */
  #svc-list .h-row > span { flex: 1 1 100% !important; }
  .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; } }
/* ⚠ 12.09 (аудит вёрстки): меню «⋯» в строках «Склада», «Сотрудников» и «Акций» (app.js, rowMore). Ставится
   position:fixed по координатам кнопки: список с прокруткой (.h-list) обрезал бы меню, прижатое к строке. */
.row-more { display: inline-flex; }
.row-more-menu { display: none; }
.row-more.open > .row-more-menu {
  display: flex; flex-direction: column; gap: 4px; position: fixed; z-index: 60;
  min-width: 210px; max-width: calc(100vw - 16px); max-height: calc(100dvh - 16px); overflow: auto;
  padding: 6px; background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, .18); cursor: default;
}
.row-more-menu > .btn { display: flex; align-items: center; justify-content: flex-start; text-align: left; width: 100%; white-space: nowrap; }
.row-more-sep { display: block; border-top: 1px solid var(--border); margin: 4px 2px; }
.row-more-btn { font-weight: 900; min-width: 38px; }
.row-more-btn[aria-expanded="true"] { border-color: var(--yellow); }
/* Строка акции: текст слева, «✏» и «⋯» справа — раньше кнопки шли прямо в тексте и переносились вперемешку с ним. */
.dc-row { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.dc-who { flex: 1 1 auto; min-width: 0; }
.dc-acts { display: flex; gap: 6px; align-items: center; flex: 0 0 auto; }

/* Разбивка продаж бара по группам (просьба управляющей площадкой 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); }
/* 17.09 (видео управляющей площадки: «сделать график полностью видным»). Само окно <dialog> было 680px, и на любом
   экране график показывал 8–9 дней из 31 — остальное прокруткой вбок. Окну графика даём ширину под весь месяц. */
#dlg-schedule { width: min(96vw, 1180px); }
/* 17.09 (видео управляющей площадки): сетка — единственное, что прокручивается, поэтому шапка с числами и
   имена всегда на экране; клетки ужаты, чтобы месяц на 20 человек помещался на ноутбуке целиком. */
.sc-wrap { overflow: auto; min-width: 0; max-height: max(240px, calc(100vh - 250px)); border: 1px solid var(--line, var(--line)); border-radius: 12px; }
.sc-tbl { border-collapse: separate; border-spacing: 0; font-size: 12.5px; }
.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: 140px; max-width: 170px;
  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: 26px; height: 24px; text-align: center; cursor: pointer; user-select: none; }
/* 17.09: столбец дня ровно 26px и в шапке, и в клетках — иначе шапка (число + отступы) раздувала столбец до 34px,
   и 31 день не помещался на ноутбуке 1366. Ширина фиксирована, лишнее в шапке не рисуем. */
.sc-tbl thead th:not(.sc-name):not(.sc-total) { width: 26px; min-width: 26px; max-width: 26px; padding: 4px 0; box-sizing: border-box; font-size: 12px; }
.sc-tbl td.sc-cell, .sc-tbl td.sc-sumcell { width: 26px; min-width: 26px; max-width: 26px; box-sizing: border-box; }
.sc-sumcell { font-weight: 700; background: #FAF7F0; text-align: center; }   /* без min-width: иначе столбец дня раздувается */
.sc-painting .sc-cell { cursor: cell; }
.sc-none { background: #F0EEE8; }
.sc-brush { border: 1px solid var(--line); background: var(--surface); border-radius: 999px; padding: 3px 10px 3px 4px;
  font: inherit; font-size: 13px; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; color: var(--ink); }
.sc-brush.on { border-color: var(--brand); box-shadow: 0 0 0 2px var(--brand-light); font-weight: 700; }
/* 06.10: должность в графике — метка в клетке («Т» — как тренер, «А» — как админ) */
.sc-as { font-size: 9px; font-weight: 700; margin-left: 1px; vertical-align: super; line-height: 0; }
.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: var(--font); 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;
}
/* ⚠ 12.09 (аудит вёрстки): правило выше делает липкими ВСЕ панели окна, и в «Сотрудниках» их три — «Добавить»,
   панель разделов и «Закрыть». Они ложились друг на друга: на 1440 панель разделов закрывала форму «Добавить
   сотрудника», на телефоне занимала полэкрана. К низу прилипает только «Закрыть»; панели внутри окна стоят на месте. */
dialog.dlg-full .actions.actions-inline {
  position: static; z-index: auto; margin: 12px 0 0; padding: 0; border-top: 0; background: none; flex-wrap: wrap;
}
@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(--on-brand); }
.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(--on-brand); }
@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; }
/* 11.09: галочка в askForm («только документ на кассе»). `.form label` раскладывает подпись колонкой,
   и галочка стояла отдельной строкой над своим текстом. Здесь галочка слева, текст рядом, пояснение под ним. */
.dlg-ask .form label.chk { flex-direction: row; flex-wrap: wrap; align-items: flex-start; gap: 4px 10px; font-weight: 600; }
.dlg-ask .form label.chk input { flex: none; width: 20px; height: 20px; margin: 0; }
.dlg-ask .form label.chk .chk-text { flex: 1 1 0; min-width: 0; }   /* иначе на телефоне текст уезжает строкой под галочку */
.dlg-ask .form label.chk .ask-hint { flex-basis: 100%; padding-left: 30px; font-weight: 400; }
.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; }
/* 12.10: окно изъятия — сумма больше, чем в кассе по её книге: жёлтое предупреждение (не запрет) */
.ask-warn { background: #F6EBD8; border: 1px solid #E0C48A; color: #6B4A11; border-radius: 8px; padding: 8px 10px; font-size: 13px; line-height: 1.35; }
.ask-warn[hidden] { display: none; }

/* 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 выше. */
/* 12.09 (продукт): «📄 Документы для гостей» — договор и правила площадки, подблок вкладки
   «⚙ Тенант». Карточка на документ: заголовок с номером редакции, текст, ссылка, кнопка.
   min-width: 0 у полей обязателен — иначе textarea и ссылка распирают карточку на узком экране
   (та же ловушка, что у полосы цен и контактов площадки). */
.doc-card { border: 1px solid var(--border); border-radius: 14px; padding: 10px 12px; margin: 8px 0;
  display: flex; flex-direction: column; gap: 8px; }
.doc-card-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; }
.doc-card > label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; min-width: 0; }
.doc-card textarea, .doc-card input { width: 100%; min-width: 0; font-family: inherit; font-size: 14px; }
.doc-card textarea { resize: vertical; border: 1px solid var(--border); border-radius: 12px; padding: 8px 10px; }
.doc-card-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
/* Формулировки согласий стоят отдельной группой от документов: это не файл, а фраза рядом
   с галочкой, и путать их владельцу нельзя — у них разные последствия. */
.doc-group { font-size: 13px; font-weight: 600; margin-top: 14px; }
.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; }
/* flex-wrap нужен и на широком экране: у площадки есть строка контактов во всю ширину карточки
   (`.pz-contacts`). Без него она не переносится, а сжимается в один ряд с названием и зоной. */
.pz-row { display: flex; flex-wrap: wrap; 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%; }
/* 12.09: адрес и телефон площадки рядом с зоной. На узком экране строка переносится (.pz-row
   уже flex), поэтому ширины задаются долями, а не пикселями: иначе на 375 поля выезжают. */
.pz-row { flex-wrap: wrap; }
.pz-row .pz-contacts { display: flex; gap: 8px; flex: 1 1 100%; }
.pz-row .pz-contacts .pz-addr { flex: 1 1 60%; min-width: 0; }
.pz-row .pz-contacts .pz-phone { flex: 1 1 30%; min-width: 0; }
/* Кнопка «Выключить» у зоны с площадками недоступна: показываем это, иначе человек жмёт её
   и не понимает, почему ничего не происходит (подсказка «почему» — в 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; }
  /* Адрес и телефон — каждый своей строкой. Рядом они не встают: основа полей (220 + 140) шире
     карточки, и на 380 px телефон уезжал за её правый край (поймано снимком, на широком не видно). */
  .pz-row .pz-contacts .pz-addr, .pz-row .pz-contacts .pz-phone { flex: 1 1 100%; min-height: 44px; }
}

/* 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%; }
}

/* ⚠ 11.09: правила экрана «Касса ↔ CRM» пришли вливанием из CRM первой площадки и
   перекрашены под палитру продукта. Это повторяющаяся работа: каждое вливание приносит
   новый CSS в старых цветах. Пока живут две копии кода — перекрашивать придётся каждый раз. */
/* 08.09: экран «Касса ↔ CRM» — карточка на арену вместо широкой таблицы (владелец не нашёл
   переключатель режима в таблице и дважды открыл соседний список). */
.cb-card { border: 1px solid var(--line, var(--line)); border-radius: 18px; padding: 14px 16px; margin: 0 0 12px; background: var(--surface); }
.cb-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; }
.cb-arena { font-size: 17px; font-weight: 600; }
.cb-badges { display: flex; gap: 6px; flex-wrap: wrap; }
.cb-badge { border-radius: 999px; padding: 3px 12px; font-size: 13px; font-weight: 600; white-space: nowrap; }
.cb-sub { font-size: 12px; color: var(--muted); margin: 4px 0 10px; }
.cb-block { border-top: 1px solid #F0F0F0; padding-top: 10px; margin-top: 10px; }
.cb-label { font-size: 12px; text-transform: uppercase; letter-spacing: .5px; color: #777; margin-bottom: 6px; }
.cb-modes { display: flex; gap: 8px; flex-wrap: wrap; }
.cb-mode { border: 1px solid #DDD; background: var(--surface); border-radius: 999px; padding: 8px 16px; font-size: 14px; font-weight: 600; cursor: pointer; }
.cb-mode:hover { background: #FAFAFA; }
.cb-mode.on { background: var(--brand-light); border-color: var(--brand-light); cursor: default; }
.cb-what { font-size: 13px; color: #444; margin-top: 8px; }
.cb-box { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 8px 0; border-bottom: 1px dashed var(--line); }
.cb-box:last-child { border-bottom: 0; }
.cb-box-name { min-width: 240px; }
.cb-box-shift { min-width: 150px; font-size: 14px; }
.cb-box-btns { display: flex; gap: 6px; margin-left: auto; flex-wrap: wrap; }
.cb-box-err { color: var(--danger); font-size: 12px; flex-basis: 100%; margin-top: 2px; }
.cb-job { border: 1px solid var(--line, var(--line)); border-radius: 18px; padding: 10px 12px; margin: 0 0 8px; }
.cb-job-top { display: flex; justify-content: space-between; gap: 8px; font-size: 12px; color: var(--muted); margin-bottom: 4px; }
@media (max-width: 600px) {
  .cb-card { padding: 12px; border-radius: 18px; }
  .cb-box-name, .cb-box-shift { min-width: 100%; }
  .cb-box-btns { margin-left: 0; }
  .cb-mode { flex: 1 1 100%; text-align: center; }
}

/* ---------- 11.09: «ℹ» в разделах — инструкция по должности из карточек базы знаний ----------
   Фирменно: жёлтая рамка-пилюля, карточки со скруглением 16px, шапка таблиц жёлтая, зебра.
   Заголовки внутри карточки — h4/h5: h3 в полноэкранном окне липнет к верху как шапка раздела. */
.btn-help {
  flex: none; width: 34px; height: 34px; padding: 0; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 17px; font-weight: 800; line-height: 1;
  background: #fff; border: 2px solid var(--yellow); color: #000;
}
/* 11.09, контраст: в теме продукта `--yellow` — это фирменный цвет площадки (у «Хвои» тёмно-зелёный), и тёмный значок
   на заливке читался плохо: 2,58:1 в шапке и 3,34:1 в окнах. На заливке значок берёт `--on-brand` — тема сама держит
   его контраст с фирменным цветом не ниже 4,5:1 (test/theme.mjs), а на жёлтой теме первой площадки он тёмный. */
.btn-help:hover, .btn-help:focus-visible { background: var(--yellow); color: var(--on-brand); }
dialog.dlg-full h3 .btn-help { margin-left: auto; }
/* 11.09: в тёмной шапке главного экрана `.topbar .btn` и `.topbar .btn-ghost` перебивали кнопку — белый значок
   на белом фоне, серая рамка, шрифт 13px. Возвращаем тот же вид, что в окнах разделов. */
/* 11.09, вторая часть: в теме продукта `.topbar .btn-ghost` даёт кнопкам шапки ТЁМНЫЙ фон — чёрный значок на нём
   снова пропадал (на слитом коде «ℹ» был пустым кольцом). Фон светлый — явно. */
.topbar .btn-help { color: var(--black); background: var(--surface, #fff); border-color: var(--yellow); font-size: 17px; padding: 0; border-radius: 999px; }
/* Тот же вес, что у `.btn-help:hover` выше, и стоит ниже — светлый фон съедал жёлтую заливку при наведении и фокусе
   с клавиатуры (нашёл соседний чат при проверке). Вес (0,3,0) — порядок правил больше не важен. Рамка — тоже: `.topbar .btn-ghost:hover`
   красил её в цвет шапки, и заливка была с чужим ободком. Значок — `--on-brand`, как в окнах (контраст — см. выше). */
.topbar .btn-help:hover, .topbar .btn-help:focus-visible { background: var(--yellow); border-color: var(--yellow); color: var(--on-brand); }
.help-list { display: flex; flex-direction: column; gap: 10px; max-width: 880px; }
.help-card { background: #fff; border: 1px solid #E8E8E8; border-radius: 16px; padding: 12px 16px; }
.help-card > summary { cursor: pointer; font-weight: 800; font-size: 15px; list-style: none; padding: 2px 0; }
.help-card > summary::-webkit-details-marker { display: none; }
.help-card > summary::before { content: '▸ '; color: var(--teal); }
.help-card[open] > summary::before { content: '▾ '; }
.help-intro { background: #FFF9D6; border-color: var(--yellow); }
.help-intro-title { font-weight: 800; font-size: 16px; margin-bottom: 4px; }
.help-body { font-size: 14px; line-height: 1.5; overflow-wrap: anywhere; }
.help-body h4, .help-body h5 { font-size: 15px; margin: 12px 0 4px; }
.help-body h5 { font-size: 14px; }
.help-body p { margin: 6px 0; }
.help-body ul, .help-body ol { margin: 6px 0; padding-left: 22px; }
.help-body li { margin: 3px 0; }
.help-body code { background: #F2F2F2; border-radius: 6px; padding: 1px 5px; font-size: 13px; }
.help-body pre { background: #F7F7F7; border-radius: 10px; padding: 8px 10px; overflow-x: auto; font-size: 12px; }
.kb-table { overflow-x: auto; margin: 8px 0; }
.kb-table table { border-collapse: collapse; font-size: 13px; min-width: 100%; }
.kb-table th { background: var(--yellow); text-align: left; }
.kb-table th, .kb-table td { border: 1px solid #E0E0E0; padding: 5px 8px; vertical-align: top; }
.kb-table tbody tr:nth-child(even) td { background: #F7F7F7; }
.help-upd { font-size: 12px; color: #888; margin-top: 6px; }
@media (max-width: 700px) {
  .btn-help { width: 38px; height: 38px; }
  .help-card { padding: 10px 12px; }
  .help-body { font-size: 15px; }
  /* 12.09: адрес и телефон площадки — в столбик. Рядом они помещаются только на широком
     экране: на 375 поле телефона уезжало за правый край и номер было не прочитать. */
  .pz-row .pz-contacts { flex-wrap: wrap; }
  .pz-row .pz-contacts .pz-addr,
  .pz-row .pz-contacts .pz-phone { flex: 1 1 100%; }
}
/* 11.09: «ℹ» в обычном (не полноэкранном) окне — шапка там не flex, кнопка встаёт справа обтеканием. */
dialog:not(.dlg-full) h3 .btn-help { float: right; margin-left: 8px; }
/* 11.09 (решение владельца): выбор оформления в ⚙ → Тенант. Образцы цветов рисует app.js
   из ответа /api/theme — цвета приходят проверенными сервером. */
.th-presets { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 10px; margin: 6px 0; }
.th-card { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; padding: 12px; text-align: left;
  border: 2px solid var(--line); border-radius: 14px; background: var(--surface); color: var(--ink);
  font: inherit; cursor: pointer; }
.th-card:hover { border-color: var(--topbar-hover); }
.th-card.on { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-light); }
.th-card small { color: var(--muted); font-size: 12px; line-height: 1.35; font-weight: 400; }
.th-sw { display: flex; gap: 4px; margin-bottom: 4px; }
.th-sw i { width: 26px; height: 26px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgba(0,0,0,.14); }

/* ─── Переучёт (склад → Инвентаризация / Праздник) ───────────────────────────
   ⚠ 19.09 (владелец: «переучёт у админов очень плохо выглядит на десктопе»).
   Было: одна колонка строк во всю ширину — название слева, поле «факт» у правого
   края в 1300 px от него, семь позиций на экран, никакой сверки с учётом.
   Стало: плитка в несколько колонок (сколько влезет по 330 px), в строке рядом
   стоят учёт, поле и разница, а шапка режима держит поиск и счётчик.
   ⚠ Селекторы с .inv-grid не для красоты: строка — это <label>, а `.form label`
   в диалоге ставит колонку через display:flex и выигрывает по весу у `.inv-row`. */
.inv-bar { position: sticky; top: 0; z-index: 4; display: flex; gap: 10px 14px; align-items: center;
  flex-wrap: wrap; padding: 8px 2px; margin-bottom: 4px; background: var(--surface);
  border-bottom: 1px solid var(--line); }
.inv-bar input[type="search"] { width: 240px; max-width: 46vw; }
.inv-bar .spacer { flex: 1; }
.inv-count { color: var(--muted); font-size: 13px; white-space: nowrap; }
.inv-diff { font-size: 13px; white-space: nowrap; color: var(--muted); }
.inv-diff.has { color: var(--warn); font-weight: 700; }
.inv-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
  gap: 4px 16px; align-content: start; max-height: none; }
.inv-grid .inv-cat { grid-column: 1 / -1; margin: 12px 0 2px; padding-bottom: 3px; font-weight: 800;
  color: var(--brand-dark); border-bottom: 1px solid var(--line); }
.inv-grid .inv-row { display: grid; grid-template-columns: minmax(0, 1fr) 68px 88px 46px; gap: 8px;
  align-items: center; padding: 4px 8px; border-radius: 8px; cursor: text;
  font-size: 14px; font-weight: 400; min-width: 0; flex: none; }
.inv-grid .inv-row:hover { background: var(--row-alt); }
.inv-grid .inv-row.gap { background: var(--warn-bg); }
.inv-grid .inv-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inv-grid .inv-have { text-align: right; color: var(--muted); font-size: 13px;
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.inv-grid .inv-row input { width: 100%; font-variant-numeric: tabular-nums; }
.inv-grid .inv-d { text-align: right; font-size: 12px; font-weight: 800; font-variant-numeric: tabular-nums; }
.inv-grid .inv-row.gap .inv-d { color: var(--warn); }
.inv-grid .inv-row.filled .inv-d { color: var(--ok); }
/* ⚠ «сошлось» НЕ заливаем: у площадки своя тема, и --brand-light там жёлтый — совпавшая
   строка читалась как расхождение. Совпадение показывает зелёная галочка, расхождение — заливка. */
@media (max-width: 760px) {
  .inv-grid { grid-template-columns: 1fr; }
  .inv-grid .inv-row { grid-template-columns: minmax(0, 1fr) 62px 80px 46px; gap: 6px; padding: 4px 2px; }
  .inv-bar input[type="search"] { width: 100%; }
}

/* ─── Игры на аренах ─────────────────────────────────────────────────────────
   ⚠ 19.09 (владелец: «сделай качественную вёрстку раздела "игры на аренах" и
   возможность выбрать отчёт по каждому из экранов»). Экран — это компьютер в зале.
   ⚠ 05.10 (владелец: «когда выбираю 7 дней, он не становится серым… пересмотри всё окно»).
   Порядок окна — по значимости: фильтры (выбранное залито фирменным цветом, как во всей CRM) →
   «сейчас» по каждому экрану → карточки игр за период (раунды, рекорды, пик, время без связи) →
   подробный отчёт → свёрнутая проверка QR. На телефоне всё в одну колонку, ничего не шире экрана. */
.gm-filters { display: flex; flex-direction: column; gap: 8px; margin: 0 0 4px; }
.gm-frow { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.gm-flabel { flex: 0 0 60px; font-size: 13px; color: var(--muted); }
.gm-chips { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; }
.gm-dates { display: flex; flex-wrap: wrap; gap: 8px; }
.gm-dates label { display: flex; flex-direction: row; align-items: center; gap: 6px; min-width: 0;
  font-size: 13px; font-weight: 400; color: var(--muted); }
.gm-dates input { width: 150px; }
.gm-sec { margin-top: 16px; min-width: 0; }
.gm-sec:empty { display: none; }
.gm-h { display: flex; justify-content: space-between; align-items: center; gap: 6px 12px; flex-wrap: wrap;
  margin: 0 0 8px; font-size: 17px; font-weight: 800; }
.gm-h-side { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 6px; font-size: 13px; font-weight: 700; }
.gm-sub { color: var(--muted); font-weight: 400; }
.gm-good { color: #2E7D46; }
.gm-bad { color: var(--danger); }
.gm-none { color: var(--muted); font-size: 13px; padding: 4px 0; }
.gm-note { margin: 8px 0; font-size: 13px; line-height: 1.45; color: var(--muted); }
.gm-note.bad { background: var(--danger-bg); color: var(--ink); border-radius: 10px; padding: 8px 12px; }
.gm-note ul { margin: 4px 0 0; padding-left: 18px; }
/* «Сейчас»: карточка на арену, строка на экран. */
.gm-agrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 10px; }
.gm-acard { min-width: 0; background: var(--surface); border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; }
.gm-aname { font-weight: 800; margin-bottom: 4px; overflow-wrap: anywhere; }
.gm-scr { display: flex; align-items: center; gap: 8px; padding: 6px 0; border-top: 1px solid var(--line); font-size: 14px; }
.gm-aname + .gm-scr { border-top: 0; }
.gm-scr .gm-dot { flex: none; width: 10px; height: 10px; border-radius: 50%; background: var(--line); }
.gm-scr.ok .gm-dot { background: #2E7D46; }
.gm-scr.warn .gm-dot { background: #B26A00; }
.gm-scr.bad .gm-dot { background: var(--danger); }
.gm-scr-name { flex: 1 1 auto; min-width: 0; font-weight: 700; overflow-wrap: anywhere; }
.gm-scr-st { flex: none; font-size: 13px; color: var(--muted); text-align: right; }
.gm-scr.ok .gm-scr-st { color: #2E7D46; }
.gm-scr.bad { background: var(--danger-bg); margin: 0 -12px; padding: 6px 12px; }
.gm-scr.bad .gm-scr-st { color: var(--danger); font-weight: 700; }
.gm-scr-warn { font-size: 12px; margin: 0 0 6px 18px; }
/* «За период»: арена — заголовок, под ней карточка на игру. */
.gm-arena { margin: 14px 0 0; }
.gm-ggrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); gap: 10px; }
.gm-gcard { display: flex; flex-direction: column; gap: 3px; width: 100%; min-width: 0; text-align: left;
  font: inherit; color: inherit; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; }
.gm-gcard:hover { border-color: var(--brand-dark); }
.gm-gcard.on { border-color: var(--brand-dark); box-shadow: 0 0 0 3px var(--brand-light); }
.gm-gc-top { font-size: 14px; }
.gm-gc-big { font-size: 14px; }
.gm-gc-big > b { font-size: 26px; line-height: 1.15; font-variant-numeric: tabular-nums; }
.gm-gc-line { font-size: 13px; line-height: 1.4; }
.gm-gc-off { margin-top: 6px; font-size: 13px; line-height: 1.4; font-weight: 700; color: var(--danger); }
.gm-gc-off span { font-weight: 400; }
.gm-gc-ok { margin-top: 6px; font-size: 13px; color: #2E7D46; }
/* Мини-шкала по дням: столбик — раунды, красная черта снизу — день, когда экран был без связи. */
.gm-spark { display: flex; align-items: flex-end; gap: 2px; height: 44px; margin-top: 8px; }
.gm-spark i { flex: 1 1 0; min-width: 0; background: var(--brand); border-radius: 2px 2px 0 0; box-sizing: border-box; }
.gm-spark i.zero { background: var(--line); }
.gm-spark i.off { border-bottom: 4px solid var(--danger); }
.gm-spark-cap { display: flex; justify-content: space-between; gap: 6px; font-size: 11px; color: var(--muted); }
.gm-more { margin-top: 12px; }
.gm-more > summary, .gm-qr > summary { cursor: pointer; font-size: 13px; color: var(--muted); }
.gm-qr { margin-top: 20px; padding-top: 10px; border-top: 1px solid var(--line); }
.gm-qr[open] > summary { margin-bottom: 8px; }
.gm-selftest { background: var(--brand-light); border-radius: 10px; padding: 10px 12px; margin-bottom: 8px; font-size: 13px; }
.gm-selftest.bad { background: var(--danger-bg); }
@media (max-width: 700px) {
  .gm-flabel { flex: 0 0 100%; }
  .gm-dates { width: 100%; flex-wrap: nowrap; }
  .gm-dates label { flex: 1 1 0; }
  .gm-dates input { width: 100%; min-width: 0; }
  .gm-agrid, .gm-ggrid { grid-template-columns: 1fr; }
  #gm-period .rep-card b { font-size: 22px; }   /* «42 ч 52 мин» в две строки на телефоне не ломается */
}

/* ─── Выручка дня на главной (только владельцу, 19.09) ───────────────────────
   Строка стоит над сеткой дня: владелец открывает арену и сразу видит деньги,
   не заходя в отчёты. Остальным сервер её не отдаёт вовсе. */
.day-money { display: flex; gap: 8px 16px; align-items: baseline; flex-wrap: wrap;
  background: var(--brand-light); border-radius: 12px; padding: 8px 14px; margin: 0 0 10px; }
.day-money .dm-total { font-size: 18px; font-variant-numeric: tabular-nums; }
.day-money .dm-part { color: var(--ink); font-size: 14px; font-variant-numeric: tabular-nums; }
.day-money .dm-note { color: var(--muted); font-size: 12px; margin-left: auto; }
@media (max-width: 760px) { .day-money .dm-note { margin-left: 0; flex: 0 0 100%; } }
/* 23.09 (аудит В8): сообщения гостю в карточке записи — когда, зачем, канал, дошло ли.
   05.10: блок стал «💬 Переписка» (и в карточке заявки ивента): гость слева, мы справа, новые сверху. */
.e-sms summary { cursor: pointer; }
.sms-ok { color: #2E7D46; }
.sms-bad { color: #C62828; font-weight: 700; }
.sms-unk { color: var(--warn); font-weight: 700; }   /* 07.10: «не подтверждено» — шлюз не ответил, могло уйти */
.chat-list { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; max-height: 40vh; overflow: auto; font-size: 13px; }
.chat-msg { max-width: 82%; padding: 6px 10px; border-radius: 12px; }
.chat-in { align-self: flex-start; background: var(--sand); border: 1px solid var(--line); border-bottom-left-radius: 4px; }
.chat-out { align-self: flex-end; background: var(--brand-light); border-bottom-right-radius: 4px; }
.chat-stop { background: var(--danger-bg); border-color: var(--danger-bg); }
.chat-meta { font-size: 11px; color: var(--muted); margin-bottom: 2px; }
.chat-text { white-space: pre-wrap; overflow-wrap: anywhere; color: var(--ink); }
.chat-none { color: var(--muted); font-style: italic; }
.chat-foot { margin-top: 4px; }
.ld-chat { max-height: 320px; }
@media (max-width: 760px) { .chat-msg { max-width: 92%; } .chat-list { max-height: 55vh; } }

/* ─── Заявки на праздник (26.09, макет утверждён владельцем) ─────────────────
   Доска из пяти колонок и карточка заявки справа. Цвета — только смысловые переменные палитры:
   «сегодня» — подложка предупреждения, «просрочено / нет шага» — подложка ошибки. */
#dlg-leads .dlg { max-width: 1680px; }
/* Шрифт полей 16px и на десктопе: окно открывают и с планшета, а iOS увеличивает страницу при фокусе на мелком поле. */
#dlg-leads input, #dlg-leads select, #dlg-lead-new input, #dlg-lead-new select { font-size: 16px; }
.ld-top { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; }
.ld-top #ld-q { flex: 1; min-width: 180px; border-radius: 999px; }
.ld-day { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.ld-chip { background: var(--surface); border: 1px solid var(--line); border-radius: 14px; padding: 8px 12px; min-width: 110px; }
.ld-chip b { display: block; font-size: 22px; font-weight: 800; }
.ld-chip span { font-size: 12px; color: var(--muted); }
.ld-chip.act { cursor: pointer; }
.ld-chip.hl { background: var(--warn-bg); border-color: var(--warn-bg); }
.ld-chip.hl span { color: var(--ink); }
.ld-chip.warn { border-color: var(--danger); }
.ld-chip.warn b { color: var(--danger); }
.ld-wrap { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; align-items: start; }
.ld-wrap.with-panel { grid-template-columns: minmax(0, 1fr) 380px; }
.ld-board { display: grid; grid-template-columns: repeat(5, minmax(190px, 1fr)); gap: 10px; overflow-x: auto; padding-bottom: 6px; }
.ld-col { background: var(--sand); border-radius: 16px; padding: 10px; min-height: 240px; }
.ld-col h4 { margin: 2px 4px 10px; font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .3px;
  display: flex; justify-content: space-between; gap: 6px; }
.ld-col h4 i { font-style: normal; color: var(--muted); }
.ld-card { background: var(--surface); border: 2px solid transparent; border-radius: 14px; padding: 9px 11px; margin-bottom: 8px;
  cursor: pointer; box-shadow: 0 1px 2px rgba(0,0,0,.06); }
.ld-card:hover { border-color: var(--line); }
.ld-card.sel { border-color: var(--brand); }
.ld-n { font-weight: 800; font-size: 14px; }
.ld-m { font-size: 12px; color: var(--muted); margin-top: 2px; }
.ld-src { display: inline-block; font-size: 11px; font-weight: 700; border-radius: 999px; padding: 1px 8px; background: var(--brand-light);
  color: var(--ink); margin-right: 4px; }
.ld-money { font-size: 12px; margin-top: 4px; }
.ld-next { margin-top: 7px; font-size: 12px; font-weight: 700; border-radius: 10px; padding: 5px 8px; background: var(--row-alt); }
.ld-next.today { background: var(--warn-bg); color: var(--warn); }
.ld-next.late { background: var(--danger-bg); color: var(--danger); }
/* 30.09: тема заявки и «кто взял» (решение владельца 29.09: раздел «Заявки» — не только праздники) */
.ld-next.ok { background: var(--brand-light); color: var(--ok); }
.ld-tags { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 3px; }
.ld-topic, .ld-who { display: inline-block; font-size: 11px; font-weight: 700; border-radius: 999px; padding: 1px 8px;
  background: var(--sand); border: 1px solid var(--line); color: var(--ink); }
.ld-who.free { background: var(--warn-bg); border-color: var(--warn-bg); color: var(--warn); }
.ld-taken { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; font-size: 13px; background: var(--row-alt);
  border-radius: 12px; padding: 7px 10px; margin-bottom: 8px; }
.ld-taken.free { background: var(--warn-bg); color: var(--warn); font-weight: 700; }
.ld-taken .btn { margin-left: auto; }
.ld-empty { font-size: 12px; color: var(--muted); padding: 4px; }
.ld-lost { margin-top: 8px; font-size: 12px; color: var(--muted); }
.ld-lost summary { cursor: pointer; font-weight: 700; padding: 4px; }
.ld-lost-row { padding: 4px 6px; cursor: pointer; border-radius: 8px; }
.ld-lost-row:hover { background: var(--surface); }
.ld-panel { background: var(--surface); border: 1px solid var(--line); border-radius: 18px; padding: 14px; position: sticky; top: 64px;
  max-height: calc(100dvh - 90px); overflow: auto; }
.ld-ph { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.ld-ph h2 { margin: 0; font-size: 20px; }
.ld-sub { color: var(--muted); font-size: 13px; margin: 2px 0 10px; }
.ld-book { background: var(--brand-light); border-radius: 12px; padding: 8px 10px; font-size: 13px; margin-bottom: 8px; }
.ld-info { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.ld-info span { flex: 1 1 140px; background: var(--row-alt); border-radius: 12px; padding: 7px 10px; font-size: 13px; }
.ld-info small, .ld-fields label { display: block; color: var(--muted); font-size: 11px; font-weight: 700; }
.ld-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 8px; margin-bottom: 8px; }
.ld-fields input, .ld-fields select { width: 100%; box-sizing: border-box; margin-top: 2px; }
.ld-sect { margin: 14px 0 6px; font-size: 12px; font-weight: 800; text-transform: uppercase; color: var(--muted); }
.ld-cur { display: flex; gap: 8px; align-items: center; margin-bottom: 6px; }
.ld-cur .ld-next { flex: 1; margin: 0; }
.ld-steps { display: flex; gap: 6px; flex-wrap: wrap; }
.ld-pill { border: 1px solid var(--line); background: var(--surface); border-radius: 999px; padding: 6px 11px; font: inherit;
  font-size: 13px; font-weight: 600; cursor: pointer; }
.ld-pill.on { background: var(--ink); color: var(--surface); border-color: var(--ink); }
.ld-when { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
.ld-when input { flex: 1 1 150px; min-width: 0; }
.ld-act { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
.ld-act .btn { flex: 1 1 auto; }
.ld-tl { border-left: 2px solid var(--line); margin-left: 6px; padding-left: 12px; }
.ld-tl > div { font-size: 13px; margin-bottom: 8px; position: relative; }
.ld-tl > div::before { content: ''; position: absolute; left: -18px; top: 5px; width: 10px; height: 10px; border-radius: 50%; background: var(--brand); }
.ld-tl small { color: var(--muted); }
/* Телефон: колонки листаются вбок по одной (прилипают к краю), карточка заявки — под доской во всю ширину. */
@media (max-width: 900px) {
  .ld-wrap, .ld-wrap.with-panel { grid-template-columns: minmax(0, 1fr); }
  .ld-board { grid-template-columns: repeat(5, 80vw); scroll-snap-type: x mandatory; }
  .ld-col { scroll-snap-align: start; }
  .ld-panel { position: static; max-height: none; }
  .ld-fields { grid-template-columns: 1fr; }
  .ld-panel { scroll-margin-top: 64px; }
  .ld-chip { min-width: 0; flex: 1 1 calc(33% - 8px); padding: 6px 8px; }
  .ld-chip b { font-size: 18px; }
  .ld-chip span { font-size: 11px; }
}

/* 02.10: аналитика ФОТ (📊 в «Смены и ЗП за период») — цифры столбиком, итог сети жирным, отделы с подписью */
.fot-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(128px, 1fr)); gap: 8px; margin: 8px 0 4px; }
.fot-kpi { display: grid; gap: 2px; padding: 10px 12px; border-radius: 12px; background: var(--row-alt); font-variant-numeric: tabular-nums; }
.fot-kpi b { font-size: 18px; }
.fot-kpi.fot-last { background: var(--accent-tint); }
.fot-tbl { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; font-size: 14px; }
.fot-tbl th { text-align: right; font-weight: 600; color: var(--muted); padding: 6px 8px; border-bottom: 2px solid var(--ink); white-space: nowrap; }
.fot-tbl td { text-align: right; padding: 7px 8px; border-bottom: 1px solid var(--line); white-space: nowrap; vertical-align: top; }
.fot-tbl th:first-child, .fot-tbl td:first-child { text-align: left; white-space: normal; }
.fot-tbl td:first-child .muted { font-size: 12px; }
.fot-tbl .fot-sum td { font-weight: 700; border-top: 2px solid var(--ink); border-bottom: none; }
.fot-tbl .fot-chg { color: var(--ink); }
@media (max-width: 600px) { .fot-tbl { font-size: 13px; } .fot-tbl th, .fot-tbl td { padding: 6px 5px; } }
#dlg-fot { width: min(96vw, 1100px); }   /* 02.10: аналитике ФОТ — ширина под месяцы и разницу */
/* 02.10: «📅 График работ» — таблица месяца: работы × дни, первая колонка закреплена; на телефоне — прокрутка внутри */
.wp-grid-wrap { overflow-x: auto; max-width: 100%; border: 1px solid var(--line, #e0e0e0); border-radius: 12px; }
.wp-grid { border-collapse: separate; border-spacing: 0; font-size: 12px; min-width: 100%; }
.wp-grid th, .wp-grid td { border-bottom: 1px solid var(--line, #eee); padding: 2px; text-align: center; vertical-align: middle; min-width: 30px; }
.wp-grid thead th, .wp-grid tr:first-child th { position: sticky; top: 0; background: var(--bg, #fff); z-index: 1; font-weight: 600; }
.wp-grid .wp-sticky { position: sticky; left: 0; background: var(--bg, #fff); z-index: 2; text-align: left; min-width: 150px; max-width: 190px; padding: 4px 6px; }
.wp-grid .wp-sticky small { display: block; color: var(--muted, #777); font-weight: 400; font-size: 11px; line-height: 1.25; }
.wp-grid .wp-today { background: rgba(252, 221, 0, .18); }
.wp-grid .wp-we { color: #b55; }
.wp-plan { background: none; border: 0; padding: 0; font: inherit; font-weight: 700; text-align: left; cursor: pointer; text-decoration: underline dotted; color: inherit; }
.wp-cell { display: inline-flex; flex-direction: column; align-items: center; justify-content: center; min-width: 26px; min-height: 30px;
  border: 1px solid #ccc; border-radius: 8px; background: #fff; padding: 0 2px; cursor: pointer; font-size: 12px; line-height: 1.1; color: inherit; }
.wp-cell small { font-size: 10px; font-weight: 700; }
.wp-cell.wp-reported { background: #fff6bf; border-color: #e8c400; }
.wp-cell.wp-accepted { background: #dff7f8; border-color: #67dbe2; }
.wp-cell.wp-rejected, .wp-cell.wp-missed { background: #fde3e1; border-color: #e53935; }
.wp-cell.wp-cancelled { opacity: .45; }
.wp-cell.wp-nobody { border-style: dashed; }
.wp-legend { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 12px; color: var(--muted, #666); margin: 4px 0; }
.wp-wait { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.wp-photos { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.wp-photos img { width: 84px; height: 84px; object-fit: cover; border-radius: 8px; max-width: 100%; }
.wpe-wd { gap: 4px 12px; }
.wo-staff { display: flex; flex-direction: column; gap: 4px; max-height: 50vh; overflow: auto; }
#mwr-text { font: inherit; width: 100%; }
/* ─── Склад → «📅 По графику» (05.10, решения владельца по переучётам) ─────────────────────────
   Неделя Пн–Вс — семь кнопок в ряд: на 1440 — широкие плитки, на 375 — узкие колонки «день / число / отметка»,
   без прокрутки вбок. Список позиций — та же плитка .inv-grid, что у «Инвентаризации»; посчитанная сегодня
   позиция (done0) — с галочкой и временем, поле можно заполнить ещё раз. */
.rp-head { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 6px; }
.rp-week { display: flex; gap: 6px; align-items: stretch; }
.rp-day, .rp-nav { border: 1px solid var(--line); border-radius: 10px; background: var(--surface); color: inherit;
  font: inherit; cursor: pointer; padding: 6px 4px; }
.rp-nav { flex: 0 0 34px; font-size: 20px; line-height: 1; font-weight: 700; }
.rp-day { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 1px; line-height: 1.15; }
.rp-day b { font-size: 13px; }
.rp-day span { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.rp-day i { font-style: normal; font-size: 13px; font-weight: 700; white-space: nowrap; font-variant-numeric: tabular-nums; }
.rp-day.st-done { background: #e8f6ea; border-color: #8fd19a; }
.rp-day.st-missed { background: var(--warn-bg); border-color: var(--warn); }
.rp-day.st-today { border-color: var(--ink); }
.rp-day.st-none, .rp-day.st-before { opacity: .6; }
.rp-day.sel { box-shadow: 0 0 0 2px var(--ink) inset; }
.rp-dayline { display: flex; flex-wrap: wrap; gap: 6px 12px; align-items: center; font-size: 14px; }
.rp-dayline > span { flex: 1 1 320px; min-width: 0; }
/* .form label в диалоге — колонка во всю ширину; поле дня здесь компактное, в строку с подписью */
.rp-dayline .rp-pick { display: inline-flex; flex-direction: row; align-items: center; gap: 6px; flex: none; margin: 0;
  font-size: 13px; color: var(--muted); }
.rp-dayline .rp-pick input { width: auto; min-width: 0; }
.rp-warn { margin: 0; }
.rp-full { padding: 8px 10px; border-radius: 10px; background: #fff6bf; border: 1px solid #e8c400; font-size: 14px; line-height: 1.35; }
.rp-legend { font-size: 12px; color: var(--muted); }
.rp-gcount { font-weight: 700; font-size: 12px; color: var(--warn); margin-left: 6px; }
.rp-gcount.ok { color: var(--ok); }
.inv-grid .inv-row.done0 .inv-d { color: var(--ok); }
.inv-grid .inv-name .rp-at { display: block; color: var(--ok); font-weight: 700; font-size: 11px; line-height: 1.2; }
.rp-day i em { font-style: normal; }
.inv-grid .rp-towels { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: flex-end; padding: 4px 8px; }
.inv-grid .rp-towels label { display: flex; flex-direction: column; gap: 2px; font-size: 13px; flex: 0 0 120px; }
.inv-grid .rp-edit { grid-column: 1 / -1; margin-top: 14px; border-top: 1px solid var(--line); padding-top: 8px; }
.rp-edit summary { cursor: pointer; font-weight: 700; padding: 4px 0; }
.rp-grid { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; max-width: 640px; }
.rp-gr { display: grid; grid-template-columns: minmax(0, 1fr) repeat(7, 38px); align-items: center; gap: 2px;
  padding: 2px 4px; border-radius: 8px; }
.rp-gr:nth-child(even) { background: var(--row-alt); }
.rp-gr > span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; }
.rp-gr > span:first-child small { color: var(--muted); }
.rp-gh { font-weight: 700; font-size: 12px; color: var(--muted); }
.rp-gh > span:not(:first-child) { text-align: center; }
/* .form label в диалоге — колонка шириной от 130 px: в клетке 38 px галочка уезжала вправо на полклетки */
.rp-grid .rp-gr label { display: flex; flex-direction: row; justify-content: center; align-items: center; margin: 0; padding: 6px 0;
  min-width: 0; flex: none; cursor: pointer; }
.rp-gr input[type="checkbox"] { width: 20px; height: 20px; margin: 0; }
@media (max-width: 760px) {
  .rp-week { gap: 3px; }
  .rp-nav { flex: 0 0 24px; padding: 6px 0; font-size: 18px; }
  .rp-day { padding: 5px 0; border-radius: 8px; }
  .rp-day b { font-size: 12px; }
  .rp-day span { font-size: 11px; }
  .rp-day i { font-size: 11px; }
  .rp-day i .rp-cnt { display: none; }
  .rp-dayline { font-size: 13px; }
  .rp-dayline > span { flex-basis: 100%; }
  .rp-gr { grid-template-columns: minmax(0, 1fr) repeat(7, 30px); gap: 1px; padding: 2px; }
  .rp-gr > span:first-child { font-size: 13px; }
  .inv-grid .rp-towels label { flex: 1 1 90px; }
}
/* 05.10: вход в CRM — ожидание «Да» в Telegram, журнал «🔐 Входы», «Мои входы» (src/auth_confirm.js). Классы новые — старое не трогают. */
@media (min-width: 761px) { #dlg-auth-wait { width: 480px; } }   /* на телефоне — как все окна (во всю ширину) */
#dlg-auth-wait .auth-wait { text-align: center; }
#dlg-auth-wait .actions { justify-content: center; }
.aw-who { font-weight: 700; font-size: 17px; margin: 4px 0 8px; }
.aw-dev { font-size: 14px; color: var(--muted); margin: 10px 0 0; }
.aw-timer { font-size: 34px; font-weight: 800; margin: 14px 0 4px; font-variant-numeric: tabular-nums; }
.aw-state { font-size: 15px; min-height: 1.4em; }
.lg-filters label { flex: 1 1 150px; }
.lg-row { align-items: flex-start; gap: 10px; }
.lg-row .lg-when { flex: 0 0 92px; font-variant-numeric: tabular-nums; color: var(--muted); font-size: 14px; padding-top: 2px; }
.lg-row .lg-main { flex: 1 1 auto; min-width: 0; line-height: 1.4; overflow-wrap: anywhere; }
.lg-row .lg-acts { flex: none; display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.lg-row.lg-bad { background: #FDECEC; border-radius: 8px; }
.lg-row.lg-sus:not(.lg-bad) { background: #FFF6D6; border-radius: 8px; }
.lg-flag { color: #C62828; }
.lg-person-box { background: var(--row-alt); border-radius: 10px; padding: 8px 12px; margin: 8px 0; line-height: 1.45; }
.lg-reach { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 8px; }
.lg-reach summary { cursor: pointer; font-weight: 700; padding: 4px 0; }
.lg-reach .sub { margin-top: 10px; font-weight: 700; }
.lg-reach label.lg-person { display: flex; gap: 8px; align-items: baseline; margin: 4px 0; flex-wrap: wrap; }
.lg-oklist { line-height: 1.5; }
@media (max-width: 760px) {
  .lg-row { flex-wrap: wrap; }
  .lg-row .lg-when { flex: 0 0 100%; padding: 0; font-size: 13px; }
  .lg-row .lg-acts { flex: 0 0 100%; justify-content: flex-start; }
  .lg-filters label { flex: 1 1 45%; }
  .aw-timer { font-size: 30px; }
}
/* ─── Склад: «слепой» пересчёт, списание, перемещение (05.10, четыре меры по переучётам) ─────────────────────────
   «Слепой» пересчёт — у того, кто считает, колонки учёта нет вовсе (сервер её не присылает): название, поле, отметка.
   «Пересчитайте ещё раз» — оранжевая рамка и подпись под названием. «🚚 Перемещение» — карточки входящих и отправленных
   над списком позиций, «В пути» — таблица; на телефоне строки таблицы переносятся. */
/* Режимов склада стало десять: на широком экране подписи переносятся строкой целиком, а не сжимаются в столбик. */
#dlg-stock .dr-mode { flex-wrap: wrap; row-gap: 6px; }
#dlg-stock .dr-mode label.radio { white-space: nowrap; }
.inv-grid.blind .inv-row { grid-template-columns: minmax(0, 1fr) 96px 30px; }
.inv-grid .inv-row.recheck { box-shadow: 0 0 0 2px var(--warn) inset; background: var(--warn-bg); }
.inv-grid .inv-cat.inv-again { color: var(--warn); }
.inv-grid .inv-name .inv-again-note { display: block; color: var(--warn); font-weight: 700; font-size: 11px; line-height: 1.2; }
.h-row.st-prod .st-qty { width: 96px; flex: none; }
.tr-head { display: flex; flex-direction: column; gap: 8px; margin: 4px 0 8px; }
.tr-block { border: 1px solid var(--line); border-radius: 10px; padding: 8px 10px; background: var(--surface); min-width: 0; }
.tr-block h4 { margin: 0 0 6px; font-size: 15px; }
.tr-block summary { cursor: pointer; font-weight: 700; padding: 2px 0; }
.tr-card { border-top: 1px solid var(--line); padding: 6px 0; display: flex; flex-direction: column; gap: 4px; font-size: 14px; min-width: 0; }
.tr-block h4 + .tr-card, .tr-block summary + .tr-card { border-top: 0; }
.tr-card.in.late { background: var(--warn-bg); border-radius: 8px; padding: 6px; }
.tr-items { color: var(--muted); font-size: 13px; overflow-wrap: anywhere; }
.tr-diff { color: var(--warn); font-size: 13px; font-weight: 700; }
.tr-st { font-weight: 700; }
.tr-st.ok { color: var(--ok); }
.tr-st.diff, .tr-st.sent { color: var(--warn); }
.tr-st.off { color: var(--muted); }
.tr-card .btn { align-self: flex-start; }
.tr-send-row { gap: 8px 14px; align-items: flex-end; }
.tr-table { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; }
.tr-trow { display: grid; grid-template-columns: 48px minmax(0, 2fr) 130px minmax(0, 1fr) 70px; gap: 8px; align-items: center;
  font-size: 13px; padding: 3px 4px; border-radius: 6px; }
.tr-trow:nth-child(even) { background: var(--row-alt); }
.tr-trow.late b { color: var(--warn); }
@media (max-width: 760px) {
  .inv-grid.blind .inv-row { grid-template-columns: minmax(0, 1fr) 84px 26px; }
  .h-row.st-prod .st-qty { width: 84px; }
  .tr-trow { display: flex; flex-wrap: wrap; gap: 2px 8px; }
  .tr-trow > span:nth-child(2) { flex: 1 1 60%; min-width: 0; }
  .tr-send-row label { flex: 1 1 100%; }
}
/* ─── 🧹 Уборки по будням (06.10, уборщица на ставке «за уборку по будням») ─────────────────────────────────────────
   Блок в ведомости «Сотрудники» и в «Учёте работ и смен»: человек → сумма месяца → будни строками (✅ / ⛔ не было) с кнопкой
   у даты. На телефоне строка дня остаётся строкой: дата, статус (переносится внутри) и кнопка справа. */
.wdc-block { margin: 8px 0; }
.wdc-top { display: flex; align-items: center; gap: 8px 14px; flex-wrap: wrap; }
.wdc-top .wdc-month { flex: none; display: flex; align-items: center; gap: 6px; }
.wdc-person { border-top: 1px solid var(--line); margin-top: 8px; padding-top: 6px; min-width: 0; }
.wdc-head { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline; }
.wdc-sum { margin-left: auto; }
.wdc-days { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; max-width: 760px; }   /* кнопка рядом с датой, а не у края экрана */
.wdc-day { display: flex; align-items: center; gap: 10px; padding: 3px 6px; border-radius: 6px; font-size: 14px; min-width: 0; }
.wdc-day:nth-child(even) { background: var(--row-alt); }
.wdc-day .wdc-date { flex: 0 0 64px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.wdc-day .wdc-st { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.wdc-day .btn { flex: none; }
.wdc-day.wdc-skip .wdc-st { color: var(--red); font-weight: 700; }
.wdc-day.wdc-holiday .wdc-st, .wdc-day.wdc-absence .wdc-st, .wdc-day.wdc-future .wdc-st { color: var(--muted); }
.wdc-day.wdc-today { box-shadow: 0 0 0 2px var(--line) inset; }
@media (max-width: 760px) {
  .wdc-sum { margin-left: 0; flex: 0 0 100%; }
  .wdc-day { gap: 6px; padding: 4px; font-size: 13px; }
  .wdc-day .wdc-date { flex: 0 0 auto; }
  .wdc-day .btn { padding: 4px 8px; }
}
/* ─── 💬 Переписка (06.10, владелец: «да, делай переписку в CRM») ──────────────────────────────────────────────────────────
   Раздел во весь экран: слева разговоры, справа карточка гостя, лента и поле ответа; список и лента прокручиваются внутри, поле
   ответа всегда у низа. На телефоне — список, по нажатию лента на весь экран («← К списку»). Значок у пункта меню — сколько ждут
   ответа; на телефоне пункт под ☰, поэтому точка — и на самом ☰. */
.inbox-badge { display: inline-block; min-width: 18px; height: 18px; line-height: 18px; padding: 0 5px; margin-left: 6px; border-radius: 999px;
  background: var(--danger); color: #fff; font-size: 11px; font-weight: 800; text-align: center; vertical-align: 1px; box-sizing: border-box; }
#btn-burger.has-new { position: relative; }
#btn-burger.has-new::after { content: ''; position: absolute; top: 2px; right: 2px; width: 9px; height: 9px; border-radius: 50%; background: var(--danger); }
.ib-wrap { display: grid; grid-template-columns: 340px minmax(0, 1fr); gap: 14px; height: calc(100dvh - 120px); min-height: 420px; }
.ib-side { display: flex; flex-direction: column; min-height: 0; min-width: 0; background: var(--sand); border-radius: 16px; padding: 10px; }
.ib-top { display: flex; flex-direction: column; gap: 8px; margin-bottom: 8px; }
.ib-top input { width: 100%; box-sizing: border-box; border-radius: 999px; }
.ib-filters { display: flex; gap: 6px; flex-wrap: wrap; }
.ib-f { border: 1px solid var(--line); background: var(--surface); color: var(--ink); border-radius: 999px; padding: 5px 11px; font: inherit;
  font-size: 13px; font-weight: 600; cursor: pointer; }
.ib-f.on { background: var(--ink); color: var(--surface); border-color: var(--ink); }
.ib-f i { font-style: normal; opacity: .75; }
.ib-list { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 6px; }
.ib-item { background: var(--surface); border: 2px solid transparent; border-radius: 14px; padding: 8px 10px; cursor: pointer; min-width: 0; }
.ib-item:hover { border-color: var(--line); }
.ib-item.sel { border-color: var(--brand); }
.ib-item.closed { opacity: .72; }
.ib-r1 { display: flex; align-items: center; gap: 6px; min-width: 0; }
.ib-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.ib-dot { flex: none; width: 9px; height: 9px; border-radius: 50%; background: var(--brand); }
.ib-time { flex: none; font-size: 12px; color: var(--muted); }
.ib-r2 { font-size: 13px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 2px; }
.ib-item.unread .ib-name, .ib-item.unread .ib-r2 { font-weight: 800; color: var(--ink); }
.ib-r3 { display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: center; margin-top: 4px; font-size: 11px; color: var(--muted); }
.ib-lead { font-weight: 700; color: var(--ink); }
.ib-st { display: inline-block; border-radius: 999px; padding: 1px 7px; font-size: 11px; font-weight: 700; background: var(--row-alt); color: var(--muted); }
.ib-st.new { background: var(--warn-bg); color: var(--warn); }
.ib-st.answered { background: var(--brand-light); color: var(--ok); }
.ib-none, .ib-empty { color: var(--muted); font-size: 14px; padding: 18px 8px; text-align: center; }
.ib-main { display: flex; flex-direction: column; min-height: 0; min-width: 0; background: var(--surface); border: 1px solid var(--line); border-radius: 18px; padding: 12px 14px; }
.ib-head { display: flex; gap: 8px 12px; align-items: flex-start; flex-wrap: wrap; }
.ib-who { flex: 1 1 260px; min-width: 0; }
.ib-who h2 { margin: 0; font-size: 19px; overflow-wrap: anywhere; }
.ib-sub { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: center; font-size: 13px; color: var(--muted); margin-top: 3px; }
.ib-acts { display: flex; gap: 6px; flex-wrap: wrap; }
.ib-back { display: none; }
.ib-guest { margin: 8px 0 6px; padding: 8px 10px; background: var(--row-alt); border-radius: 12px; font-size: 13px; display: flex; flex-direction: column; gap: 3px;
  max-height: 22vh; overflow-y: auto; }
.ib-g-t { font-size: 11px; font-weight: 800; text-transform: uppercase; color: var(--muted); }
.ib-bk { color: var(--ink); text-decoration: none; overflow-wrap: anywhere; }
.ib-bk:hover b { text-decoration: underline; }
.ib-feed { flex: 1; min-height: 120px; max-height: none; overflow-y: auto; margin: 0; padding: 4px 2px; }
.ib-compose { border-top: 1px solid var(--line); padding-top: 8px; margin-top: 6px; display: flex; flex-direction: column; gap: 6px; }
.ib-compose textarea { width: 100%; box-sizing: border-box; min-height: 64px; max-height: 30vh; resize: vertical; font: inherit; font-size: 15px;
  padding: 8px 10px; border-radius: 12px; border: 1px solid var(--line); background: var(--surface); color: var(--ink); }
.ib-cbar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.ib-hint { flex: 1 1 200px; min-width: 0; font-size: 12px; color: var(--muted); }
.ib-cbar .btn { flex: none; }
.chat-photo { display: block; max-width: 100%; max-height: 240px; border-radius: 10px; cursor: zoom-in; }
@media (max-width: 760px) {
  .ib-wrap { grid-template-columns: minmax(0, 1fr); gap: 0; height: calc(100dvh - 76px); min-height: 0; }
  .ib-wrap .ib-main { display: none; }
  .ib-wrap.with-chat .ib-side { display: none; }
  .ib-wrap.with-chat .ib-main { display: flex; border: 0; border-radius: 0; padding: 0; }
  /* Шапка разговора: [← К списку] … [✔ Закрыть] одной строкой, имя и номер — под ними: ленте остаётся больше места. */
  .ib-back { display: inline-flex; flex: none; order: 0; }
  .ib-acts { order: 1; margin-left: auto; }
  .ib-who { order: 2; flex: 1 1 100%; }
  .ib-side { padding: 8px; border-radius: 12px; }
  .ib-top input, .ib-compose textarea { font-size: 16px; }   /* iOS не зумит страницу при фокусе поля */
  .ib-who h2 { font-size: 17px; }
  .ib-guest { max-height: 18vh; }
  .ib-cbar .btn { flex: 1 1 100%; padding: 10px 14px; }
}


/* 07.10 (план «два приложения»): CRM внутри приложения сотрудника. app-shell.js ставит html.in-app и
   viewport-fit=cover — страница рисуется под часами и полоской «домой», поэтому отступы берём из safe-area.
   В браузере env(safe-area-inset-*) = 0, и правила ничего не меняют даже с ?app=1 на компьютере. */
.in-app .topbar { padding-top: calc(8px + env(safe-area-inset-top)); }
/* Раздел во весь экран начинается под часами; полоса над ним — цвета шапки, а не затемнения */
.in-app dialog.dlg-full {
  margin-top: env(safe-area-inset-top);
  height: calc(100dvh - env(safe-area-inset-top)); max-height: calc(100dvh - env(safe-area-inset-top));
  padding-bottom: env(safe-area-inset-bottom);
}
.in-app dialog.dlg-full::backdrop { background: var(--topbar-bg); }
/* Лист снизу: кнопки не прячутся под полоску «домой» */
.in-app dialog:not(.dlg-full) { padding-bottom: env(safe-area-inset-bottom); }
/* PIN в приложении — точками: телефон в руке, цифры видны соседу (на стойке поле прежнее — решение отдельной задачей) */
.in-app #login-pin { -webkit-text-security: disc; }
/* Меню ☰ стоит под шапкой (top: 96px в правиле ниже 900 px) — шапка в приложении выше на отступ под часы */
@media (max-width: 900px) { .in-app #topnav { top: calc(96px + env(safe-area-inset-top)); max-height: calc(78vh - env(safe-area-inset-top)); } }

/* 08.10 «Источники броней» (решение владельца 28.09). Кампания — длинная метка без пробелов (konkurenty_…): переносим где
   угодно, иначе она выдавливает числа за правый край. На телефоне «Другое» (аренда, тренировки — редкость) прячем: сумма и
   отменённые важнее; в файле колонка остаётся. */
.at-t-rows td:nth-child(2) { overflow-wrap: anywhere; }
.at-camp-m { display: none; }
.at-t-rows th:nth-child(2), .at-t-rows td:nth-child(2) { text-align: left; }
.at-filters select { max-width: 100%; }
@media (max-width: 700px) {
  .at-t-ch th:nth-child(5), .at-t-ch td:nth-child(5),
  .at-t-rows th:nth-child(2), .at-t-rows td:nth-child(2),
  .at-t-rows th:nth-child(5), .at-t-rows td:nth-child(5) { display: none; }
  /* Кампания — второй строкой под каналом: отдельной колонкой она рвала и канал, и себя по буквам. */
  .at-camp-m { display: block; font-size: 12px; color: var(--muted); overflow-wrap: anywhere; }
  /* Фильтры — сеткой в две колонки (даты рядом, «считать» и «касание» рядом), арена — на всю ширину: пять полей
     столбиком занимали весь первый экран, до цифр надо было листать. */
  .at-filters { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 8px; }
  .at-filters label { min-width: 0; margin: 0; }
  .at-filters label:nth-child(3) { grid-column: 1 / -1; }
  .at-filters input, .at-filters select { width: 100%; min-width: 0; font-size: 16px; box-sizing: border-box; }
}
