/* pwa/static/pwa/app.css
   Дизайн-система: docs/DESIGN_SYSTEM.md (Industrial Swiss).
   Токены — static/marketing/tokens.css, подключается ПЕРЕД этим файлом.
   Сырые hex здесь запрещены (§8.7): только роли. */

/* ── Мост к Bootstrap ────────────────────────────────────────
   BS 5.3 целиком на кастом-проперти → роли подставляются в его же
   переменные, а не переопределяются селекторами. Компоненты не трогаем. */
:root {
  --bs-body-font-family: var(--font-sans);
  --bs-body-font-size:   var(--t-sm);
  --bs-body-color:       var(--text);
  --bs-body-bg:          var(--bg-sunken);
  --bs-border-color:     var(--border);
  --bs-border-radius:    var(--r-md);
  --bs-border-radius-sm: var(--r-sm);
  --bs-border-radius-lg: var(--r-lg);
  --bs-border-radius-xl: var(--r-lg);
  --bs-secondary-color:  var(--text-muted);
  --bs-link-color:       var(--brand-ink);   /* бренд буквами (§11.7.17) */
  --bs-link-hover-color: var(--p-600);
  --bs-primary:          var(--p-500);
  --bs-primary-rgb:      31, 80, 117;    /* зеркало --p-500 для .bg-/.text- */
  --bs-danger-rgb:       138, 30, 20;    /* зеркало --stop-ink (§8.8) */
  --bs-success-rgb:      20, 86, 53;     /* зеркало --ok-ink */
}

.btn { --bs-btn-font-weight: var(--fw-semibold); }

.btn-primary {
  --bs-btn-bg: var(--p-500);           --bs-btn-border-color: var(--p-500);
  --bs-btn-hover-bg: var(--p-600);     --bs-btn-hover-border-color: var(--p-600);
  --bs-btn-active-bg: var(--p-700);    --bs-btn-active-border-color: var(--p-700);
  --bs-btn-disabled-bg: var(--p-300);  --bs-btn-disabled-border-color: var(--p-300);
}
.btn-success {
  --bs-btn-bg: var(--ok);              --bs-btn-border-color: var(--ok);
  --bs-btn-hover-bg: var(--ok-ink);    --bs-btn-hover-border-color: var(--ok-ink);
  --bs-btn-active-bg: var(--ok-ink);   --bs-btn-active-border-color: var(--ok-ink);
}
.btn-danger {
  --bs-btn-bg: var(--stop);            --bs-btn-border-color: var(--stop);
  --bs-btn-hover-bg: var(--stop-ink);  --bs-btn-hover-border-color: var(--stop-ink);
  --bs-btn-active-bg: var(--stop-ink); --bs-btn-active-border-color: var(--stop-ink);
}
.btn-outline-secondary {
  --bs-btn-color: var(--text);              --bs-btn-border-color: var(--border-strong);
  --bs-btn-hover-color: var(--text);        --bs-btn-hover-bg: var(--bg-sunken);
  --bs-btn-hover-border-color: var(--border-strong);
  --bs-btn-active-color: var(--text);       --bs-btn-active-bg: var(--n-100);
  --bs-btn-active-border-color: var(--border-strong);
}

/* Фокус — двойное кольцо (§5), одинаково на светлом и на цветном. */
.btn:focus-visible,
.form-control:focus,
.form-select:focus,
.form-check-input:focus,
.filter-chip:focus-visible,
.transition-btn:focus-visible {
  border-color: var(--p-400);
  box-shadow: var(--sh-focus);
  outline: none;
}
.form-check-input:checked { background-color: var(--p-500); border-color: var(--p-500); }

/* TomSelect (bootstrap5-тема) — та же подстановка. */
.ts-control { border-radius: var(--r-md); border-color: var(--border); }
.ts-wrapper.focus .ts-control { border-color: var(--p-400); box-shadow: var(--sh-focus); }
.ts-dropdown { border-radius: var(--r-md); border-color: var(--border); box-shadow: var(--sh-pop); }
.ts-dropdown .active { background: var(--p-50); color: var(--text); }

