/* ds-new.css — недостающие компоненты дизайн-системы. Полностью на токенах
   (ds-tokens.css), поэтому автоматически работают в светлой и тёмной темах
   без единой строки тёмного оверрайда — демонстрация выгоды токенов. */

/* ===== Алерты / баннеры ===== */
.ds-alert { display:flex; gap:12px; align-items:flex-start; padding:12px 14px;
  border-radius:var(--r-md); border:1px solid; font-size:var(--fs-md); line-height:var(--lh-normal); width:100%; }
.ds-alert__icon { flex:0 0 auto; width:20px; height:20px; margin-top:1px; }
.ds-alert__title { font-weight:var(--fw-semi); margin:0 0 2px; }
.ds-alert__body { margin:0; color:var(--text); min-width:0; overflow-wrap:anywhere; }
/* min-width:0 обязателен: без него текстовый блок во flex-контейнере не ужимается ниже
   своей минимальной ширины и вылезает за узкий экран вместе со всей страницей. */
.ds-alert--info    { background:var(--c-info-bg);    border-color:var(--c-info);    color:var(--c-info); }
.ds-alert--success { background:var(--c-success-bg); border-color:var(--c-success); color:var(--c-success); }
.ds-alert--warning { background:var(--c-warning-bg); border-color:var(--c-warning); color:var(--c-warning); }
.ds-alert--danger  { background:var(--c-danger-bg);  border-color:var(--c-danger);  color:var(--c-danger); }
.ds-alert .ds-alert__body { color:var(--text); }

/* ===== Тосты ===== */
.ds-toast { display:flex; gap:10px; align-items:center; min-width:260px; max-width:380px;
  padding:12px 14px; background:var(--surface); border:1px solid var(--border);
  border-left:3px solid var(--c-success); border-radius:var(--r-md); box-shadow:var(--sh-lg); color:var(--text); }
.ds-toast--danger  { border-left-color:var(--c-danger); }
.ds-toast--info    { border-left-color:var(--c-info); }
.ds-toast__icon { flex:0 0 auto; width:20px; height:20px; }
.ds-toast__text { font-size:var(--fs-md); flex:1; }
.ds-toast__text small { display:block; color:var(--text-muted); font-size:var(--fs-sm); }
.ds-toast__close { flex:0 0 auto; background:none; border:0; color:var(--text-subtle); cursor:pointer; font-size:16px; line-height:1; }
.ds-toast__close:hover { color:var(--text); }

/* ===== Пустое состояние ===== */
.ds-empty { text-align:center; padding:36px 20px; max-width:420px; margin:0 auto; }
.ds-empty__icon { width:56px; height:56px; margin:0 auto 14px; border-radius:var(--r-pill);
  display:flex; align-items:center; justify-content:center; background:var(--surface-3); color:var(--text-subtle); }
.ds-empty__title { font-size:var(--fs-lg); font-weight:var(--fw-semi); margin:0 0 4px; color:var(--text); }
.ds-empty__text { font-size:var(--fs-md); color:var(--text-muted); margin:0 0 16px; }

/* ===== Скелетоны загрузки ===== */
.ds-skel { position:relative; overflow:hidden; background:var(--surface-3); border-radius:var(--r-sm); }
.ds-skel::after { content:""; position:absolute; inset:0; transform:translateX(-100%);
  background:linear-gradient(90deg, transparent, rgba(255,255,255,.09), transparent); animation:ds-shimmer 1.3s infinite; }
html:not([data-bs-theme="dark"]) .ds-skel::after { background:linear-gradient(90deg, transparent, rgba(0,0,0,.05), transparent); }
.ds-skel--line { height:12px; }
.ds-skel--title { height:18px; width:60%; }
.ds-skel--avatar { width:40px; height:40px; border-radius:var(--r-pill); }
.ds-skel--btn { height:36px; width:120px; border-radius:var(--r-md); }
@keyframes ds-shimmer { 100% { transform:translateX(100%); } }

