/* 운빨 디펜스 UI */

/* 전체 서체. 프로젝트에 동봉한 로컬 폰트라 외부 요청이 없다.
   제목·버튼 = 쿠키런 Regular(원체가 두툼한 라운드 디스플레이),
   본문 = 나눔스퀘어라운드 R/B 2웨이트. */
@font-face {
  font-family: 'CookieRun';
  src: url('../assets/font/CookieRun-Regular.woff') format('woff');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'NanumSquareRound';
  src: url('../assets/font/NanumSquareRoundR.woff') format('woff');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'NanumSquareRound';
  src: url('../assets/font/NanumSquareRoundB.woff') format('woff');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

* {
  margin: 0; padding: 0; box-sizing: border-box;
  -webkit-user-select: none; user-select: none;
  -webkit-tap-highlight-color: transparent;
  /* 실제 터치 대상에서 더블탭 확대를 끈다. 핀치 확대·스크롤은 유지하며,
     캔버스와 드래그 손잡이는 아래의 touch-action: none이 우선한다. */
  touch-action: manipulation;
}
/* 값 입력이 필요한 요소만 선택·확대를 되살린다 */
input, textarea { -webkit-user-select: auto; user-select: auto; }

:root {
  --wood: #7a5230;
  --wood-dark: #5d3d22;
  --paper: #f8eed7;
  --paper-dark: #efdfbe;
  --ink: #4a3626;
  --accent: #ff9f1c;

  /* 제목·버튼·HUD는 쿠키런(단일 웨이트, 원체가 볼드급), 본문·툴팁은 나눔스퀘어라운드.
     본문 강조는 나눔스퀘어라운드 B(700)가 실제 웨이트로 처리한다. */
  --font-display: 'CookieRun', 'NanumSquareRound', 'Apple SD Gothic Neo', sans-serif;
  --font-body: 'NanumSquareRound', 'Apple SD Gothic Neo', sans-serif;
  --ls-display: 0.01em;

  /* ---- 레터박스 스케일 토큰 ----
     논리 캔버스 1000x720은 계약이라 손대지 않는다. 대신 #stage-fit을 통째로 축소해
     캔버스와 그 위의 오버레이(뽑기 연출·배너·게임오버)가 같은 비율로 줄어들게 한다.
     --stage-scale은 UI._fitStage()가 매 리사이즈마다 계산해 넣는다.
     아래 --fit-* 는 그 계산이 읽는 브레이크포인트 SSoT다. JS에 수치를 중복해 두지 않는다. */
  --stage-scale: 1;
  /* row = 스테이지 옆 사이드바(기본) · stack = 스테이지 아래 사이드바(세로 화면)
     split = HUD가 사이드바 열로 올라가고 스테이지가 화면 높이를 다 쓰는 폰 가로 */
  --fit-mode: row;
  --fit-sidebar: 300px; /* row 모드에서 사이드바에 예약하는 폭 */
  --fit-gap: 10px;
  --fit-min: 0.3;       /* 이보다 더 줄이지 않는다(가로 스크롤 대신 축소 하한) */

  /* ---- 안전 영역 ----
     viewport-fit=cover라 화면 전체를 덮는 고정 오버레이는 노치·홈 인디케이터·둥근
     모서리 아래까지 들어간다. 전체 화면 모달의 패딩은 전부 이 한 줄을 쓴다. */
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --modal-pad: calc(16px + var(--safe-t)) calc(16px + var(--safe-r))
               calc(16px + var(--safe-b)) calc(16px + var(--safe-l));
}

/* 제목·버튼·HUD처럼 짧고 크게 읽는 표면. 굵기가 아니라 자간으로 리듬을 준다. */
#hud, .hud-btn, .speed-btn, .toggle-btn, .big-btn, .act, .go-btn,
#meta-close, #codex-close, #lab-close, #forge-close, #account-close, .cx-tab,
.panel h3, .modal-card h3, .go-card h1, .go-sec h4, .load-card h2,
#banner, #toast, .bi-name, .bi-kicker, #reveal-grade, #reveal-name,
.birth-grade, .birth-verb, .birth-name,
.draft-card b, .mut-name, .go-mvp-head b, .go-tile b, .go-grade b, .go-pb-row b,
#meta-summary b, .bulk-head b {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: var(--ls-display);
}

/* 12px 이하 잔글씨는 굵기를 올릴 수 없으므로 자간을 살짝 벌려 양피지 위에서 뭉개지지 않게 한다. */
.go-tile small, .go-grade small, .go-grade i, .go-row-foot, .go-row-head small,
.go-pb-row small, .go-pb-row i,
.go-spend-row small, .go-leak, .go-empty, .go-note, .meta-row small, .meta-row em,
#meta-summary small, .mut-line small, .mut-own-label, .mut-own-pill, .mut-own-none,
.cx-cell small, .cx-relic small, .cx-row-head small, .cx-rate-label,
.mut-bar-label, .ability-card small, .ability-next,
.ability-card p, #ui-tip .tip-note, .bi-tag-label, #log, .bulk-card small,
.draft-card small, .mut-note, .sel-hint-row, .empty {
  letter-spacing: 0.01em;
}

/* 색 위에 얹히는 초소형 배지·필. 굵기를 못 올리는 대신 자간과 그림자로 대비를 세운다. */
.chip .count, .chip.combinable::after, .chip .role, .relic-badge, .go-kicker,
.mut-chip-grade, .go-mut-grade, .bulk-pill, .ability-title span, .go-pb-row em {
  letter-spacing: 0.02em;
  text-shadow: 0 1px 1px rgba(28,14,4,0.32);
}
/* 밝은 바탕 위의 잔글씨 배지는 그림자 없이 자간만 벌린다 */
.ability-summary { letter-spacing: 0.02em; }

/* 본문 안의 소제목·강조. 굵기를 못 쓰므로 자간만 살짝 벌려 주변 문장과 갈린다. */
.mut-who b, .mut-tag, .go-row-head b, .meta-head b,
#ui-tip .tip-name, .go-mvp-row > span, .go-spend-row > span {
  letter-spacing: 0.02em;
}

body {
  font-family: var(--font-body);
  font-weight: 400;
  /* Regular 한 벌뿐이라 가짜 볼드를 끈다. 기울임 합성은 그대로 둔다. */
  font-synthesis: style small-caps;
  font-synthesis-weight: none;
  background: #3a5f2a url('../assets/ui/bg_grass.png') repeat;
  background-size: 420px;
  color: var(--ink);
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  justify-content: center;
  /* 노치·홈 인디케이터를 피한다. viewport-fit=cover라 env()가 실제 값을 갖는다 */
  padding: calc(10px + env(safe-area-inset-top)) calc(10px + env(safe-area-inset-right))
           calc(10px + env(safe-area-inset-bottom)) calc(10px + env(safe-area-inset-left));
}

/* 페이지 자체는 바운스·당겨서 새로고침을 하지 않는다. 스크롤은 세로 모드의 사이드바에서만 난다. */
html, body { overscroll-behavior: none; }
html { -webkit-text-size-adjust: 100%; }

#app { width: 1340px; max-width: 100%; }

/* ---------- HUD ----------
   HUD는 한 줄에 여덟 덩어리(골드·라이프·웨이브 칩·차원 배지·최고·유닛·배속·도구)가 서는
   폭 예산 위에서 산다. 예산을 넘기면 어떤 것도 잘리거나 밀려 나가서는 안 된다 — 아래
   --hud-* 토큰이 그 예산의 손잡이고, 어느 순서로 양보하는지는 '#hud 폭 예산 사다리' 절이
   정한다. 개별 칩 규칙은 전부 이 토큰을 통해서만 폭을 쓴다(직접 px 금지). */
#hud {
  /* 사다리 0단(가장 넉넉한 기본값). 단계가 오르면 아래 .fit-* 가 이 값들을 조인다. */
  --hud-gap: 10px;
  --hud-item-pad-x: 12px;
  --hud-wide-min: 120px;
  --hud-wave-min: 168px;
  --hud-tool-gap: 6px;
  --hud-btn-pad-x: 11px;
  --hud-speed-pad-x: 13px;
  --hud-sep-mx: 3px;
  --hud-dimname-max: none;
  /* 폭 토큰이 아니라 타입 토큰이다(사다리가 건드리지 않는다). HUD 안의 보조 텍스트 ——
     웨이브 칩 카운트다운과 차원 이름 —— 은 서로 다른 칩에 살지만 같은 급으로 읽혀야
     한다. 20px 본문 옆에서 한 단계 물러나되 각주로 떨어지지는 않는 크기. */
  --hud-sub: 13.5px;
  display: flex;
  /* 데스크톱 HUD는 한 줄이 기본이다. 두 줄은 사다리 마지막 칸(.fit-6)에서만 열린다. */
  flex-wrap: nowrap;
  gap: var(--hud-gap);
  align-items: center;
  background: linear-gradient(var(--wood), var(--wood-dark));
  border-radius: 14px;
  padding: 8px 14px;
  margin-bottom: 10px;
  box-shadow: 0 3px 0 rgba(0,0,0,0.3);
}
.hud-item {
  display: flex;
  align-items: center;
  gap: 6px;
  background: rgba(0,0,0,0.25);
  border-radius: 10px;
  padding: 5px var(--hud-item-pad-x);
  color: #fff;
  /* 도현은 자폭이 좁아 같은 px에서 Jua보다 작아 보인다. 골드·웨이브 숫자만 한 단계 키운다. */
  font-size: 20px;
  line-height: 1.3;
  /* 칩은 어떤 폭에서도 두 줄이 되지 않는다. "30/30"이 슬래시에서 꺾이면 칩 하나만 두 줄
     높이로 부풀어 HUD 줄 전체가 밀린다. 폭이 모자라면 접는 대신 사다리가 답한다. */
  white-space: nowrap;
}
.hud-item img { width: 26px; height: 26px; object-fit: contain; }
.hud-item.wide { min-width: var(--hud-wide-min); justify-content: center; }
/* 최고·유닛은 한 단계 물러난 칩이다. 물러나는 것은 글자 크기와 밝기지 칩의 키가 아니다 ——
   17px * 1.3으로 두면 상자가 4px 낮아져 골드·웨이브 옆에서 이 둘만 눌린 것처럼 보인다.
   1.53은 17px에서 딱 26px, 곧 기본 칩(20px * 1.3)과 같은 줄 높이가 되는 값이다.
   좁은 화면의 축소 단계(16/14px · 15/13px)에서도 오차가 0.6px 안이라 따로 손대지 않는다. */
.hud-item.dim { opacity: 0.8; font-size: 17px; line-height: 1.53; }
/* 계속 바뀌는 숫자는 자릿수 폭을 고정한다(#hud-gold는 자체 롤링 셀 구조라 제외). */
#hud-wave, #hud-lives, #hud-best, #hud-units { font-variant-numeric: tabular-nums; }

/* ---------- 웨이브 칩 = 다음 웨이브 즉시 시작 ----------
   전용 버튼(구 #btn-wave)을 없애고 HUD의 웨이브 표시가 그 역할을 넘겨받았다.
   대기(between) 동안에만 .can-rush가 붙어 눌러도 되는 표면임을 은은한 테·숨으로 알리고,
   전투 중에는 다른 HUD 칩과 완전히 같은 모습으로 돌아간다.
   글로우는 box-shadow와 opacity만 쓰므로 레이아웃을 흔들지 않는다. */
.wave-chip {
  border: 0;
  /* font: inherit를 쓰지 않는다. 버튼의 UA 서체를 지우려던 한 줄인데, 같은 특이도의
     .hud-item보다 뒤에 있어 거기서 준 font-size: 20px · line-height: 1.3까지 함께 지워
     버렸다. 그래서 이 칩만 16px · 줄 높이 normal로 서 있었다 —— 옆의 골드(20px)보다
     글자가 작고 칩 키가 5px 낮았다(.hud-item 주석이 "골드·웨이브 숫자만 한 단계 키운다"고
     말하는 바로 그 대비가 실제로는 나지 않던 것). 필요한 것만 직접 지정하면 나머지
     타입은 .hud-item에서 그대로 내려온다. */
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  color: #fff;
  cursor: default;
  position: relative;
  white-space: nowrap;
  /* 이 칩만 .hud-item의 가운데 줄맞춤을 베이스라인으로 바꾼다. 20px 디스플레이 "웨이브 N"과
     13.5px 본문 카운트다운을 가운데로 맞추면 둘의 중심만 같고 밑선이 어긋나 카운트다운이
     혼자 떠 보인다(실제 지적). 밑선을 공유해야 크기가 달라도 한 줄로 읽힌다.
     칩 높이는 그대로다 —— 작은 쪽의 밑선 위 높이(13.3px)가 큰 쪽(19px)을 넘지 않아
     줄 상자는 여전히 20px * 1.3 = 26px이 정한다. */
  align-items: baseline;
  transition: box-shadow 0.2s ease-out, background-color 0.2s ease-out, transform 0.12s ease-out;
}
/* 카운트다운이 실제로 보이는 대기(.can-rush) 동안에만 넓힌다(사용자 결정 — 전투 중
   상시 예약은 빈 칸이 어색했다). 대기 안에서는 초가 10.0→0.1로 줄어도 아래 .wct-sec의
   고정 폭 덕에 1px도 안 흔들리고, 폭이 변하는 순간은 웨이브 시작·종료 두 번뿐이다.
   그 두 번은 ui.js _syncWaveChip이 _fitHud를 다시 불러 사다리로 흡수한다. */
.hud-item.wave-chip.can-rush { min-width: var(--hud-wave-min); }
/* 카운트다운 자리. 전투 중에는 자리째 사라진다(상시 예약 없음).
   안쪽도 베이스라인 줄맞춤이라 이 상자의 밑선 = 첫 항목(구분점)의 밑선이고, 그 밑선이
   바깥 칩에서 "웨이브 N"의 밑선과 그대로 맞물린다. 가운뎃점은 별도 보정 없이 이 밑선을
   타면 초 텍스트의 x-height 한가운데에 앉는다 —— 글리프가 원래 그 높이에 그려져 있다.
   왼쪽 -2px는 광학 보정이다. 칩의 항목 간격은 6px인데 구분점 오른쪽 간격은 4px이라
   점이 초 쪽으로 쏠려 보였다. 양쪽을 4px로 맞춰 구분점이 두 덩어리 한가운데 선다. */
.wave-chip-t {
  display: none;
  align-items: baseline;
  gap: 4px;
  margin-left: -2px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--hud-sub);
  letter-spacing: 0;
  color: #ffe6a8;
  flex: 0 0 auto;
}
.wave-chip.can-rush .wave-chip-t { display: inline-flex; }
/* 초 표기의 고정 폭. 나눔스퀘어라운드에는 고정폭 숫자가 없어 tabular-nums가 실제로는
   먹지 않는다 —— 13.5px에서 "1.1s"가 19.5px, "0.0s"가 26.8px로 7px이나 흔들린다.
   그래서 이 예약 폭이 틱마다의 떨림을 막는 진짜 장치다(선언은 폰트가 바뀔 때를 위해 남긴다).
   폭은 실제로 나올 수 있는 가장 넓은 문자열에 맞춘다. 대기 시간의 SSoT는
   CFG.waves.betweenSec = 5라 초는 언제나 한 자리이고 최악이 "0.0s" = 3.07ch다 ——
   3.15ch면 다 담는다. 예전 4.4ch는 나올 수 없는 두 자리("10.0s")를 위한 자리였고,
   남는 11px이 전부 구분점과 숫자 사이에 고여 점만 따로 떠 보이게 만들었다.
   betweenSec를 10 이상으로 올리면 이 값을 3.7ch로 함께 올린다.
   오른쪽 정렬은 그대로 둔다. 남는 여백을 앞쪽에 두어야 끝의 "s"와 ▶가 붙박이로 서고,
   흔들리는 것은 눈에 덜 띄는 앞쪽 간격뿐이다. */
.wct-sec {
  display: inline-block;
  min-width: 3.15ch;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
/* ▶는 글자가 아니라 도형이다. 밑선에 세우면 도형 전체가 초 숫자보다 아래로 처져 보이므로
   이 항목만 줄 가운데로 맞춘다(줄 높이는 초 텍스트가 정하므로 결과적으로 초의 광학 중앙). */
.wct-go {
  font-style: normal;
  align-self: center;
  font-size: 11px;
  line-height: 1;
  color: #ffd68a;
  opacity: 0.8;
}
/* 구분점은 두 덩어리를 잇는 표식일 뿐이라 초·화살표보다 한 단계 더 물러난다. */
.wct-sep { font-style: normal; opacity: 0.6; }
.wave-chip.can-rush {
  cursor: pointer;
  background-color: rgba(70,40,6,0.42);
  box-shadow: 0 0 0 1.5px rgba(255,196,88,0.75), 0 0 12px rgba(255,168,40,0.35);
  animation: waveChipBreath 1.8s ease-in-out infinite;
}
.wave-chip.can-rush:active { transform: translateY(1px) scale(0.97); }
@keyframes waveChipBreath {
  0%, 100% { box-shadow: 0 0 0 1.5px rgba(255,196,88,0.55), 0 0 8px rgba(255,168,40,0.22); }
  50%      { box-shadow: 0 0 0 1.5px rgba(255,222,140,0.95), 0 0 16px rgba(255,180,54,0.55); }
}
/* 눌러서 실제로 러시가 걸린 순간의 한 번짜리 응답 */
.wave-chip.rush-go { animation: waveChipGo 0.34s ease-out; }
@keyframes waveChipGo {
  0%   { transform: scale(1); }
  32%  { transform: scale(1.09); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .wave-chip, .wave-chip.can-rush, .wave-chip.rush-go { animation: none; transition: none; }
  .wave-chip-t { transition: none; }
  .wave-chip.can-rush { box-shadow: 0 0 0 1.5px rgba(255,210,120,0.85); }
}

/* 골드 자릿수 롤링 — 값이 바뀐 자릿수만 굴러 올라오거나 내려간다(UI._syncGold).
   숫자 칸은 tabular-nums + 1ch 고정 폭이라 굴러도 옆 글자가 밀리지 않는다. */
#hud-gold.odo {
  display: inline-flex;
  align-items: center;
  line-height: 1.3;
  font-variant-numeric: tabular-nums;
  --odo-dir: 1;
}
.odo-c, .odo-s, .odo-c > i { font-style: normal; }
.odo-c {
  position: relative;
  display: block;
  width: 1ch;
  height: 1.3em;
  overflow: hidden;
  text-align: center;
}
/* 콤마처럼 굴리지 않는 글자. 숫자 칸과 같은 높이로 두어 기준선이 어긋나지 않게 한다. */
.odo-s { display: block; height: 1.3em; }
.odo-c > i {
  position: absolute;
  inset: 0;
  display: block;
  opacity: 0;
}
.odo-c > i.on { opacity: 1; }
/* 같은 자리가 연속으로 바뀔 때 애니메이션을 다시 트리거하려면 이름이 번갈아야 한다.
   강제 리플로우 없이 재생하려고 in/in2, out/out2 두 벌을 둔다. */
.odo-c > i.in   { animation: odoIn   0.19s cubic-bezier(0.22,0.72,0.24,1); }
.odo-c > i.in2  { animation: odoIn2  0.19s cubic-bezier(0.22,0.72,0.24,1); }
.odo-c > i.out  { animation: odoOut  0.19s cubic-bezier(0.22,0.72,0.24,1); }
.odo-c > i.out2 { animation: odoOut2 0.19s cubic-bezier(0.22,0.72,0.24,1); }
/* --odo-dir가 1이면 새 숫자가 아래에서 올라오고(증가), -1이면 위에서 내려온다(감소) */
@keyframes odoIn {
  from { transform: translateY(calc(var(--odo-dir, 1) * 85%)); opacity: 0; filter: blur(3px); }
  to   { transform: translateY(0); opacity: 1; filter: blur(0); }
}
@keyframes odoIn2 {
  from { transform: translateY(calc(var(--odo-dir, 1) * 85%)); opacity: 0; filter: blur(3px); }
  to   { transform: translateY(0); opacity: 1; filter: blur(0); }
}
@keyframes odoOut {
  from { transform: translateY(0); opacity: 1; filter: blur(0); }
  to   { transform: translateY(calc(var(--odo-dir, 1) * -85%)); opacity: 0; filter: blur(3px); }
}
@keyframes odoOut2 {
  from { transform: translateY(0); opacity: 1; filter: blur(0); }
  to   { transform: translateY(calc(var(--odo-dir, 1) * -85%)); opacity: 0; filter: blur(3px); }
}
@media (prefers-reduced-motion: reduce) {
  .odo-c > i.in, .odo-c > i.in2, .odo-c > i.out, .odo-c > i.out2 { animation: none; }
}
/* ---------- HUD 도구 버튼 (도감·도전과제·사운드) ----------
   사이드바 최하단에 있던 도감·도전과제는 존재를 모르는 사용자가 많았다.
   배속·사운드와 같은 줄에 두어 스크롤 없이 항상 보이게 한다. */
/* 도구 묶음은 절대 줄어들지 않는다. 줄어들면 flex가 안쪽 버튼부터 깎아 먹는다. */
#hud-tools { display: flex; align-items: center; gap: var(--hud-tool-gap); flex: 0 0 auto; }
.hud-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: none;
  border-radius: 10px;
  padding: 5px var(--hud-btn-pad-x);
  color: #fff;
  font-size: 16px;
  line-height: 1.3;
  white-space: nowrap;
  cursor: pointer;
  background: rgba(0,0,0,0.25);
  box-shadow: inset 0 0 0 1.5px rgba(255,255,255,0.07), 0 1px 0 rgba(0,0,0,0.25);
  transition: transform 0.12s ease, box-shadow 0.12s ease, filter 0.12s ease;
}
.hud-btn .hud-ico { font-size: 17px; line-height: 1; }
/* 나무 HUD 위에서 두 버튼이 '누를 것'으로 읽히도록 모달 톤과 같은 색을 입힌다 */
#btn-codex { background: linear-gradient(#8a6bb0, #5f4483); }
#btn-meta { background: linear-gradient(#9a7449, #6f4f32); }
.hud-btn.mute { font-size: 19px; padding: 5px calc(var(--hud-btn-pad-x) + 1px); }
.hud-btn:hover {
  filter: brightness(1.12);
  box-shadow: inset 0 0 0 1.5px rgba(255,255,255,0.22), 0 2px 0 rgba(0,0,0,0.28);
  transform: translateY(-1px);
}
.hud-btn:active { transform: translateY(1px); filter: brightness(0.95); }

/* 새 게임은 판을 접는 파괴적 액션이다. 도감·도전과제처럼 색으로 부르면 손이 먼저 가고,
   그렇다고 일시정지·사운드와 똑같이 두면 줄 안에서 묻힌다. 그래서 바탕은 중립 버튼보다
   한 뼘 따뜻한 잿빛이고, 신호는 잉걸불 화살표 한 점이 전부다. 일반 규칙 뒤에 두어야
   호버 테두리가 .hud-btn:hover에 덮이지 않는다. */
.hud-btn.reset {
  background: linear-gradient(rgba(74,40,24,0.52), rgba(44,24,15,0.64));
  box-shadow: inset 0 0 0 1.5px rgba(255,150,80,0.16), 0 1px 0 rgba(0,0,0,0.25);
}
.hud-btn.reset:hover {
  box-shadow: inset 0 0 0 1.5px rgba(255,168,96,0.42), 0 2px 0 rgba(0,0,0,0.28);
}
.rs-ico { color: #ffb46a; }
/* 전투 중 자주 누르는 일시정지·사운드를 새 게임에서 떼어 놓는 실선 한 줄.
   폭을 거의 쓰지 않으므로 좁은 HUD에서도 그대로 남는다. */
.hud-sep {
  flex: 0 0 auto;
  width: 1px;
  height: 22px;
  margin: 0 var(--hud-sep-mx);
  background: linear-gradient(rgba(255,236,200,0), rgba(255,236,200,0.26), rgba(255,236,200,0));
}
@media (prefers-reduced-motion: reduce) {
  .hud-btn { transition: none; }
  .hud-btn:hover, .hud-btn:active { transform: none; }
}

/* 배속 컨트롤.
   flex: 0 0 auto가 이 묶음의 생명줄이다. 둥근 모서리를 위해 overflow: hidden을 쓰는데,
   overflow가 visible이 아닌 flex 아이템은 자동 최소 크기가 0이 된다 — 기본값(shrink 1)
   그대로 두면 HUD가 조금만 넘쳐도 이 상자부터 소리 없이 깎이고 오른쪽 끝의 4x가 잘려
   사라진다(실제 사고). 넘침은 이제 사다리(#hud.fit-*)가 받는다. */
#speed-ctl {
  margin-left: auto;
  display: flex;
  flex: 0 0 auto;
  background: rgba(0,0,0,0.25);
  border-radius: 10px;
  overflow: hidden;
}
.speed-btn {
  border: none;
  background: transparent;
  color: rgba(255,255,255,0.6);
  font-size: 16.5px;
  padding: 6px var(--hud-speed-pad-x, 13px);
  cursor: pointer;
}
.speed-btn.active {
  background: var(--accent);
  color: #fff;
  text-shadow: 0 1px 0 rgba(0,0,0,0.3);
}

/* ---------- #hud 폭 예산 사다리 ----------
   HUD가 넘치는 이유는 화면 폭만이 아니다. 같은 1340px 안에서도 차원 배지의 유무, 차원 이름
   길이("붉은 달" ↔ "황금빛 종언"), 골드 자릿수(3자리 ↔ 7자리)가 총폭을 180px 넘게 흔든다.
   미디어 쿼리는 이 셋을 볼 수 없으므로 실제 넘침을 재서 단계를 올린다(UI._fitHud).
   여기서는 "무엇을 먼저 양보하는가"만 정의한다. 클래스는 누적이다 —— .fit-3이면
   .fit-1·.fit-2도 함께 붙어 있다.

   양보 순서와 근거:
     1 여백    간격·안쪽 여백·고정 최소폭을 조인다. 잃는 정보 0
     2 차원 이름 말줄임(46px). 배지의 본 정보는 숫자이고 이름은 색과 함께 읽는 플레이버다
     3 차원 이름 숨김. 배지·숫자·모티프 색은 그대로 남는다
     4 도구 라벨 숨김(아이콘 + title·aria-label 유지). 도감·도전과제는 발견성 때문에 사이드바
       바닥에서 HUD로 올라온 버튼이라 이름을 늦게까지 지킨다 —— 그래서 차원 이름보다 뒤다
     5 웨이브 칩 예약 폭 해제. 대기 중에는 카운트다운 자리를 그대로 잡아 초가 줄어도 옆 칩이
       흔들리지 않고, 전투 중에만 자리를 반납한다(예약의 원래 목적은 유지)
     6 두 줄. 여기까지 와도 사라지는 것은 없다는 마지막 보증이다

   헤드리스 크롬 실측(창 1920, 로컬 폰트 로드 후, _hudOverflows와 같은 셈 —— 보이는 자식의
   offsetWidth 합 + 항목 간격, 여기에 HUD 가로 여백 28px을 더한 상자 폭. 단위 px).
   재는 판은 언제나 가장 넓은 쪽이다: 대기(카운트다운 표시) · 차원 5 "황금빛 종언" 배지 ·
   웨이브 128 · 최고 128 · 유닛 30/30. 5단은 예약 폭을 풀어 글자 길이가 그대로 드러나므로
   이 네 값이 결과를 바꾼다 —— 0~4단은 최소폭에 눌려 있어 영향이 없다.
     골드          0단   1단   2단   3단   4단   5단
     500          1386  1271  1254  1203  1013   989
     12,345       1417  1302  1285  1234  1044  1020
     1,234,567    1448  1333  1316  1265  1075  1051
   #app 최대폭 1340에서 배지 있는 판은 1단이면 들어가고, 골드가 7자리까지 자란 판만
   3단(이름 숨김)까지 간다. 배지 없는 판은 0단이 1267px이라 사다리가 아예 돌지 않는다.
   창 폭별 도달 단계(골드 5자리 · 대기 기준, 배지 없음 → 배지 5차원):
     1920·1440 → 0단 → 1단 · 1280 → 1단 → 3단 · 1120 → 4단 → 4단 ·
     1024 → 4단 → 6단 · 960 이하 → 6단 → 6단(두 줄, HUD 높이 52 → 92)
   전 구간에서 4x 버튼이 배속 상자 안에 온전히 있고 도구 다섯 개가 HUD 상자 안에 남는 것을
   헤드리스로 확인했다. 두 줄로 눕는 것은 대기 중에만이고, 전투에 들어가 카운트다운 자리가
   반납되면 같은 창 폭에서 한 줄로 돌아온다.

   웨이브 칩이 제 글자 크기(20px)를 되찾으면서 표가 0~4단에서 3px 늘었고, 예약 폭이
   풀리는 5단에서는 초 자리를 실제 최댓값까지 줄인 몫이 되돌아와 8px 안에서 그친다.
   같은 창 폭에서 도달 단계가 달라진 곳은 한 칸뿐이다 —— 창 960px에 배지 없이 대기
   중일 때가 5단(907)에서 6단(914)으로 2px 차이로 넘어간다. 6단은 아무것도 버리지 않는
   칸이라 잃는 정보는 없고, 2px을 되사려고 타이포를 도로 조이지 않는다. */
#hud.fit-1 {
  --hud-gap: 7px;
  --hud-item-pad-x: 9px;
  /* 최고·유닛 칩의 자리 예약. 글자 폭보다는 아직 넉넉해서 숫자가 늘어도 옆이 밀리지 않는다. */
  --hud-wide-min: 104px;
  --hud-tool-gap: 4px;
  --hud-btn-pad-x: 9px;
  --hud-speed-pad-x: 11px;
  --hud-sep-mx: 2px;
}
#hud.fit-2 { --hud-dimname-max: 46px; }
/* 말줄임은 이 칸에서만 켠다. overflow가 visible이 아닌 flex 아이템은 글자 베이스라인 대신
   상자 아래 모서리로 줄맞춤되므로, 같은 칸에서 배지 줄맞춤도 가운데로 함께 옮겨
   숫자와 이름이 어긋나 보이지 않게 한다. */
#hud.fit-2 #hud-dim-name { overflow: hidden; text-overflow: ellipsis; }
#hud.fit-2 .dimb-txt { align-items: center; }
#hud.fit-3 #hud-dim-name { display: none; }
#hud.fit-3 .dimb-txt { gap: 0; align-items: baseline; }
#hud.fit-4 .hud-btn .hud-label { display: none; }
#hud.fit-4 .hud-btn { gap: 0; }
/* auto = 자동 최소 크기(= min-content)다. 0이 아니라 auto인 것이 중요하다 —— 0으로 두면
   flex가 칩을 내용보다 좁게 눌러 "30/30"이나 웨이브 숫자가 이웃 칩 위로 삐져나온다.
   auto면 예약된 여유만 반납하고 글자는 끝까지 온전하다. */
#hud.fit-5 {
  --hud-wave-min: auto;
  --hud-wide-min: auto;
}
/* 마지막 칸. 요소를 버리는 대신 두 줄로 눕는다 —— #speed-ctl의 margin-left: auto가 그대로
   살아 있어 둘째 줄은 배속 + 도구가 오른쪽에 붙은 의도된 모양이 된다.
   HUD 높이가 바뀌면 UI.fitStage()가 스테이지 배율을 다시 계산한다. */
#hud.fit-6 { flex-wrap: wrap; row-gap: 5px; }

/* 자동합성 토글 */
.toggle-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-width: 0;
  border: none;
  border-radius: 12px;
  padding: 7px 3px;
  font-size: 12.5px;
  background: #e8dcc2;
  color: var(--ink);
  cursor: pointer;
  box-shadow: 0 2px 0 rgba(0,0,0,0.2);
  transition: transform 0.14s cubic-bezier(0.22, 0.9, 0.28, 1), filter 0.14s ease-out, box-shadow 0.14s ease-out;
}
/* 토글은 '누르는 버튼'이 아니라 상태 스위치다. 뜨지 않고 밝기만 아주 살짝 올린다 */
@media (hover: hover) {
  .toggle-btn:hover { filter: brightness(1.045); box-shadow: 0 2px 0 rgba(0,0,0,0.2), 0 3px 8px rgba(70,48,20,0.16); }
}
.toggle-btn:active { transform: scale(0.98); filter: brightness(0.94); box-shadow: 0 1px 0 rgba(0,0,0,0.2); }
@media (prefers-reduced-motion: reduce) {
  .toggle-btn:active { transform: none; }
}
.toggle-btn .track {
  flex: 0 0 auto;
  width: 34px; height: 20px;
  background: rgba(0,0,0,0.3);
  border-radius: 999px;
  position: relative;
  transition: background 0.15s;
}
.toggle-btn .thumb {
  position: absolute;
  top: 2px; left: 2px;
  width: 16px; height: 16px;
  background: #fff;
  border-radius: 50%;
  transition: left 0.15s;
}
.toggle-btn.on { background: #d9c9f2; }
.toggle-btn.on .track { background: #7d3fb0; }
.toggle-btn.on .thumb { left: 16px; }
/* 도현은 굵기가 하나뿐이라 b에 가짜 굵기가 붙지 않도록 400으로 고정한다 */
.toggle-btn b { color: #7d3fb0; font-weight: 400; }

/* 사이드바 자동화 스위치 2종(판매·뽑기)을 한 줄에 균등 배치한다. 합성은 토글이 없다.
   반칸에 "[스위치] 자동 판매 OFF"가 가로로 들어가지 않으므로 세로로 쌓는다:
   스위치 → 이름 → 상태. 2열 × 세로 3단이라 어느 폭에서도 글자가 접히지 않고,
   손가락 목표도 44px 하한을 넉넉히 넘긴다. 설정 모달의 .set-shake 토글은 건드리지 않는다. */
.toggle-row {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px;
}
.toggle-row .toggle-btn {
  flex-direction: column;
  gap: 2px;
  padding: 7px 2px 6px;
  line-height: 1.25;
  white-space: nowrap;
}
.toggle-row .toggle-btn .tg-name { font-size: 12.5px; }
.toggle-row .toggle-btn b { font-size: 12px; opacity: 0.75; }
.toggle-row .toggle-btn.on b { opacity: 1; }

/* ---------- 레이아웃 ---------- */
main { display: flex; gap: var(--fit-gap); align-items: flex-start; }

/* 레터박스 래퍼. 레이아웃 박스는 1000x720 그대로 두고 transform으로 줄인 뒤,
   음수 마진으로 줄어든 만큼의 자리를 반납한다. 래퍼 하나로 끝나므로
   #stage의 shake 애니메이션(자체 transform)과 충돌하지 않는다. */
#stage-fit {
  flex: 0 0 auto;
  width: 1000px;
  height: 720px;
  transform: scale(var(--stage-scale));
  transform-origin: top left;
  margin-right: calc((var(--stage-scale) - 1) * 1000px);
  margin-bottom: calc((var(--stage-scale) - 1) * 720px);
  /* 캔버스 위에서는 스크롤·더블탭 확대가 아니라 게임 조작만 일어난다 */
  touch-action: none;
}

#stage {
  position: relative;
  width: 1000px;
  height: 720px;
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 6px 0 rgba(0,0,0,0.35);
  touch-action: none;
}
#game { display: block; width: 1000px; height: 720px; cursor: pointer; touch-action: none; }

#sidebar {
  flex: 1 1 var(--fit-sidebar);
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  /* 스테이지가 줄면 사이드바도 같은 높이 안에 들어가야 한다.
     축소된 높이에 패널이 다 안 들어가면 페이지가 아니라 사이드바 안에서만 스크롤한다. */
  max-height: calc(720px * var(--stage-scale));
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ---------- 반응형: 좁은 화면의 HUD ----------
   HUD는 한 줄 flex라 폰 세로 폭에서는 그대로 넘친다. 접어서 흐르게 하고 항목을 줄인다.
   도구 버튼은 라벨부터 버린다. 아이콘만 남아도 title·aria-label이 뜻을 지킨다.

   지점을 1240 → 1023으로 내렸다. 라벨 숨김은 이제 폭 예산 사다리의 4단이고, 그 앞에
   여백·차원 이름이라는 더 싼 양보가 세 칸 있다. 이 미디어 쿼리는 사다리가 돌지 않는
   경우(스크립트 오류 등)의 바닥일 뿐이다 —— 실제로는 사다리가 먼저 도달해 1170px 언저리에서
   라벨을 접으므로, 이 지점을 내려도 보이는 결과는 거의 같고 넓은 화면에서만 이름이 더 오래 남는다. */
@media (max-width: 1023px) {
  .hud-btn .hud-label { display: none; }
  .hud-btn { padding: 5px 9px; }
}
@media (max-width: 820px) {
  /* 칩 본문이 줄면 보조 텍스트도 같은 비율로 따라 내린다(--hud-sub 주석 참고) */
  #hud { flex-wrap: wrap; gap: 6px; padding: 6px 9px; --hud-sub: 12px; }
  .hud-item { font-size: 16px; padding: 4px 9px; gap: 5px; }
  .hud-item img { width: 21px; height: 21px; }
  .hud-item.wide { min-width: 0; }
  .hud-item.dim { font-size: 14px; }
  /* 배속과 도구는 한 덩어리로 붙어 있어야 줄바꿈이 나도 흩어지지 않는다 */
  #speed-ctl { margin-left: auto; }
  #hud-tools { gap: 5px; }
}

/* ---------- 반응형: 세로 화면 ----------
   스테이지를 화면 폭에 꽉 채우고 사이드바를 그 아래로 내린다. 세로 스크롤로 읽는다. */
@media (max-width: 1100px) and (max-aspect-ratio: 1/1) {
  :root { --fit-mode: stack; --fit-gap: 8px; }
  main { flex-direction: column; align-items: center; }
  /* 이 모드에서는 사이드바가 제 안에서 스크롤하지 않고 페이지째 흐른다. */
  #sidebar {
    flex: 0 0 auto;
    width: 100%;
    max-height: none;
    overflow: visible;
  }
  /* 세로에서는 가로 폭이 남으므로 인벤토리를 넓게 펴고 로그를 조금 키운다 */
  #inventory { grid-template-columns: repeat(auto-fill, minmax(52px, 1fr)); }
  #log { height: 46px; }
}

/* ---------- 반응형: 낮은 가로 화면(폰 landscape) ----------
   1순위는 캔버스 최대화다. 이 구간에서 스테이지를 키우는 유일한 여유는 세로이므로
   HUD를 스테이지 위가 아니라 사이드바 열 머리로 옮겨(main을 display:contents로 풀고 #app을 2열 그리드로)
   스테이지가 화면 높이를 통째로 쓰게 한다. 남는 가로 폭은 사이드바가 가져간다.
   비터치 판정은 pointer: fine이다 — hover는 S펜 폰 등이 hover 가능으로 답해 믿을 수 없다. */
