/* Components — 해리포터 특별전 태블릿 공통 (hp-tablet 원본)
   QR 스캔 태블릿(scanner/)과 완드 체험 태블릿(wand/)이 **실제로 같이 쓰는** 컴포넌트만 둔다.
   tokens.css 를 먼저 로드할 것. 색상은 토큰만 사용 — 비토큰 값은 ⚠ 주석으로 표기(Figma 원본값).
   ⚠ 26-09-29 키 컬러 네이비 전환: 보라 계열 비토큰 값도 tokens.css 와 같은 규칙(색상 228° · 채도·명도 유지)으로
     옮겼다. 주석의 "시안 원본값" 중 보라였던 것(#ECF0FF · #9EB1FD · #464C64 · 시작 화면 라디얼)은 옮긴 값이다.

   ⚠ 26-09-28 정리: 관람객 A안 컴포넌트 14종(Box · BoxSet · Circle · TraitHexGrid · TraitHexBox ·
     Button · ResultButton · ResultCard · ResultContent · InputField · Header · ProgressBar ·
     ProgressSummary · KeywordChip · IconCircleButton · DialogBar)은 두 태블릿이 쓰지 않아 걷어냈다.
     그것들은 관람객 앱(visitor/design-system/components.css)에 남아 있다.
   화면마다 다른 규칙(스캔·선택·결과 등)은 각 앱 styles.css 에 있다 — 두 앱에서 같이 쓰게 된
   규칙만 여기로 옮긴다(README).
   담긴 것: Popup · Toast · QuestButton(+ stage) · TextGenerate · StepTitle · StartScreen(+ 배경) · LanguageToggle · Login.
   크기 단위 --ds-u(스캐너 프레임 숫자) · --ds-wu(완드 프레임 숫자)는 tokens.css 참고. */

/* ============ Popup (공통 팝업) — 스캐너 NoticePanel 기반 · Figma 4172:36544 · 4180:36996 ============
   26-09-28 부터 두 태블릿의 팝업은 이것 하나다. 예전 확인 다이얼로그(1194:17081, 반투명 청록 판)를 걷어내고
   스캐너 알림 판(NoticePanel)의 디자인을 그대로 올렸다: [명판(헤어라인 + 육각 플레이트 + 제목)] 이
   [어두운 패널(본문 + 확인 버튼)] 윗변에 31 걸친다. 확인 버튼은 26-10-01 시안(4774:12664 · 12727 · 12881)부터
   QuestButton small 대신 PillButton(밝은 알약, 아래)이다.

     <div class="popup" role="dialog" aria-labelledby="…">
       <div class="popup__plaque"><span class="popup__plate"><h2 class="popup__title" id="…">제목</h2></span></div>
       <div class="popup__panel">
         <p class="popup__body">본문 <span class="popup__em">강조</span></p>
         <button class="pill-btn"><span class="pill-btn__inner"><span class="pill-btn__label">확인</span></span></button>   ← PillButton
       </div>
     </div>

   질문이 여럿이면 .popup__body 대신 .popup__qna(Q&A 목록)를 쓴다(아래).
   화면 위에 띄울 때는 .popup-overlay 로 감싸고 --show 를 붙인다(스크림 + 가운데 정렬 + 등장).
   전체 화면 알림(스캐너 소진 · 체험 중인 QR)처럼 화면 자체가 팝업이면 오버레이 없이 놓는다.

   크기 — 값은 스캐너 시안(가로 1133×744) 숫자 그대로이고 공통 단위 --ds-u 를 곱한다(tokens.css 참고).
     기본값은 앱의 --u(없으면 1px — 프리뷰). 스캐너는 그대로, 완드는 :root 에 --ds-u 를 준다.
   ⚠ 색 #090E1E · #F5F5F5 · #ECF0FF 는 시안 원본값(비토큰)이다. 명판·헤어라인 SVG 는 assets/popup/ 에 있다
     (이 CSS 기준 상대 경로라 두 앱 사본에서 그대로 풀린다). */
/* 단위는 부분마다 읽는다 — 본문·제목만 따로 쓰일 때(프리뷰 타이포 표 등)도 크기가 풀리게 */
.popup, .popup__plaque, .popup__plate, .popup__title, .popup__panel, .popup__body,
.popup__qna, .popup__qna-item, .popup__q, .popup__a { --pu: var(--ds-u, var(--u, 1px)); }
.popup {
  display: flex; flex-direction: column; align-items: center;
  width: calc(638 * var(--pu));
  max-width: 100%;
}
/* 명판 — [헤어라인 105] 10 [플레이트 408×60] 10 [헤어라인 105]. 패널 윗변에 31 걸친다(시안 mb -31). */
.popup__plaque {
  position: relative; z-index: 2;
  display: flex; align-items: center; gap: calc(10 * var(--pu));
  margin-bottom: calc(-31 * var(--pu));
}
.popup__plaque::before,
.popup__plaque::after {
  content: "";
  width: calc(105 * var(--pu)); height: calc(2.5 * var(--pu));
  background: url("assets/popup/rule-left.svg") center/100% 100% no-repeat;
}
.popup__plaque::after { background-image: url("assets/popup/rule-right.svg"); }   /* 왼쪽 선은 판 쪽이 진하다(rule-left.svg 는 뒤집은 채로 저장) */
/* 플레이트 — 26-10-01 Figma popup/header 4778:13062: 판 408×60 위에 [바깥 붓 테두리(Rectangle 240652609 430×65)]와
   [안쪽 판(Rectangle 240652610 410×61 · #020B2F · 테두리 2.5 네이비 → 흰 라일락)]을 겹친다. 바깥 테두리가 전의 가는 선에서
   손으로 그린 듯한 채운 선으로 바뀌었다. 두 겹 모두 시안 SVG 상자(번짐 자리 포함) 그대로의 자리 · 크기로 놓는다.
   제목은 위에서 13(시안). ⚠ 시안의 #F3EEFE · #F1ECFF 는 키 컬러 규칙(26-09-29)대로 네이비 톤(#EEF1FE · #ECF0FF)으로 둔다. */
