/* zürich.moskva — мессенджер. Стили всех девяти экранов.
   Токены и вёрстка согласованы с макетами в design/*.dc.html —
   расхождения с ними правятся сначала там, потом здесь. */

:root {
  --ink: #ffffff;      /* текст и линейки */
  --paper: #000000;    /* поле */
  --rule: #ffffff;
  --grey: #7d7d7d;     /* второстепенное: подписи, плейсхолдер */
  --hair: #2b2b2b;     /* разделители внутри списка */
  --red: #e30613;      /* единственный цвет на всю страницу */
  --gap: 24px;
  --name-weight: 700;
  --name-track: -0.03em;

  /* Чтобы системные элементы — каретка, скроллбар, автозаполнение —
     не остались светлыми на чёрном. */
  color-scheme: dark;
}

* { box-sizing: border-box; }

html, body { margin: 0; height: 100%; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: "Helvetica Neue", Helvetica, Arial, "Inter", sans-serif;
  font-size: 15px;
  line-height: 1.25;
  -webkit-font-smoothing: antialiased;
  letter-spacing: -0.01em;
}

.app {
  /* Ровно во весь экран, не «не меньше»: шапка и подвал стоят на месте,
     а прокручивается только <main> — иначе на телефоне длинный список
     уносил кнопки подвала за край. */
  height: 100vh;
  height: 100dvh; /* на телефоне высота меняется вместе с адресной строкой */
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  max-width: 1080px;
  margin: 0 auto;
  /* Отступы под вырез и полосу жеста: в обычном браузере env() даёт ноль,
     так что на десктопе ничего не меняется. */
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
  padding-left: calc(var(--gap) + env(safe-area-inset-left));
  padding-right: calc(var(--gap) + env(safe-area-inset-right));
}

/* ── типографика ─────────────────────────────── */

.label {
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--grey);
}

.label--ink { color: var(--ink); }
.red { color: var(--red); }

/* ── шапка ───────────────────────────────────── */

header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20px 0 14px;
  border-bottom: 1px solid var(--rule);
}

/* Логотип — не текст, а контуры: Luminari есть только в macOS, живым
   текстом на телефоне вторая половина слова подменилась бы чем попало. */
.wordmark {
  display: block;
  width: 100%;
  height: auto;
  fill: currentColor;
}

.wordmark--head { width: 132px; }

/* ── механика экранов ────────────────────────── */

main {
  display: flex;
  flex-direction: column;
  /* safe — чтобы содержимое выше окна не уезжало за верхний край без
     возможности докрутить (обычный center так и делает). */
  justify-content: safe center;
  padding: 48px 0;
  min-height: 0;
  overflow-y: auto;

  /* Все экраны занимают одну и ту же ячейку сетки. Во время перехода
     отрисованы сразу два, и без этого сетка развела бы их по разным
     строкам, утащив подвал вниз. */
  grid-row: 2;
  grid-column: 1;

  /* display участвует в переходе через allow-discrete: иначе уходящий
     экран пропадал бы мгновенно, не дав анимации проиграться.
     Длинная запись намеренно: в сокращённой браузер, не знающий
     allow-discrete, отбросил бы правило целиком вместе с opacity. */
  transition-property: opacity, display;
  transition-duration: 220ms;
  transition-timing-function: ease;
  transition-behavior: allow-discrete;
}

main.off {
  display: none;
  opacity: 0;
}

/* Состояние, из которого приходящий экран начинает движение.
   Условие body.ready отсекает первую отрисовку: страница при загрузке
   должна появляться сразу, растворение нужно только на переходе. */
@starting-style {
  body.ready main:not(.off) { opacity: 0; }
}

main.top {
  justify-content: flex-start;
  padding: 32px 0 24px;
}

main.mid {
  justify-content: safe center;
  align-items: flex-start;
}

/* ── поля и кнопки ───────────────────────────── */

.field {
  appearance: none;
  display: block;
  width: 100%;
  font: inherit;
  font-size: 17px;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 2px solid var(--rule);
  padding: 10px 0;
  border-radius: 0;
}

.field::placeholder { color: var(--grey); }

.field:focus { outline: 0; border-bottom-color: var(--red); }

.form {
  display: grid;
  gap: 22px;
  max-width: 320px;
}

