/* Оболочка MVQ: каркас, список, разговор, разделы и телефон.
 *
 * Ширины каркаса заданы в токенах и только там: полоса пространств 68, список
 * чатов 336, остальное — разговору. Меню выезжает поверх и ширины колонок не
 * трогает: когда содержимое под меню переезжает, человек теряет то место, куда
 * смотрел.
 *
 * Два продукта — две разные сетки и два разных характера. Мессенджер тесен и
 * тих: тёмная полоса пространств, узкий список, разговор во всю оставшуюся
 * ширину. Социальная сеть просторна: навигация, лента и контекстная колонка.
 * Общего у них ровно одно — токены.
 */

/* ── Основа ──────────────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }

/* Полосы прокрутки не видно. Крутится при этом всё: колесо, тачпад, палец,
 * клавиши и перетаскивание — за это отвечает браузер, а не полоса. */
* { scrollbar-width: none; }
*::-webkit-scrollbar { display: none; width: 0; height: 0; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  font-family: var(--font);
  font-size: var(--fs);
  line-height: var(--lh);
  background: var(--bg);
  color: var(--text);
  -webkit-tap-highlight-color: transparent;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

button, input, select, textarea { font: inherit; color: inherit; }

button {
  cursor: pointer;
  transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease),
              border-color var(--fast) var(--ease), transform var(--fast) var(--ease);
}
button:active { transform: scale(.98); }
button[disabled] { cursor: default; opacity: .45; transform: none; }

/* Видимый след клавиатуры обязателен: без него доска работает вслепую. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

a { color: var(--text); }

h1, h2, h3, p { margin: 0; }

.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.muted { color: var(--muted); }
.small { font-size: var(--fs-sm); }
.tiny  { font-size: var(--fs-xs); }
.row   { display: flex; align-items: center; gap: 12px; }
.grow  { flex: 1; min-width: 0; }
.clip  { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Перенос внутри слова разрешён только живому содержимому: тексту людей и
 * названиям, которые люди придумали. Раньше `overflow-wrap: anywhere` стоял на
 * body, наследовался всеми кнопками — и в тесноте подписи «Создать» или
 * «Полка редакции» рассыпались вертикальными буквами по одной в строке. */
.post-text, .card-note, .card-row b, .card-row small, .state p, .plate,
.post-head b, .post-head small, .chat-row .who, .chat-row .preview,
.profile-info, .comment, .quote, .article-body, .article-title, .story-text,
.hero h3, .hero p, .menu-item small, .auth-field small, .social-space b,
.event, .banner, .toast { overflow-wrap: anywhere; }

/* Подпись действия не переносится вовсе: не помещается — действие уходит в
 * меню «Ещё», а не ломается в столбик. */
.btn, .chip, .folder-tab, .audience-btn, .auth-tab, .post-action,
.social-nav-btn span, .compose-tools button, .text-btn, .menu-check,
.tabbar button, .social-tabs button { white-space: nowrap; }
.btn { min-width: max-content; }
/* Кнопка во всю ширину — блок, а не сосед по строке: длинной подписи на узком
 * экране можно перенестись по словам (по словам — не по буквам). */
.btn.wide { white-space: normal; min-width: 0; }

/* Атрибут `hidden` прячет вопреки `display` любого класса. Без этого правила
 * `tabs.hidden = true` на полке `.chips` (display: flex) не делал ничего: в
 * «Обзоре» веба отбор поиска стоял вторым рядом вкладок над своими. Браузерное
 * `[hidden] { display: none }` проигрывает любому правилу автора. */
[hidden] { display: none !important; }

/* ── Корень ──────────────────────────────────────────────────────────────── */

#root { height: 100%; }

.app {
  position: relative;
  height: 100%;
  display: grid;
  grid-template-columns: var(--rail) var(--list) minmax(0, 1fr);
  /* Единственная строка сетки не выше окна: без minmax(0, 1fr) длинная
   * переписка распирала строку, и низ — поле ввода с кнопкой отправки —
   * уезжал за нижнюю границу окна. */
  grid-template-rows: minmax(0, 1fr);
  background: var(--bg);
  overflow: hidden;
}
.app.with-info { grid-template-columns: var(--rail) var(--list) minmax(0, 1fr) var(--info); }

/* Ячейка сетки по умолчанию не сжимается уже своего содержимого, и одна длинная
 * строка в шапке разговора растягивала всё окно вправо, а длинная переписка —
 * вниз. Разрешаем сжиматься везде, где содержимое обязано обрезаться и
 * прокручиваться, а не двигать соседей. */
.app > *, .conv > *, .section-body > *, .section > * { min-width: 0; min-height: 0; }

/* ── Кнопки общего вида ──────────────────────────────────────────────────── */

.btn {
  min-height: var(--tap);
  padding: 0 16px;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font-weight: 600;
}
.btn:hover:not([disabled]) { background: var(--surface2); border-color: var(--muted); }
/* Главное действие — единственное залитое: заливка берёт акцент темы. */
.btn.primary { background: var(--accent); color: var(--accentText); border-color: var(--accent); }
.btn.primary:hover:not([disabled]) { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn.ghost { background: transparent; border-color: transparent; }
.btn.ghost:hover:not([disabled]) { background: var(--surface2); border-color: transparent; }
.btn.danger { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 40%, var(--line)); }
.btn.wide { width: 100%; }
/* Кнопка на карточке справа — короткая и негромкая («Добавить», «Вступить»). */
.btn.tiny { min-height: 32px; padding: 0 12px; font-size: var(--fs-sm); border-radius: var(--r-sm); }

.icon-btn {
  width: var(--tap); height: var(--tap); flex: none;
  display: grid; place-items: center;
  border: 0; border-radius: 50%;
  background: transparent; color: var(--text);
}
.icon-btn:hover { background: var(--surface2); }
.icon-btn.on { background: var(--surface2); }
svg.ic { width: 21px; height: 21px; flex: none; fill: none; stroke: currentColor;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
svg.ic.sm { width: 17px; height: 17px; }
svg.ic.lg { width: 26px; height: 26px; }
svg.ic.solid { fill: currentColor; stroke: none; }

/* ── Знак MVQ ────────────────────────────────────────────────────────────────
 * Две связанные траектории вокруг общей точки: синий серп слева с разрывом
 * справа сверху и оливковый справа с разрывом слева снизу. Центры смещены —
 * оттого силуэт несимметричен и в мелком размере не читается кольцом.
 *
 * Дуги — заливка, а не обводка: штрих сужается к кончикам. Геометрию собирает
 * scripts/mark-gen.py, здесь только цвет. */
.mvq-mark { height: 30px; width: auto; display: block; }
.mvq-mark .mark-ink { fill: var(--accent); }
.mvq-mark .mark-arc { fill: var(--brand-arc); }
.mvq-mark .mark-dot { fill: var(--accent); }
.mvq-mark .mark-name {
  fill: var(--text); font-family: var(--font);
  font-size: 29px; font-weight: 800; letter-spacing: -.02em;
}
/* На тёмной полосе пространств знак светлеет весь: синий на синем не виден.
 * Оливковая дуга остаётся собой, но приглушается — иначе на почти чёрном она
 * становится ярче основного цвета и знак читается наоборот. */
.rail .mvq-mark .mark-ink, .rail .mvq-mark .mark-dot { fill: var(--rail-ink-on); }
.rail .mvq-mark .mark-arc { fill: var(--brand-arc); opacity: .85; }

.field {
  width: 100%; min-height: 44px;
  padding: 12px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--field); color: var(--text);
  transition: border-color var(--fast) var(--ease), background-color var(--fast) var(--ease);
}
.field:focus { border-color: color-mix(in srgb, var(--accent) 60%, var(--line)); }
.field::placeholder { color: var(--muted); }
textarea.field { resize: none; line-height: 1.4; }

.avatar {
  flex: none;
  display: grid; place-items: center;
  width: 44px; height: 44px;
  border-radius: 50%;
  background: var(--surface2);
  color: var(--text);
  font-weight: 700;
  font-size: var(--fs-sm);
  overflow: hidden;
  letter-spacing: .02em;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.avatar.sq { border-radius: var(--r-lg); }
.avatar.lg { width: 68px; height: 68px; font-size: var(--fs-lg); }
.avatar.xl { width: 88px; height: 88px; font-size: var(--fs-xl); }
.avatar.sm { width: 34px; height: 34px; font-size: var(--fs-xs); }

/* Значок группы отличается формой, а не только буквой: две группы вполне могут
 * называться одинаково, и различать их одной буквой нельзя. */
.avatar.group { border-radius: var(--r-lg); }

/* Значок с точкой присутствия. Точка — это состояние, и у неё есть подпись для
 * диктора: цветом состояние здесь не кодируется. */
.face { position: relative; flex: none; display: inline-grid; }
.face .live {
  position: absolute; right: -1px; bottom: -1px;
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--good);
  border: 2px solid var(--surface);
}
.face .live.away { background: var(--warn); }
.face .live.off { display: none; }

/* ── Полоса папок ────────────────────────────────────────────────────────── */

/* Тёмная в обеих темах: это край продукта, а не ещё одна панель. Наверху —
 * знак MVQ, дальше пространства и папки человека, внизу он сам. */
.rail {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 12px 12px;
  background: var(--rail-bg);
  color: var(--rail-ink);
  overflow: auto;
}
.rail-mark {
  flex: none; width: 40px; height: 40px; margin-bottom: 8px;
  display: grid; place-items: center;
  color: var(--rail-ink-on);
}
.rail-mark svg { width: 34px; height: 34px; }
.rail-btn {
  position: relative; flex: none;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border: 0; border-radius: var(--radius);
  background: transparent; color: var(--rail-ink);
}
.rail-btn:hover { background: rgba(255, 255, 255, .07); color: var(--rail-ink-on); }
.rail-btn.active { background: rgba(255, 255, 255, .12); color: var(--rail-ink-on); }
.rail-btn .dot {
  position: absolute; right: -2px; top: -2px;
  min-width: 18px; height: 18px; padding: 0 4px;
  display: grid; place-items: center;
  border: 2px solid var(--rail-bg);
  border-radius: var(--r-sm);
  background: var(--bad); color: #fff;
  font-size: 10px; font-weight: 700;
}
.rail-btn .tint { position: absolute; left: 3px; bottom: 3px; width: 6px; height: 6px; border-radius: 50%; }
.rail-sep { flex: none; width: 28px; height: 1px; margin: 8px 0; background: rgba(255, 255, 255, .13); }
.rail-add { margin-top: auto; }
/* Своё лицо в самом низу полосы: отсюда открываются настройки и выход. */
.rail-me { flex: none; margin-top: 8px; padding: 0; border: 0; background: transparent; border-radius: 50%; }
.rail-me .avatar { width: 40px; height: 40px; }
.rail-me .face .live { border-color: var(--rail-bg); }
.rail-name {
  font-size: 9px; letter-spacing: .01em;
}

/* ── Список чатов ────────────────────────────────────────────────────────── */

.list {
  display: flex; flex-direction: column; min-width: 0;
  background: var(--surface);
  border-right: 1px solid var(--line);
}
.list-head {
  height: var(--head); flex: none;
  padding: 0 12px 0 8px;
  display: flex; align-items: center; gap: 8px;
}
.list-title { font-weight: 650; font-size: var(--fs-lg); letter-spacing: -.01em; }
.search {
  flex: none;
  padding: 0 12px 12px;
  display: grid; grid-template-columns: 1fr auto; gap: 8px;
}
/* Поле поиска со значком внутри: значок — часть поля, а не сосед. */
.search .search-field { position: relative; min-width: 0; display: grid; }
.search .search-field > .ic {
  position: absolute; left: 12px; top: 50%; margin-top: -8px;
  color: var(--muted); pointer-events: none;
}
.search input {
  min-width: 0; height: 42px;
  padding: 0 12px 0 40px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--field); color: var(--text);
}
.search input:focus { border-color: color-mix(in srgb, var(--accent) 60%, var(--line)); }
.rows { flex: 1; min-height: 0; overflow: auto; padding: 0 8px 8px; }

/* Поиск на телефоне спрятан, пока список не вытянули вниз: он нужен изредка,
 * а место занимал бы всегда. */
.search.hidden-pull { display: none; }

/* Папки на телефоне — горизонтальной строкой и только свои. */
.folder-strip {
  flex: none;
  display: flex; gap: 8px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line);
  overflow-x: auto;
}
.folder-tab {
  flex: none;
  min-height: 34px; padding: 0 12px;
  display: inline-flex; align-items: center; gap: 8px;
  border: 0; border-radius: var(--radius);
  background: transparent; color: var(--muted);
  white-space: nowrap;
}
.folder-tab.active { background: var(--selected); color: var(--text); font-weight: 650; }

/* Сообщение уезжает за пальцем, когда его тянут влево к ответу. */
.msg { transition: transform var(--fast) var(--ease); }

.chat-row {
  position: relative; width: 100%;
  min-height: var(--row);
  padding: 12px 12px;
  display: grid; grid-template-columns: 46px minmax(0, 1fr) auto; gap: 12px;
  align-items: center;
  border: 0; border-radius: var(--radius);
  background: transparent; color: var(--text);
  text-align: left;
}
.chat-row:hover { background: var(--surface2); }
.chat-row.active { background: var(--selected); box-shadow: inset 3px 0 0 var(--accent); }
.chat-row .who { font-weight: 650; font-size: var(--fs); }
/* @имя под подписью. Мельче подписи, но **не мельче остального текста** и без
 * многоточия в середине: правило Р8 запрещает прятать различитель. Ужимается
 * только с конца, как любая длинная строка списка. */
.chat-row .handle, .conv-head .handle {
  font-size: var(--fs-xs); color: var(--muted); letter-spacing: .01em;
}
.conv-head .handle { margin-top: 1px; }
/* Лицо собеседника — моя картинка из хранилища этого устройства. */
.avatar img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: inherit; }
.chat-row .preview { color: var(--muted); font-size: var(--fs-sm); margin-top: 2px; }
.chat-row.unread .preview { color: var(--text); }
.chat-side { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }
.chat-side .time { color: var(--muted); font-size: var(--fs-xs); }
.chat-row.unread .chat-side .time { color: var(--accent); }
.badge {
  min-width: 20px; padding: 1px 8px;
  display: inline-grid; place-items: center;
  border-radius: var(--radius);
  background: var(--accent); color: var(--accentText);
  border: 1px solid var(--accent);
  font-size: var(--fs-xs); font-weight: 700;
}
.badge.quiet { background: transparent; color: var(--muted); border-color: var(--line-strong); }
.marks { display: flex; gap: 4px; color: var(--muted); align-items: center; }

/* ── Разговор ────────────────────────────────────────────────────────────── */

