/* campspace-ui.css — общие CSS-компоненты для ЛК персонала/владельца/родителя.
   ROADMAP 5.7 (компонентная библиотека). Требует /tokens.css (переменные --green,
   --r-sm и т.д.) — подключать после tokens.css.
   Классы извлечены из web/crm_scan.html (единственный экран, где паттерны уже
   были явно вынесены в CSS-класс, а не разбросаны инлайн по каждому экрану).
   Канон: docs/DESIGN_TOKENS_BIRCH.md. */

/* ── Badge ── */
.badge{display:inline-flex;align-items:center;padding:3px 10px;border-radius:20px;font-size:12px;font-weight:600;}
.badge.green{background:var(--green-light);color:var(--green);}
.badge.amber{background:var(--amber-light);color:var(--text);}
.badge.rose{background:var(--rose-light);color:var(--rose);}
.badge.sky{background:var(--sky-light);color:var(--sky);}
.badge.dim{background:var(--bg-input);color:var(--text-dim);}
.badge.accent{background:var(--green-light);color:var(--green);}

/* ── Chips (выбор одного из нескольких значений) ── */
.chips{display:flex;gap:8px;flex-wrap:wrap;}
.chip{
  padding:9px 14px;border-radius:var(--r-sm);border:1.5px solid var(--line-mid);
  font-size:13.5px;font-weight:600;color:var(--text-dim);background:var(--bg-card);
  /* Было `transition:all .12s` — `all` анимирует в т.ч. layout-свойства (padding,
     border-width) и заставляет браузер пересчитывать вёрстку каждый кадр.
     Перечисляем только композитные/цветовые свойства (ANIMATION_GUIDELINES.md §4). */
  transition:background-color var(--dur-fast,.16s) linear,
             border-color var(--dur-fast,.16s) linear,
             color var(--dur-fast,.16s) linear,
             transform var(--dur-fast,.16s) var(--ease-out,cubic-bezier(.16,1,.3,1));
  touch-action:manipulation;-webkit-tap-highlight-color:transparent;
}
.chip.active{background:var(--green);border-color:var(--green);color:#fff;}
/* Тактильный отклик на тап (не путать с `.chip.active` — это состояние ВЫБРАН). */
.chip:active{transform:scale(.97);}
@media (prefers-reduced-motion: reduce){ .chip:active{transform:none;} }
/* Тач-пол §3, task_20260914_052406_6ef0cc. `.chip` объявлен тач-элементом
   (touch-action:manipulation + :active-отклик выше), но пола не имел — в
   отличие от соседних канонов `.btn` (§9.8), `.form-ctrl` (§9.2.1) и даже
   локального `.cs-rf-chip`. Живой замер 414px/hasTouch: 36px при норме 44
   (docs/UI_DESIGN_GUIDELINES.md §9.3). Скоуп pointer:coarse, как у .btn —
   мышиная плотность не меняется. */
@media (pointer:coarse){
  .chip{ min-height:44px; display:inline-flex; align-items:center; }
}
/* Числовой счётчик ВНУТРИ чипа («Падения 49», «Вопросы 1»).
   Заведён каноном, а не локальным классом страницы, из-за замеренного дефекта
   (2026-09-25, task_20260922_134255_command_center_ux_visibility_overhaul):
   своя плашка «цветной текст на 12% подложке того же цвета» читается 4.70:1 на
   белом чипе и 1.19:1 на ВЫБРАННОМ — то есть счётчик исчезает ровно на той
   вкладке, которую пользователь открыл. Ошибка неочевидная: автор проверяет
   цвет в покое и не перепроверяет его в состоянии `.active`, где подложка
   меняется на сплошную заливку акцента.
   Поэтому в активном чипе плашка НЕПРОЗРАЧНАЯ и цвета карточки, а чернила
   остаются своими. Полупрозрачно-белая плашка (первая попытка) замер не
   прошла: 3.75:1 в светлой теме и 1.70:1 в тёмной — в тёмной `--green`
   светлее, и белое по белому схлопывается. Непрозрачная подложка снимает
   зависимость от того, какой заливкой красит акцент конкретная тема. */
.chip-count{
  margin-left:6px;padding:1px 6px;border-radius:999px;
  font-size:10px;font-weight:700;font-variant-numeric:tabular-nums;
  background:var(--tint-rose);color:var(--rose);
}
.chip.active .chip-count{ background:var(--bg-card);color:var(--rose); }

/* ── Table (cs-table — безопасный табличный рендер CampUI.renderTable) ── */
.cs-table{width:100%;border-collapse:collapse;font-size:13.5px;}
.cs-table th{
  text-align:left;font-size:11px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;
  color:var(--text-dim);padding:8px 10px;border-bottom:1.5px solid var(--line-mid);
}
.cs-table td{padding:9px 10px;border-bottom:1px solid var(--line);vertical-align:middle;}
.cs-table tbody tr:last-child td{border-bottom:none;}
.cs-table tbody tr:hover{background:var(--bg-card-2);}

/* ── Floating safe-zone (плавающие кнопки не перекрывают контент, UI_DESIGN_GUIDELINES.md §4) ──
   Паттерн для НОВЫХ position:fixed элементов (FAB и т.п.). Скролл-контейнер получает
   .fab-safe-bottom, а --fab-safe-h задаётся под конкретную кнопку (высота + отступ),
   обычно только в мобильном медиа-запросе — на десктопе места обычно достаточно.
   Готовый пример уже отдельно встроен в web/js/campspace_support.js (кнопка «Поддержка»
   не подключает этот файл, стили у неё свои scoped) — если появится второй floating-
   компонент вне campspace_support.js, использовать именно этот класс, а не копировать
   точечный !important-фикс заново. */
.fab-safe-bottom{padding-bottom:var(--fab-safe-h,76px)!important;}

/* ── Child card (аватар + имя + мета + статус-бейдж) ── */
.cs-child-card{display:flex;align-items:center;gap:14px;}
.cs-child-card .cs-avatar{
  width:52px;height:52px;border-radius:var(--r);background:var(--green);color:#fff;
  display:flex;align-items:center;justify-content:center;font-family:var(--disp);font-weight:700;font-size:20px;flex-shrink:0;
}
.cs-child-card .cs-cc-body{flex:1;min-width:0;}
.cs-child-card .cs-cc-name{font-family:var(--disp);font-size:19px;font-weight:700;}
.cs-child-card .cs-cc-meta{font-size:13px;color:var(--text-dim);margin-top:2px;}

/* ── Кнопка (КАНОН docs/UI_DESIGN_GUIDELINES.md §9.8) ──────────────────────
   Поднято сюда 2026-09-13 (task_20260911_202811_5c2117). До этого .btn был
   определён ЛОКАЛЬНО в каждом из 9 ЛК, а здесь правил не было ни одного — при
   том что ROADMAP 5.7 «компонентная библиотека» числился закрытым (§235 плана).

   Канон — не копия «лучшего» файла, а сборка по замеру 9 копий. Три отличия от
   всех до единой локальных версий, каждое по факту, а не по вкусу:

   1) color на заливке — var(--on-accent), НЕ var(--text-inv). В тёмной теме
      --green=#6cc98d светлый, а --text-inv=#ece9e0 тоже светлый: контраст 1.67
      при норме AA 4.5 (с --on-accent=#10150f — 9.13). У .btn-danger на тёмной
      --rose=#f08d84: 1.96 против 7.79. Так было в crm_v2/crm_hr/campspace_parent,
      то есть главная кнопка «Сохранить/Оплатить» там почти нечитаема. Токен
      --text-inv в tokens.css подписан «на тёмном фоне (sidebar)» — на акцентную
      заливку он попал копипастой, его контракт другой.
   2) :hover у primary — токен --green-mid, не filter:brightness() (жил в 4 ЛК с
      двумя разными коэффициентами 1.1/1.08 и осветлял и без того светлый зелёный
      тёмной темы, съедая контраст с текстом).
   3) :focus-visible есть всегда. До канона кольцо фокуса было только у crm_v2 и
      crm_hr — в остальных 7 ЛК клавиатурная навигация была невидима.

   .btn-ok/.btn-danger приведены к ЗАЛИВКЕ (так у 5 ЛК из 9; в crm_v2/crm_hr был
   бледный outline через color-mix, у crm_director — третий вариант). Специфика
   экрана (.btn-primary{width:100%} из owner_lk/campspace_parent) в канон НЕ идёт.

   Каскад: <link> на этот файл стоит во всех ЛК ДО их <style>, поэтому оставшиеся
   локальные копии перебивают канон — подъём вида не меняет, меняет его удаление
   локального блока (§9.8 п.8). Исключение — .btn-sm/.btn-ghost/.btn-danger там,
   где класс в разметке был, а правила не было НИ ОДНОГО (crm_v2 35+1, crm_hr 12,
   campspace_parent 3, crm_director 1, owner_lk 1): канон их оживляет сразу. */
.btn{
  display:inline-flex; align-items:center; gap:6px;
  padding:8px 14px;
  border:1px solid var(--line);
  border-radius:var(--r-sm);
  background:transparent;
  color:var(--text);
  font-family:var(--body); font-size:13px; font-weight:600; line-height:1.2;
  cursor:pointer;
  max-width:100%;
  transition:background .15s, border-color .15s, color .15s, box-shadow .15s, transform .08s;
}
.btn:hover{ border-color:var(--text-dim); }
.btn:active{ transform:translateY(1px); }
.btn:focus-visible{ outline:2px solid var(--green); outline-offset:2px; }
.btn:disabled, .btn[aria-disabled="true"]{
  opacity:.5; cursor:not-allowed; transform:none; box-shadow:none;
}
.btn-primary{ background:var(--green); border-color:var(--green); color:var(--on-accent); }
.btn-primary:hover{ background:var(--green-mid); border-color:var(--green-mid); }
.btn-ok{ background:var(--green); border-color:var(--green); color:var(--on-accent); }
.btn-ok:hover{ background:var(--green-mid); border-color:var(--green-mid); }
.btn-ghost{ background:transparent; border-color:var(--line); color:var(--text-dim); }
.btn-ghost:hover{ background:var(--bg-input); color:var(--text); }
.btn-danger{ background:var(--rose); border-color:var(--rose); color:var(--on-accent); }
.btn-danger:hover{ background:var(--rose); border-color:var(--rose); opacity:.88; }
.btn-wait{ background:var(--amber); border-color:var(--amber); color:var(--on-amber); }
.btn-sm{ padding:6px 12px; font-size:12px; }
/* tap-target §3. Скоуп pointer:coarse, а не max-width (§9.3.1 п.2): у планшета
   768-1024px палец тот же. Мышиную плотность при этом не трогаем.
   .btn-sm возвращается к 44px: «мелкая» кнопка мелкая только под мышью. */
@media (pointer:coarse){
  .btn{ min-height:44px; }
  .btn-sm{ min-height:44px; }
}

/* ── Ряд действий, пригодный для телефона (UI_DESIGN_GUIDELINES.md §9.3) ──
   Заведён 2026-09-10 (task_20260909_123734_scoping_axes_implementation) по факту
   ЖИВОГО замера, а не по ощущению: раздел «Структура лагеря» на 320px давал
   горизонтальный скролл (scrollWidth 330 при docWidth 320) — единственная длинная
   кнопка не помещалась в строку и не переносилась, а тач-высота кнопок в этом ЛК
   40px при норме §3 в 44px.
   ОПТ-ИН ПО-ПРЕЖНЕМУ: с 2026-09-13 канон .btn выше задаёт тач-высоту и max-width
   сам, но ПЕРЕНОС РЯДА (flex-wrap) — свойство контейнера, а не кнопки, и остаётся
   опт-ином. Ставится на КОНТЕЙНЕР ряда действий. */
.cs-actions{display:flex;gap:12px;flex-wrap:wrap;}
.cs-actions > .btn{max-width:100%;}
@media (max-width:768px){
  .cs-actions > .btn{min-height:44px;white-space:normal;text-align:left;}
}
/* .cs-field-44 УПРАЗДНЁН 2026-09-13 (task_20260911_192540_35da75). Он был опт-ином
   на тач-высоту поля; с появлением канона .form-ctrl §9.2 и тач-пола §9.2.1 выше
   44px даёт УМОЛЧАНИЕ, и класс перестал что-либо менять. Проверено гашением его
   правил через CSSOM на шести полях трёх ЛК (#cmail-tpl-subject, #cmail-host,
   #cmail-port, #struct-new-label, #hr-cap-shift, #emailplat-test-to): 44px и с ним,
   и без него. Оставлять опт-ин, который ничего не делает, вреднее, чем удалить:
   он читается как «норму надо не забыть дописать» — ровно та ложная обязанность,
   из-за которой класс стоял у 2 полей из 205. Гвард возврата:
   tests/test_tap_target_field_floor_35da75.py. */

/* ── Поле ввода: input / select / textarea (КАНОН docs/UI_DESIGN_GUIDELINES.md §9.2) ──
   Поднято сюда 2026-09-12 (task_20260909_213823_5d8f67) из web/crm_vrach.html, где
   канон лежал локальной копией вместе с тремя другими ЛК (§9.1.1). До этого правил
   у .form-ctrl не было НИ ОДНОГО ни в одном общем файле — класс на поле стоял, а
   стиля не нёс, и поле рисовалось дефолтом браузера («сырые прямоугольники», §9.1).

   Два сознательных отличия от печатного текста §9.2:
   1. у :disabled фон var(--bg-card), а не --bg-panel: токена --bg-panel в
      web/tokens.css НЕТ (легаси тёмной палитры v1, запрещён гвардом
      test_birch_migration_*), var() без фолбэка обнулил бы фон в прозрачный;
   2. медиа-запрос min-height:44px — по ЗАМЕРУ (§9.1.1 п.2): padding 8/10 при
      font-size 13px даёт 36px, до tap-target §3 сам по себе не дотягивает.

   ГЛОБАЛЬНОЕ (не опт-ин, в отличие от .cs-actions ниже): опасение «width:100%
   сдвинет соседние поля с инлайновой геометрией» проверено живьём при раскатке
   на 4 ЛК и не подтвердилось — у полей во flex-ряду flex-basis перебивает width,
   поля с инлайновым width/max-width не изменились (§9.1.1, таблица замеров).
   crm_v2/crm_hr сведены последними 2026-09-20 (продолжение task_20260909_213823_5d8f67):
   их локальные полные определения (лежали в <style> ПОСЛЕ этого <link> и
   выигрывали каскад целиком) удалены по живому замеру computed style
   (#app-search/#ic-search, testcamp) — расхождение оказалось косметическим
   (padding 2px по вертикали, оттенок focus-кольца). crm_director/crm_kitchen/
   crm_vozhatiy/crm_admin_smeny/crm_vrach сведены раньше (хвост task_20260912_103440_f94e0e).
   §9.1.1 по .form-ctrl закрыто для всех 9 ЛК (у crm_hr отдельно остаётся
   `.form-ctrl{min-width:0}` в тулбаре — не дубль канона, а намеренная дельта
   сжатия поля во flex-ряду, канона не касается). */
.form-ctrl{
  width:100%; min-width:0;
  background:var(--bg-input);
  border:1px solid var(--line);
  color:var(--text);
  padding:8px 10px;
  border-radius:var(--r-sm);
  font-size:13px;
  font-family:var(--body);
  outline:none;
  transition:border-color .15s, box-shadow .15s;
}
.form-ctrl:focus{border-color:var(--green);box-shadow:0 0 0 2px var(--green-light);}
.form-ctrl[aria-invalid="true"],.form-ctrl.is-error{border-color:var(--rose);}
.form-ctrl.is-error:focus{box-shadow:0 0 0 2px var(--rose-light);}
.form-ctrl:disabled,.form-ctrl[readonly]{opacity:.55;cursor:not-allowed;background:var(--bg-card);}
.form-ctrl[type="number"]{font-family:var(--mono);}
textarea.form-ctrl{resize:vertical;min-height:72px;}
@media (max-width:768px){.form-ctrl{min-height:44px;}textarea.form-ctrl{min-height:88px;}}

/* ── Тач-пол для полей БЕЗ .form-ctrl (КАНОН §9.2.1) ──
   Заведено 2026-09-13 (task_20260911_192540_35da75) по ЖИВОМУ замеру, а не по
   ощущению: scripts/mobile_check_bare_fields_35da75.cjs на 360/414px по 7 ЛК дал
   226 видимых полей мельче 44px — и ВСЕ 226 без класса .form-ctrl (поля С классом
   уже 44px каноном выше). Голый <input> без класса и стиля рисуется дефолтом
   браузера: замер даёт 22px при норме tap-target §3 в 44px.

   Почему правило на ЭЛЕМЕНТЕ, а не «дописать класс 449 полям»: §9.1 показала, что
   корень — не небрежность разметки, а отсутствие умолчания. Класс, который надо не
   забыть поставить, по факту не стоит (прецедент .cs-field-44: opt-in при 205
   кандидатах стоял у 2). Умолчание не забывают.

   Три сознательных решения:
   1. СКОУП pointer:coarse, а не max-width — как у канона .btn (§9.3.1 п.2): у
      планшета 768-1024px палец тот же, а плотные ДЕСКТОПНЫЕ раскладки (ряды
      фильтров, таблицы) правило не трогает вообще. Это и снимает главный риск п.3
      постановки: под мышью геометрия не меняется ни на пиксель.
   2. НУЛЕВАЯ СПЕЦИФИЧНОСТЬ исключений (:not(:where(...)) = 0,0,1): это ПОЛ, а не
      диктат. Любой класс/инлайн-стиль перебивает его без !important; собственный
      канон .form-ctrl и локальные копии в <style> страниц продолжают выигрывать.
   3. checkbox/radio/range/color/image/hidden ИСКЛЮЧЕНЫ: у них высота — часть
      глифа, min-height:44px растянул бы флажок в полосу (у директора они явно
      18x18, строка 495 crm_director.html). Тач-цель для флажка — задача обёртки
      <label>, а не самого input, и решается отдельно.
   box-sizing выравнивается здесь же: у голого input дефолт content-box, и без
   этого 44px считались бы ОТ содержимого, давая фактические ~48px. */
@media (pointer:coarse){
  input:not(:where([type="checkbox"],[type="radio"],[type="hidden"],[type="range"],[type="color"],[type="image"])),
  select,
  textarea{
    min-height:44px;
    box-sizing:border-box;
  }
}

/* ── Тач-цель СТРОКИ С ФЛАЖКОМ: .cs-check-row (UI_DESIGN_GUIDELINES.md §9.3.2) ──
   Заведён 2026-09-26 (task_20260921_060606_91684c, экран /esign).

   ЗАЧЕМ. Пол 9.2.1 выше сам исключает checkbox/radio и там же обещает: «тач-цель
   для флажка — задача обёртки <label>, а не самого input, и решается отдельно».
   Отдельно не решалось никогда — канона не существовало. Живой замер экрана
   подписания (scripts/mobile_check_esign_91684c.cjs, chrome-headless, 360/390/414px,
   hasTouch): подпись флажка согласия ПДн — РОВНО 42.0px при норме §3 в 44.
   Две строки текста 14px/1.5 = 42px, и никакой отступ строки этого не лечит:
   кликабельна сама подпись, а не воздух вокруг неё.

   ПОЧЕМУ ОПТ-ИН, а не умолчание на `input[type=checkbox] + label`. Умолчание
   здесь предпочтительнее (см. 9.2.1 п.1: опт-ин, который надо не забыть, по факту
   не стоит), и оно технически выразимо. Но проверить его нечем: в статике
   web/*.html пар «флажок + подпись» РОВНО НОЛЬ — все флажки девяти ЛК рисует JS
   в рантайме, и радиус поражения умолчания измеряется только живым проходом по
   ЛК с логинами. Ставить непроверенное умолчание на все 9 ЛК из задачи про один
   экран — ровно тот класс, за который флот и ловят. Канон заведён здесь, перевод
   его в умолчание вынесен задачей-продолжением с обязательным живым замером.

   Скоуп pointer:coarse — как у .btn (§9.3.1 п.2) и у пола 9.2.1: у планшета палец
   тот же, а десктопные раскладки не двигаются ни на пиксель. Растёт ПОДПИСЬ,
   не флажок: сам input остаётся 18x18 (высота — часть глифа). */
@media (pointer:coarse){
  .cs-check-row > label{
    min-height:44px;
    display:flex;
    align-items:center;
  }
}

/* ── Строка списка: имя + мета + действия (UI_DESIGN_GUIDELINES.md §9.6) ──
   Обобщено 2026-09-12 (task_20260909_213823_5d8f67) из .iso-row/.iso-name/.iso-meta/
   .iso-acts панели изолятора в web/crm_vrach.html. Имя НАМЕРЕННО нейтральное: тот же
   ряд «кто — чем характеризуется — что с ним можно сделать» продублирован в соседних
   ЛК (кухня, вожатый, директор) под своими локальными именами.

   Мобильная часть — не украшение: ряд обязан ПЕРЕНОСИТЬСЯ (flex-wrap), мета —
   переноситься внутри себя (overflow-wrap:anywhere, иначе длинная причина/название
   койки даёт горизонтальный скролл, §7), а кнопки действий на <=768px обязаны брать
   44px (§3 tap-target) — на десктопе 36px, чтобы ряд оставался плотным.
   margin-left:auto прижимает действия вправо только когда ряд НЕ перенесён; после
   переноса он бы оставил кнопки висеть у правого края отдельной строкой, поэтому
   на мобильной ширине снимается. */
.cs-listrow{display:flex;flex-wrap:wrap;align-items:center;gap:8px;width:100%;}
.cs-listrow .cs-listrow-name{font-weight:600;font-size:13px;}
.cs-listrow .cs-listrow-meta{font-size:11px;color:var(--text-dim);min-width:0;overflow-wrap:anywhere;}
.cs-listrow-acts{display:inline-flex;gap:6px;margin-left:auto;}
.cs-listrow-acts .btn{display:inline-flex;align-items:center;justify-content:center;min-width:36px;min-height:36px;padding:4px 8px;font-size:12px;}
@media (max-width:768px){
  .cs-listrow-acts{margin-left:0;}
  .cs-listrow-acts .btn{min-width:44px;min-height:44px;}
}
/* Модификатор для списков-панелей с разделителем строк (§9.6): .item-row/.staff-row/
   .meal-row/.rc-row до миграции держали padding+border-bottom на самой строке —
   .cs-listrow этого не делает (используется и как «голый» inline-ряд). */
.cs-listrow--divided{padding:12px 16px;border-bottom:1px solid var(--line);}
.cs-listrow--divided:last-child{border-bottom:none;}

/* ── Ошибка поля ввода (единый слой валидации, web/js/field_validation.js) ──
   Канон docs/UI_DESIGN_GUIDELINES.md §9.2 п.4: состояние ошибки живёт на
   aria-invalid самого поля (это и стиль, и доступность одним атрибутом), текст
   подсказки — СОСЕДНИЙ элемент, а не title (title не читается с телефона вообще).
   Заведено 2026-09-10, task_20260909_132233_input_validation_unification.

   ⚠️ Селектор НАМЕРЕННО привязан к [data-csv-type] (атрибут ставит валидатор),
   а не к .form-ctrl. Исходная причина (2026-09-10) — «правил .form-ctrl в этом
   файле ещё нет» — снята 2026-09-12 (task_20260909_213823_5d8f67 подняла канон,
   см. секцию выше), но привязка остаётся ОСОЗНАННО: валидатор работает и на полях
   БЕЗ класса .form-ctrl, а !important здесь нужен, чтобы перебить локальные
   определения .form-ctrl в <style> страницы (они идут в каскаде ПОЗЖЕ этого файла
   и иначе вернули бы обычную рамку полю с ошибкой). */
input[data-csv-type][aria-invalid="true"]{
  border-color:var(--rose)!important;
  box-shadow:0 0 0 2px var(--tint-rose);
}
.cs-field-err{
  display:block;
  margin-top:4px;
  font-family:var(--body);
  font-size:12px;
  line-height:1.35;
  color:var(--rose);
  overflow-wrap:anywhere;   /* §7: длинная подсказка переносится, а не режется */
}
@media (max-width:768px){
  /* На телефоне подсказка — единственный канал объяснения ошибки: не мельчим
     ниже 12px (§7.4) и оставляем воздух, чтобы она не липла к следующему полю. */
  .cs-field-err{font-size:12px;margin-bottom:2px;}
}

/* ── Coming soon (web/coming_soon.js — индикаторы «В разработке»/«Запланировано»,
   UI_DESIGN_GUIDELINES.md §9.7) ──
   Заведено 2026-09-13 (task_20260910_085139_1e97d0): движок и попап
   существовали с task_20260909_132233_coming_soon_indicators_sweep, но были
   БЕЗ единого CSS-класса — бейдж/попап рисовались браузерным дефолтом
   (unstyled button/div), хотя смоук-тест это не ловил (проверял только
   ненулевой размер элемента, не факт стилизации). Класс здесь общий для
   ЛЮБОЙ роли — движок один (web/coming_soon.js), реестры per-role
   (web/coming_soon_<role>.js) только регистрируют тексты. */
.cs-soon-badge{
  display:inline-flex;align-items:center;gap:4px;margin-left:8px;
  padding:3px 10px;border-radius:20px;border:none;cursor:pointer;
  background:var(--amber-light);color:var(--amber-mid,var(--amber));
  font-family:var(--body);font-size:11px;font-weight:700;line-height:1.4;
  white-space:nowrap;
}
.cs-soon-badge:hover{background:var(--tint-amber,var(--amber-light));}
.cs-soon-stub{
  display:flex;align-items:center;justify-content:space-between;gap:8px;
  opacity:.7;cursor:pointer;
}
.cs-soon-stub:hover{opacity:1;}
.cs-soon-pop-overlay{
  position:fixed;inset:0;z-index:2000;
  background:rgba(0,0,0,.5);
  display:flex;align-items:center;justify-content:center;
  padding:20px;
  padding-bottom:calc(20px + env(safe-area-inset-bottom,0px));
}
.cs-soon-pop-card{
  width:100%;max-width:420px;max-height:calc(100dvh - 40px);overflow-y:auto;
  background:var(--bg-card);border:1px solid var(--line);border-radius:var(--r,12px);
  box-shadow:var(--shadow-xl,0 20px 60px rgba(0,0,0,.4));
  padding:20px;font-family:var(--body);color:var(--text);
}
.cs-soon-pop-card h3{
  display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  margin:0 0 14px;font-family:var(--disp);font-size:16px;
}
.cs-soon-pop-sec{margin-bottom:12px;}
.cs-soon-pop-sec b{display:block;font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--text-dim);margin-bottom:4px;}
.cs-soon-pop-sec p{margin:0;font-size:13.5px;line-height:1.45;overflow-wrap:anywhere;}
.cs-soon-pop-close{
  width:100%;margin-top:6px;padding:10px 14px;
  border:1px solid var(--line);border-radius:var(--r-sm);
  background:var(--bg-input,var(--bg-card-2));color:var(--text);
  font-family:var(--body);font-size:13.5px;font-weight:600;cursor:pointer;
}
@media (max-width:768px){
  /* tap-target §3: 28px явно ниже канона 44px (§9.6/.chip) — живой замер
     campspace_parent 107x28, task_20260920_224613_3e6398 */
  .cs-soon-badge{min-height:44px;}
  .cs-soon-pop-close{min-height:44px;}
}

