/* Оформление поверх Tailwind: типографика, тени, движение.

   Сборки нет, поэтому правила цепляются к тем же классам, что стоят в
   разметке (`.bg-white.rounded-xl` и т. п.) — так вид меняется сразу на
   всех страницах, включая блоки, которые собираются строками в JS.

   Порядок подключения: theme.css → ui.css. В theme.css цвета тёмной темы
   помечены !important, поэтому здесь их не трогаем: тут только форма,
   отступы и движение. Цвет — в theme.css, движение — тут.

   Всё движение выключается при `prefers-reduced-motion` в конце файла. */

/* ------------------------------------------------------------ типографика */

:root {
  --ui-ease: cubic-bezier(0.32, 0.72, 0, 1);
  --ui-shadow: 0 1px 2px rgba(15, 23, 42, .04), 0 8px 24px -12px rgba(15, 23, 42, .18);
  --ui-shadow-lift: 0 2px 4px rgba(15, 23, 42, .06), 0 16px 32px -16px rgba(15, 23, 42, .28);
  --ui-radius: 0.875rem;
}
html.dark {
  --ui-shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 8px 24px -12px rgba(0, 0, 0, .6);
  --ui-shadow-lift: 0 2px 6px rgba(0, 0, 0, .5), 0 18px 36px -16px rgba(0, 0, 0, .75);
}

body {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-family: "Segoe UI", system-ui, -apple-system, "Inter", Roboto,
    "Helvetica Neue", Arial, sans-serif;
}

/* iPhone: два быстрых нажатия (стрелки листания, чипы дней) — жест «увеличить»; manipulation его снимает,
   щипок двумя пальцами остаётся. На каждом нажимаемом, а не только на html: значение не проходит
   внутрь прокручиваемых окон (окна расписания — overflow-y: auto) */
html, button, a, label, summary, select, input, textarea, [role="button"], [data-cwgo] { touch-action: manipulation; }

h1 { letter-spacing: -0.02em; }
h2, h3 { letter-spacing: -0.011em; }

