/*! Navex design-lab · shared/ui.css v1 · 2026-10-08
   Структура и движение для shared/ui.js. ЦВЕТОВ ЗДЕСЬ НЕТ: фон, рамки, тени, радиусы, шрифты задаёт theme.css варианта.
   Единственное исключение — подложка (scrim) через var(--nx-scrim, …) с прозрачным фолбэком.
   Подключать ПОСЛЕ motion-kit.css (берём его токены: --ease, --ease-drawer, --fast, --normal, --sheet, --spring …)
   и ДО theme.css варианта (вариант может переопределить всё).

   Состояние «открыто» везде — класс .is-open (как в проде: Alpine x-show / :class="{'is-open': open}").
   Служебные фазы — data-атрибуты: data-nx-as="modal|sheet|pop", data-nx-side, data-nx-snap, data-nx-closing,
   data-nx-dragging, data-nx-settle. Их ставит ui.js, вариант только читает. */

/* ================================================================
   0. ОБЩЕЕ
   ================================================================ */
:root {
  --nx-ui-z-dropdown: var(--z-dropdown, 30);
  --nx-ui-z-sheet: var(--z-sheet, 40);
  --nx-ui-z-modal: var(--z-modal, 50);
}
#nx-shell { display: contents; }               /* shell.js рисует сюда сайдбар/топбар/нижнюю навигацию — сетка .app-shell видит их напрямую */

/* Блокировка прокрутки страницы под модалкой/шторкой (счётчик в ui.js). gutter — чтобы контент не прыгал на ширину полосы прокрутки */
:root[data-nx-lock] { overflow: hidden; }
:root[data-nx-lock="gutter"] { scrollbar-gutter: stable; }

/* Переходы между страницами (cross-document View Transitions). Анимации и направления — в motion-kit.css §18,
   направление «вперёд/назад» ставит ui.js (sessionStorage 'nx-vt') + скрипт в <head> (см. _template/page.html). */
@view-transition { navigation: auto; }
@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion="full"])::view-transition-group(*),
  :root:not([data-motion="full"])::view-transition-old(*),
  :root:not([data-motion="full"])::view-transition-new(*) { animation-duration: 0s !important; }
}
:root[data-motion="reduce"]::view-transition-group(*),
:root[data-motion="reduce"]::view-transition-old(*),
:root[data-motion="reduce"]::view-transition-new(*) { animation-duration: 0s !important; }

/* ================================================================
   1. СЛОИ: модалка <dialog data-nx-modal>, шторка <dialog data-nx-sheet="bottom|right|left">
   ================================================================ */
dialog:is([data-nx-modal], [data-nx-sheet]) {
  box-sizing: border-box; padding: 0; border: 0; max-width: none; max-height: none;
  color: inherit; overflow: auto; overscroll-behavior: contain;
  opacity: 0;
}
dialog:is([data-nx-modal], [data-nx-sheet])::backdrop {
  background: var(--nx-scrim, rgb(15 15 15 / .42));
  opacity: 0; transition: opacity 150ms cubic-bezier(.23, 1, .32, 1);
}
dialog:is([data-nx-modal], [data-nx-sheet]).is-open::backdrop { opacity: 1; transition-duration: 220ms; }
dialog[data-nx-static]::backdrop { cursor: default; }

/* --- Модалка (ПК). По умолчанию растёт из центра (craft-rules); data-nx-origin="trigger" — от кнопки-открывашки (--nx-origin считает ui.js) --- */
dialog[data-nx-as="modal"] {
  position: fixed; inset: 0; margin: auto;
  width: min(var(--nx-modal-w, 520px), calc(100vw - 32px));
  height: fit-content; max-height: calc(100dvh - 48px);
  transform-origin: var(--nx-origin, 50% 50%);
  scale: var(--enter-scale, .96);
  transition: opacity var(--fast, 150ms) var(--ease, ease-out), scale var(--fast, 150ms) var(--ease, ease-out);
}
dialog[data-nx-as="modal"].is-open { opacity: 1; scale: 1; transition-duration: var(--normal, 220ms); }

