@charset "UTF-8";
/*
	Каскад сайта aiseen.cloud.

	base     — палитра, типографика, кнопки и поля (общее для всех экранов);
	public   — шапка, подвал, текстовые страницы;
	shell    — оболочка приложения: панель снизу, рельс слева, листы;
	ledger   — раскладка экранов кабинета /app/ (.app__*; наследство донора,
	           переименовать и почистить вместе с экранами aiseen).

	ios      — облик кабинета: палитра, шрифт, материалы, компоненты.

	shell идёт ПЕРЕД ledger: оболочка задаёт рамку, а ledger правит
	содержимое внутри неё, и правило содержимого должно уметь
	перебить правило рамки, а не наоборот.

	ios идёт ПОСЛЕДНИМ и целиком написан под body.shell: он переопределяет
	то, что base и ledger задали для витрины, и не может задеть её саму —
	этого класса на витрине нет. Разделение намеренное: ledger отвечает
	на вопрос «что где лежит», ios — «как это выглядит», и менять облик
	можно, не трогая раскладку.

	base и public переименованы из kartify-base и kartify-public.
	Донорские сборки (kartify-app, kartify-wizard, articles, planning)
	удалены 27.09.2026.

	Сборка: sass --no-source-map style/main.scss css/main.css
	(шапка подключает /css/main.css — см. Files::GetFileName для scss)
*/
/*
	База: переменные, сброс, типографика, кнопки, поля.

	Файл достался от донорского проекта и переименован из kartify-base
	вместе с чисткой (этап 0). Содержимое движковое — палитра, сетка,
	элементы форм — и переписывать его незачем: кабинет учёта строится
	на этих же переменных.

	═══════════════════════════════════════════════════════════════════
	ЧТО ИЗМЕНИЛОСЬ И ПОЧЕМУ
	═══════════════════════════════════════════════════════════════════

	Было: серо-синяя нейтраль и синий #2563EB — палитра по умолчанию,
	которая стоит на половине новых SaaS. Продукт от этого выглядел как
	чей угодно, а фотографии товара — главное, ради чего сюда приходят —
	лежали на холодном фоне и теряли в цвете.

	Стало:

	1. ФОН ТЁПЛЫЙ (#F6F4F1), а не голубовато-серый. Это не вкусовщина:
	   на тёплой бумаге предметная съёмка читается естественнее, холодный
	   фон уводит белый товара в синеву. Витрина, где товар выглядит
	   хуже, чем в жизни, противоречит тому, что продаёт сервис.

	2. АКЦЕНТ ЗЕЛЁНЫЙ (#0B7156), а не синий. Обещание продукта — «пройдёт
	   модерацию с первого раза». Зелёный и есть цвет «принято»: он
	   работает на смысл, а не просто заполняет роль «фирменного».
	   Заодно он далёк и от красного Kaspi (чтобы нас не приняли за
	   площадку), и от дежурного синего конкурентов.

	3. ЧЕРНИЛА ТЕПЛЕЕ И ГЛУБЖЕ (#12151A). Контраст с бумагой выше,
	   крупные заголовки держат вес.

	4. РАМОК СТАЛО МЕНЬШЕ. Раньше каждый блок был карточкой с бордером и
	   тенью — отсюда ощущение модульной сетки. Теперь блоки разделяет
	   воздух и смена фона, а рамка остаётся там, где она несёт смысл:
	   поля ввода, выбранный пресет, предупреждение.
*/
/* ── Шрифты ───────────────────────────────────────────────────────────
	Manrope — заголовки: геометрический гротеск с характером, полная
	кириллица. Inter — текст: он лучше держит мелкий кегль.
	Подключение — <link> в HeaderFooterView (один запрос на оба).
	В fallback-стеке системные шрифты той же плотности, чтобы страница
	до загрузки не прыгала.
*/
:root {
  --Font:"Inter", -apple-system, BlinkMacSystemFont, system-ui, "Segoe UI", sans-serif;
  --FontHead:"Manrope", "Inter", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --Ink:#12151A; /* основной текст */
  --InkSoft:#3B424B;
  --Muted:#787F89; /* подписи */
  --Line:#E6E3DE; /* тёплая линия */
  --LineSoft:#F0EDE8; /* линия внутри светлых блоков */
  --Bg:#FFFFFF;
  --BgSoft:#F6F4F1; /* тёплая бумага — фон страницы */
  --BgCard:#FFFFFF;
  --BgDeep:#12151A; /* тёмные секции: ими ломается монотонность ленты */
  --BgDeepSoft:#1E232B;
  --Accent:#0B7156; /* действие и «принято» */
  --AccentDark:#075742;
  --AccentSoft:#E8F2ED;
  --AccentInk:#0A5F49; /* акцент как текст на светлом */
  --Ok:#0B7156;
  --OkSoft:#E8F2ED;
  --Warn:#B45309;
  --WarnSoft:#FDF4E7;
  --Bad:#C22B2B;
  --BadSoft:#FBEDEC;
  --Radius:12px;
  --RadiusLg:20px;
  --RadiusXl:28px;
  /* тени тёплые и мягкие: холодная серая тень на тёплой бумаге грязнит */
  --Shadow:0 1px 2px rgba(28,22,14,.05), 0 2px 8px rgba(28,22,14,.05);
  --ShadowLg:0 12px 32px rgba(28,22,14,.10);
  --ShadowXl:0 28px 64px rgba(28,22,14,.16);
  --Gap:16px;
  --Max:1200px;
}

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

html, body {
  margin: 0;
  padding: 0;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  font-family: var(--Font);
  font-size: 16px;
  line-height: 1.6;
  color: var(--Ink);
  background: var(--BgSoft);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: "kern" 1, "liga" 1;
}

/*
	═══════════════════════════════════════════════════════════════════
	Страховка от классов страницы на <body>
	═══════════════════════════════════════════════════════════════════

	Ядро вешает на <body> класс с именем страницы (Kernel::FillPageInfo →
	AddBodyClass($PageInfo->Name)). Имена берутся из SystemPages: main,
	app, e404, sitemap.xml — и любые новые, которые заведут в админке.

	Значит, ЛЮБОЕ правило верхнего уровня, чьё имя совпало с именем
	страницы, попадает не только в свой блок, но и в body. Один такой
	промах ломает всю страницу разом, и выглядит это одинаково каждый раз:

	  — шапка не во всю ширину экрана,
	  — подвал не во всю ширину и не прижат к низу,
	  — полосы фона сверху и снизу от padding'а, попавшего в body.

	Искать причину по этим симптомам тяжело: они выглядят как ошибка
	в шапке и подвале, а виноват блок контента, о котором никто
	не думает. Так уже случилось с .auth в донорском проекте и с .app
	здесь.

	Правило ниже отменяет на body ровно те свойства, которыми блок
	контента превращает страницу в карточку. Селектор body[class]
	весит (0,1,1) и потому перебивает любой одиночный класс (0,1,0) —
	независимо от того, в каком файле каскада он написан.

	Это страховка, а не разрешение: блоки-контейнеры всё равно пишутся
	внутри #mb (см. ledger.scss). Здесь — чтобы следующая такая ошибка
	стоила криво выглядящего блока, а не всей страницы.
*/
body[class] {
  max-width: none;
  min-width: 0;
  width: auto;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  float: none;
  position: static;
}

a {
  color: var(--AccentInk);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

/*
	Заголовки — Manrope с отрицательным трекингом. Крупный кегль без
	поджатых межбуквенных получается рыхлым, особенно в кириллице.
*/
h1, h2, h3, h4 {
  font-family: var(--FontHead);
  margin: 0 0 12px;
  line-height: 1.14;
  font-weight: 800;
  letter-spacing: -0.022em;
  color: var(--Ink);
  text-wrap: balance;
}

h3, h4 {
  font-weight: 700;
  letter-spacing: -0.014em;
  line-height: 1.25;
}

small {
  font-size: 13px;
  color: var(--Muted);
}

::selection {
  background: var(--AccentSoft);
  color: var(--AccentDark);
}

:focus-visible {
  outline: 2px solid var(--Accent);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ── кнопки ───────────────────────────────────────────────────────────
	Вторичная кнопка — на бумаге с тонкой линией, а не белая коробка с
	тенью: рядом с главной она не должна спорить за внимание.
*/
.app__btn,
.pub__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 11px 20px;
  border: 1px solid var(--Line);
  border-radius: 999px;
  background: var(--Bg);
  color: var(--Ink);
  font-family: var(--Font);
  font-size: 15px;
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
  transition: background 0.16s ease, border-color 0.16s ease, color 0.16s ease, transform 0.16s ease;
}

.app__btn:hover,
.pub__btn:hover {
  background: var(--BgSoft);
  border-color: #D8D3CC;
  text-decoration: none;
}

.app__btn:active,
.pub__btn:active {
  transform: translateY(1px);
}

.app__btn--main,
.pub__btn--main {
  background: var(--Accent);
  border-color: var(--Accent);
  color: #FFF;
  box-shadow: 0 1px 2px rgba(11, 113, 86, 0.28);
}

.app__btn--main:hover,
.pub__btn--main:hover {
  background: var(--AccentDark);
  border-color: var(--AccentDark);
  color: #FFF;
}

/* Тёмная кнопка — для акцента на светлом блоке, где зелёная спорила бы
   с зелёными же метками «принято». */
.app__btn--ink,
.pub__btn--ink {
  background: var(--Ink);
  border-color: var(--Ink);
  color: #FFF;
}

.app__btn--ink:hover,
.pub__btn--ink:hover {
  background: #000;
  border-color: #000;
  color: #FFF;
}

.app__btn--danger {
  border-color: rgba(194, 43, 43, 0.4);
  color: var(--Bad);
}

.app__btn--danger:hover {
  background: var(--BadSoft);
  border-color: var(--Bad);
}

.app__link {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--AccentInk);
  cursor: pointer;
}

/* ── поля ── */
.app__field {
  display: block;
  margin: 0 0 16px;
}

.app__field > span {
  display: block;
  margin-bottom: 7px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--InkSoft);
}

/*
	Поля ввода перечислялись по типам: text, email, password. Любой другой
	тип оставался неоформленным — date рисовался браузерным умолчанием
	(тонкая прямоугольная рамка, свой шрифт, своя высота) и стоял в строке
	рядом с оформленными полями как чужой. Так было на правке счёта
	(«На дату») и в фильтрах операций («С даты», «По дату»).

	Список типов — плохая основа для правила: он молча отстаёт от разметки.
	Каждое новое поле (date, number, tel, search) нужно было бы вспомнить
	и дописать сюда, а забытое не падает — просто выглядит иначе.

	Поэтому наоборот: оформляем всё, кроме тех типов, которые рисует сам
	браузер и трогать их нельзя — флажки, переключатели, кнопки, файл,
	ползунок, палитра.
*/
.app__field input:not([type=checkbox]):not([type=radio]):not([type=submit]):not([type=button]):not([type=reset]):not([type=file]):not([type=range]):not([type=color]),
.app__field textarea,
.app__field select {
  width: 100%;
  /*
  	Одна высота на все поля строки. Без неё text, select и date
  	расходились на 44/46/47px: у каждого свой внутренний расчёт
  	строки, и в сетке .app__row это видно как разнобой по нижнему краю.
  */
  min-height: 46px;
  padding: 12px 14px;
  border: 1px solid var(--Line);
  border-radius: var(--Radius);
  background: var(--Bg);
  font-family: var(--Font);
  font-size: 15px;
  color: var(--Ink);
  transition: border-color 0.16s ease, box-shadow 0.16s ease;
}

.app__field input:focus,
.app__field textarea:focus,
.app__field select:focus {
  outline: 0;
  border-color: var(--Accent);
  box-shadow: 0 0 0 3px var(--AccentSoft);
}

.app__field input:disabled {
  background: var(--BgSoft);
  color: var(--Muted);
}

/*
	Календарь у input[type=date] браузер рисует сам. Прижимаем его к правому
	краю поля и красим в акцент, чтобы иконка не выглядела заимствованной.
*/
.app__field input[type=date]::-webkit-calendar-picker-indicator {
  margin-left: auto;
  cursor: pointer;
  opacity: 0.55;
}

.app__field input[type=date]:hover::-webkit-calendar-picker-indicator {
  opacity: 1;
}

/*
	═══════════════════════════════════════════════════════════════
	 ДАТА НА iPHONE ВЫЛЕЗАЛА ЗА КРАЙ КАРТОЧКИ
	═══════════════════════════════════════════════════════════════

	Safari на iOS рисует input[type=date] своим контролом: серая
	плашка, текст по центру и собственная минимальная ширина под
	«30 сент. 2026 г.». width:100% он не слушает — поле шире колонки
	сетки, правое упирается в край карточки (замечено на 15 Pro Max
	в фильтре отчётов).

	appearance:none возвращает полю наш вид (рамка, фон, отступы
	как у соседних select), min-width:0 снимает встроенный минимум,
	а у самой колонки min-width:0 — иначе сетка всё равно растянет
	её под содержимое. Текст — влево, как в остальных полях.
*/
.app__field {
  min-width: 0;
}

.app__field input[type=date],
.app__field input[type=datetime-local],
.app__field input[type=month],
.app__field input[type=time] {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  min-width: 0;
  max-width: 100%;
  text-align: left;
}

.app__field input[type=date]::-webkit-date-and-time-value,
.app__field input[type=datetime-local]::-webkit-date-and-time-value,
.app__field input[type=month]::-webkit-date-and-time-value,
.app__field input[type=time]::-webkit-date-and-time-value {
  text-align: left;
  margin: 0;
}

/*
	Флажок с подписью.

	Обёртка — <div>, а не <label>: подпись печатает HtmlTags::InputCheckbox
	парой «input + label for=…», и вложить это в ещё один label нельзя.
	Курсор-указатель поэтому на внутреннем label — кликабельна именно
	подпись, а не пустое место справа от неё.
*/
.app__check {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 12px;
}

.app__check label {
  cursor: pointer;
}

/*
	Флажки и переключатели рисуем сами.

	Браузерные выглядят на тёплой бумаге заимствованными: у каждого своя
	рамка, свой радиус, свой размер (16/13/14px в Chrome, Firefox и Safari)
	и своя синяя галочка. accent-color, который тут стоял, красит только
	заливку — размер, рамку и форму он не трогает, поэтому рядом
	с оформленными полями флажок всё равно оставался чужим.

	Правило общее, без привязки к .app__check: флажки стоят и в шапке
	таблицы операций, и в ячейках строк, и в фильтрах — перечислять места
	значит однажды забыть новое.

	Галочка нарисована фоновой картинкой, а не ::after. У input как
	заменяемого элемента псевдоэлементы поддержаны не везде (Safari их
	не строит), и там флажок остался бы просто зелёным квадратом без
	метки — то есть неотличимым от снятого для того, кто не помнит цвет.
*/
input[type=checkbox],
input[type=radio] {
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  width: 18px;
  height: 18px;
  margin: 0;
  padding: 0;
  border: 1px solid var(--Line);
  border-radius: 5px;
  background: var(--Bg) center center/100% 100% no-repeat;
  vertical-align: middle;
  cursor: pointer;
  transition: border-color 0.16s ease, background-color 0.16s ease, box-shadow 0.16s ease;
}

input[type=radio] {
  border-radius: 50%;
}

input[type=checkbox]:hover,
input[type=radio]:hover {
  border-color: var(--Accent);
}

input[type=checkbox]:checked,
input[type=radio]:checked {
  border-color: var(--Accent);
  background-color: var(--Accent);
}

input[type=checkbox]:checked {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18'%3E%3Cpath fill='none' stroke='%23fff' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round' d='M4.5 9.3l3 3 6-6.2'/%3E%3C/svg%3E");
}

/*
	Промежуточное состояние нужно «выделить все»: часть строк отмечена,
	часть нет. Без него общий флажок показывает «снято» при половине
	выделенных строк и врёт.
*/
input[type=checkbox]:indeterminate {
  border-color: var(--Accent);
  background-color: var(--Accent);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18'%3E%3Cpath fill='none' stroke='%23fff' stroke-width='2.2' stroke-linecap='round' d='M4.8 9h8.4'/%3E%3C/svg%3E");
}

input[type=radio]:checked {
  background-image: radial-gradient(circle at center, #FFF 0 3.4px, transparent 3.9px);
}

input[type=checkbox]:focus-visible,
input[type=radio]:focus-visible {
  outline: 0;
  border-color: var(--Accent);
  box-shadow: 0 0 0 3px var(--AccentSoft);
}

input[type=checkbox]:disabled,
input[type=radio]:disabled {
  background-color: var(--BgSoft);
  border-color: var(--Line);
  cursor: default;
  opacity: 0.55;
}

input[type=checkbox]:disabled + label,
input[type=radio]:disabled + label {
  color: var(--Muted);
  cursor: default;
}

.app__hint {
  margin: 7px 0 14px;
  font-size: 13px;
  color: var(--Muted);
}

/* ── бейджи ── */
.app__badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.01em;
  background: var(--LineSoft);
  color: var(--InkSoft);
}

.app__badge--ok {
  background: var(--OkSoft);
  color: var(--Ok);
}

.app__badge--bad {
  background: var(--BadSoft);
  color: var(--Bad);
}

.app__badge--wait {
  background: var(--WarnSoft);
  color: var(--Warn);
}

.app__badge--muted {
  background: var(--LineSoft);
  color: var(--Muted);
}

/* ── уведомления ── */
.app__notice {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin: 0 0 20px;
  padding: 15px 18px;
  border: 1px solid var(--Line);
  border-radius: var(--Radius);
  background: var(--Bg);
}

.app__notice p {
  margin: 4px 0;
}

.app__notice ul {
  margin: 6px 0 0;
  padding-left: 18px;
  width: 100%;
}

.app__notice--ok {
  background: var(--OkSoft);
  border-color: rgba(11, 113, 86, 0.22);
}

.app__notice--warn {
  background: var(--WarnSoft);
  border-color: rgba(180, 83, 9, 0.22);
}

.app__notice--info {
  background: var(--AccentSoft);
  border-color: rgba(11, 113, 86, 0.18);
}

/*
	Публичная часть myficance.kz: шапка, главная, текстовые страницы, подвал.

	═══════════════════════════════════════════════════════════════════
	ЧТО ЗДЕСЬ БЫЛО И ПОЧЕМУ ФАЙЛ ПЕРЕПИСАН ЦЕЛИКОМ
	═══════════════════════════════════════════════════════════════════

	Файл достался от донора (kartify-public.scss) и описывал витрину
	сервиса обработки фотографий: шторка «до/после», галерея кадров,
	тарифы, FAQ, лайтбокс, блок про модерацию Kaspi, формы регистрации.
	1008 строк, 36 КБ.

	Ни одна из этих страниц в проекте не существует — их удалил этап 0.
	То есть браузер каждого посетителя скачивал и разбирал правила для
	разметки, которой на сайте нет. Хуже того, из-за этого было не видно
	настоящую проблему: сайт выглядел «сделанным», хотя стилей для того,
	что он реально показывает, тут почти не было.

	Формы .auth__* тоже удалены: вход участника печатает
	Access\Views\LoginView классами .app__*, и стилизует его ledger.scss.

	═══════════════════════════════════════════════════════════════════
	ЧТО РИСУЕТ ПУБЛИЧНАЯ ЧАСТЬ ТЕПЕРЬ
	═══════════════════════════════════════════════════════════════════

	myficance.kz — не продукт и не витрина. Это семейная система учёта,
	регистрации нет, доступ по приглашению. Публичных экранов ровно три:

		шапка + подвал      Design\Views\HeaderView / FooterView
		главная             modules/home.php          .pub__hero, .pub__what
		текстовая страница  modules/text.php          .textpage

	Плюс страница 404 — у неё стили inline в Routes\E404Route: она обязана
	выглядеть прилично и тогда, когда main.css не отдался, а на битых
	адресах это как раз и случается.

	Всё, что ниже, описывает эти три экрана и больше ничего. Правило,
	которому не соответствует ни один элемент разметки, — это не «задел
	на будущее», а мусор, который надо будет опознавать при следующей
	правке.

	ПОЛНОШИРИННЫЕ СЕКЦИИ. Разметка плоская: секции — прямые дети #mb,
	внутренней обёртки у них нет. Чтобы залить фон во всю ширину, не
	трогая PHP, секция сама становится во всю ширину, а содержимое держат
	боковые padding'и вида max(20px, calc(50% - Max/2 + 20px)). Это
	надёжнее, чем 100vw с отрицательным margin: последний на странице
	с полосой прокрутки даёт горизонтальный скролл.
*/
/* ---------- общая раскладка ---------- */
#mb {
  display: block;
}

/*
	Прижатый подвал.

	Ядро печатает body → header → #mb → footer. На короткой странице
	(а главная здесь короткая) подвал уезжал вверх, и под ним оставалась
	полоса фона.

	Через :has(> #mb) — чтобы не задеть админку: там своя вёрстка, своего
	#mb нет, а body во flex-колонке ей бы поплохело.
*/
body:has(> #mb) {
  min-height: 100vh;
  min-height: 100dvh; /* мобильные: без прыжков при скрытии панели браузера */
  display: flex;
  flex-direction: column;
}

body:has(> #mb) > #mb {
  flex: 1 0 auto;
}

body:has(> #mb) > .pub__footer {
  flex: 0 0 auto;
}

/* ---------- шапка ---------- */
/*
	Три элемента: знак с названием, меню страниц и кнопка входа.
	Телефона, мессенджеров и переключателя языков здесь нет — они были
	от донора и к задаче «войти в свой учёт» отношения не имеют.
*/
.pub__header {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 0 max(20px, 50% - var(--Max) / 2 + 20px);
  min-height: 68px;
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(16px) saturate(1.6);
  -webkit-backdrop-filter: blur(16px) saturate(1.6);
  border-bottom: 1px solid var(--LineSoft);
}

.pub__logo {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-right: 20px;
  font-family: var(--FontHead);
  font-weight: 800;
  font-size: 20px;
  letter-spacing: -0.03em;
  color: var(--Ink);
}

.pub__logo:hover {
  text-decoration: none;
}

/*
	Знак — /images/logo.svg. Второй слой фона — сплошной акцент: если SVG
	не отдастся, на месте знака будет плашка цвета бренда, а не белая дыра
	с альтом. Тот же приём был у донора и он себя оправдал.
*/
.pub__logo-mark {
  width: 28px;
  height: 28px;
  flex: 0 0 auto;
  border-radius: 8px;
  background-image: url("/images/logo.svg"), linear-gradient(var(--Accent), var(--Accent));
  background-size: contain, cover;
  background-repeat: no-repeat, no-repeat;
  background-position: center, center;
}

.pub__menu {
  display: flex;
  gap: 2px;
  flex-wrap: wrap;
}

.pub__menu a {
  padding: 8px 14px;
  border-radius: 999px;
  color: var(--InkSoft);
  font-size: 15px;
  font-weight: 500;
  transition: background 0.16s ease, color 0.16s ease;
}

.pub__menu a:hover {
  background: var(--LineSoft);
  color: var(--Ink);
  text-decoration: none;
}

.pub__menu a.cur {
  color: var(--Ink);
  font-weight: 600;
}

/*
	Кнопка входа. margin-left:auto прижимает её вправо независимо от того,
	сколько пунктов в меню, — в том числе когда меню пустое (сейчас так
	и есть: единственная видимая страница — главная, а на неё уже ведёт
	знак слева).
*/
.pub__enter {
  margin-left: auto;
  padding: 9px 18px;
  border-radius: 999px;
  background: var(--Ink);
  color: #FFF;
  font-size: 15px;
  font-weight: 600;
}

.pub__enter:hover {
  text-decoration: none;
  background: #000;
  color: #FFF;
}

/*
	Переключатель языка (Р1) — перед кнопкой входа и сдвигает её вправо
	вместо неё: margin-left:auto у первого из двух. Коды, а не флаги:
	флаг — это страна, а язык и страна у нас выбираются независимо.
*/
.pub__langs {
  display: flex;
  gap: 2px;
  margin-left: auto;
  margin-right: 10px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
}

.pub__langs a, .pub__langs span {
  padding: 6px 8px;
  border-radius: 8px;
  color: var(--InkSoft);
}