/* ── .cs-lazy — плейсхолдер ЛЕНИВОГО раздела экрана (2026-09-22) ─────────────
   Канон §9.10 docs/UI_DESIGN_GUIDELINES.md. Заведён вместе с code-splitting
   React-экранов (web/react/_shared/LazySection.jsx): раздел ЛК грузится
   отдельным файлом по требованию, и на время загрузки его место обязано
   занимать что-то с объявленной высотой — иначе разметка «прыгает» в момент
   появления раздела (CLS), а на медленной сети пользователь видит пустоту и
   считает экран сломанным.
   min-height:120px — не «на глаз»: это высота, ниже которой плейсхолдер уже не
   отличим от пустого контейнера на 360px, самой узкой поддерживаемой ширине §1.
   Отказ загрузки рисуется не здесь, а каноном .empty-state (§9.3). */
.cs-lazy{
  display:flex;align-items:center;justify-content:center;gap:10px;
  min-height:120px;padding:24px 16px;
  font-family:var(--body);font-size:13.5px;color:var(--text-dim);
}
.cs-lazy-spinner{
  width:20px;height:20px;flex:0 0 20px;border-radius:50%;
  border:2px solid var(--line);border-top-color:var(--amber);
  animation:cs-lazy-spin .8s linear infinite;
}
.cs-lazy-text{overflow-wrap:anywhere;}
@keyframes cs-lazy-spin{to{transform:rotate(360deg);}}
/* Уважение к системной настройке «меньше движения» — вращение выключается,
   плейсхолдер остаётся видимым (тот же приём, что у прочих анимаций проекта). */