/* --- Шторка (общая) --- */
dialog[data-nx-as="sheet"] {
  position: fixed; margin: 0;
  opacity: 1;
  transition:
    translate 280ms var(--ease-drawer, cubic-bezier(.32, .72, 0, 1)),
    opacity 280ms var(--ease, ease-out),
    height var(--slow, 320ms) var(--ease-drawer, cubic-bezier(.32, .72, 0, 1));
}
dialog[data-nx-as="sheet"][open] { display: flex; flex-direction: column; }
dialog[data-nx-as="sheet"].is-open { translate: 0 0; transition-duration: var(--sheet, 420ms), var(--sheet, 420ms), var(--slow, 320ms); }
dialog[data-nx-as="sheet"][data-nx-dragging] { transition: none; user-select: none; -webkit-user-select: none; }
dialog[data-nx-as="sheet"][data-nx-settle] {
  transition: translate var(--spring, 480ms) var(--ease-spring, ease-out), height var(--spring, 480ms) var(--ease-spring, ease-out);
}
dialog[data-nx-as="sheet"]::backdrop { transition-duration: 150ms; }
dialog[data-nx-as="sheet"].is-open::backdrop { transition-duration: 420ms; }

/* Снизу: 55% / 92% экрана или по содержимому (≤ 92%). safe-area — внутри шторки, а не под ней */
dialog[data-nx-as="sheet"][data-nx-side="bottom"] {
  inset: auto 0 0 0; margin-inline: auto;
  width: 100%; max-width: var(--nx-sheet-max-w, 720px);
  height: auto; max-height: 92dvh;
  padding-bottom: env(safe-area-inset-bottom);
  translate: 0 100%;
}
dialog[data-nx-as="sheet"][data-nx-side="bottom"][data-nx-snap="half"] { height: 55dvh; }
dialog[data-nx-as="sheet"][data-nx-side="bottom"][data-nx-snap="full"] { height: 92dvh; }

/* Сбоку: карточка лида справа, фильтры слева. На телефоне — почти во всю ширину */
dialog[data-nx-as="sheet"]:is([data-nx-side="right"], [data-nx-side="left"]) {
  inset: 0 0 0 auto; height: 100dvh; max-height: 100dvh;
  width: min(var(--nx-sheet-w, 480px), calc(100vw - 24px));
  padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom);
  translate: 100% 0;
}
dialog[data-nx-as="sheet"][data-nx-side="left"] { inset: 0 auto 0 0; translate: -100% 0; }
dialog[data-nx-as="sheet"][data-nx-side].is-open { translate: 0 0; }   /* после правил сторон: специфичность выше */

/* Части шторки (необязательные, имена свободные — это рекомендация) */
.nx-sheet__handle {                     /* «ручка»: видимая 36×4, зона захвата больше */
  position: relative; flex: none; width: 36px; height: 4px; margin: 8px auto 4px; border-radius: 2px;
  background: currentColor; opacity: .22; touch-action: none; cursor: grab;
}
.nx-sheet__handle::after { content: ""; position: absolute; inset: -14px -60px; }
dialog:not([data-nx-as="sheet"]) .nx-sheet__handle,
dialog[data-nx-as="sheet"]:is([data-nx-side="right"], [data-nx-side="left"]) .nx-sheet__handle { display: none; }
dialog[data-nx-as="sheet"] [data-nx-drag] { touch-action: none; }
dialog[data-nx-as="sheet"] .nx-sheet__body,
dialog[data-nx-as="sheet"] [data-nx-scroll-area] { flex: 1 1 auto; min-height: 0; overflow: auto; overscroll-behavior: contain; }

/* ================================================================
   2. МЕНЮ / ПОПОВЕР [data-nx-menu] (Popover API, popover="manual"; на телефоне — шторка снизу)
   ================================================================ */