.pub__langs a:hover {
  text-decoration: none;
  background: var(--LineSoft, #F0EDE8);
  color: var(--Ink);
}

.pub__langs .cur {
  color: var(--Ink);
  background: var(--LineSoft, #F0EDE8);
}

.pub__langs + .pub__enter {
  margin-left: 0;
}

/* ---------- главная: первый экран ---------- */
/*
	Одна колонка по центру, а не две с картинкой справа.

	Двухколоночный первый экран нужен, когда справа стоит доказательство:
	снимок продукта, скриншот результата. Показывать здесь нечего —
	за кнопкой входа личные деньги, скриншот кабинета на публичной
	странице ставить нельзя. Пустая правая колонка выглядела бы как
	неотрисовавшаяся картинка, поэтому колонка одна.
*/
.pub__hero {
  max-width: 760px;
  margin: 0 auto;
  padding: 96px 20px 72px;
  text-align: center;
}

.pub__hero-title {
  font-family: var(--FontHead);
  font-weight: 800;
  font-size: clamp(34px, 5.4vw, 56px);
  line-height: 1.06;
  letter-spacing: -0.035em;
  margin: 0 0 20px;
  color: var(--Ink);
}

.pub__hero-lead {
  font-size: clamp(17px, 2vw, 20px);
  line-height: 1.55;
  color: var(--InkSoft);
  margin: 0 auto 32px;
  max-width: 600px;
}

.pub__hero-lead p {
  margin: 0 0 12px;
}

.pub__hero-lead p:last-child {
  margin-bottom: 0;
}

.pub__cta {
  display: inline-flex;
  align-items: center;
  padding: 15px 34px;
  border-radius: 999px;
  background: var(--Accent);
  color: #FFF;
  font-weight: 600;
  font-size: 17px;
  transition: background 0.16s ease;
}

.pub__cta:hover {
  text-decoration: none;
  background: var(--AccentDark);
  color: #FFF;
}

.pub__hint {
  margin: 18px 0 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--Muted);
}

/* ---------- главная: форма проверки (2.18) ---------- */
/*
	Поле и кнопка в одну строку, как в поиске: это и есть главное действие
	страницы. Подпись поля есть, но спрятана визуально — плейсхолдер
	подписью не считается (исчезает при вводе, не читается экранными
	дикторами). До макетов из 07-tz-dizain.md — минимальный вид.
*/
.check-form {
  max-width: 600px;
  margin: 0 auto;
  text-align: left;
}

.check-form__label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.check-form__row {
  display: flex;
  gap: 10px;
}

.check-form__input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 14px 20px;
  border: 1px solid var(--Line, #E6E3DE);
  border-radius: 999px;
  font: inherit;
  font-size: 17px;
  color: var(--Ink);
  background: #FFF;
}

.check-form__input:focus {
  outline: 2px solid var(--Accent);
  outline-offset: 2px;
  border-color: var(--Accent);
}

.check-form__input.error {
  border-color: #C0392B;
}

.check-form__button {
  flex: 0 0 auto;
  border: 0;
  cursor: pointer;
}

.check-form__button:disabled {
  opacity: 0.7;
  cursor: progress;
}

.check-form__captcha {
  margin-top: 12px;
}

.check-form .message.error {
  margin: 10px 20px 0;
  font-size: 14px;
  color: #C0392B;
}

/* ---------- страница проверки /check/{hash}/ (2.19) ---------- */
/*
	Временный вид до макетов (07-tz-dizain.md, экраны 2 и 3). Шаги —
	список с отметкой состояния: статус передаётся знаком и словом-классом,
	не только цветом (требование доступности из ТЗ).
*/
.checkpage {
  max-width: 880px;
  margin: 0 auto;
  padding: 72px 20px 88px;
}

.checkpage__domain {
  margin: 0 0 6px;
  font-family: var(--FontMono, monospace);
  color: var(--Muted);
  font-size: 15px;
}

.checkpage__title {
  font-family: var(--FontHead);
  font-weight: 800;
  font-size: clamp(28px, 4vw, 40px);
  letter-spacing: -0.03em;
  line-height: 1.1;
  margin: 0 0 20px;
  color: var(--Ink);
}

.checkpage__subtitle {
  font-family: var(--FontHead);
  font-size: 22px;
  margin: 36px 0 12px;
}

.checkpage__lead {
  font-size: 18px;
  line-height: 1.55;
  color: var(--InkSoft);
  margin: 0 0 20px;
}

.checkpage__queue {
  color: var(--InkSoft);
  margin: 0 0 16px;
}

.checkpage__steps {
  list-style: none;
  margin: 0 0 20px;
  padding: 0;
}

.checkpage__step {
  position: relative;
  padding: 9px 0 9px 32px;
  border-bottom: 1px solid var(--LineSoft, #F0EDE8);
  color: var(--Muted);
}

.checkpage__step::before {
  position: absolute;
  left: 0;
  width: 22px;
  text-align: center;
  content: "○";
}

.checkpage__step--done {
  color: var(--Ink);
}

.checkpage__step--done::before {
  content: "✓";
  color: var(--Accent);
  font-weight: 700;
}

.checkpage__step--run {
  color: var(--Ink);
  font-weight: 600;
}

.checkpage__step--run::before {
  content: "●";
  color: var(--Accent);
  animation: checkpage-pulse 1s ease-in-out infinite;
}

@keyframes checkpage-pulse {
  50% {
    opacity: 0.3;
  }
}
.checkpage__score {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin: 0 0 8px;
}

.checkpage__score-num {
  font-family: var(--FontHead);
  font-weight: 800;
  font-size: 72px;
  line-height: 1;
  color: var(--Ink);
}

.checkpage__score-max {
  font-size: 22px;
  color: var(--Muted);
}

.checkpage__grade {
  margin-left: 12px;
  padding: 4px 12px;
  border-radius: 8px;
  font-weight: 800;
  font-size: 22px;
  background: var(--LineSoft, #F0EDE8);
  color: var(--Ink);
}

.checkpage__areas {
  width: 100%;
  border-collapse: collapse;
  margin: 24px 0 0;
}

.checkpage__areas th, .checkpage__areas td {
  padding: 10px 0;
  border-bottom: 1px solid var(--LineSoft, #F0EDE8);
  text-align: left;
  font-weight: 400;
}

.checkpage__areas td {
  text-align: right;
  white-space: nowrap;
}

.checkpage__fix {
  padding-left: 22px;
  line-height: 1.6;
}

.checkpage__lost {
  color: var(--Muted);
  font-size: 14px;
  white-space: nowrap;
}

/* ---------- отчёт бесплатной проверки (2.20) ---------- */
/*
	Временный вид до макетов (07-tz-dizain.md, экран 3). Чужой текст
	(адреса, доказательства) — моноширинным и отдельным блоком: читатель
	должен видеть, где наш вывод, а где цитата с его сайта.
*/
.report__block {
  margin: 40px 0 0;
}

.report__engines {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.report__engine {
  display: flex;
  gap: 10px;
  align-items: baseline;
  padding: 8px 14px;
  border: 1px solid var(--Line);
  border-radius: 999px;
  background: #FFF;
}

.report__engine-name {
  font-size: 14px;
  color: var(--InkSoft);
}

.report__engine-score {
  font-weight: 700;
}

.report__areas {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}

.report__area {
  padding: 14px 16px;
  border: 1px solid var(--Line);
  border-radius: 14px;
  background: #FFF;
}

.report__area-title {
  margin: 0 0 6px;
  font-weight: 600;
}

.report__area-score {
  margin: 0 0 8px;
  font-size: 20px;
  font-weight: 700;
}

.report__area-note {
  margin: 8px 0 0;
  font-size: 13px;
  color: var(--Muted);
}

.report__bar {
  height: 6px;
  border-radius: 3px;
  background: var(--LineSoft, #F0EDE8);
  overflow: hidden;
}

.report__bar span {
  display: block;
  height: 100%;
  background: var(--Accent);
}

.report__fix {
  margin: 0 0 14px;
  padding: 18px 20px;
  border: 1px solid var(--Line);
  border-radius: 16px;
  background: #FFF;
}

.report__fix-meta {
  margin: 0 0 6px;
  font-size: 13px;
  color: var(--Muted);
}

.report__fix-title {
  margin: 0 0 12px;
  font-family: var(--FontHead);
  font-size: 20px;
  line-height: 1.25;
}

.report__fix-row {
  margin: 0 0 10px;
  line-height: 1.55;
}

.report__evidence {
  margin: 0 0 12px;
  padding: 10px 12px;
  border-left: 3px solid var(--Line);
  background: var(--LineSoft, #F0EDE8);
  font-family: var(--FontMono, monospace);
  font-size: 13px;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
}

.report__sev {
  white-space: nowrap;
  font-weight: 600;
}

.report__sev--problem {
  color: #B3261E;
}

.report__sev--partial {
  color: #9A6700;
}

.report__sev--ok {
  color: var(--Accent);
}

.report__gate {
  padding: 18px 20px;
  border: 1px dashed var(--Line);
  border-radius: 16px;
}

.report__gate-title {
  margin: 0 0 10px;
  font-size: 18px;
}

.report__gate-list {
  margin: 0 0 12px;
  padding-left: 22px;
  line-height: 1.7;
}

.report__gate-note {
  margin: 0;
  color: var(--InkSoft);
}

.report__plain {
  list-style: none;
  margin: 0;
  padding: 0;
  line-height: 1.8;
}

.report__scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.report__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}

.report__table th, .report__table td {
  padding: 8px 10px 8px 0;
  border-bottom: 1px solid var(--LineSoft, #F0EDE8);
  text-align: left;
  vertical-align: top;
}

.report__table th {
  font-weight: 600;
  color: var(--InkSoft);
  white-space: nowrap;
}

.report__url {
  font-family: var(--FontMono, monospace);
  font-size: 13px;
  word-break: break-all;
  min-width: 140px;
}

.report__row--skipped td {
  color: var(--Muted);
}

.report__row--bad td {
  color: #B3261E;
}

/* ---------- действия под отчётом (2.21) ---------- */
.report__actions {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
}

.report__details {
  width: 100%;
  border-top: 1px solid var(--Line);
  padding-top: 12px;
}

.report__details summary {
  cursor: pointer;
  font-weight: 600;
}

.report__form {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 560px;
  margin-top: 12px;
}

.report__form select, .report__form textarea {
  padding: 10px 12px;
  border: 1px solid var(--Line);
  border-radius: 10px;
  font: inherit;
  background: #FFF;
}

.report__form textarea {
  resize: vertical;
}

.report__form-note {
  margin: 0;
  font-size: 13px;
  color: var(--Muted);
}

.report__form .pub__cta {
  align-self: flex-start;
  border: 0;
  cursor: pointer;
}

.report__check {
  display: flex;
  gap: 8px;
  align-items: center;
}

.report__danger {
  background: #B3261E;
}

.report__danger:hover {
  background: #8C1D18;
}

.check-form__done {
  margin: 0 auto 16px;
  max-width: 600px;
  color: var(--Accent);
}

/* ---------- главная: что система делает ---------- */
/*
	Список, а не сетка карточек.

	Карточки уместны, когда пункты — самостоятельные объекты, между
	которыми выбирают (тарифы, товары). Здесь это перечисление свойств
	одной системы: рамка вокруг каждой строки добавила бы 40 пикселей
	хрома на 8 слов текста и заставила бы читать пять раз одно и то же
	движение глаза.
*/
/*
	Белая полоса на тёплом фоне страницы (body — --BgSoft). Сначала фон
	здесь был тот же --BgSoft, и вся страница читалась одним куском:
	линия сверху разделяла блоки, но не давала им разного веса. Смена
	фона отделяет «что это» от «что оно делает» лучше любой рамки.
*/
.pub__what {
  padding: 64px max(20px, 50% - var(--Max) / 2 + 20px) 88px;
  background: var(--Bg);
  border-top: 1px solid var(--LineSoft);
}

.pub__what-title {
  max-width: 760px;
  margin: 0 auto 24px;
  font-family: var(--FontHead);
  font-weight: 700;
  font-size: clamp(22px, 3vw, 28px);
  letter-spacing: -0.02em;
  color: var(--Ink);
}

/*
	Разметку списка печатает не шаблон, а визуальный редактор админки:
	в настройке HomeFeatures лежит обычный <ul>. Поэтому селекторы идут
	через потомков — на сам <ul> класс повесить нельзя.
*/
.pub__list {
  max-width: 760px;
  margin: 0 auto;
}

.pub__list ul,
.pub__list ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

.pub__list p {
  margin: 0 0 12px;
  font-size: 17px;
  line-height: 1.55;
  color: var(--InkSoft);
}

.pub__list li {
  position: relative;
  padding: 14px 0 14px 30px;
  border-bottom: 1px solid var(--Line);
  font-size: 17px;
  line-height: 1.5;
  color: var(--InkSoft);
}

.pub__list li:last-child {
  border-bottom: 0;
}

/*
	Галочка рисуется рамкой повёрнутого прямоугольника — без иконочного
	шрифта и без ещё одного запроса за SVG ради пяти строк.

	left:1px, а не 6: галочка стоит в той же вертикали, что и заголовок
	блока. Со сдвигом внутрь список выглядел смещённым вправо
	относительно «Что система делает» на неопределённую величину.
*/
.pub__list li::before {
  content: "";
  position: absolute;
  left: 1px;
  top: 22px;
  width: 6px;
  height: 11px;
  border: solid var(--Accent);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

/* ---------- подвал ---------- */
/*
	Тёмная полоса: страница короткая и однотонная, подвал даёт ей низ.
	Внутри — имя, ссылки и одна строка о том, что это за сайт.

	Плашки про cookie здесь нет и не будет: аналитики и сторонних
	скриптов на сайте нет, согласия спрашивать не на что.
*/
.pub__footer {
  padding: 48px max(20px, 50% - var(--Max) / 2 + 20px);
  background: var(--BgDeep);
  color: #A8A39A;
}

.pub__footer-in {
  display: block;
}

.pub__footer-brand {
  display: inline-block;
  font-family: var(--FontHead);
  font-weight: 800;
  font-size: 19px;
  letter-spacing: -0.03em;
  color: #FFF;
  margin-bottom: 16px;
}

.pub__footer-brand:hover {
  text-decoration: none;
}

.pub__footer-nav {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: 20px;
}

.pub__footer-nav a {
  color: #C9C4BB;
  font-size: 15px;
}

.pub__footer-nav a:hover {
  color: #FFF;
}

.pub__footer-copy {
  margin: 0;
  color: #7C776F;
  font-size: 13px;
  line-height: 1.6;
}

.pub__footer-copy span {
  margin-left: 6px;
}

/* ---------- текстовые страницы (modules/text.php) ---------- */
/*
	H1 и блок HTML из админки. Без контейнера текст растекался во всю
	ширину экрана до самых краёв.
*/
.textpage {
  max-width: 760px; /* уже основной сетки: длинный текст читают колонкой */
  margin: 0 auto;
  padding: 64px 20px 80px;
}

.textpage h1 {
  font-size: clamp(28px, 4vw, 40px);
  margin-bottom: 28px;
}

.textpage__body {
  font-size: 17px;
  line-height: 1.72;
  color: var(--InkSoft);
}

.textpage__body h2 {
  font-size: 22px;
  margin: 40px 0 14px;
  color: var(--Ink);
}

.textpage__body h3 {
  font-size: 18px;
  margin: 28px 0 10px;
  color: var(--Ink);
}

.textpage__body p {
  margin: 0 0 16px;
}

.textpage__body ul,
.textpage__body ol {
  margin: 0 0 18px;
  padding-left: 22px;
}

.textpage__body li {
  margin-bottom: 9px;
}

.textpage__body a {
  text-decoration: underline;
}

.textpage__body table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 20px;
}

.textpage__body td,
.textpage__body th {
  padding: 11px 15px;
  border: 1px solid var(--Line);
  text-align: left;
}

/* ---------- мобильная ---------- */
@media (max-width: 700px) {
  body {
    font-size: 15px;
  }
  .pub__header {
    min-height: 58px;
    padding: 8px 14px;
    gap: 8px;
  }
  .pub__logo {
    margin-right: 8px;
    font-size: 18px;
  }
  .pub__menu {
    order: 3;
    width: 100%;
    overflow-x: auto;
    flex-wrap: nowrap;
    padding-bottom: 2px;
  }
  .pub__menu::-webkit-scrollbar {
    display: none;
  }
  .pub__enter {
    margin-left: auto;
    padding: 7px 14px;
    font-size: 14px;
  }
  .pub__langs {
    margin-right: 4px;
  }
  .pub__langs + .pub__enter {
    margin-left: 0;
  }
  .pub__hero {
    padding: 56px 16px 48px;
  }
  .check-form__row {
    flex-direction: column;
  }
  .checkpage {
    padding: 44px 16px 64px;
  }
  .report__areas {
    grid-template-columns: 1fr 1fr;
  }
  .report__fix {
    padding: 14px;
  }
  .check-form__button {
    justify-content: center;
  }
  .pub__what {
    padding: 48px 16px 64px;
  }
  .pub__footer {
    padding: 36px 16px;
  }
  .textpage {
    padding: 44px 16px 64px;
  }
}
/*
	═══════════════════════════════════════════════════════════════════
	 ОБОЛОЧКА ПРИЛОЖЕНИЯ (Cabinet\Views\LayoutView)
	═══════════════════════════════════════════════════════════════════

	ОДНА РАЗМЕТКА, ДВА ВИДА. На телефоне <nav> — панель, прибитая
	к низу экрана; на большом экране он же — рельс слева. Никакого
	второго комплекта тегов: разделы печатаются один раз, и если
	появится новый, он появится в обоих видах сразу.

	ПОЧЕМУ ВНИЗУ, А НЕ ВВЕРХУ. Телефон держат одной рукой, и большой
	палец достаёт до нижней трети экрана. Меню наверху — это привычка
	мышки, а не пальца; там же, наверху, у приложения стоит заголовок
	экрана, до которого дотягиваться не нужно, потому что его читают,
	а не нажимают.

	ПРО БЕЗОПАСНЫЕ ЗОНЫ. На iPhone снизу живёт полоса жеста «домой»,
	и панель, прижатая к нулю, оказывается ПОД ней: половина касаний
	уходит системе. env(safe-area-inset-*) — это те самые поля,
	которые телефон сообщает сам; работают они только при
	viewport-fit=cover, и он выставлен в шапке (HeaderFooterView).
*/
:root {
  --ShellBar:64px; /* высота нижней панели */
  --ShellRail:232px; /* ширина рельса на большом экране */
  --ShellSheet:26px; /* скругление выезжающего листа */
  --ShellGap:10px; /* просвет вокруг плавающего рельса */
}

.shell__main {
  /*
  	Место под панель снизу — иначе последняя строка списка
  	навсегда остаётся под ней и до неё нельзя долистать.
  */
  padding-bottom: calc(var(--ShellBar) + env(safe-area-inset-bottom));
}

/*
	═══════════════════════════════════════════════════════════════════
	 ПАНЕЛЬ НАВИГАЦИИ С БОЛЬШИМ ЗАГОЛОВКОМ
	═══════════════════════════════════════════════════════════════════

	Приём, по которому экран опознаётся как приложение: сверху тонкая
	липкая полоса с «назад» и действием, под ней — крупный заголовок,
	который при прокрутке уезжает, а его короткая копия проявляется
	в полосе. Копию печатает LayoutView::Title, класс is-stuck ставит
	ShellStickyInit по наблюдателю за меткой.

	Здесь только раскладка; цвет полосы, размытие и кегли — в ios.scss.
*/
/*
	⚠️ ОБЁРТКА .nav НЕ СОЗДАЁТ КОРОБКИ, И ЭТО ГЛАВНОЕ ПРАВИЛО ЗДЕСЬ.

	position:sticky держит элемент только ПОКА ВИДЕН ЕГО РОДИТЕЛЬ. Если
	.nav — обычный блок, он кончается сразу после крупного заголовка,
	и полоса уезжает вместе с ним: на втором экране прокрутки её уже
	нет, а значит нет ни «назад», ни кнопки действия.

	display:contents убирает саму коробку, оставляя детей: полоса
	и заголовок становятся детьми .app, и прилипать полосе теперь есть
	к чему — .app высотой во всю страницу. Класс is-stuck при этом
	по-прежнему ставится на .nav и по-прежнему находится селектором.
*/
.nav {
  display: contents;
}

.nav__bar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  margin: 0 calc(var(--AppPad) * -1);
  /*
  	Восемь пикселей сверх безопасной зоны: без них кнопка действия
  	прилипает к самому краю окна. В окне приложения зона равна
  	высоте строки состояния, в браузере — нулю, и отступ нужен
  	в обоих случаях.
  */
  padding: calc(env(safe-area-inset-top) + 8px) var(--AppPad) 0;
}

.nav__back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  margin-left: -7px;
}

.nav__back:hover {
  text-decoration: none;
}

.nav__back .shell__icon {
  width: 22px;
  height: 22px;
}

/*
	Короткий заголовок — абсолютом по центру ПОЛОСЫ, а не флекс-элементом
	между кнопками: иначе он центрируется между ними, и на экране
	со стрелкой слева и кнопкой справа заголовок стоит не по середине
	экрана, а по середине остатка. Глаз это ловит сразу.
*/
.nav__small {
  position: absolute;
  left: 50%;
  max-width: 58%;
  transform: translateX(-50%);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.18s ease;
  pointer-events: none;
}

.nav.is-stuck .nav__small {
  opacity: 1;
}

.nav__act {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  margin-left: auto;
}

.nav__act:empty {
  display: none;
}

.nav__big {
  margin: 4px 0 18px;
}

/* ── общая часть: вкладка это иконка и подпись под ней ── */
.shell__nav {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 30;
  display: flex;
  align-items: stretch;
  height: calc(var(--ShellBar) + env(safe-area-inset-bottom));
  padding: 0 4px env(safe-area-inset-bottom);
  border-top: 1px solid var(--Line);
}

/*
	Фон панели — на ПСЕВДОЭЛЕМЕНТЕ, и это не украшательство.

	Содержимое под панелью должно просвечивать размыто, как в системных
	панелях, а это backdrop-filter. Но элемент с backdrop-filter (ровно
	как с filter или transform) становится системой координат для всех
	своих position:fixed потомков — а внутри панели лежит лист «Ещё»
	с затемнением на весь экран. Затемнение честно растягивалось
	на inset:0… ПАНЕЛИ, то есть на полоску в 64 пикселя, и пряталось
	за самим листом: экран позади оставался незатемнённым, и было
	непонятно, почему.

	Псевдоэлемент размывает то же самое, но системой координат
	для потомков панель при этом не становится.
*/
.shell__nav::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--BgCard);
}

@supports (backdrop-filter: blur(12px)) {
  .shell__nav::before {
    background: color-mix(in srgb, var(--BgCard) 82%, transparent);
    backdrop-filter: blur(14px) saturate(140%);
  }
}
.shell__tab,
.shell__add,
.shell__brand {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  flex: 1 1 0;
  min-width: 0;
  padding: 6px 2px;
  color: var(--Muted);
  text-decoration: none;
  font-size: 11px;
  line-height: 1.1;
  font-weight: 500;
  /* серый прямоугольник подсветки при касании — не наш стиль */
  -webkit-tap-highlight-color: transparent;
}

/* base подчёркивает ссылки при наведении; на вкладке это мусор */
.shell__tab:hover,
.shell__add:hover,
.shell__brand:hover {
  text-decoration: none;
}

.shell__icon {
  width: 24px;
  height: 24px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.shell__label {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.shell__tab--cur {
  color: var(--Accent);
}

.shell__tab--cur .shell__icon {
  stroke-width: 2.1;
}

/*
	Счётчик задач. Абсолютом поверх иконки, а не строкой рядом:
	в подписи он съедал бы её ширину, а подпись на панели и так
	помещается впритык.
*/
.shell__badge {
  position: absolute;
  top: 2px;
  left: calc(50% + 6px);
  min-width: 17px;
  padding: 0 4px;
  border-radius: 9px;
  background: var(--Bad);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 17px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* ── кнопка добавления ── */
/*
	Она не вкладка: вкладка ведёт «посмотреть», а эта — «записать»,
	и это самое частое действие с телефона. Поэтому единственный
	залитый элемент на панели: цель видно, не читая подписей.
*/
.shell__add {
  color: var(--Accent);
}

.shell__add .shell__icon {
  width: 46px;
  height: 32px;
  padding: 4px 11px;
  border-radius: 16px;
  background: var(--Accent);
  stroke: var(--Bg);
  stroke-width: 2.2;
  box-shadow: 0 4px 12px rgba(11, 113, 86, 0.28);
}

.shell__add:active .shell__icon {
  transform: scale(0.94);
}

/* бренд — только на рельсе, на панели ему места нет */
.shell__brand {
  display: none;
}

/* ── лист «Ещё»: снизу на телефоне, часть рельса на десктопе ── */
.shell__rest {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  visibility: hidden;
}

/* затемнение позади листа — оно же кнопка «закрыть» */
.shell__rest__back {
  position: fixed;
  inset: 0;
  display: block;
  background: rgba(18, 21, 26, 0.45);
  opacity: 0;
  transition: opacity 0.22s ease;
}

.shell__rest__body {
  position: relative;
  max-height: 78vh;
  overflow: auto;
  padding: 10px 12px calc(14px + env(safe-area-inset-bottom));
  border-radius: var(--ShellSheet) var(--ShellSheet) 0 0;
  background: var(--BgCard);
  box-shadow: var(--ShadowXl);
  transform: translateY(101%);
  transition: transform 0.26s cubic-bezier(0.32, 0.72, 0, 1);
}

/*
	Полоска-«ручка» сверху листа. Чисто декоративная, но именно она
	читается как «это можно закрыть», и без неё лист выглядит
	приклеенным намертво.
*/
.shell__rest__body::before {
  content: "";
  display: block;
  width: 38px;
  height: 4px;
  margin: 0 auto 10px;
  border-radius: 2px;
  background: var(--Line);
}

/*
	Открыт — по классу от скрипта ИЛИ по :target от браузера.
	Второе — не украшение: без JavaScript «Ещё» обязано работать,
	иначе девять разделов из тринадцати становятся недостижимы.
*/
.shell__rest.is-open,
.shell__rest:target {
  visibility: visible;
}

.shell__rest.is-open .shell__rest__back,
.shell__rest:target .shell__rest__back {
  opacity: 1;
}

.shell__rest.is-open .shell__rest__body,
.shell__rest:target .shell__rest__body {
  transform: none;
}

/* в листе вкладки — строками, с иконкой слева */
.shell__rest .shell__tab {
  flex-direction: row;
  justify-content: flex-start;
  gap: 12px;
  flex: none;
  min-height: 48px;
  padding: 0 12px;
  border-radius: 14px;
  color: var(--Ink);
  font-size: 15px;
  font-weight: 500;
}

.shell__rest .shell__tab--cur {
  background: var(--AccentSoft);
  color: var(--AccentDark);
}

.shell__rest .shell__badge {
  position: static;
  margin-left: auto;
}

.shell__rest .shell__label {
  max-width: none;
}

.shell__rest__foot {
  margin-top: 6px;
  padding-top: 10px;
  border-top: 1px solid var(--LineSoft);
}

.shell__who {
  display: block;
  padding: 0 12px 6px;
  color: var(--Muted);
  font-size: 13px;
}

.shell__tab--quiet {
  color: var(--Muted);
}

/* ── лист выбора «что добавить» ── */
/*
	Живёт в том же слое, что и «Ещё», и по той же механике. Разметку
	ему строит скрипт из data-атрибутов кнопки: три пункта, у каждого
	подпись и пояснение — потому что «Чек» и «Выписка» из одного слова
	не различаются, пока не попробуешь оба.
*/
.sheet {
  position: fixed;
  inset: 0;
  z-index: 50;
  visibility: hidden;
}

.sheet.is-open {
  visibility: visible;
}

.sheet__back {
  position: absolute;
  inset: 0;
  background: rgba(18, 21, 26, 0.45);
  opacity: 0;
  transition: opacity 0.22s ease;
}

.sheet.is-open .sheet__back {
  opacity: 1;
}

.sheet__body {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 10px 12px calc(14px + env(safe-area-inset-bottom));
  border-radius: var(--ShellSheet) var(--ShellSheet) 0 0;
  background: var(--BgCard);
  box-shadow: var(--ShadowXl);
  transform: translateY(101%);
  transition: transform 0.26s cubic-bezier(0.32, 0.72, 0, 1);
}

.sheet.is-open .sheet__body {
  transform: none;
}

.sheet__body::before {
  content: "";
  display: block;
  width: 38px;
  height: 4px;
  margin: 0 auto 12px;
  border-radius: 2px;
  background: var(--Line);
}

.sheet__item {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 60px;
  padding: 8px 12px;
  border-radius: 16px;
  color: var(--Ink);
  text-decoration: none;
}

.sheet__item:active {
  background: var(--BgSoft);
}

.sheet__item:hover {
  background: var(--BgSoft);
  text-decoration: none;
}

.sheet__item .shell__icon {
  width: 40px;
  height: 40px;
  padding: 8px;
  border-radius: 13px;
  background: var(--AccentSoft);
  stroke: var(--AccentDark);
  stroke-width: 1.9;
}

/*
	Подпись и пояснение — <b> и <i>: скрипт собирает лист узлами,
	а не строкой (внутри название магазина из базы), и теги нужны
	осмысленные. Начертание при этом задаёт стиль, а не тег —
	пояснение здесь не курсив.
*/
.sheet__t {
  display: block;
  font-size: 16px;
  font-weight: 600;
}

.sheet__n {
  display: block;
  color: var(--Muted);
  font-size: 13px;
  font-style: normal;
}

/* ── большой экран: та же навигация рельсом слева ── */
@media (min-width: 900px) {
  :root {
    --ShellBar:0px;
  }
  .shell__nav {
    top: 0;
    right: auto;
    bottom: 0;
    width: var(--ShellRail);
    height: 100vh;
    height: 100dvh;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    padding: 16px 12px;
    overflow: auto;
    border-top: 0;
    border-right: 1px solid var(--Line);
    background: var(--BgCard);
    backdrop-filter: none;
  }
  .shell__main {
    margin-left: var(--ShellRail);
    padding-bottom: 0;
  }
  /* вкладки строками — как в листе на телефоне, той же рукой */
  .shell__tab,
  .shell__add,
  .shell__brand {
    flex-direction: row;
    justify-content: flex-start;
    gap: 12px;
    flex: none;
    min-height: 44px;
    padding: 0 12px;
    border-radius: 12px;
    font-size: 15px;
  }
  /*
  	На рельсе бренд и кнопка добавления — сверху. В разметке
  	кнопка стоит в середине списка (там её место на панели
  	телефона), и здесь она поднимается порядком флекса: на большом
  	экране «добавить» — первое по смыслу действие, так что и обход
  	клавишей Tab от этого только выигрывает.
  */
  .shell__brand {
    order: -2;
  }
  .shell__add {
    order: -1;
  }
  .shell__brand {
    display: flex;
    margin-bottom: 14px;
    color: var(--Ink);
    font-family: var(--FontHead);
    font-size: 18px;
    font-weight: 800;
  }
  .shell__brand .shell__icon {
    stroke: var(--Accent);
  }
  /*
  	На рельсе все разделы равны. На телефоне они приходят из двух
  	мест — панель и лист — и красятся по-разному (в панели подпись
  	мельче и приглушённее, в листе это строки списка); здесь оба
  	источника надо свести к одному цвету, иначе половина рельса
  	выглядит выключенной. :not() — чтобы не перекрасить «Выход»,
  	который приглушён намеренно.
  */
  .shell__nav .shell__tab:not(.shell__tab--quiet):not(.shell__tab--cur) {
    color: var(--InkSoft);
  }
  .shell__tab:hover {
    background: var(--BgSoft);
    color: var(--Ink);
  }
  .shell__tab--cur,
  .shell__tab--cur:hover {
    background: var(--AccentSoft);
    color: var(--AccentDark);
  }
  .shell__add {
    margin-bottom: 10px;
    color: var(--Bg);
    background: var(--Accent);
    font-weight: 600;
  }
  .shell__add:hover {
    background: var(--AccentDark);
  }
  .shell__add .shell__icon {
    width: 24px;
    height: 24px;
    padding: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
    stroke: currentColor;
  }
  .shell__badge {
    position: static;
    margin-left: auto;
  }
  /* «Ещё» не нужно: на рельсе помещается всё */
  .shell__more {
    display: none;
  }
  .shell__rest {
    position: static;
    visibility: visible;
    flex: 1 1 auto;
  }
  .shell__rest__back {
    display: none;
  }
  .shell__rest__body {
    max-height: none;
    overflow: visible;
    padding: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
    transform: none;
  }
  .shell__rest__body::before {
    display: none;
  }
  .shell__rest .shell__tab {
    min-height: 44px;
    border-radius: 12px;
  }
  .shell__rest__foot {
    margin-top: 14px;
  }
  /* лист добавления на десктопе — окном по центру, а не снизу */
  .sheet__body {
    left: 50%;
    bottom: auto;
    top: 50%;
    right: auto;
    width: 420px;
    max-width: calc(100vw - 40px);
    border-radius: 20px;
    transform: translate(-50%, -46%);
    opacity: 0;
    transition: opacity 0.18s ease, transform 0.18s ease;
  }
  .sheet.is-open .sheet__body {
    transform: translate(-50%, -50%);
    opacity: 1;
  }
  .sheet__body::before {
    display: none;
  }
}
/*
	Кабинет учёта /app/ (ТЗ 10.1).

	Палитра и типографика — из base (kartify-base.scss): --Ink, --Muted,
	--Line, --BgCard, --Accent, --Bad. Здесь только то, чего в базе нет:
	вкладки, карточки сводки, таблицы операций и формы ввода.

	Главный принцип этих экранов — читаемость цифр. Суммы моноширинным
	с табличными цифрами: в колонке денег разряды обязаны стоять друг под
	другом, иначе глаз не сравнивает строки, а разбирает каждую заново.
*/
/*
	⚠️ ПОЧЕМУ ЗДЕСЬ #mb, А НЕ ПРОСТО .app

	Ядро вешает на <body> класс с именем страницы: Kernel::FillPageInfo()
	зовёт AddBodyClass($PageInfo->Name), а у страницы /app/ в SystemPages
	Name='app'. На всех экранах кабинета разметка такая:

	    <body class="app"> … <section class="app app--narrow"> … </body>

	Голое правило .app{max-width:var(--Max);margin:0 auto;padding:24px 20px 64px}
	попадало в ОБА элемента, и первым делом — в body. Последствия ровно
	те, что было видно на /app/login/:

	  — шапка не во всю ширину экрана (она внутри суженного body),
	  — подвал не во всю ширину и не прижат к низу,
	  — полоса фона под подвалом — это padding-bottom:64px у body.

	То же ограничение действует для ЛЮБОГО имени страницы: main, app,
	e404, sitemap.xml. Селектор верхнего уровня с таким именем писать
	нельзя — только внутри #mb, контейнера контента между шапкой
	и подвалом. Body под него не подпадает по определению.

	Страховка от повторения — в base.scss (правило body[class]).
*/
#mb .app {
  max-width: var(--Max);
  margin: 0 auto;
  /*
  	Боковое поле экрана — переменной, а не числом: из него же
  	считает свои отрицательные отступы панель навигации (.nav),
  	которой надо выйти за поля на всю ширину. Два числа в двух
  	местах разъехались бы на первой правке.
  */
  --AppPad:20px;
  padding: 0 var(--AppPad) 40px;
}

#mb .app--narrow {
  max-width: 420px;
  padding-top: 24px;
}

/* ── вкладки внутри раздела (верхних больше нет — см. shell.scss) ── */
/*
	Отступ снизу — на общем классе, а не только на --sub.

	Раньше margin висел на .app__tabs--sub, и ряд капсул без
	модификатора (фильтры очереди разбора) прилипал к карточке
	вплотную: ряд читался как часть карточки, а не как то, что
	ею управляет. Ряд вкладок ВСЕГДА стоит над содержимым,
	которое переключает, — значит отступ принадлежит ему самому.
*/
.app__tabs {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  margin: 0 0 20px;
}

.app__tab {
  display: inline-flex;
  align-items: center;
  padding: 8px 16px;
  border-radius: 999px;
  color: var(--InkSoft);
  font-weight: 600;
  font-size: 15px;
}

.app__tab:hover {
  background: var(--BgSoft);
  text-decoration: none;
}

/* Вкладка-кнопка («Свой период» раскрывает панель, а не уводит по адресу):
   без сброса браузер рисует ей рамку и серую заливку кнопки */
button.app__tab {
  border: 0;
  font: inherit;
  cursor: pointer;
}

button.app__tab:not(.app__tab--cur) {
  background: none;
}

.app__tab--cur {
  background: var(--Ink);
  color: #FFF;
}

.app__tab--cur:hover {
  background: var(--Ink);
  color: #FFF;
}

/*
	Счётчик рядом с названием вкладки.

	Отступ задаётся здесь, а не пробелом в разметке: .app__tab —
	inline-flex, а пробелы между флекс-элементами браузер не показывает,
	и «Все» слипалось со своей двойкой. Полагаться на пробел в шаблоне
	нельзя ещё и потому, что его съест любое переформатирование.
*/
.app__tab small {
  margin-left: 6px;
  color: var(--InkSoft);
  font-weight: 400;
}

/* на тёмной вкладке приглушённый серый не читается вовсе */
.app__tab--cur small {
  color: rgba(255, 255, 255, 0.65);
}

/* Заголовок экрана и панель навигации — в shell.scss (.nav*). */
.app__h2 {
  margin: 32px 0 12px;
  font-size: 20px;
}

.app__h3 {
  margin: 24px 0 8px;
  font-size: 17px;
}

.app__msg {
  margin: 16px 0;
  padding: 12px 16px;
  border-radius: var(--Radius);
  background: var(--OkSoft);
  color: var(--AccentDark);
  font-size: 15px;
}

.app__msg--error {
  background: var(--BadSoft);
  color: var(--Bad);
}

.app__msg--warn {
  background: var(--WarnSoft);
  color: var(--Warn);
}

.app__msg--ok {
  background: var(--OkSoft);
  color: var(--Ok);
}

/*
	═══════════════════════════════════════════════════════════════════
	 ГРАФИК (Design\LineChart)
	═══════════════════════════════════════════════════════════════════

	SVG рисуется на сервере с фиксированным viewBox 760×260 (числа —
	из Configs\ChartConfig), а ширину задаёт CSS: так график сам
	подстраивается под колонку, не зная о ней ничего.

	⚠️ ПОЧЕМУ ПОДПИСИ ЖИВУТ ВНЕ ХОЛСТА

	Из-за этого же viewBox и сломались подписи осей. Внутри SVG «11px»
	означают не одиннадцать пикселей экрана, а одиннадцать единиц
	КООРДИНАТНОЙ СЕТКИ — то есть 11/760 ширины. На колонке в 1150 px
	масштаб равен 1,5, и подпись выходила в 17 px; на широком мониторе —
	под 27 px, крупнее заголовка, ради которого её ставили.

	Множитель («считать кегль от ширины») чинил кегль и ломал другое:
	поле под подписи тоже задано в единицах сетки, на телефоне это меньше
	тридцати пикселей, и «6 002 517» не влезало туда НИКАКИМ кеглем.

	Поэтому подписи вынесены из холста обычным HTML: .chart__axis слева,
	.chart__dates снизу, .chart__unit сверху. Они верстаются пикселями
	и шрифтом сайта, ширину колонки задаёт её содержимое, а холст
	остаётся сеткой, линией и точками. К своей линии сетки подпись
	привязана процентом от высоты — соотношение сторон холста
	фиксировано, поэтому процент попадает в пиксель на любой ширине.

	В единицах сетки осталось ровно то, что обязано быть в ней: толщины
	линий сняты non-scaling-stroke (штатный ответ SVG «рисуй штрих
	в пикселях устройства»), а радиус точки так не умеет и потому
	считается множителем --ChartCoef — приёмом tan(atan2(a,b)),
	который в проекте уже применён для --MainCoef и даёт отношение a/b.
	Ему нужен container-type, и стоит он на .chart__plot, а не на всей
	карточке: масштаб холста задаёт ШИРИНА ХОЛСТА, а карточка шире его
	на колонку подписей — считая от неё, точки выходили заметно мельче
	задуманного. По той же причине внутри .chart__plot нельзя
	пользоваться --Unit и прочими величинами из variables.scss: они тоже
	считаются от cqw и начнут мерить ширину графика вместо ширины
	страницы.

	Цвета точек приходят в атрибутах (они привязаны к магазинам и должны
	совпадать с легендой), а всё остальное — из палитры темы.
*/
.app__chart {
  margin: 20px 0 8px;
  padding: 12px 10px 8px;
  border: 1px solid var(--Line);
  border-radius: var(--RadiusLg);
  background: var(--BgCard);
  /*
  	overflow:hidden здесь БЫЛ и убран: он срезал бы подсказку,
  	поднятую над верхней точкой, — ровно так же, как overflow
  	срезал выпадающие списки в таблицах.
  */
}

/*
	Колонка значений + холст, под ними — края периода.
	Подсказка лежит четвёртым элементом и выпадает из потока.
*/
.chart-box {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 8px;
  /* размеры точек — в ЭКРАННЫХ пикселях, множитель добавляется ниже */
  --ChartDot:4px;
  --ChartRing:6px;
  /* типографика подписей — обычная, без всяких множителей */
  --ChartLabel:12px;
}

.chart__axis {
  position: relative;
  grid-column: 1;
  grid-row: 1;
  /*
  	По самой длинной подписи: число символов приходит от LineChart.
  	Кегль объявлен ЗДЕСЬ, а не только на подписях: «ch» — это ширина
  	цифры ТОГО ЖЕ элемента, и при кегле по умолчанию (16px у body)
  	колонка выходила в полтора раза шире нужного — на телефоне
  	она съедала треть графика.
  */
  width: calc(var(--ChartAxisChars, 7) * 1ch + 4px);
  color: var(--Muted);
  font-size: var(--ChartLabel);
  font-variant-numeric: tabular-nums;
}

.chart__axis span {
  position: absolute;
  right: 0;
  transform: translateY(-50%);
  white-space: nowrap;
  line-height: 1;
}

.chart__plot {
  position: relative;
  grid-column: 2;
  grid-row: 1;
  min-width: 0;
  /* контейнер для cqw: масштаб холста меряется по холсту (см. выше) */
  container-type: inline-size;
}

.chart__dates {
  display: flex;
  justify-content: space-between;
  grid-column: 2;
  grid-row: 2;
  padding-top: 4px;
  color: var(--Muted);
  font-size: var(--ChartLabel);
  font-variant-numeric: tabular-nums;
}

.chart__unit {
  position: absolute;
  left: 4px;
  top: 0;
  color: var(--Muted);
  font-size: var(--ChartLabel);
  line-height: 1;
}

/*
	Высота — из соотношения сторон viewBox (760×260), а не фиксированная.
	overflow:visible — кольцо курсора рисуется в экранных пикселях
	и у крайней точки слегка выходит за холст; срезанное пополам кольцо
	выглядит как ошибка вёрстки.
*/
.chart {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 760/260;
  overflow: visible;
  /*
  	Масштаб холста: 760 единиц сетки на его нынешнюю ширину.
  	Объявлен ЗДЕСЬ, а не на .chart-box, потому что cqw считается
  	от ближайшего предка-контейнера, а сам себе контейнером элемент
  	быть не может: на .chart__plot стоит container-type, на его
  	потомке — эта строка.

  	Запасное значение первым: если браузер не знает tan()/atan2(),
  	вторая строка отбрасывается при разборе и остаётся она. 2 —
  	масштаб холста около 380 px, худший случай мобильного экрана.

  	760 — ChartConfig::$Width. При изменении править и там, и здесь
  	(и aspect-ratio выше).
  */
  --ChartCoef:2;
  --ChartCoef:tan(atan2(760px,100cqw));
}

.chart__grid {
  stroke: var(--LineSoft);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.chart__mark {
  stroke: var(--Warn);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  vector-effect: non-scaling-stroke;
}

.chart__line {
  fill: none;
  stroke: var(--Ink);
  stroke-width: 2;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

/* Период сравнения — пунктиром и тише: это фон для основной линии, а не вторая главная */
.chart__line--ghost {
  stroke: var(--Muted);
  stroke-width: 1.5;
  stroke-dasharray: 5 4;
  opacity: 0.8;
}

/*
	СТОЛБИКИ ВТОРОГО РЯДА (расходы дня под линией остатка).

	Цвет расхода — тот же --Bad, что у карточки «Расход» в сводке,
	но сильно разбавленный: столбики стоят фоном под линией и не
	должны с ней спорить. Заливка без обводки — у сотни столбиков
	за полгода обводка съедает саму заливку и превращает ряд
	в серую гребёнку.

	shape-rendering:crispEdges — иначе на узких столбиках
	сглаживание даёт разную видимую ширину у соседей, и ровный
	ряд выглядит рваным.
*/
.chart__bar {
  fill: var(--Bad);
  fill-opacity: 0.22;
  shape-rendering: crispEdges;
}

.chart__dot {
  stroke: var(--BgCard);
  stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
  r: calc(var(--ChartDot) * var(--ChartCoef));
}

/* ── наведение: полосы, перекрестие, подсказка ── */
/*
	Полоса во всю высоту под каждой точкой. Прозрачная, но события
	получает: fill:none их не получает, fill:transparent — получает.
	Мишень шириной в промежуток между точками, а не в четыре пикселя
	кружка: попасть можно и мышью, и пальцем.
*/
.chart__hit {
  fill: transparent;
  cursor: crosshair;
}

.chart__cursor {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.1s ease;
}

.chart__cursor.is-on {
  opacity: 1;
}

.chart__cross {
  stroke: var(--Muted);
  stroke-width: 1;
  stroke-dasharray: 2 3;
  vector-effect: non-scaling-stroke;
}

.chart__ring {
  fill: var(--BgCard);
  stroke: var(--Ink);
  stroke-width: 2;
  vector-effect: non-scaling-stroke;
  r: calc(var(--ChartRing) * var(--ChartCoef));
}

/*
	Подсказка — HTML НАД холстом, а не текст внутри него: только так
	она рисуется настоящими пикселями и шрифтом сайта на любой ширине
	(вся арифметика выше — про то, чего стоит обратное).

	Координаты ставит ChartInit в core.js; --Arrow — сдвиг хвостика,
	когда сама подсказка прижата к краю карточки, а точка осталась
	в стороне.
*/
.chart__tip {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 4;
  transform: translate(-50%, calc(-100% - 12px));
  padding: 8px 11px;
  border-radius: 12px;
  background: var(--BgDeep);
  color: var(--Bg);
  font-family: var(--Font);
  font-size: 12px;
  line-height: 1.35;
  white-space: nowrap;
  box-shadow: var(--ShadowLg);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s ease;
}

.chart__tip.is-on {
  opacity: 1;
}

.chart__tip i {
  display: block;
  font-style: normal;
  opacity: 0.62;
}

.chart__tip b {
  display: block;
  font-size: 15px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.chart__tip u {
  display: block;
  text-decoration: none;
  opacity: 0.62;
}

/* хвостик: ромб под подсказкой, под самой точкой */
.chart__tip::after {
  content: "";
  position: absolute;
  left: calc(50% + var(--Arrow, 0px));
  top: 100%;
  width: 10px;
  height: 10px;
  margin: -5px 0 0 -5px;
  border-radius: 2px;
  background: var(--BgDeep);
  transform: rotate(45deg);
}

/* у верхнего края холста подсказка не помещается сверху — роняем вниз */
.chart__tip--below {
  transform: translate(-50%, 12px);
}

.chart__tip--below::after {
  top: 0;
}

.chart__legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  margin: 10px 0 0;
  padding: 0 8px;
  list-style: none;
  font-size: 13px;
  color: var(--InkSoft);
}

.chart__legend li {
  display: flex;
  align-items: center;
  gap: 6px;
}

.chart__legend__dot {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  flex: 0 0 auto;
}

/*
	На узком экране холст всего около 120 px высотой, и подпись
	в 12 px занимает на нём десятую часть. Кегль там меньше — это
	не «мобильная версия ради мобильной версии», а то же правило:
	подпись должна оставаться подписью.
*/
@media (max-width: 640px) {
  .chart-box {
    --ChartLabel:10px;
  }
  .chart__tip {
    font-size: 11px;
  }
  .chart__tip b {
    font-size: 14px;
  }
}
/*
	Спарклайн в строке таблицы: только форма ряда, без осей и подписей.
	Ширина фиксированная — колонка не должна прыгать от строки к строке.
*/
.app__spark {
  width: 140px;
}

.spark {
  display: block;
  width: 120px;
  height: 28px;
}

.spark path {
  fill: none;
  stroke: var(--AccentInk);
  stroke-width: 1.5;
  stroke-linejoin: round;
}

/* «динамика →» рядом с названием категории в отчёте */
.app__trend {
  margin-left: 10px;
  font-size: 12px;
  white-space: nowrap;
}

/*
	Позиции чека в карточке операции.

	Названия с ценника длинные и в верхнем регистре («МОЛОКО ФУДМ 2,5%
	0,2Л»), поэтому колонка названия не ограничивается по ширине, а числа
	прижаты вправо табличными цифрами — иначе разряды не встают друг
	под друга и сравнить строки глазом нельзя.
*/
.app__table--items td:first-child {
  max-width: none;
  line-height: 1.4;
}

.app__table--items td.app__num {
  white-space: nowrap;
}

.app__table--items tbody tr:hover {
  background: var(--BgSoft);
}

/*
	Строки выписки, которые парсер не понял (экран загрузки).

	Моноширинный шрифт здесь не украшение: в этих строках важны позиции
	колонок — по ним видно, съехала разметка PDF или строка вообще не про
	операцию. Пропорциональный шрифт это скрывает.
*/
.app__unparsed {
  margin: 0 0 18px;
  padding: 14px 16px;
  list-style: none;
  max-height: 260px;
  overflow: auto;
  background: var(--BgSoft);
  border: 1px solid var(--Line);
  border-radius: var(--Radius);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  line-height: 1.6;
  color: var(--InkSoft);
  white-space: pre;
}

.app__empty {
  margin: 16px 0;
  padding: 20px;
  border: 1px dashed var(--Line);
  border-radius: var(--Radius);
  color: var(--Muted);
}

.app__warn {
  color: var(--Warn);
}

/* ── карточки сводки ── */
.app__cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--Gap);
  margin: 0 0 8px;
}

.app__card {
  padding: 18px 20px;
  border: 1px solid var(--Line);
  border-radius: var(--RadiusLg);
  background: var(--BgCard);
  box-shadow: var(--Shadow);
}

.app__card span {
  display: block;
  font-size: 13px;
  color: var(--Muted);
}

.app__card b {
  display: block;
  margin-top: 6px;
  font-family: var(--FontHead);
  font-size: 26px;
  font-weight: 800;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.app__card--in b {
  color: var(--Ok);
}

.app__card--out b {
  color: var(--Bad);
}

/*
	«Всего сейчас» — единственная карточка, которая отвечает не за период,
	а за текущее положение дел. Соседние три считают месяц, и без отличия
	итог читался бы как ещё одна месячная цифра.
*/
.app__card--total {
  border-color: rgba(11, 113, 86, 0.32);
  background: var(--AccentSoft);
}

.app__card--total b {
  color: var(--AccentInk);
}

/*
	Разложение итога по видам счетов — строкой под карточками.

	Сетка «подпись | значение» тут не годится: пар три-четыре, и в две
	колонки они растягиваются на пол-экрана пустотой. Нужна не таблица
	реквизитов, а строка «карты 1 200 000 ₸ · наличные 45 000 ₸».
*/
.app__facts--kinds {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
  margin: 12px 0 0;
  padding: 14px 18px;
  border: 1px solid var(--Line);
  border-radius: var(--RadiusLg);
  background: var(--BgCard);
  font-size: 14px;
}

.app__facts--kinds dt {
  color: var(--Muted);
}

.app__facts--kinds dd {
  margin: 0 18px 0 0;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Приписка под суммой: пересчёт в тенге, курс, пояснение. */
.app__small {
  font-size: 12px;
  line-height: 1.35;
}

/*
	Снятые с разбора (QueueView::PrintDismissed) — сгруппированный список.

	Одна карточка на все строки, строки разделены волосяной линией
	от начала текста. Раньше каждая снятая задача печаталась такой же
	крупной карточкой, как в очереди, и десять штук занимали три экрана:
	форма карточки оправдана там, где на ней работают, а здесь по списку
	только ищут глазами.
*/
.dis {
  overflow: hidden;
  border-radius: var(--RadiusLg);
  background: var(--BgCard);
}

.dis__row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 11px 16px;
}

.dis__row + .dis__row::before {
  content: "";
  position: absolute;
  left: 16px;
  right: 0;
  top: 0;
  height: 1px;
  background: var(--LineSoft);
}

.dis__main {
  flex: 1 1 auto;
  min-width: 0;
}

.dis__kind {
  display: block;
  font-size: 12px;
  color: var(--Muted);
}

/*
	Главная строка в одну строку и с многоточием: в payload бывает
	и двести символов, а перенос на три строки ломает список ровно так же,
	как ломали его карточки.
*/
.dis__what {
  display: block;
  margin: 1px 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dis__sub {
  display: block;
  font-size: 12px;
  color: var(--Muted);
}

.dis__act {
  flex: 0 0 auto;
  margin: 0;
}

/* ── таблицы ── */
.app__table {
  width: 100%;
  border-collapse: collapse;
  background: var(--BgCard);
  border: 1px solid var(--Line);
  border-radius: var(--RadiusLg);
  overflow: hidden;
}

.app__table th {
  padding: 12px 16px;
  text-align: left;
  font-size: 13px;
  font-weight: 600;
  color: var(--Muted);
  background: var(--BgSoft);
  border-bottom: 1px solid var(--Line);
}

.app__table td {
  padding: 12px 16px;
  border-bottom: 1px solid var(--LineSoft);
  vertical-align: top;
}

.app__table tr:last-child td {
  border-bottom: none;
}

.app__table small {
  display: block;
  margin-top: 2px;
  font-size: 13px;
  color: var(--Muted);
}

/* Суммы: табличные цифры, чтобы разряды стояли столбиком */
.app__num {
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

.app__pos {
  color: var(--Ok);
}

.app__neg {
  color: var(--Bad);
}

/*
	ХВОСТ ЧЕКА: чаевые, доставка, возврат за вес.

	Приглушённый, а не красный, и это осознанно. Хвост — это НОРМА,
	а не расхождение: деньги названы и объяснены. Красным он читался бы
	как «тут ошибка», и колонка, заведённая ради спокойствия, начала бы
	тревожить на каждой второй строке.

	Тревожит только один случай — хвост без вида: от него человеку
	и правда нужно действие.
*/
.app__tail {
  white-space: nowrap;
  color: var(--Muted);
  font-variant-numeric: tabular-nums;
}

.app__tail--ask {
  color: var(--Warn);
  font-weight: 600;
}

.app__date {
  white-space: nowrap;
  color: var(--Muted);
  font-variant-numeric: tabular-nums;
}

.app__actions {
  text-align: right;
  white-space: nowrap;
}

/*
	ТАБЛИЦА ЧЕКОВ — почему у неё свои правила ширины.

	Девять колонок требовали 1053px даже после того, как браузер
	разорвал в них весь текст по одному слову на строку. Контейнер
	кабинета шире 1160px не бывает, и на окне около 1090px таблица
	вылезала за него, утаскивая за собой горизонтальную прокрутку
	всей страницы: шапка кабинета оставалась на месте, а таблица
	уезжала правее её края.

	overflow-x:auto — не лечение, а способ спрятать симптом (и он
	в этом проекте запрещён отдельно: режет выпадающие списки).
	Колонок стало семь (см. ReceiptsView::PrintList), а здесь снимается
	то, что мешало оставшимся ужаться:

	  — отступы 12px вместо 16px: на семи колонках это 56px чистой
	    ширины, отданных тексту, а не воздуху;
	  — «Магазин», «Операция», «Разбор» переносятся по словам — это
	    фразы, а не значения, и рвать их посреди разряда нечего;
	  — суммы и даты по-прежнему nowrap: «30 801,00», разорванное
	    на «30 / 801,00», читается как два числа.
*/
.app__table--receipts th,
.app__table--receipts td {
  padding: 12px;
}

.app__table--receipts td.app__shop {
  overflow-wrap: anywhere;
}

/* номер документа под названием магазина — подпись, а не часть названия */
.app__table--receipts td.app__shop small {
  font-weight: 400;
}

.app__table--receipts th:nth-child(4),
.app__table--receipts td:nth-child(4),
.app__table--receipts th:nth-child(5),
.app__table--receipts td:nth-child(5),
.app__table--receipts th:nth-child(6),
.app__table--receipts td:nth-child(6) {
  white-space: normal;
}

/* Последняя колонка — «удалить»: узкая, к правому краю, без переноса. */
.app__table--receipts th:last-child,
.app__table--receipts td:last-child {
  width: 1%;
  text-align: right;
  white-space: nowrap;
}

/*
	Дата покупки в две строки.

	В одну строку «09.09.2026 14:15» — это 166px нерушимой ширины,
	самая широкая колонка таблицы ради значения, которое прекрасно
	читается столбиком. Время уходит под дату мелким: оно уточнение,
	а не самостоятельный факт.
*/
.app__date__day {
  display: block;
  color: var(--Ink);
}

.app__date__time,
.app__date__load {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  color: var(--Muted);
}

/*
	«дата не распознана» — это фраза, а не значение, и запрет переноса
	с неё снимается: в одну строку она требовала 193px и делала колонку
	даты самой широкой в таблице — ради надписи о том, что данных нет.
	Сама дата и время остаются nowrap правилом .app__date выше.
*/
.app__table--receipts td.app__date .app__muted,
.app__table--receipts td.app__date .app__date__load {
  display: block;
  white-space: normal;
}

/* Сумма чека и под ней — из скольких позиций она сложилась. */
.app__table--receipts .app__sum {
  display: block;
  font-variant-numeric: tabular-nums;
}

.app__table--receipts .app__num small {
  white-space: normal;
  font-size: 12px;
}

/*
	Хвост под вердиктом, а не в своей колонке: «не сошёлся на 510,00»
	и следом «чаевые 200,00» — это одна мысль, и складывать её глазами
	через полтаблицы человек не обязан.
*/
.app__verdict__tail {
  margin-top: 4px;
}

/*
	Хвост в таблице чеков переносится, а в карточке операции — нет.

	Разница не в прихоти: в карточке подпись стоит одна в широкой
	строке и переносить её незачем, а здесь она делит колонку
	с фразой вердикта и в одну строку задавала колонке 159px.
	Ломать при переносе нечего: сумма и «₸» связаны неразрывным
	пробелом внутри Money::formatWithCurrency, так что перенос
	приходится на « · » между суммой и названием вида.
*/
.app__table--receipts .app__tail {
  white-space: normal;
}

/*
	«смотреть · скачать» в узкой колонке встают столбиком, и точка
	между ними прячется: иначе первая строка кончается висящим «·».
	На широком экране — если вдруг влезут в строку — точка вернётся.
*/
.app__table--receipts td:nth-child(6) .app__link {
  display: block;
}

.app__table--receipts td:nth-child(6) .app__sep {
  display: none;
}

/* «заказ частями» — подпись под номерами, а не хвост последнего номера. */
.app__table--receipts td:nth-child(4) .app__small {
  display: block;
  margin-top: 2px;
}

/*
	РАМКА ВНУТРИ РАМКИ — что здесь было сломано.

	Раньше оформление поля (граница, фон, отступы) вешалось на .pick —
	на ОБЁРТКУ, которую строит PickInit. Но внутри обёртки лежит
	настоящий <input>, и он оформлен как поле общим правилом
	«#mb .app .pick__input». Получались две вложенные рамки: внешняя
	от обёртки и внутренняя от поля, — а подпись сжималась вдвое
	и обрывалась многоточием на «Между св…», хотя место в ячейке было.

	Правило простое и держится по всему проекту: вид поля принадлежит
	ПОЛЮ. Обёртка отвечает только за место — ширину, прижатие вправо
	и позицию стрелки, которая рисуется её псевдоэлементом.
*/
.app__actions select,
.app__actions .pick {
  max-width: 210px;
  /* ячейка прижата вправо, а блочная обёртка сама туда не встанет */
  margin-left: auto;
  /*
  	ТЕКСТ ВНУТРИ ПОЛЯ ЧИТАЕТСЯ СЛЕВА, КАК ЛЮБОЙ ДРУГОЙ ТЕКСТ.

  	text-align:right у ячейки нужен, чтобы поле прижалось к правому
  	краю строки, — и он же наследуется ВНУТРЬ поля. Названия
  	категорий выстраивались по правому краю, а в раскрытом списке
  	по правому краю вставали и все пункты: «Услуги», «— Парикм…»,
  	«— Химчист…» — читать такое можно, только водя глазами
  	по рваному левому краю.

  	text-align-last — для самого поля в закрытом виде: без него
  	Chrome оставляет выбранный пункт прижатым вправо.
  */
  text-align: left;
  text-align-last: left;
}

/* Пункты раскрытого списка рисует система — им правило нужно отдельно */
.app__actions select option {
  text-align: left;
}

/*
	Компактный вид: в строке таблицы поле не должно быть ростом
	с поле формы. #mb — чтобы перебить общее «#mb .app select»,
	у которого специфичность выше простого класса.

	Справа у поля поиска 34px под стрелку, у родного списка — свои
	10px: стрелку он рисует сам, и чужой отступ оставил бы дыру.
*/
#mb .app .app__actions select,
#mb .app .app__actions .pick__input {
  min-height: 0;
  padding: 6px 10px;
  border-radius: 8px;
  font-size: 13px;
  color: var(--InkSoft);
}

#mb .app .app__actions .pick__input {
  padding-right: 34px;
}

/* Исключённая из аналитики операция: видна, но приглушена */
.app__row--off td {
  opacity: 0.5;
}

.app__table--tx td:first-child {
  width: 1%;
}

/*
	КОЛОНКА КАТЕГОРИИ В СПИСКЕ ОПЕРАЦИЙ.

	БЫЛО: ширину ячейке отдавала таблица по остаточному принципу,
	и на длинных описаниях («Покупка · FACEBK *RABL45EK74 Курсовая
	разница») селекту доставалось около сотни пикселей — от названия
	категории оставалось «Пр…», «Од…», «— Д…». Поле для выбора,
	по которому нельзя прочесть текущее значение, не выполняет
	ни одной своей задачи: ни показать, ни сменить.

	Ширина задаётся колонке, а не полю: max-width у селекта ничего
	не значит, пока сама ячейка сжата. 210px — предел, при котором
	таблица ещё укладывается в ширину раздела без горизонтальной
	прокрутки (замерено: 57+132+386+293+210 в 1048), и при этом
	видно название категории целиком, а не «Пр…».
*/
.app__table--tx .app__actions {
  width: 210px;
}

.app__table--tx .app__actions select,
.app__table--tx .app__actions .pick {
  width: 100%;
  max-width: none;
}

/*
	Описание — единственная резиновая колонка, и её минимум держали
	неразрывные куски вроде «FACEBK *RABL45EK74»: таблица не могла
	сжать ячейку уже такого слова и выезжала за край страницы.
	Перенос внутри слова разрешён только здесь — это машинный
	идентификатор из выписки, а не текст, который читают целиком.
*/
.app__table--tx td:not([class]) {
  overflow-wrap: anywhere;
}

/*
	КАРТОЧКА ЗАГРУЖЕННОЙ ВЫПИСКИ.

	Здесь была таблица на семь колонок плюс полоса действий, и она
	провалилась дважды. По содержанию: колонки сжимались до того, что
	название счёта рассыпалось на четыре строки по слову, а записи
	и полосы действий чередовались неразличимой лапшой. По технике:
	чтобы таблица не выталкивала страницу, она ездила внутри себя,
	а контейнер прокрутки обрезал раскрытый список счетов — из семи
	пунктов было видно два.

	Карточка снимает обе беды разом: значений немного и они подписаны,
	ширину диктует сетка, а не самое длинное название, и обрезать
	выпадающий список нечему — контейнера прокрутки нет.

	auto-fill с минимумом 380px: на широком экране карточки встают
	в два-три столбца, на ноутбуке в один-два, на телефоне в один.
	Ни одного медиазапроса для этого не нужно.
*/
.app__stmts {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
  gap: var(--Gap);
  margin: 0 0 8px;
}

@media (max-width: 460px) {
  /* на телефоне 380px минимума уже нет — иначе сетка вылезет за экран */
  .app__stmts {
    grid-template-columns: 1fr;
  }
}
.app__stmt {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px 20px;
  border: 1px solid var(--Line);
  border-radius: var(--RadiusLg);
  background: var(--BgCard);
  box-shadow: var(--Shadow);
}

/*
	Шапка: слева счёт, справа итог сверки. Именно эти две вещи человек
	ищет в списке первым делом — «чья выписка» и «можно ли ей верить».
*/
.app__stmt__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.app__stmt__acc {
  font-family: var(--FontHead);
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.app__stmt__mark {
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
}

/* Период, время загрузки и номер — приглушённой строкой: это опознавательные
   признаки, а не показатели, и спорить за внимание с суммами они не должны */
.app__stmt__meta {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--Muted);
  /* Ссылка на файл живёт внутри приглушённой строки и наследует
     её размер: это такой же опознавательный признак, как период
     и номер, и выделять его кеглем значило бы звать нажимать
     на неё каждый раз. Цвет ссылочный — нажимать всё-таки можно. */
}
.app__stmt__meta .app__link {
  font-size: inherit;
}

/* «Операций 55, из них новых 1» — объяснение, а не тревога, поэтому
   тем же приглушённым тоном, что и метаданные, но своей строкой:
   иначе читается как продолжение периода загрузки */
.app__stmt__absorbed {
  margin: 6px 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--Muted);
}

/*
	Три числа в ряд. dl, а не таблица: это подписи к значениям одного
	объекта, и сравнивать их между карточками по вертикали никто
	не будет — сравнивают внутри одной.
*/
.app__stmt__nums {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 26px;
  margin: 0;
  padding: 12px 0 0;
  border-top: 1px solid var(--LineSoft);
}

.app__stmt__nums dt {
  font-size: 12px;
  color: var(--Muted);
}

.app__stmt__nums dd {
  margin: 2px 0 0;
  font-size: 16px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/*
	СКЛАДКА С ДЕЙСТВИЯМИ.

	Перенос и удаление нужны редко — счётом ошибаются раз в сто загрузок.
	Всё остальное время список счетов и две кнопки в каждой карточке
	спорят за внимание с суммами и сверкой, ради которых на экран
	и заходят, а «удалить» на виду рядом с обычным чтением — прямое
	приглашение промахнуться.

	<details> вместо кнопки со скриптом: раскрытие браузер умеет сам,
	и при отключённом JS складка просто открыта, а не заперта навсегда.
*/
.app__stmt__more {
  padding-top: 12px;
  border-top: 1px solid var(--LineSoft);
}

.app__stmt__toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: fit-content;
  padding: 4px 2px;
  color: var(--InkSoft);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  list-style: none; /* убирает родной треугольник в Firefox */
  user-select: none;
}

/* и в Safari/Chrome — у них он рисуется отдельным псевдоэлементом */
.app__stmt__toggle::-webkit-details-marker {
  display: none;
}

.app__stmt__toggle:hover {
  color: var(--Ink);
}

/* Своя стрелка: поворачивается, когда складка открыта — так видно,
   что это переключатель, а не ссылка */
.app__stmt__toggle::before {
  content: "";
  width: 7px;
  height: 7px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.16s ease;
}

.app__stmt__more[open] .app__stmt__toggle::before {
  transform: rotate(45deg);
}

.app__stmt__form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin: 12px 0 0;
}

/*
	Список счетов тянется на всё свободное место, но НЕ выталкивает
	кнопки на следующую строку: flex-basis 180px с правом сжаться.
	Названия у счетов длинные, и обрезанное многоточием «Депози…»
	не даёт выбрать — ровно с этого начались правки этого экрана.
*/
#mb .app .app__stmt__form select {
  flex: 1 1 180px;
  min-width: 0;
  min-height: 0;
  width: auto;
  padding: 8px 34px 8px 12px;
  border-radius: 8px;
  font-size: 14px;
}

/* «удалить» отодвинуто от «Перенести»: разные по цене действия,
   и соседство вплотную приглашает промахнуться */
.app__stmt__del {
  margin-left: 6px;
  white-space: nowrap;
}

/* Кнопка внутри карточки — ростом с поле рядом, а не с кнопку формы */
.app__btn--small {
  padding: 8px 16px;
  font-size: 14px;
}

/* ── формы ── */
.app__form {
  margin: 20px 0;
  padding: 24px;
  border: 1px solid var(--Line);
  border-radius: var(--RadiusLg);
  background: var(--BgCard);
}

.app__row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 0 var(--Gap);
}

.app__field--check {
  display: flex;
  align-items: center;
  gap: 8px;
}

.app__field--check span {
  margin: 0;
}

.app__filters {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 0 var(--Gap);
  align-items: end;
  margin: 0 0 8px;
  padding: 16px 20px;
  border: 1px solid var(--Line);
  border-radius: var(--RadiusLg);
  background: var(--BgCard);
}

.app__filters .app__btn {
  margin-bottom: 16px;
}

/*
	Свой период на экранах «категория во времени» и «товар».
	В Firefox и Safari на ПК поля type=month нет — браузер показывает
	текстовое поле, и подсказка «ГГГГ-ММ» говорит, что в него писать.
	«Сбросить» — ссылка, а не кнопка: она уводит на адрес без фильтров.
*/
.app__filters--period {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 12px 28px;
  margin: 12px 0 16px;
}

/* hidden должен побеждать display:flex/grid — иначе панель не прячется */
.app__filters--period[hidden],
.app__range__pair[hidden] {
  display: none;
}

/*
	Период и сравнение — две группы «с — по», как в Google Analytics:
	подпись группы сверху, поля дат рядом. На телефоне группы
	встают друг под другом, поля — по-прежнему парой.
*/
.app__range {
  display: flex;
  flex-wrap: wrap;
  gap: 0 12px;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  flex: 1 1 300px;
}

.app__range legend {
  margin: 0 0 8px;
  padding: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--InkSoft);
}

