/*
 * 시니어 특화 공통 스타일 — 셸(지역칩바 · .page-content · 하단 5탭)과 스타일 토큰까지.
 * 정본: docs/UI_GUIDE.md 「색상」·「타이포그래피」·「컴포넌트」·「하단 내비게이션」·「상단 지역칩바」
 *       docs/context/CURRENT_DECISIONS.md UI-1 ~ UI-28
 *
 * 🚫 흐림 효과(UI_GUIDE 「AI 슬롭 안티패턴」) · 글자를 오려내는 배경 처리 · 배경 장식 · 보라/인디고 금지.
 * 🚫 색상은 아래 :root 토큰 밖으로 나가지 않는다 — frontend/test/shell.test.js가 허용 목록 25색으로 대조한다.
 * 🚫 폰트 파일 선언 블록도 CDN 참조도 두지 않는다 — woff2 3종이 저장소에 없다(ADR-093 결정 4 / OD-026).
 */

:root {
    /* 배경·테두리 */
    --color-bg: #FFFFFF;
    --color-card: #F5F5F5;
    --color-border: #E0E0E0;
    --color-border-soft: #CCCCCC;

    /* 텍스트 — 저대비(회색 6자리 9 계열 이하)는 팔레트에 존재하지 않는다(UI-1) */
    --color-text: #111111;
    --color-body: #333333;
    --color-sub: #444444;
    --color-tab-label: #555555;
    --color-disabled: #757575;
    --color-toast-bg: #323232;
    /* 팝업 오버레이 뒤를 어둡게 덮는 값 — UI_GUIDE:450이 정한 검정 50% 투명을 8자리 16진수로
       적었다(0x80 = 128 ≒ 0.5). 🚨 함수형 색 표기로 되돌리지 마라: shell.test.js의 색상 허용
       목록 대조가 16진수만 훑으므로, 그 표기를 열면 팔레트 밖 색이 목록을 우회해 새는 문이
       생긴다(저대비·보라 금지가 무력해진다). */
    --color-overlay-dim: #00000080;

    /* 시맨틱 액션 */
    --color-primary: #1A73E8;
    --color-danger: #E53935;
    --color-new: #2E7D32;
    --color-call: #1A8A4D;

    /* 태그·배지 (배경/글자 쌍) */
    --color-tag-category-bg: #E3F2FD;
    --color-tag-new-bg: #E8F5E9;
    --color-tag-urgent-bg: #FFEBEE;
    --color-status-upcoming-bg: #E6F0FF;
    --color-status-upcoming-fg: #1655BF;
    --color-status-open-bg: #E5F4EA;
    --color-status-open-fg: #1A7A3C;
    --color-status-always-bg: #E0F4F0;
    --color-status-always-fg: #0E766B;
    --color-status-closed-bg: #ECEEF1;
    --color-status-closed-fg: #4A525C;

    /* ── 비례 배율 (ADR-107 결정 1·2·3·4) ──────────────────────────
       --u  = 「비례된 1px」. 기준 폭 360px · 껍데기 상한 480px에서 멈춘다(1 ~ 1.333배).
              고정 틀(칩바·탭바·검색 칩바·토스트)까지 전부 이 배율을 쓴다.
       --us = 합성 배율. 본문 밖에서는 --u와 같고, .page-content 안에서만 글자 3단이 곱해진다.
       🚫 여기서 치수 토큰에 배율을 미리 곱하지 마라 — CSS는 사용자 정의 값을 선언된 그 자리에서
          한 번 계산해 물려주므로, 미리 곱하면 그 자리의 배율(1.0)로 값이 굳어 글자 3단이 죽는다. */
    --vw-clamped: clamp(360px, 100vw, 480px);
    --u: calc(var(--vw-clamped) / 360);
    --us: var(--u);

    /* 타이포그래피 (UI_GUIDE 「타이포그래피」)
       🚨 단위 없는 숫자다 — 곱셈은 쓰는 자리에서 한다(ADR-107 결정 3).
       예: font-size: calc(var(--font-size-base) * var(--us)); */
    --font-size-base: 20;
    --font-size-card-title: 22;
    --font-size-sub: 18;

    /* 고정 크롬 높이 — 이 두 값은 마음대로 바꾸지 마라(UI-21 · 확정값 2026-08-18).
       🚨 기준 폭 360px에서의 값이며 폭에 비례한다(ADR-107). */
    --region-bar-height: 64;
    --bottom-nav-height: 80;
    --touch-target: 56;
}

/* 상자 크기를 테두리 포함으로 전역 계산한다(ADR-107 결정 6).
   🚨 문서가 적은 숫자와 실제로 그려지는 숫자를 같게 만드는 줄이다 — 착수 전에는 입력창 56→60 ·
      칩바 64→66 · 탭바 80→82로 어긋나 있었다(<button>만 브라우저 기본이 테두리 포함이었다).
   🚫 낱개 box-sizing 선언을 되살리지 마라 — 하나만 남아도 "여기만 특별히 다르다"로 읽힌다. */
*,
*::before,
*::after {
    box-sizing: border-box;
}

/* ───────────────────────── 기본 ───────────────────────── */

html,
body {
    height: 100%;
}

body {
    margin: 0;
    background: var(--color-bg);
    color: var(--color-text);
    /* 🚫 CDN·폰트 파일 선언 금지 — 시스템 폴백으로 시작한다(ADR-093 결정 4 / OD-026) */
    font-family: 'Noto Sans KR', system-ui, sans-serif;
    /* 본문 밖이라 폭 배율만 받는다 — 글자 3단은 .page-content 안쪽 몫이다(UI-13 / ADR-107 결정 2). */
    font-size: calc(var(--font-size-base) * var(--u));
    line-height: 1.6;
    -webkit-text-size-adjust: 100%;
}

button {
    font-family: inherit;
    cursor: pointer;
}

[hidden] {
    display: none !important;
}

/* 세로 구조: 지역칩바(고정) → .page-content(스크롤) → 하단 5탭(고정) */
.app-shell {
    display: flex;
    flex-direction: column;
    height: 100%;
    max-width: 480px;
    margin: 0 auto;
    /* 🚨 검색 패널이 「본문 위에 뜨는 겹」이 될 기준 상자다(ADR-109 결정 4).
       🚫 지우지 마라 — 기준이 없으면 패널이 화면(뷰포트)에 붙어 480px 껍데기를 벗어난다. */
    position: relative;
}

/* ─────────────── 상단 지역칩바 (ADR-063 · ADR-091) ───────────────
   🚨 아래 숫자는 전부 **기준 폭 360px에서의 값**이고 폭 배율(--u)이 곱해진다(ADR-107 결정 1·2).
      배율 하한이 1배라 높이 64px·버튼 56px 밑으로 내려가는 일은 구조적으로 없다.
      56으로 되돌리면 안에 든 버튼이 44px가 되어 56×56px 규칙을 깬다(UI-21).
   🚫 글자 확대(--font-scale)도 합성 배율(--us)도 여기 걸지 마라 — 고정 크롬이 커지면
      콘텐츠 영역이 잠식된다(UI-13). */
.region-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex: none;
    height: calc(var(--region-bar-height) * var(--u));
    padding: 0 calc(16 * var(--u));
    gap: calc(10 * var(--u));
    background: var(--color-bg);
    border-bottom: 2px solid var(--color-border);
}

/* 🚨 채운 파랑이다(ADR-109 결정 10). 종전에는 테두리만 있어 옆 구 칩과 겉모양이 같았고,
   무엇을 「누르는 자리」인지 색으로 구분되지 않았다. 🚫 구 칩까지 채우지 마라 —
   둘 다 강조되면 아무것도 강조되지 않는다(구 칩은 상태 표시이지 주된 행동이 아니다). */
.find-btn {
    display: inline-flex;
    align-items: center;
    gap: calc(6 * var(--u));
    height: calc(56 * var(--u));
    padding: 0 calc(16 * var(--u));
    border: 2px solid var(--color-primary);
    border-radius: 4px;
    background: var(--color-primary);
    color: var(--color-bg);
    font-size: calc(20 * var(--u));
    font-weight: 700;
}

.region-chip {
    display: inline-flex;
    align-items: center;
    gap: calc(5 * var(--u));
    height: calc(56 * var(--u));
    padding: 0 calc(14 * var(--u));
    border: 2px solid var(--color-border);
    border-radius: 4px;
    background: var(--color-bg);
    color: var(--color-text);
    font-size: calc(20 * var(--u));
    font-weight: 600;
}

/* 검색 모드의 칩바 — [입력창 = 남는 폭 전부][검색 92×56px] (ADR-091 결정 12·15 · ADR-109 결정 3)
   ♻️ **옛 고정 폭 336px은 산수가 틀려 있었다.** 칩바 안쪽 폭은 328px(360 − 좌우 16)뿐인데
      336 + 간격 10 + 「검색」 92 = 438px을 거기 넣으려 했다. 입력 요소는 줄어들 하한(기본
      20글자 폭)이 있어 넘친 몫을 오른쪽 「검색」이 통째로 뒤집어썼고, **버튼이 화면 밖으로
      나가 있었다**(폭 480px 실측: 칩바 내용 557 vs 담을 수 있는 480 — 76px 바깥).
   🚫 고정 폭을 어떤 숫자로도 되살리지 마라 — 어떤 숫자든 넓은 기기에서 남거나 좁은 기기에서 넘친다.
   🚫 `min-width: 0`을 지우지 마라: 그 한 줄이 없으면 입력 요소의 기본 하한에 걸려 안 줄어든다.
   🚫 ✕ 지움 버튼을 넣지 마라: 입력창이 좁아지고 기호만으론 뜻이 안 통한다. */
