/* moa — 팀 기록 로그 뷰어.
 *
 * 색은 tokens.css(Wanted Design System)에서 온다. 컴포넌트는 변수만 쓴다 —
 * 규칙(#378) 개정 전과 같다. 바뀐 것은 moa 테마 덮어쓰기를 허용한 것 하나다:
 * tokens.css는 도구가 다시 내보내는 자동 생성물이라 거기에 손을 대면 다음
 * 내보내기에 날아간다. 그래서 덮어쓸 값은 아래 :root의 「moa 테마」 한 블록에만
 * 적는다(tokens.css 다음에 읽히므로 이긴다). 그 블록 밖에서는 여전히 색을
 * 적지 않는다 — 값이 파일 곳곳에 흩어지는 것을 막자는 원래 취지는 그대로다.
 * 근거는 docs/결정/결정기록_테마색.md.
 *
 * 간격·타이포 스케일은 Wanted 변수에 없어서 여기 둔다. 4px 격자와
 * Pretendard 기준이다.
 *
 * 화면은 **피드**다. 보고서가 아니라 서로 보는 자리라서, 사람이 먼저
 * 오고 글이 뒤에 온다. 색은 프로젝트에만 쓴다 — 사람에게 색이나 게이지를
 * 붙이는 순간 그건 감시가 된다.
 */

:root {
  /* moa 테마. 자동 생성된 tokens.css를 다시 내보내도 유지한다. */
  --background-normal-alternative: #fafbff;
  --label-alternative: #5b6472;
  --line-solid-neutral: #e4eaf2;
  --line-solid-normal: #dce3ed;
  --surface-selected: #eef4ff;
  --surface-selected-hover: #e3edff;
  --orbit-background: #f0f6ff;
  --orbit-edge: #fafcff;
  --orbit-line: #dce7f5;

  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 20px; --sp-6: 24px; --sp-7: 32px; --sp-8: 48px;

  --fs-cap: 12px;  --lh-cap: 16px;
  --fs-sm: 13px;   --lh-sm: 18px;
  --fs-md: 15px;   --lh-md: 25px;   /* 본문. 긴 한글 문단이라 넉넉하게 */
  --fs-lg: 17px;   --lh-lg: 24px;
  --fs-xl: 24px;   --lh-xl: 32px;

  --r-sm: 8px; --r-md: 14px; --r-pill: 999px;
  --avatar: 40px;
  --rail: 44px;
}

/* 서체는 리포에 넣고 직접 준다(SIL Open Font License). 폴백을 두지 않는
 * 이유는 폴백이 곧 "사람마다 다른 화면"이기 때문이다 — 못 받으면 안 받은
 * 티가 나야 고친다. */
@font-face {
  font-family: "Pretendard Variable";
  src: url("/static/fonts/PretendardVariable.woff2") format("woff2-variations");
  font-weight: 45 920;
  font-style: normal;
  font-display: swap;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--background-normal-alternative);
  color: var(--label-normal);
  font-family: "Pretendard Variable", sans-serif;
  font-size: var(--fs-md); line-height: var(--lh-md);
  -webkit-font-smoothing: antialiased;
}

/* 한글은 어절 중간에서 끊지 않는다. 문장·단어 경계에서만 넘긴다. */
body, .prose, .post__text { word-break: keep-all; overflow-wrap: anywhere; }

a { color: var(--primary-normal); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }
:focus-visible {
  outline: 2px solid var(--primary-normal); outline-offset: 2px; border-radius: var(--r-sm);
}

/* --label-assistive는 28% 불투명도라 흰 배경에서 대비가 거의 안 난다
 * (~1.7:1) — 진짜로 안 읽혀도 되는 것(placeholder·비활성 상태·장식
 * 아이콘)에만 남기고, 사람이 실제로 읽어야 하는 보조 글자는 61%인
 * --label-alternative로 올린다(~4.7:1, WCAG AA 통과). */
.meta { font-size: var(--fs-cap); line-height: var(--lh-cap); color: var(--label-alternative);
  font-variant-numeric: tabular-nums; }

/* 빵부스러기. .meta(12px)를 그대로 쓰면 실제로 누르는 길 안내인데도
 * 캡션처럼 작아서 눈에 안 띈다는 지적 — 본문 크기로 올린다. */
.crumbs { font-size: var(--fs-md); color: var(--label-alternative); margin: 0 0 var(--sp-2); }

/* ── 메뉴(GNB) ────────────────────────────────────
 * 넓은 화면은 왼쪽 세로, 좁은 화면은 접히는 머리.
 * 마크업은 한 벌이고 여기서 두 모습으로 가른다 — 두 벌 두면
 * 하나만 고치는 실수가 난다.
 */
.shell { display: grid; grid-template-columns: 200px 1fr; min-height: 100vh; }
/* 본문 칸이 min-width:auto로 커져 넓은 표가 메뉴를 밀어내는 것을 막는다. */
.shell__body { min-width: 0; }

.gnb {
  position: sticky; top: 0; align-self: start; height: 100vh; z-index: 5;
  display: flex; flex-direction: column; gap: var(--sp-5);
  padding: var(--sp-5) var(--sp-4);
  /* 배경이 불투명해져서 backdrop-filter는 뺐다 — 뒤가 비치지 않으면 블러가
     할 일이 없다. 다시 반투명으로 돌리면 그때 같이 되살린다. */
  background: var(--background-normal-normal);
  border-right: 1px solid var(--line-solid-neutral);
}
.gnb__brand { display: flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-lg); font-weight: 800; color: var(--label-normal); }
.gnb__brand:hover { text-decoration: none; }
.gnb__mark { display: block; }
.gnb__word { letter-spacing: -.03em; }
/* 브랜드 아래에 붙인다. 옆에 두면 좁은 화면에서 먼저 잘려 나간다. */
.gnb__tag { margin: calc(var(--sp-3) * -1) 0 0; font-size: var(--fs-sm);
  font-weight: 600; color: var(--label-alternative); }
.gnb .search { width: 100%; }
/* 돋보기를 입력칸 안에 넣는다. 자료실·주간처럼 빈 검색칸이 심심해 보이던
 * 것과 같은 지적이라 여기도 아이콘을 준다(#153 UI 정리). */
.gnb__search { position: relative; }
.gnb__search-icon { position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
  color: var(--label-assistive); pointer-events: none; }
.gnb .search { padding-left: 34px; }

/* 넓은 화면에서는 접는 손잡이를 숨기고 내용만 편다.
 * details는 안 열려 있으면 브라우저가 내용을 감춘다 — 자식에 display를 줘도
 * 안 나온다. ::details-content로 그 감춤을 푼다.
 *
 * **미디어쿼리 안에 둬야 한다.** 밖에 두면 좁은 화면에서도 감춤이 풀려
 * 메뉴가 늘 열린 채가 되고, 햄버거를 눌러도 아무 일이 안 일어난다. */
@media (min-width: 861px) {
  .gnb__fold > .gnb__sum { display: none; }
  .gnb__fold::details-content { content-visibility: visible; block-size: auto; }

  /* 벨이 메뉴 목록 맨 아래 프로필 줄 밑에 아이콘 하나만 따로 한 줄 차지하고
   * 떠 있어 자리가 어색하다는 지적(실사용) — 마크업은 그대로 두고(모바일
   * 접힌 메뉴 안 배치는 그대로 써야 한다) .gnb가 이미 sticky라 위치 기준이
   * 되니, 넓은 화면에서만 로고 줄 오른쪽 끝으로 떼어 붙인다.
   * .gnb .gnb__bell로 명시도를 올린다 — 뒤에 나오는 .vault__menu{position:
   * relative}가 클래스 하나짜리라 순서상 그냥 .gnb__bell을 덮어썼었다. */
  .gnb .gnb__bell { position: absolute; top: var(--sp-5); right: var(--sp-4); }
}

/* 그 선택자를 모르는 브라우저에서는 넓은 화면에서도 손잡이를 보인다.
 * 메뉴가 통째로 사라지느니 접었다 펴는 손잡이가 하나 있는 편이 낫다.
 * CSS는 시험이 안 되므로 실패하는 쪽을 안전하게 정해 둔다. */
@supports not (selector(::details-content)) {
  .gnb__fold > .gnb__sum { display: flex !important; align-items: center; gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3); cursor: pointer; list-style: none; }
  .gnb__fold > .gnb__sum::-webkit-details-marker { display: none; }
  .gnb__burger { display: grid; place-items: center; width: 32px; height: 32px;
    margin-left: auto; color: var(--label-alternative); }
}
.gnb__menu { display: flex; flex-direction: column; gap: 2px; }
.gnb__item { display: flex; align-items: center; gap: var(--sp-3);
  padding: 8px var(--sp-3); border-radius: 8px;
  font-size: 14px; font-weight: 600; color: var(--label-alternative); }
.gnb__item:hover { background: var(--fill-alternative); text-decoration: none; }
/* 선택된 메뉴는 밝은 블루 면과 진한 파랑 글자로 구분한다.
 *
 * **면이 아니라 글자색이 신호다(#378).** 면(#eef4ff)은 페이지 배경과 대비가
 * 1.07:1이라 면만으로는 뭐가 켜졌는지 알기 어렵다. 진한 파랑 채움도 띄워
 * 봤지만 사이드바 한 줄이 통째로 파래져 너무 무거웠다. 글자는 5.00:1이라
 * 읽는 데 문제가 없고, 색만으로 알리지 않도록 aria-current를 같이 둔다.
 * 자세한 것은 docs/결정/결정기록_테마색.md. */
.gnb__item.is-on { background: var(--surface-selected); color: var(--primary-strong); }
.gnb__item.is-on:hover { background: var(--surface-selected-hover); }
.gnb__item-icon { display: block; width: 20px; height: 20px;
  flex: 0 0 20px; color: var(--label-alternative); }
.gnb__item.is-on .gnb__item-icon { color: var(--primary-strong); }

/* 알림 배지·드롭다운(#248). .vault__menu 그대로 쓰되, 사이드바 폭이
 * 300px보다 좁아 원래의 right:0 정렬로는 패널이 화면 밖으로 나간다 —
 * left:0으로 왼쪽에 붙이고 폭만 그대로 둔다. */
.gnb__bell { align-self: flex-start; }
.gnb__bell .vault__menu-btn { position: relative; width: 32px; height: 32px; }
.gnb__bell-count {
  position: absolute; top: 2px; right: 2px; min-width: 15px; height: 15px;
  padding: 0 3px; border-radius: var(--r-pill);
  background: var(--danger, #e52222); color: var(--static-white);
  font-size: 10px; font-weight: 700; line-height: 15px; text-align: center;
}
.gnb__bell-panel { left: 0; right: auto; max-height: 70vh; overflow-y: auto; }
.gnb__bell-item { display: flex; flex-direction: column; gap: 2px;
  padding: var(--sp-2); border-radius: var(--r-sm); text-decoration: none; }
.gnb__bell-item:hover { background: var(--fill-normal); }
.gnb__bell-title { color: var(--label-normal); font-size: var(--fs-sm);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gnb__bell-empty { padding: var(--sp-2); }

/* 이동 목록 아래에 붙되 선 하나로 갈라 둔다 — 이어 붙으면 로그아웃이
 * 메뉴 항목 하나처럼 보인다. */
/* 버전(#385). 사이드바 맨 아래, 길을 안 막는 자리다. */
/* label-assistive는 흰 바탕에서 대비가 1.68:1이라 거의 안 보인다(#385 리뷰).
   버전은 「내가 보는 게 언제 것인가」에 답하려고 두는 것이라 읽혀야 한다. */
.gnb__ver { margin: var(--sp-2) 0 0; padding: 0 var(--sp-3);
  font-size: var(--fs-cap); color: var(--label-alternative); }
.gnb__me { display: flex; align-items: center; gap: var(--sp-2);
  margin-top: var(--sp-4); padding-top: var(--sp-4);
  border-top: 1px solid var(--line-solid-neutral); }
.gnb__out-label { display: none; }
.gnb__bell-label { display: none; }
.gnb__name { display: flex; align-items: center; gap: var(--sp-2); min-width: 0;
  font-size: var(--fs-sm); font-weight: 600; color: var(--label-alternative); }
.gnb__out, .gnb__find {
  display: grid; place-items: center; width: 30px; height: 30px;
  border-radius: var(--r-pill); color: var(--label-assistive);
}
.gnb__out:hover, .gnb__find:hover { background: var(--fill-normal); color: var(--label-normal); }
.gnb__find, .gnb__burger { display: none; }

/* ── 좁은 화면: 머리로 접힌다 ───────────────────── */
@media (max-width: 860px) {
  /* 행 높이를 안 정하면 auto 행 둘이 남는 공간을 나눠 가져 머리 밑이 텅 빈다. */
  .shell { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
  .gnb {
    position: static; height: auto; padding: 0;
    border-right: 0; border-bottom: 1px solid var(--line-solid-neutral);
    gap: 0;
  }
  /* 브랜드와 검색창은 접히는 머리가 대신한다 — 지금 자리 이름이 브랜드다. */
  .gnb__brand, .gnb__tag, .gnb__search { display: none; }

  /* .gnb와 같은 이유로 블러 없음 — 배경이 불투명하다. */
  .gnb__fold { position: sticky; top: 0;
    background: var(--background-normal-normal); }
  .gnb__fold > .gnb__sum {
    display: flex; align-items: center; gap: var(--sp-2);
    padding: var(--sp-3) var(--sp-4); cursor: pointer;
    /* 두 줄 다 있어야 한다. 하나만 쓰면 한쪽 브라우저에 삼각형이 남는다. */
    list-style: none;
  }
  .gnb__fold > .gnb__sum::-webkit-details-marker { display: none; }
  .gnb__here { font-size: var(--fs-lg); font-weight: 800; color: var(--label-normal);
    letter-spacing: -.025em; }
  .gnb__find { display: grid; margin-left: auto; }
  .gnb__burger { display: grid; place-items: center; width: 32px; height: 32px;
    color: var(--label-alternative); }

  /* 펼치면 본문을 밀어 내린다. 덮으면 뒤에 뭐가 있었는지 잃는다. */
  .gnb__menu { padding: 0 var(--sp-4); }
  .gnb__item { padding: 12px var(--sp-3); }
  /* 펼친 메뉴 안에서는 아이콘만으로 안 된다. 글자를 같이 보인다. */
  .gnb__me { margin: var(--sp-3) var(--sp-4) 0; padding: var(--sp-3) var(--sp-3) var(--sp-4); }
  .gnb__name { font-size: 14px; }
  .gnb__out { width: auto; padding: 0 var(--sp-2); gap: var(--sp-2); margin-left: auto;
    grid-auto-flow: column; }
  .gnb__out-label { display: inline; font-size: 14px; font-weight: 600; }

  /* 접힌 메뉴에서는 벨이 아이콘 하나만 덩그러니 남아 있어서, 있는지도
   * 몰랐다는 지적(실사용) — 다른 줄처럼 폭 전체를 쓰고 글자를 붙인다. */
  .gnb__bell { align-self: stretch; margin: 0 var(--sp-4) var(--sp-3); }
  .gnb__bell .vault__menu-btn {
    width: auto; height: auto; justify-content: flex-start; gap: var(--sp-2);
    padding: 12px var(--sp-3); border-radius: 8px; color: var(--label-alternative);
  }
  .gnb__bell-label { display: inline; font-size: 14px; font-weight: 600; }
  .gnb__bell-count { position: static; margin-left: auto; }
}

.search {
  width: 220px; border: 0; border-radius: var(--r-pill); background: var(--fill-normal);
  padding: var(--sp-2) var(--sp-4); font: inherit; font-size: 14px; color: var(--label-normal);
}
.search::placeholder { color: var(--label-assistive); }

/* 일반 입력 칸. .search(알약 모양, 찾기 창 전용)를 이름·사유 같은 보통 값
 * 칸에도 돌려 쓰다 보니 폼마다 알약이 늘어서 어색했다 — 각진 모양으로 분리. */
.field { display: block; width: 100%; max-width: 360px; box-sizing: border-box;
  border: 1px solid var(--line-solid-neutral); border-radius: var(--r-sm);
  background: var(--background-normal-normal); padding: var(--sp-2) var(--sp-3);
  font: inherit; font-size: 14px; color: var(--label-normal); }
.field::placeholder { color: var(--label-assistive); }
.field:focus { outline: 0; border-color: var(--primary-normal);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-normal) 12%, transparent); }

/* 부차적인 자리(케밥 메뉴 속 "사진 추가"·"md 다시 올리기", 공지 사진
 * 첨부)의 파일 입력은 JS로 숨기지 않는다 — 자료실 "올리기"·회의록 "파일
 * 올리기" 같은 주력 업로드만 vault.js/meetings.js로 숨긴 input +
 * .vault__act 버튼을 쓴다. 대신 브라우저 기본 버튼 부분만 순수 CSS로
 * 다시 입혀 촌스러운 회색 버튼을 없앤다 — 마크업·JS 추가 없이 전체
 * 화면에 한 번에 적용된다. */
input[type="file"]::file-selector-button {
  margin-right: var(--sp-2); padding: 7px 14px; cursor: pointer;
  border: 1px solid var(--line-solid-normal); border-radius: var(--r-sm);
  background: var(--background-normal-normal); color: var(--label-normal);
  font-family: inherit; font-size: var(--fs-sm); font-weight: 600;
}
input[type="file"]::file-selector-button:hover { background: var(--fill-alternative); }
/* **기본 화살표를 끄고 직접 그린다(#368).** select의 native 화살표는
 * 테두리 바로 안쪽에 그려지고 .field의 오른쪽 padding은 그 바깥이라,
 * padding만 키우면 글자만 멀어지고 화살표는 끝에 붙은 채로 남는다.
 * ::file-selector-button을 CSS만으로 다시 입힌 것과 같은 길이다 — 마크업도
 * JS도 안 늘리고 모든 화면의 select에 한 번에 먹는다.
 *
 * **선이 아니라 그라디언트 두 겹으로 그린다.** select는 자식을 못 가져
 * 인라인 SVG를 넣을 수 없고, 외부 svg나 data URI는 토큰 색을 못 문다 —
 * 색을 직접 적지 않는다는 이 파일 머리말 규칙을 지키려면 이 방법뿐이다. */
select.field {
  cursor: pointer;
  appearance: none; -webkit-appearance: none;
  padding-right: var(--sp-7);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--label-alternative) 50%),
    linear-gradient(135deg, var(--label-alternative) 50%, transparent 50%);
  background-position: right 17px top 50%, right 12px top 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
select.field:disabled { background-image: none; }
/* 날짜칸의 달력 아이콘도 같은 이유로 테두리에 붙는다. 이쪽은 기본 UI를
 * 끄면 달력을 열 길이 없어지므로 자리만 안쪽으로 들인다.
 *
 * **밀어내는 것은 padding-right다(#368 리뷰).** 아이콘은 content box의
 * 오른쪽 끝에 앉으므로 padding이 곧 테두리와의 거리다 — indicator에
 * margin-left를 주면 글자와만 벌어지고 아이콘은 테두리에 붙은 채다.
 * 처음에 그렇게 썼다가 재 보고 고쳤다(날짜칸 12px, select 32px). */
input[type="date"].field { padding-right: var(--sp-5); }
input[type="date"].field::-webkit-calendar-picker-indicator { cursor: pointer; opacity: .55; }
input[type="date"].field::-webkit-calendar-picker-indicator:hover { opacity: 1; }