/* ── Pull-to-refresh indicator ───────────────────────────── */
.ptr-indicator {
    position: fixed;
    top: 0;
    left: 50%;
    transform: translate(-50%, -50px);
    width: 40px;
    height: 40px;
    border-radius: var(--r-md);
    background: var(--surface);
    border: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--brand-ink);
    font-size: 1.2rem;
    z-index: 10000;
    opacity: 0;
    pointer-events: none;
    transition: transform var(--d-base) var(--e-out), opacity var(--d-base) var(--e-out);
}
.ptr-indicator--refreshing .bi {
    animation: ptr-spin 0.8s linear infinite;
}
@keyframes ptr-spin {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

/* ── Переменные ──────────────────────────────────────────── */
:root {
  --pwa-nav-h: 60px;
  --pwa-header-h: 56px;
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  /* Скрим листа (§12): 40-60% чёрного. Не роль — затемнение, одинаковое
     в обеих темах: в светлой отделяет лист от листа, в тёмной от стекла. */
  --pwa-scrim: rgba(13, 16, 14, .55);
}

/* ── Основной layout ─────────────────────────────────────── */
html {
  background: var(--surface); /* заполняет safe-area под nav при любом смещении */
}

body {
  font-size: var(--t-sm);
  background: var(--bg-sunken);
  color: var(--text);
  /* Без min-height короткий экран кончается вместе с контентом, и ниже видно
     фон html (--surface): полоса другого тона поперёк страницы. */
  min-height: 100dvh;
  padding-bottom: calc(var(--pwa-nav-h) + var(--safe-bottom));
}

.pwa-header {
  position: sticky;
  top: 0;
  z-index: 100;
  min-height: calc(var(--pwa-header-h) + var(--safe-top));
  padding-top: var(--safe-top);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  /* Ландшафт: чёлка уходит вбок, поля обязаны её обойти (§12). */
  padding-left: calc(1rem + var(--safe-left));
  padding-right: calc(1rem + var(--safe-right));
  gap: .5rem;
}

/* Иконочная кнопка топбара (тема, назад): цель 44px без рамки. */
.pwa-header__btn {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  border-radius: var(--r-sm);
  background: none;
  color: var(--text-muted);
  font-size: 1.15rem;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--d-fast) var(--e-out);
}

/* §12: отклик заливкой, не сдвигом — под пальцем сдвиг не виден. */
.pwa-header__btn:active { background: var(--n-100); }

.pwa-header__title {
  font-size: var(--t-h3);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tr-head);
  line-height: var(--lh-snug);
  flex: 1;
  min-width: 0;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Вторая строка шапки — контекст экрана показаниями: счётчик, фильтр, № наряда.
   Mono uppercase (§3): это метка прибора, а не подзаголовок статьи. */
.pwa-header__titles { flex: 1; min-width: 0; }
.pwa-header__titles .pwa-header__title { flex: none; }

.pwa-header__sub {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--t-label);
  font-weight: var(--fw-medium);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  margin-top: 1px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pwa-content {
  padding: .75rem;
  padding-left: calc(.75rem + var(--safe-left));
  padding-right: calc(.75rem + var(--safe-right));
  max-width: 640px;
  margin: 0 auto;
}

/* ── Глава внутри карточки ────────────────────────────────
   Было: подпись поля (.wo-detail-label) в роли заголовка раздела — блок
   читался как ещё одна строка данных. Глава = mono-метка + линия под ней:
   тот же приём, что шапка таблицы, и та же волосяная линия (§5, §8.10). */
.pwa-block-title {
  display: flex;
  align-items: center;   /* в главе бывает кнопка («Добавить фото»), не только текст */
  justify-content: space-between;
  min-height: 1.5rem;
  gap: .5rem;
  font-family: var(--font-mono);
  font-size: var(--t-label);
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--text-muted);
  padding-bottom: .5rem;
  margin-bottom: .75rem;
  border-bottom: 1px solid var(--border);
}

/* Счётчик справа в главе — показание, не проза. */
.pwa-block-title__count {
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--tr-label);
}

/* ── Bottom nav ──────────────────────────────────────────────
   Sticky-слой над полосой → §5 разрешает тень, §11.5 разрешает стекло. */
.pwa-nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: calc(var(--pwa-nav-h) + var(--safe-bottom));
  padding-bottom: var(--safe-bottom);
  padding-left: var(--safe-left);
  padding-right: var(--safe-right);
  background: var(--surface);
  border-top: 1px solid var(--border);
  box-shadow: var(--sh-raise);
  display: flex;
  z-index: 100;
  will-change: transform; /* GPU layer без поломки bottom: 0 на iOS */
}

.pwa-nav__item {
  flex: 1;
  min-width: 0;
  min-height: 44px;          /* §12: цель нажатия */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  font-family: var(--font-mono);
  font-size: var(--t-label);  /* 11px — ниже §3 не опускаем */
  font-weight: var(--fw-medium);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--text-muted);
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--d-fast) var(--e-out);
}

