/* Светлая/тёмная тема через prefers-color-scheme, safe-area под вырез и home bar. */

:root {
  color-scheme: light dark;

  --bg: #f4f5f7;
  --surface: #ffffff;
  --text: #16181d;
  --muted: #6b7280;
  --line: #e3e4e8;
  /* Дорожка прогресса — не декор: она показывает, из чего считается доля,
     поэтому обязана быть видна. --line для рамок годится (1.27:1), а как
     носитель смысла проваливает порог 3:1 для нетекстовых элементов. */
  --track: #888e98;
  --accent: #10794f;
  --accent-soft: #e6f2ec;
  --danger: #b3261e;
  /* Предупреждение — не ошибка: цифры от прежнего описания сохранять не мешают. */
  --warn: #8a5300;
  --shadow: 0 1px 2px rgba(16, 18, 29, .06);

  /* Шкалы. Объявлены были только отступ и радиус, причём радиус использовался
     один раз на весь файл: в нём жили шесть литеральных радиусов и десять
     кеглей, семь из которых в диапазоне 11–17 с шагом в пиксель. Разница в
     пиксель не читается как иерархия, зато каждое такое число — отдельное
     правило и отдельный повод разъехаться. */
  --pad: 16px;

  /* Радиусы: мелкий (поля, кнопки), обычный (карточки), лист и пилюля. */
  --radius-s: 10px;
  --radius: 14px;
  --radius-l: 18px;
  --radius-pill: 99px;

  /* Кегли. Шесть ступеней вместо десяти: 11 и 17 px убраны как неотличимые от
     соседей (12 и 20), 24 сведён к 26 — оба были «крупное число дня».
     Исключений два, и оба намеренные: поля ввода держат 16 px (иначе Safari на
     iOS зумит вьюпорт при фокусе и обратно не откатывает), а подпись внутри
     кольца — 11 px, потому что должна уместиться между штрихами. */
  --fs-xs: 12px;      /* подписи вкладок, микроподписи в таблицах */
  --fs-s: 13px;       /* подсказки, заголовки карточек */
  --fs-m: 14px;       /* обычный текст, строки состояния */
  --fs-l: 15px;       /* названия позиций, ккал приёма */
  --fs-title: 20px;
  --fs-number: 26px;

  /* Ширина колонки. Экраны тянулись во всю ширину, и на iPad или в ландшафте
     полосы БЖУ превращались в нити длиной под тысячу пикселей при высоте 4 px:
     метка слева, значение справа, между ними пустота во весь экран. Листы уже
     были ограничены 520 px — здесь та же величина. */
  --col: 520px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #101114;
    --surface: #1a1c21;
    --text: #eceef2;
    --muted: #9aa1ac;
    --line: #2a2d34;
    --track: #6b7280;
    --accent: #3ddc91;
    --accent-soft: #16302a;
    --danger: #ff6b60;
    --warn: #f5b942;
    --shadow: none;
  }
}

* { box-sizing: border-box; }

/* Атрибут hidden обязан скрывать что угодно.
   Правило браузера `[hidden] { display: none }` имеет вес (0,1,0) и проигрывает
   любому авторскому `.class { display: flex }` — просто потому, что авторские
   стили сильнее пользовательского агента. Ловушка тихая: атрибут стоит, JS
   считает элемент скрытым, а человек видит его на экране. В файле уже было два
   точечных лечения (.screen-error[hidden] и .day-back[hidden]) — то есть на неё
   наступали дважды, и третий флекс со скрытием атрибутом наступил бы снова.
   Одно правило закрывает это для всех элементов сразу, включая будущие. */
[hidden] { display: none !important; }

html { height: 100%; }

/* Ровно высота вьюпорта и без своей прокрутки: шапка и таб-бар — края колонки,
   скроллится только .screen между ними. */
body {
  margin: 0;
  height: 100dvh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.45 -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  -webkit-text-size-adjust: 100%;
  overscroll-behavior-y: none;
}

/* --- шапка ---------------------------------------------------------------- */

.app-header {
  position: sticky;
  top: 0;
  z-index: 2;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--line);
  padding: 10px var(--pad) 10px;
  padding-top: max(10px, env(safe-area-inset-top));
  padding-left: max(var(--pad), env(safe-area-inset-left));
  padding-right: max(var(--pad), env(safe-area-inset-right));
}

.app-header h1 {
  margin: 0 auto;
  max-width: var(--col);
  font-size: var(--fs-title);
  font-weight: 650;
  letter-spacing: -0.01em;
}

/* --- экран ---------------------------------------------------------------- */

.screen {
  flex: 1;
  /* Без сброса auto-минимума флекс-элемент не сжимается ниже содержимого и
     overflow-y: auto не включается — тогда уезжает вся страница с таб-баром. */
  min-height: 0;
  overflow-y: auto;
  /* Гарантия от rubber-band ушла вместе с прокруткой body — она нужна тому,
     кто теперь скроллит. */
  overscroll-behavior-y: contain;
  -webkit-overflow-scrolling: touch;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: var(--pad);
  padding-left: max(var(--pad), env(safe-area-inset-left));
  padding-right: max(var(--pad), env(safe-area-inset-right));
}

/* Содержимое экрана держится колонкой и на широком экране: карточки — прямые
   потомки .screen, поэтому ограничиваем их, а не контейнер (у контейнера своя
   роль — прокрутка и safe-area).

   Селектор с :not, а не голое `.screen > *`: у последнего вес (0,1,0) — ровно
   как у .day и .card, и правило проигрывало бы им по порядку в файле, оставляя
   часть строк прижатой к краю. Листы и кнопка добавления исключены по смыслу:
   это перекрытия с position: fixed, а кнопке ширина 100% вдобавок ломает форму —
   круг с border-radius: 50% превращается в эллипс во всю колонку. */
.screen > :not(.sheet, .fab) {
  width: 100%;
  max-width: var(--col);
  margin-inline: auto;
}

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: var(--pad);
}

.stack { display: flex; flex-direction: column; gap: 18px; }

.day { margin: 0; color: var(--muted); font-size: var(--fs-m); }
.day::first-letter { text-transform: uppercase; }
.muted { color: var(--muted); }
.hint { margin: 6px 0 0; color: var(--muted); font-size: var(--fs-s); }
.error { color: var(--danger); }

/* Текста не видно, но он читается вслух. Нужен там, где состояние показано
   цветом и формой кнопки: глазами понятно, на слух — нет. Именно скрытие,
   а не display:none — скрытое так содержимое из дерева доступности выпадает
   и живой областью быть перестаёт. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Напоминание про экспорт: обычная подсказка тут теряется, а терять её нельзя (§6). */
