/* ============================================================
   Корпоративный чат — оформление

   Идея: переписка компании это не лента реплик, а служебный журнал.
   Отсюда три решения, на которых держится весь макет:
     · поля документа — вертикальная линейка через всю ленту,
       слева от неё только время и даты, справа только содержание;
     · тело сообщений набрано засечным PT Serif — запись, а не чат;
     · левая колонка это переплёт журнала, тёмно-синий, а не серый.
   Акцент один — синий шариковой ручки. Бордовый только для тревог.
   ============================================================ */

:root {
  --cover:      #16213c;
  --cover-2:    #22304f;
  --cover-3:    #33425f;
  --cover-ink:  #9fb0cc;

  --sheet:      #fcfcfb;
  --sheet-2:    #f2f2ef;
  --desk:       #e4e6e9;

  --ink:        #141a24;
  --ink-2:      #2f3947;
  --muted:      #6d7787;
  --faint:      #98a2b1;

  --pen:        #1f3fa8;
  --pen-deep:   #16307f;
  --pen-wash:   #eaeefa;
  --stamp:      #9b2c48;

  --rule:       #dedfda;
  --rule-soft:  #ebebe6;
  --margin-rule: rgba(31, 63, 168, .17);

  --display: 'Golos Text', 'Segoe UI', system-ui, sans-serif;
  --body:    'PT Serif', Georgia, 'Times New Roman', serif;
  --body-sans: 'Golos Text', 'Segoe UI', system-ui, sans-serif;
  --ui:      'Golos Text', 'Segoe UI', system-ui, sans-serif;
  --mono:    'PT Mono', 'SF Mono', Consolas, monospace;

  --gutter: 92px;
  --radius: 7px;
  --shadow: 0 1px 2px rgba(20,26,36,.07), 0 10px 30px rgba(20,26,36,.09);
}

* { box-sizing: border-box; }

html, body { height: 100%; margin: 0; }

body {
  background: var(--desk);
  color: var(--ink);
  font: 400 15px/1.55 var(--ui);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: var(--display);
  font-weight: 600;
  margin: 0;
  letter-spacing: -.015em;
}
button, input, textarea { font: inherit; color: inherit; }
a { color: var(--pen); }

:focus-visible { outline: 2px solid var(--pen); outline-offset: 2px; }

/* ---------- элементы управления ---------- */

