/* ko-linebreak:2026-10-08 한국어 줄바꿈 기본값. lang="ko" 인 글에만 적용 */
[lang|="ko"] { word-break: keep-all; line-break: strict; overflow-wrap: break-word; }
:where(h1, h2, h3, h4, caption, figcaption, th):where(:lang(ko)) { text-wrap-style: balance; }
:where(p, li, dd, td, blockquote, .note):where(:lang(ko)) { text-wrap-style: pretty; }
:where(code, .url):where(:lang(ko)) { overflow-wrap: anywhere; }
:where(button, .btn, .badge, .chip):where(:lang(ko)) { white-space: nowrap; }
:where(.user-text):where(:lang(ko)) { white-space: pre-line; }
/* /ko-linebreak */

/* ============================================================
   RAIM 네컷 — 토스풍 미니멀
   원칙: 흰 배경 · 회색 위계 · 블루 단일 포인트 · 평면 카드 · 장식 최소
   ============================================================ */

/* 손글씨 문구용 — 네이버 또박또박 */
@font-face{
  font-family:'Ttobak';
  src:url('https://cdn.jsdelivr.net/gh/projectnoonnu/naverfont_03@1.0/Ddobakddobak.woff') format('woff');
  font-weight:400; font-display:swap;
}
/* 본문·제목 — 페이퍼로지(Paperlogy) */
@font-face{ font-family:'Paperlogy'; font-weight:300; font-display:swap;
  src:url('https://fastly.jsdelivr.net/gh/projectnoonnu/2408-3@1.0/Paperlogy-3Light.woff2') format('woff2'); }
@font-face{ font-family:'Paperlogy'; font-weight:400; font-display:swap;
  src:url('https://fastly.jsdelivr.net/gh/projectnoonnu/2408-3@1.0/Paperlogy-4Regular.woff2') format('woff2'); }
@font-face{ font-family:'Paperlogy'; font-weight:500; font-display:swap;
  src:url('https://fastly.jsdelivr.net/gh/projectnoonnu/2408-3@1.0/Paperlogy-5Medium.woff2') format('woff2'); }
@font-face{ font-family:'Paperlogy'; font-weight:600; font-display:swap;
  src:url('https://fastly.jsdelivr.net/gh/projectnoonnu/2408-3@1.0/Paperlogy-6SemiBold.woff2') format('woff2'); }
@font-face{ font-family:'Paperlogy'; font-weight:700; font-display:swap;
  src:url('https://fastly.jsdelivr.net/gh/projectnoonnu/2408-3@1.0/Paperlogy-7Bold.woff2') format('woff2'); }
@font-face{ font-family:'Paperlogy'; font-weight:800; font-display:swap;
  src:url('https://fastly.jsdelivr.net/gh/projectnoonnu/2408-3@1.0/Paperlogy-8ExtraBold.woff2') format('woff2'); }