button {
  font: inherit;
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  border: 2px solid var(--rule);
  background: var(--ink);
  color: var(--paper);
  padding: 0 22px;
  min-height: 40px;
  border-radius: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 90ms linear, color 90ms linear;
}

/* Наведение инвертирует кнопку, а не красит: красный расписан по местам
   (фокус, .armed, .live, бейдж, .notice) и на украшения не тратится. */
button:hover:not(:disabled) { background: transparent; color: var(--ink); }
button:disabled { opacity: 0.35; cursor: default; }

button.ghost {
  background: transparent;
  color: var(--ink);
}

button.ghost:hover:not(:disabled) { background: var(--ink); color: var(--paper); border-color: var(--ink); }

button.armed {
  background: var(--red);
  border-color: var(--red);
  color: var(--paper);
}

button.armed:hover:not(:disabled) { background: var(--ink); border-color: var(--ink); }

button.wide { width: 100%; }

select {
  appearance: none;
  font: inherit;
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink);
  background: transparent;
  border: 2px solid var(--rule);
  border-radius: 0;
  padding: 0 28px 0 10px;
  min-height: 40px;
  max-width: 260px;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink) 50%), linear-gradient(135deg, var(--ink) 50%, transparent 50%);
  background-position: calc(100% - 15px) 18px, calc(100% - 10px) 18px;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  cursor: pointer;
}

select:focus { outline: 0; border-color: var(--red); }

/* ── списки ──────────────────────────────────── */

.list {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--rule);
}

.list li {
  display: grid;
  grid-template-columns: 48px 1fr auto;
  align-items: baseline;
  gap: 12px;
  padding: 14px 0;
  border-bottom: 1px solid var(--hair);
}

/* Класс ставится только новым строкам — иначе список переигрывал бы себя
   целиком на каждой перерисовке. Задержка разводит строки по очереди,
   сверху вниз, так же как читается нумерация. */
.list li.fresh {
  animation: row 260ms ease both;
  animation-delay: calc(var(--i) * 45ms);
}

@keyframes row {
  from { opacity: 0; transform: translateY(7px); }
}

.num {
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--grey);
  font-variant-numeric: tabular-nums;
}

.who {
  font-size: 30px;
  font-weight: var(--name-weight);
  letter-spacing: var(--name-track);
  overflow-wrap: anywhere;
}

.who--huge { font-size: 56px; }
.who--big { font-size: 40px; }
.who--mid { font-size: 24px; }

.sub {
  display: block;
  margin-top: 4px;
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--grey);
}

.flag {
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--grey);
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 8px;
}

.badge {
  font-size: 9px;
  font-weight: 400;
  letter-spacing: 0.14em;
  color: var(--paper);
  background: var(--red);
  padding: 2px 5px;
  margin-left: 10px;
  vertical-align: 0.35em;
  display: inline-block;
}

.dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--grey);
  flex: none;
}

.dot.live { background: var(--red); }

/* ── переписка ───────────────────────────────── */

.chat {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 14px;
}

.chat li {
  display: grid;
  grid-template-columns: 48px 1fr auto;
  gap: 12px;
  align-items: baseline;
}

.chat .t {
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--grey);
  font-variant-numeric: tabular-nums;
}

.chat .m { font-size: 15px; line-height: 1.35; }
.chat li.mine .m { color: var(--ink); }
.chat li.undelivered .m { color: var(--grey); }

.say {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 12px;
  align-items: end;
  margin-top: 24px;
}

/* ── звонок ──────────────────────────────────── */

.huge {
  font-size: 64px;
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1;
}

.timer {
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--grey);
  font-variant-numeric: tabular-nums;
}

.calling { margin: 0 0 18px; }

.answer { display: flex; gap: 12px; margin-top: 40px; }
.answer button { min-height: 52px; padding: 0 32px; }

#screen-incoming .sub { margin-top: 12px; }
.callhead { display: block; }
#call-timer { display: block; margin-top: 14px; }
.call-privacy { margin-top: 34px; }
.call-mic-note { margin-top: 16px; }
#call-error { margin-top: 22px; }

/* Слой звонка. Пока идёт звонок, body несёт data-call, и этот блок ложится
   поверх приложения целиком — со своей шапкой и подвалом, как в макетах.
   Экраны маршрутов при этом не трогаются (см. show() и селектор .app > main
   в app.js): «назад» и переходы звонок не роняют. */
