/* ============================================================
   BAZA SALONA — дизайн-система Rosé Glass (v1)
   Розовые поверхности в точечной сетке, зелёное выпуклое стекло
   для действия и главного факта. Розовый — среда, зелёный — то,
   что нужно нажать или прочитать первым. Третьего цвета нет.
   ============================================================ */

/* Шрифты — свои, с нашего домена (см. public/css/fonts.css): чужой CDN
   получал IP каждого посетителя, то есть шла трансграничная передача. */
@import url('/css/fonts.css');

:root {
  /* поверхности */
  /* 🔴 --rose-tint здесь НЕ объявляем: он живёт в дизайн-системе и сам
     переворачивается по теме. Была строка `--rose-tint: var(--rose-tint)` —
     токен, объявленный через себя. Это цикл, и по спецификации значение
     становится невалидным: в тёмной теме его перебивал [data-theme="dark"]
     дизайн-системы, а в СВЕТЛОЙ фон пропадал у всего, что на нём стоит, —
     дорожка сегмент-контрола на «Подписке» оказалась прозрачной поверх обоев
     (показал Михаил 13.09.2026), и так же молча теряли заливку .btn--tinted,
     чипы и значки строк. */
  --paper:#f2f5f6; --card:#ffffff; --line:#dde5e8;
  /* чернила */
  --ink:#1b2429; --ink-2:#35454d; --ink-3:#4a5a62;
  /* акценты */
  --rose:#16645f; --green:#16645f; --green-deep:#0f4744;
  --plum:#3f5a6b;   /* мост между розовым и зелёным — только в переливе кнопки оплаты */
  /* стекло */
  --glass: linear-gradient(180deg, rgba(30,124,118,.90), rgba(15,71,68,.94));
  --glass-edge: 0 1px 0 rgba(255,255,255,.32) inset,
                0 -14px 22px -14px rgba(0,0,0,.45) inset,
                0 10px 24px -14px rgba(15,71,68,.55);
  /* форма и тень */
  --r: 14px; --r-sm: 10px;
  --sh: 0 1px 2px rgba(27,36,41,.07), 0 10px 28px -16px rgba(15,71,68,.22);
  /* шаг */
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px;
  /* фон: точки ≈5 мм */
  --dots: radial-gradient(circle, rgba(27,36,41,.14) 1.1px, transparent 1.2px);
  --dots-step: 18.9px;

  /* 🔴 Токены СЛОЯ ПРИЛОЖЕНИЯ, которые обязаны переворачиваться вместе с
     темой. Дизайн-система переворачивает свои; эти — наши, и без них тёмная
     тема давала нечитаемое: метка раздела зелёным по тёмному (контраст 2.2),
     строка внутри группы бренда — светло-розовой плашкой поверх тёмной
     карточки со светлым текстом (контраст 1.0, то есть текста не видно). */
  --ink-on-green: var(--green-deep);   /* метки и подписи зелёным по бумаге */
  /* 🔴 Была розовая заливка из БАЗЫ мастеров (rgba(253,234,240,.35)) — розовое
     пятно посреди «Мрамора и петроля». Берём тот же приём, но в палитре
     салона: бирюзовая заливка активного состояния, приглушённая. */
  --bgrp-bg: color-mix(in srgb, var(--rose-tint) 55%, transparent); /* строка внутри группы бренда */
  /* 🔴 Салатовое стекло БАЗЫ (rgba(216,236,203)) в петроле выглядит чужим —
     та же мягкая заливка, но бирюзовая. */
  --ok-soft: linear-gradient(180deg, rgba(213,232,230,.74), rgba(186,217,214,.80));
  --ok-soft-ink: var(--green-deep);
  --ok-soft-line: rgba(255,255,255,.6);

  /* 🔴 Полосатая заглушка фото: одна полоса розовая литералом, вторая —
     бумага. В тёмной теме бумага почти чёрная, и заглушка превращалась в
     контрастную зебру, а подпись внутри пропадала (показал Михаил
     13.09.2026). Обе полосы и чернила — токенами, чтобы переворачивались. */
  --ph-stripe: #e3efee;   /* светлая полоса */
  --ph-base: var(--paper); /* тёмная (вторая) полоса */
  --ph-ink: #4f7a76;
  --ph-cap: var(--green);  /* «ДО» / «ПОСЛЕ» */
  --ph-sub: var(--ink-3);  /* «нажми, чтобы добавить» */
  --ph-drop: #e5f0ef;

  --side: 252px;   /* сайдбар десктопа */
  --rail: 336px;   /* правая колонка */
  --tab: 64px;     /* таб-бар мобильного */
}

[data-theme="dark"] {
  --ink-on-green: var(--green);
  --bgrp-bg: rgba(255, 255, 255, .04);
  --ok-soft: linear-gradient(180deg, rgba(33,88,84,.72), rgba(22,66,63,.80));
  --ok-soft-ink: #c3e4df;
  --ok-soft-line: rgba(255,255,255,.16);
  /* 🔴 Полупрозрачная полоса поверх обоев — это не текстура, а дыра: сквозь
     неё шла фотография, вторая полоса оставалась сплошной чёрной, и надпись
     «нажми, чтобы добавить» пропадала (показал Михаил 14.09.2026). Обе полосы
     плотные: тёмная вдвое светлее бумаги, светлая заметно светлее её, подписи
     белые — заглушка должна читаться на любых обоях. */
  --ph-stripe: #2c4a48;
  --ph-base: #1c3130;
  --ph-ink: rgba(255, 255, 255, .82);
  --ph-cap: #ffffff;
  --ph-sub: rgba(255, 255, 255, .82);
  --ph-drop: #2e4b49;
}

*, *::before, *::after { box-sizing: border-box; }
/* ── Иконки ───────────────────────────────────────────────────────────────
   🔴 Правило: свг-иконка всегда ровная. Инлайновая svg садится на базовую
   линию текста — внутри кнопки это сдвиг вниз на пару пикселей и вид «кривой
   иконки»; на дробном пикселе к этому добавляется мыло. Поэтому иконка внутри
   кнопки и ссылки — блочная, а геометрия считается точно, без хинтинга. */
button > svg, a > svg, .iconbtn svg, .x svg, .pick__b svg, .row__ic svg, .row__chev svg {
  display: block; shape-rendering: geometricPrecision;
}
/* 🔴 overflow-x на html ломает position:fixed на мобильных браузерах —
   панель начинает ехать вместе со страницей. Горизонтального переполнения
   у нас нет (проверено на 390px), поэтому обрезку убрали. */
html, body { margin: 0; padding: 0; }
body {
  font-family: 'Golos Text', system-ui, -apple-system, sans-serif;
  font-size: 15px; line-height: 1.55; color: var(--ink);
  background: var(--paper); background-image: var(--dots); background-size: var(--dots-step) var(--dots-step);
  -webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased;
}
/* ── Обои мастера ─────────────────────────────────────────────────────────
   Своё фото вместо розовой бумаги в точку. Два слоя: сама картинка и вуаль
   поверх — без неё текст на светлом снимке не читается совсем, а на тёмном
   пропадают карточки. Оба слоя fixed: фон не должен ехать вместе со списком,
   иначе на длинном экране мастер видит нижний угол собственной фотографии.
   Силу вуали мастер выбирает сама — у всех разные снимки. */
body.has-wall { background-image: none; }
body.has-wall::before {
  content: ''; position: fixed; inset: 0; z-index: -2;
  background-image: var(--wall); background-size: cover; background-position: center;
}
body.has-wall::after {
  content: ''; position: fixed; inset: 0; z-index: -1;
  background: var(--paper); opacity: var(--wall-dim, 0);
}
/* 🔴 В тёмной теме у вуали есть нижний предел. Со светлой фотографией и вуалью
   на нуле выходило так: карточки и панели тёмные, а фон под ними светлый —
   тема выглядела сломанной. Настройку мастера не трогаем: поднимаем только
   то, что меньше четверти. */
[data-theme="dark"] body.has-wall::after { opacity: max(var(--wall-dim, 0), .28); }
a { color: var(--green); text-underline-offset: 2px; }
a:hover { color: var(--green-deep); }
button, input, select, textarea { font-family: inherit; color: inherit; }
:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }
h1, h2, h3 { margin: 0; font-weight: 600; }

/* ── типографика ──────────────────────────────────────────── */
/* Шкала заголовков — по дизайн-системе. Свои значения были крупнее и жирнее
   (30/700, 22/600, 19/600); на длинных названиях вроде «Уведомление в
   Роскомнадзор» это ломало строку некрасиво. `text-wrap: balance` из системы
   разводит двухстрочные заголовки по равным строкам. */
.h-display { font-size: clamp(28px, 4vw, 40px); font-weight: 600; letter-spacing: -.028em;
  line-height: 1.08; text-wrap: balance; }
.h1 { font-size: 26px; font-weight: 600; letter-spacing: -.022em; line-height: 1.15; text-wrap: balance; }
.h2 { font-size: 20px; font-weight: 600; letter-spacing: -.016em; line-height: 1.25; }
.h3 { font-size: 17px; font-weight: 600; letter-spacing: -.01em; line-height: 1.3; }
.meta { font-size: 13px; line-height: 1.45; color: var(--ink-3); }
.body-2 { font-size: 14px; line-height: 1.5; color: var(--ink-2); margin: 0; }
.num { font-variant-numeric: tabular-nums; }
.big-num { font-size: 48px; font-weight: 600; line-height: .9; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
/* Метка раздела: типографикой, не заливкой; но текст поверх фона живёт на плашке */
.kick {
  display: inline-flex; align-items: center; gap: 8px; width: fit-content;
  font-family: 'IBM Plex Mono', monospace; font-size: 10px; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--ink-on-green);
  background: color-mix(in srgb, var(--card) 90%, transparent); padding: 4px 8px; border-radius: var(--r-xs);
}
.kick b { font-weight: 500; color: var(--ink-3); }
.kick--plain { background: none; padding: 0; }
/* Бэйдж-заголовок стоит вплотную к тому, что под ним — и к карточке, и к
   пунктирной рамке свободного окна. Зазор задаём здесь, а не margin'ом самого
   бэйджа: там, где он живёт внутри .stack, отступ уже даёт gap. */
.kick + .dline { margin-top: 12px; }

/* ── поверхности ──────────────────────────────────────────── */
.card { background: var(--card); border: 1px solid var(--line); border-radius: var(--r); box-shadow: var(--sh); }
.card--pad { padding: var(--s5); }
.plate { background: color-mix(in srgb, var(--card) 90%, transparent); border-radius: var(--r-sm); padding: 10px 14px; width: fit-content; }

