/* ════════════════════════════════════════════════════════════════════════
   ixo — Toast / notification (single source) — HF-10
   ────────────────────────────────────────────────────────────────────────
   Канонический компонент уведомлений. Один CSS на все страницы:
   редактор (editor.html), хабы (projects/dashboard/cabinet/help) и лендинг
   (index.html / ru/index.html). Раньше существовало 4 несовместимых копии
   (.toast-бар на cabinet/help/landing + два инлайн-дубля .tst). Markup и
   API строит js/toast.js (showNotify / showToast / showError).

   Цветовые роли заданы локально на #toast-stack (oklch — значения
   brand-guide §7), поэтому файл самодостаточен и не зависит от :root
   конкретной страницы. --radius-* — с фолбэками для хаб-страниц,
   где этих токенов нет (есть только в css/style.css редактора).
   body.dark-правила применяются только в редакторе (хабы/лендинг — light).
   ════════════════════════════════════════════════════════════════════════ */

#toast-stack {
  position: fixed;
  bottom: 20px; right: 20px;
  display: flex; flex-direction: column; gap: 8px;
  align-items: flex-end;
  z-index: 9999;
  pointer-events: none;
  width: 420px;
  max-width: calc(100vw - 40px);
}

/* T-0629 (канон — docs/design/brand-guide.md §7 «Размещение стека»): в
   редакторе `#toast-stack` (bottom:20/right:20 по умолчанию) и
   `#palette-footer .btn-add-color` (панель Palette, всегда приклеена к низу
   своей колонки — `#palette-footer{flex-shrink:0}` в css/style/06-palette.css)
   делят один и тот же угол экрана. Замер (2026-09-19, четыре высоты окна
   640/740/768/900/1100/1200 — верх `#palette-footer` НЕ зависит от высоты,
   только от режима указателя):
     · pointer:fine (мышь)   — верх футера 82px от низа viewport (кнопка h26)
     · pointer:coarse (тач)  — верх футера 96px (та же кнопка растёт до h40,
       T-0686 — рост кнопки поднимает верхнюю кромку футера на те же 14px)
   Обычный тост (без `desc`) высотой ~50px — базовые 20px оставляли ему
   нижнюю кромку кнопки под собой в pointer:fine, а под pointer:coarse (где
   кнопка ещё выше) перекрытие было ещё больше. Поднимаем стек НА РЕДАКТОРЕ
   до 94px (82+12 запаса) / 108px под coarse (96+12) — тот же запас 12px от
   верха футера в обоих режимах. Хабы/лендинг `#palette-footer` не рендерят
   вовсе — `:has()` не матчит их body, второй копии файла не нужно (тот же
   приём, что `body:has(#fc-list-printout:not(:empty))` в
   css/floss-converter.css).
   ⚠️ Цена приёма: `:has(#palette-footer)` проверяет НАЛИЧИЕ узла в DOM, а
   не его видимость. `#palette-footer` — статическая разметка внутри
   `.rp-tab-panel#tab-palette` (editor.html:1584): на вкладках «Проверка»/
   «Слои», при свёрнутой правой панели или под мобильным гейтом узел
   остаётся в DOM (просто `display:none` у панели-предка), и стек всё равно
   держит 94/108px, хотя рядом никого нет — пустой запас 74/88px в эти
   моменты. Точный видимость-гвард (`:has(#tab-palette:not([hidden]))`
   или аналог) потребовал бы синхронизации с JS-переключателем вкладок;
   решение владелицы 2026-09-19 — принять фиксированный отступ, не гнаться
   за точностью на редких сочетаниях. */
body:has(#palette-footer) #toast-stack {
  bottom: 94px;
}
@media (pointer: coarse) {
  body:has(#palette-footer) #toast-stack {
    bottom: 108px;
  }
}

/* BG-DESKEW-SUGGEST-1 (T-0567) · placement:'center' — одиночная немодальная
   карточка по центру экрана (см. showNotify() в js/toast.js). Свой
   контейнер, тот же приём pointer-events:none + .tst{pointer-events:auto}
   на дочерней карточке (см. ниже), что уже держит #toast-stack: контейнер
   кликов не перехватывает, рисовать под ним можно. Без затемнения (нет
   backdrop) — ненавязчивость держится поведением, а не модальностью. */
