/* ============================================================
   global.css — 전역 리셋 + 디자인 토큰(:root) + 공통 컴포넌트 스킨
   (모든 페이지에 적용. app/layout.js 의 정적 <link> 로 1회만 로드)

   구역 지도
   01. 리셋 / 베이스
   02. :root 디자인 토큰 (색·라운드·그림자·간격·치수)
   03. 앱 셸 (.app-shell / .app-main / .app-inner)
   04. 앱 헤더 (.app-header)
   05. 하단 탭바 (.tabbar)
   06. 카드 (.card)
   07. 버튼 (.btn)
   08. 입력 필드 (.field)
   09. 배지 / 칩 (.badge-grade, .chip)
   10. 안내 박스 (.info-box)
   11. 토스트 (.toast)
   12. 로딩 / 빈 상태 (.spinner, .empty-state)
   13. 공통 유틸 (.table-scroll, .divider-text, .sr-only)
   13-1. [chart-tooltip] 차트 툴팁 공통 (recharts — 레이어 · 타이포)
   14. 인쇄 공통
   15. [design-polish] 은은한 모션 (버튼 피드백 · 토스트 등장/퇴장)

   폰트 규칙(R4): 모든 font-size 는 clamp(min, calc(Apx + Bvw), max).
   기준 뷰포트 390px(모바일) ~ 1280px(노트북). B = (max-min)/8.9 vw.
   각 선언에 `/* 390px→Npx, 1280px→Mpx *​/` 기준 주석을 남긴다.
   색·라운드·그림자는 아래 :root 토큰을 var() 로 참조한다(하드코딩 금지).
   ============================================================ */


/* ===== 01. 리셋 / 베이스 ===== */

/* Pretendard 셀프 호스팅 (2026-08-19 — 다른 PC에서 기본 폰트로 보인 문제).
   종전에는 @font-face 가 없어 "OS에 설치된 경우"에만 Pretendard 가 나왔다.
   가변 폰트 1파일이 45~920 전 굵기를 담당한다(별도 굵기 파일 불필요).
   'Pretendard Variable' 은 폰트 스택의 두 번째 이름과도 맞도록 별칭으로 함께 선언. */
@font-face {
  font-family: 'Pretendard';
  src: url('/fonts/PretendardVariable.woff2') format('woff2-variations');
  font-weight: 45 920;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Pretendard Variable';
  src: url('/fonts/PretendardVariable.woff2') format('woff2-variations');
  font-weight: 45 920;
  font-style: normal;
  font-display: swap;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--bg-app);
  color: var(--text);
  font-family: 'Pretendard', 'Pretendard Variable', system-ui, -apple-system,
    BlinkMacSystemFont, 'Segoe UI', 'Apple SD Gothic Neo', 'Malgun Gothic',
    'Noto Sans KR', sans-serif;
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4,
h5,
p,
figure {
  margin: 0;
}

img,
svg {
  max-width: 100%;
  vertical-align: middle;
}

a {
  color: inherit;
  text-decoration: none;
}

button,
input,
select,
textarea {
  font-family: inherit;
  color: inherit;
}

button {
  cursor: pointer;
  border: 0;
  background: none;
}

button:disabled {
  cursor: not-allowed;
}

:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
}