.conv { display: grid; grid-template-rows: var(--head) auto minmax(0, 1fr) auto; min-width: 0; }
.conv-head {
  padding: 0 12px 0 16px;
  display: flex; align-items: center; gap: 12px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.conv-head .who { font-weight: 650; font-size: var(--fs-md); }
/* Волосок между «связаться» и «посмотреть»: звонки — одно дело, поиск и
 * сведения — другое, и в ряду из шести значков это единственный способ не
 * заставлять человека читать подсказки. */
.conv-head .sep-v {
  flex: none; width: 1px; height: 24px; margin: 0 4px;
  background: var(--line);
}
/* Шапка разговора обязана ужиматься до самого узкого телефона. Строка
 * состояния — самая длинная в ней, и обрезаться должна она, а не окно: любой
 * горизонтальный сдвиг здесь уводит вбок всю переписку. */
.conv-head > .grow { min-width: 0; overflow: hidden; }
.conv-badge {
  display: flex; align-items: center; gap: 8px; min-width: 0;
  color: var(--muted); font-size: var(--fs-xs);
}
.conv-badge span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.conv-badge i { width: 7px; height: 7px; border-radius: 50%; background: var(--good); flex: none; }
.conv-badge.warn i { background: var(--warn); }
.conv-badge.bad i { background: var(--bad); }

/* Полка закреплённого. Пустая строка сетки схлопывается сама — отдельного
 * «спрятать» не нужно, и лишнего отступа при пустой полке не возникает. */
.pin-bar {
  padding: 8px 8px 8px 16px;
  display: flex; align-items: center; gap: 12px; min-width: 0;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  color: var(--muted);
}
.pin-jump {
  display: grid; gap: 1px; min-width: 0;
  border: 0; background: transparent; color: var(--text);
  text-align: left; font: inherit; padding: 0;
}
.pin-jump > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pin-jump:hover > span:last-child { color: var(--accent); }

/* Выбранная строка списка: кого зовём на событие. */
.card-row.on { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Снимок в переписке.
 *
 * Ширина ограничена пузырём, высота — экраном: фотография с телефона высотой в
 * четыре тысячи точек иначе выталкивает весь разговор вниз, и человек листает
 * одну картинку вместо переписки. object-fit: contain, а не cover: обрезать
 * присланное нельзя — на срезанном крае бывает всё, ради чего снимок и слали.
 */
.conv-shot { display: grid; gap: 8px; justify-items: start; }
.conv-shot-img {
  display: block; max-width: 100%; max-height: 46vh;
  border-radius: var(--radius); object-fit: contain; cursor: zoom-in;
  background: var(--surface-muted);
}
.lightbox {
  position: fixed; inset: 0; z-index: 90; display: grid; place-items: center;
  background: rgba(9, 12, 15, 0.86); padding: 24px;
}
.lightbox-img { max-width: 96vw; max-height: 92vh; object-fit: contain; border-radius: var(--r-sm); }
.lightbox-close {
  position: absolute; top: 14px; right: 14px;
  background: rgba(255, 255, 255, 0.12); color: #fff;
}
.lightbox-close:hover { background: rgba(255, 255, 255, 0.22); }

/* Голосовое сообщение: кнопка, волна, время.
 *
 * Волна ровная и приглушённая, пока звук не разобран, и становится настоящей,
 * когда пики посчитаны (класс .real). Промежуточное состояние видно: столбики
 * одной высоты — это «ещё не знаю», а не «здесь тишина».
 */
.voice {
  display: flex; align-items: center; gap: 12px;
  min-width: 232px; padding: 8px 4px;
}
.wave-play {
  flex: none; width: 40px; height: 40px; border-radius: 50%;
  border: 0; cursor: pointer;
  background: var(--accent); color: var(--accentText);
  display: grid; place-items: center;
}
.wave-play:hover { background: var(--accent-hover); }
.wave {
  flex: 1 1 auto; display: flex; align-items: center; gap: 2px;
  height: 28px; cursor: pointer; min-width: 0;
}
.wave i {
  flex: 1 1 0; min-width: 2px; border-radius: 2px;
  background: var(--line); opacity: 0.55;
  transition: background 120ms linear;
}
.wave.real i { opacity: 1; }
.wave i.past { background: var(--accent); }
.wave-time { flex: none; font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; }

/* Голосовое, кружок и полоса записи. */
.media { display: grid; gap: 8px; justify-items: start; }
.media.note { justify-items: center; }
.note-video { width: 220px; height: 220px; border-radius: 50%; object-fit: cover; background: #000; }
.composer.tape { display: flex; align-items: center; gap: 8px; }
.tape-view { width: 56px; height: 56px; border-radius: 50%; object-fit: cover; background: #000; }
.tape-dot {
  width: 10px; height: 10px; border-radius: 50%; background: var(--bad); flex: none;
  animation: tape-blink 1.1s steps(2, jump-none) infinite;
}
@keyframes tape-blink { 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) { .tape-dot { animation: none; } }

/* Опрос. Полоса доли рисуется под текстом варианта, а не рядом: рядом она
 * съела бы ширину, которой в пузыре и так мало. */
.poll { display: grid; gap: 8px; min-width: 240px; }
.poll-q { font-weight: 650; }
.poll-opt {
  position: relative; overflow: hidden;
  min-height: 36px; padding: 8px 12px;
  display: flex; align-items: center; gap: 8px;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: transparent; color: var(--text); text-align: left; font: inherit;
}
.poll-opt.mine { border-color: var(--accent); }
.poll-opt[disabled] { opacity: .75; }
.poll-bar {
  position: absolute; inset: 0 auto 0 0;
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  pointer-events: none;
}
.poll-opt > span:not(.poll-bar) { position: relative; }

/* Знак подлинности. Значок, а не слово: слово рядом с именем читается
 * как часть имени, а знак должен быть виден как знак. */
.verified { display: inline-flex; align-items: center; color: var(--accent); }

/* Статья: читалка и её заголовок в ленте. */
.article-title { font-size: 1.6rem; line-height: 1.25; margin: 0; }
.article-lead {
  display: grid; gap: 2px; width: 100%; text-align: left;
  border: 0; background: transparent; color: var(--text); padding: 0; margin-bottom: 4px;
  font: inherit;
}
.article-lead:hover b { color: var(--accent); }
.article-body { margin-top: 12px; max-width: 42em; line-height: 1.65; }
.article-body h2 { font-size: 1.25rem; margin: 16px 0 4px; }
.article-body h3 { font-size: 1.1rem; margin: 12px 0 4px; }
.article-body p { margin: 4px 0; }

/* Событие в записи публичного пространства. */
.event {
  margin-top: 8px; padding: 12px 12px;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--surface2);
}

/* Фон разговора — однотонный с тканым узором, разница с поверхностью доли
 * процента. Пейзажей, фотографий и «обоев» здесь нет и не будет: за ними
 * перестаёт читаться текст, ради которого человек сюда пришёл. */
/* Узор лежит на самом разговоре, а не на прокручиваемом слое: иначе он ехал
 * бы вместе с сообщениями, а фон ехать не должен. */
.conv { position: relative; }
.conv::before {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none; z-index: 0;
  background-color: var(--pattern-ink);
  -webkit-mask-image: var(--weave); mask-image: var(--weave);
  -webkit-mask-size: var(--weave-size) var(--weave-size);
  mask-size: var(--weave-size) var(--weave-size);
}
.conv > * { position: relative; z-index: 1; }
.messages {
  position: relative;
  overflow: auto;
  padding: 20px clamp(16px, 7vw, 120px) 8px;
  background: transparent;
}
.day { margin: 16px 0 12px; text-align: center; color: var(--muted); font-size: var(--fs-xs); }
.day span {
  display: inline-block; padding: 4px 12px;
  border-radius: var(--radius);
  background: var(--surface2); color: var(--muted);
  font-size: var(--fs-xs);
}
.msg { display: flex; align-items: flex-end; margin: 4px 0; }
.msg.mine { justify-content: flex-end; }
/* Пузырь и строка состояния под ним — один столбик: без него «отправится при
 * подключении» вставала бы рядом с пузырём и растягивала строку.
 *
 * Колонка `minmax(0, max-content)`, а не `auto`: у пузыря стоит
 * `width: max-content`, и колонка `auto` брала бы его минимальную ширину
 * равной максимальной — длинное сообщение уезжало за правый край экрана
 * вместо переноса. Поймано на 390 точках. */
.msg-line {
  max-width: var(--bubble); min-width: 0;
  display: grid; grid-template-columns: minmax(0, max-content); justify-items: start;
}
.msg.mine .msg-line { justify-items: end; justify-content: end; }
.bubble {
  max-width: 100%;
  width: max-content; min-width: 0;
  padding: 12px 16px;
  border-radius: var(--r-lg);
  background: var(--theirs);
  border: 1px solid var(--line);
  line-height: 1.42;
  box-shadow: var(--lift);
}
.msg.mine .bubble { background: var(--mine); border-color: color-mix(in srgb, var(--good) 22%, var(--line)); }
.bubble .body { white-space: pre-wrap; overflow-wrap: anywhere; }
.bubble .author { display: block; font-weight: 650; font-size: var(--fs-sm); margin-bottom: 4px; }
.bubble .meta { margin-top: 4px; color: var(--muted); font-size: var(--fs-xs); display: flex; gap: 8px; align-items: center; justify-content: flex-end; }
/* Прочтено — зелёные галочки; доставлено — обычные. Одним цветом состояние не
 * кодируется: у галочек разная форма и своя подпись для диктора. */
.bubble .meta .seen { color: var(--good); }
.bubble .meta .queued { color: var(--warn); }
.bubble .meta .failed { color: var(--bad); }
.bubble.failed { border-color: color-mix(in srgb, var(--bad) 55%, var(--line)); }
.bubble.queued { opacity: .82; border-style: dashed; }
.bubble.gone { opacity: .65; font-style: italic; }
/* Строка «отправится при подключении» / «не ушло» под пузырём. */
.msg-note {
  display: flex; align-items: center; gap: 8px;
  margin-top: 4px; font-size: var(--fs-xs); color: var(--muted);
  justify-content: flex-end;
}
.msg-note.bad { color: var(--bad); }
.bubble .plate {
  margin-top: 4px; padding: 8px 8px;
  border: 1px dashed var(--line-strong); border-radius: calc(var(--radius) * .8);
  color: var(--muted); font-size: var(--fs-sm);
}
/* Отметки под сообщением. Своя обведена: без этого не видно, что нажатие её
 * снимет, а не поставит вторую. */
.marks-row { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; }
.mark {
  min-height: 26px; padding: 0 8px;
  display: inline-flex; align-items: center; gap: 4px;
  border: 1px solid transparent; border-radius: var(--radius);
  background: color-mix(in srgb, var(--line-strong) 55%, transparent);
  color: var(--text); font-size: var(--fs-sm); line-height: 1;
}
.mark:hover { border-color: var(--line-strong); }
.mark.mine { border-color: var(--accent); }

/* Ответ на сообщение: полоса цвета «своих» слева, имя и одна строка исходного.
 * Ответ обязан читаться как ответ, а не как ещё один абзац. */
.quote {
  margin-bottom: 8px; padding: 4px 8px;
  border-left: 3px solid var(--good);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--text) 5%, transparent);
  color: var(--muted); font-size: var(--fs-sm);
}
.quote b { display: block; color: var(--good); font-weight: 600; }
.quote-jump {
  display: block; width: 100%; text-align: left; font: inherit;
  border: 0; border-left: 3px solid var(--good); cursor: pointer;
}
.quote-jump:hover { background: color-mix(in srgb, var(--text) 9%, transparent); }
.quote-jump .clip { display: block; }
.tag { color: var(--accent); }
.mono { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; }
.bubble b { font-weight: 700; }
.bubble i { font-style: italic; }
.bubble s { text-decoration: line-through; }
.bubble code { font-family: ui-monospace, Consolas, monospace; font-size: .92em;
  background: rgba(127, 127, 127, .16); padding: 1px 4px; border-radius: 4px; }

.file-card {
  display: grid; grid-template-columns: 38px minmax(0, 1fr) auto; gap: 12px; align-items: center;
  min-width: 210px;
}
.file-icon { width: 38px; height: 38px; border-radius: calc(var(--radius) * .8);
  display: grid; place-items: center; background: var(--surface); color: var(--muted); }

.msg-tools { display: flex; align-items: center; align-self: flex-end; opacity: 0; transition: opacity var(--fast) var(--ease); }
.msg:hover .msg-tools, .msg:focus-within .msg-tools { opacity: 1; }

/* Композер закреплён последней строкой сетки разговора и всегда целиком в
 * окне: 56–64 точки в одну строку текста. Полосы ответа и вложения — своими
 * строками над полем; кнопки и поле — одной гибкой строкой, сколько бы кнопок
 * ни было (раньше сетка на три колонки роняла поле и отправку во второй ряд,
 * и низ уходил за границу окна). */
.composer {
  padding: 10px 14px calc(12px + env(safe-area-inset-bottom));
  display: grid; gap: 8px;
  border-top: 1px solid var(--line);
  background: var(--surface);
}
.composer-line { display: flex; align-items: flex-end; gap: 8px; }
.composer-line .field-wrap { position: relative; flex: 1 1 auto; min-width: 0; display: flex; }
.composer-line .field-wrap .field { flex: 1 1 auto; min-width: 0; }
/* Знак эмодзи внутри поля (телефон): зона нажатия остаётся 48, а ширину у
 * строки он не отнимает. */
.composer-line .field-wrap .icon-btn { position: absolute; right: 2px; bottom: 0; }
.composer-line .field-wrap:has(.icon-btn) .field { padding-right: 52px; }
.composer-line .icon-btn, .composer-line .send { flex: none; }
.composer .icon-btn { color: var(--muted); }
.composer .icon-btn:hover { color: var(--text); }
.composer textarea {
  max-height: 190px; min-height: 44px;
  padding: 12px 16px;
  border-color: var(--line); background: var(--field);
  border-radius: var(--r-xl);
}
/* Кнопка отправки — залитый круг акцента со знаком короткой текучей волны.
 * Не самолётик (чужой знак, к тому же означающий «улетело далеко») и не знак
 * MVQ: знак продукта не бывает кнопкой одного действия. */
.composer .send {
  width: 46px; height: 46px; padding: 0;
  display: grid; place-items: center;
  border: 0; border-radius: 50%;
  background: var(--accent); color: var(--accentText);
  box-shadow: var(--lift);
}
.composer .send svg { width: 24px; height: 24px; }
.composer .send:hover:not([disabled]) { background: var(--accent-hover); }
.composer .send[disabled] { background: var(--surface2); color: var(--muted); opacity: 1; box-shadow: none; }
.reply-bar {
  grid-column: 1 / -1;
  display: flex; align-items: center; gap: 12px;
  padding: 8px 12px; margin-bottom: 2px;
  border-left: 2px solid var(--muted); border-radius: calc(var(--radius) * .6);
  background: var(--surface2); color: var(--muted); font-size: var(--fs-sm);
}
.attach-bar {
  grid-column: 1 / -1;
  display: flex; align-items: center; gap: 12px;
  padding: 8px 12px; margin-bottom: 2px;
  border-radius: calc(var(--radius) * .6);
  background: var(--surface2); font-size: var(--fs-sm);
}

.pinned {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 16px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  font-size: var(--fs-sm);
}

/* ── Панель сведений ─────────────────────────────────────────────────────── */

.info {
  border-left: 1px solid var(--line);
  background: var(--surface);
  overflow: auto;
  padding: 20px 16px;
}

/* ── Выезжающее меню ─────────────────────────────────────────────────────── */

.app::after {
  content: ""; position: absolute; z-index: 15; inset: 0;
  background: var(--veil); opacity: 0; pointer-events: none;
  transition: opacity var(--mid) var(--ease);
}
.app.drawer-open::after { opacity: 1; pointer-events: auto; }

.drawer {
  position: absolute; z-index: 20; inset: 0 auto 0 0;
  width: var(--list);
  display: flex; flex-direction: column;
  background: var(--surface);
  border-right: 1px solid var(--line);
  box-shadow: 18px 0 60px rgba(0, 0, 0, .5);
  transform: translateX(-102%);
  transition: transform var(--slow) var(--ease);
  overflow: auto;
}
.app.drawer-open .drawer { transform: none; }
.drawer-head { position: relative; padding: 20px 20px 16px; border-bottom: 1px solid var(--line); }
.drawer-head .name { display: block; font-weight: 700; margin-top: 12px; }
.drawer-head .id { display: block; color: var(--muted); font-size: var(--fs-sm); margin-top: 4px; }
.drawer-close { position: absolute; right: 10px; top: 10px; }
.drawer-item {
  min-height: 46px; padding: 0 20px;
  display: flex; align-items: center; gap: 16px;
  border: 0; background: transparent; color: var(--text); text-align: left;
  width: 100%;
}
.drawer-item:hover { background: var(--surface2); }
.drawer-item .ic { color: var(--muted); }
.drawer-sep { height: 1px; margin: 4px 0; background: var(--line); }
.space-switch {
  display: flex; align-items: center; gap: 12px;
  margin: 8px 8px; padding: 12px 12px;
  width: calc(100% - 16px);
  border: 0; border-radius: var(--radius);
  background: transparent; color: var(--text); text-align: left;
}
.space-switch:hover { background: var(--surface2); }
.space-switch .space-icon {
  width: 34px; height: 34px; flex: none;
  display: grid; place-items: center;
  border-radius: 50%; background: var(--surface2);
}
.space-switch b { display: block; }
.space-switch small { display: block; color: var(--muted); margin-top: 2px; }

/* ── Раздел (двухколоночный экран поверх рабочей области) ────────────────── */

.section {
  position: absolute; z-index: 14; inset: 0 0 0 var(--rail);
  display: grid; grid-template-rows: var(--head) minmax(0, 1fr);
  background: var(--bg);
  animation: fade-in var(--mid) var(--ease);
}
.section-head {
  display: flex; align-items: center; gap: 8px;
  padding: 0 16px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.section-head strong { font-size: var(--fs-md); }
.section-body { min-height: 0; display: grid; grid-template-columns: 300px minmax(0, 1fr); }
.section-body.single { grid-template-columns: minmax(0, 1fr); }
.section-nav { padding: 12px; border-right: 1px solid var(--line); overflow: auto; }
.section-nav button {
  width: 100%; min-height: 46px; padding: 8px 12px;
  display: flex; align-items: center; gap: 12px;
  border: 0; border-radius: var(--radius);
  background: transparent; color: var(--text); text-align: left;
}
.section-nav button:hover { background: var(--surface2); }
.section-nav button.active { background: var(--surface2); font-weight: 650; }
.section-main { padding: 26px clamp(20px, 5vw, 64px); overflow: auto; }
.section-main h2 { font-size: var(--fs-xl); letter-spacing: -.02em; }
.section-main .lead { margin: 8px 0 20px; color: var(--muted); }
.section-inner { max-width: 760px; }

.card {
  margin-bottom: 16px;
  padding: 2px 16px;
  background: var(--surface);
  border-radius: calc(var(--radius) * 1.2);
}
.card > .card-title { padding: 16px 0 8px; color: var(--muted); font-size: var(--fs-sm); }
.card-row {
  width: 100%;
  min-height: 58px; padding: 12px 0;
  display: flex; align-items: center; gap: 12px;
  border: 0; border-bottom: 1px solid var(--line);
  background: transparent; color: var(--text); text-align: left;
}
.card-row:last-child { border-bottom: 0; }
.card-row .ic { color: var(--muted); }
.card-row b { display: block; font-weight: 600; }
.card-row small { display: block; color: var(--muted); margin-top: 4px; line-height: 1.3; }
.card-row .tail { margin-left: auto; color: var(--muted); display: flex; align-items: center; gap: 8px; }
button.card-row:hover { background: color-mix(in srgb, var(--surface2) 60%, transparent); }
.card-note { padding: 12px 0 16px; color: var(--muted); font-size: var(--fs-sm); line-height: 1.4; }

.hero {
  display: grid; grid-template-columns: 68px minmax(0, 1fr) auto; gap: 16px; align-items: center;
  margin-bottom: 16px; padding: 20px;
  background: var(--surface); border-radius: calc(var(--radius) * 1.25);
}
.hero h3 { font-size: var(--fs-lg); }
.hero p { color: var(--muted); margin-top: 4px; }

.switch {
  position: relative; flex: none;
  width: 42px; height: 24px; padding: 0;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--surface2);
}
.switch::after {
  content: ""; position: absolute; left: 2px; top: 50%;
  width: 18px; height: 18px; margin-top: -8px;
  border-radius: 50%; background: var(--muted);
  transition: transform var(--fast) var(--ease), background-color var(--fast) var(--ease);
}
.switch[aria-checked="true"] { background: color-mix(in srgb, var(--accent) 32%, var(--surface2)); border-color: var(--accent); }
.switch[aria-checked="true"]::after { transform: translateX(18px); background: var(--accent); }

.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
  min-height: 34px; padding: 0 12px;
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: transparent; color: var(--muted);
}
.chip.active { background: var(--surface2); color: var(--text); border-color: var(--line-strong); }