.callroot { display: none; }

body[data-call] .callroot {
  display: grid;
  position: fixed;
  inset: 0;
  z-index: 20;
  background: var(--paper);
  grid-template-rows: auto minmax(0, 1fr) auto;
  max-width: 1080px;
  margin: 0 auto;
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
  padding-left: calc(var(--gap) + env(safe-area-inset-left));
  padding-right: calc(var(--gap) + env(safe-area-inset-right));
}

/* Экраны слоя лежат оба, показывается один — по режиму звонка. Своей .off
   у них нет: видимостью правит только data-call, не общий переключатель. */
.callroot > main { display: none; }
body[data-call="incoming"] #screen-incoming { display: flex; }
body[data-call="call"] #screen-call { display: flex; }

/* Во входящем подвальные кнопки ни к чему — там только «Ответить»/«Отклонить». */
body[data-call="incoming"] #call-controls { visibility: hidden; }

/* Пока вызываем (звонящий ждёт ответа) — микрофоном ещё не управляем
   и время не идёт: скрываем мьют, выбор и таймер до active. */
body[data-call-state="ringing"] #mute-btn,
body[data-call-state="ringing"] #mic-select,
body[data-call-state="ringing"] #cam-btn,
body[data-call-state="ringing"] #cam-select { display: none; }
body[data-call-state="ringing"] #call-timer { visibility: hidden; }

/* ── видео ───────────────────────────────────── */

/* Сцена с картинкой. Пока ни одна камера не включена, сцены нет — экран
   выглядит как голосовой разговор. Показ каждого <video> решает свой класс
   на экране: has-remote — собеседник включил камеру, has-local — мы. */
.stage { display: none; position: relative; width: 100%; margin-top: 16px; }
#screen-call.has-remote .stage,
#screen-call.has-local .stage {
  display: block;
  /* Сцена берёт всё, что осталось под именем: картинке место нужнее,
     чем пустоте по центру. min-height: 0 — иначе flex не даст ей ужаться. */
  flex: 1 1 auto;
  min-height: 0;
}

.stage video {
  display: none;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: var(--paper);
  border: 1px solid var(--hair);
}
#screen-call.has-remote #remote-video { display: block; }
#screen-call.has-local #local-video { display: block; }

/* Своё превью — в углу картинки собеседника. Если он без камеры, превью
   занимает сцену целиком: так видно, что камера работает и что она снимает. */
#screen-call.has-remote.has-local #local-video {
  position: absolute;
  right: 10px;
  bottom: 10px;
  width: 26%;
  max-width: 200px;
  height: auto;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  border-color: var(--rule);
}
/* Фронтальную камеру показываем себе как зеркало — иначе движения «не туда». */
#local-video.mirror { transform: scaleX(-1); }

/* С картинкой экран выравнивается по верху (по центру имя и «Говорите с»
   заезжали под шапку), имя ужимается и встаёт в строку с таймером, а
   пояснение о приватности уходит: место нужнее видео, текст и так повторён
   на карточке контакта. */
#screen-call.has-remote,
#screen-call.has-local {
  justify-content: flex-start;
  padding-top: 24px;
  padding-bottom: 16px;
}
#screen-call.has-remote .callhead,
#screen-call.has-local .callhead {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  align-self: stretch; /* main.mid прижимает детей влево — а таймеру нужен правый край */
}
#screen-call.has-remote .calling,
#screen-call.has-local .calling { margin-bottom: 8px; }
#screen-call.has-remote .huge,
#screen-call.has-local .huge { font-size: 28px; }
#screen-call.has-remote #call-timer,
#screen-call.has-local #call-timer { margin-top: 0; }
#screen-call.has-remote .call-privacy,
#screen-call.has-local .call-privacy { display: none; }
#screen-call.has-remote .stage,
#screen-call.has-local .stage { cursor: pointer; }

/* Видео во весь экран: касание по сцене вешает .full на слой звонка. Шапка
   и подвал прячутся, поле без отступов, картинка заливает экран (cover),
   сверху остаются только имя и таймер — тенью, чтобы читались на любом
   кадре. Второе касание снимает класс. */
