/* find-the-end.fun - витрина миров
   Вся страница это один кадр: имя сверху, три мира по центру,
   подпись внизу. Прокрутки нет, размеры считаются от экрана,
   поэтому кадр целиком помещается и на мониторе, и на телефоне. */

:root {
  --bg: #06080d;
  --bg-2: #0a0e15;
  --line: #1b222e;
  --text: #e7ecf3;
  --muted: #7c8899;
  --dim: #748092;
  --accent: #ffb163;

  --sans: "Inter", "SF Pro Display", "Segoe UI", system-ui, -apple-system, Arial, sans-serif;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --gut: clamp(16px, 4vw, 64px);
  --card-gap: clamp(10px, 1.6vw, 26px);
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  overflow: hidden;               /* один экран, уходить некуда */
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;

  /* Витрина ведёт себя как приложение, а не как документ: текст здесь
     не для копирования. Без этого зажатая мышь тянет по странице синюю
     полосу выделения, а долгое касание вызывает лупу и меню «скопировать» -
     и то и другое мешает тянуть полку рукой. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}

/* Картинку браузер по умолчанию отдаёт системному перетаскиванию: от неё
   отрывается призрак и едет за курсором вместо полки. */
img { -webkit-user-drag: none; user-drag: none; }

/* Свет, падающий из того окна, к которому подошёл зритель.
   Цвет ставит скрипт при наведении на карточку. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(
    60vw 60vw at var(--glow-x, 50%) var(--glow-y, 50%),
    var(--glow-color, transparent),
    transparent 70%
  );
  opacity: 0;
  transition: opacity 0.7s var(--ease);
}
body.lit::before { opacity: 0.13; }

#snow {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}

/* виньетка, чтобы края кадра уходили в темноту.
   лежит над снегом, но под содержимым - текст она не глушит */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: radial-gradient(circle at 50% 40%, transparent 42%, var(--bg) 96%);
}

/* ---------- каркас кадра ---------- */

.stage {
  position: relative;
  z-index: 3;
  height: 100vh;
  height: 100svh;
  display: flex;
  flex-direction: column;
  /* safe-area, чтобы на телефонах с вырезом кадр не лез под него */
  padding:
    calc(clamp(14px, 3.2vh, 40px) + env(safe-area-inset-top, 0px))
    calc(var(--gut) + env(safe-area-inset-right, 0px))
    calc(clamp(14px, 3vh, 34px) + env(safe-area-inset-bottom, 0px))
    calc(var(--gut) + env(safe-area-inset-left, 0px));
}

/* ---------- проявление ---------- */

.reveal {
  opacity: 0;
  transform: translateY(18px);
  filter: blur(6px);
  transition:
    opacity 1s var(--ease),
    transform 1s var(--ease),
    filter 1s var(--ease);
  transition-delay: calc(var(--d, 0) * 110ms);
}
.reveal.in {
  opacity: 1;
  transform: none;
  filter: none;
}
/* Карточки являются LCP-кандидатами и должны быть нарисованы сразу. Их
   появление сохраняет движение, но не скрывает изображение opacity/filter. */
.card.reveal { opacity: 1; filter: none; }

/* ---------- имя сверху ---------- */

.top {
  flex: 0 0 auto;
  text-align: center;
}

/* размер имени ограничен и шириной, и высотой экрана:
   в низком окне оно ужимается само, а не съедает место у миров */