/* ── стекло: только действие и главный факт, не больше двух на экран ── */
.glass {
  background: var(--glass); border: 1px solid rgba(255,255,255,.26);
  box-shadow: var(--glass-edge); backdrop-filter: blur(10px) saturate(1.1);
  -webkit-backdrop-filter: blur(10px) saturate(1.1);
  color: #fff; border-radius: var(--r); padding: 20px;
}
.glass .k { font-family: 'IBM Plex Mono', monospace; font-size: 10px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--glass-ink-3); }
.glass h3 { margin: 10px 0 0; font-size: 20px; font-weight: 600; letter-spacing: -.02em; }
.glass .big { display: flex; align-items: baseline; gap: 8px; margin-top: 12px; }
.glass .big b { font-size: 48px; line-height: .9; font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.glass .big span { font-size: 14px; color: var(--glass-ink-2); }
.glass .note { margin: 14px 0 0; padding-top: 14px; border-top: 1px solid rgba(255,255,255,.18);
  font-size: 13px; color: var(--glass-ink-2); }

/* ── кнопки ───────────────────────────────────────────────── */
.btn {
  font: inherit; font-weight: 600; font-size: 15px; line-height: 1;
  border: 1px solid transparent; border-radius: var(--r-sm); padding: 13px 18px;
  cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; transition: background 140ms ease, border-color 140ms ease;
}
.btn--primary {
  background: var(--glass); color: #fff; border-color: rgba(255,255,255,.28);
  box-shadow: var(--glass-edge); backdrop-filter: blur(6px) saturate(1.1);
  -webkit-backdrop-filter: blur(6px) saturate(1.1);
}
/* 🔴 Наведение приехало травяным из БАЗЫ мастеров (rgba(84,136,68)) —
   кнопка под курсором меняла палитру на чужую. Тот же приём в «Мраморе и
   петроле»: то же стекло, только плотнее и глубже (просьба Михаила 17.09.2026). */
.btn--primary:hover { background: linear-gradient(180deg,
  color-mix(in srgb, var(--green) 92%, #fff), var(--green-deep)); }
.btn--ok {
  background: var(--ok-soft);
  color: var(--ok-soft-ink); border-color: var(--ok-soft-line);
  box-shadow: 0 1px 0 var(--ok-soft-line) inset, 0 -8px 14px -10px rgba(9,54,51,.35) inset;
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
}
.btn--quiet { background: var(--card); color: var(--ink); border-color: var(--line); }
/* Тонированная кнопка: заметное второстепенное действие. Видна в обеих темах —
   в отличие от .btn--secondary дизайн-системы, которая в тёмной теме
   превращается в тёмный прямоугольник на тёмной бумаге. */
/* 🔴 Рамка обязательна: в тёмной теме розовая заливка (--rose-tint #3a1f29)
   и карточка (--card #241519) различаются в 1.2 раза — кнопка читалась одной
   надписью без границ. Розовый контур виден в обеих темах. */
.btn--tinted { background: var(--rose-tint); color: var(--rose); font-weight: 600;
  border-color: color-mix(in srgb, var(--rose) 34%, transparent); }
.btn--tinted:hover { background: color-mix(in srgb, var(--rose-tint) 70%, var(--rose) 14%); }
/* 🔴 Цвет наведения стоял литералом (var(--paper-2) — почти белый): в тёмной теме
   текст кнопки светлый, и на такой подсветке он пропадал — Михаил 16.09:
   «при выборе дня шрифт сливается с кнопкой» (кнопки «Сегодня / Завтра /
   Через неделю» в форме дела). Берём токен второй поверхности: он свой в
   каждой теме. И прячем наведение от пальца — на телефоне оно ЗАЛИПАЕТ после
   тапа, поэтому кнопка и осталась подсвеченной на скриншоте. */
@media (hover: hover) and (pointer: fine) {
  .btn--quiet:hover { background: var(--card-2); border-color: var(--line-2); }
}
/* 🔴 Текстовая кнопка — это ДЕЙСТВИЕ, а не подпись. Цвет был самый
   приглушённый (--ink-3), и на стеклянной карточке поверх обоев она пропадала
   совсем: Михаил 15.09 — «кнопка сильно прозрачная, её не видно» (это
   «Выбрать» у клиента в приёме). Берём средний тон, а поверх обоев — полный:
   там под кнопкой чужое фото, и любой приглушённый текст тонет. */
.btn--text { background: none; color: var(--ink-2); border: 0; font-weight: 600; }
.btn--text:hover { color: var(--ink); }
body.has-wall .btn--text { color: var(--ink); }
.btn--block { width: 100%; }
.btn--sm { min-height: 38px; padding: 9px 14px; font-size: 14px; }
/* 🔴 Маленькая кнопка рисуется на 38px, а палец требует 44 (наш стандарт
   мобильного интерфейса). Раньше правило выполнялось только на вид: аудит
   14.09.2026 нашёл на «Сегодня» и «Записях» девять таких целей. Вид не
   трогаем — расширяем зону касания прозрачным полем, как у ручки листа. */
@media (pointer: coarse) {
  .btn--sm, .dmon { position: relative; }
  .btn--sm::after, .dmon::after { content: ''; position: absolute;
    left: 0; right: 0; top: 50%; transform: translateY(-50%);
    height: 44px; }   /* без pointer-events:none — поле обязано ЛОВИТЬ касание */
}
/* 🔴 Тихая текстовая кнопка рядом с подписью раздела («+ добавить», «+ добавить
   фото», «вернуть заготовку»). На вид это ссылка в строку — высота у неё была
   14px, и палец в неё не попадал. Размер оставляем, а хит-зону растягиваем до
   44px псевдоэлементом — тем же приёмом, что у .iconbtn в дизайн-системе
   (память feedback_mobile_ui_standard: сенсор 44px). */
/* 🔴 overflow:visible обязателен: у .btn в системе стоит overflow:hidden
   (он нужен ей для блика загрузки), и он обрезал хит-зону по видимым 14px —
   замер показал «хит 44px», а тык на 12px выше всё равно уходил мимо. */
.btn--inline { min-height: 0; padding: 0; position: relative; overflow: visible; }
.btn--inline::after { content: ''; position: absolute; inset: 50% auto auto 50%;
  width: 100%; height: var(--touch); transform: translate(-50%, -50%); }
.btn[disabled] { opacity: .45; cursor: not-allowed; }
.btn--icon { width: 44px; padding: 0; }

/* ── строка списка ────────────────────────────────────────── */
.row { display: flex; align-items: center; gap: 14px; padding: 14px 18px; border-bottom: 1px solid var(--line);
  width: 100%; text-align: left; background: none; border-left: 0; border-right: 0; border-top: 0; font: inherit; color: inherit; }
.row:last-child { border-bottom: 0; }
.row--link { cursor: pointer; }
.row--link:hover { background: var(--card-2); }
.row b { display: block; font-size: 15px; font-weight: 600; }
.row small { display: block; font-size: 13px; color: var(--ink-3); }
.row .sp { flex: 1; min-width: 0; }
.row__val { font-size: 13px; color: var(--ink-2); font-variant-numeric: tabular-nums; white-space: nowrap; }
.row__key { width: 62px; flex: none; font-family: 'IBM Plex Mono', monospace; font-size: 10px;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-3); }
.row__chev { color: var(--ink-3); flex: none; }

/* Строка инструмента: название сверху, характеристики под ним одной строкой.
   Номер F-004 — мелкой меткой рядом с названием: он нужен статистике, а не
   мастеру, и не должен занимать первую колонку. */
.row--tool { align-items: flex-start; }
.row--tool .sp b { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.tool__code { font-family: 'IBM Plex Mono', monospace; font-size: 10px; letter-spacing: .08em;
  color: var(--ink-3); font-weight: 400; }
/* Характеристики — одной строкой с многоточием: список нужен, чтобы выбрать и
   окинуть взглядом, а подробности всё равно открываются в карточке правки. */
.row--tool small { margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 🔴 На телефоне описание фрезы обрезалось на половине: «твердосплавная ·
   ТМ Кристал…» — а это ровно те признаки, по которым мастер её узнаёт
   (тип, цвет, нарезка, диаметр). Многоточие оставляем десктопу, где строка
   длинная; на телефоне даём двум строкам перенестись. */
@media (max-width: 900px) {
  .row--tool small { white-space: normal; overflow: visible; line-height: 1.35;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
}
/* Выбор инструмента строками: вся строка — переключатель, справа правка и
   удаление. Плиткой было тесно, а длинные названия с диаметром обрезались.
   Отступы поджаты: три круглых элемента справа съедали ширину, а Даше нужно
   видеть наименование с диаметром целиком. */
/* Прошедшая запись в списке «Сегодня»: тише, но читаема — это не отключённая
   строка, в неё по-прежнему можно зайти. */
.row.is-past { opacity: .55; }
/* Число с подписью в правой колонке строки: цифра без слова ничего не значит
   («18» — чего?), а подпись рядом с цифрой не помещается. Ставим столбиком. */
/* Средняя носка в строке клиента: главная цифра дневника, поэтому не серая
   мелочь, а отдельная строка со словом профессии — «носка 18 дней». */
.cwear { margin-top: 3px; font-weight: 600; color: var(--green); }
/* Текстовая часть строки как кнопка: открывает правку, занимает всю свободную
   ширину. Выглядит ровно как обычный текст строки — нажимается вся строка. */
.sp--tap { flex: 1; min-width: 0; text-align: left; font: inherit; color: inherit;
  background: none; border: 0; padding: 0; cursor: pointer; display: block; }
.sp--tap:active { opacity: .7; }
.row.is-past b { font-weight: 500; }

.row--pick { align-items: center; gap: 4px; padding: 12px 10px 12px 12px; }
.row__hit { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px;
  padding: 0; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.row__tick { width: 18px; height: 18px; flex: none; margin-left: 4px; border-radius: 50%; border: 1.5px solid var(--line); }
.row--pick.is-on .row__tick { background: var(--green); border-color: var(--green); position: relative; }
.row--pick.is-on .row__tick::after { content: ''; position: absolute; left: 5px; top: 2px;
  width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg); }
.row--pick.is-on { background: color-mix(in srgb, var(--green) 6%, transparent); }
/* Подпись — одной строкой: имя может занять две, но тип с маркой переносить
   нечего, иначе внизу висит осколок слова. */
/* Выбор инструмента: описание фрезы — то, по чему её узнают. Одна строка с
   многоточием прятала диаметр, который стоит в конце. Даём две строки. */
.row--pick .sp small { white-space: normal; overflow-wrap: anywhere; line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
/* 🔴 Позиция без подписи («диск M 180 грит», «колпачок 150 грит») даёт кнопку
   выбора высотой 23px: у соседей высоту держит вторая строка с типом и маркой,
   а у этих её нет. Отступы строки в 12px сверху и снизу не нажимаются — палец
   мимо. Растягиваем тап-зону на эти отступы псевдоэлементом: вид не меняется,
   высота строки не растёт, а нажимается вся строка целиком (47px).
   Иконки правки и удаления идут в разметке следом, поэтому остаются сверху. */
.row--pick .row__hit { position: relative; }
.row--pick .row__hit::after { content: ''; position: absolute; left: 0; right: 0; top: -12px; bottom: -12px; }
.row--pick .iconbtn { margin-left: 0; }
.row--pick .iconbtn--sm { width: 30px; height: 30px; }
/* Без цвета (диски, колпачки) точки нет — место под неё держим, чтобы имена
   стояли в один столбец. */
.bdot--none { visibility: hidden; }
/* Кнопка добавления — круглая розовая, рядом с «Готово» */
.pb__foot { display: flex; align-items: center; gap: 10px; }
.pb__foot .btn--primary { flex: 1; }
/* ── Отправка сообщения: значок вместо надписи ────────────────────────────
   Михаил 15.09.2026: «нужно просто сделать две кнопки — отправить смс и
   телеграм... свг понятными без надписей». Две длинные строки текста мастер
   читает, два значка — узнаёт. Обе кнопки стоят рядом и всегда на месте:
   пропадающая кнопка телеграма выглядела так, будто функции нет вовсе. */
/* Четыре круглые кнопки зелёного стекла по правому краю — Михаил 15.09:
   «давай сделаем 4 круглые зелёно-стеклянные кнопки справа». Прямоугольные
   плашки во всю ширину читались как четыре разных экрана.
   🔴 Размер задаём и шириной, и высотой: у `.btn` в системе стоит min-height
   44px, и без явной height круг превращался бы в овал на длинном значке. */
.sendrow { display: flex; justify-content: flex-end; gap: 12px; margin-top: 14px; }
.sendrow .btn {
  flex: 0 0 auto; width: 52px; height: 52px; min-height: 52px;
  padding: 0; border-radius: 50%; display: grid; place-items: center;
}
/* Значок центрируется полем 24×24, а не своим рисунком: все пять значков
   приведены к общему полю 18×18 с центром 12/12 (transform в salon.js). */
.sendrow .btn svg { width: 25px; height: 25px; display: block; }
/* 🔴 Канал, которого у клиента нет, НИЧЕМ не отличается на вид: все кнопки
   ряда одинаковые. Приглушение пробовали дважды — сперва гасили кнопку целиком
   (opacity .45), потом только значок (.5), и оба раза Михаил читал это как
   поломку, а не как состояние: «телеграм кнопка тускнее», «сама иконка тоже
   тускнее». Вывод на будущее — блёклый элемент в ряду одинаковых выглядит
   сломанным, каким бы мягким ни было затухание.
   Что канал не заполнен, мастер узнаёт по нажатию: подсказка и переход в
   карточку клиента, где это поле и заводится. */

/* Те же значки в карточке клиента: позвонить, написать, телеграм. */
.cardacts { display: flex; gap: 8px; margin-top: 10px; }
.cardacts .btn { width: 46px; min-height: 46px; padding: 0; display: grid; place-items: center; flex: none; }
.cardacts .btn svg { width: 21px; height: 21px; }

.btn--add { width: 50px; min-height: 50px; padding: 0; flex: none; border: 0; border-radius: 50%;
  background: var(--rose); color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.btn--add:active { transform: scale(.94); }

/* Цветовой индикатор фрезы: маленький кружок цвета насечки. Мастер ищет фрезу
   в подставке глазами, и точка находится быстрее, чем прочитанное слово. */
.bdot { width: 10px; height: 10px; border-radius: 50%; flex: none; display: inline-block;
  border: 1px solid rgba(58,28,38,.18); margin-right: 7px; vertical-align: -1px; }
/* Чип инструмента: точка, форма с диаметром, под ними тип мелким шрифтом */
.chip--tool { display: inline-flex; align-items: center; text-align: left; line-height: 1.25; }
.tool__sub { display: block; font-size: 11px; color: var(--ink-3); font-weight: 400; margin-top: 1px; }
.chip.on .tool__sub { color: rgba(255,255,255,.75); }

.ava { width: 40px; height: 40px; border-radius: 50%; flex: none; display: grid; place-items: center;
  background: linear-gradient(180deg, var(--rose-tint), #cfe3e1); color: var(--rose); font-weight: 600; font-size: 14px; }
.ava--lg { width: 56px; height: 56px; font-size: 20px; }

/* ── фото и заглушки ──────────────────────────────────────── */
.ph { background: repeating-linear-gradient(135deg, var(--ph-stripe) 0 6px, var(--ph-base) 6px 12px);
  border-radius: var(--r-sm); aspect-ratio: 4/3; display: grid; place-items: end start; overflow: hidden; }
.ph span { font-family: 'IBM Plex Mono', monospace; font-size: 10px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ph-ink); padding: 8px; }
.photo { position: relative; display: block; padding: 0; border: 0; overflow: hidden;
  border-radius: var(--r-sm); aspect-ratio: 4/3; background: none; cursor: default; width: 100%; }
.photo img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* Действия на снимке: развернуть · скачать · убрать. На тач-устройствах видны
   всегда — hover там не существует, а прятать действие за долгим нажатием плохо. */
.photo__acts { position: absolute; top: 6px; right: 6px; display: flex; gap: 6px; }
/* 🔴 Действия на снимке: мельче и выпуклым светло-зелёным стеклом — просьба
   Михаила 13.09.2026. Плотная тёмная заливка на фотографии выглядела заплаткой;
   стекло — фирменный приём системы, и на любом снимке оно читается.
   Кружок 26px, но тап-зона добита до 44 псевдоэлементом: уменьшать палец
   вместе с иконкой нельзя (память feedback_mobile_ui_standard). */
.photo__act { position: relative; width: 26px; height: 26px; display: grid; place-items: center;
  border: 1px solid var(--ok-soft-line); border-radius: 50%;
  background: var(--ok-soft); color: var(--ok-soft-ink); cursor: pointer; line-height: 1;
  box-shadow: 0 1px 0 rgba(255,255,255,.65) inset,
              0 -6px 10px -8px rgba(9,54,51,.55) inset,
              0 2px 6px -2px rgba(9,54,51,.38);
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  transition: transform var(--t-instant) var(--e-out), filter var(--t-fast) var(--e-out); }
.photo__act::after { content: ''; position: absolute; inset: 50% auto auto 50%;
  width: var(--touch); height: var(--touch); transform: translate(-50%, -50%); }
.photo__act svg { width: 14px; height: 14px; }
.photo__act:hover { filter: brightness(1.06); }
.photo__act:active { transform: scale(.9); }
/* Действия на снимке стоят тесно — зазор чуть больше, чтобы хит-зоны не
   наезжали друг на друга. */
.photo__acts { gap: 10px; }
.photo__cap { position: absolute; left: 8px; bottom: 8px; padding: 3px 8px; border-radius: var(--r-xs);
  background: color-mix(in srgb, var(--card) 90%, transparent); color: var(--ink); font-family: 'IBM Plex Mono', monospace;
  font-size: 10px; letter-spacing: .12em; text-transform: uppercase; }
@media (hover: hover) {
  .photo__acts { opacity: 0; transition: opacity 140ms ease; }
  .photo--filled:hover .photo__acts, .photo--filled:focus-within .photo__acts { opacity: 1; }
}

/* Пустой слот — сам кнопка загрузки */
.ph--drop { border: 1px dashed color-mix(in srgb, var(--green) 45%, transparent); cursor: pointer; padding: 0; place-items: center;
  width: 100%; font: inherit; color: var(--ph-cap); }
.ph--drop:hover { border-color: var(--green); background: repeating-linear-gradient(135deg, var(--ph-drop) 0 6px, var(--ph-base) 6px 12px); }
.ph__mid { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 0; }
.ph__mid b { font-family: 'IBM Plex Mono', monospace; font-size: 10px; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; }
.ph__mid i { font-style: normal; font-size: 12px; color: var(--ph-sub); }

/* Просмотр во весь экран.
   🔴 Ярус — диалоговый: со старым z-index 90 (до дизайн-системы) снимок лежал
   НИЖЕ шапки (200) и таб-бара (100), и крестик закрытия на телефоне прятался
   под шапкой. Фото открывают и из шторки, поэтому ярус выше шторки, но ниже
   тостов — сообщение об ошибке должно быть видно. */
.lightbox { position: fixed; inset: 0; z-index: var(--z-dialog); display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 16px; padding: 24px;
  background: rgba(14,34,33,.86); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.lightbox__wrap { position: relative; display: block; max-width: min(1100px, 92vw); }
.lightbox img { max-width: min(1100px, 92vw); max-height: 82vh; border-radius: var(--r); display: block; }
/* Действия — значками на самом снимке, как в галерее телефона: подписи
   занимали полосу под фото и повторяли словами понятные иконки
   (просьба Михаила 13.09.2026). Стекло то же, что у значков на плитках. */
.lightbox__acts { position: absolute; right: 12px; bottom: 12px; display: flex; gap: 10px; }
.lightbox__ic { width: 44px; height: 44px; display: grid; place-items: center; cursor: pointer;
  border: 1px solid var(--ok-soft-line); border-radius: 50%;
  background: var(--ok-soft); color: var(--ok-soft-ink); line-height: 1;
  box-shadow: 0 1px 0 rgba(255,255,255,.65) inset,
              0 -8px 12px -9px rgba(9,54,51,.55) inset,
              0 4px 12px -4px rgba(8,32,31,.5);
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  transition: transform var(--t-instant) var(--e-out), filter var(--t-fast) var(--e-out); }
.lightbox__ic svg { width: 19px; height: 19px; }
.lightbox__ic:hover { filter: brightness(1.06); }
.lightbox__ic:active { transform: scale(.92); }
/* Закрытие — в углу экрана, а не на фото: так его находят не глядя.
   🔴 z-index обязателен. Крестик стоит в разметке ПЕРВЫМ, а снимок — вторым;
   оба позиционированы, и без яруса побеждает тот, кто ниже по документу, то
   есть фото. На вертикальном снимке (до 92vw × 82vh) оно накрывало правый
   верхний угол, и крестик уходил под картинку (показал Михаил 14.09.2026).
   Ярусы проставлены обоим явно, чтобы это не зависело от порядка узлов. */
.lightbox__wrap { z-index: 1; }
.lightbox__x { position: absolute; z-index: 2;
  top: calc(env(safe-area-inset-top, 0px) + 14px); right: 14px; }
.photos { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s2); }
.photos--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* ── Виджет верхнего яруса ────────────────────────────────────────────────
   Две карточки одного размера: слева какой сегодня день, справа месяц в
   цифрах. Обе тянутся по высоте выше (align-items:stretch у сетки), поэтому
   содержимое разводится по краям — верх к метке, низ к нижней кромке. */
.wg { display: flex; flex-direction: column; gap: 10px; height: 100%; padding: var(--s4); }
.wg__big { font-size: 40px; line-height: .95; font-weight: 700; letter-spacing: -.03em; margin-top: auto; }
.wg__sub { font-size: 13px; line-height: 1.35; color: var(--ink-3); }
.wg__rows { display: flex; flex-direction: column; gap: 6px; margin-top: auto; }
.wg__row { display: flex; align-items: baseline; gap: 8px; }
.wg__row b { min-width: 26px; font-size: 20px; font-weight: 600; line-height: 1.25; letter-spacing: -.016em; }
.wg__row span { font-size: 12px; line-height: 1.25; color: var(--ink-3); }

.work { background: var(--card); border: 1px solid var(--line); border-radius: var(--r); box-shadow: var(--sh); overflow: hidden; }
.work .ph, .work .photo { border-radius: 0; }
.work__cap { padding: 12px 14px; }
/* 🔴 Подпись под работой не обрезается. Фамилия вроде «Пантелеймоненко-Штерн»
   не переносится сама и упиралась в край карточки, а `overflow:hidden` у .work
   срезал её на полуслове. Имя клиентки должно читаться целиком — оно здесь
   единственный способ понять, чья это работа. */
.work__cap b { display: block; font-size: 15px; font-weight: 600;
  overflow-wrap: anywhere; hyphens: auto; }
.work__cap small { font-size: 13px; color: var(--ink-3); overflow-wrap: anywhere; }
/* На телефоне три колонки оставляли под подпись 81 px — в такую ширину не
   помещается ни одно человеческое имя. Две колонки дают вдвое больше. */
@media (max-width: 620px) {
  .photos { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .work__cap { padding: 10px 12px; }
}

/* ── каркас ───────────────────────────────────────────────── */
.app { display: grid; grid-template-columns: var(--side) minmax(0, 1fr); min-height: 100vh; }

/* Сайдбар должен доезжать до низа при любом росте меню: каждый новый пункт
   отжимал блок подписки, и на ноутбучном экране главная кнопка уходила за край.
   Меню прокручивается само, подписка остаётся прижатой к низу. */
/* Каркас, стекло и строки меню (.navitem) живут в salon-ds.css. Здесь только
   то, чего в системе нет: фирменный знак, прижатая к низу подписка и правила
   для невысокого экрана. */
.side { gap: var(--s3); }
.side__brand { display: flex; align-items: center; gap: 12px; padding: 0 8px var(--s3); }
/* overflow скрыт на всякий случай: что бы ни положили внутрь, круг остаётся кругом */
.side__logo { width: 40px; height: 40px; border-radius: 50%; flex: none; display: grid; place-items: center; overflow: hidden;
  background: linear-gradient(180deg, var(--rose-tint), #cfe3e1); color: var(--rose); font-weight: 700; }
.side__brand b { display: block; font-size: 15px; font-weight: 600; }
.side__brand small { display: block; font-size: 12px; color: var(--ink-3); }
/* ── Карточка с оборотом ──────────────────────────────────────────────────
   Длинные объяснения уезжают на обратную сторону: лицо остаётся рабочим, а
   «почему так» доступно по кнопке. Разгружает экран, ничего не пряча.

   🔴 Грабля из проекта Silver: там лицевая и обратная стороны были разной
   высоты, и карточка при повороте прыгала. Причина — абсолютное
   позиционирование оборота: он выпадал из потока, и высоту задавало только
   лицо. Здесь обе стороны лежат в ОДНОЙ ячейке грида (`grid-area: 1/1`),
   поэтому высота контейнера всегда равна большей из сторон и при повороте не
   меняется ни на пиксель. Никакого absolute.

   🔴 Скрытая сторона не должна ловить нажатия: `backface-visibility` прячет
   её только визуально, кнопки под ней остались бы кликабельными. */
/* 🔴 Карточка с оборотом на широком экране не тянется во всю колонку.
   Михаил 15.09: «карточки у нас длинные и высокие, там текст на обратной
   стороне, лучше сделать прямоугольную, но меньше по ширине». Высоту такой
   карточке задаёт САМАЯ длинная из двух сторон — обычно оборот с подсказкой, —
   а ширину брала вся колонка: выходила полоса 1600×300 с пустотой внутри.
   Ограничиваем ширину — и карточка становится прямоугольником, а текст
   подсказки ложится в нормальные строки, а не в одну длинную.
   На телефоне не трогаем: там ширина и так по экрану. */
.flip { perspective: 1400px; }
@media (min-width: 900px) {
  .flip { max-width: 560px; }
}
.flip__in { display: grid; transform-style: preserve-3d;
  transition: transform var(--t-slow) var(--e-gentle); }
.flip__in > * { grid-area: 1 / 1; min-width: 0;
  -webkit-backface-visibility: hidden; backface-visibility: hidden; }
/* 🔴 Оборот остаётся В ПОТОКЕ ячейки, как и лицо. Вывести его из потока
   (absolute + прокрутка внутри) я попробовал 15.09, чтобы убрать пустой низ у
   «Как держится покрытие?», — и наткнулся на собственное предупреждение в
   `salon-glass.css`: ровно это уже пробовали 14.09, оно стоило трёх неверных
   диагнозов и дало пропадающую карточку на телефоне Михаила. Следствие, с
   которым живём сознательно: высота карточки равна БОЛЬШЕЙ стороне, поэтому
   текст оборота пишется соразмерным лицу. Пустоту лечим подрезкой подсказки,
   а не позиционированием. */
.flip__back { transform: rotateY(180deg); }
.flip.is-flipped .flip__in { transform: rotateY(180deg); }
.flip:not(.is-flipped) .flip__back,
.flip.is-flipped .flip__front { pointer-events: none; }

/* 🔴 Место под кнопку резервирует ТОЛЬКО верхний блок карточки, а не вся
   карточка. Раньше отступ стоял на `.card--flip` целиком — и всё, что внутри
   центрируется (пикер срока, чипы, пустые состояния), уезжало влево на
   половину резерва: «кнопка нарушила центрование контента» (Михаил
   14.09.2026). Кнопка висит в правом верхнем углу, значит мешает она только
   первой строке — ей отступ и нужен. */
.card--flip { position: relative; }
/* 🔴 Резерв даём ЗАГОЛОВКАМ, а не «первому блоку чему угодно». Правило
   `> :first-child` сузило первую группу брендов в «Базе» на 34px — и подложки
   строк стали разной длины (показал Михаил 14.09.2026). Первым ребёнком
   карточки бывает список, сетка или группа: у них отступ уезжает в содержимое.
   Заголовок же кнопке действительно мешает — ему и место. */
.card--flip > .h1, .card--flip > .h2, .card--flip > .h3,
.card--flip > h1, .card--flip > h2, .card--flip > h3,
.card--flip > .kick, .card--flip > .hd, .card--flip > .sect__head,
.card--flip > b, .card--flip > .wg > .kick:first-child { padding-right: 34px; }
/* Когда карточка начинается сразу со списка по брендам, кнопка висит над
   шапкой первой группы — резерв нужен ей, а не строкам под ней. */
.card--flip > .bgrp:first-child > .bgrp__h { padding-right: 46px; }
/* Заголовок оборота — тот же случай: кнопка «назад» стоит над ним. */
.flip__back .flip__ttl { padding-right: 34px; }
/* 🔴 Без кружка (просьба Михаила 14.09.2026): «убери из-под них тёмные круги,
   тогда не придётся центровать относительно этих кругов». Голая иконка не
   спорит с карточкой и не требует оптической подгонки внутри подложки.
   Область нажатия остаётся 44px — её держит ::after ниже. */
.flip__btn { position: absolute; top: var(--s4); right: var(--s4);
  width: 30px; height: 30px; display: grid; place-items: center;
  border: 0; border-radius: 0; background: none; padding: 0;
  color: var(--ink-3); cursor: pointer;
  transition: color var(--t-fast) var(--e-out), transform var(--t-fast) var(--e-out); }
.flip__btn::after { content: ''; position: absolute; inset: 50% auto auto 50%;
  width: var(--touch); height: var(--touch); transform: translate(-50%, -50%); }
.flip__btn:hover { color: var(--rose); }
.flip__btn svg { width: 19px; height: 19px; }
/* Оборот — это пояснение: тише лицевой стороны, но читаемо */
.flip__back .flip__ttl { display: block; font-size: 17px; font-weight: 600; margin-bottom: var(--s3); }
.flip__back p { margin: 0 0 var(--s3); color: var(--ink-2); font-size: 14px; line-height: 1.5; }
.flip__back p:last-child { margin-bottom: 0; }
@media (prefers-reduced-motion: reduce) {
  .flip__in { transition: none; }
}
/* На узком экране объяснение всё равно длиннее — разница высот съедает экран.
   Поворот работает так же, просто без перспективы: она на 375px не читается. */
@media (max-width: 600px) { .flip { perspective: none; } }

/* ── Сайдбар: группы и свёрнутое «Ещё» ───────────────────────────────────
   Плоский список из шестнадцати строк не влезал на ноутбук и прокручивался.
   Строки поджаты, между группами — mono-метка, второстепенное свёрнуто. */
.side__nav { gap: 1px; }
.side__k { display: block; margin: var(--s4) 0 var(--s1); padding: 0 var(--s3);
  font-family: 'IBM Plex Mono', monospace; font-size: 10px; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; color: var(--ink-3); }
.side__k:first-child { margin-top: 0; }
/* Кнопка раскрытия: выглядит как строка меню, но ведёт себя как группа */
.side__more { display: flex; align-items: center; gap: var(--s3); width: 100%;
  margin-top: var(--s3); padding: var(--s2) var(--s3); min-height: var(--hit);
  border: 0; background: none; border-radius: var(--r-sm); cursor: pointer;
  color: var(--ink-3); font: inherit; font-size: 14px; text-align: left; }
.side__more:hover { background: color-mix(in srgb, var(--rose-tint) 60%, transparent); color: var(--ink); }
.side__more-ch { display: flex; flex: none; transition: transform var(--t-fast) var(--e-out); }
.side__more[aria-expanded="true"] .side__more-ch { transform: rotate(90deg); color: var(--rose); }
.side__more-n { margin-left: auto; font-family: 'IBM Plex Mono', monospace; font-size: 12px;
  color: var(--ink-4); font-variant-numeric: tabular-nums; }
.side__more[aria-expanded="true"] .side__more-n { opacity: 0; }
.side__extra { display: flex; flex-direction: column; gap: 1px; }

/* 🔴 Сенсор требует 44px (память feedback_mobile_ui_standard), мышь — 32px.
   В системе .navitem рассчитан на мышь; на планшете добираем до пальца. */
@media (pointer: coarse) { .navitem { min-height: var(--touch); } }
.side__tail { margin-top: auto; }   /* «Подписка» прижата к низу, но над чертой */
/* Невысокий экран (ноутбук 13", окно не на весь рост): ужимаем вертикаль, а не
   прячем пункты — иначе кнопка оплаты оказывается ниже видимой части. */
@media (min-width: 901px) and (max-height: 900px) {
  .side { gap: var(--s2); padding-top: var(--s4); padding-bottom: var(--s4); }
  .navitem { min-height: 34px; }
  .side__foot { padding-top: var(--s3); }
  .side__foot .meta { margin-bottom: 8px; }
}
/* Подписка прилипает к низу сайдбара: это единственное платное действие в
   приложении, и оно не должно уезжать под край, сколько бы пунктов ни выросло
   в меню. Фон нужен обязательно — под ним прокручиваются пункты меню, и текст
   не должен просвечивать. Цвет берём РОВНО тот же, что у самого меню, иначе
   подвал читается отдельным прямоугольником. */
/* 🔴 Подложка идёт ДО НИЗА экрана. У сайдбара есть нижний отступ (--s4), и
   прилипший подвал кончался на шестнадцать пикселей выше края: в эту щель при
   прокрутке просвечивали пункты меню — Михаил показал, как под подложкой едет
   «Подписка» (17.09.2026). Лечится двумя строками сразу: `bottom` уводим на тот
   же отступ вниз (sticky прилипает к ВНУТРЕННЕЙ границе контейнера, и одного
   отрицательного поля мало), а сам отступ возвращаем внутренним padding —
   иначе «В кабинет» ляжет на самый край. */
.side__foot { position: sticky; bottom: calc(var(--s4) * -1); z-index: 2;
  padding-top: var(--s4); border-top: 1px solid var(--line);
  margin-bottom: calc(var(--s4) * -1); padding-bottom: var(--s4);
  background: color-mix(in srgb, var(--card) 72%, transparent);
  backdrop-filter: blur(10px) saturate(1.05); -webkit-backdrop-filter: blur(10px) saturate(1.05); }
.side__foot .meta { margin-bottom: 12px; }

/* 🔴 Обои и меню. Михаил трижды показывал на этот угол: «странный белый фон»,
   потом «тут по-прежнему фон». Причина была не в цвете подвала, а в том, что
   меню полупрозрачное (72%), а подвал плотнее (88%). Поверх розовой бумаги
   разницы не видно, а поверх тёмной фотографии меню темнеет, подвал остаётся
   светлым — и выглядит заплаткой, наклеенной на снимок.
   Решение: меню — это не содержимое, а обрамление. Поверх обоев оно делается
   СПЛОШНЫМ, тем же цветом, что карточка. Фотография остаётся там, ради чего
   её и ставят, — в рабочей области. Двух плотностей больше нет, поэтому и
   заплатке взяться неоткуда. Размытие снимаем: размывать нечего. */
body.has-wall .side,
body.has-wall .side__foot,
/* Плашка заголовка экрана — по той же причине: на 90% она подкрашивалась
   фотографией и переставала совпадать с карточками под собой. */
body.has-wall .hd__t,
/* Бэйджи разделов («ФОТО РАБОТ», «ПОСЛЕДНИЕ РАБОТЫ») — тоже плашки, и тоже
   подкрашивались снимком. У `.kick--plain` фона нет вовсе: он лежит прямо на
   фотографии, его красит в белый markWall, и сюда он не попадает. */
body.has-wall .kick:not(.kick--plain),
/* То же на телефоне: верхняя полоса, её круглые кнопки и таб-бар. Там
   плотности были 0.92, 0.90 и 0.94 — поверх снимка это три разных оттенка
   подряд, и рамка приложения переставала быть одним целым. */
body.has-wall .toolbar {
  background: var(--card);
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
/* 🔴 Поверх обоев низ тоже БЕЗ цвета (15.09.2026). Здесь стоял сплошной
   зелёный градиент, и он перебивал общее нейтральное правило: у Михаила обои
   включены, поэтому бар оставался зелёным, а круглая кнопка «Ещё» — которой
   это правило не касается — уже стала бесцветной. Отсюда и «не сработало».
   Плотность поверх обоев выше (.44 против .30 на чистом фоне): под баром
   оказывается чужое фото, и подписи разделов должны читаться на любом —
   и на светлом, и на пёстром. Размытие возвращаем: без него плёнка поверх
   снимка выглядит грязным пятном, а не стеклом. */
body.has-wall .tabbar, body.has-wall .tabmore {
  background: rgba(18, 12, 14, .44);
  -webkit-backdrop-filter: blur(18px) saturate(1.3); backdrop-filter: blur(18px) saturate(1.3);
}
/* Поверх обоев выбранный цвет плотнее (.52 против .34): под панелью лежит
   чужое фото, и подписи разделов должны читаться на любом — светлом, пёстром,
   контрастном. Ниже правил `body[data-bar]` по файлу, поэтому выигрывают они. */
body.has-wall[data-bar="green"] .tabbar, body.has-wall[data-bar="green"] .tabmore { background: rgba(48, 92, 40, .52); }
body.has-wall[data-bar="rose"]  .tabbar, body.has-wall[data-bar="rose"]  .tabmore { background: rgba(132, 50, 84, .52); }
body.has-wall[data-bar="plum"]  .tabbar, body.has-wall[data-bar="plum"]  .tabmore { background: rgba(78, 46, 102, .52); }
body.has-wall[data-bar="ocean"] .tabbar, body.has-wall[data-bar="ocean"] .tabmore { background: rgba(26, 70, 96, .52); }
body.has-wall[data-bar="sand"]  .tabbar, body.has-wall[data-bar="sand"]  .tabmore { background: rgba(116, 84, 38, .52); }

.main { min-width: 0; display: flex; flex-direction: column; }
.screen { display: none; padding: var(--s6); }
.screen.on { display: block; }

.hd { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s5); margin-bottom: var(--s5); }
.hd__t { background: color-mix(in srgb, var(--card) 90%, transparent); border-radius: var(--r-sm); padding: 12px 16px; }
.hd__t .meta { margin-top: 2px; }

.cols { display: grid; grid-template-columns: minmax(0, 1fr) var(--rail); gap: var(--s5); align-items: start; }
.stack { display: flex; flex-direction: column; gap: var(--s5); min-width: 0; }
.stack--tight { gap: var(--s4); }

/* Инструкция: слева пять шагов, справа семь карточек с правилами. Правая
   колонка кончалась метров на пять ниже левой, и добрая половина экрана была
   пустой. На широком экране раскладываем её в две колонки, а шаги прибиваем —
   они короткие и должны оставаться перед глазами, пока читаешь правила. */
@media (min-width: 1100px) {
  #s-help .cols { grid-template-columns: minmax(0, 340px) minmax(0, 1fr); }
  #s-help #helpSteps { position: sticky; top: var(--s5); }
  #s-help #helpAside { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); align-content: start; }
}
/* Планшет и узкий ноутбук: колонки одна под другой, но карточки правил — парами,
   иначе полоса текста в одну колонку тянется бесконечно. */
@media (min-width: 620px) and (max-width: 1099px) {
  #s-help #helpAside { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); align-content: start; }
}

.seg { display: flex; gap: var(--s2); flex-wrap: wrap; }
.seg__item { background: var(--card); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 8px 16px;
  font: inherit; font-size: 14px; color: var(--ink-2); cursor: pointer; }
.seg__item--on { background: var(--rose-tint); border-color: color-mix(in srgb, var(--rose-tint) 62%, var(--rose) 18%); color: var(--rose); font-weight: 600; }
/* ── Сегмент-контрол ──────────────────────────────────────────────────────
   🔴 Пачка одинаковых пилюль не показывала, что это ОДИН переключатель: они
   читались как четыре отдельные кнопки, а на телефоне ещё и разъезжались в
   две строки или срезались краем при прокрутке. Теперь это дорожка с одним
   бегунком: он переезжает под палец, и выбранное состояние видно движением,
   а не только цветом. Дорожка включается только для двух-четырёх вариантов
   (класс ставит syncSeg в salon.js) — фильтр медиатеки с десятком клиенток
   остаётся обычными пилюлями с переносом. */
.seg--control {
  position: relative; display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: 0; padding: 4px; background: var(--rose-tint); border-radius: var(--r-pill);
}
.seg--control .seg__item {
  position: relative; z-index: 1; border: 0; background: none; min-height: 40px;
  padding: 8px 4px; border-radius: var(--r-pill); text-align: center; font-size: 13px;
  line-height: 1.2; color: var(--ink-2); transition: color .18s ease;
  white-space: nowrap; overflow: hidden; text-overflow: clip;
}
.seg--control .seg__item--on { background: none; border-color: transparent; color: var(--rose); font-weight: 600; }
.seg__thumb {
  position: absolute; z-index: 0; top: 4px; bottom: 4px; left: 4px;
  width: calc((100% - 8px) / var(--seg-n, 2));
  background: var(--card); border-radius: var(--r-pill);
  box-shadow: 0 1px 2px rgba(58,28,38,.10), 0 6px 14px -10px rgba(58,28,38,.34);
  transform: translateX(calc(var(--seg-i, 0) * 100%));
  transition: transform .26s cubic-bezier(.22,.9,.28,1);
}
.seg:not(.seg--control) .seg__thumb { display: none; }
.seg--none .seg__thumb { opacity: 0; }
@media (prefers-reduced-motion: reduce) { .seg__thumb { transition: none; } }

/* Пустое состояние по дизайн-системе: знак в розовой плитке, заголовок,
   одна поясняющая строка — всё по центру. Вертикальные отступы взяты меньше
   системных (--s6 вместо --s7): у нас пустое состояние почти всегда лежит
   внутри карточки, и 48px сверху отрывали знак от её края. */
.empty { display: flex; flex-direction: column; align-items: center; gap: var(--s3);
  padding: var(--s6) var(--s4); text-align: center; }
.empty__ic { width: 56px; height: 56px; flex: none; border-radius: var(--r-lg);
  display: grid; place-items: center; background: var(--rose-tint); color: var(--rose); }
.empty h3 { margin: 0; font-size: 17px; font-weight: 600; }
.empty p { margin: 0; max-width: 38ch; color: var(--ink-3); font-size: 14px; line-height: 1.5; }

/* вопрос-карточка проверки носки (мобильный сценарий из макета) */
.ask { padding: var(--s4) var(--s4) var(--s3); }
.ask__who { display: flex; align-items: center; gap: 12px; }
.ask__who b { display: block; font-weight: 600; }
.ask__who small { display: block; font-size: 13px; color: var(--ink-3); }
.ask__q { margin: var(--s4) 0 var(--s3); font-size: 17px; font-weight: 600; line-height: 1.3; }
.ask__btns { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2); }
/* 🔴 Ответы приходят из словаря профессии и бывают длинными: «Вымылся быстро»
   у волос не влезал в половину карточки уже на 375px, а `.btn` в дизайн-системе
   режет по `overflow:hidden` — надпись молча обрывалась. Здесь переносим, а не
   режем: правило «надписи не обрезаются» сильнее правила «кнопка в одну строку». */
.ask__btns .btn { white-space: normal; line-height: 1.25; padding-inline: var(--s2);
  height: auto; min-height: 44px; }
.ask__more { margin-top: 6px; }

.tab-note { display: flex; align-items: center; gap: 10px; padding: 12px 16px; }

/* нижняя навигация мобильного */
.tabs { display: none; }
/* Сам таб-бар и его строки (.tabitem) — в salon-ds.css: плавающее стекло,
   сворачивается при прокрутке вниз (это делает salon-ui.js). Здесь — только
   правило узкого экрана. */
/* 🔴 Таб-бар НЕ прячется при прокрутке. В дизайн-системе он сворачивается
   вниз (`BAZA.minimizingTabBar`, его зовёт autoInit эталонного salon-ui.js), и
   на бумаге это выглядит красиво — но в работе получилось так: «нижний тулбар
   на мобиле пропадает при прокрутке, и если скроллить медленно, то он так и
   не появится, пока сильно не потянешь страницу вверх» (Михаил 13.09.2026).
   Причина в самом приёме: порог возврата считается от накопленного сдвига, и
   при медленной прокрутке палец до него не дотягивает.
   Навигация в рабочем приложении должна быть на месте всегда — шестьдесят
   четыре пикселя не стоят того, чтобы мастер ловила панель. Эталон не трогаем:
   класс по-прежнему ставится, просто ничего не меняет. */
.tabbar.is-minimized { transform: none; opacity: 1; }

/* ── «Ещё» отдельной круглой кнопкой справа ───────────────────────────────
   Решение Михаила 15.09.2026: «разделим нижний тулбар на два. В первом все
   кнопки кроме „ещё“. „Ещё“ будет отдельный справа стоящий тулбар круглый, по
   высоте точно такой же как и крупный, без надписи, просто с тремя полосками».
   Пять подписанных разделов и вход в список остальных — разные вещи, и в одном
   ряду «Ещё» отбирало у них шестую часть ширины.
   🔴 Стекло, обводку и тень берём ТЕМИ ЖЕ токенами, что у `.tabbar` в
   дизайн-системе: иначе два соседних элемента на одной линии дают чуть разный
   оттенок, и это читается как случайность. Сам `salon-ds.css` не трогаем. */
/* 🔴 Пока открыта клавиатура, нижние панели убираем совсем. Класс `kb-open`
   ставит дизайн-система (salon-ui.js), но в салоне им никто не пользовался — и
   на телефоне таб-бар с круглой кнопкой наезжал на поле ввода и на текст под
   ним. Во время набора они не нужны: человек печатает, а не переключает
   разделы. Появляются обратно, как только клавиатура закрылась. */
body.kb-open .tabbar,
body.kb-open .tabmore,
body.kb-open .bm-act { display: none !important; }

.tabmore { display: none; }
@media (max-width: 900px) {
  /* Бар ужимаем ровно на круг и зазор, чтобы правый край остался на месте. */
  .tabbar { right: calc(max(var(--s3), var(--sa-r)) + var(--tab) + var(--s2)); }
  .tabmore {
    display: grid; place-items: center;
    position: fixed; z-index: var(--z-nav);
    right: max(var(--s3), var(--sa-r));
    bottom: calc(var(--s3) + var(--sa-b));
    width: var(--tab); height: var(--tab); padding: 0;
    border-radius: 50%; border: 1px solid rgba(255,255,255,.22);
    color: var(--glass-ink-2);
    box-shadow: var(--glass-edge), var(--sh-over);
    -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
    font: inherit; cursor: pointer;
  }
  /* 🔴 Низ приложения — БЕЗ цвета, той же плёнкой, что кнопки на фото в
     медиатеке (`.phbtn`). Михаил 15.09: «мелкий тулбар стал бесцветным после
     того как ты поработал с кнопками в медиатеке — они тоже бесцветные и это
     норм». Сперва я принял за образец прозрачный зелёный (`--glass-clear`) —
     не вышло: зелёный остаётся зелёным, сколько его ни разбавляй. Образец был
     другой: нейтральная тёмная плёнка, сквозь которую виден фон, а цвет берёт
     на себя содержимое экрана, а не панель.
     ⚠️ Правило стоит ПОСЛЕ блока `.tabmore`: при равной специфичности
     выигрывает последнее, и на прежнем месте круг перебивал его своим фоном. */
  .tabbar, .tabmore {
    background: rgba(18, 12, 14, .30);
    border-color: rgba(255, 255, 255, .20);
  }

  /* ── Цвет нижних панелей ────────────────────────────────────────────────
     Выбор мастера (просьба Михаила 15.09.2026: «в настройках выбор цвета двух
     нижних тулбаров, 5-6 цветов разных, таких же стеклянных… в том числе и
     прозрачный как сейчас»). Признак ставится на <body> как `data-bar`, сами
     цвета — только здесь: из настроек приходит ИМЯ, а не стиль.
     🔴 Все варианты стеклянные — полупрозрачные, с тем же размытием. У цветных
     плотность одна на всех (.34), чтобы выбор менял оттенок, а не «тяжесть»
     панели; «без цвета» чуть легче (.30) — там нечему проступать, и лишняя
     плотность только глушила бы фон.
     Текст держим светлым нейтральным: системный `--glass-ink-2` зеленоват и на
     розовой или синей панели выглядит грязным. */
  body[data-bar] .tabbar, body[data-bar] .tabmore { border-color: rgba(255,255,255,.24); }
  body[data-bar] .tabitem, body[data-bar] .tabmore { color: rgba(255,255,255,.82); }
  body[data-bar] .tabitem[aria-current="page"] { color: #fff; }

  body[data-bar="green"] .tabbar, body[data-bar="green"] .tabmore { background: rgba(58, 106, 48, .34); }
  body[data-bar="rose"]  .tabbar, body[data-bar="rose"]  .tabmore { background: rgba(154, 62, 98, .34); }
  body[data-bar="plum"]  .tabbar, body[data-bar="plum"]  .tabmore { background: rgba(92, 56, 118, .34); }
  body[data-bar="ocean"] .tabbar, body[data-bar="ocean"] .tabmore { background: rgba(32, 82, 112, .34); }
  body[data-bar="sand"]  .tabbar, body[data-bar="sand"]  .tabmore { background: rgba(134, 98, 46, .34); }

  /* Открыт раздел из «Ещё» — кнопка подсвечена так же, как активная вкладка. */
  .tabmore[aria-current="page"] { color: #fff; background: rgba(255,255,255,.16); }
}

/* 🔴 Надпись не режется никогда (память feedback_mobile_ui_standard).
   В системе у .tabitem стоит letter-spacing:.01em — на «Клиенты» это ровно
   тот один лишний пиксель, из-за которого включалось многоточие и подпись
   читалась «Клиен…». Разрядку у подписи убираем, у иконок она не нужна. */
.tabitem__label { letter-spacing: 0; }
/* 🔴 И ещё три пикселя: у .tabitem в системе padding не сброшен, а браузер
   даёт кнопке свои 1px 6px. На шести кнопках в 375px это съедало 12px из 55,
   и «Клиенты» всё равно упиралось в многоточие. */
.tabitem { padding: 2px; }
/* Шесть кнопок на 320px — 50px на ячейку: уменьшаем шрифт и зазор,
   а не прячем подпись под многоточие. */
@media (max-width: 359px) {
  .tabitem { gap: 2px; }
  .tabitem__label { font-size: 9.5px; }
  .tabitem svg { width: 20px; height: 20px; }
}

/* ── Главное действие экрана ──────────────────────────────────────────────
   🔴 Кнопка «Добавить» стояла на экране дважды: компактно в шапке и во всю
   ширину под списком. Обе выглядели одинаково второстепенными, а нижняя
   уезжала вместе со списком — на двадцати клиентках до неё надо было
   доскроллить. Теперь действие одно, зелёное и всегда на месте.
   Не во всю ширину: это кнопка, а не плашка, и по ширине она равна своей
   подписи. Слева — потому что справа над таб-баром живёт пузырь переписки. */
.bm-act {
  display: none; position: fixed; z-index: 38;
  /* Бар в системе плавающий: он отстоит от низа на --s3, поэтому кнопке
     нужно 12 (зазор бара) + 64 (его высота) + 12 (свой воздух). */
  left: 16px; bottom: calc(var(--tab) + env(safe-area-inset-bottom) + 26px);
  max-width: calc(100vw - 96px);           /* правее — пузырь переписки */
  align-items: center; gap: 8px; min-height: 46px; padding: 0 20px 0 16px;
  border-radius: var(--r-pill); border: 1px solid rgba(255,255,255,.26);
  background: var(--glass); color: #fff; box-shadow: var(--glass-edge);
  backdrop-filter: blur(10px) saturate(1.1); -webkit-backdrop-filter: blur(10px) saturate(1.1);
  font: inherit; font-size: 15px; font-weight: 600; cursor: pointer;
  transition: transform .12s ease;
}
.bm-act span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bm-act svg { flex: none; }
.bm-act:active { transform: scale(.97); }
/* 🔴 Пока мастер листает вниз, кнопка уезжает: она закрывала строки списка —
   на «Сегодня» имя клиента, в «Клиентах» целую строку. Палец пошёл вверх —
   кнопка возвращается. Пружину нажатия это не трогает: у неё свой transform,
   поэтому уводим кнопку в отдельном состоянии и без анимации масштаба. */
.bm-act.is-away { transform: translateY(calc(var(--tab) + 40px)); opacity: 0; pointer-events: none; }
.bm-act { transition: transform .2s ease, opacity .2s ease; }
@media (prefers-reduced-motion: reduce) { .bm-act { transition: none; } }

/* ── Граница слоёв: где командует система, а где мы ───────────────────────
   🔴 Правило, выведенное кровью (тумблер на первом же экране условий
   обработки данных): каскад работает ПОСВОЙСТВЕННО. Если наше правило не
   задаёт свойство, оно доезжает из системы — и получается гибрид: наш трек
   44×26 плюс кружок системы 25px, который вылезал за край. Поэтому на каждый
   общий класс нужно явное решение, а не «как-нибудь сложится».

   Разобрано пофайлово (см. docs/DESIGN-SYSTEM-MIGRATION.md):
   — Взяли из системы целиком: .side/.navitem, .tabbar/.tabitem, .toolbar,
     .sheet со всей начинкой, .switch__track, .iconbtn (хит-зона 44px
     псевдоэлементом), .dialog, .toast, .backdrop.
   — Оставили своим: .card, .btn, .chip, .row, .empty, .field, .kick и всё,
     что ниже в этом файле.
   Ниже — четыре места, где решение пришлось записать явно. */

/* 🔴 `.field` у нас и у системы — РАЗНЫЕ вещи. У нас это сам `<input>`
   (ширина, высота, рамка, отступы), в системе — обёртка поля с подписью
   (`display:flex; flex-direction:column`). Наше правило `display` не задаёт,
   поэтому инпутам доезжал `display:flex` из системы и менял им внешний бокс.
   Возвращаем инпуту его смысл явно. Переименование (53 места) — отдельной
   фазой, чтобы не смешивать с правкой вида. */
.field { display: block; }

/* 🔴 Разделитель строки у нас свой — `border-bottom` во всю ширину. В системе
   он рисуется псевдоэлементом `.row + .row::before` с отступом 16px слева.
   Вместе это давало две черты в одном месте: полную и чуть темнее с отступа. */
.row + .row::before { content: none; }

/* Шеврон: система держит бокс 16×16, а наши svg нарисованы на 18 — иконка
   торчала за бокс. Размер отдаём содержимому. */
.row__chev { width: auto; height: auto; }

/* 🔴 Атрибут hidden обязан выигрывать у display из дизайн-системы: .iconbtn,
   .btn и .tabitem объявлены как inline-flex/flex, и браузерное правило
   [hidden]{display:none} слабее — кнопка «Назад» на «Сегодня» оставалась
   видимой, хотя JS её прятал. */
[hidden] { display: none !important; }

/* Мобильная шапка = тулбар дизайн-системы. На десктопе его нет: там заголовок
   экрана стоит в самом экране (.hd), а навигация — в сайдбаре. */
.toolbar { display: none; }

/* ── лист формы ───────────────────────────────────────────── */
/* Сам лист (шторка снизу на телефоне, окно по центру на десктопе), ручка,
   шапка и прокрутка тела — в salon-ds.css. Здесь только кнопка, прижатая к
   низу тела: в длинных формах она уезжала под экран, и мастер прокручивала
   обратно, чтобы сохранить. Фон непрозрачный — под ней идёт содержимое. */
/* Закреплённый подвал листа. 🔴 Отступ сверху обязателен: пока подвал висит
   над содержимым, его верхняя граница — это край панели, и она не должна
   прилипать вплотную к тому, что прокручивается под ней. Без отступа черта
   читалась как случайная полоса под кнопкой. */
.sheet__stick { position: sticky; z-index: 3; margin: var(--s5) calc(var(--s4) * -1) 0;
  bottom: calc(var(--s5) * -1);
  padding: var(--s3) var(--s4) var(--s4);
  background: var(--card); border-top: 1px solid var(--line);
  box-shadow: 0 -12px 18px -14px rgba(58, 28, 38, .22); }
.sheet__stick .btn { margin: 0; }
/* 🔴 Подвал листа НЕ строка по умолчанию. Когда он был flex'ом, кнопка во всю
   ширину и абзац под ней вставали рядом: кнопка ужималась вдвое, а надпись
   «Сохранить шаблоны» обрезалась с обеих сторон — у .btn в дизайн-системе
   стоит overflow:hidden и white-space:nowrap, и лишний текст просто срезается
   без многоточия. Строкой подвал становится там, где это нужно (.pb__foot). */

/* Подвал листа в одну строку: вторичное действие слева, главное справа, вместе
   — одна линия по ширине листа. Главное шире вторичного: оно главное. */
.sheet__stick--row { display: flex; gap: var(--s2); align-items: stretch; }
.sheet__stick--row .btn { flex: 1 1 0; min-width: 0; padding-left: var(--s3); padding-right: var(--s3); }
.sheet__stick--row .btn--primary { flex: 1.35 1 0; }
@media (min-width: 901px) {
  /* На десктопе строка не растягивается во всю ширину листа: см. правило
     выше про кнопки-полосы. Но внутри строки кнопки по-прежнему делят её. */
  .sheet__stick--row { justify-content: stretch; }
  .sheet__stick--row .btn { display: inline-flex; width: auto; min-width: 0; max-width: none; }
}

/* Страховка: у кнопки во всю ширину надпись переносится, а не срезается.
   Обрезанных подписей в интерфейсе не бывает (feedback_mobile_ui_standard). */
.btn--block { white-space: normal; }

/* 🔴 Кнопка «во всю ширину» — приём телефона: там одно действие на экран и
   палец должен попадать не целясь. На десктопе она превращалась в зелёную
   полосу во всю карточку — на 1500px это выглядит как баннер, а не как
   кнопка (показал Михаил 13.09.2026: «огромные кнопки на десктоп, их тоже
   нужно переработать в аккуратные»). Ширину ограничиваем по содержимому,
   но не даём стать меньше 220px, чтобы главное действие оставалось заметным.
   display возвращаем строчный: в системе у .btn--block стоит display:flex,
   и при width:auto блочный бокс всё равно растянулся бы на всю ширину. */
@media (min-width: 901px) {
  .btn--block { display: inline-flex; width: auto; min-width: 220px; max-width: 360px; }
  /* В подвале листа кнопка остаётся по центру: лист узкий, и прижатая к
     левому краю она смотрелась бы оброненной. */
  .sheet__stick { text-align: center; }
}
/* 🔴 Ручка листа — полоска 36×5. Попасть в неё пальцем почти невозможно, и
   лист «не закрывался за ручку» (Михаил 14.09.2026). Сама полоска остаётся
   тонкой — это указатель, а не кнопка, — но зона захвата расширена
   прозрачным полем: 44px по высоте и половина ширины листа. Правило про
   цель в 44px здесь работало только на вид. */
.sheet__grab { position: relative; }
.sheet__grab::after { content: ''; position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%); width: min(180px, 60vw); height: 44px; }
/* Экран условий обработки данных закрыть мимо нельзя: ни по фону, ни ручкой */
.sheet--locked .sheet__grab { visibility: hidden; }
.sheet--locked .sheet__grab::after { display: none; }

/* 🔴 ЗАКРЫТЫЕ ОВЕРЛЕИ НЕ ДОЛЖНЫ НИЧЕГО СТОИТЬ.
   В дизайн-системе затемнение и лист живут в DOM всегда: затемнение —
   во весь экран с backdrop-filter, лист — с will-change: transform. То есть
   браузер постоянно держит слой с размытием фона и ещё один композиторский
   слой, даже когда ничего не открыто. Поверх обоев мастера (фиксированное
   фото во весь экран) это пересчитывается на каждой отрисовке и на каждой
   прокрутке — отсюда «таб инструкция виснет, подписка тормозит» (Михаил
   13.09.2026). До перехода на шторку системы такого не было: прежний лист
   был display:none, а отдельного затемнения не существовало вовсе.

   Лечим в своём слое, эталон не трогаем: у закрытых снимаем размытие и
   убираем их из отрисовки. Видимость гасим С ЗАДЕРЖКОЙ на длительность
   анимации, иначе закрытие происходило бы рывком, без плавного ухода. */
.backdrop:not(.is-open) { -webkit-backdrop-filter: none; backdrop-filter: none; }
.sheet:not(.is-open) { will-change: auto; }
/* Совсем из отрисовки лист убирается уже из кода — в том же колбэке, который
   отрабатывает после анимации закрытия (closeSheet в public/js/salon.js).
   Через CSS это пришлось бы делать переходом с задержкой, а такой переход
   ведёт себя по-разному в браузерах: проверкой видимость так и оставалась
   visible. Из кода — однозначно и предсказуемо. */

.f { display: block; margin-bottom: var(--s4); }
/* ── Два поля в строку ────────────────────────────────────────────────────
   🔴 Раньше это был flex, и если подпись слева переносилась на две строки
   («перерыв после приёма»), её поле уезжало вниз — соседние поля вставали
   уступом. Подсетка кладёт подписи в один ряд, а поля — в другой, поэтому
   поля всегда на одной линии, сколько бы строк ни заняла подпись. */
.frow { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; align-items: start; }
.frow > .f { display: grid; grid-row: span 2; grid-template-rows: subgrid; row-gap: 6px; }
@supports not (grid-template-rows: subgrid) {
  .frow > .f { display: block; }
  .frow > .f > .lbl { min-height: 2.6em; }   /* запас под перенос подписи */
}
.f > .lbl { display: block; font-family: 'IBM Plex Mono', monospace; font-size: 10px; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 6px; }
.field { width: 100%; min-height: 46px; padding: 11px 14px; background: var(--card); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--r-sm); font: inherit; font-size: 15px; }
textarea.field { min-height: 92px; resize: vertical; }
/* Нативные date/time на iOS живут своей интринсик-шириной и вылезают
   за карточку — прижимаем к ширине поля */
input.field[type="date"], input.field[type="time"] {
  -webkit-appearance: none; appearance: none;
  display: block; min-width: 0; max-width: 100%; text-align: left; }
/* Фокус поля по дизайн-системе: розовая рамка и мягкое кольцо. Прежняя
   бледная рамка на телефоне не читалась — мастер не видела, куда печатает. */
.field:focus { border-color: var(--rose); outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--rose) 16%, transparent); }
/* 🔴 Подсказка в поле не должна читаться как ВВЕДЁННОЕ значение. Цифровые
   плейсхолдеры («90» в минутах, «2500» в сумме) при прежнем контрасте
   выглядели заполненными: я сам на прогоне принял пустое поле суммы за
   введённые 2500 и решил, что сумма не сохраняется. Делаем их заметно бледнее
   и чуть тоньше — текст остаётся читаемым, но спутать его с ответом нельзя. */
.field::placeholder { color: var(--ink-3); opacity: .55; font-weight: 400; }
/* ── Поле времени: печатают руками, крутят по кнопке ──────────────────────
   🔴 Просьба Дарьи 18.09.2026. Нативный `type="time"` на айфоне печатать не
   даёт совсем, поэтому основное поле здесь текстовое, а барабан вызывается
   кнопкой справа. Скрытый `type="time"` лежит в том же боксе: его нельзя
   убрать в display:none, иначе `showPicker()` не найдёт, куда открыться. */
.tfield { position: relative; display: block; }
.tfield__in { padding-right: 44px; }
.tfield__b { position: absolute; right: 5px; top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; padding: 0; border: 0; border-radius: var(--r-sm);
  background: transparent; color: var(--ink-3); cursor: pointer; }
.tfield__b:hover { color: var(--rose); background: var(--rose-tint); }
.tfield__pick { position: absolute; right: 44px; bottom: 4px;
  width: 1px; height: 1px; padding: 0; margin: 0; border: 0;
  opacity: 0; pointer-events: none; }

/* 🔴 Кнопка отправки, пока сообщение не дописано (прогон 18.09.2026). Не
   прячем и не отключаем намертво: нажатие объясняет, чего не хватает. Кнопка
   просто выглядит неготовой — иначе «Петя,» уходило клиенту одним касанием. */
.sendrow .btn.is-off { opacity: .4; }

.chips { display: flex; flex-wrap: wrap; gap: var(--s2); }
.chip { display: inline-flex; align-items: center; min-height: 38px; padding: 8px 14px; border-radius: var(--r-pill);
  background: var(--card); border: 1px solid var(--line); color: var(--ink); font: inherit; font-size: 14px; cursor: pointer; }
/* 🔴 Половина кода ставила выбранному чипу `chip--on`, а стиль был только у
   `.chip.on` — зернистость и размер диска выбирались вообще без отклика:
   мастер жала и не видела, что попала. Держим оба написания. */
.chip.on, .chip--on { background: var(--rose-tint); border-color: color-mix(in srgb, var(--rose-tint) 62%, var(--rose) 18%); color: var(--rose); font-weight: 600; }
/* Множественный выбор отличается от единичного галочкой: «пластина» и «как
   отходило» — это несколько отметок сразу, и это должно быть видно. */
.chips--many .chip { gap: 8px; }
.chips--many .chip.on::before, .chips--many .chip--on::before {
  content: ''; flex: none; width: 15px; height: 15px; border-radius: 50%;
  background: var(--rose) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5 10 17.5 19 7'/%3E%3C/svg%3E") center/11px no-repeat;
}

/* ── Пикер числа ──────────────────────────────────────────────────────────
   Срок носки, срок возврата, зернистость — это одно значение из готового ряда,
   а не набор кнопок. Девять пилюль с гритами занимали три строки и читались
   как девять разных действий. Пикер показывает выбранное крупно и меняет его
   стрелками: одно значение, одно место, движение подтверждает нажатие. */
.pick { --ph: 30px; position: relative;
  display: flex; align-items: center; width: fit-content;
  margin: 0 auto;                       /* колесо стоит по центру карточки */
  padding: 6px 20px; border-radius: var(--r-lg);   /* чуть мягче карточного 14px */
  /* Зелёное стекло, как у действия: выбранное значение — главный факт карточки */
  background: var(--glass); border: 1px solid rgba(255,255,255,.26);
  box-shadow: var(--glass-edge); }
/* 🔴 Выбранная строка отбита парой тонких линий — так делают нативные колёса.
   Без метки на крайнем значении (первое или последнее) с одной стороны пусто,
   и колесо читается как съехавшее: непонятно, где «выбрано». */
.pick::before, .pick::after {
  content: ''; position: absolute; left: 10px; right: 10px; height: 1px;
  background: rgba(255,255,255,.30); pointer-events: none;
}
.pick::before { top: 50%; margin-top: calc(var(--ph) / -2); }
.pick::after { top: 50%; margin-top: calc(var(--ph) / 2); }
.pick__b { flex: none; width: 44px; height: 44px; display: grid; place-items: center;
  border: 0; border-radius: 50%; background: var(--card); color: var(--rose);
  cursor: pointer; box-shadow: 0 1px 2px rgba(58,28,38,.10);
  transition: transform .12s ease, opacity .16s ease; }
.pick__b:active { transform: scale(.92); }
.pick__b:disabled { opacity: .38; cursor: default; box-shadow: none; }
/* Барабан: окно в три строки, выбранное значение посередине, соседние по краям
   притушены маской — сразу видно, что здесь листается. Прокрутка нативная,
   поэтому движение идёт с инерцией пальца и снапится на ближайшее значение;
   кнопки по краям прокручивают эту же ленту, а не двигают число отдельно. */
.pick__win {
  position: relative; z-index: 1; min-width: 116px; height: calc(var(--ph) * 3); padding: var(--ph) 0;
  overflow-y: auto; overscroll-behavior: contain; scroll-snap-type: y mandatory;

  -webkit-overflow-scrolling: touch; scrollbar-width: none;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 34%, #000 66%, transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 34%, #000 66%, transparent 100%);
}
.pick__win::-webkit-scrollbar { display: none; }
.pick__i { display: block; height: var(--ph); line-height: var(--ph); scroll-snap-align: center;
  text-align: center; font-size: 17px; font-weight: 600; color: #fff;
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.pick__b { position: relative; z-index: 2; }
.chip--code { font-family: 'IBM Plex Mono', monospace; font-size: 12px; }
.chip--add { border-style: dashed; color: var(--green); }

/* Тост живёт в salon-ds.css и создаётся из salon-ui.js (BAZA.toast):
   стеклянная пилюля с необязательной кнопкой «Отменить». */

/* ── адаптив ──────────────────────────────────────────────── */
@media (max-width: 1200px) {
  :root { --rail: 300px; }
  .screen { padding: var(--s5); }
}
@media (max-width: 1000px) {
  .cols { grid-template-columns: 1fr; }
}
@media (max-width: 900px) {
  .app { grid-template-columns: 1fr; }
  /* ── «Сегодня» на телефоне — сетка виджетов ─────────────────────────────
     🔴 Было: карточки одна под другой в том порядке, в каком их складывал
     код, — экран читался как лента, а не как первый экран приложения.
     Стало: ровная сетка. Верхний ярус — два виджета в ряд одного размера
     («сегодня» и «этот месяц»), ниже — полноширинные карточки в порядке
     утреннего сценария: новые заявки → у кого спросить про носку → кому
     написать → что работает → последние работы. Колонки десктопа
     «распускаем» через display:contents, порядок задаём order. */
  #s-today .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; align-items: stretch; }
  #s-today .cols > .stack { display: contents; }
  #s-today .cols > .stack > * { grid-column: 1 / -1; }
  /* Специфичность выше общего правила «во всю ширину» — иначе виджеты
     верхнего яруса встают друг под друга. */
  #s-today .cols > .stack > #todayNow { grid-column: 1 / 2; order: 0; }
  #s-today .cols > .stack > #todayMonth { grid-column: 2 / -1; order: 1; }
  /* 🔴 Порядок задан ЯВНО каждому виджету. Новый блок без своего order попадает
     в нулевую группу — то есть встаёт рядом с виджетом даты и выталкивает
     «этот месяц» из верхней строки вниз. Ровно это и случилось с «Моими делами»:
     верхний ярус перестал быть ярусом из двух карточек. Добавляешь виджет —
     добавляй ему order здесь. */
  /* 🔴 Грабля повторилась 15.09, ровно как предупреждает абзац выше: блок
     «Записи на сегодня» приехал без своего order, попал в нулевую группу к
     виджету даты и вытолкнул «этот месяц» из верхнего яруса — тот повис во
     второй колонке следующей строки, а слева осталась дыра. Нашёл Михаил на
     своём телефоне. Добавляешь виджет — добавляй ему order ЗДЕСЬ. */
  #todayNew { order: 2; } #todayAppts { order: 3; } #todayTasks { order: 4; }
  #todayPending { order: 5; } #todayWrite { order: 6; } #todayInsight { order: 7; }
  #todayWorks { order: 8; } #todayPush { order: 9; } #todayNote { order: 10; }
  /* Пустой блок не должен занимать строку сетки и оставлять двойной зазор */
  #s-today .cols > .stack > *:empty { display: none; }
  /* Дату показывает виджет — плашка с заголовком экрана на телефоне лишняя */
  #s-today > .hd { display: none; }
  /* 🔴 Заголовок экрана на телефоне стоял дважды: в шапке приложения и плашкой
     под ней («Клиенты» / «Клиенты · 8 человек в базе»). Две строки одного слова
     съедали ~60px на каждом экране. Оставляем подпись — она несёт счёт и
     состояние, а имя раздела уже написано в шапке. */
  .screen > .hd .h1 { display: none; }
  /* 🔴 …но НЕ в карточке клиента: там в `.hd` лежит не название раздела, а имя
     человека, и правило прятало его целиком. На телефоне мастер видела аватар,
     «клиент №1» и пустоту вместо имени — при том что карточка клиента и есть
     главный экран продукта. Найдено прогоном на симуляторе 16.09.2026;
     в браузере на широком экране дефект не виден, медиазапрос не срабатывает. */
  #s-client > .hd .h1 { display: block; }
  .screen > .hd .hd__t { gap: 0; }
  .screen > .hd .meta { font-size: 13px; }
  .side { display: none; }
  .tabs { display: block; }
  .toolbar { display: flex; padding-left: 20px; padding-right: 20px; }
  /* Контенту нужен запас снизу: панель теперь всегда лежит поверх экрана.
     Сверху — тоже воздух: 16px под липкой шапкой читались как «прижато»,
     первая карточка начиналась вплотную к заголовку. */
  .screen { padding: var(--s5) 20px calc(var(--tab) + env(safe-area-inset-bottom) + 44px); }
  /* Под фиксированной кнопкой действия — ещё её высота с запасом, иначе
     последняя строка списка липнет к кнопке и до неё не дотянуться. */
  body.has-act .screen { padding-bottom: calc(var(--tab) + env(safe-area-inset-bottom) + 124px); }
  .bm-act { display: inline-flex; }
  .bm-act[hidden] { display: none; }
  .hd { flex-direction: column; gap: var(--s3); }
  .hd .btn--primary { display: none; }   /* на мобильном действие живёт внизу, над таб-баром */
  .h1 { font-size: 26px; }
  .cols { gap: 20px; }
  .stack { gap: 20px; }
  .photos { gap: var(--s2); }
}
@media (min-width: 901px) {
  .only-mob { display: none !important; }
  .tabbar { display: none; }
  /* 🔴 «Неделю списком» работает и на десктопе. Раньше выбор применялся только
     к списку, а список на широком экране был скрыт совсем — Михаил 15.09:
     «неделю списком не работает, по крайней мере на десктопе». Настройка
     называлась «на телефоне показывать», но мастер ждёт от переключателя
     действия там, где она его нажала. */
  body[data-apptview="week"] #apptsList { display: block !important; }
  body[data-apptview="week"] #apptsGrid { display: none; }
}
@media (max-width: 900px) {
  .only-desk { display: none !important; }
}

/* Кнопка подписки — единственное место с живым переливом: розовый → сливовый →
   зелёный. Движение медленное (10 с), чтобы не тянуть взгляд с работы. */
.btn--shimmer {
  color: #fff; border-color: rgba(255,255,255,.30);
  /* 🔴 Цвета перелива заданы ЗДЕСЬ, а не взяты из токенов темы. В тёмной теме
     `--rose`, `--plum` и `--green` осветляются (они там работают текстом по
     тёмной бумаге), и белая надпись «Оплатить» ложилась на светлый градиент:
     замер контраста дал 2,4 при норме 4,5. На главной кнопке продукта. Здесь
     нужны насыщенные, «дневные» значения в любой теме — с белым они дают
     7,0–7,3.
     Меняешь цвет — меряй контраст с белым, а не смотри на глаз. */
  --pay-1: #16645f; --pay-2: #3f5a6b; --pay-3: #16645f;
  background: linear-gradient(115deg,
    var(--pay-1) 0%, var(--pay-2) 30%, var(--pay-3) 58%, var(--pay-2) 80%, var(--pay-1) 100%);
  background-size: 320% 100%;
  box-shadow: var(--glass-edge);
  /* 🔴 Имя перелива переименовано из bmShimmer в bmPayShimmer: в дизайн-системе
     v3 (salon-ds.css) есть свой @keyframes bmShimmer — блик скелетона. Два
     правила с одним именем не спорят, побеждает объявленное позже, и наш
     перелив ломал бы блик загрузки во всём приложении. */
  animation: bmPayShimmer 10s ease-in-out infinite;
}
.btn--shimmer:hover { filter: saturate(1.12) brightness(1.04); }
@keyframes bmPayShimmer {
  0%   { background-position:   0% 50%; }
  50%  { background-position: 100% 50%; }
  100% { background-position:   0% 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .btn--shimmer { animation: none; background-position: 20% 50%; }
}

/* ── Медиатека: группы по клиентам, мелкая сетка ──────────────────────────
   Снимки копятся быстро; одной лентой во всю ширину сотня работ читается
   как обои. Поэтому группа = клиент, карточка = маленькая, дата под ней. */
/* ── Медиатека: лента и полотно ───────────────────────────────────────────
   Переработана 15.09.2026. Снимок мастера — это её работа, и смотреть её
   плиткой в палец шириной бессмысленно. Общее для обоих видов: фото почти во
   всю ширину карточки, клиент и дата подписаны, действия — круглыми кнопками
   под снимком. */
.mediabar { display: flex; align-items: flex-end; gap: var(--s3); flex-wrap: wrap; }
.mediabar__pick { margin: 0; flex: 1; min-width: 200px; }
.mediabar__note { margin: var(--s2) 0 0; }
/* Переключатель вида. Свой, а не `.seg`: тот растягивает кнопки на всю ширину
   и рядом с пикером клиента выглядел бы вторым полем ввода, хотя это выбор
   раскладки. */
.segx { display: inline-flex; padding: 3px; gap: 2px; border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--card) 88%, transparent); border: 1px solid var(--line); }
.segx__i { border: 0; background: none; cursor: pointer; font: inherit; font-size: 13px;
  color: var(--ink-3); padding: 8px 16px; min-height: 36px; border-radius: var(--r-pill);
  transition: background var(--t-fast) var(--e-out), color var(--t-fast) var(--e-out); }
.segx__i.is-on { background: var(--rose); color: #fff; font-weight: 600; }

/* Карточка дня. Снимок не во всю ширину, а с ОДИНАКОВЫМ отступом слева,
   справа и снизу (Михаил 15.09) — рамка карточки читается как рамка, а не как
   обрезанный край фотографии. Отступ = --s4: боковые поля карточки (--s5)
   гасим на разницу, нижнее поле задаём тем же числом.
   🔴 Прозрачность задаётся ручкой `--gl-t` в salon-glass.css, а НЕ здесь: слой
   стекла подключается после этого файла и правилом большей специфичности
   (`.app .card:not(…)`) возвращает плотность 1 — значение отсюда до карточки
   не доезжает. Проверено замером: alpha оставалась дефолтной. */
.mday { margin-bottom: var(--s5); padding-bottom: var(--s4); }
.mday__top { display: flex; align-items: baseline; justify-content: space-between;
  font-size: 13px; color: var(--ink-3); margin-bottom: var(--s2); }
.mday__num { font-variant-numeric: tabular-nums; }
.mday__who { display: flex; align-items: center; gap: 12px; min-height: 44px; margin-bottom: var(--s3); }
.mday__who .sp { flex: 1; min-width: 0; }
.mday__who b { display: block; font-size: 15px; font-weight: 600; }
.mday__who small { display: block; font-size: 13px; color: var(--ink-3); }

/* 🔴 Слайдер — нативный scroll-snap. Своя механика перетаскивания на телефоне
   всегда проигрывает системной: инерцию, отскок у края и перехват жеста
   пальцем не подделать. Ширина слайда ровно 100% — от неё же считается
   активный кадр (scrollLeft / clientWidth). */
.mday__track { display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
  scrollbar-width: none; -webkit-overflow-scrolling: touch; border-radius: var(--r);
  /* Гасим боковые поля не полностью, а до --s4: столько же остаётся снизу. */
  margin-left: calc((var(--s5) - var(--s4)) * -1);
  margin-right: calc((var(--s5) - var(--s4)) * -1); }
.mday__track::-webkit-scrollbar { display: none; }
.mday__slide { flex: 0 0 100%; scroll-snap-align: center; margin: 0; }
/* 🔴 Высоту задаём пропорцией, а не долей экрана. С `62vh` карточка занимала
   почти весь экран — Михаил: «карточки слишком высокие», и листать ленту дат
   приходилось прокруткой в два приёма. 4:5 — портрет, под который снимают
   работу, и при нём на экран попадают и шапка с клиентом, и кнопки, и край
   следующей карточки: видно, что лента продолжается. */
.mday__slide img { display: block; width: 100%; aspect-ratio: 4 / 5; max-height: 38vh;
  object-fit: cover; border-radius: var(--r); background: var(--paper); }

/* Сцена: трек прокручивается внутри, кнопки стоят поверх и не едут с кадром. */
.mday__stage { position: relative; }

/* Действия НА снимке, правый верхний угол. Михаил 15.09: «в слайдере лучше
   кнопки поместить прямо на фото — верхний правый угол и сделать их намного
   более прозрачными».
   🔴 Прозрачность даём подложке, а не кнопке целиком: погасив её вместе со
   значком, мы потеряли бы значок на светлом снимке. Тёмная плёнка с размытием
   держит белую иконку читаемой на любом фото — и на почти белом, и на чёрном. */
/* Почти вплотную к правому краю снимка (Михаил 15.09). Ближе 4px нельзя:
   крайняя кнопка заходит на скругление фото и на светлом углу сливается с
   краем — проверено на белом кадре. */
.mday__acts { position: absolute; top: var(--s2); right: 4px; z-index: 2;
  display: flex; gap: 4px; }
.phbtn {
  position: relative;            /* держит невидимую зону нажатия ниже */
  width: 38px; height: 38px; padding: 0; flex: none;
  display: grid; place-items: center; border-radius: 50%; cursor: pointer;
  background: rgba(18, 12, 14, .26); color: rgba(255, 255, 255, .92);
  border: 1px solid rgba(255, 255, 255, .22);
  -webkit-backdrop-filter: blur(8px) saturate(1.1); backdrop-filter: blur(8px) saturate(1.1);
  transition: background var(--t-fast) var(--e-out), transform var(--t-fast) var(--e-out);
}
/* 🔴 Тень у значка, а не плотность у подложки. На почти белом снимке белая
   иконка поверх лёгкой плёнки теряется, и напрашивается сделать фон гуще — но
   тогда кнопки перестают быть «намного более прозрачными», ради чего их и
   переносили на фото. Тень решает читаемость, оставляя кнопку невесомой. */
.phbtn svg { filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .55)); }
.phbtn:hover { background: rgba(18, 12, 14, .44); }
.phbtn:active { transform: scale(.92); }
/* Палец крупнее кнопки: невидимая зона нажатия до 44px, как во всей системе. */
.phbtn::after { content: ''; position: absolute; inset: 50% auto auto 50%;
  width: var(--touch); height: var(--touch); transform: translate(-50%, -50%); }

/* Полоса кадров — только там, где есть ширина: на телефоне она отняла бы
   место у самого снимка, ради которого экран и открыт. */
.mday__thumbs { display: none; }

/* Полотно: сетка карточек, даты — заголовками. Две колонки на телефоне —
   это обзор, ради которого вид и переключают; одна колонка повторяла бы ленту. */
.mdayblock { margin-bottom: var(--s5); }
.mwall { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s3); }
/* Плитки в ряду одной высоты: подпись «до/после» есть не у всех, и без неё
   нижний край карточки поднимался выше соседей. */
.mwall { align-items: stretch; }
.mtile { overflow: hidden; display: flex; flex-direction: column; height: 100%; }
.mtile__ph { position: relative; }
.mtile__ph img { display: block; width: 100%; aspect-ratio: 1 / 1; object-fit: cover;
  background: var(--paper); }
.mtile__b { padding: var(--s3); display: flex; flex-direction: column; gap: 4px; flex: 1; }
.mtile__name { font-size: 14px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mtile__kind { font-size: 12px; color: var(--ink-3); }
/* Кнопки на снимке, правый верхний угол — тот же приём, что в ленте. Под фото
   остаётся только имя клиента, и плитки в ряду выходят одной высоты. */
.mtile__acts { position: absolute; top: 6px; right: 6px; z-index: 2; display: flex; gap: 4px; }
.phbtn--sm { width: 32px; height: 32px; }
/* Удаление на снимке — единственная кнопка с цветом: остальные белые, а эта
   должна отличаться раньше, чем мастер её нажмёт. */
.phbtn--warn:hover { background: rgba(160, 40, 50, .55); }
.phbtn--sm svg { width: 15px; height: 15px; }
/* Имя — кнопка перехода в карточку клиента, но выглядит как подпись. */
.mtile__name--go { border: 0; background: none; padding: 0; cursor: pointer; text-align: left;
  font: inherit; font-size: 14px; font-weight: 600; color: var(--ink); }
.mtile__name--go:hover { color: var(--rose); }

@media (min-width: 900px) {
  /* 🔴 Пустой блок не занимает строку, но ЗАЗОР между блоками даёт: у стека
     gap 24px, и два пустых контейнера («пуши», «новые заявки») опускали левую
     колонку на 48px ниже правой. На телефоне такое правило уже стояло, на
     десктопе его не было. */
  #s-today .cols > .stack > *:empty { display: none; }
  /* Правый столбец опускаем на высоту плашки-заголовка левого («Записи на
     сегодня» + её отступ): тогда верх зелёной карточки встаёт вровень с верхом
     первой крупной карточки слева, а не с её заголовком. Просьба Михаила
     16.09: «крупный блок слева и зелёный блок сделай на одном уровне по верху
     — зелёный нужно опустить ниже». Телефон не трогаем: там колонка одна. */
  #s-today .cols > .stack--tight { padding-top: 39px; }

  /* На десктопе слайдер остаётся, но под ним появляется полоса кадров: место
     есть, и щёлкнуть по нужному быстрее, чем листать. Сетку из мелких плиток
     сюда не ставим — для неё есть «Полотно». */
  /* 🔴 На широком экране карточка дня занимала ВЕСЬ экран: слайд тянулся на
     всю колонку, а вертикальный кадр вписывался по высоте — и по бокам
     оставалось по 400 пустых пикселей бумаги. Михаил 16.09: «в медиатеке на
     десктопе просто огромные карточки». Теперь дни идут сеткой по 2–3 в ряд,
     карточка ровно под кадр, а высота кадра ниже: смотреть надо много снимков,
     а не один во весь монитор. */
  #mediaGrid[data-mview="lenta"] { display: grid; gap: var(--s5); align-items: stretch;
    grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); }
  /* 🔴 Карточки в ряду одной высоты. Раньше высоту задавал сам кадр: рядом
     стояли карточка в полтора экрана и карточка в ладонь — Михаил 16.09:
     «карточки разной высоты и не обязательно в карточке отображать полную
     высоту фото». Кадр кадрируем по месту (cover), целиком его показывает
     «развернуть». */
  #mediaGrid[data-mview="lenta"] .mday { margin-bottom: 0; height: 100%;
    display: flex; flex-direction: column; }
  /* Высота кадра задана, а не берётся от снимка: иначе карточка с вертикальным
     фото вырастала под шестьсот пикселей и ряд снова разъезжался. Полосу
     миниатюр прижимаем к низу — тогда карточки в ряду ровные. */
  #mediaGrid[data-mview="lenta"] .mday__stage { flex: none; height: 320px; }
  #mediaGrid[data-mview="lenta"] .mday__thumbs { margin-top: auto; }
  #mediaGrid[data-mview="lenta"] .mday__track,
  #mediaGrid[data-mview="lenta"] .mday__slide { height: 100%; }
  .mday { padding-bottom: var(--s5); }
  .mday__slide img { aspect-ratio: auto; height: 100%; width: 100%; max-height: none;
    object-fit: cover; border-radius: var(--r); }
  .mday__thumbs { display: flex; gap: var(--s2); margin-top: var(--s3); justify-content: center;
    overflow-x: auto; scrollbar-width: none; padding-bottom: 2px; }
  .mday__thumbs::-webkit-scrollbar { display: none; }
  .mday__thumb { flex: 0 0 auto; width: 68px; height: 68px; padding: 0; cursor: pointer;
    border: 2px solid transparent; border-radius: var(--r-sm); background: none;
    transition: border-color var(--t-fast) var(--e-out), opacity var(--t-fast) var(--e-out); opacity: .6; }
  .mday__thumb img { display: block; width: 100%; height: 100%; object-fit: cover; border-radius: calc(var(--r-sm) - 2px); }
  .mday__thumb.is-on { border-color: var(--rose); opacity: 1; }
  .mwall { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
}

.mgroup { margin-bottom: var(--s6); }
.mgroup__head { display: flex; align-items: center; gap: 12px; margin-bottom: var(--s3);
  padding-bottom: 10px; border-bottom: 1px solid var(--line); }
.mgroup__head b { display: block; font-size: 15px; font-weight: 600; }
.mgroup__head small { display: block; font-size: 13px; color: var(--ink-3); }
.mgroup__head .sp { flex: 1; min-width: 0; }
.mgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: var(--s3); }
.mshot { margin: 0; }
.mshot figcaption, .mshot > figcaption { margin-top: 6px; font-size: 12px; color: var(--ink-3);
  font-variant-numeric: tabular-nums; }
.mshot--more { display: flex; align-items: center; justify-content: center; aspect-ratio: 4/3;
  border: 1px dashed var(--line); border-radius: var(--r-sm); background: color-mix(in srgb, var(--card) 60%, transparent);
  color: var(--ink-2); font: inherit; font-size: 15px; cursor: pointer; }
.mshot--more:hover { border-color: var(--line-2); color: var(--ink); }
/* 🔴 Галерея поверх обоев: подписи лежат прямо на фотографии, и белый с
   ореолом на светлом небе читается плохо (показал Михаил 14.09.2026).
   Подкладываем тёмное стекло — как в галерее телефона, а не матовую бумагу:
   бумажные заплатки поверх снимка он уже отвергал 12.09.
   Цвет текста задаём здесь явно: подложка непрозрачная, поэтому markWall
   больше не метит эти подписи как «на обоях» и белый от него не придёт. */
body.has-wall #s-media .mgroup__head{
  border-bottom: 0; border-radius: var(--r); padding: 10px 12px;
  background: rgba(24,10,16,.36);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.14);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  backdrop-filter: blur(18px) saturate(140%);
}
body.has-wall #s-media .mgroup__head b,
body.has-wall #s-media .mgroup__head small,
body.has-wall #s-media .mgroup__head .btn{ color: #fff; text-shadow: none; }
body.has-wall #s-media .mgroup__head small{ color: rgba(255,255,255,.76); }
body.has-wall #s-media .mshot figcaption{
  display: inline-block; margin-top: 6px; padding: 3px 9px;
  border-radius: var(--r-pill); color: #fff; text-shadow: none;
  background: rgba(24,10,16,.38);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
}
@media (max-width: 620px) {
  .mgrid { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: var(--s2); }
}

.plans { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--s3); }

/* ── Тарифные карточки ────────────────────────────────────────────────────
   Выбор видно и без цвета: рамка, метка-кружок и приподнятость. */
.plan { display: flex; flex-direction: column; gap: 10px; width: 100%; text-align: left;
  padding: var(--s5); border: 1px solid var(--line); border-radius: var(--r);
  background: var(--card); box-shadow: var(--sh); cursor: pointer; font: inherit; color: inherit;
  transition: border-color 140ms ease, transform 140ms ease; }
.plan:hover { border-color: var(--line-2); }
.plan--on { border-color: var(--green); box-shadow: 0 0 0 1px var(--green), var(--sh); }
.plan__top { display: flex; align-items: center; gap: 8px; }
.plan__name { font-family: 'IBM Plex Mono', monospace; font-size: 10px; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--ink-on-green); }
.plan__badge { font-family: 'IBM Plex Mono', monospace; font-size: 10px; letter-spacing: .1em;
  background: var(--rose-tint); color: var(--rose); padding: 3px 7px; border-radius: var(--r-xs); }