/* цифры в счётчиках не должны прыгать при пересчёте */
.tabular-nums, td, th, input[type="date"] { font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------- фон ----- */

/* Фон страницы плоский, как в расписании: раньше сверху было индиго-пятно,
   и две части одного сайта отличались уже цветом подложки. */

/* ------------------------------------------------------------- карточки -- */

.bg-white.rounded-xl,
.bg-white.rounded-2xl {
  box-shadow: var(--ui-shadow);
  transition: box-shadow .22s var(--ui-ease), transform .22s var(--ui-ease);
}

/* заголовок карточки: чуть плотнее и спокойнее по цвету */
.bg-white.rounded-xl > h2 {
  letter-spacing: -0.011em;
}

/* -------------------------------------------------------------- кнопки --- */

button, .tab, a[href][class*="rounded"] {
  transition: background-color .18s var(--ui-ease), border-color .18s var(--ui-ease),
              color .18s var(--ui-ease), box-shadow .18s var(--ui-ease),
              transform .12s var(--ui-ease), opacity .18s var(--ui-ease);
}
button:active:not(:disabled) { transform: scale(0.975); }
button:disabled { cursor: not-allowed; }

/* видимый фокус с клавиатуры — раньше его не было нигде */
button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid #6366f1;
  outline-offset: 2px;
}
html.dark button:focus-visible,
html.dark a:focus-visible,
html.dark input:focus-visible,
html.dark select:focus-visible,
html.dark textarea:focus-visible { outline-color: #9ea5f7; }

/* главные кнопки действия приподнимаются, а не просто темнеют */
button.bg-slate-900:hover:not(:disabled),
button.bg-emerald-600:hover:not(:disabled),
button.bg-rose-600:hover:not(:disabled) {
  box-shadow: var(--ui-shadow-lift);
}

/* ------------------------------------------------------------ поля ввода - */

input, select, textarea {
  transition: border-color .18s var(--ui-ease), box-shadow .18s var(--ui-ease);
}
input:focus, select:focus, textarea:focus {
  box-shadow: 0 0 0 3px rgba(99, 102, 241, .15);
}

/* Поле даты рисует БРАУЗЕР, и рисует везде по-разному. Отличия Safari на
   iPhone видны только там — на десктопе этого не поймать:

   1. У него своя минимальная ширина по содержимому, и `w-full` её не
      перебивает: в сетке из двух колонок поля вылезали за свои клетки и
      наезжали друг на друга. `min-width: 0` разрешает им ужиматься.
   2. Значение внутри выровнено по центру, а у всех соседних полей текст
      слева — строка «01.09.2026» стояла не на своём месте. Правится только
      через ::-webkit-date-and-time-value.
   3. Пустое поле iOS подписывает «дд.сент.гггг», Chrome на Android —
      «дд.мм.гггг», то есть на четверть у́же. Формат родного контрола ни CSS,
      ни HTML не задаётся: это локаль системы.

   `appearance: none` снимает заодно и родную рамку iOS поверх нашей. */
input[type="date"] {
  -webkit-appearance: none;
  appearance: none;
  min-width: 0;
  box-sizing: border-box;
}
input[type="date"]::-webkit-date-and-time-value {
  text-align: left;
  /* без этого при `appearance: none` пустое поле схлопывается в полоску */
  min-height: 1.2em;
}

/* Клетка сетки с полем даты, которому не хватило места (см. fitDateInputs()
   в app.js: ширину запрашивают у самого браузера, а не гадают по платформе).
   Раздвигаем на всю строку ТОЛЬКО того, кому тесно: на Android и на десктопе
   подпись короче и пара дат прекрасно стоит в два столбца — незачем портить
   раскладку всем из-за одного браузера. */
[data-date-cell].date-cell-wide { grid-column: 1 / -1; }

/* ------------------------------------------------------------- таблицы --- */

tbody tr { transition: background-color .15s var(--ui-ease); }
tbody tr:hover { background-color: rgba(99, 102, 241, .045); }
html.dark tbody tr:hover { background-color: rgba(129, 140, 248, .08); }

/* шапка таблицы не должна уезжать под контент при липком заголовке */
thead th { letter-spacing: 0.04em; }

/* ------------------------------------------------------------ появление -- */

@keyframes ui-rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
@keyframes ui-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes ui-pop {
  from { opacity: 0; transform: translateY(16px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}

/* -------------------------------------------------------------- крутилки -- */

/* Общий спиннер loader() — раньше кольцо стояло неподвижно: класс `spin`
   вешался, а самой анимации нигде не было объявлено. */
@keyframes ui-spin { to { transform: rotate(360deg); } }
.spin { animation: ui-spin .8s linear infinite; }

/* Неопределённый прогресс — полоса без процентов, для запросов, у которых
   сервер не отдаёт прогресс по шагам (сборка бланка на всю школу и т.п.):
   бегущая подсветка говорит «идёт работа», не обещая конкретный процент. */
@keyframes ui-progress-slide {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(250%); }
}
.ui-progress-track {
  height: 4px; border-radius: 999px; overflow: hidden;
  background: #e5e7eb;
}
html.dark .ui-progress-track { background: #3a4358; }
.ui-progress-fill {
  width: 40%; height: 100%; border-radius: 999px;
  background: #4f46e5;
  animation: ui-progress-slide 1.1s ease-in-out infinite;
}
html.dark .ui-progress-fill { background: #9ea5f7; }

/* экраны учителя и панели админки въезжают при переключении */
.screen.active, .panel.active {
  animation: ui-rise .3s var(--ui-ease) both;
}

/* Список учеников появляется волной: строки въезжают по очереди.
   Задержку получают только первые восемь — иначе на классе в 30 человек
   последний ученик показался бы через секунду.

   Правило висит на классе `ui-enter` у контейнера, который снимается сразу
   после волны. Иначе оно остаётся на кнопке навсегда, и когда с неё сходит
   анимация отметки, строка заново въезжает снизу на ровном месте. */
#students.ui-enter > .student {
  animation: ui-rise .26s var(--ui-ease) both;
}
#students.ui-enter > .student:nth-child(2) { animation-delay: 20ms; }
#students.ui-enter > .student:nth-child(3) { animation-delay: 40ms; }
#students.ui-enter > .student:nth-child(4) { animation-delay: 60ms; }
#students.ui-enter > .student:nth-child(5) { animation-delay: 80ms; }
#students.ui-enter > .student:nth-child(6) { animation-delay: 100ms; }
#students.ui-enter > .student:nth-child(7) { animation-delay: 120ms; }
#students.ui-enter > .student:nth-child(n+8) { animation-delay: 140ms; }

/* карточки итогов у повара и завуча */
#totals > *, #today-stats > *, #s-cards > * {
  animation: ui-rise .3s var(--ui-ease) both;
}
#totals > *:nth-child(2), #today-stats > *:nth-child(2), #s-cards > *:nth-child(2) { animation-delay: 50ms; }
#totals > *:nth-child(3), #today-stats > *:nth-child(3), #s-cards > *:nth-child(3) { animation-delay: 100ms; }
#totals > *:nth-child(4), #today-stats > *:nth-child(4), #s-cards > *:nth-child(4) { animation-delay: 150ms; }

/* Плитки классов, букв и групп ГПД: своей тени в разметке у них нет,
   без неё сетка читается как серые пятна. Поштучной анимации появления
   тоже нет — сетка перерисовывается на каждом раскрытии, и это мигало бы. */
#grade-grid > button, .letters button {
  box-shadow: var(--ui-shadow);
  transition: transform .16s var(--ui-ease), box-shadow .16s var(--ui-ease);
}
#grade-grid > button:hover, .letters button:hover {
  transform: translateY(-2px);
  box-shadow: var(--ui-shadow-lift);
}
#grade-grid > button:active,
.letters button:active { transform: translateY(0) scale(0.97); }

