/* =====================================================================
   STELLA BALL — UI THEME v3 "Dawn Observatory" (2026-08-12)
   Loaded last so it is a visual-only cascade pass. Combat logic remains
   unchanged. See ../UI_KIT_DAWN.md for the rationale and rules.
   ===================================================================== */

:root {
  --night-0: #0e0616;
  --night-1: #170a24;
  --night-2: #200f31;
  --void: #2b1b3f;
  --moon: #d2cedb;
  --star: #ffd2a0;
  --gold: #f2b35c;
  --mist: #938aa4;
  --line: #3f2e53;
  --sb-gaon: #d9b269;
  --sb-biyeon: #cf7183;
  --sb-lumi: #9270c4;
  --sb-haru: #9cc878;
  --sb-sera: #a99bd6;
  --sb-taeo: #d99a66;
  --sb-nyx: #8f7fd0;
  --sb-ria: #bca6d8;
  /* 「별빛 점화」 팔레트 (INTRO_REDESIGN_HANDOFF.md §1-1, 2026-08-18 전환).
     기존 새벽 관측소의 청록 계열을 색상 회전으로 보이드 바이올렛에 옮겼다
     (`scripts/repalette-dawn-to-void.mjs`). 앞으로 만드는 연출은 하드코딩
     대신 아래 토큰을 쓴다 — 지금 팔레트가 823종으로 흩어져 있는 이유가
     처음부터 토큰을 안 썼기 때문이다. */
  --magenta: #e84ff0;
  --magenta-dim: #c94ff0;
  --violet: #7e2bb0;
  --violet-lit: #a360e6;
  --void-deep: #2a1442;
  --panel-line: #3a2a5e;
  --ink: #fdf6e8;
  --ink-dim: #b9a8d6;
  --ink-btn: #d9c2f0;
  /* 패배·냉각 전용. 이 셋이 쓰인 화면에는 골드가 CTA 하나뿐이어야 한다. */
  --cool-0: #9fb4bd;
  --cool-1: #7d95a0;
  --cool-2: #8fb5b2;
}

