/* ============================================================================
   Экран «Заказы» — светлая тема «Стекло» (12.09.2026).
   ----------------------------------------------------------------------------
   Экран перенесён с макета `design/pult-glass-2026-09/orders.html`. Собран из
   ОБЩИХ деталей «Стекла», которые уже живут в `app.css` и рисуются из
   `overview.js`: градиентная карточка `.gcard` с бликом и «дымками», цифра из
   точечной матрицы `.dotnum`, спарклайн с пунктирной базой и двумя маркерами
   `.ovspark`, стеклянный чип `.gchip`, пилюля-лейбл `.eyebrow-pill`, строка
   запроса `.askbar`, объёмный знак `.blob`. Здесь описано только то, чего в
   системе не было: веер-колода из трёх групп оплаты, стеклянная таблица,
   чип-статус с точкой-индикатором и сегментированный фильтр.

   🔴 Цвет в правилах не пишется — только токены (правило `app.css`, §1). Это
   же касается четырёх строк ниже: экрану понадобилась ХОЛОДНАЯ роль («при
   получении» — не успех, не тревога и не отказ), и она заведена именем во
   всех трёх темах, а не подставлена в правило. Когда тот же чип возьмёт
   «Инбокс», тройку `--cool*` и блок `.ochip` переносить в `app.css` целиком.
   ========================================================================= */

:root {
  /* Холодная роль состояния — четвёртая к ok/warn/bad. Тёмная тема: светлый
     пастельный тон на почти чёрном, как у трёх остальных. */
  --cool: #9ab8f2;
  --cool-soft: rgba(154, 184, 242, 0.12);
  --cool-line: rgba(154, 184, 242, 0.32);
  /* Притенение задних карточек веера: свет падает на переднюю (реф 2, п.1).
     Тенью, а не прозрачностью — прозрачность увела бы белый текст вместе с
     фоном и съела бы контраст, а притенение фона его только добавляет. */
  --deck-shade: rgba(0, 0, 0, 0.2);
  /* Высота цифры-матрицы на карточках веера. Отдельной ступенью: у колоды
     свой размер, и он не должен ездить вместе с карточками «Обзора». */
  --dotnum-deck: 3.5rem;
  --dotnum-deck-sm: 2.6rem;
}

[data-theme="light"] {
  --cool: #2f4bb5;
  --cool-soft: rgba(47, 75, 181, 0.12);
  --cool-line: rgba(47, 75, 181, 0.3);
  --deck-shade: rgba(9, 11, 24, 0.22);
}

[data-theme="neutral"] {
  --cool: #2f4bb5;
  --cool-soft: rgba(47, 75, 181, 0.1);
  --cool-line: rgba(47, 75, 181, 0.28);
  --deck-shade: rgba(9, 11, 24, 0.18);
}

/* Своей меры у экрана больше нет: контейнер один на весь пульт и живёт в
   `layout.css` (`--content-max`). Здесь стояли ДВЕ — 98rem у рабочей зоны и
   1560px у самой вкладки, — и левый край «Заказов» оказывался на 40 px левее
   знака в шапке, а правый на 96 px правее аватара. */

/* ------------------------------------------------- квадратные кнопки шапки
   Три иконки в ряд (реф 1, §2). Каждая работает: обновить · выгрузить то, что
   на экране · снять поиск и фильтры. Четвёртой — «открыть в CRM» из макета —
   здесь нет: публичного адреса Twenty пульт не знает, а кнопка, ведущая в
   никуда, хуже отсутствующей. */
button.sqbtn {
  display: grid;
  place-items: center;
  width: 2.15rem;
  height: 2.15rem;
  min-height: 2.15rem;
  padding: 0;
  background: var(--surface-flat);
  border: 1px solid var(--line-2);
  border-radius: var(--r-ctl);
  box-shadow: var(--sh-btn);
  color: var(--silver);
}

button.sqbtn:hover:not(:disabled) {
  border-color: var(--line-3);
  color: var(--milk);
}

button.sqbtn:disabled {
  opacity: 0.36;
  cursor: not-allowed;
}

/* Подпись словом у кнопок шапки. По умолчанию её нет: на телефоне три слова
   рядом с заголовком заняли бы вторую строку, а места касания у иконок и так
   по 44 px. */
.sqbtn-txt {
  display: none;
}

/* 🔴 С 1024 px подпись видна, и кнопка перестаёт быть квадратом. Три иконки
   без слов — единственные безымянные органы пульта: на «Журнале» соседняя
   выгрузка подписана «Выгрузить CSV», и первый рабочий день менеджера
   начинался бы с угадывания воронки и стрелки (ревизия 13.09, деф. 23).
   1024 — та же ступень, на которой пульт перестаёт быть узким. */
@media (min-width: 64rem) {
  #tab-orders .head-actions button.sqbtn {
    grid-auto-flow: column;
    justify-content: center;
    align-items: center;
    gap: var(--s2);
    width: auto;
    padding-inline: var(--s3);
  }

  #tab-orders .head-actions .sqbtn-txt {
    display: inline;
    font-size: var(--t-sm);
    font-weight: 500;
    white-space: nowrap;
  }
}

/* 🔴 `button.bare` (0,1,1) перебивает `.g-arrow` и `.pill-invert` (0,1,0) по
   ВЕСУ селектора — независимо от порядка в файле. В «Обзоре» стрелка сделана
   ссылкой и этого не замечает, а вот пилюля периода — кнопка с `.bare`, и
   она давно рисуется БЕЗ фона: замерено в браузере, `background-color` у
   `#ov-now-period` — `rgba(0,0,0,0)`. Два правила ниже возвращают обоим вид,
   описанный системой, и заодно чинят пилюлю «Обзора»: тот же вес селектора,
   более поздний файл. Когда `app.css` в следующий раз откроют на правку —
   перенести их туда, к самим компонентам. */
button.g-arrow {
  background: var(--grad-btn);
  color: var(--on-grad);
  border: 0;
  padding: 0;
  min-height: 0;
  box-shadow: none;
}

button.g-arrow:hover:not(:disabled) {
  background: var(--grad-btn-hi);
  border: 0;
  color: var(--on-grad);
}

button.pill-invert {
  background: var(--pill-invert-bg);
  color: var(--pill-invert-ink);
  border: 0;
  border-radius: var(--r-pill);
  box-shadow: none;
}

button.pill-invert:hover:not(:disabled) {
  background: var(--pill-invert-bg);
  border: 0;
  color: var(--pill-invert-ink);
  opacity: 0.88;
}

/* ============================================ ВЕЕР-КОЛОДА И КАРТОЧКА ДЕНЕГ */

.odeck-row {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr);
  gap: var(--s4);
  align-items: stretch;
  margin-bottom: var(--s4);
  /* 🔴 Наклон карт веера считается по ОХВАТЫВАЮЩЕМУ прямоугольнику, а не по
     самой карточке: `rotate: 9deg` на карте в 115 px расширяет её габарит до
     140 px, и правая карта на 390 упиралась в 390.83 — документ округлял
     `scrollWidth` вверх и страница ездила вбок на один пиксель (замерено:
     360 → 13 px, 375 → 8, 390 → 1, 391 → 0).

     Именно `clip`, а НЕ `hidden`: `hidden` завёл бы здесь скролл-контейнер, и
     `position: sticky` у предков (липкая шапка пульта) перестал бы прилипать.
     `clip` прокрутки не создаёт вовсе — ни своей, ни в документе. Ось одна:
     подъём карты под курсором и её тень по вертикали обязаны остаться
     видимыми.

     🔴 `overflow-clip-margin` здесь НЕ помощник, хотя напрашивается: запас
     вокруг клипа возвращает обрезанное в габариты документа, и вылет на 390
     появляется снова (замерено: с запасом 2 rem — 391 при окне 390). Чтобы
     клип ничего не съел, раздвинут сам РЯД — см. полную ширину на телефоне
     в блоке 56rem ниже. */
  overflow-x: clip;
}