.search-input {
    flex: 1 1 auto;
    min-width: 0;
    height: calc(56 * var(--u));
    padding: 0 calc(12 * var(--u));
    border: 2px solid var(--color-border-soft);
    border-radius: 4px;
    background: var(--color-bg);
    color: var(--color-text);
    font-family: inherit;
    font-size: calc(20 * var(--u));
}

.search-submit {
    flex: none;
    width: calc(92 * var(--u));
    height: calc(56 * var(--u));
    border: 2px solid var(--color-primary);
    border-radius: 4px;
    background: var(--color-primary);
    color: var(--color-bg);
    font-size: calc(20 * var(--u));
    font-weight: 700;
}

/* 최소 2글자(ADR-091 결정 8). 🚫 숨기지 마라 — 눌러도 되는 자리라는 것은 계속 보여야 한다. */
.search-submit:disabled {
    border-color: var(--color-border);
    background: var(--color-card);
    color: var(--color-disabled);
    cursor: default;
}

/* ─────────────── 검색 패널 (ADR-091 결정 12·13·15 · ADR-106 결정 6) ───────────────
   안쪽 여백·간격·칩 56px은 **기준 폭 360px에서의 값**이고 폭 배율(--u)이 곱해진다(ADR-107).
   ♻️ 높이는 **내용 높이 그대로**다(ADR-107 결정 7). 옛 고정값 256px은 특정 폰 한 대의 뺄셈
      (뷰포트 720 − 칩바 64 − 키보드 320 − 하단 탭 80)이었고, 모든 치수가 폭에 비례하게 된
      마당에 높이만 한 기종의 산수로 묶어 둘 근거가 없어 폐기했다. 칩바 바로 아래에 끼워 넣는다.
      256px이 노렸던 「아래 목록이 비치지 않는다」는 이제 본문 덮개(.content-dim)가 맡는다.
   ♻️ **본문 위에 뜨는 겹이다**(ADR-109 결정 4). 종전에는 흐름 안의 블록이라 펼칠 때마다 본문을
      245px 아래로 밀어냈고, 화면이 낮아지면(키보드) 껍데기를 뚫어 페이지 전체가 스크롤되며
      하단 탭 5개가 화면 밖으로 나갔다(폭 480 · 높이 420 실측: 껍데기가 420px 자리에 527px).
      이제 자리를 차지하지 않으므로 아래 목록의 높이가 검색을 열어도 그대로다.
   🚨 「하단 탭을 덮지 마라」(ADR-091 결정 12)는 그대로이고, 이제 **표현 금지가 아니라 값**이
      지킨다 — 아래 max-height가 칩바와 탭바 몫을 둘 다 빼고, z-index가 탭바(100)보다 낮다.
      🚫 position: fixed · inset · 100vh는 여전히 금지다(화면 전체를 덮는 표현이다).
   🚫 글자 확대(--font-scale)도 합성 배율(--us)도 걸지 마라 — 칩바·하단 탭과 같은 고정 크롬이다(UI-13). */
.search-panel {
    display: flex;
    flex-direction: column;
    gap: calc(8 * var(--u));
    /* 🚨 칩바 **바로 아래**에서 시작한다 — 🚫 숫자로 적지 마라(칩바 높이와 값이 갈라진다). */
    position: absolute;
    top: calc(var(--region-bar-height) * var(--u));
    right: 0;
    left: 0;
    /* 본문 덮개(50)보다 위 · 하단 탭(100)보다 아래. 덮개에 가려지면 패널 자신이 어두워진다. */
    z-index: 60;
    /* 최후 안전장치: 칩바 아래에서 시작하므로 **칩바와 하단 탭 몫을 둘 다** 뺀 데까지만 자란다.
       🚫 하단 탭 몫만 빼던 옛 식으로 되돌리지 마라 — 겹이 된 지금은 아래 끝이 칩바 높이만큼
          내려가 패널이 탭 5개 위로 올라탄다(막으려던 바로 그 일이 일어난다). */
    max-height: calc(100% - (var(--region-bar-height) + var(--bottom-nav-height)) * var(--u));
    padding: calc(10 * var(--u)) calc(16 * var(--u));
    background: var(--color-bg);
    border-bottom: 2px solid var(--color-border);
    overflow-y: auto;
}

/* 입력창의 라벨을 겸한다 — 플레이스홀더만 두는 것은 안티패턴이다(UI_GUIDE). */
.search-guide {
    flex: none;
    color: var(--color-body);
    font-size: calc(20 * var(--u));
    font-weight: 700;
}

/* 추천 검색어 칩 4개 — 🚨 타이핑을 못 하는 어르신의 유일한 검색 경로다(ADR-091 결정 13). */
.search-suggests {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: calc(8 * var(--u));
    flex: none;
}

/* 🚨 48 × 18은 「터치 56 · 글자 20」(UI-1)을 **알고 어기는** 값이다(ADR-109 결정 5 → OD-070).
   선례는 스코프 세그먼트의 52 × 18(OD-041)이다. 패널이 낮아야 검색 중에도 아래 목록이 보인다.
   🚫 「위반」으로 읽고 56 · 20으로 되돌리지 마라 · 🚫 이 예외를 「그만두기」로 넓히지 마라
      (그쪽은 탈출구라 가장 누르기 쉬워야 한다). */
.search-chip {
    min-height: calc(48 * var(--u));
    padding: 0 calc(12 * var(--u));
    border: 2px solid var(--color-primary);
    border-radius: 4px;
    background: var(--color-bg);
    color: var(--color-primary);
    font-size: calc(18 * var(--u));
    font-weight: 700;
}

/* 화면 안 탈출구 — Android 뒤로가기 키를 모르는 어르신을 위한 줄(ADR-091 결정 15). */
.search-cancel {
    flex: none;
    width: 100%;
    min-height: calc(56 * var(--u));
    border: 2px solid var(--color-border);
    border-radius: 4px;
    background: var(--color-bg);
    color: var(--color-body);
    font-size: calc(20 * var(--u));
    font-weight: 700;
}

/* ─────────────── 본문 자리 (ADR-107 결정 8) ───────────────
   콘텐츠 영역과 그 위에 덮이는 겹을 함께 담는다. 덮개가 「본문 영역까지만」 덮으려면
   기준이 될 상자가 있어야 하고, 그 기준이 이것이다. */
.shell-body {
    position: relative;
    flex: 1 1 auto;
    display: flex;
    /* 안쪽(.page-content)의 스크롤이 살아 있게 한다 — 없으면 내용만큼 늘어나 셸을 뚫는다. */
    min-height: 0;
}

/* 검색 패널이 펼쳐진 동안 본문만 덮는 겹(ADR-107 결정 8·9·10).
   🚨 하단 탭 5개는 덮지 않는다 — 어르신의 탈출 경로는 항상 또렷해야 한다(ADR-091 결정 12).
   🚫 검정 단색 배경 + 투명도(opacity) 조합으로 대신하지 마라 — 결과는 같아 보이지만 팔레트 밖
      색이 색상 검사(shell.test.js의 허용 26색 대조)를 우회하는 문이 된다.
      🚨 그 세 자리 검정 표기는 이 주석에도 적지 마라 — 색상 검사가 주석까지 훑는다(실측).
   🚫 흐림 효과가 아니다(UI-3) — 검정 반투명 한 겹이다. */
.content-dim {
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    /* 🚨 탭바와 **같은 토큰**을 쓰므로 두 값이 갈라질 수 없다 — 🚫 0으로 되돌리지 마라. */
    bottom: calc(var(--bottom-nav-height) * var(--u));
    background: var(--color-overlay-dim);
    /* 하단 탭 100 · 팝업 200 · 토스트 300보다 아래다. */
    z-index: 50;
}

/* ─────────────── 콘텐츠 영역 ───────────────
   글자 크기 3단(ADR-065)은 오직 여기에만 걸린다 — 확대만, 축소 없음(20px가 하한).
   🚨 여기서부터 아래로는 **본문 안쪽**이라 치수에 합성 배율(--us)을 곱한다 — 숫자는 전부
      **기준 폭 360px·글자 3단 1.0에서의 값**이고, 배율 하한이 1배라 그 밑으로 내려가지 않는다
      (ADR-107 결정 1·2·11). 🚫 맨손 px로 되돌리지 마라 — frontend/test/scale.test.js가 막는다. */