.notice {
  margin: 0;
  padding: 10px 12px;
  border-radius: var(--radius-s);
  background: var(--accent-soft);
  font-size: var(--fs-s);
  line-height: 1.4;
}
.notice.is-warn {
  background: color-mix(in srgb, var(--danger) 10%, transparent);
  color: var(--danger);
  font-weight: 550;
}

/* Отказ базы уровня экрана: единственное место, которое переживает закрытие
   листа. Ширина карточки, а не строки, — сообщение длинное и его нельзя проглядеть. */
.screen-error {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  border: 1px solid color-mix(in srgb, var(--danger) 35%, transparent);
  border-radius: var(--radius-s);
  background: color-mix(in srgb, var(--danger) 8%, transparent);
}
.screen-error-msg {
  margin: 0;
  color: var(--danger);
  font-size: var(--fs-s);
  line-height: 1.4;
  font-weight: 550;
}
.screen-error-retry {
  align-self: flex-start;
  padding: 8px 14px;
  border: 1px solid color-mix(in srgb, var(--danger) 35%, transparent);
  border-radius: var(--radius-s);
  background: none;
  color: var(--danger);
  font-weight: 600;
  font-size: var(--fs-s);
  cursor: pointer;
}

/* Что именно выбрано на графике — прямо под ним.
   На 90 дней приходится ~3,4 px, попасть пальцем в нужный день нельзя в
   принципе; протяжка это чинит, но только если видно, что выбираешь, — а дата
   и цифры живут в карточке «Выбранный день» заметно ниже. */
.chart-readout {
  margin: 6px 0 0;
  font-size: var(--fs-s);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  min-height: 1.2em;
}
.chart-readout.is-over { color: var(--danger); }

/* День без записей: дата и прямая подпись вместо крупного нуля. */
.day-none { margin: 4px 0 0; color: var(--muted); font-size: var(--fs-l); }

.mt-8 { margin-top: 8px; }
/* Голое .mt: разметка черновика выдаёт <div class="grid-2 mt">, а правило было
   только у .field.mt — сетка полей прилипала вплотную к строке с названием. */
.mt { margin-top: 14px; }
/* min-height держит место под будущий текст, чтобы форма не дёргалась, когда
   появляется «Сохранено». Ради этого же здесь нет .status:empty { display: none } —
   с ним резерв не работал вовсе, и layout прыгал на 20 px. */
.status { margin: 0; min-height: 1.2em; color: var(--accent); font-size: var(--fs-m); }

.card-title {
  margin: 0 0 8px;
  font-size: var(--fs-s);
  font-weight: 600;
  color: var(--muted);
}

/* --- перелистывание дня ---------------------------------------------------- */

.day-nav {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 6px 6px 10px;
  border: 1px solid transparent;
  border-radius: var(--radius-s);
}
.day-nav .day { flex: 1; text-align: center; }

/* Открыт не сегодняшний день. Метка нужна именно броская: кольцо «1900 из 2000»
   выглядит одинаково для любых суток, и незамеченная чужая дата — это неверный
   вывод о собственном питании, а не мелкое неудобство. */
.day-nav.is-other {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.day-nav.is-other .day { color: var(--text); font-weight: 600; }

.day-arrow {
  flex: none;
  /* 44×44 — минимум по Human Interface Guidelines, как у стрелок календаря. */
  min-width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--surface);
  color: var(--text);
  font-size: var(--fs-title);
  line-height: 1;
  cursor: pointer;
}
/* Край: в будущее данным взяться неоткуда. То же правило, что у .cal-arrow. */
.day-arrow:disabled { opacity: .3; cursor: default; }

/* Возврат к сегодня — выход из состояния, а не главное действие экрана: стоит
   по правому краю и занимает место по содержимому. Во всю ширину он спорил бы
   за внимание с кнопкой «+». */
.day-back {
  align-self: flex-end;
  min-height: 44px;
  padding: 10px 14px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-s);
  background: var(--surface);
  color: var(--accent);
  font-size: var(--fs-m);
  font-weight: 550;
  cursor: pointer;
}

/* --- «Сегодня»: кольцо ккал ---------------------------------------------- */

.ring-row { display: flex; align-items: center; gap: 16px; }

.ring { position: relative; flex: none; width: 124px; height: 124px; }
.ring-svg { width: 100%; height: 100%; transform: rotate(-90deg); }
/* Дорожка обязана быть видна (она показывает, из чего считается доля), но в
   пустом дне сплошной серый бублик перетягивает на себя весь экран, не неся при
   этом ни одной цифры. Приглушаем, оставаясь выше порога 3:1. */
.ring-track { fill: none; stroke: color-mix(in srgb, var(--track) 55%, var(--surface)); stroke-width: 11; }
.ring-arc { fill: none; stroke-width: 11; stroke-linecap: round; }
.ring-fill { stroke: var(--accent); }
.ring-over { stroke: var(--danger); }

/* Превышение: норма уходит на второй план, сверху — дуга перебора. */
.ring.is-over .ring-fill { stroke: color-mix(in srgb, var(--accent) 32%, var(--surface)); }