.plan__mark { margin-left: auto; width: 20px; height: 20px; border-radius: 50%;
  border: 1.5px solid var(--line); position: relative; flex: none; }
.plan--on .plan__mark { border-color: var(--green); background: var(--green); }
.plan--on .plan__mark::after { content: ""; position: absolute; left: 6px; top: 3px; width: 5px; height: 9px;
  border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(42deg); }
.plan__price { display: flex; align-items: baseline; gap: 8px; }
.plan__price b { font-size: 40px; font-weight: 600; line-height: .95; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums; }
.plan__price i { font-style: normal; font-size: 14px; color: var(--ink-3); }
.plan__price s { font-size: 14px; color: var(--ink-3); opacity: .75; }
.plan__total { font-size: 13px; color: var(--ink-3); font-variant-numeric: tabular-nums; }
/* Кнопка оплаты внутри карточки тарифа: тот же перелив, что у главной, только
   ростом пониже — она не спорит с ценой, а стоит под ней. */
/* Согласие на автосписание: текст рядом с галочкой и подсветка, когда мастер
   жмёт «оплатить», не отметив его. Мигать нечем — коротко обводим рамкой. */
.check__txt { font-size: 14px; line-height: 1.4; color: var(--ink-2); }
.check__txt a { color: var(--green); text-decoration: underline; text-underline-offset: 2px; }
.check--ask .check__box { border-color: var(--rose); box-shadow: 0 0 0 3px var(--rose-tint); }

