/* СОБРАНО из assets/ui (base/atoms/molecules/organisms) по assemble из assets/ui/kit.json — не редактировать здесь, правьте источники и запустите node tools/sync-ui.mjs --write. Версия kit 0.1.0 (assets/ui/kit.json). */
/* Общие компоненты «Чёрной галереи»: Mini App и управление. Источник —
 * assets/ui/components.css, копии раскладывает `node tools/sync-ui.mjs`.
 * Требует tokens.css рядом.
 *
 * Референсы (Refero), к которым восходит каждый компонент:
 *   группа с подписью и счётчиком — Asana «My Tasks» (группировка по
 *     статусу), переведено в моноширинную подпись капслоком и линию до края;
 *   строка со знаком и мета-строкой — Cofounder «Logs»: знак слева,
 *     заголовок, под ним время/подробность тише;
 *   строки «подпись — значение» вместо таблицы — The Outsiders «Personal &
 *     Fitness Details»: подпись слева, значение справа, линии между;
 *   список разделов — Mela «Settings»: крупный заголовок с засечками над
 *     списком разделов, каждый пункт ведёт внутрь;
 *   честное пустое/недоступное — SSENSE «Account»: одна спокойная строка
 *     вместо плашки.
 * Всё без теней, без скруглений, без цвета: форма и подпись вместо окраски.
 */

* {
  box-sizing: border-box;
}

