/* ==========================================================================
   system.css — hvsdcm 디자인 시스템 단일 원본 (DESIGN.md v2 / plan.md §2)
   모든 표면(랜딩·WordMaster·smstudy·admin)이 이 파일을 가장 먼저 링크한다.
   다크 전용. 커스텀 프로퍼티 *정의*는 이 파일에만 존재한다
   (유일한 예외: smstudy 인쇄 팔레트 — @media print 안 html 셀렉터).

   사이클4 재구축 요지
   - 색 팔레트는 유지한다. 순검정 캔버스 + 4단 표면 + 강조 1색이 이미 plan §2.2의
     목표값이고, 게이트(validate.mjs canonical)와 인쇄 팔레트가 이 이름들을 잠근다.
     대신 --surface-4를 신설해 컨트롤 hover/pressed의 하드코딩(#3a3a3c)을 흡수했다.
   - 타이포 스케일을 애플식으로 다시 잘랐다. 이전 스케일은 body(17) 바로 위가
     body-lg(18~22)라 단차가 겹쳤고 title-3(19)와 충돌했다. 이제 12·14·15·17·21·
     24·32·48·80의 사다리로 고정하고 그 사이를 비웠다.
   - 세로 리듬을 줄였다: --section-gap 96~180px → 64~112px, --space-9/10도 하향.
     반경도 한 단씩 내렸다(8/12/18/28 → 6/10/14/20) — 애플 시스템 UI의 곡률에 맞춘다.
   - 깊이는 명도 단차 + 1px 헤어라인이다(DESIGN.md §4). --shadow-*는 떠 있는
     오버레이(시트·토스트·세그먼티드 인디케이터) 전용으로 격하했고 값도 낮췄다.
     .card-hover에서 그림자를 제거하고 표면 한 단으로 대체했다.
   - 그룹 리스트(.list-group / .list-row) · 인셋(.inset)을 시스템 레벨에 신설했다.
     3b·3c가 이것을 소비한다 (용법: DESIGN.md §7).
   - v14(2026-09-04): 이모지 슬롯(.emoji / .emoji-box / .emoji-lg)과 --font-emoji를 폐기했다.
     아이콘은 assets/ui-icons.svg의 테두리형 한 세트(.ui-icon)뿐이고 크기는 17/16/20 3단이다
     (DESIGN.md §5). 아이콘 뒤에 강조색·상태색 면을 두지 않는다.

   ==========================================================================
   대비 전수표 — DESIGN.md §3.1 / LESSONS 규칙 1·2
   scripts로 실측(WCAG 상대휘도). 알파 배경은 부모 표면과 합성한 실효색 기준.
   본문 하한 4.5:1 / 큰 글자(≥24px 또는 ≥19px bold) 3:1.

   전경 \ 배경     bg      bg-alt  surface surface-2 surface-3 surface-4
   --text        19.29   18.06   16.61   15.46     12.80     10.42   전 배경 본문 가능
   --text-2       8.16    7.64    7.03    6.54      5.42      4.41   surface-4에서 4.41 → 캡션·비활성 라벨만
   --text-3       5.80    5.43    4.99    4.65      3.85      3.13   surface-3/4 금지 (캡션 하한 미달)
   --text-4       4.14    3.88    3.57    3.32      2.75      2.24   비활성 전용 — 살아있는 텍스트 금지
   --accent      19.29   18.06   16.61   15.46     12.80     10.42
   --green       14.47   13.55   12.46   11.60      9.60      7.82
   --red         16.69   15.63   14.38   13.38     11.08      9.02
   --red-bright  18.62   17.43   16.03   14.92     12.35     10.06
   --orange      12.45   11.66   10.72    9.98      8.26      6.73

   -soft 뱃지(알파 .14를 부모 배경과 합성한 실효 배경 위 전경):
   --accent-soft + --accent      bg 15.21 / bg-alt 13.47 / surface 11.77 / surface-2 10.73
                                 / surface-3 8.74 / surface-4 7.20
   --green-soft  + --green       bg 11.49 / bg-alt 10.22 / surface 8.97 / surface-2 8.20
                                 / surface-3 6.72 / surface-4 5.56
   --red-soft    + --red-bright  bg 14.43 / bg-alt 12.78 / surface 11.19 / surface-2 10.22
                                 / surface-3 8.36 / surface-4 6.92
   --orange-soft + --orange      bg 10.12 / bg-alt 9.03 / surface 7.95 / surface-2 7.28
                                 / surface-3 5.97 / surface-4 4.94
   → 단색 뱃지는 모든 시스템 표면에서 본문 대비 하한을 넘는다.

   위험 버튼(.btn-danger — 14px semibold라 본문 하한 4.5 적용). 면이 불투명이라
   부모 표면과 무관하게 값이 고정된다:
   --red-bright on --red-fill       = 13.74  (normal)
   --red-bright on --red-fill-hover = 11.66  (hover)
   → 이전 구현(--red 전경 + 알파 면)은 surface 위 hover 3.94, 표 행 hover 위 4.23이었다.
     :disabled는 opacity .45라 실효 대비가 더 내려가지만 WCAG 1.4.3의 비활성 컨트롤
     예외 대상이다(대비 하한을 적용하지 않는다).

   채움 버튼: #000 on --accent-strong #ffffff = 21.00 (사용)
              --text on --accent-strong       = 1.09 ✗ (금지 — 검정만)
              #fff on --accent #f5f5f7        = 1.09 ✗ (--accent 위 흰 글자 금지)
   채움 면 라벨(--fill-fg)은 면 밝기를 따라간다 — 팔레트별로 면이 뒤집히기 때문이다:
     :root            #000 on --accent-strong #ffffff = 21.00
                      #000 on --green #d6d6d8 = 14.47
                      #000 on --red #e5e5e7 = 16.69
     스터디 라이트     #fff on #1b64da = 5.41
                      #fff on #087f5b = 5.00
                      #fff on #b42336 = 6.49
     스터디 다크       #000 on #80b6ff = 10.05
                      #000 on #5dd6a0 = 11.59
                      #000 on #ff8b95 = 9.38
     인쇄              #fff on #0e5570 = 8.23
                      #fff on #187341 = 5.89
                      #fff on #a3231b = 7.46
   선택 영역: --text on (--accent .35 over bg)      = 6.76
              --text on (--accent .35 over surface) = 5.42
   헤어라인(장식 구획선, 그래픽 3:1 요건 대상 아님 — 컨트롤 경계로 쓰지 않는다):
     --line 1.27~1.46 / --line-strong 1.79~2.05 / --line-faint 1.10~1.20
     → 컨트롤의 유일한 시각 경계로 쓰지 말 것. 컨트롤은 표면 단차로 구분한다.

   이 표는 손으로 적지 않는다 — scripts/validate.mjs의 validateContrastTable()이 위
   숫자를 파싱해 :root 토큰에서 다시 계산한 값과 소수 둘째 자리까지 대조한다(R4-M-9).
   ========================================================================== */
