/* TVN Mini App — рабочий инструмент склада. Утилитарно, без анимаций ради красоты. */

:root {
  --bg: #f2f2f2;
  --bg-secondary: #ffffff;
  --text: #1c1c1c;
  /* #707579 на --bg (#f2f2f2) даёт 4.12:1 — ниже нормы 4.5:1; на этом фоне
     живёт экран «нет доступа» (.state-msg). Затемнили до #5f6368: 5.4:1 на
     --bg и 6.05:1 на белой карточке. В Telegram --hint всё равно
     пересчитывает contrastSafeColor() в app.js::applyTheme(); эта пара —
     запасной вариант до/без Telegram. --hint нигде не фон, только текст. */
  --hint: #5f6368;
  /* #2481cc на --bg (#f2f2f2) даёт 3.69:1 и на --bg-secondary (#ffffff)
     4.13:1 — оба ниже нормы 4.5:1 (кнопка "← Дом"/"← Остатки" в карточке
     позиции, активная вкладка .tab-btn.active). Затемнили до #1f6ead:
     4.82:1 на --bg и 5.40:1 на белом — та же пара и тот же приём, что и
     у --button ниже. applyTheme() чинит link_color через
     contrastSafeColor() только когда Telegram его прислал, поэтому
     запасная (не-Telegram) палитра обязана проходить норму сама. */
  --link: #1f6ead;
  /* Белый на #2481cc даёт 4.13:1 — ниже нормы 4.5:1 для текста 15px/400
     (кнопка "Повторить"). Затемнили до #1f6ead: 5.41:1 с запасом под
     погрешность замера — тот же приём, что и для --red выше. Реальный
     button_color/button_text_color Telegram (в т.ч. в тёмной теме, где
     пара ещё хуже) app.js::applyTheme() дополнительно прогоняет через
     contrastSafeColor() против фактического фона кнопки — эта пара тут
     только запасной вариант до/без Telegram. */
  --button: #1f6ead;
  --button-text: #ffffff;
  --border: #e3e3e3;
  /* #d3402a на --red-bg (#fdeceb) даёт 4.05:1 — ниже нормы 4.5:1 для текста
     13px/600 (бейдж). Затемнили до #b8331f: 5.2:1 на том же фоне, с запасом
     под погрешность замера. Бейдж всегда рисует СВОЙ фон (--red-bg), поэтому
     карточка-алерт с тем же фоном на эту пару не влияет — важна только эта
     пара сама по себе. */
  --red: #b8331f;
  --red-bg: #fdeceb;
  --green: #1e7a38;
  --green-bg: #e3f4e8;
  --gray-bg: #ececec;
  --safe-bottom: env(safe-area-inset-bottom, 0px);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  /* clip, а НЕ hidden. hidden делает страницу скролл-контейнером и молча
     ломает position:sticky — замерено в браузере: панель фильтров уезжала
     вместе с лентой (top 104 → −196 при прокрутке на 300px), хотя
     getComputedStyle честно показывал "sticky". clip режет горизонтальный
     вылет ровно так же, но контейнера не создаёт, и залипание оживает.
     Первая строка — запас для старых движков, которые clip не знают: там
     останется hidden, то есть без залипания, но и без вылета вбок. */
  overflow-x: hidden;
  overflow-x: clip;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: 15px;
  line-height: 1.4;
}

#app {
  /* В Telegram WebView --app-min-height ставит app.js по
     tg.viewportStableHeight (следит и за открытием клавиатуры); 100vh —
     запасной вариант вне Telegram, где этой переменной никто не задаёт. */
  min-height: var(--app-min-height, 100vh);
  display: flex;
  flex-direction: column;
  padding-bottom: calc(8px + var(--safe-bottom));
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 12px 16px;
  background: var(--bg-secondary);
  border-bottom: 1px solid var(--border);
}

.topbar-title {
  font-weight: 700;
  font-size: 16px;
}