@media (prefers-reduced-motion:reduce){
  .cs-lazy-spinner{animation:none;}
}

/* ── .cs-notice — баннер-предупреждение экрана с действием (2026-09-25) ──────
   Канон §9.12 docs/UI_DESIGN_GUIDELINES.md. Заведён
   task_20260922_220617_schedule_unmatched_places_banner_counter (баннер
   «Мест не заполнено: N» в расписании админа смены), но класс нейтральный —
   любой счётчик-с-действием внутри страницы («Просрочено: N», «Ждут проверки: N»),
   а НЕ только эта задача.

   Отличие от `#offline-banner` (docs/UI_DESIGN_GUIDELINES.md §1159): тот —
   ГЛОБАЛЬНЫЙ position:fixed поверх всего ЛК, один на страницу, состояние
   сети. `.cs-notice` — ЛОКАЛЬНЫЙ блок ВНУТРИ .page-content конкретного экрана,
   таких на странице может быть несколько, и он несёт число + переход к списку,
   а не просто текст.

   Фон — `--amber-light` (тинт), а НЕ сплошная заливка `--amber`: текст остаётся
   `--text`, а не `--on-amber` — правило «текст на брендовой заливке» (§9, правило 1)
   касается СПЛОШНОЙ заливки акцентом, тинт-фон 12% под этот случай не подпадает
   (тот же расчёт, что у `.badge.amber` выше — `background:var(--amber-light);
   color:var(--text)`, а не `.chip.active`).
   flex-wrap:wrap — на 360px кнопка переносится под текст, а не даёт горизонтальный
   скролл (§7); текст сам переносится через overflow-wrap (длинное число/название
   не режется). Тач-высота кнопки внутри — уже канон `.btn` (§9.8), своей не нужно. */
