/* Календарный экран — ВЕБ-версия (static/web.html).
 *
 * РАБОЧАЯ КОПИЯ: все правки стилей календаря идут сюда.
 * Мини-апп ТГ (static/app.html) держит свою замороженную копию в
 * history-cal.css — расхождение между копиями допустимо и ожидаемо.
 *
 * ⚑ ВАЖНО: экраны дневника РИСУЮТСЯ ТЁМНЫМИ (design_handoff_calendar_dark).
 * Правила НИЖЕ — светлая база: она осталась общей с фильтрами страниц настроек
 * (#dict-filter и панели создания используют те же .fchip / .fp- / .fdrawer) и
 * потому не переписана. Тёмный слой — В САМОМ КОНЦЕ файла, блок
 * `body.theme-dark`; он и определяет, как календарь выглядит на самом деле.
 * Правишь календарь — смотри ОБА места: базу и тёмный оверрайд.
 */
/* ── Шапка экрана: бургер слева + заголовок справа ──
 *  Один ряд: кнопка бургер-меню (.burger-btn, стили в web.html) прижата
 *  влево, блок заголовка (.hero-title) — вправо (margin-left:auto +
 *  text-align:right). align-items:center — бургер и двухстрочный заголовок
 *  выровнены по своим ЦЕНТРАЛЬНЫМ ЛИНИЯМ (требование дизайна; заголовок
 *  «месяц / год» разнесён на две строки, и прижимать кнопку к его верху
 *  нельзя). Ту же разметку использует шапка панели настроек (web.html) — там
 *  вместо заголовка стоит кнопка «← в дневник» (.hero-back). */
.cal-hero {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 20px;
}
.cal-hero .hero-title {
  margin-left: auto;
  min-width: 0;
  text-align: right;
}

/* ── Filter bar (над year-strip) + нижний drawer фильтра ──
 *  Бар: «// фильтровать ——— [воронка][N]». Воронка инвертируется ТОЛЬКО при
 *  применённом фильтре (.is-applied), бейдж .fb-cnt (жёлтый, как в ТГ
 *  мини-аппе) — число применённых опций. Под баром — ряд чипов применённого
 *  фильтра (.fchip-row, стиль ТГ мини-аппа), тап по чипу снимает критерий.
 *  ⚠️ Ряд чипов остался ТОЛЬКО у фильтров страниц настроек (#dict-filter): в
 *  дневнике (тёмные экраны ниже) его сняли 2026-08-13, там от применённого
 *  фильтра снаружи видны воронка со счётчиком и круглый ✕ (.fb-reset) правее
 *  неё — полный сброс.
 *  Сам фильтр — bottom-sheet .fdrawer-overlay/.fdrawer (хром 1-в-1 как
 *  drawer фильтра ТГ мини-аппа: оверлей-подложка, шит от нижней грани,
 *  max-height 85vh, слайд translateY). См. renderFilterBar в
 *  render-calendar-web.js. */
.filter-wrap {
  margin-bottom: 12px;
}
.filter-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 2px;
  min-height: 34px;
}
.fb-label {
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
  cursor: pointer;
  flex: none;
}
.fb-label::before { content: "// "; color: var(--ink-4); }
.fb-rule {
  flex: 1;
  height: 1px;
  background: repeating-linear-gradient(
    to right, var(--line) 0, var(--line) 4px,
    transparent 4px, transparent 7px
  );
}
/* Кнопка-воронка (Feather «filter»). Инверсия — ТОЛЬКО при применённом
   фильтре (.is-applied), просто открытый drawer её не красит. */
.fb-toggle {
  flex: none;
  appearance: none;
  -webkit-appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: 1px solid var(--ink);
  background: var(--paper);
  color: var(--ink);
  min-width: 34px;
  height: 30px;
  padding: 0 8px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.12s ease, color 0.12s ease;
}
.fb-toggle:active { background: var(--ink); color: var(--paper); }
.fb-toggle.is-applied { background: var(--ink); color: var(--paper); }
.fb-toggle svg { width: 15px; height: 15px; display: block; }
/* Бейдж-счётчик применённых опций — как .fb-cnt в ТГ мини-аппе. */
.fb-toggle .fb-cnt {
  font-variant-numeric: tabular-nums;
  background: var(--hl-yellow);
  color: var(--ink);
  padding: 1px 6px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0;
  line-height: 1.4;
}
/* При применённом фильтре метка «// фильтровать» заменяется кнопкой
   «сбросить фильтр»: та же высота, что у воронки справа (30px), чёрная
   рамка, чёрный текст, БЕЗ заливки. Полный сброс одним тапом. */
.fb-reset-all {
  flex: none;
  appearance: none;
  -webkit-appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 30px;
  padding: 0 12px;
  border: 1px solid var(--ink);
  background: transparent;
  color: var(--ink);
  font-family: inherit;
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  border-radius: 0;
}
.fb-reset-all:active { background: var(--ink); color: var(--paper); }

/* Крестик полного сброса — ОТДЕЛЬНАЯ круглая кнопка правее воронки в шапке
   дневника (рисуется только при применённом фильтре, см. renderFilterBar).
   Маленькая нарочно: размер — как у ✕ на бывших чипах применённого фильтра
   (глиф 12px в круге 24px), рядом с 42-пиксельной воронкой она читается как
   спутник, а не вторая равноправная кнопка. К воронке НЕ приклеена — их
   разводит gap самой шапки; по высоте центрируется её align-items:center. */
.fb-reset {
  position: relative;
  flex: none;
  appearance: none;
  -webkit-appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 1px solid var(--ink);
  border-radius: 50%;
  background: var(--paper);
  color: var(--ink);
  font-family: inherit;
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
/* Кнопка мелкая нарочно, но пальцу нужна площадь: невидимая подушка ::after
   растягивает ЗОНУ ТАПА, не трогая вид. Слева ровно -8px = зазор до воронки:
   больше — подушка налезла бы на неё и, будучи выше по порядку отрисовки,
   съедала бы тапы по её краю.
   ⚠️ СПРАВА подушки НЕТ (right: 0). Кнопка — последняя в шапке, её правый край
   совпадает с правым краем контент-колонки, и вылезшая за него подушка давала
   ПАНЕЛИ ДНЕВНИКА 8px горизонтального скролла: у #pane-diary overflow-y:auto,
   значит overflow-x вычисляется в auto, и любой вынос вправо начинал возить
   страницу вбок при применённом фильтре. Тот же грабль, что у хит-зоны нижних
   вкладок (см. .topnav в web.html) — абсолютной подушкой вправо не растягивать.
   Зона тапа остаётся 32×40 и добирается слева/сверху/снизу. */
.fb-reset::after { content: ""; position: absolute; inset: -8px 0 -8px -8px; }
.fb-reset:active { background: var(--ink); color: var(--paper); }

/* ── Чипы применённого фильтра (под баром) — стиль ТГ мини-аппа ──
   Отступы симметричные: сверху (от бара фильтра) и снизу (до year-strip)
   одинаковый воздух — 14px маргина (+4px нижнего паддинга под скролл-слак). */
.fchip-row {
  display: flex;
  flex-wrap: nowrap;
  overflow-x: auto;
  gap: 6px;
  margin: 14px 0;
  padding: 0 2px 4px;
  scrollbar-width: none;
}
.fchip-row::-webkit-scrollbar { display: none; }
.fchip {
  flex: 0 0 auto;
  appearance: none;
  -webkit-appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.04em;
  border: 1px solid var(--ink);
  background: var(--bg-2);
  color: var(--ink);
  padding: 3px 8px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  white-space: nowrap;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  border-radius: 0;
}
.fchip:active { background: var(--ink); color: var(--paper); }
.fchip .fchip-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fchip .fchip-ic { display: inline-flex; line-height: 0; }
.fchip .fchip-ic svg { width: 13px; height: 13px; display: block; }
.fchip .fchip-x {
  color: var(--ink-3);
  font-size: 10px;
  line-height: 1;
}
.fchip:active .fchip-x { color: var(--paper); }

/* ── Drawer фильтра (bottom-sheet, хром как .fd-overlay/.fd-sheet ТГ
      мини-аппа). z-index 115: выше FAB (45), контента И крышки #safe-top
      (110) — оверлей затемняет экран ДО ФИЗИЧЕСКОГО ВЕРХА устройства,
      включая зону статус-бара/островка (явное решение 2026-08-07, реверс
      прежнего «зону не затемняем»; системный хром iOS поверх может
      подстроиться с запаздыванием — принято). НИЖЕ фуллскрин-панелей
      инбокса/редактора (120) и тач-ленты подсказок #chat-sugbar (130) —
      автокомплит поля остаётся видимым. ── */
.fdrawer-overlay {
  position: fixed;
  inset: 0;
  background: rgba(21, 23, 15, 0.5);
  z-index: 115;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease;
  /* Задержка ЗАКРЫТИЯ (базовое состояние = «шит уезжает»): оверлей гаснет не
     одновременно со слайдом, а ПОСЛЕ него — закрытие зеркалит открытие, см.
     ниже. На открытии её снимает .is-open. */
  transition-delay: 0.22s;
}
.fdrawer-overlay[hidden] { display: none; }
.fdrawer-overlay.is-open { opacity: 1; pointer-events: auto; transition-delay: 0s; }
.fdrawer {
  /* Цвет поверхности шита ОДНОЙ переменной: им же красится «юбка» прилипшего
     ряда кнопок (.fp-actions), иначе контент просвечивал бы под ним при
     скролле. Перекрашиваешь шит в своём скоупе (body.theme-dark,
     #pane-settings) — там же переопредели --fd-bg. */
  --fd-bg: var(--paper);
  /* Контейнер для «плавающего» попапа дейт-пикера периода (createDateLine
     popParent): его вешаем на шит, а не в тело — тело скроллится (overflow),
     и попап (~325px) в него целиком не влезает и обрезался бы. Сам шит не
     клипует, поэтому попап при нехватке места спокойно выходит за его верхнюю
     кромку поверх оверлея. */
  position: relative;
  width: 100%;
  max-width: 720px;
  max-height: 85vh;
  background: var(--paper);
  border-top: 1px solid var(--ink);
  border-left: 1px solid var(--ink);
  border-right: 1px solid var(--ink);
  display: flex;
  flex-direction: column;
  transform: translateY(100%);
  transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.3, 1);
  /* safe-area снизу учитывает ТОЛЬКО .fdrawer-body (calc в его padding):
     дублирование здесь давало двойной запас (~68px на iPhone) — ряд кнопок,
     прижатый к низу шита, висел над пустотой. */
}
/* Двухфазное ОТКРЫТИЕ: сначала оверлей фейдится на весь экран (0.18s),
   ПОТОМ выезжает шит — transition-delay ровно на длительность фейда.
   ЗАКРЫТИЕ — ЗЕРКАЛО открытия, а не «оба разом» (как было до 2026-08-14:
   оверлей успевал погаснуть, пока шит ещё ехал вниз, и закрытие читалось
   рывком). Теперь сперва уезжает шит (у базового состояния .fdrawer delay
   нет — 0.22s), и только потом гаснет оверлей (transition-delay: 0.22s на
   базовом .fdrawer-overlay выше). Обе фазы = 0.40s в каждую сторону.
   ⚠️ Кто закрывает шит из JS — ждёт ВСЕ 0.40s перед hidden/перерисовкой
   (DRAWER_CLOSE_MS в render-calendar-web.js и web.html): снос узла оверлея на
   полпути обрывает фейд, и подложка пропадает скачком. */