.popup__plate {
  position: relative; isolation: isolate;
  display: flex; align-items: flex-start; justify-content: center;
  width: calc(408 * var(--pu)); height: calc(60 * var(--pu));
  padding: calc(13 * var(--pu)) calc(28 * var(--pu)) 0;
  box-sizing: border-box;
}
.popup__plate::before,
.popup__plate::after {
  content: ""; position: absolute; z-index: -1;
  background: center / 100% 100% no-repeat;
  pointer-events: none;
}
.popup__plate::before {                                  /* 바깥 붓 테두리 — 430×65 · 위 -2 · 가운데 +0.5, SVG 상자 432.788×65.981 */
  left: calc(-11.79 * var(--pu)); top: calc(-2.37 * var(--pu));
  width: calc(432.788 * var(--pu)); height: calc(65.981 * var(--pu));
  background-image: url("assets/popup/plaque-back.svg");
}
.popup__plate::after {                                   /* 안쪽 판 — 410×61 · 왼쪽 -0.5, SVG 상자 416.436×66 */
  left: calc(-3.698 * var(--pu)); top: calc(-2.501 * var(--pu));
  width: calc(416.436 * var(--pu)); height: calc(66 * var(--pu));
  background-image: url("assets/popup/plaque-front.svg");
}
.popup__title {
  /* Figma accent/head1/SB-24 — Hahmlet SemiBold 24 / lh 1.3 / ls -1% */
  margin: 0;
  font-family: var(--font-display);
  font-size: calc(24 * var(--pu)); font-weight: 600;
  letter-spacing: -0.01em; line-height: 1.3;
  color: #ECF0FF;                                        /* ⚠ 시안 원본값 */
  white-space: nowrap;
}
body[data-lang="en"] .popup__title { font-family: var(--font-display-en); font-weight: 400; }   /* 영문 = Harry Beast(한 굵기) */
.popup__panel {
  position: relative; z-index: 1;
  width: 100%;
  display: flex; flex-direction: column; align-items: center;
  padding: calc(90 * var(--pu)) calc(94 * var(--pu)) calc(50 * var(--pu));
  gap: calc(40 * var(--pu));
  box-sizing: border-box;
  background: #090E1E;                                   /* ⚠ 시안 원본값 */
  border-top: 1px solid rgba(255, 255, 255, 0.2);
  border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
/* 본문 — ⚠ 시안의 안쪽 컨테이너는 501 폭이라 패널 패딩 안쪽(450)을 좌우 25 씩 넘어선다.
   시안의 긴 문구(사전 테스트 미완료 4180:36996 — 26-09-29 정책에서 빠져 화면은 삭제)가 501 이라야 한 줄로 떨어져서
   패딩이 아니라 501 을 따른다. */
.popup__body {
  margin: 0;
  width: calc(501 * var(--pu)); max-width: none;
  text-align: center; white-space: pre-line;
  font-family: var(--font-primary);
  font-size: calc(22 * var(--pu)); font-weight: 400;
  line-height: 1.5; letter-spacing: 0.01em;              /* 시안 0.22px @22px */
  color: #F5F5F5;                                        /* ⚠ 시안 원본값 */
  word-break: keep-all;
}
/* 본문 강조 — 시안(소진 4172:36544)이 횟수 한 조각만 Bold + 강조색(09-29 네이비)으로 띄운다.
   <p class="popup__body">체험 기회는 <span class="popup__em">1번</span>이에요.</p> */
.popup__em { font-weight: 700; color: #9EB1FD; }       /* ⚠ 시안 원본값 */

/* 본문 Q&A 형식 — 도움말처럼 여러 질문을 묶을 때 .popup__body 대신 쓴다(dl > div > dt + dd).
     <dl class="popup__qna">
       <div class="popup__qna-item"><dt class="popup__q">질문</dt><dd class="popup__a">답</dd></div>
     </dl>
   본문과 같은 폭 501 · 왼쪽 정렬. 항목 사이는 흰 12% 선으로 나눈다. Q · A 머리글자는 제목과 같은
   Hahmlet SemiBold 로 달고, Q 는 본문 강조(.popup__em)와 같은 네이비(#9EB1FD),
   A 는 라일락 보조 텍스트(--color-ice-200)로 둔다. 질문은 본문 22 를 굵게(700), 답은 20 보통(400). */
.popup__qna {
  display: flex; flex-direction: column;
  gap: calc(20 * var(--pu));
  width: calc(501 * var(--pu)); margin: 0;
  text-align: left;
  font-family: var(--font-primary);
  word-break: keep-all;
}
.popup__qna-item {
  display: flex; flex-direction: column;
  gap: calc(8 * var(--pu));
}
.popup__qna-item + .popup__qna-item {
  padding-top: calc(20 * var(--pu));
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}
.popup__q,
.popup__a {
  display: grid; grid-template-columns: calc(26 * var(--pu)) 1fr;
  margin: 0;
  line-height: 1.5; letter-spacing: 0.01em;
}
.popup__q { font-size: calc(22 * var(--pu)); font-weight: 700; color: #F5F5F5; }   /* ⚠ 시안 원본값(본문과 같음) */
.popup__a { font-size: calc(20 * var(--pu)); font-weight: 400; color: var(--color-ice-200); }
.popup__q::before,
.popup__a::before {
  font-family: var(--font-display); font-weight: 600;
}
.popup__q::before { content: "Q"; color: #9EB1FD; }                                 /* ⚠ 시안 원본값 */
.popup__a::before { content: "A"; color: var(--color-ice-200); }

/* 오버레이 — 화면 위에 띄우는 팝업(도움말 등). 바깥을 누르면 닫는 처리는 앱 스크립트 몫. */
.popup-overlay {
  position: fixed; inset: 0; z-index: 30;
  display: none; align-items: center; justify-content: center;
  padding: 6vh 6vw;
  box-sizing: border-box;
  background: rgba(5, 7, 15, 0.7);                      /* night-900 스크림 */
  backdrop-filter: blur(3px);
}
.popup-overlay--show { display: flex; animation: popup-in 0.25s ease both; }
@keyframes popup-in { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .popup-overlay--show { animation: none; } }

/* ============ Toast (알림 토스트) — Figma 3903:27089 (success 3903:27088 · fail 3903:27090) ============
   반투명 알약 판 + 한 가지 키 색(성공 초록 · 실패 빨강)의 테두리와 장식: 판 양쪽 테두리에 4갈래 반짝이(16)가
   걸치고, 그 바깥 11.5 에 점(3)이 하나씩. 문구 NEXON Medium 14 / 1.5 · 흰색 · 가운데. 26-09-28 두 태블릿 공통.
     <div class="toast toast--fail" role="alert">
       <div class="toast__box"><p class="toast__text">이미지 저장에 실패했어요</p></div>
     </div>
   판은 문구 틀 148(시안) 이상, 문구가 길면 그만큼 넓어진다 — 여러 줄이면 가운데로 쌓인다. 위치 · 나타나기 · 사라지기는 앱 몫.
   장식 그림은 시안에서 받은 SVG(design-system/assets/toast/)를 마스크로 써서 키 색으로 칠한다(그림 하나로 두 색).
   ⚠ 키 색 #92FA9B(green_500) · #FF8484(red_500) · 판 rgba(32,33,37,.6) 은 시안 원본값(비토큰). 단위 --ds-u(스캐너 프레임 숫자). */
.toast, .toast__box, .toast__text { --tu: var(--ds-u, var(--u, 1px)); }
.toast {
  --toast-key: #92FA9B;                                  /* ⚠ success — green_500 */
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0 calc(16 * var(--tu));                       /* 장식이 판 밖으로 16 나간다(시안 214 = 182 + 16×2) */
}
.toast--fail { --toast-key: #FF8484; }                   /* ⚠ fail — red_500 */
.toast__box {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  padding: calc(8 * var(--tu)) calc(16 * var(--tu));
  border: 1px solid var(--toast-key);
  border-radius: calc(12 * var(--tu));
  background: rgba(32, 33, 37, 0.6);                     /* ⚠ 시안 원본값 */
}
.toast__text {
  margin: 0;
  min-width: calc(148 * var(--tu));                      /* 시안 문구 틀 148 — 짧은 문구도 판 182 를 지킨다 */
  font-family: var(--font-primary);
  font-size: calc(14 * var(--tu)); font-weight: 500;     /* base/body2/M-14 */
  line-height: 1.5; letter-spacing: 0.005em;            /* 시안 0.07px @14px */
  color: var(--color-white-100);
  text-align: center;
  word-break: keep-all;
}
/* 반짝이 — 판 왼쪽 · 오른쪽 테두리 한가운데에 걸친다(시안: 왼쪽 8~24 / 판 16, 오른쪽 190~206 / 판 198) */
.toast__box::before,
.toast__box::after {
  content: ""; position: absolute; top: 50%;
  width: calc(16 * var(--tu)); height: calc(16 * var(--tu));
  margin-top: calc(-8 * var(--tu));
  background: var(--toast-key);
  -webkit-mask: url("assets/toast/sparkle-l.svg") center / 100% 100% no-repeat;
          mask: url("assets/toast/sparkle-l.svg") center / 100% 100% no-repeat;
}
.toast__box::before { left: calc(-8 * var(--tu) - 1px); }
.toast__box::after {
  right: calc(-8 * var(--tu) - 1px);
  -webkit-mask-image: url("assets/toast/sparkle-r.svg");
          mask-image: url("assets/toast/sparkle-r.svg");
}
/* 점 — 판 바깥 끝(시안 x 3 · 208, 지름 3) */
.toast::before,
.toast::after {
  content: ""; position: absolute; top: 50%;
  width: calc(3 * var(--tu)); height: calc(3 * var(--tu));
  margin-top: calc(-1.5 * var(--tu));
  background: var(--toast-key);
  -webkit-mask: url("assets/toast/dot.svg") center / 100% 100% no-repeat;
          mask: url("assets/toast/dot.svg") center / 100% 100% no-repeat;
}
.toast::before { left: calc(3 * var(--tu)); }
.toast::after { right: calc(3 * var(--tu)); }

/* ============ QuestButton (Fastpass CTA) — Figma 1932:4120 ============
   26-08-05 Fastpass 스타일 승격. 메인 플로우의 확정 CTA는 이 버튼으로 통일한다.
   코너 브래킷은 에셋(assets/cta-corner-*.svg)을 마크업으로 얹는다. */
/* QuestButton — Figma btn/main
     big   3084:5703 (376x87 프레임 / 면 359x78 / 32px Medium)
     small 3084:8124 (320x74 프레임 / 면 310x66 / 20px Bold)
   .dcta 자체가 **면**이고, 코너 삼각형은 면 바깥으로 약간 걸쳐 프레임 모서리를 만든다.
   ⚠ 소수점 값(1.846 / 18.457 / 3.691)은 Figma 원본 그대로다 — 반올림하면 보더가
     굵어 보이고 글로우 번짐이 달라진다. */
.dcta {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 359px;
  height: 78px;
  border: 1.846px solid var(--color-purple-100);
  background: var(--color-gray-900);
  cursor: pointer;
  transition: opacity 0.2s ease, filter 0.2s ease;
}
/* 안쪽 프레임 글로우 — Figma 는 굵은 반투명 보더에 blur 를 먹여 만든다.
   box-shadow 로는 이 '안쪽으로 스미는' 느낌이 안 나와 같은 방식으로 재현한다.
   pointer-events 를 끊어야 라벨 클릭을 가로채지 않는다. */
.dcta::before {
  content: "";
  position: absolute; inset: 0;
  border: 18.457px solid rgba(165, 165, 165, 0.2);
  filter: blur(3.691px);
  pointer-events: none;
}
.dcta:disabled,
.dcta.is-disabled {
  opacity: 0.45;
  cursor: default;
}
.dcta:disabled::before,
.dcta.is-disabled::before { opacity: 0.4; }
.dcta__label {
  position: relative;   /* ::before 글로우 위로 */
  font-family: var(--font-primary);
  font-weight: 500;
  font-size: 32px;
  line-height: 1.5;
  letter-spacing: 0.005em;   /* Figma 0.16px @32px */
  color: var(--color-white-100);
}
.dcta__corner {
  position: absolute;
  width: 13px;
  height: 13px;
  display: flex;
}
.dcta__corner img { width: 100%; height: 100%; }
.dcta__corner--tl { left: -4px; top: -4px; transform: rotate(180deg) scaleY(-1); }
.dcta__corner--tr { right: -4px; top: -4px; }
.dcta__corner--bl { left: -4px; bottom: -4px; transform: rotate(90deg) scaleY(-1); }
.dcta__corner--br { right: -4px; bottom: -4px; transform: rotate(90deg); }

/* small — Figma btn/main/small (3084:8124) · 결과 화면 확인 버튼 인스턴스 4063:15373.
   코너 마크 위치: 시안의 바깥 프레임은 322.9x74.5 이고 버튼(310x66)은 그 안에 가운데
   정렬돼 있다. 프레임 기준 left 0.63% · top 1.35% 를 버튼 기준으로 환산하면
   가로 -4.4px · 세로 -3.2px — 가로가 세로보다 더 많이 튀어나온다. */
.dcta--small { width: 310px; height: 66px; }
.dcta--small .dcta__label {
  font-weight: 700; font-size: 20px;
  letter-spacing: 0.005em;   /* Figma 0.1px @20px */
}
.dcta--small .dcta__corner { width: 11.07px; height: 11.07px; }
.dcta--small .dcta__corner--tl { left: -4.4px; top: -3.2px; }
.dcta--small .dcta__corner--tr { right: -4.4px; top: -3.2px; }
.dcta--small .dcta__corner--bl { left: -4.4px; bottom: -3.2px; }
.dcta--small .dcta__corner--br { right: -4.4px; bottom: -3.2px; }

/* ghost — 보조 CTA(다음에 하기 등): 지오메트리 동일, 표면 없이 보더만 */
.dcta--ghost {
  border-color: rgba(238, 241, 254, 0.45); /* purple_100 @0.45 */
  background: transparent;
}
.dcta--ghost::before { display: none; }
.dcta--ghost .dcta__label { font-weight: 500; color: var(--color-ice-200); }


/* stage — 시작 화면 등 큰 무대용: btn/main/big(3084:5703, 면 359×78)의 **모든 값에 같은 배율 k** 를 곱해
   키운 것(비율이 어긋나면 글로우 두께·브래킷만 따로 커져 시안과 달라 보인다). 문구가 길면 폭만 늘어난다
   (min-width = 시안 면 폭 × k, 줄바꿈 없음). 26-09-28 두 앱 styles.css 에서 올렸다.
   k 1.727 = 완드 620u / 359. 단위는 완드 프레임 숫자용 --ds-wu(tokens.css). */
.dcta--stage {
  --swu: var(--ds-wu, var(--u, 1px));
  --k: 1.727;
  width: auto;
  min-width: calc(359 * var(--k) * var(--swu));
  height: calc(78 * var(--k) * var(--swu));
  padding: 0 calc(40 * var(--k) * var(--swu));
  border-width: calc(1.846 * var(--k) * var(--swu));
}
.dcta--stage::before {
  border-width: calc(18.457 * var(--k) * var(--swu));
  filter: blur(calc(3.691 * var(--k) * var(--swu)));
}
.dcta--stage .dcta__label {
  font-size: calc(32 * var(--k) * var(--swu));
  letter-spacing: calc(0.16 * var(--k) * var(--swu));
  line-height: 1.5;
  white-space: nowrap;
}
.dcta--stage .dcta__corner { width: calc(13 * var(--k) * var(--swu)); height: calc(13 * var(--k) * var(--swu)); }
.dcta--stage .dcta__corner--tl { left: calc(-4 * var(--k) * var(--swu)); top: calc(-4 * var(--k) * var(--swu)); }
.dcta--stage .dcta__corner--tr { right: calc(-4 * var(--k) * var(--swu)); top: calc(-4 * var(--k) * var(--swu)); }
.dcta--stage .dcta__corner--bl { left: calc(-4 * var(--k) * var(--swu)); bottom: calc(-4 * var(--k) * var(--swu)); }
.dcta--stage .dcta__corner--br { right: calc(-4 * var(--k) * var(--swu)); bottom: calc(-4 * var(--k) * var(--swu)); }

/* ============ PillButton (밝은 알약 버튼) — Figma 모달 확인 4774:12664(소진) · 12727(도움말) · 12881(체험 중인 QR) ============
   26-10-01 공통 팝업의 확인 버튼이 QuestButton small(어두운 면 + 코너 브래킷)에서 이 버튼으로 바뀌었다. 스캐너 선택 화면
   (기숙사 · 패트로누스)의 [선택하기]와 같은 알약이다(시안 이름 Select Button Container).

     <button class="pill-btn"><span class="pill-btn__inner"><span class="pill-btn__label">확인</span></span></button>

   크기 280×59(폭은 앱이 바꿔 쓸 수 있다 — 예: 스캐너 기숙사 선택 257.5) · 여백 4 · 흰 → #E8FFFF(아래 → 위) · 그림자 0 2 2 #565150
   · 둥근 알약 · 넘침 자름. 안쪽 테두리 0.5 #858587 · 글자 NEXON Medium 22 / 33 · 자간 0.5% · #858587.
   양끝 점 — #F4FFFF 판(8) 위 #858587 점(4)이 안쪽 테두리 양끝에 걸친다: 왼쪽은 알약 왼쪽 끝, 오른쪽은 오른쪽 끝에서 1 안(시안 안쪽 기준 267).
   가운데 별 무늬(assets/pill/deco.svg 217 · #16557F 4%)는 안쪽 테두리 기준 (15.5, -83.5)에 놓여 알약 밖으로 넘쳐 잘린다.
   크기 단위는 공통 --ds-u(없으면 앱의 --u, 그것도 없으면 1px — 프리뷰).
   ⚠ 색 #E8FFFF · #565150 · #858587 · #F4FFFF 는 시안 원본값(비토큰)이다. */
.pill-btn, .pill-btn__inner, .pill-btn__label { --pbu: var(--ds-u, var(--u, 1px)); }
.pill-btn {
  position: relative; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: calc(280 * var(--pbu)); height: calc(59 * var(--pbu));
  padding: calc(4 * var(--pbu));
  border: 0; border-radius: 9999px;
  background: linear-gradient(0deg, #FFFFFF 0%, #E8FFFF 100%);
  box-shadow: 0 calc(2 * var(--pbu)) calc(2 * var(--pbu)) 0 #565150;
  overflow: hidden;
  font: inherit; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.pill-btn:active { transform: scale(0.97); }
.pill-btn:disabled { cursor: default; }
.pill-btn:focus-visible { outline: none; box-shadow: 0 calc(2 * var(--pbu)) calc(2 * var(--pbu)) 0 #565150, var(--focus-ring, 0 0 0 2px #7691FF); }
.pill-btn__inner {
  position: relative; flex: 1; align-self: stretch;
  display: flex; align-items: center; justify-content: center;
  border: calc(0.5 * var(--pbu)) solid #858587;
  border-radius: 9999px;
}
/* 별 무늬 */
.pill-btn__inner::before {
  content: ""; position: absolute;
  left: calc(15.5 * var(--pbu)); top: calc(-83.5 * var(--pbu));
  width: calc(217 * var(--pbu)); height: calc(217 * var(--pbu));
  background: url("assets/pill/deco.svg") center / 100% 100% no-repeat;
  pointer-events: none;
}
/* 양끝 점 */
.pill-btn::before,
.pill-btn::after {
  content: ""; position: absolute; top: 50%; z-index: 1;
  width: calc(8 * var(--pbu)); height: calc(8 * var(--pbu));
  transform: translateY(-50%);
  border-radius: 50%;
  background: radial-gradient(circle, #858587 0 calc(2 * var(--pbu)), #F4FFFF calc(2 * var(--pbu)));
  pointer-events: none;
}
.pill-btn::before { left: 0; }
.pill-btn::after { right: calc(1 * var(--pbu)); }
.pill-btn__label {
  position: relative;
  font-family: var(--font-primary); font-weight: 500;
  font-size: calc(22 * var(--pbu)); line-height: 1.5; letter-spacing: 0.005em;
  color: #858587;
  white-space: nowrap;
}


/* ============ TextGenerate (타이틀 등장 효과) — Aceternity text-generate-effect ============
   26-09-28 지시로 두 태블릿의 시작 화면 타이틀에 쓴다. React 컴포넌트(motion)를 옮긴 것이라
   값은 원본 그대로다: 단어마다 흐림 10px·투명 → 0.5s 에 또렷, 단어 사이 0.2s 씩 늦춤.
   단어 <span> 은 text-generate.js(HPTextGenerate.run)가 만든다 — 마크업에 직접 쓰지 않는다.
   inline-block 이라야 filter 가 단어 상자에 적용된다(줄바꿈은 공백 글자 노드에서 그대로 된다). */
.tgen__word {
  display: inline-block;
  opacity: 0;
  filter: blur(10px);
  animation: tgen-in 0.5s ease-out both;
  animation-delay: calc(var(--tgen-i, 0) * 0.2s);
}
@keyframes tgen-in { to { opacity: 1; filter: blur(0); } }
/* 반복 — 시작(대기) 화면처럼 계속 켜 두는 화면용(26-09-28 지시). 한 주기(--tgen-cycle, 기본 6s)마다
   단어가 차례로 또렷해져 머물다가 함께 흐려지고 다시 시작한다. 느긋한 분위기로 머무는 시간을 길게 잡았다
   (26-09-28 지시: 4s 주기·2.9s 머묾 → 6s 주기·4.5s 머묾). 퍼센트는 6s 기준이다:
   0.5s 에 또렷(8.33%) · 5.4s 까지 머묾(90%) · 0.6s 동안 사라짐(100%). 단어별 늦춤(0.2s·i)은 그대로라
   사라질 때도 살짝 순서가 진다. 주기를 바꾸면 퍼센트도 같은 비율로 바꿔야 한다. */
.tgen--loop .tgen__word {
  animation: tgen-loop var(--tgen-cycle, 6s) ease-out infinite;
  animation-delay: calc(var(--tgen-i, 0) * 0.2s);
}
@keyframes tgen-loop {
  0%     { opacity: 0; filter: blur(10px); }
  8.33%  { opacity: 1; filter: blur(0); }
  90%    { opacity: 1; filter: blur(0); }
  100%   { opacity: 0; filter: blur(10px); }
}
/* 줄인 모션 — 움직임만 빼고 글자는 바로 보인다 */
@media (prefers-reduced-motion: reduce) {
  .tgen__word, .tgen--loop .tgen__word { animation: none; opacity: 1; filter: none; }
}


/* ============ StepTitle (화면 타이틀) — 완드 시안 4408:12764 ============
   시작 화면을 뺀 모든 화면의 제목이다: [화살] 27 [제목] 27 [화살(좌우 반전)], 가운데 맞춤 + 글로우.
   26-09-28 두 태블릿 화면 타이틀을 이것으로 통일했다(스캐너의 명판 헤더는 팝업 안에서만 — Popup).
     <div class="title-deco">
       <img class="title-deco__arrow" src="assets/deco-arrow.svg" alt="" aria-hidden="true">
       <h2 class="step-title step-title--glow">제목</h2>
       <img class="title-deco__arrow title-deco__arrow--r" src="assets/deco-arrow.svg" alt="" aria-hidden="true">
     </div>
   화살은 h2 밖에 둔다(언어를 바꿀 때 h2 의 textContent 를 통째로 바꾼다). 화살 그림(icon/arrow_illust)은 앱 assets/.
   제목 Hahmlet SemiBold 72 / 1.25 / -1% · 화살 88×49(1080 시안 79×44 를 1200 기준으로). 단위 --ds-wu(완드 프레임 숫자). */
.title-deco, .title-deco__arrow, .step-title { --swu: var(--ds-wu, var(--u, 1px)); }
.step-title {
  font-family: var(--font-display);
  font-size: calc(72 * var(--swu));
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
}
body[data-lang="en"] .step-title { font-family: var(--font-display-en); font-weight: 400; }
.title-deco {
  display: flex; align-items: center; justify-content: center;
  gap: calc(27 * var(--swu));
}
.title-deco__arrow {
  flex: none;
  width: calc(88 * var(--swu)); height: calc(49 * var(--swu));
}
.title-deco__arrow--r { transform: scaleX(-1); }   /* 시안: rotate-180 + -scale-y = 좌우 반전 */
/* 글로우 — 시안 text-shadow(-1 -1 24.9 · rgba(250,247,212,.8))를 1200 기준으로 */
.step-title--glow {
  text-shadow: calc(-1.1 * var(--swu)) calc(-1.1 * var(--swu)) calc(27.7 * var(--swu)) rgba(250, 247, 212, 0.8);
}

/* ============ StartScreen (시작 화면 세트) — 완드 시안 4393:4273 · 4295 · 4311 ============
   두 태블릿의 시작(대기) 화면이 같은 구조·같은 크기다(26-09-28 지시). 두 앱 styles.css 에 이름만 다르게
   (스캐너 .home-* / 완드 .start-*) 똑같은 값으로 있던 것을 올렸다. 숫자는 완드 프레임(1200×1920) 값이고
   단위 --ds-wu(tokens.css)를 곱한다.

     <header class="start-header">  <div class="start-logo"><img …></div>  <div class="lang-toggle">…</div>  </header>
     <h1 class="start-title"><span class="start-title__sm">작은 줄</span><br><span class="start-title__lg">큰 줄</span></h1>
     <div class="start-sub"><img class="start-sub__spark" …><div class="start-sub__body"><p class="start-desc">…</p></div></div>
     <… class="dcta dcta--stage start-cta">…</…>

   앱에 남는 것: 스테이지(프레임 높이가 앱마다 다름) · 헤더 z-index(앱의 연출 레이어 위) · CTA 숨쉬기 애니메이션
   (두 앱이 아직 다름 — 정해지면 여기로) · 누르는 방식(완드는 화면 전체, 스캐너는 버튼). */
/* 단위는 부분마다 읽는다 — 한 부분만 따로 쓰여도(프리뷰 타이포 표 등) 크기가 풀리게 */
.start-header, .start-logo, .start-title, .start-title__sm, .start-title__lg,
.start-sub, .start-sub__spark, .start-sub__body, .start-desc, .start-cta,
.lang-toggle, .lang-btn { --swu: var(--ds-wu, var(--u, 1px)); }
/* 헤더 — 스테이지 위에서 40 · 양옆 58, [로고 | 국기] 양끝·위 맞춤. 흐름 밖이라 가운데 묶음이 흔들리지 않는다. */
.start-header {
  position: absolute;
  top: calc(40 * var(--swu)); left: calc(58 * var(--swu)); right: calc(58 * var(--swu));
  display: flex; justify-content: space-between; align-items: flex-start;
}
/* 전시 로고 — 폭 316 · 196:72 틀. PNG(700×350)는 위아래가 투명 여백이라 136.05% 높이 · -17.44% 로 잘라 보인다. */
.start-logo {
  position: relative; overflow: hidden;
  width: calc(316 * var(--swu)); aspect-ratio: 196 / 72;
  flex-shrink: 0;
}
.start-logo img {
  position: absolute; left: 0; top: -17.44%;
  width: 100%; height: 136.05%;
  filter: drop-shadow(0 0 calc(24 * var(--swu)) rgba(222, 229, 255, 0.3));
}
/* 타이틀 — 작은 줄 62 · 큰 줄 158, 굵기 400 · 행간 1.16 (시안 4393:4311 을 1200 기준으로).
   한 줄뿐이면 큰 줄 span 하나만 둔다. 시안 서체였던 Warhaven OTF 는 라이선스로 쓰지 않는다(26-10-02) — Hahmlet 400 그대로.
   영문은 Harry Beast Cyr(한 굵기라 400). */
.start-title {
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1.16;
  letter-spacing: -0.0045em;                 /* 시안 -0.64/142 */
  text-align: center;
  color: var(--color-white-100);
  text-shadow: 0 calc(6 * var(--swu)) calc(40 * var(--swu)) rgba(5, 7, 15, 0.55);
  word-break: keep-all;
}
.start-title__sm { font-size: calc(62 * var(--swu)); }
.start-title__lg { font-size: calc(158 * var(--swu)); }
body[data-lang="en"] .start-title { font-family: var(--font-display-en); font-weight: 400; }
/* 서브텍스트 블록 — 시안 4393:4295·4304: 반짝이가 헤어라인에 걸치고, 문구 뒤로 어두운 라디얼이 깔린다. */
.start-sub {
  position: relative;
  display: flex; flex-direction: column; align-items: center;
  width: calc(689 * var(--swu));
  margin-top: calc(28 * var(--swu));
}
.start-sub__spark {
  position: relative; z-index: 2;
  width: calc(40 * var(--swu)); height: calc(40 * var(--swu));
  transform: rotate(-45deg);
  margin-bottom: calc(-31 * var(--swu));      /* 헤어라인에 걸치게 */
}
.start-sub__body {
  position: relative; z-index: 1;
  width: 100%;
  padding: calc(44 * var(--swu)) calc(11 * var(--swu));
  /* 문구 뒤 어두운 라디얼 — 시안 실측(#12172D → #090C17 50% → 투명) */
  background: radial-gradient(ellipse 100% 100% at 50% 0%,
    rgba(18, 23, 45, 1) 0%, rgba(9, 12, 23, 0.5) 50%, rgba(0, 0, 0, 0) 100%);
}
/* 위쪽 헤어라인 — 양끝이 사라지는 그라디언트 선 */
.start-sub__body::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0;
  height: calc(4 * var(--swu));
  background: linear-gradient(90deg,
    rgba(239, 248, 255, 0) 0%, rgba(239, 248, 255, 0.7) 50%, rgba(239, 248, 255, 0) 100%);
}
/* 문구 — 시안 base/display3/L-36 → 40 · 굵기 300 · #EFF8FF(⚠ 시안 원본값) */
.start-desc {
  margin: 0;
  font-size: calc(40 * var(--swu));
  font-weight: 300;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: #EFF8FF;
  text-align: center;
  word-break: keep-all;
}
/* CTA 자리 — 서브텍스트 블록 아래 56. 버튼 자체는 QuestButton stage(.dcta--stage). */
.start-cta { margin-top: calc(56 * var(--swu)); }

/* 시작 화면 배경 — 시안 4393:4273: 바탕 그라디언트(#6B7179 → #06090E) 위에 사진을 overlay 로 겹치고,
   그 위에 어두운 그라디언트를 한 겹 더 덮는다. 보이는 때(opacity 1)와 사진 파일(assets/bg-start.jpg)은 앱 몫.
   ⚠ isolation:isolate 가 없으면 overlay 가 페이지 전체 배경과 섞여 색이 달라진다.
   사진 자르는 위치는 기본 가운데 — 가로 화면(스캐너)은 앱에서 object-position 을 덮는다. */
.bg-start {
  position: absolute; inset: 0;
  isolation: isolate;
  background: linear-gradient(180deg, #6B7179 0%, #06090E 100%);   /* ⚠ 시안 원본값 */
  opacity: 0;
  transition: opacity 0.8s ease;
}
.bg-start img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; object-position: center;
  mix-blend-mode: overlay;
}
.bg-start::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg,
    rgba(16, 16, 16, 0.24) 9.4%,
    rgba(16, 16, 16, 0) 44.9%,
    rgba(16, 16, 16, 0.4) 80.5%);
}


/* ============ LanguageToggle (언어 선택) — Figma Btn/flag ============
   국기 원형 버튼 두 개. 선택 = 흰 디스크 + 밝은 테두리, 비선택 = 회색 디스크에 테두리 없음.
     <div class="lang-toggle" role="group"><button class="lang-btn lang-btn--on" aria-label="한국어">
       <span class="lang-btn__disc"><img src="…/flag-kr.svg" alt=""></span></button> …</div>
   ⚠ 국기는 나라를 가리키지 언어를 가리키지 않아 aria-label 로 "한국어"/"English" 를 따로 읽힌다.
   ⚠ 중립 회색 3종은 Figma 원본값(line/solid/primary · bg/secondary · bg/tertiary)이라 팔레트 토큰으로 올리지 않는다.
   단위 --ds-wu(완드 프레임 숫자). */
.lang-toggle { display: flex; align-items: center; gap: calc(39 * var(--swu)); }
.lang-btn {
  display: flex; align-items: center; justify-content: center;
  width: calc(90 * var(--swu)); aspect-ratio: 1;
  padding: calc(5 * var(--swu));
  border: calc(3 * var(--swu)) solid transparent;
  border-radius: var(--radius-full);
  background: transparent;
  cursor: pointer;
  filter: drop-shadow(0 0 0.2px rgba(66, 71, 76, 0.32))
          drop-shadow(0 calc(3 * var(--swu)) calc(2.5 * var(--swu)) rgba(66, 71, 76, 0.08));
  transition: border-color 0.25s ease;
}
.lang-btn__disc {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%;
  border-radius: var(--radius-full);
  background: #EAEBEC;               /* bg/tertiary */
  overflow: hidden;
  transition: background 0.25s ease;
}
/* 국기는 높이로 맞춘다 — 태극기(1.28:1)와 성조기(1.38:1)의 가로세로비가 달라 너비를 고정하면 시각 크기가 어긋난다. */
.lang-btn__disc img { height: 44%; width: auto; display: block; }
.lang-btn--on { padding: calc(3 * var(--swu)); border-color: #E1E2E4; }   /* line/solid/primary */
.lang-btn--on .lang-btn__disc { background: #F7F7F8; }                   /* bg/secondary */
.lang-btn:active { transform: scale(0.94); }


/* ============ Login (운영자 로그인) · TextField — Figma 3084:8634 · textfield 3183:14183 ============
   두 태블릿의 운영자 로그인 폼. 스캐너에서 만들어 완드가 그대로 옮겨 쓰던 것을 올렸다(26-09-28).
     <div class="login-layout">
       <img class="login-logo" …><div class="login-brand"><h1 class="login-title">…</h1></div>
       <form class="login-form"><div class="login-field"><label class="login-label">…</label>
         <input class="login-input" …></div> … <p class="login-error" hidden></p>
         <button class="dcta dcta--small login-cta">…</button></form>
     </div>
   폼 블록 gap 24 · 라벨→필드 12 · 폭 352. 라벨 base/head2/B-18, 필드 = 알약형(999) · 흰 0.861px 보더 ·
   py 16 px 20 · 글자 base/head1/M-20 가운데 · 플레이스홀더 #464C64 · 글로우 0 0 4px.
   단위 --ds-u(스캐너 프레임 숫자). 한 화면만 고정 px 로 두려면 그 화면에 --ds-u: 1px.
   버튼 크기(.dcta--small × 단위)와 로그인 화면 배경은 앱 몫. */
/* 단위는 부분마다 읽는다 — 폼만 따로 쓰일 때(관리자 화면 등)도 크기가 풀리게 */
.login-layout, .login-logo, .login-title, .login-form, .login-field, .login-label,
.login-input, .login-error { --lu: var(--ds-u, var(--u, 1px)); }
.login-layout {
  display: flex; flex-direction: column; align-items: center;
  gap: calc(30 * var(--lu));
  width: calc(430 * var(--lu));
  max-width: 100%;
}
.login-logo {
  height: calc(67 * var(--lu));
  filter: drop-shadow(0 0 calc(14 * var(--lu)) rgba(222, 229, 255, 0.3));   /* glow-ice 계열 — 라일락 */
}
.login-brand { text-align: center; }
.login-title {
  font-size: calc(30 * var(--lu)); font-weight: 700;
  color: var(--color-white-100);
}
.login-form {
  display: flex; flex-direction: column; align-items: center; gap: calc(24 * var(--lu));
  width: min(calc(352 * var(--lu)), 100%);
}
.login-field { display: flex; flex-direction: column; gap: calc(12 * var(--lu)); width: 100%; }
.login-label {
  font-size: calc(18 * var(--lu)); font-weight: 700; letter-spacing: 0.005em; line-height: 1.5;
  color: var(--color-white-100);
}
.login-input {
  width: 100%; height: calc(62 * var(--lu));                 /* 30(글자) + 16×2 */
  padding: calc(16 * var(--lu)) calc(20 * var(--lu));
  font-family: var(--font-primary); font-size: calc(20 * var(--lu)); font-weight: 500;
  letter-spacing: 0.005em; line-height: 1.5; text-align: center;
  color: var(--color-blue-50, #EEF1FF);
  /* 시안은 rgba(0,0,0,.2) 를 overlay 블렌드로 얹는다 — 어두운 배경 위에선 같은 알파의 검정으로 충분하다 */
  background: rgba(0, 0, 0, 0.2);
  border: 0.861px solid var(--color-white-100);
  border-radius: 999px;
  box-shadow: 0 0 calc(4 * var(--lu)) 0 rgba(118, 145, 255, 0.4);
  outline: none;
  transition: box-shadow 0.2s ease, border-color 0.2s ease;
}
.login-input::placeholder { color: #464C64; opacity: 1; }   /* ⚠ 시안 원본값 */
.login-input:focus { border-color: var(--color-purple-200); box-shadow: 0 0 calc(10 * var(--lu)) 0 rgba(118, 145, 255, 0.6); }
/* 비밀번호 — NEXON 서브셋의 마스크 글리프(•)가 **빈 글리프**라 입력해도 칸이 비어 보인다(폰트가 글리프를
   갖고 있어 브라우저 폴백도 안 걸린다). 이 칸만 시스템 서체로 마스크를 그리고, 안내 문구는 NEXON 으로 되돌린다. */
.login-input[type="password"] { font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }
.login-input[type="password"]::placeholder { font-family: var(--font-primary); }
.login-error { font-size: calc(14 * var(--lu)); color: var(--func-error); text-align: center; }
.login-cta { margin-top: 0; }