.app__range .app__field {
  flex: 1 1 140px;
  min-width: 0;
}

.app__range .app__field:has(select) {
  flex: 1 1 100%;
}

.app__range__pair {
  display: flex;
  flex-wrap: wrap;
  gap: 0 12px;
  flex: 1 1 100%;
}

.app__range .app__hint {
  flex: 1 1 100%;
  margin: 0 0 12px;
}

.app__range__acts {
  display: flex;
  align-items: center;
  gap: 16px;
  flex: 1 1 100%;
}

.app__range__acts .app__btn {
  margin-bottom: 0;
}

.app__filters--period input[type=date] {
  width: 100%;
  min-width: 0;
}

/* Переключатель приход/расход: два крупных таргета, а не радиокнопки */
.app__toggle {
  display: inline-flex;
  padding: 4px;
  margin: 0 0 20px;
  border-radius: 999px;
  background: var(--BgSoft);
  border: 1px solid var(--Line);
}

/*
	Подсветка выбранного пункта.

	Было два источника правды: класс app__toggle-item--cur, который печатал
	сервер, и живое правило :has(input:checked). Класс оставался на пункте,
	выбранном при загрузке, а живое правило переезжало на новый — и оба
	пункта выглядели активными одновременно.

	Остался один признак — сам checked у радиокнопки. Разметку печатает
	HtmlTags::InputRadio парой «input + label for=…», поэтому подсветку
	вешаем на соседний label: правило работает во всех браузерах и не зависит
	от поддержки :has().
*/
.app__toggle input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.app__toggle label {
  display: inline-block;
  padding: 8px 22px;
  border-radius: 999px;
  cursor: pointer;
  font-weight: 600;
  font-size: 15px;
  color: var(--Muted);
  user-select: none;
  transition: background 0.16s ease, color 0.16s ease;
}

