/* ==========================================================================
   PMXpress — 화면 배치.

   ★ 반입본이 아니다. 여기 있는 것은 우리 것이며, 규칙은 하나다 —
     **컴포넌트를 새로 만들지 않고 `--pmx-*` 토큰만 읽는다**
     (PMXpress/README.md 정적 자원).

   여러 화면이 나눠 쓰게 되는 순간 그것은 화면의 배치가 아니라 컴포넌트이며,
   여기가 아니라 디자인 시스템 정본에 넣고 다시 복사한다.

   `.pmx-*` 규칙을 덮어쓰지 않는다. 시스템 스타일은 캐스케이드 레이어 안에 있고
   이 파일은 레이어 밖이라 **특정도 싸움 없이 이긴다** — 덮은 줄 모른 채 갱신만
   조용히 끊긴다 (CLAUDE.md 디자인 시스템 규칙 4).
   ========================================================================== */

/* ── 셸 머리 ───────────────────────────────────────────────────────────────
   `[data-slot="topbar"]` 가 `display:contents` 라 아래 항목들이 상단바의 직접
   자식처럼 늘어선다. 배치를 여기서 다시 짜지 않는 이유가 그것이다. */

/* 두 조각을 벌리지 않는다 — 제품명은 한 낱말이고 색만 갈린다. 참조 구현은 여기에
   간격을 두지만 그것은 띄어 쓰던 표기를 전제한 값이라 따르지 않는다. */
.app-wordmark {
	display: inline-flex;
	align-items: baseline;
	font-size: 17px;
	font-weight: 800;
	letter-spacing: -0.02em;
	text-decoration: none;
}

.app-wordmark b { color: var(--pmx-primary); }
.app-wordmark i { color: var(--pmx-wordmark-ink); font-style: normal; }

.app-topbar__sub {
	font-size: var(--pmx-font-xs);
	color: var(--pmx-text-muted);
	border-left: 1px solid var(--pmx-border-subtle);
	padding-left: 14px;
}

.app-topbar__spacer { flex: 1; }

/* 내 프로필로 가는 칩의 **틀**. 칩 자체는 정본의 `.pmx-person` 이고 여기 있는 것은
   참조 구현이 인라인 style 로 갖는 감싸개다 — 테두리 · 활성 표시 · 터치 타깃.

   **좁은 폭에서 이름을 접는 것은 정본이 한다**(6.23.0 의 `data-collapse` · PX-05).
   켜는 것은 뷰 모델이고 재는 것은 정본의 CSS 라, 여기서 폭을 좁히거나 `.pmx-person*` 을
   덮지 않는다. 이름은 눈에서만 걷히고 보조기술에는 남는다. */
.app-topbar__me {
	display: flex;
	align-items: center;
	gap: 8px;
	/* 터치 타깃 하한. 좁은 폭에서 sm 을 쓰지 않는다. */
	min-height: var(--pmx-control-lg);
	padding: 4px 9px 4px 6px;
	border: 1px solid transparent;
	border-radius: var(--pmx-radius-md);
	text-decoration: none;
	flex-shrink: 0;
	min-width: 0;
}

/* 지금 보고 있는 화면이면 채워 선다 — 레일의 aria-current 와 같은 일이다. */
.app-topbar__me[aria-current="page"] {
	background: var(--pmx-primary-subtle);
	border-color: var(--pmx-primary-border);
}

.app-topbar__caret {
	font-size: 13px;
	color: var(--pmx-text-faint);
	margin-left: 1px;
}

@media (max-width: 720px) {
	.app-topbar__sub { display: none; }
	.app-topbar__caret { display: none; }
}

/* ── 로그인 ────────────────────────────────────────────────────────────────
   **여기에 없다.** `.pmx-login*` 이 4.0 에서 참조 구현의 인라인 style 에서 정본
   CSS 로 들어왔다 — 우리가 세웠던 `app-login__*` 사본을 걷었다. 남은 것은 정본이
   갖지 않는 자리 하나뿐이다.

   `__form` 은 격자이고 간격은 그 gap 이 갖는다. 그 안에 폼이 하나 더 서는 것은
   테스트 계정 블록이 자기 폼을 갖기 때문이고(폼 안에 폼을 둘 수 없다), 그 안쪽
   폼의 간격은 정본이 알 자리가 아니다. */

.app-login__fields {
	display: grid;
	/* **`--pmx-space-6` 이어야 한다.** `.pmx-login__lede` 가 이 값을 상수로 박은 음수
	   여백(`calc(var(--pmx-space-6) * -1 + 8px)`)으로 제목에 붙으므로, 다른 값을 주면
	   제목과 설명 사이가 8px 이 아니게 된다 — 작게 주면 겹치고 크게 주면 벌어진다.
	   정본의 `__form` 과 같은 값을 쓰는 것이 그 계산이 성립하는 유일한 자리다. */
	gap: var(--pmx-space-6);
}

/* ── 역할별 테스트 계정 (local · dev) ──────────────────────────────────────
   요건이 아니라 개발 편의다. **그 둘 밖에서는 마크업이 만들어지지 않는다** —
   감추는 것이 아니라 만들지 않는 것이라, 로그인 ID 도 공용 비밀번호도 HTML 에
   실려 나가지 않는다 (PMXpress/README.md). */

.app-testlogin {
	margin-top: 28px;
	padding-top: 18px;
	border-top: 1px solid var(--pmx-border-subtle);
}

.app-testlogin__label {
	font-size: var(--pmx-font-xs);
	color: var(--pmx-text-muted);
	margin-bottom: 10px;
}

.app-testlogin__grid { display: flex; flex-wrap: wrap; gap: 8px; }

/* ── 폼과 목록의 자잘한 자리 ───────────────────────────────────────────── */

/* 값과 안내가 같은 자리에 선다 — 값을 본 뒤에 알려 주면 이미 닫은 사람에게는
   소용이 없다 (ui.md 임시 비밀번호). */
.app-secret {
	font-family: var(--pmx-font-mono);
	font-size: var(--pmx-font-lg);
	font-weight: 700;
	color: var(--pmx-text-strong);
	letter-spacing: 0.06em;
	user-select: all;
}

/* 표 안에서 고정폭으로 읽는 값 — 로그인 ID · 프로젝트코드 · 날짜. */
.app-mono {
	font-family: var(--pmx-font-mono);
	font-size: var(--pmx-font-xs);
	color: var(--pmx-text-body);
}

/* 잠긴 액션은 사라지지 않고 무채색 글자로 남는다 — 왜 못 하는지가 못 하는 것보다
   중요한 자리이며, 액션이 그냥 없으면 시스템관리자는 자기 화면이 고장 났다고
   읽는다 (ui.md SCR-010-04). */
.app-action-blocked {
	font-size: var(--pmx-font-xs);
	color: var(--pmx-text-faint);
}

/* 한 줄에 늘어놓는 행 안의 액션들. */
.app-rowactions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

/* 여러 줄을 한 칸에 쌓는 자리 — 내 프로필의 프로젝트 역할, 비밀번호 정책 오류. */
.app-stack { display: block; }
.app-stack > * { display: block; }

/* 카드 사이의 세로 간격. */
.app-gap { margin-top: var(--pmx-space-6); }

/* 카드 아래 액션 묶음. 로그아웃은 비밀번호 변경과 떨어뜨려 잘못 누르기를 막는다
   (ui.md SCR-010-07). */
.app-logout {
	margin-top: var(--pmx-space-8);
	padding-top: var(--pmx-space-5);
	border-top: 1px solid var(--pmx-border-subtle);
}

.app-note {
	margin: 10px 0 0;
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-muted);
	line-height: 1.7;
}

/* ── 역할 전환 (local · dev) ───────────────────────────────────────────────
   **검수용이며 제품에는 없는 자리다** — 참조 구현도 그렇게 적는다. 프로필 밖에서는
   빈이 서지 않아 마크업이 만들어지지 않는다. 마크업은 조각 하나이고 두 꼴이 나눠 쓴다
   (templates/fragments/shell.html).

   **넓은 폭은 세그먼트, 좁은 폭은 드롭다운이다.** 경계는 `--pmx-bp-rail`(900px)이며
   미디어 질의가 사용자 정의 속성을 읽지 못해 숫자를 적는다.

   ★ 좁은 폭 자리가 계약 밖이다 ★ — 정본은 그 폭에서 검수용 컨트롤을 드로어로 보내지만
     (`.pmx-shell__tools`), 드로어는 264px 이라 여섯을 펼치면 여러 줄이 된다. 드롭다운은
     한 줄짜리 여는 단추 하나만 머리에 두고 목록은 떠서 열리므로 그 폭에 든다.
     **덮는 것이 아니라 쓰지 않는 것이며, 정본에 물어야 하는 이탈이다**(CLAUDE.md
     디자인 시스템 2).

   **참조 구현은 Select 로 접는다. 우리는 접지 않는다** — 무JS 폼 포스트로 도는 자리라
   `change` 로 보내면 무JS 경로가 사라지고 WCAG 3.2.2 On Input 에 걸린다(PX-05 회신). */

.app-rolebar {
	display: flex;
	align-items: center;
	gap: 10px;
	min-width: 0;
}

/* 넓은 폭 — 세그먼트. 참조 구현의 1px 구분 막대를 테두리로 낸다: 별도 요소로 두면
   역할 전환이 서지 않는 프로필에서 막대만 덩그러니 남는다. */
.app-rolebar[data-place="topbar"] {
	padding-right: 12px;
	border-right: 1px solid var(--pmx-border-subtle);
}

/* 라벨은 여섯이 라벨과 함께 한 줄에 서는 폭에서만. 정본은 `--pmx-bp-tablet` 이다. */
@media (max-width: 1080px) {
	.app-rolebar__label { display: none; }
}

/* 좁은 폭 — 드롭다운. 넓은 폭에서는 세그먼트가 그 일을 하므로 서지 않는다. */
.app-rolebar[data-place="menu"] { display: none; }

@media (max-width: 900px) {
	.app-rolebar[data-place="menu"] { display: flex; }
}

/* 줄어들 수 있어야 한다 — 안에 든 역할 이름이 길면(「시스템관리자」) 320px 머리가
   가로로 넘친다. 줄이는 것은 이름이고 삼각형은 남는다. */
.app-rolemenu {
	position: relative;
	min-width: 0;
}

/* 닫힌 채로 지금 역할을 진다. 좁은 폭에서 이름 칩이 아바타만 남으므로 «지금 누구인가»를
   여기서도 말한다 — 열어야 알 수 있으면 그것을 묻는 데 한 걸음이 든다. */
.app-rolemenu__now {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-weight: var(--pmx-fw-semibold);
}

/* **360px 은 우리가 잰 값이고 정본의 토큰이 아니다.** 이 경계는 컴포넌트의 접힘이
   아니라 **글자 수에서 나온다** — 그래서 토큰을 빌려 쓰지 않는다.

   머리에서 이 단추가 쓸 수 있는 폭은 「내용 폭 − 268」이다(버거 40 · 워드마크 81 ·
   알림 38 · 아바타 45 · 좌우 여백 24 · 사이 간격 40). 이름이 넉 자는 서야 무엇인지
   읽히므로 48px 이 들고, 좌우 여백과 삼각형까지 76px 이다 — 내용 폭 344, 곧 화면
   360px 이 하한이다. 320px 에서는 37px 만 남아 이름이 통째로 말줄임된다(실측). */
.app-rolemenu__short { display: none; }

@media (max-width: 359px) {
	.app-rolemenu__now { display: none; }
	.app-rolemenu__short { display: inline; }
}

.app-rolemenu__btn {
	display: inline-flex;
	align-items: center;
	gap: 2px;
	/* 터치 타깃 하한. 좁은 폭에서만 서는 컨트롤이다. */
	min-height: var(--pmx-control-lg);
	padding: 0 8px;
	border: 1px solid var(--pmx-border-control);
	border-radius: var(--pmx-radius-md);
	background: var(--pmx-surface-card);
	color: var(--pmx-text-body);
	font-size: var(--pmx-font-xs);
	white-space: nowrap;
	cursor: pointer;
	/* 브라우저의 삼각형 표식을 걷는다 — 방향은 오른쪽의 ▾ 가 말한다. */
	list-style: none;
	max-width: 100%;
	min-width: 0;
}