/* ------------------------------------------------- ученик в списке ------- */

/* Нажатие на ученика — главное действие в системе, оно должно
   ощущаться. Тень и сдвиг вместо смены цвета фона. */
.student {
  transition: transform .16s var(--ui-ease), box-shadow .16s var(--ui-ease),
              border-color .16s var(--ui-ease), background-color .2s ease;
}
.student:hover { transform: translateX(2px); box-shadow: var(--ui-shadow); }

/* Палец уже на кнопке — вдавливаем сразу, без плавности: задержка здесь
   читается как «не нажалось». Отпускание, наоборот, плавное. */
.student:active {
  transform: scale(0.975);
  transition-duration: .04s;
}

/* Отметка сменилась: кнопка коротко пружинит и от неё расходится волна
   своего цвета. Раньше список перерисовывался целиком, и анимировать было
   нечего — узел исчезал в тот же момент; теперь кнопка та же самая. */
@keyframes ui-mark-pop {
  0%   { transform: scale(0.975); }
  45%  { transform: scale(1.015); }
  100% { transform: scale(1); }
}
@keyframes ui-mark-ring {
  from { box-shadow: 0 0 0 0 rgba(var(--mark-flash), .5); }
  to   { box-shadow: 0 0 0 12px rgba(var(--mark-flash), 0); }
}
@keyframes ui-badge-in {
  from { transform: scale(0.2) rotate(-120deg); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
@keyframes ui-label-in {
  from { opacity: 0; transform: translateX(8px); }
  to   { opacity: 1; transform: none; }
}

/* Селектор с `#students`: у правила появления списка выше та же
   специфичность, и спор решает порядок — эти строки должны быть ниже. */
#students > .student.ui-flip {
  --mark-flash: 16 185 129;
  animation: ui-mark-pop .3s var(--ui-ease), ui-mark-ring .5s ease-out;
}
#students > .student.ui-flip.border-rose-300 { --mark-flash: 244 63 94; }

/* Кружок со знаком и подпись причины — не просто меняются, а приходят:
   глаз ловит движение и видит, что именно поменялось. */
#students > .student.ui-flip .rounded-full {
  animation: ui-badge-in .34s var(--ui-ease) both;
}
#students > .student.ui-flip .whitespace-nowrap {
  animation: ui-label-in .3s var(--ui-ease) .04s both;
}

/* Долгое нажатие: кнопка медленно проседает, пока держат палец. Видно,
   что что-то происходит и сколько ещё держать. */
@keyframes ui-hold-press {
  from { transform: none; }
  to   { transform: scale(0.955); }
}
#students > .student.ui-hold {
  animation: ui-hold-press .45s var(--ui-ease) forwards;   /* = HOLD_MS в teacher.html */
}