:root {
  color-scheme: dark;
  /* ---- 색: 캔버스와 표면 (명도 단차 4단, DESIGN.md §4) ---- */
  --bg: #000;
  --bg-alt: #0b0b0d;
  --surface: #161617;
  --surface-2: #1d1d1f;
  --surface-3: #2c2c2e;
  --surface-4: #3a3a3c;
  /* 컨트롤 hover/pressed 전용. 라벨은 --text만(10.42:1) */
  /* ---- 색: 헤어라인 ---- */
  --line: rgba(255,255,255,.12);
  --line-strong: rgba(255,255,255,.22);
  --line-faint: rgba(255,255,255,.06);
  /* ---- 색: 텍스트 ---- */
  --text: #f5f5f7;
  --text-2: #a1a1a6;
  --text-3: #86868b;
  /* 캡션 전용. --surface-3 이상에서 금지(3.85:1) */
  --text-4: #6e6e73;
  /* 비활성 전용. 살아있는 텍스트에 쓰지 않는다 */
  /* ---- 색: 기능 (강조 1색 + 상태 3색) ---- */
  --accent: #f5f5f7;
  --accent-strong: #ffffff;
  /* 채움 버튼 배경. 라벨은 #fff 고정(4.70:1) */
  --fill-fg: #000;
  /* 강조·상태 **채움 면** 위 라벨 전용 잉크. 채움 면의 밝기가 팔레트마다 달라 값이 뒤집힌다 —
     채움 면이 밝으면 #000, 어두우면 #fff다(스터디 라이트만 #fff). 채움 면이 아닌 곳에 쓰지 않는다.
     :root #000 on --accent-strong #ffffff 21.00 / on --green #d6d6d8 14.47 / on --red #e5e5e7 16.69
     스터디 라이트 #fff on #1b64da 5.41 / on #087f5b 5.00 / on #b42336 6.49
     스터디 다크   #000 on #80b6ff 10.05 / on #5dd6a0 11.59 / on #ff8b95 9.38 */
  --accent-soft: rgba(255,255,255,.12);
  --green: #d6d6d8;
  --green-soft: rgba(214,214,216,.14);
  --red: #e5e5e7;
  --red-soft: rgba(229,229,231,.14);
  /* 위험 버튼 면. 알파가 아니라 불투명값이다 — 알파면 부모 표면(행 hover 등)에 따라
     대비가 흔들린다(실측: 표 행 hover 위에서 4.23까지 내려갔다). 전경 --red-bright 고정. */
  --red-fill: #242426;
  /* --red .14 over --surface를 구운 값. 5.49:1 */
  --red-fill-hover: #303033;
  /* 같은 방식 .22. 4.93:1 */
  --red-bright: #f1f1f3;
  /* -soft 배경 위 전경 전용 */
  --orange: #c7c7ca;
  --orange-soft: rgba(199,199,202,.14);
  /* ---- 그래픽: 셀렉트 셰브런 ----
     그라디언트로 삼각형을 그리는 것은 DESIGN.md §3의 금지 대상이고, 외부 아이콘 파일은
     정적 사이트의 자산 계약을 늘린다. 인라인 SVG data URI 하나를 단일 원본으로 둔다.
     data: 문서는 currentColor를 상속하지 못하므로 --text-2(#a1a1a6)를 구워 넣는다 —
     이 글리프를 쓰는 컨트롤의 글자색이 바뀌면 여기 색도 함께 바꾼다. */
  --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1.6 1.9 6 6.1 10.4 1.9' fill='none' stroke='%23a1a1a6' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  /* ---- 타이포 ----
     시스템 폰트가 먼저다. Pretendard는 비-애플 환경의 한글 폴백이며 새 웹폰트를
     추가하지 않는다(DESIGN.md §2). */
  --font-sans: -apple-system, BlinkMacSystemFont, "Pretendard Variable", Pretendard, "Apple SD Gothic Neo", system-ui, sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Code", Consolas, monospace;
  /* 사다리: 12 · 14 · 15 · 17 · 21 · 24 · 32 · 48 · 80 */
  --fs-display-xl: clamp(2.5rem, 1.6rem + 4.5vw, 5rem);
  /* 40 → 80 */
  --fs-display: clamp(1.875rem, 1.45rem + 2.1vw, 3rem);
  /* 30 → 48 */
  --fs-title-1: clamp(1.5rem, 1.35rem + .75vw, 2rem);
  /* 24 → 32 */
  --fs-title-2: 1.3125rem;
  /* 21 */
  --fs-title-3: 1.0625rem;
  /* 17 (semibold로 구분) */
  --fs-body-lg: clamp(1.0625rem, 1rem + .3vw, 1.3125rem);
  /* 17 → 21 */
  --fs-body: 1.0625rem;
  /* 17 */
  --fs-body-sm: .9375rem;
  /* 15 — 리스트 행 부제 */
  --fs-caption: .875rem;
  /* 14 */
  --fs-micro: .75rem;
  /* 12 */
  /* 한글 행간 하한: Apple SD Gothic Neo·Pretendard는 ascent+descent가 ~1.15em이라
     lh 1.05~1.06(애플 영문 디스플레이 값)에서는 두 줄짜리 한글 헤드라인의 잉크가
     맞닿는다. 디스플레이도 1.14 밑으로 내리지 않는다. */
  --lh-display: 1.14;
  --lh-title: 1.26;
  --lh-snug: 1.35;
  --lh-body: 1.5;
  --ls-display: -.022em;
  --ls-title: -.012em;
  --ls-body: 0;
  /* 본문에 음수 자간 금지 — 한글 획이 붙는다 */
  --ls-caps: .06em;
  /* ---- 간격 (세로 리듬 압축) ---- */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 44px;
  --space-8: 60px;
  --space-9: 80px;
  --space-10: 112px;
  --section-gap: clamp(64px, 3.5rem + 3.4vw, 112px);
  /* ---- 반경 ---- */
  --radius-xs: 5px;
  --radius-s: 8px;
  --radius-m: 12px;
  --radius-l: 18px;
  --radius-xl: 26px;
  --radius-full: 980px;
  /* ---- 그림자 — 떠 있는 오버레이 전용 (DESIGN.md §4) ---- */
  --shadow-1: 0 1px 2px rgba(0,0,0,.5);
  /* 세그먼티드 인디케이터 */
  --shadow-2: 0 8px 24px rgba(0,0,0,.5);
  /* 토스트·드로어 */
  --shadow-3: 0 24px 64px rgba(0,0,0,.66);
  /* 모달 시트 */
  /* ---- 모션 ---- */
  --ease-out: cubic-bezier(.25,.1,.25,1);
  --ease-swift: cubic-bezier(.32,.72,0,1);
  --dur-fast: .16s;
  --dur-med: .28s;
  --dur-slow: .6s;
  /* ---- 레이아웃 ---- */
  --container: 1000px;
  --container-wide: 1240px;
  --container-text: 680px;
  --topbar-h: 64px;
  --focus-ring: 0 0 0 4px rgba(41,151,255,.35);
  --study-shadow: 0 18px 50px rgba(0,0,0,.12);
  --study-shadow-soft: 0 8px 28px rgba(0,0,0,.08);
  /* ---- 그룹 리스트 기하 ---- */
  --row-min-h: 44px;
  /* 터치 타깃 하한 (WCAG 2.5.5) */
  --row-pad-x: var(--space-4);
  --row-pad-y: 10px;
  --list-inset: 45px;
  /* 리드(17px 아이콘)가 있는 행의 구분선 좌측 인셋 = row-pad-x 16 + 아이콘 17 + gap 12 */
}
/* Study-only palettes. The landing page never receives the study-app class. */
html.study-app[data-theme="light"] {
  color-scheme: light;
  --bg: #f2f4f6;
  --bg-alt: #f7f8fa;
  --surface: #fff;
  --surface-2: #f9fafb;
  --surface-3: #eef1f4;
  --surface-4: #e5e8eb;
  --line: rgba(0,27,55,.09);
  --line-strong: rgba(0,27,55,.25);
  --line-faint: rgba(0,27,55,.06);
  --text: #191f28;
  --text-2: #4e5968;
  --text-3: #596779;
  --text-4: #8b95a1;
  --accent: #1b64da;
  --accent-strong: #1b64da;
  --fill-fg: #fff;
  --accent-soft: #e8f1ff;
  --green: #087f5b;
  --green-soft: #e8f7ef;
  --red: #b42336;
  --red-bright: #b42336;
  --red-soft: #fff0f1;
  --red-fill: #fff0f1;
  --red-fill-hover: #ffe4e6;
  --orange: #8d5b00;
  --orange-soft: #fff5d6;
  --shadow-1: 0 1px 2px rgba(0,27,55,.05);
  --shadow-2: 0 10px 30px rgba(0,27,55,.10);
  --shadow-3: 0 26px 72px rgba(0,27,55,.18);
  --focus-ring: 0 0 0 3px rgba(27,100,218,.3);
  --study-shadow: 0 8px 24px rgba(0,27,55,.07);
  --study-shadow-soft: 0 2px 8px rgba(0,27,55,.025);
}
html.study-app[data-theme="dark"] {
  color-scheme: dark;
  --bg: #101318;
  --bg-alt: #151a21;
  --surface: #1b222c;
  --surface-2: #222b37;
  --surface-3: #293442;
  --surface-4: #354354;
  --text: #f2f5f8;
  --text-2: #c5ceda;
  --text-3: #a7b2c2;
  --text-4: #7d8b9c;
  --accent: #80b6ff;
  --accent-strong: #80b6ff;
  --fill-fg: #000;
  --accent-soft: #223a56;
  --green: #5dd6a0;
  --green-soft: #18392c;
  --red: #ff8b95;
  --red-bright: #ffadb3;
  --red-soft: #402731;
  --red-fill: #402731;
  --red-fill-hover: #51313c;
  --orange: #ffc566;
  --orange-soft: #3a321f;
}
html.study-app {
  --topbar-h: 72px;
}
/* 학습실 상단바는 780px 이하에서 두 줄(60 + 54)로 접힌다. study-toss.css의 상단바 규칙과 같은
   경계를 쓴다 — 스페이서와 sticky 위치가 이 변수를 따라간다. */