.app__toggle input:checked + label {
  background: var(--Bg);
  color: var(--Ink);
  box-shadow: var(--Shadow);
}

/* Клавиатура: радиокнопка спрятана, обводку показываем на подписи. */
.app__toggle input:focus-visible + label {
  outline: 2px solid var(--Accent);
  outline-offset: 2px;
}

/*
	Кнопка и ссылка рядом («Сохранить» / «Отмена»).

	Раньше их разделял единственный пробел между тегами в шаблоне.
	У <input type=submit> задан display:inline-flex, и этого пробела
	не хватало: «Отмена» вставала вплотную к скруглению кнопки и
	читалась как наехавшая на неё.

	Отступ между двумя органами управления обязан задаваться стилями,
	а не переносом строки в PHP: перенос легко пропадает при любой
	правке шаблона, и никто не поймёт, почему поехало.

	vertical-align:middle — чтобы ссылка стояла по центру кнопки,
	а не по базовой линии её текста.
*/
.app__form .app__btn,
.app__form .app__btn + .app__link {
  vertical-align: middle;
}

.app__form .app__btn {
  margin-right: 12px;
}

/* ── пагинация ── */
.app__pager {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 20px 0;
}

.app__page {
  min-width: 36px;
  padding: 6px 10px;
  text-align: center;
  border: 1px solid var(--Line);
  border-radius: 8px;
  color: var(--InkSoft);
  font-variant-numeric: tabular-nums;
}

.app__page:hover {
  background: var(--BgSoft);
  text-decoration: none;
}

