/* =============================================================================
   Оформление сервиса сдельных работ.

   Система взята из `mockup.html` и перенесена без выдумывания своей: те же
   токены, те же классы, те же размеры. Если что-то нужно поменять — сначала
   макет, потом сюда, иначе экраны и утверждённый макет разойдутся.

   Три вещи, за которые этот файл отвечает головой:

   1. Полная светлая палитра стоит в ГОЛОМ `:root`. Токен, определённый только
      внутри @media или [data-theme], в состоянии «системная тема» не
      применяется вовсе — страница рисует текст одной темы на фоне другой.
      Поэтому тёмная переопределяется дважды (media и [data-theme="dark"]),
      а светлая — ещё раз в [data-theme="light"], чтобы ручной выбор побеждал
      системный в обе стороны.
   2. Шрифты берутся ЛОКАЛЬНО. Интернета на рабочих местах может не быть,
      и шрифтовой хостинг превратил бы страницу в гонку с таймаутом.
      Запасной стек честный: без файлов страница читается системным шрифтом.
   3. Палец в цеху крупнее курсора: цель нажатия — не меньше 44 px, поля ввода
      от 17 px (мельче — iOS зумит страницу при фокусе, и человек теряет место).

   Порядок разделов: токены → каркас → блоки экрана → состояния → печать.
   ============================================================================= */


/* -----------------------------------------------------------------------------
   Шрифты. Файлы кладутся в `php/public/fonts/`.

   `local()` идёт первым: если шрифт стоит в системе, файл вообще не грузится.
   Если файлов нет и в системе шрифта нет — семейство считается недоступным,
   и берётся следующее из стека. Это не поломка, а запланированный запасной путь.

   Каталог называется `fonts`, а не «шрифты». Правило проекта про латиницу
   распространяется и на имена каталогов: `check_latin.py` обходит дерево
   и ругается на кириллицу в имени, то есть красным станет весь прогон
   в тот день, когда файлы шрифтов положат на место. Плюс кириллический
   путь в `url()` уезжает на сервер процентными кодами, и совпасть с именем
   каталога на диске он обязан посимвольно — проверить это без сервера нечем.
   -------------------------------------------------------------------------- */
@font-face{
  font-family:"Golos Text";
  src:local("Golos Text"), local("GolosText-Regular"),
      url("/fonts/GolosText-Regular.woff2") format("woff2");
  font-weight:400; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"Golos Text";
  src:local("Golos Text Medium"), local("GolosText-Medium"),
      url("/fonts/GolosText-Medium.woff2") format("woff2");
  font-weight:500; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"Golos Text";
  src:local("Golos Text SemiBold"), local("GolosText-SemiBold"),
      url("/fonts/GolosText-SemiBold.woff2") format("woff2");
  font-weight:600; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"Golos Text";
  src:local("Golos Text Bold"), local("GolosText-Bold"),
      url("/fonts/GolosText-Bold.woff2") format("woff2");
  font-weight:700; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"Golos Text";
  src:local("Golos Text Black"), local("GolosText-Black"),
      url("/fonts/GolosText-Black.woff2") format("woff2");
  font-weight:900; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"PT Mono";
  src:local("PT Mono"), local("PTMono-Regular"),
      url("/fonts/PTMono-Regular.woff2") format("woff2");
  font-weight:400; font-style:normal; font-display:swap;
}


/* -----------------------------------------------------------------------------
   Токены. Светлая палитра — полная и в голом :root.
   -------------------------------------------------------------------------- */
