/* ==========================================================================
   Split-layout module workspace (Комментинг) — editor on the left,
   account list (multi-selectable) on the right, activity log full-width below.
   Everything here is plain server-rendered content shown/hidden via JS, the
   same convention as the rest of the app (no framework, no client state store).
   ========================================================================== */
.commenting-workspace {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 27rem;
  gap: 1rem;
  /* stretch (not start) — the editor and the picker/summary side should read as one
     matched pair at a glance, not a tall box next to a short one, whichever side
     happens to have more content in a given state (empty/single/bulk). min-height
     (not a fixed height) — both sides should freely grow to fit their own content,
     never get clipped/shrunk to a capped number; a floor just keeps a very short
     state (e.g. nothing assigned yet) from looking tiny next to its neighbor. */
  align-items: stretch;
  min-height: 30rem;
}
@media (max-width: 1024px) {
  .commenting-workspace { grid-template-columns: 1fr; min-height: 0; }
}
.commenting-editor { display: flex; flex-direction: column; padding: 0; min-height: 20rem; height: 100%; }
.commenting-editor-empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem;
  text-align: center;
  color: var(--color-text-faint);
  font-size: 0.85rem;
}
/* Прогрев's version shows a real (inert) preview of the settings form instead of a
   short caption, so it needs its own column layout — the banner and the (reused
   .commenting-editor-scroll) preview area manage their own spacing/sizing. */
.commenting-editor-empty:has(.editor-empty-banner) {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  padding: 0;
  text-align: left;
}
/* :has() raises this selector's specificity above the plain .hidden utility class,
   which would otherwise stop winning once this one exists — restated here (same
   specificity as the rule above, so source order decides) purely so toggling
   .hidden still actually hides this element. */
.commenting-editor-empty.hidden:has(.editor-empty-banner) { display: none; }
/* A "no account assigned" empty state used to cram its "Назначить аккаунт" button
   onto the same line as the warning text via margin-left:auto — fine for short text/
   wide screens, but the button ended up crowding right against (or, at narrower
   widths, visually overlapping) the text with no real gap. The button now sits on
   its own row below the warning bar instead, so there's never a collision. */
.editor-empty-banner { margin: 1.1rem 1.25rem 0; display: flex; flex-direction: column; align-items: flex-start; gap: 0.6rem; }
.editor-empty-banner .alert-warning { width: 100%; box-sizing: border-box; }
.editor-empty-preview { opacity: 0.55; filter: saturate(0.75); pointer-events: none; }
.commenting-editor-set { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.commenting-editor-head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--color-border);
}
.commenting-editor-avatar {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-surface-muted);
  color: var(--color-primary);
  font-weight: 600;
  flex-shrink: 0;
  overflow: hidden;
}
.commenting-editor-avatar img { width: 100%; height: 100%; object-fit: cover; }
.commenting-tabs {
  display: flex;
  gap: 0.3rem;
  padding: 0.6rem 0.9rem;
  border-bottom: 1px solid var(--color-border);
  overflow-x: auto;
}
.commenting-editor-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: 1.1rem 1.25rem; }
.commenting-editor-foot {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.85rem 1.25rem;
  border-top: 1px solid var(--color-border);
  flex-wrap: wrap;
}