.swatch { width: 26px; height: 26px; padding: 0; border-radius: 50%; border: 2px solid transparent; }
.swatch.active { border-color: var(--text); }

/* ── Состояния ───────────────────────────────────────────────────────────── */

.state {
  padding: 56px 24px; text-align: center; color: var(--muted);
  display: grid; justify-items: center; gap: 12px;
}
.state strong { color: var(--text); font-size: var(--fs-md); }
.state p { max-width: 420px; line-height: 1.45; }
.state .ic { color: var(--muted); }

.banner {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 16px;
  background: var(--surface2); color: var(--text);
  font-size: var(--fs-sm);
  border-bottom: 1px solid var(--line);
}
.banner.warn { background: color-mix(in srgb, var(--warn) 18%, var(--surface2)); }
.banner.bad { background: color-mix(in srgb, var(--bad) 18%, var(--surface2)); }
.banner button { margin-left: auto; }

.skeleton {
  border-radius: var(--radius);
  background: linear-gradient(90deg, var(--surface2) 25%, color-mix(in srgb, var(--surface2) 60%, var(--line-strong)) 37%, var(--surface2) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.4s linear infinite;
}
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes screen-in { from { opacity: .2; transform: translateX(18px); } to { opacity: 1; transform: none; } }

/* ── Окна и подсказки ────────────────────────────────────────────────────── */
/* Единый стандарт окон (2026-08): спокойный графит с сине-зелёным оттенком,
 * мягкий контраст, без чистого белого и неона. Фон под окном затемняется, но
 * остаётся различимым. Заголовок, содержимое и нижние действия разделяются
 * воздухом и волосяными линиями, а не рамкой вокруг каждого элемента.
 * Заливкой выделяется только главное действие. */

.modals { position: fixed; inset: 0; z-index: 60; pointer-events: none; }
.modal-veil {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  padding: 20px;
  background: var(--veil);
  pointer-events: auto;
  animation: fade-in var(--fast) var(--ease);
}
/* Стандарт окна 2027: ширина 520–640, высота не больше 80 % окна, один
 * заголовок, понятное закрытие, прокручивается только содержимое, действия
 * закреплены снизу, главное действие одно. */
.modal {
  width: clamp(520px, 44vw, 600px); max-width: 100%;
  max-height: 80vh;
  display: flex; flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  box-shadow: var(--shade);
  overflow: hidden;
}
/* Широкое окно — создание публикации: там рядом с текстом живут вложения.
 * Шире 640 оно не бывает: дизайн-система 2027 держит все окна в 520–640, и
 * «этому окну нужно больше» — начало того, как окна перестают быть похожими
 * друг на друга. */
.modal.wide { width: min(640px, 100%); }
.modal-head { padding: 20px 24px 12px; display: flex; align-items: center; gap: 12px; flex: none; }
.modal-head h3 { font-size: var(--fs-lg); font-weight: 650; letter-spacing: -.015em; }
.modal-body { padding: 4px 24px 16px; overflow: auto; overscroll-behavior: contain; }
.modal-foot {
  padding: 16px 24px 20px; flex: none;
  display: flex; gap: 12px; align-items: center; justify-content: flex-end;
  border-top: 1px solid var(--line);
}
.modal-foot .lead { margin-right: auto; display: flex; align-items: center; gap: 12px; }

/* Подтверждение: компактное окно по центру — и на телефоне тоже. */
.modal.confirm { width: min(440px, 100%); }
.modal.confirm .modal-foot { border-top: 0; padding-top: 4px; }

/* Опасное действие подтверждает залитая красным кнопка, а не акцентная. */
.btn.danger.fill { background: var(--bad); border-color: var(--bad); color: var(--bg); }
.btn.danger.fill:hover:not([disabled]) { background: var(--bad); opacity: .88; }

/* ── Нижняя панель телефона ──────────────────────────────────────────────── */

.sheet-veil { place-items: end stretch; padding: 0; }
.sheet {
  width: 100%; max-height: min(88dvh, 92vh);
  display: flex; flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line); border-bottom: 0;
  border-radius: calc(var(--radius) * 1.8) calc(var(--radius) * 1.8) 0 0;
  box-shadow: var(--shade);
  overflow: hidden;
  animation: sheet-in var(--slow) var(--ease);
  touch-action: none;
}
.sheet-grip {
  flex: none; width: 44px; height: 4px; margin: 10px auto 4px;
  border-radius: 2px; background: var(--line-strong);
}
.sheet-head { padding: 8px 12px 8px 20px; display: flex; align-items: center; gap: 12px; flex: none; }
.sheet-head h3 { font-size: var(--fs-md); font-weight: 650; }
.sheet-body {
  padding: 4px 20px 16px; overflow: auto; overscroll-behavior: contain;
  touch-action: pan-y;
}
.sheet-foot {
  padding: 10px 18px calc(12px + env(safe-area-inset-bottom)); flex: none;
  display: flex; gap: 12px; align-items: center; justify-content: flex-end;
  border-top: 1px solid var(--line);
}
.sheet-foot .lead { margin-right: auto; display: flex; align-items: center; gap: 12px; }
@keyframes sheet-in { from { transform: translateY(24px); opacity: .6; } }

/* ── Полноэкранная форма телефона ────────────────────────────────────────── */

.screen {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  background: var(--bg); color: var(--text);
  pointer-events: auto;
  animation: screen-in var(--slow) var(--ease);
  padding-top: env(safe-area-inset-top);
}
.screen-head {
  min-height: var(--head); flex: none;
  padding: 4px 12px;
  display: flex; align-items: center; gap: 8px;
}
/* Заголовок ужимается до обычного кегля: рядом стоят «назад» и главное
 * действие, и «Новая публикация» обязана помещаться целиком на 320. */
.screen-head h3 { font-size: var(--fs); font-weight: 650; text-align: center; }
.screen-head .text-btn {
  border: 0; background: transparent; color: var(--accent);
  font-weight: 650; font-size: var(--fs); padding: 12px 12px; min-height: 44px;
  border-radius: var(--radius);
}
.screen-head .text-btn[disabled] { color: var(--muted); }
.screen-body {
  flex: 1; min-height: 0; overflow: auto; overscroll-behavior: contain;
  padding: 8px 20px 16px;
}
.screen-foot {
  flex: none; padding: 10px 18px calc(12px + env(safe-area-inset-bottom));
  display: flex; gap: 12px; align-items: center; justify-content: flex-end;
  border-top: 1px solid var(--line);
  background: var(--surface);
}
.screen-foot .lead { margin-right: auto; display: flex; align-items: center; gap: 12px; }

/* ── Меню действий ───────────────────────────────────────────────────────── */

.menu {
  position: fixed; z-index: 70;
  min-width: 220px; max-width: min(340px, calc(100vw - 20px));
  padding: 8px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) * 1.2);
  box-shadow: var(--shade);
  pointer-events: auto;
}
.menu-item {
  width: 100%; min-height: 40px; padding: 8px 12px;
  display: flex; align-items: center; gap: 12px; text-align: left;
  border: 0; border-radius: calc(var(--radius) * .8);
  background: transparent; color: var(--text);
}
.menu-item small { display: block; color: var(--muted); font-size: var(--fs-sm); }
.menu-item:hover:not([disabled]) { background: var(--surface2); }
.menu-item.danger { color: var(--bad); }
.menu-check { color: var(--accent); font-weight: 700; }
.menu .sep, .menu-sheet .sep { height: 1px; margin: 4px 4px; background: var(--line); }

/* То же меню на телефоне — нижней панелью с крупными строками. */
.menu-sheet { display: grid; padding: 4px 0 8px; }
.menu-sheet .menu-item { min-height: 48px; padding: 8px 8px; }

/* ── Панель эмодзи ───────────────────────────────────────────────────────── */

.emoji-menu { min-width: 0; padding: 12px; }
.emoji-box { width: min(300px, 82vw); }
.emoji-title { color: var(--muted); font-size: var(--fs-xs); margin: 4px 2px 4px; }
.emoji-row { display: grid; grid-template-columns: repeat(6, 1fr); gap: 2px; }
.emoji {
  min-height: var(--tap-touch); border: 0; border-radius: var(--r-sm);
  background: transparent; font-size: 22px; line-height: 1;
  display: grid; place-items: center;
}
.emoji:hover { background: var(--surface2); }

/* ── Создание публикации ─────────────────────────────────────────────────── */

.compose-author { display: flex; align-items: center; gap: 12px; padding: 4px 0 12px; }
.compose-author b { font-size: var(--fs); }

/* Единый выпадающий выбор аудитории — спокойная капсула с шевроном. */
.audience-btn {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 40px; padding: 0 12px;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: transparent; color: var(--text); font-weight: 600;
}
.audience-btn:hover { background: var(--surface2); }

/* Большое чистое поле: без рамки вокруг текста, счётчик в углу, вложения
 * внутри. Рамку несёт весь блок редактора, а не каждый элемент в нём. */
.compose-editor {
  border: 1px solid var(--line); border-radius: calc(var(--radius) * 1.3);
  background: var(--field);
  padding: 12px 16px 12px;
  display: grid; gap: 12px;
}
.compose-editor textarea {
  width: 100%; min-height: 108px; max-height: 38vh;
  border: 0; background: transparent; color: var(--text);
  resize: none; outline: none; padding: 0;
  font-size: var(--fs-md); line-height: 1.5;
}
.compose-editor textarea::placeholder { color: var(--muted); }
.compose-count { text-align: right; color: var(--muted); font-size: var(--fs-sm); }