/* 조작이 됐다는 걸 알려주는 토스트. 색은 프로젝트에만 쓰므로 초록 등으로
 * "성공"을 표시하지 않는다 — 짙은 중립색 한 가지로 통일한다. */
.toast {
  position: fixed; left: 50%; bottom: var(--sp-6); transform: translateX(-50%);
  background: var(--label-normal); color: var(--background-normal-normal);
  padding: 10px 18px; border-radius: var(--r-md); font-size: var(--fs-sm);
  font-weight: 600; box-shadow: 0 8px 24px #00000026; z-index: 100;
  animation: toast-in .2s ease, toast-out .25s ease 2.75s forwards;
}
@keyframes toast-in { from { opacity: 0; transform: translate(-50%, 8px); } }
@keyframes toast-out { to { opacity: 0; transform: translate(-50%, 8px); } }

/* 공유 링크 생성 직후 배너. 일반 토스트와 달리 스스로 안 사라진다 —
 * 자동 클립보드 복사가 페이지 새로고침 뒤라 거의 매번 조용히 실패해서
 * (실사용 지적, 재현됨) 링크를 그대로 보여주고 직접 눌러 복사하게
 * 바꿨다 — 누르기 전에 사라지면 아무 의미가 없다. */
.toast--share {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2);
  max-width: min(90vw, 520px); animation: toast-in .2s ease;
}
.toast--share__link {
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; color: var(--background-normal-normal); opacity: .85;
}
/* .btn--quiet의 기본 회색은 밝은 배경 전제라 이 짙은 토스트 위에서는
 * 잘 안 보인다 — 이 안에서만 밝은 글자·테두리로 뒤집는다.
 *
 * **.toast .toast--share__copy로 명시도를 올린다.** 클래스 하나짜리로 두면
 * 827행의 .btn--quiet가 뒤에 나와 순서로 이겨버린다 — 버튼이
 * class="btn btn--quiet toast--share__copy"라 회색 글자·회색 테두리가
 * 그대로 먹고, 짙은 배경 위라 글씨가 안 보였다(실사용 지적).
 *
 * 닫기(×)는 지금 btn 계열 클래스가 없어 밀린 적이 없다 — 나중에 붙어도
 * 같은 일이 안 생기게 예방으로 함께 올려 둔다. */
.toast .toast--share__copy {
  flex: 0 0 auto; border: 1px solid var(--background-normal-normal);
  color: var(--background-normal-normal); background: transparent;
}
.toast .toast--share__copy:hover { background: color-mix(in srgb, var(--background-normal-normal) 15%, transparent); }
.toast .toast--share__close {
  flex: 0 0 auto; width: 24px; height: 24px; border: 0; border-radius: var(--r-pill);
  background: transparent; color: var(--background-normal-normal); opacity: .7;
  font-size: 16px; line-height: 1; cursor: pointer;
}
.toast .toast--share__close:hover { opacity: 1; background: color-mix(in srgb, var(--background-normal-normal) 15%, transparent); }

/* ── 날짜줄 ───────────────────────────────────────── */
/* 글을 읽는 화면(일간·주간)은 한 줄이 길면 눈이 되돌아오기 어렵다 — 그래서
 * 가장 덜 넓힌다. 목록 화면(자료실·회의록·찾기)은 한 줄에 여러 칸이
 * 서므로 좁히면 오히려 글자가 접힌다 — `.page--list`로 더 넓힌다. */
/* **폭은 화면에 따라 늘어난다(#302).** 고정 픽셀로 두면 2560 모니터에서
   가로 빈 공간이 크게 남는다. 다만 글줄이 길면 읽기 나빠지므로, 글이 주인인
   화면은 덜 늘리고 목록·문서처럼 표가 들어오는 화면은 더 늘린다. */
/* **일간·주간은 1080px이다(#378).** 900px로 두면 넓은 모니터에서 여백만
   남는다는 지적이 있었는데, 카드가 한 열이라 넓힌 만큼 글줄이 그대로 길어진다
   — 1200px까지 가면 #302가 막으려던 문제로 돌아간다. 둘 사이를 잡았다. */
.page { max-width: min(100%, 900px); margin: 0 auto;
  padding: var(--sp-5) var(--sp-5) var(--sp-8); }
.page--feed { max-width: min(100%, 1080px); }
.page--list { max-width: min(100%, 1400px); }
/* 목록 줄은 가로로 길어 위아래가 촘촘해도 읽힌다. */
.page--list .stack { gap: var(--sp-2); }

/* 프로젝트가 열 개를 넘어 알약으로 못 늘어놓는다. 선택 상자로 고른다. */
.pick { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.pick .search { width: auto; min-width: 200px; }
.daybar { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-3);
  margin-bottom: var(--sp-3); }
.daybar__day { margin: 0; font-size: var(--fs-xl); line-height: var(--lh-xl);
  font-weight: 800; letter-spacing: -.025em; white-space: nowrap;
  font-variant-numeric: tabular-nums; }
.daybar__wd { font-size: 14px; color: var(--label-assistive); font-weight: 600;
  white-space: nowrap; }

/* 찾기 화면 안 검색칸. 넓은 화면은 위 nav 검색칸으로 충분해 여기선 숨긴다 —
 * 좁은 화면(860px 이하, nav 검색칸이 숨는 기준과 같다)에서만 보인다(#199). */
.search-page__form { display: none; gap: var(--sp-2); margin-bottom: var(--sp-4); }
@media (max-width: 860px) {
  .search-page__form { display: flex; }
  .search-page__form .field { flex: 1 1 auto; max-width: none; }
  .search-page__form .btn { flex: none; white-space: nowrap; }
}
.daynav { margin-left: auto; display: flex; align-items: center; gap: var(--sp-1); }
.daynav__form { display: flex; }
.daynav__step {
  display: grid; place-items: center; width: 30px; height: 30px;
  border-radius: var(--r-pill); color: var(--label-alternative); font-size: var(--fs-sm);
}
.daynav__step:hover { background: var(--fill-normal); text-decoration: none; }
.daynav__pick {
  border: 1px solid var(--line-solid-normal); background: var(--background-normal-normal);
  border-radius: var(--r-pill); padding: 5px var(--sp-3);
  font: inherit; font-size: var(--fs-sm); font-weight: 600; color: var(--label-alternative);
  color-scheme: light;
}

/* ── 오늘의 스펙트럼 ──────────────────────────────── */
/* 그날 팀이 어느 일에 있었는지의 비율. 사람이 아니라 일을 잰다. */
.spectrum { display: flex; gap: 3px; height: 8px; margin-bottom: var(--sp-2); }
.spectrum__seg { border-radius: var(--r-pill); min-width: 6px; }
.legend { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4); margin: 0 0 var(--sp-6);
  padding: 0; list-style: none; }
.legend__item { display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-cap); font-weight: 600; color: var(--label-alternative); }
.legend__item b { font-weight: 600; color: var(--label-assistive); }
.dot { width: 8px; height: 8px; border-radius: var(--r-pill); flex: none; }

/* ── 피드 ─────────────────────────────────────────── */
/* 일간의 "얼굴+이어지는 선" 레일은 #153에서 카드형으로 바꾸며 걷어냈다 —
   .post/.post__head/.post__body/.post__who는 unclassified.html·weekly.html이
   아직 쓰고 있어 남긴다. */
.post { display: grid; grid-template-columns: var(--rail) 1fr; gap: var(--sp-3); }
.avatar { width: var(--avatar); height: var(--avatar); border-radius: var(--r-pill);
  display: block; background: var(--fill-normal); object-fit: cover; }
.avatar--text { display: grid; place-items: center; font-size: var(--fs-sm); font-weight: 700;
  color: var(--label-alternative); }
.post__body { min-width: 0; padding-bottom: var(--sp-6); }
.post__head { display: flex; align-items: baseline; gap: var(--sp-2); margin-bottom: 7px; }
.post__who { font-size: var(--fs-md); font-weight: 700; letter-spacing: -.01em;
  color: var(--label-normal); }
/* 자료실·회의록·KB 활동 줄(#활동통합) — 요약 문단과 구분되게 작고
 * 옅게, 링크는 굳이 다른 색을 안 써서 프로젝트 태그 색과 안 헷갈리게. */
.post__activity { margin: var(--sp-2) 0 0; padding-left: 1.2em; }
.post__activity li { font-size: var(--fs-sm); color: var(--label-alternative); }
.post__activity a { color: inherit; }
/* 프로젝트 색. 채운 알약에 흰 글자를 얹으면 대비가 3:1 근처라 12px가 안
 * 읽힌다. 연한 바탕 + 진한 글자로 간다 — 대비도 되고 눈도 덜 피로하다.
 * 색 이름은 app/web/feed.py의 HUES와 짝이다. 한쪽만 늘리면 색이 안 나온다. */
.tag {
  display: inline-block; margin-bottom: 9px; margin-right: var(--sp-1); padding: 3px 10px;
  border-radius: var(--r-pill); font-size: var(--fs-cap); font-weight: 700;
  background: color-mix(in srgb, var(--hue) 12%, var(--background-normal-normal));
  color: var(--hue);
}
.tag:hover { text-decoration: none;
  background: color-mix(in srgb, var(--hue) 20%, var(--background-normal-normal)); }
.dot, .spectrum__seg { background: var(--hue); }

.hue-violet      { --hue: var(--accent-foreground-violet); }
.hue-lime        { --hue: var(--accent-foreground-lime); }
.hue-cyan        { --hue: var(--accent-foreground-cyan); }
.hue-pink        { --hue: var(--accent-foreground-pink); }
.hue-purple      { --hue: var(--accent-foreground-purple); }
.hue-red-orange  { --hue: var(--accent-foreground-red-orange); }
.hue-light-blue  { --hue: var(--accent-foreground-light-blue); }
.hue-green       { --hue: var(--accent-foreground-green); }
.hue-orange      { --hue: var(--accent-foreground-orange); }
.hue-blue        { --hue: var(--accent-foreground-blue); }
.hue-red         { --hue: var(--accent-foreground-red); }

/* ── 요약문 ───────────────────────────────────────── */
.prose { margin: 0; color: var(--label-neutral); }
.prose p { margin: 0 0 var(--sp-3); }
/* 요약문은 마크다운이라 제목이 올 수 있다. 크기만 커지고 행간은 본문값을
 * 물려받으면 줄이 겹친다 — 실제로 겹쳐 보였다. 크기마다 행간을 짝지어 준다. */
.prose h1, .prose h2, .prose h3, .prose h4 {
  margin: var(--sp-5) 0 var(--sp-2); color: var(--label-normal);
  font-weight: 700; letter-spacing: -.01em;
}
.prose h1:first-child, .prose h2:first-child,
.prose h3:first-child, .prose h4:first-child { margin-top: 0; }
.prose h1 { font-size: var(--fs-lg); line-height: 24px; }
.prose h2 { font-size: var(--fs-md); line-height: 22px; }
.prose h3, .prose h4 { font-size: 14px; line-height: 20px; }
.prose li { margin-bottom: var(--sp-1); }
.prose blockquote { margin: 0 0 var(--sp-3); padding-left: var(--sp-4);
  border-left: 2px solid var(--line-solid-normal); color: var(--label-alternative); }
.prose p:last-child { margin-bottom: 0; }
.prose strong { color: var(--label-normal); font-weight: 700; }
.prose ul, .prose ol { margin: 0 0 var(--sp-3); padding-left: var(--sp-5); }
.prose code {
  font-size: var(--fs-sm); padding: 1px 5px; border-radius: 5px;
  background: var(--fill-normal); font-family: ui-monospace, SFMono-Regular, monospace;
}

/* ── 표(GFM) — markdown-it-py의 "gfm-like" 프리셋에 이미 있다 ────── */
/* **표는 감싼 칸이 가로로 민다(#302).** width:100%로 칸에 욱여넣으면 열이
   많을 때 글자가 한 자씩 세로로 쌓여 못 읽는다 — 8열짜리에서 "페/이/지",
   "P/G/N"이 됐다. 표는 제 내용만큼 넓히고, 넘치면 그 칸만 스크롤한다. */
.prose__wide { overflow-x: auto; max-width: 100%; margin: 0 0 var(--sp-3); }
.prose table {
  width: max-content; min-width: 100%; max-width: none;
  border-collapse: collapse; font-size: var(--fs-sm);
}
/* 칸이 너무 좁아 글자가 세로로 쌓이는 것을 막는다. */
.prose th, .prose td { min-width: 4.5em; }
.prose th, .prose td {
  padding: var(--sp-2) var(--sp-3); text-align: left;
  border-bottom: 1px solid var(--line-solid-alternative);
}
.prose th { color: var(--label-normal); font-weight: 700; background: var(--fill-alternative); }

/* ── 지식창고 문서의 표(#447) — **.kb-prose 안에서만** ────────────────────────────────
 * 긴 문장이 든 표를 읽을 수 있게 한다. 실측(기준 문서 표 8개): 상한 없이는 8개가 전부 가로로 넘쳤고(본문 칸 828px에서
 * 표 폭 1,326~3,528px), 칸 폭에 34em 상한을 걸자 8개 모두 칸 안에 들어왔다. 그래도 넘치는 표는 가로로 밀고 가장자리 그림자로 알린다.
 * **.prose 전역이 아니다** — 화면정의서가 정한 범위는 지식창고 문서 읽기·미리보기이고, 홈·주간·공지·회의록·외부 공유 화면의 표는
 * 건드리지 않는다(코드리뷰: 전역으로 걸면 거기서도 표가 70vh에서 안쪽 스크롤 상자가 된다). 표시 클래스 kb-prose는
 * kbpage.html·kb.html·kb_project.html의 본문·미리보기 <article>에만 있다(tests/test_kb_table_css.py가 본다).
 * 위 전역 규칙과 명시도가 같은 것(th·td)은 아래가 뒤에 와서 이긴다.
 * - 바닥색은 --prose-surface 하나다. 페이지 배경 위와 흰 카드·팝업 안(새 문서 팝업의 미리보기)이 달라서, 그림자 덮개와
 *   고정 머리글이 같은 색을 써야 가장자리에 띠가 안 생긴다.
 * - **max-height는 감싸는 칸에 준다.** 표 자체에 overflow를 주면 칸 폭 계산이 무너진다(위 #302). 칸이 세로 스크롤을 맡으면
 *   머리글(position: sticky)은 이 칸 안에서 붙는다. 표가 70vh보다 짧으면 스크롤이 없어 sticky는 아무 일도 안 한다. */
.kb-prose { --prose-surface: var(--background-normal-alternative); }
.card .kb-prose, .upload-dialog .kb-prose { --prose-surface: var(--background-normal-normal); }
.kb-prose .prose__wide {
  overflow: auto; max-height: 70vh;
  /* 가장자리 그림자(스크롤 위치를 아는 순수 CSS 방식): 덮개(local)는 내용과 같이 움직여 끝에 닿으면 그림자를 가리고,
     그림자(scroll)는 칸 가장자리에 붙어 있다. 그래서 «아직 더 있는 쪽»에만 그림자가 보인다.
     그림자는 칸이 투명한 곳 뒤에서만 보인다. 머리글 줄은 불투명이라 그 줄에서는 가려진다 — 본문 줄에서 보이면 충분하다. */
  background:
    linear-gradient(to right, var(--prose-surface) 30%, transparent) left center / 24px 100% no-repeat local,
    linear-gradient(to left, var(--prose-surface) 30%, transparent) right center / 24px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, #1717191f, transparent) left center / 10px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, #1717191f, transparent) right center / 10px 100% no-repeat scroll;
}
/* 칸 폭 상한(34em)과 위 맞춤. 긴 문장은 칸 안에서 줄을 바꾼다. */
.kb-prose th, .kb-prose td { max-width: 34em; vertical-align: top; }
/* **머리글은 불투명이어야 한다** — --fill-alternative는 5% 투명이라 그대로 붙이면 아래 행 글자가 비친다. 바닥색 위에 겹쳐 쌓는다.
   sticky 칸은 border-collapse에서 테두리가 같이 안 붙어서, 아래 선은 box-shadow로 그린다. */
.kb-prose th {
  position: sticky; top: 0; z-index: 1;
  background-color: var(--prose-surface);
  background-image: linear-gradient(var(--fill-alternative), var(--fill-alternative));
  box-shadow: 0 1px 0 var(--line-solid-neutral);
}
/* 홀수·짝수 줄을 아주 옅게 구분한다(화면정의서). */
.kb-prose tbody tr:nth-child(even) td { background: var(--fill-alternative); }

/* ── 체크리스트(GFM, mdit-py-plugins) ─────────────────────────────
 * 기본 목록 글머리표(•)가 체크박스 앞에 같이 남으면 두 겹으로 보인다. */
.prose .contains-task-list { list-style: none; padding-left: 0; }
.prose .task-list-item { display: flex; align-items: flex-start; gap: var(--sp-2); }
.prose .task-list-item-checkbox { margin-top: 5px; flex: 0 0 auto; }

/* ── 코드블록 ─────────────────────────────────────────────────────
 * **pre.hl이 아니라 pre에 준다(#414).** _highlight_code는 언어를 안
 * 적었거나 모르는 언어면 빈 문자열을 돌려주고, 그러면 markdown-it이 클래스
 * 없는 <pre><code>를 그린다 — .hl에만 걸어 두면 그런 블록은 가로 스크롤이
 * 없어 화면을 통째로 민다. 운영 실측으로 지식창고 118장이 그렇다.
 * 그 전제는 tests/test_markdown.py가 지킨다. */
.prose pre {
  margin: 0 0 var(--sp-3); padding: var(--sp-3); border-radius: var(--r-sm);
  background: var(--fill-normal); overflow-x: auto;
}
.prose pre code {
  padding: 0; background: none; border-radius: 0;
  font-size: var(--fs-sm); font-family: ui-monospace, SFMono-Regular, monospace;
}
/* ── 그림 카드(mermaid, #447) ─────────────────────────────────────
 * 본문의 ```mermaid를 브라우저가 그린 자리. 만드는 쪽은 editor/src/diagram/scanner.js다 —
 * 카드는 어두운 남색 판이고, **상태는 data-state, 그림/원문 전환은 data-view 속성으로만 가른다**:
 *   loading            «그림을 그리는 중…»만 (원문은 아직 감춰져 있다)
 *   ready              그림. data-view=figure면 그림, source면 원문 코드
 *   syntax · toolarge · blocked  안내 + 원문 코드(고치려면 원문이 보여야 한다). 단추는 없다
 * **색 값은 tokens.css가 아니라 여기 둔다** — tokens.css는 tools/design_tokens.py가 만드는 파일이라 손으로 더하면
 * 다음 재생성에서 사라진다. mermaid 안쪽 색은 editor/src/diagram/config.js의 «색»이 정하고 이 값과 같아야 한다
 * (tests/test_kb_diagram_assets.py가 같은지 본다). 글자는 4.5:1, 박스·선은 3:1 이상이다. */
