/* LAST ORBIT - 스테이지 레이아웃 */

#app {
  position: fixed; inset: 0;
  display: grid; place-items: center;
  background: radial-gradient(ellipse at 50% 40%, #0E1A2C 0%, #050A12 70%);
}

/* 16:9 스테이지. 뷰포트에 맞춰 균등 스케일 */
#stage {
  position: relative;
  width: min(100vw, calc(100vh * 16 / 9));
  height: min(100vh, calc(100vw * 9 / 16));
  overflow: hidden;
  background: #050A12;
  box-shadow: 0 0 90px rgba(0,0,0,.85);
  container-type: inline-size;
}

/* 레이어 스택 */
.layer { position: absolute; inset: 0; }
#layer-bg    { z-index: 10; }
#layer-char  { z-index: 20; }
#layer-cg    { z-index: 30; display: none; }
#layer-fx    { z-index: 40; pointer-events: none; }
#layer-ui    { z-index: 50; }
#layer-flash { z-index: 80; pointer-events: none; background: #000; opacity: 0; }
/* 모달은 페이드(80)·엔딩 카드(85)보다 위다. 엔딩은 항상 { fade: 'black' } 을
 * 건 채로 끝나므로, 모달이 그 아래 있으면 열어도 검은 화면만 보인다.
 * 토스트는 모달 위에 떠야 하므로 따로 88 에 둔다 (ui.css 의 #toast). */
#layer-modal { z-index: 86; }

/* 배경 */
#layer-bg .lo-bg-svg { position: absolute; inset: 0; width: 100%; height: 100%; }

/* 일러스트 배경 (scenes.js). SVG 쪽 preserveAspectRatio="xMidYMid slice" 와
 * 같은 배치를 object-fit 으로 맞춘다. */
.lo-bg-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; display: block;
}

/* 켄 번즈 — 정지 그림이 죽어 보이지 않게 아주 느리게 밀어 준다.
 * opacity 를 쓰지 않는다. transform 만 쓰고, 두 끝점 모두 scale > 1 이라
 * 애니메이션이 어디서 멈춰도 화면이 비지 않는다. */
@keyframes loKenBurns {
  from { transform: scale(1.045) translate3d(0, 0, 0); }
  to   { transform: scale(1.085) translate3d(-1.1%, .9%, 0); }
}
/* 그림과 fx 를 함께 감싸는 래퍼에 건다 — fx 가 그림과 같은 변환을 받아야
 * 실측 좌표가 켄 번즈 내내 붙어 있다 (QA-11) */
.lo-bg-art { position: absolute; inset: 0; }
.lo-bg-art.is-kb {
  transform: scale(1.045);
  animation: loKenBurns 28s ease-in-out infinite alternate;
  will-change: transform;
}

/* 그림 위에 얹는 움직이는 광원 (scenes.js 의 fx).
 * 별 반짝임 · 경광등 · 레이더 스윕이 여기로 옮겨 왔다. 이미지 파일은 쓰지 않는다. */