#toast-center {
  position: fixed;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  display: flex; flex-direction: column; gap: 8px;
  /* ⚠️ FIXUP-BLOCK-BG: 900, а НЕ 9999 как у #toast-stack. Обе поверхности
     центрированы по экрану, поэтому липкая карточка (`duration:0`) физически
     ложилась НА середину любого открытого диалога — на confirmModal о потере
     калибровки (#confirm-modal, z-index 1010) и на пикер листов
     (#pdf-page-modal, 1000). Это противоречит brand-guide §7 («не перекрывает
     страницу») и собственному Esc-гварду карточки (js/toast.js), который
     СОЗНАТЕЛЬНО уступает Esc открытому диалогу — то есть уже исходит из того,
     что диалог сверху. 900 выше плавающих панелей редактора (#panel-layer =
     500, #minimap-panel = 200) и ниже всей модальной полосы 1000+
     (css/style/07-modals-core.css). #toast-stack не трогаем — он справа-снизу
     и с центрированными диалогами не пересекается. */
  z-index: 900;
  pointer-events: none;
  width: 340px;
  max-width: calc(100vw - 40px);
}
.tst.tst-center { padding: 16px 16px; }
/* Leave-анимация базового .tst сдвигает карточку вбок (translateX) — для
   строки справа-снизу это естественный уход «в сторону», а для карточки в
   центре экрана выглядит как случайный рывок. Своя, без горизонтального
   сдвига. */
.tst.tst-center.tst-leaving { transform: scale(0.96); opacity: 0; filter: blur(1px); }

/* Semantic color tokens (light)
   ⚠️ BG-DESKEW-SUGGEST-1 (T-0567, ревью-фикс): #toast-center ОБЯЗАН стоять
   в этом же селекторе, не только #toast-stack. #toast-center — самостоятельный
   ребёнок body, не потомок #toast-stack, наследования переменных нет; .tst
   читает --t-bg: var(--info-bg) БЕЗ фолбэка — без переменной это
   guaranteed-invalid, background/border/box-shadow проваливаются в initial
   (invalid-at-computed-value-time), и карточка рендерится ПРОЗРАЧНОЙ (виден
   только backdrop-filter). Найдено ревью T-0567/T-0575 по семантике CSS,
   подтверждено живым рендером в devtools. */
#toast-stack, #toast-center {
  --ok-fg:    oklch(0.36 0.10 145);
  --ok-bg:    oklch(0.96 0.05 145 / 0.92);
  --ok-bd:    oklch(0.78 0.07 145);
  --ok-dot:   oklch(0.62 0.16 145);
  --err-fg:   oklch(0.38 0.13 18);
  --err-bg:   oklch(0.96 0.045 18 / 0.92);
  --err-bd:   oklch(0.80 0.07 18);
  --err-dot:  oklch(0.60 0.18 18);
  --info-fg:  oklch(0.38 0.11 250);
  --info-bg:  oklch(0.96 0.04 250 / 0.92);
  --info-bd:  oklch(0.80 0.06 250);
  --info-dot: oklch(0.60 0.16 250);
  --warn-fg:  oklch(0.40 0.12 70);
  --warn-bg:  oklch(0.96 0.05 80 / 0.92);
  --warn-bd:  oklch(0.80 0.08 75);
  --warn-dot: oklch(0.66 0.16 70);
  --shadow-toast: 0 1px 2px rgba(15,23,42,0.04), 0 4px 12px rgba(15,23,42,0.08), 0 16px 36px rgba(15,23,42,0.10);
}
body.dark #toast-stack, body.dark #toast-center {
  --ok-fg:    oklch(0.88 0.07 145);
  --ok-bg:    oklch(0.32 0.06 145 / 0.85);
  --ok-bd:    oklch(0.50 0.07 145);
  --ok-dot:   oklch(0.74 0.16 145);
  --err-fg:   oklch(0.88 0.07 18);
  --err-bg:   oklch(0.32 0.07 18 / 0.85);
  --err-bd:   oklch(0.50 0.09 18);
  --err-dot:  oklch(0.74 0.16 18);
  --info-fg:  oklch(0.88 0.06 250);
  --info-bg:  oklch(0.32 0.06 250 / 0.85);
  --info-bd:  oklch(0.50 0.08 250);
  --info-dot: oklch(0.74 0.14 250);
  --warn-fg:  oklch(0.88 0.07 80);
  --warn-bg:  oklch(0.34 0.06 70 / 0.85);
  --warn-bd:  oklch(0.52 0.08 70);
  --warn-dot: oklch(0.78 0.15 70);
  --shadow-toast: 0 2px 4px rgba(0,0,0,0.20), 0 6px 18px rgba(0,0,0,0.30), 0 22px 48px rgba(0,0,0,0.32);
}

