.kanban-demo-panel {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: var(--shadow);
  padding: 12px;
}

.kanban-demo-box {
  display: grid;
  gap: 12px;
  margin-top: 12px;
}

.kanban-demo-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(180px, 1fr));
  gap: 10px;
}

.kanban-demo-grid label,
.kanban-demo-switches label {
  color: var(--muted);
  font-weight: 800;
  display: grid;
  gap: 6px;
}

.kanban-demo-switches,
.kanban-demo-actions,
.kanban-demo-status,
.kanban-demo-stage-stats {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}

.kanban-demo-status span,
.kanban-demo-stage-stats span {
  background: #eef4ff;
  border: 1px solid #d8e5ff;
  border-radius: 6px;
  padding: 7px 10px;
  font-weight: 800;
}

.kanban-card-demo {
  border: 2px dashed #2f5bd8;
  background: #f7fbff;
}

.status.demo {
  background: #e9f7ef;
  color: #176b3a;
}

.secondary.danger {
  border-color: #f0b9b9;
  color: #9d1c1c;
}

@media (max-width: 900px) {
  .kanban-demo-grid {
    grid-template-columns: 1fr;
  }
}

/* ---- board readability + motion ---------------------------------------- */
/* The card used to carry eight labelled fields, which made every card look the
   same from a metre away. Now: batch number, product, how much, for whom, by
   when. Everything else is one click away. */

.kanban-card {
  position: relative;
  /* Жест остаётся браузеру, пока палец не задержался: доска шире экрана, и
     тем же движением её прокручивают. Прежнее manipulation обещало обратное,
     но отключает только двойной тап - прокрутку оно не отдаёт. */
  touch-action: pan-x pan-y;
  -webkit-touch-callout: none;  /* удержание не должно звать меню iOS */
  display: flex;
  flex-direction: column;
  gap: 6px;
  transition: transform .14s ease, box-shadow .14s ease, border-color .14s ease;
}

/* Появление проигрывается один раз, при первой загрузке. Каскад по трёмстам
 * карточкам с шагом 35 мс - это десять секунд, в течение которых доска
 * выглядит зависшей; при обновлении он не нужен вовсе. */
.kanban-boot .kanban-card {
  animation: kanban-in .28s ease both;
  animation-delay: calc(min(var(--card-index, 0), 12) * 28ms);
}
/* Подъём под курсором - только там, где курсор есть. На стекле :hover
   залипает на карточке, которой коснулись, и пока палец ведёт партию мимо
   соседей, каждая из них поднимается и опускается со своей анимацией. */
@media (hover: hover) {
  .kanban-card:hover { transform: translateY(-2px); box-shadow: 0 10px 24px rgba(15,35,75,.13); }
}
/* Карточка, которую тащат, вынута из потока и едет за курсором. fixed, а не
 * absolute: колонки прокручиваются, и при прокрутке она уезжала бы вместе с
 * содержимым, отставая от пальца. */
.kanban-card.dragging {
  position: fixed; z-index: 90; margin: 0; pointer-events: none;
  cursor: grabbing; transform: rotate(1deg);
  box-shadow: 0 18px 40px rgba(16, 24, 40, .28);
  transition: none;
}
/* Пустышка на месте взятой карточки. Без неё колонка схлопывается под
 * курсором, соседние карточки подпрыгивают, и вся доска дёргается. */
.kanban-ghost {
  border-radius: 8px;
  border: 1px dashed #9db4dd;
  background: rgba(122, 167, 240, .08);
  margin-bottom: 10px;
}
/* Пока идёт перенос, выделение текста только мешает. */
body.kanban-dragging { user-select: none; -webkit-user-select: none; }

/* Карточка взята в руку - с этого мгновения жесты наши, и лист под пальцем
   стоять должен намертво: иначе доска уезжает вместе с переносимой партией. */
body.kanban-holding { touch-action: none; overscroll-behavior: none; }
body.kanban-holding .kanban-card { touch-action: none; }
/* Прокручиваемые контейнеры тоже: инерция iOS доигрывает уже после того, как
   перенос начался, и доска продолжает ехать под неподвижной карточкой. */
body.kanban-holding [data-kanban-board],
body.kanban-holding .kanban-lanes { touch-action: none; overscroll-behavior: contain; }

/* Прилипание колонок на время переноса выключается.

   Просматривать доску с ним удобно: колонка сама встаёт на место. Но во
   время переноса оно спорит с автопрокруткой - скрипт сдвигает доску на
   несколько пикселей, а привязка тут же отматывает её обратно к началу
   колонки. Внутри колонки этого не видно, потому что прокрутки нет; рвётся
   ровно тогда, когда карточку ведут к соседней. */