/* Подпись не ужимаем трекингом — обрезаем, слово подбирается короче. */
.pwa-nav__label {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pwa-nav__item:active { background: var(--n-100); }

/* Активный — буквами, не заливкой (§11.7.17 + план 0.2). */
.pwa-nav__item.active {
  color: var(--brand-ink);
  box-shadow: inset 0 2px 0 var(--brand-ink);
}

.pwa-nav__item svg { width: 24px; height: 24px; }
.pwa-nav__item .bi { font-size: 1.25rem; line-height: 1; }

/* «Ещё»: шестой раздел в панель не влезает, уходит в нативный details.
   Свой JS не нужен — Esc и клавиатура работают из коробки. */
.pwa-nav__more { flex: 1; min-width: 0; position: relative; }
.pwa-nav__more > summary { width: 100%; height: 100%; list-style: none; cursor: pointer; }
.pwa-nav__more > summary::-webkit-details-marker { display: none; }

.pwa-nav__sheet {
  position: absolute;
  bottom: 100%;
  right: 0;
  min-width: 11rem;
  margin: 0 0 1px;
  padding: 0;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--sh-pop);   /* слой над полосой → §5 тень разрешена */
}

.pwa-nav__sheet-item {
  display: flex;
  align-items: center;
  gap: .625rem;                /* ≥8px между иконкой и словом */
  min-height: 44px;
  padding: 0 1rem;
  color: var(--text);
  text-decoration: none;
  font-size: var(--t-body);
}

.pwa-nav__sheet li + li .pwa-nav__sheet-item { border-top: 1px solid var(--border-faint); }
.pwa-nav__sheet-item:active { background: var(--bg-sunken); }
.pwa-nav__sheet-item.active { color: var(--brand-ink); }
.pwa-nav__sheet-item .bi { font-size: 1.125rem; }

/* Раздел закрыт правами — виден, но мёртвый: ни нажатия, ни подсветки */
.pwa-nav__item--denied { color: var(--text-faint); }
.pwa-nav__item--denied:active { background: none; }
.pwa-nav__lock { margin-left: auto; font-size: .875rem; }

/* ── Реестр ───────────────────────────────────────────────
   Было: 20 оторванных коробок, каждая на своей волосяной рамке. Шаг
   surface↔bg-sunken — 3% светлоты, на телефоне в цеху его нет вовсе, и
   рамки в 1px не спасают: экран читается одним полем текста.
   Стало: одна плоскость с волосяными разделителями — один сильный кант
   вместо двадцати слабых. Идиома реестра из §8.10, а не стопка карточек. */
.wo-list {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;   /* планка статуса не вылезает за скруглённый угол */
}

.wo-list .wo-card {
  margin: 0;
  border-width: 0 0 1px 3px;
  border-bottom-color: var(--border-faint);   /* внутри панели — волосяная */
  border-radius: 0;
}

.wo-list .wo-card:last-child { border-bottom-width: 0; }

/* Догруженная страница — продолжение того же реестра, а не второй ящик:
   шов схлопывается, скругление остаётся только на внешних углах. */
.wo-list + .wo-list {
  margin-top: -1px;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

.wo-list--loading .wo-card { border-left-color: var(--border-faint); }

/* ── Карточка заявки ──────────────────────────────────────
   §8.4: карточки без теней. Иерархия — линия и фон.
   Сигнальная планка слева: статус — то, ради чего реестр сканируют, и он
   обязан читаться до чтения. Цвет берётся из --chip-signal, который уже
   ставят классы .status-* / .status-chip--*, — своей карты статусов нет. */
.wo-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--chip-signal, var(--border-strong));
  border-radius: var(--r-md);
  padding: .875rem 1rem;
  margin-bottom: .5rem;
  display: block;
  text-decoration: none;
  color: inherit;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--d-fast) var(--e-out);
}

/* §12.3: отклик заливкой — под пальцем сдвиг не виден. */
.wo-card:active {
  background: var(--bg-sunken);
}

.wo-card__head {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  margin-bottom: .25rem;
}

.wo-card__name {
  flex: 1;
  min-width: 0;
  font-weight: var(--fw-semibold);
  font-size: var(--t-body);   /* 17px: единственная крупная строка карточки */
  line-height: var(--lh-snug);
  letter-spacing: var(--tr-head);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* № наряда — показание (§8.9): mono, tabular, не проза. */
.wo-card__id {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-label);
  letter-spacing: var(--tr-label);
  color: var(--text-faint);
}

.wo-card__meta {
  font-size: var(--t-xs);
  color: var(--text-muted);
  display: flex;
  gap: .75rem;
  flex-wrap: wrap;
  line-height: var(--lh-flat);
}