:root{
  --ground:#EDECE8; --surface:#FFFFFF; --sunk:#F6F5F2;
  --ink:#16150F; --muted:#6E6A5F; --rule:#D4D1C7;
  --action:#2E3192; --action-ink:#FFFFFF; --action-soft:#E4E4F3;
  --warn:#B45309; --warn-soft:#FBEEDC; --warn-ink:#FFFFFF;
  --done:#0F766E; --done-soft:#DCEFED; --done-ink:#FFFFFF;
  --shadow:0 1px 0 rgba(22,21,15,.06), 0 6px 20px -12px rgba(22,21,15,.35);

  /* --warn-ink и --done-ink — чернила НА сплошной плашке, по образцу
     --action-ink из макета. В светлой теме это белый, как и было.

     Заведены потому, что в тёмной теме --warn и --done становятся СВЕТЛЫМИ
     (#E0A050 и #4FD1C5), и белый текст по ним даёт 2,2:1 и 1,9:1 — надпись
     видно на просвет. Разъезжается при этом не украшение: ровно эти две
     плашки говорят «в очереди» и «отмечено», то есть дошла работа или нет.
     Без читаемой надписи остаётся один цвет, а его в цеху различают не все. */

  /* Размеры и шрифты — тоже токенами, чтобы правка была в одном месте.
     --tap: минимальная цель нажатия. 44 px — нижняя граница, ниже неё
     промахи в цеху перестают быть случайностью. */
  --tap:48px;
  --radius:3px;
  --font-text:"Golos Text", system-ui, -apple-system, "Segoe UI", Roboto,
              "Helvetica Neue", Arial, sans-serif;
  --font-mono:"PT Mono", ui-monospace, "Cascadia Mono", Consolas,
              "DejaVu Sans Mono", "Courier New", monospace;
}

/* Тёмная под системной темой. Защита :root:not([data-theme="light"]) нужна,
   чтобы ручной выбор светлой не был перебит системой. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --ground:#141309; --surface:#1F1E16; --sunk:#191811;
    --ink:#EDECE8; --muted:#9C978A; --rule:#33322A;
    --action:#9296F0; --action-ink:#12111C; --action-soft:#26263F;
    /* Плашки здесь светлые, поэтому чернила на них тёмные — как у --action-ink.
       Белым по ним надпись «в очереди» не читается вовсе. */
    --warn:#E0A050; --warn-soft:#3A2C15; --warn-ink:#12111C;
    --done:#4FD1C5; --done-soft:#12302C; --done-ink:#12111C;
    --shadow:0 1px 0 rgba(0,0,0,.4), 0 6px 20px -12px rgba(0,0,0,.8);
  }
}

/* Тёмная по явному выбору человека — работает и при светлой системной теме. */
:root[data-theme="dark"]{
  --ground:#141309; --surface:#1F1E16; --sunk:#191811;
  --ink:#EDECE8; --muted:#9C978A; --rule:#33322A;
  --action:#9296F0; --action-ink:#12111C; --action-soft:#26263F;
  --warn:#E0A050; --warn-soft:#3A2C15; --warn-ink:#12111C;
  --done:#4FD1C5; --done-soft:#12302C; --done-ink:#12111C;
  --shadow:0 1px 0 rgba(0,0,0,.4), 0 6px 20px -12px rgba(0,0,0,.8);
}

/* Светлая по явному выбору — повтор намеренный: так выбор человека побеждает
   системную тему в обе стороны, а не только в одну. */
:root[data-theme="light"]{
  --ground:#EDECE8; --surface:#FFFFFF; --sunk:#F6F5F2;
  --ink:#16150F; --muted:#6E6A5F; --rule:#D4D1C7;
  --action:#2E3192; --action-ink:#FFFFFF; --action-soft:#E4E4F3;
  --warn:#B45309; --warn-soft:#FBEEDC; --warn-ink:#FFFFFF;
  --done:#0F766E; --done-soft:#DCEFED; --done-ink:#FFFFFF;
  --shadow:0 1px 0 rgba(22,21,15,.06), 0 6px 20px -12px rgba(22,21,15,.35);
}


/* -----------------------------------------------------------------------------
   База
   -------------------------------------------------------------------------- */
*{box-sizing:border-box}

body{
  background:var(--ground);
  margin:0;
  color:var(--ink);
  font-family:var(--font-text);
  font-size:16px; line-height:1.5;
  -webkit-font-smoothing:antialiased;
  -webkit-text-size-adjust:100%;
}

/* Скрытие через атрибут: JS показывает и прячет полосу связи именно им,
   а правила ниже раздают display: без !important атрибут проиграет. */