.topbar-user {
  font-size: 15px;
  color: var(--hint);
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Свой [hidden] нужен потому, что правило браузера по умолчанию слабее
   любого авторского display у того же элемента. */
[hidden] { display: none !important; }

/* Подпись «демо-данные» — верхний этаж липкой стопки (подпись → вкладки →
   фильтры). Красная на светло-красном: пара выверена до 5.2:1, см. --red. */
.demo-banner {
  position: sticky;
  top: 0;
  z-index: 30;
  padding: 6px 16px;
  background: var(--red-bg);
  color: var(--red);
  border-bottom: 1px solid var(--red);
  font-size: 13px;
  font-weight: 600;
  text-align: center;
}

/* Вкладки и панель фильтров под ними не уезжают вместе с лентой: на складе
   листают длинные списки, и возвращаться в начало ради смены фильтра — лишняя
   работа одной рукой с телефоном. sticky вместо relative — контейнером для
   .tabs-fade (position:absolute) он остаётся, это тоже позиционированное
   значение. */
.tabs-wrap {
  position: sticky;
  /* Не 0, а под подписью «демо-данные», когда она есть (--demo-h ставит
     app.js; без подписи там 0 и поведение прежнее). */
  top: var(--demo-h, 0px);
  z-index: 20;
}

.tabs {
  display: flex;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  background: var(--bg-secondary);
  border-bottom: 1px solid var(--border);
}

.tab-btn {
  flex: 0 0 auto;
  padding: 12px 16px;
  min-height: 44px;
  font-size: 15px;
  font-family: inherit;
  color: var(--hint);
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  cursor: pointer;
}

.tab-btn:active {
  background: var(--gray-bg);
}

.tab-btn.active {
  color: var(--link);
  border-bottom-color: var(--link);
  font-weight: 600;
}

/* Затухающий намёк, что вкладок больше, чем видно (442px полосы в 375px
   экрана) — только когда JS замерил реальное переполнение, см. app.js
   updateTabsFade(). Без transition/animation: появляется/исчезает сразу. */
.tabs-fade {
  display: none;
  position: absolute;
  top: 0;
  bottom: 1px;
  right: 0;
  width: 28px;
  pointer-events: none;
  background: linear-gradient(to right, transparent, var(--bg-secondary));
}

.tabs-wrap.has-overflow .tabs-fade {
  display: block;
}

.screen {
  flex: 1;
  padding: 12px 16px 24px;
  width: 100%;
}

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

.state-msg {
  padding: 32px 16px;
  text-align: center;
  color: var(--hint);
  font-size: 15px;
}

.state-msg.error {
  color: var(--red);
}

.retry-btn {
  display: inline-block;
  margin-top: 12px;
  padding: 10px 20px;
  min-height: 44px;
  background: var(--button);
  color: var(--button-text);
  border: none;
  border-radius: 10px;
  font-size: 15px;
  cursor: pointer;
}

/* Затемняем ФОН, а не всю кнопку: opacity гасила и текст — белое на синем
   становилось серым, и нажатая кнопка читалась как отключённая. Inset-тень
   ложится поверх фона, но под текстом, поэтому подпись остаётся белой. */
.retry-btn:active {
  box-shadow: inset 0 0 0 999px rgba(0, 0, 0, 0.2);
}

/* ── панель управления (поиск/фильтр/месяц) ── */

.controls {
  position: sticky;
  /* Ровно под полосой вкладок. Её высоту меряет app.js (--tabs-h): она
     зависит от системного размера шрифта, зашивать число нельзя. 45px —
     только на те мгновения, пока замер ещё не случился. */
  top: var(--stack-h, 45px);
  z-index: 10;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  /* Во всю ширину, с отменой боковых отступов .screen: будь панель узкой,
     лента проезжала бы в зазорах по бокам от неё, и это читалось бы как
     поломка, а не как закреплённая панель. Фон обязателен по той же
     причине — под прозрачной панелью просвечивал бы текст. */
  margin: 0 -16px 12px;
  padding: 8px 16px;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}

.controls input[type="text"],
.controls input[type="month"],
.controls select {
  flex: 1 1 140px;
  min-width: 0;
  min-height: 44px;
  padding: 8px 12px;
  font-size: 15px;
  font-family: inherit;
  color: var(--text);
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 10px;
}

.checkbox-field {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  padding: 0 8px;
  font-size: 15px;
  white-space: nowrap;
}

.checkbox-field input {
  width: 20px;
  height: 20px;
}

/* ── списки-карточки ── */

.card-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.card {
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 12px 14px;
}

.card.card-alert {
  border-color: var(--red);
  background: var(--red-bg);
}

/* Клик по строке накладной или позиции (списки/аномалии на "Доме") —
   единственные кликабельные варианты .card. */
.card[data-invoice]:active,
.card[data-item]:active {
  background: var(--gray-bg);
}

.card-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}

.card-title {
  font-weight: 600;
  font-size: 15px;
  word-break: break-word;
}

/* Главное число строки — остаток, переплата. Читают на бегу: должно
   зацепить глаз раньше и крупнее, чем название под ним. */
.card-qty {
  font-weight: 700;
  font-size: 22px;
  white-space: nowrap;
}

.card-qty.below {
  color: var(--red);
}

.card-sub {
  margin-top: 4px;
  font-size: 13px;
  color: var(--hint);
}

/* "· бейдж" — неразрывно (см. app.js): нельзя переносить точку отдельно
   от бейджа, который она вводит. */