.app-rolemenu__btn::-webkit-details-marker { display: none; }

.app-rolemenu__btn:focus-visible {
	box-shadow: var(--pmx-focus-ring);
	outline: none;
}

.app-rolemenu[open] .app-rolemenu__btn {
	border-color: var(--pmx-primary-border);
	background: var(--pmx-primary-subtle);
}

/* 흐름 밖에 뜬다 — 흐름 안에서 열면 상단바가 두 줄이 된다. 값은 알림 팝오버
   (`.app-bell__panel`)와 같은 갈래다: 한 제품에 뜨는 패널이 둘인데 모서리와 그림자가
   갈리면 다른 층으로 읽힌다. */
.app-rolemenu__panel {
	position: absolute;
	top: calc(100% + 6px);
	right: 0;
	z-index: var(--pmx-z-dropdown);
	width: min(84vw, 200px);
	display: flex;
	flex-direction: column;
	background: var(--pmx-surface-card);
	border: 1px solid var(--pmx-border-subtle);
	border-radius: var(--pmx-radius-lg);
	box-shadow: var(--pmx-shadow-lg);
	/* 줄의 모서리를 패널이 자른다 — 줄마다 반지름을 주지 않는다. */
	overflow: hidden;
}

/* 꼴을 가르는 것이 클래스가 아니라 `data-kind` 인 이유는 조각 주석에 있다 —
   Thymeleaf 의 `__…__` 전처리다. */
.app-roleitems[data-kind="menu"] {
	display: flex;
	flex-direction: column;
}

/* 드롭다운의 한 줄. **단추 꼴이 아니라 목록 줄이다** — 여섯이 세로로 서므로 알약을
   쌓으면 여백이 줄보다 커진다. 레일 항목과 같은 갈래이며, 그것은 어두운 바탕의
   것이라 값을 빌리지 않는다. */
.app-roleitem {
	display: flex;
	align-items: center;
	min-height: var(--pmx-control-lg);
	padding: 0 14px;
	border: 0;
	border-left: 3px solid transparent;
	background: transparent;
	color: var(--pmx-text-body);
	font-family: var(--pmx-font-sans);
	font-size: var(--pmx-font-sm);
	text-align: left;
	cursor: pointer;
}

.app-roleitem:hover { background: var(--pmx-surface-sunken); }

.app-roleitem:focus-visible {
	box-shadow: var(--pmx-focus-ring);
	outline: none;
}

/* 지금 그 역할이면 채워 선다 — 레일의 활성 항목과 같은 표시다. */
.app-roleitem[aria-pressed="true"] {
	border-left-color: var(--pmx-primary);
	background: var(--pmx-primary-subtle);
	color: var(--pmx-primary);
	font-weight: var(--pmx-fw-semibold);
}

.app-rolebar__label {
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-muted);
	white-space: nowrap;
}

/* ── 020 프로젝트 ──────────────────────────────────────────────────────────
   여기 있는 것은 **우리 클래스뿐**이다. `.pmx-*` 규칙을 프로젝트 CSS 로 덮지 않는다
   (CLAUDE.md 디자인 시스템 4) — 시스템 스타일은 캐스케이드 레이어 안에 있고 이 파일은
   레이어 밖이라 **특정도 싸움 없이 이긴다**. 덮은 줄 모른 채 갱신만 조용히 끊긴다. */

/* 보기 하나를 폭으로 가른다. 카드 보기는 좁은 화면에서 표와 같은 그림이 되므로
   그때는 표가 대신 선다 (REQ-020-089). 감추는 것은 이 래퍼이며 안의 시스템
   컴포넌트에는 손대지 않는다. */
.app-view[data-when="wide"] { display: none; }
.app-view[data-when="narrow"] { display: block; }
.app-onlywide { display: none; }

@media (min-width: 900px) {
	.app-view[data-when="wide"] { display: block; }
	.app-view[data-when="narrow"] { display: none; }
	.app-onlywide { display: inline-flex; }
}

/* 카드 보기의 격자. 한 줄에 셋, 좁아지면 하나 (ui.md). 시스템에는 카드를 늘어놓는
   컨테이너가 없고 카드 자체(`.pmx-projectcard`)만 있어 배치는 소비처의 것이다. */
.app-cardgrid {
	display: grid;
	gap: var(--pmx-space-5);
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
}

/* ── 개요 배치 ─────────────────────────────────────────────────────────────
   카드 자체는 정본의 `.pmx-card` 이고, 여기 있는 것은 **카드를 어디에 놓는가**다. */

/* 담당자 지정·사양 | 갭분석. 좁아지면 한 열로 쌓인다 — 중단점은 참조 구현이 모바일을
   가르는 자리(--pmx-bp-mobile 720)이며, @media 가 토큰을 읽지 못해 숫자를 적는다. */
.app-overview-cols {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--pmx-space-5);
	align-items: start;
}

.app-overview-col { display: grid; gap: var(--pmx-space-5); align-items: start; }

@media (min-width: 721px) {
	.app-overview-cols { grid-template-columns: 1fr 1fr; }
}

/* 계획 대비 실적 — **세 줄이 같은 가로 축을 쓴다.** 이름 칸을 고정 폭으로 잡아야 세
   막대의 왼쪽 끝이 한 선에 서고, 그래야 「기간은 여기까지인데 일은 여기까지」가 눈으로
   읽힌다. 이름 폭이 제각각이면 막대가 어긋나 비교가 사라진다.

   막대 자체는 정본의 `.pmx-progress` 이고 여기 있는 것은 **줄을 세우는 자리**뿐이다. */
.app-variance { display: grid; gap: var(--pmx-space-3); }

.app-variance__row {
	display: grid;
	grid-template-columns: 72px 1fr;
	align-items: center;
	gap: var(--pmx-space-4);
}

.app-variance__k {
	font-size: var(--pmx-font-xs);
	color: var(--pmx-text-muted);
}

/* 사업 정보 격자의 **행 간격**. 정본의 `.pmx-formsection__grid` 는 `gap: 0 <가로>` 라
   행 간격이 0 이다 — 폼 필드는 자기 여백을 갖지만 `.pmx-def` 는 갖지 않아 라벨과 위
   칸의 값이 붙는다. 정본 규칙을 덮지 않고 우리 클래스를 함께 붙여 행만 벌린다. */
.app-bizgrid { row-gap: var(--pmx-space-6); }

/* **좁은 폭에서는 한 열로 잠근다.** 정본은 `data-cols` 를 컨테이너 질의로 접는데, 좁은
   화면에서 그 접힘이 걸리지 않아 열이 70px 까지 눌린다(실측). 참조 구현은 이 자리를
   질의에 맡기지 않고 `columns` 를 1 로 **직접 넘긴다** — 서버 렌더에는 그 길이 없어
   미디어 질의로 잠근다. 우리 클래스이므로 `.pmx-*` 를 덮는 것이 아니다. */
@media (max-width: 720px) {
	.app-bizgrid { grid-template-columns: 1fr; }
}

/* ── 회고 ──────────────────────────────────────────────────────────────────
   세 갈래가 나란히 선다. 갈래마다 머리 색이 다른 것은 장식이 아니라 **읽는 순서**다 —
   좋았던 것(정상) → 아쉬운 것(주의) → 다음에(우리가 할 일). */

.app-retro {
	display: grid;
	gap: var(--pmx-space-5);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
}

.app-retro__card {
	display: grid;
	gap: var(--pmx-space-4);
	align-content: start;
	padding: 12px 14px 14px;
	background: var(--pmx-surface-card);
	border: 1px solid var(--pmx-border-subtle);
	border-top: 3px solid var(--_kind);
	border-radius: var(--pmx-radius-md);
}

.app-retro__card[data-kind="keep"]    { --_kind: var(--pmx-domain-health-ontrack); }
.app-retro__card[data-kind="problem"] { --_kind: var(--pmx-domain-health-watch); }
.app-retro__card[data-kind="next"]    { --_kind: var(--pmx-primary); }

.app-retro__text {
	margin: 0;
	font-size: var(--pmx-font-sm);
	color: var(--pmx-text-body);
	line-height: 1.75;
	text-wrap: pretty;
}

.app-retro__text--empty { color: var(--pmx-text-muted); }

/* 채움 안내. 완료로 넘기려면 셋 중 하나는 있어야 한다 — 셋 다가 아니다. */
.app-retro-hint { margin-bottom: var(--pmx-space-5); }
.app-retro-hint span { display: block; margin-top: 3px; font-size: var(--pmx-font-xs); }

/* ── 이해관계자 명부 ───────────────────────────────────────────────────────
   소속 하나가 `<tbody>` 하나다. 정본의 `.pmx-table` 이 표의 뼈대를 갖고, 여기 있는
   것은 **명부에만 있는 것** 셋 — 걸쳐 선 소속 칸 · 묶음 경계선 · 이름 아래 연락처.
   `.pmx-table` 규칙을 덮지 않는다. */

/* 세는 줄. 「착수 시 지정」이 이 화면을 여는 이유라 그것만 색이 갈린다. */
.app-orgcount {
	margin: 0 0 10px;
	font-size: var(--pmx-font-xs);
	color: var(--pmx-text-muted);
}

.app-orgcount b { color: var(--pmx-domain-health-watch); }

/* 걸쳐 선 소속 칸. `.pmx-table th` 가 무채색 작은 글씨라 이름만 다시 세운다. */
.app-orgcell {
	background: var(--pmx-surface-raised);
	text-align: center;
	vertical-align: middle;
	white-space: normal;
	border-bottom: 1px solid var(--pmx-border-subtle);
}

.app-orgcell__name {
	display: block;
	font-size: var(--pmx-font-sm);
	font-weight: var(--pmx-fw-bold);
	color: var(--pmx-text-strong);
}

.app-orgcell__kind {
	display: block;
	margin-top: 2px;
	font-size: var(--pmx-font-2xs);
	font-weight: var(--pmx-fw-medium);
	color: var(--pmx-text-muted);
}

/* 묶음 경계. 줄 사이의 옅은 선과 갈려야 「여기서 소속이 바뀐다」가 읽힌다. */
.app-orgtable tbody + tbody > tr:first-child > * {
	border-top: 1px solid var(--pmx-border-default);
}

.app-orgname { font-weight: var(--pmx-fw-semibold); color: var(--pmx-text-strong); }

/* 사람이 정해지지 않은 자리. 세는 줄과 같은 색이라 표와 요약이 같은 것을 가리킨다. */
.app-orgvacant {
	font-style: italic;
	font-weight: var(--pmx-fw-semibold);
	color: var(--pmx-domain-health-watch);
}

/* 이름 아래 한 줄. 넘치면 자르고 전체는 `title` 이 갖는다. */
.app-orgcontact {
	display: block;
	margin-top: 2px;
	font-family: var(--pmx-font-mono);
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-muted);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.app-orgnote { font-size: var(--pmx-font-xs); color: var(--pmx-text-muted); }

/* 편집 화면은 **폭을 제한하지 않는다.**

   정본의 `.pmx-formscreen__body` 는 720px 이고 그것이 기본값이지만, 이 제품의 편집
   화면은 **넓은 화면을 쓰는 쪽**을 고른다.

   표를 담는 화면(갭분석·사양·링크·마일스톤·이해관계자)은 **열 수가 값에 따라 늘어나므로**
   제한을 두면 화면 절반을 비워 두고 표만 가로로 스크롤한다 — 갭분석은 항목이 늘고
   이해관계자는 아홉 칸이다.

   격자로 읽는 폼(등록·수정·사용자·비밀번호)도 함께 푼다. **글줄이 길어지는 것은
   `.pmx-formsection__grid` 의 열이 받는다** — 폭이 넓어지면 컨테이너 질의가 열을 늘려
   한 칸의 폭은 그대로다.

   정본이 `FormScreen` 에 `maxWidth` 를 두어 이 값을 자리마다 갈라 주므로 계약 안의
   길이다. 인라인 대신 클래스인 것은 **자리가 여덟이라 한곳에 적어야 같이 움직이기**
   때문이다. */
.app-formwide { max-width: none; }