/* Предпросмотр вложений внутри редактора. */
.compose-attach { display: flex; flex-wrap: wrap; gap: 12px; }
.attach-tile {
  position: relative; width: 96px; height: 96px; flex: none;
  border-radius: calc(var(--radius) * 1.1); overflow: hidden;
  background: var(--surface2); border: 0; padding: 0;
  display: grid; place-items: center; color: var(--muted);
}
.attach-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.attach-tile .attach-x {
  position: absolute; top: 5px; right: 5px;
  width: 26px; height: 26px; border: 0; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(10, 13, 16, .62); color: #dce1e6;
}
.attach-tile small {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 4px 8px; font-size: var(--fs-xs); color: #dce1e6;
  background: linear-gradient(transparent, rgba(10, 13, 16, .7));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.attach-add { border: 1px dashed var(--line-strong); background: transparent; }
.attach-add:hover { background: var(--surface2); }

/* Одна панель вложений: фото, видео, файл, опрос и «Ещё». */
.compose-tools {
  display: flex; align-items: stretch;
  border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden;
}
.compose-tools button {
  flex: 1; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border: 0; background: transparent; color: var(--text); font-weight: 600;
}
.compose-tools button + button { border-left: 1px solid var(--line); }
.compose-tools button:hover { background: var(--surface2); }

/* ── Очередь выпуска в кабинете автора ───────────────────────────────────── */

/* Снимок обложки в строке очереди: то же место, что у знака вида записи, —
 * чтобы строки не прыгали, когда у одной записи картинка есть, а у другой нет. */
.planned-shot {
  width: 40px; height: 40px; flex: none;
  border-radius: calc(var(--radius) * .9); overflow: hidden;
  background: var(--surface2);
}
.planned-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.planned-row { align-items: center; gap: 12px; }

/* Когда это выйдет: «сейчас» или назначенное время.
 *
 * Отдельной видимой строкой, а не внутри свёрнутых настроек: отложенная
 * публикация меняет то, что произойдёт по нажатию главной кнопки, и прятать
 * такое под «Ещё» — верный способ выпустить в четверг то, что собирались
 * выпустить в пятницу. */
.compose-when {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-top: 8px;
}
.compose-when .audience-btn.on {
  border-color: var(--accent);
  color: var(--accent);
}

/* Сворачиваемые настройки: комментарии и видимость. */
.compose-fold {
  width: 100%; min-height: 44px; padding: 4px 2px;
  display: flex; align-items: center; gap: 12px;
  border: 0; background: transparent; color: var(--text); font-weight: 600;
  text-align: left;
}
.compose-fold .fold-arrow { margin-left: auto; transition: transform var(--mid) var(--ease); }
.compose-fold[aria-expanded="true"] .fold-arrow { transform: rotate(180deg); }
.compose-fold-body { padding: 2px 2px 8px; display: grid; gap: 12px; }

.draft-state { color: var(--muted); font-size: var(--fs-sm); display: inline-flex; align-items: center; gap: 8px; }
.draft-state .ok { color: var(--good); }

/* Телефон: панель вложений прижата вниз, подписи под значками — как в
 * телефонных редакторах; состояние черновика — строкой под панелью. */
.screen .compose-tools { border: 0; border-top: 1px solid var(--line); border-radius: 0; }
.screen .compose-tools button {
  flex-direction: column; gap: 4px; min-height: 56px; padding: 8px 2px;
  font-size: var(--fs-xs); font-weight: 500; color: var(--text);
}
.screen .compose-tools button + button { border-left: 0; }
.screen-foot.compose {
  display: block; padding: 0 0 env(safe-area-inset-bottom); border-top: 0;
}
.draft-state-row { display: flex; justify-content: center; padding: 8px 0 12px; }

/* Телефон: редактор без рамки — текст лежит прямо на экране, как на макете,
 * а фотография занимает всю ширину. */
.screen .compose-editor { border: 0; background: transparent; padding: 8px 0; border-radius: 0; }
.screen .attach-tile:not(.attach-add) { width: 100%; height: auto; min-height: 96px; }
.screen .attach-tile img { max-height: 46vh; }

/* Телефон: сенсорным целям — не меньше 48 точек. Требование доступности, а не
 * вкус: палец не попадает в сорок. */
@media (max-width: 900px) {
  .icon-btn, .btn { min-height: var(--tap-touch); }
  .icon-btn { width: var(--tap-touch); height: var(--tap-touch); }
  .post-action { min-height: var(--tap-touch); min-width: var(--tap-touch); }
  /* Пять целей в ряду под записью на узком экране не помещаются подписями, но
   * обязаны помещаться пальцем. Отступы внутри действий ужимаются, полоса
   * растягивается на всю ширину — а сами цели остаются по 48 точек. */
  .post-actions { gap: 0; }
  .post-action { padding: 0 8px; }
  .post-action.pick { min-width: 40px; padding: 0 4px; }
  .comment-replies { margin-left: 8px; padding-left: 12px; }
  .comment-acts { gap: 20px; }
  /* Ответить, изменить и удалить — тоже сенсорные цели, хоть и текстом. */
  .comment-acts .link-btn { min-height: 40px; display: inline-flex; align-items: center; }
  .react-pick { min-width: 64px; min-height: 64px; }
  .modal { width: min(560px, 100%); }
}
@media (max-width: 700px) {
  .modal-foot .btn, .sheet-foot .btn, .screen-foot .btn { min-height: var(--tap-touch); }
  .modal.confirm .modal-foot { display: grid; grid-template-columns: 1fr 1fr; }
  .modal.confirm .modal-foot .btn { width: 100%; }
  .chip { min-height: 44px; }
}

.toasts {
  position: fixed; z-index: 80; left: 50%; bottom: 22px;
  transform: translateX(-50%);
  display: grid; gap: 8px; justify-items: center;
  pointer-events: none;
}
.toast {
  max-width: min(560px, 92vw);
  padding: 12px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface); color: var(--text);
  box-shadow: var(--shade);
  animation: fade-in var(--fast) var(--ease);
}
.toast.bad { border-color: color-mix(in srgb, var(--bad) 55%, var(--line)); }
.toast.good { border-color: color-mix(in srgb, var(--good) 55%, var(--line)); }

/* ── Вход и регистрация ──────────────────────────────────────────────────── */

.auth {
  height: 100%;
  display: grid; grid-template-columns: minmax(330px, 1.08fr) minmax(390px, .92fr);
  background: var(--bg); color: var(--text);
}
/* Левая половина двери: глубокий днестровский синий с молдавским мотивом. Не
 * пейзаж и не фотография — характер здесь держат цвет и ритм.
 *
 * Мотив ОДИН и крупный, во всю высоту панели: древо жизни со стенного ковра.
 * Плитка мелкого узора на этой поверхности читалась бы обоями, а обои — это
 * фон гостиницы, а не лицо продукта. Мотив стоит справа от текста и намеренно
 * не по центру: он сопровождает рассказ, а не спорит с ним. */