.tst {
  --t-fg: var(--info-fg);
  --t-bg: var(--info-bg);
  --t-bd: var(--info-bd);
  --t-dot: var(--info-dot);
  display: grid;
  grid-template-columns: 28px 1fr auto;
  gap: 12px;
  align-items: center;
  width: 100%;
  background: var(--t-bg);
  backdrop-filter: blur(30px) saturate(180%);
  -webkit-backdrop-filter: blur(30px) saturate(180%);
  border: 0.5px solid var(--t-bd);
  border-radius: 11px;
  padding: 11px 12px;
  box-shadow: var(--shadow-toast), inset 0 1px 0 rgba(255,255,255,0.5);
  color: var(--t-fg);
  position: relative;
  overflow: hidden;
  pointer-events: auto;
  transition: transform 220ms cubic-bezier(.4,0,.2,1), opacity 220ms cubic-bezier(.4,0,.2,1), filter 220ms cubic-bezier(.4,0,.2,1);
}
body.dark .tst { box-shadow: var(--shadow-toast), inset 0 1px 0 rgba(255,255,255,0.08); }
.tst.tst-success { --t-fg: var(--ok-fg);   --t-bg: var(--ok-bg);   --t-bd: var(--ok-bd);   --t-dot: var(--ok-dot); }
.tst.tst-error   { --t-fg: var(--err-fg);  --t-bg: var(--err-bg);  --t-bd: var(--err-bd);  --t-dot: var(--err-dot); }
.tst.tst-info    { --t-fg: var(--info-fg); --t-bg: var(--info-bg); --t-bd: var(--info-bd); --t-dot: var(--info-dot); }
.tst.tst-warn    { --t-fg: var(--warn-fg); --t-bg: var(--warn-bg); --t-bd: var(--warn-bd); --t-dot: var(--warn-dot); }

.tst.tst-entering { transform: translateY(8px); opacity: 0; }
.tst.tst-leaving  { transform: translateX(16px) scale(0.96); opacity: 0; filter: blur(1px); }

.tst-ico {
  width: 26px; height: 26px;
  border-radius: 50%;
  background: rgba(255,255,255,0.55);
  border: 0.5px solid rgba(255,255,255,0.7);
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  color: var(--t-dot);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.9), 0 1px 2px rgba(15,23,42,0.06);
}
/* T-1110: статус-глифы кита — точка (.dot) цвета статуса, не лиловой души */
.tst-ico .ic { --soul: currentColor; --dot: 1; }
body.dark .tst-ico {
  background: rgba(255,255,255,0.10);
  border-color: rgba(255,255,255,0.15);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.12), 0 1px 2px rgba(0,0,0,0.18);
}

.tst-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tst-title {
  font-size: 12px; font-weight: 600; letter-spacing: -0.01em;
  color: var(--t-fg); line-height: 1.3;
  white-space: normal; word-break: break-word;
}
.tst-desc {
  font-size: 11.5px; font-weight: 400; color: var(--t-fg);
  opacity: 0.78; line-height: 1.4; letter-spacing: -0.005em;
}
.tst-meta {
  font-size: 10px; color: var(--t-fg); opacity: 0.62;
  letter-spacing: 0.02em; margin-top: 2px;
}

/* PDP-C6b (Р8) — второе место для строки про живущие ссылки (кадр 6g): своя
   черта сверху, не продолжение заголовка тоста. Опционально через
   showNotify({ desc, descDivider: true }) — обычный .tst-desc не трогает. */
.tst-desc-hr {
  margin-top: 6px; padding-top: 6px;
  border-top: 0.5px solid var(--t-bd);
}

.tst-actions { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
.tst-action {
  font-family: inherit; font-size: 11.5px; font-weight: 600;
  color: var(--t-fg);
  background: rgba(255,255,255,0.55);
  border: 0.5px solid rgba(255,255,255,0.6);
  padding: 5px 9px; border-radius: var(--radius-xs, 6px); cursor: pointer;
  letter-spacing: -0.005em;
  transition: background .12s, transform .12s;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.7);
}
body.dark .tst-action {
  background: rgba(255,255,255,0.10);
  border-color: rgba(255,255,255,0.16);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.10);
}
.tst-action:hover { background: rgba(255,255,255,0.85); }
body.dark .tst-action:hover { background: rgba(255,255,255,0.16); }

.tst-close {
  width: 22px; height: 22px; border-radius: var(--radius-xs, 6px);
  background: transparent; border: 0.5px solid transparent;
  color: var(--t-fg); opacity: 0.55;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; transition: all .12s; flex-shrink: 0;
}
.tst-close:hover { opacity: 1; background: rgba(255,255,255,0.5); border-color: rgba(255,255,255,0.4); }
body.dark .tst-close:hover { background: rgba(255,255,255,0.10); border-color: rgba(255,255,255,0.15); }

.tst-prog {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 2px; background: var(--t-dot); opacity: 0.45;
  transform-origin: left center;
  transition: transform linear;
}

.tst-spin {
  width: 14px; height: 14px; border-radius: 50%;
  border: 1.5px solid transparent;
  border-top-color: var(--t-dot); border-right-color: var(--t-dot);
  animation: tst-spin 0.85s linear infinite;
}
@keyframes tst-spin { to { transform: rotate(360deg); } }

.tst-linebar {
  margin-top: 6px; height: 3px; border-radius: var(--radius-2xs, 2px);
  background: rgba(15,23,42,0.06); overflow: hidden; position: relative;
}
body.dark .tst-linebar { background: rgba(255,255,255,0.10); }
.tst-linebar-fill {
  position: absolute; inset: 0;
  background: var(--t-dot);
  transform-origin: left center;
  transition: transform 200ms linear;
}

@media (prefers-reduced-motion: reduce) {
  .tst { transition: none; }
  .tst-spin { animation: none; }
}