[hidden]{display:none !important}

h1,h2,h3{margin:0 0 6px; letter-spacing:-.01em; line-height:1.2}
h1{font-size:24px; font-weight:900}
h2{font-size:19px; font-weight:700}
h3{font-size:16px; font-weight:700}
p{margin:0 0 8px}
a{color:var(--action)}

.mono{font-family:var(--font-mono)}
.num{font-variant-numeric:tabular-nums}
.muted{color:var(--muted)}
.lbl{
  font-size:12px; letter-spacing:.1em; text-transform:uppercase;
  color:var(--muted); font-weight:600;
}


/* -----------------------------------------------------------------------------
   Каркас: .shell → .appbar → .screen-body
   Ровно то, что собирает View::page().
   -------------------------------------------------------------------------- */
.shell{
  max-width:560px;                /* телефон и узкая колонка на ПК */
  margin:0 auto;
  min-height:100vh;
  background:var(--sunk);
  border-left:1px solid var(--rule);
  border-right:1px solid var(--rule);
  display:flex; flex-direction:column;
}
/* Широкая раскладка — экран экономиста на стационарном ПК. */
.shell.wide{max-width:1180px}

.appbar{
  position:sticky; top:0; z-index:5;
  display:flex; align-items:center; justify-content:space-between; gap:8px;
  padding:10px 14px;
  padding-top:calc(10px + env(safe-area-inset-top, 0px));
  background:var(--surface); border-bottom:1px solid var(--rule);
  box-shadow:var(--shadow);   /* видно, что список уезжает ПОД шапку, а не за край */
}
.appbar .brigade{font-weight:700; font-size:15px}
.appbar .person{font-size:12px; color:var(--muted)}
/* Кто вошёл. Телефон в цеху один на бригаду: человек обязан видеть, ЧЬЮ
   работу он отмечает, не открывая ничего. Имя набрано заметнее подписи
   экрана — оно важнее того, как экран называется. */
.appbar .whoami{display:flex; flex-wrap:wrap; align-items:baseline; gap:6px; margin-top:2px}
.appbar .whoami-name{font-size:13px; font-weight:600; color:var(--ink)}
.appbar .whoami-role{
  font-size:11px; letter-spacing:.06em; text-transform:uppercase;
  color:var(--action); background:var(--action-soft);
  padding:1px 6px; border-radius:2px; white-space:nowrap;
}
.appbar-right{display:flex; align-items:center; gap:6px; flex-wrap:wrap; justify-content:flex-end}

.screen-body{
  padding:14px;
  padding-bottom:calc(24px + env(safe-area-inset-bottom, 0px));
  display:flex; flex-direction:column; gap:12px;
  flex:1;
}

/* Переключатель языка и «Выйти» в шапке. Тоже цель нажатия, а не подпись. */
.langbtn{
  display:inline-flex; align-items:center; justify-content:center;
  min-height:44px; padding:6px 12px;
  font:inherit; font-size:13px; font-weight:600;
  text-decoration:none; cursor:pointer;
  border:1px solid var(--rule); border-radius:var(--radius);
  background:var(--sunk); color:var(--ink);
}
.langbtn[aria-pressed="true"]{background:var(--action); color:var(--action-ink); border-color:var(--action)}


/* -----------------------------------------------------------------------------
   Сводка смены
   -------------------------------------------------------------------------- */
.tally{
  background:var(--surface); border:1px solid var(--rule); border-radius:var(--radius);
  padding:14px 16px;
  display:flex; align-items:baseline; justify-content:space-between; gap:12px;
}
.tally .lbl{font-size:12px; letter-spacing:.1em; text-transform:uppercase; color:var(--muted); font-weight:600}
.tally .big{font-size:44px; font-weight:900; line-height:1; letter-spacing:-.03em}


/* -----------------------------------------------------------------------------
   Карточка позиции
   -------------------------------------------------------------------------- */
