/* ── 폰트: 자체 호스팅 서브셋(tools/build-fonts.py 가 굽는다) ──
   ⚠️ 구글 CDN 으로 되돌리지 마라. 세 가지가 한꺼번에 나빠진다:
     ① 한글이 unicode-range 99개로 쪼개져 레슨 한 화면에 48개 요청 521KB(실측)
     ② 서비스 워커가 CDN 응답을 캐시하지 못해 **오프라인에서 폰트가 아예 안 뜬다**
     ③ 서드파티 요청이 첫 페인트를 잡는다
   지금은 4굵기 + 모노 2굵기 합계 340KB, 한 번 받으면 SW 가 들고 있는다.
   ⚠️ 문항·UI 문자열을 크게 바꾸면 tools/build-fonts.py 를 다시 돌려라 —
      서브셋에 없는 글자는 두부(□)로 뜬다. */
@font-face{font-family:"Gothic A1";font-style:normal;font-weight:400;font-display:swap;
  src:url("/fonts/gothic-a1-400.woff2") format("woff2")}
@font-face{font-family:"Gothic A1";font-style:normal;font-weight:700;font-display:swap;
  src:url("/fonts/gothic-a1-700.woff2") format("woff2")}
@font-face{font-family:"Gothic A1";font-style:normal;font-weight:800;font-display:swap;
  src:url("/fonts/gothic-a1-800.woff2") format("woff2")}
@font-face{font-family:"Gothic A1";font-style:normal;font-weight:900;font-display:swap;
  src:url("/fonts/gothic-a1-900.woff2") format("woff2")}
@font-face{font-family:"Chivo Mono";font-style:normal;font-weight:700;font-display:swap;
  src:url("/fonts/chivo-mono-500.woff2") format("woff2")}
@font-face{font-family:"Chivo Mono";font-style:normal;font-weight:700;font-display:swap;
  src:url("/fonts/chivo-mono-700.woff2") format("woff2")}
/* SETOPIK — 브랜드 세계: "한국행 탑승권".
   이 앱은 시험 앱이 아니라 한국행 탑승 수속이다. 레슨은 구간, 유닛 보스는 심사대,
   완료는 여권에 찍히는 도장, 진행도는 항로다. 목적지는 매 화면에 있어야 한다.
   왜 이 세계인가: 청중(EPS-TOPIK 수험생)이 이 공부를 하는 유일한 이유가 "한국에 간다"이고,
   탑승권·여권·도장은 32개 언어 어디서나 번역 없이 읽히는 몇 안 되는 그래픽 체계다.
   색: 랑이(주황 호랑이)가 브랜드의 얼굴인데 브랜드색이 보라였다 — 캐릭터를 브랜드색으로 승격했다.
   잉크 네이비 위에 티켓 스톡, 신호는 랑이 오렌지, 승인은 도장 레드, 통과는 게이트 그린.
   글자: 데이터(편명·좌석·점수·타이머)는 모노스페이스가 말하고, 사람에게 하는 안내는 Nunito가 말한다. */
/* 폰트는 각 HTML에서 비차단(media=print→all) 로딩 — @import는 CSSOM을 막아 저속망에서 JS 실행까지 얼린다 */
:root{
  /* 브랜드 — 랑이의 주황 */
  --brand:#F0820D; --brand-d:#C4670A; --brand-l:#FFE9CC; --brand-xl:#FFF6EA;
  --brand-t:#A9540A;              /* 텍스트용(다크에서 밝게 뒤집힘) */
  --ink-navy:#101A33;             /* 서류 잉크 = 랑이 아웃라인의 진한 형제 */
  --on-brand:#101A33;             /* 주황 위 잉크 — 테마 따라 절대 뒤집지 않는다(라이트 6.49:1 · 다크 8.20:1) */
  --paper:#FBF7EF;                /* 티켓 스톡 */
  --stamp:#D83A2E;                /* 인주 — 승인·경고 */
  --gate:#00A66B;                 /* 게이트 그린 — 통과·정답 */
  /* 의미색(초록/빨강은 정답·오답 고정) */
  --green:#00A66B; --green-d:#048457; --green-l:#D6F5E8;
  --blue:#2C6FD1; --blue-d:#1F55A6;
  --ink:#1B2436; --sub:#9AA3B2; --sub-d:#616C80;
  --bg:#FFFFFF; --card:#FFFFFF; --line:#E2DED4;
  --red:#D83A2E; --red-l:#FBE3E0; --red-d:#B32A20;
  --gold:#F5B70A; --gold-d:#8F6900;
  --well:#FBF7EF; --bub:#E6E1D6; --bub-sh:#CCC5B6; --bub-tx:#A9A192; --tear:#B4AB96;
  --btn-sh:#101A33;               /* 브랜드 버튼의 4px 단차 — 페이지 배경 위라 잉크와 역할이 다르다 */
  --stub-sh:#CCC5B6;              /* 고스트 버튼 단차 */
  /* 티켓의 목소리. ⚠️ Chivo Mono 에는 한글이 없다 — 그런데 유닛 배너("탑승구 · 유닛 5"),
     문항 유형 라벨, 보스 이름 등 17곳이 이 토큰을 쓰면서 한국어를 담는다.
     폴백에 Gothic A1 을 넣지 않으면 그 한글만 시스템 고정폭으로 떨어져
     로고·본문과 다른 글꼴로 보인다(사장님 지적 2026-08-25).
     라틴·숫자는 Chivo Mono, 한글은 본문과 같은 Gothic A1 로 간다. */
  --mono:"Chivo Mono","Gothic A1",ui-monospace,"SFMono-Regular",Menlo,monospace;
}
/* 다크 = 야간 비행. 잉크 네이비를 지면으로, 티켓은 계기판 빛으로 */
:root[data-theme="dark"]{
  --bg:#0C1426; --card:#141F38; --line:#2A3550;
  --ink:#F2F5FA; --sub:#7E8AA3; --sub-d:#AAB4C7;
  --brand:#FF9B2E; --brand-d:#D97A12; --brand-l:#3A2A14; --brand-xl:#241A0F;
  --brand-t:#FFC178;
  --paper:#141F38; --ink-navy:#F2F5FA;
  --stamp:#F87C68; /* 야간 인주 — #D83A2E 는 다크 지면에서 3.99:1 로 아슬아슬하다(7.06:1) */
  --green-l:#0D3327; --green-d:#2FD69B; --red-l:#3A1A17; --red-d:#FF7A6E;
  --well:#101B31; --bub:#2A3550; --bub-sh:#1C2540; --bub-tx:#7E8AA3; --tear:#46536F;
  --btn-sh:#D97A12; --stub-sh:#3B4A6B;
}
:root[data-theme="dark"] .streak{background:#3A2A14;color:#FFC178}
/* 다크: 답 티켓 테두리를 한 단 낮춘다(7.84:1, 여전히 3:1 훨씬 위).
   흰 잉크 4개가 나란히 서면 지문보다 시끄럽다 — .arena 와 같은 판단이다.
   선택·정답·오답은 전강도로 남아 위계가 산다 */
:root[data-theme="dark"] :is(.choice,.choice .n,.vi-card,.mt,.wb){border-color:var(--sub-d)}
:root[data-theme="dark"] :is(.choice.sel,.choice.sel .n){border-color:var(--ink-navy)}
*{box-sizing:border-box;margin:0;padding:0}
/* html{font-size:16px} 를 지웠다 — 100% 가 초기값이라 같은 값을 다시 쓰는 것은
   사용자의 OS 글자 크기 설정을 무시하는 효과만 낸다. rem 구조는 이미 갖춰져 있다. */
/* 폰트 스택 — 이 앱의 콘텐츠는 한국어이고 UI는 32개 언어다.
   Nunito가 라틴을 맡고, 그 뒤로 한글·CJK 시스템 폰트를 이름으로 명시한다(폴백 운에 맡기지 않는다).
   시스템에 없을 확률이 높은 스크립트(신할라·크메르·미얀마·라오·타밀·벵골·우르두)는
   i18n.js가 그 언어를 고른 사람에게만 웹폰트를 얹는다 — 저속망에서 전부 싣지 않기 위해서다. */
/* 사장님 지시(2026-08-25): "기본폰트를 로고 폰트랑 맞춰서 다 통일해".
   예전엔 Nunito(라틴) + Noto Sans KR(한글 폴백) 두 글꼴이 한 줄에서 부딪쳤다 —
   Nunito 에 한글 글리프가 없어서다. Gothic A1 은 라틴·한글·키릴을 다 갖고 있어
   "Unit 3 · 유닛 3 · GATE 04" 가 하나의 글꼴로 읽힌다. */
body{font-family:"Gothic A1","Apple SD Gothic Neo","Malgun Gothic",system-ui,-apple-system,sans-serif;
  background:var(--bg);color:var(--ink);line-height:1.55;-webkit-tap-highlight-color:transparent;font-weight:700;word-break:keep-all;
  -webkit-font-smoothing:antialiased}
/* 위아래로 표식이 붙는 문자는 1.5 줄간격에서 잘린다 */
html:lang(my) body,html:lang(km) body,html:lang(th) body,html:lang(lo) body,
html:lang(hi) body,html:lang(ne) body,html:lang(bn) body,html:lang(si) body,html:lang(ta) body{line-height:1.78}
/* 한글 문항 본문은 어절 단위로 끊고 라틴 UI 자간 규칙을 물려받지 않게 */
.qbox h2,.passage,.choice,.vi-card b{word-break:keep-all;letter-spacing:0}
/* 미얀마·크메르 등 결합 문자 언어: 라틴용 자간·대문자·모노 조합이 글자를 부순다.
   ⚠️ 언어 목록은 i18n.js 의 웹폰트 NEED 목록과 같이 유지한다. ar·ur 은 일부러 뺐다(현재 정상) */
html:is(:lang(my),:lang(km),:lang(th),:lang(lo),:lang(hi),:lang(ne),:lang(bn),:lang(si),:lang(ta))
  :is(.cta,.check button,.nav .next,.play,.rt-l,.pm span,.tabbar a,.qbox .section,
      .unit-banner .u,.celebrate .stat,.pr-stat span,.lg-zone){
  letter-spacing:0;text-transform:none;font-family:inherit}
html{accent-color:var(--brand)}
::selection{background:var(--brand-xl);color:var(--brand-t)}
/* 브랜드 주황은 흰 배경에서 2.66:1 — 포커스 표시 최소 3:1 에 못 미친다. 잉크는 ~17:1 이고 다크에서 알아서 뒤집힌다 */
:focus-visible{outline:3px solid var(--ink-navy);outline-offset:2px;border-radius:6px}
:focus:not(:focus-visible){outline:none}
.wrap{max-width:520px;margin:0 auto;padding:16px;min-height:100dvh;display:flex;flex-direction:column}
a{color:inherit;text-decoration:none}
button{font:inherit;border:0;cursor:pointer;background:none}

/* ── 듀오식 3D 버튼 공통 ── */
.cta,.check button,.nav .next,.play{
  border-radius:16px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  transition:filter .1s;
}
.cta:active,.check button:active:not([disabled]),.nav .next:active{transform:translateY(4px);box-shadow:none !important}

/* ── 랜딩 = 탑승권 한 장 ── */
.top{display:flex;align-items:center;justify-content:space-between;padding:4px 0 12px}
.logo{display:flex;align-items:center;gap:7px;font-weight:900;font-size:1.35rem;letter-spacing:-.02em;color:var(--brand)}
.logo b{color:var(--ink)}
/* 브랜드 도장 — 실루엣을 마스크로 쓰고 잉크색은 토큰에서 온다.
   ⚠️ 원본 stamp-tiger.webp 는 원 안쪽이 #F6F6F6 흰 원판이라 다크에서 흰 동그라미로 떴다(사장님 지적).
   누끼 낸 실루엣(stamp-tiger-cut.webp)을 마스크로 쓰면 배경이 사라지고 색까지 테마를 따라간다.
   BRAND.md §6 "도장은 한 색 잉크로만" 규칙과도 맞는다. */
.logo-mark,.card .c-stamp{
  display:inline-block;flex:0 0 auto;background-color:var(--stamp);
  -webkit-mask:url("/img/brand/stamp-tiger-cut.webp") center/contain no-repeat;
          mask:url("/img/brand/stamp-tiger-cut.webp") center/contain no-repeat}
.logo-mark{width:30px;height:30px}
.logo-mark.lg{width:36px;height:36px}
.logo-t{display:inline-block} /* flex gap이 SET과 OPIK 사이에 끼어 로고가 갈라지던 버그 */
.langs{display:flex;flex-wrap:wrap;gap:6px;justify-content:center}
/* 언어 선택: 트리거 버튼 + 전체화면 시트(32개 언어) */
.lang-btn{display:inline-flex;align-items:center;gap:6px;padding:7px 12px;min-height:44px;border-radius:10px;font-size:.8rem;font-weight:800;color:var(--sub-d);border:2px solid var(--line);background:var(--card);max-width:46vw;white-space:nowrap;overflow:hidden}
.lang-btn.wide{display:flex;width:100%;justify-content:center;max-width:none;padding:13px;font-size:.95rem;color:var(--ink)}
.lang-btn:active{transform:translateY(1px)}
.lang-btn .gl{font-size:1rem}
.lang-sheet{position:fixed;inset:0;background:rgba(16,26,51,.6);z-index:70;display:flex;align-items:flex-end;justify-content:center;animation:popin .2s}
.ls-in{background:var(--card);width:100%;max-width:520px;max-height:82dvh;overflow:auto;border-radius:18px 18px 0 0;padding:20px 16px max(20px,env(safe-area-inset-bottom))}
.ls-in h2{font-size:1.15rem;font-weight:900;text-align:center;margin-bottom:14px}
.ls-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.ls-i{padding:13px 10px;border-radius:10px;border:2px solid var(--line);background:var(--card);font-weight:800;font-size:.9rem;color:var(--ink);text-align:center}
.ls-i.on{color:var(--brand-t);border-color:var(--brand);background:var(--brand-xl)}
.ls-i:active{transform:translateY(1px)}
.langs button{padding:6px 12px;min-height:44px;border-radius:10px;font-size:.78rem;font-weight:800;color:var(--sub-d);border:2px solid var(--line);background:var(--card)}
.langs button.on{color:var(--brand-t);border-color:var(--brand);background:var(--brand-xl)}

/* 탑승권 카드 — 티켓 스톡 위 잉크 인쇄. 절취선과 노치가 이 세계의 서명이다 */
.pass{position:relative;background:var(--paper);border:2.5px solid var(--ink-navy);border-radius:14px;overflow:hidden;
  box-shadow:0 10px 0 -4px rgba(16,26,51,.10)}
.pass-top{display:flex;align-items:center;justify-content:space-between;gap:10px;background:var(--brand);
  padding:9px 14px;border-bottom:2.5px solid var(--ink-navy)}
.pass-air,.pass-cls{font-family:var(--mono);font-size:.68rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:#2B1602}
.pass-body{padding:14px 16px 16px;text-align:center}
/* 항로 — FROM TODAY 에서 TO PASS 로. 목적지는 합격이지 취업이 아니다 */
.pass-route{display:grid;grid-template-columns:auto 1fr auto;align-items:end;gap:10px;color:var(--ink-navy)}
.rt{text-align:start}
.rt.end{text-align:end}
.rt-l{display:block;font-family:var(--mono);font-size:.6rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--sub-d)}
.rt-c{display:block;font-family:var(--mono);font-size:1.45rem;font-weight:700;letter-spacing:.06em;line-height:1.1;color:var(--ink-navy)}
.rt-arc{position:relative;color:var(--sub);padding-bottom:6px}
.rt-arc svg{display:block;width:100%;height:30px}
.rt-plane{position:absolute;left:50%;top:-2px;transform:translateX(-50%);font-size:.95rem;color:var(--brand)}
/* RTL은 오른쪽(출발)에서 왼쪽(도착)으로 흐른다 — 항로와 비행기를 함께 뒤집는다 */
[dir="rtl"] .rt-arc{transform:scaleX(-1)}
.pass-mascot{margin:6px 0 2px;line-height:0}
.pass-mascot .rg{margin:0 auto}
.pass h1{font-size:1.42rem;font-weight:900;line-height:1.32;margin-top:4px;color:var(--ink-navy)}
.pass p{color:var(--sub-d);margin-top:7px;font-size:.92rem;font-weight:700}
/* 티켓 필드 — 기계가 읽는 정보의 목소리 */
.pass-meta{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:14px;padding-top:12px;border-top:1.5px dashed var(--tear)}
.pm{text-align:center}
.pm span{display:block;font-family:var(--mono);font-size:.58rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--sub-d)}
.pm b{display:block;font-family:var(--mono);font-size:1.05rem;font-weight:700;color:var(--ink-navy);margin-top:2px}
/* 절취선 + 양쪽 반원 노치 */
.pass-tear{position:relative;height:0;border-top:2.5px dashed var(--tear);margin:0 12px}
.pass-tear:before,.pass-tear:after{content:"";position:absolute;top:-11px;width:20px;height:20px;border-radius:50%;
  background:var(--bg);border:2.5px solid var(--ink-navy)}