.btn {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 14px;
  padding: 9px 16px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--sheet);
  color: var(--ink);   /* без этого текст наследует цвет родителя — внутри синего пузыря сообщения (color:#fff) кнопка «Отменить» при редактировании становилась белым текстом на светлом фоне, то есть невидимой */
  cursor: pointer;
  transition: background .15s, border-color .15s, transform .06s;
}
.btn:hover { border-color: var(--muted); }
.btn:active { transform: translateY(1px); }
.btn--primary { background: var(--pen); border-color: var(--pen); color: #fff; }
.btn--primary:hover { background: var(--pen-deep); border-color: var(--pen-deep); }
.btn--wide { width: 100%; padding: 12px; }
.btn:disabled { opacity: .5; cursor: default; transform: none; }

/* Текст и SVG-иконка внутри кнопки «Отправить» по умолчанию просто строчные
   элементы — у SVG vertical-align:baseline «топит» её ниже текста. flex
   выравнивает по центру; на мобильном ниже это переопределяется на
   display:grid (кнопка становится круглой, только иконка). */
#send-btn { display: inline-flex; align-items: center; gap: 6px; }

/* Общая SVG-иконка (см. public/js/icons.js) — внутри .icon-btn центрируется
   самим .icon-btn (display:grid), это правило только для инлайновых
   случаев (иконка рядом с текстом в строке). */
.icn { flex: none; vertical-align: -3px; }

.icon-btn {
  display: grid;
  place-items: center;
  position: relative;
  width: 32px; height: 32px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  transition: background .15s, color .15s;
}
/*
 * Без этого правила display:grid выше перебивает встроенный [hidden] у
 * браузера — та же ловушка, что уже однажды была с баннером заявок на
 * регистрацию (.reg-banner[hidden]). Тогда её поправили точечно, для
 * одного конкретного элемента — но класс .icon-btn используется по всему
 * приложению, и ровно тот же баг незаметно поджидал в любом другом месте,
 * где кнопку с этим классом скрывают через hidden (например, «✎»/«🗑» у
 * новости для обычного сотрудника — сервер такой запрос всё равно отклонял,
 * но сами кнопки оставались видимыми и как будто рабочими, что и заметили).
 * Правим на уровне общего правила, а не отдельного случая — чтобы не ловить
 * этот же баг ещё раз в следующем месте, где про него забудут вспомнить.
 */
.icon-btn[hidden] { display: none; }
.icon-btn svg {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  display: block;
}
.icon-btn:hover { background: var(--sheet-2); color: var(--ink); }
.icon-btn--light { color: var(--cover-ink); }
.icon-btn--light:hover { background: var(--cover-3); color: #fff; }
.icon-btn.is-on { background: var(--pen-wash); color: var(--pen-deep); }
.icon-btn.is-on:hover { background: var(--pen-wash); }
/*
 * Постоянно подсвеченная иконка-кнопка. Внешне совпадает с .is-on выше, но
 * смысл другой и потому это отдельный класс: .is-on — переключатель, сейчас
 * включённый (его снимают и возвращают из JS), а --accent — просто «эта
 * кнопка в ряду главная», состояния у неё нет. Нужна кнопке «Новая задача»
 * в поле ввода: рядом с ней три служебные серые иконки, и без выделения
 * она читается как ещё одна из них.
 */
.icon-btn--accent { background: var(--pen-wash); color: var(--pen-deep); }
.icon-btn--accent:hover { background: #dde4f7; color: var(--pen-deep); }

/*
 * Размытие текста «на случай, если кто-то смотрит через плечо» — временное,
 * на весь сеанс, ничего не сохраняется (см. #privacy-toggle в app.js), не
 * персонально: одним классом на #app сразу и текущая лента (.bubble-text),
 * и превью во всех переписках в списке слева (.chan__preview) — то и
 * другое читаемо издалека примерно одинаково, скрывать только одно
 * не имело бы смысла. Файлы/картинки сознательно не размываются — просили
 * скрыть именно текст.
 */
.app.privacy-blur .bubble-text,
.app.privacy-blur .chan__preview {
  filter: blur(5px);
  user-select: none;
}

.chip-btn {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 13px;
  padding: 6px 13px;
  border: 1px solid var(--rule);
  border-radius: 100px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: border-color .15s, color .15s, background .15s;
}
.chip-btn:hover { color: var(--pen); border-color: var(--pen); }
.chip-btn.is-on { background: var(--pen); border-color: var(--pen); color: #fff; }

/* Кнопка «Вступить в канал» стоит в одном ряду с .chip-btn (Файлы/Участники/
   Поиск) — берёт их компактную геометрию, но акцентную заливку .btn--primary,
   чтобы явно читаться как основное действие, а не как ещё один переключатель. */
#join-channel.btn {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 13px;
  padding: 6px 13px;
  border-radius: 100px;
}

.field { display: block; margin-bottom: 14px; }
.field[hidden] { display: none; }
.field__label {
  display: block;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .05em;
  color: var(--muted);
  margin-bottom: 6px;
}
.field input, .field textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: #fff;
  transition: border-color .15s;
}
/* У textarea по умолчанию браузер разрешает тянуть за угол в обе стороны —
   в модалке фиксированной ширины (см. .modal__card) горизонтальная ручка
   бессмысленна: поле не может стать шире родителя (width: 100% выше), а
   попытка потянуть вбок просто ломает раскладку. Высота — единственное
   осмысленное направление. */
.field textarea { resize: vertical; }
.field input:focus, .field textarea:focus { border-color: var(--pen); outline: none; }

.form-error {
  margin: 0 0 12px;
  padding: 9px 13px;
  border-left: 3px solid var(--stamp);
  background: #f9edf0;
  color: #7c1f37;
  font-size: 14px;
  border-radius: 0 6px 6px 0;
}

/* ---------- вход: слева образец журнала, справа форма ---------- */

.gate { display: grid; grid-template-columns: 1.05fr .95fr; min-height: 100%; }
.gate[hidden] { display: none; }

/* ---------- приветствие после регистрации ---------- */

.welcome {
  min-height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--cover);
  padding: 32px 20px;
}
.welcome[hidden] { display: none; }
.welcome__card {
  width: min(440px, 100%);
  color: #e7ecf5;
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.welcome__card h1 {
  font-family: var(--display);
  font-size: clamp(22px, 3vw, 28px);
  font-weight: 700;
  margin: 0;
  color: #fff;
}
.welcome__lead { margin: -12px 0 0; font-size: 14.5px; color: #aab6d4; line-height: 1.5; }
.welcome__list { display: flex; flex-direction: column; gap: 10px; }
.welcome__item {
  display: flex;
  gap: 12px;
  align-items: center;
  padding: 12px 14px;
  background: rgba(255,255,255,.05);
  border: 1px solid rgba(127,155,238,.25);
  border-radius: 10px;
}
.welcome__icon {
  position: relative;
  flex: none;
  width: 30px; height: 30px;
  border-radius: 8px;
  background: rgba(127,155,238,.15);
  color: #7f9bee;
}
.welcome__icon svg {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  display: block;
}
.welcome__item b { display: block; font-family: var(--ui); font-size: 13.5px; font-weight: 600; color: #fff; }
.welcome__item span { display: block; font-size: 12px; color: #93a1c4; margin-top: 1px; }

.gate__aside {
  background: var(--cover);
  color: #e7ecf5;
  padding: 46px clamp(28px, 4.5vw, 64px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 26px;
}
.gate__mark {
  display: flex;
  align-items: center;
  gap: 13px;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(24px, 3vw, 34px);
  letter-spacing: -.025em;
  color: #fff;
}
.gate__bars { display: inline-flex; flex-direction: column; gap: 3px; width: 20px; }
.gate__bars i { height: 3px; border-radius: 2px; background: #fff; }
.gate__bars i:nth-child(1) { width: 100%; background: #7f9bee; }
.gate__bars i:nth-child(2) { width: 66%; }
.gate__bars i:nth-child(3) { width: 84%; opacity: .45; }

.gate__form {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 46px clamp(24px, 4vw, 56px);
  background: var(--sheet);
}
.gate__hint { margin: 18px 0 0; font-size: 13px; color: var(--muted); }
.gate__hint code {
  font-family: var(--mono);
  font-size: 12px;
  background: var(--sheet-2);
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: 1px 5px;
}

.tabs { display: flex; gap: 4px; margin-bottom: 24px; }
.tabs__btn {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 14px;
  padding: 7px 15px;
  border: 0;
  border-radius: 100px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.tabs__btn.is-active { background: var(--cover); color: #fff; }

/* ---------- каркас ---------- */

.app {
  display: grid;
  grid-template-columns: 395px minmax(0, 1fr);
  height: 100vh;
  height: 100dvh;   /* учитывает мобильную адресную строку — обычный 100vh может быть выше реального экрана */
  overflow: hidden; /* весь скролл — только внутри .rail__scroll / .log / .panel__body, не на уровне страницы */
}
.app[hidden] { display: none; }
.app.has-panel { grid-template-columns: 395px minmax(0, 1fr) 336px; }

/* ---------- переплёт ---------- */

.rail {
  background: var(--cover);
  color: #c3cee2;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.rail__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 15px 13px 11px 18px;
}
.rail__mark {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--display);
  font-weight: 700;
  font-size: 16px;
  letter-spacing: -.02em;
  color: #fff;
}
.rail__top-actions { display: flex; align-items: center; gap: 4px; flex: none; }
#rail-close { display: none; }

.search-trigger {
  margin: 0 12px 14px;
  padding: 9px 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  border: 1px solid var(--cover-3);
  border-radius: var(--radius);
  background: var(--cover-2);
  color: #8496b6;
  font-size: 13.5px;
  cursor: pointer;
  transition: border-color .15s, color .15s;
}
.search-trigger:hover { border-color: #4a5c7e; color: #dbe3f1; }
.search-trigger kbd {
  font-family: var(--mono);
  font-size: 10.5px;
  color: #6d80a5;
  border: 1px solid var(--cover-3);
  border-radius: 4px;
  padding: 1px 5px;
}

.news-link {
  margin: 0 12px 14px;
  padding: 9px 12px;
  display: flex;
  align-items: center;
  gap: 9px;
  border: 0;
  border-radius: var(--radius);
  background: var(--cover-2);
  color: #dbe3f1;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  width: calc(100% - 24px);
  text-align: left;
  transition: background .15s;
}
.news-link:hover { background: var(--cover-3); }
.news-link.is-active { background: var(--pen); color: #fff; }
.news-link svg { flex: none; }
.news-link__badge {
  margin-left: auto;
  background: var(--stamp);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  border-radius: 100px;
  padding: 1px 7px;
  flex: none;
}
.news-link__badge[hidden] { display: none; }
/* Непрочитанные новости помечены бордовым (--stamp) — он тут исторически
   и означает «важное, требует внимания». У задач бейдж зелёный, в пару к
   зелёному баннеру: цвет должен связывать баннер вверху ленты с пунктом
   в боковой колонке, куда этот баннер ведёт. */
.news-link__badge--green { background: #2e7d4f; }

/*
 * Переключатель «Каналы / Личные» — виден только на мобильном (см. медиа-
 * запрос max-width:860px ниже по файлу), на десктопе обе группы и так
 * видны сразу одним списком, переключать нечего. По умолчанию открыта
 * вкладка «Личные» — так и решили; реализовано на чистом CSS (группа
 * каналов скрыта по умолчанию правилом ниже), а не через JS при загрузке
 * — значит верно даже до того, как успеет отработать какой-либо скрипт.
 */
.rail__tabs { display: none; }
.rail__tab {
  flex: 1; text-align: center; padding: 8px 0; font-size: 12.5px; font-weight: 600;
  color: #8496b6; background: transparent; border: 0; border-radius: 7px; cursor: pointer;
  position: relative; font-family: inherit;
}
.rail__tab.is-active { background: var(--pen); color: #fff; }
.rail__tab-dot { position: absolute; top: 3px; right: 10px; width: 7px; height: 7px; border-radius: 50%; background: var(--stamp); }
.rail__tab-dot[hidden] { display: none; }

.rail__scroll {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;   /* список каналов/переписок — только вертикальный скролл, никогда не вбок */
  padding-bottom: 12px;
  scrollbar-width: thin;
  scrollbar-color: var(--cover-3) transparent;
}
.rail__scroll::-webkit-scrollbar { width: 9px; }
.rail__scroll::-webkit-scrollbar-track { background: transparent; }
.rail__scroll::-webkit-scrollbar-thumb {
  background: var(--cover-3);
  border-radius: 5px;
  border: 2px solid var(--cover);
  background-clip: padding-box;
}
.rail__scroll::-webkit-scrollbar-thumb:hover {
  background: var(--cover-ink);
  background-clip: padding-box;
}
.rail__group { margin-bottom: 17px; }
.rail__legend {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 8px 0 18px;
  margin-bottom: 5px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .05em;
  color: #5f7196;
}

/*
 * Подсказка «нажмите +» — показывается один раз новому сотруднику при самом
 * первом входе (см. app.js: showDmHintIfFirstTime). Обёртка ровно по
 * размеру самой кнопки — так кольца пульсации (::before/после через
 * .dm-hint__pulse) растут ровно из границ кнопки, а не откуда-то ещё.
 */
.dm-hint-wrap { position: relative; display: inline-flex; }
.dm-hint { position: absolute; inset: 0; }
.dm-hint[hidden] { display: none; }

.dm-hint__pulse {
  position: absolute;
  inset: 0;
  border-radius: 7px;
  background: var(--stamp);
  animation: dm-hint-pulse 1.6s ease-out infinite;
  pointer-events: none;
}
/* Вторая волна с задержкой — расходятся по очереди, а не миганием разом. */
.dm-hint__pulse--delay { animation-delay: .5s; }
@keyframes dm-hint-pulse {
  0% { transform: scale(1); opacity: .55; }
  100% { transform: scale(2.4); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .dm-hint__pulse { animation: none; opacity: .35; }
}

.dm-hint__bubble {
  position: absolute;
  top: calc(100% + 11px);
  right: 8px;
  width: 218px;
  background: var(--sheet);
  color: var(--ink);
  border-radius: 10px;
  padding: 12px 14px;
  box-shadow: 0 10px 28px rgba(0,0,0,.4);
  text-align: left;
  z-index: 30;
}
/* Стрелка сдвинута вместе с пузырём — по-прежнему указывает точно на кнопку. */
.dm-hint__bubble::before {
  content: '';
  position: absolute;
  top: -6px;
  right: 31px;
  width: 12px;
  height: 12px;
  background: var(--sheet);
  transform: rotate(45deg);
}
.dm-hint__text { display: block; margin-bottom: 10px; font-size: 13px; line-height: 1.5; color: var(--ink); font-family: var(--ui); letter-spacing: normal; }
.dm-hint__ok {
  display: block;
  margin-left: auto;
  border: 0;
  background: transparent;
  color: var(--pen);
  font-family: var(--ui);
  font-size: 12.5px;
  letter-spacing: normal;
  cursor: pointer;
  padding: 0;
}
.dm-hint__ok:hover { text-decoration: underline; }

.chan-list { list-style: none; margin: 0; padding: 0 8px; }
.chan {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 7px 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: #adbcd6;
  font-size: 14.5px;
  text-align: left;
  cursor: pointer;
  transition: background .12s, color .12s;
}
.chan:hover { background: var(--cover-2); color: #eaf0f9; }
.chan.is-active { background: var(--cover-3); color: #fff; }
.chan.has-unread { color: #fff; font-weight: 600; }

.chan__glyph { flex: none; font-family: var(--mono); font-size: 13.5px; color: #5f7196; }
.chan.is-active .chan__glyph, .chan.has-unread .chan__glyph { color: #7f9bee; }
.chan__name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chan__count {
  flex: none;
  min-width: 20px;
  padding: 1px 6px;
  border-radius: 100px;
  background: #7f9bee;
  color: #0e1830;
  font-family: var(--mono);
  font-size: 10.5px;
  text-align: center;
}

.dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: #445471; }
.dot.is-online { background: #6fbf8e; box-shadow: 0 0 0 2px rgba(111,191,142,.2); }

/* ---------- строка личной переписки: превью, время, закрепление ---------- */

.chan--dm { align-items: flex-start; padding: 7px 9px; gap: 8px; }

.chan__av { position: relative; flex: none; }
.chan__avdot {
  position: absolute; right: -1px; bottom: -1px;
  width: 8px; height: 8px; border-radius: 50%;
  background: #445471; border: 2px solid var(--cover);
}
.chan__avdot.is-online { background: #6fbf8e; }
.chan--dm.is-active .chan__avdot, .chan--dm:hover .chan__avdot { border-color: var(--cover-3); }

.chan__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; padding-top: 1px; }

.chan__line1 { display: flex; align-items: center; gap: 4px; }
.chan__line1 .chan__name { flex: 1; font-size: 13.5px; }
.chan__pin { flex: none; font-size: 9.5px; opacity: .85; }
.chan__time {
  flex: none;
  font-family: var(--mono);
  font-size: 10px;
  color: #7d90b0;
}
.chan.has-unread .chan__time { color: #9fb4e0; }

.chan__line2 { display: flex; align-items: center; gap: 6px; }
.chan__preview {
  flex: 1;
  min-width: 0;
  font-size: 12.5px;
  color: #8496b6;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chan.has-unread .chan__preview { color: #c6d2e8; }

.chan__pinbtn {
  flex: none;
  align-self: center;
  width: 24px; height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  font-size: 11px;
  opacity: 0;
  pointer-events: none;
  cursor: pointer;
  transition: opacity .12s, background .12s;
}
.chan--dm:hover .chan__pinbtn,
.chan__pinbtn:focus-visible {
  opacity: .6;
  pointer-events: auto;
}
.chan__pinbtn:hover, .chan__pinbtn:focus-visible { opacity: 1; background: var(--cover-3); }

.rail__me {
  border-top: 1px solid var(--cover-2);
  padding: 12px 13px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.me-trigger {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  padding: 4px;
  margin: -4px;
  border-radius: 8px;
  cursor: pointer;
  text-align: left;
  transition: background .12s;
}
.me-trigger:hover { background: var(--cover-2); }
.me-trigger .who { flex: 1; min-width: 0; }
.me-trigger .who b { display: block; color: #fff; font-size: 13.5px; font-weight: 600; }
.me-trigger .who span {
  display: block;
  font-size: 11.5px;
  color: #7f90b0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---------- аватар ---------- */

.ava {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px; height: 34px;
  border-radius: 7px;
  color: #fff;
  font-family: var(--display);
  font-weight: 600;
  font-size: 12.5px;
  user-select: none;
}
.ava-img { display: block; object-fit: cover; background: var(--sheet-2); }
.ava--sm { width: 26px; height: 26px; font-size: 10.5px; border-radius: 6px; }
.ava--lg { width: 64px; height: 64px; font-size: 22px; border-radius: 12px; }
.ava--0 { background: #2f4a86; } .ava--1 { background: #5a4a86; }
.ava--2 { background: #7a4a63; } .ava--3 { background: #8a5b44; }
.ava--4 { background: #3f6b57; } .ava--5 { background: #7d6533; }
.ava--6 { background: #4d5a6e; } .ava--7 { background: #8a4a4a; }

/* ---------- центральная колонка ---------- */

.board {
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  min-width: 0;
  min-height: 0;  /* без этого элемент сетки растёт под содержимое, а не под свою строку — и внутренний скролл ленты перестаёт работать */
  background: var(--sheet);
  position: relative;
}
/*
 * Явные grid-row обязательны здесь. Без них браузер расставляет элементы по
 * трекам автоматически, в порядке их появления в разметке, — но пропускает
 * при этом элементы с display:none, НЕ резервируя под них строку. Внутри
 * .board-banners могут быть скрыты ОБА баннера сразу (обычный сотрудник без
 * заявок и с разрешёнными уведомлениями) — то есть почти всегда. Без явного
 * grid-row в этом случае шапка/лента/поле ввода сдвигались бы на одну
 * строку раньше положенного, и поле ввода утекало бы в «резиновую» строку,
 * которая на самом деле предназначена для ленты сообщений (проверено
 * реальным рендером в браузере — voila, ровно так и происходило).
 */
.board-banners { grid-row: 1; }
.board__head { grid-row: 2; min-width: 0; }
.log-wrap { grid-row: 3; }
.composer { grid-row: 4; min-width: 0; }

/* Баннер заявок на регистрацию — виден только владельцу/админу (см. app.js:
   renderRegBanner). [hidden] снимает и участие в раскладке, так что для
   остальных ролей строка сетки .board просто схлопывается до 0. */
.reg-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  border: 0;
  padding: 10px 20px;
  background: var(--stamp);
  color: #fff;
  font: inherit;
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
}
/* Без этого правила display:flex выше перебивает встроенный [hidden] браузера
   (тот же приём, что и у .field[hidden] чуть выше по файлу) — баннер был бы
   виден всегда, независимо от того, есть заявки или нет. */
.reg-banner[hidden] { display: none; }
.reg-banner:hover { background: #7f2339; }
.reg-banner__icon { font-size: 15px; flex: none; }
.reg-banner__text { flex: 1; min-width: 0; }
.reg-banner__text b { font-weight: 600; }
.reg-banner__arrow { flex: none; opacity: .85; }

/* Заявка на вступление в канал — тот же .reg-banner, только не «опасный
   красный» (см. --stamp), а нейтральный янтарный: подать заявку и ждать
   решения владельца канала — обычное, не тревожное действие. */
.reg-banner--amber { background: #c97a12; }
.reg-banner--amber:hover { background: #a8630a; }

/* Новая задача — тот же баннер, зелёный: не тревога (красный) и не «ждёт
   вашего решения» (янтарный), а просто «вам поручили, загляните». Оттенок
   заметно темнее «весёлого» зелёного, чтобы белый текст читался и чтобы
   баннер не выбивался из общей приглушённой палитры документа. */
.reg-banner--green { background: #2e7d4f; }
.reg-banner--green:hover { background: #24663f; }

/*
 * Баннер «уведомления браузера отключены» — видят все сотрудники одинаково
 * (не только owner/admin, в отличие от баннера заявок выше). Показывается
 * заново при каждом новом входе, пока разрешение не дадут — состояние
 * закрытия («понятно, не сейчас») намеренно не сохраняется ни в localStorage,
 * ни на сервере: просто JS-переменной нет вовсе, `hidden` в разметке —
 * единственный источник истины, значит при следующей загрузке страницы
 * (то есть при новом входе) вопрос звонит снова, пока проблема не решена.
 */
.notify-banner { background: #fff8ec; border-bottom: 1px solid #f0dfb8; }
.notify-banner[hidden] { display: none; }
.notify-banner__row { display: flex; align-items: flex-start; gap: 12px; padding: 10px 20px; }
.notify-banner__icon { font-size: 15px; flex: none; margin-top: 1px; }
.notify-banner__text { flex: 1; min-width: 0; font-size: 13px; color: #4a3c1a; line-height: 1.5; }
.notify-banner__text b { color: var(--ink); }
.notify-banner__link {
  border: 0; background: transparent; color: var(--pen); cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px; font: inherit;
  font-size: 12.5px; padding: 0; margin-left: 4px;
}
.notify-banner__close { border: 0; background: transparent; color: #a0895a; cursor: pointer; font-size: 13px; flex: none; padding: 2px; }
.notify-banner__guide { padding: 0 20px 14px 47px; }
.notify-banner__guide[hidden] { display: none; }
.notify-banner__guide b { font-size: 12.5px; color: var(--ink); }
.notify-banner__guide ol { margin: 6px 0 0; padding-left: 18px; font-size: 12.5px; color: #3a3834; line-height: 1.7; }

.board__head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 20px;
  border-bottom: 1px solid var(--rule);
}
.board__burger { display: none; }
.board__title { flex: 1; min-width: 0; cursor: pointer; border-radius: 8px; padding: 3px 6px; margin: -3px -6px; }
.board__title:hover { background: var(--sheet-2); }
.board__title h1 { font-size: 16.5px; line-height: 1.3; }
.board__title p {
  margin: 1px 0 0;
  font-size: 12.5px;
  color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.board__title p .dot { display: inline-block; vertical-align: middle; margin-right: 5px; margin-bottom: 1px; }
.board__title p .board__dotsep { color: var(--faint); margin: 0 5px; }
.board__tools { display: flex; gap: 6px; position: relative; }
.board__more { display: none; }
.board__tools-menu { display: flex; gap: 6px; }

/* ---------- лента пузырями ----------
   Свои сообщения — справа, цветом акцента; чужие — слева, светлые.
   Асимметричный радиус угла заменяет классический «хвостик».         */

.log-wrap { position: relative; min-width: 0; min-height: 0; }
.log {
  height: 100%;
  overflow-y: auto;
  padding: 16px 24px 10px;
  scroll-behavior: smooth;
  scrollbar-width: thin;
  scrollbar-color: var(--desk) transparent;
  transition: padding-top .15s ease;
  /* Ручная компенсация скролла при подгрузке старой истории (см. loadOlder()
     в app.js) уже сама следит за тем, чтобы взгляд оставался на том же
     сообщении. Встроенное в браузер «якорение скролла» (CSS Scroll
     Anchoring, включено по умолчанию везде) пытается делать ровно то же
     самое независимо и одновременно — из-за этого возможна задвоенная
     компенсация. Отключаем встроенный механизм явно, чтобы за это отвечал
     только один, наш собственный расчёт. */
  overflow-anchor: none;
}
@media (prefers-reduced-motion: reduce) { .log { scroll-behavior: auto; } }

.log::-webkit-scrollbar { width: 9px; }
.log::-webkit-scrollbar-track { background: transparent; }
.log::-webkit-scrollbar-thumb {
  background: var(--desk);
  border-radius: 5px;
  border: 2px solid var(--sheet);
  background-clip: padding-box;
}
.log::-webkit-scrollbar-thumb:hover {
  background: var(--faint);
  background-clip: padding-box;
}

.day {
  display: flex;
  justify-content: center;
  max-width: 760px;
  margin: 18px auto;
}
.day span {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .04em;
  color: var(--muted);
  background: var(--sheet-2);
  border: 0.5px solid var(--rule);
  border-radius: 100px;
  padding: 5px 14px;
}

/* Системное уведомление («Иван добавлен в канал») — в том же ключе, что и
   разделитель дня выше, но легче и без рамки: это частое рядовое событие,
   а не веха вроде смены дня, и визуально их не стоит путать. */
.log__system {
  display: flex;
  justify-content: center;
  max-width: 760px;
  margin: 8px auto;
}
.log__system span {
  font-family: var(--ui);
  font-size: 11.5px;
  color: var(--faint);
  background: var(--sheet-2);
  border-radius: 100px;
  padding: 4px 12px;
}

.msg {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  padding: 1px 6px;
  max-width: 760px;
  margin-left: auto;
  margin-right: auto;
  scroll-margin-top: 80px;
}
.msg:not(.grouped) { margin-top: 10px; }
.msg.mine { flex-direction: row-reverse; justify-content: flex-start; }
.msg.is-new { animation: settle .18s ease-out; }
@keyframes settle { from { opacity: 0; transform: translateY(5px); } }

.bubble-spacer { width: 26px; flex: none; }
.bubble-col { display: flex; flex-direction: column; max-width: min(72%, 480px); min-width: 0; }
.msg.mine .bubble-col { align-items: flex-end; }

.bubble-name {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 12.5px;
  color: var(--ink);
  margin: 0 0 3px 3px;
}
.bubble-name[data-dm] { cursor: pointer; }
.bubble-name[data-dm]:hover { text-decoration: underline; }
.avatar-trigger { display: block; cursor: pointer; border-radius: 50%; }
.avatar-trigger:hover { filter: brightness(0.92); }

.bubble-forwarded {
  font-family: var(--ui);
  font-weight: 600;
  font-size: 11.5px;
  margin-bottom: 4px;
}
.bubble--other .bubble-forwarded { color: var(--pen); }
.bubble--mine .bubble-forwarded { color: rgba(255,255,255,.82); }

.bubble-quote {
  border-left: 3px solid var(--pen);
  border-radius: 0 6px 6px 0;
  padding: 4px 9px;
  margin-bottom: 6px;
  cursor: pointer;
}
.bubble-quote--media { display: flex; align-items: center; gap: 8px; }
.bubble-quote__thumb {
  flex: none;
  width: 34px; height: 34px;
  border-radius: 4px;
  object-fit: cover;
}
.bubble-quote__text { min-width: 0; flex: 1; }
.bubble-quote b { display: block; font-family: var(--ui); font-weight: 600; font-size: 12px; }
.bubble-quote span {
  display: block; font-size: 12px; line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bubble--other .bubble-quote { background: var(--sheet-2); }
.bubble--other .bubble-quote b { color: var(--pen); }
.bubble--other .bubble-quote span { color: var(--muted); }
.bubble--mine .bubble-quote { background: rgba(255,255,255,.14); border-left-color: rgba(255,255,255,.6); }
.bubble--mine .bubble-quote b { color: rgba(255,255,255,.92); }
.bubble--mine .bubble-quote span { color: rgba(255,255,255,.78); }

.bubble {
  position: relative;
  padding: 8px 12px 6px;
  border-radius: 15px;
  min-width: 0;
  max-width: 100%;
}
.bubble--other { background: var(--sheet); border: 0.5px solid var(--rule); border-top-left-radius: 4px; }
.bubble--mine { background: var(--pen); border-top-right-radius: 4px; }
.bubble.is-flash { animation: bubbleflash 1.7s ease-out; }
@keyframes bubbleflash {
  0%, 25% { box-shadow: 0 0 0 3px #f5a623, 0 0 16px 3px rgba(245, 166, 35, .55); }
  100%    { box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent; }
}

.bubble-text {
  font-family: var(--body-sans);
  font-size: 14.5px;
  line-height: 1.48;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.bubble--other .bubble-text { color: var(--ink-2); }
.bubble--mine .bubble-text { color: #fff; }
.bubble--mine .bubble-text a { color: #dbe6ff; }
.bubble--other .bubble-text a { text-decoration: underline; text-underline-offset: 2px; }
.bubble--gone .bubble-text { font-style: italic; }
.bubble--other.bubble--gone .bubble-text { color: var(--faint); }
.bubble--mine.bubble--gone .bubble-text { color: rgba(255,255,255,.75); }

.bubble-meta {
  display: block;
  text-align: right;
  font-family: var(--mono);
  font-size: 10.5px;
  margin-top: 3px;
}
.bubble--other .bubble-meta { color: var(--faint); }
.bubble--mine .bubble-meta { color: rgba(255,255,255,.72); }
.bubble-status { letter-spacing: -2px; }
.bubble-status.is-read { color: #7fd9ff; }

.bubble-acts {
  display: flex;
  gap: 2px;
  align-self: flex-end;
  padding-bottom: 4px;
  opacity: 0;
  transition: opacity .12s;
}
.msg:hover .bubble-acts { opacity: 1; }
.bubble-acts .icon-btn {
  width: 26px; height: 26px; font-size: 12px;
  background: #fff; border: 1px solid var(--rule); border-radius: 7px;
}

/* вложения — та же плашка, с поправкой на цветной фон своих пузырей */
.files { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
.file {
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 300px;
  padding: 8px 13px 8px 9px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: #fff;
  text-decoration: none;
  color: inherit;
  transition: border-color .15s, background .15s;
}
.file:hover { border-color: var(--pen); background: var(--pen-wash); }
.file--gone { cursor: default; opacity: .7; }
.file--gone:hover { border-color: var(--rule); background: #fff; }
.file--gone .file__meta span { font-style: italic; }
.file__ext {
  flex: none;
  display: grid;
  place-items: center;
  width: 32px; height: 32px;
  border-radius: 5px;
  background: var(--sheet-2);
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: .03em;
  color: var(--ink-2);
  text-transform: uppercase;
}
.file__meta { min-width: 0; }
.file__meta b {
  display: block;
  font-family: var(--ui);
  font-weight: 500;
  font-size: 13.5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.file__meta span { font-family: var(--mono); font-size: 10.5px; color: var(--muted); }
.bubble--mine .file { background: rgba(255,255,255,.14); border-color: rgba(255,255,255,.32); color: #fff; }
.bubble--mine .file:hover { background: rgba(255,255,255,.22); }
.bubble--mine .file__ext { background: rgba(255,255,255,.2); color: #fff; }
.bubble--mine .file__meta span { color: rgba(255,255,255,.75); }
.shot {
  display: block;
  max-width: min(340px, 100%);
  max-height: 300px;
  /* HTML-атрибуты width/height у самого <img> (см. attachmentsMarkup в
     app.js) браузер по умолчанию читает как базовые width/height —
     наравне с обычным CSS-свойством, только более низкого приоритета.
     Без явного auto здесь max-width и max-height сжимали бы ширину и
     высоту НЕЗАВИСИМО друг от друга, каждую до своего предела, — картинка
     растягивалась вне исходной пропорции. auto возвращает браузеру
     стандартный расчёт «уместить в рамку с сохранением пропорций», но
     сама рамка резервируется мгновенно, ещё до загрузки: атрибуты
     width/height у тега достаточно для этого сами по себе, отдельного
     запрета на auto тут не требуется. */
  width: auto;
  height: auto;
  border-radius: 10px;
}

.log__more { display: grid; place-items: center; padding: 10px 0; }
.log__note {
  padding: 30px 20px;
  color: var(--muted);
  font-size: 14px;
  max-width: 56ch;
}
.log__note b {
  display: block;
  font-family: var(--display);
  color: var(--ink);
  font-size: 16px;
  margin-bottom: 5px;
}

/* Лента сдвигается вниз, когда плашка закрепления показана — иначе первые
   сообщения оказались бы у неё под низом. */
.log--has-pin { padding-top: 56px; }

/*
 * Плашка закреплённого сообщения — тем же приёмом, что и .jump/.typing
 * чуть ниже: абсолютно спозиционирована внутри .log-wrap, не требует
 * менять сетку .board (см. хендофф — там уже был баг именно из-за лишней
 * строки грид-сетки, когда .reg-banner скрыт; повторять тот же класс
 * ошибки здесь незачем).
 */
.pin-bar {
  position: absolute; top: 0; left: 0; right: 0; z-index: 6;
  display: flex; align-items: center; gap: 10px;
  padding: 9px 16px;
  background: #fff8ec;
  border-bottom: 1px solid #f0dfb8;
  cursor: pointer;
  text-align: left;
  font: inherit;
}
.pin-bar[hidden] { display: none; }
.pin-bar__dots { display: flex; flex-direction: column; gap: 3px; flex: none; }
.pin-bar__dot { width: 3px; height: 14px; border-radius: 2px; background: #f0dfb8; }
.pin-bar__dot.is-active { background: #b5791a; height: 18px; }
.pin-bar__icon { color: #b5791a; font-size: 14px; flex: none; }
.pin-bar__text { flex: 1; min-width: 0; }
.pin-bar__label { font-size: 10.5px; color: #b5791a; font-weight: 600; letter-spacing: .02em; }
.pin-bar__preview { font-size: 12.5px; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pin-bar__close {
  border: 0; background: transparent; color: #b5791a; cursor: pointer;
  font-size: 14px; flex: none; padding: 4px 6px; border-radius: 6px;
}
.pin-bar__close:hover { background: rgba(181, 121, 26, .14); }

/* Метка 📌 у самого закреплённого сообщения в ленте — видно и без плашки, если пролистать выше. */
.bubble-pinned { color: #b5791a; font-size: 11px; margin-left: 4px; }

.jump {
  position: absolute;
  left: 50%; bottom: 14px;
  transform: translateX(-50%);
  padding: 7px 16px;
  border: 0;
  border-radius: 100px;
  background: var(--cover);
  color: #fff;
  font-family: var(--ui);
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
  box-shadow: var(--shadow);
}
.jump[hidden] { display: none; }
.jump__icon { display: none; }
.jump__count { display: none; }

.typing {
  position: absolute;
  left: 26px; bottom: 2px;
  font-family: var(--body);
  font-style: italic;
  font-size: 12.5px;
  color: var(--muted);
  pointer-events: none;
}


/* ---------- поле ввода ---------- */

.composer { position: relative; border-top: 1px solid var(--rule); padding: 12px 24px 14px; }

.emoji-picker {
  position: absolute;
  left: 24px; right: 24px; bottom: calc(100% + 8px);
  max-width: 420px;
  background: var(--sheet);
  border: 1px solid var(--rule);
  border-radius: 12px;
  box-shadow: var(--shadow);
  padding: 11px;
  z-index: 20;
}
.emoji-picker[hidden] { display: none; }
.emoji-picker__search {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  background: #fff;
  font-size: 13px;
  margin-bottom: 9px;
}
.emoji-picker__search:focus { border-color: var(--pen); outline: none; }
.emoji-picker__tabs {
  display: flex;
  gap: 2px;
  margin-bottom: 9px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--rule-soft);
}
.emoji-picker__tabs button {
  width: 28px; height: 28px;
  border: 0; border-radius: 7px;
  background: transparent;
  font-size: 14px;
  cursor: pointer;
}
.emoji-picker__tabs button:hover { background: var(--sheet-2); }
.emoji-picker__tabs button.is-on { background: var(--pen); }
.emoji-picker__grid {
  max-height: 220px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--desk) transparent;
}
.emoji-picker__grid::-webkit-scrollbar { width: 9px; }
.emoji-picker__grid::-webkit-scrollbar-track { background: transparent; }
.emoji-picker__grid::-webkit-scrollbar-thumb {
  background: var(--desk);
  border-radius: 5px;
  border: 2px solid var(--sheet);
  background-clip: padding-box;
}
.emoji-picker__grid::-webkit-scrollbar-thumb:hover {
  background: var(--faint);
  background-clip: padding-box;
}
.emoji-picker__legend {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .04em;
  color: var(--muted);
  margin: 8px 0 6px;
}
.emoji-picker__legend:first-child { margin-top: 0; }
.emoji-picker__row {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 1px;
}
.emoji-picker__row button {
  width: 100%; aspect-ratio: 1;
  border: 0; border-radius: 7px;
  background: transparent;
  font-size: 19px;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.emoji-picker__row button:hover { background: var(--sheet-2); }
.emoji-picker__empty { padding: 20px 6px; text-align: center; color: var(--muted); font-size: 13px; }

@media (max-width: 860px) {
  .emoji-picker { left: 14px; right: 14px; max-width: none; }
}
.composer__row { display: flex; align-items: flex-end; gap: 9px; }
.composer__row textarea {
  flex: 1;
  min-height: 42px;
  max-height: 220px;
  padding: 10px 13px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: #fff;
  font-family: var(--body-sans);
  font-size: 15px;
  line-height: 1.55;
  resize: none;
}
.composer__row textarea:focus { border-color: var(--pen); outline: none; }
.composer__row .icon-btn { margin-bottom: 5px; }
.composer__hint { margin: 7px 0 0 41px; font-size: 11.5px; color: var(--faint); }
.composer__files { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 9px; }
.composer__files[hidden] { display: none; }

.pending-forward {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 8px 11px;
  margin-bottom: 9px;
  border-radius: 8px;
  background: var(--pen-wash);
  border-left: 3px solid var(--pen);
}
.pending-forward[hidden] { display: none; }
.pending-forward__icon { color: var(--pen); font-size: 15px; flex: none; margin-top: 1px; }
.pending-forward__body { flex: 1; min-width: 0; }
.pending-forward__title {
  font-family: var(--ui); font-weight: 600; font-size: 12px; color: var(--pen-deep);
}
.pending-forward__preview {
  font-size: 12.5px; color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pending-forward__preview--media {
  display: flex; align-items: center; gap: 8px;
  overflow: visible; white-space: normal;
}
.pending-forward__preview--media span {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.pending-forward__thumb { flex: none; width: 28px; height: 28px; border-radius: 4px; object-fit: cover; }
.pending-forward__close { flex: none; width: 26px; height: 26px; }

.pend {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 7px 5px 11px;
  border: 1px solid var(--rule);
  border-radius: 100px;
  background: var(--sheet-2);
  font-size: 13px;
  overflow: hidden;
}
.pend__bar {
  position: absolute;
  top: 0; left: 0; bottom: 0;
  width: 100%;
  background: var(--pen-wash);
  transition: width .12s linear;
}
.pend__name { position: relative; }
.pend span { font-family: var(--mono); font-size: 10.5px; color: var(--muted); position: relative; }
.pend span.pend__pct { color: var(--pen); flex: none; }
.pend .icon-btn { width: 20px; height: 20px; font-size: 11px; position: relative; }

.drop-veil {
  position: absolute;
  inset: 0;
  display: none;
  place-items: center;
  background: rgba(252,252,251,.95);
  border: 2px dashed var(--pen);
  z-index: 5;
  font-family: var(--display);
  font-weight: 600;
  color: var(--pen-deep);
}
.board.is-dropping .drop-veil { display: grid; }

/* ---------- панель администратора ----------
   Занимает то же место сетки, что и доска сообщений — открывается вместо
   неё, а не поверх. Правая панель (поиск/файлы) в этом режиме принудительно
   закрывается: искать файлы, пока настраиваешь людей, незачем.            */

.admin {
  display: none;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--sheet);
  border-left: 1px solid var(--rule);
  grid-column: 2 / -1;
}
.app.admin-open #admin-panel { display: flex; }
.app.admin-open #news-panel { display: none; }
.app.admin-open .board, .app.admin-open .panel { display: none; }
.app.news-open #news-panel { display: flex; }
.app.news-open #admin-panel { display: none; }
.app.news-open .board, .app.news-open .panel { display: none; }
/* Задачи — третий экран того же уровня. Каждый из трёх режимов гасит два
   других явно, а не полагается на то, что классы взаимоисключающие: в JS
   они действительно снимаются при открытии (см. openTasksPanel), но если
   когда-нибудь два класса окажутся на #app одновременно, лучше показать
   один экран поверх правил, чем два внахлёст в одной ячейке грида. */
.app.tasks-open #tasks-panel { display: flex; }
.app.tasks-open #admin-panel, .app.tasks-open #news-panel { display: none; }
.app.tasks-open .board, .app.tasks-open .panel { display: none; }
.app.admin-open #tasks-panel, .app.news-open #tasks-panel { display: none; }

.admin__head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 22px;
  border-bottom: 1px solid var(--rule);
}
.admin__title { flex: 1; min-width: 0; }
.admin__title h1 { font-size: 16.5px; }
.admin__title p { margin: 1px 0 0; font-size: 12px; color: var(--muted); }

.admin__tabs {
  margin: 0;
  padding: 10px 22px;
  border-bottom: 1px solid var(--rule);
}
.admin__tabs .tabs__btn { color: var(--muted); }
.admin__tabs .tabs__btn.is-active { background: var(--pen); color: #fff; }
.admin__tabs .tabs__btn[hidden] { display: none; }

/* Второй, более лёгкий ряд фильтров: первый ряд отвечает, чьи это задачи,
   этот — в каком они состоянии. Счётчик остаётся на самой кнопке, поэтому
   пользователь видит очередь ещё до переключения. */
.task-status-tabs {
  display: flex;
  gap: 7px;
  padding: 10px 22px;
  border-bottom: 1px solid var(--rule);
  overflow-x: auto;
  scrollbar-width: none;
}
.task-status-tabs::-webkit-scrollbar { display: none; }
.task-status-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 6px 10px;
  background: var(--sheet);
  color: var(--muted);
  font: 600 12px var(--sans);
  cursor: pointer;
}
.task-status-tab span {
  display: inline-grid;
  place-items: center;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: 99px;
  background: var(--sheet-2);
  font: 11px var(--mono);
}
.task-status-tab:hover { border-color: var(--pen); color: var(--pen-deep); }
.task-status-tab.is-active { border-color: var(--pen); background: var(--pen); color: #fff; }
.task-status-tab.is-active span { background: rgba(255,255,255,.2); color: #fff; }
.task-status-tab--late:not(.is-active) { color: var(--stamp); }

.admin__body {
  flex: 1;
  overflow-y: auto;
  padding: 14px 22px 24px;
  scrollbar-width: thin;
  scrollbar-color: var(--desk) transparent;
}
.admin__body::-webkit-scrollbar { width: 9px; }
.admin__body::-webkit-scrollbar-track { background: transparent; }
.admin__body::-webkit-scrollbar-thumb {
  background: var(--desk);
  border-radius: 5px;
  border: 2px solid var(--sheet);
  background-clip: padding-box;
}
.admin__body::-webkit-scrollbar-thumb:hover {
  background: var(--faint);
  background-clip: padding-box;
}

/* ---------- Задачи ----------
   Тело раздела повторяет .admin__body (прокрутка, отступы, тонкий ползунок),
   но список здесь одноколоночный и ограничен по ширине: карточка задачи это
   строка «что сделать + кто + срок», её незачем растягивать на весь широкий
   экран, читать такую строку глазами слева направо неудобно.               */

/* Серый фон панели (--sheet-2), а не тот же белый (--sheet), что и у
   карточек, — иначе граница карточки в 1px остаётся единственным намёком
   на то, что это отдельный блок, а не просто текст на пустом месте. Тот же
   приём уже используется в детали задачи (.news-sheet__body серый, сама
   карточка .task-sheet__col — белая с тенью); здесь ровно то же самое,
   но без тени, потому что карточек в списке много и тень на каждой была
   бы избыточной рябью — контраста одного цвета уже достаточно. */
.tasks-body {
  flex: 1;
  overflow-y: auto;
  padding: 16px 22px 28px;
  background: var(--sheet-2);
  scrollbar-width: thin;
  scrollbar-color: var(--desk) transparent;
}
.tasks-body::-webkit-scrollbar { width: 9px; }
.tasks-body::-webkit-scrollbar-track { background: transparent; }
.tasks-body::-webkit-scrollbar-thumb {
  background: var(--desk);
  border-radius: 5px;
  border: 2px solid var(--sheet);
  background-clip: padding-box;
}
.tasks-body::-webkit-scrollbar-thumb:hover { background: var(--faint); background-clip: padding-box; }

.tasks-inner { max-width: 760px; }

/* Заголовок стопки «В работе»/«Выполненные». Это кнопка (стопка сворачивается),
   поэтому сбрасываем оформление кнопки полностью, а не полагаемся на .btn. */
.task-group__head {
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  padding: 6px 2px;
  margin: 0 0 9px;
  border: 0;
  background: transparent;
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .02em;
  color: var(--muted);
  text-transform: uppercase;
  cursor: pointer;
  text-align: left;
}
.task-group__head:hover { color: var(--ink-2); }
/* Заголовок несворачиваемой стопки — это <button disabled> (см. renderTasksBody).
   Браузер по умолчанию гасит такие кнопки, а здесь это просто подпись, не
   выключенное управление: возвращаем обычный цвет и убираем реакцию курсора. */
.task-group__head:disabled { color: var(--muted); opacity: 1; cursor: default; }
.task-group__head:disabled:hover { color: var(--muted); }
.task-group__head .icn { flex: none; }
.task-group + .task-group { margin-top: 18px; }
.task-group__list[hidden] { display: none; }

/* Карточка кликабельна целиком (клик проваливается в деталь, см.
   openTaskDetail в app.js) — но остаётся div, а не button: внутри уже есть
   свои кнопки («Выполнено», удалить), и button в button невалиден. Роль и
   tabindex для доступности выставляются в разметке (см. taskCardHTML). */
.task-card {
  display: block;
  width: 100%;
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 12px 15px;
  background: var(--sheet);
  margin-bottom: 9px;
  cursor: pointer;
  transition: border-color .15s, background .15s;
}
.task-card:hover { border-color: var(--pen); background: var(--pen-wash); }
/* Новое поручение, которое человек ещё не открывал, — синяя полоса слева,
   ровно тем же приёмом, что и непрочитанная новость (.news-card:not(.is-read)),
   чтобы «новое» выглядело одинаково во всех разделах. */
.task-card.is-new { box-shadow: inset 3px 0 0 var(--pen); }
/* Серый фон, в тон панели, — задумано так: выполненная задача должна
   визуально «оседать» в фон, а не оставаться такой же заметной белой
   карточкой, как активные. Рамка при этом остаётся (border-color ниже),
   так что карточка не пропадает совсем — просто теряет тот акцент, что
   даёт белая заливка активным задачам. */
/* Раньше здесь был --sheet-2 — тот же оттенок, что и у самой панели
   (.tasks-body), из-за чего карточка выполненной задачи не «оседала в
   фон», а буквально сливалась с ним, без единой заливки внутри рамки.
   --desk на ступень темнее (уже используется в проекте как раз для этого:
   фон подложки страницы позади самого приложения, полоса прокрутки) —
   серый, но заметно отличимый и от белых активных карточек, и от фона
   панели под ним. */
.task-card.is-done { background: var(--desk); border-color: var(--rule-soft); }
.task-card.is-done .task-card__title { color: var(--muted); text-decoration: line-through; }

.task-card__top { display: flex; align-items: flex-start; gap: 9px; margin-bottom: 4px; }
/* Заголовок и превью описания обрезаются в две строки многоточием — иначе
   задача с многострочным текстом (тело — до 2000 символов, с переносами
   строк, см. MAX_BODY на сервере) раздувается в разы выше соседних карточек,
   и стопка перестаёт читаться единым списком. Полный текст остаётся виден
   в деталях по клику — здесь только превью, тем же принципом, что и у
   .news-card__excerpt (короткая выжимка, не полный текст). */
.task-card__title {
  flex: 1;
  min-width: 0;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.task-card__body {
  font-size: 12.5px;
  color: var(--muted);
  line-height: 1.45;
  margin: 0 0 9px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.task-card__foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.task-card__who { font-size: 12px; color: var(--muted); min-width: 0; }
.task-card__who b { font-weight: 600; color: var(--ink-2); }
.task-card__due {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
}
.task-card__due--late { color: var(--stamp); font-weight: 700; }
.task-card__acts { display: flex; gap: 7px; flex: none; }
.task-card__acts .btn { padding: 6px 12px; font-size: 12.5px; display: inline-flex; align-items: center; gap: 5px; }

.task-pill {
  font-size: 11px;
  padding: 3px 9px;
  border-radius: 100px;
  white-space: nowrap;
  flex: none;
}
.task-pill--late { background: #fceaea; color: var(--stamp); }
.task-pill--new { background: var(--pen-wash); color: var(--pen-deep); }
.task-pill--progress { background: #fff4dd; color: #8a5a10; }
.task-pill--done { background: #eef4ee; color: #2f6b3a; }

/* Скрепка и число реплик в подвале карточки — видно, что у задачи есть
   файлы или обсуждение, ещё до того, как в неё провалились. */
.task-card__counts { display: inline-flex; align-items: center; gap: 10px; }
.task-card__count { display: inline-flex; align-items: center; gap: 3px; font-size: 11px; color: var(--faint); }

/* ---------- Деталь задачи (task-sheet) ----------
   Тот же приём, что и у открытой новости (.news-sheet__col): не голый
   текст на сером фоне листа, а отдельная белая карточка с лёгкой тенью,
   центрированная в прокручиваемой области. Без этого текст просто висел
   в пустоте на всю ширину экрана. */
.task-sheet__col {
  max-width: 930px;
  margin: 0 auto;
  background: var(--sheet);
  border-radius: 10px;
  padding: 26px 32px;
  box-shadow: 0 1px 3px rgba(20,26,36,.05);
}
/* Тег заголовка — div, не h2: общее правило `.news-sheet__body h2` (у
   открытой новости) более специфично, чем один класс, и перебивало бы
   здесь и размер шрифта, и отступ, если бы заголовок задачи тоже был h2. */
.task-sheet__title { font-size: 19px; font-weight: 700; line-height: 1.3; margin: 0 0 14px; }

/* Статус и срок — слева, кнопка действия — справа; разделительная линия
   отделяет шапку от текста задачи, той же ролью, что играет отступ после
   h2 у новости, только здесь секций больше и черта помогает не слипаться. */
.task-sheet__tags {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  padding-bottom: 16px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--rule-soft);
}
.task-sheet__badges { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.task-sheet__body { font-size: 13.5px; line-height: 1.6; white-space: pre-line; margin: 0 0 24px; }
.task-sheet__body:empty { display: none; }

.task-sheet__section-label {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .02em;
  color: var(--muted);
  text-transform: uppercase;
  margin: 0 0 10px;
}
.task-sheet__section + .task-sheet__section { margin-top: 24px; }

.task-files { display: flex; flex-direction: column; gap: 8px; margin-bottom: 24px; }
/* Внутри белой карточки строка файла с фирменным белым фоном .file
   сливалась бы с самой карточкой — здесь ей нужен собственный, чуть
   более серый оттенок, чтобы читаться как отдельный элемент. */
.task-files .file { max-width: none; width: 100%; box-sizing: border-box; background: var(--sheet-2); }
.task-files .file:hover { background: var(--pen-wash); }
.task-file__del { flex: none; color: var(--faint); }
.task-file__del:hover { color: var(--stamp); }

.task-file-add {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 10px;
  border: 1px dashed var(--rule-soft);
  border-radius: var(--radius);
  color: var(--muted);
  font-size: 12.5px;
  cursor: pointer;
  background: transparent;
  width: 100%;
}
.task-file-add:hover { border-color: var(--pen); color: var(--pen-deep); background: var(--pen-wash); }

/* Зона файлов в окне «Новая задача» (openTaskEditor) — тот же дашд-бордер
   .task-file-add снизу играет роль кнопки-приглашения, а сама обёртка
   только держит список уже приложенных файлов и подсвечивается целиком,
   когда курсор с файлом проходит именно над ней (см. dragover/dragleave
   в openTaskEditor). Функциональный перехват drop — на всей модалке (см.
   .modal__card в openModal), эта подсветка — чисто визуальная подсказка. */
.task-file-drop { display: flex; flex-direction: column; gap: 8px; margin-bottom: 4px; }
.task-file-drop #task-new-files-list:empty { display: none; }
.task-file-drop.is-dropping .task-file-add {
  border-color: var(--pen);
  color: var(--pen-deep);
  background: var(--pen-wash);
}

.task-thread-empty {
  border: 1px solid var(--rule-soft);
  border-radius: var(--radius);
  padding: 14px;
  text-align: center;
  font-size: 12.5px;
  color: var(--faint);
}

/* Ветка комментариев. Своя реплика ("mine") прижимается к тому же правому
   краю и красится тем же фирменным синим, что и собственные сообщения в
   чате (.bubble--mine) — единообразие с остальным интерфейсом важнее
   необычности: обсуждение внутри задачи это тот же разговор, просто
   в другом месте. Чужая реплика — светло-серый фон, а не белый: на белой
   карточке белый пузырь потерялся бы совсем. */
.task-thread { display: flex; flex-direction: column; gap: 14px; margin-bottom: 4px; }
.task-comment { display: flex; gap: 9px; max-width: 88%; }
.task-comment.mine { align-self: flex-end; flex-direction: row-reverse; }
.task-comment__av { flex: none; }
.task-comment__col { min-width: 0; }
.task-comment__head { display: flex; align-items: baseline; gap: 7px; margin-bottom: 2px; }
.task-comment.mine .task-comment__head { flex-direction: row-reverse; }
.task-comment__name { font-size: 12.5px; font-weight: 600; }
.task-comment__time { font-family: var(--mono); font-size: 10.5px; color: var(--faint); }
.task-comment__body {
  font-size: 13.5px;
  line-height: 1.45;
  background: var(--sheet-2);
  border-radius: 10px;
  border-top-left-radius: 4px;
  padding: 8px 12px;
  white-space: pre-wrap;
  word-break: break-word;
  color: var(--ink-2);
}
.task-comment.mine .task-comment__body {
  background: var(--pen);
  color: #fff;
  border-radius: 10px;
  border-top-left-radius: 10px;
  border-top-right-radius: 4px;
}
.task-comment.mine .task-comment__body a { color: #dbe6ff; }
.task-comment__del {
  align-self: center;
  flex: none;
  color: var(--faint);
  opacity: 0;
  transition: opacity .12s;
}
.task-comment:hover .task-comment__del { opacity: 1; }
.task-comment__del:hover { color: var(--stamp); }

/* Поле ответа — приклеено к низу листа тем же способом, что и .composer
   к низу ленты: sticky внутри прокручиваемого .news-sheet__body, а не
   fixed, чтобы не накладываться на системную полосу прокрутки. Полоса
   идёт во всю ширину листа (а не по ширине карточки) — так же, как
   строка ввода в самом чате всегда шире видимой ленты сообщений. */
/* Раньше поле ответа растягивалось на всю ширину листа отрицательными
   отступами (перекрывая padding самого .news-sheet__body) — тем же
   приёмом, что и composer под лентой сообщений в чате, где это уместно,
   потому что сама лента тоже во всю ширину. Здесь текст задачи лежит на
   узкой белой карточке (.task-sheet__col, max-width: 930px) — и поле
   ответа теперь той же ширины и с тем же центрированием, чтобы обе части
   визуально составляли одну колонку, а не две разной ширины друг под
   другом. */
.task-composer {
  position: sticky;
  bottom: -24px;
  max-width: 930px;
  margin: 24px auto -24px;
  padding: 12px 0;
  background: var(--sheet-2);
  border-top: 1px solid var(--rule);
  display: flex;
  align-items: flex-end;
  gap: 8px;
}
.task-composer textarea {
  flex: 1;
  min-width: 0;
  resize: none;
  max-height: 120px;
  padding: 9px 12px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: #fff;
  font: inherit;
  font-size: 13.5px;
  line-height: 1.4;
}
.task-composer textarea:focus { border-color: var(--pen); outline: none; }

/* ---------- Новости ---------- */

.news-filters { display: flex; gap: 10px; padding: 14px 24px 0; flex-wrap: wrap; position: relative; }
.news-filter-select {
  display: flex; align-items: center; gap: 8px; padding: 9px 14px;
  border: 1px solid var(--rule); border-radius: 9px; background: var(--sheet);
  font-size: 13px; color: var(--ink); cursor: pointer; user-select: none; position: relative;
}
.news-filter-select svg { color: var(--muted); flex: none; transition: transform .15s; }
.news-filter-select.is-open { border-color: var(--pen); box-shadow: 0 0 0 3px var(--pen-wash); }
.news-filter-select.is-open svg { transform: rotate(180deg); }
.news-filter-search {
  display: flex; align-items: center; gap: 8px; padding: 9px 14px;
  border: 1px solid var(--rule); border-radius: 9px; background: var(--sheet); flex: 1; min-width: 180px;
}
.news-filter-search svg { color: var(--muted); flex: none; }
.news-filter-search input { border: 0; outline: 0; font-size: 13px; width: 100%; font-family: inherit; color: var(--ink); background: transparent; }
.news-filter-dropdown {
  position: absolute; top: calc(100% + 6px); left: 0; min-width: 220px; max-height: 260px; overflow-y: auto;
  background: var(--sheet); border: 1px solid var(--rule); border-radius: 10px;
  box-shadow: 0 10px 28px rgba(20,26,36,.16); z-index: 5; display: none; padding: 6px;
}
.news-filter-dropdown.is-open { display: block; }
.news-filter-opt { display: flex; align-items: center; justify-content: space-between; padding: 8px 10px; border-radius: 7px; font-size: 13px; color: var(--ink); cursor: pointer; }
.news-filter-opt:hover { background: var(--sheet-2); }
.news-filter-opt.is-selected { color: var(--pen); font-weight: 600; }

.news-filter-chip {
  display: flex; align-items: center; gap: 6px; padding: 9px 14px;
  border: 1px solid var(--rule); border-radius: 9px; background: var(--sheet);
  font-size: 13px; color: var(--ink); cursor: pointer; white-space: nowrap; flex: none;
  transition: background .15s, border-color .15s, color .15s;
}
.news-filter-chip svg { color: var(--faint); flex: none; }
.news-filter-chip.is-on { background: #fff3de; border-color: #f0c56b; color: #a56a10; font-weight: 600; }
.news-filter-chip.is-on svg { color: #e08a1e; }

.news-grid {
  padding: 20px 24px;
  overflow-y: auto;
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: 14px;
  align-content: start;
}
.news-grid__empty { grid-column: 1 / -1; text-align: center; color: var(--muted); padding: 40px 0; font-size: 13.5px; }
.news-card {
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 14px 16px;
  background: var(--sheet);
  cursor: pointer;
  text-align: left;
  font: inherit;
  transition: box-shadow .15s, border-color .15s;
}
.news-card:hover { box-shadow: 0 4px 14px rgba(20,26,36,.08); }
.news-card.is-important { border-color: #f0c56b; background: #fffaf0; }
.news-card:not(.is-read) { box-shadow: inset 3px 0 0 var(--pen); }
.news-card:not(.is-read):hover { box-shadow: inset 3px 0 0 var(--pen), 0 4px 14px rgba(20,26,36,.08); }
.news-card__title-row { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
.news-card__bolt { color: #e08a1e; flex: none; }
.news-card__title { font-size: 13.5px; font-weight: 600; color: var(--ink); line-height: 1.35; }
.news-card__excerpt { font-size: 11.5px; color: var(--muted); line-height: 1.4; margin-bottom: 8px; }
.news-card__meta { display: flex; align-items: center; justify-content: space-between; font-size: 10.5px; color: var(--faint); }
.news-card__meta-left { display: flex; align-items: center; gap: 8px; min-width: 0; }
.news-card__views { display: inline-flex; align-items: center; gap: 3px; }

.news-card__tags { display: flex; gap: 5px; flex-wrap: wrap; margin-bottom: 9px; }
.news-card__tag {
  font-size: 10px; background: var(--sheet-2); color: var(--muted);
  padding: 3px 9px; border-radius: 100px; white-space: nowrap;
}
.news-card__tag--general { background: #eef4ee; color: #2f6b3a; }

.news-sheet-overlay {
  position: fixed; inset: 0; background: rgba(20,26,36,.3);
  z-index: 60; opacity: 0; pointer-events: none; transition: opacity .25s ease;
}
.news-sheet-overlay.is-open { opacity: 1; pointer-events: auto; }
.news-sheet-overlay[hidden] { display: none; }

.news-sheet {
  position: fixed; left: 0; right: 0; bottom: -100%;
  height: min(97vh, 960px);
  background: var(--sheet);
  border-radius: 16px 16px 0 0;
  box-shadow: 0 -12px 32px rgba(20,26,36,.3);
  z-index: 61;
  display: flex; flex-direction: column;
  transition: bottom .28s ease;
}
.news-sheet.is-open { bottom: 0; }
.news-sheet[hidden] { display: none; }
.news-sheet__handle { width: 36px; height: 4px; background: var(--rule); border-radius: 3px; margin: 10px auto 2px; flex: none; }
.news-sheet__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 8px 20px 12px; border-bottom: 1px solid var(--rule); flex: none;
}
.news-sheet__meta { font-size: 11.5px; color: var(--muted); }
.news-sheet__acts { display: flex; gap: 4px; }
.news-sheet__body { padding: 24px 40px; overflow-y: auto; background: var(--sheet-2); }
.news-sheet__col {
  max-width: 930px;
  margin: 0 auto;
  background: var(--sheet);
  border-radius: 10px;
  padding: 24px 32px;
  box-shadow: 0 1px 3px rgba(20,26,36,.05);
}
.news-sheet__body h2 { display: flex; align-items: center; gap: 8px; font-size: 19px; color: var(--ink); margin: 0 0 14px; }
.news-sheet__content { font-size: 14px; line-height: 1.65; color: var(--ink); }
.news-sheet__content img { max-width: 100%; border-radius: 8px; display: block; margin: 10px 0; }
.news-sheet__content ul, .news-sheet__content ol { padding-left: 22px; }
.news-sheet__file {
  display: inline-flex; align-items: center; gap: 8px; margin-top: 16px;
  border: 1px solid var(--rule); border-radius: 8px; padding: 9px 14px;
  font-size: 12.5px; color: var(--ink); text-decoration: none;
}
.news-sheet__file:hover { background: var(--sheet-2); }

.news-sheet__readers { margin-top: 22px; padding-top: 16px; border-top: 1px solid var(--rule); }
.news-sheet__readers-title {
  display: flex; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 600; letter-spacing: .03em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 10px;
}
.news-sheet__reader { display: flex; align-items: center; gap: 10px; padding: 5px 0; }
.news-sheet__reader-name { font-size: 12.5px; color: var(--ink); }
.news-sheet__reader-time { font-size: 10.5px; color: var(--faint); margin-left: auto; }

.news-callout {
  border-left: 3px solid var(--faint);
  background: var(--sheet-2);
  border-radius: 6px;
  padding: 10px 14px;
  margin: 10px 0;
}

/* ---------- Редактор новости (внутри общей модалки) ---------- */

.modal__card--wide { width: min(620px, 100%); }
.news-channel-list { border: 1px solid var(--rule); border-radius: 8px; overflow: hidden; margin-bottom: 6px; max-height: 180px; overflow-y: auto; }
.news-channel-row { display: flex; align-items: center; gap: 10px; padding: 8px 12px; border-top: 1px solid var(--rule-soft); cursor: pointer; }
.news-channel-row:first-child { border-top: 0; }
.news-channel-row:hover { background: var(--sheet-2); }
.news-channel-row__check {
  width: 16px; height: 16px; border-radius: 4px; border: 1.5px solid var(--faint);
  flex: none; display: flex; align-items: center; justify-content: center; font-size: 10px; color: #fff;
}
.news-channel-row.is-checked .news-channel-row__check { background: var(--pen); border-color: var(--pen); }
.news-channel-row__icon { color: var(--muted); font-size: 12px; width: 14px; text-align: center; flex: none; }
.news-channel-row__name { font-size: 12.5px; color: var(--ink); }
.news-target-status { font-size: 11.5px; padding: 7px 10px; border-radius: 7px; margin-bottom: 14px; }
.news-target-status.is-general { background: #eef4ee; color: #2f6b3a; }
.news-target-status.is-targeted { background: var(--pen-wash); color: var(--pen-deep); }

.rte-toolbar {
  display: flex; gap: 3px; padding: 6px;
  border: 1px solid var(--rule); border-bottom: 0; border-radius: 8px 8px 0 0;
  background: var(--sheet-2); flex-wrap: wrap;
}
.rte-toolbar button {
  min-width: 28px; height: 28px; border: 0; background: transparent;
  border-radius: 6px; cursor: pointer; font-size: 13px; color: var(--ink); padding: 0 6px;
}
.rte-toolbar button:hover { background: var(--rule); }
.rte-toolbar button.is-on { background: var(--pen); color: #fff; }
.rte-editor {
  border: 1px solid var(--rule); border-radius: 0 0 8px 8px;
  min-height: 140px; max-height: 320px; overflow-y: auto;
  padding: 12px; font-size: 13.5px; line-height: 1.6; color: var(--ink); margin-bottom: 12px;
}
.rte-editor:focus { outline: none; border-color: var(--pen); }
.rte-editor img { max-width: 100%; border-radius: 8px; display: block; margin: 8px 0; }
.rte-editor:empty::before { content: attr(data-placeholder); color: var(--faint); }

.news-file-chip {
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid var(--rule); border-radius: 8px; padding: 7px 11px;
  font-size: 12.5px; color: var(--ink); margin-bottom: 12px;
}
.news-file-chip button { border: 0; background: transparent; color: var(--stamp); cursor: pointer; font-size: 12px; }

.admin-settings {
  padding: 14px 16px;
  margin-bottom: 16px;
  background: var(--sheet-2);
  border-radius: 10px;
}
.admin-settings__title { margin: 0 0 2px; font-size: 14px; font-weight: 600; color: var(--ink); }
.admin-settings__hint { margin: 0 0 12px; font-size: 11.5px; color: var(--muted); }
.admin-settings .field { margin-bottom: 10px; max-width: 320px; }

.admin-cols {
  display: grid;
  grid-template-columns: 1.9fr 1.2fr 1fr 1.05fr 1.2fr 76px;
  gap: 0 14px;
  padding: 0 12px 8px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .04em;
  color: var(--muted);
}
.admin-row {
  display: grid;
  grid-template-columns: 1.9fr 1.2fr 1fr 1.05fr 1.2fr 76px;
  gap: 0 14px;
  align-items: center;
  padding: 9px 12px;
  border-radius: 9px;
  border: 1px solid transparent;
}
.admin-row:hover { background: var(--sheet-2); border-color: var(--rule); }

.admin-who { display: flex; align-items: center; gap: 10px; min-width: 0; }
.admin-nm { min-width: 0; }
.admin-nm b { display: flex; align-items: center; gap: 6px; font-size: 13.5px; font-weight: 600; color: var(--ink); }
.admin-nm span { display: block; font-size: 11.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.admin-title-cell {
  display: flex; align-items: center; gap: 6px;
  font-size: 13px; color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.admin-title-cell button {
  border: 0; background: transparent; color: var(--faint); font-size: 11px; cursor: pointer; flex: none;
}
.admin-title-cell button:hover { color: var(--pen); }

.admin-mail { font-family: var(--mono); font-size: 11.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.admin-phone { font-family: var(--mono); font-size: 11.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.admin-role {
  font-family: var(--mono);
  font-size: 11px;
  padding: 5px 10px;
  border-radius: 100px;
  border: 1px solid var(--rule);
  background: #fff;
  color: var(--ink-2);
  cursor: pointer;
}
.admin-role.role-owner { background: #f7f0e0; border-color: #e3d4a8; color: #8a6a2e; cursor: default; }
.admin-role.role-admin { background: var(--pen-wash); border-color: #c7d3f2; color: var(--pen-deep); }
.admin-role.role-moderator { background: #fbeeed; border-color: #f0d2cf; color: var(--stamp); }
.admin-role.role-member { background: var(--sheet-2); color: var(--muted); }

.admin-acts { display: flex; justify-content: flex-end; gap: 4px; }
.admin-acts .icon-btn { width: 28px; height: 28px; font-size: 12.5px; border: 1px solid transparent; }
.admin-acts .icon-btn:hover { border-color: var(--rule); background: #fff; }

/* Блок заявок на регистрацию — те же .admin-row внутри, но последняя
   колонка шире фиксированных 76px: там текстовая кнопка «Подтвердить»,
   а не только иконки, ей нужно больше места. */
.admin-pending { border: 1px solid #f0d2cf; background: #fbeeed; }
.admin-pending .admin-settings__title { color: var(--stamp); }
.admin-pending .admin-row { grid-template-columns: 1.8fr 1.3fr 1fr 1.4fr auto; background: #fff; margin-bottom: 6px; }
.admin-pending .admin-row:last-child { margin-bottom: 0; }
.admin-pending .admin-acts { gap: 8px; }

/* Заявки на вступление в канал — тот же .admin-pending, только янтарный
   акцент вместо красного (см. .reg-banner--amber выше — тот же принцип:
   подать заявку и ждать решения — не тревожное действие) и строки-.person
   вместо .admin-row (тут не таблица сотрудников, а простой список «кто
   куда просится»). */
.admin-pending--amber { border-color: #f0dcaa; background: #fcf1de; }
.admin-pending--amber .admin-settings__title { color: #c97a12; display: flex; align-items: center; gap: 6px; }
.admin-pending--amber .person { background: #fff; border-radius: 9px; margin-bottom: 6px; padding: 8px 10px; }
.admin-pending--amber .person:last-child { margin-bottom: 0; }
.admin-pending--amber .person:hover { background: #fff; }

.admin-note {
  margin: 16px 0 0;
  padding: 10px 13px;
  background: var(--sheet-2);
  border-radius: 8px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
  line-height: 1.5;
}

.admin-pwd {
  font-family: var(--mono);
  font-size: 16px;
  letter-spacing: .04em;
  padding: 11px 14px;
  border: 1px dashed var(--pen);
  border-radius: 8px;
  background: var(--pen-wash);
  color: var(--pen-deep);
  text-align: center;
  user-select: all;
  margin: 10px 0 14px;
}

@media (max-width: 860px) {
  .admin-cols { display: none; }
  .admin-row { grid-template-columns: 1fr auto; grid-template-areas: "who acts" "title title" "role role"; row-gap: 4px; padding: 12px; }
  .admin-who { grid-area: who; }
  .admin-acts { grid-area: acts; }
  .admin-title-cell { grid-area: title; }
  .admin-role { grid-area: role; justify-self: start; }
}

/* Блок «Управление каналами» — те же .admin-row/.admin-who/.admin-cols/
   .admin-acts, что и у списка сотрудников (см. выше), просто три колонки
   вместо шести и своя мета-ячейка (тип + число участников) вместо
   должности/почты/телефона/роли по отдельности. */
.channel-mgmt .admin-cols,
.channel-mgmt .admin-row { grid-template-columns: minmax(220px, 460px) 210px 104px; }
.chan-mgmt-meta { display: flex; align-items: center; gap: 10px; }

.chan-order { display: flex; flex-direction: column; gap: 1px; flex: none; }
.chan-order button {
  border: 0; background: transparent; color: var(--muted);
  font-size: 9px; line-height: 1; padding: 2px 4px; cursor: pointer;
}
.chan-order button:hover:not(:disabled) { color: var(--pen); }
.chan-order button:disabled { opacity: .25; cursor: default; }

/* Заявка на вступление прямо в строке канала (не отдельным блоком сверху) —
   тот же янтарный акцент, что и у .reg-banner--amber/.admin-pending--amber,
   только теперь на уровне одной строки: сама строка подсвечена, плашка со
   счётчиком в мете разворачивает карточку заявки под собой. */
.channel-mgmt-row.has-join-requests {
  background: #fcf1de;
  border-color: #f0dcaa;
}
.channel-mgmt-row.has-join-requests:hover { background: #fcf1de; }
.chan-join-badge {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  border: 0;
  border-radius: 100px;
  padding: 4px 8px;
  background: #c97a12;
  color: #fff;
  font-family: var(--mono);
  font-size: 11px;
  cursor: pointer;
}
.chan-join-badge:hover { background: #a8630a; }
.chan-join-drop {
  background: #fcf1de;
  border: 1px solid #f0dcaa;
  border-top: 0;
  border-radius: 0 0 9px 9px;
  margin: -7px 0 6px;
  padding: 4px 6px 6px;
}
.chan-join-drop .person { background: #fff; border-radius: 8px; margin-top: 4px; padding: 8px 10px; }
.chan-join-drop .person:hover { background: #fff; }

@media (max-width: 860px) {
  .channel-mgmt .admin-cols { display: none; }
  .channel-mgmt .admin-row {
    grid-template-columns: 1fr auto;
    grid-template-areas: "who acts" "meta meta";
    row-gap: 6px;
  }
  .channel-mgmt .chan-mgmt-meta { grid-area: meta; }
}

/* ---------- правая панель ---------- */

.panel {
  border-left: 1px solid var(--rule);
  background: var(--desk);
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  min-width: 0;
  min-height: 0;
}
.panel[hidden] { display: none; }
.panel__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 11px 12px 18px;
  border-bottom: 1px solid #d5d7db;
}
.panel__head h2 { font-size: 15px; }
.panel__body {
  overflow-y: auto;
  padding: 14px;
  scrollbar-width: thin;
  scrollbar-color: var(--faint) transparent;
}
.panel__body::-webkit-scrollbar { width: 9px; }
.panel__body::-webkit-scrollbar-track { background: transparent; }
.panel__body::-webkit-scrollbar-thumb {
  background: var(--faint);
  border-radius: 5px;
  border: 2px solid var(--desk);
  background-clip: padding-box;
}
.panel__body::-webkit-scrollbar-thumb:hover {
  background: var(--muted);
  background-clip: padding-box;
}

.panel__search { margin-bottom: 11px; }
.panel__search input {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: #fff;
}
.panel__search input:focus { border-color: var(--pen); outline: none; }

.panel__stat {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .04em;
  color: var(--muted);
  margin: 16px 0 8px;
}

.hit {
  display: block;
  width: 100%;
  text-align: left;
  padding: 10px 12px;
  margin-bottom: 6px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--sheet);
  cursor: pointer;
  text-decoration: none;
  transition: border-color .15s;
}
.hit:hover { border-color: var(--pen); }
.hit__top {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 4px;
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--muted);
}
.hit__top b { font-family: var(--ui); font-size: 12.5px; color: var(--ink); font-weight: 600; }
.hit__text { font-family: var(--body); font-size: 13.5px; line-height: 1.5; color: var(--ink-2); }
.hit__text mark { background: #d9e2fa; color: var(--pen-deep); padding: 0 2px; border-radius: 2px; }

.person {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  text-align: left;
  cursor: pointer;
}
.person:hover { background: var(--sheet); }
.person__who { flex: 1; min-width: 0; }
.person__who b {
  display: block; font-family: var(--ui); font-size: 13.5px; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.person__who span {
  display: block;
  font-size: 11.5px;
  color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.blank { padding: 26px 6px; color: var(--muted); font-size: 13.5px; text-align: center; }

/* ---------- профиль собеседника ---------- */

.peer-profile { text-align: center; padding: 6px 4px 16px; }
.peer-profile__avatar { display: flex; justify-content: center; margin-bottom: 12px; }
.peer-profile__name { display: flex; align-items: center; justify-content: center; gap: 7px; flex-wrap: wrap; }
.peer-profile__name b { font-family: var(--display); font-size: 16px; font-weight: 600; color: var(--ink); }
.peer-profile__title { font-size: 12.5px; color: var(--muted); margin-top: 3px; }
.peer-profile__status { font-size: 12.5px; color: var(--muted); font-style: italic; margin-top: 3px; }
.peer-profile__presence {
  display: flex; align-items: center; justify-content: center; gap: 5px;
  font-size: 11.5px; color: var(--muted); margin-top: 6px;
}

.peer-row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 6px; border-top: 1px solid var(--rule-soft);
}
.peer-row:first-child { border-top: 1px solid var(--rule); }
.peer-row__icon {
  width: 30px; height: 30px; border-radius: 8px; flex: none;
  background: var(--sheet-2); display: flex; align-items: center; justify-content: center; font-size: 14px;
}
.peer-row__text { flex: 1; min-width: 0; text-align: left; }
.peer-row__text b {
  display: block; font-size: 12.5px; font-weight: 500; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.peer-row__text span { display: block; font-size: 11px; color: var(--muted); }
/*
 * Подсказка «выключено глобально» — намеренно отдельный класс, а не тег <b>
 * внутри .peer-row__text: там уже есть правило .peer-row__text b с
 * white-space:nowrap + ellipsis для жирного заголовка строки («Уведомления»
 * и т.п.) — обычный тег <b> внутри того же контейнера страдал бы тем же
 * обрезанием, даже если сам текст длиннее заголовка (это и происходило).
 * Здесь — полноценный перенос по словам, ничего не обрезается.
 */
.peer-row__hint-off {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  line-height: 1.4;
  color: var(--stamp);
  font-weight: 600;
  white-space: normal;
}

.toggle-switch {
  width: 38px; height: 22px; border-radius: 100px; flex: none;
  background: var(--rule); border: 0; position: relative; cursor: pointer; padding: 0;
  transition: background .15s;
}
.toggle-switch i {
  position: absolute; top: 2px; left: 2px; width: 18px; height: 18px;
  border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(20,26,36,.25);
  transition: left .15s; display: block;
}
.toggle-switch.is-on { background: var(--pen); }
.toggle-switch.is-on i { left: 18px; }
.toggle-switch:disabled { opacity: .45; cursor: not-allowed; }
.toggle-switch:disabled.is-on { background: var(--muted); }   /* приглушённый — раз глобально выключено, «включенный» вид тут вводил бы в заблуждение */

.peer-profile__tabs {
  display: flex; gap: 2px; margin-top: 6px;
  border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule-soft);
}
.peer-profile__tabs button {
  flex: 1; text-align: center; padding: 9px 0; border: 0; background: transparent;
  font-family: var(--ui); font-weight: 600; font-size: 12px; color: var(--muted);
  border-bottom: 2px solid transparent; margin-bottom: -1px; cursor: pointer;
}
.peer-profile__tabs button.is-on { color: var(--pen); border-bottom-color: var(--pen); }

.peer-media-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px; padding: 8px 0; }
.peer-media-thumb {
  display: block;
  aspect-ratio: 1;
  border-radius: 5px;
  overflow: hidden;
  background: var(--sheet-2);
  border: 0;
  padding: 0;
  cursor: zoom-in;
}
.peer-media-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.peer-profile__block { margin-top: 14px; }

/* ---------- окна и уведомления ---------- */

.modal {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 20px;
  background: rgba(20,26,36,.44);
  z-index: 40;
}
.modal[hidden] { display: none; }
.modal__card {
  width: min(440px, 100%);
  background: var(--sheet);
  border-radius: 11px;
  box-shadow: var(--shadow);
  overflow: hidden;
}
.modal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 11px 14px 20px;
  border-bottom: 1px solid var(--rule);
}
.modal__head h2 { font-size: 16px; }
.modal__body {
  padding: 18px 20px 20px;
  max-height: 62vh;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--desk) transparent;
}
.modal__body::-webkit-scrollbar { width: 9px; }
.modal__body::-webkit-scrollbar-track { background: transparent; }
.modal__body::-webkit-scrollbar-thumb {
  background: var(--desk);
  border-radius: 5px;
  border: 2px solid var(--sheet);
  background-clip: padding-box;
}
.modal__body::-webkit-scrollbar-thumb:hover {
  background: var(--faint);
  background-clip: padding-box;
}
.modal-sep { border: 0; border-top: 1px solid var(--rule); margin: 6px 0 18px; }
.profile-avatar-row { display: flex; align-items: center; gap: 14px; margin-bottom: 20px; }
.profile-avatar-acts { display: flex; flex-direction: column; gap: 7px; }

.fwd-avatars { display: flex; gap: 14px; overflow-x: auto; padding: 4px 2px 6px; }
.fwd-av {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  border: 0; background: transparent; flex: none; width: 56px; cursor: pointer;
}
.fwd-av .ava { transition: outline .1s; }
.fwd-av:hover .ava, .fwd-av:focus-visible .ava { outline: 2px solid var(--pen); outline-offset: 2px; }
.fwd-av span {
  font-size: 11px; color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 56px;
}
.modal__body .radio {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 10px 12px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  margin-bottom: 8px;
  cursor: pointer;
}
.modal__body .radio b { display: block; font-family: var(--ui); font-size: 14px; }
.modal__body .radio span { font-size: 12.5px; color: var(--muted); }

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 80;
  background: #0e1416;
  display: flex;
  flex-direction: column;
}
.lightbox[hidden] { display: none; }

.lightbox__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 18px;
  background: linear-gradient(to bottom, rgba(0,0,0,.55), transparent);
  position: relative;
  z-index: 2;
}
.lightbox__info { color: #dbe6ff; font-size: 12.5px; }
.lightbox__info b {
  display: block; color: #fff; font-size: 13.5px; font-weight: 600; font-family: var(--display);
}
.lightbox__tools { display: flex; gap: 4px; }
.lightbox__tools .icon-btn { background: rgba(255,255,255,.06); }
.lightbox__tools .icon-btn:hover { background: rgba(255,255,255,.18); color: #fff; }
.lightbox__tools .icon-btn[hidden] { display: none; }
#lightbox-close:hover { background: rgba(155,44,72,.32); color: #f3d3d3; }

.lightbox__stage {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: 16px;
}
.lightbox__img {
  max-width: 100%;
  max-height: 100%;
  border-radius: 4px;
  box-shadow: 0 20px 60px rgba(0,0,0,.5);
  transition: transform .15s ease;
  cursor: zoom-in;
  user-select: none;
}
.lightbox__img[hidden] { display: none; }
.lightbox__pdf {
  width: 100%; max-width: 900px; height: 100%;
  border: 0; border-radius: 4px; background: #fff;
}
.lightbox__pdf[hidden] { display: none; }

.lightbox__zoomlabel {
  position: absolute;
  bottom: 16px; left: 50%;
  transform: translateX(-50%);
  background: rgba(0,0,0,.5);
  color: #dbe6ff;
  font-family: var(--mono);
  font-size: 11px;
  padding: 5px 12px;
  border-radius: 100px;
}
.lightbox__zoomlabel[hidden] { display: none; }

.shot-trigger {
  display: block;
  border: 0;
  background: transparent;
  padding: 0;
  cursor: zoom-in;
}

.ctx-menu {
  position: fixed;
  z-index: 70;
  min-width: 168px;
  padding: 5px;
  background: var(--sheet);
  border: 1px solid var(--rule);
  border-radius: 9px;
  box-shadow: var(--shadow);
}
.ctx-menu[hidden] { display: none; }
.ctx-menu button {
  display: block;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--ink-2);
  font-family: var(--ui);
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
}
.ctx-menu button:hover { background: var(--sheet-2); color: var(--ink); }

.toasts { position: fixed; right: 18px; bottom: 18px; display: grid; gap: 8px; z-index: 90; }
.toast {
  padding: 11px 15px;
  border-radius: var(--radius);
  background: var(--cover);
  color: #fff;
  font-size: 14px;
  max-width: 340px;
  box-shadow: var(--shadow);
  animation: rise .18s ease-out;
}
.toast--bad { background: var(--stamp); }
@keyframes rise { from { opacity: 0; transform: translateY(6px); } }

/* ---------- установка и обновление PWA ---------- */

.pwa-card {
  position: fixed;
  right: 18px;
  bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  z-index: 120;
  display: flex;
  align-items: center;
  gap: 12px;
  width: min(520px, calc(100vw - 36px));
  padding: 13px 13px 13px 15px;
  border: 1px solid rgba(255, 255, 255, .12);
  border-radius: 12px;
  background: var(--cover);
  color: #fff;
  box-shadow: 0 16px 44px rgba(20, 26, 36, .28);
  animation: rise .2s ease-out;
}
.pwa-card[hidden] { display: none; }
.pwa-card__mark {
  flex: none;
  display: grid;
  gap: 4px;
  width: 38px;
  height: 38px;
  padding: 10px 8px;
  border-radius: 9px;
  background: var(--cover-2);
}
.pwa-card__mark span { display: block; height: 2px; border-radius: 2px; background: #fff; }
.pwa-card__mark span:first-child { background: #7f9bee; }
.pwa-card__mark span:nth-child(2) { width: 70%; }
.pwa-card__mark span:nth-child(3) { width: 85%; opacity: .5; }
.pwa-card__body { flex: 1; min-width: 0; display: grid; gap: 2px; }
.pwa-card__body b { font-family: var(--display); font-size: 14px; }
.pwa-card__body span { color: #c3cee2; font-size: 12.5px; line-height: 1.4; }
.pwa-card__action { flex: none; border-color: #7f9bee; background: #7f9bee; color: #101a32; }
.pwa-card__action:hover { border-color: #93aaf0; background: #93aaf0; }
.pwa-card__close { flex: none; color: var(--cover-ink); font-size: 22px; }
.pwa-card__close:hover { background: var(--cover-3); color: #fff; }
.pwa-card--update { border-color: rgba(127, 155, 238, .45); }

/* ---------- адаптация ---------- */

@media (max-width: 1080px) {
  .app.has-panel { grid-template-columns: 395px minmax(0, 1fr); }
  .app.has-panel .panel {
    position: fixed;
    top: 0; right: 0; bottom: 0;
    width: min(376px, 100%);
    z-index: 30;
    box-shadow: var(--shadow);
  }
}

@media (max-width: 860px) {
  .pwa-card {
    left: 10px;
    right: 10px;
    bottom: calc(10px + env(safe-area-inset-bottom, 0px));
    width: auto;
    gap: 10px;
  }
  .pwa-card__mark { width: 34px; height: 34px; padding: 8px 7px; }
  .pwa-card__action { padding: 8px 11px; }

  .gate { grid-template-columns: 1fr; }
  .gate__aside { padding: 34px 24px 28px; gap: 20px; }

  .app, .app.has-panel { grid-template-columns: minmax(0, 1fr); }
  .rail {
    position: fixed;
    top: 0; bottom: 0; left: 0;
    width: 100%;
    z-index: 35;
    transform: translateX(-100%);
    transition: transform .2s ease;
  }
  .app.rail-open .rail { transform: none; }
  #rail-close { display: grid; }
  .board__burger { display: grid; }
  :root { --gutter: 60px; }
  .log { padding: 12px 14px 8px; }
  .composer { padding: 10px 14px 12px; }
  .composer__hint { display: none; }

  /* Список месяцев открывается второй кнопкой слева направо — на узком
     экране его левый край (min-width: 220px) легко упирается в правую
     границу вьюпорта. Открываем его от правого края триггера, а не от
     левого; у списка каналов (первая кнопка, ближе к левому краю) той же
     проблемы нет — его не трогаем. */
  .news-filter-dropdown { max-width: calc(100vw - 32px); }
  #news-filter-month-dropdown { left: auto; right: 0; }

  /* На touch-устройствах :hover либо не работает вовсе, либо «залипает»
     после тапа — эти кнопки и так дублируют то, что теперь есть в
     контекстном меню по долгому нажатию (см. #ctx-edit/#ctx-delete выше). */
  .bubble-acts { display: none; }

  /*
   * Панель ввода на мобильном — в стиле Telegram: скрепка отдельно слева,
   * поле ввода «таблеткой» с эмодзи внутри справа, круглая кнопка отправки.
   * Глаз (размытие) скрыт целиком — функция остаётся доступна, просто не
   * с этой панели, экономим место в узкой строке. Порядок элементов не
   * меняется в HTML — только визуально, через flex order, поэтому на
   * десктопе (где это правило не действует) всё остаётся как было.
   */
  #privacy-toggle { display: none; }
  .composer__row { align-items: center; gap: 6px; }
  #attach-btn { order: 1; }
  #editor {
    order: 2;
    background: var(--sheet-2);
    border-radius: 20px;
    padding: 9px 40px 9px 14px;
  }
  #emoji-btn {
    order: 3;
    margin-left: -38px;
    z-index: 2;
    align-self: flex-end;
    margin-bottom: 5px;
  }
  #send-btn {
    order: 4;
    width: 38px; height: 38px;
    border-radius: 50%;
    padding: 0;
    display: grid;
    place-items: center;
    flex: none;
  }
  .send-btn__text { display: none; }
  .send-btn__icon { display: block; }

  /*
   * Кнопка «прокрутить вниз» на мобильном — компактная, справа, только
   * иконка (в стиле Telegram), вместо широкой таблетки с текстом по центру,
   * как на десктопе. Меняется только на мобильном — на десктопе .jump
   * остаётся как была, этот блок её не трогает вовсе.
   */
  .jump {
    left: auto; right: 14px; bottom: 14px; transform: none;
    width: 38px; height: 38px; padding: 0;
    display: grid; place-items: center;
    background: var(--sheet); color: var(--ink); border: 1px solid var(--rule);
  }
  .jump[hidden] { display: none; }
  .jump__text { display: none; }
  .jump__icon { display: block; }
  .jump__count {
    display: flex; position: absolute; top: -4px; right: -4px;
    width: 17px; height: 17px; border-radius: 50%; background: var(--stamp); color: #fff;
    font-size: 9.5px; font-weight: 700; align-items: center; justify-content: center;
    font-family: var(--ui); line-height: 1;
  }
  .jump__count[hidden] { display: none; }

  .rail__tabs { display: flex; gap: 6px; margin: 0 16px 12px; background: var(--cover-2); border-radius: 9px; padding: 3px; }
  /* Личные — открыты по умолчанию; переключение на «Каналы» происходит
     через класс .rail--tab-channels на #rail (см. app.js), а не наоборот
     — так поведение верно даже без единой строчки JS, на голом CSS. */
  #rail-group-channels { display: none; }
  .rail--tab-channels #rail-group-channels { display: block; }
  .rail--tab-channels #rail-group-dms { display: none; }

  /*
   * Файлы/Участники/Поиск/Вступить в канал — на мобильном сворачиваются в
   * меню «⋮», те же самые кнопки (не копии — переиспользую напрямую, ничего
   * не дублирую в HTML и не завожу отдельную логику клика), просто иначе
   * расположены через CSS. На каналах, где кнопок сразу несколько (включая
   * «Вступить»), то же самое — без исключений, как и просили.
   */
  .board__more { display: grid; }
  .board__tools-menu {
    display: none;
    position: absolute; top: calc(100% + 6px); right: 0;
    flex-direction: column; gap: 2px;
    background: var(--sheet); border: 1px solid var(--rule); border-radius: 10px;
    box-shadow: var(--shadow); padding: 6px; min-width: 190px; z-index: 20;
  }
  .board__tools-menu.is-open { display: flex; }
  .board__tools-menu .chip-btn, .board__tools-menu #join-channel {
    width: 100%; justify-content: flex-start; text-align: left; border-radius: 7px;
  }

  /*
   * Шапка панели администратора/новостей (.admin__head — общий класс для
   * обеих) — на мобильном узкий ряд из стрелки/заголовка/кнопки не
   * помещался, заголовок переносился на несколько строк и наезжал на
   * кнопку. Заголовок теперь в одну строку с многоточием, если не влезает
   * целиком; кнопка (какая бы ни была — «+ Добавить пользователя» или
   * «+ Добавить новость») переезжает на отдельную строку снизу, во всю
   * ширину — flex-basis:100% гарантированно уводит её на новую строку в
   * flex-wrap, а не просто «когда не хватит места».
   */
  .admin__head { flex-wrap: wrap; row-gap: 10px; }
  .admin__title h1 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .admin__title p { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .admin__head > .btn { flex: 0 0 100%; order: 3; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