.card{
  background:var(--surface); border:1px solid var(--rule); border-radius:var(--radius);
  padding:14px; display:flex; flex-direction:column; gap:10px;
}
/* Требует внимания: отказ 1С, похожая строка, превышение. */
.card.attn{border-left:3px solid var(--warn)}
.card.done{border-left:3px solid var(--done)}
.card-model{font-weight:700; font-size:17px; line-height:1.25}
.card-meta{display:flex; flex-wrap:wrap; gap:6px 12px; font-size:12px; color:var(--muted)}
.card-op{font-size:14px; font-weight:500}

/* Просмотр чужими глазами. Полоса намеренно заметная и на каждой странице:
   забыть, что смотришь не своей ролью, легко — экраны те же, а половина
   кнопок пропала, и человек решает, что сервис сломался. */
.acting{
  background:var(--warn-soft); color:var(--warn);
  border-bottom:2px solid var(--warn);
  padding:8px 14px; font-size:14px; text-align:center;
}
.acting b{color:var(--warn)}
/* Выбор роли в шапке. Рядом с языками: и то и другое меняет, ЧТО человек
   видит, а не что он делает. */
.actform{display:inline-flex; gap:4px; align-items:center}
.actform select{
  font:inherit; font-size:13px; min-height:0; height:30px; width:auto;
  padding:2px 6px; border:1px solid var(--rule); border-radius:var(--radius);
  background:var(--surface); color:var(--ink);
}

/* -----------------------------------------------------------------------------
   Разбор расхождений.

   Карточка отвечает на два вопроса сразу: что случилось и что делать.
   Второй набран заметнее подробностей — он и есть содержание экрана,
   а машинный текст «было 40, стало 25» рядом с ним справочный.
   -------------------------------------------------------------------------- */
.incident-head{
  display:flex; align-items:flex-start; justify-content:space-between; gap:10px;
}
/* Плашка возраста не сжимается: «лежит 12 сут» в две строки читается
   как два разных числа. */
.incident-head .chip{flex:none}
/* Плашка без модификатора — сегодняшнее расхождение. Заливки нет намеренно:
   залито то, что просрочено, и заливка обязана значить именно это. */
.chip:not(.ok):not(.wait):not(.attn){
  background:var(--sunk); color:var(--muted);
}
/* Что делать. Отбито линейкой слева, а не цветом: цвет здесь уже занят
   срочностью, и второй смысл на том же признаке никто не разделит. */
.todo{
  margin:0; padding-left:10px; border-left:2px solid var(--action);
  font-size:15px; line-height:1.35;
}
/* Подробности из базы: справочный текст, набранный тише основного. */
.card-note{margin:0; font-size:13px; line-height:1.35; color:var(--muted); overflow-wrap:anywhere}
.incident-form{display:flex; flex-direction:column; gap:8px}
.incident-form textarea{min-height:64px}
.incident-section{
  margin:18px 0 0; font-size:13px; letter-spacing:.08em; text-transform:uppercase;
  color:var(--muted); font-weight:700;
}
/* Счётчик в шапке. Красным не красим: это не тревога, а «столько ждёт». */
.badge{
  display:inline-block; min-width:18px; padding:0 5px; margin-left:4px;
  border-radius:9px; background:var(--action); color:var(--action-ink);
  font-size:11px; font-weight:700; line-height:18px; text-align:center;
  font-variant-numeric:tabular-nums;
}


/* -----------------------------------------------------------------------------
   Дискретный счётчик — подпись интерфейса.

   Здесь считают штуки, поэтому и показываем штуками, а не полосой прогресса.
   Разметку строит View::blocks(): до 24 единиц — плашка на изделие,
   выше — плашка на десяток (.blocks.tens).
   -------------------------------------------------------------------------- */
.count{display:flex; flex-direction:column; gap:6px}
.count-row{display:flex; align-items:baseline; justify-content:space-between; gap:10px; font-size:13px}
.count-row .left{color:var(--muted)}
.count-row .right{font-weight:700}