[hidden] {
  display: none !important;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
/* ── Гарнитуры (BRANDBOOK §5.1–5.2, аудит шрифтов 28.09) ─────────────
   Текст — Dony Sans; заголовки и знак — Dony Display (собственный шрифт,
   решение Руководителя проекта 27.09; в kit с 28.09, версия 0.201: Regular
   400 для заголовков, Medium 500 — знак, atoms/mark.css). Dony Serif нет ни
   в kit, ни в приложениях: «три голоса — много», Display заменяет Serif
   (§5.2; сторож tools/check-no-serif.mjs).
   Файлы — копии assets/brand/fonts/ и ops/font-dony в
   <приложение>/public/fonts/, свои у каждого приложения (CSP: style-src
   'self', сторонних источников нет). Чего в файле нет (у Display — «|»),
   дорисует следующий шрифт стопки (--ui/--display в tokens/semantic.css). */

@font-face {
  font-family: 'Dony Display';
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url('../fonts/DonyDisplay-Regular.woff2') format('woff2');
}

@font-face {
  font-family: 'Dony Sans';
  font-weight: 300;
  font-style: normal;
  font-display: swap;
  src: url('../fonts/DonySans-Light.woff2') format('woff2');
  unicode-range: U+0020-007E, U+00A0, U+00A7, U+00AB, U+00BB, U+00D3, U+00F3,
    U+0400-045F, U+0490-0491, U+2013-2014, U+2018-2019, U+201C-201E, U+2026,
    U+20BD, U+2116;
}

@font-face {
  font-family: 'Dony Sans';
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url('../fonts/DonySans-Regular.woff2') format('woff2');
  unicode-range: U+0020-007E, U+00A0, U+00A7, U+00AB, U+00BB, U+00D3, U+00F3,
    U+0400-045F, U+0490-0491, U+2013-2014, U+2018-2019, U+201C-201E, U+2026,
    U+20BD, U+2116;
}

@font-face {
  font-family: 'Dony Sans';
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  src: url('../fonts/DonySans-Medium.woff2') format('woff2');
  unicode-range: U+0020-007E, U+00A0, U+00A7, U+00AB, U+00BB, U+00D3, U+00F3,
    U+0400-045F, U+0490-0491, U+2013-2014, U+2018-2019, U+201C-201E, U+2026,
    U+20BD, U+2116;
}
/* ── Знак и имя ─────────────────────────────────────────────────────
   Знак — единственный источник assets/brand/mark.svg (сверка
   tools/check-mark.mjs); контур D наследует currentColor и
   переворачивается вместе с темой сам, акут — латунь темы (--accent:
   #c9a15a на тёмной, #8b631c на светлой). Правило CSS сильнее атрибута
   fill="#c9a15a" в разметке — атрибут остаётся запасным цветом там, где
   этой таблицы нет. */

.mark {
  width: 28px;
  height: 28px;
  flex: none;
  color: var(--bone);
}

/* Словесный знак «Dóny» (BRANDBOOK §2, §4.3, §5.2): Dony Display Medium,
   строчными, латунью — только акут над «ó». Файл шрифта — копия
   assets/brand/fonts/DonyDisplay-Medium.woff2 в <приложение>/public/fonts/
   (сверка — tools/check-wordmark.mjs). */
@font-face {
  font-family: 'Dony Display';
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  src: url('../fonts/DonyDisplay-Medium.woff2') format('woff2');
}

.wordmark {
  margin: 0;
  font-family: 'Dony Display', var(--font-ui, system-ui), sans-serif;
  font-size: var(--fs-display-sm);
  font-weight: 500;
  letter-spacing: -0.005em;
  text-transform: none;
  color: var(--bone);
}

/* Акут латунью, сама «о» — цветом текста. Фон режется по глифу: выше
   0.26em от верха строки (у Dony Display это над x-высотой, typo-ascent
   0.79em, x 0.50em) — латунь, ниже — кость. */
.wm-acute {
  background: linear-gradient(180deg, var(--accent) 0.26em, var(--bone) 0.26em);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.mark-acute {
  fill: var(--accent);
}

/* Знак или слово, никогда вместе; выбор по месту (BRANDBOOK §3.4,
   Руководитель проекта 27.09). Где в разметке стоят оба, виден один:
   слово «Dóny» — если хватает места, знак «Dó» — в узкой шапке (уже
   360 px). display:none убирает скрытое и из дерева доступности, поэтому
   имя не читается дважды. Сверка на отрисованных шапках —
   tools/tests/lockup.test.mjs. */
.mark:has(~ .wordmark) {
  display: none;
}

@media (max-width: 359.98px) {
  .mark:has(~ .wordmark) {
    display: block;
  }

  .mark ~ .wordmark {
    display: none;
  }
}
/* ── Заголовок экрана и сводка ─────────────────────────────────────── */

.title {
  margin: 0;
  font: 400 var(--fs-title) / 1.08 var(--display);
  letter-spacing: -0.01em;
  color: var(--bone);
  text-wrap: balance;
}

.lead {
  margin: 14px 0 0;
  max-width: 32ch;
  font-size: var(--fs-lead);
  line-height: 1.45;
  color: var(--mist);
  text-wrap: pretty;
}

/* Строка капслоком Dony Sans: дата, роль, свежесть среза. */
.eyebrow {
  margin: 0;
  font: 400 var(--fs-meta) / 1.4 var(--ui);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ash);
}
/* ── Знак ──────────────────────────────────────────────────────────── */

.icon {
  width: 20px;
  height: 20px;
  flex: none;
  /* Штрих задаётся здесь, а не стилем внутри набора: свойства наследуются
     в теневое дерево <use>, а встроенный <style> внешнего SVG под CSP не
     обязан примениться. */
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
  stroke-linecap: square;
  stroke-linejoin: miter;
}
/* ── Группа с подписью и счётчиком ─────────────────────────────────── */

.group {
  margin-top: var(--gap-group);
}

.group-title {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  font: 500 var(--fs-meta) / 1 var(--ui);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash);
}

.group-title::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--hairline);
}

.group-count {
  color: var(--mist);
}

.group-note {
  margin: 4px 0 0;
  font-size: var(--fs-small);
  line-height: 1.45;
  color: var(--ash);
}

/* То, что требует внимания, отделено формой, а не цветом. */
.group[data-status='waiting_human'] .items,
.group[data-attention] .items,
.group[data-attention] .facts {
  border: 1px solid var(--hairline-strong);
  padding: 0 16px;
}
/* ── Строка со знаком и мета-строкой ───────────────────────────────── */

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

.item {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  padding: var(--gap-row) 0;
  border-bottom: 1px solid var(--hairline);
  color: var(--bone);
}

.items > .item:last-child,
.items-entry:last-child > .item {
  border-bottom: 0;
}

.item .icon {
  margin-top: 2px;
}

.item-body {
  min-width: 0;
  flex: 1;
}

.item-title {
  margin: 0;
  font-size: var(--fs-item);
  line-height: 1.35;
  color: var(--bone);
  overflow-wrap: anywhere;
}

