/* Stella Ball — Dawn pass (2026-08-12)
   stella-ball-theme.css 뒤에 로드. 픽셀 버튼 킷(data-pbtn) 지원과 배경 데코만 담당한다.
   전투 물리·판정색에는 관여하지 않는다. 문서: ../UI_KIT_DAWN.md */

main {
  position: relative;
  z-index: 1;
}

/* 픽셀 버튼: 테마의 !important 배경/테두리를 무력화하고 킷 캔버스(--pbtn-img)를 입힌다 */
[data-pbtn],
.hub-tab[data-pbtn],
.hub-tab.center[data-pbtn] {
  border: 0 !important;
  border-radius: 0 !important;
  background: transparent !important;
  background-image: var(--pbtn-img) !important;
  background-size: 100% 100% !important;
  box-shadow: none !important;
  image-rendering: pixelated;
  filter: drop-shadow(0 4px 0 #07020c);
}
[data-pbtn]:hover,
.hub-tab[data-pbtn]:hover,
.hub-tab.center[data-pbtn]:hover {
  border: 0 !important;
  background: transparent !important;
  background-image: var(--pbtn-img) !important;
  background-size: 100% 100% !important;
  box-shadow: none !important;
}
[data-pbtn="cta"],
[data-pbtn="tabActive"] {
  color: #fff6e6 !important;
  text-shadow:
    -1px 0 0 #6e3616,
    1px 0 0 #6e3616,
    0 -1px 0 #6e3616,
    0 2px 0 #6e3616 !important;
}
[data-pbtn="sub"],
[data-pbtn="tab"] {
  color: #ede9f5 !important;
  text-shadow: 0 2px 0 #07020c !important;
}
[data-pbtn="chip"] {
  color: #bfb8ce !important;
}
[data-pbtn="moon"] {
  color: #4a3a1a !important;
}
[data-pbtn="star"] {
  color: #4a2408 !important;
  text-shadow: 0 1px 0 #ffe9a8 !important;
}
[data-pbtn] img {
  filter: saturate(0.9) drop-shadow(0 1px 0 #07020c) !important;
}

/* 배경 데코 — main 아래(z 0), 여백에서만 클릭을 받는다 */
#dawn-sky {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
}
@keyframes dawnTwinkle {
  0%,
  100% {
    opacity: 0.2;
  }
  50% {
    opacity: 1;
  }
}
@keyframes dawnFloat {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-14px);
  }
}
@keyframes dawnAurora {
  0% {
    transform: translateX(-50px) skewX(-10deg);
  }
  100% {
    transform: translateX(50px) skewX(-2deg);
  }
}
@keyframes dawnMeteor {
  0% {
    transform: translateX(0);
    opacity: 0;
  }
  10% {
    opacity: 1;
  }
  100% {
    transform: translateX(70vw);
    opacity: 0;
  }
}
@keyframes dawnPop {
  0% {
    transform: translate(-50%, -50%) scale(0.4);
    opacity: 1;
  }
  100% {
    transform: translate(-50%, -50%) scale(1.7);
    opacity: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  #dawn-sky * {
    animation: none !important;
  }
}

/* 전투는 중앙 캔버스 자체가 계속 움직인다. 그 뒤의 6~7분짜리 성운·오로라와
   장식물 호흡까지 매 디스플레이 프레임 스타일 계산을 할 필요는 없다. 정지한
   하늘은 그대로 남기고, 전투를 떠나면 브라우저가 같은 위치에서 재개한다.

   «이름만» 부분일치로 잡는다. cssText로 넣은 animation:dawnFloat 6s ...를
   브라우저가 animation: 6s ease-in-out infinite dawnFloat처럼 이름을 뒤로
   보내 재직렬화하므로, "animation: dawnFloat" 접두 일치는 한 번도 맞은 적이
   없었다 — 실측으로 game-mode에서 여덟 개가 전부 running이었다. */
.game-mode #dawn-sky [style*="skyDrift"],
.game-mode #dawn-sky [style*="dawnFloat"],
.game-mode #dawn-sky [style*="dawnAurora"],
.game-mode #dawn-sky [style*="skyCloud"] {
  animation-play-state: paused !important;
}

/* 킷 금속 아이콘 — stella-ball-dawn.js가 --px-* 변수를 등록하면
   자리 표시용 CSS 원형 코인과 프로필 패널 글리프를 스프라이트로 바꾼다 */
.pixel-kit-icons .hub-gold b::before,
.pixel-kit-icons .result-gold i {
  width: 14px;
  height: 14px;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: var(--px-coin) center / contain no-repeat;
  image-rendering: pixelated;
}
.pixel-kit-icons .result-gold i {
  width: 18px;
  height: 18px;
}
.pixel-kit-icons .profile-grid .panel-title h2 {
  display: flex;
  align-items: center;
  gap: 7px;
}
.pixel-kit-icons .profile-grid .panel-title h2::before {
  content: "";
  flex: 0 0 auto;
  width: 21px;
  height: 21px;
  background: var(--px-icon, none) center / contain no-repeat;
  image-rendering: pixelated;
}
.pixel-kit-icons .profile-grid .profile-panel:nth-child(1) .panel-title h2 {
  --px-icon: var(--px-emblem);
}
.pixel-kit-icons .profile-grid .profile-panel:nth-child(2) .panel-title h2 {
  --px-icon: var(--px-rank);
}
.pixel-kit-icons .profile-grid .mail-panel .panel-title h2 {
  --px-icon: var(--px-mail);
}

/* ── sky-ambience.js 전용 키프레임 (design session, 2026-08-15) ──
   L0 성운의 장주기 드리프트 2종과 흘러가는 구름. 반짝임·유성은
   기존 dawnTwinkle / dawnMeteor를 그대로 재사용한다. 새 레이어가
   #dawn-sky의 자식이므로 모션 감소는 기존 `#dawn-sky *` 규칙에 걸린다. */
@keyframes skyDriftA {
  0%,
  100% {
    transform: translate(0, 0);
  }
  50% {
    transform: translate(26px, -14px);
  }
}
@keyframes skyDriftB {
  0%,
  100% {
    transform: translate(0, 0);
  }
  50% {
    transform: translate(-22px, 16px);
  }
}
@keyframes skyCloud {
  0% {
    transform: translateX(0);
    opacity: 0;
  }
  12% {
    opacity: 1;
  }
  88% {
    opacity: 1;
  }
  100% {
    transform: translateX(34vw);
    opacity: 0;
  }
}

/* ---------------------------------------------------------------------
   STARLIGHT IGNITION — 타이틀 (INTRO_REDESIGN_HANDOFF.md §2-2)
   이 블록이 스타일시트 «뒤쪽»에 있는 이유: 예전 타이틀 규칙이 네 파일
   (interface·polish·story·theme)에 흩어져 있어 특정도로 싸우면 어느 파일이
   이겼는지 아무도 추적할 수 없다. 새 이름(`ig-*`)을 쓰고 뒤쪽에 두면
   싸울 일이 없다. (2026-08-22부터 마지막 자리는 stella-ball-upgrade.css
   이지만, 그 파일은 ig-* 를 건드리지 않으므로 이 논리는 그대로다.) 걷어낸 옛 요소(.title-stars/.title-constellation/
   .title-copy)는 더 이상 만들어지지 않으므로 그 규칙들은 자연히 죽는다.
   --------------------------------------------------------------------- */