.blocks{display:flex; flex-wrap:wrap; gap:3px}
.blk{width:12px; height:18px; border-radius:1px; background:var(--rule)}
.blk.done{background:var(--action)}   /* сделала бригада */
.blk.mine{background:var(--warn)}     /* сделал я */
.blocks.tens .blk{width:auto; flex:1 1 0; min-width:14px}

.count-legend{display:flex; gap:14px; font-size:11px; color:var(--muted); flex-wrap:wrap}
.count-legend i{
  display:inline-block; width:9px; height:9px; border-radius:1px;
  margin-right:5px; vertical-align:baseline; background:var(--rule);
}
.count-legend i.done{background:var(--action)}
.count-legend i.mine{background:var(--warn)}


/* -----------------------------------------------------------------------------
   Ввод количества. Клавиатура не нужна: количество набирается кнопками.
   -------------------------------------------------------------------------- */
.stepper{display:flex; align-items:stretch; gap:8px}
.step{
  font:inherit; font-size:26px; font-weight:700; line-height:1;
  width:58px; min-height:56px; flex:none; cursor:pointer;
  border:1px solid var(--rule); background:var(--sunk); color:var(--ink);
  border-radius:var(--radius);
  -webkit-user-select:none; user-select:none;   /* двойное нажатие не выделяет знак */
  touch-action:manipulation;                    /* и не зумит страницу */
}
.step:active{background:var(--action-soft)}
.step[disabled]{opacity:.45; cursor:not-allowed}

/* Остальные поля ввода — крупные по той же причине. 17 px, а не 15:
   мельче 16 px мобильная Safari зумит страницу при фокусе. */
input[type="text"], input[type="number"], input[type="password"],
input[type="search"], input[type="tel"], input[type="date"], select, textarea{
  font:inherit; font-size:17px; width:100%; max-width:100%; min-height:var(--tap);
  padding:10px 12px;
  border:1px solid var(--rule); border-radius:var(--radius);
  background:var(--surface); color:var(--ink);
}
textarea{min-height:96px; line-height:1.4; resize:vertical}
/* Флажок и переключатель. 28 px, а не 22: 22 — это половина минимальной цели
   нажатия, и палец в рабочей перчатке в неё не попадает. Сама коробочка
   до 44 px не растягивается — выглядела бы нелепо, — поэтому экран, который
   их однажды заведёт, обязан обернуть поле в <label class="tapline">:
   тогда целью нажатия становится вся строка с подписью, а не квадратик.
   Селектор :has() здесь не годится — правило проекта запрещает то,
   в чём нельзя быть уверенным без запуска. */
input[type="checkbox"], input[type="radio"]{width:28px; height:28px}
.tapline{display:inline-flex; align-items:center; gap:10px; min-height:var(--tap)}

/* Поле количества. Стоит НИЖЕ общего правила и написано как `input.qty`,
   а не `.qty`, намеренно.

   `input[type="text"]` весит (0,1,1), голый класс `.qty` — (0,1,0), и общее
   правило перебивало крупное поле: 34 px превращались в 17, упор в 56 px —
   в 48. Экраны обходили это тем, что не писали `type` вовсе (на поле без
   атрибута селектор по атрибуту не срабатывает), а экран входа — подстановкой
   размера прямо в разметке. Обход держался на том, что никто никогда не
   допишет `type="text"`; допишет — и поле молча сожмётся.

   `input.qty` весит те же (0,1,1) и стоит позже, поэтому побеждает при любом
   `type` и без него. Обходы в экранах после этого не нужны, но и не мешают. */
/* Поле кода на входе. Начертание то же, что у поля количества, но класс свой:
   у `qty` есть поведение — клиентский скрипт правит его значение как число,
   и пароль в таком поле превращается в ноль. */
input.code{
  width:100%; min-height:56px;
  border:1px solid var(--rule); border-radius:var(--radius);
  background:var(--surface); color:var(--ink);
  font-family:var(--font-text); font-size:34px; font-weight:900;
  text-align:center; letter-spacing:.08em;
}