.item-meta {
  margin: 4px 0 0;
  font: 400 13px / 1.4 var(--ui);
  letter-spacing: 0.02em;
  color: var(--ash);
  overflow-wrap: anywhere;
  /* Разбивка по словам, не как попало: без этого длинная мета-строка на
     узком экране рвалась на 2–3 строки и оставляла хвост (время) висеть
     одним словом на своей строке. Составители строк (dialogs-view.js и
     похожие) склеивают то, что не должно рваться, неразрывным пробелом —
     здесь только «не оставляй сирот», где браузер это умеет. */
  text-wrap: pretty;
}

/* Значение справа от заголовка строки (число, версия) — Dony Sans,
   мельче заголовка и подвинуто вниз, чтобы сесть на его базовую линию —
   без этого казалось крупнее самого имени и «плавало» над строкой
   (замечание оператора 25.09 — вёрстка, п.4). */
.item-value {
  flex: none;
  margin-left: auto;
  margin-top: 4px;
  font: 400 13px / 1.3 var(--ui);
  color: var(--mist);
}

.group[data-status='done'] .item-title,
.group[data-status='failed'] .item-title,
.item[data-tone='quiet'] .item-title {
  color: var(--mist);
}

/* Строка-ссылка: весь ряд нажимается, справа тонкая угловая стрелка из
   тех же линий в 1px, что и знаки набора. */
button.item,
a.item {
  appearance: none;
  width: 100%;
  background: none;
  border-width: 0 0 1px;
  border-style: solid;
  border-color: var(--hairline);
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

/* Шеврон садится на первую строку (имя), не на середину всей карточки —
   иначе на многострочных строках (мета в 2 строки, длинное имя) он
   съезжал вниз относительно имени (замечание оператора 25.09 — вёрстка,
   п.5). 7px сверху — середина строки заголовка (line-height 1.35 × 17px)
   с поправкой на сам знак 8×8. */
button.item::after,
a.item::after {
  content: '';
  flex: none;
  align-self: flex-start;
  width: 8px;
  height: 8px;
  margin-top: 7px;
  margin-right: 4px;
  border-top: 1px solid var(--ash);
  border-right: 1px solid var(--ash);
  transform: rotate(45deg);
}

button.item:focus-visible,
a.item:focus-visible {
  outline: 1px solid var(--accent);
  outline-offset: 2px;
}

button.item[aria-current='page'] .item-title {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 5px;
}

@media (hover: hover) {
  button.item:hover .item-title,
  a.item:hover .item-title {
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 5px;
  }
}
/* ── Подпись — значение (вместо таблицы) ───────────────────────────── */

.facts {
  margin: 8px 0 0;
  padding: 0;
}

.fact {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 4px 16px;
  padding: 14px 0;
  border-bottom: 1px solid var(--hairline);
}

.fact:last-child {
  border-bottom: 0;
}

.fact dt {
  color: var(--ash);
  font-size: 15px;
}

.fact dd {
  margin: 0;
  min-width: 0;
  max-width: 100%;
  color: var(--bone);
  font-size: 15px;
  text-align: right;
  overflow-wrap: anywhere;
}

.fact dd[data-mono] {
  /* Исключение из «везде Dony Sans» (РП 30.09): идентификатор для сверки и
     копирования; вход — tools/check-no-mono.mjs, ALLOWED. */
  font-family: var(--mono);
  font-size: 14px;
  letter-spacing: 0.02em;
}

.fact[data-tone='quiet'] dd {
  color: var(--ash);
}
/* ── Честное пустое или недоступное ────────────────────────────────── */

.quiet {
  margin: 12px 0 0;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 15px;
  line-height: 1.5;
  color: var(--ash);
  overflow-wrap: anywhere;
}

.quiet .icon {
  margin-top: 2px;
}
/* ── Действия и поля ───────────────────────────────────────────────── */

.action {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 20px;
  border: 1px solid var(--hairline-strong);
  border-radius: 2px;
  background: transparent;
  color: var(--bone);
  font: 500 var(--fs-body) / 1 var(--ui);
  letter-spacing: 0.01em;
  cursor: pointer;
}

.action[data-primary] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--ink);
}

.action:active {
  background: var(--graphite);
}

.action[data-primary]:active {
  opacity: 0.85;
}
/* Текстовое действие без рамки: «Выйти», «← Управление». */
.link {
  appearance: none;
  padding: 0;
  border: 0;
  background: none;
  font: 400 var(--fs-meta) / 1.4 var(--ui);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ash);
  text-decoration: none;
  cursor: pointer;
}