.plan__pay { display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  align-self: flex-start; margin-top: 4px; min-height: 40px; padding: 0 16px;
  border-radius: var(--r-pill); border: 1px solid rgba(255,255,255,.30);
  font: inherit; font-size: 14px; font-weight: 600; cursor: pointer; }
.plan__pay .ic-rub { flex: none; }
.plan__pay:active { transform: scale(.97); }
.plan__list { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
.plan__li { position: relative; padding-left: 18px; font-size: 14px; color: var(--ink-2); }
.plan__li::before { content: ""; position: absolute; left: 2px; top: 7px; width: 6px; height: 6px;
  border-radius: 50%; background: var(--green); opacity: .55; }

/* Тумблер автопродления */
/* Сам переключатель — из дизайн-системы: трек 48×29, кружок псевдоэлементом
   ::after, который на нажатии растягивается. 🔴 Своего кружка (.switch__knob
   отдельным узлом) у нас больше нет: наш трек 44×26 выигрывал по порядку
   правил, а кружок системы в 25px оставался — два кружка в одном треке, и
   больший вылезал за правый край. Так выглядели ВСЕ восемнадцать тумблеров
   приложения, начиная с первого экрана условий обработки данных.
   Здесь остаётся только наша раскладка строки: подпись с пояснением справа,
   тумблер вверху, черта над строкой — в настройках они идут списком. */
.switch { display: flex; align-items: flex-start; gap: 12px; cursor: pointer;
  padding-top: var(--s4); border-top: 1px solid var(--line); }
.switch input:focus-visible + .switch__track { outline: 2px solid var(--green); outline-offset: 2px; }
.switch__txt b { display: block; font-size: 15px; font-weight: 600; }
.switch__txt small { display: block; font-size: 13px; color: var(--ink-3); margin-top: 2px; }

.policy { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 8px;
  color: var(--ink-2); font-size: 14px; }

@media (max-width: 620px) { .plan__price b { font-size: 32px; } }

/* Кнопка «+ Новый …» в листе выбора: липнет к низу, чтобы не искать её
   под длинным списком (просьба Дарьи). */
.sheet__add {
  position: sticky; bottom: 0; z-index: 2; margin-top: var(--s4);
  box-shadow: 0 -14px 20px -14px rgba(58,28,38,.28);
}

/* ── Тур по интерфейсу ────────────────────────────────────────────────────
   Затемнение с «окошком» вокруг того, о чём речь: подсказка без показа
   не работает — экран закрывают и всё равно не знают, где что. */
.tour { position: fixed; inset: 0; z-index: 100; pointer-events: none; }
.tour__hole { position: fixed; border-radius: var(--r-sm); pointer-events: none; opacity: 0;
  box-shadow: 0 0 0 9999px rgba(14,34,33,.62), 0 0 0 2px rgba(255,255,255,.9) inset;
  transition: opacity .14s ease; }   /* без анимации координат: она давала «полёт» через полэкрана */
.tour__card { position: fixed; top: 50%; left: 50%; z-index: 101; pointer-events: auto;
  width: min(340px, calc(100vw - 24px)); opacity: 0; transition: opacity .16s ease;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r);
  box-shadow: 0 18px 44px -20px rgba(58,28,38,.5); padding: 18px 20px 16px; }