.fdrawer-overlay.is-open .fdrawer { transform: translateY(0); transition-delay: 0.18s; }
/* Тач: шит открывается ВЫСОКИМ, чтобы поле поиска (верх шита) стояло ВЫШЕ
   будущей клавиатуры ЕЩЁ ДО тапа в него. Клавиатура layout-вьюпорт не сжимает,
   и низкий шит уходил под неё целиком: iOS «раскрывал» каретку панорамированием
   вьюпорта, лента подсказок промахивалась мимо видимой области.
   ⚠️ Двигать шит В МОМЕНТ фокуса нельзя (пробовали margin-подъём из
   focus-обработчика, в т.ч. отложенный таймером): на живом iOS поле уезжает
   из-под пальца посреди tap-жеста, WebKit не презентует клавиатуру, армед-окно
   KB_ARM_MS истекает — шит «прыгал вверх и возвращался». Поэтому высота — CSS,
   заранее; хвост шита (чипы/кнопки) при наборе накрыт клавиатурой — как ряд
   действий инбокса (над клавиатурой один слой — лента подсказок). Докоррекция
   post-factum (клавиатура выше ожидаемой) — fitDrawerToKb в web.html. */
@media (pointer: coarse) {
  .fdrawer { min-height: 62vh; min-height: 62dvh; }
}
@media (pointer: coarse) and (orientation: landscape) {
  .fdrawer { min-height: 80vh; min-height: 80dvh; }
}
/* Десктоп: шит выше контента ровно настолько, чтобы между чипами и рядом
   кнопок остался воздух (margin-top:auto на .fp-actions). Раньше было 500px
   («вдвое выше контента»): пустоты набиралось ~256px — ополовинено до ~128px
   по просьбе юзера. Считается от контента (~202px: шапка + поле + ОДНА строка
   чипов + ряд кнопок), поэтому 330, а не 500/2. Тач-шит (62dvh выше) НЕ
   трогаем: его высота держит поле поиска над будущей клавиатурой, а хвост с
   кнопками там всё равно накрыт ею. min() страхует короткие окна: min-height
   не должен перебить max-height 85vh. */