.commenting-accounts { display: flex; flex-direction: column; padding: 0; }
.commenting-accounts-head { display: flex; align-items: start; justify-content: space-between; gap: 0.5rem; padding: 1rem 1.1rem 0.7rem; }
.commenting-accounts-search { padding: 0 1rem 0.7rem; display: flex; gap: 0.5rem; }
.commenting-account-list { flex: 1; overflow-y: auto; max-height: 32rem; padding: 0 0.65rem 0.75rem; display: grid; gap: 0.3rem; }
.commenting-account-card {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr);
  gap: 0.6rem;
  align-items: center;
  padding: 0.55rem 0.6rem;
  border-radius: 0.9rem;
  border: 1px solid transparent;
  cursor: pointer;
}
.commenting-account-card:hover { background: var(--color-surface-muted); }
.commenting-account-card.is-selected { border-color: var(--color-primary); background: var(--color-primary-bg); }
.commenting-account-card input[type="checkbox"] { margin: 0; }
.commenting-account-avatar {
  width: 2.1rem;
  height: 2.1rem;
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-surface-muted);
  color: var(--color-primary);
  font-weight: 600;
  font-size: 0.85rem;
  flex-shrink: 0;
  overflow: hidden;
}
.commenting-account-avatar img { width: 100%; height: 100%; object-fit: cover; }
.commenting-account-info strong { display: block; font-size: 0.82rem; }
.commenting-account-info span { display: block; margin-top: 0.1rem; font-size: 0.7rem; color: var(--color-text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.commenting-account-state.is-on { color: var(--color-accent); }
.commenting-account-card input[type="checkbox"]:disabled { opacity: 0.4; cursor: not-allowed; }
.commenting-account-card:has(input:disabled) { cursor: not-allowed; opacity: 0.7; }

/* "Выбор аккаунтов" dual-pool picker — sits above the settings editor on every
   role page. Left pool ("Доступные") only ever feeds the assign action; right
   pool ("Назначены") keeps using [data-module-checkbox], the same attribute
   initModuleWorkspace() already drove before this component existed, so the
   single/bulk settings editor below needs no changes to keep working. */
.role-picker-intro { margin-bottom: 0.85rem; }
.role-picker-intro h2 { margin: 0; font-size: 1rem; font-weight: 600; }
.role-picker-intro p { margin: 0.25rem 0 0; font-size: 0.8rem; color: var(--color-text-faint); }

/* Two independent cards (same visual weight as the settings/summary pair
   below), not one wrapping card with an internal divider — a thin arrow
   "bridge" sits in the gap between them, its own background but no border
   of its own so it doesn't read as a third card. */
/* stretch (not start) — "Доступные" and "Назначены" must read as one matched
   pair of cards, not a tall one next to a short one whenever their account
   counts differ. */
.role-picker-dual { display: grid; grid-template-columns: minmax(0, 1fr) 3.4rem minmax(0, 1fr); gap: 1rem; align-items: stretch; }
@media (max-width: 900px) {
  .role-picker-dual { grid-template-columns: 1fr; }
  .role-picker-bridge { flex-direction: row; justify-content: center; background: transparent; padding: 0.25rem 0; }
  .role-picker-bridge-btn { transform: rotate(90deg); }
}
.role-picker-pool { min-width: 0; display: flex; flex-direction: column; padding: 0; }
.role-picker-pool-head { display: flex; align-items: start; justify-content: space-between; gap: 0.5rem; padding: 1.1rem 1.1rem 0.6rem; }
.role-picker-pool-head h3 { margin: 0; font-size: 0.82rem; font-weight: 600; }
.role-picker-pool-head p { margin: 0.2rem 0 0; font-size: 0.68rem; color: var(--color-text-faint); }

/* Search stays full-width on its own row; the selects (country/status/role)
   share an even 3-column row underneath once there's room — scales cleanly
   regardless of how many select filters this pool ends up with. */
.role-picker-filters { display: grid; grid-template-columns: 1fr; gap: 0.5rem; padding: 0 1.1rem 0.6rem; }
.role-picker-filters > .relative { grid-column: 1 / -1; }
@media (min-width: 480px) { .role-picker-filters { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.role-picker-filters .relative { position: relative; }
.role-picker-search-icon { position: absolute; left: 0.65rem; top: 50%; transform: translateY(-50%); color: var(--color-text-faint); }
.role-picker-filters input[type="text"] { padding-left: 2.1rem; width: 100%; }
.role-picker-filters select { width: 100%; }

.role-picker-selectall { display: flex; align-items: center; gap: 0.5rem; padding: 0 1.1rem 0.6rem; font-size: 0.75rem; color: var(--color-text-muted); cursor: pointer; }
.role-picker-tools { display: flex; flex-wrap: wrap; gap: 0.5rem; padding: 0 1.1rem 0.6rem; }

/* flex column (not grid) specifically so the empty-state placeholder below can
   use flex:1 to fill whatever height "Доступные"/"Назначены" stretch-matching
   gives this list — a plain CSS grid row wouldn't stretch to fill leftover
   track space the same way. */
.role-picker-list { flex: 1; overflow-y: auto; max-height: 20rem; padding: 0 0.75rem 0.6rem; display: flex; flex-direction: column; gap: 0.4rem; }
/* The "Назначены на роль" pool's true-empty state (never a filtered-search
   result there, that pool has no filters of its own) — a real .empty-state,
   not a one-line grey caption, and sized to sit comfortably inside the list
   area instead of the full-page version's generous padding. */
.role-picker-empty { flex: 1; justify-content: center; margin: 0; border-style: dashed; padding: 1.5rem 1rem; }
.role-picker-empty .icon { width: 1.75rem; height: 1.75rem; }
.role-picker-empty .empty-state-desc { max-width: 18rem; }
.role-picker-account {
  display: grid; grid-template-columns: auto auto minmax(0, 1fr) auto; gap: 0.65rem; align-items: center;
  padding: 0.6rem 0.7rem; border: 1px solid var(--color-border); border-radius: 0.85rem; background: var(--color-surface); cursor: pointer;
}
.role-picker-account:hover { border-color: var(--color-primary); background: var(--color-surface-muted); }
.role-picker-account.is-selected { border-color: var(--color-primary); background: var(--color-primary-bg); }
.role-picker-account input[type="checkbox"] { margin: 0; }
.role-picker-account input[type="checkbox"]:disabled { opacity: 0.4; cursor: not-allowed; }
.role-picker-account:has(input:disabled) { cursor: not-allowed; opacity: 0.65; }
.role-picker-avatar {
  width: 2.15rem; height: 2.15rem; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center;
  background: var(--color-surface-muted); color: var(--color-primary); font-weight: 600; font-size: 0.85rem; flex-shrink: 0; overflow: hidden;
}
.role-picker-avatar img { width: 100%; height: 100%; object-fit: cover; }
.role-picker-copy { min-width: 0; }
.role-picker-copy strong { display: block; font-size: 0.82rem; }
.role-picker-copy span { display: block; margin-top: 0.1rem; font-size: 0.7rem; color: var(--color-text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.role-picker-copy .is-ok { color: var(--color-success-text); }
.role-picker-copy .is-error { color: var(--color-danger-text); }
.role-picker-copy .is-muted-text { color: var(--color-text-faint); }
.role-picker-tag { flex-shrink: 0; padding: 0.2rem 0.5rem; border-radius: 999px; background: var(--color-surface-muted); color: var(--color-text-muted); font-size: 0.65rem; font-weight: 700; white-space: nowrap; }
.role-picker-tag.is-active { background: var(--color-success-text); color: #fff; }
.role-picker-tag.is-muted { opacity: 0.7; }

.role-picker-pagination { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; padding: 0.5rem 1.1rem 0.9rem; font-size: 0.7rem; color: var(--color-text-muted); }

.role-picker-bridge { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.6rem; align-self: center; }
.role-picker-bridge form { display: contents; }
.role-picker-bridge-btn {
  width: 2.3rem; height: 2.3rem; border-radius: var(--radius-md); border: 1px solid var(--color-border); background: var(--color-surface);
  color: var(--color-primary); display: flex; align-items: center; justify-content: center;
}
.role-picker-bridge-btn:hover { border-color: var(--color-primary); background: var(--color-primary-bg); }

/* Account picker on the "Настройка аккаунтов" tab — sits to the RIGHT of the
   settings editor (paired inside .commenting-workspace, same convention as
   the editor/summary pair once did) instead of a horizontal bar above it.
   Its rows carry [data-module-checkbox] directly since it now lives on the
   same tab/DOM subtree as the editor it drives. */
.role-editor-picker { display: flex; flex-direction: column; padding: 0; min-height: 0; }
.role-editor-picker-filters { display: grid; grid-template-columns: 1fr; gap: 0.5rem; padding: 1.1rem 1.1rem 0.6rem; }
.role-editor-picker-filters > .relative { grid-column: 1 / -1; }
@media (min-width: 480px) { .role-editor-picker-filters { grid-template-columns: minmax(0, 1fr) 8rem; } }
.role-editor-picker-filters .relative { position: relative; }
.role-editor-picker-filters input[type="text"] { padding-left: 2.1rem; width: 100%; }
.role-editor-picker-filters select { width: 100%; }
.role-editor-picker-list { flex: 1; min-height: 0; overflow-y: auto; padding: 0 0.75rem 0.6rem; display: flex; flex-direction: column; gap: 0.4rem; }

/* Overlapping avatar stack — shown in the bulk-edit header in place of a
   generic "editing multiple accounts" icon, so it's clear at a glance which
   accounts a bulk save is about to apply to. */
.avatar-stack { display: flex; align-items: center; flex-shrink: 0; }
.avatar-stack .role-picker-avatar {
  width: 2.1rem; height: 2.1rem; border: 2px solid var(--color-surface); margin-left: -0.6rem;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
}
.avatar-stack .role-picker-avatar:first-child { margin-left: 0; }
.btn-lg { min-height: 2.9rem; padding: 0.7rem 1.6rem; font-size: 0.95rem; }

/* Replaces the old per-account on/off switch in the warmup single-editor head — one
   prominent action that starts every account currently assigned to this role at once
   (equivalent granular enable/disable/remove actions still live in the "Добавление
   аккаунтов" tab's "Назначены на роль" toolbar). Pinned to the viewport corner —
   .hidden on the ancestor account-panel (tab switch) still hides it like anything
   else, position:fixed doesn't fight that — so it stays reachable while scrolling
   a tall settings form without following the user onto other tabs. */
.role-launch-fab { position: fixed; right: 1.5rem; bottom: 1.5rem; z-index: 40; }
.role-launch-fab .btn { box-shadow: 0 12px 28px color-mix(in srgb, var(--color-primary) 35%, transparent); }
@media (max-width: 640px) {
  .role-launch-fab { left: 1rem; right: 1rem; bottom: 1rem; }
  .role-launch-fab .btn { width: 100%; }
}

.avatar-stack-more {
  width: 2.1rem; height: 2.1rem; margin-left: -0.6rem; border: 2px solid var(--color-surface); border-radius: 50%;
  background: var(--color-surface-muted); color: var(--color-text-muted); font-size: 0.68rem; font-weight: 700;
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}

.commenting-logs-head { display: flex; align-items: center; gap: 0.75rem; padding: 0.9rem 1.1rem; border-bottom: 1px solid var(--color-border); flex-wrap: wrap; }
.commenting-logs-title strong { display: block; font-size: 0.85rem; }
.commenting-logs-title span { display: block; margin-top: 0.15rem; font-size: 0.72rem; color: var(--color-text-faint); }
.commenting-logs-body { max-height: 16rem; overflow-y: auto; }
.commenting-logs-body.hidden { display: none; }



/* --- Загрузка целей выгрузкой (components/target_import.html) --------------------
   Блок заменил список каналов с галочками во всех модулях, работающих по целям.
   Верстка нарочно узкая по смыслу: зона файла, поле для вставки, действие — и ниже
   то, что уже загружено. Всё остальное про цели живёт на странице «Каналы». */
.dm-target-import-actions {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.75rem; flex-wrap: wrap; margin-top: 0.7rem;
}
.dm-checkbox-inline {
  display: inline-flex; align-items: center; gap: 0.45rem;
  font-size: 0.85rem; color: var(--color-text-muted); cursor: pointer;
}
.dm-checkbox-inline input { width: 1rem; height: 1rem; accent-color: var(--color-primary); }

.dm-target-loaded { margin-top: 0.9rem; border-top: 1px solid var(--color-border); padding-top: 0.8rem; }
.dm-target-loaded-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.75rem; margin-bottom: 0.6rem;
}
.dm-target-loaded-head strong { font-size: 0.88rem; }
.dm-ghost-btn {
  display: inline-flex; align-items: center; gap: 0.35rem;
  border: 1px solid transparent; background: none; cursor: pointer;
  padding: 0.3rem 0.55rem; border-radius: var(--radius-md);
  font-size: 0.82rem; color: var(--color-text-faint);
}
.dm-ghost-btn svg { width: 0.95rem; height: 0.95rem; }
.dm-ghost-btn:hover { color: var(--color-danger); border-color: var(--color-danger); background: var(--color-danger-bg); }
/* Хвост «и ещё N» — не цель, а счётчик, и кликать по нему нечего. */
.dm-chip.is-muted { color: var(--color-text-faint); border-style: dashed; cursor: default; }

/* Кнопка «загрузить из файла» у полей, где цели хранятся строкой. */
/* Столбцом и во всю ширину: внутри зона загрузки, а не кнопка, и в строке она
   сжималась по содержимому — коробка получалась уже поля, под которым стоит. */
.dm-target-fill { display: block; margin-top: 0.5rem; }
.dm-target-fill .dropzone { width: 100%; }
.dm-target-fill .dm-hint { margin: 0; }

/* Аккаунт, который прямо сейчас выполняет роль этого модуля (открытый module_run) —
   оранжевое внешнее кольцо, видно и поверх violet-выделения назначенного. Не зелёный
   --color-accent: тот совпадает с «всё хорошо», а кольцо предупреждает — перезапуск
   перепишет текущую работу аккаунта. */
.role-outline-demo { pointer-events: none; margin-top: 4px; max-width: 340px; cursor: default; }
.role-picker-account.is-running-now,
.dm-account-card.is-running-now { outline: 2px solid var(--color-warning); outline-offset: 1px; }