/* ── 이해관계자 편집 ───────────────────────────────────────────────────────
   열 폭은 `<colgroup>` 이 갖는다. 여기 있는 것은 계정에 이어진 이메일 칸과 순서 단추
   둘뿐이다 — 나머지는 정본의 `.pmx-table` · `.pmx-field` · `.pmx-select` 가 갖는다. */

/* 표에 폭을 적었으므로 지켜져야 한다. 고정 배치가 아니면 긴 값이 열을 밀어
   **셀렉트가 다시 눌린다** — 그것이 이 화면의 원래 증상이었다. */
/* **1240px 이 아래로 내려가지 않는다.** 입력 아홉이 각자 `.pmx-control` 의 좌우
   패딩을 먹으므로, 그보다 좁히면 부서·성명·휴대폰의 값이 칸에서 잘린다. 참조 구현이
   1320px 을 잡은 것도 같은 이유이며 그쪽은 그 위에 720px 카드 제한까지 갖는다. */
.app-orgedit { table-layout: fixed; min-width: 1240px; }

/* 이메일 칸. **두 모습이 함께 렌더되고 감싸개의 `data-linked` 가 하나를 고른다.**
   `hidden` 속성을 쓰지 않는 이유는 정본에 전역 `[hidden]{display:none}` 이 없어서다 —
   `.pmx-field` 의 `display:flex` 가 UA 규칙을 이겨 둘 다 보인다. 감추는 것은 우리
   요소이므로 `.pmx-*` 를 덮는 것이 아니다. */
.app-orgmail { display: block; min-width: 0; }

.app-orgmail:not([data-linked]) > .app-orgmail__linked { display: none; }
.app-orgmail[data-linked] > .app-orgmail__field { display: none; }

.app-orgmail__linked { display: block; min-width: 0; }

/* 계정에서 따라온 값이다 — 편집란이 아니라 사실의 표시라 테두리를 두지 않는다. */
.app-orgmail__addr {
	display: block;
	font-family: var(--pmx-font-mono);
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-muted);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.app-orgmail__cut {
	display: block;
	margin: 0;
	padding: 0;
	border: none;
	background: none;
	cursor: pointer;
	font-family: var(--pmx-font-sans);
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-link);
	text-align: left;
}

.app-orgmove { display: inline-flex; gap: 2px; }

/* 마일스톤 점 행 — **예정일순 등간격**이다 (REQ-020-081). 오늘 선을 두지 않는다:
   등간격이라 오늘이 놓일 위치가 없고, 대신 각 점이 `D-n`·`지연 n일`·`완료` 로
   오늘과의 거리를 말한다. 색은 셋이다 — 달성 · 지연 · 예정. */
.app-milestones {
	display: grid;
	gap: var(--pmx-space-4);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 160px), 1fr));
}

.app-milestone {
	display: grid;
	gap: 4px;
	justify-items: center;
	text-align: center;
	padding-top: 14px;
	border-top: 1px solid var(--pmx-border-subtle);
}

.app-milestone__dot {
	width: 11px;
	height: 11px;
	border-radius: 50%;
	border: 2px solid var(--pmx-border-default);
	background: var(--pmx-surface-card);
}

.app-milestone[data-state="done"] .app-milestone__dot {
	background: var(--pmx-domain-health-ontrack);
	border-color: var(--pmx-domain-health-ontrack);
}

.app-milestone[data-state="late"] .app-milestone__dot {
	background: var(--pmx-domain-health-risk);
	border-color: var(--pmx-domain-health-risk);
}

.app-milestone__name {
	font-size: var(--pmx-font-sm);
	font-weight: var(--pmx-fw-semibold);
	color: var(--pmx-text-strong);
}

.app-milestone__date {
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-muted);
}

.app-milestone__state {
	font-size: var(--pmx-font-xs);
	font-weight: var(--pmx-fw-medium);
	color: var(--pmx-text-muted);
}

.app-milestone[data-state="late"] .app-milestone__state { color: var(--pmx-domain-health-risk); }
.app-milestone[data-state="done"] .app-milestone__state { color: var(--pmx-domain-health-ontrack); }

/* ── 060 특일 ──────────────────────────────────────────────────────────────
   카드 둘과 줄 두 개뿐이라 새로 그릴 컴포넌트가 없다. 여기 있는 것은 **어디에
   놓는가**이며, `.pmx-*` 규칙을 덮지 않는다. */

/* 특일 목록 | 적재 기록. 좁아지면 쌓인다 — 중단점은 참조 구현이 잡은 380px 이고,
   auto-fit 이라 미디어 질의 없이 접힌다. */
.app-holiday-cols {
	display: grid;
	gap: var(--pmx-space-5);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 380px), 1fr));
	align-items: start;
}

/* 한 줄에 늘어놓는 입력과 단추. `.app-rowactions` 와 갈리는 것은 세로 정렬 하나다 —
   라벨이 입력 위에 서므로 가운데가 아니라 **아래**로 맞춰야 단추가 입력 줄에 선다.

   **스크립트가 없어 단추가 하나씩 붙는다.** 셀렉트와 체크박스가 바뀌는 즉시 다시
   부르는 길이 없으므로 «고르고 누르기» 두 걸음이며, 그 단추가 이 배치의 마지막
   칸이다. */
.app-holiday-inline {
	display: flex;
	gap: var(--pmx-space-4);
	align-items: flex-end;
	flex-wrap: wrap;
}

/* ── 030 WBS ───────────────────────────────────────────────────────────────
   간트의 격자·눈금·막대는 **정본의 것**이다(`.pmx-gantt*` · `.pmx-timeline*`).
   여기 있는 것은 그 격자에 우리가 얹는 자리뿐이다 — 이름 칸의 배치, 마일스톤 이름
   층, 편집 표의 칸 폭. **`.pmx-*` 규칙을 덮지 않는다.** */

/* 막대 끝의 종료일에는 **우리 규칙을 두지 않는다** — 가로 자리·글꼴·색은 물론
   `split` 두 띠 중 어느 줄에 앉는지도 정본이 갖는다(`.pmx-gantt__note` · `--actual`, 6.31.0).
   그 위에 세로 밀기를 얹으면 두 벌이 되어 계획 날짜가 레인 위로 올라가 잘리고 실적 날짜는
   자기 띠 아래로 처진다(좁은 상자에서 축이 밀리는 간트에서 실측). */

/* 간트 이름 칸 — 번호 · 이름 · 마일스톤 · 담당 · 진척이 한 줄에 선다.
   **번호가 계층의 정본이다**(ui.md) — 들여쓰기는 눈에만 보인다. */
.app-wbslabel {
	display: flex;
	align-items: center;
	gap: 7px;
	min-width: 0;
	width: 100%;
}

/* 접힌 채 축이 길어지면 **이름이 함께 밀려 나간다** — 그 배치에서 이름 줄은 격자의 «한
   행»이라 밀면 글자가 화면 밖으로 나가고 **이름 없는 막대**가 남는다. 넓은 폭에서는
   정본의 `.pmx-gantt__label` 이 `sticky` 로 막지만 접힌 모드는 그것을 `static` 으로
   끄므로(한 열이 되어 막을 것이 없다) 축이 길어지는 순간 자리가 빈다.

   **고정하는 것은 정본의 상자가 아니라 그 안의 우리 span 이다** — 정본 규칙을 덮으면
   다음 반입이 조용히 끊긴다(루트 CLAUDE.md 의 디자인 시스템 규칙 4). 정본도 이 자리를
   「그 줄의 내용 고정은 소비처 몫」으로 적어 두었다.

   `width:max-content` 여야 붙을 자리가 생긴다 — 100% 면 격자 폭이라 밀 여지가 없다.
   그때 `.app-wbsprog` 의 `margin-left:auto` 가 밀 자리를 잃고 이름 뒤에 서는데, 좁은
   폭에서는 그 편이 맞다. `100cqi` 가 긴 이름이 상자를 넘는 것을 막는다. */
@container (max-width: 720px) {
	.pmx-gantt[data-stack-below="720"] .app-wbslabel {
		position: sticky;
		left: 0;
		width: max-content;
		max-width: 100cqi;
	}
}

.app-wbsnum {
	color: var(--pmx-text-muted);
	flex-shrink: 0;
}

.app-wbsname {
	font-size: var(--pmx-font-sm);
	color: var(--pmx-text-strong);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.pmx-gantt__label--summary .app-wbsname { font-weight: var(--pmx-fw-semibold); }

.app-wbsprog {
	margin-left: auto;
	flex-shrink: 0;
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-muted);
}

/* 단계 접기 — 라벨이 버튼이라는 것은 보아서 알 수 없으므로 화면이 한 줄로 적는다.
   **스크립트가 세우는 자리**이며, 없으면 라벨이 그대로 글자로 선다. */
.app-wbstoggle {
	display: flex;
	align-items: center;
	gap: 6px;
	width: 100%;
	min-width: 0;
	border: none;
	background: transparent;
	padding: 0;
	cursor: pointer;
	text-align: left;
	font-family: var(--pmx-font-sans);
	color: inherit;
}

.app-wbscaret {
	flex-shrink: 0;
	width: 10px;
	font-size: 10px;
	color: var(--pmx-text-muted);
	transition: transform 120ms;
}

.app-wbscaret[data-collapsed="true"] { transform: rotate(-90deg); }

/* 접힌 행. **`hidden` 속성을 쓰지 않는다** — 정본에 전역 `[hidden]{display:none}` 이
   없고 `.pmx-gantt__label` 이 `display:flex` 라 UA 기본값을 이긴다. */
.app-wbshidden { display: none; }

/* 편집 표 — 입력칸 다섯이 서므로 좁아지면 가로로 민다. **페이지가 아니라 표 안이
   밀린다**(`.pmx-table-scroll` 이 그 자리다). */
.app-wbstable { min-width: 1080px; }

.app-wbscell {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	min-width: 0;
}

/* 폭은 **감싸개**가 갖는다 — `.pmx-input` 은 `flex:1` 이라 제 폭을 지키지 못한다. */
.app-wbsnamefield { min-width: 180px; }

/* 담당은 열을 갖지 않고 **이름 아래 한 줄**이다(ui.md) — 열을 하나 더 두면 좁은
   화면에서 계획·실적 기간이 먼저 뭉개진다. */
.app-wbsowner {
	display: block;
	margin-top: 4px;
}

/* 폭은 **감싸개**가 갖는다 — 셀렉트에 주면 감싸개가 칸을 다 차지해 화살표(`::after`)가
   고르개가 아니라 칸의 오른쪽 끝에 선다. */
.app-wbsowner .pmx-select-wrap { max-width: 220px; }

.app-wbsdates {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	color: var(--pmx-text-muted);
}

.app-wbsdate { width: 150px; flex: none; }

.app-wbsprogress { min-width: 120px; }

.app-wbsprogressfield {
	width: 96px;
	margin-top: 4px;
}

/* 숫자는 오른쪽으로 — 자릿수가 달라도 일의 자리가 한 줄에 선다. */
.app-wbsprogressfield .pmx-input { text-align: right; }

/* 액션 여섯이 한 줄에 선다 — **누를 수 없을 때도 자리를 지킨다**(ui.md). */
.app-wbsactions { flex-wrap: nowrap; gap: 2px; }

.app-wbsfoot {
	display: flex;
	align-items: center;
	gap: var(--pmx-space-4);
	flex-wrap: wrap;
	margin-top: var(--pmx-space-4);
}

/* ── 040 인력·공수 ─────────────────────────────────────────────────────────
   정본의 `.pmx-table` 이 표의 뼈대를, `.pmx-grade` · `.pmx-skill-chip` ·
   `.pmx-person` 이 칩을 갖는다. 여기 있는 것은 **격자에만 있는 것** 넷 —
   잠긴 칸 · 좁은 숫자 입력칸 · 이름 칸의 세로 쌓기 · 이력 링크다.
   `.pmx-*` 규칙을 덮지 않는다. */

/* 투입 구간 표. 날짜 칸이 여섯 열 중 둘이라 폭을 지정하지 않으면 이름과 스킬이
   그 자리를 먹는다. */
/* 날짜 칸의 폭. **테는 `.pmx-control` 이 지므로** 폭도 그 상자에 준다 —
   `.pmx-input` 은 `border:none` 이고 `flex:1` 이라 상자 안을 채운다(정본). */
.app-asgdate { width: 150px; }