.card-sub-badge {
  white-space: nowrap;
}

.badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
}

.badge-red { background: var(--red-bg); color: var(--red); }
/* Фон бейджей — переменной, как у красного: в тёмной теме захардкоженный
   светлый фон давал светло-зелёный текст на почти белом (контраст 1.67:1). */
.badge-green { background: var(--green-bg); color: var(--green); }
/* Текст серого бейджа — основным цветом, а НЕ --hint: хинт Telegram намеренно
   приглушён (до #999 на светлой теме) и не даёт 4.5:1 ни на каком фоне,
   даже на чистом белом. Приглушённость передаём фоном, а не текстом. */
.badge-gray { background: var(--gray-bg); color: var(--text); }

/* ── журнал ── */

.journal-entry {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}

.journal-entry:last-child { border-bottom: none; }

.journal-main { min-width: 0; }

.journal-item {
  font-weight: 600;
  word-break: break-word;
}

.journal-meta {
  font-size: 13px;
  color: var(--hint);
  margin-top: 2px;
}

.journal-qty {
  white-space: nowrap;
  font-weight: 700;
  font-size: 22px;
  text-align: right;
}

/* Раньше вся строка гасилась opacity:0.55 — это смешивало ЛЮБОЙ текст
   строки (включая уже выверенный до 4.5:1 --hint в .journal-meta) с фоном
   карточки и роняло контраст до ~2.2:1. Различаем "отменено" тем, что и так
   проходит по контрасту на полную непрозрачность: зачёркнутое число
   приглушённым (но замеренным) --hint и отдельный бейдж со своим фоном —
   ничего не подмешивается к фону, поэтому ничего и не проверяется заново. */
.journal-entry.undone .journal-qty {
  text-decoration: line-through;
  color: var(--hint);
}

/* Настоящий текст (см. app.js) — то, что раньше рисовалось CSS-content,
   не копировалось и не читалось озвучкой. Обёртка вокруг .badge — не сам
   красный текст: у бейджа свой фон, поэтому контраст его пары гарантирован
   так же, как у любого другого .badge-*, а не заново подбирается здесь. */
.journal-undone-note {
  margin-top: 4px;
}

/* ── отчёт ── */

.report-total {
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px;
  margin-bottom: 12px;
}

.report-total-label {
  font-size: 14px;
  font-weight: 400;
  color: var(--hint);
}

/* Главная цифра экрана — итог месяца — тонула в те же 16px, что и всё
   остальное. Теперь она самая крупная на странице. */
.report-total-amount {
  font-size: 24px;
  font-weight: 700;
  margin-top: 2px;
}

.report-total .hint {
  font-size: 13px;
  font-weight: 400;
  color: var(--hint);
  margin-top: 6px;
}

.report-group-title {
  font-weight: 700;
  font-size: 14px;
  margin: 16px 0 8px;
  color: var(--hint);
}

.event-card summary {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 14px;
  cursor: pointer;
  font-weight: 600;
  list-style: none;
  min-height: 44px;
}

.event-card summary::-webkit-details-marker { display: none; }

.event-card summary:active { background: var(--gray-bg); }

/* Деньги — главное в строке мероприятия, поэтому полный --text и крупнее
   названия: с --hint глаз цеплялся за название, а ради суммы отчёт и
   открывают. nowrap — сумма не рвётся, даже если название длинное. */
.event-card summary .money {
  color: var(--text);
  font-weight: 700;
  font-size: 17px;
  white-space: nowrap;
  flex: 0 0 auto;
  margin-left: 12px;
}

.event-lines {
  padding: 0 14px 12px;
}

.event-line {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 0;
  font-size: 15px;
  border-top: 1px solid var(--border);
}

/* ── дом ── */

.home-card {
  margin-bottom: 8px;
}

.home-metric-label {
  font-size: 14px;
  font-weight: 400;
  color: var(--hint);
}

/* Базовый размер — "Расход за месяц" (3-е по важности число экрана). */
.home-metric-value {
  font-size: 22px;
  font-weight: 700;
  margin-top: 2px;
  word-break: break-word;
}

/* Самое крупное число экрана — стоимость склада сейчас. */
.home-metric-value-xl {
  font-size: 32px;
}

/* Второе по величине — пойманные переплаты. */
.home-metric-value-lg {
  font-size: 26px;
}

/* Переплата должна читаться как победа — но только когда что-то реально
   поймали; ноль остаётся нейтральным текстом (см. app.js). */
.home-metric-value-green {
  color: var(--green);
}

.home-metric-sub {
  font-size: 13px;
  font-weight: 400;
  color: var(--hint);
  margin-top: 4px;
}