.wo-card__meta > span {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  min-width: 0;
}

.wo-card__meta .bi { color: var(--text-faint); font-size: .9em; }

/* Дата — показание, а не проза (§8.9, §8.11). */
.wo-card__date {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-xs);
  color: var(--text-faint);
}

.wo-card__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: .5rem;
}

/* ── Статусы: тон по ключу ────────────────────────────────
   Форма чипа — общая (static/css/components.css, §8.14). Здесь только
   карта «ключ статуса → тон»: разметка и JS строят класс из самого ключа
   (`status-${o.status}`), поэтому перекладывать маппинг в шаблоны значит
   писать его в восьми местах вместо одного. Значения — те же --chip-*,
   что у .status-chip--ok|warn|stop|info. */
.status-new         { --chip-signal: var(--info); --chip-bg: var(--info-bg); --chip-ink: var(--info-ink); }
.status-in_progress { --chip-signal: var(--warn); --chip-bg: var(--warn-bg); --chip-ink: var(--warn-ink); }
.status-done        { --chip-signal: var(--ok);   --chip-bg: var(--ok-bg);   --chip-ink: var(--ok-ink); }
.status-failed      { --chip-signal: var(--stop); --chip-bg: var(--stop-bg); --chip-ink: var(--stop-ink); }
.status-canceled    { --chip-signal: var(--n-400); --chip-bg: var(--n-100); --chip-ink: var(--n-600); }

/* ── Filter chips ────────────────────────────────────────── */
.filter-chips {
  display: flex;
  gap: .5rem;                  /* §12.1: зазор между целями */
  overflow-x: auto;
  padding: 0 0 .75rem;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.filter-chips::-webkit-scrollbar { display: none; }

/* Высота 44 — это §12.1, а не вкус: прежние 32px стояли с
   `min-height: 0 !important`, то есть правило цели нажатия было снято явно. */
.filter-chip {
  flex: 0 0 auto;               /* иначе flex ужимает чип и длинная подпись вылезает наружу */
  white-space: nowrap;
  padding: 0 1rem;
  border-radius: var(--r-md);   /* §8.14: пилюли нет */
  border: 1px solid var(--border);
  background: var(--surface);
  font-size: var(--t-xs);
  font-weight: var(--fw-medium);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  color: var(--text-muted);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 44px;
  min-width: 4.5rem;
  line-height: 1;
  transition: background var(--d-fast) var(--e-out), border-color var(--d-fast) var(--e-out),
              color var(--d-fast) var(--e-out);
}

.filter-chip:active { background: var(--bg-sunken); }

.filter-chip.active {
  border-color: var(--p-500);
  background: var(--p-500);
  color: var(--ink-on-brand);
  font-weight: var(--fw-semibold);
}

/* ── Поиск ────────────────────────────────────────────────
   Голая пара .form-control + .btn читалась как неоформленный HTML.
   Иконка врезана в поле, кнопка — квадрат 44 рядом. */
.pwa-search {
  display: flex;
  gap: .5rem;
  margin-bottom: .75rem;
}

.pwa-search__field { position: relative; flex: 1; min-width: 0; }

.pwa-search__field .bi {
  position: absolute;
  left: .75rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-faint);
  pointer-events: none;
  line-height: 1;
}

.pwa-search__field input { width: 100%; padding-left: 2.375rem; }

.pwa-search__btn {
  flex: 0 0 44px;
  width: 44px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* ── Панель действий страницы ─────────────────────────────
   Главное действие экрана («Запустить», «Создать наряд») жило в конце
   прокрутки и находилось только скроллом. Слой висит НАД полосой → §5
   разрешает тень; безопасные зоны и высота нижней навигации учтены (§12.5). */
.pwa-actionbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(var(--pwa-nav-h) + var(--safe-bottom));
  z-index: 90;
  display: flex;
  gap: .5rem;
  padding: .5rem;
  padding-left: calc(.75rem + var(--safe-left));
  padding-right: calc(.75rem + var(--safe-right));
  background: var(--surface);
  border-top: 1px solid var(--border);
  box-shadow: var(--sh-raise);
}

.pwa-actionbar > * { flex: 1; margin: 0; }

/* Место под панель, иначе она перекрывает хвост списка (§12.5). */
.pwa-actionbar-gap { height: 60px; }

/* ── Bottom sheet ────────────────────────────────────────── */
.bs-overlay {
  position: fixed;
  inset: 0;
  background: var(--pwa-scrim);
  z-index: 500;
  display: flex;
  align-items: flex-end;
}