.ring-center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  padding: 0 12px; /* длинные подписи переносятся, а не лезут на кольцо */
  text-align: center;
  line-height: 1.15;
}
.ring-center strong {
  font-size: var(--fs-number);
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
/* Единственный кегль вне шкалы, и по той же причине, что 16 px у полей ввода:
   тут физическое ограничение. Подпись обязана уместиться внутрь кольца, между
   штрихами, а «перебор +783» на 12 px её уже перекрывает. */
.ring-center span { font-size: 11px; }
.ring-note { color: var(--accent); font-weight: 600; }
.ring.is-over .ring-center strong,
.ring.is-over .ring-note { color: var(--danger); }

/* --- «Сегодня»: полосы БЖУ ----------------------------------------------- */

.macro-bars {
  flex: 1;
  min-width: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 11px;
}
.macro {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: baseline;
  column-gap: 8px;
}
.macro dt { color: var(--muted); font-size: var(--fs-xs); font-weight: 600; }
.macro dd {
  margin: 0;
  text-align: right;
  font-size: var(--fs-s);
  font-variant-numeric: tabular-nums;
}
.bar {
  grid-column: 1 / -1;
  height: 4px;
  margin-top: 5px;
  border-radius: var(--radius-pill);
  background: var(--track);
  overflow: hidden;
}
.bar > span { display: block; height: 100%; border-radius: var(--radius-pill); background: var(--accent); }
.macro.is-over dd { color: var(--danger); }
.macro.is-over .bar > span { background: var(--danger); }

/* --- «Сегодня»: вес и темп (Э5) ------------------------------------------- */
/* Одна строка, а не своя вкладка: взвешивание — часть утра. */

.weight-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.weight-label {
  flex: none;
  color: var(--muted);
  font-size: var(--fs-m);
  font-weight: 550;
}
/* Перебивает общее width: 100% для input[type=text]: поле стоит в строку с
   подписью и кнопкой, а не в столбец формы. Селектор из двух классов намеренно —
   у общего правила есть атрибут, и по одному классу оно бы выиграло. */
.weight-row .weight-input {
  width: 5.5em;
  min-height: 44px;
  margin-left: auto;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--bg);
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.weight-unit { flex: none; color: var(--muted); font-size: var(--fs-m); }

.weight-spark { margin-top: 12px; }
.weight-spark:empty { display: none; }
.spark-svg { display: block; width: 100%; height: 60px; }
/* Сырая точка бледная, сглаженная линия — основная: читают линию, но разброс
   обязан быть виден, иначе кривая обещает точность, которой нет. */
.spark-raw {
  stroke: var(--muted);
  stroke-width: 3.4;
  stroke-linecap: round;      /* нулевой штрих с круглым концом и есть точка */
  vector-effect: non-scaling-stroke;
  opacity: .35;
}
.spark-ema {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* Линия растянута по X через preserveAspectRatio — без этого она бы утолщалась
     вместе с масштабом и на широком экране выглядела бы жирнее, чем на узком. */
  vector-effect: non-scaling-stroke;
}
/* История до текущего окна: показана, но приглушена — подпись под графиком
   говорит про последние N дней, а не про всё нарисованное. */
.spark-ema.is-old { stroke: var(--muted); opacity: .45; }

.weight-trend {
  margin: 10px 0 0;
  font-size: var(--fs-m);
  font-weight: 550;
  font-variant-numeric: tabular-nums;
}
.weight-trend:empty { display: none; }
/* Оговорка про смещение — мельче числа, но не спрятана: она объясняет, что
   именно описывает ±N, и без неё число читается как абсолютное. */
.weight-note {
  margin: 6px 0 0;
  color: var(--muted);
  font-size: var(--fs-xs);
  line-height: 1.45;
}
.weight-goal { margin-top: 10px; }

/* --- «Сегодня»: приёмы пищи ---------------------------------------------- */
/* Карточка на приём, внутри позиции: правится позиция, удаляется приём целиком. */

.meals { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.meals .empty { padding: 4px 0; color: var(--muted); font-size: var(--fs-m); }

.meal {
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  /* Карточка внутри карточки: чуть темнее поверхности, без второй тени. */
  background: color-mix(in srgb, var(--bg) 55%, var(--surface));
}

.meal-head { display: flex; align-items: center; gap: 10px; }
/* Заголовок, итог и время — стопкой: карточке внутри карточки достаётся 285 px,
   в них «ккал + БЖУ» справа не оставляют заголовку места на перенос по словам.

   Шапка бывает и <button> (приём из одной позиции — тап правит эту позицию), и
   <div> (во всех остальных случаях и всегда в истории). Разделено так же, как у
   строки позиции: общее — здесь, кнопочное — в модификаторе. */
.meal-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
/* Сбросы собственного оформления <button> и то, чего у <div> не бывает.
   min-height — норма HIG 44 px; шапка из трёх строк и так выше, но зона нажатия
   должна держаться правилом, а не тем, сколько строк туда сегодня попало. */
.meal-body.is-editable {
  min-height: 44px;
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
  cursor: pointer;
}
/* Превью одно на приём (одно фото — один приём), поэтому оно в шапке (§2). */
.meal-thumb {
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-s);
  object-fit: cover;
  background: var(--bg);
}
.meal-title { font-size: var(--fs-l); font-weight: 600; overflow-wrap: anywhere; }
.meal-total { font-size: var(--fs-s); font-variant-numeric: tabular-nums; }
.meal-sub { color: var(--muted); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
.meal-sub.is-armed { color: var(--danger); font-weight: 600; }
.meal-kcal { font-size: var(--fs-l); font-weight: 650; font-variant-numeric: tabular-nums; }
/* Итог не рвётся посреди значения: на узком экране «У» оставалось на одной
   строке, а число уезжало на следующую. Перенос допускается между макросами. */
.meal-macros { white-space: nowrap; color: var(--muted); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }

/* Взведённая кнопка краснеет, а словами предупреждает подпись приёма: текст на
   самой кнопке менял её ширину и рвал заголовок на лишние строки. */
.meal-del {
  flex: none;
  /* 44×44 — минимальная зона нажатия по Human Interface Guidelines. Кнопка
     деструктивная и стоит в 10 px от большой зоны правки: промах стирал бы
     не то. Иконка внутри осталась прежнего размера, выросла только зона. */
  min-width: 44px;
  height: 44px;
  padding: 0 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--surface);
  color: var(--muted);
  font-size: var(--fs-s);
  cursor: pointer;
}
.meal-del svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.meal-del.is-armed {
  border-color: var(--danger);
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 12%, transparent);
}

.meal .entries { margin-top: 8px; border-top: 1px solid var(--line); }

/* «Добавить позицию»: строка во всю ширину карточки, отбитая линией, — в шапке
   рядом с превью, итогом и удалением на 285 px места уже нет.

   Зона нажатия 44 px, как у всех прочих: «строка неглавная» — не повод мельчить.
   Она стоит вплотную под последней позицией приёма, а тап по позиции открывает её
   правку: промах мимо низкой строки попадает не в соседнюю пустоту, а в чужое
   действие. Восемь лишних пикселей на карточку эта разница стоит. */
.meal-add {
  width: 100%;
  min-height: 44px;
  margin-top: 8px;
  padding: 8px 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: 0;
  border-top: 1px solid var(--line);
  background: none;
  color: var(--muted);
  font-size: var(--fs-s);
  cursor: pointer;
}
.meal-add svg {
  width: 15px;
  height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
}

/* Выход из разбора дня в его правку. Ссылка, а не кнопка: адрес настоящий, его
   видно в строке состояния и он открывается долгим тапом. */
.day-open {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-top: 6px;
  color: var(--accent);
  font-size: var(--fs-m);
  font-weight: 550;
  text-decoration: none;
}

/* --- список позиций (внутри приёма и в разборе дня в истории) ------------- */

.entries { list-style: none; margin: 0; padding: 0; }
.entries > li + li { border-top: 1px solid var(--line); }

/* Строка позиции — одна на оба экрана. Разметку её строит один модуль (ui.js),
   и второго набора правил под неё быть не должно: расхождение стиля с разметкой
   не видно ни в диффе, ни в тестах, в отличие от расхождения двух экранов в JS.
   Различаются экраны ровно обёрткой — «Сегодня» даёт <button> (тап открывает
   правку), история <div> (там только чтение), — поэтому всё, что имеет смысл
   лишь у кнопки, живёт в модификаторе, а не в копии базовых правил.

   Отбивка одна: список позиций бывает только внутри карточки приёма, поэтому
   пара `.entry { 11px }` и `.meal .entry { 9px }` держала значение, которое
   негде было увидеть и нечем проверить. */
