/* addons.css — стилевые надстройки поверх готовых css/*.css.
   Тот же приём и та же причина, что у js-надстроек (js/rxfx.js, js/mediasave.js): components.css
   весит 94 КБ, а патч несёт файл целиком, поэтому точечная правка едет отдельным маленьким файлом.
   Подключается списком в index.php в КОНЕЦ <head> — после стилей chat.html, значит при равной силе
   селектора выигрывает он. Когда components.css будет правиться по другому поводу, эти правила
   стоит внести туда, а файл убрать. */

/* ================= #150: грязные углы под изображениями ================= */
/* ПРОВЕРЕНО НА ЖИВОМ КЛИЕНТЕ 29.07.2026 — углы чистые, задумка закрыта. Правила не снимать «как
   лишнюю страховку»: эту задумку уже однажды закрыли по неверному диагнозу, и она вернулась.

   Прошлый диагноз («суперэллипс рамки полнее круглой обрезки блюр-слоя») к ленте не применим:
   layout.css держит `#messages *{corner-shape:round}`, суперэллипс внутри ленты остался только у
   .msgbubble и .deskavatar. Поэтому та правка (background:transparent + border-radius:inherit) и
   не помогла — форма кадра и так круглая, а обрезка так и осталась делом overflow:hidden.

   Живой диагноз. Внутри .media-frame живут КОМПОЗИТНЫЕ слои: .media-blur (filter:blur(14px) +
   transform:scale(1.15)) и три плашки .media-dur/.media-size/.media-time с backdrop-filter.
   Скруглённая обрезка родителя (overflow:hidden + border-radius) для такого слоя — самое хрупкое
   место движка: backdrop-filter обязан взять «фон под собой», а фон лежит ВНЕ обрезки, и
   Chromium/WebKit в этом месте роняют круглый клип на прямоугольный. Итог на экране: у кадра
   вылезают прямые углы — как раз «грязные углы», и заметнее всего снизу, где эти плашки и стоят
   (время справа, размер слева).

   Лечим двумя независимыми ходами — чтобы починилось, даже если у движка причина своя.

   1) Геометрическая гарантия: clip-path. В отличие от overflow:hidden это НАСТОЯЩАЯ обрезка формы,
   которая применяется ко всему поддереву, включая композитные слои и backdrop-filter. Радиусы
   повторяют border-radius кадра один в один, включая «хвостик» 6px у своего/чужого угла и
   обнулённый низ при сшивке с подписью (#151) — иначе clip-path скруглил бы то, что должно быть
   прямым, и между кадром и подписью открылась бы щель. */
.media-frame{clip-path:inset(0 round 16px)}
.mmine .media-frame:not(.round){clip-path:inset(0 round 16px 16px 6px 16px)}
.mthem .media-frame:not(.round){clip-path:inset(0 round 16px 16px 16px 6px)}
/* Сшивка с подписью идёт ПОСЛЕ правил стороны: сила селекторов равна, и должен победить низ «в
   ноль» — у кадра с подписью нижних углов нет вовсе, их несёт подпись. */
.mcontent > .media-frame:not(.round):has(+ .media-caption){clip-path:inset(0 round 16px 16px 0 0)}
.media-frame.round{clip-path:inset(0 round 50%)}
/* Клетки коллажа квадратные (у них border-radius:0!important) — обрезать нечего, и !important
   здесь по той же причине, что у соседей: правило стороны выше по силе. */
.album-cell .media-frame{clip-path:none!important}

/* 2) Убираем сам источник промоушена. Плашки времени, размера и длительности размывают под собой
   фотографию — то есть ничего: под ними всегда кадр, а не обои и не текст. Пользы от blur(6px) там
   нет, а цена — композитный слой в каждом кадре ленты (десятки на экран) и тот самый сорванный
   клип. Взамен плашка чуть плотнее (.42 → .5), чтобы белые цифры читались так же уверенно. */
.media-dur,
.media-size,
.media-time{-webkit-backdrop-filter:none;backdrop-filter:none;background:rgba(0,0,0,.5)}


