/* ============================================================
   main.css — 사용자 앱 페이지 전용 (로그인/회원가입/문진/홈/측정이력)
   (global.css 다음에 로드. 색·라운드·그림자는 global.css :root 토큰 var() 참조)

   구역 지도
   01. 페이지 공통 (.page-title / .section-label / .state-block)
   02. 히어로 (.hero-card / .hero-banner)
   03. 로그인 (.login-page)
   04. 회원가입 (.signup-page / .pass-card) — 약관 동의 블록은 파일 맨 끝 [signup-agree]
   05. 지역 선택 (.region-picker)
   06. 문진 인트로 (.survey-intro)
   07. 문진 단계 (.survey-page)
   08. 홈 (.home-page / .menu-grid)
   09. 측정이력 셸 (.history-page / .tab-switch / .period-bar)
   10. 대시보드 탭 (.metric-grid / .metric-head — 변화 탭 카드 헤더와 공용)
       ※ 2026-08-27 개편 후 대시보드 탭의 정본은 파일 맨 끝 [dash-v2] 블록이다.
         이 구역의 .metric-grid 계열은 .metric-head 만 변화 탭과 공용으로 살아 있다.
   11. AI 분석 요약 (.ai-summary / .advice-grid) — 2026-08-27 개편으로 화면에서 내려갔다([dash-v2] 참조)
   12. 변화 탭 (.change-grid — 2026-08-27 부터 2열)
   13. 상세리포트 탭 툴바 (.report-toolbar)
   14. 모달 / 최근값 행 (.modal-backdrop / .latest-row)
   15. 인쇄 (리포트만 남기기)
   15-1. 인쇄 — 변화 탭 지면 = 세로 A4 1장 2열 4행 (@page change-page / .change-print-head)
   15-2. 인쇄 — 상세리포트 + 변화 부록 한 번에 4쪽 (.print-appendix)
   ── 이하 에이전트 추가 블록 ──
   [design-polish] 은은한 모션 (모달 · 팝오버 · 선택지 전환) — 파일 맨 끝
   [period-picker] 측정이력 조회기간 — 측정일 선택 (.period-picker) — 파일 맨 끝
   [dash-v2] 측정이력 대시보드 탭 2열 6카드 (.dash-grid) — 파일 맨 끝
   [signup-agree] 회원가입 약관 동의 블록 + 전문보기 모달 (.signup-agree / .legal-modal) — 파일 끝 쪽
     (모달 본문 문서 타이포는 [consent] ② .doc-page 규칙을 셀렉터 확장으로 공유)
   [print-choice] 상세리포트 인쇄 — 변화 페이지 함께 인쇄 선택 모달 (.print-choice-modal) — 파일 끝 쪽, [checkup] 앞
   [inbody] 측정입력(체성분) 인바디 연동 — 불러오기·최근 기록 셀렉트·상세값 패널
     (.form-head-row / .inbody-pick-row / .form-inbody-note / .inbody-detail) — 파일 중간, [ai-cancel] 블록 바로 앞
   [checkup] 국가건강검진 결과 3화면 (.checkup-page / .checkup-list / .checkup-import / .checkup-detail) — 파일 맨 끝

   폰트 규칙(R4): 모든 font-size 는 clamp() + `/* 390px→Npx, 1280px→Mpx *​/` 기준 주석.
   ※ 페이지 에이전트(frontend-dev)는 이 파일 하단에
     `/* ===== [frontend-dev] 블록명 ===== *​/` 로만 추가한다(기존 블록 수정 금지).
   ============================================================ */


/* ===== 01. 페이지 공통 ===== */
.page-title {
  font-size: clamp(30px, calc(23.87px + 1.573vw), 44px);
  /* 390px→30px, 1280px→44px */
  font-weight: 900;
  letter-spacing: -.045em;
  line-height: 1.15;
  color: var(--text);
}

.page-desc {
  margin: var(--sp-2) 0;
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 600;
  color: var(--text-sub);
}

/* 폼 섹션 제목 — "1. 본인인증" 같은 단계 라벨 */
.section-label {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: var(--sp-6) 0 var(--sp-3);
  font-size: clamp(17px, calc(14.8px + 0.562vw), 22px);
  /* 390px→17px, 1280px→22px */
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--text);
}

.section-label::before {
  content: "";
  width: 4px;
  height: 20px;
  border-radius: var(--r-pill);
  background: var(--blue);
}

/* API 미기동·빈 데이터 공용 폴백 (로딩/에러/빈상태 3종) */
.state-block {
  display: grid;
  place-items: center;
  gap: var(--sp-3);
  padding: var(--sp-8) var(--sp-4);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-lg);
  background: var(--surface-soft);
  text-align: center;
  font-size: clamp(14px, calc(12.25px + 0.449vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 600;
  color: var(--text-sub);
}

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

.state-block .state-title {
  font-size: clamp(16px, calc(13.8px + 0.562vw), 21px);
  /* 390px→16px, 1280px→21px */
  font-weight: 700;
  color: var(--text);
}

.state-block.state-error {
  border-color: var(--grade-consult-line);
  background: var(--grade-consult-bg);
  color: var(--grade-consult);
}

.state-block.state-error .state-title {
  color: var(--grade-consult);
}

.state-block.state-loading {
  border-style: solid;
  border-color: var(--line);
}


/* ===== 02. 히어로 ===== */
/* 로그인·회원가입 상단 큰 카드 (시안 01) */
.hero-card {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  overflow: hidden;
  padding: var(--sp-6);
  border-radius: var(--r-xl);
  background: linear-gradient(135deg, var(--surface-soft) 0%, var(--purple-soft) 100%);
}

.hero-card .hero-title {
  font-size: clamp(34px, calc(26.5px + 1.91vw), 51px);
  /* 390px→34px, 1280px→51px */
  font-weight: 900;
  letter-spacing: -.05em;
  line-height: 1.1;
  color: var(--text);
}

.hero-card .hero-desc {
  margin-top: var(--sp-3);
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 600;
  line-height: 1.5;
  color: var(--text-sub);
}

/* [design-css] 일러스트 폭 +28% (clamp(120px,34vw,260px) → 154/43.5vw/333px).
   아래로 남던 카드 여백은 "밀착" 기법으로 제거한다 — 이미지만 카드 바닥에 붙이고
   (align-self:end + 카드 아래 패딩만큼 음수 마진), 텍스트 쪽 패딩은 그대로 둔다.
   .hero-card 는 overflow:hidden 이라 라운드 밖으로 비어져 나오지 않는다. */
.hero-card .hero-art {
  flex: 0 0 auto;
  align-self: flex-end;
  width: clamp(154px, 43.5vw, 333px);
  margin-bottom: calc(var(--sp-6) * -1);
}

/* 측정이력·서브페이지 상단 배너 (시안 12~14) */
/* [design-css] 일러스트를 흐름에서 빼고(absolute) 배너 높이는 min-height 로 고정한다.
   이전에는 이미지가 배너 높이를 결정해서(200×141) 이미지를 키우면 제목·탭·본문이 통째로 밀렸다.
   min-height 값은 "확대 전 이미지 높이"와 같아 아래 레이아웃은 1px 도 움직이지 않는다.
   (이미지 22vw × 세로비 0.7028 = 15.46vw, 상한 200px×0.7028 = 141px) */
.hero-banner {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  /* 사용자 피드백(2026-08-13): 배너 영역 높이 20% 축소 (158→126px, 일러스트도 비례 축소) */
  min-height: clamp(59px, 13.9vw, 125px);
  padding-right: clamp(122px, calc(24.3vw + 16px), 237px);
  margin-bottom: var(--sp-4);
}

.hero-banner .hero-banner-title {
  font-size: clamp(30px, calc(23.87px + 1.573vw), 46px);
  /* 390px→30px, 1280px→44px */
  font-weight: 900;
  letter-spacing: -.045em;
  color: var(--text);
}

.hero-banner .hero-banner-desc {
  margin-top: var(--sp-2);
  font-size: clamp(14px, calc(12.25px + 0.449vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 600;
  color: var(--text-sub);
}

/* [design-css] 확대 이력 clamp(96px,22vw,200px) → +15% → 여기서 +20% (230px → 276px).
   absolute 라 아래 레이아웃은 그대로고, 넘치는 만큼만 min-height 로 흡수한다
   (이미지 세로비 0.7028 → 276px×0.7028 = 194px, min-height 158px 로 위아래 18px 씩 여백에 얹힌다). */
.hero-banner .hero-banner-art {
  position: absolute;
  /* 사용자 피드백(2026-08-13): 이미지가 너무 위에 떠 보여 중앙(50%)보다 아래로 내림 */
  top: 62%;
  right: 0;
  transform: translateY(-50%);
  /* 배너 높이 20% 축소에 맞춰 비례 축소 (276→221px) */
  width: clamp(106px, 24.3vw, 221px);
  pointer-events: none;
}

/* [design-css] 모바일에서도 일러스트를 보여준다(기존 display:none 제거).
   좁은 폭에서는 이미지만 줄이고 가로 2단 배치는 유지 — 390px 기준 텍스트 최소폭 확보. */
@media (max-width: 560px) {

  .hero-card,
  .hero-banner {
    align-items: center;
    gap: var(--sp-3);
  }

  .hero-card {
    padding: var(--sp-4);
  }

  /* [design-css] 모바일도 같은 비율로 +28% (100/26vw/150px → 128/33.3vw/192px) */
  .hero-card .hero-art {
    width: clamp(128px, 33.3vw, 192px);
    margin-bottom: calc(var(--sp-4) * -1);
  }

  .hero-card .hero-desc {
    margin-top: var(--sp-2);
    font-size: clamp(14px, calc(11.81px + 0.562vw), 19px);
    /* 390px→14px, 1280px→19px */
  }

  /* [design-css] 모바일도 데스크톱과 같은 비율로 +20% (기존 clamp(97px, 27.6vw, 150px)).
     여백은 6px 만 둔다 — 390px 에서 설명문 한 줄이 두 줄로 접히면
     배너가 21px 높아져 아래 탭·본문이 밀리기 때문. */
  .hero-banner {
    padding-right: clamp(99px, calc(26.5vw + 6px), 150px);
  }

  .hero-banner .hero-banner-art {
    /* 배너 20% 축소 비례 (180→144px) */
    width: clamp(93px, 26.5vw, 144px);
  }
}


/* ===== 03. 로그인 ===== */
/* 노트북에서 세로 스크롤 없이 한 화면: .app-main 안에서 세로 중앙 정렬 */
/* [design-css] 콘텐츠 영역은 1500px 정책을 따르고(래퍼), 읽기 폭은 카드에서 잡는다 */
.login-page {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: var(--sp-5);
  min-height: 100%;
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;
}

.login-page>.card {
  width: 100%;
  max-width: 820px;
}

.login-page .login-actions {
  display: grid;
  gap: var(--sp-3);
  margin-top: var(--sp-2);
}

.login-page .login-guide {
  margin-top: var(--sp-2);
}


/* ===== 04. 회원가입 ===== */
.signup-page {
  width: 100%;
  /* 사용자 피드백(2026-08-13): 가로 확대 (720 → 1080px) */
  max-width: 1080px;
  margin: 0 auto;
  display: grid;
  gap: var(--sp-5);
}

/* 회원정보 입력 필드 — PC(760px+)에서 한 줄에 2개씩 */
.signup-field-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-4) var(--sp-5);
}

@media (min-width: 760px) {
  .signup-field-grid {
    grid-template-columns: 1fr 1fr;
  }
}

/* [design-css] 카드 안 요소들이 서로 margin 없이 붙어 있었다(지역 필드↔가입 버튼↔안내↔링크).
   리듬을 3단계로 통일한다 — 큰 덩어리 sp-6 / 섹션 제목 위 sp-6 / 입력 사이 sp-5(.field 기본). */
.signup-page>.card {
  display: grid;
  gap: var(--sp-6);
}

/* 섹션 제목의 필수·선택 뱃지 — 측정입력(.measure-user .label-req/.label-opt 2026-08-21)과 같은 모양.
   .section-label 이 flex + gap 이라 좌측 여백은 gap 이 준다(margin-left 0). */
.signup-page .label-req,
.signup-page .label-opt {
  padding: 1px 8px;
  border-radius: var(--r-pill);
  font-size: clamp(11px, calc(10.12px + 0.225vw), 13px);
  /* 390px→11px, 1280px→13px */
  font-weight: 700;
}

.signup-page .label-req {
  background: var(--blue-soft);
  border: 1px solid var(--blue-line);
  color: var(--blue);
}

.signup-page .label-opt {
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  color: var(--text-mute);
}

/* 카드 gap 과 섹션 제목의 자기 여백이 겹치지 않게 폼 첫 제목만 0 */
.signup-page form>.section-label:first-child {
  margin-top: 0;
}

/* 제출 버튼은 입력 영역과 확실히 떼어 놓는다 */
.signup-page form>.btn {
  margin-top: var(--sp-6);
}

/* 본인인증으로 채운 칸 — 잠김(readonly). disabled 와 달리 값이 제출되고 복사도 된다 */
.signup-page .field-input[readonly] {
  background: var(--surface-sunken);
  color: var(--text-sub);
}

.signup-page .field-input[readonly]:focus {
  border-color: var(--line-strong);
  box-shadow: none;
}

/* PASS 본인인증 카드 — NICE 팝업 활성 (2026-09-14, 종전 "추후 제공" 비활성 안내에서 전환)
   구성: .pass-icon(로고) | .pass-text(.pass-title / .pass-desc) | .pass-btn(공용 .btn.btn-primary)
   본인인증은 선택이라 카드가 눈에 띄되 "필수 관문"처럼 보이면 안 된다 — 보라 톤(가입 동선 색)으로 묶는다.
   가져온 shadcn/Tailwind 버튼(색·그림자·라운드)은 R3 위반이라 쓰지 않고 이 블록에서 값을 잡는다. */
.pass-card {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-5);
  border: 1px solid var(--purple-line);
  border-radius: var(--r-lg);
  background: var(--purple-soft);
}

.pass-card .pass-icon {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 52px;
  height: 52px;
  border-radius: var(--r-md);
  background: var(--surface);
  border: 1px solid var(--line);
  font-size: clamp(24px, calc(20.49px + 0.899vw), 32px);
  /* 390px→24px, 1280px→32px */
}

.pass-card .pass-text {
  flex: 1 1 auto;
  min-width: 0;
}

.pass-card .pass-title {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: clamp(16px, calc(13.8px + 0.562vw), 21px);
  /* 390px→16px, 1280px→21px */
  font-weight: 700;
  color: var(--text);
}

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

/* 인증 버튼 — 아이콘 상자(52px)와 같은 높이로 맞추고, 문구가 "시작하기"↔"진행 중…" 으로 바뀌어도
   카드 폭이 흔들리지 않게 최소 폭을 준다. 줄바꿈 금지(종전 파일 하단의 [frontend-dev] 별도 블록을 여기로 모음) */
.pass-card .pass-btn {
  flex: 0 0 auto;
  min-height: 52px;
  min-width: 11em;
  padding: 0 var(--sp-5);
  border-radius: var(--r-md);
  white-space: nowrap;
  font-size: clamp(16px, calc(13.8px + 0.562vw), 21px);
  /* 390px→16px, 1280px→21px — 제목과 같은 크기 */
}

/* 팝업이 열려 있는 동안(진행 중) — 눌린 상태가 아니라 대기 중임이 보이게 옅게만 */
.pass-card .pass-btn:disabled {
  opacity: .6;
  cursor: default;
}

/* 팝업이 열려 있는 동안 안내 — 주의 톤 글자(카드 배경과 대비) */
.pass-card .pass-note {
  margin-top: var(--sp-2);
  font-size: clamp(12px, calc(10.7px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
  font-weight: 700;
  color: var(--grade-attention);
}

/* 인증 완료 — 카드가 초록 톤으로 바뀌고 배지가 붙는다. 색만으로 구분하지 않도록 배지 문구가 뜻을 말한다 */
.pass-card.is-verified {
  background: var(--success-bg);
  border-color: var(--grade-normal-line);
}

.pass-card .pass-badge {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding: 0 var(--sp-2);
  border-radius: var(--r-pill);
  background: var(--success);
  color: var(--text-invert);
  font-size: clamp(11px, calc(9.7px + 0.337vw), 14px);
  /* 390px→11px, 1280px→14px */
  font-weight: 700;
  white-space: nowrap;
}

/* 본인인증으로 확정된 칸의 안내 — 오류(빨강)와 대비되게 초록.
   색만으로 구분하지 않도록 문구("본인인증으로 확인된 …")가 그대로 뜻을 말한다 */
.signup-page .field-hint.is-ok {
  color: var(--success);
  font-weight: 700;
}

/* 640px 이하: 아이콘·텍스트·버튼이 좁게 눌리지 않게 세로 적층 + 버튼은 한 줄 전체
   (종전 480px — 버튼이 커지면서 500px 안팎에서 설명이 5줄로 눌려 640px 로 올림) */
@media (max-width: 640px) {
  .pass-card {
    flex-wrap: wrap;
  }

  /* 기준폭 0 — 아이콘 옆 남은 폭만큼만 차지해 로고와 같은 줄에 남는다(auto 면 로고 혼자 한 줄) */
  .pass-card .pass-text {
    flex: 1 1 0;
  }

  .pass-card .pass-title {
    flex-wrap: wrap;
  }

  .pass-card .pass-btn {
    flex: 1 1 100%;
    min-width: 0;
  }
}


/* ===== 05. 지역 선택 (트리거 + 모달 3단계) ===== */
/* [design-css] 시/도·시군구·동 그리드를 페이지에 세로로 늘어놓던 방식 → 트리거 한 줄 + 모달.
   모달은 한 번에 한 단계만 보여주고 내용이 교체된다(RegionPicker 의 step 상태). */
.region-picker {
  display: grid;
  gap: var(--sp-3);
}

/* 페이지에 남는 유일한 요소 — 선택 전/후 문구가 바뀐다 */
.region-picker .region-trigger {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  min-height: 58px;
  padding: 0 var(--sp-3) 0 var(--sp-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  text-align: left;
  transition: border-color .15s ease, background .15s ease;
}

.region-picker .region-trigger:hover {
  border-color: var(--blue-line);
  background: var(--blue-soft);
}

.region-picker .region-trigger-icon {
  flex: 0 0 auto;
  font-size: clamp(18px, calc(15.37px + 0.674vw), 24px);
  /* 390px→18px, 1280px→24px */
  line-height: 1;
}

.region-picker .region-trigger-text {
  flex: 1 1 auto;
  min-width: 0;
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--text-mute);
}

.region-picker .region-trigger.is-filled .region-trigger-text {
  font-weight: 700;
  color: var(--text);
}

.region-picker .region-trigger-action {
  flex: 0 0 auto;
  padding: 6px var(--sp-3);
  border-radius: var(--r-pill);
  background: var(--blue-soft);
  color: var(--blue-dark);
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 700;
}

/* --- 모달 카드 --- */
/* [frontend-dev 2026-08-12] 카드 높이를 뷰포트에 묶어(height: min(86dvh, 640px)) flex 컬럼으로 쓴다.
   → 헤더는 flex 고정칸이라 언제나 보이고, 스크롤은 본문 안(패널 목록 · 동 그리드)에서만 일어난다.
   종전에는 카드 자체가 늘어나며 헤더(제목·뒤로가기)가 화면 위로 잘려 나갔다. */
.region-modal .region-modal-card {
  display: flex;
  flex-direction: column;
  width: min(820px, 100%);
  height: min(86dvh, 640px);
  max-height: 86dvh;
  overflow: hidden;
}

/* 동이 6개뿐인 군(郡)도 있어 풀그리드 화면은 내용만큼만 쓴다.
   상한(86dvh)은 그대로라 목록이 길면 그리드 안에서 스크롤된다 — 헤더는 항상 보인다. */
.region-modal .region-modal-card.is-dong {
  height: auto;
  min-height: min(58dvh, 400px);
  max-height: min(86dvh, 640px);
}

.region-modal .region-modal-head {
  position: sticky;
  top: 0;
  z-index: 2;
  flex: 0 0 auto;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
}

.region-modal .region-head-text {
  flex: 1 1 auto;
  min-width: 0;
  text-align: center;
}

.region-modal .region-trail {
  margin-top: 2px;
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 700;
  color: var(--blue-dark);
}

/* 제목은 폭에 따라 둘 중 하나만 — 데스크톱은 2패널이라 "지역 선택" 한 줄 */
.region-modal .region-title-narrow {
  display: none;
}

.region-modal .region-trail-narrow {
  display: none;
}

.region-modal .region-back {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: var(--tap-min);
  height: var(--tap-min);
  border-radius: var(--r-sm);
  color: var(--text-sub);
  font-size: clamp(26px, calc(22.5px + 0.899vw), 34px);
  /* 390px→26px, 1280px→34px */
  line-height: 1;
}

.region-modal .region-back:hover {
  background: var(--surface-sunken);
}

.region-modal .region-back.is-hidden {
  visibility: hidden;
}

/* 데스크톱 2패널에서는 시/군/구 단계도 같은 화면이라 뒤로가기가 필요 없다 */
.region-modal .region-modal-card.step-sigungu .region-back {
  visibility: hidden;
  pointer-events: none;
}

/* 본문은 남는 높이를 전부 쓰고, 스크롤은 그 안쪽에서 처리한다 */
.region-modal .region-modal-body {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-rows: minmax(0, 1fr);
  padding: var(--sp-4);
  overflow: hidden;
}

/* --- 2패널 (① 시/도 · ② 시/군/구) --- */
/* [frontend-dev 2026-08-12] 참고 이미지(연도 선택) 방식 — 좌측에서 시/도를 고르면
   우측이 즉시 갱신된다. 시/도 선택 전 우측은 점선 플레이스홀더. */
.region-modal .region-panes {
  display: grid;
  grid-template-columns: minmax(160px, 0.85fr) minmax(0, 1.6fr);
  gap: var(--sp-4);
  min-height: 0;
}

.region-modal .region-pane {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
}

/* ② 패널은 왼쪽에 구분선을 두어 두 단계가 나뉘어 보이게 한다 */
.region-modal .region-pane.is-sigungu {
  padding-left: var(--sp-4);
  border-left: 1px solid var(--line);
}

.region-modal .region-pane-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--line);
}

.region-modal .region-pane-no {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--blue);
  color: var(--text-invert);
  font-size: clamp(12px, calc(10.7px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
  font-weight: 700;
  line-height: 1;
}

.region-modal .region-pane-name {
  font-size: clamp(14px, calc(12.25px + 0.449vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--text);
}

.region-modal .region-pane-pick {
  margin-left: auto;
  overflow: hidden;
  max-width: 55%;
  padding: 3px var(--sp-2);
  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;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* 목록만 스크롤한다 — 서울(구 25개) · 경기(시군구 31개) 도 헤더를 밀지 않는다 */
.region-modal .region-pane-list {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  align-content: start;
  gap: 4px;
  overflow-y: auto;
  padding: 2px;
}

.region-modal .region-pane-list.is-grid {
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: var(--sp-2);
}

/* 패널 안의 로딩·빈·에러 폴백도 남는 높이를 채운다 */
.region-modal .region-pane>.state-block {
  flex: 1 1 auto;
  min-height: 0;
}

.region-modal .region-item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--tap-min);
  padding: 0 var(--sp-3);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--text);
  text-align: left;
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  font-weight: 700;
  letter-spacing: -.02em;
  transition: background .12s ease, border-color .12s ease, color .12s ease;
}

.region-modal .region-item-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.region-modal .region-item-arrow {
  flex: 0 0 auto;
  color: var(--text-mute);
  font-size: clamp(16px, calc(14.25px + 0.449vw), 20px);
  /* 390px→16px, 1280px→20px */
  line-height: 1;
}

.region-modal .region-item:hover {
  border-color: var(--blue-line);
  background: var(--blue-soft);
}

.region-modal .region-item.is-selected {
  border-color: var(--blue);
  background: var(--blue);
  color: var(--text-invert);
}

.region-modal .region-item.is-selected .region-item-arrow {
  color: var(--text-invert);
}

/* 시/도 선택 전의 ② 패널 — 점선 박스 안내 */
.region-modal .region-pane-empty {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: var(--sp-2);
  padding: var(--sp-4);
  border: 2px dashed var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface-soft);
  text-align: center;
}

.region-modal .region-pane-empty-icon {
  font-size: clamp(28px, calc(24.5px + 0.899vw), 36px);
  /* 390px→28px, 1280px→36px */
  line-height: 1;
}

.region-modal .region-pane-empty-text {
  font-size: clamp(14px, calc(12.25px + 0.449vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 700;
  color: var(--text-mute);
}

/* --- ③ 읍/면/동 풀그리드 (참고 이미지의 "월 선택" 화면) --- */
.region-picker .region-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  align-content: start;
  gap: var(--sp-2);
  min-height: 0;
  overflow-y: auto;
  padding: 2px;
}

.region-picker .region-btn {
  min-height: 54px;
  padding: 0 var(--sp-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-soft);
  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 .12s ease, border-color .12s ease, color .12s ease;
}

.region-picker .region-btn:hover {
  border-color: var(--blue-line);
  background: var(--blue-soft);
}

.region-picker .region-btn.is-selected {
  border-color: var(--blue);
  background: var(--blue);
  color: var(--text-invert);
}


/* ===== 06. 문진 인트로 ===== */
.survey-intro {
  display: grid;
  gap: var(--sp-5);
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
}

.survey-intro .intro-steps {
  display: grid;
  gap: var(--sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.survey-intro .intro-step {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 700;
  color: var(--text);
}

.survey-intro .intro-step .step-no {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--blue-soft);
  color: var(--blue);
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 900;
}


/* ===== 07. 문진 단계 ===== */
.survey-page {
  display: grid;
  gap: var(--sp-5);
  width: 100%;
  max-width: 820px;
  margin: 0 auto;
}

.survey-page .survey-progress {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 700;
  color: var(--blue);
}

.survey-page .progress-track {
  flex: 1 1 auto;
  height: 10px;
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  overflow: hidden;
}

.survey-page .progress-fill {
  display: block;
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--blue);
  transition: width .25s ease;
}

.survey-page .survey-question {
  display: grid;
  gap: var(--sp-3);
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

.survey-page .question-index {
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--blue);
}

.survey-page .question-text {
  font-size: clamp(18px, calc(15.37px + 0.674vw), 24px);
  /* 390px→18px, 1280px→24px */
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1.35;
  color: var(--text);
}

/* 문진 작성(스텝)·만족도 조사 화면도 같은 상단 여백 (2026-08-24) */
.survey-page {
  margin-top: var(--sp-4);
}

.survey-page .choice-list {
  display: grid;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.survey-page .choice-item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  min-height: 58px;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--text);
  font-size: clamp(16px, calc(13.8px + 0.562vw), 21px);
  /* 390px→16px, 1280px→21px */
  font-weight: 700;
  text-align: left;
  letter-spacing: -.02em;
  /* 선택지는 클릭 대상 — 포인터 커서 (2026-08-24 사용자 지시. label 이라 기본 커서였다.
     만족도 조사도 .survey-page 스킨을 그대로 쓰므로 함께 적용된다) */
  cursor: pointer;
  transition: background .12s ease, border-color .12s ease;
}

.survey-page .choice-item:hover {
  border-color: var(--blue-line);
  background: var(--blue-soft);
}

.survey-page .choice-item.is-checked {
  border-color: var(--blue);
  background: var(--blue-soft);
  color: var(--blue-dark);
}

.survey-page .choice-mark {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border: 2px solid var(--line-strong);
  border-radius: 50%;
  background: var(--surface);
  color: transparent;
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 700;
}

.survey-page .choice-item.is-checked .choice-mark {
  border-color: var(--blue);
  background: var(--blue);
  color: var(--text-invert);
}

.survey-page .choice-mark.is-square {
  border-radius: 7px;
}

.survey-page .survey-nav {
  display: flex;
  gap: var(--sp-3);
}

.survey-page .survey-nav .btn {
  flex: 1 1 0;
}


/* ===== 08. 홈 (시안 07) ===== */
/* [design-css] 콘텐츠 최대 폭 1500px 중앙 정렬 (AppShell wide 모드에서 폭을 직접 잡는다) */
/* [design-css] 시안 07 — 인사 일러스트 아래끝이 카드 그리드 위끝에 밀착(간격 0)해야 하므로
   행 간격은 0으로 두고, 안내박스만 자기 위 여백(margin-top)을 직접 갖는다. */
.home-page {
  display: grid;
  gap: 0;
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;
}

/* [frontend-dev 2026-08-12] 안내박스가 배경(#EEF0F7)에 묻혀 보이지 않던 문제 —
   흰 카드 + 블루 좌측 액센트 바 + 테두리로 바탕과 확실히 분리한다.
   (.info-box.is-blue 의 연블루 배경보다 대비가 크다. 두 규칙은 명시도가 같고
    main.css 가 global.css 뒤에 로드되므로 여기 값이 이긴다.) */
.home-page>.info-box {
  align-items: center;
  margin-top: var(--sp-5);
  border: 1px solid var(--blue-line);
  border-left: 5px solid var(--blue);
  border-radius: var(--r-md);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

.home-page>.info-box .info-text {
  min-width: 0;
}

.home-page>.info-box .info-body {
  min-width: 0;
}

.home-page .greet-block {
  display: grid;
  gap: var(--sp-1);
}

/* [design-css] 모바일에서 우측 일러스트와 나란히 놓기 위해 하한만 낮춘다(상한 동일) */
.home-page .greet-title {
  font-size: clamp(22px, calc(13.24px + 2.247vw), 42px);
  /* 390px→22px, 1280px→42px */
  font-weight: 900;
  letter-spacing: -.05em;
  line-height: 1.15;
  color: var(--text);
}

.home-page .greet-sub {
  font-size: clamp(15px, calc(11.06px + 1.011vw), 24px);
  /* 390px→15px, 1280px→24px */
  font-weight: 700;
  color: var(--text);
}

.home-page .greet-memo {
  font-size: clamp(12px, calc(9.81px + 0.562vw), 17px);
  /* 390px→12px, 1280px→17px */
  font-weight: 600;
  color: var(--text-mute);
}

.menu-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-3);
}

.menu-grid .menu-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-1);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-card);
  text-align: center;
  transition: transform .2s ease, box-shadow .2s ease;
}

/* [design-css] 마우스 오버 시 카드가 살짝 떠오른다 (터치 기기는 제외 — sticky hover 방지).
   위로 5px 이동 + 아래로 크게 깔리는 그림자 = 공중에 떠 있는 느낌.
   홈 .menu-card 전용 — 측정이력 .metric-card 등에는 적용하지 않는다. */
@media (hover: hover) {
  .menu-grid .menu-card:hover {
    transform: translateY(-5px);
    box-shadow: 0 18px 40px rgba(37, 99, 235, .16), 0 6px 12px rgba(15, 23, 42, .08);
  }

  .menu-grid .menu-card:active {
    transform: translateY(-1px);
    box-shadow: 0 8px 18px rgba(37, 99, 235, .14), 0 2px 6px rgba(15, 23, 42, .07);
  }

  /* [frontend-dev 2026-08-12] 카드 어디에 올려도 안쪽 버튼이 같이 반응한다
     (버튼 위에 정확히 올렸을 때만 색이 변해 "눌리는 곳"이 헷갈리던 문제).
     시작하기·확인하기 = 블루 → 진한 블루, 준비 중 = outline 배경만 옅게. */
  .menu-grid .menu-card:hover .menu-action.btn-blue {
    background: var(--blue-dark);
    box-shadow: 0 6px 16px rgba(37, 99, 235, .28);
  }

  .menu-grid .menu-card:hover .menu-action.btn-outline {
    background: var(--purple-soft);
    border-color: var(--purple-deep);
  }
}

/* 모션 민감 사용자는 이동 없이 그림자만 */
@media (prefers-reduced-motion: reduce) {

  .menu-grid .menu-card,
  .menu-grid .menu-card:hover,
  .menu-grid .menu-card:active {
    transform: none;
    transition: box-shadow .2s ease;
  }
}

/* [design-css] 번호 배지(.menu-no)는 시안 07 재확인 후 삭제.
   썸네일은 카드 폭 전체를 쓰는 고정 높이 박스 + contain 이라
   가로/세로 비율이 다른 자산들도 카드 간 크기가 일정하게 보인다. */
/* [design-css] 썸네일 아래가 제목에 붙어 보여 아래 여백만 sp-4 로 키운다
   (카드 gap sp-2 + margin-bottom sp-4 = 24px). 위 여백은 종전 sp-2 유지. */
.menu-grid .menu-thumb {
  width: 100%;
  height: clamp(96px, 11.7vw, 95px);
  object-fit: contain;
  margin: var(--sp-1) var(--sp-2) var(--sp-2) var(--sp-2);
}

.menu-grid .menu-title {
  font-size: clamp(18px, calc(15.37px + 0.674vw), 24px);
  /* 390px→18px, 1280px→24px */
  font-weight: 700;
  letter-spacing: -.04em;
  color: var(--text);
}

.menu-grid .menu-desc {
  font-size: clamp(13px, calc(11.25px + 0.449vw), 16px);
  /* 390px→13px, 1280px→17px */
  font-weight: 600;
  line-height: 1.35;
  color: var(--text-sub);
}

/* [frontend-dev 2026-08-12] 카드 리프트(.2s)와 같은 호흡으로 색이 바뀌도록 전환을 맞춘다 */
.menu-grid .menu-action {
  width: 100%;
  margin-top: var(--sp-3);
  transition: background .2s ease, border-color .2s ease, color .2s ease, box-shadow .2s ease;
}

.menu-grid .menu-badge {
  position: absolute;
  top: var(--sp-4);
  right: var(--sp-4);
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding: 0 var(--sp-2);
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  color: var(--text-mute);
  font-size: clamp(11px, calc(9.7px + 0.337vw), 14px);
  /* 390px→11px, 1280px→14px */
  font-weight: 700;
}

/* 측정 완료 뱃지 — 준비 중 뱃지와 같은 자리에 초록 톤으로 (사용자 지시 2026-08-16) */
.menu-grid .menu-badge.is-done {
  gap: 3px;
  border: 1px solid color-mix(in srgb, var(--success) 35%, transparent);
  background: var(--success-bg);
  color: var(--success);
}

.menu-grid .menu-card.is-disabled {
  background: var(--surface-soft);
  box-shadow: none;
}

.menu-grid .menu-card.is-disabled .menu-thumb {
  opacity: .5;
}

.menu-grid .menu-card.is-disabled .menu-title,
.menu-grid .menu-card.is-disabled .menu-desc {
  color: var(--text-mute);
}

/* [design-css] 데스크톱 3열(시안 07) / 중간 폭 2열 / 모바일 1열 */
@media (max-width: 1099px) {
  .menu-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 560px) {
  .menu-grid {
    grid-template-columns: 1fr;
  }

  /* [2026-08-27 사용자 지정 — 개발자도구로 이 규칙을 직접 짚어 지시] sp-2 → 0.
     인사 배너의 시니어 일러스트 아래끝이 메뉴 카드 그리드에 **완전히 밀착**해야 한다.
     이 폭에서만 8px 을 주던 종전 판단(아래 묘비)을 사용자 화면 확인으로 뒤집는다.
     [묘비] "모바일에서는 일러스트가 작아 밀착 효과가 살지 않고 인사 문구가 카드에 그대로
     닿아 보인다 — 이 폭에서만 최소 간격을 준다(데스크톱 시안 07 은 그대로 0)."
     ※ 부작용 확인(390px 실측). .home-page 직계 자식은 셋이라 gap 이 두 경계에 닿는다:
         .greet-block → .menu-grid   8 → 0px   ← 지시한 밀착
         .menu-grid  → .info-box    28 → 20px  ← 곁딸린 변화
       뒤쪽은 .info-box 가 자기 margin-top 20px 을 따로 들고 있어 gap 이 빠져도 20px 이 남는다.
       하단 안내 상자가 카드 그리드에 붙지 않으므로 이 폭에서 손볼 것은 없다. */
  .home-page {
    gap: 0;
  }
}


/* ===== 09. 측정이력 셸 (시안 12~14) ===== */
.history-page {
  display: grid;
  gap: var(--sp-3);
}

/* [design-css] 탭 본문 래퍼(.tab-panel-enter)가 블록이라 .history-page 의 gap 이 안쪽까지
   내려가지 않았다 — 지표 그리드와 AI 분석 카드가 간격 0 으로 맞붙던 원인.
   래퍼에도 같은 간격을 주고, 자기 margin 을 갖는 섹션 제목은 gap 과 겹치지 않게 0 으로 맞춘다. */
.history-page .tab-panel-enter {
  display: grid;
  gap: var(--sp-5);
}

.history-page .tab-panel-enter>.section-label {
  margin: 0;
}

/* 탭 고도화(사용자 피드백 2026-08-13, 참고 이미지): 전체 폭 기준선(2px) 위에
   버튼 폭을 꽉 채우는 두꺼운 활성 밑줄이 얹히는 스타일 + 라벨 확대 */
.tab-switch {
  display: flex;
  gap: var(--sp-6);
  border-bottom: 2px solid var(--line);
}

.tab-switch .tab-switch-btn {
  position: relative;
  min-height: 54px;
  padding: 0 var(--sp-4);
  color: var(--text-mute);
  font-size: clamp(16px, calc(13.8px + 0.562vw), 24px);
  /* 390px→16px, 1280px→21px */
  font-weight: 700;
  letter-spacing: -.02em;
  transition: color .15s ease;
}

.tab-switch .tab-switch-btn:hover {
  color: var(--text-sub);
}

.tab-switch .tab-switch-btn.is-active {
  color: var(--blue);
  font-weight: 700;
}

.tab-switch .tab-switch-btn.is-active::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 4px;
  border-radius: var(--r-pill) var(--r-pill) 0 0;
  background: var(--blue);
  animation: tab-underline-in .18s ease-out both;
}

@keyframes tab-underline-in {
  from {
    transform: scaleX(.4);
    opacity: .4;
  }

  to {
    transform: scaleX(1);
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .tab-switch .tab-switch-btn.is-active::after {
    animation: none;
  }
}

.period-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}

/* [2026-08-27 사용자 지시] 변화 탭 인쇄 버튼 — 필터 바 **우측 끝**에 세운다.
   margin-left:auto 는 앞 요소들이 쓰고 남은 자리를 전부 왼쪽 여백으로 삼는다. 이 바는
   조회 기간·장소 필터가 폭을 다 쓰지 않는 경우가 많아(장소 필터가 없는 회원 등) auto 마진이
   있어야 어느 조합에서도 오른쪽에 붙는다.
   flex-shrink 0 — 좁은 폭에서 "🖨 인쇄하기" 가 두 줄로 접히거나 눌리지 않게 한다.
   ※ 버튼 자체 시각 규칙은 공용 .btn.btn-blue 가 갖는다. `.report-toolbar .print-btn` 는
      상세리포트 툴바로 스코프돼 있어 이 바에는 걸리지 않으므로 크기 보정도 여기서 하지 않는다.
   ※ .period-bar 는 JSX 에서 no-print 라 지면에는 이 버튼이 나오지 않는다. */
.period-bar .change-print-btn {
  flex: 0 0 auto;
  margin-left: auto;
}

.period-bar .period-field {
  flex: 1 1 0;
  min-width: 0;
}

.period-bar .period-input {
  width: 100%;
  min-height: 52px;
  padding: 0 var(--sp-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 700;
  color: var(--text);
}

.period-bar .period-input:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 4px rgba(37, 99, 235, .14);
}

.period-bar .period-tilde {
  flex: 0 0 auto;
  color: var(--text-mute);
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 700;
}

.period-bar .period-submit {
  flex: 0 0 auto;
  min-width: 120px;
  min-height: 52px;
}

@media (max-width: 560px) {
  .period-bar {
    flex-wrap: wrap;
  }

  .period-bar .period-field {
    flex: 1 1 40%;
  }

  .period-bar .period-submit {
    flex: 1 1 100%;
  }
}


/* ===== 10. 대시보드 탭 — 지표 카드 ===== */
.metric-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-4);
}

.metric-grid .metric-card {
  display: grid;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

/* [design-css] 지표 카드 헤더 — 대시보드 탭(.metric-card)과 변화 탭(.change-card) 공용.
   이전에는 `.metric-grid .metric-head` 로 묶여 있어서 같은 마크업을 쓰는 변화 탭에서는
   규칙이 하나도 걸리지 않았고, 아이콘·지표명·단위가 좌상단에 세로로 붙어 보였다.
   블록 클래스(.metric-head) 기준으로 바꿔 두 탭이 같은 헤더를 쓴다. */
.metric-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}

.metric-head .metric-icon {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  /* 사용자 지시(2026-08-14): 기본 크기 확대 + 디바이스 폭에 따라 가변 (고정 40px → 44~56px) */
  width: clamp(44px, calc(38.74px + 1.348vw), 56px);
  height: clamp(44px, calc(38.74px + 1.348vw), 56px);
  border-radius: var(--r-md);
  background: var(--blue-soft);
  font-size: clamp(22px, calc(18.5px + 0.899vw), 30px);
  /* 390px→22px, 1280px→30px */
}

/* 지표명 + 단위 묶음. 아이콘 오른쪽에 붙고 남는 폭을 먹어 수치를 오른쪽 끝으로 민다 */
.metric-head .metric-title {
  flex: 1 1 auto;
  min-width: 0;
}

.metric-head .metric-name {
  font-size: clamp(17px, calc(14.8px + 0.562vw), 26px);
  /* 390px→17px, 1280px→22px */
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1.25;
  color: var(--text);
}

.metric-head .metric-unit {
  margin-top: 2px;
  font-size: clamp(12px, calc(11.7px + 0.337vw), 18px);
  /* 390px→12px, 1280px→15px */
  font-weight: 600;
  line-height: 1.2;
  color: var(--text-mute);
}

.metric-head .metric-value {
  flex: 0 0 auto;
  font-size: clamp(24px, calc(20.49px + 0.899vw), 32px);
  /* 390px→24px, 1280px→32px */
  font-weight: 700;
  letter-spacing: -.04em;
  color: var(--text);
  text-align: right;
}

/* 추이 카드 범례 (2026-08-17) — 차트 밖 한 줄. 차트 안 범례는 값 라벨과 겹쳐 폐기했다.
   글자를 키우라는 지시라 축 눈금(12px)보다 한 단계 위로 잡았다. */
.metric-legend,
.change-card .metric-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1) var(--sp-4);
  margin: calc(var(--sp-1) * -1) 0 var(--sp-1);
  color: var(--text-sub);
  font-size: clamp(13px, calc(12.12px + 0.225vw), 15px);
  /* 390px→13px, 1280px→15px */
  font-weight: 700;
}

.metric-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.metric-legend-line {
  display: inline-block;
  width: 16px;
  height: 3px;
  border-radius: 2px;
}

.metric-grid .metric-chart {
  width: 100%;
  height: clamp(150px, 16vw, 190px);
}

.metric-grid .metric-foot {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-soft);
  overflow: hidden;
}

.metric-grid .foot-cell {
  padding: var(--sp-2);
  border-right: 1px solid var(--line);
  text-align: center;
}

.metric-grid .foot-cell:last-child {
  border-right: 0;
}

.metric-grid .foot-key {
  font-size: clamp(12px, calc(10.7px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
  font-weight: 700;
  color: var(--text-mute);
}

.metric-grid .foot-val {
  margin-top: 2px;
  font-size: clamp(14px, calc(12.25px + 0.449vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 700;
  color: var(--blue);
}

.metric-grid .foot-val.is-up {
  color: var(--danger);
}

.metric-grid .foot-val.is-down {
  color: var(--blue);
}

@media (max-width: 1000px) {
  .metric-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 640px) {
  .metric-grid {
    grid-template-columns: 1fr;
  }
}


/* ===== 11. AI 분석 요약 ===== */
.ai-summary {
  display: grid;
  gap: var(--sp-4);
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

.ai-summary .ai-summary-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
}

/* 제목 옆 안내 — 분석 기준(최근 6개월 종합)을 밝힌다 (사용자 지시 2026-08-19) */
.ai-summary .ai-summary-note {
  color: var(--text-mute);
  font-size: clamp(12px, calc(10.9px + 0.281vw), 14.5px);
  /* 390px→12px, 1280px→14.5px */
  font-weight: 600;
  text-align: right;
}

.ai-summary .ai-summary-title {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: clamp(18px, calc(15.37px + 0.674vw), 24px);
  /* 390px→18px, 1280px→24px */
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--text);
}

/* [design-css] 게이지 · 코멘트 · 태그를 하나의 연회색 패널로 묶는다(시안 12).
   간격은 패널 padding(sp-4) / 열 gap(sp-5) / 세로 gap(sp-3) 3단계로만 쓴다. */
.ai-summary .ai-summary-body {
  display: grid;
  grid-template-columns: minmax(0, 210px) minmax(0, 1fr);
  gap: var(--sp-5);
  align-items: center;
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface-soft);
}

/* 게이지 상세(트랙·호·애니메이션)는 파일 하단 [design-ai-card] 블록 */
.ai-summary .ai-score {
  display: grid;
  justify-items: center;
  gap: var(--sp-2);
  padding-right: var(--sp-5);
  border-right: 1px solid var(--line);
}

/* 코멘트 + 태그 열 — 두 덩어리 사이 간격을 gap 하나로 관리한다 */
.ai-summary .ai-summary-text {
  display: grid;
  gap: var(--sp-3);
  min-width: 0;
}

.ai-summary .ai-comment-list {
  display: grid;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 600;
  line-height: 1.6;
  color: var(--text);
}

.ai-summary .ai-tag-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-2);
}

/* [design-css] 라벨-상태가 한 줄에 붙어 있어 좁아지면 줄바꿈이 지저분했다 —
   2행 스택 + 왼쪽 상태색 바 (상태색은 tone 클래스로 온다) */
.ai-summary .ai-tag {
  display: grid;
  align-content: center;
  gap: 2px;
  min-height: 54px;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line);
  border-left: 4px solid var(--text-mute);
  border-radius: var(--r-md);
  background: var(--surface);
}

.ai-summary .ai-tag.is-ok {
  border-left-color: var(--grade-normal);
}

.ai-summary .ai-tag.is-warn {
  border-left-color: var(--grade-attention);
}

.ai-summary .ai-tag.is-danger {
  border-left-color: var(--grade-consult);
}

.ai-summary .ai-tag-key {
  font-size: clamp(12px, calc(10.7px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
  font-weight: 700;
  color: var(--text-mute);
}

.ai-summary .ai-tag-val {
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 700;
  line-height: 1.3;
  color: var(--text);
}

.ai-summary .ai-tag.is-ok .ai-tag-val {
  color: var(--grade-normal);
}

.ai-summary .ai-tag.is-warn .ai-tag-val {
  color: var(--grade-attention);
}

.ai-summary .ai-tag.is-danger .ai-tag-val {
  color: var(--grade-consult);
}

@media (max-width: 1180px) {
  .ai-summary .ai-tag-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 900px) {
  .ai-summary .ai-summary-body {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-4);
  }

  /* 1열로 접히면 세로 구분선으로 바꾼다 */
  .ai-summary .ai-score {
    padding-right: 0;
    padding-bottom: var(--sp-4);
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }
}

@media (max-width: 520px) {
  .ai-summary .ai-tag-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* 맞춤 건강 관리 권장사항 3열 (운동·식단·모니터링) */
.advice-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-4);
}

/* [design-css] 연한 단색 배경이라 페이지 바탕(#EEF0F7)과 구분이 안 됐다 —
   ① 위에서 아래로 틴트→흰색 그라데이션(아래쪽이 바탕보다 확실히 밝다)
   ② 계열색 테두리 + 카드 그림자
   ③ 좌측 6px 액센트 바 + 흰 아이콘 칩
   카드별 계열색은 아래 is-* 에서 --advice-ink/tint/line 3개만 갈아끼운다. */
.advice-grid .advice-card {
  --advice-ink: var(--blue);
  --advice-tint: var(--blue-soft);
  --advice-line: var(--blue-line);
  position: relative;
  display: grid;
  gap: var(--sp-3);
  align-content: start;
  overflow: hidden;
  padding: var(--sp-4) var(--sp-4) var(--sp-4) calc(var(--sp-4) + 6px);
  border: 1px solid var(--advice-line);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, var(--advice-tint) 0%, var(--surface) 72%);
  box-shadow: var(--shadow-card);
}

.advice-grid .advice-card::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 6px;
  background: var(--advice-ink);
}

.advice-grid .advice-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: clamp(16px, calc(13.8px + 0.562vw), 21px);
  /* 390px→16px, 1280px→21px */
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--text);
}

.advice-grid .advice-icon {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  border: 1px solid var(--advice-line);
  border-radius: var(--r-pill);
  background: var(--surface);
  font-size: clamp(16px, calc(14.24px + 0.45vw), 20px);
  /* 390px→16px, 1280px→20px */
  line-height: 1;
}

.advice-grid .advice-list {
  display: grid;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 600;
  line-height: 1.55;
  color: var(--text-sub);
}

.advice-grid .advice-list li {
  position: relative;
  padding-left: 16px;
}

/* 기본 disc 대신 계열색 점 — 여러 줄로 접혀도 들여쓰기가 유지된다 */
.advice-grid .advice-list li::before {
  content: "";
  position: absolute;
  top: .6em;
  left: 2px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--advice-ink);
}

.advice-grid .advice-card.is-exercise {
  --advice-ink: var(--blue);
  --advice-tint: var(--blue-soft);
  --advice-line: var(--blue-line);
}

.advice-grid .advice-card.is-diet {
  --advice-ink: var(--grade-normal);
  --advice-tint: var(--grade-normal-bg);
  --advice-line: var(--grade-normal-line);
}

.advice-grid .advice-card.is-monitor {
  --advice-ink: var(--purple-deep);
  --advice-tint: var(--purple-soft);
  --advice-line: var(--purple-line);
}

@media (max-width: 900px) {
  .advice-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-3);
  }
}


/* ===== 12. 변화 탭 (지표별 추이 카드) ===== */
/* [dash-v2 2026-08-27] 항목이 5 → 8개(산소포화도·골격근량·체수분 추가)로 늘면서 1열 세로
   나열은 세로 길이가 두 배가 됐다 — 대시보드 탭과 같은 2열 그리드로 바꾼다.
   1열 → 2열 전환점은 780px(대시보드 .dash-grid 와 같은 값)이고, 카드가 절반 폭이 되면
   .change-body 의 `1fr 220px` 이 눌리므로 아래 1180px 분기에서 side 를 차트 밑으로 내린다. */
.change-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-4);
}

/* [design-css] 헤더와 본문이 간격 0 으로 맞붙어 있어 grid + gap 으로 띄운다
   (대시보드 탭 .metric-card 와 같은 sp-3 간격) */
.change-grid .change-card {
  display: grid;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

.change-grid .change-body {
  display: grid;
  grid-template-columns: 1fr 220px;
  gap: var(--sp-4);
  align-items: center;
}

.change-grid .change-chart {
  width: 100%;
  height: clamp(150px, 15vw, 200px);
  min-width: 0;
}

.change-grid .change-side {
  display: grid;
  gap: var(--sp-2);
  padding: var(--sp-3);
  border-radius: var(--r-md);
  background: var(--surface-soft);
}

.change-grid .change-side-label {
  font-size: clamp(12px, calc(10.7px + 0.337vw), 16px);
  /* 390px→12px, 1280px→15px */
  font-weight: 700;
  color: var(--text-mute);
}

.change-grid .change-side-value {
  font-size: clamp(22px, calc(18.49px + 0.899vw), 30px);
  /* 390px→22px, 1280px→30px */
  font-weight: 700;
  letter-spacing: -.04em;
  color: var(--text);
}

.change-grid .change-side-delta {
  font-size: clamp(14px, calc(12.25px + 0.449vw), 20px);
  /* 390px→14px, 1280px→18px */
  font-weight: 700;
  color: var(--text-sub);
}

.change-grid .change-side-delta.is-up {
  color: var(--danger);
}

.change-grid .change-side-delta.is-down {
  color: var(--blue);
}

/* [dash-v2 2026-08-27] 종전 분기는 780px 이었다(1열 그리드 전제). 2열이 되면서 카드 안쪽 폭이
   절반이 됐으므로 side 를 내리는 시점을 1180px 로 올린다 — 1180px 에서 카드 안쪽은
   (1180-40-16)/2-40 ≈ 522px 이고, 여기서 side 220px 을 빼면 차트가 286px 밖에 안 남는다.
   ≤780px 은 그리드가 1열로 돌아가지만 모바일에서는 어차피 세로 스택이 맞으므로 이 분기가 겸한다.

   [2026-08-27 사용자 지시 "사이드 정보가 모바일에서 세로 4줄로 나온다 — 1줄에 배치"]
   side 가 차트 **아래 가로 띠**로 내려오는 순간(=이 분기) 세로 1열은 폭을 다 버리고 높이만
   먹는다. [최근값 쌍] | [변화량 쌍] 두 칸으로 나눠 나란히 세운다 — 라벨·값 쌍은 그대로 유지.
   마크업은 그대로다(.change-side 의 자식 4~5개가 순서대로 라벨·값·라벨·변화량[·변화량2]).
   클래스가 라벨 두 개에 같아서(.change-side-label 이 1·3번째) 클래스 선택자로는 좌우를 가를
   수 없다 — 자식 순서가 곧 계약이라 nth-child 로 짚는다.
   혈압만 변화량이 두 줄(수축기·이완기)인데, 5번째 자식을 오른쪽 칸 3행에 두면 그 칸 안에서
   자연스럽게 아래로 쌓인다(왼쪽 칸은 2행까지라 빈 3행은 높이 0). 항목별 예외 처리가 필요 없다.
   행 간격을 row-gap 이 아니라 padding-top 으로 주는 이유: 오른쪽 칸의 세로 구분선을
   border-left 로 그리는데, row-gap 이면 행 사이에서 선이 끊긴다(padding 안쪽은 테두리가 이어진다).
   390px 실측: side 안쪽 308px → 칸당 144px. 혈압 값 "111/74 mmHg" 113px · 변화량
   "수축기 +5 mmHg" 98px 로 둘 다 들어간다. */
@media (max-width: 1180px) {
  .change-grid .change-body {
    grid-template-columns: minmax(0, 1fr);
  }

  .change-grid .change-side {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-content: start;
    row-gap: 0;
    column-gap: var(--sp-5);
  }

  .change-grid .change-side>:nth-child(1) {
    grid-area: 1 / 1;
  }

  .change-grid .change-side>:nth-child(2) {
    grid-area: 2 / 1;
    padding-top: 2px;
  }

  .change-grid .change-side>:nth-child(3) {
    grid-area: 1 / 2;
  }

  .change-grid .change-side>:nth-child(4) {
    grid-area: 2 / 2;
    padding-top: 2px;
  }

  /* 혈압 전용 — 이완기 줄. 있을 때만 3행이 생긴다 */
  .change-grid .change-side>:nth-child(5) {
    grid-area: 3 / 2;
    padding-top: 2px;
  }

  /* 두 쌍 사이 구분선. 오른쪽 칸 자식 전부에 걸어 위→아래로 이어지게 한다
     (row-gap 0 + padding-top 조합이라 3행까지 한 줄로 보인다) */
  .change-grid .change-side>:nth-child(3),
  .change-grid .change-side>:nth-child(4),
  .change-grid .change-side>:nth-child(5) {
    padding-left: var(--sp-4);
    border-left: 1px solid var(--line);
  }
}

@media (max-width: 780px) {
  .change-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}


/* ===== 13. 상세리포트 탭 툴바 ===== */
.report-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  /* 리포트 본문(AI 건강리포트 대제목)과의 간격 (2026-08-24 사용자 지시) */
  margin-bottom: var(--sp-5);
}

.report-toolbar .report-select {
  min-height: 48px;
  min-width: 200px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 700;
  color: var(--text);
}

.report-toolbar .print-btn {
  flex: 0 0 auto;
}

@media (max-width: 560px) {
  .report-toolbar {
    flex-wrap: wrap;
  }

  .report-toolbar .report-select {
    flex: 1 1 100%;
  }
}


/* ===== 14. 모달 / 최근값 행 ===== */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: grid;
  place-items: center;
  padding: var(--sp-4);
  background: rgba(14, 22, 38, .45);
}

.modal-card {
  width: min(560px, 100%);
  max-height: 86dvh;
  overflow-y: auto;
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-pop);
}

.modal-card .modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--line);
}

.modal-card .modal-title {
  font-size: clamp(18px, calc(15.37px + 0.674vw), 24px);
  /* 390px→18px, 1280px→24px */
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--text);
}

.modal-card .modal-close {
  display: grid;
  place-items: center;
  width: var(--tap-min);
  height: var(--tap-min);
  border-radius: var(--r-sm);
  color: var(--text-mute);
  font-size: clamp(20px, calc(17.37px + 0.674vw), 26px);
  /* 390px→20px, 1280px→26px */
}

.modal-card .modal-close:hover {
  background: var(--surface-sunken);
}

.modal-card .modal-body {
  padding: var(--sp-5);
}

.modal-card .modal-foot {
  display: flex;
  gap: var(--sp-2);
  padding: 0 var(--sp-5) var(--sp-5);
}

.modal-card .modal-foot .btn {
  flex: 1 1 0;
}

.latest-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--line);
}

.latest-row:last-child {
  border-bottom: 0;
}

.latest-row .latest-key {
  font-size: clamp(14px, calc(12.25px + 0.449vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 700;
  color: var(--text-sub);
}

.latest-row .latest-val {
  font-size: clamp(18px, calc(15.37px + 0.674vw), 24px);
  /* 390px→18px, 1280px→24px */
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--text);
}

.latest-row .latest-date {
  font-size: clamp(12px, calc(10.7px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
  font-weight: 600;
  color: var(--text-mute);
}


/* ===== 15. 인쇄 — 앱 크롬을 걷어내고 그때 열려 있는 탭만 남긴다 ===== */
/* [스코프 확인 2026-08-27] 탭 본문은 React 가 **활성 탭 하나만** 렌더한다
   (`{tab === "change" ? renderChangeTab() : null}` 꼴). 상세리포트 탭에서 인쇄하면 .change-grid
   가 DOM 에 아예 없고, 변화 탭에서 인쇄하면 .report 가 없다. 그래서 "리포트만 남긴다" 식의
   전역 body 숨김 규칙은 원래부터 없고(위 목록은 크롬 요소만 지운다), 아래 변화 탭 지면 규칙도
   .change-* 로만 스코프돼 두 탭이 서로를 건드리지 않는다. */
@media print {

  .tab-switch,
  .report-toolbar,
  .period-bar,
  .hero-banner,
  .modal-backdrop {
    display: none !important;
  }

  .history-page {
    gap: 0;
  }
}


/* ===== 15-1. 인쇄 — 변화 탭 지면 = 세로 A4 1장 · 2열 4행 (2026-09-11 사용자 확정) ===== */
/* 앱 크롬(헤더·탭 스위치·필터 바·하단 탭바)을 지우는 일은 위 15 블록과 global.css 14 블록이
   이미 한다 — 여기서는 **지면 규격과 카드 조판만** 더한다(중복 제거 규칙을 만들지 않는다).
   스코프는 탭 정체 훅 `.tab-panel-enter.is-change` 다. 활성 탭만 렌더되므로 실제 충돌은
   없지만, 두 탭이 동시에 렌더되는 구조로 바뀌어도 이 규칙이 상세리포트 지면으로 새지 않는다.

   [지면 방향의 내력] 2026-09-10 "인쇄물이 너무 작다" 지시로 zoom .72 세로 지면(차트 글자
   3.6pt)을 버리고 가로 A4 로 갔다가, 2026-09-11 사용자 확정으로 **다시 세로 A4** 로 돌아왔다.
   되돌아와도 작아지지 않는 이유는 지면 방향이 아니라 아래 두 가지를 그때 해결했기 때문이다 —
     ① zoom 폐기(글자가 화면 크기 그대로 찍힌다)
     ② 인쇄 직전 차트 재측정(html.print-change) — 차트가 지면 상자에 1:1 로 얹힌다
   세로는 오히려 높이가 넉넉해(1054px) 차트를 150px 까지 키울 수 있다(가로 때 100px).

   [지면 규격] A4 세로 210×297mm − 여백 9mm = 192×279mm = 726×1054px(96dpi).
     2열 + 칸 간격 6px → 카드 폭 360px, 카드 안쪽 334px(= 차트 폭).

   [명명 페이지] report.css 인쇄 블록의 전역 `@page { size: A4; margin: 9mm }` 는 상세리포트
     지면(세로 3쪽)의 정본이라 손대지 않는다. 변화 탭은 **이름 있는 지면**(change-page)으로
     분리해 둔다 — 지금은 규격이 같지만(둘 다 A4 세로 9mm), 이름이 있어야 한쪽 지면을 바꿔도
     다른 쪽이 따라 움직이지 않는다. 상세리포트 탭은 DOM 에 .is-change 가 없어 이름이 붙지
     않는다(실측: 세로 3쪽 유지).

   [세로 배분 — 1054px] 머리 블록 54 + 카드 4행×236 + 행 간격 3×6 = 1016px (여유 38px).
     여유를 넉넉히 두는 이유 — 프린터·드라이버가 여백을 더 크게 잡아(일부 기종 12.7mm) 지면
     본문이 1026px 로 줄어도 마지막 행이 다음 장으로 밀리지 않아야 한다.
     카드 속(236 − 세로 padding 16 − 테두리 2 = 218px):
       머리 줄 31 + 3 + [혈압만 범례 16 + 3] + 차트 150px(혈압 108px) + 3 + 사이드 24px(혈압 두 줄 46px)
     머리 블록이 없어도 넘치지 않는다(962px 로 끝나고 아래가 빈다).
     카드가 4~6장인 회원은 빈 칸 없이 위에서부터 채워진다(그리드 자동 배치 그대로).

   [차트 — 화면에서 잰 SVG 가 그대로 인쇄된다] recharts ResponsiveContainer 는 지면에서 크기를
     다시 재지 않는다(인쇄 레이아웃은 ResizeObserver 콜백을 받지 못한다. 실측으로 확인했다 —
     지면의 차트 글자가 정확히 `12px × zoom × 차트박스/화면SVG` 였다).
     SVG 는 viewBox + width/height 100% 라 박스에 맞춰 **통째로 축소**되고 글자도 같은 배로
     작아진다. 그래서 두 길을 함께 둔다:
       ① 인쇄 버튼 → 아래 화면 리허설이 돌아 차트가 지면 상자 그대로 다시 그려진다(1:1).
       ② Ctrl+P·메뉴 인쇄 → 훅이 돌지 않아 축소된다. 축 글자를 키워 축소배를 되돌린다
          (세로 지면은 상자가 334×150 이라 축소배가 약 .71 — 가로 때 .55 보다 유리하다).
     ※ 축 눈금은 <text> 의 속성(font-size)이라 CSS 가 그냥 이긴다. 값 라벨만 recharts 가
       인라인 style 로 박아 !important 가 필요하다 — 인쇄 스코프 한정이라 라이브편집을
       막는 전역 가드가 아니다.

   [값 라벨 재작업 — 2026-09-11 사용자 재지적 "라벨이 선·점을 가로지른다"]
     recharts LabelList 는 점마다 같은 자리(위쪽 offset 10)에 라벨을 놓을 뿐 이웃 라벨·선·점을
     피하지 못한다. 세로 지면은 차트가 334×150px 로 작아 실데이터 18건에서 겹침이 났다.
     그래서 훅 경로의 값 라벨은 TrendChart 가 **자체 레이어(.print-value-labels)** 로 그린다 —
     픽셀 공간 충돌 회피로 자리를 잡고 글자 크기는 위 --pchg-label-fs 를 읽어 맞춘다.
     CSS 가 맡는 몫은 두 가지뿐이다: ① 그 값(변수 한 벌) ② 못 피한 라벨이 선 위에서도 읽히게
     하는 흰 후광(아래 ⑦). LabelList 계열 규칙(크기·정렬·솎기)은 훅이 없는 폴백 전용으로
     `html:not(.print-change)` 에 묶어 두 경로가 서로를 건드리지 않게 했다.
     같은 재작업에서 혈압 카드 범례를 머리 줄 오른쪽으로 올려(행 하나를 없애) 2선 차트 높이를
     108 → 127px 로 키웠다 — 라벨이 피해 앉을 세로 공간을 그만큼 더 준다.

   [색] report.css 의 print-color-adjust: exact 는 .report 스코프라 여기 걸리지 않는다 —
     카드에 같은 값을 건다(상속되는 속성이라 사이드 틴트·구분선·축 눈금선까지 함께 살아난다).
     없으면 브라우저 기본값(economy)이 배경색을 통째로 버려 카드 경계가 지면에서 사라진다. */

/* 인쇄 전용 머리 블록 — 화면에는 없는 요소다(frontend 가 history/page.js 에서 렌더한다) */
.change-print-head {
  display: none;
}

/* ----- 지면 기하 수치 한 벌 (2026-09-11 세로 기준) -----
   아래 인쇄 블록과 그 짝인 화면 리허설(html.print-change)이 **같은 변수**를 본다 —
   두 곳에 숫자를 따로 적으면 한쪽만 고쳐졌을 때 차트가 다시 레터박스(축소)로 돌아간다.
   값의 근거는 위 [세로 배분]·[지면 규격] 주석과 같다(세로 본문 726×1054px · 2열 카드 360px).
   ※ 차트 높이는 실측값이다 — 머리 줄(아이콘 26px + 지표명 한 줄) 31px, 사이드 한 줄 24px,
     행 간격 3px 을 빼면 카드 안쪽 218px 에서 150px 가 남는다. 혈압 카드는 사이드가 두 줄
     (46px)이라 127px 가 남는다(범례는 2026-09-11 부터 머리 줄 오른쪽에 얹어 행을 쓰지 않는다 —
     31 + 3 + 127 + 3 + 46 = 210 ≤ 218).
   ※ --pchg-label-fs 는 **CSS 가 정본인 값을 JS 에 넘기는 창구**다. 인쇄용 값 라벨은 이제
     TrendChart 가 자체 레이어(.print-value-labels)로 그리는데, 그 글자 크기와 충돌 회피 배치
     계산이 이 값을 읽는다(양쪽에 숫자를 따로 적으면 갈라진다). 지면 폭이 726px 로 고정이라
     아래 축 눈금 clamp(12px, 1.86vw, 15px) 의 해석값과 같은 값을 적어 둔다 — 둘은 항상 같이
     움직여야 한다(축 날짜와 값 라벨이 같은 크기로 읽혀야 한다). */
.tab-panel-enter.is-change {
  --pchg-card-h: 236px;
  --pchg-chart-w: 334px;
  --pchg-chart-h: 150px;
  --pchg-chart-h-pair: 127px;
  --pchg-label-fs: 13.5px;
}

/* ----- 인쇄 직전 화면 리허설 (frontend 훅과 짝) -----
   [왜 필요한가] recharts 는 인쇄 레이아웃에서 크기를 다시 재지 않는다(위 [차트] 주석).
   그래서 frontend 가 **인쇄를 누른 순간** html 에 .print-change 를 달고(재측정 대기 →
   window.print() → afterprint 에 제거), 그 사이에 차트 상자만 지면 크기로 바꿔 recharts 가
   지면 크기로 다시 그리게 한다. 지면에서는 같은 상자(334×150) 위에 1:1 로 얹힌다.
   [왜 차트 상자만 바꾸는가] recharts 가 보는 것은 .change-chart 상자 하나뿐이다. 지면 조판
   전체(2열·머리 블록·사이드 줄)를 화면에 재현하면 규칙 30여 개가 두 벌이 되어 값이 갈라질
   위험만 커지고, 인쇄 대화상자가 뜨기 전 화면이 통째로 번쩍인다.
   [@media screen 인 이유] 실제 인쇄 순간에도 이 클래스는 붙어 있다. 그때 폭을 고정하면
   A4 가 아닌 용지(레터 등)에서 카드가 좁아질 때 차트가 이웃 칸으로 넘친다. 인쇄 지면의
   폭은 아래 인쇄 블록이 상한(max-width)으로 맡는다. */
@media screen {

  html.print-change .tab-panel-enter.is-change .change-chart {
    width: var(--pchg-chart-w);
    height: var(--pchg-chart-h);
  }

  /* 범례가 한 줄 더 붙는 카드(혈압)는 지면에서 차트가 그만큼 낮다 */
  html.print-change .tab-panel-enter.is-change .change-card:has(.metric-legend) .change-chart {
    height: var(--pchg-chart-h-pair);
  }
}

@media print {

  /* ① 지면 규격 — 변화 탭 전용 이름.
     [!important 가 필요한 이유 — 실측] Chromium 은 @page 규칙에 **선택자 특정도를 적용하지
     않는다**. 이름 있는 @page 든 이름 없는 @page 든 같은 무게로 놓고 문서 순서로 이긴다.
     AppShell 은 스타일시트를 main.css → report.css 순으로 붙이므로 report.css 의
     이름 없는 `@page` 가 **뒤에 와서** 이 규칙을 덮어쓴다(가로 지면이던 때 실측으로 확인:
     landscape 를 걸어도 A4 세로로 찍혔고, !important 를 붙이자 가로가 됐다).
     지금은 두 지면의 규격이 같아 결과가 같지만, 이 한 줄이 "변화 탭 지면은 A4 세로"라는
     약속을 문서 순서와 무관하게 지켜 준다(컴포넌트 시각값 가드가 아니라 지면 규격 한 줄이라
     라이브편집을 막지 않는다). */
  @page change-page {
    size: A4 !important;
    margin: 9mm;
  }

  /* [빈 장 방지 — 2026-09-11 사용자 인쇄 사고] 지면 이름은 **문서 뿌리에** 건다.
     문서 중간 상자(.history-page·패널)에 이름을 걸면 그 앞뒤에서 **페이지 이름이 바뀌고**,
     Chromium 은 이름이 바뀌는 자리마다 강제 개행을 넣는다 — 앞에는 빈 장, 뒤에는 셸의 남는
     높이(아래 min-height 참조)만큼 또 한 장이 붙는다(사용자 실측 3쪽: 빈 장 + 본문 + 빈 장).
     ※ 우리 쪽 headless 인쇄(printToPDF, 머리글 on/off × preferCSSPageSize on/off 4종)에서는
       재현되지 않았다 — 크롬 버전·프린터 드라이버에 따라 갈리는 자리다. 그래서 조건을 맞추는
       대신 **전환 자체를 없앴다**: 뿌리에 걸면 문서 전체가 한 이름이라 바뀌는 자리가 없다.
     상세리포트 탭은 DOM 에 .is-change 가 없어 이름이 붙지 않는다(세로 3쪽 그대로). */
  html:has(.tab-panel-enter.is-change) {
    page: change-page;
  }

  /* 앱 셸은 화면에서 100dvh 다(global.css 03). 지면에서도 한 장 높이만큼 상자가 서므로
     본문이 끝난 뒤에도 셸 바닥이 남아 뒤에 빈 장을 만들 수 있다 — 변화 탭 지면에서는 끈다. */
  html:has(.tab-panel-enter.is-change) .app-shell {
    min-height: 0;
  }

  /* ② 머리 블록 — 제목 + 회원·기간·측정일수·인쇄일 */
  .change-print-head {
    display: block;
    /* .change-grid 안에 들어가더라도 카드 칸에 끼지 않고 한 줄을 통째로 쓴다 */
    grid-column: 1 / -1;
    margin: 0 0 6px;
  }

  .change-print-head .cph-title {
    margin: 0;
    font-size: clamp(19px, 3.17vw, 25px);
    /* 세로 A4(726px)→23px ≈ 17pt */
    font-weight: 800;
    letter-spacing: -.03em;
    line-height: 1.2;
    color: var(--text);
  }

  .change-print-head .cph-meta {
    margin: 3px 0 0;
    font-size: clamp(12px, 1.86vw, 15px);
    /* 세로 A4(726px)→13.5px ≈ 10pt */
    font-weight: 600;
    line-height: 1.25;
    color: var(--text-sub);
  }

  /* ③ 2열 4행. 행 높이는 카드에 직접 준다 — grid-auto-rows 로 주면 머리 블록이 그리드 안에
     들어왔을 때 그 줄까지 카드 높이로 잡아 지면 한 장을 넘긴다. */
  .tab-panel-enter.is-change .change-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 6px;
  }

  /* ④ 카드 = [머리 줄 | 범례] / [차트] / [사이드 한 줄]. 세로 지면은 높이가 넉넉해
     사이드를 머리 줄에 얹지 않고 **카드 하단 가로 한 줄**로 둔다(사용자 지시 원안).
     범례(혈압 전용)는 반대로 **머리 줄 오른쪽**에 얹어 제 행을 쓰지 않게 한다 — 그 16px + 행
     간격 3px 이 그대로 혈압 카드 차트 높이가 된다(108 → 127px). */
  .tab-panel-enter.is-change .change-card {
    height: var(--pchg-card-h);
    /* 머리(아이콘·지표명·단위)는 내용 폭 그대로, 남는 폭은 범례 칸이 받는다.
       범례가 없는 카드는 둘째 칸이 폭 0 이라 머리 줄 모양이 달라지지 않는다. */
    grid-template-columns: auto minmax(0, 1fr);
    /* 차트 줄은 1fr 이 아니라 **정해진 높이**다 — 화면 리허설이 그리는 상자와 1px 도 어긋나면
       그만큼 축소가 생긴다(같은 변수를 본다). 남는 높이는 머리 줄(auto)이 가져간다. */
    grid-template-rows: auto var(--pchg-chart-h) auto;
    row-gap: 3px;
    column-gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3);
    /* 카드가 쪽 경계에 걸쳐 반 토막 나는 것을 막는다(두 줄 병기는 report.css 인쇄 블록과 같은 이유) */
    break-inside: avoid;
    page-break-inside: avoid;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
    /* 그림자는 지면에서 회색 얼룩으로 찍힌다(리포트가 그림자를 전부 없앤 것과 같은 이유) */
    box-shadow: none;
  }

  /* 사이드가 두 줄이 되는 혈압 카드는 차트 줄이 그만큼 낮다 */
  .tab-panel-enter.is-change .change-card:has(.metric-legend) {
    grid-template-rows: auto var(--pchg-chart-h-pair) auto;
  }

  /* .change-body 는 [차트 | 사이드] 칸을 만드는 껍데기다. 지면에서는 껍데기를 없애고
     (display: contents) 차트·사이드를 카드 격자에 직접 앉힌다 — 화면의 `1fr 220px` 2열이
     사라지면서 차트가 카드 폭을 통째로 쓰고 사이드가 제 줄을 갖는다. */
  .tab-panel-enter.is-change .change-body {
    display: contents;
  }

  .tab-panel-enter.is-change .metric-head {
    grid-area: 1 / 1;
    gap: var(--sp-2);
    min-width: 0;
  }

  .tab-panel-enter.is-change .metric-icon {
    width: 26px;
    height: 26px;
    border-radius: 8px;
    font-size: clamp(13px, 2.07vw, 17px);
    /* 세로 A4(726px)→15px */
  }

  /* 지표명·단위는 지면에서 한 줄로 붙인다(두 줄이면 머리 줄이 14px 더 높아진다) */
  .tab-panel-enter.is-change .metric-title {
    display: flex;
    align-items: baseline;
    gap: 6px;
    white-space: nowrap;
  }

  .tab-panel-enter.is-change .metric-name {
    font-size: clamp(14px, 2.2vw, 18px);
    /* 세로 A4(726px)→16px ≈ 12pt */
  }

  .tab-panel-enter.is-change .metric-unit {
    margin-top: 0;
    font-size: clamp(11px, 1.65vw, 13px);
    /* 세로 A4(726px)→12px ≈ 9pt */
  }

  /* 범례(혈압 전용) — 머리 줄 **오른쪽 끝**. 제 행을 쓰지 않으므로 그만큼 차트가 높아진다.
     실측(카드 안쪽 334px): 머리 "🩺 혈압 mmHg" 107px + 간격 8 + 범례 188px = 303px 로 한 줄에
     든다(여유 31px). 폭이 모자라는 회원·언어가 생기면 글자를 11px 로 한 단계 낮춘다. */
  .tab-panel-enter.is-change .metric-legend {
    grid-area: 1 / 2;
    justify-self: end;
    align-self: center;
    margin: 0;
    gap: 0 var(--sp-3);
    font-size: clamp(11px, 1.65vw, 13px);
    /* 세로 A4(726px)→12px */
  }

  .tab-panel-enter.is-change .change-chart {
    grid-column: 1 / -1;
    grid-row: 2;
    /* 화면 clamp(150~200px) 를 덮고 리허설과 같은 값을 쓴다.
       폭은 A4 세로에서 리허설과 같은 334px 이 되도록 상한만 건다 — 고정 폭으로 주면 더 좁은
       용지(레터 등)에서 이웃 칸으로 넘치고, 상한이 없으면 칸이 넓어질 때 1:1 이 깨진다. */
    width: 100%;
    max-width: var(--pchg-chart-w);
    height: var(--pchg-chart-h);
    min-height: 0;
  }

  .tab-panel-enter.is-change .change-card:has(.metric-legend) .change-chart {
    height: var(--pchg-chart-h-pair);
  }

  /* ⑤ 사이드(최근값·변화량) — 카드 하단 가로 한 줄 */
  .tab-panel-enter.is-change .change-side {
    grid-column: 1 / -1;
    grid-row: 3;
    align-self: end;
    display: flex;
    align-items: baseline;
    justify-content: center;
    /* 한 줄이 원칙이고, 폭이 모자랄 때만 스스로 두 줄이 된다(잘라내지 않는다) —
       카드 안쪽 334px 에 혈압 카드의 다섯 토막("최근값 116/70 mmHg | 변화량 수축기 -4 mmHg
       이완기 0 mmHg")은 들어가지 않는다. 그 카드만 두 줄이 되고, 높이는 위 :has(.metric-legend)
       규칙이 차트에서 미리 빼 둔다. */
    flex-wrap: wrap;
    white-space: nowrap;
    gap: 0 6px;
    padding: 2px var(--sp-2);
    border-radius: 8px;
  }

  /* 위 [1180px] 분기가 사이드를 2칸 격자로 짜 두었다(지면 폭 726px 이라 그 분기가 걸린다).
     한 줄이 된 지면에서는 칸 지정·세로 구분선을 걷어내고 두 쌍 사이에만 구분선을 남긴다.
     `:nth-child(n)` 은 "모든 자식"이다 — 1180px 분기의 nth-child 규칙과 같은 무게를 맞추려고
     이 꼴을 쓴다(순서 의존을 피한다). */
  .tab-panel-enter.is-change .change-side>:nth-child(n) {
    padding: 0;
    border-left: 0;
  }

  .tab-panel-enter.is-change .change-side>:nth-child(3) {
    padding-left: 6px;
    border-left: 1px solid var(--line);
  }

  .tab-panel-enter.is-change .change-side-label {
    font-size: clamp(11px, 1.65vw, 13px);
    /* 세로 A4(726px)→12px ≈ 9pt (지면 최소 글자 — 9pt 하한을 지킨다) */
  }

  .tab-panel-enter.is-change .change-side-value {
    font-size: clamp(13px, 2.07vw, 17px);
    /* 세로 A4(726px)→15px ≈ 11.3pt */
    letter-spacing: -.02em;
  }

  .tab-panel-enter.is-change .change-side-delta {
    font-size: clamp(11.5px, 1.79vw, 15px);
    /* 세로 A4(726px)→13px ≈ 9.8pt */
  }

  /* ⑥ 차트 속 글자·선 — 지면 전용 값.
     [두 갈래인 이유] 인쇄 버튼으로 인쇄하면 위 리허설이 돌아 차트가 지면 크기 그대로 찍히고
     (1:1) 값 라벨도 TrendChart 의 인쇄 레이어(.print-value-labels)가 그린다. 사용자가
     Ctrl+P·메뉴로 바로 인쇄하면(beforeprint 를 주지 않는 브라우저 포함) 훅이 돌지 않아
     화면에서 잰 SVG 가 축소돼 찍히고, 값 라벨도 recharts 기본 LabelList 그대로다
     (세로 지면 기준 축소배 약 .71 — 인쇄를 누른 창 폭에 따라 .5~.78).
     그래서 **축 눈금은 두 갈래로 크기를 갈라 쓰고**, LabelList 계열 규칙은 폴백 전용이다. */

  /* ⑥-1 훅이 돈 인쇄 — 축소가 없으므로 찍히는 크기 = 적은 크기.
     값 라벨은 여기 없다(자체 레이어가 그린다 — ⑦ 참조). */
  html.print-change .tab-panel-enter.is-change .recharts-cartesian-axis-tick-value {
    font-size: clamp(12px, 1.86vw, 15px);
    /* 세로 A4(726px)→13.5px ≈ 10.1pt (사용자 하한 10pt). 위 --pchg-label-fs 와 같은 값이다 —
       축 날짜와 값 라벨이 같은 크기로 읽혀야 하므로 한쪽만 고치지 말 것. */
  }

  /* 훅은 돌았는데 레이어가 아직 없는 상태(TrendChart 인쇄 레이어가 붙기 전 버전, 또는 어떤
     이유로 레이어를 못 그린 차트)에서는 LabelList 가 그대로 남는다 — 그때만 크기를 맞춰 준다.
     레이어가 있으면 이 규칙은 걸리지 않는다(:has 로 카드마다 따로 판단한다). */
  html.print-change .tab-panel-enter.is-change .change-card:not(:has(.print-value-labels)) .recharts-label-list text {
    font-size: clamp(12px, 1.86vw, 15px) !important;
    /* 세로 A4(726px)→13.5px ≈ 10.1pt — 1:1 이므로 축 눈금과 같은 값 */
  }

  /* ⑥-2 훅 없는 인쇄(Ctrl+P 폴백) — SVG 가 통째로 축소되므로 축소배를 되돌려 키운다 */
  html:not(.print-change) .tab-panel-enter.is-change .recharts-cartesian-axis-tick-value {
    font-size: clamp(16px, 2.48vw, 20px);
    /* 세로 A4(726px)→18px, 축소(.71) 뒤 약 12.7px ≈ 9.6pt */
  }

  html:not(.print-change) .tab-panel-enter.is-change .recharts-label-list text {
    font-size: clamp(16px, 2.48vw, 20px) !important;
    /* 726px→18px, 축소 뒤 ≈ 9.6pt. recharts 가 인라인 style 로 박아 !important 가 필요하다 */
  }

  /* 값 라벨을 점 가운데 정렬로 두면 **맨 왼쪽 점의 라벨이 y축 눈금 위로 올라탄다**(첫 점이
     플롯 왼쪽 끝이라 라벨 절반이 축 영역으로 넘어간다 — 실측에서 "56"과 "53.5"가 겹쳤다).
     점 오른쪽에서 시작하게 바꾸면 축과 부딪히지 않는다.
     **레이어가 그린 라벨에는 걸지 않는다** — 그쪽은 JS 가 자리를 잡으므로 정렬까지 JS 몫이다.
     훅 클래스가 아니라 `:has(.print-value-labels)` 로 가르는 이유: 레이어가 붙기 전 버전이나
     레이어를 못 그린 차트에서도 이 보호가 살아 있어야 한다(카드마다 따로 판단한다). */
  .tab-panel-enter.is-change .change-card:not(:has(.print-value-labels)) .recharts-label-list text {
    text-anchor: start;
  }

  /* [날짜 겹침 방지 — 두 갈래 공통] 세로 지면의 플롯 폭은 약 274px(차트 334 − y축 44 −
     오른 여백 16)이다. 날짜 한 개가 13.5px 에서 약 34px 이므로 눈금이 7개를 넘으면 서로
     붙는다(10일치 실측 한 칸 30px). 눈금이 7개 이상일 때만 짝수 번째를 감춘다 — 점·격자·선은
     그대로라 모양은 잃지 않고, 남는 날짜는 10pt 를 지킨다.
     ※ x축 눈금은 훅 경로에서도 recharts 가 그린다(레이어로 옮긴 것은 값 라벨뿐)이라 이 규칙은
       두 갈래 모두에 건다. */
  .tab-panel-enter.is-change .recharts-xAxis .recharts-cartesian-axis-tick:first-of-type:nth-last-of-type(n+7)~.recharts-cartesian-axis-tick:nth-of-type(even) {
    display: none;
  }

  /* 값 라벨 솎기도 **LabelList 를 쓰는 경우만**이다(레이어는 하나도 버리지 않고 자리를 옮겨
     피한다). 감추는 자리가 위 날짜 규칙과 같은 짝수 번째라 남는 날짜와 값이 짝을 이룬다. */
  .tab-panel-enter.is-change .change-card:not(:has(.print-value-labels)) .recharts-label-list text:first-of-type:nth-last-of-type(n+7)~text:nth-of-type(even) {
    display: none;
  }

  /* 마우스 툴팁은 지면에 의미가 없다. recharts 는 숨김 상태에서도 상자를 남겨 두는데,
     그 상자가 카드 오른쪽으로 28px 삐져나온다(실측) — 지면에서는 아예 지운다. */
  .tab-panel-enter.is-change .recharts-tooltip-wrapper {
    display: none;
  }

  /* 선·점 굵기 — TrendChart 의 인쇄 기하와 짝이다(JS 도 같은 값을 박는다). 한쪽만 바꾸면
     레이어가 계산한 라벨 자리와 실제 그림이 어긋난다. */
  .tab-panel-enter.is-change .recharts-line-curve {
    stroke-width: 2.5;
  }

  .tab-panel-enter.is-change .recharts-dot {
    r: 4;
  }

  /* ⑦ 인쇄용 값 라벨 레이어 (.print-value-labels) — TrendChart 가 인쇄 모드에서만 그린다.
     자리는 JS 가 픽셀 공간 충돌 회피로 잡고, **글자 크기도 JS 가 속성으로 박는다**
     (여기서 다시 정하면 배치 계산과 그림이 어긋난다 — font-size 를 넣지 말 것).
     CSS 는 안전망 하나만 맡는다: 배치 엔진이 끝내 피하지 못한 라벨이 선·점 위에 얹혀도
     읽히도록 글자 뒤에 흰 후광을 깐다. paint-order: stroke 라 테두리가 글자 **뒤**로 가고,
     linejoin: round 로 모서리가 뾰족하게 튀지 않는다. 화면 리허설에서도 같이 보이지만
     인쇄 대화상자가 뜨기 전 한순간이라 따로 가르지 않는다. */
  .tab-panel-enter.is-change .print-value-labels text {
    paint-order: stroke;
    stroke: #fff;
    stroke-width: 4px;
    stroke-linejoin: round;
  }

  /* 키운 값 라벨이 플롯 오른쪽 끝에서 SVG 경계에 잘리지 않게 한다(SVG 루트는 기본 overflow:
     hidden). 넘치는 폭은 카드 안쪽 padding 12px 안에서 끝나 이웃 카드와 겹치지 않는다. */
  .tab-panel-enter.is-change .change-chart svg {
    overflow: visible;
  }
}


/* ===== 15-2. 인쇄 — 상세리포트 + 변화 부록 = 한 번에 4쪽 (2026-09-30 사용자 지시) =====
   [동작] 상세리포트 [인쇄하기] → 모달 [변화 페이지도 함께 인쇄] 를 고른 동안만 frontend 가 리포트 탭
     안(상세리포트 뒤)에 변화 탭과 같은 마크업을 붙인다(history/page.js):
       <div class="tab-panel-enter is-change print-appendix" aria-hidden="true">…</div>
     카드 조판은 15-1 의 `.tab-panel-enter.is-change …` 규칙을 **그대로** 받는다(선택자 복제 없음 —
     부록이 곧 변화 탭 지면이다). 이 블록이 더하는 것은 넷뿐이다:
       ① 화면 숨김 ② 등장 모션 끄기 ③ 새 쪽에서 시작 ④ 지면 이름 정리.
   [특정도] 부록도 `.history-page .tab-panel-enter`(09 구역, 0,2,0 · display:grid)에 걸린다 —
     숨김 규칙은 그보다 무거운 `.tab-panel-enter.is-change.print-appendix`(0,3,0)로 적어 순서와
     무관하게 이긴다. 지면에서는 숨김이 풀리면 09 구역의 grid 가 그대로 살아 변화 탭과 같은 상자가 된다.
   ["상세리포트만 남긴다" 계열과의 관계 — 실측] 지면에서 지우는 규칙은 15 블록(.tab-switch ·
     .report-toolbar · .period-bar · .hero-banner · .modal-backdrop)과 global.css 14(.app-header ·
     .tabbar · .toast · .no-print)뿐이고, report.css 인쇄 규칙은 전부 `.report` 스코프다. 부록은 어느
     것에도 걸리지 않아 **예외 규칙을 따로 두지 않았다** — 부록 마크업 안에 저 클래스를 넣지 말 것. */

/* ② 등장 모션 끄기 — 필수. .tab-panel-enter 는 global.css 02-1 의 page-enter(opacity 0 → 1,
   backwards)를 받는다. 화면에서 display:none 이던 상자는 **지면에서 처음 상자가 생기는 순간** 이
   애니메이션을 0초 지점부터 시작하고, 인쇄는 그 한 장면을 찍는다 → 부록 전체가 투명(opacity 0)으로
   찍힌다. 화면에 보인 적이 없는 상자라 모션이 있을 이유도 없다. */
.tab-panel-enter.is-change.print-appendix {
  animation: none;
}

/* ① 화면 숨김 — 지면 전용 섹션. `@media screen` 에만 두어 지면 쪽 display 는 09 구역 값을
   그대로 받게 한다(인쇄 블록에 display 값을 다시 적으면 09 구역과 두 벌이 된다). */
@media screen {
  .tab-panel-enter.is-change.print-appendix {
    display: none;
  }
}

@media print {

  /* ③ 새 쪽에서 시작 — 상세리포트 3쪽 뒤에 변화 1쪽. 두 줄 병기는 report.css 인쇄 블록과 같은 이유 */
  .tab-panel-enter.is-change.print-appendix {
    break-before: page;
    page-break-before: always;
  }

  /* ④ 지면 이름 정리 — 부록이 있는 문서는 **이름 없는 지면(report.css `@page`) 하나로** 찍는다.
     15-1 은 `html:has(.tab-panel-enter.is-change)` 로 문서 뿌리에 change-page 를 거는데, 부록도
     `.is-change` 라 그대로 두면 상세리포트 3쪽까지 change-page 로 찍힌다. 규격은 지금 같지만
     (둘 다 A4 세로 · 9mm) 상세리포트 지면의 정본은 report.css `@page` 다 — 3쪽 예산을 그 지면
     그대로 지키도록 뿌리 이름을 되돌린다. 부록에만 이름을 거는 안은 쓰지 않는다: 문서 중간에서
     이름이 바뀌면 크롬이 강제 개행을 넣어 빈 장이 생긴 전력이 있다(15-1 [빈 장 방지]).
     이 규칙이 있으면 문서 전체가 한 이름(= 이름 없음)이라 바뀌는 자리가 아예 없다.
     특정도 (0,3,1) > 15-1 (0,2,1) 이라 순서와 무관하게 이긴다. 15-1 의 `.app-shell{min-height:0}`
     (셸 바닥이 뒤에 빈 장을 만들지 않게)는 부록 문서에도 그대로 걸리게 둔다.
     ※ 부록 지면(726×1054px) = 상세리포트 지면이라 15-1 의 --pchg-* 기하가 그대로 맞는다. 훗날
       change-page 규격을 바꾸면(가로 등) 부록은 따라가지 않는다 — 그때는 이 블록을 다시 볼 것. */
  html:has(.tab-panel-enter.is-change.print-appendix) {
    page: auto;
  }
}


/* ===== [frontend-dev] 홈 인사 영역 — 시안 07 우측 일러스트 ===== */
/* .greet-block 은 09 구역의 1열 그리드. 여기서 텍스트/그림 2열로 확장만 한다. */
.home-page .greet-block {
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--sp-4);
}

.home-page .greet-text {
  display: grid;
  gap: var(--sp-1);
  min-width: 0;
  /* [design-css] 좁은 폭에서 한글이 음절 단위로 끊기지 않도록 어절 단위 줄바꿈 */
  word-break: keep-all;
}

/* [design-css] 시안 07 기준 확대 — 우측 상단 여백을 크게 차지한다.
   align-self:end 로 그림 아래끝을 블록 아래끝(= 카드 그리드 위끝)에 맞춰 간격 0 을 만든다.
   블록 흐름 안에 있으므로 위로는 콘텐츠 영역까지만 자라고 상단 헤더(흰 바)를 침범하지 않는다. */
.home-page .greet-art {
  flex: 0 0 auto;
  display: block;
  align-self: end;
  /* 높이 −10% (사용자 지시 2026-08-16). 비율 고정 이미지라 폭을 0.9배로 줄인다 */
  width: clamp(135px, 25.2vw, 275px);
}

/* [design-css] 모바일에서도 인사말 일러스트를 보여준다(기존 display:none 제거).
   텍스트 폰트 하한을 낮춰(08 구역) 좁은 폭에서도 2단 배치를 유지한다. */
@media (max-width: 700px) {
  .home-page .greet-block {
    gap: var(--sp-3);
  }

  /* [2026-08-26 사용자 지시] 모바일에서 그림이 카드 위에 작게 떠 보였다 — 390px 에서 폭 112px·
     높이 68px 뿐이라 인사 문구 높이(92px)에 못 미쳐, align-self:end 로 아래를 맞춰도 그림
     위·오른쪽으로 배경이 비쳤다. 기울기를 세워 **좁은 폭은 종전 값 그대로**(320px→104px —
     제목 "안녕하세요, ○○○ 님!"이 한 줄로 남는 한계폭) 두고 390px 부터 키운다.
     390px→153px 는 그림 높이(92px)가 문구 높이와 같아지는 값이라 배너 높이가 그대로고
     아래 카드 그리드도 1px 움직이지 않는다. 상한 180px 는 종전과 같아 700px 경계에서
     데스크톱 규칙(25.2vw)으로 매끄럽게 이어진다. (기존 clamp(104px, 28.8vw, 180px)) */
  .home-page .greet-art {
    width: clamp(104px, calc(-120px + 70vw), 180px);
    /* 320px→104px, 390px→153px, 428px 이상→180px */
  }
}


/* ===== [frontend-dev] 문진 인트로 일러스트 ===== */
/* 1.35배 확대 (사용자 지시 2026-08-17) — 문진 첫 화면이라 시선을 먼저 끌어야 한다 */
.survey-intro .intro-art {
  display: block;
  width: clamp(216px, 43.2vw, 405px);
  margin: 0 auto var(--sp-4);
}


/* ===== [frontend-dev] 문진 단계 보강 (머리말 / 3-1 중첩 / 마무리 배너) ===== */
.survey-page .survey-header {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
}

.survey-page .survey-header-art {
  flex: 0 0 auto;
  /* [2026-08-24 사용자 지시 "더 크게"] 86 → 112px */
  width: clamp(68px, 8.8vw, 112px);
}

/* 문항 번호 + 질문을 한 줄로 (시안 04~06) */
.survey-page .question-head {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
}

.survey-page .question-head .question-index {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  min-width: 32px;
  height: 32px;
  padding: 0 var(--sp-2);
  border-radius: var(--r-pill);
  background: var(--blue);
  color: var(--text-invert);
}

/* 3-1 조건부 문항 — 문3 카드 안에 들여쓴 블록 */
.survey-page .sub-question {
  display: grid;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border: 1px solid var(--blue-line);
  border-radius: var(--r-lg);
  background: var(--blue-soft);
}

.survey-page .sub-condition {
  display: block;
  margin-bottom: var(--sp-1);
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 700;
  color: var(--blue);
}

/* 미응답 안내 */
.survey-page .survey-hint {
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 700;
  color: var(--text-mute);
  text-align: center;
}

/* 마지막 단계 하단 배너 (시안 06) */
.survey-page .survey-outro {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4);
  border-radius: var(--r-lg);
  background: var(--blue-soft);
}

.survey-page .survey-outro-art {
  flex: 0 0 auto;
  width: clamp(96px, 16vw, 190px);
}

.survey-page .survey-outro-title {
  display: block;
  font-size: clamp(17px, calc(14.8px + 0.562vw), 22px);
  /* 390px→17px, 1280px→22px */
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--blue-dark);
}

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

@media (max-width: 560px) {

  .survey-page .survey-header-art,
  .survey-page .survey-outro-art {
    display: none;
  }
}


/* ===== [frontend-dev] 측정이력 콘텐츠 폭 (리포트 1500px 대응) ===== */
/* AppShell 이 wide 모드에서 .app-inner(1100px) 래퍼를 벗기므로 여기서 폭을 잡는다 */
.history-page {
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;
}


/* ===== [frontend-dev] AI 종합 점수 도넛 (시안 12) — 폐기 ===== */
/* 2026-08-27 대시보드 개편으로 "종합 건강 점수(100−위험점수)" 지표 자체가 없어졌다.
   경위와 대체물은 파일 맨 끝 [dash-v2] 블록 머리 주석 참조(위험점수 바 .dash-risk 로 대체). */


/* ===== [frontend-dev] 문진 선택지 배열 — 시안 04·06 은 가로 4열 ===== */
/* 07 구역의 .choice-list 는 1열 그리드. 단일선택만 가로로 펼치고,
   복수선택(q5)은 문장이 길어 .is-stacked 로 1열을 유지한다. */
/* 문진 작성(스텝)·만족도 조사 화면도 같은 상단 여백 (2026-08-24) */
.survey-page {
  margin-top: var(--sp-4);
}

.survey-page .choice-list {
  grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
}

.survey-page .choice-list.is-stacked {
  grid-template-columns: 1fr;
}

@media (max-width: 560px) {

  /* 문진 작성(스텝)·만족도 조사 화면도 같은 상단 여백 (2026-08-24) */
  .survey-page {
    margin-top: var(--sp-4);
  }

  .survey-page .choice-list {
    grid-template-columns: 1fr;
  }
}


/* [2026-08-20 사용자 지시] 홈 화면의 높이(max-height) 기반 압축 분기 3종을 전부 제거했다 —
   높이에 따라 문구(.greet-memo, .info-lead)가 사라지고 카드 크기가 변하던 동작을 없앤다.
   이제 홈은 화면 높이와 무관하게 같은 레이아웃이고, 좁은 화면에서는 자연 스크롤된다. */






/* [2026-08-20 사용자 지시] 로그인 화면의 높이(max-height) 기반 압축 분기 2종 제거 —
   높이와 무관하게 같은 레이아웃, 좁은 화면에서는 자연 스크롤. (홈과 같은 정리) */



/* ===== [frontend-dev] 사용자 측정 입력 (시안 08~11) — .measure-user =====
   홈 "시작하기" → /measure/[type]. 현장 키오스크 동선이라 시안의 "관리자 입력 화면"
   안내박스를 그대로 둔다. 블록: .measure-user, 하위 역할 클래스만 사용.
   구성: .topbar-row(뒤로+제목) / .hero-row(.device-art | .hero-side) / .form-card / .action-row
   ※ admin.css 의 .measure-page 와는 별개 블록(사용자 앱은 admin.css 를 로드하지 않는다) */
.measure-user {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  width: 100%;
  /* 사용자 피드백(2026-08-13): 측정 입력도 콘텐츠 최대 폭 1500px 정책 */
  max-width: var(--content-max);
  margin: 0 auto;
}

/* --- 상단 뒤로가기 + 제목 --- */
.measure-user .topbar-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.measure-user .back-btn {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: var(--tap-min);
  height: var(--tap-min);
  border-radius: var(--r-md);
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--text-sub);
  font-size: clamp(24px, calc(20.49px + 0.899vw), 32px);
  /* 390px→24px, 1280px→32px */
  font-weight: 700;
  line-height: 1;
}

.measure-user .back-btn:hover {
  background: var(--blue-soft);
  border-color: var(--blue-line);
  color: var(--blue);
}

.measure-user .page-heading {
  min-width: 0;
  font-size: clamp(24px, calc(19.17px + 1.236vw), 35px);
  /* 390px→24px, 1280px→35px */
  font-weight: 700;
  letter-spacing: -.045em;
  line-height: 1.2;
  color: var(--text);
}

/* --- 기기 일러스트 + 안내/최근기록 --- */
/* [2026-08-17 사용자 지시] 일러스트가 우측 안내 상자보다 훨씬 높아 상단이 비대했다.
   두 열을 stretch 로 붙이고 그림 높이를 우측 열에 맞춘다 — 안내 상자가 짧으면 그림도
   같이 낮아져 상단 높이가 우측 기준으로 정해진다(contain 이라 비율은 그대로).
   max-height 는 안전 상한 — 우측에 최근 기록 상자까지 붙어 열이 길어질 때 그림만
   과하게 커지지 않게 잡아 둔다. */
.measure-user .hero-row {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  align-items: stretch;
  gap: var(--sp-4);
}

.measure-user .device-art {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 0;
  max-height: clamp(120px, calc(96.2px + 6.292vw), 176px);
  /* 390px→120px, 1280px→176px */
  object-fit: contain;
}

.measure-user .hero-side {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  min-width: 0;
}

.measure-user .notice-box {
  align-items: center;
  border: 1px solid var(--blue-line);
}

/* 최근 측정 기록 — 모달로 보던 정보를 입력 화면 안으로 옮긴 것 */
.measure-user .recent-box {
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}

.measure-user .recent-heading {
  margin-bottom: var(--sp-1);
  font-size: clamp(14px, calc(12.25px + 0.449vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--text-sub);
}

.measure-user .recent-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.measure-user .recent-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: 6px 0;
  border-bottom: 1px solid var(--line);
}

.measure-user .recent-item:last-child {
  border-bottom: 0;
}

.measure-user .item-date {
  flex: 0 0 auto;
  font-size: clamp(12px, calc(10.7px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
  font-weight: 600;
  color: var(--text-mute);
}

.measure-user .item-value {
  text-align: right;
  font-size: clamp(14px, calc(12.25px + 0.449vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--text);
}

.measure-user .recent-box .state-block {
  padding: var(--sp-3);
  gap: var(--sp-1);
}

/* --- 입력 폼 카드 --- */
.measure-user .form-card {
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

.measure-user .form-heading {
  margin-bottom: var(--sp-4);
  font-size: clamp(19px, calc(16.37px + 0.674vw), 25px);
  /* 390px→19px, 1280px→25px */
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--text);
}

.measure-user .field-grid {
  display: grid;
  gap: var(--sp-3) var(--sp-5);
  grid-template-columns: 1fr;
}

.measure-user .field-row {
  min-width: 0;
}

.measure-user .field-row.is-wide {
  grid-column: 1 / -1;
}

/* 측정일시는 열 수와 무관하게 한 줄 전체를 쓰되 입력칸만 적당한 폭으로 */
.measure-user .field-row.is-wide .input-unit {
  max-width: 420px;
}

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

.measure-user .label-sub {
  margin-left: 4px;
  font-size: clamp(12px, calc(10.7px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
  font-weight: 600;
  color: var(--text-sub);
}

/* 필수 뱃지 — 선택(회색)과 한 쌍. 필수는 블루 강조로 한눈에 갈리게 (2026-08-21 사용자 지시) */
.measure-user .label-req {
  margin-left: 6px;
  padding: 1px 8px;
  border-radius: var(--r-pill);
  background: var(--blue-soft);
  border: 1px solid var(--blue-line);
  font-size: clamp(11px, calc(10.12px + 0.225vw), 13px);
  /* 390px→11px, 1280px→13px */
  font-weight: 700;
  color: var(--blue);
}

.measure-user .label-opt {
  margin-left: 6px;
  padding: 1px 8px;
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  font-size: clamp(11px, calc(10.12px + 0.225vw), 13px);
  /* 390px→11px, 1280px→13px */
  font-weight: 700;
  color: var(--text-mute);
}

/* 입력칸 + 단위 태그 (시안 08~11 의 회색 단위 블록) */
.measure-user .input-unit {
  display: flex;
  align-items: stretch;
  overflow: hidden;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  transition: border-color .15s ease, box-shadow .15s ease;
}

.measure-user .input-unit:focus-within {
  border-color: var(--blue);
  box-shadow: 0 0 0 4px rgba(37, 99, 235, .14);
}

.measure-user .input-unit .field-input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 54px;
  border: 0;
  border-radius: 0;
  background: transparent;
}

.measure-user .input-unit .field-input:focus {
  box-shadow: none;
}

/* 숫자 입력 스피너 제거 — 단위 태그와 겹쳐 보이는 것을 막는다 */
.measure-user .input-unit .field-input::-webkit-outer-spin-button,
.measure-user .input-unit .field-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.measure-user .input-unit .field-input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}

.measure-user .unit-tag {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  min-width: 62px;
  padding: 0 var(--sp-3);
  border-left: 1px solid var(--line);
  background: var(--surface-sunken);
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 700;
  color: var(--text-sub);
}

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

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

/* 측정 구분 라디오 (시안 11) */
.measure-user .radio-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-5);
  min-height: 54px;
}

.measure-user .radio-item {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--tap-min);
  cursor: pointer;
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 700;
  color: var(--text);
}

.measure-user .radio-item input {
  width: 24px;
  height: 24px;
  accent-color: var(--blue);
  cursor: pointer;
}

/* --- 하단 버튼 --- */
.measure-user .action-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-3);
  margin-top: var(--sp-5);
}

/* 시안 08 은 2열 폼. 좁은 폭에서는 1열로 접는다 */
@media (min-width: 760px) {
  .measure-user.is-body .field-grid.is-two {
    grid-template-columns: 1fr 1fr;
  }

  /* 시안 09·11 처럼 라벨 왼쪽 / 입력칸 오른쪽 */
  .measure-user .field-grid.is-one .field-row {
    display: grid;
    grid-template-columns: minmax(0, 190px) minmax(0, 1fr);
    align-items: center;
    column-gap: var(--sp-4);
  }

  .measure-user .field-grid.is-one .row-label {
    margin-bottom: 0;
  }

  .measure-user .field-grid.is-one .row-hint,
  .measure-user .field-grid.is-one .row-error {
    grid-column: 2;
  }

  .measure-user .action-row {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr);
  }
}

/* 노트북 무스크롤 압축 (1366×768 / 1536×864) — 100dvh 안에 들어가게
   안내박스와 최근기록을 가로로 눕혀 히어로 높이를 절반으로 줄인다 */
@media (min-width: 1000px) and (max-height: 900px) {
  .measure-user {
    gap: var(--sp-3);
  }

  .measure-user .hero-row {
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.7fr);
  }

  .measure-user .hero-side {
    flex-direction: row;
    align-items: stretch;
  }

  .measure-user .hero-side>* {
    flex: 1 1 0;
    min-width: 0;
  }

  .measure-user .notice-box {
    padding: var(--sp-3);
  }

  .measure-user .recent-item:nth-child(n+3) {
    display: none;
  }

  /* 가로로 눕은 최근기록 칸은 폭이 좁다 — 요약값을 한 줄로 잘라 높이를 고정한다 */
  .measure-user .item-value {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }

  .measure-user .device-art {
    /* 상단 영역 20% 축소 비례 (154→123px) */
    max-height: 123px;
  }

  .measure-user .form-card {
    padding: var(--sp-4) var(--sp-5);
  }

  .measure-user .form-heading {
    margin-bottom: var(--sp-3);
  }

  .measure-user .field-grid {
    gap: var(--sp-2) var(--sp-5);
  }

  .measure-user .input-unit .field-input,
  .measure-user .radio-row {
    min-height: 48px;
  }

  .measure-user .action-row {
    margin-top: var(--sp-4);
  }

  .measure-user .action-row .btn-lg {
    min-height: 52px;
  }

  /* 항목이 8개인 신체계측만 3열로 더 압축 */
  .measure-user.is-body .field-grid.is-two {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* 2단계 압축 (1366×768 처럼 세로가 짧은 노트북) */
@media (min-width: 1000px) and (max-height: 820px) {
  .measure-user {
    gap: var(--sp-2);
  }

  .measure-user .back-btn {
    width: 40px;
    height: 40px;
  }

  .measure-user .device-art {
    max-height: 112px;
  }

  .measure-user .notice-box,
  .measure-user .recent-box {
    padding: var(--sp-2) var(--sp-3);
  }

  .measure-user .recent-box .state-block {
    padding: var(--sp-2);
    gap: 2px;
  }

  .measure-user .recent-box .state-icon {
    display: none;
  }

  .measure-user .form-card {
    padding: var(--sp-3) var(--sp-4);
  }

  .measure-user .form-heading {
    margin-bottom: var(--sp-2);
  }

  .measure-user .field-grid {
    gap: 6px var(--sp-4);
  }

  .measure-user .row-label {
    margin-bottom: 2px;
  }

  .measure-user .input-unit .field-input,
  .measure-user .radio-row {
    min-height: 40px;
  }

  .measure-user .action-row {
    margin-top: var(--sp-3);
  }

  .measure-user .action-row .btn-lg {
    min-height: 46px;
  }
}

/* 항목 8개짜리 신체계측만 4열로 — 세로가 짧은 노트북에서 행 하나를 줄인다 */
@media (min-width: 1280px) and (max-height: 820px) {
  .measure-user.is-body .field-grid.is-two {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* 모바일 390px — 기기 그림 위, 안내/최근기록 아래로 쌓는다 */
@media (max-width: 759px) {
  .measure-user .hero-row {
    grid-template-columns: 1fr;
    gap: var(--sp-3);
  }

  .measure-user .form-card {
    padding: var(--sp-4);
    border-radius: var(--r-lg);
  }

  .measure-user .action-row {
    grid-template-columns: 1fr;
  }
}


/* ============================================================
   [front-speed] 라우트 전환 스켈레톤 + 갱신 표시
   ------------------------------------------------------------
   목적: 라우트를 클릭한 즉시(데이터 도착 전에) 화면 뼈대를 그려
         "빈 화면에서 기다리는" 구간을 없앤다.
   사용처: app/**\/loading.js → components/RouteSkeleton.jsx
           + components/Skeleton.jsx (.sk 계열)
   색·라운드는 전부 :root 토큰(global.css). 하드코딩 없음.
   ============================================================ */

/* 스켈레톤 본문 래퍼 — 실제 페이지 본문과 같은 세로 리듬 */
.sk-page {
  display: grid;
  gap: var(--sp-4);
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;
}

/* 기본 블록 — admin.css 의 .adm-skel 과 같은 shimmer 톤 */
.sk {
  display: block;
  position: relative;
  overflow: hidden;
  width: 100%;
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
}

.sk::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .72), transparent);
  background-size: 200% 100%;
  animation: skShimmer 1.5s ease-in-out infinite;
}

@keyframes skShimmer {
  0% {
    background-position: -200% 0;
  }

  100% {
    background-position: 200% 0;
  }
}

/* 카드 자리 — 흰 카드와 같은 라운드 */
.sk-card {
  border-radius: var(--r-lg);
}

/* 배너 자리 */
.sk-hero {
  border-radius: var(--r-xl);
}

/* 지표 카드 그리드 — history 의 .metric-grid 와 같은 흐름 */
.sk-grid {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}

/* 2열 (측정 입력의 기기그림 + 안내/최근기록) */
.sk-two {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

/* 탭 3종 자리 */
.sk-tabs {
  display: grid;
  gap: var(--sp-2);
  grid-template-columns: repeat(3, minmax(0, 1fr));
  max-width: 520px;
}

/* 문장 여러 줄 자리 */
.sk-lines {
  display: grid;
  gap: var(--sp-2);
}

@media (max-width: 759px) {

  .sk-two,
  .sk-grid {
    grid-template-columns: 1fr;
  }
}

/* 움직임을 줄이는 설정에서는 shimmer 를 멈춘다 */
@media (prefers-reduced-motion: reduce) {
  .sk::after {
    animation: none;
  }
}

/* ── 저장본 표시 중 + 백그라운드 갱신 중 알림 ────────────────
   position: fixed 라 어떤 페이지에 넣어도 레이아웃을 밀지 않는다.
   하단 탭바 위로 띄운다. */
.arc-refresh {
  position: fixed;
  right: var(--sp-4);
  bottom: calc(var(--tabbar-h) + var(--sp-3));
  z-index: 60;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 6px var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  box-shadow: var(--shadow-card);
  color: var(--text-sub);
  font-size: clamp(12px, calc(11.1px + 0.225vw), 14px);
  /* 390px→12px, 1280px→14px */
  pointer-events: none;
}

.arc-refresh-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--blue);
  animation: arcPulse 1.1s ease-in-out infinite;
}

@keyframes arcPulse {

  0%,
  100% {
    opacity: .35;
  }

  50% {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .arc-refresh-dot {
    animation: none;
  }
}


/* ============================================================
   [user-ui-polish] 사용자 앱 입력 UI 고도화 (2026-08-12)
   ------------------------------------------------------------
   브라우저 기본 팝업(datetime-local · date · select)을 관리자와 같은 수준의
   커스텀 팝오버로 바꾼 공용 컴포넌트 스킨.
     components/DateTimeField.jsx  → .ui-dtf  (측정 입력 "측정 일시")
     components/DateRangeField.jsx → .ui-drf  (측정이력 대시보드 "조회 기간")
     components/SelectField.jsx    → .ui-sel  (측정이력 상세리포트 "날짜 선택")
   admin.css 도 같은 ui-* 이름을 관리자 톤으로 따로 정의한다
   (사용자 앱과 관리자는 서로의 스타일시트를 로드하지 않는다 — 충돌 없음).

   시니어 친화: 트리거 54px · 시간/옵션 버튼 44px 이상 · 큰 글자.
   좁은 화면(<=560px)에서는 JS 가 .is-sheet 를 붙여 화면 하단 시트로 띄운다.

   구성
     P1. 트리거 (ui-dtf-trigger / ui-sel-trigger)
     P2. 팝오버 · 하단 시트 · 머리말 · 푸터 (공용)
     P3. 시간 선택 (오전·오후 / 시 / 분 버튼 그리드)
     P4. 달력 rdp-ui (react-day-picker v10 오버라이드)
     P5. 기간 선택 (프리셋 · 달력 2개)
     P6. Select 패널
     P7. 페이지 적용 (측정입력 · 기간바 · 리포트 툴바)
     P8. 반응형
   ============================================================ */


/* ===== [user-ui-polish] P1. 트리거 ===== */
.ui-dtf,
.ui-drf {
  display: block;
  min-width: 0;
}

.ui-sel {
  display: inline-grid;
  min-width: 0;
}

.ui-sel.is-block {
  display: grid;
  width: 100%;
}

.ui-dtf-trigger,
.ui-sel-trigger {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  min-height: 54px;
  padding: 0 var(--sp-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--text);
  /* 상한 22px → 20px (2026-08-18 **사용자 직접 수정** — 에이전트가 무관 변경으로 오인해
     원복했다가 사용자 재지시로 확정. 다시 되돌리지 말 것) */
  font-size: clamp(17px, calc(15.68px + 0.337vw), 20px);
  /* 390px→17px, 1280px→20px */
  font-weight: 700;
  letter-spacing: -.02em;
  text-align: left;
  transition: border-color .15s ease, box-shadow .15s ease;
}

.ui-dtf-trigger:hover:not(:disabled),
.ui-sel-trigger:hover:not(:disabled) {
  border-color: var(--blue-line);
}

.ui-dtf-trigger.is-open,
.ui-sel-trigger.is-open {
  border-color: var(--blue);
  box-shadow: 0 0 0 4px var(--blue-soft);
}

.ui-dtf-trigger.is-invalid {
  border-color: var(--danger);
}

.ui-dtf-trigger:disabled,
.ui-sel-trigger:disabled {
  background: var(--surface-sunken);
  color: var(--text-mute);
}

.ui-dtf-icon {
  flex: 0 0 auto;
  color: var(--blue);
}

/* flex-basis 0 — 트리거의 최소 폭이 글자 길이에 끌려가지 않게 한다(좁은 화면 가로 스크롤 방지) */
.ui-dtf-text,
.ui-sel-text {
  flex: 1 1 0;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-variant-numeric: tabular-nums;
}

.ui-dtf-placeholder,
.ui-sel-placeholder {
  color: var(--text-mute);
  font-weight: 600;
}

.ui-dtf-chevron {
  flex: 0 0 auto;
  color: var(--text-mute);
  transition: transform .2s ease;
}

.ui-dtf-chevron.is-open {
  transform: rotate(-180deg);
}


/* ===== [user-ui-polish] P2. 팝오버 · 하단 시트 ===== */
/* body 포털 + fixed — 카드 overflow 에 잘리지 않는다 */
.ui-dtf-pop,
.ui-drf-pop {
  position: fixed;
  z-index: 70;
  width: max-content;
  max-width: calc(100vw - 24px);
  /* 창 높이가 낮은 노트북에서 패널이 화면 밖으로 나가지 않게 — 내부 스크롤 */
  max-height: calc(100dvh - 16px);
  overflow-y: auto;
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-pop);
}

/* 배경 딤 — 하단 시트 뒤를 어둡게 (전용 토큰이 없어 그림자와 같은 남색 계열로 맞춘다) */
.ui-dtf-backdrop {
  position: fixed;
  inset: 0;
  z-index: 69;
  background: rgba(23, 43, 77, .45);
}

.ui-dtf-pop.is-sheet,
.ui-drf-pop.is-sheet,
.ui-sel-sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  top: auto;
  z-index: 70;
  width: 100%;
  max-width: 100%;
  max-height: 88dvh;
  overflow-y: auto;
  padding: var(--sp-4) var(--sp-3) calc(var(--sp-5) + env(safe-area-inset-bottom));
  border: 0;
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  background: var(--surface);
  box-shadow: var(--shadow-pop);
  animation: uiSheetUp .18s ease-out;
}

@keyframes uiSheetUp {
  from {
    transform: translateY(14px);
    opacity: .6;
  }

  to {
    transform: translateY(0);
    opacity: 1;
  }
}

.ui-dtf-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding-bottom: var(--sp-3);
  margin-bottom: var(--sp-3);
  border-bottom: 1px solid var(--line);
}

.ui-dtf-head-title {
  font-size: clamp(16px, calc(14.25px + 0.449vw), 20px);
  /* 390px→16px, 1280px→20px */
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--text);
}

.ui-dtf-close {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: var(--tap-min);
  height: var(--tap-min);
  border-radius: var(--r-md);
  background: transparent;
  color: var(--text-mute);
  font-size: clamp(15px, calc(13.69px + 0.337vw), 18px);
  /* 390px→15px, 1280px→18px */
  font-weight: 700;
}

.ui-dtf-close:hover {
  background: var(--surface-sunken);
  color: var(--text);
}

.ui-dtf-body {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
}

.ui-dtf-cal {
  flex: 0 0 auto;
}

.ui-dtf-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding-top: var(--sp-3);
  margin-top: var(--sp-3);
  border-top: 1px solid var(--line);
}

.ui-dtf-today {
  flex: 0 0 auto;
  min-height: var(--tap-min);
  padding: 0 var(--sp-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--text-sub);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  font-weight: 700;
}

.ui-dtf-today:hover:not(:disabled) {
  border-color: var(--blue-line);
  background: var(--blue-soft);
  color: var(--blue-dark);
}

.ui-dtf-today:disabled {
  opacity: .45;
}

.ui-dtf-summary {
  flex: 1 1 auto;
  text-align: center;
  color: var(--text-sub);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.ui-dtf-summary.has-value {
  color: var(--blue-dark);
  font-weight: 700;
}

.ui-dtf-apply {
  flex: 0 0 auto;
  min-width: 104px;
  min-height: 48px;
  padding: 0 var(--sp-5);
}


/* ===== [user-ui-polish] P3. 시간 선택 (오전·오후 / 시 / 분) ===== */
.ui-dtf-time {
  flex: 0 0 auto;
  display: grid;
  align-content: start;
  gap: var(--sp-2);
  width: 214px;
}

.ui-dtf-seg {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  padding: 4px;
  border-radius: var(--r-md);
  background: var(--surface-sunken);
}

.ui-dtf-seg-btn {
  min-height: var(--tap-min);
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--text-sub);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  font-weight: 700;
}

.ui-dtf-seg-btn.is-on {
  background: var(--surface);
  color: var(--blue-dark);
  box-shadow: var(--shadow-card);
}

.ui-dtf-time-label {
  margin-top: 2px;
  font-size: clamp(13px, calc(12.12px + 0.225vw), 15px);
  /* 390px→13px, 1280px→15px */
  font-weight: 700;
  color: var(--text-sub);
}

.ui-dtf-grid {
  display: grid;
  gap: 5px;
}

.ui-dtf-grid.is-hour {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.ui-dtf-grid.is-min {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.ui-dtf-cell {
  min-height: var(--tap-min);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--text-sub);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  transition: border-color .12s ease, background .12s ease, color .12s ease;
}

.ui-dtf-cell:hover {
  border-color: var(--blue-line);
  background: var(--blue-soft);
  color: var(--blue-dark);
}

.ui-dtf-cell.is-on {
  border-color: var(--blue);
  background: var(--blue);
  color: var(--text-invert);
  font-weight: 700;
}


/* ===== [user-ui-polish] P4. 달력 rdp-ui ===== */
.rdp-ui.rdp-root {
  --rdp-accent-color: var(--blue);
  --rdp-accent-background-color: var(--blue-soft);
  --rdp-range_middle-background-color: var(--blue-soft);
  --rdp-range_middle-color: var(--text);
  --rdp-range_start-color: var(--text-invert);
  --rdp-range_end-color: var(--text-invert);
  --rdp-range_start-date-background-color: var(--blue);
  --rdp-range_end-date-background-color: var(--blue);
  --rdp-selected-border: 2px solid var(--blue);
  --rdp-today-color: var(--blue-dark);
  --rdp-outside-opacity: .38;
  --rdp-day-width: 44px;
  --rdp-day-height: 44px;
  --rdp-nav_button-width: 36px;
  --rdp-nav_button-height: 36px;
  color: var(--text);
  font-size: clamp(14px, calc(13.12px + 0.225vw), 16px);
  /* 390px→14px, 1280px→16px */
  font-weight: 600;
}

.rdp-ui .rdp-day_button {
  border-radius: var(--r-sm);
  font-variant-numeric: tabular-nums;
}

.rdp-ui .rdp-weekday {
  color: var(--text-mute);
  font-size: clamp(12px, calc(11.12px + 0.225vw), 14px);
  /* 390px→12px, 1280px→14px */
  font-weight: 700;
}

.rdp-ui .rdp-selected .rdp-day_button,
.rdp-ui .rdp-today:not(.rdp-selected) .rdp-day_button {
  font-weight: 700;
}

.rdp-ui .rdp-chevron {
  fill: var(--blue);
}

.rdp-ui .rdp-month_caption {
  padding: 2px 0 var(--sp-2);
}

.rdp-ui .rdp-dropdowns {
  display: flex;
  align-items: center;
  gap: 6px;
}

.rdp-ui .rdp-dropdown_root {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.rdp-ui .rdp-dropdown_root .rdp-caption_label {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  min-height: 38px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
  color: var(--text);
  font-size: clamp(14px, calc(13.12px + 0.225vw), 16px);
  /* 390px→14px, 1280px→16px */
  font-weight: 700;
}

.rdp-ui .rdp-dropdown_root:hover .rdp-caption_label {
  border-color: var(--blue);
}

.rdp-ui .rdp-dropdown_root .rdp-caption_label .rdp-chevron {
  width: 14px;
  height: 14px;
  fill: var(--text-mute);
}

.rdp-ui select.rdp-dropdown {
  cursor: pointer;
}

.rdp-ui .rdp-button_previous:hover,
.rdp-ui .rdp-button_next:hover {
  background: var(--surface-sunken);
  border-radius: var(--r-sm);
}


/* ===== [user-ui-polish] P5. 기간 선택 (프리셋 · 달력 2개) ===== */
.ui-drf-presets {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  padding-bottom: var(--sp-3);
  margin-bottom: var(--sp-3);
  border-bottom: 1px solid var(--line);
}

.ui-drf-preset {
  min-height: var(--tap-min);
  padding: 0 var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  color: var(--text-sub);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  font-weight: 700;
  transition: border-color .15s ease, background .15s ease, color .15s ease;
}

.ui-drf-preset:hover {
  border-color: var(--blue-line);
  background: var(--blue-soft);
  color: var(--blue-dark);
}

.ui-drf-cals {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
}

.ui-drf-divider {
  width: 1px;
  align-self: stretch;
  background: var(--line);
}

/* 적용된 기간의 일수 배지 (트리거 안) */
.ui-drf-days {
  flex: 0 0 auto;
  padding: 2px 10px;
  border-radius: var(--r-pill);
  background: var(--blue-soft);
  color: var(--blue-dark);
  font-size: clamp(13px, calc(12.12px + 0.225vw), 15px);
  /* 390px→13px, 1280px→15px */
  font-weight: 700;
}


/* ===== [user-ui-polish] P6. Select 패널 ===== */
.ui-sel-trigger {
  justify-content: space-between;
  min-width: 220px;
}

.ui-sel-panel {
  position: fixed;
  z-index: 95;
  max-width: min(420px, calc(100vw - 24px));
  max-height: 320px;
  margin: 0;
  padding: var(--sp-1);
  overflow-y: auto;
  list-style: none;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-pop);
}

/* 하단 시트 안에서는 패널이 시트의 본문이 된다 */
.ui-sel-panel.is-sheet {
  position: static;
  max-width: none;
  max-height: 56dvh;
  padding: 0;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

.ui-sel-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  min-height: 52px;
  padding: 0 var(--sp-4);
  border-radius: var(--r-md);
  color: var(--text-sub);
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 700;
  cursor: pointer;
  transition: background .12s ease, color .12s ease;
}

.ui-sel-option.is-active {
  background: var(--blue-soft);
  color: var(--blue-dark);
}

.ui-sel-option.is-selected {
  color: var(--blue-dark);
  font-weight: 700;
}

.ui-sel-option-text {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.ui-sel-check {
  flex: 0 0 auto;
  color: var(--blue);
}


/* ===== [user-ui-polish] P7. 페이지 적용 ===== */
/* 측정 입력(시안 08~11) — 측정 일시 트리거는 숫자 입력칸과 같은 폭으로 */
.measure-user .field-row.is-wide .ui-dtf {
  max-width: 420px;
}

/* 측정이력 대시보드 기간 바 — 네이티브 date 2개 → 기간 선택기 한 개 + 안내 */
.period-bar .period-label {
  flex: 0 0 auto;
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--text);
}

.period-bar .ui-drf {
  flex: 1 1 auto;
  min-width: 0;
  max-width: 560px;
}

.period-bar .period-note {
  flex: 0 0 auto;
  margin-left: auto;
  color: var(--text-sub);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  font-weight: 700;
}

/* 상세리포트 툴바 — 라벨 + 커스텀 Select (라벨과 선택창을 한 줄로, 사용자 지시 2026-08-16) */
.report-toolbar .report-pick {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-width: 0;
}

.report-toolbar .report-pick .field-label {
  flex: 0 0 auto;
  margin-bottom: 0;
  white-space: nowrap;
}

.report-toolbar .ui-sel {
  min-width: 260px;
}


/* ===== [user-ui-polish] P8. 반응형 ===== */
@media (max-width: 760px) {
  .period-bar .period-note {
    width: 100%;
    margin-left: 0;
  }
}

@media (max-width: 560px) {
  .period-bar {
    flex-wrap: wrap;
    gap: var(--sp-2) var(--sp-3);
  }

  .period-bar .ui-drf {
    flex: 1 1 100%;
    max-width: none;
  }

  /* 좁은 화면에서는 일수 배지를 접는다 — 날짜 두 개를 다 보여주는 쪽이 낫고,
     바로 아래 안내문("최근 N일 기록을 보고 있습니다.")이 같은 정보를 준다 */
  .ui-drf-days {
    display: none;
  }

  .report-toolbar .report-pick,
  .report-toolbar .ui-sel {
    flex: 1 1 100%;
    min-width: 0;
  }

  /* [2026-08-21] 휴대폰 가로 넘침 수정 — 상세리포트 탭이 좌우로 밀리던 원인.
     .history-page 는 display:grid 이고 .report-toolbar 는 그 그리드 아이템이라
     min-width:auto(=자동 최소 크기)가 걸린다. 그래서 툴바의 min-content 폭이
     그대로 그리드 열의 하한이 되고, 열이 컨테이너보다 넓어지면 .app-main
     (overflow-x:auto)이 그 넘침을 가로 스크롤로 만든다. 이때 문서 루트의
     scrollWidth 는 clientWidth 와 같아서 흔한 넘침 점검에는 잡히지 않는다.

     툴바의 min-content 를 만든 것은 .report-pick 한 줄이었다(실측 390px 뷰포트):
       라벨 "최근 측정 리포트 선택" 123.47px(nowrap, flex 0 0 auto)
       + gap 12px + .ui-sel 220px = 355.47px, + 툴바 패딩 32 + 테두리 2 = 389.47px.
     쓸 수 있는 폭은 366px(390 - .app-main 좌우 패딩 24) 뿐이라 23.47px 이 넘쳤다.
     이 하한은 뷰포트와 무관한 고정값이라 413px 미만 화면이 전부 같은 증상이었다.

     라벨을 줄이거나 지우지 않고 배치로 푼다 — .report-pick 을 접을 수 있게 하면
     라벨과 선택창이 각자 한 줄을 쓰고, 툴바의 min-content 는 가장 넓은 아이템
     하나(.ui-sel 220px)로 내려간다. min-width:0 은 그리드 아이템의 자동 최소
     크기를 아예 끊어 두는 이중 안전장치다(라벨 문구가 길어져도 다시 넘치지 않는다).
     .report-toolbar 는 .no-print 라 인쇄 지면에는 영향이 없고,
     561px 이상(데스크톱·태블릿)은 이 블록 밖이라 그대로다. */
  .report-toolbar {
    min-width: 0;
  }

  .report-toolbar .report-pick {
    flex-wrap: wrap;
  }

  /* 하단 시트 — 달력·시간을 세로로 쌓고 버튼을 화면 폭에 맞춰 넓힌다 */
  .ui-dtf-pop.is-sheet .ui-dtf-body {
    flex-direction: column;
    align-items: stretch;
  }

  .ui-dtf-pop.is-sheet .ui-dtf-cal,
  .ui-drf-pop.is-sheet .ui-drf-cals {
    display: grid;
    justify-items: center;
  }

  .ui-dtf-pop.is-sheet .ui-dtf-time {
    width: 100%;
  }

  .ui-dtf-pop.is-sheet .ui-dtf-grid.is-hour,
  .ui-dtf-pop.is-sheet .ui-dtf-grid.is-min {
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }

  .ui-drf-pop.is-sheet .ui-drf-preset {
    flex: 1 1 44%;
  }

  /* 시트 안에서 내용이 길면 내부 스크롤 — 제목과 [확인] 은 항상 보이게 고정한다 */
  .ui-dtf-pop.is-sheet .ui-dtf-head,
  .ui-drf-pop.is-sheet .ui-dtf-head {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--surface);
  }

  .ui-dtf-pop.is-sheet .ui-dtf-foot,
  .ui-drf-pop.is-sheet .ui-dtf-foot {
    position: sticky;
    bottom: 0;
    z-index: 1;
    padding-bottom: var(--sp-2);
    background: var(--surface);
  }

  .ui-dtf-foot {
    flex-wrap: wrap;
  }

  .ui-dtf-summary {
    order: -1;
    width: 100%;
    text-align: left;
  }

  .ui-dtf-today,
  .ui-dtf-apply {
    flex: 1 1 auto;
  }

  /* 390px 에서도 달력이 가로로 넘치지 않게 한 칸을 줄인다 */
  .rdp-ui.rdp-root {
    --rdp-day-width: 40px;
    --rdp-day-height: 40px;
  }
}

@media (prefers-reduced-motion: reduce) {

  .ui-dtf-pop.is-sheet,
  .ui-drf-pop.is-sheet,
  .ui-sel-sheet {
    animation: none;
  }
}


/* ============================================================
   [design-polish] 은은한 모션 — 사용자 앱
   원칙: 0.15~0.25s, 등장(enter)에만 건다. 차트·리포트 본문 등
   콘텐츠 자체에는 걸지 않는다. 블록 끝에서 prefers-reduced-motion 처리.
   ============================================================ */

/* ===== [design-polish] 모달 (.modal-backdrop / .modal-card) ===== */
/* 딤은 페이드만, 카드는 페이드 + 살짝 상승 + 살짝 확대 */
.modal-backdrop {
  animation: dim-in .18s ease-out both;
}

.modal-card {
  animation: pop-rise .2s cubic-bezier(.2, .7, .3, 1) both;
}

@keyframes dim-in {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@keyframes pop-rise {
  from {
    opacity: 0;
    transform: translateY(10px) scale(.98);
  }

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


/* ===== [design-polish] 팝오버 (달력 · 기간 · Select) ===== */
/* 데스크톱 팝오버는 페이드만 건다.
   popoverPos 의 useFitInViewport 가 렌더 직후 getBoundingClientRect() 로 패널을
   재서 화면 밖 삐져나감을 보정하는데, transform(이동·스케일)은 그 측정값을
   흔들어 보정이 건너뛰어진다(실제로 1px 넘침 발생). 위치를 건드리지 않는
   opacity 만 쓴다. 이동감은 아래 하단 시트(uiSheetUp)에서 살린다. */
.ui-dtf-pop:not(.is-sheet),
.ui-drf-pop:not(.is-sheet),
.ui-sel-panel:not(.is-sheet) {
  animation: pop-open .16s ease-out both;
}

@keyframes pop-open {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

/* 하단 시트 뒤 딤 — 시트(uiSheetUp)와 같은 호흡으로 페이드 */
.ui-dtf-backdrop {
  animation: dim-in .18s ease-out both;
}

/* 하단 시트는 useFitInViewport 대상이 아니라(sheet 면 즉시 return) 이동을 써도 안전하다.
   기존 uiSheetUp(.6 → 1)을 완전 페이드로 다듬는다. */
.ui-dtf-pop.is-sheet,
.ui-drf-pop.is-sheet,
.ui-sel-sheet {
  animation: sheet-up .22s cubic-bezier(.2, .7, .3, 1) both;
}

@keyframes sheet-up {
  from {
    opacity: 0;
    transform: translateY(18px);
  }

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


/* ===== [design-polish] 선택지 색 전환 (문진 · 측정 폼) ===== */
/* 문진 선택지: 카드 배경/테두리는 07 블록에서 이미 전환된다. 원형 마크도 같이 전환. */
.survey-page .choice-item {
  transition: background .18s ease, border-color .18s ease, color .18s ease;
}

.survey-page .choice-mark {
  transition: background .18s ease, border-color .18s ease, color .18s ease;
}

/* 측정 폼 라디오: 네이티브 accent-color 는 전환되지 않으므로
   선택된 항목의 글자색만 부드럽게 바꿔 "선택됨"을 거들어 준다. */
.measure-user .radio-item {
  transition: color .18s ease;
}

.measure-user .radio-item:has(input:checked) {
  color: var(--blue-dark);
}


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

  .modal-backdrop,
  .modal-card,
  .ui-dtf-pop:not(.is-sheet),
  .ui-drf-pop:not(.is-sheet),
  .ui-sel-panel:not(.is-sheet),
  .ui-dtf-backdrop,
  .ui-dtf-pop.is-sheet,
  .ui-drf-pop.is-sheet,
  .ui-sel-sheet {
    animation: none;
  }

  /* 스켈레톤 shimmer 도 함께 멈춘다 */
  .sk::after {
    animation: none;
  }
}


/* ===== [design-region-modal] 지역 선택 모달 모션 ===== */
/* 등장은 [design-polish] 의 dim-in / pop-rise 를 그대로 쓴다.
   여기서는 ① 퇴장(.is-closing) ② 390px 하단 시트 두 가지만 더한다.
   JSX 는 .is-closing 을 180ms 유지한 뒤 언마운트한다(RegionPicker.close). */
.modal-backdrop.is-closing {
  animation: dim-out .18s ease-in both;
}

.modal-backdrop.is-closing .modal-card {
  animation: pop-fall .18s ease-in both;
}

@keyframes dim-out {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
  }
}

@keyframes pop-fall {
  from {
    opacity: 1;
    transform: none;
  }

  to {
    opacity: 0;
    transform: translateY(8px) scale(.98);
  }
}

/* 모바일 — 아래에서 올라오는 시트 (Select·달력 하단 시트와 같은 결)
   [frontend-dev 2026-08-12] 390px 에서는 2패널이 너무 좁다 — 마크업은 그대로 두고
   CSS 로 활성 패널 하나만 보여 종전의 단계 교체(시/도 → 시/군/구 → 동) 동선을 유지한다. */
@media (max-width: 560px) {
  .region-modal {
    place-items: end stretch;
    padding: 0;
  }

  .region-modal .region-modal-card {
    width: 100%;
    height: min(86dvh, 620px);
    max-height: 86dvh;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    animation: sheet-up .22s cubic-bezier(.2, .7, .3, 1) both;
  }

  .region-modal.is-closing .region-modal-card {
    animation: sheet-down .2s ease-in both;
  }

  /* 제목·뒤로가기는 단계별 표기로 되돌린다 */
  .region-modal .region-title-wide {
    display: none;
  }

  .region-modal .region-title-narrow,
  .region-modal .region-trail-narrow {
    display: block;
  }

  .region-modal .region-modal-card.step-sigungu .region-back {
    visibility: visible;
    pointer-events: auto;
  }

  /* 한 번에 한 패널 — 구분선·번호 머리말은 헤더 제목과 중복이라 접는다 */
  .region-modal .region-panes {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }

  .region-modal .region-modal-card.step-sido .region-pane.is-sigungu,
  .region-modal .region-modal-card.step-sigungu .region-pane.is-sido {
    display: none;
  }

  .region-modal .region-pane-head {
    display: none;
  }

  .region-modal .region-pane.is-sigungu {
    padding-left: 0;
    border-left: 0;
  }

  .region-modal .region-pane-list.is-grid {
    grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  }

  .region-picker .region-grid {
    grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  }
}

@keyframes sheet-down {
  from {
    opacity: 1;
    transform: none;
  }

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

@media (prefers-reduced-motion: reduce) {

  .modal-backdrop.is-closing,
  .modal-backdrop.is-closing .modal-card,
  .region-modal .region-modal-card,
  .region-modal.is-closing .region-modal-card {
    animation: none;
  }
}


/* ===== [design-css] 데스크톱에는 하단 탭바가 없다 (global.css 분기와 짝) ===== */
/* .arc-refresh 는 main.css 정의라 global.css 의 같은 분기가 순서상 밀린다 — 여기서 한 번 더 */
@media (min-width: 701px) {
  .arc-refresh {
    bottom: var(--sp-4);
  }
}


/* ===== [design-ai-card] · [ai-grade-badge] — 삭제 (2026-08-27) =====
   여기에 있던 두 블록(.ai-summary .ai-score* SVG 스트로크 게이지 + .badge-grade-lg 확대 변형)은
   '종합 건강 점수(100−위험점수)' 게이지 전용 규칙이었다. 2026-08-27 대시보드 개편에서 그 게이지가
   폐지되고(plan_dashboard_v2 §8) 등급 카드(.dash-grade) + 위험점수 바(.dash-risk) 로 나뉘어 대체돼,
   어느 화면에서도 걸리지 않는 죽은 규칙이 됐다 — 라이브편집(개발자도구 왕복) 때 실제 화면에 없는
   130여 줄을 계속 스크롤하게 되므로 남겨 두지 않는다. 복구가 필요하면 이 커밋 직전 리비전에 있다.
   후속 정리 대상(이번 범위 밖): 같은 이유로 화면에서 내려간 10·11 구역의 .metric-grid · .ai-summary ·
   .advice-grid 계열. plan_dashboard_v2 §3-3 이 "이번 커밋에서는 손대지 않는다" 로 못박아 남겨 뒀다. */


/* ===== [measure-3col] 측정 입력 PC 3열 (사용자 피드백 2026-08-13) ===== */
/* PC(≥1100px)에서는 입력 필드를 한 줄에 3개씩. 라디오(측정 구분)는 한 줄 전체를 쓴다.
   is-one(혈압·혈당·산소포화도)의 "라벨 왼쪽" 행 배치는 3열에서는 라벨 위 배치로 되돌린다. */
@media (min-width: 1100px) {

  .measure-user .field-grid,
  .measure-user.is-body .field-grid.is-two {
    grid-template-columns: repeat(3, 1fr);
  }

  .measure-user .field-grid.is-one .field-row {
    display: block;
  }

  .measure-user .field-grid.is-one .row-label {
    margin-bottom: var(--sp-2);
  }

  .measure-user .field-grid .field-row.is-radio {
    grid-column: 1 / -1;
  }

  .measure-user .field-grid.is-one .row-hint,
  .measure-user .field-grid.is-one .row-error {
    grid-column: auto;
  }
}


/* ===== [period-picker] 측정이력 조회기간 — 측정일 범위 지정 (사용자 지시 2026-08-13) ===== */
/* 일반 달력을 걷어내고 "본인이 측정받은 날짜"만 고르게 한다.
   트리거 버튼 1개 → 팝오버에서 측정일 두 번 클릭으로 범위 확정(적용 버튼 없음).
   팝오버 껍데기·딤·등장 애니메이션은 기존 .ui-drf-pop / .ui-dtf-backdrop 규약을 그대로 쓰고
   여기서는 날짜 목록(.ui-mrf-*)과 기간 바 배치만 정의한다. */
.period-bar.period-picker {
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-4);
}

.period-picker .ui-mrf {
  display: grid;
  flex: 1 1 auto;
  min-width: 0;
  max-width: 480px;
}

/* 측정 기록이 아직 없거나 목록을 불러오는 중일 때의 안내 */
.period-picker .period-empty {
  flex: 1 1 auto;
  color: var(--text-sub);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  font-weight: 700;
}

/* 트리거 — 현재 범위를 그대로 읽어주는 버튼 하나 */
.ui-mrf-trigger {
  width: 100%;
  justify-content: flex-start;
}

/* 팝오버 — 날짜 목록이 길어지면 목록만 스크롤된다 */
.ui-mrf-pop {
  width: min(420px, calc(100vw - 24px));
}

.ui-mrf-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-3);
}

.ui-mrf-all {
  flex: 0 0 auto;
  min-height: var(--tap-min);
  padding: 0 var(--sp-4);
  border: 1px solid var(--blue-line);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--blue);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  font-weight: 700;
  letter-spacing: -.02em;
  transition: background .15s ease, opacity .15s ease;
}

.ui-mrf-all:hover:not(:disabled) {
  background: var(--blue-soft);
}

.ui-mrf-all:disabled {
  opacity: .45;
}

/* 지금 무엇을 고를 차례인지 알려준다 — 시니어 대상이라 문장으로 */
.ui-mrf-guide {
  flex: 1 1 auto;
  text-align: right;
  color: var(--blue);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  font-weight: 700;
  letter-spacing: -.02em;
}

.ui-mrf-list {
  display: grid;
  gap: var(--sp-2);
  max-height: 46dvh;
  padding: 2px;
  overflow-y: auto;
  list-style: none;
}

.ui-mrf-day {
  width: 100%;
  min-height: 52px;
  padding: 0 var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--text);
  font-size: clamp(16px, calc(14.25px + 0.449vw), 20px);
  /* 390px→16px, 1280px→20px */
  font-weight: 700;
  letter-spacing: -.02em;
  text-align: left;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}

.ui-mrf-day:hover {
  border-color: var(--blue-line);
  background: var(--blue-soft);
}

/* 고르는 중인 구간(첫 클릭 ~ 커서) · 이미 적용된 구간 — 연한 강조 */
.ui-mrf-day.is-in-range {
  border-color: var(--blue-line);
  background: var(--blue-soft);
  color: var(--blue-dark);
}

/* 구간의 양 끝 — 진하게 */
.ui-mrf-day.is-edge {
  border-color: var(--blue);
  background: var(--blue);
  color: var(--text-invert);
}

.ui-mrf-day.is-edge:hover {
  background: var(--blue-dark);
}

/* 첫 클릭 지점 — 종료일을 고르는 동안 기준점을 잃지 않게 테두리를 두껍게 */
.ui-mrf-day.is-anchor {
  box-shadow: 0 0 0 3px rgba(37, 99, 235, .22);
}

/* 좁은 화면(390px 포함) — 하단 시트. 목록이 화면을 넘지 않게 높이를 줄인다 */
@media (max-width: 560px) {
  .period-picker .ui-mrf {
    flex: 1 1 100%;
    max-width: none;
  }

  .ui-mrf-pop.is-sheet {
    width: 100%;
  }

  .ui-mrf-pop.is-sheet .ui-mrf-list {
    max-height: 42dvh;
  }

  .ui-mrf-guide {
    font-size: clamp(13px, calc(12.13px + 0.225vw), 15px);
    /* 390px→13px, 1280px→15px */
  }
}

/* ===== [login-gate] 로그인 안내 모달 (측정이력·설정 — 로그인 전 클릭 시) ===== */
.modal-card.login-gate {
  width: min(420px, calc(100% - 32px));
  text-align: center;
}

.login-gate .gate-icon {
  font-size: clamp(34px, calc(30.5px + 0.899vw), 42px);
  /* 390px→34px, 1280px→42px */
  line-height: 1;
  margin-bottom: var(--sp-3);
}

.login-gate .gate-title {
  font-size: clamp(18px, calc(16.25px + 0.449vw), 22px);
  /* 390px→18px, 1280px→22px */
  font-weight: 700;
  color: var(--text);
}

.login-gate .gate-desc {
  margin-top: var(--sp-2);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  color: var(--text-sub);
}

.login-gate .modal-foot {
  display: grid;
  grid-template-columns: 1fr 1.4fr;
  gap: var(--sp-3);
}

/* ============================================================
   [places] 측정 장소 (2026-08-14 사용자 확정 · 계약 §3-1)
   구성 ① 내비/탭바 "장소선택" 표기  ② 장소 선택 모달  ③ 측정입력 장소 바
        ④ 측정이력 장소 필터
   원칙: 색·크기는 전부 토큰 + clamp(390px→1280px). 인라인 style 은 쓰지 않는다.
   ============================================================ */

/* ===== [places] ① 상단 헤더 우측 — 장소 칩 (관리자 상단바와 같은 배치, 2026-08-14) ===== */
/* 우측 묶음: [장소 칩][회원명][로그아웃]. user-area 의 margin-left:auto 는 묶음이 대신 맡는다 */
.app-header .hdr-right {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-left: auto;
  min-width: 0;
}

.app-header .hdr-right .user-area {
  margin-left: 0;
}

/* 선택된 장소 칩 — 관리자 상단 칩(dvp-chip)과 같은 차콜 라운드 박스 + 옐로우 글자
   (사용자 지시 2026-08-14 — 파란 알약 칩에서 통일). 핀·이름·남은 시간·변경으로 구성. */
.app-header .nav-place-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 40px;
  max-width: 280px;
  padding: 0 var(--sp-3);
  border: 1px solid #3D4450;
  border-radius: var(--r-md);
  background: linear-gradient(180deg, #333945 0%, #272C35 100%);
  color: #FFD666;
  font-family: inherit;
  cursor: pointer;
}

/* ===== [places] 헤더 겹침 방지 — 1024~1440px (2026-08-27 사용자 지시) ===== */
/* [증상] 1280×1020 키오스크(실증 장비 폭)에서 우측 묶음[장소 칩 + 회원명 + 로그아웃]이
   좌측 내비의 "만족도 조사" 글자 **위를 덮었다**.
   [근본 원인] global.css 의 `.app-header .app-nav` 가 `position: absolute; left: 50%` 로
   **흐름 밖**에 있다. 화면 정중앙 고정이라 폭이 줄어도 자리를 비켜 주지 않고, 헤더의 flex 는
   존재하지 않는 요소로 취급해 우측 묶음을 그 위까지 밀어 넣는다. 1280px 실측:
     내비 오른쪽 끝 963px  ↔  우측 묶음 왼쪽 끝 790px  →  173px 겹침
   폭 조절(칩 max-width·글자 축약)만으로는 190px 을 걷어내야 해 장소명이 남지 않는다 —
   현장 운영 기능(장소명·변경 버튼)을 지우게 되므로 택하지 않았다.
   [해법] 겹침이 생기는 폭에서만 내비를 **흐름 안으로** 되돌린다. `position: static` +
   `margin-inline: auto` 면 브랜드와 우측 묶음 사이 남는 공간의 가운데에 서므로, flex 가
   세 덩어리를 나란히 배치해 겹침이 구조적으로 불가능해진다. 1440px 초과에서는 종전
   화면-정중앙 고정을 그대로 둔다(그 폭에서는 원래 겹치지 않는다). */
@media (max-width: 1440px) {
  .app-header .app-nav {
    position: static;
    transform: none;
    margin-inline: auto;
    min-width: 0;
    /* 항목 간 간격을 sp-6 → sp-4 로 좁혀 다섯 항목이 좁은 폭에서도 한 줄에 남게 한다 */
    gap: var(--sp-4);
  }

  /* 우측 묶음은 남는 폭 안에서만 자란다 — 내비를 밀어내지 않는다 */
  .app-header .hdr-right {
    min-width: 0;
    flex: 0 1 auto;
  }

  /* 칩은 가장 먼저 양보하는 조각이다. 장소명은 ellipsis 로 줄이되 지우지 않고,
     핀 아이콘·"변경" 버튼은 그대로 남긴다(현장 운영 기능). */
  .app-header .nav-place-chip {
    flex: 0 1 auto;
    max-width: clamp(150px, 17vw, 240px);
    /* 1024px→174px, 1440px→240px */
    min-width: 0;
  }

  .app-header .npc-name {
    text-overflow: ellipsis;
  }
}

.app-header .nav-place-chip:hover {
  border-color: rgba(255, 214, 102, .55);
}

.app-header .nav-place-chip:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.app-header .npc-ico {
  display: inline-flex;
  flex: 0 0 auto;
}

.app-header .npc-ico svg {
  width: 16px;
  height: 16px;
}

.app-header .npc-body {
  display: grid;
  gap: 1px;
  min-width: 0;
  text-align: left;
}

.app-header .npc-name {
  overflow: hidden;
  font-size: clamp(13px, calc(12.12px + 0.225vw), 15px);
  /* 390px→13px, 1280px→15px */
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.app-header .npc-time {
  /* 남은 시간은 보조 정보 — 옐로우 본문과 위계를 가르는 밝은 회색 */
  color: #B8BFCC;
  font-size: 11px;
  font-weight: 400;
  line-height: 1.2;
}

.app-header .npc-act {
  flex: 0 0 auto;
  padding-left: var(--sp-2);
  border-left: 1px solid currentColor;
  font-size: 12px;
  font-weight: 700;
  opacity: .8;
}

/* 미선택 상태 — 같은 차콜에 밝은 경고 옐로우로 "골라 달라"가 도드라지게 (관리자 칩과 동일 규칙) */
.app-header .nav-place-chip.is-empty {
  border-color: rgba(255, 201, 77, .6);
  color: #FFC94D;
}

/* ----- 모바일 (내비는 숨지만 장소 칩은 남긴다, 사용자 지시 2026-08-14) ----- */
@media (max-width: 700px) {
  .app-header .nav-place-chip {
    min-height: 36px;
    max-width: 180px;
    gap: var(--sp-1);
    padding: 0 var(--sp-2);
  }

  .app-header .npc-time {
    font-size: 10px;
  }

  /* 로그아웃은 아이콘만 — 칩 자리 확보 */
  .app-header .logout-text {
    display: none;
  }

  .app-header .logout-btn {
    min-width: 38px;
    justify-content: center;
    padding: 0 var(--sp-2);
  }
}

/* ===== [places] ① 하단 탭바 — 고른 장소가 있으면 이름을 그대로 보여 준다 ===== */
.tabbar .tab-item.is-place {
  min-width: 0;
  padding: 0 var(--sp-2);
}

.tabbar .tab-item.is-place .tab-name {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* ===== [places] ② 장소 선택 모달 =====
   .modal-backdrop / .modal-card 규약을 그대로 쓰고(등장 dim-in·pop-rise,
   퇴장 .is-closing) 목록 영역만 따로 스크롤시킨다 — 머리말·버튼은 늘 보인다. */
.place-modal .place-modal-card {
  display: flex;
  flex-direction: column;
  width: min(520px, 100%);
  max-height: 86dvh;
  overflow: hidden;
}

.place-modal .place-modal-card:focus {
  outline: none;
}

.place-modal .place-modal-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.place-modal .place-desc {
  margin-bottom: var(--sp-4);
  color: var(--text-sub);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  line-height: 1.5;
}

.place-modal .place-list {
  display: grid;
  gap: var(--sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* 시니어 친화 — 68px 이상 큰 버튼, 선택은 색 + 테두리 + "✓ 선택됨" 글자 3중 표기 */
.place-modal .place-item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  min-height: 68px;
  padding: var(--sp-3) var(--sp-4);
  border: 2px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  text-align: left;
  transition: border-color .15s ease, background .15s ease;
}

@media (hover: hover) {
  .place-modal .place-item:hover {
    border-color: var(--blue-line);
    background: var(--surface-soft);
  }
}

.place-modal .place-item.is-selected {
  border-color: var(--blue);
  background: var(--blue-soft);
}

.place-modal .place-item-pin {
  flex: 0 0 auto;
  font-size: clamp(20px, calc(17.37px + 0.674vw), 26px);
  /* 390px→20px, 1280px→26px */
  line-height: 1;
}

.place-modal .place-item-text {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  gap: 2px;
}

.place-modal .place-item-name {
  overflow: hidden;
  color: var(--text);
  font-size: clamp(17px, calc(15.25px + 0.449vw), 21px);
  /* 390px→17px, 1280px→21px */
  font-weight: 700;
  letter-spacing: -.02em;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.place-modal .place-item.is-selected .place-item-name {
  color: var(--blue-dark);
}

.place-modal .place-item-addr {
  overflow: hidden;
  color: var(--text-mute);
  font-size: clamp(13px, calc(12.13px + 0.225vw), 15px);
  /* 390px→13px, 1280px→15px */
  font-weight: 600;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.place-modal .place-item-mark {
  flex: 0 0 auto;
  color: var(--blue);
  font-size: clamp(13px, calc(12.13px + 0.225vw), 15px);
  /* 390px→13px, 1280px→15px */
  font-weight: 700;
  white-space: nowrap;
}

.place-modal .place-modal-foot {
  flex: 0 0 auto;
  border-top: 1px solid var(--line);
  padding-top: var(--sp-4);
}

/* 모바일 — 아래에서 올라오는 시트(지역 선택 모달과 같은 결) */
@media (max-width: 560px) {
  .place-modal {
    place-items: end stretch;
    padding: 0;
  }

  .place-modal .place-modal-card {
    width: 100%;
    max-height: 88dvh;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    animation: sheet-up .22s cubic-bezier(.2, .7, .3, 1) both;
  }

  .place-modal.is-closing .place-modal-card {
    animation: sheet-down .2s ease-in both;
  }

  .place-modal .place-modal-foot {
    flex-direction: column-reverse;
  }
}

@media (prefers-reduced-motion: reduce) {

  .place-modal .place-modal-card,
  .place-modal.is-closing .place-modal-card,
  .place-modal .place-item {
    animation: none;
    transition: none;
  }
}

/* ===== [places] ③ 측정입력 화면 상단 장소 바 =====
   차콜 배경 + 옐로우 글자 (사용자 지시 2026-08-14 — 파란 톤에서 고도화).
   페이지 전체가 밝은 톤이라 이 바만 진하게 눌러 "지금 어디서 재는지"가 한눈에 온다. */
.measure-user .place-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
  padding: var(--sp-2) var(--sp-4);
  border: 1px solid #3D4450;
  border-radius: var(--r-lg);
  /* 위가 살짝 밝은 차콜 — 단색보다 눌린 느낌이 덜하다 */
  background: linear-gradient(180deg, #333945 0%, #272C35 100%);
}

.measure-user .place-bar-pin {
  flex: 0 0 auto;
  display: inline-flex;
  color: #FFD666;
}

.measure-user .place-bar-pin svg {
  width: clamp(18px, calc(16.25px + 0.449vw), 22px);
  /* 390px→18px, 1280px→22px */
  height: auto;
}

.measure-user .place-bar-label {
  flex: 0 0 auto;
  color: #B8BFCC;
  font-size: clamp(13px, calc(12.12px + 0.225vw), 15px);
  /* 390px→13px, 1280px→15px */
  font-weight: 700;
}

.measure-user .place-bar-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  color: #FFD666;
  font-size: clamp(16px, calc(14.69px + 0.337vw), 19px);
  /* 390px→16px, 1280px→19px */
  font-weight: 700;
  letter-spacing: -.02em;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.measure-user .place-bar-name.is-empty {
  color: #98A0AE;
  font-weight: 700;
}

/* 차콜 배경 위 버튼 — 옐로우 라인 고스트 버튼으로 맞춘다 */
.measure-user .place-bar-change {
  flex: 0 0 auto;
  min-height: 38px;
  padding: 0 var(--sp-4);
  border-color: rgba(255, 214, 102, .55);
  background: transparent;
  color: #FFD666;
}

.measure-user .place-bar-change:hover {
  border-color: #FFD666;
  background: rgba(255, 214, 102, .1);
  color: #FFD666;
}

/* 모바일 — 줄바꿈 없이 한 줄 유지, 라벨을 숨겨 높이를 줄인다 (사용자 지시 2026-08-14) */
@media (max-width: 560px) {
  .measure-user .place-bar {
    gap: var(--sp-2);
    margin-bottom: var(--sp-3);
    padding: var(--sp-1) var(--sp-3);
  }

  .measure-user .place-bar-label {
    display: none;
  }

  .measure-user .place-bar-change {
    min-height: 34px;
    padding: 0 var(--sp-3);
  }
}

/* ===== [places] ④ 측정이력 장소 필터 (조회 기간 옆) ===== */
.period-bar .period-place-label {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
}

.period-bar .period-place {
  flex: 0 1 240px;
  min-width: 0;
}

/* 좁은 화면에서는 [조회 기간] 줄과 [측정 장소] 줄을 나눈다
   (기존 .period-bar 의 flex-wrap 분기는 560px 이라 여기서 900px 까지 앞당긴다) */
@media (max-width: 900px) {
  .period-bar {
    flex-wrap: wrap;
  }

  .period-bar .period-place {
    flex: 1 1 100%;
  }
}


/* ============================================================
   [recent-prefill] 홈 카드 "최근 완료" + 측정입력 프리필 안내 (2026-08-14 사용자 지시)
   ============================================================ */

/* ===== [recent-prefill] 홈 카드 — ✅ 최근 측정 8.14 09:30 · 미라클 크루즈 ===== */
.menu-grid .menu-recent {
  display: block;
  width: 100%;
  min-height: 1.35em;
  overflow: hidden;
  /* 한 단계 확대 (사용자 지시 2026-08-16 — 시니어 가독성) */
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 700;
  line-height: 1.35;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.menu-grid .menu-recent.is-done {
  color: var(--success);
}

.menu-grid .menu-recent.is-none {
  color: var(--text-mute);
  font-weight: 600;
}

/* 측정 이력 카드 — AI리포트 최근 생성 일시 (2026-08-20 사용자 지시, 붉은 텍스트) */
.menu-grid .menu-recent.is-report {
  color: #d13e30;
}

.menu-grid .menu-recent-mark {
  margin-right: 4px;
  font-size: .9em;
}

.menu-grid .menu-card.is-disabled .menu-recent {
  color: var(--text-mute);
}

/* [2026-08-20 정리] 이 자리에 있던 동일 미디어쿼리(노트북 압축 — 썸네일 44px·행 간격 등)는
   08 구역의 같은 쿼리 블록으로 병합했다 — 같은 셀렉터가 두 블록에서 서로 덮던 중복 제거. */

/* ===== [recent-prefill] 측정입력 — 지난 값 프리필 안내 문구 ===== */
.measure-user .form-prefill-note {
  margin: calc(var(--sp-2) * -1) 0 var(--sp-3);
  color: var(--text-sub);
  font-size: clamp(13px, calc(12.13px + 0.225vw), 15px);
  /* 390px→13px, 1280px→15px */
  font-weight: 600;
}


/* ============================================================
   [ai-report] 홈 "측정 이력" 카드 — 버튼 2개 (2026-08-16 사용자 지시)
   [확인하기](외곽선) + [AI리포트 생성](파랑) 을 한 줄에 나란히 둔다.
   ------------------------------------------------------------
   높이 규칙: 행(.menu-action-row)에는 여백을 주지 않고 버튼(.menu-action)이
   기존 margin-top 을 그대로 쓴다 → 08 구역·컴팩트 분기의 여백 조정이 그대로
   먹혀 버튼 1개 카드와 카드 높이가 항상 같다(노트북 무스크롤 유지).
   ============================================================ */

.menu-grid .menu-card.is-multi .menu-action-row {
  display: flex;
  gap: var(--sp-2);
  width: 100%;
}

/* flex-basis 0 으로 정확히 반씩 나눈다. 글자는 한 줄 고정 —
   줄바꿈이 생기면 이 카드만 높이가 늘어 그리드 정렬이 깨진다. */
.menu-grid .menu-card.is-multi .menu-action {
  flex: 1 1 0;
  min-width: 0;
  width: auto;
  padding: 0 var(--sp-2);
  white-space: nowrap;
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px — 버튼이 둘이라 기본(15~19px)보다 한 단계 낮춘다 */
}

/* 버튼이 둘인 카드에서는 "카드에 올리면 안쪽 버튼도 같이 반응"을 끈다 —
   두 버튼의 동작이 서로 달라 어느 쪽이 눌리는지 헷갈리면 안 된다.
   대신 각 버튼이 자기 위에 올렸을 때만 global.css 의 hover 색을 쓴다. */
@media (hover: hover) {
  .menu-grid .menu-card.is-multi:hover .menu-action.btn-blue {
    background: var(--blue);
    box-shadow: none;
  }

  .menu-grid .menu-card.is-multi:hover .menu-action.btn-blue:hover:not(:disabled) {
    background: var(--blue-dark);
    box-shadow: 0 6px 16px rgba(37, 99, 235, .28);
  }
}

/* 생성 중 — 눌린 상태가 글자("생성 중…")와 흐림 두 가지로 드러난다 */
.menu-grid .menu-card.is-multi .menu-action:disabled {
  cursor: default;
}

/* 모바일 1열(≤560px)에서도 두 버튼을 한 줄로 유지한다.
   390px 카드 안쪽 폭 ≈ 318px → 버튼 각 155px 로 "AI리포트 생성"(13px)이 넉넉히 들어간다. */
@media (max-width: 560px) {
  .menu-grid .menu-card.is-multi .menu-action-row {
    gap: var(--sp-1);
  }
}

/* ===== [ai-report] 상세리포트 — GPT 해설 작성 중 안내 (인쇄 제외) ===== */
.history-page .report-ai-pending {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: calc(var(--sp-2) * -1) 0 var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--blue-line);
  border-radius: var(--r-md);
  background: var(--blue-soft);
  color: var(--text-sub);
  font-size: clamp(13px, calc(12.13px + 0.225vw), 15px);
  /* 390px→13px, 1280px→15px */
  font-weight: 600;
  line-height: 1.4;
}

.history-page .report-ai-pending-ico {
  flex: none;
  font-size: 1.15em;
}

/* [ai-retry] 안내문과 버튼을 한 줄에 두되, 좁아지면 버튼이 아래로 내려간다 */
.history-page .report-ai-pending-text {
  flex: 1 1 260px;
}

/* 생성 실패 — 노랑(주의). 리포트 자체가 잘못된 것이 아니므로 등급 빨강은 쓰지 않는다 */
.history-page .report-ai-pending.is-failed {
  flex-wrap: wrap;
  border-color: var(--grade-attention-line);
  background: var(--grade-attention-bg);
}

.history-page .report-ai-pending .report-ai-retry {
  flex: none;
  min-height: var(--tap-min);
  padding: 0 var(--sp-4);
  font-size: clamp(14px, calc(13.13px + 0.225vw), 16px);
  /* 390px→14px, 1280px→16px */
}

@media (max-width: 560px) {

  /* 390px — 버튼을 한 줄 아래에 폭 전체로 둔다(시니어 터치 타깃 확보) */
  .history-page .report-ai-pending .report-ai-retry {
    flex: 1 0 100%;
  }
}


/* ============================================================
   [ai-report] AI 건강리포트 생성 진행 화면 (2026-08-16 사용자 지시)
   components/ReportProgressModal.jsx — 4단계 진행 목록.
   14 구역 .modal-backdrop / .modal-card 규약을 그대로 쓰고 목록만 덧입힌다.
   상태 3중 표기: 색(테두리·배경) + 표식(✓/숫자/스피너/⏱/!) + 오른쪽 글자.
   폰트는 전부 clamp(390px→1280px) 기준.
   ============================================================ */

.rpg-modal .rpg-card {
  width: min(560px, 100%);
}

.rpg-card .rpg-body {
  display: grid;
  gap: var(--sp-4);
}

/* ----- 머리말 ----- */
.rpg-card .rpg-head {
  display: grid;
  justify-items: center;
  gap: var(--sp-1);
  text-align: center;
}

/* ----- AI 분석 장면 (2026-08-20 사용자 지시 — 로봇 + 스캔 링 + 데이터 궤도 + 심전도) -----
   진행 중(.is-run)에만 움직인다. 완료·오류 화면에서는 정지 상태의 차분한 그림이 된다. */
.rpg-card .rpg-hero {
  position: relative;
  width: min(240px, 70vw);
  height: 128px;
  margin-bottom: var(--sp-1);
}

.rpg-card .rpg-hero-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.rpg-card .rpg-hero-bot {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: clamp(46px, 12vw, 56px);
  height: auto;
}

.rpg-hero.is-run .rpg-hero-bot {
  animation: rpg-bot-float 2.6s ease-in-out infinite;
}

/* 스캔 링 — 퍼지는 파동. 정지 상태에서는 가장 안쪽 링 하나만 옅게 */
.rpg-hero .rpg-ring {
  fill: none;
  stroke: var(--blue-line);
  stroke-width: 1.5;
  opacity: 0;
  transform-origin: 120px 64px;
}

.rpg-hero .rpg-ring.r1 {
  opacity: .55;
}

.rpg-hero.is-run .rpg-ring {
  animation: rpg-ring-pulse 2.4s ease-out infinite;
}

.rpg-hero.is-run .rpg-ring.r2 {
  animation-delay: .8s;
}

.rpg-hero.is-run .rpg-ring.r3 {
  animation-delay: 1.6s;
}

/* 데이터 점 궤도 — 진행 중에만 회전 */
.rpg-hero .rpg-orbit {
  transform-origin: 120px 64px;
}

.rpg-hero.is-run .rpg-orbit {
  animation: rpg-orbit-spin 9s linear infinite;
}

.rpg-hero .rpg-orbit-path {
  fill: none;
  stroke: var(--blue-line);
  stroke-width: 1;
  stroke-dasharray: 3 5;
  opacity: .7;
}

.rpg-hero .rpg-dot {
  stroke: #fff;
  stroke-width: 2;
}

.rpg-hero .rpg-dot.d1 {
  fill: #d13e30;
}

.rpg-hero .rpg-dot.d2 {
  fill: #6B21A8;
}

.rpg-hero .rpg-dot.d3 {
  fill: #16a34a;
}

.rpg-hero .rpg-dot-glyph {
  fill: #fff;
  font-size: 9px;
  font-weight: 700;
}

/* 심전도 라인 — 대시가 흘러가며 "신호를 읽는 중" 느낌 */
.rpg-hero .rpg-ecg {
  stroke: var(--blue);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: .5;
  stroke-dasharray: 260 640;
  stroke-dashoffset: 0;
}

.rpg-hero.is-run .rpg-ecg {
  opacity: .8;
  animation: rpg-ecg-flow 2.8s linear infinite;
}

@keyframes rpg-bot-float {

  0%,
  100% {
    transform: translate(-50%, -50%);
  }

  50% {
    transform: translate(-50%, calc(-50% - 5px));
  }
}

@keyframes rpg-ring-pulse {
  0% {
    transform: scale(.62);
    opacity: .75;
  }

  100% {
    transform: scale(1.22);
    opacity: 0;
  }
}

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

@keyframes rpg-ecg-flow {
  to {
    stroke-dashoffset: -900;
  }
}

/* 궤도 점들이 뒤집혀 보이지 않게 — 점(글리프 포함)만 역방향으로 되돌린다.
   바깥 g 의 translate(위치)를 CSS transform 이 덮어쓰면 안 되므로 안쪽 래퍼에만 건다. */
.rpg-hero.is-run .rpg-dot-wrap {
  transform-box: fill-box;
  transform-origin: center;
  animation: rpg-orbit-counter 9s linear infinite;
}

@keyframes rpg-orbit-counter {
  to {
    transform: rotate(-360deg);
  }
}

/* 모션 민감 사용자 — 전부 정지 */
@media (prefers-reduced-motion: reduce) {

  .rpg-hero.is-run .rpg-hero-bot,
  .rpg-hero.is-run .rpg-ring,
  .rpg-hero.is-run .rpg-orbit,
  .rpg-hero.is-run .rpg-dot-wrap,
  .rpg-hero.is-run .rpg-ecg {
    animation: none;
  }
}

.rpg-card .rpg-title {
  font-size: clamp(20px, calc(17.37px + 0.674vw), 26px);
  /* 390px→20px, 1280px→26px */
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--text);
  word-break: keep-all;
}

.rpg-card .rpg-sub {
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  color: var(--text-sub);
  word-break: keep-all;
}

/* ----- 단계 목록 ----- */
.rpg-card .rpg-steps {
  display: grid;
  gap: var(--sp-2);
  list-style: none;
  margin: 0;
  padding: 0;
}

.rpg-card .rpg-step {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: var(--tap-min);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-sunken);
  transition: background .18s ease-out, border-color .18s ease-out;
}

.rpg-card .rpg-step-mark {
  flex: none;
  display: grid;
  place-items: center;
  width: clamp(34px, calc(31.37px + 0.674vw), 40px);
  height: clamp(34px, calc(31.37px + 0.674vw), 40px);
  /* 390px→34px, 1280px→40px */
  border-radius: var(--r-pill);
  background: var(--line);
  color: var(--text-mute);
  font-size: clamp(15px, calc(13.69px + 0.337vw), 18px);
  /* 390px→15px, 1280px→18px */
  font-weight: 700;
  line-height: 1;
}

.rpg-card .rpg-step-text {
  flex: 1 1 auto;
  display: grid;
  gap: 2px;
  min-width: 0;
}

.rpg-card .rpg-step-label {
  font-size: clamp(16px, calc(14.25px + 0.449vw), 20px);
  /* 390px→16px, 1280px→20px */
  font-weight: 700;
  color: var(--text);
  word-break: keep-all;
}

.rpg-card .rpg-step-desc {
  font-size: clamp(13px, calc(12.13px + 0.225vw), 15px);
  /* 390px→13px, 1280px→15px */
  color: var(--text-mute);
  word-break: keep-all;
}

.rpg-card .rpg-step-state {
  flex: none;
  font-size: clamp(12px, calc(10.7px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
  font-weight: 700;
  color: var(--text-mute);
  white-space: nowrap;
}

/* 진행 중 — 파랑으로 강조 */
.rpg-card .rpg-step.is-run {
  border-color: var(--blue-line);
  background: var(--blue-soft);
}

.rpg-card .rpg-step.is-run .rpg-step-mark {
  background: var(--blue);
}

.rpg-card .rpg-step.is-run .rpg-step-label,
.rpg-card .rpg-step.is-run .rpg-step-state {
  color: var(--blue);
}

/* [ai-retry] 자동 재시도 중 — 노랑. 진행 중이긴 하지만 "한 번 더" 라는 것이 드러나야 한다 */
.rpg-card .rpg-step.is-retry {
  border-color: var(--grade-attention-line);
  background: var(--grade-attention-bg);
}

.rpg-card .rpg-step.is-retry .rpg-step-mark {
  background: var(--grade-attention);
}

.rpg-card .rpg-step.is-retry .rpg-step-label,
.rpg-card .rpg-step.is-retry .rpg-step-state {
  color: var(--grade-attention);
}

/* 완료 — 초록 */
.rpg-card .rpg-step.is-done {
  border-color: var(--grade-normal-line);
  background: var(--grade-normal-bg);
}

.rpg-card .rpg-step.is-done .rpg-step-mark {
  background: var(--grade-normal);
  color: var(--text-invert);
}

.rpg-card .rpg-step.is-done .rpg-step-state {
  color: var(--grade-normal);
}

/* 계속 진행(제한 시간 초과) — 노랑. 실패가 아니다 */
.rpg-card .rpg-step.is-late {
  border-color: var(--grade-attention-line);
  background: var(--grade-attention-bg);
}

.rpg-card .rpg-step.is-late .rpg-step-mark {
  background: var(--grade-attention);
  color: var(--text-invert);
}

.rpg-card .rpg-step.is-late .rpg-step-state {
  color: var(--grade-attention);
}

/* 실패 — 빨강 */
.rpg-card .rpg-step.is-fail {
  border-color: var(--grade-consult-line);
  background: var(--grade-consult-bg);
}

.rpg-card .rpg-step.is-fail .rpg-step-mark {
  background: var(--grade-consult);
  color: var(--text-invert);
}

.rpg-card .rpg-step.is-fail .rpg-step-state {
  color: var(--grade-consult);
}

/* 스피너 — 진행 중 표식 안에서만 돈다 */
.rpg-card .rpg-spin {
  width: 55%;
  height: 55%;
  border: 3px solid rgba(255, 255, 255, .45);
  border-top-color: var(--text-invert);
  border-radius: var(--r-pill);
  animation: rpg-spin .8s linear infinite;
}

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

/* ----- 안내 / 오류 줄 ----- */
.rpg-card .rpg-note,
.rpg-card .rpg-error {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  padding: var(--sp-3);
  border: 1px solid var(--blue-line);
  border-radius: var(--r-md);
  background: var(--blue-soft);
  color: var(--text-sub);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  font-weight: 600;
  line-height: 1.45;
  word-break: keep-all;
}

/* [ai-retry] 해설만 실패 — 오류(빨강)가 아니라 주의(노랑). 리포트 본문은 멀쩡하다 */
.rpg-card .rpg-warn {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  padding: var(--sp-3);
  border: 1px solid var(--grade-attention-line);
  border-radius: var(--r-md);
  background: var(--grade-attention-bg);
  color: var(--text-sub);
  font-size: clamp(13px, calc(12.13px + 0.225vw), 15px);
  /* 390px→13px, 1280px→15px */
  font-weight: 600;
  line-height: 1.45;
  text-align: left;
}

.rpg-card .rpg-warn-ico {
  flex: none;
  font-size: 1.1em;
  color: var(--grade-attention);
}

.rpg-card .rpg-error {
  border-color: var(--grade-consult-line);
  background: var(--grade-consult-bg);
  color: var(--grade-consult);
}

.rpg-card .rpg-note-ico,
.rpg-card .rpg-error-ico {
  flex: none;
  font-size: 1.1em;
  line-height: 1.35;
}

.rpg-card .rpg-foot .btn {
  flex: 1 1 0;
}

/* 완료 발 — [닫기](보조) + [상세리포트 보기](주). 자동 이동 대신 이 버튼으로 넘어간다.
   주버튼을 넓게 잡아(1 : 1.8) 어느 쪽이 다음 걸음인지 크기로도 드러낸다.
   두 버튼 높이는 flex 기본 stretch 로 btn-lg(58px)에 맞춰 나란해진다. */
.rpg-card .rpg-foot.is-done {
  display: grid;
  grid-template-columns: 1fr 1.8fr;
  gap: var(--sp-3);
}

.rpg-card .rpg-foot.is-done .btn {
  flex: none;
}

/* [ai-retry] 해설만 실패한 발 — [닫기] · [상세리포트 보기] · [다시 시도] 셋.
   [2026-09-10 사용자 지시] **주행동이 [상세리포트 보기](.rpg-go) 로 바뀌었다.** 해설이 없어도
   본문은 룰엔진 결과라 그대로 보는 것이 기본 동선이고, [다시 시도](.rpg-again)는 보조다.
   그래서 넓은 칸을 .rpg-again → .rpg-go 로 옮긴다(DOM 순서 [닫기][상세리포트 보기][다시 시도]는
   그대로다 — 열 폭만 바뀐다). 완료 발(.is-done)의 1 : 1.8 과 같은 결로 주버튼이 가장 넓다.
   셋 다 44px 이상(.btn 기본). */
.rpg-card .rpg-foot.is-failed {
  display: grid;
  grid-template-columns: .9fr 1.6fr 1.1fr;
  gap: var(--sp-2);
}

.rpg-card .rpg-foot.is-failed .btn {
  flex: none;
  padding: 0 var(--sp-2);
  font-size: clamp(15px, calc(13.69px + 0.337vw), 18px);
  /* 390px→15px, 1280px→18px */
}

/* 보조 버튼도 시니어 기준을 지킨다 — 글자만 한 단계 작다 */
.rpg-card .rpg-foot .rpg-close {
  border-radius: var(--r-lg);
  font-size: clamp(16px, calc(14.25px + 0.449vw), 20px);
  /* 390px→16px, 1280px→20px */
  font-weight: 700;
}

/* 모바일 — 아래에서 올라오는 시트(장소 선택 모달과 같은 결) */
@media (max-width: 560px) {
  .rpg-modal {
    place-items: end stretch;
    padding: 0;
  }

  .rpg-modal .rpg-card {
    width: 100%;
    max-height: 92dvh;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    animation: sheet-up .22s cubic-bezier(.2, .7, .3, 1) both;
  }

  /* 390px 에서는 설명 줄을 접어 4단계가 한 화면에 다 보이게 한다 */
  .rpg-card .rpg-step-desc {
    display: none;
  }

  /* [ai-retry] 버튼 3개는 390px 한 줄에 안 들어간다 — 주행동을 위로 올리고 둘을 아래에 나눈다.
     [2026-09-10] 위로 올라가는 주행동이 [다시 시도] → [상세리포트 보기](.rpg-go)로 바뀌었다.
     아래 줄은 자동 배치로 [닫기] | [다시 시도] 가 된다(DOM 순서 그대로). */
  .rpg-card .rpg-foot.is-failed {
    grid-template-columns: 1fr 1fr;
  }

  .rpg-card .rpg-foot.is-failed .rpg-go {
    grid-column: 1 / -1;
    grid-row: 1;
  }
}

@media (prefers-reduced-motion: reduce) {

  .rpg-modal .rpg-card {
    animation: none;
  }

  .rpg-card .rpg-spin {
    animation-duration: 2.4s;
  }
}


/* ============================================================
   [field-guard] 측정 입력 — 선택 항목 안내 / 두 칸 관계 오류 (2026-08-17)
   ------------------------------------------------------------
   · .form-optional-note … "신장·체중만 입력해도 저장됩니다" (체성분기 없는 현장)
   · .form-cross-error   … 혈압 전치처럼 **칸 하나에 붙일 수 없는** 오류.
                           칸별 .row-error 와 톤을 맞추되 폼 폭 전체를 쓰고 아이콘을 단다.
   ============================================================ */

.measure-user .form-optional-note {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--blue-line);
  border-radius: var(--r-md);
  background: var(--blue-soft);
  color: var(--text-sub);
  font-size: clamp(14px, calc(13.12px + 0.225vw), 16px);
  /* 390px→14px, 1280px→16px */
  font-weight: 600;
  line-height: 1.45;
  word-break: keep-all;
}

.measure-user .form-cross-error {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  margin: var(--sp-3) 0 0;
  padding: var(--sp-3);
  border: 1px solid var(--grade-consult-line);
  border-radius: var(--r-md);
  background: var(--grade-consult-bg);
  color: var(--grade-consult);
  font-size: clamp(14px, calc(13.12px + 0.225vw), 16px);
  /* 390px→14px, 1280px→16px */
  font-weight: 700;
  line-height: 1.5;
  word-break: keep-all;
}


/* ============================================================
   [inbody] 측정입력(체성분) — 체성분기 연동 UI (2026-09-23, 계약 §14-9)
   ------------------------------------------------------------
   인바디가 웹훅으로 보낸 최근 측정을 폼 빈칸에 채워 넣는 동선. 수기 입력 UI 는
   그대로 두고 **표시 요소 4개만** 더한다.
     · .form-head-row    … 폼 제목 + [인바디 최근 측정 불러오기] 를 한 줄에
     · .inbody-load-btn  … 그 버튼. 색은 .btn-outline-blue 그대로 쓰고 치수만 맞춘다
     · .form-inbody-note … 상태 안내(회색) / 불러오기 성공 출처(파랑) 한 줄
     · .label-inbody     … 칸 라벨 옆 "이 값은 장비에서 왔다" 뱃지
   전부 .measure-user 안에서만 정의한다(다른 화면의 같은 이름과 섞이지 않게).
   ============================================================ */

/* 제목의 margin-bottom 을 래퍼로 옮기지 않고 baseline 정렬로 감싼다.
   위쪽 압축 분기 3곳(max-height 900/820, 기본)이 .form-heading 의 margin-bottom 으로
   폼 전체 높이를 조절하고 있어서, 여백을 래퍼로 옮기면 노트북 무스크롤 계산이 어긋난다. */
.measure-user .form-head-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2) var(--sp-3);
}

.measure-user .inbody-load-btn {
  flex: 0 0 auto;
  /* 높이는 .btn 기본값(min-height: var(--tap-min) = 44px)을 그대로 쓴다 — 시니어 터치 타깃 */
  padding: 0 var(--sp-4);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px — 제목(19~25px) 옆이라 한 단계 낮춘다 */
}

/* 안내 줄 — "아직 데이터가 없다/받는 중이다"는 **오류가 아니다**.
   .form-cross-error 의 붉은 톤을 쓰지 않는 이유가 그것이다. */
.measure-user .form-inbody-note {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-sunken);
  color: var(--text-sub);
  font-size: clamp(14px, calc(13.12px + 0.225vw), 16px);
  /* 390px→14px, 1280px→16px — .form-optional-note 와 같은 눈높이 */
  font-weight: 600;
  line-height: 1.45;
  word-break: keep-all;
}

/* 값이 실제로 들어온 줄만 파랑 — 폼 안의 숫자가 어디서 왔는지를 알리는 신호다 */
.measure-user .form-inbody-note.is-source {
  border-color: var(--blue-line);
  background: var(--blue-soft);
  color: var(--blue-dark);
}

/* **이미 저장된 측정**을 다시 불러온 줄 (2026-09-23 사용자 지시로 이 동선이 생겼다).
   [왜 색을 바꾸나] 파랑(is-source)은 "방금 잰 값이 들어왔다"는 신호로 이미 쓰고 있다.
   같은 파랑으로 두면 새 측정과 지난 측정이 구분되지 않아, 담당자가 이미 저장한 값을
   새로 잰 값으로 착각한 채 저장 버튼을 누른다(결과는 같지만 판단이 틀린다).
   경고가 아니라 **주의**라서 노랑 계열(--grade-attention-*)을 쓴다 — 오류 빨강이 아니다.
   글자는 --ck-ink-warn 과 같은 yellow-700(#A16207): --grade-attention-bg 위 4.58:1 로 AA 통과다
   (원색 --grade-attention 은 3.x 라 미달 — [checkup] C1 에서 같은 이유로 한 단계 내렸다). */
.measure-user .form-inbody-note.is-source.is-saved {
  border-color: var(--grade-attention-line);
  background: var(--grade-attention-bg);
  color: #A16207;
}

/* [inbody-pick] 최근 인바디 기록 피커 (2026-09-28, 계약 §14-9 17)
   라벨 + 공용 SelectField. 체성분(body) 화면에서는 진입 즉시 항상 나타난다(2026-09-29 계약 §14-9 22 —
   불러오는 중·기록 없음은 비활성 placeholder, 24시간 안 최신 기록은 선택된 상태).
   · 자리: .form-head-row **안**, 제목과 [불러오기] 사이. 위 .form-head-row 주석과 같은 이유로
     줄을 새로 만들지 않는다 — 1366×768 실측에서 별도 줄(≈66px)은 [저장하기]를 화면 밖으로 밀고,
     제목 줄 안이면 트리거(54px)가 버튼(44px)보다 큰 만큼(+7px)만 늘어난다.
   · margin-left:auto — 제목은 왼쪽, [피커][불러오기]는 오른쪽에 붙는다(space-between 의 가운데 방지).
   · 트리거 높이는 공용 .ui-sel-trigger(54px) 그대로 둔다(시니어 터치 타깃).
   · 759px 이하는 제목 줄이 grid 로 쌓이므로 auto 여백을 걷어 폭 전체를 쓰고,
     560px 이하는 라벨 위·선택칸 아래로 쌓는다(그 폭에서 목록은 하단 시트로 뜬다). */
.measure-user .inbody-pick-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-3);
  margin: 0 0 0 auto;
}

.measure-user .inbody-pick-label {
  flex: 0 0 auto;
  color: var(--text);
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px — 칸 라벨(.row-label)과 같은 크기: "입력 칸 이름" 과 같은 위계다 */
  font-weight: 700;
  letter-spacing: -.02em;
  word-break: keep-all;
}

@media (max-width: 759px) {
  .measure-user .inbody-pick-row {
    margin-left: 0;
  }
}

@media (max-width: 560px) {
  .measure-user .inbody-pick-row {
    flex-direction: column;
    align-items: stretch;
  }
}

/* 칸별 출처 뱃지 — .label-req/.label-opt 와 같은 치수, 채운 파랑으로 구분한다.
   nowrap: 4열 압축 분기에서 "인바디 산출"이 두 줄로 접히면 행 높이가 튄다. */
.measure-user .label-inbody {
  margin-left: 6px;
  padding: 1px 8px;
  border-radius: var(--r-pill);
  background: var(--blue);
  border: 1px solid var(--blue);
  font-size: clamp(11px, calc(10.12px + 0.225vw), 13px);
  /* 390px→11px, 1280px→13px */
  font-weight: 700;
  color: var(--text-invert);
  white-space: nowrap;
}

/* ── 상세값 패널 (2026-09-23 사용자 지시 "들고오는 값 전부 보기 좋게") ──────────────
   [인바디 최근 측정 불러오기]가 폼 7칸을 채우고 나면, 인바디가 함께 보낸 나머지 값은
   안내 줄의 "상세항목 16개" 라는 **개수**로만 남았다. 그 값들을 펼쳐 보여 주는 자리다.

   자리: 폼 그리드 **아래**, [저장하기] 위 (page.js 렌더 주석 참조). 기본 접힘.
   색·라운드·그림자를 새로 만들지 않는다 — 카드는 .form-card 의 어휘(--line/--r-md/
   --surface), 값 타일은 [dash-v2] .mini-value/.mini-unit 의 "큰 숫자 + 작은 단위" 어휘,
   조판은 [checkup] C5-2 항목 행과 같은 판단(숫자는 tabular-nums 로 세로로 줄 세운다).

   구성
     .inbody-detail-toggle … 펼침 버튼(44px+, "보기/접기"가 글자로 보인다)
     .range-list / .range-row … 표준범위 쌍 + 폼에 든 내 값
     .detail-grid / .detail-item … 낱개 값 타일
     .detail-extra … 저장되지 않는 참고값(있을 때만)
   ------------------------------------------------------------------------------ */
.measure-user .inbody-detail {
  margin-top: var(--sp-4);
}

/* 접기 버튼 — 폼 폭 전체를 쓰는 가로 막대. 시니어 터치 타깃이라 높이를 --tap-min 으로 잡고
   글자도 입력 라벨과 같은 급으로 둔다(작은 "더보기" 링크로 만들지 않는다). */
.measure-user .inbody-detail-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  width: 100%;
  min-height: var(--tap-min);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-sunken);
  color: var(--text-sub);
  text-align: left;
  transition: border-color .15s ease, background .15s ease, color .15s ease;
}

.measure-user .inbody-detail-toggle:hover {
  border-color: var(--blue-line);
  background: var(--blue-soft);
  color: var(--blue-dark);
}

.measure-user .inbody-detail-toggle .toggle-text {
  min-width: 0;
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px — .row-label 과 같은 급 */
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.35;
  word-break: keep-all;
}

.measure-user .inbody-detail-toggle .toggle-mark {
  flex: 0 0 auto;
  font-size: clamp(13px, calc(11.69px + 0.337vw), 16px);
  /* 390px→13px, 1280px→16px */
  line-height: 1;
}

.measure-user .inbody-detail-body {
  display: grid;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
  padding: var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-soft);
}

/* 묶음 머리말 — 숫자 나열이 아니라 "무엇을 보는 중인지" 를 알려 준다 */
.measure-user .detail-sub {
  margin: 0;
  font-size: clamp(13px, calc(11.69px + 0.337vw), 16px);
  /* 390px→13px, 1280px→16px */
  font-weight: 700;
  line-height: 1.4;
  color: var(--text-sub);
  word-break: keep-all;
}

/* 두 번째 묶음부터는 앞 묶음과 떨어뜨린다(그리드 gap 위에 한 단만 더) */
.measure-user .detail-sub:not(:first-child) {
  margin-top: var(--sp-2);
}

/* --- 표준범위 쌍 --- */
.measure-user .range-list {
  display: grid;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.measure-user .range-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px var(--sp-3);
  min-width: 0;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}

.measure-user .range-label {
  flex: 0 0 auto;
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--text);
}

/* 지금 폼에 들어 있는 값 — 이 줄의 주인공이다(표준범위는 그 값을 읽는 잣대) */
.measure-user .range-mine {
  font-size: clamp(17px, calc(14.81px + 0.562vw), 22px);
  /* 390px→17px, 1280px→22px */
  font-weight: 800;
  letter-spacing: -.03em;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.measure-user .range-std {
  font-size: clamp(15px, calc(13.69px + 0.337vw), 18px);
  /* 390px→15px, 1280px→18px */
  font-weight: 600;
  color: var(--text-sub);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.measure-user .range-unit {
  margin-left: 3px;
  font-size: clamp(13px, calc(12.12px + 0.225vw), 15px);
  /* 390px→13px, 1280px→15px — 단위는 값보다 작게, 색으로도 한 단계 낮춘다 */
  font-weight: 700;
  color: var(--text-sub);
}

/* 표준범위 안/밖 — 장비가 함께 보낸 자기 기준과 비교한 사실 표시다(진단이 아니다).
   등급 토큰을 그대로 쓴다: 안은 --grade-normal, 밖은 --grade-attention(경고색 아님). */
.measure-user .range-state {
  margin-left: auto;
  flex: 0 0 auto;
  padding: 1px 9px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--surface-sunken);
  font-size: clamp(12px, calc(10.7px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
  font-weight: 700;
  white-space: nowrap;
  color: var(--text-mute);
}

.measure-user .range-state.is-in {
  border-color: var(--grade-normal-line);
  background: var(--grade-normal-bg);
  color: var(--grade-normal);
}

.measure-user .range-state.is-out {
  border-color: var(--grade-attention-line);
  background: var(--grade-attention-bg);
  color: var(--grade-attention);
}

/* --- 낱개 값 타일 --- */
.measure-user .detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(146px, 1fr));
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.measure-user .detail-item {
  display: grid;
  gap: 2px;
  min-width: 0;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}

.measure-user .detail-label {
  font-size: clamp(13px, calc(11.69px + 0.337vw), 16px);
  /* 390px→13px, 1280px→16px */
  font-weight: 700;
  line-height: 1.3;
  color: var(--text-sub);
  word-break: keep-all;
}

/* 값 + 단위 — [dash-v2] .mini-value/.mini-unit 과 같은 어휘, 여기는 행 안이라 인라인 */
.measure-user .detail-value {
  font-size: clamp(18px, calc(15.37px + 0.674vw), 24px);
  /* 390px→18px, 1280px→24px */
  font-weight: 800;
  letter-spacing: -.035em;
  line-height: 1.2;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.measure-user .detail-unit {
  margin-left: 3px;
  font-size: clamp(13px, calc(12.12px + 0.225vw), 15px);
  /* 390px→13px, 1280px→15px */
  font-weight: 700;
  letter-spacing: 0;
  color: var(--text-sub);
}

/* --- 저장되지 않는 참고값 (서버가 보내올 때만 렌더된다) ---
   저장되는 값과 **같은 모양으로 두지 않는다**: 바탕을 눌러(--surface-sunken) 한 단계 뒤로
   보내고 숫자도 한 급 낮춘다. 단위가 확정되지 않은 값이 섞일 수 있어 큰 숫자로 띄우면
   사용자가 kg 으로 읽는다. */
.measure-user .detail-extra {
  margin-top: var(--sp-2);
  padding-top: var(--sp-3);
  border-top: 1px dashed var(--line);
}

.measure-user .detail-sub.is-extra {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  color: var(--text-mute);
}

.measure-user .extra-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(146px, 1fr));
  gap: 6px;
  margin: var(--sp-2) 0 0;
  padding: 0;
  list-style: none;
}

/* [2026-09-23 수정 — QA] 라벨·값을 **위아래로** 쌓는다. 한 줄(label ↔ value)로 두면
   146px 타일 안에 "ECW_RATIO 0.365 단위 미확인"(최소폭 233px)이 들어가지 못해 글자가
   타일 밖으로 흘러 옆 타일을 덮었다(1280px에서 15개 중 14개). 저장되는 값(.detail-item)과
   달리 여기 라벨은 장비 원문 코드라 길이를 예측할 수 없고 "단위 미확인"까지 붙는다 —
   폭에 기대지 않는 세로 배치가 유일하게 안전한 모양이다. */
.measure-user .extra-item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  min-width: 0;
  padding: 6px var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-sunken);
}

.measure-user .extra-label {
  max-width: 100%;
  font-size: clamp(13px, calc(11.69px + 0.337vw), 16px);
  /* 390px→13px, 1280px→16px */
  font-weight: 600;
  line-height: 1.3;
  color: var(--text-sub);
  /* 장비 원문 코드(`ECW_RATIO`·`RA_5kHz`)는 공백이 없어 줄바꿈 지점이 없다 —
     keep-all 만 두면 타일을 뚫고 나간다. 어디서든 끊어 타일 안에 가둔다. */
  word-break: keep-all;
  overflow-wrap: anywhere;
}

.measure-user .extra-value {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  max-width: 100%;
  font-size: clamp(15px, calc(13.69px + 0.337vw), 18px);
  /* 390px→15px, 1280px→18px — 저장되는 값(18~24px)보다 한 급 낮다 */
  font-weight: 700;
  color: var(--text-sub);
  font-variant-numeric: tabular-nums;
}

.measure-user .extra-unit {
  margin-left: 3px;
  font-size: clamp(12px, calc(10.7px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
  font-weight: 700;
  color: var(--text-mute);
}

/* 단위를 모르는 값은 그 사실을 적는다 — 빈칸으로 두면 사용자가 kg 이라고 읽는다.
   "단위 미확인" 은 단위 기호가 아니라 문장이라 굵기를 낮추고 줄바꿈만 막는다. */
.measure-user .extra-unit.is-unknown {
  font-weight: 600;
  white-space: nowrap;
}

/* 노트북 무스크롤 화면(1366×768 · 1536×864)에서 펼치면 [저장하기]가 화면 밖으로 나간다.
   패널만 스크롤시켜 버튼을 화면 안에 남긴다 — 접기 버튼도 위에 그대로 보인다. */
@media (min-width: 1000px) and (max-height: 900px) {
  .measure-user .inbody-detail-body {
    max-height: 44vh;
    overflow: auto;
  }
}

/* 모바일 — 타일 폭이 146px 최소치에 걸려 1열로 늘어지는 구간이 있다. 2열로 고정한다 */
@media (max-width: 759px) {

  .measure-user .detail-grid,
  .measure-user .extra-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  /* 판정 칩이 오른쪽 끝으로 밀리면 좁은 폭에서 홀로 한 줄을 쓴다 — 값 옆에 붙인다 */
  .measure-user .range-state {
    margin-left: 0;
  }
}

@media (max-width: 759px) {

  /* 좁은 폭에서는 제목 옆에 버튼이 들어가지 않는다 — 아래로 내리고 폭 전체를 준다.
     그러면 제목의 margin-bottom 이 제목·버튼 사이로 들어가 버리므로 여백을 래퍼로 옮긴다
     (모바일에는 위 압축 분기가 걸리지 않아 기본값 --sp-4 하나만 맞추면 된다). */
  .measure-user .form-head-row {
    display: grid;
    /* [inbody-pick] 기본 규칙의 space-between 이 grid 에도 걸려 auto 열이 늘어나지 않았다 —
       "폭 전체" 가 실제로는 버튼 글자 폭(390px 실측 173px)에 멈춰 있었다. 열을 늘려 버튼·피커가
       정말 폭 전체를 쓰게 한다(2026-09-28). */
    justify-content: stretch;
    gap: var(--sp-2);
    margin-bottom: var(--sp-4);
  }

  .measure-user .form-head-row .form-heading {
    margin-bottom: 0;
  }

  .measure-user .inbody-load-btn {
    width: 100%;
  }
}


/* ============================================================
   [ai-cancel] 진행 모달 탈출구 — 늦어질 때만 나오는 [그만두기] (2026-08-17)
   ------------------------------------------------------------
   실패 통보가 아니라 "기다릴지 말지" 를 묻는 자리라 오류색을 쓰지 않는다.
   안내 한 줄 위, 버튼 한 줄 아래(세로 배치) — 담당자가 문장을 읽고 누르게.
   ============================================================ */

.rpg-card .rpg-foot.is-slow {
  display: grid;
  gap: var(--sp-2);
}

.rpg-card .rpg-slow-text {
  margin: 0;
  color: var(--text-sub);
  font-size: clamp(14px, calc(13.12px + 0.225vw), 16px);
  /* 390px→14px, 1280px→16px */
  font-weight: 600;
  line-height: 1.45;
  text-align: center;
  word-break: keep-all;
}

.rpg-card .rpg-foot.is-slow .rpg-cancel {
  flex: none;
  min-height: 52px;
  border-radius: var(--r-lg);
  font-size: clamp(16px, calc(14.25px + 0.449vw), 20px);
  /* 390px→16px, 1280px→20px */
  font-weight: 700;
}


/* ============================================================
   [a11y-senior] 시니어 기준 — 터치 타깃 44px / 모바일 본문 하한 14px (2026-08-17)
   ------------------------------------------------------------
   대상은 **모바일 폭(≤700px)** 뿐이다. 데스크톱은 clamp 최대값 구간이라 이미 기준을
   넘고, 노트북 무스크롤 압축(높이 기준 분기)과도 부딪히지 않는다.
   ============================================================ */

/* 로그아웃 — 38px 였다. 데스크톱에서도 44px 로 올린다(글자 크기는 그대로) */
.app-header .logout-btn {
  min-height: 44px;
}

@media (max-width: 700px) {

  /* 아이콘만 남는 모바일에서도 정사각 44px 를 지킨다 */
  .app-header .logout-btn {
    min-width: 44px;
    min-height: 44px;
  }

  /* 장소 칩 — 측정 전 반드시 누르는 버튼이라 로그아웃과 같은 기준으로 */
  .app-header .nav-place-chip {
    min-height: 44px;
  }

  /* ----- 본문 하한 14px ----- */
  /* 홈 카드 설명 · 홈 안내박스 · 폼 힌트/오류는 실제로 읽는 문장이다.
     최대값은 건드리지 않고 좁은 화면의 하한만 12~13px → 14px 로 올린다. */
  .menu-grid .menu-desc,
  .menu-grid .menu-recent,
  .home-page>.info-box .info-body,
  .survey-page .survey-hint,
  .measure-user .row-hint,
  .measure-user .row-error,
  .measure-user .form-prefill-note,
  .field-hint,
  .field-error {
    font-size: 14px;
  }
}


/* ===== [frontend-dev] 사전문진 열람 (.survey-view) — 2026-08-19 계약 §3-0 ===== */
/* 본인 문진 답변을 12문항(+3-1) 목록으로 보여주는 화면.
   노트북(1366~1536px)에서 스크롤을 줄이려고 문항 목록을 2열 그리드로 접는다.
   색·간격은 global.css :root 토큰만 참조한다(하드코딩 금지). */

/* --- 상단 탭 개수. global.css 의 repeat(3,1fr) 을 덮는다 ---
   3개 → 4개(2026-08-19 사전문진) → 5개(2026-08-24 만족도 조사, 계약 §10-6). */
.tabbar {
  grid-template-columns: repeat(5, 1fr);
}

/* 칸이 늘수록 390px 에서 아이콘+글자 가로 배치가 빠듯하다.
   global.css 의 세로 배치 분기(≤380px)를 480px 까지 넓혀 글자가 잘리지 않게 한다.
   (481px 이상에서는 한 칸이 96px 넘어 가로 배치가 들어간다 — 5칸 기준 계산) */
@media (max-width: 480px) {
  .tabbar .tab-item {
    flex-direction: column;
    gap: 2px;
  }
}

/* 5칸이 되면 한 칸이 390px 에서 78px — 탭 이름이 두 줄로 접히면 높이가 무너진다.
   장소 탭은 이미 [places] 블록에서 말줄임 처리되어 있고, 나머지는 접힘만 막으면 된다.
   ("만족도 조사"는 탭바에서만 "만족도"로 줄여 쓴다 — components/TabBar.jsx TABS.short) */
.tabbar .tab-name {
  white-space: nowrap;
}

.survey-view {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  /* 헤더와 너무 붙어 보인다는 지시(2026-08-24) — 상단 살짝 띄움 */
  margin-top: var(--sp-4);
  /* wide 레이아웃은 폭 상한을 페이지가 스스로 갖는다 — 홈(.home-page)과 같은 1500px.
     (2026-08-21 사용자 지적: 상한 없이 전체 폭으로 퍼졌던 것 수정) */
  max-width: var(--content-max);
  margin-inline: auto;
  width: 100%;
}

/* --- 머리말 --- */
.survey-view .survey-view-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
}

.survey-view .svh-art {
  flex: 0 0 auto;
  width: clamp(64px, calc(50.25px + 3.539vw), 110px);
  /* 390px→64px, 1280px→96px */
  height: auto;
}

/* --- 마지막 수정일 + 수정하기 --- */
.survey-view .survey-view-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
}

.survey-view .svb-date {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--sp-2);
  font-size: clamp(16px, calc(14.25px + 0.449vw), 20px);
  /* 390px→16px, 1280px→20px */
  font-weight: 800;
  color: var(--text);
}

.survey-view .svb-label {
  font-size: clamp(14px, calc(12.65px + 0.348vw), 17px);
  /* 390px→14px, 1280px→17px */
  font-weight: 700;
  color: var(--text-sub);
}

.survey-view .svb-edit {
  flex: 0 0 auto;
  min-width: 132px;
}

/* --- 문항 목록 --- */
.survey-view .sv-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-3);
  list-style: none;
  margin: 0;
  padding: 0;
}

.survey-view .sv-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-3);
  min-height: var(--tap-min);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}

/* 3-1(조건부)은 부모 문항에 딸린 항목임을 왼쪽 굵은 선으로 알린다 */
.survey-view .sv-row.is-sub {
  border-left: 4px solid var(--blue-line);
  background: var(--surface-soft);
}

.survey-view .sv-q {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  min-width: 0;
}

.survey-view .sv-no {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 30px;
  height: 30px;
  padding: 0 6px;
  border-radius: var(--r-pill);
  background: var(--blue-soft);
  color: var(--blue-dark);
  font-size: clamp(13px, calc(11.65px + 0.348vw), 16px);
  /* 390px→13px, 1280px→16px */
  font-weight: 800;
}

.survey-view .sv-q-text {
  font-size: clamp(15px, calc(13.65px + 0.348vw), 18px);
  /* 390px→15px, 1280px→18px */
  font-weight: 700;
  line-height: 1.45;
  color: var(--text);
}

.survey-view .sv-cond {
  display: block;
  font-size: clamp(13px, calc(12.1px + 0.225vw), 15px);
  /* 390px→13px, 1280px→15px */
  font-weight: 600;
  color: var(--text-mute);
}

.survey-view .sv-a {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 46%;
  padding: 6px var(--sp-3);
  border-radius: var(--r-pill);
  background: var(--blue-soft);
  border: 1px solid var(--blue-line);
  color: var(--blue-dark);
  font-size: clamp(15px, calc(13.65px + 0.348vw), 18px);
  /* 390px→15px, 1280px→18px */
  font-weight: 800;
  text-align: right;
}

.survey-view .sv-a-mark {
  font-size: 14px;
  line-height: 1;
}

/* 미응답 / 해당 없음 — 회색으로 낮춘다 */
.survey-view .sv-a.is-muted {
  background: var(--surface-sunken);
  border-color: var(--line);
  color: var(--text-mute);
  font-weight: 700;
}

/* 정본 선택지에 없는 옛 코드 — 값을 그대로 보이되 강조하지 않는다 */
.survey-view .sv-a.is-unknown {
  background: var(--surface-sunken);
  border-color: var(--line-strong);
  color: var(--text-sub);
}

.survey-view .sv-a.is-muted .sv-a-mark,
.survey-view .sv-a.is-unknown .sv-a-mark {
  display: none;
}

/* --- 안내 한 줄 + 하단 버튼 --- */
.survey-view .sv-note {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  background: var(--blue-soft);
  border: 1px solid var(--blue-line);
  font-size: clamp(14px, calc(12.65px + 0.348vw), 17px);
  /* 390px→14px, 1280px→17px */
  font-weight: 600;
  line-height: 1.5;
  color: var(--text-sub);
}

.survey-view .sv-foot {
  display: flex;
  justify-content: center;
}

.survey-view .sv-foot .btn {
  min-width: 220px;
}

/* 빈 상태(문진 미작성)의 [작성하기] 버튼 여백 */
.survey-view .state-block .btn {
  margin-top: var(--sp-2);
  min-width: 200px;
}

/* --- 넓은 화면: 2열로 접어 세로 스크롤을 줄인다 --- */
@media (min-width: 901px) {
  .survey-view .sv-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sp-3) var(--sp-4);
  }
}

/* --- 좁은 화면: 답변 칩을 문항 아래로 내린다(가로로 눌려 두 줄 나는 것 방지) --- */
@media (max-width: 560px) {
  .survey-view .survey-view-head {
    align-items: flex-start;
  }

  .survey-view .survey-view-bar {
    flex-direction: column;
    align-items: stretch;
  }

  .survey-view .svb-edit {
    width: 100%;
  }

  .survey-view .sv-row {
    flex-direction: column;
    align-items: stretch;
    gap: var(--sp-2);
  }

  .survey-view .sv-a {
    max-width: 100%;
    align-self: flex-start;
    margin-left: calc(30px + var(--sp-2));
    text-align: left;
  }

  .survey-view .sv-foot .btn,
  .survey-view .state-block .btn {
    width: 100%;
  }
}


/* ============================================================
   [consent] 민감정보 수집·이용 동의 (2026-08-21 · 계약 §9)
   ① 동의 모달 (.consent-modal — 측정 입력 진입 시 최초 1회)
   ② 문서 3페이지 (.doc-page — /consent/sensitive · /terms · /privacy)
      └ 문서 타이포는 회원가입 전문보기 모달(.legal-modal)과 공유 — 파일 끝 [signup-agree] ② 참조

   참고 디자인(비헬씨)의 **구성**만 가져오고 색은 전부 사용자 앱 토큰이다
   (참고본의 주황 브랜드색은 쓰지 않는다 — 계약 §9-5 말미).
   시니어 대상이라 본문 글자는 앱 기본보다 한 단계 크게 잡는다.
   ============================================================ */

/* ===== ① 동의 모달 ===== */
.modal-card.consent-modal {
  width: min(460px, calc(100% - 32px));
  padding: var(--sp-6) var(--sp-5) var(--sp-5);
  text-align: center;
}

.consent-modal .consent-title {
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--line);
  font-size: clamp(20px, calc(17.37px + 0.674vw), 26px);
  /* 390px→20px, 1280px→26px */
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--text);
}

.consent-modal .consent-lead {
  padding: var(--sp-5) var(--sp-1);
  border-bottom: 1px solid var(--line);
  font-size: clamp(16px, calc(14.25px + 0.449vw), 20px);
  /* 390px→16px, 1280px→20px */
  font-weight: 500;
  line-height: 1.5;
  color: var(--text-sub);
}

/* 체크박스 행 — 행 전체가 44px 이상이라 시니어도 정확히 누른다 */
/* [2026-08-21 사용자 재지시] 링크 2개는 **항상 체크박스 우측 같은 줄** — 줄바꿈 금지.
   자리는 체크 문구가 두 줄로 접히며 내어 준다(min-width:0). */
.consent-modal .consent-check-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: var(--sp-4) 0 var(--sp-3);
}

.consent-modal .consent-check {
  display: flex;
  flex: 1 1 0;
  min-width: 0;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--tap-min);
  padding: 0 var(--sp-1);
  border-radius: var(--r-md);
  cursor: pointer;
  text-align: left;
}

.consent-modal .consent-check:hover {
  background: var(--surface-sunken);
}

/* 실제 input 은 지우지 않고 시각적으로만 가린다(키보드 포커스·스크린리더 유지) */
.consent-modal .consent-check input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.consent-modal .consent-check-box {
  position: relative;
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border: 2px solid var(--line-strong);
  border-radius: 8px;
  background: var(--surface);
  transition: background .15s ease, border-color .15s ease;
}

.consent-modal .consent-check-box::after {
  content: "";
  position: absolute;
  left: 7px;
  top: 2px;
  width: 7px;
  height: 13px;
  border: solid var(--text-invert);
  border-width: 0 3px 3px 0;
  transform: rotate(45deg) scale(.4);
  opacity: 0;
  transition: opacity .12s ease, transform .12s ease;
}

.consent-modal .consent-check input:checked~.consent-check-box {
  background: var(--blue);
  border-color: var(--blue);
}

.consent-modal .consent-check input:checked~.consent-check-box::after {
  opacity: 1;
  transform: rotate(45deg) scale(1);
}

.consent-modal .consent-check input:focus-visible~.consent-check-box {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
}

.consent-modal .consent-check-text {
  font-size: clamp(15px, calc(13.69px + 0.337vw), 18px);
  /* 390px→15px, 1280px→18px */
  font-weight: 600;
  line-height: 1.35;
  color: var(--text);
}

/* 상세 문서로 가는 화살표 — 체크와 분리된 별도 버튼(누른다고 동의되지 않는다) */
.consent-modal .consent-more {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: var(--tap-min);
  height: var(--tap-min);
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  color: var(--text-sub);
  font-size: clamp(20px, calc(17.37px + 0.674vw), 26px);
  /* 390px→20px, 1280px→26px */
  font-weight: 700;
  line-height: 1;
}

.consent-modal .consent-more:hover {
  background: var(--blue-soft);
  color: var(--blue);
}

/* 라운드 박스 링크 2개 — 나란히 */
/* 홈 안내박스 우측 — 약관·처리방침 라운드 링크 (2026-08-21 사용자 확정 위치) */
.home-page .info-box .home-doc-links {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  align-self: center;
}

.home-page .info-box .home-doc {
  min-height: 34px;
  padding: 0 12px;
  border: 1px solid var(--blue-line);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--text-sub);
  white-space: nowrap;
  font-size: clamp(12px, calc(11.12px + 0.225vw), 14px);
  /* 390px→12px, 1280px→14px */
  font-weight: 600;
  cursor: pointer;
}

.home-page .info-box .home-doc:hover {
  color: var(--blue);
  border-color: var(--blue);
}

/* 좁은 폭 — 안내 문구 아래 줄로 흘러 우측 정렬 유지 */
@media (max-width: 640px) {
  .home-page .info-box {
    flex-wrap: wrap;
  }

  .home-page .info-box .home-doc-links {
    flex: 1 1 100%;
    justify-content: flex-end;
  }
}

/* [2026-08-21 사용자 재지시] 링크 2개는 화면 폭과 무관하게 체크박스 우측 고정 */
.consent-modal .consent-doc-links {
  display: flex;
  flex: 0 0 auto;
  gap: 6px;
  margin-left: auto;
}

.consent-modal .consent-doc {
  min-height: 34px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface-soft);
  color: var(--text-sub);
  white-space: nowrap;
  font-size: clamp(11.5px, calc(10.62px + 0.225vw), 13.5px);
  /* 390px→11.5px, 1280px→13.5px — 우측 보조 링크라 본문보다 한 단계 작게 */
  font-weight: 600;
  letter-spacing: -.02em;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}

.consent-modal .consent-doc:hover {
  background: var(--blue-soft);
  border-color: var(--blue-line);
  color: var(--blue);
}

.consent-modal .consent-error {
  margin-bottom: var(--sp-3);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  font-weight: 600;
  color: var(--danger);
}

.consent-modal .consent-actions {
  display: grid;
  gap: var(--sp-2);
}

/* [닫기] 는 주버튼보다 낮은 위계 — 테두리만 남긴다 */
.consent-modal .consent-close {
  border-color: var(--line);
  color: var(--text-sub);
}

.consent-modal .consent-close:hover:not(:disabled) {
  background: var(--surface-sunken);
  border-color: var(--line-strong);
  color: var(--text);
}


/* ===== ② 문서 페이지 (/consent/sensitive · /terms · /privacy) ===== */
.doc-page {
  max-width: 900px;
  margin: 0 auto;
  padding-bottom: var(--sp-6);
}

.doc-page .doc-topbar {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.doc-page .back-btn {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: var(--tap-min);
  height: var(--tap-min);
  border-radius: var(--r-md);
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--text-sub);
  font-size: clamp(24px, calc(20.49px + 0.899vw), 32px);
  /* 390px→24px, 1280px→32px */
  font-weight: 700;
  line-height: 1;
}

.doc-page .back-btn:hover {
  background: var(--blue-soft);
  border-color: var(--blue-line);
  color: var(--blue);
}

.doc-page .doc-heading {
  min-width: 0;
  font-size: clamp(21px, calc(17.49px + 0.899vw), 29px);
  /* 390px→21px, 1280px→29px */
  font-weight: 700;
  letter-spacing: -.04em;
  line-height: 1.2;
  color: var(--text);
}

.doc-page .doc-version {
  margin: var(--sp-2) 0 var(--sp-4) calc(var(--tap-min) + var(--sp-2));
  font-size: clamp(13px, calc(11.69px + 0.337vw), 16px);
  /* 390px→13px, 1280px→16px */
  font-weight: 600;
  color: var(--text-mute);
}

.doc-page .doc-body {
  padding: var(--sp-6) var(--sp-6) var(--sp-5);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

/* [signup-agree 2026-09-30] 아래 문서 타이포(.doc-lede ~ .doc-callout strong)는 회원가입 전문보기 모달
   (.legal-modal)과 공유한다 — 셀렉터를 `, .legal-modal .doc-x` 로만 넓혔고 값은 그대로다(.doc-page 결과 불변).
   .doc-topbar/.back-btn/.doc-heading/.doc-version/.doc-body/.doc-foot 은 페이지 전용이라 넓히지 않았다. */
.doc-page .doc-lede,
.legal-modal .doc-lede {
  margin-bottom: var(--sp-4);
  font-size: clamp(20px, calc(16.49px + 0.899vw), 28px);
  /* 390px→20px, 1280px→28px */
  font-weight: 700;
  letter-spacing: -.04em;
  line-height: 1.3;
  color: var(--text);
}

.doc-page .doc-h3,
.legal-modal .doc-h3 {
  margin: var(--sp-6) 0 var(--sp-3);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--line);
  font-size: clamp(17px, calc(15.25px + 0.449vw), 21px);
  /* 390px→17px, 1280px→21px */
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--text);
}

/* 첫 소제목은 위 구분선을 생략한다(리드 문단 바로 아래라 선이 두 겹으로 보인다) */
.doc-page .doc-lede+.doc-h3,
.doc-page .doc-body>.doc-h3:first-child,
.legal-modal .doc-lede+.doc-h3,
.legal-modal .doc-body>.doc-h3:first-child {
  margin-top: var(--sp-4);
  padding-top: 0;
  border-top: 0;
}

.doc-page .doc-p,
.legal-modal .doc-p {
  margin-bottom: var(--sp-3);
  font-size: clamp(15px, calc(13.69px + 0.337vw), 18px);
  /* 390px→15px, 1280px→18px */
  line-height: 1.7;
  color: var(--text-sub);
}

.doc-page .doc-muted,
.legal-modal .doc-muted {
  color: var(--text-mute);
}

.doc-page .doc-note,
.legal-modal .doc-note {
  font-size: .88em;
  color: var(--text-mute);
}

.doc-page .doc-list,
.legal-modal .doc-list {
  margin: 0 0 var(--sp-3);
  padding-left: 1.15em;
  font-size: clamp(15px, calc(13.69px + 0.337vw), 18px);
  /* 390px→15px, 1280px→18px */
  line-height: 1.65;
  color: var(--text-sub);
}

.doc-page .doc-list li,
.legal-modal .doc-list li {
  margin-bottom: var(--sp-2);
}

.doc-page .doc-list li::marker,
.legal-modal .doc-list li::marker {
  color: var(--blue);
}

/* 표는 좁은 화면에서 가로로만 스크롤한다(페이지 본문은 가로 스크롤 금지) */
.doc-page .doc-table-scroll,
.legal-modal .doc-table-scroll {
  margin: var(--sp-3) 0 var(--sp-4);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.doc-page .doc-table,
.legal-modal .doc-table {
  width: 100%;
  min-width: 460px;
  border-collapse: collapse;
  border: 1px solid var(--line);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  line-height: 1.6;
}

.doc-page .doc-table th,
.doc-page .doc-table td,
.legal-modal .doc-table th,
.legal-modal .doc-table td {
  padding: var(--sp-3);
  border: 1px solid var(--line);
  vertical-align: top;
  text-align: left;
  color: var(--text-sub);
}

.doc-page .doc-table thead th,
.legal-modal .doc-table thead th {
  background: var(--blue-soft);
  color: var(--text);
  font-weight: 700;
  text-align: center;
  vertical-align: middle;
}

.doc-page .doc-table tbody th,
.legal-modal .doc-table tbody th {
  width: 34%;
  background: var(--surface-sunken);
  color: var(--text);
  font-weight: 700;
}

.doc-page .doc-table .doc-list,
.legal-modal .doc-table .doc-list {
  margin: 0;
  font-size: 1em;
}

.doc-page .doc-callout,
.legal-modal .doc-callout {
  margin: var(--sp-4) 0;
  padding: var(--sp-4);
  border: 1px solid var(--blue-line);
  border-radius: var(--r-md);
  background: var(--blue-soft);
  font-size: clamp(15px, calc(13.69px + 0.337vw), 18px);
  /* 390px→15px, 1280px→18px */
  line-height: 1.65;
  color: var(--text);
}

.doc-page .doc-callout strong,
.legal-modal .doc-callout strong {
  color: var(--blue-dark);
}

.doc-page .doc-foot {
  display: flex;
  justify-content: center;
  margin-top: var(--sp-5);
}

.doc-page .doc-ok {
  min-width: 220px;
}

@media (max-width: 700px) {
  .doc-page .doc-body {
    padding: var(--sp-5) var(--sp-4) var(--sp-4);
    border-radius: var(--r-lg);
  }

  .doc-page .doc-version {
    margin-left: 0;
  }

  .doc-page .doc-ok {
    width: 100%;
  }

  .modal-card.consent-modal {
    padding: var(--sp-5) var(--sp-4) var(--sp-4);
  }
}

/* ===== 터치 키패드 대비 — 측정 입력 하단 여백 (2026-08-21 사용자 지시) =====
   태블릿·키오스크에서 화면 키패드가 올라오면 아래쪽 입력칸·저장 버튼이 가려졌다.
   터치 입력이 **하나라도 있는** 기기에 적용한다(any-pointer: coarse).
   [2026-08-21 기준 변경] 처음에는 "주 입력이 손가락"(hover:none and pointer:coarse)으로
   잡았지만, 실증 키오스크(윈도우 10 터치 + 마우스 인식 장치)는 주 입력이 마우스로
   판정돼 빠질 수 있다 — 터치 가능성만 있으면 여백을 준다. 이 여백은 측정 입력 화면
   한정이라 터치 노트북에서의 부담도 작다.
   [2026-08-26 사용자 지시] **모바일(≤759px)은 제외한다** — 휴대폰은 any-pointer:coarse 가
   항상 참이라 42vh(예: 844px 화면에서 354px)가 늘 붙어, 저장 버튼 아래로 화면 절반에
   가까운 빈 배경이 이어졌다. 폭 조건은 이 블록의 모바일 분기(위 max-width:759px)와
   같은 자리에서 끊는다 — 태블릿·키오스크(760px+)의 키패드 대비는 그대로 유지된다. */
@media (any-pointer: coarse) and (min-width: 760px) {
  .measure-user .form-card {
    margin-bottom: 42vh;
  }
}



/* ===== [satisfaction] 만족도 조사 (계약 §10-6, 2026-08-24) =====
   라우트 하나(app/(user)/satisfaction)에서 열람·작성·완료 3상태를 그린다.

   재사용 원칙 — 새 스킨을 만들지 않는다:
     · 작성(form)  … 사전문진 스텝의 .survey-page 카드/라디오 스킨을 그대로 얹고,
                      폭 상한만 .survey-page.satisfaction-page 로 덮는다(2단 선택자라 우선순위 승).
     · 열람(view)  … 사전문진 열람 .survey-view 목록(sv-row/sv-q/sv-a)을 그대로 쓴다.
   여기서는 만족도에만 필요한 차이(5지선다 가로 배치·구분 라벨·미응답 표시·완료 안내)만 더한다.
   색·간격은 global.css :root 토큰만 참조한다(하드코딩 금지). */

/* --- 작성: 와이드 폭. 사전문진(820px)과 달리 선택지 5개가 한 줄에 들어가야 한다 --- */
.survey-page.satisfaction-page {
  max-width: var(--content-max);
  gap: var(--sp-4);
}

/* 진행 표시는 스크롤 내내 붙어 있는다 — 단일 페이지라 "몇 개 남았는지"가 유일한 이정표다 */
.satisfaction-page .stf-progress {
  position: sticky;
  top: 0;
  z-index: 5;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

/* 문항 머리 — [번호] 문항 …………… [구분 라벨] */
.satisfaction-page .question-head {
  align-items: center;
}

.satisfaction-page .question-head .question-text {
  flex: 1 1 auto;
  min-width: 0;
}

.satisfaction-page .stf-q-short {
  flex: 0 0 auto;
  padding: 4px 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(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 700;
  white-space: nowrap;
}

/* 5지선다 한 줄 — 1500px 폭에서 가장 긴 라벨("전혀 그렇지 않다")도 한 줄에 들어간다 */
.satisfaction-page .choice-list {
  grid-template-columns: repeat(5, minmax(0, 1fr));
}

/* 좁아지면 랩 — 한 칸이 라벨을 못 담기 전에 2열로 접는다 */
@media (max-width: 900px) {
  .satisfaction-page .choice-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 420px) {
  .satisfaction-page .choice-list {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* [제출]을 눌렀을 때 남아 있던 미응답 문항 — 스크롤로 데려온 뒤 눈에 띄게 표시한다 */
.satisfaction-page .stf-q.is-missing {
  border-color: var(--warn);
  box-shadow: 0 0 0 3px var(--warn-bg);
}

/* 미응답이 있는 동안의 [제출하기] — 비활성처럼 보이되 클릭은 살아 있다
   (누르면 첫 미응답 문항으로 데려간다. 진짜 disabled 면 아무 반응이 없어 시니어가 막힌다) */
.satisfaction-page .btn.is-inactive {
  opacity: .5;
}

/* 문항이 10개라 하단 버튼까지 스크롤이 길다 — 좁은 화면에서는 카드 여백을 줄인다 */
@media (max-width: 560px) {
  .satisfaction-page .survey-question {
    padding: var(--sp-4);
  }

  .satisfaction-page .stf-q-short {
    display: none;
  }
}

/* --- 완료(done) — 새 라우트 없이 같은 자리에서 바뀐다 --- */
.satisfaction-view .stf-done {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-8) var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-card);
  text-align: center;
}

.satisfaction-view .stf-done-art {
  width: clamp(140px, 22vw, 240px);
  height: auto;
}

.satisfaction-view .stf-done-title {
  font-size: clamp(21px, calc(17.93px + 0.787vw), 28px);
  /* 390px→21px, 1280px→28px */
  font-weight: 800;
  letter-spacing: -.03em;
  color: var(--blue-dark);
}

.satisfaction-view .stf-done-desc {
  font-size: clamp(15px, calc(13.65px + 0.348vw), 18px);
  /* 390px→15px, 1280px→18px */
  font-weight: 600;
  line-height: 1.6;
  color: var(--text-sub);
}

.satisfaction-view .stf-done-acts {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-3);
  margin-top: var(--sp-2);
}

.satisfaction-view .stf-done-acts .btn {
  min-width: 200px;
}

@media (max-width: 560px) {
  .satisfaction-view .stf-done-acts .btn {
    width: 100%;
  }
}


/* ===== [satisfaction] 머리말 일러스트 확대 (2026-08-24 사용자 지시) =====
   사전문진 열람·만족도 조사 헤더의 클립보드 그림이 작다는 지적 — 두 화면 모두 한 단계 키운다.
   비율은 이미지가 유지하고(높이 auto) 폭만 clamp 로 올린다. 모바일에서 과해지지 않도록
   하한도 같은 비율(약 +22%)로만 올렸다. */

/* 열람 화면(.survey-view) — 사전문진 열람과 만족도 열람이 같은 클래스를 쓴다.
   종전: clamp(64px, calc(50.25px + 3.539vw), 110px) → 390px 64px / 1280px 96px / 상한 110px */
.survey-view .svh-art {
  width: clamp(78px, calc(61.2px + 4.31vw), 134px);
  /* 390px→78px, 1280px→117px, 상한 134px */
}

/* 만족도 작성 화면의 머리말 그림.
   .survey-page 를 공유하는 사전문진 스텝 화면은 지시 대상이 아니라 건드리지 않으려고
   .satisfaction-page 로 좁혀 덮는다(종전: clamp(56px, 7vw, 86px)).
   ※ 560px 이하에서는 위 07 구역이 이 그림을 감추므로 모바일 영향은 없다. */
.satisfaction-page .survey-header-art {
  /* [2026-08-24 사용자 지시 "더 크게"] 105 → 132px */
  width: clamp(80px, 10.5vw, 132px);
}


/* ============================================================
   [dash-v2] 측정이력 · 대시보드 탭 — 2열 6카드 (2026-08-27)

   경위: 사용자 참고 시안대로 대시보드를 전면 개편했다(_workspace/plan_dashboard_v2.md).
     · 폐기: 지표 3카드(.metric-grid) · AI 분석 카드(.ai-summary, 종합 점수 링 + 태그 7종) ·
       권장사항 3카드(.advice-grid). 링 규칙은 이미 삭제했고(파일 중간 [design-ai-card] 묘비),
       나머지 세 계열의 규칙은 plan §3-3 지시대로 이번 커밋에서 남겨 뒀다.
     · 신설: .dash-grid > .dash-card 6장 — 등급 / 주요 요약 / 위험지수 / 통합 추이 /
       가이드 요약 / 맞춤 포인트.

   분담(plan §3 클래스 계약): 도형·값은 JSX, 색·크기·배치는 이 파일.
   JSX 가 인라인 CSS 변수로만 값을 넘긴다 — .risk-seg 는 --w(세그먼트 폭, 단위 없는 수),
   .risk-pin 은 --pos(위험점수 0~100, **단위 없는 수**. 기존 .ai-score --score 와 같은 규약).
   ============================================================ */


/* ===== [dash-v2] 그리드 · 카드 공통 ===== */
/* 1500px 최대 폭은 .history-page 가 이미 잡는다(위 [frontend-dev] 측정이력 콘텐츠 폭).
   여기서는 그 안을 두 장씩 끊어 넣는다.

   [2026-08-27 사용자 지시 "행마다 카드 폭 비율을 시안대로"] grid → flex-wrap 으로 바꿨다.
   grid 의 열 트랙은 모든 행이 공유하므로 행마다 다른 비율(1:2 / 1:1.2 / 1.2:1)을 낼 수 없다.
   33분할 트랙 + span 으로 흉내낼 수는 있지만 거터가 32번 생겨 gap 이 폭을 다 먹는다.
   flex-wrap 은 카드마다 width 를 직접 주므로 **마크업 변경 없이** 행별 비율이 나온다
   (비율은 아래 [행별 폭 비율] 블록). 한 행의 두 카드 폭 합 = 100% − gap 이라 정확히
   두 장씩 접히고, 같은 줄의 카드는 flex 기본 stretch 로 높이를 맞춘다 — 보이는 결과는
   grid 때와 같고 .dash-grade/.dash-risk 의 margin-bottom:auto 빈 공간 배분도 그대로 통한다. */
.dash-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
}

/* --col = 이 카드가 한 행에서 차지하는 비율(0~1). 기본 .5 는 비율을 지정하지 않은 카드가
   생겼을 때의 폴백이라 종전 5:5 로 선다.
   빼는 .5px 은 반올림 여유다 — 두 카드 폭이 각각 올림되어 합이 100% 를 1/64px 이라도
   넘기면 둘째 카드가 다음 줄로 접힌다. 눈에 보이지 않는 값으로 그 사고만 막는다. */
.dash-grid>.dash-card {
  flex: 0 0 auto;
  width: calc((100% - var(--sp-4) - .5px) * var(--col, .5));
}

/* grid 가 아니라 flex 열이다 — 한 행의 두 카드는 서로 높이를 맞춰 늘어나는데(그리드 기본
   stretch), 짧은 카드(등급·위험지수)가 옆의 긴 카드(요약·추이) 높이까지 늘면 아래쪽에
   빈 공간이 크게 남는다. flex 라야 `margin-bottom: auto` 두 개로 그 여유를 제목 아래와
   본문 아래에 반씩 나눠 줄 수 있다(아래 [빈 공간 배분] 규칙). grid 의 align-content 로는
   트랙 밖 여유를 아이템이 흡수하지 못한다. 세로 1열이라 보이는 결과는 grid 때와 같다. */
.dash-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  min-width: 0;
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

.dash-card>.dash-card-title {
  font-size: clamp(18px, calc(15.37px + 0.674vw), 24px);
  /* 390px→18px, 1280px→24px */
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1.25;
  color: var(--text);
}

/* [빈 공간 배분] 짧은 카드(등급·위험지수) 전용. auto 마진 두 개가 남는 세로 여유를
   "제목 아래"와 "본문 아래"에 반씩 나눠 가져 본문이 카드 가운데에 선다.
   제목은 여전히 카드 맨 위 — 옆 카드(요약·추이)의 제목과 같은 줄에서 시작한다.
   :last-child 로 잡아 두면 리포트 없는 회원의 빈 상태(.state-block) 한 덩어리일 때도 그대로 통한다. */
.dash-grade>.dash-card-title,
.dash-grade>:last-child,
.dash-risk>.dash-card-title,
.dash-risk>:last-child {
  margin-bottom: auto;
}

/* 리포트가 없는 신규 회원의 빈 상태 — 공용 .state-block 이 카드 안에 들어오면
   바깥 여백(sp-8)이 카드 padding 과 겹쳐 카드가 과하게 길어진다. 안쪽용으로만 줄인다. */
.dash-card .state-block {
  padding: var(--sp-5) var(--sp-4);
}


/* ===== [dash-v2] 행별 폭 비율 (2026-08-27) ===== */
/* 참고 시안은 행마다 두 카드 폭이 다르다 — 그 행의 주인공이 넓다.
     1행  등급 : 주요 요약   = 1   : 2.6  → .28 / .72      (요약은 미니카드 4칸이 한 줄로 들어간다)
     2행  위험지수 : 추이    = 1   : 1.2  → .4545 / .5455  (추이는 차트라 가로가 길수록 읽힌다)
     3행  가이드 : 포인트    = 1.2 : 1    → .5455 / .4545  (가이드는 아이콘+두 줄 문장 4행)

   [2026-08-27 사용자 지시 ①②] 1행을 1:2(.3333/.6667) → **1:2.6(.28/.72)** 로 좁혔다.
   요약 4칸을 2×2 에서 **1×4 한 줄**로 바꾸라는 지시(②)와 한 세트다 — 4칸이 한 줄에 서려면
   요약 카드가 그만큼 넓어야 한다. 1500px 실측으로 등급 카드가 견디는지 확인했다:
     등급 카드 폭 484 → 407px, 안쪽 글자 폭 365px
     표정 아이콘 136 · 등급 "상담 필요" 165 · 설명 "전문기관 상담을 권장합니다." 240px
   가장 긴 설명도 365px 안에 한 줄로 들어간다. 1366px(안쪽 327px)에서도 같다.

   nth-child 가 아니라 카드 클래스에 준다 — 카드 순서가 바뀌어도 비율이 카드를 따라가고,
   규칙만 읽어도 어느 카드가 넓은지 알 수 있다.
   각 행의 합이 정확히 1.0 이라 (100% − gap) 을 남김없이 나눠 갖는다.
   ※ 780px 이하 1열 스택에서는 아래 [반응형]이 width:100% 로 덮는다. */
.dash-grid>.dash-grade {
  --col: .28;
}

.dash-grid>.dash-summary {
  --col: .72;
}

.dash-grid>.dash-risk {
  --col: .4545;
}

.dash-grid>.dash-trend {
  --col: .5455;
}

.dash-grid>.dash-guide {
  --col: .5455;
}

.dash-grid>.dash-points {
  --col: .4545;
}


/* ===== [dash-v2] 종합 건강위험 등급 (.dash-grade) ===== */
/* 등급색 3종(--grade-ink/tint/line)만 갈아끼우는 구조. 기본값은 정상(초록)이고,
   JSX 가 카드에 is-* 를 붙인다(.ai-score 가 쓰던 is-attention/caution/consult 와 같은 어휘).
   :has() 갈래는 프론트가 modifier 를 카드가 아니라 안쪽 아이콘에 붙였을 때를 위한 보험이다. */
.dash-card.dash-grade {
  --grade-ink: var(--grade-normal);
  --grade-tint: var(--grade-normal-bg);
  --grade-line: var(--grade-normal-line);
  align-items: center;
  text-align: center;
}

.dash-grade.is-attention,
.dash-grade:has(.is-attention) {
  --grade-ink: var(--grade-attention);
  --grade-tint: var(--grade-attention-bg);
  --grade-line: var(--grade-attention-line);
}

.dash-grade.is-caution,
.dash-grade:has(.is-caution) {
  --grade-ink: var(--grade-caution);
  --grade-tint: var(--grade-caution-bg);
  --grade-line: var(--grade-caution-line);
}

.dash-grade.is-consult,
.dash-grade:has(.is-consult) {
  --grade-ink: var(--grade-consult);
  --grade-tint: var(--grade-consult-bg);
  --grade-line: var(--grade-consult-line);
}

/* 제목만 카드 왼쪽 끝에 붙여 옆 카드(.dash-summary)와 같은 줄에서 시작하게 한다
   (카드가 align-items:center 라 제목도 가운데로 몰리는 것을 stretch 로 되돌린다) */
.dash-grade>.dash-card-title {
  align-self: stretch;
  text-align: left;
}

/* 큰 표정 아이콘 — 시안의 주인공. 이모지라 line-height 1 로 상자를 글리프에 맞춘다 */
.dash-grade .grade-face {
  display: grid;
  place-items: center;
  width: clamp(96px, calc(78.47px + 4.494vw), 136px);
  height: clamp(96px, calc(78.47px + 4.494vw), 136px);
  margin-top: var(--sp-4);
  border: 2px solid var(--grade-line);
  border-radius: 50%;
  background: var(--grade-tint);
  font-size: clamp(48px, calc(39.24px + 2.247vw), 68px);
  /* 390px→48px, 1280px→68px */
  line-height: 1;
}

.dash-grade .grade-label {
  margin-top: var(--sp-2);
  font-size: clamp(26px, calc(20.74px + 1.348vw), 38px);
  /* 390px→26px, 1280px→38px */
  font-weight: 800;
  letter-spacing: -.04em;
  line-height: 1.1;
  color: var(--grade-ink);
}

.dash-grade .grade-desc {
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 600;
  line-height: 1.5;
  color: var(--text-sub);
}

.dash-grade .grade-note {
  margin-top: var(--sp-1);
  font-size: clamp(12px, calc(10.9px + 0.281vw), 14.5px);
  /* 390px→12px, 1280px→14.5px */
  font-weight: 600;
  color: var(--text-mute);
}


/* ===== [dash-v2] 주요 요약 4칸 (.dash-summary > .mini-grid > .mini-card) ===== */
/* 판정색은 --mini-tone 하나로 흐른다(.mini-state 글자색). 값은 아래 [상태 톤] 블록이 정한다.
   is-none 은 미측정 — 회색으로 두어 "정상"으로 오독되지 않게 한다.
   2×2 는 모바일에서도 유지한다(plan §3-3 4항): 4칸이 1열로 늘어지면 카드가 너무 길어진다.
   데스크톱(≥1300px) 1×4 는 아래 [1×4 한 줄] 참고.

   [2026-08-27 사용자 지시 "참고 시안 레이아웃 그대로"] 칸 안을 시안 배치로 다시 짰다.
     name   ← [작은 아이콘 + 지표명] 한 행
     val    ← 값(가장 큼)
     unit   ← 단위가 값 **아랫줄**로 내려온다(.mini-value 를 세로 flex 로 돌려서 만든다)
     sub    ← 보조값(체성분·혈당만)
     state  ← 판정(상태색)
     date   ← 측정일. 시안에는 없지만 "언제 값인지"는 버릴 수 없어 **최하단 최소 크기**로 남긴다
   전 항목 가운데 정렬(시안). 자식이 조건부(sub/state/date 는 미측정 칸에서 아예 없다)라
   nth-child 배치는 쓸 수 없다 — 클래스별 named area 라야 빠진 칸이 자기 자리만 비운다.
   row-gap 0 도 같은 이유다: 빈 행에 gap 이 붙으면 미측정 칸만 아래가 뜬다. 줄 간격은
   각 항목의 margin-top 이 책임진다.

   [묘비 — 종전 머리 행 2단] 직전 판(2026-08-27 오전)은 `"name date" / "val val" / …` 로
   측정일을 지표명 옆에 올려 한 줄을 줄였고, 620px 이하와 1300px 이상에서만 세로 스택으로
   되돌렸다. 시안이 측정일을 최하단에 두므로 그 2단 배치와 두 개의 되돌림 분기를 함께 걷어냈다
   — 이제 전 폭이 같은 한 줄짜리 템플릿이다. */
/* flex:1 — 요약 카드는 옆 등급 카드 높이에 맞춰 늘어나는데, 그리드가 제 내용만큼만 차지하면
   그 여유가 카드 아래 빈 띠로 남았다(1500px 실측 376px 카드에 약 110px). 그리드가 여유를 받고,
   칸은 아래 align-content:center 로 그 안에서 내용을 세로 가운데에 세운다 — 시안의 두툼한
   카드 비례가 그대로 나오고 빈 띠가 사라진다. */
.dash-summary .mini-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /* 2×2 일 때 두 행 높이를 같게 맞춘다 — 그리드 행은 서로 독립이라 보조행이 두 줄로 접히는
     칸이 있는 행만 두꺼워지고, 그러면 아래 행 카드의 지표명·판정 y 가 위 행과 어긋난다
     ("4칸의 상단/하단 y 가 모두 일치" 지시). 1fr 로 묶으면 네 칸이 같은 높이가 된다. */
  grid-auto-rows: minmax(0, 1fr);
  flex: 0 1 auto;
  /* 카드에 남는 세로는 그리드가 먹지 않고 위아래로 반씩 흘려보낸다 — 칸 안이 촘촘해지고
     (위 .mini-card align-content:center) 요약 카드 안에서 4칸 묶음이 가운데에 선다. */
  margin-block: auto;
  gap: var(--sp-3);
}

.dash-summary .mini-card {
  /* --ico-d = 아이콘 칩 지름. 시안의 "작은 아이콘"이라 지표명 글자 두 배 남짓으로 잡는다. */
  /* --sub-h = 보조행(.mini-sub) 한 줄 높이 = 보조값 글자(13.5~18px) x line-height 1.3 + 여백 */
  --sub-h: clamp(50px, calc(17.4px + 0.67vw), 26px);
  /* 390px→20px, 1280px→26px */
  --ico-d: clamp(48px, calc(44.5px + 0.9vw), 56px);
  /* 390px→48px, 1280px→56px — [2026-09-02 사용자 지시] 아이콘 칩·글리프 추가 확대
     (24~32 → 32~40 → **48~56px**). 1×4 타이트 구간만 아래 [칩 하한 보정]이 되돌린다. */
  --mini-tone: var(--text-mute);
  /* --ico-ink = 아이콘 칩 색(아래 [미니카드 아이콘 칩]). 지표별 값은 nth-child 가 덮는다 */
  --ico-ink: var(--text-sub);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  /* [2026-08-27 사용자 지시 "상단·하단 고정"] 네 칸은 보조행(.mini-sub) 유무가 달라 종전
     가운데 정렬에서는 지표명·판정의 y 가 칸마다 어긋났다(들쭉날쭉). 행 역할을 못 박고
     **남는 세로를 중간의 빈 행(gap) 하나가 통째로 먹게** 해서, 위 두 조각은 상단에
     아래 두 조각은 하단에 고정한다 — 보조행이 있든 없든 네 칸의 y 가 일치한다.
     빈 행이 1fr 이라 카드가 늘어나도 늘어난 만큼 그 행만 커진다. */
  /* [2026-08-27 사용자 "수치와 상태 사이가 너무 벌어졌다"] 종전에는 중간에 1fr 빈 행을 두어
     위/아래로 밀어붙였다 — 4칸 y 는 맞았지만 값과 판정 사이에 100px 넘는 대공백이 생겼다.
     이제 빈 행 없이 다섯 줄을 **촘촘히 묶고 묶음째 세로 가운데**에 세운다(align-content:center).
     y 정렬은 다른 방법으로 지킨다 — 보조행(.mini-sub)에 한 줄 최소 높이를 줘서 보조값이 없는
     칸(혈압·산소포화도)도 같은 자리를 차지하게 한다. 네 칸의 콘텐츠 높이가 같아지므로
     가운데 정렬해도 판정·측정일 y 가 일치한다. */
  /* 보조행 최소 높이는 **행 트랙**에 준다 — .mini-sub 요소에 min-height 를 걸면 그 요소가
     아예 없는 칸(혈압·산소포화도)에는 아무 효과가 없어 네 칸 콘텐츠 높이가 어긋난다
     (실측: 지표명 y 가 21 ↔ 33 으로 12px 벌어졌다 = 보조행 한 줄만큼). 트랙에 주면 빈 행도
     자리를 차지해, 가운데 정렬해도 네 칸의 판정·측정일 y 가 일치한다. */
  grid-template-rows: auto auto minmax(var(--sub-h), auto) auto auto;
  grid-template-areas:
    "name"
    "val"
    "sub"
    "state"
    "date";
  justify-items: center;
  align-content: center;
  row-gap: 0;
  min-width: 0;
  /* 위아래 패딩을 sp-3 → sp-5 로 키워 지표명·측정일이 카드 가장자리에 붙지 않게 한다(지시) */
  padding: var(--sp-6) var(--sp-3);
  /* [2026-08-27 사용자 지시 "하늘색 배경 빼고 테두리 박스로"]
     --surface-soft(#F8FAFF) 면 → 흰 면 + 테두리. 부모 카드도 흰색이라 이제 경계를 지는 것은
     테두리 하나뿐이라, 종전 --line(#dadeeb) 을 --line-strong(#D3D9E8) 으로 한 단계 올려
     네 칸이 흰 바탕 위에서 각각 박스로 읽히게 한다(옅은 면이 하던 몫을 선이 넘겨받는다).

     [묘비 — 좌측 4px 상태 액센트 바] `border-left: 4px solid var(--mini-ink)` 를 없앴다.
     사용자가 "시안에 없으니 제거 여부 재량"으로 넘긴 건이고, 세 가지가 같은 답을 가리킨다:
       · 시안에 없다.
       · 칸 안이 전부 가운데 정렬로 바뀌었다 — 왼쪽만 4px 두꺼운 테두리는 글자 중심선을
         상자 중심에서 2px 밀어 정렬이 어긋나 보인다(테두리가 border-box 안쪽이므로).
       · 판정을 상태색으로 말하는 수단이 이미 있다 — .mini-state 가 20px/800 에 4.5:1 이상
         대비로 서 있다(아래 [상태 톤] 실측표). 색면과 글자가 같은 말을 두 번 하던 구조였다.
     되돌리려면 이 자리에 그 한 줄을 도로 넣고 --mini-tone 옆에 --mini-ink 를 살리면 된다. */
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  text-align: center;
}

/* [상태 톤] (2026-08-27 사용자 피드백 ③ "warn 앰버가 흰 배경에서 잘 보이는지 확인")
   실측했더니 등급 4색을 **글자색**으로 쓰는 것이 문제였다. 당시 카드 배경 --surface-soft
   (#F8FAFF) 기준 명도대비:
     정상 #16A34A → 2.86:1   (AA 4.5:1 미달, 큰글씨 3:1 도 미달)
     관심 #CA8A04 → 2.84:1   (사용자 지적 그대로 — 앰버가 흰 배경에서 뜬다)
     상담 #DC2626 → 4.62:1   (유일하게 통과)
   등급색 자체는 바꾸지 않는다(다른 화면·리포트와 어휘·색이 묶여 있다). 대신 **면(4px 액센트
   바)과 글자를 분리**해 글자만 같은 색상의 한 단계 어두운 값으로 내린다. 면은 넓어서 밝은
   원색이 잘 읽히고, 글자는 획이 가늘어 더 어두운 값이 필요하다 — 디자인시스템에서 500(면)/
   700(글자)을 나눠 쓰는 것과 같은 분담이다.
     정상 #15803D → 4.80:1 ✓
     관심 #A16207 → 4.50:1 ✓
     상담 #DC2626 → 4.62:1 ✓ (원색 그대로 — 이미 통과라 굳이 어둡게 하지 않는다)
   세 톤이 초록/황갈/적으로 색상환에서 충분히 떨어져 "확실히 갈린다"(피드백 ③ 후반).
   ※ 2026-08-27 카드 면이 흰색(--surface)으로 바뀌면서 세 값 모두 4.86 / 4.55 / 4.68:1 로
     조금씩 올라갔다 — 위 선택은 더 안전한 쪽으로만 움직였으므로 값은 그대로 둔다. */
.dash-summary .mini-card.is-ok {
  --mini-tone: #15803D;
}

.dash-summary .mini-card.is-warn {
  --mini-tone: #A16207;
}

.dash-summary .mini-card.is-danger {
  /* 유일하게 원색 그대로 — 토큰을 그대로 참조해 등급색이 바뀌면 글자도 같이 따라간다 */
  --mini-tone: var(--grade-consult);
}

.dash-summary .mini-card.is-none {
  --mini-tone: var(--text-mute);
}

/* 시안의 머리 행 — [작은 아이콘 + 지표명]. 칩이 이 안의 첫 자식이고 그 뒤에 지표명이 온다.
   flex 한 행이라 칩과 글자의 세로 중심이 맞고, 칸 안에서 통째로 가운데에 선다
   (.mini-card 의 justify-items:center 가 이 상자를 가운데로 보내고, 안쪽은 이 flex 가 잡는다). */
.dash-summary .mini-name {
  grid-area: name;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 0;
  font-size: clamp(16px, calc(13.37px + 0.674vw), 22px);
  /* 390px→16px, 1280px→22px — [2026-08-27 사용자 지시] 확대(종전 14.5~19px) */
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.25;
  color: var(--text-sub);
}


/* ===== [dash-v2] 미니카드 아이콘 칩 (.mini-ico) ===== */
/* [2026-08-27 사용자 지시] 시안처럼 작은 원형 컬러 칩 — 지표별 옅은 배경 + 아이콘.
   칩 색은 그 지표의 **추이선 색(--chart-*)** 을 그대로 쓴다. 주요 요약 4칸과 통합 추이·
   변화 탭이 같은 지표를 같은 색으로 불러 화면 사이에서 색이 어긋나지 않는다.
   배경은 그 색의 14% 틴트를 color-mix 로 만든다 — 틴트 값을 따로 적어 두면 잉크색이
   바뀔 때 한쪽만 남는다.

   판정색(--mini-ink: 왼쪽 액센트 바 + .mini-state)과는 역할이 다르다.
   칩 = "무슨 지표인가", 액센트 = "판정이 어떤가". 그래서 칩 색은 is-ok/warn/danger 에
   따라 변하지 않는다 — 둘 다 상태색이면 한 칸에 같은 말이 두 번 적힌다.

   칸 순서는 summaryMiniCards() 가 [혈압 · 혈당 · 산소포화도 · 체성분] 으로 고정한다.
   지표를 가리키는 클래스가 마크업에 없어 nth-child 로 짚는다(순서가 곧 계약이다).
   체성분은 체중·BMI 와 한 계열이라 --chart-weight 를 쓴다.
   실제 글리프: 🩺 혈압 · 🩸 혈당 · 💨 산소포화도 · 💪 체성분.
   자리잡기는 이 규칙이 하지 않는다 — 칩은 .mini-name 의 첫 자식이고, 그 flex 행이 소유한다.

   [2026-08-27 사용자 피드백 ① "아이콘을 2배로"] 종전 24~30px → 지름을 --ico-d 로 뽑고
   1100px 이상에서 2배(54~60px)로 덮는다(아래 [2배 칩]). 이 기본값(30~44px)은 그 미만 폭에서
   쓰는 크기다 — 칩이 .mini-name 의 flex 자식이라 여기서 더 키우면 머리 행이 그만큼 두꺼워져
   피드백 ②(높이 축소)와 정면으로 부딪힌다. 좁은 폭에서는 옆으로 뺄 자리도 없다(390px 기준
   칸 안쪽 글자 폭이 132px 뿐이고, 60px 칩을 왼쪽에 세우면 "111/74 mmHg" 가 들어가지 않는다). */
.dash-summary .mini-ico {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: var(--ico-d);
  height: var(--ico-d);
  border-radius: 50%;
  background: color-mix(in srgb, var(--ico-ink) 14%, #fff);
  font-size: clamp(25px, calc(23.2px + 0.46vw), 29px);
  /* 390px→25px, 1280px→29px — 이모지 글리프 기준. 칩 지름의 약 52%(종전 비율 유지) */
  line-height: 1;
  /* SVG 아이콘(currentColor)이 오는 경우를 위해 잉크색도 함께 흘린다 */
  color: var(--ico-ink);
}

.dash-summary .mini-grid>.mini-card:nth-child(1) {
  /* 혈압 */
  --ico-ink: var(--chart-systolic);
}

.dash-summary .mini-grid>.mini-card:nth-child(2) {
  /* 혈당 */
  --ico-ink: var(--chart-glucose);
}

.dash-summary .mini-grid>.mini-card:nth-child(3) {
  /* 산소포화도 */
  --ico-ink: var(--chart-spo2);
}

.dash-summary .mini-grid>.mini-card:nth-child(4) {
  /* 체성분 — 골격근량/BMI 둘 다 체중 계열 */
  --ico-ink: var(--chart-weight);
}

/* 값 + 단위. [2026-08-27 사용자 지시 "시안처럼 값 아래에 단위"] 종전에는 둘이 한 글줄에
   baseline 으로 나란히 섰다(`116/70 mmHg`). 시안은 값 아래에 단위가 따로 놓인다.
   마크업은 그대로다 — 단위 span 이 .mini-value 의 자식이므로, 이 상자를 **세로 flex** 로
   돌리면 [숫자 텍스트][단위 span] 두 줄이 된다(숫자는 익명 flex 아이템이 된다).
   JSX 가 단위 앞에 공백 하나를 달고 오는데(` mmHg`), 줄 첫머리 공백은 접히므로 그대로 둔다.
   가로가 좁은 1×4 칸에서 오히려 유리하다 — 값과 단위가 각자 한 줄을 온전히 쓴다. */
.dash-summary .mini-value {
  grid-area: val;
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: var(--sp-2);
  font-size: clamp(27px, calc(21.3px + 1.461vw), 40px);
  /* 390px→27px, 1280px→40px */
  font-weight: 800;
  letter-spacing: -.045em;
  line-height: 1.02;
  color: var(--text);
}

/* 단위 줄 — 값보다 한참 작고 흐리다(시안). 세로 flex 라 값 바로 아래 한 줄로 앉는다 */
.dash-summary .mini-unit {
  margin-top: 2px;
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 700;
  letter-spacing: 0;
  line-height: 1.15;
  color: var(--text-mute);
}

/* 미측정 칸은 값 자리에 "미측정" 세 글자가 들어온다 — 큰 숫자 급으로 두면 측정된 칸보다
   눈에 띄어 위계가 뒤집힌다. 본문 급 회색으로 낮춘다. */
.dash-summary .mini-card.is-none .mini-value {
  font-size: clamp(16px, calc(13.81px + 0.562vw), 21px);
  /* 390px→16px, 1280px→21px */
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--text-mute);
}

/* 체성분 칸의 둘째 줄(골격근 · 체지방률처럼 값이 두 개인 경우) */
.dash-summary .mini-sub {
  grid-area: sub;
  margin-top: 2px;
  /* (보조행 최소 높이는 위 .mini-card 의 grid-template-rows 가 맡는다) */
  min-height: 1.3em;
  font-size: clamp(13.5px, calc(11.53px + 0.506vw), 18px);
  /* 390px→13.5px, 1280px→18px */
  font-weight: 700;
  line-height: 1.3;
  color: var(--text-sub);
}

/* 판정 — 시안의 카드 최하단 문구(측정일만 그 아래). 위계상 이름(19px)보다 크고 값(40px)보다
   작다. 굵기도 800 이라 같은 크기의 이름 줄과 확실히 갈라 선다.
   색은 --mini-tone(글자용 어두운 값)이다 — 위 [상태 톤] 실측표 참고. 좌측 액센트 바를 걷어낸
   지금은 이 글자가 칸의 판정색을 말하는 **유일한** 수단이라 대비 기준을 더 엄격히 지킨다. */
.dash-summary .mini-state {
  grid-area: state;
  /* 값 묶음(이름·값·단위·보조)과 판정 사이의 절제된 간격 — 종전 1fr 대공백을 대신한다 */
  margin-top: var(--sp-3);
  font-size: clamp(16.5px, calc(14.09px + 0.618vw), 22px);
  /* 390px→16.5px, 1280px→22px — [2026-08-27 사용자 지시] 추가 확대(종전 15~20px, 굵기 유지) */
  font-weight: 800;
  letter-spacing: -.02em;
  line-height: 1.25;
  color: var(--mini-tone);
}

/* latest 는 전체 기간의 최신값이라 "언제 값인지"를 밝힌다(plan §1-1행 우).
   [2026-08-27 사용자 지시] 시안에는 이 줄이 없다. 그래도 지우지 않는 이유 —
   주요 요약의 값은 "최근 6개월 안의 마지막 측정"이라 **언제 잰 값인지가 값의 뜻을 바꾼다**
   (석 달 전 혈압과 어제 혈압은 같은 숫자라도 다른 정보다). 대신 지시대로 칸의 **최하단에
   가장 작은 급**으로 내려 시안의 시각 위계를 흐리지 않게 한다: 10.5~13px 은 이 화면에서
   가장 작은 글자이고, 판정(20px)과 두 배 이상 벌어져 눈이 먼저 가지 않는다. */
.dash-summary .mini-date {
  grid-area: date;
  white-space: nowrap;
  font-size: clamp(12.5px, calc(11.4px + 0.281vw), 15px);
  /* 390px→12.5px, 1280px→15px — [2026-08-27 사용자 지시] 확대(종전 10.5~13px).
     그래도 칸 안에서 가장 작은 급이라 판정(22px)과 위계가 뒤집히지 않는다. */
  font-weight: 600;
  line-height: 1.3;
  color: var(--text-mute);
}


/* ===== [dash-v2] 건강위험 지수 바 (.dash-risk) ===== */
/* 축은 위험 방향(낮을수록 좋다) — 종전 "종합 건강 점수" 게이지와 방향이 반대라서
   .risk-note 문구가 필수다(plan §1-2행 좌).
   폭: 세그먼트 4개가 flex-grow 로 나눠 갖는다. 정본은 JSX 가 서버 컷(criteria.grades.cuts)
   에서 계산해 넣는 인라인 --w 이고, 아래 is-* 의 --w 기본값(30/20/25/25)은 그 값을 주지
   않는 경우의 폴백이다 — report.css .score-track .seg:nth-child() 와 같은 분담. */
.dash-card.dash-risk {
  /* [2026-08-27] 레이더 오버레이의 위치 기준(containing block). 넓은 폭에서 .risk-radar 가
     absolute 로 이 카드의 padding box 를 기준으로 우측 상단에 앉는다 — 이 한 줄이 없으면
     기준이 조상 어딘가로 밀려 그림이 화면 밖으로 날아간다(실측: top −621px). */
  position: relative;
  /* --radar-w = 레이더 그림 **폭**. 높이가 아니라 폭인 것이 핵심이다 — 프론트가
     ResponsiveContainer 를 aspect=1 로 쓰므로 폭이 곧 높이다(높이를 따로 주면 두 값이 싸운다).
     이 기본값은 세로 스택(1240px 미만) 용이고, 오버레이에서는 아래 분기가 덮는다.
     상한 280px 근거: 세로 스택은 레이더가 본문 아래 전폭에 붙는 자리라, 폭을 풀어 두면
     카드 안쪽 폭만큼 정사각이 되어 카드가 두 배로 길어진다(실측 1200px 화면에서 481px). */
  --radar-w: clamp(240px, 55vw, 330px);
  /* 390px→240px, 600px 이상 330px — [2026-08-27 사용자 "그림이 너무 작다"] 종전 200~280px
     에서 같은 비율(1.2배)로 올렸다. 390px 칸 안쪽이 332px 이라 240px 은 여유가 남는다. */
  /* [2026-08-27 사용자 지시 "리포트 판정 게이지 스타일로 재스킨"]
     마커 치수 한 벌. report.css 13번 구역(.gauge-marker/.marker-dot/.marker-value)의 언어를
     옮기되, 리포트 게이지는 트랙 12px·점 14px 인쇄용 축소판이고 이 막대는 화면 전용 18~26px
     이라 치수는 그 비율로 키웠다(점 = 막대 두께에 거의 맞먹게 — 막대 위에 "앉은" 것으로 읽힌다).
       --dot-d    점 바깥 지름(box-sizing:border-box 라 흰 링 포함한 값)
       --dot-ring 흰 링 두께. report .marker-dot 의 4px(14px 점) = 지름의 28.6% 를 그대로 적용
       --bub-h    값 말풍선 상자 높이 어림값. 아래 .risk-bar 의 위 여백을 여기서 되짚는다
       --bub-gap  말풍선 아래끝 ↔ 점 위끝 사이. report .marker-value 의 2px 을 화면 크기에 맞춰 5px
       --marker-ink 마커 잉크. report.css :root --marker-ink(#17233d) 복제 — 복제 이유는
                    아래 --seg-* 와 같다(main.css 는 report.css 없이 로드되는 페이지도 쓴다). */
  --dot-d: clamp(18px, calc(15.37px + 0.674vw), 24px);
  /* 390px→18px, 1280px→24px */
  --dot-ring: clamp(4px, calc(3.12px + 0.225vw), 5px);
  /* 390px→4px, 1280px→6px(상한 5px) */
  --bub-h: clamp(21px, calc(19.1px + 0.483vw), 25px);
  /* 390px→21px, 1280px→25px — 말풍선 글자 14.5px×1.3 + 세로 padding 4 + 테두리 2 */
  --bub-gap: 5px;
  --marker-ink: #17233d;
  /* [2026-08-27 사용자 "그래프 채움을 블루/스카이 톤으로"] 레이더 도형 잉크.
     프론트 계약상 **선·점·방사 그라디언트 stop 이 이 토큰 하나를 따라오므로** 여기 한 줄로
     도형 전체가 바뀐다(기본 폴백은 --grade-caution 주황이었다).
     값은 새 색을 만들지 않고 기존 --chart-weight(#2563EB)를 그대로 쓴다 — 이 화면에서 이미
     체중·BMI 선에 쓰는 블루라 대시보드 안에서 색이 한 벌로 유지된다.
     · 방사 그라디언트(중심 .5 → 바깥 .08)를 태우면 중심의 진한 블루가 바깥으로 가면서
       스카이 톤으로 자연스럽게 풀린다(같은 색상의 알파 감쇠라 색이 탁해지지 않는다).
     · 격자 --chart-grid(#E7EAF3)·축 라벨 --text-sub 와 색상이 갈려 도형이 격자 위에 뜬다.
     · 위험 방향 오독 우려 — 블루는 등급 4색(녹/황/주/적) 어디에도 속하지 않는 중성색이라
       "좋다/나쁘다"로 읽히지 않는다. 축의 해석은 캡션("좁을수록 좋습니다")이 맡는다. */
  --risk-radar-ink: var(--chart-weight);
  /* --donut-d = 위험점수 도넛 링 바깥 지름. 레이더(240px)와 시각 균형을 맞춘 값이고,
     카드 min-height 540px 안에서 설명 아래 ~ 막대 위 여백에 들어간다. */
  --donut-d: clamp(120px, calc(106.8px + 3.4vw), 150px);
  /* 390px→120px, 1280px→150px */
  /* [세그 램프 한 벌] (2026-08-27 정리) 종전에는 이 네 색이 .risk-seg 규칙 안에 하드코딩돼
     있었다. 이제 칩(.risk-stage-chip)·활성 라벨(.risk-labels .is-active)도 같은 구간색을
     쓰므로 정의 지점을 하나로 모은다 — 세 곳에 흩어 두면 report.css 와 맞추라는 아래 주석을
     따를 때 한 곳이 빠진다. --*-ink 는 그 구간색 위/틴트 위에 올릴 **글자용** 어두운 값이다
     (미니카드 --mini-ink/--mini-tone 과 같은 면·글자 분담). */
  --seg-low: #c6daf3;
  --seg-mid: #8db4e2;
  --seg-high: #c96a1e;
  --seg-vhigh: #c62f21;
  --seg-low-ink: #1B4F8F;
  --seg-mid-ink: #164780;
  --seg-high-ink: #8F4A12;
  --seg-vhigh-ink: #A0231A;
}

.dash-risk .risk-bar {
  position: relative;
  display: flex;
  /* 막대 위로 솟는 것은 이제 점의 위 절반 + 그 위 값 말풍선이다(종전 지도핀 + 꼬리에서 교체).
     점은 막대 중심에 앉으므로 위로 나오는 높이는 반지름뿐이다. 종전 실측 41px(1280px 이상)
     ↔ 새 값 42px 로 세로 리듬이 유지된다. */
  margin-top: calc(var(--bub-h) + var(--bub-gap) + var(--dot-d) / 2);
  /* [2026-08-27 사용자 피드백 ④ "위험지수 카드가 허전하다"] 14~20px → 18~26px.
     옆 카드(.dash-trend)가 추이 3행이라 길어서 이 카드가 그 높이까지 늘어난다 — 막대가
     가늘면 남는 세로가 그대로 빈 띠로 보인다. 두께는 구간색 네 조각의 면적도 같이 키운다. */
  height: clamp(18px, calc(14.49px + 0.899vw), 26px);
  /* 조각 사이 흰 틈 — 색 대비가 약한 이웃(옅은 블루 ↔ 중간 블루)도 경계가 선으로 보인다.
     report.css .score-track 이 같은 이유로 2px gap 을 둔다. 아래 .risk-labels 에도 같은
     gap 을 줘서 라벨이 조각과 어긋나지 않게 한다. */
  gap: 2px;
}

/* [2026-08-27 사용자 지시 "각 조각 끝이 둥근 캡슐형(현재는 모서리 각짐)"]
   종전에는 리포트 트랙(.report .track)처럼 **양 끝 조각의 바깥쪽만** 둥글렸다 — 안쪽 경계는
   직각이라 26px 두께에서 네모 토막 네 개로 보였다(리포트는 트랙이 12px 이라 그 각이 눈에
   띄지 않는다). 이제 조각마다 캡슐이다. 두 가지가 같이 좋아진다:
     · 각 구간이 "한 덩어리"로 읽혀 라벨(낮음/보통/높음/매우 높음)과 1:1 로 맞는다
     · 아래 활성 강조의 아웃라인이 캡슐 윤곽을 따라 그려진다 — 직각 조각에 알약 아웃라인을
       두르면 선이 상자로 보인다(리포트 .seg.is-current 는 조각이 직각이라 상자 선이 맞았다)
   조각 사이 흰 틈(위 gap 2px)은 그대로라 캡슐끼리 붙지 않는다.
   ※ overflow:hidden 으로 라운드를 만들지 않는 것은 종전과 같은 이유다 — 흐름 밖 마커가 잘린다.

   [채움] 기본값은 그 구간색의 30% 틴트(= 비활성). 원색은 아래 :has() 브리지가 현재 구간에만
   되돌린다. 네 틴트의 명도가 여전히 단조 감소(0.90 → 0.80 → 0.68 → 0.59)라 "낮음→매우 높음"
   램프 순서는 흐려지지 않는다.
   45% 로 먼저 잡았다가 30% 로 내린 이유(1500px 실측): 구간 램프의 첫 조각(--seg-low #c6daf3,
   명도 0.68)은 원래 옅은 색이라, 이웃한 --seg-mid 의 45% 틴트(0.72)보다 오히려 밝지 않았다.
   현재 구간이 "낮음"일 때 활성 조각이 비활성 이웃보다 밝아 보여 강조가 뒤집혔다. 30% 로
   내리면 비활성 mid 가 0.80 이 되어 활성 low(0.68)가 확실히 더 진하다.
   --seg-tone 을 조각마다 나눠 두면 채움·아웃라인·활성 되돌림이 색 이름을 한 번만 적는다. */
.dash-risk .risk-seg {
  flex: var(--w, 25) 1 0;
  min-width: 0;
  border-radius: var(--r-pill);
  background: var(--seg-tone, var(--seg-low));
}

/* [2026-08-27 사용자 지시 "보통/높음 구분이 약하다"] 등급 4색(녹·노·주·적) → 상세리포트
   위험점수 막대와 같은 **세그먼트 램프**로 교체했다.
   종전 노랑 #CA8A04 → 주황 #EA580C 은 색상환에서 이웃이라 "보통 ↔ 높음" 경계가 뭉쳤다.
   새 램프는 블루 2단계에서 주황으로 건너뛰므로 그 경계가 색상 자체로 갈린다.

   값 출처: front/public/styles/report.css :root 의 --seg-0 ~ --seg-3
            (2026-08-17 --seg-0 조정 · 2026-08-18 --seg-3 조정 반영본, 복제 2026-08-27).
   복제하는 이유: main.css 는 report.css 없이 로드되는 페이지(홈·문진·측정)에서도 쓰이는
   독립 파일이라 다른 페이지 CSS 의 토큰에 의존하면 안 된다. **report.css 쪽 값이 바뀌면
   여기도 같이 고칠 것** — 두 막대가 같은 위험 축을 그리므로 색이 갈라지면 안 된다.
     --seg-0  #c6daf3  옅은 블루   L* 86.1   낮음
     --seg-1  #8db4e2  중간 블루   L* 72.1   보통       ΔL* 14.0
     --seg-2  #c96a1e  짙은 주황   L* 54.9   높음       ΔL* 17.2
     --seg-3  #c62f21  선명한 적   L* 44.0   매우 높음   ΔL* 10.9
   명도가 단조 감소하는 램프라 흑백 인쇄·색각이상에서도 네 단계가 순서대로 읽힌다.
   ※ 값 자체는 위 .dash-card.dash-risk 의 --seg-* 한 곳에만 적혀 있다(2026-08-27 정리). */
.dash-risk .risk-seg.is-low {
  --w: 30;
  --seg-tone: var(--seg-low);
}

.dash-risk .risk-seg.is-mid {
  --w: 20;
  --seg-tone: var(--seg-mid);
}

.dash-risk .risk-seg.is-high {
  --w: 25;
  --seg-tone: var(--seg-high);
}

.dash-risk .risk-seg.is-vhigh {
  --w: 25;
  --seg-tone: var(--seg-vhigh);
}


/* ===== [dash-v2] 현재 구간 강조 — 칩 → 세그먼트 :has() 브리지 (2026-08-27) ===== */
/* 리포트 게이지의 .seg.is-current 는 JSX 가 조각에 직접 클래스를 붙여 만든다. 대시보드
   마크업에는 그 클래스가 없지만, **칩과 세그먼트가 같은 수식자 어휘(is-low/mid/high/vhigh)를
   쓰기 때문에** 칩에서 판정을 끌어와 같은 조각을 짚을 수 있다(핀 색 정합에 쓴 것과 같은 수법).
   JSX 무수정으로 리포트와 같은 "현재 구간" 표시를 얻는다.

   3단 캐스케이드다 — 아래로 갈수록 좁다:
     1) .risk-seg              원색      ← 칩이 없는 경우(구버전 마크업·리포트 없음)의 폴백.
                                           칩 없이 막대만 있으면 종전 램프 그대로 보인다.
     2) :has(칩) .risk-seg     45% 틴트  ← 칩이 있으면 네 조각을 모두 뒤로 물린다
     3) :has(칩.is-X) .is-X    원색+선   ← 그중 현재 구간만 앞으로 끌어낸다
   특정도 (0,2,0) < (0,3,0) < (0,5,0) 라 순서가 뒤집히지 않는다.

   아웃라인 색·불투명도는 report.css .seg.is-current 복제다(rgba(23,35,61,.55), 안쪽 오프셋).
   두께만 1.5 → 2px 로 올렸다 — 리포트 트랙은 12px 이고 이 막대는 18~26px 이라, 같은 1.5px
   이면 조각 면적 대비 선이 가늘어 강조가 약하다(실측: 1500px 에서 1.5px 는 1px 로 반올림돼
   렌더된다). 그림자가 아니라 선이라 흑백 인쇄에서도 현재 구간이 남는다 — 리포트가 그 이유로
   inset 그림자에서 아웃라인으로 옮긴 결정을 그대로 따른다. */
.dash-risk:has(.risk-stage-chip) .risk-seg {
  background: color-mix(in srgb, var(--seg-tone, var(--seg-low)) 30%, #fff);
}

.dash-risk:has(.risk-stage-chip.is-low) .risk-seg.is-low,
.dash-risk:has(.risk-stage-chip.is-mid) .risk-seg.is-mid,
.dash-risk:has(.risk-stage-chip.is-high) .risk-seg.is-high,
.dash-risk:has(.risk-stage-chip.is-vhigh) .risk-seg.is-vhigh {
  background: var(--seg-tone);
  outline: 2px solid rgba(23, 35, 61, .55);
  outline-offset: -2px;
}

/* ===== [dash-v2] 현재 위치 마커 — 점 + 값 말풍선 (2026-08-27 재스킨) ===== */
/* [묘비] 종전은 "숫자를 품은 지도핀"이었다 — 잉크 알약(--pin-d 24~32px) + 아래로 뾰족한 꼬리
   (::before 흰 삼각형 9px 위에 ::after 잉크 삼각형 7px)를 막대 위 5px 에 띄우고, 흰 링 2px +
   그림자로 세그먼트 위에서 형태를 지켰다. 등급별 핀 색 규칙(.risk-pin.is-attention/-caution/
   -consult)과 칩에서 색을 끌어오던 :has() 3줄도 그 구조의 부품이었다.
   사용자 지시로 **리포트 판정 게이지의 마커 언어**(report.css 13번 구역)로 교체한다 —
   막대 **위에 앉는 점** + 그 위 **값 말풍선**. 리포트와 대시보드가 "지금 여기"를 한 가지
   모양으로 말하게 된다(리포트 안에서 게이지·혈압 분류 그래프가 이미 같은 언어를 쓴다).

   [잉크 고정] 점·말풍선의 잉크는 구간색을 따라가지 않고 --marker-ink(#17233d) 한 벌이다.
   리포트가 그렇게 확정한 이유가 여기에도 그대로 적용된다:
     · 마커는 "축 위의 좌표"지 "판정"이 아니다. 판정은 이미 칩·활성 세그·활성 라벨 세 곳이
       구간색으로 말한다 — 마커까지 구간색이면 같은 말이 네 번이고, 정작 좌표가 흐려진다.
     · 구간색은 명도가 제각각(낮음 L*86 ↔ 매우높음 L*44)이라 잉크를 그 색으로 두면 자기
       구간 위에서 대비가 무너진다. 종전 실측이 그 문제를 그대로 보여 준다(잉크 대비
       낮음 2.31:1 · 보통 1.37:1 · 높음 1.06:1 · 매우 1.14:1 — 흰 링에 기대 버티고 있었다).
       고정 잉크 #17233d 는 네 구간색 위에서 6.6~12.5:1 이라 어디에 서든 같은 세기로 읽힌다.
   그래서 직전에 넣었던 구간색 :has() 3줄은 여기서 폐기하고, 같은 수법을 **활성 세그먼트
   강조**(위 [현재 구간 강조] 블록)로 옮겼다 — 강조가 필요한 곳은 마커가 아니라 구간이다.

   [가로 위치] --pos 는 **단위 없는 위험점수(0~100)** 다(JSX 가 style={{"--pos": riskScore}} 로
   넘긴다 — 기존 .ai-score --score 와 같은 규약).
   양 끝에서 점이 막대 밖으로 반쯤 걸치지 않도록 반지름만큼 안쪽으로 물린다 — 리포트 JSX 가
   .gauge-marker 에 `clamp(12px, …, calc(100% - 12px))` 로 넣는 가드와 같은 값·같은 뜻이고,
   여기서는 마크업을 못 고치므로 CSS 가 --dot-d 로 계산한다. 이 가드가 말풍선 넘침도 같이
   해결한다(말풍선이 점 중앙 정렬이라, 점이 안쪽으로 들어온 만큼 말풍선도 따라 들어온다).

   [세로 위치] top 50% + translateY(-50%) = 점 중심이 막대 중심. 리포트도 마커를 트랙 위에
   **얹어** 어느 구간인지가 좌표가 아니라 겹침으로 보이게 한다. */
.dash-risk .risk-pin {
  position: absolute;
  left: clamp(calc(var(--dot-d) / 2), calc(var(--pos, 0) * 1%), calc(100% - var(--dot-d) / 2));
  top: 50%;
  z-index: 1;
  width: var(--dot-d);
  height: var(--dot-d);
  transform: translate(-50%, -50%);
  /* 흰 링은 그림자가 아니라 실제 테두리다(리포트와 같은 결정) — 지면에도 선으로 찍힌다 */
  border: var(--dot-ring) solid var(--surface);
  border-radius: 50%;
  background: var(--marker-ink);
  /* 바깥 잉크선 — 옅은 구간(낮음 #c6daf3)이나 45% 틴트 위에서도 흰 링의 윤곽이 남는다.
     report .marker-dot 의 1.25px rgba(23,35,61,.5) 복제. */
  outline: 1.25px solid rgba(23, 35, 61, .5);
  line-height: 1;
  pointer-events: none;
}

/* 값 말풍선 — 점 바로 위. 흰 배경 + 잉크 테두리 + 잉크 숫자(report .marker-value 언어).
   absolute 라 점 크기를 밀지 않는다.
   bottom 기준면이 점의 **padding box**(= 흰 링 안쪽)라 링 두께를 되더해야 말풍선 아래끝이
   점의 바깥 윤곽에 맞는다 — 그 위에 --bub-gap 을 띄운다.
   색을 반전(잉크 배경 + 흰 글자)하지 않는 이유: 말풍선이 카드 배경 위에 뜨는데, 잉크 덩어리는
   막대보다 무거워 시선이 숫자로 먼저 가 버린다. 흰 알약은 축 위에서 가볍게 떠 있고 테두리로
   점과 한 몸임을 말한다(리포트가 같은 판단으로 흰 알약을 쓴다). */
.dash-risk .risk-pin-num {
  position: absolute;
  bottom: calc(100% + var(--dot-ring) + var(--bub-gap));
  left: 50%;
  transform: translateX(-50%);
  padding: 2px 7px;
  border: 1px solid var(--marker-ink);
  border-radius: var(--r-pill);
  background: var(--surface);
  white-space: nowrap;
  font-size: clamp(11.5px, calc(10.2px + 0.337vw), 14.5px);
  /* 390px→11.5px, 1280px→14.5px */
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1.3;
  color: var(--marker-ink);
}

/* 낮음 / 보통 / 높음 / 매우 높음 — 세그먼트와 같은 폭으로 나눠 각 조각 아래 가운데 선다.
   폭 폴백은 위 .risk-seg 와 같은 30/20/25/25 를 nth-child 로 준다(라벨에는 is-* 가 없다).
   --lab-tone/--lab-ink 도 같은 nth-child 가 나눠 준다 — 현재 구간(.is-active)만 그 색을 쓴다. */
.dash-risk .risk-labels {
  display: flex;
  /* 막대와 같은 gap — 없으면 조각 경계와 라벨 경계가 최대 6px 어긋난다 */
  gap: 2px;
  margin-top: var(--sp-2);
  font-size: clamp(13px, calc(11.69px + 0.337vw), 16px);
  /* 390px→13px, 1280px→16px */
  font-weight: 700;
  color: var(--text-mute);
}

.dash-risk .risk-labels>span {
  flex: var(--w, 25) 1 0;
  min-width: 0;
  text-align: center;
}

.dash-risk .risk-labels>span:nth-child(1) {
  --w: 30;
  --lab-ink: var(--seg-low-ink);
}

.dash-risk .risk-labels>span:nth-child(2) {
  --w: 20;
  --lab-ink: var(--seg-mid-ink);
}

.dash-risk .risk-labels>span:nth-child(3) {
  --w: 25;
  --lab-ink: var(--seg-high-ink);
}

.dash-risk .risk-labels>span:nth-child(4) {
  --w: 25;
  --lab-ink: var(--seg-vhigh-ink);
}

/* 현재 구간 라벨 — 구간색 + 밑줄(report .legend>span.is-current 언어).
   [2026-08-27 재스킨] 종전에는 옅은 구간색 알약을 깔았다. 알약은 조각 폭 전체를 차지해
   막대 아래에 **두 번째 막대**처럼 보였고, 이제 활성 세그먼트 자체가 원색 + 아웃라인으로
   앞에 나오므로 그 아래에서 또 한 겹 채색하면 강조가 두 번 겹친다. 밑줄은 폭을 글자만큼만
   쓰므로 라벨 행이 축 아래 얇은 한 줄로 남는다(리포트가 같은 이유로 밑줄을 쓴다).
   ※ 알약이 사라져 span 의 padding 3px·border-radius 도 위에서 함께 걷어냈다.
   밑줄 간격은 리포트 2px 보다 1px 넓다 — 여기 라벨은 13~16px 로 리포트(9.5~10.5px)보다 커서
   같은 2px 이면 글자에 붙어 보인다. */
.dash-risk .risk-labels>span.is-active {
  font-weight: 800;
  color: var(--lab-ink);
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}


/* ===== [dash-v2] 현재 구간 칩 · 설명 (.risk-stage-chip / .risk-stage-desc) ===== */
/* [2026-08-27 사용자 피드백 ④ "위험지수 카드가 허전하다"] 종전 이 카드에는 막대·라벨·한 줄
   안내뿐이라, 옆 카드(추이 3행) 높이에 맞춰 늘어난 만큼이 빈 공간으로 남았다. 프론트가
   제목 아래에 [현재 구간 칩] + [구간 설명 한 줄]을 추가했고 여기서 그 두 조각을 조판한다.
   DOM: 제목 → .risk-stage-chip(안에 -cap "현재 구간" + -name "낮음") → .risk-stage-desc →
        막대 → 라벨 → .risk-note.

   칩 색: 구간색을 **그대로 배경에 깔지 않고** 30% 틴트 + 같은 색상의 어두운 글자로 만든다.
   원색을 배경에 쓰면 네 구간의 명도가 제각각이라(낮음 L*86 ↔ 매우높음 L*44) 글자색을 흰↔검
   으로 갈아끼워야 하고, 그러면 "높음" 구간에서 흰 글자 대비가 3.77:1 로 떨어져 작은 자막이
   읽히지 않는다. 틴트 + 어두운 글자로 통일하면 네 구간이 같은 짜임이면서 대비가 모두 통과한다:
     낮음 #1B4F8F on 30% #c6daf3 = 7.49:1
     보통 #164780 on 30% #8db4e2 = 7.57:1
     높음 #8F4A12 on 30% #c96a1e = 4.68:1
     매우 #A0231A on 30% #c62f21 = 4.66:1
   테두리는 같은 색 70% 라 칩이 막대의 어느 조각을 가리키는지 색으로 이어진다. */
.dash-risk .risk-stage-chip {
  /* 카드가 flex 세로열이라 기본 stretch 로 두면 칩이 카드 폭을 다 먹는다 — 내용 폭으로 세운다.
     프론트가 칩을 래퍼 안에 넣더라도 inline-flex 가 같은 결과를 낸다(align-self 는 무효가 될 뿐). */
  align-self: flex-start;
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4);
  border: 1px solid color-mix(in srgb, var(--stage-tone, var(--seg-low)) 70%, #fff);
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--stage-tone, var(--seg-low)) 30%, #fff);
  color: var(--stage-ink, var(--seg-low-ink));
}

.dash-risk .risk-stage-chip.is-low {
  --stage-tone: var(--seg-low);
  --stage-ink: var(--seg-low-ink);
}

.dash-risk .risk-stage-chip.is-mid {
  --stage-tone: var(--seg-mid);
  --stage-ink: var(--seg-mid-ink);
}

.dash-risk .risk-stage-chip.is-high {
  --stage-tone: var(--seg-high);
  --stage-ink: var(--seg-high-ink);
}

.dash-risk .risk-stage-chip.is-vhigh {
  --stage-tone: var(--seg-vhigh);
  --stage-ink: var(--seg-vhigh-ink);
}

/* 자막 "현재 구간" — 색은 구간명과 같게 두고 **크기·굵기로만** 급을 나눈다.
   중성색(--text-sub)으로 낮추면 매우높음 칩 배경에서 3.60:1 까지 떨어져 작은 글자가 흐려진다. */
.dash-risk .risk-stage-chip-cap {
  font-size: clamp(12.5px, calc(10.97px + 0.393vw), 16px);
  /* 390px→12.5px, 1280px→16px */
  font-weight: 700;
  letter-spacing: -.01em;
  line-height: 1.2;
}

/* 구간명 "낮음/보통/높음/매우 높음" — 시니어 가독 목적으로 카드 안에서 제목 다음으로 크다 */
.dash-risk .risk-stage-chip-name {
  font-size: clamp(22px, calc(18.49px + 0.899vw), 30px);
  /* 390px→22px, 1280px→30px */
  font-weight: 800;
  letter-spacing: -.03em;
  line-height: 1.15;
}

/* 구간 설명 한 줄 — 칩이 "무엇인지", 이 문장이 "그게 무슨 뜻인지"를 맡는다 */
.dash-risk .risk-stage-desc {
  font-size: clamp(14px, calc(12.25px + 0.449vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 600;
  line-height: 1.55;
  color: var(--text-sub);
}

/* [빈 공간 배분 — 위험지수 카드 전용] (2026-08-27 피드백 ④ 마무리)
   위 [빈 공간 배분] 공통 규칙은 남는 세로를 "제목 아래"와 "본문 아래"에 반씩 나눈다. 이 카드는
   이제 머리 덩어리가 [제목 + 칩 + 설명] 세 조각이라, 제목 바로 아래에서 갈라 놓으면 제목만
   위에 떨어져 남고 칩이 카드 한가운데로 내려온다. 갈라 놓는 지점을 설명문 아래로 옮겨
   머리 덩어리는 위에 붙이고, 남는 공간은 막대 위/아래로만 흐르게 한다.
   :has() 로 좁힌 이유 — 리포트가 없는 회원은 칩·설명 대신 .state-block 한 덩어리만 오는데,
   그때는 공통 규칙(제목 아래 auto)이 그대로 살아 빈 상태가 카드 가운데에 서야 한다. */
.dash-risk:has(.risk-stage-chip)>.dash-card-title {
  margin-bottom: 0;
}

/* 자손 결합자다(종전 `>`) — 레이더가 붙은 회원은 설명문이 .risk-body 안으로 한 겹 들어간다.
   그 경우 auto 마진이 .risk-body 의 flex 열 안에서 같은 일을 한다(막대 그룹을 아래로 민다). */
.dash-risk .risk-stage-desc {
  margin-bottom: auto;
}


/* ===== [dash-v2] 위험지수 카드 — 우측 레이더 오버레이 (2026-08-27 사용자 지시) ===== */
/* [묘비 — 직전 판의 grid 2열] 카드를 `"title title" / "body radar"` 그리드로 갈라 좌 본문 /
   우 레이더 두 열로 세웠었다(열 트랙 clamp(180px,35%,240px) · grid-template-rows auto
   minmax(0,1fr) 로 제목 행 고정 · .risk-body/.risk-radar 의 grid-area · 1300px 전환점).
   사용자가 "영역을 나누지 말고 위험분포 그래프는 우측에 **레이어 형태로 띄워서**" 로 정정했다.
   열을 나누면 본문(막대·라벨·콜아웃)이 카드 폭의 60%만 쓰게 되는데, 이 카드의 주인공은
   가로로 긴 위험 축이라 그 축을 줄이는 대가가 컸다. 이제 본문은 카드 폭을 그대로 쓰고,
   레이더는 흐름 밖으로 나가 **칩·설명 오른쪽 + 막대 위**의 빈 자리에 떠 있다.
   함께 죽은 규칙: 제목의 grid-area, 그리드 행/열 트랙, 레이더 열 좌우 인셋(padding 0 8px).

   [자리] 카드가 위치 기준이 된다(.dash-card.dash-risk 의 position:relative).
     top    = 카드 padding(20) + 제목 한 줄(30) + 카드 gap(12) → 제목 바로 아래에서 시작
     right  = 카드 padding 안쪽
   세로로는 막대 위 여백에서 멈춘다 — 본문이 `.risk-stage-desc` 의 auto 마진으로 막대 그룹을
   카드 아래쪽으로 밀어 두기 때문에 그 위가 통째로 비어 있다(이 카드가 옆 추이 카드 높이에
   맞춰 늘어나면서 생기던 바로 그 빈 자리다).
   가로로는 칩(내용 폭 ~170px)·설명(~250px)의 오른쪽이라 본문 글자와 부딪히지 않는다.

   [겹침 안전 폭] 카드가 좁아지면 설명문이 길어져(줄바꿈) 오버레이 밑으로 파고든다. 실측으로
   하한을 정했다 — 아래 [세로 스택] 분기 주석 참조. 그 미만에서는 흐름 배치로 되돌린다. */
.dash-risk .risk-body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  gap: var(--sp-3);
  min-width: 0;
}

/* 오버레이 — 기본은 흐름 배치(세로 스택)다. 넓은 폭에서만 아래 [오버레이] 분기가 띄운다.
   기본을 흐름으로 두는 쪽이 안전하다: 분기가 안 걸리는 폭에서 그림이 본문 위에 겹쳐 앉는
   사고가 구조적으로 일어나지 않는다. */
.dash-risk .risk-radar {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-0);
  min-width: 0;
}

/* 그림 칸(.risk-radar-chart). **높이를 주지 않는다** — ResponsiveContainer 가 aspect=1 이라
   폭 하나로 정사각이 결정되고, 여기에 height 를 겹쳐 주면 두 값이 어긋나 그림이 눌린다. */
.dash-risk .risk-radar-chart {
  width: 100%;
  max-width: var(--radar-w);
  min-width: 0;
}

/* 캡션 — "무엇을 그린 그림인지" 한 줄. 그림을 누르지 않는 최소 급 */
.dash-risk .risk-radar-cap {
  font-size: clamp(13px, calc(11.9px + 0.281vw), 15.5px);
  /* 390px→13px, 1280px→15.5px — [2026-08-27 사용자 "캡션이 잘 안 보인다"] 한 급 올렸다.
     색도 --text-mute → --text-sub 로 본문급. 굵기는 600 그대로 두고 크기·색만 올린 것은
     "과하게 하지 말 것" 조건 때문이다 — 카드 제목(24px/700)이나 구간명과 급이 겹치지 않고,
     그림 아래 한 줄 설명으로 남는다. */
  font-weight: 600;
  line-height: 1.4;
  text-align: center;
  /* [2026-08-28] 오버레이 200px 폭에서 캡션이 글자 중간에서 접혔다("…좋습니/다").
     한글 기본 word-break 는 음절 단위라 아무 데서나 끊는다 — keep-all 로 공백(어절)
     단위 줄바꿈만 허용해 "영역별 위험 분포 · / 좁을수록 좋습니다" 로 깔끔히 갈라진다.
     한 줄이 들어가는 폭에서는 종전 그대로 한 줄이다. */
  word-break: keep-all;
  color: var(--text-sub);
}

/* 레이더 격자·축 톤 — 채움/선 색은 프론트가 인라인 var() 로 주입하고(계약), 여기서는
   **배경 격자와 축 글자**만 화면 톤에 맞춘다. recharts 는 이 둘에 인라인 스타일을 주지
   않아 CSS 로만 잡힌다(global.css 의 툴팁 규칙과 같은 분담).
   SVG 라 글자색은 color 가 아니라 fill 이다. */
.dash-risk .risk-radar .recharts-polar-grid-angle line,
.dash-risk .risk-radar .recharts-polar-grid-concentric-polygon,
.dash-risk .risk-radar .recharts-polar-grid-concentric-circle {
  stroke: var(--chart-grid);
}

/* [2026-08-27 레이더 고도화 후속] 축 라벨이 recharts 기본 tick → 프론트 커스텀
   <text class="risk-radar-tick"> (tspan 2줄 규칙 분리)로 바뀌며 종전 셀렉터
   .recharts-polar-angle-axis-tick-value 가 죽었다 — 클래스만 갈아탄다. 미지정이면
   페이지 19px 검정을 상속받아 "대사증후군"이 7px 잘린다(프론트 실측: 12px대면 잘림 0). */
.dash-risk .risk-radar .risk-radar-tick {
  font-size: clamp(10.5px, calc(9.62px + 0.225vw), 13px);
  /* 390px→10.5px, 1280px→13px */
  font-weight: 700;
  fill: var(--text-sub);
}

/* 최외곽 링(=100점 자리)만 한 단계 진하게 — 도형이 닿는 "만점 경계"가 격자와 구분된다.
   concentric 그룹의 path 는 안쪽부터 0·25·50·75·100 순서라 last-child 가 최외곽이다
   (프론트 실측). 색은 카드 테두리급 --line-strong — 격자(--chart-grid)보다 한 단계 진한 기존 값. */
.dash-risk .risk-radar .recharts-polar-grid-concentric path:last-child {
  stroke: var(--line-strong);
}

/* 축 라벨이 그림 상자 밖으로 나가도 잘리지 않게 한다 — PolarAngleAxis 라벨은 반지름 바깥에
   그려져 상자 경계에서 잘린다(실측: 긴 라벨 앞 두 글자가 날아갔다). outerRadius 는 JSX
   프롭이라 CSS 로 줄일 수 없으므로 흘러나오게 두고, 그 자리를 아래 오버레이의 right 여백과
   카드 padding 이 받는다. */
.dash-risk .risk-radar .recharts-surface {
  overflow: visible;
}

/* ===== [dash-v2] 위험점수 도넛 게이지 (.risk-donut) — 2026-08-27 사용자 지시 ===== */
/* 카드 **왼쪽 빈 영역**(설명문 아래 ~ 막대 위, 레이더 오버레이 왼쪽)에 들어간다. 그 자리는
   본문의 `.risk-stage-desc { margin-bottom: auto }` 가 막대 그룹을 아래로 밀어 만든 여백이고,
   오른쪽은 레이더가 이미 덮고 있다 — 남은 왼쪽 사각형이 도넛 자리다.

   마크업 계약: .risk-donut > svg(트랙 circle + 진행 circle) + .risk-donut-num + .risk-donut-max.
   숫자 두 조각이 svg 의 **형제**라 도형과 겹쳐 세우려면 이 상자가 기준이 되어야 한다 —
   .risk-donut 을 position:relative 그리드로 잡고 svg 만 absolute 로 깔면 숫자 두 줄이
   그리드 가운데에 쌓인다(리포트 지면 종합점수 링과 같은 짜임).

   [자리잡기] .risk-body 는 세로 flex 열이다.
     margin-block: auto → 설명과 막대 사이 남는 세로의 가운데에 선다(desc 의 auto 와 나눠 갖는다)
     가로는 아래 [오버레이 tier] 가 레이더 폭을 뺀 **왼쪽 영역의 정중앙**으로 보낸다.
     좁은 폭(스택)에서는 레이더가 흐름으로 내려오므로 그냥 카드 가운데(align-self:center)다. */
.dash-risk .risk-donut {
  position: relative;
  display: grid;
  place-items: center;
  align-content: center;
  flex: 0 0 auto;
  align-self: center;
  width: var(--donut-d);
  height: var(--donut-d);
  margin-block: auto;
}

/* 링 도형 — 상자를 꽉 채우고 흐름에서 빠진다. viewBox 120×120 이라 어떤 지름에서도
   stroke-width 10 이 비례로 스케일된다. 이 규칙이 없으면 SVG 가 기본 폭(부모 전폭 556px)을
   먹어 카드 왼쪽 영역을 통째로 차지한다(프론트 보고 증상). */
.dash-risk .risk-donut-arc {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* 숫자 상자 — 계약에 없던 추가 래퍼(p.risk-donut-value). 링 **중앙**에 절대배치하고
   안쪽에서 점수/최대치를 세로로 쌓는다(span 두 개라 기본 인라인이면 옆으로 붙는다). */
.dash-risk .risk-donut-value {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  margin: 0;
  text-align: center;
}

/* 트랙 — 옅은 중성. 다른 차트 격자와 같은 톤이라 화면 안에서 한 벌로 읽힌다.
   두 circle 은 마크업에 `stroke="currentColor"` **프레젠테이션 속성**으로 와 있어 CSS 선언이
   그대로 이긴다(프레젠테이션 속성은 저자 스타일시트보다 우선순위가 낮다).
   stroke-opacity 는 프론트가 자리표시로 .15 를 걸어 둔 값이라 1 로 되돌린다 — 색을 여기서
   정하는 이상 불투명도까지 두 곳에서 나눠 가지면 톤이 어긋난다. */
.dash-risk .risk-donut-track {
  stroke: var(--chart-grid);
  stroke-opacity: 1;
}

/* 진행 링 — 현재 구간색. 값은 아래 :has() 브리지가 --risk-donut-ink 로 넣는다.
   기본값은 칩이 없는 경우(리포트 없음·점수 없음)의 폴백이라 중성 회색으로 둔다.
   채움 길이(dasharray, pathLength 100 정규화)는 프론트가 --score 로 이미 정확히 그린다 —
   CSS 는 크기·색·배치만 맡는다. */
.dash-risk .risk-donut-fill {
  stroke: var(--risk-donut-ink, var(--line-strong));
  stroke-opacity: 1;
}

/* 중앙 점수 — 지면 종합점수 링처럼 크게. 색은 본문 잉크(구간색은 링이 이미 말한다) */
.dash-risk .risk-donut-num {
  display: block;
  font-size: clamp(26px, calc(22.5px + 0.9vw), 34px);
  /* 390px→26px, 1280px→34px */
  font-weight: 800;
  letter-spacing: -.04em;
  line-height: 1;
  color: var(--text);
}

.dash-risk .risk-donut-max {
  display: block;
  margin-top: 2px;
  font-size: clamp(12px, calc(10.9px + 0.281vw), 14.5px);
  /* 390px→12px, 1280px→14.5px */
  font-weight: 700;
  line-height: 1;
  color: var(--text-mute);
}

/* 구간색 브리지 — 칩·활성 세그·활성 라벨과 **같은 값**을 쓴다(핀 정합·세그 강조에 쓴 것과
   같은 수법: 판정을 이미 들고 있는 칩에서 :has() 로 끌어온다, 마크업 무수정).
   진행 링은 면적이 넓은 색면이라 글자용 어두운 --seg-*-ink 가 아니라 **원색 --seg-*** 를 쓴다
   (미니카드 [상태 톤]에서 정리한 면/글자 분담과 같다). */
.dash-risk:has(.risk-stage-chip.is-low) {
  --risk-donut-ink: var(--seg-low);
}

.dash-risk:has(.risk-stage-chip.is-mid) {
  --risk-donut-ink: var(--seg-mid);
}

.dash-risk:has(.risk-stage-chip.is-high) {
  --risk-donut-ink: var(--seg-high);
}

.dash-risk:has(.risk-stage-chip.is-vhigh) {
  --risk-donut-ink: var(--seg-vhigh);
}


/* 카드 수준 auto 마진 배분(위 [빈 공간 배분])은 그대로 살린다 — 레이더가 흐름 밖으로 나가면
   본문이 카드 세로를 혼자 쓰므로, .risk-stage-desc 의 auto 가 막대 그룹을 아래로 밀어
   오버레이가 앉을 위쪽 공간을 만든다. 그 배분이 이 레이아웃의 전제다. */


/* ===== [dash-v2] 레이더 오버레이 전환 — 1261px 이상 (2026-08-27 재조정) ===== */
/* 그림을 240~260px 로 키우면서 전환점을 1240 → 1300px 으로 재산정했다.
   조건: 설명 "위험 요인이 적은 안정 구간입니다."(250px)가 오버레이 왼쪽에서 한 줄로 남아야
   한다 → 카드 안쪽 ≥ 그림(240) + 간격(16) + 설명(250) = 506px.
   `카드 = (vw − 48) × .4545`, `안쪽 = 카드 − 42` 이므로 vw ≥ 1276px → 여유를 두고 1300px.
   실측 확인: 1300px 안쪽 520 → 설명 자리 264px ✓ / 1366px 294px ✓ / 1500px 355px ✓.
   1300px 미만은 위 기본 규칙대로 흐름에 남아 본문 아래 세로 스택이 된다 — 겹침이 원천 봉쇄된다.
   ※ 주요 요약 1×4 전환점과 같은 값이 됐다 — "넓은 데스크톱 배치"를 한 지점에서 켠다. */
@media (min-width: 1261px) {
  .dash-risk:has(.risk-radar) .risk-radar {
    position: absolute;
    /* [2026-08-27 버그 수정] 카드 padding(sp-5) + 제목 한 줄(24px×1.25=30). 종전에는 카드
       gap(sp-3)까지 더해 12px 더 내려와 있었다 — 제목과 붙지 않는 선에서 최대한 끌어올린다. */
    top: calc(var(--sp-1) + 10px);
    right: var(--sp-5);
    z-index: 1;
    width: var(--radar-w);
  }

  /* 도넛 가로 위치 — 레이더가 덮는 오른쪽(그림 폭 + 간격)을 뺀 **왼쪽 영역의 정중앙**.
     `100%` 는 .risk-body 폭(= 카드 안쪽)이라 폭이 바뀌어도 식 하나로 따라온다.
     이 분기 밖(1300px 미만 스택)에서는 레이더가 흐름으로 내려오므로 기본 align-self:center 로
     카드 가운데에 선다 — 여기서 margin-left 를 주면 좁은 폭에서 음수가 되어 왼쪽으로 삐져나간다
     (실측 390px x=−5px). 그래서 이 한 줄은 반드시 이 미디어 블록 안에 있어야 한다. */
  .dash-risk:has(.risk-radar) .risk-donut {
    align-self: flex-start;
    margin-left: calc((100% - var(--radar-w) - var(--sp-4) - var(--donut-d)) / 2);
  }

  .dash-card.dash-risk:has(.risk-radar) {
    /* 오버레이가 차지한 오른쪽 폭만큼 본문 글줄을 물려 둔다 — 막대·라벨·콜아웃은 카드 폭을
       그대로 쓰고(사용자 지시), **글자만** 오버레이 밑으로 들어가지 않게 한다.
       칩·설명에만 걸리는 이유는 아래 규칙이 그 둘만 짚기 때문이다. */
    --radar-w: clamp(270px, 40%, 290px);
    /* [2026-08-27 사용자 "왜 또 작아졌어"] 겹침 수정 때 220px 로 줄였던 것을 240~260px 로
       되돌린다. 여유는 폭을 깎는 대신 아래 min-height 로 만든다(맞바꿈). */

    /* [말풍선 여유] 직전 검증이 놓친 것 — "오버레이 아래끝 ↔ **막대 위끝** 4px" 로 쟀는데,
       핀은 막대 위로 더 솟는다. 막대 위끝 위로 [점 반지름 12 + 말풍선 gap 5 + 말풍선 높이 25]
       ≈ 42px 이 더 있고, 그 구간에 캡션이 물렸다(사용자 스크린샷: 점수 64 = 막대 우측 64%,
       오버레이가 걸친 구간). 기준을 **말풍선 상단**으로 바꿔 다시 세운다:
         가용 세로 = 말풍선 상단 − 오버레이 top
                   = (막대 위끝 − 42) − (20 + 30)
       막대 위끝은 카드 높이에 따라 내려오므로 아래 min-height 로 그 값을 확보한다.
       오버레이 높이 = 그림(폭과 같음) + gap 8 + 캡션 22 = 폭 + 30.
       240px 기준 → 높이 270, 아래끝 321. 말풍선 상단(368)이 그보다 아래라 겹치지 않는다. */
    min-height: 500px;
    /* 480px(옆 추이 카드 높이) → 500 → **540px**. 폭을 240px 로 되돌리면 오버레이 높이가
       240 + 30 = 270 이 되어 아래끝이 51 + 270 = 321px 이다. 말풍선 상단이 그보다 위에 있으면
       안 되므로 카드를 60px 더 키워 막대·핀을 그만큼 내렸다 — 실측 말풍선 상단 368px,
       여유 47px(요구 ≥20px). 같은 행의 추이 카드도 stretch 로 함께 540px 이 된다. */
  }

  .dash-risk:has(.risk-radar) .risk-stage-chip,
  .dash-risk:has(.risk-radar) .risk-stage-desc {
    max-width: calc(100% - var(--radar-w) - var(--sp-4));
  }
}



/* ===== [dash-v2] 미니카드 칩 하한 보정 — 1251~1340px (2026-09-02) ===== */
/* 1×4 가 켜지는 1251px 부터 칸이 가장 좁다. 머리 행은 [칩 + 6px + 지표명]이고 가장 긴 이름은
   "산소포화도"(22px 기준 110px)다. 실측 칸 안쪽 글자 폭:
     1251px 163px · 1300px 168px · 1340px 174px · 1366px 187px · 1500px 211px
   48px 칩이면 머리 행이 164px 라 1251~1340px 에서 칸을 넘긴다(1251px 기준 1px 초과).
   그 구간만 42px 로 낮춘다 — 42 + 6 + 110 = 158px 로 1251px 에서도 5px 여유가 남는다.
   1341px 부터는 기본 48~56px 이 그대로 산다(1366px 실측 여유 15px). */
@media (min-width: 1251px) and (max-width: 1340px) {
  .dash-summary .mini-card {
    --ico-d: 42px;
  }

  .dash-summary .mini-ico {
    font-size: clamp(21px, calc(20.5px + 0.04vw), 22px);
    /* 1251px→21px, 1340px→21.05px — 42px 칩의 약 52%(위 비율 유지) */
  }
}


/* ===== [dash-v2] 좁은 오버레이 구간 폭 보정 — 1261~1319px (2026-08-27) ===== */
/* 전환점을 1300 → 1261px 로 내리면서(사용자: "1280px 키오스크에서도 오버레이 유지") 새로
   오버레이가 켜지는 1261~1319px 구간을 다시 쟀다. 이 구간은 카드가 좁은데 --radar-w 는
   270~290px(사용자 라이브 조정값)이라 설명문 자리가 모자란다:
     1261px → 카드 안쪽 509px − 그림 270 − 간격 16 = 설명 자리 **223px**  (필요 250px, 부족)
   설명 두 줄을 허용하는 대신 **그 구간에 한해 그림을 240px 로 줄여** 한 줄을 지켰다
   (사용자 조건: 한 줄 유지 우선). 보정 후 1261px → 509 − 240 − 16 = **253px** ✓.
   상한 1319px 인 이유: 1320px 부터는 안쪽이 536px 을 넘어 270px 그림으로도 설명 250px 이
   확보된다 — 그 위로는 사용자가 키운 270~290px 이 그대로 산다. */
@media (min-width: 1261px) and (max-width: 1319px) {
  .dash-card.dash-risk:has(.risk-radar) {
    --radar-w: clamp(220px, 34%, 240px);
  }
}


/* "낮을수록 좋습니다 · 위험점수 N점" — 점수 방향이 종전과 반대임을 화면이 직접 말한다.
   [2026-08-27 사용자 피드백 "맨 텍스트로 덩그러니 떠 있다"] 옅은 중성 틴트 스트립 +
   ⓘ 아이콘의 안내 콜아웃으로 감쌌다.
   색은 **중성**(--surface-sunken 면 + --line 테두리 + --text-sub 글자)이다 — 등급색·구간색을
   쓰면 카드 안에서 판정을 말하는 네 번째 색면이 되어(칩·활성 세그·활성 라벨에 이어) 축의
   판정 신호를 흐린다. 이 줄은 판정이 아니라 "축을 읽는 법" 안내다.
   자리: 카드가 flex 세로열이라 stretch 로 카드 폭을 가득 채운다 — 스트립이라 그게 맞다.
   [빈 공간 배분]의 `.dash-risk>:last-child { margin-bottom: auto }` 가 이 요소를 잡으므로
   margin-top 은 카드 gap(sp-3)에 맡기고 여기서 따로 주지 않는다(종전 sp-2 는 그 위에 얹혀
   라벨과의 간격만 20px 로 벌리던 값이라, 스트립이 된 지금은 gap 하나로 충분하다). */
.dash-risk .risk-note {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-sunken);
  font-size: clamp(12.5px, calc(11.19px + 0.337vw), 15.5px);
  /* 390px→12.5px, 1280px→15.5px */
  font-weight: 600;
  line-height: 1.45;
  color: var(--text-sub);
}

/* ⓘ 아이콘 — 글리프(ℹ U+2139)가 아니라 CSS 로 그린 원 + 소문자 i 다.
   Windows 10 에서 ℹ 는 폰트에 따라 이모지 프레젠테이션(파란 사각 아이콘)으로 떨어지거나
   글리프가 없어 두부(􏰀)로 나온다 — 시니어 화면에서 확인할 수 없는 위험을 지지 않는다.
   원은 border-radius 50% + 1.5px 테두리, 안의 i 는 content 로 넣은 ASCII 'i' 라
   어떤 시스템 폰트에도 있다. flex 아이템이라 글줄이 두 줄로 접혀도 세로 중앙에 남는다. */
.dash-risk .risk-note::before {
  content: "i";
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 1.35em;
  height: 1.35em;
  border: 1.5px solid currentColor;
  border-radius: 50%;
  font-size: clamp(11px, calc(9.9px + 0.281vw), 13.5px);
  /* 390px→11px, 1280px→13.5px — 본문보다 한 급 작다(아이콘이 글자를 눌러선 안 된다) */
  font-weight: 800;
  font-style: italic;
  line-height: 1;
  /* 이탤릭 i 가 원 안에서 오른쪽으로 쏠리는 것을 되돌린다 */
  text-indent: -.06em;
  opacity: .75;
}

/* 점수 조각 — 프론트가 숫자 부분만 <strong class="risk-note-score"> 로 감싼다.
   안내 문장 안에서 유일하게 회원마다 달라지는 값이라 본문색 + 800 으로 한 급 올린다
   (구간색이 아니다 — 위 블록 주석의 "중성" 원칙). */
.dash-risk .risk-note-score {
  font-weight: 800;
  letter-spacing: -.01em;
  color: var(--text);
}

/* 카드 안의 보조 안내 한 줄 (예: 맞춤 포인트 카드의 "데이터가 더 쌓이면…") */
.dash-card .dash-note {
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 600;
  line-height: 1.55;
  color: var(--text-mute);
}


/* ===== [dash-v2] 주요 건강지표 추이 (.dash-trend) ===== */
/* 혈압(수축기)·혈당·산소포화도. 색은 recharts 가 --chart-* 를 그대로 쓴다.

   [묘비 2026-08-27] "3선 한 차트 + 공용 범례" 구성이 폐기됐다. 아래 세 규칙이 그 부품이었다:
     .dash-trend .metric-legend      — 카드 안에서만 범례 글자 크기를 키우던 덮개
     .dash-trend .metric-legend-line — 같은 범례의 선 조각(16×3)을 점(12×12)으로 바꾸던 덮개
     .dash-trend .dash-trend-chart   — 단일 차트 높이 clamp(190px, 19vw, 260px)
   범례는 이제 행마다 .trend-row-head 가 겸하고, 차트는 행마다 .trend-row-chart 다.
   ※ 공용 .metric-legend / .metric-legend-line 과 .change-card .metric-legend 는 변화 탭이
      계속 쓴다 — 지운 것은 .dash-trend 로 스코프된 덮개 세 개뿐이다. */


/* ===== [dash-v2] 추이 3행 (.trend-rows > .trend-row) ===== */
/* [2026-08-27 사용자 지시] 3선을 한 축에 겹쳐 그리던 통합 차트를 **지표당 한 행**으로 나눴다.
   세 지표의 대역(수축기 90~190 / 혈당 70~200 / SpO₂ 90~100)이 한 축을 공유하면 SpO₂ 선이
   바닥에 눌려 변화가 보이지 않았다 — 행을 나누면 지표마다 자기 축을 갖는다.
   한 행 = [.trend-row-head 색점+이름+단위] + [.trend-row-chart 작은 추이].

   행 높이: 트랙을 auto 로 두고 각 행이 제 내용만큼만 차지하게 한다. 세 행 모두 x축 라벨을
   이고 있으므로 세 행이 같은 높이 가산(--trend-axis-h)을 받는다 — 균등해야 하는 것은 행
   총높이가 아니라 **플롯 높이**다(아래 .trend-row-chart 규칙).
   카드 채우기: flex:1 로 카드의 남는 세로를 .trend-rows 가 받고, grid 기본 align-content:
   stretch 가 그 여유를 auto 트랙 세 개에 똑같이 나눠 준다 — 옆 카드(.dash-risk)가 더 길어
   카드가 늘어나도 아래에 빈 띠가 남지 않는다. */
.dash-card.dash-trend {
  /* --trend-plot-h = 한 행의 플롯(선이 그려지는 그림) 높이. 세 행이 이 값 하나를 공유한다.
     --trend-axis-h = 각 행이 이고 있는 일자(MM.DD) 라벨 x축 높이(recharts XAxis 기본값). */
  /* [2026-08-27 사용자 지시 ④ "추이 행 높이를 눈에 띄게 줄여라"] 58~86px → 44~62px.
     행마다 일자 라벨 x축(--trend-axis-h)을 따로 이고 있어 카드 총높이는 (플롯+30)×3 이다 —
     플롯을 24px 줄이면 카드는 72px 줄어든다. 실측 552 → 426px(−23%).
     44px 하한은 선의 오르내림이 살아 있는 최소치다(1500px 실측: 세 지표 모두 진폭이 보인다).
     라벨·행 머리는 이 값과 무관하다 — .trend-row 가 `auto minmax(0,1fr)` 두 트랙이라 머리는
     제 높이를 갖고, x축 30px 은 아래 .trend-row-chart 가 따로 되더한다. */
  --trend-plot-h: clamp(44px, calc(36.11px + 2.022vw), 62px);
  /* 390px→44px, 1280px→62px */
  --trend-axis-h: 30px;
}

.dash-trend .trend-rows {
  display: grid;
  grid-auto-rows: auto;
  flex: 1 1 auto;
  min-width: 0;
}

/* [2026-09-02 추이 1행 전환] 지표 3행 → 위험도 점수 1행이 되면서 이 구조가 어긋났다.
   auto 트랙이 여러 개일 때는 flex:1 로 받은 카드 여유를 stretch 가 트랙들에 나눠 줘서 고르게
   찼는데, 트랙이 하나면 그 행은 제 내용(플롯 + 축)만큼만 차지하고 **남는 높이가 전부 카드
   아래 빈 띠로 몰린다**(frontend 스크린샷).
   [채택안 ①: 플롯을 키워 카드를 채운다] 행을 1fr 로 늘리고 그 안에서 차트가 남는 세로를
   전부 가져간다. ②(행 중앙 배치 + 카드 높이 축소)를 고르지 않은 이유 — 이 카드는 같은 행
   위험지수 카드와 stretch 로 높이를 맞추는데(레이더·도넛이 그 높이를 전제로 자리를 잡는다),
   카드를 줄이면 그 두 도형의 배치 계산을 다시 세워야 한다. 늘리는 쪽은 행 균형을 건드리지
   않으면서 추이 가독까지 올린다 — 한 지표만 남았으니 플롯이 클수록 좋다.
   1행일 때만 걸리게 `.trend-row` 가 하나뿐인 경우로 좁힌다(인접 형제가 없으면 1행이다) —
   나중에 지표가 다시 여러 행으로 돌아가면 위 auto 트랙 배분이 그대로 되살아난다.
   min-height 로 종전 높이를 바닥값으로 남겨, 카드가 짧아지는 폭에서도 플롯이 눌리지 않는다. */
.dash-trend .trend-rows:not(:has(.trend-row+.trend-row)) {
  /* [2026-09-04 보정] auto-rows 1fr 는 두 번째 자식(.dash-note 한 줄 캡션)에도 절반을
     나눠 줘 카드 하단 절반이 캡션 아래 빈 띠로 남았다(1500px 실측 195.6px 낭비 — frontend
     검증 FAIL). 명시 템플릿 [차트 1fr / 노트 auto]로 차트 행만 남는 세로를 갖게 한다.
     노트가 없는 회원(자식 1개)도 두 번째 트랙이 빈 트랙이라 무해하다. */
  grid-template-rows: minmax(0, 1fr) auto;
}

.dash-trend .trend-rows:not(:has(.trend-row+.trend-row)) .trend-row-chart {
  height: 100%;
  min-height: calc(var(--trend-plot-h) + var(--trend-axis-h));
}

.dash-trend .trend-row {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  align-content: center;
  gap: 4px;
  min-width: 0;
  padding: var(--sp-2) 0;
}

/* 행 사이 옅은 구분선 — .guide-row 와 같은 어휘(가이드 카드와 같은 줄 나눔으로 읽힌다) */
.dash-trend .trend-row+.trend-row {
  border-top: 1px solid var(--line);
}

.dash-trend .trend-row:first-child {
  padding-top: 0;
}

.dash-trend .trend-row:last-child {
  padding-bottom: 0;
}

/* 머리 라벨 — 좌측 정렬. 색점이 곧 아래 선 색이라 범례를 겸한다.
   [2026-08-27 사용자 피드백 ⑤ "행 머리 텍스트가 작다"] 12.5~15.5 → 14.5~19px.
   카드 제목(.dash-card-title 18~24px)의 바로 아래 급이라, 세 행이 무엇을 그린 그림인지
   차트를 보기 전에 먼저 읽힌다. 미니카드 지표명(.mini-name)과 같은 clamp 를 써서
   두 카드의 "지표 이름" 급이 화면에서 같은 크기로 선다. */
.dash-trend .trend-row-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-width: 0;
  font-size: clamp(14.5px, calc(12.53px + 0.506vw), 19px);
  /* 390px→14.5px, 1280px→19px */
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.3;
  color: var(--text-sub);
}

/* 색점 — 아래 선 색과 같아 이 점 하나가 행의 범례를 겸한다.
   색은 프론트가 인라인 background 로 시리즈의 --chart-* 를 넣고(하드코딩 금지 규칙),
   크기·모양(원형)은 여기가 소유한다 — 공용 .metric-legend-line 과 같은 분담이다.
   글자가 커진 만큼(피드백 ⑤) 10 → 12px — 점이 그대로면 이름 옆에서 눌려 보인다. */
.dash-trend .trend-row-dot {
  flex: 0 0 auto;
  width: 12px;
  height: 12px;
  border-radius: 50%;
}

/* 지표명은 머리 라벨의 크기(위 .trend-row-head)를 그대로 쓰고 톤만 올린다 —
   본문색 + 800 이라 옆의 단위(회색 600)와 급이 갈린다(피드백 ⑤ "가독 확보").
   nowrap 은 "혈압(수축기)"가 괄호 앞에서 접혀 단위가 둘째 줄로 밀리는 것을 막는다. */
.dash-trend .trend-row-name {
  min-width: 0;
  white-space: nowrap;
  font-weight: 800;
  color: var(--text);
}

/* 단위는 이름보다 한 급 낮다 — "혈압(수축기) mmHg" 에서 뒤쪽이 보조 정보임을 보인다 */
.dash-trend .trend-row-unit {
  font-size: clamp(12.5px, calc(10.97px + 0.393vw), 16px);
  /* 390px→12.5px, 1280px→16px */
  font-weight: 600;
  letter-spacing: 0;
  white-space: nowrap;
  color: var(--text-mute);
}

/* 차트 칸. recharts ResponsiveContainer 는 부모 높이가 미정이면 0 으로 접히므로
   트랙 크기에 맡기지 않고 height 를 직접 준다. 이 값이 곧 플롯(그림) 높이다. */
.dash-trend .trend-row-chart {
  width: 100%;
  min-width: 0;
  /* recharts XAxis 가 차트 높이에서 30px 을 먼저 떼 가므로 그만큼을 되돌려 준다 —
     이 가산이 없으면 --trend-plot-h 가 "플롯 + 축" 을 합친 값이 되어 그림이 그만큼 눌린다.
     축 높이는 화면 폭을 따라 변하지 않는 recharts 기본값이라 clamp 대상이 아니다.

     [2026-08-27 두 번째 지시] 종전에는 이 가산을 맨 아래 행(.trend-row.is-last)에만 줬다.
     x축 라벨이 월 슬롯이라 맨 아래 행에서만 그렸기 때문이다. 이제 세 행이 각자 일자(MM.DD)
     라벨을 그리므로 가산도 세 행 공통이다 — 프론트가 is-last 클래스 부여 자체를 없앴다.
     [묘비] `.dash-trend .trend-row.is-last .trend-row-chart` 가 이 가산을 갖고 있었다.
     이관 전 실측(라벨이 세 행에 다 붙은 뒤): 1366px 플롯 44px · 390px 플롯 16px 로 눌려 있었다. */
  height: calc(var(--trend-plot-h) + var(--trend-axis-h));
}


/* ===== [dash-v2] 생활관리 가이드 요약 (.dash-guide > .guide-row) ===== */
/* 4행 목록이라 카드 gap 을 0 으로 두고 행마다 위아래 여백 + 구분선을 준다
   (gap 을 남기면 구분선이 여백 한가운데 떠서 행과 붙지 않는다). */
.dash-card.dash-guide {
  gap: 0;
}

/* [2026-08-27 피드백 ⑥ 일관 점검] sp-2 → sp-3. 다른 다섯 카드는 .dash-card 의 gap(sp-3)이
   제목과 본문 사이를 벌리는데 이 카드만 gap 을 0 으로 두고 padding 으로 대신하고 있어
   제목 아래 간격이 혼자 4px 좁았다. 같은 값으로 맞춘다(행 구분선 위치는 그대로). */
.dash-guide>.dash-card-title {
  padding-bottom: var(--sp-3);
}

/* 아이콘은 두 줄(항목명 + 지침)을 세로로 가로지른다 — 문장이 길어 접혀도 정렬이 유지된다 */
.dash-guide .guide-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  column-gap: var(--sp-3);
  padding: var(--sp-3) 0;
}

.dash-guide .guide-row+.guide-row {
  border-top: 1px solid var(--line);
}

.dash-guide .guide-row:last-child {
  padding-bottom: 0;
}

.dash-guide .guide-icon {
  display: grid;
  place-items: center;
  grid-row: 1 / span 2;
  width: clamp(40px, calc(34.74px + 1.348vw), 52px);
  height: clamp(40px, calc(34.74px + 1.348vw), 52px);
  border: 1px solid var(--blue-line);
  border-radius: 50%;
  background: var(--blue-soft);
  font-size: clamp(19px, calc(16.37px + 0.674vw), 25px);
  /* 390px→19px, 1280px→25px */
  line-height: 1;
}

.dash-guide .guide-name {
  grid-column: 2;
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.3;
  color: var(--text);
}

.dash-guide .guide-text {
  grid-column: 2;
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 600;
  line-height: 1.55;
  color: var(--text-sub);
}


/* ===== [dash-v2] 맞춤 관리 포인트 (.dash-points > .point-list) ===== */
/* 도형은 리포트와 같은 컴포넌트(parts.jsx NoteCheckIcon, currentColor 로 그린다)를 쓰지만
   스타일은 영역별로 따로 소유한다 — report.css 의 .report .note-check 는 .report 스코프라
   여기서 걸리지 않고, v8 보존 원칙상 그 파일은 한 줄도 건드리지 않는다(plan §6).
   규격은 리포트와 같다: 아이콘 1em(글줄보다 작아 li 높이를 늘리지 않는다) · 색은 --blue ·
   li 가 [아이콘 | 텍스트] flex 두 칸뿐이라 두 줄로 접히는 항목은 아이콘 폭만큼 자동
   들여쓰기(hanging indent)된다.
   윗여백 .3em = (line-height 1.6em − 아이콘 1em) / 2 — 아이콘 중심을 첫 줄 글자 중심에 맞춘다
   (리포트는 line-height 1.3 이라 .15em 이었다. 값이 다른 것은 글줄 높이가 다르기 때문이다). */
.dash-points .point-list {
  display: grid;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 600;
  line-height: 1.6;
  color: var(--text);
}

.dash-points .point-list li {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
}

.dash-points .point-check {
  flex: 0 0 auto;
  width: 1em;
  height: 1em;
  margin-top: .3em;
  color: var(--blue);
}


/* ===== [dash-v2] 반응형 ===== */
/* 780px — 2열 → 1열. 변화 탭(.change-grid)과 같은 전환점이라 두 탭이 같은 폭에서 접힌다.
   카드 순서가 곧 우선순위다(등급 → 요약 → 위험 → 추이 → 가이드 → 포인트, plan §8).
   행별 비율(--col)은 두 카드가 한 줄에 설 때만 뜻이 있다 — 1열에서는 width 로 덮는다
   (선택자 특정도가 위 [행별 폭 비율]과 같고 뒤에 있어 이긴다). */
@media (max-width: 780px) {
  .dash-grid>.dash-card {
    width: 100%;
  }
}

/* ===== [dash-v2] 1행 비율 — 2×2 구간 재조정 (2026-08-27 사용자 지시) ===== */
/* 기본 비율 .28/.72 는 요약 4칸이 **한 줄(1×4)** 로 설 때를 위한 값이다. 1250px 이하로 내려와
   2×2 로 접히면 요약은 그 폭이 필요 없어지는데(칸이 두 배 넓어질 뿐이다) 등급 카드는 계속
   28% 에 묶여 표정 원·등급·설명이 비좁게 눌린다(사용자 스크린샷 ~1180px).
   이 구간만 **.36/.64** 로 옮긴다 — 등급 카드가 약 29% 넓어지고, 요약은 11% 줄지만 2×2 라
   칸당 폭은 오히려 종전 1×4 때보다 넓다.
   구간 하한 781px 인 이유: 780px 이하는 아래 [반응형]이 두 카드를 100% 폭으로 덮으므로
   비율 자체가 뜻을 잃는다. 상한 1250px 은 요약 1×4 전환점(1251px)과 맞물린다 — 배치가
   바뀌는 지점과 비율이 바뀌는 지점을 같게 두어야 그 경계에서 한 번만 움직인다. */
@media (min-width: 781px) and (max-width: 1250px) {
  .dash-grid>.dash-grade {
    --col: .36;
  }

  .dash-grid>.dash-summary {
    --col: .64;
  }
}

/* 620px — 체성분 칸의 보조값("골격근량 · 체지방률 22.6%")이 이 폭부터 두 줄로 접힌다.
   보조행 최소 높이를 두 줄로 올려 네 칸의 콘텐츠 높이를 다시 같게 맞춘다(y 정렬 조건).
   [묘비 620px] 미니카드 머리 행(지표명 | 측정일) 2단을 이 폭에서 1열로 되돌리던 분기가
   있었다. 시안 재배치로 **전 폭이 1열 템플릿**이 되면서(위 [주요 요약 4칸] 참고) 이 분기가
   기본값과 같아져 지웠다. */
@media (max-width: 620px) {
  .dash-summary .mini-card {
    --sub-h: clamp(36px, calc(30.8px + 1.34vw), 48px);
    /* 두 줄 — 390px→36px, 1280px→48px */
  }
}

/* 480px — 카드 안쪽 여백을 한 단계 줄여 .mini-card 2×2 가 눌리지 않게 한다.
   390px 기준 실측: 카드 안쪽 폭 ≈ 390 − 24(페이지) − 32(카드) = 334px → 칸당 163px. */
@media (max-width: 480px) {
  .dash-card {
    padding: var(--sp-4);
  }

  .dash-summary .mini-grid {
    gap: var(--sp-2);
  }

  .dash-summary .mini-card {
    padding: var(--sp-2) var(--sp-3);
  }
}

/* [묘비 — 미니카드 2배 아이콘 메달리온 (1100~1299px)] 2026-08-27 오전 사용자 지시 "아이콘을
   2배로" 로 만들었던 분기다. 칩을 absolute 로 흐름에서 빼 세로 자리를 0 으로 만들고
   (`--ico-d: clamp(54px,…,60px)` · `padding-left: calc(--ico-d + 24px)` · 칩 `position:absolute;
   left/top: var(--sp-3)` · 글리프 `clamp(28px,…,31px)`), 칩이 이름·값 두 줄을 세로로 가로지르는
   왼쪽 메달리온이 되게 했다.
   같은 날 오후 사용자가 **참고 시안 레이아웃 그대로**를 지시했고, 시안의 머리 행은
   [작은 아이콘 + 지표명] 이다 — 메달리온이 들어설 자리가 없다. 칩은 기본값(24~32px 인라인)
   하나로 통일하고 이 분기를 걷어냈다.
   ※ 되살릴 일이 생기면 카드에 position:relative 를 함께 돌려놔야 한다(칩의 컨테이닝 블록).
     그 선언도 시안 재배치에서 함께 빠졌다. */


/* ===== [dash-v2] 주요 요약 1×4 한 줄 — 1300px 이상 (2026-08-27 사용자 지시 ②) ===== */
/* "참고 시안은 4칸이 한 줄이다" — 2×2 를 1×4 로 편다. 위 [행별 폭 비율]의 .28/.72 와 한 세트다.

   [2026-08-27 사용자 지시] 전환점을 1300 → **1251px** 로 내렸다("1300 직하에서 바뀌지 말고
   1250px 이하에서만 2×2 로"). 종전 1300 근거는 시안 재배치·타이포 확대(판정 22px·아이콘 40px)
   **이전** 레이아웃 실측이라 지금 배치로 다시 쟀다 — 가장 넓은 조각은 판정 "식후 정상 범위"다.
   1251~1300px 구간을 지키려고 값·단위·보조값 clamp 하한만 한 단계 내렸다(값 34→32px,
   단위·보조 15→14px). 지표명 22px·판정 22px·아이콘 40px 은 그대로 간다.
   1250px 이하는 2×2 로 돌아간다(칸이 두 배 넓어진다).

   한 줄이 되면 카드 안 미니카드 **행이 하나뿐**이라, 칸마다 여러 줄로 쌓아도 요약 카드는
   종전 2×2(두 행)보다 낮다.
   ※ 칸 안 배치(1열 세로 스택)는 이제 전 폭 공통이라 여기서 다시 선언하지 않는다 — 이 분기가
     하는 일은 **그리드를 4열로 펴는 것**과, 좁아진 칸에 맞춰 값·단위·보조값을 한 급 낮추는
     것 둘뿐이다. */
@media (min-width: 1251px) {
  .dash-summary .mini-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  /* 값·단위·보조값만 한 급 낮춘다 — 지표명(19px)·판정(20px)·측정일(13px)은 원래 폭이
     좁아 그대로 둔다. 위계(이름 < 값 > 상태)는 값 34px 대 상태 20px 로 여전히 유지된다. */
  .dash-summary .mini-value {
    font-size: clamp(32px, calc(11.2px + 1.62vw), 37px);
    /* 1251px→32.5px, 1500px→37px — 하한을 34 → 32px 로 내렸다(전환점 1300→1251px 재실측) */
  }

  .dash-summary .mini-unit,
  .dash-summary .mini-sub {
    font-size: clamp(14px, calc(1.5px + 1vw), 16.5px);
    /* 1251px→14px, 1500px→16.5px — 보조값 "골격근량 · 체지방률 22.6%" 가 한 줄에 남는 크기 */
  }
}


/* ============================================================
   [signup-agree] 회원가입 약관 동의 (2026-09-30 사용자 지시)
   ① 동의 블록 (.signup-agree — /signup "3. 약관 동의" 섹션, 입력칸과 [회원가입 완료] 사이)
   ② 전문보기 모달 (.legal-modal — 기존 .modal-backdrop / .modal-card 위에 얹는다)

   · 체크 스킨은 [consent] ① .consent-modal .consent-check* 와 같은 모양·크기
     (26px 상자 · 흰 체크 · focus-visible 링)를 .signup-agree-* 이름으로 재현한다.
     실제 input 은 clip 으로만 가린다(키보드·스크린리더 유지).
   · 모달 본문의 문서 타이포(.doc-lede/.doc-h3/.doc-p/…)는 여기서 새로 쓰지 않는다 —
     [consent] ② 의 .doc-page 규칙을 `, .legal-modal .doc-x` 로 확장해 /terms·/privacy 와 같은 규칙 하나를 공유한다.
   · 색은 전부 global.css :root 토큰. 위험색은 .field.is-error 와 같은 --danger.
   ============================================================ */

/* ===== [signup-agree] ① 동의 블록 ===== */
/* 섹션 제목 위 여백 — 바로 앞 입력 그리드의 마지막 칸이 이미 margin-bottom(sp-5)을 들고 온다.
   제목 기본 margin-top(sp-6)을 그대로 두면 44px 이 벌어져, 1→2 섹션(본인인증 카드→제목 24px)과
   리듬이 어긋난다. 합이 24px(sp-5 + sp-1)이 되도록 이 자리에서만 줄인다. */
.signup-page .signup-field-grid+.section-label {
  margin-top: var(--sp-1);
}

.signup-agree {
  padding: var(--sp-2) var(--sp-4) var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  transition: border-color .15s ease;
}

/* 행 = 라벨 전체가 누르는 자리(44px 이상). 옅은 면은 누를 수 있는 범위를 보여 준다 */
.signup-agree .signup-agree-check {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-width: 0;
  min-height: var(--tap-min);
  padding: var(--sp-1) var(--sp-2);
  border-radius: var(--r-md);
  cursor: pointer;
}

.signup-agree .signup-agree-check:hover {
  background: var(--surface-sunken);
}

/* 실제 input 은 지우지 않고 시각적으로만 가린다(키보드 포커스·스크린리더 유지) */
.signup-agree .signup-agree-check input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* 체크 상자 — .consent-modal .consent-check-box 와 같은 값 */
.signup-agree .signup-agree-box {
  position: relative;
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border: 2px solid var(--line-strong);
  border-radius: 8px;
  background: var(--surface);
  transition: background .15s ease, border-color .15s ease;
}

.signup-agree .signup-agree-box::after {
  content: "";
  position: absolute;
  left: 7px;
  top: 2px;
  width: 7px;
  height: 13px;
  border: solid var(--text-invert);
  border-width: 0 3px 3px 0;
  transform: rotate(45deg) scale(.4);
  opacity: 0;
  transition: opacity .12s ease, transform .12s ease;
}

.signup-agree .signup-agree-check input:checked~.signup-agree-box {
  background: var(--blue);
  border-color: var(--blue);
}

.signup-agree .signup-agree-check input:checked~.signup-agree-box::after {
  opacity: 1;
  transform: rotate(45deg) scale(1);
}

.signup-agree .signup-agree-check input:focus-visible~.signup-agree-box {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
}

/* 전체 동의 "일부만 동의" — JSX 가 두 항목 중 하나만 켜지면 input.indeterminate 를 켠다.
   네이티브 표시는 input 과 함께 가려져 있으므로 상자에 가로 막대로 다시 그린다.
   다 켠 상태(파랑 면 + 흰 체크)와 헷갈리지 않게 옅은 파랑 면 + 파랑 막대로 한 단계 낮춘다 */
.signup-agree .signup-agree-check input:indeterminate~.signup-agree-box {
  background: var(--blue-soft);
  border-color: var(--blue);
}

.signup-agree .signup-agree-check input:indeterminate~.signup-agree-box::after {
  left: 5px;
  top: 9.5px;
  width: 12px;
  height: 0;
  border-color: var(--blue);
  border-width: 0 0 3px;
  border-radius: 2px;
  transform: none;
  opacity: 1;
}

/* 문구 — 시니어 대상 최소 15px(모바일)·18px(PC). 한국어는 어절 단위로만 접는다(keep-all),
   한 어절이 한 줄보다 길 때만 안전하게 끊는다(anywhere — 가로 넘침 방지) */
.signup-agree .signup-agree-text {
  min-width: 0;
  font-size: clamp(15px, calc(13.69px + 0.337vw), 18px);
  /* 390px→15px, 1280px→18px */
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: -.02em;
  color: var(--text);
  word-break: keep-all;
  overflow-wrap: anywhere;
}

/* 전체 동의 — 한 단계 크고 굵게 */
.signup-agree .signup-agree-check.is-all .signup-agree-text {
  font-size: clamp(16px, calc(14.25px + 0.449vw), 20px);
  /* 390px→16px, 1280px→20px */
  font-weight: 800;
  letter-spacing: -.03em;
}

/* [필수] 칩 — 섹션 제목의 .label-req 와 같은 파랑 톤(em 기본 기울임은 끈다) */
.signup-agree .signup-agree-tag {
  display: inline-block;
  padding: 0 7px;
  border: 1px solid var(--blue-line);
  border-radius: var(--r-pill);
  background: var(--blue-soft);
  color: var(--blue);
  font-style: normal;
  font-size: clamp(13px, calc(12.12px + 0.225vw), 15px);
  /* 390px→13px, 1280px→15px */
  font-weight: 700;
  line-height: 1.45;
  letter-spacing: 0;
  white-space: nowrap;
}

/* 개별 항목 목록 — 위 구분선이 곧 "전체 동의 행 아래 구분선"이다
   (선을 라벨이 아니라 목록에 두어 라벨의 hover 면이 선과 겹치지 않는다) */
.signup-agree .signup-agree-list {
  display: grid;
  gap: var(--sp-2);
  margin: var(--sp-2) 0 0;
  padding: var(--sp-2) 0 0;
  border-top: 1px solid var(--line);
  list-style: none;
}

/* 항목 행 = [체크 + 문구 | 전문보기] 양끝 정렬. 문구가 두 줄로 접혀도 버튼은 세로 가운데·우측.
   [실측 2026-09-30] 버튼을 늘 옆에 두면 360px 에서 "필수" 칩이 첫 줄에 혼자 남고(3줄),
   320px 에서는 "개인정보처리방/침에" 처럼 어절이 잘렸다. 그래서 줄바꿈을 폭 분기 대신 **내용 기준**으로 건다 —
   라벨 기준폭(flex-basis) = 문구 칸 11em("필수 개인정보처리방침에" 실측 10.4em + 여유) + 상자·간격·안쪽 여백.
   이 폭과 버튼이 한 줄에 못 서면 버튼만 다음 줄 우측으로 내려가고(margin-left:auto) 문구는 전폭을 쓴다.
   em 기준이라 시니어가 글자를 키워도 같은 규칙으로 접힌다.
   실측(Pretendard): 375px↑ 버튼 옆(문구 2줄, 768px↑ 1줄) · 360px↓ 버튼 아래 우측(문구 1~2줄) · 칩 고립·어절 잘림 0 */
.signup-agree .signup-agree-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  /* 세로 간격 0 — 버튼이 아래로 내려가면 제 문구에 붙고, 다음 항목과는 목록 gap 만큼 떨어져 묶음이 갈린다 */
  gap: 0 var(--sp-2);
  font-size: clamp(15px, calc(13.69px + 0.337vw), 18px);
  /* 390px→15px, 1280px→18px — .signup-agree-text 와 같은 값. 아래 기준폭(em)의 단위 */
}

.signup-agree .signup-agree-item>.signup-agree-check {
  /* 11em(문구) + 26px(상자) + sp-3(상자↔문구) + 좌우 안쪽 여백 sp-2×2 */
  flex: 1 1 calc(11em + 26px + var(--sp-3) + var(--sp-2) * 2);
}

/* 전문보기 — 외곽선 pill. 체크와 분리된 별도 버튼(누른다고 동의되지 않는다).
   margin-left:auto — 옆에 설 때는 남는 폭이 0 이라 효과가 없고, 다음 줄로 내려가면 우측에 붙인다 */
.signup-agree .signup-agree-view {
  display: inline-flex;
  flex: 0 0 auto;
  margin-left: auto;
  align-items: center;
  justify-content: center;
  min-height: var(--tap-min);
  padding: 0 var(--sp-4);
  border: 1px solid var(--blue-line);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--blue);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  font-weight: 700;
  letter-spacing: -.02em;
  white-space: nowrap;
  transition: background .15s ease, border-color .15s ease;
}

.signup-agree .signup-agree-view:hover {
  background: var(--blue-soft);
  border-color: var(--blue);
}

/* 오류(미동의 제출) — 블록 테두리 + 아직 안 켠 **필수 항목** 상자 테두리를 위험색으로.
   전체 동의 상자는 칠하지 않는다(그 자체는 필수 항목이 아니고, 일부 동의 막대와 색이 섞인다).
   색만으로 알리지 않는다: 블록 아래 .field-error 문구(role=alert)가 뜻을 말한다 */
.signup-agree.is-error {
  border-color: var(--danger);
}

.signup-agree.is-error .signup-agree-item input:not(:checked)~.signup-agree-box {
  border-color: var(--danger);
}

/* 블록 아래 오류 문구 — 기본 .field-error(12→15px)보다 한 단계 크게. 제출 버튼 바로 위라 놓치지 않게 */
.signup-agree+.field-error {
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
}

/* 480px 이하 — 카드(.card) 안의 카드라 좌우 여백을 한 단계씩 줄여 문구 칸에 돌려준다.
   기준폭도 줄어든 안쪽 여백(sp-1×2)으로 다시 계산한다 — 390px 에서 버튼 옆 배치에 22px 여유 */
@media (max-width: 480px) {
  .signup-agree {
    padding: var(--sp-1) var(--sp-2) var(--sp-2);
  }

  .signup-agree .signup-agree-check {
    padding: var(--sp-1);
  }

  /* 버튼이 문구 아래로 내려가는 폭(≈370px 이하)에서 항목끼리 확실히 떼어 둔다 */
  .signup-agree .signup-agree-list {
    gap: var(--sp-3);
  }

  .signup-agree .signup-agree-item>.signup-agree-check {
    flex-basis: calc(11em + 26px + var(--sp-3) + var(--sp-1) * 2);
  }

  .signup-agree .signup-agree-view {
    padding: 0 var(--sp-3);
  }
}


/* ===== [signup-agree] ② 전문보기 모달 ===== */
/* 세로 flex 3단(머리 · 본문 · 하단 버튼 바). 카드 자체는 스크롤하지 않고 본문만 스크롤한다 —
   그래야 제목과 [닫기]/[동의하고 닫기] 가 긴 문서 어디에서도 항상 보인다.
   폭: 지시 min(720px, calc(100vw - 32px)) 와 같은 값을 100% 로 쓴다 — .modal-backdrop 이 좌우
   sp-4(16px) 여백을 이미 갖고 있어 결과가 같고, 데스크톱 스크롤바·노치 여백까지 넘지 않는다. */
.modal-card.legal-modal {
  display: flex;
  flex-direction: column;
  width: min(720px, 100%);
  max-height: min(88dvh, 820px);
  overflow: hidden;
}

/* 노치 기기(viewport-fit=cover) — 바깥 여백을 safe-area 보다 작게 두지 않는다.
   이 모달의 딤에만 건다(다른 모달의 .modal-backdrop 은 그대로) */
.modal-backdrop:has(>.legal-modal) {
  padding: max(var(--sp-4), env(safe-area-inset-top, 0px)) max(var(--sp-4), env(safe-area-inset-right, 0px)) max(var(--sp-4), env(safe-area-inset-bottom, 0px)) max(var(--sp-4), env(safe-area-inset-left, 0px));
}

.legal-modal .legal-modal-head {
  flex: 0 0 auto;
  padding: var(--sp-5) var(--sp-6) var(--sp-4);
  border-bottom: 1px solid var(--line);
}

.legal-modal .legal-modal-title {
  font-size: clamp(20px, calc(17.37px + 0.674vw), 26px);
  /* 390px→20px, 1280px→26px */
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1.25;
  color: var(--text);
  word-break: keep-all;
}

/* .doc-page .doc-version 과 같은 크기·색 */
.legal-modal .legal-modal-version {
  margin-top: var(--sp-1);
  font-size: clamp(13px, calc(11.69px + 0.337vw), 16px);
  /* 390px→13px, 1280px→16px */
  font-weight: 600;
  color: var(--text-mute);
}

/* 본문 — 여기만 스크롤. 끝까지 내려도 뒤 페이지로 스크롤이 번지지 않는다(contain).
   tabIndex=0 이라 키보드로 초점이 오면 방향키로 읽힌다 — 링은 카드 overflow 에 잘리지 않게 안쪽으로 */
.legal-modal .legal-modal-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: var(--sp-5) var(--sp-6) var(--sp-3);
  /* [2026-09-30 추가 지시] 한국어는 어절 단위로만 접는다 — "실 / 증", "합니 / 다" 처럼 낱말 중간에서 끊기던 것 방지.
     긴 URL·영문·가운뎃점으로 이어진 긴 열거는 칸을 넘칠 때만 끊는다(anywhere). 상속이라 표 칸(.doc-table)까지 같다.
     ※ .doc-page(/terms·/privacy) 본문은 "결과 불변" 원칙으로 손대지 않았다 — 페이지 개선은 별도 확인 사항 */
  word-break: keep-all;
  overflow-wrap: anywhere;
}

/* 표 칸만 break-word — anywhere 는 칸의 최소 폭(min-content)까지 줄여 자동 표 배치가 "구분" 칸을 52px 로
   눌렀고, 그 폭에 "회원정보"·"민감정보"가 안 들어가 낱말 중간에서 끊겼다. break-word 는 최소 폭 계산에 끼지 않아
   칸이 낱말 폭(78px)을 확보한다. 실측 390px: 표 폭 460px 그대로 · 칸 넘침 0 · 낱말 중간 끊김 2→0.
   ※ 한 칸에 공백 없는 아주 긴 열(긴 URL 등)이 생기면 표가 그만큼 넓어질 수 있다 — 그래도 .doc-table-scroll 안에서만 스크롤된다 */
.legal-modal .doc-table th,
.legal-modal .doc-table td {
  overflow-wrap: break-word;
}

.legal-modal .legal-modal-body:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: -3px;
}

/* 하단 버튼 바 — 항상 보인다. PC 는 우측 정렬(닫기 | 동의하고 닫기) */
.legal-modal .legal-modal-actions {
  display: flex;
  flex: 0 0 auto;
  justify-content: flex-end;
  gap: var(--sp-2);
  padding: var(--sp-4) var(--sp-6);
  border-top: 1px solid var(--line);
  background: var(--surface-soft);
}

.legal-modal .legal-modal-actions .btn {
  min-width: 9em;
}

@media (max-width: 480px) {
  .legal-modal .legal-modal-head {
    padding: var(--sp-4) var(--sp-4) var(--sp-3);
  }

  .legal-modal .legal-modal-body {
    padding: var(--sp-4) var(--sp-4) var(--sp-2);
  }

  /* 모바일 — 두 버튼 균등 폭 한 줄(세로로 쌓으면 본문 높이를 44px 더 먹는다) */
  .legal-modal .legal-modal-actions {
    padding: var(--sp-3) var(--sp-4);
  }

  .legal-modal .legal-modal-actions .btn {
    flex: 1 1 0;
    min-width: 0;
    padding: 0 var(--sp-3);
  }
}


/* ============================================================
   [print-choice] 상세리포트 인쇄 — "변화 페이지도 함께 인쇄할까요?" 모달 (2026-09-30)
   마크업: front/components/PrintChoiceModal.jsx (frontend-dev)
     .modal-backdrop > .modal-card.print-choice-modal
       h2.print-choice-title · p.print-choice-lead
       .print-choice-actions > .btn.btn-blue.btn-lg.btn-block.print-choice-both
                             + .btn.btn-outline-blue.btn-block.print-choice-only
       button.print-choice-x (닫기 — 인쇄하지 않고 닫기)
   · 14 구역 .modal-backdrop/.modal-card 규약과 [design-polish] 등장 모션을 그대로 쓴다.
   · 모양은 [consent] ① 동의 모달과 같은 계열 — 가운데 정렬 · 제목 아래 선 · 안내문 아래 선 · 버튼 세로.
   · 시니어 대상: 안내문 16→20px(모바일 15px↑ · PC 18px↑ 조건), 버튼 높이 --tap-min 이상
     (주 버튼 .btn-lg 58px · 둘째 버튼 .btn 44px), 닫기 44×44.
   · 버튼은 **폭과 무관하게 세로로 쌓는다**(모바일 세로 배치 조건 포함). PC 에서도 주 버튼 라벨이
     24px 로 길어("변화 페이지도 함께 인쇄") 둘을 나란히 두면 카드가 넓어지고 두 버튼 높이(58/44)가
     어긋난다 — 동의 모달과 같은 판단.
   · 둘째 버튼은 동의 모달의 [닫기](회색 테두리)와 달리 **실제 인쇄 동작**이라 .btn-outline-blue 색을
     그대로 둔다(위계는 채움/외곽선으로만 가른다).
   · 인쇄 지면에서는 15 블록의 .modal-backdrop 숨김이 이미 지운다(여기서 따로 두지 않는다).
   ============================================================ */
.modal-card.print-choice-modal {
  /* 닫기 버튼(absolute)의 기준 상자 */
  position: relative;
  /* 폭은 딤의 좌우 여백(sp-4) 안에서 100% — 390px 에서 358px. 동의 모달보다 한 단계 넓은 480px 은
     제목(26px)이 PC 에서 한 줄로 들어가는 폭이다. */
  width: min(480px, 100%);
  padding: var(--sp-6) var(--sp-5) var(--sp-5);
  text-align: center;
}

.print-choice-modal .print-choice-title {
  /* 좌우 32px — 우상단 닫기(44px) 자리를 비워 두면서 가운데 정렬을 유지한다(좌우 같은 값) */
  padding: 0 var(--sp-8) var(--sp-4);
  border-bottom: 1px solid var(--line);
  font-size: clamp(20px, calc(17.37px + 0.674vw), 26px);
  /* 390px→20px, 1280px→26px (동의 모달 제목과 같은 값) */
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1.3;
  color: var(--text);
  word-break: keep-all;
  text-wrap: balance;
}

.print-choice-modal .print-choice-lead {
  padding: var(--sp-5) var(--sp-1);
  border-bottom: 1px solid var(--line);
  font-size: clamp(16px, calc(14.25px + 0.449vw), 20px);
  /* 390px→16px, 1280px→20px (동의 모달 안내문과 같은 값 — 모바일 15px↑ · PC 18px↑) */
  font-weight: 500;
  line-height: 1.55;
  color: var(--text-sub);
  /* 한국어는 어절 단위로만 접는다("인쇄하시 / 겠습니까" 끊김 방지). 가운데 정렬 3줄 안팎이라
     줄 길이를 고르게 나눈다(balance) — 768px 에서 "…추이를 볼 / 수 있어요" 처럼 한 어절만 넘어가던 줄 방지.
     balance 를 모르는 브라우저는 종전 줄바꿈 그대로다. */
  word-break: keep-all;
  text-wrap: balance;
}

.print-choice-modal .print-choice-actions {
  display: grid;
  gap: var(--sp-2);
  margin-top: var(--sp-5);
}

/* 버튼 라벨도 어절 단위로만 접는다 — 좁은 폭·큰 글꼴 설정에서 두 줄이 되더라도 낱말이 쪼개지지 않게 */
.print-choice-modal .print-choice-both,
.print-choice-modal .print-choice-only {
  word-break: keep-all;
}

/* 닫기 — 14 구역 .modal-close 와 같은 모양(44×44 · 옅은 글자 · 눌림 배경) */
.print-choice-modal .print-choice-x {
  position: absolute;
  top: var(--sp-2);
  right: var(--sp-2);
  display: grid;
  place-items: center;
  width: var(--tap-min);
  height: var(--tap-min);
  padding: 0;
  border-radius: var(--r-sm);
  color: var(--text-mute);
  font-size: clamp(24px, calc(21.37px + 0.674vw), 30px);
  /* 390px→24px, 1280px→30px — "×" 글리프가 작아 제목보다 한 단계 크게 */
  line-height: 1;
}

.print-choice-modal .print-choice-x:hover {
  background: var(--surface-sunken);
  color: var(--text);
}

/* 모바일 — 동의 모달과 같은 분기에서 안쪽 여백만 줄인다(버튼은 원래 세로) */
@media (max-width: 700px) {
  .modal-card.print-choice-modal {
    padding: var(--sp-5) var(--sp-4) var(--sp-4);
  }
}

/* ===== [checkup] 국가건강검진 결과 (계약 §13 · 2026-09-14 신설 / 2026-09-21 항목 UI 개편) =====
   화면 3종 — 목록 /checkup · 불러오기 /checkup/import · 상세 /checkup/[date].
   전부 `.checkup-page` 를 바깥 블록으로 두고, 화면별로 `.checkup-list` `.checkup-import`
   `.checkup-detail` 을 함께 건다(공통 머리줄·판정 배지·개수 스트립을 한 벌만 정의하려고).

   구역 지도
     C1. 페이지 공통 — 폭 · 머리줄(.topbar-row) · 상태 톤 변수(--ck-tone / -bg / -line) ·
         흰 카드(.form-card) · 버튼 줄(.action-row) · 단계 버튼(.step-cta / .step-sub)
     C2. 판정 배지(.judge-badge) · 개수 스트립(.count-strip) — 3화면 공용
     C3. 목록 (.checkup-list) — 머리부(.hero-row) · 검진 1건 카드(.visit-card)
     C4. 불러오기 (.checkup-import) — 단계표시 / 인증수단 타일 / 대기 / 조회중 / 결과 / 복원 탈출
     C5. 상세 (.checkup-detail) — 머리부(.detail-head) / 안내 줄(.detail-memo) /
         C5-2 항목 UI(.result-summary 요약 카드 + .result-group 그룹 카드 + .item-row 항목 줄) /
         각주(.result-foot) / 검진기관 소견(.detail-opinion) / 하단 버튼(.detail-actions)
     C6. 반응형 — 759px(목록 머리부 세로 — .measure-user 와 같은 값) · 900px(상세 머리부 세로) ·
         1100px(항목 줄 한 줄 조판) · 480px(카드 여백·요약 칩) · 560px(불러오기 단계표시·타일)
     C7. 인쇄 — 상세 화면 지면 규칙(색 보존 · 카드 분할 금지 · 버튼 숨김). **블록 맨 끝이어야 한다**

   [2026-09-22 사용자 지시 — "measure/body 처럼 색상 통일하고 깔끔하게"]
     이 블록의 색 언어를 측정 입력 화면(.measure-user, 2797행~)과 **한 벌로 맞췄다**:
     블루 단색(--blue / --blue-dark / --blue-soft / --blue-line) + 흰 면(--surface) + --line.
     걷어낸 것
       ① 인증수단 타일 5종의 **브랜드색** — 카카오 #FEE500 / PAYCO #FF2D2D / KB #FFBC00 /
          삼성 #1428A0 / PASS #E4002B. 한 화면에 노랑·적·감청 다섯 계열이 깔려 "통일이 안 된"
          인상의 주범이었다(선택지를 가르는 단서는 원래부터 색이 아니라 글자였다 — 아래 C4).
       ② 단계표시 완료 칸의 **초록**(--grade-normal-bg / --ck-ink-ok) → --blue-soft / --blue-dark.
          초록은 이 블록에서 **판정 상태색**(정상A)이다. 진행 상태에까지 쓰면 두 뜻이 겹친다.
       ③ 목록 출처 뱃지 .meta-source.is-nhis 의 초록 → 블루(같은 이유).
       ④ 보조 버튼 .step-sub 의 **회색 외곽선** → btn-outline-blue 언어(파란 선·파란 글자).
       ⑤ 결과 화면 성공 표시 ✅(글꼴이 정한 초록 이모지) → 파란 원 + ✓(.done-icon.is-mark).
     남긴 것 — **판정 상태색**은 통일 대상이 아니다: C1 의 --ck-ink-ok/-warn/-danger 3종,
     C2 판정 배지·개수 스트립, 만료 경고 면(--grade-attention-*), 오류 안내 줄(.is-red).
     이것들은 브랜드색이 아니라 "이 값이 어떤 상태인가"를 말하는 색이라 파랗게 만들면 뜻이 사라진다.

   [--text-mute 를 안내 글자에서 뺐다 — 같은 지시]
     --text-mute(#848d9e)는 흰 면 3.34:1 로 AA(4.5:1) 미달이다. 이 블록의 안내·메모성 글자는
     전부 --text-sub(5.88:1)로 올렸다: .hero-memo · .tile-memo · .done-memo · .load-what ·
     .load-elapsed · .meta-source · .row-tag · .escape-memo · .flow-step · .step-no ·
     .step-sub.is-quiet. (C5-2 각주·.judge-key 에서 이미 내린 판단을 블록 전체로 넓힌 것)
     남긴 자리는 하나 — C1 의 `--ck-tone` **기본값**이다. 이것은 글자색이 아니라 상태 톤 변수의
     초깃값이고, 실제 표시 요소는 언제나 .is-ok/.is-warn/.is-danger/.is-none 중 하나를 달아
     덮어쓴다(.is-none 은 이미 --text-sub 다). 화면에 나오는 값이 아니라 건드리지 않았다.

   [2026-09-21 — 상세 항목 UI 를 표에서 카드+행으로 바꿨다]
     종전 C5 는 4열 fr 그리드 표(name 1.45 / value .85 / judge .95 / ref 1.25)였다. 실측으로
     1366px 참고치 열 263px 상시 공백 · 768px 단위 문자열 오버플로 · 740→761px 575px 높이 절벽이
     확인돼 통째로 대체했다(C5-2 머리 주석에 삭제한 선택자 목록과 근거가 있다).
     같은 개편에서 그룹 상자를 [dash-v2] .dash-card 레시피(--r-xl · sp-5 · --shadow-card)로
     맞췄고, 목록 카드(.visit-card)·소견 카드(.opinion-block)도 같은 라운드·여백으로 통일했다 —
     사용자 앱에서 검진만 다른 카드 언어를 쓰던 지점을 없애려는 것이다.

   [색은 왜 등급 4색이 아니라 3색인가]
     검진 항목 판정은 서버가 §5-8 [별표 4] 기준으로 ok·warn·danger·none 네 가지만 준다
     (리포트 등급의 4단계와는 다른 축이다). 그래서 정상(초록)·경계(황갈)·질환의심(적)
     세 톤 + 판정없음(회색)만 쓴다. 글자색은 아래 C1 의 --ck-ink-* 세 개가 정본이다 —
     #15803D / #A16207 / #B91C1C, 전부 등급 면색(--grade-*)의 **한 단계 진한(-700) 값**이다.
     흰 배경에서 4.5:1 을 넘기려고 면(원색)과 글자(한 단계 진한 값)를 나눠 쓴 [dash-v2] 의
     판단을 따르되, danger 만은 그 블록과 값이 다르다: [dash-v2] .mini-card 는 원색
     #DC2626(red-600)을 그대로 쓰는데(그 블록의 면 위에서는 4.62:1 로 통과한다), 이 블록은
     더 옅은 --grade-consult-bg / 행 틴트 위에 글자를 얹어 3.95~4.35:1 로 떨어졌다.
     그래서 여기서만 red-700 으로 내렸다. 색만으로 구분하지 않고 ✓ △ ▲ 기호와
     글자("정상A"·"경계"·"질환의심")를 함께 쓴다(시니어·색각 이상 대비 3중 표기).
   ============================================================ */

/* ===== C1. 페이지 공통 ===== */
.checkup-page {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;

  /* 판정 잉크 정본 — 등급 면색(--grade-*)의 한 단계 진한 값. 이 블록 안에서 초록·황갈·적을
     글자에 쓰는 자리는 **전부 이 세 변수만 본다**(같은 리터럴을 여러 줄에 다시 적지 않는다:
     종전엔 #15803D 가 네 곳, #A16207 이 두 곳에 흩어져 있었다).
     흰 면 대비 — ok 5.02:1 / warn 4.92:1 / danger 6.47:1.
     danger 를 --grade-consult(#DC2626, red-600)에서 red-700 으로 내린 이유: 그 값은 이 블록이
     쓰는 옅은 면 위에서 AA 를 못 넘겼다(--grade-consult-bg 3.95:1 · 행 틴트 4.35:1 ·
     판정 낱말은 18.66px bold 문턱을 739px 부터 넘으므로 390~738px 전 구간 실패).
     #B91C1C 로 바꾼 뒤: 흰 면 6.47 / --grade-consult-bg 5.30 / 행 틴트(#FEF0F0) 5.83 — 전부 통과.
     면·선(--ck-tone-bg/-line)은 손대지 않았다. 색면 인상은 그대로고 잉크만 한 단계 진해진다. */
  --ck-ink-ok: #15803D;      /* green-700  (--grade-normal  #16A34A 의 한 단계 진한 값) */
  --ck-ink-warn: #A16207;    /* yellow-700 (--grade-attention #CA8A04 의 한 단계 진한 값) */
  --ck-ink-danger: #B91C1C;  /* red-700    (--grade-consult  #DC2626 의 한 단계 진한 값) */

  /* 상태 톤 — 하위 요소는 .is-ok / .is-warn / .is-danger / .is-none 한 클래스만 걸고
     면·선·글자색은 아래 --ck-tone* 세 변수로 받는다(색을 요소마다 다시 적지 않는다) */
  --ck-tone: var(--text-mute);
  --ck-tone-bg: var(--surface-sunken);
  --ck-tone-line: var(--line);
}

.checkup-page .is-ok {
  --ck-tone: var(--ck-ink-ok);
  --ck-tone-bg: var(--grade-normal-bg);
  --ck-tone-line: var(--grade-normal-line);
}

.checkup-page .is-warn {
  --ck-tone: var(--ck-ink-warn);
  --ck-tone-bg: var(--grade-attention-bg);
  --ck-tone-line: var(--grade-attention-line);
}

.checkup-page .is-danger {
  --ck-tone: var(--ck-ink-danger);
  --ck-tone-bg: var(--grade-consult-bg);
  --ck-tone-line: var(--grade-consult-line);
}

/* 판정이 없는 항목(별표 4 에 기준이 없는 참고형)은 **상태색을 쓰지 않는다** — 회색 그대로다.
   다만 --text-mute 는 --surface-sunken 위 3.03:1 이라 레일 기호가 거의 보이지 않았다.
   회색이라는 성격은 그대로 두고 한 단계만 진하게(--text-sub, 같은 면 위 5.34:1) 한다. */
.checkup-page .is-none {
  --ck-tone: var(--text-sub);
  --ck-tone-bg: var(--surface-sunken);
  --ck-tone-line: var(--line);
}

/* --- 상단 뒤로가기 + 제목 (.measure-user 와 같은 규격 — 사용자 앱 안에서 머리줄이 흔들리지 않게) --- */
.checkup-page .topbar-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.checkup-page .back-btn {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: var(--tap-min);
  height: var(--tap-min);
  border-radius: var(--r-md);
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--text-sub);
  font-size: clamp(24px, calc(20.49px + 0.899vw), 32px);
  /* 390px→24px, 1280px→32px */
  font-weight: 700;
  line-height: 1;
}

.checkup-page .back-btn:hover {
  background: var(--blue-soft);
  border-color: var(--blue-line);
  color: var(--blue);
}

.checkup-page .page-heading {
  min-width: 0;
  /* .measure-user .page-heading 과 **같은 식**이다(종전 19.18 → 19.17). 사용자 앱 안에서
     머리줄 글자가 화면마다 0.01px 씩 달라질 이유가 없다 — 한 값으로 붙여 둔다. */
  font-size: clamp(24px, calc(19.17px + 1.236vw), 35px);
  /* 390px→24px, 1280px→35px */
  font-weight: 700;
  letter-spacing: -.045em;
  line-height: 1.2;
  color: var(--text);
}

/* 화면 안내/오류 줄 — 토스트로 흘려보내지 않고 화면에 남긴다(시니어 대상).
   global.css .info-box 에는 적색 변형이 없어 여기서만 만든다(전역을 건드리지 않는다). */
.checkup-page .notice-line {
  align-items: center;
}

.checkup-page .notice-line.is-red {
  background: var(--danger-bg);
  color: #991B1B;
}

.checkup-page .notice-line.is-red .info-icon {
  background: var(--danger);
}

/* 카드 제목 — [2026-09-22] .measure-user .form-heading(2946행)과 **같은 값**으로 내렸다.
   종전 20→28px/800 은 같은 앱의 폼 제목(19→25px/700)보다 한 단계 커서, 화면을 옮길 때마다
   제목 크기가 달라 보였다. 이 클래스가 쓰이는 자리가 곧 카드 제목이다(① 선택 · ② 승인 대기 ·
   ③ 조회중 · ④ 결과). */
.checkup-page .block-title {
  font-size: clamp(19px, calc(16.37px + 0.674vw), 25px);
  /* 390px→19px, 1280px→25px */
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1.3;
  color: var(--text);
}

.checkup-page .block-desc {
  font-size: clamp(14px, calc(12.25px + 0.449vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 600;
  line-height: 1.6;
  color: var(--text-sub);
}

/* 단계 주버튼 / 보조버튼 — 시니어 대상이라 주버튼은 58px 이상.
   폭은 420px 로 묶고 가운데 세운다: 980px 를 가득 채운 버튼은 글자가 한가운데 하나뿐이라
   "누르는 것"처럼 보이지 않고, 넓은 화면에서 시선이 좌우로 흩어진다. */
.checkup-page .step-cta {
  width: min(100%, 420px);
  min-height: 58px;
  align-self: center;
  margin-inline: auto;
}

/* [2026-09-22] 종전에는 여기서 회색 외곽선(--line-strong / --text-sub)으로 덮어썼다.
   측정 입력 화면의 보조 버튼은 **btn-outline-blue**(파란 선·파란 글자)라 두 화면의 보조 버튼이
   서로 다른 색이었다. 이제 마크업이 .btn-outline-blue 를 직접 달고(global 07), 이 규칙은
   **치수만** 잡는다 — 색을 두 곳에서 정의하지 않는다(왕복 편집에서 어느 쪽이 정본인지 헷갈린다). */
.checkup-page .step-sub {
  width: min(100%, 420px);
  min-height: var(--tap-min);
  align-self: center;
  margin-inline: auto;
}


/* --- 흰 카드 · 버튼 줄 (.measure-user 와 같은 레시피) ---
   [왜 카드인가] 측정 입력 화면은 화면의 내용이 **전부** 흰 카드 안에 들어가고, 배경(--bg-app)
   위에 카드가 떠 있다. 검진 화면은 단계표시·인증 타일·안내가 배경 위에 맨몸으로 놓여 있어
   같은 앱의 다른 화면처럼 보였다. 다섯 속성(sp-5 · 1px --line · --r-xl · --surface ·
   --shadow-card)은 .measure-user .form-card(2937행)와 **한 글자도 다르지 않다**. */
.checkup-page .form-card {
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

/* 주·보조 버튼이 함께 서는 줄 — 측정 입력의 .action-row(3124행)와 같은 2열 grid.
   auto-fit 을 쓰는 이유: ④ 결과 화면은 주 버튼(검진 결과 보기)이 없을 때가 있고
   (latest_checkup_date 가 비었을 때), 그때 남은 보조 버튼 하나가 줄 전체를 쓰게 한다.
   240px 는 시니어용 큰 글자 버튼("인증 취소하고 처음부터")이 한 줄로 들어가는 하한이다. */
.checkup-page .action-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  gap: var(--sp-3);
  width: 100%;
  margin-top: var(--sp-5);
}

/* 줄 안에서는 버튼이 칸을 채운다 — .step-cta/.step-sub 가 홀로 설 때 쓰는
   `width: min(100%, 420px)` + 가운데 정렬은 2열 줄에서 칸 안에 또 여백을 만든다.
   align-self 도 되돌린다: center 로 두면 주 버튼(58px)과 보조 버튼(44px)의 높이가 달라
   한 줄에 턱이 생긴다. stretch 면 낮은 쪽이 줄 높이에 맞춰 늘어나 윗변·아랫변이 맞는다. */
.checkup-page .action-row .step-cta,
.checkup-page .action-row .step-sub {
  width: 100%;
  margin-inline: 0;
  align-self: stretch;
}


/* ===== C2. 판정 배지 · 개수 스트립 (3화면 공용) ===== */
.checkup-page .judge-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px var(--sp-3);
  border: 1px solid var(--ck-tone-line);
  border-radius: var(--r-pill);
  background: var(--ck-tone-bg);
  color: var(--ck-tone);
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 800;
  letter-spacing: -.02em;
  line-height: 1.25;
}

.checkup-page .judge-badge.is-lg {
  padding: 8px var(--sp-4);
  font-size: clamp(15px, calc(12.37px + 0.674vw), 21px);
  /* 390px→15px, 1280px→21px */
}

.checkup-page .judge-mark {
  font-weight: 900;
  line-height: 1;
}

/* 개수 스트립 — 질환의심 / 경계 / 정상 세 칸 */
.checkup-page .count-strip {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.checkup-page .count-cell {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 40px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--ck-tone-line);
  border-radius: var(--r-md);
  background: var(--ck-tone-bg);
  color: var(--ck-tone);
  font-size: clamp(13px, calc(11.69px + 0.337vw), 16px);
  /* 390px→13px, 1280px→16px */
  font-weight: 700;
}

.checkup-page .count-mark {
  font-weight: 900;
  line-height: 1;
}

.checkup-page .count-num {
  font-size: clamp(18px, calc(15.37px + 0.674vw), 24px);
  /* 390px→18px, 1280px→24px */
  font-weight: 900;
  line-height: 1;
}


/* ===== C3. 목록 (.checkup-list) ===== */
/* --- 머리부 (.hero-row) — **측정 입력 화면과 같은 구조**다(2856행 .measure-user .hero-row) ---
   [2026-09-22 사용자 지시] 종전에는 큰 흰 카드 하나 안에 그림·제목·설명·버튼을 전부 넣은
   이 화면만의 구성(.checkup-hero)이었다. 같은 앱의 측정 입력은 "왼쪽 그림 | 오른쪽 파란
   안내박스"라 두 화면의 첫인상이 달랐다. 트랙 규칙·정렬·그림 높이 식까지 그대로 옮겼고,
   이 화면에만 있는 것(CTA 버튼·마지막 불러온 때)은 .hero-side 안에 세로로 잇는다.
   그림 자산은 그대로다(layer-10 — 홈 카드와 같아 화면이 이어져 보인다). */
.checkup-list .hero-row {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  align-items: stretch;
  gap: var(--sp-4);
}

/* 높이 식(clamp)과 contain 은 .measure-user .device-art 와 같은 값이다.
   다른 한 줄만 있다 — align-self: center. 측정 화면의 오른쪽 열은 안내박스 하나지만 이 화면은
   안내박스 + CTA 버튼 + 최근 불러온 때 3단이라 늘 오른쪽이 더 길다. stretch 로 두면 그림이
   상한(176px)에 걸린 채 칸 위쪽에 붙어 아래가 빈다. 가운데 세워 두 열의 무게를 맞춘다. */
.checkup-list .device-art {
  display: block;
  align-self: center;
  width: 100%;
  height: 100%;
  min-height: 0;
  max-height: clamp(120px, calc(96.2px + 6.292vw), 176px);
  /* 390px→120px, 1280px→176px */
  object-fit: contain;
}

.checkup-list .hero-side {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  min-width: 0;
}

/* global .info-box.is-blue 에 테두리 한 줄과 세로 가운데 정렬만 더한다 —
   .measure-user .notice-box(2884행)와 같은 두 줄이다 */
.checkup-list .notice-box {
  align-items: center;
  border: 1px solid var(--blue-line);
}

/* 안내박스 제목은 global .info-box .info-title(--blue-dark, 15→19px)을 그대로 쓴다.
   이 화면에서는 h2 가 그 자리에 오므로 글자 크기만 한 단계 올려 제목 값을 지킨다
   (--blue-soft #E8F0FE 위 --blue-dark #1D4ED8 = 5.85:1, AA 통과). */
.checkup-list .notice-box .info-title {
  font-size: clamp(17px, calc(14.37px + 0.674vw), 23px);
  /* 390px→17px, 1280px→23px */
  letter-spacing: -.03em;
  line-height: 1.3;
}

.checkup-list .hero-cta {
  width: 100%;
}

/* [대비] 안내성 글자에 --text-mute(흰 면 3.34:1)를 쓰지 않는다 — 블록 머리 주석 참조 */
.checkup-list .hero-memo {
  font-size: clamp(12.5px, calc(11.4px + 0.281vw), 15px);
  /* 390px→12.5px, 1280px→15px */
  font-weight: 600;
  color: var(--text-sub);
}

/* 검진 1건 카드 */
/* [2026-09-21] 상세 화면이 .dash-card 레시피(sp-5 · --r-xl · --shadow-card)로 바뀌면서
   목록 카드도 같은 다섯 속성으로 맞췄다 — 한 흐름 안에서 라운드가 18/24 로 섞이고 여백이
   위아래만 4px 얕던 것을 없앤다. 트랙 규칙(auto-fill)과 내용은 그대로다. */
.checkup-list .visit-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 340px), 1fr));
  gap: var(--sp-4);
  list-style: none;
  margin: 0;
  padding: 0;
}

.checkup-list .visit-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

.checkup-list .visit-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
}

.checkup-list .visit-date {
  font-size: clamp(21px, calc(17.93px + 0.787vw), 28px);
  /* 390px→21px, 1280px→28px */
  font-weight: 800;
  letter-spacing: -.03em;
  line-height: 1.2;
  color: var(--text);
}

.checkup-list .visit-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px var(--sp-2);
  font-size: clamp(13px, calc(11.69px + 0.337vw), 16px);
  /* 390px→13px, 1280px→16px */
  font-weight: 600;
  color: var(--text-sub);
}

.checkup-list .meta-kind {
  padding: 2px var(--sp-2);
  border-radius: var(--r-sm);
  background: var(--blue-soft);
  color: var(--blue-dark);
  font-weight: 700;
}

.checkup-list .meta-org {
  min-width: 0;
}

/* 출처 — 공단에서 받은 행과 사람이 입력한 행을 한눈에 가른다(계약 §13-6) */
.checkup-list .meta-source {
  padding: 2px var(--sp-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
  color: var(--text-sub);
  font-weight: 700;
}

/* [2026-09-22] 공단 자료 뱃지의 초록을 블루로 바꿨다.
   이 뱃지가 말하는 것은 "어디서 온 자료인가"(출처)이지 "정상인가"(판정)가 아니다. 그런데
   이 블록에서 초록은 --ck-ink-ok, 곧 **판정 정상A** 의 색이다 — 같은 카드 안에서 판정 배지와
   나란히 놓이니 "정상 판정"으로 읽힐 여지가 있었다. 출처는 상태가 아니므로 화면의 주색
   (블루)으로 내린다. 대비: --blue-soft 위 --blue-dark 5.85:1(AA 통과).
   manual(직접 입력) 은 위의 회색 기본값 그대로 — 둘을 가르는 것은 색 하나가 아니라
   **글자**다("국민건강보험공단" / "직접 입력", page.js SOURCE_LABEL). */
.checkup-list .meta-source.is-nhis {
  border-color: var(--blue-line);
  background: var(--blue-soft);
  color: var(--blue-dark);
}

.checkup-list .visit-go {
  width: 100%;
  min-height: 50px;
  margin-top: auto;
}


/* ===== C4. 불러오기 (.checkup-import) ===== */
/* 한 번에 하나씩 결정하는 화면이라 1500px 를 다 쓰지 않는다 — 시선 이동을 줄이려고
   980px 로 묶고 가운데 세운다(폼 성격의 화면에 적용하는 폭 정책). */
.checkup-import {
  max-width: 980px;
}

/* --- 단계 표시 1·2·3 --- */
.checkup-import .flow-steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-2);
  list-style: none;
  margin: 0;
  padding: 0;
}

.checkup-import .flow-step {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--tap-min);
  padding: 0 var(--sp-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--text-sub);
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 700;
  letter-spacing: -.02em;
  text-align: center;
}

.checkup-import .flow-step .step-no {
  display: grid;
  place-items: center;
  position: relative;
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--surface-sunken);
  color: var(--text-sub);
  font-size: clamp(13px, calc(11.69px + 0.337vw), 16px);
  /* 390px→13px, 1280px→16px */
  font-weight: 800;
  line-height: 1;
}

/* 지금 단계 — 색 + 굵기 + 테두리 3중으로 구분한다(색만으로 구분하지 않는다) */
.checkup-import .flow-step.is-now {
  border-color: var(--blue);
  background: var(--blue-soft);
  color: var(--blue-dark);
}

.checkup-import .flow-step.is-now .step-no {
  background: var(--blue);
  color: var(--text-invert);
}

/* 끝난 단계 — [2026-09-22] 종전에는 번호 원이 **초록**(--grade-normal-bg / --ck-ink-ok)이었다.
   이 블록에서 초록은 판정 상태색(정상A)이라 "진행이 끝났다"와 "검사값이 정상이다"가 같은
   색으로 말해졌다. 진행 상태는 화면의 주색(블루)으로 옮기고, 초록은 판정에만 남긴다.
   면(--blue-soft)은 현재 단계(--blue 꽉 찬 원)보다 옅어 둘이 섞이지 않는다. */
.checkup-import .flow-step.is-done {
  border-color: var(--blue-line);
  background: var(--blue-soft);
  color: var(--blue-dark);
}

/* 번호 대신 ✓ — 끝난 단계는 "몇 번째인가"보다 "끝났다"가 중요하다. 숫자를 지우지 않고
   투명하게 만든 뒤 겹쳐 그린다(마크업의 1·2·3 은 그대로 두고 CSS 안에서만 바꾼다).
   이 span 은 aria-hidden 이라 보조기기 읽기에는 영향이 없다. */
.checkup-import .flow-step.is-done .step-no {
  background: var(--blue-soft);
  color: transparent;
}

.checkup-import .flow-step.is-done .step-no::after {
  content: "\2713";
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--blue-dark);
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px */
  font-weight: 900;
  line-height: 1;
}

/* --- ① 인증 수단 선택 --- */
/* 본문은 흰 카드(.form-card, C1) 안에 들어간다 — .auth-pick 은 그 바깥 껍질일 뿐이라
   자체 면·테두리를 갖지 않는다. 카드 안의 세로 리듬만 여기서 잡는다. */
.checkup-import .auth-pick .form-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

/* 제목 바로 아래 설명은 제목에 붙여 한 덩어리로 읽히게 한다(카드 gap 16px → 8px) */
.checkup-import .auth-pick .block-title+.block-desc {
  margin-top: calc(var(--sp-2) - var(--sp-4));
}

.checkup-import .auth-tiles {
  display: grid;
  /* 좁은 화면 1열 · 노트북 2~3열. 타일 자체는 어느 폭에서도 세로 72px 이상이라
     터치 타깃(44px) 규정을 넉넉히 넘긴다. */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: var(--sp-3);
}

.checkup-import .auth-tile {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--sp-3);
  min-height: 76px;
  padding: var(--sp-3) var(--sp-4);
  border: 2px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease, box-shadow .15s ease;
}

.checkup-import .auth-tile:hover {
  border-color: var(--blue-line);
  background: var(--blue-soft);
}

/* 라디오 자체는 숨기되 포커스는 타일로 옮겨 받는다(키보드 조작 가능) */
.checkup-import .auth-tile input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.checkup-import .auth-tile:focus-within {
  box-shadow: 0 0 0 3px var(--focus-ring);
}

/* 고른 상태 — 테두리 + 배경 + ✓ 표시 3중(색 하나로만 알리지 않는다) */
.checkup-import .auth-tile.is-on {
  border-color: var(--blue);
  background: var(--blue-soft);
  box-shadow: var(--shadow-card);
}

/* 약칭 칩 — [2026-09-22 사용자 지시] 브랜드 색 5종을 걷어내고 **블루 한 벌**로 바꿨다.
   [무엇을 잃지 않았나] 종전 주석이 이미 적어 둔 사실이 근거다: "카카오/KB 가 둘 다 노랑,
   PAYCO/PASS 가 둘 다 적색이라 색만으로는 갈리지 않는다 — 타일의 주된 단서는 색이 아니라
   이름 글자다." 색은 처음부터 보조였고, 다섯 계열이 한 화면에 깔린 대가(화면 전체의 색
   언어가 무너진 것)가 그 보조보다 컸다. 구분은 칩 글자(카 / PAY / KB / 삼성 / PASS)와
   이름(.tile-name 18→23px)이 계속 맡는다.
   대비: --blue-soft #E8F0FE 위 --blue-dark #1D4ED8 = 5.85:1 (AA 통과). */
/* 공식 앱 아이콘 (2026-09-22 사용자 지시 — 같은 날 걷어냈던 브랜드 표식을 **이미지로** 되돌림).
   아이콘이 스스로 브랜드 면색(카카오 노랑·KB 노랑·삼성 감청·PASS 적)을 가지므로 여기서
   면색을 깔지 않는다 — 깔면 투명 배경 PNG 뒤로 우리 색이 비친다. 라운드는 타일과 같은 --r-md.
   object-fit: cover — 5장 전부 정사각(카카오·KB·삼성 512² / PAYCO 140² / PASS 914×934)이라
   잘림이 없다. PAYCO 140² 는 52px 표시의 2배 해상도(104px)를 넘으므로 흐려지지 않는다. */
.checkup-import .auth-tile .tile-logo {
  display: block;
  flex: 0 0 auto;
  width: 52px;
  height: 52px;
  border-radius: var(--r-md);
  object-fit: cover;
  background: var(--surface);
}

/* 고른 타일의 칩만 면·글자를 뒤집는다 — 테두리(--blue) · 면(--blue-soft) · ✓ 에 더해
   네 번째 단서다. 색 하나로만 알리지 않는다는 규칙은 그대로다. */
/* 선택됨 — 아이콘 둘레에 파란 링. 타일 테두리(.is-on)·체크(.tile-check)에 더해 세 번째
   단서다. 아이콘 색은 브랜드 것이라 건드리지 않고 **둘레만** 칠한다. */
.checkup-import .auth-tile.is-on .tile-logo {
  box-shadow: 0 0 0 2px var(--blue);
}

.checkup-import .auth-tile .tile-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.checkup-import .auth-tile .tile-name {
  font-size: clamp(18px, calc(15.81px + 0.562vw), 23px);
  /* 390px→18px, 1280px→23px */
  font-weight: 800;
  letter-spacing: -.03em;
  line-height: 1.25;
  color: var(--text);
}

.checkup-import .auth-tile .tile-memo {
  font-size: clamp(12.5px, calc(11.19px + 0.337vw), 15.5px);
  /* 390px→12.5px, 1280px→15.5px */
  font-weight: 600;
  line-height: 1.4;
  color: var(--text-sub);
}

.checkup-import .auth-tile .tile-check {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border: 2px solid var(--line-strong);
  border-radius: 50%;
  background: var(--surface);
  color: transparent;
  font-size: clamp(15px, calc(13.69px + 0.337vw), 18px);
  /* 390px→15px, 1280px→18px */
  font-weight: 900;
  line-height: 1;
}

.checkup-import .auth-tile.is-on .tile-check {
  border-color: var(--blue);
  background: var(--blue);
  color: var(--text-invert);
}

.checkup-import .guide-box {
  align-items: flex-start;
}

/* --- ② 승인 대기 · ③ 조회중 · ④ 결과 (공통 카드) ---
   면·테두리·라운드·그림자는 C1 의 .form-card 가 한 벌로 준다(마크업이 form-card 를 함께 단다).
   여기서는 이 세 화면만의 조판 — 가운데 정렬과 위아래 여유 — 만 더한다. */
.checkup-import .auth-wait,
.checkup-import .auth-loading,
.checkup-import .auth-done {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  padding-block: var(--sp-6);
  text-align: center;
}

.checkup-import .wait-art {
  font-size: clamp(44px, calc(35.24px + 2.247vw), 64px);
  /* 390px→44px, 1280px→64px */
  line-height: 1;
}

.checkup-import .wait-guide {
  max-width: 640px;
  font-size: clamp(15px, calc(12.81px + 0.562vw), 20px);
  /* 390px→15px, 1280px→20px */
  font-weight: 600;
  line-height: 1.6;
  color: var(--text-sub);
}

/* 무엇을 하면 되는지 1·2·3 — 대기 화면에서 사용자가 할 일이 분명해야 한다 */
.checkup-import .wait-howto {
  display: grid;
  gap: var(--sp-2);
  width: min(100%, 560px);
  margin: 0;
  padding: var(--sp-4) var(--sp-4) var(--sp-4) calc(var(--sp-6) + 8px);
  border-radius: var(--r-lg);
  background: var(--surface-sunken);
  text-align: left;
  font-size: clamp(14px, calc(12.25px + 0.449vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 600;
  line-height: 1.6;
  color: var(--text-sub);
}

/* 만료 안내 — 2026-09-22 부터 **남은 시간 카운트다운**이다(종전: "늦어도 10:24 까지").
   대비: --text-mute 는 흰 면 3.34:1 로 AA 미달이고, 이 줄은 이제 매초 바뀌는 수치를 담는
   정보 줄이라 --text-sub(5.88:1)로 올린다 — [checkup] C5-2 에서 각주에 적용한 판단과 같다. */
.checkup-import .wait-expire {
  font-size: clamp(13px, calc(11.47px + 0.393vw), 16.5px);
  /* 390px→13px, 1280px→16.5px */
  font-weight: 600;
  line-height: 1.6;
  color: var(--text-sub);
}

.checkup-import .wait-expire b {
  color: var(--text);
}

/* 남은 시간 숫자. **tabular-nums 가 핵심이다** — 비례폭 숫자는 1초마다 글자폭이 달라져
   뒤따르는 괄호 문구가 좌우로 흔들린다(매초 움직이는 화면은 시니어 대상에서 특히 나쁘다). */
.checkup-import .expire-left {
  font-size: clamp(16px, calc(13.53px + 0.633vw), 21.5px);
  /* 390px→16px, 1280px→21.5px */
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
  color: var(--text);
}

/* 절대 시각은 보조 정보다 — 카운트다운이 주(主)이고 이쪽은 대조용으로만 남긴다 */
.checkup-import .expire-at {
  font-size: clamp(12.5px, calc(11.4px + 0.281vw), 15px);
  /* 390px→12.5px, 1280px→15px */
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-sub);
}

/* 0 에 닿았다 — 색 + 면 + 문장 세 겹으로 알린다(색만으로 구분하지 않는다).
   [checkup] C1 의 경고 톤(--grade-attention-*)을 그대로 쓴다. 새 색을 만들지 않는다. */
.checkup-import .wait-expire.is-over {
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--grade-attention-line);
  border-radius: var(--r-md);
  background: var(--grade-attention-bg);
  /* 같은 리터럴(#A16207)을 두 줄에 다시 적지 않는다 — C1 의 --ck-ink-warn 이 정본이다 */
  color: var(--ck-ink-warn);
}

.checkup-import .wait-expire.is-over b {
  color: var(--ck-ink-warn);
}

/* --- ③ 조회 대기 (최대 1분) --- */
/* 회전 고리 하나 + 경과 시간 + "지금 무슨 일이 일어나는가" 세 줄.
   진행률 막대를 두지 않는다 — 서버가 단계를 알려 주지 않으므로 어떤 막대든 지어낸 값이 된다
   (홈 리포트 진행 모달의 "가짜 진행바 아님" 원칙과 같다). */
.checkup-import .load-spin {
  display: block;
  width: 56px;
  height: 56px;
  border: 6px solid var(--blue-soft);
  border-top-color: var(--blue);
  border-radius: 50%;
  animation: ck-spin 1s linear infinite;
}

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

.checkup-import .load-desc {
  max-width: 620px;
  font-size: clamp(15px, calc(12.81px + 0.562vw), 20px);
  /* 390px→15px, 1280px→20px */
  font-weight: 600;
  line-height: 1.6;
  color: var(--text-sub);
}

.checkup-import .load-elapsed {
  font-size: clamp(16px, calc(13.37px + 0.674vw), 22px);
  /* 390px→16px, 1280px→22px */
  font-weight: 700;
  color: var(--text-sub);
}

.checkup-import .load-elapsed b {
  color: var(--blue-dark);
}

.checkup-import .load-what {
  display: grid;
  gap: 6px;
  width: min(100%, 560px);
  margin: 0;
  padding: var(--sp-4) var(--sp-4) var(--sp-4) calc(var(--sp-6) + 4px);
  border-radius: var(--r-lg);
  background: var(--surface-sunken);
  text-align: left;
  font-size: clamp(13px, calc(11.47px + 0.393vw), 16.5px);
  /* 390px→13px, 1280px→16.5px */
  font-weight: 600;
  line-height: 1.6;
  color: var(--text-sub);
}

/* --- 복원 로딩 탈출구 (2026-09-22) ---
   "진행 중인 인증을 확인하는 중입니다" 로딩이 3초를 넘기면 그 아래에 뜨는 줄.
   로딩 블록(.state-block)과 한 덩어리로 보이도록 가운데 세우고 간격만 준다. */
.checkup-import .restore-wait {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  /* 다른 단계 화면과 같은 흰 카드 안에 있다(마크업이 .form-card 를 함께 단다) */
  padding-block: var(--sp-5);
}

.checkup-import .restore-escape {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  width: 100%;
}

.checkup-import .escape-memo {
  max-width: 560px;
  margin: 0;
  text-align: center;
  font-size: clamp(13px, calc(11.47px + 0.393vw), 16.5px);
  /* 390px→13px, 1280px→16.5px */
  font-weight: 600;
  line-height: 1.6;
  color: var(--text-sub);
}

/* 조회 대기 화면의 [인증 취소하고 처음부터] 를 같은 줄의 [그만두기]보다 한 단계 물러세운다.
   [2026-09-22] 새 버튼 언어를 만들지 않는다는 원칙은 그대로고, 바탕 클래스만 바꿨다:
   마크업이 global .btn-ghost(투명 면 · --text-sub)를 달고, 여기서는 밑줄만 더한다
   ("눌리는 글자"임을 색 말고 하나 더로 알린다). 높이는 .step-sub 의 --tap-min 그대로다. */
.checkup-page .step-sub.is-quiet {
  text-decoration: underline;
  text-underline-offset: 4px;
}

/* --- ④ 결과 --- */
.checkup-import .done-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
}

.checkup-import .done-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
}

.checkup-import .done-icon {
  font-size: clamp(40px, calc(32.99px + 1.798vw), 56px);
  /* 390px→40px, 1280px→56px */
  line-height: 1;
}

/* 성공 표시 — [2026-09-22] 종전 ✅ 이모지는 글꼴이 칠하는 **초록**이라 CSS 로 바꿀 수 없었고,
   이 블록에서 초록은 판정 상태색(정상A)이다. 저장 성공은 판정이 아니므로 화면의 주색으로
   옮긴다: 파란 원 + 흰 ✓. (빈 결과의 📭 는 상태색과 무관한 그림이라 그대로 둔다) */
.checkup-import .done-icon.is-mark {
  display: grid;
  place-items: center;
  width: clamp(56px, calc(46.18px + 2.517vw), 78px);
  height: clamp(56px, calc(46.18px + 2.517vw), 78px);
  /* 원 지름 390px→56px, 1280px→78px */
  border-radius: 50%;
  background: var(--blue);
  color: var(--text-invert);
  font-size: clamp(30px, calc(24.76px + 1.343vw), 42px);
  /* 390px→30px, 1280px→42px */
  font-weight: 900;
}

.checkup-import .done-list {
  display: grid;
  gap: var(--sp-2);
  width: 100%;
  list-style: none;
  margin: 0;
  padding: 0;
}

.checkup-import .done-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2) var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface-soft);
}

.checkup-import .done-row .row-date {
  font-size: clamp(16px, calc(13.81px + 0.562vw), 21px);
  /* 390px→16px, 1280px→21px */
  font-weight: 800;
  letter-spacing: -.02em;
  color: var(--text);
}

.checkup-import .done-row .row-org {
  font-size: clamp(13px, calc(11.69px + 0.337vw), 16px);
  /* 390px→13px, 1280px→16px */
  font-weight: 600;
  color: var(--text-sub);
}

.checkup-import .done-row .row-tag {
  padding: 2px var(--sp-2);
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
  color: var(--text-sub);
  font-size: clamp(12px, calc(10.69px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
  font-weight: 700;
}

.checkup-import .done-row .row-tag.is-new {
  background: var(--blue-soft);
  color: var(--blue-dark);
}

.checkup-import .done-memo {
  font-size: clamp(13px, calc(11.69px + 0.337vw), 16px);
  /* 390px→13px, 1280px→16px */
  font-weight: 600;
  line-height: 1.6;
  color: var(--text-sub);
}

/* 하단 버튼 줄 — 2열 grid·칸 채우기·auto-fit 은 C1 의 .action-row 가 준다(마크업이 함께 단다).
   여기서는 폭 상한만 — 가운데 정렬된 카드 안에서 버튼 줄이 카드 끝까지 벌어지면 성글어 보인다. */
.checkup-import .done-actions {
  width: min(100%, 560px);
}


/* ===== C5. 상세 (.checkup-detail) ===== */
.checkup-detail .detail-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

.checkup-detail .head-main {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.checkup-detail .head-kind {
  align-self: flex-start;
  padding: 2px var(--sp-3);
  border-radius: var(--r-pill);
  background: var(--blue-soft);
  color: var(--blue-dark);
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px */
  font-weight: 700;
}

.checkup-detail .head-date {
  font-size: clamp(24px, calc(19.62px + 1.124vw), 34px);
  /* 390px→24px, 1280px→34px */
  font-weight: 800;
  letter-spacing: -.04em;
  line-height: 1.15;
  color: var(--text);
}

.checkup-detail .head-org {
  font-size: clamp(14px, calc(12.25px + 0.449vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 600;
  color: var(--text-sub);
}

.checkup-detail .head-judge {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  min-width: 0;
}

/* [대비] 옆 배지가 **무엇에 대한 판정인지**를 말하는 유일한 글자다 — 이것이 흐리면 배지가
   떠 있는 숫자가 된다. 13px/700 은 AA large 문턱 밖이라 4.5:1 이 필요한데 --text-mute 는
   흰 면 3.34:1 이었다. --text-sub(5.88:1)로 올린다(.judge-note·.result-foot 과 같은 판단). */
.checkup-detail .judge-key {
  font-size: clamp(13px, calc(11.69px + 0.337vw), 16px);
  /* 390px→13px, 1280px→16px */
  font-weight: 700;
  color: var(--text-sub);
}

/* 계약 §13-7 — 파생 판정(derived)은 "공단이 그렇게 판정했다"가 아니다. 반드시 각주를 붙인다.
   [대비] 이 각주는 "이 판정이 어디서 나왔나"를 밝히는 글이라 읽혀야 한다 — 12.5px/600 은
   AA large 문턱(18.66px bold) 밖이라 4.5:1 이 요구된다. --text-mute 는 흰 면 3.34:1 로
   미달이라 --text-sub(5.88:1)로 올렸다. C5-2 가 단위·참고치에 적용한 것과 같은 판단이다. */
.checkup-detail .judge-note {
  max-width: 320px;
  font-size: clamp(12.5px, calc(11.19px + 0.337vw), 15.5px);
  /* 390px→12.5px, 1280px→15.5px */
  font-weight: 600;
  line-height: 1.45;
  text-align: right;
  color: var(--text-sub);
}

.checkup-detail .detail-memo {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-soft);
  font-size: clamp(13px, calc(11.47px + 0.393vw), 16.5px);
  /* 390px→13px, 1280px→16.5px */
  font-weight: 600;
  line-height: 1.55;
  color: var(--text-sub);
}

.checkup-detail .detail-memo.is-old {
  border-color: var(--grade-attention-line);
  background: var(--grade-attention-bg);
  color: var(--ck-ink-warn);
}

/* --- 항목표 --- */
.checkup-detail .result-body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

/* --- C5-2. 항목 UI — 요약 카드 + 상태 레일 2단 줄 (2026-09-21 개편) ------------
   사용자 지시: "검진 데이터를 보기 편하게 건강검진 항목 UI 를 다른 페이지랑 비슷하게".

   [무엇을 대체했나 — 삭제한 종전 선언]
     .result-group(--r-lg + overflow:hidden + padding 0) / .group-title(--surface-sunken 채운 띠) /
     .group-head,.item-row 4열 fr 그리드 / .group-head 열 제목 줄 / .item-row(min-height 58px +
     왼쪽 inset 4px 액센트) / .item-name / .item-value(한 줄 접합) / .item-judge / .judge-chip(알약) /
     .item-ref(독립 열) — 전부 지웠다. 죽은 선택자를 남기지 않는다.
     C6 의 `@media (max-width:760px)` 항목표 접기도 함께 지웠다(아래 1100px 한 자리로 대체).

   [왜 바꿨나 — 실측]
     · 1366px 참고치 열 348.9px 중 263px 가 상시 공백, 항목명 열도 215px 남았다
     · 768px  "mL/min/1.73m²"(135.8px)가 값 트랙(124.3px)을 넘어 판정 칸으로 삐져나왔다
     · 740→761px 21px 리사이즈에 문서 높이가 2516→1941px, 575px 튀었다
     · 이상 5개가 15줄에 흩어졌다(1366px y=164~960 · 390px y=145~1297)
   폭 비율(fr 4개)이 아니라 **역할**로 나눈다: 레일(상태) | 항목명 | 값·참고치 | 판정.

   [정렬축] 판정은 마지막 트랙이라 **셀의 오른쪽 변이 곧 행의 오른쪽 변**이다 — 고정폭 변수를
   두지 않아도 모든 행의 판정 오른쪽 모서리가 한 x 에 선다. 초안은 `--ck-judge-w` 로 폭을 못
   박았는데, 그 하한의 근거로 삼은 "최장 판정 문구"가 사실이 아니었다(청력이 보내는
   "질환의심(한쪽 이상 포함)"이 더 길다 — checkup_service.ITEMS). 서버 문자열 길이에 CSS 를
   묶는 결합만 남고 얻는 것이 없어 폐기했다. 대신 항목명 트랙에 최소폭(7em)을 줘 긴 판정이
   와도 이름이 굶지 않게 하고, 판정은 어절 단위(keep-all)로만 접힌다.

   [색] 새 색을 한 개도 만들지 않는다. C1(위 --ck-tone / --ck-tone-bg / --ck-tone-line)과
   `.is-ok/.is-warn/.is-danger/.is-none` 한 벌로 면·선·글자를 받는다. 잉크 값이 [dash-v2] 의
   `.mini-card.is-*` 와 ok·warn 은 같은 값이고(#15803D / #A16207), danger 만 다르다 —
   그 블록은 원색 #DC2626 이 자기 면 위에서 4.62:1 로 통과하지만, 이 블록의 옅은 면·행 틴트
   위에서는 3.95~4.35:1 이라 red-700(#B91C1C)으로 내렸다(C1 --ck-ink-danger 주석 참고).
   [카드] .dash-card(6921~6931)와 같은 5속성: sp-5 padding · 1px --line · --r-xl · --surface ·
   --shadow-card. 카드 레시피를 새로 발명하지 않는다.
   [대비] 단위·참고치·각주는 **수치의 뜻 그 자체**라 --text-mute(흰 면 3.34:1, AA 미달)를 쓰지
   않고 --text-sub(5.88:1)로 올렸다. 단위 하한도 13 → 15px 로 올렸다(설계 검토 지적).
   ---------------------------------------------------------------------------- */

.checkup-detail {
  /* 레일 폭 — 기호 하나가 들어가는 최소 트랙. 390px→36px, 1280px→46px */
  --ck-rail-w: clamp(36px, calc(31.62px + 1.124vw), 46px);
}

/* --- 카드 2종(요약·그룹)은 같은 레시피 --- */
.checkup-detail .result-summary,
.checkup-detail .result-group {
  /* 카드 안쪽 여백을 변수로 뽑는 이유: 항목 줄이 카드 좌우 끝까지 흘러야 상태 틴트가
     "띠"로 읽힌다. .item-list 의 음수 마진과 .item-row 의 좌우 패딩이 이 값을 같이 본다 —
     두 곳에 숫자를 따로 적으면 480px 에서 한쪽만 줄어 카드가 어긋난다. */
  --ck-pad: var(--sp-5);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  min-width: 0;
  padding: var(--ck-pad);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

/* 카드 제목 — .dash-card-title(6933~6940)과 같은 식. 채운 회색 띠를 버렸다:
   사용자 앱의 다른 카드는 전부 흰 바탕에 평문 제목이고 검진만 띠를 쓰고 있었다. */
.checkup-detail .summary-title,
.checkup-detail .group-title {
  font-size: clamp(18px, calc(15.37px + 0.674vw), 24px);
  /* 390px→18px, 1280px→24px */
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1.25;
  color: var(--text);
}

/* ===== 요약 카드 "한눈에 보기" =====
   서버가 이미 보내고 있던 §5-9 summary.suspect_items / border_items 를 화면이 처음으로 읽는다
   (api/routers/checkups.py 가 상세 응답에 붙인다). 종전 화면은 개수만 다시 세고 목록을 버렸다. */

/* C2 의 .count-strip 은 flex-wrap 이다(목록 화면이 그 모양을 쓴다). 요약 카드 안에서만
   3등분 그리드로 바꾼다 — 스코프가 .result-summary 라 목록 화면은 그대로다. */
.checkup-detail .result-summary .count-strip {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-2);
}

.checkup-detail .result-summary .count-cell {
  justify-content: center;
  min-height: 56px;
  font-size: clamp(14px, calc(12.25px + 0.449vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 800;
}

.checkup-detail .result-summary .count-num {
  font-size: clamp(22px, calc(18.06px + 1.011vw), 31px);
  /* 390px→22px, 1280px→31px */
}

.checkup-detail .summary-lead {
  font-size: clamp(15px, calc(13.25px + 0.449vw), 19px);
  /* 390px→15px, 1280px→19px */
  font-weight: 700;
  line-height: 1.5;
  color: var(--text-sub);
}

.checkup-detail .summary-lead.is-clear {
  /* 이 한 줄에는 .is-ok 톤 클래스가 걸리지 않아 --ck-tone 이 아니라 잉크 정본을 직접 받는다
     (C1 --ck-ink-ok, 흰 면 5.02:1). 리터럴을 여기 다시 적지 않는다. */
  color: var(--ck-ink-ok);
}

.checkup-detail .flag-rows {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

/* 한 줄 = 한 판정("▲ 질환의심 3개" + 그 항목 칩들).
   [3중 표기의 분담] 색 + 기호 + 낱말은 **줄 머리가 한 번만** 말하고(흰 카드 면 위 --ck-tone,
   4.68~4.86:1), 칩은 항목 이름·값만 담는다. 칩 글자까지 톤색으로 채우면 틴트 위에서
   3.95:1(danger)로 내려간다 — [dash-v2] 7184~7199 가 면과 글자를 나눠 쓴 그 판단이다. */
.checkup-detail .flag-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-2);
}

.checkup-detail .flag-key {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: clamp(15.5px, calc(13.53px + 0.506vw), 20px);
  /* 390px→15.5px, 1280px→20px */
  font-weight: 800;
  letter-spacing: -.02em;
  line-height: 1.3;
  color: var(--ck-tone);
  word-break: keep-all;
}

.checkup-detail .flag-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* 이상 항목 칩 — 누르면 그 행으로 내려간다. 터치 타깃 44px(--tap-min) 하한 준수.
   알약 어휘는 C2 .judge-badge / global .chip 과 같다(틴트 면 + 1px 선 + 700~800). */
.checkup-detail .flag-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: var(--tap-min);
  padding: 8px var(--sp-4);
  border: 1px solid var(--ck-tone-line);
  border-radius: var(--r-pill);
  background: var(--ck-tone-bg);
  color: var(--text);
  font-size: clamp(14px, calc(12.25px + 0.449vw), 18px);
  /* 390px→14px, 1280px→18px */
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.3;
  word-break: keep-all;
}

.checkup-detail .flag-chip:hover {
  border-color: var(--ck-tone);
}

.checkup-detail .flag-name {
  min-width: 0;
  word-break: keep-all;
}

/* 이름은 700, 값은 800 — 흐리기(opacity)가 아니라 굵기로 위계를 만든다
   (report.css .ck-chip 5241~5245 의 판단과 같다) */
.checkup-detail .flag-value {
  display: inline-block;
  max-width: 12em;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  /* 흉부방사선 display 는 기관 자유기재 소견(chest_xray_text, 최대 120자)이 그대로 실린다.
     자르지 않으면 칩 하나가 1366px 에서 1040px 까지 자라 칩 줄을 통째로 무너뜨린다(실측).
     전문은 아래 항목 줄이 그대로 보여 주고, 칩은 "어느 항목인가"만 말하면 된다. */
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

.checkup-detail .flag-unit {
  margin-left: 2px;
  font-size: clamp(13px, calc(11.69px + 0.337vw), 16px);
  /* 390px→13px, 1280px→16px */
  font-weight: 700;
  color: var(--text-sub);
  white-space: nowrap;
}

.checkup-detail .summary-note {
  font-size: clamp(13px, calc(11.25px + 0.449vw), 17px);
  /* 390px→13px, 1280px→17px — .dash-note(8155~8161)와 같은 식 */
  font-weight: 600;
  line-height: 1.55;
  color: var(--text-sub);
}

/* ===== 그룹 카드 ===== */
.checkup-detail .group-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2) var(--sp-3);
}

.checkup-detail .group-count {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-3);
}

/* 그룹 안에 무엇이 걸렸는지 — 정상만 있는 그룹에는 아무것도 그리지 않는다.
   글자는 흰 카드 면 위 --ck-tone(4.68~4.86:1)이다. 틴트 면을 깔지 않는 이유는 작은 글자에
   틴트를 깔면 danger 조합이 3.95:1 로 내려가기 때문이다(요약 칩과 같은 판단). */
.checkup-detail .gcount {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: clamp(13px, calc(11.47px + 0.393vw), 16.5px);
  /* 390px→13px, 1280px→16.5px */
  font-weight: 800;
  letter-spacing: -.02em;
  line-height: 1.3;
  color: var(--ck-tone);
  word-break: keep-all;
}

/* ===== 항목 목록 ===== */
/* 음수 마진으로 카드 좌우 끝까지 흘린다 — 구분선과 상태 틴트가 카드 폭 전체를 쓴다.
   (.dash-guide 8338~8352 의 "gap 0 + 행마다 세로 padding + 행 사이 border-top" 계열) */
.checkup-detail .item-list {
  list-style: none;
  margin: 0 calc(var(--ck-pad) * -1);
  padding: 0;
}

.checkup-detail .item-row {
  display: grid;
  /* 폭 비율이 아니라 역할로 나눈 3트랙(≥1100px 은 아래에서 4트랙 한 줄로 편다).
     항목명 트랙의 최소폭 7em 이 유일한 방어값이다 — 판정(auto)이 길어져도 이름이 굶지 않는다.
     판정 트랙은 minmax(0, auto) 라 필요하면 스스로 줄어들며 어절 단위로 접힌다. */
  grid-template-columns: var(--ck-rail-w) minmax(7em, 1fr) minmax(0, auto);
  grid-template-areas:
    "rail head  judge"
    "rail facts facts"
    "rail note  note";
  align-items: center;
  /* row-gap 0 + 조각마다 margin-top — [dash-v2] .mini-card(7124~7135)와 같은 처리.
     각주가 없는 행에서도 note 트랙의 거터가 붙어(종전 390px 에서 13행 × 8px = 104px)
     빈 줄을 벌리던 것을 막는다. */
  row-gap: 0;
  column-gap: var(--sp-3);
  padding: var(--sp-3) var(--ck-pad);
  border-top: 1px solid var(--line);
  scroll-margin-block: var(--sp-5);
}

/* 이상 항목만 아주 옅게 깐다 — 레일(100% 틴트)과 두 단계 차이라 행이 통째로 읽힌다.
   정상 행에는 깔지 않는다(8개가 색면이면 이상 3개가 묻힌다).
   ※ color-mix 를 모르는 렌더러에서는 이 선언만 무시되고 레일·기호·판정 글자는 그대로 남는다
     (3중 표기가 보험이다). 같은 함수를 .mini-ico(7258~7271)가 이미 쓰고 있다. */
.checkup-detail .item-row.is-danger,
.checkup-detail .item-row.is-warn {
  background: color-mix(in srgb, var(--ck-tone-bg) 50%, var(--surface));
}

/* 요약 카드의 칩에서 내려왔을 때 어디에 도착했는지 알려 준다.
   --focus-ring 은 포커스 전용 토큰이라 쓰지 않는다(비포커스 상태에 전용하면 뜻이 흐려진다). */
.checkup-detail .item-row:target {
  outline: 2px solid var(--blue);
  outline-offset: -2px;
}

.checkup-detail .item-rail {
  grid-area: rail;
  display: grid;
  place-items: center;
  align-self: stretch;
  border-radius: var(--r-sm);
  background: var(--ck-tone-bg);
  color: var(--ck-tone);
  font-size: clamp(17px, calc(14.81px + 0.562vw), 22px);
  /* 390px→17px, 1280px→22px */
  font-weight: 900;
  line-height: 1;
}

.checkup-detail .item-head {
  grid-area: head;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px var(--sp-2);
  min-width: 0;
}

.checkup-detail .name-text {
  font-size: clamp(16px, calc(13.81px + 0.562vw), 21px);
  /* 390px→16px, 1280px→21px — 판정(16.5~22px)보다 작게.
     종전은 라벨 20px > 판정 16.5px 으로 위계가 뒤집혀 있었다(시니어 화면에서 "질환의심"이
     검사 이름보다 작았다). */
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.3;
  color: var(--text);
  word-break: keep-all;
}

/* 목표질환(당뇨병·고혈압…) — 무엇을 보려고 하는 검사인지 알려 주는 보조 표시 */
.checkup-detail .name-target {
  padding: 1px 7px;
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
  color: var(--text-sub);
  font-size: clamp(12px, calc(10.69px + 0.337vw), 15px);
  /* 390px→12px, 1280px→15px */
  font-weight: 700;
  white-space: nowrap;
}

.checkup-detail .item-facts {
  grid-area: facts;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px var(--sp-3);
  min-width: 0;
  margin-top: 5px;
}

.checkup-detail .item-value {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  min-width: 0;
}

/* 수치값(is-num) — numeric · bp. 큰 숫자 + 작은 단위는 [dash-v2] .mini-value/.mini-unit
   (7299~7322, 2026-08-27 사용자 지시)과 같은 어휘다. 단, 여기는 행 안이라 세로 스택이
   아니라 인라인으로 붙인다(.change-side-value·.count-num 쪽 계열). */
.checkup-detail .value-num {
  font-size: clamp(22px, calc(18.06px + 1.011vw), 31px);
  /* 390px→22px, 1280px→31px */
  font-weight: 800;
  letter-spacing: -.035em;
  line-height: 1.2;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* 글자값(is-word — "음성(-)"·"정상 / 정상"·흉부 소견 원문)과 참고형(is-ref —
   "186.2cm / 130.5kg"). 숫자가 아닌 것을 31px/800 로 찍으면 단어가 비명처럼 보이고,
   단위 내장 문자열은 이미 길다. 한 급 낮춘다 — 값의 뜻은 그대로이고 조판만 다르다.
   기관 자유기재 소견이 실릴 수 있어 어절 단위 줄바꿈을 켠다(한 어절이 트랙보다 길면
   그때만 글자 단위로 끊는다 — 한글은 기본이 음절 단위라 그냥 두면 아무 데서나 끊긴다). */
.checkup-detail .item-value.is-word .value-num,
.checkup-detail .item-value.is-ref .value-num {
  font-size: clamp(17px, calc(14.59px + 0.618vw), 22.5px);
  /* 390px→17px, 1280px→22.5px */
  font-weight: 700;
  letter-spacing: -.02em;
  word-break: keep-all;
  overflow-wrap: anywhere;
}

.checkup-detail .value-unit {
  font-size: clamp(15px, calc(13.69px + 0.337vw), 18px);
  /* 390px→15px, 1280px→18px — 단위는 "숫자의 뜻" 그 자체라 --mini-unit(13~17px)보다 올렸고
     색도 --text-mute(3.34:1) 가 아니라 --text-sub(5.88:1)로 둔다(설계 검토 지적). */
  font-weight: 700;
  line-height: 1.3;
  color: var(--text-sub);
  /* "mL/min/1.73m²" 가 슬래시에서 끊기지 않게 */
  white-space: nowrap;
}

/* 참고치 — 값의 캡션. 전용 열(종전 1.25fr)을 없앴다 */
.checkup-detail .item-ref {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  font-size: clamp(13px, calc(11.69px + 0.337vw), 16px);
  /* 390px→13px, 1280px→16px */
  font-weight: 600;
  line-height: 1.45;
  color: var(--text-sub);
  /* 성별 미상 회원은 "남 13.0~16.5 · 여 12.0~15.5" 처럼 두 기준이 병기돼 온다 —
     nowrap 으로 묶으면 360px 에서 트랙을 넘긴다. 어절 단위로만 접히게 둔다. */
  word-break: keep-all;
}

/* "참고치" 라는 말머리는 작은 칩으로 — 값 뒤에 회색 글자만 이어 붙이면 값의 일부로 읽힌다 */
.checkup-detail .ref-key {
  padding: 1px 6px;
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
  color: var(--text-sub);
  font-weight: 700;
  white-space: nowrap;
}

/* 판정 — 이 행에서 가장 크게 읽혀야 하는 글자. 알약(종전 .judge-chip 13~16.5px)을 버리고
   [dash-v2] .mini-state(7351~7361)와 **같은 clamp 식**의 큰 색글자로 간다.
   [크기를 셀에 준다] 기호 `.judge-mark` 는 C2 공용 클래스라 font-weight·line-height 만 갖고
   크기는 부모에서 상속한다. 낱말에만 clamp 를 주면 기호만 본문 크기로 남아 한 덩어리로
   안 읽힌다 — 그래서 셀이 크기와 톤색을 지고 낱말은 굵기·자간만 갖는다. */
.checkup-detail .item-judge {
  grid-area: judge;
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 5px;
  min-width: 0;
  text-align: right;
  font-size: clamp(16.5px, calc(14.09px + 0.618vw), 22px);
  /* 390px→16.5px, 1280px→22px */
  color: var(--ck-tone);
}

/* ※ 선택자에 `.item-judge` 를 반드시 끼운다 — `.judge-text` 는 C2 의 JudgeBadge 가 쓰는
     공용 클래스라, 스코프를 빼면 상세 머리부의 종합판정 배지(is-lg, 15~21px)까지 덮어
     한 알약 안에서 기호와 낱말 크기가 어긋난다. */
.checkup-detail .item-judge .judge-text {
  font-weight: 800;
  letter-spacing: -.02em;
  line-height: 1.3;
  word-break: keep-all;
}

.checkup-detail .item-note {
  grid-area: note;
  margin-top: var(--sp-2);
  font-size: clamp(13px, calc(11.69px + 0.337vw), 16px);
  /* 390px→13px, 1280px→16px */
  font-weight: 600;
  line-height: 1.45;
  color: var(--text-sub);
  word-break: keep-all;
}

/* 참고형 각주는 값 줄에 인라인으로 붙는다(제 줄을 갖지 않는다) — 시력 행만 81px 로
   튀어 59px 리듬을 깨던 자리다. grid-area 를 auto 로 돌려 .item-facts 의 flex 자식이 된다. */
.checkup-detail .item-note.is-inline {
  grid-area: auto;
  margin-top: 0;
}

/* --- 참고형(state "none") — 신장/체중 · 시력 ---
   [별표 4]에 판정기준이 없는 항목이다. 숨기지 않는다(사용자의 기록이다). 다만
   판정 항목과 같은 무게로 두지도 않는다: 레일 무채색 · 이름 600 · 값 한 급 작게(is-ref) ·
   판정 자리는 회색 작은 글자 · 상하 패딩 절반. 줄은 남되 시선은 지나간다. */
.checkup-detail .item-row.is-none {
  padding-block: var(--sp-2);
}

.checkup-detail .item-row.is-none .item-rail {
  font-size: clamp(14px, calc(12.69px + 0.337vw), 17px);
  /* 390px→14px, 1280px→17px — 기호도 한 급 작게 */
}

.checkup-detail .item-row.is-none .name-text {
  font-weight: 600;
  color: var(--text-sub);
}

.checkup-detail .item-row.is-none .item-facts {
  margin-top: 2px;
}

.checkup-detail .item-row.is-none .item-judge {
  font-size: clamp(13px, calc(11.47px + 0.393vw), 16.5px);
  /* 390px→13px, 1280px→16.5px — "판정 없음"은 판정이 아니라 사실 진술이라 톤색을 쓰지 않는다 */
  color: var(--text-sub);
}

.checkup-detail .item-row.is-none .item-judge .judge-text {
  font-weight: 700;
}

/* [대비] 카드 밖 --bg-app(#f0f6fd) 위라 흰 면보다 조건이 나쁘다 — --text-mute 는 여기서
   3.07:1 이었다(12.5px/600 은 AA large 밖이라 4.5:1 요구). --text-sub 로 올려 5.40:1.
   강조(b)는 본문과 같은 색이 되면 안 되므로 한 단계 더 올려 --text 로 둔다. */
.checkup-detail .result-foot {
  padding: 0 var(--sp-1);
  font-size: clamp(12.5px, calc(11.19px + 0.337vw), 15.5px);
  /* 390px→12.5px, 1280px→15.5px */
  font-weight: 600;
  line-height: 1.6;
  color: var(--text-sub);
}

.checkup-detail .result-foot b {
  color: var(--text);
}

/* --- 검진기관 소견 --- */
.checkup-detail .detail-opinion {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
  gap: var(--sp-3);
}

.checkup-detail .opinion-block {
  /* [2026-09-21] 요약·그룹 카드와 같은 레시피로 — 한 화면에 라운드 18/24 가 섞이지 않게 */
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

.checkup-detail .opinion-title {
  margin-bottom: var(--sp-2);
  font-size: clamp(16px, calc(13.81px + 0.562vw), 21px);
  /* 390px→16px, 1280px→21px */
  font-weight: 800;
  letter-spacing: -.03em;
  color: var(--text);
}

.checkup-detail .opinion-text {
  font-size: clamp(14.5px, calc(12.53px + 0.506vw), 19px);
  /* 390px→14.5px, 1280px→19px */
  font-weight: 600;
  line-height: 1.7;
  color: var(--text-sub);
  white-space: pre-line;
}

.checkup-detail .detail-actions {
  display: flex;
  justify-content: center;
}

.checkup-detail .detail-actions .step-sub {
  width: min(100%, 320px);
}


/* ===== C6. 반응형 ===== */
/* 태블릿 — 상세 머리부의 판정을 왼쪽 정렬로 내린다(오른쪽 정렬이 줄바꿈과 겹쳐 읽기 나쁘다) */
@media (max-width: 900px) {
  .checkup-detail .detail-head {
    flex-direction: column;
    align-items: flex-start;
  }

  .checkup-detail .head-judge {
    align-items: flex-start;
  }

  .checkup-detail .judge-note {
    max-width: none;
    text-align: left;
  }
}

/* 항목 줄 — 넓은 화면에서는 항목명과 값을 **한 줄로 편다**(4트랙).
   [브레이크포인트가 왜 1100px 인가] 한 줄 조판의 실제 하한이다 — 항목명 250 + 값·참고치 340 +
   레일 46 + 판정 230(최장 "질환의심(한쪽 이상 포함)") + 거터 36 ≈ 902px(카드 안쪽).
   1100px 에서 카드 안쪽이 1028px 라 126px 여유. 그 아래는 2단으로 접는다.
   **이 파일에 이미 있는 분기값**이다([measure-3col] 4506행이 같은 1100px 를 쓴다) —
   새 사다리를 만들지 않았다. 종전 C6 의 `@media (max-width:760px)` 항목표 접기는 지웠다:
   740→761px 21px 리사이즈에 문서 높이가 575px 튀던 절벽의 원인이었고, 새 기본 레이아웃(3트랙)이
   390~1099px 전 구간을 한 벌로 덮는다(폭이 바뀌어도 배치가 바뀌지 않으니 절벽이 없다). */
@media (min-width: 1100px) {

  /* 폴백(subgrid 미지원 브라우저) — 행마다 자기 트랙을 잡는다. 아래 @supports 가 덮어쓴다. */
  .checkup-detail .item-row {
    grid-template-columns: var(--ck-rail-w) minmax(7em, 1fr) minmax(0, 1.15fr) minmax(0, auto);
    grid-template-areas:
      "rail head facts judge"
      "rail note note  note";
  }

  .checkup-detail .item-facts,
  .checkup-detail .item-row.is-none .item-facts {
    margin-top: 0;
  }

  /* ★ 열을 **목록 단위로 공유**한다 (2026-09-22 사용자 지적 "숫자와 글자가 삐쭉삐죽").
     [왜 어긋났나] 각 행(li)이 독립된 grid 라 `1fr`·`auto` 트랙이 **행마다 따로** 계산됐다.
     판정 글자가 "정상A"(짧다)인 행과 "질환의심(복부비만)"(길다)인 행은 판정 트랙 폭이 다르고,
     그만큼 앞의 fr 트랙들이 밀려 **값 열의 시작 x 가 행마다 달랐다.** 세로로 훑으면 숫자가
     지그재그로 보인다. 행 안에서 아무리 정렬을 만져도 고쳐지지 않는 종류의 문제다 —
     트랙 자체를 목록 하나가 소유해야 한다. subgrid 가 정확히 그 장치다.
     [판정 열은 왼쪽 정렬로] 열 폭이 목록 전체에서 하나가 됐으니, 오른쪽 끝에 붙이던 판정도
     왼쪽 정렬로 바꾼다 — ✓ △ ▲ 기호가 한 수직선에 서고 낱말이 같은 x 에서 시작한다.
     (폴백 레이아웃은 열 왼쪽 변이 행마다 달라 오른쪽 정렬이 그나마 낫다 — 그대로 둔다.)
     [지원] Chrome 117+ · Safari 16+ · Firefox 71+. 그 밖은 위 폴백으로 동작한다. */
  @supports (grid-template-columns: subgrid) {
    /* [5열 — 값과 참고치를 갈라 세운다 · 2026-09-22 2차 지적 "세로 중간 단어들이 삐뚤"]
       열을 공유한 뒤에도 남던 어긋남: 값 열 하나에 "숫자+단위"와 "참고치"가 같이 들어 있어
       혈색소 행은 참고치가 숫자 옆에, 신사구체여과율 행(단위 mL/min/1.73m² 가 길다)은 참고치가
       **아래 줄로** 접혔다. 같은 열인데 행마다 두 번째 낱말의 자리가 달라 지그재그였다.
       그래서 값(value)과 참고치(ref)를 별도 트랙으로 나눈다 — 이제 표다:
         기호 | 항목 | 값·단위 | 참고치 | 판정
       값 트랙은 max-content — 목록에서 가장 긴 값("186.2cm / 130.5kg" 또는 "114 mL/min/1.73m²")
       하나가 폭을 정하고 모든 숫자가 같은 x 에서 시작한다. .item-facts 는 display: contents 로
       껍데기만 벗긴다(마크업 그대로) — 그 안의 두 span 이 행의 grid item 이 된다. */
    .checkup-detail .item-list {
      display: grid;
      grid-template-columns:
        var(--ck-rail-w) minmax(9em, 1fr) max-content minmax(0, .9fr) auto;
      column-gap: var(--sp-3);
    }

    .checkup-detail .item-row {
      grid-column: 1 / -1;
      grid-template-columns: subgrid;
      grid-template-areas:
        "rail head value ref  judge"
        "rail note note  note note";
      /* 글자 크기가 다른 셀(값 31px · 판정 22px · 참고치 16px · 이름 21px)을 가운데 맞추면
         같은 줄인데 위아래가 들쭉날쭉해 보인다 — **바닥선(baseline)** 으로 맞춘다. 표의 한 행이
         한 줄 글처럼 읽힌다. 레일은 align-self: stretch 가 따로 있어 영향 없다. */
      align-items: baseline;
    }

    .checkup-detail .item-facts {
      display: contents;
    }

    .checkup-detail .item-value {
      grid-area: value;
    }

    .checkup-detail .item-ref,
    .checkup-detail .item-note.is-inline {
      grid-area: ref;
    }

    .checkup-detail .item-judge {
      justify-content: flex-start;
      text-align: left;
    }
  }
}

/* 좁은 화면 — .card·.dash-card 가 padding 을 sp-4 로 줄이는 지점(global 547 · main 8469)과 같은 480px */
@media (max-width: 480px) {

  .checkup-detail .result-summary,
  .checkup-detail .result-group {
    --ck-pad: var(--sp-4);
  }

  .checkup-detail .result-summary .count-strip {
    gap: 6px;
  }

  /* 세 칸이 한 줄에 다 안 들어간다 — 기호/이름/숫자를 세로로 쌓는다 */
  .checkup-detail .result-summary .count-cell {
    flex-direction: column;
    gap: 2px;
    padding: 6px 4px;
  }

  /* 칩 5개가 폭이 제각각인 채 한 줄씩 차지해 너덜거렸다(실측). 한 칸짜리 목록으로 세우고
     값을 오른쪽 끝으로 민다 — "무엇이 / 얼마" 두 열로 읽힌다. 44px 타깃은 그대로. */
  .checkup-detail .flag-list {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
  }

  .checkup-detail .flag-chip {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    border-radius: var(--r-md);
  }

  .checkup-detail .flag-value {
    margin-left: auto;
    max-width: 9em;
  }
}

/* 목록 머리부 — 그림 위, 안내/버튼 아래로 쌓는다.
   [브레이크포인트가 왜 759px 인가] **측정 입력 화면과 같은 값이다**(main.css 3300행
   `@media (max-width: 759px)` — .measure-user .hero-row 를 1열로 접는 그 자리).
   같은 구조를 옮겨 왔으니 접히는 지점도 같아야 한다. 종전 값(560px)은 이 화면만의
   사다리였고, 560~759px 구간에서 두 화면이 서로 다르게 접혔다. */
@media (max-width: 759px) {
  .checkup-list .hero-row {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-3);
  }

  .checkup-page .form-card {
    padding: var(--sp-4);
    border-radius: var(--r-lg);
  }

  /* 버튼 줄도 1열로 — .measure-user .action-row 와 같다 */
  .checkup-page .action-row {
    grid-template-columns: 1fr;
  }
}

/* 모바일 — 불러오기 화면의 단계표시·타일 압축 */
@media (max-width: 560px) {
  .checkup-import .flow-steps {
    gap: 4px;
  }

  .checkup-import .flow-step {
    flex-direction: column;
    gap: 2px;
    padding: 6px 4px;
    line-height: 1.2;
  }

  /* [2026-09-22] 카드 여백은 .form-card 한 곳에서만 준다(C1 기본 sp-5 · C6 759px 에서 sp-4).
     여기서 다시 `padding: sp-5 sp-4` 를 적으면 561~759px 보다 ≤560px 이 **더 넓어지는**
     역전이 생긴다 — 좁아질수록 여백이 줄어야 한다. */

  .checkup-import .auth-tile {
    gap: var(--sp-2);
    padding: var(--sp-3);
  }

  .checkup-import .auth-tile .tile-logo {
    width: 46px;
    height: 46px;
  }
}


/* ===== C7. 인쇄 — 상세 화면 =====
   [대상인지 먼저 확인했다] 상세 화면에는 인쇄 버튼이 없다(page.js 의 하단 버튼은 "검진 목록으로"
   하나뿐). 그러나 Ctrl+P 는 언제나 가능하고, 그때 걸리는 것은 global.css 14(앱 크롬 제거 ·
   .app-main padding 0)뿐이라 [checkup] 전용 지면 규칙이 한 줄도 없었다. 검진 결과를 종이로
   뽑아 병원에 들고 가는 동선이 실제로 있다.
   [지면 규격을 새로 만들지 않는다] report.css 의 @page(상세리포트 A4)와 main.css 15-1 의
   change-page 는 각자 정본이고, 이 화면은 **이름 없는 기본 지면**을 그대로 쓴다
   (이름을 붙이면 Chromium 이 이름이 바뀌는 자리에 빈 장을 넣는다 — 15-1 주석 참조).
   A4 세로 기본 여백의 본문 폭은 약 718px 라 위 1100px 분기가 걸리지 않아 2단으로 찍힌다.
   그게 맞다 — 지면에서 한 줄로 펴면 항목명이 두 줄로 접힌다.
   [색] 기본값(economy)은 배경색을 통째로 버려 레일과 상태 틴트가 사라진다. exact 로 살린다.
   ※ 색이 빠져도 기호(▲△✓)와 글자(질환의심·경계·정상)는 남으므로 흑백 인쇄에서도 판정이
     읽힌다 — 3중 표기의 본래 목적이다.
   [순서 계약] 이 블록은 [checkup] 의 **맨 끝**에 있어야 한다. 아래 .detail-actions 숨김은
   C5 의 `.checkup-detail .detail-actions { display: flex }` 와 특정도가 같아(0,2,0)
   **뒤에 오기 때문에** 이긴다. 블록을 앞으로 옮기면 종이에 누를 수 없는 버튼이 찍힌다. */
@media print {

  .checkup-detail {
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
  }

  .checkup-detail .result-summary,
  .checkup-detail .detail-head,
  .checkup-detail .opinion-block {
    box-shadow: none;
    break-inside: avoid;
    page-break-inside: avoid;
  }

  /* 그룹 카드는 장 경계에서 쪼개져도 된다 — 아래 .item-row 가 이미 avoid 라 한 항목의
     값과 판정이 두 장으로 갈리는 일은 그대로 막힌다. 카드 통째 avoid 는 혈액검사 카드(801px)를
     다음 장으로 통째 밀어 앞 장 바닥에 399px 를 비웠다(A4 실측, 본문 총 2525~2798px 가 4쪽).
     항목 수에 비례해 자라는 상자라 지질검사가 실시된 해에는 가용 지면(1046px)을 넘겨
     어차피 강제 분할되고, 그때는 앞 장 공백만 더 커진다. 대신 제목만 홀로 남는 것을 막는다. */
  .checkup-detail .result-group {
    box-shadow: none;
  }

  .checkup-detail .group-bar {
    break-after: avoid;
    page-break-after: avoid;
  }

  /* 한 항목이 장 경계에서 반으로 잘리지 않게 — 값과 판정이 다른 장으로 갈라지면 오독이 된다 */
  .checkup-detail .item-row,
  .checkup-detail .flag-row {
    break-inside: avoid;
    page-break-inside: avoid;
  }

  /* 화면 전용 터치 타깃 — 지면에서는 빈 공간만 만든다 */
  .checkup-detail .flag-chip {
    min-height: 0;
  }

  /* 누를 수 없는 것은 찍지 않는다 */
  .checkup-detail .back-btn,
  .checkup-detail .detail-actions {
    display: none;
  }
}