.entry {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 0;
}

/* Редактируемая строка. Первые четыре свойства гасят собственное оформление
   кнопки (фон, рамку, центровку текста, ширину по содержимому) — у <div> гасить
   нечего. Остальные три у <div> смысла не имеют вовсе.

   min-height — норма HIG 44 px. Сейчас строка и так выше (имя 15 px и БЖУ 12 px
   при line-height 1.45 плюс отбивка дают 57 px), но зона нажатия обязана
   держаться правилом, а не совпадением размеров шрифта. */
.entry.is-editable {
  width: 100%;
  min-height: 44px;
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
}

.entry-main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.entry-side { flex: none; display: flex; flex-direction: column; align-items: flex-end; }
.entry-name { font-size: var(--fs-l); font-weight: 550; overflow-wrap: anywhere; }
.entry-kcal { font-size: var(--fs-l); font-weight: 600; font-variant-numeric: tabular-nums; }
.entry-macros, .entry-grams {
  font-size: var(--fs-xs);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* --- «История»: график за 90 дней ---------------------------------------- */

.hchart {
  display: block;
  width: 100%;
  height: auto;
  /* Вертикальный скролл страницы остаётся браузеру, горизонтальная протяжка — нам. */
  touch-action: pan-y;
  -webkit-tap-highlight-color: transparent;
}
.hchart:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.hbar { fill: var(--accent); }
.hbar.is-over { fill: var(--danger); }

/* Подсветка выбранного дня: видна и на дне без записей, где столбика нет. */
.hsel { fill: color-mix(in srgb, var(--text) 12%, transparent); }

.haxis { stroke: var(--line); stroke-width: 1; }
.haxis-tick { stroke: var(--line); stroke-width: 1; }
/* Размер задан в единицах viewBox, а он ужимается под ширину экрана: при 364
   единицах на 375 px масштаб 0.85, на iPhone SE — 0.70. Прежние 11 давали
   9.3 и 7.7 реальных пикселя, то есть подписи осей были нечитаемы почти на
   всех iPhone. 14 единиц — это ~12 px на обычном экране и ~10 на самом узком. */
.haxis-label { fill: var(--muted); font-size: 14px; text-anchor: middle; }
/* Крайние подписи прижимаются к своему краю, иначе центрированная первая метка
   уезжает за левую границу SVG и обрезается — от «2 май» оставалось «лай». */
.haxis-label.at-start { text-anchor: start; }
.haxis-label.at-end { text-anchor: end; }

.hgoal {
  stroke: var(--muted);
  stroke-width: 1;
  stroke-dasharray: 3 3;
}
/* Обводка цветом карточки: подпись читается и когда за ней столбик. */
.hgoal-label {
  fill: var(--muted);
  /* Тоже в единицах viewBox — см. .haxis-label: 11 давало ~9 px на экране. */
  font-size: 14px;
  paint-order: stroke;
  stroke: var(--surface);
  stroke-width: 2.5px;
  stroke-linejoin: round;
}

.chart-legend { margin: 8px 0 0; font-size: var(--fs-xs); }

/* --- «История»: неделя вперёд (Э6) ---------------------------------------- */
/* Строка покрытия стоит НАД советами и обычным размером текста, а не подписью
   внизу карточки: «не ели овощей» и «не записали ужины» дают одинаковую картину
   и противоположные советы, и оговорку под правдоподобным текстом не читают. */

.week-cov { margin: 0 0 12px; font-size: var(--fs-m); color: var(--text); }

.week-tips { list-style: none; margin: 0 0 14px; padding: 0; }
.week-tip + .week-tip { margin-top: 12px; }
.week-tip-text { margin: 0; font-size: var(--fs-l); }

/* Основание совета — не украшение: показывается всегда, потому что совет без
   опоры на реальную позицию расчёта до экрана не доходит вовсе, и видно это
   должно быть глазами. */
.week-tip-basis { margin: 3px 0 0; font-size: var(--fs-xs); color: var(--muted); }

.week-status { margin: 0 0 12px; font-size: var(--fs-m); color: var(--muted); }
.week-error { margin: 0 0 6px; font-size: var(--fs-m); color: var(--danger); }

/* Посчитанные цифры: их видно независимо от советов, и если совет разойдётся
   с ними, разойдётся он на виду. */
.week-groups { list-style: none; margin: 0 0 12px; padding: 0; }

.week-group {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 5px 0;
  font-size: var(--fs-s);
}
.week-group + .week-group { border-top: 1px solid var(--line); }
.week-group-name { color: var(--muted); }
.week-group-val { text-align: right; }

/* «Ни разу» намеренно НЕ красится акцентом. В этом приложении акцентный зелёный
   значит «уложились в цель» (кольцо ккал, клетки календаря, полосы БЖУ), и тот
   же цвет на отсутствующей группе читался бы как «здесь всё хорошо» — ровно
   наоборот. Плюс пока карта групп не заполнена, «ни разу» стоит почти в каждой
   строке, и выделение перестаёт что-либо значить. */
.week-group.is-missing .week-group-val { color: var(--text); }

/* «Без группы» — состояние нашей карты, а не рациона: приглушено, чтобы не
   читалось как ещё одна пустая группа. */
.week-group.is-unknown .week-group-val { color: var(--muted); }

/* --- «История»: календарь ------------------------------------------------- */
/* Выбирать дату столбиками в 2 px неудобно, поэтому под графиком месячная сетка. */

.cal-nav { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.cal-month { flex: 1; margin: 0; text-align: center; font-size: var(--fs-l); font-weight: 600; }
.cal-month::first-letter { text-transform: uppercase; }

.cal-arrow {
  flex: none;
  min-width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--surface);
  color: var(--text);
  font-size: var(--fs-title);
  line-height: 1;
  cursor: pointer;
}
/* Край истории: дальше в будущее или до первой записи листать нечего. */
.cal-arrow:disabled { opacity: .3; cursor: default; }

.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.cal-wd {
  padding-bottom: 2px;
  text-align: center;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--muted);
}

.cal-day {
  /* Якорь для точки-признака у дней с перебором (см. .cal-day.is-over::after). */
  position: relative;
  aspect-ratio: 1;
  /* На 320 px клетка по aspect-ratio выходила 33 px — по сетке из семи колонок
     промахнуться легко. Нижняя граница держит её в пределах нормы. */
  min-height: 42px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  /* Клетка без данных чуть темнее карточки: иначе сетки не видно вовсе. */
  background: color-mix(in srgb, var(--bg) 55%, var(--surface));
  color: var(--muted);
  font-size: var(--fs-s);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}