@media (max-width: 780px) {
  html.study-app {
    --topbar-h: 114px;
  }
}
/* ==========================================================================
   리셋
   ========================================================================== */
*, *::before, *::after {
  box-sizing: border-box;
}
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  letter-spacing: var(--ls-body);
  /* DESIGN.md §2 — 한국어 어절 쪼개짐 금지. keep-all은 비CJK 텍스트에서 normal과
     동일하게 동작하므로 전역 기본값으로 두어도 영문 조판에 영향이 없다.
     끊을 곳이 없는 긴 토큰(URL·합성어)은 overflow-wrap이 받는다. */
  word-break: keep-all;
  overflow-wrap: break-word;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
h1, h2, h3, h4, p, figure, blockquote, dl, dd {
  margin: 0;
}
ul[class], ol[class] {
  margin: 0;
  padding: 0;
  list-style: none;
}
img, svg, video {
  display: block;
  max-width: 100%;
}
input, button, textarea, select {
  font: inherit;
  color: inherit;
}
button {
  cursor: pointer;
}
a {
  color: var(--accent);
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}
hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 0;
}
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--radius-xs);
}
::selection {
  background: rgba(41,151,255,.35);
  color: var(--text);
}
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}
/* ==========================================================================
   레이아웃 유틸리티
   ========================================================================== */
.container {
  width: min(var(--container), 100% - clamp(32px, 7vw, 72px));
  margin-inline: auto;
}
.container-wide {
  width: min(var(--container-wide), 100% - clamp(32px, 5vw, 56px));
  margin-inline: auto;
}
.container-text {
  width: min(var(--container-text), 100% - clamp(32px, 7vw, 72px));
  margin-inline: auto;
}
.stack {
  display: grid;
  gap: var(--space-4);
  align-content: start;
}
.stack-tight {
  display: grid;
  gap: var(--space-2);
  align-content: start;
}
.stack-loose {
  display: grid;
  gap: var(--space-6);
  align-content: start;
}
.skip-link {
  position: fixed;
  top: -100%;
  left: var(--space-4);
  z-index: 200;
  padding: var(--space-2) var(--space-4);
  background: var(--surface-2);
  border-radius: var(--radius-full);
  color: var(--text);
}
/* :focus로 확장 — 프로그램적 focus()에서도 가시화 (N-2) */
.skip-link:focus {
  top: var(--space-4);
}
/* ==========================================================================
   앱 셸 보조 (3c 승격) — 랜딩 밖 3면(smstudy·WordMaster·admin)이 글자 하나까지 같은
   값으로 각자 갖고 있던 지역 규칙들이다. 같은 모양을 화면마다 다시 만들지 않는다
   (DESIGN.md §7). 페이지 여백·뷰 헤더·사이드바 보조·푸터가 세 화면에서 어긋나면
   "한 벌로 보이지 않는다"는 인상이 바로 여기서 생긴다.
   ========================================================================== */
/* 시각적으로만 숨긴다 — 스크린리더와 라벨 연결(for/id)은 그대로 동작한다.
   position: absolute이므로, 스크롤 컨테이너 안에 둘 때는 그 컨테이너가
   position: relative여야 새어 나가지 않는다(.table-wrap이 그렇게 되어 있다). */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.app-page {
  padding: var(--space-6) 0 var(--space-9);
}
.app-main {
  min-width: 0;
  display: grid;
  gap: var(--space-5);
  align-content: start;
}
/* 화면 전환 때 프로그램적으로 focus()를 부르므로 포커스 링을 그리지 않는다. */
.app-main:focus {
  outline: none;
}
.view-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
}
/* 앱 아이콘(20px .ui-icon) + 제목 묶음. 아이콘은 뷰마다 한 번, 헤더에만 놓는다 (§5). */
.view-head-main {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
}
.view-head-main > div {
  min-width: 0;
}
.view-head h1 {
  font-size: var(--fs-title-1);
  line-height: var(--lh-title);
  letter-spacing: var(--ls-title);
  font-weight: 700;
}
.view-head p {
  margin-top: 2px;
  font-size: var(--fs-caption);
  color: var(--text-2);
}
/* 눈썹 라벨 바로 아래 제목이 오면 최소 여백을 준다 — 제목의 폰트 상자가 줄 상자보다
   커서, 붙여 두면 두 글자 상자가 실제로 1~2px 겹친다. */
.app-main .kicker {
  margin-bottom: 2px;
}
.side-facts {
  display: grid;
  gap: var(--space-2);
  margin: 0;
  padding: 0 var(--space-3);
}
.side-facts > div {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
}
.side-facts dt {
  font-size: var(--fs-caption);
  color: var(--text-3);
}
.side-facts dd {
  margin: 0;
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--text);
}
.side-note {
  padding: 0 var(--space-3);
  font-size: var(--fs-micro);
  line-height: 1.6;
  color: var(--text-3);
}
.app-footer {
  border-top: 1px solid var(--line-faint);
  padding: var(--space-5) 0 var(--space-7);
}
.app-footer-inner {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
  font-size: var(--fs-micro);
  color: var(--text-3);
}
@media (max-width: 640px) {
  .app-page {
    padding-top: var(--space-4);
  }
}
/* ==========================================================================
   텍스트 프리미티브
   ========================================================================== */
.display-xl {
  font-size: var(--fs-display-xl);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
  font-weight: 700;
}
.display {
  font-size: var(--fs-display);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
  font-weight: 700;
}
.title-1 {
  font-size: var(--fs-title-1);
  line-height: var(--lh-title);
  letter-spacing: var(--ls-title);
  font-weight: 700;
}
.title-2 {
  font-size: var(--fs-title-2);
  line-height: var(--lh-title);
  letter-spacing: var(--ls-title);
  font-weight: 600;
}
.title-3 {
  font-size: var(--fs-title-3);
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-body);
  font-weight: 600;
}
.kicker {
  display: block;
  font-size: var(--fs-caption);
  font-weight: 600;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--text-3);
}
.text-secondary {
  color: var(--text-2);
}
.text-tertiary {
  color: var(--text-3);
}
.text-accent {
  color: var(--accent);
}
.text-body-lg {
  font-size: var(--fs-body-lg);
  line-height: 1.45;
}
.text-body-sm {
  font-size: var(--fs-body-sm);
}
.text-caption {
  font-size: var(--fs-caption);
  color: var(--text-3);
}
.num {
  font-variant-numeric: tabular-nums;
}
/* Compact mark + word lockup. The SVG is the single brand source. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-weight: 750;
  letter-spacing: normal;
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
}
.brand:hover {
  text-decoration: none;
}
.brand-mark {
  width: 32px;
  height: 32px;
  flex: none;
}
/* ==========================================================================
   UI 아이콘 (DESIGN.md §5 v14) — assets/ui-icons.svg의 테두리형 한 세트만 쓴다.
   기본 17px(사이드바·행 선두). 툴바·버튼 안은 16px, 뷰 헤더·엠블럼은 20px — 이 3단 외
   임의 크기는 없다. 선 굵기는 크기와 무관하게 1.75다. 기본 선 색은 --text-3, hover·활성은
   --accent. 아이콘 뒤에 --accent / --accent-soft / 상태색 면을 두지 않는다(§5.2 게이트 4).
   ========================================================================== */