.title {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0 clamp(12px, 2.4vw, 38px);
  font-size: clamp(2.4rem, min(12.5vw, 15.5vh), 9.5rem);
  font-weight: 700;
  line-height: 0.88;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.title span { display: block; }

/* ---------- миры по центру ---------- */

/* deck забирает всю высоту, которая осталась от имени и подписи.
   По краям он отдаёт место стрелкам, остальное занимает полка. */
.deck {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  gap: clamp(4px, 1vw, 14px);
  padding: clamp(14px, 3.6vh, 46px) 0;
}

/* полка: три мира видно, остальные приезжают стрелками.
   Она же мера для карточек: 100cqh это её высота, 100cqw - ширина */
.rail {
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
  container-type: size;

  display: flex;
  align-items: center;
  gap: var(--card-gap);

  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.rail::-webkit-scrollbar { display: none; }

/* пока миров меньше, чем влезает, ряд стоит по центру */
.rail.short { justify-content: center; }

/* Полку можно тянуть рукой - и мышью, и пальцем. Пальца это касается только
   курсором: его прокрутку с инерцией браузер везёт сам, лучше не мешать.
   Мышь тянет скрипт (main.js), отсюда ему нужны две вещи:
   snap на время перетаскивания снимается - иначе он на каждый сдвиг
   утягивает полку обратно к ближайшему миру, и рука едет по резинке. */
.rail { cursor: grab; }
.rail.short { cursor: default; }
.rail.dragging { scroll-snap-type: none; }
/* курсор сжатой руки нужен на всей полке: над карточкой-ссылкой иначе
   останется указатель, будто тянуть перестало быть можно */
.rail.dragging,
.rail.dragging * { cursor: grabbing; }

/* ---------- стрелки ---------- */

.arrow {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0;
  padding: clamp(6px, 1vw, 12px) 0;
  border: 0;
  background: none;
  color: var(--dim);
  cursor: pointer;
  transition: color 0.5s var(--ease), opacity 0.5s var(--ease);
}
.arrow:hover:not(:disabled),
.arrow:focus-visible:not(:disabled) {
  color: var(--text);
  outline: none;
}
.arrow:disabled { opacity: 0.22; cursor: default; }

.chev {
  display: block;
  width: clamp(9px, 1.1vw, 15px);
  margin: 0 -2px;                 /* уголки идут внахлёст, как след */
  opacity: 0.35;
  animation: chev 1.9s var(--ease) infinite;
  animation-delay: calc(var(--i, 0) * 160ms);
}
.arrow:disabled .chev { animation: none; opacity: 1; }

.chev svg {
  display: block;
  width: 100%;
  height: auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* волна бежит от ближнего уголка к дальнему, в сторону движения */
.arrow.prev { --dir: -1; }
.arrow.next { --dir: 1; }

@keyframes chev {
  0%, 55%, 100% { opacity: 0.3; transform: none; }
  20%           { opacity: 1; transform: translateX(calc(var(--dir, 1) * 3px)); }
}

/* ---------- карточка мира ---------- */

/* ширина карточки это меньшее из трёх: разумный максимум,
   сколько позволяет высота полки и сколько позволяет её ширина */
.card {
  flex: 0 0 auto;
  width: min(500px, 26vw);
  container-type: inline-size;
  scroll-snap-align: start;
}

@supports (width: 1cqh) {
  .card {
    width: min(
      500px,
      100cqh * 0.75,
      (100cqw - 2 * var(--card-gap)) / 3
    );
  }
}

.card-link {
  position: relative;
  display: block;
  aspect-ratio: 3 / 4;
  border: 1px solid var(--line);
  border-radius: 3px;
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  background: var(--bg-2);
  transition: border-color 0.5s var(--ease), transform 0.5s var(--ease);
}

.frame { position: absolute; inset: 0; }
.frame picture { display: block; width: 100%; height: 100%; }
.frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1.2s var(--ease), filter 0.8s var(--ease);
}
/* кадр всегда притоплен в темноту, чтобы текст читался */
.frame::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top,
    rgba(6, 8, 13, 0.96) 0%,
    rgba(6, 8, 13, 0.72) 34%,
    rgba(6, 8, 13, 0.18) 70%,
    rgba(6, 8, 13, 0.35) 100%
  );
}

/* текст на карточке меряется самой карточкой (cqw),
   поэтому мелкая карточка не разваливается от крупных букв */
.card-body {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: clamp(12px, 6cqw, 26px);
}

.card-no {
  margin: 0 0 clamp(5px, 2.4cqw, 10px);
  font-size: clamp(8px, 2.6cqw, 10px);
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: var(--dim);
}
.card-body h2 {
  margin: 0;
  font-size: clamp(15px, 7cqw, 28px);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: 0.01em;
}
.card-line {
  margin: clamp(5px, 2.2cqw, 10px) 0 0;
  font-size: clamp(11px, 3.6cqw, 14px);
  line-height: 1.45;
  color: var(--muted);

  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* открытая карточка */
.card.open .card-link:hover,
.card.open .card-link:focus-visible {
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  transform: translateY(-6px);
}
.card.open .card-link:hover .frame img { transform: scale(1.06); }

/* закрытая карточка: тот же кадр, но за темнотой.
   при наведении чуть проступает, будто вглядываешься */
/* кадр за замком должен только угадываться, поэтому светлые снимки
   тоже уводим в темноту - иначе закрытый мир выглядит открытым */
.card.locked .frame img {
  filter: grayscale(0.8) brightness(0.32) blur(6px);
  transform: scale(1.08);
}
.card.locked:hover .frame img {
  filter: grayscale(0.45) brightness(0.5) blur(3px);
}
.card.locked .card-body h2 { color: var(--muted); }
.card.locked .card-line { color: var(--dim); }
.card.locked .card-link { cursor: default; }

/* замок не значок в углу, а печать на всей витрине:
   он лежит поверх кадра целиком, без рамки */
.lock {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  /* низ карточки отдан подписи, поэтому замок садится выше центра */
  padding-bottom: 22%;
  color: var(--dim);
  opacity: 0.5;
  transition: color 0.6s var(--ease), opacity 0.6s var(--ease);
}
.card.locked:hover .lock {
  color: color-mix(in srgb, var(--accent) 70%, var(--muted));
  opacity: 0.85;
}
.lock svg {
  width: 42cqw;
  height: auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
  stroke-linecap: round;
}

/* ---------- подпись внизу ---------- */

.foot {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 6px clamp(12px, 2vw, 28px);
  font-size: clamp(9px, 1vw, 11px);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--dim);
}
.foot p { margin: 0; }