input.qty{
  flex:1; text-align:center; min-width:0; min-height:56px;
  border:1px solid var(--rule); border-radius:var(--radius);
  background:var(--surface); color:var(--ink);
  font-family:var(--font-text); font-size:34px; font-weight:900;
  font-variant-numeric:tabular-nums; padding:6px 0;
}


/* -----------------------------------------------------------------------------
   Кнопки. Ниже 44 px не опускается ни одна — иначе в цеху промахиваются.
   -------------------------------------------------------------------------- */
.btn{
  font:inherit; font-weight:700; font-size:16px; cursor:pointer;
  min-height:var(--tap); padding:13px 16px;
  border:1px solid transparent; border-radius:var(--radius);
  display:flex; align-items:center; justify-content:center; gap:8px;
  width:100%; text-align:center; text-decoration:none;
  background:var(--action); color:var(--action-ink);
  touch-action:manipulation;
}
.btn.ghost{background:var(--surface); color:var(--ink); border-color:var(--rule)}
/* Чернила токеном, а не белым насмерть: в тёмной теме плашка светлая. */
.btn.warnbtn{background:var(--warn); color:var(--warn-ink)}
.btn.donebtn{background:var(--done); color:var(--done-ink)}
.btn.small{min-height:44px; padding:8px 14px; font-size:13px; width:auto}
.btn[disabled], .btn[aria-disabled="true"]{
  opacity:.5; cursor:not-allowed;
  /* Заблокированная кнопка обязана объясняться рядом текстом: сама по себе
     она молчаливый отказ. См. правило «нет молчаливых отказов». */
}
.btn-row{display:flex; gap:8px; flex-wrap:wrap}
.btn-row .btn{width:auto; flex:1 1 40%}

/* Состояния отправки, их ставит service.js.
   Подпись на кнопке при этом меняется тоже (data-queued-text, data-done-text):
   цвет здесь второй признак, а не единственный. Чернила — токеном, иначе
   в тёмной теме единственная надпись «в очереди» становится нечитаемой,
   и остаётся ровно тот один цвет, которого быть не должно. */
form.is-queued .btn{background:var(--warn); color:var(--warn-ink); border-color:transparent}
form.is-done .btn{background:var(--done); color:var(--done-ink); border-color:transparent}


/* -----------------------------------------------------------------------------
   Двуязычная подпись.

   View::pair() даёт <span class="pair"><span>основной</span>
   <span class="hint">второй язык</span></span>. Порядок языков решает сервер:
   основной сверху, подсказка снизу.

   Разметка макета с .lang-ru/.lang-uz поддерживается тоже — в ней язык
   переключается прямо на клиенте, атрибутом data-lang на корне страницы.
   -------------------------------------------------------------------------- */
.pair{display:flex; flex-direction:column; min-width:0}
.hint{display:block; color:var(--muted); font-size:.8em; font-weight:400; line-height:1.3}
.btn .pair{align-items:center}
/* Подсказка на втором языке поверх сплошной кнопки.
   .82, а не .72: при .72 в тёмной теме получалось 4,3:1 при мелком кегле —
   ниже порога ровно у той строки, ради которой двуязычные подписи заведены.
   Приглушённость сохраняется: основную строку она по-прежнему не перебивает. */
.btn .hint{color:inherit; opacity:.82}

[data-lang="ru"] .lang-uz{color:var(--muted); font-size:.8em; font-weight:400}
[data-lang="uz"] .lang-ru{color:var(--muted); font-size:.8em; font-weight:400}

/* Разворот пары — ТОЛЬКО для разметки макета, где языки лежат в постоянном
   порядке (.lang-ru, следом .lang-uz) и порядок правит клиент.

   Раньше здесь стояло `[data-lang="uz"] .pair{flex-direction:column-reverse}`,
   и оно переворачивало ВСЕ пары, включая собранные View::pair(). А там
   порядок уже выбран сервером по языку человека: первой идёт строка на его
   языке, второй — подсказка в .hint. Разворот ставил узбекоязычному рабочему
   мелкую серую РУССКУЮ подсказку над крупной узбекской строкой — на каждой
   кнопке, на каждой подписи и на каждом отказе (View::noticePair собирает
   ту же .pair). То есть ровно наоборот тому, что обещает комментарий выше
   и ради чего двуязычные подписи вообще заведены.

   Теперь порядок меняется свойством order у самой русской строки макета,
   а не разворотом контейнера: правило физически не может задеть серверную
   разметку — класса .lang-ru в ней нет. Порядок чтения экранной озвучкой
   остаётся авторским, он идёт по DOM. */