.auth-story {
  position: relative; overflow: hidden;
  padding: 40px clamp(32px, 6vw, 90px) 40px;
  /* Знак сверху, рассказ — **по центру вместе с перечнем**.
   *
   * Было `space-between` на трёх блоках: знак прижимался к верху, перечень к
   * низу, а между заголовком и перечнем зияла треть экрана. Проверка так и
   * записала — «слишком много пустоты». Теперь пустота делится поровну сверху
   * и снизу от одного смыслового блока, а не вставляется в его середину. */
  /* Знак сверху, мотив во всю высоту, одна строка внизу — если она есть.
   * Ничего по центру: центр отдан композиции, а не тексту. */
  display: grid; grid-template-rows: auto 1fr auto;
  background: var(--rail-bg);
  color: #F3EFE7;
}
/* Одна строка на первом знакомстве. Больше на этой панели текста не бывает. */
.auth-line {
  position: relative; z-index: 1;
  align-self: end; max-width: 30ch;
  font-size: var(--fs-sm); line-height: 1.5;
}
.auth-story::after {
  content: ""; position: absolute; inset: 0;
  pointer-events: none;
  /* Маска, а не картинка: цвет древа берётся из темы одной строкой ниже, и
   * один файл работает на всех поверхностях. Прежние два файла — светлый и
   * тёмный — расходились при каждой правке геометрии. */
  background-color: var(--brand-pattern-ink);
  -webkit-mask-image: url("../pattern/pomul-brand.svg");
  mask-image: url("../pattern/pomul-brand.svg");
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  /* Правым краем и во всю высоту: под текстом мотива нет, он идёт рядом. */
  /* Композиция намеренно не по центру: древо стоит правее и чуть выше
   * середины, как на ковре, где мотив никогда не совпадает с осью полотна. */
  -webkit-mask-position: right 10% center; mask-position: right 10% center;
  -webkit-mask-size: auto 88%; mask-size: auto 88%;
}
.auth-story .mvq-mark .mark-ink, .auth-story .mvq-mark .mark-dot { fill: #F3EFE7; }
/* Оливковая дуга на почти чёрном тонет: на светлой поверхности #58704A читается,
 * на этой — нет. Берём тот же тон светлее, а не «просто белый»: белым знак
 * снова стал бы кольцом с точкой. */
.auth-story .mvq-mark .mark-arc { fill: #BCCBAA; }
.auth-story .mvq-mark .mark-name { fill: #F3EFE7; }
.auth-story .auth-line { color: rgba(243, 239, 231, .72); }
.auth-mark { position: relative; z-index: 1; align-self: start; }
.auth-mark .mvq-mark { height: 40px; }
/* Знак над формой. Показывается там, где рассказа слева нет: на шагах и на
 * узком окне. Два знака сразу — на двери и над формой — были бы повтором. */
.auth-brand { display: none; margin-bottom: 20px; }
.auth-brand .mvq-mark { height: 32px; }
.auth.solo .auth-brand { display: flex; }

.auth-side {
  position: relative;
  padding: 34px clamp(24px, 5vw, 64px);
  display: grid;
  /* `safe center` вместо `center`: когда карточка выше окна, обычное
   * центрирование срезает ей **верх**, и до заголовка не добраться ничем —
   * прокрутка не идёт выше нулевой точки. `safe` в тесноте прижимает к началу.
   * Ровно это было видно на 150 и 200 %. */
  align-content: safe center;
  background: var(--bg);
  min-height: 0;
  overflow-y: auto;
  /* Горизонтальной прокрутки на входе не бывает: длинному слову положено
   * переноситься, а не расширять экран. */
  overflow-x: hidden;
}
/* Поле, получившее клавиатуру, не должно оказаться под ней или под краем
 * окна: браузер сам подкручивает к нему, а этот отступ оставляет воздух. */
.auth-side .field:focus, .auth-side button:focus-visible { scroll-margin-block: 24px; }
/* Форма — карточка на поверхности, а не текст, висящий в пустом столбце.
 * Рамка и подложка дают ей вес: глазу видно, где начинается и кончается то,
 * что надо заполнить. */
.auth-card {
  width: min(440px, 100%); margin: auto;
  padding: 30px clamp(20px, 4vw, 34px);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.auth-card h1 { font-size: var(--fs-xl); letter-spacing: -.02em; }
.auth-card > p.lead { margin: 8px 0 24px; color: var(--muted); line-height: 1.5; }
.auth-actions { display: grid; gap: 12px; }
.auth-divider { display: flex; align-items: center; gap: 12px; margin: 16px 0; color: var(--muted); font-size: var(--fs-xs); }
.auth-divider::before, .auth-divider::after { content: ""; height: 1px; flex: 1; background: var(--line); }
.auth-field { display: grid; gap: 8px; margin-bottom: 12px; color: var(--muted); font-size: var(--fs-xs); }
.auth-field .field { min-height: 46px; }
.auth-field small { line-height: 1.35; }
.auth-inline { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 2px 0 16px; color: var(--muted); font-size: var(--fs-sm); }
.link-btn { border: 0; background: transparent; color: var(--text); padding: 0; text-decoration: underline; text-underline-offset: 3px; }
.auth-steps { display: flex; gap: 8px; margin-bottom: 20px; }
.auth-steps i { height: 3px; flex: 1; border-radius: 2px; background: var(--line); }
.auth-steps i.on { background: var(--text); }
/* Подпись шага живёт словами, полосы — только украшение под aria-hidden. */
.auth-steps-wrap { margin-bottom: 20px; }
.auth-steps-wrap .auth-step-label { margin: 0 0 8px; color: var(--muted); font-size: var(--fs-xs); }
.auth-steps-wrap .auth-steps { margin-bottom: 0; }
/* Постоянные подписи полей и пополевые ошибки: placeholder исчезает при
 * вводе, поэтому подписью не считается. */
.field-label { color: var(--text); font-size: var(--fs-xs); font-weight: 600; }
.field-err { margin: 0; color: var(--bad); font-size: var(--fs-xs); line-height: 1.35; }
.field-err:empty { display: none; }
.field[aria-invalid="true"] { border-color: var(--bad); }
.recovery-box {
  padding: 16px;
  border: 1px dashed var(--line-strong); border-radius: var(--radius);
  background: var(--field);
  font: 600 var(--fs) ui-monospace, Consolas, monospace;
  line-height: 1.7; overflow-wrap: anywhere;
}
.auth-back { position: absolute; left: 14px; top: 14px; }
.qr-box { display: grid; place-items: center; padding: 16px; background: #fff; border-radius: var(--radius); }
.qr-box svg { width: min(240px, 60vw); height: auto; }

/* ── Социальная сеть ─────────────────────────────────────────────────────────
 * Другое пространство и другая сетка: слева разделы, посередине лента, справа
 * связи. Мессенджер сюда не заглядывает и своих колонок не навязывает. */

.social {
  height: 100%;
  display: grid;
  /* Правая колонка объявлена `minmax(0, --side)`, а не жёсткими 320.
   *
   * Жёсткая ширина у последней колонки означает: не влезло — вылезет за край.
   * Ровно это и было видно на 1440 — колонка уезжала правым краем за окно, и
   * «Все» превращалось в «Вс», а кнопки «Добавить» и «Вступить» обрезались.
   * С minmax колонка сама ужимается на десяток точек, а её содержимое давно
   * умеет ужиматься: у строк стоит `clip`. */
  grid-template-columns: var(--nav) minmax(0, 1fr) minmax(0, var(--side));
  grid-template-rows: minmax(0, 1fr);
  background: var(--bg);
  overflow: hidden;
}
.social > * { min-width: 0; min-height: 0; }
/* Правой колонке нечего сказать на этом экране — колонки нет, место у ленты. */
.social.no-side { grid-template-columns: var(--nav) minmax(0, 1fr); }
.social.no-side .social-side { display: none; }

/* Левая колонка: знак, своя карточка, разделы, внизу — явная дверь в
 * приватный мессенджер. Внизу же лежит тканый узор: единственное место, где
 * характер виден глазом, и он не мешает читать. */
.social-nav {
  position: relative;
  display: flex; flex-direction: column; gap: 2px;
  padding: 20px 16px 16px;
  background: var(--bg);
  overflow: auto;
}
/* Узор внизу колонки. Гаснет кверху — не второй маской (две маски
 * складываются в браузерах по-разному и однажды разошлись бы), а самим
 * цветом: маска вырезает мотив из вертикального перехода в прозрачность. */
.social-nav::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0; height: 220px;
  pointer-events: none; z-index: 0;
  background-image: linear-gradient(transparent 8%, var(--pattern-ink-strong));
  -webkit-mask-image: var(--weave);
  mask-image: var(--weave);
  -webkit-mask-size: var(--weave-size) var(--weave-size);
  mask-size: var(--weave-size) var(--weave-size);
}
.social-nav > * { position: relative; z-index: 1; }
.social-brand { padding: 0 12px 24px; display: flex; align-items: center; }
.social-brand .mvq-mark { height: 34px; }

/* Своя карточка: лицо, имя, короткий адрес и шеврон — отсюда открываются
 * настройки, смена языка и выход. Адрес пишется как @vadim и никогда с
 * техническим хвостом. */
.social-me {
  width: 100%; margin-bottom: 12px; padding: 8px 12px;
  display: flex; align-items: center; gap: 12px;
  border: 0; border-radius: var(--radius);
  background: transparent; color: var(--text); text-align: left;
  min-height: 56px;
}
.social-me:hover { background: var(--surface2); }
.social-me b { display: block; font-weight: 650; }
.social-me small { display: block; color: var(--muted); font-size: var(--fs-sm); margin-top: 1px; }
.social-me .ic { color: var(--muted); }

.social-nav-btn {
  min-height: 48px; padding: 0 16px;
  display: flex; align-items: center; gap: 16px;
  border: 0; border-radius: var(--radius);
  background: transparent; color: var(--text); text-align: left;
  font-size: var(--fs-md);
}
.social-nav-btn .ic { color: var(--muted); }
.social-nav-btn:hover { background: var(--surface2); }
.social-nav-btn.active { background: var(--accent); color: var(--accentText); font-weight: 650; }
.social-nav-btn.active .ic { color: var(--accentText); }
/* Дверь в другой продукт прижата к низу и отделена волоском: это переход
 * между пространствами, а не ещё один раздел ленты. */
/* Дверь в другой продукт: подпись целиком и мельче остальных разделов. Она
 * длиннее любого имени раздела, и на 260 точках только так помещается без
 * многоточия — приёмка размеров ловила обрезку на четырёх ширинах. */
.social-back {
  margin-top: auto; padding: 16px 8px 0;
  gap: 12px;
  border-top: 1px solid var(--line);
  border-radius: 0;
  color: var(--muted); font-size: var(--fs-sm);
}
.social-back .ic { width: 18px; height: 18px; }
.social-back:hover { background: transparent; color: var(--text); }

.social-main { min-width: 0; overflow: auto; }
/* Шапка — всегда одна строка. Заголовок обрезается многоточием, отборы
 * прокручиваются вбок без видимой полосы; вертикальных букв и второй строки
 * поверх содержимого здесь не бывает ни на какой ширине. */
.social-head {
  position: sticky; top: 0; z-index: 2;
  height: var(--head);
  padding: 0 20px;
  display: flex; align-items: center; gap: 12px;
  flex-wrap: nowrap;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(14px);
}
.social-head .head-title {
  flex: 0 1 auto; min-width: 2.5em;
  font-size: var(--fs-xl); font-weight: 700; letter-spacing: -.025em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Полка отборов — сегментированная капсула, как на эталоне: одна подложка,
 * выбранная фишка залита акцентом. */
.social-head .chips {
  flex: 0 1 auto; min-width: 0;
  flex-wrap: nowrap; overflow-x: auto;
  padding: 4px; gap: 2px;
  border-radius: var(--r-lg);
  background: var(--surface2);
}
/* Фишка на полке не сжимается: сжатая фишка обрезала «Для вас» до одной буквы.
 * Не помещается полка — прокручивается сама, подпись всегда целая. */
.social-head .chip {
  flex: none; border: 0; background: transparent; border-radius: var(--r-md);
  min-height: 34px; color: var(--muted); font-weight: 600;
}
.social-head .chip:hover { color: var(--text); }
.social-head .chip.active { background: var(--accent); color: var(--accentText); }
.social-head .icon-btn { position: relative; }
.social-head .icon-btn .dot {
  position: absolute; right: 0; top: 1px;
  min-width: 17px; height: 17px; padding: 0 4px;
  display: grid; place-items: center;
  border-radius: var(--r-sm);
  background: var(--bad); color: #fff;
  font-size: 10px; font-weight: 700;
}
/* Создание записи — единственное залитое действие шапки. */
.social-head .head-compose {
  background: var(--accent); color: var(--accentText);
}
.social-head .head-compose:hover { background: var(--accent-hover); }

/* Знак MVQ вместо заголовка — так шапка ленты выглядит на телефоне. */
.social-head .head-brand { flex: none; display: flex; align-items: center; }
.social-head .head-brand .mvq-mark { height: 30px; }
/* Полка отборов на телефоне — первой строкой содержимого, не в шапке: в шапке
 * она отняла бы высоту у ленты навсегда, а здесь уезжает вместе с ней. */
.modes-row { padding: 12px 12px 2px; max-width: var(--feed); margin: 0 auto; }
.modes-row .chips {
  display: flex; gap: 2px; padding: 4px; overflow-x: auto;
  border-radius: var(--r-lg); background: var(--surface2);
}
.modes-row .chip {
  flex: 1 0 auto; justify-content: center; min-height: 38px;
  border: 0; background: transparent; border-radius: var(--r-md);
  color: var(--muted); font-weight: 600;
}
.modes-row .chip.active { background: var(--accent); color: var(--accentText); }

/* ⚠️ `grid-template-columns: minmax(0, 1fr)`, а не просто `display: grid`.
 *
 * У элементов сетки `min-width` по умолчанию `auto`: ужаться ниже своей
 * минимальной ширины они не могут и вылезают за колонку. Так и было — колонка
 * 320, а карточки внутри 325, и на 1440 «Все» превращалось в «Вс», а кнопки
 * «Добавить» и «Вступить» обрезались правым краем окна. Само по себе
 * `overflow: hidden` на `.social` эту беду только прятало. */
.social-side {
  padding: 16px 16px 24px; overflow: auto;
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: 16px; align-content: start;
}
/* Колонка уже 288 — а карточки в ней остаются 325 без этой строки.
 *
 * `minmax(0, 1fr)` задаёт ширину **дорожки**, но у самих элементов сетки
 * `min-width` по-прежнему `auto`, и ужаться ниже своей минимальной ширины они
 * не могут: вылезают за дорожку и обрезаются краем окна. Ровно это и делало
 * «Все» → «Вс» и резало «Добавить». Проверено измерением, а не на глаз. */
.social-side > * { min-width: 0; max-width: 100%; }
/* Стопка карточек правой колонки. `minmax(0, 1fr)` обязателен: без него
 * дорожка считается по max-content и колонка уезжает за край окна. */
.side-stack {
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: 16px; min-width: 0;
}
.side-stack > * { min-width: 0; }
.social-side .side-search { position: relative; display: grid; }
.social-side .side-search > .ic { position: absolute; left: 12px; top: 50%; margin-top: -8px; color: var(--muted); pointer-events: none; }
.social-side .side-search input { padding-left: 40px; }
/* Карточка контекста: заголовок, «Все» и три-четыре строки — не больше.
 * Правая колонка помогает, а не пересказывает экран ещё раз. */
.side-card {
  padding: 16px 16px 8px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.side-card > header {
  display: flex; align-items: center; gap: 8px; margin-bottom: 4px;
}
/* Заголовок ужимается, «Все» — никогда.
 *
 * Без min-width: 0 длинное название («Рекомендуемые сообщества») выталкивало
 * ссылку за правый край карточки, и от «Все» оставалось «Вс». Ужимать надо
 * то, чего много, а не то, чего три буквы. */
.side-card > header b {
  font-size: var(--fs); font-weight: 650;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.side-card > header .link-btn {
  margin-left: auto; flex: none;
  color: var(--muted); font-size: var(--fs-sm); text-decoration: none;
}
.side-card > header .link-btn:hover { color: var(--accent); }
.side-row {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 0; min-height: 52px;
}
/* Середина строки ужимается, лицо и кнопка действия — нет. По той же причине:
 * иначе «Добавить» и «Вступить» обрезались на узкой колонке. */
.side-row > .grow { min-width: 0; }
.side-row > .avatar, .side-row > .btn, .side-row > .side-date, .side-row > .side-thumb { flex: none; }
.side-row .grow b { display: block; font-weight: 650; font-size: var(--fs-sm); }
.side-row .grow small { display: block; color: var(--muted); font-size: var(--fs-xs); margin-top: 2px; }
.side-row .avatar.sq { width: 44px; height: 44px; border-radius: var(--r-sm); }
/* Дата события квадратиком: день крупно, месяц под ним. */
.side-date {
  flex: none; width: 48px; padding: 4px 0;
  display: grid; justify-items: center;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface2);
}
.side-date b { font-size: var(--fs-md); font-weight: 700; line-height: 1.1; }
.side-date small { color: var(--muted); font-size: 10px; text-transform: uppercase; letter-spacing: .06em; }

.social-page { padding: 4px 20px 24px; }
.social-page-inner { max-width: var(--feed); margin: 0 auto; }

.social-compose, .post {
  max-width: var(--feed); margin: 14px auto;
  padding: 16px 16px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.social-compose { display: grid; grid-template-columns: 44px minmax(0, 1fr); gap: 12px; align-items: start; }
.social-compose textarea { border-color: transparent; background: transparent; }
/* Ряд быстрых вложений под полем: фото, ссылка, событие — слева; эмодзи и
 * голос — справа. */
.compose-row { display: flex; align-items: center; gap: 2px; margin-top: 4px; }
.compose-row .icon-btn { color: var(--muted); }
.compose-row .icon-btn:hover { color: var(--accent); }
.compose-row .grow { flex: 1; }

.post-head { display: grid; grid-template-columns: 44px minmax(0, 1fr) auto; gap: 12px; align-items: center; }
.post-head b { display: block; }
/* Строка под именем — гибкая, а не блочная: имя, время и аудитория стоят
 * через промежуток. Блочный `small` схлопывал их в «@имя·16:20·Все». */
.post-head small { display: flex; flex-wrap: wrap; margin-top: 2px; color: var(--muted); font-size: var(--fs-sm); }
.post-head .avatar { border: 0; cursor: pointer; }
/* Кому видна запись — значком и словом рядом со временем: аудитория здесь
 * решение автора, а не мелкий шрифт в меню. */
.post-audience { display: inline-flex; align-items: center; gap: 4px; }
.post-audience .ic { width: 14px; height: 14px; }
.post-why { margin-top: 8px; color: var(--muted); font-size: var(--fs-xs); }
.post-text { margin: 8px 0 0; line-height: 1.5; white-space: pre-wrap; }
.post-media { display: grid; gap: 4px; margin-top: 12px; border-radius: var(--r-md); overflow: hidden; }
.post-media.many { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* Три вложения — первое во всю высоту слева, два справа: так снимок с людьми
 * читается, а не превращается в три марки. */
.post-media.three { grid-template-columns: 1.6fr 1fr; grid-template-rows: repeat(2, 1fr); }
.post-media.three > *:first-child { grid-row: 1 / 3; }

/* Запись, ждущая связи: пунктирная рамка и приглушённый вид — как у пузыря в
 * очереди. Она не пропала и не ушла; она ждёт. */
.post.queued { border-style: dashed; border-color: var(--line-strong); }
.post.queued .post-head small { color: var(--warn); }
.media-cell {
  min-height: 92px; padding: 12px;
  display: flex; align-items: center; gap: 12px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface2); color: var(--muted);
}
.post-place { margin-top: 8px; display: flex; align-items: center; gap: 8px; color: var(--muted); font-size: var(--fs-sm); }
.repost { margin-top: 12px; border: 1px solid var(--line); border-radius: var(--radius); }
.repost .post { margin: 0; background: transparent; }

.poll { margin-top: 12px; display: grid; gap: 8px; }
.poll-option {
  position: relative; overflow: hidden;
  min-height: 40px; padding: 0 12px;
  display: flex; align-items: center; gap: 12px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface2); color: var(--text); text-align: left;
}
.poll-option.mine { border-color: var(--accent); }
.poll-fill {
  position: absolute; left: 0; top: 0; bottom: 0;
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  transition: width var(--slow) var(--ease);
}
.poll-option > .grow, .poll-option > .small { position: relative; }

/* Отклики: три слева, «отложить» — справа у края. Числа рядом со значком, а не
 * подписи: подписи в четыре слова превращают ленту в панель управления. */
.post-actions { display: flex; align-items: center; gap: 4px; margin-top: 12px; }
.post-actions .grow { flex: 1; }
.post-action {
  min-height: var(--tap); min-width: var(--tap); padding: 0 12px;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border: 0; border-radius: var(--radius);
  background: transparent; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.post-action:hover { background: var(--surface2); color: var(--text); }
.post-action.on { color: var(--accent); }
/* «Нравится» — единственное место, где в ленте появляется красный: это
 * отклик человека, а не тревога. Отмеченное отличается и формой знака
 * (залитое сердце), и цветом — состояние не кодируется одним лишь цветом. */
.post-action.like.on { color: var(--bad); }

/* Кнопка выбора знака — рядом с самим знаком, узкой и без числа: она не про
 * «сколько», а про «чем». На пальце она всё равно получает свои 48 px (см.
 * правило ниже), но места в ряду занимает вдвое меньше обычного действия. */
.post-action.pick { padding: 0 8px; opacity: .75; }
.post-action.pick:hover { opacity: 1; }
.react-glyph { font-size: 1.05em; line-height: 1; }

/* Свод откликов: какими знаками откликнулись и сколько всего.
 * Показывается только когда знаков больше одного — под записью с тремя
 * сердцами он повторял бы саму кнопку. */
.post-reacts {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 8px; color: var(--muted); font-size: var(--fs-sm);
}
.react-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px; border-radius: var(--r-lg);
  background: var(--surface2);
}
.react-total { margin-left: 2px; }

/* Полоска выбора знака: шесть знаков в ряд, у каждого имя под ним.
 * Имя обязательно — полоска без имён это ребус для того, кто видит её
 * впервые, и пустота для того, кто не видит вовсе. */
.react-menu { min-width: 0; padding: 12px; }
.react-row { display: flex; gap: 4px; }
.react-pick {
  min-width: 56px; min-height: 56px; padding: 8px 4px;
  display: grid; justify-items: center; gap: 2px;
  border: 1px solid transparent; border-radius: var(--radius);
  background: transparent; color: var(--muted);
}
.react-pick .react-glyph { font-size: 1.7em; }
.react-pick small { font-size: var(--fs-xs); }
.react-pick:hover { background: var(--surface2); color: var(--text); }
.react-pick.on { border-color: var(--accent); color: var(--text); }
/* На телефоне полоска приезжает нижней панелью и получает всю ширину. */
.sheet .react-row { justify-content: space-between; flex-wrap: wrap; }

/* Подсказка имён после «@». Тот же вид, что у меню действий: это список, из
 * которого выбирают, и другого вида ему незачем. */
.mention-menu { padding: 8px; max-height: min(320px, 50vh); overflow-y: auto; }
.mention-menu .menu-item.active { background: var(--surface2); }

/* ── Обсуждение ──────────────────────────────────────────────────────────── */

.comment-thread { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); }
.comment { display: grid; grid-template-columns: 34px minmax(0, 1fr) auto; gap: 8px; margin: 8px 0; }
.comment-body { margin: 2px 0 0; line-height: 1.45; }
.comment-branch { margin: 8px 0; }
/* Ответы — со сдвигом и волосяной чертой слева: уровень ровно один, и черта
 * говорит, где ветка кончается. Лестница из пяти уровней на экране телефона
 * превращается в колонку шириной в слово, поэтому её здесь нет. */
.comment-replies {
  margin-left: 20px; padding-left: 12px;
  border-left: 1px solid var(--line);
}
.comment-acts { display: flex; gap: 16px; margin-top: 4px; color: var(--muted); font-size: var(--fs-sm); }
.comment-acts .link-btn { color: var(--muted); text-decoration: none; }
.comment-acts .link-btn:hover { color: var(--text); text-decoration: underline; }
.comment-acts .link-btn.danger { color: color-mix(in srgb, var(--bad) 75%, var(--muted)); }

/* Знак под откликом.
 *
 * Подчёркивания при наведении здесь нет намеренно: соседи по строке —
 * слова («Ответить», «Изменить»), и подчёркнутый значок читался бы как
 * подчёркнутая буква. Свой знак красится тем же цветом, что и под записью:
 * одно действие обязано выглядеть одинаково в обоих местах. */
.comment-acts .comment-like {
  display: inline-flex; align-items: center; gap: 4px;
}
.comment-acts .comment-like:hover { text-decoration: none; }
.comment-acts .comment-like.on { color: var(--bad); }
/* Чипы здесь мельче, чем под записью: строка действий отклика узкая, и в
 * ней шесть полноразмерных чипов уехали бы на второй ряд. */
.comment-reacts { display: inline-flex; align-items: center; gap: 4px; }
.comment-reacts .react-chip { padding: 1px 8px; font-size: var(--fs-xs); }
.comment-reacts .react-total { display: none; }

/* Свод откликов: кнопка тому, кому есть куда нажать, текст всем прочим.
 *
 * У кнопки должно быть видно, что она кнопка, — иначе дверь к списку
 * открывает только тот, кто случайно по ней попал. Обводка при выходе с
 * клавиатуры обязательна отдельно: `:focus-visible` — единственное, что видит
 * человек без мыши. */
.react-sum, .react-open {
  display: inline-flex; align-items: center; gap: 4px; flex-wrap: wrap;
}
.react-open {
  background: none; border: 0; padding: 2px 4px; margin: -2px -4px;
  border-radius: var(--r-md); color: inherit; font: inherit; cursor: pointer;
}
.react-open:hover { background: var(--surface2); }
.react-open:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.comment-reacts .react-open { padding: 1px 4px; margin: -1px -4px; }
/* «Показать более ранние» — над разговором, во всю ширину и с воздухом.
 *
 * Во всю ширину, потому что это граница страницы, а не действие над соседней
 * строкой: узкая ссылка посреди обсуждения читается как чей-то ответ. */
.comment-earlier {
  display: block; width: 100%; text-align: center;
  padding: 8px 0; margin-bottom: 4px; min-height: var(--tap);
}
/* Строка медиатеки: выбор во всю ширину, «убрать» отдельной кнопкой.
 *
 * Выбор — кнопка без вида кнопки: она занимает строку целиком, и рамка вокруг
 * неё читалась бы как вторая кнопка рядом с настоящей. Обводка при выходе с
 * клавиатуры обязательна отдельно — без неё строка недостижима без мыши. */
.media-pick {
  display: flex; align-items: center; gap: 12px; text-align: left;
  background: none; border: 0; padding: 0; margin: 0; color: inherit;
  font: inherit; cursor: pointer; min-width: 0;
}
.media-pick:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.media-pick-text { min-width: 0; }
.media-pick-text b, .media-pick-text small {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.comment.hidden-by-mod { opacity: .75; }
.comment-form { margin-top: 12px; }
.comment-form .row { display: flex; gap: 8px; align-items: flex-end; }
.comment-form textarea { min-height: var(--tap); resize: vertical; }
.comment-form.in-branch { margin: 4px 0 8px; }
.comment-reply-slot:empty { display: none; }
/* Пусто, беда и тишина сети под записью — одним и тем же видом, но короче
 * общего: полный экран состояния внутри карточки выглядел бы поломкой самой
 * карточки. */
.state.small { padding: 20px 12px; gap: 8px; }
.state.small p { font-size: var(--fs-sm); }

/* Плашка: короткое пояснение внутри карточки — «скрыто модерацией», «выйдет
 * позже», «нет связи». Пунктирная рамка, а не заливка: это примечание к
 * записи, а не вторая запись. */
.plate {
  display: flex; align-items: center; gap: 8px;
  margin-top: 8px; padding: 8px 12px;
  border: 1px dashed var(--line-strong); border-radius: calc(var(--radius) * .8);
  color: var(--muted); font-size: var(--fs-sm);
}
.plate.bad { border-color: color-mix(in srgb, var(--bad) 60%, var(--line-strong)); color: var(--bad); }

/* ── Полки избранного ────────────────────────────────────────────────────── */

.coll-list { display: grid; gap: 4px; }
.coll-row { display: flex; align-items: center; gap: 4px; }
.coll-pick {
  min-height: var(--tap); padding: 8px 12px;
  display: flex; align-items: center; gap: 12px; text-align: left;
  border: 1px solid transparent; border-radius: var(--radius);
  background: transparent; color: var(--text);
}
.coll-pick:hover { background: var(--surface2); }
.coll-row.on .coll-pick { border-color: var(--accent); }
.coll-pick small { display: block; color: var(--muted); font-size: var(--fs-sm); }
.coll-box .card-note { margin-top: 8px; }
/* Полки в «Избранном» — второй строкой отбора, под видами. Многоточие внутри
 * фишки открывает правку той же полки. */
.coll-chips .chip small { color: var(--muted); font-variant-numeric: tabular-nums; }
.chip-more { display: inline-flex; align-items: center; color: var(--muted); }
.chip-more:hover { color: var(--text); }

/* Кнопка внутри всплывающего сообщения: «отложено» и сразу «выбрать полку».
 * Полку выбирают в тот же миг, когда откладывают, а не через неделю. */
.toast-do {
  margin-left: 8px; padding: 2px 12px; min-height: 28px;
  border: 1px solid var(--line-strong); border-radius: var(--r-lg);
  background: transparent; color: var(--text); font-size: var(--fs-sm);
}

/* Перепост в окне «поделиться»: показываем, чем именно делимся. Карточка
 * внутри окна не должна жить своей жизнью — её действия здесь не нужны. */
.repost-preview {
  margin-top: 8px; padding: 12px 12px;
  border: 1px solid var(--line); border-radius: var(--radius);
  max-height: 40vh; overflow-y: auto;
}
.repost-preview .post-text { margin-top: 4px; }

.profile-card {
  max-width: 680px; margin: 14px auto;
  background: var(--surface); border-radius: calc(var(--radius) * 1.3); overflow: hidden;
}
.profile-cover { height: 140px; background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 26%, var(--surface2)), var(--surface2)); }
.profile-info { padding: 0 20px 20px; }
.profile-avatar { margin-top: -44px; border: 5px solid var(--surface); }
.profile-info h2 { margin: 8px 0 2px; font-size: var(--fs-lg); }
.profile-info p { margin-top: 8px; color: var(--muted); line-height: 1.45; }
.profile-stats { display: flex; gap: 24px; margin-top: 12px; flex-wrap: wrap; }
.profile-stats b, .profile-stats small { display: block; }
.profile-stats small { color: var(--muted); }

.card-row.fresh { background: color-mix(in srgb, var(--accent) 10%, transparent); }

.social-tabs { display: none; }

/* Сужение идёт в утверждённом порядке: сначала исчезает правая колонка,
 * потом сжимается до значков левая навигация, а ниже 900 остаётся одна
 * колонка с нижними вкладками. Ни на одном шаге ничего не наезжает. */
@media (max-width: 1180px) {
  .social, .social.no-side { grid-template-columns: var(--nav) minmax(0, 1fr); }
  .social-side { display: none; }
}
@media (max-width: 1000px) {
  .social, .social.no-side { grid-template-columns: 76px minmax(0, 1fr); }
  .social-nav-btn span, .social-me .grow, .social-me .ic, .social-space { display: none; }
  .social-nav-btn, .social-me { justify-content: center; padding: 0; }
  .social-brand { justify-content: center; padding: 0 0 20px; }
  .social-brand .mvq-mark { width: 38px; }
  .social-nav::after { display: none; }
}
@media (max-width: 900px) {
  .social, .social.no-side { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto; }
  .social-nav { display: none; }
  .social-tabs {
    display: grid; grid-template-columns: repeat(5, 1fr);
    padding: 4px 4px calc(4px + env(safe-area-inset-bottom));
    background: var(--surface); border-top: 1px solid var(--line);
  }
  .social-tabs button {
    position: relative; min-height: var(--tap-touch);
    display: grid; justify-items: center; align-content: center; gap: 4px;
    border: 0; background: transparent; color: var(--muted); font-size: 11px;
  }
  .social-tabs button.active { color: var(--accent); font-weight: 650; }
  .social-tabs button .dot {
    position: absolute; top: 2px; left: 54%;
    min-width: 16px; height: 16px; padding: 0 4px;
    display: grid; place-items: center; border-radius: var(--r-sm);
    background: var(--bad); color: #fff; font-size: 10px; font-weight: 700;
  }
  .social-head { padding: 0 12px; gap: 8px; }
  /* Узкой шапке с отборами заголовок не нужен: «Главная» уже написана на
   * нижней вкладке, а огрызок «Гл…» рядом с отборами — не заголовок. */
  .social-head:has(.chips) .head-title { display: none; }
  /* На 320 круглым кнопкам и полке отборов тесно: полке остаётся ~70 точек
   * (с «Эфиром» и того меньше), и даже «Для вас» видна только прокруткой.
   * Прячем «Создать публикацию» и «События» — у обеих есть вторая дверь,
   * одноимённые вкладки нижней навигации, которая на этой ширине есть
   * всегда. «Настройке ленты», «Поиску» и «Эфиру» второй двери нет, их не
   * трогаем: без них полка и так получает ≥110 точек. */
  @media (max-width: 360px) {
    .social-head .head-compose, .social-head .head-bell { display: none; }
  }
  .social-page { padding: 12px; }
  .post, .social-compose, .profile-card { margin: 10px auto; }
}

/* ── Телефон и узкое окно ────────────────────────────────────────────────────
 * Сначала прячется панель сведений, потом окно переходит к экранам по одному:
 * список, разговор, раздел. Ничего не наезжает и ничего не рисуется поверх. */

/* Что видно только в узком окне, а что только в широком. Кнопка «назад» на
 * компьютере не нужна: список и разговор стоят рядом и никуда не уходят. */
.only-narrow { display: none !important; }

@media (max-width: 1180px) {
  .app.with-info { grid-template-columns: var(--rail) var(--list) minmax(0, 1fr); }
  .app.with-info .info { display: none; }
  .only-wide { display: none !important; }
}

/* Средний размер: боковая колонка ужимается, разговор остаётся читаемым.
 * Три полноценные колонки на тысяче точек превращают разговор в щель — а
 * сжимать их «до нечитаемого» прямо запрещено заданием. */
@media (max-width: 1100px) {
  .app, .app.with-info { grid-template-columns: var(--rail) 280px minmax(0, 1fr); }
}

@media (max-width: 900px) {
  .app, .app.with-info { grid-template-columns: minmax(0, 1fr); }
  .app > .rail { display: none; }
  .only-narrow { display: grid !important; }
  .drawer { width: min(360px, 92vw); }
  .section { inset: 0; }
  .section-body { grid-template-columns: minmax(0, 1fr); }
  .app[data-narrow-view="list"] > .conv { display: none; }
  .app[data-narrow-view="conv"] > .list { display: none; }
  .app[data-narrow-view="conv"] > .conv { animation: screen-in var(--slow) var(--ease); }
  .section[data-narrow-view="main"] .section-nav { display: none; }
  .section[data-narrow-view="nav"] .section-main { display: none; }
  .messages { padding: 20px 12px; }
  .msg-line { max-width: 84%; }
  /* Тесная шапка разговора: зоны нажатия остаются 48, а место имени
   * отвоёвывается у промежутков и полей, а не у пальца. */
  .conv-head { padding: 0 4px 0 2px; gap: 2px; }
  .conv-head .grow { margin: 0 4px; }
  /* То же с полем ввода: на 320 точках экономятся отступы, а не зоны
   * нажатия. Приёмка размеров требует полю не меньше 120 точек. */
  .composer { padding: 8px calc(6px + env(safe-area-inset-left)) calc(8px + env(safe-area-inset-bottom)) calc(6px + env(safe-area-inset-right)); }
  .composer-line { gap: 4px; }
  /* Одна колонка — и вторая строка обязана быть прокручиваемым ящиком.
   *
   * Без `minmax(0, 1fr)` строка растёт по содержимому, `.auth-side` получает
   * свою natural-высоту и **вылезает за окно**: на 200 % основная кнопка
   * уходила за нижний край, и достать её было нечем. Проверено кадром.
   */
  .auth {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
  }
  /* На телефоне рассказ не прячется совсем, а ужимается в полосу сверху.
   *
   * Прежде он исчезал целиком, и мобильная дверь превращалась в форму на
   * пустом листе: ни знака, ни узора, ни ответа на вопрос «во что я вхожу».
   * Проверка так и записала — «выглядят как универсальные формы». Полоса
   * занимает высоту одной строки с надстрочником и отвечает на этот вопрос. */
  .auth-story {
    display: grid; grid-template-rows: auto;
    padding: 16px 20px 20px;
    gap: 4px;
  }
  /* На узком окне остаётся знак: полоса высотой в строку отвечает на вопрос
   * «во что я вхожу», а мотив на такой высоте не читается вовсе. */
  .auth-story .auth-mark { display: flex; }
  .auth-story::after { display: none; }
  .auth-line { display: none; }
  .auth-side { padding: 24px 20px; align-content: start; min-height: 0; overflow-y: auto; }
  .auth-card { padding: 20px 20px; margin-top: 16px; }
  /* Знак над формой — только если брендовой полосы нет вовсе. Здесь она
     осталась (со своим знаком), и второй знак прямо под первым читался как
     сбой вёрстки. Видно на масштабе 200 %, где окно переходит на эту
     раскладку при обычном размере. */
  .auth-brand { display: none; }
}

/* Нижняя навигация — только на телефоне: на компьютере её роль играет меню. */
.tabbar { display: none; }
@media (max-width: 900px) {
  .app.phone { grid-template-rows: minmax(0, 1fr) auto; grid-template-columns: minmax(0, 1fr); }
  .app.phone > .tabbar {
    display: grid; grid-template-columns: repeat(4, 1fr);
    padding: 6px 4px calc(6px + env(safe-area-inset-bottom));
    background: var(--surface); border-top: 1px solid var(--line);
  }
  .app.phone > .tabbar button {
    display: grid; justify-items: center; gap: 4px;
    border: 0; background: transparent; color: var(--muted);
    font-size: 10px; padding: 4px 2px;
  }
  .app.phone > .tabbar button.active { color: var(--accent); font-weight: 650; }
  /* Открытый разговор прячет нижнюю навигацию: экран разговора — это экран
   * разговора, а не он же с полосой поверх. Разделы её не прячут: они и есть
   * вкладки, и уходить из них человек должен той же полосой. */
  .app.phone[data-narrow-view="conv"] > .tabbar { display: none; }
  /* Раздел на телефоне встаёт в сетку рядом с нижней полосой, а не поверх неё:
   * так его нижние строки не оказываются под навигацией. */
  .app.phone.section-open > .section {
    position: static; grid-row: 1; grid-column: 1; min-height: 0;
  }
  .app.phone.section-open > .list,
  .app.phone.section-open > .conv { display: none; }
}

/* Вложения: картинка занимает ячейку целиком, а не лежит рядом со значком. */
.media-cell.shot { padding: 0; overflow: hidden; position: relative; display: block; cursor: zoom-in; }
.media-cell.shot img { display: block; width: 100%; height: 100%; max-height: 420px; object-fit: cover; }
.media-cell.play { padding: 0; overflow: hidden; display: block; }
.media-cell.play video { display: block; width: 100%; max-height: 420px; background: #000; }
.media-cell.play audio { display: block; width: 100%; padding: 12px; }
.media-note {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 8px 12px;
  font-size: var(--fs-sm); color: #fff; text-align: left;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.62));
}
a.media-cell { text-decoration: none; }

/* Полка историй. Её нет вовсе, когда историй нет: пустой ряд серых кружков
   занимает место и обещает то, чего не случилось. */
/* Полка историй.
 *
 * На компьютере — высокие карточки с обложкой и лицом поверх; на телефоне —
 * компактный ряд кружков. Разметка одна, разница только в оформлении: два
 * набора узлов однажды разошлись бы. */
.stories {
  display: flex; gap: 12px; overflow-x: auto; padding: 12px 2px 4px;
  max-width: var(--feed); margin: 0 auto;
}
/* Прилипания к плиткам здесь нет нарочно: `scroll-snap-type` уводил полку на
 * первую историю, и плитка «Создать историю» уезжала за левый край — у неё
 * не было своей точки прилипания. Поймано на 390 точках. */
.story-dot {
  position: relative; flex: 0 0 auto;
  width: 104px; height: 148px; padding: 0;
  display: grid; justify-items: center; align-content: center; gap: 8px;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface2); color: var(--text);
  cursor: pointer; overflow: hidden;
}
.story-cover { position: absolute; inset: 0; }
.story-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.story-cover::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(rgba(8, 16, 22, .15) 40%, rgba(8, 16, 22, .72));
}
.story-dot .ring {
  position: relative; z-index: 1;
  display: grid; place-items: center; width: 62px; height: 62px; border-radius: 50%;
  background: var(--accent); padding: 4px;
}
.story-dot.seen .ring { background: var(--line-strong); }
.story-dot .avatar { width: 56px; height: 56px; border: 2px solid var(--surface); }
.story-dot small {
  position: relative; z-index: 1; max-width: 92px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--fs-sm); font-weight: 600;
}
.story-dot:not(.plain) small { color: #F3EFE7; text-shadow: 0 1px 3px rgba(0, 0, 0, .55); }
.story-dot.plain small { color: var(--muted); }
/* Первая плитка — «создать историю»: пустая карточка с тканым узором. */
.story-add {
  position: relative; flex: 0 0 auto;
  width: 104px; height: 148px; padding: 0;
  display: grid; justify-items: center; align-content: center; gap: 8px;
  border: 1px dashed var(--line-strong); border-radius: var(--r-lg);
  background: var(--surface2); color: var(--text);
  cursor: pointer; overflow: hidden;
}
.story-add .plus {
  width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--accent); color: var(--accentText);
}
.story-add small { position: relative; z-index: 1; color: var(--muted); font-size: var(--fs-sm); text-align: center; line-height: 1.25; }
.story-add:hover { border-color: var(--accent); }

/* Телефон: кружки, а не карточки. Обложке на 390 точках места нет. */
@media (max-width: 900px) {
  .stories { gap: 16px; padding: 12px 12px 8px; }
  .story-dot, .story-add {
    width: 74px; height: auto; padding: 0;
    border: 0; background: transparent; border-radius: 0;
  }
  .story-cover { display: none; }
  .story-dot small, .story-add small { color: var(--muted); text-shadow: none; max-width: 74px; }
  .story-add .plus { width: 62px; height: 62px; background: var(--surface2); color: var(--text); }
}

.story-view { display: grid; gap: 12px; }
.story-head { display: grid; gap: 8px; }
.story-ticks { display: flex; gap: 4px; }
.story-ticks span {
  height: 3px; flex: 1; border-radius: 2px; background: var(--line-strong);
}
.story-ticks span.on { background: var(--accent); }
.story-frame { min-height: 220px; display: grid; gap: 12px; align-content: start; }
.story-text { font-size: calc(var(--fs-md) * 1.15); line-height: 1.45; }
.story-nav { display: flex; gap: 8px; }

/* Точка «в эфире». Мигает — но уважает просьбу не двигать картинку. */
.live-dot {
  width: 10px; height: 10px; border-radius: 50%; background: var(--bad);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--bad) 24%, transparent);
  animation: live-pulse 1.6s ease-in-out infinite;
}
.live-dot.off { background: var(--muted); box-shadow: none; animation: none; }
@keyframes live-pulse { 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) { .live-dot { animation: none; } }