.ui-icon {
  width: 17px;
  height: 17px;
  flex: none;
  fill: none;
  color: var(--text-3);
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.btn .ui-icon, .toolbar .ui-icon, .segmented-btn .ui-icon {
  width: 16px;
  height: 16px;
}
.btn-secondary:hover .ui-icon, .btn-ghost:hover .ui-icon, .topbar-link:hover .ui-icon, .segmented-btn:hover .ui-icon, .segmented-btn[aria-selected="true"] .ui-icon, .segmented-btn[aria-pressed="true"] .ui-icon {
  color: var(--accent);
}
.view-head-main > .ui-icon, .sidebar-emblem .ui-icon {
  width: 20px;
  height: 20px;
  color: var(--text-3);
}
/* ==========================================================================
   상단바
   ========================================================================== */
.topbar {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  height: var(--topbar-h);
  border-bottom: 1px solid var(--line);
  background: var(--bg);
}
.topbar-inner {
  height: 100%;
  display: flex;
  align-items: center;
  gap: var(--space-4);
}
/* 폭 배분: 브랜드와 앱 배지가 먼저 자리를 잡고(flex: none) 링크 묶음이 남는 폭을 쓴다.
   줄어드는 것은 nav뿐이다. */
.topbar .brand {
  font-size: 1rem;
  flex: none;
}
.topbar-nav {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-left: auto;
  min-width: 0;
}
.topbar-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* 텍스트가 보이는 폭에서도 터치 타깃은 44px이다 — 실브라우저 감사에서 라벨 링크가
     21px 높이로 WCAG 2.5.8 하한(24px)에 미달했다. 상단바 높이(64/72px) 안에서
     세로 중앙 정렬되므로 레이아웃은 그대로다. */
  min-height: 44px;
  font-size: var(--fs-caption);
  font-weight: 500;
  color: var(--text-2);
  text-decoration: none;
  /* 링크 라벨은 한 줄이다 — "사회·문화"가 두 줄로 꺾이지 않는다. 폭이 모자라면 줄을 늘리지
     않고 아래 ≤420 규칙이 텍스트를 접근성 이름으로만 남긴다. */
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-out);
}
.topbar-link:hover {
  color: var(--text);
  text-decoration: none;
}
.topbar-link .ui-icon {
  width: 16px;
  height: 16px;
}
.topbar-crumb {
  flex: none;
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--text-2);
  padding: 3px 10px;
  border-radius: var(--radius-full);
  background: var(--surface-2);
  white-space: nowrap;
}
/* 링크 텍스트 래퍼 — 375px에서 텍스트가 들어가지 않는 화면만 라벨을
   <span class="topbar-link-text">로 감싼다(opt-in). ≤420px에서는 아이콘만 시각적으로 남기고
   텍스트는 .sr-only와 같은 선언으로 숨겨 접근성 이름을 유지한다(미디어 쿼리 안에서 클래스를
   바꿀 수 없어 선언을 되쓴다). 아이콘만 남은 링크는 44×44 터치 영역을 갖고, 마지막 링크는
   아이콘의 오른쪽 변이 컨테이너 기준선에 서도록 (44 − 16) / 2 만큼 되돌린다. */
@media (max-width: 420px) {
  .topbar-link:has(.topbar-link-text) {
    min-width: 44px;
    justify-content: center;
    gap: 0;
  }
  .topbar-link:has(.topbar-link-text):last-child {
    margin-right: -14px;
  }
  .topbar-link-text {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
}
.topbar-spacer {
  height: var(--topbar-h);
}
/* ==========================================================================
   버튼
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: 10px 18px;
  border: 1px solid transparent;
  border-radius: var(--radius-full);
  font-size: var(--fs-caption);
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.btn:hover {
  text-decoration: none;
}
.btn:active {
  transform: scale(.97);
}
.btn:disabled, .btn[aria-disabled="true"] {
  cursor: default;
  opacity: .45;
  transform: none;
}
.btn-primary {
  background: var(--accent-strong);
  color: var(--fill-fg);
}
.btn-primary:hover {
  /* 채움 면의 86% + 검정 14%. 팔레트마다 면 밝기가 달라도 한 단계만 내려간다
     (--fill-fg 기준 :root 15.17 / 스터디 라이트 6.81 / 스터디 다크 7.51). */
  background: color-mix(in srgb, var(--accent-strong) 86%, #000);
}
.btn-secondary {
  background: var(--surface-3);
  color: var(--text);
}
.btn-secondary:hover {
  background: var(--surface-4);
}
.btn-ghost {
  background: transparent;
  color: var(--accent);
  border-color: var(--line-strong);
}
.btn-ghost:hover {
  border-color: var(--text-3);
}
/* 면은 불투명 --red-fill, 전경은 --red-bright다. 알파 면 + --red 전경은 표 행 hover
   위에서 4.23까지 내려갔다 (상단 대비표 "위험 버튼" 참조). */
.btn-danger {
  background: var(--red-fill);
  color: var(--red-bright);
}
.btn-danger:hover {
  background: var(--red-fill-hover);
}
.btn-sm {
  min-height: 40px;
  padding: 8px 13px;
  font-size: var(--fs-caption);
}
.btn-lg {
  min-height: 50px;
  padding: 13px 24px;
  font-size: var(--fs-body);
}
/* 애플 "더 알아보기" 어법의 화살표 링크 */
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--fs-body);
  font-weight: 400;
  color: var(--accent);
}
.link-arrow:hover {
  text-decoration: none;
}
.link-arrow span {
  text-decoration: inherit;
}
.link-arrow:hover span {
  text-decoration: underline;
}
.link-arrow::after {
  content: "›";
  display: inline-block;
  font-size: 1.15em;
  transition: transform var(--dur-fast) var(--ease-out);
}
.link-arrow:hover::after {
  transform: translateX(3px);
}
/* ==========================================================================
   입력 필드
   ========================================================================== */
.field {
  display: grid;
  gap: var(--space-2);
}
.field-label {
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--text-2);
}
.field-input {
  width: 100%;
  min-height: 46px;
  padding: 11px 14px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  color: var(--text);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.field-input::placeholder {
  color: var(--text-4);
}
.field-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--focus-ring);
}
.field-input:disabled {
  opacity: .45;
}
select.field-input {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 36px;
  background-image: var(--select-arrow);
  background-position: right 13px center;
  background-size: 12px 8px;
  background-repeat: no-repeat;
  cursor: pointer;
}
/* 툴바 안에 들어가는 작은 입력. .btn-sm과 같은 36→30px 기하로 맞춘다. */
.field-input-sm {
  padding: 6px 11px;
  font-size: var(--fs-caption);
}
select.field-input-sm {
  padding-right: 30px;
  background-position: right 11px center;
}
/* 행 안의 값 컨트롤 — .list-row-value 자리에 들어가는 입력·셀렉트.
   .field-input을 그대로 쓰면 행 높이가 두 배가 되므로, 테두리 대신 --surface-3 면으로
   구분하는 컴팩트 변형을 시스템 레벨에 둔다. 화면별 CSS에서 다시 만들지 않는다. */