.secret {
  border: 0;
  background: none;
  width: 44px;
  height: 44px;
  padding: 0;
  display: inline-grid;
  place-items: center;
  font: inherit;
  color: var(--line);
  cursor: pointer;
  transition: color 1.2s var(--ease), text-shadow 1.2s var(--ease);
}
.secret:hover,
.secret:focus-visible {
  color: var(--muted);
  text-shadow: 0 0 12px currentColor;
  outline: none;
}
.secret:focus-visible, .card-link:focus-visible {
  outline: 2px solid var(--muted);
  outline-offset: 4px;
}
/* секрет всплывает над подписью и не двигает кадр */
.secret-text {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(100% + 8px);
  margin: 0;
  text-align: center;
  font-size: clamp(9px, 1vw, 11px);
  letter-spacing: 0.3em;
  color: var(--muted);
  animation: fade 1.4s var(--ease);
}
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

/* ---------- узкий экран: миры ложатся в столбик ---------- */

@media (max-width: 900px) and (orientation: portrait) {
  /* имя всегда в две строки: find the / end */
  .title .last { flex-basis: 100%; }

  /* пальцем листать удобнее, чем кнопками, поэтому стрелки уходят,
     а полка встаёт вертикально и прокручивается свайпом */
  .arrow { display: none; }

  .rail {
    flex-direction: column;
    overflow-x: hidden;
    overflow-y: auto;
    scroll-snap-type: y mandatory;
    overscroll-behavior-y: contain;
  }

  .card {
    width: 100%;
    max-width: 640px;
    container: card / size;
  }
  /* делим не ровно на три: край четвёртого мира должен выглядывать,
     иначе не видно, что полку можно листать пальцем */
  @supports (height: 1cqh) {
    .card { height: min(210px, (100cqh - 2 * var(--card-gap)) / 3.3); }
  }
  @supports not (height: 1cqh) {
    .card { height: min(210px, 22vh); }
  }

  .card-link { aspect-ratio: auto; height: 100%; }

  /* кадр теперь широкий, поэтому темнота набегает слева, а не снизу */
  .frame::after {
    background: linear-gradient(
      to right,
      rgba(6, 8, 13, 0.96) 4%,
      rgba(6, 8, 13, 0.7) 52%,
      rgba(6, 8, 13, 0.25) 100%
    );
  }

  .card-body {
    top: 0;
    right: auto;
    width: min(78%, 420px);
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: clamp(12px, 3.6vw, 22px);
  }
  .card-no { margin-bottom: 4px; font-size: 9px; }
  .card-body h2 { font-size: clamp(17px, 5vw, 22px); }
  .card-line { margin-top: 5px; font-size: clamp(11px, 3.4vw, 13px); -webkit-line-clamp: 2; line-clamp: 2; }

  /* карточка теперь широкая и низкая: замок отходит вправо,
     туда, где кадр не занят подписью */
  .lock {
    padding: 0 clamp(12px, 3.6vw, 20px) 0 0;
    justify-items: end;
  }
  .lock svg { width: auto; height: 52cqh; }

  .foot { gap: 4px 14px; }
}

/* низкая плитка отдаёт лишнее: сначала строка настроения ужимается,
   потом уходит совсем - имя мира важнее */
@container card (max-height: 155px) {
  .card-line { -webkit-line-clamp: 1; line-clamp: 1; }
}
@container card (max-height: 132px) {
  .card-line { display: none; }
  .card-body { padding-top: 10px; padding-bottom: 10px; }
}

/* ---------- уважение к настройке «меньше движения» ---------- */

/* Уход в мир: витрина гаснет, и мир продолжает эту же темноту.
   Раньше переход был встык — щелчок, и вместо галереи сразу чужой кадр.
   Теперь галерея темнеет за полсекунды, а мир открывается уже из темноты
   своим появлением (`awaken.js` в каждой игре): получается одно движение,
   а не два разных экрана подряд. */
#leaving {
  position: fixed;
  inset: 0;
  z-index: 9;
  /* Цвет неба того мира, куда уходим (ставит main.js по data-bg карточки);
     без него - своя темнота витрины. */
  background: var(--leave-bg, var(--bg));
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.45s ease;
}
body.leaving #leaving {
  opacity: 1;
  /* Перехватываем указатель: второй клик по той же карточке во время ухода
     ничего не даст, а вот дёрнуть подсветку успеет. */
  pointer-events: auto;
}

@media (prefers-reduced-motion: reduce) {
  #leaving { transition: none; }
  .reveal { opacity: 1; transform: none; filter: none; transition: none; }
  .frame img, .card-link, .secret { transition: none; }
  .secret-text { animation: none; }
  .chev { animation: none; opacity: 0.7; }
}