.app__page--cur {
  background: var(--Ink);
  border-color: var(--Ink);
  color: #FFF;
}

/*
	Разрыв в ряду номеров. Без рамки и без наведения: это знак
	«здесь пропущено», а не кнопка. Рамка делала бы его похожим
	на страницу, которую можно нажать.
*/
.app__page--gap {
  min-width: 0;
  padding: 6px 2px;
  border-color: transparent;
  color: var(--Muted);
}

/*
	Шаг назад-вперёд на краю остаётся на месте приглушённым,
	а не исчезает: пропадающая кнопка сдвигает весь ряд, и
	следующий щелчок попадает не туда, куда целились.
*/
.app__page--off {
  border-color: var(--LineSoft);
  color: var(--Line);
  cursor: default;
}

/*
	ПРЫЖОК НА ПРОИЗВОЛЬНУЮ СТРАНИЦУ.

	Ряд номеров отвечает на «шаг вперёд» и «в конец», список —
	на «мне на сто сороковую». Это разные действия, поэтому список
	стоит отдельной строкой под рядом, а не втискивается в него.

	Кнопка «Перейти» настоящая, а не украшение: переход по onchange
	добавлен сверх неё, но поле, которое без скрипта молчит, — это
	кнопка, нарисованная на стене.
*/
.app__jump {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 10px;
  margin: -8px 0 20px;
}

.app__jump-f {
  flex: 0 1 220px;
  margin: 0;
}

.app__jump-s {
  width: 100%;
}

/* ── отчёты ── */
/*
	ЗДЕСЬ БЫЛИ .app__table--calc, .app__table--report, .app__row--total
	и .app__row--sub — оформление отчёта, когда он был двумя таблицами.
	Убраны вместе с самими таблицами: сверка остатков стала списком
	строк-блоков (.app__calc), а категории — карточками (.app__cat).
	Причину см. в шапке этих блоков ниже; коротко — на 390px таблица
	получалась 423px, обрезалась вместе с шапкой, а колонки сравнения
	и вовсе прятались правилом display:none, то есть данные исчезали
	молча.

	Мёртвые правила не оставлены «на всякий случай»: правило без разметки
	ищут в коде и не находят, зачем оно, а потом боятся тронуть.
*/
.app__muted {
  color: var(--InkSoft);
}

.app__ok,
.app__bad {
  margin: 12px 0 24px;
  padding: 12px 14px;
  border-radius: 10px;
  line-height: 1.5;
}

.app__ok {
  background: rgba(34, 150, 83, 0.1);
  color: #1B7A45;
}

.app__bad {
  background: rgba(200, 50, 50, 0.1);
  color: #A32B2B;
}

.app__bad .app__link {
  color: inherit;
  text-decoration: underline;
}

/*
	Действие внутри серой справочной строки.

	Список «уже закрыты другим чеком» намеренно приглушён — это
	не выбор, а объяснение. Но одно действие там всё же есть
	(«тот же заказ»), и оно обязано читаться как кнопка, не ломая
	приглушённости списка: подчёркиванием и полным цветом, а не
	размером.
*/
.app__link--act {
  color: var(--AccentInk);
  text-decoration: underline;
  white-space: nowrap;
}

/* ── массовое выделение ── */
.app__check-cell {
  width: 36px;
}

.app__check-cell input {
  width: 17px;
  height: 17px;
}

/*
	Панель действий липнет к низу экрана: разбирают длинными списками,
	и уезжающая наверх панель означает прокрутку туда-обратно на каждую пачку.
*/
.app__bulkbar {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-top: 12px;
  padding: 12px 14px;
  background: var(--Bg);
  border: 1px solid var(--Line);
  border-radius: 12px;
  box-shadow: var(--Shadow);
}

.app__bulkbar__count {
  color: var(--InkSoft);
}

.app__bulkbar__count b {
  color: var(--Ink);
  font-variant-numeric: tabular-nums;
}

.app__bulkbar select,
.app__bulkbar .pick {
  min-width: 200px;
}

/*
	Панель массового действия выстраивается в одну линию.

	Мешали две мелочи, каждая заметна ровно тогда, когда смотришь
	на всю строку целиком:

	1. У флажков остался нижний отступ 12px из общего правила .app__check
	   (там он нужен: в формах флажки идут столбиком). В строке
	   с align-items:center этот отступ входит в высоту блока, и подписи
	   вставали на 6px выше остальных — половину отступа.

	2. Кнопка была 42px против 46px у списка. Разница маленькая, но
	   в ряду из двух скруглённых элементов читается как небрежность.
*/
.app__bulkbar .app__check {
  margin: 0;
}

.app__bulkbar .app__btn {
  min-height: 46px;
}

.app__check {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--InkSoft);
}

.app__check input {
  width: 16px;
  height: 16px;
}

/* ── очередь разбора ── */
.app__badge {
  display: inline-block;
  min-width: 18px;
  margin-left: 6px;
  padding: 1px 6px;
  background: #C8322E;
  border-radius: 9px;
  color: #FFF;
  font-size: 11px;
  font-weight: 600;
  line-height: 16px;
  vertical-align: middle;
}

.app__tabs--sub {
  flex-wrap: wrap;
} /* отступ — на .app__tabs */
/*
	Карточка задачи. Крупная и одна на экран — по таблице разбирают долго,
	а разбирать нужно каждый день. Ширина ограничена: длинная строка
	описания через весь монитор читается хуже, чем в колонку.
*/
.app__task {
  max-width: 640px;
  padding: 24px;
  background: var(--Bg);
  border: 1px solid var(--Line);
  border-radius: 16px;
  box-shadow: var(--Shadow);
}

.app__task__head {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 20px;
  color: var(--InkSoft);
  font-size: 13px;
}

.app__task__kind {
  font-weight: 600;
  color: var(--Ink);
}

.app__task__amount {
  margin: 0 0 8px;
  font-size: 34px;
  font-weight: 600;
  line-height: 1.1;
}

.app__task__what {
  margin: 0 0 6px;
  font-size: 17px;
  line-height: 1.4;
}

.app__task__meta {
  margin: 0;
  color: var(--InkSoft);
  font-size: 13px;
}

.app__task__payload {
  margin: 12px 0 0;
  font-size: 14px;
}

.app__task__payload dt {
  color: var(--InkSoft);
  font-size: 12px;
}

.app__task__payload dd {
  margin: 0 0 8px;
}

.app__task__form {
  margin-top: 24px;
}

/*
	Подсказки — главное действие экрана: одна кнопка закрывает задачу.
	Поэтому они стоят выше полного списка категорий и крупнее его.
*/
.app__task__hints {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 16px;
}

.app__btn--hint {
  padding: 10px 16px;
  background: var(--BgSoft);
  border: 1px solid var(--Line);
  font-size: 15px;
}

.app__btn--hint:hover {
  background: var(--Bg);
  border-color: var(--Ink);
}

/*
	Кандидат, который закрывает чек не полностью. Отличается заметно,
	но не тревожно: это законный выбор, а не ошибка, — просто человек
	должен видеть разницу до нажатия, а не после.
*/
.app__btn--part {
  border-color: rgba(180, 83, 9, 0.4);
  color: var(--Warn);
  white-space: normal;
  text-align: left;
}

.app__btn--part:hover {
  background: var(--WarnSoft);
  border-color: var(--Warn);
}

.app__task__pick {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
}

.app__task__pick select,
.app__task__pick .pick {
  flex: 1 1 240px;
}

/*
	Вид выпадающего списка.

	Раньше он описывался ТОЛЬКО внутри .app__field — а на экране разбора
	список лежит в .app__task__pick, и оставался системным: серая рамка
	Windows среди скруглённых полей. Признак «это поле ввода» не должен
	зависеть от того, в какую обёртку его положили.
*/
#mb .app select,
#mb .app .pick__input {
  min-height: 46px;
  padding: 12px 14px;
  border: 1px solid var(--Line);
  border-radius: var(--Radius);
  background: var(--Bg);
  color: var(--Ink);
  font-family: var(--Font);
  font-size: 15px;
}

#mb .app select:focus,
#mb .app .pick__input:focus {
  outline: 0;
  border-color: var(--Accent);
  box-shadow: 0 0 0 3px var(--AccentSoft);
}

/*
	═══════════════════════════════════════════════════════════════════
	Выпадающий список с поиском (jscripts/core.js → PickInit)
	═══════════════════════════════════════════════════════════════════

	Поле поиска намеренно описано ТЕМ ЖЕ правилом, что и родной select:
	это один и тот же элемент управления, просто в улучшенном виде,
	и две копии значений разъехались бы на первой правке палитры.

	Без скрипта разметка не появляется вовсе — страница остаётся с обычным
	списком, и ни одно правило ниже ни на что не влияет.
*/
/*
	Обёртка НЕ должна попадать в правила оформления полей (.app__field
	input/select и подобные): один раз она туда попала — и получила
	padding 12/14 и рамку, то есть стала вторым полем вокруг настоящего.
	Высота выросла с 46 до 72, и в фильтрах операций «Счёт» с «Категорией»
	встали выше соседей. Обёртка — это только система координат
	для выпадающего списка, у неё нет своего вида.

	Ширину тоже не задаём: в обычном блоке она и так во всю ширину,
	а во флекс-строке (панель массового действия) width:100% заставил бы
	её занять строку целиком и вытолкнуть соседей на следующую.
*/
.pick {
  position: relative;
  display: block;
}

/*
	display:block у поля — не косметика, а лечение.

	<input> по умолчанию inline-block, то есть стоит на строке. Строка
	тянет за собой межстрочный интервал body (16px × 1.6), и обёртка
	получалась 72px вместо 46 — на четверть выше соседних полей.
	В фильтрах операций это было видно сразу: «Счёт» и «Категория»
	висели выше «С даты» и «Поиска», хотя сетка выравнивает поля по низу.
*/
/*
	text-overflow нужен именно здесь. В узких местах — построчный список
	категорий в таблице операций — подпись длиннее поля, и <input> просто
	обрезает её на полуслове, да ещё и под стрелкой: «Между своими сче».
	Выглядит как поломанная вёрстка, хотя значение выбрано верно.
	Многоточие сообщает то же самое честно.
*/
.pick__input {
  display: block;
  width: 100%;
  cursor: pointer;
  text-overflow: ellipsis;
}

.pick--open .pick__input {
  cursor: text;
}

/*
	Родной список остаётся в потоке и прозрачен, а не display:none.
	Скрытому полю браузер не может показать сообщение проверки и молча
	отменяет отправку формы — на required-списке это выглядит как
	«кнопка не работает» (см. комментарий в PickBuild).
*/
.pick__native {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  min-height: 0;
  margin: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
}

/*
	Место под стрелку. Правило с #mb — чтобы перебить .app__field input,
	у которого из-за цепочки :not() специфичность выше обычного класса.
*/
#mb .app .pick__input {
  padding-right: 34px;
}

/* Стрелка: без неё поле читается как обычный ввод, а не как выбор. */
.pick::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 15px;
  width: 8px;
  height: 8px;
  margin-top: -6px;
  border-right: 2px solid var(--Muted);
  border-bottom: 2px solid var(--Muted);
  transform: rotate(45deg);
  pointer-events: none;
  transition: transform 0.16s ease, margin-top 0.16s ease;
}

.pick--open::after {
  margin-top: -2px;
  transform: rotate(225deg);
}

/*
	Координаты ставит JS (PickPlace): список открывается над страницей,
	чтобы его не резал overflow:hidden таблицы. Здесь остаётся только
	вид и запасные значения на случай, если скрипт не отработал.
*/
.pick__drop {
  position: fixed;
  z-index: 400;
  max-height: 284px;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 6px;
  border: 1px solid var(--Line);
  border-radius: var(--Radius);
  background: var(--BgCard);
  box-shadow: var(--ShadowLg);
}

.pick__opt {
  padding: 8px 10px;
  border-radius: 8px;
  font-size: 15px;
  line-height: 1.35;
  color: var(--Ink);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Текущее значение узнаётся до того, как человек начал читать список. */
.pick__opt--cur {
  font-weight: 700;
}

/* Подсветка идёт за клавиатурой и за мышью — состояние ровно одно. */
.pick__opt--on {
  background: var(--AccentSoft);
  color: var(--AccentInk);
}

.pick__opt:hover {
  background: var(--LineSoft);
}

.pick__opt--on:hover {
  background: var(--AccentSoft);
}

/* Чем строка подошла: в списке из тридцати найденных это не очевидно. */
.pick__hit {
  background: var(--WarnSoft);
  color: var(--Warn);
  border-radius: 3px;
}

/*
	Родитель дочерней категории в результатах поиска.

	Приглушён и не переносится: это подпись «чего именно аренда»,
	а не второе название. Читатель ищет глазами само название,
	родитель нужен ему только чтобы не спутать две одинаковые ветки.
*/
.pick__parent {
  color: var(--Muted);
  white-space: nowrap;
}

.pick__empty {
  padding: 8px 10px;
  font-size: 14px;
  color: var(--Muted);
}

/*
	Поле выбора файла.

	Из общего правила .app__field оно исключено намеренно: <input type=file>
	не поле ввода, а кнопка с подписью, и одинаковая с текстовым полем
	рамка вокруг него врала бы, что туда можно печатать. Но и системным
	оставлять нельзя — серая квадратная кнопка среди скруглённых полей
	выглядит чужой.

	Поэтому красим саму кнопку выбора, а имя файла остаётся рядом
	обычным текстом.
*/
.app__field input[type=file] {
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: var(--InkSoft);
  font-family: var(--Font);
  font-size: 15px;
  line-height: 46px;
}

.app__field input[type=file]::file-selector-button {
  margin-right: 12px;
  padding: 12px 18px;
  border: 1px solid var(--Line);
  border-radius: 999px;
  background: var(--Bg);
  color: var(--Ink);
  font-family: var(--Font);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
}

.app__field input[type=file]::file-selector-button:hover {
  background: var(--BgSoft);
  border-color: var(--Ink);
}

/*
	Зазор между кнопками карточки задаём стилем, а не пробелом в шаблоне:
	пробел съедает любое переформатирование, и «Разобрано» с «Отложить»
	слипаются. Та же причина, что у счётчика во вкладках.
*/
.app__task__form > .app__btn,
.app__task__form > .app__link {
  margin-right: 12px;
}

.app__task__form .app__check {
  margin-top: 14px;
}

.app__task__skip {
  margin-top: 20px;
}

.app__task__skip .app__link {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font: inherit;
}

/* ── здоровье данных ── */
.app__table--health td:first-child {
  max-width: 520px;
}

.app__table--health small {
  display: block;
  margin-top: 4px;
  color: var(--InkSoft);
  line-height: 1.45;
}

.app__table--health .app__num {
  white-space: nowrap;
}

.app__table--health .app__num div {
  margin-top: 6px;
  font-size: 13px;
}

/*
	Проблемная строка помечается полосой слева, а не заливкой: строк
	немного, и залитая красным таблица читается как «всё сломано»,
	хотя сломано одно.
*/
.app__row--bad td:first-child {
  border-left: 3px solid #C8322E;
  padding-left: 13px;
}

/* ── карточка операции ── */
.app__rowlink {
  color: inherit;
}

.app__rowlink:hover {
  color: var(--Ink);
}

.app__card--tx {
  max-width: 680px;
  padding: 24px;
  background: var(--Bg);
  border: 1px solid var(--Line);
  border-radius: 16px;
  box-shadow: var(--Shadow);
}

/*
	Реквизиты в две колонки: подпись слева узкой, значение справа.
	Определения списком, а не таблицей — это и есть список определений,
	и разметка должна об этом говорить.
*/
.app__facts {
  display: grid;
  grid-template-columns: 180px 1fr;
  gap: 10px 16px;
  margin: 24px 0 0;
  font-size: 15px;
}

.app__facts dt {
  color: var(--InkSoft);
}

/*
	Переключатель периода сразу под реквизитами (отчёт по категории)
	прилипал к последней строке: у .app__facts отступа снизу нет,
	у .app__tabs — только снизу. Зазор даёт сосед, а не сами реквизиты:
	в карточке операции под ними форма со своим отступом.
*/
.app__facts + .app__tabs {
  margin-top: 20px;
}

.app__facts dd {
  margin: 0;
}

.app__facts small {
  color: var(--InkSoft);
}

.app__task__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 24px;
}

.app__btn--danger {
  color: #A32B2B;
  border-color: rgba(200, 50, 50, 0.35);
}

.app__btn--danger:hover {
  background: rgba(200, 50, 50, 0.08);
  border-color: #C8322E;
}

/* Похожие операции — не список для чтения, а подтверждение масштаба. */
.app__similar {
  margin: 12px 0 0;
  padding-left: 18px;
  font-size: 14px;
}

.app__similar li {
  margin-bottom: 4px;
  color: var(--InkSoft);
}

.app__history {
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
  font-size: 14px;
}

.app__history li {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  padding: 8px 0;
  border-top: 1px solid var(--Line);
}

.app__history__at {
  color: var(--InkSoft);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.app__history__who {
  color: var(--InkSoft);
}

.app__history__what {
  flex: 1 1 240px;
}

/*
	══════════════════════════════════════════════════════════════════
	 СВЕРКА С БАНКОМ  (Ledger\Views\ReconciliationView)
	══════════════════════════════════════════════════════════════════

	ЗДЕСЬ БЫЛИ .ob__* — оформление экрана «Входящие остатки». Экрана
	больше нет: он считал разницу с банком своей арифметикой и расходился
	в числах с Ledger\ReconciliationService, а две параллельные
	арифметики на один вопрос — это гарантия того, что человек однажды
	увидит два разных ответа. Правила удалены вместе с разметкой:
	мёртвое правило потом ищут в коде, не находят, зачем оно, и боятся
	тронуть.

	НИ ОДНОЙ ТАБЛИЦЫ, И ЭТО ГЛАВНОЕ РЕШЕНИЕ ЭКРАНА.

	Чисел здесь под три десятка, у каждого длинная подпись («Банк:
	книжный остаток») и пояснение в две строки. Табличная вёрстка
	на таком материале даёт один из двух исходов: либо колонки
	схлопываются и текст слипается в кашу, либо таблица не помещается
	в ширину и просит overflow-x:auto — а он, однажды поставленный,
	обрезает заодно и всё, что должно вылезать за пределы контейнера,
	включая выпадающие списки. На этом уже обжигались на экране импорта.

	Поэтому каждое число — блок из трёх строк: подпись, значение,
	пояснение. Раскладывает их auto-fit-сетка: на широком экране три
	колонки, на ноутбуке две, на телефоне одна. Ни одного медиазапроса
	для перестроения не нужно, и вылезти за экран нечему — минимум
	колонки задан в min(…,100%), а не жёстким числом пикселей.
*/
.rec {
  margin: 0 0 28px;
  padding: 20px 22px 22px;
  border: 1px solid var(--Line);
  border-radius: var(--RadiusLg);
  background: var(--BgCard);
  box-shadow: var(--Shadow);
}

/* Складка «почему система не считает это сама» — до всех карточек */
.rec__why {
  margin: 0 0 20px;
}

.rec__why p {
  margin: 8px 0 0;
  max-width: 78ch;
}

/* ── 1. ответ одной строкой ── */
.rec__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 14px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--LineSoft);
}

.rec__cur {
  padding: 3px 9px;
  border-radius: 6px;
  background: var(--BgSoft);
  color: var(--InkSoft);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
}

/*
	Вердикт — самое крупное на экране. Ради него сюда и приходят,
	и он обязан читаться с расстояния вытянутой руки: «Сходится»
	или «Не сходится на 481 262,61 ₸».
*/
.rec__verdict {
  font-family: var(--FontHead);
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.rec__verdict--ok {
  color: var(--AccentInk);
}

.rec__verdict--bad {
  color: var(--Bad);
}

.rec__verdict--none {
  color: var(--Warn);
  font-size: 22px;
}

.rec__as {
  color: var(--Muted);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}

/* Пояснение переносится на свою строку целиком: это предложение,
   а не подпись, и рядом с вердиктом ему тесно */
.rec__says {
  flex: 1 0 100%;
  margin: 6px 0 0;
  max-width: 78ch;
  color: var(--Ink);
  font-size: 15px;
  line-height: 1.5;
}

/* ── полоса-замечание: покрытие, поведение разницы ── */
.rec__note {
  margin: 0 0 8px;
  padding: 14px 16px;
  border: 1px solid var(--Line);
  border-radius: 12px;
  background: var(--BgSoft);
  line-height: 1.5;
}

.rec__note--ok {
  border-color: rgba(11, 113, 86, 0.28);
  background: var(--AccentSoft);
}

/*
	Дыра в покрытии — подозреваемый номер один, и полоса у неё
	предупреждающая. Это не украшение: пока дыра есть, любое другое
	объяснение расхождения остаётся гаданием, и увидеть её человек
	должен раньше, чем начнёт читать суммы.
*/
.rec__note--warn {
  border-color: rgba(180, 83, 9, 0.32);
  background: var(--WarnSoft);
}

.rec__note__t {
  display: block;
  font-weight: 700;
  font-size: 15px;
}

.rec__note__text {
  margin: 6px 0 0;
  max-width: 78ch;
}

/* Число дней без выписок — с подписью, как и всё остальное */
.rec__note__fact {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.rec__note__t + .rec__note__text {
  margin-top: 4px;
}

.rec__note__n {
  display: inline;
}

.rec__note__v {
  font-size: 22px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--Warn);
}

.rec__gaps {
  margin: 10px 0 0;
  padding: 0;
  list-style: none;
}

.rec__gap {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
  padding: 7px 0;
  border-top: 1px solid rgba(180, 83, 9, 0.2);
}

.rec__gap__at {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.rec__gap__len {
  color: var(--InkSoft);
  font-size: 13px;
}

/* ── 3. подозреваемые ── */
.rec__cands {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
  gap: 12px;
}

.rec__cand {
  padding: 14px 16px;
  border: 1px solid var(--LineSoft);
  border-radius: 12px;
  background: var(--Bg);
}

/*
	Кандидат, объясняющий расхождение ЦЕЛИКОМ, — единственный случай,
	когда система вправе сказать «вот причина», а не «вот подозреваемый».
	Поэтому у него своя рамка, а не просто жирная подпись.
*/
.rec__cand--full {
  border-color: rgba(11, 113, 86, 0.4);
  background: var(--AccentSoft);
}

.rec__cand__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px 12px;
}

.rec__cand__t {
  flex: 1 1 180px;
  font-size: 15px;
  font-weight: 700;
  line-height: 1.35;
}

.rec__cand__v {
  font-size: 19px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* «сумма неизвестна» — это ответ, а не пустое место: за дырой
   в покрытии может стоять что угодно, и ноль соврал бы */
.rec__cand__v--none {
  color: var(--Muted);
  font-size: 14px;
  font-weight: 600;
}

/*
	Плашка «объясняет расхождение целиком» — пилюлей в строке подписей.
	В шапке карточки она вставала полосой во всю ширину (flex-basis:100%
	у элемента с width:fit-content не работает) и спорила с названием
	кандидата за первое место.
*/
.rec__cand__full {
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--Accent);
  color: #FFF;
  font-size: 12px;
  font-weight: 700;
}

.rec__cand__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 14px;
  margin: 6px 0 0;
}

.rec__cand__m {
  color: var(--InkSoft);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.rec__cand__d {
  margin: 8px 0 0;
  color: var(--Ink);
  font-size: 13px;
  line-height: 1.5;
}

.rec__cand__h {
  margin: 6px 0 0;
  color: var(--InkSoft);
  font-size: 13px;
  line-height: 1.5;
}

/* ── 4. числа последней точки ── */
.rec__facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: 12px;
}

.rec__fact {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px 14px;
  border: 1px solid var(--LineSoft);
  border-radius: 12px;
  background: var(--Bg);
}

.rec__fact__t {
  color: var(--Muted);
  font-size: 12px;
  line-height: 1.35;
}

.rec__fact__b {
  font-size: 19px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

/* Пояснение мельче подписи и не жирное: оно нужно один раз, при первом
   чтении, и спорить за внимание с числом не должно */
.rec__fact__h {
  color: var(--InkSoft);
  font-size: 12px;
  line-height: 1.45;
}

/* Ответ экрана — разница — выделен фоном, а не размером: увеличивать
   шрифт здесь некуда, суммы и так крупные */
.rec__fact--result {
  border-color: rgba(194, 43, 43, 0.32);
  background: var(--BadSoft);
}

.rec__fact--ok {
  border-color: rgba(11, 113, 86, 0.32);
  background: var(--AccentSoft);
}

.rec__fact--ok .rec__fact__v {
  color: var(--AccentInk);
}

.rec__fact--neg .rec__fact__v {
  color: var(--Bad);
}

/* ── 5. ряд контрольных точек ── */
.rec__points {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
  gap: 12px;
}

.rec__point {
  padding: 14px 16px;
  border: 1px solid var(--LineSoft);
  border-radius: 12px;
  background: var(--Bg);
}

.rec__point--bad {
  border-color: rgba(194, 43, 43, 0.28);
}

.rec__point__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--LineSoft);
}