.field-input-inline {
  width: auto;
  padding: 4px 9px;
  background: var(--surface-3);
  border: 0;
  border-radius: var(--radius-s);
  color: var(--text);
  /* on --surface-3 12.80:1 */
  font-size: var(--fs-body-sm);
}
select.field-input-inline {
  appearance: none;
  -webkit-appearance: none;
  max-width: 100%;
  padding-right: 24px;
  background-image: var(--select-arrow);
  background-position: right 8px center;
  background-size: 10px 7px;
  background-repeat: no-repeat;
  cursor: pointer;
}
.field-hint {
  font-size: var(--fs-caption);
  color: var(--text-3);
}
.field-error {
  font-size: var(--fs-caption);
  color: var(--red);
  min-height: 1.2em;
}
/* 비어 있는 알림 줄이 자리를 먹지 않게 한다 — role="alert"는 내용이 생길 때만 읽힌다. */
.field-error:empty {
  display: none;
  min-height: 0;
}
/* ==========================================================================
   카드 — 그림자 없음. 깊이는 표면 단차와 헤어라인 (DESIGN.md §4)
   ========================================================================== */
.card {
  background: var(--surface);
  border: 1px solid var(--line-faint);
  border-radius: var(--radius-l);
  padding: var(--space-5);
}
.card-hover {
  transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.card-hover:hover {
  background: var(--surface-2);
  border-color: var(--line);
}
.card-xl {
  border-radius: var(--radius-xl);
  padding: clamp(20px, 4vw, 44px);
}
/* 인셋 — 카드·그룹 *안쪽*의 한 단 밝은 패널. 인용·예시·결과 박스. */
.inset {
  background: var(--surface-2);
  border-radius: var(--radius-m);
  padding: var(--space-4);
}
/* ==========================================================================
   그룹 리스트 (DESIGN.md §7.1) — iOS 설정·건강 앱 어법
   문장 하나에 카드 하나를 두르는 조판을 대체한다.
   ========================================================================== */
.list-group {
  background: var(--surface);
  border: 1px solid var(--line-faint);
  border-radius: var(--radius-l);
  overflow: hidden;
}
/* 카드·인셋 안에 들어가는 그룹은 한 단 위 표면으로. */
.list-group.is-inset {
  background: var(--surface-2);
  border-color: transparent;
}
.list-group-head {
  display: block;
  padding: 0 var(--space-3) var(--space-2);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--text-3);
  /* on --bg 5.80:1 / on --surface 4.99:1 */
}
.list-group-foot {
  display: block;
  padding: var(--space-2) var(--space-3) 0;
  font-size: var(--fs-caption);
  color: var(--text-3);
}
.list-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--row-min-h);
  padding: var(--row-pad-y) var(--row-pad-x);
  width: 100%;
  border: 0;
  background: transparent;
  color: var(--text);
  font-size: var(--fs-body);
  text-align: left;
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease-out);
}
/* 행 자체가 동작인 경우(링크·버튼·파일 선택 라벨)만 hover를 받는다. */
a.list-row:hover, button.list-row:hover, label.list-row:hover {
  background: var(--line-faint);
  text-decoration: none;
}
label.list-row {
  cursor: pointer;
}
/* 행 사이 인셋 헤어라인. 직접 border-top을 그리지 않는다. */
.list-row + .list-row::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--row-pad-x);
  right: 0;
  height: 1px;
  background: var(--line-faint);
}
/* 리드(아이콘) 슬롯이 있는 행은 리드 폭만큼 더 들여쓴다. */
.list-row + .list-row:has(.list-row-lead)::before {
  left: var(--list-inset);
}
/* 행 선두 아이콘 슬롯 — 안에 17px .ui-icon 하나. 배경 타일 없음(§5). */
.list-row-lead {
  display: flex;
  flex: none;
  align-items: center;
  color: var(--text-3);
  /* on --surface 4.99:1 — 그래픽 3:1 요건 충족 */
}
a.list-row:hover .list-row-lead, button.list-row:hover .list-row-lead, label.list-row:hover .list-row-lead {
  color: var(--accent);
}
.list-row-body {
  display: grid;
  gap: 1px;
  min-width: 0;
  /* 축소 안전 — 자식에 nowrap을 걸면 가로 넘침이 난다 */
  flex: 1;
}
.list-row-title {
  font-size: var(--fs-body);
  line-height: var(--lh-snug);
  color: var(--text);
}
.list-row-sub {
  font-size: var(--fs-body-sm);
  line-height: var(--lh-snug);
  color: var(--text-2);
}
/* 부제가 비면 줄 자체를 접는다 — 렌더러가 빈 슬롯을 남겨도 행 높이가 늘지 않는다(§6.1). */
.list-row-sub:empty {
  display: none;
}
.list-row-value {
  flex: none;
  margin-left: auto;
  font-size: var(--fs-body-sm);
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.list-row-nav::after {
  content: "›";
  flex: none;
  margin-left: var(--space-1);
  color: var(--text-3);
  font-size: 1.2em;
  line-height: 1;
}
/* 행 전체를 진입점으로 늘린다 (3b가 smstudy 로컬 .sm-row-open으로 만든 패턴의 승격본).
   행 안에 체크박스·스위치가 함께 있을 때 필수다 — 버튼 안에 컨트롤을 중첩하면 마크업이
   무효이므로, 진입 버튼을 작게 두고 ::after 오버레이로 히트 영역만 행 전체로 넓힌다.
   .list-row가 position: relative이므로 오버레이의 기준 상자는 행이다.
   같은 행의 다른 컨트롤은 .list-row-accessory로 오버레이 위에 올린다. */
.list-row-stretch {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
}
.list-row-stretch::after {
  content: "";
  position: absolute;
  inset: 0;
}
/* 진입점이 행 전체이므로 hover도 행 전체가 받는다 (a/button .list-row와 같은 값). */
.list-row:has(.list-row-stretch):hover {
  background: var(--line-faint);
}
.list-row-accessory {
  position: relative;
  /* 오버레이(z-index 0) 위로 올라와 클릭을 받는다 */
  z-index: 1;
  flex: none;
  display: grid;
  place-items: center;
}
.list-row-accessory input[type="checkbox"] {
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
  cursor: pointer;
}
/* 비활성 행 — 배경 반응까지 끈다. hover 규칙(0,2,1)보다 특이도가 높아야 이긴다. */
.list-row:disabled, .list-row[aria-disabled="true"] {
  opacity: .45;
  cursor: default;
}
.list-row:disabled:hover, .list-row[aria-disabled="true"]:hover {
  background: transparent;
}
/* 두 줄 행 변형 — 값 슬롯(세그먼트·컨트롤)이 좁은 폭에서 제목을 밀어내는 행이 opt-in한다
   (기준: 375px에서 값 슬롯이 행 내용폭의 60%를 넘는 행). ≤480px에서 제목이 첫 줄 전폭·한 줄,
   컨트롤이 둘째 줄 전폭이다. 글자 크기·패딩을 줄여 한 줄에 우겨넣지 않는다(§6.1).
   481px 위에서는 보통 행과 같다. */
@media (max-width: 480px) {
  .list-row.is-stacked {
    flex-wrap: wrap;
  }
  /* 첫 줄이 제목 전용이라 nowrap이 안전하다 — "본문 자식 nowrap 금지"(§7)의 유일한 예외. */
  .list-row.is-stacked .list-row-title {
    white-space: nowrap;
  }
  .list-row.is-stacked .list-row-value, .list-row.is-stacked .segmented {
    flex: 1 1 100%;
    margin-left: 0;
    text-align: left;
  }
  /* 세그먼트는 둘째 줄 전폭을 자식 수만큼 같은 열로 나눈다(5개면 5등분). wrap 없음. */
  .list-row.is-stacked .segmented {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
  }
  /* 칩 폭은 열이 정한다 — 좌우 패딩은 여백이 아니라 글자와 칩 변 사이 안전거리(4px). */
  .list-row.is-stacked .segmented-btn {
    min-width: 0;
    padding-inline: var(--space-1);
    text-align: center;
  }
}
/* "헤더 + 우측 버튼 한 줄" 변형. 헤더의 좌우·아래 여백을 이 줄이 통째로 가져가고
   안쪽 .list-group-head는 여백을 0으로 내린다 — 버튼이 붙어도 그룹 위 리듬(8px)이
   그대로 유지된다. 3b는 이 여백을 부모 grid gap으로 메우고 padding-bottom을 0으로
   덮어썼는데, 그러면 부모 gap이 없는 화면에서 헤더가 그룹에 붙는다. */
.list-group-head-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 30px;
  padding: 0 var(--space-3) var(--space-2);
}
.list-group-head-row > .list-group-head {
  padding: 0;
}
/* ==========================================================================
   접힘 그룹 (disclosure) — <details>/<summary>. 그룹 리스트와 같은 곡률·헤어라인이며
   summary 행의 기하는 .list-row와 같은 토큰(--row-min-h / --row-pad-*)을 쓴다.
   3b가 smstudy 로컬 .sm-fold로 만든 패턴의 승격본이다.
   ========================================================================== */