.page-content {
    --font-scale: 1.0;
    /* 🚨 여기서 다시 선언하는 것이 핵심이다 — 글자 3단(--font-scale)이 이 요소에만 걸려 있어
       최상위에서 계산하면 1.0으로 굳는다(ADR-107 결정 3·4). 상한 1.5배 = 글자 30px. */
    --us: min(calc(var(--font-scale) * var(--u)), 1.5px);
    flex: 1 1 auto;
    /* 🚨 「남는 만큼만 차지하고 안에서 스크롤한다」의 나머지 반쪽이다(ADR-109 결정 6).
       flex 항목의 기본 하한은 「내용 높이」라, 이 줄이 없으면 자리가 좁아졌을 때(키보드가
       올라온 낮은 화면) 안에서 스크롤되는 대신 **밖으로 부풀어** 껍데기를 뚫는다 —
       그 넘친 몫이 페이지 스크롤이 되고 고정된 하단 탭이 화면 밖으로 밀려난다.
       🚫 지우지 마라(실측: 높이 420px 자리에서 껍데기가 527px까지 자랐다). */
    min-height: 0;
    overflow-y: auto;
    /* 🚨 한글은 기본값이면 **글자 단위로 아무 데서나** 잘린다 — 「지원사」/「업」처럼 낱말
       한가운데가 갈린다(ADR-109 결정 9). 낱말(어절) 단위로만 줄을 바꾼다.
       🚨 여기 한 자리에 걸면 본문 전체가 물려받아 **글자 3단 전부**에 한 번에 걸린다 —
          🚫 화면마다·크기마다 따로 붙이지 마라(두 벌이 되면 한쪽만 고쳐진다). */
    word-break: keep-all;
    /* 한 낱말이 한 줄보다 길 때(긴 기관명·주소)만 예외로 자른다 — 없으면 상자 밖으로 삐져나온다. */
    overflow-wrap: break-word;
    padding-top: calc(16 * var(--us));
    padding-right: calc(16 * var(--us));
    padding-left: calc(16 * var(--us));
    /* 고정된 하단 5탭에 가리지 않도록 탭바와 같은 토큰을 비워 둔다 — 🚫 숫자로 되돌리지 마라(값이 갈라진다).
       🚨 본문 안쪽에서 **유일하게 폭 배율(--u)을 쓰는 줄**이고 그것이 의도다 — 가려질 대상인 탭바가
          글자 3단을 안 받으므로(UI-13), 여기에 --us를 쓰면 글자를 키웠을 때 여백만 커져 빈 자리가 남는다.
       🚫 --us로 바꾸지 마라(scale.test.js가 이 한 줄만 예외로 알고 있다). */
    padding-bottom: calc(var(--bottom-nav-height) * var(--u));
    font-size: calc(var(--font-size-base) * var(--us));
}

.page-content[data-font-scale='big'] {
    --font-scale: 1.15;
}

.page-content[data-font-scale='huge'] {
    --font-scale: 1.3;
}

/* 화면 제목 — 일정 · 설정 · 가족 세 화면에만 있다(ADR-109 결정 8).
   🚨 **착수 시점에 이 선택자에 규칙이 하나도 없었다.** 브라우저 기본값(글자 40px · 위아래 여백
      각 27px — 기준 폭 360px 환산)이 그대로 나와, 본문 자체 여백까지 더해 제목 위에만 43px이
      비었다. 제목이 없는 다른 탭들과 시작 높이가 어긋나 **그 세 화면만 위가 휑해 보였다.**
   🚫 규칙을 지워 브라우저 기본으로 되돌리지 마라 · 🚫 제목 자체를 지워서 해결하지 마라 —
      보조기술 사용자에게 「여기가 어디인지」를 알려 줄 것이 없어진다(UI-20).
   위 여백이 0인 것은 의도다 — 본문의 위 여백(16px)이 이미 제목 위 간격이다. */
.page-title {
    font-size: calc(22 * var(--us));
    font-weight: 700;
    color: var(--color-text);
    margin: 0 0 calc(12 * var(--us));
}

/* ─────────────── 하단 내비게이션 (5탭 — 상단 카테고리 바 없음) ───────────────
   확정값(2026-08-18): 바 80px · 아이콘 28px · 레이블 14px/700.
   🚨 셋 다 **기준 폭 360px에서의 값**이고 폭 배율(--u)이 곱해진다(ADR-107 결정 2).
      레이블 14px은 「폰트 최소 20px」의 알고 고른 예외이며(OD-028) 비례 전환이 그것을 바꾸지 않는다.
   🚫 합성 배율(--us)을 쓰지 마라 — 글자 3단으로 탭바가 커지면 공고가 보일 자리를 잡아먹는다(UI-13).
   🚫 레이블 텍스트를 지우지 마라 — 어르신이 실제로 읽는 것은 이모지가 아니라 레이블이다. */
.bottom-nav {
    position: fixed;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    width: 100%;
    max-width: 480px;
    height: calc(var(--bottom-nav-height) * var(--u));
    border-top: 2px solid var(--color-border);
    background: var(--color-bg);
    z-index: 100;
}

.tab-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: calc(2 * var(--u));
    position: relative;
    min-height: calc(56 * var(--u));
    border: none;
    background: var(--color-bg);
    padding: 0;
}

.tab-icon {
    font-size: calc(28 * var(--u));
    line-height: 1;
}

.tab-label {
    font-size: calc(14 * var(--u));
    font-weight: 700;
    color: var(--color-tab-label);
}

/* 이모지는 CSS color가 먹지 않는다 — 활성 구분은 레이블 색으로만 한다(ADR-041: 색만으로 구분하지 않도록
   aria-selected가 함께 붙는다). */
.tab-btn[aria-selected='true'] .tab-label {
    color: var(--color-primary);
}

/* ─────────────── 대형 버튼 (모든 주요 액션) ─────────────── */
.btn-large {
    min-height: calc(56 * var(--us));
    font-size: calc(22 * var(--us));
    font-weight: 700;
    border-radius: 12px;
    padding: calc(14 * var(--us)) calc(20 * var(--us));
    width: 100%;
    border: none;
}

.btn-primary {
    background: var(--color-primary);
    color: var(--color-bg);
}

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

.btn-ghost {
    background: var(--color-card);
    color: var(--color-text);
    border: 2px solid var(--color-border-soft);
}

/* 가족에게 공유하기 (ADR-064 · 대상어는 ADR-094가 자녀→가족으로 바꿨다) */
.btn-outline {
    background: var(--color-bg);
    color: var(--color-primary);
    border: 2px solid var(--color-primary);
}

/* 전화로 문의하기 — 행동 강조 초록 (ADR-064) */
.btn-call {
    background: var(--color-call);
    color: var(--color-bg);
}

/* ─────────────── 상태 배지 4종 (ADR-062 · ADR-090) ───────────────
   🚫 다섯 번째 클래스를 만들지 마라 — status가 null이면 "배지 없음"이라는 값이 아니라 요소의 부재다. */
.status-upcoming {
    background: var(--color-status-upcoming-bg);
    color: var(--color-status-upcoming-fg);
}

.status-open {
    background: var(--color-status-open-bg);
    color: var(--color-status-open-fg);
}

.status-always {
    background: var(--color-status-always-bg);
    color: var(--color-status-always-fg);
}

.status-closed {
    background: var(--color-status-closed-bg);
    color: var(--color-status-closed-fg);
}

/* ─────────────── 목록 스코프 세그먼트·건수 (UI_GUIDE 「목록 스코프 세그먼트」:347-350 정본 · ADR-061) ───────────────
   🚫 카테고리 바가 아니다 — 「보는 범위」를 고르는 자리다(UI-4 무개정 · UI-6).
   🚨 min-height 52px · font-size 18px는 「터치 56px · 글자 20px」(UI-1) 하한을 **알고 어기는** 값이며
      UI_GUIDE 정본 그대로다(→ docs/OPEN_DECISIONS.md OD-041). 🚫 임의로 56px·20px로 올리지 마라 —
      세 칸이 한 줄에 들어가지 않아 세그먼트가 두 줄이 된다.
   🚨 두 숫자는 **기준 폭 360px에서의 값**이고 합성 배율이 곱해진다(ADR-107) — 비례 전환이 이 예외를
      바꾸지 않는다. 🚫 「비례로 옮기는 김에 규칙에 맞췄다」로 고치지 마라(OD-041의 판단이 뒤집힌 것으로 읽힌다). */
.scope-seg {
    display: flex;
    gap: calc(8 * var(--us));
}

.scope-seg button {
    flex: 1;
    min-height: calc(52 * var(--us));
    font-size: calc(18 * var(--us));
    font-weight: 700;
    border-radius: 12px;
    border: 2px solid var(--color-border-soft);
    background: var(--color-bg);
    color: var(--color-primary);
}

.scope-seg button.active {
    background: var(--color-primary);
    color: var(--color-bg);
    border-color: var(--color-primary);
}

/* 「{스코프 라벨} · 총 N건」 — 라벨의 출처는 스코프다(OD-044). 🚫 항상 구 이름을 쓰지 마라. */
.list-count {
    font-size: calc(18 * var(--us));
    color: var(--color-sub);
    font-weight: 600;
    margin: calc(12 * var(--us)) calc(4 * var(--us));
}

/* 서버가 준 문장을 그대로 담는 자리(ADR-092). 🚫 여기에 문구를 하드코딩하지 마라.
   🚨 「오늘 새로 온 공고」 화면(C-4)도 **같은 겉모양**이라 선택자만 나눠 쓴다(ADR-112 결정 7) —
      🚫 그쪽에 전용 색·전용 치수를 새로 만들지 마라. */
.list-error,
.today-error {
    font-size: calc(20 * var(--us));
    color: var(--color-danger);
    margin: 0 0 calc(16 * var(--us));
}

/* 카드 사이 간격은 .ann-card의 margin-bottom이 만든다 — 여기서 겹쳐 주지 않는다. */
.ann-list {
    display: block;
}

/* 「더보기」는 .btn-large·.btn-ghost 재사용이다(UI_GUIDE:556 — 🚫 전용 색을 만들지 마라).
   🚨 「오늘 새로 온 공고」 화면(C-4)의 「더보기」도 **같은 것**이다 — 선택자만 나눠 쓴다. */
.list-more,
.today-more {
    display: block;
    width: 100%;
    margin: calc(4 * var(--us)) 0 calc(8 * var(--us));
}

/* ─────────────── 검색 결과 (UI_GUIDE 「검색 결과」 · ADR-091 결정 18 · ADR-106 결정 5) ───────────────
   🚫 스코프 세그먼트를 만들지 마라 — 검색 범위는 항상 합집합이라 고를 것이 없다(UI-26).
   🚫 낱말을 색으로 강조하지 마라 — 색상표에 없는 색을 늘리지 않는다(ADR-091 결정 18). */