/* «Карты в руке»: центральная впереди и крупнее, боковые повёрнуты на ∓9°,
   меньше и притенены. Наклон покоя — данные экрана, а не эффект: он один и
   тот же всегда. Двигается он ровно в одном случае — под курсором веер
   расходится на ±3° (раздел «ДВИЖЕНИЕ» в конце файла). */
/* По центру высоты, а не по низу: соседняя карточка денег выше колоды на
   легенду из пяти строк, и при нижнем выравнивании над веером открывалась
   пустая полоса в треть экрана. */
.odeck {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s2) 0;
}

.odeck-card {
  width: 12.1rem;
  min-height: 16.4rem;
  padding: var(--s4) var(--s4) var(--s3);
  border: 1px solid var(--grad-btn-hi);
  border-radius: 1.75rem;
}

.odeck-card--c {
  z-index: 3;
  box-shadow: var(--sh-3), var(--sh-2);
}

.odeck-card--l,
.odeck-card--r {
  width: 10.9rem;
  min-height: 14.5rem;
  padding: var(--s3) var(--s3) var(--s2);
  z-index: 1;
  box-shadow: var(--sh-lift);
}

/* 🔴 Наклон вынесен из `transform` в отдельное свойство `rotate`, а сдвиг
   остался в `transform`. Матрица та же (браузер применяет `transform`
   первым, `rotate` — поверх, то есть ровно прежний `rotate() translate()`),
   но теперь `translate` и `scale` СВОБОДНЫ: ими двигает раздача колоды, и
   без этого разделения она стирала бы позу — три карты сходились бы в
   стопку посреди анимации. */
.odeck-card--l {
  margin-right: -1.3rem;
  transform: translate(-0.25rem, 0.6rem);
  rotate: -9deg;
  transform-origin: bottom center;
}

.odeck-card--r {
  margin-left: -1.3rem;
  transform: translate(0.25rem, 0.6rem);
  rotate: 9deg;
  transform-origin: bottom center;
}

/* Притенение задней карточки. Отдельным слоем, а не `filter: brightness`:
   фильтр гасит и белый текст вместе с фоном, то есть уменьшает контраст,
   а этот слой лежит ПОД содержимым и контраст только увеличивает. */
.odeck-shade {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--deck-shade);
}

/* Цифра по центру карточки. `.dotnum` в системе тянется по ширине и
   прижимается влево (у «Обзора» это правильно — там под ней подпись слева).
   На карточке веера число стоит ПО ЦЕНТРУ, поэтому здесь svg получает высоту
   и считает ширину сам из viewBox, а `margin-inline: auto` его центрирует. */
.odeck-mid {
  flex: 1;
  display: grid;
  align-content: center;
  padding: var(--s3) 0 var(--s2);
}

.odeck-mid .dotnum {
  display: block;
  width: 100%;
}

.odeck-mid .dotnum-figure {
  display: block;
}

.odeck-mid .dotnum svg {
  width: auto;
  height: var(--dotnum-deck);
  max-height: none;
  max-width: 100%;
  margin-inline: auto;
}

.odeck-card--l .dotnum svg,
.odeck-card--r .dotnum svg {
  height: var(--dotnum-deck-sm);
}

.odeck-card .g-sub {
  text-align: center;
  line-height: 1.4;
}

.odeck-wave {
  margin-top: auto;
  padding-top: var(--s2);
}

/* Брендовая карточка денег: главная цифра точками, легенда с квадратиками и
   волна во всю ширину. */
.odeck-hero {
  justify-content: flex-start;
}

.odeck-hero .dotnum {
  margin-top: var(--s4);
}

.odeck-hero .g-sub {
  margin-top: var(--s2);
}

.odeck-hero .legend {
  margin-top: var(--s4);
}

.odeck-hero-spark {
  margin-top: auto;
  padding-top: var(--s4);
}

/* Пятый квадратик легенды: групп оплаты пять, а не четыре, потому что сумма
   строк обязана сойтись с числом загруженных заказов. */
.legend i.is-e {
  background: rgba(255, 255, 255, 0.1);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.42);
}

/* ====================== РЯД ПО СУТКАМ · КОЛЬЦО ГРУПП · КАНАЛЫ · ПЛИТКИ
   Три стеклянные панели и три градиентные плитки между веером и таблицей.
   Собраны из тех же деталей, что «Обзор»: мини-столбики `.ovbars`, кольцо
   `.dn` (оно же на «Журнале»), спарклайн `.ovspark`, объёмный знак `.blob`,
   цифра из точек `.dotnum`. Нового здесь ровно три вещи: второй ряд
   столбиков в сутках, легенда кольца на стекле и обойма из трёх плиток. */

.ostats {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.78fr) minmax(0, 1fr);
  gap: var(--s4);
  align-items: stretch;
  margin-bottom: var(--s4);
}

.ostat {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  min-width: 0;
}

.ostat-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s2);
}

.ostat .h3 {
  margin: 0;
  font-size: var(--t-md);
  font-weight: 600;
  letter-spacing: -0.02em;
}

.ostat .eyebrow {
  line-height: 1.4;
}

/* Подпись графика прижата к низу панели: три панели в ряду разной высоты,
   и без этого их пояснения стояли бы на трёх разных уровнях. */
.ostat > .eyebrow:last-child {
  margin-top: auto;
}

/* ------------------------------------------------- легенда двух рядов */
.okeys {
  display: flex;
  gap: var(--s3);
  flex-wrap: wrap;
}

.okey {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--mute);
  font-size: var(--t-xs);
}

.okey::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 2px;
  background: var(--c, var(--accent));
}

.okey--made {
  --c: var(--accent);
}

.okey--paid {
  --c: var(--ok);
}

/* --------------------------------------- ряд по суткам: два столбика в дне */
/* Ряд занимает всё свободное место панели: три прибора в ряду выравниваются
   по самому высокому (кольцо с легендой из пяти строк), и при фиксированной
   высоте под столбиками открывалась пустая полоса в половину карточки. */
.obars {
  flex: 1;
  min-height: 6.4rem;
  margin-top: 0;
}

.obars .pair {
  display: flex;
  align-items: flex-end;
  gap: 3px;
}

.obars .stem {
  width: 6px;
}

/* 🔴 Цвет столбика означает ЧТО (заказ или оплата), а не КОГДА. Правило
   `.ovbars figure.on .stem` из app.css красит сегодняшний столбик в общий
   белый — здесь оно перебивается тем же весом селектора из более позднего
   файла, иначе сегодня оба ряда сливались бы в один. */
.obars figure .stem--made,
.obars figure.on .stem--made {
  background: var(--accent);
}

.obars figure .stem--paid,
.obars figure.on .stem--paid {
  background: var(--ok);
}

/* Пустые сутки — волосок: «ноль» и «дня нет в ряду» обязаны различаться. */
.obars figure .stem.is-zero,
.obars figure.on .stem.is-zero {
  background: var(--line-3);
}

/* ------------------------------------------------------------ кольцо */
.oring {
  display: grid;
  place-items: center;
  margin-top: auto;
}

.oring .dn {
  width: 8.4rem;
  height: 8.4rem;
}

/* Середина кольца — сумма, а не двузначное число: ступень кегля меньше, чем
   у кольца тревог, и ещё одна — для длинных сумм («146 800 ₴»). */
.oring .dn-big {
  font-size: 1.05rem;
}

.oring .dn-big.is-long {
  font-size: 0.84rem;
}

.oring-leg {
  display: grid;
  gap: 4px;
}

.oleg {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: var(--t-xs);
  color: var(--silver);
}

.oleg i {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--c, var(--accent));
}

.oleg span {
  margin-right: auto;
}

.oleg small {
  color: var(--mute);
  font-variant-numeric: tabular-nums;
}