body[data-call] .callroot.full { padding: 0; }
.callroot.full header,
.callroot.full footer { display: none; }
.callroot.full #screen-call { padding: 0; position: relative; }
.callroot.full #screen-call > :not(.stage):not(.callhead) { display: none; }
.callroot.full .stage { margin: 0; }
.callroot.full .stage video { border: 0; }
.callroot.full #remote-video { object-fit: cover; }
.callroot.full #screen-call.has-local:not(.has-remote) #local-video { object-fit: cover; }
.callroot.full #screen-call.has-remote.has-local #local-video {
  right: calc(20px + env(safe-area-inset-right));
  bottom: calc(28px + env(safe-area-inset-bottom));
  border: 1px solid var(--rule);
}
/* Селекторы с #screen-call — чтобы перебить правила видео-состояния выше. */
.callroot.full #screen-call .callhead {
  position: absolute;
  z-index: 1;
  left: calc(20px + env(safe-area-inset-left));
  top: calc(20px + env(safe-area-inset-top));
  display: block;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
}
.callroot.full #screen-call .calling { display: none; }
.callroot.full #screen-call .huge { font-size: 20px; }
.callroot.full #screen-call #call-timer { margin-top: 6px; color: var(--ink); }

/* ── прочее ──────────────────────────────────── */

.empty {
  padding: 28px 0 0;
  max-width: 40ch;
  color: var(--grey);
  font-size: 14px;
  line-height: 1.45;
}

.notice {
  margin-top: 20px;
  border-left: 2px solid var(--red);
  padding: 2px 0 2px 12px;
  font-size: 13px;
  line-height: 1.45;
  max-width: 52ch;
}

/* Полоса «вышла новая версия» — сервер обновился, пока вкладка открыта.
   Нейтральная (инверсия поля), без красного: это не тревога, а подсказка. */
.update-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 10;
  padding: 10px 16px;
  text-align: center;
  font-size: 13px;
  background: var(--ink);
  color: var(--paper);
}

/* Для мелких блоков: ошибок, свежей ссылки, подписей. Экраны так прятать
   нельзя — !important не анимируется, им положен класс .off. */
.hidden, [hidden] { display: none !important; }

/* ── частности экранов ───────────────────────── */

/* регистрация */
.lead { margin: 0 0 26px; }

/* Тупик приглашения (использовано, сгорело, нет такого): вместо формы —
   врезка и, где есть куда идти, одна кнопка. */
#register-dead .notice { margin-top: 0; }
#register-dead button { margin-top: 22px; }

/* контакты: строка ведёт на карточку — курсор об этом говорит */
#contacts li { cursor: pointer; }

/* контакт */
.person {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 24px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--rule);
}

#screen-contact .chat { margin-top: 24px; }

/* Панель «Ещё»: инлайн-блок под шапкой карточки, без модалок.
   justify-items: start — кнопка удаления не должна растягиваться
   на всю ширину и выглядеть главным действием экрана. */
.more {
  display: grid;
  gap: 16px;
  justify-items: start;
  padding: 20px 0;
  border-bottom: 1px solid var(--hair);
}

.pet {
  display: grid;
  grid-template-columns: minmax(0, 320px) auto;
  gap: 12px;
  align-items: end;
  width: 100%;
}

.more .notice { margin-top: 0; }

/* исход звонка на карточке */
.outcome { padding: 14px 0; border-bottom: 1px solid var(--hair); }

/* заявки */
.ask {
  display: grid;
  grid-template-columns: minmax(0, 320px) auto;
  gap: 12px;
  align-items: end;
  padding-bottom: 28px;
}

/* приглашения */
.quota {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 24px;
  padding-bottom: 26px;
}

.of { color: var(--grey); }

.fresh-link {
  margin: 0 0 26px;
  max-width: none;
  padding-top: 10px;
  padding-bottom: 10px;
}

.fresh-link .label { margin: 0 0 8px; }

.fresh-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 12px;
  align-items: center;
}

.fresh-row .link { font-size: 14px; overflow-wrap: anywhere; }

/* Действие в третьей колонке строки — кнопка, но выглядит ссылкой:
   настоящая кнопка в каждой строке спорила бы с «Выпустить ссылку». */