/* 「‘교통비’ · 3건」 — 🚨 지역 이름을 붙이지 마라(ADR-106 결정 5). 목록의 건수 줄과 같은 겉모양이다. */
.search-count {
    font-size: calc(18 * var(--us));
    color: var(--color-sub);
    font-weight: 600;
    margin: calc(12 * var(--us)) calc(4 * var(--us));
}

/* 서버가 준 문장을 그대로 담는 자리(ADR-092). 🚫 여기에 문구를 하드코딩하지 마라. */
.search-error {
    font-size: calc(20 * var(--us));
    color: var(--color-danger);
    margin: 0 0 calc(16 * var(--us));
}

.search-results {
    display: block;
}

/* 「더보기」는 .btn-large·.btn-ghost 재사용이다(🚫 전용 색을 만들지 마라). */
.search-more {
    display: block;
    width: 100%;
    margin: calc(4 * var(--us)) 0 calc(8 * var(--us));
}

/* 0건 화면의 추천어 재노출(ADR-091 결정 19) — 패널의 칩과 같은 겉모양·같은 통로다.
   빈 상태 상자가 가운데 정렬 flex라 칸 너비를 여기서 되돌려 준다. */
.empty-state .search-suggests {
    width: 100%;
    margin-top: calc(16 * var(--us));
}

/* ─────────────── 공고 카드 (UI_GUIDE 「공고 카드」:137-163 정본 · ADR-064 · ADR-098 결정 4) ───────────────
   🚨 카드 전체가 <button>이다(OD-043) — 「자세히 보기 >」 글자만 누르게 하면 터치 타깃이
      56px에 한참 못 미친다(UI-1). 그래서 브라우저 기본 버튼 스타일을 눌러 준다.
   🚫 요약 줄(줄글)을 두지 마라 — 라벨 행 3줄이 전부다(ADR-062). */
.ann-card {
    display: block;
    width: 100%;
    min-height: calc(56 * var(--us)); /* UI-1 — 카드 전체가 터치 타깃이다(--touch-target과 같은 값을 명시한다) */
    text-align: left;
    font-family: inherit;
    cursor: pointer;
    background: var(--color-bg);
    border: 2px solid var(--color-border);
    border-radius: 12px;
    padding: calc(20 * var(--us));
    margin-bottom: calc(12 * var(--us));
}

/* 제목과 상태 배지를 양끝으로 벌린다 — 배지가 제목에 눌려 줄바꿈되지 않게 flex: none을 준다. */
.ann-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: calc(8 * var(--us));
}

.ann-head .tag {
    flex: none;
}

.ann-title {
    font-size: calc(var(--font-size-card-title) * var(--us));
    font-weight: 600;
    color: var(--color-text);
    margin: calc(8 * var(--us)) 0;
}

.ann-tags {
    display: flex;
    flex-wrap: wrap;
    gap: calc(8 * var(--us));
}

.ann-row {
    display: flex;
    gap: calc(8 * var(--us));
    font-size: calc(var(--font-size-sub) * var(--us));
    margin-top: calc(6 * var(--us));
}

/* 저대비 금지 — 라벨은 --color-sub 이상이다(UI-1). 🚫 팔레트 밖 회색을 여기 적지 마라 */
.ann-row .label {
    flex: none;
    width: calc(74 * var(--us));
    color: var(--color-sub);
    font-weight: 600;
}

.ann-row .value {
    color: var(--color-text);
    font-weight: 500;
}

.ann-more {
    display: block;
    text-align: right;
    color: var(--color-primary);
    font-weight: 700;
    margin-top: calc(8 * var(--us));
}

/* ─────────────── 태그 (UI_GUIDE 「상태 배지·태그」:204-206) ───────────────
   .tag는 상태 배지 4종과 함께 쓰는 공용 껍데기다 — 색만 각 클래스가 정한다.
   .tag-category는 복지 탭에서만 붙는다(ADR-098 결정 7 — 다른 탭은 카드가 전부 같은 카테고리라 소음이다). */
.tag {
    font-size: calc(16 * var(--us));
    font-weight: 700;
    padding: calc(4 * var(--us)) calc(10 * var(--us));
    border-radius: 6px;
}

.tag-category {
    background: var(--color-tag-category-bg);
    color: var(--color-primary);
}

.tag-new {
    background: var(--color-tag-new-bg);
    color: var(--color-new);
}

.tag-urgent {
    background: var(--color-tag-urgent-bg);
    color: var(--color-danger);
}

/* ─────────────── 빈 상태 (UI_GUIDE 「빈 상태」:386-388 정본 · ADR-054) ─────────────── */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: calc(8 * var(--us));
    padding: calc(60 * var(--us)) calc(20 * var(--us));
    text-align: center;
}

.empty-state-title {
    font-size: calc(20 * var(--us));
    font-weight: 600;
    color: var(--color-body);
}

.empty-state-desc {
    font-size: calc(var(--font-size-sub) * var(--us));
    color: var(--color-sub);
    line-height: 1.6;
}

/* ─────────────── 로딩 인디케이터 (ADR-054 · ADR-109 결정 2) ───────────────
   🚫 스켈레톤 금지. 문구는 UI_GUIDE가 정한 「로딩 중...」 그대로다.
   ♻️ **자리를 차지하지 않는 겹이다**(결정 2). 종전에는 본문 맨 위에 끼어드는 블록이라,
      켜지는 순간 아래 내용이 통째로 밀렸다가 응답이 오면 그만큼 되튀었다(폭 480px 실측:
      316px 밀림). 탭 전환·「더보기」마다 이 왕복이 나서 화면이 뚝뚝 끊겨 보였다.
   🚫 흐름 안의 블록으로 되돌리지 마라(display 토글만으로 되돌려도 밀림이 그대로 온다).
   기준 상자는 `.shell-body`다 — 본문이 스크롤돼도 로딩은 제자리에 뜬다. */
.spinner-wrap {
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    /* 본문 덮개(50)보다 아래 — 검색 중에 로딩이 덮개 위로 튀어나오면 「지금 검색 중」이 깨진다. */
    z-index: 40;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: calc(12 * var(--us));
    padding: calc(60 * var(--us)) 0;
}

.spinner {
    width: calc(40 * var(--us));
    height: calc(40 * var(--us));
    border: 4px solid var(--color-border);
    border-top-color: var(--color-primary);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}

.spinner-text {
    font-size: calc(var(--font-size-sub) * var(--us));
    color: var(--color-sub);
}

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

/* ─────────────── 공통 오류 배너 (ADR-038) ───────────────
   🚨 네트워크 레벨 실패 전용이다. 서버가 응답한 4xx/5xx는 여기 오지 않는다(api.js의 NetworkError만). */
.network-error-banner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: calc(16 * var(--us));
    padding: calc(40 * var(--us)) calc(20 * var(--us));
    text-align: center;
}

.network-error-banner p {
    font-size: calc(20 * var(--us));
    color: var(--color-body);
    margin: 0;
}

/* ─────────────── 팝업 오버레이·하단 시트 (UI_GUIDE 「확인 팝업」:446-462) ───────────────
   E-2 약관 전문 시트(온보딩 A-2 · 앱 정보 D-4 공용)와 확인 팝업(C-1·D-3·D-6)이 함께 쓴다.
   z-index 200 — 토스트(300)보다 낮다. 🚫 흐림 효과·그라디언트·네온·보라 금지(UI-3). */
.popup-overlay {
    position: fixed;
    inset: 0;
    background: var(--color-overlay-dim);
    display: flex;
    align-items: flex-end;
    z-index: 200;
}

.popup-sheet {
    background: var(--color-bg);
    border-radius: 20px 20px 0 0;
    padding: calc(32 * var(--us)) calc(24 * var(--us)) calc(40 * var(--us));
    width: 100%;
}

/* ─────────────── 확인 팝업 (E-1 — UI_GUIDE:443-462 도해·CSS 정본 · ADR-054 #8) ───────────────
   질문 한 문장 + 버튼 2개가 전부다. 🚫 세 번째 컨트롤·✕ 아이콘을 더하지 마라. */
.popup-text {
    font-size: calc(24 * var(--us));
    font-weight: 600;
    text-align: center;
    margin-bottom: calc(24 * var(--us));
}

/* 「취소」가 왼쪽, 실행이 오른쪽 — .btn-large가 width: 100%라 flex: 1로 반씩 나눈다. */
.popup-actions {
    display: flex;
    gap: calc(12 * var(--us));
    align-items: center;
}

/* <a> 갈래(C-1 전화 걸기)도 <button>과 같은 크기·정렬로 보여야 한다 — 링크 기본 스타일을 눌러 준다. */
.popup-actions > * {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
}

/* ─────────────── 약관 전문 시트 (E-2 — ADR-097 결정 3) ───────────────
   약관은 화면보다 길다 — 시트 높이를 묶고 본문만 안에서 스크롤시킨다(🚫 iframe 두 겹 스크롤 아님).
   🚨 글자 크기 3단(ADR-065)은 .page-content의 --font-scale로 오며, 시트가 그 안에 붙어 물려받는다.
      약관은 법적 고지라 가장 읽혀야 하는 화면이다 — 확대 대상에서 빼지 마라. */
.terms-sheet {
    display: flex;
    flex-direction: column;
    max-width: 480px;
    margin: 0 auto;
    max-height: 85%;
    font-size: calc(var(--font-size-base) * var(--us));
}

.terms-sheet-title {
    flex: none;
    font-size: calc(24 * var(--us));
    font-weight: 700;
    color: var(--color-text);
    margin: 0 0 calc(16 * var(--us));
}

.terms-sheet-body {
    flex: 1 1 auto;
    overflow-y: auto;
    margin-bottom: calc(20 * var(--us));
    color: var(--color-body);
}