.oleg b {
  min-width: 1.6rem;
  color: var(--milk);
  font-weight: 600;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.oeva-wave {
  margin-top: auto;
  padding-top: var(--s2);
}

/* Волна канала выше системной ступени: панель тянется по высоте соседнего
   кольца, и полоска в 2,4 rem висела бы в пустоте. */
.oeva-wave .ovspark {
  height: 3.4rem;
}

/* 🔴 Спарклайн системы нарисован для ГРАДИЕНТНОЙ карточки: линия у него
   `--on-grad`, то есть почти белая. На стеклянной панели в светлой теме
   такая волна невидима — здесь у неё цвета из палитры состояний. */
.ostat .ovspark-line {
  stroke: var(--accent);
}

.ostat .ovspark-dot {
  fill: var(--accent);
}

.ostat .ovspark-dot.is-hollow {
  fill: none;
  stroke: var(--accent);
}

.ostat .ovspark-base {
  stroke: var(--line-3);
}

.ostat .dotgrid {
  background-image: radial-gradient(var(--line-3) 1px, transparent 1px);
}

/* ------------------------------------------------ три градиентные плитки */
.otrio {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s4);
  margin-bottom: var(--s4);
}

.otrio .gcard {
  min-height: 10.2rem;
}

.otrio .dotnum {
  margin-top: var(--s3);
}

.otrio .g-sub {
  margin-top: var(--s1);
}

.otrio-wave {
  margin-top: auto;
  padding-top: var(--s3);
}

/* ------------------------------------------------------------ скелетон
   До первого ответа `/api/orders` на месте графиков стоит плашка с
   пробегающим бликом, а не пустота: пустая панель читается как поломка.
   Держится на `:empty` — как в «Обзоре»: класс знал бы ровно то же самое. */
#od-days:empty,
#od-ring:empty,
#od-ring-leg:empty,
#od-eva:empty,
#od-eva-spark:empty {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-sm);
  background: var(--ink-780);
}

#od-days:empty {
  height: 5.2rem;
}

#od-ring:empty {
  height: 8.4rem;
  border-radius: 50%;
  margin-inline: auto;
  width: 8.4rem;
}

#od-ring-leg:empty,
#od-eva:empty {
  height: 5.6rem;
}

#od-eva-spark:empty {
  height: 2.4rem;
}

#od-days:empty::after,
#od-ring:empty::after,
#od-ring-leg:empty::after,
#od-eva:empty::after,
#od-eva-spark:empty::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, var(--shimmer), transparent);
  transform: translateX(-100%);
  animation: shimmer 1.4s var(--ease) infinite;
}

/* =================================================== СТЕКЛЯННАЯ ТАБЛИЦА */

.orders-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s4);
  align-items: start;
}

.orders-layout.has-detail {
  grid-template-columns: minmax(0, 1.5fr) minmax(19rem, 1fr);
}

.orders-panel {
  padding: 0;
  overflow: hidden;
  min-width: 0;
}

.orders-panel-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s3);
  padding: var(--s4) var(--s5) 0;
}

.orders-panel-head .h3 {
  margin: 0;
  font-size: var(--t-md);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--milk);
}

.orders-panel-head .eyebrow {
  margin-bottom: 2px;
}

.orders-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  padding: var(--s3) var(--s5) var(--s4);
  border-bottom: 1px solid var(--line);
}

.orders-search {
  display: flex;
  align-items: center;
  gap: var(--s2);
  flex: 1 1 15rem;
  min-width: 0;
  min-height: 2.25rem;
  padding: 0 var(--s3);
  background: var(--ink-860);
  border: 1px solid var(--line-2);
  border-radius: var(--r-ctl);
  color: var(--faint);
}

.orders-search:focus-within {
  border-color: var(--accent);
  box-shadow: var(--sh-focus);
}

.orders-search input {
  flex: 1;
  min-width: 0;
  min-height: 2rem;
  padding: 0;
  background: none;
  border: 0;
  color: var(--milk);
  font: inherit;
  font-size: var(--t-sm);
}

.orders-search input:focus {
  outline: none;
  border: 0;
  background: none;
}

.orders-search input::placeholder {
  color: var(--faint);
}

button.orders-search-clear {
  display: grid;
  place-items: center;
  flex: none;
  width: 1.4rem;
  height: 1.4rem;
  min-height: 1.4rem;
  padding: 0;
  border: 0;
  border-radius: var(--r-pill);
  background: none;
  box-shadow: none;
  color: var(--mute);
}

button.orders-search-clear:hover:not(:disabled) {
  background: none;
  border: 0;
  color: var(--milk);
}

/* Сегментированный фильтр — тот же приём, что таб-бар шапки: набор, в котором
   ровно один пункт текущий. Пустых групп в наборе нет: кнопка, всегда дающая
   пустой список, — это не фильтр. */
.osegs {
  display: flex;
  flex: 0 1 auto;
  gap: 2px;
  padding: 2px;
  min-width: 0;
  max-width: 100%;
  overflow-x: auto;
  background: var(--seg-bg);
  border-radius: var(--r-ctl);
  scrollbar-width: thin;
  scroll-snap-type: x proximity;
  scroll-padding-inline: 2.5rem;
  overscroll-behavior-x: contain;
}

/* 🔴 M-7 (мобильный аудит 24.09): лента срезов в 1002 px прокручивалась в
   полосе 343 px, и «Ожидается», «Наложка…», «Не прошла», «Возвращена» стояли
   за правым краем без намёка, что они там есть (то же на 812×375 и у каналов).
   Намёк — угасание края, за которым есть ещё пилюли: классы ставит
   `stripEdges()` в orders.js по положению прокрутки, поэтому у ленты, которая
   помещается целиком, края не гаснут. Маска, а не цветная заливка: она не
   знает темы и не заводит нового цвета. */
.osegs.is-more-r {
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 2.5rem), transparent);
  mask-image: linear-gradient(90deg, #000 calc(100% - 2.5rem), transparent);
}