.lo-bg-fx { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.lo-fx {
  position: absolute; left: var(--x); top: var(--y);
  width: var(--s); aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  animation-delay: var(--dl);
}

/* 점멸 · 반짝임 · 맥동은 전부 opacity 로 하되 기본값을 보이는 쪽에 둔다.
 * 애니메이션이 돌지 않는 환경에서 base 스타일이 그대로 남기 때문이다
 * (fill-mode 를 쓰거나 기본을 0 으로 두면 광원이 영구히 사라진다). */
.lo-fx--blink, .lo-fx--twinkle, .lo-fx--glow {
  background: radial-gradient(circle closest-side, var(--c) 0%, transparent 70%);
  opacity: .9;
}
@keyframes loFxBlink   { 0%,100% { opacity: .95; } 55% { opacity: .12; } }
@keyframes loFxTwinkle { 0%,100% { opacity: .85; } 50% { opacity: .22; } }
@keyframes loFxGlow    { 0%,100% { opacity: .85; } 50% { opacity: .55; } }
.lo-fx--blink   { animation-name: loFxBlink;   animation-duration: var(--d);
                  animation-iteration-count: infinite; }
.lo-fx--twinkle { animation-name: loFxTwinkle; animation-duration: var(--d);
                  animation-iteration-count: infinite; }
.lo-fx--glow    { animation-name: loFxGlow;    animation-duration: var(--d);
                  animation-iteration-count: infinite; }

/* 레이더 스윕 — 원판은 그림에 그려져 있고 도는 선만 얹는다 */
.lo-fx--sweep { background: none; }
.lo-fx--sweep::before {
  content: ''; position: absolute;
  left: 50%; top: 0; bottom: 50%; width: 2px; margin-left: -1px;
  background: linear-gradient(to top, var(--c), transparent);
  transform-origin: 50% 100%;
  animation: loFxSweep var(--d) linear infinite;
  animation-delay: var(--dl);
}
@keyframes loFxSweep { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

/* 감속 모션 설정에서는 배경이 정지한다. 광원은 켜진 채로 남는다 */
@media (prefers-reduced-motion: reduce) {
  .lo-bg-art.is-kb { animation: none; }
  .lo-fx--blink, .lo-fx--twinkle, .lo-fx--glow { animation: none; }
  .lo-fx--sweep::before { animation: none; }
}

/* 시간대 파생 — 그림을 새로 그리지 않고 다른 시간대 것을 빌려 쓸 때만 걸린다.
 * 각 1회씩만 나오는 3차 티어 5조합용이라 정밀할 필요는 없고, 방을 다른 방으로
 * 보이게 하지 않는 선에서 색온도만 옮긴다. */
.lo-bg-img.is-graded[data-grade="day"] {
  filter: brightness(1.16) saturate(.92) hue-rotate(-6deg) contrast(.96);
}
.lo-bg-img.is-graded[data-grade="dusk"] {
  filter: brightness(1.02) saturate(1.1) sepia(.18) hue-rotate(-12deg);
}
.lo-bg-img.is-graded[data-grade="night"] {
  filter: brightness(.72) saturate(.86) hue-rotate(12deg) contrast(1.06);
}
.bg-slot {
  position: absolute; inset: 0;
  transition: opacity var(--t-slow) var(--ease);
}
.bg-slot.is-out { opacity: 0; }

/* 캐릭터 슬롯 */
.char-slot {
  position: absolute; bottom: 0;
  height: 96%;
  width: 42%;
  /* 슬롯은 bottom 고정이고 스탠딩은 발밑이 바닥에 닿아 있다.
   * 기본 원점(중심)으로 scale 하면 화자 강조(1.015)는 하단을 무대 밖으로 밀어
   * 잘리게 하고, 비화자 디밍(0.975)은 하단을 들어올려 바닥에서 뜨게 한다.
   * 발밑을 원점으로 잡아야 배율이 바뀌어도 접지선이 그대로 유지된다. */
  transform-origin: 50% 100%;
  transition: opacity var(--t-mid) var(--ease),
              transform var(--t-mid) var(--ease),
              filter var(--t-mid) var(--ease);
  will-change: transform, opacity;
}
.char-slot[data-at="left"]   { left: 2%;  }
.char-slot[data-at="center"] { left: 29%; }
.char-slot[data-at="right"]  { right: 2%; }
.char-slot[data-at="far_left"]  { left: -6%; }
.char-slot[data-at="far_right"] { right: -6%; }

.char-slot .lo-char-svg { width: 100%; height: 100%; display: block; }
/* PNG 스탠딩 (sprites.js). 어느 컨테이너에 놓여도 넘치지 않는 기본값 */
.lo-char-img {
  display: block; max-width: 100%; max-height: 100%;
}
/* 무대 위에서는 SVG 의 preserveAspectRatio="xMidYMax meet" 와 같은 배치를 맞춘다 */
.char-slot .lo-char-img {
  width: 100%; height: 100%;
  object-fit: contain; object-position: bottom center;
}

/* 표정 전환 디졸브 (stage.js 의 swapArt).
 * 옛 그림과 새 그림이 같은 자리에 겹쳐야 하므로 절대 배치한다. 배치 규칙
 * 자체는 위와 같고, 슬롯이 고정 크기라 보이는 결과도 평소와 동일하다. */
.char-slot > .lo-char-img,
.char-slot > .lo-char-svg {
  position: absolute; inset: 0;
  transition: opacity var(--t-pose) var(--ease);
}
/* 사라지는 쪽. 레이어를 띄워 두면 컴포지터가 투명도만 합성하므로 페이드 중에
 * 비트맵을 다시 래스터하지 않는다 — 여기서 재래스터가 생기면 없애려던
 * 자글거림이 사라지는 쪽에서 되돌아온다. */
.char-slot > .is-pose-out {
  opacity: 0;
  will-change: opacity;
}

.char-slot.is-enter { opacity: 0; transform: translateY(26px) scale(.97); }
.char-slot.is-dim {
  filter: brightness(.52) saturate(.62) blur(.4px);
  transform: scale(.975);
}
.char-slot.is-active { filter: brightness(1.04); transform: scale(1.015); }
.char-slot.is-leave { opacity: 0; transform: translateY(16px) scale(.98); }

/* 화면 흔들림 */
@keyframes shakeAnim {
  0%,100% { transform: translate(0,0); }
  15% { transform: translate(-9px, 5px); }
  30% { transform: translate(8px, -6px); }
  45% { transform: translate(-7px, -4px); }
  60% { transform: translate(6px, 6px); }
  75% { transform: translate(-4px, 2px); }
}
#stage.is-shaking { animation: shakeAnim .5s var(--ease); }

/* 엔딩 CG */
#layer-cg.is-on { display: block; }
.lo-cg { position: absolute; inset: 0; overflow: hidden; background: #04070E;
         container-type: inline-size; }
.lo-cg__bg { position: absolute; inset: 0; }
.lo-cg__bg .lo-bg-svg { width: 100%; height: 100%; }
.lo-cg__bg .lo-bg-img { width: 100%; height: 100%; }
.lo-cg__motes, .lo-cg__rays { position: absolute; inset: 0; width: 100%; height: 100%; }
.lo-cg__rays { mix-blend-mode: screen; opacity: .5; }
.lo-cg__char { position: absolute; bottom: -6%; height: 120%; width: 58%; }
.lo-cg__char .lo-char-svg { width: 100%; height: 100%; }
.lo-cg__char .lo-char-img { width: 100%; height: 100%; display: block;
  object-fit: contain; object-position: bottom center; }
.lo-cg__char--left   { left: 1%; }
.lo-cg__char--center { left: 24%; }
.lo-cg__char--right  { right: 1%; }
/* 일러스트 CG (cg-images.js). 합성 CG 와 같은 .lo-cg 안에 놓이므로
 * 자막(.lo-cg__caption)의 컨테이너 쿼리 폰트 크기를 그대로 물려받는다. */
.lo-cg-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; display: block;
}

.lo-cg__grade { position: absolute; inset: 0; mix-blend-mode: soft-light; }
.lo-cg__grade--cyan   { background: linear-gradient(120deg, rgba(79,195,247,.55), transparent 60%); }
.lo-cg__grade--violet { background: linear-gradient(120deg, rgba(179,157,219,.55), transparent 60%); }
.lo-cg__grade--amber  { background: linear-gradient(300deg, rgba(255,183,77,.55), transparent 60%); }
/* 같은 CG 를 긴 장면에 걸쳐 유지할 때 자막만 끈다 (stage.showCG 의 noCap) */
#layer-cg.no-cap .lo-cg__caption { display: none; }
.lo-cg__caption {
  position: absolute; left: 0; right: 0; bottom: 6%;
  text-align: center; font-size: 2.8cqw; letter-spacing: .06em;
  color: #F2F8FF; text-shadow: 0 2px 18px rgba(0,0,0,.95), 0 0 40px rgba(0,0,0,.8);
}

/* 플래시 / 페이드 */
#layer-flash { transition: opacity var(--t-slow) var(--ease); }
#layer-flash.is-black { opacity: 1; }
#layer-flash.is-white { background: #fff; opacity: 1; }

/* 페이드 위에 그려지는 최상위 연출 레이어 */
#layer-top { z-index: 85; pointer-events: none; }
#layer-top #endcard.is-on { pointer-events: auto; }
