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

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

/* ── Строка списка: имя + мета + действия (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){
  .cs-soon-badge{min-height:28px;}
  .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(--accent);
  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;}
}