/* ===== Аватары ===== */
.ds-avatar { position:relative; display:inline-flex; align-items:center; justify-content:center;
  border-radius:var(--r-pill); background:var(--surface-3); color:var(--text-subtle);
  font-weight:var(--fw-semi); overflow:visible; flex:0 0 auto; }
.ds-avatar img { width:100%; height:100%; border-radius:inherit; object-fit:cover; }
.ds-avatar--sm { width:28px; height:28px; font-size:var(--fs-xs); }
.ds-avatar--md { width:40px; height:40px; font-size:var(--fs-md); }
.ds-avatar--lg { width:56px; height:56px; font-size:var(--fs-lg); }
.ds-avatar__status { position:absolute; right:-1px; bottom:-1px; width:11px; height:11px;
  border-radius:var(--r-pill); border:2px solid var(--surface); background:var(--text-subtle); }
.ds-avatar__status--online { background:var(--c-success); }
.ds-avatar__status--busy { background:var(--c-danger); }

/* ===== Прогресс ===== */
.ds-progress { height:8px; border-radius:var(--r-pill); background:var(--surface-3); overflow:hidden; width:100%; }
.ds-progress__bar { height:100%; border-radius:inherit; background:var(--accent); transition:width .3s; }
.ds-progress__bar--success { background:var(--c-success); }
.ds-progress__bar--danger { background:var(--c-danger); }

/* ===== Пагинация ===== */
.ds-pagination { display:inline-flex; gap:4px; align-items:center; }
.ds-pagination a, .ds-pagination span { display:inline-flex; align-items:center; justify-content:center;
  min-width:34px; height:34px; padding:0 8px; border-radius:var(--r-md); font-size:var(--fs-md);
  color:var(--text); text-decoration:none; border:1px solid transparent; transition:background .12s,border-color .12s; }
.ds-pagination a:hover { background:var(--surface-3); }
.ds-pagination .is-active { background:var(--accent); color:var(--on-accent); font-weight:var(--fw-semi); }
.ds-pagination .is-disabled { color:var(--text-subtle); pointer-events:none; }
.ds-pagination .ds-pagination__dots { pointer-events:none; color:var(--text-subtle); }

/* ===== Валидация форм ===== */
.ds-field { display:flex; flex-direction:column; gap:6px; }
.ds-field__label { font-size:var(--fs-md); font-weight:var(--fw-med); color:var(--text); }
.ds-field .form-control { transition:border-color .12s, box-shadow .12s; }
.ds-field .form-control:focus { border-color:var(--accent); box-shadow:0 0 0 3px var(--focus-ring); outline:none; }
.ds-field .form-control.is-invalid { border-color:var(--c-danger); }
.ds-field .form-control.is-invalid:focus { box-shadow:0 0 0 3px var(--c-danger-bg); }
.ds-field .form-control.is-valid { border-color:var(--c-success); }
.ds-hint { font-size:var(--fs-sm); color:var(--text-muted); }
.ds-error { font-size:var(--fs-sm); color:var(--c-danger); display:flex; align-items:center; gap:5px; }
.ds-error__ico { width:14px; height:14px; flex:0 0 auto; }

/* ===== Тултип по hover ===== */
.ds-tip { position:relative; display:inline-flex; align-items:center; }
.ds-tip__bubble { position:absolute; bottom:calc(100% + 8px); left:50%; transform:translateX(-50%);
  background:var(--text); color:var(--bg); font-size:var(--fs-sm); padding:6px 10px; border-radius:var(--r-md);
  white-space:nowrap; opacity:0; pointer-events:none; transition:opacity .12s; box-shadow:var(--sh-md); z-index:var(--z-dropdown); }
.ds-tip__bubble::after { content:""; position:absolute; top:100%; left:50%; transform:translateX(-50%);
  border:5px solid transparent; border-top-color:var(--text); }
.ds-tip:hover .ds-tip__bubble { opacity:1; }