/* **쓰기 액션은 가로로 밀어도 남는다.** 표가 좁은 폭에서 밀리는데 이 칸이 오른쪽 끝이라
   가장 먼저 잘린다 — 「제거」가 보이지 않아 **없는 기능으로 읽힌 자리다**(실측 1012px).
   정본이 간트 이름 칸을 세우는 것과 같은 수단이며(`.pmx-gantt__label`), 배경을 물려받아
   머리줄과 줄무늬를 그대로 진다: 정본이 `thead tr` 과 `tbody tr` 에 배경을 주므로
   `inherit` 하나로 둘 다 맞는다. */
.app-asgtable th:last-child,
.app-asgtable td:last-child {
	position: sticky;
	right: 0;
	background: inherit;
	box-shadow: -1px 0 0 var(--pmx-border-subtle);
}

/* 동명이인일 때만 서는 로그인 ID — 이름 아래 한 줄이다(REQ-040-042). */
.app-asgid {
	display: block;
	margin-top: 2px;
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-muted);
}

/* 인력 추가 줄. 라벨이 입력 위에 서므로 아래로 맞춰야 단추가 입력 줄에 선다 —
   060 의 사용자정의 휴무일 줄과 같은 자리다. */
.app-asgadd {
	display: flex;
	align-items: flex-end;
	gap: var(--pmx-space-4);
	flex-wrap: wrap;
	margin-top: var(--pmx-space-5);
}

/* 폼의 이름. **줄 전체를 차지해 입력 위에 선다** — 이 줄은 아래끝으로 맞추므로
   그냥 끼우면 입력과 같은 줄에 눕는다.
   **조판은 정본의 구획 머리와 같은 언어다**(`.pmx-section__head` · `.pmx-formsection__head`)
   — 3px 액센트 바에 굵은 글씨다. 필드 라벨과 같은 조판이면 **제목이 아니라 아래 칸의
   라벨로 읽힌다**(실측: 둘 다 13px · 굵기 500 · text-body 였다).
   **그 클래스를 그대로 입지 못한다** — 좌우 20px 패딩과 아래 테두리를 갖고 있어 카드 본문
   안에서 패딩이 이중으로 들어가고, 고쳐 쓰는 것은 «프로젝트 CSS 로 `.pmx-*` 를 덮지
   않는다» 에 걸린다. 그래서 **토큰만 참조해** 같은 언어를 세운다. */
.app-asgadd__name {
	flex: 0 0 100%;
	display: flex;
	align-items: center;
	gap: var(--pmx-space-4);
	margin-bottom: calc(var(--pmx-space-2) - var(--pmx-space-4));
	font-size: var(--pmx-font-sm);
	font-weight: var(--pmx-fw-bold);
	color: var(--pmx-text-strong);
}

/* 액센트 바. **판정을 싣지 않는 강조**라 시그니처 티일이 맞는 자리다(정본 「Signature
   rules」) — 상태 · 위험에는 쓰지 않는다. 마크업이 아니라 가상 요소인 것은 읽을 것이
   없기 때문이다. */
.app-asgadd__name::before {
	content: "";
	flex-shrink: 0;
	width: 3px;
	height: 13px;
	border-radius: 2px;
	background: var(--pmx-accent-strong);
}

/* 강제 색상 모드에서는 배경이 걷히므로 바가 사라진다 — 정본이 자기 바에 거는 것과 같은
   처리다(`.pmx-section__bar` · `.pmx-formsection__bar`). */
@media (forced-colors: active) {
	.app-asgadd__name::before { forced-color-adjust: none; }
}


/* **줄 안의 필드는 높이가 같아야 아래끝 맞춤이 선다.** 한 칸에만 힌트가 붙으면 그 칸의
   입력칸만 힌트 높이만큼 떠올라 날짜 칸과 어긋난다 — 정본의 필터 줄
   (`.pmx-listscreen__filters`)이 힌트를 두지 않는 것도 같은 까닭이다.
   그래서 **할 말은 줄 아래 한 자리에 모으고**, 두 문장을 나란히 세운다. */
.app-asgnote {
	display: flex;
	flex-wrap: wrap;
	gap: var(--pmx-space-2) var(--pmx-space-5);
	margin-top: var(--pmx-space-3);
}

/* 월별 공수 격자. 열이 달마다 하나씩 늘어 **표 안이 밀린다** — 페이지가 아니라
   `.pmx-table-scroll` 이 그것을 받는다(정본). */
.app-mmgrid { border-collapse: collapse; }

.app-mmgrid th[data-current] {
	color: var(--pmx-primary);
	border-bottom: 2px solid var(--pmx-accent);
}

/* 이름 칸은 두 줄을 걸쳐 선다(`rowspan`). 이름 · 직급 · 로그인 ID 가 세로로 쌓인다.
   **정렬은 적지 않는다** — 숫자 칸의 오른쪽 맞춤은 정본의 `data-align="right"` 가 하며
   그것이 `tabular-nums` 까지 함께 준다. 클래스로 적으면 `.pmx-table th` 보다 약해
   머리글만 왼쪽에 남는다(실측). */
.app-mmname {
	vertical-align: top;
	white-space: nowrap;
}

.app-mmsub {
	display: block;
	font-size: var(--pmx-font-2xs);
	font-weight: var(--pmx-fw-regular);
	color: var(--pmx-text-muted);
}

/* **잠긴 칸은 감추지 않고 잠근다**(REQ-040-017) — 빈칸이면 「안 넣었다」와
   「넣을 수 없다」가 구분되지 않는다. 칠은 훑기 위한 것이고 뜻은 `title` 이 갖는다. */
.app-mmcell[data-locked] { background: var(--pmx-surface-sunken); }

/* 앞으로 오는 달(참조 구현) — **실적이 아직 있을 수 없는 달과 넣지 않은 달이 화면에서
   같아 보이면 안 된다.** 막지는 않는다: 무엇을 언제 넣는지를 요건이 정하지 않았고
   REQ-040-018 은 「언제든 넣고 고칠 수 있다」로 적는다. */
.app-mmgrid th[data-future],
.app-mmcell[data-future] { background: var(--pmx-surface-inset); }

.app-mmgrid th[data-future] { color: var(--pmx-text-faint); }

/* 격자의 칸은 **상자가 아니라 밑줄**이다(참조 구현 `.mmg-in`) — 칸이 백 개가 넘어
   테를 두르면 표가 격자무늬가 된다. **밑줄을 여기서 그린다**: 정본의 `.pmx-input` 은
   `border:none` 이고 테는 `.pmx-control` 이 지는데, 칸마다 그 상자를 두를 수 없다. */
.app-mminput {
	width: 64px;
	padding-right: 6px;
	padding-left: 6px;
	border-bottom: 1px solid var(--pmx-border-subtle);
	border-radius: 0;
	font-family: var(--pmx-font-mono);
	text-align: right;
}

.app-mminput[readonly] {
	border-color: transparent;
	background: transparent;
	color: var(--pmx-text-faint);
}

/* 이력을 여는 자리. 칸마다 서므로 글자를 키우지 않는다 — 값이 먼저 읽혀야 한다. */
.app-mmhist {
	display: block;
	margin-top: 2px;
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-muted);
}

.app-mmsum {
	font-weight: var(--pmx-fw-semibold);
	white-space: nowrap;
}

.app-mmreason {
	max-width: 520px;
	margin-top: var(--pmx-space-4);
}

/* ── 040 인력 목록·상세 ────────────────────────────────────────────────── */

/* 이름 아래 한 줄 — **동명이인이 함께 나왔을 때의 로그인 ID 뿐**이다(REQ-040-042).
   직급과 고용 형태는 칩의 보조 줄이 갖는다(참조 구현) — 한 사람의 이름표를 두 줄로
   가르지 않는다.

   들여쓰기는 **아바타 폭 + 칩의 간격**이다(`.pmx-person__avatar` 28px + `gap` 8px) —
   이름 글자와 같은 자리에서 시작해야 한 사람의 것으로 읽힌다. */
.app-personsub {
	display: block;
	margin-top: 2px;
	margin-left: 36px;
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-muted);
}

/* 지금 투입된 프로젝트 — **갯수 배지가 목록 앞에 선다**(ui.md). 없으면 배지도 없다. */
.app-prjlist {
	display: flex;
	align-items: baseline;
	gap: 6px;
	flex-wrap: wrap;
}

.app-prjlist a { display: inline-block; }

/* 인력 상세의 카드 배치. 좁아지면 쌓인다 — 060 의 두 칸과 같은 수단이다. */
.app-person-cols {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(380px, 1fr));
	gap: var(--pmx-space-5);
	align-items: start;
}

/* 스킬 격자 — 분류 하나가 묶음 하나다. 편집도 조회도 같은 배치를 쓴다. */
.app-skillgroup { margin-bottom: var(--pmx-space-5); }

.app-skillgroup__name {
	display: block;
	margin-bottom: var(--pmx-space-3);
	font-size: var(--pmx-font-xs);
	font-weight: var(--pmx-fw-semibold);
	color: var(--pmx-text-muted);
}

.app-skillrows {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
	gap: var(--pmx-space-3) var(--pmx-space-5);
}

.app-skillrow {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--pmx-space-3);
	min-width: 0;
}

.app-skillrow__name {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.app-skillrow .pmx-select-wrap { flex: 0 0 auto; width: 132px; }


/* 자격증·교육 편집의 한 줄. 이름과 날짜와 지우개가 나란히 선다. */
.app-credrow {
	display: flex;
	align-items: flex-end;
	gap: var(--pmx-space-3);
	margin-bottom: var(--pmx-space-3);
}


.app-crednamefield { flex: 1 1 220px; min-width: 0; }

.app-creddate { width: 160px; }

/* 시스템 구간과 수기 경력을 **한 목록에 시간순으로** 세운다(REQ-040-047).
   출처 칸이 어디서 온 것인지를 가른다(REQ-040-048). */
.app-source { white-space: nowrap; }

/* 숙련도 범례 — 필터 줄 끝에 선다(참조 구현). 칩의 진하기가 **크기로 읽히는** 값이라
   무엇이 진한 것인지 알려 주는 자리가 필요하다. */
.app-skilllegend {
	display: inline-flex;
	align-items: center;
	gap: var(--pmx-space-4);
	flex-wrap: wrap;
}

/* ── 050 렌탈 장비 ──────────────────────────────────────────────────────── */

/* 상태 태그가 **둘 나란히 설 수 있다**(ui.md 겹친 상태의 표기) — 만료되었는데 아직
   누가 쓰고 있으면 `지급됨`과 `계약만료`가 함께 서고, **좁아지면 줄을 바꾼다**.
   열을 나누지 않는 이유는 Q-080 에 있다. */
.app-assettags {
	display: inline-flex;
	gap: 5px;
	flex-wrap: wrap;
}

/* `계약 기간` 열의 정렬 기준을 열 머리에 적는다(ui.md) — 한 칸에 날짜가 둘이라
   밝히지 않으면 어느 쪽으로 줄 세운 것인지 물어야 한다. */
.app-sorthint {
	display: block;
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-muted);
	font-weight: var(--pmx-fw-regular);
}

/* 비고처럼 사람이 줄을 나눠 넣은 값. 넣은 대로 보이지 않으면 문단이 한 줄로 붙는다. */
.app-prewrap { white-space: pre-wrap; text-wrap: pretty; }

/* 계약 종료일이 지났는데 반납하지 않은 상태 — 상세의 계약 카드에서 날짜 자체가
   위험색으로 선다. **판정이지 저장값이 아니다**(REQ-050-018).

   **`.pmx-*` 규칙을 덮지 않는다**(CLAUDE.md 디자인 시스템 4) — 시스템 스타일은 캐스케이드
   레이어 안에 있고 프로젝트 CSS 는 레이어 밖이라 특정도 싸움 없이 이긴다. 그래서 우리
   클래스를 함께 붙여 색만 얹는다. */
.app-expired { color: var(--pmx-domain-asset-expired); }