.cs-notice{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:12px 14px;margin-bottom:12px;
  border-radius:var(--r-sm);border:1px solid var(--line);
  background:var(--bg-card);color:var(--text);
  font-family:var(--body);font-size:13.5px;line-height:1.4;
}
.cs-notice-icon{flex-shrink:0;display:inline-flex;}
.cs-notice-text{flex:1;min-width:0;overflow-wrap:anywhere;}
.cs-notice.warn{
  background:var(--amber-light);
  border-color:color-mix(in srgb, var(--amber) 35%, transparent);
}

/* ── .cs-table-cards — карточная раскладка таблицы на узком экране (2026-09-27) ──
   Канон §9.13 docs/UI_DESIGN_GUIDELINES.md. Обобщение фикса
   task_20260923_070947_220810 (`#finance-wallet-closed-table` в crm_director.html):
   там ячейка действий уезжала за правый край 360px, потому что общий канон
   страницы `.table-wrap table{min-width:560px}` (crm_director.html:167,
   тот же механизм — `.table-scroll table{min-width:560px}` в crm_admin_smeny.html)
   шире любого телефона, а горизонтальный скролл ради последней колонки —
   плохая эргономика для таблицы, где основное действие лежит в последней
   колонке (см. CPP-118, docs/CROSS_PROJECT_PATTERN_REGISTRY.md).

   Опт-ин: добавь класс `cs-table-cards` НА <table> (не на .table-wrap — обёртка
   остаётся, просто на <=480px перестаёт быть тесной) и `data-label="Колонка"`
   на каждый <td>, который должен подписываться в карточном режиме. Колонки без
   data-label (например уже самоочевидная колонка "Имя" как заголовок карточки)
   можно оставить без подписи — просто не проставляй атрибут.
   На >480px класс не меняет ничего — обычная таблица с общим `.table-wrap`.
   На <=480px: thead прячется, каждая строка — карточка, вложенные
   `[data-label]::before` подставляют подписи, `.cs-table-cards-actions` —
   опциональный модификатор для ячейки действий (кнопки растягиваются в ряд,
   тач-цель >=44px под pointer:coarse). */