/* ===== Хлебные крошки ===== */
.ds-breadcrumb { display:flex; flex-wrap:wrap; align-items:center; gap:6px; font-size:var(--fs-md); }
.ds-breadcrumb a { color:var(--text-muted); text-decoration:none; }
.ds-breadcrumb a:hover { color:var(--text); }
.ds-breadcrumb__sep { color:var(--text-subtle); }
.ds-breadcrumb__current { color:var(--text); font-weight:var(--fw-med); }

/* ===== Мелкая утилита стека для превью ===== */
.dsx-stack { display:flex; flex-direction:column; gap:12px; width:100%; }

/* ===== Разбор очереди модерации: плитка превью, размытие, панель массового действия =====
   Живут в системе, а не в разделе: этих деталей просят уже три раздела модерации (промахи,
   взрослые каналы, дальше наркотики с документами), и третья копия размытия — гарантированный
   расход: где-то оно снимается наведением, где-то кнопкой, где-то забудут выключить в таблице. */

.ds-tiles { display:grid; grid-template-columns:repeat(auto-fill, minmax(210px, 1fr)); gap:var(--sp-3); }
.ds-tile { border:1px solid var(--border); border-radius:var(--r-lg); overflow:hidden; background:var(--surface); }
.ds-tile.is-selected { outline:2px solid var(--accent); outline-offset:-1px; background:var(--surface-2); }
.ds-tile__frames { display:grid; grid-template-columns:1fr 1fr; grid-template-rows:1fr 1fr; gap:2px;
  aspect-ratio:2 / 1.35; background:var(--surface-3); }
/* grid-template-rows обязателен: без него высоту строк задают сами картинки, соотношение
   сторон не соблюдается и карточки в ряду получаются разной высоты. */
.ds-tile__frames img { width:100%; height:100%; min-height:0; object-fit:cover; display:block; }
.ds-tile__frames--single { grid-template-columns:1fr; }
.ds-tile__empty { grid-column:1 / -1; display:grid; place-items:center; color:var(--text-subtle); font-size:var(--fs-xs); }
.ds-tile__meta { padding:var(--sp-2) var(--sp-3); }
.ds-tile__title { font-size:var(--fs-sm); font-weight:var(--fw-med); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ds-tile__sub { font-size:var(--fs-xs); color:var(--text-muted); margin-top:2px; }

/* Размытие включается на КОНТЕЙНЕРЕ и снимается наведением на конкретный кадр.
   Смысл: листать экранами открытую порнографию модератор не обязан, а проверить конкретный
   канал — должен. Поэтому размытие не «настройка красоты», а свойство рабочего места. */
.ds-blur .ds-tile__frames img { filter:blur(10px) saturate(.6); }
.ds-blur .ds-tile:hover .ds-tile__frames img { filter:none; }
@media (prefers-reduced-motion: no-preference) {
  .ds-tile__frames img { transition:filter .12s ease-out; }
}

/* Панель массового действия: всегда на виду, потому что действие затрагивает много строк. */
.ds-bulkbar { display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap;
  padding:var(--sp-3); border:1px solid var(--border); border-radius:var(--r-md); background:var(--surface-2); }
.ds-bulkbar__count { font-weight:var(--fw-med); }
.ds-bulkbar__spacer { flex:1; }

/* Селект: класс использовался разделами модерации, но в системе его не было — поля оставались
   без стилей. Заводим здесь, чтобы разметка не расходилась с системой. */
.ds-select { appearance:none; border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); color:var(--text); font-size:var(--fs-md); padding:6px 28px 6px 10px;
  background-image:linear-gradient(45deg, transparent 50%, var(--text-muted) 50%),
                   linear-gradient(135deg, var(--text-muted) 50%, transparent 50%);
  background-position:calc(100% - 15px) 52%, calc(100% - 10px) 52%;
  background-size:5px 5px, 5px 5px; background-repeat:no-repeat; }
.ds-select:focus-visible { outline:2px solid var(--accent); outline-offset:1px; }
