/* Оформление поверх 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;
}

h1 { letter-spacing: -0.02em; }
h2, h3 { letter-spacing: -0.011em; }

/* цифры в счётчиках не должны прыгать при пересчёте */
.tabular-nums, td, th, input[type="date"] { font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------- фон ----- */

/* Плоская заливка сменяется мягким пятном сверху: страница перестаёт
   выглядеть как серый лист, но остаётся спокойной. */
body.bg-slate-100 {
  background-image:
    radial-gradient(60rem 30rem at 50% -12rem, rgba(99, 102, 241, .10), transparent 70%);
  background-attachment: fixed;
}
html.dark body.bg-slate-100 {
  background-image:
    radial-gradient(60rem 30rem at 50% -12rem, rgba(99, 102, 241, .16), transparent 70%);
}

/* ------------------------------------------------------------- карточки -- */

.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: #818cf8; }

/* главные кнопки действия приподнимаются, а не просто темнеют */
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);
}

/* ------------------------------------------------------------- таблицы --- */

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; }
}

/* экраны учителя и панели админки въезжают при переключении */
.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);
}

/* ---------------------------------------------------------- модалки ------ */

/* 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(16, 26, 45, .82) !important;
  }
}

/* Липкие полосы: вкладки админки и шапка повара. Страница внутри
   контейнера с отступами, поэтому полоса растягивается отрицательными
   полями — иначе под ней просвечивал бы контент по краям. */
.tabbar, .pagebar {
  margin-inline: -1rem;
  padding-inline: 1rem;
  background-color: rgba(241, 245, 249, .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(6, 13, 26, .85);
  box-shadow: 0 1px 0 rgba(226, 232, 240, .08);
}
@media (min-width: 640px) {
  .tabbar, .pagebar { margin-inline: -2rem; padding-inline: 2rem; }
}


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

/* Установленное приложение занимает весь экран: полоса состояния сверху и
   полоса жестов снизу — наши, и рисует под ними страница, а не система.
   Величины держим переменными, а не пишем 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-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)); }

/* Широкая страница (повар, завуч): отступ добирает контейнер, а липкие
   полосы прилипают ПОД полосу состояния, а не под неё саму — иначе к их
   высоте пришлось бы навсегда добавить сорок пикселей пустоты. */
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)); }
.fixed.inset-0.bg-black\/40 { padding-bottom: calc(1rem + var(--safe-bottom)); }

/* ---------------------------------------------------------- кнопка темы -- */

/* Слот держит свой размер ещё до того, как 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: #cbd5e1 transparent; }
html.dark * { scrollbar-color: #35435f transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: #cbd5e1;
  border: 3px solid transparent;
  border-radius: 9999px;
  background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background: #94a3b8; background-clip: content-box; }
html.dark ::-webkit-scrollbar-thumb { background: #35435f; 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;
  }
}