.disclosure {
  background: var(--surface);
  border: 1px solid var(--line-faint);
  border-radius: var(--radius-l);
  overflow: hidden;
}
.disclosure-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--row-min-h);
  padding: var(--row-pad-y) var(--row-pad-x);
  cursor: pointer;
  list-style: none;
}
.disclosure-head::-webkit-details-marker {
  display: none;
}
.disclosure-head::after {
  content: "›";
  flex: none;
  margin-left: var(--space-2);
  color: var(--text-3);
  font-size: 1.2em;
  line-height: 1;
  transition: transform var(--dur-fast) var(--ease-out);
}
.disclosure[open] > .disclosure-head::after {
  transform: rotate(90deg);
}
.disclosure-head:hover {
  background: var(--line-faint);
}
.disclosure-title {
  font-size: var(--fs-body);
  font-weight: 600;
}
.disclosure-hint {
  margin-left: auto;
  font-size: var(--fs-caption);
  color: var(--text-2);
}
/* on --surface 7.03:1 */
.disclosure-body {
  display: grid;
  gap: var(--space-4);
  padding: var(--space-4) var(--row-pad-x) var(--space-5);
  border-top: 1px solid var(--line-faint);
}
/* ==========================================================================
   시트 / 모달
   ========================================================================== */
.sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 150;
  display: grid;
  place-items: center;
  padding: var(--space-4);
  /* blur 없는 순수 스크림 (§3). 뒤 배경을 흐리는 대신 더 어둡게 덮어 시트를 분리한다. */
  background: rgba(0,0,0,.78);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-med) var(--ease-out), visibility 0s linear var(--dur-med);
}
.sheet-backdrop.open {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--dur-med) var(--ease-out);
}
.sheet {
  width: min(400px, 100%);
  max-height: min(85dvh, 720px);
  overflow: auto;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-3);
  /* 오버레이 — 그림자 허용 (§4) */
  padding: clamp(20px, 4vw, 32px);
  transform: translateY(12px) scale(.985);
  transition: transform var(--dur-med) var(--ease-swift);
}
.sheet-backdrop.open .sheet {
  transform: none;
}
.sheet-title {
  font-size: var(--fs-title-2);
  font-weight: 600;
  letter-spacing: var(--ls-title);
}
.sheet-sub {
  color: var(--text-2);
  font-size: var(--fs-caption);
  margin-top: var(--space-1);
}
/* ==========================================================================
   표
   ========================================================================== */
.table-wrap {
  /* 스크롤 컨테이너를 절대 배치의 컨테이닝 블록으로도 만든다. 이게 없으면 표 안의
     position: absolute 자식(스크린리더 전용 라벨 등)이 초기 컨테이닝 블록에 붙어
     스크롤 밖으로 새어 나가고, 문서 전체에 가로 스크롤 폭을 만든다(admin 768px에서 실측). */
  position: relative;
  overflow-x: auto;
  border: 1px solid var(--line-faint);
  border-radius: var(--radius-l);
  background: var(--surface);
}
.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-caption);
}
.table th, .table td {
  padding: 10px 14px;
  text-align: left;
  border-bottom: 1px solid var(--line-faint);
  white-space: nowrap;
}
.table th {
  font-weight: 600;
  color: var(--text-3);
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
}
.table tbody tr:last-child td {
  border-bottom: 0;
}
.table tbody tr:hover td {
  background: var(--line-faint);
}
/* ==========================================================================
   뱃지 — --surface-2까지의 배경 위에만 (상단 대비표)
   ========================================================================== */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  border-radius: var(--radius-full);
  font-size: var(--fs-micro);
  font-weight: 600;
  background: var(--surface-3);
  color: var(--text-2);
  /* on --surface-3 5.42:1 */
}
.badge-accent {
  background: var(--accent-soft);
  color: var(--accent);
}
.badge-green {
  background: var(--green-soft);
  color: var(--green);
}
.badge-red {
  background: var(--red-soft);
  color: var(--red-bright);
}
.badge-orange {
  background: var(--orange-soft);
  color: var(--orange);
}
/* ==========================================================================
   세그먼티드 컨트롤
   ========================================================================== */
.segmented {
  display: inline-flex;
  padding: 2px;
  gap: 2px;
  background: var(--surface-2);
  border-radius: var(--radius-full);
}
.segmented-btn {
  border: 0;
  background: transparent;
  color: var(--text-2);
  padding: 5px 14px;
  border-radius: var(--radius-full);
  font-size: var(--fs-caption);
  font-weight: 600;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.segmented-btn:hover {
  color: var(--text);
}
.segmented-btn.is-active, .segmented-btn[aria-pressed="true"], .segmented-btn[aria-selected="true"] {
  background: var(--surface-4);
  color: var(--text);
  /* on --surface-4 10.42:1 */
  box-shadow: var(--shadow-1);
  /* 떠 있는 인디케이터 — 예외 허용 (§4) */
}
/* ==========================================================================
   툴바
   ========================================================================== */
.toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding: var(--space-2) var(--space-3);
  background: var(--surface);
  border: 1px solid var(--line-faint);
  border-radius: var(--radius-m);
}
.toolbar-group {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.toolbar-spacer {
  flex: 1;
}
/* 스크롤 콘텐츠가 밑으로 지나가므로 불투명해야 한다 — blur는 §3이 금지한다.
   .toolbar의 --surface와 같은 값이라 sticky 여부로 표면이 달라지지 않는다. */
.toolbar-sticky {
  position: sticky;
  top: calc(var(--topbar-h) + var(--space-2));
  z-index: 50;
  background: var(--surface);
}
/* ==========================================================================
   사이드바 / 앱 셸
   ========================================================================== */
.app-shell {
  display: grid;
  grid-template-columns: 256px minmax(0, 1fr);
  gap: var(--space-7);
  align-items: start;
}
.sidebar {
  position: sticky;
  top: calc(var(--topbar-h) + var(--space-5));
  display: grid;
  gap: var(--space-5);
  max-height: calc(100dvh - var(--topbar-h) - var(--space-7));
  overflow-y: auto;
  padding: var(--space-4);
  border: 1px solid var(--line-faint);
  border-radius: var(--radius-l);
  background: rgba(255,255,255,.025);
}
/* 사이드바 최상단 엠블럼 — 흰 헤어라인 박스 + 흰 로고 마크(DESIGN.md §8, plan.md §3.4).
   그라디언트·그림자 없음(§3·§4). 보더는 --line-strong(기존 토큰 중 가장 강한 헤어라인)로
   "하얀 테두리" 요청을 만족시킨다 — 더 강한 토큰이 필요해지면 system.css에 추가한다. */
.sidebar-emblem {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 56px;
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  background: var(--surface);
  color: var(--text);
}
.sidebar-emblem .brand-mark {
  width: 34px;
  height: 34px;
}
.sidebar-group {
  display: grid;
  gap: 1px;
}
.sidebar-label {
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  /* --text-4는 비활성 전용(상단 대비표) — 살아있는 라벨은 --text-3 (on --bg 5.80:1) */
  color: var(--text-3);
  padding: 0 var(--space-3) var(--space-1);
}
.sidebar-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: 9px 12px;
  border-radius: var(--radius-m);
  border: 0;
  background: transparent;
  color: var(--text-2);
  font-size: var(--fs-caption);
  font-weight: 500;
  text-decoration: none;
  text-align: left;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.sidebar-item:hover {
  background: var(--line-faint);
  color: var(--text);
  text-decoration: none;
}
.sidebar-item .ui-icon {
  width: 17px;
  height: 17px;
  color: var(--text-3);
}
.sidebar-item:hover .ui-icon, .sidebar-item.is-active .ui-icon, .sidebar-item[aria-current] .ui-icon {
  color: var(--accent);
}
.sidebar-item.is-active, .sidebar-item[aria-current] {
  background: var(--surface-3);
  color: var(--text);
}
/* 모바일 사이드바 — 가로 스크롤 스트립이 아니라 줄바꿈 칩 격자다(DESIGN.md §6.1 · v13).
   첫 줄은 엠블럼(+제품 이름) 한 줄, 그 아래 그룹이 인라인으로 이어지며 그룹 사이는
   헤어라인 하나다. 모든 항목이 잘리지 않고 보이고, 항목 높이는 44px 터치 하한을 지킨다.
   설명 슬롯(.side-facts / .side-note / 제품 부제)은 모바일에서 렌더하지 않는다 —
   사이드바 항목은 라벨 한 줄만이다. 6개 화면이 이 규칙 하나를 소비한다. */
@media (max-width: 860px) {
  .app-shell {
    grid-template-columns: minmax(0, 1fr);
  }
  .sidebar {
    position: static;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
    max-width: 100%;
    max-height: none;
    overflow: visible;
    padding: var(--space-3);
  }
  .sidebar > .sidebar-emblem, .sidebar > .sidebar-product {
    flex: 1 0 100%;
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--line-faint);
  }
  .sidebar > .sidebar-emblem {
    justify-content: flex-start;
    height: auto;
    padding: 0 0 var(--space-2);
    border-top: 0;
    border-left: 0;
    border-right: 0;
    border-radius: 0;
    background: transparent;
  }
  .sidebar-group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    flex: 0 1 auto;
    gap: var(--space-1);
    min-width: 0;
  }
  .sidebar-group + .sidebar-group {
    padding-left: var(--space-2);
    border-left: 1px solid var(--line-faint);
  }
  /* 컨트롤(체크박스 행 등)을 담은 그룹은 한 줄을 통째로 쓴다 — 칩 사이에 끼면 접힌다. */
  .sidebar-group:has(> .list-group) {
    flex: 1 0 100%;
    padding-left: 0;
    border-left: 0;
  }
  .sidebar-group > .list-group {
    flex: 1 0 100%;
  }
  .sidebar-label {
    flex: 0 0 auto;
    padding: 0 var(--space-1) 0 0;
  }
  .sidebar-item {
    flex: 0 1 auto;
    min-width: 0;
    white-space: normal;
  }
  .sidebar .side-facts, .sidebar .side-note, .sidebar .sidebar-product-copy span {
    display: none;
  }
  /* 설명 슬롯이 숨으면 그 그룹의 라벨도 함께 숨긴다 — 라벨만 남아 빈 제목이 뜨지 않게.
     항목(.sidebar-item / .list-group)이 함께 있는 그룹은 라벨을 유지한다. */
  .sidebar .sidebar-group:has(> .side-facts, > .side-note):not(:has(> .sidebar-item, > .list-group)) {
    display: none;
  }
}
/* ==========================================================================
   토스트
   ========================================================================== */