[data-lang="uz"] .pair > .lang-ru{order:1}
/* Порядок частей здесь важен. `data-lang` ставит service.js на КОРЕНЬ страницы,
   то есть он всегда снаружи кнопки, а не внутри неё. Пока правило было записано
   как `.btn [data-lang="ru"] .lang-uz`, оно требовало элемента с data-lang
   ВНУТРИ .btn и не срабатывало никогда: подсказка второго языка на сплошной
   кнопке красилась в серый по синему и переставала читаться. */
[data-lang="ru"] .btn .lang-uz, [data-lang="uz"] .btn .lang-ru{color:inherit; opacity:.82}


/* -----------------------------------------------------------------------------
   Чипы состояния. Форма несёт смысл наравне с цветом: точка слева читается
   и в чёрно-белой печати, и при дальтонизме.
   -------------------------------------------------------------------------- */
.chip{
  display:inline-flex; align-items:center; gap:6px;
  font-size:11px; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
  padding:4px 8px; border-radius:2px; white-space:nowrap;
}
.chip.ok{background:var(--done-soft); color:var(--done)}
.chip.wait{background:var(--action-soft); color:var(--action)}
.chip.attn{background:var(--warn-soft); color:var(--warn)}
.chip i{width:6px; height:6px; border-radius:50%; background:currentColor; flex:none}


/* -----------------------------------------------------------------------------
   Сообщения. Ошибка объясняет, что случилось и что делать.
   -------------------------------------------------------------------------- */
.notice{
  border:1px solid var(--rule); border-left-width:3px; border-radius:var(--radius);
  background:var(--surface); color:var(--ink);
  padding:12px 14px; font-size:14px;
}
.notice.info{border-left-color:var(--action); background:var(--action-soft)}
.notice.ok{border-left-color:var(--done); background:var(--done-soft)}
.notice.warn, .notice.attn{border-left-color:var(--warn); background:var(--warn-soft)}

/* Полоса «нет связи». Живёт под шапкой, показывается service.js:
   пока она видна, отметки лежат на телефоне и уйдут сами.

   Намеренно НЕ прилипающая: шапка уже прилипла к верху, и вторая липкая
   полоса на том же месте наезжает на неё. О несохранённой отметке человеку
   говорит ещё и сама кнопка на карточке — этого достаточно. */
.offline{
  background:var(--warn-soft); color:var(--warn);
  font-size:12px; font-weight:600;
  padding:8px 14px; display:flex; gap:8px; align-items:center;
}


/* -----------------------------------------------------------------------------
   Пустой экран — приглашение к действию, а не тупик.
   -------------------------------------------------------------------------- */
.empty{
  border:1px dashed var(--rule); border-radius:var(--radius);
  background:var(--surface);
  padding:28px 18px; text-align:center; color:var(--ink);
}
.empty p{margin:0 0 6px; font-weight:600}
.empty-hint{margin:0; color:var(--muted); font-size:14px; font-weight:400}
.empty .btn{width:auto; margin:14px auto 0; display:inline-flex}


/* -----------------------------------------------------------------------------
   Таблица экономиста.

   .table — контейнер прокрутки, внутри обычная <table>. Прокручивается
   таблица, а не страница: уехавшая вбок страница на ПК теряет левый столбец
   с исполнителем, и строка перестаёт читаться.
   -------------------------------------------------------------------------- */