/* «Уложились» против «перебора» кодировалось ОДНИМ ТОЛЬКО тоном: замеры дают
   разницу по светлоте в 1,14 раза у фона и 1,21 у цифры при одинаковом весе
   шрифта, форме и размере. То есть различие ровно в паре красный/зелёный —
   именно той, которую не разбирает самый частый тип дальтонизма (~8% мужчин).
   Для такого человека календарь — сетка одинаковых пастельных квадратов, а
   легенда под ней («зелёная — уложились, красная — превышение») бесполезна.

   Второй признак — точка под числом: она есть только у дней с перебором и от
   цвета не зависит. Заодно разводим светлоту заливок, чтобы «в норме» читалось
   светлее «перебора» даже в оттенках серого. */
.cal-day.is-goal {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
  color: var(--accent);
  font-weight: 650;
}
.cal-day.is-over {
  background: color-mix(in srgb, var(--danger) 26%, var(--surface));
  border-color: color-mix(in srgb, var(--danger) 60%, var(--line));
  color: var(--danger);
  font-weight: 650;
}
.cal-day.is-over::after {
  content: '';
  position: absolute;
  bottom: 5px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: currentColor;
}
/* Цель по ккал выключена: «в норме / перебор» сравнивать не с чем, и клетка
   говорит только «здесь есть записи». */
.cal-day.is-data {
  background: color-mix(in srgb, var(--text) 10%, var(--surface));
  color: var(--text);
  font-weight: 650;
}
/* Будущее данными не наполнится — клетка держит сетку, но нажать её нельзя. */
.cal-day.is-future { opacity: .3; cursor: default; }

/* Сегодня — кольцо внутри клетки, выбор — рамка снаружи: два состояния на одной
   клетке не должны перекрывать друг друга. */
.cal-day.is-today { box-shadow: inset 0 0 0 2px var(--muted); }
.cal-day.is-sel { outline: 2px solid var(--text); outline-offset: 1px; }

/* --- «История»: сводка --------------------------------------------------- */

.stats-wrap { overflow-x: auto; }

.stats {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}
.stats th, .stats td {
  padding: 7px 0;
  text-align: right;
  white-space: nowrap;
}
.stats thead th {
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--muted);
  border-bottom: 1px solid var(--line);
}
.stats tbody tr + tr th, .stats tbody tr + tr td { border-top: 1px solid var(--line); }
.stats tbody th {
  text-align: left;
  font-size: var(--fs-s);
  font-weight: 550;
}
.stats thead th:first-child { text-align: left; }
.stats td { font-size: var(--fs-m); }
.stats td:last-child, .stats thead th:last-child { font-size: var(--fs-s); }
.stats .is-over { color: var(--danger); }

/* --- «История»: выбранный день ------------------------------------------- */

.day-head { display: flex; flex-direction: column; gap: 2px; margin-bottom: 6px; }
.day-date { margin: 0; font-size: var(--fs-s); color: var(--muted); }
.day-date::first-letter { text-transform: uppercase; }
.day-kcal { margin: 0; font-variant-numeric: tabular-nums; }
.day-kcal strong { font-size: var(--fs-number); font-weight: 700; letter-spacing: -0.02em; }
.day-kcal .muted { font-size: var(--fs-s); }
.day-delta { font-size: var(--fs-xs); color: var(--accent); font-weight: 600; }
.day-kcal.is-over strong, .day-kcal.is-over .day-delta { color: var(--danger); }
.day-macros { margin: 0; font-size: var(--fs-s); color: var(--muted); font-variant-numeric: tabular-nums; }

.empty-big { margin: 0; font-size: var(--fs-l); }

/* --- кнопка «+» ----------------------------------------------------------- */

.fab-space { height: 68px; flex: none; }