.toast {
  position: fixed;
  left: 50%;
  bottom: var(--space-6);
  transform: translateX(-50%) translateY(8px);
  z-index: 180;
  padding: 10px 18px;
  background: var(--surface-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-full);
  box-shadow: var(--shadow-2);
  /* 오버레이 — 그림자 허용 (§4) */
  font-size: var(--fs-caption);
  font-weight: 600;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-med) var(--ease-out), transform var(--dur-med) var(--ease-swift), visibility 0s linear var(--dur-med);
}
.toast.open {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%);
  transition: opacity var(--dur-med) var(--ease-out), transform var(--dur-med) var(--ease-swift);
}
/* ==========================================================================
   스탯 (수치 강조) — 뷰당 1묶음까지 (DESIGN.md §6 장식 상한)
   ========================================================================== */
.stat {
  display: grid;
  gap: 2px;
}
.stat-value {
  font-size: var(--fs-title-1);
  font-weight: 600;
  letter-spacing: var(--ls-title);
  font-variant-numeric: tabular-nums;
}
.stat-label {
  font-size: var(--fs-caption);
  color: var(--text-3);
}
/* ==========================================================================
   콘솔 대시보드 프리미티브 (DESIGN.md §1 "조작" 레퍼런스 / plan.md §3.4)
   운영 화면(admin·usage)의 공통 어법이다: 정보를 제목 있는 카드로 구획하고,
   라벨(회색·소형)과 값(흰색·대형)의 위계를 고정하며, 상태는 색 점으로만 말한다.
   화면 CSS에서 같은 모양을 다시 만들지 않는다 (DESIGN.md §7).

   신규 **색 토큰은 없다** — 아래 조합은 모두 기존 토큰이고, 값은 파일 상단 대비 전수표에서
   그대로 가져온 실측치다(그 표는 validate.mjs가 :root에서 재계산해 대조한다):
     .summary-cell   --text  on --surface 16.61  (값)   / --text-3 on --surface 4.99 (라벨)
     .gauge-fill     --accent on --surface-3 4.62 / --orange 6.78 / --red 4.09
                     → 셋 다 그래픽 요건 3:1 충족 (게이지 막대는 텍스트가 아니다)
     .status-dot     --green on --surface 8.94 / --orange on --surface 8.80
   ========================================================================== */
/* 상태 요약 스트립 — 가로 1카드를 N칸으로 나눈다. 칸 사이 경계는 1px gap 위로 비치는
   부모 배경이다(헤어라인을 칸마다 그리면 줄바꿈 때 좌측에 선이 남는다). */
.summary-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1px;
  background: var(--line-faint);
  border: 1px solid var(--line-faint);
  border-radius: var(--radius-m);
  overflow: hidden;
}
.summary-cell {
  display: grid;
  gap: 2px;
  align-content: start;
  min-width: 0;
  padding: var(--space-3) var(--space-4);
  background: var(--surface);
}
/* 한 줄에 칸이 여럿이므로 값은 뷰 제목(title-1)보다 한 단 낮춘다. */
.summary-cell .stat-value {
  font-size: var(--fs-title-2);
}
/* 값 자리에 상태(점+텍스트)가 오는 칸. */
.summary-cell .stat-state {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-body);
}
/* 상태 점 — 상태 표시 전용(DESIGN.md §3). 의미를 점이 단독으로 지지 않는다: 항상 옆에
   텍스트 라벨이 있고 점 자신은 aria-hidden이다. */
.status-dot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--green);
}
.status-dot.is-warn {
  background: var(--orange);
}
/* 진행 중(working·reviewing) 상태 점 — 강조 1색을 상태 신호로 쓴다 (DESIGN.md §3).
   그래픽 3:1 기준: --accent on surface 6.00 / surface-2 5.58 / surface-3 4.62 (상단 표). */
.status-dot.is-accent {
  background: var(--accent);
}
/* 끝났거나 쉬는(done·waiting) 상태 점 — 중립. 항상 텍스트 라벨과 짝이라 점 자체는
   장식 그래픽이다. --text-3: surface 4.99 / surface-2 4.65 / surface-3 3.85 / surface-4 3.13. */
.status-dot.is-idle {
  background: var(--text-3);
}
/* 퍼센트 게이지 — 실제 진행/소진 데이터에만 쓴다 (DESIGN.md §6).
   .list-row-body 안에 넣으면 제목과 부제 사이 한 줄을 차지한다. */
