/* Мини-апп заказ-нарядов. Редизайн v2.

   Имена классов те же, что в боевом приложении: файл кладётся вместо
   старого app.css и работает без правок JS. Что нельзя сделать одним CSS,
   перечислено в ВНЕДРЕНИЕ.md.

   Три вещи, ради которых всё переделано.

   1. Заголовки. Было: .h2 15px приглушённым серым, то есть тише текста
      под собой. Стало: 19px полужирным светлым. Владелец 22.08.2026:
      «заголовки надо выделить».

   2. Расстояния. Было: 8-10px между нажимаемыми элементами при подушечке
      пальца в 45-50px. Стало: цель не ниже 48px, зазор 12px, между
      действиями с разными последствиями 24px. Владелец: «чтобы палец
      случайно не попадал на другие кнопки».

   3. Подтверждение. Всё, что уходит человеку наружу, требует двух нажатий.
      Взведённая кнопка становится янтарной: этот цвет больше нигде
      не встречается, спутать не с чем. */

:root {
  color-scheme: dark;

  /* ПАЛИТРА. Одна нейтральная ось и два акцента.
   *
   * 23.08.2026, посчитано по файлу. Было шесть цветовых семейств, и главное:
   * основной текст стоял на 100° (зелёный сектор), поверхности на 213-216°
   * (синий), вторичный текст на 207°. Белый текст не был согласован
   * ни с фоном, ни с собственным серым - отсюда ощущение грязного набора
   * цветов. Плюс белая табличка номера уходила в жёлто-зелёный.
   *
   * Теперь как в системе iOS: все поверхности на одной оси с насыщенностью
   * 4%, то есть практически нейтральные; подписи не отдельные серые цвета,
   * а полупрозрачный белый с холодным отливом (235,235,245) - он ложится
   * на любую подложку и всегда с ней согласован.
   *
   * Акцента два: фирменный зелёный и красный на ошибку. Оранжевый - только
   * взведённая кнопка, системный. Голубое пятно на фотографии убрано:
   * это было седьмое семейство и чистое украшение.
   */

  /* Поверхности. Графит, а не чёрный: чёрный на телефоне в боксе бликует. */
  --void:   #0B0B0D;   /* шапка, нижняя панель */
  --paper:  #121214;   /* фон страницы */
  --panel:  #1C1C1F;   /* подложка секции, плоское значение для стыков */
  --raise:  #2A2A2E;   /* поле ввода, нажатое состояние */

  /* Поверхности НЕ заливаются ровным цветом. Разница верх-низ около 5%:
     хватает, чтобы предмет поймал свет сверху и перестал быть краской,
     и мало, чтобы кто-то заметил градиент как приём. Свечения нет:
     владелец забраковал «компьютерную игру», это правило остаётся. */
  --lift:   linear-gradient(180deg, #212124 0%, #191919 100%);
  --lift-2: linear-gradient(180deg, #2E2E33 0%, #26262A 100%);

  /* Линии. Полупрозрачные, чтобы одинаково ложиться на любую подложку. */
  --edge:   rgba(255, 255, 255, .08);
  --edge-2: rgba(255, 255, 255, .14);

  /* Текст. Не отдельные серые цвета, а полупрозрачный белый с холодным
     отливом: так подпись всегда согласована с тем, на чём лежит. */
  --ink:    rgba(255, 255, 255, .96);
  --ink-2:  rgba(235, 235, 245, .68);
  /* Прозрачность подписи выбрана по контрасту, а не на глаз. При .46
     подпись давала 4.13 на фоне страницы и 3.79 на поле ввода - ниже
     нормы 4.5 для мелкого текста, и это была каждая подпись в приложении.
     .60 держит 5.36 даже на самой светлой подложке. Заодно это значение
     системной вторичной метки iOS. */
  --muted:  rgba(235, 235, 245, .60);
  --faint:  rgba(235, 235, 245, .38);   /* неактивное, прочерки */

  /* Фирменный #00FF15 посажен до нережущего. Живёт только на действии
     и на статусе «идёт сейчас»: не на фоне, не на фотографии. */
  --signal:      #32D267;
  --signal-ink:  #06170C;
  --signal-dim:  rgba(50, 210, 103, .14);

  --stamp:     #FF6B5F;   /* просрочка, ошибка */
  --stamp-dim: rgba(255, 107, 95, .13);

  /* Оранжевый - только взведённая кнопка. Системный, единственная роль. */
  --amber:     #FF9F0A;
  --amber-ink: #1F1200;

  --white-plate: #F5F5F7;  /* госномер, единственное белое пятно */

  /* ЦВЕТ РАЗДЕЛА. Владелец 03.09.2026 голосовым: «кнопки там повыделять
     ещё цветами, чтобы удобней было по разделам».
     
     Красим ТОЛЬКО вкладку внизу, и вот почему. Зелёный уже означает
     действие и «идёт сейчас», коралловый - просрочку и ошибку, оранжевый -
     взведённую кнопку. Если раскрасить кнопки по разделам, эти смыслы
     поедут: персонал выучил «зелёное значит делать», и синяя главная
     кнопка в нарядах ломает навык.
     
     Плюс палитру 23.08.2026 специально свели к одной оси и двум акцентам,
     потому что владелец забраковал «компьютерную игру». Поэтому цвет
     раздела виден по одному за раз: активная вкладка и есть указатель,
     где ты находишься. */
  --sec:     var(--signal);
  --sec-dim: var(--signal-dim);

  /* Объём даёт светлая линия по верхней кромке, а не тень:
     тени на тёмном фоне не видно. */
  --gleam: inset 0 1px 0 rgba(255, 255, 255, .07);
  --gleam-strong: inset 0 1px 0 rgba(255, 255, 255, .24);
  --drop: 0 1px 2px rgba(0, 0, 0, .45);
  --drop-lift: 0 10px 28px rgba(0, 0, 0, .5);

  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 18px;
  --r-xl: 22px;

  /* Шаг 4. Веб-шрифты не грузятся: приложение уже воевало с белым экраном
     на LTE в подземном паркинге. */
  --text: "Golos Text", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, monospace;

  --ease: cubic-bezier(.32, .72, 0, 1);
  --spring: cubic-bezier(.2, .9, .3, 1.02);
  --fast: 120ms;

  /* Нижняя панель плавающая, поэтому запас снизу считается от неё. */
  --nav-h: 64px;
  --safe-b: max(10px, env(safe-area-inset-bottom, 0px));
}


/* Golos Text - кириллический гротеск Паратайпа, сделан под русский
   интерфейс. Системный стек остаётся фолбэком.

   Шесть файлов: кириллица и латиница отдельно, три веса. Начертание 500
   не скачивается вовсе - браузер возьмёт ближайшее 600, а на медленном
   LTE это 59 сэкономленных килобайт.

   font-display: swap - текст рисуется системным шрифтом сразу и подменяется,
   когда файл доедет. Из-за шрифта белого экрана не будет. */
/* Golos Text вариативный: ось веса 400-900 живёт внутри одного файла.
   Поэтому и объявление одно на подмножество, с диапазоном весов.

   Раньше здесь стояло шесть объявлений с весами 400, 600 и 700, и все
   шесть вели на ОДИН И ТОТ ЖЕ файл с базовым весом 400. Браузер честно
   его брал и жирнил сам: каждый заголовок в приложении рисовался
   искусственно утолщённым Regular, а не настоящим Golos Bold. Видно
   было по ширинам - 700, 800 и 900 давали ровно одну и ту же строку.
   Заодно ушли четыре лишние копии, 128 КБ. */
@font-face {
  font-family: "Golos Text";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/golos-cyrillic.woff2") format("woff2-variations");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "Golos Text";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/golos-latin.woff2") format("woff2-variations");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 17px/1.45 var(--text);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
  /* Слово, которое не влезает в строку, переносится с разрывом, а не
     распирает страницу. Без этого хватает одного слитного слова, чтобы
     вёрстка поехала: замер 03.09.2026 на карточке наряда - слово из 45
     символов вылезло за край в 25 местах сразу, включая заголовок экрана
     и подписи под работами. В данных такое приходит само: ссылка
     в заметке о клиенте, слитное название услуги, длинная марка.

     Именно anywhere, а не break-word: break-word разрешает перенос,
     но НЕ уменьшает минимальную ширину элемента по содержимому. Кнопка
     или чип, чья ширина считается от текста, с ним всё равно распирает
     строку - проверено, те же 25 мест остались на месте. Фразы с
     пробелами оба варианта рвут одинаково, по пробелам. */
  overflow-wrap: anywhere;
}
/* Обрезка нужна и на корне, а не только на body.
   overflow-x на body Сафари для прокрутки вьюпорта не учитывает: страница
   всё равно уезжает вбок, и левый край содержимого пропадает за экраном.
   Одно переполнившее поле - и весь наряд смотрится косым.
   Прокрутки по горизонтали в мини-аппе нет нигде: всё, что не влезает
   по ширине, либо переносится, либо листается внутри своего блока
   (лента образов в примерке - у неё своя overflow-x: auto). */
html { overflow-x: clip; }
/* Запас для старых айфонов. clip Сафари понимает только с 16-й версии,
   а ползло как раз на айфоне: на iOS 15 и ниже правило выше молча
   не работает, и страница снова уезжает вбок.
   hidden делает то же, но создаёт контейнер прокрутки, поэтому включаем
   его только там, где clip не поддержан - иначе он ломает липкие шапки
   (position: sticky у фильтра плёнок и у заголовка экрана). */
@supports not (overflow: clip) {
  html, body { overflow-x: hidden; }
}


/* Крупный заголовок экрана. Тридцать четыре пикселя против семнадцати
   в тексте - разница вдвое, и она видна прищурившись. Это и есть ответ
   на «заголовки надо выделить» на уровне всего экрана, а не секции. */
.bigtitle {
  max-width: 480px;
  margin: 8px auto 4px;
  padding: 0 14px;
  font: 700 34px/1.1 var(--text);
  letter-spacing: -.03em;
  color: var(--ink);
}
/* Цвет раздела на заголовке. Нижняя панель занимает 52 пикселя из
   восьмисот, и цвет там видит только тот, кто его ищет: Stephan дважды
   посмотрел тихий вариант и сказал «не вижу что поменялось».
   
   Обрубок в 44 пикселя под широким заголовком он назвал кривым, и был
   прав: правый конец полоски обрывался посреди слова и читался как
   недоделка. Поэтому линия во всю ширину заголовка, а сам заголовок
   берёт цвет раздела.
   
   Только там, где раздел известен: на клиентской витрине data-sec нет,
   и заголовок остаётся белым. */
.bigtitle::after { content: ""; display: none; }
body[data-sec] .bigtitle { color: var(--sec); }
body[data-sec] .bigtitle::after {
  display: block;
  height: 3px;
  margin: 10px 0 2px;
  border-radius: 2px;
  background: var(--sec);
  opacity: .5;
}
/* Браузерное [hidden] это display: none из его собственной таблицы, и любое
   авторское display его перебивает. Редизайн раздал display: flex строкам,
   и всё, что код прячет свойством .hidden, осталось на экране: «Скидка -
   0 ₽» и «Сумма работ» висели в каждом наряде без скидки, строка оклада -
   в каждом исполнителе. Двадцать четыре места в коде рассчитывают на hidden. */
[hidden] { display: none !important; }

main {
  max-width: 480px;
  margin: 0 auto;
  padding: 12px 14px calc(var(--nav-h) + var(--safe-b) + 28px);
}

/* Цифры везде табличные: иначе денежная колонка гуляет при прокрутке.
   Моноширинный не берём - на Android нет SF Mono, фолбэк даёт другие
   метрики и колонка едет ровно на дешёвом телефоне. */
.num, .price__sum, .slot__sum, .total, .week__total, .summary__n, .item__pay {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ============================================================
   ЗАГОЛОВКИ. Главная правка релиза.
   ============================================================ */

/* Было 15px var(--muted) - тише текста под собой. Теперь заголовок
   выигрывает по всем трём признакам: размер, вес, цвет. */
.h2 {
  margin: 28px 0 12px;
  font: 700 19px/1.25 var(--text);
  letter-spacing: -.01em;
  color: var(--ink);
  text-transform: none;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.h2:first-child { margin-top: 4px; }

/* Правая приписка в заголовке: «3 наряда», «расчёт сразу». */
.h2 > span:last-child:not(:first-child) {
  font: 400 15px/1.2 var(--text);
  color: var(--muted);
  letter-spacing: 0;
  flex: none;
}

/* Крупный заголовок экрана - только на клиентских экранах. */
.hero--photo .hero__title { text-shadow: 0 2px 12px rgba(0,0,0,.5); }
.hero__title, .result__title {
  font: 700 32px/1.08 var(--text);
  letter-spacing: -.025em;
  margin: 0;
}
/* У героя под заголовком свой отступ в подложке, а у результата примерки
   сразу карточка с ценой - без поля они лежали вплотную, ноль пикселей. */
.result__title { margin-bottom: 12px; }

/* Служебная метка. Капитель приглушённым серым осталась ТОЛЬКО здесь:
   раньше так выглядели все заголовки, и экран читался серой стеной. */
.hero__eyebrow, .consent__head, .done__head, .issues__head, .notes .note__head,
.mark__head, .day__head, .preset__head {
  /* Подпись слева, счётчик справа - как у крупных заголовков экрана.
     Впритык они читались одной строкой: «КЛИЕНТ ВИЗИТОВ: 2». */
  justify-content: space-between;
  font: 600 12px/1.2 var(--text);
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Фильтр по финишу над каталогом. Липкий: цветов 746, и вернуться
   к переключателю с середины списка иначе значит листать вверх. */
.chips--finish {
  position: sticky; top: 56px; z-index: 20;
  margin: 0 -16px 10px; padding: 10px 16px;
  background: var(--paper);
  border-bottom: 1px solid var(--edge);
}

.chips-sub {
  margin: 14px 0 8px;
  font: 600 13px/1.2 var(--text);
  color: var(--ink-2);
}

.muted { color: var(--muted); }
/* Подпись под блоком. Нижнее поле обязательно: у кнопки своего верхнего
   отступа нет, и без этого «Записаться на осмотр» вставала вплотную
   к тексту - пять пикселей вместо двенадцати по всему приложению. */
.footnote, .hint, .field-hint {
  font: 400 14px/1.4 var(--text);
  color: var(--muted);
  margin: 8px 0 12px;
}

/* ============================================================
   ХРОМ: шапка и нижняя панель. Стекло живёт только здесь.
   ============================================================ */

/* Размытие стоит дорого, поэтому оно на двух неподвижных элементах,
   а не на прокручиваемом содержимом. На вебвью без backdrop-filter
   работает сплошная заливка из первой строки. */
.masthead {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; gap: 10px;
  padding: 12px 16px calc(12px + env(safe-area-inset-top, 0px));
  padding-top: calc(12px + env(safe-area-inset-top, 0px));
  background: transparent;
  /* Заливка и линия под шапкой появляются, только когда под неё заехало
     содержимое. Над снимком шапка прозрачная: сверху человек видит машину,
     а не полоску с названием.
     Пока экран не прокручен, шапка сливается с фоном и не режет его. */
  border-bottom: 1px solid transparent;
  transition: border-color var(--fast) var(--ease);
}
.masthead--scrolled { background: rgba(11, 11, 13, .96); border-bottom-color: var(--edge); }
/* Мелкая подпись экрана живёт только после того, как крупный заголовок
   уехал под шапку. Иначе на экране два одинаковых заголовка подряд. */
.masthead__screen {
  opacity: 0;
  transform: translateY(4px);
  transition: opacity var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.masthead--scrolled .masthead__screen { opacity: 1; transform: none; }
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .masthead--scrolled {
    background: rgba(11, 11, 13, .72);
    -webkit-backdrop-filter: blur(24px) saturate(180%);
    backdrop-filter: blur(24px) saturate(180%);
  }
}
/* В шапке стоит знак студии, а не слово. Он у них уже есть и узнаётся
   сам: три полосы с косым срезом. Пропорция знака 3:1, поэтому ширина
   считается от высоты, а не задаётся отдельно. */
.masthead__brand {
  display: inline-flex; align-items: center;
  color: var(--ink);
  /* Высота держится до ответа сервера: знак приходит из настроек, и без
     этой строки шапка на долю секунды была бы ниже, а потом прыгала. */
  min-height: 18px;
}
.brand--text {
  /* Студия без своего знака. Имя набирается мелко и в разрядку: в шапке
     оно работает как логотип, а не как заголовок. */
  width: auto; height: auto;
  font-size: 12px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; white-space: nowrap;
  filter: none;
}
.brand {
  display: block;
  /* Просветы между полосами это 12.5% высоты знака, то есть ниже 16px
     они схлопываются и знак читается смазанным пятном. 18px - предел,
     где все три полосы ещё держатся раздельно. */
  /* Ширина от пропорций самого знака, а не посчитанная под один логотип:
     приложение продаётся разным студиям, и у каждой знак своей формы. */
  height: 18px; width: auto;
  fill: currentColor;
  /* Над снимком шапка прозрачная, и белый знак на светлом кадре
     пропал бы. На тёмном фоне тень незаметна. */
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, .55));
}
.masthead__screen {
  margin-left: auto;
  font: 500 15px/1 var(--text); color: var(--ink-2);
}

/* Нижняя панель. Плавающая пилюля, а не полоса во всю ширину:
   так она читается как элемент поверх содержимого, а не как его край. */
.nav {
  position: fixed; z-index: 50;
  left: 12px; right: 12px; bottom: var(--safe-b);
  max-width: 456px; margin: 0 auto;
  /* Зазор 4px между вкладками - исключение из правила «12px между целями».
     Панель это один сегментный контрол: вкладка шириной 110-117px, целятся
     в середину, и промах уводит в соседнюю вкладку, а не в необратимое
     действие. Правило 12px написано для кнопок, стоящих в потоке столбиком. */
  display: flex; gap: 4px;
  padding: 6px;
  border-radius: var(--r-xl);
  background: rgba(20, 20, 23, .97);
  border: 1px solid var(--edge-2);
  box-shadow: var(--drop-lift), var(--gleam);
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .nav {
    background: rgba(20, 20, 23, .74);
    -webkit-backdrop-filter: blur(28px) saturate(180%);
    backdrop-filter: blur(28px) saturate(180%);
  }
}
.nav__btn {
  flex: 1; min-height: 52px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px;
  padding: 6px 4px;
  border: 0; border-radius: 16px;
  background: none; color: var(--muted);
  font: 500 11px/1.1 var(--text);
  cursor: pointer;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.nav__btn svg { width: 22px; height: 22px; }
/* Текущее положение - главный ориентир персонала, поэтому оно читается
   заливкой и цветом сразу, а не одной сменой оттенка текста. */
/* Активная вкладка заливается цветом раздела ПЛОТНО. Подсветка на 16%
   и полоска сверху проверены на живом человеке и не сработали: разницу
   не увидел ни Stephan, ни, значит, персонал в боксе. */
.nav__btn--on {
  background: var(--sec);
  color: #0B0B0D;
  box-shadow: var(--gleam);
  font-weight: 700;
}

/* Раздел красит сам себя. Зелёный оставлен приёмке: там действие
   «принять», и зелёный на нём уже привычен. */
body[data-sec="orders"] { --sec: #4AA8E8; --sec-dim: rgba(74, 168, 232, .26); }
body[data-sec="mine"]   { --sec: #A98BF2; --sec-dim: rgba(169, 139, 242, .26); }
body[data-sec="price"]  { --sec: #2FBFAE; --sec-dim: rgba(47, 191, 174, .26); }
.nav__btn:active { background: var(--raise); }

.offline-strip {
  position: sticky; top: 0; z-index: 45;
  padding: 10px 16px;
  background: var(--stamp); color: #2A0F0C;
  font: 600 15px/1.2 var(--text); text-align: center;
}

/* ============================================================
   СЕКЦИИ И КАРТОЧКИ
   ============================================================ */

.card {
  background: var(--lift);
  border: 0;
  border-radius: var(--r-md);
  padding: 16px;
  margin-bottom: 12px;
  box-shadow: var(--gleam);
}

/* Карточки, стоящие подряд, склеиваются в один предмет.
 *
 * Долг мастерам это не четыре коробки, а список из четырёх строк. Задачи
 * не три коробки, а список из трёх. Пока каждая несла свою рамку и свои
 * скругления, экран читался стопкой одинаковых прямоугольников - ровно то,
 * из-за чего он выглядел дёшево.
 *
 * Первая в череде получает верхние углы, последняя нижние, между ними
 * волосяная линия. Одиночная карточка остаётся скруглённой со всех сторон.
 * Всё на соседних селекторах, разметку JS трогать не нужно.
 */
.card + .card:not(.order) {
  margin-top: 0;
  border-top: 1px solid var(--edge);
  border-top-left-radius: 0; border-top-right-radius: 0;
}
.card:not(.order):has(+ .card:not(.order)) {
  margin-bottom: 0;
  border-bottom-left-radius: 0; border-bottom-right-radius: 0;
}

/* Пара «подпись слева - значение справа». Редизайн потерял разведение
   по краям, и суммы липли к подписи посреди пустой карточки: «Провёл
   нарядов на 620 000 ₽» вместо колонки цифр по правому краю. */
.row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.row.muted { font-size: 15px; }
/* Кнопка в такой строке. У .btn ширина 100%, и пара кнопок в одном ряду
   переполняла его: «Похоже» вылезала за карточку и наезжала на «Не похоже».
   Делим ряд поровну, min-width снимает флексовый минимум по содержимому. */
.row > .btn { flex: 1 1 0; width: auto; min-width: 0; }

/* Строка наряда в списке. Нажимается целиком, поэтому внутри неё
   мелких кнопок нет. */
/* Иконка из спрайта. Цвет наследуется от родителя, поэтому одна и та же
   иконка работает и на тихой кнопке, и на зелёной. */
.ico { width: 20px; height: 20px; flex: none; display: block; }

.card.order {
  display: flex; align-items: center; gap: 14px;
  width: 100%;
  margin: 0; padding: 14px 4px;
  background: none; border-radius: 0; box-shadow: none;
  cursor: pointer;
  transition: background var(--fast) var(--ease);
}

/* Миниатюра машины. Снимок приёмки, тот же, что стоит в шапке документа.
   Без фото - значок: строки обязаны стоять по одной сетке, иначе список
   разъезжается и выглядит собранным на коленке. */
.order__thumb {
  width: 60px; height: 60px; flex: none;
  display: grid; place-items: center;
  border-radius: 12px;
  background: var(--raise);
  color: rgba(235, 235, 245, .34);
  overflow: hidden;
  box-shadow: var(--gleam);
}
.order__thumb--photo { background: #000; }
.order__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.order__thumb .ico { width: 26px; height: 26px; }

.order__body { flex: 1; min-width: 0; }
/* Стрелка говорит, что строка открывается. Без неё список читается
   как справка, а не как список, в который можно провалиться. */
.order__go { flex: none; color: rgba(235, 235, 245, .34); display: grid; place-items: center; }
.order__go .ico { width: 18px; height: 18px; }

/* Разделитель между машинами, а не рамка вокруг каждой. */
.card.order + .card.order {
  border-top: 1px solid var(--edge);
  /* Линия начинается там, где начинается текст: 60px миниатюра плюс 14 зазор.
     Под миниатюрой она не идёт - так список читается сгруппированным,
     а не расчерченным на клетки. */
  border-image: linear-gradient(90deg, transparent 74px, var(--edge) 74px) 1;
}
/* Строка без миниатюры: предложение и запись, из которой заводят наряд.
   У них слева нет фото, а флекс от строки наряда расставлял их блоки
   шеренгой и сплющивал до нуля - список уезжал за край экрана. */
.card.order--flat { display: block; }
.card.order--flat + .card.order--flat {
  border-image: none;
  border-top: 1px solid var(--edge);
}

/* Нажатие подсвечивает строку во всю ширину экрана, как в списке iOS. */
.card.order { transition: background var(--fast) var(--ease), transform 90ms var(--spring); }
.card.order:active {
  background: var(--panel);
  box-shadow: 0 0 0 14px var(--panel);
  border-radius: 4px;
  transform: scale(.985);
}
.order__head { justify-content: space-between; margin-bottom: 6px; }
/* Машина - доминанта строки. Всё остальное заметно тише: раньше название
   было 17px, а номер и сумма 15px, и разница в одну ступень не читалась. */
.order__car {
  margin: 0 0 3px;
  font: 700 19px/1.2 var(--text);
  letter-spacing: -.02em;
}
.order__client { font-size: 15px; color: var(--ink-2); margin: 0 0 4px; }
/* Число работ слева, деньги справа в колонку. */
.order .row.muted { justify-content: space-between; font-size: 14px; }
/* Сумма справа в строке наряда гремит нарочно. В строке без миниатюры
   справа стоит номер предложения, ему греметь незачем. */
.order:not(.order--flat) .row.muted > span:last-child {
  color: var(--ink); font-weight: 600; font-size: 16px;
}

/* Блочный: в строке наряда это span внутри обычного блока, а у строчных
   элементов вертикальный margin не работает - зазор пропадал. */
.due { display: block; font-size: 14px; color: var(--muted); margin: 8px 0 0; }
/* В строке задачи срок стоит рядом с именами, а не отдельным абзацем
   под ними: верхний отступ там сдвигал плашку вниз на восемь пикселей. */
.row > .due { margin-top: 0; }
.due--late { color: var(--stamp); font-weight: 600; }


/* ============================================================
   СТРОКИ-ПЕРЕХОДЫ. Один экран - одно дело.
   ============================================================ */

/* Сгруппированы в один предмет с разлиновкой внутри, а не рассыпаны
   отдельными кнопками: это оглавление, а не три разных действия. */
.jumps {
  margin: 24px 0 0;
  border-radius: var(--r-md);
  overflow: hidden;
  background-image: var(--lift);
  box-shadow: var(--gleam);
}
.jump {
  display: flex; align-items: center; gap: 12px;
  width: 100%; min-height: 56px;
  padding: 14px 16px;
  border: 0; background: none;
  color: var(--ink);
  font: 600 17px/1.3 var(--text);
  letter-spacing: -.01em;
  text-align: left;
  cursor: pointer;
  transition: background var(--fast) var(--ease);
}
.jump + .jump { border-top: 1px solid var(--edge); }
.jump:active { background: rgba(255, 255, 255, .05); }
.jump__name { flex: 1; min-width: 0; }
/* Цифра, ради которой на экран заходят: она правее подписи и тише её. */
.jump__value {
  flex: none;
  font: 400 16px/1.2 var(--text);
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
.jump__go { flex: none; display: grid; place-items: center; color: var(--faint); }
.jump__go .ico { width: 18px; height: 18px; }

/* ============================================================
   ГОСНОМЕР. Единственное белое пятно в приложении.
   В боксе машину опознают по номеру, поэтому он набран как настоящая
   табличка, а не как строка текста. Цвета намеренно не из токенов.
   ============================================================ */
.plate {
  display: inline-flex; align-items: stretch; flex: none;
  border: 1.5px solid #0A0B0C; border-radius: 5px;
  background: var(--white-plate); color: #0A0B0C;
  overflow: hidden;
  font: 700 16px/1 var(--mono);
  letter-spacing: .06em;
  box-shadow: 0 1px 0 rgba(255, 255, 255, .18), var(--drop);
  text-transform: uppercase;
}
.plate__main { padding: 8px 9px; }
.plate__region {
  padding: 7px 7px; border-left: 1.5px solid #0A0B0C;
  font-size: 12px; display: flex; align-items: center;
  background: #EDEDF0;
}

/* ============================================================
   СТАТУСЫ. Риска и цвет текста, а не пилюля.
   ============================================================ */
.tag {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 0; border-radius: 0;
  font: 600 13px/1 var(--text);
  color: var(--muted);
  background: none;
}
.tag::before {
  content: ""; width: 7px; height: 7px; flex: none;
  border-radius: 50%;
  background: var(--muted);
}
.tag--work { color: var(--signal); }
.tag--work::before { background: var(--signal); }
.tag--check { color: var(--stamp); }
.tag--check::before { background: var(--stamp); }

.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--signal); flex: none; }
.mark, .mark__status { font-size: 15px; }
.warn { color: var(--stamp); font-weight: 600; }

/* ============================================================
   КНОПКИ. Цель не ниже 48px, зазор между соседними 12px.
   ============================================================ */
.btn {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; min-height: 52px;
  padding: 15px 18px;
  border: 0; border-radius: var(--r-md);
  cursor: pointer;
  font: 600 17px/1.2 var(--text);
  letter-spacing: -.01em;
  color: var(--ink);
  background: var(--lift-2);
  box-shadow: var(--gleam);
  transition: transform var(--fast) var(--ease), background var(--fast) var(--ease);
}
.btn:active { transform: scale(.985); background: #33333A; }
.btn svg { width: 20px; height: 20px; flex: none; }

/* Главное действие. Блик по верхней кромке даёт стеклянный вид
   без backdrop-filter, который на прокручиваемом элементе стоит дорого. */
/* Главное действие переливается, как их же плёнка.
 *
 * Приём взят не с потолка: студия продаёт хамелеоны, перламутры и аврору -
 * 762 такие плёнки в каталоге. Перелив это буквально их товар, а не
 * украшение поверх серого.
 *
 * Два слоя. Нижний - сам цвет со сдвигом в бирюзу и в лайм: так ведёт себя
 * сатин под разным углом. Верхний - узкая светлая полоса, она и едет.
 *
 * Двигается ТОЛЬКО она и только на кнопке: одно такое место на экран.
 * Восемь секунд на проход, потому что это отблеск, а не мигалка; и это
 * не свечение, которое владелец забраковал - там был ореол вокруг кнопки,
 * здесь блик по её поверхности.
 */
.btn--go {
  background-image:
    linear-gradient(102deg, transparent 34%, rgba(255,255,255,.26) 47%,
                    rgba(255,255,255,.06) 56%, transparent 66%),
    linear-gradient(118deg, #23C9A4 0%, #32D267 34%, #6FDE5C 62%, #32D267 100%);
  background-size: 280% 100%, 100% 100%;
  background-position: 120% 0, 0 0;
  color: var(--signal-ink);
  box-shadow: var(--gleam-strong), var(--drop);
  font-weight: 700;
  animation: iri 8s linear infinite;
}
@keyframes iri {
  to { background-position: -180% 0, 0 0; }
}
.btn--go:active {
  background-image:
    linear-gradient(118deg, #1FB994 0%, #28BC5C 34%, #62CC50 62%, #28BC5C 100%);
  animation: none;
}

/* Кнопка на снимке живёт по тем же правилам. */
.hero__cta {
  background-image:
    linear-gradient(102deg, transparent 34%, rgba(255,255,255,.26) 47%,
                    rgba(255,255,255,.06) 56%, transparent 66%),
    linear-gradient(118deg, #23C9A4 0%, #32D267 34%, #6FDE5C 62%, #32D267 100%);
  background-size: 280% 100%, 100% 100%;
  background-position: 120% 0, 0 0;
  animation: iri 8s linear infinite;
}
.hero__cta:active { animation: none; }

/* Перелив выключается вместе со всей анимацией: правило ниже гасит его
   для тех, кто просил меньше движения. */


/* Второе зелёное пятно на экране обесценивает первое. Главное действие
   в потоке остаётся заливным, действие внутри карточки становится тихим
   с зелёной подписью. */
.card .btn--go {
  background: transparent;
  color: var(--signal);
  border: 1px solid rgba(53, 196, 106, .45);
  box-shadow: none;
}
.card .btn--go:active { background: var(--signal-dim); }

.btn--quiet {
  background: transparent; color: var(--ink);
  border: 1px solid var(--edge-2);
  box-shadow: none;
}
.btn--quiet:active { background: var(--raise); }

.btn--flag { background: var(--stamp-dim); color: var(--stamp); border: 1px solid rgba(228,105,92,.3); }

.btn:disabled {
  background: transparent;
  border: 1px solid var(--edge);
  /* Цветом, а не opacity: на тёмном она даёт грязь.
     Но не самым тусклым: подпись неактивной кнопки часто объясняет, ПОЧЕМУ
     она неактивна («Ничего не выбрано»), и такое надо читать. На .38 выходило
     3.16 при норме 4.5. Неактивность и так видна по отсутствию заливки. */
  color: rgba(235, 235, 245, .52);
  box-shadow: none;
  cursor: default;
}
.btn:disabled:active { transform: none; }

/* Было 8px. Соседние кнопки одного веса. */
.btn + .btn { margin-top: 12px; }
/* Своего верхнего отступа у кнопки нет, и строка текста над ней его тоже
   не даёт: «Сделано» в задаче и «Выплатить» в деньгах вставали к подписи
   вплотную, ноль пикселей при общем ритме в двенадцать. */
.row + .btn, .paylines + .btn { margin-top: 12px; }
/* То же для текстовой ссылки, и здесь это не про воздух, а про палец:
   её цель добирается псевдоэлементом до 48px, то есть на 8px выше рамки.
   Впритык к кнопке сверху цели перекрывались, и «напомнить клиенту»
   отбирало нижнюю кромку у «Оплачено 15 000 ₽». */
.btn + .linkbtn, .row + .linkbtn, .linkbtn + .btn { margin-top: 12px; }
/* Необратимое действие отбивается от частого. */
.btn--flag, .btn--go + .btn--quiet { margin-top: 24px; }

/* ============================================================
   ВЗВЕДЁННАЯ КНОПКА. Всё, что уходит человеку наружу, требует
   двух нажатий. ui.js подменяет текст и вешает .armed; класс
   переименовывать нельзя.

   Янтарный больше нигде в приложении не встречается: взведённое
   состояние нельзя спутать ни с обычной кнопкой, ни с ошибкой.
   ============================================================ */
.armed,
.btn.armed,
.btn--quiet.armed,
.linkbtn.armed {
  background: var(--amber) !important;
  color: var(--amber-ink) !important;
  border-color: var(--amber) !important;
  font-weight: 700 !important;
  box-shadow: var(--gleam-strong), 0 0 0 3px rgba(232, 177, 76, .22) !important;
}
.linkbtn.armed {
  padding: 10px 14px; border-radius: var(--r-sm);
  text-decoration: none;
}

/* Текстовая кнопка внутри строки. Цель добирается псевдоэлементом,
   а не раздуванием вида. */
.linkbtn {
  position: relative;
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 32px;
  padding: 4px 2px;
  border: 0; background: none; cursor: pointer;
  color: var(--signal);
  font: 600 15px/1.2 var(--text);
}
.linkbtn::after {
  content: ""; position: absolute;
  left: -10px; right: -10px; top: 50%;
  height: 48px; transform: translateY(-50%);
}
.linkbtn--on { color: var(--ink); }
/* «Убрать» красным, «изменить» обычным: это разные по цене действия,
   и одинаковыми их делать нельзя - промахнётся палец, пропадёт работа.
   Редизайн потерял цвет, и «удалить наряд» выглядело как «изменить». */
.linkbtn--drop { color: var(--stamp); }

/* ============================================================
   ВИТРИНА КЛИЕНТА
   ============================================================ */

/* Снимок из их бокса во весь верх экрана.
 *
 * До 23.08.2026 фото было полоской в 300 пикселей с полями по бокам, под ним
 * три одинаковые строки-действия. Экран читался как меню: серый список,
 * в котором главное действие ничем не отличалось от остальных.
 *
 * Теперь снимок идёт от края до края и от самого верха, под шапку. Шапка
 * над ним прозрачная, линии под ней нет, пока экран не тронут: сверху
 * человек видит машину, а не полоску с названием. Запись живёт прямо
 * на снимке - это единственное действие, ради которого сюда заходят.
 */
.hero {
  position: relative;
  /* Вылезаем из полей main и уходим под шапку: она 56px с безопасной зоной. */
  margin: calc(-1 * (56px + env(safe-area-inset-top, 0px))) -14px 24px;
  padding: calc(96px + env(safe-area-inset-top, 0px)) 20px 22px;
  min-height: 74vh;
  display: flex; align-items: flex-end;
  background: var(--panel);
  overflow: hidden;
}
.hero--photo::before {
  content: ""; position: absolute; inset: 0;
  background-image: var(--shot, none);
  background-size: cover; background-position: 50% 42%;
}
/* Затемнение снизу. Без него белые буквы теряются на светлом кузове,
   а нижняя граница снимка режет экран пополам. */
/* Затемнение снизу. Без него белые буквы теряются на светлом кузове,
 * а нижняя граница снимка режет экран пополам.
 *
 * Голубое пятно, которое здесь стояло 23.08.2026, убрано: в палитре
 * и без него было шесть цветовых семейств, а это добавляло седьмое.
 * Свет на нижнюю часть кадра теперь даёт сама фотография.
 */
.hero--photo::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(180deg,
      rgba(10,12,14,.55) 0%, rgba(10,12,14,.12) 28%,
      rgba(16,18,21,.72) 62%, var(--paper) 100%);
}
.hero__body { position: relative; z-index: 1; width: 100%; }

.hero__eyebrow { margin: 0 0 10px; color: rgba(255,255,255,.80); text-shadow: 0 1px 3px rgba(0,0,0,.7); }
/* Адрес и часы приходят двумя span без разделителя. Без flex они
   склеиваются в «Приморский пр., 72Ежедневно 11:00-21:00». */
.hero__meta {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 4px 12px;
  margin: 10px 0 0; font-size: 15px; color: var(--ink-2);
  text-shadow: 0 1px 3px rgba(0,0,0,.6);
}
.hero__meta > span + span::before {
  content: "·"; margin-right: 12px; color: var(--muted);
}
.hero__slot {
  display: inline-flex; align-items: center; gap: 8px;
  margin: 14px 0 0; padding: 9px 14px;
  border-radius: 999px;
  background: rgba(53, 196, 106, .16);
  border: 1px solid rgba(53, 196, 106, .32);
  color: var(--signal);
  font: 600 14px/1.2 var(--text);
}

/* Запись прямо на снимке. Слева действие, справа время: человеку не нужно
   читать строку целиком, чтобы понять, когда его примут. */
.hero__cta {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; min-height: 58px;
  margin-top: 20px; padding: 14px 18px;
  border: 0; border-radius: var(--r-md);
  color: var(--signal-ink);
  box-shadow: var(--gleam-strong), var(--drop-lift);
  cursor: pointer;
  transition: transform 90ms var(--spring), background var(--fast) var(--ease);
}
.hero__cta:active { transform: scale(.985); }
.hero__cta-main { font: 700 18px/1.2 var(--text); letter-spacing: -.01em; }
.hero__cta-when {
  font: 600 15px/1.2 var(--text);
  font-variant-numeric: tabular-nums;
  padding: 5px 10px; border-radius: 8px;
  background: rgba(8, 21, 13, .16);
}

/* Крупные строки-действия. Было margin-bottom 10px, стало 12px,
   высота гарантирована 72px. */
.act {
  display: flex; align-items: center; gap: 14px;
  width: 100%; min-height: 72px;
  margin-bottom: 12px; padding: 16px;
  text-align: left; cursor: pointer;
  background: var(--lift); color: var(--ink);
  border: 0; border-radius: var(--r-md);
  box-shadow: var(--gleam), var(--drop);
  font: 400 17px/1.35 var(--text);
  transition: transform var(--fast) var(--ease), background var(--fast) var(--ease);
}
.act:active { transform: scale(.99); background: var(--raise); }
.act--go {
  border-color: rgba(53, 196, 106, .35);
  background: linear-gradient(180deg, rgba(53,196,106,.13), rgba(53,196,106,.05));
}
.act--go .act__head { color: var(--signal); }
.act__icon {
  width: 44px; height: 44px; flex: none;
  display: grid; place-items: center;
  border-radius: 12px;
  background: var(--raise);
  color: var(--ink-2);
  box-shadow: var(--gleam);
}
.act--go .act__icon { background: var(--signal-dim); color: var(--signal); }
.act__icon svg { width: 22px; height: 22px; }
.act__body { flex: 1; min-width: 0; }
.act__head { display: block; font: 700 17px/1.25 var(--text); letter-spacing: -.01em; }
.act__sub { display: block; margin-top: 3px; font-size: 14px; color: var(--muted); }
.act__arrow { flex: none; color: var(--muted); display: grid; place-items: center; }
.act__arrow svg { width: 18px; height: 18px; }

/* ============================================================
   ПРАЙС И ДЕНЬГИ
   Суммы всех строк секции стоят в одной вертикали справа.
   ============================================================ */
.pricelist {
  background-image: var(--lift);
  background-color: var(--panel);
  /* Единственная вертикальная линия в приложении: она отбивает колонку
     сумм и превращает список в документ. */
  background-image: linear-gradient(90deg,
    transparent calc(100% - 128px), var(--edge) calc(100% - 128px),
    var(--edge) calc(100% - 127px), transparent calc(100% - 127px));
  border: 0;
  border-radius: var(--r-md);
  padding: 4px 16px;
  margin-bottom: 12px;
  box-shadow: var(--gleam);
}
.price {
  display: flex; align-items: baseline; gap: 14px;
  justify-content: space-between;
  padding: 14px 0;
  border-top: 1px solid var(--edge);
}
.price:first-child { border-top: 0; }
.price__name { flex: 1; min-width: 0; font-size: 16px; }
.price__sum {
  flex: none; min-width: 104px; text-align: right;
  font: 600 17px/1.2 var(--text);
  letter-spacing: -.01em;
}
.price__sum--ask { font-weight: 400; font-size: 15px; color: var(--muted); }
.price--total {
  border-top: 1px solid var(--edge-2);
  font-weight: 700;
}
.price--total .price__name { font-weight: 700; font-size: 17px; }
.price--total .price__sum { font-size: 19px; }

/* Итог по цеху - строка-итог, набирается как заголовок. */
.total {
  display: flex; justify-content: space-between; align-items: baseline;
  padding: 14px 2px;
  margin-bottom: 12px;
  background: none;
  border: 0; border-top: 1px solid var(--edge-2);
  font: 700 19px/1.2 var(--text);
}
/* А это не итог, а поясняющая строка под расписанием: «В работе 2 машины
   на 400 000 ₽, осталось получить 200 000 ₽». Заголовочным кеглем она
   притворялась итогом и выглядела лишней коробкой. */
.week__total {
  margin: 4px 2px 16px;
  padding: 0;
  background: none; border: 0;
  font: 400 15px/1.45 var(--text);
  color: var(--muted);
}
.summary-line { font-size: 15px; color: var(--ink-2); margin: 0 0 12px; }

/* Сводка тремя счётчиками. */
.summary {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  margin-bottom: 20px;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--edge);        /* проступает в зазорах и работает разлиновкой */
  box-shadow: var(--gleam);
}
.summary__cell {
  display: flex; flex-direction: column; gap: 3px;
  padding: 16px 14px;
  background: var(--lift);
  border: 0;
}
.summary__n { font: 700 28px/1 var(--text); letter-spacing: -.02em; }
.summary__n--check { color: var(--stamp); }
.summary__cell .muted { font-size: 13px; }

.payline, .paylines .payline {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 12px 0;
  border-top: 1px solid var(--edge);
  font-size: 15px;
}
.payline:first-child { border-top: 0; }
.payline--gap { margin-top: 10px; }
.payline__what {
  display: flex; align-items: center; gap: 12px;
  flex: 1; min-width: 0; color: var(--ink-2);
}

/* Пара «подпись - значение». Значение справа держит свою ширину: «3 работы»
   ломалось на «3» и «работы», и перенос читался как сбой. Ужимается подпись
   слева, поэтому ей разрешено уходить уже своего минимума - иначе строка
   вылезала бы за карточку, а body её молча срезает.

   Шапку наряда правило обходит: там слева госномер с собственным flex: none,
   и запрет сжиматься ещё и марке уводил «Mercedes-Benz GLE 400d Coupe»
   на 73px за край экрана без всякой прокрутки. */
.card > .row:not(.order__head) > :first-child:not(:only-child) { min-width: 0; }
/* Кнопка тут исключение: она не «значение справа», а половина ряда,
   и flex: none оставлял её по ширине подписи - «Не похоже» выходило
   заметно уже «Похоже». Ширину кнопкам раздаёт `.row > .btn` выше. */
.card > .row:not(.order__head) > :last-child:not(:first-child):not(.btn) { flex: none; }
.item__pay { font-weight: 600; }

/* ============================================================
   СПИСКИ ВЫБОРА: услуга, день, время
   ============================================================ */
.pick {
  display: flex; align-items: center; gap: 14px;
  width: 100%; min-height: 60px;
  margin-bottom: 12px; padding: 16px;
  text-align: left; cursor: pointer;
  background: var(--lift); color: var(--ink);
  border: 0; border-radius: var(--r-md);
  box-shadow: var(--gleam), var(--drop);
  font: 400 17px/1.3 var(--text);
  transition: transform var(--fast) var(--ease), background var(--fast) var(--ease);
}
.pick:active { transform: scale(.99); background: var(--raise); }
.pick__name { flex: 1; min-width: 0; font-weight: 500; }
.pick__price { flex: none; color: var(--muted); font-size: 15px; }
.pick--off {
  background: transparent; border-style: dashed; border-color: var(--edge);
  color: var(--faint); cursor: default;
}
.pick--off:active { transform: none; background: transparent; }

/* Сетка времени. Слотов бывает до двадцати, поэтому это именно сетка,
   а не ряд из трёх чипов. */
.times {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 12px;
  margin-bottom: 12px;
}
.time, .slot__time {
  min-height: 52px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-md);
  border: 1px solid var(--edge-2);
  background: var(--lift);
  color: var(--ink);
  font: 600 17px/1 var(--text);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  box-shadow: var(--gleam);
  transition: transform var(--fast) var(--ease), background var(--fast) var(--ease);
}
.time:active { transform: scale(.97); background: var(--raise); }

.day { margin-bottom: 12px; }
.day__head {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-bottom: 8px;
}
.day__name { font: 700 17px/1.2 var(--text); color: var(--ink); text-transform: none; letter-spacing: -.01em; }
.day__empty { font-size: 15px; color: var(--muted); padding: 6px 0 2px; }
.day--off { opacity: 1; }
.day--off .day__name { color: var(--muted); }
/* Заголовок секции в цеху.
 *
 * JS собирает их как .row.muted.month, а не .h2 - в manager.js это шесть
 * мест, плюс shifts.js и tasks.js. Без отдельного правила главная жалоба
 * владельца («заголовки надо выделить») на самом плотном экране остаётся
 * в силе: .muted красит их серым, .row.muted сажает до 15px, и заголовок
 * снова оказывается тише текста под собой.
 *
 * Тот же класс несёт разделитель месяца в истории. Там это тоже заголовок
 * секции, и выглядеть он должен так же.
 */
.row.muted.month {
  margin: 28px 0 12px;
  padding: 0;
  border: 0;
  font: 700 19px/1.25 var(--text);
  letter-spacing: -.01em;
  color: var(--ink);
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
/* Правая половина строки - значение. Оно тише заголовка. */
.row.muted.month > span:last-child:not(:first-child) {
  font: 400 15px/1.2 var(--text);
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  letter-spacing: 0;
  flex: none;
}

.week { margin-bottom: 12px; }
.slot {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 14px 0;
  border-top: 1px solid var(--edge);
}
.slot:first-child { border-top: 0; }
.slot__time { min-height: 0; padding: 0; border: 0; background: none; box-shadow: none;
  font-size: 15px; color: var(--muted); width: 52px; justify-content: flex-start; }
/* Строка записи: машина, работа, клиент - тремя строками. Редизайн снял
   с блока колонку, и три span-а слиплись в один абзац: «BMW M8» рвалось
   пополам, слот раздувался до 151px вместо шестидесяти. */
.slot__body { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
/* Машина - доминанта строки, как в списке нарядов: по ней узнают запись.
   Работа и клиент мельче не для красоты: колонка в слоте всего 183px,
   и на 15px «Керамическое покрытие» переносилось на вторую строку -
   строка записи раздувалась до 145px, а в дне их бывает шесть. */
.slot__car { font: 600 16px/1.25 var(--text); color: var(--ink); }
.slot__what { font-size: 14px; color: var(--ink-2); }
.slot__who { font-size: 13px; color: var(--muted); }
/* Телефон в расписании это ссылка tel:, по ней звонят. Двадцать пикселей
   высоты для звонка мало, а расширять зону псевдоэлементом здесь нельзя:
   слоты стоят вплотную, и невидимая зона залезла бы на соседнюю строку.
   Поэтому ссылка честно занимает 48px и делается заметной. */
.slot__tel {
  display: inline-flex; align-items: center;
  min-height: 48px;
  font-size: 15px;
  color: var(--signal);
  text-decoration: none;
}
.slot__sum { flex: none; font-weight: 600; }
/* Строка с галочкой. Сам квадратик системный и мелкий - 13px, - но нажимают
   по всей строке: label переключает свой input. Значит цель это строка,
   и она обязана быть не ниже 48px. Таких строк в приложении семь:
   согласие на обработку данных, «записываю клиента, а не себя», доп. работы
   в наряде и в предложении, чек-лист, исполнители, оклад мастера. */
.walkin,
label.row:has(> input[type="checkbox"]),
label:has(> input[type="checkbox"]):not(.consent__agree) {
  display: flex; align-items: center; gap: 12px;
  /* Не по краям: квадратик стоит первым, подпись должна идти сразу за ним. */
  justify-content: flex-start;
  min-height: 48px;
  padding: 8px 0;
  cursor: pointer;
  color: var(--ink-2);
  font-size: 15px;
}
/* Свой квадратик вместо системного. Системный - это 13px серого браузерного
   виджета, который в тёмном интерфейсе выглядит инородно, а на зелёном
   accent-color превращается в ядовитый лаймовый квадрат. */
input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  width: 24px; height: 24px; flex: none; margin: 0;
  display: inline-grid; place-content: center;
  border: 1.5px solid rgba(235, 235, 245, .34);
  border-radius: 7px;
  background: rgba(255, 255, 255, .05);
  transition: background-color .16s ease, border-color .16s ease;
}
input[type="checkbox"]::after {
  content: "";
  width: 6px; height: 11px;
  margin-top: -2px;
  border: solid var(--void);
  border-width: 0 2.5px 2.5px 0;
  transform: rotate(45deg) scale(0);
  transition: transform .16s cubic-bezier(.2, .9, .3, 1.35);
}
input[type="checkbox"]:checked {
  background: var(--signal);
  border-color: var(--signal);
}
input[type="checkbox"]:checked::after { transform: rotate(45deg) scale(1); }
/* Пока запрос в пути, код гасит галочку. Системный вид блокировки ушёл
   вместе с appearance: none, поэтому рисуем его сами - иначе человек
   тычет в живую с виду галочку впустую. */
input[type="checkbox"]:disabled { opacity: .4; cursor: default; }
input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--signal); outline-offset: 2px;
}

/* ============================================================
   ПРИМЕРКА ПЛЁНКИ
   ============================================================ */

/* ============================================================
   БЕГУНОК «ДО И ПОСЛЕ» в примерке
   ============================================================ */

/* pan-y обязателен: без него палец, ведущий бегунок вбок, уводил бы
   страницу вниз, а вниз в Telegram это жест закрытия приложения.
   С pan-y вертикальная прокрутка остаётся у страницы, горизонталь наша. */
.cmp {
  --cmp: 50%;
  --cmp-n: .5;
  position: relative;
  /* Сверху 14px не для красоты: над бегунком стоит ссылка «примерить ещё»,
     у неё зона нажатия шире самой надписи, и вплотную бегунок эту зону
     перекрывал. Ссылка теряла половину площади. */
  margin: 14px 0 12px;
  border-radius: var(--r-lg);
  overflow: hidden;
  touch-action: pan-y;
  cursor: ew-resize;
  user-select: none;
  -webkit-user-select: none;
  background: var(--panel);
}
/* Нижний снимок задаёт высоту, верхний ложится на него и режется. */
.cmp__after { display: block; width: 100%; }
.cmp__before {
  position: absolute; inset: 0;
  clip-path: inset(0 calc(100% - var(--cmp)) 0 0);
}
.cmp__before img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}

/* Граница и ручка. Белая линия читается на любом кузове, тень держит её
   на светлом кадре. */
.cmp__handle {
  position: absolute; top: 0; bottom: 0;
  left: var(--cmp);
  width: 44px; margin-left: -22px;
  padding: 0; border: 0; background: none;
  display: grid; place-items: center;
  cursor: ew-resize;
}
.cmp__handle::before {
  content: ""; position: absolute; top: 0; bottom: 0; left: 21px;
  width: 2px; background: #fff;
  box-shadow: 0 0 6px rgba(0, 0, 0, .45);
}
.cmp__grip {
  position: relative;
  width: 38px; height: 38px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .96);
  box-shadow: 0 2px 10px rgba(0, 0, 0, .45);
}
/* Стрелки внутри ручки рисуются рамками: две фигуры вместо картинки. */
.cmp__grip::before, .cmp__grip::after {
  content: ""; position: absolute; top: 50%;
  width: 0; height: 0;
  border: 5px solid transparent;
  transform: translateY(-50%);
}
.cmp__grip::before { left: 7px; border-right-color: #16181B; }
.cmp__grip::after  { right: 7px; border-left-color: #16181B; }

.cmp--live .cmp__grip { transform: scale(1.06); }

/* Подписи по углам. Пропадают, когда бегунок доехал до края: подпись
   «до» над кадром, где этого «до» уже не видно, только путает. */
.cmp__tag {
  position: absolute; top: 12px;
  padding: 5px 10px;
  border-radius: 999px;
  background: rgba(0, 0, 0, .55);
  color: #fff;
  font: 600 12px/1 var(--text);
  letter-spacing: .06em;
  text-transform: uppercase;
  pointer-events: none;
}
.cmp__tag--before {
  left: 12px;
  opacity: clamp(0, calc((var(--cmp-n, .5) - .04) * 14), 1);
}
.cmp__tag--after {
  right: 12px;
  opacity: clamp(0, calc((.96 - var(--cmp-n, .5)) * 14), 1);
}

@media (prefers-reduced-motion: reduce) {
  .cmp--live .cmp__grip { transform: none; }
}

.shot-box { margin-bottom: 12px; }
.shot {
  width: 100%; display: block;
  border-radius: var(--r-lg);
  border: 1px solid var(--edge);
}
.shot--result { margin-bottom: 12px; }
.hidden-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

.presets-block { margin-bottom: 8px; }
.presets {
  display: flex; gap: 12px; overflow-x: auto;
  margin: 0 -14px; padding: 4px 14px 8px;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.presets::-webkit-scrollbar { display: none; }
.preset {
  flex: none; width: 200px; min-height: 92px;
  scroll-snap-align: start;
  display: flex; flex-direction: column; gap: 6px;
  padding: 14px;
  text-align: left; cursor: pointer;
  border-radius: var(--r-md);
  border: 1px solid var(--edge-2);
  background: var(--panel);
  color: var(--ink);
  box-shadow: var(--gleam);
}
.preset--on { border-color: var(--signal); background: var(--signal-dim); }
.preset__head { color: var(--ink); font: 700 16px/1.2 var(--text); letter-spacing: 0; text-transform: none; }
.preset__sub { font-size: 13px; color: var(--muted); line-height: 1.35; }
.preset__dabs { display: flex; gap: 5px; margin-top: auto; }
.preset__dabs i {
  width: 14px; height: 14px; border-radius: 4px;
  border: 1px solid rgba(255,255,255,.18);
  display: block;
}

/* Плитка материала: настоящий образец, а не абстрактная кнопка.
   Это язык товара - глянец, сатин, мат клиент видит в веере. */
.chips { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 12px; }
.chip {
  display: inline-flex; align-items: center; gap: 10px;
  min-height: 48px;
  padding: 10px 16px 10px 12px;
  border-radius: var(--r-md);
  background: var(--lift); color: var(--ink);
  border: 1px solid var(--edge-2);
  box-shadow: var(--gleam);
  font: 500 15px/1.2 var(--text);
  cursor: pointer;
  transition: transform var(--fast) var(--ease);
}
.chip:active { transform: scale(.97); }
.chip--on {
  border-color: var(--signal);
  background: var(--signal-dim);
  color: var(--ink);
  font-weight: 600;
}
.chip__dab {
  width: 26px; height: 26px; flex: none;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, .22);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.25), inset 0 -6px 10px rgba(0,0,0,.28);
}
.chips-group { margin-bottom: 12px; }
.chips-group .h2 { margin: 0 0 10px; font-size: 17px; }
.chips-group .chips { margin-bottom: 0; }
.chips-group .chips-group { margin: 12px 0 0 12px; }
.catalog { margin-bottom: 12px; }


/* Выкраски бренда. Внутри каталога чипы перестают быть пилюлями
   и становятся плитками с настоящим цветом плёнки: человек выбирает
   по цвету, а не по названию. Кружок 26px для этого мал.

   :has() держат все современные вебвью. Где его нет, каталог остаётся
   лентой пилюль - это рабочее состояние, а не поломка. */
.chips-group:has(> .catalog) .chips {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 12px;
}
.chips-group:has(> .catalog) .chip {
  flex-direction: column; align-items: stretch; gap: 8px;
  min-height: 96px;
  padding: 8px 8px 10px;
  border-radius: var(--r-md);
  font: 500 12px/1.25 var(--text);
  text-align: left;
}
/* Название плёнки под выкраской: три строки максимум, дальше многоточие.
   Иначе сетка теряет ритм: у одних плёнок имя в слово, у других в строку. */
.chips-group:has(> .catalog) .chip > span:last-child {
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden; color: var(--ink-2);
}
.chips-group:has(> .catalog) .chip__dab {
  width: 100%; height: 58px;
  border-radius: 9px;
  /* Плёнка на выкраске ловит свет сверху. Один блик, без градиента
     по всему пятну: иначе цвет читается неверно. */
  box-shadow: inset 0 1px 0 rgba(255,255,255,.30),
              inset 0 -10px 16px rgba(0,0,0,.22);
}
.chips-group:has(> .catalog) .chip--on {
  border-color: var(--signal);
  box-shadow: var(--gleam), 0 0 0 2px rgba(53,196,106,.35);
}
.catalog { justify-content: flex-start; text-align: left; font-weight: 600; }
.catalogs { display: grid; gap: 12px; }

/* Что выбрано. На боевом экране 22.08.2026 эта строка висела сиротой
   между каталогами и сметой: «Metallic Yellow · 90330» без подписи,
   без подложки, просто обрывок текста посреди экрана. Теперь это
   именованный блок с меткой - видно, что это выбор, а не мусор. */
.picked {
  margin: 20px 0 12px;
  padding: 14px 16px;
  border-radius: var(--r-md);
  background: var(--panel);
  box-shadow: var(--gleam);
}
.picked::before {
  content: "Выбрано";
  display: block;
  margin-bottom: 6px;
  font: 600 12px/1.2 var(--text);
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--muted);
}
.picked-line { margin: 0; font: 600 17px/1.35 var(--text); color: var(--ink); }
.picked .muted.picked-line { font-weight: 400; color: var(--muted); }
/* Карточка выбранной работы складывается СТОЛБИКОМ.
 *
 *  Здесь же был корень поломки, из-за которой 29.08.2026 разъезжались
 *  карточки снимков: класс ставил display:flex в направлении row, и всё
 *  содержимое карточки вставало в РЯД. Тогда починили точечно, только
 *  для .shots. Владелец 30.08.2026 прислал тот же разъезд на экране
 *  наряда: название работы сжималось в колонку и переносилось по слогам,
 *  «изменить/убрать» ложилось поверх имени мастера, «мастеру 80 000 ₽»
 *  разрывалось на три строки, а в режиме правки поля формы уезжали
 *  за правый край экрана.
 *
 *  Внутри карточки лежат три блока (название с ценой, мастер с кнопками,
 *  сумма мастеру), и в режиме правки - вся форма. Все они строки,
 *  а не колонки. Поэтому направление вертикальное. */
.picked__row { display: flex; flex-direction: column; gap: 6px; padding: 6px 0; }
/* Длинное название («Полная оклейка кузова в цветной PPF») не должно
   распирать строку и выдавливать цену за край. */
.picked__row .row > span:first-child { min-width: 0; overflow-wrap: anywhere; }
.picked__row .row { flex-wrap: wrap; }
/* Цена держится правого края, даже когда название заняло всю строку
   и цену перенесло вниз.
 *
 *  Владелец 31.08.2026 про коммерческое предложение: «там вёрстка ползёт
 *  чуть». В предложении названия длинные - «Стёкла: Атермальная тонировка
 *  Llumar Air 80». Замер на 375px: название занимало все 315 пикселей строки
 *  в три ряда, а цена уезжала на следующую строку ВЛЕВО, под название,
 *  и вместо «работа слева, деньги справа» получался столбик из двух
 *  прижатых к левому краю чисел.
 *
 *  Перенос строки сам по себе правильный: без него цена выдавливалась
 *  за край. Не хватало только прижать её вправо на новой строке. */
.picked__row .row > .num { margin-left: auto; }
.estimate { margin-top: 12px; }
.counter { font-size: 14px; color: var(--muted); text-align: center; margin: 12px 0 0; }

/* ============================================================
   КАРТОЧКА НАРЯДА. Самый плотный экран: тридцать блоков подряд.
   Именно здесь заголовки и отбивка решают всё.
   ============================================================ */
.car-head {
  display: flex; align-items: center; gap: 12px;
  margin: 24px 0 12px; padding-top: 20px;
  border-top: 1px solid var(--edge-2);
  font: 700 19px/1.25 var(--text);
  letter-spacing: -.01em;
}
.car-head:first-child { margin-top: 0; padding-top: 0; border-top: 0; }

.item__accept {
  /* Было 37px высотой у каждой работы, десяток подряд. */
  min-height: 48px;
  margin-top: 12px; padding: 12px 16px;
  font-size: 16px;
}
.item__comment { margin: 8px 0 0; font-size: 15px; color: var(--ink-2); }
.plan { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--edge); }
.add-work { margin-top: 12px; }

/* Исполнители работы. Владелец 13.08.2026: «машину может клеить два
   человека». Блок одного исполнителя сцеплен рамкой: иначе четыре поля
   подряд читаются как один длинный список, и непонятно, чья сумма к кому
   относится.

   Редизайн сделал блок горизонтальным флексом, и шесть полей одного
   мастера сплющило в строку: подпись «По окладу, отдельно не платим»
   зажималась в колонку 68px и разъезжалась на три строки. Возвращаем
   вертикаль. */
.crew__row {
  margin-bottom: 12px;
  padding: 12px;
  border: 1px solid var(--edge);
  border-radius: var(--r-md);
}
.crew__row .field { margin-bottom: 8px; }
.crew__row .field:last-of-type { margin-bottom: 0; }
.crew__drop { display: block; margin-top: 8px; }

.field {
  display: block; width: 100%; min-height: 52px;
  padding: 14px 16px;
  margin-bottom: 12px;
  border-radius: var(--r-md);
  border: 1px solid var(--edge-2);
  background: var(--raise);
  color: var(--ink);
  font: 400 17px/1.3 var(--text);
}
.field::placeholder { color: var(--faint); }
.field:focus { outline: none; border-color: var(--signal); box-shadow: 0 0 0 3px var(--signal-dim); }
/* Поле даты в Сафари держит СВОЮ минимальную ширину: под «дд.мм.гггг»,
   иконку календаря и внутренние отступы. width: 100% её не отменяет,
   и на узком айфоне поле распирает карточку - страница начинает ездить
   вбок, а левый край содержимого уходит за экран. Stephan 03.09.2026
   про наряд из шести работ: «в заказ нарядах вёрстка ползёт».
   В Хроме этого не видно: там поле сжимается само. */
input[type="date"].field, input[type="time"].field {
  min-width: 0; max-width: 100%;
  /* Одних min-width и max-width не хватило, и это доказано замером
     с телефона владельца 05.09.2026 13:43: «Новый наряд», экран 440px,
     поле 446px в родителе 412px. То есть правило применилось (браузер
     отдал min-width 0 и max-width 100%), а поле всё равно распёрло.
     Ширину задаёт не сам input, а нативный контрол внутри него, и пока
     он рисуется системой, он тянет одеяло на себя. Поэтому отключаем
     системный вид и ужимаем внутренний элемент со значением.

     Настольный WebKit это не воспроизводит: там поле честно сжимается
     до 412px. Проверить можно только на его айфоне, и дозор вёрстки
     сообщит сам, если вылет останется. */
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
}
input[type="date"].field::-webkit-date-and-time-value,
input[type="time"].field::-webkit-date-and-time-value {
  /* Внутренний элемент со значением по умолчанию центрируется и держит
     ширину под самый длинный формат. Прижимаем влево и отдаём ему ровно
     ширину поля, не больше. */
  text-align: left;
  width: 100%;
  min-width: 0;
  margin: 0;
}
/* Иконка календаря у нативного поля на айфоне занимает место справа
   и участвует в той же минимальной ширине. Оставляем её, но не даём
   ей растягивать поле. */
input[type="date"].field::-webkit-calendar-picker-indicator {
  margin-left: auto;
  flex: none;
}
select.field {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' stroke='%239099A1' stroke-width='2' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
  padding-right: 44px;
}

.notes { margin-bottom: 12px; }
.note {
  padding: 14px 0;
  border-top: 1px solid var(--edge);
}
.note:first-child { border-top: 0; }
.note--mine .note__text { color: var(--ink); }
.note__author { font: 600 13px/1.2 var(--text); color: var(--muted); }
.note__text { margin: 0; font-size: 16px; white-space: pre-wrap; overflow-wrap: anywhere; }
.note__empty { color: var(--muted); font-size: 15px; padding: 12px 0; }
.note__input { margin-bottom: 12px; }
.note__photo { width: 100%; border-radius: var(--r-md); margin-top: 10px; display: block; }
.note__head { margin-bottom: 8px; align-items: baseline; }

/* Снимки в КП: две колонки, не три.
 *
 *  При трёх на телефоне 375px колонка выходит 117px, а подпись
 *  «поставить в шапку» одна занимает около 120px. Две ссылки в строке
 *  туда не влезали и вылезали поверх соседних снимков и за сетку -
 *  владелец 29.08.2026 прислал скриншот именно с этим.
 *
 *  Две колонки заодно чинят второе: по превью в 117px нельзя оценить
 *  примерку, а лежат они там ровно для этого. */
.shots { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-bottom: 12px; }
.shots img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--r-sm); display: block; }
/* Карточка снимка складывается СТОЛБИКОМ.
 *
 *  Корень поломки был здесь: у неё класс picked__row, а он ставит
 *  display:flex - то есть снимок и строка действий ложились рядом
 *  по горизонтали внутри ячейки шириной в пол-экрана. Замер на 375px:
 *  карточка 14..183, строка действий 129..193, десять пикселей наружу.
 *  Оттуда подписи и налезали на соседние снимки. */
.shots .card { display: block; }
/* Действия под снимком переносятся и умеют сжиматься. Даже если надпись
   когда-нибудь удлинится, наружу она больше не вылезет. */
.shots .row { flex-wrap: wrap; justify-content: flex-start; gap: 6px 12px; margin-top: 6px; }
.shots .row .linkbtn { min-width: 0; }

.qr { width: 160px; height: 160px; display: block; margin: 0 auto 12px; border-radius: var(--r-sm); background: #fff; padding: 8px; }

.issues, .done, .task, .mark { margin-bottom: 12px; }
.issues__head, .done__head, .mark__head { margin-bottom: 10px; }
.task__title { font: 600 17px/1.3 var(--text); margin: 0 0 6px; }
/* Срок держим на одной строке с именами: при двух исполнителях список
   переносится, и плашка уезжала к середине. По базовой линии, а не по
   верхнему краю - шрифты тут разного размера. */
.task > .row.muted { align-items: baseline; }
.mark__row { display: flex; align-items: center; gap: 12px; padding: 10px 0; }
.mark__chips { display: flex; gap: 10px; flex-wrap: wrap; }
.mark__note, .mark__hint { font-size: 14px; color: var(--muted); margin: 6px 0 0; }
.mark__past { color: var(--muted); font-size: 15px; }

/* ============================================================
   СОГЛАСИЕ НА ОБРАБОТКУ ДАННЫХ
   ============================================================ */
.consent { margin-bottom: 12px; }
.consent__head { margin-bottom: 10px; }
.consent__list { margin: 0 0 12px; padding-left: 20px; }
.consent__item { margin-bottom: 6px; font-size: 16px; }
.consent__text, .consent__who { font-size: 15px; color: var(--ink-2); }
.consent__text { margin: 0; white-space: pre-wrap; }
.consent__note { font-size: 14px; color: var(--muted); margin-top: 10px; }
.consent__agree {
  display: flex; align-items: flex-start; gap: 14px;
  min-height: 56px;
  padding: 16px;
  margin: 16px 0;
  border-radius: var(--r-md);
  border: 1px solid var(--edge-2);
  background: var(--panel);
  cursor: pointer;
  font-size: 16px;
}
/* Размеры и вид галочка берёт из общего правила input[type="checkbox"];
   accent-color после appearance: none не работает, здесь он не нужен. */

/* ============================================================
   СОСТОЯНИЯ
   ============================================================ */
.state {
  padding: 40px 16px; color: var(--muted); text-align: center; font-size: 16px;
}
.state--error { color: var(--stamp); }

.done { color: var(--ink-2); }

/* ============================================================
   ПОДТВЕРЖДЕНИЕ ОТПРАВКИ КЛИЕНТУ.
   Новые классы, добавляются к существующим именам, ничего не ломают.
   ============================================================ */
.sheet-back {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(0, 0, 0, .55);
  display: flex; align-items: flex-end;
}
@supports (backdrop-filter: blur(1px)) {
  .sheet-back { -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
}
.sheet {
  width: 100%; max-width: 480px; margin: 0 auto;
  padding: 20px 16px calc(20px + var(--safe-b));
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  background: var(--panel);
  border-top: 1px solid var(--edge-2);
  box-shadow: var(--gleam), 0 -20px 50px rgba(0,0,0,.5);
}
.sheet__grip {
  width: 40px; height: 4px; border-radius: 2px;
  background: var(--edge-2);
  margin: 0 auto 18px;
}
.sheet__title { font: 700 21px/1.25 var(--text); letter-spacing: -.015em; margin: 0 0 10px; }
.sheet__what {
  padding: 14px; margin: 0 0 8px;
  border-radius: var(--r-md);
  background: var(--raise);
  font-size: 16px;
}
.sheet__to { font-size: 15px; color: var(--muted); margin: 0 0 20px; }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 2px;
  border-radius: 4px;
}


/* ============================================================
   СТРОКА-ПЕРЕХОД
   ============================================================

   Имя, счётчик, стрелка - одна строка вместо блока на восемьсот
   пикселей. Заметки, чек-лист и видео в карточке наряда занимали
   раскрытыми 1796px из 4766, то есть 38%, и выдавливали вниз кнопки,
   которыми пользуются каждый день.

   Высота 56px: столько же, сколько у остальных строк списка, чтобы
   переход не выделялся из ритма экрана. */
.jump {
  display: flex; align-items: center; gap: 10px; width: 100%;
  min-height: 56px; padding: 10px 14px;
  border-radius: var(--r-md);
  background: var(--lift); border: 1px solid var(--edge-2);
  box-shadow: var(--gleam);
  color: var(--ink); font: 600 16px/1.25 var(--text);
  text-align: left; cursor: pointer;
  transition: transform var(--fast) var(--ease);
}
.jump:active { transform: scale(.99); background: var(--raise); }
.jump + .jump { margin-top: 8px; }
.jump__t { flex: 1; min-width: 0; }
.jump__n {
  flex: none; color: var(--muted); font-weight: 500; font-size: 15px;
}
/* «нет» вместо нуля: ноль читается как счётчик и заставляет всматриваться,
   слово - как состояние, и его видно не глядя. */
.jump__n--none { color: var(--faint); font-size: 14px; }
.jump__a { flex: none; color: var(--faint); font-weight: 400; }


/* ============================================================
   ЛИЦО ПЛЁНКИ
   ============================================================

   Цвет лежит в самом блоке, свет накладывается сверху отдельным слоем.
   Свет не нарисован, а снят: кусок кузова из их же бокса, переведённый
   в чистую светлоту. Форма бликов поэтому подлинная - она повторяет
   изгиб панели, и нарисовать её нельзя.

   Владелец 27.08.2026 про рисованный вариант: «выглядит как глянцевая
   кнопка, а не как плёнка».

   Каждой плитке достаётся свой участок текстуры через --px/--py: сетка
   из восемнадцати одинаково бликующих образцов читается как обои,
   а вживую два выкраса рядом никогда не отражают свет одинаково. */
.dab {
  position: relative; display: block; width: 100%;
  border-radius: 9px; overflow: hidden;
  background: var(--c, #888);
  border: 1px solid rgba(255, 255, 255, .14);
}
.dab::after, .dab::before {
  background-size: 118% auto;
  background-position: var(--px, 0%) var(--py, 0%);
}
.dab::after {
  content: ""; position: absolute; inset: 0;
  mix-blend-mode: soft-light;
}

/* Глянцу мягкого света мало: настоящий глянец даёт зеркальные полосы,
   а не лёгкий отлив. Второй слой жёстким светом добавляет резкости. */
.d-gloss::after { background-image: url("tex/gloss.png"); opacity: 1; }
.d-gloss::before {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background-image: url("tex/gloss.png");
  mix-blend-mode: hard-light; opacity: .34;
}

/* Сатин обязан читаться МЕЖДУ глянцем и матом. На одном мягком свете
   он с матом сливался: мягкий свет на насыщенном цвете почти не виден. */
.d-satin::after { background-image: url("tex/satin.png"); opacity: 1; }
.d-satin::before {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background-image: url("tex/satin.png");
  mix-blend-mode: hard-light; opacity: .11;
}

/* Мат: тот же приём вполсилы. Слабый отлив и есть мат. */
.d-matte::after { background-image: url("tex/matte.png"); opacity: .55; }
.d-supermatte::after { background-image: url("tex/matte.png"); opacity: .16; }

/* Хром. Снимка хромовой машины у студии нет, выведен из глянцевого.
   Зеркало показывает не блик, а комнату: светлый потолок, резкая линия
   горизонта, отражённый пол. Без этой линии хром выглядит светло-серым. */
.d-chrome {
  background: linear-gradient(180deg,
    var(--c-hi) 0%, var(--c) 34%, var(--c-lo) 49%, #0A0A0C 50.5%,
    var(--c) 56%, var(--c-hi) 82%, var(--c-hi) 100%);
}
.d-chrome::after {
  background-image: url("tex/gloss.png");
  mix-blend-mode: hard-light; opacity: .5;
}

/* Хамелеон. Снимка тоже нет. Цвет уезжает по углу взгляда, поэтому под
   светом лежит переход между двумя оттенками, а не один. */
.d-chameleon {
  background: linear-gradient(114deg,
    var(--c-a) 0%, var(--c) 48%, var(--c-b) 100%);
}
.d-chameleon::after { background-image: url("tex/gloss.png"); opacity: .85; }

/* Прозрачная - не цвет, а верхний слой. Белый брусок про прозрачность
   не говорит ничего, шашечку узнают без подписи. */
.d-clear {
  background:
    linear-gradient(45deg, rgba(255,255,255,.12) 25%, transparent 25%,
      transparent 75%, rgba(255,255,255,.12) 75%) 0 0 / 14px 14px,
    linear-gradient(45deg, rgba(255,255,255,.12) 25%, transparent 25%,
      transparent 75%, rgba(255,255,255,.12) 75%) 7px 7px / 14px 14px,
    #2A2A2E;
}
.d-clear::after {
  background-image: url("tex/gloss.png");
  mix-blend-mode: screen; opacity: .3;
}

/* Металлик и блёстки поверх любой поверхности. Шаги слоёв взаимно
   непростые (7, 9, 11, 13): при кратных узор совмещается сам с собой
   и читается как ткань в горошек, а не как металлик. */
.d-flake .fl {
  position: absolute; inset: 0; z-index: 2;
  background-image:
    radial-gradient(circle at 21% 34%, rgba(255,255,255,.9) .35px, transparent .45px),
    radial-gradient(circle at 67% 19%, rgba(255,255,255,.6) .3px, transparent .4px),
    radial-gradient(circle at 43% 71%, rgba(255,255,255,.75) .3px, transparent .4px);
  background-size: 7px 9px, 9px 7px, 11px 13px;
  mix-blend-mode: screen; opacity: .34;
}
/* На матовой крошка не блестит, а лишь угадывается: матовая поверхность
   свет рассеивает, а не отражает. Ярче - и получается горошек. */
.d-matte.d-flake .fl { opacity: .16; }

/* МЕЛКИЙ ОБРАЗЕЦ. Ниже двадцати пикселей текстура перестаёт быть
   поверхностью и становится грязью: в кружок попадает случайный обрывок
   панели. Там оставляем цвет и один тонкий отлив. */
.dab--tiny { border-radius: 6px; }
.dab--tiny::after, .dab--tiny::before { background-image: none !important; }
.dab--tiny::after {
  mix-blend-mode: normal; opacity: 1;
  background: linear-gradient(160deg, rgba(255,255,255,.34) 0%,
    rgba(255,255,255,.06) 34%, rgba(0,0,0,.16) 100%);
}
.dab--tiny .fl { display: none; }


/* ============================================================
   ВОРОНКА КАТАЛОГА ПЛЁНОК
   ============================================================

   Владелец 27.08.2026: «нужно чтобы было не всё вывалено, а сам выбирал
   по поиску цвет, тип, глянец, монохром, а затем марку плёнки». */

.pillbar {
  display: flex; gap: 8px; overflow-x: auto;
  padding: 2px 0 10px; scrollbar-width: none;
}
.pillbar::-webkit-scrollbar { display: none; }
.pill {
  flex: none; display: inline-flex; align-items: center; gap: 7px;
  min-height: 40px; padding: 0 14px; border-radius: 20px;
  background: var(--lift); color: var(--ink);
  border: 1px solid var(--edge-2); font: 500 14px/1 var(--text);
  white-space: nowrap; cursor: pointer;
}
.pill--on {
  border-color: var(--signal); background: var(--signal-dim);
  font-weight: 600;
}
.pill--x { color: var(--muted); }
.pill__dab { display: block; width: 17px; height: 17px; flex: none; }
.pill__dab .dab { height: 17px; }

/* Строка поиска. Взято у МГК: на тысяче позиций найти плёнку
   по артикулу фильтрами нельзя, а код с веера человек знает. */
.search { margin-bottom: 12px; }

.huehead { margin: 14px 0 8px; display: flex;
  justify-content: space-between; align-items: baseline; gap: 8px; }

/* Три колонки: восемнадцать плиток в две давали четыре экрана
   прокрутки только ради меню. */
.huegrid { display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 8px; margin-bottom: 12px; }
.huegrid--solo { grid-template-columns: 1fr; }
/* На узких экранах три колонки не держат строку «Серебристый 30»: замер
   05.09.2026 на 320px дал шесть порванных плиток из восемнадцати. Две
   колонки шире, и всё встаёт в строку. Порог 360, а не 320: на 360
   рвалась одна. */
@media (max-width: 360px) {
  /* :not обязателен. Одиночная плитка тоже .huegrid, специфичность
     у правил одинаковая, и медиазапрос ниже по файлу молча делал её
     половинной - «Прозрачные с блёстками» рвалось уже из-за меня. */
  .huegrid:not(.huegrid--solo) { grid-template-columns: repeat(2, 1fr); }
}
.huecell {
  /* Поля по бокам 6px, а не 8. Замер 05.09.2026: строке «Серебристый 30»
     при восьми не хватало ровно этих четырёх пикселей, и она рвалась
     на две. */
  padding: 8px 6px 9px; border-radius: var(--r-md);
  background: var(--lift); border: 1px solid var(--edge-2);
  cursor: pointer; text-align: left;
}
.huecell:active { transform: scale(.98); }
.huecell .dab { height: 40px; }
/* Кружок цвета на шаге выбора.
   Обводка не украшение: чёрная плёнка на тёмном фоне без неё выглядит
   дырой в карточке, а белая сливается с подложкой. Проверено на кадре:
   при .16 «Чёрный» читался как пустое место. */
.huedot {
  display: block; width: 46px; height: 46px; border-radius: 50%;
  background: var(--c);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .32),
              inset 0 -6px 12px rgba(0, 0, 0, .18);
  margin: 2px auto 2px;
}
.huecell--dot { text-align: center; }
.huecell--dot b { justify-content: center; gap: 6px; }
.huegrid--solo .huecell .dab { height: 52px; }
/* Имя и счётчик в одну строку: отдельной строкой счётчик добавлял
   по 17px на каждую из восемнадцати плиток, то есть почти экран. */
.huecell b {
  display: flex; justify-content: space-between; gap: 4px;
  font: 600 11px/1.3 var(--text); color: var(--ink); margin-top: 6px;
}
.huecell b i {
  font: 500 11px/1.3 var(--text); color: var(--muted);
  font-style: normal; font-variant-numeric: tabular-nums;
  /* Число не переносится и не сжимается.
     Замер каталога 05.09.2026: у трёх плиток из восемнадцати длинное имя
     («Серебристый», «Коричневый», «Фиолетовый») выдавливало счётчик,
     и «30» вставало столбиком - «3» и под ней «0». Это читается как
     поломка, а не как число. */
  white-space: nowrap; flex: 0 0 auto;
}
.huecell b span {
  /* Длинное имя переносится само, а не ломает соседа. min-width нужен:
     во флексе элемент по умолчанию не уже своего содержимого и вместо
     переноса распирает строку. */
  min-width: 0; overflow-wrap: anywhere;
}

.typebar { margin-bottom: 12px; }
.typebar .chip { min-height: 54px; padding: 8px 14px 8px 8px;
  font-size: 14px; }
/* 38px, а не 26: чип типа существует, чтобы показать поверхность.
   На 26px текстура отключается как грязь, и глянец с матом выходят
   одинаковыми цветными квадратами - то есть чип не делает своей работы. */
.typebar .dab { width: 38px; height: 38px; flex: none; border-radius: 8px; }

/* Панель фильтров не влезает в 375px и прокручивается пальцем.
   Чтобы это было видно, а не выглядело обрезанным, край подтуманиваем. */
.pillbar { position: relative; -webkit-mask-image:
  linear-gradient(90deg, #000 0, #000 calc(100% - 22px), transparent 100%);
  mask-image:
  linear-gradient(90deg, #000 0, #000 calc(100% - 22px), transparent 100%); }
/* «Сбросить» уезжала за край и была не видна вовсе. Ставим её первой,
   когда есть что сбрасывать: это единственная кнопка, которую ищут
   специально. */
.pill--x { order: -1; color: var(--stamp); border-color: var(--edge-2); }

.brands { display: grid; gap: 8px; margin-bottom: 12px; }
.brandrow {
  display: flex; align-items: center; gap: 10px; width: 100%;
  min-height: 52px; padding: 8px 12px; border-radius: var(--r-md);
  background: var(--lift); border: 1px solid var(--edge-2);
  color: var(--ink); font: 600 15px/1.2 var(--text);
  text-align: left; cursor: pointer;
}
.brandrow--on { border-color: var(--signal); background: var(--signal-dim); }
.brandrow__n { flex: 1; min-width: 0; }
/* .brandrow__d (пять образцов у марки) убран: владелец 05.09.2026 просил
   «убрать кристаллики», и это была единственная россыпь мелких цветных
   квадратов на экране. Стили удалены вместе с разметкой, чтобы не гнить
   мёртвым правилом. */
.brandrow__k {
  color: var(--muted); font-weight: 500; font-size: 14px;
  font-variant-numeric: tabular-nums;
}
.brandrow__a { color: var(--faint); font-weight: 400; }

.found { margin-bottom: 8px; }
.filmgrid { display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 10px; margin-bottom: 12px; }
.filmcell {
  padding: 8px 8px 9px; border-radius: var(--r-md);
  background: var(--lift); border: 1px solid var(--edge-2);
  cursor: pointer; text-align: left;
}
.filmcell:active { transform: scale(.98); }
.filmcell.chip--on {
  border-color: var(--signal); background: var(--signal-dim);
}
.filmcell .dab { height: 58px; }
.filmcell b {
  display: block; font: 600 12px/1.3 var(--text);
  color: var(--ink); margin-top: 7px;
}
.filmcell i {
  display: block; font: 500 11px/1.3 var(--text);
  color: var(--faint); font-style: normal;
}

/* Выкраска под результатом примерки. Владелец сравнивает её, снятую макро
   с ладони, с бортом машины, снятым с трёх метров, и говорит «не похоже».
   Оба масштаба рядом снимают вопрос без единой генерации. Высоту держим:
   выкраски приходят разной формы, от квадрата до широкой полосы, и без
   ограничения полоса на пол-экрана отодвигала бы кнопки оценки за сгиб. */
.swatch__img {
  display: block; width: 100%; max-height: 190px;
  object-fit: cover; border-radius: var(--r-sm);
}
.swatch__hint { margin-top: 10px; }

/* Сравнение цветов: каталожный против замеренного со снимка мастера. */
.colcmp { margin: 12px 0; }
.colcmp__row { display: flex; gap: 16px; }
.colcmp__one { text-align: center; }
.colcmp__dab { display: block; width: 72px; height: 72px;
               border-radius: 8px;
               border: 1px solid rgba(255, 255, 255, .15); }
.colcmp__one i { font-size: 12px; opacity: .7; font-style: normal; }
.colcmp__btns { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.swatchpick { margin-top: 8px; }

/* Повторная примерка под сравнением цветов: снимок уже применён,
   и результат надо перерисовать.
   Волосяная линия, а не только отступ: без неё кнопка читалась четвёртой
   в ряду решений по цвету, хотя она про другое. Проверено глазами
   на 375 px, отступа в 14 px не хватало. */
.refit {
  margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--edge);
}

/* ============================================================
   СТРАХОВКА ОТ РАСПОЛЗАНИЯ. Причина, а не симптом.
   ============================================================

   Флекс-ребёнок по умолчанию имеет min-width: auto, то есть НЕ сжимается
   ниже своего содержимого. Хром в большинстве случаев всё равно ужимает
   текст, а Сафари честно распирает строку - и вся страница уезжает вбок.
   Владелец 03.09.2026, второй раз про наряды: «вёрстка ползёт, ты не всё
   поправил». У него айфон, у меня Хром: на 320, 360, 375, 390, 393, 412,
   430 и 440 пикселях, с крупным шрифтом и слитными словами - ровно.

   В приложении 50 флекс-контейнеров, и разрешение сжиматься было прописано
   у четырнадцати. Вместо охоты за конкретным местом снимаем запрет там,
   где живёт текст: в карточках, строках и списках.

   Тем, кто держать ширину ОБЯЗАН - иконки, кружки, миниатюры, госномер -
   это не мешает: у них flex: none, и сжимать их браузер не станет.

   Картинкам отдельная страховка: снимок шире контейнера тоже уводит
   страницу, а max-width это чинит раз и навсегда. */
.card *, .order *, .row > *, .jump > *, .price > *, .payline > *,
.summary__cell > *, .mark__row > *, .day__head > *, .slot > *,
.item > *, .booking > *, .hero__body > * { min-width: 0; }

/* Статус работы. Текст его задаёт код («ждёт», «на проверке»), но
   страховка нужна и здесь: тег - это inline-flex, а значит тоже
   распирает строку, если однажды в него попадёт что-то длинное. */
.tag { max-width: 100%; }

img, svg, video, canvas { max-width: 100%; }