@media (pointer: fine) and (max-height: 560px) and (min-aspect-ratio: 1/1) {
  :root { --fit-mode: split; --fit-sidebar: 246px; --fit-gap: 6px; }
  html, body { height: 100%; overflow: hidden; }
  body { padding: calc(6px + env(safe-area-inset-top)) calc(6px + env(safe-area-inset-right))
                  calc(6px + env(safe-area-inset-bottom)) calc(6px + env(safe-area-inset-left)); }
  #app {
    height: 100%;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
    gap: var(--fit-gap);
  }
  main { display: contents; }
  #stage-fit { grid-column: 1; grid-row: 1 / span 2; }
  #hud { grid-column: 2; grid-row: 1; }
  #sidebar { grid-column: 2; grid-row: 2; max-height: none; min-height: 0; }
  #hud { gap: 5px; padding: 4px 8px; margin-bottom: 0; border-radius: 11px; flex-wrap: wrap; --hud-sub: 11.5px; }
  .hud-item { font-size: 15px; padding: 3px 8px; gap: 4px; }
  .hud-item img { width: 19px; height: 19px; }
  .hud-item.wide { min-width: 0; }
  .hud-item.dim { font-size: 13px; }
  /* 가로 폭이 가장 귀한 구간이다. 최고 기록은 게임 오버·도전과제에서 볼 수 있으니 HUD에서 내린다 */
  .hud-item:has(#hud-best) { display: none; }
  .hud-btn { padding: 4px 8px; font-size: 15px; }
  .hud-btn .hud-ico { font-size: 16px; }
  .panel { padding: 7px; }
  #selected-panel { padding: 7px; }
  #selected-panel.is-empty { padding: 4px 7px; }
  .sel-hint-row { font-size: 12px; }
  .actions { gap: 6px; }
  .big-btn { font-size: 15px; padding: 8px 6px; }
  .big-btn img { width: 26px; height: 26px; }
  /* 좁아진 사이드바에서 버튼 라벨이 두 줄로 접히면 버튼 높이가 들쭉날쭉해진다 */
  .big-btn span { white-space: nowrap; }
  .big-btn.ten { font-size: 13.5px; gap: 4px; }
  .big-btn.ten .ten-mark { font-size: 12.5px; padding: 4px 5px; }
  #log { height: 24px; font-size: 12px; }
}

/* ---------- 터치 기기 ----------
   손가락은 커서보다 훨씬 뭉툭하다. 눌러야 하는 표면은 44px 아래로 내려가지 않게 한다.
   판정은 pointer: coarse 하나다 — hover: none을 함께 걸면 S펜 폰(hover 가능 판정)이 빠진다. */
@media (pointer: coarse) {
  .big-btn, .toggle-btn, .go-btn, .go-share-btn, .act,
  .go-rank-btn, .go-nick, .mod-btn, .cx-tab, .lab-btn, .lab-node, .dim-card, .forge-up,
  #meta-close, #codex-close, #lab-close, #forge-close, #account-close, #rank-close { min-height: 44px; }
  .cx-reward-btn { min-height: 34px; }
  /* 도감·도전과제·새 게임은 모달·판을 여는 주요 진입점이다. HUD에 있어도 44px를 지킨다. */
  .hud-btn { min-width: 44px; min-height: 44px; }
  .speed-btn { min-width: 46px; min-height: 44px; }
  /* 선택 패널의 강화·판매는 접힌 폴드 안이라 글자만 작다. 상자는 44px로 세운다. */
  .sel-btns .act { padding: 8px 5px; }
  /* 튜토리얼 [건너뛰기]는 글자만 있는 버튼이라 상자가 25px밖에 안 된다 */
  .tt-skip { min-height: 44px; padding: 6px 10px; }

  /* 볼륨 슬라이더: 트랙은 그대로 7px로 보이되 손가락이 닿는 상자를 30px로 넓힌다 */
  .set-vol { min-height: 44px; }
  .set-vol input[type="range"] {
    height: 30px;
    border-radius: 0;
    box-shadow: none;
    background:
      linear-gradient(
        to right,
        #ffb03a 0 calc(var(--p, 100) * 1%),
        #ddc8a2 calc(var(--p, 100) * 1%) 100%
      ) no-repeat center / 100% 7px;
  }
  .set-vol input[type="range"]::-webkit-slider-thumb { width: 22px; height: 22px; }
  .set-vol input[type="range"]::-moz-range-thumb { width: 20px; height: 20px; }

  /* 정보 칩(유물·변이·변형 규칙)은 탭하면 툴팁이 뜬다. 탭 목표를 조금 키운다.
     칩을 44px로 키우면 사이드바에서 덩어리가 너무 무거워지므로, 보이는 크기는 40px로
     두고 투명한 ::before로 히트 영역만 44px까지 넓힌다(바의 gap 5px보다 작은 확장이라
     이웃 칩의 영역과 겹치지 않는다). */
  .relic-chip, .mut-chip { width: 40px; height: 40px; }
  .relic-chip::before, .mut-chip::before {
    content: '';
    position: absolute;
    inset: -2px;
    border-radius: inherit;
  }
  .relic-chip .relic-art { width: 30px; height: 30px; }
  /* 변형 칩은 가로로 긴 알약이라 세로만 모자란다. 폭은 그대로 두고 높이만 세운다. */
  .mod-chip { min-height: 44px; }

  /* 손을 뗀 뒤에도 hover가 남는 기기가 있다. 눌린 채로 떠 있는 것처럼 보이지 않게 한다 */
  .relic-chip:hover, .mut-chip:hover, .mod-chip:hover,
  .draft-card:hover, .mut-card:hover { transform: none; }
  /* 테두리를 도는 광원도 같은 이유로 끈다. 탭한 카드만 계속 빛나 보이면 안 된다. */
  .draft-card:hover::after, .mut-card:hover::after { opacity: 0; animation: none; }

}

.panel {
  background: var(--paper);
  border-radius: 14px;
  padding: 10px;
  box-shadow: 0 3px 0 rgba(0,0,0,0.25), inset 0 0 0 3px var(--paper-dark);
}
.panel h3 { font-size: 17px; margin-bottom: 8px; }
.panel h3 .dim { font-family: var(--font-body); font-weight: 600; letter-spacing: 0; }
.panel .dim { opacity: 0.55; font-size: 13px; }
.panel.grow { flex: 1; overflow: hidden; display: flex; flex-direction: column; }

/* ---------- 액션 버튼 ---------- */
.actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.big-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  justify-content: center;
  border: none;
  border-radius: 12px;
  padding: 10px 8px;
  font-size: 17.5px;
  color: #fff;
  cursor: pointer;
  box-shadow: 0 3px 0 rgba(0,0,0,0.3);
  transition: transform 0.14s cubic-bezier(0.22, 0.9, 0.28, 1),
              filter 0.14s ease-out,
              box-shadow 0.14s ease-out;
}
/* 커서가 올라가면 나무 판에서 살짝 뜨고 밝아진다. HUD 도구 버튼과 같은 계열의 반응이다.
   비활성 버튼(.disabled)은 pointer-events가 꺼져 있어 이 규칙이 아예 걸리지 않는다.
   hover: none 기기에서 손을 뗀 뒤 떠 있는 채로 남지 않도록 hover 질의로 감싼다. */
@media (hover: hover) {
  .big-btn:hover {
    transform: translateY(-2px);
    filter: brightness(1.09) saturate(1.04);
    box-shadow: 0 5px 0 rgba(0,0,0,0.3), 0 7px 14px rgba(48,26,8,0.26);
  }
}
/* 누름은 hover보다 나중에·빠르게 붙어야 '딱' 눌린 느낌이 산다.
   내려앉음 + 살짝 쪼그라듦 + 어두워짐을 함께 줘야 커서가 버튼 위에 있어도 확실히 읽힌다 */
.big-btn:active {
  transform: translateY(2px) scale(0.97);
  filter: brightness(0.93);
  box-shadow: 0 1px 0 rgba(0,0,0,0.3), inset 0 2px 4px rgba(30,14,4,0.28);
  transition-duration: 0.05s;
}
/* .hud-btn과 같은 처리 — 움직임만 걷어내되, 눌림은 밝기·안쪽 그림자로 여전히 답해야 한다 */
@media (prefers-reduced-motion: reduce) {
  .big-btn, .toggle-btn { transition: none; }
  .big-btn:hover, .big-btn:active { transform: none; }
  .big-btn:active { filter: brightness(0.88); box-shadow: 0 1px 0 rgba(0,0,0,0.3), inset 0 2px 5px rgba(30,14,4,0.35); }
}
.big-btn img { width: 34px; height: 34px; object-fit: contain; }
.big-btn span { display: flex; flex-direction: column; line-height: 1.22; }
/* 버튼 라벨은 도현, 그 아래 수치 부속 라벨은 잘 읽히도록 본문체 굵은 굵기로 되돌린다 */
.big-btn small {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0;
  opacity: 0.9;
}
.gacha-group { grid-column: 1 / -1; display: grid; grid-template-columns: 1.4fr 1fr; gap: 8px; }
/* 합성·웨이브 버튼이 사라지고 남은 두 줄이다. 운빨 강화가 뽑기 묶음 아래 한 줄을 채운다. */
.actions #btn-luck { grid-column: 1 / -1; }
/* 유물·변이·변형 칩 바 묶음. .actions 2열 그리드에서 전체 폭 한 줄을 차지하고,
   안에서는 세 바가 세로로 쌓인다(각 바의 grid-column은 이 래퍼가 대신 진다). */