.tour.is-ready .tour__hole, .tour.is-ready .tour__card { opacity: 1; }
.tour__k { font-family: 'IBM Plex Mono', monospace; font-size: 10px; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-3); }
.tour__t { margin: 8px 0 0; font-size: 17px; font-weight: 600; line-height: 1.3; }
.tour__d { margin: 8px 0 0; font-size: 14px; line-height: 1.5; color: var(--ink-2); }
.tour__nav { display: flex; gap: 8px; margin-top: 16px; }
.tour__nav .btn { flex: 1; }
.tour__nav .btn--primary { flex: 1.6; }
/* Карточка всегда непрозрачная: под ней просвечивал интерфейс */
.tour__card { background: var(--card); }

/* ── Шахматка записей ─────────────────────────────────────────────────────
   Дни по горизонтали, время по вертикали: так видно окна между клиентами,
   а не только список. Один час = 56px, запись позиционируется по минутам. */
.sh { --sh-hour: 56px; --sh-time: 62px;
  border: 1px solid var(--line); border-radius: var(--r); background: var(--card);
  box-shadow: var(--sh); overflow: auto; }
/* На узком десктопе неделя не сжимается до нечитаемых колонок — уезжает вбок */
.sh__head, .sh__body { min-width: 660px; }
.sh__head { display: grid; grid-template-columns: var(--sh-time) repeat(7, minmax(0, 1fr));
  border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 2; background: var(--card); }
