/* PiexelLock — вступительные сцены.
   Главная: консоль поднимает сервер, затем расходится театральный занавес.
   Кабинет: пиксельная стена собирается при уходе и рассыпается при входе.

   Всё построено на CSS-анимациях, которые стартуют сами по себе: если js/intro.js
   не загрузится, сцена всё равно доиграет и уберётся (последний кадр слоя —
   visibility:hidden). Скрипт отвечает только за пропуск, память сессии и удаление
   слоя из DOM. Скролл намеренно не блокируется — заблокировать его без JS легко,
   а вот вернуть при сбое уже нечем.

   Темп всей сцены задаёт --k: одна переменная растягивает или сжимает все
   задержки разом (на телефонах ниже она меньше — сцена короче). */

.pl-intro {
  --k: 1;
  --gold: #facc15;
  --gold-soft: #fde68a;
  --gold-deep: #a16207;
  --velvet-hi: #8b34a8;
  --velvet: #5d1d74;
  --velvet-lo: #1d0827;
  --mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
  --disp: "Space Grotesk", "Manrope", sans-serif;

  position: fixed;
  inset: 0;
  z-index: 9999;
  overflow: hidden;
  background: #05040a;
  contain: strict;
}

/* Предпоказ уже случился в этой сессии — слой не должен даже мелькнуть.
   Класс ставит инлайн-скрипт в <head> до первой отрисовки. */
html.pl-no-intro .pl-intro { display: none; }

.pl-skip {
  position: absolute;
  right: clamp(14px, 3vw, 30px);
  bottom: clamp(14px, 3vh, 28px);
  z-index: 40;
  font-family: var(--disp);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: #efe7ff;
  background: #0b0a13b3;
  border: 1px solid #ffffff2e;
  border-radius: 999px;
  padding: 8px 15px;
  cursor: pointer;
  opacity: 0;
  animation: plSkipIn 0.5s calc(1.1s * var(--k)) ease-out forwards;
}
.pl-skip:hover { border-color: var(--gold); color: var(--gold-soft); }
.pl-skip:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }
@keyframes plSkipIn { to { opacity: 0.72; } }

/* ============================ АКТ 1 — КОНСОЛЬ ============================ */

