/*
  프로젝트관리 전용 스타일.

  [왜 늦게 생겼나]

  다른 다섯 업무는 처음부터 모듈 스타일시트를 두었는데 여기만 없었다.
  그래서 화면들이 쓰는 `pm-*` 클래스 몇 개가 **어디에도 정의돼 있지 않았고**,
  그 자리들이 아무 스타일 없이 나왔다. 눈에 잘 안 띄는 종류의 어긋남이다 —
  화면은 뜨고 글자도 다 보이므로 「원래 이런가 보다」 로 읽힌다.
*/

/* ── 구역 제목 ───────────────────────────────────────────

   한 화면에 표가 둘 이상일 때 그 사이를 가른다. `<h2>` 를 그냥 두면
   화면 제목(PageHeading)과 크기가 비슷해 어느 쪽이 위인지 헷갈린다.
   ---------------------------------------------------------- */

.pm-subhead {
  margin: 0.8rem 0 0.4rem;
  font-size: var(--jsini-fs-md);
  font-weight: 600;
}

/* ── 탭 안쪽 ─────────────────────────────────────────────

   DevExpress 탭은 내용 높이를 스스로 정하지 않는다. 안에 좌우 분할이
   들어가면 높이 0 으로 잡혀 **아무것도 안 보인다.** 최소 높이를 준다.
   ---------------------------------------------------------- */

.pm-tab-body {
  min-height: 60vh;
  padding-top: 0.6rem;
}

/* ── 다이어그램 자리 ─────────────────────────────────────

   그림이 자기 컨테이너의 크기를 그대로 쓴다. 높이를 안 주면 0 이 되어
   빈 칸으로 보인다 — Monaco 와 같은 함정이다.
   ---------------------------------------------------------- */

.pm-diagram-host {
  position: relative;   /* 아래 「불러오는 중」 덮개의 기준 */
  width: 100%;
  height: min(70vh, 760px);
  border: 1px solid var(--jsini-border);
  border-radius: 0.25rem;
  overflow: hidden;
}

/* 그림을 불러오는 동안 덮는다.

   대상 DB 에 네 번 물어보는 동안(저장본·표 목록·칸·외래키) 화면은 **옛 그림
   그대로**다. 그 사이 사람은 눌린 것인지 알 수 없어 다시 누른다. 덮개가
   그것을 막고(클릭이 캔버스에 닿지 않는다) 진행 중임을 말한다. */
.pm-diagram-loading {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  gap: 0.5rem;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--jsini-surface) 72%, transparent);
  color: var(--jsini-text-muted, #6b7280);
  font-size: var(--jsini-fs-sm);
}

/* 도는 표시. 저장소의 도는 표시는 DevExpress 것 한 벌인데, 그것은 부품 안에
   들어가는 물건이라 이 덮개에는 CSS 로 둔다. */
.pm-diagram-loading::before {
  content: "";
  width: 1.1rem;
  height: 1.1rem;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: pm-spin 0.8s linear infinite;
}

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

/* 움직임을 줄여 달라고 한 사람에게는 돌리지 않는다 — 자리만 지킨다. */
@media (prefers-reduced-motion: reduce) {
  .pm-diagram-loading::before { animation: none; }
}

/* ── 덧붙이는 말 ─────────────────────────────────────────

   확인 창의 부연 설명처럼, 읽어도 되고 안 읽어도 되는 줄.
   본문과 같은 굵기·색이면 사용자가 두 문장을 같은 무게로 읽는다.
   ---------------------------------------------------------- */

.pm-muted {
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-md);
}

/* 바깥 화면 iframe(`pm-frame`)은 여기 없다. 유형이 EMBEDDED 인 메뉴를
   셸의 공용 화면 하나가 받게 되면서 규칙도 `app.css` 의 `jsini-embed`
   한 벌로 합쳤다 — 같은 규칙이 모듈마다 갈려 있던 자리다. */

/* ── 쿼리 테스터의 위아래 분할 (`pm-query-splitter`) ──────────

   위가 SQL 편집기(`pm-query`), 아래가 결과 표다.

   [이 규칙을 화면의 `.razor.css` 에 두면 **한 줄도 안 걸린다**]

   실제로 그렇게 두었다가 **편집기도 표도 안 보이는** 상태로 올라갔다.
   Blazor 의 CSS 격리는 범위 표시(`b-xxxxx`)를 **HTML 요소에만** 붙인다 —
   `<DxSplitter>` 는 부품이라 안 붙는다. 그래서

     · `.pm-query-splitter { height: … }` → `.pm-query-splitter[b-xxxxx]` 가
       되어 **아무것도 겨누지 못하고**, 판이 높이를 못 받는다.
     · `::deep .dxbl-splitter-pane-content` → `[b-xxxxx] .dxbl-…` 이라
       **범위 표시를 가진 요소의 자손**만 본다. 판 안쪽에 닿지 못한다.

   빌드도 통과하고 생성된 CSS 파일에 규칙이 멀쩡히 들어 있어서, 파일만
   보면 정상으로 보인다. 그래서 모듈 CSS(격리 없음)에 둔다.

   [높이는 부모가 줘야 한다]

   `.dxbl-splitter` 가 `height: 100%` 다. 높이가 안 정해진 블록
   (`.jsini-shell__content` 는 `overflow: auto` 인 보통 블록이다) 안에서는
   그 100% 가 **내용만큼**으로 접혀 판 둘이 함께 사라진다. 포털관리의
   `ad-vsplit` 과 같은 방법으로 본문을 세로 flex 로 만들어 남은 높이를 준다
   (admin.css 의 「높이를 부모가 정해야 한다」).
   ---------------------------------------------------------- */