/* 약관 본문은 자신의 <style>을 벗고 들어오므로(sheet.js가 제거한다) 여기서 다시 입힌다.
   🚫 저대비 회색 금지(위 :root 토큰 밖으로 나가지 않는다) · 본문 20px 이상(UI-1). */
.terms-sheet-body h1 {
    font-size: calc(24 * var(--us));
    font-weight: 700;
    color: var(--color-text);
    margin: 0 0 calc(8 * var(--us));
}

.terms-sheet-body h2 {
    font-size: calc(20 * var(--us));
    font-weight: 700;
    color: var(--color-text);
    margin: calc(24 * var(--us)) 0 calc(8 * var(--us));
    padding-bottom: calc(6 * var(--us));
    border-bottom: 1px solid var(--color-border);
}

.terms-sheet-body p,
.terms-sheet-body li {
    font-size: calc(20 * var(--us));
    color: var(--color-body);
    margin: 0 0 calc(12 * var(--us));
}

.terms-sheet-body ul {
    margin: calc(8 * var(--us)) 0 calc(16 * var(--us));
    padding-left: calc(22 * var(--us));
}

.terms-sheet-body table {
    width: 100%;
    border-collapse: collapse;
    margin: calc(12 * var(--us)) 0 calc(16 * var(--us));
}

.terms-sheet-body th,
.terms-sheet-body td {
    border: 1px solid var(--color-border);
    padding: calc(10 * var(--us)) calc(12 * var(--us));
    text-align: left;
    font-size: calc(var(--font-size-sub) * var(--us));
    color: var(--color-body);
}

.terms-sheet-body th {
    background: var(--color-card);
    font-weight: 700;
    color: var(--color-text);
}

/* 불러오기 실패 안내 — 🚫 빈 시트를 남기지 마라(OD-037). */
.terms-sheet-error {
    font-size: calc(20 * var(--us));
    color: var(--color-body);
}

/* ─────────────── 토글 버튼 (UI_GUIDE 「알림 설정」:502-508) ───────────────
   설정 D-2 알림 설정과 온보딩 A-2 약관 동의가 함께 쓴다. 🚫 체크박스로 바꾸지 마라 —
   작은 체크박스는 시니어에게 터치·인식이 어렵다(UI_GUIDE가 이 패턴을 명시). */
.toggle-group {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: calc(8 * var(--us));
}

/* 필수 약관은 「동의함」 하나뿐이라 칸을 나누지 않는다 — 반칸짜리 버튼은 터치 폭이 줄어든다. */
.toggle-group-single {
    grid-template-columns: 1fr;
}

.toggle-btn {
    min-height: calc(56 * var(--us));
    font-size: calc(20 * var(--us));
    font-weight: 700;
    border-radius: 12px;
    border: 2px solid var(--color-border-soft);
    background: var(--color-card);
    color: var(--color-tab-label);
}

.toggle-btn.active {
    background: var(--color-primary);
    color: var(--color-bg);
    border-color: var(--color-primary);
}

/* ─────────────── 약관 동의 화면 (A-2 — UI_GUIDE 「약관 동의 화면」:790-792) ───────────────
   위치정보 행은 없다(ADR-071). 필수 2종 + 선택 1종 = 3행이며, 선택 행만 토글 2개다.
   🚨 아래 세 규칙의 font-size·color·display는 UI_GUIDE:790-792 정본 그대로다 — 나머지 선언은
      그것을 화면에 앉히기 위한 보탬이다(터치 타깃·여백). 정본 값을 바꾸지 마라. */
.consent-row {
    margin-bottom: calc(20 * var(--us));
}

.consent-label {
    font-size: calc(20 * var(--us));
    font-weight: 600;
    color: var(--color-text);
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: calc(8 * var(--us));
    margin: 0 0 calc(10 * var(--us));
}

/* 「보기 >」도 손가락으로 누르는 것이다 — 56×56px 아래로 내리지 마라(UI-1). */
.consent-view-link {
    font-size: calc(18 * var(--us));
    color: var(--color-primary);
    font-weight: 600;
    flex: none;
    min-height: calc(56 * var(--us));
    min-width: calc(56 * var(--us));
    padding: 0 calc(12 * var(--us));
    border: none;
    background: var(--color-bg);
    text-align: right;
}

.consent-title {
    font-size: calc(24 * var(--us));
    font-weight: 700;
    color: var(--color-text);
    line-height: 1.4;
    margin: calc(8 * var(--us)) 0 calc(24 * var(--us));
}

.consent-all {
    margin-bottom: calc(28 * var(--us));
}

.consent-list {
    list-style: none;
    margin: 0 0 calc(8 * var(--us));
    padding: 0;
}

/* 알림 항목 보조 문구 — 18px/#444444는 UI_GUIDE가 명시한 예외값이다(본문 하한 20px). */
.consent-note {
    font-size: calc(var(--font-size-sub) * var(--us));
    color: var(--color-sub);
    margin: calc(10 * var(--us)) 0 0;
}

/* 서버가 준 문장을 그대로 담는 자리(ADR-092). 🚫 여기에 문구를 하드코딩하지 마라. */
.consent-error {
    font-size: calc(20 * var(--us));
    color: var(--color-danger);
    margin: 0 0 calc(16 * var(--us));
}

.consent-next {
    margin-bottom: calc(24 * var(--us));
}

/* 🚨 비활성이 눈에 보여야 한다 — 눌리는데 아무 일도 없으면 어르신은 앱이 멈췄다고 느낀다.
   회색 글자·회색 배경은 팔레트의 --color-disabled / --color-card 쌍이며 저대비 금지선 위다(UI-1).
   🚫 주석에도 팔레트 밖 색을 6자리로 적지 마라 — shell.test.js의 16진수 스캔이 주석까지 훑는다. */
.consent-next:disabled {
    background: var(--color-card);
    color: var(--color-disabled);
    border: 2px solid var(--color-border-soft);
    cursor: default;
}

/* ─────────────── 지역 선택 화면 (A-5 — UI_GUIDE 「온보딩 화면 특수 규칙」:876) ───────────────
   25개 구 리스트. 서버가 가나다순으로 주며 프론트는 순서를 건드리지 않는다.
   🚨 항목 최소 60px는 UI_GUIDE 정본 값이고, 56×56px 터치 타깃 규칙(UI-1)도 함께 만족해야 하므로
      실제로는 64px를 준다 — 🚫 60px 아래로 내리지 마라(기준 폭 360px에서의 값이고 배율 하한이
      1배다 — ADR-107). jsdom은 레이아웃을 계산하지 않아 이 수치를 지키는 것은 이 파일의 소스뿐이다. */
.district-title {
    font-size: calc(24 * var(--us));
    font-weight: 700;
    color: var(--color-text);
    line-height: 1.4;
    margin: calc(8 * var(--us)) 0 calc(20 * var(--us));
}

/* 서버가 준 문장을 그대로 담는 자리(ADR-092). 🚫 여기에 문구를 하드코딩하지 마라. */
.district-error {
    font-size: calc(20 * var(--us));
    color: var(--color-danger);
    margin: 0 0 calc(16 * var(--us));
}

.district-list {
    list-style: none;
    margin: 0 0 calc(24 * var(--us));
    padding: 0;
}

.district-item {
    margin-bottom: calc(8 * var(--us));
}

.district-btn {
    display: block;
    width: 100%;
    min-height: calc(64 * var(--us));
    padding: 0 calc(20 * var(--us));
    font-size: calc(22 * var(--us));
    font-weight: 600;
    text-align: left;
    color: var(--color-text);
    background: var(--color-bg);
    border: 2px solid var(--color-border);
    border-radius: 12px;
}

/* 누른 것이 눈에 보여야 한다 — 반응이 없으면 어르신은 앱이 멈췄다고 느낀다. */
.district-btn:active {
    background: var(--color-card);
    border-color: var(--color-primary);
}

/* 지금 보고 있는 구 하나(D-1 구 변경 — ADR-108 결정 9). A-5(온보딩)에는 이 개념이 없어
   그 화면에서는 이 규칙이 아무 항목에도 걸리지 않는다 — 두 화면이 CSS만 공유하는 자리다.
   🚨 색만으로 구분하지 않는다 — 마크업의 aria-current가 짝이고 선택자도 그것이다(ADR-041). */
.district-btn[aria-current='true'] {
    color: var(--color-bg);
    background: var(--color-primary);
    border-color: var(--color-primary);
}

/* ─────────────── 설정 탭 (B-5) · 알림 설정 (D-2 — UI_GUIDE:485-508) ───────────────
   🚫 나머지 3칸(D-1 구 변경 · D-4 앱 정보 · D-6 탈퇴)의 스타일을 미리 만들지 마라
      (ADR-097 결정 7) — 화면이 없는데 규칙만 남으면 다음 세션이 「빠뜨렸다」고 읽는다.
   ⚠️ D-1.5(글자 크기)는 그 목록에서 빠졌다 — `settings-rest` step 2가 채웠고 규칙은 이 절 끝에 있다.
   ⚠️ D-3(가족)은 그 목록에서 빠졌다 — 설정 탭에는 링크 행(.settings-link) 하나만 두고 화면은
      #/family가 따로 소유한다(ADR-102 결정 1·2).
   토글 버튼(.toggle-group·.toggle-btn)은 약관 동의 화면과 공용이라 위에 이미 있다 —
   🚫 여기서 다시 정의하지 마라(중복 정의는 한쪽만 고쳐지는 순간 갈라진다). */
.settings-section {
    margin-bottom: calc(24 * var(--us));
}