.home-split {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

.home-split-row {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 3px 0;
  font-size: 15px;
}

.home-split-row span:last-child {
  font-weight: 600;
  white-space: nowrap;
}

.home-trend-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
}

.home-trend-label {
  flex: 0 0 44px;
  font-size: 13px;
  color: var(--hint);
}

.home-trend-bar-wrap {
  flex: 1 1 auto;
  min-width: 0;
  height: 10px;
  background: var(--gray-bg);
  border-radius: 5px;
  overflow: hidden;
}

.home-trend-bar {
  height: 100%;
  min-width: 2px;
  background: var(--link);
  border-radius: 5px;
}

.home-trend-value {
  flex: 0 0 auto;
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
}

/* ── карточка позиции ── */

.item-detail-back {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 8px;
  margin-bottom: 8px;
  margin-left: -8px;
  color: var(--link);
  background: none;
  border: none;
  border-radius: 8px;
  font-size: 15px;
  cursor: pointer;
}

.item-detail-back:active {
  background: var(--gray-bg);
}

/* "Хватит на N дней" — отдельная заметная строка между остатком/нормой
   и статистикой расхода, не такая крупная, как .card-qty (это не число
   для сверки, это вывод), но заметно крупнее .card-sub. */
.item-days-row {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  font-size: 15px;
  font-weight: 600;
}

.price-row {
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}

.price-row:last-child {
  border-bottom: none;
}

.price-row-main {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}

.price-supplier {
  font-weight: 600;
  word-break: break-word;
}

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

/* Приход/коррекция вверх — единственный случай зелёного числа в истории
   движений; обычное списание остаётся нейтральным цветом текста. Красный
   в этом приложении зарезервирован за алертами (below/переплата), а не
   за любым расходом склада — списание это норма, а не проблема. */
.journal-qty-in {
  color: var(--green);
}

/* ── накладные ── */

.invoice-detail-back {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 8px;
  margin-bottom: 8px;
  margin-left: -8px;
  color: var(--link);
  background: none;
  border: none;
  border-radius: 8px;
  font-size: 15px;
  cursor: pointer;
}

.invoice-detail-back:active {
  background: var(--gray-bg);
}

/* Карточки на строку — вид по умолчанию, работает на любой ширине.
   Таблица (все 7 колонок в 1025px) только для широких экранов — на 341px
   контейнера она пряталась во внутренней прокрутке без единого намёка,
   и была видна ровно одна колонка "Позиция". */
.invoice-lines-cards {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.invoice-lines-table {
  display: none;
}

@media (min-width: 640px) {
  .invoice-lines-cards {
    display: none;
  }
  .invoice-lines-table {
    display: block;
  }
}

.line-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px;
}

.line-table-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: 12px;
}

.line-table th, .line-table td {
  padding: 8px 10px;
  text-align: left;
  white-space: nowrap;
  border-bottom: 1px solid var(--border);
}

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

/* ── закуп ── */

.order-card .supplier-block {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
}

.order-card .supplier-name {
  font-weight: 600;
  font-size: 15px;
  color: var(--hint);
  margin-bottom: 4px;
}

.order-line {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 15px;
  padding: 3px 0;
}

/* ── пагинация ── */

.load-more-btn {
  display: block;
  width: 100%;
  margin-top: 12px;
  padding: 12px;
  min-height: 44px;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--link);
  font-size: 15px;
  font-family: inherit;
  cursor: pointer;
}

.load-more-btn:active {
  background: var(--gray-bg);
}

/* Запасной вариант — только когда страница открыта НЕ в Telegram (там
   applyTheme() в app.js красит --border/--red/--red-bg/--green сама по
   реальной теме Telegram, темнее и точнее этого медиазапроса). */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0e0e0f;
    --bg-secondary: #1c1c1e;
    --text: #f2f2f2;
    --hint: #9a9a9e;
    --border: #2c2c2e;
    --red: #ff6b5b;
    --red-bg: #3a1f1c;
    --green: #4cd964;
    /* Раньше здесь не хватало --green-bg/--gray-bg: badge-green/badge-gray
       (в т.ч. "лучшая цена", "цена могла устареть", "отменено") оставались
       на светлых значениях из :root — почти белый текст на светло-сером/
       светло-зелёном фоне, контраст рушился ниже нормы. Те же значения,
       что app.js::applyTheme() уже ставит для реального тёмного Telegram —
       здесь только запасной вариант до/без Telegram, см. коммент выше. */
    --green-bg: #1d3324;
    --gray-bg: #2a2a2c;
    --link: #6ab7ff;
    --button: #6ab7ff;
    --button-text: #0e0e0f;
  }
}