.jsini-shell__content:has(> .pm-query-splitter) {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* 조회부(`CommSch`)와 경고 줄은 제 높이만 갖는다. 안 적으면 flex 기본값
   (`shrink: 1`)으로 눌려 조건 칸이 납작해진다. */
.jsini-shell__content:has(> .pm-query-splitter) > *:not(.pm-query-splitter) {
  flex: 0 0 auto;
}

.jsini-shell__content > .pm-query-splitter {
  flex: 1;
  min-height: 0;
}

/* 판 안쪽 여백을 걷어낸다. 규칙과 다투지 않고 **그 규칙이 읽는 변수**를
   덮는다 — 크기 모드마다 값이 달라(`dxbl-sm` · `dxbl-lg`) 그쪽을 겨누면
   서랍에서 크기를 바꿀 때만 여백이 되돌아온다. */
.pm-query-splitter.dxbl-splitter {
  --dxbl-splitter-pane-padding-x: 0;
  --dxbl-splitter-pane-padding-y: 0;
}

/* 판은 기본이 `min-height: fit-content` 라 **안쪽 내용보다 작아지지 않는다.**
   결과가 30줄이면 아래 판이 그만큼 부풀어 분할판 밖으로 나간다. */
.pm-query-splitter > .dxbl-splitter-pane {
  min-height: 0;
}

.pm-query-splitter > .dxbl-splitter-pane > .dxbl-splitter-pane-content {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}

/* 위 판 — 자리표시 한 줄(`pm-editor__hint`)은 편집기 **밖**에 있다.
   그래서 편집기에 `height: 100%` 를 주면 딱 그 줄만큼 판을 넘는다. */
.pm-query {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

.pm-query > .pm-editor {
  flex: 1;
  min-height: 0;
}

/* 아래 판 — `CommCont` 가 판 높이를 받고 표가 그 안을 채운다.
   본문 규칙(`.jsini-shell__content:has(> .commcont)`)은 `>` 로 겨누므로
   판 **안쪽**까지 오지 않는다. 같은 일을 이 판에 대해 적는다.
   표 자신은 app.css 의 `.commcont__body > .commgrd` 가 이미 채워 준다. */
.pm-query-splitter .commcont {
  flex: 1;
  min-height: 0;
}

.pm-query-splitter .commcont > .commcont__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

/* ── 좌우 분할 (`pm-split`) ───────────────────────────────────

   포털관리의 `ad-split` 과 같은 구실이다. 옮겨 쓰지 않는 이유는 그쪽이
   포털관리 모듈의 CSS 라서다 — **두 모듈이 쓰면 복제, 세 번째부터 승격**
   (web/CLAUDE.md). 세 번째 모듈이 필요해지면 `app.css` 로 올린다.

   판 둘은 `CommCont` 여야 한다. 높이를 채우는 규칙이 `.commcont` 를
   겨누므로 맨 `<div>` 로 두면 표가 판 높이를 못 받는다.
   ---------------------------------------------------------- */

.pm-split {
  display: grid;
  grid-template-columns: 1fr;

  /* 두 판이 붙어 있으면 어디까지가 왼쪽인지 눈이 먼저 안 가른다.
     본문 여백(24px)과 같은 값을 주어 판 사이도 같은 리듬으로 읽히게 한다. */
  gap: var(--jsini-sp-2xl);
  align-items: start;
}

@media (width >= 62rem) {
  .pm-split { grid-template-columns: minmax(320px, 34%) 1fr; }
}

.pm-split__side,
.pm-split__main {
  min-width: 0;
}

/* 자료 판이 둘 나란히 설 때는 위아래 간격을 격자가 정한다 —
   공통 규칙(`.commcont + .commcont`)은 쌓인 경우를 본 것이라 여기서 끈다. */
.pm-split > .commcont + .commcont {
  margin-top: 0;
}

/* ── 끌 수 있는 좌우 분할 (`pm-splitpane` · `SplitPane.razor`) ──

   위의 `pm-split` 은 폭이 고정된 격자다. 이쪽은 가운데 손잡이를 끌어 폭을
   바꾸는 `DxSplitter` 이고, 마스터-디테일 화면 셋이 쓴다
   (테이블·컬럼 설명 관리 · DB 도구 · Glue 추적).

   [이름이 `pm-split` 이면 안 된다 — 실제로 밟았다]

   처음엔 `CssClass="pm-split"` 로 넘겼다. 그러면 위의 격자 규칙이
   `.dxbl-splitter` 의 `display: flex` 를 **같은 자릿수로, 더 뒤에서** 덮는다
   (모듈 CSS 가 테마 CSS 뒤에 꽂힌다). 판 둘과 손잡이가 격자 칸 셋이 되고
   `align-items: start` 때문에 높이가 내용만큼으로 접혀 **표가 통째로
   사라졌다.** 오류는 없고 조건줄만 남아서, 증상이 「그리드가 안 보인다」
   하나뿐이라 원인이 클래스 이름으로 보이지 않는다.

   [규칙을 격리 CSS 에 두지 않는다]

   위 `pm-query-splitter` 머리말과 같은 이유다 — 범위 표시(`b-xxxxx`)가
   `<DxSplitter>` 에는 안 붙어서, `CssClass` 로 넘긴 이름도 `::deep` 로 적은
   판 안쪽도 **아무것도 겨누지 못한다.** 생성된 CSS 에는 규칙이 멀쩡히 들어
   있으므로 파일만 보면 정상으로 보인다.

   [높이는 두 갈래로 준다]

   `.dxbl-splitter` 가 `height: 100%` 라 부모가 높이를 정해 줘야 한다.
   본문 바로 아래에 놓이면 남은 높이를 다 먹이고(`:has`), 탭 안처럼 그럴 수
   없는 자리에서는 어림값을 쓴다. 겉의 `<div>` 가 그 높이를 받는 자리다.
   ---------------------------------------------------------- */

.pm-splitpane {
  /* 탭 안처럼 부모가 높이를 안 주는 자리의 바닥값. 아래 `:has` 가 걸리는
     자리에서는 `flex: 1` 이 이것을 대신한다. */
  height: 62vh;
  min-height: 320px;
}

.jsini-shell__content:has(> .pm-splitpane) {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* 조회부(`CommSch`)와 경고 줄은 제 높이만 갖는다. 안 적으면 flex 기본값
   (`shrink: 1`)으로 눌려 조건 칸이 납작해진다. */
.jsini-shell__content:has(> .pm-splitpane) > *:not(.pm-splitpane) {
  flex: 0 0 auto;
}

.jsini-shell__content > .pm-splitpane {
  flex: 1;
  height: auto;
  min-height: 0;
}

/* 판 안쪽 여백을 걷어낸다. 규칙과 다투지 않고 **그 규칙이 읽는 변수**를
   덮는다 — 크기 모드마다 값이 달라(`dxbl-sm` · `dxbl-lg`) 그쪽을 겨누면
   서랍에서 크기를 바꿀 때만 여백이 되돌아온다. */
.pm-splitpane__ui.dxbl-splitter {
  --dxbl-splitter-pane-padding-x: 0;
  --dxbl-splitter-pane-padding-y: 0;
}

/* 판은 기본이 `min-height: fit-content` 라 **안쪽 내용보다 작아지지 않는다.**
   표가 서른 줄이면 그만큼 부풀어 분할판 밖으로 나간다. */
.pm-splitpane__ui > .dxbl-splitter-pane {
  min-height: 0;
}

.pm-splitpane__ui > .dxbl-splitter-pane > .dxbl-splitter-pane-content {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}

.pm-splitpane__first,
.pm-splitpane__second {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
  min-height: 0;
  overflow: auto;
}

/* 좌우로 섰을 때만 손잡이 옆에 숨을 준다. 위아래로 접힌 좁은 화면에서
   같은 자리를 벌리면 판이 옆으로 밀린다. */
.dxbl-splitter-horizontal > .dxbl-splitter-pane > .dxbl-splitter-pane-content > .pm-splitpane__first {
  padding-right: var(--jsini-sp-lg);
}

.dxbl-splitter-horizontal > .dxbl-splitter-pane > .dxbl-splitter-pane-content > .pm-splitpane__second {
  padding-left: var(--jsini-sp-lg);
}

/* 판 안의 `pm-tab-body` — 탭 안쪽에 주는 최소 높이(60vh)가 여기서는
   판을 넘기는 값이 된다. 판 높이를 그대로 받게 되돌린다. */
.pm-splitpane__first > .pm-tab-body,
.pm-splitpane__second > .pm-tab-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  padding-top: 0;
}

/* `CommCont` 가 판 높이를 받고 표가 그 안을 채운다. 본문 규칙
   (`.jsini-shell__content:has(> .commcont)`)은 `>` 로 겨누므로 판 안쪽까지
   오지 않는다 — 같은 일을 이 판에 대해 적는다. */
.pm-splitpane .commcont {
  flex: 1;
  min-height: 0;
}

.pm-splitpane .commcont > .commcont__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

/* `CommCont` 없이 표만 놓인 판(테이블·컬럼 설명 관리)도 채워 준다.
   app.css 의 어림값 상한(`--commgrd-max-height`, 58vh)은 판 높이와 무관해서
   판이 그보다 낮으면 표가 판을 넘고, 높으면 아래가 빈다. */
.pm-splitpane .commgrd {
  flex: 1;
  min-height: 0;
}

.pm-splitpane .commgrd > .dxbl-grid {
  height: 100%;
  max-height: none;
}

/* ── 기준 세그먼트 (`pm-axis`) ────────────────────────────────

   참여 배정 보드의 「프로젝트 | 사람」 토글이다. 두 칸짜리 세그먼트를
   DevExpress 가 따로 주지 않아 맨 단추로 그린다 — 테마 서랍의 밝기·크기
   칸(`jsini-theme__segmented`)도 같은 이유로 맨 단추다.

   글자 크기를 `rem` 으로 박지 않는다. 사다리를 쓰지 않으면 이 칸만
   크기 여섯 단계를 안 따라온다(web/CLAUDE.md).

   한 모듈만 쓰는 동안은 여기 둔다. 둘째 모듈이 필요해지면 복제하고,
   셋째부터 `app.css` 로 올린다.
   ---------------------------------------------------------- */

.pm-axis {
  display: inline-flex;
  border: 1px solid var(--jsini-border);
  border-radius: 0.375rem;
  overflow: hidden;
}

.pm-axis__btn {
  appearance: none;
  border: 0;
  background: var(--jsini-bg);
  color: var(--jsini-text-muted);
  font: inherit;
  font-size: var(--jsini-fs-sm);
  padding: 0.3rem 0.9rem;
  cursor: pointer;
}

.pm-axis__btn + .pm-axis__btn {
  border-left: 1px solid var(--jsini-border);
}

.pm-axis__btn:hover {
  background: color-mix(in srgb, currentcolor 7%, transparent);
  color: var(--jsini-text);
}

/* 고른 칸은 **면으로** 칠한다. 테두리를 두르면 그 칸만 키가 커져 세그먼트가
   들썩인다 — 테마 서랍에서 같은 이유로 같은 방법을 쓴다. */
.pm-axis__btn[aria-pressed='true'] {
  background: var(--jsini-accent);
  color: #fff;
  font-weight: 600;
}

.pm-axis__btn:focus-visible {
  outline: 2px solid var(--jsini-accent);
  outline-offset: -2px;
}

/* 좌우 분할의 한쪽을 다시 위아래로 쌓는다 (`pm-stack`).

   프로젝트 DB 등록이 그렇다 — 왼쪽에 접속, 오른쪽 위에 속성, 오른쪽 아래에
   값 편집기. `min-width: 0` 이 있어야 안쪽 표가 칸을 넘지 않는다(격자 항목도
   flex 항목도 기본 최소 폭이 내용 크기다). */
.pm-stack {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  min-width: 0;
}

/* 쌓인 판 사이 간격은 위 `gap` 이 정한다 — 공통 규칙과 겹치면 두 배가 된다. */
.pm-stack > .commcont + .commcont {
  margin-top: 0;
}

/* 분할판 안에 쌓였을 때는 **판 높이를 받아야 한다.**

   판(`pm-splitpane__second`)은 세로 flex 인데 이 덩어리에 `flex` 를 안 주면
   높이가 내용만큼으로 잡힌다. 그러면 안쪽 `CommCont` 에 걸린
   `flex: 1; min-height: 0`(위 `pm-splitpane` 절)이 **기준 높이 0 짜리**가
   되어 표와 편집기가 납작하게 접힌다 — 손잡이를 끌어 판을 넓혀도 그대로다. */
.pm-splitpane .pm-stack {
  flex: 1;
  min-height: 0;
}

/* 표 안에서 여러 줄짜리 값을 한 줄로 붙잡는다. 질의 조각이나 도형 JSON 이
   들어오는 칸이라, 그대로 두면 줄 하나가 표를 세로로 늘린다. */
.pm-oneline {
  display: block;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* 진행 현황의 진척 차트. 통계 타일과 표 사이에 놓인다.
   높이는 차트가 직접 갖고(240px) 여기서는 여백만 정한다 — 둘 다 여기서
   정하면 어느 쪽이 이기는지 화면마다 달라진다. */
.pm-chart {
    margin: 0.75rem 0;
}

/* 표 안에서 바로 고치는 칸(테이블 관리의 설명). 칸을 꽉 채워야 옆 칸과
   높이가 맞는다 — 기본 폭으로 두면 줄 높이가 칸마다 달라 보인다. */
.pm-cell-edit {
    width: 100%;
}

/* 코드 편집기를 받는 동안. 자리를 지켜야 판이 무너지지 않는다 —
   편집기가 뜨면서 화면이 아래로 밀리는 것을 막는다. */
.pm-editor__loading {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    color: var(--jsini-text-muted, #6b7280);
    font-size: var(--jsini-fs-sm);
}


/* ── ERD 상자 (`.pm-erd`) ─────────────────────────────────────

   maxgraph 가 그리는 것은 **네모와 테두리**뿐이고, 속은 HTML 라벨이라
   여기 CSS 가 정한다. 그래서 테마(글자 크기·색)를 그대로 따라간다.

   높이는 JS 가 잰다(`HEAD_H` · `FIELD_LINE_H`). **여기 값을 고치면 그쪽도
   같이 고친다** — 어긋나면 칸이 상자 밖으로 나가거나 아래가 휑해진다.
   ---------------------------------------------------------- */

.pm-erd {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  font-size: 11px;
  line-height: 1.35;
  text-align: left;
}

/* 칸을 안 펼친 상자 — **이름만 가운데.**

   띠와 아래 선은 「여기서부터 칸 목록」이라는 표시다. 목록이 없는데 그대로
   두면 선 아래가 빈자리로 보여서, 「컬럼 보기」를 껐을 때 상자마다 공백이
   생겼다. 높이는 JS 가 `PLAIN_H` 로 줄인다 — **두 값을 함께 고친다.** */
.pm-erd--plain {
  justify-content: center;
}

.pm-erd--plain .pm-erd__head {
  padding: 2px 8px;
  background: none;
  border-bottom: 0;
  text-align: center;
}

/* 머리띠 — 이름과 설명. 표 하나를 멀리서도 알아보게 하는 자리다. */
.pm-erd__head {
  padding: 4px 8px;
  background: #eef3f9;
  border-bottom: 1px solid #d9d9d9;
}

.pm-erd__title {
  font-weight: 700;
  color: #1f3a57;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.pm-erd__desc {
  color: #6b7f93;
  font-size: 10px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.pm-erd__body {
  padding: 3px 0;
}

/* 칸 한 줄 — 왼쪽에 이름, 오른쪽에 자료형. 자료형이 길어도 이름을 밀지 않는다. */
.pm-erd__row {
  display: flex;
  gap: 4px;
  align-items: baseline;
  padding: 0 8px;
  height: 18px;
}

.pm-erd__key {
  width: 8px;
  color: #c79a1f;
  font-size: 9px;
}

.pm-erd__name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pm-erd__type {
  color: #8c8c8c;
  font-size: 10px;
  max-width: 45%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 열쇠 칸은 굵게 — 표를 이어 보는 눈이 제일 먼저 찾는 줄이다. */
.pm-erd__row--pk .pm-erd__name { font-weight: 700; }

/* ── 도구상자 (`.pm-palette`) ─────────────────────────────────

   그림 위가 아니라 **조회 영역 아래 한 줄**로 둔다. 캔버스에 겹쳐 띄우면
   도형을 그 자리에 놓을 수 없고, 좁은 화면에서 그림을 덮는다.
   ---------------------------------------------------------- */

.pm-palette {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  align-items: center;
  margin-bottom: 0.5rem;
}

.pm-palette__label {
  margin-right: 0.25rem;
  color: var(--jsini-text-muted, #6b7280);
  font-size: var(--jsini-fs-sm);
}


/* ── 도구상자 (`.pm-tools`) ───────────────────────────────────

   왼쪽에 세우고 **핀으로 두 가지 상태**를 만든다.

     꽂힘(기본)  그림 **옆**에 자리를 차지한다 — 그림을 덮지 않는다.
     뺌          그림 **위에** 뜬다 — 좁은 화면에서 그림을 넓게 쓰고 싶을 때.

   숨기면 손잡이만 남긴다. 완전히 없애면 **다시 여는 길이 안 보인다.**
   ---------------------------------------------------------- */

.pm-canvas {
  position: relative;
  display: flex;
  gap: 0.5rem;
  align-items: stretch;
}

/* 꽂혀 있을 때만 그림이 자리를 나눈다. 떠 있을 때는 그림이 폭을 다 쓴다. */
.pm-canvas > .pm-diagram-host {
  flex: 1;
  min-width: 0;
}

.pm-tools {
  display: flex;
  flex-direction: column;
  width: 210px;
  max-height: min(70vh, 760px);
  border: 1px solid var(--jsini-border);
  border-radius: 0.25rem;
  background: var(--jsini-surface);
  overflow: hidden;
}

/* 핀을 빼면 그림 위에 뜬다. 그림자를 줘야 위에 떠 있는 것으로 읽힌다. */
.pm-tools--float {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
  z-index: 3;
  box-shadow: 0 2px 10px rgb(0 0 0 / 18%);
}

.pm-tools__head {
  display: flex;
  gap: 0.25rem;
  align-items: center;
  padding: 0.25rem 0.5rem;
  border-bottom: 1px solid var(--jsini-border);
}

.pm-tools__title {
  flex: 1;
  font-size: var(--jsini-fs-sm);
  font-weight: 600;
}

.pm-tools__body {
  flex: 1;
  min-height: 0;      /* 안 주면 목록이 길 때 판을 밀고 나간다 */
  overflow-y: auto;
  padding: 0.4rem 0.5rem 0.6rem;
}

.pm-tools__group + .pm-tools__group {
  margin-top: 0.35rem;
}

/* 묶음 머리 — 누르면 접히고 펴진다(`<details>` 라 브라우저가 한다). */
.pm-tools__group-title {
  display: flex;
  gap: 0.3rem;
  align-items: center;
  padding: 0.2rem 0;
  color: var(--jsini-text-muted, #6b7280);
  font-size: var(--jsini-fs-2xs);
  font-weight: 600;
  cursor: pointer;
  user-select: none;
}

.pm-tools__group-title::-webkit-details-marker {
  display: none;      /* 기본 삼각형 대신 아래 것을 쓴다 */
}

.pm-tools__group-title::before {
  content: "▸";
  font-size: 0.7em;
  transition: transform 0.12s ease;
}

.pm-tools__group[open] > .pm-tools__group-title::before {
  transform: rotate(90deg);
}

/* 묶음 안의 도구 수. 접힌 채로도 어디에 몇 개가 있는지 보인다. */
.pm-tools__count {
  margin-left: auto;
  padding: 0 0.25rem;
  border-radius: 0.5rem;
  background: color-mix(in srgb, currentColor 12%, transparent);
  font-size: 0.9em;
  font-weight: 400;
}

.pm-tools__items {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
}

/* ── 도구 한 칸 ──────────────────────────────────────────────

   **모양을 실제로 그려 보여 준다.** 이름만 적으면 「Or」·「Card」 가 무슨
   모양인지 알 수 없다. 그 그림은 maxgraph 가 그린 것을 그대로 끼운 것이라
   캔버스에 놓았을 때와 같은 모양이다.

   칸은 **모두 같은 크기**다. 비율은 `viewBox` 가 지키므로 납작한 화살표도
   정사각 칸 안에서 제 비율로 눕는다 — 칸 크기가 제각각이면 목록이 들쭉날쭉해
   눈이 훑지 못한다.
   ---------------------------------------------------------- */

.pm-tool {
  padding: 0.2rem 0.45rem;
  border: 1px solid var(--jsini-border);
  border-radius: 0.25rem;
  background: var(--jsini-surface);
  color: inherit;
  font-size: var(--jsini-fs-2xs);
  line-height: 1.5;
  cursor: pointer;
}

.pm-tool:hover {
  background: color-mix(in srgb, currentColor 8%, transparent);
}

/* 머리띠의 아이콘 단추. 글자를 쓰면 제목이 밀린다. */
.pm-tools__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 0.25rem;
  background: none;
  color: var(--jsini-text-muted, #6b7280);
  cursor: pointer;
}

.pm-tools__icon:hover {
  background: color-mix(in srgb, currentColor 10%, transparent);
}

/* 핀이 꽂혀 있는 상태를 색으로 알린다 — 아이콘이 같으므로 이것뿐이다. */
.pm-tools__icon--on {
  border-color: var(--jsini-border);
  color: var(--jsini-accent-text, #0f6cbd);
}

.pm-tools__icon [class^="jsini-icon-"] {
  width: 0.95rem;
  height: 0.95rem;
}

/* 숨겼을 때 남는 손잡이.

   **자리를 차지하지 않는다**(`position: absolute`). 흐름에 두면 숨긴 뒤에도
   폭을 먹어 그림이 그만큼 좁아진다 — 숨기는 뜻이 없어진다. 그림 위에 떠서
   맨 위 층에 있고, 캔버스는 폭을 다 쓴다. */
.pm-tools-handle {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
  z-index: 4;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  padding: 0;
  border: 1px solid var(--jsini-border);
  border-radius: 0.25rem;
  background: var(--jsini-surface);
  color: var(--jsini-text-muted, #6b7280);
  box-shadow: 0 1px 4px rgb(0 0 0 / 15%);
  cursor: pointer;
}

.pm-tools-handle:hover {
  color: var(--jsini-accent-text, #0f6cbd);
}

.pm-tools-handle [class^="jsini-icon-"] {
  width: 1.1rem;
  height: 1.1rem;
}

/* 눈에는 안 보이고 화면 낭독기에는 읽히는 글자. 아이콘만 있는 단추에 붙인다. */
/* ── 저장 안 한 변경 (`.pm-save--dirty`) ──────────────────────

   그 사실을 **눌러야 할 단추 자신이** 들고 있다. 안내 줄은 자리를 먹고
   표를 위아래로 밀고, 토스트는 7초면 걷혀 뒤에 온 사람에게 단서가 없다.

   테두리를 두르지 않고 **바깥으로 고리**를 둔다(`box-shadow`) — 테두리는
   단추 크기를 바꿔서 옆 단추들이 밀린다. 글자의 `•` 는 색을 못 보는 눈과
   흑백 화면을 위한 것이다(색 하나에만 기대지 않는다).
   ---------------------------------------------------------- */

.pm-save--dirty {
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--jsini-primary, #3f7ad6) 45%, transparent);
}

/* ── 그림 바탕 (격자·점) ──────────────────────────────────────

   무늬만 여기서 정하고 **칸 크기와 시작점은 JS 가 정한다**
   (`diagram-viewer.js` 의 `paintBackground` — 배율과 이동을 따라가야 한다).
   색을 CSS 가 쥐고 있어야 테마 스물둘을 그대로 따라온다.

   `background-size` 가 한 칸이므로 여기서는 **한 칸에 그릴 것**만 적는다 —
   격자는 왼쪽·위 변, 점은 왼쪽 위 모서리에서 `1.5px` 안쪽(그 값이 JS 의
   `DOT_INSET` 이다. **두 곳이 같아야 점이 붙는 자리에 찍힌다**).
   ---------------------------------------------------------- */

.pm-diagram--grid,
.pm-diagram--dots {
  /* 바탕이 진하면 그림이 묻힌다. 테두리 색을 옅게 섞어 쓴다. */
  --pm-grid-ink: color-mix(in srgb, var(--jsini-border) 80%, transparent);
}

.pm-diagram--grid {
  background-image:
    linear-gradient(to right, var(--pm-grid-ink) 1px, transparent 1px),
    linear-gradient(to bottom, var(--pm-grid-ink) 1px, transparent 1px);
}

.pm-diagram--dots {
  /* 점은 격자보다 눈에 덜 띄어서 옅게 섞지 않고 테두리 색을 그대로 쓴다.
     **100% 를 넘겨 진하게 만들 수는 없다** — `color-mix` 의 비율이 0~100%
     밖이면 그 색이 통째로 무효가 되고, 그러면 `background-image` 선언 하나가
     조용히 버려져 **점이 아예 안 찍힌다**(브라우저에서 실제로 그랬다). */
  --pm-grid-ink: var(--jsini-border);

  background-image:
    radial-gradient(circle at 1.5px 1.5px, var(--pm-grid-ink) 1.4px, transparent 1.5px);
}

/* ── 미니맵 (`.pm-minimap`) ───────────────────────────────────

   그림이 화면보다 커지면 **지금 어디를 보고 있는지** 알 길이 그림 자체밖에
   없다. 켜면 오른쪽 아래에 전체가 뜨고, 그 안의 네모를 끌어 옮겨 다닌다.
   그리는 것은 maxgraph 의 `Outline` 이다(`diagram-viewer.js`).

   **자리는 부모(`.pm-diagram-host`)가 잡는다** — 그 칸이 `position: relative`
   라 여기서 `absolute` 한 줄이면 된다. 도구상자(핀 뺀 상태)는 왼쪽 위라
   서로 겹치지 않는다.

   층은 **1** 이다. 「불러오는 중」 덮개가 2 라, 물어보는 동안에는 미니맵도
   함께 덮여야 한다 — 그 사이 그림은 옛 그대로이고 미니맵도 마찬가지다.
   ---------------------------------------------------------- */

/* 켜기 전에는 자리를 차지하지 않는다. 빈 칸이라 값도 치르지 않는다. */
.pm-minimap {
  display: none;
}

.pm-minimap--on {
  position: absolute;
  right: 0.5rem;
  bottom: 0.5rem;
  z-index: 1;
  display: block;
  width: 220px;
  height: 160px;
  border: 1px solid var(--jsini-border);
  border-radius: 0.25rem;
  background: var(--jsini-surface);
  box-shadow: 0 2px 10px rgb(0 0 0 / 18%);
  overflow: hidden;
}

/* 좁은 화면에서는 그림을 가린다. 그림이 안 보이면 미니맵도 쓸 데가 없다. */
@media (max-width: 767px) {
  .pm-minimap--on {
    width: 150px;
    height: 110px;
  }
}

/* 도구상자 머리의 미니맵 단추 그림 — 테두리 안에 「보는 자리」 네모.

   `app.css` 의 `jsini-icon-*` 묶음에 없는 그림이라 여기 둔다(모듈 하나만
   쓰는 동안은 그 모듈 CSS 에 두는 규칙). 재는 법과 색은 그쪽과 같게 맞춘다 —
   `currentColor` 로 칠하므로 테마 스물둘을 그대로 따라온다. */
.pm-icon-minimap {
  display: inline-block;
  width: var(--jsini-icon-size);
  height: var(--jsini-icon-size);
  vertical-align: middle;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M1 2.5h14v11H1zm1.6 1.6v7.8h10.8V4.1z'/%3E%3Cpath d='M8.2 7.4h4.6v3.4H8.2z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M1 2.5h14v11H1zm1.6 1.6v7.8h10.8V4.1z'/%3E%3Cpath d='M8.2 7.4h4.6v3.4H8.2z'/%3E%3C/svg%3E");
}

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


/* 도구상자의 찾는 칸. 목록이 백여든 개라 없으면 굴리다 지친다. */
/* 바탕 고르개(없음·격자·점). 테마 서랍의 칸 묶음과 같은 모양이다 —
   셋 중 하나라 고른 것을 **면으로** 칠한다(테두리를 두르면 그 칸만 키가
   커져 줄이 들썩인다). */
.pm-tools__seg {
  display: flex;
  gap: 0.2rem;
  align-items: center;
  padding: 0.35rem 0.5rem;
  border-bottom: 1px solid var(--jsini-border);
}

.pm-tools__seg-label {
  margin-right: 0.1rem;
  color: var(--jsini-text-muted, #6b7280);
  font-size: var(--jsini-fs-2xs);
}

.pm-tools__seg-btn {
  flex: 1;
  padding: 0.15rem 0.2rem;
  border: 1px solid var(--jsini-border);
  border-radius: 0.25rem;
  background: var(--jsini-surface);
  color: inherit;
  font-size: var(--jsini-fs-2xs);
  cursor: pointer;
}

.pm-tools__seg-btn:hover {
  background: color-mix(in srgb, currentColor 8%, var(--jsini-surface));
}

.pm-tools__seg-btn--on {
  border-color: var(--jsini-primary, #3f7ad6);
  background: color-mix(in srgb, var(--jsini-primary, #3f7ad6) 14%, var(--jsini-surface));
  font-weight: 600;
}

.pm-tools__filter {
  padding: 0.35rem 0.5rem;
  border-bottom: 1px solid var(--jsini-border);
}

.pm-tools__search {
  width: 100%;
  padding: 0.2rem 0.4rem;
  border: 1px solid var(--jsini-border);
  border-radius: 0.25rem;
  background: var(--jsini-surface);
  color: inherit;
  font-size: var(--jsini-fs-2xs);
}

.pm-tools__empty {
  margin: 0.4rem 0;
  color: var(--jsini-text-muted, #6b7280);
  font-size: var(--jsini-fs-2xs);
}


/* 모양 칸. 글자 칸(선 모양)과 나란히 서므로 높이를 맞춘다.

   **작을수록 한 화면에 많이 든다.** 도형이 백일흔한 개라 한 줄에 다섯 개가
   들어가면 굴리는 길이가 눈에 띄게 줄어든다. 무엇인지는 마우스를 올렸을 때
   뜨는 큰 그림이 말해 주므로 칸은 알아볼 수 있을 만큼만 크면 된다. */
.pm-tool--shape {
  position: relative;   /* 덧붙이는 말의 기준 */
  width: 1.85rem;
  height: 1.85rem;
  padding: 0.1rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: grab;         /* 끌어다 놓을 수 있다는 표시 */
}

.pm-tool--shape:active {
  cursor: grabbing;
}

.pm-tool__art {
  width: 100%;
  height: 100%;
  overflow: visible;    /* 테두리가 잘리지 않게 */
  pointer-events: none; /* 그림이 단추의 클릭을 먹지 않게 */
  font-size: 0.55rem;   /* 미리보기가 없을 때의 글자 */
}

/* 덧붙이는 말 — 큰 그림과 이름.

   `title` 을 쓰지 않는 이유는 **그림을 담을 수 없어서**다. 뜨는 자리는
   도구 오른쪽이고, 도구상자가 왼쪽에 있으므로 그림 위로 나온다. */
.pm-tool__tip {
  position: absolute;
  top: 50%;
  left: calc(100% + 0.4rem);
  z-index: 6;
  display: none;
  flex-direction: column;
  gap: 0.2rem;
  align-items: center;
  width: max-content;
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--jsini-border);
  border-radius: 0.25rem;
  background: var(--jsini-surface);
  box-shadow: 0 2px 10px rgb(0 0 0 / 18%);
  transform: translateY(-50%);
  pointer-events: none;
}

.pm-tool--shape:hover .pm-tool__tip,
.pm-tool--shape:focus-visible .pm-tool__tip {
  display: flex;
}

/* 큰 그림도 **같은 상자**다 — 비율만 viewBox 가 지킨다. */
.pm-tool__tip-art {
  width: 5.5rem;
  height: 5.5rem;
  overflow: visible;
}

.pm-tool__tip-name {
  max-width: 9rem;
  color: inherit;
  font-size: var(--jsini-fs-2xs);
  text-align: center;
  word-break: break-word;
}


/* ── 마인드맵 (`.pm-mind`) ───────────────────────────────────────

   그림 자리는 ERD 쪽(`.pm-diagram-host`)과 거의 같다. 따로 둔 까닭은 둘뿐이다 —
   **도구상자가 없어** 옆자리를 나눌 일이 없고(마인드맵은 마디를 자판으로
   붙인다), 캔버스에 **자판 초점**이 가야 해서 그 표시를 보여 줘야 한다.
   ---------------------------------------------------------- */

.pm-mind-host {
  position: relative;   /* 「불러오는 중」 덮개의 기준 */
  width: 100%;
  height: min(70vh, 760px);
  border: 1px solid var(--jsini-border);
  border-radius: 0.25rem;
  overflow: hidden;
}

.pm-mind {
  width: 100%;
  height: 100%;

  /* 빈 자리를 끌면 그림이 움직인다(`useLeftButtonForPanning`). 커서가 그렇게
     말해 주지 않으면 **끌 수 있다는 것을 알 길이 없다** — 상자는 못 끄는
     그림이라 더 그렇다. */
  cursor: grab;
}

.pm-mind:active { cursor: grabbing; }

/* 캔버스가 자판을 받는다(Tab·Enter·F2·Delete). `tabindex` 는 JS 가 달고,
   초점이 어디 있는지는 여기서 보인다 — 안 보이면 「단축키가 안 먹는다」가
   되고 실제 원인은 초점이 딴 데 있는 것이다.

   **`:focus-visible` 이 아니라 `:focus` 다.** 캔버스를 마우스로 누르면 JS 가
   초점을 가져오는데(`mind-map.js` 의 `focusCanvas`), 그렇게 옮긴 초점은
   `:focus-visible` 에 걸리지 않는다 — 브라우저가 「자판으로 온 초점」에만
   테를 그리기 때문이다. 그러면 **자판이 이제 여기로 온다는 것을 알릴 길이
   없어진다.** 이 화면에서는 그 표시가 곧 단축키가 산다는 뜻이라 늘 그린다. */
.pm-mind:focus {
  outline: 2px solid var(--jsini-primary, #3f7ad6);
  outline-offset: -2px;
}

/* ── mermaid 글 칸 ───────────────────────────────────────────

   접어 둔다. 그림으로 그리는 사람에게는 안 보이는 편이 낫다.
   ---------------------------------------------------------- */

.pm-mind-text {
  margin-top: 0.6rem;
}

.pm-mind-text__toggle {
  padding: 0.2rem 0;
  border: 0;
  background: none;
  color: var(--jsini-text-muted, #6b7280);
  font-size: var(--jsini-fs-sm);
  cursor: pointer;
}

.pm-mind-text__toggle:hover { color: var(--jsini-text, #262626); }

/* 글은 고정폭이라야 들여쓰기가 층으로 보인다 — 그것이 mermaid 가 층을 정하는
   방법이라, 비례폭으로 두면 잘못 들여쓴 줄을 눈으로 찾을 수 없다. */
.pm-mind-text__area textarea {
  font-family: var(--jsini-font-mono, ui-monospace, "Cascadia Mono", "D2Coding", monospace);
  font-size: var(--jsini-fs-sm);
  white-space: pre;
}

/* ── 일정표의 주·일 보기 (`DxScheduler`) ─────────────────────

   `DxScheduler` 는 자기 키를 부모에서 받는다. 부모가 키를 주지 않으면
   **달력이 한 주 높이로 납작해진다.** 최소 키를 준다.

   월 보기는 여기 없다 — 우리가 그린다(`pm-gc-month`). 남은 것은 시간
   격자가 필요한 주·일뿐이다.

   화면 옆 CSS(`ProjectScheduler.razor.css`)로 두지 않은 이유는, 이 화면의
   마크업이 부품 태그뿐이라 **범위 표시를 달 요소가 없기 때문이다** —
   `::deep` 이 걸 조상이 없어 규칙이 조용히 안 먹는다.
   ---------------------------------------------------------- */

.pm-sched {
  min-height: 620px;
}

/* ── 다중선택 고르개(CodeSelect Multiple) ────────────────────

   다중 고르개는 **자기 너비를 스스로 정하지 않는다.** 아무것도 안 고른
   상태에서는 내용이 없어 12px 로 쪼그라들고, 조건줄에서는 그것이 **누를
   수도 없는 실선 한 줄**로 보인다(실제로 그랬다). 단일 고르개(`DxComboBox`)
   는 자기 기본 너비가 있어서 이 문제가 없다.

   화면 옆 CSS 로 두지 않는다 — `CodeSelect` 의 마크업이 부품 태그뿐이라
   `::deep` 이 걸 조상이 없다(`.pm-sched` 와 같은 사정).
   ---------------------------------------------------------- */

.pm-code-select--multi {
  min-width: 220px;
  max-width: 100%;
}

/* 좁은 화면에서는 최소 너비를 푼다. 안 그러면 조건줄이 화면 밖으로 나간다. */
@media (max-width: 767px) {
  .pm-code-select--multi {
    min-width: 0;
  }
}

/* 펼친 체크 목록. 코드가 스물 넘는 묶음(예: HOMEWORK)에서도 판이 화면을
   넘지 않게 키를 묶고 안쪽만 구르게 한다 — 넘치면 아래쪽 항목을 **체크할
   방법이 없다.** */
.pm-code-checks {
  max-height: 280px;
  min-width: 200px;
}

/* ── AI 작업 (`pm-ai-*`) ──────────────────────────────────────

   **상태 배지에 색을 새로 만들지 않는다.** 공통 배지(`jsini-badge--on` ·
   `--off` · `--warn` · `--err`)를 그대로 쓴다.

   처음에는 `--bs-*` 변수로 바탕색을 주는 배지를 따로 만들었는데, 이 포털에는
   **부트스트랩 변수가 아예 없다.** 그래서 대체값(밝은 파스텔)이 늘 쓰였고
   어두운 테마에서 흰 칩이 떠 있었다 — 오류는 안 나고 보기만 나쁘다.
   ---------------------------------------------------------- */

/* 요약과 조회 조건은 휴대폰에서만 접는다. 넓은 화면에서는 `display: contents`
   로 기존의 세로 flex 구조를 그대로 유지해, 접이식 껍데기가 데스크톱 높이
   계산에 끼어들지 않게 한다.

   **`display: contents` 가 여는 일까지 해 주지는 않는다.** 크롬 131 부터
   `<details>` 의 속은 `::details-content` 에 담기고 닫혀 있는 동안
   `content-visibility: hidden` 이 걸린다 — 안쪽의 `display` 로는 안 걷힌다.
   그래서 **닫힌 채로 두면 넓은 화면에서도 안 보인다.** 여는 것은 화면이
   한다(`open="@(!_isPhone)"`, 같은 767px 경계). */
.pm-ai-overview__body {
  display: contents;
}

/* ── 여닫는 띠는 **넓은 화면에도 둔다** ──────────────────────

   한동안 이 띠를 데스크탑에서 숨겼다(`display: none`). 그런데 요약과 조회
   조건을 늘 펴 두면 그만큼 아래 두 판이 낮아지고, 오른쪽 「지시」 탭은
   **편집기가 남은 높이를 받아 쓰는 자리**라 그 손해가 고스란히 편집기로 간다
   (1600×1000 에서 220px 가량).

   그래서 숨기는 대신 **접을 수 있게 둔다.** 펴 둔 것이 기본이고(조회가 이
   화면에서 제일 자주 쓰는 물건이다), 글을 길게 쓸 때는 띠를 눌러 접는다.
   띠 자체는 한 줄이라 얇다. */
.pm-ai-overview__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--jsini-sp-sm);
  margin-bottom: var(--jsini-sp-sm);
  padding: 0.4rem 0.6rem;
  border-radius: var(--jsini-radius-md);
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-sm);
  font-weight: 600;
  list-style: none;
  cursor: pointer;
}

.pm-ai-overview__summary:hover {
  background: var(--jsini-surface-hover, rgba(127, 127, 127, 0.12));
}

.pm-ai-overview__summary::-webkit-details-marker {
  display: none;
}

.pm-ai-overview__chevron {
  transition: transform 0.2s;
}

.pm-ai-overview[open] .pm-ai-overview__chevron {
  transform: rotate(180deg);
}

@media (min-width: 768px) {
  /* 데스크톱은 접이식 껍데기가 높이 계산에 끼어들지 않게 속판의 상자를 지운다. */
  .pm-ai-overview > .pm-ai-overview__body {
    display: contents !important;
  }
}

/* 표에서 「없음」을 뜻하는 자리. 배지를 쓰면 없는 것이 있는 것처럼 보인다. */
.pm-ai-dim { color: var(--jsini-text-muted); }

/* 편집기와 아래 단추 줄 사이. 붙어 있으면 편집기 테두리와 단추가 한 줄로
   보여 어디까지가 입력칸인지 흐려진다(`pm-query` 와 같은 이유다). */
/* ── 지시 탭 · 실행 탭 ────────────────────────────────────────

   오른쪽 판을 탭 둘로 갈랐다. 한 칸에 폼·편집기·이력·결과·로그를 다 쌓으면
   **둘 다 가운데를 원하는데 자리가 하나**라, 도는 것을 보려면 편집기를 지나
   한참 내려가야 했다.

   높이를 픽셀로 박지 않고 화면을 따라가게 둔다 — 창을 키우면 편집기와
   로그가 같이 커진다. 모니터 크기를 우리가 정할 수는 없다.
   ---------------------------------------------------------- */

.pm-ai-write,
.pm-ai-watch { padding-top: var(--jsini-sp-lg); }

/* ── 설정 줄 (`pm-editform` · `pm-ai-opts`) ───────────────────

   한동안 칸 여덟이 8·4 / 3·3·6 / 4·4·4 로 **세 줄에 제각각 걸쳐** 있었다.
   줄마다 칸 폭이 달라 왼쪽 끝만 겨우 맞았고, 체크칸 둘은 「끝나면」·「알림」
   이라는 라벨을 이고 서 있어서 무엇이 이름이고 무엇이 값인지 눈이 매번
   다시 골라야 했다.

   이제 성격으로 가른다 — **무엇을 시키나**(폼)와 **끝난 뒤에 무엇을
   하나**(`pm-ai-opts`). 뒤엣것은 폼 밖의 한 줄이라 라벨이 아예 없다.

   줄 높이(32px)는 건드리지 않는다 — 칸을 키우면 설정이 화면을 더 먹고,
   이 화면에서 가운데를 가져야 하는 것은 **지시문**이다.
   ---------------------------------------------------------- */

.pm-editform .dxbl-fl-item {
  margin-bottom: var(--jsini-sp-md);
}

/* 마지막 줄 밑은 아래 옵션 줄의 여백이 맡는다. 겹치면 그 사이만 휑하다. */
.pm-editform .dxbl-fl-item:nth-last-child(-n + 4) {
  margin-bottom: 0;
}

/* 끝난 뒤의 일. 체크 셋과 알림 칸 둘이 **한 줄**이다 —
   좁아지면 접히되 순서(켠다 → 누구에게 → 언제)는 지킨다. */
.pm-ai-opts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--jsini-sp-sm) var(--jsini-sp-lg);
  align-items: center;
  margin-top: var(--jsini-sp-md);
}

/* 「저장소에 올리기」와 알림 셋 사이의 선. 성격이 다른 두 묶음이
   같은 간격으로 서 있으면 다섯이 한 줄로 읽힌다. */
.pm-ai-opts__sep {
  width: 1px;
  align-self: stretch;
  min-height: 1.4rem;
  background: var(--jsini-border);
}

/* 칸에 붙는 이름. 폼 라벨(테두리에 얹히는 것)과 달리 **칸 왼쪽에 나란히**
   선다 — 줄 하나에 칸 둘이 들어가야 해서 위에 얹을 자리가 없다. */
.pm-ai-opts__field {
  display: inline-flex;
  min-width: 0;
  align-items: center;
  gap: var(--jsini-sp-sm);
}

.pm-ai-opts__label {
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-sm);
  white-space: nowrap;
}

/* 칸 높이를 위 폼과 맞춘다. 폼 안의 칸은 라벨을 얹느라 키를 받아 두는데
   (`app.css` 의 `--jsini-field-h`) 이 줄은 폼 밖이라 그 규칙이 안 걸린다 —
   그대로 두면 **바로 윗줄보다 눈에 띄게 낮은 칸**이 둘 선다. */
.pm-ai-opts__field .dxbl-text-edit {
  width: 100%;
  min-height: var(--jsini-field-h);
}

/* 「받는 사람」은 메일 주소가 들어가고 「보낼 때」는 고르는 칸이다.
   내용에 맞는 폭을 각자 준다 — 둘이 같은 폭이면 주소가 늘 잘린다.

   **차례(`nth-of-type`)로 가르지 않는다.** 이 줄의 형제에는 `DxCheckBox` 도
   있고 그것이 뿌리에 무슨 태그를 쓰는지는 DevExpress 가 정한다 — `<label>` 을
   쓰는 판이면 번호가 통째로 밀린다. 이름으로 가른다. */
.pm-ai-opts__field--to { flex: 1 1 16rem; }
.pm-ai-opts__field--when { flex: 0 1 12rem; }

/* 편집기 자리. `CodeEditor` 는 `Height` 를 제 상자의 style 에 그대로 넣으므로
   이 판에 실제 높이가 있어야 100% 가 뜻을 갖는다 — Monaco 는 높이를 못 받으면
   5px 로 접힌다(아래 `pm-editor__inner` 머리말).

   **위쪽에 선을 하나 긋는다.** 설정과 지시문은 성격이 다른데(하나는 골라
   두는 값, 하나는 사람이 쓰는 글) 여백만으로는 그 경계가 안 읽혔다.
   선 하나면 눈이 둘을 따로 본다.

   [높이를 재지 않는다 — 남은 자리를 받는다]

   한동안 `clamp(18rem, calc(100vh - 33rem), 60rem)` 이었다. 빼는 33rem 이
   **편집기 위아래에 쌓인 것의 합**(머리줄·탭 줄·조회판·설정 폼·단추 줄·
   본문 여백)이라, 위에 줄 하나만 늘어도 그만큼 단추 줄이 화면 밖으로
   밀렸다. 그런데 이 화면의 안내줄은 상태에 따라 넷까지 나타났다 사라지므로
   **그 값이 맞는 순간이 아예 없었다.**

   이제 지시 탭이 세로 flex 고 편집기가 그 남은 높이를 받는다
   (`pm-ai-page` 머리말). 쌓인 것을 셀 필요가 없다.

   **여기 적는 24rem 은 휴대폰·좁은 창의 값이다.** 거기서는 쪽이 통째로
   구르므로 남은 높이라는 것이 없다 — 데스크탑 값은 아래 `pm-ai-page` 절이
   덮는다. */
.pm-ai-editor {
  height: 24rem;
  margin: var(--jsini-sp-lg) 0;
  padding-top: var(--jsini-sp-lg);
  border-top: 1px solid var(--jsini-border);
}

.pm-ai-actions {
  display: flex;
  flex: 0 0 auto;
  flex-wrap: wrap;
  gap: var(--jsini-sp-sm);
  padding-bottom: var(--jsini-sp-sm);
}

/* 고른 AI 의 한도 한 줄. 폼 바로 아래에 붙는다.

   **값이 없으면 마크업째 안 그린다**(화면 쪽 `@if`). 자리를 늘 차지하게
   두면 빈 줄이 하나 늘어 그만큼 편집기가 낮아진다 — 「빠른 지시」의
   `pm-ask__limit` 와 같은 규칙이고, 거기서는 자판 때문에 더 아팠다. */
.pm-ai-limit {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: var(--jsini-sp-sm);
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
}


/* ── 편집기 머리줄 ───────────────────────────────────────────

   「지시 내용」이라는 이름과 「AI 도우미」 단추가 한 줄에 선다.

   **설정과 지시문을 가르는 선이 이 줄로 올라온다.** 그 선은 원래
   `.pm-ai-editor` 가 이고 있었는데, 그 사이에 줄이 하나 끼면 선이 머리줄
   **아래**에 그어져 이름이 설정 묶음에 붙어 보인다. 선은 「여기부터 글을
   쓰는 자리」를 뜻하므로 이름보다 위여야 한다. */
.pm-ai-editor__bar {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: space-between;
  gap: var(--jsini-sp-sm);
  margin-top: var(--jsini-sp-lg);
  padding-top: var(--jsini-sp-lg);
  border-top: 1px solid var(--jsini-border);
}

.pm-ai-editor__label {
  font-weight: 600;
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
}

/* 선을 머리줄에 넘겨준 편집기. 기본 규칙(위 `.pm-ai-editor`)은 머리줄이
   없는 쓰임을 위해 그대로 둔다. */
.pm-ai-editor__bar + .pm-ai-editor {
  margin-top: var(--jsini-sp-sm);
  padding-top: 0;
  border-top: 0;
}

/* ── AI 도우미 (`AiTaskWriteAssist`) ─────────────────────────

   단추 하나와 그것이 여는 창. **헬프데스크 요청 등록의 같은 단추를 옮겨 온
   것이다**(`hd-ai`) — 업무 모듈끼리는 서로를 참조할 수 없어 스타일도 함께
   복제했다.

   **거기서 흘러내리던 판이 여기서는 창이다.** 이 단추가 선 자리(지시 탭의
   편집기 머리줄)는 창 바닥 가까이라 흘러내린 판이 `dxbl-tabs-content` 의
   `overflow: hidden` 에 잘린다 — 위로 펼쳐도 이번에는 탭 위쪽에 걸린다.
   까닭은 부품 머리말에 재어 둔 값과 함께 적어 두었다. */
.pm-aiassist__btn {
  flex: 0 0 auto;
}

/* **무엇을 기다리는지 적는 줄이다.** 로컬 LLM 은 십 초가 넘는 일이 있어서,
   돌아가는 표시만 있으면 사람이 멈춘 줄 알고 다시 누른다. */
.pm-aiassist__wait,
.pm-aiassist__hint {
  margin: 0;
  color: var(--jsini-text-muted);
}

.pm-aiassist__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* 후보 사이의 가는 선이 「하나씩 고르는 것」을 말해 준다 — 그래서
   「누르면 제목 칸에 들어갑니다」라는 설명 줄을 따로 두지 않는다. */
.pm-aiassist__list > li + li {
  border-top: 1px solid var(--jsini-border);
}

.pm-aiassist__pick {
  display: block;
  width: 100%;
  padding: var(--jsini-sp-sm);
  background: none;
  border: 0;
  border-radius: var(--jsini-radius-md);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.pm-aiassist__pick:hover {
  background: var(--jsini-surface-hover, rgba(127, 127, 127, 0.12));
}

/* 이번 답을 만든 AI. **고르는 칸이 아니라 알려 주는 줄**이라 작고 흐리다. */
.pm-aiassist__model {
  margin: var(--jsini-sp-md) 0 0;
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
}

/* ── 임시저장 목록 창 ────────────────────────────────────────

   `CommGrd` 를 쓰지 않는다 — 조회·정렬·내보내기가 붙을 자료가 아니고,
   한 줄에서 **고르고 버리는 두 가지**만 하면 된다(화면 쪽 머리말). */
.pm-ai-drafts {
  margin: var(--jsini-sp-md) 0 0;
  padding: 0;
  list-style: none;
}

.pm-ai-drafts__empty {
  margin: var(--jsini-sp-md) 0 0;
  color: var(--jsini-text-muted);
}

.pm-ai-drafts__row {
  display: flex;
  align-items: stretch;
  gap: var(--jsini-sp-xs, 0.25rem);
}

.pm-ai-drafts__row + .pm-ai-drafts__row {
  border-top: 1px solid var(--jsini-border);
}

/* 줄 전체가 「불러오기」다. 고르는 일이 버리는 일보다 훨씬 잦다. */
.pm-ai-drafts__pick {
  display: flex;
  flex: 1 1 auto;
  min-width: 0;
  flex-direction: column;
  gap: 0.15rem;
  padding: var(--jsini-sp-sm);
  background: none;
  border: 0;
  border-radius: var(--jsini-radius-md);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.pm-ai-drafts__pick:hover {
  background: var(--jsini-surface-hover, rgba(127, 127, 127, 0.12));
}

/* 제목은 한 줄로 자른다. 임시본의 제목은 본문 첫 줄일 수 있어
   (`DraftTitle`) 그대로 두면 한 줄이 창을 넘는다. */
.pm-ai-drafts__title {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-weight: 600;
}

.pm-ai-drafts__meta {
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
}

.pm-ai-drafts__drop {
  flex: 0 0 auto;
  padding: 0 var(--jsini-sp-sm);
  background: none;
  border: 0;
  border-radius: var(--jsini-radius-md);
  color: var(--jsini-text-muted);
  cursor: pointer;
}

.pm-ai-drafts__drop:hover {
  color: var(--jsini-danger, #dc2626);
  background: var(--jsini-surface-hover, rgba(127, 127, 127, 0.12));
}

/* ── 편집기 높이 (`pm-editor__inner`) ─────────────────────────

   **여기 있으면 안 되는 규칙이다.** 정본은 `CodeEditor.razor.css` 의
   `::deep .pm-editor__inner { height: 100% }` 인데, 셸의 `<head>` 에
   범위 CSS 묶음(`JSini.Web.Shell.styles.css`)을 싣는 줄이 없어서
   **`*.razor.css` 여섯 장이 통째로 안 실린다.**

   그 결과 Monaco 가 자기 상자의 높이를 못 받아 **5px 로 접힌다** —
   테두리만 남고 글이 한 줄도 안 보이는 그 증상이다. 오류는 나지 않는다.

   묶음을 싣는 것이 진짜 고침이고 그것은 셸 한 줄인데, 함께 살아나는
   부품이 여섯이라(SplitPane · DiagramViewer · BizSelect …) 화면
   백몇 개의 모양이 같이 바뀐다. 그 확인을 따로 하기로 하고, 여기서는
   실려 있는 모듈 CSS 로 편집기만 세워 둔다.
   ---------------------------------------------------------- */

.pm-editor {
  position: relative;
}

.pm-editor__inner,
.monaco-editor-container {
  height: 100%;
}

/* ── 편집기 아이콘 줄 (`EditorActions` — 복사 · 자동 줄바꿈) ──────

   Monaco 가 선 자리마다 **같은 아이콘이 같은 모양으로** 선다. 한동안
   `CodeEditor` 안에만 있어서, 비교 편집기를 직접 세우는 로그 번역판
   (`LogTranslateView`)에는 복사 아이콘이 아예 없었다.

   글자를 붙이지 않는다 — 편집기 본문 위에 얹히는 줄이라, 글자가 있으면
   그만큼 코드를 가린다. 무엇을 하는 단추인지는 `title`·`aria-label` 에 있다.

   두 가지로 선다.
     · `pm-edact--float` — 편집기 **안쪽** 오른쪽 위에 떠서 얹힌다.
       얹는 자리(부모)에 `position: relative` 가 있어야 한다.
     · 그냥 `pm-edact` — 글 흐름에 그대로 선다(칸 이름표 오른쪽 끝). */
.pm-edact {
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
  user-select: none;
}

.pm-edact--float {
  position: absolute;
  top: 0.35rem;
  right: 0.5rem;
  z-index: 10;
  padding: 0.15rem 0.25rem;
  border-radius: 6px;
  background: var(--jsini-surface, rgba(255, 255, 255, 0.92));
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 1px solid var(--jsini-border, #e3e5e8);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
  opacity: 0.85;
  transition: opacity 0.15s ease, box-shadow 0.15s ease;
}

/* 떠 있는 줄은 **가만히 있을 때 옅다.** 편집기에 손이 가면 또렷해진다 —
   늘 진하게 두면 오른쪽 위 글자를 늘 가리는 것으로 읽힌다. */
.pm-editor:hover .pm-edact--float,
.pm-edact--float:hover,
.pm-edact--float:focus-within {
  opacity: 1;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
}

/* 아이콘 단추 (줄바꿈 · 복사 공통) */
.pm-edact__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.55rem;
  height: 1.55rem;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 4px;
  background: transparent;
  cursor: pointer;
  color: var(--jsini-text-muted, #6b7280);
  transition: color 0.15s, background 0.15s, border-color 0.15s;
}

.pm-edact__btn:hover {
  color: var(--jsini-text-main, #111827);
  background: var(--jsini-hover-bg, rgba(0, 0, 0, 0.06));
}

/* 줄바꿈 켜짐. 켜고 끈 것이 아이콘 색으로 바로 읽혀야 한다. */
.pm-edact__btn.is-active {
  color: var(--jsini-primary, #0070cc);
  background: rgba(0, 112, 204, 0.12);
  border-color: rgba(0, 112, 204, 0.28);
}

.pm-edact__btn.is-active:hover {
  background: rgba(0, 112, 204, 0.18);
}

/* 복사된 뒤 1.5초. 체크 아이콘과 함께 선다 — **된 때에만** 켠다. */
.pm-edact__btn.is-copied {
  color: #10b981;
  background: rgba(16, 185, 129, 0.12);
  border-color: rgba(16, 185, 129, 0.28);
}

/* 자동 줄바꿈 아이콘 (text-wrap) */
.jsini-icon-wrap {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M2 3.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm0 4a.5.5 0 0 1 .5-.5h7a2.5 2.5 0 0 1 0 5H6.707l1.147-1.146a.5.5 0 1 0-.708-.708l-2 2a.5.5 0 0 0 0 .708l2 2a.5.5 0 0 0 .708-.708L6.707 13H9.5a3.5 3.5 0 1 0 0-7H2.5a.5.5 0 0 1-.5-.5zm0 5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M2 3.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm0 4a.5.5 0 0 1 .5-.5h7a2.5 2.5 0 0 1 0 5H6.707l1.147-1.146a.5.5 0 1 0-.708-.708l-2 2a.5.5 0 0 0 0 .708l2 2a.5.5 0 0 0 .708-.708L6.707 13H9.5a3.5 3.5 0 1 0 0-7H2.5a.5.5 0 0 1-.5-.5zm0 5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
}


/* 「쓸 수 있는 AI」 체크칸 줄. 값이 둘뿐이라 가로로 세운다 —
   세로로 쌓으면 그 한 칸 때문에 편집 창이 두 줄 더 길어진다. */
.pm-ai-kinds {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1.25rem;
  align-items: center;
  min-height: 2rem;
}

/* ── 실행 이력 바 (탭 밖 상단) ─────────────────────────────────
   지시 / 결과 / 로그 탭의 상단에 항상 노출되어 어느 탭에서든
   현재 작업의 실행 차수별 상태와 경과 시간을 확인하고 차수를 고를 수 있다. */
.pm-ai-runs-bar {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin-bottom: 0.75rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--jsini-border, #e3e5e8);
  border-radius: var(--jsini-radius-md, 6px);
  background: var(--jsini-surface, #f7f8fa);
}

.pm-ai-runs-bar__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

.pm-ai-runs-bar__title {
  font-size: var(--jsini-fs-xs, 0.75rem);
  font-weight: 600;
  color: var(--jsini-text-muted, #6b7280);
}

.pm-ai-runs-bar__cur {
  font-size: var(--jsini-fs-xs, 0.75rem);
  color: var(--jsini-accent-text, #0070cc);
  font-weight: 500;
}

.pm-ai-run__seq {
  font-weight: 600;
}

.pm-ai-result-tab,
.pm-ai-log-tab {
  padding-top: var(--jsini-sp-md, 0.75rem);
}

.pm-ai-runs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.pm-ai-run {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.25rem 0.6rem;
  border: 1px solid var(--jsini-border, #e3e5e8);
  border-radius: 999px;
  background: transparent;
  font-size: var(--jsini-fs-xs);
  cursor: pointer;
}

/* 가동 시간. **숫자가 1초마다 바뀌는 자리**라 고정폭으로 둔다 —
   아니면 글자 너비가 들쭉날쭉해 옆의 단추들이 초마다 흔들린다. */
.pm-ai-elapsed {
  font-variant-numeric: tabular-nums;
  color: var(--jsini-text-muted);
}

/* 도는 중인 것만 진하게. 끝난 것들과 섞여 있을 때 **지금 도는 것**을
   색으로 먼저 찾게 한다. */
.pm-ai-elapsed--on {
  color: var(--jsini-accent-text);
  font-weight: 600;
}

/* 고른 실행. 테마의 강조색을 쓴다 — 여기도 부트스트랩 변수는 없다. */
.pm-ai-run--on {
  border-color: var(--jsini-accent);
  box-shadow: inset 0 0 0 1px var(--jsini-accent);
}

/* 로그와 결과는 **글자 그대로** 보여 준다. 줄바꿈을 접으면 표·트리 모양의
   출력이 뭉개져서 무슨 일이 있었는지 읽을 수 없다.

   **바탕과 글자를 둘 다 못 박는다.** 한쪽만 정하면 나머지가 어디서 오는지에
   따라 어두운 테마에서 흰 바탕에 밝은 글씨가 된다 — 재연결 덮개에서 한 번
   났던 사고와 같은 종류다(app.css). 테마를 따라가는 토큰(`--jsini-*`)을 쓴다. */
/* 로그는 **판을 채우고 자기 안에서만 구른다.** 320px 로 묶어 두면 긴 로그가
   그 구멍 안에서만 움직여, 지켜보는 동안 내내 여남은 줄씩만 보인다.

   여기 적는 값은 편집기와 같은 이유로 **좁은 창의 것**이다 — 데스크탑에서는
   탭이 준 남은 높이를 그대로 받는다(`pm-ai-page` 절). */
.pm-ai-log { height: 24rem; }

.pm-ai-result { max-height: 28rem; }

.pm-ai-log,
.pm-ai-result {
  margin: 0;
  padding: 0.6rem 0.75rem;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: var(--jsini-fs-xs);
  line-height: 1.5;
  color: var(--jsini-text);
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-md);
}

/* 로그는 한 단 눌러 둔다 — 결과문과 갈라 보이게. */
.pm-ai-log { background: var(--jsini-surface); }

/* 결과는 AI 의 답이라 본문처럼 읽힌다. 바탕도 본문과 같게. */
.pm-ai-result { background: var(--jsini-bg); }

/* 이어서 시킬 내용을 받는 칸. 한 줄짜리가 아니라 문단을 쓰는 자리다. */
.pm-ai-addition { width: 100%; }

/* 목록의 제목 칸. 제목 뒤에 「임시」 표가 붙을 수 있어 한 줄로 세운다.
   제목이 길면 표가 밀려 보이지 않으므로 **제목 쪽만** 줄인다. */
.pm-ai-title {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-width: 0;
}

.pm-ai-title__text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── 작업 목록 한 줄 (`pm-ai-row`) ────────────────────────────

   이 판은 34% 짜리다. 제목·상태·요청·대상·요청시각을 칸 다섯으로 늘어놓으면
   **정작 제목이 서너 글자만 보인다** — 고를 때 필요한 것은 「무슨 일인가」인데
   그것부터 잘렸다. 제목이 한 줄을 차지하고 나머지는 잔글씨로 아래 붙는다.
   ---------------------------------------------------------- */

.pm-ai-row {
  display: flex;
  flex-direction: column;

  /* 제목과 잔글씨가 붙어 있으면 두 줄이 한 덩어리로 뭉쳐 보인다.
     0.1rem 은 붙은 것과 다름없었다. */
  gap: var(--jsini-sp-2xs);
  padding: var(--jsini-sp-2xs) 0;
  min-width: 0;
}

.pm-ai-row__title {
  display: flex;
  gap: 0.35rem;
  align-items: center;
  min-width: 0;
  font-weight: 600;
}

.pm-ai-row__sub {
  display: flex;
  gap: 0.35rem;
  align-items: baseline;
  font-size: 0.85em;
  opacity: 0.75;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 상태와 요청여부를 한 칸에 나란히. 좁아지면 아래로 접힌다 —
   **둘 다 보여야 한다**(「취소를 눌렀는데 아직 돌고 있다」). */
.pm-ai-row__state {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem;
}

/* ── 상태 칸 다중 선택 필터 (`pm-ai-status-filter`) ──────────────
   필터 행 셀 안의 TagBox.
   좁은 셀 안에서 자연스럽게 보이도록 너비를 꽉 채운다.
   태그가 많아지면 팝업이 위로 열리므로 min-height 는 두지 않는다. */
.pm-ai-status-filter {
  width: 100%;
}

/* ── AI 작업 화면 머리·반응형 강화 ─────────────────────────────

   기존 기능과 데이터 흐름은 그대로 두고, 첫 화면에서 「무엇을 할 수 있는지」와
   「지금 무슨 상태인지」가 먼저 읽히도록 화면의 정보 순서를 정리한다.
   통계는 현재 조회 결과를 기준으로 하므로 필터를 바꿔도 숫자가 거짓말하지 않는다.
   ---------------------------------------------------------- */

/* **머리줄이 세로를 덜 먹게 줄였다.** 여기서 아낀 높이가 그대로 편집기와
   로그로 간다 — 이 화면에서 오래 들여다보는 것은 그 둘이다. 안쪽 여백을
   한 단(24 → 16px) 줄이고, 제목·숫자도 한 단씩 내렸다. */
.pm-ai-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--jsini-sp-lg);
  align-items: center;
  margin-bottom: var(--jsini-sp-md);
  padding: var(--jsini-sp-lg) var(--jsini-sp-xl);
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-lg, 0.75rem);
  background:
    radial-gradient(circle at 100% 0, color-mix(in srgb, var(--jsini-accent) 12%, transparent), transparent 38%),
    var(--jsini-surface);
}

.pm-ai-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--jsini-accent-text);
  font-size: var(--jsini-fs-xs);
  font-weight: 700;
  letter-spacing: 0.04em;
}

.pm-ai-eyebrow__dot,
.pm-ai-live__indicator {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--jsini-success, #1f9d65);
}

.pm-ai-hero__heading {
  display: flex;
  gap: var(--jsini-sp-lg);
  align-items: end;
  justify-content: space-between;
  margin-top: var(--jsini-sp-2xs);
}

.pm-ai-hero h1 {
  margin: 0;
  color: var(--jsini-text);
  font-size: var(--jsini-fs-2xl);
  line-height: 1.2;
}

.pm-ai-hero p {
  margin: 0.2rem 0 0;
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-sm);
}

.pm-ai-live {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 0.4rem;
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-xs);
  white-space: nowrap;
}

.pm-ai-live__indicator--busy {
  background: var(--jsini-accent);
  animation: pm-ai-pulse 1.2s ease-in-out infinite;
}

.pm-ai-list-stats {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--jsini-sp-md);
  padding: 0.5rem var(--jsini-sp-md);
  background: var(--jsini-surface);
  border-bottom: 1px solid var(--jsini-border);
}

.pm-ai-list-stat {
  display: inline-flex;
  align-items: baseline;
  gap: 0.3rem;
  font-size: var(--jsini-fs-sm);
}

.pm-ai-list-stat__label {
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
}

.pm-ai-list-stat__value {
  font-weight: 700;
  color: var(--jsini-text);
}

.pm-ai-list-stat--accent .pm-ai-list-stat__value { color: var(--jsini-accent); }
.pm-ai-list-stat--success .pm-ai-list-stat__value { color: var(--jsini-success, #1f9d65); }
.pm-ai-list-stat--danger .pm-ai-list-stat__value { color: var(--jsini-danger, #d94b55); }

.pm-ai-list-stats .pm-ai-live {
  margin-left: auto;
}

.pm-ai-stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(5.5rem, 1fr));
  gap: var(--jsini-sp-sm);
}

.pm-ai-stat {
  min-width: 0;
  padding: 0.45rem 0.7rem;
  border-left: 3px solid var(--jsini-border);
  border-radius: var(--jsini-radius-md, 0.4rem);
  background: color-mix(in srgb, var(--jsini-bg) 72%, transparent);
}

.pm-ai-stat--accent { border-left-color: var(--jsini-accent); }
.pm-ai-stat--success { border-left-color: var(--jsini-success, #1f9d65); }
.pm-ai-stat--danger { border-left-color: var(--jsini-danger, #d94b55); }

.pm-ai-stat__label {
  display: block;
  overflow: hidden;
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-xs);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pm-ai-stat__value {
  display: block;
  margin-top: 0.1rem;
  color: var(--jsini-text);
  font-size: var(--jsini-fs-xl);
  font-variant-numeric: tabular-nums;
}

.pm-ai-loading {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin: 0 0 var(--jsini-sp-md);
  padding: 0.6rem 0.8rem;
  border: 1px solid color-mix(in srgb, var(--jsini-accent) 28%, var(--jsini-border));
  border-radius: var(--jsini-radius-md, 0.4rem);
  background: color-mix(in srgb, var(--jsini-accent) 7%, var(--jsini-surface));
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-sm);
}

.pm-ai-loading__spinner {
  width: 0.9rem;
  height: 0.9rem;
  border: 2px solid color-mix(in srgb, var(--jsini-accent) 25%, transparent);
  border-top-color: var(--jsini-accent);
  border-radius: 50%;
  animation: pm-spin 0.8s linear infinite;
}

.pm-ai-context {
  display: flex;
  gap: var(--jsini-sp-md);
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--jsini-sp-md);
  padding: 0.65rem 0.8rem;
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-md, 0.4rem);
  background: color-mix(in srgb, var(--jsini-accent) 5%, var(--jsini-surface));
}

.pm-ai-context__main {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 0.15rem;
}

.pm-ai-context__main strong {
  word-break: keep-all;
  overflow-wrap: break-word;
}

.pm-ai-context__kicker {
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-xs);
}

.pm-ai-context__meta {
  display: flex;
  flex: 0 0 auto;
  flex-wrap: wrap;
  gap: 0.25rem;
  justify-content: end;
}

.pm-ai-empty {
  display: grid;
  min-height: 22rem;
  place-content: center;
  justify-items: center;
  gap: 0.45rem;
  padding: var(--jsini-sp-xl);
  border: 1px dashed var(--jsini-border);
  border-radius: var(--jsini-radius-md, 0.4rem);
  text-align: center;
}

.pm-ai-empty__icon {
  display: grid;
  width: 2.5rem;
  height: 2.5rem;
  place-items: center;
  margin-bottom: 0.35rem;
  border-radius: 50%;
  background: color-mix(in srgb, var(--jsini-accent) 12%, transparent);
  color: var(--jsini-accent-text);
  font-size: 1.35rem;
}

.pm-ai-empty p {
  max-width: 28rem;
  margin: 0;
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-sm);
}

.pm-ai-empty__hint {
  margin-top: 0.4rem;
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-xs);
}

@keyframes pm-ai-pulse {
  50% { opacity: 0.35; transform: scale(0.75); }
}

@media (max-width: 74rem) {
  .pm-ai-hero {
    grid-template-columns: 1fr;
    align-items: stretch;
  }

  .pm-ai-stats { grid-template-columns: repeat(4, 1fr); }
}

@media (max-width: 62rem) {
  .pm-split { gap: var(--jsini-sp-lg); }
  .pm-ai-detail-panel { order: -1; }
  .pm-ai-list-panel { order: 1; }
}

@media (max-width: 42rem) {
  .pm-ai-hero {
    padding: var(--jsini-sp-lg);
  }

  .pm-ai-hero__heading {
    align-items: start;
    flex-direction: column;
    gap: var(--jsini-sp-md);
  }

  .pm-ai-stats {
    grid-template-columns: repeat(2, 1fr);
  }

  .pm-ai-context {
    align-items: start;
    flex-direction: column;
  }

  .pm-ai-context__meta { justify-content: start; }
  .pm-ai-empty { min-height: 16rem; }
}

@media (prefers-reduced-motion: reduce) {
  .pm-ai-live__indicator--busy,
  .pm-ai-loading__spinner { animation: none; }
}

/* ── 빠른 지시 (`pm-ask`) ─────────────────────────────────────

   모바일에서 주로 쓰는 화면이다. 그래서 규칙이 「AI 작업」 화면과 반대다 —
   저쪽은 넓은 판을 쪼개 쓰고, 이쪽은 **한 줄로 세운다.**

   고정 폭을 두지 않는다. 판 하나짜리 세로 배치라 좁은 화면에서는 그대로
   꽉 차고, 넓은 화면에서는 읽기 좋은 폭에서 멈춘다 — 27인치 모니터에서
   글상자가 1.5미터로 늘어나면 그것대로 못 쓴다.
   ---------------------------------------------------------- */

.pm-ask {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-width: 52rem;
}

/* 대상과 AI 를 한 줄에. 좁아지면 각자 한 줄을 갖는다 —
   **가로로 구겨 넣지 않는다.** 손가락으로 누르는 칸이다. */
.pm-ask__where {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

.pm-ask__target { flex: 3 1 14rem; }
.pm-ask__kind { flex: 1 1 9rem; }

/* AI 고르개 한 줄 — 이름과 한도가 두 층이다.

   **목록 칸의 높이를 고정하지 않는다.** CLI 마다 한도 줄 수가 달라서
   (claude 는 하나, antigravity 는 둘) 높이를 못 박으면 긴 쪽이 잘린다.
   DevExpress 가 칸 안쪽 여백을 이미 주므로 여기서는 세로만 세운다. */
.pm-ask__kinditem {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.pm-ask__kindhead {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-width: 0;
}

/* 이름이 길어도 배지를 밀어내지 않는다 — 배지가 밀리면 「사용 92%」가
   목록 밖으로 나가 **가장 봐야 할 값이 안 보인다.** */
.pm-ask__kindname {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pm-ask__kindhead .jsini-badge,
.pm-ask__limit .jsini-badge,
.pm-ai-limit .jsini-badge,
.pm-peek__usage {
  flex: 0 0 auto;
  font-size: 0.75em;
  padding: 0.15em 0.4em;
}

/* 고른 줄·마우스가 올라간 줄은 **바탕이 강조색으로 칠해진다.** 그 위에서
   흐린 회색(`--jsini-text-muted`)과 초록 배지는 거의 안 읽힌다 — 실제로
   고른 줄의 한도가 파란 바탕에 묻혀 있었다(헤드리스로 색을 재어 확인).

   DevExpress 가 그 줄의 글자색을 흰색으로 바꿔 주므로 **그 색을 물려받는다.**
   여기서 흰색을 박으면 밝기·강조색을 바꿨을 때 그것만 안 따라온다. */
.dxbl-list-box-item-selected .pm-ask__kindhead .jsini-badge,
.dxbl-list-box-item-focused .pm-ask__kindhead .jsini-badge,
li:hover > * > .pm-ask__kinditem .pm-ask__kindhead .jsini-badge {
  color: inherit;
  border-color: currentColor;
}

/* 닫힌 고르개 아래 한 줄. 고른 AI 의 한도다.

   **자리를 늘 차지하게 두지 않는다** — 값이 없으면 마크업째 안 그린다.
   글상자 위에 빈 줄이 하나 늘면 자판이 올라온 화면에서 단추가 그만큼
   내려간다(단추 줄을 한 줄로 묶은 것과 같은 까닭). */
.pm-ask__limit {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: -0.35rem 0 0.75rem;
  font-size: 0.85em;
  color: var(--jsini-text-muted);
}


/* 글상자. 화면 세로 길이의 30%를 넘지 않게 한다 —
   모바일 자판이 올라와도 화면을 가리지 않게 한다. */
.pm-ask__text,
.pm-ask__text .dxbl-memo-scroll-container,
.pm-ask__text textarea {
  max-height: 30vh;
  max-height: 30dvh;
}

.pm-ask__text {
  width: 100%;
}

/* 글자 크기는 **서랍에서 고른 단계를 따른다**(`--jsini-fs-md`).

   한동안 `1rem` 이 박혀 있었다. 까닭은 아래 iOS 한 줄인데, 그 값이
   `<html>`(16px)에 묶여 있어 **크기 여섯 단계 중 어느 것을 골라도 이 칸만
   꿈쩍하지 않았다** — 가장 많이 들여다보는 칸이 하필 그랬다.
   (web/CLAUDE.md 「글자 크기를 `rem` 으로 박지 않는다」) */
.pm-ask__text textarea {
  min-height: min(6rem, 30vh);
  font-size: var(--jsini-fs-md);
  line-height: 1.5;
  overflow-y: auto;
}

/* **손가락으로 쓰는 기기에서만 16px 바닥을 둔다.** iOS 사파리는 글자가
   16px 보다 작은 칸에 들어가는 순간 화면을 확대하고, 그 확대는 저절로
   풀리지 않아 「적고 나면 화면이 커져 있다」가 된다.

   바닥을 모든 기기에 두면 위에서 되찾은 것을 그대로 잃는다 — 책상에서
   작게 고른 사람은 손가락으로 쓰지 않는다. */
@media (pointer: coarse) {
  .pm-ask__text textarea {
    font-size: max(var(--jsini-fs-md), 1rem);
  }
}

/* 임시저장 복원 안내 한 줄 */
.pm-ask__draft {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin: 0.4rem 0 0;
  padding: 0.35rem 0.65rem;
  font-size: 0.85em;
  background: var(--jsini-bg-subtle, rgba(0, 0, 0, 0.04));
  border-radius: 4px;
}

.pm-ask__draft-text {
  color: var(--jsini-text-muted);
}

.pm-ask__draft-clear {
  padding: 0;
  background: none;
  border: none;
  color: var(--jsini-primary, #1976d2);
  cursor: pointer;
  text-decoration: underline;
  font-size: 0.9em;
}

.pm-ask__draft-clear:hover {
  opacity: 0.8;
}


/* ── 함께 보내는 파일 (`pm-ask__files`) ──────────────────────

   글상자와 단추 줄 사이다. 여기에 들어가는 높이는 **자판이 올라온 화면에서
   단추를 그만큼 밀어내므로** 인색하게 쓴다 — 붙이면 칩이 줄줄이 늘어서는
   대신 **줄바꿈으로 흐른다.**

   **고른 것이 없으면 이 판은 아예 안 그려진다**(2026-10-05). 고르개가
   단추 줄의 아이콘으로 옮겨 가면서 여기 남은 것이 「선택됨」 줄과 칩뿐이고,
   둘 다 없는데 상자만 두면 위 여백 0.6rem 이 늘 서 있게 된다.
   ---------------------------------------------------------- */

.pm-ask__files {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 0.6rem 0 0;
}

/* 임시 첨부가 만들어지기 전에도 파일을 골랐다는 사실과 이름을 보여 준다. */
.pm-ask__selected {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.5rem;
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-sm);
  overflow-wrap: anywhere;
}

/* 붙인 것. **격자가 아니라 흐름이다** — 이름 길이가 제각각이라 칸을 맞추면
   짧은 이름 옆이 통째로 빈다. */
.pm-ask__clips {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
}

/* 칩 한 개. 그림 · 이름 · 크기 · 떼기 넷이 한 줄이다.

   **폭을 묶는다.** 안 묶으면 휴대폰 사진의 긴 이름
   (`Screenshot_20260925-093012_Chrome.jpg`) 하나가 줄을 통째로 먹어
   두 개째부터 아래로 쌓인다. */
.pm-ask__clip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  max-width: 16rem;
  padding: 0.25rem 0.3rem 0.25rem 0.25rem;
  border: 1px solid var(--jsini-border, rgba(0, 0, 0, 0.14));
  border-radius: 999px;
  font-size: 0.85em;
  line-height: 1.4;
}

/* 미리보기. **네모로 잘라 채운다**(`cover`) — 비율대로 두면 세로 사진과
   가로 사진이 섞일 때 칩 높이가 줄마다 달라진다. */
.pm-ask__clipimg {
  width: 1.6rem;
  height: 1.6rem;
  flex: 0 0 auto;
  object-fit: cover;
  border-radius: 50%;
  background: var(--jsini-bg-subtle, rgba(0, 0, 0, 0.04));
}

/* 그림이 아닌 것. 글자 대신 **클립 모양**을 세운다 — 확장자를 적으면
   `.xlsx` 와 `.pptx` 가 같은 폭을 먹으면서 이름이 그만큼 잘린다. */
.pm-ask__clipicon,
.pm-peek__fileicon {
  width: 1.6rem;
  height: 1.6rem;
  flex: 0 0 auto;

  /* **바탕을 깔지 않는다.** 마스크가 그림 모양 밖을 전부 잘라 내므로
     둥근 바탕을 줘 봐야 함께 잘린다 — 그림만 남는다. 색은 글자색에서
     받아 테마 스물둘을 그대로 따라간다(app.css 의 아이콘 묶음과 같다). */
  background: var(--jsini-text-muted, currentColor);
  mask: var(--jsini-clip-svg) center / 0.9rem 0.9rem no-repeat;
  -webkit-mask: var(--jsini-clip-svg) center / 0.9rem 0.9rem no-repeat;
}

/* 클립 그림 한 장. **파일로 두지 않는다** — 세 자리에만 쓰는 것이라
   요청을 하나 더 내면서까지 밖에 둘 이유가 없다.

   **조상이 아니라 쓰는 요소 자신에 둔다.** 상세는 휴대폰에서만 `.pm-peek`
   창 안이고 넓은 화면에서는 탭(`AiTaskViewPage`)이라 감싸는 상자가 갈린다 —
   조상에 두면 **탭으로 열었을 때만 그림이 사라진다.** */
.pm-ask__clipicon,
.pm-peek__fileicon,
.pm-ask__clipmark::before {
  --jsini-clip-svg: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48'/%3E%3C/svg%3E");
}

/* 이름. **꼬리를 자른다** — 앞이 잘리면 무슨 파일인지 알 수 없다. */
.pm-ask__clipname {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pm-ask__clipsize {
  flex: 0 0 auto;
  color: var(--jsini-text-muted);
  font-size: 0.9em;
}

/* 떼기. **손가락이 겨누는 폭을 글자보다 크게 둔다** — 칩이 촘촘히 붙어
   있어서 작으면 옆 칩을 누른다. */
.pm-ask__clipx {
  flex: 0 0 auto;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  background: none;
  border: none;
  border-radius: 50%;
  color: var(--jsini-text-muted);
  font-size: 1.1em;
  line-height: 1;
  cursor: pointer;
}

.pm-ask__clipx:hover:not(:disabled) {
  background: var(--jsini-bg-subtle, rgba(0, 0, 0, 0.08));
  color: var(--jsini-danger, #c62828);
}

.pm-ask__clipx:disabled {
  opacity: 0.4;
  cursor: default;
}

/* ── 고르개는 **아이콘 하나**다 (2026-10-05) ─────────────────

   단추 줄(`pm-ask__bar`)에 이메일·앱알림과 나란히 선다. 공용 부품
   (`FilePicker`)은 문서 폼 기준이라 끌어놓기 칸이 줄 폭을 다 쓰고 안내가
   한 줄 더 서는데, 그 둘이 그대로 들어오면 **줄이 두세 층으로 풀리고
   그때 보내기 단추가 자판 밑으로 내려간다** — 이 화면이 서서 피하려던
   바로 그 일이다.

   **그림을 우리가 그리지 않는다.** DevExpress 가 그 단추 안에 이미
   올리기 그림을 넣어 두었다(`dx-uc-upload`) — 글자만 떼면 아이콘 단추다.
   ---------------------------------------------------------- */

.pm-ask__pick {
  flex: 0 0 auto;
  display: inline-flex;
}

/* 안내 줄을 껐으니(`ShowHint="false"`) 세로로 쌓을 것이 없다. */
.pm-ask__pick .jsini-picker {
  display: block;
  gap: 0;
}

/* 부품이 스스로 얹는 위 여백(`--dxbl-upload-padding-top`, 1.2rem)을 걷는다.
   남겨 두면 아이콘 혼자 줄 위로 떠서 체크칸과 밑줄이 어긋난다. */
.pm-ask__pick .dxbl-upload {
  padding-top: 0;
}

/* 끌어놓기 칸의 줄 간격(1rem)을 걷는다. 안에 단추 하나뿐이라 쓰이지 않는
   값이지만, 남겨 두면 단추를 하나 더 켜는 날 줄이 소리 없이 벌어진다.

   **자릿수를 테마와 맞춘다**(`.dxbl-upload .dxbl-upload-container …`).
   짧게 적으면 테마 규칙(0,3,0)에 져서 **규칙은 실려 있는데 안 먹는다** —
   아래 목록에서 실제로 밟았다. */
.pm-ask__pick .dxbl-upload .dxbl-upload-container .dxbl-upload-panel {
  gap: 0;
}

/* 네모 단추 하나. 손가락이 겨누는 폭을 보내기 단추만큼은 아니어도
   체크칸보다는 넉넉히 둔다.

   **겨누는 단추가 바뀌었다 (2026-10-05).** 전에는 DevExpress 가 그리는
   `.dxbl-upload-select-btn` 을 붙잡아 글자(`.dxbl-btn-caption`)를 감추고
   강조색을 벗겼다. 지금은 그 단추 자체가 꺼져 있고 고르개가 **제 아이콘
   단추**(`.jsini-picker__pick`)를 세운다(`FilePicker` 머리말) — 글자가 아예
   없으니 감출 것이 없고, 부품이 중립 단추(`Secondary`)로 세우므로 색을
   벗길 일도 없다. 여기 남는 것은 **이 줄에서만 다른 치수**뿐이다.

   한 줄에 파란 덩이가 둘이면 어느 것이 보내는 단추인지 한눈에 안 갈린다 —
   이 줄에서 눈이 맨 끝 단추로 가야 한다는 것이 이 판의 전제다(아래
   `pm-ask__go` 머리말). */
.pm-ask__pick .jsini-picker__pick {
  min-width: 2.5rem;
  min-height: 2.25rem;
  padding-inline: 0.6rem;
}

/* DevExpress 가 그리는 파일 목록은 **비었을 때만 감춘다.**

   비어 있어도 위 여백 1.375rem 을 들고 서 있어서, 그냥 두면 아무것도 안
   고른 평소에 단추 줄이 22px 높아진다 — 고르개를 아이콘으로 줄인 값을
   그만큼 도로 내놓는 셈이다.

   **통째로 감추지는 않는다.** 그 목록이 유일하게 말해 주는 것이 하나
   있다 — **DevExpress 가 거절한 파일**(한 개 100MB 초과)이다. 그 건은
   올리기가 시작되지 않아 `FilesUploading` 도 안 오고, 화면의 「선택됨」
   줄에는 이름이 남는다. 목록까지 감추면 **왜 안 올라가는지 말하는 자리가
   화면에서 사라진다.**

   있을 때는 줄을 통째로 쓴다(`:has`). 아이콘 폭(40px) 안에 파일 이름과
   진행 막대를 밀어 넣으면 읽을 수 없다. 올리는 동안과 거절당했을 때뿐이라
   줄이 잠깐 두 층이 되는 것은 값을 한다. */
.pm-ask__pick .dxbl-upload .dxbl-upload-container .dxbl-upload-file-list-view:empty {
  display: none;
}

.pm-ask__pick:has(.dxbl-upload-file-list-view:not(:empty)) {
  flex: 1 1 100%;
}

/* 카드의 클립 표시. 개수만 적는다 — 위 razor 주석 참고. */
.pm-ask__clipmark {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 0.15rem;
  color: var(--jsini-text-muted);
  font-size: 0.9em;
}

.pm-ask__clipmark::before {
  content: "";
  width: 0.75rem;
  height: 0.75rem;
  background: currentColor;
  mask: var(--jsini-clip-svg) center / contain no-repeat;
  -webkit-mask: var(--jsini-clip-svg) center / contain no-repeat;
}

/* 글상자 아래 한 줄 — 첨부 아이콘과 보내기 단추.
   체크박스(이메일, 앱알림)가 제거되었으며, 첨부 아이콘은 왼쪽, 보내기(올리기)
   단추는 오른쪽 끝에서 항상 같은 라인으로 나란히 선다. */
.pm-ask__bar {
  display: flex;
  align-items: center;
  gap: 0.5rem 1rem;
  margin: 0.75rem 0 0;
}

/* 체크박스 스타일은 더 이상 쓰이지 않으나 하위 호환성을 위해 유지 */
.pm-ask__opt {
  font-size: 0.9em;
  line-height: 1.5;
}

/* 보내기(올리기) 단추. 첨부 아이콘과 같은 라인 우측 끝에 배치 */
.pm-ask__go {
  min-height: 2.75rem;
  padding-inline: 1.25rem;
  margin-left: auto;
  font-weight: 600;
  white-space: nowrap;
  flex-shrink: 0;
}


/* ── 판 안의 탭 줄 (`pm-ask__tabs`) ─────────────────────────

   휴대폰에서 카드를 누르면 상세가 이 판 안에 선다. 포털의 탭 줄은 그 폭에서
   통째로 감춰져 있어서(`app.css` 의 ≤767px) 주소로 옮겨 가 봐야 **열어 둔
   것들 사이를 오갈 길이 없다.** 그래서 이 판이 제 탭 줄을 갖는다.

   **붙박이로 세운다**(`sticky`). 결과문은 화면 몇 장짜리라, 다른 건으로
   갈아타려고 맨 위까지 다시 굴려 올려야 하면 탭을 둔 뜻이 반쯤 없어진다.
   바탕을 칠하는 까닭은 그 아래로 글이 지나가기 때문이다.

   본문 좌우 여백이 거의 없는 폭이라(`--jsini-shell-pad-inline`) 줄이 화면을
   거의 다 쓴다. 넘치면 **옆으로 민다** — 줄바꿈하면 탭 넷에 두 층이 되고,
   그 두 층이 붙박이로 화면 위에 남는다. */
.pm-ask__tabs {
  position: sticky;
  top: 0;
  z-index: 3;

  display: flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.3rem 0;
  background: var(--jsini-bg);
  border-bottom: 1px solid var(--jsini-border);

  overflow-x: auto;
  scrollbar-width: none;
}

.pm-ask__tabs::-webkit-scrollbar { display: none; }

/* 칸 하나. 맨 앞의 「빠른 지시」는 단추 하나뿐이고(닫을 수 없다) 나머지는
   **이름 단추 + 닫기 단추** 둘을 안는다. 둘을 한 칸처럼 보이게 테두리는
   바깥 상자가 긋는다.

   `flex: 0 0 auto` 가 있어야 한다 — 없으면 탭이 늘어날수록 칸이 쪼그라들어
   이름이 한 글자씩 남는다. 넘치는 것은 옆으로 미는 쪽으로 푼다. */
.pm-ask__tab {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;

  max-width: 11rem;
  border: 1px solid var(--jsini-border);
  border-radius: 0.3rem;
  background: var(--jsini-surface);
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-2xs);
}

/* 고른 칸은 **면으로** 칠한다. 테두리를 두껍게 하면 그 칸만 키가 커져 줄이
   들썩인다 — 단 수 고르개(`pm-ask__col`)가 같은 까닭으로 같은 방법을 쓴다. */
.pm-ask__tab--on {
  border-color: var(--jsini-accent);
  background: var(--jsini-accent);
  color: #fff;
  font-weight: 600;
}

/* 맨 앞 칸(「빠른 지시」)은 상자 자신이 단추다. 안쪽 여백만 준다. */
.pm-ask__tabs > button.pm-ask__tab,
.pm-ask__tabgo {
  appearance: none;
  min-width: 0;
  padding: 0.25rem 0.5rem;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  line-height: 1.6;
  cursor: pointer;

  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* 닫기는 **이름 옆에 따로** 둔다. 칸을 눌러 갈아타는 것과 닫는 것이 같은
   자리면 갈아타려다 닫는다 — 손가락으로 겨누는 자리라 1.5rem 은 남긴다. */
.pm-ask__tabx {
  appearance: none;
  flex: 0 0 auto;
  min-width: 1.5rem;
  padding: 0.25rem 0.35rem 0.25rem 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  line-height: 1.6;
  opacity: 0.7;
  cursor: pointer;
}

.pm-ask__tabx:hover { opacity: 1; }

.pm-ask__tab:focus-within {
  outline: 2px solid var(--jsini-accent);
  outline-offset: 1px;
}

/* ── 상세를 여는 동안 적는 자리를 감춘다 ─────────────────────

   **지우지 않는다.** 적다 만 글을 붙드는 JS(`ask-draft.js`)가 글상자를
   `#아이디 .pm-ask__text` 로 잡고 있고, 미는 손짓과 끝까지 굴렸는지 보는
   감지점도 그 안에 산다. 마크업이 사라지면 그 셋이 함께 끊긴다. */
.pm-ask__pane--off { display: none; }

/* 상세 판. 목록과 달리 **화면 폭을 통째로 쓴다** — 결과문을 읽는 자리다.
   `.pm-ask` 가 둔 52rem 상한은 그대로 받는다(휴대폰에서는 걸릴 일이 없다).

   상세를 보는 동안에는 판을 화면 높이로 채워 단추가 늘 바닥에 서게 한다. */
.pm-ask:has(> .pm-ask__view) {
  min-height: 100%;
}

.pm-ask__view {
  display: flex;
  flex-direction: column;
  flex: 1;
}

.pm-ask__view .commcont__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}

/* ── 보낸 것 목록 ──────────────────────────────────────────── */

/* 목록이 아니라 **카드**다. 줄로 늘어놓으면 제목 · 상태 · 시각 · 걸린 시간이
   한 줄을 다투다가 좁은 화면에서 제목부터 잘린다.

   **몇 단으로 깔지는 사람이 고른다**(`--pm-ask-cols`, 1~3). 화면이 그 값을
   상자에 실어 내리고 여기서는 단 수만 받아 쓴다. 기본은 둘이고 한동안
   그것 하나뿐이었다 — 넉 장을 한 줄에 한 장씩 쌓으면 세로로 길어져 마지막
   장이 자판 아래로 밀리는데, 둘씩 놓으면 높이가 반이라 한눈에 들어온다.

   **화면 폭으로 이 값을 되돌리지 않는다.** 좁은 화면에서 셋을 고른 사람은
   제목이 잘리는 것을 알고 고른 것이고, 반 폭에서 잘리는 제목은 눌러서 본다.
   폭이 고른 값을 말없이 덮으면 「눌렀는데 안 바뀐다」가 된다.

   `minmax(0, 1fr)` 이라야 한다 — 맨 `1fr` 은 최소 너비가 내용이라 긴 제목
   하나가 제 칸을 밀어내고, 세 단에서는 그것이 바로 드러난다.

   `align-items: start` 가 필요하다 — 없으면 격자가 같은 줄의 카드를
   **펼친 쪽 높이에 맞춰 늘려** 옆 카드에 빈 바닥이 생긴다. */
.pm-ask__list {
  --pm-ask-cols: 2;

  list-style: none;
  display: grid;
  grid-template-columns: repeat(var(--pm-ask-cols), minmax(0, 1fr));
  align-items: start;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
}

/* 단 수 고르개. **목록 위 오른쪽**에 붙는다 — 아래 「더보기」 줄에 두면
   카드가 적을 때 그 줄이 안 서서 고르는 길이 사라진다.

   높이를 아낀다. 이 판은 「길에서 한 줄 던지는 자리」라 글상자와 카드 사이가
   벌어지면 방금 보낸 것이 자판 밖으로 밀린다 — 한 층을 새로 내는 것이 아니라
   **카드 한 층의 절반쯤**으로 눌러 둔다. */
.pm-ask__cols {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.35rem;
  margin-bottom: 0.35rem;
}

.pm-ask__cols-label {
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-2xs);
}

/* 칸 셋이 한 덩어리다(`pm-axis` 와 같은 모양). 글자가 한 자뿐이라 좌우
   여백으로 누르는 자리를 만든다 — 손가락으로 겨누는 자리라 2rem 은 남긴다. */
.pm-ask__col {
  appearance: none;
  min-width: 2rem;
  padding: 0.2rem 0.45rem;
  border: 1px solid var(--jsini-border);
  border-radius: 0.3rem;
  background: var(--jsini-surface);
  color: var(--jsini-text-muted);
  font: inherit;
  font-size: var(--jsini-fs-2xs);
  line-height: 1.6;
  cursor: pointer;
}

.pm-ask__col:hover {
  background: color-mix(in srgb, currentcolor 8%, var(--jsini-surface));
  color: var(--jsini-text);
}

/* 고른 칸은 **면으로** 칠한다. 테두리를 두껍게 하면 그 칸만 키가 커져 줄이
   들썩인다 — 테마 서랍과 `pm-axis` 가 같은 까닭으로 같은 방법을 쓴다. */
.pm-ask__col[aria-pressed='true'] {
  border-color: var(--jsini-accent);
  background: var(--jsini-accent);
  color: #fff;
  font-weight: 600;
}

.pm-ask__col:focus-visible {
  outline: 2px solid var(--jsini-accent);
  outline-offset: 1px;
}

/* 상태를 카드의 색으로 말한다. 왼쪽 띠가 그 색이고 상태 글자도 같은 색이다.

   **색은 공통 배지(`jsini-badge--*`)가 쓰는 값 그대로다.** 이 화면만의 색을
   새로 만들면 같은 「완료」가 화면마다 다른 초록이 된다. 바탕은 칠하지
   않는다 — 밝은 쪽 값이 굳어 어두운 테마에서 흰 칩이 되는 것과 같은 함정이다.

   기본값은 흐림(`--off`)이라 수식어가 안 붙어도 띠가 사라지지는 않는다. */
.pm-ask__item {
  --pm-ask-tone: var(--jsini-text-muted);

  border: 1px solid var(--jsini-border);
  border-left: 0.35rem solid var(--pm-ask-tone);
  border-radius: 0.5rem;
  overflow: hidden;
}

.pm-ask__item--on { --pm-ask-tone: #2e7d32; }
.pm-ask__item--warn { --pm-ask-tone: #b26a00; }
.pm-ask__item--err { --pm-ask-tone: #d32f2f; }
.pm-ask__item--off { --pm-ask-tone: var(--jsini-text-muted); }

/* ── 오른쪽으로 밀면 확인 완료 ────────────────────────────

   움직이는 값을 **전부 인라인 사용자 지정 속성으로** 받는다. 클래스로 받지
   않는 이유는 목록이 5초마다 다시 그려지기 때문이다 — Blazor 가 카드의
   `class` 를 제 손으로 쥐고 있어서, 손짓 중에 그 갱신이 한 번 끼면
   **JS 가 붙인 클래스가 소리 없이 지워지고 카드가 밀린 자리에 굳는다.**
   `style` 은 Blazor 가 아예 그리지 않으므로(카드에 `style` 속성이 없다)
   그 자리는 건드리지 않는다. 대는 쪽은 `js/ask-swipe.js`.

     --pm-ask-swipe     밀린 거리. 본문이 그만큼 가고 띠가 그만큼 열린다
     --pm-ask-swipe-ms  그 둘의 전환 시간. 끄는 동안은 0
     --pm-ask-ready     문턱을 넘었나(0/1)
     --pm-ask-alive     살아 있나(1) 스러지는 중인가(0)

   **단 수와 무관하다.** 미는 거리도 문턱도 카드 제 폭(`clientWidth`)에서
   재므로 한 단이든 세 단이든 같은 비율로 열린다 — 세 단의 좁은 카드에서는
   띠에 ✓ 만 남고 「확인」 글자가 잘리는데, 그 상태로도 무슨 일이 일어나는지는
   초록과 ✓ 가 이미 말한다. */
.pm-ask__item[data-swipe] {
  --pm-ask-swipe: 0px;
  --pm-ask-swipe-ms: 0ms;
  --pm-ask-ready: 0;
  --pm-ask-alive: 1;

  position: relative;
  opacity: var(--pm-ask-alive);
  transition: opacity 180ms ease;
}

/* 카드 **왼쪽**에서 열리는 띠. 가만히 있을 때 폭이 0 이라 보이지 않는다.

   카드 밑에 깔아 두고 본문으로 덮는 흔한 방법을 쓰지 않았다 — 그러려면
   본문에 불투명한 바탕을 깔아야 하는데, 이 판의 바탕은 테마가 정하는 값이라
   스물둘 중 어긋나는 쪽이 반드시 나온다. 미는 만큼만 넓어지면 덮을 일이 없다.

   초록은 「완료」 카드의 띠 색 그대로다(`--on`). 확인은 완료된 건에 하는
   일이므로 같은 뜻의 색이 두 가지면 안 된다.

   **이 띠에는 안쪽 여백을 주지 않는다.** 여백을 주면 폭이 0 이어도 상자가
   그만큼은 남아(`width` 는 여백 아래로 못 내려간다 — `border-box` 로 해도
   마찬가지다) 밀 수 있는 카드마다 왼쪽에 **초록 실오라기**가 늘 서 있다.
   밀기 전에는 이 띠가 화면에 **한 픽셀도 없어야** 한다. 글자를 안쪽으로
   들이는 일은 안의 ✓ 가 제 바깥 여백으로 한다. */
.pm-ask__ack {
  position: absolute;
  inset-block: 0;
  left: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 0.25rem;
  width: max(0px, var(--pm-ask-swipe));
  overflow: hidden;
  background: #2e7d32;
  color: #fff;
  font-size: var(--jsini-fs-2xs);
  white-space: nowrap;
  pointer-events: none;
  transition: width var(--pm-ask-swipe-ms) ease;
}

/* 문턱을 넘으면 ✓ 가 커지고 글자가 또렷해진다. **손을 떼기 전에**
   「놓으면 처리된다」를 알려 주는 것이 이 둘의 일이다 — 넘기 전에 놓으면
   카드는 제자리로 돌아간다. */
.pm-ask__ack-mark {
  flex: 0 0 auto;
  margin-left: 0.5rem;
  font-size: var(--jsini-fs-sm);
  line-height: 1;
  transform: scale(calc(0.7 + (0.3 * var(--pm-ask-ready))));
  transition: transform 120ms ease;
}

.pm-ask__ack-text {
  opacity: calc(0.45 + (0.55 * var(--pm-ask-ready)));
  transition: opacity 120ms ease;
}

/* 카드 **오른쪽**에서 열리는 삭제 띠. 왼쪽으로 미는 만큼 넓어진다. */
.pm-ask__del {
  position: absolute;
  inset-block: 0;
  right: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.25rem;
  width: max(0px, calc(-1 * var(--pm-ask-swipe)));
  overflow: hidden;
  background: #c62828;
  color: #fff;
  font-size: var(--jsini-fs-2xs);
  white-space: nowrap;
  pointer-events: none;
  transition: width var(--pm-ask-swipe-ms) ease;
}

.pm-ask__del-mark {
  flex: 0 0 auto;
  margin-right: 0.5rem;
  font-size: var(--jsini-fs-sm);
  line-height: 1;
  transform: scale(calc(0.7 + (0.3 * var(--pm-ask-ready))));
  transition: transform 120ms ease;
}

.pm-ask__del-text {
  opacity: calc(0.45 + (0.55 * var(--pm-ask-ready)));
  transition: opacity 120ms ease;
}

/* 선택된 카드 강조 */
.pm-ask__item--picked {
  border-color: var(--jsini-accent);
  background: color-mix(in srgb, var(--jsini-accent) 7%, var(--jsini-bg));
}

/* 카드 전체가 누르는 자리다. 표가 아니라 단추 목록인 이유는 **모바일에서
   표의 가로 스크롤이 최악**이기 때문이다.

   **가로 둘이다** — 왼쪽이 지시자의 얼굴, 오른쪽이 두 층(`pm-ask__rows`)이다.
   얼굴을 윗층 안에 끼우지 않는 이유는 그 줄이 이미 셋을 쓰고 있어서
   (배지 · 상태 · 총작업시간) 반 폭 카드에서 폭을 다투기 때문이고, 무엇보다
   「누구 것인가」는 **두 층 모두에 걸리는 값**이라 한 줄에 속하지 않는다.

   여백을 줄였다. 다만 `min-height` 는 그대로 둔다 —
   **손가락으로 겨누는 자리의 바닥**이지 카드의 장식이 아니다. */
.pm-ask__head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  min-height: 2.75rem;
  padding: 0.5rem 0.6rem;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.pm-ask__head:hover {
  background: color-mix(in srgb, var(--jsini-text) 6%, transparent);
}

/* 밀 수 있는 카드의 본문. **띠 위에 놓인다**(`z-index`) — 띠가 절대 자리라
   그대로 두면 문서에서 뒤인 띠가 본문을 덮어 제목이 초록에 가린다.

   `touch-action: pan-y` 가 이 기능의 핵심 한 줄이다. 세로 끌기는 브라우저가
   제 스크롤로 가져가고 **가로 끌기만 우리에게 온다** — 안 걸면 손가락을
   옆으로 밀어도 브라우저가 스크롤로 삼켜 아무 일도 일어나지 않거나,
   막아 버리면 이번엔 카드 위에서 목록을 위아래로 굴릴 수 없다. */
.pm-ask__item[data-swipe] > .pm-ask__head {
  position: relative;
  z-index: 2;
  touch-action: pan-y;
  transform: translateX(var(--pm-ask-swipe));
  transition: transform var(--pm-ask-swipe-ms) ease;
}

/* 움직임을 줄여 달라고 한 사람에게는 **결과만** 준다. 카드는 그 자리에서
   바로 사라지고 띠는 열리지 않는다 — 확인이 됐다는 것은 카드가 목록에서
   빠지는 것으로 이미 말한다. */
@media (prefers-reduced-motion: reduce) {
  .pm-ask__item[data-swipe],
  .pm-ask__item[data-swipe] > .pm-ask__head,
  .pm-ask__ack,
  .pm-ask__ack-mark,
  .pm-ask__ack-text,
  .pm-ask__del,
  .pm-ask__del-mark,
  .pm-ask__del-text {
    transition: none;
  }
}

/* ── 모바일 작업 목록 (`pm-ai-mobile-list`) ─────────────────── */
.pm-ai-mobile-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.pm-ai-mobile-list > .pm-ask__list {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.pm-ai-mobile-bar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: 0.25rem 0;
}

/* 두 층이 들어가는 칸. **`min-width: 0` 이 있어야 한다** — 없으면 안쪽
   제목이 자기 글자 길이만큼 칸을 밀어내 얼굴이 카드 밖으로 밀린다
   (flex 항목의 기본 최소 너비가 `auto` 다). */
.pm-ask__rows {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
}

/* ── 사람의 얼굴 (`pm-face`) ─────────────────────────────────

   누가 시킨 건인지를 **글자가 아니라 얼굴로** 말한다. 이 화면은 여럿이
   같은 목록을 보므로(서버가 「내 것만」으로 가리지 않는다 — `LoadRecentAsync`)
   카드마다 지시자가 다르다.

   **카드와 상세가 같은 동그라미를 쓴다**(`UserFaceMark`). 그래서 이름이
   `pm-ask__…` 가 아니다 — 「빠른 지시」 화면의 조각이 아니라 그 화면과
   상세가 나눠 쓰는 부품이다.

   **줄어들지 않는다**(`flex: 0 0 auto`). 반 폭 카드에서 제목이 길 때
   이것이 먼저 눌리면 동그라미가 타원이 된다. 카드에서 잘려도 되는 것은
   제목 꼬리 하나뿐이다(목록 머리말).

   사진이 없으면 이름 첫 글자다. 바탕을 옅게 깔아 **빈자리가 아니라는
   것**을 보인다 — 상태 띠 색(`--pm-ask-tone`)을 쓰지 않는다. 그 색은
   「어떻게 됐나」를 말하는 자리라, 얼굴까지 같은 색으로 칠하면 사람이
   상태의 일부로 읽힌다. */
.pm-face {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  overflow: hidden;
  background: color-mix(in srgb, var(--jsini-text) 10%, transparent);
  color: var(--jsini-text);
}

/* 상세의 머리띠에 서는 것. **카드보다 작다** — 옆에 이름이 함께 서고
   그 줄은 글 한 줄 높이라, 카드만 한 동그라미를 넣으면 그것 하나 때문에
   머리띠가 한 단 두꺼워진다. */
.pm-face--lg {
  width: 1.5rem;
  height: 1.5rem;
}

.pm-face__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.pm-face__initial {
  font-size: 0.75em;
  font-weight: 700;
  line-height: 1;
  opacity: 0.8;
}

/* 첫 층 — **상태와 총작업시간.** 「끝났나 · 얼마나 걸렸나」가 한 줄이다.
   양 끝으로 밀어 두면 반 폭 카드에서도 둘이 안 붙는다. */
.pm-ask__line {
  display: flex;
  gap: 0.4rem;
  align-items: baseline;
  justify-content: space-between;
}

/* 둘째 층 — **실행일시와 제목.** 시각이 앞에 서고 제목이 남은 폭을 쓴다.

   시각을 앞에 두는 이유는 그것이 **자리가 고정된 값**이기 때문이다. 뒤에
   두면 제목 길이에 따라 시각의 x 좌표가 카드마다 달라져, 넉 장을 세로로
   훑을 때 눈이 그 값을 따라다녀야 한다. */
.pm-ask__line--sub {
  gap: 0.35rem;
  justify-content: flex-start;
  min-width: 0;
}

/* 상태 글자. 띠와 같은 색을 쓴다(`--pm-ask-tone`). */
.pm-ask__state {
  font-size: 0.8em;
  font-weight: 700;
  color: var(--pm-ask-tone);
  white-space: nowrap;
}

/* 총작업시간. 표제를 달지 않는다 — 「3분 12초」는 그 자리에 무엇이 적혀
   있는지 이미 말한다. 자릿수가 흔들리지 않게 고정폭 숫자를 쓴다.

   **왼쪽 여백을 자동으로 민다.** 윗층이 셋이 되면서(배지 · 상태 · 시간)
   `space-between` 만으로는 상태가 가운데로 떠 배지와 갈라졌다 — 그 둘은
   「어느 AI 가 · 어떻게 됐나」로 붙어 읽히는 짝이다. `auto` 여백이
   `justify-content` 보다 먼저 남는 폭을 먹으므로 앞의 둘이 왼쪽에 모인다. */
.pm-ask__span {
  margin-left: auto;
  font-size: 0.85em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  opacity: 0.85;
}

/* ── 어느 AI 에게 간 건인가 (`pm-ai`) ───────────────────────

   한 글자짜리 칩이다. 왜 한 글자인지는 `AiRunnerMark` 머리말에 있다 —
   **가르는 것은 글자가 아니라 색**이고, 그 색은 각 AI 의 심볼색이다.

   상태 띠(`--pm-ask-tone`)와 섞이지 않게 **칠하는 방식을 달리했다.**
   상태는 글자와 왼쪽 띠에만 색이 있고(면은 안 칠한다), 이쪽은 면을 칠한
   칩이다. 같은 카드 안의 두 색이 서로 다른 것을 말하므로 모양으로도
   갈라 둔다.

   `flex: 0 0 auto` 는 있어야 한다 — 없으면 반 폭 카드에서 제목이 길 때
   이 칩이 먼저 눌려 찌그러진다. 한 글자라 `min-width` 로 동그라미에 가까운
   너비를 잡아 둔다(자리가 고정된 값이라 카드를 세로로 훑을 때 상태 글자의
   x 좌표가 건마다 흔들리면 안 된다).

   **카드(`pm-ask__item`)와 들여다보는 창(`pm-peek__top`)이 같이 쓴다.**
   그래서 이름에 어느 한쪽의 블록을 달지 않았다 — 카드에서 색으로 알아본
   건을 눌러 들어왔을 때 **같은 칩이 같은 모양으로 서 있어야** 「그 건이
   맞다」가 눈으로 확인된다(얼굴을 두 곳에 같이 세우는 것과 같은 까닭이다). */
.pm-ai {
  --pm-ai: var(--jsini-text-muted);
  --pm-ai-ink: #ffffff;

  flex: 0 0 auto;
  align-self: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.15rem;
  height: 1.15rem;
  padding: 0 0.2rem;
  border-radius: 0.3rem;
  background: var(--pm-ai);
  color: var(--pm-ai-ink);
  font-size: 0.7em;
  font-weight: 800;
  line-height: 1;
}

/* Claude — Anthropic 의 흙빛 주황. */
.pm-ai--claude { --pm-ai: #d97757; }

/* 안티그래비티 — 구글의 파랑. */
.pm-ai--antigravity { --pm-ai: #4285f4; }

/* Copilot — 깃허브의 먹빛.

   **어두운 테마에서 뒤집는다.** 어두운 판에 검은 칩을 올리면 배지가 통째로
   사라져 「AI 가 안 적혀 있다」로 보인다. 깃허브가 제 마크를 다루는 방식과
   같게 바탕과 글자를 맞바꾼다. */
.pm-ai--copilot { --pm-ai: #24292f; }

:root[data-theme='dark'] .pm-ai--copilot {
  --pm-ai: #d0d7de;
  --pm-ai-ink: #16181d;
}

/* 공통코드에 새로 생긴 AI. 색을 정해 준 적이 없으므로 흐림으로 둔다 —
   **배지 자리가 비는 것보다 낫다**(`AiRunnerMark` 머리말). */
.pm-ai--etc { --pm-ai: var(--jsini-text-muted); }

/* 들여다보는 창의 머리띠에 서는 칩. **한 치수 크다** — 머리띠의 글자가
   카드보다 크고(`pm-peek__value`), 거기에 카드 치수의 칩을 그대로 세우면
   라벨 사이에 낀 점처럼 보인다.

   `em` 이 아니라 `rem` 으로 적는다. 머리띠는 `font-size` 를 따로 안 주는
   자리라 `em` 으로 두면 테마의 바탕 글자 크기에 따라 칩만 들쭉날쭉해진다. */
.pm-ai--lg {
  min-width: 1.5rem;
  height: 1.5rem;
  padding: 0 0.3rem;
  border-radius: 0.35rem;
  font-size: var(--jsini-fs-xs);
}

/* 실행일시. 제목보다 한 단 흐리다 — 자리가 고정된 값이라 눈에 띌 필요가
   없고, 같은 굵기로 두면 제목과 다툰다. */
.pm-ask__when {
  font-size: 0.8em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  opacity: 0.65;
}

.pm-ask__target {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 40%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.8em;
  opacity: 0.75;
}

.pm-ask__target-kind {
  font-weight: 700;
}

/* 아직 안 읽은 남긴말. **내가 올린 요청 카드에만 선다**(`IsMine`).

   「AI 작업 요청」 목록의 것과 같은 모양이다(`pm-req__new`) — 두 화면에서
   같은 것을 세는 배지라, 색이 갈리면 같은 값을 둘로 읽게 된다.

   반 폭 카드라 **줄이지 않는다**(`flex: 0 0 auto`). 줄어도 되는 것은 제목
   쪽이고, 그쪽은 … 로 잘려도 앞머리가 남는다. */
.pm-ask__new {
  flex: 0 0 auto;
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  font-size: 0.75em;
  font-weight: 700;
  white-space: nowrap;
  color: #fff;
  background: #d32f2f;
}

/* 제목. 남은 폭을 다 쓰고 넘치면 … 로 자른다. **여기만 잘린다** —
   잘려도 무엇을 시켰는지의 앞머리는 남고, 전문은 눌러서 본다. */
.pm-ask__title {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.9em;
  font-weight: 600;
}

/* 목록 아래 「더보기」 줄. **격자 밖이다** — 안에 넣으면 반 폭 칸 하나를
   차지해 옆에 빈자리가 생긴다.

   더보기가 폭을 다 쓴다. 흔들리는 자리에서 누른다는 이유가 위 보내기 단추와
   같고, 남은 건수가 거기 적히므로 글자도 길다. */
.pm-ask__more {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

.pm-ask__morego {
  flex: 1 1 auto;
  min-height: 2.75rem;
  padding-inline: 1.25rem;
  font-weight: 600;
}

/* 접기. **더보기보다 흐리고 좁다** — 되돌리는 길이지 권하는 길이 아니다.
   혼자 남는 때(다 펼친 뒤)가 있어 그때는 제 폭만 쓰고 왼쪽에 선다. */
.pm-ask__fold {
  flex: 0 0 auto;
  min-height: 2.5rem;
  opacity: 0.75;
}

.pm-ask__more-sentinel {
  display: block;
  height: 1px;
  pointer-events: none;
}

/* ── AI 작업 요청 (`pm-req`) ──────────────────────────────────

   일반 사용자가 지시를 적어 두는 자리다(`AiRequestList`). 「빠른 지시」와
   **모양은 닮았고 하는 일이 다르다** — 저쪽은 적는 즉시 돌지만 이쪽은
   쌓아 두기만 한다.

   그래서 클래스를 `pm-ask__…` 로 함께 쓰지 않는다. 지금은 규칙이 겹쳐
   보이지만, 저쪽은 **흔들리는 손으로 한 줄 던지는 화면**이라 앞으로도 칸을
   줄이는 쪽으로 움직이고 이쪽은 **앉아서 길게 적는 화면**이라 반대쪽으로
   움직인다. 한 벌을 나눠 쓰면 그 두 방향이 서로를 갉는다.

   판 하나짜리 세로 배치다. 넓은 화면에서 글상자가 끝까지 늘어나지 않게
   읽기 좋은 폭에서 멈춘다.
   ---------------------------------------------------------- */

.pm-req {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-width: 56rem;
}

/* 칸 하나 — 이름과 입력이 위아래로 선다. 이름을 왼쪽에 두는 두 칸짜리
   격자로 하지 않는 이유는, 본문 글상자가 열 줄짜리라 그 옆의 이름 한 줄이
   허공에 뜨기 때문이다. */
.pm-req__field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin-bottom: 0.9rem;
}

.pm-req__label {
  font-size: 0.85em;
  font-weight: 600;
  color: var(--jsini-text-muted);
}

.pm-req__title {
  width: 100%;
}

/* 본문. **「빠른 지시」와 달리 높이를 조이지 않는다** — 그쪽은 모바일 자판이
   올라와도 단추가 안 가려지게 30vh 로 막아 두었지만, 여기는 앉아서 길게
   적는 자리라 열 줄을 그대로 쓴다. */
.pm-req__text {
  width: 100%;
}

.pm-req__text textarea {
  min-height: 12rem;
  font-size: var(--jsini-fs-md);
  line-height: 1.5;
}

/* 손가락으로 쓰는 기기에서만 16px 바닥을 둔다. (iOS 확대 방지) */
@media (pointer: coarse) {
  .pm-req__text textarea {
    font-size: max(var(--jsini-fs-md), 1rem);
  }
}

/* 단추 줄. 저장이 오른쪽 끝이고(`margin-left:auto`) 취소·거둬들이기가
   왼쪽에 선다 — 되돌리는 단추와 진행하는 단추가 손가락 하나 거리에 붙어
   있으면 안 된다.

   좁아지면 저장이 제 줄을 다 쓴다. 한 줄을 혼자 쓰면서 오른쪽에만 붙어
   있으면 왼쪽 절반이 빈 채로 남는다. */
.pm-req__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
}

.pm-req__save {
  min-height: 2.6rem;
  padding-inline: 1.25rem;
  margin-left: auto;
  font-weight: 600;
}

.pm-req__cancel,
.pm-req__drop {
  min-height: 2.6rem;
}

/* 왜 못 고치는지 적는 한 줄. 회색 단추만 두면 「고장」으로 읽힌다. */
.pm-req__why {
  flex: 1 1 14rem;
  font-size: 0.9em;
  color: var(--jsini-text-muted);
}

@media (width <= 30rem) {
  .pm-req__save {
    flex: 1 1 100%;
    margin-left: 0;
  }
}

/* ── 내가 올린 것 ──────────────────────────────────────────── */

/* **줄로 쌓는다.** 「빠른 지시」는 반 폭 카드 둘씩이지만 그쪽은 「갔나·끝났나」
   넉 장을 한눈에 넣으려는 자리다. 여기는 제목이 곧 「내가 무엇을 부탁했나」라
   잘리면 고를 수가 없다 — 폭을 다 준다. */
.pm-req__list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
}

/* 왼쪽 띠가 단계를 말한다. 배지 글자와 같은 색을 쓰므로 훑을 때는 색으로,
   확인할 때는 글자로 읽는다. */
.pm-req__item {
  --pm-req-tone: var(--jsini-text-muted);

  border: 1px solid var(--jsini-border, rgba(0, 0, 0, 0.12));
  border-left: 0.35rem solid var(--pm-req-tone);
  border-radius: 6px;
  overflow: hidden;
}

.pm-req__item--on { --pm-req-tone: #2e7d32; }
.pm-req__item--warn { --pm-req-tone: #b26a00; }
.pm-req__item--err { --pm-req-tone: #d32f2f; }
.pm-req__item--off { --pm-req-tone: var(--jsini-text-muted); }

/* 줄 전체가 누르는 자리다. `<button>` 이라 자판으로도 닿는다 —
   `<li>` 에 `@onclick` 을 걸면 탭으로 옮겨 갈 수 없다. */
.pm-req__head {
  display: block;
  width: 100%;
  padding: 0.55rem 0.75rem;
  background: none;
  border: none;
  text-align: left;
  cursor: pointer;
  color: inherit;
  font: inherit;
}

.pm-req__head:hover {
  background: var(--jsini-bg-subtle, rgba(0, 0, 0, 0.04));
}

/* 지금 위 판에 실려 있는 줄. **어느 것을 고치는 중인지**를 목록에서도
   보여 준다 — 스크롤해 내려오면 위 판의 제목이 화면 밖이다. */
.pm-req__head--picked {
  background: var(--jsini-bg-subtle, rgba(0, 0, 0, 0.06));
  box-shadow: inset 0 0 0 1px var(--pm-req-tone);
}

.pm-req__rows {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.pm-req__line {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  min-width: 0;
}

.pm-req__line--sub {
  font-size: 0.95em;
}

/* 단계 글자. 띠와 같은 색을 쓴다. */
.pm-req__stage {
  flex: 0 0 auto;
  font-size: 0.85em;
  font-weight: 700;
  color: var(--pm-req-tone);
}

.pm-req__when {
  flex: 0 0 auto;
  font-size: 0.8em;
  color: var(--jsini-text-muted);
}

/* 제목. 한 줄에서 넘치면 꼬리를 자른다 — 전문은 눌러서 본다. */
.pm-req__title-text {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 목록 아래 「더보기」 줄. 다섯 줄 밖으로 밀린 것을 여기서 꺼낸다.

   더보기가 폭을 다 쓴다 — 남은 건수가 거기 적혀 글자가 길고, 휴대폰에서
   목록 바로 아래에 오는 단추라 손가락이 닿는 넓이가 필요하다.
   「빠른 지시」의 같은 줄(`pm-ask__more`)과 규칙이 같다. */
.pm-req__more {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

.pm-req__morego {
  flex: 1 1 auto;
  min-height: 2.5rem;
}

/* 접기. **더보기보다 흐리고 좁다** — 되돌리는 길이지 권하는 길이 아니다.
   혼자 남는 때(다 펼친 뒤)가 있어 그때는 제 폭만 쓰고 왼쪽에 선다. */
.pm-req__fold {
  flex: 0 0 auto;
  min-height: 2.5rem;
  opacity: 0.75;
}

/* 목록 줄의 남긴말 배지.

   **안 읽은 것이 있을 때와 없을 때가 다른 글자다.** 상태 배지(`pm-req__stage`)
   로는 답할 수 없는 물음이라 따로 세운다 — 관리자가 말만 남기고 아직 대상을
   안 채웠으면 상태는 그대로 「접수 대기」다.

   색을 새로 만들지 않는다. 안 읽은 것은 줄의 색띠와 무관하게 **눈에 걸려야**
   하므로 채운 칩이고, 다 읽은 것은 곁글이라 흐린 글자만 남긴다. */
.pm-req__new,
.pm-req__notes {
  flex: 0 0 auto;
  margin-left: 0.4rem;
  font-size: 0.75em;
  white-space: nowrap;
}

.pm-req__new {
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  font-weight: 700;
  color: #fff;
  background: #d32f2f;
}

.pm-req__notes {
  color: var(--jsini-text-muted);
}

/* 남긴 말 판을 담는 자리. 위아래 판과 같은 간격을 쓰되 **왼쪽에 띠를 둔다** —
   이 판만 「고른 건 하나에 딸린 것」이라 목록·적는 자리와 성격이 다르다. */
.pm-req__notepane {
  border-left: 0.2rem solid color-mix(in srgb, var(--jsini-accent) 55%, transparent);
  padding-left: 0.75rem;
}

/* ── 오간 남길말 (`pm-note`) ─────────────────────────────────

   요청 하나에 관리자와 올린 사람이 번갈아 남기는 말이다(`AiTaskNotes`).
   **서는 자리가 둘이고 모양은 하나다** — 「AI 작업 요청」 화면의 판과
   관리자 상세(창·탭)의 「지시와 결과」 판 끝.

   대화라서 **말풍선**이다. 표로 두면 누가 누구에게 한 말인지를 글자로만
   읽어야 하는데, 그 판단이 이 화면에서 가장 잦다.

   왼쪽이 올린 사람, 오른쪽이 관리자다. **로그인한 사람으로 가르지 않는다** —
   가르면 같은 대화가 두 화면에서 좌우가 뒤집혀 보인다(그 부품 머리말).
   ---------------------------------------------------------- */

.pm-note {
  margin-top: 1rem;
}

.pm-note__head {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0 0 0.5rem;
  font-size: 0.95em;
  font-weight: 700;
}

.pm-note__count {
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  font-size: 0.8em;
  font-weight: 600;
  color: var(--jsini-text-muted);
  border: 1px solid var(--jsini-border);
}

.pm-note__empty {
  margin: 0 0 0.75rem;
  color: var(--jsini-text-muted);
}

.pm-note__list {
  list-style: none;
  margin: 0 0 0.75rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

/* 줄 하나. 얼굴과 말풍선이 가로로 서고, 관리자 줄은 **차례를 뒤집는다**. */
.pm-note__item {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  max-width: 100%;
}

.pm-note__item--admin {
  flex-direction: row-reverse;
}

/* 말풍선. **폭을 다 쓰지 않는다** — 좌우 차이가 보이려면 반대편에 빈자리가
   남아 있어야 한다. 휴대폰에서는 그 여백이 아까우므로 아래에서 넓힌다. */
.pm-note__bubble {
  flex: 0 1 auto;
  max-width: 85%;
  padding: 0.45rem 0.65rem;
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-md);
  background: color-mix(in srgb, var(--jsini-surface) 70%, transparent);
}

/* 관리자 줄은 옅게 칠한다. 테두리만으로 가르면 어두운 테마에서 둘이 같아진다. */
.pm-note__item--admin .pm-note__bubble {
  background: color-mix(in srgb, var(--jsini-accent) 10%, var(--jsini-surface));
}

.pm-note__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 0.2rem;
  font-size: 0.78em;
  color: var(--jsini-text-muted);
}

.pm-note__who {
  font-weight: 600;
  color: var(--jsini-text);
}

/* 아직 안 읽은 줄. **관리자 쪽에서 뜻이 있다** — 「내 말이 닿았나」에 답한다. */
.pm-note__unread {
  padding: 0.02rem 0.35rem;
  border-radius: 999px;
  font-weight: 700;
  color: #fff;
  background: #b26a00;
}

/* 본문. 적은 그대로 편다 — 상자 안이 구르지 않는다(`pm-peek__pre` 와 같은 규칙). */
.pm-note__text {
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;
  line-height: 1.5;
  font-family: inherit;
  font-size: 0.92em;
  color: var(--jsini-text);
}

/* 적는 줄. 글상자가 남는 폭을 다 먹고 단추는 오른쪽 아래에 붙는다. */
.pm-note__write {
  display: flex;
  align-items: flex-end;
  gap: 0.5rem;
}

.pm-note__box {
  flex: 1 1 auto;
  min-width: 0;
}

.pm-note__save {
  flex: 0 0 auto;
  min-height: 2.5rem;
}

/* 좁은 화면에서는 단추가 아래로 내려가 폭을 다 쓴다 — 글상자 옆에 두면
   둘 다 손가락이 닿기 어려운 넓이가 된다. */
@media (max-width: 40rem) {
  .pm-note__bubble {
    max-width: 92%;
  }

  .pm-note__write {
    flex-direction: column;
    align-items: stretch;
  }
}

/* ── 들여다보는 창 (`pm-peek`) ───────────────────────────────

   카드를 누르면 뜨는 창이다(`AiTaskPeek`). 위에서부터 **머리띠 → 처리 요약
   → 탭 줄**이고, 탭이 둘이다 — 「지시와 결과」(지시 → 결과)와 「로그」.
   왜 지시와 결과를 한 판에 두는지, 왜 요약만 탭 밖으로 올렸는지는
   `AiTaskView.razor` 의 머리말에 적어 두었다.

   **휴대폰에서는 창이 화면을 꽉 채운다.**
   ---------------------------------------------------------- */

/* 머리띠 — 상태 · AI 칩 · 지시자 · 시각 셋. 탭을 옮겨도 제자리에 남는다.

   **라벨이 붙는 것은 시각 셋뿐이다.** 상태·AI·지시자는 값 자신이 무엇인지를
   말하므로(배지 글자 · 색 칩 · 얼굴) 이름표를 달면 그만큼 칸만 먹는다.

   **가운데로 맞춘다 — 밑줄(`baseline`)이 아니다.** 한동안 밑줄 맞춤이었는데,
   머리띠에 선 것들이 **밑줄을 가진 것과 안 가진 것으로 갈려 있어** 넷이
   계단처럼 어긋났다(재어 본 값 — 위끝이 상태 20 · AI 칩 21.1 · 얼굴 22.3 ·
   시각 23px). 까닭이 자리마다 다르다.

   | | 왜 어긋났나 |
   |---|---|
   | 상태 배지 | 제 글자의 밑줄로 선다. 위아래 여백이 있어 상자가 가장 두껍다 |
   | AI 칩 | `align-self: center` 라 **줄 전체**의 가운데에 선다 — 밑줄과 무관하다 |
   | 지시자 | 안쪽이 가운데 맞춤이라 **칸 자신의 밑줄**이 아래로 내려가 있다 |
   | 시각 셋 | 라벨(작은 글자)과 값이 밑줄을 맞춘 그 줄이 칸의 밑줄이다 |

   밑줄로는 이 넷을 한 줄에 세울 수가 없다 — 동그라미와 색 칩에는 밑줄이
   없고, 없는 것에 억지로 밑줄을 만들어 주면 그 값이 글꼴에 따라 흔들린다.
   가운데로 맞추면 높이가 제각각이어도 **한가운데 선 하나**에 모두 꿰인다
   (재어 확인 — 넷의 가운데가 33.02~33.03px). 칸 **안쪽**은 그대로 밑줄
   맞춤이다(라벨과 값은 글자끼리라 밑줄이 맞는 쪽이 옳다). */
.pm-peek__top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.9rem;
  margin-bottom: 0.6rem;
}

/* 창에서는 표제를 단다. 카드와 달리 자리가 넉넉하고, 값 둘이 나란히 서면
   어느 쪽이 어느 것인지 한 번은 읽어야 하기 때문이다. */
.pm-peek__cell {
  display: inline-flex;
  align-items: baseline;
  gap: 0.3rem;
}

.pm-peek__label {
  font-size: var(--jsini-fs-2xs);
  opacity: 0.6;
}

/* 지시자 칸은 **안쪽도 가운데 맞춤**이다. 다른 칸은 라벨과 값이 글자끼리라
   밑줄을 맞추는데, 여기는 동그라미와 이름이 나란히 서서 밑줄로 두면 얼굴이
   제 아래끝을 글자 밑줄에 대고 위로 솟는다. */
.pm-peek__cell--who {
  align-items: center;
  gap: 0.35rem;
}

.pm-peek__value {
  font-size: var(--jsini-fs-sm);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.pm-peek__pane {
  padding-top: 0.6rem;
}

.pm-peek__dim {
  margin: 0 0 0.5rem;
  opacity: 0.7;
}

/* 글 상자 셋(지시 · 결과 · 로그)이 같은 몸가짐을 쓴다.

   **상자에는 높이 상한이 없다**(로그만 예외 — 아래). 한동안 있었는데
   (지시 11rem · 결과 22rem) 그러면 한 판 안에 구르는 자리가 셋이 된다 —
   바깥 판 · 지시 상자 · 결과 상자. 휴대폰에서 손가락이 상자 위에 있으면
   판이 안 움직이고(스크롤 체이닝), 22rem 짜리 구멍으로 보는 긴 결과문은
   **끝까지 읽었는지 알 수 없다.**

   길면 판이 길어지게 둔다. 판은 창이든 화면이든 이미 구르는 자리가
   하나씩 있다 — 창은 `CommPopup` 의 `Scrollable`, 화면은 페이지 그대로. */
.pm-peek__pre {
  margin: 0 0 0.5rem;
  padding: 0.6rem 0.75rem;
  white-space: pre-wrap;
  word-break: break-word;
  line-height: 1.5;
  color: var(--jsini-text);
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-md);
}

/* 지시와 결과는 사람이 읽는 글이다. 바탕도 본문과 같게. */
.pm-peek__pre--ask,
.pm-peek__pre--result {
  font-size: var(--jsini-fs-sm);
  background: var(--jsini-bg);
}

/* 한 판 안의 칸 이름 — 「처리 요약」 · 「지시」 · 「결과」.

   셋이 한 판에 이어 서므로 **어디서 어디까지가 무엇인지**를 이 줄이
   혼자 말한다. 크게 키우지 않는다 — 제목이 글보다 눈에 띄면 훑는 눈이
   제목만 타고 내려간다. */
.pm-peek__head {
  margin: 0.9rem 0 0.35rem;
  font-size: var(--jsini-fs-xs);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--jsini-text-muted);
}

/* 판의 첫 칸(요약이 있으면 「처리 요약」, 없으면 「지시」)은 위 여백이
   필요 없다. 탭 몸통이 이미 띄워 준다. */
.pm-peek__pane > .pm-peek__head:first-child,
.pm-peek__pane > .pm-peek__head-row:first-child {
  margin-top: 0;
}

/* 머리말과 보조 단추(음성 읽기 등)가 한 줄에 나란히 설 때 */
.pm-peek__head-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin: 0.9rem 0 0.35rem;
}

.pm-peek__head-row > .pm-peek__head {
  margin: 0;
}

/* ── 접는 머리 (`pm-peek__fold`) ─────────────────────────────

   「처리 요약」의 제목이 곧 접는 단추다(2026-10-12). **제목째로 단추에
   담는다** — 제목 옆에 화살표만 따로 세우면 누를 자리가 손가락보다 작아
   휴대폰에서 두세 번 빗나간다.

   단추처럼 보이지 않게 한다. 이 줄은 여전히 칸 이름이고, 테두리와 바탕이
   붙는 순간 아래 글보다 먼저 읽힌다 — `.pm-peek__head` 를 작고 흐리게
   둔 까닭과 같다. 손가락이 올라왔을 때만 글자가 진해진다. */
.pm-peek__fold {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  cursor: pointer;
  user-select: none;
}

.pm-peek__fold:hover,
.pm-peek__fold:focus-visible {
  color: var(--jsini-text);
}

/* 화살표. **글자 하나를 돌려 쓴다** — ▸ 와 ▾ 를 갈아 끼우면 그 사이에
   글자폭이 달라져 제목이 좌우로 흔들린다. */
.pm-peek__fold-mark {
  display: inline-block;
  flex: 0 0 auto;
  width: 0.7rem;
  font-size: var(--jsini-fs-2xs, 0.75rem);
  line-height: 1;
  transition: transform 0.15s ease-in-out;
}

.pm-peek__fold-mark--open {
  transform: rotate(90deg);
}

/* 음성으로 읽어주기 단추 */
.pm-peek__speech-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.18rem 0.5rem;
  font-size: var(--jsini-fs-2xs, 0.75rem);
  font-weight: 500;
  color: var(--jsini-accent, #0078d4);
  background: var(--jsini-surface, #fff);
  border: 1px solid var(--jsini-border, #d1d5db);
  border-radius: var(--jsini-radius-sm, 4px);
  cursor: pointer;
  line-height: 1.4;
  user-select: none;
  transition: all 0.15s ease-in-out;
}

.pm-peek__speech-btn:hover:not(:disabled) {
  background: var(--jsini-accent-soft, rgba(0, 120, 212, 0.1));
  border-color: var(--jsini-accent, #0078d4);
}

.pm-peek__speech-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.pm-peek__speech-btn--speaking {
  color: #fff;
  background: var(--jsini-danger, #d13438);
  border-color: var(--jsini-danger, #d13438);
  animation: pm-peek-pulse 1.5s infinite;
}

.pm-peek__speech-btn--speaking:hover {
  background: var(--jsini-danger-hover, #a80000);
  border-color: var(--jsini-danger-hover, #a80000);
}

.pm-peek__speech-icon {
  font-size: 0.85rem;
  line-height: 1;
}

@keyframes pm-peek-pulse {
  0% { box-shadow: 0 0 0 0 rgba(209, 52, 56, 0.4); }
  70% { box-shadow: 0 0 0 5px rgba(209, 52, 56, 0); }
  100% { box-shadow: 0 0 0 0 rgba(209, 52, 56, 0); }
}

/* ── 함께 보낸 파일 (`pm-peek__files`) ───────────────────────

   지시와 결과 사이다. **줄로 쌓는다** — 「빠른 지시」의 칩은 적는 중에
   곁눈으로 보는 것이라 작고 촘촘하지만, 여기는 「무엇을 보고 시킨
   건이었나」를 되짚는 자리라 이름이 잘리면 안 된다.

   그림을 크게 깔지 않는다. 이 판에는 지시·결과·로그가 함께 있어서,
   사진 다섯 장을 펴 놓으면 정작 결과가 화면 밖으로 밀린다 — 원본은
   눌러서 새 탭으로 연다.
   ---------------------------------------------------------- */

.pm-peek__files {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  margin: 0 0 0.5rem;
  padding: 0;
}

.pm-peek__file > a {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--jsini-border, rgba(0, 0, 0, 0.12));
  border-radius: 4px;
  color: inherit;
  text-decoration: none;
}

.pm-peek__file > a:hover {
  background: var(--jsini-bg-subtle, rgba(0, 0, 0, 0.04));
}

/* 미리보기. 칩보다 크게 두되 줄 높이를 지배하지 않을 만큼만 —
   「어느 화면이었나」가 떠오르면 그것으로 제 몫을 한다. */
.pm-peek__fileimg {
  width: 2.5rem;
  height: 2.5rem;
  flex: 0 0 auto;
  object-fit: cover;
  border-radius: 3px;
  background: var(--jsini-bg-subtle, rgba(0, 0, 0, 0.04));
}

/* 이름이 자리를 다 쓰고 크기가 오른쪽 끝에 붙는다. */
.pm-peek__filename {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pm-peek__filesize {
  flex: 0 0 auto;
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-xs);
}

/* 처리 요약을 담는 칸 — **탭 줄 위다**(2026-10-11).

   한동안 「지시와 결과」 탭 안의 맨 위였다. 로그를 펴면 요약이 통째로
   사라져서, 돌고 있는 건을 지켜보는 사람이 결론을 보려면 탭을 되돌아가야
   했다(`AiTaskView.razor` 의 그 자리 머리말).

   **머리띠와 탭 줄 사이에 선다.** 아래 여백은 머리띠가 탭 줄에 두던 것과
   같은 값이라(`.pm-peek__top` 의 `0.6rem`), 요약이 없는 건에서는 머리띠가
   탭 줄에 그대로 붙어 고치기 전과 같은 모습이 된다. */
.pm-peek__summary {
  margin-bottom: 0.6rem;
}

/* 첫 줄은 위 여백이 필요 없다 — 머리띠가 이미 띄워 준다.
   (탭 안에서 같은 몫을 하던 규칙이 `.pm-peek__pane > …:first-child` 다.) */
.pm-peek__summary > .pm-peek__head-row:first-child {
  margin-top: 0;
}

/* 처리 요약 — 그 칸의 알맹이.

   **이 칸만 상자가 아니라 띠다.** 아래 탭 안의 글 상자들(테두리+바탕)과
   같은 몸가짐이면 구별되지 않는다. 왼쪽에 선을 세워 「이건 원문이 아니라
   정리한 말」임을 한눈에 가른다. */
.pm-peek__sum {
  margin: 0 0 0.5rem;
  padding: 0.6rem 0.75rem;
  background: var(--jsini-accent-soft, var(--jsini-surface));
  border-left: 3px solid var(--jsini-accent);
  border-radius: 0 var(--jsini-radius-md) var(--jsini-radius-md) 0;
}

/* 결론 한 문장. 요약 안에서 이것만 굵다. */
.pm-peek__sum-line {
  margin: 0;
  font-size: var(--jsini-fs-sm);
  font-weight: 600;
  line-height: 1.5;
  color: var(--jsini-text);
}

.pm-peek__sum-list {
  margin: 0.4rem 0 0;
  padding-left: 1.1rem;
  font-size: var(--jsini-fs-sm);
  line-height: 1.55;
  color: var(--jsini-text);
}

.pm-peek__sum-list li + li {
  margin-top: 0.15rem;
}

/* 「확인할 것」 머리말. 사람이 이어서 해야 할 일이라 색을 달리 준다 —
   한 일 목록과 섞여 읽히면 그 칸이 있으나 마나다. */
.pm-peek__sum-label {
  margin: 0.6rem 0 0;
  font-size: var(--jsini-fs-2xs);
  font-weight: 600;
  color: var(--jsini-warning);
}

.pm-peek__sum-list--check {
  color: var(--jsini-text);
}

/* ── 처리 파일 (`pm-peek__chg`) ──────────────────────────────

   **셋째 탭의 알맹이다**(2026-10-12). 하루는 요약 바로 아래의 묶음이라
   바깥 칸을 요약과 같은 것(`.pm-peek__summary`)으로 썼는데, 제 탭을 얻은
   뒤로는 탭 몸통(`.pm-peek__pane`)이 그 자리를 준다 — 여기서 꾸미는 것은
   목록과 줄뿐이다.

   요약처럼 왼쪽에 선을 세우지 않는다. 그 선은 「이건 AI 가 다시 쓴 말」이라는
   표시인데, 여기 적히는 것은 **고쳐 쓴 말이 아니라 git 이 센 사실**이다.
   줄마다 테두리 하나로 담담하게 쌓는다.
   ---------------------------------------------------------- */

.pm-peek__chg {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin: 0 0 0.5rem;
  padding: 0;
}

.pm-peek__chg-row {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  min-width: 0;
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--jsini-border, rgba(0, 0, 0, 0.12));
  border-radius: 4px;
  font-size: var(--jsini-fs-sm);
  line-height: 1.5;
  background: var(--jsini-surface);
}

/* 「외 N개」. 파일이 아니라 세어 둔 말이라 테두리를 빼고 흐리게 둔다. */
.pm-peek__chg-row--more {
  justify-content: center;
  border: 0;
  padding: 0.15rem 0;
  background: none;
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-xs);
}

/* 무엇을 당했나. **색이 먼저 읽히고 글자가 확인해 준다** — 지운 파일 한 줄이
   고친 파일 스무 줄 사이에 섞여 있을 때 눈이 그것부터 찾아야 한다.
   너비를 고정해 경로의 시작점이 줄마다 어긋나지 않게 한다. */
.pm-peek__chg-mark {
  flex: 0 0 auto;
  min-width: 3.1rem;
  text-align: center;
  padding: 0.05rem 0.3rem;
  border-radius: 3px;
  font-size: var(--jsini-fs-2xs);
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

.pm-peek__chg-mark--add {
  color: var(--jsini-success, #2b8a3e);
  background: var(--jsini-success-soft, rgba(43, 138, 62, 0.12));
}

.pm-peek__chg-mark--mod {
  color: var(--jsini-accent, #0078d4);
  background: var(--jsini-accent-soft, rgba(0, 120, 212, 0.12));
}

.pm-peek__chg-mark--del {
  color: var(--jsini-danger, #c92a2a);
  background: var(--jsini-danger-soft, rgba(201, 42, 42, 0.12));
}

.pm-peek__chg-mark--move {
  color: var(--jsini-warning, #e8590c);
  background: var(--jsini-warning-soft, rgba(232, 89, 12, 0.12));
}

/* 경로. **자르지 않고 접는다.**

   한 번 말줄임으로 줄여 봤다 — 앞자리(`web/src/Apps/…`)는 줄마다 되풀이되는
   글자라 거기를 접고 이름을 살리려고 `direction: rtl` 을 걸었는데, **그것이
   경로를 틀리게 그린다.** 방향을 뒤집으면 양 끝의 `/` 와 `...` 이 반대쪽으로
   옮겨 붙어 `web/src/…/Shared/` 가 **`/web/src/…/Shared`** 로,
   `.../Pages/` 가 **`/Components/Pages/...`** 로 나온다(헤드리스로 찍어
   확인했다). 있지도 않은 경로가 멀쩡한 얼굴로 서는 것이라, 자리를 아끼는
   값보다 나쁘다.

   그래서 폭이 모자라면 **두 줄로 접는다.** 배지는 첫 줄에 맞춰 선다
   (줄의 `align-items: baseline`). 제 탭 안이라 길어져도 밀어낼 것이 없다 —
   여기까지 건너온 사람이 보려는 것이 그 목록이다. */
.pm-peek__chg-path {
  min-width: 0;
  word-break: break-all;
  line-height: 1.5;
}

.pm-peek__chg-dir {
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-xs);
}

.pm-peek__chg-name {
  font-weight: 600;
  color: var(--jsini-text);
}

/* ── 휴대폰에서는 로그 상자를 화면 높이에 맞춘다 ─────────────

   이 상세를 보는 자리가 길이고, 거기서 결과문을 읽으려면 폭도 높이도
   전부다. 22rem 그대로 두면 **화면은 남는데 상자만 구른다.**

   한동안 이 상자 위에 창을 화면 가득 펴는 규칙이 함께 있었다
   (`.dxbl-popup.pm-peek` 의 `100vw`/`100dvh`). 휴대폰에서도 상세가 창이
   아니라 **판 안의 탭**으로 서게 되면서(2026-10-11, `AiAskPanel` 머리말)
   그 창 자체가 없어졌다 — 폭을 다투는 상대가 사라졌으니 띄울 것도 없다.

   지시와 결과는 여기서도 상한이 없다 — 판이 통째로 구른다. */
/* ── 일정표: 구글 캘린더 (`pm-gc-*`) ─────────────────────────

   화면 머리말([왜 구글 캘린더를 그대로 본떴나])에 까닭을 적어 두었다.
   여기서는 그 모양을 만드는 규칙만 둔다.

   **`@media` 로 휴대폰을 가르지 않는다.** 그릴지 말지는 화면이 정한다
   (`DxLayoutBreakpoint` → `_isPhone` → `pm-gc-month--phone`). 여기서 폭으로
   또 가르면 판정이 두 곳이 되고, 둘이 어긋나는 폭에서 **아무 규칙도 안
   걸린 격자**가 뜬다.

   예외가 하나 있다 — 왼쪽 기둥이 위로 올라가는 것(`pm-gc__body`)은 폭만
   보면 되는 일이라 `@media` 로 둔다. 어긋나도 줄이 바뀔 뿐이다.
   ---------------------------------------------------------- */

.pm-gc {
  /* 구글의 파랑. 오늘 동그라미 · 고른 보기 · 끌어 놓을 자리가 이 색이다. */
  --pm-gc-accent: #1a73e8;

  display: flex;
  flex-direction: column;
}

:root[data-theme='dark'] .pm-gc {
  /* 어두운 바탕에서 #1a73e8 은 글자를 못 읽게 한다 — 구글도 다크에서 올린다. */
  --pm-gc-accent: #8ab4f8;
}

/* ── 머리줄 ──────────────────────────────────────────────── */

.pm-gc__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--jsini-sp-sm);
  padding: var(--jsini-sp-sm) 0;
}

.pm-gc__today {
  height: 2.125rem;
  padding: 0 var(--jsini-sp-lg);
  font: inherit;
  color: var(--jsini-text);
  cursor: pointer;
  background: transparent;
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-full);
}

.pm-gc__today:hover {
  background: color-mix(in srgb, var(--jsini-text) 6%, transparent);
}

.pm-gc__arrows {
  display: flex;
  gap: var(--jsini-sp-3xs);
}

.pm-gc__arrow,
.pm-gc-mini__nav {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--jsini-text-muted);
  cursor: pointer;
  background: transparent;
  border: 0;
  border-radius: var(--jsini-radius-full);
}

.pm-gc__arrow {
  width: 2rem;
  height: 2rem;
}

.pm-gc__arrow:hover,
.pm-gc-mini__nav:hover {
  background: color-mix(in srgb, var(--jsini-text) 8%, transparent);
}

/* 꺾쇠는 아래를 보는 것 하나뿐이다 — 돌려서 좌우로 쓴다(`app.css` 머리말). */
.pm-gc__chev--prev { transform: rotate(90deg); }
.pm-gc__chev--next { transform: rotate(-90deg); }

.pm-gc__title {
  margin: 0;
  font-size: var(--jsini-fs-2xl);

  /* 구글의 제목은 굵지 않다. 굵게 하면 그 옆 단추들이 다 눌려 보인다. */
  font-weight: 400;
  white-space: nowrap;
}

.pm-gc__views {
  display: flex;
  margin-left: auto;
  overflow: hidden;
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-full);
}

.pm-gc__view {
  height: 2rem;
  padding: 0 var(--jsini-sp-md);
  font: inherit;
  color: var(--jsini-text);
  white-space: nowrap;
  cursor: pointer;
  background: transparent;
  border: 0;
}

.pm-gc__view:hover {
  background: color-mix(in srgb, var(--jsini-text) 6%, transparent);
}

.pm-gc__view--on {
  font-weight: 600;
  color: var(--pm-gc-accent);
  background: color-mix(in srgb, var(--pm-gc-accent) 14%, transparent);
}

/* 조회가 도는 동안의 실선. 자리를 늘 차지하고 색만 켜진다 —
   나타났다 사라지면 그때마다 아래가 2px 씩 들썩인다. */
.pm-gc__busy {
  position: relative;
  height: 2px;
  overflow: hidden;
}

.pm-gc__busy--on {
  background: color-mix(in srgb, var(--pm-gc-accent) 22%, transparent);
}

.pm-gc__busy--on::after {
  position: absolute;
  inset: 0;
  width: 35%;
  content: '';
  background: var(--pm-gc-accent);
  animation: pm-gc-slide 1.1s linear infinite;
}

@keyframes pm-gc-slide {
  from { transform: translateX(-100%); }
  to { transform: translateX(320%); }
}

/* ── 몸통과 왼쪽 기둥 ────────────────────────────────────── */

.pm-gc__body {
  display: flex;
  align-items: flex-start;
  gap: var(--jsini-sp-lg);
}

.pm-gc__rail {
  flex: 0 0 13.5rem;
  width: 13.5rem;
}

.pm-gc__main {
  flex: 1 1 auto;
  min-width: 0;
  outline: none;
}

/* 단축키를 받으려면 촛점이 여기 있어야 한다 — 어디에 있는지 보여 준다. */
.pm-gc__main:focus-visible {
  border-radius: var(--jsini-radius-md);
  outline: 2px solid var(--pm-gc-accent);
  outline-offset: 2px;
}

/* 넓은 화면에서는 기둥을 접을 이유가 없다 — 여는 단추를 숨기고 늘 펴 둔다.
   `<details>` 는 `open` 이 없어도 이렇게 열어 둘 수 있다. */
@media (min-width: 768px) {
  .pm-gc__rail > .pm-gc__rail-body {
    display: block !important;
  }

  .pm-gc__rail-summary {
    display: none;
  }
}

@media (max-width: 767px) {
  /* **`align-items` 를 같이 되돌린다.** 위의 `flex-start` 는 가로로 놓였을 때
     「위에 붙인다」는 뜻이지만, 여기서 줄이 세로로 서는 순간 같은 값이
     「내용만큼만 넓힌다」로 바뀐다. 그러면 달력 판(`pm-gc__main`)이 제 폭을
     못 받고, 월 격자는 `repeat(7, 1fr)` 이라 줄 폭이 없으면 글자 하나 너비로
     오그라든다 — 화면이 남아도는데 달력만 좁게 서 있던 까닭이다.
     왼쪽 기둥이 `width: 100%` 를 달고 있는 것도 같은 일을 겪은 자국이다. */
  .pm-gc__body {
    flex-direction: column;
    align-items: stretch;
  }

  .pm-gc__rail {
    flex: none;
    width: 100%;
    margin-bottom: var(--jsini-sp-sm);
    background: var(--jsini-surface-raised, var(--jsini-surface));
    border: 1px solid var(--jsini-border);
    border-radius: var(--jsini-radius-lg);
    box-shadow: var(--jsini-shadow-card, var(--jsini-shadow-sm));
  }

  /* 기본 삼각형을 지운다 — 오른쪽 꺾쇠가 그 일을 한다. `list-style` 만으로는
     웹킷에서 안 지워져 가짜 요소까지 함께 끈다. */
  .pm-gc__rail-summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.85rem;
    font-weight: 600;
    list-style: none;
    cursor: pointer;
  }

  .pm-gc__rail-summary::-webkit-details-marker {
    display: none;
  }

  .pm-gc__rail-chevron {
    transition: transform 0.2s;
  }

  .pm-gc__rail[open] .pm-gc__rail-chevron {
    transform: rotate(180deg);
  }

  .pm-gc__rail-body {
    padding: 0 0.85rem 0.85rem;
  }

  /* 판 안에 판이 겹친다 — 조건줄이 자기 테두리를 또 그리면 상자가 둘로 보인다. */
  .pm-gc__rail .commsch {
    padding-top: 0;
    margin-bottom: 0;
    border: none;
    box-shadow: none;
  }

  .pm-gc__title {
    font-size: var(--jsini-fs-xl);
  }

  /* 보기 고르개가 제목 아래 한 줄을 통째로 갖는다 — 다섯 칸이 제목 옆에
     끼면 둘 다 잘린다. */
  .pm-gc__views {
    width: 100%;
    margin-left: 0;
    justify-content: space-between;
  }

  .pm-gc__view {
    flex: 1;
    padding: 0 var(--jsini-sp-sm);
  }
}

/* 구글의 「만들기」. 이 화면에서 가장 자주 누르는 단추라 기둥 맨 위에 둔다. */
.pm-gc__create {
  display: inline-flex;
  gap: var(--jsini-sp-sm);
  align-items: center;
  height: 2.5rem;
  padding: 0 var(--jsini-sp-xl);
  margin-bottom: var(--jsini-sp-lg);
  font: inherit;
  font-weight: 600;
  color: var(--jsini-text);
  cursor: pointer;
  background: var(--jsini-surface-raised, var(--jsini-surface));
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-full);
  box-shadow: var(--jsini-shadow-sm);
}

.pm-gc__create:hover {
  box-shadow: var(--jsini-shadow-md);
}

/* 색이 무엇을 뜻하는지 적어 둔 줄. */
.pm-gc__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--jsini-sp-xs) var(--jsini-sp-md);
  padding: 0;
  margin: var(--jsini-sp-md) 0 0;
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
  list-style: none;
}

.pm-gc__legend li {
  display: flex;
  gap: var(--jsini-sp-2xs);
  align-items: center;
}

.pm-gc__dot {
  display: inline-block;
  width: 0.625rem;
  height: 0.625rem;
  border-radius: var(--jsini-radius-full);
}

/* 상태 넷의 색. 화면(`StateLabels`)과 **같은 값**이어야 한다 — 주·일 보기는
   부품이 그 값으로 칠하고, 월 보기는 여기 규칙으로 칠한다. */
.pm-gc__dot--READY,
.pm-gc-bar--READY { background: #f9ab00; }

.pm-gc__dot--RUNNING,
.pm-gc-bar--RUNNING { background: #1a73e8; }

.pm-gc__dot--OVER,
.pm-gc-bar--OVER { background: #d93025; }

.pm-gc__dot--COMP,
.pm-gc-bar--COMP { background: #80868b; }

/* 일요일 빨강 · 토요일 파랑. 요일 머리와 날짜 숫자가 함께 쓴다. */
.pm-gc__sun { color: #d93025; }
.pm-gc__sat { color: #1a73e8; }

/* ── 작은 달력 ───────────────────────────────────────────── */

.pm-gc-mini {
  margin-bottom: var(--jsini-sp-lg);
}

.pm-gc-mini__head {
  display: flex;
  gap: var(--jsini-sp-3xs);
  align-items: center;
  margin-bottom: var(--jsini-sp-2xs);
}

.pm-gc-mini__title {
  flex: 1;
  font-size: var(--jsini-fs-sm);
  font-weight: 600;
}

.pm-gc-mini__nav {
  width: 1.5rem;
  height: 1.5rem;
}

.pm-gc-mini__dow,
.pm-gc-mini__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
}

.pm-gc-mini__dow > span {
  padding: var(--jsini-sp-3xs) 0;
  font-size: var(--jsini-fs-2xs);
  color: var(--jsini-text-muted);
  text-align: center;
}

.pm-gc-mini__day {
  aspect-ratio: 1;
  padding: 0;
  font: inherit;
  font-size: var(--jsini-fs-2xs);
  color: var(--jsini-text);
  cursor: pointer;
  background: transparent;
  border: 0;
  border-radius: var(--jsini-radius-full);
}

.pm-gc-mini__day:hover {
  background: color-mix(in srgb, var(--jsini-text) 10%, transparent);
}

.pm-gc-mini__day--out {
  color: var(--jsini-text-muted);
  opacity: 0.55;
}

/* 큰 달력이 지금 어디를 보고 있나. 옅게 깔아 둔다. */
.pm-gc-mini__day--in {
  background: color-mix(in srgb, var(--pm-gc-accent) 14%, transparent);
}

/* 오늘이 맨 뒤다 — 보고 있는 구간 안의 오늘도 동그라미가 이겨야 한다. */
.pm-gc-mini__day--today {
  font-weight: 700;
  color: #fff;
  background: var(--pm-gc-accent);
}

/* ── 월 격자 ─────────────────────────────────────────────────

   칸 높이는 **막대 줄 수와 한 몸**이다 —
   칸 = 날짜줄 + (보이는 줄 + 「더보기」 한 줄) × (막대 + 사이).
   화면의 `MaxLanes` 를 고치면 여기 `--pm-gc-cell` 도 같이 고쳐야 한다.
   ------------------------------------------------------------ */

.pm-gc-month {
  --pm-gc-num: 1.625rem;
  --pm-gc-bar: 1.25rem;
  --pm-gc-gap: 2px;
  --pm-gc-cell: 7.25rem;

  overflow: hidden;
  background: var(--jsini-surface-raised, var(--jsini-surface));
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-lg);

  /* 세로 쓸기는 브라우저에 맡긴다 — 가로만 우리가 본다. */
  touch-action: pan-y;
}

.pm-gc-month--phone {
  --pm-gc-num: 1.375rem;
  --pm-gc-bar: 1rem;
  --pm-gc-cell: 5rem;
}

.pm-gc-month__dow {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
}

.pm-gc-month__dow-cell {
  padding: var(--jsini-sp-xs) 0;
  font-size: var(--jsini-fs-2xs);
  color: var(--jsini-text-muted);
  text-align: center;
  letter-spacing: 0.06em;
}

.pm-gc-week {
  position: relative;
  min-height: var(--pm-gc-cell);
}

.pm-gc-week__days {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  height: 100%;
  min-height: var(--pm-gc-cell);
}

.pm-gc-day {
  position: relative;
  min-width: 0;
  border-top: 1px solid var(--jsini-border);
  border-left: 1px solid var(--jsini-border);
}

.pm-gc-day:first-child {
  border-left: 0;
}

/* 이 달이 아닌 자투리 날. 지우지 않고 흐리게 둔다 — 달을 넘나드는 일이
   거기서 이어지는 것이 보여야 한다. */
.pm-gc-day--out {
  background: color-mix(in srgb, var(--jsini-text) 4%, transparent);
}

.pm-gc-day--drop {
  background: color-mix(in srgb, var(--pm-gc-accent) 16%, transparent);
}

/* 고른 날의 테두리. 안쪽에 그려야 칸 크기가 안 흔들린다. */
.pm-gc-day--on::after {
  position: absolute;
  inset: 0;
  pointer-events: none;
  content: '';
  border: 2px solid var(--pm-gc-accent);
}

/* 칸의 빈 자리 전체가 단추다 — 구글에서 빈 곳을 누르면 새 일정이 열린다. */
.pm-gc-day__fill {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  cursor: pointer;
  background: transparent;
  border: 0;
}

.pm-gc-day__fill:hover {
  background: color-mix(in srgb, var(--jsini-text) 5%, transparent);
}

/* 날짜 숫자는 **동그라미**다 — 오늘 표시가 원이어야 하므로 폭과 높이를
   같게 묶는다. 두 자리 숫자에서 타원이 되면 구글의 그것으로 안 보인다.

   높이를 날짜줄(`--pm-gc-num`)에서 빼서 잡는다. 픽셀로 박아 두면 휴대폰에서
   줄을 줄였을 때 **막대판이 숫자 위로 올라앉는다** — 막대판의 시작점이 바로
   그 변수이기 때문이다. */
.pm-gc-day__num {
  position: relative;
  z-index: 1;
  display: block;
  width: calc(var(--pm-gc-num) - 0.25rem);
  height: calc(var(--pm-gc-num) - 0.25rem);
  padding: 0;
  margin: var(--jsini-sp-3xs) auto 0;
  font: inherit;
  font-size: var(--jsini-fs-xs);
  line-height: calc(var(--pm-gc-num) - 0.25rem);
  color: var(--jsini-text);
  text-align: center;
  cursor: pointer;
  background: transparent;
  border: 0;
  border-radius: var(--jsini-radius-full);
}

.pm-gc-day__num:hover {
  background: color-mix(in srgb, var(--jsini-text) 10%, transparent);
}

.pm-gc-day--sun .pm-gc-day__num { color: #d93025; }
.pm-gc-day--sat .pm-gc-day__num { color: #1a73e8; }
.pm-gc-day--out .pm-gc-day__num { opacity: 0.5; }

/* 오늘은 파란 동그라미. 구글에서 한 화면에 딱 하나뿐인 표시다. */
.pm-gc-day--today .pm-gc-day__num {
  font-weight: 700;
  color: #fff;
  background: var(--pm-gc-accent);
}

/* 막대판. 칸 위에 통째로 얹혀 **칸 경계를 가로지른다** — 이것이 여러 날에
   걸친 일을 한 줄로 흐르게 하는 장치다.

   판 자체는 눌리지 않는다(`pointer-events: none`). 막대만 눌린다 —
   안 그러면 판이 칸의 빈 자리를 덮어 새 일정을 못 연다. */
.pm-gc-week__bars {
  position: absolute;
  top: var(--pm-gc-num);
  right: 0;
  bottom: 2px;
  left: 0;
  display: grid;
  grid-auto-rows: var(--pm-gc-bar);
  grid-template-columns: repeat(7, 1fr);
  gap: var(--pm-gc-gap) 0;
  overflow: hidden;
  pointer-events: none;
}

.pm-gc-week__bars > * {
  pointer-events: auto;
}

.pm-gc-bar {
  min-width: 0;
  padding: 0 var(--jsini-sp-xs);
  margin: 0 2px;
  overflow: hidden;
  font: inherit;
  font-size: var(--jsini-fs-2xs);
  line-height: var(--pm-gc-bar);
  color: #fff;
  text-align: left;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
  border: 0;
  border-radius: var(--jsini-radius-sm);
}

.pm-gc-bar:disabled {
  cursor: default;
}

/* 노랑 위의 흰 글자는 안 읽힌다 — 이 하나만 글자색을 뒤집는다. */
.pm-gc-bar--READY {
  color: #3c2f00;
}

/* 주를 넘어가는 쪽은 모서리를 편다. 잘린 끝이 둥글면 **거기서 끝난 것**으로
   보여서, 사흘짜리 일이 주마다 따로 있는 것처럼 읽힌다. */
.pm-gc-bar--clip-s {
  margin-left: 0;
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

.pm-gc-bar--clip-e {
  margin-right: 0;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

.pm-gc-week__more {
  min-width: 0;
  padding: 0 var(--jsini-sp-sm);
  overflow: hidden;
  font: inherit;
  font-size: var(--jsini-fs-2xs);
  line-height: var(--pm-gc-bar);
  color: var(--jsini-text-muted);
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
  background: transparent;
  border: 0;
}

.pm-gc-week__more:hover {
  text-decoration: underline;
}

/* 휴대폰의 한 칸은 50px 언저리다. 좌우 여백이 그 중 3분의 1을 먹으면 막대에
   남는 글자가 한두 자뿐이라, 무슨 일인지 알아볼 수 없는 색띠가 된다.
   글은 `+N` 으로 줄여 두었으므로(화면) 가운데에 놓는다. */
.pm-gc-month--phone .pm-gc-bar {
  padding: 0 3px;
}

.pm-gc-month--phone .pm-gc-week__more {
  padding: 0 2px;
  text-align: center;
}

/* ── 일정 목록 ───────────────────────────────────────────────

   세 자리가 **같은 줄**을 쓴다 — 일정 보기 · 휴대폰 격자 아래 · 날 펼침 창.
   ------------------------------------------------------------ */

.pm-gc-agenda--under {
  margin-top: var(--jsini-sp-lg);
}

.pm-gc-agenda__head {
  display: flex;
  gap: var(--jsini-sp-sm);
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--jsini-sp-sm);
}

.pm-gc-agenda__title {
  font-weight: 600;
}

.pm-gc-agenda__empty {
  padding: var(--jsini-sp-xl);
  margin: 0;
  color: var(--jsini-text-muted);
  text-align: center;
}

.pm-gc-agenda__day {
  display: flex;
  gap: var(--jsini-sp-md);
  padding: var(--jsini-sp-sm) 0;
  border-top: 1px solid var(--jsini-border);
}

.pm-gc-agenda__date {
  flex: 0 0 3.5rem;
  text-align: center;
}

.pm-gc-agenda__dnum {
  display: block;
  font-size: var(--jsini-fs-xl);
  line-height: 1.2;
}

.pm-gc-agenda__day--today .pm-gc-agenda__dnum {
  font-weight: 700;
  color: var(--pm-gc-accent);
}

.pm-gc-agenda__dow,
.pm-gc-agenda__month {
  display: block;
  font-size: var(--jsini-fs-2xs);
  color: var(--jsini-text-muted);
}

.pm-gc-agenda__items {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--jsini-sp-2xs);
  min-width: 0;
  padding: 0;
  margin: 0;
  list-style: none;
}

.pm-gc-agenda__item {
  min-width: 0;
}

/* 고칠 수 없는 사람에게도 줄은 보인다 — 누르는 것만 막는다(`disabled`).
   창을 못 열어도 제목·기간·담당·상태·메모가 여기 다 적혀 있다. */
.pm-gc-agenda__btn {
  display: block;
  width: 100%;
  padding: var(--jsini-sp-sm) var(--jsini-sp-md);
  font: inherit;
  color: var(--jsini-text);
  text-align: left;
  cursor: pointer;
  background: var(--jsini-surface-raised, var(--jsini-surface));
  border: 1px solid var(--jsini-border);
  border-left: 4px solid var(--jsini-border);
  border-radius: var(--jsini-radius-md);
}

.pm-gc-agenda__btn:disabled {
  cursor: default;
}

.pm-gc-agenda__item--READY .pm-gc-agenda__btn { border-left-color: #f9ab00; }
.pm-gc-agenda__item--RUNNING .pm-gc-agenda__btn { border-left-color: #1a73e8; }
.pm-gc-agenda__item--OVER .pm-gc-agenda__btn { border-left-color: #d93025; }
.pm-gc-agenda__item--COMP .pm-gc-agenda__btn { border-left-color: #80868b; }

.pm-gc-agenda__top {
  display: flex;
  gap: var(--jsini-sp-sm);
  align-items: center;
  justify-content: space-between;
}

.pm-gc-agenda__name {
  overflow: hidden;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pm-gc-agenda__sub {
  display: block;
  margin-top: var(--jsini-sp-3xs);
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
}

/* 묻는 창 둘(「이어서 지시」· 「수동 재시도」)의 AI 고르는 칸.

   **한 줄이다** — 고르개와 그 AI 의 남은 한도가 나란히 선다.

   [라벨을 걷고 한도를 끌어올렸다 — 2026-10-11]

   전에는 칸 앞에 라벨(`.pm-peek__label`)이 서고 **그 아래 또 한 줄**에 한도
   배지(`.pm-ai-limit`)가 있었다. 라벨은 고르개에 AI 이름이 이미 적혀 있어
   한 번 더 하는 말이었고, 한도는 고를 때 가장 보고 싶은 값인데 칸에서 한 줄
   떨어져 있었다. 층이 둘에서 하나로 줄었다 — 이 창은 휴대폰에서 화면을 꽉
   채우고 그 아래에 글상자(다섯 줄)와 단추 줄이 서므로, **줄 하나가 곧 자판
   밑으로 내려가는 단추**다.

   칸이 남은 폭을 다 쓰고(`flex: 1`) 배지는 제 너비만 갖는다. AI 이름이
   「안티그래비티 (agy)」처럼 길어서 `max-content` 로 두면 칸마다 폭이 달라
   보인다. */
.pm-peek__field {
  display: flex;
  gap: var(--jsini-sp-sm);
  align-items: center;
  margin-top: var(--jsini-sp-sm);
}

/* 상세 보기의 항목 이름(「작성」·「동작」·「작업」). 묻는 창 둘은 이제
   라벨을 쓰지 않는다 — 고르개에 AI 이름이 이미 적혀 있다. */
.pm-peek__label {
  flex: 0 0 auto;
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
}

.pm-peek__kind {
  flex: 1 1 auto;
  min-width: 0;
}

/* 한도 배지. **밀리지 않고 접히지 않는다** — 「사용 92%」가 두 줄로 꺾이면
   칸 높이가 들쭉날쭉해지고, 밀리면 가장 봐야 할 값이 창 밖으로 나간다.
   치수는 위의 배지 묶음이 함께 정한다(`.pm-peek__usage`). */
.pm-peek__usage {
  white-space: nowrap;
}

/* 「수동 재시도」 창의 지난 회차 자국 — 상태 · AI · 횟수 · 끝난 시각 · 까닭.

   **바로 위 한 줄(`jsini-hint`)과 갈라 보여야 한다.** 그쪽은 「이번에 어디로
   가나」이고 이쪽은 「지난번에 무슨 일이 있었나」다. 둘이 같은 회색 작은 글씨로
   붙어 서면 한 문단으로 읽혀, 지난 실패의 까닭이 이번 안내의 꼬리처럼 보인다.
   왼쪽에 선을 긋고 바탕을 옅게 깔아 **인용 조각**으로 둔다.

   **두 줄까지만 보인다.** 실행기가 적는 글은 길이를 가늠할 수 없어서, 안 묶으면
   이 한 조각이 창을 밀어 글상자와 단추를 자판 밑으로 내려보낸다. C# 쪽도 140자에서
   자르지만(`AiTaskActions.Shorten`) 글자 폭은 화면마다 달라 여기서 한 번 더 묶는다. */
.pm-peek__status {
  margin: var(--jsini-sp-2xs) 0 0;
  padding: var(--jsini-sp-2xs) var(--jsini-sp-xs);
  border-left: 2px solid var(--jsini-border);
  border-radius: 0 var(--jsini-radius-sm) var(--jsini-radius-sm) 0;
  background: var(--jsini-surface-muted);
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
}

/* 상세 아래 단추의 글자. 휴대폰에서는 **짧은 쪽**이 선다 —
   「사용자확인완료」 여섯 글자가 넷이 서는 줄에서 혼자 한 줄을 먹는다.

   `.pm-peek .dxbl-modal-footer` 를 손보던 규칙이 여기 함께 있었다. 상세가
   창이 아니라 판 안의 탭으로 서게 되면서(2026-10-11) 그 바닥 띠가 없어져
   걷었다 — 지금 단추가 서는 줄은 `pm-taskview__acts` 다. */
.pm-peek__btn-text--full { display: inline; }
.pm-peek__btn-text--short { display: none; }

@media (max-width: 767px) {
  .pm-peek__btn-text--full { display: none; }
  .pm-peek__btn-text--short { display: inline; }
}

/* ── AI 작업: 쪽이 통째로 구르지 않는다 (`pm-ai-page`) ────────

   데스크탑에서 이 화면은 **본문 높이 안에서 끝난다.** 머리줄과 조건줄은 늘
   위에 붙어 있고, 구르는 것은 왼쪽 목록과 오른쪽 탭 **안쪽**뿐이다.

   [왜 필요했나 — 빼기로 흉내 낸 높이는 맞는 순간이 없었다]

   한동안 편집기와 로그가 `calc(100vh - 33rem)` · `calc(100vh - 32rem)` 로
   서 있었다. 빼는 값이 「위아래에 쌓인 것의 합」이라 위에 줄 하나만 늘어도
   그만큼 아래가 잘린다. 그런데 이 화면은 안내줄이 **상태에 따라 넷까지**
   나타났다 사라지고(대기 안내·임시저장·저장소 막힘·지난 실패), 실행 이력
   띠도 있을 때와 없을 때가 다르다 — 그 두 값이 맞는 조합이 사실상 없었다.
   증상은 「단추 줄이 화면 밖으로 밀린다」와 「아래가 휑하다」로 번갈아 났다.

   저장소의 다른 화면들은 이 일을 이미 `CommCont` 로 한다
   (`app.css` 의 `.jsini-shell__content:has(> .commcont)`). 이 화면은 맨 위가
   머리줄(`pm-ai-hero`)이라 그 규칙에 안 걸린다 — 같은 일을 여기서 해 준다.

   [좁은 창은 건드리지 않는다]

   휴대폰·좁은 창에서는 두 판이 위아래로 쌓이므로(62rem 아래) 한 화면에
   욱여넣을 수가 없다. 거기서는 지금처럼 쪽이 구르고, 편집기·로그는 각자
   적어 둔 높이(24rem)를 쓴다.
   ---------------------------------------------------------- */

@media (width >= 62rem) {
  /* 본문을 세로 flex 로 바꾼다. `:has()` 로 **이 화면일 때만** 건다 —
     본문은 모든 화면이 함께 쓰는 자리다. */
  .jsini-shell__content:has(> .pm-ai-page) {
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }

  .pm-ai-page {
    display: flex;
    flex: 1;

    /* 이것이 없으면 flex 자식이 내용만큼 부풀어 본문을 넘는다.
       높이 계산이 통째로 어긋나는 자리다. */
    min-height: 0;
    flex-direction: column;
  }

  /* 머리줄·조건줄·불러오는 중 안내는 제 높이만큼. */
  .pm-ai-page > .pm-ai-overview,
  .pm-ai-page > .pm-ai-loading {
    flex: 0 0 auto;
  }

  /* 두 판이 남은 높이를 다 먹고 **끝까지 같은 키로** 선다.
     `align-items: start` 였던 것을 푼다 — 그러면 짧은 쪽이 내용만큼만
     서서 왼쪽 표와 오른쪽 탭의 아래 끝이 어긋난다. */
  .pm-ai-page > .pm-splitpane {
    flex: 1;
    min-height: 0;
    align-items: stretch;
  }


  /* 왼쪽 표. 판이 준 높이를 그대로 쓰고 **줄만 안에서 구른다** —
     칸 머리와 아래 띠(＋ · 다시 읽기 · 엑셀)는 제자리에 남는다.
     `commcont__body > .commgrd` 규칙은 app.css 에 이미 있다. */

  /* 탭 위에 쌓이는 것들(고른 작업 · 안내줄 넷 · 실행 이력 띠)은 **줄어들지
     않는다.** flex 의 기본이 `0 1 auto` 라 자리가 모자라면 이것들부터 눌리는데,
     그러면 안내문이 반 줄만 보이거나 실행 이력 단추가 잘린다. 줄어드는 것은
     탭 안쪽이어야 한다. */
  .pm-ai-detail-panel > .commcont__body > * {
    flex: 0 0 auto;
  }

  /* 오른쪽 탭이 남은 높이를 다 먹는다. */
  .pm-ai-detail-panel > .commcont__body > .pm-ai-tabs,
  .pm-ai-tabs {
    display: flex;
    flex: 1;
    min-height: 0;
    flex-direction: column;
  }

  /* **DevExpress 의 상자 둘이 높이를 안 물려준다** —
     `dxbl-tabs-content-panel` · `dxbl-tabs-content` 가 `min-height: auto` 인
     flex 항목이라 안쪽 내용보다 작아지지 않는다. 그대로 두면 로그가 길어질수록
     판이 늘어나 단추 줄이 판 밖으로 나간다(포털관리에서 두 번 밟은 자리다 —
     `ad-roletabs` · `ad-send__tabs`). */
  .pm-ai-tabs > .dxbl-tabs-content-panel,
  .pm-ai-tabs .dxbl-tabs-content {
    flex: 1;
    min-height: 0;
    overflow: hidden;
  }

  /* 탭 안쪽. `min-height: 60vh` 로 바닥을 깔아 둔 공용 규칙을 여기서 끈다 —
     남은 높이를 정확히 받는 자리에서는 그 바닥이 판을 밀어낸다. */
  .pm-ai-tabs .pm-tab-body {
    display: flex;
    height: 100%;
    min-height: 0;
    flex-direction: column;
  }

  /* 지시 탭 — 설정과 단추 줄은 제 높이만큼, 편집기가 남은 자리를 다 먹는다.

     **새 줄을 넣을 때마다 여기 이름을 더해야 한다.** 빠뜨리면 그 줄이
     flex 기본값(`1 1 auto`)으로 늘어나 편집기 몫을 가져간다 — 한도 줄과
     편집기 머리줄이 그래서 여기 있다. */
  .pm-ai-write > .pm-editform,
  .pm-ai-write > .pm-ai-limit,
  .pm-ai-write > .pm-ai-editor__bar,
  .pm-ai-write > .pm-ai-opts {
    flex: 0 0 auto;
  }

  /* **그래도 안 들어가는 날이 있다 — 그때는 구른다.**

     이 탭에 쌓이는 줄은 상태에 따라 늘었다 준다(안내줄 넷 · 실행 이력 띠 ·
     한도 줄 · 편집기 머리줄). 편집기가 더 줄 수 없는 데까지 줄고도 남으면
     위 `.dxbl-tabs-content` 의 `overflow: hidden` 이 **나머지를 잘라 낸다** —
     잘리는 것이 하필 아래쪽의 편집기와 「저장·요청」 줄이라, 화면에는 설정
     폼만 남아서 **「편집기가 없어졌다」**로 보인다(헤드리스로 재어 확인:
     1600×1000 · 조회판을 편 상태에서 단추 줄이 `top: 1013`).

     잘라 내는 대신 구르게 한다. 들어가는 날에는 막대가 아예 안 뜬다. */
  .pm-ai-tabs .pm-ai-write {
    overflow-y: auto;
  }

  .pm-ai-write > .pm-ai-editor {
    flex: 1;
    height: auto;
    min-height: 10rem;
  }

  /* 결과·로그 탭 — 안의 자료 판이 높이를 받아 **글만 자기 안에서 구른다.** */
  .pm-ai-result-tab > .commcont,
  .pm-ai-log-tab > .commcont {
    flex: 1;
    min-height: 0;
  }

  .pm-ai-result-tab > .commcont > .commcont__body,
  .pm-ai-log-tab > .commcont > .commcont__body {
    display: flex;
    flex: 1;
    min-height: 0;
    flex-direction: column;
  }

  .pm-ai-log-tab .pm-ai-log {
    flex: 1;
    height: auto;
    min-height: 0;
  }

  /* 결과문은 **자기 키만큼만** 서고 길어지면 줄어들며 안에서 구른다.
     로그처럼 `flex: 1` 로 두면 세 줄짜리 답도 판 끝까지 늘어난 테두리
     안에 덩그러니 놓인다. `min-height: 0` 이 없으면 줄어들지를 못해
     (flex 항목의 기본이 `min-height: auto`) 긴 답이 판을 넘는다. */
  .pm-ai-result-tab .pm-ai-result {
    flex: 0 1 auto;
    max-height: none;
    min-height: 0;
  }

  /* 아무것도 안 고른 자리. 22rem 을 깔아 두면 판보다 커질 수 있다. */
  .pm-ai-detail-panel .pm-ai-empty {
    flex: 1;
    min-height: 0;
  }
}

/* ── AI 작업: 모바일·좁은 화면에서는 스플리터로 보이지 않고 위아래로 쌓는다 ────

   데스크탑에서는 가운데 손잡이를 끌어 너비를 조절하지만, 모바일(휴대폰)·좁은
   화면에서는 상자를 지워(`display: contents`) 두 판이 위아래로 자연스럽게 쌓이고
   쪽 전체가 구르게 한다.
   마크업을 화면 크기마다 갈아 끼우면 화면 회전이나 크기 변경 시 편집 상태와
   조회가 다시 만들어지므로 상자만 없앤다. 구분선(손잡이)은 감춘다(`display: none`).
   셸 사이드바(`app.css`)·사람롤(`admin.css`)·프로시저결과(`helpdesk.css`)와
   같은 방식(web/CLAUDE.md). */
@media (max-width: 62rem) {
  .pm-ai-page > .pm-splitpane,
  .pm-ai-page > .pm-splitpane > .pm-splitpane__ui,
  .pm-ai-page > .pm-splitpane > .pm-splitpane__ui > .dxbl-splitter-pane,
  .pm-ai-page > .pm-splitpane > .pm-splitpane__ui > .dxbl-splitter-pane > .dxbl-splitter-pane-content {
    display: contents !important;
  }

  .pm-ai-page > .pm-splitpane > .pm-splitpane__ui > .dxbl-splitter-separator {
    display: none !important;
  }

  .pm-ai-page .pm-splitpane__first,
  .pm-ai-page .pm-splitpane__second {
    display: block;
    overflow: visible;
    flex: none;
    padding: 0 !important;
  }

  .pm-ai-page .pm-splitpane__second {
    margin-top: var(--jsini-sp-lg);
  }

  .pm-ai-page .pm-splitpane .commcont {
    height: auto;
    min-height: 0;
  }
}

.pm-ai-page--phone > .pm-splitpane,
.pm-ai-page--phone > .pm-splitpane > .pm-splitpane__ui,
.pm-ai-page--phone > .pm-splitpane > .pm-splitpane__ui > .dxbl-splitter-pane,
.pm-ai-page--phone > .pm-splitpane > .pm-splitpane__ui > .dxbl-splitter-pane > .dxbl-splitter-pane-content {
  display: contents !important;
}

.pm-ai-page--phone > .pm-splitpane > .pm-splitpane__ui > .dxbl-splitter-separator {
  display: none !important;
}

.pm-ai-page--phone .pm-splitpane__first,
.pm-ai-page--phone .pm-splitpane__second {
  display: block;
  overflow: visible;
  flex: none;
  padding: 0 !important;
}

.pm-ai-page--phone .pm-splitpane__second {
  margin-top: var(--jsini-sp-lg);
}

.pm-ai-page--phone .pm-splitpane .commcont {
  height: auto;
  min-height: 0;
}

/* ── 탭으로 펼쳐 보는 지시 한 건 (`pm-taskview`) ──────────────

   `AiTaskViewPage` — 책상에서 카드를 누르거나 앱알림·메일의 「화면에서 보기」
   로 들어오는 자리다. 속을 그리는 것은 창과 같은 부품(`AiTaskView`)이므로
   여기서 정하는 것은 **머리줄과 단추 줄뿐**이다.
   ---------------------------------------------------------- */

.pm-taskview {
  display: flex;
  flex-direction: column;
  min-height: 100%;
}

.pm-taskview > .commcont,
.pm-taskview > .commcont > .commcont__body {
  display: flex;
  flex-direction: column;
  flex: 1;
}

.pm-taskview__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.75rem;
  margin-bottom: 0.75rem;
}

/* 돌아갈 자리. 알림을 누르고 들어온 사람에게는 이 화면이 첫 화면이라
   **제목보다 먼저** 읽히게 왼쪽에 둔다. */
.pm-taskview__back {
  flex: 0 0 auto;
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
  text-decoration: none;
}

.pm-taskview__back:hover {
  color: var(--jsini-text);
  text-decoration: underline;
}

/* 제목은 **줄바꿈한다.** 지시의 첫 줄이 그대로 제목이라 길다 — 한 줄로
   자르면 무엇을 시킨 건인지가 잘리는 쪽이다. */
.pm-taskview__title {
  min-width: 0;
  margin: 0;
  font-size: var(--jsini-fs-lg);
  font-weight: 600;
  line-height: 1.4;
  word-break: break-word;
}

/* 단추 줄. 한동안 창이 있던 자리에서는 바닥 띠가 하던 일을, 여기서는
   바닥에 붙박이(`position: sticky; bottom: 0`)로 세워 내용이 길어도
   언제나 보여지는 화면의 아래쪽에 머물러 즉시 선택할 수 있게 한다.

   휴대폰에서 「빠른 지시」 판 안에 서는 상세도 같은 줄을 쓴다
   (`pm-ask__viewacts`). 서는 단추가 같은 부품(`AiTaskActions`)이라 모양까지
   갈라 둘 이유가 없다. */
.pm-taskview__acts,
.pm-ask__viewacts {
  position: sticky;
  bottom: 0;
  z-index: 10;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-top: auto;
  padding: 0.75rem 0;
  background: var(--jsini-bg);
  border-top: 1px solid var(--jsini-border);
  box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.05);
}

@media (max-width: 767px) {
  /* 띠 몫은 **본문이 이미 비워 두었다** (2026-10-11)

     전에는 여기서 `bottom: calc(60px + 안전영역)` 으로 하단 띠를 한 번 더
     피했다. 그런데 `position: sticky` 가 자리를 재는 상자는 본문의 **안쪽**
     상자이고, 그 상자는 셸이 이미 띠만큼 줄여 놓은 것이다(app.css) —
     띠를 두 번 센 셈이라 줄의 아래끝이 바닥에서 136px 에 섰다. 떠다니는
     단추(바닥에서 76px)가 그보다 아래였으니, 화면을 쓰는 사람에게는
     **그 화면의 단추가 셸의 보조 단추보다 위**로 보였다.

     그래서 0 으로 붙인다. 셸 쪽에서 `.jsini-actbar` 를 가진 본문의 아래
     여백을 띠 높이에 **딱 맞춰** 두므로(숨 쉬는 1rem 은 이 줄이 제 여백으로
     갖는다) 0 이 곧 띠의 윗금이다. 띠를 끄면 그 여백이 안전영역뿐이라
     같은 0 이 화면 맨 아래가 된다 — 갈래를 둘로 둘 까닭이 없어져
     `--bottomnav-hidden` 쪽 규칙도 함께 걷었다.

     높이를 **보장한다.** 떠다니는 단추가 올라서는 거리는 셸이
     `--jsini-actbar-height` 만큼 민다. 그 값과 실제 줄 높이가 어긋나면
     단추가 줄에 걸치거나 허공에 뜨므로, 줄 쪽에서 같은 토큰으로 아래끝을
     받친다. 단추가 늘어 **줄이 갈리면** 그 보장이 깨지니 휴대폰에서는 줄을
     내리지 않는다(`nowrap`) — 글자는 이미 짧은 쪽으로 바뀌어 있고
     (`pm-peek__btn-text--short`), 그래도 넘치면 옆으로 민다. */
  .pm-taskview__acts,
  .pm-ask__viewacts {
    bottom: 0;
    min-height: var(--jsini-actbar-height);
    padding: 0.5rem 0;

    /* 줄을 내리지 않는다. 오른쪽으로 모으는 일은 `justify-content` 가
       아니라 **첫 단추의 왼쪽 여백**이 한다 — `flex-end` 로 모아 둔 줄이
       넘치면 넘친 쪽이 시작 모서리 밖으로 밀려 **옆으로 밀어도 영영 안
       나온다.** 자동 여백은 넘치는 순간 0 이 되어 그 덫이 없다. */
    flex-wrap: nowrap;
    justify-content: flex-start;
    overflow-x: auto;
  }

  /* 단추는 **줄이지도 줄바꿈하지도 않는다.** 폭이 모자랄 때 줄이면 글자가
     단추 **안에서** 두 줄로 갈려 줄 높이가 52px 을 넘고, 그러면 떠다니는
     단추를 밀어 둔 거리(`--jsini-actbar-height`)가 틀린 값이 된다 —
     320px 에 단추 다섯을 세워 재 보니 89px 까지 자랐다. 넘치는 것은
     위의 `overflow-x` 로 민다. */
  .pm-taskview__acts > *,
  .pm-ask__viewacts > * {
    flex: 0 0 auto;
    white-space: nowrap;
  }

  .pm-taskview__acts > :first-child,
  .pm-ask__viewacts > :first-child {
    margin-inline-start: auto;
  }
}

@media (max-width: 767px) {
  .pm-ai-overview {
    display: block;
    margin-bottom: var(--jsini-sp-lg);
    background: var(--jsini-surface-raised, var(--jsini-surface));
    border: 1px solid var(--jsini-border);
    border-radius: var(--jsini-radius-lg);
    box-shadow: var(--jsini-shadow-card, var(--jsini-shadow-sm));
  }

  .pm-ai-overview__body {
    display: block;
  }

  /* 휴대폰에서는 띠가 판의 머리줄이다 — 위 공통 모양에서 여백만 키우고
     모서리를 판에 맞춘다. */
  .pm-ai-overview__summary {
    margin-bottom: 0;
    padding: 0.85rem;
    border-radius: 0;
    color: inherit;
    font-size: inherit;
  }

  /* 아코디언 안에서는 두 판의 테두리가 겹치지 않게 한다. */
  .pm-ai-overview .pm-ai-hero {
    margin-bottom: 0;
    border-radius: 0;
    border-inline: 0;
  }

  .pm-ai-overview .commsch {
    margin-bottom: 0;
    border: 0;
    border-top: 1px solid var(--jsini-border);
    border-radius: 0;
    box-shadow: none;
  }
}

/* ============================================================
   AI 작업 현황 (AiDashboard.razor)
   ============================================================

   [판을 나누는 것이 이 화면의 전부다]

   대시보드에서 가장 흔한 실패는 **모든 것이 같은 크기로 늘어서는 것**이다.
   그러면 사람이 어디부터 읽어야 하는지 알 수 없다. 그래서 위에서 아래로
   읽는 차례를 자리로 고정한다 —

     지금 띠 → 머리 숫자 → 전망 → 한도 → 추세 → 빈도 → 순위 → 최근 실행

   각 띠는 `pm-aidash__row` 로 나란히 서고, 좁아지면 한 칸으로 접힌다.
   ------------------------------------------------------------ */

.pm-aidash {
  display: flex;
  flex-direction: column;
  gap: var(--jsini-sp-lg);
}

/* 「지금」 띠. **기간을 타지 않는 값만 여기 둔다** — 화면이 그 사실을
   글자로 말해야 지난달로 조회한 사람이 「실행 중 0건」을 오해하지 않는다. */
.pm-aidash__now {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--jsini-sp-xs) var(--jsini-sp-sm);
}

.pm-aidash__now-label {
  font-size: var(--jsini-fs-sm);
  font-weight: 600;
  color: var(--jsini-text-muted);
}

/* 「언제 기준인가」. 숫자 옆에 늘 붙는 작은 글자라 색을 죽인다 —
   이 글자가 숫자만큼 진하면 숫자가 안 보인다. */
.pm-aidash__stamp {
  font-size: var(--jsini-fs-2xs);
  color: var(--jsini-text-muted);
}

/* 타일 아래 한 줄. 큰 숫자 하나로는 못 하는 말을 여기서 한다
   (중앙값 · 근거 · 내역). */
.pm-aidash__sub {
  display: block;
  margin-top: var(--jsini-sp-2xs);
  font-size: var(--jsini-fs-2xs);
  color: var(--jsini-text-muted);
}

.pm-aidash__tiles .jsini-stat {
  align-items: flex-start;
}

/* 나란한 판. `minmax(0, …)` 가 요점이다 — 안 주면 차트가 자기 내용만큼
   넓어지려 해서 칸이 화면 밖으로 밀린다. */
.pm-aidash__row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
  gap: var(--jsini-sp-lg);
  align-items: start;
}

.pm-aidash__panel {
  min-width: 0;
}

.pm-aidash__chart {
  min-width: 0;
}

/* ── 한도 계량기 ──────────────────────────────────────────
   차트가 아니라 계량기다. 값이 하나뿐인 비율은 막대 하나가 가장 정확하고,
   원형 게이지로 그리면 같은 자리를 세 배 먹으면서 읽기는 더 어렵다.
   ---------------------------------------------------------- */

.pm-aidash__meters {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  gap: var(--jsini-sp-md);
}

.pm-aidash__meter-card {
  display: flex;
  flex-direction: column;
  gap: var(--jsini-sp-xs);
  padding: var(--jsini-sp-md);
  background: var(--jsini-surface);
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-md);
}

.pm-aidash__meter-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--jsini-sp-2xs) var(--jsini-sp-xs);
}

.pm-aidash__meter-label {
  display: flex;
  justify-content: space-between;
  gap: var(--jsini-sp-xs);
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
}

.pm-aidash__meter-track {
  height: 0.5rem;
  overflow: hidden;
  background: var(--jsini-bg);
  border-radius: var(--jsini-radius-full);
}

/* 끝을 둥글게 둔다. 막대의 시작은 축에 붙어 있어야 하므로
   **끝쪽만** 둥글다 — 양끝을 다 둥글리면 0 에 가까운 값이 실제보다 커 보인다. */
.pm-aidash__meter-fill {
  display: block;
  height: 100%;
  border-radius: 0 var(--jsini-radius-full) var(--jsini-radius-full) 0;
}

.pm-aidash__meter-error {
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
}

.pm-aidash__tokens {
  font-size: var(--jsini-fs-sm);
}

/* CLI 출력 원문. **접어 둔다** — 평소에는 방해가 되고, 형식이 바뀐
   날에는 이것만이 단서다. */
.pm-aidash__raw > summary {
  font-size: var(--jsini-fs-2xs);
  color: var(--jsini-text-muted);
  cursor: pointer;
}

.pm-aidash__raw pre {
  max-height: 12rem;
  margin: var(--jsini-sp-2xs) 0 0;
  padding: var(--jsini-sp-xs);
  overflow: auto;
  font-size: var(--jsini-fs-2xs);
  white-space: pre-wrap;
  word-break: break-all;
  background: var(--jsini-bg);
  border-radius: var(--jsini-radius-md);
}

/* ── 순위 표 ──────────────────────────────────────────────
   막대가 아니라 표인 이유는 이름이 길기 때문이다(화면 주석 참고).
   대신 이름 아래에 얇은 막대를 깔아 순위를 눈으로 먼저 읽게 한다.
   ---------------------------------------------------------- */

.pm-aidash__rank {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--jsini-fs-sm);
}

.pm-aidash__rank th,
.pm-aidash__rank td {
  padding: var(--jsini-sp-xs) var(--jsini-sp-2xs);
  text-align: left;
  border-bottom: 1px solid var(--jsini-border);
}

.pm-aidash__rank th {
  font-size: var(--jsini-fs-2xs);
  font-weight: 600;
  color: var(--jsini-text-muted);
}

/* 숫자 칸은 오른쪽으로 모으고 자릿수를 고정한다. 안 그러면 세로로
   자릿수가 어긋나 큰 수와 작은 수를 눈으로 비교할 수 없다. */
.pm-aidash__num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.pm-aidash__rank td:first-child {
  position: relative;
  min-width: 8rem;
  padding-bottom: calc(var(--jsini-sp-xs) + 0.25rem);
}

/* 이름 아래 얇은 띠. **숫자를 대신하지 않는다** — 숫자는 그대로 옆에 있고
   이것은 순위를 훑기 위한 것이라 색을 죽여 둔다. */
.pm-aidash__bar {
  position: absolute;
  left: var(--jsini-sp-2xs);
  bottom: 0.35rem;
  height: 0.2rem;
  max-width: calc(100% - var(--jsini-sp-sm));
  background: var(--jsini-accent);
  opacity: 0.35;
  border-radius: var(--jsini-radius-full);
}

@media (max-width: 767px) {
  /* 좁은 화면에서는 한 칸씩 쌓는다. 차트를 둘씩 넣으면 가로축 글자가
     겹쳐서 무엇을 그린 것인지 알 수 없어진다. */
  .pm-aidash__row {
    grid-template-columns: 1fr;
  }
}

/* ════════════════════════════════════════════════════════════
   WBS 대시보드 — 사내망에서 옮겨 온 화면들
   ════════════════════════════════════════════════════════════ */

/* 표 칸 안의 진행 막대.

   **숫자를 대신하지 않는다** — 막대 위에 값이 겹쳐 적혀 있고, 막대는 여러
   줄을 위아래로 훑을 때만 쓴다. 칸 높이에 맞춰 눕혀 두어 줄 높이가 늘지
   않는다(표가 30줄씩 나오는 화면이라 한 줄이 4px 만 커져도 한 쪽이 줄어든다). */
.pm-bar {
  position: relative;
  height: 1.15rem;
  min-width: 6rem;
  background: var(--jsini-surface-2);
  border-radius: var(--jsini-radius-sm);
  overflow: hidden;
}

.pm-bar__fill {
  position: absolute;
  inset: 0 auto 0 0;
  background: var(--jsini-accent);
  opacity: 0.35;
}

/* 실적 막대만 색을 달리한다. 계획과 실적을 나란히 둔 화면에서 둘이 같은
   색이면 **어느 쪽이 밀린 것인지** 한눈에 안 들어온다. */
.pm-bar--done .pm-bar__fill {
  background: var(--jsini-success, var(--jsini-accent));
  opacity: 0.45;
}

.pm-bar__text {
  position: relative;
  display: block;
  text-align: center;
  font-size: var(--jsini-fs-xs);
  font-variant-numeric: tabular-nums;
  line-height: 1.15rem;
}

/* 요약 타일 중 날짜가 들어가는 칸. 기본 타일 글자가 커서 기간(양끝 날짜)이
   두 줄로 접힌다. */
.pm-stat--dates {
  font-size: var(--jsini-fs-sm);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ── 사람 × 기간 매트릭스 ──────────────────────────────────── */

/* 칸이 자료에 따라 정해져서 표 부품을 쓰지 못한다(화면 머리말). 가로로는
   기간이 계속 늘어나므로 **판만 구르게** 한다 — 쪽 전체가 가로로 구르면
   왼쪽의 이름 칸이 화면 밖으로 나간다. */
.pm-heat-wrap {
  overflow-x: auto;
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-md);
  background: var(--jsini-surface);
}

.pm-heat {
  border-collapse: collapse;
  width: 100%;
  font-size: var(--jsini-fs-sm);
}

.pm-heat th,
.pm-heat td {
  border: 1px solid var(--jsini-border);
  padding: var(--jsini-sp-2xs) var(--jsini-sp-xs);
  text-align: center;
  white-space: nowrap;
}

.pm-heat thead th {
  background: var(--jsini-surface-2);
  font-weight: 600;
}

/* 이름 칸은 구르는 동안에도 붙어 있어야 한다 — 기간이 스물몇이면 가운데쯤에서
   누구 줄인지 알 수 없게 된다. */
.pm-heat__name {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--jsini-surface);
  text-align: left;
  min-width: 8rem;
}

.pm-heat thead .pm-heat__name {
  background: var(--jsini-surface-2);
}

.pm-heat__cell {
  cursor: pointer;
  min-width: 3.5rem;
}

.pm-heat__cell.is-empty {
  cursor: default;
}

.pm-heat__cnt {
  display: block;
  font-variant-numeric: tabular-nums;
}

/* 완료 건수는 작게 둔다. 두 숫자가 같은 크기로 붙어 있으면 어느 쪽이
   배정이고 어느 쪽이 완료인지 판을 볼 때마다 다시 생각하게 된다. */
.pm-heat__done {
  display: block;
  font-size: var(--jsini-fs-xs);
  opacity: 0.7;
  font-variant-numeric: tabular-nums;
}

.pm-heat__total {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  background: var(--jsini-surface-2);
}

/* 창 안에서 구역을 가르는 제목. 인터페이스 상세가 기본정보 · 관리항목 ·
   단계 · 메모 · 첨부 다섯을 세로로 쌓는데, 탭으로 나누면 그 안의 표가
   탭 상자보다 커져 아래 단추가 밖으로 밀려난다(화면 머리말). */
.pm-section {
  margin: var(--jsini-sp-md) 0 var(--jsini-sp-xs);
  font-size: var(--jsini-fs-sm);
  font-weight: 600;
  color: var(--jsini-text-2);
}

/* 걷어 온 JSON 을 붙여넣는 칸. 줄바꿈을 그대로 두고 고정폭으로 보여 준다 —
   붙여넣기가 잘렸는지는 괄호가 맞는지로 알아보는 수밖에 없다. */
.pm-paste textarea {
  font-family: var(--jsini-font-mono);
  font-size: var(--jsini-fs-xs);
  white-space: pre;
}

/* 공유 문서 본문. 담긴 것이 HTML 이라 그대로 그리고, 안쪽 여백만 준다. */
.pm-doc {
  padding: var(--jsini-sp-sm);
  line-height: 1.7;
  overflow-x: auto;
}

.pm-doc img {
  max-width: 100%;
}

.pm-doc-editor {
  min-height: 24rem;
}

/* 계정 발급 현황 열한 칸. 라벨을 칸마다 붙이면 창이 두 배로 길어져서
   자리표시 글이 라벨 노릇을 한다. */
.pm-account-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: var(--jsini-sp-2xs);
}

/* ── Git 모니터링 (`pm-gitmon`) ─────────────────────────────

   **이 화면은 쪽이 구른다.** 업무 화면 대부분은 본문 높이 안에서 끝나지만
   (`app.css` 의 `.jsini-shell__content:has(> .commcont)`), 그 규칙은 자료
   판이 **하나**일 때를 본 것이다 — 그 하나가 남은 높이를 다 먹고 표만
   자기 안에서 구른다.

   여기는 저장소를 고르면 판이 **일곱**까지 쌓인다(가지 · 풀 리퀘스트 ·
   방문 · 깨진 자리 · GHCR · 릴리스 · 기여자). 그 규칙에 걸리면 일곱이
   `flex: 1` 로 한 화면 높이를 나눠 갖고, 본문이 `overflow: hidden` 이라
   **넘친 것은 굴려서 볼 수도 없다** — 저장소를 고르는 순간 아래가 통째로
   사라졌다. 고르기 전에는 판이 하나도 없어 규칙이 안 걸리니 멀쩡해 보이고,
   그래서 「고르면 아래가 안 보인다」로 나타난다.

   덩어리로 감싸 그 규칙에 **안 걸리게** 한다 — `:has(> .commcont)` 는 `>`
   로 겨누므로 한 겹만 넣으면 안 맞는다. 본문은 제 기본값(`overflow: auto`)
   으로 돌아가고 쪽이 구른다. AI 작업 현황(`pm-aidash`)이 같은 이유로 같은
   모양이다.

   맨 위 저장소 표는 그대로 `--commgrd-max-height`(58vh) 에 묶여 자기 안에서
   구른다 — 저장소가 서른 개여도 아래 판들이 화면 밖으로 밀리지 않는다.
   ---------------------------------------------------------- */

.pm-gitmon {
  display: flex;
  flex-direction: column;
  gap: var(--jsini-sp-lg);
}

/* 판 사이 간격은 위 `gap` 이 정한다 — 공통 규칙(`.commcont + .commcont`)이
   함께 걸리면 두 배로 벌어진다. 이 화면은 판이 죄다 형제로 붙어 있어서
   `pm-stack` 과 달리 실제로 겹친다. */
.pm-gitmon > .commcont + .commcont {
  margin-top: 0;
}

/* 깨진 단계 목록. 잡과 단계를 `잡 / 단계` 로 적어 두므로 한 줄이 길어진다 —
   고정폭으로 두어야 같은 잡의 여러 단계가 눈에 묶인다. */
.pm-failsteps {
  margin: 0;
  padding-left: var(--jsini-sp-md);
  font-family: var(--jsini-font-mono);
  font-size: var(--jsini-fs-sm);
  line-height: 1.8;
}

/* 상세 모드의 제목. 목록으로 돌아가는 단추 옆에 어느 건을 보고 있는지 적는다
   — 구역이 다섯이라 아래로 내려가면 무엇을 고치고 있었는지 잊는다. */
.pm-detail-title {
  font-weight: 600;
  font-size: var(--jsini-fs-md);
  margin-left: var(--jsini-sp-xs);
}

/* ── 개발자 정보 — 목차와 문서 ────────────────────────────── */

/* 목차. 제목만 세운다 — 긴 제목은 잘라서 한 줄로 둔다. 두 줄로 흐르면
   쪽이 열몇일 때 목차가 본문만큼 길어진다. */
.pm-toc {
  display: flex;
  flex-direction: column;
  gap: var(--jsini-sp-3xs, 2px);
}

.pm-toc__item {
  display: block;
  width: 100%;
  padding: var(--jsini-sp-2xs) var(--jsini-sp-xs);
  border: 0;
  border-radius: var(--jsini-radius-sm);
  background: transparent;
  color: var(--jsini-text);
  font-size: var(--jsini-fs-sm);
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}

.pm-toc__item:hover {
  background: var(--jsini-surface-2);
}

/* 고른 쪽. 색만으로 가르지 않고 굵기도 준다 — 색을 못 가르는 눈에도
   어느 쪽을 보고 있는지 남아야 한다. */
.pm-toc__item.is-on {
  background: var(--jsini-surface-2);
  color: var(--jsini-accent-text);
  font-weight: 600;
}

/* 문서 머리 줄 — 제목 · 고친 때 · 단추들이 한 줄에 앉는다. */
.pm-doc-head {
  display: flex;
  align-items: center;
  gap: var(--jsini-sp-xs);
  margin-bottom: var(--jsini-sp-sm);
}

.pm-doc-title {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: var(--jsini-fs-md);
  font-weight: 600;
}

.pm-doc-when {
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-2);
  white-space: nowrap;
}

/* ── 대상 git 상태 ────────────────────────────────────────────

   기계가 만든 글자(가지 이름 · sha · 경로 · 원격 주소)와 사람이 쓴 글자를
   **눈으로 가른다.** 같은 글꼴로 두면 커밋 제목과 가지 이름이 한 덩어리로
   읽혀서, 표에서 가지를 찾으려면 매번 읽어야 한다. */
.pm-gitmono {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: var(--jsini-fs-xs);
}

/* 배지가 값 옆에 붙을 때의 사이. 붙여 두면 가지 이름의 일부로 읽힌다. */
.pm-git__tag {
  margin-left: 0.375rem;
}

/* 커밋 제목은 sha 뒤에 따라붙는 곁글이다. 한 줄로 붙잡지 않으면 제목 하나가
   표를 세로로 늘린다(`pm-oneline` 과 함께 쓴다). */
.pm-git__subject {
  margin-left: 0.375rem;
  color: var(--jsini-text-muted);
}

/* 확인된 지 오래된 시각. **지운 것이 아니라 흐린 것**이다 — 값이 틀린 것보다
   값이 멈춘 것이 알아채기 어려워서 색으로 말한다. */
.pm-git__stale {
  color: var(--jsini-text-muted);
  font-style: italic;
}

/* ── 「자세히」 판 ───────────────────────────────────────────

   이름과 값을 두 단으로 세운다. 좁아지면 위아래로 접힌다 — 경로와 원격
   주소가 긴 값이라, 좁은 화면에서 두 단을 고집하면 값 쪽이 글자 서넛 폭이
   된다. */
.pm-git {
  display: grid;
  grid-template-columns: 7rem 1fr;
  gap: 0.5rem 1rem;
  margin: 0;
  align-items: baseline;
}

.pm-git > dt {
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-sm);
}

.pm-git > dd {
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* 사람이 봐야 하는 줄. 배지로 두지 않는 이유는 여기 오는 것이 한 줄짜리
   설명이라 배지에 담기지 않기 때문이다. */
.pm-git__bad {
  color: #d32f2f;
}

/* 정리 안 된 파일 목록. git 이 찍은 것이라 고정폭이고, 스무 줄까지만 온다. */
.pm-git__files {
  margin: 0.5rem 0 0;
  padding: 0.5rem 0.75rem;
  max-height: 14rem;
  overflow: auto;
  background: var(--jsini-surface);
  border: 1px solid var(--jsini-border);
  border-radius: 4px;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: var(--jsini-fs-xs);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

@media (max-width: 767px) {
  .pm-git {
    grid-template-columns: 1fr;
    gap: 0.125rem;
  }

  /* 한 단이 되면 이름과 값이 붙어야 한 짝으로 읽힌다 — 짝 사이만 벌린다. */
  .pm-git > dd + dt {
    margin-top: 0.625rem;
  }
}
.pm-ai-overview__title { flex: 1; }
.pm-ai-drafts-badge {
    background: none;
    border: none;
    color: var(--jsini-color-primary, #007bff);
    font-weight: 600;
    cursor: pointer;
    padding: 0 0.5rem;
}
.pm-ai-drafts-badge:hover { text-decoration: underline; }

.pm-usage-week-val {
    font-size: 1.15em;
    font-weight: 800;
}

/* ────────────────────────────────────────────────────────────
   로그 — 원문·번역 좌우 비교 (`LogTranslateView`)

   「AI 작업」 상세의 **로그 탭 그 자체다.** 머리띠 한 줄과 Monaco 비교
   편집기 한 틀이 전부다.

   보는 법을 고르는 줄(`.pm-peek__logmode`)과 그 옆의 글상자
   (`.pm-peek__pre--log`)가 함께 있었다. 원문을 보여 주는 자리가 둘이 되어
   걷어냈다 — 지금은 왼쪽 칸 하나다.
   ──────────────────────────────────────────────────────────── */

.pm-xlat {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 0 0 0.5rem;
}

/* 머리띠 — 줄바꿈 · 되돌리기 · 몇 줄을 옮겼나.

   좁은 화면에서는 줄바꿈으로 흘러내리게 둔다. 가로로 구르는 머리띠는
   거기서 단추 절반이 화면 밖에 남는다. */
.pm-xlat__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem;
}

.pm-xlat__stat {
  font-size: var(--jsini-fs-xs);
  opacity: 0.75;
}

/* 칸 이름 — 왼쪽 「원문」 · 오른쪽 「번역 · 한국어」.

   머리띠 끝의 한 마디(「왼쪽 원문 · 오른쪽 번역」)로는 모자랐다. 번역이 덜
   된 판은 좌우가 둘 다 영어라 **글만 봐서는 어느 쪽이 원문인지 못 가린다.**

   **반반을 믿고 적는 글이다.** 그래서 가운데 가름쇠를 끌어 옮기는 길을 닫았다
   (`EnableSplitViewResizing = false`). 끌 수 있게 두면 칸은 7:3 이 되는데
   이름표는 5:5 에 남아 이름이 남의 칸을 가리킨다. */
.pm-xlat__heads {
  display: flex;
  margin: 0 0 -0.25rem;
  font-size: var(--jsini-fs-xs);
  font-weight: 600;
  opacity: 0.8;
}

/* 이름표 한 칸. 이름은 왼쪽, 복사 아이콘은 **오른쪽 끝**이다 —
   그 칸의 글이 끝나는 자리에 그 칸을 복사하는 단추가 선다. */
.pm-xlat__head {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  flex: 1 1 50%;
  min-width: 0;
  padding: 0 0.5rem;
}

.pm-xlat__head-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 아이콘을 오른쪽 끝으로 민다. */
.pm-xlat__head .pm-edact {
  margin-left: auto;
}

/* 이름표는 작은 글씨인데 단추는 1.55rem 이라, 그대로 두면 이름표 줄만
   두 배로 두꺼워진다. 여기서는 글자 높이에 맞춰 줄인다. */
.pm-xlat__head .pm-edact__btn {
  width: 1.25rem;
  height: 1.25rem;
}

/* 오른쪽 이름표 앞의 세로선이 편집기 가운데 가름쇠와 같은 자리에 선다.
   휴대폰에서는 칸이 하나뿐이라 그 선이 설 자리가 없다(클래스를 안 붙인다). */
.pm-xlat__head--right {
  border-left: 1px solid var(--jsini-border);
}

/* ── 휴대폰 — 번역 칸 하나만 선다 ────────────────────────────

   좌우로 가르면 한 칸이 스무 글자라 **양쪽 다 못 읽는다.** 비교 편집기
   대신 번역 모델 하나를 보통 편집기에 걸어 통째로 보여 준다
   (`LogTranslateView` 머리말). 여기서 하는 일은 그 한 칸이 폭을 다
   쓰게 두는 것뿐이다. */
.pm-xlat--solo .pm-xlat__head {
  flex: 1 1 100%;
}

/* 누르면 무슨 일이 나는지. 눌러 보기 전에 읽혀야 하는 글이라 판 아래에 둔다. */
.pm-xlat__foot {
  font-size: var(--jsini-fs-xs);
  opacity: 0.7;
}

/* 진행과 누가 답했는지. 남는 폭을 먹어 오른쪽 끝에 선다. */
.pm-xlat__stat {
  margin-left: auto;
  text-align: right;
}

/* 로그가 설 틀.

   **높이를 여기서 준다.** Monaco 는 부모의 높이를 재서 제 몸을 그리므로,
   높이가 없는 상자에 넣으면 만들어지기는 하는데 줄이 하나도 안 보인다.
   값은 이 자리에 있던 글상자(22rem)보다 넉넉하다 — 좌우로 나뉘어 한 줄이
   접히는 일이 잦아 같은 높이면 보이는 줄이 절반이 된다.

   휴대폰의 목록(`pm-xlat__list`)도 같은 틀을 쓴다. 그쪽은 제가 구른다. */
.pm-xlat__editor {
  height: 30rem;
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-md);
  overflow: hidden;
}

.pm-xlat__inner {
  width: 100%;
  height: 100%;
}

/* 휴대폰에서는 화면 높이로 잰다. **글상자가 쓰던 값을 그대로 가져온다**
   (`.pm-peek__pre--log` 의 62dvh) — 이 틀이 그 상자를 대신하는 자리이고,
   30rem 은 작은 화면에서 거의 한 판을 다 먹는다. */
@media (max-width: 767px) {
  .pm-xlat__editor {
    height: 62dvh;
  }
}

/* ── 휴대폰 — 줄을 그대로 그린 목록 ──────────────────────────

   Monaco 가 아니다. **굴림을 브라우저에게 돌려주려고** 그렇게 한다
   (`LogTranslateView` 머리말의 표 — 32ms 넘긴 프레임 67 ↔ 8). Monaco 는
   손가락 굴리기를 제 손으로 해서 한 프레임 한 프레임이 본문 스레드의
   일인데, 네이티브 굴림은 합성기에서 돈다.

   **바닥 여백이 이 판의 핵심이다.** 화면 아래에 붙박인 단추 줄
   (`jsini-actbar` — 이어서 지시·재시도·확인)이 이 상자의 아래쪽을 덮고
   있어서, 여백이 없으면 끝까지 굴려도 마지막 줄이 그 줄 뒤에 깔린다.
   재어 보면 390×844 에서 상자가 265~790px 이고 단추 줄이 711~784px 이라
   **아래 79px 이 가려진다** — 마지막 네댓 줄을 영영 못 본다.

   쪽을 굴려 피하기는 어렵다. 손가락이 닿는 자리가 거의 다 이 상자라
   **그 손짓을 상자가 먼저 먹는다** — 쪽이 따라 구르는 것은 상자가 끝에
   닿은 뒤이고, 300줄짜리 로그에서 그 끝은 한참 멀다.

   줄 높이(3.5rem)에 숨 쉴 틈을 더해 둔다. 그만큼 안쪽으로 비켜서면
   끝까지 굴렸을 때 마지막 줄이 단추 줄 **위**에 선다(재어 확인 —
   705px ↔ 줄 윗금 711px).

   **`box-sizing` 을 적어야 한다.** app.css 에 전역 규칙이 없어서, 안 적으면
   여백이 안쪽을 비키는 대신 **상자를 그만큼 키운다**(재어 확인 — 높이가
   523 → 607px 이 되고 가려지는 양은 그대로다). */
.pm-xlat__list {
  box-sizing: border-box;
  height: 100%;
  overflow: auto;
  padding-bottom: calc(var(--jsini-actbar-height) + 1.75rem);
  font-family: var(--jsini-font-mono, ui-monospace, "Cascadia Mono", "D2Coding", monospace);
  font-size: var(--jsini-fs-sm);
  line-height: 1.45;

  /* 세로만 브라우저에게 준다. 가로는 줄바꿈을 끈 판에서 상자가 받는다. */
  touch-action: pan-y;
}

/* 줄 하나. **빈 줄도 자리를 갖는다** — 로그의 빈 줄이 사라지면 토막이
   붙어 버려 어디서 끊겼는지가 안 보인다. */
.pm-xlat__row {
  display: flex;
  gap: 0.5rem;
  padding: 0 0.5rem;
  min-height: 1.45em;
  cursor: pointer;
}

/* 줄 번호. **고르기에서 뺀다** — 줄을 끌어 글을 복사할 때 번호가 함께
   딸려 오면 붙여 넣은 로그가 못 쓰게 된다. */
.pm-xlat__rownum {
  flex: 0 0 auto;
  min-width: 2.5em;
  text-align: right;
  color: var(--jsini-text-muted);
  user-select: none;
}

/* 글. 줄바꿈은 켜고 시작한다 — 맞출 짝이 없는 자리라 끈 채로 두면 로그
   한 줄이 화면 폭의 몇 배다. */
.pm-xlat__rowtext {
  flex: 1 1 auto;
  min-width: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* 줄바꿈을 끄면 한 줄로 두고 **상자가 가로로 구른다.** */
.pm-xlat__list--nowrap {
  overflow-x: auto;
  touch-action: pan-x pan-y;
}

.pm-xlat__list--nowrap .pm-xlat__row {
  width: max-content;
  min-width: 100%;
}

.pm-xlat__list--nowrap .pm-xlat__rowtext {
  white-space: pre;
  overflow-wrap: normal;
}

/* 누른 줄. **비교판의 불과 같은 색이다** — 기기를 돌려 넓은 화면이 되면
   같은 줄에 같은 색이 서야 한 화면으로 읽힌다. */
.pm-xlat__row--hit {
  background: color-mix(in srgb, var(--jsini-accent, #0054a6) 22%, transparent);
  box-shadow: inset 2px 0 0 var(--jsini-accent, #0054a6);
}

.pm-xlat__row--hit .pm-xlat__rownum {
  color: var(--jsini-accent, #0054a6);
  font-weight: 700;
}

/* 지금 옮기고 있는 줄. 로컬 LLM 왕복이 몇 초라, 표시가 없으면 **눌러도
   아무 일이 없는 것**으로 보인다. */
.pm-xlat__row--redo {
  background: color-mix(in srgb, var(--jsini-warning, #f0a500) 26%, transparent);
  animation: pm-xlat-redo 1.1s ease-in-out infinite;
}

.pm-xlat__row--redo .pm-xlat__rownum {
  color: var(--jsini-warning, #f0a500);
  font-weight: 700;
}

.pm-xlat__loading {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  font-size: var(--jsini-fs-xs);
  opacity: 0.7;
}

/* 비교 편집기의 바뀜 색을 눌러 둔다.

   Monaco 의 비교 편집기는 **고친 자리**를 보여 주려고 만들어진 것이라,
   다른 줄마다 지우기 빨강 · 더하기 초록을 깐다. 그런데 여기서 좌우가 다른
   것은 고친 것이 아니라 **번역**이고, 번역은 거의 모든 줄이 다르다 —
   그대로 두면 판 전체가 빨강과 초록으로 덮여 글자가 안 읽힌다.

   지우지 않고 **눌러만 둔다.** 옅게 남겨 두면 「좌우가 같은 줄」(아직 안
   옮긴 줄)과 「옮겨진 줄」이 여전히 구분된다 — 어디까지 왔는지를 머리띠의
   숫자 말고 판에서도 읽을 수 있다. */
.pm-xlat .line-insert,
.pm-xlat .line-delete,
.pm-xlat .char-insert,
.pm-xlat .char-delete {
  background: color-mix(in srgb, var(--jsini-color-primary, #0054a6) 6%, transparent) !important;
  border: none !important;
}

/* 좌우 줄 수가 같아 끼울 빈 칸이 없으므로 빗금 칸은 뜰 일이 거의 없지만,
   뜰 때 무늬가 튀지 않게 함께 눌러 둔다. */
.pm-xlat .diagonal-fill {
  opacity: 0.25;
}

/* ── 커서가 선 줄에 켜는 불 ──────────────────────────────────

   **좌우 두 칸에 같은 색이 선다.** 왼쪽 108번째 줄을 누르면 오른쪽 108번째
   줄에도 같은 불이 켜져, 그 줄의 번역이 무엇인지 눈으로 따라갈 필요가 없다.

   Monaco 가 들고 있는 「지금 줄」 표시(`renderLineHighlight`)를 끄고 이것으로
   대신한다 — 그것은 **커서가 있는 칸에만** 켜져서 반쪽이다.

   **강조색을 쓰고 `!important` 를 붙인다.** 이 줄에는 비교 편집기가 깐 바꿈
   색(`.line-insert`)이 이미 얹혀 있고 그쪽이 `!important` 다. 같은 무게로
   적지 않으면 **번역된 줄에서만 불이 안 켜진다** — 하필 거의 모든 줄이다. */
.pm-xlat .pm-xlat__hit {
  background: color-mix(in srgb, var(--jsini-accent, #0054a6) 22%, transparent) !important;
  box-shadow: inset 2px 0 0 var(--jsini-accent, #0054a6);
}

/* 줄 번호도 함께 짙게. 좌우를 번호로 짚어 말할 때(「108번째 줄」) 그 번호가
   눈에 먼저 들어와야 한다. */
.pm-xlat .pm-xlat__hit-num {
  color: var(--jsini-accent, #0054a6) !important;
  font-weight: 700;
}

/* 지금 혼자 다시 옮기고 있는 줄. **오른쪽에만** 선다 — 바뀌는 것이 그쪽이다.

   로컬 LLM 왕복이 몇 초라, 표시가 없으면 **눌러도 아무 일이 없는 것**으로
   보인다. 머리띠의 글자(「108번째 줄을 다시 옮기는 중…」)는 눈이 가 있는
   자리에서 멀다. */
.pm-xlat .pm-xlat__redo {
  background: color-mix(in srgb, var(--jsini-warning, #f0a500) 26%, transparent) !important;
  animation: pm-xlat-redo 1.1s ease-in-out infinite;
}

.pm-xlat .pm-xlat__redo-num {
  color: var(--jsini-warning, #f0a500) !important;
  font-weight: 700;
}

@keyframes pm-xlat-redo {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

/* 움직임을 반기지 않는 사람에게는 깜박임 대신 색만 남긴다. */
@media (prefers-reduced-motion: reduce) {
  .pm-xlat .pm-xlat__redo,
  .pm-xlat__row--redo {
    animation: none;
  }
}