.osegs.is-more-l {
  -webkit-mask-image: linear-gradient(270deg, #000 calc(100% - 2.5rem), transparent);
  mask-image: linear-gradient(270deg, #000 calc(100% - 2.5rem), transparent);
}

.osegs.is-more-l.is-more-r {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 2.5rem, #000 calc(100% - 2.5rem), transparent);
  mask-image: linear-gradient(90deg, transparent, #000 2.5rem, #000 calc(100% - 2.5rem), transparent);
}

button.osegs-btn {
  scroll-snap-align: start;
  flex: none;
  padding: 0.28rem 0.7rem;
  min-height: 1.8rem;
  background: none;
  border: 0;
  border-radius: calc(var(--r-ctl) - 2px);
  box-shadow: none;
  color: var(--silver);
  font-size: var(--t-xs);
  font-weight: 500;
  white-space: nowrap;
}

button.osegs-btn:hover:not(:disabled) {
  background: var(--ink-740);
  border: 0;
  color: var(--milk);
}

button.osegs-btn.is-on {
  background: var(--accent-soft-2);
  border: 0;
  color: var(--accent-2);
  font-weight: 600;
}

.orders-table-scroll {
  overflow-x: auto;
}

.otable {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-sm);
}

.otable th {
  padding: 0.7rem var(--s4);
  border-bottom: 1px solid var(--line);
  color: var(--mute);
  font-size: var(--t-micro);
  font-weight: 700;
  letter-spacing: 0.07em;
  text-align: left;
  text-transform: uppercase;
  white-space: nowrap;
}

.otable td {
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line);
  color: var(--silver);
  vertical-align: middle;
}

.otable tbody tr:last-child td {
  border-bottom: 0;
}

/* 🔴 Прибавку ширины забирает ОДНА колонка — «Город и доставка». Браузер в
   `table-layout: auto` делит лишнее между всеми колонками пропорционально, и
   на 1696 номер заказа, сумма, оплата, канал и статус расползались по строке
   ровными островами: глаз вёл от «DEMO-EVA-101» к «Новый» через четыре
   пустоты. Теперь служебные колонки стоят по содержимому, а растёт адрес —
   единственное поле, которому ширина действительно нужна. */
.otable th:nth-child(2),
.otable td:nth-child(2) {
  width: 34%;
}

/* 🔴 Колонка «ЗАКАЗ» не сжимается НИКОГДА. Замерено в браузере на 1440 с
   открытой карточкой заказа: таблица теряет треть ширины, `table-layout: auto`
   отдаёт лишнее соседям, а первая колонка ужимается 186 → 71 px — номер
   «DEMO-EVA-101» вставал в три строки («DEMO/-EVA-/101»), дата «13.09.2026,
   07:10» — в три. Номер заказа это ИМЯ строки: по нему ищут, его диктуют по
   телефону и сверяют с CRM, и разорванным по слогам он перестаёт читаться.

   Ломало его `overflow-wrap: anywhere` у `.onum` (снято ниже): с ним
   минимальная ширина ячейки для браузера равна ОДНОЙ БУКВЕ, и колонку можно
   ужать во что угодно. С `nowrap` минимум колонки — сам номер, и таблица
   больше не имеет права опустить её ниже. `min-width` держит ту же меру для
   строк с коротким номером: колонка не должна прыгать от заказа к заказу. */
.otable th:first-child,
.otable td:first-child {
  min-width: 8.5rem;
  white-space: nowrap;
}

.otable tbody tr:hover td {
  background: var(--ink-740);
}

/* Выбранная строка отмечена и засечкой, и фоном: цвет в пульте никогда не
   единственный носитель смысла. */
.otable tbody tr.is-on td {
  background: var(--ink-700);
}

.otable tbody tr.is-on td:first-child {
  box-shadow: inset 3px 0 0 var(--accent);
}

.otable .right {
  text-align: right;
}

/* 🔴 `overflow-wrap: anywhere` здесь БЫЛО и здесь его быть не должно: оно
   разрешает разрыв между любыми двумя буквами, а значит объявляет браузеру
   минимальную ширину колонки в один знак. Ровно этим номер заказа и рвался на
   «DEMO/-EVA-/101» при открытой карточке. Длинный номер теперь не переносится,
   а таблица при нехватке места прокручивается в своей дорожке
   (`.orders-table-scroll`) — как и положено таблице. */
a.onum {
  display: inline-block;
  color: var(--milk);
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  text-decoration: none;
}

a.onum:hover {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 4px;
}

/* 🔴 `--mute`, а не `--faint`. Измерение в браузере: мелкая подпись `--faint`
   на ВЫДЕЛЕННОЙ строке тёмной темы (`--ink-700`) даёт 4.42:1 — под порогом.
   На обычной строке она проходила бы, но строка под курсором — тот самый
   момент, когда дату читают. */
.osub {
  display: block;
  margin-top: 2px;
  color: var(--mute);
  font-size: var(--t-micro);
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
}

.osub.is-bad {
  color: var(--bad);
}

.omoney {
  color: var(--milk);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  white-space: nowrap;
}

.ostate {
  color: var(--silver);
  white-space: nowrap;
}

/* -------------------------------------------------------- чип-статус
   Стеклянная пилюля с точкой-индикатором (реф 2, п.3). Не `.gchip`: тем
   именем в системе уже назван чип с иконкой в квадратике (факты персоны,
   карточка клиента), и второй смысл на одно имя развалил бы оба. */
.ochip {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: 1.65rem;
  padding: 0.1rem var(--s3);
  background: var(--glass-3);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid var(--hairline-hi);
  outline: 1px solid var(--line);
  outline-offset: -1px;
  border-radius: var(--r-pill);
  box-shadow: var(--sh-btn);
  font-size: var(--t-xs);
  font-weight: 600;
  white-space: nowrap;
}

.ochip i {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
}

.ochip--ok {
  color: var(--ok);
}

.ochip--ok i {
  background: var(--ok);
}

.ochip--warn {
  color: var(--warn);
}

.ochip--warn i {
  background: var(--warn);
}

.ochip--bad {
  color: var(--bad);
}

.ochip--bad i {
  background: var(--bad);
}

.ochip--cool {
  color: var(--cool);
}

.ochip--cool i {
  background: var(--cool);
}

.ochip--mute {
  color: var(--mute);
}

.ochip--mute i {
  background: var(--faint);
}

.orders-empty {
  margin: 0;
  padding: var(--s7) var(--s5);
  color: var(--mute);
  font-size: var(--t-base);
  line-height: 1.7;
}

.orders-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: var(--s3) var(--s5);
  border-top: 1px solid var(--line);
  color: var(--mute);
  font-size: var(--t-xs);
  line-height: 1.6;
}

.orders-notice {
  margin: var(--s4) 0;
  padding: var(--s4) var(--s5);
  border: 1px solid var(--warn-line);
  border-radius: var(--r-sm);
  background: var(--warn-soft);
  color: var(--warn);
  font-size: var(--t-base);
  line-height: 1.6;
}

/* ==================================================== КАРТОЧКА ЗАКАЗА */

.order-detail {
  min-width: 0;
  scroll-margin-top: 72px;
}

.order-detail-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  margin-bottom: var(--s5);
}

.order-detail h3 {
  margin: 0;
  font-size: var(--t-lg);
  line-height: 1.4;
  overflow-wrap: anywhere;
}

.order-close {
  color: var(--accent);
  font-size: var(--t-sm);
  padding: var(--s2) 0;
}

.order-detail h4 {
  margin: var(--s6) 0 var(--s3);
  color: var(--milk);
  font-size: var(--t-base);
  font-weight: 500;
}

/* Покупатель, телефон, оплата и сумма — четырьмя стеклянными чипами с
   иконками в квадратиках (реф 1, §4). В таблице покупателя нет: список
   заказов CRM его не отдаёт, и здесь он появляется первым. */
.ochips {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s2);
  margin-bottom: var(--s5);
}

.ochips .gchip b {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* «Показать телефон» / «Скопировать» внутри чипа с телефоном (ревизия 12.09).
   Маленькая и тихая: это не действие экрана, а служебная кнопка у одного
   значения — рядом с ней в том же чипе стоит сам номер. Своей строкой, а не
   в строку с номером: номер бывает длинным, и кнопка вытолкнула бы его в
   многоточие. */
.ochips .gchip--phone > span:last-child {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 0;
}

.oph-btn {
  margin-top: var(--s2);
  min-height: 1.6rem;
  padding: 0.1rem var(--s3);
  font-size: var(--t-xs);
}

.order-facts {
  display: grid;
  grid-template-columns: minmax(100px, 1fr) minmax(0, 1.7fr);
  gap: var(--s3) var(--s4);
  margin: 0;
  font-size: var(--t-sm);
  line-height: 1.65;
}

.order-facts dt {
  color: var(--mute);
}

.order-facts dd {
  margin: 0;
  color: var(--milk);
  overflow-wrap: anywhere;
}

.order-items {
  margin: 0;
  padding: 0;
  list-style: none;
}

.order-items li {
  padding: var(--s3) 0;
  border-bottom: 1px solid var(--line-2);
  font-size: var(--t-base);
  line-height: 1.6;
}

.order-items small {
  display: block;
  margin-top: var(--s1);
  color: var(--mute);
  font-size: var(--t-sm);
}

.order-comment {
  color: var(--silver);
  font-size: var(--t-sm);
  line-height: 1.8;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}

/* ==================================================== БЛОК «ДЕЙСТВИЯ»
   21.09.2026. Раздел перестал быть экраном для чтения: статус, ТТН,
   доставка, заметка и счёт живут здесь, в правой панели, — ровно там, где
   менеджер уже смотрит на заказ. Органы стоят В ПОРЯДКЕ РАБОТЫ смены:
   подтвердил → отправил → починил адрес → записал, что помнить → выписал
   счёт.

   Ширина панели на 1440 px — около 26 rem, и ряды сделаны гибкими: поле и
   кнопка стоят рядом, пока помещаются, и переносятся, когда нет. Ни одного
   фиксированного `min-width` шире панели: горизонтальной прокрутки в пульте
   нет нигде. */
.oact {
  margin-top: var(--s6);
  padding-top: var(--s5);
  border-top: 1px solid var(--line);
}

.oact h4 {
  margin: 0 0 var(--s3);
}

.oact-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--s3);
  margin-top: var(--s4);
}

.oact-row > div {
  flex: 1 1 11rem;
  min-width: 0;
}

.oact-row > button {
  flex: 0 0 auto;
}

/* Поле причины живёт ПОД рядом статуса и появляется только у «Отменён» и
   «Невыкуп»: у остальных переходов объяснять нечего, и пустое поле там
   означало бы «а вдруг надо». */