[data-nx-menu][popover] {
  box-sizing: border-box; position: fixed; inset: auto; margin: 0;
  color: inherit; overflow: auto; overscroll-behavior: contain;
  max-height: calc(100dvh - 16px); max-width: calc(100vw - 16px);
  opacity: 0; scale: .98;
  transform-origin: var(--nx-origin, 50% 0);
  transition: opacity var(--fast, 150ms) var(--ease, ease-out), scale var(--fast, 150ms) var(--ease, ease-out), translate var(--fast, 150ms) var(--ease, ease-out);
}
[data-nx-menu][data-nx-as="pop"]:not(.is-open) { translate: 0 var(--nx-pop-y, -4px); scale: var(--enter-scale, .96); }
[data-nx-menu][data-nx-as="pop"][data-nx-closing] { translate: 0 0; scale: .98; }
[data-nx-menu].is-open { opacity: 1; scale: 1; translate: 0 0; transition-duration: var(--normal, 220ms); }
[data-nx-menu][data-nx-instant] { transition: none; }          /* контекстное меню — без анимации */
[data-nx-menu] [role^="menuitem"] { cursor: pointer; }
[data-nx-menu] [role^="menuitem"][aria-disabled="true"] { cursor: default; }
[data-nx-menu]::backdrop { background: transparent; }

[data-nx-menu][data-nx-as="sheet"] {
  inset: auto 0 0 0; width: 100%; max-width: 100%; max-height: 80dvh;
  padding-bottom: max(8px, env(safe-area-inset-bottom));
  opacity: 1; scale: 1; translate: 0 100%;
  transition: translate 280ms var(--ease-drawer, cubic-bezier(.32, .72, 0, 1)), opacity 280ms var(--ease, ease-out);
}
[data-nx-menu][data-nx-as="sheet"].is-open { translate: 0 0; transition-duration: var(--sheet, 420ms); }
[data-nx-menu][data-nx-as="sheet"]::backdrop {
  background: var(--nx-scrim, rgb(15 15 15 / .42)); opacity: 0; transition: opacity 150ms ease;
}
[data-nx-menu][data-nx-as="sheet"].is-open::backdrop { opacity: 1; transition-duration: 300ms; }

/* ================================================================
   3. БОКОВОЕ МЕНЮ [data-nx-sidebar]: ПК — kit (nxSidebar, 248 ↔ 76), телефон — выезжающая панель слева
   ================================================================ */