.settings-heading {
    font-size: calc(22 * var(--us));
    font-weight: 700;
    color: var(--color-text);
    margin: calc(8 * var(--us)) 0 calc(16 * var(--us));
}

/* 서버가 준 문장을 그대로 담는 자리(ADR-092). 🚫 여기에 문구를 하드코딩하지 마라. */
.settings-error {
    font-size: calc(20 * var(--us));
    color: var(--color-danger);
    margin: 0 0 calc(16 * var(--us));
}

/* 마스터 동의가 없을 때의 안내 카드 — 카테고리 토글 대신 이것 하나만 뜬다. */
.settings-notice {
    border: 2px solid var(--color-border);
    border-radius: 12px;
    padding: calc(20 * var(--us));
}

.settings-notice-text {
    font-size: calc(20 * var(--us));
    line-height: 1.5;
    color: var(--color-text);
    margin: 0 0 calc(20 * var(--us));
}

.settings-row {
    margin-bottom: calc(24 * var(--us));
}

.settings-row-label {
    font-size: calc(20 * var(--us));
    font-weight: 600;
    color: var(--color-text);
    margin: 0 0 calc(10 * var(--us));
}

/* 「가족 >」 링크 행 — 알림 설정 아래에 구분선(border-top)과 함께 놓인다(ADR-102 결정 2).
   🚨 .settings-row를 재사용하지 마라 — 그 이름은 카테고리 토글 전용이고 테스트가 개수를 못박는다.
   🚨 min-height 56px는 터치 타깃 하한이다(UI-1) — 🚫 줄이지 마라. */
.settings-link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    min-height: calc(56 * var(--us));
    padding: calc(14 * var(--us)) calc(4 * var(--us));
    border: none;
    border-top: 2px solid var(--color-border);
    background: var(--color-bg);
    color: var(--color-text);
    text-align: left;
}

.settings-link-label {
    font-size: calc(22 * var(--us));
    font-weight: 700;
}

.settings-link-arrow {
    font-size: calc(22 * var(--us));
    font-weight: 700;
    color: var(--color-sub);
}

/* ─────────────── 설정 탭 · 글자 크기 3단 (D-1.5 — UI_GUIDE:737-741 · ADR-065 · ADR-108) ───────────────
   숫자는 UI_GUIDE의 예시 CSS 그대로이고 **치수에만 합성 배율을 곱했다**(그 문서가 직전 페이즈보다
   오래돼 맨손 px로 적혀 있다 — 값을 바꾼 것이 아니다. ADR-107 / frontend/test/scale.test.js).
   🚨 이 줄은 본문 영역 안쪽이라 합성 배율(--us)이 맞다 — 🚫 폭 배율(--u)로 적지 마라.
   🚫 토글 스위치(슬라이더)로 바꾸지 마라 — 대형 버튼 3개다(ADR-065). */
.font-seg {
    display: flex;
    gap: calc(10 * var(--us));
}

.font-seg button {
    flex: 1;
    /* 🚨 터치 타깃 하한(UI-1) — 🚫 줄이지 마라. */
    min-height: calc(56 * var(--us));
    font-size: calc(20 * var(--us));
    font-weight: 700;
    color: var(--color-text);
    background: var(--color-bg);
    border: 2px solid var(--color-border-soft);
    border-radius: 12px;
}

/* 지금 고른 단 하나. 🚨 색만으로 구분하지 않는다 — 마크업의 aria-pressed가 짝이다(ADR-041). */
.font-seg button.active {
    color: var(--color-bg);
    background: var(--color-primary);
    border-color: var(--color-primary);
}

.font-seg-guide {
    font-size: calc(var(--font-size-sub) * var(--us));
    line-height: 1.5;
    color: var(--color-sub);
    margin: calc(12 * var(--us)) 0 0;
}

/* ─────────────── 설정 탭 · 앱 정보 (D-4 — UI_GUIDE:798-807 · ADR-044 · ADR-108 결정 3·7) ───────────────
   이름·숫자는 UI_GUIDE의 예시 CSS 그대로이고 **치수에만 합성 배율을 곱했다**(그 문서가 직전
   페이즈보다 오래돼 맨손 px로 적혀 있다 — ADR-107 / frontend/test/scale.test.js).
   🚨 세로 자리는 **가족 링크 행 아래·탈퇴 위**다(ADR-108 결정 3이 ADR-044 결정 2를 뒤집었다).
   🚫 `.settings-link`를 재사용하지 마라 — 그쪽은 한 칸짜리 링크 행이고 이쪽은 구분선이 이어지는
      표 형태다. 🚫 새 색을 만들지 마라(팔레트 토큰만 — shell.test.js가 주석까지 훑는다). */
.app-info-section {
    margin-top: calc(20 * var(--us));
    padding-top: calc(20 * var(--us));
    border-top: 1px solid var(--color-border);
}

.app-info-label {
    font-size: calc(var(--font-size-sub) * var(--us));
    font-weight: 700;
    color: var(--color-sub);
    margin: 0 0 calc(8 * var(--us));
}

/* 버전 줄과 문서 줄이 같은 모양이다 — 다른 것은 문서 줄만 <button>이라는 점뿐이다.
   🚨 min-height 56px는 터치 타깃 하한이다(UI-1) — 🚫 줄이지 마라. */
.app-info-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    min-height: calc(56 * var(--us));
    padding: 0 calc(4 * var(--us));
    margin: 0;
    border: none;
    border-bottom: 1px solid var(--color-border);
    background: var(--color-bg);
    font-size: calc(var(--font-size-sub) * var(--us));
    color: var(--color-body);
    text-align: left;
}

.app-info-row:last-child {
    border-bottom: none;
}

.app-info-chevron {
    font-size: calc(var(--font-size-base) * var(--us));
    color: var(--color-sub);
}

/* ─────────────── 설정 탭 · 탈퇴 (D-6 — UI_GUIDE:829-836 정본 · ADR-047·050·108 결정 3·8) ───────────────
   🚨 설정 탭의 **가장 마지막 항목**이고 앞 칸들과 **시각적으로 분리**한다 — 이 앱에서 유일하게
      되돌릴 수 없는 동작이라 실수로 닿을 확률이 가장 낮은 자리여야 한다. 분리에 쓰는 것은
      구분선과 여백뿐이다(🚫 새 색을 만들지 마라 — shell.test.js가 주석까지 훑는다). */
.withdraw-section {
    margin-top: calc(32 * var(--us));
    padding-top: calc(20 * var(--us));
    border-top: 2px solid var(--color-border);
}

/* 🚨 탈퇴 실패 문장의 **자기 자리**다 — 🚫 `.settings-error`를 재사용하지 마라: 그쪽은 알림
   설정 섹션 안의 알림 저장 실패용이고, 둘 다 서버가 준 한글 문장이라 거기에 띄우면
   어르신은 「알림 저장이 실패했다」로 읽는다. */
.withdraw-error {
    font-size: calc(var(--font-size-base) * var(--us));
    line-height: 1.5;
    color: var(--color-danger);
    margin: 0 0 calc(16 * var(--us));
}

/* 색은 이미 있는 `.btn-danger`(위 393줄)가 준다 — 🚫 여기서 다시 정의하지 마라(두 벌이 되면
   어느 쪽이 먹는지가 규칙 순서에 달리고 색 대조 검사는 둘 다 통과해 아무도 못 잡는다).
   🚫 `.btn-large`를 함께 걸지 마라 — 그 이름은 「모든 주요 액션」의 것이라(22px·가로 꽉) 탈퇴가
      화면에서 가장 눈에 띄는 버튼이 된다. 탈퇴는 **찾을 수 있으면 되는** 버튼이지 누르라고
      있는 버튼이 아니다. 치수는 하한 그대로다(터치 56px · 글자 20px — UI-1). */
.withdraw-btn {
    min-height: calc(56 * var(--us));
    padding: calc(14 * var(--us)) calc(24 * var(--us));
    border: none;
    border-radius: 12px;
    font-size: calc(var(--font-size-base) * var(--us));
    font-weight: 700;
}

/* ─────────────── 가족 D-3 (UI_GUIDE:688-724 도해 정본 · ADR-102) ───────────────
   🚫 색을 원시 16진수로 쓰지 마라 — 위 :root 토큰 밖으로 나가지 않는다(파일 머리의 규칙). */
.family-error {
    font-size: calc(var(--font-size-base) * var(--us));
    line-height: 1.5;
    color: var(--color-danger);
    margin: 0 0 calc(16 * var(--us));
}

/* 🚨 연결이 0건이면 이 블록만 숨는다(hidden) — 🚫 빈 상태 카드로 대체하지 마라(ADR-039 예외). */
.family-list {
    border-top: 2px solid var(--color-border);
}

.family-item {
    display: flex;
    align-items: center;
    gap: calc(12 * var(--us));
    min-height: calc(56 * var(--us));
    padding: calc(12 * var(--us)) 0;
    border-bottom: 2px solid var(--color-border);
}

/* 라벨은 전부 「가족」이고 구분값은 오른쪽 연결일뿐이다(ADR-094 결정 4). */
.family-label {
    font-size: calc(var(--font-size-base) * var(--us));
    font-weight: 700;
    color: var(--color-text);
}

.family-date {
    flex: 1;
    font-size: calc(var(--font-size-base) * var(--us));
    color: var(--color-body);
}

/* .btn-large는 width:100%라 행 안에서 쓸 수 없다 — .btn-ghost의 색만 물려받고 크기는 여기서 준다. */
.family-disconnect {
    min-height: calc(56 * var(--us));
    min-width: calc(56 * var(--us));
    padding: calc(12 * var(--us)) calc(20 * var(--us));
    border-radius: 12px;
    font-size: calc(var(--font-size-base) * var(--us));
    font-weight: 700;
}