/* ================= Контекстное меню: ровный столбец пунктов ================= */
/* «Сохранить изображение» уезжало на вторую строку: ширина .ctx-items прибита к 216px, а перенос
   рвал ровный столбец — пункт становился вдвое выше соседей. Лечим с двух сторон: подпись стала
   короткой («Сохранить», вид медиа ушёл в подсказку — см. js/mediasave.js), а сам список больше не
   переносит строки и растёт по содержимому. 216px остались нижней границей, чтобы узкое меню не
   схлопывалось в столбик из трёх букв; верхняя — экран минус поля. Многоточие — страховка на
   случай очень длинной подписи: лучше обрезать хвост, чем сломать ряд. */
.ctx-items{width:max-content;min-width:216px;max-width:min(320px,calc(100vw - 24px))}
.ctx-item{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ================= Реакции на фото, видео и кружках ================= */
/* Палитру над кадром открывает js/mediarx.js, а пилюлям нужно место: под кадром, в том же
   .mcontent. Строка без своего фона — фон несут сами пилюли, потому что под ними обои, а не
   пузырь: и белая rx-out (.22 прозрачности), и accent-soft на пёстрых обоях читались бы никак.
   Своим пилюли жмутся вправо, чужие — влево, как и сами кадры. */
.rx-host{display:flex;flex-wrap:wrap;gap:4px;margin-top:4px}
.mmine .rx-host{justify-content:flex-end}
.rx-host .reaction.rx-in,
.rx-host .reaction.rx-out{background:var(--panel2);border:1px solid var(--border);color:var(--text);
  -webkit-backdrop-filter:blur(var(--blur));backdrop-filter:blur(var(--blur))}

/* Кадру на один вызов подвешивается класс msgbubble (эффект удара ищет пузырь). Здесь гасим всё,
   что пузырь принёс бы с собой: подстановка не должна двигать ни пикселя, иначе всплеск улетел бы
   мимо центра кадра. */
.media-frame.msgbubble{background:none!important;border:0!important;box-shadow:none!important;
  padding:0!important;margin:0!important;max-width:none!important;min-width:0!important}


/* ================= Список каналов: нижняя навигация на своём месте ================= */
/* #sidebar — колонка-флекс, и низ ей держит #conv-list с flex:1: список забирает остаток высоты,
   а #mobnav прижимается к краю экрана. У #channel-list своего правила не было вовсе — в папке
   «Каналы» колонка переставала заполняться, и навигация всплывала под коротким списком, посреди
   экрана. Даём каналам ровно то же правило, что диалогам. Заодно чинится и десктоп: длинный
   список каналов теперь прокручивается внутри колонки, а не растягивает её.
   Пустые состояния — той же меркой: подсказка занимает остаток, навигация остаётся внизу. */
#channel-list{flex:1;overflow-y:auto;padding:6px}
#channel-empty,
#list-empty,
#folder-empty{flex:1}

/* ================= #193: общий подвальчик сообщения ================= */
/* Реакции переехали в .mmeta — в ту же строку, где время и галочки (applyReactions в
   js/feat-react.js). Здесь только раскладка этой строки.
   order:-1 вместо «вставить первым»: отметку «изменено» живая правка бота дописывает первым
   ребёнком .mmeta (patchMessageRow), и порядок вставки разошёлся бы у двух путей отрисовки одного
   и того же ряда. Порядком занимается флекс — тогда путь безразличен.
   margin-right:auto прижимает пилюли влево, а время с галочками остаются справа: у самой .mmeta
   стоит justify-content:flex-end, и без этого «авто» весь подвал уехал бы вправо одной кучей.
   margin:0 гасит прежний отступ бара сверху — строкой над временем он больше не стоит.
   flex-wrap: пилюль может быть больше, чем влезает; тогда они переносятся, а время остаётся
   на своей строке справа — вместо того чтобы распирать пузырь бесконечно. */
.mmeta{flex-wrap:wrap}
.mmeta > .reactions{order:-1;margin:0 auto 0 0}
/* Счётчик после стопки аватарок. Размером и весом равняется на эмодзи пилюли, а не на время:
   он часть пилюли, а не подписи ряда. */
.rx-n{font-size:12px;font-weight:600;opacity:.85;padding-right:1px}

/* ================= Фирменная форма мелочи в ленте ================= */
/* layout.css держит `#messages *{corner-shape:round}` — и это правило проекта, а не небрежность:
   суперэллипс считается заметно дороже круга, а мелочи в ленте десятки на экран. Здесь мы его НЕ
   отменяем — называем поимённо пять элементов, которые владелец просил привести к фирменной форме.
   Их на ряд одна-три, а не десятки, поэтому цена остаётся ничтожной, как у пузыря и аватарки.
   Селектор тяжелее звёздочки (id + класс) — иначе правило молча проигрывает, сколько бы файлов их
   ни отделяло (правило проекта, на нём уже обожглись в #150).
   Если после этого патча на инерционном скролле вдруг появятся рывки — снимать надо именно эти
   строки, они тут единственная добавка к стоимости отрисовки ряда. */
#messages .reaction,
#messages .media-dur,
#messages .media-size,
#messages .media-time{corner-shape:superellipse(2.6)}

/* Обводка аватарок в пилюле реакции (задумка владельца, второй заход).

   Первый заход красил обводку ПОЛУПРОЗРАЧНЫМ цветом текста — и на фоне пилюли она вышла ровно
   такой, как просили, а на нахлёсте пропала. Причина арифметическая, а не глазомерная: 26%
   тёмного поверх светлого фона пилюли (яркость 169) дают 132 — линия видна; те же 26% поверх
   ФОТОГРАФИИ нижней аватарки (яркость 30–120) дают 31–93, то есть сдвиг на единицы яркости.
   Верхняя аватарка ложится на нижнюю, её левая грань оказывается на фотографии — и разделяющей
   черты не остаётся. Замерено по скриншоту: предсказание 132.4, на экране 132.

   Лечение: тот же цвет, но НЕПРОЗРАЧНЫЙ. Для этого 26% текста подмешиваются не в пустоту, а в
   ФОН САМОЙ ПИЛЮЛИ — результат на фоне пилюли пиксель в пиксель тот же, что и раньше (владельцу
   он нравится), а на фотографии это уже сплошная светлая линия, а не полупрозрачная плёнка.
   Отсюда --rx-pill: пилюля объявляет свой фон значением, потому что прочитать фон родителя из CSS
   нельзя, а у «своей» пилюли он вообще полупрозрачно-белый ПОВЕРХ градиента пузыря — под ним нет
   плоского цвета, который можно было бы взять. Его и считаем: белый 22% поверх акцента (в тёмной
   теме — поверх притемнённого акцента, потому что там и пузырь темнее). Расхождение с настоящим
   фоном — единицы яркости на канал, на двухпиксельной линии его не существует.

   Итог ровно тот, что просили: у обеих аватарок одна и та же рамка, снаружи пары она смыкается в
   общий контур, а внутри отделяет верхнюю от нижней. Форма достаётся даром — граница идёт по
   контуру элемента, а суперэллипс кружку задан в components.css (#192/#193). */
#messages .reaction.rx-in{--rx-pill:var(--accent-soft)}
#messages .reaction.rx-out{--rx-pill:color-mix(in srgb, #fff 22%, var(--accent))}
body.dark #messages .reaction.rx-out{--rx-pill:color-mix(in srgb, #fff 22%, color-mix(in oklch, var(--accent), black 25%))}
/* Пилюля под кадром (mediarx.js) носит те же rx-in/rx-out, но фон у неё свой — panel2. Селектор
   тяжелее обоих правил выше, чтобы не зависеть от порядка строк. */
#messages .rx-host .reaction.rx-in,
#messages .rx-host .reaction.rx-out{--rx-pill:var(--panel2)}
#messages .rx-av{border-color:color-mix(in srgb, var(--text) 26%, var(--rx-pill, var(--panel2)))}

/* ================= Десктоп: вся лента прижата влево ================= */
/* Задумка владельца по донору: на широком экране и свои, и чужие сообщения стоят одной колонкой
   слева, аватарка у каждого ряда своя и тоже слева. Своё от чужого по-прежнему отличают цвет
   пузыря и галочки — как в доноре.
   Условие — НЕ ширина окна, а указатель (правило проекта): десктопная аватарка уже спрятана
   ровно так же (`@media (pointer:coarse){.deskavatar{display:none}}` в comp-rec.css). Значит
   выравнивание и аватарки включаются и гаснут ОДНОЙ приметой и не могут разъехаться: где виден
   кружок — там и колонка слева. Планшет и телефон (coarse) остаются как были — там сейчас
   идеально, и ни одна строка ниже до них не доходит.
   Дальше всё правило — про «хвостик» пузыря. Он у своих смотрел вправо, потому что справа они и
   стояли; переехав налево, они обязаны носить его слева — иначе уголок торчит наружу от колонки.
   Поэтому каждая строка ниже — это ЗЕРКАЛО уже существующей строки про своё сообщение, и селекторы
   у них те же самые: файл подключается последним, при равной силе выигрывает он. */
@media not all and (pointer:coarse){
  .mrow.mmine{align-items:flex-start}
  /* 14px, а не var(--bub-radius): у .mthem .msgbubble нижние углы прибиты числом, и зеркало обязано
     повторить именно его — иначе при сдвинутом ползунке радиуса свой пузырь стал бы не такой, как
     чужой, ровно там, где они впервые стоят рядом. Ползунок для обоих — отдельная маленькая правка. */
  .mmine .msgbubble{border-bottom-right-radius:14px;border-bottom-left-radius:6px}
  /* Пилюли реакций под кадром жмутся к левому краю кадра, как у чужих. */
  .mmine .rx-host{justify-content:flex-start}
  /* Кадр: и радиус, и обрезка (#150 держит их в паре — clip-path обязан повторять border-radius
     один в один, иначе между кадром и подписью откроется щель). */
  .mmine .media-frame{border-bottom-right-radius:16px;border-bottom-left-radius:6px}
  .mmine .media-frame:not(.round){clip-path:inset(0 round 16px 16px 16px 6px)}
  /* Сшивка кадра с подписью (#151): нижний угол уносит подпись, значит зеркалить надо её. */
  .mmine .mcontent > .media-frame:not(.round) + .media-caption{border-bottom-right-radius:var(--bub-radius,14px);border-bottom-left-radius:6px}
  .mmine .mcontent > .msgbubble + .media-caption{border-bottom-right-radius:var(--bub-radius,14px);border-bottom-left-radius:6px}
  /* Коллаж и его общая подпись — тот же приём, свои радиусы: обычный 16, у ряда-альбома 18. */
  .mmine .album-grid{border-bottom-right-radius:16px;border-bottom-left-radius:6px}
  .mrow[data-album].mmine .album-grid{border-bottom-right-radius:18px;border-bottom-left-radius:8px}
  .mrow[data-album].mmine .mcontent > .album-caps:not(:empty){border-bottom-right-radius:18px;border-bottom-left-radius:8px}
}

/* ================= Блоки сообщений: воздух между заходами и хвостик у пузыря ================= */
/* Метки grp-start / grp-end ставит regroupAvatars (js/rnd-days.js): группу рвёт смена автора,
   граница дня и пауза больше часа. Здесь — только вид.

   Расстояния держим переменными, а не числами в правилах: их подгоняет отдельная надстройка
   (css/tgsize.css), и она должна менять ДВЕ строки, а не переписывать разметку отступов.
   Внутри блока работает gap самой ленты, между блоками к нему добавляется margin-top у первого
   ряда — так «между блоками втрое больше, чем внутри» получается сложением, и ни одно правило не
   спорит с другим за один и тот же отступ. */
#messages{--msg-gap:5px;--grp-gap:10px;gap:var(--msg-gap)}
.mrow.grp-start{margin-top:var(--grp-gap)}
#messages > .mrow.grp-start:first-child{margin-top:0}

/* Хвостик к аватарке (задумка владельца). Он только у ПОСЛЕДНЕГО пузыря блока — как в телеграме:
   несколько реплик подряд читаются одним облаком, и отросток у него один.
   Форма — clip-path по кривой, а не «квадрат с вырезом фоном»: под лентой обои, вырезать хвост
   цветом фона там нечем. Кривая подобрана отрисовкой (клин 9×14 с вогнутой верхней гранью).
   Цвет: чужому — сам --bubble-in, он плоский. Своему — --accent, а НЕ --bubble-out: последний
   linear-gradient, и на кусочке 9×14 он развернул бы весь переход заново, вместо того чтобы
   продолжить пузырь. Тот же довод, что у рамки кадра в #195: --accent это цвет градиента без
   перехода, а в тёмной теме пузырь темнее акцента, поэтому там своя строка.
   Угол под хвостом становится острым: скруглённый оставил бы между ними просвет. Остальные
   пузыри блока получают ПОЛНЫЙ радиус — «срезанный уголок» был заменителем хвоста, и держать
   оба сразу значит рисовать два разных признака конца блока. */
.mrow:not(.grp-end) .msgbubble{border-bottom-left-radius:var(--bub-radius,14px);border-bottom-right-radius:var(--bub-radius,14px)}
.mrow.grp-end .msgbubble::after{
  content:'';position:absolute;bottom:0;width:9px;height:14px;pointer-events:none;
  background:var(--bubble-in);
  clip-path:path('M9,0 C9,8 5,13 0,14 L9,14 Z');
}
.mthem.grp-end .msgbubble{border-bottom-left-radius:0}
.mthem.grp-end .msgbubble::after{left:-8px}
.mmine.grp-end .msgbubble{border-bottom-right-radius:0}
.mmine.grp-end .msgbubble::after{right:-8px;transform:scaleX(-1);background:var(--accent)}
body.dark .mmine.grp-end .msgbubble::after{background:color-mix(in oklch, var(--accent), black 25%)}
/* На десктопе лента прижата влево (правило выше), и «своё» стоит той же колонкой — значит и
   хвостик у него смотрит влево, к аватарке, а не наружу от колонки. */
@media not all and (pointer:coarse){
  .mmine.grp-end .msgbubble{border-bottom-right-radius:var(--bub-radius,14px);border-bottom-left-radius:0}
  .mmine.grp-end .msgbubble::after{left:-8px;right:auto;transform:none}
}

/* ================= Цитата ответа читается ссылкой ================= */
/* Прыжок к оригиналу в ней был с волны А, но выглядела она подписью, а не тем, на что нажимают.
   Отклик нажатия и подсветка при наведении — весь недостающий признак; курсор и подсказку ставит
   сама кнопка (js/rnd-albums.js). Полоска слева при наведении наливается акцентом — она и есть
   «ниточка» к оригиналу. */
.mreply{cursor:pointer;transition:background-color .15s ease}
.mreply:hover{background:var(--accent-soft)}
.mmine .mreply:hover{background:rgba(255,255,255,.26)}
.mreply:active{transform:translateY(1px)}
.mreply[disabled]{cursor:default}
.mreply[disabled]:hover{background:inherit}

/* ================= Коллаж: щели прозрачные, клетки скруглены ================= */
/* Донор кладёт кадры альбома отдельными плитками: между ними просто просвет обоев, у каждой свои
   скруглённые углы, общей рамки нет. У нас щели показывали ФОН сетки (цвет пузыря отправителя), а
   рамку по краю давал padding тем же цветом — коллаж читался единой цветной пластиной.
   Меняем ровно три вещи и ничего не ломаем в механике: фон сетки прозрачный (щели показывают
   обои), padding в ноль (рамки нет), клетке возвращаются углы. Щель делаем чуть шире 2px —
   на прозрачном фоне тонкая щель читается хуже, чем на цветном.
   Углы клетке: border-radius снят `!important` в comp-bots.css («клетки квадратные»), поэтому
   возвращаем тем же весом, а форму — суперэллипсом, как у всего фирменного в ленте (селектор
   тяжелее `#messages *`, иначе правило проиграет — правило проекта после #150).
   Обрезку кадра внутри клетки снимал `clip-path:none!important` — теперь она снова нужна, иначе
   углы скруглятся у клетки, а фотография в ней останется прямоугольной. */
.album-grid{background:transparent!important;padding:0;gap:3px}
.album-cell .media-frame{border-radius:12px!important}
#messages .album-cell .media-frame{corner-shape:superellipse(2.6)}
#messages .album-cell .media-frame{clip-path:inset(0 round 12px)!important}
/* Общий контур коллажа больше не рисуется фоном, поэтому «хвостик» угла у крайних клеток —
   единственное, что показывало сторону. Оставляем как есть: у ряда-альбома углы задаёт
   .album-grid, а он теперь прозрачен, и лишнего скругления никто не увидит. */
.mrow[data-album] .mcontent > .album-caps:not(:empty){margin-top:3px}

/* ================= Закреплённые: полоска-счётчик, кнопка списка, сам список ================= */
/* Разметку плашки не трогаем (chat.html — 60 КБ): новые части дорисовывает renderPinBar, прежние
   счётчик и ✕ он же прячет. Здесь только вид. */
/* Сплошную акцентную полоску слева снимаем — её место заняла полоска-счётчик. */
#pin-bar .pin-body{border-left:0;padding-left:0}
.pin-rail{display:flex;flex-direction:column;gap:2px;width:3px;height:30px;flex-shrink:0;align-self:center}
.pin-seg{flex:1;border-radius:2px;background:color-mix(in srgb, var(--accent) 28%, transparent);transition:background-color .15s ease}
.pin-seg.on{background:var(--accent)}
/* Сверх пяти закрепов делений не прибавляем, а разводим их шире: полоска читается пунктиром,
   и «где я в списке» видно ровно так же, а линия не превращается в сплошную. */
.pin-rail.many{gap:4px}
.pin-list-btn{border:none;background:transparent;color:var(--text2);cursor:pointer;padding:5px;border-radius:9px;flex-shrink:0;display:flex;align-items:center;justify-content:center}
.pin-list-btn:hover{color:var(--accent);background:var(--hover)}

/* Список закреплённых. Собран готовыми классами настроек, поэтому здесь только то, чего у них
   нет: строка списка — это .set-row с кнопкой внутри (нажимать надо тело, а не всю строку, иначе
   «открепить» попадало бы в прыжок), и подсветка текущего пина. */
.pin-row{padding:0}
.pin-row-main{flex:1;min-width:0;display:block;text-align:left;border:0;background:transparent;
  color:inherit;font:inherit;cursor:pointer;padding:11px 4px 11px 13px}
.pin-row-who{font-size:13.5px;font-weight:650;color:var(--accent);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pin-row.on .pin-row-main{background:var(--accent-soft)}
.pin-row-x{border:0;background:transparent;color:var(--text2);cursor:pointer;padding:8px 13px 8px 6px;
  display:flex;align-items:center;flex-shrink:0;align-self:stretch}
.pin-row-x:hover{color:var(--danger)}
#pin-list-box{max-height:min(62vh,460px);overflow-y:auto}

/* ================= Лента гаснет под плавающими плашками (телефон) ================= */
/* На телефоне шапка и закреп плавают НАД лентой, и сообщения уезжали под них резаными краями.
   Маска гасит содержимое ленты к самому верху — остаются одни обои, как просил владелец. Гасим
   именно ленту, а не рисуем поверх неё градиент: под лентой обои и узор, и «замазать» их плоским
   цветом нечем.
   Высота растворения считается от того, что сверху висит: без закрепа это шапка, с закрепом —
   ещё и он (метку has-pin ставит renderPinBar).
   ЧЕСТНОЕ ПРЕДУПРЕЖДЕНИЕ: маска на прокручиваемом полотне — единственная в этом патче добавка к
   стоимости кадра, и она ложится ровно на то место, за которое в этом проекте воевали пятью
   патчами (плавность ленты). Появятся рывки или пустые провалы под пальцем — снимать надо этот
   блок целиком, три строки, и ничего больше от него не зависит. */
@media(max-width:760px){
  #messages{
    --feed-fade:74px;
    -webkit-mask-image:linear-gradient(to bottom, transparent 0, #000 var(--feed-fade));
    mask-image:linear-gradient(to bottom, transparent 0, #000 var(--feed-fade));
  }
  body.has-pin #messages{--feed-fade:124px}
}