/* 상세의 카드 격자 — 기본 · 계약 · 지급 구간이 한 줄, 같은 임대처 · 변경 이력이
   한 줄이다(참조 구현).

   **020 의 `.app-cardgrid` 를 쓰지 않는다.** 그것은 `auto-fill` 이라 채울 카드가 없어도
   트랙을 만들어 두므로, 카드가 둘뿐인 줄에서 **넓은 화면의 오른쪽 절반이 통째로 비고
   카드는 최소폭으로 눌린다**(실측 — 1450px 에서 트랙 넷 중 둘만 찼다). `auto-fit` 은
   빈 트랙을 접어 남은 카드가 폭을 나눠 갖는다.

   최소폭이 340px 인 것도 참조 구현의 값이다 — 지급 구간 카드가 그 폭에서 폼을 접어야
   한다. */
.app-eqgrid {
	display: grid;
	gap: var(--pmx-space-5);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
	align-items: start;
	margin-bottom: var(--pmx-space-5);
}

/* 지급 구간 — 한 장비가 사람을 옮겨 다닌 자취다. 표가 아니라 카드 목록인 것은
   행마다 **펼침과 폼**이 들어가기 때문이다(회수 · 날짜 정정). */
.app-spans {
	display: grid;
	gap: var(--pmx-space-4);
}

.app-span {
	display: grid;
	gap: var(--pmx-space-3);
	padding: var(--pmx-space-4) var(--pmx-space-5);
	border: 1px solid var(--pmx-border-subtle);
	border-radius: var(--pmx-radius-md);
}

/* 열린 구간이 맨 위에 서고 왼쪽 띠로 갈린다 — 지금 누가 쓰고 있는지가 훑는 눈에
   먼저 들어와야 한다. **색이 아니라 자리도 함께 말한다**(정렬이 그것을 맡는다). */
.app-span[data-open] { border-left: 3px solid var(--pmx-domain-asset-deployed); }

.app-span__head {
	display: flex;
	align-items: center;
	gap: var(--pmx-space-4);
	flex-wrap: wrap;
}

.app-span__del {
	display: flex;
	align-items: center;
	gap: var(--pmx-space-3);
	margin-left: auto;
}

/* 날짜 둘과 단추 하나가 한 줄에 선다. 좁아지면 접힌다 — 칸이 눌려 값이 한 자도
   보이지 않는 것보다 낫다. */
.app-span__form {
	display: flex;
	align-items: flex-end;
	gap: var(--pmx-space-4);
	flex-wrap: wrap;
}

.app-span__form .pmx-field { width: 170px; }

.app-span__form .pmx-select-wrap { min-width: 240px; }

.app-span__add { margin-top: var(--pmx-space-5); }

/* 같은 임대처 장비 — 누르면 그 장비의 상세로 간다(Q-119). */
.app-vendorlist {
	display: grid;
	gap: var(--pmx-space-3);
}

.app-vendorrow {
	display: flex;
	align-items: center;
	gap: var(--pmx-space-4);
	min-width: 0;
	text-decoration: none;
}

/* 자산번호는 줄이지 않는다 — 좁은 칸에서 `RNT-` 와 `9002` 로 갈리면 **코드가 코드로
   읽히지 않는다.** 줄어드는 것은 이름 쪽이며 거기에 말줄임이 붙는다. */
.app-vendorrow > .app-mono { flex-shrink: 0; white-space: nowrap; }

.app-vendorrow__name {
	color: var(--pmx-text-body);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.app-vendorrow > .app-assettags { margin-left: auto; flex-shrink: 0; }

/* 변경 이력 — 시각 · 무엇이 · 이전 → 지금 · 바꾼 사람. 표가 아니라 줄 목록인 것은
   칸의 폭이 값마다 크게 달라 표로 세우면 시각 칸만 넓어지기 때문이다. */
.app-histlist {
	display: grid;
	gap: var(--pmx-space-3);
}

.app-histrow {
	display: flex;
	align-items: baseline;
	gap: var(--pmx-space-4);
	flex-wrap: wrap;
	font-size: var(--pmx-font-xs);
	color: var(--pmx-text-body);
}

.app-histrow__at { flex-shrink: 0; }

.app-histrow__field { font-weight: var(--pmx-fw-medium); color: var(--pmx-text-strong); }

.app-histrow__actor { margin-left: auto; color: var(--pmx-text-muted); }

/* ── 100 이슈 ─────────────────────────────────────────────────────────────
   상세가 두 칸이다(참조 구현) — 본문과 논의가 왼쪽에, 값과 사람과 참조자가 오른쪽에
   선다. 정본이 이 배치를 갖지 않으므로 우리 클래스이며, `.pmx-*` 규칙을 덮지 않는다.

   레일 폭을 고정으로 두는 것은 그 칸이 정의 목록이라 **글줄이 아니라 라벨과 값의 짝**이
   기 때문이다 — 비율로 두면 넓은 화면에서 라벨 옆이 통째로 빈다. */
/* 상세의 본문 블록 사이 간격.
   **계약의 `pmx-detail` 은 탭이 있는 상세를 전제한다** — 슬롯이 알림 · KPI · 탭 · 탭
   패널이고, `__panel` 은 `role="tabpanel"` 을 달고 있어 탭 없는 상세의 본문에 쓸 이름이
   아니다. 컨테이너에 `gap` 을 더하는 것은 `.pmx-*` 를 덮는 일이라 하지 않는다 —
   **덮는 대신 우리 이름으로 여백만 준다.** 정본에 슬롯을 요청한 뒤 걷는다. */
.app-detailblock { margin-top: var(--pmx-space-6); }

.app-issuegrid {
	display: grid;
	grid-template-columns: minmax(0, 1fr) 340px;
	gap: var(--pmx-space-5);
	align-items: start;
}

.app-issuegrid__main,
.app-issuegrid__rail {
	display: grid;
	gap: var(--pmx-space-5);
	min-width: 0;
}

/* 좁은 폭에서 한 칸으로 접힌다(REQ-NFR-004). 340px 레일이 남으면 본문이 글줄로 서지
   못하고 표가 가로로 스크롤한다. */
@media (max-width: 960px) {
	.app-issuegrid { grid-template-columns: minmax(0, 1fr); }
}

/* 이슈 본문의 쓰기·미리보기 탭 — **스크립트가 없으면 탭 줄을 감춘다**(이슈 본문 Markdown
   설계 §3.1). 정본은 도구 줄을 `pmx-mdtools:not(:defined)` 로 감추지만 탭에는 같은 규칙이
   없다 — 상세의 탭이 링크라 스크립트 없이도 서야 해서다. 우리 표식만 잡고 `.pmx-*` 는
   덮지 않는다. 미리보기 패널은 서버가 hidden 으로 내므로 남는 것은 입력칸 — 기준선이다. */
pmx-tabs.app-mdtabs:not(:defined) {
	display: none;
}

/* `.pmx-formsection__grid` 의 행 간격이 0 이라(`gap: 0 <가로>`) 정의 목록은 자기 여백이
   없어 라벨과 위 칸의 값이 붙는다. 정본을 덮지 않고 행만 벌린다 — 020 이 같은 자리에서
   같은 답을 썼다. */
.app-issuedefs { row-gap: var(--pmx-space-4); }

/* 사람 한 자리 — **값과 창구가 세로로 쌓인다.** 칩 · 고르는 줄 · 보조 문장이 차례로
   서고, 간격은 여기 한 곳이 정한다. */
.app-cardstack {
	display: grid;
	gap: var(--pmx-space-3);
	justify-items: stretch;
}

/* 참조자 목록 — 사람마다 한 줄이다. */
.app-peoplelist {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: var(--pmx-space-3);
}

/* 사람 한 줄 — **칩이 왼쪽, 그 사람에게 거는 동작이 오른쪽 끝**이다. 동작을 칩 바로
   옆에 붙이면 이름 길이마다 단추 자리가 달라져 줄이 들쭉날쭉해진다. */
.app-cardrow {
	display: flex;
	align-items: center;
	gap: var(--pmx-space-3);
	min-width: 0;
}

.app-cardrow__act { margin-left: auto; }

/* 고르는 칸과 단추가 한 줄이다 — 칸이 남는 폭을 먹는다. `min-width: 0` 이 없으면
   **긴 이름이 든 셀렉트가 줄을 밀어내** 단추가 카드 밖으로 나간다. */
.app-pickrow {
	display: flex;
	align-items: center;
	gap: var(--pmx-space-3);
}

.app-pickrow > .pmx-field {
	flex: 1;
	min-width: 0;
}

/* `pmx-dlist` 의 값 칸에 붙는 둘.

   `.app-personsub` 는 **칩 아래 보조행**이라 아바타 폭만큼 들여쓴다. 여기서는 값에
   딸리는 줄이라 그 들여쓰기를 되돌린다.

   창구가 붙는 줄은 **값과 단추가 오른쪽 끝에 나란히** 선다 — 값 칸이 오른쪽 정렬이라
   왼쪽으로 밀면 그 줄만 다른 축에 서서 훑는 눈이 걸린다. */
.pmx-dlist__v > .app-personsub { margin: 2px 0 0; }

.app-dlistact {
	display: flex;
	align-items: center;
	justify-content: flex-end;
	gap: var(--pmx-space-4);
}

/* `.app-personsub` 는 **칩 아래 보조행**이라 아바타 폭만큼 들여쓴다. 여기서는 칩에
   딸린 줄이 아니라 구획 자신의 문장이므로 그 들여쓰기를 되돌린다. */
.app-cardstack > .app-personsub,
.app-cardrow > .app-personsub {
	margin: 0;
}

/* 논의 — 댓글과 변경 줄이 한 목록에 시간순으로 서되 **두 종류가 구분된다**
   (REQ-100-105). 구분을 색이 아니라 **왼쪽 선과 배경**으로 내는 것은 색으로 뜻을
   나타내는 축이 다섯으로 정해져 있고(설계 원칙) 이 슬라이스가 쓰는 것은 심각도
   하나이기 때문이다 — 논의에 여섯째 축을 만들지 않는다. */
.app-thread {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: var(--pmx-space-4);
}

.app-thread__item {
	padding: var(--pmx-space-3) 0 var(--pmx-space-3) var(--pmx-space-4);
	border-left: 2px solid var(--pmx-border-subtle);
}

/* 기계가 쓴 줄은 흐리게 — 사람이 쓴 말과 같은 무게로 읽히지 않게 한다. */
.app-thread__item[data-kind="change"] {
	border-left-color: var(--pmx-border-strong, var(--pmx-border-subtle));
	background: var(--pmx-surface-sunken);
}

.app-thread__head {
	display: flex;
	align-items: baseline;
	gap: var(--pmx-space-3);
	flex-wrap: wrap;
	font-size: var(--pmx-font-xs);
}

.app-thread__body {
	margin: var(--pmx-space-2) 0 0;
	white-space: pre-wrap;
	font-size: var(--pmx-font-sm);
	line-height: 1.7;
	color: var(--pmx-text-body);
}

.app-thread__item[data-kind="change"] .app-thread__body { color: var(--pmx-text-muted); }

/* 첨부 목록 — `AttachmentList` 는 평평하다(Q-155). 파일 이름 · 크기와 올린 사람 · 떼기가
   한 줄에 서며, 폴더도 버전 열도 없다. */
.app-attachlist {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: var(--pmx-space-3);
}

.app-attachrow {
	display: flex;
	align-items: baseline;
	gap: var(--pmx-space-3);
	flex-wrap: wrap;
	font-size: var(--pmx-font-sm);
}

/* 떼기는 오른쪽 끝이다 — 이름과 붙어 있으면 잘못 누른다. */
.app-attachrow > form { margin-left: auto; }

/* 첨부는 본문 카드 안에 **구분선 하나로 이어 붙는다** — 카드를 가르면 «무엇에 붙은
   파일인가» 가 배치에서 사라진다. 선을 여기 한 번만 두므로 올리기 폼은 갖지 않는다. */
.app-attachblock {
	margin-top: var(--pmx-space-6);
	padding-top: var(--pmx-space-5);
	border-top: 1px solid var(--pmx-border-subtle);
}

.app-attachblock__head {
	margin: 0 0 var(--pmx-space-4);
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-muted);
}

.app-attachform {
	display: grid;
	gap: var(--pmx-space-3);
	margin-top: var(--pmx-space-4);
	justify-items: start;
}

