/* Components — Harry Potter 특별전
   출처: Figma HarryPotter_UI 컴포넌트 시트 (components.md와 1:1 대응)
   tokens.css를 먼저 로드할 것. 색상은 토큰만 사용 — 비토큰 값은 ⚠️ 주석으로 표기(Figma 원본값). */

/* ⚠️ 2026-10-07: Button(구 1140:25470 · .btn/.btn--popup QR 시뮬레이션 데모) · ResultButton(1168:38149) ·
   ResultCard(1168:37955) · ResultContent(1173:9833) · InputField 밑줄형(1173:10152)은 실사용 0건이라 삭제 —
   쓰던 옛 결과 카드(app.js qrCardHtml · resultCardHtml)가 도달할 수 없는 분기였다. */

/* ============ Field (알약 입력) — Figma "Field" 3085:5578/5579/5585 (Empty/Focus/Filled) ============
   온보딩 이메일 · 인증 코드 · 닉네임이 쓴다(앱 쪽 크기 · 색 덮어쓰기는 app.css .form-row .input-field--pill).
   2026-10-07: 밑줄형 기본 규칙(.input-field · .input-field__control)을 지우면서, 알약형이 거기서 물려받던
   배치 · 글꼴 · 색을 여기로 옮겼다 — 이름은 마크업 그대로 두고 모양은 --pill 이 전부 정한다.
   <div class="input-field input-field--pill"><input class="input-field__control"></div> */