.pass-tear:before{left:-23px;clip-path:inset(0 0 0 50%)}
.pass-tear:after{right:-23px;clip-path:inset(0 50% 0 0)}
.pass-stub{padding:14px 16px 16px;display:grid;gap:9px}

.streak{display:inline-flex;align-items:center;gap:6px;margin:14px auto 0;padding:8px 14px;border-radius:999px;background:var(--brand-l);color:var(--brand-t);font-weight:800;font-size:.85rem}
.streak[hidden]{display:none} /* CSS display가 hidden 속성을 덮는 것 방지 — 신규 유저에게 빈 "0 day streak" 칩 노출 버그 */
/* 버튼 — 게이트 표시판: 각지고 단단하게, 눌리면 티켓이 눌리듯 */
.cta{display:block;width:100%;padding:15px;background:var(--brand);color:#2B1602;font-size:1rem;text-align:center;
  border:2.5px solid var(--on-brand);border-radius:10px;box-shadow:0 4px 0 var(--btn-sh)}
.cta.ghost{background:var(--card);color:var(--ink-navy);box-shadow:0 4px 0 var(--stub-sh)}
.cards{display:grid;gap:10px;margin-top:22px}
.card{background:var(--card);border:2px solid var(--line);border-radius:12px;padding:13px 15px;display:flex;gap:12px;align-items:center}
.card .ico{font-size:1.5rem}
.card .c-stamp{width:44px;height:44px;align-self:flex-start}
.card b{display:block;font-size:.95rem;font-weight:800;color:var(--ink-navy)}
.card span{color:var(--sub-d);font-size:.82rem;font-weight:700}
.dress-chip{display:block;margin-top:14px;text-align:center;padding:11px;border-radius:10px;border:2px dashed var(--brand);
  color:var(--brand-t);background:var(--brand-xl);font-weight:800;font-size:.86rem}
.foot{margin-top:auto;padding:20px 0 8px;text-align:center;color:var(--sub-d);font-size:.75rem}