.bs-sheet {
  width: 100%;
  background: var(--surface);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  border-top: 2px solid var(--p-500);
  box-shadow: var(--sh-pop);   /* слой над полосой — тень разрешена (§5) */
  padding: 1rem 1rem calc(1.5rem + var(--safe-bottom));
  padding-left: calc(1rem + var(--safe-left));
  padding-right: calc(1rem + var(--safe-right));
  max-height: 90vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

/* Свайп вниз не виден и не доступен с клавиатуры — нужен видимый выход (§12). */
.bs-close {
  position: absolute;
  top: .25rem;
  right: .25rem;
  width: 44px;
  height: 44px;
  min-height: unset;
  padding: 0;
  border: none;
  border-radius: var(--r-sm);
  background: none;
  color: var(--text-muted);
  font-size: 1.15rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.bs-close:active { background: var(--n-100); }
.bs-sheet__head { position: relative; }

.bs-handle {
  width: 36px;
  height: 4px;
  background: var(--border-strong);
  border-radius: var(--r-xs);
  margin: 0 auto .75rem;
}

.bs-title {
  font-size: var(--t-body);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tr-head);
  margin-bottom: 1rem;
  text-align: center;
}

.bs-title--stop { color: var(--stop-ink); }
.bs-title--ok   { color: var(--ok-ink); }

/* ── Список переходов статуса ────────────────────────────── */
.transition-btn {
  display: flex;
  align-items: center;
  width: 100%;
  padding: .875rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg-sunken);
  color: var(--text);
  font-size: var(--t-sm);
  font-weight: var(--fw-medium);
  margin-bottom: .5rem;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  gap: .75rem;
  transition: background var(--d-fast) var(--e-out);
}

.transition-btn:active { background: var(--n-100); }

/* ── Детали заявки ───────────────────────────────────────── */
.wo-detail-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 1rem;
  margin-bottom: .75rem;
}

.wo-detail-field {
  display: flex;
  flex-direction: column;
  padding: .5rem 0;
  border-bottom: 1px solid var(--border-faint);
}

.wo-detail-field:last-child { border-bottom: none; }

.wo-detail-label {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  color: var(--text-faint);
  font-weight: var(--fw-medium);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  margin-bottom: .2rem;
}

.wo-detail-value {
  font-size: var(--t-sm);
  font-weight: var(--fw-medium);
}

/* ── Touch targets (§12) ─────────────────────────────────── */
button, a, .filter-chip {
  min-height: 44px;
}

/* Поле ниже 16px → iOS Safari зумит вьюпорт на фокусе и экран уезжает.
   Это не про читаемость, а про то, что форма перестаёт работать (§12). */
.pwa-content input,
.pwa-content select,
.pwa-content textarea,
.bs-sheet input,
.bs-sheet select,
.bs-sheet textarea,
.login-card input,
.ts-control input {
  font-size: 16px;
  min-height: 44px;
}
.pwa-content textarea, .bs-sheet textarea { min-height: 88px; }

/* ── Form section в bottom sheet ────────────────────────── */
.bs-section {
  margin-bottom: 1rem;
}

.bs-section-title {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--text-muted);
  margin-bottom: .5rem;
}

/* ── Кнопка «добавить фото» ──────────────────────────────── */
.photo-add-btn {
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 6px 12px;
  border: 1px solid var(--brand-ink);
  border-radius: var(--r-md);
  color: var(--brand-ink);
  font-size: var(--t-xs);
}

/* Сетка фото — один класс на 4 места (галерея + 2 формы). */
.photo-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: .5rem;
}

.photo-grid__item { position: relative; }

/* Кнопка удаления (форма в галерее / голая кнопка в превью формы). */
.photo-grid__item > form,
.photo-grid__item > .photo-remove-btn {
  position: absolute;
  top: 4px;
  right: 4px;
}

.photo-thumb {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: var(--r-md);
  border: 1px solid var(--border);
}

.photo-remove-btn {
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  border-radius: var(--r-sm);
  background: var(--pwa-scrim);
  color: var(--n-0);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: unset;
}

/* ── Чек-лист ────────────────────────────────────────────── */
.pwa-checklist-item {
  min-height: 44px;
  padding: .5rem 0;
  border-bottom: 1px solid var(--border-faint);
}

.pwa-checklist-item:last-child { border-bottom: none; }

.pwa-checklist-cb {
  width: 22px;
  height: 22px;
  accent-color: var(--p-500);
  cursor: pointer;
}