.mm-card, .mm-zoom { --mm-bg: #161618; --mm-text: #e6efff; --mm-muted: #9aa3ad; --mm-edge: #3570b8; --mm-rule: #3a3a3e; }
.mm-card {
  margin: 0 0 var(--sp-3); padding: var(--sp-3); border-radius: var(--r-sm);
  background: var(--mm-bg); color: var(--mm-text);
}
.mm-card[data-state="loading"] {
  min-height: 120px; display: flex; align-items: center; justify-content: center;
}
/* 문단 규칙은 .prose p(0,1,1)를 이겨야 해서 .mm-card를 앞에 붙인다(0,2,0) — 안 붙이면 안내 문단에 아래 여백 12px가 붙는다. */
.mm-card .mm-card__msg { margin: 0; font-size: var(--fs-sm); line-height: var(--lh-sm); }
.mm-card .mm-card__detail {
  margin: var(--sp-1) 0 0; font-size: var(--fs-cap); line-height: var(--lh-cap);
  color: var(--mm-muted); font-family: ui-monospace, SFMono-Regular, monospace;
  overflow-wrap: anywhere;
}
.mm-card__body { margin: 0; }
.mm-card[data-state="syntax"] .mm-card__body,
.mm-card[data-state="toolarge"] .mm-card__body,
.mm-card[data-state="blocked"] .mm-card__body { margin: 0 0 var(--sp-3); }
/* 작은 그림은 늘리지 않는다 — mermaid가 svg에 max-width를 인라인으로 준다. 여기서는 가운데 두고 높이만 비율을 따른다. */
.mm-card__body svg { display: block; margin: 0 auto; height: auto; }
.mm-card__bar { display: flex; justify-content: flex-end; gap: var(--sp-2); margin: 0 0 var(--sp-2); }
.mm-card__btn {
  padding: 4px 10px; border: 1px solid var(--mm-edge); border-radius: var(--r-sm);
  background: transparent; color: var(--mm-text); font: inherit; font-size: var(--fs-cap); line-height: var(--lh-cap);
  cursor: pointer;
}
.mm-card__btn:hover { background: #0b2c4f; }
.mm-card__btn:focus-visible { outline: 2px solid var(--mm-text); outline-offset: 2px; }
/* 원문은 지금 코드 블록과 같은 모양이다. 카드 안에서는 바깥 여백을 걷고, 그림이 있을 때는 단추가 켜야 보인다. */
/* 높이를 막는다 — «너무 큼»은 정의상 긴 원문이 오는 상태라 막지 않으면 520줄이 페이지를 13,000px로 늘린다(실제 Chrome 측정). 안에서 스크롤한다. */
.prose .mm-card__src { margin: 0; max-height: 36em; overflow: auto; }
.mm-card[data-view="figure"] .mm-card__src,
.mm-card[data-view="source"] .mm-card__body { display: none; }
.prose .mm-card__notice { margin: 0 0 var(--sp-2); }

/* ── 확대 보기(#447) — 그림 카드의 «⛶ 확대»가 여는 전체 화면 팝업. 만드는 쪽은 editor/src/diagram/viewer.js다.
 * <dialog>가 열림·닫힘·Esc·뒤 문서 막기를 해 준다. 그림은 카드 것을 복제해 transform으로 키운다(다시 안 그린다).
 * display는 [open]일 때만 준다 — 안 그러면 닫힌 팝업이 기본 display:none을 이겨 화면에 남는다.
 * 닫기·맞춤·± 단추는 카드 단추(.mm-card__btn)와 같은 모양이다. */
.mm-zoom {
  position: fixed; inset: 0; margin: auto; padding: 0; border: 0; border-radius: var(--r-md);
  width: calc(100vw - 32px); height: calc(100vh - 32px); max-width: none; max-height: none;
  background: var(--mm-bg); color: var(--mm-text); overflow: hidden;
}
.mm-zoom[open] { display: flex; flex-direction: column; }
.mm-zoom::backdrop { background: rgba(0, 0, 0, .6); }
.mm-zoom__bar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); flex: 0 0 auto;
  padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--mm-rule);
}
.mm-zoom__title { margin: 0; font-size: var(--fs-sm); line-height: var(--lh-sm); font-weight: 700; color: var(--mm-text); }
.mm-zoom__tools { display: flex; align-items: center; gap: var(--sp-1); flex-wrap: wrap; justify-content: flex-end; }
.mm-zoom__btn { min-width: 2.4em; }
.mm-zoom__btn[data-act="reset"] { min-width: 4.2em; }
.mm-zoom__btn:disabled { opacity: .4; cursor: default; }
.mm-zoom__btn:disabled:hover { background: transparent; }
.mm-zoom__viewport { position: relative; flex: 1 1 auto; min-height: 0; overflow: hidden; cursor: grab; touch-action: none; }
.mm-zoom__viewport.is-grabbing { cursor: grabbing; }
.mm-zoom__stage { position: absolute; left: 0; top: 0; transform-origin: 0 0; }
.mm-zoom__stage svg { display: block; max-width: none; }
/* 열려 있는 동안 뒤 문서가 스크롤되지 않는다(화면정의서). */
html.mm-lock { overflow: hidden; }
@media (max-width: 860px) {
  .mm-zoom { width: calc(100vw - 16px); height: calc(100vh - 16px); }
}

/* ── 문법 강조(pygments) ───────────────────────────────────────────
 * 아래 .hl .* 는 pygments HtmlFormatter(style="default").get_style_defs(".hl")를
 * 그대로 가져온 것 — 토큰 색은 직접 못 바꾼다(바꾸면 pygments 업데이트마다
 * 다시 떠 와야 한다). 배경·테두리·바깥 여백은 위 .prose pre가 맡는다.
 *
 * 이 줄은 pygments가 제 배경을 칠하는 것을 지운다. 위 .prose pre(0,1,1)가
 * .hl(0,1,0)을 명시도로 이기므로 배경은 moa 토큰이 남는다 — 싸우는 게 아니라
 * 각자 제자리다. */