:root{
  /* 토스 뉴트럴 */
  --bg:#F9FAFB; --surface:#FFFFFF;
  --ink:#191F28; --sub:#4E5968; --muted:#636D7A;   /* 흐린 글씨도 본문이라 AA 4.5:1을 넘겨야 한다 */
  /* 단일 포인트 — 토스 블루 */
  --primary:#3182F6; --primary-press:#1B64DA;
  /* 면(버튼 배경)에는 --primary, 옅은 바탕 위 글씨에는 --primary-ink. #3182F6은 글씨로 쓰면 3.6:1뿐이다 */
  --primary-ink:#1B64DA;
  --primary-weak:#EBF4FF; --danger:#F04452; --danger-ink:#D32F3C; --danger-weak:#FDEDEE;
  --line:#F2F4F6; --line-strong:#E5E8EB; --fill:#F2F4F6;
  /* 그림자 — 아주 은은하게 한 겹 */
  --shadow-sm:0 1px 3px rgba(25,31,40,.06);
  --shadow:0 4px 16px rgba(25,31,40,.08);
  --shadow-lg:0 8px 28px rgba(25,31,40,.12);
  --radius:20px; --radius-sm:14px;
  --display:'Paperlogy','Pretendard',system-ui,-apple-system,sans-serif;
  --body:'Paperlogy','Pretendard',system-ui,-apple-system,sans-serif;
  --hand:'Ttobak','Paperlogy',cursive;
}
*{box-sizing:border-box; -webkit-tap-highlight-color:transparent;}
html,body{height:100%;}
/* 키오스크: 시스템 글꼴 크기 무시 · 당겨서 새로고침 차단 · 더블탭 확대 차단 */
html{-webkit-text-size-adjust:100%; text-size-adjust:100%; overscroll-behavior:none; touch-action:manipulation;}
/* 폰트 전부 반영 — 버튼·입력 등 폼 요소는 폰트를 상속하지 않으므로 명시 */
button,input,select,textarea{font-family:var(--body); letter-spacing:-.01em;}
body{
  margin:0; font-family:var(--body); color:var(--ink);
  font-weight:400; letter-spacing:-.01em; -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
  background:var(--bg);
  overflow:hidden; user-select:none; -webkit-user-select:none;
  overscroll-behavior:none; touch-action:manipulation;
  -webkit-touch-callout:none;   /* 롱프레스 미리보기·저장 팝업 */
}
/* 문구 입력창만 선택·편집 허용 */
input,textarea{user-select:text; -webkit-user-select:text; -webkit-touch-callout:default;}
/* 사진·로고 드래그해서 빼내기 차단 */
img,canvas,video{-webkit-user-drag:none; user-drag:none;}
/* 키오스크 한 칸. 폭을 '높이에서' 끌어와 세로 비율(0.62 — 갤럭시탭 A9 세로와 같은 모양)을 지킨다.
   PC 가로 모니터에서 max-width만 두면 박스가 820×창높이가 되어, 창이 낮을 때(노트북) 정사각형을
   지나 가로로 퍼진다 — 세로 전제로 짜인 화면(선택·결과)이 그 지점에서 무너진다.
   폰·태블릿은 100%가 언제나 더 작아 계산에 걸리지 않으므로 지금 모양 그대로다.
   360px 하한은 창을 아주 낮게 줄였을 때 패널이 실 가닥처럼 좁아지는 것만 막는다. */
/* 높이 상한 1322px = 820/0.62 — 폭이 820에서 멈추는 큰 화면(세로로 돌린 PC 모니터)에서만 걸린다.
   없으면 패널이 1920px까지 늘어나 같은 내용이 성기게 퍼진다. inset:0 + margin:auto로 가운데에 세운다. */
#app{width:min(100%, 820px, max(62dvh, 360px)); height:min(100dvh, 1322px);
  position:absolute; inset:0; margin:auto; overflow:hidden; background:var(--bg);}
/* PC — 남는 좌우 여백이 '덜 그려진 화면'이 아니라 세워둔 키오스크로 읽히게 바닥을 한 톤 내린다 */
@media (min-width:900px){
  body:not(.adminbody){background:#EDF0F4;}   /* 관리자 화면은 긴 문서라 바닥을 그대로 둔다 */
  #app{box-shadow:var(--shadow-lg);}
}
/* 마우스가 붙은 PC에서만 — 누르기 전에 반응이 보이게. 터치 기기는 :active가 담당한다.
   :not(.sel)은 고른 칸의 파란 테두리를 회색 호버가 덮지 않게 하려는 것(특성도가 같아 뒤 규칙이 이긴다) */
@media (hover:hover){
  .btn:hover:not([disabled]){filter:brightness(1.07);}
  .vercard:hover, .robot:hover{border-color:var(--primary);}
  .coloropt:not(.sel):hover .swatch{box-shadow:0 0 0 3px var(--surface), 0 0 0 5px var(--line-strong);}
  .thumb:not(.sel):hover{outline-color:var(--line-strong);}
  .langtoggle button:hover, .iconbtn:hover{color:var(--ink);}
}
/* 앱으로 설치 버튼 — 마우스가 있는 환경에서 설치가 가능할 때만 뜬다(index.html).
   PC에서는 가운데 패널 밖 여백에 놓인다 */
#installbtn{position:fixed; right:18px; bottom:18px; z-index:50;
  border:1px solid var(--line-strong); background:var(--surface); color:var(--sub);
  font-family:var(--body); font-weight:600; font-size:14px; padding:11px 18px;
  border-radius:999px; box-shadow:var(--shadow); cursor:pointer;}