@media (pointer: fine) {
  .fdrawer { min-height: min(330px, 80vh); }
}
.fdrawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px 12px;
  border-bottom: 1px solid var(--ink);
}
.fdrawer-head h2 {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0;
}
.fdrawer-head h2::before { content: "// "; color: var(--ink-4); }
.fdrawer-close {
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  border: 1px solid var(--ink);
  color: var(--ink);
  width: 28px;
  height: 28px;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  border-radius: 0;
}
.fdrawer-close:active { background: var(--ink); color: var(--paper); }
.fdrawer-body {
  flex: 1;
  overflow-y: auto;
  /* Снизу минимум воздуха (12px + safe-area): ряд кнопок прижат к нижней
     грани шита (margin-top:auto + sticky на .fp-actions), запас — только под
     home-indicator.
     ⚠️ Нижний отступ вынесен в переменную --fd-pad-b: тем же значением
     .fp-actions задаёт свою sticky-привязку и «юбку» (см. её блок ниже).
     Переопределяешь padding-bottom тела в своём скоупе (#pane-settings) —
     переопредели там же --fd-pad-b, иначе прилипший ряд встанет мимо. */
  --fd-pad-b: calc(12px + env(safe-area-inset-bottom));
  padding: 16px 16px var(--fd-pad-b);
  -webkit-overflow-scrolling: touch;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* 1. Поле поиска — то же, что жило в баре раньше (словарный автокомплит
   .tag-field, стили в web.html), теперь во всю ширину; кнопок справа нет.
   Рамка без фокуса — --ink-2 (та же, что у чипов/кнопок характеристик рядом):
   ТЕМНЕЕ и плейсхолдера (--ink-4), и подписей на кнопках (--ink-3), но светлее
   чёрного --ink — иначе тонкая --line терялась на бумажном фоне. Каретка
   встала в поле → рамка добирает до --ink. */
.fp-field-wrap {
  position: relative;
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 0 4px 0 8px;
  border: 1px solid var(--tgl-line);
  background: var(--paper);
  transition: border-color 0.12s ease;
}
.fp-field-wrap:focus-within { border-color: var(--ink); }
.fp-field-wrap .tag-inwrap { flex: 1 1 auto; min-width: 0; }
/* Горизонтального padding у поля нет (он на обёртке) — иначе placeholder
   (::before, left:0) разъехался бы с печатаемым текстом. */
.fp-field-wrap .tag-field {
  font-size: 16px;
  line-height: 1.5;
  min-height: 0;
  padding: 5px 0;
  white-space: nowrap;         /* single-line: тянемся вбок, не переносимся */
  overflow-x: auto;
  scrollbar-width: none;
}
.fp-field-wrap .tag-field::-webkit-scrollbar { display: none; }
.fp-clear {
  flex: none;
  appearance: none;
  -webkit-appearance: none;
  border: none;
  background: transparent;
  color: var(--ink-3);
  font-size: 15px;
  line-height: 1;
  width: 24px;
  height: 28px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.fp-clear:active { color: var(--warn); }

/* 1a. Диапазон дат — под полем поиска, ТОЛЬКО в лентах («Лента» /
   «Черновики»: year-strip там убран, период задаётся здесь).

   Внутри каждого поля — СВОЙ пикер проекта (createDateLine из web.html, тот же
   движок и вид, что у строки даты инбокса: .date-line + попап .date-pop);
   нативного <input type="date"> здесь больше нет. Он рисовался системой (чужой
   вид), на iOS не показывал плейсхолдер незаполненной границы — поле схлопывалось
   в тонкую полоску, — и тапабелен по сути был только значок календаря.

   Обе границы — ОДНОЙ строкой, между ними тире, половины делят ширину поровну
   (как было с нативными инпутами). Поэтому дата в поле числовая («07.05.2026»,
   createDateLine compact): словесная в половину мобильной ширины не влезает.
   Кнопки «сбросить»/«сегодня» живут В ПОПАПЕ (.dp-foot), в самой строке их нет —
   ширины на них тут нет. Рамка/фон — как у поля поиска выше; высота держится
   содержимым, поэтому пустое поле такое же по высоте, как заполненное. */
.fp-dates {
  display: flex;
  align-items: stretch;
  gap: 8px;
}
.fp-dates-dash {
  flex: none;
  align-self: center;
  color: var(--ink-4);
  font-size: 12px;
}
.fp-date {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  padding: 0 6px;
  border: 1px solid var(--tgl-line);
  background: var(--paper);
  cursor: pointer;
  transition: border-color 0.12s ease;
}
.fp-date:focus-within { border-color: var(--ink); }
/* Подсветка наведения — на ВСЁ поле (тапабельно оно целиком), а не на одну
   фразу внутри: своя серая подложка у .date-line рисовала бы «бокс в боксе». */
.fp-date:hover { background: var(--bg-2); }
.fp-date .date-line:hover { background: transparent; }
.fp-date-lbl {
  flex: none;
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
}
/* Обёртка пикера занимает всю оставшуюся ширину поля. Базовые margin/align-self
   обёртки инбокса тут гасим: поле фильтра выравнивает содержимое само. */
.fp-date .date-line-wrap {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  align-self: stretch;
  margin: 0;
}
/* Строка внутри поля: половина ряда узкая, поэтому 14px (iOS-зума тут бояться
   нечего — это div, а не input) и без боковых падингов; не влезло — многоточие. */
.fp-date .date-line {
  flex: 1 1 auto;
  min-width: 0;
  gap: 5px;
  padding: 5px 0;
  font-size: 14px;
  white-space: nowrap;
}
.fp-date .dl-icon svg { width: 14px; height: 14px; }
.fp-date .dl-text { overflow: hidden; text-overflow: ellipsis; }

/* 2. Чипы — ТРИ РЯДА (.fp-chips-props), по ряду на группу критериев:
   огоньки (интенсивность) · характеристики
   словаря · ТЕГИ (.fp-tags-row — сами пункты словаря с флагом «Тег», по чипу
   на тег; добавлен 2026-09-02 последним рядом: это не характеристика, и в
   ряду с «Активность/Локация/Тренер» читался бы как ещё одна; первым чипом
   ряда с 2026-09-03 идёт «нет тегов» — .fp-notag, обратное требование
   «в сообщении нет ни одного тега», взаимоисключающее с чипами тегов;
   своего скина у него нет — тот же .fp-flag, отличается косым крестом
   в иконке).
   Ряда «есть черновики» (day-фильтр) больше нет — снят 2026-09-03 вместе
   с самим фильтром.
   Раньше всё шло ОДНОЙ строкой с вертикальными
   разделителями и переносом по ширине — с приходом огоньков группы в такой
   каше перестали читаться (правка 2026-08-27). Огоньки шли последним рядом и
   подняты ПЕРВЫМ по просьбе юзера 2026-09-02 (сразу под строкой поиска, на
   всех вкладках). Внутри ряда чипы
   по-прежнему ПЕРЕНОСЯТСЯ по ширине (.fp-chips-row, flex-wrap; первый ряд —
   не чипы, а контрол интенсивности из панелей ввода, см. .fp-int-row),
   горизонтального скролла нет. Вертикальных разделителей .dict-flags-sep тут
   больше НЕТ (в переносящемся ряду палка регулярно оказывалась висящей в
   конце строки) — группы разводят сами ряды; в настройках словаря разделитель
   живёт по-прежнему.
   Сами чипы — прямоугольные тогглы «иконка + имя» (та же база, что .dict-btn
   в настройках, но с подписью — имя читается без тапа и тултипа).
   Рамки (и у чипа, и у кнопок характеристик) — --ink-2: темнее подписей
   (--ink-3), но светлее чёрного --ink, который остаётся за состоянием
   .is-on (заливка) и за кнопками ряда действий. */
.fp-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 1px;
}
/* Ряды идут КОЛОНКОЙ. align-items: stretch обязателен — базовый center в
   колоночном направлении центрировал бы ряды по горизонтали.
   Зазор МЕЖДУ рядами (16px) заметно больше зазора чипов ВНУТРИ ряда (8px, он
   же зазор между строками переноса) — иначе блок сущностей словаря, занявший
   несколько строк переноса, слипался бы со следующим блоком в кашу. */
.fp-chips-props {
  flex-direction: column;
  align-items: stretch;
  gap: 16px;
}
.fp-chips-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
/* ДВОЙНОЙ отступ после огоньков и после ряда характеристик (просьба юзера
   2026-09-07): оба ряда — «свои» критерии записи, а всё, что ниже, — блоки
   САМИХ пунктов словаря; на всех вкладках дневника шит один и тот же.
   16px гэпа колонки + 16px margin = 32px, ровно вдвое против зазора между
   остальными рядами. Только ПРЯМЫЕ дети .fp-chips-props — ряд чипов внутри
   блока словаря (.fp-group .fp-chips-row) под правило не подпадает. */
.fp-chips-props > .fp-int-row,
.fp-chips-props > .fp-props-row { margin-bottom: 16px; }
/* Свёрнутая панель блока: ряд чипов прячем ЯВНО — display:flex выше перебил
   бы браузерный [hidden]. */
.fp-chips-row[hidden] { display: none; }
/* Раскрытие панели — ПЛАВНОЕ: max-height едет от 0 до высоты ряда (её меряет
   и проставляет JS, см. dictPanel в render-calendar-web.js — чипы переносятся
   по ширине, фиксированной высоты у блока нет), прозрачность за компанию.
   Длительность держать синхронной с PANEL_MS. */
.fp-group .fp-chips-row {
  overflow: hidden;
  opacity: 0;
  transition: max-height 0.24s ease, opacity 0.18s ease;
  /* ВЛОЖЕННОСТЬ (просьба юзера 2026-09-07): содержимое раскрытой категории
     сдвинуто вправо относительно её заголовка — видно, что чипы принадлежат
     ему, а не идут следующим рядом опций. Отступ на РЯДЕ, а не на чипах:
     чипы переносятся по ширине, и лесенка отступов на каждой строке переноса
     разъехалась бы. Высоту раскрытия JS меряет scrollHeight'ом уже с этим
     падингом (сузилась строка → чипов переносится больше → замер выше), так
     что анимация остаётся точной. */
  padding-left: 16px;
}
.fp-group.is-open .fp-chips-row { opacity: 1; }

/* ── Блок сущностей словаря — сворачиваемая панель ──
   Заголовок (иконки набора + перечисление его характеристик + счётчик
   выбранного + шеврон) на всю ширину, под ним — ряд чипов блока. Свёрнута по
   умолчанию (просьба юзера 2026-09-04): пунктов словаря сотни, развёрнутыми
   они топили бы ряды огоньков и характеристик — те не сворачиваются. */
.fp-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.fp-group-head {
  appearance: none;
  -webkit-appearance: none;
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 0;
  border: 0;
  background: none;
  color: var(--ink-3);
  font-family: inherit;
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
/* Заголовок = характеристики набора парами «иконка + её имя» через «·»
   (просьба юзера 2026-09-04: у каждой иконки своя подпись рядом, а не кучка
   иконок слева от общей строки). Пары не переносятся — длинное имя режется
   многоточием, чтобы счётчик и шеврон справа остались на месте. */
.fp-group-title {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  overflow: hidden;
}
.fp-group-mark {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.fp-group-sep { flex: none; opacity: 0.6; }
.fp-group-ic { display: flex; flex: none; }
.fp-group-ic svg { width: 15px; height: 15px; display: block; }
.fp-group-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Счётчик выбранного внутри — у закрытой панели единственный признак, что
   критерий из неё применён. */
.fp-group-cnt {
  flex: none;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--hl-yellow, #f4ec3a);
  color: var(--ink);
  font-size: 9px;
  line-height: 1;
}
.fp-group-cnt[hidden] { display: none; }
.fp-group-chev {
  flex: none;
  display: flex;
  transition: transform 0.15s;
}
.fp-group-chev svg { width: 14px; height: 14px; display: block; }
.fp-group.is-open .fp-group-chev { transform: rotate(180deg); }
/* .dict-flags-sep (вертикальная палка между группами) остался только в
   настройках словаря; его отступы — 6px с каждой стороны поверх общего gap
   8px — живут там же, в web.html. */
/* Кнопка характеристики — ПРЯМОУГОЛЬНЫЙ тоггл: иконка слева, имя справа
   (ширина по содержимому, не квадрат 30×30). Единственный тип кнопки в рядах
   опций: текстовый чип .fp-chip («есть черновики») снят вместе с самим
   фильтром 2026-09-03. */
.fp-flag {
  flex: none;
  appearance: none;
  -webkit-appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 30px;
  padding: 0 10px;
  border: 1px solid var(--tgl-line);
  background: var(--paper);
  color: var(--ink-3);
  font-family: inherit;
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  border-radius: 0;
}
/* Иконок в слоте может быть НЕСКОЛЬКО: чип сущности словаря несёт ВСЕ
   характеристики своего пункта (активность + кастом-характеристика и т.п.) —
   тогда они идут в ряд с маленьким зазором. У чипа характеристики иконка одна,
   и gap ничего не меняет. */
.fp-flag-ic { display: flex; flex: none; gap: 3px; }
.fp-flag svg { width: 15px; height: 15px; display: block; }
.fp-flag.is-on {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

/* 3. Кнопка «применить» — ОДНА (прежней пары [календарь]/[список] нет: режим
   показа задаёт нижняя вкладка веба — Календарь / Лента / Черновики, а фильтр
   только сужает то, что она показывает). Тёмная, подпись + стрелка справа
   (Feather arrow-right); применяет весь набор фильтра — текст + чипы. Слева от
   неё — «сбросить» (.fp-reset): чёрная рамка, чёрный текст, без заливки;
   рисуется только при применённом фильтре.
   Ряд прижат ВПРАВО (flex-end) и к НИЖНЕЙ грани шита: margin-top auto внутри
   flex-колонки .fdrawer-body съедает свободную высоту (десктопный min-height
   шита ниже даёт «воздух» между полем/чипами и кнопками; на тач-шите 62dvh —
   то же самое, и при выезде клавиатуры ряд ОСТАЁТСЯ у нижней грани экрана,
   т.е. перекрывается ею — осознанное поведение, кнопки на время набора не
   нужны). ⚠️ Вертикальный скролл .fdrawer-body от margin-top:auto не
   страдает: это не justify-content:flex-end на скролл-контейнере (см. грабли
   панелей инбокса) — при переполнении auto-маргин схлопывается в 0, и ряд
   держит уже sticky (см. ниже). */
.fp-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: auto;
  /* ── ПРИЛИПШИЙ РЯД (2026-09-04) ──
     margin-top:auto прижимает ряд к низу шита, только пока контент КОРОЧЕ
     тела: раскрыл панель сущностей с длинным списком чипов — тело поехало
     скроллом, и «применить» уезжала под нижнюю грань, до неё надо было
     доскроллить. Теперь ряд ещё и sticky — виден всегда, на любом скролле.
     bottom:0 — это НЕ «вплотную к грани шита»: точка прилипания отсчитывается
     от КОНТЕНТ-бокса скролл-контейнера, т.е. нижний падинг .fdrawer-body
     (--fd-pad-b, запас под home-indicator) остаётся под кнопками. Проверено
     и в chromium, и в webkit: ряд стоит на одном месте и в покое, и на любом
     скролле — прежняя геометрия 1-в-1.
     Собственный бокс ряда НЕ трогаем (ни падингов, ни отрицательных
     маргинов — пробовали «юбку» падингом, flex+sticky давали двойной сдвиг
     вверх): полосу под кнопками закрывает ::after, полосу над ними — ::before.
     z-index:1 — над контентом тела, но НИЖЕ автокомплита поля (.ac-menu, 20)
     и попапа дат (.date-pop, 30).
     ⚠️ Селектор ПРЯМОГО ребёнка — прилипают только ряды ФИЛЬТРОВ (календарь и
     список словаря кладут .fp-actions прямо в тело шита). В шитах СОЗДАНИЯ
     на страницах настроек ряд «добавить» лежит внутри формы .dict-create — там
     он по-прежнему в потоке, под полями: прилипший ряд накрывал бы собой
     последний ряд сетки иконок (см. test_ui_props
     ::test_props_create_icon_grid_never_touches_add_button). */
}
.fdrawer-body > .fp-actions {
  position: sticky;
  bottom: 0;
  z-index: 1;
  background: var(--fd-bg, var(--paper));
}
/* «Юбка» вниз до нижней грани шита: под прилипшим рядом остаётся полоса в
   высоту падинга тела, и контент прокручивался бы в ней на виду. */
.fdrawer-body > .fp-actions::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  height: var(--fd-pad-b, 0px);
  background: var(--fd-bg, var(--paper));
}
/* Сверху контент не упирается в кнопки встык, а утекает под них: короткий
   градиент поверхности над рядом. pointer-events:none — полоса висит над телом
   шита и не должна перехватывать тапы по чипам под ней. */
.fdrawer-body > .fp-actions::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: 16px;
  background: linear-gradient(to top, var(--fd-bg, var(--paper)), transparent);
  pointer-events: none;
}
.fp-go {
  flex: none;
  appearance: none;
  -webkit-appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  border: 1px solid var(--ink);
  background: var(--ink);
  color: var(--paper);
  padding: 0 14px;
  min-width: 130px;
  height: 38px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.fp-go svg { width: 19px; height: 19px; display: block; flex: none; }
.fp-reset {
  flex: none;
  appearance: none;
  -webkit-appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--ink);
  background: transparent;
  color: var(--ink);
  height: 38px;                /* +25%, в рост [календарь]/[список] */
  padding: 0 15px;
  font-family: inherit;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  border-radius: 0;
}
.fp-reset:active { background: var(--ink); color: var(--paper); }

/* Заголовок ужат относительно прежнего (было clamp(36px, 9vw, 52px)): месяц и
   год разнесены на две строки и прижаты вправо, поэтому крупный кегль здесь
   больше не нужен — строка «дневник сохранений» под ним удалена. */
.cal-hero h1 {
  font-family: 'Space Grotesk', sans-serif;
  font-weight: 600;
  font-size: clamp(24px, 6vw, 32px);
  line-height: 1.05;
  letter-spacing: -0.03em;
  margin: 0;
  color: var(--ink);
  text-transform: capitalize;
}
/* Вторая строка заголовка календаря — год (месяц строкой выше). */
.cal-hero .hero-year {
  font-family: 'Space Grotesk', sans-serif;
  font-weight: 600;
  font-size: clamp(16px, 4vw, 21px);
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--ink-3);
}
.cal-hero .month-sub {
  font-size: 11px;
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin-top: 4px;
}
/* Кнопка «← в дневник» — ЕДИНСТВЕННОЕ содержимое правой части шапки настроек
   (названия раздела там нет: страница подписывает себя сама). Line-art стрелка
   + подпись, без заливки: возврат, а не действие. Высота — ровно как у
   .burger-btn (38px), поэтому обе кнопки шапки читаются одним рядом; из-за
   этого же padding/кегль заданы под фиксированную высоту, а не наоборот. */