.nx-scrim {
  position: fixed; inset: 0; z-index: calc(var(--nx-ui-z-sheet) - 1);
  background: var(--nx-scrim, rgb(15 15 15 / .42));
  opacity: 0; pointer-events: none; transition: opacity 280ms var(--ease, ease-out);
}
.nx-scrim[data-on] { opacity: 1; pointer-events: auto; transition-duration: var(--sheet, 420ms); }
/* :not(#nx-strong) поднимает специфичность: theme.css варианта (position: sticky у .sidebar) не сломает шторку */
@media (max-width: 767.98px) {
  :root:not(#nx-strong) [data-nx-sidebar] {
    position: fixed; inset: 0 auto 0 0; z-index: var(--nx-ui-z-sheet);
    width: min(var(--nx-drawer-w, 320px), 86vw); height: 100dvh; max-height: none;
    padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom);
    overflow-y: auto; overscroll-behavior: contain; touch-action: pan-y;
    translate: -100% 0; visibility: hidden;
    transition: translate 280ms var(--ease-drawer, cubic-bezier(.32, .72, 0, 1)), visibility 0s linear 280ms;
  }
  :root:not(#nx-strong) [data-nx-sidebar].is-open { translate: 0 0; visibility: visible; transition: translate var(--sheet, 420ms) var(--ease-drawer, cubic-bezier(.32, .72, 0, 1)), visibility 0s; }
  :root:not(#nx-strong) [data-nx-sidebar][data-nx-dragging] { transition: none; }
  /* в шторке подписи видны всегда, даже если на ПК меню свёрнуто в рейку */
  :root:not(#nx-strong)[data-sidebar] [data-nx-sidebar] .nx-side__label { opacity: 1; }
}

/* ================================================================
   4. ПОДТВЕРЖДЕНИЕ nxConfirm (базовая разметка, если у варианта нет <template id="nx-confirm-tpl">)
   ================================================================ */
.nx-confirm { --nx-modal-w: 440px; }
.nx-confirm__body { padding: 24px; display: grid; gap: 12px; }
.nx-confirm__title { margin: 0; font-size: 1.15em; line-height: 1.3; }
.nx-confirm__text { margin: 0; }
.nx-confirm__type { display: grid; gap: 6px; }
.nx-confirm__type[hidden] { display: none; }
.nx-confirm__input { font: inherit; font-size: max(16px, 1em); min-height: 44px; box-sizing: border-box; width: 100%; }
.nx-confirm__actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; margin-top: 8px; }
@media (max-width: 767.98px) {
  .nx-confirm__actions { flex-direction: column-reverse; }
  .nx-confirm__actions > * { width: 100%; min-height: 48px; }
}

/* ================================================================
   5. OTP [data-nx-otp]: 6 ячеек поверх ОДНОГО input (автозаполнение из SMS, вставка, экранный диктор — нативно)
   ================================================================ */
.nx-otp { position: relative; display: inline-block; max-width: 100%; }
.nx-otp__cells { display: flex; gap: var(--nx-otp-gap, 8px); pointer-events: none; }
.nx-otp__cell {
  position: relative; display: grid; place-items: center; flex: 1 1 0;
  width: var(--nx-otp-w, 48px); min-width: 0; height: var(--nx-otp-h, 56px);
  font-variant-numeric: tabular-nums;
}
.nx-otp__char[data-new] { animation: nx-ui-pop var(--normal, 220ms) var(--ease, ease-out); }
.nx-otp__caret { position: absolute; width: 2px; height: 40%; border-radius: 1px; background: currentColor; opacity: 0; }
.nx-otp[data-focus] .nx-otp__cell[data-active] .nx-otp__caret { animation: nx-ui-caret 1s steps(1) infinite; }
.nx-otp__input {
  position: absolute; inset: 0; width: 100%; height: 100%; box-sizing: border-box;
  margin: 0; padding: 0; border: 0; outline: 0; background: transparent;
  color: transparent; -webkit-text-fill-color: transparent; caret-color: transparent;
  font-size: 16px;                                  /* < 16px — iOS увеличит страницу */
  letter-spacing: -.5em; text-indent: -9999px; cursor: text;
}
.nx-otp__input::selection { background: transparent; }
.nx-otp__input:-webkit-autofill { -webkit-box-shadow: 0 0 0 1000px transparent inset; transition: background-color 600000s; }
@keyframes nx-ui-caret { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
@keyframes nx-ui-pop { from { scale: .6; opacity: 0; } }

/* ================================================================
   6. ЧАТ: статус своего сообщения (nxSendMessage), автовысота поля
   ================================================================ */
.nx-msg__meta { display: inline-flex; align-items: center; gap: 4px; max-width: 100%; white-space: nowrap; font-variant-numeric: tabular-nums; }
.nx-msg[data-status="failed"] .nx-msg__meta { white-space: normal; flex-wrap: wrap; row-gap: 2px; }
.nx-msg__fail, .nx-msg__time { white-space: nowrap; }
.nx-typing[hidden] { display: none; }            /* в motion-kit .nx-typing { display: inline-flex } перебивает hidden */
.nx-msg__status { display: inline-flex; align-items: center; gap: 4px; }
.nx-msg__status svg { width: 1.15em; height: 1.15em; flex: none; animation: nx-ui-pop var(--fast, 150ms) var(--ease, ease-out); }
.nx-msg__status [data-spin] { animation: nx-spin 1.6s linear infinite; transform-origin: 50% 50%; transform-box: fill-box; }
.nx-msg__retry { font: inherit; padding: 0; border: 0; background: none; color: inherit; text-decoration: underline; text-underline-offset: 2px; cursor: pointer; min-height: 24px; }
.nx-msg[data-status="failed"] { opacity: .92; }

textarea[data-nx-autosize] {
  resize: none; field-sizing: content; box-sizing: border-box;
  max-height: calc(var(--nx-ta-rows, 5) * 1lh + var(--nx-ta-pad, 20px));
  overflow-y: auto;
}

/* ================================================================
   7. ⌘K — оверлей поиска (создаёт ui.js; <template id="nx-cmdk-tpl"> варианта — по желанию). По motion.md — без анимации
   ================================================================ */
dialog[data-nx-cmdk-panel][data-nx-as="modal"] {
  inset: 0; margin: min(12dvh, 120px) auto auto;
  width: min(var(--nx-cmdk-w, 640px), calc(100vw - 32px));
  max-height: min(70dvh, 560px);
}
dialog[data-nx-cmdk-panel][open] { display: flex; flex-direction: column; overflow: hidden; }
dialog[data-nx-cmdk-panel], dialog[data-nx-cmdk-panel]::backdrop { transition: none !important; }
.nx-cmdk__head { display: flex; align-items: center; gap: 8px; flex: none; }
.nx-cmdk__input { flex: 1; min-width: 0; font: inherit; font-size: max(16px, 1em); border: 0; outline: 0; background: transparent; color: inherit; min-height: 48px; }
.nx-cmdk__input::-webkit-search-cancel-button { display: none; }
.nx-cmdk__list { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.nx-cmdk__item { display: flex; align-items: center; gap: 12px; min-height: 44px; cursor: pointer; }
.nx-cmdk__label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nx-cmdk__hint { flex: none; }
.nx-cmdk__item mark { background: none; color: inherit; font-weight: 700; }
.nx-cmdk__foot { flex: none; }
@media (max-width: 767.98px) { .nx-cmdk__foot { display: none; } }

/* ================================================================
   8. КАНБАН [data-nx-kanban]: стили перетаскивания — в motion-kit.css §15. Здесь только служебное
   ================================================================ */
[data-nx-kanban] [data-nx-col-list] { touch-action: pan-x pan-y; }
[data-nx-kanban][data-nx-dnd="on"] [data-id] { cursor: grab; }

/* ================================================================
   9. МЕНЬШЕ ДВИЖЕНИЯ: слои не едут и не масштабируются — только проявляются
   ================================================================ */
@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion="full"]) dialog:is([data-nx-modal], [data-nx-sheet]),
  :root:not([data-motion="full"]) [data-nx-menu][popover] { translate: 0 0 !important; scale: 1 !important; }
  :root:not([data-motion="full"]) dialog[data-nx-as="sheet"]:not(.is-open),
  :root:not([data-motion="full"]) [data-nx-menu][data-nx-as="sheet"]:not(.is-open) { opacity: 0; }
  :root:not([data-motion="full"]) :is(.nx-otp__char, .nx-msg__status svg) { animation: none; }
}
@media (prefers-reduced-motion: reduce) and (max-width: 767.98px) {
  :root:not([data-motion="full"]):not(#nx-strong) [data-nx-sidebar] { translate: 0 0; opacity: 0; transition-property: opacity, visibility; }
  :root:not([data-motion="full"]):not(#nx-strong) [data-nx-sidebar].is-open { opacity: 1; }
}
:root[data-motion="reduce"] dialog:is([data-nx-modal], [data-nx-sheet]),
:root[data-motion="reduce"] [data-nx-menu][popover] { translate: 0 0 !important; scale: 1 !important; }
:root[data-motion="reduce"] dialog[data-nx-as="sheet"]:not(.is-open),
:root[data-motion="reduce"] [data-nx-menu][data-nx-as="sheet"]:not(.is-open) { opacity: 0; }
@media (max-width: 767.98px) {
  :root[data-motion="reduce"]:not(#nx-strong) [data-nx-sidebar] { translate: 0 0; opacity: 0; transition-property: opacity, visibility; }
  :root[data-motion="reduce"]:not(#nx-strong) [data-nx-sidebar].is-open { opacity: 1; }
}
:root[data-motion="reduce"] :is(.nx-otp__char, .nx-msg__status svg) { animation: none; }

/* ================================================================
   10. Тосты кита поверх модалки: ui.js переносит .nx-toaster в верхний слой (popover="manual").
   Сбрасываем то, что браузер даёт любому [popover] (рамка, фон, отступы, inset: 0).
   ================================================================ */
.nx-toaster[popover] { margin: 0; padding: 0; border: 0; background: none; color: inherit; overflow: visible; top: auto; right: auto; }