.gauge-track {
  /* span 마크업이라 명시적 블록화 필요 — 인라인이면 height가 무시된다 */
  display: block;
  height: 6px;
  margin: 5px 0 4px;
  border-radius: var(--radius-full);
  background: var(--surface-3);
  overflow: hidden;
}
.gauge-fill {
  /* 인라인 span에는 width %가 적용되지 않아 채움이 0으로 보인다 */
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--accent);
}
.gauge-fill.is-warn {
  background: var(--orange);
}
.gauge-fill.is-over {
  background: var(--red);
}
/* ==========================================================================
   스크롤 등장 (prefers-reduced-motion 존중)
   JS가 <html>에 .js를 붙인 뒤 IntersectionObserver로 .in을 부여한다.
   모션 감소 환경·JS 미동작 시 항상 보이는 상태가 기본값이다.
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  .js .reveal {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-swift);
  }
  .js .reveal.in {
    opacity: 1;
    transform: none;
  }
}
/* ==========================================================================
   Workspace suite — Usage · Admin · WordMaster
   세 화면의 제품 문법만 공유한다. 다른 학습 화면은 기존 앱 셸을 그대로 유지한다.
   ========================================================================== */
html.auth-pending body {
  visibility: hidden;
}
/* 캔버스는 순검정 한 면이다 — 글로우·격자 그라디언트 없음(DESIGN.md §1.1·§3). */
.workspace-app {
  min-height: 100dvh;
  background: var(--bg);
}
.workspace-app .topbar {
  border-bottom-color: rgba(255,255,255,.08);
  background: #07080b;
}
/* 현재 앱 이름 알약 — 강조색 틴트가 아니라 중립 표면 한 단(§3·§5 색 타일 금지).
   --text-2 on --surface-3 5.42:1 (상단 대비표). 강조색은 뷰당 한 곳, 활성 컨트롤에만 남긴다. */
.workspace-app .topbar-crumb {
  background: var(--surface-3);
  color: var(--text-2);
  text-transform: uppercase;
  letter-spacing: .08em;
}
.workspace-app .app-page {
  padding-top: clamp(28px, 4vw, 56px);
  padding-bottom: 80px;
}
.workspace-app .app-shell {
  grid-template-columns: 284px minmax(0, 1fr);
  gap: clamp(30px, 4vw, 64px);
}
.workspace-app .app-main {
  width: 100%;
  min-width: 0;
}
.workspace-app .sidebar {
  top: calc(var(--topbar-h) + 28px);
  gap: 22px;
  max-height: calc(100dvh - var(--topbar-h) - 56px);
  padding: 16px;
  border-color: var(--line-faint);
  border-radius: 24px;
  /* 깊이는 명도 단차 한 단 + 헤어라인뿐 — 그라디언트·그림자 없음(§4). */
  background: var(--surface);
}
.sidebar-product {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  padding: 7px;
}
.workspace-app .sidebar-emblem {
  width: 48px;
  height: 48px;
  flex: none;
  border-color: var(--line);
  border-radius: 15px;
  background: var(--surface-2);
}
.workspace-app .sidebar-emblem .brand-mark {
  width: 30px;
  height: 30px;
}
.workspace-app .sidebar-emblem > .ui-icon {
  color: var(--text);
}
.sidebar-product-copy {
  display: grid;
  min-width: 0;
  gap: 2px;
}
.sidebar-product-copy span {
  color: var(--text-3);
  font-size: .58rem;
  font-weight: 750;
  letter-spacing: .14em;
}
.sidebar-product-copy strong {
  overflow: hidden;
  color: var(--text);
  font-size: 1rem;
  line-height: 1.2;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.workspace-app .sidebar-label {
  padding-inline: 12px;
  /* 토큰화 이전 하드코딩 #777e8f는 --surface #161617 위 4.45:1로 캡션 하한(4.5)에
     0.05 미달이었다(실브라우저 감사 320~1280px 전 폭). --text-3는 같은 면에서 4.99:1. */
  color: var(--text-3);
  font-size: .59rem;
  letter-spacing: .14em;
}
.workspace-app .sidebar-item {
  min-height: 46px;
  padding: 10px 12px;
  border: 1px solid transparent;
  border-radius: 13px;
  color: #aeb4c2;
}
.workspace-app .sidebar-item:hover {
  border-color: rgba(255,255,255,.08);
  background: rgba(255,255,255,.05);
}
/* 활성 항목 — 색 면이 아니라 표면 한 단(--surface-3) + 헤어라인으로 구분한다(§4·§5).
   v14에서 강조 항목(.sidebar-item-featured)과 우측 메타 배지(.sidebar-item-meta)를 폐기했다 —
   사이드바 항목은 아이콘 + 라벨 한 줄뿐이다(§6.1). */
.workspace-app .sidebar-item.is-active, .workspace-app .sidebar-item[aria-current] {
  border-color: var(--line);
  background: var(--surface-3);
  color: var(--text);
}
.workspace-kicker {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  color: #80c6ff;
  font-size: .64rem;
  font-weight: 800;
  letter-spacing: .16em;
}
.workspace-kicker i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
}
.workspace-app .view-head {
  align-items: flex-end;
  padding-bottom: 24px;
  border-bottom: 1px solid rgba(255,255,255,.08);
}
.workspace-app .view-head .title-1 {
  font-size: clamp(2.25rem, 2rem + 1.25vw, 3.5rem);
  line-height: .98;
  letter-spacing: -.055em;
}
.workspace-app .view-head-main p {
  max-width: 680px;
  margin-top: 12px;
  color: #858c9b;
  font-size: .95rem;
}
.workspace-app .btn {
  border-radius: 13px;
}
/* 모바일 — 줄바꿈 칩 격자는 위의 공통 .sidebar 규칙이 담당한다. 여기서는 워크스페이스
   셸의 여백·곡률만 맞춘다. 가로 스크롤 스트립(overflow-x · nowrap · flex: none)은 폐지했다. */
@media (max-width: 860px) {
  .workspace-app .app-shell {
    display: block;
  }
  .workspace-app .sidebar {
    position: static;
    gap: var(--space-2);
    max-height: none;
    margin-bottom: 30px;
    padding: 12px;
    border-radius: 18px;
  }
  .workspace-app .sidebar-product {
    gap: 10px;
    padding: 0 0 8px;
  }
  .workspace-app .sidebar-product .sidebar-emblem {
    width: 40px;
    height: 40px;
  }
  .workspace-app .sidebar-label {
    padding-inline: 0 4px;
  }
  .workspace-app .sidebar-item {
    min-height: 44px;
    padding: 8px 12px;
  }
}
@media (max-width: 620px) {
  .workspace-app .topbar-nav .topbar-link:not(:last-child) {
    display: none;
  }
  .workspace-app .app-page {
    padding-top: 20px;
  }
  .workspace-app .sidebar {
    margin-inline: -4px;
    margin-bottom: 24px;
  }
  .workspace-app .sidebar-product .sidebar-emblem {
    width: 36px;
    height: 36px;
  }
  .workspace-app .view-head {
    align-items: flex-start;
    padding-bottom: 20px;
  }
  .workspace-app .view-head .toolbar-group {
    width: 100%;
    justify-content: space-between;
  }
}
/* Printing study notes is always paper-colored, independently of the saved display theme. */
@media print {
  html.study-app[data-theme] {
    color-scheme: light;
    --bg: #fff;
    --bg-alt: #fff;
    --surface: #fff;
    --surface-2: #fff;
    --surface-3: #f2f4f5;
    --surface-4: #e7eaec;
    --text: #101418;
    --text-2: #3d474f;
    --text-3: #4f5963;
    --text-4: #68737d;
    --accent: #176785;
    --accent-strong: #0e5570;
    --accent-soft: #eef8fc;
    /* 인쇄 팔레트의 채움 면은 테마와 무관하게 전부 어둡다 — 라벨은 흰색 고정.
       #fff on --accent-strong #0e5570 8.23 / on --green #187341 5.89 / on --red #a3231b 7.46
       (이전에는 --accent-strong이 테마 값 그대로여서 다크 테마 인쇄 시 #80b6ff 면이
        흰 종이 위 1.89:1로 사라졌다.) */
    --fill-fg: #fff;
    --green: #187341;
    --green-soft: #eff9f3;
    --red: #a3231b;
    --red-bright: #8c1e17;
    --red-soft: #fdeceb;
  }
}