.screen{position:absolute; inset:0; display:none; flex-direction:column; padding:28px 26px 30px;}
.screen.on{display:flex; animation:fade .35s cubic-bezier(.16,1,.3,1) both;}
@keyframes fade{from{opacity:0; transform:translateY(8px)} to{opacity:1; transform:none}}
@media (prefers-reduced-motion:reduce){*{animation:none!important; transition:none!important;}}

/* ---------------- 헤더 ---------------- */
.topbar{display:flex; align-items:center; justify-content:space-between; margin-bottom:18px;}
.brand{display:flex; align-items:center; gap:12px;}
.brand .museumlogo{height:42px; width:auto; display:block; object-fit:contain;}
.brand b{font-family:var(--display); font-weight:700; font-size:21px; letter-spacing:-.02em; line-height:1;}
.brand small{display:block; color:var(--muted); font-size:11px; font-weight:500; letter-spacing:1.2px; margin-top:3px;}
.langtoggle{display:flex; border-radius:12px; padding:3px; gap:2px; background:var(--fill);}
.langtoggle button{border:0; background:none; font-family:var(--body); font-weight:600; color:var(--muted); padding:8px 14px; border-radius:9px; font-size:14px; cursor:pointer; transition:color .15s, background .15s;}
.langtoggle button.sel{background:var(--surface); color:var(--ink); font-weight:700; box-shadow:var(--shadow-sm);}

h1{font-family:var(--display); font-weight:800; line-height:1.24; letter-spacing:-.025em; margin:0;}
.lead{color:var(--sub); margin:10px 0 0; line-height:1.6; font-weight:400;}

/* ---------------- 버튼 — 플랫·정제된 타이포 ---------------- */
.btn{font-family:var(--display); font-weight:700; font-size:19px; letter-spacing:-.01em;
  border-radius:14px; padding:18px 24px; color:#fff; cursor:pointer; width:100%; border:0;
  background:var(--ink); line-height:1.2;
  transition:background .15s ease, transform .12s ease, opacity .15s ease;}