body.kanban-holding .kanban-board,
body.kanban-holding [data-kanban-board] { scroll-snap-type: none; }

/* Палец лёг - карточка отзывается сразу, ещё до того, как поедет. Без этого
   триста миллисекунд удержания читаются как «не нажалось». */
.kanban-card.is-holding { transform: scale(0.98); transition: transform .12s ease; }
.kanban-card.is-armed {
  transform: scale(1.03);
  box-shadow: 0 10px 24px rgba(16, 32, 64, .22);
  border-color: #2f5bd8;
}

/* Палец толще курсора: на стекле цели крупнее, а наведение не работает вовсе,
   поэтому действия карточки всегда видны, а не проявляются при наведении. */
@media (pointer: coarse) {
  .kanban-card-actions .primary,
  .kanban-card-actions .secondary { min-height: 44px; padding: 8px 10px; font-size: 13px; }
  .kanban-card { padding: 12px; }
  .kanban-card__id { font-size: 17px; }
  .kanban-card__id i { font-size: 12.5px; }
  .kanban-column-search input { min-height: 40px; font-size: 15px; }
  /* 16px - порог, ниже которого Safari сам зумит страницу на фокусе поля. */
  .kanban-filters input, .kanban-filters select { min-height: 44px; font-size: 16px; }
}
body.kanban-dragging .kanban-card { cursor: grabbing; }
/* Пока идёт запрос, карточка уже стоит на новом месте - показываем это
 * приглушением, а не спиннером: движение уже произошло, ждём лишь ответа. */
.kanban-card.is-moving { opacity: .6; }

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