/* Без этого удержание выделяет фамилию и поднимает системное меню. */
.student {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

/* Счётчик присутствующих подпрыгивает вслед за отметкой — иначе цифра
   меняется незаметно, а это единственная сводка на экране. */
@keyframes ui-count-tick {
  0%   { transform: none; }
  40%  { transform: translateY(-3px) scale(1.06); }
  100% { transform: none; }
}
#counter-text {
  display: inline-block;
  animation: ui-count-tick .3s var(--ui-ease);
}
#counter-text.hidden { display: none; }       /* id сильнее .hidden: без этого счёт торчал и вне списка класса */

/* Короткая подсветка блока, к которому только что прокрутили по клику на
   плитку — само содержимое уже есть на странице, это просто указатель.
   Общая для повара (клик по «Диета» → список ниже) и завуча (клик по смене
   на «Сегодня» → её карточки классов): один и тот же приём, один класс.

   Именно анимацией, а не transition на outline-color: пока класс висит,
   свойство не меняется, а снятие класса убирает outline целиком — подсветка
   просто мигала бы вместо плавного затухания. Цвет один на обе темы:
   sky — не участвует в семантике «присутствует/отсутствует/ждём», поэтому
   не спорит ни с одной палитрой строк. */
.scroll-flash { animation: ui-scroll-flash 1.6s ease-out both; }
@keyframes ui-scroll-flash {
  from { outline: 3px solid #38bdf8; outline-offset: 2px; }
  60%  { outline: 3px solid #38bdf8; outline-offset: 2px; }
  to   { outline: 3px solid rgba(56, 189, 248, 0); outline-offset: 2px; }
}

/* ---------------------------------------------------------- модалки ------ */

/* backdrop и окно: затемнение проявляется, окно выезжает снизу */
.fixed.inset-0.bg-black\/40 { animation: ui-fade .18s ease both; }
.fixed.inset-0.bg-black\/40 > * { animation: ui-pop .26s var(--ui-ease) both; }
.fixed.inset-0.bg-black\/40 > .bg-white {
  box-shadow: 0 24px 64px -16px rgba(15, 23, 42, .45);
}

/* загрузка: подложка не мигает, а проявляется */
#loader { animation: ui-fade .15s ease both; backdrop-filter: blur(2px); }

/* тост выезжает снизу */
#toast:not(.hidden) { animation: ui-pop .28s var(--ui-ease) both; }

/* --------------------------------------------------------- липкие шапки -- */

/* Полупрозрачный фон с размытием: под шапкой видно, что список продолжается.
   Цвет берём из темы — здесь только прозрачность и размытие. */
header.sticky, nav.sticky, #submit-bar.sticky {
  backdrop-filter: saturate(180%) blur(12px);
}
@supports (backdrop-filter: blur(12px)) {
  header.sticky.bg-white, #submit-bar.sticky.bg-white {
    background-color: rgba(255, 255, 255, .82);
  }
  html.dark header.sticky.bg-white,
  html.dark #submit-bar.sticky.bg-white {
    background-color: rgba(22, 26, 35, .82) !important;
  }
}

/* Липкие полосы: вкладки админки и шапка повара. Страница внутри
   контейнера с отступами, поэтому полоса растягивается отрицательными
   полями — иначе под ней просвечивал бы контент по краям. */
.tabbar, .pagebar {
  margin-inline: -1rem;
  padding-inline: 1rem;
  background-color: rgba(245, 246, 248, .85);
  backdrop-filter: saturate(180%) blur(12px);
  /* граница снизу отделяет полосу от контента, который под неё уезжает */
  box-shadow: 0 1px 0 rgba(15, 23, 42, .07);
}
html.dark .tabbar, html.dark .pagebar {
  background-color: rgba(14, 17, 23, .85);
  box-shadow: 0 1px 0 rgba(232, 235, 242, .08);
}
@media (min-width: 640px) {
  .tabbar, .pagebar { margin-inline: -2rem; padding-inline: 2rem; }
}

/* На телефоне полоса вкладок прокручивается вбок (см. admin.html): своя
   полоса прокрутки под ней была бы третьей горизонтальной линией подряд —
   прячем её, жест остаётся. Но без полосы прокрутки ничего и не намекает,
   что вкладок больше, чем видно, — десять пунктов в одну строку не влезают.
   Тень по краям — классический приём без JS (по мотивам техники Lea Verou):
   два слоя «прикрывают» тень тем же цветом, что и сама полоса, и едут
   вместе с контентом (background-attachment: local), а сама тень неподвижна
   (attachment: scroll). У самого края прикрывающий слой глушит тень
   полностью — докрутили до конца, подсказка гаснет сама, без единой
   строки JS. */