.pwa-checklist-label {
  font-size: var(--t-sm);
  line-height: var(--lh-body);
}

/* Удаление пункта: подложка сигнала, текст из -ink (§8.8). */
.pwa-checklist-remove {
  width: 44px;
  height: 44px;
  min-height: unset;
  padding: 0;
  flex-shrink: 0;
  border: 1px solid var(--stop-bg);
  border-radius: var(--r-md);
  background: var(--stop-bg);
  color: var(--stop-ink);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ── QR-сканер ───────────────────────────────────────────── */
/* --n-950 прибит в обеих темах (§11.7.18) — здесь это и нужно:
   подложка видеопотока чёрная всегда, а не «край градиента». */
.qr-video {
  width: 100%;
  display: block;
  border-radius: var(--r-md);
  background: var(--n-950);
}

.qr-stage {
  position: relative;
  margin-bottom: .75rem;
}

/* Прицел: линия и кант. Ни свечения, ни blur поверх потока (§11.7.19). */
.qr-frame {
  position: absolute;
  inset: 15% 15%;
  border: 2px solid var(--n-0);
  border-radius: var(--r-sm);
  pointer-events: none;
}

.qr-hint {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: var(--t-xs);
  color: var(--text-muted);
  margin-bottom: .75rem;
}

.qr-hint--fail { color: var(--warn-ink); }

/* Индикатор поиска ступенчатый (§6.3): дискретное читается как измеренное,
   плавное — как декорация. Длительность из токена: prefers-reduced-motion
   гасит её на уровне токенов, свой захардкоженный ms этот предохранитель обходит. */
.qr-progress {
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  border-radius: var(--r-xs);
  background: var(--brand-ink);
  animation: qr-pulse calc(var(--d-slow) * 4) var(--e-step) infinite;
}

@keyframes qr-pulse {
  from { opacity: .25; }
  to   { opacity: 1; }
}

/* ── Строка списка внутри карточки ───────────────────────── */
.pwa-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  min-height: 44px;
  padding: .5rem 0;
  border-bottom: 1px solid var(--border-faint);
  color: inherit;
  text-decoration: none;
  font-size: var(--t-sm);
}

.pwa-row:last-child { border-bottom: none; }
.pwa-row:active { background: var(--bg-sunken); }

.pwa-row__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pwa-row__none {
  font-size: var(--t-xs);
  color: var(--text-faint);
  margin: .5rem 0 0;
}

/* ── Экран входа ─────────────────────────────────────────── */
.login-body {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: 1.5rem;
  padding-top: calc(1.5rem + var(--safe-top));
  background: var(--bg-sunken);
}

/* §8.4: без тени — граница и фон. */
.login-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  border-top: 2px solid var(--p-500);
  padding: 2rem 1.5rem;
  width: 100%;
  max-width: 360px;
}

.login-logo {
  display: block;
  width: 160px;
  height: auto;
  margin: 0 auto .75rem;
}

/* Подпись под логотипом — mono-метка зоны, как табличка на щите (§3). */
.login-sub {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  font-weight: var(--fw-medium);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--text-faint);
  text-align: center;
  margin: 0 0 1.75rem;
}

/* ── Подписи полей ────────────────────────────────────────
   Одна форма подписи на всю зону: та же mono-метка, что у .wo-detail-label,
   иначе форма выглядит чужой рядом с карточкой данных (§3, §9.9). */
.pwa-content .form-label,
.bs-sheet .form-label,
.login-card .form-label {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  font-weight: var(--fw-medium);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--text-muted);
  margin-bottom: .25rem;
}

/* ── iOS install hint ────────────────────────────────────── */
.ios-hint {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  background: var(--p-500);
  color: var(--n-0);
  font-size: var(--t-xs);
  padding: .6rem 1rem;
  text-align: center;
  padding-top: calc(.6rem + var(--safe-top));
}

.ios-hint__close {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  min-height: unset;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  color: var(--n-0);
  font-size: .95rem;
  line-height: 1;
  padding: 0;
  cursor: pointer;
}

/* ── HTMX loading indicator ──────────────────────────────── */
.htmx-indicator {
  display: none;
}
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  display: inline-block;
}

/* ── Таблица показаний ────────────────────────────────────
   §8.10: зебры нет, строки делит волосяная линия. Числа — .cell-num. */
.pwa-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-sm);
  margin-top: .5rem;
}

.pwa-table td,
.pwa-table th {
  padding: .5rem .25rem;
  border-bottom: 1px solid var(--border-faint);
  vertical-align: middle;
}

.pwa-table tr:last-child td { border-bottom: none; }