/* 내 연결 번호(②)·가족 번호 넣기(③) — 목록과 안내 사이에 차례로 놓인다.
   구분선은 목록 항목과 같은 굵기·색이다(도해의 ───── 줄). */
.family-code,
.family-connect {
    margin-top: calc(24 * var(--us));
    padding-top: calc(16 * var(--us));
    border-top: 2px solid var(--color-border);
}

.family-code-heading,
.family-connect-heading {
    font-size: calc(22 * var(--us));
    font-weight: 700;
    color: var(--color-text);
    margin: 0 0 calc(12 * var(--us));
}

/* 🚨 48px는 UI_GUIDE:902(「숫자 입력 (6자리 번호, ADR-030)」)가 정한 값이다 — 기준 폭 360px에서의
      값이고 어르신이 눈으로 읽어 옮겨 적어야 하는 번호다. 🚫 낮추지 마라.
   🚨 자릿수 간격은 letter-spacing으로만 준다 — 🚫 문자열에 공백을 끼워 넣지 마라(화면에 뜬 값이
      서버 값과 달라지고, 앞자리 0인 번호를 그대로 읽어 주지 못한다). */
.family-code-value {
    font-size: calc(48 * var(--us));
    font-weight: 700;
    letter-spacing: calc(8 * var(--us));
    text-align: center;
    color: var(--color-text);
    margin: 0 0 calc(8 * var(--us));
}

/* 🚨 18px는 도해가 정한 보조 문구 값이다(.family-notice와 같은 계열의 명시적 예외). */
.family-code-hint {
    font-size: calc(var(--font-size-sub) * var(--us));
    color: var(--color-sub);
    margin: 0;
}

.family-connect-row {
    display: flex;
    align-items: center;
    gap: calc(12 * var(--us));
}

/* 🚨 min-height 56px는 UI_GUIDE:705 도해가 정한 값이다(터치 타깃 하한 — UI-1). 🚫 줄이지 마라. */
.family-connect-input {
    flex: 1;
    min-width: 0;
    min-height: calc(56 * var(--us));
    padding: calc(12 * var(--us)) calc(16 * var(--us));
    border: 2px solid var(--color-border-soft);
    border-radius: 12px;
    background: var(--color-bg);
    color: var(--color-text);
    font-size: calc(24 * var(--us));
    font-weight: 700;
}

/* .btn-large는 width:100%라 행 안에서 쓸 수 없다 — 색만 물려받고 크기는 여기서 준다
   (.family-disconnect와 같은 이유). */
.family-connect-submit {
    min-height: calc(56 * var(--us));
    min-width: calc(56 * var(--us));
    padding: calc(12 * var(--us)) calc(24 * var(--us));
    border-radius: 12px;
    font-size: calc(var(--font-size-base) * var(--us));
    font-weight: 700;
}

/* 잠금(CODE_EXPIRED) 동안의 회색 쌍 — 저대비 금지선 위다(.consent-next:disabled와 같은 값). */
.family-connect-input:disabled,
.family-connect-submit:disabled {
    background: var(--color-card);
    color: var(--color-disabled);
}

/* [새 번호 넣기] — 잠긴 동안에만 보인다(hidden). 🚫 이 버튼이 서버를 부르지 않는다. */
.family-connect-reset {
    min-height: calc(56 * var(--us));
    margin-top: calc(16 * var(--us));
    padding: calc(12 * var(--us)) calc(20 * var(--us));
    border-radius: 12px;
    font-size: calc(var(--font-size-base) * var(--us));
    font-weight: 700;
}

/* 입력칸 옆 한 줄 — 서버가 준 문장이 그대로 오기도 한다(ADR-092).
   🚫 여기에 문구를 하드코딩하지 마라(문장은 pages/family.js가 소유한다). */
.family-connect-error {
    font-size: calc(var(--font-size-base) * var(--us));
    line-height: 1.5;
    color: var(--color-danger);
    margin: calc(12 * var(--us)) 0 0;
}

/* 기기 변경 안내(ADR-042) — 🚨 18px는 UI_GUIDE 도해가 정한 값이다. 🚫 20px로 올리지 마라:
   본문 하한 20px(UI-1)의 위반이 아니라 보조 문구의 명시적 예외다(.consent-note·.toast와 같은 계열).
   🚨 연결이 0건이어도 이 줄은 남는다 — 숨는 것은 위 목록 블록 하나뿐이다. */
.family-notice {
    font-size: calc(var(--font-size-sub) * var(--us));
    line-height: 1.6;
    color: var(--color-sub);
    margin: calc(24 * var(--us)) 0 0;
}

/* ─────────────── 토스트 (E-6 — UI_GUIDE:653-662 정본 그대로) ───────────────
   구 변경(D-1)·알림 설정 저장(D-2)·가족 공유(C-1)가 함께 쓰는 2초 피드백이다.
   🚨 font-size 18px는 UI_GUIDE가 정한 값이다 — 🚫 20px로 올리지 마라. 본문 하한 20px(UI-1)의
      위반이 아니라, 2초 뒤 사라지는 짧은 피드백이라 본문 규칙의 대상이 아니다(약관 화면의
      .consent-note 18px와 같은 성질의 명시적 예외). 여기서 올리면 두 화면의 토스트가 갈린다.
   🚨 아래 숫자는 **기준 폭 360px에서의 값**이고 폭 배율(--u)이 곱해진다 — 이 띠는 .page-content가
      아니라 화면 몸통에 직접 붙으므로(toast.js) 본문이 아니라 고정 틀이다(ADR-107 결정 2).
   🚫 합성 배율(--us)을 쓰지 마라 — 본문 밖이라 글자 3단이 애초에 닿지 않는다. */
.toast {
    position: fixed;
    top: calc(16 * var(--u));
    left: 50%;
    transform: translateX(-50%);
    background: var(--color-toast-bg);
    color: var(--color-bg);
    font-size: calc(18 * var(--u));
    font-weight: 600;
    padding: calc(12 * var(--u)) calc(24 * var(--u));
    border-radius: 8px;
    z-index: 300;
    margin: 0;
}

/* ─────────────── 공고 상세 C-1 · 공고 없음 C-2 (UI_GUIDE:571-609 정본) ───────────────
   🚨 라벨 행은 목록 카드의 .ann-row를 그대로 쓰고, 상세 전용 조정만 .detail-row로 덧입힌다 —
      두 화면의 라벨 행이 갈라지면 같은 값이 다르게 보인다.
   🚫 색을 원시 16진수로 쓰지 마라 — 위 :root 토큰 밖으로 나가지 않는다(파일 머리의 규칙).
      허용 목록 대조는 원시 16진수도 통과시키므로 초록불이 이 위반을 잡아 주지 않는다. */
.detail-back {
    display: block;
    width: 100%;
    text-align: left;
    margin-bottom: calc(16 * var(--us));
}

.detail-error {
    font-size: calc(var(--font-size-base) * var(--us));
    line-height: 1.5;
    color: var(--color-danger);
    margin: 0 0 calc(16 * var(--us));
}

/* image_url이 있을 때만 그린다. 🚫 없을 때 색 띠로 대체하지 않는다(OD-046). */
.detail-image {
    display: block;
    width: 100%;
    max-height: calc(200 * var(--us));
    object-fit: cover;
    border-radius: 12px;
    margin-bottom: calc(16 * var(--us));
}

.detail-title {
    font-size: calc(25 * var(--us));
    font-weight: 700;
    line-height: 1.4;
    color: var(--color-text);
    margin: calc(8 * var(--us)) 0 calc(4 * var(--us));
}

.detail-region {
    font-size: calc(var(--font-size-sub) * var(--us));
    color: var(--color-sub);
    margin: 0 0 calc(16 * var(--us));
}

.detail-rows {
    border-top: 2px solid var(--color-border);
    padding-top: calc(16 * var(--us));
}

/* 상세는 값이 길어 여러 줄이 된다 — 라벨을 첫 줄에 붙여 둔다. */
.detail-row {
    align-items: flex-start;
    font-size: calc(var(--font-size-base) * var(--us));
    margin-top: calc(12 * var(--us));
}

.detail-row .value {
    line-height: 1.6;
    white-space: pre-line;
}

/* 버튼은 세로로 쌓이고 서로 12px 이상 떨어진다(UI-1). */
.detail-actions {
    display: flex;
    flex-direction: column;
    gap: calc(12 * var(--us));
    margin-top: calc(24 * var(--us));
}

/* 5분 비활성 중에만 보이는 고정 안내(UI_GUIDE 「"가족에게 공유하기" 버튼 동작」 2).
   🚫 남은 시간을 초로 세지 마라 — 서버도 그 값을 주지 않는다(ADR-014). */
.detail-share-note {
    font-size: calc(var(--font-size-sub) * var(--us));
    color: var(--color-sub);
    margin: calc(-4 * var(--us)) 0 0;
    text-align: center;
}

/* 🚫 링크로 만들지 마라 — 터치 동작이 없는 표기다(ADR-044 결정 1). */
.detail-source {
    font-size: calc(var(--font-size-sub) * var(--us));
    color: var(--color-sub);
    margin: calc(24 * var(--us)) 0 0;
}

/* C-2 공고 없음 — 화면 전체가 이것으로 갈린다(UI_GUIDE:603-609). */
.detail-gone {
    text-align: center;
    padding: calc(48 * var(--us)) calc(20 * var(--us));
}

.detail-gone-text {
    font-size: calc(var(--font-size-card-title) * var(--us));
    font-weight: 600;
    line-height: 1.5;
    color: var(--color-text);
    margin: 0 0 calc(24 * var(--us));
}