.pl-boot {
  position: absolute;
  inset: 0;
  z-index: 30;
  display: grid;
  place-items: center;
  padding: 20px;
  background: radial-gradient(680px 460px at 50% 46%, #150f26, #05040a 74%);
  animation: plBootGone 0.4s calc(1.8s * var(--k)) ease-out forwards;
}
@keyframes plBootGone { to { opacity: 0; visibility: hidden; } }

.pl-term {
  width: min(520px, 100%);
  border: 1px solid #ffffff26;
  border-radius: 12px;
  background: #0a0912f7;
  box-shadow: 0 40px 90px -30px #000, 0 0 0 1px #a855f71f, 0 0 90px -40px #a855f7;
  overflow: hidden;
  transform-origin: 50% 50%;
  animation: plTermOut 0.45s calc(1.7s * var(--k)) cubic-bezier(0.7, 0, 0.3, 1) forwards;
}
@keyframes plTermOut {
  60% { transform: scaleY(0.07) scaleX(1.03); opacity: 1; }
  100% { transform: scaleY(0.006) scaleX(0.18); opacity: 0; }
}

.pl-term__bar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 9px 13px;
  border-bottom: 1px solid #ffffff14;
  background: #ffffff05;
}
.pl-term__bar i { width: 8px; height: 8px; border-radius: 50%; background: #ffffff24; }
.pl-term__bar i:first-child { background: #f87171; }
.pl-term__bar i:nth-child(2) { background: #fbbf24; }
.pl-term__bar span { margin-left: 8px; font-family: var(--mono); font-size: 11px; color: #837e96; }
.pl-term__bar b {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #c4b5fd;
}

.pl-term__body {
  padding: 15px 17px 17px;
  font-family: var(--mono);
  font-size: clamp(10.5px, 1.5vw, 12.5px);
  line-height: 2;
  color: #cfc9de;
}
.pl-ln {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  white-space: nowrap;
  animation: plLine 0.28s calc(var(--d, 0s) * var(--k)) both;
}
@keyframes plLine { from { opacity: 0; transform: translateY(8px); } }
.pl-ln .pl-p { color: #c4b5fd; }
.pl-ln .pl-ok { color: #86efac; }
.pl-ln .pl-dots { flex: 1; border-bottom: 1px dotted #ffffff20; height: 10px; }
.pl-ln em { font-style: normal; color: #837e96; }
.pl-ln--done { color: var(--gold-soft); margin-top: 6px; }

.pl-caret {
  width: 8px;
  height: 13px;
  background: var(--gold);
  display: inline-block;
  animation: plBlink 0.85s steps(1, end) infinite;
}
@keyframes plBlink { 50% { opacity: 0; } }

.pl-bar {
  height: 4px;
  border-radius: 3px;
  background: #ffffff10;
  overflow: hidden;
  margin: 12px 0 5px;
}
.pl-bar i {
  display: block;
  height: 100%;
  border-radius: 3px;
  transform-origin: 0 50%;
  background: linear-gradient(90deg, #7c3aed, #a855f7 55%, var(--gold));
  animation: plFill calc(1.2s * var(--k)) calc(0.15s * var(--k)) cubic-bezier(0.5, 0.05, 0.2, 1) both;
}
@keyframes plFill { from { transform: scaleX(0); } to { transform: scaleX(1); } }

.pl-scan {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 2px;
  background: linear-gradient(90deg, transparent, #22d3ee, transparent);
  opacity: 0;
  animation: plScan 0.45s calc(1.72s * var(--k)) ease-out forwards;
}
@keyframes plScan {
  0% { opacity: 0; transform: scaleX(0.2); }
  45% { opacity: 1; transform: scaleX(1); }
  100% { opacity: 0; transform: scaleX(0.05); }
}

/* ============================ АКТ 2 — ЗАНАВЕС ============================ */

.pl-house {
  position: absolute;
  inset: 0;
  z-index: 5;
  background:
    radial-gradient(120% 80% at 50% 120%, #facc151f, transparent 62%),
    radial-gradient(90% 70% at 50% 40%, #1a0c26, #05040a 78%);
}

/* Рампа: тёплый свет снизу, как от софитов у авансцены. */
.pl-foot {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -6%;
  height: 34%;
  z-index: 14;
  background: radial-gradient(120% 100% at 50% 100%, #facc1533, #facc1510 42%, transparent 72%);
  opacity: 0;
  animation: plFootOn 1.4s calc(1.9s * var(--k)) ease-out forwards;
}
@keyframes plFootOn { to { opacity: 1; } }

/* Луч прожектора на логотип, с медленным покачиванием. */
.pl-beam {
  position: absolute;
  left: 50%;
  top: -14%;
  width: min(62vmin, 560px);
  height: 96%;
  z-index: 12;
  transform: translateX(-50%);
  background: linear-gradient(180deg, #fde68a26, #facc150f 42%, transparent 74%);
  clip-path: polygon(41% 0, 59% 0, 100% 100%, 0 100%);
  filter: blur(3px);
  opacity: 0;
  animation:
    plBeamOn 0.7s calc(1.95s * var(--k)) ease-out forwards,
    plBeamSway 5.2s calc(2.2s * var(--k)) ease-in-out infinite;
}
@keyframes plBeamOn { to { opacity: 1; } }
@keyframes plBeamSway {
  0%, 100% { transform: translateX(-50%) rotate(-0.8deg); }
  50% { transform: translateX(-50%) rotate(0.8deg); }
}

/* Пылинки в луче — то, что делает свет объёмным. */
.pl-dust { position: absolute; inset: 0; z-index: 13; pointer-events: none; }
.pl-dust i {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--s, 3px);
  height: var(--s, 3px);
  border-radius: 50%;
  background: #fde68a;
  opacity: 0;
  animation: plDust calc(var(--dur, 5s) * var(--k)) calc(var(--dl, 0s) * var(--k)) ease-in-out infinite;
}
@keyframes plDust {
  0% { opacity: 0; transform: translate3d(0, 0, 0); }
  30% { opacity: 0.55; }
  70% { opacity: 0.3; }
  100% { opacity: 0; transform: translate3d(14px, -70px, 0); }
}

/* --- полотнища --- */
.pl-cur {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 51.5%;
  z-index: 16;
  overflow: hidden;
  will-change: transform;
  box-shadow: 0 0 70px 20px #00000080;
}
.pl-cur--l {
  left: 0;
  animation: plCurtainL calc(1.8s * var(--k)) calc(2.6s * var(--k)) cubic-bezier(0.42, 0.02, 0.16, 1) forwards;
}
.pl-cur--r {
  right: 0;
  transform: scaleX(-1);
  animation: plCurtainR calc(1.8s * var(--k)) calc(2.6s * var(--k)) cubic-bezier(0.42, 0.02, 0.16, 1) forwards;
}
/* Вдох перед раскрытием: полотно чуть подаётся к центру, потом уходит за кулису. */
@keyframes plCurtainL {
  0% { transform: translateX(0); }
  13% { transform: translateX(1.4%); }
  100% { transform: translateX(-102%); }
}
@keyframes plCurtainR {
  0% { transform: scaleX(-1) translateX(0); }
  13% { transform: scaleX(-1) translateX(1.4%); }
  100% { transform: scaleX(-1) translateX(-102%); }
}

/* Ткань живёт отдельно от полотна: пока полотно уезжает, сама ткань сжимается
   к кулисе — складки уплотняются, и бархат читается собранным, а не съехавшей
   картинкой. Ради этого silk шире контейнера и обрезается им. */
.pl-cur__silk {
  position: absolute;
  top: -2%;
  bottom: -2%;
  left: 0;
  width: 104%;
  transform-origin: 0 50%;
  background:
    linear-gradient(180deg, #23092f 0 2%, transparent 12%),
    linear-gradient(180deg, #8b34a8 0%, #6a2184 24%, #4a1560 58%, #2a0c37 84%, #16051f 100%);
  animation: plSilkGather calc(1.8s * var(--k)) calc(2.6s * var(--k)) cubic-bezier(0.42, 0.02, 0.16, 1) forwards;
}
@keyframes plSilkGather {
  0%, 13% { transform: scaleX(1); }
  100% { transform: scaleX(0.46); }
}

/* Складки: мягкие переходы с несколькими промежуточными стопами — резкие полосы
   читаются как штрихкод, а не как бархат. */
.pl-cur__silk::before {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    90deg,
    #00000099 0px,
    #00000066 14px,
    #00000022 30px,
    #ffffff12 46px,
    #ffffff22 54px,
    #ffffff0d 64px,
    #00000040 82px,
    #00000099 104px
  );
  /* Складки едва заметно ходят — стоячая ткань выглядит нарисованной. */
  animation: plFolds 7s ease-in-out infinite;
}
@keyframes plFolds {
  0%, 100% { transform: translateX(0) scaleX(1); }
  50% { transform: translateX(7px) scaleX(1.012); }
}
/* Верхний блик и глубокая тень у пола — объём полотна. */
.pl-cur__silk::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, #ffffff1a, transparent 26%, #00000038 78%, #00000073 100%),
    radial-gradient(120% 46% at 50% 106%, #facc1526, transparent 64%);
}

/* Внутренний край: золотой кант, за ним бахрома с нитями. */
.pl-cur__edge {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  width: clamp(10px, 1.1vw, 16px);
  z-index: 3;
  background: linear-gradient(90deg, #00000000, #2a0d36 34%, var(--gold-deep) 62%, var(--gold) 80%, var(--gold-soft) 100%);
  box-shadow: 0 0 34px #facc1540;
}
.pl-cur__edge::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: -7px;
  width: 7px;
  background: repeating-linear-gradient(180deg, var(--gold) 0 9px, transparent 9px 20px);
  opacity: 0.42;
}

/* Вспышка света в щели ровно в момент, когда полотна тронулись. */
.pl-slit {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 3px;
  z-index: 15;
  transform: translateX(-50%);
  background: linear-gradient(180deg, transparent, var(--gold-soft) 30%, var(--gold) 70%, transparent);
  box-shadow: 0 0 60px 26px #facc1540;
  opacity: 0;
  animation: plSlit 0.9s calc(2.8s * var(--k)) ease-out forwards;
}
@keyframes plSlit {
  0% { opacity: 0; transform: translateX(-50%) scaleX(1); }
  30% { opacity: 1; transform: translateX(-50%) scaleX(6); }
  100% { opacity: 0; transform: translateX(-50%) scaleX(22); }
}

/* --- ламбрекен: пять фестонов с золотым кантом и кистями --- */
.pl-valance {
  position: absolute;
  top: 0;
  left: -1%;
  right: -1%;
  height: clamp(74px, 13vh, 148px);
  z-index: 18;
  display: flex;
  will-change: transform;
  animation: plValanceUp calc(1.6s * var(--k)) calc(2.8s * var(--k)) cubic-bezier(0.42, 0.02, 0.16, 1) forwards;
}
@keyframes plValanceUp { to { transform: translateY(-112%); } }

.pl-valance i {
  flex: 1;
  position: relative;
  margin-inline: -1.6%;
  border-radius: 0 0 46% 46% / 0 0 96% 96%;
  background:
    radial-gradient(130% 150% at 50% -30%, #a03fc0, #6a2184 46%, #3a1149 78%, #26082f 100%);
  border-bottom: 2px solid var(--gold);
  box-shadow: inset 0 -14px 22px #00000059, 0 14px 30px #00000073;
}
/* Карниз поверх фестонов — золотая штанга, на которой всё держится. */
.pl-valance::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 7px;
  background: linear-gradient(180deg, var(--gold-soft), var(--gold) 45%, var(--gold-deep));
  box-shadow: 0 3px 18px #facc1559;
}
.pl-tassel {
  position: absolute;
  top: calc(clamp(74px, 13vh, 148px) - 12px);
  z-index: 19;
  width: 11px;
  height: 30px;
  transform: translateX(-50%);
  border-radius: 50% 50% 46% 46% / 26% 26% 74% 74%;
  background: linear-gradient(180deg, var(--gold-soft), var(--gold) 35%, var(--gold-deep));
  box-shadow: 0 0 18px #facc1559;
  /* Свой кадр подъёма: общий plValanceUp затёр бы центровку translateX(-50%). */
  animation: plTasselUp calc(1.6s * var(--k)) calc(2.8s * var(--k)) cubic-bezier(0.42, 0.02, 0.16, 1) forwards;
}
@keyframes plTasselUp { to { transform: translateX(-50%) translateY(-320%); } }
.pl-tassel::before {
  content: "";
  position: absolute;
  left: 50%;
  top: -12px;
  width: 2px;
  height: 13px;
  transform: translateX(-50%);
  background: var(--gold-deep);
}

/* --- логотип на закрытом занавесе --- */
.pl-mark {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: clamp(8px, 1.4vh, 14px);
  text-align: center;
  font-family: var(--disp);
  color: #f6f1ff;
  padding: 20px;
  opacity: 0;
  animation:
    plMarkIn 0.55s calc(1.9s * var(--k)) ease-out forwards,
    plMarkOut 0.6s calc(2.6s * var(--k)) ease-in forwards;
}
.pl-mark i {
  font-style: normal;
  font-size: clamp(34px, 6vw, 58px);
  color: var(--gold);
  text-shadow: 0 0 34px #facc1599, 0 0 90px #facc1540;
  animation: plStar 3.4s calc(2s * var(--k)) ease-in-out infinite;
}
.pl-mark span {
  font-size: clamp(22px, 4.6vw, 44px);
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  text-shadow: 0 6px 40px #00000080;
}
.pl-mark small {
  font-size: clamp(9px, 1.5vw, 12px);
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: #e9d5ffb3;
}
@keyframes plMarkIn { from { opacity: 0; transform: translateY(12px) scale(0.97); } to { opacity: 1; } }
@keyframes plMarkOut { to { opacity: 0; transform: scale(1.14); filter: blur(4px); } }
@keyframes plStar {
  0%, 100% { transform: scale(1) rotate(0deg); }
  50% { transform: scale(1.09) rotate(8deg); }
}

/* Виньетка зала: тёмные углы собирают внимание в центр, к логотипу, и гаснут
   ровно тогда, когда полотна тронулись — дальше свет должен идти со сцены. */
.pl-intro--stage::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 17;
  pointer-events: none;
  background: radial-gradient(120% 92% at 50% 46%, transparent 36%, #000000b8 100%);
  animation: plVignetteOut 1s calc(2.9s * var(--k)) ease-out forwards;
}
@keyframes plVignetteOut { to { opacity: 0; } }

/* Слой уходит сам, даже если скрипт не отработал. */
.pl-intro--stage { animation: plIntroGone 0.35s calc(4.4s * var(--k)) ease-out forwards; }
@keyframes plIntroGone { to { opacity: 0; visibility: hidden; pointer-events: none; } }

/* ===================== ПИКСЕЛЬНЫЙ ПЕРЕХОД В КАБИНЕТ ===================== */

.pl-intro--blocks { background: #0b0a14; }

.pl-blocks {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  grid-template-rows: repeat(8, 1fr);
}
.pl-blk {
  background: #141024;
  border: 1px solid #ffffff0d;
  box-shadow: inset 0 0 22px #00000066;
  will-change: transform, opacity;
}
.pl-blk--p { background: #2b1a4d; }
.pl-blk--g { background: #3a2f12; }
.pl-blk--a { background: #10283a; }

/* Вход в кабинет: стена рассыпается диагональной волной. */
.pl-intro--blocks .pl-blk {
  animation: plBlkOut 0.5s calc(var(--d, 0ms) * var(--k)) cubic-bezier(0.5, 0, 0.3, 1) forwards;
}
@keyframes plBlkOut { to { opacity: 0; transform: scale(0.26) rotate(16deg); } }

.pl-intro--blocks .pl-mark {
  animation:
    plMarkIn 0.4s 0.05s ease-out forwards,
    plMarkOut 0.45s calc(0.45s * var(--k)) ease-in forwards;
}
.pl-intro--blocks { animation: plIntroGone 0.3s calc(1.5s * var(--k)) ease-out forwards; }

/* Уход на кабинет: та же стена, но собирается обратно. Оверлей создаёт
   js/intro.js по клику и держит до перехода на страницу. */
.pl-intro--out { animation: none; }
.pl-intro--out .pl-blk {
  opacity: 0;
  transform: scale(0.26) rotate(-16deg);
  animation: plBlkIn 0.42s calc(var(--d, 0ms) * 0.55) cubic-bezier(0.3, 0.8, 0.4, 1) forwards;
}
@keyframes plBlkIn { to { opacity: 1; transform: none; } }
.pl-intro--out .pl-mark { animation: plMarkIn 0.4s 0.32s ease-out forwards; }

/* ============================ АДАПТАЦИЯ ============================ */

/* На телефоне сцена та же, но короче: узкий экран занавес проходит быстрее,
   и лишняя секунда ожидания там заметнее. */
@media (max-width: 760px) {
  .pl-intro { --k: 0.78; }
  .pl-term { width: min(420px, 100%); }
  .pl-valance { height: clamp(58px, 9vh, 96px); }
  .pl-tassel { top: calc(clamp(58px, 9vh, 96px) - 12px); }
}

/* Ландшафтная мелочь вроде телефона боком — ламбрекен не должен съедать экран. */
@media (max-height: 460px) {
  .pl-valance { height: 54px; }
  .pl-tassel { top: 42px; }
}

/* Системная настройка «меньше движения»: сцены нет вовсе. Слой всё равно
   рендерится (разметка общая), поэтому просто убираем его из потока. */
@media (prefers-reduced-motion: reduce) {
  .pl-intro { display: none; }
}