.pwa-table th {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  font-weight: var(--fw-medium);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--text-faint);
  text-align: left;
}

.pwa-table__unit {
  width: 3.5rem;
  color: var(--text-faint);
  font-size: var(--t-xs);
}

/* Справочный остаток под названием материала — не цифра расхода. */
.pwa-table__stock,
.ts-stock {
  display: block;
  font-size: var(--t-label);
  color: var(--text-faint);
}

/* Поле количества: mono, вправо, 44px и 16px (§8.11 + §12). */
.pwa-qty {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  text-align: right;
  width: 5.5rem;
  min-width: 5.5rem;
}

/* ── Плитки сводки ────────────────────────────────────────
   §8.4: граница и фон, без тени. Сигнал — только полоса слева, число
   всегда текстом из --text (§8.8): цифра не красится в сигнальный цвет. */
.tile-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: .5rem;
}

.tile {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: .5rem;
  min-height: 96px;
  padding: .875rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 4px solid var(--tile-signal, var(--border-strong));
  border-radius: var(--r-md);
  color: inherit;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--d-fast) var(--e-out);
}

.tile:active { background: var(--bg-sunken); }

.tile__value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  /* Плитка сводки — единственное место, где цифра работает массой. Кегль
     фиксированный: clamp-шкала лендинга здесь дала бы разнобой между
     плитками при переносе подписи. §1.10 плана: вес 900 в app-зоне нет. */
  font-size: 2rem;
  font-weight: var(--fw-bold);
  letter-spacing: var(--tr-head);
  line-height: 1;
  color: var(--text);
}

/* Подпись — метка шкалы, а не проза (§3, §8.9). */
.tile__label {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  font-weight: var(--fw-medium);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  line-height: var(--lh-flat);
  color: var(--text-muted);
}

.tile--stop { --tile-signal: var(--stop); }
.tile--warn { --tile-signal: var(--warn); }
.tile--ok   { --tile-signal: var(--ok); }
.tile--info { --tile-signal: var(--info); }

/* Ноль — хорошая новость: сигнал снят, число приглушено. Красный ноль
   читается тревогой и обесценивает сигнал на соседних плитках. */
.tile--muted { --tile-signal: var(--border-faint); }
.tile--muted .tile__value { color: var(--text-faint); }

/* ── Календарь ────────────────────────────────────────────
   Своя сетка, а не FullCalendar: тот несёт палитру --fc-* и в тёмной теме
   остаётся белым (§11.8). Здесь всё на ролях, значит тема бесплатна. */
.cal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  margin-bottom: .5rem;
}

.cal-title {
  font-size: var(--t-body);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tr-head);
  text-transform: capitalize;
}

.cal {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
  margin-bottom: .75rem;
}

.cal th {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  font-weight: var(--fw-medium);
  text-transform: uppercase;
  letter-spacing: var(--tr-label);
  color: var(--text-faint);
  padding-bottom: .35rem;
}

.cal-day {
  position: relative;
  height: 44px;              /* §12: клетка — тач-цель */
  text-align: center;
  vertical-align: middle;
  border: 1px solid var(--border-faint);
  background: var(--surface);
}

.cal-day__num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-sm);
}

/* Нерабочий день — утоплен, а не покрашен: цвет здесь не событие (§8.2). */
.cal-day--off {
  background: var(--bg-sunken);
  color: var(--text-faint);
}

.cal-day--blank {
  background: none;
  border-color: transparent;
}

.cal-day--today .cal-day__num {
  color: var(--brand-ink);
  font-weight: var(--fw-bold);
}

/* Наряды дня — счётчик, не заливка клетки: заливка спорит с «нерабочий день». */
.cal-day__count {
  position: absolute;
  right: 2px;
  bottom: 1px;
  font-family: var(--font-mono);
  font-size: var(--t-label);
  line-height: 1;
  color: var(--info-ink);
  background: var(--info-bg);
  border-radius: var(--r-xs);
  padding: 1px 3px;
}

/* ── Пустое состояние и экран ошибки ─────────────────────── */
.pwa-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .5rem;
  padding: 2.5rem 1rem;
  text-align: center;
  color: var(--text-muted);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}

/* Иконка в рамке-квадрате: пустое состояние — тоже плоскость системы,
   а не серый знак посреди листа. Ни тени, ни свечения (§8.4). */
.pwa-empty .bi {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  font-size: 1.5rem;
  color: var(--text-faint);
  line-height: 1;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg-sunken);
  margin-bottom: .25rem;
}