.input-field--pill {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 335px;
  /* 보더는 칸(<input>)이 아니라 이 틀의 고리(아래 「고리 테두리」)가 그린다 — <input> 에는 가상 요소를 못 단다 */
  position: relative;
  border-radius: var(--radius-full);
  --ring: linear-gradient(#4c4968, #4c4968); /* ⚠️ non-token: Figma Field Empty 보더 */
}
.input-field--pill .input-field__control {
  width: 100%;
  padding: 12px 10px;
  border: 1px solid transparent;   /* 두께만 — 색은 틀의 고리 */
  border-radius: var(--radius-full);
  background: rgba(20, 27, 30, 0.4); /* ⚠️ non-token: Figma Field 표면 */
  /* base/body1/M-16 (Figma 3085-5557/5560/5563) */
  font-family: var(--font-base);
  font-weight: 500;
  font-size: 16px;
  line-height: 1.5;
  letter-spacing: 0.005em;
  text-align: center;
  color: var(--text-primary);
  outline: none;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
/* 피그마 플레이스홀더는 흰색 54% → purple 계열로 흐르는 클립텍스트다.
   ::placeholder에는 그라디언트를 입힐 수 없어 중간값 한 색으로 근사했다. */
.input-field--pill .input-field__control::placeholder { color: rgba(255, 255, 255, 0.44); }
.input-field--pill:focus-within,
.input-field--pill.is-filled { --ring: var(--paint-ice-200); --ring-c: #C2E2FF; }   /* 보더 text/accent(ice/200) — 2026-10-06 네이비(전: purple_300) */
.input-field--pill .input-field__control:focus,
.input-field--pill.is-filled .input-field__control {
  box-shadow: 0 0 6px 0 color-mix(in srgb, var(--text-accent) 45%, transparent);   /* 보더와 같은 색 @ 45% */
}

/* ============ Header (상단 바) — 1178:10982 ============
   ⚠️ 2026-08-25: header--content·header--test는 실사용 0건이라 삭제.
   ⚠️ 2026-10-07: header__logo-row·__logo(전체보기 화면 삭제)·__wordmark·__actions(홈 헤더가 마름모 하나로)·
      __title·__trailing-row·__icon--28(학생증 화면 삭제)도 실사용 0건이라 삭제.
   header--acset(계정설정)은 여기 없고 app.css에 있다 — 실사용 헤더 변형이 이제
   login·home·acset 3개 + 결과 화면 전용 .hreveal__header로 나뉘어 있다는 뜻이다
   (피그마 새 Header 컴포넌트의 default/onboarding/home/setting/result 5variant와 매핑:
   onboarding→header--login, setting→header--acset, result→.hreveal__header). */
.header {
  display: flex;
  align-items: center;
  width: 375px;
  box-sizing: border-box;
}
.header--login {
  justify-content: space-between;
  padding: 4px 20px;
  overflow: hidden;
}
/* home — Figma Header(home) 3923:16539 (2026-09-16 홈 리프레시 · 2026-10-06 네이비).
   지금은 계정 버튼 하나(계정관리)만 선다 — 오른쪽 정렬 · 여백은 app.css .header--home 이 덮는다.
   그림은 마름모 안 모자(home/header-btn-hat · Figma wand_after 4981:56403 Header). 10-29 UT(모자를 계정으로 본 사람 0/5)로
   사람 그림(#363 유리 원 · #367 마름모 안 사람)을 썼다가 10-30 모자로 되돌렸다 — 대신 코치마크 뒤에 한 번 알려 준다
   (app.css 「계정관리 안내」). 그림은 37.477 정사각형이라 -45° 돌려 53 칸 한가운데에 둔다. */
.header--home {
  justify-content: space-between;
  padding: 2px 20px;
}
.header__account {
  display: grid;
  place-items: center;
  width: 53px;
  height: 53px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.header__account img {
  display: block;
  width: 37.477px;
  height: 37.477px;
  transform: rotate(-45deg);
  transition: transform 0.16s ease;
}
.header__account:active img { transform: rotate(-45deg) scale(0.92); }
.header__icon-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 0;
  padding: 0;
  color: var(--text-primary);
  cursor: pointer;
}
.header__icon--24 { width: 24px; height: 24px; }

/* ⚠️ 2026-08-25: ProgressBar(1178:10791)·ProgressSummary(1178:10860)는 실사용 0건이라 삭제. */
/* ⚠️ 2026-10-07: IconCircleButton(2270:4859)·SealingWax(3883:22813)·Q1~Q3Select(3938:29991·3091:7693·3102:21447)도
   실사용 0건이라 삭제 — 학생증 삭제 · 사전 테스트 폐지(10-03)로 쓰던 화면이 없어졌다. Toggle(2991:20946)은 09-16 에 이미 삭제. */

/* ══════════════════════════════════════════════════════════════════════
   2026-08-25 신규 — Figma "component" 시트(3085:5553) 최신화분.
   출처: https://www.figma.com/design/T6atU6SmDzk76CfyougYrC/HarryPotter_UI?node-id=3085-5553
   ══════════════════════════════════════════════════════════════════════ */

/* ============ Logo — 3102:22511 ============
   스플래시 · 로그인(.splash-logo 234×100)과 홈(.home-logo 200×85)이 같은 그림(assets/home/logo-hp-gold.webp, 702×299)을
   크기만 바꿔 쓴다 — 클래스는 쓰는 화면(app.css)에 있다. 새 크기가 필요해도 새 이미지를 따로 만들지 않는다. */


/* ============ Button — 3938:14271 (component set · 2026-09-17 최신화 · primary/disabled 2026-10-06 네이비) ============
   ⚠️ 옛 .btn-pill-glow 를 흡수했다. variant 는 일곱이고 **둘로 갈린다**:

     primary / disabled — 폭 335 · 네 변 2px ice/200(#C2E2FF) 보더 · 완전 알약 · 글로우 178×56(시안 svg) ·
       라벨 navy_700 → black. 공유 버튼이 없는 화면(스플래시·온보딩·계정·동의)이 쓴다.
       2026-10-06 네이비로 바뀌었다(전: blue_100 보더 · 보라 글로우 · purple_700 라벨).
     blue / red / green / yellow / gray — 폭 273 · **아래 변 4px + 40% 투명** ·
       라운드 999/40/160/999 · 글로우 249. **공유 버튼(.btn-share, 50)과 한 줄로 묶어 쓴다**
       (50 + 12 + 273 = 335 로 primary 와 같은 폭이 된다). 결과 화면이 쓴다.

   ⚠️ 라운드는 시안값(999/40/160/999)을 그대로 쓸 수 없다 — CSS 는 한 변의 라운드 합이 그 변보다
      크면 네 모서리를 같은 비율로 한꺼번에 줄인다. 왼쪽 변이 1998 인데 높이가 55 라 비율이
      0.0275 까지 떨어져 오른쪽이 1px·4px 로 뭉개진다. Figma 는 모서리마다 따로 깎는다.
      그래서 시안 렌더 윤곽을 재서 얻은 실제 값(27.5 / 11 / 44 / 27.5)을 넣는다.
   ⚠️ 글로우 색은 보더색과 **따로다**(green 보더 rgba(7,97,14,.4) ↔ 글로우 #28AB3C). export 실측값이다.

   <button class="btn-cta"><span class="btn-cta__label">다음</span></button>
   <button class="btn-cta btn-cta--green"><span class="btn-cta__label">저장하기</span></button> */
.btn-cta {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  box-sizing: border-box;
  max-width: 100%;
  overflow: hidden;
  /* 면 gray_100 · 보더 2px ice/200(시안은 변수 없이 #C2E2FF — 같은 값인 원색). 2026-10-27 색 대신 단색 그림으로 칠한다 —
     삼성 인터넷 강제 다크 모드가 밝은 CSS 바탕 · 보더를 어둡게 바꿔 버튼이 바탕에 묻혔다(UT 10-27 · tokens.css --paint-*).
     보더 칸은 투명하게 두고 그 아래로 ice 그림(border-box)을, 안쪽에 gray 그림(padding-box)을 깐다 — 둥근 모서리를 따른다. */
  background: var(--paint-gray-100) padding-box, var(--paint-ice-200) border-box;
  cursor: pointer;
  /* primary 가 기본 모양이다 — 색 variant 가 아래에서 덮어쓴다 */
  width: 335px;
  /* 시안은 오른쪽 안쪽 여백만 10 이라 라벨이 5 왼쪽에 앉는다(글로우도 −5.5) — 그대로 둔다 */
  padding: 12px 10px 12px 0;
  border: 2px solid transparent;
  border-radius: var(--radius-full);
}
/* 글로우 — 시안 svg(Ellipse 1582: slate #9DAFC8 60% · 번짐 20). 타원 178 × 56 을 블러 여백 40 씩 품어 258 × 136.
   버튼이 overflow:hidden 이라 타원의 위쪽 일부만 보인다(시안과 같다) */
.btn-cta::before {
  content: "";
  position: absolute;
  left: calc(50% - 5.5px);
  top: -18px;                 /* 시안 타원 top 22 − 블러 여백 40 */
  width: 258px;
  height: 136px;
  transform: translateX(-50%);
  background: url('../assets/shared/btn-glow-primary.svg') center / 100% 100% no-repeat;
  pointer-events: none;
}
/* 라벨 — 시안은 navy_700 → black 세로 그라디언트를 글자에 클립한다(색 variant 는 아래에서 purple_700 으로).
   버튼 면색(gray_100)과 같은 요소에는 못 걸어서 라벨을 따로 둔다.
   어두운 그라디언트는 삼성 인터넷 강제 다크 모드에서도 그대로라 면(단색 그림)과 함께 시안대로 남는다 —
   단색 글자로 바꾸면 안 된다(어두운 단색 글자는 밝게 뒤집혀 밝은 면 위에서 사라진다). */
.btn-cta__label {
  position: relative;
  background-image: linear-gradient(180deg, var(--navy-700) 0%, #000 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  font-family: var(--font-base);
  font-weight: 500;
  font-size: 18px;
  line-height: 1.5;
  letter-spacing: 0.09px;
  white-space: nowrap;
}
.btn-cta:active { transform: translateY(1px); }
.btn-cta:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
/* disabled 글로우는 같은 타원을 불투명하게(Ellipse 1581) — 40% 로 흐려진 면 위에서 primary 와 비슷하게 보인다 */
.btn-cta:disabled::before { background-image: url('../assets/shared/btn-glow-disabled.svg'); }
.btn-cta:disabled:active { transform: none; }

/* ── 색 variant — 공유 버튼과 한 줄로 쓰는 쪽 ── */
.btn-cta--blue,
.btn-cta--red,
.btn-cta--green,
.btn-cta--yellow,
.btn-cta--gray {
  width: 273px;
  padding: 12px 40px;
  border: 0;
  border-bottom: 4px solid var(--btn-cta-edge);
  border-radius: 27.5px 11px 44px 27.5px;
  background: var(--paint-gray-100) border-box;   /* 면이 아래 변(반투명) 밑까지 깔린다 — 위 primary 와 같은 그림 */
}
.btn-cta--blue::before,
.btn-cta--red::before,
.btn-cta--green::before,
.btn-cta--yellow::before,
.btn-cta--gray::before {
  top: -155px;
  left: 50%;
  width: 249px;
  height: 249px;
  margin-left: -124.5px;
  transform: none;
  background: radial-gradient(circle,
    var(--btn-cta-glow) 0%,
    color-mix(in srgb, var(--btn-cta-glow) 30%, transparent) 60.35%,
    transparent 100%);
  opacity: 0.4;
  filter: blur(27px);
}
/* 색 variant 의 라벨은 그대로 purple_700 → black (시안 3846-27498 등) */
.btn-cta--blue .btn-cta__label,
.btn-cta--red .btn-cta__label,
.btn-cta--green .btn-cta__label,
.btn-cta--yellow .btn-cta__label,
.btn-cta--gray .btn-cta__label { background-image: linear-gradient(180deg, var(--purple-700) 0%, #000 100%); }
.btn-cta--blue   { --btn-cta-edge: rgba(78, 143, 255, 0.4);  --btn-cta-glow: #2C8EFD; }
.btn-cta--red    { --btn-cta-edge: rgba(255, 108, 108, 0.4); --btn-cta-glow: #FF6565; }
.btn-cta--green  { --btn-cta-edge: rgba(7, 97, 14, 0.4);     --btn-cta-glow: #28AB3C; }
.btn-cta--yellow { --btn-cta-edge: rgba(129, 116, 17, 0.4);  --btn-cta-glow: #D4CC39; }
.btn-cta--gray   { --btn-cta-edge: rgba(53, 58, 130, 0.4);   --btn-cta-glow: #7A7C98; }

/* ============ 결과 머리말 (content/title) ============
   Figma 3102-17762 (component set) · 색 variant 6종. 결과 화면들이 **다 같이 쓴다** —
   완드 lightblue · 그리핀도르 red · 슬리데린 green · 후플푸프 yellow · 래번클로 blue ·
   나머지(gray 포함)는 물약 몫이다.
   + pink — 컴포넌트 variant 가 아니라 물약 시안(2026-10-07 아모텐시아 · 스켈레그로)이 blue 를 분홍으로 덮어쓴
     인스턴스다. 그림(assets/design-system/content-title/pink.svg)만 더했고 짜임새는 다섯과 같아 CSS 가 따로 없다.

   ⚠️ **red 만 컴포넌트 안에서 짜임새가 다르다.** 나머지 다섯은 가로줄까지 한 장(312.496)에
      구워져 있는데, red 는 가운데 장식(33.865)만 그림이고 가로줄은 테두리로 그린다.
      그래서 red 일 때만 줄을 여기서 그린다 — export 가 잘려 나온 게 아니라 원래 그렇다.
   ⚠️ red 의 줄 색 #FEA4A4 는 **6종 중 유일한 실측값**이다. Tailwind export 에 투명한
      테두리(border-b rgba(255,255,255,0))로만 나와서 컴포넌트 렌더의 1px 을 직접 집었다.
      나머지는 제 svg 안에 있다(green #8DFFB1 · blue #C5CCF8 · yellow #FFF5DA ·
      lightblue #99E4FF · gray #3C3E60) — 장식 색에서 유추할 수 없다.

   등장 — 줄이 가운데에서 좌우로 펼쳐진다(완드 결과에서 쓰던 연출이다).
   ⚠️ **닫힌 모습이 기본이다.** 뿌리에 is-in 이 없으면 데코가 접힌 채로 안 보인다 —
      결과 화면들은 이미 그 클래스를 쓰고 있어서(.wres.is-in · .hres.is-in) 새 화면은
      뿌리에 붙이기만 하면 된다.
   <div class="content-title content-title--lightblue">
     <span class="content-title__deco" aria-hidden="true"><img src="…/lightblue.svg" alt=""></span>
     <div class="content-title__text"><p class="content-title__kicker">…</p>
                                      <p class="content-title__name">…</p></div>
   </div> */
.content-title {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: 312.496px;
  max-width: 100%;
}
.content-title__deco {
  position: relative;
  display: flex;
  justify-content: center;
  width: 100%;
  height: 22px;
}
.content-title__deco img { display: block; width: 100%; height: 22px; }
/* red — 장식만 그림이라 폭을 제 크기로 두고 줄을 여기서 그린다(위 ⚠️ 참고) */
.content-title--red .content-title__deco img { width: 33.865px; }
.content-title--red .content-title__deco::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  height: 1px;
  /* 시안 linear-gradient(90deg, transparent, #FEA4A4 50%, transparent) 를 단색 + 투명도로 다시 쓴 그림 —
     CSS 그라디언트는 삼성 인터넷 강제 다크 모드에서 어두워진다(tokens.css --paint-* 머리말) */
  background: url('../assets/shared/grad/title-red-rule.svg') 0 0 / 100% 100% no-repeat;
}
.content-title__text {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
}
.content-title__kicker {
  width: 302px;
  margin: 0;
  opacity: 0.7;
  /* accent/body4/R-12 */
  font-family: var(--font-accent);
  font-weight: 400;
  font-size: 12px;
  line-height: 1.5;
  letter-spacing: -0.01em;
  text-align: center;
  color: var(--blue-50);
}
.content-title__name {
  margin: 0;
  /* Nimbus Roman 40 — 2026-10-14 기본값으로(전: HarryBeast/R-36). Harry Beast 를 쓸 수 없게 되면서 결과 화면 넷이
     모두 이 값으로 덮어쓰고 있었다. 시안 행간은 normal 이다. */
  font-family: var(--font-serif-title);
  font-size: 40px;
  line-height: normal;
  letter-spacing: -0.5px;
  /* 줄바꿈은 이름 안의 \n 에서만 한다(살아있는 죽음의 물약 "DRAUGHT OF / LIVING DEATH") — 폭으로는 안 접는다 */
  white-space: pre;
  text-align: center;
  text-transform: none;   /* 대소문자는 문안 그대로 — 결과 이름은 대문자로 적어 둔다 */
  color: var(--white);
}
/* gray — 이름이 위에서 아래로 흐려진다(시안 컴포넌트 3102-17854, 흰색 → rgba(146,138,160,0.6)).
   다른 색 variant 의 이름은 흰색 단색이다. */
.content-title--gray .content-title__name {
  /* 시안 linear-gradient(180deg, #FFFFFF, rgba(146,138,160,.6)) 를 단색 + 투명도로 다시 쓴 그림(강제 다크 대비) */
  background: url('../assets/shared/grad/text-title-gray.svg') 0 0 / 100% 100% no-repeat;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* 등장 — 줄이 가운데에서 좌우로 펼쳐진다(완드 결과에서 쓰던 연출을 컴포넌트로 옮겼다).
   ⚠️ 뿌리의 is-in 에 걸어 둔다 — 쓰는 화면마다 그 클래스를 이미 쓰고 있어서
      (.wres.is-in · .hres.is-in) 새 화면은 뿌리에 is-in 만 붙이면 그대로 돈다. */
.content-title__deco { clip-path: inset(0 50% 0 50%); }
.is-in .content-title__deco {
  transition: clip-path 1.15s cubic-bezier(0.22, 1, 0.36, 1) 0.1s;
  clip-path: inset(0 0 0 0);
}
@media (prefers-reduced-motion: reduce) {
  .content-title__deco { clip-path: none; }
  .is-in .content-title__deco { transition: none; }
}

/* ============ 저장 줄 (공유 + 저장) ============
   색 variant 버튼은 **공유 버튼과 한 줄로** 쓴다(50 + 12 + 273 = 335 로 primary 와 같은 폭).
   결과 화면들이 같이 쓴다 — 자리(위치·등장 애니메이션)는 쓰는 화면이 정한다.
   <div class="save-row"><button class="btn-share">…</button><button class="btn-cta btn-cta--green">…</button></div> */
.save-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 335px;
  max-width: 100%;
}

/* ============ btn/share — 3893:27308 (2026-09-17 신규) ============
   50 원에 공유 아이콘 하나.
   ⚠️ 시안 트리에는 아이콘에 -scale-y-100 이 걸려 있지만 **CSS 로 뒤집으면 안 된다** — Figma 가
      내보낸 svg 에 그 변환이 이미 구워져 있다(path 가 M12 15V2, 화살표가 위를 향한다).
      한 번 더 뒤집으면 공유가 아니라 내려받기 모양이 된다.
   <button class="btn-share"><img src="assets/shared/icon-share.svg" alt=""></button> */
.btn-share {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 50px;
  height: 50px;
  box-sizing: border-box;
  padding: 12px 16px;
  border: 0;
  border-radius: var(--radius-full);
  /* ⚠️ non-token: 시안 gray_100 20% — 색 대신 단색 그림(밝은 CSS 바탕은 강제 다크에서 어두워진다 · tokens.css --paint-*) */
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0H99999V99999H0Z' fill='%23D7D7D7' fill-opacity='0.2'/%3E%3C/svg%3E");
  cursor: pointer;
}
.btn-share img { width: 24px; height: 24px; }
.btn-share:active { transform: scale(0.94); }

/* ============ 홈 콘텐츠 아이콘 레일 (btn) ============
   2026-10-14 app.css 에서 옮겨 왔다 — 홈 한 곳에서만 쓰지만 디자인시스템 페이지에서도 같은 규칙으로 그린다.
   자리(캡션 아래 27)는 쓰는 화면(app.css)이 정한다. */
/* ── btn — 홈 콘텐츠 아이콘 레일 (Figma btn 4981-54884 · 2026-10-14 색 1차 3873-60746) — 6칸 · 폭 49 · 간격 8 · 아래 정렬 ──
   2026-10-14: 꺼진 아이콘 lavender #7A7C98 → 무채색 #63636E, 비활성 필 아래 색 → slate/300, 활성 필 면 → #101121 16% ·
   밑줄 → lavender/600(border/card), 활성 빛 → 하늘빛 #D5E3FF 48%. 같은 날 기숙사 · 물약 · 패트로누스 아이콘 새 그림. */
.home-rail {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 8px;
  flex-shrink: 0;
  width: 335px;
}
.home-rail__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  width: 49px;
  height: 86px;               /* 활성 칸 높이로 고정 — 비활성은 위 여백 14 로 72 칸(필 45 + 아래 16)에 앉는다 */
  padding: 14px 0 0;
  border: 0;
  background: none;
  cursor: pointer;
  /* ⚠️ 높이·display 를 바꾸면 활성 칸이 튀어 오른다 — 위 여백·필 높이·라벨 투명도만 전환해 자연스럽게 올라오게 한다 */
  transition: padding-top 0.28s cubic-bezier(0.2, 0.7, 0.2, 1);
}
/* 비활성 필 — 45px, 위 rgba(8,9,24,.25) → 아래 slate/300 25%, 전체 60% */
.home-rail__pill {
  position: relative;
  display: grid;
  place-items: center;
  width: 49.167px;
  height: 45px;
  border-radius: 20px;
  overflow: hidden;
  background: linear-gradient(180deg, rgba(8, 9, 24, 0.25) 0%, color-mix(in srgb, var(--slate-300) 25%, transparent) 100%); /* ⚠️ non-token(위 색): Figma 비활성 필 */
  border-bottom: 1px solid transparent;
  opacity: 0.6;
  /* 둥근 자르기를 clip-path 로도 건다 — 아이폰 사파리는 필터가 걸린 안쪽 요소(빛)를 overflow 만으로는 둥근 모서리에서
     자르지 못할 때가 있다(2026-10-20 "상단이 깨져 보임"). */
  isolation: isolate;
  clip-path: inset(0 round 20px);
  transition: height 0.28s cubic-bezier(0.2, 0.7, 0.2, 1), opacity 0.28s ease, background 0.28s ease, border-color 0.28s ease;
}
.home-rail__icon { grid-area: 1 / 1; display: block; width: 32px; height: 32px; transition: opacity 0.2s ease; }
.home-rail__icon--on { opacity: 0; }
/* 활성 필 안의 빛(Figma Vector 788 — 2026-10-14 하늘빛 #D5E3FF 48%, 전: 네이비) — 필 (30, −34) 의 64 × 134 도형 ·
   레이어 블러 20(CSS 10). 필 밖으로 넘치는 만큼 잘린다.
   2026-10-20 svg 그림(<img>, 블러 필터를 품은 104 × 174)에서 CSS 로 바꿨다 — 필터를 품은 svg 를 <img> 로 넣으면 확대된 화면
   (휴대폰)에서 낮은 해상도로 구워져 위쪽이 깨져 보였다(잠금 원 .home-slide__lock-icon 과 같은 까닭).
   도형은 ::before 가 clip-path 로 오리고, 블러는 바깥 상자가 건다(블러가 오린 뒤에 걸리게). */
.home-rail__glow {
  position: absolute;
  top: -34px;
  left: 30px;
  width: 64px;
  height: 134px;
  opacity: 0;
  filter: blur(10px);
  transition: opacity 0.28s ease;
  pointer-events: none;
}
.home-rail__glow::before {
  content: '';
  position: absolute;
  inset: 0;
  /* ⚠️ non-token: Figma Vector 788 #D5E3FF 48% — 색 대신 단색 그림(강제 다크 대비 · tokens.css --paint-*) */
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0H99999V99999H0Z' fill='%23D5E3FF' fill-opacity='0.48'/%3E%3C/svg%3E");
  clip-path: path('M6.6462 117.438C68.0862 96.962 29.9487 49.1835 3.2 27.8539L0 3.0112L58.5846 0L64 134H6.6462V117.438Z');
}
/* 라벨 — base/body2/R-14, 흰색 → 금빛 그라디언트 글자. 활성 항목에서 아래에서 떠오른다.
   그라디언트(Figma 197.4°, #FFFEFA 17.8% → #F9E5AF 131.35%)는 단색 + 투명도로 다시 쓴 그림이다 — CSS 그라디언트는
   삼성 인터넷 강제 다크 모드에서 어두워져 라벨이 사라졌다(UT 10-27 · tokens.css --paint-* 머리말) */
.home-rail__label {
  font-family: var(--font-base);
  font-size: 14px;
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: 0.01em;
  white-space: nowrap;
  background: url('../assets/shared/grad/text-rail-label.svg') 0 0 / 100% 100% no-repeat; /* ⚠️ non-token: Figma 라벨 그라디언트 */
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.28s ease, transform 0.28s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.home-rail__item:active .home-rail__pill { transform: scale(0.94); }
.home-rail__item.is-active { padding-top: 0; }
.home-rail__item.is-active .home-rail__pill {
  height: 50px;
  opacity: 1;
  background: rgba(16, 17, 33, 0.16);              /* ⚠️ non-token: Figma 활성 필 #101121 16%(2026-10-14, 전: navy-selected #202248 16%) */
  border-bottom-color: var(--lavender-600);         /* Figma border/card → lavender/600 (전: slate/300) */
}
.home-rail__item.is-active .home-rail__glow { opacity: 1; }
.home-rail__item.is-active .home-rail__icon--off { opacity: 0; }
.home-rail__item.is-active .home-rail__icon--on { opacity: 1; }
.home-rail__item.is-active .home-rail__label { opacity: 1; transform: none; }

/* ============ 콘텐츠 프레임 (contentframe) — Figma 컴포넌트 4981-55577 ============
   2026-10-23 app.css 에서 옮겨 왔다 — 홈 한 곳에서만 쓰지만 디자인시스템 페이지에서도 같은 규칙으로 그린다.
   아트 줄(230) + 12 + 캡션 자리(72) = 314. 상태 셋:
     · locked   (6088:48192) — 새까만 실루엣 + 자물쇠 "체험 완료 시 해제" · 콘텐츠 이름 / "4. THE GREAT HALL"
     · Unlocked (4981:55597) — 결과 그림 · 보조줄(흐린 글씨) / 결과 이름
     · add      (6086:25722) — 결과 그림 · 보조줄이 흰 글씨 + 꺾쇠인 링크 / 결과 이름(기숙사 "내 기숙사 사진").
                               꺾쇠 링크는 그림과 **다른 곳**으로 갈 때만 쓴다 — 퀴디치는 2026-10-29 UT 로 Unlocked 와 같은
                               흐린 보조줄("나의 퀴디치 기록")이 됐다(전: "오늘의 순위 보러가기 >" — 그림과 같은 곳이라 헷갈림)
   자리(로고 아래)와 칸 넘김 · 떠다니기 연출은 쓰는 화면(app.css)이 정한다. */
.home-frame {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
  width: 100%;
}
/* 스테이지 — 230 줄 한가운데에 262 칸이 놓여 위아래로 16씩 넘친다(시안 그대로). 칸은 자르지 않는다 —
   그림자와 칸보다 큰 아트(패트로누스 후)가 그대로 밖으로 나온다. 콘텐츠는 칸을 옆으로 끌어 넘긴다(app.js). */
.home-stage {
  position: relative;
  width: 100%;
  height: 230px;
}
.home-box {
  position: relative;
  width: 262px;
  height: 262px;
  margin: -16px auto 0;
}
.home-slide {
  position: absolute;
  inset: 0;
}

/* 아트·안내를 감싸는 안쪽 상자 — 칸 전체를 덮는 버튼(.home-slide__open) 위에 얹히고 클릭은 아래로 떨어진다 */
.home-slide__inner {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
/* 아트 — 262 칸 안의 자리(--art-w/--art-h/--art-top, 가로는 기본 가운데)는 app.js homeSlide 가 넣는다.
   그림자: 체험 후 컷은 Figma object_shadow(0 4 24 #CBF1FF 20%)가 **그림에 구워져 있다**(컴포넌트째 내보낸 컷,
   2026-10-06 하늘빛으로 다시 받음) — 여기서 또 걸면 번짐이 두 겹이 된다. 체험 전은 아래에서 0 6 20 #EFEEFF 10%. */
.home-slide__art {
  position: absolute;
  top: calc(var(--art-top, 0px) + var(--art-drop, 0px));   /* --art-drop — 그림마다 내리는 만큼(app.js HOME_ART_DROP, 10-29) */
  left: var(--art-left, 50%);
  width: var(--art-w);
  height: var(--art-h);
  transform: translateX(var(--art-tx, -50%));
  object-fit: contain;
  pointer-events: none;
}
/* 체험 전 컷은 **새까만 실루엣**이다(시안은 color-burn 으로 눌러 검게 만든다 — 같은 결과를 brightness(0) 으로 낸다).
   그림자는 컷에서 빼고 내보냈다(app.js HOME_META 주석) — 검게 누른 뒤에 시안 그림자를 건다(2026-10-20 컴포넌트 4981:49882).
     · 기본 — 0 4 24 #CBF1FF 20% (지팡이 · 기숙사 · 초상화 · 퀴디치)
     · 물약 — 0 0 24 #EAD8FF 20% */
.home-slide.is-locked .home-slide__art {
  filter: brightness(0) drop-shadow(0 4px 24px rgba(203, 241, 255, 0.2)); /* ⚠️ non-token: Figma 체험 전 그림자 */
}
.home-slide.is-locked[data-content="potion"] .home-slide__art {
  filter: brightness(0) drop-shadow(0 0 24px rgba(234, 216, 255, 0.2));   /* ⚠️ non-token: Figma 물약 체험 전 그림자 */
}
/* 패트로누스만 예외 — 체험 전 컷이 스스로 빛나는 구체라 검게 누르면 형태 없는 검은 원이 된다(디자이너 확인 2026-09-17).
   시안 컴포넌트에도 그림자가 없다. */
.home-slide.is-locked[data-content="patronus"] .home-slide__art { filter: none; }


/* 아트가 없는 콘텐츠 — 아트가 준비될 때까지 자리만 지킨다 (⚠️ asset 필요) */
.home-slide__art--empty {
  border-radius: 50% 50% 46% 46% / 60% 60% 40% 40%;
  background: radial-gradient(60% 60% at 50% 40%, var(--navy-800) 0%, var(--navy-950) 100%);
}

/* 체험 전 안내 — 자물쇠 + "체험 완료 시 해제" (Figma contentframe locked 6088:48192 — 줄 y 69 = 칸 y 85). 누르는 건 칸 전체다. */
.home-slide__lock {
  position: absolute;
  top: 85px;
  left: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: 131px;
  transform: translateX(-50%);
}
/* 자물쇠 원 — 60 원 + 테두리 바깥 1 = 62(시안 Group 1189 inset −1.67%). 2026-10-14 시안부터 푸른빛이다 —
   안쪽 빛 ice #4288C0 20%(전: purple_200 40%) · 면 가운데 slate/300(전: #DACFF0).
   2026-10-20 원은 CSS 로 그린다 — 시안 export svg(배경 흐림 foreignObject · 안쪽 그림자 필터)를 <img> 로 넣으면 브라우저가
   낮은 해상도로 굽고 화면 확대(--app-scale)에 늘려 테두리가 흐릿했다(디자이너 지적). 자물쇠 모양만 svg 로 남겼다.
     면 흰 → slate/300 → 흰 10% · 테두리 #FEFDFC 32% · 안쪽 빛 블러 8(시안 stdDeviation 4) · 배경 흐림 10(시안 20) */
.home-slide__lock-icon {
  display: grid;
  place-items: center;
  box-sizing: border-box;
  width: 62px;
  height: 62px;
  margin: -1px;
  border: 1px solid transparent;
  border-radius: 50%;
  /* ⚠️ non-token: 시안 면 그라데이션(흰 → slate/300 #9DAFC8 → 흰) · 10% + 테두리 #FEFDFC 32% — 둘 다 한 장의 그림으로
     (단색 + 투명도로 다시 쓴 벡터라 확대해도 또렷하다). 색으로 칠하면 삼성 인터넷 강제 다크 모드에서 어두워진다 */
  background: url('../assets/shared/grad/lock-icon.svg') border-box 0 0 / 100% 100% no-repeat;
  box-shadow: inset 0 0 8px rgba(66, 136, 192, 0.2);   /* ⚠️ non-token: 시안 안쪽 빛 ice #4288C0 20% */
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
.home-slide__lock-icon img { display: block; width: 15.768px; height: 21.4px; }
.home-slide__lock-label {
  /* base/body1/R-16 */
  font-family: var(--font-base);
  font-size: 16px;
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: 0.01em;
  white-space: nowrap;
  color: color-mix(in srgb, var(--text-primary) 90%, transparent); /* ⚠️ non-token alpha: Figma 90% */
}

/* ── 캡션 — 자리 72, 두 상태 모두 위에서부터 쌓는다(프레임 314 고정 — 그래서 레일 자리가 상태와 상관없다) ──
     · locked — 콘텐츠 이름(accent/head1/SB-24) 위 · "4. THE GREAT HALL"(base/body2/L-14) 아래, 간격 4 · 67 칸 안 가운데
                (.home-caption__link--static) — 72 자리 아래 5 는 비어 있다
     · Unlocked · add — 보조줄 위 · 결과 이름(세리프 36 · 줄 43) 아래, 간격 4 → 72
       (2026-10-23 컴포넌트 수정 — 전: 간격 8 · 자리 67 에서 5 넘침) ── */
.home-caption {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  flex-shrink: 0;
  height: 72px;
  text-align: center;
  white-space: nowrap;
}
.home-caption__link {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.home-caption__link--static { justify-content: center; height: 67px; cursor: default; }
/* 물약 제조 실패 — 「물약 제조 실패! / FAIL」 두 줄(21 + 4 + 31)을 67 칸 가운데에(Figma potion_before 6719:24549 · 6806:17228).
   결과 이름(Nimbus 36)보다 낮아 위에서부터 쌓으면 시안보다 5 위에 앉는다. 누르면 결과로 간다(버튼 그대로). */
.home-caption__link--center { justify-content: center; height: 67px; }
/* 보조줄이 따로 누르는 링크인 콘텐츠(기숙사 "내 기숙사 사진") — 캡션이 버튼 둘로 갈린다: 보조줄 · 결과 이름(결과 화면) */
.home-caption__link--split { cursor: default; }
.home-caption__open {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.home-caption__name {
  font-family: var(--font-accent);
  font-weight: 600;
  font-size: 24px;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--text-primary);
}
.home-caption__sub {
  font-family: var(--font-base);
  font-size: 14px;
  font-weight: 300;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--text-secondary);
}
/* add — 보조줄이 링크다: 흰 base/body2/R-14 + 꺾쇠 16(간격 2) (Figma contentframe add 6086:25715).
   기숙사 "내 기숙사 사진"(따로 누르는 버튼 — 키오스크 사진 자리). 그림과 다른 곳으로 가는 보조줄에만 쓴다
   (퀴디치 "오늘의 순위 보러가기 >"는 2026-10-29 UT 로 걷었다 — 캡션 전체가 같은 결과로 가던 경우). */
.home-caption__sub--link {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 0;
  border: 0;
  background: none;
  font-weight: 400;
  color: var(--text-primary);
}
button.home-caption__sub--link { cursor: pointer; }
.home-caption__sub--link img { display: block; width: 16px; height: 16px; }
/* 결과 이름 — Figma Nimbus Roman No9 L Regular 36 · 자간 −0.5 (2026-10-06, 예전 Harry Beast 자리). 폰트는 tokens.css */
.home-caption__result {
  font-family: var(--font-serif-title);
  font-weight: 400;
  font-size: 36px;
  line-height: normal;
  letter-spacing: -0.5px;
  text-transform: uppercase;
  color: var(--text-primary);
}

/* ============ 나의 QR (myqr) — Figma 컴포넌트 5743-52170 ============
   2026-10-14 app.css 에서 옮겨 왔다 — 홈 한 곳에서만 쓰지만 디자인시스템 페이지에서도 같은 규칙으로 그린다.
   자리(화살표 아래 33)는 쓰는 화면(app.css)이 정한다. QR 그림은 화면이 qrcode.js 로 그려 .home-myqr__img 로 넣는다.
   2026-10-29 「루모스」로 바꿨다(시안 비교 preview 를 거쳐 다운 결정) — 위에서 내려온 선 끝의 빛 한 점이 천천히 숨 쉬고,
   그 점에서 빛줄기가 퍼져 내려와 어두운 원 위에 뜬 QR 을 비추고 바닥에 고인다. 빛줄기 둘레로 빛먼지가 떠내려간다.
   전: 가로 띠 판 · 회청색 번짐(#525C63) · 팔각 빛 테두리(myqr-frame.svg). ⚠️ Figma 컴포넌트는 아직 전 모양이다.
   장식 72(판과 10 겹친다) + 판(8 · 제목 24 · 6 · 본문 276 · 8) = 384, 폭 334.
   <section class="home-myqr">
     <img class="home-myqr__deco" src="assets/home/myqr-deco.svg" alt="">
     <div class="home-myqr__panel">
       <h2 class="home-myqr__title">나의 QR</h2>
       <div class="home-myqr__body">
         <div class="home-myqr__code"><svg class="home-myqr__img">…(오른쪽 아래 돋보기 .home-myqr__glass)</svg><button class="home-myqr__zoom"></button></div>
         <p class="home-myqr__number">1234 5678</p>          ← 값이 없으면 <p class="home-myqr__empty">…</p> 하나만
       </div>
     </div>
   </section> */
.home-myqr {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 334px;
}
/* 빛 한 점 — 선 끝 56 에 있다. 그림은 단색 + 투명도로만 그렸다(삼성 인터넷 강제 다크는 그라디언트만 어둡게 누른다) */
.home-myqr__deco {
  position: relative;
  z-index: 2;
  display: block;
  width: 48px;
  height: 72px;
  margin-bottom: -10px;
  animation: myqr-breathe 3.6s ease-in-out infinite;
}
@keyframes myqr-breathe { 50% { opacity: 0.7; } }
/* 빛줄기 — 빛 한 점(위 56)을 꼭짓점으로 한 원뿔. 각도로는 가운데가 둥글게 밝고 양옆으로 서서히 사라진다(가운데 선이 서지 않게).
   꼭대기는 마스크로 비워 제목 둘레가 뭉쳐 밝아지지 않고, QR 쯤에서 가장 밝다가 바닥으로 사라진다 */
.home-myqr::after {
  content: '';
  position: absolute;
  top: 56px;
  left: 50%;
  width: 380px;
  height: 340px;
  transform: translateX(-50%);
  transform-origin: 50% 0;   /* 불 켜기(아래)에서 꼭짓점을 두고 퍼진다 */
  background: conic-gradient(from 0deg at 50% 0,
    rgba(194, 226, 255, 0) 0deg 148deg, rgba(194, 226, 255, 0.035) 155deg, rgba(194, 226, 255, 0.09) 162deg,
    rgba(194, 226, 255, 0.15) 169deg, rgba(194, 226, 255, 0.19) 175deg, rgba(194, 226, 255, 0.2) 180deg,
    rgba(194, 226, 255, 0.19) 185deg, rgba(194, 226, 255, 0.15) 191deg, rgba(194, 226, 255, 0.09) 198deg,
    rgba(194, 226, 255, 0.035) 205deg, rgba(194, 226, 255, 0) 212deg 360deg);
  -webkit-mask-image: linear-gradient(180deg, transparent 0, rgba(0, 0, 0, 0.35) 40px, #000 110px, rgba(0, 0, 0, 0.6) 220px, transparent 330px);
  mask-image: linear-gradient(180deg, transparent 0, rgba(0, 0, 0, 0.35) 40px, #000 110px, rgba(0, 0, 0, 0.6) 220px, transparent 330px);
  pointer-events: none;
}
.home-myqr__panel {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 100%;
  padding: 8px 0;
  box-sizing: border-box;
}
/* 빛먼지 — 점 하나가 그림 한 겹(6×340 세로 반복). 두 겹을 다른 빠르기로 내려 깊이를 준다.
   QR 칸(174)은 마스크로 비워 모듈 위로는 지나가지 않는다 — 빛 한 점부터 QR 아래까지, 빛줄기 둘레에서만 보인다.
   구멍 자리(59, 123.5)는 판 위 −60 에서 잰 QR 자리 − 4 다 — 본문은 위에서부터 쌓아(아래 .home-myqr__body) QR 이 늘 같은 자리다 */
.home-myqr__panel::before,
.home-myqr__panel::after {
  content: '';
  position: absolute;
  z-index: 1;
  top: -60px;
  left: 50%;
  width: 300px;
  height: 340px;
  margin-left: -150px;
  background-size: 6px 340px;
  background-repeat: repeat-y;
  background-position-y: 0;
  -webkit-mask: linear-gradient(#000 0 0) 59px 123.5px / 182px 182px no-repeat, radial-gradient(closest-side, #000 45%, transparent) 0 0 / 100% 100% no-repeat;
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) 59px 123.5px / 182px 182px no-repeat exclude, radial-gradient(closest-side, #000 45%, transparent) 0 0 / 100% 100% no-repeat;
  pointer-events: none;
}
.home-myqr__panel::before {
  background-image:
    radial-gradient(circle at 3px 60px, rgba(230, 244, 255, 0.9) 0 1.2px, rgba(230, 244, 255, 0) 2px),
    radial-gradient(circle at 3px 210px, rgba(230, 244, 255, 0.75) 0 1px, rgba(230, 244, 255, 0) 1.8px),
    radial-gradient(circle at 3px 30px, rgba(230, 244, 255, 0.95) 0 1.3px, rgba(230, 244, 255, 0) 2.1px),
    radial-gradient(circle at 3px 120px, rgba(230, 244, 255, 0.7) 0 1px, rgba(230, 244, 255, 0) 1.8px),
    radial-gradient(circle at 3px 250px, rgba(230, 244, 255, 0.85) 0 1.2px, rgba(230, 244, 255, 0) 2px),
    radial-gradient(circle at 3px 80px, rgba(230, 244, 255, 0.75) 0 1px, rgba(230, 244, 255, 0) 1.8px),
    radial-gradient(circle at 3px 300px, rgba(230, 244, 255, 0.85) 0 1.2px, rgba(230, 244, 255, 0) 2px),
    radial-gradient(circle at 3px 170px, rgba(230, 244, 255, 0.7) 0 1px, rgba(230, 244, 255, 0) 1.8px),
    radial-gradient(circle at 3px 320px, rgba(230, 244, 255, 0.8) 0 1.1px, rgba(230, 244, 255, 0) 1.9px),
    radial-gradient(circle at 3px 290px, rgba(230, 244, 255, 0.7) 0 1px, rgba(230, 244, 255, 0) 1.8px);
  background-position-x: 40px, 82px, 118px, 150px, 176px, 206px, 238px, 262px, 60px, 136px;
  animation: myqr-dust 24s linear infinite, myqr-twinkle 5.2s ease-in-out infinite alternate;
}
.home-myqr__panel::after {
  background-image:
    radial-gradient(circle at 3px 150px, rgba(230, 244, 255, 0.6) 0 0.7px, rgba(230, 244, 255, 0) 1.5px),
    radial-gradient(circle at 3px 20px, rgba(230, 244, 255, 0.55) 0 0.7px, rgba(230, 244, 255, 0) 1.5px),
    radial-gradient(circle at 3px 190px, rgba(230, 244, 255, 0.6) 0 0.7px, rgba(230, 244, 255, 0) 1.5px),
    radial-gradient(circle at 3px 230px, rgba(230, 244, 255, 0.55) 0 0.7px, rgba(230, 244, 255, 0) 1.5px),
    radial-gradient(circle at 3px 40px, rgba(230, 244, 255, 0.6) 0 0.7px, rgba(230, 244, 255, 0) 1.5px),
    radial-gradient(circle at 3px 100px, rgba(230, 244, 255, 0.55) 0 0.7px, rgba(230, 244, 255, 0) 1.5px),
    radial-gradient(circle at 3px 260px, rgba(230, 244, 255, 0.5) 0 0.7px, rgba(230, 244, 255, 0) 1.5px);
  background-position-x: 54px, 98px, 164px, 222px, 250px, 126px, 30px;
  animation: myqr-dust 38s linear infinite, myqr-twinkle 7.4s ease-in-out -2s infinite alternate;
}
@keyframes myqr-dust { to { background-position-y: 340px; } }
/* 한 겹의 밝기가 65 ~ 100% 사이를 오간다(두 겹이 엇갈린다). 반짝임은 filter 의 opacity 로 — opacity 속성은 불 켜기의
   번져 나오기가 쓴다.
   ⚠️ 겹째 좌우로 흔들지 않는다(10-30 디자이너 「QR 이 나오고 나서 살짝 흔들린다」) — 둘레 빛먼지가 한꺼번에 옆으로 움직이면
      가만히 있는 QR 이 반대로 흔들려 보인다(유도 운동 착시). 전: 9초 · 13초에 ±4px */
@keyframes myqr-twinkle { from { filter: opacity(0.65); } to { filter: opacity(1); } }
/* ── 불 켜기 (2026-10-29) — 홈을 내려 나의 QR 이 화면에 들어오면 app.js 가 .is-dark 를 .is-lighting 으로 바꾼다.
   0 선이 위에서 내려온다(0.7s) → 0.55 선 끝에서 빛 한 점이 터진다 → 0.5 제목 → 0.6 빛줄기가 꼭짓점에서 퍼지며 내려온다(1s)
   → 0.8 어두운 원 · QR 이 윗변 가운데부터 번져 드러난다(0.9s) → 1.1 바닥에 빛이 고인다 · 1.25 조회 번호 → 1.2 빛먼지가 번져 나온다.
   그 뒤로는 빛 점의 숨쉬기와 빛먼지만 잔잔히 이어진다. 기본은 켜진 모습이다 — .is-dark 일 때만 감추므로, 스크립트가 못 돌면
   QR 은 처음부터 보인다(디자인시스템 페이지도 켜진 모습). */
/* 또렷한 것(선 · 제목 · QR · 조회 번호)은 처음부터 끝까지 제 겹에서 그린다(10-30 「QR 이 나오고 나서 살짝 흔들린다」).
   불 켜기 동안 빛줄기 · 어두운 원 · 바닥 빛 · 번쩍임은 움직이는 동안만 따로 떼어 그려지고, 그 위에 겹치는 QR · 글자도
   덩달아 따로 그려졌다가 움직임이 끝나는 1.5 ~ 1.9초에 원래대로 돌아온다. 앱 무대가 기기 폭에 맞춰 확대돼
   있어(아이폰 15 1.048배) 모듈 경계가 픽셀 사이에 걸리고, 두 방식이 반올림을 달리하면 그 순간 1px 쯤 움찔한다. */
.home-myqr :is(.home-myqr__deco, .home-myqr__title, .home-myqr__img, .home-myqr__number) { will-change: transform; }
.home-myqr.is-dark :is(.home-myqr__deco, .home-myqr__title, .home-myqr__number, .home-myqr__img),
.home-myqr.is-dark::after,
.home-myqr.is-dark .home-myqr__panel::before,
.home-myqr.is-dark .home-myqr__panel::after,
.home-myqr.is-dark .home-myqr__code::before,
.home-myqr.is-dark .home-myqr__code::after { opacity: 0; }
.home-myqr.is-lighting .home-myqr__deco {
  animation: myqr-line-in 0.7s cubic-bezier(0.45, 0, 0.25, 1) both, myqr-breathe 3.6s ease-in-out 1.6s infinite;
}
@keyframes myqr-line-in { from { clip-path: inset(0 0 100% 0); } to { clip-path: inset(0); } }
/* 빛 한 점이 터지는 번쩍임 — 선 끝(위 56)에서 한 번 */
.home-myqr.is-lighting::before {
  content: '';
  position: absolute;
  z-index: 3;
  top: 36px;
  left: 50%;
  width: 40px;
  height: 40px;
  margin-left: -20px;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(230, 244, 255, 0.8), rgba(194, 226, 255, 0.25) 45%, rgba(194, 226, 255, 0));
  pointer-events: none;
  animation: myqr-flash 0.7s ease-out 0.55s both;
}
@keyframes myqr-flash {
  0% { opacity: 0; transform: scale(0.5); }
  30% { opacity: 0.6; }
  100% { opacity: 0; transform: scale(1.4); }
}
.home-myqr.is-lighting::after { animation: myqr-beam-in 1s cubic-bezier(0.25, 0.6, 0.3, 1) 0.6s both; }
@keyframes myqr-beam-in {
  from { opacity: 0; transform: translateX(-50%) scale(0.15, 0.3); }
  to { opacity: 1; transform: translateX(-50%); }
}
.home-myqr.is-lighting .home-myqr__title { animation: myqr-fade-in 0.6s ease-out 0.5s both; }
.home-myqr.is-lighting .home-myqr__code::before { animation: myqr-circle-in 0.7s ease-out 0.8s both; }
@keyframes myqr-circle-in { from { opacity: 0; transform: scale(0.85); } to { opacity: 1; transform: none; } }
/* QR — 빛줄기가 닿는 윗변 가운데에서부터 둥글게 번져 드러난다(가장자리 48 은 부드럽게).
   번지는 반지름은 등록한 속성(@property)이라야 움직인다 — 모르는 브라우저는 반쯤에서 한 번에 드러난다 */
@property --myqr-lit { syntax: '<length>'; inherits: false; initial-value: 240px; }
.home-myqr.is-lighting .home-myqr__img {
  -webkit-mask-image: radial-gradient(circle at 50% 0, #000 var(--myqr-lit), transparent calc(var(--myqr-lit) + 48px)), linear-gradient(180deg, #000, rgba(0, 0, 0, 0.9));
  -webkit-mask-composite: source-in;
  mask-image: radial-gradient(circle at 50% 0, #000 var(--myqr-lit), transparent calc(var(--myqr-lit) + 48px)), linear-gradient(180deg, #000, rgba(0, 0, 0, 0.9));
  mask-composite: intersect;
  animation: myqr-qr-in 0.9s cubic-bezier(0.3, 0.5, 0.3, 1) 0.8s both;
}
/* 번지는 동안 걸던 밝기 필터(brightness 1.7 → 1)는 뺐다(10-29) — 모듈이 이미 흰색이라 더 밝아지지 않았고, 마스크와
   필터를 함께 건 그림은 Safari 가 네모난 겹으로 따로 그려 QR 이 생길 때 뒤에 네모 상자가 비쳤다(디자이너 실기기). */
@keyframes myqr-qr-in {
  from { --myqr-lit: -48px; }
  to { --myqr-lit: 240px; }
}
.home-myqr.is-lighting .home-myqr__code::after { animation: myqr-floor-in 0.8s ease-out 1.1s both; }
@keyframes myqr-floor-in { from { opacity: 0; transform: scaleX(0.3); } to { opacity: 1; transform: none; } }
.home-myqr.is-lighting .home-myqr__number { animation: myqr-fade-in 0.6s ease-out 1.25s both; }
.home-myqr.is-lighting .home-myqr__panel::before {
  animation: myqr-dust 24s linear infinite, myqr-twinkle 5.2s ease-in-out infinite alternate, myqr-fade-in 1.4s ease-out 1.2s both;
}
.home-myqr.is-lighting .home-myqr__panel::after {
  animation: myqr-dust 38s linear infinite, myqr-twinkle 7.4s ease-in-out -2s infinite alternate, myqr-fade-in 1.4s ease-out 1.4s both;
}
@keyframes myqr-fade-in { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .home-myqr__deco,
  .home-myqr__panel::before,
  .home-myqr__panel::after,
  .home-myqr.is-lighting::before,
  .home-myqr.is-lighting::after,
  .home-myqr.is-lighting :is(.home-myqr__deco, .home-myqr__title, .home-myqr__img, .home-myqr__number),
  .home-myqr.is-lighting .home-myqr__code::before,
  .home-myqr.is-lighting .home-myqr__code::after,
  .home-myqr.is-lighting .home-myqr__panel::before,
  .home-myqr.is-lighting .home-myqr__panel::after { animation: none; }
  .home-myqr.is-lighting::before { display: none; }
}
.home-myqr__title {
  margin: 0;
  /* accent/body1/R-18 */
  font-family: var(--font-accent);
  font-weight: 400;
  font-size: 18px;
  line-height: 24px;
  letter-spacing: -0.01em;
  text-align: center;
  color: var(--text-primary);
  text-shadow: 0 0 14px rgba(194, 226, 255, 0.55);   /* ⚠️ non-token: 빛 한 점 아래라 은은하게 빛난다 */
}
/* 본문 276 = QR 상자 233(213 + 위아래 10) · 4 · 조회 번호 39(위 18 + 21). 위에서부터 쌓는다 — 조회 번호가 없어도 QR 이
   제자리에 있어야 빛먼지 구멍과 맞는다. 안내 문구(.home-myqr__empty)는 가운데에 */
.home-myqr__body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  height: 276px;
}
.home-myqr__code {
  position: relative;
  width: 213px;
  height: 213px;
  margin: 10px;
  cursor: default;
}
/* QR 뒤 어두운 원 — 가장자리를 여러 단으로 천천히 풀어 경계가 서지 않게.
   색은 --myqr-shade(r, g, b) · 진하기는 --myqr-depth(배수)다 — 기본은 남색 navy-950(0, 5, 23) · 0.75. 뒤 배경이 금빛(기숙사 · 초상화)인 칸은 쓰는 화면이 바꾼다
   (app.css .home-myqr[data-tone="gold"], 2026-10-29). */
.home-myqr__code::before {
  content: '';
  position: absolute;
  inset: -22px;
  --k: var(--myqr-depth, 0.75);   /* 진하기 — 남색은 0.75(10-29 "너무 색이 보인다" → 전 1), 금빛 칸은 화면이 1 로 둔다 */
  background: radial-gradient(closest-side,
    rgba(var(--myqr-shade, 0, 5, 23), calc(0.92 * var(--k))) 62%, rgba(var(--myqr-shade, 0, 5, 23), calc(0.72 * var(--k))) 74%,
    rgba(var(--myqr-shade, 0, 5, 23), calc(0.36 * var(--k))) 87%, rgba(var(--myqr-shade, 0, 5, 23), calc(0.1 * var(--k))) 95%,
    rgba(var(--myqr-shade, 0, 5, 23), 0) 100%);
}
/* 바닥 — 빛줄기가 닿아 고인 빛.
   가장 밝은 속(흰빛)이 45% 에서 곧게 끝나 바깥 번짐과의 경계가 줄처럼 보여(디자이너 10-29) 두 겹 모두 단을 나눠
   천천히 사라지게 했다 — 속은 바깥 번짐 안쪽(70%)까지 이어 풀리고, 바깥 번짐도 끝에서 한 번 더 늦춘다. */
.home-myqr__code::after {
  content: '';
  position: absolute;
  left: -8px;
  right: -8px;
  bottom: -24px;
  height: 18px;
  border-radius: 50%;
  background:
    radial-gradient(closest-side, rgba(230, 244, 255, 0.5), rgba(230, 244, 255, 0.36) 14%, rgba(230, 244, 255, 0.2) 28%,
      rgba(230, 244, 255, 0.09) 42%, rgba(230, 244, 255, 0.03) 56%, rgba(230, 244, 255, 0) 70%),
    radial-gradient(closest-side, rgba(194, 226, 255, 0.28), rgba(194, 226, 255, 0.2) 30%, rgba(194, 226, 255, 0.1) 60%,
      rgba(194, 226, 255, 0.03) 85%, rgba(194, 226, 255, 0));
}
/* QR 174 — 32자리 hex 는 버전 3(29 모듈)이라 한 칸이 딱 6px 다. 칸 경계를 흐리지 않는다.
   위에서 비춘 것처럼 아래로 갈수록 조금(90%) 어두워진다 */
.home-myqr__img {
  position: absolute;
  top: 19.5px;
  left: 19.5px;
  width: 174px;
  height: 174px;
  shape-rendering: crispEdges;
  -webkit-mask-image: linear-gradient(180deg, #000, rgba(0, 0, 0, 0.9));
  mask-image: linear-gradient(180deg, #000, rgba(0, 0, 0, 0.9));
}
/* 생성 QR — 모듈 path 를 흰빛으로 칠하고 바탕은 비운다 — 뒤의 어두운 원이 바탕이 된다. 반전 QR 은 스캐너가 읽는다
   (개발팀 41d7f46 에서 attemptBoth 추가). rect 는 라이브러리 그림(디자인시스템 페이지 예시)의 흰 바탕이다. */
.home-myqr__img rect { fill: transparent; }
.home-myqr__img path { fill: var(--icon-primary); }
/* 크게 보기(돋보기) — QR 안 오른쪽 아래 귀퉁이 5 × 5 칸 자리(#425 · 2026-11-04 다운 「QR 은 그대로 두고 돋보기를 누르면 크게」 ·
   「QR 안 우측 하단에 자연스럽게」). 그림은 QR 그림 안에 QR 과 같은 흰빛 선으로 있다(app.js qrSvgTag glass) — 덧붙인 딱지가
   아니라 QR 의 한 칸처럼 보이고, 불 켜기 때 QR 과 함께 드러난다. 칸이 아니라 선이라 모서리를 둥글게 그린다.
   단추는 그 위에 투명하게 얹는다(QR 칸 기준 24 ~ 28번째 칸 = 163.5 · 30). 누르는 자리는 44.
   누르면 화면이 QR 을 화면 폭 가득 띄운다(app.css 「나의 QR 크게 보기」). */
.home-myqr__img .home-myqr__glass {
  fill: none;
  stroke: var(--icon-primary);
  stroke-width: 0.34;   /* 칸 단위 — 6px 칸에서 약 2px */
  stroke-linecap: round;
  shape-rendering: geometricPrecision;
}
.home-myqr__zoom {
  position: absolute;
  z-index: 1;
  top: 163.5px;    /* QR 윗변 19.5 + 24칸 × 6 */
  left: 163.5px;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.home-myqr__zoom::before { content: ''; position: absolute; inset: -7px; }   /* 누르는 자리 44 */
.home-myqr__zoom:focus-visible { outline: 2px solid var(--ice-200); outline-offset: 2px; }
/* 조회 번호 — base 14 · ice/100 85% · 자간 넓게 */
.home-myqr__number {
  margin: 18px 0 0;
  font-family: var(--font-base);
  font-size: 14px;
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: 0.24em;
  color: var(--ice-100);
  opacity: 0.85;
}
/* QR 값(pairingId)이 없을 때 — QR 대신 안내만 (정상 흐름에서는 안 나온다: 로그인 전이거나 서버를 못 읽은 경우) */
.home-myqr__empty {
  margin: auto 0;
  font-family: var(--font-base);
  font-size: 16px;
  font-weight: 400;
  line-height: 1.5;
  text-align: center;
  color: var(--text-secondary);
}

/* ══════════════════════════════════════════════════════════════════════
   동의 · 약관 컴포넌트 — 2026-10-19
   출처: Figma HarryPotter_UI 「ㄴ 디컴 미팅 이후 UI」 component 섹션
     check 5818:53747 · tag 5818:53754 · consent all 5818:53799 · consent item 5818:53787 ·
     sheet header 5819:6231 · callout 5819:6239 · btn/secondary 5819:6242 · btn/small 5819:6244 · link/legal 5819:6248
   쓰는 곳: 동의 시트 · 나이 확인 시트 · 보호자 동의 · 동의서 상세 · 로그인 아래 약관 시트 · 약관 페이지(app.css).
   ⚠️ 컴포넌트 설명에는 테두리가 흰 24%(전체 동의) · 40%(체크 · 보조 버튼)로 적혀 있지만 10-19 시안의 칠은
      흰 100%다 — 칠을 따랐다. 콜아웃 면도 설명(surface/glass 30%)이 아니라 칠(neutral/950)을 따랐다.
   ══════════════════════════════════════════════════════════════════════ */

/* ============ check — 22 원형. 꺼짐 흰 테두리 1.5 / 켜짐 text/accent 면 + 체크(surface/card 2) ============
   진짜 checkbox(.check-input)는 보조기기 · 키보드용으로 남기고 눈에서만 감춘다 — 바로 뒤의 .check 가 모양이다.
   <input class="check-input" type="checkbox"><span class="check" aria-hidden="true"></span> */
.check-input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}
.check {
  display: block;
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  background: url('../assets/shared/check-off.svg') center / 100% 100% no-repeat;
}
.check-input:checked + .check { background-image: url('../assets/shared/check-on.svg'); }
.check-input:focus-visible + .check { border-radius: 50%; outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* ============ tag — 문서 머리 · 화면 머리 표시. 위아래 2(예외) · 좌우 8 · 모서리 4 · base/body3/M-12 ============
   accent = 필수 · 만 14세 미만(#414F70 40% · 흰 글자) / neutral = 선택(흰 면 · 검은 글자) */
.tag {
  display: inline-flex;
  align-items: flex-start;
  padding: 2px 8px;
  border-radius: 4px;
  font-family: var(--font-base);
  font-weight: 500;
  font-size: 12px;
  line-height: 1.5;
  letter-spacing: 0.005em;
  white-space: nowrap;
}
.tag--accent {
  background: rgba(65, 79, 112, 0.4); /* ⚠️ non-token: Figma tag accent 면 #414F70 40% */
  color: var(--text-primary);
}
.tag--neutral {
  background: var(--white);
  color: var(--text-on-light);
}

/* ============ consent all — 전체 동의 상자. 테두리 1 · 모서리 12 · 여백 16 · 간격 12 · base/body1/B-16 ============
   <label class="consent-all"><input class="check-input" type="checkbox"><span class="check" aria-hidden="true"></span>
     <span class="consent-all__label">전체 동의</span></label> */
.consent-all {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px;
  position: relative;
  border: 1px solid transparent;   /* 흰 보더 — 아래 「고리 테두리」 */
  --ring: var(--paint-white); --ring-c: #fff;
  border-radius: 12px;
  cursor: pointer;
}
.consent-all__label {
  font-family: var(--font-base);
  font-weight: 700;
  font-size: 16px;
  line-height: 1.5;
  letter-spacing: 0.005em;
  color: var(--text-primary);
}

/* ============ consent item — 체크 + [필수]/[선택] + 항목 + 보기 · 간격 12 ============
   두 줄 항목은 시안에서 체크 · 보기가 첫 줄에 붙는다(위 정렬) — 한 줄 항목도 같은 규칙으로 두고, 보기만 첫 줄 가운데로
   내린다(21 줄 높이 − 14 밑줄 칸 = 3.5). 항목 글은 [필수] 오른쪽에서 줄을 바꾼다(시안 두 글 상자).
   <div class="consent-item">
     <label class="consent-item__pick"><input class="check-input" type="checkbox"><span class="check" aria-hidden="true"></span>
       <span class="consent-item__label"><em class="consent-item__kind">[필수]</em><span class="consent-item__text">항목 이름</span></span></label>
     <button class="consent-item__more" type="button">보기</button></div> */
.consent-item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
.consent-item__pick {
  display: flex;
  flex: 1;
  align-items: flex-start;
  gap: 12px;
  min-width: 0;
  cursor: pointer;
}
.consent-item__label {
  display: flex;
  flex: 1;
  gap: 4px;
  min-width: 0;
  padding-top: 0.5px;   /* 체크 22 · 글줄 21 — 시안처럼 첫 줄을 체크 가운데에 */
  font-family: var(--font-base);
  font-weight: 500;
  font-size: 14px;
  line-height: 1.5;
  letter-spacing: 0.005em;
  color: var(--text-primary);
}
.consent-item__kind {
  flex-shrink: 0;
  font-style: normal;
  color: var(--text-accent);
}
.consent-item__kind--optional { color: var(--gray-200); }
.consent-item__text { word-break: keep-all; }
/* 보기 — base/body3/R-12 gray_200 · 밑줄 0.4(시안은 14 높이 칸의 아래 테두리) */
.consent-item__more {
  flex-shrink: 0;
  height: 14px;
  margin-top: 4px;
  padding: 0;
  border: 0;
  border-bottom: 0.4px solid var(--gray-200);
  background: none;
  font-family: var(--font-base);
  font-weight: 400;
  font-size: 12px;
  line-height: 14px;
  letter-spacing: 0.01em;
  white-space: nowrap;
  color: var(--gray-200);
  cursor: pointer;
}

/* ============ sheet header — 바텀시트 머리 ============
   action   제목(base/head2/M-18) + 취소(base/body2/R-14 gray_200) · 설명(base/body2/R-14 gray_200) · 간격 4
   document 손잡이(36 × 4) + 제목(base/head1/M-20) · 시행일(base/body3/R-12 text/secondary) + 닫기(32 surface/control) */
.sheet-header {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.sheet-header__row {
  display: flex;
  align-items: center;
  gap: 12px;
}
.sheet-header__title {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-family: var(--font-base);
  font-weight: 500;
  font-size: 18px;
  line-height: 1.5;
  letter-spacing: 0.005em;
  color: var(--text-primary);
}
.sheet-header__cancel {
  flex-shrink: 0;
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--font-base);
  font-weight: 400;
  font-size: 14px;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--gray-200);
  cursor: pointer;
}
.sheet-header__lead {
  margin: 0;
  word-break: keep-all;
  font-family: var(--font-base);
  font-weight: 400;
  font-size: 14px;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--gray-200);
}
.sheet-header--document { gap: 8px; }
.sheet-header__grip {
  display: block;
  width: 36px;
  height: 4px;
  margin: 8px auto 4px;
  border-radius: 2px;
  background: var(--paint-white);   /* 흰 면 — 단색 그림(강제 다크 대비 · tokens.css --paint-*) */
}
.sheet-header__head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 20px 16px;
}
.sheet-header__titles {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.sheet-header--document .sheet-header__title { font-size: 20px; }
.sheet-header__meta {
  margin: 0;
  font-family: var(--font-base);
  font-weight: 400;
  font-size: 12px;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--text-secondary);
}
.sheet-header__close {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 16px;
  background: var(--surface-control);
  cursor: pointer;
}
.sheet-header__close img { display: block; width: 12px; height: 12px; }

/* ============ callout — 중요 내용 · 어린이 안내. neutral/950 면 · 모서리 12 · 여백 16 ============
   emphasis 글 하나(base/body2/R-14 흰 글자) / note 제목(base/body2/M-14) + 글(base/body2/R-14 gray_200) · 간격 8 */
.callout {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 0;
  padding: 16px;
  border-radius: 12px;
  background: var(--neutral-950);
  word-break: keep-all;
  font-family: var(--font-base);
  font-weight: 400;
  font-size: 14px;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--text-primary);
}
.callout--note { gap: 8px; }
.callout__title {
  margin: 0;
  font-weight: 500;
  letter-spacing: 0.005em;
  color: var(--text-primary);
}
.callout__text { margin: 0; color: var(--gray-200); }

/* ============ toggle — 켜고 끄는 스위치 51 × 31 (Figma C-1 6803:15272 「toggle · on/off」, 2026-10-30) ============
   <button role="switch" aria-checked> 하나에 켜짐 면(.toggle__on)과 손잡이(.toggle__knob)를 둔다.
   끔: 면 흰 18% · 손잡이 왼쪽 / 켬: 면 #94B7D6 · 손잡이 오른쪽(20 이동). 손잡이 흰 27 원 · 그림자 0 2 4 검정 25%.
   시안은 상태마다 그림(svg)이지만 손잡이가 미끄러지게 CSS 로 그린다 — 칸 · 색 · 그림자는 그림 값 그대로다.
   누르는 칸은 44 높이로 넓힌다(::after). */
.toggle {
  position: relative;
  isolation: isolate;
  flex-shrink: 0;
  width: 51px;
  height: 31px;
  padding: 0;
  border: 0;
  border-radius: 15.5px;
  background: color-mix(in srgb, var(--neutral-0) 18%, transparent);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.toggle::after { content: ""; position: absolute; inset: -7px -4px; }
.toggle__on {
  position: absolute;
  inset: 0;
  isolation: isolate;
  border-radius: inherit;
  background: #94B7D6;   /* ⚠️ non-token: Figma toggle · on 면 */
  opacity: 0;
  transition: opacity 0.2s ease;
}
.toggle__knob {
  position: absolute;
  top: 2px;
  left: 2px;
  isolation: isolate;
  width: 27px;
  height: 27px;
  border-radius: 50%;
  background: var(--neutral-0);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
  transition: transform 0.2s ease;
}
.toggle[aria-checked="true"] .toggle__on { opacity: 1; }
.toggle[aria-checked="true"] .toggle__knob { transform: translateX(20px); }
.toggle:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.toggle[aria-busy="true"] { cursor: progress; }
@media (prefers-reduced-motion: reduce) {
  .toggle__on, .toggle__knob { transition: none; }
}

/* ============ btn/secondary — 외곽선 알약 335 × 55 · 라벨 base/head2/M-18 ============ */
.btn-secondary {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 335px;
  max-width: 100%;
  height: 55px;
  box-sizing: border-box;
  padding: 0;
  position: relative;
  border: 1px solid transparent;   /* 흰 보더 — 아래 「고리 테두리」 */
  --ring: var(--paint-white); --ring-c: #fff;
  border-radius: var(--radius-full);
  background: none;
  font-family: var(--font-base);
  font-weight: 500;
  font-size: 18px;
  line-height: 1.5;
  letter-spacing: 0.005em;
  white-space: nowrap;
  color: var(--text-primary);
  cursor: pointer;
}
.btn-secondary:active { transform: translateY(1px); }

/* ============ btn/small — 입력 칸 옆 52 높이 외곽선 알약 · 좌우 16 · 라벨 base/body2/M-14 ============ */
.btn-small {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  height: 52px;
  box-sizing: border-box;
  padding: 0 16px;
  position: relative;
  border: 1px solid transparent;   /* 흰 보더 — 아래 「고리 테두리」 */
  --ring: var(--paint-white); --ring-c: #fff;
  border-radius: var(--radius-full);
  background: none;
  font-family: var(--font-base);
  font-weight: 500;
  font-size: 14px;
  line-height: 1.5;
  letter-spacing: 0.005em;
  white-space: nowrap;
  color: var(--text-primary);
  cursor: pointer;
}
.btn-small:disabled { opacity: 0.4; cursor: default; }

/* ============ link/legal — 로그인 아래 약관 링크. 이용약관 · 개인정보처리방침 · 간격 8 · base/body3/R-12 text/id-number ============ */
.link-legal {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-base);
  font-weight: 400;
  font-size: 12px;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--text-id-number);
}
.link-legal__item {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  letter-spacing: inherit;
  color: inherit;
  cursor: pointer;
}

/* ══════════════════════════════════════════════════════════════════════
   피드백 컴포넌트 — 2026-10-21
   출처: Figma HarryPotter_UI 「ㄴ 디컴 미팅 이후 UI」 component 섹션 Feedback
     toast 3903:27089(property1 = success · fail) · dialog(확인 팝업 — 코치마크 coach 6064:25290 의 무드를 따름)
   쓰는 곳: 화면 아래 토스트(app.js showToast · app.css .scope-toast) · 로그아웃 · 회원탈퇴 · 동의 거부 확인 팝업(.acdialog).
   ══════════════════════════════════════════════════════════════════════ */

/* ============ toast — 반투명 알약 + 양끝 반짝이 · 점. 성공은 초록, 실패는 빨강 ============
   알약은 글자 폭만큼(시안 글자 칸 148 → 알약 182 × 39, 테두리 1 안쪽), 긴 문구는 두 줄로 감긴다.
   반짝이 · 점은 알약 가장자리에 시안 자리 그대로 붙는다(시안 틀 214 에 알약 x16) — 반짝이는 양쪽 다 테두리
   안쪽 끝에서 9 바깥(16 칸 가운데쯤이 테두리에 걸친다), 점은 14 바깥. 위아래는 알약 가운데(시안 반짝이 top 12 · 점 18).
   <div class="toast toast--success">
     <img class="toast__dot toast__dot--l" src="assets/shared/toast-dot-success.svg" alt="">
     <img class="toast__spark toast__spark--l" src="assets/shared/toast-spark-success-l.svg" alt="">
     <p class="toast__text">이미지가 저장되었어요</p>
     <img class="toast__spark toast__spark--r" src="assets/shared/toast-spark-success-r.svg" alt="">
     <img class="toast__dot toast__dot--r" src="assets/shared/toast-dot-success.svg" alt="">
   </div> */
.toast {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  max-width: 303px;   /* 화면 375 에서 양끝 장식(14 · 14)과 여백 20 을 뺀 폭 */
  padding: 8px 16px;
  box-sizing: border-box;
  border: 1px solid transparent;   /* green_500 보더 — 아래 「고리 테두리」 */
  --ring: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0H99999V99999H0Z' fill='%2392FA9B'/%3E%3C/svg%3E"); --ring-c: #92FA9B;
  border-radius: 12px;
  background: rgba(32, 33, 37, 0.6);   /* ⚠️ non-token: 시안 토스트 면(#202125 60%) */
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);           /* 배경 흐림 2(시안 4 — 이 프로젝트는 시안 배경 흐림의 반을 쓴다, 2026-10-21) */
}
.toast--fail { --ring: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0H99999V99999H0Z' fill='%23FF8484'/%3E%3C/svg%3E"); --ring-c: #FF8484; }   /* red_500 */
.toast__text {
  min-width: 148px;   /* 시안 글자 칸 — 짧은 문구도 알약이 182 아래로 줄지 않는다 */
  margin: 0;
  word-break: keep-all;
  text-wrap: balance;   /* 두 줄이 되면 줄 길이를 고르게(끝 단어 하나만 내려가지 않게) */
  white-space: pre-line;   /* 문구의 \n 자리에서 줄을 바꾼다 — 두 문장 토스트는 문장 사이에서 나눈다(선택 동의 관리 11-04) */
  /* base/body2/M-14 */
  font-family: var(--font-base);
  font-weight: 500;
  font-size: 14px;
  line-height: 1.5;
  letter-spacing: 0.005em;
  text-align: center;
  color: var(--white);
}
.toast__spark,
.toast__dot { position: absolute; display: block; max-width: none; pointer-events: none; }
.toast__spark { top: calc(50% - 7.5px); width: 16px; height: 16px; }
.toast__dot { top: calc(50% - 1.5px); width: 3px; height: 3px; }
.toast__spark--l { left: -9px; }     /* 테두리 안쪽에서 잰다 — 시안 x8(알약 테두리 안쪽 17) */
.toast__dot--l { left: -14px; }      /* 시안 x3 */
.toast__spark--r { right: -9px; }    /* 시안 x190(16 칸 끝 206 · 테두리 안쪽 197) */
.toast__dot--r { right: -14px; }     /* 시안 x208 */

/* ============ dialog — 확인 팝업. 코치마크 말풍선(.coach__card)과 같은 글자 · 버튼, 판은 빛이 더 넓고 옅다 ============
   폭 335(화면 여백 20) · 안쪽 여백 16 · 20(테두리 1 은 여백에서 뺀다) · 제목 B-16 → 본문 R-14(4) → (16) 버튼 줄(양 끝).
   테두리는 판 안쪽 1(neutral_950 — 바탕과 같은 색) + 판 바깥 1(slate_600 24% — 시안 border 칸이 inset −1), 빛은 흰 10% · 12.
   본문에서 짚을 구절은 <b> — 굵기는 그대로, 색만 text/accent. 왼쪽 [취소]는 글자만, 오른쪽 실행 버튼은 코치마크 [다음]과
   같은 알약. 자리(가운데) · 딤은 쓰는 화면(app.css .acdialog)이 정한다.
   <div class="dialog" role="alertdialog" aria-modal="true" aria-labelledby="…">
     <p class="dialog__title">로그아웃할까요?</p>
     <p class="dialog__body">다시 로그인하면<br><b>체험 내역을 그대로</b> 볼 수 있어요</p>
     <div class="dialog__actions">
       <button class="dialog__btn" type="button">취소</button>
       <button class="dialog__btn dialog__btn--go" type="button"><span class="dialog__btn-label">로그아웃</span></button>
     </div>
   </div> */
.dialog {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 335px;
  padding: 15px 19px;
  box-sizing: border-box;
  /* 안쪽 선 neutral_950 은 판 면(surface/card)과 같은 색이라 투명으로 두고 면이 그 밑까지 깔리게 한다 — 보기는 같고,
     어두운 테두리 색은 삼성 인터넷 강제 다크 모드에서 밝게 뒤집혀 판 둘레에 흰 선이 생겼다(2026-10-27) */
  border: 1px solid transparent;
  border-radius: 12px;
  background: var(--surface-card);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--slate-600) 24%, transparent);   /* 바깥 선 */
  filter: drop-shadow(0 0 12px rgba(255, 255, 255, 0.1));   /* ⚠️ non-token: 시안 팝업 빛(흰 10% · 12) */
  text-align: left;
}
.dialog__title {
  margin: 0;
  word-break: keep-all;
  /* base/body1/B-16 */
  font-family: var(--font-base);
  font-weight: 700;
  font-size: 16px;
  line-height: 1.5;
  letter-spacing: 0.005em;
  color: var(--text-primary);
}
.dialog__body {
  margin: 4px 0 0;
  word-break: keep-all;
  /* base/body2/R-14 */
  font-family: var(--font-base);
  font-weight: 400;
  font-size: 14px;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--gray-200);
}
.dialog__body b { font-weight: 400; color: var(--text-accent); }
.dialog__actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 16px;
}
.dialog__btn {
  height: 36px;
  padding: 0 14px;
  border: 0;
  border-radius: var(--radius-full);
  background: none;
  /* base/body2/M-14 */
  font-family: var(--font-base);
  font-weight: 500;
  font-size: 14px;
  line-height: 1.5;
  letter-spacing: 0.005em;
  color: var(--gray-200);
  cursor: pointer;
}
/* 실행 — 코치마크 [다음]과 같은 알약. 면은 #EEF1FE 단색 그림, 글자(<span class="dialog__btn-label">)는 navy_700 을
   그라디언트로 클립한다 — 밝은 CSS 면 · 어두운 단색 글자는 삼성 인터넷 강제 다크 모드에서 뒤집힌다(tokens.css --paint-*) */
.dialog__btn--go {
  padding: 0 18px;
  background: var(--paint-purple-100);   /* ⚠️ non-token: 시안 다음 버튼 면(Figma scanner/token/purple-100 — 관람객 토큰에 없다) */
  color: var(--navy-700);
}
.dialog__btn--go .dialog__btn-label {
  background: linear-gradient(var(--navy-700), var(--navy-700));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.dialog__btn:active:not(:disabled) { transform: translateY(1px); }
.dialog__btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.dialog__btn:disabled { cursor: default; }

/* ============ 고리 테두리 — 삼성 인터넷 강제 다크 모드 대비 (2026-10-27) ============
   강제 다크 모드는 CSS 테두리 색을 뒤집는다 — 밝은 테두리는 어둡게, 어두운 테두리는 밝게(사용성 테스트 10-27 ·
   tokens.css --paint-* 머리말). 그래서 테두리 칸은 투명하게(두께는 그대로) 두고 그 자리에 가상 요소로 고리를 그린다.
     --ring    고리 칠 — 밝은 색은 단색 그림(var(--paint-*) · 단색 SVG), 어두운 색은 같은 색 linear-gradient
     --ring-w  고리 두께 = 투명하게 둔 테두리 두께(기본 1px)
   고리는 테두리 칸만 칠한다(content-box 를 빼는 투명도 마스크 — 강제 다크가 건드리지 않는다). 둥근 모서리를 따른다.
   부드러운 빛(box-shadow)은 그대로 둔다 — 일반 화면을 바꾸지 않으려고(강제 다크에서는 옅어진다).
   쓰는 요소는 position 이 static 이 아니어야 하고 ::before 를 이 규칙에 내준다. 화면 요소는 app.css 「고리 테두리」. */
.consent-all::before,
.btn-secondary::before,
.btn-small::before,
.toast::before,
.input-field--pill::before {
  content: "";
  position: absolute;
  inset: calc(-1 * var(--ring-w, 1px));
  padding: var(--ring-w, 1px);
  border-radius: inherit;
  background: var(--ring);
  -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 exclude, linear-gradient(#000 0 0);
  pointer-events: none;
}
/* 입력칸 — 고리는 틀에 단다(<input> 에는 가상 요소를 못 단다). 틀이 곧 칸의 바깥 테두리 상자라 안쪽으로 당기지 않는다 */
.input-field--pill::before { inset: 0; }

/* ============ 삼성 인터넷 면 · 고리 (2026-10-28 갤럭시 실기기) ============
   삼성 인터넷 '어둡게 보기'(29.0)는 밝은 칠을 거의 다 어둡게 누른다 — CSS 색 · <path> 단색 그림 · <img> · 인라인 SVG ·
   밝은 회색 글자까지(시험 페이지 v3 1번). **캔버스에 그린 것만은 그대로** 남았다(면 다섯 색 · 로고 모두 원래 색 — v3 1-4 · 2-3).
   그래서 이 브라우저에서만(<html class="is-samsung"> — index.html 머리) 밝은 면 밑에 1×1 캔버스(.sface)를 깔아 --face 색으로
   칠한다. 캔버스는 app.js 「삼성 인터넷 — 캔버스로 칠하기」가 넣는다. 다른 브라우저는 그림 칠 그대로다.
     --face      면 색(캔버스 색)
     --face-out  캔버스를 상자 밖으로 내미는 만큼 — 투명 테두리 밑까지 칠할 때(음수)
     --ring-c    고리 색 — 고리는 글자(█)로 칠한다(흰 고리는 글자로 또렷했다 — v2 4-A · v3 4-2). --ring 옆에 둔다.
                 어두운 고리는 비워 둔다(글자가 투명해 --ring 이 보인다)
   화면 요소는 app.css 「삼성 인터넷 면 · 고리」. */
.is-samsung :is(.btn-cta, .dialog__btn--go, .sheet-header__grip) { position: relative; isolation: isolate; }
.is-samsung .sface {
  position: absolute;
  top: var(--face-out, 0px);
  left: var(--face-out, 0px);
  width: calc(100% - 2 * var(--face-out, 0px));
  height: calc(100% - 2 * var(--face-out, 0px));
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
}
.is-samsung .btn-cta { --face: #D7D7D7; }   /* gray_100 */
.is-samsung .dialog__btn--go { --face: #EEF1FE; }   /* purple-100 */
.is-samsung .sheet-header__grip { --face: #FFFFFF; }
.is-samsung .toggle__on { --face: #94B7D6; }
.is-samsung .toggle__knob { --face: #FFFFFF; }
/* 고리 — 「고리 테두리」의 가상 요소를 글자로 채운다(마스크가 테두리 칸만 남긴다) */
.is-samsung :is(.consent-all, .btn-secondary, .btn-small, .toast, .input-field--pill)::before {
  content: "\2588\2588\2588";
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  font: 400px/1 monospace;
  white-space: nowrap;
  color: var(--ring-c, transparent);
}
/* 체크 동그라미(꺼짐) — 흰 선 그림(check-off.svg)이 탁해졌다. 같은 굵기 1.5 의 글자칠 고리로 */
.is-samsung .check { position: relative; }
.is-samsung .check-input:not(:checked) + .check { background: none; }
.is-samsung .check-input:not(:checked) + .check::before {
  content: "\2588\2588";
  position: absolute;
  inset: 0;
  padding: 1.5px;
  border-radius: 50%;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  font: 400px/1 monospace;
  white-space: nowrap;
  color: #fff;
  -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 exclude, linear-gradient(#000 0 0);
}

/* ============ 삼성 인터넷 글자 — 그림으로 칠한 글자를 단색으로 (2026-10-28) ============
   실기기 삼성 인터넷(강제 다크)은 그림(SVG · PNG)을 글자에 클립한 칠을 지운다 — 바탕에 묻혀 글자가 안 보인다
   (Remote Test Lab 갤럭시 S26 FE · 시험 페이지 preview/dark-lab/ C줄). 밝은 단색 글자와 투명도 마스크는 그대로 둔다.
   그래서 이 브라우저에서만(<html class="is-samsung"> — index.html 머리) 그라디언트를 단색 · 투명도로 바꾼다.
   다른 브라우저는 시안 그대로다. 화면 쪽은 app.css 「삼성 인터넷 글자」. */
.is-samsung .content-title--gray .content-title__name {   /* 흰색 → 회보라 60% — 흰 글자를 아래로 흐리게 */
  background: none;
  color: var(--white);
  -webkit-mask-image: linear-gradient(180deg, #000, rgba(0, 0, 0, 0.4));
  mask-image: linear-gradient(180deg, #000, rgba(0, 0, 0, 0.4));
}
.is-samsung .home-rail__label { background: none; color: #FDF5DF; }   /* #FFFEFA → #FBECC4 의 가운데 */