.rec__point__at {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.rec__point__src {
  color: var(--InkSoft);
  font-size: 13px;
}

.rec__point__flag {
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
  white-space: nowrap;
}

.rec__point__flag--ok {
  background: var(--AccentSoft);
  color: var(--AccentInk);
}

.rec__point__flag--bad {
  background: var(--BadSoft);
  color: var(--Bad);
}

.rec__point__flag--done {
  background: var(--BgSoft);
  color: var(--InkSoft);
}

/*
	Пять чисел точки — в две колонки, подпись над значением.
	«Подпись слева, число справа» здесь не годится: подпись «Банк:
	книжный остаток» рядом с числом сжимается в колонку шириной
	в одно слово и встаёт лесенкой.
*/
.rec__point__nums {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(140px, 100%), 1fr));
  gap: 10px 14px;
  margin: 12px 0 0;
}

.rec__point__t {
  display: block;
  color: var(--Muted);
  font-size: 12px;
  line-height: 1.3;
}

.rec__point__b {
  margin-top: 2px;
}

.rec__point__v {
  font-size: 16px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Разница — итог карточки, и стоит она во всю её ширину:
   рядом с четырьмя равными плитками её не отличить от слагаемого */
.rec__point__fact--sum {
  grid-column: 1/-1;
  padding-top: 10px;
  border-top: 1px solid var(--LineSoft);
}

.rec__point__fact--sum .rec__point__v {
  font-size: 19px;
}

.rec__point__mark {
  margin: 10px 0 0;
  color: var(--InkSoft);
  font-size: 12px;
  line-height: 1.45;
}

/* ── 6. действия ── */
.rec__acts {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.rec__act {
  padding: 16px 18px;
  border: 1px solid var(--Line);
  border-radius: 14px;
  background: var(--Bg);
}

/* Готовое решение — единственное действие, которое система предлагает
   сама, и выделено оно ровно поэтому */
.rec__act--main {
  border-color: rgba(11, 113, 86, 0.4);
  background: var(--AccentSoft);
}

/*
	Единственная кнопка экрана, создающая строку в ленте операций.
	Красная рамка здесь — предупреждение, а не украшение: человек
	обязан узнать о последствии до нажатия.
*/
.rec__act--danger {
  border-color: rgba(194, 43, 43, 0.32);
  background: var(--BadSoft);
}

.rec__act--fold {
  padding: 14px 18px;
}

.rec__act__title {
  margin: 0 0 6px;
  font-size: 17px;
}

.rec__act__text {
  margin: 0 0 12px;
  max-width: 78ch;
  color: var(--InkSoft);
  line-height: 1.5;
}

/* Числа «сейчас стоит → станет → на дату» у готового решения */
.rec__act__facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr));
  gap: 12px;
  margin: 0 0 14px;
}

.rec__act__t {
  display: block;
  color: var(--Muted);
  font-size: 12px;
}

.rec__act__b {
  margin-top: 2px;
}

.rec__act__v {
  font-size: 19px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.rec__act__v--new {
  color: var(--AccentInk);
}

/*
	Поля формы шире стандартных: сумма остатка бывает семизначной,
	и поле в четверть строки под неё не годится. Ширину ограничиваем
	по максимуму, а не по минимуму: на телефоне поле обязано быть
	во всю ширину.
*/
.rec__form .app__field {
  max-width: 460px;
}

.rec__form {
  margin: 0;
}

/* Подпись «Завёл / Правил / Примечание» у входящего остатка */
.rec__by {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 12px;
  margin: 0 0 14px;
  font-size: 13px;
}

.rec__by dt {
  color: var(--Muted);
}

.rec__by dd {
  margin: 0;
}

.rec__by__v {
  font-weight: 600;
}

/* Складки экрана — тем же переключателем, что и на импорте */
.rec__fold {
  margin: 0;
}

.rec__fold > *:not(summary) {
  margin-top: 10px;
}

/* ── 7. журнал сверок ── */
.rec__log {
  display: flex;
  flex-direction: column;
}

.rec__log__row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 14px;
  padding: 10px 0;
  border-top: 1px solid var(--LineSoft);
  font-size: 14px;
}

.rec__log__at {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  font-weight: 600;
}

.rec__log__src {
  color: var(--InkSoft);
}

/* Разница в журнале — с подписью: без неё это голое число в ленте
   из пяти таких же */
.rec__log__fact {
  display: flex;
  align-items: baseline;
  gap: 6px;
}

.rec__log__t {
  color: var(--Muted);
  font-size: 12px;
}

.rec__log__b {
  display: inline;
}

.rec__log__v {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.rec__log__flag {
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
  white-space: nowrap;
}

.rec__log__flag--ok {
  background: var(--AccentSoft);
  color: var(--AccentInk);
}

.rec__log__flag--bad {
  background: var(--BadSoft);
  color: var(--Bad);
}

.rec__log__flag--done {
  background: var(--BgSoft);
  color: var(--InkSoft);
}

.rec__log__note {
  flex: 1 1 200px;
  color: var(--InkSoft);
}

.rec__log__form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  flex: 1 1 260px;
}

.rec__log__form input[type=text] {
  flex: 1 1 160px;
  min-width: 0;
  padding: 7px 10px;
  border: 1px solid var(--Line);
  border-radius: 8px;
  font-size: 13px;
}

/*
	══════════════════════════════════════════════════════════════════
	 СПИСОК СЧЕТОВ  (Ledger\Views\AccountsView)
	══════════════════════════════════════════════════════════════════

	ЗДЕСЬ БЫЛА .app__table--accounts — таблица на четыре колонки,
	которая на телефоне этими же правилами разбиралась в карточки.
	Стала карточками везде: к трём величинам счёта добавилась
	четвёртая, ради которой экран и открывают, — сходится ли счёт
	с банком и на сколько. Пятая колонка в таблице, где две уже заняты
	двумя числами с датой и подписью, в ширину ноутбука не помещается.

	Подписи к числам теперь настоящие теги, а не ::before из data-label:
	псевдоэлемент не выделишь, не скопируешь и не найдёшь поиском
	по странице.
*/
.acc__list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
  gap: 14px;
}

.acc {
  padding: 16px 18px 14px;
  border: 1px solid var(--Line);
  border-radius: var(--RadiusLg);
  background: var(--BgCard);
  box-shadow: var(--Shadow);
}

.acc--off {
  opacity: 0.55;
}

.acc__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--LineSoft);
}

.acc__title {
  font-family: var(--FontHead);
  font-size: 18px;
  font-weight: 700;
}

.acc__kind {
  flex: 1 0 100%;
  color: var(--Muted);
  font-size: 13px;
}

/*
	Вердикт сверки прямо в списке. Расхождение, про которое надо
	не забыть зайти и посмотреть, не смотрят: список счетов —
	единственный экран, куда заглядывают без повода.
*/
.acc__flag {
  margin-top: 4px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 700;
  white-space: nowrap;
}

.acc__flag--ok {
  background: var(--AccentSoft);
  color: var(--AccentInk);
}

.acc__flag--ok:hover {
  background: var(--AccentSoft);
  text-decoration: underline;
}

.acc__flag--bad {
  background: var(--BadSoft);
  color: var(--Bad);
}

.acc__flag--bad:hover {
  background: var(--BadSoft);
  text-decoration: underline;
}

.acc__flag--none {
  background: var(--BgSoft);
  color: var(--InkSoft);
}

.acc__flag--none:hover {
  background: var(--BgSoft);
  text-decoration: underline;
}

.acc__nums {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
  gap: 12px 16px;
  margin: 12px 0 0;
}

.acc__t {
  display: block;
  color: var(--Muted);
  font-size: 12px;
  line-height: 1.3;
}

.acc__b {
  margin-top: 2px;
}

.acc__v {
  font-size: 18px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.acc__h {
  display: block;
  margin-top: 2px;
  color: var(--InkSoft);
  font-size: 12px;
  line-height: 1.4;
}

.acc__acts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 14px;
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--LineSoft);
}

/*
	═══════════════════════════════════════════════════════════════════
	ПОДПИСАННОЕ ЧИСЛО И КАРТОЧКИ ОТЧЁТА
	═══════════════════════════════════════════════════════════════════

	ЧТО БЫЛО СЛОМАНО. Отчёт был таблицей в пять колонок: сумма, доля,
	было, изменение — а подписи к ним в шапке, то есть отдельно
	от самих чисел. Две беды сразу:

	  — на 390px таблица получалась 423px и обрезалась `.app__table
	    { overflow:hidden }`. Три колонки из пяти исчезали ВМЕСТЕ
	    с шапкой, и на экране оставались голые цифры без единой подписи.
	    Именно это и было видно на скриншоте: «КАТЕГОРИЯ СУММА ДОЛЯ»
	    и обрыв;
	  — на широком экране всё помещалось, но чтобы понять четвёртое
	    число в строке, надо было вести глазом вверх и считать колонки.

	ПОЧЕМУ НЕ overflow-x:auto. Прокрутка не решает, а прячет: тот же
	контейнер обрезает выпадающие списки внутри строк — на этом уже
	обжигались на экране операций, и лечили ровно так же, заменой
	таблицы на карточки.

	РЕШЕНИЕ. Строка отчёта стала карточкой, а каждое число — узлом
	«подпись + значение», который вёрсткой не разорвать. Числа лежат
	в сетке auto-fit: на телефоне это две колонки, на планшете три,
	на широком экране пять. Подпись всегда над своим числом, а не
	в шапке за три строки выше.
*/
.app__fig__label {
  display: block;
  font-size: 13px;
  line-height: 1.3;
  color: var(--Muted);
}

.app__fig__value {
  display: block;
  margin-top: 4px;
  font-family: var(--FontHead);
  font-weight: 800;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  /*
  	Длинная сумма в узкой колонке переносится, а не вылезает
  	за карточку: горизонтальная прокрутка страницы недопустима.

  	break-word, а не anywhere: anywhere рвёт строку в ЛЮБОМ месте,
  	включая неразрывный пробел перед «₸», и «+2 270 712,74 ₸»
  	разъезжалось на две строки с знаком валюты во второй.
  */
  overflow-wrap: break-word;
}

.app__fig__hint {
  display: block;
  margin-top: 6px;
  font-size: 12px;
  line-height: 1.35;
  color: var(--Muted);
}

/*
	Карточка сводки: число крупное, оно здесь главное.

	Размер плавающий, а не фиксированные 26px. Карточек в ряду четыре,
	и на 1024px каждой достаётся 230px; «4 888 634,98 ₸» при 26px
	занимает 200px и переносится — знак валюты уезжал на вторую строку
	отдельно от своего числа. clamp даёт 26px там, где место есть,
	и ужимает до 20px там, где его нет: сумма остаётся целой, а это
	важнее её роста.
*/
.app__card.app__fig .app__fig__value {
  font-size: clamp(20px, 2vw, 26px);
  margin-top: 6px;
}

/* Число внутри строки-карточки: их пять, и 26px превратили бы
   карточку категории в простыню на три экрана */
.app__fig--inline .app__fig__value {
  font-size: 18px;
}

.app__figs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 14px 20px;
}

/* Двенадцать месяцев истории капитала: тем же узлом, но плотнее —
   иначе год занимает три экрана прокрутки */
.app__figs--dense {
  grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
  gap: 12px 16px;
}

.app__figs--dense .app__fig__value {
  font-size: 16px;
}

/* ── сверка остатков ── */
/*
	Сверка — формула, а не таблица: строки читаются сверху вниз
	и складываются. Поэтому строка здесь блок «подпись + пояснение +
	сумма», а не две ячейки. В прежней таблице на телефоне подпись
	из четырёх слов сжималась в колонку шириной в одно слово и вставала
	лесенкой рядом с числом — ровно то «всё слиплось», о котором шла речь.
*/
.app__calc {
  display: grid;
  gap: 1px;
  margin: 0 0 14px;
  background: var(--Line);
  border: 1px solid var(--Line);
  border-radius: var(--RadiusLg);
  overflow: hidden;
}

.app__calc__row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 2px 16px;
  padding: 12px 18px;
  background: var(--BgCard);
}

.app__calc__what {
  flex: 1 1 220px;
  min-width: 0;
}

.app__calc__name {
  display: block;
}

.app__calc__hint {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  line-height: 1.35;
  color: var(--Muted);
}

.app__calc__sum {
  flex: 0 0 auto;
  font-size: 17px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.app__calc__row--total {
  background: var(--BgSoft);
}

.app__calc__row--total .app__calc__name {
  font-weight: 700;
}

/* Фактический остаток — то, с чем сравнивают всё остальное:
   он выделен, иначе строка теряется среди слагаемых */
.app__calc__row--fact {
  background: var(--AccentSoft);
}

.app__calc__row--fact .app__calc__name {
  font-weight: 700;
}

.app__calc__row--fact .app__calc__sum {
  color: var(--AccentInk);
}

/* ── карточки категорий ── */
.app__report {
  display: grid;
  gap: var(--Gap);
}

.app__cat {
  padding: 16px 20px 18px;
  border: 1px solid var(--Line);
  border-radius: var(--RadiusLg);
  background: var(--BgCard);
  box-shadow: var(--Shadow);
}

.app__cat__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 12px;
  margin-bottom: 12px;
}

.app__cat__title {
  font-family: var(--FontHead);
  font-size: 18px;
  font-weight: 700;
  color: var(--Ink);
}

a.app__cat__title {
  color: var(--Ink);
}

a.app__cat__title:hover {
  color: var(--AccentInk);
}

.app__cat__head .app__trend {
  margin-left: 0;
}

/* Итог периода стоит первым и задаёт масштаб, в котором читаются
   доли всех карточек ниже — поэтому он выделен, как «Всего сейчас» */
.app__cat--total {
  border-color: rgba(11, 113, 86, 0.32);
  background: var(--AccentSoft);
}

.app__cat--total .app__cat__title {
  color: var(--AccentInk);
}

/*
	Подкатегории ВНУТРИ карточки родителя, а не отдельными строками
	с отступом. Отступ на телефоне съедается первым, и подкатегория
	становится неотличима от категории — а в отчёте это разные уровни
	с разными долями.
*/
.app__cat__kids {
  display: grid;
  gap: 10px;
  margin-top: 12px;
}

/*
	Складка подкатегорий. Развёрнутые сразу, полсотни подкатегорий
	давали страницу в двадцать шесть тысяч пикселей — двенадцать
	экранов прокрутки на телефоне, — и главный вопрос отчёта тонул
	в них: чтобы дойти от «Транспорта» до «Продуктов», надо было
	проскроллить восемь подкатегорий такси и топлива.
*/
.app__cat__more {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--LineSoft);
}

.app__cat__toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  list-style: none;
  font-size: 14px;
  color: var(--AccentInk);
}

/* маркер убираем и в Safari/Chrome — там он отдельным псевдоэлементом */
.app__cat__toggle::-webkit-details-marker {
  display: none;
}

.app__cat__toggle:hover {
  color: var(--Ink);
}

/* Своя стрелка: по ней видно, открыта складка или закрыта */
.app__cat__toggle::before {
  content: "";
  width: 7px;
  height: 7px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.15s;
}

.app__cat__more[open] .app__cat__toggle::before {
  transform: rotate(45deg);
}

.app__cat--sub {
  padding: 12px 14px;
  border-radius: var(--Radius);
  background: var(--BgSoft);
  box-shadow: none;
}

.app__cat--sub .app__cat__head {
  margin-bottom: 8px;
}

.app__cat--sub .app__cat__title {
  font-size: 15px;
}

.app__cat--sub .app__fig__value {
  font-size: 16px;
}