.oact-reason {
  margin-top: var(--s2);
}

.oact-lab {
  display: block;
  margin-bottom: var(--s1);
  color: var(--mute);
  font-size: var(--t-xs);
  line-height: 1.5;
}

/* Значение, к которому относится кнопка рядом («Доставка: Відділення № 12»).
   Переносится по словам: названия отделений бывают в три строки. */
.oact-now {
  flex: 1 1 11rem;
  min-width: 0;
  color: var(--silver);
  font-size: var(--t-sm);
  line-height: 1.5;
  overflow-wrap: anywhere;
}

button.oact-mini {
  margin-top: var(--s2);
  min-height: 1.7rem;
  padding: 0.1rem var(--s3);
  font-size: var(--t-xs);
}

/* Отказ стоит РЯДОМ С ОРГАНОМ, а не тостом в углу: тост живёт шесть секунд и
   не отвечает на вопрос «что именно я сделал не так». */
.oact-err {
  margin: var(--s4) 0 0;
  padding: var(--s3) var(--s4);
  border: 1px solid var(--bad-line);
  border-radius: var(--r-sm);
  background: var(--bad-soft);
  color: var(--bad);
  font-size: var(--t-sm);
  line-height: 1.6;
  overflow-wrap: anywhere;
}

.oact-err button {
  display: block;
  margin-top: var(--s2);
}

.oact-said {
  margin: 0 0 var(--s2);
  color: var(--silver);
  font-size: var(--t-sm);
  line-height: 1.6;
  overflow-wrap: anywhere;
}

/* Готовый текст покупателю и ссылка на оплату: их НЕ отправляет пульт —
   рассылки решает владелец. Здесь они лежат под кнопку «скопировать». */
.oact-link {
  margin-top: var(--s3);
  padding: var(--s4);
  border: 1px solid var(--line-2);
  border-radius: var(--r-sm);
  background: var(--glass-3);
}

.oact-url {
  display: block;
  color: var(--accent);
  font-size: var(--t-sm);
  line-height: 1.6;
  overflow-wrap: anywhere;
}

.oact-fix {
  margin-top: var(--s3);
  padding: var(--s4);
  border: 1px solid var(--line-2);
  border-radius: var(--r-sm);
  background: var(--glass-3);
}

.oact-fix > * + * {
  margin-top: var(--s3);
}

.oact-fix button {
  width: 100%;
}

/* ----------------------------------------------- поле с подсказками НП
   Список лежит НАД содержимым карточки (`position: absolute`), иначе
   открытые подсказки раздвигали бы всё ниже и кнопка уезжала из-под
   пальца. Высота ограничена: пятьдесят отделений большого города не имеют
   права закрыть карточку целиком. */
.osug {
  position: relative;
}

.osug-list {
  position: absolute;
  z-index: 6;
  left: 0;
  right: 0;
  max-height: 13rem;
  margin: var(--s1) 0 0;
  padding: var(--s1);
  overflow-y: auto;
  list-style: none;
  background: var(--ink-860);
  border: 1px solid var(--line-field);
  border-radius: var(--r-sm);
  box-shadow: var(--sh-btn);
}

.osug-item {
  padding: var(--s2) var(--s3);
  border-radius: calc(var(--r-sm) - 2px);
  color: var(--milk);
  font-size: var(--t-sm);
  line-height: 1.5;
  cursor: pointer;
  overflow-wrap: anywhere;
}

.osug-item:hover,
.osug-item.is-at {
  background: var(--accent-soft-2);
  color: var(--accent-2);
}

.osug-note {
  margin: var(--s1) 0 0;
  color: var(--mute);
  font-size: var(--t-xs);
  line-height: 1.5;
}

.osug-note.is-bad {
  color: var(--bad);
}

/* ------------------------------------------------- «нужно действие»
   Срез, а не группа оплаты: он пересекается с любой из них, поэтому у чипа
   своя метка и своё место — первое в ряду. */
button.osegs-btn[data-view] {
  color: var(--warn);
}

button.osegs-btn[data-view]::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: var(--s2);
  border-radius: 50%;
  background: var(--warn);
  vertical-align: middle;
}

button.osegs-btn[data-view].is-on {
  background: var(--warn-soft);
  color: var(--warn);
}

/* Строка заказа, которым надо заняться, помечена кромкой, а не заливкой:
   заливка спорила бы с выделением открытой карточки. */
.otable tbody tr.is-need td:first-child {
  box-shadow: inset 2px 0 0 var(--warn);
}

/* 🔴 Причина «нужно действие» — единственная строка первой колонки, которой
   ПОЗВОЛЕНО переноситься. `nowrap` у колонки заведён ради НОМЕРА заказа: его
   диктуют по телефону, и рваться он не имеет права (деф. 9 ревизии 13.09).
   Объяснение из нескольких слов держать одной строкой незачем — при открытой
   карточке оно растягивает колонку и выталкивает таблицу за свою дорожку
   (поймано смоуком 21.09.2026 на признаке «наложка: предоплата не пришла»). */
.osub.is-need-why {
  color: var(--warn);
  white-space: normal;
  max-width: 20rem;
}

/* ------------------------------------------------- строка запроса и сноска */

.orders-ask {
  max-width: 38rem;
  margin: var(--s5) auto 0;
}

.orders-footnote {
  max-width: 85ch;
  margin: var(--s5) 0 0;
}

/* ============================================================= ДВИЖЕНИЕ
   Кадры (`gDeal`, `gDot`, `gDraw`, `gFade`, `gShake`, `gSheen`) и бегущий
   блик `.g-gloss` — ОБЩИЕ, живут в `overview.css`: одна форма движения на
   весь продукт читается как система, шесть разных — как набор эффектов.
   Здесь только то, чего у «Обзора» нет: колода трёх групп оплаты,
   стеклянная таблица и её скелетон.

   Границы те же, что у всего пульта: в кадре только `transform`/`opacity`
   и независимые `translate`/`rotate`/`scale` (плюс `stroke-dashoffset`
   рисующейся линии), микро ≤ 300 мс, циклы ≥ 6 с, на 390 px параллакса
   нет, `prefers-reduced-motion: reduce` гасит всё. */

/* ------------------------------------------------ раздача колоды
   Карты раздаются при КАЖДОМ входе на экран — и ни разу больше. Класса для
   этого не нужно: спрятанная вкладка лежит в `display: none`, и браузер
   запускает анимацию заново ровно в момент показа экрана. Тем самым
   движение здесь означает «экран открылся», а не «прошёл такт опроса». */
.odeck-card {
  animation: gDeal var(--dur-3) var(--ease-out) backwards;
  transition:
    rotate var(--dur-2) var(--ease),
    translate var(--dur-2) var(--ease);
}

.odeck-card--c {
  animation-delay: 0ms;
}

.odeck-card--l {
  animation-delay: 80ms;
}

.odeck-card--r {
  animation-delay: 160ms;
}

/* Под курсором веер расходится на ±3°, наведённая карта выходит вперёд —
   и глубиной, и подъёмом. */
.odeck:hover .odeck-card--l {
  rotate: -12deg;
}

.odeck:hover .odeck-card--r {
  rotate: 12deg;
}

.odeck-card:hover {
  z-index: 4;
  translate: 0 -4px;
}

/* Брендовая карточка денег и панель таблицы отзываются подъёмом. Отдельным
   свойством `translate`, а не `transform`: у карточек колоды `transform`
   держит позу, и общее правило её стирало бы. */
.odeck-hero,
.orders-panel {
  transition: translate var(--dur-2) var(--ease);
}

.odeck-hero:hover {
  translate: 0 -2px;
}