/* 보류를 여닫는 자리 — **계약의 「누르면 액션 줄이 폼으로 바뀐다」를 서버 렌더가 쓰는
   모양**이다. `<summary>` 가 액션 줄의 단추 하나로 서야 하므로 목록 표지를 걷고,
   펼친 폼은 `.pmx-istatctl__hold` 가 갖는다. */
.app-holddisc > summary {
	list-style: none;
	cursor: pointer;
}

.app-holddisc > summary::-webkit-details-marker { display: none; }

.app-holddisc[open] > summary { margin-bottom: var(--pmx-space-4); }

/* 댓글 폼 — **보류 폼과 한 규칙을 나눠 쓰던 자리**였다. 보류가 계약의
   `.pmx-istatctl__hold` 로 가면서 남은 하나라, 두 벌로 갈려 있던 것을 합친다. */
.app-commentform {
	display: grid;
	gap: var(--pmx-space-3);
	margin-top: var(--pmx-space-4);
	margin-bottom: var(--pmx-space-4);
	padding-bottom: var(--pmx-space-4);
	justify-items: stretch;
	border-bottom: 1px solid var(--pmx-border-subtle);
}

.app-commentform > button { justify-self: start; }

/* 논의 줄의 고치기·지우기 — 글 아래에 작게 선다. */
.app-thread__acts {
	display: flex;
	gap: var(--pmx-space-3);
	align-items: flex-start;
	margin-top: var(--pmx-space-2);
	flex-wrap: wrap;
}

.app-thread__acts summary {
	cursor: pointer;
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-muted);
}

.app-thread__acts form { display: grid; gap: var(--pmx-space-2); }

/* ── 070 대시보드 ─────────────────────────────────────────────────────────
   **화면이 여섯이고 주소가 하나다**(REQ-070-002). 여기 있는 것은 그 여섯이 나눠 쓰는
   배치이며, 색·간격·타이포는 --pmx-* 토큰만 읽는다.

   ★ 도메인 축 색을 무관한 계열에 빌려 쓰지 않는다 ★(REQ-070-052) — 계열을 가르기만
     하는 막대(사업유형 · 고용 형태 · 종료와 착수 · 역할 분포)는 **중립 색조**다.
     빌려 쓰면 이 화면에서 초록이 «계획대로» 가 아니라 «인력이 풀린다» 는 뜻이 된다.
     ui.md 가 그 중립 색조를 «디자인 시스템에 요청한 자리» 로 적어 두었으므로, 정본이
     그 이름을 세우면 아래 값들이 그것으로 바뀐다.

   ★ 판정된 값에는 도메인 축을 그대로 쓴다 ★ — 편차 · 진척 · 소진이 그것이며
     --pmx-domain-var-* 를 읽는다. */

.app-dash { display: grid; gap: var(--pmx-space-6); }

/* 지표 줄 — 좁아지면 접되 **순서를 유지한다**(위치가 순서를 말하므로 재배치하지
   않는다 · ui.md 반응형). */
.app-dash__kpis {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 158px), 1fr));
	gap: var(--pmx-space-4);
}

.app-dash__card { margin: 0; }
.app-dash__card .pmx-section { margin-top: 0; }

/* 두 구획을 나란히 — 좁아지면 한 칸으로 접힌다. */
.app-dash__pair,
.app-dash__cards {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 330px), 1fr));
	gap: var(--pmx-space-6);
	align-items: start;
}

.app-dash__stack { display: grid; gap: var(--pmx-space-6); align-items: start; }

/* PM 화면이 다른 화면과 **잇대어 서는** 자리의 경계(REQ-070-019) — 경계가 없으면
   전사 숫자와 담당분이 한 화면에 섞여 읽힌다. */
.app-dash__handoff {
	display: flex;
	align-items: baseline;
	gap: var(--pmx-space-4);
	flex-wrap: wrap;
	margin-top: var(--pmx-space-6);
	padding-top: var(--pmx-space-5);
	border-top: 1px solid var(--pmx-border-subtle);
}

/* **머리줄 바로 아래면 경계를 그리지 않는다** — 일반사용자를 겸한 PM 은 이 화면이 맨
   앞이고(REQ-070-019), 계약의 `.pmx-pageheader` 가 이미 아래 테두리를 갖는다. 그대로
   두면 **가로선이 두 줄로 겹쳐 선다.** 가를 것이 위에 없으므로 여백도 격자의 것만 쓴다.

   **여섯 화면 중 이것만 테두리로 시작한다** — 나머지 다섯은 `.app-dash__kpis` 나 카드로 열려
   겹칠 선이 없다. */
.pmx-pageheader + .app-dash__handoff {
	margin-top: 0;
	padding-top: 0;
	border-top: 0;
}

.app-dash__handofftitle {
	margin: 0;
	font-size: var(--pmx-font-lg);
	font-weight: var(--pmx-fw-bold);
	color: var(--pmx-text-strong);
}

.app-dash__handoff .app-note { margin: 0; }

/* 표 안의 보조 줄 — .app-personsub 와 달리 **아바타 들여쓰기가 없다**: 칩이 없는
   칸에서 쓰이므로 이름 글자와 같은 자리에서 시작한다. */
.app-sub {
	display: block;
	margin-top: 2px;
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-muted);
}

/* 편차 셀 — **격차에만 색을 준다**(ui.md). 문턱은 서버가 정했고 여기서 다시 적지
   않는다: 적으면 같은 사실이 화면마다 다르게 판정된다(REQ-070-047). */
.app-var {
	font-family: var(--pmx-font-mono);
	font-size: var(--pmx-font-sm);
	font-weight: var(--pmx-fw-semibold);
	color: var(--pmx-domain-var-onplan);
	white-space: nowrap;
}

.app-var[data-tone="under"] { color: var(--pmx-domain-var-under); }
.app-var[data-tone="over"] { color: var(--pmx-domain-var-over); }

/* 소진이 진척을 앞선 정도 — **원가 축이다**(Q-164). 같은 화면의 편차 셀과 한 색을
   나누지 않는다: 나누면 같은 빨강이 일정과 원가를 함께 말한다. */
.app-var[data-tone="cost-warn"] { color: var(--pmx-domain-cost-warn); }
.app-var[data-tone="cost-risk"] { color: var(--pmx-domain-cost-risk); }

/* 값 하나에 붙는 강조 — 기한을 넘긴 날짜. */
[data-tone="over"].app-mono,
.app-mono [data-tone="over"],
b[data-tone="over"] { color: var(--pmx-domain-var-over); font-weight: var(--pmx-fw-semibold); }

/* 값 하나에 붙는 강조 — 소진이 진척을 10%p 넘게 앞선 숫자(원가 축). */
.app-mono [data-tone="cost-risk"],
b[data-tone="cost-risk"] { color: var(--pmx-domain-cost-risk); font-weight: var(--pmx-fw-semibold); }

[data-tone="watch"].app-mono { color: var(--pmx-domain-health-watch); font-weight: var(--pmx-fw-semibold); }

.app-over {
	margin-left: 5px;
	font-size: var(--pmx-font-2xs);
	font-weight: var(--pmx-fw-semibold);
	color: var(--pmx-domain-health-risk);
}

/* 사유 줄 — 색 붙은 조각을 한 행에 둘 두지 않으므로 **글자로만** 선다(ui.md). */
.app-reasons {
	display: block;
	margin-top: 4px;
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-domain-health-watch);
	font-weight: var(--pmx-fw-medium);
}

.app-vacant {
	font-size: var(--pmx-font-xs);
	font-weight: var(--pmx-fw-semibold);
	color: var(--pmx-domain-health-watch);
}

/* ── 계약 공수 소진율 — 진척과 소진 두 줄이 나란히 놓여야 뜻이 생긴다 ─────── */
.app-burn { display: grid; gap: 4px; margin-top: var(--pmx-space-5); }

.app-burn__head {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 10px;
}

.app-burn__head > a {
	font-size: var(--pmx-font-sm);
	font-weight: var(--pmx-fw-semibold);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.app-burn__row {
	display: grid;
	grid-template-columns: 40px 1fr 42px;
	align-items: center;
	gap: 8px;
}

.app-burn__k { font-size: var(--pmx-font-2xs); color: var(--pmx-text-muted); }
.app-burn__v { text-align: right; }
.app-burn .app-note { margin: 2px 0 0; }

/* ── 이슈로 본 위험 ─────────────────────────────────────────────────────── */
.app-riskcard {
	display: flex;
	align-items: center;
	gap: var(--pmx-space-4);
	margin-top: var(--pmx-space-4);
	padding: 11px 13px;
	border: 1px solid var(--pmx-border-subtle);
	border-left: 3px solid var(--pmx-domain-sev-high);
	border-radius: var(--pmx-radius-md);
	background: var(--pmx-surface-inset);
	text-decoration: none;
}

.app-riskcard__main { min-width: 0; flex: 1; }

.app-riskcard__name {
	display: block;
	font-size: var(--pmx-font-sm);
	font-weight: var(--pmx-fw-semibold);
	color: var(--pmx-text-strong);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.app-riskcard__meta {
	margin-top: 3px;
	display: flex;
	align-items: center;
	gap: 8px;
	flex-wrap: wrap;
}

.app-riskcard__score { text-align: right; flex-shrink: 0; }

.app-riskcard__score > b {
	display: block;
	font-family: var(--pmx-font-mono);
	font-size: 17px;
	color: var(--pmx-domain-sev-high);
}

.app-riskcard__score > span {
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-muted);
	white-space: nowrap;
}

/* ── 종료 · 착수 타임라인 ──────────────────────────────────────────────────
   막대가 **차트 전용 색조**를 쓴다 — 종료와 착수는 계획 편차도 일정 위험도 아니라
   도메인 축을 빌려 쓰지 않는다(REQ-070-052). 계열이 둘이라 색조가 갈리는 두 값을
   집는다 — 정본이 파랑↔회색을 번갈아 두었다. */
.app-flow {
	display: grid;
	grid-template-columns: repeat(6, minmax(0, 1fr));
	gap: 8px;
	align-items: end;
	margin-top: var(--pmx-space-5);
}

.app-flow__col { text-align: center; }

.app-flow__bars {
	height: 92px;
	display: flex;
	align-items: flex-end;
	justify-content: center;
	gap: 4px;
}

.app-flow__bar {
	width: 15px;
	min-height: 2px;
	border-radius: 3px 3px 0 0;
	background: var(--pmx-domain-chart-1);
}

.app-flow__bar[data-series="in"] { background: var(--pmx-domain-chart-2); }

.app-flow__label {
	display: block;
	margin-top: 6px;
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-muted);
}

.app-flow__net {
	display: block;
	font-weight: var(--pmx-fw-bold);
	color: var(--pmx-text-faint);
}

.app-flow__net[data-sign="plus"] { color: var(--pmx-text-strong); }
.app-flow__net[data-sign="minus"] { color: var(--pmx-domain-health-watch); }

.app-flow__legend {
	display: flex;
	gap: var(--pmx-space-5);
	flex-wrap: wrap;
	margin-top: var(--pmx-space-4);
	padding-top: var(--pmx-space-4);
	border-top: 1px solid var(--pmx-border-subtle);
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-muted);
}

.app-flow__legend span { display: inline-flex; align-items: center; gap: 5px; }
.app-flow__legend i { width: 9px; height: 9px; border-radius: 2px; background: var(--pmx-domain-chart-1); }
.app-flow__legend i[data-series="in"] { background: var(--pmx-domain-chart-2); }
.app-flow__legendtail { margin-left: auto; }

.app-flow__rows {
	margin: var(--pmx-space-4) 0 0;
	padding: 0;
	list-style: none;
	display: grid;
	gap: 6px;
	font-size: var(--pmx-font-xs);
	line-height: 1.7;
}

.app-flow__rows li { display: flex; gap: 10px; }
.app-flow__rowlabel { width: 34px; flex-shrink: 0; font-weight: var(--pmx-fw-semibold); }

/* ── 계열을 가르기만 하는 수평 막대 (사업유형 · 고용 형태 · 역할 분포) ──────
   셋이 한 규칙을 나눠 쓰고 **차트마다 첫 계열부터 다시 센다** — 계열은 순서를 뜻하지
   않으므로 값이 큰 쪽에 진한 색을 주지 않는다(정본이 램프로 만들지 않은 이유와 같다).
   역할 분포는 계열이 하나라 기본값 한 색으로 선다. */