.link:hover,
.link:focus-visible {
  color: var(--bone);
}

.link:focus-visible {
  outline: 1px solid var(--accent);
  outline-offset: 2px;
}
.field {
  display: block;
  margin: 18px 0 0;
  font: 500 var(--fs-meta) / 1.4 var(--ui);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ash);
}

.field input {
  display: block;
  width: 100%;
  margin-top: 8px;
  padding: 10px 0;
  border: 0;
  border-bottom: 1px solid var(--hairline-strong);
  border-radius: 0;
  background: transparent;
  color: var(--bone);
  font: 400 var(--fs-body) / 1.4 var(--ui);
  letter-spacing: 0.02em;
  text-transform: none;
}

.field input:focus {
  outline: none;
  border-bottom-color: var(--accent);
}

/* select — тот же приём, что .field input: без него подпись и раскрывашка
 * остаются на одной строке (select по умолчанию inline-block), а не
 * подписью сверху и полем на всю ширину под ней (суперадмин: фильтры
 * журналов, apps/admin/public/ops-screens.js). */
.field select {
  display: block;
  width: 100%;
  margin-top: 8px;
  padding: 10px 0;
  border: 0;
  border-bottom: 1px solid var(--hairline-strong);
  border-radius: 0;
  background: transparent;
  color: var(--bone);
  font: 400 var(--fs-body) / 1.4 var(--ui);
  letter-spacing: 0.02em;
  text-transform: none;
}

.field select:focus {
  outline: none;
  border-bottom-color: var(--accent);
}
/* ── Пункт меню с замком и счётчиком (ui-menu-item) ────────────────
 * SPEC-345 §А9: «Управление» в меню шапки — пункт kit, а не класс
 * оболочки. Замок и счётчик — тихие, справа; подпись — как у пунктов
 * меню. Зона касания 44. */

.ui-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  padding: 0 10px;
  border: 0;
  border-radius: 2px;
  background: transparent;
  color: var(--bone);
  font: 400 var(--fs-body) / 1.3 var(--ui);
  text-align: left;
  cursor: pointer;
}

.ui-menu-item:hover,
.ui-menu-item:focus-visible {
  background: var(--graphite);
}

.ui-menu-item-title {
  flex: 1 1 auto;
  min-width: 0;
}

.ui-menu-item-count {
  font: 500 var(--fs-meta) / 1.4 var(--ui);
  color: var(--ash);
}

/* Пункт, который виден, но недоступен: причина названа в подписи, поэтому
   гасить его нельзя — иначе человек решает, что потерял доступ (#3006). */
.ui-menu-item[data-state="unavailable"] {
  cursor: default;
  color: var(--ash);
}

.ui-menu-item[data-state="unavailable"]:hover {
  background: none;
}

.ui-menu-item .icon {
  flex: none;
  color: var(--ash);
}

@media (prefers-reduced-motion: no-preference) {
  .ui-menu-item {
    transition: background-color 140ms ease;
  }
}
/* ── Лист подтверждения ключом доступа (ui-confirm-sheet) ──────────
 * SPEC-345 §Б3: суть действия от сервера, «Подтвердить на устройстве»,
 * отмена. Один на клиента и сотрудника; построитель — ui.confirmSheet().
 * Референс (Refero): нижний лист подтверждения оплаты — суть крупно, под
 * ней одна строка о способе, действие во всю ширину. Здесь без
 * скруглений и теней: лист отделён линией и затемнением фона. На телефоне
 * прижат к низу (палец), на широком экране — посередине. */

.ui-confirm-sheet {
  width: 100%;
  max-width: 100%;
  max-height: 100%;
  margin: auto 0 0;
  padding: 24px var(--gap-page) calc(20px + var(--pad-bottom, 0px));
  border: 0;
  border-top: 1px solid var(--hairline-strong);
  border-radius: 0;
  background: var(--ink);
  color: var(--bone);
  overflow-wrap: anywhere;
  opacity: 0;
  /* opacity — безусловно, в любом motion-режиме (§6: держите
     цвет/прозрачность, убирайте только смену позиции). overlay/display —
     структурные: держат лист в верхнем слое на время перехода, сами не
     производят видимого движения, тоже безусловны. */
  transition: opacity 280ms cubic-bezier(0.32, 0.72, 0, 1),
    overlay 280ms allow-discrete, display 280ms allow-discrete;
  /* Закрытый лист ещё 280 мс остаётся отрисованным — `display …
     allow-discrete` именно для этого и держит его в верхнем слое. Без
     `pointer-events` он всё это время перехватывает нажатия: тап по
     «исчезающей» кнопке подтверждения выполнил бы действие, которое человек
     только что отменил. Закрытому состоянию hit-testing не нужен. */
  pointer-events: none;
}