/* --------------------------------------------- ХЕРО ЖИВОЙ: свет и дыхание
   🔴 Ревизия 13.09 (деф. 13): у «Заказов» не было НИ ОДНОГО бесконечного
   цикла — `getAnimations()` в открытой вкладке возвращал только оболочку
   (фон пульта, точка состояния, бейдж шапки). Раздача колоды и каскад строк
   означают «экран открылся» и «данные приехали»; когда всё уже приехало,
   экран замирал насмерть, тогда как у «Обзора», «Инбокса», БЗ и «Настроек»
   херо продолжает жить. Канон README дизайна требует живого херо на каждом
   экране.

   Приём тот же, что у «Обзора» (`ovFloat`/`ovSpin`/`ovBreathe`) и «Инбокса»
   (`ibFloat`): медленный цикл на СВЕТЕ, а не на данных. Здесь свет —
   единственное, чему на карточке денег можно двигаться: цифра, легенда и
   волна означают факты, и шевелить ими значило бы врать «изменилось».

   Двигается `::before` — слой «дымок» самой `.gcard` (два белых радиальных
   пятна из app.css). Он лежит ПОД содержимым (`z-index: -1`), в кадре только
   `translate` и `scale`, и обрезан скруглением карточки. `inset` растянут за
   край: без запаса дрейф оголял бы угол, где пятно кончается.

   Второй цикл — «дыхание» кромочного блика `::after` прозрачностью. Оба
   длиннее шести секунд и не совпадают периодами (17 и 9,4 с): совпади они —
   получилось бы одно мигание в два раза ярче, а не жизнь. */
@keyframes odDrift {
  from {
    translate: -5.5% -3%;
    scale: 1;
  }

  to {
    translate: 5.5% 3.5%;
    scale: 1.11;
  }
}

@keyframes odGlow {
  from {
    opacity: 0.55;
  }

  to {
    opacity: 1;
  }
}

.odeck-hero::before {
  inset: -16%;
  animation: odDrift 14s var(--ease) infinite alternate;
}

.odeck-hero::after {
  animation: odGlow 8.6s var(--ease) infinite alternate;
}

/* Объёмный знак из трёх шаров дышит — ровно как в «EVA сейчас» у «Обзора»
   (`ovBreathe`, overview.css). Кадр общий: второй такой же с другим именем
   читался бы как другой прибор. */
#tab-orders .ostat-head .blob,
#tab-orders .orders-panel-head .blob {
  animation: ovBreathe 7.2s var(--ease) infinite alternate;
}

/* ------------------------------- приборы под веером и обойма трёх плиток
   Три плитки раздаются из колоды при КАЖДОМ входе на экран тем же шагом
   80 мс, что «три карточки дня» в «Обзоре»: это одна и та же раздача,
   просто карты легли сеткой. Класса не нужно — спрятанная вкладка лежит в
   `display: none`, и браузер запускает анимацию в момент показа. */
.otrio .gcard {
  animation: gDeal var(--dur-3) var(--ease-out) backwards;
}

.otrio .gcard:nth-child(2) {
  animation-delay: 80ms;
}

.otrio .gcard:nth-child(3) {
  animation-delay: 160ms;
}

/* Столбики растут снизу. Высоту задают данные (CSSOM из js), кадр двигает
   только масштаб по Y — раскладка при этом не пересчитывается. Класс
   `is-grown` ставит `orders.js` и ТОЛЬКО когда ряд изменился: экран
   перерисовывается на каждую букву в поиске, а график от поиска не зависит. */
#od-days.is-grown .stem {
  transform-origin: bottom center;
  animation: ovBar var(--dur-3) var(--ease-out) backwards;
  animation-delay: calc(var(--i, 0) * 40ms);
}

#od-days.is-grown .val,
#od-days.is-grown .lbl {
  animation: gFade var(--dur-2) var(--ease) 120ms backwards;
}

/* Значение прошлых суток проступает под курсором — сегодняшнее видно всегда:
   вопрос у графика один, «сколько сегодня». */
#od-days figure:not(.on) .val {
  opacity: 0;
  transition: opacity var(--dur-1) var(--ease);
}

#od-days figure:not(.on):hover .val,
#od-days figure:not(.on):focus-within .val {
  opacity: 1;
}

#od-days.is-grown figure:not(.on) .val {
  animation: none;
}

.ostat {
  transition: translate var(--dur-2) var(--ease);
}

.ostat:hover {
  translate: 0 -2px;
}

/* ------------------------------------------ цифры из точек и волны
   Правила общие, в `overview.css` (`.dotnum.is-lit svg circle`,
   `.is-drawn .ovspark-*`). Зажигание и рисование ставит классами
   `orders.js`: css не знает, изменилось ли показание, а мигать на каждой
   перерисовке — значит через час перестать что-либо означать. */

/* ------------------------------------------------- строки таблицы
   Каскад приезда ставит общий `cascade()` из app.js (класс `.rise`, шаг
   20 мс) — на загрузке и на смене фильтра, но НЕ на каждой букве в поиске:
   пятьдесят строк, приезжающих на каждое нажатие клавиши, читаются как
   тормоза, а не как ответ.

   Строка, приехавшая с кнопкой «Загрузить ещё», вспыхивает: в таблице из
   ста одинаковых строк иначе не видно, какие именно догрузились. */
.otable tbody tr {
  position: relative;
}

.otable tbody tr.is-new::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: var(--g-flash);
  animation: gFlash 1.8s var(--ease) both;
}

/* ------------------------------------------------- чипы и фильтры
   Активное состояние переезжает за 150 мс, а не переключается кадром. */
button.osegs-btn {
  transition:
    background 150ms var(--ease),
    color 150ms var(--ease);
}

.ochip {
  transition: border-color 150ms var(--ease);
}

/* ------------------------------------------------- карточка заказа
   Выезжает шторкой за 240 мс. Класса не нужно: карточка прячется
   атрибутом `hidden`, то есть уходит в `display: none`, и анимация
   запускается ровно в момент показа. */
@keyframes odSheet {
  from {
    opacity: 0;
    transform: translateX(18px);
  }
}

/* На телефоне карточка — шторка во весь экран и приезжает снизу. */
@keyframes odSheetUp {
  from {
    opacity: 0;
    transform: translateY(24px);
  }
}

.order-detail:not([hidden]) {
  animation: odSheet 240ms var(--ease-out) backwards;
}

/* --------------------------------------------------- кнопки экрана */
#tab-orders button:active:not(:disabled) {
  transform: scale(0.98);
}

/* ------------------------------------------- движение блока «Действия»
   Три кадра и ни одним больше, и каждый означает СОБЫТИЕ, а не украшение:
   раскрылось поле доставки, приехали подсказки, появился результат. Форма —
   та же, что у шторки карточки (`odSheet`): короткий сдвиг вверх с
   проявлением, микро ≤ 300 мс, только `opacity` и `transform`. */
@keyframes odRise {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
}

.oact-fix:not([hidden]),
.oact-link:not([hidden]),
.osug-list:not([hidden]) {
  animation: odRise 180ms var(--ease-out) backwards;
}

/* Чип «Нужно действие» — единственный цикл этого блока: медленное дыхание
   точки (6 с, как требует канон для бесконечных кадров). Оно означает «есть
   работа» и гаснет само, когда срез пустеет и чип исчезает. */
@keyframes odNeed {
  50% {
    opacity: 0.45;
  }
}

button.osegs-btn[data-view]::before {
  animation: odNeed 6s var(--ease) infinite;
}

/* --------------------------------------------------------- скелетон
   Шесть плашек с пробегающим бликом на время ПЕРВОЙ загрузки: пустая
   панель читается как поломка, а строка «Загружаем заказы…» не показывает,
   сколько всего приедет. На обновлениях не рисуется — мигание хуже
   ожидания (правило `skeleton()` в app.js, оттуда же и плашки). */
.orders-skel {
  display: grid;
  gap: var(--s2);
  padding: var(--s4) var(--s5);
}

/* ------------------------------------------------- отказ сети
   Одна тряска поля поиска — и больше ничего: повторяющаяся анимация ошибки
   читается как поломка интерфейса, а не как отказ сервера. */
.orders-search.is-shake {
  animation: gShake 380ms var(--ease) both;
}

/* ======================================================= АДАПТИВ ЭКРАНА */