/* ── выгрузка ── */
.app__export {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.app__export__label {
  font-size: 13px;
  color: var(--Muted);
}

/* ── телефон ── */
@media (max-width: 640px) {
  #mb .app {
    --AppPad:14px;
    padding-bottom: 28px;
  }
  /*
  	═══════════════════════════════════════════════════════════════
  	ТАБЛИЦЫ НА ТЕЛЕФОНЕ
  	═══════════════════════════════════════════════════════════════

  	Что было. Ни одна таблица в экран не влезала, и вбок ехала
  	ВСЯ СТРАНИЦА: на загрузке выписки документ получался 927px при
  	экране 390. Это худший из возможных вариантов — уезжают и шапка,
  	и заголовок, и кнопки, а человек листает вбок, чтобы вернуть
  	на место меню.

  	Стало: каждая таблица прокручивается внутри себя. display:block
  	превращает таблицу в обычный блок, а строки внутри собираются
  	в анонимную таблицу — вёрстка колонок сохраняется, разряды
  	по-прежнему стоят столбиком, но вбок ездит только сама таблица.
  	Страница стоит на месте.

  	overflow-y:hidden обязателен рядом с overflow-x:auto: одно
  	значение auto, второе visible браузер не допускает и молча
  	делает оба auto — появляется лишняя вертикальная полоса.
  */
  .app__table {
    display: block;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
  }
  .app__table th,
  .app__table td {
    padding: 10px 12px;
  }
  /*
  	Раз таблица уехала в прокрутку — строкам незачем переносить слова.
  	Без этого «01.03.2026 — 31.03.2026» ломается на три строки, а рядом
  	остаётся пустое место, до которого всё равно нужно долистать:
  	худшее из обоих миров. Одна строка на запись читается и листается
  	предсказуемо.

  	Исключения — таблицы, где в ячейке живёт текст, а не значение:
  	подсказки «Здоровья», названия товаров в чеке и список операций
  	(он вообще не прокручивается).

  	Плюс модификатор --wrap для случаев, когда перенос ЛУЧШЕ прокрутки:
  	остатки по счетам на сводке — это две колонки, «название плюс номер»
  	и сумма. С переносом они укладываются в экран целиком (359px из 390),
  	а с запретом переноса начинают ездить вбок ради длинного номера
  	счёта, который никто не читает.
  */
  /*.app__table th,
  .app__table td{ white-space:nowrap; }*/
  .app__table--tx td,
  .app__table--wrap td,
  .app__table--health td,
  .app__table--items td {
    white-space: normal;
  }
  /*
  	А вот деньги и даты не переносятся НИГДЕ и ни при каких
  	исключениях. «7 300,00 $», разорванное на «7 / 300,00 / $»,
  	читается как три разных числа — ровно та ошибка, ради которой
  	в этом проекте суммы набраны табличными цифрами.
  */
  .app__table td.app__num,
  .app__table td.app__date {
    white-space: nowrap;
  }
  /*
  	Происхождение входящего остатка («с 01.03.2026 · введён вручную»)
  	переносится по словам. Ячейки таблицы на телефоне намеренно
  	nowrap — иначе суммы рвутся посреди разряда, — но эта подпись
  	не сумма, а фраза, и в одну строку она растягивает колонку
  	вдвое: таблица счетов переставала помещаться в 390px.
  */
  .app__table td .app__origin {
    white-space: normal;
  }
  /*
  	В остатках по счетам первая ячейка содержит номер счёта —
  	двадцать символов без единого пробела. Перенести его негде,
  	и колонка распирала таблицу до прокрутки, пряча за краем как раз
  	сумму. Номер счёта переносим где придётся: читают его редко
  	и по частям, а сумму — всегда и целиком.
  */
  .app__table--wrap td:first-child {
    overflow-wrap: anywhere;
  }
  /*
  	Список операций — исключение: он главный экран, и листать его
  	вбок недопустимо. Строка раскладывается в две полосы: дата
  	и сумма сверху, описание под ними во всю ширину. Читается
  	как карточка, а сумма остаётся справа и по-прежнему выровнена.

  	Действия прячем: менять категорию удобнее на карточке операции,
  	а не в тесной строке.
  */
  .app__table--tx {
    display: table;
    overflow: visible;
  }
  .app__table--tx .app__actions {
    display: none;
  }
  .app__table--tx tbody tr {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 10px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--LineSoft);
  }
  .app__table--tx tbody tr:last-child {
    border-bottom: 0;
  }
  .app__table--tx tbody td {
    padding: 0;
    border: 0;
  }
  /* порядок полос: галочка · дата · сумма, затем описание строкой ниже */
  .app__table--tx .app__check-cell {
    order: 1;
    width: auto;
  }
  .app__table--tx .app__date {
    order: 2;
    font-size: 13px;
  }
  .app__table--tx .app__num {
    order: 3;
    margin-left: auto;
  }
  /*
  	Описание — единственная ячейка без класса. Селектор по отсутствию
  	класса выглядит хрупко, но здесь он честнее нумерации: колонка
  	с галочками то есть, то нет (на сводке её не бывает), и nth-child
  	начал бы показывать описание не там.
  */
  .app__table--tx tbody td:not([class]) {
    order: 4;
    flex: 1 0 100%;
  }
  /* Шапка с «выделить все» тоже становится полосой, а не таблицей. */
  .app__table--tx thead tr {
    display: flex;
    padding: 10px 14px;
  }
  .app__table--tx thead th {
    padding: 0;
    border: 0;
  }
  .app__table--tx thead th[colspan] {
    display: none;
  }
  .app__card b {
    font-size: 22px;
  }
  /* На телефоне панель действий разворачивается в столбец. */
  .app__bulkbar {
    flex-direction: column;
    align-items: stretch;
  }
  .app__bulkbar select,
  .app__bulkbar .pick {
    min-width: 0;
    width: 100%;
  }
  /*
  	ЗДЕСЬ ПРЯТАЛИСЬ КОЛОНКИ СРАВНЕНИЯ отчёта по категориям
  	(display:none на 4-й и 5-й). Правило убрано вместе с таблицей,
  	и это главное, что оно делало неправильно: «было в прошлом
  	периоде» и «изменение» на телефоне не обрезались, а ИСЧЕЗАЛИ —
  	молча и без следа. Теперь это два подписанных числа в карточке,
  	и видны они на любой ширине.
  */
  /* Карточка задачи на телефоне: во всю ширину, кнопки в столбец. */
  .app__task {
    padding: 18px;
    border-radius: 12px;
  }
  .app__task__amount {
    font-size: 28px;
  }
  .app__task__pick {
    flex-direction: column;
    align-items: stretch;
  }
  .app__task__pick select,
  .app__task__pick .pick,
  .app__task__pick .app__btn {
    width: 100%;
  }
  /*
  	БЫЛО: flex:1 1 240px с десктопа доезжал сюда. В столбце основа
  	флекса — это высота, и поле категории на телефоне вырастало
  	до 240px пустоты со стрелкой посередине. В столбце поле
  	занимает свою высоту, не больше.
  */
  .app__task__pick select,
  .app__task__pick .pick {
    flex: 0 0 auto;
  }
  /*
  	═══════════════════════════════════════════════════════════════
  	 СЧЕТА НА ТЕЛЕФОНЕ
  	═══════════════════════════════════════════════════════════════

  	ЗДЕСЬ БЫЛА РАЗБОРКА ТАБЛИЦЫ В КАРТОЧКИ. Четыре колонки в 390px
  	не помещались никак: измеренная ширина таблицы — 736px при 348px
  	доступных, и это было не прокруткой, а ОБРЕЗАНИЕМ (у .app__table
  	стоит overflow:hidden ради скруглённых углов).

  	Теперь список счетов — карточки на любой ширине, и разбирать
  	нечего: сетка .acc__list сама складывается в одну колонку,
  	а минимум колонки задан в min(340px,100%) — вылезти за экран
  	нечему. Здесь остаётся только уплотнение.
  */
  .acc {
    padding: 14px 14px 12px;
    border-radius: 14px;
  }
  .acc__title {
    font-size: 17px;
  }
  /* Числа счёта — в одну колонку: в 316px две по 150px не встают,
     а встав, режут «1 566 109,17 ₸» посреди числа */
  .acc__nums {
    grid-template-columns: 1fr;
    gap: 10px;
  }
  .acc__acts {
    gap: 10px;
  }
  .acc__acts .app__btn {
    width: 100%;
  }
  /* Здоровье на телефоне: подсказки остаются, ширина не ограничена. */
  .app__table--health td:first-child {
    max-width: none;
  }
  /* Карточка операции на телефоне: реквизиты в одну колонку. */
  .app__card--tx {
    padding: 18px;
    border-radius: 12px;
  }
  .app__facts {
    grid-template-columns: 1fr;
    gap: 2px 0;
  }
  .app__facts dt {
    margin-top: 10px;
    font-size: 13px;
  }
  .app__task__actions form,
  .app__task__actions .app__btn {
    width: 100%;
  }
  /*
  	СВЕРКА НА ТЕЛЕФОНЕ.

  	Вёрстка экрана сеточная и в одну колонку складывается сама —
  	медиазапрос ей не нужен. Здесь только то, что само не решается:
  	уплотнение полей карточек и размер вердикта.

  	22px, а не 26: «Не сходится на 481 262,61 ₸» при 26px не влезает
  	в 348px даже в две строки — браузер отрывает знак валюты от числа,
  	и сумма читается как оборванная.
  */
  .rec {
    padding: 16px 16px 18px;
    border-radius: 14px;
  }
  .rec__verdict {
    font-size: 22px;
  }
  .rec__verdict--none {
    font-size: 19px;
  }
  .rec__act {
    padding: 14px;
    border-radius: 12px;
  }
  .rec__point {
    padding: 12px 14px;
  }
  /* Пять чисел точки — в одну колонку: в 288px две по 140px встают
     впритык, и подпись «Банк: книжный остаток» рвётся посреди слова */
  .rec__point__nums {
    grid-template-columns: 1fr;
  }
  /* Подписи «Завёл / Правил / Примечание» — значение под подписью:
     в две колонки на 288px значение сжимается в одно слово */
  .rec__by {
    grid-template-columns: 1fr;
    gap: 0;
  }
  .rec__by dt {
    margin-top: 8px;
  }
  /* Кнопка «разобрано» — на своей строке во всю ширину: рядом с полем
     она сжимается до трёх букв с переносом */
  .rec__log__form {
    flex: 1 0 100%;
  }
  .rec__log__form .app__btn {
    width: 100%;
  }
  /*
  	ПОДПИСАННЫЕ ЧИСЛА НА ТЕЛЕФОНЕ.

  	Внутри карточки категории под сетку остаётся 308px: 390 минус
  	поля страницы (14+14) и поля карточки (20+20). С minmax(150px)
  	и зазором 20px двум колонкам нужно 320 — не помещается,
  	и сетка схлопывалась в ОДНУ колонку. Пять чисел в столбик
  	на каждую из шестидесяти карточек и давали ту самую простыню.

  	132px и зазор 14px: 132·2+14=278 ≤ 308 — две колонки помещаются
  	с запасом, а под третью (424px) места нет, так что «в одну
  	строку по пять» здесь не случится.
  */
  .app__figs {
    grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
    gap: 12px 14px;
  }
  .app__figs--dense {
    grid-template-columns: repeat(auto-fit, minmax(116px, 1fr));
    gap: 10px 12px;
  }
  /*
  	16px, а не 18: в колонке 158px «+2 270 712,74 ₸» при 17px
  	не помещается на строку, и браузер отрывает знак валюты
  	от его числа — сумма читается как оборванная.
  */
  .app__fig--inline .app__fig__value {
    font-size: 16px;
  }
  .app__card.app__fig .app__fig__value {
    font-size: 23px;
  }
  .app__cat {
    padding: 14px 14px 16px;
    border-radius: 14px;
  }
  .app__cat--sub {
    padding: 12px;
  }
  .app__cat__title {
    font-size: 16px;
  }
  /* Сверка: сумма уходит ПОД подпись. Рядом с ней подпись из четырёх
     слов сжимается в колонку шириной в одно слово и встаёт лесенкой */
  .app__calc__row {
    display: block;
    padding: 12px 14px;
  }
  .app__calc__sum {
    display: block;
    margin-top: 4px;
    font-size: 19px;
  }
  /* Кнопки выгрузки на своей строке: рядом с заголовком раздела
     они выдавливают его в две строки по одному слову */
  .app__export {
    width: 100%;
  }
  /*
  	ПОЗИЦИИ ЧЕКА — БЕЗ ПРОКРУТКИ ВБОК.

  	Общее правило выше отправляет таблицы в собственную прокрутку,
  	и для выписки с восемью колонками это верно. Но у позиций чека
  	уезжала за край «Сумма» — единственное, ради чего в чек
  	и смотрят. Колонки при этом всего четыре.

  	Сузить их не выходит: названия с ценника длинные («МОЛОКО ФУДМ
  	2,5% 0,2Л»), и в колонке шириной 130px они рвались посреди слова.
  	Поэтому на телефоне строка перестаёт быть строкой таблицы
  	и становится блоком: название сверху, числа под ним в одну
  	полосу.

  	ПОДПИСИ К ЧИСЛАМ БЕРУТСЯ ИЗ data-l, А НЕ ИЗ ШАПКИ. Шапка при
  	такой раскладке скрыта, и без подписей остались бы три голых
  	числа подряд — ровно то, с чего начинался весь этот разбор.
  	Атрибут ставит вьюха, и она же отвечает за то, чтобы подпись
  	совпадала с колонкой.
  */
  .app__table--items,
  .app__table--items tbody,
  .app__table--items tr {
    display: block;
    overflow: visible;
  }
  .app__table--items thead {
    display: none;
  }
  .app__table--items tr {
    padding: 10px 14px;
    border-bottom: 1px solid var(--LineSoft);
  }
  .app__table--items tr:last-child {
    border-bottom: none;
  }
  .app__table--items td {
    display: inline-block;
    padding: 0;
    border: none;
    vertical-align: baseline;
  }
  .app__table--items td:first-child {
    display: block;
    margin-bottom: 6px;
    font-weight: 600;
    line-height: 1.35;
  }
  .app__table--items td[data-l] {
    margin-right: 16px;
    text-align: left;
  }
  .app__table--items td[data-l]:last-child {
    margin-right: 0;
  }
  .app__table--items td[data-l]::before {
    content: attr(data-l) ": ";
    font-weight: 400;
    font-size: 12px;
    color: var(--Muted);
  }
}
/*
	УЖЕ 800px СЕМИ КОЛОНКАМ НЕ ХВАТАЕТ НИКАК — и это не лечится
	ужиманием: 756px — это min-content, ширина при полностью разорванном
	тексте. Дальше остаётся выбор из двух: пустить таблицу вбок
	(overflow-x:auto — в этом проекте запрещён, он прячет половину строки
	за краем) или перестать быть таблицей.

	Строка становится карточкой. Заголовок не нужен: дату, название
	магазина, сумму и «смотреть · скачать» человек узнаёт по виду,
	а не по подписи сверху — ровно так же сделан список операций
	(.app__table--tx на телефоне). Общий приём вместо нового.

	Порядок в карточке — по важности, а не по порядку колонок:
	дата и сумма в одну строку сверху, под ними магазин, дальше
	операция и вердикт, действия — последними.
*/
@media (max-width: 800px) {
  .app__table--receipts {
    display: block;
    border: 0;
    background: none;
  }
  .app__table--receipts thead {
    display: none;
  }
  .app__table--receipts tbody {
    display: block;
  }
  .app__table--receipts tbody tr {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 4px 12px;
    margin-bottom: 12px;
    padding: 14px;
    background: var(--BgCard);
    border: 1px solid var(--Line);
    border-radius: var(--RadiusLg);
  }
  .app__table--receipts tbody td {
    display: block;
    padding: 0;
    border: 0;
    grid-column: 1/-1;
  }
  /* Дата слева, сумма справа — как в шапке чека. */
  .app__table--receipts td.app__date {
    grid-column: 1;
    grid-row: 1;
  }
  .app__table--receipts td.app__num {
    grid-column: 2;
    grid-row: 1;
    text-align: right;
  }
  /*
  	Дата и время в карточке — в одну строку. Блочный .app__date__day
  	нужен только в таблице, где узкая колонка; здесь он уводил время
  	на вторую строку, да ещё с отступом слева, и «14:15» повисало
  	отдельной строкой ни под чем.
  */
  .app__table--receipts td.app__date .app__date__day {
    display: inline;
  }
  .app__table--receipts td.app__date .app__date__time {
    display: inline;
    margin-left: 6px;
  }
  .app__table--receipts td.app__shop {
    font-weight: 600;
    color: var(--Ink);
  }
  /*
  	В карточке места хватает — ссылки на оригинал возвращаются
  	в строку вместе с разделителем. Столбиком они стоят только
  	в узкой колонке широкой таблицы.
  */
  .app__table--receipts td:nth-child(6) .app__link {
    display: inline;
  }
  .app__table--receipts td:nth-child(6) .app__sep {
    display: inline;
  }
  /*
  	width:1% с последней колонки СНИМАЕТСЯ, и это не косметика.
  	В таблице этот 1% значит «сожмись по содержимому», но у блока
  	в карточке он значит буквально один процент ширины — семь
  	пикселей. Рамка сверху превращалась в короткий штрих, а само
  	«удалить» вываливалось из своей коробки влево.
  */
  .app__table--receipts tbody td:last-child {
    width: auto;
    margin-top: 6px;
    padding-top: 10px;
    border-top: 1px solid var(--LineSoft);
    text-align: right;
  }
}
/* ── Роботы на экране «Здоровье» ───────────────────────────────
	Таблица устроена как метрики выше: слева что и зачем, справа
	состояние и одно действие. Своя таблица, а не строки в общей,
	потому что у робота есть то, чего нет у метрики, — вывод
	последнего прогона, и он занимает целую строку во всю ширину.
*/
.app__table--robots td:first-child small {
  display: block;
}

/* Форма с кнопкой не должна ломать выравнивание колонки по правому
   краю: без inline-block она становится блоком и уезжает влево. */
.app__inline {
  display: inline-block;
}

.app__row--out td {
  padding-top: 0;
}

.app__row--out summary {
  cursor: pointer;
  font-size: 13px;
  color: var(--InkSoft);
}

/* Вывод робота — моноширинным и с переносами: это терминальный
   текст, и колонки в нём значащие, но горизонтальной прокрутки
   в таблице быть не должно (см. запрет overflow-x по проекту). */