.ui-confirm-sheet[open] {
  opacity: 1;
  /* Возврат кликабельности: закрытое состояние гасит hit-testing, открытое
     обязано его вернуть, иначе лист перестал бы принимать нажатия. */
  pointer-events: auto;
}

@starting-style {
  .ui-confirm-sheet[open] {
    opacity: 0;
  }
}

.ui-confirm-sheet::backdrop {
  background: var(--ink);
  opacity: 0;
  transition: opacity 280ms cubic-bezier(0.32, 0.72, 0, 1),
    overlay 280ms allow-discrete, display 280ms allow-discrete;
}

.ui-confirm-sheet[open]::backdrop {
  opacity: 0.72;
}

@starting-style {
  .ui-confirm-sheet[open]::backdrop {
    opacity: 0;
  }
}

/* Сдвиг листа (translateY) — это уже движение, добавляется отдельно,
   только при no-preference. Полный список transition переобъявляется
   целиком (opacity + transform + overlay + display) — CSS не даёт
   «дописать» к уже объявленному transition, только заменить список
   полностью; при равной специфичности это правило, идущее позже в
   файле, побеждает, когда медиа-условие выполняется. */
@media (prefers-reduced-motion: no-preference) {
  .ui-confirm-sheet {
    transform: translateY(100%);
    transition: opacity 280ms cubic-bezier(0.32, 0.72, 0, 1),
      transform 280ms cubic-bezier(0.32, 0.72, 0, 1),
      overlay 280ms allow-discrete, display 280ms allow-discrete;
  }

  .ui-confirm-sheet[open] {
    transform: translateY(0);
  }

  @starting-style {
    .ui-confirm-sheet[open] {
      transform: translateY(100%);
    }
  }
}

@media (min-width: 720px) and (prefers-reduced-motion: no-preference) {
  @starting-style {
    .ui-confirm-sheet[open] {
      /* На широком экране лист центрирован margin:auto (правило ниже в
         файле, не трогать) — выезд на всю высоту читался бы как «из
         середины экрана», поэтому здесь вход короче: лист начинает на
         12px ниже своего места и поднимается вверх в него, а не выезжает
         снизу целиком. */
      transform: translateY(12px);
    }
  }
}

.ui-confirm-sheet-summary {
  margin: 10px 0 0;
  font: 400 var(--fs-display-sm) / 1.2 var(--display);
  color: var(--bone);
  text-wrap: balance;
}

.ui-confirm-sheet-hint {
  margin: 12px 0 0;
  font-size: var(--fs-small);
  line-height: 1.5;
  color: var(--ash);
}

.ui-confirm-sheet-status {
  margin: 16px 0 0;
  padding: 10px 0 0;
  border-top: 1px solid var(--hairline);
  font-size: var(--fs-small);
  line-height: 1.5;
  color: var(--bone);
}

.ui-confirm-sheet[data-state='wait'] .ui-confirm-sheet-status {
  color: var(--ash);
}

.ui-confirm-sheet-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 22px 0 0;
}

.ui-confirm-sheet-actions .action {
  width: 100%;
}

.ui-confirm-sheet-actions .action:disabled {
  opacity: 0.5;
  cursor: default;
}

.ui-confirm-sheet .action:focus-visible {
  outline: 1px solid var(--accent);
  outline-offset: 2px;
}

@media (min-width: 720px) {
  .ui-confirm-sheet {
    width: min(100% - 32px, 480px);
    margin: auto;
    padding: 28px;
    border: 1px solid var(--hairline-strong);
    border-radius: 2px;
  }

  .ui-confirm-sheet-actions {
    flex-direction: row-reverse;
    justify-content: flex-start;
  }

  .ui-confirm-sheet-actions .action {
    width: auto;
  }
}
@media (prefers-reduced-motion: no-preference) {
  .action,
  .link {
    transition: background-color 140ms ease, color 140ms ease;
  }
}