@media (max-width: 78rem) {
  .odeck-row {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Три прибора в ряд на 1250 px дают панель в 19 rem: кольцо в ней ещё
     помещается, а ряд из семи суток — уже нет. Сутки и кольцо встают парой,
     каналы уходят под них во всю ширину. */
  .ostats {
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr);
  }

  .ostats > .ostat:last-child {
    grid-column: 1 / -1;
  }

  .orders-layout.has-detail {
    grid-template-columns: minmax(0, 1fr);
  }

  .order-detail {
    grid-row: 1;
  }
}

/* 🔴 Открытая карточка заказа забирает у таблицы треть ширины, и шести колонок
   в оставшейся полосе не остаётся. Уступает ВТОРОСТЕПЕННАЯ — «Канал»: это
   единственная колонка, чьё значение целиком повторено в самой карточке
   («Канал · Telegram») и в фильтре над таблицей, и единственная, по которой не
   ищут глазами. Прежде вместо неё уступал НОМЕР заказа — имя строки, — и это
   было ровно наоборот.

   Порог 90rem, а не «всегда»: на 1600 и шире шесть колонок помещаются без
   натуги, и прятать там нечего. Ниже 78rem карточка встаёт над таблицей во всю
   ширину — колонка возвращается сама. */
@media (min-width: 78.01rem) and (max-width: 90rem) {
  .orders-layout.has-detail .otable th:nth-child(5),
  .orders-layout.has-detail .otable td:nth-child(5) {
    display: none;
  }
}

@media (max-width: 56rem) {
  :root {
    --dotnum-deck: 3rem;
    --dotnum-deck-sm: 2.3rem;
  }

  /* На телефоне кнопки экрана становятся сеткой во всю ширину (app.css,
     «тач-габариты»), и три иконки вставали столбиком на три строки. Здесь
     они остаются рядом. */
  #tab-orders .head-actions {
    width: auto;
    display: flex;
  }

  /* 🔴 `width: auto` без нижней меры ужимал кнопку до самой иконки — замерено
     в браузере: 17×34 px на 390 и на 768. Общее мобильное правило app.css
     («тач-габариты», min-height 2,75rem) сюда не достаёт: оно исключает
     `.bare`, а все три кнопки шапки — `.bare`. Попасть пальцем в 17 px нельзя,
     поэтому мера возвращается здесь — квадратом 44×44, как требует чек-лист. */
  #tab-orders .head-actions > button {
    width: auto;
    min-width: 2.75rem;
    min-height: 2.75rem;
    grid-column: auto;
  }

  /* 🔵 M-13 (мобильный аудит 24.09): на 430 веер занимал 466 px — правая
     карта уходила за край окна, подпись «При получении наложенным платежом…»
     теряла правый край, левая карта вылезала за левое поле. Ширина карт
     теперь ещё и доля окна (`36vw`/`31vw`), а заход боковых под центральную
     глубже: веер целиком в окне на 320…896. */
  .odeck-card {
    width: min(10.6rem, 36vw);
    min-height: 14.5rem;
    padding: var(--s3);
  }

  .odeck-card--l,
  .odeck-card--r {
    width: min(9.4rem, 31vw);
    min-height: 12.8rem;
    padding: var(--s3) var(--s2) var(--s2);
  }

  .odeck-card--l {
    margin-right: -1.4rem;
  }

  .odeck-card--r {
    margin-left: -1.4rem;
  }

  /* 🔴 Ряд веера на телефоне идёт ВО ВСЮ ШИРИНУ ОКНА, а поля возвращаются ему
     же отступом. Смысл ровно один: клип (см. `.odeck-row` выше) режет по
     краю padding-box, и по краю КОЛОНКИ он срезал бы углы повёрнутых карт на
     ~16 px — на 391 у левой карты пропадала первая буква, «жидается» вместо
     «Ожидается» (замерено снимком). С полной шириной край клипа совпадает с
     краем окна: пропадает ровно то, чего и так не видно, а внутренняя
     раскладка не меняется — колонка внутри ряда той же ширины, что была. */
  .odeck-row {
    margin-inline: calc(-1 * var(--gutter));
    padding-inline: var(--gutter);
  }

  /* Приборы и плитки встают столбиком: на 56 rem трёхколоночная обойма даёт
     плитку в 11 rem, а в ней цифра из точек уже не читается. */
  .ostats,
  .otrio {
    grid-template-columns: minmax(0, 1fr);
  }

  .ostats > .ostat:last-child {
    grid-column: auto;
  }

  .orders-panel-head {
    flex-wrap: wrap;
    padding: var(--s4) var(--s4) 0;
  }

  /* 🔴 Замерено в браузере на 390 px: ряд фильтров тянул документ до 433 px и
     страница ездила вбок. Пилюль в наборе шесть, и на телефоне они обязаны
     прокручиваться ВНУТРИ своей дорожки, а не растягивать экран, — для этого
     дорожка получает всю ширину ряда, а ряд перестаёт быть строкой. */
  .orders-toolbar {
    flex-direction: column;
    align-items: stretch;
    padding: var(--s3) var(--s4) var(--s4);
  }

  .osegs {
    width: 100%;
  }

  /* M-7: пилюли срезов и каналов на телефоне — цели касания 44 px. */
  button.osegs-btn {
    min-height: 2.75rem;
    padding-inline: 0.85rem;
  }

  .orders-search {
    flex-basis: 100%;
    min-height: 2.75rem;
  }

  /* Поле поиска — вся высота рамки: рамка была 44 px, а само поле 32, и
     касание над или под текстом в поле не попадало. Теперь само поле 44. */
  .orders-search input {
    align-self: stretch;
    min-height: 2.75rem;
  }

  /* Цели касания экрана, которые общий блок «тач-габаритов» (app.css) не
     достаёт — все они `.bare`: стрелки «↗» на картах веера и приборах
     (26–32 px), пилюля сортировки (24 px по высоте). Вид не меняется —
     растёт только зона нажатия, невидимым слоем вокруг. */
  #tab-orders .g-arrow::after,
  #orders-sort::after {
    content: "";
    position: absolute;
    inset: -0.5rem;
  }

  #orders-sort {
    position: relative;
  }

  #orders-sort::after {
    inset: -0.65rem -0.25rem;
  }

  /* Строка запроса: два вопроса и две квадратные кнопки в одну строку на
     390 px не помещаются — переносим. Её кнопки — 26 px по умолчанию;
     на телефоне они 44 px, как требует чек-лист касаний. */
  .orders-ask .askbar-row {
    flex-wrap: wrap;
    row-gap: var(--s2);
  }

  .orders-ask .askbar-round,
  .orders-ask .askbar-sq {
    width: 2.75rem;
    height: 2.75rem;
  }

  .orders-ask .askbar-pill {
    height: 2.75rem;
  }

  .orders-empty,
  .orders-foot {
    padding-inline: var(--s4);
  }

  /* ---------------------------------------- карточка заказа — шторка
     На телефоне карточка вставала в поток НАД таблицей, под веером,
     приборами и кольцом: чтобы открыть заказ из середины списка, экран
     уезжал на тысячи пикселей вверх, а после «Закрыть» место в списке
     терялось. Теперь это полноэкранная шторка поверх пульта (и шапки, и
     панели вкладок — ниже только тосты и окна): список под ней стоит где
     стоял, закрывается «Закрыть», Esc и системным «назад» — адрес тот же
     `#/orders?id=…`.

     Фон непрозрачный (`--surface-flat`, как у `.glass--flat`): полупрозрачное
     стекло во весь экран показало бы под текстом карточки строки таблицы.
     `fixed` здесь работает, потому что у предков карточки нет ни
     `transform`, ни `filter`, ни `backdrop-filter` — это проверяет смоук. */
  .order-detail:not([hidden]) {
    position: fixed;
    inset: 0;
    z-index: 150;
    margin: 0;
    padding: 0 var(--gutter, var(--s4)) calc(var(--s6) + env(safe-area-inset-bottom, 0px));
    overflow-y: auto;
    overscroll-behavior: contain;
    border: 0;
    border-radius: 0;
    outline: 0;
    box-shadow: none;
    background: var(--surface-flat);
    animation-name: odSheetUp;
  }

  .order-detail-head {
    position: sticky;
    top: 0;
    z-index: 2;
    align-items: center;
    margin: 0 calc(-1 * var(--gutter, var(--s4))) var(--s4);
    padding: calc(var(--s2) + env(safe-area-inset-top, 0px)) var(--gutter, var(--s4)) var(--s2);
    background: var(--surface-flat);
    border-bottom: 1px solid var(--line);
  }

  .order-close {
    display: inline-flex;
    align-items: center;
    flex: none;
    min-height: 2.75rem;
    padding: 0 var(--s3);
    margin-right: calc(-1 * var(--s3));
    border-bottom: 0;
    font-weight: 600;
  }

  .ochips {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Таблица на телефоне — карточки: подпись колонки уходит в `data-label`,
     номер заказа занимает всю ширину строки. */
  .otable thead {
    display: none;
  }

  .otable tbody {
    display: block;
  }

  .otable tbody tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--s3);
    padding: var(--s4);
    border-bottom: 1px solid var(--line-2);
  }

  .otable tbody tr:last-child {
    border-bottom: 0;
  }

  /* 🔴 M-6 (мобильный аудит 24.09): `width: 34%` у «Города и доставки» и
     `min-width: 8.5rem` у номера — меры ТАБЛИЦЫ. В сетке карточки процент
     считается от ячейки сетки, и колонка адреса сжималась до ≈50 px (41 на
     320): «Відділення № 12», «ТТН 20458831 7702», «Дніпро» вставали по
     букве в строку — вдобавок к общему `.tab td { overflow-wrap: anywhere }`
     из layout.css. В карточке мер таблицы нет, а слово рвётся только тогда,
     когда целиком не помещается в строку. */
  .otable td,
  .otable td:first-child,
  .otable td:nth-child(2) {
    display: block;
    width: auto;
    min-width: 0;
    padding: 0;
    border: 0;
    overflow-wrap: break-word;
  }

  /* Адрес — единственное длинное поле карточки: ему строка целиком, как и
     номеру. Сумма · оплата и канал · статус встают парами под ним. */
  .otable td:nth-child(2) {
    grid-column: 1 / -1;
  }

  .otable td:first-child .osub {
    white-space: normal;
  }

  /* На 320 полкарточки — 121 px, а «Наложка · ждём 200 ₴» в одну строку —
     150: чип выпирал из карточки. В карточке он переносится по словам. */
  .otable .ochip {
    max-width: 100%;
    white-space: normal;
  }

  /* Вся карточка — одна цель касания: ссылка номера растянута на строку
     (`::after`), а не 94×21 px текста. С клавиатуры фокус по-прежнему на
     ссылке, чипы оплаты и канала — подписи, а не кнопки, и нажатие по ним
     тоже открывает заказ. */
  .otable tbody tr {
    position: relative;
  }

  .otable a.onum::after {
    content: "";
    position: absolute;
    inset: 0;
  }

  .otable a.onum:focus-visible {
    outline: none;
  }

  .otable tbody tr:has(a.onum:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
  }

  .otable tbody tr:hover td,
  .otable tbody tr.is-on td {
    background: none;
  }

  .otable tbody tr.is-on {
    background: var(--ink-700);
    box-shadow: inset 3px 0 0 var(--accent);
  }

  .otable td:first-child {
    grid-column: 1 / -1;
  }

  /* Кегль карточки — телефонный: текст 14 px, подписи и вторые строки
     12 px (на 10,6 px капслок-меры таблицы подпись читается с трудом). */
  .otable {
    font-size: var(--t-base);
  }

  .otable td::before {
    content: attr(data-label);
    display: block;
    margin-bottom: var(--s1);
    color: var(--mute);
    font-size: var(--t-xs);
    font-weight: 500;
    letter-spacing: normal;
  }

  .otable .osub {
    font-size: var(--t-xs);
  }

  .otable td:first-child::before {
    display: none;
  }

  .otable .right {
    text-align: left;
  }
}