.cal-hero .hero-back {
  appearance: none;
  -webkit-appearance: none;
  margin-left: auto;
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 38px;
  padding: 0 13px;
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--ink);
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.cal-hero .hero-back svg { width: 16px; height: 16px; display: block; flex: none; }
.cal-hero .hero-back:active { background: var(--ink); color: var(--paper); }
/* Режим ленты: заголовок = запрос (не месяц). Не капитализируем и гасим до
   одной строки с многоточием — запрос бывает длинным. */
.cal-hero.is-search h1 {
  text-transform: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Year-strip — годовая полоса */
.year-strip {
  margin-bottom: 18px;
  border: 1px solid var(--ink);
  background: var(--paper);
  padding: 8px 10px 6px;
}
.year-strip .ys-head {
  display: flex;
  justify-content: space-between;
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 6px;
}
.year-strip .ys-head .yr { color: var(--ink); font-weight: 700; min-width: 32px; text-align: center; }
.year-strip .ys-year {
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
.year-strip .ys-nav {
  font-family: 'JetBrains Mono', monospace;
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
  border: 1px solid var(--ink);
  background: var(--paper);
  color: var(--ink);
  padding: 2px 8px;
  cursor: pointer;
  user-select: none;
}
.year-strip .ys-nav:active { background: var(--ink); color: var(--paper); }
.year-strip .ys-grid {
  display: grid;
  grid-template-columns: repeat(53, 1fr);
  gap: 1px;
}
/* Квадратик заливки меньше самой клетки: padding + отрисовка фона по
   content-box. Бокс клетки при этом прежнего размера, поэтому МАРКЕР ТЕКУЩЕГО
   МЕСЯЦА не сжимается: и `outline` (светлая тема), и `box-shadow` (тёмная)
   рисуются от border-box и padding их не двигает.
   ⚠️ ПОЛ 0.75px обязателен — без него на мобильной ширине клетка ~4.5px, и
   голые 5-8% дают падинг ~0.2-0.35px: пустота между рамкой месяца и квадратом
   схлопывается в субпиксель и её попросту не видно. 0.75px = 1.5-2 физических
   пикселя на retina — тонко, но различимо. Процент оставлен, чтобы на широких
   экранах (клетка >9px) зазор рос вместе с клеткой, а не выглядел волоском.
   ⚠️ Цвета уровней ниже задаются `background-color`, а НЕ шорткатом
   `background` — шорткат сбрасывает background-clip обратно в border-box,
   и квадраты снова стали бы во всю клетку. */
.year-strip .ys-cell {
  aspect-ratio: 1 / 1;
  background-color: var(--bg-2);
  background-clip: content-box;
  padding: max(0.75px, 8%);
  border: 0.5px solid transparent;
  cursor: pointer;
}
/* Заливка клетки = МАКСИМАЛЬНАЯ интенсивность за день (не число сообщений):
   1 → светлый, 2 → средний, 3 → тёмный. Рамп совпадает с цветом огоньков. */
.year-strip .ys-cell.lvl-1 { background-color: var(--ink-4); }
.year-strip .ys-cell.lvl-2 { background-color: var(--ink-3); }
.year-strip .ys-cell.lvl-3 { background-color: var(--ink); }
/* offset 0, а не -1px: инсетная рамка съедала бы ту самую пустоту, ради которой
   квадрат заливки ужат падингом (в тёмной теме её роль играет box-shadow, он и
   так рисуется от границы бокса наружу). */
.year-strip .ys-cell.is-current-month { outline: 1px solid var(--ink); outline-offset: 0; }

/* Месяц-grid */
.month-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 8px 0 8px;
}
.month-nav .label {
  font-family: 'Space Grotesk', sans-serif;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-transform: capitalize;
}
.month-nav .nav-left {
  display: inline-flex;
  gap: 6px;
}
.month-nav .nav-btn {
  font-family: 'JetBrains Mono', monospace;
  font-size: 13px;
  font-weight: 700;
  border: 1px solid var(--ink);
  background: var(--paper);
  color: var(--ink);
  padding: 4px 0;
  width: 30px;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  user-select: none;
}
.month-nav .nav-btn:active { background: var(--ink); color: var(--paper); }
.month-nav .today-btn svg { width: 10px; height: 10px; display: block; }

.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
  border: 1px solid var(--ink);
  background: var(--ink);
  padding: 1px;
}
.cal-dow {
  background: var(--bg-2);
  text-align: center;
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding: 6px 0 4px;
  font-weight: 600;
}
.cal-cell {
  background: var(--paper);
  aspect-ratio: 1 / 1;
  position: relative;
  padding: 4px 5px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  user-select: none;
}
.cal-cell.outside { background: var(--bg-2); cursor: pointer; }
/* Приглушённое число даты для дней чужого месяца (.outside) — светло-серый
   #c2c4bb (светлее --ink-4 #9a9d8e, тон сохранён — тёплый/зеленца).
   Несовпавшие дни при поиске (.no-match) гасим ЕЩЁ светлее (#d9dad2, ниже) —
   поиск сильнее «утапливает» пустые дни, чем нейтральные чужие даты. */