.chip-bars { grid-column: 1 / -1; display: flex; flex-direction: column; min-width: 0; }
.big-btn.gacha { background: linear-gradient(#ffb03a, #f2860c); }
/* 자동 뽑기가 켜져 있으면 버튼이 저절로 눌린다. 왜 그런지 눈이 먼저 알아채도록
   뽑기 버튼 둘레에 아주 느린 호박색 숨을 얹는다(UI.refresh가 .auto-on을 붙인다).
   버튼 자신의 box-shadow는 hover 뜸·눌림 안쪽 그림자·천장 연출이 이미 쓰고 있으므로
   광원은 겹치는 유령 레이어에만 둔다 — 레이아웃도 클릭도 건드리지 않는다. */
.big-btn.gacha.auto-on { position: relative; }
.big-btn.gacha.auto-on::after {
  content: '';
  position: absolute;
  inset: -3px;
  border-radius: 15px;
  pointer-events: none;
  /* 양피지 패널이 이미 밝은 크림색이다. 옅은 노란 광원은 묻히므로
     버튼 자신의 주황 계열로 가느다란 테를 두르고 그 바깥으로만 번지게 한다. */
  box-shadow: 0 0 0 2px rgba(255,170,46,0.9), 0 0 12px 3px rgba(232,124,10,0.45);
  animation: gachaAutoBreath 2.6s ease-in-out infinite;
}
@keyframes gachaAutoBreath {
  0%, 100% { opacity: 0.3; }
  50% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .big-btn.gacha.auto-on::after { animation: none; opacity: 0.72; }
}
.big-btn.ten {
  background: linear-gradient(#ffcf5e, #e0700a);
  gap: 6px;
  padding: 10px 6px;
  font-size: 15px;
  line-height: 1.25;
}
/* 라벨 "10연 뽑기"는 항상 한 줄, 가격은 그 아래 줄로 고정 */
.big-btn.ten > span:last-child { white-space: nowrap; }
.big-btn.ten small { display: block; }
.big-btn.ten .ten-mark {
  display: block;
  flex: 0 0 auto;
  font-size: 15px;
  line-height: 1;
  padding: 6px 7px;
  border-radius: 9px;
  background: rgba(90,44,4,0.45);
  color: #ffe9a8;
  text-shadow: 0 0 8px rgba(255,214,120,0.8);
  box-shadow: inset 0 0 0 1px rgba(255,229,160,0.4);
}
.big-btn.ten #gacha10-info { white-space: nowrap; font-size: 11px; }
.big-btn.gacha #gacha-info.hot { color: #fff0a8; opacity: 1; font-weight: bold; }
.big-btn.gacha #gacha-info { white-space: nowrap; font-size: 11px; }
.big-btn.gacha #gacha-info.ready {
  color: #5a2c04;
  font-weight: bold;
  opacity: 1;
  margin-top: 2px;
  padding: 1px 6px 2px;
  border-radius: 999px;
  background: linear-gradient(#fff8d8, #ffcb5e);
  text-shadow: 0 1px 0 rgba(255,255,255,0.5);
  box-shadow: 0 0 10px rgba(255,214,120,0.85), inset 0 0 0 1px rgba(255,255,255,0.6);
  animation: pity-badge 1.1s ease-in-out infinite alternate;
}
@keyframes pity-badge {
  from { box-shadow: 0 0 6px rgba(255,214,120,0.55), inset 0 0 0 1px rgba(255,255,255,0.5); }
  to   { box-shadow: 0 0 16px rgba(255,224,150,1), inset 0 0 0 1px rgba(255,255,255,0.85); }
}

/* 전설 천장 게이지 — 뽑기 아이콘을 감싸는 원형 진행 링.
   진행 각도는 UI.refresh가 --pity-a에 넣고, 등록 속성이라 transition으로 부드럽게 찬다. */
@property --pity-a {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: true;
}
#btn-gacha { position: relative; }
/* 정렬 속성은 쓰지 않는다. place-items는 inset:0으로 깔아 둔 링 레이어까지
   가운데로 오그라들게 만들어서, 아이콘만 transform으로 직접 중앙에 놓는다. */
.pity-ring {
  position: relative;
  flex: 0 0 auto;
  display: block;
  width: 42px;
  height: 42px;
  font-style: normal;
  /* 채운 구간은 두 색으로 흐르고 남은 구간은 나무 버튼에 파인 홈처럼 어둡게 둔다 */
  --pity-lo: rgba(255,255,255,0.5);
  --pity-hi: rgba(255,255,255,0.92);
  --pity-rest: rgba(74,32,2,0.45);
  --pity-glow: transparent;
  transition: --pity-a 0.4s cubic-bezier(0.25,0.9,0.3,1);
}
.pity-ring img {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 28px;
  height: 28px;
  transform: translate(-50%, -50%);
}
/* 진행 호. 12시에서 시작해 시계 방향으로 찬다. */
.pity-ring::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(from -90deg,
    var(--pity-lo) 0deg,
    var(--pity-hi) var(--pity-a),
    var(--pity-rest) var(--pity-a) 360deg);
  -webkit-mask: radial-gradient(circle farthest-side, transparent calc(100% - 4px), #000 calc(100% - 4px));
  mask: radial-gradient(circle farthest-side, transparent calc(100% - 4px), #000 calc(100% - 4px));
}
/* 후광. 평소에는 투명하고 hot·ready에서만 켜진다. */
.pity-ring::after {
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  opacity: 0;
  box-shadow: 0 0 11px 1px var(--pity-glow);
  pointer-events: none;
  transition: opacity 0.25s;
}
/* 천장이 가까워지면 따뜻한 주황~금색으로 물든다 */
.pity-ring.hot {
  --pity-lo: #ffcf6b;
  --pity-hi: #fff3c4;
  --pity-glow: rgba(255,176,46,0.9);
}
.pity-ring.hot::after { opacity: 1; animation: pityRingPulse 1.15s ease-in-out infinite alternate; }
/* 전설 확정 — 링이 전설 등급색으로 꽉 차고 광이 한 바퀴 돈다 */
.pity-ring.ready {
  --pity-lo: #ffb02e;
  --pity-hi: #fff8d8;
  --pity-rest: #ffb02e;
  --pity-glow: rgba(255,190,70,1);
}
.pity-ring.ready::after { opacity: 1; animation: pityRingPulse 0.95s ease-in-out infinite alternate; }
.pity-spin {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  opacity: 0;
  background: conic-gradient(from 0deg,
    rgba(255,255,255,0) 0deg 210deg,
    rgba(255,255,255,0.95) 322deg,
    rgba(255,255,255,0) 360deg);
  -webkit-mask: radial-gradient(circle farthest-side, transparent calc(100% - 4px), #000 calc(100% - 4px));
  mask: radial-gradient(circle farthest-side, transparent calc(100% - 4px), #000 calc(100% - 4px));
  pointer-events: none;
  transition: opacity 0.3s;
}
.pity-ring.ready .pity-spin { opacity: 1; animation: pityRingSpin 1.5s linear infinite; }
@keyframes pityRingPulse {
  from { opacity: 0.45; transform: scale(0.95); }
  to   { opacity: 1; transform: scale(1.06); }
}
@keyframes pityRingSpin {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .pity-ring::after { animation: none; }
  .pity-ring .pity-spin { animation: none; opacity: 0; }
  .pity-ring { transition: none; }
}
.big-btn.luck { background: linear-gradient(#66bb6a, #3d8b40); }
.big-btn.disabled { filter: grayscale(0.8) brightness(0.75); pointer-events: none; }
.big-btn.glow { animation: glow 1.2s infinite alternate; }
/* 신화·초월 연출 보호 창 —— 연출이 절정을 지나기 전까지 뽑기 입력을 삼킨다.
   .disabled와 달리 pointer-events를 살려 둔다. 눌렸을 때 흔들림으로 "잠깐만"이라고
   답해 줘야 버튼이 죽은 것으로 읽히지 않는다. 창은 1.4~1.8초 안에 스스로 풀린다. */
.big-btn.gacha.reveal-hold {
  filter: brightness(0.74) saturate(0.72);
  cursor: default;
}
@media (hover: hover) {
  .big-btn.gacha.reveal-hold:hover {
    transform: none;
    filter: brightness(0.74) saturate(0.72);
    box-shadow: 0 3px 0 rgba(0,0,0,0.3);
  }
}
.big-btn.gacha.reveal-hold:active { transform: none; box-shadow: 0 3px 0 rgba(0,0,0,0.3); }
.big-btn.gacha.reveal-hold.nope { animation: gachaNope 0.3s ease-out; }
@keyframes gachaNope {
  0%, 100% { transform: translateX(0); }
  24% { transform: translateX(-4px); }
  60% { transform: translateX(3px); }
  84% { transform: translateX(-1.5px); }
}
@media (prefers-reduced-motion: reduce) {
  .big-btn.gacha.reveal-hold.nope { animation: none; }
}
@keyframes glow { from { box-shadow: 0 3px 0 rgba(0,0,0,0.3), 0 0 4px #ffd23f; } to { box-shadow: 0 3px 0 rgba(0,0,0,0.3), 0 0 18px #ffd23f; } }

/* ---------- 선택 패널 ----------
   빈 상태로 196px을 예약하던 패널이다. 이제 선택이 없으면 한 줄 힌트로 접히고
   그만큼을 아래 인벤토리가 가져간다. 두 폴드를 DOM에 함께 두고 여닫기만 한다. */
#selected-panel {
  flex-shrink: 0;
  display: grid;
  padding: 10px;
  transition: padding 0.2s ease;
}
#selected-panel.is-empty { padding: 5px 10px; }
.sel-fold {
  display: grid;
  grid-template-rows: 1fr;
  opacity: 1;
  transition: grid-template-rows 0.2s ease, opacity 0.16s ease;
}
.sel-fold-in { overflow: hidden; min-height: 0; }
#selected-panel.is-empty > #sel-body,
#selected-panel:not(.is-empty) > #sel-hint { grid-template-rows: 0fr; opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  #selected-panel, .sel-fold { transition: none; }
}
.sel-hint-row {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 3px 2px;
  font-size: 13px;
  line-height: 1.5;
  opacity: 0.62;
}
.sel-hint-row b { font-weight: 700; opacity: 0.95; }
.sel-hint-ico { font-size: 15px; line-height: 1; }
.sel-head { display: flex; gap: 10px; align-items: center; }
.sel-head canvas { border: 3px solid #ccc; border-radius: 12px; background: #fff8e8; }
.sel-head .lv { font-size: 13px; opacity: 0.7; }
.ability-card {
  margin-top: 6px;
  padding: 6px 8px;
  border-radius: 10px;
  background: #fff8e8;
  box-shadow: inset 0 0 0 2px #ead9b7;
  font-family: var(--font-body);
}
.ability-title { display: flex; align-items: center; gap: 6px; margin-bottom: 2px; }
.ability-title span {
  border-radius: 7px;
  padding: 2px 6px;
  background: var(--wood);
  color: #fff;
  font-size: 10px;
  font-weight: 800;
  line-height: 1.3;
}
.ability-title b { font-size: 14px; font-weight: 700; }
.ability-card p { font-size: 11px; line-height: 1.45; }
.ability-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 1px 4px;
}
.ability-summary span { white-space: nowrap; }
.ability-summary i { font-style: normal; opacity: 0.55; }
.ability-card small { display: block; margin-top: 2px; color: #75634f; font-size: 10px; line-height: 1.35; }
.ability-next {
  margin-top: 3px;
  border-top: 1px dashed #d4c09a;
  padding-top: 3px;
  color: #7250a1;
  font-size: 10px;
  line-height: 1.35;
}
.sel-btns { display: flex; gap: 8px; margin-top: 8px; }
.sel-btns .act { padding: 6px 5px; font-size: 12px; line-height: 1.2; }
.act {
  flex: 1;
  border: none;
  border-radius: 10px;
  padding: 8px;
  font-size: 14.5px;
  color: #fff;
  cursor: pointer;
  box-shadow: 0 2px 0 rgba(0,0,0,0.25);
}
.act.upgrade { background: linear-gradient(#ffd23f, #f0a500); color: #5d3d22; }
.act.sell { background: linear-gradient(#b0a08e, #8d7964); }
.act.disabled { filter: grayscale(0.8) brightness(0.8); pointer-events: none; }

/* 튜토리얼 강화 단계의 목업 패널. 마크업은 실제 선택 패널과 같으므로 '예시' 배지 하나로만 구분한다. */
#selected-panel.tt-demo .act { cursor: default; }
.sel-demo-tag {
  display: inline-block;
  margin-left: 6px;
  border-radius: 999px;
  padding: 1px 6px;
  background: #ead9b7;
  color: #75634f;
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 800;
  line-height: 1.4;
  vertical-align: 1px;
}

/* ---------- 인벤토리 ---------- */
#inventory {
  display: grid;
  grid-template-columns: repeat(auto-fill, 52px);
  gap: 6px;
  overflow-y: auto;
  flex: 1;
  align-content: start;
  min-height: 120px;
  padding: 2px;
}
#inventory { scrollbar-color: #b79f78 #ead9b7; scrollbar-width: thin; }
#inventory::-webkit-scrollbar { width: 8px; }
#inventory::-webkit-scrollbar-track { background: #ead9b7; border-radius: 4px; }
#inventory::-webkit-scrollbar-thumb { background: #b79f78; border-radius: 4px; }
.panel.grow { min-height: 158px; }
.chip {
  position: relative;
  width: 52px;
  height: 52px;
  background: #fff8e8;
  border: 3px solid #ccc;
  border-radius: 10px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.chip.selected { box-shadow: 0 0 0 3px #fff, 0 0 10px 3px var(--accent); }
/* 초월 칩 — 인벤토리에서 한 칸만 다른 등급이라는 게 바로 보이도록 청록 후광을 맥동시킨다.
   selected의 box-shadow와 겹치지 않게 filter로만 빛낸다 */
.chip.transcend { animation: transcendChip 1.3s ease-in-out infinite alternate; }
@keyframes transcendChip {
  from { filter: drop-shadow(0 0 4px rgba(102,252,241,0.6)); }
  to { filter: drop-shadow(0 0 12px rgba(102,252,241,1)) brightness(1.06); }
}
.sel-head canvas.transcend {
  box-shadow: 0 0 14px rgba(102,252,241,0.75), inset 0 0 12px rgba(102,252,241,0.35);
}
.chip.combinable::after {
  content: '합';
  position: absolute;
  top: -7px; left: -7px;
  background: #ab63d8;
  color: #fff;
  font-size: 11px;
  border-radius: 8px;
  padding: 1px 5px;
}
.chip .count {
  position: absolute;
  bottom: -6px; right: -6px;
  background: var(--ink);
  color: #fff;
  font-size: 11px;
  border-radius: 8px;
  padding: 1px 5px;
}
.chip .role {
  position: absolute;
  top: -6px;
  right: -6px;
  z-index: 2;
  border-radius: 7px;
  padding: 1px 4px;
  background: #fff3d7;
  color: var(--ink);
  box-shadow: 0 1px 3px rgba(0,0,0,0.22);
  font-size: 9px;
  font-weight: 800;
  line-height: 1.4;
  white-space: nowrap;
}
.empty { opacity: 0.5; text-align: center; padding: 20px 0; grid-column: 1 / -1; }

/* ---------- 로그 ---------- */
#log-wrap { padding: 8px; }
#log {
  height: 30px;
  overflow-y: auto;
  font-size: 13px;
  line-height: 1.5;
  display: flex;
  flex-direction: column;
}

/* ---------- 사운드/연출 설정 ---------- */
.settings-panel {
  padding: 6px 9px;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
.set-vol {
  display: flex;
  align-items: center;
  gap: 5px;
  flex: 1 1 0;
  min-width: 0;
  cursor: pointer;
}
.set-vol .set-ico {
  flex: 0 0 auto;
  font-size: 13px;
  line-height: 1;
  transition: opacity 0.15s;
}
/* 볼륨 0이면 채널이 꺼진 것을 아이콘으로도 알린다 */
.set-vol.off .set-ico { opacity: 0.3; }
.set-vol.off input[type="range"] { filter: grayscale(1); }
.set-vol input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  flex: 1 1 0;
  min-width: 0;
  height: 7px;
  border-radius: 999px;
  cursor: pointer;
  background: linear-gradient(
    to right,
    #ffb03a 0 calc(var(--p, 100) * 1%),
    #ddc8a2 calc(var(--p, 100) * 1%) 100%
  );
  box-shadow: inset 0 1px 2px rgba(0,0,0,0.28);
}
.set-vol input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: #fff8e8;
  border: 2px solid var(--wood);
  box-shadow: 0 1px 2px rgba(0,0,0,0.35);
}
.set-vol input[type="range"]::-moz-range-thumb {
  width: 12px; height: 12px;
  border-radius: 50%;
  background: #fff8e8;
  border: 2px solid var(--wood);
  box-shadow: 0 1px 2px rgba(0,0,0,0.35);
}
.set-vol input[type="range"]::-moz-range-track { height: 7px; background: transparent; }
.set-vol input[type="range"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.set-vol:hover input[type="range"]::-webkit-slider-thumb { background: #fff; }

/* 흔들림 토글: 사이드바 토글과 같은 톤의 축소판 */
.toggle-btn.set-shake {
  flex: 0 0 auto;
  gap: 5px;
  padding: 5px 8px;
  border-radius: 10px;
  font-size: 11px;
  white-space: nowrap;
}
.set-shake .track { width: 26px; height: 15px; }
.set-shake .thumb { width: 11px; height: 11px; top: 2px; left: 2px; }
.toggle-btn.set-shake.on .thumb { left: 13px; }

/* ---------- 오버레이 ---------- */
#flash {
  position: absolute; inset: 0;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(ellipse at center, rgba(255,0,0,0) 55%, rgba(255,30,30,0.45));
}
#flash.on { animation: flash 0.45s; }
@keyframes flash { 0% { opacity: 0; } 25% { opacity: 1; } 100% { opacity: 0; } }

#reveal {
  position: absolute; inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
/* 10연 결과 그리드가 깔려 있는 동안에는 중앙 카드를 위로 밀어 겹침을 막는다 */
#reveal { transition: padding-bottom 0.3s ease; }
#reveal.with-bulk { padding-bottom: 160px; }
.reveal-card {
  background: rgba(30,22,12,0.92);
  border-radius: 18px;
  padding: 18px 34px 14px;
  text-align: center;
  animation: revealIn 0.35s cubic-bezier(0.2, 1.6, 0.4, 1);
  border: 3px solid #fff;
}
/* 스프라이트 캔버스는 논리 크기를 CSS로 고정한다. 지정하지 않으면 백킹 해상도
   (140*DPR)가 그대로 레이아웃 크기가 되어 고DPR 화면에서 카드가 2배 이상 부풀고
   상단 배너와 겹친다. */
#reveal-sprite { display: block; width: 140px; height: 130px; }
/* 천장 리본 — 카드 윗변에 걸쳐 "이번 뽑기는 보장으로 확정됐다"를 카드 안에서 말한다 */
#reveal-pity {
  position: absolute;
  z-index: 4;
  left: 50%;
  top: 0;
  transform: translate(-50%, -58%);
  display: flex;
  align-items: center;
  gap: 7px;
  white-space: nowrap;
  padding: 5px 15px 5px 11px;
  border-radius: 999px;
  border: 2px solid #ffe9b0;
  background: linear-gradient(#ffd979, #ffb02e 55%, #e08a12);
  box-shadow: 0 2px 0 rgba(96,48,0,0.45), 0 0 20px #ffb02ecc, inset 0 1px 0 rgba(255,255,255,0.8);
  animation: pityRibbon 0.55s 0.12s cubic-bezier(0.2, 1.9, 0.35, 1) backwards,
             pityRibbonGlow 1.1s 0.7s ease-in-out infinite alternate;
}
#reveal-pity b {
  font-family: var(--font-display, inherit);
  font-size: 14px;
  line-height: 1;
  color: #ffb02e;
  background: #3b2506;
  border-radius: 999px;
  padding: 4px 9px 3px;
  text-shadow: 0 0 8px rgba(255,176,46,0.7);
}
#reveal-pity span {
  font-family: var(--font-display, inherit);
  font-size: 15px;
  line-height: 1;
  color: #4a2b02;
  text-shadow: 0 1px 0 rgba(255,255,255,0.55);
}
@keyframes pityRibbon {
  0% { transform: translate(-50%, -58%) scale(0.4) rotate(-8deg); opacity: 0; }
  60% { transform: translate(-50%, -58%) scale(1.14) rotate(2deg); opacity: 1; }
  100% { transform: translate(-50%, -58%) scale(1) rotate(0); opacity: 1; }
}
@keyframes pityRibbonGlow {
  from { box-shadow: 0 2px 0 rgba(96,48,0,0.45), 0 0 16px #ffb02e99, inset 0 1px 0 rgba(255,255,255,0.8); }
  to { box-shadow: 0 2px 0 rgba(96,48,0,0.45), 0 0 34px #ffd979, inset 0 1px 0 rgba(255,255,255,0.9); }
}
#reveal.g0 .reveal-card { border-color: #9aa3ad; }
#reveal.g1 .reveal-card { border-color: #3b9bff; box-shadow: 0 0 24px #3b9bff88; }
#reveal.g2 .reveal-card { border-color: #b45bff; box-shadow: 0 0 30px #b45bff99; }
#reveal.g3 .reveal-card { border-color: #ffb02e; box-shadow: 0 0 44px #ffb02ebb; }
#reveal.g4 .reveal-card { border-color: #ff4d6d; box-shadow: 0 0 70px #ff4d6dcc; animation: revealIn 0.5s cubic-bezier(0.2, 1.8, 0.4, 1), mythicPulse 0.8s infinite alternate; }
/* 초월 — 프리즘 테두리가 흐르고 카드가 청록 후광에 잠긴다 */
#reveal.g5 .reveal-card {
  border-color: transparent;
  background:
    linear-gradient(rgba(10,26,28,0.94), rgba(4,14,16,0.96)) padding-box,
    conic-gradient(from 0deg, #66fcf1, #a8fff6, #ffffff, #c9b6ff, #5be7a9, #7fd8ff, #66fcf1) border-box;
  box-shadow: 0 0 110px #66fcf1e0, 0 0 200px #66fcf160, inset 0 0 34px rgba(102,252,241,0.22);
}
/* 천장 카드는 금색 테를 한 겹 더 두른다. outline은 등급 글로우 애니메이션이
   건드리는 box-shadow와 충돌하지 않아 어떤 등급 위에도 안전하게 겹친다. */
#reveal.pity .reveal-card {
  outline: 2px solid rgba(255, 176, 46, 0.6);
  outline-offset: 6px;
}
@keyframes mythicPulse { from { transform: scale(1); } to { transform: scale(1.05); } }
@keyframes revealIn { from { transform: scale(0.3); opacity: 0; } to { transform: scale(1); opacity: 1; } }
#reveal-grade {
  display: inline-block;
  color: #fff;
  border-radius: 10px;
  padding: 2px 14px;
  font-size: 15px;
  margin-bottom: 6px;
}
#reveal-name { color: #fff; font-size: 24px; margin-top: 4px; }

#banner {
  position: absolute;
  top: 70px;
  left: 0; right: 0;
  text-align: center;
  font-size: 34px;
  color: #fff;
  text-shadow: 0 3px 0 rgba(0,0,0,0.5), 0 0 22px rgba(0,0,0,0.4);
  pointer-events: none;
}
#banner.pop { animation: bannerPop 2.2s forwards; }
/* 뽑기 카드가 떠 있는 동안에는 배너를 스테이지 최상단으로 올리고 한 단계 줄인다.
   #banner는 DOM에서 #reveal의 뒤 형제라 형제 결합자로 카드 표시 여부를 읽을 수 있다.
   천장 배너는 카드 안 리본으로 대체됐지만, 보스 웨이브·유물·합성 배너가 뽑기와
   겹쳐 뜨는 경우까지 같은 규칙으로 막는다. */
#reveal:not(.hidden) ~ #banner {
  top: 10px;
  font-size: 26px;
  z-index: 5;
}
@keyframes bannerPop {
  0% { transform: scale(0.5); opacity: 0; }
  12% { transform: scale(1.08); opacity: 1; }
  20% { transform: scale(1); }
  82% { opacity: 1; }
  100% { opacity: 0; }
}

/* ---------- 신화·초월 탄생 배너 ----------
   최고 등급이 태어나는 순간은 배너 텍스트 한 줄로 흘려보낼 장면이 아니다.
   등급 재질 플레이트에 캐릭터 초상을 얹어 스테이지 상단에 세우고, 화면 전체에
   짧은 등급색 글로우를 한 번 얹어 시선이 어디 있어도 읽히게 한다.
   캔버스 쪽 빛기둥·프리즘 연출은 Game._spawnRevealFx가 이미 담당한다. */
#birth {
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* #bulk-result(0) · #reveal(auto) · 뽑기 중 #banner(5)보다 위,
     #loading(10) · #gameover(12)보다 아래 — 모달과 게임 오버는 절대 가리지 않는다 */
  z-index: 6;
}
/* 탄생 플레이트가 상단을 차지하는 동안에는 일반 배너를 아래로 내린다.
   뽑기 카드 규칙보다 뒤에 둬서 둘이 동시에 떠도 이 규칙이 이긴다. */
#birth:not(.hidden) ~ #banner {
  top: 172px;
  font-size: 26px;
  z-index: 5;
}

/* 순간 글로우 — 등급색이 위에서 쏟아지고 중앙에 백색 코어가 한 번 번진다 */
.birth-glow {
  position: absolute;
  inset: 0;
  opacity: 0;
}
#birth.mythic .birth-glow {
  background:
    radial-gradient(150% 68% at 50% -8%, rgba(255,77,109,0.55), rgba(255,230,109,0.22) 42%, transparent 74%),
    radial-gradient(circle at 50% 44%, rgba(255,255,255,0.3), transparent 52%);
}
#birth.transcend .birth-glow {
  background:
    radial-gradient(150% 72% at 50% -8%, rgba(102,252,241,0.6), rgba(201,182,255,0.26) 42%, transparent 76%),
    conic-gradient(from 0deg, rgba(102,252,241,0.2), rgba(255,255,255,0.28), rgba(201,182,255,0.2), rgba(91,231,169,0.16), rgba(127,216,255,0.24), rgba(102,252,241,0.2));
}
#birth.on .birth-glow { animation: birthGlow 0.4s ease-out forwards; }
#birth.transcend.on .birth-glow { animation: birthGlow 0.62s ease-out forwards; }
@keyframes birthGlow {
  0% { opacity: 0; }
  14% { opacity: 1; }
  100% { opacity: 0; }
}

/* 슬롯이 가로 중앙 정렬을 맡아서 플레이트의 transform을 애니메이션이 통째로 쓴다 */
.birth-slot {
  position: absolute;
  top: 24px;
  left: 0; right: 0;
  display: flex;
  justify-content: center;
}
.birth-plate {
  position: relative;
  display: flex;
  align-items: center;
  gap: 15px;
  padding: 11px 28px 11px 13px;
  border-radius: 24px;
  border: 3px solid transparent;
  overflow: hidden; /* 광택 스윕을 플레이트 안에 가둔다 */
  transform-origin: 50% 20%;
  opacity: 0;
}
/* 신화 — 무지개 테두리와 맥동하는 홍색 후광 */
#birth.mythic .birth-plate {
  background:
    linear-gradient(152deg, rgba(48,17,25,0.95), rgba(23,10,14,0.96)) padding-box,
    conic-gradient(from 0deg, #ff4d6d, #ffb02e, #ffe66d, #5be7a9, #3b9bff, #b45bff, #ff4d6d) border-box;
  box-shadow: 0 12px 30px rgba(0,0,0,0.5), 0 0 34px rgba(255,77,109,0.55);
}
/* 초월 — 프리즘 테두리에 청록 후광 두 겹, 안쪽에 스펙트럼 안개가 깔린다 */
#birth.transcend .birth-plate {
  background:
    linear-gradient(152deg, rgba(8,31,35,0.95), rgba(3,14,18,0.96)) padding-box,
    conic-gradient(from 0deg, #66fcf1, #a8fff6, #ffffff, #c9b6ff, #5be7a9, #7fd8ff, #66fcf1) border-box;
  box-shadow: 0 12px 34px rgba(0,0,0,0.55), 0 0 46px rgba(102,252,241,0.62), 0 0 96px rgba(102,252,241,0.3);
}
#birth.transcend .birth-plate::before {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(70% 120% at 12% 100%, rgba(102,252,241,0.3), transparent 62%),
    radial-gradient(60% 120% at 88% 0%, rgba(201,182,255,0.26), transparent 60%);
  pointer-events: none;
}
#birth.mythic.on .birth-plate {
  animation: birthPop 0.54s cubic-bezier(0.16, 1.9, 0.3, 1) both,
             birthHaloMythic 1.05s 0.54s ease-in-out infinite alternate;
}
#birth.transcend.on .birth-plate {
  animation: birthPop 0.6s cubic-bezier(0.14, 2, 0.3, 1) both,
             birthHaloTranscend 0.95s 0.6s ease-in-out infinite alternate;
}
@keyframes birthPop {
  0% { opacity: 0; transform: translateY(-26px) scale(0.6); }
  46% { opacity: 1; transform: translateY(0) scale(1.07); }
  72% { transform: translateY(0) scale(0.985); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes birthHaloMythic {
  from { box-shadow: 0 12px 30px rgba(0,0,0,0.5), 0 0 26px rgba(255,77,109,0.45); }
  to { box-shadow: 0 12px 30px rgba(0,0,0,0.5), 0 0 52px rgba(255,230,109,0.7); }
}
@keyframes birthHaloTranscend {
  from { box-shadow: 0 12px 34px rgba(0,0,0,0.55), 0 0 34px rgba(102,252,241,0.5), 0 0 70px rgba(102,252,241,0.24); }
  to { box-shadow: 0 12px 34px rgba(0,0,0,0.55), 0 0 62px rgba(168,255,246,0.85), 0 0 120px rgba(102,252,241,0.42); }
}
#birth.out .birth-plate { animation: birthOut 0.25s cubic-bezier(0.4, 0, 0.7, 0.35) forwards; }
@keyframes birthOut {
  from { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
  to { opacity: 0; transform: translateY(-42px) scale(0.94); filter: blur(3px); }
}

/* 초상 메달 — 발밑에 등급색 광원을 깔아 캐릭터가 빛 위에 서 있게 한다 */
.birth-medal {
  position: relative;
  display: block;
  width: 78px;
  height: 74px;
  flex: 0 0 auto;
  border-radius: 17px;
  background: radial-gradient(circle at 50% 76%, rgba(255,255,255,0.16), rgba(0,0,0,0.34) 72%);
  box-shadow: inset 0 0 0 1.5px rgba(255,255,255,0.14);
}
.birth-medal::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 7px;
  width: 56px;
  height: 13px;
  margin-left: -28px;
  border-radius: 50%;
  background: radial-gradient(ellipse at 50% 50%, var(--c, #fff), transparent 70%);
  opacity: 0.72;
  filter: blur(2px);
}
.birth-medal canvas { display: block; position: relative; z-index: 1; }
#birth.on .birth-medal { animation: birthMedal 0.5s 0.08s cubic-bezier(0.18, 1.8, 0.3, 1) both; }
@keyframes birthMedal {
  0% { opacity: 0; transform: scale(0.5) rotate(-10deg); }
  55% { opacity: 1; transform: scale(1.12) rotate(4deg); }
  100% { opacity: 1; transform: scale(1) rotate(0); }
}

.birth-lines {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
}
.birth-top { display: inline-flex; align-items: center; gap: 9px; }
.birth-grade {
  font-size: 15px;
  line-height: 1;
  color: #1d1013;
  background: var(--c, #fff);
  border-radius: 999px;
  padding: 5px 12px 4px;
  box-shadow: 0 0 16px var(--c, #fff), inset 0 1px 0 rgba(255,255,255,0.55);
}
.birth-verb {
  font-style: normal;
  font-size: 21px;
  line-height: 1;
  color: #ffeccf;
  text-shadow: 0 2px 0 rgba(0,0,0,0.45);
}
/* 플레이트가 광택 스윕을 가두려고 overflow:hidden이라 바깥으로 번지는 글로우는 잘린다.
   외곽 후광은 플레이트 box-shadow가 맡고, 글자 글로우는 플레이트 안에서 끝나게 좁힌다. */
.birth-name {
  font-size: 38px;
  line-height: 1.06;
  color: #fff;
  text-shadow: 0 3px 0 rgba(0,0,0,0.5), 0 0 14px var(--c, #fff), 0 0 30px var(--c, #fff);
}
#birth.on .birth-top { animation: birthLine 0.42s 0.16s cubic-bezier(0.2, 1.4, 0.35, 1) both; }
#birth.on .birth-name { animation: birthLine 0.46s 0.22s cubic-bezier(0.2, 1.4, 0.35, 1) both; }
@keyframes birthLine {
  from { opacity: 0; transform: translateX(-18px); }
  to { opacity: 1; transform: translateX(0); }
}

/* 광택 스윕 1회 — 플레이트가 자리를 잡은 직후 한 번만 지나간다 */
.birth-sheen {
  position: absolute;
  top: -20%;
  bottom: -20%;
  left: 0;
  width: 34%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,0.4) 46%, rgba(255,255,255,0.62) 52%, transparent);
  transform: skewX(-18deg) translateX(-180%);
  opacity: 0;
  pointer-events: none;
}
#birth.on .birth-sheen { animation: birthSheen 0.8s 0.3s cubic-bezier(0.3, 0.1, 0.2, 1) both; }
@keyframes birthSheen {
  0% { opacity: 0; transform: skewX(-18deg) translateX(-180%); }
  14% { opacity: 1; }
  86% { opacity: 0.9; }
  100% { opacity: 0; transform: skewX(-18deg) translateX(430%); }
}

/* 팝·스윕·맥동은 걷어내고 등장/퇴장 페이드만 남긴다 */
@media (prefers-reduced-motion: reduce) {
  #birth.mythic.on .birth-plate,
  #birth.transcend.on .birth-plate { animation: birthFadeIn 0.3s ease-out both; }
  #birth.out .birth-plate { animation: birthFadeOut 0.18s ease-in forwards; }
  #birth.on .birth-medal,
  #birth.on .birth-top,
  #birth.on .birth-name { animation: none; }
  #birth.on .birth-sheen { animation: none; opacity: 0; }
  @keyframes birthFadeIn { from { opacity: 0; } to { opacity: 1; } }
  @keyframes birthFadeOut { from { opacity: 1; } to { opacity: 0; } }
}

/* 보스 등장 카드 — 원형 아레나 바깥(좌상단)이라 전투를 가리지 않는다 */
#boss-intro {
  position: absolute;
  top: 14px;
  left: 14px;
  width: 300px;
  pointer-events: none;
  z-index: 1; /* 캔버스 위, 중앙 reveal·모달 아래 */
}
#boss-intro.in { animation: bossIntroIn 0.42s cubic-bezier(0.16, 1.2, 0.3, 1) both; }
#boss-intro.out { animation: bossIntroOut 0.22s ease-in forwards; }
@keyframes bossIntroIn {
  from { opacity: 0; transform: translateX(-34px) scale(0.94); }
  to { opacity: 1; transform: translateX(0) scale(1); }
}
@keyframes bossIntroOut {
  from { opacity: 1; transform: translateX(0); }
  to { opacity: 0; transform: translateX(-22px); }
}
.bi-card {
  position: relative;
  background: linear-gradient(160deg, rgba(46,18,26,0.94), rgba(24,12,14,0.94));
  border: 2px solid #ff4d6d;
  border-left-width: 6px;
  border-radius: 14px;
  padding: 10px 14px 12px;
  box-shadow: 0 6px 22px rgba(0,0,0,0.55), 0 0 26px rgba(255,77,109,0.35);
  overflow: hidden;
}
.bi-card::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 30%, rgba(255,255,255,0.16) 48%, transparent 62%);
  transform: translateX(-120%);
  animation: bossIntroSheen 1.1s 0.15s ease-out;
}
@keyframes bossIntroSheen { to { transform: translateX(120%); } }
.bi-head { display: flex; align-items: center; gap: 8px; }
.bi-kicker {
  font-size: 12px;
  letter-spacing: 2px;
  color: #2a0d14;
  background: #ff4d6d;
  border-radius: 6px;
  padding: 1px 7px;
  box-shadow: 0 0 12px rgba(255,77,109,0.6);
}
.bi-name {
  font-size: 26px;
  color: #fff;
  text-shadow: 0 2px 0 rgba(0,0,0,0.55), 0 0 16px rgba(255,77,109,0.7);
}
.bi-tele {
  list-style: none;
  margin: 8px 0 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.bi-tele li {
  position: relative;
  padding-left: 15px;
  font-size: 14px;
  line-height: 1.35;
  color: #ffdfe4;
  opacity: 0;
  animation: bossIntroLine 0.3s ease-out both;
  animation-delay: calc(0.18s + var(--i) * 0.09s);
}
.bi-tele li::before {
  content: '';
  position: absolute;
  left: 2px;
  top: 7px;
  width: 6px; height: 6px;
  border-radius: 2px;
  background: #ff8fa3;
  transform: rotate(45deg);
}
@keyframes bossIntroLine {
  from { opacity: 0; transform: translateX(-8px); }
  to { opacity: 1; transform: translateX(0); }
}
.bi-tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px;
  margin-top: 9px;
  padding-top: 8px;
  border-top: 1px dashed rgba(255,143,163,0.4);
}
.bi-tag-label { font-size: 12px; color: #ffb3c0; opacity: 0.85; }
.bi-tag {
  font-size: 13px;
  color: #fff3c4;
  background: rgba(255,159,28,0.18);
  border: 1px solid rgba(255,159,28,0.7);
  border-radius: 999px;
  padding: 1px 10px;
}

/* 토스트는 빠르게 연속 갱신된다. keyframes 대신 transition을 쓰면 퇴장 도중 새 메시지가
   와도 중간 위치에서 그대로 다시 올라온다. 센터링 translateX(-50%)에 translateY를 합성한다. */
#toast {
  position: absolute;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%) translateY(8px);
  background: rgba(30,22,12,0.9);
  color: #ffd9a0;
  border-radius: 12px;
  padding: 8px 20px;
  font-size: 15px;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  /* 퇴장은 진입보다 빠르게 — 다음 메시지가 곧바로 올라올 수 있어야 한다 */
  transition:
    opacity 0.12s ease-in,
    transform 0.12s ease-in,
    visibility 0s linear 0.12s;
}
#toast.show {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
  transition:
    opacity 0.18s cubic-bezier(0.23, 1, 0.32, 1),
    transform 0.18s cubic-bezier(0.23, 1, 0.32, 1),
    visibility 0s;
}
@media (prefers-reduced-motion: reduce) {
  #toast, #toast.show { transform: translateX(-50%); }
}

/* ---------- 판 결과 리포트 ---------- */
/* #app 바깥의 고정 전체 화면 모달이다(index.html 참고). 스테이지 안에 있던 시절에는
   --stage-scale 축소를 함께 받아 좁은 화면에서 입력·버튼이 20px 아래로 내려갔다.
   층은 다른 모달(20~30) 아래 — 변형 규칙 선택이 결과 화면 위에 겹치는 순서를 지킨다. */
#gameover {
  position: fixed; inset: 0;
  background: radial-gradient(circle at 50% 40%, rgba(62,32,16,0.6), rgba(16,9,5,0.9));
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--modal-pad);
  z-index: 18;
}
.go-card {
  width: min(600px, 100%);
  /* 부모가 이미 안전 영역만큼 패딩을 먹었으므로 100%가 곧 사용 가능한 높이다 */
  max-height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border-radius: 20px;
  overflow: hidden;
  box-shadow: 0 14px 44px rgba(0,0,0,0.55), 0 0 0 3px rgba(122,82,48,0.4);
}
.go-head {
  flex: 0 0 auto;
  text-align: center;
  padding: 18px 24px 13px;
  background: linear-gradient(#fff8e7, #f2e3c2);
  border-bottom: 2px dashed #e0cca4;
}
.go-card h1 {
  font-size: 36px;
  color: #c62828;
  letter-spacing: 1px;
  text-shadow: 0 2px 0 rgba(255,255,255,0.65);
}
/* ---------- 정복 승리 화면 ---------- */
/* 패배(붉은 제목 · 종이 톤)와 한눈에 구분되도록 금색+청록 축하 톤으로 뒤집는다 */
.go-card.win {
  box-shadow: 0 14px 44px rgba(0,0,0,0.55), 0 0 0 3px rgba(217,154,21,0.55), 0 0 60px rgba(102,252,241,0.35);
}
.go-card.win .go-head {
  position: relative;
  overflow: hidden;
  background: linear-gradient(#fffbe9, #ffeec0 55%, #e9f7f6);
  border-bottom-color: #e2c98d;
}
/* 헤더를 가로지르는 빛 띠 — 승리 화면에서만 계속 흐른다 */
.go-card.win .go-head::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 36%, rgba(255,255,255,0.85) 50%, rgba(102,252,241,0.5) 58%, transparent 70%);
  animation: winSweep 3.2s linear infinite;
  pointer-events: none;
}
@keyframes winSweep {
  from { transform: translateX(-120%); }
  to { transform: translateX(120%); }
}
.go-card.win h1 {
  position: relative;
  color: #d99a15;
  text-shadow: 0 2px 0 rgba(255,255,255,0.8), 0 0 18px rgba(255,205,86,0.85), 0 0 34px rgba(102,252,241,0.6);
  animation: winTitle 1.4s ease-in-out infinite alternate;
}
@keyframes winTitle {
  from { transform: scale(1); filter: brightness(1); }
  to { transform: scale(1.035); filter: brightness(1.14); }
}
.go-card.win .go-note { position: relative; color: #2f7d76; }
.go-card.win .go-foot {
  background: linear-gradient(#f7edd2, #ecefe2);
  border-top-color: #e2c98d;
}
/* [무한 계속] — 승리 화면에서만 노출되는 청록 강조 버튼 */
.go-btn.continue {
  background: linear-gradient(#5fe3da, #169c96);
  box-shadow: 0 3px 0 #0e6f6b, 0 0 20px rgba(102,252,241,0.55);
  text-shadow: 0 1px 2px rgba(0,0,0,0.3);
}
.go-btn.continue:hover { filter: brightness(1.08); }
.go-sub { font-size: 16px; color: #7a6448; margin-top: 3px; }
.go-sub b { color: var(--ink); }
.go-note { font-size: 13px; color: #8a7358; margin-top: 3px; }

.go-body { flex: 1; min-height: 0; overflow-y: auto; padding: 15px 20px 18px; }
.go-page { display: flex; flex-direction: column; gap: 15px; }
.go-foot {
  flex: 0 0 auto;
  display: flex;
  gap: 10px;
  padding: 12px 20px 15px;
  border-top: 2px dashed #e0cca4;
  background: linear-gradient(#f6ead0, #efdfbe);
}
.go-btn {
  flex: 1;
  border: none;
  border-radius: 12px;
  padding: 11px 8px;
  font-size: 17.5px;
  color: #fff;
  cursor: pointer;
  transition: transform 0.08s, filter 0.15s;
}
.go-btn.primary { background: linear-gradient(#66bb6a, #3d8b40); box-shadow: 0 3px 0 #2c6630; }
.go-btn.ghost {
  background: linear-gradient(#fdf3dc, #e8d5ac);
  color: #6b4a28;
  box-shadow: 0 3px 0 #c4a97c;
}
.go-btn:hover { filter: brightness(1.05); }
.go-btn:active { transform: translateY(3px); box-shadow: none; }

/* ---------- 결과 공유 카드 줄 ---------- */
/* 판을 끝내는 버튼(새 판·무한 계속)과 섞이면 안 되므로 별도 줄에 어두운 톤으로 둔다 */
.go-share {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 20px;
  border-top: 2px dashed #e0cca4;
  background: linear-gradient(#2b1d12, #1c130b);
}
.go-share-label {
  flex: 0 0 auto;
  margin-right: auto;
  font-family: var(--font-display);
  font-size: 15px;
  letter-spacing: 0.5px;
  color: #e3c489;
  text-shadow: 0 1px 2px rgba(0,0,0,0.6);
}
.go-share-btn {
  flex: 0 0 auto;
  border: 1px solid rgba(227,196,137,0.5);
  border-radius: 10px;
  padding: 8px 16px;
  font-family: var(--font-display);
  font-size: 15px;
  color: #ffeec8;
  background: linear-gradient(rgba(122,82,48,0.85), rgba(72,45,24,0.9));
  box-shadow: 0 2px 0 rgba(0,0,0,0.45), inset 0 1px 0 rgba(255,236,196,0.18);
  cursor: pointer;
  transition: transform 0.08s, filter 0.15s, box-shadow 0.15s;
}
.go-share-btn:hover {
  filter: brightness(1.15);
  box-shadow: 0 2px 0 rgba(0,0,0,0.45), 0 0 14px rgba(255,205,86,0.4), inset 0 1px 0 rgba(255,236,196,0.25);
}
.go-share-btn:active { transform: translateY(2px); box-shadow: none; }
.go-share-btn[disabled] { opacity: 0.55; cursor: default; filter: none; transform: none; }
/* 정복 화면에서는 공유 줄도 금·청록 톤을 따라간다 */
.go-card.win .go-share {
  border-top-color: #e2c98d;
  background: linear-gradient(#241a24, #10201f);
}
.go-card.win .go-share-label { color: #ffd766; text-shadow: 0 0 12px rgba(255,205,86,0.6); }
.go-card.win .go-share-btn {
  border-color: rgba(255,215,102,0.6);
  background: linear-gradient(rgba(120,86,26,0.85), rgba(22,80,78,0.9));
}

/* ---------- 온라인 랭킹 등록 줄 ---------- */
/* 요약 페이지에 속한 줄이다(상세 통계에서는 접힌다). 어두운 공유 줄과 붙어 있으므로
   양피지 톤을 그대로 유지해 "게임 안의 종이"에서 이름을 적어 올리는 인상으로 둔다. */
.go-rank {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 11px 20px 12px;
  border-top: 2px dashed #e0cca4;
  background: linear-gradient(#fbf3de, #f2e5c6);
}
.go-rank-form { display: flex; align-items: center; gap: 8px; }
.go-rank-account {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 11px;
  border: 2px solid #c9b77f;
  border-radius: 10px;
  background: linear-gradient(#fffdf5, #f2ead1);
  color: var(--ink);
}
.go-rank-account-icon { flex: 0 0 auto; font-size: 19px; line-height: 1; }
.go-rank-account-copy { min-width: 0; display: flex; flex-direction: column; line-height: 1.15; }
.go-rank-account-copy b {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 15px;
}
.go-rank-account-copy small { margin-top: 2px; font-size: 10.5px; color: #8a7358; }
.go-nick {
  flex: 1 1 auto;
  min-width: 0;
  border: 2px solid #dcc389;
  border-radius: 10px;
  padding: 8px 11px;
  background: #fffbf0;
  font-family: var(--font-body);
  /* 16px 미만이면 iOS 사파리가 포커스 순간 페이지를 확대하고 되돌리지 않는다 */
  font-size: 16px;
  font-weight: 700;
  color: var(--ink);
  outline: none;
  transition: border-color 0.14s ease-out, box-shadow 0.14s ease-out;
}
.go-nick::placeholder { color: #b5a288; font-weight: 400; }
.go-nick:focus { border-color: #ffb02e; box-shadow: 0 0 0 3px rgba(255,176,46,0.25); }
.go-nick[disabled] { opacity: 0.6; background: #f4ecd9; }
.go-rank-btn {
  flex: 0 0 auto;
  border: none;
  border-radius: 10px;
  padding: 9px 14px;
  font-family: var(--font-display);
  font-size: 14.5px;
  letter-spacing: var(--ls-display);
  color: #6b4a28;
  background: linear-gradient(#fdf3dc, #e8d5ac);
  box-shadow: 0 3px 0 #c4a97c;
  cursor: pointer;
  transition: transform 0.08s, filter 0.15s;
}
.go-rank-btn.primary {
  color: #fff;
  background: linear-gradient(#ffb02e, #e07d10);
  box-shadow: 0 3px 0 #a75c07;
  text-shadow: 0 1px 2px rgba(120,60,10,0.5);
}
/* 등록을 마친 판은 초록으로 잠긴다 — 눌러도 되는 버튼처럼 보이면 안 된다 */
.go-rank-btn.primary.is-done {
  background: linear-gradient(#8fbf72, #4d8a3a);
  box-shadow: 0 3px 0 #386828;
}
.go-rank-btn[disabled] { opacity: 0.5; cursor: default; filter: none; transform: none; }
.go-rank-btn.is-done[disabled] { opacity: 1; }
@media (hover: hover) {
  .go-rank-btn:not([disabled]):hover { filter: brightness(1.06); }
}
.go-rank-btn:not([disabled]):active { transform: translateY(3px); box-shadow: none; }
.go-rank-msg { font-size: 12.5px; line-height: 1.45; color: #8a7358; padding-left: 2px; }
.go-rank-msg.is-warn { color: #c0552b; }
.go-rank-msg.is-good { color: #3d7a30; font-weight: 700; }
/* API가 없는 환경(로컬 개발)에서는 입력 자체를 걷고 안내 한 줄만 남긴다 */
.go-rank.is-offline { padding: 10px 20px; }
.go-rank.is-offline .go-rank-msg { color: #8a7358; font-weight: 400; text-align: center; }
.go-card.win .go-rank {
  border-top-color: #e2c98d;
  background: linear-gradient(#fdf7e4, #f3eedc);
}
@media (prefers-reduced-motion: reduce) {
  .go-nick, .go-rank-btn { transition: none; }
  .go-rank-btn:not([disabled]):active { transform: none; }
}

/* 통계 타일 */
.go-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(76px, 1fr));
  gap: 7px;
}
.go-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  padding: 8px 4px;
  border-radius: 12px;
  background: #fff8e8;
  box-shadow: inset 0 0 0 2px #ead9b7;
}
.go-tile b { font-size: 19px; color: #7a4e24; line-height: 1.1; }
.go-tile small { font-size: 10px; color: #75634f; }

/* 이번 판이 연구소에 넣어 준 클로버. 계산식을 숨기지 않는다 —— 차원 배율이
   얼마나 벌어 줬는지 눈으로 보여야 다음 판에 차원을 올릴 이유가 된다. */
.go-clover {
  display: grid;
  gap: 3px;
  padding: 10px 13px;
  border-radius: 14px;
  background: linear-gradient(120deg, #f2fbec, #dcf1d2);
  box-shadow: inset 0 0 0 2px #96c98a, 0 2px 8px rgba(30,84,36,0.15);
}
.go-clover-head { display: flex; align-items: baseline; gap: 7px; }
.go-clover-head i { font-style: normal; font-size: 16px; }
.go-clover-head span {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 14px;
  color: #2f6b34;
}
.go-clover-head b {
  margin-left: auto;
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 21px;
  color: #26612c;
}
.go-clover-calc { font-size: 11.5px; color: #56784f; letter-spacing: 0.01em; }
.go-clover-purse { font-size: 10.5px; color: #7d9276; letter-spacing: 0.01em; }

/* 이번 판이 각 직업에 남긴 문장. 피해 지분이 곧 배분이라 이 줄은 "누가 이 판을 끌었나"의
   결산이기도 하다 —— MVP 카드와 같은 이야기를 성장 쪽에서 한 번 더 맺는다. */
.go-emblem {
  display: grid;
  gap: 7px;
  padding: 10px 13px;
  border-radius: 14px;
  background: linear-gradient(120deg, #fdf6e6, #f5e6c6);
  box-shadow: inset 0 0 0 2px #e0bc72, 0 2px 8px rgba(120,80,20,0.15);
}
.go-emblem-head { display: flex; align-items: baseline; gap: 7px; }
.go-emblem-head i { font-style: normal; font-size: 15px; }
.go-emblem-head span {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 14px;
  color: #8a5a12;
}
.go-emblem-head b {
  margin-left: auto;
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 21px;
  color: #7a4e24;
}
.go-emblem-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(78px, 1fr));
  gap: 6px;
}
.go-emblem-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  border-radius: 11px;
  padding: 6px 4px 5px;
  background: #fffaf0;
  box-shadow: inset 0 0 0 1.5px #e6d4b0;
  cursor: help;
}
.go-emblem-chip canvas { display: block; }
.go-emblem-chip small { font-size: 10px; color: #75634f; letter-spacing: 0.01em; }
.go-emblem-chip b {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 14px;
  color: #a3752a;
}

/* 차원 정복 축하판. 마지막 차원(황금빛 종언)만 금빛으로 갈아입는다. */
.go-dim {
  display: grid;
  gap: 3px;
  padding: 12px 14px;
  border-radius: 16px;
  background: linear-gradient(120deg, #fff0f3, #fbdbe1);
  box-shadow: inset 0 0 0 2px #e58ba0, 0 3px 12px rgba(150,30,60,0.2);
  text-align: center;
}
.go-dim-kicker {
  justify-self: center;
  border-radius: 999px;
  padding: 2px 12px;
  background: linear-gradient(135deg, #ff7d97, #c8324f);
  color: #fff4f6;
  font-family: var(--font-display);
  font-size: 11px;
  letter-spacing: 0.02em;
  text-shadow: 0 1px 2px rgba(110,10,32,0.45);
}
.go-dim-name {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 20px;
  color: #a3123a;
}
.go-dim-next { font-size: 11.5px; line-height: 1.45; color: #8a5f68; letter-spacing: 0.01em; }
.go-dim.is-final {
  background: linear-gradient(120deg, #fff8dd, #f7e6ad);
  box-shadow: inset 0 0 0 2px #e0b14e, 0 3px 14px rgba(180,130,20,0.28);
}
.go-dim.is-final .go-dim-kicker { background: linear-gradient(135deg, #ffd76a, #d9911f); color: #4a2f04; }
.go-dim.is-final .go-dim-name { color: #a1701a; }
.go-dim.is-final .go-dim-next { color: #8a7038; }

/* 이번 판 vs 역대 기록 */
.go-pb { display: grid; gap: 5px; }
.go-pb-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: baseline;
  gap: 8px;
  padding: 6px 11px;
  border-radius: 11px;
  background: #fff8e8;
  box-shadow: inset 0 0 0 2px #ead9b7;
}
.go-pb-row small { font-size: 12px; color: #75634f; }
.go-pb-row b { font-size: 17px; color: #7a4e24; }
.go-pb-row i { font-style: normal; font-size: 11px; color: #9a8666; }
.go-pb-row em {
  font-style: normal;
  font-size: 10px;
  color: #fff6e0;
  background: linear-gradient(135deg, #f0a92c, #d9701f);
  border-radius: 999px;
  padding: 2px 8px;
  box-shadow: 0 1px 3px rgba(120,70,10,0.35);
}
/* 경신한 줄만 금빛으로 띄운다 */
.go-pb-row.is-new {
  grid-template-columns: 1fr auto auto auto;
  background: linear-gradient(120deg, #fff6dc, #ffe9b8);
  box-shadow: inset 0 0 0 2px #f0c069, 0 2px 7px rgba(190,130,30,0.28);
}
.go-pb-row.is-new b { color: #b5651a; }
.go-pb-row.is-new i { color: #a2803f; }

/* MVP */
.go-mvp {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 16px 12px 10px;
  border-radius: 16px;
  background: linear-gradient(120deg, #fff8e6, #f3e3bf);
  box-shadow: inset 0 0 0 2px var(--c, #e0cca4), 0 2px 8px rgba(80,55,20,0.18);
}
.go-mvp.is-empty { justify-content: center; padding: 16px; }
.go-mvp-art {
  position: relative;
  flex: 0 0 92px;
  display: grid;
  place-items: end center;
}
.go-mvp-art::before {
  content: '';
  position: absolute;
  left: 6px; right: 6px; bottom: 4px;
  height: 62px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--c, #ffb02e), rgba(255,255,255,0) 70%);
  opacity: 0.4;
}
.go-mvp-art canvas { position: relative; }
.go-mvp-info { flex: 1; min-width: 0; }
.go-mvp-head { display: flex; align-items: baseline; gap: 8px; }
.go-mvp-head b { font-size: 23px; color: #4a3626; }
.go-mvp-head em { font-style: normal; font-size: 13px; }
.go-kicker {
  font-size: 11px;
  letter-spacing: 2px;
  color: #a8701f;
  background: #fff2d0;
  border-radius: 999px;
  padding: 2px 9px;
  box-shadow: inset 0 0 0 1.5px #e8cd94;
}
.go-mvp-row {
  display: grid;
  grid-template-columns: 56px 1fr 42px;
  align-items: center;
  gap: 8px;
  margin-top: 7px;
}
.go-mvp-row small { font-size: 12px; color: #75634f; }
.go-mvp-row > span { text-align: right; font-size: 14px; color: #5c452e; }

.go-bar {
  height: 8px;
  border-radius: 999px;
  background: #e7d9bd;
  overflow: hidden;
  box-shadow: inset 0 1px 2px rgba(90,65,30,0.22);
}
.go-bar i {
  display: block;
  height: 100%;
  min-width: 3px;
  border-radius: inherit;
  background: linear-gradient(90deg, #ffb02e, #ff8a3d);
}

/* 섹션 */
.go-sec { display: flex; flex-direction: column; gap: 8px; }
.go-sec h4 {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 15px;
  color: #8a6a3c;
  padding-bottom: 4px;
  border-bottom: 2px solid #ead9b7;
}
.go-sec h4 span {
  margin-left: auto;
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0;
  color: #9a8666;
}
.go-empty { font-size: 12px; color: #8b775c; padding: 4px 2px; }

/* ---------- 다음 판 팁 ----------
   요약 페이지의 마지막 절. 리포트 수치를 가리면 안 되므로 값 타일보다 낮은 위계로 두되,
   바탕(#fff8e8)보다 한 단 밝고 따뜻한 배지로 깔아 눈이 마지막에 반드시 한 번 머물게 한다. */
.go-tips { display: grid; gap: 6px; }
.go-tip {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 10px;
  padding: 8px 12px 8px 9px;
  border-radius: 13px;
  background: linear-gradient(104deg, #fffbee, #fdf0cd);
  box-shadow: inset 0 0 0 2px #f0d79a, 0 1px 5px rgba(150,110,40,0.16);
  /* 순번 --i는 카드 생성 시 인라인으로 붙는다. 다른 절이 다 그려진 뒤 순서대로 들어온다. */
  animation: goTipIn 0.28s cubic-bezier(0.23, 1, 0.32, 1) backwards;
  animation-delay: calc(var(--i, 0) * 0.07s + 0.12s);
}
.go-tip-ico {
  display: grid;
  place-items: center;
  width: 31px;
  height: 31px;
  border-radius: 10px;
  background: radial-gradient(circle at 50% 32%, #fff6da, #f2dba4);
  box-shadow: inset 0 0 0 1.5px #e6c98d, 0 1px 2px rgba(140,100,35,0.18);
}
.go-tip-art { display: block; width: 22px; height: 22px; object-fit: contain; }
.go-tip-art.is-glyph { font-size: 17px; line-height: 1; text-align: center; }
.go-tip p {
  font-size: 12.5px;
  line-height: 1.5;
  color: #6d5334;
  letter-spacing: 0.01em;
}
@keyframes goTipIn {
  from { opacity: 0; transform: translateY(7px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .go-tip { animation: goTipFadeIn 0.2s ease-out backwards; animation-delay: calc(var(--i, 0) * 0.07s); }
  @keyframes goTipFadeIn { from { opacity: 0; } to { opacity: 1; } }
}
@media (max-width: 520px) {
  .go-tip { padding: 7px 10px 7px 8px; gap: 8px; }
  .go-tip-ico { width: 27px; height: 27px; border-radius: 9px; }
  .go-tip-art { width: 19px; height: 19px; }
  .go-tip-art.is-glyph { font-size: 15px; }
  .go-tip p { font-size: 12px; line-height: 1.45; }
}

/* 빌드 유물·변이. 등급 색은 아래 '성능 등급 4단계' 절의 --rq-* 토큰에서 온다. */
.go-relics { display: flex; flex-wrap: wrap; gap: 6px; }
.go-muts { margin-top: 7px; }
.go-relic {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 11px 3px 4px;
  /* 필도 카드·칩과 같은 등급 테두리 계단을 쓴다 */
  border: var(--rq-cbw, 1.5px) solid transparent;
  border-radius: 999px;
  background:
    linear-gradient(var(--rq-top, #fdf3dc), var(--rq-bot, #eddcb4)) padding-box,
    var(--rq-edge, linear-gradient(#dcc389, #dcc389)) border-box;
  box-shadow:
    var(--rq-bevel, 0 0 0 0 transparent),
    0 0 0 var(--rq-rw, 0px) var(--rq-ring, transparent),
    0 0 var(--rq-halo, 0px) var(--rq-glow, transparent);
  font-size: 13px;
  font-weight: 600;
  color: var(--rq-ink, #6b4a28);
  cursor: help;
}
.go-relic-icon { display: grid; place-items: center; width: 24px; height: 24px; }
.go-relic .relic-art { width: 22px; height: 22px; }
.go-relic .relic-art.is-glyph { font-size: 13px; }

/* 변이 필 — 등급 글자를 앞머리 배지로 두고 테두리·배지 색도 등급을 따른다.
   등급별 --mc- 토큰과 --rq- 토큰은 아래 '직업 변이' 절의 .mut-b/.mut-a/.mut-s가 준다. */
.go-mut { padding-left: 3px; }
.go-mut-grade {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: linear-gradient(var(--mc-soft, #8fb3cf), var(--mc-key, #3f6284));
  color: #fff;
  font-size: 12px;
  text-shadow: 0 1px 1px rgba(20,10,40,0.45);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.45);
}
.go-mut.mut-s .go-mut-grade {
  background: linear-gradient(150deg, #35d8ff, #8a5bff 45%, #ff6fd8);
}

/* 직업별 전투 기록 */
.go-rows { display: flex; flex-direction: column; gap: 6px; }
.go-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 11px;
  border-radius: 12px;
  background: #fffaf0;
  box-shadow: inset 0 0 0 1.5px #ecdcbb;
}
.go-row > canvas { flex: 0 0 40px; }
.go-row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.go-row-head { display: flex; align-items: baseline; gap: 6px; font-size: 15px; }
.go-row-head b { color: #4a3626; }
.go-row-head em { font-style: normal; font-size: 11px; }
.go-row-head small { font-size: 11px; color: #8b775c; }
.go-row-head span { margin-left: auto; font-size: 13px; color: #5c452e; }
.go-row-foot { font-size: 11px; color: #7d6a52; }

/* 뽑기 등급 분포 */
/* 등급 수(초월 포함 6종)가 늘어도 한 줄을 유지하도록 고정 5칸 대신 auto-fit을 쓴다 */
.go-grades { display: grid; grid-template-columns: repeat(auto-fit, minmax(58px, 1fr)); gap: 6px; }
.go-grade {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 6px 2px;
  border-radius: 10px;
  background: #fffaf0;
  box-shadow: inset 0 0 0 2px var(--c);
}
.go-grade small { font-size: 10px; color: var(--c); filter: brightness(0.82); }
.go-grade b { font-size: 16px; color: #5c452e; line-height: 1.2; }
.go-grade i { font-style: normal; font-size: 10px; color: #8b775c; }
.go-grade.zero { opacity: 0.42; }

/* 용도별 지출 */
.go-spend { display: flex; flex-direction: column; gap: 6px; }
.go-spend-row {
  display: grid;
  grid-template-columns: 62px 1fr 74px;
  align-items: center;
  gap: 8px;
  font-size: 12px;
}
.go-spend-row small { color: #75634f; }
.go-spend-row > span { text-align: right; color: #5c452e; font-size: 13px; }

/* 누수 기록 */
.go-leaks { display: flex; flex-wrap: wrap; gap: 5px; }
.go-leak {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12px;
  color: #8a4a42;
  background: #fdece9;
  box-shadow: inset 0 0 0 1.5px #f0c9c4;
}
.go-leak b { font-size: 11px; color: #c1584c; }

@media (max-height: 720px) {
  .go-card h1 { font-size: 30px; }
  .go-sub { font-size: 14px; }
  .go-head { padding: 13px 20px 10px; }
  .go-body { padding: 12px 16px 14px; }
  .go-page { gap: 12px; }
}

/* 폰 세로. 한 줄에 [입력 + 등록 + 리더보드]를 밀어 넣으면 닉네임 칸이 손가락 두 개
   폭으로 쪼그라든다. 입력을 자기 줄로 올리고 버튼 둘이 아래 줄을 나눠 갖는다. */
@media (max-width: 430px) {
  .go-head { padding: 14px 16px 10px; }
  .go-rank, .go-share, .go-foot { padding-left: 16px; padding-right: 16px; }
  .go-rank-form { flex-wrap: wrap; }
  .go-nick, .go-rank-account { flex: 1 0 100%; }
  .go-rank-btn { flex: 1 1 0; min-width: 0; }
  .go-foot { flex-wrap: wrap; }
  .go-btn { flex: 1 1 40%; font-size: 16px; }
}

#loading {
  position: absolute; inset: 0;
  background: #2e4a21;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
}
.load-card { text-align: center; color: #eaf5d8; }
.load-card h2 { font-size: 44px; margin-bottom: 8px; text-shadow: 0 3px 0 rgba(0,0,0,0.4); }
.load-card p { opacity: 0.8; margin-bottom: 16px; }
.load-track {
  width: 320px; height: 14px;
  background: rgba(0,0,0,0.4);
  border-radius: 8px;
  overflow: hidden;
}
#loading-bar {
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, #a8e063, #56ab2f);
  transition: width 0.2s;
}

/* ---------- 공용 모달 ---------- */
#meta-modal, #codex-modal {
  position: fixed; inset: 0;
  background: rgba(20,12,8,0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--modal-pad);
  z-index: 20;
}
.modal-card {
  background: var(--paper);
  border-radius: 18px;
  padding: 20px;
  width: min(420px, 100%);
  max-height: 70vh;
  /* 모바일 사파리는 주소창이 접히고 펴지면서 100vh가 화면보다 커진다. dvh가 실제 보이는 높이다 */
  max-height: 70dvh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 10px 40px rgba(0,0,0,0.5);
}
.modal-card h3 { margin-bottom: 12px; font-size: 19px; }
.modal-card h3 small {
  font-family: var(--font-body);
  font-weight: 500;
  letter-spacing: 0;
  opacity: 0.6;
  font-size: 13px;
}
#meta-close, #codex-close, #lab-close, #forge-close, #account-close, #rank-close {
  margin-top: 12px;
  border: none;
  background: var(--wood);
  color: #fff;
  border-radius: 10px;
  padding: 9px;
  font-size: 15.5px;
  cursor: pointer;
}

/* ---------- 온라인 리더보드 ---------- */
/* 타이틀 화면(z-index 60) 위에서도 열리는 유일한 모달이라 층을 한 단 더 올린다 */
#rank-modal {
  position: fixed; inset: 0;
  background: rgba(20,12,8,0.68);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--modal-pad);
  z-index: 70;
}
.rank-wrap { width: min(480px, 100%); max-height: 78vh; max-height: 78dvh; }
.rank-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}
.rank-list { display: flex; flex-direction: column; gap: 3px; margin-top: 5px; }
/* 다섯 열 폭은 헤더 줄과 본문 줄이 공유한다. 점수 열은 두 줄(점수 / 웨이브·진행률)을
   담고, 무한 스테이지의 "W1000 · 100%"와 세 자리 분(分)까지 들어갈 폭을 잡는다 */
.rank-row {
  display: grid;
  grid-template-columns: 30px minmax(0, 1fr) 84px 44px 50px;
  align-items: center;
  gap: 6px;
  padding: 7px 8px;
  border-radius: 10px;
  background: #fff8e8;
  font-size: 13px;
  color: var(--ink);
}
.rank-row.is-head {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 0 8px 5px;
  border-radius: 0;
  border-bottom: 2px solid #ead9b7;
  background: var(--paper);
  font-family: var(--font-display);
  font-size: 11.5px;
  letter-spacing: var(--ls-display);
  color: #9a8666;
}
.rank-no { font-family: var(--font-display); text-align: center; color: #9a8666; }
.rank-no.is-top1 { color: #d99a15; }
.rank-no.is-top2 { color: #8fa0ae; }
.rank-no.is-top3 { color: #b5793f; }
/* 닉네임 + 차원 배지. 자리가 좁아지면 이름이 먼저 줄고 배지는 끝까지 남는다 */
.rank-who { min-width: 0; display: flex; align-items: center; gap: 5px; }
.rank-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 700; }
.rank-dim {
  flex: 0 0 auto;
  padding: 1.5px 6px;
  border-radius: 999px;
  border: 1px solid currentColor;
  font-size: 10px;
  line-height: 1.5;
  letter-spacing: 0.01em;
  white-space: nowrap;
  opacity: 0.9;
}
/* 차원별 색은 각 차원의 정체색에서 한 단계 낮춰 잡는다 — 줄의 주인공은 점수다 */
.rank-dim.is-d1 { color: #b5503f; background: #fdeee9; }
.rank-dim.is-d2 { color: #6f7480; background: #f0f1f3; }
.rank-dim.is-d3 { color: #4b6f96; background: #eaf1f8; }
.rank-dim.is-d4 { color: #6d4c9c; background: #f1ecfa; }
.rank-dim.is-d5 { color: #a07a17; background: #fbf3dd; }
/* 점수 열: 큰 수가 점수, 아래 작은 줄이 그 점수를 만든 웨이브 */
.rank-score {
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  line-height: 1.15;
  white-space: nowrap;
}
.rank-score b { font-family: var(--font-display); font-weight: 400; font-size: 14px; color: var(--ink); }
.rank-score small { margin-top: 1px; font-size: 10.5px; color: #9a8666; }
.rank-score small i { font-style: normal; margin-right: 2px; }
.rank-lives, .rank-time { white-space: nowrap; font-size: 12px; color: #8a7358; text-align: right; }
/* 100웨이브 정복 기록 — 왕관 줄은 금테로 따로 세운다 */
.rank-row.is-conquest {
  background: linear-gradient(#fffbeb, #fbeec6);
  box-shadow: inset 0 0 0 1.5px #e8c775;
}
/* 내 기록. 정복 여부와 무관하게 초록 테두리가 이긴다 */
.rank-row.is-me {
  background: linear-gradient(#ebfaf2, #d7f1e4);
  box-shadow: inset 0 0 0 2px #45ab7e;
}
.rank-row.is-conquest.is-me { background: linear-gradient(#fdf8e6, #ddf2e2); }
.rank-gap {
  padding: 5px 0 3px;
  text-align: center;
  letter-spacing: 4px;
  color: #bdab8c;
}
.rank-note {
  padding: 16px 6px;
  text-align: center;
  font-size: 13px;
  line-height: 1.65;
  color: #8b775c;
}
/* 점수 계산 안내. 목록 맨 아래에서 조용히 한 줄만 거든다 */
.rank-hint {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px dashed #ead9b7;
  text-align: center;
  font-size: 11.5px;
  line-height: 1.5;
  color: #a08d6f;
}
/* 폰 세로. 닉네임과 차원 배지가 한 칸을 나눠 쓰므로 나머지 열을 한 단계씩 줄여
   이름에 폭을 돌려준다 */
@media (max-width: 430px) {
  .rank-row { grid-template-columns: 24px minmax(0, 1fr) 74px 36px 42px; gap: 5px; padding: 7px 6px; }
  .rank-row.is-head { padding: 0 6px 5px; }
  .rank-who { gap: 4px; }
  .rank-dim { padding: 1px 5px; font-size: 9.5px; }
  .rank-score b { font-size: 13px; }
  .rank-score small { font-size: 10px; }
}

/* ---------- 영구 도전과제 ---------- */
.meta-wrap { width: min(500px, 100%); max-height: 78vh; max-height: 78dvh; }
#meta-summary {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin-bottom: 12px;
}
#meta-summary span {
  display: flex;
  flex-direction: column;
  align-items: center;
  border-radius: 10px;
  padding: 8px 4px;
  background: #fff8e8;
  box-shadow: inset 0 0 0 2px #ead9b7;
}
#meta-summary b { color: #7a4e24; font-size: 18px; }
#meta-summary small { color: #75634f; font-size: 10px; }
#meta-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 0;
  overflow-y: auto;
}
.meta-row {
  border: 2px solid #dfceb0;
  border-radius: 12px;
  padding: 8px 10px;
  background: #fffaf0;
}
.meta-row.done { border-color: #d8b64a; background: #fff7d8; }
.meta-head { display: flex; justify-content: space-between; align-items: center; }
.meta-head b { font-size: 14px; }
.meta-head span { color: #7d3fb0; font-size: 11px; font-weight: bold; }
.meta-row.done .meta-head span { color: #9b7416; }
.meta-row small { display: block; color: #75634f; font-size: 11px; }
.meta-row em { display: block; margin-top: 2px; color: #3d8b40; font-size: 11px; font-style: normal; }
.meta-progress {
  height: 5px;
  margin-top: 6px;
  border-radius: 999px;
  overflow: hidden;
  background: #e4d8c2;
}
.meta-progress i {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, #ab63d8, #ffd23f);
}

/* ---------- 도감 ----------
   발견한 칸만 색과 이름을 갖고, 미발견은 검은 실루엣 + '???'로 남는다.
   유물·변이 타일은 드래프트 카드가 쓰는 --rq-* / --mc- 등급 토큰을 그대로 물려받는다. */
.codex-wrap { width: min(620px, 100%); max-height: 82vh; max-height: 82dvh; }
/* 6카테고리. 좁은 화면에서는 두 줄로 접히되 칸 폭은 서로 같게 유지한다. */
.codex-tabs {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 6px;
  margin-bottom: 10px;
}
@media (max-width: 560px) {
  .codex-tabs { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.cx-tab {
  border: 2px solid #dfceb0;
  border-radius: 10px;
  padding: 7px 3px;
  background: #fffaf0;
  color: #8a7358;
  font-size: 13.5px;
  cursor: pointer;
  transition: background 0.12s, color 0.12s, border-color 0.12s, transform 0.12s;
}
.cx-tab:hover { border-color: #c2a475; transform: translateY(-1px); }
.cx-tab.on {
  border-color: #4c3454;
  background: linear-gradient(#8a6bb0, #5f4483);
  color: #fff;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.35), 0 2px 0 rgba(58,38,72,0.35);
}

.codex-rate {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  font-size: 11px;
  color: #75634f;
}
.codex-rate b { color: #7a4e24; font-family: var(--font-display); font-size: 14px; }
.cx-rate-bar {
  flex: 1;
  height: 5px;
  border-radius: 999px;
  background: #e4d8c2;
  overflow: hidden;
}
.cx-rate-bar span {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, #ab63d8, #ffd23f);
  transition: width 0.25s;
}

.codex-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding-right: 2px;
}

.cx-row {
  border: 2px solid #dfceb0;
  border-radius: 12px;
  background: #fffaf0;
  padding: 7px 8px 9px;
}
.cx-row-head { display: flex; align-items: baseline; gap: 6px; margin-bottom: 6px; }
.cx-row-head b { font-size: 13.5px; letter-spacing: 0.02em; }
.cx-row-head small { color: #9a836a; font-size: 10.5px; }
.cx-row-tally { margin-left: auto; color: #7d3fb0; font-size: 11px; }

.cx-cells { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 5px; }
.cx-grid { display: grid; gap: 7px; }
.cx-grid.mobs { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
.cx-grid.relics { grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); }

.cx-cell {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  border: 2px solid #d6c5a6;
  border-radius: 10px;
  padding: 3px 2px 4px;
  background: #e8dcc3;
  /* 미발견은 형태만 남긴 어두운 칸이다. 이름 자리는 등급 이름으로 채워 진행도를 보여 준다 */
  opacity: 0.6;
}
/* 캔버스 크기는 _codexCanvas가 인라인으로 준다(100% + max-width 원본 px + height auto) */
.cx-cell small { font-size: 9.5px; line-height: 1.2; text-align: center; color: #6b5a44; }
.cx-cell.found {
  opacity: 1;
  border-color: var(--c, #d6c5a6);
  background: linear-gradient(#fffdf6, #f4e7ca);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.8), 0 2px 0 rgba(90,64,30,0.16);
  cursor: help;
}
.cx-cell.found small { color: var(--c, #6b5a44); text-shadow: 0 1px 0 rgba(255,255,255,0.85); }
/* 초월 열은 등급 하나만 다른 게 아니라 완전 신규 디자인이라 열 자체를 프리즘 톤으로 세운다 */
.cx-cell.transcend { border-color: rgba(102,252,241,0.45); }
.cx-cell.transcend.found {
  border-color: #66fcf1;
  background: linear-gradient(#f4fffe, #d9f8f5);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.9), 0 0 12px rgba(102,252,241,0.5);
}

.cx-cell.wide { padding: 6px 4px 7px; }
.cx-cell.wide small { font-size: 11px; }
.cx-boss-tag {
  position: absolute;
  top: 4px; right: 4px;
  padding: 1px 5px;
  border-radius: 7px;
  background: linear-gradient(#ff7b90, #d92c4c);
  color: #fff;
  font-size: 9.5px;
  font-style: normal;
  letter-spacing: 0.02em;
  text-shadow: 0 1px 1px rgba(60,6,18,0.45);
}
.cx-cell.boss:not(.found) .cx-boss-tag { filter: grayscale(1); opacity: 0.7; }

.cx-relic {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  border: var(--rq-cbw, 2px) solid transparent;
  border-radius: 12px;
  padding: 7px 4px 8px;
  background:
    linear-gradient(var(--rq-top, #e8dcc3), var(--rq-bot, #dccdac)) padding-box,
    var(--rq-edge, linear-gradient(#c9b795, #c9b795)) border-box;
  box-shadow: var(--rq-bevel, 0 0 0 0 transparent), 0 2px 0 var(--rq-drop, rgba(60,40,15,0.18));
}
.cx-relic .relic-art { width: 34px; height: 34px; }
.cx-relic .relic-art.is-glyph { font-size: 19px; }
.cx-relic small {
  font-size: 10px;
  line-height: 1.25;
  text-align: center;
  color: var(--rq-ink, #6b5a44);
}
.cx-relic.found { cursor: help; }
/* 미발견 유물은 아이콘을 완전히 눌러 검은 실루엣으로만 남긴다 */
.cx-relic:not(.found) { opacity: 0.72; }
.cx-relic:not(.found) .relic-art { filter: grayscale(1) brightness(0.18); }
.cx-relic:not(.found) .relic-art.is-glyph { color: #33240f; filter: none; }

.cx-muts { display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 5px; }
.cx-mut {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
  border: 2px solid var(--mc-line, #d6c5a6);
  border-radius: 10px;
  padding: 5px 7px 5px 5px;
  background: linear-gradient(var(--mc-top, #e8dcc3), var(--mc-bot, #dccdac));
  opacity: 0.62;
}
.cx-mut.found { opacity: 1; cursor: help; }
.cx-mut b {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 11.5px;
  letter-spacing: 0.01em;
  color: var(--mc-ink, #6b5a44);
}
.cx-mut-grade {
  flex: 0 0 auto;
  width: 19px; height: 19px;
  display: grid;
  place-items: center;
  border-radius: 6px;
  font-style: normal;
  font-size: 11px;
  color: #fff;
  letter-spacing: 0.02em;
  background: linear-gradient(var(--mc-soft, #b4a184), var(--mc-key, #83704f));
  text-shadow: 0 1px 1px rgba(20,10,40,0.4);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.4);
}
.cx-mut.found.mut-s .cx-mut-grade {
  background: linear-gradient(150deg, #35d8ff, #8a5bff 42%, #ff6fd8 72%, #ffd84d);
}

/* 변형 타일 — 유물과 같은 자리 문법에 modifier 모달의 청록 재질을 입힌다 */
.cx-grid.mods { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
.cx-modx {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  border-radius: 11px;
  padding: 8px 5px 7px;
  background: #fffaf0;
  box-shadow: inset 0 0 0 2px #dfceb0;
}
.cx-modx-art {
  display: grid;
  place-items: center;
  width: 36px; height: 36px;
  border-radius: 50%;
  background: linear-gradient(#12666c, #073238);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.25), 0 1px 3px rgba(10,60,64,0.3);
}
.cx-modx-art .mod-art { width: 24px; height: 24px; }
.cx-modx-art .mod-art.is-glyph { font-size: 16px; color: #eafffe; }
.cx-modx small {
  font-size: 10px;
  line-height: 1.25;
  text-align: center;
  color: #6b5a44;
  letter-spacing: 0.01em;
}
.cx-modx.found {
  background: linear-gradient(#f3fffe, #e0f6f4);
  box-shadow: inset 0 0 0 2px #7fd6d2, 0 1px 0 rgba(14,90,94,0.16);
  cursor: help;
}
.cx-modx.found small { color: #0d5f66; }
.cx-modx:not(.found) .mod-art { filter: grayscale(1) brightness(0.2); }
.cx-modx:not(.found) .mod-art.is-glyph { color: #1d2a2b; filter: none; }
.cx-modx:not(.found) .cx-modx-art { background: linear-gradient(#3c4a4b, #1d2727); }

/* ---- 수집률 보상 줄 ----
   달성한 목표만 버튼이 살아난다. 잔액 칩을 같은 줄에 두어 수령한 결과가 바로 보인다. */
.codex-rewards {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-bottom: 10px;
  padding-bottom: 10px;
  border-bottom: 2px dashed #e6d7b8;
}
.cx-purse {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border-radius: 999px;
  padding: 4px 10px;
  background: linear-gradient(#e9f8e2, #cfeec2);
  box-shadow: inset 0 0 0 1.5px #96c98a;
  font-size: 12px;
  color: #2f6b34;
}
.cx-purse i { font-style: normal; font-size: 12px; }
.cx-purse b { font-family: var(--font-display); font-size: 14px; color: #26612c; }
.cx-reward {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: 10px;
  padding: 4px 5px 4px 8px;
  background: #fffaf0;
  box-shadow: inset 0 0 0 2px #e2d3b5;
}
.cx-reward-goal {
  font-family: var(--font-display);
  font-size: 12px;
  letter-spacing: var(--ls-display);
  color: #9a8666;
}
.cx-reward-gain { font-size: 12px; color: #7a4e24; }
.cx-reward-btn {
  border: none;
  border-radius: 8px;
  padding: 4px 9px;
  background: #ddd0b6;
  color: #7d6b52;
  font-family: var(--font-display);
  font-size: 12px;
  letter-spacing: var(--ls-display);
  cursor: default;
}
.cx-reward.is-ready { box-shadow: inset 0 0 0 2px #6fbf5f, 0 0 10px rgba(111,191,95,0.35); }
.cx-reward.is-ready .cx-reward-gain { color: #2f6b34; }
.cx-reward.is-ready .cx-reward-btn {
  background: linear-gradient(#7fd86b, #3f9c3c);
  color: #fff;
  cursor: pointer;
  box-shadow: 0 2px 0 #2c7a2b;
  text-shadow: 0 1px 1px rgba(12,48,14,0.4);
  animation: cxRewardPulse 1.6s ease-in-out infinite;
}
.cx-reward.is-ready .cx-reward-btn:hover { filter: brightness(1.08); }
.cx-reward.is-ready .cx-reward-btn:active { transform: translateY(2px); box-shadow: none; }
.cx-reward.is-done { opacity: 0.6; }
@keyframes cxRewardPulse {
  0%, 100% { box-shadow: 0 2px 0 #2c7a2b, 0 0 0 rgba(111,191,95,0); }
  50%      { box-shadow: 0 2px 0 #2c7a2b, 0 0 12px rgba(111,191,95,0.65); }
}

@media (prefers-reduced-motion: reduce) {
  .cx-tab, .cx-rate-bar span { transition: none; }
  .cx-reward.is-ready .cx-reward-btn { animation: none; }
}

/* ---------- 클로버 연구소 ----------
   도감·도전과제와 같은 양피지 카드지만 클로버 경제의 초록을 액센트로 쓴다.
   티어는 위에서 아래로 자라고, 잠긴 티어는 해금 조건만 남긴 채 물러난다. */
#lab-modal {
  position: fixed; inset: 0;
  background: radial-gradient(120% 100% at 50% 0%, rgba(24,64,32,0.6), rgba(14,20,12,0.78));
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--modal-pad);
  z-index: 20;
}
.lab-wrap {
  width: min(660px, 100%);
  max-height: 84vh;
  max-height: 84dvh;
  background: linear-gradient(180deg, #fbf7e6, var(--paper));
  box-shadow: 0 12px 42px rgba(10,30,14,0.55), inset 0 0 0 3px #8fce7f;
}
.lab-wrap h3 { color: #2f6b34; margin-bottom: 4px; }
.lab-wrap h3 small { color: #4b8b4f; opacity: 0.85; }

.lab-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 9px;
  padding-bottom: 9px;
  border-bottom: 2px dashed #dbe7cd;
}
.lab-purse {
  display: flex;
  align-items: baseline;
  gap: 5px;
  border-radius: 12px;
  padding: 6px 12px;
  background: linear-gradient(#e9f8e2, #cfeec2);
  box-shadow: inset 0 0 0 2px #96c98a;
  white-space: nowrap;
}
.lab-purse-ico { font-style: normal; font-size: 15px; }
.lab-purse b {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 21px;
  color: #26612c;
}
.lab-purse small { font-size: 10px; color: #4d7a4f; letter-spacing: 0.01em; }
.lab-idle {
  flex: 1;
  min-width: 0;
  font-size: 11.5px;
  line-height: 1.45;
  color: #6f7f68;
  letter-spacing: 0.01em;
}

/* 진행 중인 연구 하나. 남은 시간과 진행 막대만 1초마다 갱신된다(UI._labTick). */
.lab-active {
  margin-bottom: 9px;
  border-radius: 12px;
  padding: 9px 11px 10px;
  background: linear-gradient(#fff8e0, #f8ecc6);
  box-shadow: inset 0 0 0 2px #e0c473;
}
.lab-active-head { display: flex; align-items: baseline; gap: 7px; }
.lab-active-kicker {
  border-radius: 999px;
  padding: 2px 8px;
  background: linear-gradient(#ffc85e, #e09310);
  color: #4a2f04;
  font-family: var(--font-display);
  font-size: 10.5px;
  letter-spacing: 0.02em;
  text-shadow: 0 1px 0 rgba(255,240,200,0.5);
}
.lab-active-head b { font-family: var(--font-display); font-size: 15px; color: #7a5410; }
.lab-active-time {
  margin-left: auto;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: #9b7416;
}
.lab-active-bar {
  height: 6px;
  margin: 7px 0 5px;
  border-radius: 999px;
  background: #e8dcbc;
  overflow: hidden;
}
.lab-active-bar i {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, #8fce7f, #ffd23f);
  transition: width 0.9s linear;
}
.lab-active small { display: block; font-size: 11px; color: #7f6a44; letter-spacing: 0.01em; }

.lab-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-right: 2px;
}
.lab-tier-head {
  display: flex;
  align-items: baseline;
  gap: 7px;
  margin-bottom: 6px;
}
.lab-tier-head b {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 14px;
  color: #2f6b34;
}
.lab-tier-head small { font-size: 10.5px; color: #8a9682; letter-spacing: 0.01em; }
/* 잠긴 티어는 지우지 않는다 —— 무엇이 기다리는지 보여야 차원을 오를 이유가 된다 */
.lab-tier.is-locked .lab-tier-head b { color: #97a292; }
.lab-tier.is-locked .lab-tier-head small { color: #b08a62; }
.lab-tier.is-locked .lab-grid { opacity: 0.55; }

.lab-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 7px;
}
@media (max-width: 620px) {
  .lab-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.lab-node {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  border: 2px solid #dfceb0;
  border-radius: 12px;
  padding: 8px 9px 7px;
  background: #fffaf0;
  font-family: var(--font-body);
  text-align: left;
  cursor: pointer;
  transition: transform 0.12s, box-shadow 0.12s, border-color 0.12s;
}
.lab-name {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 13.5px;
  color: var(--ink);
}
.lab-desc {
  display: block;
  min-height: 26px;
  font-size: 10.5px;
  line-height: 1.35;
  color: #75634f;
  letter-spacing: 0.01em;
}
.lab-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  width: 100%;
  margin-top: 2px;
}
.lab-state { font-size: 10px; color: #8a7358; letter-spacing: 0.01em; }
.lab-cost {
  font-family: var(--font-display);
  font-style: normal;
  font-size: 12px;
  letter-spacing: var(--ls-display);
  color: #2f6b34;
  white-space: nowrap;
}

.lab-node.is-ready { border-color: #96c98a; box-shadow: 0 2px 0 rgba(47,107,52,0.16); }
.lab-node.is-ready:hover, .lab-node.is-ready:focus-visible {
  transform: translateY(-2px);
  border-color: #6fbf5f;
  box-shadow: 0 7px 14px rgba(30,84,36,0.2), 0 0 12px rgba(111,191,95,0.4);
  outline: none;
}
.lab-node.is-done {
  border-color: #8fce7f;
  background: linear-gradient(#f0fbea, #dcf1d2);
  cursor: default;
}
.lab-node.is-done .lab-state { color: #3d8b40; }
.lab-node.is-done .lab-name::after { content: ' ✓'; color: #3d8b40; }
.lab-node.is-active {
  border-color: #e0c473;
  background: linear-gradient(#fff8e0, #f7ebc4);
  cursor: default;
}
.lab-node.is-active .lab-state { color: #9b7416; font-variant-numeric: tabular-nums; }
.lab-node.is-idle { opacity: 0.66; cursor: not-allowed; }
.lab-node.is-idle .lab-cost { color: #a3512f; }
.lab-node.is-locked { opacity: 0.5; filter: grayscale(0.7); cursor: not-allowed; }
.lab-node.nope { animation: modNope 0.32s ease-out; }

@media (prefers-reduced-motion: reduce) {
  .lab-node, .lab-active-bar i { transition: none; }
  .lab-node.is-ready:hover { transform: none; }
  .lab-node.nope { animation: none; }
}

/* ---------- 직업 문장 연마 ----------
   연구소가 판 밖의 "설비"라면 연마는 판 안의 "손"이다. 그래서 클로버의 초록이 아니라
   모루·황동의 호박색을 쓰고, 카드마다 그 직업의 초상이 먼저 눈에 들어오게 세운다. */
#forge-modal {
  position: fixed; inset: 0;
  background: radial-gradient(120% 100% at 50% 0%, rgba(74,44,12,0.62), rgba(20,14,8,0.8));
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--modal-pad);
  z-index: 20;
}
.forge-wrap {
  width: min(680px, 100%);
  max-height: 84vh;
  max-height: 84dvh;
  background: linear-gradient(180deg, #fdf6e6, var(--paper));
  box-shadow: 0 12px 42px rgba(46,26,6,0.55), inset 0 0 0 3px #e0a03a;
}
.forge-wrap h3 { color: #8a5a12; margin-bottom: 4px; }
.forge-wrap h3 small { color: #a3752a; opacity: 0.85; }
.forge-note {
  margin-bottom: 10px;
  padding-bottom: 9px;
  border-bottom: 2px dashed #ecd9ae;
  font-size: 11.5px;
  line-height: 1.45;
  color: #8a7358;
  letter-spacing: 0.01em;
}
.forge-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding-right: 2px;
}
.forge-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}
@media (max-width: 620px) {
  .forge-grid { grid-template-columns: minmax(0, 1fr); }
}

.forge-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  border: 2px solid #e6d4b0;
  border-radius: 13px;
  padding: 9px 10px;
  background: #fffaf0;
}
/* 올릴 수 있는 카드만 테두리가 살아난다 —— 어디를 눌러야 하는지가 한눈에 갈린다 */
.forge-card.is-ready {
  border-color: #e0a03a;
  background: linear-gradient(#fffdf4, #fdf1d6);
  box-shadow: 0 2px 0 rgba(140,95,18,0.16);
}
.forge-card.is-max {
  border-color: #cbb98f;
  background: linear-gradient(#fdf8ec, #f2e7cd);
}
.forge-art { position: relative; display: block; flex: 0 0 auto; }
.forge-art canvas { display: block; }
.forge-lv {
  position: absolute;
  left: 50%;
  bottom: -3px;
  transform: translateX(-50%);
  border-radius: 999px;
  padding: 1px 7px;
  background: linear-gradient(#5a3f1c, #33230c);
  color: #ffe6b0;
  font-family: var(--font-display);
  font-style: normal;
  font-size: 10px;
  letter-spacing: 0.02em;
  white-space: nowrap;
  box-shadow: 0 1px 3px rgba(30,18,4,0.45);
}
.forge-card.is-max .forge-lv { background: linear-gradient(#e0a03a, #a3701a); color: #3d2504; }
.forge-info { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.forge-head { display: flex; align-items: baseline; gap: 5px; }
.forge-head b {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 14px;
}
.forge-head small { font-size: 10px; color: #9a836a; letter-spacing: 0.01em; }
.forge-rank {
  margin-left: auto;
  font-family: var(--font-display);
  font-size: 11px;
  letter-spacing: var(--ls-display);
  color: #a3752a;
}
.forge-bar {
  position: relative;
  height: 5px;
  border-radius: 999px;
  background: #e8dcc2;
}
.forge-bar > i {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, #e0a03a, #ffd23f);
  transition: width 0.25s;
}
/* 구간 눈금 — 지나온 구간은 게이지 위에서 밝게, 남은 구간은 홈처럼 파여 보이게 */
.forge-bar > i.forge-tick {
  position: absolute;
  top: -2px;
  width: 2px;
  height: 9px;
  margin-left: -1px;
  border-radius: 1px;
  background: #c3b291;
  transition: background 0.25s;
}
.forge-bar > i.forge-tick.is-on { background: #fff3cd; box-shadow: 0 0 3px rgba(224,160,58,0.9); }
/* 축이 셋으로 늘면 좁은 카드에서 줄이 넘어간다. keep-all이 없으면 "사거 / 리"로 쪼개진다. */
.forge-stat {
  font-size: 10.5px;
  color: #7a4e24;
  letter-spacing: 0.01em;
  word-break: keep-all;
  line-height: 1.4;
}
.forge-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 2px 8px;
}
.forge-purse { font-size: 10px; color: #9a8666; letter-spacing: 0.01em; }
.forge-next {
  font-size: 10px;
  color: #a8762a;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.forge-card.is-max .forge-bar > i.forge-tick { background: #e8c98a; box-shadow: none; }

/* 카드 안 구간 보너스 칩 —— 잠긴 구간은 바래고, 도달한 구간만 색이 든다.
   능력 강화 칩(is-ability)은 공용 스탯 칩과 톤을 갈라 "이 직업만의 것"으로 읽히게 한다. */
.forge-ms {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  margin-top: 1px;
}
.forge-ms-chip {
  font-style: normal;
  font-size: 9.5px;
  line-height: 1.35;
  letter-spacing: 0.01em;
  word-break: keep-all;
  color: #a89880;
  background: #f4eede;
  border: 1px solid #e6dcc2;
  border-radius: 999px;
  padding: 2px 7px;
}
.forge-ms-chip b {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  color: inherit;
  opacity: 0.85;
  margin-right: 4px;
}
.forge-ms-chip.is-on {
  color: #8a6a30;
  background: #fbf1d9;
  border-color: #e0c283;
}
.forge-ms-chip.is-on b { color: #a8762a; opacity: 1; }
.forge-ms-chip.is-ability.is-on {
  color: #5f4a86;
  background: #f1ecfb;
  border-color: #cdbfe8;
}
.forge-ms-chip.is-ability.is-on b { color: #7558b8; }

.forge-up {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  flex: 0 0 auto;
  border: none;
  border-radius: 11px;
  padding: 8px 12px;
  background: linear-gradient(#f0b64a, #c07d14);
  box-shadow: 0 3px 0 #8f5c0d, inset 0 1px 0 rgba(255,255,255,0.35);
  color: #fff7e4;
  cursor: pointer;
  text-shadow: 0 1px 2px rgba(80,48,4,0.45);
  transition: filter 0.12s, transform 0.08s;
}
.forge-up b {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 14px;
}
.forge-up span { font-size: 10.5px; opacity: 0.9; }
.forge-up:hover:not(:disabled) { filter: brightness(1.08); }
.forge-up:active:not(:disabled) { transform: translateY(3px); box-shadow: none; }
.forge-up:disabled {
  background: linear-gradient(#d8cbb0, #b0a184);
  box-shadow: 0 3px 0 #8d8069;
  color: #f4efe4;
  cursor: not-allowed;
  filter: grayscale(0.4);
}
@media (prefers-reduced-motion: reduce) {
  .forge-up, .forge-bar i { transition: none; }
  .forge-up:active:not(:disabled) { transform: none; }
}

/* ---------- 사이드바 메타 진입 버튼(연구소·연마) ----------
   판 밖에서만 자라는 재화라 잔액을 버튼 자신이 들고 있다.
   #mobile-actions로 옮기지 않으므로 서랍/시트 안에서만 보인다. */
.lab-entry { display: flex; flex-direction: column; gap: 6px; }
.lab-btn {
  position: relative;
  flex: 1;
  display: flex;
  align-items: center;
  gap: 9px;
  border: none;
  border-radius: 12px;
  padding: 8px 12px;
  background: linear-gradient(#4d7a3f, #2f5c2c);
  box-shadow: 0 3px 0 rgba(20,48,20,0.45), inset 0 1px 0 rgba(255,255,255,0.18);
  color: #f2fbe9;
  cursor: pointer;
  text-align: left;
  transition: filter 0.12s, transform 0.08s;
}
.lab-btn:hover { filter: brightness(1.08); }
.lab-btn:active { transform: translateY(2px); box-shadow: 0 1px 0 rgba(20,48,20,0.45); }
.lab-btn-ico { font-size: 21px; line-height: 1; filter: drop-shadow(0 1px 2px rgba(8,30,10,0.5)); }
.lab-btn-txt { display: flex; flex-direction: column; min-width: 0; }
.lab-btn-txt b {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 15px;
}
.lab-btn-txt small { font-size: 11px; color: #c9e6b8; letter-spacing: 0.01em; }
.lab-btn-badge {
  margin-left: auto;
  flex: 0 0 auto;
  border-radius: 999px;
  padding: 3px 8px;
  font-family: var(--font-body);
  font-style: normal;
  font-size: 10.5px;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.lab-btn-badge.is-busy {
  background: rgba(255,210,63,0.18);
  box-shadow: inset 0 0 0 1.5px rgba(255,210,63,0.6);
  color: #ffe28a;
}
.lab-btn-badge.is-ready {
  background: linear-gradient(#a8e88f, #5aa84c);
  color: #123312;
  box-shadow: 0 0 10px rgba(120,220,110,0.45);
  animation: labReadyBreath 1.9s ease-in-out infinite;
}
@keyframes labReadyBreath {
  0%, 100% { box-shadow: 0 0 6px rgba(120,220,110,0.3); }
  50%      { box-shadow: 0 0 14px rgba(140,240,120,0.7); }
}
/* 연마 버튼은 같은 뼈대에 모루의 호박색만 갈아입는다 */
.lab-btn.forge-btn {
  background: linear-gradient(#8a6224, #5a3d13);
  box-shadow: 0 3px 0 rgba(52,34,8,0.45), inset 0 1px 0 rgba(255,255,255,0.18);
  color: #fdf1dc;
}
.lab-btn.forge-btn:active { box-shadow: 0 1px 0 rgba(52,34,8,0.45); }
.lab-btn.forge-btn .lab-btn-txt small { color: #e8cca0; }
.lab-btn.forge-btn .lab-btn-badge.is-ready {
  background: linear-gradient(#ffd76a, #d9911f);
  color: #3d2504;
  box-shadow: 0 0 10px rgba(240,190,80,0.45);
  animation: forgeReadyBreath 1.9s ease-in-out infinite;
}
@keyframes forgeReadyBreath {
  0%, 100% { box-shadow: 0 0 6px rgba(240,190,80,0.3); }
  50%      { box-shadow: 0 0 14px rgba(255,210,100,0.7); }
}
@media (prefers-reduced-motion: reduce) {
  .lab-btn.forge-btn .lab-btn-badge.is-ready { animation: none; }
}

/* ---------- 계정 · 클라우드 동기화 (AC-U) ----------
   연구소는 초록, 연마는 호박. 계정은 재화가 아니라 재화를 지키는 자리라 하늘빛으로 떨어뜨려
   같은 줄에 있어도 "성장"과 "보관"이 구분되게 한다. */
.lab-btn.account-btn {
  background: linear-gradient(#3f5f86, #27405e);
  box-shadow: 0 3px 0 rgba(14,26,42,0.45), inset 0 1px 0 rgba(255,255,255,0.18);
  color: #eaf3ff;
}
.lab-btn.account-btn:active { box-shadow: 0 1px 0 rgba(14,26,42,0.45); }
.lab-btn.account-btn .lab-btn-txt small { color: #bcd4ef; }
.lab-btn.account-btn.is-authed {
  background: linear-gradient(#3d6d7a, #245057);
  box-shadow: 0 3px 0 rgba(12,36,38,0.45), inset 0 1px 0 rgba(255,255,255,0.2);
}
/* 「온라인 전용」은 알림이 아니라 사유 표시다 —— 부제를 더 낮춰 눌러 봐야 소용없음을 알린다. */
.lab-btn.account-btn.is-offline { filter: saturate(0.55) brightness(0.92); }
.lab-btn.account-btn.is-offline .lab-btn-txt small { color: #9fb2c6; }

#account-modal {
  position: fixed; inset: 0;
  background: radial-gradient(120% 100% at 50% 0%, rgba(22,44,72,0.62), rgba(10,16,24,0.8));
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--modal-pad);
  z-index: 70; /* 타이틀(60)·게임오버(18) 양쪽에서 열린다 */
}
.account-wrap {
  width: min(420px, 100%);
  max-height: 88vh;
  max-height: 88dvh;
  display: flex;
  flex-direction: column;
  background: linear-gradient(180deg, #f7fafe, var(--paper));
  box-shadow: 0 12px 42px rgba(8,22,40,0.55), inset 0 0 0 3px #7fa8ce;
}
.account-wrap h3 { color: #2c4d75; margin-bottom: 6px; }
.account-lead {
  font-size: 11.5px;
  line-height: 1.5;
  color: #6d7f93;
  word-break: keep-all;
  margin-bottom: 10px;
}
.account-lead b {
  display: block;
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 13px;
  color: #2c4d75;
  margin-bottom: 3px;
}

.account-tabs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  padding: 3px;
  border-radius: 12px;
  background: #e3ecf6;
  margin-bottom: 10px;
}
.account-tab {
  border: none;
  border-radius: 9px;
  padding: 8px 4px;
  min-height: 38px;
  background: transparent;
  color: #6d829a;
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 13px;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.account-tab.is-on {
  background: #fff;
  color: #2c4d75;
  box-shadow: 0 1px 3px rgba(30,60,96,0.18);
}

.account-body { flex: 1; min-height: 0; overflow-y: auto; }
.account-pane { display: flex; flex-direction: column; gap: 9px; }
.account-field { display: flex; flex-direction: column; gap: 4px; }
.account-field > span {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 12px;
  color: #46617f;
}
.account-field input {
  border: 2px solid #cfdcea;
  border-radius: 10px;
  padding: 9px 11px;
  min-height: 42px;
  font: inherit;
  font-size: 14px;
  color: #23364a;
  background: #fff;
}
.account-field input:focus {
  outline: none;
  border-color: #6fa0d0;
  box-shadow: 0 0 0 3px rgba(111,160,208,0.24);
}
.account-field small { font-size: 10px; color: #8698ab; letter-spacing: 0.01em; }
.account-field small.account-warn { color: #b26a2c; }

.account-link {
  align-self: flex-start;
  border: none;
  background: none;
  padding: 4px 0;
  min-height: 32px;
  font: inherit;
  font-size: 11px;
  color: #4a7fb5;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  word-break: keep-all;
  text-align: left;
}
.account-submit {
  border: none;
  border-radius: 12px;
  padding: 11px 14px;
  min-height: 46px;
  background: linear-gradient(#5b93cc, #35618f);
  box-shadow: 0 3px 0 #24466a, inset 0 1px 0 rgba(255,255,255,0.28);
  color: #fff;
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 14px;
  cursor: pointer;
}
.account-submit:active { transform: translateY(2px); box-shadow: 0 1px 0 #24466a; }
.account-submit:disabled { filter: grayscale(0.6) brightness(0.9); cursor: default; transform: none; }
.account-submit.ghost {
  background: linear-gradient(#f2f6fb, #dfe8f2);
  box-shadow: 0 3px 0 #bcccdd, inset 0 1px 0 #fff;
  color: #3f6285;
}

/* 공용 닫기 버튼은 갈색 큰 덩어리라, 바로 위의 파란 주 동작과 무게가 같아진다.
   계정 화면에서만 낮춰 「로그인/회원가입」이 유일한 주 동작으로 읽히게 한다. */
#account-close {
  background: none;
  box-shadow: none;
  color: #7a8da0;
  font-size: 12.5px;
}
#account-close:active { transform: none; }

.account-error {
  margin-top: 9px;
  border-radius: 10px;
  padding: 8px 10px;
  background: #fdecec;
  border: 1px solid #f2c4c4;
  color: #a8382f;
  font-size: 11.5px;
  line-height: 1.45;
  word-break: keep-all;
}

.account-signed { display: flex; flex-direction: column; gap: 8px; }
.account-signed-who {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 15px;
  color: #245057;
}
.account-signed-when { font-size: 11px; color: #7d8fa1; }
.account-signed-acts { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; margin-top: 4px; }

/* 복구 코드 —— 화면에서 가장 중요한 것이 코드 그 자체라 나머지를 전부 낮춘다. */
#recovery-modal, #sync-modal {
  position: fixed; inset: 0;
  background: rgba(12,18,26,0.82);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--modal-pad);
  z-index: 74; /* 계정 모달(70) 위 */
}
.recovery-wrap, .sync-wrap {
  width: min(420px, 100%);
  background: linear-gradient(180deg, #fffdf5, var(--paper));
  box-shadow: 0 14px 46px rgba(8,14,22,0.6), inset 0 0 0 3px #d9b45e;
}
.recovery-wrap h3, .sync-wrap h3 {
  color: #7a5410;
  margin-bottom: 6px;
  word-break: keep-all;
  line-height: 1.35;
}
.recovery-note, .sync-note {
  font-size: 11.5px;
  line-height: 1.5;
  color: #8a7358;
  word-break: keep-all;
  margin-bottom: 10px;
}
.recovery-lead {
  border-radius: 10px;
  padding: 8px 10px;
  margin-bottom: 9px;
  background: #eef7ea;
  border: 1px solid #c8e0bd;
  color: #3f6b33;
  font-size: 11.5px;
  line-height: 1.45;
  word-break: keep-all;
}
.recovery-code {
  display: block;
  border-radius: 12px;
  padding: 14px 10px;
  margin-bottom: 12px;
  background: #2f2415;
  color: #ffe6a8;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: clamp(15px, 5.2vw, 21px);
  letter-spacing: 0.12em;
  text-align: center;
  word-break: break-all;
  user-select: all;
  -webkit-user-select: all;
}
.recovery-acts, .sync-acts { display: grid; gap: 8px; }
.recovery-acts { grid-template-columns: 1fr 1.4fr; }

.sync-choice {
  display: flex;
  flex-direction: column;
  gap: 3px;
  align-items: flex-start;
  border: 2px solid #e2cf9f;
  border-radius: 13px;
  padding: 12px 13px;
  min-height: 62px;
  background: linear-gradient(#fffdf3, #f6edd6);
  color: #6b4a12;
  cursor: pointer;
  text-align: left;
  transition: border-color 0.15s, transform 0.1s;
}
.sync-choice:hover { border-color: #c9a94f; }
.sync-choice:active { transform: translateY(1px); }
.sync-choice b {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 14px;
  color: #5a3d0c;
}
.sync-choice small { font-size: 11px; color: #92764a; letter-spacing: 0.01em; }

/* 게임오버 안내 —— 클로버·문장 행 바로 아래에 붙는다. */
.go-account {
  border-radius: 13px;
  padding: 12px 13px;
  margin-bottom: 10px;
  background: linear-gradient(#eef5fd, #dfeaf7);
  border: 2px solid #bed4ea;
}
.go-account-head {
  display: block;
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 14px;
  color: #2c4d75;
  word-break: keep-all;
  line-height: 1.35;
}
.go-account-body {
  margin-top: 4px;
  font-size: 11.5px;
  line-height: 1.5;
  color: #5c718a;
  word-break: keep-all;
}
.go-account-acts { display: flex; gap: 8px; margin-top: 10px; }
.go-account-cta {
  flex: 1;
  border: none;
  border-radius: 11px;
  padding: 10px 12px;
  min-height: 44px;
  background: linear-gradient(#5b93cc, #35618f);
  box-shadow: 0 3px 0 #24466a, inset 0 1px 0 rgba(255,255,255,0.28);
  color: #fff;
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 13.5px;
  cursor: pointer;
  word-break: keep-all;
}
.go-account-cta:active { transform: translateY(2px); box-shadow: 0 1px 0 #24466a; }
.go-account-later {
  flex: 0 0 auto;
  border: none;
  border-radius: 11px;
  padding: 10px 14px;
  min-height: 44px;
  background: rgba(255,255,255,0.7);
  color: #6d829a;
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
}
.go-account-saved {
  border-radius: 11px;
  padding: 9px 12px;
  margin-bottom: 10px;
  background: #e9f4f2;
  border: 1px solid #bcdcd6;
  color: #2f6058;
  font-size: 12px;
  letter-spacing: 0.01em;
  word-break: keep-all;
}
@media (prefers-reduced-motion: reduce) {
  .lab-btn { transition: none; }
  .lab-btn:active { transform: none; }
  .lab-btn-badge.is-ready { animation: none; }
}

/* ---------- HUD 차원 배지 ----------
   차원 1 이상으로 시작한 판에서만 붙는다. 다른 HUD 칩과 같은 규칙으로 접히고 흐른다.
   차원에 들어서면 배경·BGM·몹 색이 전부 바뀐다. 배지도 같은 팔레트를 입어야 "지금 어느
   차원인가"가 숫자가 아니라 색으로 먼저 읽힌다 — 색 출처는 CFG.ascension.dims[].visual의
   flies/ray/vignette다. 차원별 규칙은 --dimb-* 여섯 변수만 갈아끼우고, 앞의 모티프는
   .dimb-mark::before 한 장이 모양(초승달·안개·번개·심연·광휘)을 바꾼다.
   게임 화면 위에 얹히는 칩이라 애니메이션은 두지 않는다. 정지한 미세 글로우까지만. */
.hud-item.dim-badge {
  gap: 7px;
  /* "황금빛 종언" 같은 긴 이름도 절대 꺾이지 않는다. 꺾이면 배지만 두 줄 높이로 부풀어
     HUD 줄 전체가 밀린다. 대신 줄어들지도 않게 해 다른 칩이 이름을 파고들지 못하게 한다. */
  white-space: nowrap;
  flex: 0 0 auto;
  /* 다른 HUD 칩과 같은 키. 배지 글자는 다른 칩(20px)보다 작아서 내용만으로는 상자가
     4px 낮게 서고, 나란히 놓으면 배지만 눌린 것처럼 보인다. box-sizing이 border-box라
     세로 여백 10px(5px * 2)까지 더해야 .hud-item과 같은 36px가 된다. em은 .hud-item의
     20px 기준이라 본문 크기가 바뀌면 함께 따라온다. */
  min-height: calc(1.3em + 10px);
  /* 값 없는 판(알 수 없는 차원)에서도 읽히도록 기본은 차원 1 팔레트다 */
  --dimb-top: rgba(122,26,26,0.55);
  --dimb-bot: rgba(58,10,14,0.62);
  --dimb-line: rgba(255,138,99,0.5);
  --dimb-glow: rgba(255,95,74,0.4);
  --dimb-num: #ffdcc6;
  --dimb-name: #ff9d7e;
  background: linear-gradient(var(--dimb-top), var(--dimb-bot));
  box-shadow: inset 0 0 0 1.5px var(--dimb-line), 0 0 12px -5px var(--dimb-glow);
}
/* 모티프는 껍데기(.dimb-mark)와 그림(::before) 두 겹이다. 마스크·클립은 그림에만 걸고
   글로우는 껍데기의 filter로 준다 — 한 요소에 같이 걸면 글로우까지 잘려 나간다. */
.dimb-mark {
  position: relative;
  flex: 0 0 auto;
  width: 11px; height: 11px;
  /* 상자 가운데(align-items: center)는 글자의 광학 중앙보다 0.5px쯤 위다 —— 줄 상자 중앙은
     밑선 위 (ascent - descent) / 2, 글자가 실제로 차 보이는 중앙은 x-height의 절반이라
     둘이 어긋난다. 모티프는 도형이라 이 차이가 바로 보이므로 그만큼만 내려 앉힌다.
     position: relative의 top이라 레이아웃 폭·높이에는 영향이 없다. */
  top: 0.5px;
}
.dimb-mark::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: radial-gradient(circle at 32% 30%, #ffd9e0, #ff4d6d 62%, #a3122f);
}
/* "차원 1 붉은 달"은 세 조각이 아니라 라벨 하나다. 예전에는 접두어 11px · 숫자 17px ·
   이름 13px으로 한 라벨 안에서 크기가 1.5배까지 벌어져 숫자만 튀어 나왔다(실제 지적).
   지금은 12 / 15 / 13.5px —— 가장 큰 곳과 작은 곳의 차이가 1.25배 안이라 한 덩어리로
   읽히고, 숫자의 강조는 크기가 아니라 디스플레이 서체와 밝은 색이 맡는다.
   크기를 맞추면 밑선 줄맞춤의 부수 효과도 따라온다: 줄 상자를 어느 한쪽이 독점하지 않아
   글자 덩어리의 중앙이 곧 왼쪽 모티프가 맞춰야 할 높이가 된다. */
.dimb-txt { display: inline-flex; align-items: baseline; gap: 5px; flex: 0 0 auto; white-space: nowrap; }
.dimb-txt b {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 15px;
  color: var(--dimb-num);
}
.dimb-txt b::before { content: '차원 '; font-size: 12px; opacity: 0.72; }
/* 이름은 배지에서 가장 먼저 양보하는 자리다(폭 예산 사다리 2·3단). 말줄임으로 줄어드는
   동안에도 숫자·모티프·색은 그대로라 "지금 몇 차원인가"는 끝까지 읽힌다. */
#hud-dim-name {
  /* 웨이브 칩 카운트다운과 같은 급. HUD 보조 텍스트는 어느 칩에 있든 한 크기다. */
  font-size: var(--hud-sub);
  color: var(--dimb-name);
  letter-spacing: 0.01em;
  white-space: nowrap;
  min-width: 0;
  max-width: var(--hud-dimname-max, none);
}
@media (max-width: 820px) {
  #hud-dim-name { display: none; }
}

/* 차원 1 붉은 달 — 달 자체를 모티프로. 배경이 반투명이라 색으로 덮어 초승달을 만들 수
   없어서, 오른쪽을 마스크로 베어 낸다. */
.dim-badge.is-d1 .dimb-mark { filter: drop-shadow(0 0 3px rgba(255,120,80,0.7)); }
.dim-badge.is-d1 .dimb-mark::before {
  background: radial-gradient(circle at 30% 28%, #ffd489, #ff6a45 58%, #a3122f);
  -webkit-mask-image: radial-gradient(circle at 126% 50%, transparent 60%, #000 62%);
  mask-image: radial-gradient(circle at 126% 50%, transparent 60%, #000 62%);
}

/* 차원 2 잿빛 안개 — 색을 뽑아낸 회백. 가로로 깔린 세 겹 띠에 미세한 블러를 얹어
   덩어리가 아니라 흘러가는 안개로 읽히게 한다. */
.dim-badge.is-d2 {
  --dimb-top: rgba(104,112,116,0.5);
  --dimb-bot: rgba(48,54,58,0.62);
  --dimb-line: rgba(226,234,236,0.42);
  --dimb-glow: rgba(230,238,240,0.34);
  --dimb-num: #eef3f4;
  --dimb-name: #c3cbce;
}
.dim-badge.is-d2 .dimb-mark { filter: blur(0.35px); }
.dim-badge.is-d2 .dimb-mark::before {
  border-radius: 3px;
  background:
    linear-gradient(90deg, rgba(244,246,247,0), #f4f6f7 34%, #f4f6f7 70%, rgba(244,246,247,0)) 0 1.4px / 100% 1.7px no-repeat,
    linear-gradient(90deg, rgba(179,187,192,0), #b3bbc0 20%, #e6eaec 64%, rgba(230,234,236,0)) 0 4.6px / 100% 1.7px no-repeat,
    linear-gradient(90deg, rgba(154,162,166,0), #9aa2a6 40%, #cfd6d9 84%, rgba(207,214,217,0)) 0 7.8px / 100% 1.7px no-repeat;
}

/* 차원 3 강철 뇌운 — 폭풍 직전의 청강색. 번개 한 줄기를 클립으로 오린다. */
.dim-badge.is-d3 {
  --dimb-top: rgba(44,72,106,0.55);
  --dimb-bot: rgba(16,30,48,0.66);
  --dimb-line: rgba(166,220,255,0.5);
  --dimb-glow: rgba(166,220,255,0.45);
  --dimb-num: #dcefff;
  --dimb-name: #9fc9ea;
}
.dim-badge.is-d3 .dimb-mark { filter: drop-shadow(0 0 3px rgba(140,200,255,0.75)); }
.dim-badge.is-d3 .dimb-mark::before {
  border-radius: 0;
  background: linear-gradient(160deg, #f2fbff, #a6dcff 45%, #6ea9dc);
  clip-path: polygon(58% 0%, 16% 56%, 44% 56%, 34% 100%, 84% 40%, 54% 40%);
}

/* 차원 4 칠흑의 심연 — 가운데가 뚫린 암보라 고리. 안쪽은 빛이 없고 바깥으로만
   보랏빛 잔광이 번진다. */
.dim-badge.is-d4 {
  --dimb-top: rgba(62,36,98,0.58);
  --dimb-bot: rgba(18,9,34,0.7);
  --dimb-line: rgba(193,148,255,0.46);
  --dimb-glow: rgba(143,92,255,0.5);
  --dimb-num: #e6d5ff;
  --dimb-name: #b795e8;
}
.dim-badge.is-d4 .dimb-mark::before {
  background: radial-gradient(circle at 50% 50%,
    #08040f 0 30%,
    #6f3fd6 34%, #c194ff 46%,
    rgba(143,92,255,0.3) 58%,
    rgba(143,92,255,0) 80%);
}

/* 차원 5 황금빛 종언 — 마지막 차원만 금빛으로 빛난다. 네 갈래 광휘. */
.dim-badge.is-d5 {
  --dimb-top: rgba(126,88,26,0.55);
  --dimb-bot: rgba(58,36,6,0.66);
  --dimb-line: rgba(255,224,138,0.5);
  --dimb-glow: rgba(255,206,110,0.55);
  --dimb-num: #ffeeba;
  --dimb-name: #e8c073;
}
.dim-badge.is-d5 .dimb-mark { filter: drop-shadow(0 0 4px rgba(255,196,90,0.7)); }
.dim-badge.is-d5 .dimb-mark::before {
  border-radius: 0;
  background: radial-gradient(circle at 50% 50%, #fff6cf, #ffe08a 40%, #ffb347 78%);
  clip-path: polygon(50% 0%, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0% 50%, 40% 40%);
}

/* ---------- 보스 유물 드래프트 ---------- */
#draft-modal {
  position: fixed; inset: 0;
  background: rgba(20,12,8,0.65);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--modal-pad);
  z-index: 25;
}
/* 별의 은총으로 후보가 4장이 되면 폭을 한 단계 넓힌다. 부모가 안전 영역 + 16px를 이미
   먹었으므로 카드는 남은 폭(100%)만 채우면 된다. */
.draft-wrap { width: min(520px, 100%); }
.draft-wrap.four { width: min(680px, 100%); }
#draft-list { display: flex; gap: 10px; }
.draft-wrap.four #draft-list { gap: 8px; }
.draft-wrap.four .draft-card { padding: 13px 7px; }
.draft-wrap.four .draft-card small { font-size: 11.5px; }
@media (max-width: 720px) {
  .draft-wrap.four #draft-list { flex-wrap: wrap; }
  .draft-wrap.four .draft-card { flex: 1 1 44%; }
}
/* 폰 가로처럼 낮은 화면에서는 카드 3장이 잘리지 않게 모달 높이를 풀고 안에서 스크롤한다 */
@media (max-height: 560px) {
  .draft-wrap { max-height: 92dvh; padding: 13px; }
  #draft-list { overflow-y: auto; }
}

/* 카드 스태거 진입 — 유물 3·4택과 변이 드래프트가 같은 리듬을 쓴다.
   fill-mode는 backwards다: 지연 구간만 시작 상태를 잡고 끝난 뒤에는 아무것도 남기지 않아
   :hover / :active의 transform이 그대로 살아난다. 순번 --i는 카드 생성 시 인라인으로 붙는다.
   opacity/transform만 건드리므로 재생 중에도 클릭은 즉시 받는다. */
#draft-list .draft-card,
#mutation-list .mut-card {
  animation: draftCardIn 0.26s cubic-bezier(0.23, 1, 0.32, 1) backwards;
  animation-delay: calc(var(--i, 0) * 0.055s);
}
/* diamond·S는 테두리 프리즘 회전(rqPrism)을 갖고 있다. animation은 통째로 덮이는
   속성이라 여기서 두 애니메이션을 함께 선언해야 회전이 살아남는다(#ui-tip과 같은 처리). */
#draft-list .draft-card.is-diamond,
#mutation-list .mut-card.mut-s {
  animation:
    draftCardIn 0.26s cubic-bezier(0.23, 1, 0.32, 1) backwards,
    rqPrism 6s linear infinite;
  animation-delay: calc(var(--i, 0) * 0.055s), 0s;
}
@keyframes draftCardIn {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  #draft-list .draft-card,
  #mutation-list .mut-card { animation-name: draftCardFadeIn; }
  /* 회전은 멈추고 페이드만 남긴다 */
  #draft-list .draft-card.is-diamond,
  #mutation-list .mut-card.mut-s {
    animation: draftCardFadeIn 0.26s ease-out backwards;
    animation-delay: calc(var(--i, 0) * 0.055s);
  }
  @keyframes draftCardFadeIn { from { opacity: 0; } to { opacity: 1; } }
}
/* 유물 카드의 색은 전부 아래 '성능 등급 4단계' 절의 --rq-* 토큰에서 온다 */
.draft-card {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  /* 테두리는 폭(--rq-bw)과 재질(--rq-edge)이 둘 다 등급을 따라 바뀐다.
     padding-box/border-box 2단 배경이라 테두리 자체가 그라데이션을 받을 수 있다. */
  border: var(--rq-bw, 2px) solid transparent;
  background:
    linear-gradient(var(--rq-top, #fff8e8), var(--rq-bot, #f7e9c8)) padding-box,
    var(--rq-edge, linear-gradient(#d8b64a, #d8b64a)) border-box;
  border-radius: 14px;
  padding: 14px 10px;
  font-family: var(--font-body);
  cursor: pointer;
  overflow: hidden;
  /* 광택·반짝임·테두리 광원의 z-index를 카드 안으로 가둔다 */
  isolation: isolate;
  box-shadow:
    var(--rq-bevel, 0 0 0 0 transparent),
    0 3px 0 var(--rq-drop, rgba(0,0,0,0.18)),
    0 0 0 var(--rq-rw, 0px) var(--rq-ring, transparent),
    0 0 var(--rq-halo, 0px) var(--rq-glow, transparent);
  transition: transform 0.13s cubic-bezier(0.23, 1, 0.32, 1), box-shadow 0.12s;
}
.draft-card:hover {
  transform: translateY(-4px);
  box-shadow:
    var(--rq-bevel, 0 0 0 0 transparent),
    0 9px 20px var(--rq-drop, rgba(0,0,0,0.28)),
    0 0 0 var(--rq-rw, 0px) var(--rq-key, transparent),
    0 0 var(--rq-halo-hi, 12px) var(--rq-glow, rgba(255,176,46,0.35));
}
/* 눌림 피드백. hover 뒤에 와야 눌린 동안 뜬 상태를 덮는다.
   hover를 끄는 터치 기기 블록(위쪽 미디어 쿼리)보다도 뒤라 탭에서도 그대로 산다. */
.draft-card:active { transform: scale(0.97); }
/* 광택·반짝임 레이어(z 0·1) 위로 본문을 띄운다 */
.draft-card > .draft-icon,
.draft-card > b,
.draft-card > small { position: relative; z-index: 2; }
.draft-icon {
  position: relative;
  display: grid;
  place-items: center;
  width: 54px;
  height: 54px;
  /* 원반 테두리도 카드와 같은 재질을 쓴다. 등급이 올라갈수록 금속감이 짙어진다. */
  border: var(--rq-bw, 2px) solid transparent;
  background:
    radial-gradient(circle at 50% 34%, #fffdf5 0%, var(--rq-top, #fdf1d2) 58%, var(--rq-bot, #f2ddaa) 100%) padding-box,
    var(--rq-edge, linear-gradient(#d8b64a, #d8b64a)) border-box;
  border-radius: 50%;
  color: var(--rq-ink, #7a5a10);
  box-shadow: inset 0 0 0 2px rgba(255,255,255,0.5), 0 2px 6px rgba(122,90,16,0.18);
}
.draft-card:hover .draft-icon {
  box-shadow:
    inset 0 0 0 2px rgba(255,255,255,0.6),
    0 0 var(--rq-halo-hi, 14px) var(--rq-glow, rgba(255,176,46,0.5));
}
.draft-icon .relic-art { width: 42px; height: 42px; }
.draft-icon .relic-art.is-glyph { font-size: 22px; }
.draft-card b { font-size: 15px; color: var(--rq-ink, #7a5a10); }
.draft-card small { font-size: 12px; line-height: 1.35; color: #6b5a3a; }

/* 유물 아트: 이미지가 없으면 한 글자 글리프로 대체된다 */
.relic-art {
  display: block;
  object-fit: contain;
  image-rendering: auto;
  filter: drop-shadow(0 1px 1px rgba(80,55,15,0.35));
  pointer-events: none;
}
.relic-art.is-glyph {
  display: grid;
  place-items: center;
  font-weight: bold;
  line-height: 1;
  color: #7a5a10;
  filter: none;
}

/* 보유 유물 바 */
#relic-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  align-items: center;
  /* 변이 바가 전체 폭 한 줄로 내려가면서 옆 반칸이 비지 않도록 유물 바도 전체 폭을 쓴다 */
  grid-column: 1 / -1;
  min-height: 26px;
  padding: 6px 2px 0;
  font-size: 11px;
}
#relic-bar .dim { opacity: 0.45; font-size: 12px; }
.relic-chip {
  position: relative;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: var(--rq-cbw, 1.5px) solid transparent;
  border-radius: 10px;
  background:
    linear-gradient(160deg, rgba(255,255,255,0.55), rgba(255,255,255,0) 55%) padding-box,
    linear-gradient(var(--rq-top, #fdf3dc), var(--rq-bot, #eddcb4)) padding-box,
    var(--rq-edge, linear-gradient(#d8b64a, #d8b64a)) border-box;
  box-shadow:
    var(--rq-bevel, 0 0 0 0 transparent),
    0 2px 4px var(--rq-drop, rgba(60,40,15,0.25)),
    0 0 0 var(--rq-rw, 0px) var(--rq-ring, transparent),
    0 0 var(--rq-halo, 0px) var(--rq-glow, transparent);
  cursor: help;
  outline: none;
  transition: transform 0.12s, box-shadow 0.12s;
}
.relic-chip::after {
  content: '';
  position: absolute;
  inset: -3px;
  border-radius: 13px;
  background: radial-gradient(circle, var(--rq-glow, rgba(255,196,74,0.5)), rgba(255,255,255,0) 70%);
  opacity: 0;
  transition: opacity 0.15s;
  pointer-events: none;
}
.relic-chip:hover, .relic-chip:focus-visible {
  transform: translateY(-2px);
  box-shadow:
    var(--rq-bevel, 0 0 0 0 transparent),
    0 5px 10px var(--rq-drop, rgba(60,40,15,0.3)),
    0 0 0 var(--rq-rw, 1px) var(--rq-key, rgba(255,176,46,0.5)),
    0 0 var(--rq-halo-hi, 0px) var(--rq-glow, transparent);
}
.relic-chip:hover::after, .relic-chip:focus-visible::after { opacity: 1; }
.relic-chip .relic-art { width: 26px; height: 26px; }
.relic-chip .relic-art.is-glyph { font-size: 15px; }
/* 보호막(수호 부적)은 색이 아니라 파란 숫자 뱃지로만 표시한다. 칩 색은 등급 전용이다. */
.relic-chip.spent { filter: grayscale(0.7); opacity: 0.55; }

.relic-badge {
  position: absolute;
  right: -4px;
  bottom: -4px;
  min-width: 16px;
  height: 16px;
  padding: 0 3px;
  display: grid;
  place-items: center;
  border-radius: 8px;
  background: linear-gradient(#f3c34a, #d29a1c);
  border: 1px solid rgba(90,60,10,0.45);
  color: #402c08;
  font-size: 11px;
  line-height: 1;
  box-shadow: 0 1px 2px rgba(0,0,0,0.3);
  pointer-events: none;
}
.relic-badge.shield {
  background: linear-gradient(#8ecbff, #3e9aff);
  border-color: rgba(10,50,100,0.45);
  color: #fff;
}

/* ---------- 유물·변이 성능 등급 4단계 ----------
   bronze < silver < gold < diamond. 사용자 요구에 따라 등급을 글자로 쓰지 않는다.
   색상(hue)만으로는 네 단계가 구분되지 않는다는 피드백에 따라, 색과 무관하게 읽히는
   네 개의 축을 동시에 계단으로 쌓는다.

     축          bronze        silver         gold            diamond
     테두리 폭   1.5px         2px            3px             3px
     테두리 재질 무광 단색      브러시드 금속   금박 그라데이션  회전하는 프리즘
     바깥 링     없음          없음           진한 금 키라인   진한 청 키라인
     광원        없음          없음           상시 16px       상시 24px + 반짝임
     바탕 명도   가장 어두움    밝은 냉백      따뜻한 크림     가장 밝은 빙백

   폭·링 개수·광원 유무는 흑백으로 봐도 순서가 그대로 남으므로 색약에서도 단계가 읽힌다.
   토큰을 소비하는 표면: .draft-card, .relic-chip, .go-relic, .mut-card, .mut-chip, #ui-tip */

/* 프리즘 테두리 회전용. @property를 못 받는 브라우저에서는 각도가 고정될 뿐 무지개는 남는다. */
@property --rq-ang {
  syntax: '<angle>';
  initial-value: 200deg;
  inherits: false;
}

.is-bronze {
  --rq-bw: 1.5px;
  --rq-cbw: 1px;
  --rq-line: #8f5a2a;
  --rq-key: #6b3d15;
  /* 네 등급 중 유일하게 바탕을 낮은 명도로 눌러 두어 한눈에 최하위로 읽히게 한다 */
  --rq-top: #eddcc4;
  --rq-bot: #d9bd9a;
  --rq-ink: #52300f;
  --rq-ring: transparent;
  --rq-rw: 0px;
  --rq-glow: rgba(143,90,42,0.35);
  --rq-halo: 0px;
  --rq-halo-hi: 8px;
  --rq-drop: rgba(62,38,14,0.24);
  /* 무광 단색 한 줄 */
  --rq-edge: linear-gradient(#8f5a2a, #8f5a2a);
  --rq-bevel: 0 0 0 0 transparent;
}
/* 변이 등급 B는 유물 silver의 은청 재질을 그대로 쓴다 */
.is-silver, .mut-b {
  --rq-bw: 2px;
  --rq-cbw: 1.5px;
  --rq-line: #93a3b4;
  --rq-key: #55677a;
  --rq-top: #ffffff;
  --rq-bot: #dde7f1;
  --rq-ink: #33404e;
  --rq-ring: transparent;
  --rq-rw: 0px;
  --rq-glow: rgba(126,152,180,0.5);
  --rq-halo: 0px;
  --rq-halo-hi: 14px;
  --rq-drop: rgba(34,46,60,0.22);
  /* 밝음-어두움-밝음이 반복되는 브러시드 은. 단색인 bronze와 재질부터 갈린다. */
  --rq-edge: linear-gradient(150deg,
    #f7fbff 0%, #c2cfdd 17%, #67798e 38%,
    #f2f7fc 55%, #9aabbd 74%, #5c6e82 100%);
  --rq-bevel: inset 0 0 0 1px rgba(255,255,255,0.95);
}
/* 변이 등급 A는 유물 gold의 금박 재질을 그대로 쓴다 */
.is-gold, .mut-a {
  --rq-bw: 3px;
  --rq-cbw: 2px;
  --rq-line: #e0a91e;
  --rq-key: #a97406;
  --rq-top: #fffaea;
  --rq-bot: #f6dc93;
  --rq-ink: #66450a;
  /* 금테 바깥에 진한 금 키라인을 한 겹 더 둘러 '이중 테두리'로 읽히게 한다 */
  --rq-ring: rgba(126,84,4,0.9);
  --rq-rw: 2px;
  --rq-glow: rgba(255,175,44,0.55);
  --rq-halo: 16px;
  --rq-halo-hi: 26px;
  --rq-drop: rgba(92,60,6,0.26);
  --rq-edge: linear-gradient(150deg,
    #ffe9a8 0%, #fffbe4 15%, #c1890a 37%,
    #fff0b8 55%, #d99f14 74%, #8a5f04 100%);
  --rq-bevel: inset 0 0 0 1px rgba(255,244,203,0.8);
  --rq-sheen: rgba(255,244,198,0.62);
  --rq-spark: rgba(255,231,163,0.95);
}
/* 변이 최고 등급 S는 유물 diamond의 회전 프리즘 연출을 그대로 물려받는다 */
.is-diamond, .mut-s {
  --rq-bw: 3px;
  --rq-cbw: 2px;
  --rq-line: #45c8f0;
  --rq-key: #0787bb;
  --rq-top: #ffffff;
  --rq-bot: #d2f0fd;
  --rq-ink: #044a66;
  --rq-ring: rgba(4,74,104,0.92);
  /* 바깥 링도 gold(2px)보다 두꺼워 흑백에서 두 상위 등급이 갈린다 */
  --rq-rw: 2.5px;
  --rq-glow: rgba(74,208,247,0.7);
  --rq-halo: 26px;
  --rq-halo-hi: 40px;
  --rq-drop: rgba(6,54,74,0.26);
  /* 청록 한 색이 아니라 자홍·라임까지 도는 무지개다. gold와 채도 계단이 아니라 성질이 다르다. */
  --rq-edge: conic-gradient(from var(--rq-ang, 200deg),
    #35d8ff 0%, #ffffff 8%, #ff6fd8 20%, #8a5bff 32%,
    #35a0ff 44%, #3ef2c8 58%, #ffffff 68%, #ffd84d 80%, #ff5fa2 90%, #35d8ff 100%);
  --rq-bevel: inset 0 0 0 1px rgba(238,252,255,0.95);
  --rq-sheen: rgba(224,251,255,0.8);
  --rq-spark: rgba(214,250,255,1);
}

/* 다이아만 테두리 무지개가 천천히 돈다. 정지 화면에서도 등급이 즉시 튄다.
   --rq-ang은 inherits:false라 자식 원반에는 따로 걸어 준다. */
.is-diamond,
.is-diamond > .draft-icon,
.mut-s {
  animation: rqPrism 6s linear infinite;
}
/* #ui-tip은 자기 등장 애니메이션이 있어 프리즘 회전을 함께 선언한다 */
#ui-tip.is-diamond { animation: tipIn 0.12s ease-out, rqPrism 6s linear infinite; }
@keyframes rqPrism {
  from { --rq-ang: 0deg; }
  to { --rq-ang: 360deg; }
}

/* silver — 움직이지 않는 정지 광택 한 줄. 스윕 애니메이션이 붙는 gold와
   '광이 도는가'가 아니라 '광이 움직이는가'로 갈린다. */
.draft-card.is-silver::before,
.mut-card.mut-b::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  background: linear-gradient(112deg,
    rgba(255,255,255,0) 30%,
    rgba(255,255,255,0.6) 44%,
    rgba(222,236,250,0.55) 50%,
    rgba(255,255,255,0) 62%);
  pointer-events: none;
}

/* bronze — 연출 레이어가 아예 붙지 않고(ui.js _rarityShine) 채도까지 눌러 무광으로 둔다.
   칩에는 filter를 걸지 않는다. .relic-chip.spent의 grayscale과 서로 덮어쓴다. */
.draft-card.is-bronze { filter: saturate(0.86); }

/* 광택 스윕(gold·diamond) — 카드 배경 위, 본문(z 2) 아래 */
.draft-sheen {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(108deg,
    rgba(255,255,255,0) 34%,
    rgba(255,255,255,0.5) 46%,
    var(--rq-sheen, rgba(255,240,190,0.4)) 52%,
    rgba(255,255,255,0) 64%);
  background-size: 280% 100%;
  background-repeat: no-repeat;
  animation: draftSheen 4.2s ease-in-out infinite;
}
.is-diamond > .draft-sheen,
.mut-s > .draft-sheen { animation-duration: 3.1s; }
@keyframes draftSheen {
  0%, 58% { background-position: -60% 0; }
  100% { background-position: 160% 0; }
}

/* 반짝임 3점(diamond 전용) */
.draft-spark, .draft-spark::before, .draft-spark::after {
  position: absolute;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: radial-gradient(circle, #fff 0 24%, var(--rq-spark, rgba(255,231,163,0.95)) 42%, rgba(255,255,255,0) 72%);
  pointer-events: none;
}
/* 아이콘 원반(z-index 2)에 가리지 않도록 좌우 여백 쪽에만 찍는다 */
.draft-spark {
  z-index: 1;
  top: 30px;
  right: 30px;
  animation: draftSpark 2.6s ease-in-out infinite;
}
.draft-spark::before {
  content: '';
  top: 34px;
  left: -84px;
  width: 5px;
  height: 5px;
  animation: draftSpark 2.6s ease-in-out 0.85s infinite;
}
.draft-spark::after {
  content: '';
  top: -8px;
  left: -98px;
  width: 4px;
  height: 4px;
  animation: draftSpark 2.6s ease-in-out 1.7s infinite;
}
@keyframes draftSpark {
  0%, 70%, 100% { opacity: 0; transform: scale(0.5); }
  18%, 34% { opacity: 1; transform: scale(1); }
}

/* 보유 유물·변이 칩 — 링과 광원은 전부 --rq-* 토큰이 조립하므로
   여기서는 광원 레이어에 아이콘이 묻히지 않게 순서만 정리한다. */
.relic-chip.is-gold .relic-art,
.relic-chip.is-diamond .relic-art { position: relative; z-index: 1; }
.relic-glow {
  position: absolute;
  inset: -4px;
  z-index: 0;
  border-radius: 13px;
  background: radial-gradient(circle, var(--rq-glow, rgba(255,198,78,0.6)), rgba(255,255,255,0) 70%);
  pointer-events: none;
  animation: relicPulse 2.8s ease-in-out infinite;
}
/* 다이아 칩의 후광은 더 넓고 더 빠르게 맥동한다 */
.is-diamond > .relic-glow,
.mut-s > .relic-glow {
  inset: -6px;
  border-radius: 15px;
  animation-duration: 2s;
}
@keyframes relicPulse {
  0%, 100% { opacity: 0.32; transform: scale(0.9); }
  50% { opacity: 0.85; transform: scale(1.06); }
}

/* 테두리를 도는 광원 — 마우스를 올렸거나 키보드 포커스가 갔을 때만 돈다.
   빛의 색은 등급 재질 토큰(--rq-line)을 그대로 써서 어느 등급인지 흐려지지 않는다.
   카드가 overflow:hidden이라 링은 테두리 바로 안쪽 선을 따라 그려진다.
   두 겹 마스크(전체 - content-box)로 padding 두께만 남긴 고리를 만든다. */
@property --gl-a {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}
.draft-card::after, .mut-card::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 5;
  padding: 2px;
  border-radius: calc(var(--gl-r, 14px) - var(--rq-bw, 2px));
  background: conic-gradient(from var(--gl-a),
    transparent 0deg 28deg,
    var(--rq-line, #e0a91e) 64deg,
    var(--gl-core, #fff) 84deg,
    var(--rq-line, #e0a91e) 104deg,
    transparent 140deg 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s;
}
.mut-card::after { --gl-r: 16px; }
/* bronze는 무광 등급이므로 빛의 심지도 흰색이 아니라 따뜻한 크림으로 눌러 둔다 */
.draft-card.is-bronze::after { --gl-core: rgba(255,233,206,0.85); }
.draft-card:hover::after, .draft-card:focus-visible::after,
.mut-card:hover::after, .mut-card:focus-visible::after {
  opacity: 1;
  animation: cardGlowSpin 2.6s linear infinite;
}
@keyframes cardGlowSpin {
  to { --gl-a: 360deg; }
}

@media (prefers-reduced-motion: reduce) {
  .draft-sheen, .draft-spark, .draft-spark::before, .draft-spark::after,
  .relic-glow, .is-diamond, .is-diamond > .draft-icon, .mut-s { animation: none; }
  /* 회전은 멈추고 테두리 빛만 은은하게 켜 둔다 */
  .draft-card:hover::after, .draft-card:focus-visible::after,
  .mut-card:hover::after, .mut-card:focus-visible::after { animation: none; opacity: 0.75; }
  .draft-spark, .draft-spark::before, .draft-spark::after { opacity: 0.8; }
  .relic-glow { opacity: 0.5; }
  /* 회전이 멈춰도 무지개 테두리 자체는 남아야 등급이 계속 읽힌다 */
  .is-diamond, .mut-s { --rq-ang: 200deg; }
  #ui-tip.is-diamond { animation: none; }
}

/* ---------- 직업 변이 드래프트 ----------
   성능 등급 3단계(B < A < S) 하나가 카드의 모든 색을 정한다.
   테두리 재질·링·광원은 유물과 공유하는 --rq-* 토큰(B=은청 / A=금박 / S=회전 프리즘),
   카드 안쪽 바탕과 상단 띠·등급 배지는 같은 등급의 --mc-* 색이 맡는다. */
#mutation-modal {
  position: fixed; inset: 0;
  background: radial-gradient(125% 100% at 50% 0%, rgba(52,22,78,0.6), rgba(10,26,30,0.74));
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--modal-pad);
  z-index: 25;
}
.mutation-wrap {
  width: min(700px, 100%);
  max-height: min(88vh, 780px);
  max-height: min(88dvh, 780px);
  gap: 2px;
  box-shadow: 0 12px 44px rgba(18,8,34,0.55), inset 0 0 0 3px #e6d6f2;
}
.mutation-wrap h3 { color: #4b2b6d; margin-bottom: 2px; }
.mutation-wrap h3 small { color: #12666b; opacity: 0.85; }
.mut-note {
  margin-bottom: 10px;
  font-size: 11.5px;
  line-height: 1.4;
  color: #8b7b66;
}
#mutation-list {
  display: flex;
  align-items: stretch;
  gap: 10px;
  min-height: 0;
  overflow-y: auto;
  padding: 6px 2px 4px;
}

.mut-card {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  padding: 15px 12px 12px;
  /* 카드 안쪽은 등급 색(--mc-*), 테두리 폭·재질·링·광원은 같은 등급의 --rq-*가 맡는다 */
  border: var(--rq-bw, 2px) solid transparent;
  border-radius: 16px;
  background:
    linear-gradient(168deg, var(--mc-top), #fffdf7 52%, var(--mc-bot)) padding-box,
    var(--rq-edge, linear-gradient(var(--mc-line), var(--mc-line))) border-box;
  font-family: var(--font-body);
  text-align: left;
  cursor: pointer;
  overflow: hidden;
  /* 광택·반짝임·테두리 광원의 z-index를 카드 안으로 가둔다 */
  isolation: isolate;
  box-shadow:
    var(--rq-bevel, inset 0 0 0 1px rgba(255,255,255,0.5)),
    0 3px 0 var(--rq-drop, rgba(0,0,0,0.16)),
    0 0 0 var(--rq-rw, 0px) var(--rq-ring, transparent),
    0 0 var(--rq-halo, 0px) var(--rq-glow, transparent);
  transition: transform 0.14s cubic-bezier(0.23, 1, 0.32, 1), box-shadow 0.13s;
}
/* 등급별 안쪽 색. 카드·칩·게임오버 필이 같은 값을 공유한다. */
.mut-b {
  --mc-key: #3f6284; --mc-soft: #8fb3cf; --mc-line: #b7cadb;
  --mc-top: #f4faff; --mc-bot: #e2ecf6; --mc-ink: #2e4356; --mc-glow: rgba(63,98,132,0.3);
}
.mut-a {
  --mc-key: #a97406; --mc-soft: #ecc463; --mc-line: #e4cb8d;
  --mc-top: #fffaea; --mc-bot: #f8ecc6; --mc-ink: #6a4708; --mc-glow: rgba(214,157,20,0.36);
}
.mut-s {
  --mc-key: #6c3fd0; --mc-soft: #45c8f0; --mc-line: #b9d9f2;
  --mc-top: #f6fdff; --mc-bot: #e6f3fd; --mc-ink: #0c4a70; --mc-glow: rgba(74,208,247,0.4);
}
.mut-card:hover, .mut-card:focus-visible {
  transform: translateY(-4px);
  box-shadow:
    var(--rq-bevel, inset 0 0 0 1px rgba(255,255,255,0.5)),
    0 10px 22px var(--rq-drop, var(--mc-glow)),
    0 0 0 var(--rq-rw, 0px) var(--rq-key, transparent),
    0 0 var(--rq-halo-hi, 12px) var(--rq-glow, var(--mc-glow));
  outline: none;
}
/* 유물 카드와 같은 눌림 피드백. hover·focus-visible 뒤에 둔다. */
.mut-card:active { transform: scale(0.97); }
/* 광택·반짝임 레이어 위로 본문·등급 표식을 띄운다 */
.mut-head, .mut-name, .mut-tags, .mut-effects, .mut-own { position: relative; z-index: 2; }
.mut-card > .draft-spark { top: 46px; right: 20px; }
.mut-edge {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 3;
  height: 5px;
  background: linear-gradient(90deg, var(--mc-key), var(--mc-soft));
}
/* S만 상단 띠도 무지개로 흘려 카드 테두리의 프리즘과 짝을 맞춘다 */
.mut-card.mut-s .mut-edge {
  background: linear-gradient(90deg, #35d8ff, #8a5bff 24%, #ff6fd8 48%, #ffd84d 72%, #3ef2c8);
}
.mut-grade {
  position: absolute;
  z-index: 4;
  top: 11px; right: 11px;
  width: 24px; height: 24px;
  display: grid;
  place-items: center;
  border-radius: 8px;
  font-size: 14px;
  color: #fff;
  text-shadow: 0 1px 2px rgba(20,10,40,0.45);
  background: linear-gradient(var(--mc-soft), var(--mc-key));
  box-shadow: 0 1px 3px rgba(30,10,50,0.35), inset 0 1px 0 rgba(255,255,255,0.45);
}
.mut-card.mut-s .mut-grade {
  background: linear-gradient(150deg, #35d8ff, #8a5bff 42%, #ff6fd8 72%, #ffd84d);
}

.mut-head { display: flex; align-items: center; gap: 9px; padding-right: 26px; }
.mut-portrait {
  flex: 0 0 auto;
  display: grid;
  place-items: end center;
  width: 62px; height: 62px;
  border: 1.5px solid var(--mc-line);
  border-radius: 16px;
  background: radial-gradient(circle at 50% 32%, #fffdf7, var(--mc-top) 72%);
  box-shadow: inset 0 0 0 3px rgba(255,255,255,0.55);
  overflow: hidden;
}
.mut-portrait canvas { display: block; }
.mut-who { display: flex; flex-direction: column; min-width: 0; }
.mut-who b { font-size: 15px; color: #6a5844; }
.mut-who small {
  align-self: flex-start;
  margin-top: 3px;
  padding: 0 7px;
  border: 1px solid var(--mc-line);
  border-radius: 999px;
  background: rgba(255,255,255,0.72);
  font-size: 11px;
  color: var(--mc-ink);
}
.mut-name {
  font-size: 20px;
  line-height: 1.15;
  color: var(--mc-ink);
  text-shadow: 0 1px 0 rgba(255,255,255,0.7);
}

.mut-tags { display: flex; flex-wrap: wrap; gap: 4px; }
.mut-tag {
  padding: 1px 8px;
  border: 1px solid var(--mc-line);
  border-radius: 999px;
  background: rgba(255,255,255,0.66);
  font-size: 11.5px;
  color: var(--mc-ink);
}

.mut-effects {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 8px 9px;
  border: 1px dashed var(--mc-line);
  border-radius: 11px;
  background: rgba(255,255,255,0.6);
}
.mut-line { display: flex; align-items: flex-start; gap: 6px; }
.mut-line i {
  flex: 0 0 12px;
  font-style: normal;
  font-size: 13px;
  line-height: 1.4;
  text-align: center;
}
.mut-line small { font-size: 12.5px; line-height: 1.4; color: #5c4830; }
.mut-line.is-up i { color: #2f8f6d; }
.mut-line.is-down i { color: #b4553b; }
.mut-line.is-down small { color: #7c5545; }
.mut-line.is-flat i { color: var(--mc-soft); }

.mut-own {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  margin-top: auto;
  padding-top: 9px;
  border-top: 1px solid var(--mc-line);
}
.mut-own-label { font-size: 11px; color: #8b7b66; }
.mut-own-pill {
  padding: 0 7px;
  border: 1.5px solid var(--c);
  border-radius: 999px;
  background: #fff;
  font-size: 11.5px;
  color: var(--c);
}
.mut-own-none {
  padding: 0 7px;
  border: 1px dashed #c9b697;
  border-radius: 999px;
  background: rgba(255,255,255,0.5);
  font-size: 11.5px;
  color: #a08f7a;
}

/* 좁거나 낮은 창에서도 3장이 잘리지 않게 */
@media (max-width: 760px) {
  #mutation-list { flex-direction: column; }
  /* 가로일 때의 flex: 1 1 0(폭 균등 분배)이 세로로 바뀌면 높이 균등 분배가 된다.
     리스트는 overflow-y: auto라 남는 높이가 0이고, basis 0에 shrink까지 열려 있어
     카드가 40px짜리 알약으로 짜부라졌다(실기기 세로 버그). 세로에서는 내용 높이를 그대로 쓰고
     넘치는 만큼 리스트가 스크롤한다. */
  .mut-card { flex: 0 0 auto; padding-bottom: 11px; }
}
@media (max-height: 760px) {
  .mut-portrait { width: 52px; height: 52px; border-radius: 14px; }
  .mut-name { font-size: 18px; }
  .mut-card { gap: 6px; padding: 13px 11px 10px; }
  .mut-note { margin-bottom: 8px; }
}

/* 보유 변이 바 — 유물·변형 바와 같이 획득한 것만 보여 준다. 하나도 없으면 바 자체가 숨는다.
   최대 5개까지 쌓이면 좁은 사이드바에서 줄바꿈이 나고, 등급 배지가 칩 밖으로 4px 튀어나오므로
   행 간격을 따로 벌려 아랫줄 칩과 겹치지 않게 한다. */
#mutation-bar {
  display: flex;
  flex-wrap: wrap;
  column-gap: 5px;
  row-gap: 9px;
  align-items: center;
  /* .actions는 2열 그리드다. 변형 바처럼 전체 폭 한 줄을 차지해야
     칩이 반칸 안에서 어색하게 줄바꿈되지 않는다. */
  grid-column: 1 / -1;
  min-width: 0;
  min-height: 26px;
  padding: 6px 2px 0;
}
.mut-bar-label {
  font-size: 11px;
  letter-spacing: 1px;
  color: #8b7b66;
  opacity: 0.85;
}
.mut-chip {
  position: relative;
  display: grid;
  place-items: end center;
  width: 34px; height: 34px;
  /* 칩 안쪽은 등급 색, 테두리 폭·재질·링·광원도 같은 등급의 토큰이다 */
  border: var(--rq-cbw, 1.5px) solid transparent;
  border-radius: 10px;
  background:
    linear-gradient(160deg, rgba(255,255,255,0.6), rgba(255,255,255,0) 55%) padding-box,
    linear-gradient(var(--mc-top), var(--mc-bot)) padding-box,
    var(--rq-edge, linear-gradient(var(--mc-line), var(--mc-line))) border-box;
  box-shadow:
    var(--rq-bevel, 0 1px 0 rgba(255,255,255,0.6) inset),
    0 2px 4px var(--rq-drop, rgba(40,20,60,0.24)),
    0 0 0 var(--rq-rw, 0px) var(--rq-ring, transparent),
    0 0 var(--rq-halo, 0px) var(--rq-glow, transparent);
  overflow: visible;
  cursor: help;
  outline: none;
  transition: transform 0.12s, box-shadow 0.12s;
}
.mut-chip canvas { display: block; border-radius: 8px; position: relative; z-index: 1; }
.mut-chip:hover, .mut-chip:focus-visible {
  transform: translateY(-2px);
  box-shadow:
    var(--rq-bevel, 0 1px 0 rgba(255,255,255,0.7) inset),
    0 5px 10px var(--rq-drop, rgba(40,20,60,0.3)),
    0 0 0 var(--rq-rw, 1px) var(--rq-key, var(--mc-key)),
    0 0 var(--rq-halo-hi, 0px) var(--rq-glow, transparent);
}
.mut-chip-grade {
  position: absolute;
  z-index: 2;
  right: -4px; bottom: -4px;
  width: 16px; height: 16px;
  display: grid;
  place-items: center;
  border-radius: 7px;
  border: 1px solid rgba(30,10,50,0.35);
  background: linear-gradient(var(--mc-soft), var(--mc-key));
  color: #fff;
  font-size: 11px;
  font-style: normal;
  line-height: 1;
  text-shadow: 0 1px 1px rgba(20,10,40,0.5);
}
.mut-chip.mut-s .mut-chip-grade {
  background: linear-gradient(150deg, #35d8ff, #8a5bff 45%, #ff6fd8);
}

/* ---------- 변형 규칙(정복자 전용) ----------
   초월 등급색(#66fcf1)을 프레임 톤으로 써서 다른 드래프트와 격을 구분한다. */
#modifier-modal {
  position: fixed; inset: 0;
  background: radial-gradient(120% 100% at 50% 0%, rgba(10,58,66,0.62), rgba(8,16,22,0.8));
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--modal-pad);
  /* 게임 오버 화면(18) 위, 커스텀 툴팁(30) 아래 */
  z-index: 26;
}
.modifier-wrap {
  width: min(760px, 100%);
  max-height: min(88vh, 800px);
  max-height: min(88dvh, 800px);
  background: linear-gradient(180deg, #fbf4e4, var(--paper));
  box-shadow: 0 14px 46px rgba(4,26,30,0.6), inset 0 0 0 3px #66fcf1, inset 0 0 22px rgba(102,252,241,0.28);
}
.modifier-wrap h3 { color: #0d5f66; margin-bottom: 2px; }
.modifier-wrap h3 small { color: #157f86; opacity: 0.85; }
.mod-note {
  margin-bottom: 10px;
  font-family: var(--font-body);
  font-size: 11.5px;
  line-height: 1.4;
  color: #7d8a84;
}

/* ---- 차원 승천 선택 줄 ----
   변형과 같은 화면에 있지만 성격이 반대다(변형=규칙 변주, 차원=난이도 사다리).
   그래서 청록 대신 붉은 달의 톤을 쓰고, 가로로 한 줄에 늘어놓아 사다리로 읽히게 한다. */
.dim-select {
  margin-bottom: 10px;
  padding-bottom: 10px;
  border-bottom: 2px dashed rgba(200,80,100,0.3);
}
.dim-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 7px; }
.dim-head b {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 15px;
  color: #a3123a;
}
.dim-head small {
  flex: 1;
  min-width: 0;
  font-size: 11px;
  line-height: 1.4;
  color: #8a7a7c;
  letter-spacing: 0.01em;
}
.dim-list {
  display: flex;
  gap: 7px;
  overflow-x: auto;
  padding: 2px 2px 4px;
  scrollbar-width: thin;
}
.dim-card {
  flex: 0 0 auto;
  width: 168px;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  border: 2px solid #e2ccd0;
  border-radius: 12px;
  padding: 8px 9px;
  background: #fffaf4;
  font-family: var(--font-body);
  text-align: left;
  cursor: pointer;
  transition: transform 0.12s, box-shadow 0.12s, border-color 0.12s, background 0.12s;
}
.dim-mark {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: linear-gradient(#f0dfe2, #d9c3c8);
  font-family: var(--font-display);
  font-size: 14px;
  color: #8a6a70;
}
.dim-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.dim-info b {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 13.5px;
  color: var(--ink);
}
.dim-info small { font-size: 10px; line-height: 1.3; color: #8a7358; letter-spacing: 0.01em; }
.dim-info em { font-style: normal; font-size: 10px; line-height: 1.3; color: #3d8b40; letter-spacing: 0.01em; }
.dim-card:hover, .dim-card:focus-visible {
  transform: translateY(-2px);
  border-color: #d98a9c;
  outline: none;
}
.dim-card.is-picked {
  border-color: #c8324f;
  background: linear-gradient(#fff2f4, #fbdde2);
  box-shadow: 0 3px 0 rgba(140,20,45,0.2), 0 0 14px rgba(255,90,120,0.35);
}
.dim-card.is-picked .dim-mark {
  background: radial-gradient(circle at 32% 30%, #ffd9e0, #ff4d6d 62%, #a3122f);
  color: #fff;
  text-shadow: 0 1px 2px rgba(90,10,26,0.5);
}
.dim-card.is-picked .dim-info b { color: #a3123a; }
/* 차원 0(일반)은 사다리의 바닥이라 붉은 강조 대신 차분한 초록으로 고른 티만 낸다 */
.dim-card.is-picked[data-dim="0"] {
  border-color: #6fbf5f;
  background: linear-gradient(#f4fdf0, #ddf2d4);
  box-shadow: 0 3px 0 rgba(40,100,44,0.18);
}
.dim-card.is-picked[data-dim="0"] .dim-mark {
  background: linear-gradient(#a8e88f, #4f9c46);
  color: #123312;
}
.dim-card.is-picked[data-dim="0"] .dim-info b { color: #2f6b34; }
.dim-card.is-locked {
  opacity: 0.5;
  filter: grayscale(0.6);
  cursor: not-allowed;
}
.dim-card.is-locked:hover { transform: none; border-color: #e2ccd0; }
.dim-card.nope { animation: modNope 0.32s ease-out; }
@media (prefers-reduced-motion: reduce) {
  .dim-card { transition: none; }
  .dim-card:hover { transform: none; }
  .dim-card.nope { animation: none; }
}
#modifier-list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 9px;
  min-height: 0;
  overflow-y: auto;
  padding: 4px 2px;
}
@media (max-width: 720px) {
  #modifier-list { grid-template-columns: repeat(2, 1fr); }
}

.mod-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  padding: 11px 34px 11px 12px;
  border: 2px solid transparent;
  border-radius: 14px;
  background:
    linear-gradient(168deg, #fffefa, #f1e8d3) padding-box,
    linear-gradient(150deg, #cbd9d4, #a9bfba) border-box;
  font-family: var(--font-body);
  text-align: left;
  cursor: pointer;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.7), 0 2px 0 rgba(52,64,62,0.18);
  transition: transform 0.12s, box-shadow 0.12s, background 0.12s, opacity 0.12s;
}
.mod-card:hover, .mod-card:focus-visible {
  transform: translateY(-3px);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.7), 0 8px 16px rgba(14,58,62,0.24), 0 0 14px rgba(102,252,241,0.4);
  outline: none;
}
.mod-icon {
  display: grid;
  place-items: center;
  width: 30px; height: 30px;
  border-radius: 9px;
  background: linear-gradient(#7fe6e0, #1d9aa0);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.55), 0 1px 3px rgba(10,60,64,0.35);
  font-family: var(--font-display);
  font-size: 15px;
  color: #f3ffff;
  text-shadow: 0 1px 2px rgba(6,50,54,0.5);
}
/* 그린 아이콘이 있는 타일은 밝은 청록을 걷고 깊은 우물로 바꾼다.
   유물 칩과 같은 규칙 — 판은 배경일 뿐이고 그림이 주인공이다. */
.mod-icon.has-art {
  background: linear-gradient(#12666c, #073238);
  box-shadow:
    inset 0 0 0 1px rgba(168,255,246,0.28),
    inset 0 2px 5px rgba(2,22,26,0.5),
    0 1px 3px rgba(10,60,64,0.35);
  text-shadow: none;
}
.mod-icon .mod-art { width: 24px; height: 24px; }
.mod-icon .mod-art.is-glyph { font-size: 15px; color: inherit; }

/* 변형 아이콘 아트: 이미지가 없으면 한 글자 글리프로 대체된다 */
.mod-art {
  display: block;
  object-fit: contain;
  image-rendering: auto;
  filter: drop-shadow(0 1px 2px rgba(2,26,30,0.5));
  pointer-events: none;
}
.mod-art.is-glyph {
  display: grid;
  place-items: center;
  font-family: var(--font-display);
  line-height: 1;
  filter: none;
}
.mod-name {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 15px;
  color: #2f4a4d;
}
.mod-desc { font-size: 11.5px; line-height: 1.38; color: #6b5a3a; }
/* 선택·잠금 표식. 기본 상태에서는 아무것도 그리지 않는다. */
.mod-mark {
  position: absolute;
  top: 10px; right: 10px;
  display: grid;
  place-items: center;
  width: 20px; height: 20px;
  border: 1.5px dashed rgba(60,86,88,0.35);
  border-radius: 50%;
  font-style: normal;
  font-size: 12px;
  line-height: 1;
  color: transparent;
}
.mod-card.is-picked {
  background:
    linear-gradient(168deg, #f2fffd, #d9f7f4) padding-box,
    linear-gradient(150deg, #a8fff6, #16a3a8) border-box;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.8), 0 3px 0 rgba(14,90,94,0.28), 0 0 18px rgba(102,252,241,0.6);
}
.mod-card.is-picked .mod-name { color: #0d5f66; }
.mod-card.is-picked .mod-mark {
  border: none;
  background: linear-gradient(#7fe6e0, #12959b);
  color: #ffffff;
  box-shadow: 0 1px 3px rgba(8,60,64,0.45);
}
.mod-card.is-picked .mod-mark::before { content: '✓'; }
.mod-card.is-locked {
  filter: grayscale(0.85);
  opacity: 0.44;
  cursor: not-allowed;
}
.mod-card.is-locked:hover { transform: none; box-shadow: inset 0 0 0 1px rgba(255,255,255,0.7), 0 2px 0 rgba(52,64,62,0.18); }
.mod-card.is-locked.nope { animation: modNope 0.32s ease-out; }
@keyframes modNope {
  0%, 100% { transform: translateX(0); }
  22% { transform: translateX(-5px); }
  58% { transform: translateX(4px); }
  82% { transform: translateX(-2px); }
}
.mod-card.is-locked .mod-mark { color: #7c6a55; }
.mod-card.is-locked .mod-mark::before { content: '✕'; }

.mod-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 2px dashed rgba(22,120,124,0.28);
}
.mod-btn {
  border: none;
  border-radius: 11px;
  padding: 10px 20px;
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
  font-size: 15.5px;
  color: #fff;
  cursor: pointer;
  transition: filter 0.12s, transform 0.08s;
}
.mod-btn.primary {
  background: linear-gradient(#5fe3da, #169c96);
  box-shadow: 0 3px 0 #0d6f6b, 0 0 14px rgba(102,252,241,0.45);
  text-shadow: 0 1px 2px rgba(6,60,58,0.4);
}
.mod-btn.primary.is-plain {
  background: linear-gradient(#9db3ae, #6c827e);
  box-shadow: 0 3px 0 #4e615e;
}
.mod-btn.ghost { background: rgba(0,0,0,0.32); }
.mod-btn:hover { filter: brightness(1.07); }
.mod-btn:active { transform: translateY(3px); box-shadow: none; }

/* 사이드바 활성 변형 칩 */
#modifier-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  align-items: center;
  grid-column: 1 / -1;
  padding: 6px 2px 0;
}
.mod-bar-label {
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 1px;
  color: #2f6f72;
  opacity: 0.85;
}
.mod-chip {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px 3px 4px;
  border: 1.5px solid #66fcf1;
  border-radius: 999px;
  background: linear-gradient(#14666c, #0d474c);
  box-shadow: 0 1px 3px rgba(10,50,54,0.3), inset 0 1px 0 rgba(168,255,246,0.3);
  cursor: help;
  outline: none;
  transition: box-shadow 0.12s, transform 0.12s;
}
.mod-chip:hover, .mod-chip:focus-visible {
  transform: translateY(-1px);
  box-shadow: 0 2px 6px rgba(0,0,0,0.4), 0 0 16px rgba(102,252,241,0.65);
}
.mod-chip-icon {
  display: grid;
  place-items: center;
  width: 19px; height: 19px;
  border-radius: 6px;
  background: linear-gradient(#7fe6e0, #1d9aa0);
  font-family: var(--font-display);
  font-style: normal;
  font-size: 11px;
  color: #052f33;
}
/* 칩 자체가 이미 짙은 청록이다. 아트 타일은 밝은 판 대신 옅은 유리로 깔아 그림만 뜨게 한다. */
.mod-chip-icon.has-art {
  background: rgba(168,255,246,0.13);
  box-shadow: inset 0 0 0 1px rgba(168,255,246,0.24);
}
.mod-chip-icon .mod-art { width: 17px; height: 17px; }
.mod-chip-icon .mod-art.is-glyph { font-size: 11px; color: inherit; }
.mod-chip b {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 11.5px;
  color: #d8fffb;
}

/* 운빨 봉인: 비활성 회색이 아니라 잠긴 상태로 읽히게 한다 */
.big-btn.luck.sealed {
  background: linear-gradient(#5d7a63, #3a4f42);
  filter: none;
  box-shadow: 0 3px 0 rgba(0,0,0,0.3), inset 0 0 0 1.5px rgba(102,252,241,0.35);
}
.big-btn.luck.sealed img { filter: grayscale(1); opacity: 0.5; }
.big-btn.luck.sealed #luck-info { color: #a8fff6; opacity: 0.95; }

/* ---------- 커스텀 툴팁 ---------- */
#ui-tip {
  position: fixed;
  z-index: 30;
  width: max-content;
  max-width: 240px;
  padding: 9px 11px 10px;
  border-radius: 12px;
  border: var(--rq-cbw, 1.5px) solid transparent;
  background:
    linear-gradient(var(--rq-top, #fffaee), var(--rq-bot, #f4e6c6)) padding-box,
    var(--rq-edge, linear-gradient(#d8b64a, #d8b64a)) border-box;
  color: var(--ink);
  box-shadow:
    var(--rq-bevel, 0 0 0 0 transparent),
    0 8px 22px rgba(30,18,6,0.4),
    0 0 0 var(--rq-rw, 0px) var(--rq-ring, transparent),
    0 0 var(--rq-halo, 0px) var(--rq-glow, transparent);
  pointer-events: none;
  animation: tipIn 0.12s ease-out;
}
@keyframes tipIn {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: translateY(0); }
}
/* 인접 칩으로 옮겨 탄 재오픈(skip-delay). 진입 연출만 생략하고 내용을 그 자리에서 갈아끼운다.
   다이아 등급의 프리즘 회전은 툴팁의 정체성이라 그대로 남긴다. */
#ui-tip.instant { animation: none; }
#ui-tip.is-diamond.instant { animation: rqPrism 6s linear infinite; }
@media (prefers-reduced-motion: reduce) {
  #ui-tip.is-diamond.instant { animation: none; }
}
#ui-tip .tip-head {
  display: flex;
  align-items: center;
  gap: 7px;
  padding-bottom: 6px;
  margin-bottom: 6px;
  border-bottom: 1px solid rgba(122,82,48,0.22);
}
#ui-tip .tip-icon {
  display: grid;
  place-items: center;
  flex: 0 0 26px;
  width: 26px;
  height: 26px;
  border-radius: 8px;
  border: 1px solid var(--rq-line, rgba(216,182,74,0.8));
  background: rgba(255,255,255,0.6);
}
#ui-tip .tip-icon .relic-art { width: 20px; height: 20px; }
#ui-tip .tip-icon .relic-art.is-glyph { font-size: 13px; }
/* 변형 규칙 툴팁. 등급색이 없는 대상이라 청록 테두리를 직접 준다. */
#ui-tip .tip-icon:has(.mod-art) {
  border-color: rgba(22,120,124,0.7);
  background: linear-gradient(#12666c, #0a3f45);
}
#ui-tip .tip-icon .mod-art { width: 20px; height: 20px; }
#ui-tip .tip-icon .mod-art.is-glyph { font-size: 13px; color: #d8fffb; }
#ui-tip .tip-name { font-size: 14px; color: var(--rq-ink, #7a5a10); }
#ui-tip .tip-desc { font-size: 12.5px; line-height: 1.45; color: #5c4830; }
#ui-tip .tip-note {
  margin-top: 5px;
  font-size: 11.5px;
  color: #8a6a2e;
  opacity: 0.9;
}
#ui-tip .tip-arrow {
  position: absolute;
  left: var(--tip-arrow-x, 50%);
  bottom: -6px;
  width: 10px;
  height: 10px;
  margin-left: -5px;
  background: var(--rq-bot, #f4e6c6);
  border-right: 1.5px solid var(--rq-line, #d8b64a);
  border-bottom: 1.5px solid var(--rq-line, #d8b64a);
  transform: rotate(45deg);
}
#ui-tip.below .tip-arrow {
  bottom: auto;
  top: -6px;
  background: var(--rq-top, #fffaee);
  border: none;
  border-left: 1.5px solid var(--rq-line, #d8b64a);
  border-top: 1.5px solid var(--rq-line, #d8b64a);
}

/* 등급 툴팁 — 테두리 폭·재질·링·광원은 전부 --rq-* 토큰이 조립한다.
   등급을 글자로 적지 않으므로 뱃지 요소는 없다. */
#ui-tip .tip-head { border-bottom-color: var(--rq-line, rgba(122,82,48,0.35)); }
#ui-tip .tip-icon { border-color: var(--rq-line, rgba(216,182,74,0.8)); }

.hidden { display: none !important; }

/* ---------- 키보드 포커스 ----------
   :where()로 특이도 0을 유지해, 이미 자체 포커스 연출(outline:none + 링·광원)을 가진
   카드·칩은 지금 모습 그대로 남기고 아무 표시도 없던 버튼에만 은은한 금색 링을 준다. */
:where(button, [href], input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid rgba(255, 176, 46, 0.75);
  outline-offset: 3px;
}
/* 모달을 열 때 읽기 위치를 옮기려고 임시로 포커스를 받는 카드에는 링을 그리지 않는다 */
:where([tabindex="-1"]):focus, :where([tabindex="-1"]):focus-visible { outline: none; }

/* 스크롤바 */
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.25); border-radius: 4px; }
::-webkit-scrollbar-track { background: transparent; }

/* ---------- 영웅 이상 뽑기 연출 ---------- */
#reveal .reveal-card {
  position: relative;
  z-index: 3;
  overflow: visible;
}

.fx-layer {
  position: absolute;
  inset: -300px;
  pointer-events: none;
  overflow: visible;
  z-index: -1;
}

#epic-flash {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  z-index: 2;
}
#epic-flash.hero-flash { background: radial-gradient(circle, rgba(180,91,255,.6), transparent 62%); }
#epic-flash.legend-flash { background: radial-gradient(circle, rgba(255,255,255,.9), rgba(255,176,46,.48) 35%, transparent 72%); }
#epic-flash.mythic-flash { background: conic-gradient(from 0deg, rgba(255,77,109,.45), rgba(255,230,109,.5), rgba(91,231,169,.45), rgba(59,155,255,.5), rgba(180,91,255,.5), rgba(255,77,109,.45)); }
/* 초월 — 백색 코어 위에 청록 스펙트럼이 겹치는 2중 플래시 */
#epic-flash.transcend-flash {
  background:
    radial-gradient(circle, rgba(255,255,255,.98), rgba(168,255,246,.6) 26%, transparent 64%),
    conic-gradient(from 0deg, rgba(102,252,241,.62), rgba(255,255,255,.7), rgba(201,182,255,.6), rgba(91,231,169,.58), rgba(127,216,255,.66), rgba(102,252,241,.62));
}
#epic-flash.on { animation: epicFlash 1.1s ease-out forwards; }
#epic-flash.transcend-flash.on { animation: transcendFlash 1.9s ease-out forwards; }

@keyframes epicFlash {
  0% { opacity: 0; }
  15% { opacity: .95; }
  100% { opacity: 0; }
}
/* 두 번 터진다 — 백색 섬광 뒤에 스펙트럼이 한 번 더 밀려온다 */
@keyframes transcendFlash {
  0% { opacity: 0; transform: scale(1.25); }
  8% { opacity: 1; transform: scale(1); }
  30% { opacity: .18; }
  46% { opacity: .8; transform: scale(1.04); }
  100% { opacity: 0; transform: scale(1.12); }
}

#reveal.epic .reveal-card {
  animation: epicCardIn .55s cubic-bezier(.12,1.7,.35,1), epicGlow 1s .55s ease-in-out infinite alternate;
}
#reveal.mythic .reveal-card {
  animation: epicCardIn .6s cubic-bezier(.12,1.9,.35,1), mythicGlow .65s .6s ease-in-out infinite alternate;
}
#reveal.transcend .reveal-card {
  animation: transcendCardIn .85s cubic-bezier(.1,2,.3,1), transcendGlow .9s .85s ease-in-out infinite alternate;
}
/* 흰 점에서 터져 나와 한 바퀴 돌며 자리를 잡는다 */
@keyframes transcendCardIn {
  0% { transform: scale(.04) rotate(-14deg); opacity: 0; filter: brightness(3.5); }
  38% { transform: scale(1.22) rotate(5deg); opacity: 1; filter: brightness(2.1); }
  62% { transform: scale(.94) rotate(-2deg); filter: brightness(1.3); }
  100% { transform: scale(1) rotate(0); opacity: 1; filter: brightness(1); }
}
@keyframes transcendGlow {
  from { filter: brightness(1.08) drop-shadow(0 0 22px #66fcf1); }
  to { filter: brightness(1.45) drop-shadow(0 0 52px #a8fff6) drop-shadow(0 0 90px #66fcf1); }
}

/* 초월 리빌 구조물 — 교차 빛기둥과 퍼지는 프리즘 링 */
.fx-pillar {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 30px;
  height: 1100px;
  margin: -550px 0 0 -15px;
  background: linear-gradient(to bottom, transparent, var(--c) 30%, #ffffff 50%, var(--c) 70%, transparent);
  filter: blur(1.5px) drop-shadow(0 0 16px var(--c));
  opacity: 0;
  transform-origin: 50% 50%;
  animation: revealPillar 1.7s var(--delay) cubic-bezier(.1,.85,.25,1) forwards;
}
@keyframes revealPillar {
  0% { opacity: 0; transform: rotate(var(--rot)) scale(.02, .1); }
  14% { opacity: .95; transform: rotate(calc(var(--rot) + 12deg)) scale(1.5, 1); }
  100% { opacity: 0; transform: rotate(calc(var(--rot) + 70deg)) scale(.25, 1); }
}

.fx-ring {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 90px;
  height: 90px;
  margin: -45px 0 0 -45px;
  border: 3px solid var(--c);
  border-radius: 50%;
  box-shadow: 0 0 22px var(--c), inset 0 0 18px var(--c);
  opacity: 0;
  animation: revealRing 1.5s var(--delay) cubic-bezier(.14,.8,.24,1) forwards;
}
@keyframes revealRing {
  0% { opacity: 0; transform: scale(.1); }
  18% { opacity: 1; }
  100% { opacity: 0; transform: scale(8.5); border-width: 1px; }
}

@keyframes epicCardIn {
  from { transform: scale(.15) rotate(-5deg); opacity: 0; }
  to { transform: scale(1) rotate(0); opacity: 1; }
}
@keyframes epicGlow {
  from { filter: brightness(1); }
  to { filter: brightness(1.2) drop-shadow(0 0 24px currentColor); }
}
@keyframes mythicGlow {
  from { filter: brightness(1.05) drop-shadow(0 0 18px #ff4d6d); }
  to { filter: brightness(1.35) drop-shadow(0 0 38px #ffe66d); }
}

/* ---------- 10연 뽑기 결과 그리드 ---------- */
#bulk-result {
  --s: 1;
  position: absolute;
  left: 50%;
  bottom: 74px;
  /* left:50% 기준 shrink-to-fit은 남은 절반 폭까지만 늘어난다. max-content로 한 줄을 유지한다. */
  width: max-content;
  max-width: 94%;
  padding: 9px 13px 11px;
  border-radius: 16px;
  background: linear-gradient(rgba(38,27,15,0.9), rgba(22,15,8,0.94));
  box-shadow: 0 10px 26px rgba(0,0,0,0.45), inset 0 0 0 2px rgba(255,236,196,0.12);
  pointer-events: none;
  z-index: 0; /* 중앙 reveal 카드가 항상 위에 오도록 auto 레벨을 넘지 않는다 */
  transform-origin: 50% 100%;
  transform: translateX(-50%) scale(var(--s));
  animation: bulkIn .34s cubic-bezier(.18,1.5,.4,1);
}
#bulk-result.shiny { box-shadow: 0 10px 26px rgba(0,0,0,0.45), 0 0 34px var(--c), inset 0 0 0 2px var(--c); }
#bulk-result.out { animation: bulkOut .2s ease-in forwards; }

@keyframes bulkIn {
  from { opacity: 0; transform: translateX(-50%) translateY(20px) scale(calc(var(--s) * .88)); }
  to { opacity: 1; transform: translateX(-50%) translateY(0) scale(var(--s)); }
}
@keyframes bulkOut {
  from { opacity: 1; transform: translateX(-50%) translateY(0) scale(var(--s)); }
  to { opacity: 0; transform: translateX(-50%) translateY(12px) scale(calc(var(--s) * .96)); }
}

/* 신화 포함 묶음 — 무지개 테두리 링과 맥동하는 후광 */
#bulk-result.mythic {
  border: 3px solid transparent;
  background:
    linear-gradient(rgba(38,27,15,0.92), rgba(22,15,8,0.95)) padding-box,
    conic-gradient(from 0deg, #ff4d6d, #ffb02e, #ffe66d, #5be7a9, #3b9bff, #b45bff, #ff4d6d) border-box;
  box-shadow: 0 10px 26px rgba(0,0,0,0.45), 0 0 30px rgba(255,230,109,0.55);
  animation: bulkIn .34s cubic-bezier(.18,1.5,.4,1), bulkMythicHalo 1.5s .34s ease-in-out infinite alternate;
}
#bulk-result.mythic.out { animation: bulkOut .2s ease-in forwards; }
@keyframes bulkMythicHalo {
  from { box-shadow: 0 10px 26px rgba(0,0,0,0.45), 0 0 26px rgba(255,77,109,0.5); }
  to { box-shadow: 0 10px 26px rgba(0,0,0,0.45), 0 0 46px rgba(255,230,109,0.75); }
}

/* 초월 포함 묶음 — 프리즘 테두리와 청록 후광 (신화 무지개보다 한 단계 위) */
#bulk-result.transcend {
  border: 3px solid transparent;
  background:
    linear-gradient(rgba(8,26,28,0.93), rgba(4,14,16,0.96)) padding-box,
    conic-gradient(from 0deg, #66fcf1, #a8fff6, #ffffff, #c9b6ff, #5be7a9, #7fd8ff, #66fcf1) border-box;
  box-shadow: 0 10px 26px rgba(0,0,0,0.45), 0 0 42px rgba(102,252,241,0.7);
  animation: bulkIn .34s cubic-bezier(.18,1.5,.4,1), bulkTranscendHalo 1.1s .34s ease-in-out infinite alternate;
}
#bulk-result.transcend.out { animation: bulkOut .2s ease-in forwards; }
@keyframes bulkTranscendHalo {
  from { box-shadow: 0 10px 26px rgba(0,0,0,0.45), 0 0 30px rgba(102,252,241,0.55); }
  to { box-shadow: 0 10px 26px rgba(0,0,0,0.45), 0 0 64px rgba(168,255,246,0.9); }
}

.bulk-head {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 7px;
}
.bulk-head b { font-size: 14px; color: #ffe9c2; letter-spacing: 0.5px; }
.bulk-pill {
  font-size: 11px;
  border: 1px solid;
  border-radius: 999px;
  padding: 1px 8px;
  background: rgba(0,0,0,0.4);
}

.bulk-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 7px; }
.bulk-card {
  position: relative;
  width: 74px;
  padding: 3px 3px 4px;
  border: 2px solid var(--c);
  border-radius: 12px;
  background: radial-gradient(circle at 50% 22%, rgba(255,255,255,0.16), rgba(0,0,0,0.42) 72%);
  animation: bulkCardIn .32s calc(var(--i) * 42ms) backwards cubic-bezier(.2,1.7,.4,1);
}
.bulk-card::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 10px;
  background: var(--c);
  opacity: 0.18;
  pointer-events: none;
}
.bulk-card canvas { position: relative; display: block; margin: 0 auto; max-width: 100%; }
.bulk-card small {
  position: relative;
  display: block;
  text-align: center;
  font-size: 11px;
  color: #fff3dc;
  text-shadow: 0 1px 2px rgba(0,0,0,0.75);
}
@keyframes bulkCardIn {
  from { opacity: 0; transform: translateY(14px) scale(.6); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

/* 최고 등급 카드 강조 */
.bulk-card.best {
  z-index: 2;
  transform: scale(1.09);
  border-width: 3px;
  box-shadow: 0 0 16px var(--c), 0 0 5px rgba(255,255,255,0.7) inset;
  animation: bulkBestIn .4s calc(var(--i) * 42ms) backwards cubic-bezier(.2,1.8,.4,1),
             bulkBestPulse .9s .55s ease-in-out infinite alternate;
}
.bulk-card.best::after {
  content: '★';
  position: absolute;
  top: -10px;
  right: -7px;
  font-size: 15px;
  line-height: 1;
  color: var(--c);
  text-shadow: 0 0 7px var(--c), 0 1px 2px rgba(0,0,0,0.8);
}
@keyframes bulkBestIn {
  from { opacity: 0; transform: scale(.25) rotate(-9deg); }
  to { opacity: 1; transform: scale(1.09) rotate(0); }
}
@keyframes bulkBestPulse {
  from { transform: scale(1.05); filter: brightness(1); }
  to { transform: scale(1.15); filter: brightness(1.22); }
}

@media (max-width: 1080px) {
  #bulk-result { --s: 0.86; bottom: 62px; }
}
@media (max-width: 820px) {
  #bulk-result { --s: 0.7; bottom: 52px; }
}

#stage.shake { animation: stageShake .5s ease-out; }
@keyframes stageShake {
  0%, 100% { transform: translate(0, 0); }
  20% { transform: translate(-5px, 3px); }
  40% { transform: translate(5px, -3px); }
  60% { transform: translate(-3px, 2px); }
  80% { transform: translate(3px, -2px); }
}

/* ---------- 일시정지 ----------
   #app 바깥의 고정 전체 화면 딤이다. 스테이지만 덮으면 사이드바의 배속·토글이 그대로
   눌리므로 반드시 화면 전체를 가린다. 층은 모달(20~30) 위, 타이틀(60)·리더보드(70) 아래.
   딤 배경 자체가 재개 버튼이라 커서도 포인터로 둔다. */
#pause-screen {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--modal-pad);
  background: radial-gradient(ellipse at 50% 45%, rgba(24,16,9,0.58), rgba(9,6,4,0.85));
  -webkit-backdrop-filter: blur(3px) saturate(0.75);
  backdrop-filter: blur(3px) saturate(0.75);
  cursor: pointer;
  animation: pauseDimIn 0.16s ease-out;
}
@keyframes pauseDimIn { from { opacity: 0; } to { opacity: 1; } }

.ps-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 11px;
  padding: 26px 38px 20px;
  border-radius: 22px;
  background: linear-gradient(#f9efd9, #ebdab5);
  box-shadow: 0 16px 46px rgba(0,0,0,0.55), inset 0 0 0 3px rgba(255,252,240,0.55),
              0 0 0 2px rgba(122,82,48,0.5);
  cursor: default;
  text-align: center;
  animation: pauseCardIn 0.24s cubic-bezier(0.23, 1, 0.32, 1) backwards;
}
@keyframes pauseCardIn {
  from { opacity: 0; transform: translateY(10px) scale(0.96); }
  to { opacity: 1; transform: none; }
}

/* 이모지 대신 CSS 두 막대 — 어떤 플랫폼에서도 같은 모양으로 크게 선다 */
.ps-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 58px;
  height: 58px;
  border-radius: 50%;
  background: linear-gradient(150deg, #ffd27a, #e08a1c);
  box-shadow: 0 3px 0 #ab6410, inset 0 2px 0 rgba(255,255,255,0.5),
              0 0 22px rgba(255,176,46,0.35);
}
.ps-mark::before, .ps-mark::after {
  content: '';
  width: 7px;
  height: 23px;
  border-radius: 3px;
  background: #6b3f10;
  box-shadow: inset 0 -1px 0 rgba(0,0,0,0.2);
}
.ps-title {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: 0.06em;
  font-size: 27px;
  color: #6b4a28;
  text-shadow: 0 1px 0 rgba(255,255,255,0.6);
}
.ps-resume {
  min-width: 176px;
  border: none;
  border-radius: 13px;
  padding: 12px 22px;
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: var(--ls-display);
  font-size: 18px;
  color: #fff;
  background: linear-gradient(#66bb6a, #3d8b40);
  box-shadow: 0 3px 0 #2c6630;
  text-shadow: 0 1px 2px rgba(0,0,0,0.28);
  cursor: pointer;
  transition: transform 0.08s, filter 0.15s;
}
@media (hover: hover) {
  .ps-resume:hover { filter: brightness(1.06); }
}
.ps-resume:active { transform: translateY(3px); box-shadow: none; }
.ps-hint { font-size: 12px; color: #8a7358; letter-spacing: 0.01em; }
.ps-hint b {
  display: inline-block;
  min-width: 20px;
  margin-right: 2px;
  padding: 1px 5px;
  border-radius: 5px;
  background: #fffaea;
  box-shadow: inset 0 0 0 1.5px #dcc389, 0 1px 0 #cbb079;
  color: #7a5230;
}
@media (max-width: 520px), (max-height: 460px) {
  .ps-card { gap: 9px; padding: 20px 26px 16px; border-radius: 18px; }
  .ps-mark { width: 48px; height: 48px; gap: 7px; }
  .ps-mark::before, .ps-mark::after { width: 6px; height: 19px; }
  .ps-title { font-size: 22px; }
  .ps-resume { min-width: 150px; padding: 11px 18px; font-size: 16.5px; }
}
@media (prefers-reduced-motion: reduce) {
  #pause-screen { animation: none; }
  .ps-card { animation: pauseCardFadeIn 0.2s ease-out backwards; }
  @keyframes pauseCardFadeIn { from { opacity: 0; } to { opacity: 1; } }
  .ps-resume { transition: none; }
  .ps-resume:active { transform: none; filter: brightness(0.94); }
}

/* HUD의 일시정지 버튼. 평소에는 사운드와 같은 중립 톤이고,
   멈춰 있는 동안만 호박색으로 물들어 "지금 이 버튼이 켜져 있다"를 한눈에 알린다. */
#btn-pause { background: linear-gradient(rgba(0,0,0,0.32), rgba(0,0,0,0.2)); }
.hud-btn.on {
  background: linear-gradient(#ffd27a, #e79a24);
  box-shadow: 0 2px 0 #a9660f, inset 0 1px 0 rgba(255,255,255,0.45);
  color: #5a3a12;
  text-shadow: 0 1px 0 rgba(255,255,255,0.35);
}
/* 타이틀·게임 오버에서 잠긴 도구 버튼 */
.hud-btn[disabled] {
  opacity: 0.38;
  cursor: default;
  filter: none;
  transform: none;
  pointer-events: none;
}

/* ---------- 온보딩 스포트라이트 튜토리얼 ----------
   첫 플레이어에게 실제 버튼을 하나씩 짚어 주는 관문. 타이틀(60)·리더보드(70) 위에 서야
   타이틀에서 "게임 방법"으로 열었을 때도 가려지지 않는다.

   딤을 그리는 것은 오버레이가 아니라 #tt-hole의 거대 box-shadow다. 구멍 하나를 남기고
   바깥 전부를 어둡게 칠하므로 clip-path 없이도 대상만 원래 밝기로 남고, 구멍의 이동은
   left/top/width/height 트랜지션 하나로 이어진다. 루트는 투명하고 클릭만 가로챈다. */
#tutorial {
  position: fixed;
  inset: 0;
  z-index: 80;
  background: transparent;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  animation: ttDimIn 0.2s ease-out;
}
@keyframes ttDimIn { from { opacity: 0; } to { opacity: 1; } }

/* 구멍 자체. 크기·위치·모서리는 UI._ttPlace()가 실측 rect로 넣는다.
   대상 요소는 건드리지 않는다 — transform: scale을 걸면 레이아웃과 히트 영역이 함께
   흔들리므로, '앞으로 튀어나온 느낌'은 구멍을 대상보다 살짝 키우고 둘레에 금색 링과
   글로우를 두르는 것으로 대신한다. */
#tt-hole {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
  border-radius: 16px;
  pointer-events: none;
  box-shadow: 0 0 0 9999px rgba(9,6,4,0.76);
  transition: left 0.3s cubic-bezier(0.22, 1, 0.36, 1),
              top 0.3s cubic-bezier(0.22, 1, 0.36, 1),
              width 0.3s cubic-bezier(0.22, 1, 0.36, 1),
              height 0.3s cubic-bezier(0.22, 1, 0.36, 1),
              border-radius 0.3s ease-out;
}
/* 스크롤·리사이즈로 대상이 움직인 재계산. 딤이 한 박자 늦게 따라오면 어긋나 보인다 */
#tt-hole.no-anim { transition: none; }

/* 상시 링 + 안쪽으로 스며드는 금빛. 구멍 안이 주변보다 밝게 '떠 보이는' 몫을 맡는다 */
#tt-hole::before {
  content: '';
  position: absolute;
  inset: -3px;
  border-radius: inherit;
  border: 2px solid rgba(255,208,116,0.92);
  box-shadow: 0 0 22px rgba(255,186,74,0.5), inset 0 0 30px rgba(255,206,126,0.22);
}
/* 바깥으로 한 겹 퍼지는 펄스 */
#tt-hole::after {
  content: '';
  position: absolute;
  inset: -3px;
  border-radius: inherit;
  border: 2px solid rgba(255,208,116,0.7);
  animation: ttPulse 1.7s cubic-bezier(0.16, 1, 0.3, 1) infinite;
}
@keyframes ttPulse {
  0% { transform: scale(1); opacity: 0.7; }
  65%, 100% { transform: scale(1.085); opacity: 0; }
}

/* 말풍선. 위/아래 배치와 좌우 위치는 JS가 실측해 넣고, 꼬리 x는 --tt-tail-x로 받는다 */
#tt-bubble {
  position: absolute;
  left: 0;
  top: 0;
  width: min(330px, calc(100vw - 28px));
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 14px 17px 12px;
  border-radius: 16px;
  background: linear-gradient(#f9efd9, #ebdab5);
  box-shadow: 0 14px 34px rgba(0,0,0,0.5), inset 0 0 0 2px rgba(255,252,240,0.6),
              0 0 0 2px rgba(122,82,48,0.5);
  animation: ttBubbleIn 0.24s cubic-bezier(0.23, 1, 0.32, 1) backwards;
}
@keyframes ttBubbleIn {
  from { opacity: 0; transform: translateY(6px) scale(0.97); }
  to { opacity: 1; transform: none; }
}

/* 꼬리. 말풍선과 같은 양피지를 45° 돌린 정사각형이라 테두리 두 변만 남기면 이음매가 없다 */
.tt-tail {
  position: absolute;
  left: var(--tt-tail-x, 50%);
  width: 15px;
  height: 15px;
  margin-left: -7.5px;
  transform: rotate(45deg);
  background: #f9efd9;
  box-shadow: -2px -2px 0 -0.5px rgba(122,82,48,0.5);
}
#tutorial[data-side="below"] .tt-tail { top: -7px; }
#tutorial[data-side="above"] .tt-tail {
  bottom: -7px;
  background: #ebdab5;
  box-shadow: 2px 2px 0 -0.5px rgba(122,82,48,0.5);
}
/* 옆에 붙는 경우(대상이 세로로 화면을 다 먹는 폰 가로)는 꼬리를 지운다 */
#tutorial[data-side="free"] .tt-tail { display: none; }

.tt-step {
  font-family: var(--font-display);
  font-size: 11.5px;
  letter-spacing: 0.16em;
  color: #b0906a;
}
.tt-title {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: 0.03em;
  font-size: 20px;
  line-height: 1.25;
  color: #6b4a28;
  text-shadow: 0 1px 0 rgba(255,255,255,0.6);
}
.tt-text {
  font-size: 14px;
  line-height: 1.72;
  color: #6d5b41;
  word-break: keep-all;
}
.tt-text b { color: #96551a; font-weight: 700; }

.tt-foot {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 3px;
  padding-top: 10px;
  border-top: 1px solid rgba(122,82,48,0.16);
}

/* 진행 점. 되돌아가는 조작이 없으므로 순수 표시이며, 단계 수는 "n / 5"가 함께 읽어 준다 */
.tt-dots {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 auto;
}
.tt-dot {
  width: 7px;
  height: 7px;
  border-radius: 99px;
  background: #d3bd93;
  box-shadow: inset 0 0 0 1px rgba(122,82,48,0.26);
  transition: width 0.22s cubic-bezier(0.16, 1, 0.3, 1), background 0.22s ease-out;
}
.tt-dot.on {
  width: 18px;
  background: linear-gradient(#f0a92c, #d98210);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.45);
}
/* 지나온 단계는 알약까지 늘리지 않고 색만 남긴다 */
.tt-dot.past { background: #e0bd7d; }

.tt-skip {
  flex: 0 0 auto;
  border: none;
  border-radius: 8px;
  padding: 6px 2px;
  background: none;
  font-size: 12.5px;
  color: #a08b6a;
  cursor: pointer;
  transition: color 0.15s ease-out;
}
@media (hover: hover) { .tt-skip:hover { color: #7a5230; } }

/* 마지막 단계에서 "시작하기!"로 바뀌어도 폭이 흔들리지 않게 넉넉히 잡는다 */
.tt-next {
  flex: 0 0 auto;
  min-width: 92px;
  border: none;
  border-radius: 11px;
  padding: 9px 16px;
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: var(--ls-display);
  font-size: 15px;
  color: #fff;
  background: linear-gradient(#66bb6a, #3d8b40);
  box-shadow: 0 3px 0 #2c6630;
  text-shadow: 0 1px 2px rgba(0,0,0,0.28);
  cursor: pointer;
  transition: transform 0.08s, filter 0.15s;
}
@media (hover: hover) { .tt-next:hover { filter: brightness(1.06); } }
.tt-next:active { transform: translateY(3px); box-shadow: none; }

@media (max-width: 520px), (max-height: 520px) {
  #tt-bubble { gap: 6px; padding: 12px 14px 10px; border-radius: 14px; }
  .tt-title { font-size: 18px; }
  .tt-text { font-size: 13.5px; line-height: 1.68; }
  .tt-next { min-width: 82px; padding: 8px 13px; font-size: 14px; }
}
/* 폰 가로. 말풍선이 화면 높이의 절반을 넘지 않게 본문만 줄인다 */
@media (max-height: 430px) {
  .tt-step { display: none; }
  .tt-text { font-size: 13px; line-height: 1.6; }
}

@media (prefers-reduced-motion: reduce) {
  #tutorial { animation: none; }
  /* 구멍은 순간이동하고 링은 멈춘 채로 남는다 */
  #tt-hole { transition: none; }
  #tt-hole::after { animation: none; opacity: 0.45; transform: scale(1.04); }
  #tt-bubble { animation: ttFade 0.18s ease-out backwards; }
  .tt-dot { transition: none; }
  .tt-next { transition: none; }
  .tt-next:active { transform: none; filter: brightness(0.94); }
  @keyframes ttFade { from { opacity: 0; } to { opacity: 1; } }
}

/* ---------- 타이틀 화면 ----------
   로딩이 끝나고 게임에 들어가기 전 한 번 멈추는 관문. 배경은 실제 아레나 아트를
   흐리고 어둡게 깔아 "이 숲으로 들어간다"는 인상을 만들고, 그 위에 양피지·황동
   재질의 문장(紋章)과 게임 이름만 세운다. 장식은 전부 CSS이며 캔버스를 쓰지 않는다. */
#title-screen {
  position: fixed;
  inset: 0;
  z-index: 60; /* 모달(25~30)·툴팁(30)보다 위. 타이틀 위로 올라오는 UI는 없다 */
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: #0d1410;
  opacity: 1;
  transition: opacity 0.5s ease-out;
  /* 노치·홈 인디케이터 위로 버튼이 물리지 않게. 배경 층은 inset:0이라 영향받지 않는다 */
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
}
/* 시작 클릭 — 화면 전체가 살짝 다가오며 사라진다 */
#title-screen.is-leaving {
  opacity: 0;
  pointer-events: none;
}
#title-screen.is-leaving .ts-inner { transform: scale(1.06); }
#title-screen.is-leaving .ts-scene { transform: scale(1.1); }

/* '게임 방법' — 투어가 짚을 게임 화면이 보이도록 타이틀만 잠시 비킨다. display:none이
   아니라 가시성만 끄므로 레이아웃·배경 애니메이션은 그대로 남고, 투어가 닫히면 즉시
   원래 자리로 돌아온다. 게임은 시작되지 않았고 타이틀 BGM도 그대로다. */
#title-screen.is-preview {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.22s ease-out, visibility 0s linear 0.22s;
}

/* 배경: 아레나 아트를 크게 확대·블러해 숲의 색과 빛만 남긴다 */
.ts-scene {
  position: absolute;
  inset: -6%;
  background: #16281a url('../assets/ui/bg_forest_arena.png') center/cover no-repeat;
  filter: blur(8px) saturate(1.02) brightness(0.5) contrast(1.06);
  transform: scale(1.04);
  transition: transform 0.6s ease-out;
  animation: tsDrift 34s ease-in-out infinite alternate;
}
/* 위아래 어둠 + 중앙 온기. 문장과 버튼이 어떤 배경 위에서도 읽히게 하는 층 */
.ts-scene::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(72% 52% at 50% 38%, rgba(255,214,132,0.30), rgba(255,196,96,0.06) 55%, rgba(0,0,0,0) 74%),
    radial-gradient(128% 100% at 50% 42%, rgba(0,0,0,0) 26%, rgba(5,11,7,0.68) 72%, rgba(3,7,5,0.95)),
    linear-gradient(180deg, rgba(6,12,8,0.72), rgba(6,12,8,0.06) 34%, rgba(4,9,6,0.55) 76%, rgba(3,7,5,0.88));
}
@keyframes tsDrift {
  from { transform: scale(1.04) translate3d(-1.2%, 0.6%, 0); }
  to { transform: scale(1.11) translate3d(1.2%, -0.8%, 0); }
}

/* 북쪽 석문에서 내려오는 빛기둥 두 줄 */
.ts-shafts {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(101deg, rgba(0,0,0,0) 27%, rgba(255,229,158,0.16) 33%, rgba(255,229,158,0) 40%),
    linear-gradient(94deg, rgba(0,0,0,0) 44%, rgba(255,240,190,0.11) 48%, rgba(255,240,190,0) 55%),
    linear-gradient(86deg, rgba(0,0,0,0) 60%, rgba(255,226,150,0.13) 66%, rgba(255,226,150,0) 74%);
  /* 위에서 내려오는 빛이라 아래로 갈수록 흐려진다 */
  -webkit-mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,0.35) 62%, rgba(0,0,0,0) 92%);
  mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,0.35) 62%, rgba(0,0,0,0) 92%);
  mix-blend-mode: screen;
  animation: tsShafts 9s ease-in-out infinite alternate;
}
@keyframes tsShafts {
  from { opacity: 0.55; }
  to { opacity: 1; }
}

/* 떠다니는 빛 모트 12개. 크기·시작 위치·속도만 인라인 없이 nth-child로 흩는다 */
.ts-motes { position: absolute; inset: 0; pointer-events: none; }
.ts-motes i {
  position: absolute;
  bottom: -6%;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: radial-gradient(circle, #fff6cf, rgba(255,214,110,0.15) 62%, rgba(255,214,110,0));
  opacity: 0;
  animation: tsMote 13s linear infinite;
}
/* --ms는 개체별 크기 배율. transform은 keyframe이 전부 덮으므로 여기서 직접 주지 않는다 */
.ts-motes i:nth-child(1)  { left: 7%;  animation-duration: 15s; animation-delay: -1s;  --ms: 1;    }
.ts-motes i:nth-child(2)  { left: 16%; animation-duration: 19s; animation-delay: -7s;  --ms: 0.6;  }
.ts-motes i:nth-child(3)  { left: 25%; animation-duration: 12s; animation-delay: -3s;  --ms: 1.3;  }
.ts-motes i:nth-child(4)  { left: 33%; animation-duration: 17s; animation-delay: -11s; --ms: 0.7;  }
.ts-motes i:nth-child(5)  { left: 42%; animation-duration: 21s; animation-delay: -5s;  --ms: 1.05; }
.ts-motes i:nth-child(6)  { left: 49%; animation-duration: 14s; animation-delay: -15s; --ms: 0.5;  }
.ts-motes i:nth-child(7)  { left: 58%; animation-duration: 18s; animation-delay: -9s;  --ms: 1.15; }
.ts-motes i:nth-child(8)  { left: 66%; animation-duration: 16s; animation-delay: -2s;  --ms: 0.65; }
.ts-motes i:nth-child(9)  { left: 74%; animation-duration: 20s; animation-delay: -13s; --ms: 0.95; }
.ts-motes i:nth-child(10) { left: 82%; animation-duration: 13s; animation-delay: -6s;  --ms: 1.25; }
.ts-motes i:nth-child(11) { left: 89%; animation-duration: 22s; animation-delay: -17s; --ms: 0.55; }
.ts-motes i:nth-child(12) { left: 95%; animation-duration: 15s; animation-delay: -10s; --ms: 0.85; }
@keyframes tsMote {
  0%   { opacity: 0; transform: translate3d(0, 0, 0) scale(calc(0.8 * var(--ms, 1))); }
  12%  { opacity: 0.85; }
  50%  { transform: translate3d(22px, -52vh, 0) scale(var(--ms, 1)); }
  82%  { opacity: 0.6; }
  100% { opacity: 0; transform: translate3d(-14px, -104vh, 0) scale(calc(0.75 * var(--ms, 1))); }
}

/* ---- 중앙 기둥 ---- */
.ts-inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(8px, 1.6vh, 16px);
  padding: 24px clamp(16px, 5vw, 48px);
  text-align: center;
  transform: scale(1);
  transition: transform 0.5s ease-out;
}

/* 문장: 황동 링 안에 게임의 클로버 아이콘. 링만 천천히 돌고 아이콘은 고정이다 */
.ts-crest {
  position: relative;
  width: clamp(76px, 13vh, 116px);
  height: clamp(76px, 13vh, 116px);
  display: grid;
  place-items: center;
  animation: tsRise 0.7s cubic-bezier(0.2, 0.9, 0.25, 1) both;
}
.ts-crest-ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px dashed rgba(255, 205, 106, 0.5);
  box-shadow:
    0 0 0 6px rgba(255, 196, 84, 0.07),
    0 0 34px rgba(255, 196, 84, 0.3) inset;
  animation: tsSpin 26s linear infinite;
}
.ts-crest::after {
  content: '';
  position: absolute;
  inset: -30%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255,213,120,0.34), rgba(255,213,120,0) 66%);
  animation: tsPulse 3.6s ease-in-out infinite alternate;
  pointer-events: none;
}
.ts-crest-art {
  position: relative;
  z-index: 1;
  width: 62%;
  height: 62%;
  object-fit: contain;
  filter: drop-shadow(0 3px 6px rgba(0,0,0,0.55));
}
@keyframes tsSpin { to { transform: rotate(360deg); } }
@keyframes tsPulse {
  from { opacity: 0.55; transform: scale(0.94); }
  to { opacity: 1; transform: scale(1.06); }
}

/* 제목: 양피지 금색 그라디언트 + 두툼한 외곽선. data-text 복제층이 뒤에서 광원을 낸다 */
.ts-title {
  position: relative;
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: 0.03em;
  font-size: clamp(38px, 8.4vw, 82px);
  line-height: 1.06;
  color: #ffeec4;
  background: linear-gradient(180deg, #fff6dc 6%, #ffd070 52%, #e79a2a 96%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 3px 0 rgba(80, 42, 10, 0.85)) drop-shadow(0 10px 22px rgba(0,0,0,0.6));
  animation: tsRise 0.7s cubic-bezier(0.2, 0.9, 0.25, 1) 0.06s both;
}
/* 글자 뒤 은은한 후광. 그라디언트 클립이 걸린 본체 대신 이 복제층이 빛을 낸다 */
.ts-title::before {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  z-index: -1;
  color: rgba(255, 198, 92, 0.42);
  -webkit-text-fill-color: rgba(255, 198, 92, 0.42);
  filter: blur(16px);
  animation: tsGlow 4.2s ease-in-out infinite alternate;
}
@keyframes tsGlow {
  from { opacity: 0.5; }
  to { opacity: 1; }
}

/* 부제: 좌우로 뻗는 금선 사이의 작은 라벨 */
.ts-sub {
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: var(--font-display);
  font-size: clamp(13px, 1.9vw, 17px);
  letter-spacing: 0.22em;
  color: #f0dcae;
  text-shadow: 0 2px 6px rgba(0,0,0,0.7);
  animation: tsRise 0.7s cubic-bezier(0.2, 0.9, 0.25, 1) 0.12s both;
}
.ts-sub i {
  width: clamp(26px, 7vw, 62px);
  height: 1px;
  background: linear-gradient(90deg, rgba(226,190,120,0), rgba(226,190,120,0.9));
}
.ts-sub i:last-child { transform: scaleX(-1); }

.ts-tag {
  font-size: clamp(12px, 1.7vw, 15px);
  color: rgba(233, 224, 198, 0.72);
  text-shadow: 0 2px 5px rgba(0,0,0,0.65);
  animation: tsRise 0.7s cubic-bezier(0.2, 0.9, 0.25, 1) 0.18s both;
}

/* 시작 버튼: 사이드바의 big-btn 재질을 크게 키운 형태 */
.ts-start {
  position: relative;
  margin-top: clamp(10px, 2.4vh, 26px);
  min-width: clamp(200px, 34vw, 288px);
  padding: clamp(12px, 1.9vh, 17px) 34px;
  border: none;
  border-radius: 16px;
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: 0.06em;
  font-size: clamp(18px, 2.7vw, 25px);
  color: #fff;
  cursor: pointer;
  overflow: hidden;
  background: linear-gradient(#ffc04c, #ef8a10);
  box-shadow:
    0 5px 0 #a75c07,
    0 12px 26px rgba(0,0,0,0.45),
    0 0 0 1px rgba(255,226,160,0.4) inset;
  text-shadow: 0 2px 3px rgba(120, 60, 10, 0.55);
  transition: transform 0.14s cubic-bezier(0.22, 0.9, 0.28, 1),
              filter 0.14s ease-out,
              box-shadow 0.14s ease-out;
  animation: tsRise 0.7s cubic-bezier(0.2, 0.9, 0.25, 1) 0.26s both;
}
.ts-start-label { position: relative; z-index: 1; }
/* 좌에서 우로 훑는 광택 */
.ts-start::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 46%;
  left: -60%;
  background: linear-gradient(105deg, rgba(255,255,255,0) , rgba(255,255,255,0.5), rgba(255,255,255,0));
  animation: tsSheen 3.4s ease-in-out infinite;
}
@keyframes tsSheen {
  0%, 55% { left: -60%; }
  100% { left: 130%; }
}
@media (hover: hover) {
  .ts-start:hover { transform: translateY(-2px); filter: brightness(1.07); }
}
.ts-start:active {
  transform: translateY(3px);
  box-shadow: 0 2px 0 #a75c07, 0 6px 14px rgba(0,0,0,0.4);
}

/* 게임 방법·리더보드 — 시작 버튼 아래 한 줄로 나란히 선다. 리더보드가 숨겨진 환경에서는
   게임 방법 하나만 남고 gap이 접혀 자연히 가운데 정렬된다 */
.ts-links {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 9px;
  margin-top: clamp(7px, 1.4vh, 14px);
}
.ts-links .ts-rank { margin-top: 0; }
#title-guide { animation-delay: 0.3s; }

/* 시작 버튼과 경쟁하면 안 되는 보조 동선이라 테두리만 남긴 황동 알약으로 둔다.
   리더보드는 API가 없는 환경에서 UI가 아예 숨긴다 */
.ts-rank {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: clamp(7px, 1.4vh, 14px);
  padding: 7px 17px;
  border: 1px solid rgba(226,190,120,0.38);
  border-radius: 999px;
  background: rgba(18,13,8,0.34);
  font-family: var(--font-display);
  font-size: clamp(12px, 1.6vw, 14px);
  letter-spacing: 0.06em;
  color: rgba(238,220,178,0.86);
  text-shadow: 0 2px 5px rgba(0,0,0,0.6);
  cursor: pointer;
  transition: color 0.16s ease-out, border-color 0.16s ease-out,
              background 0.16s ease-out, transform 0.14s ease-out;
  animation: tsRise 0.7s cubic-bezier(0.2, 0.9, 0.25, 1) 0.34s both;
}
.ts-rank-mark { font-size: 0.95em; }
@media (hover: hover) {
  .ts-rank:hover {
    color: #ffe7b4;
    border-color: rgba(255,214,140,0.72);
    background: rgba(44,30,15,0.5);
    transform: translateY(-1px);
  }
}
.ts-rank:active { transform: translateY(1px); }

/* 역대 최고 기록 한 줄. 기록이 없으면 UI가 숨긴다 */
.ts-best {
  margin-top: 4px;
  font-size: clamp(11px, 1.5vw, 13.5px);
  letter-spacing: 0.04em;
  color: rgba(226, 214, 186, 0.66);
  text-shadow: 0 2px 5px rgba(0,0,0,0.6);
  animation: tsRise 0.7s cubic-bezier(0.2, 0.9, 0.25, 1) 0.32s both;
}
.ts-best b { color: #ffd884; font-weight: 400; font-family: var(--font-display); }

@keyframes tsRise {
  from { opacity: 0; transform: translateY(16px); }
  to { opacity: 1; transform: translateY(0); }
}

/* 폰 가로처럼 세로가 짧은 화면: 문장을 줄이고 간격을 압축한다 */
@media (max-height: 480px) {
  .ts-inner { gap: 6px; padding: 12px 20px; }
  .ts-crest { width: 58px; height: 58px; }
  .ts-title { font-size: clamp(30px, 6.6vw, 46px); }
  .ts-tag { display: none; }
  .ts-start { margin-top: 8px; padding: 10px 26px; }
  .ts-links { margin-top: 6px; }
  .ts-rank { margin-top: 6px; padding: 5px 14px; }
}
/* 아주 좁은 세로 화면: 부제 선을 짧게 */
@media (max-width: 380px) {
  .ts-sub { letter-spacing: 0.14em; gap: 8px; }
}

@media (prefers-reduced-motion: reduce) {
  #title-screen { transition: opacity 0.2s linear; }
  #title-screen.is-leaving .ts-inner,
  #title-screen.is-leaving .ts-scene { transform: none; }
  .ts-scene, .ts-shafts, .ts-motes, .ts-crest::after,
  .ts-crest-ring, .ts-title::before, .ts-start::after { animation: none; }
  .ts-motes { display: none; }
  .ts-crest, .ts-title, .ts-sub, .ts-tag, .ts-start, .ts-rank, .ts-best { animation: none; }
  .ts-rank { transition: none; }
  .ts-rank:hover, .ts-rank:active { transform: none; }
}

/* =========================================================================
   F3 · UI/UX 폴리시 (docs/VFX_POLISH_PLAN.md 4장)
   F3-01 모달 스프링 · 인벤토리 칩 등장/소멸
   F3-02 리빌 1막 '기대'
   F3-03 모달 앰비언트 배경 (CSS 전용)
   ========================================================================= */

/* ---------- F3-01 모달 상태 전환 스프링 ----------
   .modal-in / .modal-out은 UI._showModal / UI._hideModal이 붙인다. 열림은 딤 페이드 +
   카드 scale(0.95→1) 오버슛, 닫힘은 그 역방향이며 퇴장이 끝난 뒤에야 .hidden이 붙는다.
   일시정지·튜토리얼은 각자의 진입 연출을 이미 갖고 있어 퇴장에만 참여시킨다. */
#meta-modal.modal-in,
#codex-modal.modal-in,
#rank-modal.modal-in,
#account-modal.modal-in,
#recovery-modal.modal-in,
#sync-modal.modal-in,
#draft-modal.modal-in,
#mutation-modal.modal-in,
#modifier-modal.modal-in,
#gameover.modal-in {
  animation: modalDimIn 0.18s ease-out;
}
#meta-modal.modal-in > .modal-card,
#codex-modal.modal-in > .modal-card,
#rank-modal.modal-in > .modal-card,
#account-modal.modal-in > .modal-card,
#recovery-modal.modal-in > .modal-card,
#sync-modal.modal-in > .modal-card,
#draft-modal.modal-in > .modal-card,
#mutation-modal.modal-in > .modal-card,
#modifier-modal.modal-in > .modal-card,
#gameover.modal-in > .go-card {
  animation: modalCardIn 0.2s cubic-bezier(0.22, 1.55, 0.36, 1) backwards;
}

/* 퇴장 —— 클릭·포커스는 UI가 inert로 이미 끊었고, 여기서는 그림만 걷는다.
   id 선택자를 함께 쓰는 규칙(#pause-screen 등)을 이기려면 이쪽도 id로 특정해야 한다. */
#meta-modal.modal-out,
#codex-modal.modal-out,
#rank-modal.modal-out,
#account-modal.modal-out,
#recovery-modal.modal-out,
#sync-modal.modal-out,
#draft-modal.modal-out,
#mutation-modal.modal-out,
#modifier-modal.modal-out,
#gameover.modal-out,
#pause-screen.modal-out,
#tutorial.modal-out {
  pointer-events: none;
  animation: modalDimOut 0.15s ease-in forwards;
}
.modal-out > .modal-card,
.modal-out > .go-card,
.modal-out > .ps-card {
  animation: modalCardOut 0.15s ease-in forwards;
}

@keyframes modalDimIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes modalDimOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes modalCardIn {
  0% { opacity: 0; transform: scale(0.95) translateY(8px); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}
@keyframes modalCardOut {
  from { opacity: 1; transform: scale(1); }
  to { opacity: 0; transform: scale(0.96); }
}

@media (prefers-reduced-motion: reduce) {
  /* 퇴장은 UI._startModalExit이 아예 건너뛴다. 진입만 위치 이동 없는 페이드로 남긴다. */
  #meta-modal.modal-in > .modal-card,
  #codex-modal.modal-in > .modal-card,
  #rank-modal.modal-in > .modal-card,
  #draft-modal.modal-in > .modal-card,
  #mutation-modal.modal-in > .modal-card,
  #modifier-modal.modal-in > .modal-card,
  #gameover.modal-in > .go-card {
    animation: modalDimIn 0.16s ease-out backwards;
  }
}

/* ---------- F3-01 인벤토리 칩 등장·소멸 ----------
   UI._buildInventory가 직전 스냅샷과 비교해 바뀐 칩에만 클래스를 붙인다. 매 갱신마다
   전체 칩이 다시 튀지 않으며 _invSig 리빌드 최적화도 그대로다.
   .chip.transcend는 무한 후광(transcendChip)을 갖고 있어 animation 단축 속성이 통째로
   덮인다 —— 드래프트 카드(rqPrism)와 같은 방식으로 두 애니메이션을 함께 선언한다. */
.chip.chip-in { animation: chipPopIn 0.28s cubic-bezier(0.22, 1.6, 0.36, 1) backwards; }
.chip.chip-bump { animation: chipBump 0.24s ease-out; }
.chip.chip-out {
  pointer-events: none;
  animation: chipFadeOut 0.22s ease-in forwards;
}
.chip.transcend.chip-in {
  animation: chipPopIn 0.28s cubic-bezier(0.22, 1.6, 0.36, 1) backwards,
             transcendChip 1.3s 0.28s ease-in-out infinite alternate;
}
.chip.transcend.chip-bump {
  animation: chipBump 0.24s ease-out,
             transcendChip 1.3s 0.24s ease-in-out infinite alternate;
}
.chip.transcend.chip-out { animation: chipFadeOut 0.22s ease-in forwards; }

@keyframes chipPopIn {
  0% { opacity: 0; transform: scale(0.42) translateY(7px); }
  62% { opacity: 1; transform: scale(1.12) translateY(0); }
  100% { opacity: 1; transform: scale(1); }
}
@keyframes chipBump {
  0% { transform: scale(1); }
  45% { transform: scale(1.13); }
  100% { transform: scale(1); }
}
@keyframes chipFadeOut {
  from { opacity: 1; transform: scale(1); }
  to { opacity: 0; transform: scale(0.55); }
}

@media (prefers-reduced-motion: reduce) {
  .chip.chip-in, .chip.chip-bump { animation: none; }
  .chip.transcend.chip-in,
  .chip.transcend.chip-bump { animation: transcendChip 1.3s ease-in-out infinite alternate; }
  .chip.chip-out, .chip.transcend.chip-out { animation: none; opacity: 0; }
}

/* ---------- 리빌 1막 '기대' — 초월 전용 (2026-07-28 재설계) ----------
   #reveal-antic은 #stage 안의 오버레이다. 층은 #bulk-result(0) 위, #birth(6) 아래.
   영웅·전설·신화의 기대 막은 UI.ANTIC_MS에서 0으로 내려 재생되지 않는다 —— 아래 규칙은
   초월(6등급)에서만 켜지며, 게임에서 가장 희귀한 결과답게 화면을 통째로 장악한다.

   모든 단계가 --dur(1450ms) 하나에 비례한다. 퍼센트로 읽는 4막 구성:
     0~10%   암전  —— 비네트가 바깥에서 안으로 닫히며 화면이 거의 검게 눌린다
     10~40%  수렴  —— 프리즘 빛줄기·알갱이가 사방에서 중앙으로 가속하며 빨려 든다
     40~85%  임계  —— 광점이 다섯 번 맥동하며 부풀고 균열·충격파·화면 진동이 겹친다
     85~100% 폭발  —— 백색 섬광이 화면을 덮고 그 위로 카드가 태어난다
   막이 끝나면 노드가 통째로 비워지므로 상시 도는 애니메이션이 남지 않는다. */
#reveal-antic {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  pointer-events: none;
}
/* 폭발이 절정인 순간 카드가 뜬다. 레이어를 카드 아래(z-index 0)로 내리고 흰색을 천천히
   걷어야 흰색 → 어둠 → 카드 플래시로 한 프레임 끊기지 않는다. 길이는 UI.ANTIC_OUT_MS. */
#reveal-antic.ra-out {
  z-index: 0;
  animation: raOut 260ms ease-out forwards;
}
@keyframes raOut { from { opacity: 1; } to { opacity: 0; } }

/* 1막 암전 —— 화면을 눌러 어둡게. 중앙만 남기고 주변이 닫힌다 */
.ra-veil {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 50%,
    rgba(0,0,0,0.10) 0%, rgba(0,0,0,0.58) 56%, rgba(0,0,0,0.84) 100%);
  opacity: 0;
  animation: raVeil var(--dur, 500ms) cubic-bezier(0.4, 0, 0.6, 1) forwards;
}
@keyframes raVeil { from { opacity: 0; } to { opacity: 1; } }

/* 초월 —— 거의 완전한 암전. 비네트를 1.9배에서 1배로 좁혀 어둠이 바깥에서 안으로
   덮쳐 오게 만든다(게임 화면이 뒤로 물러나는 인상). 0.1초 만에 대부분 닫힌다. */
#reveal-antic.transcend .ra-veil {
  background: radial-gradient(circle at 50% 50%,
    rgba(0,14,18,0.74) 0%, rgba(0,0,0,0.94) 40%, #000 76%);
  animation: raVeilT var(--dur, 1450ms) cubic-bezier(0.16, 0.9, 0.3, 1) forwards;
}
@keyframes raVeilT {
  0%   { opacity: 0;    transform: scale(1.9); }
  10%  { opacity: 0.94; transform: scale(1.24); }
  32%  { opacity: 1;    transform: scale(1.02); }
  86%  { opacity: 1;    transform: scale(1); }
  100% { opacity: 0.82; transform: scale(1.01); }
}

/* 2막 수렴 —— 중심을 향해 날아드는 프리즘 빛줄기. 회전축이 화면 중앙이고
   translateX가 곧 중심까지의 거리다. 안쪽 끝이 밝은 혜성 꼬리 모양이 된다. */
.ra-streaks { position: absolute; inset: 0; }
.ra-streaks i {
  position: absolute;
  left: 50%; top: 50%;
  width: var(--len, 200px);
  height: var(--w, 3px);
  margin-top: calc(var(--w, 3px) / -2);
  border-radius: 999px;
  transform-origin: 0% 50%;
  background: linear-gradient(to right, var(--c, #fff), rgba(255,255,255,0) 88%);
  filter: drop-shadow(0 0 8px var(--c, #fff));
  opacity: 0;
  /* 강한 ease-in —— 멀리서는 느긋하다가 중심에 가까울수록 빨라진다 */
  animation: raStreak calc(var(--dur, 1450ms) * var(--sp, 0.58)) var(--delay, 0ms)
             cubic-bezier(0.66, 0, 0.86, 0.14) forwards;
}
@keyframes raStreak {
  0%   { opacity: 0; transform: rotate(var(--a, 0deg)) translateX(var(--d, 420px)) scaleX(0.55); }
  14%  { opacity: 0.95; }
  70%  { opacity: 1; }
  100% { opacity: 0; transform: rotate(var(--a, 0deg)) translateX(-16px) scaleX(0.06); }
}

/* 3막 임계 —— 중앙에서 자라는 빛의 씨앗. 막이 끝나는 순간 카드의 폭발과 이어진다 */
.ra-core {
  position: absolute;
  left: 50%; top: 50%;
  width: 26px; height: 26px;
  margin: -13px 0 0 -13px;
  border-radius: 50%;
  background: radial-gradient(circle, #fff 0%, var(--c, #fff) 46%, transparent 74%);
  box-shadow: 0 0 26px var(--c, #fff), 0 0 64px var(--c, #fff);
  opacity: 0;
  animation: raCore var(--dur, 500ms) cubic-bezier(0.55, 0, 0.85, 0.3) forwards;
}
@keyframes raCore {
  0% { opacity: 0; transform: scale(0.2); }
  55% { opacity: 0.85; transform: scale(1.15); }
  100% { opacity: 1; transform: scale(2.7); }
}
/* 초월 —— 다섯 번 맥동하며 매번 더 크게 부푼다. 마지막 8%에서 화면 밖까지 터진다. */
#reveal-antic.transcend .ra-core {
  background: radial-gradient(circle, #fff 0%, #a8fff6 40%, rgba(201,182,255,0.55) 62%, transparent 78%);
  box-shadow: 0 0 34px #a8fff6, 0 0 90px #66fcf1, 0 0 150px rgba(102,252,241,0.5);
  animation: raCoreT var(--dur, 1450ms) linear forwards;
}
@keyframes raCoreT {
  0%   { opacity: 0;    transform: scale(0.10); }
  12%  { opacity: 0.40; transform: scale(0.52); }
  26%  { opacity: 0.72; transform: scale(0.88); }
  36%  { opacity: 0.48; transform: scale(0.66); }
  46%  { opacity: 0.90; transform: scale(1.28); }
  55%  { opacity: 0.58; transform: scale(0.94); }
  64%  { opacity: 1;    transform: scale(1.75); }
  72%  { opacity: 0.70; transform: scale(1.30); }
  80%  { opacity: 1;    transform: scale(2.40); }
  86%  { opacity: 0.82; transform: scale(1.90); }
  92%  { opacity: 1;    transform: scale(4.20); }
  100% { opacity: 1;    transform: scale(11); }
}

/* 사방에서 중심으로 빨려 드는 빛 알갱이. 각도·거리·지연·크기는 인라인 변수다 */
.ra-motes { position: absolute; inset: 0; }
.ra-motes i {
  position: absolute;
  left: 50%; top: 50%;
  width: var(--sz, 4px);
  height: var(--sz, 4px);
  margin: calc(var(--sz, 4px) / -2) 0 0 calc(var(--sz, 4px) / -2);
  border-radius: 50%;
  background: var(--c, #fff);
  box-shadow: 0 0 9px var(--c, #fff);
  opacity: 0;
  animation: raMote calc(var(--dur, 500ms) * 0.75) var(--delay, 0ms)
             cubic-bezier(0.5, 0, 0.75, 0.2) forwards;
}
@keyframes raMote {
  0% { opacity: 0; transform: rotate(var(--a, 0deg)) translateX(var(--d, 200px)) scale(0.4); }
  22% { opacity: 1; }
  100% { opacity: 0.9; transform: rotate(var(--a, 0deg)) translateX(0) scale(1.3); }
}
/* 초월 —— 더 짧고 더 급하게 빨려 들어 마지막 순간까지 흐름이 끊기지 않는다 */
#reveal-antic.transcend .ra-motes i {
  animation: raMoteT calc(var(--dur, 1450ms) * 0.42) var(--delay, 0ms)
             cubic-bezier(0.72, 0, 0.9, 0.12) forwards;
}
@keyframes raMoteT {
  0%   { opacity: 0; transform: rotate(var(--a, 0deg)) translateX(var(--d, 300px)) scale(0.3); }
  18%  { opacity: 1; }
  86%  { opacity: 1; transform: rotate(var(--a, 0deg)) translateX(24px) scale(1.5); }
  100% { opacity: 0; transform: rotate(var(--a, 0deg)) translateX(0) scale(0.2); }
}

/* 3막 임계 —— 중앙에서 갈라져 나오는 방사형 균열. 지연(60~80%)에 흩어 뿌려
   한 번에 쩍 갈라지는 대신 연달아 터지는 파열로 읽히게 한다. */
.ra-cracks { position: absolute; inset: 0; }
.ra-cracks i {
  position: absolute;
  left: 50%; top: 50%;
  width: var(--len, 420px);
  height: 2px;
  margin-top: -1px;
  transform-origin: 0% 50%;
  background: linear-gradient(to right, #fff, var(--c, #a8fff6) 38%, rgba(255,255,255,0) 100%);
  filter: drop-shadow(0 0 10px var(--c, #fff));
  opacity: 0;
  animation: raCrack calc(var(--dur, 1450ms) * 0.3) var(--delay, 0ms)
             cubic-bezier(0.12, 0.9, 0.3, 1) forwards;
}
@keyframes raCrack {
  0%   { opacity: 0;    transform: rotate(var(--a, 0deg)) scaleX(0); }
  10%  { opacity: 1; }
  40%  { opacity: 0.9;  transform: rotate(var(--a, 0deg)) scaleX(1); }
  62%  { opacity: 0.45; }
  100% { opacity: 0;    transform: rotate(var(--a, 0deg)) scaleX(1.35); }
}

/* 3막 임계 —— 두 겹의 충격파. 균열과 같은 구간에서 바깥으로 밀려 나간다 */
.ra-shock {
  position: absolute;
  left: 50%; top: 50%;
  width: 60px; height: 60px;
  margin: -30px 0 0 -30px;
  border: 2px solid var(--c, #fff);
  border-radius: 50%;
  box-shadow: 0 0 20px var(--c, #fff), inset 0 0 14px var(--c, #fff);
  opacity: 0;
  animation: raShock calc(var(--dur, 1450ms) * 0.42) var(--delay, 0ms)
             cubic-bezier(0.1, 0.85, 0.25, 1) forwards;
}
@keyframes raShock {
  0%   { opacity: 0;    transform: scale(0.2); }
  12%  { opacity: 0.95; }
  100% { opacity: 0;    transform: scale(11); border-width: 1px; }
}

/* 4막 폭발 —— 마지막 15%에서 백색 섬광이 화면을 덮는다. 카드는 이 흰색 위에서 태어나고,
   .ra-out이 260ms에 걸쳐 걷어 내는 동안 카드의 transcendFlash가 이어받는다. */
.ra-burst {
  position: absolute;
  inset: -20%;
  background: radial-gradient(circle at 50% 50%,
    #fff 0%, rgba(255,255,255,0.96) 24%, rgba(168,255,246,0.72) 46%,
    rgba(102,252,241,0.26) 66%, transparent 82%);
  opacity: 0;
  animation: raBurst var(--dur, 1450ms) linear forwards;
}
@keyframes raBurst {
  0%   { opacity: 0;    transform: scale(0.35); }
  84%  { opacity: 0;    transform: scale(0.35); }
  90%  { opacity: 0.55; transform: scale(0.82); }
  96%  { opacity: 0.95; transform: scale(1.06); }
  100% { opacity: 1;    transform: scale(1.16); }
}

/* 3막 임계의 미세 화면 진동. #stage 자체 transform이라 stageShake와 같은 자리이며,
   UI._fadeAntic이 카드의 .shake를 붙이기 전에 클래스를 걷어 낸다.
   흔들림 끄기·동작 줄이기에서는 UI._quake가 아예 걸지 않는다. */
#stage.antic-quake { animation: anticQuake var(--antic-dur, 1450ms) linear; }
@keyframes anticQuake {
  0%, 40% { transform: translate(0, 0); }
  46%  { transform: translate(-1px, 1px); }
  52%  { transform: translate(1px, -1px); }
  58%  { transform: translate(-2px, 1px); }
  64%  { transform: translate(2px, -2px); }
  70%  { transform: translate(-3px, 2px); }
  76%  { transform: translate(3px, -2px); }
  82%  { transform: translate(-4px, 3px); }
  88%  { transform: translate(5px, -3px); }
  93%  { transform: translate(-6px, 4px); }
  97%  { transform: translate(4px, -3px); }
  100% { transform: translate(0, 0); }
}

/* 동작 줄이기에서는 기대 막 자체를 재생하지 않는다(UI._anticMs가 0을 돌려준다).
   혹시 남은 노드가 있어도 여기서 완전히 지운다. */
@media (prefers-reduced-motion: reduce) {
  #reveal-antic { display: none !important; }
  #stage.antic-quake { animation: none; }
}

/* ---------- F3-03 모달 앰비언트 배경 (CSS 전용) ----------
   도감·도전과제·게임 오버가 열려 있는 동안에만 오로라 두 겹과 부유 모트가 흐른다.
   z-index:-1이라 모달 딤 위·카드 아래에 깔린다(카드의 position을 건드리지 않는다).
   닫히면 .hidden의 display:none이 서브트리를 통째로 내려 애니메이션이 실제로 멈추므로
   게임 루프 성능과 무관하다. Canvas·JS 파티클은 쓰지 않는다. */
#codex-modal, #meta-modal, #gameover { overflow: hidden; }

#codex-modal::before,
#meta-modal::before,
#gameover::before {
  content: '';
  position: absolute;
  inset: -14%;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(38% 30% at 24% 26%, rgba(126, 214, 164, 0.30), transparent 70%),
    radial-gradient(34% 26% at 77% 20%, rgba(255, 202, 112, 0.28), transparent 72%),
    radial-gradient(46% 34% at 52% 86%, rgba(126, 182, 255, 0.22), transparent 74%);
  animation: modalAurora 26s ease-in-out infinite alternate;
}
/* 도전과제는 금빛, 게임 오버는 잿빛 잔불 쪽으로 살짝 기울인다 */
#meta-modal::before {
  background:
    radial-gradient(38% 30% at 22% 24%, rgba(255, 208, 120, 0.32), transparent 70%),
    radial-gradient(32% 26% at 78% 22%, rgba(255, 168, 90, 0.24), transparent 72%),
    radial-gradient(46% 34% at 50% 88%, rgba(190, 220, 150, 0.20), transparent 74%);
}
#gameover::before {
  background:
    radial-gradient(38% 30% at 26% 24%, rgba(255, 148, 96, 0.24), transparent 70%),
    radial-gradient(34% 26% at 76% 22%, rgba(255, 206, 126, 0.22), transparent 72%),
    radial-gradient(48% 36% at 50% 88%, rgba(120, 196, 190, 0.20), transparent 74%);
}
@keyframes modalAurora {
  0%   { transform: translate3d(-2.5%, -1.5%, 0) scale(1.04) rotate(-1.5deg); opacity: 0.72; }
  50%  { transform: translate3d(1.5%, 2%, 0) scale(1.12) rotate(1deg); opacity: 1; }
  100% { transform: translate3d(3%, -2%, 0) scale(1.06) rotate(2deg); opacity: 0.8; }
}

/* 부유 모트 —— 타일 배경을 정확히 한 타일 높이(260px)만큼 밀어 이음매 없이 순환한다.
   transform 애니메이션이라 리페인트 없이 합성기에서 돈다. */
#codex-modal::after,
#meta-modal::after,
#gameover::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  /* 한 주기(-260px)만큼 밀려 올라가도 화면 아래쪽이 비지 않도록 넉넉히 길게 잡는다 */
  top: -60%;
  height: 320%;
  z-index: -1;
  pointer-events: none;
  opacity: 0.45;
  background-image:
    radial-gradient(circle, rgba(255,246,214,0.85) 0 1.1px, transparent 1.9px),
    radial-gradient(circle, rgba(255,232,176,0.6) 0 1.4px, transparent 2.3px),
    radial-gradient(circle, rgba(206,255,232,0.5) 0 1px, transparent 1.7px);
  background-size: 210px 260px, 330px 260px, 158px 130px;
  background-position: 0 0, 40px 120px, 110px 60px;
  animation: modalMotes 30s linear infinite;
}
@keyframes modalMotes {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(0, -260px, 0); }
}

@media (prefers-reduced-motion: reduce) {
  #codex-modal::before, #meta-modal::before, #gameover::before,
  #codex-modal::after, #meta-modal::after, #gameover::after { animation: none; }
}

/* F3-02 · 10연 요약 그리드의 순차 등장(42ms 간격)은 .bulk-card의 --i 스태거가 이미
   담당한다. 동작 줄이기에서는 튀어오름을 걷고 같은 리듬의 페이드만 남긴다 ——
   .best의 정적 scale(1.09)은 키프레임이 transform을 건드리지 않아 그대로 유지된다. */
@media (prefers-reduced-motion: reduce) {
  .bulk-card,
  .bulk-card.best {
    animation: bulkCardFade 0.2s calc(var(--i, 0) * 42ms) backwards ease-out;
  }
  @keyframes bulkCardFade { from { opacity: 0; } to { opacity: 1; } }
}

/* =========================================================================
   모바일 오버레이 레이아웃 (터치 전용 · 가로/세로 2모드)
   -------------------------------------------------------------------------
   판정은 pointer: coarse 하나이고, 화면 방향이 모드를 가른다.
     · 가로 → --fit-mode: overlay   아레나 전체가 화면에 들어간다.
     · 세로 → --fit-mode: portrait  아레나 중심부만 잘라 크게 보여 준다.
   hover 질의는 판정에 쓰지 않는다 —— S펜 폰 등이 hover: hover로 답해 실기기가
   오버레이에서 빠지는 사고가 있었다(2026-07-28). 주 입력이 마우스인 기기는
   창을 아무리 줄여도 기존 row/stack/split 레이아웃을 그대로 쓴다.

   공통 구조: 캔버스가 화면을 채우고 HUD·액션 버튼·사이드바가 전부 그 위에 뜬다.
   사이드바는 아래에서 끌어올리는 바텀 시트(#sidebar.drawer-open)가 되고
   손잡이(#drawer-handle)는 상시 노출된다. 배율 계산은 UI.fitStage()가 맡는다
   (--fit-mode가 SSoT).

   비용이 큰 대형 backdrop-filter는 쓰지 않는다 —— 반투명 단색 + 1px 테두리로만
   층을 만든다. 모든 전이는 transform/opacity/box-shadow에 한정한다.
   ========================================================================= */

/* 손잡이와 모바일 액션 레이어는 터치 모드에서만 존재한다.
   다른 모드에서는 DOM에 있어도 그리지 않는다. */
#drawer-handle { display: none; }
/* 세로 전용 표면 셋 —— 풀블리드 배경, 시트 탭 줄, 시트 밖 닫기 표면.
   다른 모드에서는 DOM에 있어도 그리지 않는다. */
#portrait-bg, #sheet-tabs, #sheet-scrim { display: none; }
/* 세로 모드의 크롭 래퍼. 그 밖에서는 레이아웃에서 통째로 사라져(display: contents)
   #stage-fit이 예전 그대로 main/그리드의 직접 자식처럼 행동한다. */
#stage-crop { display: contents; }
#mobile-actions {
  position: fixed;
  inset: 0;
  z-index: 13;
  pointer-events: none;
}
#mobile-actions.hidden { display: none; }

/* ---- 터치 공통(가로·세로 둘 다) ---- */
@media (pointer: coarse) {
  :root {
    --fit-gap: 0px;
    /* 접힌 서랍에서 손잡이만 남기는 높이. 홈 인디케이터 여백을 포함한다.
       --pt-peek는 순수 px라 UI.fitStage()가 parseFloat로 그대로 읽는다. */
    --pt-peek: 46px;
    --drawer-peek: calc(var(--pt-peek) + var(--safe-b));
    --drawer-lift: 0px;
    /* 세로 모드의 시트 탭 줄 높이. 가로에서는 탭 줄이 없어 0이다.
       UI.fitStage()가 parseFloat로 그대로 읽으므로 순수 px를 유지한다. */
    --pt-tabs: 0px;
  }

  html, body { height: 100%; overflow: hidden; }
  body { padding: 0; display: block; }

  /* 캔버스 밖 거터. 잔디 타일 위에 숲 그늘과 비네트를 덧씌워 캔버스 밖까지 같은 장면으로 읽히게 한다. */
  body::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background:
      radial-gradient(ellipse 78% 108% at 50% 50%, rgba(12,20,12,0) 46%, rgba(9,15,10,0.78) 100%),
      linear-gradient(168deg, rgba(58,86,46,0.92), rgba(24,42,26,0.94) 58%, rgba(12,22,15,0.96));
  }

  #app { position: relative; z-index: 1; width: 100%; height: 100%; }
  main { display: block; height: 100%; }
  #stage { border-radius: 0; box-shadow: none; }

  /* ---- 상단 HUD 오버레이 ---- */
  #hud {
    /* 칩 본문이 20 → 15px으로 줄어드는 자리라 보조 텍스트도 같은 비율로 따라 내린다.
       13.5px을 그대로 두면 카운트다운·차원 이름이 본문과 거의 같은 크기가 되어
       위계가 사라진다. */
    --hud-sub: 11.5px;
    position: fixed;
    z-index: 14;
    top: calc(4px + var(--safe-t));
    left: calc(6px + var(--safe-l));
    right: calc(6px + var(--safe-r));
    margin: 0;
    padding: 3px 6px;
    gap: 5px;
    border-radius: 12px;
    background: rgba(26,17,10,0.5);
    box-shadow: inset 0 0 0 1px rgba(255,228,176,0.18);
  }
  .hud-item {
    font-size: 15px;
    padding: 3px 8px;
    gap: 4px;
    background: rgba(0,0,0,0.34);
    min-width: 0;
    flex: 0 1 auto;
  }
  .hud-item img { width: 19px; height: 19px; }
  .hud-item.wide { min-width: 0; }
  /* 가로 폭이 가장 귀하다. 최고 기록은 게임 오버·도전과제에서 볼 수 있다. */
  .hud-item:has(#hud-best) { display: none; }
  #speed-ctl { margin-left: auto; }
  #hud-tools { gap: 4px; }
  #speed-ctl, #hud-tools { flex: 0 0 auto; }
  /* 오버레이 HUD에서 도구는 아이콘만 남긴다(뜻은 title·aria-label이 지킨다). */
  .hud-btn .hud-label { display: none; }
  .hud-btn { padding: 4px 8px; font-size: 15px; }
  .hud-btn .hud-ico { font-size: 16px; }

  /* ---- 액션 오버레이 공통 ----
     #mobile-actions는 서랍(#sidebar)의 transform 밖에 있는 별도 고정 레이어다.
     변환된 조상은 고정 위치 자식의 컨테이닝 블록이 되어 버튼이 서랍을 따라 사라지기 때문이다.
     캐릭터를 고르면 서랍이 선택 패널만큼 올라온다. 액션 레이어도 같은 만큼 함께 올라가
     버튼이 서랍 뒤로 숨지 않는다. 레이어 하나만 옮기므로 버튼의 :active transform과
     싸우지 않고, 레이아웃도 건드리지 않는다. */
  #mobile-actions {
    transform: translateY(calc(-1 * var(--drawer-lift, 0px)));
    transition: transform 0.26s cubic-bezier(0.22, 0.9, 0.28, 1), opacity 0.2s ease-out;
  }
  /* 서랍이 펼쳐지면 액션은 그 아래로 숨는다. 반쯤 가려진 채로 남기지 않고 함께 접는다. */
  body.drawer-open #mobile-actions { opacity: 0; pointer-events: none; }
  .actions { display: flex; flex-direction: column; gap: 8px; }
  #mobile-actions .big-btn {
    pointer-events: auto;
    box-shadow: 0 3px 0 rgba(0,0,0,0.35), 0 0 0 1px rgba(255,232,180,0.22);
  }
  #mobile-actions .big-btn span { white-space: nowrap; }

  /* ---- 하단 서랍(바텀 시트) ---- */
  #sidebar {
    position: fixed;
    z-index: 15;
    left: 0;
    right: 0;
    bottom: 0;
    width: auto;
    min-height: 0;
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 0 calc(10px + var(--safe-r)) calc(8px + var(--safe-b)) calc(10px + var(--safe-l));
    border-radius: 18px 18px 0 0;
    background: rgba(28,19,11,0.9);
    box-shadow: 0 -6px 22px rgba(0,0,0,0.45), inset 0 0 0 1px rgba(255,228,176,0.16);
    overflow-y: auto;
    overscroll-behavior: contain;
    transform: translateY(calc(100% - var(--drawer-peek) - var(--drawer-lift, 0px)));
    transition: transform 0.26s cubic-bezier(0.22, 0.9, 0.28, 1);
  }
  #sidebar.drawer-open { transform: translateY(0); }

  /* 손잡이는 스크롤해도 늘 서랍 맨 위에 붙어 있다 */
  #drawer-handle {
    order: 0;
    position: sticky;
    top: 0;
    z-index: 3;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    flex: 0 0 auto;
    /* 접힌 서랍에서 밖으로 나와 있는 딱 그만큼(--drawer-peek)이 손잡이 자신이다.
       홈 인디케이터 여백은 손잡이의 아래 패딩으로 흡수해, 접혔을 때 서랍 내용이
       손가락 아래로 삐져나오지 않게 한다. */
    height: var(--drawer-peek);
    padding-bottom: var(--safe-b);
    margin: 0 calc(-10px - var(--safe-r)) 0 calc(-10px - var(--safe-l));
    border: 0;
    border-radius: 18px 18px 0 0;
    background: rgba(28,19,11,0.96);
    color: #f4e3c4;
    font-family: var(--font-display);
    font-size: 13px;
    letter-spacing: var(--ls-display);
    cursor: pointer;
    touch-action: none;
  }
  .dh-grip {
    display: block;
    width: 44px;
    height: 4px;
    border-radius: 999px;
    background: rgba(255,228,176,0.5);
    transition: transform 0.22s ease-out, background-color 0.22s ease-out;
  }
  #sidebar.drawer-open .dh-grip { background: rgba(255,200,90,0.85); transform: scaleX(0.72); }
  .dh-label { opacity: 0.72; }

  /* 선택 패널도 손잡이 바로 아래에 붙어 있어야, 접힌 서랍이 살짝 올라올 때 그것만 보인다 */
  #selected-panel { order: 1; position: sticky; top: var(--drawer-peek); z-index: 2; flex: 0 0 auto; }
  /* 내용 순서: 토글·칩 바 → 인벤토리 → 로그 → 설정 → 메타 진입점.
     새 게임은 서랍에 없다 —— 상단 HUD 도구 줄로 올라갔다. */
  .panel.actions { order: 2; }
  .panel.grow { order: 3; flex: 0 0 auto; overflow: visible; }
  #log-wrap { order: 4; }
  .settings-panel { order: 5; }

  .panel { padding: 8px; }
  /* 서랍 자체가 스크롤 컨테이너다. 인벤토리 안에 또 스크롤을 만들지 않는다. */
  #inventory {
    grid-template-columns: repeat(auto-fill, minmax(52px, 1fr));
    overflow: visible;
    min-height: 0;
  }
  #log { height: 40px; font-size: 12px; }

  @media (prefers-reduced-motion: reduce) {
    #sidebar { transition: none; }
    #mobile-actions { transition: none; }
    .dh-grip { transition: none; }
  }
}

/* ---- 가로(landscape): 아레나 전체 + 좌우 액션 ---- */
@media (pointer: coarse) and (orientation: landscape) {
  :root { --fit-mode: overlay; }

  /* 캔버스는 화면 정중앙. 논리 1000x720은 그대로 두고 CSS 배율만 바꾼다. */
  #stage-fit {
    position: fixed;
    left: 50%;
    top: 50%;
    margin: 0;
    transform: translate(-50%, -50%) scale(var(--stage-scale));
    transform-origin: center center;
  }

  /* 오버레이 HUD는 한 줄을 넘길 수 없다. 넘치면 칩이 줄어들 수 있게 열어 둔다. */
  #hud { flex-wrap: nowrap; }
  .speed-btn, .hud-btn { min-height: 38px; min-width: 40px; }
  /* 도구가 다섯 개인 줄이라 구분선은 자리를 거의 쓰지 않게 접는다 */
  .hud-sep { height: 18px; margin: 0 1px; }

  /* 좌우 액션. #mobile-actions는 화면 전체를 덮는 입력 투과 레이어이고,
     그 안의 버튼만 입력을 받는다. 서랍이 접혀 있어도 늘 보인다. */
  #mobile-actions .gacha-group {
    position: absolute;
    pointer-events: auto;
    right: calc(8px + var(--safe-r));
    bottom: calc(var(--drawer-peek) + 8px);
    width: 148px;
    display: flex;
    flex-direction: column;
    gap: 7px;
  }
  #mobile-actions #btn-luck {
    position: absolute;
    pointer-events: auto;
    left: calc(8px + var(--safe-l));
    bottom: calc(var(--drawer-peek) + 8px);
    width: 138px;
  }
  #mobile-actions .big-btn { min-height: 48px; padding: 8px; font-size: 15px; }
  #mobile-actions .big-btn.ten { min-height: 44px; font-size: 13.5px; }
  #mobile-actions #btn-luck img { width: 28px; height: 28px; }

  #sidebar { height: 55dvh; max-height: 55dvh; }
}

/* =========================================================================
   ---- 세로(portrait): 크롭 확대 뷰 + 하단 액션 바 ----
   세로에서는 1000x720을 통째로 넣으면 아레나가 손톱만 해진다. 논리 캔버스와
   렌더·좌표는 그대로 두고(계약), 양옆 장식 숲만 잘라 전투 영역(x 229~771)에
   여유를 더한 --portraitCropW 폭만 화면에 보여 준다. 자르는 일은 #stage-crop의
   overflow: hidden 하나가 하고, 캔버스 자신은 여전히 1000x720 * --stage-scale이다.
   입력 좌표는 캔버스의 getBoundingClientRect 기준이라 크롭과 무관하게 정확하다.
   보이는 논리 폭의 SSoT는 CFG.canvas.portraitCropW이고, 배율·크롭 창의 위치는
   UI.fitStage()의 'portrait' 분기가 --pt-top/--pt-h로 넣는다.
   ========================================================================= */
@media (pointer: coarse) and (orientation: portrait) {
  :root {
    --fit-mode: portrait;
    /* 하단 액션 바·시트 탭 줄의 고정 높이. 순수 px라 UI.fitStage()가 그대로 읽는다.
       세로 여유 = 뷰포트 - HUD 아래 - (--pt-bar + --pt-tabs + --pt-peek + safe-b). */
    --pt-btn-h: 62px;
    --pt-bar: 76px;
    --pt-tabs: 50px;
    /* 세로에서는 서랍을 손잡이로 끌어올리지 않는다. 시트는 [보유•강화]·[설정] 탭이 열고
       닫힌 상태에서는 화면 밖으로 완전히 내려간다 —— 그래서 엿보기 높이가 0이다.
       (선택 들어올림 --drawer-lift는 그대로 살아 있다.) */
    --pt-peek: 0px;
    --drawer-peek: 0px;
    --pt-top: 60px;
    --pt-h: 60vh;
  }

  /* ---- 풀블리드 배경 ----
     크롭 창 위아래로 남는 화면 전체를 맵과 이어지는 숲 그림 두 장이 채운다.
     가로 배율은 캔버스와 같은 논리 배율(1000 논리폭 = 1000px * --stage-scale)이고
     중심도 화면 중앙이라, 캔버스가 잘린 것과 똑같은 창으로 정렬된다.
     스트립이 모자라는 아주 긴 화면은 background-color(가장자리 평균 톤)가 잇는다. */
  #portrait-bg {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
  }
  .pt-strip {
    position: absolute;
    left: 0;
    right: 0;
    display: block;
    background-repeat: no-repeat;
    background-size: calc(1000px * var(--stage-scale, 1)) auto;
  }
  /* 스트립 이미지는 캔버스와 만나는 행이 원본 맵 가장자리와 픽셀 연속이 되도록
     오프라인 미러 블렌딩되어 있다(2026-07-29). 이음새 쪽은 덮지 않고,
     화면 바깥 먼 가장자리만 어둡게 눌러 시선을 아레나로 모은다. */
  .pt-strip::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
  }
  .pt-strip-top {
    top: 0;
    height: var(--pt-top);
    background-image: url(../assets/ui/bg_forest_portrait_top.jpg);
    background-position: center bottom;
    background-color: #0d2116;
  }
  .pt-strip-top::after {
    background: linear-gradient(to bottom,
      rgba(8,18,12,0.72) 0%, rgba(8,18,12,0.12) 34%, rgba(8,18,12,0) 70%);
  }
  .pt-strip-bot {
    top: calc(var(--pt-top) + var(--pt-h));
    bottom: 0;
    background-image: url(../assets/ui/bg_forest_portrait_bottom.jpg);
    background-position: center top;
    background-color: #16311c;
  }
  .pt-strip-bot::after {
    background: linear-gradient(to top,
      rgba(8,18,12,0.72) 0%, rgba(8,18,12,0.12) 34%, rgba(8,18,12,0) 70%);
  }
  /* 배경이 화면을 통째로 채우므로 거터 그라디언트는 세로에서 쓰지 않는다. */
  body::before { display: none; }

  /* 크롭 창. HUD 아래에서 시작해 액션 바 위에서 끝난다. 양옆으로 잘려 나간 자리는
     거터와 같은 숲 톤으로 이어 붙여 잘린 티가 나지 않게 한다. */
  #stage-crop {
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    top: var(--pt-top);
    height: var(--pt-h);
    z-index: 1;
    overflow: hidden;
    background: linear-gradient(168deg, rgba(48,74,40,0.96), rgba(20,36,22,0.98));
  }
  /* 캔버스 쪽 이음새 음영은 쓰지 않는다 — 스트립이 픽셀 연속이라 경계 자체가 없고,
     여기에 그늘을 얹으면 오히려 가로 띠로 보인다(2026-07-29 실기기 QA). */
  /* 캔버스는 크롭 창의 정중앙. 세로(720 * scale)는 전부 보이고 가로만 잘린다. */
  #stage-fit {
    position: absolute;
    left: 50%;
    top: 50%;
    margin: 0;
    transform: translate(-50%, -50%) scale(var(--stage-scale));
    transform-origin: center center;
  }

  /* HUD는 좁은 폭에 맞춰 두 줄로 접힌다. 폰 세로 폭(≈390px)에서
     1줄 = 골드·라이프·웨이브 칩·유닛 수가 한 줄에 들어가도록 칩을 한 단 조인다.
     2줄 = 배속(왼쪽) · 도구 아이콘(오른쪽). 도구·배속의 44px 탭 상자는 그대로 둔다. */
  #hud { flex-wrap: wrap; row-gap: 4px; gap: 4px; padding: 3px 5px; }
  .hud-item { font-size: 14.5px; padding: 3px 7px; gap: 3px; }
  .hud-item img { width: 18px; height: 18px; }
  #speed-ctl { margin-left: 0; }
  #hud-tools { margin-left: auto; gap: 2px; }
  /* 2줄 = 배속 + 도구 다섯 개(아이콘만)를 폰 세로 폭 한 줄에 붙잡아 두기 위한 조임.
     탭 상자는 44px 그대로다 —— 줄이는 건 사이 여백뿐이다. 그래도 모자라는 아주 좁은
     기기에서는 도구 줄이 통째로 다음 줄로 내려간다(#hud가 wrap). */
  .speed-btn { min-width: 44px; padding: 6px 7px; }
  .hud-sep { height: 20px; margin: 0; }

  /* ---- 하단 액션 바 ----
     시트 탭 줄 바로 위에 붙는 한 줄이다. 세 버튼은 하나의 세트다 —— 높이·코너 반경·
     그림자·타이포 스케일이 모두 같고, 가운데 뽑기만 폭이 넓은 주 버튼이다.
     .gacha-group은 display: contents로 풀어 두 자식이 이 바의 flex 항목이 되게 하고,
     자리는 order로 정한다. */
  #mobile-actions {
    inset: auto 0 calc(var(--pt-tabs) + var(--safe-b)) 0;
    height: var(--pt-bar);
    display: flex;
    align-items: flex-end;
    justify-content: center;
    gap: 8px;
    padding: 0 calc(9px + var(--safe-r)) 8px calc(9px + var(--safe-l));
  }
  #mobile-actions .gacha-group { display: contents; }
  /* 세트 공통. 개별 규칙은 폭·타이포만 다르게 잡는다. */
  #mobile-actions .big-btn {
    height: var(--pt-btn-h);
    min-height: var(--pt-btn-h);
    border-radius: 15px;
    padding: 7px 6px;
    gap: 6px;
    box-shadow: 0 3px 0 rgba(0,0,0,0.36), 0 0 0 1px rgba(255,232,180,0.24),
                0 6px 14px rgba(20,10,2,0.28);
  }
  #mobile-actions .big-btn:active {
    box-shadow: 0 1px 0 rgba(0,0,0,0.36), 0 0 0 1px rgba(255,232,180,0.24),
                inset 0 2px 5px rgba(30,14,4,0.3);
  }
  #mobile-actions .big-btn span { align-items: flex-start; }
  #mobile-actions .big-btn small { font-size: 10.5px; opacity: 0.92; }
  /* 좌우 보조 버튼은 서로 완전히 같은 상자다. */
  #mobile-actions #btn-luck,
  #mobile-actions #btn-gacha10 {
    flex: 1 1 0;
    max-width: 112px;
    font-size: 13.5px;
  }
  #mobile-actions #btn-luck { order: 1; }
  #mobile-actions #btn-luck img { width: 25px; height: 25px; }
  #mobile-actions #btn-gacha10 { order: 3; }
  #mobile-actions #btn-gacha10 .ten-mark {
    font-size: 11.5px;
    padding: 4px 5px;
    border-radius: 8px;
  }
  #mobile-actions #btn-gacha10 #gacha10-info { font-size: 10.5px; }
  /* 주인공. 같은 높이·같은 반경이지만 폭이 넓고 타이포가 한 단 크며,
     따뜻한 외곽 광원 하나만 더 걸친다. */
  #mobile-actions #btn-gacha {
    order: 2;
    flex: 0 0 auto;
    width: min(44vw, 196px);
    padding: 7px 10px;
    font-size: 16px;
    gap: 8px;
    box-shadow: 0 3px 0 rgba(0,0,0,0.36), 0 0 0 1px rgba(255,232,180,0.32),
                0 8px 20px rgba(224,124,10,0.28);
  }
  #mobile-actions #btn-gacha small { font-size: 11px; }
  #mobile-actions #btn-gacha .pity-ring { width: 40px; height: 40px; }
  #mobile-actions #btn-gacha .pity-ring img { width: 27px; height: 27px; }

  /* ---- 시트 탭 줄 ----
     화면 맨 아래 상시 노출되는 알약 두 개. 누른 탭의 시트만 올라오고,
     같은 탭을 다시 누르면 닫힌다. 시트(#sidebar, z 15)보다 위에 있어
     열린 상태에서도 다른 탭으로 바로 건너갈 수 있다. */
  #sheet-tabs {
    display: flex;
    position: fixed;
    z-index: 16;
    left: 0;
    right: 0;
    bottom: 0;
    height: calc(var(--pt-tabs) + var(--safe-b));
    padding: 0 calc(10px + var(--safe-r)) var(--safe-b) calc(10px + var(--safe-l));
    align-items: center;
    justify-content: center;
    gap: 10px;
    /* 닫힌 시트의 맨 윗줄이 이 바로 뒤에 정확히 겹친다. 반투명이면 그 내용이
       비쳐 글자가 겹쳐 보이므로 불투명하게 덮는다. */
    background: linear-gradient(rgba(26,17,10,0.98), rgba(16,10,6,1));
    box-shadow: inset 0 1px 0 rgba(255,228,176,0.16);
  }
  .sheet-tab {
    flex: 1 1 0;
    max-width: 170px;
    min-height: 42px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    border: 0;
    border-radius: 999px;
    background: rgba(62,43,24,0.9);
    box-shadow: inset 0 0 0 1px rgba(255,228,176,0.22);
    color: #f4e3c4;
    font-family: var(--font-display);
    font-size: 14px;
    letter-spacing: var(--ls-display);
    cursor: pointer;
    transition: transform 0.14s cubic-bezier(0.22, 0.9, 0.28, 1),
                background-color 0.16s ease-out, color 0.16s ease-out;
  }
  .sheet-tab .st-ico { font-size: 15px; }
  .sheet-tab.on {
    background: linear-gradient(#ffc85e, #e29310);
    color: #3d2205;
    box-shadow: inset 0 0 0 1px rgba(255,244,208,0.5), 0 0 12px rgba(226,147,16,0.35);
  }
  .sheet-tab:active { transform: translateY(1px) scale(0.97); }

  /* 시트 밖을 눌러 닫는 표면. HUD(z 14)보다 아래라 배속·도구는 계속 누를 수 있다. */
  #sheet-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 13;
    background: rgba(6,11,8,0.44);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.22s ease-out;
  }
  body.sheet-open #sheet-scrim { opacity: 1; pointer-events: auto; }

  @media (prefers-reduced-motion: reduce) {
    .sheet-tab { transition: none; }
    .sheet-tab:active { transform: none; }
    #sheet-scrim { transition: none; }
  }

  /* 크롭 창 밖은 보이지 않는다. 스테이지 안에 떠 있는 오버레이는 보이는 논리 폭
     (CFG.canvas.portraitCropW = 640 → 중심 500 기준 x 180~820) 안으로 끌어들인다.
     이 요소들은 #stage 안이라 단위가 곧 논리 좌표다. */
  #boss-intro { left: 196px; width: 288px; }
  #banner { left: 180px; right: 180px; }
  #bulk-result { max-width: 600px; }
  #toast { max-width: 600px; }

  /* ---- 버튼식 시트 ----
     같은 #sidebar 바텀 시트를 재사용하되, 세로에서는 손잡이가 없다. 탭 줄 바로 위에
     올라오고 닫히면 화면 밖으로 완전히 내려간다(--drawer-peek: 0).
     "선택 들어올림"(--drawer-lift)은 그대로다 —— 캐릭터를 고르면 시트 맨 위의
     선택 패널만 그만큼 올라온다. */
  #drawer-handle { display: none; }
  #sidebar {
    bottom: calc(var(--pt-tabs) + var(--safe-b));
    /* 시트는 자기 내용만큼만 올라온다. 섹션이 짧은 [설정]에서 빈 갈색 판이 남지 않는다. */
    height: auto;
    max-height: 56dvh;
    padding-top: 10px;
    padding-bottom: 10px;
    border-radius: 20px 20px 0 0;
  }
  /* 시트 섹션 토글. 열린 탭에 속한 것만 남기고 나머지는 아예 접는다. */
  body.sheet-owned #sidebar .toggle-row,
  body.sheet-owned #sidebar .settings-panel,
  body.sheet-owned #sidebar .lab-entry { display: none; }
  body.sheet-settings #sidebar .chip-bars,
  body.sheet-settings #sidebar .panel.grow,
  body.sheet-settings #sidebar #log-wrap { display: none; }
  /* 폭이 넉넉하니 인벤토리 칸을 조금 키운다 */
  #inventory { grid-template-columns: repeat(auto-fill, minmax(56px, 1fr)); }
}