.fab {
  position: fixed;
  z-index: 3;
  /* На широком экране кнопка держится правого края КОЛОНКИ, а не вьюпорта:
     иначе она отрывается от списка, к которому относится. */
  right: max(var(--pad), env(safe-area-inset-right), calc(50vw - var(--col) / 2 + var(--pad)));
  bottom: calc(66px + env(safe-area-inset-bottom));
  width: 56px;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 50%;
  background: var(--accent);
  color: var(--surface);
  box-shadow: 0 6px 18px rgba(16, 18, 29, .2);
  cursor: pointer;
}
.fab svg { width: 26px; height: 26px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; }
.fab:active { transform: scale(.94); }
@media (prefers-color-scheme: dark) {
  .fab { color: #08130e; box-shadow: 0 6px 18px rgba(0, 0, 0, .45); }
}

/* Подсветка нажатия гасится у всего интерактивного сразу, а не у половины:
   раньше её гасили шесть селекторов из двенадцати, а .pick-item, .btn-primary,
   .btn-ghost, .btn-danger, .btn-small и .ditem-drop мигали синим системным
   прямоугольником. Вперемешку это читается как сбой отрисовки. Обратная связь
   на нажатие даётся через :active.

   Правило общее — значит поэлементных повторов быть не должно: повтор нельзя
   отличить от нужного объявления, и правку общего правила он молча переживёт.
   Единственное исключение — .hchart: это <svg tabindex="0">, ни кнопка, ни
   ссылка, и сюда он не попадает. */
button, a, .pick-item { -webkit-tap-highlight-color: transparent; }

/* А вот сам отклик на нажатие общим не был: гашение подсветки сделали для всех,
   а :active добавили восьми селекторам из полутора десятков. Остальные —
   .btn-primary, .btn-ghost, .btn-danger, .btn-small, .pick-item, .ditem-drop и
   все три вкладки — не реагировали на палец ничем: подсветку у них сняли, а
   замену не дали. Среди немых была главная кнопка каждого листа («Добавить»,
   «Сохранить»). Правило здесь такое же общее, как и гашение. */
button:active, a:active, .pick-item:active { opacity: .55; }
/* У кнопки-действия своя обратная связь (масштаб), гасить её ещё и
   прозрачностью не нужно — иначе нажатие выглядит как отключение. */
.fab:active { opacity: 1; }

/* Курсор есть на iPad с трекпадом и на десктопе, а :hover не был описан нигде:
   ни один элемент не отзывался на наведение. Эффект намеренно слабее нажатия. */
@media (hover: hover) and (pointer: fine) {
  button:hover, a:hover, .pick-item:hover { opacity: .82; }
  .fab:hover { opacity: 1; filter: brightness(1.06); }
}

/* --- формы --------------------------------------------------------------- */

.field { border: 0; margin: 0; padding: 0; min-inline-size: 0; }
.field > label:not(.check), .field > legend {
  display: block;
  padding: 0;
  margin-bottom: 6px;
  font-size: var(--fs-s);
  font-weight: 600;
  color: var(--muted);
}

/* Поля ввода задают размер и цвет ЯВНО, а не наследуют.

   Размер: Safari на iOS зумит вьюпорт при фокусе на поле мельче 16 px и обратно
   не откатывает — шапка и таб-бар уезжают за экран. С `font: inherit` числовые
   поля брали размер у родителя `.inline` (13 px), то есть зумило при каждой
   правке граммов — на основном пути приложения.

   Цвет: `.inline` красит подписи в --muted, и введённые данные наследовали его
   же. Собственные цифры пользователя не должны быть бледнее подписей к ним
   (в светлой теме это давало контраст 4,43:1 — ниже порога 4,5:1).

   Кнопки наследуют по-прежнему: у них свои размеры по месту. */
input, select, textarea {
  font-family: inherit;
  font-size: 16px;
  color: var(--text);
}

/* Кнопки берут шрифт и цвет у страницы: собственные у них системные. Правило
   общее, поэтому в отдельных кнопках повторять его не нужно — там, где кнопке
   нужен свой размер, стоит один font-size поверх, а не второй font: inherit. */
button { font: inherit; color: inherit; }

input[type="password"], input[type="text"], input[type="number"], input[type="search"], select {
  width: 100%;
  /* Норма HIG 44 px касается всего, во что целятся пальцем, — поле ввода не
     исключение. Отбивка и шрифт давали 41 px: промах по граммам открывает
     соседнее поле, а на основном пути приложения это происходит каждый раз.
     Именно поэтому здесь min-height, а не увеличенная отбивка: текст в поле
     браузер центрирует сам, и высота растёт симметрично. */
  min-height: 44px;
  padding: 10px 12px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  appearance: none;
}
/* Ссылки здесь тоже: «Открыть настройки →» из ошибки распознавания получала
   бледное системное кольцо вместо фирменного. */
input:focus-visible, select:focus-visible, button:focus-visible,
.tab:focus-visible, a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Рамка выбранного дня и кольцо фокуса рисуются одним и тем же свойством, а
   .cal-day.is-sel (0,2,0) специфичнее, чем button:focus-visible (0,1,1). Пока
   этого правила не было, клавиатурный фокус на выбранной дате был неотличим
   от самого выбора: человек не видел, где он находится. */
.cal-day.is-sel:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.inline { display: flex; flex-direction: column; gap: 4px; font-size: var(--fs-s); color: var(--muted); }
.inline input { font-variant-numeric: tabular-nums; }

.check {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  margin-top: 14px;
  font-size: var(--fs-m);
  font-weight: 400;
  color: var(--text);
}
.check input {
  width: 20px;
  height: 20px;
  margin: 1px 0 0;
  accent-color: var(--accent);
  flex: none;
}
.check .hint { margin: 2px 0 0; }

.btn-primary {
  width: 100%;
  padding: 12px;
  border: 0;
  border-radius: var(--radius-s);
  background: var(--accent);
  color: var(--surface);
  font-weight: 600;
  cursor: pointer;
}
@media (prefers-color-scheme: dark) {
  .btn-primary { color: #08130e; }
}

.btn-ghost {
  width: 100%;
  padding: 12px;
  border: 0;
  border-radius: var(--radius-s);
  background: none;
  color: var(--muted);
  font-weight: 550;
  cursor: pointer;
}

.btn-danger {
  width: 100%;
  padding: 12px;
  border: 1px solid color-mix(in srgb, var(--danger) 40%, transparent);
  border-radius: var(--radius-s);
  background: none;
  color: var(--danger);
  font-weight: 600;
  cursor: pointer;
}
/* Первый тап взводит кнопку, второй удаляет: без нативного confirm(). */
.btn-danger.is-armed { background: var(--danger); color: var(--surface); border-color: var(--danger); }
@media (prefers-color-scheme: dark) {
  .btn-danger.is-armed { color: #1a0605; }
}

/* --- нижние листы (добавление и правка) ----------------------------------- */

.sheet {
  position: fixed;
  inset: auto 0 0 0;
  width: 100%;
  max-width: 520px;
  max-height: 88dvh;
  margin: 0 auto;
  overflow-y: auto;
  border: 0;
  border-radius: var(--radius-l) var(--radius-l) 0 0;
  background: var(--surface);
  color: var(--text);
  /* Боковые отступы через safe-area: в ландшафте на iPhone с вырезом
     inset-left доходит до 47 px, и поля с кнопками уезжали под него.
     У .screen и .fab это уже сделано — лист был единственным исключением. */
  padding: 18px var(--pad) calc(18px + env(safe-area-inset-bottom));
  padding-left: max(var(--pad), env(safe-area-inset-left));
  padding-right: max(var(--pad), env(safe-area-inset-right));
  box-shadow: 0 -8px 30px rgba(16, 18, 29, .2);
  /* Лист прокручивается сам; без contain доскролл до края уходит в резину
     всего вебвью, и страница под модальным окном начинает ездить. */
  overscroll-behavior: contain;
}

/* Тёмная тема: тёмная тень на тёмном фоне не отрывает лист от страницы.
   У .card и .fab перекрытие уже было, у листа — нет. */
@media (prefers-color-scheme: dark) {
  .sheet { box-shadow: 0 -8px 30px rgba(0, 0, 0, .55); }
}
.sheet::backdrop { background: rgba(8, 9, 12, .45); }
.sheet-title { margin: 0 0 14px; font-size: var(--fs-title); font-weight: 650; }
/* Приём, в который уйдёт новая позиция: заголовок «добавить позицию» сам по себе
   не говорит, в какой из приёмов дня. Имя приёма бывает длинным — перенос по словам. */
.form-into {
  margin: -8px 0 14px;
  font-size: var(--fs-s);
  color: var(--muted);
  overflow-wrap: anywhere;
}
/* Кнопки листа прилипают к его низу.
   Лист прокручивается сам (max-height: 88dvh), и форма правки давно переросла
   короткий экран: к граммам и макросам добавились время, пресеты и предупреждение
   о несвежих цифрах. На невысоком телефоне «Сохранить» оказывалась ниже кромки —
   а того, что лист вообще скроллится, ничем не показано, и главное действие
   просто пропадало. Прилипшая полоса решает это на всех высотах сразу и заодно
   убирает лишний скролл на длинном черновике по фото.

   Отрицательные поля — чтобы полоса перекрывала боковые отступы листа и
   уезжающий под неё текст не просвечивал сбоку. */
.sheet-actions {
  position: sticky;
  bottom: calc(-18px - env(safe-area-inset-bottom));
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 16px calc(-1 * var(--pad)) calc(-18px - env(safe-area-inset-bottom));
  padding: 12px var(--pad) calc(18px + env(safe-area-inset-bottom));
  background: var(--surface);
  border-top: 1px solid var(--line);
}

.field.mt { margin-top: 14px; }

.pick { display: flex; flex-direction: column; gap: 8px; }
.pick-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--bg);
  text-align: left;
  cursor: pointer;
}
.pick-item:disabled { opacity: .45; cursor: default; }
.pick-icon {
  flex: none;
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  color: var(--accent);
}
.pick-item:disabled .pick-icon { color: var(--muted); }
.pick-text { display: flex; flex-direction: column; }
.pick-label { font-size: var(--fs-l); font-weight: 550; }
.pick-note { font-size: var(--fs-xs); color: var(--muted); }

/* Почему фото недоступно — рядом с выключенными кнопками, а не после спиннера. */
.pick-offline {
  margin: 10px 0 0;
  padding: 10px 12px;
  border-radius: var(--radius-s);
  background: color-mix(in srgb, var(--danger) 10%, transparent);
  color: var(--danger);
  font-size: var(--fs-s);
  font-weight: 550;
}
.pick-offline:empty { display: none; }

/* --- библиотека продуктов -------------------------------------------------- */

.lib-search { margin-bottom: 4px; }

/* Список прокручивается внутри листа, а не тянет его на всю высоту экрана:
   кнопка «Отмена» должна оставаться на виду.
   Отступ справа — под полосу прокрутки: она рисуется поверх содержимого, и без
   него срезает как раз ккал, ради которых в список и заходят. */
.lib { max-height: 46dvh; overflow-y: auto; padding-right: 8px; }

.lib-group + .lib-group { margin-top: 14px; }
.lib-group-title {
  margin: 12px 0 2px;
  font-size: var(--fs-s);
  font-weight: 600;
  color: var(--muted);
}
.lib-empty { margin: 14px 0; font-size: var(--fs-m); color: var(--muted); }

/* Хвост, не поместившийся в группы. Отделён линией — это не строка списка,
   а сообщение о том, что список кончился раньше продуктов. */
.lib-more {
  margin: 0;
  padding-top: 10px;
  border-top: 1px solid var(--line);
  font-size: var(--fs-s);
  color: var(--muted);
}

/* --- черновик по фото ----------------------------------------------------- */

.draft-head { display: flex; gap: 12px; align-items: center; margin-bottom: 14px; }

.draft-photo {
  flex: none;
  width: 84px;
  height: 84px;
  border-radius: var(--radius-s);
  object-fit: cover;
  background: var(--bg);
  border: 1px solid var(--line);
}
.draft-photo:not([src]) { visibility: hidden; }

.draft-status { margin: 0; font-size: var(--fs-s); color: var(--muted); }

/* Спиннер только у занятого состояния: в покое строка — обычная подпись. */
.draft-status.is-busy { display: flex; align-items: center; gap: 8px; color: var(--text); }
.draft-status.is-busy::before {
  content: '';
  flex: none;
  width: 15px;
  height: 15px;
  border: 2px solid var(--line);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .draft-status.is-busy::before { animation-duration: 2.4s; }
}

.draft-error {
  padding: 12px;
  border: 1px solid color-mix(in srgb, var(--danger) 35%, transparent);
  border-radius: var(--radius-s);
  background: color-mix(in srgb, var(--danger) 8%, transparent);
}
.draft-error-msg { margin: 0; font-size: var(--fs-m); font-weight: 550; color: var(--danger); }
.draft-error-detail {
  margin: 6px 0 0;
  font-size: var(--fs-xs);
  color: var(--muted);
  overflow-wrap: anywhere;
}
.draft-error-hint { margin: 8px 0 0; font-size: var(--fs-xs); color: var(--muted); }
.draft-link {
  display: inline-block;
  margin-top: 8px;
  color: var(--accent);
  font-size: var(--fs-m);
  font-weight: 600;
  text-decoration: none;
}

.draft-note { margin: 10px 0 0; font-size: var(--fs-s); color: var(--muted); }
.draft-note:empty { display: none; }
.draft-empty { margin: 0; font-size: var(--fs-m); }

.ditem {
  margin: 0 0 10px;
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  min-inline-size: 0;
}
.ditem.is-off { opacity: .45; }
.ditem-head { display: flex; gap: 8px; align-items: center; }
.ditem-name { flex: 1; min-width: 0; font-weight: 550; }
.ditem-drop {
  flex: none;
  min-height: 44px;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: none;
  color: var(--muted);
  font-size: var(--fs-s);
  cursor: pointer;
}
.ditem-calc {
  margin: 12px 0 0;
  padding: 8px 10px;
  border-radius: var(--radius-s);
  background: var(--accent-soft);
  font-size: var(--fs-s);
  font-variant-numeric: tabular-nums;
}
.ditem-conf { margin: 8px 0 0; font-size: var(--fs-xs); color: var(--muted); }
/* Низкая уверенность — не украшение: модель плохо угадывает вес, это предупреждение. */
.ditem-conf.is-low { color: var(--danger); font-weight: 550; }

/* --- пересчёт по описанию (M10) ------------------------------------------- */

/* Цифры остались от прежнего описания. Сохранять не мешаем — правка могла быть
   косметической, — но и молчать нельзя: именно из-за молчания жил баг. Поэтому
   жёлтое предупреждение, а не красная ошибка. */
.recalc-warn {
  margin: 10px 0 0;
  padding: 8px 10px;
  border: 1px solid color-mix(in srgb, var(--warn) 40%, transparent);
  border-radius: var(--radius-s);
  background: color-mix(in srgb, var(--warn) 10%, transparent);
  color: var(--warn);
  font-size: var(--fs-xs);
  font-weight: 550;
}

.recalc { display: flex; flex-direction: column; gap: 6px; margin-top: 12px; }
.recalc .btn-small { width: 100%; }
.recalc-status { margin: 0; font-size: var(--fs-xs); color: var(--muted); }
.recalc-status:empty { display: none; }
.recalc-status.is-error { color: var(--danger); font-weight: 550; }

/* Пометка узнавания: цифры в полях ниже подставлены из библиотеки, а не
   посчитаны моделью. Это не подпись, а предупреждение о подмене — потому
   акцентная плашка, а не серый мелкий текст: не заметить её и есть тот самый
   риск, ради которого пометка заводилась. Отменённое узнавание гасим до
   обычного текста — подмены больше нет, предупреждать не о чем. */
.ditem-match {
  display: flex;
  gap: 8px;
  align-items: center;
  margin: 10px 0 0;
  padding: 6px 6px 6px 10px;
  border-radius: var(--radius-s);
  background: var(--accent-soft);
  font-size: var(--fs-s);
}
.ditem-match.is-off { background: none; padding-left: 0; color: var(--muted); }
.ditem-match-text { flex: 1; min-width: 0; }
.ditem-unmatch {
  flex: none;
  min-height: 44px;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--surface);
  color: var(--text);
  font-size: var(--fs-s);
  font-weight: 550;
  cursor: pointer;
}