/* ── Скелетон ─────────────────────────────────────────────
   Спиннер Bootstrap на пустом экране — «страница ещё не сделана».
   Ступенчатая пульсация (§6.3): дискретное читается как измеренное. */
.pwa-skeleton {
  display: block;
  height: .875rem;
  border-radius: var(--r-sm);
  background: var(--border-faint);
  animation: pwa-skel calc(var(--d-slow) * 3) var(--e-step) infinite alternate;
}

.pwa-skeleton + .pwa-skeleton { margin-top: .625rem; }
.pwa-skeleton--wide { width: 70%; }
.pwa-skeleton--narrow { width: 40%; }
.pwa-skeleton--tall { height: 1.5rem; }

@keyframes pwa-skel {
  from { opacity: .45; }
  to   { opacity: 1; }
}

.pwa-empty__title {
  font-size: var(--t-body);
  font-weight: var(--fw-semibold);
  color: var(--text);
  margin: 0;
}

.pwa-empty__hint {
  font-size: var(--t-xs);
  margin: 0 0 .5rem;
  max-width: 34ch;
}

/* Офлайн: гасим только то, что помечено — очередь тянет всё остальное сама. */
body.is-offline [data-needs-network] {
  opacity: .45;
  pointer-events: none;
}

/* Offline banner — сигнальная полоса слева, текст из -ink (§8.8) */
.offline-banner {
  display: none;
  align-items: center;
  justify-content: center;
  background: var(--warn-bg);
  color: var(--warn-ink);
  border-bottom: 1px solid var(--warn);
  font-size: var(--t-xs);
  padding: 0.4rem 1rem;
  text-align: center;
}

/* Toast notification */
.pwa-toast {
  position: fixed;
  bottom: calc(env(safe-area-inset-bottom) + 72px);
  left: 1rem;
  right: 1rem;
  padding: 0.6rem 1.2rem;
  border-left: 4px solid;
  border-radius: 0 var(--r-md) var(--r-md) 0;
  font-size: var(--t-xs);
  font-weight: var(--fw-medium);
  z-index: 9999;
  text-align: center;
  pointer-events: none;
  box-shadow: var(--sh-pop);
  animation: pwa-toast-in var(--d-base) var(--e-out);
}
.pwa-toast--warning { background: var(--warn-bg); color: var(--warn-ink); border-color: var(--warn); }
.pwa-toast--success { background: var(--ok-bg);   color: var(--ok-ink);   border-color: var(--ok); }
.pwa-toast--info    { background: var(--info-bg); color: var(--info-ink); border-color: var(--info); }

@keyframes pwa-toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ══ Тёмная тема ═══════════════════════════════════════════
   Лестница --n-* и роли переопределены в tokens.css — здесь только то,
   что лестницей не чинится.

   1. Тройки --bs-*-rgb дублируют ступени ЧИСЛАМИ: подставить в них роль
      нельзя, значит светлые числа поедут в тёмную как есть. Те же грабли,
      что §11.9.1 в кабинете.
   2. Стекло — только нижней навигации и листу: оба висят НАД полосой
      (§11.5). Список нарядов, карточка, таблица остаются плоскостями.
   ═════════════════════════════════════════════════════════ */
[data-theme="dark"] {
  --bs-primary-rgb: 42, 115, 196;    /* зеркало тёмного --p-500 #2A73C4 */
  --bs-danger-rgb:  241, 154, 143;   /* зеркало --stop-ink #F19A8F */
  --bs-success-rgb: 102, 211, 163;   /* зеркало --ok-ink #66D3A3 */
}

/* Логотип — готовый reverse, не фильтр по тёмно-синему (§11.8). */
[data-theme="dark"] .login-logo {
  content: url("../marketing/images/brand/logo-reverse.e2d9ba4a3c33.svg");
}

@supports (backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px)) {
  [data-theme="dark"] .pwa-nav,
  [data-theme="dark"] .bs-sheet {
    background: var(--glass-bg);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    box-shadow: var(--glass-edge), var(--sh-raise);
  }
  [data-theme="dark"] .bs-sheet { box-shadow: var(--glass-edge), var(--sh-pop); }

  /* Предохранитель ставится на СНЯТИЕ (§11.7.21): reduce, не no-preference —
     иначе там, где браузер фичу не знает, стекло тихо исчезает у всех. */
  @media (prefers-reduced-transparency: reduce) {
    [data-theme="dark"] .pwa-nav,
    [data-theme="dark"] .bs-sheet {
      background: var(--surface);
      -webkit-backdrop-filter: none;
      backdrop-filter: none;
    }
  }
}