.app-bar {
	display: block;
	margin-top: var(--pmx-space-4);
	text-decoration: none;
}

.app-bar__head {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 10px;
	margin-bottom: 5px;
	font-size: var(--pmx-font-sm);
}

.app-bar__track {
	display: block;
	height: 8px;
	border-radius: 4px;
	background: var(--pmx-surface-sunken);
	overflow: hidden;
}

.app-bar__fill {
	display: block;
	height: 100%;
	border-radius: 4px;
	background: var(--pmx-domain-chart-1);
}

.app-bar__fill[data-series="OPERATION"],
.app-bar__fill[data-series="CONTRACT"] { background: var(--pmx-domain-chart-2); }
.app-bar__fill[data-series="OUTSOURCED"] { background: var(--pmx-domain-chart-3); }

/* ── 인력 가동률 — **막대에 색을 주지 않고** 강조하는 것은 미투입 하나다 ──── */
.app-util__head {
	display: flex;
	justify-content: space-between;
	align-items: flex-start;
	gap: var(--pmx-space-4);
	padding-bottom: var(--pmx-space-4);
	border-bottom: 1px solid var(--pmx-border-subtle);
}

.app-util__value {
	font-size: 22px;
	font-weight: var(--pmx-fw-bold);
	color: var(--pmx-text-strong);
}

.app-util__side {
	text-align: right;
	font-size: var(--pmx-font-xs);
	color: var(--pmx-text-muted);
	line-height: 1.7;
}

.app-util__side b { color: var(--pmx-domain-health-watch); }

/* ── 다가오는 마감 — 구분 칩. **색이 종류를 가르기만 한다** ───────────────── */
.app-kind {
	display: inline-flex;
	align-items: center;
	padding: 1px 8px 2px;
	border-radius: var(--pmx-radius-sm);
	background: var(--pmx-surface-sunken);
	color: var(--pmx-text-body);
	font-size: var(--pmx-font-2xs);
	font-weight: var(--pmx-fw-semibold);
	white-space: nowrap;
}

.app-kind[data-kind="milestone"] { background: var(--pmx-primary-subtle); color: var(--pmx-primary-hover); }
.app-kind[data-kind="issue"] { background: var(--pmx-domain-health-risk-bg); color: var(--pmx-domain-health-risk); }

.app-due { font-size: var(--pmx-font-sm); color: var(--pmx-text-strong); }
.app-due[data-tone="over"] { color: var(--pmx-domain-health-risk); }
.app-due[data-tone="watch"] { color: var(--pmx-domain-health-watch); }

/* ── 투입 인력의 담당 프로젝트 칩 ────────────────────────────────────────── */
.app-chips { display: flex; gap: 5px; flex-wrap: wrap; align-items: center; }

.app-prjchip {
	border: 1px solid var(--pmx-primary-border);
	background: var(--pmx-primary-subtle);
	color: var(--pmx-primary-hover);
	border-radius: var(--pmx-radius-sm);
	padding: 1px 7px 2px;
	font-family: var(--pmx-font-mono);
	font-size: var(--pmx-font-2xs);
	font-weight: var(--pmx-fw-semibold);
	white-space: nowrap;
	text-decoration: none;
}

/* ── 참여 인력 지급 장비의 요약 3칸 ──────────────────────────────────────── */
.app-summary3 {
	display: flex;
	align-items: stretch;
	margin-top: var(--pmx-space-4);
	border: 1px solid var(--pmx-border-subtle);
	border-top: 3px solid var(--pmx-accent);
	border-radius: var(--pmx-radius-md);
	overflow: hidden;
}

.app-summary3__cell { flex: 1; min-width: 0; padding: 10px 14px 12px; }
.app-summary3__cell + .app-summary3__cell { border-left: 1px solid var(--pmx-border-subtle); }
.app-summary3__k { display: block; font-size: var(--pmx-font-2xs); color: var(--pmx-text-muted); }

.app-summary3__v {
	font-size: 19px;
	font-weight: var(--pmx-fw-bold);
	color: var(--pmx-text-strong);
	letter-spacing: -0.02em;
}

.app-summary3__v[data-tone="watch"] { color: var(--pmx-domain-health-watch); }
.app-summary3__u { font-size: var(--pmx-font-2xs); color: var(--pmx-text-muted); margin-left: 3px; }

/* ── PM 담당 프로젝트 카드 — 정본의 카드 아래에 우리 줄이 붙는다 ─────────── */
.app-pmcard { display: flex; flex-direction: column; }

.app-pmcard .pmx-projectcard {
	border-bottom: none;
	border-bottom-left-radius: 0;
	border-bottom-right-radius: 0;
	box-shadow: none;
}

.app-pmcard__foot {
	background: var(--pmx-surface-card);
	border: 1px solid var(--pmx-border-subtle);
	border-radius: 0 0 var(--pmx-radius-lg) var(--pmx-radius-lg);
	padding: 13px 16px 15px;
	box-shadow: var(--pmx-shadow-xs);
	display: grid;
	gap: 11px;
}

.app-pmcard__crew { display: flex; align-items: center; gap: 8px; min-width: 0; }
.app-pmcard__crew .app-note { margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 아바타는 다섯까지 겹쳐 세운다 — 이름은 옆의 줄이 잇는다. */
.app-avatars { display: inline-flex; flex-shrink: 0; }
.app-avatars > .pmx-person + .pmx-person { margin-left: -6px; }

/* ── 인계 대기 카드 ─────────────────────────────────────────────────────── */
.app-handover {
	display: flex;
	align-items: center;
	gap: var(--pmx-space-5);
	margin-top: var(--pmx-space-4);
	padding: 12px 14px;
	border: 1px solid var(--pmx-border-subtle);
	border-left: 3px solid var(--pmx-domain-health-watch);
	border-radius: var(--pmx-radius-md);
	background: var(--pmx-surface-inset);
	text-decoration: none;
	flex-wrap: wrap;
}

/* 착수일을 넘긴 것 — 020 이 착수지연으로 판정하는 자리와 같은 사실이다. */
.app-handover[data-late] { border-left-color: var(--pmx-domain-health-risk); }

.app-handover__main { min-width: 0; flex: 1; }

.app-handover__name {
	display: block;
	font-size: var(--pmx-font-sm);
	font-weight: var(--pmx-fw-semibold);
	color: var(--pmx-text-strong);
}

.app-handover__d { text-align: right; flex-shrink: 0; }
.app-handover__d > b { display: block; font-size: 15px; color: var(--pmx-domain-health-watch); }
.app-handover[data-late] .app-handover__d > b { color: var(--pmx-domain-health-risk); }

.app-handover__chip {
	flex-shrink: 0;
	padding: 3px 10px 4px;
	border-radius: var(--pmx-radius-full);
	font-size: var(--pmx-font-2xs);
	font-weight: var(--pmx-fw-semibold);
	background: var(--pmx-domain-health-watch-bg);
	color: var(--pmx-domain-health-watch);
}

/* ── 진행 단계별 건수 — **위치가 순서를 말하므로 재배치하지 않고 줄만 접는다** */
.app-stages {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 96px), 1fr));
	gap: var(--pmx-space-3);
	margin-top: var(--pmx-space-4);
}

.app-stages__cell {
	display: block;
	padding: 10px 12px 12px;
	border: 1px solid var(--pmx-border-subtle);
	border-radius: var(--pmx-radius-md);
	background: var(--pmx-surface-inset);
	text-align: center;
	text-decoration: none;
}

.app-stages__k { display: block; font-size: var(--pmx-font-2xs); color: var(--pmx-text-muted); }

.app-stages__v {
	display: block;
	margin-top: 4px;
	font-size: var(--pmx-font-xl);
	font-weight: var(--pmx-fw-bold);
	color: var(--pmx-text-strong);
}

/* ── 일반사용자의 참여 프로젝트 칩 줄 ───────────────────────────────────── */
.app-chiprow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.app-chiprow > .app-note { margin: 0; white-space: nowrap; }

.app-prjbtn {
	display: inline-flex;
	align-items: stretch;
	min-height: var(--pmx-control-lg);
	background: var(--pmx-surface-card);
	border: 1px solid var(--pmx-border-subtle);
	border-radius: var(--pmx-radius-md);
	box-shadow: var(--pmx-shadow-xs);
	overflow: hidden;
	text-decoration: none;
}

/* 왼쪽 띠가 일정 위험이다 — **판정하지 않은 것은 속성이 없어 중립으로 선다**. */
.app-prjbtn__band { width: 3px; flex-shrink: 0; background: var(--pmx-border-default); }
.app-prjbtn__band[data-health="ontrack"] { background: var(--pmx-domain-health-ontrack); }
.app-prjbtn__band[data-health="watch"] { background: var(--pmx-domain-health-watch); }
.app-prjbtn__band[data-health="risk"] { background: var(--pmx-domain-health-risk); }

.app-prjbtn__body {
	display: flex;
	align-items: baseline;
	gap: 8px;
	flex-wrap: wrap;
	padding: 7px 13px 8px 8px;
}

.app-prjbtn__name { font-size: var(--pmx-font-sm); font-weight: var(--pmx-fw-semibold); color: var(--pmx-text-strong); }
.app-prjbtn__stage { color: var(--pmx-text-muted); }

.app-prjbtn__count,
.app-prjbtn__role {
	font-size: var(--pmx-font-2xs);
	font-weight: var(--pmx-fw-semibold);
	border-radius: var(--pmx-radius-full);
	padding: 1px 7px 2px;
}

.app-prjbtn__count {
	font-family: var(--pmx-font-mono);
	color: var(--pmx-primary-hover);
	background: var(--pmx-primary-subtle);
	border: 1px solid var(--pmx-primary-border);
}

.app-prjbtn__role { color: var(--pmx-text-muted); background: var(--pmx-surface-sunken); }

/* 목록 하나짜리 자리 — 표를 세우기에는 열이 없고 문단으로 두면 항목이 안 보인다. */
.app-plain { margin: var(--pmx-space-3) 0 0; padding-left: var(--pmx-space-6); display: grid; gap: 4px; }
.app-plain .app-sub { display: inline; margin-left: 6px; }

/* 빈 상태가 함께 두는 길 — `필터 지우기` 또는 `이슈 등록`(ui.md 상태별 화면).
   문구 아래 줄에 서서 «없다» 와 «그래서 무엇을 하면 되나» 가 갈려 읽힌다. */
.app-emptyact {
	display: block;
	margin-top: var(--pmx-space-4);
}

/* ── 090 시스템 관리 ────────────────────────────────────────────────────────

   상세의 카드 격자. `auto-fit` 인 것은 카드가 둘뿐인 줄에서 `auto-fill` 이 **빈 트랙을
   남겨** 오른쪽 절반이 통째로 비고 카드가 최소폭으로 눌리기 때문이다 — 050 이 같은
   자리에서 같은 답을 냈다(`.app-eqgrid`). 그쪽 이름을 빌려 쓰지 않는 것은 두 슬라이스의
   카드 최소폭이 갈릴 때 한쪽을 고치면 다른 쪽이 함께 움직이기 때문이다. */
.app-loggrid {
	display: grid;
	gap: var(--pmx-space-5);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
	align-items: start;
}

/* 본문·스택·전후 값이 서는 한 덩어리. 이름줄과 값이 세로로 붙는다. */
.app-logpart + .app-logpart { margin-top: var(--pmx-space-5); }

.app-logpart__label {
	font-size: var(--pmx-font-2xs);
	font-weight: var(--pmx-fw-semibold);
	color: var(--pmx-text-muted);
	margin-bottom: 5px;
}

/* 잘렸다는 표시. **본문과 스택이 따로 단다**(erd.md) — `500` 인 `POST` 는 둘이 함께
   있어 하나로 합치면 어느 쪽이 잘렸는지 알 수 없다. */
.app-logpart__cut {
	margin-left: 8px;
	font-weight: var(--pmx-fw-regular);
	color: var(--pmx-domain-health-watch);
}

/* 저장된 그대로를 보이는 자리(ui.md) — 색을 입히거나 JSON 을 펴지 않는다. 손대면
   *"실제로 뭐가 왔는지"* 가 흐려진다.

   **가로로 밀지 않는다**(REQ-NFR-004) — 스택 한 줄이 페이지를 밀지 않도록 접고,
   접히지 않는 긴 토큰(URL·해시)은 `overflow-wrap` 이 끊는다. */