/* Личные сообщения ленты.
 *
 * Свои — справа и цветом ленты, чужие — слева: это единственный признак, по
 * которому переписку читают, не вчитываясь. Внутренняя прокрутка своя, чтобы
 * поле ввода оставалось на месте, когда разговор длинный. */
.dm-list {
  display: flex; flex-direction: column; gap: 12px;
  max-height: 52vh; overflow-y: auto;
  padding: 12px 2px; margin: 8px 0;
}
.dm { max-width: 78%; align-self: flex-start; }
.dm-mine { align-self: flex-end; }
.dm-text {
  padding: 8px 12px; border-radius: var(--radius);
  background: var(--surface2); color: var(--text);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.dm-mine .dm-text {
  background: color-mix(in srgb, var(--accent) 22%, var(--surface2));
}
.dm-when { display: flex; gap: 8px; align-items: center; margin-top: 4px; }
.dm-mine .dm-when { justify-content: flex-end; }
.dm-send { display: flex; gap: 12px; align-items: flex-end; }
.dm-send .field { flex: 1; min-width: 0; }
.col-end { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }

/* Полоса «нет связи» над сохранённым видом ленты.
 *
 * Полоса, а не пустой экран с надписью: обрыв сети не завершает сеанс, и
 * человек должен видеть то, что уже загружено, а не «всё пропало». Цвет
 * предупреждающий, но не тревожный: ничего не сломалось, связь вернётся сама. */
.social-offline {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 12px;
  padding: 12px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface2);
  color: var(--muted);
  font-size: 13px;
}
.social-offline .link-btn { flex: 0 0 auto; }