.flag .act {
  border: 0;
  background: none;
  color: var(--ink);
  padding: 0;
  min-height: 0;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.flag .act:hover:not(:disabled) { background: none; color: var(--grey); }

/* настройки */
.me { padding-bottom: 26px; }

.sect { margin: 0 0 16px; }

.pass {
  display: flex;
  align-items: center;
  gap: 16px;
  padding-bottom: 32px;
}

.pass .sub { margin: 0; }

.sect-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 4px;
}

/* Устройства: «браузер · система» вместо сырого User-Agent, обычным
   начертанием — это справка, а не имя. */
.dev { font-size: 15px; letter-spacing: -0.01em; }
.list.tight li { padding: 11px 0; }

/* Модальное окно: затемнение и панель с той же двухпиксельной рамкой, что
   у кнопок. Радиусов и теней нет — как везде. */
.modal {
  width: calc(100% - 40px);
  max-width: 420px;
  margin: auto;
  padding: 24px 22px 22px;
  background: var(--paper);
  color: var(--ink);
  border: 2px solid var(--rule);
}

.modal::backdrop { background: rgba(0, 0, 0, 0.72); }

.modal form { display: grid; gap: 22px; }
.modal .label { margin: 0; }
.modal .form { max-width: none; }
.modal .notice { margin-top: 0; }
.modal .actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }

/* уведомления о звонках: строка состояния и одна кнопка */
.push {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}
.push-note { margin: 16px 0 32px; }

/* ── подвал и хром ───────────────────────────── */

footer {
  border-top: 1px solid var(--rule);
  padding: 14px 0 22px;
  display: flex;
  flex-wrap: wrap;
  gap: 12px 20px;
  align-items: center;
  justify-content: space-between;
}

.controls {
  display: flex;
  gap: 10px;
  align-items: stretch;
  flex-wrap: wrap;
}

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

/* Подвал скрыт, пока экран не назван (первая отрисовка идёт до ответа
   /api/me), и на входе с регистрацией: управлять там пока нечем. */
body:not([data-screen]) footer,
body[data-screen="login"] footer,
body[data-screen="register"] footer { display: none; }

/* Кнопки подвала лежат в разметке все разом; какие видны — решает экран.
   Это про кнопки, не про экраны: им display: none не мешает.
   Только про подвал приложения: у слоя звонка (.callroot) свои кнопки,
   и прятать их этим правилом нельзя. */
.app footer .controls button { display: none; }

body[data-screen="contacts"] footer .nav,
body[data-screen="contact"] footer .nav { display: inline-flex; }

/* Карточке контакта возврат тоже нужен: браузерное «назад» есть не у всех
   на виду, а список — единственный дом этого экрана. */
body[data-screen="contact"] footer .nav--back,
body[data-screen="requests"] footer .nav--back,
body[data-screen="invites"] footer .nav--back { display: inline-flex; }

body[data-screen="settings"] footer .nav--back,
body[data-screen="settings"] footer .nav--exit { display: inline-flex; }

/* ── телефон ─────────────────────────────────── */

@media (max-width: 560px) {
  :root { --gap: 20px; }

  .wordmark--head { width: 108px; }
  .form { max-width: none; }

  .list li { grid-template-columns: 32px 1fr auto; }
  .list li .flag { grid-column: 2; margin-top: 6px; }
  .list.tight li .flag { grid-column: 3; margin-top: 0; }

  .who { font-size: 26px; }
  .who--big { font-size: 30px; }
  .huge { font-size: 44px; }

  .person { display: block; }
  .person .controls { margin-top: 16px; }

  .answer { flex-direction: column; align-self: stretch; }

  footer { flex-direction: column; align-items: flex-start; padding: 12px 0 18px; gap: 10px; }
  footer .controls { width: 100%; gap: 8px; }
  /* Теснее, чем на десктопе: три кнопки должны влезть в один ряд. */
  footer .controls button { flex: 1 1 auto; padding: 0 12px; }

  /* Подвал звонка: кнопка и выбор устройства парами, «Завершить» на всю ширину. */
  .callroot footer .controls { display: grid; grid-template-columns: 1fr 1fr; }
  .callroot footer .controls select { max-width: none; width: 100%; }
  .callroot footer .controls #hangup-btn { grid-column: 1 / -1; }
}

@media (prefers-reduced-motion: reduce) {
  main { transition: none; }
  .list li.fresh { animation: none; }
}