.app-logbody {
	margin: 0;
	padding: 10px 12px;
	background: var(--pmx-surface-sunken);
	border-radius: var(--pmx-radius-md);
	font-family: var(--pmx-font-mono);
	font-size: var(--pmx-font-xs);
	line-height: 1.6;
	color: var(--pmx-text-body);
	white-space: pre-wrap;
	overflow-wrap: anywhere;
}

/* ── 110 알림 ──────────────────────────────────────────────────────────────

   셸 머리의 종과 그것이 여는 팝오버, 그리고 알림 한 줄. **정본이 갖지 않는 자리다** —
   참조 구현이 이 셋을 전부 인라인 style 로 갖고 있고(`pmx-shell.jsx` 의 BellIcon),
   시스템의 `.pmx-popover` 는 산문 중간에 서는 **24px 아이콘 트리거의 것**이라 380px
   짜리 목록 패널이 그 계약에 들어가지 않는다.

   `.pmx-*` 규칙을 덮지 않는다 — 여기 있는 것은 전부 우리 이름이다. */

.app-bell {
	position: relative;
	display: flex;
	align-items: center;
	flex-shrink: 0;
}

/* 터치 타깃 하한을 스스로 진다 — `.pmx-btn` 이 아니라 우리 요소라 계약의 720px 규칙이
   걸리지 않는다(정본 6.8.0). */
.app-bell__btn {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 38px;
	height: var(--pmx-control-lg);
	border: 1px solid transparent;
	border-radius: var(--pmx-radius-md);
	color: var(--pmx-text-muted);
	text-decoration: none;
}

.app-bell__btn:hover { color: var(--pmx-text-body); }

/* 열린 동안 눌린 채로 선다 — 패널이 어디서 나왔는지를 말한다. */
.app-bell__btn[aria-expanded="true"] {
	color: var(--pmx-primary);
	background: var(--pmx-primary-subtle);
	border-color: var(--pmx-primary-border);
}

/* **`0` 이면 서지 않는다**(prd.md 예외표) — 서버가 아예 그리지 않으므로 여기에 «0 을
   감춘다» 는 규칙이 없다. */
.app-bell__badge {
	position: absolute;
	top: 3px;
	right: 3px;
	min-width: 15px;
	height: 15px;
	padding: 0 4px;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: var(--pmx-radius-full);
	/* 도메인 축이 아니라 **시맨틱 층**을 읽는다. 참조 구현은 이 자리를
	   `--pmx-domain-health-risk` 로 갖는데 그것은 «프로젝트 상태» 축의 색이라, 빌려
	   쓰면 같은 빨강이 한 화면에서 두 가지를 뜻하게 된다 — 알림에는 도메인 축이 아예
	   없다(REQ-110-026). 인라인 style 에만 있는 값이라 정본이 그것을 정한 적도 없다. */
	background: var(--pmx-danger);
	color: var(--pmx-on-status);
	font-family: var(--pmx-font-mono);
	font-size: 10px;
	font-weight: var(--pmx-fw-bold);
	line-height: 1;
}

/* 뒤를 가리지 않는다 — 모달이 아니라 팝오버라 스크림이 없다. */
.app-bell__panel {
	position: absolute;
	top: calc(100% + 6px);
	right: 0;
	z-index: var(--pmx-z-dropdown);
	width: min(92vw, 380px);
	max-height: min(70dvh, 460px);
	display: flex;
	flex-direction: column;
	background: var(--pmx-surface-card);
	border: 1px solid var(--pmx-border-subtle);
	border-radius: var(--pmx-radius-lg);
	box-shadow: var(--pmx-shadow-lg);
	overflow: hidden;
}

/* ★ 이 줄이 없으면 패널이 닫히지 않는다 ★ — `display` 를 준 요소에는 브라우저 기본
   스타일시트의 `[hidden] { display: none }` 이 지므로, **숨기라는 속성이 아무 일도 하지
   않는다.** 정본의 패널 셋(`.pmx-popover__panel` · `.pmx-tooltip__tip` ·
   `.pmx-treepicker__panel`)이 전부 같은 줄을 갖는 이유가 그것이다. */
.app-bell__panel[hidden] { display: none; }

/* 좁은 폭에서는 종이 아니라 **상단바에 맞춘다.** 종의 오른쪽 끝에 맞추면 그 오른쪽에
   선 아바타만큼 패널이 왼쪽으로 밀려 화면 밖으로 잘린다(390px 에서 왼쪽으로 121px).
   폭을 줄이는 것으로는 풀리지 않는다 — 기준점이 화면 가장자리가 아니기 때문이다.

   **기준을 비우면 sticky 인 상단바가 그 자리를 갖는다** — 사이의 `pmx-drawer` 와
   슬롯은 `display:contents` 라 기준이 되지 않는다. 좌우를 함께 박아 폭이 화면을
   따라가게 두며, 12px 은 그 폭에서 상단바가 갖는 좌우 여백이다. */
@media (max-width: 720px) {
	.app-bell { position: static; }

	.app-bell__panel {
		left: 12px;
		right: 12px;
		width: auto;
	}
}

.app-bell__inner {
	display: flex;
	flex-direction: column;
	min-height: 0;
}

.app-bell__head {
	display: flex;
	align-items: center;
	gap: var(--pmx-space-4);
	padding: 10px 12px;
	border-bottom: 1px solid var(--pmx-border-subtle);
}

.app-bell__title { font-size: var(--pmx-font-md); color: var(--pmx-text-strong); }

.app-bell__count {
	font-size: var(--pmx-font-2xs);
	font-weight: var(--pmx-fw-bold);
	color: var(--pmx-danger-text);
}

/* 목록만 구른다 — 머리와 발은 제자리에 남아 «전체 보기» 가 늘 닿는다. */
.app-bell__list { overflow-y: auto; flex: 1; min-height: 0; }

.app-bell__none {
	margin: 0;
	padding: 32px 14px;
	text-align: center;
	font-size: var(--pmx-font-sm);
	color: var(--pmx-text-muted);
}

.app-bell__foot {
	display: flex;
	justify-content: space-between;
	gap: var(--pmx-space-4);
	padding: 9px 12px;
	border-top: 1px solid var(--pmx-border-subtle);
	background: var(--pmx-surface-inset);
	font-size: var(--pmx-font-xs);
}

/* 알림 한 줄 — **팝오버와 전체 화면이 같은 줄을 쓴다**(ui.md). 두 자리의 차이는
   감싸는 상자이지 줄이 아니다. */
.app-noticelist { display: flex; flex-direction: column; }

.app-notice {
	display: block;
	padding: 11px 14px 12px;
	border-bottom: 1px solid var(--pmx-border-subtle);
	/* 안 읽은 것에만 켜지는 띠. 자리는 늘 잡아 두어 켜질 때 줄이 밀리지 않는다. */
	border-left: 3px solid transparent;
	color: inherit;
	text-decoration: none;
}

.app-notice:last-child { border-bottom: 0; }

/* **읽은 것에는 아무 표시도 없다**(ui.md 「침묵이 기본입니다」) — 양쪽에 표시를 두면
   표시가 뜻을 잃는다. 색만으로 말하지 않는 것은 줄 안의 숨은 글자가 진다. */
.app-notice[data-unread] {
	border-left-color: var(--pmx-primary);
	background: var(--pmx-primary-subtle);
}

.app-notice__head {
	display: flex;
	align-items: baseline;
	gap: var(--pmx-space-4);
	flex-wrap: wrap;
}

/* 종류는 **묶어 보이는 축**이라 중립색이다 — 색으로 급한 정도를 말하지 않는다
   (REQ-110-026). */
.app-notice__kind {
	font-size: var(--pmx-font-2xs);
	font-weight: var(--pmx-fw-bold);
	color: var(--pmx-text-muted);
	white-space: nowrap;
}

.app-notice__title {
	font-size: var(--pmx-font-sm);
	color: var(--pmx-text-strong);
	text-wrap: pretty;
}

.app-notice[data-unread] .app-notice__title { font-weight: var(--pmx-fw-bold); }

/* 한 줄이며 넘치면 말줄임한다(ui.md). */
.app-notice__body {
	display: block;
	margin-top: 3px;
	font-size: var(--pmx-font-xs);
	color: var(--pmx-text-muted);
	line-height: 1.6;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.app-notice__meta {
	display: flex;
	gap: 10px;
	margin-top: 5px;
	font-size: var(--pmx-font-2xs);
	color: var(--pmx-text-faint);
}

/* 수신 설정 격자의 종류 머리글 — **스위치가 아니라 라벨이다**(Q-144). */
.app-prefgroup {
	font-size: var(--pmx-font-2xs);
	font-weight: var(--pmx-fw-semibold);
	color: var(--pmx-text-muted);
	letter-spacing: var(--pmx-ls-wide);
	background: var(--pmx-surface-sunken);
	text-align: left;
}

/* ── 결함 보고 모달 (설계 7절) ───────────────────────────────────────────────
   부품은 전부 정본의 것(`.pmx-field` · `.pmx-radiogroup` · `.pmx-check` · `.pmx-filedrop` · `.pmx-disclosure` ·
   `.pmx-alert` · `.pmx-btn`)이고 여기는 **칸 사이의 간격과 미리보기 · 맥락 표의 배치**뿐이다. `.pmx-*` 를
   덮지 않고 **모달 폭도 정하지 않는다** — 정본 기본 520px 이다(설계 7절 「모달은 문장을 줄이고 부품으로 말한다」). */
/* 트랙의 최소를 0 으로 둔다 — 칸 하나의 최소 폭이 트랙을 넓히면 400px 에서 본문이 옆으로 구른다(설계 7절). */
.app-defect { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--pmx-space-4); }
/* `.app-note` 의 위쪽 여백(10px 0 0)은 다른 자리(문단 뒤에 붙는 안내)의 값이라 여기서는 쓰지 않는다 —
   머리 바로 아래인 이 자리는 첫 칸과의 간격만 있으면 된다. `.app-note` 자체는 덮지 않는다. */
/* 글자는 본문 크기다 — `.app-note` 의 안내 글씨(2xs)로는 머리에서 읽히지 않는다(사람의 결정 · 설계 7절). */
.app-defect__lead { margin: 0 0 var(--pmx-space-4); font-size: var(--pmx-font-md); }
/* 결과 자리는 격자 밖이라 비면 아무것도 먹지 않고, 알림 상자가 서면 아래 칸과 간격을 둔다. */
.app-defect__result:not(:empty) { margin-block-end: var(--pmx-space-4); }
.app-defect__picture { display: grid; gap: var(--pmx-space-3); }
/* 미리보기 — 붙인 순서대로 한 줄에 셋. **표시 규칙은 `:not([hidden])` 에만 준다** — 비었을 때 `hidden` 이 서야 격자
   칸 사이의 간격이 빈 자리에 서지 않는다(IMPLEMENTATION.md 함정 「`display` 를 준 요소를 `hidden` 으로 감춘다」). */
.app-defect__previews:not([hidden]) { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--pmx-space-2); }
/* 작게 두되 이 탭을 찍었는지는 알아볼 크기다 — 잘못 고른 것은 미리보기가 잡는다(설계 6절). */
.app-defect__thumb { display: block; width: 100%; aspect-ratio: 16 / 10; object-fit: contain; background: var(--pmx-surface-sunken); border: 1px solid var(--pmx-border-subtle); border-radius: var(--pmx-radius-md); }
.app-defect__ctxsum { font-weight: var(--pmx-fw-regular); color: var(--pmx-text-muted); overflow-wrap: anywhere; }
.app-defect__context { margin: 0; display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--pmx-space-1) var(--pmx-space-4); font-size: var(--pmx-font-xs); }
.app-defect__context dt { color: var(--pmx-text-muted); }
.app-defect__context dd { margin: 0; overflow-wrap: anywhere; }
.app-defect__body { margin: 0; max-height: 40vh; overflow: auto; font-size: var(--pmx-font-xs); background: var(--pmx-surface-sunken); padding: var(--pmx-space-3); border-radius: var(--pmx-radius-md); white-space: pre-wrap; }
