/* ============================================================
   JLPT 낱말 셔플 - 앰비언트 학습 표면
   토큰: 쿨 뉴트럴 1팔레트 + 버밀리언 액센트 1개.
   반경: 인터랙티브 요소 3px, 그 외 0. 카드 상자 없음(헤어라인 분리).
   ============================================================ */

/* ---------- 폰트 (전부 로컬, 시스템 폴백 필수) ---------- */
/* 폰트 URL 의 ?v= 는 캐시 무효화용. 서브셋을 다시 뜨면(tools/subset-fonts.sh) 이 숫자를 data 버전과 같이 올린다.
   안 올리면 브라우저가 옛 woff2 를 써서 새로 들어온 한자가 두부(□)로 보인다. */
@font-face{font-family:"Klee One";font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/klee-one-japanese-400-normal.woff2?v=29") format("woff2")}
@font-face{font-family:"Klee One";font-style:normal;font-weight:600;font-display:swap;
  src:url("fonts/klee-one-japanese-600-normal.woff2?v=29") format("woff2")}

@font-face{font-family:"Noto Sans JP";font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/noto-sans-jp-japanese-400-normal.woff2?v=29") format("woff2")}
/* JP 굵은 자체는 담지 않는다 — 굵게 쓰는 자리(.mark b)가 한국어라 KR 폰트가 그린다.
   혹시 일본어에 700이 걸리면 브라우저 합성 볼드로 처리된다. 330KB 절약. */
/* 라틴 서브셋은 unicode-range 로 좁혀야 일본어 선언을 덮어쓰지 않는다 */
@font-face{font-family:"Noto Sans JP";font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/noto-sans-jp-latin-400-normal.woff2?v=29") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}

@font-face{font-family:"Noto Sans KR";font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/noto-sans-kr-korean-400-normal.woff2?v=29") format("woff2")}
@font-face{font-family:"Noto Sans KR";font-style:normal;font-weight:700;font-display:swap;
  src:url("fonts/noto-sans-kr-korean-700-normal.woff2?v=29") format("woff2")}

/* かな 글꼴 6종. かな 만 담은 서브셋(각 20~36KB)이라 한자는 이 폰트에 없고 스택 뒤쪽으로 폴백된다.
   글꼴이 바뀌어도 같은 글자를 읽어낼 수 있어야 진짜로 읽는 것이다 — 그게 이 6종이 있는 이유다. */
@font-face{font-family:"Kosugi Maru";font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/kosugi-maru-japanese-400-normal.woff2?v=29") format("woff2")}
@font-face{font-family:"Shippori Mincho";font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/shippori-mincho-japanese-400-normal.woff2?v=29") format("woff2")}
@font-face{font-family:"Zen Kurenaido";font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/zen-kurenaido-japanese-400-normal.woff2?v=29") format("woff2")}
@font-face{font-family:"Yusei Magic";font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/yusei-magic-japanese-400-normal.woff2?v=29") format("woff2")}
@font-face{font-family:"Hachi Maru Pop";font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/hachi-maru-pop-japanese-400-normal.woff2?v=29") format("woff2")}
@font-face{font-family:"Dela Gothic One";font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/dela-gothic-one-japanese-400-normal.woff2?v=29") format("woff2")}

/* ---------- 토큰 ---------- */
:root{
  --bg:#0d0f11;
  --bg-elev:#15181b;
  --bg-sunk:#090b0c;
  --fg:#f3f2ef;
  --fg-2:#a3a19b;
  --fg-3:#82807b;          /* 4.52:1 on --bg-elev. #6c6a65 는 3.30 이었다 */
  --line:#23282b;
  --line-soft:#191c1f;
  --accent:#f06a4c;
  --accent-sunk:rgba(240,106,76,.14);
  --r:3px;
  --scale:1;
  /* --f-sel 은 글꼴 설정이 고른 일본어 글꼴(쉼표까지 포함한 조각)이다. 비어 있으면 지금까지의 스택 그대로다.
     var(--f-sel,) 의 빈 폴백이 핵심 — 정의 안 된 변수를 쓰면 font-family 선언 전체가 무효가 된다. */
  --f-sel:;
  --f-word:var(--f-sel,) "Klee One","Hiragino Mincho ProN","Noto Sans JP","Hiragino Kaku Gothic ProN","Apple SD Gothic Neo",system-ui,sans-serif;
  --f-jp:var(--f-sel,) "Noto Sans JP","Hiragino Kaku Gothic ProN","Yu Gothic","Apple SD Gothic Neo",system-ui,sans-serif;
  --f-kana:var(--f-sel,) "Noto Sans JP","Hiragino Kaku Gothic ProN","Yu Gothic",system-ui,sans-serif;
  /* 후리가나 크기·글꼴. 濁点(゛)과 半濁点(゜)은 작으면 구별이 안 된다 — ば/ぱ 를 못 가린다.
     루비는 100% かな 라서 かな 전용 글꼴을 그대로 쓸 수 있다. 13px 에서 재보니 Noto Sans JP 는
     획이 제일 가늘어 마크가 뭉갰고, Kosugi Maru 가 半濁点 동그라미를 또렷하게 그렸다. */
  --rt:.62em;
  --f-rt:"Kosugi Maru","Noto Sans JP","Hiragino Maru Gothic ProN",system-ui,sans-serif;
  --f-kr:"Noto Sans KR","Apple SD Gothic Neo","Malgun Gothic",system-ui,sans-serif;
  --f-ui:"Noto Sans KR","Noto Sans JP","Apple SD Gothic Neo",system-ui,-apple-system,sans-serif;
  color-scheme:dark;
}
:root[data-theme="light"]{
  --bg:#f4f5f6;
  --bg-elev:#ffffff;
  --bg-sunk:#e9ebec;
  --fg:#15181b;
  --fg-2:#585c60;
  --fg-3:#666b6f;          /* 4.51:1 on --bg-sunk. #8a8f93 는 2.73 이었다 */
  --line:#dcdfe1;
  --line-soft:#e7e9ea;
  --accent:#bf3d24;
  --accent-sunk:rgba(191,61,36,.10);
  color-scheme:light;
}

*,*::before,*::after{box-sizing:border-box}
[hidden]{display:none !important}
html,body{height:100%}
body{
  margin:0;
  background:var(--bg);
  color:var(--fg);
  font-family:var(--f-ui);
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  overflow:hidden;
}
body.idle{cursor:none}
h1,h2,h3,p,ol,ul,figure{margin:0}
ol{padding:0;list-style:none}
kbd{
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:max(.78em,11px);border:1px solid var(--line);border-radius:var(--r);
  padding:.1em .38em;color:var(--fg-2);background:var(--bg-sunk);
}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:var(--r)}
/* 우리가 그리지 않은 표면도 이 팔레트를 따라야 한다 */
::selection{background:var(--accent-sunk);color:var(--fg)}
:root{scrollbar-color:var(--line) transparent;scrollbar-width:thin}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--line);border-radius:999px}
::-webkit-scrollbar-thumb:hover{background:var(--fg-3)}
a{color:var(--accent);text-underline-offset:.18em;text-decoration-thickness:1px}

/* ---------- 셸 ---------- */
#app{
  display:grid;
  grid-template-rows:auto minmax(0,1fr) auto;
  min-height:100dvh;
  height:100dvh;
}
.chrome{transition:opacity .45s ease}
body.idle .chrome{opacity:0;pointer-events:none}

/* ---------- 상단 바 ---------- */
.topbar{
  display:flex;align-items:center;gap:1rem;
  padding:.85rem clamp(1rem,4vw,3.25rem);
  border-bottom:1px solid var(--line-soft);
  min-height:56px;
}
.mark{
  display:flex;align-items:baseline;gap:.5rem;
  font:inherit;font-size:.82rem;letter-spacing:.02em;color:var(--fg-2);white-space:nowrap;
  background:none;border:0;padding:0;cursor:pointer;text-align:left;
  transition:color .15s ease;
}
.mark:disabled{cursor:default}            /* 시작 화면에서는 갈 곳이 없다 */
/* 마크 전체가 홈으로 가는 한 덩어리다. 예전엔 b 만 물들였는데 모드 목록이 길어지면서
   '낱말' 만 반응하는 게 덩어리와 안 맞아 보였다 */
.mark:not(:disabled):hover,.mark:not(:disabled):hover b,
.mark:not(:disabled):focus-visible,.mark:not(:disabled):focus-visible b{color:var(--accent)}
.mark b{font-family:var(--f-jp);font-weight:700;color:var(--fg);letter-spacing:.06em;transition:color .15s ease}
.deckinfo{
  margin-left:auto;display:flex;align-items:center;gap:.9rem;
  font-size:.78rem;color:var(--fg-3);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;
}
.topacts{display:flex;gap:.4rem;flex-shrink:0}

/* ---------- 버튼 ---------- */
.btn{
  font:inherit;font-size:.79rem;line-height:1;
  color:var(--fg-2);background:transparent;
  border:1px solid var(--line);border-radius:var(--r);
  padding:.5rem .7rem;cursor:pointer;white-space:nowrap;
  transition:color .15s ease,border-color .15s ease,background-color .15s ease,transform .08s ease;
}
.btn:hover{color:var(--fg);border-color:var(--fg-3)}
.btn:active{transform:translateY(1px)}
.btn[aria-pressed="true"],.btn.on{color:var(--accent);border-color:var(--accent);background:var(--accent-sunk)}
/* 독의 상태 토글은 여러 개가 동시에 켜진다 — 넷이 액센트를 두르면 액센트가 뜻을 잃는다.
   설정의 선택 버튼은 그룹당 하나만 켜지므로 '이게 지금 값' 이라는 액센트 의미가 살아 있다. */
.readacts .btn.on,.drillacts .btn.on{color:var(--fg);border-color:var(--fg-3);background:transparent}
.btn:disabled{opacity:.4;cursor:not-allowed}
.btn-quiet{border-color:transparent}
.btn-quiet:hover{border-color:var(--line)}
.btn-icon{
  display:inline-flex;align-items:center;justify-content:center;
  width:2.6rem;height:2.6rem;padding:0;
}
.btn-icon svg{width:1.15rem;height:1.15rem;fill:currentColor}
.btn-play{width:3.2rem;height:3.2rem}
.btn-play svg{width:1.35rem;height:1.35rem}

/* ---------- 무대 / 카드 ---------- */
.stage{
  position:relative;
  display:flex;align-items:center;justify-content:center;
  padding:clamp(1rem,3vh,2.5rem) clamp(1rem,4vw,3.25rem);
  overflow:hidden;
  touch-action:pan-y;
}
/* 배율 하한까지 줄여도 안 들어가면 자르지 말고 스크롤한다. 글자를 읽을 수 없게 만드는 것보다 낫다 */
.stage[data-tall="1"]{overflow-y:auto;align-items:flex-start}
/* 블록은 가운데, 글줄은 왼쪽 정렬 - 카드가 바뀌어도 시선 기준선이 고정된다 */
.card{
  width:100%;max-width:min(76ch,100%);
  display:flex;flex-direction:column;
  gap:clamp(.7rem,1.6vh,1.4rem);
}
.card.enter{animation:cardin .34s cubic-bezier(.16,1,.3,1) both}
@keyframes cardin{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}

.meta{
  display:flex;align-items:center;gap:.75rem;flex-wrap:wrap;
  font-size:max(11px,calc(clamp(.68rem,.6vmin + .58rem,.95rem) * var(--scale)));
  color:var(--fg-3);letter-spacing:.06em;
}
.lv{
  border:1px solid var(--fg-3);border-radius:var(--r);
  padding:.15em .45em;font-weight:700;color:var(--fg-2);letter-spacing:.08em;
}
.pos{color:var(--fg-2)}
.seen{margin-left:auto;font-variant-numeric:tabular-nums}

.kana{
  font-family:var(--f-jp);font-weight:400;
  font-size:calc(clamp(1.05rem,2.1vmin + .5rem,2.15rem) * var(--scale));
  letter-spacing:.14em;color:var(--fg-2);line-height:1.3;
}
.word{
  font-family:var(--f-word);font-weight:600;
  font-size:calc(clamp(3rem,8.6vmin + 1.4rem,8.5rem) * var(--scale));
  line-height:1.08;letter-spacing:.01em;color:var(--fg);
  white-space:nowrap;cursor:pointer;
  padding-bottom:.06em;
  transition:color .15s ease;
}
.card:not(.is-drill) .word:hover{color:var(--accent)}
:root[data-fontfallback="1"] .word{font-weight:400;letter-spacing:.02em}

.read{
  display:flex;align-items:baseline;gap:1rem;flex-wrap:wrap;
  font-size:calc(clamp(1rem,1.5vmin + .5rem,1.75rem) * var(--scale));
  line-height:1.4;
}
.hangul{font-family:var(--f-kr);color:var(--fg-2)}
.hanja{color:var(--accent);font-family:var(--f-kr);font-weight:700}
/* 한자음이 한국어 뜻과 같은 단어: 한국인에게는 이미 아는 단어라는 신호 */
.hanja.is-same{
  background:var(--accent-sunk);border-radius:var(--r);
  padding:.1em .5em;margin-left:-.1em;
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--accent) 35%,transparent);
}
.hanja.is-same small::after{content:" = 한국어";letter-spacing:0}
.hanja small{
  font-weight:400;font-size:max(11px,.62em);letter-spacing:.1em;
  color:var(--fg-3);margin-right:.45em;
}
/* 한자별 한자음은 아래 .hjp i b / .hjp i span 에서 이미 한자=Klee, 한글=KR 로 나뉘어 있다.
   서브셋도 그 경계에 맞춰 글자를 담는다 (tools/font-charset.js). */
.hjp{
  display:flex;gap:.9rem;flex-wrap:wrap;
  font-size:max(11px,calc(clamp(.8rem,.9vmin + .48rem,1.15rem) * var(--scale)));
  color:var(--fg-3);
}
.hjp i{font-style:normal;display:inline-flex;align-items:baseline;gap:.18em}
.hjp i b{font-family:var(--f-word);font-weight:400;color:var(--fg-2);font-size:1.25em}
.hjp i span{font-family:var(--f-kr)}

.rule{border:0;border-top:1px solid var(--line);margin:clamp(.2rem,1vh,.6rem) 0}

.meanwrap{position:relative}
.means{
  display:flex;flex-direction:column;gap:.3em;
  font-family:var(--f-kr);font-weight:700;
  font-size:calc(clamp(1.35rem,2.9vmin + .5rem,3rem) * var(--scale));
  line-height:1.28;color:var(--fg);
}
.means li{display:flex;gap:.5em;align-items:baseline}
.means li em{
  font-style:normal;font-weight:400;color:var(--fg-3);
  font-size:max(11px,.5em);font-variant-numeric:tabular-nums;min-width:1.2em;
}
.meanwrap.masked .means{visibility:hidden}
/* 예문 번역과 영어 뜻에 답이 그대로 들어 있다. 뜻만 가리면 회상 장치가 헛돈다 */
:root[data-masked="1"] .ex-o,:root[data-masked="1"] .en{visibility:hidden}
.mask{
  display:none;position:absolute;inset:0;
  align-items:center;justify-content:flex-start;gap:.6rem;
  font:inherit;font-size:calc(clamp(.85rem,1vmin + .5rem,1.2rem) * var(--scale));
  color:var(--fg-3);background:var(--bg);
  border:0;border-bottom:1px dashed var(--line);
  padding:0;cursor:pointer;width:100%;
}
.meanwrap.masked .mask{display:flex}
.mask:hover{color:var(--fg-2);border-color:var(--fg-3)}

/* 클릭하면 예문을 읽는다. 커서만으로는 안 보이니 hover 에 왼쪽 띠를 세운다 */
.ex{
  display:flex;flex-direction:column;gap:.42em;
  cursor:pointer;border-radius:var(--r);
  margin-left:-.8em;padding-left:.8em;
  border-left:2px solid transparent;
  transition:border-color .18s ease;
}
.ex:hover{border-left-color:var(--accent)}
body.idle .ex{cursor:none}
.ex p{
  font-size:calc(clamp(.95rem,1.45vmin + .45rem,1.5rem) * var(--scale));
  line-height:1.6;
}
.ex-j{font-family:var(--f-jp);color:var(--fg)}
.ex-k{font-family:var(--f-jp);color:var(--fg-2)}
/* 예문 한글 발음: 어절 사이를 넉넉히 벌려 읽기 단위가 보이게 */
.ex-h{font-family:var(--f-kr);color:var(--fg-3);word-spacing:.35em}   /* opacity:.82 는 4.5:1 을 3.83 으로 깎았다 */
/* 발음 보조 묶음: 예문·뜻이 본문, かな와 한글 발음은 그 아래 딸린 발음 표기 */
.pron{
  display:flex;flex-direction:column;gap:.15em;
  margin-top:.3em;padding-left:.8em;
  border-left:1px solid var(--line);
}
.ex-k,.ex-h{
  font-size:calc(clamp(.72rem,.85vmin + .32rem,1rem) * var(--scale));
  line-height:1.5;color:var(--fg-3);
}
.ex-o{font-family:var(--f-kr);color:var(--fg-3)}

/* 한자 카드: 이 한자를 쓰는 단어들 */
.kex{display:flex;flex-direction:column;gap:.3em}
.kex-row{
  display:flex;align-items:baseline;gap:.55em;cursor:pointer;
  border-radius:var(--r);border-left:2px solid transparent;
  margin-left:-.8em;padding-left:.8em;
  transition:border-color .18s ease;
}
.kex-row:hover{border-left-color:var(--accent)}
body.idle .kex-row{cursor:none}
.kex-row b{font-family:var(--f-word);font-weight:400;color:var(--fg);
  font-size:calc(clamp(1rem,1.6vmin + .5rem,1.7rem) * var(--scale))}
.kex-k{font-family:var(--f-jp);color:var(--fg-2)}
.kex-h{font-family:var(--f-kr);color:var(--fg-3)}
.kex-k,.kex-h{font-size:max(11px,calc(clamp(.72rem,.85vmin + .32rem,1rem) * var(--scale)))}
.kex-row em{font-family:var(--f-kr);font-style:normal;color:var(--fg-3);margin-left:auto;
  font-size:max(11px,calc(clamp(.8rem,1vmin + .35rem,1.1rem) * var(--scale)))}
/* 훈독은 음독과 구별되게 — 음독이 한국 한자음과 이어지는 쪽이다 */
.hjp i.is-kun b{color:var(--fg-2)}   /* opacity 로 흐리게 하면 4.5:1 을 다시 깎는다 (실측 3.82:1) */
.hjp.k-read i:first-child::before,
.hjp.k-read i.kun-first::before{
  font-family:var(--f-kr);font-size:.8em;color:var(--fg-3);
  margin-right:.15em;
}
.hjp.k-read i:first-child::before{content:"음"}
.hjp.k-read i.kun-first::before{content:"훈"}
.en{
  font-size:calc(clamp(.8rem,.9vmin + .48rem,1.1rem) * var(--scale));
  color:var(--fg-3);letter-spacing:.01em;
}

/* 빈 상태 */
.empty{
  max-width:52ch;display:flex;flex-direction:column;gap:.9rem;
}
.empty h2{font-size:1.5rem;font-weight:700;letter-spacing:-.01em}
.empty p{color:var(--fg-2);line-height:1.7;font-size:.95rem}
.empty code{
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.85em;
  background:var(--bg-sunk);border:1px solid var(--line);border-radius:var(--r);padding:.1em .35em;
}
.empty .btn{align-self:flex-start}

/* ---------- 하단 도크 ---------- */
.dock{border-top:1px solid var(--line-soft)}
.progress{height:2px;background:var(--line-soft);overflow:hidden}
.progress span{
  display:block;height:100%;background:var(--accent);
  transform-origin:left center;transform:scaleX(0);
  will-change:transform;
}
.dockrow{
  display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:1rem;
  padding:.6rem clamp(1rem,4vw,3.25rem) .9rem;
}
.transport{display:flex;align-items:center;gap:.35rem;justify-self:start}
.counter{
  justify-self:center;font-size:.82rem;color:var(--fg-3);
  font-variant-numeric:tabular-nums;letter-spacing:.04em;white-space:nowrap;
}
.counter b{color:var(--fg-2);font-weight:400}
/* 기사 번호는 읽는 자리에서 그대로 고쳐 뛴다 */
.counter .jump{display:inline-flex;align-items:center;gap:.1rem}
.counter .jump input{
  width:2.4em;padding:.1rem .1rem;text-align:center;
  font:inherit;font-variant-numeric:tabular-nums;
  color:var(--fg);background:var(--bg-elev);
  border:1px solid var(--line);border-radius:var(--r);
  /* 스피너는 두 자리 숫자를 덮어 뭉갠다. ↑↓ 키는 스피너 없이도 그대로 듣는다 */
  appearance:textfield;-moz-appearance:textfield;
}
.counter .jump input::-webkit-outer-spin-button,
.counter .jump input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.counter .jump input:focus{outline:0;border-color:var(--accent)}
.dockacts{display:flex;align-items:center;gap:.4rem;justify-self:end;flex-wrap:wrap;justify-content:flex-end}

/* ---------- 소리가 나는 동안 ----------
   이 앱에서 가장 자주 하는 동작인데 관측 가능한 변화가 하나도 없었다. 재생 중이면
   발음 버튼과 스피커가 액센트로 물든다. 애니메이션이 아니라 상태다 — 한 번만 바뀐다. */
:root[data-speaking="1"] #btnSpeak,
:root[data-speaking="1"] #btnMute:not(.on){color:var(--accent);border-color:var(--accent)}

/* ---------- 음량 팝오버 ---------- */
.volwrap{position:relative;display:inline-flex}
.volpop{
  position:absolute;bottom:calc(100% + 7px);left:50%;
  transform:translateX(-50%) scale(.96);
  transform-origin:bottom center;       /* 버튼에서 자라 나오게 */
  /* 음량과 속도 두 줄. 라벨 / 슬라이더 / 값 세 칸을 맞춘다 */
  display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:.32rem .55rem;
  padding:.55rem .7rem;white-space:nowrap;
  background:var(--bg-elev);border:1px solid var(--line);border-radius:var(--r);
  opacity:0;pointer-events:none;
  transition:opacity .16s ease,transform .16s cubic-bezier(.2,.8,.2,1);
}
/* 꼬리. 테두리색 삼각형 위에 배경색 삼각형을 1px 겹쳐 테두리 있는 말풍선처럼 보이게 한다 */
.volpop::before,.volpop::after{
  content:'';position:absolute;left:50%;transform:translateX(-50%);
  width:0;height:0;border-style:solid;
  border-left-color:transparent;border-right-color:transparent;
}
/* border-width 를 한 줄로 다 적는다 — 따로 적으면 뒤에 오는 값이 아래쪽 0 을 덮어쓰고
   그 테두리가 currentColor 로 칠해져 반대 방향 삼각형이 나온다 */
.volpop::before{top:100%;border-width:7px 7px 0;border-top-color:var(--line)}
.volpop::after{top:calc(100% - 1px);border-width:6px 6px 0;border-top-color:var(--bg-elev)}
.volpop input[type=range]{width:7rem}
.vol-lb{font-size:.72rem;color:var(--fg-3);letter-spacing:.02em}
.volnum{
  font-size:.74rem;color:var(--fg-2);
  font-variant-numeric:tabular-nums;min-width:2.6em;text-align:right;
}
/* 포인터가 있는 기기에서만 펼친다. 터치는 hover 가 없고 탭은 음소거라 자리가 겹친다 —
   모바일은 시스템 음량이 있고 설정 패널에도 같은 슬라이더가 있다 */
@media (hover:hover){
  .volwrap:hover .volpop,.volwrap:focus-within .volpop{
    opacity:1;pointer-events:auto;transform:translateX(-50%) scale(1);
  }
  /* 버튼과 팝오버 사이 7px 을 덮는다 — 안 덮으면 마우스가 건너가는 동안 hover 가 끊겨 닫힌다.
     hover 중에만 존재해서 평소에는 위쪽 클릭을 가로막지 않는다 */
  .volwrap:hover::after,.volwrap:focus-within::after{
    content:'';position:absolute;left:0;right:0;bottom:100%;height:9px;
  }
  /* 팝오버에 마우스가 올라가 있어도 버튼이 켜진 것처럼 보이게 — 둘이 한 덩어리로 읽힌다 */
  .volwrap:hover .btn,.volwrap:focus-within .btn{color:var(--fg);border-color:var(--line)}
}
/* 독 버튼 안의 아이콘. .lbl 이 숨는 좁은 화면에서도 버튼이 비지 않게 하는 역할이다 */
.btn .ic{width:1.05rem;height:1.05rem;fill:currentColor;flex-shrink:0}
.btn:has(.ic){display:inline-flex;align-items:center;gap:.35rem}
.star{width:1.05rem;height:1.05rem}

/* ---------- 설정 패널 ---------- */
.panel{
  position:fixed;top:0;right:0;bottom:0;width:min(24rem,100vw);
  background:var(--bg-elev);border-left:1px solid var(--line);
  display:flex;flex-direction:column;
  transform:translateX(101%);
  /* visibility 로 탭 순서에서 뺀다 — transform 만으로 닫으면 화면 밖 컨트롤 59개가 탭 가능하고
     스크린 리더가 71개를 전부 읽는다. 지연을 줘서 미끄러져 나가는 동안은 보이게 둔다. */
  visibility:hidden;
  transition:transform .3s cubic-bezier(.16,1,.3,1),visibility 0s .3s;
  z-index:20;
}
.panel[data-open="1"]{transform:none;visibility:visible;transition:transform .3s cubic-bezier(.16,1,.3,1),visibility 0s 0s}
.panel header{
  display:flex;align-items:center;justify-content:space-between;gap:1rem;
  padding:1rem 1.25rem;border-bottom:1px solid var(--line);flex-shrink:0;
}
.panel header h2{font-size:.95rem;font-weight:700;letter-spacing:.01em}
.panel .body{overflow-y:auto;padding:.5rem 1.25rem 2.5rem;flex:1}
.grp{padding:1.15rem 0;border-bottom:1px solid var(--line-soft)}
.grp:last-child{border-bottom:0}
.grp>h3{
  font-size:.7rem;font-weight:700;letter-spacing:.14em;
  color:var(--fg-3);margin-bottom:.85rem;
}
.row{display:flex;align-items:center;justify-content:space-between;gap:1rem;margin-bottom:.7rem}
.row:last-child{margin-bottom:0}
.row label,.row .lbl{font-size:.85rem;color:var(--fg);flex:1}
.row .val{font-size:.8rem;color:var(--fg-2);font-variant-numeric:tabular-nums;min-width:3.2rem;text-align:right}
.hint{font-size:.75rem;color:var(--fg-3);line-height:1.6;margin-top:.5rem;word-break:keep-all;white-space:pre-line}
.chips{display:flex;flex-wrap:wrap;gap:.4rem}

/* 스위치 */
.sw{
  position:relative;width:2.5rem;height:1.4rem;flex-shrink:0;
  background:var(--bg-sunk);border:1px solid var(--line);border-radius:999px;
  cursor:pointer;padding:0;transition:background-color .18s ease,border-color .18s ease;
}
.sw::after{
  content:"";position:absolute;top:.15rem;left:.15rem;
  width:.95rem;height:.95rem;border-radius:50%;background:var(--fg-3);
  transition:transform .18s cubic-bezier(.16,1,.3,1),background-color .18s ease;
}
.sw[aria-checked="true"]{background:var(--accent-sunk);border-color:var(--accent)}
.sw[aria-checked="true"]::after{transform:translateX(1.08rem);background:var(--accent)}
.sw:disabled{opacity:.4;cursor:not-allowed}

/* 레인지 / 셀렉트 */
input[type=range]{
  -webkit-appearance:none;appearance:none;width:100%;height:1.2rem;background:transparent;cursor:pointer;margin:0;
}
input[type=range]::-webkit-slider-runnable-track{height:2px;background:var(--line);border-radius:999px}
input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none;appearance:none;width:.9rem;height:.9rem;margin-top:-.4rem;
  border-radius:50%;background:var(--accent);border:0;
}
input[type=range]::-moz-range-track{height:2px;background:var(--line)}
input[type=range]::-moz-range-thumb{width:.9rem;height:.9rem;border:0;border-radius:50%;background:var(--accent)}
input[type=range]:disabled{opacity:.4;cursor:not-allowed}
.slider{display:flex;flex-direction:column;gap:.25rem;margin-bottom:1rem}
.slider .head{display:flex;align-items:center;justify-content:space-between}
.slider .head span:first-child{font-size:.85rem;color:var(--fg)}
.slider .head span:last-child{font-size:.8rem;color:var(--fg-2);font-variant-numeric:tabular-nums}

select{
  font:inherit;font-size:.82rem;color:var(--fg);background:var(--bg-sunk);
  border:1px solid var(--line);border-radius:var(--r);padding:.42rem .55rem;
  max-width:60%;cursor:pointer;
}
select:disabled{opacity:.4;cursor:not-allowed}

/* ---------- 도움말 ---------- */
.overlay{
  position:fixed;inset:0;z-index:30;
  display:none;align-items:center;justify-content:center;padding:1.5rem;
  background:color-mix(in srgb,var(--bg) 88%,transparent);
}
.overlay[data-open="1"]{display:flex}
.sheet{
  width:min(30rem,100%);max-height:80vh;overflow-y:auto;
  background:var(--bg-elev);border:1px solid var(--line);border-radius:var(--r);
  padding:1.5rem;
}
.sheet h2{font-size:1rem;font-weight:700;margin-bottom:1.1rem}
.keys{display:flex;flex-direction:column;gap:.6rem}
.keys div{display:flex;align-items:center;gap:.9rem;font-size:.85rem;color:var(--fg-2)}
.keys div kbd{min-width:3.6rem;text-align:center}
.sheet .btn{margin-top:1.4rem}

/* ---------- 전체화면 ---------- */
/* 전체화면은 크게 시작하고, app.js 의 fit() 이 화면 높이에 맞춰 필요한 만큼만 줄인다 */
:root[data-fs="1"]{--scale:1.5}
:root[data-fs="1"] .stage{padding-block:clamp(1.5rem,6vh,5rem)}
:root[data-fs="1"] .card{max-width:min(94ch,100%);gap:clamp(.9rem,2vh,2rem)}
:root[data-fs="1"] .progress{height:3px}

/* ---------- 모바일 ---------- */
@media (max-width:640px){
  .topbar{padding:.7rem 1rem;gap:.6rem}
  .deckinfo{display:none}
  /* safe center: 들어가면 가운데, 넘치면 위에서부터 잘리지 않게. flex-start 로 두면
     정리 화면처럼 짧은 내용이 위에 붙고 아래 절반이 죽는다 */
  .stage{padding:1rem;align-items:safe center;overflow-y:auto}
  .card{gap:.6rem}
  .dockrow{grid-template-columns:1fr;justify-items:center;gap:.6rem;padding:.6rem 1rem .8rem}
  .transport,.dockacts{justify-self:center}
  .panel{width:100vw;top:auto;height:min(86vh,100dvh);border-left:0;border-top:1px solid var(--line);
    transform:translateY(101%)}
  .panel[data-open="1"]{transform:none}
  :root[data-fs="1"]{--scale:1}
}
@media (max-width:400px){
  .dockacts .btn span.lbl{display:none}
}



/* ---------- 키보드가 올라온 타자 화면 ----------
   안드로이드 키보드는 세로를 절반으로 가져간다. 1000x310 에서 상단 바 70 + 독 67 =
   137px, 화면의 44% 가 크롬이었고 글자는 59px 까지 쪼그라들었다.
   타자 중에 눈이 가는 건 글자와 입력칸뿐이라 나머지를 접어 그 높이를 글자에 준다. */
@media (max-height:560px){
  :root[data-drill="1"] .topbar{padding:.25rem clamp(1rem,4vw,3.25rem);min-height:0;gap:.6rem}
  :root[data-drill="1"] .topbar .btn{font-size:.72rem;padding:.3rem .45rem}
  :root[data-drill="1"] .dockrow{padding:.3rem clamp(1rem,4vw,3.25rem) .35rem}
  :root[data-drill="1"] .drillacts .btn,
  :root[data-drill="1"] .dockacts .btn{font-size:.72rem;padding:.32rem .45rem}
  /* 메타줄과 안내줄은 키보드를 내리면 다시 보인다. 안내줄은 첫 글자 이후로는 읽지 않는다 */
  :root[data-drill="1"] .card.is-drill .meta,
  :root[data-drill="1"] .drill-tip{display:none}
  /* 스테이지 여백 2rem 은 이 화면에서 글자 한 줄값이다 */
  :root[data-drill="1"] .stage{padding:.35rem .75rem}
  :root[data-drill="1"] .card.is-drill{gap:.3rem}
  :root[data-drill="1"] .drill{gap:.2rem}
  /* 입력칸과 정답줄을 --scale 에서 뗀다. --scale 은 카드 전체를 줄이는 값이라,
     글자가 화면을 넘으면 fit() 이 정답줄까지 같이 줄여 버린다 — 그래서 정답이 10px 로 찍혔다.
     읽어야 하는 건 정답 쪽이다. 이렇게 두면 fit() 이 큰 글자만 줄이고 정답은 크기를 지킨다. */
  :root[data-drill="1"] .drill-in{font-size:1.25rem}
  :root[data-drill="1"] .drill-ans{font-size:1rem}
  :root[data-drill="1"] .card.is-drill .word{font-size:calc(min(62vh,46vw) * var(--scale))}
}

/* ---------- 모션 축소 ---------- */
/* 움직임 축소: 장식은 끄고 상태 변화는 남긴다. 전역으로 전환을 0 으로 죽이면
   스위치·팝오버·토스트가 '바뀌었다'는 신호를 잃어 무엇이 켜졌는지 알 수 없다. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important}
  .card.enter{animation:none}
  /* 위치가 움직이는 것만 끈다 */
  .volpop,.toast,.panel{transition-property:opacity !important}
  .btn:active{transform:none}
}
.alt{font-size:.72em;color:var(--fg-3);font-family:var(--f-jp);letter-spacing:.02em}

/* ---------- 채점 (배치·복습 모드에서만 노출) ---------- */
.grades{display:flex;gap:.35rem;flex-wrap:wrap;justify-content:flex-end}
.grades .btn{font-size:.74rem;padding:.42rem .55rem;display:inline-flex;align-items:center;gap:.35rem}
.grades .btn:hover{border-color:var(--accent);color:var(--accent)}

/* ---------- 토스트 ---------- */
.toast{
  position:absolute;left:50%;bottom:1.2rem;transform:translateX(-50%) translateY(6px);
  font-size:.8rem;color:var(--fg-2);background:var(--bg-elev);
  border:1px solid var(--line);border-radius:var(--r);
  padding:.45rem .8rem;pointer-events:none;opacity:0;
  transition:opacity .2s ease,transform .2s ease;
}
.toast[data-on="1"]{opacity:1;transform:translateX(-50%) translateY(0)}
@media (prefers-reduced-motion:reduce){.toast{transition:none}}

/* ============================================================
   시작 화면 (학습 선택)
   ============================================================ */
.home{width:100%;max-width:min(58ch,100%);display:flex;flex-direction:column;gap:clamp(1rem,3vh,2rem)}
.home-h{
  font-family:var(--f-kr);font-weight:700;letter-spacing:-.01em;
  font-size:calc(clamp(1.35rem,2.2vmin + .8rem,2.1rem) * var(--scale));
  color:var(--fg);line-height:1.25;
}
.pickrows{display:flex;flex-direction:column;border-top:1px solid var(--line)}
/* 카드 상자 대신 헤어라인. hover 는 예문 줄과 같은 왼쪽 액센트 띠 idiom */
.pickrow{
  display:flex;align-items:center;gap:clamp(.9rem,2vw,1.5rem);
  width:100%;text-align:left;font:inherit;cursor:pointer;
  background:transparent;border:0;border-bottom:1px solid var(--line);
  border-left:2px solid transparent;border-radius:var(--r);
  padding:clamp(.85rem,1.8vh,1.35rem) clamp(.5rem,1.5vw,1rem);
  transition:border-color .18s ease,background-color .18s ease;
}
.pickrow:hover,.pickrow:focus-visible{border-left-color:var(--accent);background:var(--bg-elev)}
.pickrow[aria-disabled="true"]{opacity:.4;cursor:not-allowed}
.pickrow[aria-disabled="true"]:hover{border-left-color:transparent;background:transparent}
.pick-spec{
  font-family:var(--f-word);font-weight:400;color:var(--fg-2);
  font-size:calc(clamp(1.7rem,3.4vmin + .6rem,2.9rem) * var(--scale));
  line-height:1;flex-shrink:0;min-width:2.2em;
  transition:color .18s ease;
}
.pick-spec-kana{font-family:var(--f-kana);letter-spacing:.02em}
.pickrow:hover .pick-spec{color:var(--accent)}
.pick-txt{display:flex;flex-direction:column;gap:.25rem;min-width:0;flex:1}
.pick-txt b{
  font-family:var(--f-kr);font-weight:700;color:var(--fg);
  font-size:calc(clamp(1rem,1.2vmin + .6rem,1.3rem) * var(--scale));letter-spacing:.01em;
}
.pickrow[data-go="kana"] .pick-txt b{font-family:var(--f-jp)}
.pick-txt em{
  font-style:normal;color:var(--fg-3);line-height:1.5;word-break:keep-all;
  font-size:calc(clamp(.75rem,.6vmin + .5rem,.92rem) * var(--scale));
}
.pick-n{
  color:var(--fg-3);font-variant-numeric:tabular-nums;white-space:nowrap;flex-shrink:0;
  font-size:calc(clamp(.72rem,.5vmin + .5rem,.88rem) * var(--scale));
}
.home-foot{font-size:.78rem;color:var(--fg-3);line-height:1.6}

/* 덱을 불러오는 동안. 모드마다 처음 한 번만 지나가는 상태다 —
   덱은 부트가 아니라 모드에 들어갈 때 온다. */
:root[data-busy="1"] .pickrow{opacity:.45;pointer-events:none}
:root[data-busy="1"] .home-foot::after{
  content:"덱을 불러오는 중…";display:block;color:var(--accent);
}

/* ============================================================
   かな 타자 연습
   ============================================================ */
/* 한 글자만 크게 놓고 그 아래 답을 치는 화면이라 이 카드만 가운데 정렬이다.
   다른 카드는 줄이 여러 개라 왼쪽 기준선이 있어야 시선이 고정되지만, 여기는 기준선이 글자 하나다. */
.card.is-drill{max-width:min(26rem,100%);align-items:center;text-align:center}
.card.is-drill{gap:clamp(.4rem,1.1vh,.9rem)}
.card.is-drill .meta,.card.is-drill .rule,.card.is-drill .drill{width:100%}
.card.is-drill .meta{justify-content:center}
.card.is-drill .seen{margin-left:0}
/* 이 글자를 제한하는 건 세로 공간이다 — 입력칸과 안내줄이 같이 들어가야 한다.
   14vmax 는 가로를 재던 값이라 폴더블 가로 모드에서는 남는 높이를 못 쓰고,
   키보드가 올라와 화면이 반으로 줄어도 그대로였다. 46vw 는 좁은 화면에서 글자가 카드를 넘지 않게 잡는다. */
.card.is-drill .word{
  width:100%;cursor:default;
  font-size:calc(clamp(4.5rem,min(38vh,46vw),18rem) * var(--scale));
}
.drill{display:flex;flex-direction:column;align-items:center;gap:.4rem}
.drill-in{
  font:inherit;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:calc(clamp(1.5rem,2.6vmin + .7rem,2.4rem) * var(--scale));
  width:6ch;padding:.1em .15em;letter-spacing:.06em;
  color:var(--fg);background:transparent;caret-color:var(--accent);
  border:0;border-bottom:2px solid var(--fg-3);border-radius:0;
  transition:border-color .18s ease,color .18s ease;
}
.drill-in::placeholder{color:var(--fg-3);opacity:.45}
.drill-in:focus{outline:0;border-bottom-color:var(--fg-2)}
.drill-tip{font-size:.8rem;color:var(--fg-3);line-height:1.5}
/* 정답 줄은 항상 자리를 차지한다. visibility 토글이라 글자가 밀리지 않는다 —
   다만 비어 있는 flex 상자는 높이가 0 이라 min-height 로 한 줄을 잡아 둬야 그 말이 참이 된다.
   안 잡으면 fit() 이 이 줄 없이 카드를 맞춰 놓고, 정답이 뜨는 순간 카드가 커져서 독에 잘린다. */
.drill-ans{
  display:flex;align-items:baseline;justify-content:center;gap:.8rem;visibility:hidden;
  min-height:1.4em;
  font-size:calc(clamp(1rem,1.5vmin + .5rem,1.7rem) * var(--scale));
}
.drill-ans.on{visibility:visible}
.drill-ans b{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:400;color:var(--accent);letter-spacing:.04em}
.drill-ans span{font-family:var(--f-kr);color:var(--fg-2)}
.drill-ans em{font-style:normal;font-size:.6em;color:var(--fg-3);font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.word.is-kana{font-family:var(--f-kana);font-weight:400;white-space:nowrap}

/* 채점 피드백. 색 하나로 맞고 틀림을 다 표현할 수 없으니 움직임으로 나눈다 —
   틀리면 흔들리고, 맞으면 커진다. 색맹·모션 축소 환경에서도 정답 줄이 남는다. */
.card.ng .word{color:var(--accent);animation:shake .32s cubic-bezier(.36,.07,.19,.97) both}
.card.ng .drill-in{border-bottom-color:var(--accent);color:var(--accent)}
.card.ok .word{animation:pop .26s cubic-bezier(.16,1,.3,1) both}
@keyframes shake{
  10%,90%{transform:translateX(-2px)}
  20%,80%{transform:translateX(4px)}
  30%,50%,70%{transform:translateX(-7px)}
  40%,60%{transform:translateX(7px)}
}
@keyframes pop{from{transform:scale(1)}55%{transform:scale(1.07)}to{transform:scale(1)}}

.dockrow.is-drill .drillacts{grid-column:1;grid-row:1;justify-self:start}
.dockrow.is-drill .counter{grid-column:2;grid-row:1}
.dockrow.is-drill .dockacts{grid-column:3;grid-row:1}
.drillacts{display:flex;gap:.35rem;flex-wrap:wrap}
.drillacts .btn{font-size:.74rem;padding:.42rem .55rem;display:inline-flex;align-items:center;gap:.35rem}

/* ============================================================
   글자 고르기 시트 (열 단위 선택)
   ============================================================ */
.overlay-wide{padding:clamp(.6rem,3vh,2rem)}
.sheet-wide{
  width:min(48rem,100%);max-height:min(88vh,100dvh);
  display:flex;flex-direction:column;padding:0;overflow:hidden;
}
.sheet-head,.sheet-foot{
  display:flex;align-items:center;justify-content:space-between;gap:1rem;
  padding:1rem 1.25rem;flex-shrink:0;
}
.sheet-head{border-bottom:1px solid var(--line)}
.sheet-foot{border-top:1px solid var(--line)}
.sheet-head h2{font-size:.95rem;font-weight:700;margin:0}
.sheet-foot .hint{margin:0;flex:1}
.sheet-body{overflow-y:auto;padding:.25rem 1.25rem 1.25rem;flex:1}
.btn-go{color:var(--accent);border-color:var(--accent);background:var(--accent-sunk);padding:.6rem 1rem}
.btn-go:hover{color:var(--fg);border-color:var(--accent)}

.kgrp{padding:1.1rem 0;border-bottom:1px solid var(--line-soft)}
.kgrp:last-child{border-bottom:0}
.kgrp-head{display:flex;align-items:baseline;gap:.7rem;margin-bottom:.6rem}
.kgrp-head h3{font-size:.8rem;font-weight:700;letter-spacing:.02em;color:var(--fg)}
.kgrp-head .kgrp-n{font-size:.72rem;color:var(--fg-3);font-variant-numeric:tabular-nums}
.kgrp-head .btn{margin-left:auto;font-size:.7rem;padding:.3rem .5rem}
/* 표는 열 하나가 버튼이다. 모바일에서는 가로로 스크롤한다 (realkana 도 같은 구조) */
.ktable{
  display:flex;overflow-x:auto;overscroll-behavior-x:contain;
  border-left:1px solid var(--line);
  -webkit-overflow-scrolling:touch;
}
.kcol{
  display:flex;flex-direction:column;flex-shrink:0;
  border:0;border-right:1px solid var(--line);
  background:transparent;padding:0;cursor:pointer;font:inherit;
  transition:background-color .15s ease;
}
.kcol:hover{background:var(--bg-sunk)}
.kcol.on{background:var(--accent-sunk);border-right-color:color-mix(in srgb,var(--accent) 26%,transparent)}
.kcol.on:hover{background:color-mix(in srgb,var(--accent) 22%,transparent)}
.kcell{
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.05em;
  width:3.05rem;height:2.8rem;
}
.kcell b{font-family:var(--f-jp);font-weight:400;font-size:1.02rem;line-height:1.1;color:var(--fg-3)}
.kcell i{font-style:normal;font-size:.7rem;line-height:1;color:var(--fg-3);letter-spacing:.01em}
.kcol.on .kcell b{color:var(--fg)}
.kcol.on .kcell i{color:var(--fg-2)}

/* ============================================================
   글꼴 고르기 타일
   ============================================================ */
.fonts{display:grid;grid-template-columns:repeat(3,1fr);gap:.4rem}
.ftile{
  display:flex;flex-direction:column;align-items:center;gap:.2rem;
  font:inherit;cursor:pointer;padding:.5rem .3rem;
  color:var(--fg-2);background:transparent;
  border:1px solid var(--line);border-radius:var(--r);
  transition:color .15s ease,border-color .15s ease,background-color .15s ease;
}
.ftile:hover{border-color:var(--fg-3)}
.ftile.on{color:var(--accent);border-color:var(--accent);background:var(--accent-sunk)}
.ftile b{font-weight:400;font-size:1.6rem;line-height:1.15;color:var(--fg)}
.ftile.on b{color:var(--accent)}
.ftile span{font-size:.7rem;line-height:1.25;color:var(--fg-3);text-align:center;letter-spacing:.01em}
.ftile.on span{color:var(--fg-2)}

/* 좁은 화면에서 상단 바가 넘치면 버튼이 화면 밖으로 밀린다. 라벨을 짧은 쪽으로 바꿔 끼운다 */
.narrow{display:none}
.keys-h{font-size:.8rem;font-weight:700;color:var(--fg-3);margin:1.4rem 0 .8rem;letter-spacing:.06em}

/* ---------- 모바일 ---------- */
@media (max-width:640px){
  .wide{display:none}
  .narrow{display:inline}
  .mark span{display:none}
  .topacts{gap:.2rem}
  .home{max-width:100%;gap:1rem}
  .pickrow{gap:.8rem;padding:.9rem .4rem}
  .pick-spec{min-width:1.9em}
  /* iOS 는 16px 미만 입력칸에 포커스하면 화면을 확대한다 */
  .drill-in{font-size:max(1.6rem,16px);width:5ch}
  .drill-tip{font-size:.74rem}
  /* 한 줄에 다 넣는다. 키보드가 올라오면 도크가 두 줄을 쓸 여유가 없다 */
  .dockrow.is-drill{grid-template-columns:auto 1fr auto;gap:.4rem}
  .dockrow.is-drill .drillacts{grid-column:1;justify-self:start}
  .dockrow.is-drill .counter{grid-column:2;justify-self:center}
  .dockrow.is-drill .dockacts{grid-column:3;justify-self:end}
  .dockrow.is-drill .drillacts kbd{display:none}
  .dockrow.is-drill .drillacts .btn{padding:.42rem .45rem}
  .sheet-wide{max-height:94dvh}
  .sheet-head,.sheet-foot{padding:.8rem 1rem}
  .sheet-body{padding:.25rem 1rem 1rem}
  .sheet-foot{flex-wrap:wrap}
  .sheet-foot .btn-go{flex:1}
  .kcell{width:2.6rem;height:2.5rem}
  .fonts{grid-template-columns:repeat(3,1fr)}
}

/* ============================================================
   한 바퀴 정리 (틀린 글자)
   ============================================================ */
/* ch 는 라틴 0 의 폭이라 한글·かな 를 담는 상자의 잣대로는 못 쓴다 (읽기 카드에서 같은 함정을 밟았다) */
.summary{
  width:100%;max-width:min(48rem,100%);
  display:flex;flex-direction:column;gap:clamp(.8rem,2.2vh,1.5rem);
}
.sum-h{
  font-family:var(--f-kr);font-weight:700;letter-spacing:-.01em;
  font-size:calc(clamp(1.3rem,2.1vmin + .8rem,2rem) * var(--scale));
  color:var(--fg);line-height:1.25;
}
.sum-line{font-size:.86rem;color:var(--fg-2);line-height:1.6;word-break:keep-all}
/* 바퀴 기록. 이번 바퀴 결과(.sum-line)보다 약하게 — 참고지 결과가 아니다 */
.sum-rec{font-size:.78rem;color:var(--fg-3);line-height:1.6;margin:.25rem 0 0;
  font-variant-numeric:tabular-nums;word-break:keep-all}
.sum-rec:empty{display:none}
/* 정답률은 따로 줄을 둔다 — 글자 수와 시도 수는 분모가 달라서 한 문장에 넣으면 같은 것으로 읽힌다 */
.sum-rate{font-size:.85rem;color:var(--fg-2);margin:.15rem 0 0;font-variant-numeric:tabular-nums}
/* 만점은 액센트로, 실패는 중립으로. 예전에는 실패만 화면을 받았다 */
.summary[data-perfect="1"] .sum-h{color:var(--accent)}
/* 글자 고르기 표와 같은 셀 어휘를 쓴다 — 같은 것을 보고 있다는 신호 */
.sum-grid{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(6.2rem,1fr));gap:.4rem;
  max-height:46vh;overflow-y:auto;
}
/* 브리프가 '카드 상자 없음(헤어라인 분리)' 라고 적어 뒀다. 같은 크기 상자 격자는
   그 선언을 어기고, 실수 목록을 성적표처럼 보이게 만든다. 헤어라인 줄로 바꾼다. */
.miss{
  display:flex;align-items:baseline;gap:.5rem;
  padding:.45rem .1rem;
  border-bottom:1px solid var(--line-soft);
}
.miss b{font-family:var(--f-jp);font-weight:400;font-size:1.5rem;line-height:1.1;color:var(--fg);min-width:1.6em}
.miss i{font-style:normal;font-size:.68rem;color:var(--fg-2);letter-spacing:.01em}
.miss em{font-style:normal;font-size:max(11px,.72rem);color:var(--fg-2);font-variant-numeric:tabular-nums;margin-left:auto}
.sum-acts{display:flex;gap:.5rem;flex-wrap:wrap;align-items:center}

@media (max-width:640px){
  .summary{max-width:100%}
  .sum-grid{grid-template-columns:repeat(auto-fill,minmax(5rem,1fr));max-height:52vh}
  .miss b{font-size:1.5rem}
  .sum-acts .btn{flex:1}
}

/* ============================================================
   읽기 (뉴스 기사)
   ============================================================ */
.art{display:flex;flex-direction:column;gap:clamp(.5rem,1.4vh,1rem)}
/* 한 문장씩 모드에서 지금 읽는 기사 제목을 위에 작게 남긴다 (.kana 자리를 빌려 쓴다) */
.card .kana:empty{display:none}
.art-body{display:flex;flex-direction:column}
.art-row{padding:.45em 0;border-bottom:1px solid var(--line-soft)}
.art-row:last-child{border-bottom:0}
.art-head{display:flex;align-items:flex-start;gap:.6rem}
/* 문장을 누르면 읽어 준다. 커서만으로는 안 보이니 예문 줄과 같은 왼쪽 띠 idiom */
.art-jp{
  flex:1;cursor:pointer;font-family:var(--f-jp);color:var(--fg);
  font-size:calc(clamp(.95rem,1.35vmin + .5rem,1.35rem) * var(--scale));
  line-height:2.1;                       /* 루비가 들어갈 자리 */
  margin-left:-.6em;padding-left:.6em;
  border-left:2px solid transparent;border-radius:var(--r);
  transition:border-color .18s ease;
}
.art-jp:hover{border-left-color:var(--accent)}
body.idle .art-jp{cursor:none}
.art-row.is-title .art-jp{
  font-weight:700;color:var(--fg);
  font-size:calc(clamp(1.15rem,1.9vmin + .6rem,1.8rem) * var(--scale));
  line-height:1.9;
}
.art-jp ruby{ruby-align:center}
.art-jp rt{
  font-family:var(--f-rt);color:var(--fg-2);
  font-size:var(--rt);line-height:1.1;letter-spacing:.02em;
  /* 작은 글자에서 획이 뭉개지지 않게. ば 와 ぱ 가 같아 보이면 루비가 제 일을 못 한다 */
  -webkit-font-smoothing:subpixel-antialiased;
  font-feature-settings:"palt" 0;
}
/* 후리가나를 끄면 줄 높이가 그대로여야 한다 — 글줄이 튀면 읽던 자리를 잃는다 */
:root[data-furi="0"] .art-jp rt{visibility:hidden}
.art-ko{
  font-family:var(--f-kr);color:var(--fg-2);
  font-size:calc(clamp(.82rem,1vmin + .45rem,1.05rem) * var(--scale));
  line-height:1.65;word-break:keep-all;
  padding:.15em 0 .2em .8em;margin-top:.1em;
  border-left:1px solid var(--line);
}
.eye{
  flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;
  width:1.9rem;height:1.9rem;padding:0;margin-top:.2em;
  color:var(--fg-3);background:transparent;
  border:1px solid transparent;border-radius:var(--r);cursor:pointer;
  transition:color .15s ease,border-color .15s ease,background-color .15s ease;
}
.eye svg{width:1.05rem;height:1.05rem;fill:currentColor}
.eye:hover{color:var(--fg-2);border-color:var(--line)}
/* 켜짐은 '밝아짐' 으로 낸다. 액센트는 한자음=한국어 배지가 쓰는 색이라
   한 화면에 눈 버튼 6개가 그걸 두르면 액센트가 아무것도 구별하지 못한다. */
.eye.on{color:var(--fg);border-color:var(--fg-3);background:transparent}
.art-src{
  display:flex;gap:.7rem;flex-wrap:wrap;align-items:baseline;
  font-size:.72rem;color:var(--fg-3);padding-top:.3em;
}
.art-src a{color:var(--fg-2);text-decoration:none;border-bottom:1px solid var(--line)}
.art-src a:hover{color:var(--accent);border-bottom-color:var(--accent)}
.readacts{display:flex;gap:.35rem;flex-wrap:wrap}
.readacts .btn{font-size:.74rem;padding:.42rem .55rem;display:inline-flex;align-items:center;gap:.35rem}
.dockrow.is-read{grid-template-columns:auto auto 1fr auto}
.dockrow.is-read .transport{grid-column:1;grid-row:1;justify-self:start}
.dockrow.is-read .readacts{grid-column:2;grid-row:1;justify-self:start}
.dockrow.is-read .counter{grid-column:3;grid-row:1;justify-self:center}
.dockrow.is-read .dockacts{grid-column:4;grid-row:1;justify-self:end}

@media (max-width:640px){
  .art-jp{line-height:2.2}
  .art-ko{padding-left:.6em}
  .dockrow.is-read{grid-template-columns:auto 1fr auto;gap:.4rem}
  .dockrow.is-read .transport{grid-column:1;grid-row:1;justify-self:start}
  .dockrow.is-read .readacts{grid-column:2;grid-row:1;justify-self:center}
  .dockrow.is-read .counter{grid-column:3;grid-row:1;justify-self:end}
  .dockrow.is-read .dockacts{grid-column:1 / -1;grid-row:2;justify-self:center}
  .dockrow.is-read .readacts kbd{display:none}
  /* 버튼이 넷이라 기본 여백으로는 줄을 넘긴다 — 라벨은 줄이지 않고 여백만 조인다 */
  .dockrow.is-read .readacts{gap:.22rem}
  .dockrow.is-read .readacts .btn{padding:.42rem .32rem}
  /* 좁은 화면에서는 '기사' 이름표를 뺀다 — 넣으면 읽기 버튼들이 두 줄로 밀린다 */
  .counter .jump-lb{display:none}
}

/* ---------- 읽기: 한 문장씩 크게 ----------
   기사 전체를 한 화면에 넣으면 문장이 작아지고 루비는 그 절반이라 濁点·半濁点이 안 보인다.
   한 문장만 띄우면 같은 화면에서 글자를 두 배 넘게 키울 수 있다. */
/* safe center: 내용이 스테이지보다 크면 가운데 정렬이 위쪽을 잘라 먹는다. safe 가 그걸 막는다 */
:root[data-readone="1"] .stage{overflow-y:auto;align-items:safe center}

/* 기사 카드는 넓게 쓴다. .card 의 76ch 는 라틴 '0' 폭 기준이라 16px 에서 약 610px 에 묶이는데,
   본문이 51px 인 한 문장씩 모드에서는 한 줄에 12자밖에 안 들어가 계속 접힌다.
   줄 길이는 ch 가 아니라 em 으로 잡는다 — 글자 크기를 바꾸면 같이 따라와야 하기 때문이다. */
.card.is-read{max-width:min(1500px,100%)}
.art-jp{max-width:42em}                        /* 전체 보기: 일본어 한 줄 42자 */
.art-body.one .art-row.is-now .art-jp{max-width:24em;flex:0 1 auto;min-width:0}
/* 카드가 넓어지면 flex:1 인 본문이 남는 폭을 다 먹어 눈 버튼만 멀리 떨어진다.
   fit-content + max-width:100% 라야 좁은 화면에서 본문이 줄어들고 버튼이 화면 밖으로 안 밀린다. */
.art-body.one .art-head{justify-content:flex-start;width:fit-content;max-width:100%}
.art-body.one .art-row.is-next .art-jp{max-width:52em}
.art-body.one{gap:0}
.art-body.one .art-row{border-bottom:0;padding:0}
.art-body.one .art-row.is-now{padding:.2em 0 .6em}
.art-body.one .art-row.is-now .art-jp{
  font-size:calc(clamp(1.6rem,4.4vmin + .95rem,3.2rem) * var(--scale));
  line-height:2;
}
.art-body.one .art-row.is-title.is-now .art-jp{
  font-size:calc(clamp(1.7rem,4.8vmin + 1rem,3.4rem) * var(--scale));
}
.art-body.one .art-row.is-now .art-ko{
  font-size:calc(clamp(.95rem,1.3vmin + .55rem,1.3rem) * var(--scale));
  padding-left:1em;
}
/* 다음에 올 문장. 읽는 자리를 잃지 않게 자리만 알려 주고 눈은 끌지 않는다 */
/* 다음 문장 미리보기는 색으로 물린다. opacity 로 흐리게 하면 이미 4.5:1 인 값을 배율로 깎아
   .4 에서 2.13:1, .22 에서 1.43:1 이 됐고 경계선은 1.04:1 이었다 — 읽으라고 띄운 글이 안 읽혔다.
   같은 위계를 --fg-3(4.52:1)으로 낸다. hover 로 --fg 까지 올린다. */
.art-body.one .art-row.is-next{border-top:1px solid var(--line-soft)}
.art-body.one .art-row.is-next .art-jp{color:var(--fg-3)}
.art-body.one .art-row.is-next rt{color:var(--fg-3)}
.art-body.one .art-row.is-next .art-jp{
  font-size:calc(clamp(.85rem,1vmin + .45rem,1.05rem) * var(--scale));
  line-height:1.9;
}
.art-body.one .art-row.is-next + .art-row.is-next .art-jp{font-size:.92em}   /* 두 번째 미리보기는 크기로 더 물린다 */
.art-body.one .art-row.is-next .art-ko{display:none}
.art-body.one .art-row.is-next:hover .art-jp,.art-body.one .art-row.is-next:hover rt{color:var(--fg)}
.art-body.one .eye{width:2.2rem;height:2.2rem}
.art-body.one .eye svg{width:1.25rem;height:1.25rem}

@media (max-width:640px){
  .art-body.one .art-row.is-now .art-jp{
    font-size:calc(clamp(1.5rem,5.6vmin + .8rem,2.4rem) * var(--scale));
    line-height:2.05;
  }
  .art-body.one .art-row.is-title.is-now .art-jp{
    font-size:calc(clamp(1.5rem,5.8vmin + .8rem,2.5rem) * var(--scale));
  }
  /* 좁은 화면에서는 다음 문장 하나만. 두 줄이면 지금 읽는 문장과 자리를 다툰다 */
  .art-body.one .art-row.is-next + .art-row.is-next{display:none}
  .art-body.one .art-row.is-next .art-jp{font-size:calc(clamp(.8rem,.9vmin + .42rem,.95rem) * var(--scale))}
}

/* ============================================================
   터치 타겟 (WCAG 2.5.5, 44px)
   포인터가 없는 기기에서만 넓힌다. 마우스에는 이 크기가 필요 없고, 독은 한 줄을 지켜야 한다.
   이 블록이 파일 끝에 있는 이유: .eye 같은 규칙이 뒤에 정의돼 있어서 앞에 두면 덮인다.
   ============================================================ */
@media (hover:none){
  /* 팝오버는 레이아웃에서 뺀다. opacity:0 으로만 숨기면 상자가 남아 좁은 화면에서
     뷰포트를 넘어가고 가로 스크롤이 생겼다(실측: 카나 화면 390px 에서 17px). */
  .volpop{display:none}

  /* 세로가 남는 자리는 실제로 키운다 */
  .panel .btn,.sheet-wide .btn,.sum-acts .btn{min-height:2.75rem}
  .eye,.art-body.one .eye{width:2.75rem;height:2.75rem}
  input[type=range]{height:2.75rem}          /* 트랙은 2px 이라 손가락이 닿을 자리가 없다 */
  #kanaIn{min-height:2.75rem}
  /* '낱말' 글자 폭이 24px 이라 여백으로 44 를 만든다. 음수 마진으로 원래 정렬을 지킨다 */
  .mark{padding:.78rem .65rem;margin-left:-.65rem}

  /* 스위치만 AA 미달이다(40x22, 기준 24px). 위아래 3px 씩 넓혀 28px 로 올린다.
     행 간격이 34px 이라 이웃과 겹치지 않는다.
     나머지 독 버튼(27~42px)은 AA 를 이미 통과한다. 예전에 이걸 전부 44px 로 넓혔더니
     줄이 넘칠 때 히트박스가 13px 겹쳐 옆 버튼이 탭을 가로챘다 — 라벨과 다른 동작이 실행됐다
     (실측: 390px 한 문장 모드에서 번역 아래를 누르면 elementFromPoint 가 btnOne). 44px 는 AAA 기준이고,
     그걸 맞추려고 옳은 동작을 잃는 건 거래가 안 된다. */
  .sw{position:relative}
  .sw::before{content:"";position:absolute;left:0;right:0;top:-3px;bottom:-3px}
  /* input 은 ::before 를 못 가진다. 오버레이는 입력을 막고, 44px 로 넓히면 카운터가 커져
     독이 두 줄로 밀린다. 31x36 은 AA(2.5.8, 24px)를 통과한다 — 44px 는 AAA(2.5.5) 기준이다. */
  #jumpIn{height:2.25rem}
}