.sh__day { padding: 10px 8px; text-align: center; border-left: 1px solid var(--line); }
.sh__day b { display: block; font-family: 'IBM Plex Mono', monospace; font-size: 10px;
  letter-spacing: .16em; text-transform: uppercase; color: var(--ink-3); font-weight: 500; }
.sh__day span { display: block; margin-top: 4px; font-size: 14px; font-weight: 600; }
.sh__day--now { background: var(--rose-tint); }
.sh__day--now b, .sh__day--now span { color: var(--rose); }
.sh__day--off { opacity: .5; }
.sh__cell--off { background: repeating-linear-gradient(135deg, transparent 0 7px, color-mix(in srgb, var(--ink) 4%, transparent) 7px 8px); }
.sh__body { position: relative; }
.sh__grid { display: grid; grid-template-columns: var(--sh-time) repeat(7, minmax(0, 1fr));
  grid-auto-rows: var(--sh-hour); }
.sh__time { font-family: 'IBM Plex Mono', monospace; font-size: 11px; color: var(--ink-3);
  padding: 4px 8px; border-top: 1px solid var(--line); }
.sh__cell { border-top: 1px solid var(--line); border-left: 1px solid var(--line); cursor: pointer; }
.sh__cell:hover { background: var(--card-2); }
[data-theme="dark"] .sh__cell:hover { background: rgba(255,255,255,.04); }
/* Слой записей поверх сетки: колонка = день, отступ сверху = минуты от начала дня */
.sh__layer { position: absolute; inset: 0 0 0 var(--sh-time); pointer-events: none;
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.sh__appt { pointer-events: auto; grid-column: calc(var(--col) + 1); grid-row: 1;
  margin: 2px 4px; padding: 6px 8px; text-align: left; cursor: pointer;
  align-self: start; border: 1px solid rgba(255,255,255,.26); border-radius: var(--r-sm);
  background: var(--glass); color: #fff; box-shadow: var(--glass-edge);
  transform: translateY(calc(var(--top) * (var(--sh-hour) / 60)));
  min-height: calc(var(--len) * (var(--sh-hour) / 60) - 4px);
  font: inherit; overflow: hidden; }
/* Перенос записи мышью: курсор показывает, что блок можно взять, а во время
   переноса он приподнят и не перехватывает события — иначе под курсором всё
   время оказывается он сам, а не сетка. Пальцу перетаскивание не отдаём:
   на телефоне шахматки нет, а жест отнял бы прокрутку. */
@media (hover: hover) and (pointer: fine) {
  .sh__appt[data-appt] { cursor: grab; }
  .sh__appt[data-appt].is-drag { cursor: grabbing; z-index: 3; opacity: .92;
    box-shadow: 0 10px 24px -12px rgba(20, 12, 16, .65); }
}
/* 🔴 Длинная фамилия срезалась по краю ячейки: у блока записи
   `overflow:hidden`, а «Пантелеймоненко-Штерн» в колонку недели не влезает
   ни на планшете, ни на десктопе — мастер видела «Пантелеймон» с обрубленной
   буквой. Разрешаем перенос ВНУТРИ слова: лучше две строки, чем обрубок
   (правило «надписи не обрезаются», feedback_mobile_ui_standard). */
.sh__appt b { display: block; font-size: 13px; font-weight: 600; overflow-wrap: anywhere; }
.sh__appt small { display: block; font-size: 11px; color: var(--glass-ink-2); margin-top: 2px; }
/* Заявка с публичной страницы: время уже занято, но ещё не подтверждено —
   штриховая рамка отличает её от обычной записи с одного взгляда. */
.sh__appt.is-new { background: var(--card); color: var(--ink); border: 1.5px dashed var(--green);
  box-shadow: none; }
.sh__appt.is-new small { color: var(--green); }
.sh__appt.is-done { background: var(--paper); color: var(--ink-2); border-color: var(--line);
  border-left: 3px solid var(--green); box-shadow: none; }
.sh__appt.is-done b { font-weight: 500; }
.sh__appt.is-done small { color: var(--ink-3); }
/* Занятое личное время: не клиентка и не свободное окно. Ни зелёного стекла,
   ни розового — обычная бумага в полоску, чтобы в шахматке было видно, что
   час занят, но это не приём. */
.sh__appt--busy { background: var(--paper); color: var(--ink-2); box-shadow: none;
  border: 1px solid var(--line); border-left: 3px solid var(--ink-3);
  background-image: repeating-linear-gradient(135deg, color-mix(in srgb, var(--ink-3) 14%, transparent) 0 6px, transparent 6px 12px); }
.sh__appt--busy small { color: var(--ink-3); }
/* Линия «сейчас»: единственная розовая черта в сетке, поверх блоков. Точка
   слева — чтобы её было видно и там, где линия пришлась на границу часа. */
.sh__now { grid-column: calc(var(--col) + 1); grid-row: 1; align-self: start; position: relative;
  z-index: 2; height: 0; margin: 0 2px; border-top: 2px solid var(--rose); pointer-events: none;
  transform: translateY(calc(var(--top) * (var(--sh-hour) / 60))); }
.sh__now::before { content: ''; position: absolute; left: -3px; top: -4px;
  width: 7px; height: 7px; border-radius: 50%; background: var(--rose); }
/* 🔴 Подпись времени обязательна: Михаил увидел линию и спросил «что за розовая
   полоса?». Черта без числа читается как дефект вёрстки, а не как «сейчас». */
.sh__now::after { content: attr(data-t); position: absolute; right: 0; top: -8px;
  font-family: 'IBM Plex Mono', monospace; font-size: 10px; line-height: 16px;
  padding: 0 4px; border-radius: 8px; color: #fff; background: var(--rose); }
@media (max-width: 1200px) { .sh { --sh-hour: 48px; } }

/* ── День на телефоне: полоса дней + лента с окнами ───────────────────────
   Сетка недели на 390px нечитаема, поэтому мобильный вид — один день,
   где видно не только записи, но и свободные окна между ними. */
/* Неделя целиком, без скролла: мастер должна видеть все семь дней сразу */
.dstrip { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 5px; margin: 0 0 14px; }
/* 🔴 Лента дней тянется СВОБОДНО, без прилипания (18.09.2026). Прилипание
   стояло мягкое (`proximity`), чтобы числа не оставались враскоряку, но на
   телефоне оно ловит ленту после каждого движения пальца, и это читается
   ровно так: «не листается плавно, а перескакивает с даты на дату». Ровные
   числа того не стоят — лента должна ехать как лента. */
.dslide { display: flex; gap: 5px; margin: 0 0 14px; padding-bottom: 2px;
  overflow-x: auto; scroll-snap-type: none; overscroll-behavior-x: contain;
  scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.dslide::-webkit-scrollbar { display: none; }
/* 🔴 Семь с половиной дней в окне, а не ровно семь (Дарья 18.09.2026:
   «сделай непрерывную прокрутку дат, чтобы удобно было быстро листать»).
   Ровно семь читались как статичная неделя: край ленты совпадал с краем
   экрана, и по картинке было не понять, что её вообще можно тянуть.
   Половинка следующего дня — единственный честный способ это показать. */
.dslide .dchip { flex: 0 0 calc((100% - 37px) / 7.5); }

/* Месяц над лентой: меняется сам, пока листаешь, и открывает выбор месяца */
.dmonwrap { position: relative; display: inline-block; margin: 0 0 8px; }
.dmon { display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 10px 6px 12px; border: 0; border-radius: var(--r-pill);
  background: var(--rose-tint); color: var(--rose); cursor: pointer;
  font: inherit; font-size: 14px; font-weight: 600; }
.dmon svg { flex: none; opacity: .8; transition: transform .18s ease; }
.dmon.is-open svg { transform: rotate(180deg); }

/* Выпадающее окно месяцев — маленькое: месяц выбирают мимоходом, ради этого
   не стоит ни закрывать экран листом, ни занимать пол-телефона списком.
   Больше пяти месяцев — прокрутка внутри окна. */
.dmenu { position: absolute; z-index: 30; top: calc(100% + 6px); left: 0;
  min-width: 232px; max-height: 216px; overflow-y: auto; overscroll-behavior: contain;
  padding: 5px; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: 0 18px 40px -20px rgba(58,28,38,.4), 0 2px 6px rgba(58,28,38,.06);
  animation: dmenuIn .16s cubic-bezier(.2,.8,.3,1) both; }
.dmenu button { display: flex; align-items: center; gap: 8px; width: 100%;
  min-height: 40px; padding: 6px 8px; border: 0; border-radius: var(--r-sm);
  background: none; color: var(--ink); font: inherit; font-size: 14px;
  text-align: left; cursor: pointer; }
.dmenu button:hover { background: var(--card-2); }
.dmenu button.is-on { background: var(--rose-tint); color: var(--rose); font-weight: 600; }
.dmenu small { margin-left: auto; font-size: 12px; color: var(--ink-3);
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.dmenu button.is-on small { color: var(--rose); }
.dmenu__gap { flex: none; width: 16px; }
.dmenu__m { white-space: nowrap; }        /* месяц не переносится на две строки */
.dmenu__tick { flex: none; }
@keyframes dmenuIn { from { opacity: 0; transform: translateY(-6px) scale(.98); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .dmenu { animation: none; } }
.dchip { min-width: 0; padding: 9px 2px 7px; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--card); color: var(--ink); font: inherit; }
.dchip b { display: block; font-family: 'IBM Plex Mono', monospace; font-size: 10px; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; color: var(--ink-3); }
.dchip span { display: block; margin-top: 4px; font-size: 17px; font-weight: 600; line-height: 1; }
.dchip i { display: block; margin: 6px auto 0; width: 5px; height: 5px; border-radius: 50%;
  background: var(--green); opacity: 0; }
.dchip.has i { opacity: 1; }
.dchip--off { opacity: .45; }
.dchip--today:not(.dchip--on) { border-color: rgba(22,100,95,.42); }
.dchip--today:not(.dchip--on) b { color: var(--rose); }
.dchip--on { background: var(--glass); color: #fff; border-color: rgba(255,255,255,.26); box-shadow: var(--glass-edge); }
.dchip--on b { color: var(--glass-ink-2); }
.dchip--on i { background: #fff; }

.dline { display: flex; flex-direction: column; gap: 8px; }
.tl { display: flex; align-items: center; gap: 14px; width: 100%; text-align: left; cursor: pointer;
  padding: 13px 14px; font: inherit; color: var(--ink);
  background: var(--card); border: 1px solid var(--line); border-left: 3px solid var(--green);
  border-radius: var(--r-sm); box-shadow: var(--sh); }
.tl__t { flex: 0 0 auto; min-width: 58px; font-family: 'IBM Plex Mono', monospace; font-size: 15px; }
.tl__t i { display: block; margin-top: 3px; font-style: normal; font-size: 11px; color: var(--ink-3); }
.tl__b { flex: 1; min-width: 0; }
.tl__b b { display: block; font-size: 15px; font-weight: 600; }
.tl__b small { display: block; margin-top: 2px; font-size: 13px; color: var(--ink-2); }
.tl--done { border-left-color: var(--line); background: var(--paper); }
.tl--busy { border-left-color: var(--ink-3); background: var(--paper);
  background-image: repeating-linear-gradient(135deg, color-mix(in srgb, var(--ink-3) 12%, transparent) 0 6px, transparent 6px 12px); }
.tl--busy .tl__b b { font-weight: 600; color: var(--ink-2); }
.tl--done .tl__b b { font-weight: 500; color: var(--ink-2); }
/* Время ушло — строка гаснет ровно так же, как на «Сегодня», и всегда несёт
   подпись «время прошло»: бледность без слов читалась бы как поломка. */
.tl--past { opacity: .58; }
.tl--past .tl__b b { font-weight: 500; }

/* ── Планер ───────────────────────────────────────────────────────────────
   Дело — та же строка дня, что и приём: в одном дне сразу видно, где клиентка
   (зелёная полоса слева), а где своё (розовая). Третьего цвета нет, поэтому
   вид дела читается формой иконки, а не окраской. */
.tl--task { padding: 0; gap: 0; border-left-color: var(--rose); }
.tl__box { flex: 0 0 52px; align-self: stretch; display: grid; place-items: center;
  background: none; border: 0; padding: 0; cursor: pointer; color: transparent; }
.tl__box i { width: 23px; height: 23px; border-radius: 50%; border: 1.8px solid var(--line);
  display: grid; place-items: center; background: #fff; transition: background .12s ease, border-color .12s ease; }
.tl--task.tl--done .tl__box { color: #fff; }
.tl--task.tl--done .tl__box i { background: var(--green); border-color: var(--green); }
.tl__hit { flex: 1; min-width: 0; display: flex; align-items: center; gap: 14px;
  padding: 13px 14px 13px 0; background: none; border: 0; font: inherit; color: inherit;
  text-align: left; cursor: pointer; }
.tl--task .tl__b b { display: flex; align-items: flex-start; gap: 7px; }
/* Колонка времени у дела уже, чем у приёма: там может стоять дата, а не час,
   и широкая колонка съедала место у названия. */
.tl--task .tl__t { flex: 0 0 58px; min-width: 0; font-size: 13px; line-height: 1.25; }
/* Узкая строка: без колонки времени, отступ слева отдаёт кружок отметки. */
.tl--narrow .tl__hit { padding-left: 0; gap: 0; }
.tl--narrow .tl__box { flex: 0 0 46px; }
.tsk__ic { margin-top: 2px; }
.tsk__ic { flex: 0 0 auto; color: var(--ink-3); }
.tl--task.tl--done .tl__b b { text-decoration: line-through; color: var(--ink-2); font-weight: 500; }
/* Просроченное — розовая заливка вместо белой карточки. Не красный: ругаться
   на мастера за несделанное дело незачем, достаточно выделить. */
.tl--late { background: var(--rose-tint); border-color: color-mix(in srgb, var(--rose-tint) 70%, var(--rose) 12%); }
.tgrp { margin-bottom: 18px; }
.tgrp__all { margin-top: 10px; min-height: 44px; }
.tgrp .dline { display: grid; gap: 8px; }
/* ── Сигнал коллег ────────────────────────────────────────────────────────
   Предупреждение, а не приговор: тёплая бумага и обычные чернила, никакого
   красного. Человек по ту сторону номера не преступник, и выглядеть это должно
   как заметка на полях, а не как розыск. */
.peer { display: flex; gap: 11px; align-items: flex-start; margin-top: 10px;
  padding: 12px 14px; border-radius: var(--r-sm);
  background: var(--rose-tint); border: 1px solid color-mix(in srgb, var(--rose-tint) 70%, var(--rose) 12%); }
.peer__ic { flex: none; color: var(--rose); margin-top: 1px; }
.peer .sp b { display: block; font-size: 14px; font-weight: 600; }
.peer .sp small { display: block; margin-top: 3px; font-size: 13px; color: var(--ink-2); }
.peer__mine { display: flex; align-items: center; gap: 10px; margin-top: 12px;
  padding-top: 12px; border-top: 1px solid var(--line); }
.peer__mine b { display: block; font-size: 14px; font-weight: 600; }
.peer__mine small { display: block; font-size: 12px; color: var(--ink-3); }

/* Пометка чёрного списка в строке. Нейтральная, без красного: это личная
   отметка мастера, а не клеймо — и выглядеть она должна соответственно. */
.cmark {
  display: inline-block; margin-left: 8px; padding: 1px 7px; border-radius: var(--r-pill);
  background: var(--paper); border: 1px solid var(--line-2); color: var(--ink-3);
  font-family: 'IBM Plex Mono', monospace; font-size: 10px; font-style: normal;
  font-weight: 500; letter-spacing: .06em; vertical-align: middle;
}
/* ── Бэйдж и заметка — по дизайн-системе ─────────────────────────────────
   Статус читается не только цветом: у бэйджа есть точка того же цвета, а у
   заметки — свой фон и рамка. Семантика отдельно от бренд-акцента: розовый
   это среда, а не «плохо». */
.badge { display: inline-flex; align-items: center; gap: 5px; height: 22px; padding: 0 8px;
  border-radius: var(--r-pill); font-size: 12px; font-weight: 500; vertical-align: middle;
  background: var(--paper-2); color: var(--ink-2); border: 1px solid var(--line); }
.badge::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; opacity: .85; }
.badge--ok   { background: var(--ok-bg);   color: var(--ok);   border-color: var(--ok-line); }
.badge--warn { background: var(--warn-bg); color: var(--warn); border-color: var(--warn-line); }
.badge--bad  { background: var(--bad-bg);  color: var(--bad);  border-color: var(--bad-line); }
.badge--rose { background: var(--rose-tint); color: var(--rose); border-color: transparent; }
.badge--plain::before { display: none; }

/* Счётчик в заголовке группы («4 просрочено», «12») — тот же бэйдж, но без
   точки: он не статус, а число. Имя класса оставлено прежним: мест много. */
.tbadge { display: inline-flex; align-items: center; height: 22px; margin-left: 6px; padding: 0 8px;
  border-radius: var(--r-pill); background: var(--rose-tint); color: var(--rose);
  font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums; vertical-align: middle; }

/* Полоса — компонент дизайн-системы: 6px, пилюля, вдавленная дорожка.
   Была россыпью инлайновых стилей с высотой 8 и радиусом 99px. Заливка
   остаётся чернильной, а не зелёной: зелёный в этом приложении означает
   действие и главный факт, а полоса материала — сравнение, а не действие.
   Штриховка — «данных ещё мало»: полоса есть, но на неё рано опираться. */
.progress { height: 6px; border-radius: var(--r-pill); background: var(--paper-2); overflow: hidden; }
.progress__f { display: block; height: 100%; border-radius: inherit; background: var(--ink-2);
  transition: width var(--t-slow) var(--e-out); }
/* 🔴 Штриховка «мало данных» приехала розовой из БАЗЫ мастеров (#c9aeb8 /
   #f4e2e8) — в «Мраморе и петроле» это единственное розовое пятно на экране
   статистики. Собираем её из токенов салона: приглушённый акцент по бумаге. */
.progress--weak .progress__f {
  background: repeating-linear-gradient(135deg,
    color-mix(in srgb, var(--rose) 45%, var(--paper-2)) 0 4px,
    var(--rose-tint) 4px 8px); }

/* Заметка внутри содержимого: не карточка, а врезка со своим смыслом. */
.notice { display: flex; gap: var(--s3); padding: var(--s4);
  border-radius: var(--r-sm); border: 1px solid var(--line); background: var(--card-2); }
.notice svg { flex: none; width: 18px; height: 18px; margin-top: 2px; }
.notice--warn { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); }
.notice--bad  { background: var(--bad-bg);  border-color: var(--bad-line);  color: var(--bad); }
.notice--info { background: var(--info-bg); border-color: var(--info-line); color: var(--info); }
.notice b { display: block; color: var(--ink); font-size: 15px; margin-bottom: 2px; }
.notice p { margin: 0; color: var(--ink-2); font-size: 14px; line-height: 1.45; }

.gap { display: flex; align-items: center; gap: 14px; width: 100%; text-align: left; cursor: pointer;
  padding: 11px 14px; font: inherit; color: var(--ink-3);
  background: transparent; border: 1px dashed var(--line); border-radius: var(--r-sm); }
.gap__t { flex: 0 0 auto; min-width: 58px; font-family: 'IBM Plex Mono', monospace; font-size: 12px; }
.gap__l { font-size: 13px; }
/* Свободное окно — единственное место, где текст лежит прямо на фоне. На
   бумаге это нормально, на фотографии — нет. Дымку с фона мы убрали, поэтому
   подложку даём самой строке окна, а не всему экрану. */
body.has-wall .gap { background: color-mix(in srgb, var(--card) 62%, transparent); border-color: color-mix(in srgb, var(--card) 75%, transparent);
  backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); }
.gap:active { background: var(--rose-tint); }
.gap--past { opacity: .5; }

/* Выбор рабочих дней в «Моём графике» */
.dsel { display: flex; gap: 6px; flex-wrap: wrap; }
.dsel__d { flex: 1 1 40px; padding: 9px 4px; cursor: pointer; font: inherit; font-size: 13px;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--card); color: var(--ink-3); }
.dsel__d--on { background: var(--glass); color: #fff; border-color: rgba(255,255,255,.26); box-shadow: var(--glass-edge); }

/* ── Шапка карточки клиента ───────────────────────────────────────────────
   Длинное имя и список признаков раньше вылезали за карточку: теперь имя
   переносится, действия — компактные иконки, которые не отжимают текст. */
.chead { display: flex; align-items: flex-start; gap: 14px; }
.chead__t { flex: 1; min-width: 0; }
.chead__t .h1 { display: block; font-size: 26px; line-height: 1.12; overflow-wrap: break-word; }
.chead__t .meta { display: block; margin-top: 6px; overflow-wrap: anywhere; }
.chead__act { flex: none; display: flex; gap: 6px; }
.iconbtn { width: 38px; height: 38px; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--card); color: var(--ink-2);
  cursor: pointer; transition: border-color .16s ease, color .16s ease, transform .12s ease; }
.iconbtn:hover { border-color: var(--rose); color: var(--rose); }
.iconbtn:active { transform: scale(.94); }
.iconbtn--warn:hover { border-color: var(--bad); color: var(--bad); }
/* 🔴 На телефоне две кнопки-иконки справа отжимали имя и метки в узкий
   столбик: «Анна / Ковалёва», а под ними «клиент №1 · тонкая · склонная к
   отслойкам» в четыре строки шириной в одно слово. Ровно та каша, на которую
   Михаил показывал в материалах и в инструкции по РКН.
   Решение то же: действия уходят на свою строку под именем, а имени и меткам
   достаётся вся ширина карточки. Правка и удаление — не то, ради чего
   открывают карточку клиентки, и первым экраном им быть незачем. */
@media (max-width: 600px) {
  .chead { flex-wrap: wrap; }
  .chead__t { flex: 1 1 100%; order: 1; }
  .chead .ava { order: 0; }
  .chead__act { order: 2; margin-left: auto; }
  .chead__t .h1 { font-size: 20px; }
}
/* 🔴 Палец не попадает в 32 px. Иконки-действия в строках были 32×32 —
   на экране инструмента таких по две на каждой позиции, и промах приводил
   к открытию не той карточки. Минимум для нажатия — 44. */
@media (max-width: 900px) {
  .iconbtn { width: 44px; height: 44px; }
  .iconbtn--sm { width: 44px; height: 44px; }
}

/* Экран условий обработки данных клиенток: закрыть его, не приняв, нельзя —
   иначе поручение на обработку остаётся неподтверждённым. */
/* Шапка листа без заголовка не нужна вовсе — лист начинается сразу с дела */
.sheet--notitle .sheet__head { display: none; }

/* Иконка главного действия в ряду (поделиться): зелёное стекло вместо слова.
   Рядом стоят обычные .iconbtn — «скопировать» и «открыть». */
.iconbtn--act { background: var(--glass); color: #fff; border-color: rgba(255,255,255,.26);
  box-shadow: var(--glass-edge); }
.iconbtn--act:hover { border-color: rgba(255,255,255,.4); color: #fff; filter: brightness(1.06); }
/* «Поделиться кодом» под самим QR — по центру карточки */
/* Поделиться кодом — значком в правом верхнем углу карточки (просьба
   Михаила 13.09.2026): по центру под кодом кнопка разрывала карточку надвое
   и читалась как главное действие, хотя QR тут для печати. */
/* 🔴 Кнопка «поделиться» и кнопка переворота-подсказки обе метили в правый
   верхний угол и наложились друг на друга (Михаил 16.09). Подсказку вешает
   `salon-hints.js` на первую карточку блока — то есть как раз на эту.
   Ставим их столбиком, как он и попросил: стрелка переворота в самом углу,
   «поделиться» под ней. Так обе у правого края и ни одна не спорит с
   заголовком. */
.qrcard__share { position: absolute; top: calc(var(--s3) + 38px); right: var(--s3); margin: 0; }

/* ── Кнопки-значки круглые ────────────────────────────────────────────────
   Решение Михаила 15.09.2026: «давай все кнопки с свг сделаем круглыми».
   Раньше значок сидел в скруглённом квадрате (--r-sm), и рядом с круглыми
   кнопками отправки, круглым «Добавить» и круглым «Ещё» это выглядело как два
   разных набора. Круг — для кнопок, где ТОЛЬКО значок: .iconbtn во всех
   размерах (шапка, строки, лист, «поделиться»), значки связи в карточке
   клиента, ряд отправки и «Ещё» (у последних двух свой круг задан выше).
   🔴 Исключение — `.flip__btn` (стрелка переворота карточки): у неё нет ни
   фона, ни рамки, и круг там уже пробовали. Дословно, 14.09: «верни на
   стрелки и убери из под них тёмные круги». Форму ей не задаём. */
.iconbtn, .cardacts .btn { border-radius: 50%; }

/* Мелкие иконки-действия в строках (переотметить носку, удалить приём) */
.iconbtn--sm { width: 32px; height: 32px; flex: none; }
.row .iconbtn { margin-left: 6px; }
/* 🔴 Правило «палец не попадает в 32px» стояло выше по файлу и этой строкой
   перебивалось обратно: на телефоне мелкие иконки так и оставались 32×32.
   Повторяем после — теперь 44 действительно применяются. */
@media (max-width: 900px) {
  .iconbtn--sm { width: 44px; height: 44px; }
  .row--pick .iconbtn--sm { width: 40px; height: 40px; }   /* в листе выбора плотнее, но не 30 */
}

/* ── «Ещё»: группы вместо рулона ──────────────────────────────────────────
   Заголовок группы отвечает на вопрос «где искать», строка — только имя
   раздела и иконка. Описания под каждым пунктом превращали меню в текст,
   который читают, вместо списка, по которому попадают с первого взгляда. */
.mgrp { margin-bottom: var(--s4); }
.mgrp > .kick { display: block; margin: 0 0 8px 2px; }
.mgrp:last-of-type { margin-bottom: var(--s3); }
.row--nav { min-height: 56px; }
/* Кнопка темы стоит в шапке приложения: на телефоне в тулбаре, на десктопе
   в шапке сайдбара. Это переключатель вида, а не раздел, — среди разделов его
   неудобно искать (просьба Михаила 13.09.2026). */
.side__brand .sp { flex: 1; min-width: 0; }
/* Имя приложения в одну строку: из-за кнопки темы оно ломалось на
   «BAZA / MASTERA». Подзаголовок при нехватке места обрезается многоточием —
   он вспомогательный, в отличие от названия. */
.side__brand b, .side__brand small { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Кнопка темы отняла у строки ширину, и названию не хватило восьми точек —
   добираем их у аватара, отступов и зазора, а не кеглем: имя приложения
   должно читаться полностью. */
.side__brand { gap: 10px; padding-left: 4px; padding-right: 4px; }
.side__logo { width: 34px; height: 34px; }
.side__theme { flex: none; }
.row--nav .sp b { font-weight: 500; }
.row__ic { flex: none; width: 38px; height: 38px; display: grid; place-items: center;
  border-radius: var(--r-sm); background: var(--rose-tint); color: var(--rose); }
/* Размер шеврона задаёт сама svg, кегль здесь ни на что не влиял */
.row--nav .row__chev { color: var(--ink-3); }
/* Выход в кабинет — последняя строка списка, а не прилипшая к низу кнопка:
   у листа «Ещё» главного действия нет, и sheet__add накрывал собой группу. */
.mgrp__out { margin-top: var(--s2); }
/* Пока открыт лист — ни пузыря переписки, ни кнопки действия поверх него */
body.sheet-open .dm-fab, body.sheet-open .bm-act { display: none !important; }

/* Строка приёма на телефоне. Прошлая правка развела по строкам дату и иконки,
   но кнопка «Отметить носку» осталась без порядка — она вставала рядом с текстом
   и отжимала его в колонку шириной в одно слово, растягивая карточку на пол-экрана.
   Теперь раскладка ярусная и одинаковая для обоих состояний приёма:
     1) дата слева, носка и мелкие действия справа;
     2) материалы — во всю ширину, не длиннее двух строк;
     3) клиент, фрезы и время — одной строкой с многоточием;
     4) «Отметить носку» — отдельной кнопкой во всю ширину.
   Порядок задан КАЖДОМУ элементу строки: без него flex ставит новичка в начало. */
@media (max-width: 700px) {
  .row--visit { flex-wrap: wrap; align-items: center; row-gap: 6px; column-gap: 8px; padding: 12px 14px; }
  .row--visit .row__key { order: 0; width: auto; margin-right: auto; }
  .row--visit .row__val { order: 1; }
  .row--visit .iconbtn { order: 2; margin-left: 0; }
  .row--visit .sp { order: 3; flex: 1 0 100%; }
  /* 🔴 Надпись не обрезаем: текст сокращает код («ещё N»), а вёрстка его
     показывает целиком. Клэмп в две строки съедал хвост названия. */
  .row--visit .sp b { overflow-wrap: anywhere; line-height: 1.3; }
  /* 🔴 Была одна строка с многоточием, и «Ольга Пантелеймоненко-Штерн · F-001 ›
     F-…» обрывалась ровно там, где начинается полезное — чем работали. Теперь
     две строки с переносом: длинная фамилия не съедает список инструмента. */
  .row--visit .sp small { margin-top: 2px; white-space: normal; overflow-wrap: anywhere; line-height: 1.35; }
  .row--visit > .btn { order: 4; flex: 1 0 100%; margin-top: 2px; }
}

/* ── Выбор профессии: первый экран новой мастера ─────────────────────────
   Строками во всю ширину, а не плиткой: названия у профессий длинные
   («Косметология и уход за лицом»), а плитка их режет — ровно то, чего
   Михаил просил не допускать. Подпись переносится, строка тянется. */
.nrow { align-items: flex-start; gap: 12px; padding: 13px 14px; border-radius: var(--r-sm);
  border: 1px solid var(--line); margin-bottom: 8px; background: var(--card); }
.nrow:last-child { margin-bottom: 0; }
.nrow .sp b { white-space: normal; }
.nrow .sp small { white-space: normal; margin-top: 2px; line-height: 1.35; }
.nrow__ic { flex: none; width: 38px; height: 38px; border-radius: var(--r-sm); display: grid; place-items: center;
  background: color-mix(in srgb, var(--green) 8%, transparent); color: var(--ink-on-green); }
/* Галочка держит своё место всегда: без неё строки дёргались бы по ширине
   в момент выбора — список из одиннадцати пунктов это заметно трясёт. */
.nrow__tick { flex: none; width: 22px; height: 22px; display: grid; place-items: center;
  color: var(--green); opacity: 0; transform: scale(.7); transition: opacity .16s, transform .16s; }
/* Наведение не должно спорить с выбором: розовая заливка .row--link читалась
   как «уже выбрано». Под курсором — только рамка, выбор всегда зелёный. */
.nrow:hover { background: var(--card); border-color: rgba(22,100,95,.45); }
.nrow { -webkit-tap-highlight-color: color-mix(in srgb, var(--green) 12%, transparent); }
.nrow:active { transform: scale(.995); }
.nrow.is-on, .nrow.is-on:hover { border-color: var(--green); background: color-mix(in srgb, var(--green) 7%, transparent); }
.nrow.is-on .nrow__ic { background: var(--glass); color: #fff; border: 1px solid rgba(255,255,255,.26);
  box-shadow: var(--glass-edge); }
.nrow.is-on .nrow__tick { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .nrow__tick { transition: none; } }

/* 🔴 Одиннадцать профессий не помещаются в лист, и кнопка «Продолжить»
   уезжала под список: чтобы её нажать, надо было доскроллить до конца.
   Прокручивается список, главное действие остаётся на виду. */
/* 🔴 Своего скролла у списка НЕТ намеренно: прокручивается сам лист, а кнопка
   «Продолжить» живёт в липком футере (.sheet__stick). Вложенный скролл внутри
   скролла на телефоне съедал тапы по строкам — выбрать профессию было нельзя. */
.nlist { margin-top: 14px; }

/* 🔴 Вид приёма: у разных профессий подписи разной длины, и на 375px три
   пункта режутся краем (у сегмента nowrap + overflow:hidden). Подписи в
   niches.js подобраны под эту ширину, а здесь — страховка на узкие экраны
   и на профессии, которые добавятся позже. */
@media (max-width: 400px) {
  #v_service .seg__item { font-size: 12px; padding-left: 2px; padding-right: 2px; }
}

/* 🔴 Роли материалов у разных профессий разной длины: «топ» и «размягчитель».
   Жёсткие 62px срезали длинные слова прямо в соседнюю колонку. Ширина —
   по содержимому, с потолком, чтобы название материала не ужималось. */
.row__key--wide { width: auto; min-width: 62px; max-width: 116px; }

/* Полоса «позиции другой профессии скрыты». Мастер, сменившая профессию,
   должна видеть, что список не потерялся, — иначе решит, что база пропала. */
.nnote { display: block; width: 100%; text-align: left; margin: 0 0 10px; padding: 10px 14px;
  border: 1px dashed rgba(22,100,95,.4); border-radius: var(--r-sm); background: none;
  color: var(--ink-3); font: inherit; font-size: 13px; cursor: pointer; }
.nnote:hover { border-color: var(--rose); color: var(--ink-2); }

/* К кому человек ходит, если не только по текущей работе мастера: одна и та же
   женщина бывает и на ногтях, и на бровях. У мастера одной профессии таких
   меток нет вовсе. */
.cmark--niche { background: color-mix(in srgb, var(--green) 10%, transparent); color: var(--ink-on-green); }

/* Действия по записи — одной строкой. Раньше их было три ряда, и «Карточка
   клиента» с «Провести приём» ломались на две строки. Иконка и короткое слово
   под ней: так же устроен таб-бар, где шесть подписей уживаются на 360px.
   Полное название остаётся в title и aria-label. */
.apact { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 6px; margin-top: 12px; }
.apact__b { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  min-height: 60px; padding: 8px 2px; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--card); color: var(--ink-2); font: inherit; font-size: 11px; font-weight: 500;
  line-height: 1.1; cursor: pointer; white-space: nowrap; }
.apact__b:hover { border-color: rgba(22,100,95,.45); color: var(--ink); }
.apact__b:active { transform: scale(.97); }
.apact__b--warn { color: var(--rose); }
.apact__b--warn:hover { border-color: var(--rose); color: var(--rose); }
@media (max-width: 360px) { .apact { gap: 4px; } .apact__b { font-size: 10px; padding: 8px 1px; } }

/* Строка «кого пора спросить» на десктопе. Была плоским рядом из шести
   элементов — на узкой колонке срок вставал между строками переносящегося
   текста. Теперь сетка: кто и когда сверху, чем работали под ним, действия
   своим рядом справа. */
.wrow { display: grid; grid-template-columns: auto 1fr auto; gap: 4px 12px;
  align-items: center; padding: 14px 18px; border-bottom: 1px solid var(--line); }
.wrow:last-child { border-bottom: 0; }
.wrow__ava { grid-row: span 2; }
.wrow__who { min-width: 0; }
.wrow__who b { display: block; font-size: 15px; font-weight: 600; }
.wrow__who small { display: block; font-size: 13px; color: var(--ink-3); line-height: 1.35; }
.wrow__age { font-size: 13px; color: var(--ink-2); white-space: nowrap; align-self: start; padding-top: 2px; }
.wrow__act { grid-column: 2 / -1; display: flex; gap: 8px; align-items: center; margin-top: 8px; flex-wrap: wrap; }
.wrow__act .btn { margin: 0; }

/* ── Текст поверх обоев ───────────────────────────────────────────────────
   🔴 Решение Михаила: текст БЕЗ подложки перекрашивается в белый. Текст на
   подложке — карточка, лист, стекло, меню, плашка, строка, поле — остаётся
   своего цвета.

   🔴 Список подложек вести нельзя. Я попробовал: перечислил карточку, стекло,
   лист, меню, шапку, строку — и всё равно «СООБЩЕНИЯ» в карточке стало белым
   на белом, потому что `.kick--plain` живёт и на обоях, и внутри карточек.
   Опрос живой разметки нашёл больше двадцати фонов (dchip, tl, wg, gap, ask,
   sh, tbadge, work, tab-note, dmon…), и любой новый компонент ломал бы правило
   заново. Поэтому правило **опт-ин**: красится только то, что помечено
   классом `.on-wall`, а метку ставит `markWall()` в salon.js — она проверяет
   реальные фоны предков, а не их названия. Новый компонент с фоном
   учитывается сам собой.

   Белый держит тёмный ореол в три слоя: узкий контур не даёт буквам слиться
   со светлым снимком, средний даёт объём, широкий гасит пестроту под строкой.
   Двух слоёв не хватало — на светлом фото читалось бледно.
   ⚠️ Вуаль мастера тут НЕ помощник: она цвета бумаги, то есть светлая. */
body.has-wall .on-wall {
  color: #fff;
  text-shadow:
    0 0 1px rgba(30,10,16,.85),
    0 1px 3px rgba(30,10,16,.72),
    0 0 16px rgba(30,10,16,.6);
  background: none; backdrop-filter: none; -webkit-backdrop-filter: none;
}

/* 🔴 Светлое фото в обоях: чернила вместо белил. Белый текст поверх светлого
   снимка давал контраст 2,0 при норме 4,5 — на медиатеке подпись пропадала
   целиком (померено 18.09.2026). Яркость снимка считает `wallTone()` в
   приложении и ставит `data-walltone`; здесь только цвет и ореол — зеркально
   прежнему: тёмные буквы со светлым свечением. Тёмные фото ведут себя
   по-старому, правило «на фотографии подписи без подложки» не меняется. */
body.has-wall[data-walltone="light"] .on-wall {
  color: var(--ink);
  text-shadow:
    0 0 1px rgba(255,255,255,.92),
    0 1px 3px rgba(255,255,255,.85),
    0 0 16px rgba(255,255,255,.75);
}
/* В тёмной теме у вуали есть нижний предел (.28), фон под текстом всегда
   притемнён — там белый работает и на светлом снимке. */
[data-theme="dark"] body.has-wall[data-walltone="light"] .on-wall {
  color: #fff;
  text-shadow:
    0 0 1px rgba(30,10,16,.85),
    0 1px 3px rgba(30,10,16,.72),
    0 0 16px rgba(30,10,16,.6);
}


/* Доза препарата: поле стоит в строке под своей ролью, поэтому оно узкое и
   не тянется на всю карточку — иначе «24» выглядели бы как заметка.
   Подсказка идёт под полем в той же строке: абзацем под карточкой она
   прилипала к следующей роли. */
.row--dose { align-items: flex-start; }
.row--dose .sp { display: flex; flex-direction: column; gap: 5px; }
.field--dose { width: 96px; min-height: 40px; padding: 8px 10px; }
.row--dose .row__val { padding-top: 12px; }

/* ── Инструкция «Уведомление в Роскомнадзор»: поля для копирования ─────────
   🔴 Правка Михаила 13.09.2026: на телефоне эти карточки были обычной строкой
   `.row` — подпись занимала фиксированную колонку 116px, кнопка «скопировать»
   ещё 44, и значение оставалось в узком столбце по одному-двум словам в строке.
   Плюс подпись прижималась к левому краю карточки, а кнопка — к правому.
   Теперь: подпись сверху, значение во всю ширину, кнопка в правом верхнем углу,
   отступ от края карточки одинаковый слева и справа. */
.rknf { padding: 14px 16px; }
.rknf + .rknf { border-top: 1px solid var(--line); }
.rknf__top { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.rknf__key { font-family: 'IBM Plex Mono', monospace; font-size: 10px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-3); padding-top: 11px; line-height: 1.3; }
/* Кнопка не вылезает за отступ карточки и не растягивается под длинной подписью */
.rknf__copy { flex: none; }
.rknf__val { margin: 6px 0 0; font-weight: 500; line-height: 1.5; color: var(--ink);
  overflow-wrap: anywhere; }
.rknf__note { margin: 8px 0 0; font-size: 12px; line-height: 1.45; color: var(--ink-3); }
@media (max-width: 420px) {
  /* На узком экране подпись уходит своей строкой целиком — так она читается
     как заголовок поля, а не как обрывок рядом с кнопкой. */
  .rknf { padding: 12px 14px; }
  .rknf__key { padding-top: 12px; }
}

/* ── Карточка с QR-кодом (согласие, публичная запись) ──────────────────────
   🔴 Скриншот Михаила 13.09.2026: код уезжал вправо, подпись оставалась слева
   внизу. Причина не в выравнивании: `.kick` — inline-flex, то есть строку он
   НЕ разрывает, и подпись с картинкой вставали на одну строку. `text-align:
   center` центрировал их как пару, отчего код и оказывался сбоку.
   Колонка с явным центрированием — и порядок больше не зависит от того,
   какой у подписи display. */
.qrcard { position: relative; display: flex; flex-direction: column; align-items: center; gap: 12px; text-align: center; }
/* Подпись не должна заезжать под кнопку в углу */
/* Справа над заголовком стоит стрелка переворота (кнопка «поделиться» ушла под
   неё), поэтому запас нужен с обеих сторон — но одной кнопки, а не двух. */
.qrcard .kick { margin: 0; padding: 0 44px; }
/* 🔴 Подложка QR остаётся БЕЛОЙ в любой теме: код считывается по контрасту
   чёрного на белом, на тёмной подложке камера его просто не возьмёт. */
.qrcard__img { display: block; width: 100%; max-width: 200px; border-radius: var(--r-sm); background: #fff; }
.qrcard .btn { width: 100%; }
.qrcard .meta { margin: 0; }

/* ── Календарь исключений в «Моём графике» ─────────────────────────────────
   Недельный рисунок отвечает на «как обычно», а этот календарь — на «а вот
   в эту среду не работаю» (просьба Михаила 13.09.2026). Клетка — цель для
   пальца, поэтому 40px в высоту даже на узком экране: семь колонок по 44
   не помещаются на 320px, а 40 с зазором 4 — помещаются. */
.cal { margin-top: 10px; }
.cal__top { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.cal__top b { font-weight: 600; font-size: 15px; text-transform: lowercase; }
.cal__wd { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin-bottom: 4px; }
.cal__wd span { text-align: center; font-family: 'IBM Plex Mono', monospace; font-size: 9.5px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); }
.cal__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.cal__x { display: block; }
.cal__d {
  min-height: 40px; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--card);
  color: var(--ink); font: inherit; font-size: 14px; cursor: pointer;
  transition: border-color .14s ease, background .14s ease, color .14s ease;
}
.cal__d--today { border-color: var(--rose); font-weight: 600; }
/* Выходной — приглушённый, рабочий — обычный. Зелёным красим только то, что
   мастер отметила руками: так видно, где график отличается от обычного. */
.cal__d--off { background: var(--rose-tint); color: var(--ink-3); text-decoration: line-through; }
.cal__d--mark { border-color: var(--green); color: var(--ink-on-green); font-weight: 600; }
.cal__d--off.cal__d--mark { background: var(--rose-tint); color: var(--rose); border-color: var(--rose); }
.cal__d--past { opacity: .35; pointer-events: none; }
.cal__d:active { transform: scale(.96); }
/* Точка «в этот день есть записи» на календаре исключений */
.cal__d { position: relative; }
.cal__dot { position: absolute; bottom: 5px; left: 50%; transform: translateX(-50%);
  width: 4px; height: 4px; border-radius: 50%; background: var(--green); }
.cal__d--off .cal__dot { background: var(--rose); }

/* ── Материалы по брендам ──────────────────────────────────────────────────
   Просьба Дарьи 13.09.2026: «у бренда большой перечень материалов, и чтобы не
   скроллить огромный список — кнопка с названием бренда, по нажатию
   разворачивается». Заголовок группы — цель для пальца во всю ширину. */
.bgrp { border-bottom: 1px solid var(--line); }
.bgrp:last-child { border-bottom: 0; }
.bgrp__h {
  width: 100%; display: flex; align-items: center; gap: 12px; padding: 14px 18px;
  background: none; border: 0; font: inherit; color: inherit; text-align: left; cursor: pointer;
  min-height: 52px;
}
.bgrp__h:hover { background: var(--card-2); }
.bgrp__h b { flex: 1; min-width: 0; font-size: 15px; font-weight: 600; }
/* Счётчик позиций — мелко и моно, как остальные служебные числа в приложении */
.bgrp__n { font-family: 'IBM Plex Mono', monospace; font-size: 12px; color: var(--ink-3); flex: none; }
.bgrp__chev { display: flex; color: var(--ink-3); flex: none; transition: transform .16s ease; }
.bgrp--open .bgrp__chev { transform: rotate(90deg); color: var(--green); }
.bgrp--open .bgrp__h b { color: var(--ink-on-green); }
/* Строки внутри группы — с отступом, чтобы было видно, что они принадлежат
   бренду. 🔴 Подложка — отдельной плашкой со скруглением, а не прямоугольником
   во всю ширину: «кривые угловатые подложки» (Михаил 14.09.2026). Ширина у всех
   одинаковая, потому что задаётся отступами, а не содержимым. */
.bgrp__b { padding: 0 10px 6px; }
.bgrp__b .row { padding-left: 20px; background: var(--bgrp-bg);
  border-radius: var(--r-sm); margin-top: 6px; }
.bgrp__b .row + .row::before { display: none; }   /* плашки уже разделены зазором */
.bgrp__b .row:last-child { border-bottom: 0; }
/* 🔴 Фон группы перебивал отметку выбора: у `.bgrp__b .row` та же весомость,
   но правило стоит ниже — выбранная строка внутри бренда переставала зеленеть. */
.bgrp__b .row--pick.is-on { background: color-mix(in srgb, var(--green) 10%, transparent); }

/* Состав связки: два списка в одном листе. Без потолка высоты форма уезжала на
   несколько экранов, и кнопка «Сохранить» оказывалась под сотней материалов. */
.cpick { max-height: 46vh; overflow-y: auto; -webkit-overflow-scrolling: touch; }

/* Строка связки: название, под ним счёт состава, ниже сами позиции. Названия
   держим в двух строках — полный состав открывается в карточке. */
.row--combo { align-items: flex-start; }
.row .combo__parts { margin-top: 2px; color: var(--ink-2); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

/* ── Сетка смен: неделя строками по мастерам ──────────────────────────────
   Прокручивается вбок на телефоне: семь дней в 375px не влезают никак, а
   сжимать до нечитаемого — хуже, чем прокрутить. Имя мастера липнет к левому
   краю, иначе при прокрутке непонятно, чья это строка. */
.shgrid { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.shrow { display: grid; grid-template-columns: 116px repeat(7, minmax(56px, 1fr)); gap: 6px; align-items: stretch; margin-bottom: 6px; min-width: 520px; }
.shname { position: sticky; left: 0; z-index: 1; display: flex; align-items: center; padding-right: 6px;
  font-size: 13px; font-weight: 600; color: var(--ink); background: var(--card); overflow-wrap: anywhere; }
.shrow--head .shname { background: transparent; }
/* Ряд действий в шапке экрана. На широком — в строку справа; на телефоне
   переносится и прокручивается вбок, чтобы ни одна кнопка не уехала за край.
   🔴 Раньше стоял инлайновый flex без переноса: сегмент «Неделя / День по
   мастерам» ломался на две строки, а «Мой график» обрезался экраном. */
.hd__acts { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
@media (max-width: 720px) {
  .hd__acts { width: 100%; margin-top: 10px; }
  .hd__acts .seg { width: 100%; }
  .hd__acts .seg .seg__item { flex: 1 1 0; }
}

.shday { display: flex; flex-direction: column; align-items: center; justify-content: center;
  font-size: 11px; color: var(--ink-3); letter-spacing: .04em; }
.shday b { font-size: 15px; color: var(--ink); font-weight: 600; }
.shday--now b { color: var(--green); }
.shcell { min-height: 46px; border: 1px dashed var(--line-2); border-radius: var(--r-sm);
  background: var(--paper-2); color: var(--ink-3); font: 600 12px/1.25 inherit;
  display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: pointer; }
.shcell:disabled { opacity: .5; cursor: default; }
.shcell small { font-weight: 500; opacity: .8; }
.shcell--on { border-style: solid; border-color: transparent; background: var(--green); color: #fff; }
/* Неподтверждённая смена — контур, а не заливка: видно, что это ещё просьба. */
.shcell--wait { background: var(--rose-tint); color: var(--green); border-color: var(--green); border-style: dashed; }
@media (max-width: 720px) { .shrow { grid-template-columns: 96px repeat(7, 58px); } }

/* ── День по мастерам: колонка на человека ────────────────────────────────
   Строится той же механикой, что недельная шахматка (сетка + слой блоков
   поверх), но колонки — люди, а не дни. Час = 60px, поэтому длительность
   записи переводится в пиксели один к одному. */
/* 🔴 На телефоне колонки мастеров не влезают по ширине — даём прокрутку вбок
   и не даём им сжиматься в нечитаемые полоски. Сам вид на телефоне нужен:
   администратор смотрит день по креслам со стойки, а не с ноутбука. */
.shd { --h: 60px; display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.shd__head, .shd__body { min-width: max-content; }
.shd__head { display: grid; grid-template-columns: 56px repeat(var(--cols), minmax(150px, 1fr)); gap: 0;
  position: sticky; top: 0; z-index: 3; background: var(--paper); padding-bottom: 6px; }
.shd__col { padding: 6px 8px; border-left: 1px solid var(--line); }
.shd__col b { display: block; font-size: 13px; font-weight: 600; color: var(--ink); }
.shd__col span { display: block; font-size: 11px; color: var(--ink-3); margin-top: 2px; }
/* Колонка «Салон» — записи без исполнителя. Отличаем её визуально, иначе
   администратор примет их за чью-то чужую работу. */
.shd__col--house b { color: var(--ink-3); }
.shd__body { position: relative; display: grid;
  grid-template-columns: 56px repeat(var(--cols), minmax(150px, 1fr));
  grid-auto-rows: var(--h); }
.shd__time { font-size: 11px; color: var(--ink-3); padding-top: 2px; }
.shd__cell { border-left: 1px solid var(--line); border-top: 1px solid var(--line); }
.shd__cell--off { background: var(--paper-2); }
.shd__cell[data-dayslot] { cursor: pointer; }
.shd__cell[data-dayslot]:hover { background: var(--rose-tint); }
.shd__layer { position: absolute; inset: 0 0 0 56px; pointer-events: none;
  display: grid; grid-template-columns: repeat(var(--cols), minmax(150px, 1fr)); }
/* Деньги приёма: подпись под суммой не должна слипаться со следующим
   заголовком — на телефоне «…не попадёт в выручку.» упиралась в «ЧЕМ
   ЗАПЛАТИЛИ». */
#vMoneyWrap .f + .f { margin-top: 14px; }

/* Колонка слоя: в ней и живут блоки записей. Своя система координат нужна,
   потому что absolute внутри grid игнорирует grid-column (см. salon.js). */
.shd__lcol { position: relative; }
/* 🔴 Формула времени была невалидной: `var(--top) * (var(--h)/60) * 1px` даёт
   произведение двух длин (px × px), и браузер молча отбрасывал всё правило —
   блок прилипал к верху сетки и получал высоту по содержимому. Считаем часы
   как число: минуты ÷ 60 × высоту часа. */
.shd__appt { pointer-events: auto; position: absolute; left: 4px; right: 4px;
  border: 0; border-radius: var(--r-sm);
  background: var(--green); color: #fff; text-align: left; padding: 6px 8px; cursor: pointer;
  top: calc(var(--top) / 60 * var(--h)); height: calc(var(--len) / 60 * var(--h) - 4px);
  overflow: hidden; }
.shd__appt b { display: block; font-size: 12px; font-weight: 600; }
.shd__appt small { display: block; font-size: 11px; opacity: .85; }
.shd__appt.is-done { background: var(--ink-3); }
.shd__appt.is-noshow { background: var(--bad); }
.shd__appt.is-late { background: var(--warn); color: var(--ink); }
.shd__appt.is-new { outline: 2px dashed var(--rose); outline-offset: -2px; }

/* Метка категории клиента: «давно не был» — повод действовать, а не украшение,
   поэтому она заметнее ниши, но тише чёрного списка. */
.cmark--cat { background: var(--warn-bg); color: var(--warn); border-color: var(--warn-line); }

/* ── Мессенджер Даши (общий модуль direct.js) ─────────────────────────────
   🔴 Модуль один на витрину, кабинет, БАЗУ и салон, а палитра у него зашита
   розово-травяная (розовая иконка #f0398a, травяная кнопка «отправить»,
   бордовое окно). В «Мраморе и петроле» это было единственное чужое пятно на
   экране — и единственное место, где после чистки 17.09 оставался зелёный из
   БАЗЫ мастеров. Модуль отдал точки подмены, мы задаём в них свои цвета.

   🔴 Специфичность не косметика: свои `--dm-*` модуль ставит прямо на
   `.dm-back` и на `[data-theme="dark"] .dm-back`, а его <style> приезжает в
   head ПОЗЖЕ этого файла. Поэтому переопределяем с `html[data-theme]` —
   иначе правило выглядит применённым, а окно остаётся бордовым. */
:root {
  --dm-fab: var(--rose-2);                 /* иконку надо находить глазом */
  --dm-fab-sh: color-mix(in srgb, var(--rose-2) 70%, transparent);
  --dm-me: var(--glass);                   /* мой пузырь и «отправить» */
  --dm-dot: var(--rose);
}
html[data-theme] .dm-back {
  --dm-card: var(--card); --dm-ink: var(--ink); --dm-ink-2: var(--ink-3); --dm-line: var(--line);
  --dm-her: var(--rose-tint); --dm-her-ink: var(--ink); --dm-field: var(--card-2);
  --dm-ava: var(--rose-tint); --dm-ava-ink: var(--rose);
  --dm-veil: rgba(14,34,33,.34); --dm-file: color-mix(in srgb, var(--ink) 6%, transparent);
  --dm-err: var(--bad);
}
html[data-theme="dark"] .dm-back {
  --dm-veil: rgba(4,14,14,.55); --dm-file: rgba(255,255,255,.07);
}

/* ══ ПЛАНШЕТ: 620–900px ══════════════════════════════════════════════════
   🔴 До 18.09.2026 этой зоны в стилях не было вовсе (`docs/ROADMAP.md` §2:
   «десктоп разобран, планшет нет»). Всё, что ниже 901px, считалось телефоном:
   на iPad в руках администратора салона строка клиента растягивалась на 780px — имя слева,
   стрелка у правого края, между ними полметра пустоты, — а кнопка «Держится»
   превращалась в полосу во весь экран.

   Телефонный каркас (таб-бар, кнопка действия внизу, заголовок в шапке)
   оставляем: планшет держат в руках, и дотянуться до верха так же неудобно.
   Правим ровно то, что ломает ШИРИНА. */
@media (min-width: 620px) and (max-width: 900px) {
  /* 1. Колонка содержимого не шире 720px и по центру. Читать строку длиной
     в экран невозможно: глаз теряет начало следующей. */
  .screen > .hd, .screen > .stack, .screen > .cols {
    max-width: 720px; margin-left: auto; margin-right: auto;
  }
  /* Исключения — экраны, которым ширина нужна по делу: шахматка дня,
     сетка снимков и графики статистики. */
  #s-appts > .hd, #s-appts > .stack,
  #s-media > .hd, #s-media > .stack { max-width: none; }

  /* 2. Записи показываем ШАХМАТКОЙ, как на десктопе: на 820px в неделю
     выходит по ~105px на день — это читается, а «день с окнами» оставлял
     одну полосу и пустой экран под ней. Выбор «неделю списком» остаётся
     главнее: мастер его нажала сама. */
  /* 🔴 `!important` здесь обязателен: правило `.only-desk { display:none
     !important }` живёт в блоке «до 900px» и без него побеждает — шахматка
     оставалась скрытой, а на её месте был пустой экран (поймано проверкой). */
  #apptsGrid.only-desk { display: block !important; }
  #apptsList.only-mob { display: none !important; }
  body[data-apptview="week"] #apptsGrid.only-desk { display: none !important; }
  body[data-apptview="week"] #apptsList.only-mob { display: block !important; }

  /* 3. Кнопка во всю ширину — приём телефона, где палец попадает не целясь.
     На планшете это полоса в 720px: то же решение, что и на десктопе. */
  .btn--block { display: inline-flex; width: auto; min-width: 220px; max-width: 360px; }
  .sheet__stick { text-align: center; }
  /* Пара кнопок в подвале листа делит строку — им потолок не нужен. */
  .sheet__stick--row .btn { max-width: none; }

  /* 4. Сегмент-дорожка: плашка, едущая через весь экран, выглядит поломкой. */
  .seg--control { max-width: 560px; }

  /* 5. Лист всплывает узким окном, а не во всю ширину планшета: держать
     форму на 720px и целиться пальцем от края до края неудобно. */
  .sheet { max-width: 620px; margin-left: auto; margin-right: auto; }
}


/* ══ ЭКРАН ВХОДА В ПРИЛОЖЕНИЕ (/baza, /salon для неавторизованного) ═════════
   Одна страница на оба продукта, цвета приходят из подключённого файла
   приложения. Одно действие на экран, поля крупные: сюда попадают с телефона,
   часто одной рукой и в чужом кабинете. */
.app-login { min-height: 100svh; display: grid; place-items: center; padding: 24px 16px; }
.plog { width: 100%; max-width: 420px; }
.plog__box { width: 100%; }
.plog__title { margin: 6px 0 4px; }
.plog__sub { margin-bottom: 18px; }
.plog__root { display: block; }
.plog__form { display: block; margin-bottom: 4px; }
.plog__form .btn--block { margin-top: 6px; }
.plog__err { color: var(--rose); font-size: 14px; margin: 0 0 12px; }
.plog__link { display: inline-flex; margin-top: 10px; padding-left: 0; }
.plog__or { text-align: center; margin: 16px 0 10px; }
.plog__social { display: flex; flex-direction: column; gap: 10px; }
.plog__foot { margin-top: 14px; line-height: 1.5; }
.plog__foot a { color: inherit; }
/* На десктопе карточка не должна растягиваться на весь экран: это форма, а не
   раздел приложения. */
@media (min-width: 620px) { .plog { max-width: 400px; } }

/* Кнопка входа по Face ID стоит над полями: тому, у кого ключ привязан,
   вводить ничего не нужно. */
.plog__pk { margin-bottom: 4px; }
.plog__pkerr { color: var(--rose); margin: 8px 0 0; }
.plog__pk .plog__or { margin: 14px 0 12px; }

/* ══ ЗАМОК ПРИЛОЖЕНИЯ ══════════════════════════════════════════════════════
   Поверх всего, без прозрачности: за ним база клиентов, и «подсмотреть сквозь»
   не должно получаться даже краем глаза. */
.applock { position: fixed; inset: 0; z-index: 9999; display: grid; place-items: center;
  padding: 24px 16px; background: var(--paper); }
.applock__card { width: 100%; max-width: 380px; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-lg, 18px); padding: 24px 20px; box-shadow: var(--sh, 0 18px 40px -28px rgba(20,12,16,.5)); }
.applock__kick { font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 10px;
  letter-spacing: .18em; text-transform: uppercase; color: var(--ink-3); margin: 0 0 10px; }
.applock__title { margin: 0 0 8px; }
.applock__sub { color: var(--ink-2); font-size: 14px; line-height: 1.5; margin: 0 0 18px; }
.applock__err { color: var(--rose); font-size: 13px; margin: 10px 0 0; }
.applock__out { margin-top: 12px; padding-left: 0; }
[data-theme="dark"] .applock { background: var(--paper); }

.applock__tap { font-size: 12px; color: var(--ink-3); margin: 12px 0 0; text-align: center; }
.applock__card { cursor: pointer; }

.applock__pass { margin-top: 12px; display: grid; gap: 8px; }
.applock__alt { margin-top: 10px; padding-left: 0; }