.kanban-card__top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.kanban-card__id {
  font-weight: 700;
  font-size: 15px;
  letter-spacing: .02em;
  font-variant-numeric: tabular-nums;
}
.kanban-card__flag {
  padding: 2px 8px; border-radius: 999px;
  font-size: 11px; font-weight: 600; white-space: nowrap;
}
.kanban-card__flag.is-critical { background: #fdeaec; color: #a3182a; }
.kanban-card__flag.is-urgent   { background: #fff3e0; color: #9a5b00; }

.kanban-card__product { margin: 0; font-size: 14px; font-weight: 600; line-height: 1.3; }
.kanban-card__meta { margin: 0; font-size: 12.5px; color: #5a6b8c; display: flex; gap: 6px; flex-wrap: wrap; }
.kanban-card__foot {
  display: flex; justify-content: space-between; gap: 8px;
  font-size: 12px; color: #6b7a97;
  padding-top: 6px; border-top: 1px dashed #e3e9f5;
}
.kanban-card__who { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kanban-card__due { font-variant-numeric: tabular-nums; white-space: nowrap; }

.kanban-card.is-blocked { border-left: 3px solid #d92f3c; }

.kanban-card__demo {
  position: absolute; top: 6px; right: -1px;
  padding: 1px 6px; border-radius: 3px 0 0 3px;
  background: #eef1f8; color: #8a95ab; font-size: 9.5px; letter-spacing: .06em;
}

/* column: a coloured dot per stage makes the board scannable sideways */
.kanban-head { display: flex; align-items: center; gap: 8px; }
.kanban-head__dot { width: 8px; height: 8px; border-radius: 50%; background: #b9c4da; flex: none; }
[data-stage-code="queue"]     .kanban-head__dot { background: #98a5bd; }
[data-stage-code="mixing"]    .kanban-head__dot { background: #3f7fd8; }
[data-stage-code="forming"]   .kanban-head__dot { background: #4aa3c7; }
[data-stage-code="proofing"]  .kanban-head__dot { background: #d9a72f; }
[data-stage-code="oven"]      .kanban-head__dot { background: #e0722f; }
[data-stage-code="warehouse"] .kanban-head__dot { background: #7a6bd8; }
[data-stage-code="done"]      .kanban-head__dot { background: #2f9e55; }

.kanban-column { transition: background .18s ease, box-shadow .18s ease; }

/* a card that just arrived gets one flash, so a move is visible across the room */
.kanban-card.just-moved { animation: kanban-flash .9s ease-out; }
@keyframes kanban-flash {
  0%   { box-shadow: 0 0 0 0 rgba(47,158,85,.55); border-color: #2f9e55; }
  100% { box-shadow: 0 0 0 16px rgba(47,158,85,0); }
}
.badge.count-changed { animation: kanban-bump .45s ease; }
@keyframes kanban-bump { 40% { transform: scale(1.35); } }

.kanban-empty { padding: 14px 4px; font-size: 12.5px; opacity: .6; text-align: center; }

@media (prefers-reduced-motion: reduce) {
  .kanban-card, .kanban-card.just-moved, .badge.count-changed { animation: none !important; }
}

/* ---- board polish ------------------------------------------------------ */
.kanban-board-large { gap: 14px; padding-bottom: 8px; align-items: start; }

.kanban-column {
  background: #f4f6fb;
  border: 1px solid #e5eaf4;
  border-radius: 14px;
  padding: 10px;
}
.kanban-head {
  padding: 2px 4px 10px;
  font-size: 13px;
  letter-spacing: .01em;
  text-transform: uppercase;
  color: #46536b;
}
.kanban-head strong { flex: 1; font-size: 13px; }
.kanban-head .badge {
  background: #fff; border: 1px solid #dde4f0; color: #46536b;
  border-radius: 999px; min-width: 24px; text-align: center;
  font-size: 12px; font-weight: 700; padding: 1px 7px;
}
.kanban-column-search input {
  width: 100%; margin-bottom: 10px;
  border: 1px solid #e1e7f3; border-radius: 9px;
  padding: 7px 10px; font-size: 12.5px; background: #fff;
}
.kanban-column-search input:focus { outline: 2px solid #a9c4f5; outline-offset: -1px; }
.kanban-cards { display: flex; flex-direction: column; gap: 8px; }

.kanban-card {
  background: #fff;
  border: 1px solid #e4e9f4;
  border-radius: 10px;
  padding: 9px 10px;
  gap: 6px;
  box-shadow: 0 1px 2px rgba(16,32,64,.05);
}
.kanban-card__id { color: #16233d; font-size: 13px; }
.kanban-card__batch-ref { margin-left: auto; color: #71809b; font-size: 10.5px; white-space: nowrap; }
.kanban-card h3.kanban-card__product { color: #24314c; font-size: 15px; line-height: 1.25; }
.kanban-card__meta { font-size: 11.5px; }
.kanban-card__foot { padding-top: 4px; font-size: 11.5px; }
.kanban-card__meta strong { color: #16233d; font-weight: 700; }
.kanban-card__products { display: grid; gap: 4px; }
.kanban-card__products > div {
  display: flex; justify-content: space-between; gap: 8px;
  padding: 4px 0; border-bottom: 1px solid #edf0f6;
  font-size: 12px; line-height: 1.25;
}
.kanban-card__products > div:last-child { border-bottom: 0; }
.kanban-card__products span { min-width: 0; }
.kanban-card__products strong { flex: none; color: #16233d; white-space: nowrap; }

/* Actions are secondary to reading the board: quiet until the card is hovered,
   so a column of cards reads as information rather than a wall of buttons. */
.kanban-card-actions { display: flex; flex-wrap: nowrap; gap: 5px; margin-top: 0; }
.kanban-card-actions form,
.kanban-card-actions > .secondary { flex: 1 1 0; min-width: 0; margin: 0; }
.kanban-card-actions .primary,
.kanban-card-actions .secondary {
  width: 100%; min-height: 36px; padding: 5px 6px; border-radius: 8px;
  font-size: 12px; font-weight: 600; text-align: center;
  border: 1px solid transparent; cursor: pointer;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.kanban-card-actions .primary { background: #eef3ff; color: #2f5bd8; }
.kanban-card-actions .secondary {
  background: #fff; border-color: #e1e7f3; color: #61708c;
  display: inline-flex; align-items: center; justify-content: center; text-decoration: none;
}
.kanban-card:hover .kanban-card-actions .primary { background: #2f5bd8; color: #fff; }
.kanban-card-actions .secondary:hover { border-color: #b9c8e6; color: #2f5bd8; }


/* ---- места оборудования -------------------------------------------------
   Колонка отвечает за этап, дорожка - за устройство. Пустое место должно
   читаться так же быстро, как занятое: на доску смотрят через цех, и вопрос у
   смотрящего один - «куда поставить следующую».

   Поэтому свободное устройство нарисовано пунктирным контуром без заливки
   (место есть, предмета нет), занятое - обычной карточкой на светлом фоне, а
   недоступное приглушено целиком: на него не поставить, и предлагать его
   взгляду незачем.
   -------------------------------------------------------------------------*/
.kanban-lanes {
  display: flex; flex-direction: column; gap: 8px;
  flex: 1; min-height: 0; overflow-y: auto;
  padding-right: 3px;
}
.kanban-cards { flex: none; overflow: visible; padding-right: 0; }

.kanban-lane {
  border-radius: 11px;
  padding: 7px 8px 8px;
  border: 1px dashed #d7dfee;
  background: transparent;
  transition: background .16s ease, border-color .16s ease, box-shadow .16s ease;
}
.kanban-lane.is-busy { border-style: solid; border-color: #e3e9f5; background: #fbfcfe; }
.kanban-lane.is-blocked { opacity: .55; }

/* «Не распределено» - не устройство, а очередь к ним. Сплошная подложка
   отделяет её от мест: карточки в ней лежат стопкой, а не стоят на чём-то. */
.kanban-lane.is-pool {
  border-style: solid;
  border-color: transparent;
  background: rgba(20, 40, 80, .045);
}
/* Своей прокрутки у очереди нет. Полоса внутри полосы - это два разных «вниз»
   в одной колонке, и на доске, которую читают через цех, непонятно, какая из
   них сейчас нужна. Растёт очередь свободно: она стоит последней, и устройства
   над ней остаются на месте. */

.kanban-lane__head {
  display: flex; align-items: center; gap: 8px;
  padding: 0 2px 6px;
  font-size: 11px; letter-spacing: .02em;
}
.kanban-lane__name {
  flex: 1; min-width: 0; font-weight: 600; color: #46536b;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kanban-lane.is-pool .kanban-lane__name { text-transform: uppercase; font-size: 10.5px; color: #61708c; }
.kanban-lane__state { color: #98a5bd; font-size: 10.5px; white-space: nowrap; }
.kanban-lane__state.is-busy { color: #2f5bd8; font-weight: 600; }
.kanban-lane__state.is-blocked { color: #a3182a; font-weight: 600; }
.kanban-lane__count {
  min-width: 20px; text-align: center;
  background: #fff; border: 1px solid #dde4f0; border-radius: 999px;
  color: #46536b; font-size: 11px; font-weight: 700; padding: 0 6px;
}
.kanban-lane__empty {
  margin: 0; padding: 9px 2px; text-align: center;
  font-size: 11.5px; color: #a3adc2;
}

/* Цель броска подсвечивается на дорожке, а не на колонке: колонок семь, а мест
   в них до шести, и «сюда» должно значить конкретное устройство. Занятое место
   отвечает отказом сразу - узнать об этом лучше до броска, чем после. */
.kanban-lane.drop-target {
  border-style: solid; border-color: #7aa7f0;
  background: #eef4ff;
  box-shadow: 0 0 0 3px rgba(122, 167, 240, .22);
}
.kanban-lane.drop-refused {
  border-style: solid; border-color: #e79aa3;
  background: #fdf1f2;
  box-shadow: 0 0 0 3px rgba(217, 47, 60, .14);
}
body.kanban-dragging .kanban-lane.drop-refused { cursor: not-allowed; }

/* Загрузка этапа в шапке колонки: «3/5» отвечает на вопрос «есть ли куда
   ставить» без пересчёта дорожек глазами. */
.kanban-head__load {
  font-variant-numeric: tabular-nums;
  font-size: 11px; font-weight: 700; letter-spacing: 0;
  color: #46536b; background: #fff;
  border: 1px solid #dde4f0; border-radius: 999px; padding: 1px 7px;
}
.kanban-head__load.is-full { color: #a3182a; border-color: #f0c9ce; background: #fdf1f2; }

/* Номер партии и её день - один знак, а не число со сноской.

   Нумерация начинается заново каждый производственный день, а на доске
   лежит вся неделя: «01» там встречается столько раз, сколько дней на
   доске. Партию называет только пара «номер + день», поэтому они стоят в
   одной плашке и разделены чертой - прочитать одно, не увидев второго,
   уже нельзя. День тише числа, но не шёпотом: его должно быть видно. */
.kanban-card__id {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 2px 8px; border-radius: 8px;
  background: #eef1f8; border: 1px solid #e0e6f1;
}
.kanban-card__id b { font-weight: 700; }
.kanban-card__id i {
  font-style: normal; font-weight: 600; font-size: 11.5px; color: #5a6a89;
  font-variant-numeric: tabular-nums;
  padding-left: 6px; border-left: 1px solid #ccd5e6;
}
.kanban-card__flags { display: flex; flex-wrap: wrap; gap: 4px; }
.kanban-card__flag.is-demo { background: #eef1f8; color: #8a95ab; font-weight: 500; }