/* КАСКАД (task_20260927_225059_401c8c). Этот файл подключён в <head> ДО локального
   <style> страницы, поэтому одиночный `.cs-table-cards{min-width:0}` (0,1,0)
   проигрывал `.table-wrap table{min-width:560px}` (0,1,1, crm_director.html) и
   `.table-scroll table` (crm_admin_smeny.html), а `.cs-table-cards td` (0,1,1) —
   залипающей первой колонке `.table-wrap td:first-child` (0,2,1): класс стоял,
   раскладка не менялась. Поэтому конфликтующие правила продублированы с
   префиксом носителя — `.table-wrap table.cs-table-cards …` бьёт локальные
   правила специфичностью, независимо от порядка. Доказательство — живой замер
   tests/live_table_actions_reach_audit_a5ee9a.mjs, а не наличие класса. */
.cs-table-cards,
.table-wrap table.cs-table-cards,
.table-scroll table.cs-table-cards{min-width:0;}
@media(max-width:480px){
  .cs-table-cards,.cs-table-cards tbody{display:block;width:100%;}
  .cs-table-cards thead{display:none;}
  .cs-table-cards tr{
    display:block;border:1px solid var(--line);border-radius:var(--r-sm);
    padding:10px 12px;margin-bottom:10px;
  }
  .cs-table-cards td,
  .table-wrap table.cs-table-cards td,
  .table-scroll table.cs-table-cards td{
    display:flex;gap:10px;justify-content:space-between;align-items:center;
    padding:5px 0;border-bottom:none;position:static;background:none;
    min-width:0;overflow-wrap:anywhere;text-align:right;
  }
  .cs-table-cards td[data-label]::before{
    content:attr(data-label);color:var(--text-dim);font-size:11px;font-weight:700;
    letter-spacing:.05em;text-transform:uppercase;flex-shrink:0;text-align:left;
  }
  .cs-table-cards td[colspan],
  .table-wrap table.cs-table-cards td[colspan]{display:block;text-align:left;}
  .cs-table-cards-actions,
  .table-wrap table.cs-table-cards td.cs-table-cards-actions{
    justify-content:flex-start!important;padding-top:10px!important;
    flex-wrap:wrap;gap:8px;}
  .cs-table-cards-actions .btn{flex:1 1 auto;justify-content:center;margin-left:0!important;}
}
@media(pointer:coarse){.cs-table-cards-actions .btn{min-height:44px;}}