/* Дверь соцсети: стандартная регистрация (26.08.2026). */
.auth.solo { grid-template-columns: 1fr; }
.auth.solo .auth-story { display: none; }
.auth.solo .auth-card { max-width: 560px; margin-inline: auto; }

/* Шаги: слева узкая полоса продукта, справа — форма во всю оставшуюся ширину.
 *
 * Полоса несёт знак, надстрочник и название — и ничего больше: обещания на
 * шаге повторять нечего, а вот на вопрос «куда я ввожу пароль» экран обязан
 * отвечать всё время, а не только на первом шаге. */
.auth.steps { grid-template-columns: minmax(200px, 300px) minmax(0, 1fr); }
.auth.steps .auth-story { padding: 40px 28px; }
.auth.steps .auth-card { width: min(560px, 100%); }
/* Имя и фамилия рядом, когда есть куда: две короткие строки друг под другом
 * растягивают шаг вниз без всякой нужды. */
.auth-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; }
@media (max-width: 620px) { .auth-pair { grid-template-columns: 1fr; } }
.auth-tabs { display: flex; gap: 4px; background: var(--field); border-radius: var(--r-md);
  padding: 4px; margin-bottom: 12px; }
.auth-tab { flex: 1; min-height: 42px; border: 0; border-radius: calc(var(--r-md) - 3px);
  background: transparent; color: var(--muted); font: inherit; cursor: pointer; }
.auth-tab.on { background: var(--selected); color: var(--text); }
.pass-meter { display: flex; gap: 4px; margin: 8px 0 4px; }
.pass-meter span { height: 4px; flex: 1; border-radius: 2px; background: var(--line); }
.pass-meter span.good { background: var(--good); }
.pass-meter span.weak { background: var(--warn); }
.auth-ways { display: grid; gap: 8px; margin-bottom: 12px; }
.auth-way { display: flex; align-items: center; gap: 12px; border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 12px 16px; cursor: pointer; min-height: 46px; }
.auth-way.on { border-color: var(--accent); }
.auth-note { border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: 12px 16px; margin: 4px 0 8px; display: grid; gap: 4px; }
.row-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.dm-request { border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: 12px 12px; margin: 8px 0; display: grid; gap: 8px; }

/* ── Своя строка окна ────────────────────────────────────────────────────────

   Разметки у неё нет: строку рисует само окно (см. shell_windows.go), потому
   что дочернее окно WebView2 забирает мышь и нарисованные здесь кнопки не
   работали бы. Странице остаётся не залезать под строку — этим и занят отступ.

   Отступ ставится телу при box-sizing: border-box, тогда экраны внутри
   по-прежнему считают высоту от 100 % и ни один не приходится править.

   ⚠️ Полотно окна уже начинается ПОД строкой, поэтому отступ здесь нулевой:
   он оставлен точкой правки на случай, если строка когда-нибудь переедет
   внутрь страницы. Класс `native` при этом сохраняется — по нему отличают
   своё окно от вкладки браузера. */
body.native { padding-top: 0; box-sizing: border-box; }

/* ── Системная полоса ────────────────────────────────────────────────────────

   Спокойная сводка над списком разговоров: что со связью и что с этим делать.
   Не красная и не во всю высоту — беда со связью не отнимает у человека ни
   слова, написанное уходит само, и пугать этим нечестно. Красным в окне
   остаётся только то, что человек потеряет.

   Раскладка — столбиком, а не строкой. Колонка списка узкая (336), а кнопки
   не переносятся по словам: поставленные в один ряд с текстом, они отжимали
   его до одного слова в строке и наезжали сверху. Проверено кадром 28.08.2026.
   Поэтому текст занимает всю ширину, а кнопки стоят под ним справа. */
.syspanel {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas: "dot text" "actions actions";
  align-items: start;
  column-gap: 12px; row-gap: 8px;
  padding: 12px 16px 12px;
  background: var(--surface2);
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-sm);
}
.syspanel-dot {
  grid-area: dot;
  width: 8px; height: 8px; margin-top: 4px; border-radius: 50%;
  background: var(--warn);
}
.syspanel .grow { grid-area: text; min-width: 0; }
.syspanel.wait .syspanel-dot { animation: pulse 1.6s ease-in-out infinite; }
.syspanel-text { font-weight: 600; }
.syspanel-note { color: var(--muted); font-size: var(--fs-xs); }
.syspanel-actions {
  grid-area: actions;
  display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end;
}
/* Кнопки в узкой колонке: по 336 точкам две подписи в один ряд помещаются
   впритык, поэтому ряд занимает всю ширину полосы, а не только колонку текста. */
.syspanel-actions .btn { padding: 0 12px; }

@keyframes pulse { 0%, 100% { opacity: 1 } 50% { opacity: .35 } }

/* ── Узкое окно: вход в одну колонку ─────────────────────────────────────────

   Это повторение правил из медиазапроса выше — и оно необходимо.

   `.auth.steps` и `.auth.solo` объявлены ПОСЛЕ того медиазапроса и весом
   выше (два класса против одного), поэтому в узком окне они молча возвращали
   две колонки. Экран пароля как раз «шаг» — на 150 % и 200 % он оставался
   двухколонным, карточка не помещалась, и основная кнопка уходила за нижний
   край. Видно на кадрах, приложенных к заданию.

   Поэтому: правила для узкого окна стоят последними в файле и перекрывают всё,
   что объявлено до них. */
@media (max-width: 900px) {
  .auth, .auth.steps {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
  }
  /* Бренд-панель ужимается в полосу со знаком: крупная композиция на высоте в
     одну строку не читается, а место отнимает. */
  .auth.steps .auth-story,
  .auth .auth-story {
    display: grid; grid-template-rows: auto;
    padding: 12px 16px;
    min-height: 0;
  }
  .auth .auth-story::after { display: none; }
  .auth .auth-mark .mvq-mark { height: 28px; }
  /* Средняя часть — единственный прокручиваемый ящик экрана. */
  .auth .auth-side, .auth.steps .auth-side {
    align-content: start;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 16px 16px 24px;
  }
  .auth .auth-card { width: 100%; max-width: 560px; margin: 0 auto; }
}

/* Совсем тесно: убираем и полосу, оставляя знак над формой. Так на 800×600
   при 200 % заголовок, поле и основная кнопка помещаются без прокрутки. */
@media (max-width: 900px) and (max-height: 560px) {
  .auth .auth-story, .auth.steps .auth-story { display: none; }
  .auth, .auth.steps { grid-template-rows: minmax(0, 1fr); }
  .auth .auth-brand { display: flex; }
}

/* ── Низкое окно: заголовок, поле и главная кнопка обязаны помещаться ────────

   На 200 % окно по умолчанию даёт примерно 360 логических точек высоты. Туда
   не влезают знак, заголовок, объяснение, подпись поля, само поле и кнопка —
   и кнопка «Открыть» оказывалась наполовину за нижним краем. Прокрутка её
   доставала, но человек, впервые открывший программу, видит обрезанную кнопку
   и решает, что она сломана.

   Поэтому в тесноте по высоте убирается то, без чего вход работает:
   объяснение под заголовком и лишние отступы. Заголовок, поле и кнопка
   остаются на месте. */