.hl { background: transparent; }
.hl .c { color: #3D7B7B; font-style: italic }
.hl .err { border: 1px solid #F00 }
.hl .k { color: #008000; font-weight: bold }
.hl .o { color: var(--label-alternative) }
.hl .ch, .hl .cm, .hl .cpf, .hl .c1, .hl .cs { color: #3D7B7B; font-style: italic }
.hl .cp { color: #9C6500 }
.hl .gd { color: #A00000 }
.hl .ge { font-style: italic }
.hl .ges { font-weight: bold; font-style: italic }
.hl .gr { color: #E40000 }
.hl .gh, .hl .gp { color: #000080; font-weight: bold }
.hl .gi { color: #008400 }
.hl .go { color: var(--label-alternative) }
.hl .gs { font-weight: bold }
.hl .gu { color: #800080; font-weight: bold }
.hl .gt { color: #04D }
.hl .kc, .hl .kd, .hl .kn, .hl .kr { color: #008000; font-weight: bold }
.hl .kp, .hl .nb, .hl .bp, .hl .sx { color: #008000 }
.hl .kt { color: #B00040 }
.hl .m, .hl .mb, .hl .mf, .hl .mh, .hl .mi, .hl .mo, .hl .il { color: var(--label-alternative) }
.hl .s, .hl .sa, .hl .sb, .hl .sc, .hl .dl, .hl .sh, .hl .s2, .hl .sr, .hl .s1 { color: #BA2121 }
.hl .sd { color: #BA2121; font-style: italic }
.hl .na { color: #687822 }
.hl .nc, .hl .nn, .hl .fm { color: #00F; font-weight: bold }
.hl .no { color: #800 }
.hl .nd, .hl .ow { color: #A2F; font-weight: bold }
.hl .ni { color: var(--label-alternative); font-weight: bold }
.hl .ne { color: #CB3F38; font-weight: bold }
.hl .nf { color: #00F }
.hl .nl { color: #767600 }
.hl .nt { color: #008000; font-weight: bold }
.hl .nv, .hl .vc, .hl .vg, .hl .vi, .hl .vm, .hl .ss { color: #19177C }
.hl .se { color: #AA5D1F; font-weight: bold }
.hl .si { color: #A45A77; font-weight: bold }
.hl .sr { color: #A45A77 }
.hl .w { color: var(--label-assistive) }

/* 사람 언급. 괄호와 약간의 굵기만 준다 — 배경색을 깔면 이름이 튀어서
 * 지목하는 느낌이 난다. */
.mention { color: var(--label-normal); font-weight: 600; white-space: nowrap; }

/* @login 멘션(#248) — 아는 사람이면 링크가 된다. .mention과 달리 실제로
 * 눌러 그 사람 화면으로 간다 — 눌리는 것임을 색으로 드러낸다. */
.mention-link { color: var(--primary-normal); font-weight: 600; white-space: nowrap; }
.mention-link:hover { text-decoration: underline; }

/* 회의록을 텍스트로 바로 썼을 때(#194). 마크다운으로 안 읽으므로 "#"·"*"가
 * 서식으로 안 바뀌고 글자 그대로 보인다 — 대신 줄바꿈은 살려야 읽힌다. */
.meet__text { white-space: pre-wrap; }

/* 회의록 목록 줄. 지금까지 이 클래스들에 맞는 CSS가 아예 없어서 맨 링크
 * 밑줄로만 보이고 있었다 — #212가 목록을 한 판으로 바꾸며 놓친 것 같다
 * (개요 사이드바 작업 중 발견, #UI방향1). .vault__row와 같은 카드꼴을
 * 쓰되, 미리보기 줄이 있어 두 줄짜리 블록으로 짠다. */
.meet {
  display: block; padding: var(--sp-3) var(--sp-4);
  background: var(--background-normal-normal); border-radius: var(--r-md);
  border: 1px solid var(--line-solid-neutral); color: var(--label-normal);
  text-decoration: none;
}
.meet:hover { background: var(--fill-alternative); }
.meet__head { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.meet__title { font-weight: 600; min-width: 0; }
.meet__peek {
  margin: var(--sp-1) 0 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* 회의록 목록 — 카드 그리드(실사용 요청: "화려하면서 정리된 느낌", SNS
 * 피드 벤치마킹, 실사용 지적으로 점+색 글자 방식으로 확정). 새 색은 안
 * 쓴다 — 프로젝트 hue(기존 .tag·.dot이 쓰는 것과 같은 팔레트)를 재사용해서
 * "화려함"을 낸다. 격자 정렬 자체가 "정리된" 느낌을 맡는다. 카드 자체엔
 * 테두리 액센트를 안 준다 — 둥근 모서리와 두꺼운 단면 테두리가 만나는
 * 자리가 어색해 보인다(impeccable border-accent-on-rounded). 그라디언트·
 * 그림자는 안 쓴다. */
.meetC-count { margin: 0 0 var(--sp-4); font-variant-numeric: tabular-nums; }
.meetC-count a { margin-left: 4px; }
.meetC-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--sp-4);
}
.meetC-card {
  display: flex; flex-direction: column; min-height: 190px;
  padding: var(--sp-4); border-radius: var(--r-md);
  background: var(--background-normal-normal);
  border: 1px solid var(--line-solid-neutral);
  color: var(--label-normal);
}
.meetC-card:hover { border-color: var(--line-solid-strong); }
.meetC-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); }
.meetC-project { display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-cap); font-weight: 700; color: var(--hue); }
.meetC-day {
  font-size: var(--fs-cap); color: var(--label-assistive); font-variant-numeric: tabular-nums;
}
/* 제목+미리보기를 하나의 링크로 묶는다 — a 안에 a를 못 넣어서 카드
 * 전체를 링크로 못 감싸는 대신 이 자리가 클릭 영역을 맡는다. */
.meetC-open {
  display: block; flex: 1 1 auto; text-decoration: none; color: inherit; min-height: 0;
}
.meetC-title {
  display: block; font-size: 17px; font-weight: 700; letter-spacing: -.01em;
  line-height: 1.35; margin: var(--sp-2) 0;
}
.meetC-open:hover .meetC-title { color: var(--primary-normal); }
.meetC-peek {
  margin: 0; color: var(--label-alternative); font-size: var(--fs-sm); line-height: 20px;
  overflow: hidden; display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
/* 해시태그는 상자 없이 색으로만 구분한다(실사용 지적: 알약 상자가
 * 너무 둥글고 다닥다닥 붙어 읽기 힘들다). .notice__when(일정 날짜)이
 * 이미 쓰는 것과 같은 accent — --primary-normal 하나로 통일해서
 * "이건 해시태그"라는 신호만 준다. 프로젝트 hue와 안 겹치게 태그마다
 * 다른 색은 안 쓴다. 간격은 넓혀서 다닥다닥 안 붙게 한다.
 * **눌러서 그 태그로 좁힌다**(실사용 요청) — 따로 필터 바를 안 둔다. */
.meetC-hashtags { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-2); }
.meetC-hash { font-size: var(--fs-cap); font-weight: 600; color: var(--primary-normal);
  text-decoration: none; }
.meetC-hash:hover { text-decoration: underline; }
.meetC-foot {
  display: flex; align-items: center; gap: var(--sp-2); margin-top: var(--sp-2);
  padding-top: var(--sp-2); border-top: 1px solid var(--line-solid-alternative);
}
@media (max-width: 560px) {
  .meetC-grid { grid-template-columns: 1fr; }
}

/* ── 근거 ─────────────────────────────────────────── */
.evidence { margin-top: var(--sp-3); }
.evidence__toggle {
  display: inline-block; list-style: none; cursor: pointer;
  border-radius: var(--r-pill); background: var(--fill-normal);
  padding: 5px var(--sp-3); font-size: var(--fs-sm); font-weight: 600;
  color: var(--label-alternative);
}
.evidence__toggle::-webkit-details-marker { display: none; }
.evidence__toggle:hover { background: var(--fill-strong); }
.evidence__list { margin: var(--sp-3) 0 0; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: var(--sp-2); }
.evidence__item { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-2);
  font-size: var(--fs-sm); line-height: var(--lh-sm); }
.evidence__repo, .evidence__at { color: var(--label-assistive); font-size: var(--fs-cap);
  font-variant-numeric: tabular-nums; }
.evidence__title { flex: 1 1 240px; min-width: 0; color: var(--label-neutral); }
.chip {
  display: inline-block; padding: 3px 10px; border-radius: var(--r-pill);
  background: var(--fill-normal); font-size: var(--fs-cap); font-weight: 600;
  color: var(--label-alternative);
}
.chip:hover { background: var(--fill-strong); text-decoration: none; }
.chip--kind {
  padding: 2px 8px; border-radius: var(--r-pill); background: var(--fill-normal);
  font-size: var(--fs-cap); font-weight: 600; color: var(--label-alternative);
}
.chip--denied { background: var(--status-negative); color: var(--static-white); }
.audit__row { align-items: baseline; gap: var(--sp-3); }
.audit__at { font-variant-numeric: tabular-nums; white-space: nowrap; }
.audit__filters { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3);
  margin-bottom: var(--sp-6); }
.audit__filters .field { width: auto; max-width: 220px; }
.badge { margin-left: var(--sp-2); padding: 2px 8px; border-radius: var(--r-pill);
  background: var(--accent-background-red-orange); color: var(--static-white);
  font-size: var(--fs-cap); font-weight: 700; }

/* ── 묶음 필터 ────────────────────────────────────── */
/* wrap을 기본으로 준다 — org 세그먼트는 2~3개라 한 줄로 끝나지만, 회의록
 * 프로젝트 필터처럼 항목이 늘어날 수 있는 자리는 줄바꿈 없이는 화면 밖으로
 * 넘친다. */
.segmented { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin-bottom: var(--sp-6); }
.filters { display: flex; flex-wrap: wrap; gap: var(--sp-5); }
.filters .segmented { margin-bottom: var(--sp-6); }
.feed { display: flex; flex-direction: column; }
.segmented__item { padding: 5px var(--sp-3); border-radius: var(--r-pill);
  font-size: var(--fs-sm); font-weight: 600; color: var(--label-alternative); }
.segmented__item:hover { background: var(--fill-alternative); text-decoration: none; }
.segmented__item.is-on { background: var(--surface-selected); color: var(--primary-strong); }
.segmented__item.is-on:hover { background: var(--surface-selected-hover); }
.segmented__item { cursor: pointer; }
/* 라디오로 만든 탭(회의록 만들기 방법·형식). label 안에 라디오를 두고
 * sr-only로 숨긴다 — 클릭·키보드는 그대로 되고, :has()로 label 자신의
 * 상태를 본다. 형제 결합자와 달리 마크업 위치를 안 따져서 더 안전하다.
 *
 * **켜진 색이 위 .is-on과 다른 것은 알고 둔 것이다(#378).** 링크로 만든 탭은
 * 연한 파란 면, 라디오로 만든 탭은 검은 면이다 — /meetings·/kb·/infra는 한
 * 화면에 둘이 같이 나오므로 눈에 띈다. 맞추자는 제안이 리뷰에서 나왔지만
 * 팀장이 지금 모양을 골랐다(docs/결정/결정기록_테마색.md). 버그로 보고
 * 한쪽을 고치기 전에 그 문서를 먼저 읽는다. */
.segmented__item:has(input:checked) { background: var(--label-normal); color: var(--static-white); }
.segmented__item:has(input:focus-visible) { outline: 2px solid var(--primary-normal); outline-offset: 2px; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

/* ── 빈 화면 ──────────────────────────────────────── */
.empty { padding: var(--sp-8) 0; text-align: center; }
.empty__title { margin: 0; font-size: var(--fs-lg); font-weight: 700; }
.empty__detail { margin: var(--sp-2) 0 0; font-size: var(--fs-sm); color: var(--label-alternative); }

/* ── 사람·프로젝트 머리 ───────────────────────────── */
.profile { display: flex; align-items: center; gap: var(--sp-4); margin-bottom: var(--sp-5); }
.avatar--profile { width: 56px; height: 56px; }
.avatar--sm { width: 22px; height: 22px; flex-shrink: 0; }
.avatar--sm.avatar--text { font-size: 10px; }
.people { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin-bottom: var(--sp-6); }
/* 날짜 기준 화면(사람·프로젝트·주간)은 레일이 없다. 카드만 쌓는다. */
.stack { display: flex; flex-direction: column; gap: var(--sp-4); }
.card__date { font-size: var(--fs-sm); font-weight: 700; color: var(--label-normal);
  font-variant-numeric: tabular-nums; }
.profile h1 { margin: 0; font-size: var(--fs-xl); line-height: var(--lh-xl);
  font-weight: 800; letter-spacing: -.025em; }
.profile__meta { margin: 2px 0 0; font-size: var(--fs-sm); color: var(--label-alternative); }

/* ── 알림 쪽지 ────────────────────────────────────── */
/* 사람의 일이 아니라 손봐야 할 것. 피드와 섞이지 않게 따로 둔다. */
.note { margin-top: var(--sp-7); border-radius: var(--r-md);
  background: var(--fill-alternative); padding: var(--sp-5); }
.note__title { margin: 0 0 var(--sp-3); font-size: var(--fs-sm); font-weight: 700;
  color: var(--label-normal); display: flex; align-items: center; gap: var(--sp-2); }
/* 관리자만 본다는 것을 화면에 적어둔다 — 안 적으면 남도 보는 줄 안다. */
.note__only { font-size: var(--fs-cap); font-weight: 600; color: var(--label-assistive);
  background: var(--fill-normal); border-radius: var(--r-pill); padding: 2px 8px; }
.note__list { margin: 0 0 var(--sp-3); padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: var(--sp-1); }
.note__row { display: flex; align-items: baseline; gap: var(--sp-2); }
.note__row code { font-size: var(--fs-sm); font-family: ui-monospace, SFMono-Regular, monospace;
  color: var(--label-normal); }

/* ── 찾기 ────────────────────────────────────────
 * 한 줄이 하나의 찾은 것이다. 종류 라벨을 붙여 **왜 이게 나왔는지** 보이게 한다 —
 * 요약 단위로만 묶으면 커밋 제목에서 걸린 결과가 "이 요약엔 그 말이 없는데?"로 보인다.
 */
.hit { padding: var(--sp-4) 0; border-bottom: 1px solid var(--line-solid-neutral); }
.hit:last-child { border-bottom: 0; }
.hit__head { display: flex; align-items: baseline; gap: var(--sp-2);
  flex-wrap: wrap; margin-bottom: 6px; }
.hit__kind { padding: 2px 8px; border-radius: var(--r-pill);
  background: var(--fill-normal); color: var(--label-neutral);
  font-size: var(--fs-cap); font-weight: 700; }
.hit__who { font-size: var(--fs-sm); font-weight: 600; color: var(--label-normal); }
.hit__who:hover { text-decoration: underline; }
.hit__text { display: block; color: var(--label-neutral); font-size: var(--fs-md);
  line-height: 24px; text-decoration: none; }
.hit__text:hover { color: var(--label-normal); }
/* 찾은 낱말은 굵게만. 색은 프로젝트 태그가 이미 쓰고 있어 겹치면 둘 다 안 읽힌다. */
.hit__text strong { color: var(--label-normal); font-weight: 700; }
/* 자료실 파일의 작업내용. 이름 아래에 한 단 낮춰 붙인다 — 같은 무게로 서면
 * 어느 것이 파일 이름인지 모른다. */
/* 안을 못 본 파일에 붙는 한 줄(#387). 결과를 가리지 않게 작게 둔다. */
.hit__note { margin: var(--sp-1) 0 0; }
.hit__sub { margin: var(--sp-1) 0 0; font-size: var(--fs-sm); line-height: 20px;
  color: var(--label-assistive); }
.hit__sub strong { color: var(--label-alternative); font-weight: 700; }

/* ── 직접 쓴 기록 ─────────────────────────────────
 * 수집한 것과 사람이 쓴 것을 구분선과 라벨로 가른다. 섞이면 요약을 못 믿는다.
 * **배경색은 안 쓴다** — 쓴 칸만 색이 들면 그 자체가 눈에 띄는 표시가 되고,
 * 안 쓴 사람이 비어 보인다.
 */
.note-own { margin-top: var(--sp-4); padding-top: var(--sp-4);
  border-top: 1px solid var(--line-solid-neutral); }
.note-own__label { margin: 0 0 var(--sp-2); font-size: var(--fs-sm); font-weight: 700;
  color: var(--label-neutral); }
.note-own__label .meta { font-weight: 500; margin-left: var(--sp-2); }

.note-edit { margin-top: var(--sp-3); }

/* 올리기·받기 팝업(#199). <dialog>가 열림·닫힘·포커스 가두기·Esc 닫기를
 * 다 해 준다 — 모양만 카드(.card)에 맞춰 입힌다. */
.upload-dialog { position: fixed; margin: auto; inset: 0;
  width: min(720px, calc(100vw - 32px)); max-height: calc(100vh - 64px);
  overflow: auto; padding: var(--sp-5); border: 1px solid var(--line-solid-neutral);
  border-radius: var(--r-md); background: var(--background-normal-normal);
  /* **손으로 키울 수 있게 한다(#368).** 긴 설명을 쓰거나 사진을 여러 장
   * 붙일 때 기본 720px이 좁다. resize는 overflow가 visible이 아니어야
   * 먹는데 바로 위에서 auto를 주고 있다. 이 리포에 textarea로 쓰는
   * 전례가 둘 있다.
   *
   * **min-height는 안 준다(#368 리뷰).** 이 클래스는 자료실 지우기·옮기기
   * 같은 짧은 확인 모달도 같이 쓴다 — 180px을 걸었더니 자연 높이 146px인
   * 모달이 180px로 늘어 단추 아래가 34px 비었다(실측). 세로는 위아래
   * 패딩이 있어 0으로 무너지지 않으므로, 가로만 막으면 된다. */
  resize: both; min-width: min(320px, calc(100vw - 32px)); }
/* **이미 쓴 공지를 고칠 때만 본문 칸을 키운다(#426).** 새로 쓰는 칸은
   비어 있어 4줄로 충분하지만, 고치는 칸에는 이미 글이 들어 있다 —
   팀장이 "팝업이 너무 작아서 수정하기 어렵다"고 한 자리다.
   **rows가 아니라 여기서 준다.** rows로 키우면 스크립트가 안 떠 메뉴
   패널(300px)에 남았을 때 거기까지 커져 고치기가 더 나빠진다. */
.upload-dialog .notice-form--edit textarea { min-height: 22rem; }
.upload-dialog::backdrop { background: rgba(0, 0, 0, .4); }
.upload-dialog__head { display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); margin-bottom: var(--sp-3); }
/* **[hidden]을 따로 못 박는다(#368 리뷰).** display:flex를 준 이 규칙이
 * 브라우저 기본 [hidden]{display:none}보다 나중에 와서 이긴다 — 디자인Q는
 * 이 머리를 숨겨 보내고 JS가 모달로 올린 뒤에 켜는데, 안 막으면 스크립트가
 * 없을 때 아무 동작도 안 하는 X가 떠 있다. .vault__dl-bar에서 이미 겪었다. */
.upload-dialog__head[hidden] { display: none; }
.upload-dialog__title { margin: 0; font-size: var(--fs-lg); font-weight: 800;
  letter-spacing: -.02em; }
.upload-dialog__close { border: 0; background: none; padding: 4px 8px; cursor: pointer;
  font-size: 22px; line-height: 1; color: var(--label-alternative); }
.upload-dialog__close:hover { color: var(--label-normal); }
/* 트리거였던 <summary>용 클래스를 <button>에도 그대로 쓴다 — 브라우저
 * 기본 버튼 배경·테두리가 얹히지 않게 리셋만 더한다. */
button.vault__act { background: var(--background-normal-normal); font: inherit; }
/* 주간의 여는 자리는 .note-edit__toggle이다 — 모달로 올리면 <summary>가
 * <button>이 되는데, 리셋이 없으면 글자 링크였던 것이 브라우저 기본
 * 테두리·회색 배경을 뒤집어쓴다(#369 리뷰). vault__act와 같은 손질. */
button.note-edit__toggle { border: 0; background: none; padding: 0; font: inherit;
  font-size: var(--fs-sm); font-weight: 600; }

.note-edit__toggle { font-size: var(--fs-sm); font-weight: 600; color: var(--primary-normal);
  cursor: pointer; list-style: none; }
.note-edit__toggle::-webkit-details-marker { display: none; }
.note-edit__toggle:hover { text-decoration: underline; }

/* 칸들이 기본 줄바꿈 여백(몇 px)만으로 붙어 있어 빽빽했다 — 세로로 쌓고
 * 칸마다 같은 틈을 준다. 버튼까지 늘어나면 안 돼서 stretch는 안 쓴다. */
.note-edit__form { margin-top: var(--sp-3); display: flex; flex-direction: column;
  align-items: flex-start; gap: var(--sp-3); }
.note-edit__form textarea {
  width: 100%; box-sizing: border-box; padding: 12px 14px;
  border: 1px solid var(--line-solid-normal); border-radius: var(--r-sm);
  font-family: inherit; font-size: var(--fs-md); line-height: 24px;
  color: var(--label-normal); background: var(--background-normal-normal); resize: vertical;
}
.note-edit__form textarea:focus { outline: 0; border-color: var(--primary-normal);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-normal) 12%, transparent); }
/* txt 끌어다 놓기(meetings.js) — 포커스와 같은 색으로, 놓을 자리라는 것만
 * 알려주면 된다. */
.note-edit__form textarea.is-dragover { border-color: var(--primary-normal);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-normal) 12%, transparent); }
.note-edit__form .meta { margin: 0; }

/* 회의록 "바로 쓰기"·"파일 올리기" 패널. 기본은 바로 쓰기(AI 요약)
 * 패널이 보인다 — 클로바 노트 원문을 붙여넣고 정리를 맡기는 길을 제일
 * 많이 써서(실사용 지적) 이미 정제된 md가 있을 때뿐인 파일 올리기보다
 * 앞세운다. :has()로 팝업 안 어디에 라디오가 있든 상관없이 켜고 끈다.
 * 자료실 올리기 팝업과 같은 모양으로 바뀌며(#213 후속) details가 아니라
 * dialog가 됐다. */
.meet-panel { width: 100%; display: flex; flex-direction: column; gap: var(--sp-2); }
.meet-panel--upload { display: none; }
.upload-dialog:has(#mode-upload:checked) .meet-panel--write { display: none; }
.upload-dialog:has(#mode-upload:checked) .meet-panel--upload { display: flex; }

/* 보기·고치기 토글(#213 후속). "고치기"가 화면 맨 아래 딴 자리에 있던
 * 것을 본문 자리로 옮겼다 — 위에서 보던 것과 아래서 고치는 게 다른
 * 물건처럼 느껴졌다. */
.meet-view { margin: var(--sp-5) 0; }
.view-panel--edit { display: none; }
.meet-view:has(#view-tab-edit:checked) .view-panel--read { display: none; }
.meet-view:has(#view-tab-edit:checked) .view-panel--edit { display: block; }

/* 쓰기·미리보기 탭(#208 후속). 같은 라디오+:has() 방식 — 회의록·공지·
 * 지식창고 세 폼이 전부 같은 마크업(.js-md-preview-tab 라디오)을 쓰므로
 * 토글 규칙은 여기 한 곳에서만 정의한다. */
.md-panel { width: 100%; display: flex; flex-direction: column; }
.md-panel--preview { display: none; }
.meet-view:has(.js-md-preview-tab:checked) .md-panel--write,
.notice-form:has(.js-md-preview-tab:checked) .md-panel--write,
.kb-form:has(.js-md-preview-tab:checked) .md-panel--write { display: none; }
.meet-view:has(.js-md-preview-tab:checked) .md-panel--preview,
.notice-form:has(.js-md-preview-tab:checked) .md-panel--preview,
.kb-form:has(.js-md-preview-tab:checked) .md-panel--preview { display: flex; }
.js-md-preview:empty::before { content: "미리보기 탭을 누르면 여기 나와요."; color: var(--label-assistive); }

/* 회의록 상세 — 제목 옆 관리 케밥(#UI방향1). .vault__menu를 그대로 쓴다 —
 * 새 컴포넌트를 만들 이유가 없다. 다만 기본 색(--label-assistive, 28%
 * 불투명도)이 빽빽한 목록 줄 안에서는 적당했지만, 큰 제목(h1) 옆
 * 혼자 있으니 점 세 개가 묻혀 잘 안 보였다(모바일 실사용 제보,
 * 호버가 없는 화면이라 더했다) — 여기서만 늘 보이는 옅은 배경을 준다. */
/* 아래 여백은 .meet-head가 직접 갖는다 — 제목의 margin-bottom은 0으로
 * 눌러야 한다(flex 줄 안에서는 그 여백이 머리 높이를 키운다). 그러면
 * 제목 아래 간격이 "뒤에 뭐가 오느냐"에 달리는데, <p>가 오는 화면
 * (회의록·지식창고 상세)만 문단 기본 여백으로 우연히 12px이 생기고
 * <span class="tag">(디자인 큐 상세)나 <nav>(목록 화면들)가 오면 제목에
 * 딱 붙었다 — 6개 화면 중 4개가 0px이었다(실사용 지적, 실측 확인).
 *
 * 뒤 요소가 제 위 여백을 갖고 있어도 겹쳐 쌓이지 않는다 — 이웃한
 * 위아래 여백은 큰 쪽 하나로 합쳐지기 때문이다(실측: .meta 12px는
 * 그대로 12px, .vault__h2 20px는 20px 유지). 그래서 뒤 요소마다
 * 여백을 빼 주는 예외를 따로 안 둔다. */
.meet-head { display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-3); margin-bottom: var(--sp-3); }
.meet-head .page__title { margin-bottom: 0; }
.meet-head .vault__menu-btn { background: var(--fill-normal); color: var(--label-alternative); }
.meet-head .vault__menu-btn:hover { background: var(--fill-alternative); color: var(--label-normal); }

/* AI 요약 개요 사이드바(#UI방향1). 소제목이 많은 문서(실측 40개 가까이)를
 * 훑을 때 쓴다 — 짧은 회의록엔 필요 없어 outline이 있을 때만 렌더링된다
 * (app/web/markdown.py의 render_meeting_with_outline). 그래서 페이지 폭도
 * 그때만 넓힌다(.page--doc, meeting.html의 page_class 블록). */
.page--doc { max-width: min(100%, 1100px); }
/* **트리가 있는 화면만 넓힌다(#298).** page--doc은 회의록도 쓰는 클래스라
   그냥 넓히면 회의록 본문까지 같이 넓어진다. */
/* **목차가 아니라 트리가 있는지로 정한다.** page--doc은 목차가 있을 때만
   붙는데, 제목 없이 표만 있는 문서는 목차가 안 생겨 좁은 폭으로 떨어졌다
   (실측: 1920 화면에서 본문이 608px). */
.page:has(.kb-nav) { max-width: min(100%, 1600px); }
/* ── 넓게 보기(#447) — 문서 읽기 화면의 폭 제한을 푼다. 켜짐은 main.page의 data-wide 속성이다.
 * 위 두 규칙(.page--doc 0,1,0 · .page:has(.kb-nav) 0,2,0)을 이기려고 main.page[data-wide](0,2,1)로 쓴다.
 * 좌우 여백(.page의 padding)은 그대로다. 속성은 kbpage.html의 인라인 한 줄과 kb-wide.js가 단다. */
main.page[data-wide] { max-width: none; }
/* **선택자를 .page > .meet-head > .meet-head__tools(0,3,0)로 쓴다.** 아래 2146행의 `.page > .meet-head > div { flex: 1 1 260px }`
 * (일간·주간 머리글용, 0,2,1)가 이 묶음 div까지 잡아 늘려서, 단추와 ⋯ 메뉴가 오른쪽 끝이 아니라 제목 바로 옆에 몰렸다(실제 Chrome 측정). */
.page > .meet-head > .meet-head__tools { display: flex; align-items: flex-start; gap: var(--sp-2); flex: 0 0 auto; min-width: auto; }
.kb-wide-btn { padding: 4px 10px; font-size: var(--fs-cap); line-height: var(--lh-cap); white-space: nowrap; }
.kb-wide-btn[aria-pressed="true"] { background: var(--fill-normal); color: var(--label-normal); }
/* **[hidden]을 따로 못 박는다** — 위 display 규칙들이 브라우저 기본 [hidden]을 순서로 이기는 일을 이 리포가 여러 번 겪었다. */
.meet-head .kb-wide-btn[hidden] { display: none; }
/* 860px 이하는 이미 전체 폭이라 단추가 할 일이 없다(화면정의서). */
@media (max-width: 860px) { .meet-head .kb-wide-btn { display: none; } }
.meet-layout { display: flex; gap: var(--sp-7); align-items: flex-start; }
.meet-main { flex: 1 1 auto; min-width: 0; }
.meet-outline {
  position: sticky; top: var(--sp-5);
  flex: 0 0 200px; width: 200px;
  display: flex; flex-direction: column; gap: 2px;
  max-height: calc(100vh - var(--sp-8)); overflow-y: auto;
}
.meet-outline__label { margin: 0 0 var(--sp-2); font-size: var(--fs-cap);
  font-weight: 600; color: var(--label-assistive); }
.meet-outline__item {
  padding: 3px 0; font-size: var(--fs-sm); line-height: 18px; color: var(--label-alternative);
  text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.meet-outline__item:hover { color: var(--label-normal); }
.meet-outline__item[data-level="3"] { padding-left: var(--sp-3); font-size: var(--fs-cap); }
/* 고치는 동안은 뜻이 없다 — 자리표시(id)가 보기 화면에만 있다. */
.meet-layout:has(#view-tab-edit:checked) .meet-outline { display: none; }
/* 사이드바 폭까지 감당하기엔 좁다 — 숨기고 본문만 남긴다. */
@media (max-width: 860px) {
  .meet-outline { display: none; }
}

.btn { padding: 9px 18px; border: 0; border-radius: var(--r-sm);
  background: var(--primary-normal); color: var(--static-white);
  font-family: inherit; font-size: var(--fs-sm); font-weight: 600; cursor: pointer; }
.btn:hover { background: var(--primary-heavy); }
/* 지우기처럼 되돌릴 수 없는 것은 눈에 덜 띄게 둔다 — 저장 옆에 같은 무게로
 * 서면 잘못 누른다. 빨강은 진짜 되돌릴 수 없는 것(.btn--danger)에만 쓴다 —
 * "링크 만들기"·"열기" 같은 무해한 조용한 버튼까지 올리면 빨개지던 걸 바로잡는다. */
.btn--quiet { background: transparent; color: var(--label-alternative);
  border: 1px solid var(--line-solid-neutral); }
.btn--quiet:hover { background: var(--fill-normal); color: var(--label-normal); }
/* 지우기 계열은 hover 전에도 구분되어야 한다 — 옆의 "링크 만들기"·"열기"
 * 같은 무해한 조용한 버튼과 마우스를 올리기 전까진 똑같아 보인다는 지적. */
.btn--quiet.btn--danger { color: var(--danger, #e52222); }
.btn--quiet.btn--danger:hover { background: color-mix(in srgb, var(--danger, #e52222) 10%, transparent); }

/* ── 자료실 (#108) ────────────────────────────────── */
.page__title { margin: 0 0 var(--sp-2); font-size: var(--fs-xl); line-height: var(--lh-xl);
  font-weight: 800; letter-spacing: -.025em; }
.vault__h2 { margin: var(--sp-5) 0 var(--sp-2); font-size: var(--fs-md);
  font-weight: 700; color: var(--label-alternative); }
.stack + .note-edit { margin-top: var(--sp-4); }

/* ── 자료실 파일 목록 (구글드라이브식 한 판, #153 재검토) ───────
 * 왼쪽에 전체 폴더 트리를 늘 켜 두던 것을 없앴다 — 층이 깊어지면 글씨가
 * 잘리고 옆으로 밀렸다. 위 경로(.crumbs) + 지금 자리의 폴더·파일만
 * 보여주는 한 판으로 바꿨다. */
.vault__actions { display: flex; align-items: flex-start; gap: var(--sp-3);
  flex-wrap: wrap; margin: var(--sp-4) 0; }
.vault__actions .note-edit { margin-top: 0; }
.explorer__main { position: relative; margin-top: var(--sp-4); }
.vault__workspace {
  display: grid; grid-template-columns: minmax(0, 1fr) 280px;
  gap: var(--sp-5); align-items: start; margin-top: var(--sp-4);
}
.vault__workspace .explorer__main { margin-top: 0; min-width: 0; }
.vault__list-head { display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-3); margin-bottom: var(--sp-3); }
.vault__list-head .vault__h2 { margin: 0; }

/* 우측 보조판. 위에 흩어져 있던 만들기·올리기·공유 관리와 선택한 파일
 * 정보를 한 자리에 모은다. 목록을 읽는 동안 같은 자리에 남아 있다. */
.vault__inspector { position: sticky; top: var(--sp-5);
  display: flex; flex-direction: column; gap: var(--sp-3); min-width: 0; }
.vault__inspector-card { padding: var(--sp-4); background: var(--background-normal-normal);
  border: 1px solid var(--line-solid-neutral); border-radius: var(--r-md); }
.vault__inspector-label { margin: 0 0 var(--sp-1); color: var(--label-assistive);
  font-size: var(--fs-cap); font-weight: 700; }
.vault__inspector-title { margin: 0 0 var(--sp-1); font-size: var(--fs-lg); line-height: var(--lh-lg);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vault__inspector-actions { display: grid; gap: var(--sp-2); margin-top: var(--sp-4); }
/* **제 식구에게만 닿게 한다(#426).** 전에는 자손 선택자라 「새 폴더」
   모달 «안»의 '만들기'까지 가로로 늘렸다 — 모달이 여는 단추 바로 옆에
   있던 탓이다. 이제 모달은 body에 붙으므로 저절로 안 맞게 되는데, 그
   사실에 기대지 않고 조합자로 못 박는다. 여는 단추는 JS가 <summary>를
   <button>으로 갈아 끼워도 .note-edit 감싸개 안에 남는다. */
.vault__inspector-actions > .btn,
.vault__inspector-actions > .note-edit > .btn { width: 100%; justify-content: center; }
.vault__inspector-actions .note-edit { margin: 0; }
.vault__inspector-actions summary { list-style: none; }
.vault__inspector-actions summary::-webkit-details-marker { display: none; }
.vault__inspector-link { margin: var(--sp-3) 0 0; font-size: var(--fs-sm); }
.vault__inspector-file-head { display: grid; grid-template-columns: 28px minmax(0, 1fr);
  gap: var(--sp-3); align-items: start; }
.vault__inspector-icon { display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; }
.vault__inspector-icon .vault__fs-icon { width: 24px; height: 24px; }
.vault__inspector-name { margin: 0; font-size: var(--fs-sm); line-height: var(--lh-sm);
  overflow-wrap: anywhere; }
.vault__inspector-meta { display: flex; flex-direction: column; gap: var(--sp-2);
  margin: var(--sp-4) 0 0; font-size: var(--fs-cap); }
.vault__inspector-meta div { display: grid; grid-template-columns: 68px minmax(0, 1fr); gap: var(--sp-2); }
.vault__inspector-meta dt { color: var(--label-assistive); }
.vault__inspector-meta dd { margin: 0; color: var(--label-normal); overflow-wrap: anywhere; }
.vault__inspector-note { margin: var(--sp-3) 0 0; padding-top: var(--sp-3);
  border-top: 1px solid var(--line-solid-alternative); overflow-wrap: anywhere; }
.vault__inspector-selection[hidden], .vault__inspector-note[hidden],
.vault__inspector-meta [hidden] { display: none; }
/* 끌어다 놓기(드롭박스·구글드라이브 벤치마킹). 사유는 여전히 사람이
 * 적어야 해서 놓는다고 바로 올라가진 않는다 — 올리기 칸을 열고
 * 골라 둔 파일 취급만 해 준다. 파일 영역 전체가 놓는 자리다. */
.vault__drop-hint {
  position: absolute; inset: 0; z-index: 5;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-2);
  background: color-mix(in srgb, var(--primary-normal) 6%, var(--background-normal-normal) 94%);
  border: 2px dashed var(--primary-normal); border-radius: var(--r-md);
  color: var(--primary-normal); font-weight: 600; pointer-events: none;
}
.vault__drop-hint svg { width: 32px; height: 32px; }
.vault__drop-hint p { margin: 0; }
/* 반복되는 함정: [hidden] 기본 규칙(display:none)을 이 파일 위쪽의
 * display:flex가 소스 순서로 이긴다 — 여기서 다시 못 박는다. */
.vault__drop-hint[hidden] { display: none; }

/* 한 줄에 이름·담당·개수·날짜. 이름만 늘어나고 나머지는 오른쪽에 붙는다. */
.vault__row { display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: var(--background-normal-normal); border-radius: var(--r-md);
  border: 1px solid var(--line-solid-neutral); color: var(--label-normal); }
a.vault__row:hover { background: var(--fill-alternative); text-decoration: none; }
.vault__name { font-weight: 600; min-width: 0; }
/* .tag는 원래 여러 줄로 접히는 목록(project 태그들)용이라 margin-bottom이
 * 있다 — 위아래 여백이 짝 안 맞아 이 줄(.vault__row)의 가운데 정렬과
 * 부딪혀 살짝 떠 보인다. 이 줄 안에서는 gap이 간격을 맡으니 지운다. */
.vault__row .tag { margin-bottom: 0; margin-right: 0; }
/* 개수부터 오른쪽으로 민다. 가운데가 비어도 줄이 안 흔들린다. */
.vault__count { margin-left: auto; }
.vault__row form { margin: 0; }
/* 공유 주소는 길어서 줄을 밀어낸다. 줄을 접고 아랫줄에 통째로 앉힌다 —
 * 안 접으면 이름이 한 자씩 세로로 쪼개진다. */
/* --wrap도 같은 문제다: 회의록 사진 줄의 마크다운 스니펫도 길어서 줄을 민다. */
.vault__row--share, .vault__row--wrap { flex-wrap: wrap; }
.vault__row--share .vault__name, .vault__row--wrap .vault__name { flex: 1 1 auto; min-width: 0; }
.vault__row--wrap .meet__snip { flex: 1 0 100%; margin-top: var(--sp-2); overflow-x: auto; white-space: nowrap; }
/* 공유 주소 + 복사 단추. 주소는 길어서 줄을 통째로 차지하고(위 --wrap과
 * 같은 이유), 단추는 그 줄 오른쪽에 붙는다.
 *
 * **min-width: 0이 여기 있어야 한다.** flex 아이템의 자동 최소 크기가
 * min-width: auto라, 안쪽 <code>가 white-space: nowrap이면 그 min-content
 * 폭(주소 전체 길이) 아래로 안 줄어든다. 그러면 이 줄이 부모 칸을 뚫고
 * 나가 화면 전체가 가로로 밀린다(모바일 실사용 지적 — 390px에서 584px).
 * 자식(.meet__snip)에만 min-width: 0을 줘봐야 부모가 버티면 소용없다. */
.vault__share-link { display: flex; align-items: center; gap: var(--sp-2); flex: 1 0 100%; min-width: 0; margin-top: var(--sp-2); }
.vault__share-link .meet__snip { flex: 1 1 auto; min-width: 0; overflow-x: auto; white-space: nowrap; }
.vault__share-link .btn { flex: none; }
.vault__row .btn { padding: 4px 10px; font-size: var(--fs-cap); }

/* 폴더와 파일을 눈으로 가른다. 같은 모양이면 목록이 한 덩어리로 보인다. */
/* 폴더 표시. 이모지를 쓰면 기기마다 다른 그림이 나온다 — moa 색으로 직접 그린다.
 * 흰 몸통 + 파란 테두리(파일 아이콘과 같은 스타일). CSS 안 url()이라
 * app.css 자체가 새로 받아져도 이 그림 주소는 안 바뀐다 — 브라우저가
 * 휴리스틱 캐싱으로 옛 그림을 계속 물고 있는다(_static_version 주석과
 * 같은 문제). ?v=를 직접 박아 두고, 그림을 고칠 때마다 숫자를 올린다. */
.vault__row--folder .vault__name { display: inline-flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.vault__row--folder .vault__name::before {
  content: ""; flex: 0 0 auto; width: 20px; height: 20px;
  background: url("/static/img/folder.svg?v=3") center / contain no-repeat;
}
/* **지식창고는 자료실과 다른 그림을 쓴다.** 둘 다 폴더 줄이라 같은 파란
 * 폴더를 쓰면 화면만 보고 어디에 있는지 헛갈린다. 지식창고는 Confluence처럼
 * "페이지 안의 페이지"라 폴더가 아니라 페이지 묶음으로 그리고, 색도 파랑
 * 대신 보라로 갈랐다 — 모양만 바꾸면 20px에서 잘 안 갈린다. */
.vault__row--kb .vault__name::before {
  background-image: url("/static/img/kb-folder.svg?v=1");
}

/* 이름이 길어도 줄을 안 밀고 말줄임표로 줄인다 — 굳이 두 줄로 안 접는다. */
.vault__name-text { flex: 1 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
/* ── 자료실 파일·폴더 목록 (그리드) ─────────────────────────
 * 21st 1안 채택(#153 UI 정리). 이름·올린 사람·날짜·크기가 줄마다 flex로
 * 흘러다녀 세로로 안 맞았다 — 고정 열로 맞추고, 폴더/파일 아이콘과 여는
 * 삼각 표시로 구분한다. vault.html의 프로젝트 줄(.vault__row--folder)과는
 * 다른 화면이라 새 이름(.vault__fs-*)을 쓴다 — 거길 건드리면 프로젝트
 * 목록까지 같이 바뀐다. */
.vault__fs-head {
  display: grid; grid-template-columns: minmax(0,1fr) 110px 78px 64px 40px;
  gap: var(--sp-3); padding: 0 var(--sp-4) var(--sp-2);
  font-size: 11px; font-weight: 600; letter-spacing: .02em; color: var(--label-alternative);
}
/* 열 이름을 누르면 폴더·파일 각 그룹을 그 기준으로 정렬하고, 같은 열을
 * 다시 누르면 방향을 뒤집는다. */
.vault__fs-sort { color: inherit; text-decoration: none; }
.vault__fs-sort:hover { color: var(--label-normal); text-decoration: none; }
.vault__fs-sort.is-on { color: var(--label-normal); }
.vault__fs-sort--num { text-align: right; }
/* 폴더와 파일을 같은 목록에 둔다. 서로 다른 카드 묶음으로 가르면 지금
 * 자리의 항목을 한 번에 훑지 못한다. 폴더는 늘 위에 두되, 폴더·파일 각
 * 그룹 안에서는 같은 정렬 기준을 따른다 — 파일 관리자에서 익숙한 규칙이다. */
.vault__fs { display: flex; flex-direction: column; background: var(--background-normal-normal);
  border: 1px solid var(--line-solid-neutral); border-radius: var(--r-md); }
.vault__file { border-top: 1px solid var(--line-solid-alternative); }
.vault__file:first-child { border-top: 0; }
.vault__fs-row { border: 0; }
.vault__file:first-child .vault__fs-row { border-top-left-radius: var(--r-md); border-top-right-radius: var(--r-md); }
.vault__file:last-child .vault__fs-row { border-bottom-left-radius: var(--r-md); border-bottom-right-radius: var(--r-md); }
.vault__fs-row:hover { background: var(--fill-alternative); }
.vault__fs-row-main {
  display: grid; grid-template-columns: minmax(0,1fr) 110px 78px 64px 40px;
  gap: var(--sp-3); align-items: center; padding: var(--sp-3) var(--sp-4);
}
.vault__fs-name { display: inline-flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.vault__fs-icon { flex-shrink: 0; width: 22px; height: 22px; color: var(--label-assistive); }
.vault__fs-icon--folder { width: 22px; height: 22px;
  background: url("/static/img/folder.svg?v=3") center / contain no-repeat; }
.vault__fs-name-text { font-size: var(--fs-sm); font-weight: 500; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.vault__fs-row--folder .vault__fs-name-text { font-weight: 600; }
.vault__fs-meta { font-size: var(--fs-cap); color: var(--label-alternative);
  font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vault__fs-meta--num { text-align: right; }
.vault__fs-end { display: flex; justify-content: flex-end; }
.vault__fs-del { width: 20px; height: 20px; border: 0; border-radius: var(--r-sm);
  background: transparent; color: var(--label-assistive); font-size: 15px; line-height: 1;
  cursor: pointer; }
.vault__fs-del:hover { background: var(--fill-normal); color: var(--danger, #e52222); }

.vault__fs-row.is-archived .vault__fs-name-text,
.vault__fs-row.is-archived .vault__fs-icon { color: var(--label-assistive); }
.vault__fs-row.is-archived .vault__fs-icon--folder { filter: grayscale(1); opacity: .5; }

/* 폴더도 같은 열에 맞추되 첫 칸은 비워 둔다 — 묶음 작업은 파일만 한다. */
.vault__fs-row--item .vault__fs-row-main { grid-template-columns: 24px minmax(0,1fr) 110px 78px 64px 40px; }
.vault__fs-head--file { grid-template-columns: 24px minmax(0,1fr) 110px 78px 64px 40px; }
.vault__fs-check { width: 18px; height: 18px; margin: 0; }
.vault__fs-check-space { display: block; width: 18px; height: 18px; }
.vault__file.is-selected {
  background: color-mix(in srgb, var(--primary-normal) 8%, var(--background-normal-normal) 92%);
}

/* 파일이 하나도 없을 때. 회색 글자 한 줄만 있으면 고장난 것처럼 보인다 —
 * 점선 상자 + 아이콘으로 "여기 담는 자리"라는 걸 보여준다. 전체 화면용
 * ui.empty()는 여기 쓰기엔 너무 크다(위에 이미 폴더 목록이 있다). */
/* 이제 .vault__fs 자체가 흰 카드라 여기 또 흰 배경·둥근 바깥 테두리를
 * 겹치면 상자 속 상자가 된다 — 점선 놓는 자리만 안쪽에 살짝 띄워 보여준다. */
.vault__fs-empty {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-2);
  margin: var(--sp-3); padding: var(--sp-6) var(--sp-4);
  border: 1px dashed var(--line-solid-normal); border-radius: var(--r-sm);
  text-align: center;
}
.vault__fs-empty .vault__fs-icon { width: 28px; height: 28px; color: var(--label-assistive); opacity: .6; }
.vault__fs-empty p { margin: 0; color: var(--label-normal); font-size: var(--fs-sm); }
.vault__fs-empty p.meta { color: var(--label-assistive); }

@media (max-width: 560px) {
  .vault__fs-head { display: none; }
  .vault__fs-row-main { grid-template-columns: minmax(0,1fr) 20px; }
  .vault__fs-row--item .vault__fs-row-main { grid-template-columns: 24px minmax(0,1fr) 36px; }
  .vault__fs-meta { display: none; }
}

/* 묶음 받기 줄. 하나라도 고르기 전엔 자리를 안 차지한다.
 * [hidden]을 따로 못 박는 이유 — display:flex를 준 클래스가 있으면
 * 기본 [hidden]{display:none}보다 이 규칙이 나중에 와서 이긴다. */
.vault__dl-bar { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2);
  margin: var(--sp-4) 0 0; padding-top: var(--sp-3);
  border-top: 1px solid var(--line-solid-alternative); }
.vault__dl-bar[hidden] { display: none; }

/* 올리기·받기 진행률. 바이트 진행률(올리기)과 개수 진행률(받기) 둘 다
 * 이 모양을 같이 쓴다. */
.upload-progress { display: flex; align-items: center; gap: var(--sp-2); margin: var(--sp-2) 0; }
.upload-progress[hidden] { display: none; }
.upload-progress__bar { flex: 1 1 auto; height: 6px; border-radius: var(--r-pill);
  background: var(--fill-normal); overflow: hidden; }
.upload-progress__fill { height: 100%; width: 0; background: var(--primary-normal);
  border-radius: var(--r-pill); transition: width .2s ease; }
.upload-pick { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }

/* 원문 칸에 붙여넣어 사진으로만 붙은 그림 목록(#316). 본문에 자국이 안
 * 남으니 여기서 보여주고 빼게 한다 — 안 그러면 잘못 붙여넣어도 물릴 수 없다. */
.js-md-paste-list { list-style: none; margin: var(--sp-2) 0 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--sp-1); }
.js-md-paste-list li { display: flex; align-items: center; gap: var(--sp-2); }

/* 올리기 줄 하나당 조각 진행률. 파일마다 따로 보여준다 — 여러 개를
 * 한꺼번에 골랐을 때 어느 게 끝났는지 알아야 한다.
 * 부모(.note-edit__form)가 align-items:flex-start라 자식 너비가 내용
 * 크기로 줄어든다(shrink-to-fit) — width:100%를 안 주면 폴더째 올릴 때
 * 긴 경로가 이 칸을 옆으로 늘려서 팝업 밖으로 밀려난다. */
.upload-queue { display: flex; flex-direction: column; gap: var(--sp-1); margin: var(--sp-2) 0;
  width: 100%; }
.upload-queue[hidden] { display: none; }
.upload-queue__row { display: flex; align-items: center; gap: var(--sp-2); font-size: 13px; }
.upload-queue__name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; color: var(--label-normal); }
.upload-queue__size { flex: none; color: var(--label-alternative); }
.upload-queue__bar { flex: 0 1 120px; height: 6px; border-radius: var(--r-pill);
  background: var(--fill-normal); overflow: hidden; }
.upload-queue__fill { height: 100%; width: 0; background: var(--primary-normal);
  border-radius: var(--r-pill); transition: width .2s ease; }
.upload-queue__status { flex: none; width: 5.5em; text-align: right; color: var(--label-alternative); }

/* "⋯" 메뉴(드롭박스·드라이브 벤치마킹). 줄을 누르면 이력·공유·고치기·
 * 지우기가 한꺼번에 펼쳐지던 걸 그만두고, 메뉴를 열어 하나씩만 고른다.
 * 항목마다 <details name="menu-{id}">로 같은 이름을 줘서, 하나를 열면
 * 나머지는 자바스크립트 없이 저절로 접힌다(브라우저 기본 동작).
 * 바깥을 눌러 메뉴를 닫는 것은 못 한다 — moa는 이 화면 올리기·받기 말고는
 * 자바스크립트를 안 쓴다는 원칙을 지킨다(폴더 만들기·올리기와 같은 한계). */
.vault__menu { position: relative; }
.vault__fs-row-main > .vault__menu { justify-self: end; }
.vault__menu-btn {
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border: 1px solid var(--line-solid-alternative);
  border-radius: var(--r-sm); background: var(--fill-alternative);
  color: var(--label-alternative); font-size: 20px; font-weight: 700;
  line-height: 1; cursor: pointer; list-style: none;
}
.vault__menu-btn::-webkit-details-marker { display: none; }
.vault__menu-btn:hover, details.vault__menu[open] > .vault__menu-btn {
  background: var(--fill-normal); color: var(--label-normal); }

.vault__menu-panel {
  position: absolute; right: 0; top: calc(100% + var(--sp-1)); z-index: 20; width: 300px;
  background: var(--background-normal-normal); border: 1px solid var(--line-solid-neutral);
  border-radius: var(--r-md); box-shadow: 0 4px 16px color-mix(in srgb, black 12%, transparent);
  padding: var(--sp-2);
}
.vault__menu-item { border-top: 1px solid var(--line-solid-alternative); }
.vault__menu-item:first-child { border-top: 0; }
.vault__menu-item > summary,
button.vault__menu-open {
  padding: var(--sp-2); font-size: var(--fs-sm); font-weight: 600; color: var(--label-normal);
  cursor: pointer; list-style: none; border-radius: var(--r-sm);
}
.vault__menu-item > summary::-webkit-details-marker { display: none; }
.vault__menu-item > summary:hover,
button.vault__menu-open:hover { background: var(--fill-normal); }
/* 모달로 올라간 메뉴 항목의 여는 단추(#426). modal-form.js가 <summary>를
   <button>으로 갈아 끼우므로 **같은 선언을 같이 받아야** 메뉴 한 줄이
   나머지와 똑같이 보인다 — 따로 적어 두면 한쪽만 바뀌는 날이 온다.
   리셋은 단추에만 준다. 없으면 브라우저 기본 테두리·회색 배경·가운데
   정렬을 뒤집어쓴다. */
button.vault__menu-open {
  display: block; width: 100%; text-align: left;
  border: 0; background: none; font: inherit;
}
.vault__menu-item--danger > summary { color: var(--danger, #e52222); }
.vault__menu-body { padding: 0 var(--sp-2) var(--sp-3); }
/* 남의 자료라 고치기·지우기를 안 그릴 때 그 자리에 두는 안내(#341).
   summary가 없어 위 여백을 직접 준다 — vault__menu-body는 여는 글
   바로 아래에 붙는 것이 전제라 padding-top이 0이다. */
.vault__menu-note { padding: var(--sp-2); font-size: var(--fs-sm); }
/* 덮어쓴 적 있는 파일에만 붙는 판수(#337). 이름 옆 작은 알약이라
   프로젝트 태그(hue-*)와 안 겹치게 색을 안 쓴다 — 목록에서 눈에 띄는
   것은 파일 이름이어야 한다. */
/* 이름과 판수를 한 칸에 묶는다 — 줄이 6열 그리드라 판수를 밖에 두면
   칸을 하나 더 차지해 뒤 열이 전부 밀린다. min-width는 0이어야 긴
   이름이 줄어들며 잘린다(자동 최소 크기가 안 줄어든다). */
.vault__fs-namecell { display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--sp-1); min-width: 0; }
.vault__fs-name-line { display: flex; align-items: center; gap: var(--sp-2);
  width: 100%; min-width: 0; }
.vault__fs-name-line .vault__fs-name { min-width: 0; }
.vault__fs-subline { display: flex; align-items: center; gap: var(--sp-2);
  width: 100%; min-width: 0; padding-left: calc(22px + var(--sp-2)); }
.vault__vers {
  /* 글자에 빈칸이 생겼다("버전 2개", #353) — nowrap이 없으면 알약 안에서
     두 줄로 접혀 줄 높이가 들쭉날쭉해질 수 있다. */
  flex: none; white-space: nowrap; padding: 1px 6px;
  border-radius: var(--r-pill); background: var(--fill-normal);
  font-size: var(--fs-cap); font-weight: 600; color: var(--label-alternative);
}
.vault__vers:hover { color: var(--label-normal); text-decoration: none; }
.vault__lock { display: inline-flex; align-items: center; gap: var(--sp-1);
  flex: none; white-space: nowrap; padding: 1px 6px;
  border: 1px solid var(--line-solid-neutral); border-radius: var(--r-pill);
  background: var(--background-normal-normal); color: var(--label-alternative);
  font-size: var(--fs-cap); font-weight: 600; }
.vault__lock svg { flex: none; }
/* 판 지우기 줄(#337). `.pick`을 그대로 쓰면 아래 여백(--sp-4)이 붙어 줄
   높이가 들쭉날쭉해지고, flex 아이템이 글자 밑으로 줄어 "지우기"가 두 줄로
   접힌다 — 실제로 그렇게 그려졌다. 입력칸만 줄고 단추는 안 줄게 한다. */
.vault__vers-kill { display: flex; align-items: center; gap: var(--sp-2); margin: 0; }
.vault__vers-kill .field { min-width: 0; }
.vault__vers-kill .btn { flex: none; white-space: nowrap; }
/* 손으로 지운 판(#349). 번호는 남기되 살아 있는 판과 무게를 달리한다 —
   "2판이 왜 없지"에 답하는 자리지 읽을 거리가 아니다. opacity만 쓴다:
   글자 크기나 여백을 줄이면 줄 높이가 들쭉날쭉해진다. */
.vault__row.is-gone { opacity: .55; }
.vault__menu-body .note-edit__form,
.vault__menu-body .pick { margin-top: 0; }

/* 여는 손잡이가 누를 것으로 보여야 한다. 파란 글씨만 두면 링크로 읽힌다. */
.vault__act { display: inline-flex; align-items: center; gap: var(--sp-2);
  margin-top: var(--sp-3); padding: 7px 14px; cursor: pointer; list-style: none;
  border: 1px solid var(--line-solid-normal); border-radius: var(--r-sm);
  font-size: var(--fs-sm); font-weight: 600; color: var(--label-normal); }
.vault__act::-webkit-details-marker { display: none; }
.vault__act::before { content: "+"; font-weight: 700; color: var(--label-assistive); }
.vault__act:hover { background: var(--fill-alternative); }

@media (max-width: 560px) {
  /* 예전엔 이름을 아랫줄로 내려 두 줄을 만들었다 — 이름은 말줄임표로
     줄이고(.vault__name-text) 한 줄에 붙인다. 조직 배지·날짜 같은
     보조 정보만 접는다(파일 탐색기의 .vault__fs-meta와 같은 방식).
     .vault__url(인프라 서버 주소 링크)은 .meta를 곁들여 쓰지만 보조
     정보가 아니라 본체라 여기서 같이 접으면 안 된다(실사용 지적 —
     모바일에서 주소 링크 자체가 안 보였다). .vault__purpose(용도)도
     같은 이유로 뺀다 — "이 주소가 그 서비스다"를 알려주는 게 용도라,
     데스크톱에서만 보이고 모바일에서 접히면 정작 필요할 때 안 보인다
     (실사용 지적, 재현됨). */
  .vault__row { padding: var(--sp-3); gap: var(--sp-2); }
  .vault__row > .meta:not(.vault__count):not(.vault__url):not(.vault__purpose) { display: none; }
}

/* 조직 제목. 조직이 하나면 아예 안 그린다 — 대신 "전체"로 볼 때 조직이
 * 바뀌는 자리에 구분선만 긋는다. 제목을 달면 목록이 길어지고 일간·주간·
 * 찾기와 방식이 갈린다(#130 결정 유지) — 줄마다 붙는 .vault__org 배지로
 * 충분하다. */
.vault__row--org-sep { margin-top: var(--sp-3); padding-top: var(--sp-4);
  border-top: 1px solid var(--line-solid-alternative); }

/* 아카이브는 색을 죽이되 **표시도 붙인다** — 색만으로 가르면 색을 못 보는
 * 사람에게 안 보인다. */
.vault__row.is-archived { background: var(--fill-normal); }
.vault__row.is-archived .vault__name { color: var(--label-assistive); font-weight: 500; }
.vault__row.is-archived .vault__name::before { filter: grayscale(1); opacity: .5; }
/* "전체"로 볼 때 어느 조직 것인지. 이름만으로는 공통자료를 못 가른다. */
.vault__org { padding: 1px 7px; border-radius: var(--r-pill);
  background: var(--fill-normal); font-size: var(--fs-cap); font-weight: 600; }

/* 파일이 무엇인지 파일명 바로 아래에 맞춘다. 줄 바깥에 두면 체크박스 아래에서
 * 시작해 어느 파일의 설명인지 한눈에 이어지지 않았다. */
.vault__note { flex: 0 1 auto; min-width: 0; margin: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }

/* **이름 칸이 먼저 굶는다(#397 리뷰).** 이 레이아웃의 고정 비용은 보조판
 * 280px + 올린사람·날짜·크기·메뉴 292px다. 둘을 빼고 남는 것이 전부 이름
 * 칸인데, 보조판을 접는 중단점이 860px이면 그 위 구간에서 이름이 굶는다 —
 * 1024px에서 재 보니 이름 74px에 메타 292px이었고 「충전기 제안서.pptx」가
 * 101px을 요구해 잘렸다. 표에서 제일 중요한 칸이 제일 좁은 꼴이다.
 *
 * 1150px에서 접는다. 1024(맥북에어·아이패드 가로·반쪽 창)가 안쪽으로
 * 들어오고, 1280에서는 이름이 330px 나온다. */
@media (max-width: 1150px) {
  .vault__workspace { grid-template-columns: minmax(0, 1fr); }
  .vault__inspector { position: static; order: -1; display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 560px) {
  .vault__workspace { gap: var(--sp-3); }
  .vault__inspector { display: flex; }
  .vault__inspector-card { padding: var(--sp-3); }
  .vault__list-head { margin-top: var(--sp-2); }
}
/* .vault__panel·.vault__panel-block·.vault__panel-h은 위 "열리면 나오는 판"
 * 블록에서 정의한다 — 여기서 또 두면 뭐가 실제로 먹는지 헷갈린다. */
.vault__log { margin: 0; padding-left: var(--sp-4);
  font-size: 13px; line-height: 20px; color: var(--label-alternative); }

/* ── 디자인 ───────────────────────────────────────── */
.design__name { margin: 0; font-size: var(--fs-lg); font-weight: 700; }
.design__who { margin: 0 0 var(--sp-3); font-size: var(--fs-sm); }
.design__days { margin: var(--sp-3) 0 0; padding: 0; list-style: none;
  font-size: var(--fs-sm); line-height: 22px; }
/* 날짜는 폭을 고정해 제목이 세로로 줄을 맞춘다 — 안 맞추면 눈이 매번 다시 찾는다. */
.design__day { display: inline-block; width: 92px; color: var(--label-assistive); }
.design__titles { color: var(--label-neutral); }
/* 시안은 카드 폭을 3등분해 채운다. 고정 폭으로 늘어놓으면 셋째 장이 잘리고
   스크롤바가 생겨, 한눈에 보라는 자리에서 오히려 조작을 요구한다. */
.shots { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-3); }
.shot { margin: 0; min-width: 0; }
.shot img {
  width: 100%; height: 160px; object-fit: cover; object-position: top;
  border-radius: var(--r-sm); border: 1px solid var(--line-solid-neutral);
  background: var(--fill-normal); display: block;
}
/* 이름이 길면 줄여 붙인다 — 캡션이 두 줄로 늘면 카드마다 높이가 달라진다. */
.shot figcaption { margin-top: var(--sp-1); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }

/* ── 카드(주간·일별) ──────────────────────────────── */
.card { background: var(--background-normal-normal); border-radius: var(--r-md);
  border: 1px solid var(--line-solid-neutral); padding: var(--sp-5); }
.card__head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2);
  margin-bottom: var(--sp-3); }
.card__head .tag { margin-bottom: 0; }
.dailies { margin-top: var(--sp-4); border-top: 1px solid var(--line-solid-neutral);
  padding-top: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-4); }
/* 21st 3안 채택(#153 UI 정리). 날짜를 왼쪽 고정 칸으로 빼서 세로로 맞춘다 —
 * 전엔 "날짜 · 프로젝트"가 본문과 같은 줄에 섞여 스캔하기 느렸다. */
.daily { display: grid; grid-template-columns: 52px 1fr; gap: var(--sp-3); }
.daily__date { padding-top: 2px; font-variant-numeric: tabular-nums; }
.daily__head { margin: 0 0 var(--sp-1); }
.daily__body { font-size: 14px; line-height: 22px; color: var(--label-alternative); }

/* "요약" 라벨. "직접 쓴 기록"과 같은 무게로 둬서 AI가 쓴 것과 사람이 쓴 것을
 * 눈이 같은 방식으로 가르게 한다. */
.card__label { margin: 0 0 var(--sp-2); font-size: var(--fs-sm); font-weight: 700;
  color: var(--label-neutral); }
.card__section + .note-own { margin-top: var(--sp-4); }
/* 일간에서 한 사람이 여러 프로젝트를 했으면 한 카드 안에 구간이 여럿 선다 —
 * 구분선으로만 가른다(#153, 주간과 같은 문법). */
.card__section + .card__section { margin-top: var(--sp-4); padding-top: var(--sp-4);
  border-top: 1px solid var(--line-solid-neutral); }
.card__section .tag { margin-bottom: var(--sp-2); }

@media (max-width: 560px) {
  .daily { grid-template-columns: 1fr; gap: 2px; }
  .daily__date { padding-top: 0; }
}

@media (max-width: 520px) {
  /* 좁으면 날짜 이동을 다음 줄로 내린다. 한 줄에 우겨넣으면 "일요일"이
   * 글자마다 줄바꿈돼 세로로 쪼개진다. */
  .daynav { margin-left: 0; width: 100%; }
  .page { padding: var(--sp-5) var(--sp-4) var(--sp-8); }
  :root { --rail: 36px; --avatar: 32px; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ── 바깥에서 보는 화면 (#112) ────────────────────
 * base.html을 안 쓴다 — 거기엔 메뉴·찾기·로그인한 사람 이름이 들어 있다.
 * 안으로 들어가는 길을 안 주는 것이 이 화면의 요지다.
 */
.out { display: grid; place-items: center; min-height: 100vh; padding: var(--sp-6); }
/* **둘 다 min-width: 0이다(#414).** .out이 grid라 이 둘은 그리드 칸이고,
 * 기본 min-width:auto로 «내용보다 작아지지 못한다» — 안쪽 코드블록이 밀면
 * max-width까지 벌어지고, place-items:center가 그걸 가운데 놓아 375px
 * 화면에서 양옆이 잘렸다(실측 넘침 1,099px). 왼쪽으로는 스크롤도 안 돼
 * 되돌릴 수가 없다. .vault__share-link에서 겪은 것과 같은 함정이다.
 *
 * .out__box는 지금 안 깨진다 — 짧은 글뿐이고 body의 overflow-wrap:anywhere가
 * 긴 낱말을 쪼갠다. 그래도 같이 준다: 거기 보여 주는 것이 문서 이름이고,
 * 실측 자료실 파일 이름에 124자짜리가 있다. */
.out__box { max-width: 420px; text-align: center; min-width: 0; }
.out__doc { max-width: 720px; width: 100%; min-width: 0; }
.out__mark { display: block; margin: 0 auto var(--sp-4); }
.out__doc .out__mark { margin-left: 0; }
.out__name { margin: 0 0 var(--sp-2); font-size: var(--fs-xl); line-height: var(--lh-xl);
  font-weight: 800; letter-spacing: -.025em; }
.out .btn { display: inline-block; margin-top: var(--sp-4); text-decoration: none; }
.out .btn:hover { text-decoration: none; }

/* ── 인프라: 서버 주소 등록 — EC2에서 고르기(#인프라_EC2등록) ────────── */
/* **켜짐을 감싸개 클래스로 찾지 않는다(#379).** 전에는 .infra-add를 조상으로
 * 잡았는데, modal-form.js가 모달로 올리면 라디오와 이 칸은 <dialog>로 옮겨
 * 가고 .infra-add는 여는 단추만 든 형제 <div>에 남는다 — 선택자가 안 맞아
 * 「EC2에서 고르기」를 눌러도 아무것도 안 나왔다. 기본값이 display:none이라
 * 조용히 사라져서 #369 때 아무도 못 봤다.
 *
 * body에서 id로 찾으면 노드가 어디로 옮겨가든 맞는다 — .kb-nav의 접기가
 * 이미 쓰는 방식이다. */
.infra-add__ec2-pick { display: none; margin-bottom: var(--sp-3); }
body:has(#infra-add-ec2:checked) .infra-add__ec2-pick { display: block; }
/* "주소 등록"을 펼치면 직접입력/EC2고르기 버튼이 summary 바로 밑에 붙어
 * 있었다 — 위쪽에 숨 쉴 틈을 준다(아래는 .segmented 기본 margin-bottom로
 * 이미 넉넉하다).
 *
 * 부모가 아니라 안에 든 것으로 찾는다(#379) — 위와 같은 이유로, 모달로
 * 올라가면 부모가 바뀐다. 다른 화면의 .segmented는 안 건드린다. */
.segmented:has(#infra-add-ec2) { margin-top: var(--sp-3); }

/* ── 인프라: EC2 상태 (#인프라 EC2 상태) ──────────────────
 * 열이 9개라 좁은 화면에서 접을 도리가 없다 — 줄이지 않고 가로 스크롤만
 * 준다(자료실 파일 목록의 vault__fs-*는 열이 5개라 접었지만, 여긴 접으면
 * 정작 보려던 지표가 없어진다). 문장 하나가 1080px(page--list)를 그대로
 * 채우면 줄이 너무 길어 읽기 힘들다 — 안내 문단만 폭을 좁힌다. */
.infra__ec2-note { max-width: 560px; }
/* 칸 너비를 변수로 뺀다 — infra.js가 이 변수만 바꾸면 머리·줄 전부(같은
 * .infra__ec2-scroll 아래라 상속된다) 한 번에 늘어난다. 기본값은 기존
 * 고정폭 그대로. */
.infra__ec2-scroll {
  overflow-x: auto;
  --w-name: 220px; --w-type: 92px; --w-state: 64px; --w-ip: 110px; --w-domain: 160px;
  --w-cpu: 56px; --w-mem: 64px; --w-disk: 64px; --w-ebs: 72px; --w-checked: 120px;
}
/* 기본 칸 너비 합이 사이드바를 뺀 본문 폭보다 넓어서, 칸을 하나도 안
 * 늘려도 항상 가로 스크롤바가 떠 있었다 — macOS가 스크롤바를 늘 보여주는
 * 설정이면 특히 두꺼운 회색 바로 눈에 띈다. 스크롤 자체는 필요하니(10칸을
 * 줄이면 지표가 안 보인다) 없애지 않고, 얇고 튀지 않게만 다듬는다. */
.infra__ec2-scroll::-webkit-scrollbar { height: 6px; }
.infra__ec2-scroll::-webkit-scrollbar-track { background: transparent; }
.infra__ec2-scroll::-webkit-scrollbar-thumb {
  background: var(--line-solid-normal); border-radius: 3px;
}
.infra__ec2-scroll::-webkit-scrollbar-thumb:hover { background: var(--label-assistive); }
.infra__ec2-scroll { scrollbar-width: thin; scrollbar-color: var(--line-solid-normal) transparent; }
.infra__ec2-head, .infra__ec2-row {
  display: grid;
  grid-template-columns:
    var(--w-name) var(--w-type) var(--w-state) var(--w-ip) var(--w-domain) var(--w-cpu)
    var(--w-mem) var(--w-disk) var(--w-ebs) var(--w-checked);
  gap: var(--sp-3); align-items: center; min-width: 720px;
}
.infra__ec2-head {
  position: relative;
  padding: 0 var(--sp-2) var(--sp-2);
  font-size: 11px; font-weight: 600; letter-spacing: .02em; color: var(--label-alternative);
}
.infra__ec2-th { position: relative; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
/* 칸 경계를 잡아 끄는 손잡이. 머리에만 있어도 같은 grid-template-columns를
 * 쓰는 모든 줄이 같이 늘어난다 — 줄마다 따로 안 둬도 된다. */
.infra__ec2-resize {
  position: absolute; top: -4px; bottom: -4px; right: calc(var(--sp-3) / -2 - 1px);
  width: 9px; cursor: col-resize; touch-action: none;
}
.infra__ec2-resize::after {
  content: ""; position: absolute; top: 15%; bottom: 15%; left: 4px; width: 1px;
  background: var(--line-solid-normal);
}
.infra__ec2-resize:hover::after, .infra__ec2-resize.is-active::after {
  background: var(--primary-normal); width: 2px; left: 3.5px;
}
.infra__ec2-row {
  padding: var(--sp-3) var(--sp-2);
  border-top: 1px solid var(--line-solid-alternative);
}
.infra__ec2-row:first-of-type { border-top: 0; }
.infra__ec2-name { font-weight: 600; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.infra__ec2-num { text-align: right; }
/* 80% 이상 — 주의색(토큰의 --status-cautionary)만 쓴다. 프로젝트 색은
 * org/project 구분 전용이고 빨강(--danger)은 되돌릴 수 없는 것 전용이라
 * 둘 다 못 쓴다. */
.infra__ec2-num.is-high { color: var(--status-cautionary); font-weight: 700; }
/* IP는 meet__snip(회의록 스니펫용, 크기 기본값 그대로)을 그대로 갖다 썼더니
 * 몸통 글자 크기라 110px 칸 안에서 줄바꿈됐다 — 칸에 맞는 작은 고정폭
 * 글자로 따로 둔다. */
.infra__ec2-ip { font-size: 12px; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; min-width: 0; color: var(--label-alternative); }
.infra__ec2-domain { font-size: 12px; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; min-width: 0; display: block; }
/* 계정 소제목. org 하나가 AWS 계정을 여럿 묶을 때만 나온다(#인프라). */
.infra__ec2-account { padding: var(--sp-3) var(--sp-2) 0; font-weight: 600; }
/* 꺼진 서버는 지금 안 쓰는 것이니 통째로 흐리게 — 링크·태그 색까지 섞여
 * 있어도 grayscale로 한 번에 죽인다(글자 색을 하나하나 바꾸는 것보다
 * 안전하다 — 나중에 칸이 늘어도 자동으로 회색이 된다). */
.infra__ec2-row.is-stopped { filter: grayscale(1); opacity: .55; }
/* 사용률 경고(is-high)를 줄 맨 앞에서도 바로 보이게 — 칸을 다 훑지 않아도 된다. */
.infra__ec2-warn { color: var(--status-cautionary); margin-right: 2px; }
/* .tag의 margin-bottom:9px(줄바꿈되는 태그 목록용)가 grid의
 * align-items:center와 부딪혀 옆 칸 글자보다 위로 떠 보인다 —
 * vault__row에서 이미 한 번 겪은 것과 같은 문제(app.css:564 참고). */
.infra__ec2-row .tag { margin-bottom: 0; margin-right: 0; }
/* grid 칸의 justify-items 기본값은 stretch라, inline-block인 .tag까지
 * "상태" 칸 너비(64px)만큼 늘어난다 — 알약 배경만 넓어지고 글자는 왼쪽에
 * 그대로 있어 글자가 알약 안에서 왼쪽으로 쏠려 보인다. 늘어나지 않게
 * 원래 크기(글자+여백)만큼만 차지하게 막는다. */
.infra__ec2-row .tag { justify-self: start; }

/* ── 공지사항 (#공지사항) — 팀 공지 + 팀장 일정 ────────────── */
/* 새로 만들기는 자료실 "폴더 만들기"·인프라 "주소 등록"과 같은 자리
 * (.note-edit + .vault__act, 이미 있는 CSS를 그대로 쓴다 — 여기서
 * 새로 만든 건 공지/일정 갈래를 나누는 라디오 칸뿐이다. */
/* 날짜·만료·고정이 한 묶음이다 — 세로로 쌓으면(.note-edit__form 기본값)
 * 셋을 한눈에 견주기 어려워 한 줄로 묶고, 줄바꿈은 좁은 화면만 대비한다.
 * "위에 고정"은 라벨 위에 글이 없어(체크박스+글자 한 줄뿐) 옆 칸(라벨+
 * 입력칸 두 줄)과 줄 수가 안 맞았다 — flex-end·baseline 둘 다 써 봤지만
 * 입력칸 한가운데에 맞지 않고 위나 아래로 치우쳐 보였다(실사용 제보 2회).
 * 안 보이는 자리표시 줄을 얹어 다른 칸과 같은 두 줄 구조로 맞추고
 * align-items: flex-start로 위쪽부터 맞춘다 — 그러면 둘째 줄(입력칸·
 * 체크박스)끼리 자연히 높이가 맞는다. */
/* 관련 있는 칸을 한 줄에 펼친다. **.field의 max-width:360px를 여기서
 * 푼다** — 안 풀면 넓은 화면에서도 360px 띠로 남는다(#368). 공지가 먼저
 * 쓰던 이름(.notice-form__row)을 그대로 두고 일반 이름을 같이 건다 —
 * 디자인Q와 앞으로 옮길 폼들이 같은 규칙을 쓴다. */
.form-row,
.notice-form__row { display: flex; flex-wrap: wrap; gap: var(--sp-3) var(--sp-5); align-items: flex-start; }
/* **줄은 폼 폭을 다 쓴다.** .note-edit__form이 align-items: flex-start라
 * 자식이 내용 너비로 쪼그라든다 — 그건 버튼이 늘어나지 않게 하려고 일부러
 * 둔 것이라(바로 위 주석) 폼 쪽을 안 건드리고 줄에만 stretch를 준다.
 * 공지가 쓰던 .notice-form__row에는 안 준다 — 지금 모양을 바꾸지 않는다. */
.form-row { align-self: stretch; }
.form-row .field,
.notice-form__row .field { width: auto; max-width: none; }
/* 설명칸처럼 혼자 한 줄을 다 쓰는 것. */
.form-row--wide .field { flex: 1 1 100%; }
/* 칸 옆에 붙는 빠른 단추(#371). 날짜칸이 대표 사례다. */
.field-quick { display: inline-flex; align-items: center; gap: var(--sp-2); }
.field-quick__btns { display: inline-flex; gap: var(--sp-1); }
/* **[hidden]을 따로 못 박는다.** 바로 위 display:inline-flex가 브라우저
 * 기본 [hidden]{display:none}을 순서로 이긴다 — 단추는 숨겨져 오고 JS가
 * 켜는데, 안 막으면 스크립트가 없을 때 먹통 단추가 떠 있다(#368에서
 * .upload-dialog__head로 똑같이 겪었다). */
.field-quick__btns[hidden] { display: none; }
.notice-form__row label.meta { display: flex; flex-direction: column; gap: var(--sp-1); align-items: flex-start; }
.notice-form__pin-spacer { visibility: hidden; font-size: var(--fs-cap); line-height: 16px; }
.notice-form__pin-row { display: inline-flex; align-items: center; gap: var(--sp-1); min-height: 38px; }
/* 공지·일정 라디오에 맞춰 칸을 가른다(#213의 :has() 토글과 같은 방식). */
.notice-form:has(#notice-kind-schedule:checked) .notice-form__notice-only { display: none; }
.notice-form:not(:has(#notice-kind-schedule:checked)) .notice-form__schedule-only { display: none; }

/* 신규공지 알림 대상(#248) — "지정"을 고를 때만 사람 찾기 칸을 보여준다. */
.notify-picker { display: flex; flex-direction: column; gap: var(--sp-2); align-items: flex-start; }
.notify-picker__radios { display: flex; gap: var(--sp-3); flex-wrap: wrap; }
.notify-picker:not(:has(input[name="notify"][value="selected"]:checked)) .js-notify-input,
.notify-picker:not(:has(input[name="notify"][value="selected"]:checked)) .notify-picker__selected {
  display: none;
}
.notify-picker__selected { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.notify-chip { display: inline-flex; align-items: center; gap: 4px; }
.notify-chip__x {
  border: none; background: none; padding: 0; cursor: pointer;
  color: var(--label-assistive); font-size: var(--fs-cap); line-height: 1;
}
.notify-chip__x:hover { color: var(--label-neutral); }

/* @멘션·수신자 찾기 드롭다운(mentions.js). textarea·입력칸 바로 아래 뜬다. */
.people-panel:empty { display: none; }
.people-panel {
  display: flex; flex-direction: column; margin-top: var(--sp-1);
  border: 1px solid var(--line-solid-neutral); border-radius: var(--r-md);
  background: var(--background-normal-normal); max-height: 200px; overflow-y: auto;
}
.people-panel__item {
  display: block; width: 100%; text-align: left; border: none; background: none;
  padding: var(--sp-2) var(--sp-3); font-size: var(--fs-sm); color: var(--label-neutral); cursor: pointer;
}
.people-panel__item:hover { background: var(--fill-normal); }
/* 쓰기·미리보기 탭 토글은 위 .md-panel 자리에서 공지·지식창고까지 한
 * 번에 정의한다(줄마다 라디오 id가 달라(row.id) id 대신 공유 클래스로
 * 켠다) — 여기 남는 건 공지·지식창고 폼만 공유하는 textarea 꾸밈이다. */
.notice-form .md-panel--write textarea,
.kb-form .md-panel--write textarea { width: 100%; box-sizing: border-box; padding: 12px 14px;
  border: 1px solid var(--line-solid-normal); border-radius: var(--r-sm);
  font-family: inherit; font-size: var(--fs-md); line-height: 24px;
  color: var(--label-normal); background: var(--background-normal-normal); resize: vertical; }
.notice-form .md-panel--write textarea:focus,
.kb-form .md-panel--write textarea:focus { outline: 0; border-color: var(--primary-normal);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-normal) 12%, transparent); }

/* 지식창고 올리기 — 파일 올리기·바로 쓰기 갈래(회의록 dialog와 같은
 * :has() 토글, 다만 팝업이 아니라 인라인 disclosure라 .kb-form이 그
 * 자리를 대신한다), 쓰기·미리보기 탭은 공지 올리기와 같은 규칙. */
.kb-panel { width: 100%; display: flex; flex-direction: column; gap: var(--sp-2); }
.kb-panel--upload { display: none; }
.kb-form:has(#kb-mode-upload:checked) .kb-panel--write { display: none; }
.kb-form:has(#kb-mode-upload:checked) .kb-panel--upload { display: flex; }

/* ── 지식창고 편집기(#438) ───────────────────────────────────
 * 서드파티 CSS(ProseMirror·표·gapcursor)는 번들 산출물 kb-editor.css에 있고, 우리 화면의 모양은 여기서
 * tokens.css 값만 쓴다. 편집 영역은 «보기»와 같은 .prose를 쓴다 — 편집 중에 보이는 모양이 저장 뒤 보기 화면과 같아야 한다.
 *
 * **[hidden]을 따로 못 박는다.** 아래 규칙들이 display를 줘서 브라우저 기본 [hidden]{display:none}을 이긴다
 * (.upload-dialog__head에서 이미 겪었다). 틀 안의 hidden은 전부 이 한 줄이 지킨다. */
.kb-editor [hidden] { display: none !important; }
/* 편집기가 켜지면 «쓰기 / 미리보기» 탭(.segmented)을 숨긴다 — 그 탭은 틀 밖에 있어 위 규칙이 안 닿고,
 * .segmented의 display:flex가 [hidden]을 이긴다. */
.segmented[hidden] { display: none; }
.kb-editor { position: relative; width: 100%; box-sizing: border-box;
  border: 1px solid var(--line-solid-normal); border-radius: var(--r-sm);
  background: var(--background-normal-normal); }
.kb-editor:focus-within { border-color: var(--primary-normal);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-normal) 12%, transparent); }
/* 서식 줄과 링크 줄은 한 머리로 묶여 긴 문서에서도 위에 붙어 다닌다. */
.kb-editor__head { position: sticky; top: 0; z-index: 3; background: var(--background-normal-normal);
  border-radius: var(--r-sm) var(--r-sm) 0 0; }
/* 좁은 화면에서는 줄바꿈 없이 가로로 민다. */
.kb-editor__toolbar { display: flex; flex-wrap: nowrap; align-items: center; gap: 2px;
  padding: var(--sp-1) var(--sp-2); border-bottom: 1px solid var(--line-solid-neutral);
  overflow-x: auto; scrollbar-width: thin; }
.kb-editor__heading { flex: 0 0 auto; margin-right: var(--sp-1); padding: 3px var(--sp-2);
  border: 1px solid var(--line-solid-neutral); border-radius: var(--r-sm);
  background: var(--background-normal-normal); color: var(--label-neutral);
  font: inherit; font-size: var(--fs-sm); font-weight: 600; }
.kb-editor__btn { flex: 0 0 auto; padding: 4px var(--sp-2); border: 0; border-radius: var(--r-sm);
  background: transparent; color: var(--label-alternative); font: inherit; font-size: var(--fs-sm);
  font-weight: 600; white-space: nowrap; cursor: pointer; }
.kb-editor__btn:hover:not(:disabled) { background: var(--fill-normal); color: var(--label-normal); }
/* 눌린 모양은 라디오 탭(.segmented__item:has(input:checked))과 같은 검은 면이다. */
.kb-editor__btn[aria-pressed="true"] { background: var(--label-normal); color: var(--static-white); }
.kb-editor__btn:focus-visible, .kb-editor__heading:focus-visible { outline: 2px solid var(--primary-normal); outline-offset: 1px; }
.kb-editor__btn:disabled, .kb-editor__heading:disabled { opacity: .4; cursor: default; }
.kb-editor__btn[aria-pressed="true"]:disabled { opacity: .7; }
.kb-editor__btn[data-cmd="bold"] { font-weight: 800; }
.kb-editor__btn[data-cmd="italic"] { font-style: italic; }
.kb-editor__btn[data-cmd="strike"] { text-decoration: line-through; }
.kb-editor__sep { flex: 0 0 1px; align-self: stretch; margin: 4px var(--sp-1); background: var(--line-solid-neutral); }

.kb-editor__linkbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2); border-bottom: 1px solid var(--line-solid-neutral); background: var(--fill-alternative); }
.kb-editor__linkinput { flex: 1 1 14rem; min-width: 0; box-sizing: border-box; padding: 6px 10px;
  border: 1px solid var(--line-solid-normal); border-radius: var(--r-sm); font: inherit;
  font-size: var(--fs-sm); color: var(--label-normal); background: var(--background-normal-normal); }
.kb-editor__linkinput:focus { outline: 0; border-color: var(--primary-normal); }
.kb-editor__linkbar .btn { padding: 5px 12px; }
.kb-editor__linkmsg { flex: 1 0 100%; margin: 0; color: var(--danger, #e52222); }

/* 표 위에 뜨는 줄. 위치는 스크립트가 정한다(top·left). */
.kb-editor__tablebar { position: absolute; z-index: 4; display: flex; flex-wrap: wrap; gap: var(--sp-1);
  max-width: 100%; box-sizing: border-box; padding: var(--sp-1);
  border: 1px solid var(--line-solid-neutral); border-radius: var(--r-md);
  background: var(--background-normal-normal); box-shadow: 0 4px 16px rgba(0, 0, 0, .12); }
.kb-editor__tablebar .btn { padding: 4px 10px; font-size: var(--fs-cap); }
.kb-editor__tablebar .btn:disabled { opacity: .4; cursor: default; }

/* 슬래시 메뉴는 @멘션 드롭다운(.people-panel)과 같은 모양이다. 위치는 스크립트가 정한다. */
.kb-slash { position: absolute; z-index: 5; width: min(260px, 100%); margin-top: 0;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .12); }
.kb-slash__item { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.kb-slash__item[aria-selected="true"] { background: var(--fill-normal); color: var(--label-normal); }
.kb-slash__hint { color: var(--label-assistive); font-family: ui-monospace, SFMono-Regular, monospace;
  font-size: var(--fs-cap); }

.kb-editor__surface { padding: 12px 14px; overflow-x: auto; font-size: var(--fs-md); line-height: 24px; }
.kb-editor__surface .ProseMirror { min-height: 22rem; outline: 0; }
.kb-editor__surface .ProseMirror > :first-child { margin-top: 0; }
.kb-editor__surface .ProseMirror img { max-width: 100%; height: auto; }
/* 빈 문서의 안내 문구 — 입력칸의 placeholder 자리다. */
.kb-editor__empty > p:first-child::before { content: "글을 쓰거나 / 를 눌러 블록을 골라요";
  float: left; height: 0; color: var(--label-assistive); pointer-events: none; }
.kb-editor__uploading { color: var(--label-assistive); font-size: var(--fs-sm); }
/* 체크리스트: Milkdown은 data-checked만 그린다. 칸은 li의 ::before이고, 누르면 바뀌는 것은 core.js의 체크칸이다. */
.kb-editor__surface li[data-item-type="task"] { position: relative; padding-left: 1.7rem; list-style: none; }
.kb-editor__surface li[data-item-type="task"]::before { content: ""; position: absolute; left: 0; top: .3rem;
  width: 1rem; height: 1rem; box-sizing: border-box; border: 1.5px solid var(--label-assistive);
  border-radius: 4px; cursor: pointer; }
.kb-editor__surface li[data-item-type="task"][data-checked="true"]::before { border-color: var(--primary-normal);
  background: var(--primary-normal) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-6.5' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 100% no-repeat; }
.kb-editor__msg { margin: 0; }

/* 카드 한 장 = 공지 한 건(주간·일별 롤업과 같은 언어, #130 계열과
 * 반대로 여기는 카드가 맞다 — 본문이 있는 콘텐츠 블록이다). 머리는
 * .card__head를 그대로 쓴다 — 새 헤더 컴포넌트를 나란히 만들 이유가
 * 없다. */
/* gap을 안 쓴다 — 접혔을 때(<details> 안 summary 아닌 자식은 브라우저가
 * 렌더링에서 뺀다) flex gap이 "그 자리"만 남겨 카드가 실제 내용보다
 * 훨씬 커 보이고, 남은 한 줄은 위로 쏠려 가운데처럼 안 보였다(실사용
 * 제보: "박스 여백이 너무 많다", "가운데 정렬이 아니다" — 사실 한
 * 원인). 대신 .notice__content가 그려질 때만 margin-top으로 스스로
 * 간격을 낸다. */
.notice { display: flex; flex-direction: column; position: relative; }
.notice__title { font-weight: 700; }
.notice__when { font-size: var(--fs-sm); font-weight: 600; color: var(--primary-normal); }
.notice--pinned { border-color: var(--primary-normal); }
.notice--past { opacity: .7; }
.notice .card__head { margin-bottom: 0; padding-right: 40px; }
.notice__content { margin-top: var(--sp-3); }
/* 케밥은 summary 안이 아니라 형제로 둔다(#UI방향1 케밥과 달리 여기는
   details 하나를 또 다른 details 안에 중첩하면 HTML 자체가 무효고 —
   summary는 phrasing content만 담을 수 있어 details를 못 담는다 —
   파싱 단계에서 예상 못 한 트리로 깨진다) — .notice를 기준으로 절대
   위치로 고정해 눌러도 카드 자체는 안 건드린다.

   **접힌 <details>는 위치 기준(containing block) 계산이 어긋난다** —
   실측: 닫혀 있을 때 같은 top 값을 줘도 펼쳤을 때보다 52px 더 아래로
   그려졌다(크로미움 확인, .notice가 flex인 것과 무관 — display:block
   으로 바꿔도 동일). 펼쳤을 때는 정상이다. 원인은 명확히 못 밝혔지만
   —닫힌 details의 렌더 높이(summary만)와 절대 위치 계산에 쓰이는
   컨테이닝 블록 높이가 서로 다른 값을 쓰는 것으로 보인다— 증상은
   재현 가능하고 고정폭이라, 접힘 여부별로 다른 값을 준다. 세로
   가운데는 summary 줄(패딩 바로 아래) 기준으로 맞춘다. */
.notice > .vault__menu { position: absolute; right: var(--sp-2); top: calc(var(--sp-5) + 4px); }
/* details.notice로 한정한다 — 본문·사진 없는 카드는 <article class="notice">라
   open 속성 자체가 없어 :not([open])가 항상 걸린다. 그 경우는 접힌
   <details> 렌더 버그와 무관하니 -52px을 받으면 안 된다. */
details.notice:not([open]) > .vault__menu { top: calc(var(--sp-5) + 4px - 52px); }
.notice .vault__menu-btn { background: var(--fill-normal); color: var(--label-alternative); }
.notice .vault__menu-btn:hover { background: var(--fill-alternative); color: var(--label-normal); }
/* 게시판처럼 접어 둔다(실사용 요청) — 내용·사진이 있는 공지만 펴 볼
   화살표가 붙는다(비어 있으면 details 자체를 안 쓴다, notices.html의
   notice_card 매크로 참고). 펼치면 90도 돌아간다.

   화살표는 문자(▸)가 아니라 SVG다 — 문자는 폰트마다 그림 속 위치가
   달라 옆 글자와 세로로 안 맞아 보일 때가 있었다(실사용 지적: "불릿
   마크는 별로야"). 고정 크기 SVG는 flex 세로 가운데 정렬이 그대로
   먹는다 — 나머지 화면 아이콘(nav_icon 등)과 같은 방식으로 맞췄다. */
/* display:block을 명시한다 — 공용 규칙 .card__head가 display:flex를
 * 준다. 그대로 두면 summary가 (지금은 자식이 head-row 하나뿐이라
 * 안 드러날 뿐) 계속 flex 컨테이너로 남는다 — 나중에 summary 바로
 * 밑에 형제가 하나 더 붙으면 .card__head의 flex-wrap:wrap까지 같이
 * 살아나 아래 설명하는 옛 버그가 재현될 수 있다. block으로 눌러
 * 확실히 끈다. */
.notice > summary { cursor: pointer; list-style: none; display: block; }
.notice > summary::-webkit-details-marker { display: none; }
/* flex 자리는 summary가 아니라 감싸는 .notice__head-row 하나가 진다.
 * 실제 원인은 "summary+flex가 자식을 세로로 쌓는다"는 렌더링 특이점이
 * 아니었다 — 예전엔 화살표·고정 배지·제목·메타가 전부 한 flex-wrap
 * 줄에 같이 있어서, 좁은 화면에서 배지가 줄을 다 먹으면 제목(길 수
 * 있는 텍스트)이 flex 항목째로 다음 줄로 밀려났을 뿐이다(평범한
 * flex-wrap 동작). wrap을 아예 안 쓰는 이 줄에 화살표·제목만 남기면
 * 둘이 갈라질 일 자체가 없다 — 나머지(배지·날짜·글쓴이)는
 * .notice__meta-row에서 따로 wrap한다. */
.notice__head-row { display: flex; flex-wrap: nowrap; align-items: flex-start; gap: var(--sp-2); }
/* 화살표를 제목의 첫 줄 높이에 맞춰 올린다 — flex-start만 쓰면 제목이
 * 두 줄로 접힐 때 화살표가 제목 전체(두 줄) 가운데가 아니라 위쪽에
 * 붙는데, 글머리처럼 읽히는 자리라 그게 맞다(실사용 지적: "제목이
 * 미들로 와야해" — 화살표와 제목 *첫 줄*이 나란해야 한다는 뜻이었다). */
.notice__chevron { flex: 0 0 auto; margin-top: 5px; color: var(--label-assistive);
  transition: transform .15s ease; }
.notice[open] .notice__chevron { transform: rotate(90deg); }
/* 제목은 화살표와 항상 같은 줄에 선다. 고정 배지·날짜·글쓴이 같은
 * 부가정보는 title과 한 flex-wrap 목록에 같이 두면 좁은 화면에서
 * 부가정보가 줄을 다 차지해 제목이 혼자 다음 줄로 밀려났다 — 그래서
 * 제목을 뺀 나머지만 .notice__meta-row로 따로 감싸 자기 줄에서만
 * wrap하게 한다. */
.notice__body-head { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; }
.notice__meta-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.notice__meta-row:empty { display: none; }
/* AI 요약(회의록)은 소제목이 문서 안 갈래 표시라 본문 크기와 가까워도
 * 됐지만, 공지는 그 자체가 짧은 한 장짜리 글이라 소제목이 곧 그 글의
 * 제목처럼 읽혀야 한다 — 한 단계씩 키운다(실사용 지적: "헤더 글씨가
 * 왜 이렇게 작냐"). 새 크기를 만들지 않고 있는 단계표만 옮겨 쓴다. */
.notice .prose h1 { font-size: var(--fs-xl); line-height: 32px; }
.notice .prose h2 { font-size: var(--fs-lg); line-height: 24px; }
.notice .prose h3, .notice .prose h4 { font-size: var(--fs-md); line-height: 25px; }
/* 목록 썸네일이 아니라 글과 나란히 보는 사진이다(실사용 요청: "적당한
 * 크기로 크게"). 원본 비율 그대로 두고(object-fit 없이) 폭만 재운다 —
 * 잘라내면(cover) 안내판·현수막처럼 글자가 든 사진에서 그 글자가
 * 잘린다. 업로드 때 이미 1600px로 줄여 두므로(_resized) 여기서
 * max-width만 잡아도 큰 원본을 그대로 내려받는 일은 없다. */
.notice__photos { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.notice__photo { width: 100%; max-width: 420px; height: auto; border-radius: var(--r-sm);
  border: 1px solid var(--line-solid-neutral); }

/* 지난 공지 — 자료실 아카이브(#130)와 뜻은 같지만, 쌓일수록 접어 두는
 * 쪽이 맞아 늘 펴진 섹션 대신 details로 접는다(#공지사항 설계). */
.notice-past { margin-top: var(--sp-5); }
.notice-past > summary { list-style: none; cursor: pointer; }
.notice-past > summary::-webkit-details-marker { display: none; }
.notice-past > .stack { margin-top: var(--sp-3); }

/* ── 디자인 큐 — 칸반보드(결정기록_디자인큐) ────────────────────── */
/* "카드 등록" 펼침 바로 밑에 보드가 붙어 있었다 — 위쪽에 숨 쉴 틈을 준다
 * (.segmented:has(#infra-add-ec2)가 같은 문제를 --sp-3로 푼 것과 같은 이유 —
 * 여기는 칸반 칼럼 전체와 떨어뜨리는 자리라 한 단계 더 큰 --sp-5를 쓴다). */
.board { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-4);
  margin-top: var(--sp-5); }
@media (max-width: 720px) { .board { grid-template-columns: 1fr; } }
.board__col { background: var(--fill-alternative); border-radius: var(--r-md); padding: var(--sp-3); }
.board__col-title { font-weight: 700; margin: 0 0 var(--sp-3); }
.board__card {
  background: var(--background-normal-normal); border: 1px solid var(--line-solid-alternative);
  border-radius: var(--r-sm); padding: var(--sp-3); margin-bottom: var(--sp-3); cursor: grab;
}
/* 카드 아래 단추들. **form은 블록이라 그냥 두면 위아래로 쌓인다** —
 * 되돌리기가 생기면서 한 카드에 단추가 둘이 될 수 있다(#326). 한 줄에
 * 세워 카드가 길어지지 않게 한다. */
.board__acts { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

.board__card.is-dragging { opacity: .4; }
.board__col.is-dragover { outline: 2px dashed var(--primary-normal); outline-offset: -2px; }
.board__card-title { display: block; font-weight: 600; margin: 0 0 var(--sp-2); color: var(--label-normal);
  -webkit-user-drag: none; }
.board__card-title:hover { text-decoration: underline; }
/* 우선순위는 색으로 튀지 않게 왼쪽 굵은 테두리 하나로만 — 색 자체는
 * hue-*(프로젝트 전용)·빨강(--danger, 되돌릴 수 없는 것 전용)과 안 겹치게
 * status-cautionary 계열만 쓴다(EC2 상태 80% 표시와 같은 원칙). */
/* 위 근거로 둔 예외다. 마커는 **한 줄**이어야 한다 — 여러 줄 주석으로 쓰면
 * 규칙 줄과 사이가 벌어져 안 먹는다(실제로 그랬다). */
/* impeccable-disable-next-line side-tab -- 장식이 아니라 우선순위 표시다 */
.board__card.priority-high { border-left: 3px solid var(--status-cautionary); }

/* 기본값(보통)이 아닌 것만 글자로 단다(#336). **색만으로는 안 된다** —
 * 색각 이상인 사람에게 안 보이고, 바로 옆 프로젝트 태그가 이미 hue-*로
 * 색을 쓰고 있어 색이 둘 겹친다. 높음은 위 왼쪽 테두리와 같은 계열로
 * 묶고, 낮음은 글자색을 낮춰 조용히 둔다 — 낮음까지 튀면 우선순위를
 * 낮춘 카드가 더 눈에 띄는 꼴이 된다. */
.board__prio {
  display: inline-block; padding: 1px 7px; border-radius: var(--r-pill);
  font-size: var(--fs-cap); font-weight: 600; vertical-align: middle;
}
.board__prio--high {
  color: var(--status-cautionary);
  background: color-mix(in srgb, var(--status-cautionary) 14%, transparent);
}
.board__prio--low { color: var(--label-alternative); background: var(--fill-normal); }

/* 디자인Q 필터 줄(#355). 칩 하나가 곧 주소라 <a>로 둔다 — 자바스크립트
 * 없이 새로고침·북마크·공유가 된다. 고른 칩은 색만으로 알리지 않고
 * aria-current도 같이 둔다(템플릿). */
.dq-filters {
  display: flex; flex-direction: column; gap: var(--sp-4);
  margin: var(--sp-6) 0 var(--sp-7);
  padding: var(--sp-5) 0;
  border-top: 1px solid var(--line-solid-neutral);
  border-bottom: 1px solid var(--line-solid-neutral);
}
.dq-filter { display: grid; grid-template-columns: 64px minmax(0, 1fr);
  align-items: start; column-gap: var(--sp-3); }
.dq-filter__label { padding-top: var(--sp-2); font-size: var(--fs-sm);
  line-height: var(--lh-sm); color: var(--label-alternative); }
.dq-filter__options { display: flex; flex-wrap: wrap; gap: var(--sp-2); min-width: 0; }
.dq-filter__chip {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 36px; max-width: 100%; padding: 6px var(--sp-4);
  border: 1px solid var(--line-solid-neutral); border-radius: var(--r-pill);
  background: var(--background-normal-normal); color: var(--label-alternative);
  font-size: var(--fs-sm); line-height: var(--lh-sm); font-weight: 600;
  overflow-wrap: anywhere;
}
/* **호버는 고른 것과 달라야 한다(#378 리뷰).** 호버도 고른 것과 같은
   연한 파란 면 + 파랑 글자로 두면, 칩에 마우스를 올리는 것만으로 그 칩이
   골라진 것처럼 보인다 — 한 줄에 둘이 켜진 것처럼 읽힌다. 고른 색은 그대로
   두고 호버만 무채색 쪽으로 뺀다. */
.dq-filter__chip:hover { background: var(--fill-alternative); color: var(--label-normal);
  text-decoration: none; }
.dq-filter__chip.is-on { background: var(--surface-selected); color: var(--primary-strong);
  border-color: var(--surface-selected); }
.dq-filters + .board { margin-top: 0; }
.board__card-title { overflow-wrap: anywhere; }
@media (max-width: 560px) {
  .dq-filter { grid-template-columns: 1fr; row-gap: var(--sp-2); }
  .dq-filter__label { padding-top: 0; font-weight: 600; }
  .dq-filter__chip { min-height: 44px; }
}

/* 카드 상세의 사진 — 링크 글자("사진 1")만 있으면 열어 보기 전엔 뭔지
 * 몰라 매번 눌러야 했다(실사용 지적) — 작게라도 미리보기를 보여준다. */
.dq-photos { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.dq-photo { display: flex; flex-direction: column; align-items: center; gap: var(--sp-1); }
.dq-photo__img {
  display: block; width: 96px; height: 96px; object-fit: cover;
  border-radius: var(--r-sm); border: 1px solid var(--line-solid-neutral);
  background: var(--fill-normal);
}

/* 지식창고 — 옮겨온 문서의 계층 경로(#292). 경로 한 줄 + 그 아래 들여쓰기.
   진짜 중첩 트리를 안 만드는 이유는 app/web/kb.py의 tree()에 적어 뒀다. */
.kb__crumb {
  margin: var(--sp-5) 0 var(--sp-2);
  /* 경로가 길면 줄을 바꾼다 — 모바일에서 오른쪽으로 넘치면 안 된다. */
  overflow-wrap: anywhere;
}
.kb__under {
  /* 왼쪽 선 하나로 "이 경로 아래"를 보인다. 들여쓰기만 하면 좁은 화면에서
     본문이 더 좁아져 제목이 두 줄로 접힌다. */
  border-left: 2px solid var(--line-solid-normal);
  padding-left: var(--sp-3);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

/* 본문 그림 — 옮겨온 문서가 스크린샷을 그대로 갖고 있어 원본 크기로 나오면
   글보다 그림이 커진다(#292). 크기를 묶되 누르면 원본이 열린다
   (app/web/markdown.py의 _drop_outside_images). */
.prose__shot { display: block; margin: 0 0 var(--sp-3); }
.prose img {
  display: block;
  /* 글 읽는 폭을 안 넘게. 좁은 화면에서는 100%가 이긴다. */
  max-width: min(100%, 420px);
  /* 세로로 긴 그림이 화면을 통째로 먹지 않게. */
  max-height: 50vh;
  width: auto;
  height: auto;
  border: 1px solid var(--line-solid-normal);
  border-radius: var(--r-sm);
}

/* 지식창고 문서 아래 하위 문서 목록(#294). */
/* 하위 문서 머리 — 제목과 「만들기」를 한 줄에(#413). 좁은 화면에서는
 * 접어 내린다. */
/* 하위 문서 제목줄 — 제목과 「하위 문서 만들기」를 한 줄에 둔다(#413).
   **여백은 제목이 아니라 이 줄이 갖는다.** 제목에 margin을 두면 flex
   아이템의 margin이 되어 줄 아래 간격이 사라진다. */
.kb__kids-head {
  display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap;
  margin: var(--sp-6) 0 var(--sp-3);
}
.kb__kids-title {
  margin: 0;
  font-size: var(--fs-md);
  font-weight: 700;
}

/* 문서 내비게이션 — 읽는 동안 같은 폴더의 문서를 왼쪽에 둔다(#298).
   Confluence가 그렇게 두고, 없으면 옆 문서로 가려고 폴더까지 올라갔다
   내려와야 한다. */
.kb-nav {
  position: sticky; top: var(--sp-5);
  flex: 0 0 220px; width: 220px;
  display: flex; flex-direction: column; gap: 2px;
  max-height: calc(100vh - var(--sp-8)); overflow-y: auto;
}
.kb-nav__label { margin: 0 0 var(--sp-2); font-size: var(--fs-cap);
  font-weight: 600; color: var(--label-assistive);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kb-nav__item {
  padding: 4px 6px; border-radius: var(--r-sm);
  font-size: var(--fs-sm); line-height: 18px; color: var(--label-alternative);
  text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kb-nav__item:hover { background: var(--fill-normal); color: var(--label-normal); }
/* 지금 보고 있는 문서. 링크가 아니라 표시다. */
.kb-nav__item.is-here { background: var(--fill-normal); color: var(--label-normal); font-weight: 700; }
.kb-nav__item--kid { padding-left: var(--sp-4); }
.kb-nav__item--folder::before { content: "› "; color: var(--label-assistive); }
.kb-nav__toggle { display: none; }

/* **1200px 아래에서는 목차를 접는다.** 3단을 그대로 두면 1024px에서 본문이
   300px까지 눌려(실측) 표·코드가 든 기술 문서를 못 읽는다. 트리가 이 화면의
   목적이니 목차를 먼저 내준다 — 1440px에서는 본문이 676px로 넉넉하다. */
@media (max-width: 1199px) {
  /* **트리가 있는 화면만이다.** 회의록은 트리가 없어 자리를 다투지 않는데,
     그냥 걸면 원래 860px까지 보이던 개요가 861~1199px에서 사라진다. */
  .meet-layout:has(.kb-nav) .meet-outline { display: none; }
}

/* 좁은 화면 — 트리를 접어 두고 눌러서 편다. 목차처럼 아예 숨기면
   폰에서 문서 사이를 못 옮긴다. **접은 <details>를 CSS로 펴는 건 안 된다**
   (브라우저가 속을 content-visibility로 건너뛴다) — 실측 후 이 방식으로 왔다. */
@media (max-width: 767px) {
  /* **세로로 쌓는다.** 가로로 두면 트리 82px·본문 229px로 끼어 양쪽 다
     잘린다(실측). 트리는 본문 위에 통째로 놓는다. */
  /* **본문 폭을 100%로 묶는다.** align-items:flex-start가 세로로 쌓일 때
     자식 폭을 내용 크기로 늘려, 넓은 표가 페이지를 통째로 밀어낸다
     (실측: 375 화면에서 본문이 1845px이 됐다). */
  .meet-layout { flex-direction: column; gap: var(--sp-4); align-items: stretch; }
  .meet-main { width: 100%; }
  .kb-nav { display: none; flex: 0 0 auto; width: auto; position: static; max-height: 40vh; }
  body:has(#kb-nav-toggle:checked) .kb-nav { display: flex; }
  .kb-nav__toggle {
    display: inline-block; margin: 0 0 var(--sp-3);
    font-size: var(--fs-sm); color: var(--primary-normal); cursor: pointer;
  }
  .kb-nav__toggle::before { content: "▸ "; }
  body:has(#kb-nav-toggle:checked) .kb-nav__toggle::before { content: "▾ "; }
}

/* 페이지 상단의 탐색·등록 영역. 본문 속 편집 폼의 밀도는 유지한다. */
.page > .meet-head { margin-bottom: var(--sp-6); flex-wrap: wrap; }
.page > .meet-head > div { min-width: 0; flex: 1 1 260px; }
.page > .meet-head > .meta { flex: 0 0 auto; }
.page > .page__title { margin-bottom: var(--sp-4); }
.page > .page__title + .meta { margin-top: 0; margin-bottom: var(--sp-6); }
.page > .note-edit { margin: var(--sp-6) 0; }
.page > .segmented, .page > .filters { margin-bottom: var(--sp-6); }
.page > .filters { gap: var(--sp-4) var(--sp-6); }
.page > .filters > .segmented { margin-bottom: 0; }
.page > .segmented, .page > .filters > .segmented { gap: var(--sp-2); }
.page > .segmented > .segmented__item,
.page > .filters > .segmented > .segmented__item {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 40px; max-width: 100%; line-height: var(--lh-sm);
  padding: var(--sp-2) var(--sp-4); overflow-wrap: anywhere;
}
@media (max-width: 560px) {
  .page > .segmented > .segmented__item,
  .page > .filters > .segmented > .segmented__item { min-height: 44px; }
  .page > .filters { flex-direction: column; }
}

/* 목록 등록 동작은 필터 다음, 목록 앞에 둔다. */
.page-actions { display: flex; flex-wrap: wrap; gap: var(--sp-3);
  margin: var(--sp-6) 0; }