.btn:active{transform:scale(.99);}
.btn.primary{background:var(--primary);}
.btn.primary:active{background:var(--primary-press);}
.btn.pop{background:var(--primary);}
.btn.pop:active{background:var(--primary-press);}
.btn.ghost{background:var(--surface); color:var(--sub); border:1px solid var(--line-strong);}
.btn[disabled]{background:var(--fill); color:#C1C8D0; cursor:default; transform:none;}
.row{display:flex; gap:12px;}
.grow{flex:1;}

/* ---------------- 시작 화면 ---------------- */
.hero{flex:1; display:flex; flex-direction:column; justify-content:center; gap:22px;}
.heroart{align-self:center; width:216px; height:216px; position:relative;}
.heroart .ring{display:none;}   /* 회전 블러 링 제거 — 정적이고 차분하게 */
.heroart .face{position:absolute; inset:0; border-radius:50%; background:var(--surface);
  display:grid; place-items:center; border:1px solid var(--line); box-shadow:var(--shadow); overflow:hidden;}
.heroart .face svg{width:124px; height:124px;}
.heroart .face .faceimg{width:82%; height:82%; object-fit:contain;}
.steps{display:flex; gap:8px; justify-content:center; flex-wrap:wrap; margin-top:4px;}
.pill{background:var(--surface); border:1px solid var(--line); color:var(--sub);
  padding:9px 15px; border-radius:999px; font-size:13px; font-weight:500;}
.pill b{color:var(--ink); font-weight:600;}

/* ---------------- 촬영 ---------------- */
.stage{flex:1; position:relative; border-radius:var(--radius); overflow:hidden; background:#101720; box-shadow:var(--shadow); display:grid; place-items:center;}
#video{width:100%; height:100%; object-fit:cover; transform:scaleX(-1);}
.countdown{position:absolute; inset:0; display:grid; place-items:center; pointer-events:none;}
.cdnum{font-family:var(--display); font-weight:800; font-size:160px; color:#fff; text-shadow:0 8px 40px rgba(0,0,0,.4); animation:pop .9s cubic-bezier(.16,1,.3,1) both;}
@keyframes pop{0%{transform:scale(.4); opacity:0}30%{opacity:1}100%{transform:scale(1.15); opacity:0}}
.flash{position:absolute; inset:0; background:#fff; opacity:0;}
.flash.go{animation:flash .45s ease;}
@keyframes flash{0%{opacity:0}10%{opacity:.95}100%{opacity:0}}
.shotchips{position:absolute; top:14px; left:14px; display:flex; gap:6px; flex-wrap:wrap; max-width:70%;}
.shotchips span{width:26px; height:26px; border-radius:8px; background:rgba(255,255,255,.22); display:grid; place-items:center; color:#fff; font-weight:700; font-size:12px; font-family:var(--display);}
.shotchips span.done{background:var(--primary); color:#fff;}
.aibadge{position:absolute; bottom:14px; left:14px; display:flex; align-items:center; gap:8px; background:rgba(16,23,32,.62); color:#fff; padding:9px 15px; border-radius:999px; font-size:13px; font-weight:500;}
.aibadge .dot{width:8px; height:8px; border-radius:50%; background:#4E9BFF; animation:pulse 1.6s infinite;}
@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(78,155,255,.55)}70%{box-shadow:0 0 0 12px rgba(78,155,255,0)}100%{box-shadow:0 0 0 0 rgba(78,155,255,0)}}
.capnote{text-align:center; color:var(--muted); margin:16px 0 0; font-weight:500;}

/* ---------------- 분석 ---------------- */
.analyzing{flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:24px;}
.scanner{width:190px; height:190px; border-radius:32px; background:var(--surface); border:1px solid var(--line); box-shadow:var(--shadow); position:relative; overflow:hidden; display:grid; place-items:center;}
.scanner svg{width:116px; height:116px;}
.scanline{position:absolute; left:0; right:0; height:3px; background:linear-gradient(90deg,transparent,var(--primary),transparent); animation:scan 1.6s ease-in-out infinite;}
@keyframes scan{0%,100%{top:6%}50%{top:92%}}

/* ---------------- 4컷 선택 ---------------- */
/* 좌(썸네일 그리드)·우(프레임 미리보기) 두 칸을 같은 너비로, 상단 라인 맞춰 배치 */
/* 두 칸의 폭은 app.js의 fitSelRow()가 정한다 — 왼쪽 썸네일 묶음과 오른쪽 프레임의 높이를 맞추기 위해 */
.selrow{flex:1; display:flex; gap:16px; min-height:0; margin-top:10px; justify-content:center; align-items:flex-start;}
.selcol{min-width:0; display:flex; flex-direction:column; align-items:center; gap:10px;}
.grid8{display:grid; grid-template-columns:1fr 1fr; gap:12px; width:100%;}
.thumb{position:relative; border-radius:var(--radius-sm); overflow:hidden; aspect-ratio:3/4; background:#101720; box-shadow:var(--shadow-sm); cursor:pointer; outline:3px solid transparent; outline-offset:0; transition:outline-color .15s, transform .15s;}
.thumb img{width:100%; height:100%; object-fit:cover;}
.thumb.sel{outline-color:var(--primary);}
.thumb.sel img{transform:scale(1.02);}
.thumb .check{position:absolute; top:9px; right:9px; width:30px; height:30px; border-radius:50%; background:rgba(255,255,255,.92); display:grid; place-items:center; font-family:var(--display); font-weight:700; color:var(--muted); font-size:15px;}
.thumb.sel .check{background:var(--primary); color:#fff;}
/* 순번 배지(우상단 30px)와 겹치지 않게 폭을 제한한다. 얼굴을 덜 가리도록 아래쪽으로 내렸다 */
.thumb .rec{position:absolute; bottom:9px; left:9px; max-width:calc(100% - 18px); background:rgba(255,255,255,.94); color:var(--primary-ink); font-size:11px; font-weight:700; padding:5px 10px; border-radius:999px;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.plabel{font-size:12px; font-weight:600; color:var(--muted); letter-spacing:.5px; flex:none;}
/* 캔버스는 2:3 비율 고정 — 세로로 늘어나 흰 여백이 생기지 않게 */
.selpreview canvas{width:100%; height:auto; aspect-ratio:2/3; object-fit:contain; border-radius:12px; border:1px solid var(--line); box-shadow:var(--shadow); background:#fff;}
.selbar{display:flex; align-items:center; gap:14px; margin-top:12px;}
.count{font-family:var(--display); font-weight:700; font-size:18px; white-space:nowrap; color:var(--sub);}

/* ---------------- 프레임 만들기 — 한 페이지에 한 단계, 가운데 정렬 ---------------- */
.wizdots{display:flex; gap:8px; justify-content:center; margin:0 0 14px;}
.wizdots span{width:8px; height:8px; border-radius:50%; background:var(--line-strong); transition:width .2s ease, background .2s ease;}
.wizdots span.done{background:var(--primary); opacity:.45;}   /* 앞으로 올 단계(--line-strong)보다 진해야 '지나왔다'로 읽힌다 */
.wizdots span.on{width:26px; border-radius:5px; background:var(--primary);}
.wizq{font-size:26px; text-align:center; margin-bottom:6px;}
.wizhelp{text-align:center; color:var(--sub); font-size:15px; margin:0 0 4px; line-height:1.5;}
/* .btn은 width:100%라 flex 안에서 서로 밀어낸다 — 여기서만 폭을 flex에 맡긴다 */
.wiznav{display:flex; gap:12px;}
.wiznav .btn{width:auto;}
.wiznav .btn.ghost{flex:0 0 30%;}
.wiznav .btn.primary{flex:1;}
/* 보기는 남는 공간 한가운데로 모은다. 세로가 짧은 기기에서만 스크롤이 생기게 */
.wizbody{flex:1; min-height:0; display:flex; flex-direction:column; align-items:center; justify-content:safe center;
  overflow-y:auto; -webkit-overflow-scrolling:touch; padding:18px 0;}
.wizbody > *{width:100%;}
/* 색 9개 = 3×3으로 딱 떨어진다. 개수가 바뀌어도 마지막 줄이 가운데 모이게 flex로 둔다
   (예전 주석은 "색은 8개"였는데 한가위가 늘면서 4열에서 한 칸이 외따로 떨어졌었다) */
.colors{display:flex; flex-wrap:wrap; justify-content:center; gap:16px 14px; max-width:540px; margin:0 auto;}
.coloropt{flex:0 0 calc((100% - 28px)/3);}
.coloropt{display:flex; flex-direction:column; align-items:center; gap:9px; cursor:pointer;}
.coloropt .swatch{width:100%; aspect-ratio:1; border-radius:20px; border:2px solid transparent; box-shadow:var(--shadow-sm);
  position:relative; display:block; transition:transform .15s, box-shadow .15s;}
.coloropt:active .swatch{transform:scale(.95);}
.coloropt.sel .swatch{box-shadow:0 0 0 3px var(--surface), 0 0 0 5px var(--primary);}
.coloropt.sel .swatch::after{content:"✓"; position:absolute; inset:0; display:grid; place-items:center; color:#fff;
  font-family:var(--display); font-weight:700; font-size:28px; text-shadow:0 1px 6px rgba(0,0,0,.35);}
/* 흰 프레임 — 배경과 구분되게 테두리를 주고 체크표시를 어둡게 */
.coloropt .swatch.light{border-color:var(--line-strong);}
.coloropt.sel .swatch.light::after{color:#191F28; text-shadow:none;}
.coloropt .nm{font-family:var(--display); font-weight:600; font-size:14px; color:var(--sub);}
.coloropt.sel .nm{font-weight:700; color:var(--primary-ink);}
/* 결과 화면 자동 복귀 안내 */
.autoreset{text-align:center; color:var(--muted); font-size:13px; margin:10px 0 0; min-height:18px;}
/* 선택 카드 공통 — 흰 카드 + 선택 시 블루 보더/틴트 */
.versions{display:flex; gap:12px; max-width:520px; margin:0 auto;}
.vercard{flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:6px; padding:20px 14px; border-radius:16px; cursor:pointer;
  font-family:var(--display); font-weight:600; font-size:18px; text-align:center;
  background:var(--surface); border:1.5px solid var(--line-strong);
  transition:border-color .15s, background .15s, transform .12s;}
.vercard:active{transform:scale(.98);}
.vercard.sel{border-color:var(--primary); background:var(--primary-weak); color:var(--ink); font-weight:700;}
.vercard .emo{font-size:30px; line-height:1;}
.vercard .desc{font-size:13px; font-weight:400; color:var(--muted); line-height:1.45;}
.vercard.sel .desc{color:var(--sub);}
/* 로봇 10종 — 3열이면 마지막 한 칸이 남는다. 남는 칸이 가운데로 모이게 flex로 둔다 */
.robots{display:flex; flex-wrap:wrap; justify-content:center; gap:12px; max-width:600px; margin:0 auto;}
.robot{flex:0 0 calc((100% - 24px)/3);}
.robot{background:var(--surface); border:1.5px solid var(--line-strong); border-radius:16px; padding:12px 8px; text-align:center; cursor:pointer;
  transition:border-color .15s, background .15s, transform .12s;}
.robot:active{transform:scale(.97);}
.robot.sel{border-color:var(--primary); background:var(--primary-weak);}
.robot .ill{width:72px; height:72px; margin:0 auto 6px; border-radius:14px; overflow:hidden; display:grid; place-items:center; background:var(--bg);}
.robot .ill img,.robot .ill svg{width:100%; height:100%; object-fit:contain;}
.robot .nm{font-family:var(--display); font-weight:600; font-size:15px; line-height:1.15;}
.robot.sel .nm{font-weight:700;}
.robot .nm small{display:block; color:var(--muted); font-size:11px; font-weight:500;}
.initwrap{max-width:520px; margin:0 auto;}
.initwrap input{width:100%; font-family:var(--hand); font-size:34px; letter-spacing:1px; text-align:center; padding:16px; border:1.5px solid var(--line-strong); border-radius:14px; background:var(--surface); color:var(--ink); transition:border-color .15s, box-shadow .15s;}
.initwrap input:focus{outline:0; border-color:var(--primary); box-shadow:0 0 0 3px rgba(49,130,246,.15);}
.initwrap .hint{color:var(--muted); font-size:13px; margin:9px 2px 0; font-weight:400; text-align:center;}
/* 문구를 비워도 된다는 안내. 파란 틴트 카드로 두면 '선택된 .vercard'와 똑같이 보여
   누르려다 아무 일도 안 일어나므로, 누를 수 없는 글씨임이 드러나게 평범한 문장으로 둔다 */
.initwrap .skiphint{margin:14px 0 0; color:var(--sub); font-size:14px; font-weight:500; text-align:center; line-height:1.5;}

/* ---------------- 완성 ---------------- */
/* 위에서부터 프레임 사진 · QR 카드 · [처음부터] 버튼. 사진이 남는 높이를 전부 가져간다.
   카드 폭은 고정이다 — 예전에는 fitResultRow()가 사진 폭에 맞췄는데, 카드가 좁아지면 문구 줄이
   늘어 카드가 높아지고 그만큼 사진이 작아져 다시 카드가 좁아지는 되먹임에 걸려, 화면이 정사각형에
   가까우면 사진이 몇 px까지 붕괴했다. 카드를 사진과 무관하게 두면 그 고리가 끊긴다.
   카드가 넓을수록 문구가 적게 접혀 카드가 낮아지고, 결과적으로 사진이 커진다. */
.resultrow{flex:1; display:flex; flex-direction:column; gap:14px; align-items:center; min-height:0; margin-top:12px;}
.resultphoto{flex:1; display:flex; align-items:center; justify-content:center; min-height:0; min-width:0; width:100%;}
.resultphoto canvas{max-width:100%; max-height:100%; object-fit:contain; border-radius:16px; border:1px solid var(--line); box-shadow:var(--shadow-lg); background:#fff;}
.qrcard{flex:none; width:min(100%, 560px); background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); padding:18px 22px;
  box-shadow:var(--shadow-sm);
  display:flex; flex-direction:row; gap:20px; align-items:center; justify-content:flex-start; text-align:left;}
/* ⚠️ QR 표시 크기를 158px 아래로 줄이지 말 것 — jsQR 실측(서명 URL 12종):
   104px 8/12, 118px 10/12, 146px 9/12, 158px 11/12, 220px 12/12.
   158px이 예전부터 검증된 하한이다. 더 줄이려면 QR에 담는 URL을 짧게 만들고 다시 실측할 것. */
.qrcard #qrbox{width:172px; height:172px; flex:none; display:grid; place-items:center; background:#fff; border:1px solid var(--line); border-radius:14px; font-size:40px;}
.qrcard #qrbox img,.qrcard #qrbox canvas{width:158px; height:158px; max-width:100%;}
.qrcard .qtxt{flex:1; min-width:0;}
.qrcard .qtxt b{font-family:var(--display); font-weight:700; font-size:19px; letter-spacing:-.02em;}
.qrcard .qtxt p{margin:7px 0 0; color:var(--sub); font-size:14px; line-height:1.5; font-weight:400;}
.qrcard .qtxt .qrexpire{margin-top:9px; font-weight:600; color:var(--danger-ink); font-size:13.5px; line-height:1.45; white-space:pre-line;}
.qrcard.err{background:var(--danger-weak); border-color:#F8C9CC;}

.loaderdots:after{content:"·"; animation:dots 1.2s steps(4,end) infinite;}
@keyframes dots{0%{content:""}25%{content:"·"}50%{content:"··"}75%{content:"···"}}
::-webkit-scrollbar{width:0;height:0}

/* ============================================================
   ADMIN — 통계 대시보드 (admin.html)
   ============================================================ */
body.adminbody{overflow:auto; user-select:text; -webkit-user-select:text;}
.adminwrap{max-width:760px; margin:0 auto; padding:28px 22px 60px;}
.adminbar{display:flex; align-items:center; justify-content:space-between; margin-bottom:20px;}
.adminbar h1{font-size:26px;}
.adminbar .sub{color:var(--muted); font-size:13px; margin-top:4px; font-weight:500;}
.iconbtn{border:0; background:var(--fill); color:var(--sub); font-family:var(--body); font-weight:600; font-size:14px; padding:10px 16px; border-radius:12px; cursor:pointer;}
.iconbtn:active{transform:scale(.97);}

/* 필터 (기간·단위) */
.seg{display:inline-flex; border-radius:12px; padding:3px; gap:2px; background:var(--fill);}
.seg button{border:0; background:none; font-family:var(--body); font-weight:600; color:var(--muted); padding:9px 15px; border-radius:9px; font-size:14px; cursor:pointer; white-space:nowrap; transition:color .15s, background .15s;}
.seg button.sel{background:var(--surface); color:var(--ink); font-weight:700; box-shadow:var(--shadow-sm);}
.filters{display:flex; gap:10px; flex-wrap:wrap; align-items:center; margin-bottom:16px;}
.customrange{display:flex; gap:8px; align-items:center; flex-wrap:wrap;}
.customrange input[type=date]{border:1.5px solid var(--line-strong); border-radius:10px; padding:8px 10px; font-family:var(--body); font-size:14px; color:var(--ink); background:var(--surface);}
.customrange .apply{border:0; background:var(--primary); color:#fff; font-weight:700; font-size:14px; padding:9px 16px; border-radius:10px; cursor:pointer;}

/* 스탯 타일 */
.tiles{display:grid; grid-template-columns:repeat(4,1fr); gap:10px; margin-bottom:14px;}
@media (max-width:560px){.tiles{grid-template-columns:repeat(2,1fr);}}
.tile{background:var(--surface); border:1px solid var(--line); border-radius:16px; padding:16px 14px;}
.tile small{display:block; color:var(--muted); font-size:12px; font-weight:500; margin-bottom:6px;}
.tile b{font-family:var(--display); font-weight:800; font-size:24px; letter-spacing:-.02em;}
.tile b em{font-style:normal; font-size:13px; color:var(--muted); font-weight:500; margin-left:2px;}

/* 카드 공통 */
.card{background:var(--surface); border:1px solid var(--line); border-radius:18px; padding:20px; margin-bottom:14px;}
.card h2{font-family:var(--display); font-weight:700; font-size:16px; margin:0 0 14px; letter-spacing:-.02em; display:flex; align-items:center; gap:6px;}
.card h2 small{color:var(--muted); font-weight:500; font-size:12px; margin-left:auto;}

/* 막대 차트 */
.chartwrap{overflow-x:auto;}
.bars{display:flex; align-items:flex-end; gap:6px; height:150px; min-width:100%;}
.bar{flex:1; min-width:22px; display:flex; flex-direction:column; align-items:center; justify-content:flex-end; gap:4px; height:100%;}
.bar .val{font-size:11px; color:var(--sub); font-weight:600;}
.bar .stick{width:100%; max-width:34px; background:var(--primary-weak); border-radius:6px 6px 0 0; position:relative; transition:height .3s ease;}
.bar.hot .stick{background:var(--primary);}
.bar .lab{font-size:10.5px; color:var(--muted); font-weight:500; white-space:nowrap;}

/* 캐릭터 랭킹 */
.ranklist{display:flex; flex-direction:column; gap:10px;}
.rankrow{display:flex; align-items:center; gap:12px;}
.rankrow .rimg{width:40px; height:40px; border-radius:11px; background:var(--bg); overflow:hidden; flex:none; display:grid; place-items:center;}
.rankrow .rimg img{width:100%; height:100%; object-fit:contain;}
.rankrow .rname{width:86px; flex:none; font-weight:600; font-size:14px;}
.rankrow .rname small{display:block; color:var(--muted); font-size:11px; font-weight:500;}
.rankrow .rtrack{flex:1; height:10px; background:var(--fill); border-radius:999px; overflow:hidden;}
.rankrow .rfill{height:100%; background:var(--primary); border-radius:999px; transition:width .3s ease;}
.rankrow.top .rfill{background:linear-gradient(90deg,var(--primary),#5EA1FF);}
.rankrow .rcnt{width:74px; flex:none; text-align:right; font-family:var(--display); font-weight:700; font-size:14px;}
.rankrow .rcnt small{color:var(--muted); font-weight:500; font-size:11px; margin-left:3px;}
/* 로봇 버전 배지 (일반/여름) — 로봇 × 버전 순위에서 한 줄에 둘을 같이 보여준다 */
.vbadge{display:inline-block; margin-top:3px; padding:1px 7px; border-radius:999px; font-size:10.5px; font-weight:700; line-height:1.6;}
.vbadge.normal{background:var(--fill); color:var(--sub);}
.vbadge.summer{background:#DFF3F2; color:#1E8A85;}
.vbadge.chuseok{background:#FBEBCF; color:#9A6B12;}

/* 로봇 버전 on/off 토글 */
.chip.toggle{cursor:pointer; font-family:var(--body); gap:6px;}
.chip.toggle b{font-weight:700; font-size:11.5px; color:var(--muted);}
.chip.toggle.on{background:#EAF2FE; border-color:#BBD5FB; color:var(--primary);}
.chip.toggle.on b{color:var(--primary);}
.chip.toggle:active{transform:scale(.97);}
.chip.toggle.soon{opacity:.45; cursor:default;}
.chip.toggle.soon:active{transform:none;}
.verbar{display:flex; align-items:center; gap:12px; margin-top:14px;}
.verbar .iconbtn:disabled{opacity:.55; cursor:default;}
.vermsg{font-size:12.5px; font-weight:500; color:var(--muted);}
.vermsg.err{color:var(--danger);}

/* 분포 칩 (버전·색) */
.chips{display:flex; gap:8px; flex-wrap:wrap;}
.chip{display:flex; align-items:center; gap:7px; background:var(--bg); border:1px solid var(--line); border-radius:999px; padding:7px 13px; font-size:13px; font-weight:600; color:var(--sub);}
/* 화이트 프레임 점이 밝은 배경에 묻히지 않게 테두리를 준다 */
.chip .dot{width:12px; height:12px; border-radius:50%; flex:none; border:1px solid var(--line-strong);}
.chip b{font-family:var(--display); color:var(--ink);}

/* 요약 분석 */
.insight{background:var(--primary-weak); border:1px solid #D6E8FF; border-radius:18px; padding:20px; margin-bottom:14px;}
.insight h2{font-family:var(--display); font-weight:700; font-size:16px; margin:0 0 12px; color:var(--ink);}
.insight ul{margin:0; padding:0 0 0 2px; list-style:none; display:flex; flex-direction:column; gap:8px;}
.insight li{font-size:14px; line-height:1.6; color:var(--sub); display:flex; gap:8px;}
.insight li::before{content:"·"; color:var(--primary); font-weight:800;}
.insight .note{margin:12px 0 0; font-size:11.5px; color:var(--muted);}

.adminstate{padding:60px 0; text-align:center; color:var(--muted); font-weight:500;}
.adminstate.error{color:var(--danger);}