/* ─────────────── 달력 — B-4 일정 탭의 월 격자 (UI_GUIDE 「### 달력 (ADR-065)」 절 정본) ───────────────
   🚨 날짜 칸만은 가로 56px을 채우지 못한다 — 알고 어기는 것이다(ADR-103 결정 10 → OD-053).
      한 줄에 칸이 일곱 개라 구조적으로 불가능하다: 360 − (16 × 2) = 328, 328 ÷ 7 = 46.9px.
      🚨 이 셈은 **기준 폭 360px에서의 것**이다 — 가로·세로가 같은 배율로 함께 커지므로(ADR-107)
         넓은 기기에서도 비율은 그대로이고 예외의 성질이 바뀌지 않는다.
      그래서 **세로만 56px을 주고 가로는 남는 대로** 둔다(약 47 × 56px).
      🚫 「위반」으로 읽고 한 줄을 6칸으로 줄이거나 가로 스크롤을 넣지 마라 — 달력이 아니게 된다.
      🚫 이 예외를 날짜 칸 밖으로 넓히지 마라 — 달 넘기기 버튼·리스트 항목은 56px을 지킨다.
   🚫 점 색을 카테고리별로 나누지 마라(ADR-047 / UI-14) — .cal-dot 하나뿐이다.
   ⚠️ jsdom은 레이아웃을 계산하지 않으므로 실제 칸 크기는 실기기 육안 확인이 따로 필요하다. */
/* 달 넘기기 줄 (ADR-103 결정 1 / UI_GUIDE 「달력」 절).
   🚨 이 줄은 금지된 「상단 카테고리 바」가 아니다 — 지금 보는 달을 표시·변경한다(UI-4의 세 번째
      예외, 선례 ADR-063 → ADR-091 결정 10). 🚫 「위반」으로 읽고 지우지 마라.
   🚫 버튼을 56px 아래로 줄이지 마라 — 날짜 칸의 가로 예외(위 주석)는 여기까지 넓히지 않는다. */
.cal-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    min-height: calc(var(--touch-target) * var(--us));
    margin-bottom: calc(8 * var(--us));
}

.cal-nav-btn {
    min-height: calc(var(--touch-target) * var(--us));
    min-width: calc(var(--touch-target) * var(--us));
    padding: 0 calc(16 * var(--us));
    font-size: calc(var(--font-size-base) * var(--us));
    color: var(--color-text);
    background: var(--color-bg);
    border: 2px solid var(--color-border);
    border-radius: 12px;
}

/* 범위 끝 — 🚫 숨기지 마라(자리가 밀려 옆 버튼을 잘못 누른다). 색은 표시일 뿐이고 실제 신호는
   `disabled` 속성이다(보조기술이 그것을 읽는다). */
.cal-nav-btn:disabled {
    color: var(--color-disabled);
    border-color: var(--color-border);
    background: var(--color-card);
    cursor: default;
}

.cal-nav-label {
    font-size: calc(var(--font-size-card-title) * var(--us));
    font-weight: 700;
    color: var(--color-text);
}

.cal-weekdays,
.cal-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
}

.cal-weekdays {
    margin: calc(4 * var(--us)) 0;
}

/* 일 빨강 / 토 파랑 / 나머지 진회색 — UI_GUIDE 달력 절 그대로다. */
.cal-wd {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: calc(40 * var(--us));
    font-size: calc(var(--font-size-base) * var(--us));
    font-weight: 700;
    color: var(--color-sub);
}

.cal-wd-sun {
    color: var(--color-danger);
}

.cal-wd-sat {
    color: var(--color-primary);
}

/* 세로 56px은 지킨다 — 가로만 못 지킨다(위 주석). */
.cal-cell {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    min-height: calc(var(--touch-target) * var(--us));
    padding: calc(2 * var(--us)) 0;
}

/* 🚨 공고가 있는 날만 <button>이다(ADR-103 결정 3) — 없는 날과 앞뒤 빈칸은 <div>다.
   버튼 기본 모양(회색 배경·테두리·작은 글꼴)만 되돌린다. 🚫 새 색을 쓰지 마라. */
button.cal-cell {
    background: var(--color-bg);
    border: none;
    font-family: inherit;
    cursor: pointer;
}

.cal-day {
    display: flex;
    align-items: center;
    justify-content: center;
    width: calc(36 * var(--us));
    height: calc(36 * var(--us));
    font-size: calc(var(--font-size-base) * var(--us));
    color: var(--color-text);
}

/* 오늘 강조 — 원형 채움(UI_GUIDE 달력 절). */
.cal-today {
    background: var(--color-primary);
    color: var(--color-bg);
    border-radius: 50%;
}

.cal-dot-wrap {
    display: flex;
    justify-content: center;
    gap: calc(3 * var(--us));
    margin-top: calc(3 * var(--us));
}

.cal-dot {
    width: calc(7 * var(--us));
    height: calc(7 * var(--us));
    border-radius: 50%;
    background: var(--color-primary);
}

/* 「외 N건」 — 점 3개로 다 못 담은 나머지를 글자로 알린다.
   ⚠️ 20px(UI-1 하한)을 지키면 가장 좁은 기기의 47px 칸보다 글자가 넓어 그 줄만 두 줄이 된다.
      칸 안에 갇히는 쪽을 골랐다(🚫 nowrap으로 옆 칸을 침범하게 만들지 마라). OD-053과 같은 자리에서
      실기기 확인이 필요하다. 🚫 하한을 내려 20px 미만으로 줄이지 마라. */
.cal-more {
    font-size: calc(var(--font-size-base) * var(--us));
    line-height: 1.2;
    color: var(--color-sub);
    text-align: center;
}

/* 그 달에 공고가 하나도 없을 때의 안내 한 줄(ADR-103 결정 8 — 문구는 화면 모듈이 쥔다). */
.cal-empty {
    font-size: calc(var(--font-size-base) * var(--us));
    color: var(--color-sub);
    text-align: center;
    padding: calc(24 * var(--us)) 0;
    margin: 0;
}

/* ── 그리드 아래 「이번 달 주요 일정」 리스트 (ADR-103 결정 2·6 / UI_GUIDE 「달력」 절) ──
   .cal-list-item은 UI_GUIDE 달력 절 CSS 블록의 스니펫이 정본이다 — 버튼으로 만들었으므로
   기본 글자·정렬·너비만 되돌려 준다.
   🚨 항목은 56px을 지킨다 — 날짜 칸의 가로 예외(위 주석 · OD-053)를 여기까지 넓히지 마라.
   🚫 카테고리를 `.tag-category` 배지로 그리지 마라 — 그 배지는 세 카테고리가 섞이는 복지 탭의
      것이다(UI-31). 여기서는 글자 한 덩어리다. */
.cal-list-title {
    font-size: calc(var(--font-size-card-title) * var(--us));
    font-weight: 700;
    color: var(--color-text);
    margin: calc(24 * var(--us)) 0 calc(12 * var(--us));
}

.cal-list-item {
    display: flex;
    gap: calc(12 * var(--us));
    align-items: center;
    width: 100%;
    min-height: calc(var(--touch-target) * var(--us));
    padding: calc(14 * var(--us)) calc(16 * var(--us));
    margin-bottom: calc(10 * var(--us));
    background: var(--color-bg);
    border: 2px solid var(--color-border);
    border-radius: 12px;
    font-size: calc(var(--font-size-base) * var(--us));
    color: var(--color-text);
    text-align: left;
}

/* 날짜 상자 — 줄바꿈으로 「8」과 「일」이 갈리지 않게 폭을 고정한다. */
.cal-list-date {
    flex: 0 0 auto;
    min-width: calc(56 * var(--us));
    font-weight: 700;
    color: var(--color-text);
    text-align: center;
}

.cal-list-cat {
    flex: 0 0 auto;
    color: var(--color-sub);
}

/* 제목은 남는 폭을 다 쓰고 길면 줄바꿈한다 — 🚫 말줄임으로 잘라내지 마라(무슨 공고인지 사라진다). */
.cal-list-name {
    flex: 1 1 auto;
    color: var(--color-text);
    word-break: keep-all;
}

/* ── 날짜 시트 (ADR-103 결정 3 / UI_GUIDE 「달력」 절 「달력 화면 유지」) ──
   겉모양은 위 `.popup-overlay`·`.popup-sheet`를 **그대로 재사용**한다 — 🚫 새 오버레이 CSS를
   만들지 마라(시니어에게 시트가 두 종류로 보이면 안 된다). 여기 있는 것은 시트 **안쪽**뿐이다.
   🚨 항목은 리스트와 같은 `.cal-list-item`을 쓴다(축약 렌더 한 벌 — 56px을 지킨다).
   하루에 공고가 많으면 화면보다 길어지므로 시트 높이를 묶고 안에서 스크롤시킨다
   (약관 시트가 같은 사정으로 같은 처리를 한다). */
.cal-sheet {
    max-height: 70vh;
    overflow-y: auto;
}

.cal-sheet-title {
    font-size: calc(var(--font-size-card-title) * var(--us));
    font-weight: 700;
    color: var(--color-text);
    text-align: center;
    margin: 0 0 calc(20 * var(--us));
}

.cal-sheet-list {
    margin-bottom: calc(20 * var(--us));
}

/* 서버가 준 문장을 그대로 담는 자리(ADR-092). 🚫 여기에 문구를 하드코딩하지 마라. */
.cal-error {
    font-size: calc(var(--font-size-base) * var(--us));
    color: var(--color-danger);
    margin: 0 0 calc(16 * var(--us));
}