.app__out {
  margin: 8px 0 0;
  padding: 10px 12px;
  max-height: 280px;
  overflow-y: auto;
  border-radius: var(--Radius, 10px);
  background: var(--Fill, #F2F2F7);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
}

/*
	«Убрать из очереди» — действие рядом с основным, но не равное ему.

	Основное действие у упавшего входящего — «Попробовать снова»:
	чаще всего сбой разовый. Удаление стоит вторым, ссылкой
	и приглушённым красным: оно необратимо, и попасть в него
	по инерции после кнопки быть не должно.
*/
.app__link--drop {
  display: inline-block;
  margin-top: 8px;
  color: var(--Neg, #C0392B);
  opacity: 0.75;
}

.app__link--drop:hover {
  opacity: 1;
  text-decoration: underline;
}

/*
	═══════════════════════════════════════════════════════════════════
	 ОБЛИК КАБИНЕТА
	═══════════════════════════════════════════════════════════════════

	Весь файл написан под body.shell — класс, который ставит AppRoute
	на экранах /app/. Витрина сайта его не получает и потому не меняется
	ни на строку, что бы здесь ни происходило.

	ЗАЧЕМ ОТДЕЛЬНЫЙ ФАЙЛ, А НЕ ПРАВКА LEDGER. Разные вопросы. ledger.scss
	отвечает «что где лежит»: какая таблица, какая сетка, какие отступы.
	Этот файл отвечает «как это выглядит»: цвет, шрифт, скругление,
	материал панели. Смешав их, нельзя поменять облик, не перебирая
	раскладку, — а именно это и пришлось бы делать.

	ЧЕМ ЭТОТ ОБЛИК ОТЛИЧАЕТСЯ ОТ ПРЕЖНЕГО. Прежний был «тёплой бумагой»
	веб-страницы: кремовый фон, коричневатые линейки, рамки у карточек,
	заголовки гротеском. Приложение устроено иначе:

	  — фон холодно-серый, а содержимое лежит на нём БЕЛЫМИ группами
	    без рамок (рамка — приём печати, на экране её роль выполняет
	    сам факт, что карточка светлее фона);
	  — разделители внутри группы тонкие и начинаются не от края,
	    а от начала текста — так видно, что строки принадлежат одному
	    списку, а не стоят каждая сама по себе;
	  — панели полупрозрачные и размывают то, что под ними: это
	    единственный способ показать, что содержимое уехало ПОД панель,
	    а не кончилось;
	  — шрифт системный. -apple-system на его технике — это SF, тот же
	    шрифт, которым подписаны все остальные приложения; ничего
	    более «родного» веб-страница предложить не может, а заодно
	    это на один сетевой запрос меньше.

	Акцент остался фирменным зелёным, а не системным синим: это цвет
	проекта, и деньги в нём читаются лучше.
*/
body.shell {
  /* ── шрифт ── */
  --Font:-apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter",
  	"Segoe UI", system-ui, sans-serif;
  --FontHead:-apple-system, BlinkMacSystemFont, "SF Pro Display", "Inter",
  	"Segoe UI", system-ui, sans-serif;
  /* ── подложки ── */
  --Bg:#FFFFFF;
  --BgCard:#FFFFFF;
  --BgSoft:#F2F2F7; /* фон под группами */
  --BgDeep:#1C1C1E;
  --BgDeepSoft:#2C2C2E;
  /* ── текст ── */
  --Ink:#000000;
  --InkSoft:#3C3C43;
  --Muted:#8A8A8E;
  /*
  	── линии ──

  	Тише, чем были: разделитель внутри группы — почти намёк.
  	Группы и так разделены воздухом и скруглением, и линия здесь
  	не строит структуру, а только не даёт строкам слипнуться.
  */
  --Line:#D8D8DC;
  --LineSoft:#EBEBEF;
  /*
  	Серая заливка — та, из которой сделаны поля ввода, чипы
  	и сегментный переключатель. Полупрозрачная, а не сплошная:
  	один и тот же тон ложится и на белую карточку, и на серый фон.
  */
  --Fill:rgba(120,120,128,.12);
  --FillStrong:rgba(120,120,128,.20);
  /* ── акцент и состояния ── */
  --Accent:#0B7156;
  --AccentDark:#075742;
  --AccentSoft:rgba(11,113,86,.12);
  --AccentInk:#0A5F49;
  --Ok:#1C8C4C;
  --OkSoft:rgba(28,140,76,.12);
  --Warn:#C77700;
  --WarnSoft:rgba(199,119,0,.12);
  --Bad:#D7342A;
  --BadSoft:rgba(215,52,42,.12);
  /*
  	── форма ──

  	Скругления КРУПНЫЕ. Это главное, чем нынешний облик системы
  	отличается от прежнего: группа с радиусом 10 читается как рамка
  	таблицы, с радиусом 20 — как отдельная мягкая плашка, лежащая
  	на фоне. Мелочь, по которой глаз и отличает «сайт» от «системного
  	экрана», причём не задумываясь.
  */
  --Radius:14px;
  --RadiusLg:20px;
  --RadiusXl:28px;
  /*
  	Теней почти нет. Тень здесь — не украшение, а признак того,
  	что элемент ЛЕЖИТ ПОВЕРХ: её получают только всплывающие листы
  	и приподнятая кнопка добавления.
  */
  --Shadow:none;
  --ShadowLg:0 8px 28px rgba(0,0,0,.14);
  --ShadowXl:0 -2px 40px rgba(0,0,0,.18);
  background: var(--BgSoft);
  font-family: var(--Font);
  -webkit-font-smoothing: antialiased;
  /* резина под пальцем на iOS — но не горизонтальная */
  overscroll-behavior-x: none;
}

body.shell a {
  color: var(--AccentInk);
}

/* ═══════════════ панель навигации ═══════════════ */
body.shell .nav__bar {
  /*
  	Полоса прозрачна, пока содержимое не уехало под неё: сплошная
  	плашка на самом верху экрана делит его пополам там, где делить
  	нечего. Как только страницу тронули — появляется материал:
  	размытие и волосяная линия.
  */
  background: transparent;
  transition: background-color 0.2s ease, box-shadow 0.2s ease;
}

body.shell .nav.is-stuck .nav__bar {
  background: color-mix(in srgb, var(--BgSoft) 78%, transparent);
  /*
  	0.5px, а не 1px: на экране с удвоенной плотностью это ровно
  	один физический пиксель — та самая «волосяная» линия, которой
  	разделены все системные панели. Через box-shadow, потому что
  	border в полпикселя браузеры округляют вверх.
  */
  box-shadow: 0 0.5px 0 var(--Line);
}

@supports (backdrop-filter: blur(20px)) {
  body.shell .nav.is-stuck .nav__bar {
    background: color-mix(in srgb, var(--BgSoft) 62%, transparent);
    backdrop-filter: saturate(180%) blur(20px);
  }
}
body.shell .nav__small {
  color: var(--Ink);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

body.shell .nav__back {
  border-radius: 50%;
  color: var(--Accent);
}

body.shell .nav__back .shell__icon {
  stroke-width: 2.4;
}

body.shell .nav__back:hover {
  background: var(--Fill);
}

/*
	Большой заголовок. 34 пункта и плотный трекинг — это ровно
	системный «Large Title»; всё, что мельче, читается как обычный
	веб-заголовок.
*/
body.shell .nav__big {
  font-family: var(--FontHead);
  font-size: 34px;
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.022em;
  color: var(--Ink);
}

@media (max-width: 640px) {
  body.shell .nav__big {
    font-size: 30px;
  }
}
/* ═══════════════ панель снизу и рельс ═══════════════ */
body.shell .shell__nav {
  border-top: 0;
}

body.shell .shell__nav::before {
  background: color-mix(in srgb, var(--Bg) 80%, transparent);
  box-shadow: 0 -0.5px 0 var(--Line);
}

@supports (backdrop-filter: blur(20px)) {
  body.shell .shell__nav::before {
    background: color-mix(in srgb, var(--Bg) 62%, transparent);
    backdrop-filter: saturate(180%) blur(20px);
  }
}
body.shell .shell__tab {
  color: var(--Muted);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.01em;
}

body.shell .shell__icon {
  stroke-width: 1.8;
}

body.shell .shell__tab--cur {
  color: var(--Accent);
}

body.shell .shell__badge {
  background: var(--Bad);
  font-size: 11px;
  font-weight: 600;
  line-height: 18px;
  min-width: 18px;
  border-radius: 9px;
}

/* кнопка добавления: круг, как системная «составить» */
body.shell .shell__add {
  color: var(--Accent);
  font-size: 10px;
  font-weight: 600;
}

body.shell .shell__add .shell__icon {
  width: 38px;
  height: 38px;
  padding: 8px;
  border-radius: 50%;
  background: var(--Accent);
  stroke: #FFF;
  stroke-width: 2.4;
  box-shadow: 0 2px 10px rgba(11, 113, 86, 0.3);
  transition: transform 0.12s ease;
}

/* ── листы: группы строк, как в настройках ── */
body.shell .shell__group {
  padding: 20px 20px 7px;
  color: var(--Muted);
  font-size: 13px;
  font-weight: 400;
}

/*
	Группа — белая карточка со скруглением, строки внутри разделены
	линией, начинающейся от текста, а не от края. Это и есть «вставной
	сгруппированный список», из которого собраны все системные экраны
	настроек.
*/
body.shell .shell__list {
  overflow: hidden;
  border-radius: var(--Radius);
  background: var(--Bg);
}

body.shell .shell__list .shell__tab {
  min-height: 50px;
  padding: 0 18px;
  border-radius: 0;
  color: var(--Ink);
  font-size: 17px;
  font-weight: 400;
}

/*
	Разделитель начинается ОТ ТЕКСТА, а не от края карточки. Это мелочь
	ровно до тех пор, пока её не увидишь: линия во всю ширину режет
	группу на отдельные плашки, а линия от подписи — оставляет её одним
	списком, у которого слева колонка значков.

	54 пикселя — это поле 18 + значок 24 + зазор 12, то есть ровно
	начало подписи.
*/
body.shell .shell__list .shell__tab {
  position: relative;
}

body.shell .shell__list .shell__tab + .shell__tab::before {
  content: "";
  position: absolute;
  left: 54px;
  right: 0;
  top: 0;
  height: 0.5px;
  background: var(--LineSoft);
}

body.shell .shell__list .shell__icon {
  color: var(--Accent);
}

body.shell .shell__list .shell__tab--cur {
  background: var(--AccentSoft);
  color: var(--AccentInk);
  font-weight: 500;
}

body.shell .shell__rest__body {
  background: var(--BgSoft);
}

body.shell .shell__rest__body::before {
  background: var(--FillStrong);
}

body.shell .shell__who {
  padding: 16px 14px 6px;
  font-size: 13px;
}

body.shell .shell__rest__foot {
  border-top: 0;
}

body.shell .shell__tab--quiet {
  color: var(--Bad);
}

body.shell .shell__tab--quiet .shell__icon {
  color: var(--Bad);
}

/* ── лист выбора ── */
body.shell .sheet__body {
  background: var(--BgSoft);
}

body.shell .sheet__body::before {
  background: var(--FillStrong);
}

/* подпись пункта — обычный текст: это строка списка, а не ссылка */
body.shell .sheet__item {
  background: var(--Bg);
  border-radius: 0;
  color: var(--Ink);
}

body.shell .sheet__item:first-of-type {
  border-radius: var(--Radius) var(--Radius) 0 0;
}

body.shell .sheet__item:last-of-type {
  border-radius: 0 0 var(--Radius) var(--Radius);
}

body.shell .sheet__item + .sheet__item {
  box-shadow: inset 0 0.5px 0 var(--LineSoft);
}

body.shell .sheet__item .shell__icon {
  background: var(--AccentSoft);
  stroke: var(--Accent);
  border-radius: 10px;
}

body.shell .sheet__t {
  font-size: 17px;
  font-weight: 500;
}

/* ═══════════════ рельс на большом экране ═══════════════ */
@media (min-width: 900px) {
  /* на большом экране поле шире: содержимое не должно липнуть к рельсу */
  body.shell #mb .app {
    --AppPad:26px;
  }
  /*
  	⚠️ РЕЛЬС ПЛАВАЕТ, А НЕ ПРИБИТ К КРАЮ.

  	Было: колонка во всю высоту, впритык к краю окна, отделённая
  	волосяной линией. Это вёрстка почтового клиента десятилетней
  	давности — и ровно она выдавала «сайт».

  	Стало: панель с полями со всех сторон, крупным скруглением
  	и мягкой тенью. Между ней и краем окна видно фон — и именно
  	этот просвет превращает колонку в ПРЕДМЕТ, лежащий поверх,
  	а линию-границу делает ненужной: её роль выполняет воздух.

  	Полупрозрачность и размытие остаются: панель лежит НАД
  	содержимым, и то, что под ней просвечивает, — единственный
  	честный способ это показать.
  */
  body.shell .shell__nav {
    top: var(--ShellGap);
    bottom: var(--ShellGap);
    left: var(--ShellGap);
    width: calc(var(--ShellRail) - var(--ShellGap) * 2);
    height: auto;
    padding: 12px 8px;
    border-right: 0;
    border-radius: var(--RadiusXl);
    background: color-mix(in srgb, #FBFBFD 88%, transparent);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04), 0 10px 30px rgba(0, 0, 0, 0.05);
  }
  @supports (backdrop-filter: blur(20px)) {
    body.shell .shell__nav {
      background: color-mix(in srgb, #FBFBFD 70%, transparent);
      backdrop-filter: saturate(180%) blur(24px);
    }
  }
  body.shell .shell__nav::before {
    display: none;
  }
  body.shell .shell__brand {
    margin: 4px 0 14px;
    padding: 0 12px;
    font-size: 22px;
    font-weight: 700;
    letter-spacing: -0.02em;
  }
  /* на рельсе группы — без карточек: заголовок и строки */
  body.shell .shell__list {
    background: none;
    border-radius: 0;
    overflow: visible;
  }
  body.shell .shell__list .shell__tab {
    border-radius: 8px;
    font-size: 15px;
  }
  /* на рельсе группы разделены воздухом и заголовком — линии лишние */
  body.shell .shell__list .shell__tab + .shell__tab {
    box-shadow: none;
  }
  body.shell .shell__list .shell__tab + .shell__tab::before {
    display: none;
  }
  body.shell .shell__group {
    padding: 16px 10px 4px;
    font-size: 12px;
    font-weight: 600;
  }
  body.shell .shell__nav .shell__tab {
    min-height: 34px;
    padding: 0 10px;
    border-radius: 8px;
    font-size: 15px;
    font-weight: 400;
    color: var(--Ink);
  }
  body.shell .shell__nav .shell__tab .shell__icon {
    width: 21px;
    height: 21px;
    color: var(--Accent);
  }
  body.shell .shell__nav .shell__tab:hover {
    background: var(--Fill);
  }
  body.shell .shell__nav .shell__tab--cur,
  body.shell .shell__nav .shell__tab--cur:hover {
    background: var(--AccentSoft);
    color: var(--AccentInk);
    font-weight: 500;
  }
  body.shell .shell__add {
    min-height: 38px;
    border-radius: 10px;
    background: var(--Accent);
    color: #FFF;
    font-size: 15px;
    font-weight: 600;
  }
  body.shell .shell__add .shell__icon {
    width: 20px;
    height: 20px;
    padding: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
    stroke: currentColor;
    stroke-width: 2.2;
  }
  body.shell .shell__who {
    padding: 14px 10px 4px;
  }
  body.shell .sheet__body {
    background: var(--Bg);
  }
}
/* ═══════════════ содержимое экранов ═══════════════ */
/*
	Группа содержимого — белый прямоугольник со скруглением и без рамки.
	Рамка вокруг светлой карточки на светлом фоне даёт третью линию там,
	где хватает двух тонов.
*/
body.shell .app__card,
body.shell .app__facts--kinds,
body.shell .app__table,
body.shell .app__chart,
body.shell .app__stmt,
body.shell .app__unparsed {
  border: 0;
  border-radius: var(--RadiusLg);
  background: var(--Bg);
  box-shadow: none;
}

/*
	Поля внутри группы — двадцать пикселей, а не шестнадцать. Крупное
	скругление и тесное поле спорят друг с другом: угол уводит взгляд
	внутрь, а текст упирается в край. Воздух должен быть соразмерен
	скруглению, иначе мягкой выглядит только рамка.
*/
body.shell .app__card {
  padding: 18px 20px;
}

body.shell .app__facts--kinds {
  padding: 16px 20px;
}

body.shell .app__chart {
  padding: 14px 12px 10px;
}

body.shell .app__card span {
  font-size: 13px;
}

/*
	Сумма не переносится НИГДЕ: 22 пункта вместо 28 — цена за то,
	что «5 480 213,40 ₸» остаётся одним числом, а не числом и знаком
	тенге на отдельной строке. Карточек в ряду бывает четыре, и ширина
	у каждой — четверть экрана.
*/
body.shell .app__cards {
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

body.shell .app__card b {
  font-family: var(--FontHead);
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.022em;
  white-space: nowrap;
}

body.shell .app__card--total b {
  font-size: 24px;
}

/*
	Итоговая карточка выделена ЗАЛИВКОЙ, а не обводкой.

	Обводка в полтора пикселя — приём таблицы: она рисует границу там,
	где у остальных карточек границы нет, и единственная жёсткая линия
	на экране притягивает взгляд сильнее, чем само число. Светлая
	заливка тем же цветом выделяет ровно так же, но остаётся в общем
	строе мягких плашек.
*/
body.shell .app__card--total {
  background: color-mix(in srgb, var(--Accent) 8%, var(--Bg));
  box-shadow: none;
}

body.shell .app__card--total span {
  color: var(--AccentInk);
  opacity: 0.75;
}

/*
	⚠️ НА ТЕЛЕФОНЕ КАРТОЧКИ СВОДКИ — В ДВЕ КОЛОНКИ

	Сетка ledger просит минимум 220px на карточку, и на экране в 390
	это ровно одна колонка: четыре карточки занимали почти три экрана,
	и до графика нужно было листать. А цифры в них короткие — им хватает
	половины ширины с запасом.

	Итог остаётся во всю ширину: он отвечает не на вопрос месяца,
	а на вопрос «сколько есть сейчас», и это главное число экрана.
*/
@media (max-width: 640px) {
  body.shell .app__cards {
    grid-template-columns: 1fr 1fr;
    gap: 10px;
  }
  body.shell .app__card {
    padding: 12px 14px;
  }
  body.shell .app__card span {
    font-size: 12px;
  }
  /*
  	Сумма в половинной карточке НЕ ПЕРЕНОСИТСЯ: «1 240 000,00 ₸»,
  	разорванное так, что знак тенге уезжает на вторую строку, —
  	это уже не число, а две строки мусора. Цифры табличные,
  	то есть ширина предсказуема, и 17 пунктов в половину экрана
  	помещаются с запасом.
  */
  body.shell .app__card b {
    margin-top: 3px;
    font-size: 17px;
    letter-spacing: -0.02em;
    white-space: nowrap;
  }
  body.shell .app__card--total {
    grid-column: 1/-1;
    padding: 16px 18px;
  }
  body.shell .app__card--total b {
    font-size: 30px;
  }
}
/* ── таблицы: шапка светлая, разделители от текста ── */
body.shell .app__table th {
  padding: 12px 20px;
  background: none;
  border-bottom: 0.5px solid var(--Line);
  color: var(--Muted);
  font-size: 13px;
  font-weight: 400;
}

body.shell .app__table td {
  padding: 13px 20px;
  border-bottom: 0.5px solid var(--LineSoft);
}

body.shell .app__table small {
  font-size: 13px;
}

/*
	═══════════════════════════════════════════════════════════════════
	 ТАБЛИЦА НА ТЕЛЕФОНЕ — ЭТО НЕ ТАБЛИЦА, А СПИСОК
	═══════════════════════════════════════════════════════════════════

	ledger.scss уже не даёт таблице утащить вбок всю страницу: она
	прокручивается внутри себя. Но прокручивать список операций вбок,
	чтобы увидеть сумму, — всё равно что не показывать её вовсе,
	а сумма и есть то, ради чего в список заходят.

	Здесь строка разбирается на две: сверху дата, под ней название
	и сумма по краям. Ширины хватает, потому что вбок больше ничего
	не тянется.

	:has(.app__num) — намеренный ограничитель. Так устроены списки,
	где есть деньги: дата, кому, сколько. Таблицы другой формы
	(сверка, разложение по валютам) под это правило не подпадают
	и остаются прокручиваемыми — им перестроение только навредит.
*/
@media (max-width: 640px) {
  body.shell .app__table:has(.app__num) {
    display: block;
    width: 100%;
    overflow: visible;
    border-radius: var(--RadiusLg);
  }
  body.shell .app__table:has(.app__num) tbody,
  body.shell .app__table:has(.app__num) tr {
    display: block;
  }
  /* строка заголовков столбцов списку не нужна */
  body.shell .app__table:has(.app__num) tr:has(th) {
    display: none;
  }
  body.shell .app__table:has(.app__num) tr:has(td) {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 10px;
    row-gap: 1px;
    padding: 11px 14px;
  }
  /* разделитель от текста, а не от края карточки — как в списках */
  body.shell .app__table:has(.app__num) tr + tr {
    position: relative;
  }
  body.shell .app__table:has(.app__num) tr + tr::before {
    content: "";
    position: absolute;
    left: 14px;
    right: 0;
    top: 0;
    height: 0.5px;
    background: var(--LineSoft);
  }
  body.shell .app__table:has(.app__num) td {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0;
    border: 0;
  }
  /* дата — отдельной строкой сверху, мелко и приглушённо */
  body.shell .app__table:has(.app__num) .app__date {
    order: -1;
    flex: 1 1 100%;
    font-size: 13px;
  }
  /* сумма — к правому краю, крупнее подписи */
  body.shell .app__table:has(.app__num) .app__num {
    flex: 0 0 auto;
    margin-left: auto;
    font-size: 16px;
  }
  /* кнопки строки — своей строкой, иначе они ломают выравнивание */
  body.shell .app__table:has(.app__num) .app__actions {
    flex: 1 1 100%;
    margin-top: 6px;
    text-align: left;
  }
}
/*
	═══════════════════════════════════════════════════════════════════
	 РАЗБИВКА КАТЕГОРИИ НА ТЕЛЕФОНЕ (подкатегории, магазины, товары)
	═══════════════════════════════════════════════════════════════════

	Общий «список» выше раскладывал строку флексом с переносом: название,
	сумма, доля и спарклайн вставали кто куда поместился — у одной строки
	сумма рядом с названием, у соседней под ним, спарклайн то справа,
	то на отдельной строке. Глаз не находил сумму на одном и том же месте.

	Здесь строка — сетка с постоянной формой: название во всю ширину
	(количество под ним), ниже одной полосой сумма · доля · спарклайн
	у правого края. У каждой строки одинаково.
*/
@media (max-width: 640px) {
  body.shell .app__table--trend:has(.app__num) tr:has(td) {
    display: grid;
    grid-template-columns: auto auto minmax(0, 1fr);
    align-items: center;
    column-gap: 12px;
    row-gap: 6px;
  }
  body.shell .app__table--trend:has(.app__num) td {
    min-width: 0;
  }
  body.shell .app__table--trend:has(.app__num) td:first-child {
    grid-column: 1/-1;
    white-space: normal;
    overflow-wrap: break-word;
    line-height: 1.35;
  }
  body.shell .app__table--trend:has(.app__num) td.app__num {
    margin-left: 0;
    font-size: 16px;
  }
  body.shell .app__table--trend:has(.app__num) td.app__num + td.app__num {
    font-size: 13px;
    color: var(--Muted);
  }
  body.shell .app__table--trend .app__spark {
    width: auto;
    justify-self: end;
  }
  body.shell .app__table--trend .spark {
    width: 96px;
    height: 24px;
  }
}
/*
	НЕ УДАЛОСЬ РАЗОБРАТЬ НА ТЕЛЕФОНЕ.

	Причина от робота бывает в три строки («модель не нашла на картинке
	ни одной позиции… пришлите ФАЙЛОМ…»), а ячейки таблиц на телефоне
	намеренно nowrap — фраза уезжала за край карточки и обрезалась.
	Причина — текст, а не значение: переносим по словам, во всю ширину.

	Голая «1» рядом со ссылками читалась как номер или счётчик чего-то
	непонятного: шапка «Попыток» на телефоне скрыта. Подпись берём
	из data-l, как у позиций чека.
*/
@media (max-width: 640px) {
  body.shell .app__table--failed td.app__why {
    flex: 1 1 100%;
    white-space: normal;
    overflow-wrap: break-word;
  }
  body.shell .app__table--failed td.app__why small {
    margin-top: 4px;
    line-height: 1.4;
  }
  body.shell .app__table--failed td[data-l] {
    order: 5;
    flex: 0 0 auto;
    margin-left: auto;
    font-size: 13px;
    color: var(--Muted);
  }
  body.shell .app__table--failed td[data-l]::before {
    content: attr(data-l) ": ";
  }
}
/*
	Сводка: четыре числа в две колонки.

	БЫЛО: grid 1fr 1fr. У 1fr минимум — auto, то есть min-content,
	а сумма не переносится (nowrap), и колонка раздвигалась по самой
	длинной сумме: «2 349 887,82 ₸» при 23px вылезало за правый край.
	minmax(0,1fr) держит колонки ровно в половину, а кегль считается
	от ширины самой карточки (cqi): где места мало, число мельчает,
	но остаётся целым и в карточке.
*/
@media (max-width: 640px) {
  body.shell .app__cards {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  body.shell .app__card {
    min-width: 0;
  }
  body.shell .app__card.app__fig {
    container-type: inline-size;
  }
  body.shell .app__card.app__fig .app__fig__value {
    font-size: clamp(14px, 11cqi, 23px);
    white-space: nowrap;
  }
}
/*
	Цифры товара — парами «подпись · значение» в строку. Общий вид
	.app__facts на телефоне — подпись над значением, и семь пар
	растягивались на полтора экрана до графика.
*/
@media (max-width: 640px) {
  body.shell .app__facts--pairs {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 8px 12px;
    margin-top: 16px;
  }
  body.shell .app__facts--pairs dt {
    margin-top: 0;
    font-size: 14px;
  }
  body.shell .app__facts--pairs dd {
    text-align: right;
  }
  body.shell .app__facts--pairs dd small {
    display: block;
  }
}
body.shell .app__task__meta .app__badge {
  white-space: nowrap;
}

/*
	КАТАЛОГ ТОВАРОВ НА ТЕЛЕФОНЕ.

	Общий «список» ставил название, фасовку и число покупок в одну
	полосу флексом: длинное название («200 GB (Google One) (разработчик
	Google LLC)») не переносилось и уезжало за край, а голые «1», «4»,
	«40» справа от него было не отличить друг от друга — шапка
	с «Фасовка» и «Покупок» на телефоне скрыта.

	Строка — сетка постоянной формы: дата, название во всю ширину
	с переносом, под ним подписанные «фасовка · покупок» слева и цена
	справа. Подписи из data-l — тот же приём, что у позиций чека.
*/
@media (max-width: 640px) {
  body.shell .app__table--products:has(.app__num) tr:has(td) {
    display: grid;
    grid-template-columns: auto auto minmax(0, 1fr);
    align-items: baseline;
    column-gap: 14px;
    row-gap: 4px;
  }
  body.shell .app__table--products:has(.app__num) td {
    min-width: 0;
  }
  body.shell .app__table--products:has(.app__num) .app__date {
    grid-column: 1/-1;
    grid-row: 1;
  }
  body.shell .app__table--products:has(.app__num) td:first-child {
    grid-column: 1/-1;
    grid-row: 2;
    white-space: normal;
    overflow-wrap: break-word;
    line-height: 1.35;
  }
  body.shell .app__table--products:has(.app__num) td[data-l] {
    grid-row: 3;
    margin-left: 0;
    font-size: 13px;
    color: var(--Muted);
  }
  body.shell .app__table--products:has(.app__num) td[data-l]::before {
    content: attr(data-l) " ";
  }
  body.shell .app__table--products:has(.app__num) td[data-l] .app__muted {
    color: inherit;
  }
  body.shell .app__table--products:has(.app__num) td.app__num:not([data-l]) {
    grid-row: 3;
    justify-self: end;
    margin-left: 0;
  }
}
/*
	ПЕРЕКЛЮЧАТЕЛЬ ПЕРИОДА БЮДЖЕТА — те же капсулы, что у фильтров
	списков (.app__tabs выше).

	БЫЛО: planning задаёт выбранной вкладке чёрную заливку и белый
	текст, но «body.shell a{color:AccentInk}» сильнее по весу,
	чем «.plan__tab--cur», — и выбранная вкладка выходила зелёным
	текстом по чёрному, на ПК и на телефоне одинаково.
*/
body.shell .plan__tabs {
  gap: 8px;
}

body.shell .plan__tab {
  padding: 7px 16px;
  border-radius: 999px;
  background: var(--Fill);
  color: var(--Ink);
  font-size: 14px;
  font-weight: 500;
}

body.shell .plan__tab:hover {
  background: var(--FillStrong);
  text-decoration: none;
}

body.shell .plan__tab--cur,
body.shell .plan__tab--cur:hover {
  background: var(--Accent);
  color: #FFF;
}

/* Слово «больше на / меньше на» — тем же цветом, что сумма, но обычным
   шрифтом: оно подпись к числу, а не часть его */
.plan__delta {
  font-size: 13px;
  font-weight: 600;
}

.plan__arrow {
  margin-right: 4px;
  font-weight: 700;
  font-size: 15px;
}

/*
	На телефоне остаётся стрелка с суммой: «↑ 115 225,00 ₸».
	Слово прячем визуально, но не display:none — экранный диктор
	его по-прежнему читает.
*/
@media (max-width: 640px) {
  .plan__delta {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
}
/*
	На телефоне месяц — своей строкой над кнопками, а кнопки — парой
	по краям. Три элемента в одном переносимом ряду вставали лесенкой:
	«← предыдущий» и месяц сверху, «следующий →» одна под ними.
*/
@media (max-width: 480px) {
  body.shell .plan__nav {
    justify-content: space-between;
  }
  body.shell .plan__period {
    order: -1;
    flex: 1 0 100%;
  }
}
/*
	ЗДОРОВЬЕ И РОБОТЫ НА ТЕЛЕФОНЕ.

	Значение проверки здесь не сумма, а фраза: «0.00 из 8.00 USD
	за сутки · 0.00 из 2.50 за час · вызовов 0». Общий запрет переноса
	у .app__num держал её одной строкой, и она уезжала за край.
	У роботов то же с описанием и расписанием — --robots не был
	в списке исключений ledger, и подпись «Чеки и выписки…» резалась.

	Значение переносится по словам и прижато вправо; если оно длинное,
	занимает всю строку под описанием.
*/
@media (max-width: 640px) {
  body.shell .app__table--health td,
  body.shell .app__table--robots td {
    white-space: normal;
    overflow-wrap: break-word;
  }
  body.shell .app__table--health td.app__num,
  body.shell .app__table--robots td.app__num {
    flex: 0 1 auto;
    max-width: 100%;
    text-align: right;
  }
  body.shell .app__table--robots .app__out {
    max-width: 100%;
    overflow-x: auto;
  }
}
/* ── снятые с разбора ── */
body.shell .dis {
  border-radius: var(--RadiusLg);
}

body.shell .dis__row {
  min-height: 58px;
  padding: 12px 20px;
}

body.shell .dis__row + .dis__row::before {
  height: 0.5px;
  left: 20px;
}

body.shell .dis__what {
  font-size: 16px;
}

body.shell .dis__act .app__btn {
  padding: 6px 13px;
  font-size: 14px;
}

/*
	На телефоне кнопка ОСТАЁТСЯ В СТРОКЕ. Сначала она была перенесена
	под текст «чтобы тексту досталось больше места» — и каждая строка
	выросла вдвое, а девять снятых задач снова превратились в простыню,
	от которой список и спасал. Тексту хватает и остатка: он в одну
	строку с многоточием, а узнают запись по сумме и дате, то есть
	по её началу.
*/
@media (max-width: 640px) {
  body.shell .dis__row {
    gap: 10px;
    padding: 9px 14px;
    min-height: 52px;
  }
  body.shell .dis__what {
    font-size: 15px;
  }
  body.shell .dis__act .app__btn {
    padding: 6px 11px;
    font-size: 13px;
  }
}
/* ── кнопки ── */
body.shell .app__btn {
  border: 0;
  border-radius: 999px;
  background: var(--Fill);
  color: var(--AccentInk);
  font-size: 15px;
  font-weight: 600;
  padding: 9px 16px;
  transition: background-color 0.12s ease, transform 0.08s ease;
}

body.shell .app__btn:hover {
  background: var(--FillStrong);
}

body.shell .app__btn:active {
  transform: scale(0.97);
}

body.shell .app__btn--main {
  background: var(--Accent);
  color: #FFF;
}

body.shell .app__btn--main:hover {
  background: var(--AccentDark);
}

body.shell .app__btn--ink {
  background: var(--Ink);
  color: #FFF;
}

body.shell .app__btn--danger {
  background: var(--BadSoft);
  color: var(--Bad);
}

body.shell .app__btn--danger:hover {
  background: var(--Bad);
  color: #FFF;
}

body.shell .app__link {
  font-weight: 500;
}

/* ── сегментный переключатель ── */
/*
	Подвкладки раздела — это выбор одного из двух-трёх видов одного
	и того же экрана, а не переход в другое место. Системный вид такого
	выбора — сегментный переключатель: серая дорожка, а выбранное
	приподнято белой «костяшкой».
*/
body.shell .app__tabs--sub {
  display: inline-flex;
  /*
  	Перенос разрешён как страховка, а не как замысел. Переключатель
  	рассчитан на два-три пункта и в одну строку помещается всегда;
  	но если однажды в него положат шесть, он перенесётся второй
  	строкой, а не уедет за край экрана вместе со страницей.
  */
  flex-wrap: wrap;
  max-width: 100%;
  gap: 0;
  padding: 3px;
  border-radius: 12px;
  background: var(--Fill);
}

body.shell .app__tabs--sub .app__tab {
  padding: 6px 16px;
  border-radius: 9px;
  color: var(--Ink);
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
}

body.shell .app__tabs--sub .app__tab:hover {
  background: none;
}

body.shell .app__tabs--sub .app__tab--cur,
body.shell .app__tabs--sub .app__tab--cur:hover {
  background: var(--Bg);
  color: var(--Ink);
  font-weight: 600;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 0 0.5px rgba(0, 0, 0, 0.04);
}

/*
	Остальные .app__tab — фильтры списка, а их бывает и восемь. Это уже
	не переключатель, а набор признаков, и системный вид для него —
	капсулы: серая по умолчанию, акцентная у выбранной.
*/
body.shell .app__tabs:not(.app__tabs--sub) {
  gap: 8px;
}

body.shell .app__tabs:not(.app__tabs--sub) .app__tab {
  padding: 7px 16px;
  border-radius: 999px;
  background: var(--Fill);
  color: var(--Ink);
  font-size: 14px;
  font-weight: 500;
}

body.shell .app__tabs:not(.app__tabs--sub) .app__tab:hover {
  background: var(--FillStrong);
}

body.shell .app__tabs:not(.app__tabs--sub) .app__tab--cur,
body.shell .app__tabs:not(.app__tabs--sub) .app__tab--cur:hover {
  background: var(--Accent);
  color: #FFF;
}

body.shell .app__tab small {
  color: inherit;
  opacity: 0.62;
}

body.shell .app__tab--cur small {
  color: inherit;
  opacity: 0.8;
}

/* ── поля ввода ── */
/*
	⚠️ 17px — НЕ ВКУСОВЩИНА. Safari на iPhone увеличивает страницу,
	когда фокус попадает в поле с кеглем меньше 16px, и обратно
	не возвращает: экран остаётся сдвинутым, а половина формы —
	за краем. Ровно это происходило в форме ввода операции.
*/
body.shell .app__field input:not([type=checkbox]):not([type=radio]):not([type=submit]):not([type=button]):not([type=reset]):not([type=file]):not([type=range]):not([type=color]),
body.shell .app__field textarea,
body.shell .app__field select,
body.shell .pick__field {
  border: 0;
  border-radius: var(--Radius);
  min-height: 46px;
  background: var(--Fill);
  color: var(--Ink);
  font-size: 17px;
  padding: 11px 12px;
}

body.shell .app__field input:focus,
body.shell .app__field textarea:focus,
body.shell .app__field select:focus {
  background: var(--Bg);
  outline: 0;
  box-shadow: 0 0 0 3px var(--AccentSoft), inset 0 0 0 1px var(--Accent);
}

body.shell .app__field > span {
  font-size: 13px;
  color: var(--Muted);
  font-weight: 400;
}

body.shell .app__hint {
  font-size: 13px;
}

/* ── сообщения и плашки ── */
body.shell .app__msg {
  border: 0;
  border-radius: var(--RadiusLg);
  padding: 14px 20px;
  background: var(--AccentSoft);
  color: var(--AccentInk);
  font-size: 15px;
}

body.shell .app__msg--error {
  background: var(--BadSoft);
  color: var(--Bad);
}

body.shell .app__msg--warn {
  background: var(--WarnSoft);
  color: var(--Warn);
}

body.shell .app__msg--ok {
  background: var(--OkSoft);
  color: var(--Ok);
}

body.shell .app__badge {
  border-radius: 999px;
  font-weight: 600;
}

body.shell .app__h2 {
  font-family: var(--FontHead);
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.018em;
}

body.shell .app__h3 {
  font-size: 17px;
  font-weight: 600;
}

body.shell .app__empty {
  border-radius: var(--RadiusLg);
  background: var(--Bg);
  color: var(--Muted);
}

/* ── страницы списка ── */
body.shell .app__page {
  border: 0;
  border-radius: var(--Radius);
  background: var(--Fill);
  font-weight: 500;
}

/*
	СОСТОЯНИЯ ПЕРЕКЛЮЧАТЕЛЯ ПОВТОРЯЮТСЯ ЗДЕСЬ, И ЭТО НЕ ДУБЛЬ.

	Правило выше задано селектором body.shell — его специфичность
	выше простого класса, и .app__page--cur из ledger.scss до фона
	не доходил: текущая страница выглядела ровно как остальные.
	Раньше это терялось среди двух сотен номеров, а теперь номеров
	семь, и «где я» — первое, что от них требуется.
*/
body.shell .app__page--cur {
  background: var(--Ink);
  color: #FFF;
}

body.shell .app__page--gap {
  background: none;
  color: var(--Muted);
}

body.shell .app__page--off {
  background: var(--Fill);
  color: var(--Line);
}

/* ── график ── */
/*
	График лежит на белой группе, и линии сетки должны быть тише, чем
	разделители списка: сетка — это фон измерения, а не содержимое.
*/
body.shell .chart-box {
  --ChartLabel:13px;
}

body.shell .chart__tip {
  border-radius: 14px;
  font-size: 13px;
}

body.shell .chart__tip b {
  font-size: 17px;
}

/*# sourceMappingURL=main.css.map */