.table{overflow-x:auto; -webkit-overflow-scrolling:touch; max-width:100%}
.table table{border-collapse:collapse; width:100%; font-size:13px; min-width:640px}
.table th{
  text-align:left; font-size:11px; letter-spacing:.08em; text-transform:uppercase;
  color:var(--muted); font-weight:600; padding:10px 12px;
  border-bottom:1px solid var(--rule); white-space:nowrap;
  background:var(--surface); position:sticky; top:0; z-index:1;
}
.table td{padding:11px 12px; border-bottom:1px solid var(--rule); vertical-align:top}
.table tr:last-child td{border-bottom:none}
.table td.n, .table th.n{text-align:right; font-variant-numeric:tabular-nums; font-weight:700}
.table .mono{font-size:12px}

/* Строка, которую нельзя подписывать не глядя. */
.rowattn{background:var(--warn-soft)}
.rowattn td{border-bottom-color:var(--warn)}


/* -----------------------------------------------------------------------------
   Наклейка на партию. Лента 58 мм.

   Кодирует ключ позиции — заказ, изделие, операцию. Расценок, сумм
   и заказчика на ней нет и быть не может.
   -------------------------------------------------------------------------- */
.labels{display:flex; flex-wrap:wrap; gap:8px}
.label{
  /* Печать всегда чёрным по белому: цвет ленты и токены темы тут ни при чём. */
  background:#FFFFFF; color:#111111;
  width:54mm; min-height:26mm;
  border:1px dashed #999999; border-radius:2px;
  padding:3mm; display:flex; gap:3mm; align-items:center;
}
.label .qr{flex:none; width:24mm; height:24mm}
.label .label-txt{font-family:var(--font-text); line-height:1.25; min-width:0}
.label .m{font-weight:900; font-size:15px}      /* модель */
.label .o{font-size:12px}                       /* операция и количество */
.label .c{font-family:var(--font-mono); font-size:12px; margin-top:3px}  /* номер заказа */


/* -----------------------------------------------------------------------------
   Фокус. Видимый у всего, что можно нажать или заполнить: в цеху работают
   и с клавиатуры на стационарном ПК, и без мыши.
   -------------------------------------------------------------------------- */
a:focus-visible,
button:focus-visible,
.btn:focus-visible,
.step:focus-visible,
.langbtn:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible{
  outline:3px solid var(--action);
  outline-offset:2px;
}
/* На сплошной кнопке действия синяя обводка сливается с фоном — берём чернила. */
.btn:focus-visible{outline-color:var(--ink)}

/* Карточка, найденная по наклейке.
   Фокус на неё ставит service.js после удачного разбора кода: карточек
   на экране десяток, они одинаковые, и без обводки человек не видит,
   к какой его привела камера. Правило на :focus, а не на :focus-visible,
   намеренно — фокус здесь ставит программа после касания, а такой фокус
   браузер «видимым» считает не всегда. По Tab карточка не ловится:
   tabindex у неё -1. */
.card[tabindex]:focus{outline:3px solid var(--action); outline-offset:2px}


/* -----------------------------------------------------------------------------
   Движение. Кому оно мешает — тому его нет.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion:reduce){
  *{animation:none !important; transition:none !important; scroll-behavior:auto !important}
}


/* -----------------------------------------------------------------------------
   Печать: лист наклеек и ничего лишнего.

   На бумагу уходят только наклейки. Шапка, кнопки, полосы состояния
   и счётчики к принтеру этикеток отношения не имеют — и печатать их
   значит переводить ленту.
   -------------------------------------------------------------------------- */
@media print{
  @page{margin:3mm}

  body{background:#FFFFFF; color:#000000}
  .shell, .shell.wide{
    max-width:none; min-height:0; background:#FFFFFF; border:none; display:block;
  }
  .screen-body{padding:0; gap:0; display:block}

  .appbar, .appbar-right, .langbtn,
  .btn, .btn-row, .stepper, .step, .qty,
  .notice, .offline, .tally, .count, .count-legend, .blocks, .chip,
  .empty, .noprint{
    display:none !important;
  }

  .labels{display:block}
  .label{
    border:none; margin:0 0 2mm;
    break-inside:avoid; page-break-inside:avoid;
  }
}