.tabbar {
  background-image:
    linear-gradient(to right, #f5f6f8 40%, rgba(245, 246, 248, 0)),
    linear-gradient(to left, #f5f6f8 40%, rgba(245, 246, 248, 0)),
    linear-gradient(to right, rgba(15, 23, 42, .16), rgba(15, 23, 42, 0)),
    linear-gradient(to left, rgba(15, 23, 42, .16), rgba(15, 23, 42, 0));
  background-repeat: no-repeat;
  background-size: 1.75rem 100%, 1.75rem 100%, .75rem 100%, .75rem 100%;
  background-position: left, right, left, right;
  background-attachment: local, local, scroll, scroll;
}
html.dark .tabbar {
  background-image:
    linear-gradient(to right, #0e1117 40%, rgba(14, 17, 23, 0)),
    linear-gradient(to left, #0e1117 40%, rgba(14, 17, 23, 0)),
    linear-gradient(to right, rgba(232, 235, 242, .18), rgba(232, 235, 242, 0)),
    linear-gradient(to left, rgba(232, 235, 242, .18), rgba(232, 235, 242, 0));
}
/* На этой ширине вкладки уже не скроллятся, а переносятся — подсказка не
   нужна. Идёт ПОСЛЕ безусловных правил выше и перечисляет оба селектора
   (обычный и `html.dark`): специфичность `html.dark .tabbar` выше, чем у
   голого `.tabbar`, и одно только позднее место в файле её не перебило бы —
   в тёмной теме подсказка осталась бы и на широком экране. */
@media (min-width: 640px) {
  .tabbar, html.dark .tabbar { background-image: none; }
}

/* Своя полоса прокрутки под тенью была бы третьей горизонтальной линией
   подряд — прячем её, жест остаётся. */
.tabbar { scrollbar-width: none; -ms-overflow-style: none; }
.tabbar::-webkit-scrollbar { display: none; }

/* Ряд крупных плиток-цифр (заявка повара: «Питается — всего», смены,
   «по типам питания»). Раньше это была решётка grid-cols-2 — как только
   плиток становилось больше четырёх (например, добавилась «ГПД»), лишняя
   плитка повисала на отдельной строке одна. Тот же приём тени по краям,
   что у .tabbar выше, только цвет прикрывающего слоя — фон страницы
   (у плиток своего фона нет, под ними сразу body): plitки не переносятся,
   а прокручиваются вбок, и там, где все помещаются без остатка, тень сама
   гаснет — .tile-scroll не нужен отдельный @media-выключатель, как у
   .tabbar, потому что здесь нет переноса на широком экране, который надо
   было бы отличать от прокрутки. */
.tile-scroll {
  display: flex;
  flex-wrap: nowrap;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  gap: .75rem;
  padding-bottom: 2px;
  scrollbar-width: none;
  -ms-overflow-style: none;
  background-image:
    linear-gradient(to right, #f5f6f8 40%, rgba(245, 246, 248, 0)),
    linear-gradient(to left, #f5f6f8 40%, rgba(245, 246, 248, 0)),
    linear-gradient(to right, rgba(15, 23, 42, .16), rgba(15, 23, 42, 0)),
    linear-gradient(to left, rgba(15, 23, 42, .16), rgba(15, 23, 42, 0));
  background-repeat: no-repeat;
  background-size: 1.75rem 100%, 1.75rem 100%, .75rem 100%, .75rem 100%;
  background-position: left, right, left, right;
  background-attachment: local, local, scroll, scroll;
}
.tile-scroll::-webkit-scrollbar { display: none; }
.tile-scroll > * { scroll-snap-align: start; flex: 1 1 9rem; min-width: 9rem; }
html.dark .tile-scroll {
  background-image:
    linear-gradient(to right, #0e1117 40%, rgba(14, 17, 23, 0)),
    linear-gradient(to left, #0e1117 40%, rgba(14, 17, 23, 0)),
    linear-gradient(to right, rgba(232, 235, 242, .18), rgba(232, 235, 242, 0)),
    linear-gradient(to left, rgba(232, 235, 242, .18), rgba(232, 235, 242, 0));
}

/* --------------------------------------------------- широкие таблицы ----- */

/* Таблица заявки — восемь колонок: на телефоне из 739 px видно 341, за краем
   остаются «Питается» и «Отмечен», ради которых страницу и открывают. Двумя
   приёмами: узкие боковые отступы (это −128 px) и липкая первая колонка —
   прокрутив вбок, повар иначе терял, чья это строка.

   :where() у фона строки не для красоты — он обнуляет специфичность, иначе
   правило перебило бы Tailwind-подсветку неотмеченных строк (bg-rose-50,
   bg-amber-50/60), которая приезжает с CDN позже этого файла. Липкая ячейка
   берёт фон строки через inherit, поэтому подсветка едет вместе с ней. */
:where(.table-sticky tbody tr, .table-sticky tfoot tr) {
  background-color: var(--row-bg, #fff);
}
.table-sticky thead th:first-child { background-color: var(--row-head, #f9fafb); }

@media (max-width: 640px) {
  .table-sticky th, .table-sticky td { padding-left: .5rem; padding-right: .5rem; }
  .table-sticky th:first-child,
  .table-sticky td:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
    background-color: inherit;
    /* волосяная граница вместо тени: без неё цифры уезжают прямо под
       название класса и не видно, где кончается липкая колонка */
    box-shadow: 1px 0 0 var(--row-edge, rgba(15, 23, 42, .08));
  }
  .table-sticky thead th:first-child { z-index: 2; }
}


/* ------------------------------------------------------- края экрана ----- */

/* Установленное приложение занимает весь экран: полоса состояния сверху и
   полоса жестов снизу — наши, и рисует под ними страница, а не система.
   Величины держим переменными, а не пишем env() по месту: так их видно в
   одном месте, а вёрстку можно проверить, подменив значение в devtools.
   В обычной вкладке они нулевые — ни одно правило ниже ничего не меняет.

   Работает это только с viewport-fit=cover в <meta viewport>; он стоит на
   всех четырёх страницах, и убирать его нельзя — env() обнулится. */
:root {
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
}

/* Заглушки под системными панелями: матовое стекло, сквозь которое видно,
   что страница продолжается, а часы и батарея остаются читаемыми. Цвет
   подложки — в theme.css, здесь только форма. */
body::before,
body::after {
  content: "";
  position: fixed;
  left: 0;
  right: 0;
  z-index: 45;              /* выше липких шапок (30), ниже модалок (50) */
  pointer-events: none;
  background-color: var(--edge-bar, var(--edge-tint));
  backdrop-filter: saturate(180%) blur(10px);
}
body::before { top: 0; height: var(--safe-top); }
body::after { bottom: 0; height: var(--safe-bottom); }

/* вырез в альбомной ориентации забирает и бок */
body {
  padding-left: var(--safe-left);
  padding-right: var(--safe-right);
}

/* Узкая страница (учитель, вход): шапка стоит первой, и под полосу
   состояния уходит её собственный фон — значит, отступ добирает она сама,
   а не контейнер, иначе сверху осталась бы полоска фона страницы. */
body > .max-w-md > header.sticky { padding-top: calc(1.25rem + var(--safe-top)); }
body > .max-w-md > header:not(.sticky) { padding-top: calc(1.5rem + var(--safe-top)); }
body > .max-w-md > footer { padding-bottom: calc(1rem + var(--safe-bottom)); }
#submit-bar { padding-bottom: calc(0.75rem + var(--safe-bottom)); }

/* Широкая страница (повар, завуч, статистика питания): отступ добирает
   контейнер, а липкие полосы прилипают ПОД полосу состояния, а не под неё
   саму — иначе к их высоте пришлось бы навсегда добавить сорок пикселей
   пустоты. Ширины перечислены поимённо, и это ловушка: страница статистики
   объявлена как max-w-4xl и под правило не попадала — в установленном
   приложении её шапка уезжала под часы. Заводите новую ширину — дописывайте
   её сюда. */
body > .max-w-4xl,
body > .max-w-5xl,
body > .max-w-6xl {
  padding-top: calc(1rem + var(--safe-top));
  padding-bottom: calc(1rem + var(--safe-bottom));
}
/* .sticky в селекторе не для красоты: у Tailwind есть свой top-0 с той же
   специфичностью, и он приезжает с CDN позже нашего файла */
.pagebar.sticky, .tabbar.sticky { top: var(--safe-top); }

/* всплывающее не должно попадать под полосу жестов */
#toast { bottom: calc(1.5rem + var(--safe-bottom)); }
/* и всегда поверх окон (у них z-50, как у тоста): окно, открытое после тоста, иначе ложится на него сверху */
#toast { z-index: 60; }
.fixed.inset-0.bg-black\/40 { padding-bottom: calc(1rem + var(--safe-bottom)); }

/* ------------------------------------------------ страница ждёт данные -- */

/* Всё, что зависит от ответа сервера, прячем (.after-load) вместе с подвалом, пока не пришли первые данные: body.is-loading
   снимает loader(false) (app.js). Иначе кнопки и пустые карточки стоят вверху сразу и «уезжают», когда над ними вставляются итоги;
   скрытое не считается сдвигом — при появлении всё сразу на своём месте. */
body.is-loading .after-load { display: none; }
body.is-loading footer { visibility: hidden; }
/* подвал держит место под название учреждения (две строки) — текст приходит с /api/meta */
#footer-school { min-height: 2.75em; }

/* ---------------------------------------------------------- кнопка темы -- */

/* Слот держит свой размер ещё до того, как app.js создаст саму кнопку:
   иначе ряд шапки перестраивается на глазах уже после отрисовки страницы.
   Размер один на все страницы — раньше каждая правила его своим <style>. */
#theme-slot {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
}
#theme-slot #theme-toggle { width: 100%; height: 100%; }

/* ------------------------------------------------------------- вкладки --- */

.tab { position: relative; }
.tab:not(.active):hover {
  transform: translateY(-1px);
  box-shadow: var(--ui-shadow);
}
.tab.active { box-shadow: var(--ui-shadow-lift); }

/* --------------------------------------------------------- полоса счёта -- */

#counter-bar { transition: width .45s var(--ui-ease), background-color .3s ease; }

/* ------------------------------------------------------------ скроллбар -- */

* { scrollbar-width: thin; scrollbar-color: #d3d7de transparent; }
html.dark * { scrollbar-color: #3a4358 transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: #d3d7de;
  border: 3px solid transparent;
  border-radius: 9999px;
  background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background: #9aa1af; background-clip: content-box; }
html.dark ::-webkit-scrollbar-thumb { background: #3a4358; background-clip: content-box; }

/* ---------------------------------------------------- меньше движения ---- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-delay: 0ms !important;
    transition-duration: .001ms !important;
  }
  .student:hover, .tab:not(.active):hover, button:active:not(:disabled) {
    transform: none;
  }
}

/* (в конце файла — должно идти после базовых правил полос и подложек, иначе они перебьют) Телефон: размытие фона под липкими панелями (backdrop-filter) пересчитывается на каждый кадр прокрутки и смены темы —
   на средних Android (Samsung серии A и т. п.) это главный расход кадров. На сенсорных экранах панели почти непрозрачные, без
   размытия: выглядит так же, а плавность заметно выше. */
@media (pointer: coarse) {
  header.sticky, nav.sticky, #submit-bar.sticky, .tabbar, .pagebar, body::before, body::after {
    backdrop-filter: none;
  }
  @supports (backdrop-filter: blur(12px)) {
    header.sticky.bg-white, #submit-bar.sticky.bg-white { background-color: rgba(255, 255, 255, .96); }
    html.dark header.sticky.bg-white, html.dark #submit-bar.sticky.bg-white { background-color: rgba(22, 26, 35, .96) !important; }
  }
  .tabbar, .pagebar { background-color: rgba(245, 246, 248, .96); }
  html.dark .tabbar, html.dark .pagebar { background-color: rgba(14, 17, 23, .96); }
}

/* подзаголовок страницы повара (дата · учреждение) держит место под свои строки: текст приходит позже, и шапка не должна расти.
   На телефоне — две строки: название учреждения длинное и переносится */
.sub-reserve { min-height: 1.25rem; }
@media (max-width: 640px) { .sub-reserve { min-height: 2.5rem; } }