body {
  color: var(--moon);
  background: radial-gradient(
    ellipse at 50% -15%,
    #1f122e 0%,
    #170a24 43%,
    #0e0616 82%
  ) !important;
}
body::before {
  opacity: 0.3 !important;
}
header {
  border-color: #362649 !important;
  background: linear-gradient(100deg, #11071cef, #190b26ef) !important;
  box-shadow:
    0 8px 24px #06010b99,
    0 0 0 1px #f2b35c22 !important;
}
header .tag,
.hud-label span,
.status-line span,
.boss-name span,
.blaze-score-label {
  color: #877c9b !important;
}
header h1,
.boss-name b {
  color: #f3ede2 !important;
  text-shadow: 0 0 14px #f2b35c44 !important;
}
header p,
.status-line {
  color: #988ea9 !important;
}

button {
  border-color: #4d3d61 !important;
  background: linear-gradient(180deg, #210e34, #170a24) !important;
  color: #cdc7d9 !important;
  box-shadow: 0 3px 0 #08020e !important;
}
button:hover {
  border-color: #f6c48e !important;
  background: linear-gradient(180deg, #2a1541, #210e34) !important;
  box-shadow:
    0 0 0 1px #f6c48e33,
    0 5px 14px #06010b99 !important;
}
.hub-start,
.title-enter,
.meta-launch,
.onboarding-card button,
.outcome-cut.win button {
  border-color: #f6c48e !important;
  background: linear-gradient(#ffd98e, #c98a3e) !important;
  color: #2a1608 !important;
  box-shadow:
    0 4px 0 #4a2a16,
    0 0 0 3px #f2b9881a !important;
}
.outcome-cut.fail button {
  border-color: #877c9b !important;
  background: linear-gradient(#3d2359, #2e1745) !important;
}

.stage {
  border-color: #4d3d61 !important;
  background: #10061b !important;
  box-shadow:
    0 0 0 1px #f2b35c22,
    0 18px 42px #06010bbb !important;
}
.game-mode .hud-card,
.game-mode .boss-panel,
.game-mode .boss-banner,
.hud-card,
.boss-panel {
  border-color: #45345a !important;
  background: linear-gradient(145deg, #190b26f4, #13081ef4) !important;
  box-shadow:
    0 3px 0 #07020cbb,
    0 0 20px #0009 !important;
}
.hud-label b,
.status-line b {
  color: #ffe3c0 !important;
}
.meter {
  border-color: #2a1541 !important;
  background: #100719 !important;
}
.meter i,
.meter.hp i {
  background: linear-gradient(90deg, #b96a3e, #ffdcae 56%, #fff3e2) !important;
  box-shadow: 0 0 9px #eea56f66 !important;
}
.shot-dots {
  color: #e8a874 !important;
  text-shadow: 0 0 9px #d18a5266 !important;
}
.tip {
  border-color: #3f2e53 !important;
  background: #11071cdd !important;
  color: #d2cedb !important;
}
.tip b {
  color: #ffd2a0 !important;
}
.toast {
  border-color: #ecac7c !important;
  background: #190b26f4 !important;
  color: #f0e3c5 !important;
}
/* A queued banner is deliberately short-lived, so it says how many are still
   waiting instead of letting the player wonder what they missed. */
.toast.queued {
  border-color: #f6c48e !important;
}
.toast .toast-more {
  display: inline-block;
  margin-left: 7px;
  padding: 1px 5px;
  border-radius: 999px;
  background: #ffd98e;
  color: #241708;
  font:
    700 9px ui-monospace,
    monospace;
  font-style: normal;
  vertical-align: middle;
}
.combo-readout.hot {
  color: #ffe3c0 !important;
  text-shadow:
    0 0 12px #dd9560,
    0 0 26px #604e7855 !important;
}
.blaze-card {
  border-color: #4d3d61 !important;
  background: linear-gradient(
    135deg,
    #210e34f5,
    #241238ee 68%,
    #2b1b3ff0
  ) !important;
  color: #b8b1c6 !important;
  box-shadow:
    0 3px 0 #07020cbb,
    0 0 22px #0f0618bb !important;
}
.blaze-score-label {
  color: #8e83a1 !important;
}
.blaze-card b {
  color: #ffe9d2 !important;
  text-shadow:
    2px 2px 0 #0e0e14,
    0 0 11px #d18a5266 !important;
}
.blaze-card small {
  border-color: #4d3d6155 !important;
  color: #988ea9 !important;
}
.blaze-card[data-heat="1"],
.blaze-card[data-heat="2"],
.blaze-card[data-heat="3"] {
  border-color: #e8a670 !important;
  background: linear-gradient(
    135deg,
    #210e34f5,
    #361c54f0 68%,
    #452a65ec
  ) !important;
}
.blaze-card[data-heat="1"] b,
.blaze-card[data-heat="2"] b,
.blaze-card[data-heat="3"] b {
  text-shadow:
    2px 2px 0 #181624,
    0 0 15px #e89e6e,
    0 0 31px #725c9755 !important;
}
.boss-banner.impact-heavy {
  border-color: #ffe9d2 !important;
  background: #361c54f0 !important;
  box-shadow:
    0 0 0 1px #ffe9d2,
    0 0 24px #d18a5288 !important;
}
.impact-flash {
  background: radial-gradient(
    circle at 55% 44%,
    #ffe3b8aa 0%,
    #ff8a7055 24%,
    transparent 58%
  ) !important;
}

.overlay,
.meta-scene,
.stage-map-scene,
.stage-select-scene,
.tutorial-scene,
.constellation-map-scene,
.story-intro-scene {
  background: #0e0616f2 !important;
}
.survivor-hub,
.meta-hub,
.world-map,
.stage-select,
.tutorial-shell,
.constellation-map-shell {
  background: linear-gradient(155deg, #190b26e8, #11071ce8) !important;
}
.overlay h2 {
  color: #f3ede2 !important;
  text-shadow: 0 2px 0 #1b0d29 !important;
}
.overlay p {
  color: #d2cedb !important;
}
.hub-topbar {
  border-color: #45345a !important;
}
.hub-avatar,
.hub-resource,
.hub-party-row,
.hub-quick {
  border-color: #362649 !important;
  background: #170a24 !important;
}
.hub-avatar {
  border-color: #b49554 !important;
  color: #ffdcae !important;
  box-shadow:
    0 3px 0 #08020e,
    0 0 14px #f2b35c33 !important;
}
.hub-resource b {
  color: #ffd2a0 !important;
}
.hub-mission {
  border-color: #f2b35c !important;
  background-color: #201030 !important;
  background-image:
    linear-gradient(90deg, #0f0618ec, #190b26d9 52%, #2b1b3f70),
    var(--stage-art) !important;
  box-shadow:
    0 9px 24px #06010bbb,
    0 0 0 1px #f2b9881a !important;
}
.hub-player b,
.hub-mission h2,
.stage-select-card b,
.map-node b {
  color: #f3ede2 !important;
}
.map-node,
.stage-select-card,
.constellation-node {
  border-color: #4d3d61 !important;
  background-color: #1b0d29 !important;
  background-image:
    linear-gradient(90deg, #0f0618ed, #190b26db 54%, #2b1b3f6b),
    var(--stage-art) !important;
}
.map-node:hover,
.map-node.active,
.stage-select-card:hover,
.stage-select-card.active,
.constellation-node:hover {
  border-color: #f6c48e !important;
  box-shadow:
    0 10px 22px #000b,
    0 0 0 2px #f6c48e44 !important;
}
.constellation-route path {
  stroke: #604e78 !important;
  filter: drop-shadow(0 0 5px #75609266) !important;
}

.roster-shell,
.roster-detail,
.deployment-map,
.draft-scene .choice-card,
.tutorial-card,
.help-panel {
  border-color: #45345a !important;
  background: linear-gradient(145deg, #1b0d29, #13081e) !important;
  box-shadow: 0 9px 24px #06010bbb !important;
}
.roster-unit.active {
  opacity: 1;
  filter: none !important;
  border-color: #f6c48e !important;
  background: linear-gradient(
    145deg,
    #2e2418 0%,
    #200f31 52%,
    #170a24 100%
  ) !important;
  box-shadow:
    0 0 0 1px #f6c48e44,
    0 0 14px #f2b35c26,
    inset 0 0 17px #f2b9881a !important;
  color: #fff3e2 !important;
}
.roster-unit.active .portrait {
  filter: saturate(0.85) drop-shadow(0 3px 0 #07020c)
    drop-shadow(0 0 8px #f2b988) !important;
}
.slot-card {
  border-color: var(--unit) !important;
  background: #170a24de !important;
  box-shadow:
    0 0 0 5px #0e0616,
    0 0 16px color-mix(in srgb, var(--unit) 30%, transparent) !important;
}
.choice-card:hover,
.draft-scene .choice-card:hover {
  border-color: #f6c48e !important;
  background: #200f31 !important;
}

.outcome-cut {
  border-color: #5b4b70 !important;
  background-color: #170a24f2 !important;
  box-shadow:
    0 18px 44px #06010bbb,
    0 0 0 1px #e8a67022 !important;
}
.outcome-constellation {
  color: #f7c493 !important;
  text-shadow: 0 0 12px #dd9560 !important;
}
.outcome-cut h2 {
  color: #ffe3c0 !important;
  text-shadow: 0 0 19px #d18a5266 !important;
}
.result-card {
  border-color: #514165 !important;
  background: linear-gradient(145deg, #200f31, #170a24) !important;
  box-shadow: inset 0 0 0 1px #ffe3c00c !important;
}
.result-metric {
  color: #8e83a1 !important;
}
.result-metric b {
  color: #ffd2a0 !important;
}
.achievement-card,
.system-panel,
.settings-group {
  border-color: #362649 !important;
  background: #170a24 !important;
}
.setting-row input[type="range"] {
  accent-color: #f2b35c !important;
}
.language-choice button.active {
  border-color: #e8a874 !important;
  background: #2e2418 !important;
  color: #f3ede2 !important;
}

.meta-tab img,
.meta-rune img,
.meta-rune-art,
.meta-daily > img,
.result-medal,
.result-metric img,
.draft-card .upgrade-icon,
.unit-skill,
.deploy-skill,
.detail-skill-icon,
.stage-emblem,
.shot-dots .shot-icon,
.picked-mark,
.party-chip .slot-marker,
.map-node img,
.title-party img,
.help-control img,
.hub-quick img,
.meta-actions button img,
.title-content button img,
.meta-launch img,
.title-enter img,
.hub-avatar img,
.tutorial-visual img {
  filter: saturate(0.72) hue-rotate(-6deg) brightness(1.04)
    drop-shadow(0 2px 0 #07020c) !important;
}
.title-wordmark,
.meta-brand img {
  filter: saturate(0.8) brightness(1.06) drop-shadow(0 3px 0 #1b0d29) !important;
}
.achievement-card.locked img {
  filter: grayscale(0.75) brightness(0.62) !important;
}

.title-sequence {
  background: radial-gradient(
    ellipse at 50% 31%,
    #2b1b3f88 0%,
    #170a24d9 42%,
    #0e0616ef 88%
  ) !important;
}
.title-sequence::before {
  filter: saturate(0.5) brightness(0.9) !important;
  opacity: 0.4 !important;
}
.title-stars i {
  background: #ffe3b8 !important;
  box-shadow: 0 0 10px #eea56f !important;
}
.title-orb {
  filter: drop-shadow(0 0 12px #ffe3b8) drop-shadow(0 0 28px #604e78) !important;
}
.title-kicker {
  color: #cdc7d9 !important;
  border-color: #d2cedb88 !important;
  text-shadow: 0 0 10px #d2cedb !important;
}
.title-copy p {
  color: #d2cedb !important;
  text-shadow: 0 2px 0 #170a24 !important;
}
.title-copy::before {
  background: linear-gradient(
    90deg,
    transparent,
    #ffe3b8,
    #c7c1d3,
    #ffe3b8,
    transparent
  ) !important;
  box-shadow: 0 0 14px #f2b988aa !important;
}
.hub-night-sky i {
  background: #f8ca96 !important;
  box-shadow:
    0 0 7px #cd7f4a,
    0 0 15px #f8ca9666 !important;
}

/* Meta hub: one clear battle destination with compact supporting choices. */
.survivor-hub {
  display: flex;
  min-height: 100%;
  flex-direction: column;
  gap: 10px;
  padding: 14px 16px 16px !important;
}
.hub-topbar {
  flex: 0 0 auto;
  padding: 0 0 10px !important;
}
.hub-battle-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 11px;
}
.hub-battle-tags span {
  padding: 4px 6px;
  border: 1px solid #4d3d61;
  background: #11071ccf;
  color: #f0bc8c;
  font:
    700 8px ui-monospace,
    monospace;
}
.hub-stage-change {
  position: absolute !important;
  top: 10px;
  right: 10px;
  padding: 5px 7px !important;
  border-color: #4d3d61 !important;
  background: #13081ed9 !important;
  color: #d2cedb !important;
  font-size: 8px !important;
}
/* The prototype bench, parked beside the play button so it reads as a second
   destination rather than a second call to action. */
.hub-training {
  align-self: end;
  flex: 0 0 auto;
  min-width: 62px;
  padding: 7px 9px !important;
  border-color: #4d3d61 !important;
  background: #13081ed9 !important;
  color: #d2cedb !important;
  font-size: 9px !important;
  line-height: 1.35;
}
.hub-battle-play {
  align-self: end;
  min-width: 136px;
  padding: 12px 13px !important;
  border-color: #f6c48e !important;
  background: linear-gradient(#ffd98e, #c98a3e) !important;
  color: #2a1608 !important;
  font-size: 12px !important;
  font-weight: 900 !important;
  box-shadow: 0 4px 0 #4a2a16 !important;
}
.hub-battle-play img {
  width: 17px;
  height: 17px;
  margin-right: 5px;
  vertical-align: -4px;
  image-rendering: pixelated;
  filter: saturate(0.72) hue-rotate(-6deg) brightness(0.36) !important;
}
.hub-gold b {
  color: #ffc98e !important;
  text-shadow: 0 0 8px #f2b35c55;
}
.hub-gold b::before,
.result-gold i {
  display: inline-block;
  width: 11px;
  height: 11px;
  margin-right: 4px;
  border: 1px solid #ffd6a0;
  border-radius: 50%;
  background: radial-gradient(
    circle at 36% 31%,
    #fff3dd 0 13%,
    #e09454 18% 58%,
    #7e4a26 62%
  );
  box-shadow: 0 0 7px #f2ab6e55;
  content: "";
  vertical-align: -1px;
}
.result-gold {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  width: min(220px, 100%);
  margin: 12px auto 0;
  padding: 8px 10px;
  border: 1px solid #f2b35c;
  background: #10061bd9;
  box-shadow:
    inset 0 0 0 1px #ffd2a018,
    0 3px 0 #07020c;
  color: #d2cedb;
  font-size: 10px;
}
.result-gold i {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  margin-right: 1px;
}
.result-gold b {
  color: #ffce9a;
  font-size: 12px;
}
.hub-party-slots {
  grid-column: 1 / -1;
  display: flex;
  gap: 8px;
}
.hub-party-slot {
  width: 42px;
  height: 42px;
  border-color: #4d3d61;
  background: #11071c;
  box-shadow: inset 0 0 0 3px #07020c;
}
.hub-party-slot img {
  width: 36px;
  height: 36px;
}
.gacha-scene {
  align-items: center !important;
  padding: 18px !important;
  background:
    radial-gradient(circle at 50% 15%, #3f265d38, transparent 42%), #0a0213f5 !important;
}
.gacha-shell {
  width: min(540px, 100%);
  padding: 16px;
  border: 1px solid #4d3d61;
  background: linear-gradient(145deg, #1b0d29, #0f0618);
  box-shadow:
    0 14px 36px #000c,
    inset 0 0 0 1px #f2b98813;
}
.gacha-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.gacha-header button {
  padding: 6px 9px !important;
  font-size: 9px !important;
}
.gacha-header span {
  text-align: right;
}
.gacha-header small,
.gacha-copy small {
  display: block;
  color: #938aa4;
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.13em;
}
.gacha-header b {
  display: block;
  margin-top: 3px;
  color: #ffc98e;
  font-size: 11px;
}
.gacha-ritual {
  position: relative;
  display: grid;
  min-height: 180px;
  margin: 14px 0 11px;
  overflow: hidden;
  place-items: center;
  border: 1px solid #3f2e53;
  background:
    radial-gradient(circle at 50% 46%, #452a6566, transparent 24%),
    repeating-radial-gradient(
      circle at 50% 50%,
      #ffd98e0b 0 1px,
      transparent 2px 20px
    ),
    #13081e;
}
.gacha-ritual::before,
.gacha-ritual::after {
  position: absolute;
  content: "";
  pointer-events: none;
}
.gacha-ritual::before {
  width: 230px;
  height: 230px;
  border: 1px solid #f2b35c66;
  border-radius: 50%;
  box-shadow: 0 0 30px #f2b35c22;
}
.gacha-ritual::after {
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 34%,
    #ffce9a12 49%,
    transparent 64%
  );
  transform: translateX(-100%);
  animation: gachaSweep 4.2s ease-in-out infinite;
}
.gacha-orbit {
  position: absolute;
  width: 180px;
  height: 180px;
  border: 1px dashed #eeaa7855;
  border-radius: 50%;
  animation: gachaOrbit 13s linear infinite;
}
.gacha-orbit i {
  position: absolute;
  /* 도트 사다리 16 — 글자는 투명, 그림만. ✧ 자리(2번째)는 저휘도 벌. */
  width: 16px;
  height: 16px;
  background: url("../assets/library/icons/star/star-16.png") center no-repeat;
  image-rendering: pixelated;
  color: transparent;
  font-style: normal;
  text-shadow: none;
}
.gacha-orbit i:nth-child(2) {
  background-image: url("../assets/library/icons/star/star-dim-16.png");
}
.gacha-orbit i:nth-child(1) {
  left: 12px;
  top: 26px;
}
.gacha-orbit i:nth-child(2) {
  right: 6px;
  top: 81px;
}
.gacha-orbit i:nth-child(3) {
  bottom: 20px;
  left: 45px;
}
.gacha-reveal {
  position: relative;
  z-index: 1;
  display: grid;
  min-width: 170px;
  min-height: 112px;
  place-items: center;
  align-content: center;
  gap: 7px;
  color: #ffe6be;
  text-align: center;
}
.gacha-reveal > span:first-child:not(.portrait) {
  /* 37px 글자였다 — 사다리 최대 단(32) 정수배로 몰아 넣는다. */
  width: 32px;
  height: 32px;
  background: url("../assets/library/icons/star/star-32.png") center no-repeat;
  image-rendering: pixelated;
  color: transparent;
  font-size: 37px;
  text-shadow: none;
}
.gacha-reveal small {
  color: #d2cedb;
  font-size: 10px;
  line-height: 1.45;
}
.gacha-reveal b {
  color: #ffdcac;
  font-size: 15px;
}
.gacha-reveal.rolling {
  animation: gachaRolling 0.92s ease-in-out;
}
.gacha-reveal.revealed {
  animation: gachaReveal 0.45s ease-out;
}
.gacha-reveal .portrait {
  width: 72px;
  height: 72px;
  border: 1px solid #f6c48e;
  background-color: #13081e;
  box-shadow: 0 0 18px #ffd98e55;
  image-rendering: pixelated;
}
.gacha-copy h2 {
  margin: 4px 0 5px;
  color: #f3ede2;
  font-size: 22px;
}
.gacha-copy p {
  margin: 0;
  color: #d2cedb;
  font-size: 10px;
  line-height: 1.5;
}
.gacha-pool {
  margin-top: 13px;
  padding-top: 10px;
  border-top: 1px solid #362649;
}
.gacha-pool-heading {
  display: flex;
  justify-content: space-between;
  color: #9489a7;
  font-size: 9px;
}
.gacha-pool-heading b {
  color: #ffd2a0;
}
.gacha-pool-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 6px;
  margin-top: 8px;
}
.gacha-pool-unit {
  display: grid;
  min-height: 76px;
  place-items: center;
  align-content: center;
  gap: 2px;
  padding: 5px;
  border: 1px solid #3f2e53;
  background: #150822;
  color: #cdc7d9;
  text-align: center;
}
.gacha-pool-unit .portrait {
  width: 34px;
  height: 34px;
  opacity: 0.52;
  filter: grayscale(0.8) brightness(0.72);
  image-rendering: pixelated;
}
.gacha-pool-unit b {
  font-size: 9px;
}
.gacha-pool-unit small {
  color: #938aa4;
  font-size: 7px;
}
.gacha-pool-unit.unlocked {
  border-color: #8a5f34;
  background: #171023;
}
.gacha-pool-unit.unlocked .portrait {
  opacity: 0.8;
  filter: saturate(0.72) brightness(1.02);
}
.gacha-pool-unit.unlocked small {
  color: #eeaa78;
}
.gacha-draw {
  width: 100%;
  margin-top: 13px;
  padding: 10px !important;
  border-color: #f6c48e !important;
  background: linear-gradient(#ffd98e, #c98a3e) !important;
  color: #2a1608 !important;
  font-weight: 900 !important;
  box-shadow: 0 4px 0 #4a2a16 !important;
}
.gacha-draw.insufficient {
  border-color: #4d3d61 !important;
  background: #2b1541 !important;
  color: #d2cedb !important;
  box-shadow: 0 3px 0 #150822 !important;
}
.gacha-draw.complete {
  opacity: 0.65;
}
@keyframes gachaOrbit {
  to {
    transform: rotate(360deg);
  }
}
@keyframes gachaSweep {
  42%,
  100% {
    transform: translateX(100%);
  }
}
@keyframes gachaRolling {
  25% {
    transform: scale(0.88) rotate(-5deg);
    filter: brightness(1.5);
  }
  70% {
    transform: scale(1.14) rotate(6deg);
    filter: brightness(2);
  }
}
@keyframes gachaReveal {
  from {
    opacity: 0;
    transform: scale(0.55);
    filter: brightness(2.4);
  }
  to {
    opacity: 1;
    transform: scale(1);
    filter: brightness(1);
  }
}
/* --- summon sequence (about 7s) ---------------------------------------
   Phases come from [data-phase] on .summon-stage; game-meta.js owns the
   clock. Palette is the Dawn Observatory set only: --star #ffd2a0,
   --gold #f2b35c, --moon #d2cedb, --mist #938aa4 over --night-0 #0e0616. */
.gacha-ritual.summoning::after {
  animation: none;
}
.gacha-ritual.summoning .gacha-orbit {
  opacity: 0.1;
}
.gacha-ritual.summoning .gacha-reveal.revealed {
  z-index: 2;
}
.gacha-ritual.summoning .gacha-reveal:not(.revealed) {
  opacity: 0;
}
.summon-stage {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  overflow: hidden;
  pointer-events: none;
}
.summon-stage::before {
  position: absolute;
  content: "";
  inset: 0;
  background: #0e0616;
  opacity: 0;
  transition: opacity 0.9s ease;
}
.summon-stage[data-phase="call"]::before,
.summon-stage[data-phase="observe"]::before {
  opacity: 0.74;
}
.summon-stage[data-phase="answer"]::before {
  opacity: 0.52;
}
.summon-stage[data-phase="manifest"]::before {
  opacity: 0.12;
  animation: summonFlash 0.72s ease-out both;
}
.summon-circle {
  position: absolute;
  width: 64px;
  height: 64px;
  border: 1px solid #f2b35c88;
  border-radius: 50%;
  opacity: 0;
  transition:
    width 1.4s cubic-bezier(0.2, 0.7, 0.2, 1),
    height 1.4s cubic-bezier(0.2, 0.7, 0.2, 1),
    opacity 0.6s ease,
    border-color 0.6s ease,
    box-shadow 0.6s ease;
}
.summon-stage[data-phase="call"] .summon-circle {
  width: 196px;
  height: 196px;
  opacity: 1;
}
.summon-stage[data-phase="observe"] .summon-circle {
  width: 172px;
  height: 172px;
  opacity: 1;
  border-color: #ffd2a099;
}
.summon-stage[data-phase="answer"] .summon-circle {
  width: 94px;
  height: 94px;
  opacity: 1;
  border-color: #ffd2a0;
  box-shadow: 0 0 26px #ffd2a044;
}
.summon-stage[data-phase="manifest"] .summon-circle,
.summon-stage[data-phase="intro"] .summon-circle {
  width: 244px;
  height: 244px;
  opacity: 0;
}
.summon-motes {
  position: absolute;
  inset: 0;
}
.summon-motes i {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 3px;
  height: 3px;
  margin: -1.5px;
  background: #ffe6c4;
  box-shadow: 0 0 6px #ffd2a0;
  opacity: 0;
}
.summon-stage[data-phase="observe"] .summon-motes i {
  animation: summonGather 2.3s cubic-bezier(0.4, 0, 0.7, 1) infinite;
  animation-delay: calc(var(--i) * -0.19s);
}
.summon-stage[data-phase="answer"] .summon-motes i {
  animation: summonGather 0.95s cubic-bezier(0.4, 0, 0.7, 1) infinite;
  animation-delay: calc(var(--i) * -0.07s);
}
.summon-answer {
  position: absolute;
  width: 6px;
  height: 6px;
  background: #d2cedb;
  box-shadow: 0 0 16px #ffd2a0;
  opacity: 0;
}
.summon-stage[data-phase="answer"] .summon-answer {
  opacity: 1;
  animation: summonAnswer 1.5s ease-out both;
}
.summon-stage[data-phase="manifest"] .summon-answer,
.summon-stage[data-phase="intro"] .summon-answer {
  opacity: 0;
  transition: opacity 0.4s ease;
}
.summon-lines {
  position: absolute;
  width: 212px;
  height: 212px;
  opacity: 0;
  transition: opacity 0.8s ease;
}
.summon-lines polyline {
  fill: none;
  stroke: #ffd2a0;
  stroke-width: 1.5;
  stroke-linejoin: round;
  stroke-dasharray: 560;
  stroke-dashoffset: 560;
}
.summon-stage[data-phase="manifest"] .summon-lines {
  opacity: 1;
}
.summon-stage[data-phase="manifest"] .summon-lines polyline {
  animation: summonTrace 1.2s ease-in-out forwards;
}
.summon-stage[data-phase="intro"] .summon-lines {
  opacity: 0.3;
}
.summon-stage[data-phase="intro"] .summon-lines polyline {
  stroke-dashoffset: 0;
}
.summon-caption {
  position: absolute;
  bottom: 11px;
  color: #ffd2a0;
  font:
    900 11px/1 ui-monospace,
    monospace;
  letter-spacing: 0.34em;
  opacity: 0.92;
  transition: opacity 0.4s ease;
}
.summon-stage[data-phase="intro"] .summon-caption {
  opacity: 0;
}
.summon-skip {
  position: absolute;
  top: 9px;
  right: 11px;
  color: #938aa4;
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.1em;
  opacity: 0.75;
}
.summon-cost {
  position: absolute;
  top: 46%;
  color: #ffd2a0;
  font:
    900 13px ui-monospace,
    monospace;
  font-style: normal;
  opacity: 0;
  animation: summonCost 1.9s ease-out forwards;
}
.summon-lore {
  display: block;
  max-width: 250px;
  margin: 3px auto 0;
  color: #d2cedb;
  font:
    700 10px/1.55 ui-monospace,
    monospace;
  font-style: normal;
  text-align: center;
  text-wrap: pretty;
  opacity: 0;
  animation: summonLore 0.6s ease-out forwards;
}
.summon-lore span {
  display: block;
  margin-top: 2px;
  color: #938aa4;
  font-weight: 400;
}
@keyframes summonGather {
  0% {
    opacity: 0;
    transform: rotate(calc(var(--i) * 30deg)) translateY(-104px) scale(1);
  }
  22% {
    opacity: 0.95;
  }
  100% {
    opacity: 0;
    transform: rotate(calc(var(--i) * 30deg)) translateY(-10px) scale(0.4);
  }
}
@keyframes summonAnswer {
  0% {
    transform: rotate(45deg) scale(0.2);
  }
  28% {
    transform: rotate(45deg) scale(2.7);
  }
  58% {
    transform: rotate(45deg) scale(1);
  }
  100% {
    transform: rotate(45deg) scale(1.7);
  }
}
@keyframes summonFlash {
  0% {
    opacity: 0.82;
    background: #ffe6c4;
  }
  100% {
    opacity: 0.12;
    background: #0e0616;
  }
}
@keyframes summonTrace {
  to {
    stroke-dashoffset: 0;
  }
}
@keyframes summonCost {
  0% {
    opacity: 0;
    transform: translateY(8px);
  }
  18% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translateY(-42px);
  }
}
@keyframes summonLore {
  to {
    opacity: 1;
  }
}
@media (max-width: 680px) {
  .survivor-hub {
    gap: 7px;
    padding: 10px !important;
  }
  .hub-battle-play {
    grid-column: 1 / -1;
    width: 100%;
    min-width: 0;
    padding: 10px !important;
  }
  .hub-party-slot {
    width: 38px;
    height: 38px;
  }
  .hub-party-slot img {
    width: 33px;
    height: 33px;
  }
  .gacha-scene {
    padding: 10px !important;
  }
  .gacha-shell {
    padding: 11px;
  }
  .gacha-ritual {
    min-height: 145px;
    margin: 10px 0;
  }
  .gacha-ritual::before {
    width: 185px;
    height: 185px;
  }
  .gacha-orbit {
    width: 142px;
    height: 142px;
  }
  .gacha-pool-unit {
    min-height: 66px;
    padding: 3px;
  }
  .gacha-pool-unit .portrait {
    width: 29px;
    height: 29px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .gacha-ritual::after,
  .gacha-orbit,
  .gacha-reveal.rolling,
  .gacha-reveal.revealed {
    animation: none;
  }
  .summon-motes i,
  .summon-answer,
  .summon-cost {
    animation: none !important;
    opacity: 0 !important;
  }
  .summon-circle {
    transition: none !important;
  }
  .summon-lines polyline {
    animation: none !important;
    stroke-dashoffset: 0 !important;
  }
  .summon-lore {
    animation: none !important;
    opacity: 1 !important;
  }
}

/* ---------------------------------------------------------------------
   PAUSE — ESC or the arena button. The backdrop stays translucent so the
   frozen table is still readable behind the card.
   --------------------------------------------------------------------- */
/* A plain two-bar control.  The dawn kit used to draw a crescent behind the
   glyph, which read as a shoe rather than as pause.
   `display` belongs to the scene rules below and to nothing else: the button
   exists in the arena markup at all times, so anything that sets a display
   value here unconditionally leaves it sitting on the hub and the menus. */
.pause-button {
  /* Top-left: the toast owns the top-right corner of the stage. */
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 6;
  display: none;
  place-items: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 1px solid #f2b35c66;
  border-radius: 50%;
  background: #11071cd9;
  color: #f3ede2;
  font-size: 14px;
  line-height: 1;
  letter-spacing: 1px;
  cursor: pointer;
  transition:
    border-color 0.16s ease,
    background 0.16s ease,
    transform 0.16s ease;
}
body.game-mode .pause-button {
  display: grid;
}
body.onboarding-locked .pause-button {
  display: none;
}
.pause-button:hover {
  border-color: #f2b35c;
  background: #1f0d32f2;
  transform: translateY(-1px);
}
.pause-button:focus-visible {
  outline: 2px solid var(--star);
  outline-offset: 2px;
}
.overlay.pause-scene {
  background: #0e0616c4 !important;
  backdrop-filter: blur(2px);
}
.pause-card {
  width: min(360px, 88vw);
  padding: 22px 20px 16px;
  border: 1px solid #f2b35c44;
  border-radius: 14px;
  background: linear-gradient(165deg, #1a0c27f2, #11071cf7);
  box-shadow:
    0 20px 60px #06010b99,
    0 0 0 1px #ffffff08 inset;
  text-align: center;
}
.pause-kicker {
  display: block;
  color: var(--gold);
  font-size: 10px;
  letter-spacing: 0.34em;
}
.pause-card h2 {
  margin: 10px 0 6px !important;
  color: #f3ede2;
  font-size: 19px !important;
  letter-spacing: -0.02em;
}
.pause-card p {
  margin: 0 auto 16px !important;
  max-width: 280px !important;
  color: var(--mist);
  font-size: 12px;
  line-height: 1.65;
}
.pause-actions {
  display: grid;
  gap: 7px;
}
.pause-actions button {
  width: 100%;
  padding: 11px 12px;
  border: 1px solid #3f2e53;
  border-radius: 10px;
  background: #1f0d32;
  color: #c3bcd1;
  font-size: 13px;
  cursor: pointer;
  transition:
    border-color 0.16s ease,
    background 0.16s ease,
    color 0.16s ease;
}
.pause-actions button:hover {
  border-color: #f2b35c;
  background: #25123a;
  color: #f3ede2;
}
.pause-actions button:focus-visible {
  outline: 2px solid var(--star);
  outline-offset: 2px;
}
/* The shared `.overlay button` brass/violet rule is heavy, so the pause
   actions restate their own palette rather than fight it on specificity. */
.pause-actions button {
  border: 1px solid #3f2e53 !important;
  background: #1f0d32 !important;
  box-shadow: none !important;
  color: #c3bcd1 !important;
}
.pause-actions button:hover {
  border-color: #f2b35c !important;
  background: #25123a !important;
  color: #f3ede2 !important;
}
.pause-actions .pause-primary {
  border-color: #f2b35c !important;
  background: linear-gradient(180deg, #ffd98e, #c98a3e) !important;
  color: #241708 !important;
  font-weight: 700;
}
.pause-actions .pause-primary:hover {
  background: linear-gradient(180deg, #ffd2a0, #cd7f4a) !important;
  color: #241708 !important;
}
.pause-actions .pause-exit {
  border-color: #3f2e5377 !important;
  color: #938aa4 !important;
}
.pause-hint {
  display: block;
  margin-top: 12px;
  color: #5c4e74;
  font-size: 10px;
  letter-spacing: 0.04em;
}
@media (max-width: 760px) {
  /* Left, not right: the button moved corners and this kept the old `right`,
     which does nothing while `left` is set but reads as the wrong intent. */
  .pause-button {
    top: 8px;
    left: 8px;
    width: 40px;
    height: 40px;
  }
  .pause-card {
    padding: 18px 15px 13px;
  }
  .pause-card h2 {
    font-size: 17px !important;
  }
  .pause-hint {
    display: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .pause-button,
  .pause-actions button {
    transition: none;
  }
}

/* ---------------------------------------------------------------------
   HUB MAP MODE — the constellation map lives inside the hub and the menu
   moves to a bottom tab bar, so the tall empty band under the old cards
   becomes the playfield of the screen.
   --------------------------------------------------------------------- */
.survivor-hub.hub-map-mode {
  gap: 9px;
}
.hub-map {
  position: relative;
  flex: 1 1 auto;
  min-height: 236px;
  /* Must be able to shrink below its content box on short screens. */
  flex-basis: 0;
  overflow: hidden;
  border: 1px solid #3f2e53;
  border-radius: 13px;
  background:
    radial-gradient(circle at 72% 16%, #f2b35c14, transparent 34%),
    radial-gradient(circle at 22% 74%, #4d288d1a, transparent 40%),
    linear-gradient(165deg, #170924f2, #0f0618f5);
  box-shadow:
    inset 0 0 0 1px #ffffff05,
    0 10px 26px #06010b88;
}
.hub-map .constellation-route {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.hub-map .constellation-route path {
  fill: none;
  stroke: #f2b35c66;
  stroke-width: 0.7;
  stroke-dasharray: 2 3;
}
.hub-map .constellation-route path.future {
  stroke: #3f2e5388;
}
.hub-map .constellation-node {
  width: 168px;
}
.hub-map .constellation-node .stage-star {
  width: 40px;
  height: 40px;
  font-size: 17px;
}
.hub-map .constellation-node b {
  font-size: 12px;
}
.hub-map .constellation-node .node-status {
  display: none;
}
/* The mission bar already carries the selected stage's full rule. Repeating
   that sentence on every map node made the edge nodes wider than the map and
   buried the constellation silhouette under scrolling labels. */
.hub-map .constellation-node .stage-copy > span {
  display: none;
}
.hub-mission-bar {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 12px;
  padding: 11px 13px;
  border: 1px solid #f2b35c88;
  border-radius: 12px;
  background: linear-gradient(115deg, #1a0c27f2, #11071cf6);
  box-shadow:
    0 8px 22px #06010b99,
    0 0 0 1px #f2b98814;
}
.hub-mission-bar .hub-mission-info {
  display: grid;
  flex: 1 1 auto;
  gap: 3px;
  min-width: 0;
}
.hub-mission-bar small {
  color: #9489a7;
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.12em;
}
.hub-mission-bar b {
  overflow: hidden;
  color: #f3ede2;
  font-size: 17px;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-shadow: 0 2px 0 #07020c;
}
.hub-mission-bar .hub-party-slots {
  display: flex;
  gap: 5px;
  margin-top: 2px;
}
.hub-mission-bar .hub-party-slot,
.hub-mission-bar .hub-party-slot.empty {
  width: 30px;
  height: 30px;
  min-height: 0;
}
.hub-mission-bar .hub-party-slot img {
  width: 25px;
  height: 25px;
}
.hub-mission-bar .hub-battle-play {
  flex: 0 0 auto;
}
.hub-tabbar {
  display: grid;
  flex: 0 0 auto;
  grid-template-columns: repeat(5, 1fr);
  gap: 7px;
  align-items: end;
  padding: 8px 8px 7px;
  border: 1px solid #45345a;
  border-radius: 13px;
  background: linear-gradient(180deg, #170a24f4, #0f051af8);
  box-shadow:
    0 -6px 18px #06010b66,
    0 8px 22px #06010b99;
}
.hub-tab {
  display: grid;
  gap: 3px;
  justify-items: center;
  padding: 8px 4px 7px;
  border: 1px solid #3f2e53aa !important;
  border-radius: 10px !important;
  background: #1b0d29 !important;
  box-shadow: none !important;
  color: #d2cedb !important;
  cursor: pointer;
  transition:
    border-color 0.16s ease,
    background 0.16s ease,
    transform 0.16s ease;
}
.hub-tab span {
  color: #877c9b;
  font-size: 15px;
  line-height: 1;
}
.hub-tab b {
  font-size: 10px;
  letter-spacing: 0.06em;
}
.hub-tab:hover {
  border-color: #f2b35c !important;
  background: #200f31 !important;
  color: #f3ede2 !important;
  transform: translateY(-2px);
}
.hub-tab:hover span {
  color: #ffd2a0;
}
.hub-tab:focus-visible {
  outline: 2px solid var(--star);
  outline-offset: 2px;
}
.hub-tab.center {
  padding: 12px 4px 10px;
  border-color: #f2b35c !important;
  background: linear-gradient(180deg, #ffd98e, #c98a3e) !important;
  color: #241708 !important;
  box-shadow:
    0 6px 16px #f2b35c33,
    0 0 0 1px #f3ede233 inset !important;
  transform: translateY(-6px);
}
.hub-tab.center span {
  color: #241708 !important;
  font-size: 18px;
}
.hub-tab.center b {
  font-size: 11px;
  font-weight: 800;
}
.hub-tab.center:hover {
  background: linear-gradient(180deg, #ffd2a0, #cd7f4a) !important;
  transform: translateY(-8px);
}
@media (max-width: 900px) {
  .hub-map .constellation-node {
    width: 148px;
    padding: 6px 7px 6px 6px;
  }
  .hub-map .constellation-node .stage-star {
    width: 34px;
    height: 34px;
    font-size: 15px;
  }
  .hub-map .constellation-node .node-status {
    display: none;
  }
}
@media (max-width: 760px) {
  /* Portrait has no room for full node cards, so each star collapses to a
     badge and the map shrinks to whatever height is left over. */
  .survivor-hub.hub-map-mode {
    gap: 6px;
  }
  .hub-map {
    min-height: 132px;
  }
  .hub-map .constellation-node {
    flex-direction: column;
    gap: 2px;
    width: 62px;
    padding: 6px 4px;
    text-align: center;
  }
  .hub-map .constellation-node .stage-star {
    width: 30px;
    height: 30px;
    font-size: 14px;
  }
  .hub-map .constellation-node b,
  .hub-map .constellation-node .stage-copy > span {
    display: none;
  }
  .hub-map .constellation-node small {
    font-size: 8px;
    letter-spacing: 0.04em;
  }
  .hub-mission-bar {
    gap: 9px;
    padding: 9px 10px;
  }
  .hub-mission-bar b {
    font-size: 15px;
  }
  .hub-mission-bar .hub-party-slot,
  .hub-mission-bar .hub-party-slot.empty {
    width: 26px;
    height: 26px;
  }
  .hub-mission-bar .hub-party-slot img {
    width: 21px;
    height: 21px;
  }
  .hub-mission-bar .hub-battle-play {
    padding: 9px 12px !important;
    font-size: 12px !important;
  }
  .hub-tabbar {
    gap: 5px;
    padding: 6px 6px 5px;
  }
  .hub-tab {
    padding: 6px 3px 5px;
  }
  .hub-tab span {
    font-size: 13px;
  }
  .hub-tab b {
    font-size: 9px;
  }
  .hub-tab.center {
    padding: 9px 3px 8px;
    transform: translateY(-4px);
  }
  .hub-tab.center span {
    font-size: 15px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .hub-tab,
  .hub-tab.center {
    transition: none;
  }
}

/* Luna's portrait was hidden when the lesson card became a thin dock banner.
   The six-card layout has room for her again. */
.onboarding-dock .onboarding-helper img {
  display: block !important;
  width: 26px;
  height: 26px;
  border: 1px solid #f2b35c66;
  border-radius: 50%;
  background: #0f0618;
  object-fit: cover;
  image-rendering: pixelated;
}
.onboarding-dock .onboarding-helper {
  display: flex !important;
  align-items: center;
  gap: 7px;
}

/* Hub map nodes are fixed-width but positioned by percentage, so narrow
   windows pushed the outer stars past the frame.  Scale the card with the
   map and pull the extremes inward. */
.hub-map .constellation-node {
  width: min(168px, 30%);
}
.hub-map .constellation-node.s1 {
  left: 20%;
  top: 75%;
}
.hub-map .constellation-node.s2 {
  left: 36%;
  top: 53%;
}
.hub-map .constellation-node.s3 {
  left: 57%;
  top: 31%;
}
.hub-map .constellation-node.s4 {
  left: 76%;
  top: 48%;
}
.hub-map .constellation-node.s5 {
  left: 70%;
  top: 75%;
}
@media (max-width: 760px) {
  .hub-map .constellation-node {
    width: 62px;
  }
}

/* ---------------------------------------------------------------------
   REWARD TOAST — the 연타 promotion pattern applied to earnings: a
   claim card that announces itself, then leaves.
   --------------------------------------------------------------------- */
.reward-toast {
  position: absolute;
  top: 18px;
  left: 50%;
  z-index: 9;
  display: grid;
  gap: 2px;
  justify-items: center;
  min-width: 190px;
  padding: 11px 18px;
  border: 1px solid #f2b35c;
  border-radius: 12px;
  background: linear-gradient(160deg, #201032f7, #11071cfa);
  box-shadow:
    0 12px 30px #06010baa,
    0 0 0 1px #f3ede226 inset;
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, -14px) scale(0.96);
  transition:
    opacity 0.26s ease,
    transform 0.26s cubic-bezier(0.2, 1.5, 0.4, 1);
}
.reward-toast.show {
  opacity: 1;
  transform: translate(-50%, 0) scale(1);
}
.reward-toast.actionable {
  cursor: pointer;
  pointer-events: auto;
  font: inherit;
  text-align: center;
}
.reward-toast.actionable::after {
  content: "클릭하여 업적 보기";
  color: #f0cf82;
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.08em;
}
.reward-toast small {
  color: var(--gold);
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.22em;
}
.reward-toast b {
  color: #f3ede2;
  font-size: 19px;
  text-shadow: 0 2px 0 #07020c;
}
.reward-toast span {
  color: var(--mist);
  font-size: 10px;
}
.reward-toast.inline {
  position: relative;
  top: auto;
  left: auto;
  z-index: auto;
  display: grid;
  grid-template-columns: auto auto auto;
  align-items: center;
  min-width: 0;
  width: max-content;
  max-width: 100%;
  margin: 6px auto;
  padding: 5px 10px;
  border-radius: 4px;
  transform: translateY(-8px) scale(0.98);
}
.reward-toast.inline.show {
  transform: translateY(0) scale(1);
}
.reward-toast.inline b {
  font-size: 12px;
}
.reward-toast.inline span {
  display: none;
}
.reward-toast.inline.actionable::after {
  content: "업적 보기";
}

/* ---------------------------------------------------------------------
   SHOP — cosmetic meteor skins bought with earned gold.
   --------------------------------------------------------------------- */
.overlay.shop-scene {
  padding: 0 !important;
  place-content: stretch !important;
}
.shop-shell {
  display: flex;
  flex-direction: column;
  gap: 12px;
  height: 100%;
  padding: 16px 18px 18px;
  overflow-y: auto;
  text-align: left;
}
.shop-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.shop-head span {
  text-align: right;
}
.shop-head small {
  display: block;
  color: var(--mist);
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.13em;
}
.shop-head b {
  display: block;
  margin-top: 3px;
  color: #ffc98e;
  font-size: 11px;
}
.shop-head button {
  padding: 6px 9px !important;
  font-size: 9px !important;
}
.shop-intro small {
  color: var(--gold);
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.24em;
}
.shop-intro h2 {
  margin: 5px 0 4px !important;
  color: #f3ede2;
  font-size: 22px !important;
}
.shop-intro p {
  margin: 0 !important;
  max-width: none !important;
  color: var(--mist);
  font-size: 11px;
}
.shop-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 10px;
}
.shop-card {
  display: grid;
  gap: 9px;
  justify-items: center;
  padding: 14px 12px 12px;
  border: 1px solid #3f2e53;
  border-radius: 12px;
  background: linear-gradient(165deg, #1c0e2af2, #10061bf7);
  text-align: center;
}
.shop-card.active {
  border-color: #f2b35c;
  box-shadow:
    0 0 0 1px #f2b35c44,
    0 8px 20px #06010b99;
}
.shop-orb {
  display: grid;
  width: 74px;
  height: 74px;
  place-items: center;
  border-radius: 50%;
  background: radial-gradient(circle, var(--skin-glow) 0%, transparent 68%);
}
.shop-orb img {
  width: 42px;
  height: 42px;
  image-rendering: pixelated;
  filter: hue-rotate(var(--skin-hue)) saturate(1.15);
}
.shop-copy b {
  display: block;
  color: #f3ede2;
  font-size: 13px;
}
.shop-copy small {
  display: block;
  margin-top: 3px;
  color: var(--mist);
  font-size: 10px;
}
.shop-buy {
  width: 100%;
  padding: 9px 10px !important;
  font-size: 11px !important;
}
.shop-price {
  display: block;
  margin-top: 6px;
  color: var(--star);
  font:
    700 11px ui-monospace,
    monospace;
  letter-spacing: 0.04em;
}
.shop-card.active .shop-price {
  color: var(--gold);
}
.shop-buy.short {
  opacity: 0.5;
  cursor: not-allowed;
}
.shop-state {
  color: var(--gold);
  font:
    700 10px ui-monospace,
    monospace;
  letter-spacing: 0.1em;
}
.hub-record-chip {
  cursor: pointer;
  font: inherit;
  text-align: inherit;
  transition:
    border-color 0.16s ease,
    color 0.16s ease;
}
.hub-record-chip:hover {
  border-color: #f2b35c !important;
  color: #f3ede2 !important;
}
.hub-mission-hint {
  display: block;
  margin-top: 3px;
  color: var(--mist);
  font-size: 10px;
}

/* ---------------------------------------------------------------------
   SQUAD — choosing the party and placing it is one decision, so it is one
   screen: the field map on top, every owned starkeeper in a tray below,
   drag one onto the other.  The map keeps the arena's 720x900 ratio so a
   slot that reads as "close to the colossus" really is.  The old
   .roster-shell / .deploy-* rules went with the two-step flow.
   --------------------------------------------------------------------- */
/* Absolute, not fixed: every menu screen fills the observatory column so the
   width never jumps between them.  See the MENU TABS block for the reasoning. */
.overlay.squad-scene {
  position: absolute !important;
  inset: 0 !important;
  z-index: 40;
  padding: 0 !important;
  place-content: stretch !important;
  /* `place-content: stretch`만으로는 칸이 넓어지는 것을 막지 못한다. 암묵
     격자 칸은 `auto`라 내용의 최대폭까지 자라고, 아래 트레이는 보유 별지기
     수만큼 가로로 길어진다 — 여덟 명이면 646px이라 `.squad-layout`이 712px로
     부풀고 `.stage`가 `overflow: hidden`으로 오른쪽 210px를 잘라 냈다.
     별지기가 셋일 때는 502px 안에 들어와 드러나지 않았다.
     `minmax(0, 1fr)`은 칸이 그릇보다 커질 수 없게 한다. 그러면 트레이가
     이미 가진 `min-width: 0` + `overflow-x: auto`가 비로소 작동해, 넘치는
     별지기는 잘리는 대신 가로로 굴러간다. */
  grid-template-columns: minmax(0, 1fr) !important;
}
.squad-layout {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  height: 100%;
  padding: 15px 20px 16px;
  text-align: left;
}
/* 부제는 한 줄을 통째로 쓰고, 제목과 안내문만 좌우로 나눈다. 셋을 한 줄에
   밀어 넣으면 제목 칸이 172px까지 눌려 부제와 제목이 함께 끊긴다. */
.squad-head {
  display: grid;
  flex: 0 0 auto;
  grid-template-columns: minmax(0, 1fr) minmax(0, 196px);
  align-items: end;
  column-gap: 16px;
}
.squad-head small {
  grid-column: 1 / -1;
  display: block;
  color: var(--gold);
  /* 8px에서는 Galmuri11의 획이 뭉개져 한글 부제를 읽을 수 없었다. */
  font:
    700 11px ui-monospace,
    monospace;
  letter-spacing: 0.14em;
}
.squad-head h2 {
  margin: 5px 0 0 !important;
  color: #f3ede2;
  font-size: 22px !important;
}
.squad-head p {
  grid-column: 2;
  margin: 0 !important;
  max-width: none !important;
  color: var(--mist);
  font-size: 11px;
  line-height: 1.6;
  text-align: right;
}

/* The map takes the full column height and the one detail card sits beside
   it, so the board stays as large as a 720x900 ratio allows on a landscape
   window.  Stacking the card underneath cost the map ~110px of height.
   Grid, not flex: the map sizes itself from `height: 100%`, and only an
   explicit `minmax(0, 1fr)` track gives that percentage a definite height to
   resolve against.  In a flex row the line height comes from the detail card
   instead, and the map collapses to it. */
.squad-field {
  display: grid;
  flex: 1 1 auto;
  /* 상세 카드가 262px를 고정으로 가져가면 배치판에 186px밖에 남지 않아,
     선언해 둔 720/900 비율을 지키지 못하고 0.607로 찌그러졌다(필요한 폭보다
     59px 부족). 상세는 세로도 307 중 217만 쓰던 칸이므로 폭을 내준다. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 200px);
  grid-template-rows: minmax(0, 1fr);
  gap: 14px;
  min-height: 0;
}
.squad-field .deployment-map {
  justify-self: end;
}
.deployment-map {
  position: relative;
  width: auto !important;
  height: 100% !important;
  max-width: 100%;
  aspect-ratio: 720 / 900;
  border: 1px solid #3f2e53;
  border-radius: 11px;
  background: linear-gradient(180deg, #1a1626 0%, #160923 62%, #10061b 100%);
  box-shadow: inset 0 0 0 1px #ffffff06;
}
.deployment-map .map-launch {
  position: absolute;
  bottom: 5px;
  left: 50%;
  color: #5c4e74;
  font:
    700 11px ui-monospace,
    monospace;
  letter-spacing: 0.12em;
  transform: translateX(-50%);
}
.deployment-map .map-boss {
  font-size: 22px;
}
/* The base sheet stamps a "고정 룬석 배치도" caption from an older layout.
   The header already names the stage, so the map does not repeat itself. */
.deployment-map:after {
  content: none !important;
}
/* A marker has to read as the unit standing there, not as a dot, so the
   portrait fills it and the name/zone hang outside the circle. */
.deployment-map .slot-card {
  display: grid !important;
  place-items: center;
  /* 말의 크기는 초상화 하나에서 나온다. CSS와 `setPortrait`이 서로 다른 값을
     쓰면 옆 프레임이 새므로(2026-08-18) 한 변수로 묶는다. 원본 스프라이트가
     192×192이라 48은 1/4 — 정수 배율이라 픽셀이 뭉개지지 않는다. */
  width: calc(var(--slot-portrait, 48px) + 12px) !important;
  height: calc(var(--slot-portrait, 48px) + 12px) !important;
  min-width: 0 !important;
  min-height: 0 !important;
  padding: 4px !important;
  overflow: visible;
  cursor: grab;
}
.deployment-map .slot-card .portrait {
  width: var(--slot-portrait, 48px) !important;
  height: var(--slot-portrait, 48px) !important;
}
.deployment-map .slot-card .slot-index {
  position: absolute;
  top: -3px;
  left: -3px;
  display: grid;
  place-items: center;
  /* 말이 작아졌으므로 번호 표식도 같은 비율로 줄인다. */
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: linear-gradient(180deg, #ffd98e, #c98a3e);
  color: #241708;
  font:
    700 10px ui-monospace,
    monospace !important;
}
.deployment-map .slot-card .slot-name {
  position: absolute;
  top: calc(100% + 5px);
  left: 50%;
  color: #f3ede2;
  font-size: 11px;
  white-space: nowrap;
  text-shadow: 0 2px 0 #07020c;
  transform: translateX(-50%);
}
.deployment-map .slot-card .slot-zone {
  position: absolute;
  top: calc(100% + 22px);
  left: 50%;
  margin: 0;
  color: var(--mist);
  font:
    700 10px ui-monospace,
    monospace;
  letter-spacing: 0.06em;
  white-space: nowrap;
  transform: translateX(-50%);
}
.deployment-map .slot-card.empty {
  border-color: #49375f !important;
  background: #160923b8 !important;
  box-shadow: 0 0 0 5px #11041e !important;
}
.deployment-map .slot-card.empty .slot-name {
  color: #5c4e74;
}
.deployment-map .slot-card.dragging {
  opacity: 0.4;
}
.deployment-map .slot-card.drop-ready {
  border-color: #f6c48e !important;
  box-shadow:
    0 0 0 5px #11041e,
    0 0 0 7px #f6c48e66 !important;
}

.squad-detail {
  display: flex;
  flex: 0 1 262px;
  flex-direction: column;
  gap: 9px;
  align-self: center;
  max-width: 262px;
  padding: 13px;
  border: 1px solid #3f2e53;
  border-radius: 12px;
  background: #160923d9;
}
.squad-detail-portrait {
  flex: 0 0 auto;
  width: 58px;
  height: 58px;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  image-rendering: pixelated;
  filter: drop-shadow(0 2px 0 #07020c);
}
.squad-detail-copy {
  min-width: 0;
}
.squad-detail-copy b {
  display: block;
  font-size: 14px;
}
.squad-detail-copy small {
  display: block;
  margin-top: 2px;
  color: var(--mist);
  font-size: 10px;
}
.squad-detail-copy p {
  margin: 7px 0 0 !important;
  max-width: none !important;
  color: #aaa2bb;
  font-size: 10px;
  line-height: 1.6;
}
.squad-detail-zone {
  margin-top: auto;
  padding-top: 9px;
  border-top: 1px solid #2d1d41;
  color: var(--star);
  font:
    700 10px ui-monospace,
    monospace;
}
.squad-detail-zone i {
  display: block;
  margin-top: 4px;
  color: var(--mist);
  /* 자리 설명은 배치를 고르는 근거다. 9px로 두면 그 근거를 읽을 수 없다. */
  font:
    400 11px ui-monospace,
    monospace;
  line-height: 1.5;
}

.squad-tray-shell {
  flex: 0 0 auto;
  /* flex 항목의 기본 min-width: auto는 내용보다 작아지기를 거부한다.
     그래서 안쪽 트레이의 가로 스크롤이 켜지지 않고 상자가 대신 늘어났다. */
  min-width: 0;
  padding: 9px 12px 10px;
  border: 1px solid #3f2e53;
  border-radius: 13px;
  background: #160923d9;
}
.squad-tray-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 8px;
}
.squad-tray-head small {
  color: var(--gold);
  /* 「보유 별지기」도 8px이었다. 한글은 이 크기에서 읽히지 않는다. */
  font:
    700 11px ui-monospace,
    monospace;
  letter-spacing: 0.14em;
}
.squad-tray-head b {
  color: #f3ede2;
  font-size: 11px;
}
.squad-tray {
  display: flex;
  /* 부모가 내용폭을 그대로 물려주면 이 auto는 영원히 켜지지 않는다. */
  min-width: 0;
  gap: 10px;
  overflow-x: auto;
  padding-bottom: 3px;
}
.squad-unit {
  position: relative;
  display: grid !important;
  flex: 0 0 auto;
  justify-items: center;
  gap: 3px;
  /* 칸 폭은 초상화에서 따라 나온다 — 좌우 여백 4px씩과 테두리 1px씩. */
  width: calc(var(--tray-portrait, 48px) + 10px);
  padding: 7px 4px 6px !important;
  border: 1px solid #47345d !important;
  border-radius: 12px !important;
  background: #200f31 !important;
  box-shadow: 0 3px 0 #08020e !important;
  cursor: grab;
}
.squad-unit .portrait {
  width: var(--tray-portrait, 48px);
  height: var(--tray-portrait, 48px);
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  image-rendering: pixelated;
  filter: drop-shadow(0 2px 0 #07020c);
}
.squad-unit b {
  color: #bfb8ce;
  font-size: 10px;
}
.squad-unit:hover {
  border-color: #f6c48e !important;
  background: #2a1541 !important;
}
.squad-unit.on {
  border-color: var(--unit) !important;
  background: #26113c !important;
  box-shadow:
    0 3px 0 #08020e,
    0 0 0 1px color-mix(in srgb, var(--unit) 42%, transparent) !important;
}
.squad-unit.on b {
  color: #f3ede2;
}
.squad-unit.picked {
  border-color: #f6c48e !important;
  box-shadow:
    0 3px 0 #08020e,
    0 0 0 2px #f6c48e66 !important;
}
.squad-unit.dragging {
  opacity: 0.4;
}
.squad-slot-mark {
  position: absolute;
  top: -7px;
  right: -7px;
  display: grid;
  place-items: center;
  width: 21px;
  height: 21px;
  border-radius: 50%;
  background: linear-gradient(180deg, #ffd98e, #c98a3e);
  color: #241708;
  font:
    700 10px ui-monospace,
    monospace;
  font-style: normal;
}
.overlay.squad-scene .overlay-actions {
  flex: 0 0 auto;
  justify-content: center;
}
@media (max-width: 860px) {
  .squad-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
  }
  .squad-head p {
    max-width: none !important;
    text-align: left;
  }
  .squad-detail {
    flex-wrap: wrap;
  }
  .squad-detail-zone {
    text-align: left;
  }
}

/* ---------------------------------------------------------------------
   CLAIMS — earned gold waits in the 업적 tab until the player collects it,
   so the hub badge and the claim buttons are the only "free money" surface.
   --------------------------------------------------------------------- */
/* Red, not brass: this badge is the only "you left something behind" mark in
   the game, and brass on a brass hub chip read as decoration. */
.claim-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 2px 6px;
  border-radius: 999px;
  background: linear-gradient(180deg, #f2564e, #b32a28);
  color: #fff1ea;
  font:
    700 8px ui-monospace,
    monospace;
  font-style: normal;
  letter-spacing: 0.04em;
  vertical-align: middle;
  box-shadow:
    0 0 0 1px #4a0f0f,
    0 0 10px #f2564e55;
}
.claim-badge.card-dot {
  position: absolute;
  top: -7px;
  right: -6px;
  z-index: 2;
  margin: 0;
  padding: 3px 7px;
  position: relative;
  box-shadow:
    0 0 0 1px #4a0f0f,
    0 0 8px #f2564e55;
}
/* 위 clearedStarPulse와 같은 이유 - blur 반경 애니메이션 대신 교차 페이드. */
.claim-badge.card-dot:after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  box-shadow:
    0 0 0 1px #4a0f0f,
    0 0 15px #f2564eaa;
  opacity: 0;
  animation: claimPulse 1.6s ease-in-out infinite;
}
@keyframes claimPulse {
  0%,
  100% {
    opacity: 0;
  }
  50% {
    opacity: 1;
  }
}
@media (prefers-reduced-motion: reduce) {
  .claim-badge.card-dot {
    animation: none;
  }
}
/* Wider cards, so the copy can be read instead of squeezed: the grid used to
   fit five 233px columns and every card ran 8-9px type at three lines. */
.achievement-card {
  position: relative;
  overflow: visible;
  min-height: 118px !important;
  padding: 12px 13px 12px 74px !important;
  border-radius: 8px !important;
  background:
    linear-gradient(90deg, var(--record-tone, #d6a55c) 0 3px, transparent 3px),
    linear-gradient(145deg, #180c22, #110919) !important;
}
.achievement-card:nth-child(3n + 2) {
  --record-tone: #69b8b2;
  background:
    linear-gradient(90deg, var(--record-tone) 0 3px, transparent 3px),
    linear-gradient(145deg, #0f1a21, #0d1018) !important;
}
.achievement-card:nth-child(3n) {
  --record-tone: #c57996;
  background:
    linear-gradient(90deg, var(--record-tone) 0 3px, transparent 3px),
    linear-gradient(145deg, #20101d, #120a16) !important;
}
.achievement-card.locked {
  --record-tone: #625b69;
}
.achievement-card img {
  left: 13px !important;
  top: 15px !important;
  width: 46px !important;
  height: 46px !important;
}
.achievement-card b {
  font-size: 13px;
}
.achievement-card small {
  display: block;
  margin-top: 4px;
  color: #aaa2bb;
  font-size: 11px;
  line-height: 1.55;
}
.achievement-card em {
  display: block;
  margin-top: 5px;
  color: var(--mist);
  font:
    700 9px ui-monospace,
    monospace;
  font-style: normal;
  letter-spacing: 0.05em;
}
.achievement-reward {
  font-size: 11px !important;
}
.achievement-claim {
  font-size: 11px !important;
}
.achievement-claim-state {
  font-size: 10px !important;
}
.hub-record-chip.has-claim {
  border-color: #e2564e !important;
}
.claim-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 10px 0;
  padding: 12px 14px;
  border: 1px solid #3f2e53;
  border-radius: 12px;
  background: linear-gradient(115deg, #1a0c27f2, #11071cf7);
  text-align: left;
}
.claim-banner.ready {
  border-color: #f2b35c;
  box-shadow:
    0 0 0 1px #f2b35c33,
    0 8px 20px #06010b88;
}
.claim-banner small {
  display: block;
  color: var(--gold);
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.2em;
}
/* Galmuri11 is a bitmap face: at 21px the ascenders of 쌓/찾 were clipped.
   Multiples of 11 are the only sizes it renders whole. */
.claim-banner b {
  display: block;
  margin: 4px 0 3px;
  color: #f3ede2;
  font-size: 22px;
  line-height: 1.35;
  text-shadow: 0 2px 0 #07020c;
}
.claim-banner span {
  display: block;
  color: var(--mist);
  font-size: 10px;
}
.claim-banner button {
  flex: 0 0 auto;
  padding: 10px 14px !important;
  font-size: 11px !important;
}
.claim-banner button[disabled] {
  opacity: 0.4;
  cursor: not-allowed;
}
.achievement-card.claimable {
  border-color: #f2b35c !important;
  box-shadow:
    0 0 0 1px #f2b35c33,
    0 8px 18px #06010b88 !important;
}
.achievement-reward {
  display: block;
  margin-top: 6px;
  color: var(--star);
  font:
    700 10px ui-monospace,
    monospace;
}
.achievement-claim {
  width: 100%;
  margin-top: 7px;
  padding: 8px 10px !important;
  font-size: 10px !important;
}
.achievement-claim-state {
  display: block;
  margin-top: 7px;
  color: #5c4e74;
  font:
    700 9px ui-monospace,
    monospace;
  letter-spacing: 0.06em;
}

/* ---------------------------------------------------------------------
   MARQUEE — long stage names scroll instead of being cut with an ellipsis.
   Only names that actually overflow get the animation (JS adds .scrolling),
   so short names stay still.
   --------------------------------------------------------------------- */
.marquee {
  position: relative;
  overflow: hidden;
}
.marquee > span {
  display: inline-block;
  white-space: nowrap;
}
.marquee.scrolling > span {
  padding-right: 42px;
  animation: marqueeSlide var(--marquee-duration, 9s) linear infinite;
}
.marquee.scrolling:hover > span {
  animation-play-state: paused;
}
@keyframes marqueeSlide {
  0%,
  12% {
    transform: translateX(0);
  }
  88%,
  100% {
    transform: translateX(calc(-1 * var(--marquee-shift, 0px)));
  }
}
.hub-mission-bar b.marquee {
  text-overflow: clip;
  white-space: normal;
}
.hub-map .constellation-node .stage-copy > span.marquee {
  text-overflow: clip;
}

/* ---------------------------------------------------------------------
   CLEARED STAGES — a returned star is lit, so progress is visible on the
   map at a glance instead of only in a counter.
   --------------------------------------------------------------------- */
.constellation-node.cleared {
  border-color: #f2b35c !important;
}
.constellation-node.cleared .stage-star {
  border-color: #ffd9a8;
  position: relative;
  box-shadow:
    0 0 0 4px #f6b57c22,
    0 0 26px #ffb987aa,
    0 0 46px #f2b35c66;
}
/* box-shadow의 blur 반경을 애니메이션하면 매 프레임 큰 흐림 영역을 다시
   칠한다. 게다가 클리어한 별 전부에 걸리므로 진행도가 쌓일수록 비싸지고,
   허브는 플레이어가 가장 오래 머무는 화면이다. 밝은 쪽 그림자를 고정으로
   한 번만 굽고 불투명도로 교차시킨다 — 합성기에서 처리되고 보이는 결과는
   거의 같다. */
.constellation-node.cleared .stage-star:after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  pointer-events: none;
  box-shadow:
    0 0 0 5px #f6b57c33,
    0 0 34px #ffb987ee,
    0 0 62px #f2b35c88;
  opacity: 0;
  animation: clearedStarPulse 3.4s ease-in-out infinite;
}
.constellation-node.cleared .node-status,
.constellation-node.cleared .stage-cleared-mark {
  color: #ffd9a8;
}
.stage-cleared-mark {
  position: absolute;
  top: -7px;
  right: -7px;
  padding: 2px 6px;
  border-radius: 999px;
  background: linear-gradient(180deg, #ffd98e, #c98a3e);
  color: #241708;
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.06em;
}
@keyframes clearedStarPulse {
  0%,
  100% {
    opacity: 0;
  }
  50% {
    opacity: 1;
  }
}
@media (prefers-reduced-motion: reduce) {
  .constellation-node.cleared .stage-star {
    animation: none;
  }
  .marquee.scrolling > span {
    animation: none;
  }
}

/* ---------------------------------------------------------------------
   CONFIRM DIALOG — leaving for the title screen asks first, reusing the
   pause card's language so the two reads the same.
   --------------------------------------------------------------------- */
.overlay.confirm-scene {
  background: #0e0616cc !important;
  backdrop-filter: blur(2px);
}
.confirm-card {
  width: min(340px, 88vw);
  padding: 20px 18px 15px;
  border: 1px solid #f2b35c44;
  border-radius: 14px;
  background: linear-gradient(165deg, #1a0c27f2, #11071cf7);
  box-shadow:
    0 20px 60px #06010b99,
    0 0 0 1px #ffffff08 inset;
  text-align: center;
}
.confirm-card small {
  display: block;
  color: var(--gold);
  font:
    700 10px ui-monospace,
    monospace;
  letter-spacing: 0.3em;
}
.confirm-card h2 {
  margin: 10px 0 6px !important;
  color: #f3ede2;
  font-size: 18px !important;
}
.confirm-card p {
  margin: 0 auto 15px !important;
  max-width: 260px !important;
  color: var(--mist);
  font-size: 11px;
  line-height: 1.6;
}
.confirm-actions {
  display: grid;
  gap: 7px;
}
.confirm-actions button {
  width: 100%;
  padding: 11px 12px !important;
  border: 1px solid #3f2e53 !important;
  border-radius: 10px !important;
  background: #1f0d32 !important;
  box-shadow: none !important;
  color: #c3bcd1 !important;
  font-size: 12px !important;
}
.confirm-actions button:hover {
  border-color: #f2b35c !important;
  background: #25123a !important;
  color: #f3ede2 !important;
}
.confirm-actions .confirm-yes {
  border-color: #f2b35c !important;
  background: linear-gradient(180deg, #ffd98e, #c98a3e) !important;
  color: #241708 !important;
  font-weight: 700;
}

/* ---------------------------------------------------------------------
   MENU TABS — summon, shop and profile fill the observatory column, not the
   window.  They used to be `position: fixed`, which took the whole viewport
   and made the width jump 502px -> 1280px every time one was opened from the
   hub.  Measured at 502px none of them overflow, so the column costs nothing
   and keeps the dawn sky and its scattered props visible on both sides —
   that framing is what OUTER_OBSERVER_INTRO_SPEC's margin beats depend on.
   --------------------------------------------------------------------- */
.overlay.gacha-scene,
.overlay.shop-scene,
.overlay.profile-scene {
  position: absolute !important;
  inset: 0 !important;
  z-index: 40;
  padding: 0 !important;
  place-content: stretch !important;
}
.gacha-shell,
.shop-shell,
.profile-shell {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
  max-width: none !important;
  height: 100%;
  padding: 20px 24px 22px !important;
  overflow-y: auto;
  text-align: left;
}
.gacha-ritual {
  flex: 1 1 auto;
  min-height: 190px !important;
}
.gacha-pool-grid {
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)) !important;
}
.gacha-draw {
  flex: 0 0 auto;
}
.shop-grid {
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  align-content: start;
}

/* Shopkeeper -------------------------------------------------------- */
.shop-keeper {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 13px;
  border: 1px solid #3f2e53;
  border-left: 3px solid #f2b35c;
  border-radius: 11px;
  background: linear-gradient(115deg, #1c0e2af2, #11071cf7);
}
.shop-keeper-art {
  display: grid;
  flex: 0 0 auto;
  width: 58px;
  height: 58px;
  place-items: center;
  overflow: hidden;
  border: 1px solid #f2b35c66;
  border-radius: 50%;
  background: radial-gradient(circle, #2b1b3f 0%, #0f0618 72%);
  color: var(--star);
  font-size: 20px;
}
.shop-keeper-art img {
  width: 46px;
  height: auto;
  image-rendering: pixelated;
  filter: drop-shadow(0 2px 0 #07020c);
}
.shop-keeper small {
  display: block;
  color: var(--gold);
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.16em;
}
.shop-keeper p {
  margin: 4px 0 0 !important;
  max-width: none !important;
  color: #d2cedb;
  font-size: 12px;
  line-height: 1.55;
}

/* Profile ----------------------------------------------------------- */
.profile-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.profile-head span {
  text-align: right;
}
.profile-head small {
  display: block;
  color: var(--mist);
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.16em;
}
.profile-head b {
  display: block;
  margin-top: 3px;
  color: #f3ede2;
  font-size: 13px;
}
/* The observatory ID gets a face.  Every option is either a rune stone the
   build already ships or a silhouette the dawn kit draws, so the picker costs
   no new art. */
.profile-identity {
  display: flex;
  align-items: center;
  gap: 12px;
}
.profile-avatar {
  position: relative;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  padding: 0 !important;
  border: 1px solid #f2b35c66 !important;
  border-radius: 50% !important;
  background: radial-gradient(circle, #2b1b3f 0%, #0f0618 72%) !important;
  box-shadow: none !important;
  cursor: pointer;
}
.profile-avatar:hover {
  border-color: #f6c48e !important;
}
.profile-avatar img {
  width: 42px;
  max-height: 44px;
  object-fit: contain;
  image-rendering: pixelated;
  filter: drop-shadow(0 2px 0 #07020c);
}
.profile-avatar-edit {
  position: absolute;
  right: -3px;
  bottom: -3px;
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: linear-gradient(180deg, #ffd98e, #c98a3e);
  color: #241708;
  font:
    700 10px ui-monospace,
    monospace;
  font-style: normal;
}
.icon-picker {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: 24px;
  background: #08020ecc;
  backdrop-filter: blur(2px);
}
.icon-picker-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: min(560px, 100%);
  max-height: min(78vh, 620px);
  padding: 16px 18px 18px;
  border: 1px solid #3f2e53;
  border-radius: 14px;
  background: linear-gradient(150deg, #200f31, #11071c);
  box-shadow: 0 22px 50px #06010b99;
  text-align: left;
}
.icon-picker-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.icon-picker-head small {
  display: block;
  color: var(--gold);
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.18em;
}
.icon-picker-head h3 {
  margin: 5px 0 0;
  color: #f3ede2;
  font-size: 18px;
}
.icon-picker-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(94px, 1fr));
  gap: 9px;
  overflow-y: auto;
  padding-right: 3px;
}
.profile-icon-option {
  display: grid !important;
  justify-items: center;
  gap: 6px;
  padding: 11px 6px 9px !important;
  border: 1px solid #47345d !important;
  border-radius: 12px !important;
  background: #170a24 !important;
  color: #bfb8ce !important;
  font-size: 10px !important;
  cursor: pointer;
}
.profile-icon-option img {
  width: 44px;
  height: 44px;
  object-fit: contain;
  image-rendering: pixelated;
  filter: drop-shadow(0 2px 0 #07020c);
}
.profile-icon-option:hover {
  border-color: #f6c48e !important;
  background: #26113c !important;
}
.profile-icon-option.on {
  border-color: #f6c48e !important;
  background: #2a1541 !important;
  color: #f3ede2 !important;
  box-shadow: 0 0 0 1px #f6c48e55 !important;
}
.profile-head button {
  padding: 6px 9px !important;
  font-size: 9px !important;
}
.profile-grid {
  display: grid;
  flex: 1 1 auto;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 12px;
  align-content: start;
  min-height: 0;
}
.profile-panel {
  position: relative;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 15px 15px 13px;
  border: 1px solid #3f2e53;
  border-radius: 8px;
  background: linear-gradient(165deg, #1a0c27f0, #10061bf7);
}
.profile-panel::before {
  position: absolute;
  top: 0;
  left: 0;
  width: 42%;
  height: 2px;
  background: var(--profile-line, #f2b35c);
  content: "";
  opacity: 0.82;
}
.profile-panel:nth-child(2) {
  --profile-line: #73c9c2;
  background: linear-gradient(165deg, #101c27f0, #0d111cf7);
}
.profile-panel.mail-panel {
  --profile-line: #d58aa6;
  background: linear-gradient(165deg, #21101ff0, #120a18f7);
}
.panel-title small {
  display: block;
  color: var(--gold);
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.24em;
}
.panel-title h2 {
  margin: 5px 0 0 !important;
  color: #f3ede2;
  font-size: 17px !important;
}
.profile-panel .profile-stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 7px;
}
.profile-panel .profile-stat {
  padding: 9px 10px;
  border: 1px solid #352547;
  border-radius: 9px;
  background: #160923;
}
.profile-panel .profile-stat small {
  display: block;
  color: var(--mist);
  font-size: 9px;
}
.profile-panel .profile-stat b {
  display: block;
  margin-top: 3px;
  color: #f3ede2;
  font-size: 15px;
}
.rank-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 11px;
}
.rank-table th {
  padding: 6px 5px;
  border-bottom: 1px solid #352547;
  color: var(--mist);
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.1em;
  text-align: left;
}
.rank-table td {
  padding: 8px 5px;
  border-bottom: 1px solid #25143a00;
  color: #d2cedb;
}
.rank-table tr.you td {
  color: #f3ede2;
}
.rank-table tr.you td:first-child {
  color: var(--star);
  font-weight: 700;
}
.rank-note {
  margin: auto 0 0 !important;
  max-width: none !important;
  color: #5c4e74;
  font-size: 10px;
  line-height: 1.6;
}
.rank-note b {
  color: var(--mist);
}
.mail-list {
  display: grid;
  gap: 7px;
  align-content: start;
}
.mail-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 11px;
  border: 1px solid #3f2e53;
  border-radius: 10px;
  background: #170a24;
}
.mail-item.read {
  opacity: 0.55;
}
.mail-item b {
  display: block;
  color: #f3ede2;
  font-size: 12px;
}
.mail-item small {
  display: block;
  margin-top: 3px;
  color: var(--mist);
  font-size: 10px;
}
.mail-item button {
  flex: 0 0 auto;
  padding: 8px 10px !important;
  font-size: 10px !important;
}
.mail-state {
  flex: 0 0 auto;
  color: #5c4e74;
  font:
    700 9px ui-monospace,
    monospace;
}
.mail-empty {
  margin: 0 !important;
  max-width: none !important;
  color: #5c4e74;
  font-size: 11px;
}
@media (max-width: 760px) {
  .gacha-shell,
  .shop-shell,
  .profile-shell {
    padding: 14px 14px 16px !important;
  }
  .profile-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* The old Prism Breakers key art still showed through the title backdrop.
   Stella Ball owns this screen now, so the brand card is replaced by the
   observatory gradient it was sitting under. */
.title-sequence:before {
  background:
    linear-gradient(180deg, #0f0618a8 0%, transparent 34%, #0e0616f2 92%),
    radial-gradient(
      ellipse at 50% 26%,
      #2b1b3f66 0%,
      #170a24e6 46%,
      #0e0616 88%
    ) !important;
  opacity: 1 !important;
}

/* ---------------------------------------------------------------------
   ACHIEVEMENTS — the observatory column, its own scroll region, and a back
   button that stays reachable instead of being clipped off the bottom.
   Absolute rather than fixed so the width matches every other menu screen;
   the scroll region below is what keeps the grid readable in the narrower
   column, and it is why narrowing does not clip the back button.
   --------------------------------------------------------------------- */
.overlay.archive-scene {
  position: absolute !important;
  inset: 0 !important;
  z-index: 40;
  padding: 0 !important;
  place-content: stretch !important;
}
.overlay.archive-scene .meta-hub {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
  max-width: none !important;
  height: 100%;
  padding: 18px 22px 16px !important;
  min-height: 0;
}
.overlay.archive-scene .system-panel {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: 10px;
  min-height: 0;
  width: auto !important;
  max-width: none !important;
  /* The shared panel centres itself with auto side margins; the archive uses
     the whole column instead. */
  margin: 0 !important;
}
.overlay.archive-scene .profile-stats {
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.overlay.archive-scene .achievement-grid {
  flex: 0 0 auto;
  /* 이 값을 220px 으로 낮춰 두 열을 세워 봤다가 되돌렸다. 두 열은 서지만
     카드 폭이 224px 이 되어 다섯 장 전부에서 글자가 25~42px 넘쳤고,
     늘어난 높이가 「뒤로」를 화면 밖으로 밀었다. 「측정이 이득을 보이지
     않으면 넣지 않는다」. 열을 늘리려면 카드 레이아웃부터 좁은 폭을
     감당해야 한다. */
  grid-template-columns: repeat(auto-fill, minmax(304px, 1fr)) !important;
  align-content: start;
  /* 카드 «한 장»을 바닥으로 둔다. 이전에는 min-height:0 이라 창이 낮을 때
     이 칸이 0px 까지 눌렸다 — 1280x760 에서 업적 다섯 장이 한 장도 안
     보였다(높이 0, 스크롤바조차 없음). 위에 쌓인 것이 338px 인데 패널이
     462px 뿐이라 남은 자리를 전부 뺏겼기 때문이다.
     바닥을 «한 장»으로 잡은 이유: 1280x900 에서 이 칸은 자연히 142px 을
     받으므로 118px 바닥은 아무것도 바꾸지 않는다. 눌려 죽는 창에서만
     발동해서, 멀쩡한 크기의 배치를 건드리지 않는다. */
  min-height: 118px;
  /* 행이 «내용»이 아니라 카드의 min-height(118px)로 잡히고 있었다. 수령
     가능한 카드는 「수령하기」 버튼(30px + 여백 7px)이 더 붙어 내용이
     142px 인데, 행이 118px 이라 그 버튼이 카드 테두리 «밖으로» 25px
     삐져나와 다음 카드에 잘렸다 — 이 화면에서 골드를 주는 유일한 버튼이다.
     카드가 overflow:visible 이라 잘리지 않고 겹쳐 그려져서 더 눈에 띈다. */
  grid-auto-rows: max-content;
  overflow: visible;
  padding-right: 4px;
}
/* 바닥을 준 대신 패널이 넘칠 수 있다. 넘칠 때만 패널이 스크롤한다 —
   자리가 넉넉하면 여전히 목록만 스크롤하므로 탭과 「뒤로」는 그대로 붙어
   있고, 좁을 때만 상점·프로필과 같은 «패널 통째 스크롤»로 떨어진다. */
.overlay.archive-scene .system-panel {
  overflow-y: auto;
}
.overlay.archive-scene .settings-actions {
  flex: 0 0 auto;
  margin-top: auto;
  padding-top: 4px;
}

/* Archive and library used an inner list scrollbar inside an already
   scrollable panel. One scroll surface keeps the heading, cards and exit in a
   predictable reading order and avoids a detached button well at the bottom. */
.overlay.library-scene .codex-split {
  flex: 0 0 auto;
  align-items: flex-start;
}
.overlay.library-scene .codex-list {
  overflow: visible;
}

@media (min-height: 700px) {
  .overlay.settings-scene {
    place-content: center !important;
  }
  .overlay.settings-scene .meta-hub {
    height: auto;
  }
}
.overlay.archive-scene .settings-actions button {
  min-width: 120px;
}

/* ---------------------------------------------------------------------
   CLAIM FEEL — the card is pressed, pops, then the reward lifts away.
   Reference: opening a chest — squash on press, overshoot on release.
   --------------------------------------------------------------------- */
.achievement-claim,
.claim-banner button,
.mail-item button {
  transition:
    transform 0.12s cubic-bezier(0.2, 1.6, 0.4, 1),
    border-color 0.16s ease,
    background 0.16s ease;
}
.achievement-claim:active,
.claim-banner button:active,
.mail-item button:active {
  transform: scale(0.94);
}
.achievement-card.claiming {
  animation: claimPop 0.52s cubic-bezier(0.2, 1.5, 0.35, 1);
}
.claim-banner.claiming {
  animation: claimPop 0.52s cubic-bezier(0.2, 1.5, 0.35, 1);
}
@keyframes claimPop {
  0% {
    transform: scale(1);
  }
  22% {
    transform: scale(0.955);
  }
  52% {
    transform: scale(1.045);
  }
  76% {
    transform: scale(0.99);
  }
  100% {
    transform: scale(1);
  }
}
.claim-burst {
  position: absolute;
  z-index: 12;
  color: var(--star);
  font:
    700 15px ui-monospace,
    monospace;
  text-shadow: 0 2px 0 #07020c;
  pointer-events: none;
  animation: claimFloat 0.95s cubic-bezier(0.15, 0.8, 0.3, 1) forwards;
}
@keyframes claimFloat {
  0% {
    opacity: 0;
    transform: translate(-50%, 0) scale(0.7);
  }
  18% {
    opacity: 1;
    transform: translate(-50%, -12px) scale(1.12);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -54px) scale(1);
  }
}
.claim-spark {
  position: absolute;
  z-index: 11;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--star);
  box-shadow: 0 0 8px #ffd2a0;
  pointer-events: none;
  animation: claimSpark 0.7s ease-out forwards;
}
@keyframes claimSpark {
  0% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
  }
  100% {
    opacity: 0;
    transform: translate(
        calc(-50% + var(--sx, 0px)),
        calc(-50% + var(--sy, 0px))
      )
      scale(0.3);
  }
}
@media (prefers-reduced-motion: reduce) {
  .achievement-card.claiming,
  .claim-banner.claiming {
    animation: none;
  }
  .claim-burst,
  .claim-spark {
    display: none;
  }
}

/* ---------------------------------------------------------------------
   ONBOARDING — the lesson is the thing to read, so it sits in the middle
   of the frame at a size you can actually read, not tucked into a corner
   at 10px.  Once the player is shooting, the card leaves the table and
   becomes a slim bar above it, so the arena is never explained over.
   The base sheet still paints this card in the old navy; the dawn
   palette lands here too.
   --------------------------------------------------------------------- */
.onboarding-dock {
  align-items: center !important;
  justify-content: center !important;
  padding: 0 !important;
}
.onboarding-dock .onboarding-card {
  width: min(444px, calc(100% - 34px)) !important;
  max-height: none !important;
  padding: 17px 19px 15px !important;
  border: 1px solid #5b4b70 !important;
  border-radius: 15px !important;
  background: linear-gradient(150deg, #210f34f7, #11071cf7) !important;
  box-shadow:
    0 22px 52px #06010bcc,
    0 0 0 1px #f6c48e2e !important;
  overflow: visible !important;
}
.onboarding-dock .onboarding-card:before {
  display: none;
}
.onboarding-dock .onboarding-kicker {
  color: var(--gold) !important;
  font-size: 9px !important;
}
.onboarding-dock .onboarding-card h3 {
  margin: 9px 0 7px !important;
  color: #f3ede2 !important;
  font-size: 17px !important;
  line-height: 1.45 !important;
  text-shadow: 0 2px 0 #07020c !important;
}
.onboarding-dock .onboarding-card p,
.onboarding-dock .onboarding-card .onboarding-copy {
  color: #c3bcd1 !important;
  font-size: 13px !important;
  line-height: 1.7 !important;
}
.onboarding-dock .onboarding-card button {
  width: 100%;
  margin-top: 12px !important;
  padding: 13px 16px !important;
  font-size: 13px !important;
}
/* 강조 비트(2026-08-23): 설명 카드가 자기 스포트라이트를 가리지 않게 판
   하단으로 내린다. 별지기는 판 중단, 거상·안내별은 상단이라 하단 고정이
   모두를 비운다. 개념 하나짜리라 카드도 작게 둔다. */
.onboarding-dock .onboarding-card.onboarding-emphasis {
  align-self: flex-end !important;
  width: min(400px, calc(100% - 28px)) !important;
  margin-bottom: 12px !important;
  padding: 13px 16px 12px !important;
}
.onboarding-dock .onboarding-card.onboarding-emphasis h3 {
  margin: 7px 0 6px !important;
  font-size: 15px !important;
}
/* Practice steps: out of the play area entirely. */
.onboarding-dock .onboarding-card.table-live {
  position: fixed !important;
  top: 14px !important;
  left: 50% !important;
  right: auto !important;
  bottom: auto !important;
  align-self: auto !important;
  width: min(600px, calc(100vw - 30px)) !important;
  padding: 9px 15px !important;
  border-radius: 11px !important;
  opacity: 1 !important;
  transform: translateX(-50%) !important;
}
.onboarding-dock .onboarding-card.table-live h3 {
  margin: 4px 0 3px !important;
  font-size: 13px !important;
}
.onboarding-dock .onboarding-card.table-live p,
.onboarding-dock .onboarding-card.table-live .onboarding-copy {
  font-size: 11px !important;
  line-height: 1.5 !important;
}
.onboarding-dock .onboarding-card.table-live button {
  margin-top: 8px !important;
  padding: 9px 14px !important;
  font-size: 11px !important;
}

/* ---------------------------------------------------------------------
   WORLDS — one constellation is one world and its stars are its stages,
   so node positions come from the world's real figure (inline left/top)
   instead of a per-index .s1-.s5 class that only fit five nodes.
   --------------------------------------------------------------------- */
.hub-map .constellation-node,
.constellation-map .constellation-node {
  position: absolute !important;
  right: auto !important;
  bottom: auto !important;
  margin: 0 !important;
  /* 값을 그대로 박으면 폴리시의 호버 규칙이 통째로 죽는다 — 실제로
     `translate(-50%,-53%)`의 들어올림이 한 번도 적용된 적이 없었다. 자리
     고정은 유지하되 두 축을 변수로 열어 둔다. */
  transform: translate(var(--node-x, -50%), var(--node-y, -50%)) !important;
}
.hub-map .constellation-node.active,
.constellation-map .constellation-node.active {
  z-index: 3;
  transform: translate(var(--node-x, -50%), var(--node-y, -50%)) scale(1.06) !important;
}
.world-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 6px;
  padding: 8px 12px;
  border: 1px solid #3f2e53;
  border-radius: 12px;
  background: #160923d9;
  text-align: left;
}
.world-name {
  flex: 1 1 auto;
  min-width: 0;
}
.world-name small {
  display: block;
  color: var(--gold);
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.2em;
}
.world-name b {
  display: block;
  margin-top: 3px;
  color: #f3ede2;
  font-size: 15px;
}
.world-name span {
  display: block;
  margin-top: 2px;
  color: var(--mist);
  font-size: 10px;
}
.world-step {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  padding: 0 !important;
  border-radius: 50% !important;
  font-size: 11px !important;
}
.world-step[disabled] {
  opacity: 0.3;
  cursor: not-allowed;
}
/* A seven-node figure only reads if a node is a star, not a card.  The
   mission bar under the map already carries the full name and rule, so the
   node keeps the Bayer letter, the star name and its state. */
.hub-map .constellation-node {
  display: grid !important;
  justify-items: center;
  gap: 3px;
  width: 78px !important;
  min-height: 0 !important;
  padding: 0 !important;
  border: 0 !important;
  border-radius: 0 !important;
  background: transparent !important;
  box-shadow: none !important;
  text-align: center;
}
.hub-map .constellation-node .stage-star {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border: 2px solid #5b4b70;
  border-radius: 50%;
  background: radial-gradient(circle, #28153c 0%, #10061b 74%);
  color: #877c9b;
  font-size: 15px;
}
.hub-map .constellation-node.cleared .stage-star {
  border-color: #f6c48e;
  color: #ffd2a0;
  box-shadow:
    0 0 0 3px #0e0616,
    0 0 16px #f6c48e66;
}
.hub-map .constellation-node.active .stage-star {
  border-color: #ad97e2;
  color: #d6cee9;
  box-shadow:
    0 0 0 3px #0e0616,
    0 0 18px #9578ca88;
}
.hub-map .constellation-node.locked {
  opacity: 0.42;
}
.hub-map .constellation-node .stage-copy {
  display: block;
  width: 100%;
}
.hub-map .constellation-node .stage-copy small {
  display: block;
  color: var(--gold);
  font:
    700 7px ui-monospace,
    monospace;
  letter-spacing: 0.1em;
}
.hub-map .constellation-node .stage-copy b {
  display: block;
  margin-top: 1px;
  color: #f3ede2;
  font-size: 10px;
  text-shadow: 0 2px 0 #07020c;
}
/* The stage name, rule text and status word belong to the mission bar, not to
   a node that has to sit inside a constellation. */
.hub-map .constellation-node .stage-copy > strong,
.hub-map .constellation-node .stage-copy > span,
.hub-map .constellation-node .node-status,
.hub-map .constellation-node .stage-cleared-mark {
  display: none !important;
}

/* ---------------------------------------------------------------------
   LIBRARY, ATTENDANCE, STARKEEPER SKINS
   --------------------------------------------------------------------- */
.archive-tabs {
  display: flex;
  gap: 8px;
  margin-bottom: 12px;
}
.archive-tab {
  padding: 9px 18px !important;
  border: 1px solid #47345d !important;
  border-radius: 999px !important;
  background: #170a24 !important;
  color: #938aa4 !important;
  font-size: 11px !important;
}
.archive-tab.on {
  border-color: #f6c48e !important;
  background: linear-gradient(#ffd98e, #c98a3e) !important;
  color: #2a1608 !important;
}
.codex-split {
  display: flex;
  flex: 1 1 auto;
  gap: 12px;
  align-items: stretch;
  min-height: 0;
}
.codex-list {
  display: grid;
  flex: 1 1 auto;
  grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
  gap: 9px;
  align-content: start;
  overflow-y: auto;
  padding-right: 4px;
  min-width: 0;
  min-height: 0;
}
.codex-side {
  flex: 0 0 clamp(240px, 30%, 310px);
}
.codex-card {
  display: flex;
  gap: 11px;
  padding: 11px 12px;
  border: 1px solid #362649;
  border-left: 3px solid var(--unit);
  border-radius: 12px;
  background: #170a24;
  text-align: left;
}
.codex-card.locked {
  opacity: 0.45;
  filter: saturate(0.3);
}
.codex-portrait {
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  image-rendering: pixelated;
}
.codex-copy {
  flex: 1 1 auto;
  min-width: 0;
}
.codex-copy small {
  display: block;
  color: var(--unit);
  font:
    700 9px ui-monospace,
    monospace;
  letter-spacing: 0.06em;
}
.codex-copy b {
  display: block;
  margin-top: 3px;
  color: #f3ede2;
  font-size: 12px;
}
.codex-copy p {
  margin: 5px 0 0 !important;
  max-width: none !important;
  color: #aaa2bb;
  font-size: 10px;
  line-height: 1.55;
}
.codex-lore {
  display: block;
  margin-top: 5px;
  color: #d8b28e;
  font-size: 9px;
  font-style: normal;
}
.codex-skin {
  flex: 0 0 auto;
  align-self: flex-start;
  color: var(--mist);
  font:
    700 9px ui-monospace,
    monospace;
}
.codex-side {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 13px;
  border: 1px solid #362649;
  border-radius: 12px;
  background: #170a24;
  text-align: left;
}
.codex-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 10px;
}
.codex-table th {
  padding: 5px 4px;
  border-bottom: 1px solid #2d1d41;
  color: var(--gold);
  font:
    700 8px ui-monospace,
    monospace;
  letter-spacing: 0.12em;
  text-align: left;
}
.codex-table td {
  padding: 6px 4px;
  border-bottom: 1px solid #231335;
  color: #d2cedb;
}
.codex-table td:last-child {
  color: var(--star);
  font-weight: 700;
  text-align: right;
}
.codex-table tr.down td:last-child {
  color: #938aa4;
}
.codex-note {
  margin: 4px 0 0 !important;
  max-width: none !important;
  color: var(--mist);
  font-size: 9px;
  line-height: 1.55;
}
/* Stacked on a narrow window: the list must stop being a scroll box, or it
   gets squeezed to nothing between the header and the multiplier table.

   높이도 함께 본다(2026-08-23 실측). 이 패널의 폭은 창 너비가 아니라
   `main` 에서 오고, `main` 은 판 비율을 지키려고 `80vh` 기반으로 좁아진다 —
   그래서 1024x680 이나 1280x760 처럼 «낮은» 창에서 두 단(목록 최소 290 +
   옆칸 최소 240 + 여백)이 들어갈 자리가 없는데, 조건이 너비만 보고 있어
   접히지 않았다. 실측 넘침: 1024x680 에서 108px, 1280x760 에서 44px
   (필요 578 / 가용 470·534). 넘친 만큼은 가로 스크롤로 밀려 배율 표
   오른쪽 열과 「뒤로」가 화면 밖에 있었다. 1280x900 은 0이라 두 단 유지. */
@media (max-width: 900px), (max-height: 820px) {
  .codex-split {
    flex-direction: column;
    overflow-y: auto;
  }
  .codex-list {
    flex: 0 0 auto;
    overflow: visible;
  }
  .codex-side {
    flex: 0 0 auto;
  }
}

.claim-banner.attendance {
  border-left: 3px solid #f2b35c;
}
.hero-skin-list {
  display: grid;
  gap: 9px;
}
.hero-skin-row {
  display: grid;
  grid-template-columns: 108px minmax(0, 1fr);
  gap: 12px;
  align-items: center;
  padding: 10px 12px;
  border: 1px solid #362649;
  border-left: 3px solid var(--unit);
  border-radius: 12px;
  background: #170a24;
  text-align: left;
}
.hero-skin-name b {
  display: block;
  color: #f3ede2;
  font-size: 12px;
}
.hero-skin-name small {
  display: block;
  margin-top: 2px;
  color: var(--unit);
  font:
    700 9px ui-monospace,
    monospace;
}
.hero-skin-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.hero-skin-chip {
  display: grid !important;
  justify-items: center;
  gap: 3px;
  width: 86px;
  padding: 7px 4px 6px !important;
  border: 1px solid #47345d !important;
  border-radius: 11px !important;
  background: #200f31 !important;
  color: #bfb8ce !important;
  font-size: 9px !important;
}
.hero-skin-chip:hover {
  border-color: #f6c48e !important;
  background: #2a1541 !important;
}
.hero-skin-chip.on {
  border-color: #f6c48e !important;
  background: #2a1541 !important;
  box-shadow: 0 0 0 1px #f6c48e55 !important;
}
.hero-skin-chip.buy small {
  color: var(--star);
}
.hero-skin-chip b {
  color: #d2cedb;
  font-size: 10px;
}
.hero-skin-swatch {
  width: 44px;
  height: 44px;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  image-rendering: pixelated;
}

/* ---------------------------------------------------------------------
   스테이지 지도 — 디자인 세션 §2·§3
   월드색은 hue 하나(--wh)로만 들어온다. L과 C는 톤마다 고정이므로 어느
   월드에서도 대비와 무게가 같고, 글자 가독성이 월드마다 달라지지 않는다.
   --wc는 색을 지울 스위치다(관측되지 않은 월드는 0).
   색이 실리는 곳은 네 군데뿐이다 — 머리글·항로선·노드 테두리·별먼지.
   유닛 고유색·희귀도·체력바·안내별은 월드가 바뀌어도 그대로다.
   --------------------------------------------------------------------- */
.constellation-map-shell {
  --w-line: oklch(0.63 calc(0.115 * var(--wc, 1)) var(--wh, 0));
  --w-node: oklch(0.72 calc(0.09 * var(--wc, 1)) var(--wh, 0));
  --w-glow: oklch(0.79 calc(0.105 * var(--wc, 1)) var(--wh, 0));
  --w-text: oklch(0.86 calc(0.075 * var(--wc, 1)) var(--wh, 0));
  --w-deep: oklch(0.26 calc(0.045 * var(--wc, 1)) var(--wh, 0));
}
/* 상단 7칸 띠 — 월드 진행과 팔레트를 한 줄로 동시에 말한다. */
.world-band {
  display: flex;
  gap: 4px;
  margin: 0 0 10px;
}
.world-band i {
  flex: 1;
  height: 4px;
  border-radius: 2px;
  background: #342544;
}
.world-band i.open {
  background: oklch(0.5 0.06 var(--wh));
}
.world-band i.on {
  background: oklch(0.79 0.105 var(--wh));
  box-shadow: 0 0 8px oklch(0.79 0.105 var(--wh) / 0.55);
}
.constellation-map-head .tag {
  color: var(--w-text) !important;
  letter-spacing: 0.14em;
}
.constellation-map-head h2 {
  color: #f3ede2 !important;
}
.constellation-map-scene .constellation-route path,
.constellation-map-scene .constellation-route line {
  stroke: var(--w-line) !important;
}
/* 노드. 초상이 들어오므로 글자를 담지 않는다 — 상태는 형태로 가른다. */
.constellation-map-scene .constellation-node {
  position: absolute !important;
  overflow: visible !important;
  width: 54px !important;
  height: 62px !important;
  padding: 0 !important;
  gap: 3px !important;
  flex-direction: column !important;
  border-color: color-mix(in oklab, var(--w-node) 55%, #0e0616) !important;
  background: #12071de8 !important;
}
.constellation-map-scene .constellation-node .stage-star {
  width: 38px !important;
  height: 38px !important;
  overflow: hidden;
  border-radius: 50%;
  border: 1px solid color-mix(in oklab, var(--w-node) 45%, #0e0616);
  background: #0e0616;
  display: grid;
  place-items: center;
}
.constellation-map-scene .constellation-node .stage-star img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  image-rendering: pixelated;
}
.constellation-map-scene .constellation-node .stage-id {
  font-size: 11px !important;
  color: #d2cedb !important;
}
.constellation-map-scene .constellation-node.picked {
  border-color: var(--w-glow) !important;
  box-shadow:
    0 0 0 1px var(--w-glow),
    0 0 18px oklch(0.79 0.105 var(--wh) / 0.4) !important;
}
/* 고른 칸은 별이 52px 로 커진다(다른 칸 38px). 그런데 칸 높이가 62px 로
   고정이라 52 + 간격 3 + 번호 15 = 70 이 되어 번호가 칸 밖으로 9px 밀려
   아래 테두리에 반이 잘렸다(2026-08-23 실측: 고른 칸 .stage-id 아래끝 71
   대 칸 높이 62. 다른 칸은 38+3+15=56 이라 멀쩡했다). 별을 줄이면 「고른
   칸이 크다」는 강조가 죽으므로 칸을 키운다 — 중심 정렬이
   translate(-50%, -50%) 라 높이를 키워도 지도 위 좌표는 그대로다. */
.constellation-map-scene .constellation-node.picked {
  height: 72px !important;
}
.constellation-map-scene .constellation-node.picked .stage-star {
  width: 52px !important;
  height: 52px !important;
  border-radius: 6px;
}
.constellation-map-scene .constellation-node.locked {
  filter: saturate(0.2) brightness(0.72);
}
.constellation-map-scene .constellation-node.locked .stage-star img {
  filter: grayscale(1) brightness(0.5);
}
/* `.constellation-node span`(폴리시 9px)이 더 구체적이라 여기 특이도를 올린다.
   요구서가 9px 설명문을 문제로 짚었으므로 새로 넣는 글자는 그 아래로 가지
   않는다 — 이건 문장이 아니라 상태 글리프 하나다. */
.constellation-map-scene .constellation-node .stage-mark {
  position: absolute;
  top: -5px;
  right: -5px;
  width: 15px;
  height: 15px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: #0e0616;
  /* 요구서가 9px 설명문을 문제로 짚었으므로 새로 넣는 글자도 그 아래로
     내려가지 않게 한다. 이건 문장이 아니라 상태 글리프 하나다. */
  font-size: 10px;
  line-height: 1;
}
.constellation-map-scene .constellation-node .stage-mark.locked {
  color: #857c8f;
  border: 1px solid #433554;
}
.constellation-map-scene .constellation-node .stage-mark.cleared {
  color: #ffd2a0;
  border: 1px solid #6b5a3a;
}
/* 하단 서랍 — 고른 하나만 여기서 펼친다. 9px 설명문을 노드마다 흩어 놓던
   것을 대체한다. 글자는 10px 이상이고, 잘릴 카드가 없다. */
.stage-drawer {
  display: flex;
  align-items: center;
  gap: 11px;
  margin: 10px 0 0;
  padding: 9px 11px;
  border: 1px solid color-mix(in oklab, var(--w-node) 40%, #0e0616);
  border-radius: 10px;
  background: linear-gradient(100deg, #10061b99, #150822dd);
}
.stage-drawer-face {
  flex: none;
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
  border-radius: 8px;
  border: 1px solid color-mix(in oklab, var(--w-node) 35%, #0e0616);
  background: #0c0414;
  overflow: hidden;
}
.stage-drawer-mark {
  /* 상속 16px 글자였다(실측 13×23) — 사다리 16 정수배로. */
  display: inline-block;
  width: 16px;
  height: 16px;
  background: url("../assets/library/icons/star/star-16.png") center no-repeat;
  image-rendering: pixelated;
  color: transparent;
}
.stage-drawer-face img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  image-rendering: pixelated;
}
.stage-drawer-copy {
  min-width: 0;
  flex: 1;
}
.stage-drawer-head {
  display: flex;
  align-items: baseline;
  gap: 7px;
}
.stage-drawer-head b {
  color: var(--w-text);
  font-size: 13px;
}
.stage-drawer-head small {
  color: #938aa4;
  font-size: 10px;
  letter-spacing: 0.1em;
}
.stage-drawer-copy strong {
  display: block;
  margin: 1px 0 3px;
  color: #f3ede2;
  font-size: 12px;
}
.stage-drawer-copy p {
  margin: 0;
  color: #cac2d3;
  font-size: 10px;
  line-height: 1.5;
}
.stage-drawer-meta {
  display: flex;
  gap: 9px;
  margin-top: 5px;
}
.stage-drawer-meta span {
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid #3f2e53;
  color: #b2a8bf;
  /* 이 화면에서 9px 아래로 내려가는 글자를 두지 않는다. */
  font-size: 10px;
}
.stage-drawer-go {
  flex: none;
  align-self: stretch;
  padding: 0 13px;
  border: 1px solid var(--w-glow);
  border-radius: 8px;
  background: color-mix(in oklab, var(--w-deep) 70%, #0e0616);
  color: var(--w-text);
  font-size: 11px;
  font-weight: 700;
  cursor: pointer;
}
.stage-drawer-go:hover {
  background: color-mix(in oklab, var(--w-line) 30%, #0e0616);
}

/* 월드 별자리 형상. 항로선과 노드 뒤에 아주 옅게 깔려 「여기가 어느 별자리인가」를
   글자 없이 말한다. 배경 이미지 한 장이라 합성 레이어가 늘지 않는다. */
.constellation-map-scene .constellation-map:before {
  content: "";
  position: absolute;
  inset: 6%;
  pointer-events: none;
  background: var(--figure) center/contain no-repeat;
  opacity: 0.16;
  filter: saturate(0.55) brightness(1.42)
    drop-shadow(0 0 18px oklch(0.72 0.09 var(--wh) / 0.22));
  image-rendering: pixelated;
}
.constellation-map-scene .constellation-map:after {
  inset: 3%;
  background:
    linear-gradient(var(--w-line), var(--w-line)) 50% 0 / 1px 100% no-repeat,
    linear-gradient(90deg, var(--w-line), var(--w-line)) 0 50% / 100% 1px
      no-repeat,
    radial-gradient(circle at 50% 48%, var(--w-deep), transparent 58%);
  opacity: 0.2;
}

/* Carry the selected world's observatory tone out of the canvas and into the
   fixed instrument frame. Gameplay colours remain untouched. */
.game-mode {
  --battle-world-line: oklch(
    0.68 calc(0.1 * var(--battle-world-chroma, 0)) var(--battle-world-hue, 0)
  );
  --battle-world-deep: oklch(
    0.24 calc(0.05 * var(--battle-world-chroma, 0)) var(--battle-world-hue, 0)
  );
}
.game-mode .stage {
  border-color: color-mix(
    in oklab,
    var(--battle-world-line) 62%,
    #665184
  ) !important;
  box-shadow:
    0 0 0 1px color-mix(in oklab, var(--battle-world-line) 24%, transparent),
    0 18px 42px #0a020dbb !important;
}
.game-mode .hud-card,
.game-mode .boss-panel,
.game-mode .boss-banner,
.game-mode .blaze-card {
  border-color: color-mix(
    in oklab,
    var(--battle-world-line) 48%,
    #543c75
  ) !important;
  background: linear-gradient(
    145deg,
    color-mix(in oklab, var(--battle-world-deep) 46%, #260736),
    #17051ff4
  ) !important;
}

/* ---------------------------------------------------------------------
   루나 말풍선 (§5). 판 «밖» 아래에 선다 — 판을 가리지 않고, 매 프레임
   갱신되지 않으므로 DOM이어도 프레임 예산을 건드리지 않는다. 수업 카드와
   같은 어휘를 이어받는다.
   --------------------------------------------------------------------- */
.luna-speech {
  position: absolute;
  left: 50%;
  bottom: -12px;
  transform: translate(-50%, 12px);
  display: flex;
  align-items: center;
  gap: 11px;
  width: min(520px, 94%);
  padding: 9px 13px;
  border: 1px solid #8f7ad0;
  border-radius: 12px;
  background: linear-gradient(100deg, #14102bf2, #1b1638ee);
  box-shadow: 0 10px 26px #09020cbb;
  opacity: 0;
  pointer-events: none;
  transition:
    opacity 0.22s ease,
    transform 0.22s ease;
  z-index: 6;
}
.luna-speech.show {
  opacity: 1;
  transform: translate(-50%, 0);
}
.luna-speech-face {
  flex: none;
  width: 64px;
  height: 64px;
  border-radius: 10px;
  border: 1px solid #8f7ad0;
  background: #0e0616;
  object-fit: cover;
  image-rendering: pixelated;
}
.luna-speech-body b {
  display: block;
  color: #cbb8ff;
  font-size: 10px;
  letter-spacing: 0.1em;
}
.luna-speech-text {
  margin: 3px 0 0;
  color: #ece7ff;
  font-size: 12px;
  line-height: 1.55;
}
@media (prefers-reduced-motion: reduce) {
  .luna-speech {
    transition: none;
  }
}

/* 인트로 건너뛰기 (§10-2). 스펙의 「연출 완료를 기다리지 않고 접근 가능」을
   지키는 쪽이다 — 1.0초부터 늘 눌린다. 시작 버튼과 달리 잠기지 않는다. */
.oo2-skip {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 70;
  padding: 7px 14px;
  border: 1px solid #614885;
  border-radius: 999px;
  background: #0e0616cc;
  color: #b3a8c7;
  font-size: 11px;
  letter-spacing: 0.08em;
  cursor: pointer;
}
.oo2-skip:hover {
  border-color: #9578ca;
  color: #e3def4;
}

/* ---------------------------------------------------------------------
   허브 인라인 지도 — 전체화면 지도와 같은 처리 (§2·§3)
   허브가 「게임 시작」 뒤 처음 보이는 화면이므로, 전체화면 지도만 고치면
   바꾼 것이 플레이어에게 도달하지 않는다. 노드 크기와 배치는 그대로 두고
   월드색·초상·형상만 얹는다 — 배치는 META_UI_REQUEST 1-1의 별도 사안이다.
   --------------------------------------------------------------------- */
.survivor-hub.hub-map-mode {
  --w-line: oklch(0.63 calc(0.115 * var(--wc, 1)) var(--wh, 0));
  --w-node: oklch(0.72 calc(0.09 * var(--wc, 1)) var(--wh, 0));
  --w-glow: oklch(0.79 calc(0.105 * var(--wc, 1)) var(--wh, 0));
  --w-text: oklch(0.86 calc(0.075 * var(--wc, 1)) var(--wh, 0));
}
.hub-map-mode .world-band {
  display: flex;
  gap: 4px;
  margin: 0 0 7px;
}
.hub-map-mode .world-band i {
  flex: 1;
  height: 4px;
  border-radius: 2px;
  background: #342544;
}
.hub-map-mode .world-band i.open {
  background: oklch(0.5 0.06 var(--wh));
}
.hub-map-mode .world-band i.on {
  background: oklch(0.79 0.105 var(--wh));
  box-shadow: 0 0 8px oklch(0.79 0.105 var(--wh) / 0.55);
}
.hub-map-mode .world-name small {
  color: var(--w-text) !important;
}
.hub-map .constellation-route path,
.hub-map .constellation-route line {
  stroke: var(--w-line) !important;
}
.hub-map .constellation-node {
  border-color: color-mix(in oklab, var(--w-node) 55%, #0e0616) !important;
}
.hub-map .constellation-node.active {
  border-color: var(--w-glow) !important;
}
/* 초상. 노드 크기는 그대로 두고 별 자리에만 들어간다. */
.hub-map .constellation-node .stage-star {
  overflow: hidden;
  border-radius: 50%;
  border: 1px solid color-mix(in oklab, var(--w-node) 45%, #0e0616);
  background: #0e0616;
  display: grid;
  place-items: center;
}
.hub-map .constellation-node .stage-star img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  image-rendering: pixelated;
}
.hub-map .constellation-node.locked .stage-star img {
  filter: grayscale(1) brightness(0.5);
}
/* 월드 별자리 형상. 전체화면 지도와 같은 그림, 같은 옅기다. */
.hub-map:before {
  content: "";
  position: absolute;
  inset: 6%;
  pointer-events: none;
  background: var(--figure) center/contain no-repeat;
  opacity: 0.075;
  filter: saturate(0.2) brightness(1.5);
  image-rendering: pixelated;
}