/* ===== 02. :root 디자인 토큰 ===== */
:root {
  /* 배경 / 면 — 흰 카드 위 옅은 블루 바탕 (2026-08-18 사용자 지정 #f0f6fd,
     종전 라벤더빛 회색 #EEF0F7) */
  --bg-app: #f0f6fd;
  --bg-soft: #F6F7FB;
  --surface: #FFFFFF;
  --surface-soft: #F8FAFF;
  --surface-sunken: #F1F4FA;

  /* 선 */
  --line: #dadeeb;
  --line-strong: #D3D9E8;

  /* 글자 */
  --text: #1B2436;
  --text-sub: #5A6579;
  --text-mute: #848d9e;
  --text-invert: #FFFFFF;

  /* 브랜드 블루 — 홈·문진·측정·관리자 기본색 (시안 07·12) */
  --blue: #2563EB;
  --blue-dark: #1D4ED8;
  --blue-soft: #E8F0FE;
  --blue-line: #C7DBFB;

  /* 보라 — 로그인/회원가입 주버튼, 사용자 앱 액센트 (시안 01) */
  --purple: #6366F1;
  --purple2: #A855F7;
  --purple-deep: #4F46E5;
  --purple-soft: #F1F2FE;
  --purple-line: #DCDDFB;
  --grad-primary: linear-gradient(90deg, #6366F1 0%, #A855F7 100%);
  --grad-primary-hover: linear-gradient(90deg, #4F46E5 0%, #9333EA 100%);

  /* 등급 4색 — normal(정상) / attention(관심) / caution(주의) / consult(상담필요) */
  --grade-normal: #16A34A;
  --grade-normal-bg: #DCFCE7;
  --grade-normal-line: #B7EBC8;
  --grade-attention: #CA8A04;
  --grade-attention-bg: #FEF9C3;
  --grade-attention-line: #F0E39A;
  --grade-caution: #EA580C;
  --grade-caution-bg: #FFEDD5;
  --grade-caution-line: #FBD3AC;
  --grade-consult: #DC2626;
  --grade-consult-bg: #FEE2E2;
  --grade-consult-line: #F8C2C2;

  /* 상태 */
  --success: #16A34A;
  --success-bg: #DCFCE7;
  --warn: #CA8A04;
  --warn-bg: #FEF9C3;
  --danger: #DC2626;
  --danger-bg: #FEE2E2;
  --focus-ring: #93B4FB;

  /* 차트 계열 (recharts 에서 var() 참조) — 측정이력 추이 선 색.
     [chart-palette] 2026-08-16: 관리자 팔레트(admin.css U0)와 같은 기준으로 다시 골랐다.
     · 혈압은 한 차트에 2선(수축기·이완기)이 겹치는 유일한 그래프다. 구 조합
       #EF4444/#3B82F6 은 색으로는 갈렸지만(정상 ΔE 35.2) 회색조 L* 차가 0.7 —
       흑백으로 뽑으면 두 선이 완전히 같은 회색이었다. 새 조합은 L* 33.2 / 46.1 로
       12.9 벌어지고, 색각이상 ΔE 30.8 · 정상 ΔE 34.9 를 유지한다.
     · 점 위 값 라벨이 선 색을 그대로 쓰므로 대비도 함께 올렸다
       (수축기 8.31:1 · 이완기 5.17:1 — 구 조합은 3.76:1 로 본문 기준 미달이었다).
     · 선은 색 외에 범례·값 라벨·상하 위치로도 구분된다(색 단독 의존 금지). */
  --chart-weight: #2563EB;
  /* 체중·BMI — 단일 계열이라 팔레트 1번색 */
  --chart-systolic: #d13e30;
  /* [2026-08-18] #991B1B 딥레드가 빨강으로 안 읽힘 — 선명한 적색으로 */
  /* 수축기 — 진한 적색(혈압=빨강 관습 유지, 흑백에서 가장 진한 선) */
  --chart-diastolic: #2563EB;
  /* 이완기 */
  --chart-glucose: #6B21A8;
  /* 혈당·체지방률 — 단일 계열, 체중 차트와 카드가 달라 색이 겹치지 않는다 */

  /* [dash-v2 2026-08-27] 변화 탭 신규 3항목 + 대시보드 통합 추이 차트용 3색.
     기존 4색(청 #2563EB · 적 #d13e30 · 보라 #6B21A8)과 색상환에서 겹치지 않는 자리만 골랐다.
     · 점 위 값 라벨이 선 색을 그대로 쓰므로 흰 배경 대비 4.5:1 이상을 하한으로 뒀다
       (기존 --chart-systolic 이 4.72:1 — 그 급을 맞춘다).
     · 세 색이 한 차트에 함께 서는 경우는 없다(통합 추이 = 적·보라·청록 3선, 나머지는 카드별 1선). */
  --chart-spo2: #067A55;
  /* 산소포화도 — 청록. plan 제안 #0E9F6E 는 대비 3.39:1 로 값 라벨 기준 미달이라
     같은 색상(hue 162°)에서 명도만 낮춰 5.35:1 로 맞췄다 */
  --chart-muscle: #B45309;
  /* 골격근량 — 구리/앰버(5.02:1). 근조직의 붉은 갈색 연상, 적색(혈압)과는 다른 카드에만 선다 */
  --chart-water: #0E7490;
  /* 체수분 — 시안(5.36:1). 물의 관습색이고 체중 청색(#2563EB)과는 다른 카드에만 선다 */
  --chart-body-fat: #BE185D;
  /* [2026-08-27] 체지방률 전용 — 마젠타(6.04:1). 종전에는 전용 토큰이 없어 변화 탭이
     --chart-glucose(보라)를 빌려 썼는데, 대시보드 추이 3행에 체지방률이 들어오며 혈당 행과
     같은 색이 되는 충돌이 생겼다(frontend 는 임시로 --chart-muscle 을 빌렸음). 같은 지표가
     화면마다 다른 색이 되지 않게 전용 색을 신설 — 주홍(혈압)·보라(혈당)·구리(골격근)와
     색상환에서 전부 갈린다. 변화 탭·추이 카드 두 곳이 이 토큰 하나를 참조한다. */

  --chart-grid: #E7EAF3;

  /* 차트 툴팁 — 관리자·사용자 앱 전 차트 공용 정본.
     실제 숫자는 여기 한 곳에만 두고, chartTheme.js 의 TOOLTIP_* 상수는
     var() 문자열로 이 토큰을 참조한다(값이 두 군데로 갈라지지 않게).
     크기 근거: 시니어·현장 담당자가 보는 화면이라 데스크톱 최소 14px 이상.
     조각 라벨(도넛 안 "3 / 13.6%" = 15px/12px)보다 작아 보이지 않도록 올렸다. */
  --chart-tip-fs: clamp(14px, calc(13.12px + 0.225vw), 16px);
  /* 390px→14px, 1280px→16px — 툴팁 본문(항목명) 기준 */
  --chart-tip-label-fs: clamp(14.5px, calc(13.62px + 0.225vw), 16.5px);
  /* 390px→14.5px, 1280px→16.5px — 머리 라벨(날짜·월) */
  --chart-tip-value-fs: clamp(14.5px, calc(13.4px + 0.281vw), 17px);
  /* 390px→14.5px, 1280px→17px — 값. 항목명보다 한 단계 크고 굵게(위계).
     [2026-08-18 사용자 지시] 1px 축소(18→17) — 전 차트 공통 */

  /* 차트 툴팁 레이어 — 카드 안 오버레이(도넛 중앙 라벨 등) 위,
     앱 헤더(관리자 상단바 10 · 탭바 20)와 토스트(60)·모달(80) 아래.
     recharts 는 툴팁 래퍼에 z-index 를 넣지 않아 DOM 순서로만 겹쳐 그려진다 —
     그래서 도넛 중앙 라벨(뒤에 오는 형제)이 툴팁을 덮었다. */
  --z-chart-tip: 6;

  /* 라운드 — 시안의 큰 라운드 카드 */
  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 18px;
  --r-xl: 24px;
  --r-pill: 999px;

  /* 그림자 */
  --shadow-card: 0 6px 20px rgba(23, 43, 77, .06);
  --shadow-pop: 0 14px 36px rgba(23, 43, 77, .16);
  --shadow-side: 0 0 24px rgba(23, 43, 77, .06);

  /* 간격 */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 20px;
  --sp-6: 24px;
  --sp-8: 32px;

  /* 치수 */
  --header-h: 62px;
  --tabbar-h: 68px;
  --side-w: 248px;
  --content-max: 1500px;
  --app-max: 1100px;
  --tap-min: 44px;
  /* 터치 타깃 최소 높이 */
}


/* ===== 02-1. 페이지 전환 효과 (사용자 앱 — app/(user)/template.js 가 감싼다) ===== */
/* [design-css] fill-mode 는 backwards — both(=forwards 포함) 로 두면 애니메이션이 끝난 뒤에도
   to 키프레임의 transform(=항등 행렬)이 남아 이 요소가 position:fixed 의 컨테이닝 블록이 된다.
   그러면 안쪽 모달(.modal-backdrop)이 뷰포트가 아니라 페이지 전체 높이 기준으로 중앙 정렬돼
   스크롤된 긴 페이지에서 모달이 화면 위로 잘려 나갔다. to 값이 요소의 기본 상태와 같으므로
   forwards 를 빼도 보이는 결과는 동일하다. */
.page-enter {
  animation: page-enter .22s ease-out backwards;
}

@keyframes page-enter {
  from {
    opacity: 0;
    transform: translateY(6px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

/* 탭 본문 전환 효과 — 측정이력 등에서 key 교체로 리마운트될 때 재생 */
.tab-panel-enter {
  animation: page-enter .2s ease-out backwards;
}

/* 모션 민감 사용자는 전환 애니메이션 생략 */
@media (prefers-reduced-motion: reduce) {

  .page-enter,
  .tab-panel-enter {
    animation: none;
  }
}


/* ===== 03. 앱 셸 ===== */
/* 노트북(1366×768)에서 로그인·홈이 세로 스크롤 없이 들어가도록 100dvh flex 컬럼 */
.app-shell {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  background: var(--bg-app);
}

.app-main {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: var(--sp-2) var(--sp-4) var(--sp-6);
}

.app-inner {
  width: 100%;
  max-width: var(--app-max);
  margin: 0 auto;
}

.app-shell.has-tabbar .app-main {
  padding-bottom: calc(var(--tabbar-h) + var(--sp-4));
}

@media (max-width: 480px) {
  .app-main {
    padding: var(--sp-4) var(--sp-3) var(--sp-5);
  }
}


/* ===== 04. 앱 헤더 ===== */
/* [design-css] 자식이 로고·내비·회원영역 3개로 늘어 space-between 대신
   회원영역만 오른쪽으로 밀어낸다(내비가 없을 때도 배치는 종전과 동일). */
.app-header {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  height: var(--header-h);
  /* 배경 바는 풀폭 유지, 내부 콘텐츠(로고·회원명)만 1500px 중앙 정렬 */
  padding: 0 max(var(--sp-4), calc((100% - var(--content-max)) / 2));
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

/* 로고 전체가 홈 링크 */
.app-header .brand {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-width: 0;
  cursor: pointer;
}

.app-header .brand-mark {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  border-radius: var(--r-sm);
  background: var(--blue);
  color: var(--text-invert);
  font-size: clamp(16px, calc(15.12px + 0.225vw), 18px);
  /* 390px→16px, 1280px→18px */
}

.app-header .brand-name {
  font-size: clamp(17px, calc(14.8px + 0.562vw), 22px);
  /* 390px→17px, 1280px→22px */
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.app-header .user-area {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-left: auto;
}

.app-header .user-name {
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 700;
  color: var(--text-sub);
  white-space: nowrap;
}

.app-header .logout-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 38px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--text-sub);
  font-size: clamp(12px, calc(10.7px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
  font-weight: 700;
  transition: background .15s ease, border-color .15s ease;
}

.app-header .logout-btn:hover {
  background: var(--surface-sunken);
  border-color: var(--text-mute);
}

@media (max-width: 480px) {
  .app-header .user-name {
    display: none;
  }
}


/* ===== 05. 하단 탭바 (홈 / 측정이력 / 설정) ===== */
.tabbar {
  position: sticky;
  bottom: 0;
  z-index: 20;
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  height: var(--tabbar-h);
  background: var(--surface);
  border-top: 1px solid var(--line);
  /* 배경 바는 풀폭 유지, 탭 3개만 1500px 중앙 정렬 */
  padding-left: max(0px, calc((100% - var(--content-max)) / 2));
  padding-right: max(0px, calc((100% - var(--content-max)) / 2));
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

.tabbar .tab-item {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--tap-min);
  color: var(--text-mute);
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 700;
  transition: color .15s ease;
}

.tabbar .tab-icon {
  font-size: clamp(20px, calc(17.37px + 0.674vw), 26px);
  /* 390px→20px, 1280px→26px */
  line-height: 1;
}

.tabbar .tab-item.is-active {
  color: var(--blue);
}

.tabbar .tab-item.is-active::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 22%;
  right: 22%;
  height: 3px;
  border-radius: var(--r-pill);
  background: var(--blue);
}

.tabbar .tab-item.is-purple.is-active {
  color: var(--purple-deep);
}

.tabbar .tab-item.is-purple.is-active::before {
  background: var(--purple-deep);
}

@media (max-width: 380px) {
  .tabbar .tab-item {
    flex-direction: column;
    gap: 2px;
  }
}


/* ===== 06. 카드 ===== */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-card);
  padding: var(--sp-5);
}

.card.is-flat {
  box-shadow: none;
}

.card.is-soft {
  background: var(--surface-soft);
}

.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}

.card-title {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: clamp(17px, calc(14.8px + 0.562vw), 22px);
  /* 390px→17px, 1280px→22px */
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--text);
}

.card-desc {
  margin-top: var(--sp-1);
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 500;
  color: var(--text-sub);
}

.card-body {
  min-width: 0;
}

@media (max-width: 480px) {
  .card {
    border-radius: var(--r-lg);
    padding: var(--sp-4);
  }
}


/* ===== 07. 버튼 ===== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--tap-min);
  padding: 0 var(--sp-5);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: var(--surface-sunken);
  color: var(--text);
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 700;
  letter-spacing: -.02em;
  transition: background .15s ease, border-color .15s ease, box-shadow .15s ease,
    opacity .15s ease;
}

.btn:disabled {
  opacity: .45;
}

/* 로그인·회원가입 주버튼 — 시안 01 의 보라→마젠타 그라데이션 */
.btn-primary {
  background: var(--grad-primary);
  color: var(--text-invert);
  box-shadow: 0 6px 16px rgba(99, 102, 241, .28);
}

.btn-primary:hover:not(:disabled) {
  background: var(--grad-primary-hover);
}

/* 홈·문진·측정·조회 버튼 — 시안 07·12 의 블루 */
.btn-blue {
  background: var(--blue);
  color: var(--text-invert);
}

.btn-blue:hover:not(:disabled) {
  background: var(--blue-dark);
}

.btn-outline {
  background: var(--surface);
  border-color: var(--purple);
  color: var(--purple-deep);
}

.btn-outline:hover:not(:disabled) {
  background: var(--purple-soft);
}

.btn-outline-blue {
  background: var(--surface);
  border-color: var(--blue-line);
  color: var(--blue);
}

.btn-outline-blue:hover:not(:disabled) {
  background: var(--blue-soft);
}

.btn-ghost {
  background: transparent;
  color: var(--text-sub);
}

.btn-ghost:hover:not(:disabled) {
  background: var(--surface-sunken);
}

.btn-danger {
  background: var(--danger);
  color: var(--text-invert);
}

.btn-block {
  width: 100%;
}

/* 시니어 대상 큰 버튼 */
.btn-lg {
  min-height: 58px;
  padding: 0 var(--sp-6);
  border-radius: var(--r-lg);
  font-size: clamp(18px, calc(15.37px + 0.674vw), 24px);
  /* 390px→18px, 1280px→24px */
  font-weight: 700;
}

.btn-sm {
  min-height: 36px;
  padding: 0 var(--sp-3);
  border-radius: var(--r-sm);
  font-size: clamp(12px, calc(10.7px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
}


/* ===== 08. 입력 필드 ===== */
.field {
  display: block;
  margin-bottom: var(--sp-5);
}

.field-label {
  display: block;
  margin-bottom: var(--sp-2);
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--text);
}

.input-wrap {
  position: relative;
  display: flex;
  align-items: center;
}

.input-wrap .input-icon {
  position: absolute;
  left: var(--sp-4);
  display: grid;
  place-items: center;
  pointer-events: none;
  color: var(--purple);
  font-size: clamp(18px, calc(15.37px + 0.674vw), 24px);
  /* 390px→18px, 1280px→24px */
}

.input-wrap.has-icon .field-input {
  padding-left: 52px;
}

.field-input {
  width: 100%;
  min-height: 58px;
  padding: 0 var(--sp-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  font-size: clamp(17px, calc(14.8px + 0.562vw), 22px);
  /* 390px→17px, 1280px→22px */
  font-weight: 600;
  letter-spacing: -.02em;
  color: var(--text);
  transition: border-color .15s ease, box-shadow .15s ease;
}

.field-input::placeholder {
  color: var(--text-mute);
  font-weight: 500;
}

.field-input:focus {
  outline: none;
  border-color: var(--purple);
  box-shadow: 0 0 0 4px rgba(99, 102, 241, .14);
}

.field-input:disabled {
  background: var(--surface-sunken);
  color: var(--text-mute);
}

.field.is-error .field-input {
  border-color: var(--danger);
}

.field-hint {
  margin-top: var(--sp-2);
  font-size: clamp(12px, calc(10.7px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
  font-weight: 500;
  color: var(--text-mute);
}

.field-error {
  margin-top: var(--sp-2);
  font-size: clamp(12px, calc(10.7px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
  font-weight: 700;
  color: var(--danger);
}


/* ===== 09. 배지 / 칩 ===== */
/* 등급 4색 — 색 + 아이콘 + 텍스트 3중 표기(시니어 친화). 아이콘은 JSX 에서 주입 */
.badge-grade {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 30px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  color: var(--text-sub);
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 800;
  letter-spacing: -.02em;
}

.badge-grade.is-normal {
  background: var(--grade-normal-bg);
  border-color: var(--grade-normal-line);
  color: var(--grade-normal);
}

.badge-grade.is-attention {
  background: var(--grade-attention-bg);
  border-color: var(--grade-attention-line);
  color: var(--grade-attention);
}

.badge-grade.is-caution {
  background: var(--grade-caution-bg);
  border-color: var(--grade-caution-line);
  color: var(--grade-caution);
}

.badge-grade.is-consult {
  background: var(--grade-consult-bg);
  border-color: var(--grade-consult-line);
  color: var(--grade-consult);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--blue-line);
  border-radius: var(--r-pill);
  background: var(--blue-soft);
  color: var(--blue-dark);
  font-size: clamp(12px, calc(10.7px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
  font-weight: 700;
}


/* ===== 10. 안내 박스 ===== */
.info-box {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border-radius: var(--r-lg);
  background: var(--purple-soft);
  color: var(--text-sub);
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 600;
  line-height: 1.6;
}

.info-box .info-icon {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--purple-deep);
  color: var(--text-invert);
  font-size: clamp(16px, calc(15.12px + 0.225vw), 18px);
  /* 390px→16px, 1280px→18px */
  font-weight: 700;
}

.info-box .info-list {
  margin: 0;
  padding-left: 18px;
  display: grid;
  gap: 6px;
}

.info-box.is-blue {
  background: var(--blue-soft);
}

.info-box.is-blue .info-icon {
  background: var(--blue);
}

.info-box .info-title {
  margin-bottom: var(--sp-1);
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 700;
  color: var(--blue-dark);
}


/* ===== 11. 토스트 ===== */
/* 상단 알림 (사용자 지시 2026-08-16 — 하단 탭바·OS 작업표시줄에 가리던 자리에서 위로).
   헤더 아래에 뜨도록 top 만 잡는다(헤더 z-index 보다 위). */
.toast {
  position: fixed;
  left: 50%;
  top: calc(var(--header-h, 64px) + var(--sp-4));
  z-index: 60;
  transform: translateX(-50%);
  display: grid;
  gap: var(--sp-2);
  width: min(560px, calc(100% - 32px));
  pointer-events: none;
}

.toast .toast-item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  background: rgba(27, 36, 54, .94);
  color: var(--text-invert);
  box-shadow: var(--shadow-pop);
  font-size: clamp(14px, calc(12.25px + 0.449vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 700;
}

.toast .toast-item.is-success {
  background: var(--success);
}

.toast .toast-item.is-error {
  background: var(--danger);
}


/* ===== 12. 로딩 / 빈 상태 ===== */
.spinner {
  display: inline-block;
  width: 22px;
  height: 22px;
  border: 3px solid var(--line-strong);
  border-top-color: var(--blue);
  border-radius: 50%;
  animation: spin .7s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.empty-state {
  display: grid;
  place-items: center;
  gap: var(--sp-2);
  padding: var(--sp-8) var(--sp-4);
  color: var(--text-mute);
  font-size: clamp(14px, calc(12.25px + 0.449vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 600;
  text-align: center;
}

.empty-state .empty-icon {
  font-size: clamp(32px, calc(27.25px + 1.348vw), 44px);
  /* 390px→32px, 1280px→44px */
}


/* ===== 13. 공통 유틸 ===== */
/* 넓은 표를 감싸는 가로 스크롤 래퍼 — 표 자체 시각값은 건드리지 않는다 */
.table-scroll {
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.divider-text {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin: var(--sp-5) 0;
  color: var(--text);
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 700;
}

.divider-text::before,
.divider-text::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--line);
}

/* ===== 맨 위로 버튼 (2026-08-21 사용자 지시 — 긴 화면 공용) ===== */
.scroll-top-btn {
  position: fixed;
  right: 22px;
  bottom: 26px;
  z-index: 40;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  width: 52px;
  height: 52px;
  border: 1px solid var(--blue-line);
  border-radius: 50%;
  background: var(--surface);
  color: var(--blue);
  box-shadow: var(--shadow-pop);
  font-size: 18px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}

.scroll-top-btn:hover {
  background: var(--blue-soft);
}

.scroll-top-btn .scroll-top-text {
  font-size: 11px;
  font-weight: 700;
}

/* 사용자 앱 하단 탭바 위로 띄운다 */
.app-shell.has-tabbar .scroll-top-btn {
  bottom: calc(var(--tabbar-h) + 18px);
}

/* 모바일 — 더 작게, 화면 우측 가장자리에 바짝 (사용자 지시 2026-08-21) */
@media (max-width: 640px) {
  .scroll-top-btn {
    right: 8px;
    width: 42px;
    height: 42px;
    font-size: 15px;
  }

  .scroll-top-btn .scroll-top-text {
    font-size: 9.5px;
  }

  .app-shell.has-tabbar .scroll-top-btn {
    bottom: calc(var(--tabbar-h) + 12px);
  }
}

@media print {
  .scroll-top-btn {
    display: none;
  }
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}


/* ============================================================
   13-1. [chart-tooltip] 차트 툴팁 공통 (recharts) — 2026-08-17
   ------------------------------------------------------------
   관리자 대시보드·회원상세·사용자 앱 측정이력의 모든 recharts 차트가 함께 쓴다.
   global.css 는 루트 레이아웃이 전역 로드하므로 두 구역 모두에 걸린다.

   [증상] 관리자 "지역별 참여현황" 도넛에서 조각에 마우스를 올리면 툴팁이
          가운데 라벨("전국 / 37명 / (시군구 27곳)") **뒤에** 깔려 글자가 겹쳤다.

   [근본 원인] recharts 는 툴팁 래퍼(.recharts-tooltip-wrapper)에 position:absolute
          만 인라인으로 넣고 z-index 는 넣지 않는다(= z-index:auto). 도넛 중앙 라벨
          .adm-donut-center 도 position:absolute · z-index:auto 이고, 같은 부모
          .adm-donut-wrap 안에서 **툴팁보다 뒤(DOM 순서상 나중)** 에 온다.
          z-index 가 둘 다 auto 면 겹침 순서는 DOM 순서로 결정되므로 중앙 라벨이
          항상 툴팁 위에 그려졌다. 화면별 z-index 덧칠이 아니라, 툴팁 래퍼에
          레이어를 한 번 부여하는 것이 정답이다(아래 한 줄이 전 차트에 적용된다).

   [주의] .recharts-wrapper / .adm-donut-wrap 은 position:relative 이지만
          z-index:auto 라 쌓임 맥락을 만들지 않는다 — 그래서 이 z-index 가
          중앙 라벨과 같은 쌓임 맥락에서 직접 비교되어 의도대로 위로 온다.
   ============================================================ */
.recharts-tooltip-wrapper {
  z-index: var(--z-chart-tip);
  /* recharts 가 인라인으로도 넣지만, 커서 아래 조각의 hover 가 끊기지 않게 명시한다 */
  pointer-events: none;
}

/* 툴팁 상자 — contentStyle(chartTheme.TOOLTIP_STYLE)을 넘기지 않는 차트도
   같은 모양·같은 글자 크기가 되도록 여기서 한 번 더 잡는다.
   (인라인 스타일이 우선하지만 두 값 모두 같은 토큰을 보므로 결과가 일치한다) */
.recharts-default-tooltip {
  font-size: var(--chart-tip-fs);
  line-height: 1.35;
}

.recharts-tooltip-label {
  font-size: var(--chart-tip-label-fs);
  font-weight: 700;
}

.recharts-tooltip-item {
  font-size: var(--chart-tip-fs);
}

/* 항목명 : 값 위계 — 값을 한 단계 크고 굵게(굵기 700 상한 준수).
   name/separator/value/unit 스팬에는 recharts 가 인라인 스타일을 주지 않아
   CSS 로만 잡힌다(= 차트마다 따로 줄 필요가 없는 유일한 지점). */
.recharts-tooltip-item-name {
  font-weight: 600;
}

.recharts-tooltip-item-separator {
  opacity: .65;
}

.recharts-tooltip-item-value,
.recharts-tooltip-item-unit {
  font-size: var(--chart-tip-value-fs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}


/* ===== 14. 인쇄 공통 ===== */
@media print {
  body {
    background: #fff;
  }

  .app-header,
  .tabbar,
  .toast,
  .no-print {
    display: none !important;
  }

  .app-main,
  .app-shell.has-tabbar .app-main {
    overflow: visible;
    padding: 0;
  }
}


/* ============================================================
   15. [design-polish] 은은한 모션
   원칙: 0.15~0.25s, 콘텐츠(차트·리포트 본문)에는 걸지 않는다.
   블록 끝의 prefers-reduced-motion 에서 전부 끈다.
   ============================================================ */

/* ===== [design-polish] 버튼 피드백 ===== */
/* 07 블록의 transition 에 transform·filter 를 더한다(색·굵기 등 시각값은 그대로) */
.btn {
  transition: background .18s ease, border-color .18s ease, box-shadow .18s ease,
    opacity .18s ease, filter .18s ease, transform .12s ease;
}

/* 눌림 — 스케일 .98 은 44px 버튼에서 약 1px 이라 "살짝 들어간다" 정도로만 읽힌다 */
.btn:active:not(:disabled) {
  transform: scale(.98);
}

/* 터치 기기는 hover 가 눌린 상태로 남으므로 제외 */
@media (hover: hover) {

  .btn-primary:hover:not(:disabled) {
    filter: brightness(1.03);
    box-shadow: 0 8px 22px rgba(99, 102, 241, .34);
  }

  .btn-blue:hover:not(:disabled) {
    box-shadow: 0 6px 18px rgba(37, 99, 235, .26);
  }

  .btn-danger:hover:not(:disabled) {
    filter: brightness(1.05);
    box-shadow: 0 6px 18px rgba(220, 38, 38, .26);
  }

  .btn-outline:hover:not(:disabled),
  .btn-outline-blue:hover:not(:disabled) {
    box-shadow: 0 4px 12px rgba(23, 43, 77, .10);
  }
}


/* ===== [design-polish] 토스트 등장 · 퇴장 ===== */
/* 화면 위(헤더 아래)에 뜨므로 위에서 내려온다 (2026-08-16 위치 이동에 맞춤) */
.toast .toast-item {
  animation: toast-in .2s ease-out both;
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(-12px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

/* ToastHost 가 제거 직전 .is-leaving 을 붙인다 — 사라질 때는 페이드만 */
.toast .toast-item.is-leaving {
  animation: toast-out .18s ease-in both;
}

@keyframes toast-out {
  from {
    opacity: 1;
    transform: none;
  }

  to {
    opacity: 0;
    transform: translateY(4px);
  }
}


/* ===== [design-polish] 모션 민감 사용자 ===== */
@media (prefers-reduced-motion: reduce) {

  .btn {
    transition: background .18s ease, border-color .18s ease, opacity .18s ease;
  }

  .btn:active:not(:disabled) {
    transform: none;
  }

  .toast .toast-item,
  .toast .toast-item.is-leaving {
    animation: none;
  }
}


/* ===== [design-css] 내비 반응형 분기 (데스크톱=상단 헤더 / 모바일=하단 탭바) ===== */
/* 같은 메뉴를 두 벌 마운트하고 폭으로만 갈라 보여준다.
   경계 700px — 태블릿 세로(768px)부터는 상단 내비가 읽기 편하고,
   데스크톱에서 탭바가 빠지는 만큼 세로 여유(68px)가 생긴다. */
/* [design-css] 내비는 헤더 정중앙(사용자 지시 2026-08-13) — 절대 배치라
   로고·회원영역(로그인 전처럼 없는 경우 포함) 폭과 무관하게 항상 화면 중앙 */
.app-header {
  position: relative;
}

.app-header .app-nav {
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: var(--sp-6);
}

.app-header .nav-item {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 100%;
  min-height: var(--tap-min);
  padding: 0 var(--sp-4);
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  color: var(--text-sub);
  font-size: clamp(14px, calc(12.24px + 0.45vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 700;
  letter-spacing: -.02em;
  white-space: nowrap;
  transition: color .15s ease, background .15s ease;
}

.app-header .nav-icon {
  font-size: clamp(15px, calc(13.68px + 0.337vw), 18px);
  /* 390px→15px, 1280px→18px */
  line-height: 1;
}

@media (hover: hover) {
  .app-header .nav-item:hover {
    background: var(--surface-sunken);
    color: var(--text);
  }
}

.app-header .nav-item.is-active {
  color: var(--blue);
  font-weight: 700;
}

/* 헤더 아래 테두리를 덮는 활성 밑줄 */
.app-header .nav-item.is-active::after {
  content: "";
  position: absolute;
  right: var(--sp-2);
  bottom: -1px;
  left: var(--sp-2);
  height: 3px;
  border-radius: var(--r-pill) var(--r-pill) 0 0;
  background: var(--blue);
}

/* 데스크톱 — 하단 탭바를 감춘다(높이도 함께 사라져 본문이 그만큼 넓어진다) */
/* [design-css 2026-08-14] `screen and` 로 한정 — 인쇄 지면(A4 세로)의 레이아웃 뷰포트는
   210mm = 793.7px 라 폭 조건만 쓰면 인쇄에도 매칭돼, 여기의 아래 여백(24px)이
   리포트 뒤에 남아 거의 빈 마지막 장이 한 장 더 나왔다. 인쇄 여백은 위 14 구역이 잡는다. */
@media screen and (min-width: 701px) {

  .tabbar {
    display: none;
  }

  /* 탭바 자리로 비워 두던 아래 여백·띄움 높이를 되돌린다 */
  .app-shell.has-tabbar .app-main {
    padding-bottom: var(--sp-8);
  }

  /* 상단 알림은 데스크톱과 같은 자리 — 탭바가 없는 폭이라 별도 보정이 필요 없다 */
}

/* (.arc-refresh 는 main.css 정의라 같은 분기를 main.css 하단에서 한 번 더 잡는다) */

/* 중간 폭(701~900px) — 헤더 3덩어리가 겹치지 않게 간격만 줄인다(중앙 정렬 유지) */
@media (min-width: 701px) and (max-width: 900px) {
  .app-header .app-nav {
    gap: var(--sp-2);
  }

  .app-header .nav-item {
    padding: 0 var(--sp-3);
  }
}

/* 모바일 — 상단 내비를 감추고 하단 탭바만 남긴다 */
@media (max-width: 700px) {
  .app-header .app-nav {
    display: none;
  }
}