@media (max-height: 560px) {
  .auth .auth-card { padding: 16px 20px; margin-top: 0; }
  .auth .auth-card > p.lead { display: none; }
  .auth .auth-card h1 { font-size: var(--fs-lg); }
  .auth .auth-brand { margin-bottom: 12px; }
  .auth .auth-brand .mvq-mark { height: 26px; }
  .auth-side { padding-top: 12px; padding-bottom: 16px; }
}

/* ── Аккаунт ограничен решением модерации ────────────────────────────────────
 *
 * Единственный экран, который человек читает целиком и подряд, — поэтому он и
 * свёрстан как документ, а не как лента: одна колонка, ограниченная ширина,
 * заметные заголовки, таблица «поле — значение» посередине.
 *
 * Действия стоят **до** истории решений: сначала выход, потом объяснения.
 * Наоборот было бы честно по хронологии и жестоко по существу — человек ищет
 * глазами кнопку, а не читает журнал. */
.blocked-screen {
  max-width: 640px; margin: 0 auto;
  padding: 40px 24px 56px;
  display: grid; gap: 24px;
}
.block-head { display: grid; justify-items: start; gap: 12px; }
.block-head .ic { color: var(--warn); }
.block-head h2 { font-size: var(--fs-lg); }
.block-head .lead { color: var(--muted); line-height: 1.5; }
.block-rows {
  display: grid; gap: 1px;
  background: var(--line); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden;
}
.block-row {
  display: grid; grid-template-columns: minmax(0, 34%) minmax(0, 1fr); gap: 12px;
  padding: 12px 16px; background: var(--surface);
}
.block-name { color: var(--muted); font-size: var(--fs-sm); }
.block-value { line-height: 1.45; overflow-wrap: anywhere; }
.block-appeal {
  padding: 16px 16px; border-radius: var(--radius);
  background: var(--surface2); border: 1px solid var(--line);
  display: grid; gap: 8px; line-height: 1.45;
}
.block-actions { display: flex; flex-wrap: wrap; gap: 12px; }
.block-history { display: grid; gap: 12px; }
.block-history h3 { font-size: var(--fs-md); }
.block-event {
  display: flex; gap: 12px; align-items: baseline;
  padding: 12px 0; border-top: 1px solid var(--line);
}
.block-when { color: var(--muted); font-size: var(--fs-xs); white-space: nowrap; }
@media (max-width: 560px) {
  .blocked-screen { padding: 24px 16px 40px; }
  /* На узком экране пара «поле — значение» разворачивается в столбец: две
   * колонки на 320 точках дают по восемь знаков в каждой. */
  .block-row { grid-template-columns: 1fr; gap: 2px; }
  .block-event { flex-direction: column; gap: 4px; }
}

/* ── Конфликт синхронизации ──────────────────────────────────────────────────
 *
 * Две версии рядом, обе выбираемые нажатием. Карточка — кнопка, а не радио с
 * подписью: выбирают здесь не «вариант», а текст, и нажимать хочется по
 * тексту. Выбранная помечена рамкой и `aria-pressed`, а не одним цветом:
 * статус, переданный только цветом, для части людей не передан вовсе. */
.conflict-sides { display: grid; gap: 12px; }
.conflict-side {
  display: grid; gap: 4px; text-align: left;
  padding: 12px 16px; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--surface);
  color: var(--text); cursor: pointer;
}
.conflict-side.on { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.conflict-side.on b::after { content: " ✓"; color: var(--accent); }
.conflict-peek {
  margin-top: 4px; color: var(--muted); font-size: var(--fs-sm);
  line-height: 1.4; max-height: 4.2em; overflow: hidden;
}
.conflict-diff { max-height: 52vh; overflow: auto; }
.conflict-line {
  display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
  padding: 4px 8px; font-size: var(--fs-sm); line-height: 1.45;
}
.conflict-line.head { font-weight: 600; border-bottom: 1px solid var(--line); }
/* Расхождение помечено и заливкой, и полосой слева: одной заливки мало тому,
 * кто не различает её оттенок. */
.conflict-line.differs { background: var(--surface2); border-left: 3px solid var(--warn); }
.conflict-cell { white-space: pre-wrap; overflow-wrap: anywhere; }
@media (max-width: 560px) {
  .conflict-line { grid-template-columns: 1fr; gap: 2px; }
}

/* ── Дверь: приглашение, сеть, аккаунт ───────────────────────────────────────
 *
 * Добавлено потоком web/PWA (01.09.2026). Три вещи, которых на двери не было:
 * кто позвал, есть ли связь и куда идти за своими устройствами. Блок стоит в
 * конце файла целиком, а не вкраплён в разделы выше, — так его проще и внести,
 * и снять, пока правки ленты идут в несколько рук.
 */

/* Приглашение — первым, до заголовка. Не плашка-объявление: приглушённая
 * подложка и цветная полоса слева. Человек, нажавший на друга, должен увидеть
 * друга, а не рекламу продукта. */
.auth-invite {
  display: grid; gap: 4px;
  margin: 0 0 16px;
  padding: 12px 16px;
  border-radius: var(--r-md);
  border-left: 3px solid var(--accent);
  background: var(--surface2, var(--field));
  line-height: 1.45;
}
.auth-invite b { font-size: var(--fs); }
.auth-invite .small { color: var(--muted); }

/* Выбор языка на двери ленты (только веб): те же капсулы, что в настройках,
   внизу карточки — до входа человек ещё ничего не вводил и ничего не теряет. */
.auth-lang { margin-top: 16px; }

/* Нет сети. Тон предупреждения, а не отказа: дверь читается и без связи,
 * а вот завести аккаунт — нет, и об этом сказано прямо. */
.auth-offline {
  margin: 0 0 16px;
  padding: 12px 12px;
  border-radius: var(--r-md);
  border: 1px solid var(--warn);
  color: var(--text);
  font-size: var(--fs-sm); line-height: 1.45;
}
.auth-offline[hidden] { display: none; }

/* Аккаунт и доступ — обычная страница ленты из карточек; своей сетки не
 * заводит. Здесь только запас снизу: последняя карточка не должна упираться
 * в нижнюю полку навигации на телефоне. */
.account-page { padding-bottom: 24px; }

/* ── Страница человека ───────────────────────────────────────────────────────
 *
 * Обложка, лицо, ссылки, три вкладки и сетка снимков. До этого страница
 * человека состояла из одной карточки: обложка была прямоугольником с
 * заливкой, ссылки сервер хранил, но показать их было негде, а вкладок не
 * существовало вовсе.
 *
 * Ширина везде та же, что у ленты (680): страница человека и его записи стоят
 * в одной колонке, и разъехавшиеся края читаются как две разные страницы.
 */

.profile-screen { padding-bottom: 24px; }

/* Обложка: своя картинка поверх заливки. Заливка остаётся под ней — пока
 * картинка грузится, на её месте не должно быть дырки. */
.profile-cover.has-image { position: relative; padding: 0; }
.profile-cover.has-image img {
  display: block;
  width: 100%; height: 100%;
  object-fit: cover;
}

/* Имя и знак подлинности в одной строке: знак относится к имени, а не к
 * странице, и на второй строке он читался бы как отдельное сообщение. */
.profile-name { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.profile-name h2 { margin: 8px 0 2px; font-size: var(--fs-lg); }

.profile-at { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.profile-at .link-btn { display: inline-flex; align-items: center; gap: 4px; }

/* Профессиональная подпись — не описание: она короткая, стоит сразу под
 * именем и набрана иначе, чтобы их не путали. */
.profile-role { margin-top: 4px; color: var(--text); font-weight: 600; }
.profile-bio { margin-top: 8px; color: var(--muted); line-height: 1.45; white-space: pre-wrap; }

/* Ссылки: список, а не строчки в описании. Каждая нажимается целиком —
 * попадать пальцем в подчёркнутое слово среди текста на телефоне нельзя. */
.profile-links { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.profile-link {
  display: flex; align-items: center; gap: 8px;
  min-height: var(--tap); padding: 8px 12px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface2); color: var(--accent);
  text-decoration: none; max-width: 100%;
}
.profile-link:hover { border-color: var(--accent); }
.profile-link .clip { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Кнопки отношения. Переносятся строками и не сжимаются до нечитаемого:
 * «Подписаться» и «Предложить дружбу» — разные действия, и обрезанные до
 * «Подпис…» они превращаются в лотерею. */
.profile-actions { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
.profile-actions .btn { flex: 0 1 auto; }

/* ── Вкладки страницы ─────────────────────────────────────────────────────── */

.profile-tabs {
  display: flex; gap: 4px;
  max-width: 680px; margin: 14px auto 0;
  padding: 4px;
  border-radius: var(--radius);
  background: var(--surface);
}
.profile-tab {
  flex: 1;
  min-height: var(--tap);
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border: 0; border-radius: calc(var(--radius) - 2px);
  background: transparent; color: var(--muted);
  font-size: var(--fs-sm);
}
.profile-tab:hover { color: var(--text); }
/* Выбранное отличается не только цветом: цвет один плохо читается при
 * дальтонизме и на выцветшем экране под солнцем. */
.profile-tab.active {
  background: var(--surface2); color: var(--text); font-weight: 650;
  box-shadow: inset 0 -2px 0 var(--accent);
}

/* ── Сетка снимков ───────────────────────────────────────────────────────── */

.media-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 4px;
  max-width: 680px; margin: 14px auto;
}
.media-tile {
  position: relative;
  aspect-ratio: 1 / 1;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  border: 0; border-radius: var(--r-sm);
  background: var(--surface2); color: var(--muted);
  padding: 0;
}
.media-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.media-tile:hover img { opacity: .9; }
.media-tile.is-video { background: #000; color: #fff; }

/* Дата в углу: снимок без времени теряет половину смысла, а подпись под ним
 * растянула бы сетку вдвое. */
.media-tile-when {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 12px 8px 4px;
  font-size: var(--fs-xs); color: #fff; text-align: right;
  background: linear-gradient(transparent, rgb(0 0 0 / .55));
  pointer-events: none;
}

/* Та же сетка в окне выбора: там дат нет — выбирают глазами, а не по времени. */
.media-grid.pick { margin: 8px 0; max-height: 46vh; overflow-y: auto; }

/* ── Встречи на странице человека ─────────────────────────────────────────── */

.event-row {
  display: flex; align-items: center; gap: 12px; width: 100%;
  max-width: 680px; margin: 10px auto;
  padding: 12px; text-align: left;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); color: var(--text);
}
.event-row:hover { border-color: var(--accent); }
/* Прошедшее не прячется и не вычёркивается: оно было. Приглушается — чтобы
 * не спорить взглядом с тем, на что ещё можно прийти. */
.event-row.past { opacity: .62; }
.event-day {
  flex: none; width: 52px;
  display: grid; justify-items: center; align-content: center; gap: 2px;
  padding: 8px 0; border-radius: var(--r-sm);
  background: var(--surface2);
}
.event-day b { font-size: var(--fs-lg); line-height: 1; }
.event-day small { color: var(--muted); text-transform: lowercase; }
.event-row .grow b, .event-row .grow small { display: block; }
.event-row .grow small { color: var(--muted); }
.event-row .grow small.bad { color: var(--bad); }

/* ── Правка своей страницы ───────────────────────────────────────────────── */

/* Обложка и лицо рядом и в тех же пропорциях, что на самой странице: правка,
 * показывающая другое соотношение сторон, обещает не то, что выйдет. */
.edit-media { display: flex; gap: 12px; align-items: flex-start; margin-top: 8px; }
.edit-media > div:first-child { flex: 1; min-width: 0; }
.edit-cover {
  height: 96px; border-radius: var(--radius); overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface2); border: 1px solid var(--line);
}
.edit-cover img { width: 100%; height: 100%; object-fit: cover; }
.edit-face {
  flex: none; width: 88px; height: 88px;
  border-radius: 50%; overflow: hidden;
  display: flex; align-items: center; justify-content: center; text-align: center;
  background: var(--surface2); border: 1px solid var(--line);
  padding: 8px;
}
.edit-face img { width: 100%; height: 100%; object-fit: cover; padding: 0; }

.edit-links .field { flex: 1; min-width: 0; }

/* Строка настройки, у которой ответы не помещаются рядом с вопросом. */
.card-row.wrap { flex-wrap: wrap; row-gap: 8px; }

@media (max-width: 900px) {
  .profile-tabs { margin: 10px auto 0; border-radius: 0; }
  /* Подпись у вкладки остаётся: их три, и на 320 точках они помещаются. */
  .media-grid { gap: 2px; margin: 10px auto; }
  .edit-media { flex-direction: column-reverse; align-items: center; }
  .edit-media > div:first-child { width: 100%; }
}

/* Приложение на телефоне: боковые поля выреза для того, что рисуется во всю
 * ширину. Нижнего поля здесь нет — его держит полка навигации. */
@media (display-mode: standalone) {
  .profile-tabs, .media-grid {
    padding-left: env(safe-area-inset-left, 0px);
    padding-right: env(safe-area-inset-right, 0px);
  }
}

/* ── Телефон: две грабли, снятые с живого iPhone 05.09.2026 ─────────────────

   1. Строка «Послушать» + четыре кнопки: `.row-actions` переносит кнопки в
      столбик и оставляет `.grow` полоску в одну букву — подпись стояла
      вертикально. На узком экране кнопки уходят второй строкой на всю ширину.
   2. Safari увеличивает страницу при фокусе в поле с буквами меньше 16px и
      не возвращает масштаб — экран пароля уезжал вправо. На устройствах с
      пальцем полям даётся 16px, и зум не срабатывает вовсе. */
@media (max-width: 700px) {
  .card-row:has(> .row-actions) { flex-wrap: wrap; }
  .card-row > .row-actions { flex-basis: 100%; padding-left: 36px; }
}
@media (pointer: coarse) {
  .field, input.field, textarea.field, select.field,
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]), textarea, select {
    font-size: max(16px, 1em);
  }
}

/* Телефонное меню раздела — карточка строк, а не столбик названий. */
.section-nav.section-nav-phone { padding: 8px 16px 16px; border-right: 0; }
.section-nav.section-nav-phone .card-row { min-height: 60px; }
.section-nav.section-nav-phone .card-row .ic { color: var(--accent); }

/* Метка «демо»: демонстрационные люди и их записи не выдаются за живых (05.09.2026). */
.demo-tag {
  display: inline-block;
  padding: 0 8px;
  border-radius: var(--r-sm);
  background: var(--surface2);
  color: var(--muted);
  font-size: var(--fs-xs);
  font-weight: 600;
  line-height: 16px;
  text-transform: lowercase;
  letter-spacing: .02em;
  white-space: nowrap;
}
.avatar.demo { position: relative; }
.avatar.demo::after {
  content: attr(data-demo);
  position: absolute; left: 0; right: 0; bottom: 0;
  font-size: 8px; line-height: 12px; font-weight: 700;
  text-align: center; text-transform: lowercase;
  background: rgba(17, 20, 23, .72);
  color: #FFFFFF;
  pointer-events: none;
}