/* M-13: наклон покоя на телефоне ∓6°, а не ∓9°. Повёрнутая карта расширяет
   свой габарит на h·sin(угла) — на 9° это 30–36 px с каждой стороны, и веер
   не помещался в окно ни на 320 (348 px), ни на 375 (393), ни на 430 (466).
   Расхождения под курсором на телефоне нет (см. ниже) — угол один. */
@media (max-width: 30rem) {
  .odeck:hover .odeck-card--l,
  .odeck-card--l {
    rotate: -6deg;
  }

  .odeck:hover .odeck-card--r,
  .odeck-card--r {
    rotate: 6deg;
  }
}

/* 390 px: веер обязан уместиться целиком. Замерено в браузере — на прежних
   9,6 + 8,4 rem левая карточка уезжала за край экрана. */
@media (max-width: 26rem) {
  :root {
    --dotnum-deck: 2.6rem;
    --dotnum-deck-sm: 2rem;
  }

  /* 🔴 На 360 px поле и кнопка рядом не помещаются: «Отправлен» ужимался до
     трёх букв, а ТТН — до четырёх цифр. Каждый орган встаёт своей строкой во
     всю ширину; горизонтальной прокрутки на телефоне в пульте нет нигде. */
  .oact-row > div,
  .oact-row > button,
  .oact-now {
    flex: 1 1 100%;
    width: 100%;
  }

  .odeck {
    padding-inline: 0;
  }

  /* M-13: ширина карт — ещё и доля окна: на 320 веер в 8,4 + 2 × 7,2 rem
     не помещается ни при каком заходе. */
  .odeck-card {
    width: min(8.4rem, 36vw);
    min-height: 12.2rem;
    padding: var(--s3) var(--s2);
  }

  .odeck-card--l,
  .odeck-card--r {
    width: min(7.2rem, 31vw);
    min-height: 10.6rem;
    padding: var(--s2);
  }

  .odeck-card--l {
    margin-right: -1.4rem;
  }

  .odeck-card--r {
    margin-left: -1.4rem;
  }

  .odeck-card .g-cap {
    font-size: var(--t-xs);
  }

  .odeck-card .g-sub {
    font-size: var(--t-micro);
  }

  /* Параллакса и расхождения веера на телефоне нет: палец наводится не так,
     как мышь, и «наведение» на нём означает нажатие. */
  .odeck-card:hover,
  .odeck-hero:hover,
  .ostat:hover {
    translate: none;
  }

  /* Кольцо на 390 px меньше: панель во всю ширину, но круг в 8,4 rem рядом с
     легендой из пяти строк выталкивал бы подпись за сгиб. */
  .oring .dn {
    width: 7.2rem;
    height: 7.2rem;
  }

  #od-ring:empty {
    width: 7.2rem;
    height: 7.2rem;
  }
}

/* ================================================== ДВИЖЕНИЕ ВЫКЛЮЧЕНО
   Четвёртый уровень выключателя живёт в js (`REDUCED` из app.js): каскад
   строк, зажигание цифр и рисование волн ставятся классами, и css их не
   достанет.

   Общий блок app.css уже сокращает длительности до 0,001 мс. Здесь именно
   `animation: none` — чтобы «выключено» можно было ПРОВЕРИТЬ: смоук читает
   `animationName` и ждёт `none`, а не «идёт, но быстро». */
@media (prefers-reduced-motion: reduce) {
  .odeck-card,
  .odeck-hero::before,
  .odeck-hero::after,
  #tab-orders .ostat-head .blob,
  #tab-orders .orders-panel-head .blob,
  .order-detail:not([hidden]),
  .oact-fix:not([hidden]),
  .oact-link:not([hidden]),
  .osug-list:not([hidden]),
  button.osegs-btn[data-view]::before,
  .orders-search.is-shake,
  .otable tbody tr.is-new::after,
  .otrio .gcard,
  #od-days.is-grown .stem,
  #od-days.is-grown .val,
  #od-days.is-grown .lbl,
  #od-days:empty::after,
  #od-ring:empty::after,
  #od-ring-leg:empty::after,
  #od-eva:empty::after,
  #od-eva-spark:empty::after {
    animation: none !important;
  }

  .otable tbody tr.is-new::after {
    opacity: 0;
  }

  .odeck-card:hover,
  .odeck-hero:hover,
  .ostat:hover {
    translate: none;
  }
}