/* ── 학습 경로 (듀오 스타일) ── */
.hud{display:flex;align-items:center;gap:16px;padding:6px 0 12px;font-weight:900;font-size:1rem}
.hud .flame{color:#ff9600}.hud .xp{color:var(--blue)}
/* 유닛 배너 = 게이트 안내판. 주황 위 흰 글씨는 2.7:1이라 잉크로 쓴다 */
.unit-banner{width:100%;background:var(--brand);color:var(--on-brand);border-radius:12px;padding:13px 16px;margin:14px 0 4px;border:2.5px solid var(--on-brand);box-shadow:0 4px 0 var(--btn-sh)}
.unit-banner .u{font-family:var(--mono);font-size:.64rem;font-weight:700;letter-spacing:.14em;opacity:.75;text-transform:uppercase}
.unit-banner .t{font-weight:900;font-size:1.05rem}
.path{display:flex;flex-direction:column;align-items:center;gap:22px;padding:12px 0 100px;position:relative}
.bubble{width:78px;height:78px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.9rem;background:var(--bub);border:0;box-shadow:0 7px 0 var(--bub-sh);position:relative;color:var(--bub-tx)}
/* 완료 = 여권에 찍힌 도장. 금색 원판이 아니라 잉크 자국이다 */
.bubble.done{background:var(--paper);box-shadow:0 7px 0 var(--bub-sh);color:var(--stamp);
  border:3px solid var(--stamp);font-family:var(--mono);font-weight:700;font-size:1.15rem;
  transform:rotate(-7deg)}
.bubble.done .cr{background:var(--stamp);color:var(--paper);border-color:var(--stamp)}
/* 도장은 제각각 비뚤게 찍힌다 — 같은 각도로 넷이 나란하면 스티커지 도장이 아니다 */
.yut .bubble.done.n0{transform:rotate(-8deg)}
.yut .bubble.done.n1{transform:rotate(5deg)}
.yut .bubble.done.n2{transform:rotate(-4deg)}
.yut .bubble.done.n3{transform:rotate(7deg)}
/* 보스 클리어 = 게이트 통과 도장 */
.yut .bubble.boss.done{background:var(--paper);border:3px solid var(--gate);color:var(--gate);
  box-shadow:0 7px 0 var(--bub-sh);transform:translate(-50%,-50%) rotate(-6deg)}
.bubble.now{background:var(--brand);box-shadow:0 7px 0 var(--brand-d);color:var(--on-brand);animation:bob 1.8s ease-in-out infinite}
.bubble:active{transform:translateY(5px);box-shadow:none}
.bubble:nth-of-type(4n+2){transform:translateX(-48px)}
.bubble:nth-of-type(4n+4){transform:translateX(48px)}
/* 현재 노드의 랑이 말 — 노드보다 위 레이어, 살짝 올려 세운다 */
.yut .piece{z-index:3;filter:drop-shadow(0 3px 3px rgba(16,26,51,.22))}
.path .side-mascot{position:absolute;font-size:2.6rem;right:8%;top:190px;animation:bob 3s ease-in-out infinite}
.tabbar{position:fixed;bottom:0;left:0;right:0;background:var(--card);border-top:2px solid var(--line);display:flex;justify-content:space-around;padding:8px 0 max(8px,env(safe-area-inset-bottom));z-index:10}
.tabbar a{display:flex;flex-direction:column;align-items:center;gap:1px;font-size:.62rem;font-weight:900;color:var(--sub-d);text-transform:uppercase;letter-spacing:.02em;padding:6px 8px;border-radius:12px;min-width:0}
/* 활성 탭 테두리도 상태 표시라 3:1 이 필요하다 — var(--brand) 2.66:1 → --brand-t 5.3:1 */
.tabbar a.on{color:var(--brand-t);background:var(--brand-xl);border:2px solid var(--brand-t)}
.tabbar .ti{font-size:1.4rem}

/* ── 레슨 플레이어 ── */
.lesson-top{display:flex;align-items:center;gap:12px;padding:6px 0 14px}
/* ⚠️ 터치 타깃 44px — 실측 17×33 이었다. 한 손으로 쓰는 앱에서 닫기가 안 눌린다.
   글자 크기는 그대로 두고 음수 마진으로 위치를 지키면서 히트 영역만 넓힌다. */
.lesson-top .x{font-size:1.35rem;color:var(--sub);font-weight:900;
  min-width:44px;min-height:44px;display:inline-flex;align-items:center;justify-content:center;
  margin:-8px -8px -8px -12px}
.lesson-top .progress{flex:1;margin:0}
.hearts{font-weight:900;color:var(--red);font-size:.95rem;white-space:nowrap}
/* 진행바 = 항로. 지나온 길은 잉크로 그어지고 비행기가 그 끝에 있다 */
.progress{position:relative;height:14px;border-radius:99px;background:var(--line);overflow:hidden;margin-bottom:14px}
.progress i{display:block;height:100%;width:100%;background:var(--brand);border-radius:99px;transform:translateX(-100%);transition:transform .3s;position:relative}
.progress i:after{content:"";position:absolute;top:3px;left:8px;right:8px;height:4px;border-radius:99px;background:rgba(255,255,255,.28)}
.qbox{background:var(--card);border:0;border-radius:16px;padding:6px 2px}
/* 문항 종류 = 티켓 필드 라벨. 본문 가독성은 건드리지 않고 프레임만 브랜드 문법으로 */
.qbox .section{font-family:var(--mono);font-size:.66rem;font-weight:700;color:var(--brand-t);letter-spacing:.14em;text-transform:uppercase}
.qbox h2{font-size:1.25rem;font-weight:900;margin-top:8px;line-height:1.4}
/* 지문 = 티켓에 붙은 안내문 — 왼쪽에 절취 구멍이 난 종이 */
.passage{position:relative;margin-top:14px;padding:14px 16px 14px 22px;border-radius:10px;border:2px solid var(--line);background:var(--paper);font-size:1rem;white-space:pre-wrap;font-weight:700}
.passage:before{content:"";position:absolute;inset-inline-start:0;top:0;bottom:0;width:5px;border-radius:8px 0 0 8px;background:repeating-linear-gradient(180deg,var(--bub-sh) 0 6px,transparent 6px 12px)}
.audio-zone{margin-top:14px;display:flex;align-items:center;gap:10px}
.play{padding:14px 20px;background:var(--brand);color:var(--on-brand);font-size:1.1rem;border:2.5px solid var(--on-brand);box-shadow:0 4px 0 var(--btn-sh);border-radius:10px}
.play[disabled]{opacity:.45;box-shadow:none}
.audio-note{color:var(--sub-d);font-size:.75rem;font-weight:700}
.choices{display:grid;gap:10px;margin-top:18px}
.choice{display:flex;gap:12px;align-items:center;text-align:start;padding:14px;border-radius:10px;border:2px solid var(--ink-navy);border-bottom-width:4px;background:var(--card);font-size:1rem;font-weight:700}
.choice:active{transform:translateY(2px);border-bottom-width:2px}
.choice .n{min-width:26px;height:26px;display:flex;align-items:center;justify-content:center;border:2px solid var(--ink-navy);border-radius:6px;font-family:var(--mono);font-size:.78rem;font-weight:700;color:var(--sub-d)}
.choice.sel{border-color:var(--ink-navy);background:var(--brand-l);color:var(--ink-navy)}
.choice.sel .n{border-color:var(--ink-navy);color:var(--ink-navy)}
.choice.right{border-color:var(--green-d);background:var(--green-l);color:var(--green-d)}
.choice.right .n{border-color:var(--green-d);color:var(--green-d)}
.choice.wrong{border-color:var(--red-d);background:var(--red-l);color:var(--red-d)}
.choice.wrong .n{border-color:var(--red-d);color:var(--red-d)}

/* 모의고사 상단 */
.exam-top{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:4px 0 10px;position:sticky;top:0;background:var(--bg);z-index:5}
.timer{font-family:var(--mono);font-variant-numeric:tabular-nums;font-weight:700;font-size:1.05rem;padding:6px 12px;border-radius:6px;background:var(--on-brand);color:var(--brand);border:2px solid var(--on-brand)}
.timer.low{background:var(--red);color:#fff;border-color:var(--red)}
.qno{font-family:var(--mono);color:var(--sub-d);font-weight:700;font-size:.8rem;letter-spacing:.08em}
.nav{display:flex;gap:10px;margin-top:16px}
.nav button{flex:1;padding:14px;border-radius:14px;font-weight:800;font-size:.95rem}
.nav .prev{background:var(--card);border:2px solid var(--line);border-bottom-width:4px;color:var(--sub-d)}
.nav .next{background:var(--brand);color:var(--on-brand);border:2.5px solid var(--on-brand);box-shadow:0 4px 0 var(--btn-sh)}
.nav .next[disabled]{opacity:.5;box-shadow:none}
/* 비활성 이전 버튼도 눌리는 느낌을 걷는다 — opacity 만 주면 4px 단차가 남아 눌릴 것처럼 보인다 */
.nav .prev[disabled]{opacity:.5;border-bottom-width:2px}

/* ── 피드백 시트(듀오식) ── */
/* 고정 CTA 바가 깔린 화면은 그만큼 아래를 비워 둔다.
   ⚠️ 새 단어 카드의 8개 절단을 걷어내자 목록이 길어지면서 마지막 카드가
      CONTINUE 바 뒤로 잘렸다 — 절단 버그를 고치니 드러난 두 번째 버그다. */
.wrap.cta-pad{padding-bottom:104px}
.check{position:fixed;left:0;right:0;bottom:0;padding:14px 16px max(14px,env(safe-area-inset-bottom));background:var(--card);border-top:2px solid var(--line);z-index:10}
.check .inner{max-width:520px;margin:0 auto}
.check button{width:100%;padding:15px;font-size:1rem;background:var(--brand);color:var(--on-brand);border:2.5px solid var(--on-brand);box-shadow:0 4px 0 var(--btn-sh)}
.check button[disabled]{background:var(--line);color:var(--sub-d);border-color:var(--sub-d);box-shadow:none}
/* 피드백 = 심사 결과. 통과는 게이트 그린 도장, 불통과는 인주 레드 도장 */
.check.ok{background:var(--green-l);border-top:3px solid var(--green)}
.check.ok button{background:var(--green);color:#fff;border-color:var(--green-d);box-shadow:0 4px 0 var(--green-d)}
.check.no{background:var(--red-l);border-top:3px solid var(--red)}
.check.no button{background:var(--red);color:#fff;border-color:var(--red-d);box-shadow:0 4px 0 var(--red-d)}
/* 승인·거부 도장이 피드백 시트 모서리에 찍힌다 */
.check.ok .fb:before,.check.no .fb:before{content:"";position:absolute;inset-inline-end:2px;top:-6px;
  width:52px;height:52px;background-size:contain;background-repeat:no-repeat;opacity:.9;pointer-events:none;
  animation:stampHit .5s cubic-bezier(.2,.9,.3,1) .1s both}
.check.ok .fb:before{background-image:url("/img/brand/stamp-check.webp")}
.check.no .fb:before{background-image:url("/img/brand/stamp-plane.webp");filter:grayscale(1) opacity(.35)}
@media (prefers-reduced-motion:reduce){.check.ok .fb:before,.check.no .fb:before{animation:none}}
.fb{position:relative;max-width:520px;margin:0 auto 12px;display:none}
.check.ok .fb,.check.no .fb{display:block}
.fb .h{font-weight:900;font-size:1.2rem}
.check.ok .h{color:var(--green-d)}.check.no .h{color:var(--red-d)}
.fb .e{font-size:.88rem;margin-top:4px;font-weight:700}
.check.ok .e{color:var(--green-d)}.check.no .e{color:var(--red-d)}
.fb .v{font-size:.78rem;margin-top:4px;opacity:.75;font-weight:700}
.check.ok .v{color:var(--green-d)}.check.no .v{color:var(--red-d)}

/* ── 결과·해설 ── */
.score{text-align:center;padding:30px 0 10px}
.score .big{font-size:3.2rem;font-weight:900}
.score .pass{color:var(--green)} .score .fail{color:var(--red)}
.score p{color:var(--sub-d);margin-top:6px;font-size:.9rem}
.review{margin-top:18px;display:grid;gap:10px}
.rev{background:var(--card);border:2px solid var(--line);border-radius:16px;padding:14px}
.rev .head{display:flex;justify-content:space-between;font-weight:800;font-size:.88rem;gap:8px}
.rev .ok{color:var(--green)} .rev .no{color:var(--red)}
.rev .exp{margin-top:8px;padding:12px;border-radius:12px;background:var(--well);font-size:.85rem;color:var(--ink)}
.rev .vocab{margin-top:6px;font-size:.8rem;color:var(--sub-d)}

/* ── 축하 화면 ── */
.celebrate{text-align:center;padding:50px 16px}
.celebrate .big{font-size:4.5rem;animation:bob 1.6s ease-in-out infinite}
.celebrate h1{font-size:1.5rem;font-weight:900;margin-top:10px;color:var(--gold-d)}
.celebrate .stats{display:flex;gap:10px;justify-content:center;margin-top:24px}
.celebrate .stat{background:var(--card);border:2px solid var(--line);border-radius:16px;padding:12px 22px;font-weight:800;font-size:.8rem;color:var(--sub-d);text-transform:uppercase;letter-spacing:.05em}
.celebrate .stat b{display:block;font-size:1.5rem;font-weight:900}
.celebrate .stat.xp{border-color:var(--gold)}.celebrate .stat.xp b{color:var(--gold-d)}
.celebrate .stat.fl{border-color:#ff9600}.celebrate .stat.fl b{color:#ff9600}

/* 전설로 넘은 게이트 — 금색 도장으로 남는다 */
.bubble.boss.legend{background:linear-gradient(160deg,#FFD98A,#E8A317);border-color:#A9720B;color:#4A3208}
.bubble.boss.legend .lk{display:none}

/* 월간 챌린지 — 일일 퀘스트 위에 한 장. 달 단위라 테두리를 잉크로 구분한다 */
.q-card.q-month{border-color:var(--ink);border-bottom-width:4px;background:var(--paper)}

/* 스피드 매치 — 5쌍이 한 화면에 들어와야 눈이 멈추지 않는다 */
.mt-grid.sp-grid{gap:8px;margin-top:10px}
.sp-grid .mt{min-height:50px;padding:10px 8px;font-size:.9rem}
.sp-grid .mt.ko{font-size:1rem}
/* 10초 남으면 시간 막대가 붉어진다 — 숫자보다 색이 먼저 보인다 */
.progress.hurry i{background:var(--red)}

/* ── 준비도 리포트 — 게이트 앞 심사 결과판 ── */
.rd-head{display:flex;align-items:center;gap:14px;margin:16px 0 18px;padding:16px;
  border-radius:14px;background:var(--paper);border:2.5px solid var(--ink-navy)}
.rd-total{font-family:var(--mono);font-weight:700;line-height:1;color:var(--ink-navy);white-space:nowrap}
.rd-total b{font-size:2.6rem}.rd-total i{font-style:normal;font-size:1rem;opacity:.6}
.rd-total-l{font-weight:900;font-size:1rem;color:var(--ink-navy)}
/* 커트라인이 고정이 아니라는 사실을 화면에 같이 둔다 — 정원제라 예측 자체가 불가능하다 */
.rd-note{margin-top:3px;font-size:.72rem;color:var(--sub-d);line-height:1.45;word-break:keep-all}

.rd-rings{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
.rd-ring{text-align:center}
.rd-arc{--v:0;width:100%;aspect-ratio:1;max-width:96px;margin:0 auto;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:conic-gradient(var(--brand) calc(var(--v)*1%),var(--line) 0)}
.rd-arc:after{content:"";position:absolute;width:0;height:0}
.rd-arc b{width:74%;height:74%;border-radius:50%;background:var(--card);
  display:flex;align-items:center;justify-content:center;
  font-family:var(--mono);font-size:1.15rem;font-weight:700;color:var(--ink)}
.rd-arc b i{font-style:normal;font-size:.62rem;opacity:.6;margin-inline-start:1px}
.rd-arc.none{background:var(--line)}
.rd-ring span{display:block;margin-top:7px;font-size:.78rem;font-weight:800;color:var(--ink)}
.rd-ring em{display:block;margin-top:1px;font-style:normal;font-size:.68rem;color:var(--sub-d);
  word-break:keep-all;line-height:1.35}

.rd-row{display:flex;align-items:center;gap:12px;background:var(--card);border:2px solid var(--line);
  border-radius:10px;padding:12px 14px;margin-bottom:8px}
.rd-row>div:first-child{flex:1;min-width:0}
.rd-row b{display:block;font-size:.92rem;font-weight:800;color:var(--ink);word-break:keep-all}
.rd-row span{font-size:.74rem;color:var(--sub-d)}
.rd-row input[type=date]{font:inherit;font-size:.82rem;font-weight:700;padding:8px 10px;
  border-radius:8px;border:2px solid var(--line);background:var(--bg);color:var(--ink)}
/* 약한 유형 막대 — 길수록 나쁘다. 그래서 브랜드색이 아니라 도장 빨강이다 */
.rd-bar{flex:0 0 74px;height:8px;border-radius:99px;background:var(--line);overflow:hidden}
.rd-bar i{display:block;height:100%;border-radius:99px;background:var(--red)}

/* ── 연습 허브 · 단어장 ── */
.pr-head{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin:14px 0 16px}
.pr-stat{background:var(--card);border:2px solid var(--line);border-radius:12px;padding:12px 8px;text-align:center}
.pr-stat b{display:block;font-family:var(--mono);font-size:1.35rem;font-weight:700;color:var(--ink)}
.pr-stat span{font-size:.66rem;font-weight:800;color:var(--sub-d);text-transform:uppercase;letter-spacing:.04em}
.pr-card{display:flex;align-items:center;gap:14px;background:var(--card);border:2px solid var(--line);
  border-bottom-width:4px;border-radius:12px;padding:14px;margin-bottom:10px}
.pr-card:active{transform:translateY(2px);border-bottom-width:2px}
.pr-card .ico{font-size:1.7rem}
.pr-card b{display:block;font-size:1rem;font-weight:800;color:var(--ink)}
.pr-card span{font-size:.82rem;color:var(--sub-d)}
/* 할 게 없는 카드는 지우지 않고 잠재운다 — 사라지면 왜 없는지 알 수 없다 */
.pr-card.off{opacity:.5;border-bottom-width:2px;pointer-events:none}

.wd-find{width:100%;padding:12px 14px;border-radius:10px;border:2px solid var(--line);
  background:var(--card);color:var(--ink);font:inherit;font-weight:700;margin-bottom:12px}
.wd-find:focus{outline:none;border-color:var(--brand)}
.wd-list{display:grid;gap:8px}
.wd{display:grid;grid-template-columns:1fr 76px 26px;align-items:center;gap:10px;text-align:start;
  background:var(--card);border:2px solid var(--line);border-radius:10px;padding:10px 12px}
.wd b{display:block;font-size:1rem;font-weight:800;color:var(--ink)}
.wd span{font-size:.78rem;color:var(--sub-d)}
.wd em{font-style:normal;font-size:1rem;opacity:.55}
.wd.heard em{opacity:1}
/* 숙련도 막대 — 시간이 지나면 저절로 줄어든다. 줄어드는 게 보여야 복습하러 온다 */
.wd-bar{height:8px;border-radius:99px;background:var(--line);overflow:hidden}
.wd-bar i{display:block;height:100%;border-radius:99px;background:var(--brand)}
.wd-none{padding:26px 10px;text-align:center;color:var(--sub-d);font-weight:700}

/* 일러스트가 안 뜨면 글자로 떨어진다 — 오프라인에서 그림 문항이 백지가 되던 자리 */
.illust-box{display:inline-flex;align-items:center;justify-content:center;position:relative}
.illust-alt{display:none}
.illust-box.failed>.illust{display:none}
.illust-box.failed>.illust-alt{display:flex;align-items:center;justify-content:center;text-align:center;
  width:100%;height:100%;padding:6px;font-size:.8rem;font-weight:800;color:var(--ink);
  border:2px dashed var(--line);border-radius:10px;word-break:keep-all;line-height:1.3;overflow:hidden}

/* ── 드릴(짝 맞추기·단어 배열) — 문제 유형 다양화 ── */
.mt-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:18px}
.mt-col{display:grid;gap:10px;align-content:start}
.mt{padding:14px 10px;border-radius:10px;border:2px solid var(--ink-navy);border-bottom-width:4px;
  background:var(--card);font-size:.95rem;font-weight:700;color:var(--ink);min-height:56px;
  display:flex;align-items:center;justify-content:center;text-align:center;word-break:keep-all;
  transition:border-color .12s ease,background .12s ease,opacity .18s ease}
.mt.ko{font-size:1.05rem}
.mt:active{transform:translateY(2px);border-bottom-width:2px}
.mt.on{border-color:var(--brand);background:var(--brand-xl);color:var(--brand-t)}
.mt.bad{border-color:var(--red);background:var(--red-l);color:var(--red-d);animation:shake .35s}
/* 맞춘 짝은 자리를 지킨 채 사라진다 — 무너지면 남은 짝을 다시 찾아야 한다 */
.mt.gone{opacity:.18;pointer-events:none;border-style:dashed;border-bottom-width:2px}

.wb-line{display:flex;flex-wrap:wrap;gap:8px;margin-top:18px;min-height:58px;padding:10px;
  border-radius:10px;border:2px dashed var(--line);align-content:flex-start}
.wb-bank{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}
.wb{padding:10px 14px;border-radius:9px;border:2px solid var(--ink-navy);border-bottom-width:4px;
  background:var(--card);font-size:1rem;font-weight:700;color:var(--ink)}
.wb:active{transform:translateY(2px);border-bottom-width:2px}
.wb.in{border-color:var(--brand);background:var(--brand-xl);color:var(--brand-t)}
/* 이미 집어간 조각은 자리만 남긴다 — 사라지면 줄이 출렁여서 누르던 위치를 잃는다 */
.wb.used{opacity:.2;pointer-events:none;border-style:dashed;border-bottom-width:2px}

/* ── 연결 끊김 — 흰 화면 대신. 항공사 결항 안내판의 문법을 빌린다 ── */
.offline-card{text-align:center;padding:56px 20px;max-width:340px;margin:0 auto}
.offline-card h2{font-size:1.05rem;font-weight:800;margin:14px 0 4px;color:var(--sub-d);
  text-wrap:balance;word-break:keep-all}
.offline-card .cta{margin-top:22px}

/* ── 디테일 2차: 콤보·흔들림·팝 ── */
@keyframes shake{0%,100%{transform:translateX(0)}25%{transform:translateX(-5px)}75%{transform:translateX(5px)}}
.hearts.hit{animation:shake .35s}
@keyframes popin{0%{scale:.4;opacity:0}70%{scale:1.15}100%{scale:1;opacity:1}}
/* 살짝 떠서 숨 쉬는 모션 — 현재 레슨 방울·상자·랑이 말·축하 화면이 이걸 쓴다.
   ⚠️ 5곳에서 부르는데 정의가 아예 없어서 전부 조용히 멈춰 있었다(실측).
   ⚠️ transform 이 아니라 translate 로 준다: .bubble 은 nth-of-type 로 translateX 를,
   .yut .nb 는 translate(-50%,-50%) 를, .bubble.done 은 rotate 를 이미 쓰고 있어
   transform 을 애니메이션하면 도장 각도와 중앙 정렬이 통째로 날아간다. */
@keyframes bob{0%,100%{translate:0 0}50%{translate:0 -7px}}
@media (prefers-reduced-motion:reduce){
  .bubble.now,.path .side-mascot,.celebrate .big,.bubble.chest.ready,.yut .piece{animation:none}
}
.fb .h{animation:popin .3s}
.combo{display:inline-block;margin-inline-start:8px;padding:3px 10px;border-radius:999px;background:var(--gold);color:#7a5800;font-size:.72rem;font-weight:900;letter-spacing:.05em;animation:popin .35s}
.bubble.lock .lk{position:absolute;font-size:.95rem;bottom:6px;right:6px}
/* 연속 이정표 — 듀오링고가 화면을 통째로 내주는 순간. 여기선 축하 화면 안의 도장 한 장 */
.celebrate .milestone{margin-top:18px;padding:16px 14px;border-radius:14px;
  background:var(--brand-xl);border:2px dashed var(--brand);animation:popin .4s}
.celebrate .milestone b{display:block;font-family:var(--mono);font-size:2.1rem;font-weight:700;color:var(--brand-t);line-height:1}
.celebrate .milestone span{display:block;margin-top:4px;font-size:.95rem;font-weight:900;color:var(--ink)}
.celebrate .milestone em{display:block;margin-top:2px;font-style:normal;font-size:.8rem;color:var(--sub-d);word-break:keep-all}

/* 끊긴 연속 기록 — 48시간 안에는 되살 수 있다. 학습맵 맨 위에 한 번만 뜬다 */
.rp-banner{display:flex;align-items:center;gap:12px;background:var(--card);border:2px solid var(--red);
  border-bottom-width:4px;border-radius:12px;padding:12px 14px;margin-bottom:14px}
.rp-banner .ico{font-size:1.35rem;filter:grayscale(1);opacity:.75}
.rp-banner div{flex:1;min-width:0}
.rp-banner b{display:block;font-size:.9rem;font-weight:800;color:var(--ink);word-break:keep-all}
.rp-banner span{font-size:.78rem;color:var(--sub-d)}
.rp-banner button{padding:9px 12px;border-radius:9px;background:var(--brand);color:var(--ink);
  font-weight:900;font-size:.8rem;border:2px solid var(--ink);border-bottom-width:4px;white-space:nowrap}
.rp-banner button:disabled{opacity:.45}

/* ── 가이드(SEO 정적 페이지) ── */
.g-crumb{font-size:.8rem;font-weight:800;margin:2px 0 10px}
.g-crumb a{color:var(--brand)}
.g-art h1{font-size:1.5rem;font-weight:900;line-height:1.35}
.g-art .upd{color:var(--sub-d);font-size:.75rem;margin:4px 0 14px}
.g-art h2{font-size:1.15rem;font-weight:900;margin:22px 0 8px;color:var(--brand-t)}
.g-art p{margin:10px 0;font-weight:700}
.g-art ul{margin:10px 0 10px 20px;font-weight:700}
.g-art li{margin:6px 0}
.g-box{margin-top:22px;border:2px solid var(--line);border-radius:16px;padding:14px 16px}
.g-box b{display:block;margin-bottom:6px;font-size:.78rem;text-transform:uppercase;letter-spacing:.06em;color:var(--sub-d)}
.g-box a{display:block;padding:7px 0;color:var(--brand);font-weight:800;word-break:break-all;font-size:.9rem}
.g-list a{display:block;padding:14px 16px;border:2px solid var(--line);border-bottom-width:4px;border-radius:14px;margin-top:10px;font-weight:800}
.g-list a:active{transform:translateY(2px);border-bottom-width:2px}
.langs a.lg{padding:6px 10px;border-radius:12px;font-size:.78rem;font-weight:800;color:var(--sub-d);border:2px solid var(--line);border-bottom-width:4px;background:var(--card)}
.langs a.lg.on{color:var(--brand);border-color:var(--brand);background:var(--brand-xl)}

/* ── HUD(하트·젬·스트릭·부스트 공용 헤더) ── */
.hud{display:flex;align-items:center;gap:10px;font-weight:900;font-size:.92rem}
.hud-i{white-space:nowrap}
.hud-i.fl{color:#ff9600}.hud-i.gm{color:var(--blue)}.hud-i.ht{color:var(--red)}.hud-i.bo{color:var(--gold-d)}

/* ── 경로 디테일: 유닛 색 순환·크라운·보물상자·연습 FAB ── */
/* 유닛 배너 = 게이트 안내판. 듀오링고식 색 로테이션(초록·파랑·주황)을 버렸다 —
   공항 안내판은 유닛마다 색이 바뀌지 않는다. 대신 게이트 번호와 구간이 정보를 나른다 */
.unit-banner{display:flex;align-items:center;gap:12px}
.unit-banner .g{flex:0 0 auto;font-family:var(--mono);font-size:1.5rem;font-weight:700;line-height:1;
  padding:6px 9px;border-radius:6px;background:var(--on-brand);color:var(--brand);letter-spacing:.02em}
.unit-banner .t{font-weight:900;font-size:1.02rem}
/* 접힌(잠긴) 유닛 — 배너만. 누르면 보드를 그려 펼친다 */
.unit-banner.folded{background:var(--well);color:var(--sub-d);border-color:var(--line);box-shadow:none;cursor:pointer;position:relative}
.unit-banner.folded .g{background:var(--bub);color:var(--sub-d)}
.unit-banner.folded .lk{position:absolute;inset-inline-end:14px;top:50%;transform:translateY(-50%);font-size:.95rem;opacity:.7}
.unit-banner.folded:active{transform:translateY(1px)}
.bubble .cr{position:absolute;bottom:4px;right:4px;min-width:22px;height:22px;border-radius:50%;background:var(--card);color:var(--gold-d);border:2px solid var(--gold);font-size:.7rem;font-weight:900;display:flex;align-items:center;justify-content:center}
.bubble.chest{background:none;box-shadow:none;font-size:2.5rem;filter:drop-shadow(0 5px 0 rgba(0,0,0,.12))}
.bubble.chest.lock{opacity:.4;filter:grayscale(1)}
.bubble.chest.ready{animation:bob 1.2s ease-in-out infinite;cursor:pointer}
.bubble.chest.opened{opacity:.75}
/* ⚠️ [hidden] 이 display:flex 에 먹힌다 — .streak · .fab-n 에 이어 세 번째다 */
.fab[hidden]{display:none}
.fab{position:fixed;right:14px;bottom:86px;width:58px;height:58px;border-radius:50%;background:var(--card);border:2px solid var(--line);border-bottom-width:5px;display:flex;align-items:center;justify-content:center;font-size:1.6rem;z-index:9}
.fab .fab-n{position:absolute;top:-4px;right:-4px;min-width:22px;height:22px;border-radius:50%;background:var(--red);color:#fff;font-size:.7rem;font-weight:900;display:flex;align-items:center;justify-content:center}
.fab-n[hidden]{display:none} /* display:flex 가 hidden 속성을 덮는다 — .streak 때와 같은 함정 */

/* ── 리그 ── */
.shields{display:flex;gap:4px;padding:8px 0 4px;flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none}
.shields::-webkit-scrollbar{display:none}
.shield{flex:0 0 auto}
.shields > :first-child{margin-left:auto}
.shields > :last-child{margin-right:auto}
.shield{width:34px;height:38px;display:flex;align-items:center;justify-content:center;font-size:1.25rem;border-radius:10px;opacity:.55}
.shield.on{opacity:1;background:var(--brand-xl);border:2px solid var(--brand);transform:scale(1.15)}
.shield.lk{opacity:.3;font-size:.95rem}
.lg-head{text-align:center;padding:6px 0 12px}
.lg-head h1{font-size:1.3rem;font-weight:900}
.lg-sub{color:var(--sub-d);font-size:.8rem;font-weight:800;margin-top:4px}
.lg-list{display:grid;gap:6px}
.lg-row{display:flex;align-items:center;gap:12px;background:var(--card);border:2px solid var(--line);border-radius:14px;padding:10px 14px;font-weight:800}
.lg-row.me{border-color:var(--brand);background:var(--brand-xl);color:var(--brand-t)}
.lg-row .rk{min-width:30px;text-align:center;font-weight:900}
.lg-row .av{width:34px;height:34px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-weight:900;color:#3c3c3c}
.lg-row .nm{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lg-row .pt{color:var(--sub-d);font-size:.85rem}
.lg-row.me .pt{color:var(--brand-t)}
.lg-zone{text-align:center;font-size:.7rem;font-weight:900;letter-spacing:.08em;padding:4px 0}
.lg-zone.up{color:var(--green-d)}
.lg-zone.dn{color:var(--red-d)}
.lg-rule{text-align:center;color:var(--sub-d);font-size:.72rem;font-weight:700;padding:14px 0 4px}

/* ── 퀘스트 ── */
.q-hero{display:flex;align-items:center;gap:14px;padding:10px 0 16px}
.q-hero h1{font-size:1.3rem;font-weight:900}
.q-card{display:flex;align-items:center;gap:12px;background:var(--card);border:2px solid var(--line);border-radius:16px;padding:14px;margin-bottom:10px}
.q-ico{font-size:1.7rem}
.q-body{flex:1;min-width:0}
.q-body b{font-size:.92rem;font-weight:800}
.q-bar{position:relative;height:16px;margin:8px 0 0}
.q-bar span{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:.66rem;font-weight:900;color:var(--ink)}
.q-bar i{background:var(--gold)}
.q-claim{padding:10px 12px;min-height:44px;border-radius:12px;background:var(--green);color:#fff;font-weight:900;font-size:.78rem;box-shadow:0 3px 0 var(--green-d);white-space:nowrap;animation:popin .3s}
.q-claim:active{transform:translateY(3px);box-shadow:none}
.q-done{color:var(--green-d);font-weight:900;font-size:.8rem;white-space:nowrap}
.q-gem{opacity:.4;font-size:1.2rem}

/* ── 프로필: 캘린더·배지·상점·설정 ── */
.p-hero{display:flex;align-items:center;gap:12px;padding:6px 0 14px}
.cal{flex:1;display:grid;grid-template-columns:repeat(7,1fr);gap:4px}
.cd{text-align:center;border:2px solid var(--line);border-radius:10px;padding:6px 0;background:var(--card)}
.cd.on{border-color:#ff9600;background:#fff5d9}
:root[data-theme="dark"] .cd.on{background:#3a2c10}
.cd i{font-style:normal;font-size:.95rem;display:block}
.cd b{font-size:.62rem;color:var(--sub-d)}
.cards.grid2{grid-template-columns:1fr 1fr}
.p-h{margin:24px 0 8px;font-size:1rem;font-weight:900}
.badges{display:grid;grid-template-columns:repeat(5,1fr);gap:8px}
/* 업적 = 여권에 모은 도장. 딴 것은 잉크로, 못 딴 것은 빈 도장란으로 */
.badge{text-align:center;background:var(--paper);border:2.5px solid var(--stamp);border-radius:10px;padding:10px 2px}
.badge:nth-child(3n+1){transform:rotate(-3deg)}
.badge:nth-child(3n){transform:rotate(2.5deg)}
.badge.lk{border-color:var(--bub);border-style:dashed;background:transparent;opacity:.6;filter:grayscale(.7);transform:none}
.badge span{font-size:1.5rem;display:block}
.badge b{font-size:.56rem;font-weight:800;color:var(--sub-d);display:block;margin-top:3px;line-height:1.25}
.shop-row{display:flex;align-items:center;gap:12px;background:var(--card);border:2px solid var(--line);border-radius:16px;padding:14px;margin-bottom:10px}
.shop-row .ico{font-size:1.7rem}
.shop-row>div{flex:1;min-width:0}
.shop-row b{display:block;font-size:.92rem;font-weight:800}
.shop-row span{color:var(--sub-d);font-size:.78rem;font-weight:700}
.shop-row em{display:block;font-style:normal;color:var(--brand);font-size:.72rem;font-weight:900;margin-top:2px}
.buy{padding:10px 14px;min-height:44px;border-radius:12px;background:var(--blue);color:#fff;font-weight:900;font-size:.82rem;box-shadow:0 3px 0 var(--blue-d);white-space:nowrap}
.buy:active:not([disabled]){transform:translateY(3px);box-shadow:none}
.buy[disabled]{opacity:.4;box-shadow:none}
.set-row{display:flex;align-items:center;justify-content:space-between;padding:14px 2px;border-bottom:2px solid var(--line);font-weight:800;font-size:.92rem}
.tgl{width:52px;height:30px;border-radius:99px;background:var(--line);position:relative;transition:background .15s}
/* 스위치는 보이는 크기를 키우면 조잡해진다 — 히트 영역만 44px 로 넓힌다 */
.tgl::after{content:"";position:absolute;inset:-7px -0px;border-radius:99px}
.tgl:after{content:"";position:absolute;top:3px;left:3px;width:24px;height:24px;border-radius:50%;background:var(--card);transition:left .15s;box-shadow:0 1px 3px rgba(0,0,0,.25)}
.tgl.on{background:var(--green)}
.tgl.on:after{left:25px}

/* ── 옷장(가챠) ── */
.rc{color:#8a9aa6}.rr{color:var(--blue)}.re{color:#a855f7}.rl{color:var(--gold-d)}
.wd-stage{text-align:center;padding:8px 0 4px;filter:drop-shadow(0 6px 0 rgba(0,0,0,.07))}
.wd-gacha{text-align:center}
.wd-rates{margin-top:8px;color:var(--sub-d);font-size:.72rem;font-weight:800}
.wd-rates i{font-style:normal;font-weight:900}
.wd-hint{color:var(--sub-d);font-size:.75rem;font-weight:700;margin:-2px 0 10px}
.wd-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
.wd-cell{position:relative;background:var(--card);border:2px solid var(--line);border-bottom-width:4px;border-radius:16px;padding:10px 4px;text-align:center}
.wd-cell:active:not(.lk){transform:translateY(2px);border-bottom-width:2px}
.wd-cell.rc{border-color:#c3ced6}.wd-cell.rr{border-color:var(--blue)}.wd-cell.re{border-color:#a855f7}.wd-cell.rl{border-color:var(--gold)}
.wd-cell.lk{opacity:.5;filter:grayscale(.7);border-color:var(--line)}
.wd-cell.eq{background:var(--brand-xl)}
.wd-cell b{display:block;font-size:.66rem;font-weight:900;margin-top:2px}
.wd-cell i{display:block;font-style:normal;font-size:.58rem;font-weight:800;color:var(--sub-d);text-transform:uppercase;letter-spacing:.04em}
.wd-cell .q{display:flex;align-items:center;justify-content:center;height:58px;font-size:1.8rem;font-weight:900;color:var(--sub)}
.wd-cell .chk{position:absolute;top:-7px;right:-7px;width:24px;height:24px;border-radius:50%;background:var(--green);color:#fff;font-weight:900;font-size:.8rem;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 0 var(--green-d)}
.wd-reveal{position:fixed;inset:0;background:rgba(10,14,26,.82);z-index:70;display:flex;align-items:center;justify-content:center;flex-direction:column}
.wd-reveal[hidden]{display:none}
.wd-box{font-size:5.5rem;filter:drop-shadow(0 8px 0 rgba(0,0,0,.3))}
.wd-box.shakeit{animation:wdShake .9s ease-in-out}
@keyframes wdShake{0%,100%{transform:rotate(0) scale(1)}20%{transform:rotate(-10deg) scale(1.05)}40%{transform:rotate(10deg) scale(1.1)}60%{transform:rotate(-8deg) scale(1.18)}80%{transform:rotate(8deg) scale(1.28)}}
.wd-result{text-align:center;color:#fff;animation:popin .35s;padding:0 20px;width:100%;max-width:420px}
.wd-item{display:inline-flex;align-items:center;justify-content:center;width:150px;height:150px;border-radius:24px;background:#fff;border:4px solid var(--line)}
.wd-item.rr{border-color:var(--blue)}.wd-item.re{border-color:#a855f7;box-shadow:0 0 30px rgba(168,85,247,.6)}.wd-item.rl{border-color:var(--gold);box-shadow:0 0 40px rgba(255,200,0,.7)}
.wd-rar{margin-top:12px;font-weight:900;letter-spacing:.1em;text-transform:uppercase;font-size:.8rem}
.wd-result h2{font-size:1.4rem;font-weight:900;margin-top:4px}
.wd-new{margin-top:6px;font-weight:900;color:var(--gold)}
/* 랑이 코스메틱 모션 */
.rg .rg-tw{animation:rgTw 1.7s ease-in-out infinite}
@keyframes rgTw{0%,100%{opacity:.35}50%{opacity:1}}
.wd-edit{position:absolute;bottom:-2px;right:-6px;background:var(--card);border:2px solid var(--line);border-radius:50%;width:30px;height:30px;display:flex;align-items:center;justify-content:center;font-size:.9rem}

/* ── 온보딩 ── */
.ob-stage{text-align:center;padding:14px 0 16px}
.ob-stage h1{font-size:1.35rem;font-weight:900;margin-top:10px;line-height:1.35}
.ob-grid{display:grid;gap:10px}
.ob-grid.cols2{grid-template-columns:1fr 1fr;gap:8px}
.ob-grid.cols2 .ob-card{padding:12px 10px;gap:8px;font-size:.86rem}
/* 모의고사 세트 = 탑승권 스텁 한 장씩 */
.ob-grid.cols2 .ob-card{position:relative;background:var(--paper);border-color:var(--bub-sh)}
/* impeccable-disable-next-line side-tab -- 티켓 스텁의 절취 구멍(브랜드 문법). 장식 사이드탭이 아니다 */
.ob-grid.cols2 .ob-card:before{content:"";position:absolute;left:0;top:0;bottom:0;width:5px;
  background:repeating-linear-gradient(180deg,var(--brand) 0 7px,transparent 7px 13px);border-radius:10px 0 0 10px}
.ob-grid.cols2 .ob-card .ico{font-size:1.1rem}
.ob-card{display:flex;align-items:center;gap:14px;text-align:start;padding:16px;border-radius:16px;border:2px solid var(--line);border-bottom-width:4px;background:var(--card);font-weight:800;font-size:1rem}
.ob-card:active{transform:translateY(2px);border-bottom-width:2px}
.ob-card .ico{font-size:1.5rem}
.ob-quiz .section{font-size:.72rem;font-weight:900;color:var(--brand);letter-spacing:.08em;text-transform:uppercase}
.ob-quiz h2{font-size:1.2rem;font-weight:900;margin-top:8px;line-height:1.4}

/* ── 레슨 도입 새 단어 ── */
.vi-head{text-align:center;padding:6px 0 10px}
.vi-head h2{font-size:1.3rem;font-weight:900;margin-top:6px}
.vi-list{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.vi-card{position:relative;text-align:start;padding:14px;border-radius:14px;border:2px solid var(--ink-navy);border-bottom-width:4px;background:var(--card)}
.vi-card:active{transform:translateY(2px);border-bottom-width:2px}
.vi-card b{display:block;font-size:1.05rem;font-weight:900;color:var(--brand-t)}
.vi-card span{display:block;color:var(--sub-d);font-size:.8rem;font-weight:700;margin-top:2px}
.vi-card i{position:absolute;top:10px;right:10px;font-style:normal;font-size:.9rem;opacity:.6}
/* 예문 — 목록만 훑는 것과 문장 안에서 만나는 것은 다르다.
   RTL 에서도 한국어 예문은 좌→우다(dir=ltr 을 마크업이 아니라 여기서 고정) */
.vi-card em{display:block;margin-top:7px;padding-top:7px;border-top:1.5px dashed var(--line);
  font-style:normal;font-size:.86rem;font-weight:700;color:var(--ink);line-height:1.5;
  word-break:keep-all;direction:ltr;text-align:start}
.vi-card em u{text-decoration:none;color:var(--brand-t);font-weight:900;
  box-shadow:inset 0 -6px 0 var(--brand-xl)}
.vi-card.heard{border-color:var(--brand);background:var(--brand-xl)}
.vi-card.heard i{opacity:1}

/* ── 토스트·색종이·부스트 태그·하트 소진 ── */
.toast{position:fixed;left:50%;bottom:110px;transform:translateX(-50%);background:var(--ink);color:var(--bg);padding:12px 18px;border-radius:14px;font-weight:800;font-size:.85rem;z-index:60;animation:popin .3s;max-width:88vw;text-align:center}
.toast.out{opacity:0;transition:opacity .4s}
.cfp{position:fixed;top:-12px;z-index:55;pointer-events:none;animation:cfall linear forwards}
@keyframes cfall{0%{transform:translateY(0) rotate(0)}100%{transform:translateY(105vh) rotate(560deg)}}
.boost-tag{display:inline-block;margin-top:10px;padding:6px 14px;border-radius:999px;background:#fff5d9;color:#cd7900;font-weight:900;font-size:.8rem;animation:popin .35s}
:root[data-theme="dark"] .boost-tag{background:#3a2c10}
.boost-tag.hb{background:var(--red-l);color:var(--red-d)}
.ho-sub{color:var(--sub-d);font-size:.9rem;margin-top:8px}
.ho-eta{margin:14px 0 4px;font-weight:900;color:var(--red);font-variant-numeric:tabular-nums}

/* ── 마스코트 "랑이(Rangi)" v9 — 전부 라스터. 모든 컷이 같은 규격으로 정렬돼 있어
   박스에 꽉 채우면 끝(스케일 보정 없음).
   모션 원칙: 발은 바닥에 붙어 있고(transform-origin 50% 94%) 몸만 늘고 준다.
   키프레임을 촘촘히(6~8개) 두고 이징을 구간마다 다르게 줘서 기계적 왕복을 지운다. ── */
.mascot .rg{vertical-align:bottom}
.rg{position:relative;display:inline-block;line-height:0}
/* impeccable-disable-next-line bounce-easing -- 캐릭터 등장·타격은 스쿼시&스트레치가 원칙(애니메이션 12원칙).
   UI 트랜지션이 아니라 캐릭터 물리라서 오버슈트가 맞다. 버튼·시트·페이지 전환에는 쓰지 않는다. */
.rg>img{width:100%;height:100%;object-fit:contain;transform-origin:50% 94%;will-change:transform;
  animation:rgxIn .42s cubic-bezier(.34,1.56,.64,1) both;-webkit-user-select:none;user-select:none}
.cos-ico{object-fit:contain;-webkit-user-select:none;user-select:none}
@keyframes rgxIn{0%{transform:scale(.72) translateY(8%);opacity:0}55%{transform:scale(1.06) translateY(-2%);opacity:1}78%{transform:scale(.985) translateY(0)}100%{transform:scale(1) translateY(0);opacity:1}}

/* idle — 들숨은 느리게, 날숨은 조금 빠르게. 꼬리 흔들림 대신 무게중심 미세 이동 */
.rg-idle>img{animation:rgxIn .42s cubic-bezier(.34,1.56,.64,1) both,rgxBreathe 4.2s .42s cubic-bezier(.45,.05,.55,.95) infinite}
@keyframes rgxBreathe{
  0%{transform:scale(1,1) translateX(0)}
  28%{transform:scale(.994,1.016) translateX(-.4%)}
  46%{transform:scale(1.008,.99) translateX(0)}
  70%{transform:scale(.997,1.008) translateX(.4%)}
  100%{transform:scale(1,1) translateX(0)}}

/* happy — 예비동작(살짝 눌림) → 도약 → 착지 흡수. 애니메이션 12원칙의 squash&stretch */
.rg-happy>img{animation:rgxIn .42s cubic-bezier(.34,1.56,.64,1) both,rgxPerk 1.9s .42s cubic-bezier(.33,.66,.4,1) infinite}
@keyframes rgxPerk{
  0%,74%,100%{transform:translateY(0) scale(1,1)}
  10%{transform:translateY(1%) scale(1.05,.94)}
  22%{transform:translateY(-8%) scale(.95,1.07)}
  34%{transform:translateY(-4%) scale(1,1)}
  46%{transform:translateY(0) scale(1.04,.95)}
  58%{transform:translateY(-1%) scale(.99,1.02)}}

/* cheer — 더 크게 뛰고 공중에서 살짝 뜬 채 머문다(행업) */
.rg-cheer>img{animation:rgxIn .42s cubic-bezier(.34,1.56,.64,1) both,rgxCheer 1.25s .42s cubic-bezier(.36,.62,.42,1) infinite}
@keyframes rgxCheer{
  0%,86%,100%{transform:translateY(0) scale(1,1) rotate(0)}
  9%{transform:translateY(2%) scale(1.07,.9)}
  26%{transform:translateY(-13%) scale(.93,1.09) rotate(-1.5deg)}
  40%{transform:translateY(-15%) scale(.97,1.03) rotate(1.5deg)}
  56%{transform:translateY(-2%) scale(1.02,.98)}
  70%{transform:translateY(0) scale(1.05,.95)}
  78%{transform:translateY(-2%) scale(.99,1.01)}}

/* party — 좌우 스텝 + 반박자 어긋난 상하 반동 = 춤처럼 */
.rg-party>img{animation:rgxIn .42s cubic-bezier(.34,1.56,.64,1) both,rgxDance 1.5s .42s cubic-bezier(.45,.05,.55,.95) infinite}
@keyframes rgxDance{
  0%,100%{transform:translate(0,0) rotate(-3.5deg) scale(1,1)}
  12%{transform:translate(-2%,-5%) rotate(-2deg) scale(.98,1.03)}
  25%{transform:translate(-3%,0) rotate(0) scale(1.04,.96)}
  37%{transform:translate(-1%,-6%) rotate(2deg) scale(.97,1.04)}
  50%{transform:translate(0,0) rotate(3.5deg) scale(1.03,.97)}
  62%{transform:translate(2%,-5%) rotate(2deg) scale(.98,1.03)}
  75%{transform:translate(3%,0) rotate(0) scale(1.04,.96)}
  88%{transform:translate(1%,-6%) rotate(-2deg) scale(.97,1.04)}}

/* sad — 축 늘어진 좌우 흔들림. 아주 느리고 진폭이 서서히 줄었다 는다 */
.rg-sad>img{animation:rgxIn .42s cubic-bezier(.34,1.56,.64,1) both,rgxSway 5.4s .42s cubic-bezier(.42,0,.58,1) infinite}
@keyframes rgxSway{
  0%,100%{transform:rotate(-1.8deg) translateY(0)}
  30%{transform:rotate(.6deg) translateY(.6%)}
  50%{transform:rotate(1.8deg) translateY(0)}
  80%{transform:rotate(-.6deg) translateY(.6%)}}

/* cry — 흐느낌: 짧게 들썩이고 잠깐 멈춘다(들썩임 사이의 정적이 울음을 만든다) */
.rg-cry>img{animation:rgxIn .42s cubic-bezier(.34,1.56,.64,1) both,rgxSob 1.35s .42s ease-in-out infinite}
@keyframes rgxSob{
  0%,52%,100%{transform:translateX(0) scale(1,1)}
  8%{transform:translateX(-1%) scale(1.02,.97)}
  16%{transform:translateX(1%) scale(1.01,.985)}
  24%{transform:translateX(-.8%) scale(1.02,.97)}
  32%{transform:translateX(.6%) scale(1,1)}
  40%{transform:translateX(0) scale(1.01,.99)}}

/* confused — 고개 갸웃: 기울이고 멈춰서 생각하다가 반대로. 정지 구간이 길다 */
.rg-confused>img{animation:rgxIn .42s cubic-bezier(.34,1.56,.64,1) both,rgxPonder 4.6s .42s cubic-bezier(.65,0,.35,1) infinite}
@keyframes rgxPonder{
  0%,10%{transform:rotate(0) translateY(0)}
  22%,44%{transform:rotate(3deg) translateY(-1%)}
  56%,78%{transform:rotate(-2deg) translateY(0)}
  90%,100%{transform:rotate(0) translateY(0)}}

/* wow — 놀람: 훅 커졌다가 두 번 여운. 여운 진폭은 절반씩 줄인다 */
.rg-wow>img{animation:rgxIn .42s cubic-bezier(.34,1.56,.64,1) both,rgxAwe 2.6s .42s cubic-bezier(.22,1,.36,1) infinite}
@keyframes rgxAwe{
  0%,62%,100%{transform:scale(1,1) translateY(0)}
  8%{transform:scale(1.07,1.04) translateY(-2%)}
  20%{transform:scale(.99,1) translateY(0)}
  32%{transform:scale(1.035,1.02) translateY(-1%)}
  44%{transform:scale(.997,1) translateY(0)}
  52%{transform:scale(1.015,1.01) translateY(-.4%)}}

/* 전투 포즈 — attack·hurt는 순간을 잡은 그림이라 정지가 맞고, guard·win은 살아 있어야 한다 */
.rg-attack>img,.rg-hurt>img{animation:rgxIn .42s cubic-bezier(.34,1.56,.64,1) both}
.rg-guard>img{animation:rgxIn .42s cubic-bezier(.34,1.56,.64,1) both,rgxGuard 2.2s .42s cubic-bezier(.45,.05,.55,.95) infinite}
@keyframes rgxGuard{0%,100%{transform:scale(1,1) translateY(0)}30%{transform:scale(.995,1.012) translateY(-.5%)}62%{transform:scale(1.008,.992) translateY(0)}}
.rg-win>img{animation:rgxIn .42s cubic-bezier(.34,1.56,.64,1) both,rgxWin 1.7s .42s cubic-bezier(.33,.66,.4,1) infinite}
@keyframes rgxWin{0%,72%,100%{transform:translateY(0) rotate(0) scale(1,1)}14%{transform:translateY(-6%) rotate(-2deg) scale(.97,1.05)}30%{transform:translateY(0) rotate(0) scale(1.04,.96)}46%{transform:translateY(-4%) rotate(2deg) scale(.98,1.03)}60%{transform:translateY(0) rotate(0) scale(1.02,.98)}}

@media (prefers-reduced-motion:reduce){.rg>img{animation:none!important}}
.fb{position:relative}
.fb-mascot{position:absolute;inset-inline-start:4px;top:-64px;pointer-events:none} /* 도장이 오른쪽 모서리를 쓰므로 랑이는 반대편 */
@keyframes hop{0%,100%{transform:translateY(0)}26%{transform:translateY(-13px) scaleY(1.04)}52%{transform:translateY(0) scaleY(.97)}72%{transform:translateY(-6px) scaleY(1.02)}88%{transform:translateY(0) scaleY(1)}}
.check.ok .fb-mascot .rg{animation:hop .9s .1s cubic-bezier(.33,.66,.4,1)}

/* 그림 문항 일러스트 — 다크모드에서도 흰 타일 유지(색 고정, .wd-item 패턴) */
.q-illust{display:flex;justify-content:center;margin-top:14px}
/* 일러스트 타일은 다크모드에서도 흰 배경 고정 — 라스터가 흰 배경 전제로 그려져 있다 */
.q-illust .illust{background:#fff;border:2px solid var(--line);border-radius:18px;padding:10px;object-fit:contain}
.choices.picgrid{grid-template-columns:1fr 1fr}
.choice.pic{flex-direction:column;gap:6px;padding:10px 8px 12px;align-items:center}
.choice.pic .illust{background:#fff;border-radius:12px;object-fit:contain}
.choice.pic .n{align-self:flex-start;margin-bottom:-4px}

/* 랜딩 꾸미기 후킹 칩 */
.mascot{display:inline-block}

/* 데스크톱: 탭바를 콘텐츠 폭(520px)에 정렬, FAB를 콘텐츠 우측 모서리에 고정 */
@media(min-width:700px){
  .tabbar{justify-content:center;gap:64px}
  .fab{right:calc(50% - 274px)}
}

/* ── 윷놀이 말판 학습맵: 유닛 = 모서리 4레슨 + 중앙 보스, 대각 말길 ── */
.yut{position:relative;width:100%;max-width:340px;height:320px;margin:8px auto 6px}
.yut .board{position:absolute;inset:0;width:100%;height:100%}
.yut .bubble{position:absolute;width:66px;height:66px;font-size:1.6rem;margin:0;transform:none}
.yut .bubble:active{transform:translateY(4px)}
.yut .bubble .lk{position:absolute;bottom:0;right:0;font-size:.85rem}
.yut .n0{left:4%;top:4%}.yut .n1{right:4%;top:4%}.yut .n2{right:4%;bottom:4%}.yut .n3{left:4%;bottom:4%}
.yut .nb{left:50%;top:50%;transform:translate(-50%,-50%);width:84px;height:84px;font-size:1.9rem}
.yut .nb:active{transform:translate(-50%,-50%) translateY(4px)}
/* 보스 노드는 캐릭터가 직접 선다 — 라스터 보스는 배경이 투명이라
   보라 원판을 깔면 캐릭터 뒤에 판이 하나 더 생긴다(상자 노드와 같은 처리). */
.yut .bubble.boss:not(.done){background:none;box-shadow:none;border:0}
.boss-face{width:100%;height:100%;object-fit:contain;
  filter:drop-shadow(0 4px 2px rgba(16,26,51,.28))}
.yut .bubble.boss.done{background:#564a72;box-shadow:0 7px 0 #332b47;color:#fff}
.yut .bubble.boss.lock{filter:grayscale(.8) opacity(.55)}
.yut .bubble.boss.ready{animation:bossPulse 1.4s ease-in-out infinite}
@keyframes bossPulse{0%,100%{filter:drop-shadow(0 0 0 rgba(139,92,246,0))}50%{filter:drop-shadow(0 0 14px rgba(139,92,246,.85))}}
.yut .piece{position:absolute;z-index:4;pointer-events:none;filter:drop-shadow(0 3px 2px rgba(0,0,0,.18));animation:bob 1.6s ease-in-out infinite}

/* ── 보스전 UI ── */
/* ── 보스전 무대 — 랑이 vs 보스. 타격 연출은 돌진→임팩트→넉백 3박자로 읽히게 ── */
.stage{margin:2px 0 10px;user-select:none}
.bhud{display:flex;align-items:center;gap:8px;margin-bottom:6px}
.bhud .bname{font-family:var(--mono);font-size:.72rem;font-weight:700;letter-spacing:.06em;color:var(--ink);white-space:nowrap;max-width:42%;overflow:hidden;text-overflow:ellipsis}
.hpbar{position:relative;flex:1;height:18px;border-radius:99px;background:var(--line);overflow:hidden;border:2px solid var(--line)}
.hpbar i{position:absolute;inset:0;border-radius:99px;background:linear-gradient(180deg,#ff7a6b,#e53935);transform-origin:0 50%;transition:transform .45s cubic-bezier(.22,1,.36,1)}
.hpbar.low i{background:linear-gradient(180deg,#ffb44d,#f4511e);animation:hpPulse .9s ease-in-out infinite}
@keyframes hpPulse{0%,100%{filter:brightness(1)}50%{filter:brightness(1.25)}}
.hpbar b{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:.66rem;font-weight:900;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.45);font-variant-numeric:tabular-nums}
/* 보스전 무대 = 입국 심사대. 티켓 스톡 바닥에 게이트 대기선이 그어져 있다 */
.arena{position:relative;height:150px;border-radius:12px;background:var(--paper);
  border:2.5px solid var(--ink-navy);overflow:hidden}
.arena:before{content:"";position:absolute;inset:0;pointer-events:none;
  background:repeating-linear-gradient(90deg,transparent 0 26px,rgba(16,26,51,.05) 26px 28px)}
:root[data-theme="dark"] .arena{background:#0F1A30;border-color:var(--bub)}
/* 바닥선 — 두 캐릭터가 같은 무대에 서 있다는 신호 */
.arena:after{content:"";position:absolute;left:6%;right:6%;bottom:14px;height:2px;border-radius:2px;background:var(--line)}
.fighter{position:absolute;bottom:6px;line-height:0}
.fighter img{height:112px;width:auto;filter:drop-shadow(0 6px 6px rgba(34,48,79,.18))}
.hero{left:6%}
.foe{right:4%;bottom:10px}
.foe img{height:104px}
/* 평상시 숨쉬기 — 정지 화면처럼 보이지 않게 */
.hero img{animation:fBreath 2.6s ease-in-out infinite;transform-origin:50% 100%}
.foe img{animation:fFloat 2.2s ease-in-out infinite}
@keyframes fBreath{0%,100%{transform:scale(1,1)}50%{transform:scale(1.02,.978)}}
@keyframes fFloat{0%,100%{transform:translateY(0) rotate(-1.5deg)}50%{transform:translateY(-7px) rotate(1.5deg)}}
/* 돌진 — 예비동작(살짝 뒤로) 후 튀어나간다 */
.hero.lunge{animation:lunge .6s cubic-bezier(.34,1.56,.64,1)}
@keyframes lunge{0%{transform:translateX(0)}18%{transform:translateX(-10px)}40%{transform:translateX(96px) scaleX(1.04)}62%{transform:translateX(70px)}100%{transform:translateX(0)}}
.foe.foe-lunge{animation:foeLunge .6s cubic-bezier(.34,1.56,.64,1)}
@keyframes foeLunge{0%{transform:translateX(0)}18%{transform:translateX(10px)}40%{transform:translateX(-92px) scaleX(1.04)}62%{transform:translateX(-66px)}100%{transform:translateX(0)}}
/* 넉백 — 맞은 쪽이 뒤로 밀리며 기울어진다 */
.foe.knock{animation:knock .5s ease-out}
@keyframes knock{0%{transform:translateX(0) rotate(0)}22%{transform:translateX(26px) rotate(9deg)}55%{transform:translateX(10px) rotate(3deg)}100%{transform:translateX(0) rotate(0)}}
.hero.knock-back{animation:knockBack .5s ease-out}
@keyframes knockBack{0%{transform:translateX(0) rotate(0)}22%{transform:translateX(-24px) rotate(-9deg)}55%{transform:translateX(-9px) rotate(-3deg)}100%{transform:translateX(0) rotate(0)}}
.foe.ko{animation:foeKo 1.3s cubic-bezier(.4,0,.6,1) forwards}
@keyframes foeKo{0%{transform:translate(0,0) rotate(0);opacity:1}25%{transform:translate(18px,-16px) rotate(14deg)}100%{transform:translate(34px,42px) rotate(38deg);opacity:.15}}
/* 임팩트 스프라이트 — 두 캐릭터 사이에서 터진다 */
.fx{position:absolute;left:50%;top:42%;transform:translate(-50%,-50%);pointer-events:none;line-height:0;z-index:2}
.fx.right{left:66%}   /* 랑이가 때린 자리 = 보스 쪽 */
.fx.left{left:32%}    /* 보스가 때린 자리 = 랑이 쪽 */
.fx img{width:104px;height:104px;object-fit:contain}
.fx.pop img{animation:fxPop .4s ease-out forwards}
@keyframes fxPop{0%{transform:scale(.35) rotate(-14deg);opacity:0}30%{transform:scale(1.15) rotate(4deg);opacity:1}100%{transform:scale(1.5) rotate(10deg);opacity:0}}
/* 데미지 숫자 */
.dmg-pop{position:absolute;right:16%;top:22%;z-index:3;font-weight:900;font-size:1.5rem;opacity:0;pointer-events:none;text-shadow:0 2px 0 rgba(0,0,0,.25)}
.dmg-pop.hit{color:#ffd166}
.dmg-pop.hurt{color:#ff6b6b;right:auto;left:14%}
.dmg-pop.ko{color:var(--gold);font-size:1.15rem;right:auto;left:50%;top:16%;transform:translateX(-50%);letter-spacing:.04em}
.dmg-pop.up{animation:dmgUp .78s ease-out}
@keyframes dmgUp{0%{opacity:0;transform:translateY(10px) scale(.7)}22%{opacity:1;transform:translateY(-6px) scale(1.25)}70%{opacity:1;transform:translateY(-22px) scale(1)}100%{opacity:0;transform:translateY(-34px) scale(.95)}}
.dmg-pop.ko.up{animation:dmgUpKo .9s ease-out}
@keyframes dmgUpKo{0%{opacity:0;transform:translateX(-50%) scale(.5)}25%{opacity:1;transform:translateX(-50%) scale(1.3)}75%{opacity:1;transform:translateX(-50%) scale(1)}100%{opacity:0;transform:translateX(-50%) scale(1)}}
/* 화면 흔들림 — 타격의 무게. red는 피격, big은 격파 */
.stage.shake{animation:stShake .32s}
.stage.shake.big{animation:stShake .5s}
@keyframes stShake{0%,100%{transform:translate(0,0)}15%{transform:translate(-6px,3px)}30%{transform:translate(6px,-3px)}45%{transform:translate(-5px,-2px)}60%{transform:translate(5px,2px)}80%{transform:translate(-3px,1px)}}
.stage.shake.red .arena{animation:arenaRed .38s}
@keyframes arenaRed{0%{box-shadow:inset 0 0 0 100vmax rgba(255,75,75,.32)}100%{box-shadow:none}}
.btimer{height:8px;border-radius:99px;background:var(--line);overflow:hidden;margin-top:8px}
.btimer i{display:block;height:100%;width:100%;border-radius:99px;background:var(--gold);transform-origin:0 50%}
.btimer i.run{animation:btime linear forwards}
.btimer.hurry i{background:var(--red)}
@keyframes btime{from{transform:scaleX(1)}to{transform:scaleX(0)}}
@keyframes flashRed{0%{box-shadow:inset 0 0 0 100vmax rgba(255,75,75,.16)}100%{box-shadow:none}}
.dmg{animation:flashRed .5s}
@media (prefers-reduced-motion:reduce){
  .hero img,.foe img,.hero,.foe,.stage,.fx img,.dmg-pop{animation:none!important}
  .dmg-pop.up{opacity:1}
}
/* 도장 찍힘 — 이 앱에서 "해냈다"는 언제나 도장이 쿵 찍히는 소리로 온다.
   높은 곳에서 내려와 살짝 비뚤게 박히고, 잉크가 번지듯 한 번 흔들린다 */
.stamp-hit{display:flex;justify-content:center;margin:-6px 0 2px;pointer-events:none}
.stamp-hit img{width:84px;height:84px;object-fit:contain;
  animation:stampHit .52s cubic-bezier(.2,.9,.3,1) .18s both}
@keyframes stampHit{
  0%{opacity:0;transform:scale(2.6) rotate(-22deg)}
  55%{opacity:1;transform:scale(.92) rotate(-9deg)}
  72%{transform:scale(1.05) rotate(-11deg)}
  100%{opacity:1;transform:scale(1) rotate(-10deg)}}
@media (prefers-reduced-motion:reduce){.stamp-hit img{animation:none}}

/* 승리 화면 — 쓰러진 보스 옆에서 랑이가 이겼다는 그림 한 장 */
.win-scene{position:relative;display:flex;align-items:flex-end;justify-content:center}
.win-scene .win-foe{height:86px;filter:grayscale(.3);opacity:.85;transform:rotate(-16deg);margin-right:-10px;animation:winFoe .6s ease-out}
.win-scene .win-hero{height:140px;animation:winHero .7s cubic-bezier(.34,1.56,.64,1)}
@keyframes winFoe{0%{opacity:0;transform:rotate(-40deg) translateY(-20px)}100%{opacity:.85;transform:rotate(-16deg) translateY(0)}}
@keyframes winHero{0%{opacity:0;transform:scale(.6) translateY(24px)}60%{transform:scale(1.08) translateY(-6px)}100%{opacity:1;transform:scale(1) translateY(0)}}
@media (prefers-reduced-motion:reduce){.win-scene img{animation:none!important}}