.cal-cell.outside .day-num { color: #c2c4bb; }
.cal-cell:active { background: var(--bg-2); }

/* Поиск НЕ перекрашивает клетки. При активном запросе бэкенд отдаёт огоньки
 * только для совпавших сообщений (count/intensities отфильтрованы), а сама
 * клетка остаётся в обычном стиле — прежние состояния .match/.dim убраны.
 * Метки при поиске: .has-match — у дня с совпадениями число даты жирным
 * (усиливаем акцент); .no-match — у дня своего месяца без совпадений число
 * гасим в светлый серый (#d9dad2) — светлее чужого месяца
 * (.outside, #c2c4bb), чтобы пустые дни уходили на фон, но всё же читались.
 * См. render-calendar-web.js и db.get_web_entries_counts. */
/* Совпавший день при активном поиске — число даты крупнее на 20% и жирным.
 * scale (не font-size) не гонит reflow; transform-origin left center держит
 * левый край на месте (числа выровнены влево), растёт вправо/симметрично по
 * вертикали. Для today-клетки со совпадением тот же scale наследует
 * .day-num целиком → жёлтая индикация (background) масштабируется вместе с
 * числом «пропорционально увеличению на 20%».
 * Масштабируем ОБЁРТКУ .day-top, а не само число: точка-черновик приколота к
 * углу этого «квадрата вокруг числа» — иначе крупное число въезжало бы под неё
 * (transform layout не двигает). Так число и точка растут вместе. */
.cal-cell.has-match .day-top {
  transform: scale(1.2);
  transform-origin: left center;
}
.cal-cell.has-match .day-num { font-weight: 700; }
/* День соседнего месяца со совпадением подсвечивается ТАК ЖЕ, как свой (жирный
 * + scale выше), и число берёт обычный цвет: приглушённый #c2c4bb съедал бы
 * акцент, и совпадение в хвосте сетки читалось слабее остальных. «Чужесть»
 * месяца по-прежнему видна по фону клетки (--bg-2).
 * Специфичность (0,4,0) перебивает .cal-cell.outside .day-num (0,3,0). */
.cal-cell.outside.has-match .day-num { color: var(--ink); }
.cal-cell.no-match .day-num { color: #d9dad2; font-weight: 400; }
/* no-match + today: НЕ убираем today-индикатор, а приглушаем его — жёлтая
 * подложка светлеет, подчёркивающая линия становится тем же серым, что и число
 * несовпавшего дня (#d9dad2). Специфичность (0,4,0) перебивает и .no-match, и
 * .today ниже, поэтому цвет/вес/фон задаём тут явно; размеры/позиция/padding
 * фона наследуются от .cal-cell.today .day-num. */
.cal-cell.no-match.today .day-num {
  color: #d9dad2;
  font-weight: 400;
  background-image:
    linear-gradient(#f8f2a0, #f8f2a0),
    linear-gradient(#d9dad2, #d9dad2);
}

.cal-cell.today .day-num {
  font-weight: 700;
  align-self: flex-start;
  width: max-content;
  background-image:
    linear-gradient(var(--hl-yellow), var(--hl-yellow)),
    linear-gradient(var(--ink), var(--ink));
  background-size:
    100% 6px,
    100% 1px;
  background-position:
    0 calc(100% - 1px),
    0 100%;
  background-repeat: no-repeat;
  padding: 0 2px 1px;
}

.cal-cell .day-num {
  font-family: 'JetBrains Mono', monospace;
  font-size: 13px;
  color: var(--ink);
  font-weight: 500;
  letter-spacing: -0.02em;
}
/* .day-top — коробка ровно по числу (align-self:flex-start не даёт флекс-колонке
   .cal-cell растянуть её на всю клетку). Ширина = ширина числа, поэтому точка,
   приколотая к её правому краю, стоит на одинаковом расстоянии и от «5», и от
   «18» — фиксированного «квадрата» тут быть не должно: с ним у однозначных дней
   метка уезжала вправо, а у двузначных прилипала к цифрам. */
.cal-cell .day-top {
  position: relative;
  align-self: flex-start;
}
/* Точка (.draft-dot) = на этой дате лежит НЕСОХРАНЁННЫЙ черновик инбокса
   (localStorage устройства, не БД) — та же метка, что в дейт-пикере инбокса
   (.dp-day.has-draft в web.html). Верхний правый угол числа, надстрочником:
   left:100% + зазор 2px (не касается цифр ни при какой их ширине), top — чуть
   ниже верха строки, на уровне верхушки цифр. */
.cal-cell .draft-dot {
  position: absolute;
  top: 1px;
  left: 100%;
  margin-left: 2px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--ink);
  pointer-events: none;
}
/* Крестик «ничего не сохранено» (.no-save-cross) — прошедший день без единого
   ГОТОВОГО сохранения (черновики не в счёт: день с одними черновиками получает
   и крестик, и точку черновика у числа). Рисуется и в своём месяце, и в
   хвостах чужого (.outside). Центр клетки; цвет — тот же приглушённый серый,
   что у числа дня чужого месяца (.cal-cell.outside .day-num, #c2c4bb), чтобы
   сетка не «зачёркивалась» в лоб. Сегодня/будущее крестика не получают; при
   активном фильтре не рисуется — см. renderMonthGrid в render-calendar-web.js. */
.cal-cell .no-save-cross {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 13.5px;
  height: 13.5px;
  transform: translate(-50%, -50%);
  color: #c2c4bb;
  line-height: 0;
  pointer-events: none;
}
.cal-cell .no-save-cross svg { width: 100%; height: 100%; display: block; }

.cal-cell .dots {
  display: flex;
  gap: 2px;
  align-items: flex-end;
  flex-wrap: wrap;
  min-height: 7px;
}
/* Огонёк = одно сообщение (сохранение) дня; СТИЛЬ инбокса (createIntensity):
   контур (.fire-line, всегда целиком) + заливка (.fire-fill, обрезается по
   уровню). Уровень = степень заливки: 1 — только контур, 2 — чуть меньше
   половины, 3 — целиком; цвет всегда --ink. Заменил чёрные кружки. Сортировка
   по убыванию интенсивности — в render-calendar-web.js. Размер вдвое больше
   прежних кружков. */
.cal-cell .fire {
  position: relative;
  width: 15px;
  height: 15px;
  display: block;
  color: var(--ink);
}
.cal-cell .fire svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.cal-cell .fire .fire-line path {
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: square; stroke-linejoin: miter;
}
.cal-cell .fire .fire-fill path { fill: currentColor; stroke: none; }
/* Степень заливки по уровню — те же клипы, что у инбокса (.int-stop.lN);
   у «2» краска снизу на 30.92% высоты (= 32.55% минус 5%, просьба юзера
   2026-09-03; проценты относительные). */
.cal-cell .fire.l1 .fire-fill { clip-path: inset(100% 0 0 0); }
.cal-cell .fire.l2 .fire-fill { clip-path: inset(69.08% 0 0 0); }
.cal-cell .fire.l3 .fire-fill { clip-path: none; }
/* Кружок — legacy-маркер (маркер неразобранного ниже всё ещё .dot). */
.cal-cell .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ink);
}
/* Маркер неразобранного ввода: серый кружок (того же размера, 7px как .dot).
 * День с failed/pending entry без тренировок. Цвет --dot-unparsed зарезервирован
 * под этот тип и не используется для типов тренировок. (Легаси бот-флоу —
 * в веб-дневнике на web_entries не используется.) */
.cal-cell .dot.unparsed {
  background: var(--dot-unparsed);
}

/* ── Лента результатов поиска (режим «список», кнопка с тремя полосками) ──
 *  Показывается «на месте календаря месяца» (year-strip остаётся сверху).
 *  Карточки сообщений read-only — те же .msg-стили, что на дневном экране
 *  (history-day-web.css). Заголовки-даты группируют ленту по дням; клик по
 *  карточке открывает соответствующий день. Рендер — window.RenderMsgFeed. */
.cal-feed { margin-top: 6px; }
/* Сентинел автоподгрузки — последний элемент .cal-feed, под лентой. Он же
   индикатор: пока следующая порция летит, крутится стандартный лоадер проекта
   (.pulse-loader.sm, «столбики»). IntersectionObserver ловит его ЗАРАНЕЕ
   (rootMargin 800px), поэтому обычно порция успевает приехать до того, как
   пользователь досюда доскроллит, и лоадера он не видит. Когда грузить нечего —
   элемент снимается из DOM (feedStopAutoload). */
.feed-more {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 18px 0 28px;
  color: var(--ink-3);
}
/* Лента = таймлайн ДИАПАЗОНА: показывается КАЖДЫЙ календарный день от самой свежей
   до самой старой совпавшей даты. Слева вертикальная серая РЕЛЬСА, на которой сидят
   точки-даты (по одной на день; пустой день → крестик). Рельса рисуется JS-оверлеями
   (.frail-seg) поверх измеренной раскладки (render-day-web.js → paint): сегменты
   рвутся у каждого маркера (зазор вокруг), но линия непрерывна — идёт через все дни
   подряд. Толщина рельсы = --frail-w, цвет — --frail-color (светлее маркеров). День
   без совпадений = только строка-дата, приглушённая вдвое (.frow-date.is-empty), без
   карточек. Прежних волн-разрывов, чисел «дельты» и блоков-заглушек больше нет.
   (Пробовали вместо точки/крестика иконку календаря — откатили, вернулись сюда.) */
.feed {
  --fdisc: 22px;                 /* ширина бокса маркера-даты (центрирование на рельсе) */
  --fdot-d: 10.35px;             /* бокс крестика; JS уточняет = 98% высоты цифры + 15% */
  /* x центра рельсы = радиус маркера → ЛЕВЫЙ край кружка/крестика совпадает с левой
     гранью годового календаря (левый край .feed = граница year-strip). */
  --frail: calc(var(--fdot-d) / 2);
  --frail-w: 1px;                /* толщина рельсы */
  /* Рельса и маркеры — каждый затемнён на 15% (RGB × 0.85) от своего исходного
     цвета: рельса #bcbeb1, точка --ink-3 (#6c7062), крестик --ink-4 (#9a9d8e).
     Хардкод-хексы, а не color-mix: у custom-property нет рабочего фолбэка (старый
     браузер съел бы color-mix как валидный токен и обнулил цвет при использовании). */
  --frail-color: #a0a296;        /* рельса — светлее маркеров */
  --fdot-color: #5c5f53;         /* кружок дня с сохранениями */
  --fcross-color: #838579;       /* крестик пустого дня */
  position: relative;
}
.frow { position: relative; }
/* Отрезок рельсы (JS выставляет top/height инлайном). Центр — на --frail. */
.frail-seg {
  position: absolute;
  left: calc(var(--frail) - var(--frail-w) / 2);
  width: var(--frail-w);
  background: var(--frail-color);
}

/* Заголовок группы даты. Маркер — серая точка (.fdot) в боксе .fslash, её центр
   сидит на рельсе и по вертикали центрирован с датой; сама дата слева, «сегодня» —
   жирным. Между группами — вертикальный зазор (не первая группа). */
.frow-date { padding: 6px 0; }
/* День без совпадений — маркер и подпись приглушаем: opacity накрывает маркер
   и текст равномерно. Держим ЧУТЬ светлее (0.4), чтобы пустые дни сильнее
   уходили на фон, но подпись даты оставалась читаемой. Opacity — на ДЕТЯХ
   .feed-date, а не на строке: точка черновика (.feed-draft-dot) должна
   оставаться чёрной и на пустом дне, а из-под opacity родителя ребёнка не
   вытащить. Плюс тот же «воздух» снизу, что после последней карточки дня
   (.frow-msg.day-end): у этой строки паддинг-низ 6px против 5px у карточки,
   поэтому маргин на 1px меньше — суммарный зазор до следующей даты одинаковый
   (30px). */
.frow-date.is-empty { margin-bottom: 24px; }
.frow-date.is-empty .feed-date > *:not(.feed-draft-dot) { opacity: 0.4; }
/* День С совпадениями — дату (и её точку-маркер ниже) делаем чуть темнее
   обычных подписей, чтобы матч-дни читались на фоне пустых. */
.frow-date:not(.is-empty) .feed-date { color: var(--ink-2); }
.feed-date {
  display: flex;
  align-items: center;           /* точку центрируем по вертикали относительно даты */
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 600;
}
.feed-date .fslash {
  flex: none;
  width: var(--fdisc);
  margin-left: calc(var(--frail) - var(--fdisc) / 2);   /* центр бокса = --frail (рельса) */
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Кружок (день с совпадениями) — в 1.5 раза МЕНЬШЕ крестика (--fdot-d/1.5),
   центрирован на рельсе (левый край чуть правее крестика — это ок, бусина на нити). */
.feed-date .fdot {
  width: calc(var(--fdot-d) / 1.5);
  height: calc(var(--fdot-d) / 1.5);
  border-radius: 50%;
  background: var(--fdot-color);
}
/* Крестик пустого дня — полный бокс/размер --fdot-d (левый край выровнен по левой
   грани года через --frail); dim-строка сверху ещё и гасит его opacity .4. */
.feed-date .fcross {
  width: var(--fdot-d);
  height: var(--fdot-d);
  color: var(--fcross-color);
  line-height: 0;
}
.feed-date .fcross svg { width: 100%; height: 100%; display: block; }
.feed-date .feed-today { font-weight: 800; color: var(--ink); }   /* «сегодня» — жирным */
/* Подпись даты — ЕДИНСТВЕННЫЙ вход в день из ленты (карточки сообщений туда
   больше не ведут, см. render-day-web.js → dateRow/feedCard). Кликается и у
   ПУСТОГО дня — открывается пустой день календаря. Падинг растит хит-зону под
   палец, равный ему отрицательный маргин гасит вклад в высоту строки (подпись —
   flex-элемент .feed-date, иначе ряд бы подрос). */
.feed-date-link {
  cursor: pointer;
  padding: 5px 7px;
  margin: -5px -7px;
  border-radius: 6px;
  transition: color 0.12s ease, background 0.12s ease;
}
.feed-date-link:hover { color: var(--ink); }
.feed-date-link:active { background: var(--bg-2); }
/* Черновик в дне (web_entries.is_draft, флаг has_draft из /web/history — тот же
   источник, что у .draft-dot на клетке календаря): точка-надстрочник в САМОМ
   конце строки даты, сразу после дня недели. Тот же размер/цвет, что на клетке
   (5px, --ink). Flex-сосед подписи (НЕ внутри её span'а): на пустом дне
   приглушаются только маркер и подпись — точка остаётся чёрной. */
.feed-date .feed-draft-dot {
  flex: none;
  align-self: flex-start;   /* к верху строки — «справа вверху от даты» */
  margin-top: -1px;         /* и ещё чуть выше верха line-box */
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--ink);
  margin-left: 4px;
}

/* Карточка сообщения — сдвинута СЛЕВА, правый край на месте (выровнен по правой
   грани года). Отступ ФИКСИРОВАННЫЙ и отсчитывается ОТ РЕЛЬСЫ: ровно 10px зазора
   между линией таймлайна и рамкой карточки на любой ширине. Раньше было 2.5%
   ширины ленты — на мобильном (лента ~400px против ~690px на десктопе) это
   съедало зазор до 3px, карточка почти липла к рельсе. Контент переносится по
   словам. */
.frow-msg { padding: 5px 0; }
.frow-msg .feed-msg { margin-left: calc(var(--frail) + 10px); }
/* Воздух после КРАЙНЕГО сообщения дня — отделяет блок дня от следующей даты.
   Класс ставит render-day-web.js на последнюю карточку дня. Суммарный зазор
   (паддинг-низ строки + маргин) = 30px — ровно такой же, как у пустых строк-дат
   (.frow-date.is-empty, там паддинг 6px и маргин 24px): «единица» ленты (день)
   всегда заканчивается одинаковым воздухом. */
.frow-msg.day-end { margin-bottom: 25px; }
/* Отбив шапки (огонёк+время) от текста в ленте — ВДВОЕ против базовых 6px
   (в дне — 1.5×, см. history-day-web.css): карточки ленты стоят реже и
   крупнее, шапке нужно больше воздуха. Действует и на черновики — шапка у
   них та же (.feed-msg покрывает оба вида). */
.feed-msg .msg-head { margin-bottom: 12px; }
.feed-msg-link { cursor: pointer; transition: border-color 0.12s ease, background 0.12s ease; }
.feed-msg-link:hover { border-color: var(--ink-3); }
/* Прижатый фон — только когда жмут САМУ карточку: :active поднимается с
   нажатого элемента на предков, и без :not(:has(...)) тап по «⋮» черновика
   (или по пункту его меню) мигал бы фоном всей карточки. Та же поправка, что у
   плашки черновика в дне (.day-draft, history-day-web.css). */
.feed-msg-link:active:not(:has(.msg-menu-wrap :active)) { background: var(--bg-2); }
/* Черновик в ленте (вкладки «Лента» без запроса и «Черновики»): серый пунктир
   вместо сплошной рамки + жёлтая метка «черновик» в шапке карточки — та же
   пара сигналов, что у плашки черновика на экране дня (.day-draft*, стили
   метки — в history-day-web.css). Текст приглушаем: черновик — ещё не запись. */
.feed-msg.is-draft { border-style: dashed; border-color: var(--ink-4); }
.feed-msg.is-draft .msg-body { color: var(--ink-3); }

/* Подсветка совпадения в тексте сообщения — как сегодняшний день на календаре
   (жёлтая полоса снизу + тонкая линия). Сбрасываем дефолтный жёлтый фон <mark>;
   box-decoration-break — чтобы подсветка переносилась по строкам. */
.msg-body mark.hl-match {
  background-color: transparent;
  color: inherit;
  background-image:
    linear-gradient(var(--hl-yellow), var(--hl-yellow)),
    linear-gradient(var(--ink), var(--ink));
  background-size: 100% 6px, 100% 1px;
  background-position: 0 calc(100% - 1px), 0 100%;
  background-repeat: no-repeat;
  padding-bottom: 1px;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

/* Легенда типов */
.legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 14px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  background: var(--paper);
  margin-top: 12px;
  font-size: 11px;
  color: var(--ink-2);
}
.legend .leg {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.legend .dot {
  width: 8px; height: 8px; border-radius: 50%;
}

/* Mini popover-календарь (открывается из app-bar) */
.popover {
  position: fixed;
  inset: 0;
  background: rgba(21, 23, 15, 0.4);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 70px 16px 16px;
  z-index: 100;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}
.popover.open { opacity: 1; pointer-events: auto; }
.popover .pop-inner {
  width: 100%;
  max-width: 380px;
  background: var(--paper);
  border: 1px solid var(--ink);
  padding: 12px;
  transform: translateY(-6px);
  transition: transform 0.15s ease;
}
.popover.open .pop-inner { transform: translateY(0); }

@media (max-width: 480px) {
  .wrap { padding: 12px 10px 60px; }
  .cal-cell { padding: 3px 4px; }
  .cal-cell .day-num { font-size: 12px; }
  .cal-cell .dot { width: 5px; height: 5px; }
  .cal-cell .fire { width: 12px; height: 12px; }
  .cal-cell .no-save-cross { width: 12px; height: 12px; }
}

/* ══════════════════════════════════════════════════════════════════════
   ТЁМНАЯ ТЕМА КАЛЕНДАРЯ (design_handoff_calendar_dark)
   ─────────────────────────────────────────────────────────────────────
   Всё под `body.theme-dark` — классом, который renderCal вешает на <body>
   на вкладках дневника (Календарь / Лента / Черновики) и снимают renderDay и
   переход в настройки. Токены (--ink/--paper/--d-*) объявлены там же,
   в <style> web.html; сюда прилетают готовыми.

   Правила выше НЕ трогаем: они обслуживают светлую половину приложения, и
   те же классы (.fchip, .fp-…, .fdrawer) живут на страницах настроек. Всё, что
   ниже, — только про экраны хендоффа.
   ⚠️ Звёздочку со слешом подряд внутри этих комментариев не писать: она
   закрывает комментарий досрочно, и следующее за ним правило пропадает.
   ═════════════════════════════════════════════════════════════════════ */

/* ── Шапка: [бургер 42] ——— заголовок по центру ——— [воронка 42] ──
   Год в заголовке — моно и цианом, В ОДНУ строку с месяцем (в светлой теме
   он стоял второй строкой и весь блок прижимался вправо). */
body.theme-dark .cal-hero {
  gap: 8px;
  align-items: center;
  margin-bottom: 14px;
}
body.theme-dark .cal-hero .hero-title {
  flex: 1 1 auto;
  margin-left: 0;
  min-width: 0;
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 8px;
  text-align: center;
}
body.theme-dark .cal-hero h1 {
  font-family: var(--d-ui);
  font-size: 24px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.01em;
  color: var(--ink);
}
body.theme-dark .cal-hero .hero-year {
  font-family: var(--d-mono);
  font-size: 14px;
  font-weight: 400;
  line-height: 1;
  letter-spacing: 0;
  color: var(--d-accent);
}
/* Режим ленты: заголовок = запрос, под ним счётчик — там колонка, не строка. */
body.theme-dark .cal-hero.is-search .hero-title {
  flex-direction: column;
  align-items: center;
  gap: 2px;
}
body.theme-dark .cal-hero.is-search h1 { max-width: 100%; }
body.theme-dark .cal-hero .month-sub {
  font-family: var(--d-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--d-label);
  margin-top: 0;
}

/* Воронка фильтра — правый элемент шапки. Пассивная: круг 42px «приподнятой»
   заливки. Применённая (.is-applied): циановая ПИЛЮЛЯ «воронка + число». */
body.theme-dark .cal-hero .fb-toggle {
  width: 42px;
  height: 42px;
  min-width: 0;
  padding: 0;
  gap: 7px;
  border: 1px solid var(--d-border-2);
  border-radius: 50%;
  background: var(--d-raise);
  color: #bfe9f5;
  transition: background 0.1s ease, color 0.1s ease, border-color 0.1s ease;
}
body.theme-dark .cal-hero .fb-toggle svg { width: 16px; height: 16px; }
body.theme-dark .cal-hero .fb-toggle:active {
  background: var(--d-accent);
  border-color: var(--d-accent);
  color: var(--d-on-accent);
}
body.theme-dark .cal-hero .fb-toggle.is-applied {
  width: auto;
  padding: 0 14px 0 13px;
  border-radius: 999px;
  background: var(--d-accent);
  border-color: var(--d-accent);
  color: var(--d-on-accent);
}
body.theme-dark .cal-hero .fb-toggle .fb-cnt {
  padding: 0;
  background: none;
  color: var(--d-on-accent);
  font-family: var(--d-mono);
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
}

/* Крестик полного сброса — спутник воронки в шапке (только при применённом
   фильтре). Пассивный вид — тот же «приподнятый» круг, что у пассивной
   воронки, но вдвое меньше и подписью-глифом цвета моно-меток; нажатый —
   циан, как у всех активных элементов тёмной темы.
   ⚠️ Ряда чипов применённого фильтра тут больше НЕТ (снят 2026-08-13):
   .fchip-row / .fchip остались только у фильтров светлых страниц настроек,
   поэтому их тёмных переопределений здесь тоже нет. */
body.theme-dark .filter-wrap { margin-bottom: 0; }
body.theme-dark .cal-hero .fb-reset {
  border: 1px solid var(--d-border-2);
  background: var(--d-raise);
  color: var(--d-label);
  font-family: var(--d-ui);
  transition: background 0.1s ease, color 0.1s ease, border-color 0.1s ease;
}
body.theme-dark .cal-hero .fb-reset:active {
  background: var(--d-accent);
  border-color: var(--d-accent);
  color: var(--d-on-accent);
}

/* ── Year-strip — карточка r18. Клетка = день года, заливка по МАКСИМАЛЬНОЙ
   интенсивности дня. Текущий месяц не обводим рамкой-прямоугольником: каждая
   его клетка получает неоновый контур, и месяц читается по своим клеткам. */
body.theme-dark .year-strip {
  padding: 12px 14px;
  margin-bottom: 14px;
  border: 1px solid var(--d-border);
  border-radius: 18px;
  background: var(--d-card);
}
body.theme-dark .year-strip .ys-head {
  align-items: center;
  margin-bottom: 8px;
  font-family: var(--d-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: none;
  color: var(--d-label);
}
body.theme-dark .year-strip .ys-year { gap: 10px; }
/* ⚠️ ОПТИЧЕСКОЕ выравнивание строки «год ‹ 2026 ›» по вертикали (2026-08-17).
   `align-items: center` у .ys-head уже центрирует БОКСЫ — замер даёт совпадение
   до 0.01px, и трогать раскладку не нужно. Разъезжаются ЧЕРНИЛА внутри боксов:
   кегли разные (10 / 12 / 15px) и классы глифов разные — строчные с выносным
   «д», цифры, пунктуация. Каждый глиф сидит в своём em-боксе по-своему.
   Замер по «плотной» полосе чернил (порог 30% от массы строки — отсекает тонкие
   ножки «д», иначе они утянули бы центр подписи вниз) против полосы цифр года:
     • «год»    выше центра цифр на 0.40px  → опускаем
     • «‹ ›»    ниже центра цифр на 1.15px  → поднимаем
   transform, а не `position: relative`: не заводим позиционный контекст ради
   сдвига в доли пикселя. Величины привязаны к ТЕКУЩИМ кеглям — меняешь
   font-size у .ys-nav / .ys-head, перемеряй и эти два числа. */
body.theme-dark .year-strip .ys-label { transform: translateY(0.4px); }
body.theme-dark .year-strip .ys-nav   { transform: translateY(-1.15px); }
body.theme-dark .year-strip .ys-head .yr {
  font-family: var(--d-mono);
  font-size: 12px;
  font-weight: 600;
  color: var(--ink);
}
body.theme-dark .year-strip .ys-nav {
  padding: 0;
  border: none;
  background: none;
  color: var(--d-label);
  font-family: var(--d-mono);
  /* Стрелки года — на 25% крупнее прежних 12px (2026-08-17). Строка `год ‹ 2026 ›`
     выровнена по центру (.ys-head align-items: center), поэтому подпись «год» и
     само число от подросшего глифа не съезжают. */
  font-size: 15px;
  line-height: 1;
  font-weight: 600;
}
body.theme-dark .year-strip .ys-nav:active { background: none; color: var(--d-accent); }
body.theme-dark .year-strip .ys-grid { gap: 2px; padding: 3px 0; }
/* Только background-COLOR — шорткат `background` сбросил бы background-clip
   из базового правила, и квадрат перестал бы быть на 10% меньше клетки. */
body.theme-dark .year-strip .ys-cell {
  border: none;
  border-radius: 1px;
  background-color: #14313e;
}
body.theme-dark .year-strip .ys-cell.lvl-1 { background-color: #1d5468; }
body.theme-dark .year-strip .ys-cell.lvl-2 { background-color: #2a86a5; }
body.theme-dark .year-strip .ys-cell.lvl-3 { background-color: var(--d-accent); }
body.theme-dark .year-strip .ys-cell.is-current-month {
  outline: none;
  box-shadow: 0 0 0 0.5px rgba(74, 215, 245, 0.85), 0 0 5px rgba(74, 215, 245, 0.5);
}

/* ── Ряд «сегодня ——— ‹ ›» (название месяца ушло в шапку). */
body.theme-dark .month-nav {
  align-items: center;
  margin: 0 0 14px;
}
body.theme-dark .month-nav .nav-right { display: inline-flex; gap: 8px; margin-left: auto; }
body.theme-dark .month-nav .nav-btn {
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid var(--d-border);
  border-radius: 50%;
  background: var(--d-neutral);
  color: #bfe9f5;
  font-family: var(--d-ui);
  /* Глиф ‹ › на 25% крупнее прежних 14px (2026-08-17) — сам круг 34px не
     трогали, растёт только стрелка внутри него. Центрирование держится на
     flex-центре родителя (.nav-btn — inline-flex, center/center) плюс
     line-height: 1: без него line-box наследует `normal` и на крупном кегле
     съезжает по вертикали внутри круга. */
  font-size: 17.5px;
  line-height: 1;
  font-weight: 400;
}
/* Глиф ‹ › сидит в своём em-боксе НЕ по центру: ink самой чёрточки уходит
   ниже середины (у Manrope на кегле 17.5px — на 0.9px, замерено скриншотом
   кнопки). Flex-центр родителя ровняет line-box, а не ink, поэтому стрелка
   визуально «опущена» в круге. Поднимаем padding-bottom'ом, опираясь на
   box-sizing: border-box из светлого .nav-btn: контент-бокс ниже на 2px →
   flex-центр уезжает вверх, диаметр круга 34px не меняется. 2px — середина
   плато (1 и 2 дают одинаковый растр, с 3px стрелка уже задрана на пиксель
   выше центра); итог совпадает с ink-центром иконки .today-btn. Меняешь
   font-size / диаметр — перемеряй. .today-btn под правило не попадает:
   там SVG, он центрируется сам. */
body.theme-dark .month-nav .nav-btn:not(.today-btn) { padding-bottom: 2px; }
/* Кнопка «на текущий месяц» — ТОТ ЖЕ круг 34px, что и стрелки: с 2026-08-14
   в ней иконка-мишень вместо подписи «сегодня» словом (пилюли с текстом в
   ряду больше нет — три равные круглые кнопки читаются ровнее). Своих
   размеров у .today-btn поэтому не осталось, только переход цвета и размер
   глифа. */
body.theme-dark .month-nav .today-btn {
  transition: background 0.1s ease, color 0.1s ease, border-color 0.1s ease;
}
/* Иконка-прицел мельче кнопки против прежних 17px (2026-09-02, по запросу
   пользователя «в полтора раза»): у crosshair из пака PROP_ICONS засечки уходят в
   самый край вьюбокса, поэтому на прежнем кегле пятно было плотнее соседних шевронов.

   ⚠️ Кегль держать ЧЁТНЫМ. Кнопка — 34px (чётная), flex-центр даёт иконке отступ
   (34 − size) / 2: на чётном size это целый пиксель, на нечётном/дробном — половина
   или четверть, и растр съезжает. Замеряно скриншотом (dsf 8, поля ink внутри диска):
   11.5px → L5.50 R5.00 T5.50 B5.00 (сдвиг 0.25px влево-вверх, видно глазом),
   11px → 0.5px вправо-вверх, 13px → 0.5px мимо; 12px и 10px → поля равны со всех
   сторон. Поэтому 12px, а не «ровно 17 / 1.5 = 11.33»: точное деление попадает мимо
   пиксельной сетки. Меняешь диаметр кнопки — пересчитай чётность. */
body.theme-dark .month-nav .today-btn svg { width: 12px; height: 12px; display: block; }
body.theme-dark .month-nav .nav-btn:active {
  background: var(--d-accent);
  border-color: var(--d-accent);
  color: var(--d-on-accent);
}

/* ── Сетка месяца — одна карточка r24 без внутренних линий (в светлой теме
   сетку рисовали зазоры на чёрной подложке). Клетка — колонка по центру:
   [число] [огоньки | крестик], фиксированной высоты. */
body.theme-dark .cal-grid {
  gap: 0;
  padding: 12px 10px;
  border: 1px solid var(--d-border);
  border-radius: 24px;
  background: var(--d-card-2);
}
body.theme-dark .cal-dow {
  padding: 0 0 8px;
  background: none;
  color: var(--d-label);
  font-family: var(--d-ui);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
}
body.theme-dark .cal-cell {
  aspect-ratio: auto;
  height: 52px;
  padding: 5px 0 0;
  gap: 4px;
  align-items: center;
  justify-content: flex-start;
  background: none;
}
body.theme-dark .cal-cell.outside,
body.theme-dark .cal-cell:active { background: none; }
body.theme-dark .cal-cell .day-top { align-self: center; }
body.theme-dark .cal-cell .day-num {
  font-family: var(--d-mono);
  font-size: 13px;
  font-weight: 500;
  line-height: 20px;
  letter-spacing: 0;
  color: var(--ink-2);
}
body.theme-dark .cal-cell.outside .day-num { color: var(--d-dim); }

/* Сегодня — ЧИСЛО В АКЦЕНТНОЙ ПЛАШКЕ, ТЁМНЫМ. История: циановый квадрат
   заливкой за числом → тонкая рамка вокруг всей КЛЕТКИ (сняли: обводила заодно
   огоньки и точку черновика, т.е. акцентировала клетку, а не дату) → цвет
   акцента + подчёркивание → прямоугольник с обводкой и утемнённой заливкой под
   белое число. Сейчас — заливка и грань одним --d-accent, число --d-on-accent
   (#0a1219): белое на #4ad7f5 давало контраст ~1.6:1 и пропадало, тёмное на нём
   — ~9:1. Это та же пара «акцент + контент на акценте», что у активной вкладки
   и FAB.
   Плашка висит на .day-num (не на .cal-cell и не на .day-top) — это и есть
   «только число»: огоньки, крестик пустого дня и точка черновика остаются
   снаружи. Точка приколота к правому краю .day-top, ширина которого = ширина
   числа с падингами, — значит она отступает от плашки, а не наезжает на неё.
   ФОРМА — КВАДРАТ (правка 2026-08-14, было 27.6×22 «лежащим» прямоугольником):
   высота плашки = line-height 20 + рамки = 22px, поэтому боковой падинг ужат
   до 3px (двузначное число → 23.6px ширины), а min-width 22px не даёт
   однозначному дню («5») схлопнуться в узкую полоску — с ним плашка ровно
   22×22. text-align центрирует цифру в этой ширине. */
body.theme-dark .cal-cell.today .day-num,
body.theme-dark .cal-cell.no-match.today .day-num {
  position: relative;
  padding: 0 3px;
  min-width: 22px;
  text-align: center;
  border: 1px solid var(--d-accent);
  border-radius: 4px;
  background-image: none;
  background-color: var(--d-accent);
  color: var(--d-on-accent);
  font-weight: 600;
  text-decoration: none;
}
/* Точка черновика — циановая, справа-сверху от числа. У сегодняшнего дня она
   ТА ЖЕ, что у остальных: заливки под ней больше нет, и тёмная точка
   (--d-on-accent, читалась чёрной) на карточке просто пропадала. */
body.theme-dark .cal-cell .draft-dot { background: var(--d-accent); }

/* Крестик пустого прошедшего дня — В ПОТОКЕ, под числом (в светлой теме он
   стоял абсолютом по центру клетки). Пустой ряд огоньков при этом схлопываем,
   иначе к зазору добавился бы второй gap.
   ⚠️ Крестик занимает РОВНО тот же слот колонки, что ряд огоньков: его бокс =
   боксу огонька (12×12) и собственного отступа у него нет — вертикаль ему
   задаёт только gap клетки, тот же самый, что и .dots. Поэтому в одной неделе
   крестики и огоньки стоят на одной оси. Было 11×11 + margin-top:4px — крестик
   висел на 3.5px ниже огоньков соседних дней, и в строке это было видно.
   Не возвращать ни свой margin, ни размер «покомпактнее»: и то, и другое
   сдвигает крестик относительно огоньков. Рисунок X внутри бокса центрирован
   (path 2.5→9.5 в viewBox 12), силуэт огонька — тоже, так что совпадение
   боксов = совпадение глифов. */
body.theme-dark .cal-cell .dots {
  gap: 1px;
  max-width: 40px;
  min-height: 0;
  justify-content: center;
}
body.theme-dark .cal-cell .dots:empty { display: none; }
body.theme-dark .cal-cell .no-save-cross {
  position: static;
  transform: none;
  width: 12px;
  height: 12px;
  margin-top: 0;
  color: var(--d-cross);
}

/* Огоньки: шкала = СТЕПЕНЬ ЗАЛИВКИ силуэта (как в светлой базе выше и как в
   панелях ввода), а не прозрачность:
     1 — пустой (только контур);
     2 — залит чуть меньше половины (клип inset(69.08%) — краска снизу на
         30.92% высоты: 35% → −7% (2026-09-02) → 32.55% → −5% (2026-09-03),
         проценты относительные);
     3 — залит целиком.
   ⚠️ Контур у всех трёх — ОДНОГО цвета (--d-accent на полной плотности) и
   огоньки одного размера: пробовали гасить «1» и «2» группой до --fire-mid
   (+ у «2» заливать силуэт целиком) — откатили по просьбе юзера 2026-09-02,
   уровни различались тоном, а не количеством краски. Тёмная тема добавляет к
   базе только размер (12px), цвет и толщину линии — клипы .lN не переопределяет.
   Правится вместе с такими же правилами лент/дня (.feed-msg ниже,
   .msg-list/.day-draft в history-day-web.css) — шкала должна выглядеть
   одинаково везде. В панелях ввода и в фильтре свой вид (три пламени
   meter-режима), туда это правило не заходит. */
body.theme-dark .cal-cell .fire { width: 12px; height: 12px; color: var(--d-accent); }
body.theme-dark .cal-cell .fire .fire-line path { stroke-width: 1.6; }

/* Фильтр: у совпавших дней число ярче и жирнее, у несовпавших — гаснет до
   --d-dim; масштабирования числа (светлая тема) в макете нет. Крестики при
   активном фильтре и так не рисуются (см. renderMonthGrid). */
body.theme-dark .cal-cell.has-match .day-top { transform: none; }
body.theme-dark .cal-cell.has-match .day-num { color: var(--ink); font-weight: 700; }
body.theme-dark .cal-cell.outside.has-match .day-num { color: var(--ink); }
body.theme-dark .cal-cell.no-match .day-num { color: var(--d-dim); font-weight: 500; }
/* Сегодня переживает поиск: у совпавшего дня число светлеет до --ink, а на
   акцентной плашке светлое число пропало бы (контраст ~1.6:1). Держим
   --d-on-accent — и в своём месяце, и в хвосте чужого (сегодняшняя дата видна
   в сетке соседнего месяца). Несовпавший день (.no-match) уже накрыт правилом
   плашки выше — там селектор на класс длиннее (0,4,0). */
body.theme-dark .cal-cell.today.has-match .day-num,
body.theme-dark .cal-cell.outside.today.has-match .day-num {
  color: var(--d-on-accent);
}

/* ══════════════════════════════════════════════════════════════════════
   ЛЕНТА — ТЁМНАЯ (design_handoff_feed_dark, экран 09)
   ─────────────────────────────────────────────────────────────────────
   Вкладки «Лента» / «Черновики». Всё заскоплено `body.theme-dark`, потому что
   те же классы (.msg, .msg-head, .msg-time, .msg-body, .msg-tok, .msg-more,
   .msg-menu, .day-draft-tag) рисуют СВЕТЛЫЙ экран дня — его макета в хендоффах
   нет, и разъезжаться он не должен.
   ═════════════════════════════════════════════════════════════════════ */

/* Геометрия таймлайна из макета: колонка маркера 22px, рельса 2px по её центру
   (x = 11px), подпись даты и карточка — на 12px правее колонки (левый край
   34px). Прежняя привязка «левый край крестика = левая грань year-strip» тут
   не действует: в лентах year-strip'а нет вовсе, выравниваться не по чему.
   Размеры маркеров — фиксированные px макета (точка 7, крестик 11); JS-замер
   --fdot-d (98% высоты цифры + 15%) в тёмной теме не участвует. */
body.theme-dark .feed {
  --fdisc: 22px;
  --frail: 11px;
  --frail-w: 2px;
  --frail-color: #21414f;    /* = --d-border */
  --fdot-color: #9db8c4;     /* = --ink-3, день с записями */
  --fcross-color: #4a6b7c;   /* пустой день: светлее рельсы, темнее точки */
}
body.theme-dark .feed-date .fdot { width: 7px; height: 7px; }
body.theme-dark .feed-date .fcross { width: 11px; height: 11px; }

/* Строка-дата — моно 12, без капса и разрядки. День с записями светлый
   (--ink-2), пустой — приглушённый ЦВЕТОМ (--d-dim), а не opacity: в макете у
   пустой даты свой тон, а не полупрозрачный основной. */
body.theme-dark .feed-date {
  gap: 12px;                 /* воздух между колонкой маркера и подписью */
  font-family: var(--d-mono);
  font-size: 12px;
  font-weight: 400;
  line-height: 17px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d-dim);
}
body.theme-dark .frow-date:not(.is-empty) .feed-date { color: var(--ink-2); }
body.theme-dark .frow-date.is-empty .feed-date > *:not(.feed-draft-dot) { opacity: 1; }
body.theme-dark .feed-date .feed-today { font-weight: 700; color: inherit; }
/* Тап по дате — акцентный цвет и лёгкая подложка (у пустой даты тоже: в дне
   без записей запись как раз и добавляют). */
body.theme-dark .feed-date-link:hover { color: var(--d-accent); }
body.theme-dark .feed-date-link:active {
  color: var(--d-accent);
  background: rgba(74, 215, 245, 0.1);
}

/* Вертикальный ритм макета: 10px между сообщениями одного дня, 6px от строки-
   даты до первой карточки (это её собственный нижний паддинг), 40px от крайней
   карточки/пустой даты до следующей даты (34 + 6 верхнего паддинга той даты).
   Держим паддингами, а не маргинами: маргин соседа схлопывался бы. */
body.theme-dark .frow-msg { padding: 0 0 10px; }
body.theme-dark .frow-msg.day-end { margin-bottom: 0; padding-bottom: 34px; }
body.theme-dark .frow-date.is-empty { margin-bottom: 0; padding-bottom: 34px; }
body.theme-dark .frow-msg .feed-msg { margin-left: calc(var(--fdisc) + 12px); }

/* Карточка сообщения — скруглённая (r16) карточка-градиент с рамкой. */
body.theme-dark .feed-msg {
  padding: 12px 14px;
  border: 1px solid var(--d-border);
  border-radius: 16px;
  background: var(--d-card);
}
body.theme-dark .feed-msg .msg-head { margin-bottom: 11px; }
body.theme-dark .feed-msg .msg-head-left { gap: 8px; }
body.theme-dark .feed-msg .msg-time {
  font-family: var(--d-mono);
  font-size: 11px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--d-label);
}
/* Огонёк интенсивности — циановый, шкала = степень заливки (пусто → почти
   половина → целиком, клипы .int-stop.lN из базы web.html). Контур у всех трёх
   уровней одного цвета и размера — обоснование и вся шкала в комментарии у
   .cal-cell .fire выше. */
body.theme-dark .feed-msg .int-flame,
body.theme-dark .feed-msg .int-flame svg { width: 14px; height: 14px; }
body.theme-dark .feed-msg .int-stop.on { color: var(--d-accent); }
/* Текст — пропорциональный (Manrope), просторный интерлиньяж макета. */
body.theme-dark .feed-msg .msg-body {
  font-family: var(--d-ui);
  font-size: 13px;
  line-height: 1.9;
  color: var(--ink-2);
}
/* ⚠️ Пилюль-тегов в ленте НЕТ. В макете пункты словаря нарисованы пилюлями —
   это ОШИБКА ДИЗАЙНА (подтверждено заказчиком): в реальном сообщении пунктов
   десятки, и текст превращался в сплошную ленту чипов. Просмотр в ленте — тот
   же, что в дне: активность жирным, локация/тренер — иконкой слева (без
   жирного), остальное обычным текстом. Правила токенов ОБЩИЕ для дня и ленты
   и живут в history-day-web.css (body.theme-dark .msg-tok*, БЕЗ скоупа
   поверхности) — своих тут нет намеренно, иначе поверхности разъезжаются.
   Пилюли остаются только в РЕДАКТИРОВАНИИ (.tok в инбоксе и панели-редакторе).

   Подсветка совпадения поиска — ЦИАНОВАЯ линия под словом вместо жёлтого
   «маркера» светлой темы (тот на тёмном фоне читается как чужеродное пятно).
   Толщина 3px = втрое толще штриха Manrope 400 на 13px (1px, с него начинали —
   читалось слишком тихо).

   ⚠️ Линия рисуется ФОНОВЫМ ГРАДИЕНТОМ, а НЕ text-decoration: underline —
   не откатывать. WebKit (Safari, WKWebView iOS-приложения) не красит
   подчёркивание под ПРОБЕЛАМИ-разделителями слов, и у многословного совпадения
   («жим лёжа») линия разваливалась на куски по словам; ни
   `text-decoration-skip-ink: none`, ни `-webkit-text-decoration-skip`
   это не лечат (проверено на webkit: остаётся дыра ~2px на пробеле).
   В chromium (веб, Android) обе техники рисуют одинаково — градиент выбран как
   единственный вариант, сплошной ВЕЗДЕ. Геометрия подобрана 1-в-1 под прежний
   underline (3px толщиной, на 3px ниже базовой линии): полоса лежит в
   padding-bottom, поэтому высоту строки не меняет. box-decoration-break: clone —
   чтобы совпадение, разорванное переносом строки, было подчёркнуто на обеих. */
body.theme-dark .feed .msg-body mark.hl-match {
  background-color: transparent;
  color: inherit;
  background-image: linear-gradient(var(--d-accent), var(--d-accent));
  background-size: 100% 3px;
  background-position: 0 calc(100% - 1px);
  background-repeat: no-repeat;
  padding-bottom: 3px;
  text-decoration: none;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

/* Черновик — та же карточка пунктиром на полупрозрачной подложке, текст
   приглушён; метка «черновик» — циановая пилюля со СПЛОШНОЙ рамкой (жёлтая
   плашка светлого дня в тёмной ленте выпадала бы из гаммы). Пунктир остаётся
   только у самой карточки: два пунктира вложенно читались грязно. */
body.theme-dark .feed-msg.is-draft {
  border: 1px dashed #2b4a59;
  background: rgba(18, 38, 49, 0.5);
}
body.theme-dark .feed-msg.is-draft .msg-body { color: var(--ink-3); }
body.theme-dark .feed-msg .day-draft-tag {
  padding: 2px 8px;
  border: 1px solid #2a86a5;
  border-radius: 999px;
  background: none;
  color: var(--d-accent);
  font-family: var(--d-mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: none;
}
body.theme-dark .feed-msg-link:active:not(:has(.msg-menu-wrap :active)) {
  background: var(--d-raise);
}

/* «⋮» черновика — в ленте кнопка В РАМКЕ (26×26, r8): на карточке-градиенте
   голые точки терялись. В светлом дне рамки по-прежнему нет. */
body.theme-dark .feed-msg .msg-more {
  border: 1px solid var(--d-border-2);
  border-radius: 8px;
  background: var(--d-neutral);
  color: var(--ink-3);
}
body.theme-dark .feed-msg .msg-more svg { width: 15px; height: 15px; }
body.theme-dark .feed-msg .msg-more:active,
body.theme-dark .feed-msg .msg-more[aria-expanded="true"] {
  border-color: var(--d-accent);
  color: var(--d-accent);
}
/* Выпадающее меню — попап r12 на тёмной поверхности; единственный пункт
   «удалить» сразу в danger-цвете (в светлом дне он красится по ховеру). */
body.theme-dark .feed .msg-menu {
  top: calc(100% + 2px);
  right: -6px;
  min-width: 0;
  padding: 4px;
  border: 1px solid var(--d-border-2);
  border-radius: 12px;
  background: #10222d;
  box-shadow: 0 12px 28px rgba(4, 10, 14, 0.6);
}
body.theme-dark .feed .msg-menu-item {
  gap: 8px;
  padding: 9px 16px 9px 11px;
  border-radius: 9px;
  font-family: var(--d-ui);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  white-space: nowrap;
  color: var(--ink-2);
}
body.theme-dark .feed .msg-menu-item + .msg-menu-item { border-top: none; }
body.theme-dark .feed .msg-menu-item.mi-del,
body.theme-dark .feed .msg-menu-item.mi-del:hover { color: #ff8a7a; }
body.theme-dark .feed .msg-menu-item:hover,
body.theme-dark .feed .msg-menu-item:active,
body.theme-dark .feed .msg-menu-item.mi-del:active {
  background: var(--d-neutral);
  color: #ff8a7a;
}

/* Шапка ленты: заголовок мельче календарного (20 против 24), счётчик под ним —
   моно цианом (в календаре тем же цветом набран год). */
body.theme-dark .cal-hero.is-search h1 { font-size: 20px; }
body.theme-dark .cal-hero.is-search .month-sub {
  font-size: 11px;
  letter-spacing: 0;
  text-transform: none;      /* «46 записей», а не «46 ЗАПИСЕЙ» — как в макете */
  color: var(--d-accent);
}

body.theme-dark .legend { border-color: var(--d-border); background: var(--d-card); }