/* --- прочее --------------------------------------------------------------- */

.row { display: flex; gap: 8px; align-items: center; }
.row > input { flex: 1; min-width: 0; }
.row-even > .btn-small { flex: 1; }

.btn-small {
  flex: none;
  min-height: 44px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--bg);
  font-size: var(--fs-m);
  font-weight: 550;
  cursor: pointer;
}
.btn-small:disabled { opacity: .5; cursor: default; }

.status.is-error { color: var(--danger); }

/* Нейтральный фон, а не акцентный: это превью подсчёта, и половину времени в
   нём стоит «Итог посчитается, когда заданы граммы и ккал» — подсказка, которую
   зелёный цвет успеха выдавал за достижение. */
.calc {
  margin: 14px 0 0;
  padding: 10px 12px;
  border-radius: var(--radius-s);
  background: color-mix(in srgb, var(--text) 6%, var(--surface));
  font-size: var(--fs-s);
  font-variant-numeric: tabular-nums;
}
.form-error { margin: 8px 0 0; color: var(--danger); font-size: var(--fs-s); }
.form-error:empty { display: none; }

/* --- таб-бар -------------------------------------------------------------- */

.tabbar {
  display: flex;
  border-top: 1px solid var(--line);
  background: var(--surface);
  padding-bottom: env(safe-area-inset-bottom);
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}