.ig-title {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  overflow: hidden;
}
.ig-keyart {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  opacity: 0.92;
  image-rendering: pixelated;
  pointer-events: none;
}
.ig-copy {
  position: relative;
  display: grid;
  justify-items: center;
  margin-bottom: 3vh;
  padding: 0 24px;
  text-align: center;
}
/* 성도. 선과 별을 골드로만 그린다 — §1-1의 「골드는 조준 대상·보상·CTA·
   별자리에만」 규칙에서 별자리가 여기다. */
.ig-map {
  width: min(250px, 22vh);
  margin-bottom: 6px;
  overflow: visible;
  opacity: 0;
  animation: igFade 0.8s ease 1s forwards;
}
.ig-map line {
  stroke: var(--star, #ffd98e);
  stroke-width: 1.6;
  opacity: 0.8;
  filter: drop-shadow(0 0 6px #ffd98e);
}
.ig-map circle {
  fill: #fff5c9;
  filter: drop-shadow(0 0 8px #ffe6a5);
}
.ig-map .ig-map-mark {
  fill: none;
  stroke: var(--magenta-dim, #c94ff0);
  stroke-opacity: 0.4;
  filter: none;
}
.ig-chart {
  display: flex;
  align-items: center;
  gap: 13px;
  width: min(430px, 80vw);
  margin-bottom: 20px;
  opacity: 0;
  animation: igFade 0.5s ease 0.8s forwards;
}
.ig-chart i {
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, transparent, #c94ff06e, transparent);
}
.ig-chart small {
  color: #e6c9f5;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.3em;
  white-space: nowrap;
  text-shadow: 0 0 10px #c94ff055;
}
.ig-wm-wrap {
  position: relative;
}
/* 원본 328×224, 8px 유닛. 492px는 정확히 1.5배라 8px 유닛이 12px로 떨어진다 —
   시안의 440px는 1.34배여서 유닛 폭이 들쭉날쭉해진다. */
.ig-wm {
  width: min(492px, 66vw, 46vh);
  opacity: 0;
  image-rendering: pixelated;
  animation:
    igWmSlam 0.6s cubic-bezier(0.2, 0.85, 0.25, 1.1) 0.1s both,
    igWmGlow 4s ease-in-out 1.5s infinite;
}
.ig-ring,
.ig-ring2 {
  position: absolute;
  left: 50%;
  top: 50%;
  border-radius: 50%;
  opacity: 0;
  transform: translate(-50%, -50%);
  pointer-events: none;
}
.ig-ring {
  width: 560px;
  height: 560px;
  border: 2px solid #ffd98e77;
  animation: igRing 1s cubic-bezier(0.3, 0.1, 0.2, 1) 0.3s both;
}
.ig-ring2 {
  width: 640px;
  height: 640px;
  border: 2px dashed #c94ff055;
  animation: igRing 1.3s cubic-bezier(0.3, 0.1, 0.2, 1) 0.45s both;
}
.ig-dust {
  position: absolute;
  left: 50%;
  top: 56%;
  width: 0;
  height: 0;
  pointer-events: none;
}
.ig-dust i {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--dz, 3px);
  height: var(--dz, 3px);
  animation: igDust 0.6s ease-out forwards;
}
.ig-kicker {
  margin: min(20px, 2.2vh) 0 min(30px, 3.2vh);
  padding-top: min(14px, 1.5vh);
  border-top: 1px solid #c94ff03a;
  color: var(--ink-dim, #b9a8d6);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.3em;
  white-space: nowrap;
  opacity: 0;
  animation: igFade 0.5s ease 0.95s forwards;
}
.ig-cta {
  position: relative;
  min-width: 320px;
  padding: 17px 44px;
  overflow: hidden;
  border: 2px solid #f6c48e !important;
  border-radius: 0 !important;
  background: linear-gradient(#f0b078, #a05c32) !important;
  color: #241304 !important;
  font:
    900 19px "Galmuri11",
    sans-serif !important;
  letter-spacing: 0.05em;
  opacity: 0;
  transform: translateY(12px);
  box-shadow:
    0 4px 0 #4a2a16,
    0 0 0 3px #f2b9881a;
  cursor: pointer;
  /* igCtaGlow(box-shadow 2.6s 무한)를 걷었다 — 2026-08-23 실측. 픽셀 버튼
     킷이 들어온 뒤 `.ig-cta[data-pbtn]`의 `box-shadow: none !important`가
     키프레임을 이겨(CSS 캐스케이드: !important 저자 선언 > 애니메이션)
     0.3초 간격 12표본 전부 계산값이 `none` 한 값이었다. 즉 «보이는 것은
     하나도 없이» 매 프레임 스타일 재계산만 시켰다: 4초 창 recalc 165.6ms
     → 66ms(-60%), task 549 → 385ms. 킷을 걷어 box-shadow가 다시 살아나면
     그때는 opacity 애니메이션 오버레이로 되살린다(합성 가능한 속성). */
  animation: igFadeUp 0.42s cubic-bezier(0.18, 0.9, 0.22, 1.2) 1.1s forwards;
}
.ig-cta:hover {
  filter: brightness(1.12);
}
.ig-cta > i {
  position: absolute;
  inset: 3px;
  background: linear-gradient(90deg, transparent, #fff6be77, transparent);
  transform: translateX(-130%);
  animation: igCtaShine 2.6s ease 1.8s infinite;
  pointer-events: none;
}
.ig-sub {
  display: flex;
  gap: 26px;
  margin-top: 24px;
  opacity: 0;
  transform: translateY(8px);
  animation: igFadeUp 0.4s ease 1.35s forwards;
}
.ig-link {
  border: 0 !important;
  background: none !important;
  box-shadow: none !important;
  color: #c6b8e0 !important;
  font:
    700 11px "Galmuri11",
    sans-serif !important;
  white-space: nowrap;
  cursor: pointer;
}
.ig-link:hover {
  color: #fff !important;
}
.ig-link b {
  color: #ffe3a1;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.ig-credit {
  margin-top: 22px;
  color: #6f6390;
  font-size: 8px;
  letter-spacing: 0.32em;
  opacity: 0;
  animation: igFade 0.5s ease 1.55s forwards;
}
.ig-credit b {
  color: #c9b489;
  font-weight: 600;
}
.ig-flash {
  position: absolute;
  inset: 0;
  z-index: 5;
  background: #fff3d6;
  animation: igFlash 0.2s steps(2) forwards;
  pointer-events: none;
}
@keyframes igWmSlam {
  0% {
    opacity: 0;
    transform: scale(2.1);
  }
  52% {
    opacity: 1;
    transform: scale(0.92);
  }
  74% {
    transform: scale(1.05);
  }
  100% {
    opacity: 1;
    transform: scale(1);
  }
}
@keyframes igWmGlow {
  0%,
  100% {
    filter: drop-shadow(0 0 8px #ffeec6aa) drop-shadow(0 0 26px #f4cd7877);
  }
  50% {
    filter: drop-shadow(0 0 12px #fff8e6) drop-shadow(0 0 40px #ffd98ecc);
  }
}
@keyframes igRing {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.5);
  }
  18% {
    opacity: 0.7;
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.5);
  }
}
@keyframes igDust {
  0% {
    opacity: 0.95;
    transform: translate(0, 0);
  }
  100% {
    opacity: 0;
    transform: translate(var(--dx), var(--dy));
  }
}
@keyframes igFade {
  to {
    opacity: 1;
  }
}
@keyframes igFadeUp {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
/* @keyframes igCtaGlow 가 여기 있었다 — 위 .ig-cta 주석 참고(보이지 않는
   애니메이션이라 걷었다, 2026-08-23). */
@keyframes igCtaShine {
  0%,
  52% {
    transform: translateX(-130%);
  }
  78%,
  100% {
    transform: translateX(130%);
  }
}
@keyframes igFlash {
  0% {
    opacity: 0.92;
  }
  100% {
    opacity: 0;
  }
}
/* 움직임을 줄이면 각 장면의 «최종 정지 프레임»만 보여준다(§1-3). */
.ig-title.ig-still .ig-map,
.ig-title.ig-still .ig-chart,
.ig-title.ig-still .ig-wm,
.ig-title.ig-still .ig-kicker,
.ig-title.ig-still .ig-cta,
.ig-title.ig-still .ig-sub,
.ig-title.ig-still .ig-credit {
  opacity: 1;
  transform: none;
  animation: none;
}
@media (prefers-reduced-motion: reduce) {
  .ig-map,
  .ig-chart,
  .ig-wm,
  .ig-kicker,
  .ig-cta,
  .ig-sub,
  .ig-credit {
    opacity: 1;
    transform: none;
    animation: none;
  }
  .ig-ring,
  .ig-ring2,
  .ig-flash {
    display: none;
  }
}

/* ---------------------------------------------------------------------
   STARLIGHT IGNITION — 프롤로그 (§2-3)
   하늘 비트는 줄이 넘어갈 때마다 클래스가 «쌓인다». 별이 꺼진 자리에 공허가
   고이고 그 위로 유성이 떨어지는 순서라, 앞 비트를 지우면 네 문장이 서로
   이어지지 않는다.
   --------------------------------------------------------------------- */
.pro-scene {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  padding: 0 24px;
  text-align: center;
  cursor: pointer;
}
/* 등장은 애니메이션이 아니라 트랜지션으로 한다. 애니메이션은 프레임이
   멈춘 창(document.hidden)에서 0% 키프레임에 못 박히므로, 「보여야 하는 것」이
   영영 안 보인다 — 오늘 교습 안내에서 같은 실수를 한 번 했다
   (prototypes/MAINTENANCE.md의 함정 목록). 트랜지션은 멈춰도 최종값이 남는다. */
.pro-bar {
  position: fixed;
  left: 0;
  right: 0;
  height: 6.5vh;
  background: #03020c;
  transform: translateY(0);
  transition: transform 0.5s cubic-bezier(0.3, 0.1, 0.2, 1);
  pointer-events: none;
}
.pro-bar-t {
  top: 0;
}
.pro-bar-b {
  bottom: 0;
}
.pro-sky {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
/* 별이 꺼진다 — 하늘 전체를 눌러 「빛이 줄었다」를 만든다. 개별 별을 끄는
   것은 #dawn-sky가 자기 캔버스에서 하므로 여기서 흉내 내지 않는다. */
.pro-scene.pro-dim .pro-sky {
  background: radial-gradient(ellipse at 50% 40%, #03020c00, #03020cc9 78%);
  transition: background 1.4s ease-out;
}
.pro-pool,
.pro-meteor,
.pro-wake {
  position: absolute;
  opacity: 0;
}
/* 공허가 고인다 — 반지름 0에서 190px까지 1.5초(§2-3). */
.pro-pool {
  left: 50%;
  top: 46%;
  width: 380px;
  height: 380px;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    #7e2bb066,
    #2a144233 52%,
    transparent 72%
  );
  transform: translate(-50%, -50%) scale(0);
}
.pro-scene.pro-pool .pro-pool {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
  transition:
    transform 1.5s cubic-bezier(0.2, 0.7, 0.3, 1),
    opacity 0.6s ease-out;
}
/* 유성이 떨어지고, 닿은 자리에서 별지기가 깨어난다. */
.pro-meteor {
  left: 50%;
  top: 0;
  width: 5px;
  height: 34px;
  border-radius: 3px;
  background: linear-gradient(#ffd98e00, #ffd98e);
  transform: translate(-50%, -60px) rotate(14deg);
}
.pro-scene.pro-wake .pro-meteor {
  animation: proFall 1s cubic-bezier(0.5, 0, 0.85, 0.4) both;
}
.pro-wake {
  left: 50%;
  top: 46%;
  width: 96px;
  height: 96px;
  border: 2px solid #ffd98e88;
  border-radius: 50%;
  transform: translate(-50%, -50%) scale(0.4);
}
.pro-scene.pro-wake .pro-wake {
  animation: proWake 0.9s cubic-bezier(0.2, 0.85, 0.3, 1) 0.95s both;
}
/* 별자리가 기억을 되찾는다 — 두 선이 그려진다. */
.pro-links {
  position: absolute;
  left: 50%;
  top: 34%;
  width: min(320px, 46vw);
  transform: translate(-50%, -50%);
  opacity: 0;
  overflow: visible;
}
.pro-links line {
  stroke: #ffd98e;
  stroke-width: 2;
  stroke-dasharray: 120;
  stroke-dashoffset: 120;
  filter: drop-shadow(0 0 6px #ffd98e);
}
.pro-scene.pro-link .pro-links {
  opacity: 1;
  transition: opacity 0.4s ease-out;
}
.pro-scene.pro-link .pro-links line {
  animation: proDraw 0.9s ease-out both;
}
.pro-scene.pro-link .pro-links line:nth-child(2) {
  animation-delay: 0.5s;
}
.pro-kicker {
  margin-bottom: 14px;
  color: var(--magenta-dim, #c94ff0);
  font:
    700 11px "Galmuri11",
    monospace;
  letter-spacing: 0.3em;
  text-shadow: 0 0 12px #c94ff055;
}
.pro-line {
  max-width: 640px;
  min-height: 3.4em;
  color: var(--ink, #fdf6e8);
  font-size: 22px;
  line-height: 1.6;
  text-shadow:
    0 2px 0 #0a0418,
    0 0 18px #ffd98e33;
}
.pro-line b {
  color: #ffd98e;
  font-weight: 700;
}
.pro-line b.void {
  color: var(--magenta-dim, #c94ff0);
}
/* 기본이 보이는 상태이고, 새 줄이 들어올 때만 잠깐 아래에서 떠오른다.
   반대로 만들면(기본 투명 + 등장 시 켜기) 위와 같은 이유로 위험하다. */
.pro-line {
  opacity: 1;
  transform: none;
  transition: transform 0.5s ease-out;
}
/* 등장에서 opacity는 «전혀» 건드리지 않는다. 움직임만 준다.
   투명도로 등장을 만들면 프레임이 멈춘 순간 글자가 사라지고, 그건 연출
   실패가 아니라 읽을 수 없는 화면이다. 움직임이 멈추면 그냥 제자리에 있다. */
.pro-line.pro-enter {
  transform: translateY(10px);
  transition: none;
}
.pro-hint {
  margin-top: 34px;
  color: #6f6390;
  font-size: 10px;
  letter-spacing: 0.26em;
  animation: proHint 2.4s ease-in-out infinite;
}
@keyframes proFall {
  0% {
    opacity: 0;
    transform: translate(-50%, -60px) rotate(14deg);
  }
  12% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(-50%, 46vh) rotate(14deg);
  }
}
@keyframes proWake {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.4);
  }
  30% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.9);
  }
}
@keyframes proDraw {
  to {
    stroke-dashoffset: 0;
  }
}
@keyframes proHint {
  0%,
  100% {
    opacity: 0.35;
  }
  50% {
    opacity: 0.85;
  }
}
@media (prefers-reduced-motion: reduce) {
  .pro-bar,
  .pro-line,
  .pro-hint,
  .pro-meteor,
  .pro-wake {
    animation: none;
  }
  .pro-line {
    opacity: 1;
  }
  .pro-scene.pro-link .pro-links line {
    stroke-dashoffset: 0;
    animation: none;
  }
}

/* 컷신이 도는 동안 타이틀 문안을 감춘다(§2-2는 리빌을 「컷신 종료 직후」로
   둔다). 관측소 실루엣과 하늘은 그대로 둔다 — 그건 연출의 무대이지 UI가
   아니다. 감추는 쪽이므로 프레임이 멈춰도 위험하지 않다: 멈추면 계속 감춰져
   있을 뿐이고, 드러내는 것은 클래스 제거라 즉시 적용된다. */
/* !important와 animation:none이 둘 다 필요하다. 리빌 애니메이션은 forwards로
   opacity:1을 «채워» 두는데, 채움값은 보통 선언보다 캐스케이드에서 위다 —
   그래서 첫 실행(리빌을 미뤄 애니메이션이 아직 안 돈 상태)에서는 숨김이
   통하고 「인트로 다시 보기」(이미 한 번 돈 상태)에서는 통하지 않았다. */
body.oo-intro .ig-map,
body.oo-intro .ig-chart,
body.oo-intro .ig-wm-wrap,
body.oo-intro .ig-kicker,
body.oo-intro .ig-cta,
body.oo-intro .ig-credit,
body.oo-intro .ig-wm {
  animation: none !important;
  opacity: 0 !important;
  pointer-events: none;
}
/* 서브 링크는 «보이지 않게 하되 누를 수는 있게» 둔다. opacity:0인 요소도
   히트 테스트는 받으므로, 원래 코드가 「튜토리얼 버튼은 잠그지 않는다」고
   못 박아 둔 탈출구와 온보딩 E2E의 클릭이 모두 살아 있다.
   0.55로 남겨 뒀더니 컷신 캡션 바로 위에 겹쳐 떠서, 로고만 사라지고 UI는
   그대로라는 제보를 받았다. pointer-events는 건드리지 않는다.

   2026-08-23: 키아트(.ig-keyart, 화면 중앙 y≈469)와 부제 문안(.ig-copy,
   「ARIES · 양자리 관측 항로」)이 숨김 목록에서 빠져 인트로 내내 유효
   opacity 0.92~1.0으로 비쳤다 — 「인트로 때 UI가 희미하게 남아 부자연스
   럽다」는 제보의 정체다. 같은 규칙에 넣어 감춘다. ig-copy 안의 「1분
   튜토리얼」 링크는 opacity:0이어도 히트 테스트를 받으므로 탈출구와
   온보딩 E2E의 클릭이 그대로 살아 있다(pointer-events 미변경). */
/* 2026-08-24: 이 묶음에 pointer-events도 넣는다.

   앞서는 opacity만 0으로 두고 클릭은 살려 두었다 — 「1분 튜토리얼」이
   outer-observer.js가 명시한 탈출구이고 온보딩 E2E도 그것을 누른다는
   이유였다. 그런데 그 버튼은 화면에서 «컷신 자막 바로 밑»에 있다. 실측:
   자막 「ANOMALY」·「OBSERVATION LOG — 04:17」의 중심에서
   elementFromPoint가 #titleHelp를 돌려준다. 자막 레이어는 전부
   pointer-events:none이라, 자막을 누르면 그 밑의 보이지 않는 버튼이
   눌린다 — 연출이 통째로 사라지고 곧장 수업이 시작된다. 오너 제보
   「인트로 중에 anomaly 누르니까 바로 온보딩으로 감」이 이것이다.

   보이지 않는 버튼이 위에 있는 것을 대신 받으면 탈출구가 아니라 함정이다.
   같은 이유로 「인트로 다시 보기」는 이미 막혀 있었고, 「1분 튜토리얼」만
   남아 있었다. 탈출구는 그대로 있다 — 「건너뛰기」가 1.0초부터 항상
   눌리고(z-index 70) 누르면 즉시 타이틀로 간다. E2E도 2026-08-23에
   「인트로가 풀린 뒤에 누른다」로 이미 고쳤다(ca5df2d). */
body.oo-intro .ig-sub,
body.oo-intro .ig-keyart,
body.oo-intro .ig-copy {
  animation: none !important;
  opacity: 0 !important;
  pointer-events: none;
}

/* 컷신 동안에는 판 자체를 비운다. 문안만 감췄더니 「글은 사라져도 UI 창이
   남아 괴물을 가린다」는 제보가 왔다 — 실제로 .stage(불투명 배경 + 테두리)와
   .overlay(95% 불투명)가 화면 가운데 920×700 상자로 남아 있었고, 존재는
   설계상 그 «뒤»(main z-index 1 뒤)를 지나가므로 통째로 가려졌다.
   존재를 앞으로 끌어내는 대신 판을 비운다 — 「존재는 중앙 UI 위로 오지
   않는다」는 원래 제약을 깨지 않으면서 하늘 전체가 무대가 된다. */
body.oo-intro .stage {
  background: transparent !important;
  border-color: transparent !important;
  box-shadow: none !important;
}
/* 2026-08-24: 의사요소도 함께 비운다. 위 규칙은 .stage «본체»만 손대는데,
   판의 액자는 실제로 둘이 더 그린다 —
     .stage::before  1px 테두리 + inset 0 0 44px #b46ed716 (안쪽 보라 글로)
     .stage::after   radial-gradient 비네트(!important)
   그래서 배경을 지워도 화면 가운데에 920x700 둥근 사각형이 «희미하게»
   남았다. 오너 제보 「인트로 때 그 메인 UI 네모가 희미하게 남아있음」이
   이것이다. .title-sequence 쪽은 의사요소까지 이미 처리돼 있었는데 판만
   빠져 있었다. ::after 의 배경이 !important 라 이쪽도 !important 가 필요하다. */
body.oo-intro .stage::before,
body.oo-intro .stage::after {
  background: transparent !important;
  background-image: none !important;
  border-color: transparent !important;
  box-shadow: none !important;
}
body.oo-intro .overlay.title-scene,
body.oo-intro .title-sequence,
body.oo-intro .title-sequence::before,
body.oo-intro .title-sequence::after {
  background: transparent !important;
}

/* ---------------------------------------------------------------------
   STARLIGHT IGNITION — 전투 입장 (§2-4)
   캔버스가 그리는 거상 강하·별지기 맺힘 위에 DOM으로 얹는다. 비트 클래스는
   frameClock으로만 켜지므로(game-session.js) 캔버스와 어긋나지 않는다.
   전부 `main` 안이라 여백 소품과 HUD 바깥을 침범하지 않는다.
   --------------------------------------------------------------------- */
.cin {
  position: absolute;
  inset: 0;
  z-index: 3;
  overflow: hidden;
  pointer-events: none;
}
.cin-bar {
  position: absolute;
  left: 0;
  right: 0;
  height: 8%;
  background: #03020c;
  transform: translateY(var(--from, 0));
  transition: transform 0.5s cubic-bezier(0.3, 0.1, 0.2, 1);
}
.cin-bar-t {
  top: 0;
  --from: -100%;
}
.cin-bar-b {
  bottom: 0;
  --from: 100%;
}
.cin.is-bars .cin-bar {
  --from: 0;
}
.cin.is-open .cin-bar-t {
  --from: -100%;
}
.cin.is-open .cin-bar-b {
  --from: 100%;
}
/* 전장만 어둡게. 거상이 내려오는 동안 판이 한 톤 죽어 있어야 착지의 플래시가
   산다 — 밝은 판에 흰 플래시를 터뜨리면 아무 일도 안 일어난 것처럼 보인다. */
.cin-veil {
  position: absolute;
  inset: 0;
  background: #05010f;
  opacity: 0;
  transition: opacity 0.45s ease-out;
}
.cin.is-bars .cin-veil {
  opacity: 0.42;
}
.cin.is-open .cin-veil {
  opacity: 0;
}
.cin-flash {
  position: absolute;
  inset: 0;
  background: #fff3d6;
  opacity: 0;
}
.cin.is-land .cin-flash {
  animation: cinFlash 0.2s steps(2) forwards;
}
/* 착지 링 두 겹. 골드가 먼저, 마젠타가 엇박으로 뒤따른다(§1-3 충격 3종 세트). */
.cin-ring {
  position: absolute;
  left: 50%;
  top: 26%;
  width: 90px;
  height: 90px;
  border-radius: 50%;
  opacity: 0;
  transform: translate(-50%, -50%);
}
.cin-ring-a {
  border: 3px solid #ffd98eaa;
}
.cin-ring-b {
  border: 2px dashed #c94ff088;
}
.cin.is-land .cin-ring-a {
  animation: cinRing 0.55s cubic-bezier(0.2, 0.7, 0.3, 1) both;
}
.cin.is-land .cin-ring-b {
  animation: cinRing 0.55s cubic-bezier(0.2, 0.7, 0.3, 1) 0.09s both;
}
.cin-dust {
  position: absolute;
  left: 50%;
  top: 26%;
  width: 0;
  height: 0;
}
.cin-dust i {
  position: absolute;
  width: 3px;
  height: 3px;
  background: #ffd98e;
  animation: cinDust 0.62s ease-out forwards;
}
/* 네임플레이트. 작은 라벨 → 좌우 골드 헤어라인 → 대형 타이틀 순서가
   §1-2가 정한 네임플레이트 문법이다. */
.cin-plate {
  position: absolute;
  left: 50%;
  top: 42%;
  display: flex;
  align-items: center;
  gap: 14px;
  opacity: 0;
  transform: translate(-50%, -50%);
  white-space: nowrap;
}
.cin-plate b {
  color: #fdf6e8;
  font-size: 34px;
  letter-spacing: -0.03em;
  text-shadow:
    0 3px 0 #0a0418,
    0 0 22px #ffd98e66;
}
/* 폭이 아니라 «크기»로 자란다.

   원래는 `width: 0 → 92px`였다. width는 레이아웃 속성이라 컴포지터에 올라가지
   못하고, 자라는 0.42초 동안 매 프레임 메인스레드를 스타일+레이아웃으로
   끌고 간다. 실측: 입장 구간의 강제 레이아웃이 초당 111회 · 스타일 재계산
   240회였고, 같은 순간 동시에 도는 애니메이션 38개 중 이 둘만 안전하지
   않았다(나머지 36개는 전부 transform·opacity다). 저장소의 최적화 후
   기준선은 초당 1회다.

   scaleX는 같은 「안쪽에서 바깥으로 뻗는 선」을 레이아웃 없이 그린다. 폭이
   처음부터 92px로 잡혀 있으므로 자라는 동안 이름표 글자가 밀리지 않는다 —
   글자가 제자리에 서고 선만 뻗는다. */
.cin-hair {
  width: 92px;
  height: 2px;
  background: linear-gradient(90deg, transparent, #ffd98e, transparent);
  transform: scaleX(0);
  will-change: transform;
}
/* 이름표 쪽 끝을 축으로 삼아 바깥으로 뻗는다. */
.cin-hair-l {
  transform-origin: 100% 50%;
}
.cin-hair-r {
  transform-origin: 0 50%;
}
.cin.is-plate .cin-plate {
  animation: cinSlam 0.5s cubic-bezier(0.2, 0.85, 0.25, 1.1) both;
}
.cin.is-plate .cin-hair {
  animation: cinHair 0.42s ease-out 0.18s both;
}
/* 별지기 컷인 세 장. 속성색을 엣지로만 써서 캔버스의 유닛 판정색과 같은
   신호를 반복한다 — 새 색을 들이지 않는다. */
.cin-cuts {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 15%;
  display: flex;
  justify-content: center;
  gap: 12px;
}
.cin-portrait {
  position: relative;
  display: grid;
  justify-items: center;
  gap: 4px;
  padding: 7px 10px 6px;
  border-left: 3px solid var(--edge, #ffd98e);
  background: #0e0616e0;
  opacity: 0;
  transform: translateX(-26px) skewX(-8deg);
}
.cin-portrait i {
  width: 48px;
  height: 48px;
  background-position: 0 0;
  background-repeat: no-repeat;
  background-size: 288px 48px;
  image-rendering: pixelated;
}
.cin-portrait b {
  color: #fdf6e8;
  font-size: 11px;
}
.cin.is-cut1 .cin-p1,
.cin.is-cut2 .cin-p2,
.cin.is-cut3 .cin-p3 {
  animation: cinCut 0.32s cubic-bezier(0.2, 0.85, 0.25, 1.05) both;
}
.cin.is-open .cin-cuts {
  opacity: 0;
  transition: opacity 0.35s ease-out;
}
/* 조작 프롬프트. 연출이 끝났다는 마지막 신호이자 첫 조작 안내다. */
.cin-prompt {
  position: absolute;
  left: 50%;
  bottom: 9%;
  color: #ffd98e;
  font:
    700 12px "Galmuri11",
    monospace;
  letter-spacing: 0.3em;
  opacity: 0;
  transform: translateX(-50%);
  transition: opacity 0.4s ease-out;
}
.cin.is-open .cin-prompt {
  opacity: 1;
  animation: cinBlink 1.4s steps(2) infinite;
}
@keyframes cinFlash {
  0% {
    opacity: 0.9;
  }
  100% {
    opacity: 0;
  }
}
@keyframes cinRing {
  0% {
    opacity: 0.9;
    transform: translate(-50%, -50%) scale(0.3);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(3.4);
  }
}
@keyframes cinDust {
  0% {
    opacity: 1;
    transform: translate(0, 0);
  }
  100% {
    opacity: 0;
    transform: translate(var(--dx), var(--dy));
  }
}
@keyframes cinSlam {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(2.1);
  }
  52% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(0.92);
  }
  74% {
    transform: translate(-50%, -50%) scale(1.05);
  }
  100% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
  }
}
@keyframes cinHair {
  to {
    transform: scaleX(1);
  }
}
@keyframes cinCut {
  to {
    opacity: 1;
    transform: translateX(0) skewX(-8deg);
  }
}
@keyframes cinBlink {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.28;
  }
}
@media (prefers-reduced-motion: reduce) {
  .cin-flash,
  .cin-ring,
  .cin-dust {
    display: none;
  }
  .cin .cin-plate,
  .cin .cin-portrait,
  .cin .cin-prompt {
    opacity: 1;
    transform: translate(-50%, -50%);
    animation: none;
  }
  .cin .cin-portrait {
    transform: none;
  }
}

/* ---------------------------------------------------------------------
   STARLIGHT IGNITION — 승리 / 패배 (§2-5 · §2-6)
   한 뼈대에 색과 속도만 다르게 걸린다. 승리는 골드로 「회수」, 패배는 냉색으로
   「중단」을 말한다. 패배 화면의 골드는 재도전 CTA 하나뿐이고 플래시·파티클은
   없다 — 패배는 처벌이 아니라 관측이 끊긴 것이다.
   --------------------------------------------------------------------- */
.oc {
  position: relative;
  display: grid;
  justify-items: center;
  padding: 30px 26px;
  text-align: center;
}
.oc-veil {
  position: absolute;
  inset: -30px -26px;
  opacity: 0;
  pointer-events: none;
}
.oc-win .oc-veil {
  background: radial-gradient(ellipse at 50% 42%, #ffd98e1f, transparent 68%);
  animation: ocVeil 0.9s ease-out 0.15s forwards;
}
.oc-lose .oc-veil {
  background:
    linear-gradient(
      112deg,
      transparent 48.8%,
      #9fb4bd22 49.7% 50.3%,
      transparent 51.2%
    ),
    linear-gradient(
      68deg,
      transparent 48.8%,
      #9fb4bd18 49.7% 50.3%,
      transparent 51.2%
    ),
    radial-gradient(ellipse at 50% 42%, #9fb4bd1a, transparent 72%);
  animation: ocVeil 1.4s ease-out 0.3s forwards;
}
.oc-lose::before,
.oc-lose::after {
  position: absolute;
  top: 9%;
  bottom: 9%;
  z-index: 0;
  width: 42%;
  border: 1px solid #7d95a044;
  content: "";
  pointer-events: none;
  opacity: 0;
}
.oc-lose::before {
  left: -4%;
  border-right: 0;
  transform: translateX(-34px) skewY(12deg);
  animation: ocShutterLeft 1.05s ease-out 0.35s forwards;
}
.oc-lose::after {
  right: -4%;
  border-left: 0;
  transform: translateX(34px) skewY(-12deg);
  animation: ocShutterRight 1.05s ease-out 0.35s forwards;
}
.oc-kicker {
  color: var(--magenta-dim, #c94ff0);
  font:
    700 10px ui-monospace,
    monospace;
  letter-spacing: 0.3em;
  opacity: 0;
  animation: ocFade 0.4s ease-out 0.2s forwards;
}
.oc-lose .oc-kicker {
  color: var(--cool-1, #7d95a0);
}
/* 네임플레이트 문법(§1-2): 작은 라벨 → 좌우 헤어라인 → 대형 타이틀. */
.oc-plate {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 10px 0 4px;
  opacity: 0;
}
.oc-win .oc-plate {
  animation: ocSlam 0.5s cubic-bezier(0.2, 0.85, 0.25, 1.1) 0.75s forwards;
}
/* 패배는 슬램하지 않는다. 느리게 떠오르는 것이 「중단」의 속도다. */
.oc-lose .oc-plate {
  animation: ocFade 0.8s ease-out 0.9s forwards;
}
.oc-plate b {
  font-size: 30px;
  letter-spacing: -0.02em;
}
.oc-win .oc-plate b {
  color: #fdf6e8;
  text-shadow:
    0 3px 0 #0a0418,
    0 0 22px #ffd98e66;
}
.oc-lose .oc-plate b {
  color: var(--cool-0, #9fb4bd);
  text-shadow: 0 2px 0 #0a0418;
}
.oc-hair {
  width: 0;
  height: 2px;
}
.oc-win .oc-hair {
  background: linear-gradient(90deg, transparent, #ffd98e, transparent);
  animation: ocHair 0.42s ease-out 0.95s forwards;
}
.oc-lose .oc-hair {
  background: linear-gradient(90deg, transparent, #7d95a0, transparent);
  animation: ocHair 0.7s ease-out 1.2s forwards;
}
.oc-caption {
  max-width: 460px;
  margin: 0 0 16px !important;
  color: var(--ink-dim, #b9a8d6);
  font-size: 12px !important;
  line-height: 1.7;
  opacity: 0;
  animation: ocFade 0.5s ease-out 1.3s forwards;
}
.oc-lose .oc-caption {
  color: var(--cool-1, #7d95a0);
  animation-delay: 1.5s;
}
/* 기록은 한 줄씩 160ms 간격으로 들어온다 — 한꺼번에 뜨면 읽는 순서가 없다. */
.oc-rows {
  display: flex;
  gap: 10px;
  margin-bottom: 20px;
}
.oc-row {
  display: grid;
  gap: 3px;
  min-width: 92px;
  padding: 9px 12px;
  border: 1px solid #3f2e53;
  background: #170a24b8;
  opacity: 0;
  transform: translateY(8px);
  animation: ocRow 0.36s ease-out calc(1.55s + var(--i) * 0.16s) forwards;
}
.oc-lose .oc-row {
  border-color: #3d4a50;
  background: #131c20b8;
  animation-delay: calc(1.8s + var(--i) * 0.16s);
}
.oc-row small {
  color: var(--ink-dim, #b9a8d6);
  font-size: 9px;
  letter-spacing: 0.14em;
}
.oc-lose .oc-row small {
  color: var(--cool-1, #7d95a0);
}
.oc-row b {
  color: #fdf6e8;
  font-size: 17px;
}
.oc-lose .oc-row b {
  color: var(--cool-0, #9fb4bd);
}
.oc-ctas {
  display: flex;
  gap: 12px;
  opacity: 0;
  animation: ocFade 0.4s ease-out 2.2s forwards;
}
.oc-lose .oc-ctas {
  animation-delay: 2.6s;
}
/* 패배 화면에서 골드는 여기 하나뿐이다(§2-6). */
.oc-go {
  border: 2px solid #f6c48e !important;
  border-radius: 0 !important;
  background: linear-gradient(#f0b078, #a05c32) !important;
  color: #241304 !important;
  font-weight: 900 !important;
}
.oc-ghost {
  border: 1px solid #4d3d61 !important;
  border-radius: 0 !important;
  background: transparent !important;
  color: var(--ink-btn, #d9c2f0) !important;
}
.oc-lose .oc-ghost {
  border-color: #3d4a50 !important;
  color: var(--cool-1, #7d95a0) !important;
}
@keyframes ocVeil {
  to {
    opacity: 1;
  }
}
@keyframes ocShutterLeft {
  to {
    opacity: 0.65;
    transform: translateX(0) skewY(12deg);
  }
}
@keyframes ocShutterRight {
  to {
    opacity: 0.65;
    transform: translateX(0) skewY(-12deg);
  }
}
@keyframes ocFade {
  to {
    opacity: 1;
  }
}
@keyframes ocSlam {
  0% {
    opacity: 0;
    transform: scale(2);
  }
  52% {
    opacity: 1;
    transform: scale(0.94);
  }
  74% {
    transform: scale(1.04);
  }
  100% {
    opacity: 1;
    transform: scale(1);
  }
}
@keyframes ocHair {
  to {
    width: 74px;
  }
}
@keyframes ocRow {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
/* 움직임을 줄이면 최종 정지 프레임만. 결과 화면은 읽어야 하는 화면이므로
   여기서 투명도가 남아 있으면 정보가 사라진다. */
@media (prefers-reduced-motion: reduce) {
  .oc-kicker,
  .oc-plate,
  .oc-caption,
  .oc-row,
  .oc-ctas,
  .oc-veil {
    opacity: 1;
    transform: none;
    animation: none;
  }
  .oc-lose::before,
  .oc-lose::after {
    opacity: 0.65;
    transform: none;
    animation: none;
  }
  .oc-hair {
    width: 74px;
    animation: none;
  }
}

/* ---------------------------------------------------------------------
   허브 상단의 「타이틀」 나가기 (2026-08-24)
   프로필 안쪽에 있던 것을 자원 줄 끝으로 올렸다. 옆의 칩들(업적·골드·
   최단 기록)은 «값을 읽는» 것이고 이것은 «누르는» 것이라, 같은 높이에
   서되 문법을 흉내 내지 않는다 — 칩은 라벨 위 금색 값 두 줄이고 이쪽은
   한 줄이다. 평소에는 물러나 있다가 가리키면 살구빛으로 올라온다.
   --------------------------------------------------------------------- */
.hub-title-exit {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  align-self: stretch;
  padding: 6px 10px;
  border: 1px solid #3b2a55;
  border-radius: 4px;
  background: #12041f;
  color: #8d80a8;
  cursor: pointer;
  font:
    700 9px ui-monospace,
    monospace;
  letter-spacing: 0.06em;
  white-space: nowrap;
  transition:
    border-color 0.16s ease,
    color 0.16s ease,
    background-color 0.16s ease;
}
.hub-title-exit span {
  font-size: 11px;
  line-height: 1;
}
.hub-title-exit:hover,
.hub-title-exit:focus-visible {
  border-color: #f2b35c;
  background: #1b0a2b;
  color: #f3ede2;
}

/* ---------------------------------------------------------------------
   조작 박스 (2026-08-24)
   「남은 유성」과 같은 카드다. 다른 것은 값이 아니라 «지금 이 손이 무엇을
   하는가»를 적는다는 점뿐이라, 키 이름만 단색 칩으로 떼어 놓고 뜻은
   본문 색으로 둔다 — 읽는 순서가 키 -> 뜻이 되게.
   --------------------------------------------------------------------- */
.control-card .control-line {
  display: flex;
  align-items: baseline;
  gap: 7px;
  margin-top: 5px;
}
.control-card .control-line em {
  flex: 0 0 auto;
  min-width: 44px;
  padding: 2px 4px;
  border: 1px solid #3b2a55;
  border-radius: 3px;
  background: #12041f;
  color: #cbbde6;
  font:
    700 8px ui-monospace,
    monospace;
  font-style: normal;
  letter-spacing: 0.04em;
  text-align: center;
}
.control-card .control-line span {
  color: #b2a6cb;
  font-size: 10px;
  line-height: 1.35;
}

/* ---------------------------------------------------------------------
   수업 카드는 판 «위쪽»에 선다 (2026-08-24)

   여태 판 정중앙이었다. 그 자리가 비어 있었기 때문인데, 같은 날 수업의
   상대를 판 한가운데로 옮기면서 카드가 정확히 그 위에 앉게 됐다 —
   실측으로 카드 상자(488,360,444x222)가 보스 화면 좌표(710,471)를 덮었다.
   루나가 「저 앞의 별지기에게 맞춰 둘게요」라고 말하는 동안 그 판이
   카드에 가려 있는 셈이다.

   보스가 내려오면서 판 위쪽 삼분의 일이 비었으므로 그리로 올린다. 별지기는
   보스 아래(판 y 590~700)에 서므로 아래쪽도 카드가 앉을 자리가 아니다.
   위쪽만 남는다. --------------------------------------------------- */
.onboarding-dock {
  align-items: flex-start !important;
  padding-top: 18px !important;
}

/* ---------------------------------------------------------------------
   지도에서 «끝낸 곳»과 «갈 곳» (2026-08-24)

   클리어한 별이 금색 테두리·글로를 받아 지도에서 가장 밝았다. 성취의
   표시로 만든 것이지만, 지도에서 제일 밝은 것은 「여기로 가라」로 읽힌다 —
   끝낸 1-1 이 계속 손짓하고 있었다는 오너 제보가 그것이다.

   방향을 뒤집는다. 클리어는 물러나고(초상 탈색, 테두리·글로 죽임, 맥동
   정지), 아직 안 깬 첫 판만 밝아진다. 별은 사라지지 않는다 — ✓ 배지와
   금색 테두리 한 겹은 남겨 「했다」가 읽히게 하되, 밝기 경쟁에서만 뺀다.
   --------------------------------------------------------------------- */
.hub-map .constellation-node.cleared .stage-star,
.constellation-node.cleared .stage-star {
  border-color: #6a5638 !important;
  box-shadow: 0 0 0 3px #0e0616 !important;
}
.constellation-node.cleared .stage-star:after {
  animation: none !important;
  opacity: 0 !important;
}
.constellation-node.cleared .stage-star img {
  filter: grayscale(0.82) brightness(0.62);
}
.constellation-node.cleared {
  border-color: #3b2f27 !important;
  opacity: 0.72;
}
.constellation-node.cleared .node-status {
  color: #7e7263 !important;
}
/* 배지는 트로피가 아니라 «체크»다. 금색 알약은 그 자체로 상이라, 끝낸
   칸에 붙으면 다시 그리로 눈이 간다. */
.constellation-node.cleared .stage-cleared-mark {
  background: #2b2118 !important;
  color: #c9ab72 !important;
  box-shadow: 0 0 0 1px #6a5638 !important;
}

/* 갈 곳. 지도에서 유일하게 맥동하는 것이 여기가 되도록. */
.hub-map .constellation-node.next .stage-star,
.constellation-node.next .stage-star {
  border-color: #ffd98e !important;
  color: #fff1cf !important;
  box-shadow:
    0 0 0 3px #0e0616,
    0 0 20px #ffbe6faa,
    0 0 40px #f2b35c55 !important;
}
.constellation-node.next {
  border-color: #f2b35c !important;
}
.constellation-node.next .node-status {
  color: #ffd98e !important;
  font-weight: 700;
}
@keyframes hubNextPulse {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.07);
  }
}
.constellation-node.next .stage-star {
  animation: hubNextPulse 2.2s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .constellation-node.next .stage-star {
    animation: none;
  }
}

/* ---------------------------------------------------------------------
   발사 세기 — 판 오른쪽 세로 게이지 (2026-08-24)

   처음엔 유성 둘레에 46x5 막대로 그렸다. 판은 창 높이에 따라 0.65~0.99배로
   줄어들어 노트북에서 눈금이 뭉갰다. 왼쪽 HUD 카드로 옮겼다가, 다시 판
   오른쪽 세로로 세운다 — 끌기는 «아래로 당기는» 손이라 세로축이 손의
   방향과 같고, 차오르는 방향(아래에서 위)이 곧 세기다.
   채움은 transform:scaleY 라 매 프레임 레이아웃을 물지 않는다.
   --------------------------------------------------------------------- */
.force-rail {
  display: grid;
  justify-items: center;
  gap: 6px;
  padding: 10px 8px 12px;
  border: 1px solid #2c1f45;
  border-radius: 10px;
  background: #10061bcc;
}
.force-rail > small {
  color: #8d80a8;
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.12em;
}
.force-rail .force-column {
  position: relative;
  width: 16px;
  height: 168px;
  border: 1px solid #3b2a55;
  border-radius: 3px;
  background: #0a0413;
  overflow: hidden;
}
.force-rail .force-column i {
  position: absolute;
  inset: 0;
  transform: scaleY(0);
  transform-origin: bottom center;
  background: linear-gradient(0deg, #6fb7ab, #ffe09a);
  transition: transform 0.08s linear;
}
/* 28% 하한 눈금. 이보다 약하게는 나가지 않는다는 것을 자리로 말한다. */
.force-rail .force-floor {
  position: absolute;
  left: -1px;
  right: -1px;
  bottom: 28%;
  height: 1px;
  background: #8ba39f;
}
.force-rail .force-column[data-grade="high"] {
  border-color: #f2b35c;
}
.force-rail > b {
  color: #f2cb79;
  font:
    700 13px ui-monospace,
    monospace;
}
.force-rail .force-note {
  margin: 0;
  max-width: 116px;
  color: #8d80a8;
  font-size: 9px;
  line-height: 1.4;
  text-align: center;
}
/* 궤도 전환 충전. 「남은 유성」 점과 같은 자리·같은 크기로 둔다. */
.control-card .control-charge:empty {
  display: none;
}
/* 지금 누를 수 없는 줄은 물러난다. 「—」만으로는 못 쓰는 줄과 아직 안 읽은
   줄이 같아 보인다. */
.control-card .control-line[data-off="1"] {
  opacity: 0.38;
}
.control-card .control-line[data-off="1"] em {
  border-color: #2a2038;
  color: #6d6382;
}

/* 발사 버튼 — 세기 게이지 바로 아래(2026-08-24).
   게이지가 «얼마나»를 말하고 이 버튼이 «지금»을 맡는다. 쏠 수 없을 때는
   흐려지고 커서도 바뀐다 — 눌리지 않는 이유가 손에 먼저 닿아야 한다. */
.force-rail .force-fire {
  display: grid;
  justify-items: center;
  gap: 1px;
  width: 100%;
  margin-top: 2px;
  padding: 8px 6px 7px;
  border: 1px solid #f2b35c;
  border-radius: 6px;
  background: linear-gradient(180deg, #3a1f10, #1d0d08);
  color: #ffe6b8;
  cursor: pointer;
  font:
    700 12px "Galmuri11",
    ui-monospace,
    monospace;
  letter-spacing: 0.04em;
  transition:
    background-color 0.16s ease,
    border-color 0.16s ease,
    opacity 0.16s ease;
}
.force-rail .force-fire small {
  color: #c79a63;
  font:
    700 7px ui-monospace,
    monospace;
  letter-spacing: 0.16em;
}
.force-rail .force-fire:hover:not(:disabled),
.force-rail .force-fire:focus-visible:not(:disabled) {
  border-color: #ffd98e;
  background: linear-gradient(180deg, #4a2814, #26120a);
}
.force-rail .force-fire:active:not(:disabled) {
  transform: translateY(1px);
}
.force-rail .force-fire:disabled {
  border-color: #3b2a55;
  background: #12041f;
  color: #6d6382;
  cursor: not-allowed;
  opacity: 0.7;
}
.force-rail .force-fire:disabled small {
  color: #55496b;
}

/* ---------------------------------------------------------------------
   음소거 한 버튼 (2026-08-24)
   타이틀과 전투에서만 선다. 허브·상점·프로필에는 설정 탭이 바로 옆에 있어
   겹쳐 설 이유가 없다. 꺼진 상태는 글자를 바꾸지 않고 사선 한 줄로 말한다 —
   ♪ 가 그대로 있고 그 위에 금이 가는 편이 「무엇이 꺼졌는지」가 빠르다.
   --------------------------------------------------------------------- */
#muteButton {
  position: fixed;
  right: 14px;
  /* 우상단(2026-08-24, 오너 지시). 앞서는 우하단 62px이었는데, 그 구석에는
     인트로의 「건너뛰기」(우하단 18px, z-index 70)가 이미 서 있어 둘이 세로로
     겹쳐 쌓여 있었다. 위쪽 구석은 실제로 비어 있다 — 이 사각형과 겹치는
     요소를 인트로·타이틀·전투에서 훑어 보면 전면 배경층(#dawn-sky,
     #sky-ambience, 장식 div, 캔버스)뿐이고 UI는 하나도 없다. 1280·680
     둘 다에서 확인했다(680이 이 저장소의 최소 브레이크포인트다).
     z-index 는 알려진 최상단 오버레이(「건너뛰기」 70) 위로 둔다. 소리를
     당장 끄고 싶은 순간이 인트로이므로, 인트로 층에 절대 묻히지 않아야 한다. */
  top: 14px;
  z-index: 71;
  display: none;
  place-items: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid #3b2a55;
  border-radius: 50%;
  background: #12041fd9;
  color: #cbbde6;
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  transition:
    border-color 0.16s ease,
    color 0.16s ease;
}
body.title-mode #muteButton,
body.game-mode #muteButton {
  display: grid;
}
#muteButton:hover,
#muteButton:focus-visible {
  border-color: #f2b35c;
  color: #f3ede2;
}
#muteButton[data-muted="1"] {
  color: #6d6382;
}
#muteButton[data-muted="1"]::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 24px;
  height: 2px;
  background: #ff5a4d;
  transform: translate(-50%, -50%) rotate(-45deg);
}
/* 언어 토글 버튼. 음소거 버튼(right:14px, 34px) 왼쪽에 8px 띄워 같은 원형으로
   선다. 표시·z-index 는 음소거와 동일하게 맞춰 «타이틀·전투»에서만 보인다. */
#langButton {
  position: fixed;
  right: 56px;
  top: 14px;
  z-index: 71;
  display: none;
  place-items: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid #3b2a55;
  border-radius: 50%;
  background: #12041fd9;
  color: #cbbde6;
  cursor: pointer;
  font:
    700 12px/1 Galmuri11,
    ui-monospace,
    monospace;
  letter-spacing: 0.01em;
  transition:
    border-color 0.16s ease,
    color 0.16s ease;
}
body.title-mode #langButton,
body.game-mode #langButton {
  display: grid;
}
#langButton:hover,
#langButton:focus-visible {
  border-color: #f2b35c;
  color: #f3ede2;
}