.tab {
  /* Якорь для полоски активной вкладки (см. .tab.is-active::before). */
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 8px 4px 6px;
  color: var(--muted);
  text-decoration: none;
}
.tab-icon {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
}
.tab-label { font-size: var(--fs-xs); font-weight: 500; }

/* Активная вкладка отличалась ТОЛЬКО тоном: #10794f против #6b7280 — светлота
   различается в 1,12 раза, вес шрифта одинаковый, иконка та же обводка без
   заливки. При красно-зелёной слепоте (и просто на солнце) активная вкладка
   неотличима от соседних. Два признака, не зависящих от цвета: вес подписи и
   полоска над иконкой. */
.tab.is-active { color: var(--accent); }
.tab.is-active .tab-label { font-weight: 700; }
.tab.is-active .tab-icon { stroke-width: 2.1; }
.tab.is-active::before {
  content: '';
  position: absolute;
  top: 0;
  width: 26px;
  height: 2px;
  border-radius: 0 0 2px 2px;
  background: currentColor;
}

/* Селектор выглядит селектором.
   appearance: none снимает системную стрелку, и поле «Модель» было неотличимо
   от соседнего текстового «Ключ Gemini API»: что модель можно сменить, узнать
   было неоткуда. Стрелка нарисована рамками — картинку из data:-URI CSP не
   пропустит (img-src 'self' blob:). */
.select-wrap { position: relative; }
.select-wrap > select { padding-right: 38px; }
.select-wrap::after {
  content: '';
  position: absolute;
  right: 15px;
  top: 50%;
  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;
}

/* Программный фокус на заголовке — приём для скринридера: после смены вкладки
   он называет открытый экран. Кольцо при этом рисовал браузер своим дефолтным
   оранжевым, вокруг всей строки и всем видимое — жирная рамка вокруг «История»
   читается как ошибка ввода. Скрываем именно рамку, а не фокусируемость. */
.app-header h1:focus { outline: none; }

/* Полоса подтверждения импорта: своё действие своей кнопкой рядом со своим
   описанием. Подмена самой кнопки «Импорт JSON» на «Точно применить?» создала
   бы другую ловушку — выбрать другой файл станет нечем, и выйти из состояния
   можно будет только уходом с экрана. */
.confirm {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 12px;
  padding: 8px 12px;
  border: 1px solid color-mix(in srgb, var(--warn) 40%, var(--line));
  border-radius: var(--radius-s);
  background: color-mix(in srgb, var(--warn) 10%, transparent);
}
.confirm-text {
  flex: 1;
  min-width: 0;
  font-size: var(--fs-s);
  color: var(--text);
  overflow-wrap: anywhere;
}
.confirm .btn-small { flex: none; }
/* Применение перезаписывает дневник — кнопка выглядит соответственно. */
.btn-confirm {
  border-color: color-mix(in srgb, var(--danger) 55%, var(--line));
  color: var(--danger);
  font-weight: 650;
}

/* Полоса отмены удаления. Взвод защищает от случайного касания, но не от
   «удалил не ту карточку»: после подтверждения записи не было уже нигде, хотя
   живая строка её только что назвала. */
.undo {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 12px;
  padding: 8px 12px;
  border: 1px solid color-mix(in srgb, var(--warn) 40%, var(--line));
  border-radius: var(--radius-s);
  background: color-mix(in srgb, var(--warn) 10%, transparent);
}
.undo-text {
  flex: 1;
  min-width: 0;
  font-size: var(--fs-s);
  color: var(--text);
  overflow-wrap: anywhere;
}
.undo .btn-small { flex: none; }

/* Ландшафт: высоты мало, ширины много.
   Кольцо занимало большую часть экрана, и список приёмов начинался за краем;
   таб-бар при этом держал ту же высоту, что и в портрете. */
@media (orientation: landscape) and (max-height: 460px) {
  .ring-row { gap: 12px; }
  .ring { width: 108px; height: 108px; }
  .ring-center strong { font-size: var(--fs-title); }
  .card { padding: 12px var(--pad); }

  /* Таб-бар стал ниже — кнопка опускается вслед за ним и слегка уменьшается:
     иначе она висит посреди экрана и закрывает первую карточку целиком.
     48 px всё ещё выше минимальной цели касания. */
  .fab { bottom: calc(48px + env(safe-area-inset-bottom)); width: 48px; height: 48px; }
  .fab svg { width: 22px; height: 22px; }

  .tab { flex-direction: row; gap: 6px; padding: 6px 4px; }
  .tab-icon { width: 18px; height: 18px; }
  .tab.is-active::before { display: none; }
}

/* Листы выезжают снизу, а не возникают на месте.
   Движения в приложении не было вовсе, и нижний лист появлялся мгновенно —
   вместе с ним пропадала пространственная метафора, ради которой «Отмена» стоит
   внизу, а лист вообще называется нижним. */
@keyframes sheet-in {
  from { transform: translateY(12%); opacity: .6; }
  to { transform: translateY(0); opacity: 1; }
}
.sheet[open] { animation: sheet-in .16s ease-out; }

@media (prefers-reduced-motion: reduce) {
  .sheet[open] { animation: none; }
}

/* Экран читает базу дольше пары кадров: до этого в <main> было пусто. */
.screen-loading {
  margin: 0;
  padding: 20px 0;
  text-align: center;
  color: var(--muted);
  font-size: var(--fs-s);
}
