/* =========================================================================
   adops.css — 광고운영관리-웹 앱 CSS  (㉳ 전사 디자인 키트 도입, 2026-08-02)

   ⚠️ 링크 순서: cape-ui-tokens.css → cape-ui-components.css → **이 파일**.
      앞의 둘은 CAPE-Alfred 원본의 **복사본**이다 — 고치지 마세요. 총괄이
      「재복사 필요」로 플래그하면 통째로 덮어씁니다. 우리 것은 이 파일 하나뿐입니다.

   구성:  ① 강조색   ② 앱 크롬   ③ 글자표   ④ 공용 부품   ⑤ 사용성   ⑥ 화면별 잔여
   ========================================================================= */

/* --- ① 강조색 — 우리 렌즈(보라) [재윤 확정 2026-08-02] -------------------
   전사 총괄 예시색을 그대로 쓴다. 나머지 두 값은 같은 계열의 표준 톤이다
   (키트 README 는 --accent 하나만 말하지만 실제 토큰은 3개 세트다). */
:root {
  --accent:      #7c3aed;
  --accent-ink:  #5b21b6;
  --accent-soft: #f5f3ff;
}

/* --- ② 앱 크롬: 사이드바 셸 (㉲-1) -------------------------------------
   ㉲-1(2026-08-04) — 상단 띠(`.appbar`+가로 `.nav`)를 걷고 좌측 사이드바로
   바꿨다. 키트 `.appbar`·`.nav` 는 **본문 안에 뜨는 카드/탭** 전제(둥근
   모서리·그림자·아래 여백)라 가로 띠에도 세로 사이드바에도 그대로는 안
   맞는다 — 그래서 우리 클래스(`.shell`/`.side`/`.brand`/`.sec`/`.gchip`)를
   새로 정의한다. 값은 전부 토큰에서 온다(`#166534`·`#991b1b` 두 개만 예외 —
   진한 상태 잉크는 키트에 대응 토큰이 없어 하드코딩한다, deploy.html 절과
   같은 알려진 공백). 키트 파일 자체는 고치지 않는다(README 가 정한 대로 앱
   CSS 를 맨 뒤에 링크해 덮는다). */
.shell { display:flex; min-height:100vh; }
.side { width:220px; flex:none; background:var(--surface);
        border-right:1px solid var(--border); padding:20px 14px;
        position:sticky; top:0; height:100vh; overflow-y:auto; }
.brand { font-weight:800; font-size:var(--fs-lg); color:var(--ink-strong);
         padding:0 10px 14px; }
.brand small { display:block; font-weight:500; color:var(--muted);
               font-size:var(--fs-xs); margin-top:2px; }
/* 🔴 Task 3 리뷰 Important 2 — 이 `<nav>` 는 클래스가 없다(위 `_nav.html` 주석
   참고). `.side .nav`(클래스 선택자)로 걸면 마크업이 다시 `class="nav"` 를
   갖는 순간 키트 `.nav`/`.nav a`/`.nav a.active`(cape-ui-components.css:21~24 —
   가로 탭 전제의 `border-bottom`·`margin-bottom`·active 보라 밑줄)가 함께
   상속돼, 우리가 여기서 안 덮은 속성만 조용히 새어 들어온다. **타입 선택자**
   (`.side nav`)로 걸어 그 상속 경로 자체를 끊는다 — 승인된 프로토타입과 같은
   방식. */
.side nav { display:block; background:transparent; border:0; padding:0; }
/* ── Task 6(㉲-2) — 번호 칩 + 건수 뱃지.
   `display:block` → `flex` 로 바꾼 이유는 장식이 아니다 — 뱃지를 오른쪽 끝에
   붙이는 `margin-left:auto` 가 block 안에서는 동작하지 않는다. 기존 선언(패딩·
   여백·모양)은 그대로 두고 `display` 만 고쳐 쓴다 — 덮어쓰는 두 번째 규칙을
   더하면 "어느 쪽이 최종값인가"가 다음 사람에게 안 보인다(§76 #4 가 클래스를
   덮지 않고 떼어낸 것과 같은 이유). */
.side nav a { display:flex; align-items:center; gap:8px; padding:9px 10px; margin:2px 0;
               border-radius:var(--r-md); color:var(--ink);
               text-decoration:none; font-size:var(--fs-sm); font-weight:500; }
.side nav a:hover { background:var(--surface-2); }
.side nav a.active { background:var(--accent-soft); color:var(--accent-ink);
                      font-weight:700; }
.side nav a .n { flex:none; width:20px; height:20px; border-radius:var(--r-full);
  background:var(--surface-2); color:var(--muted); font-size:11px; font-weight:700;
  display:flex; align-items:center; justify-content:center; }
.side nav a.active .n { background:var(--accent); color:#fff; }
/* 0·확인 불가일 때는 아예 안 그린다(hidden) — 「0건」을 뱃지로 말하지 않는다. */
.side nav a .navcount { margin-left:auto; font-size:var(--fs-xs); font-weight:700;
  color:#fff; background:var(--warn); border-radius:var(--r-full); padding:1px 7px; }
.side .sec { display:block; font-size:var(--fs-xs); color:var(--faint);
                  font-weight:700; padding:14px 10px 4px; }
.side .globals { margin-top:18px; border-top:1px solid var(--border);
                 padding-top:12px; }
/* 게이트 칩 — 최종 전체 리뷰 I3 이후 `<div>` 가 아니라 `<a>` 다(누르면 `/batch2`
   의 명부 대조 카드로 간다). 그래서 링크 기본 밑줄을 끄고, 대신 hover 에서만
   밑줄을 켜 「눌리는 것」임을 알린다 — 밑줄은 `.txt` 에 직접 건다(`.gchip` 이
   flex 컨테이너라 컨테이너에 건 text-decoration 은 브라우저마다 자식에게
   내려가는 정도가 다르다). 색·배경은 아래 상태별 규칙(ok·bad)이 이미 잡으므로
   hover 에서 건드리지 않는다(같은 특이도라 순서 싸움이 난다).
   ⚠️ 이 주석에 상태 선택자를 **완전한 형태로 적지 않는다** —
      `tests/test_web_nav.py` 가 규칙 본문을 잘라 볼 때 `css.split(...)` 이 주석의
      첫 등장에 걸려 엉뚱한 조각을 검사하게 된다(실측으로 한 번 깨졌다). */
.gchip { display:flex; align-items:center; gap:7px; font-size:var(--fs-xs);
         padding:6px 10px; border-radius:var(--r-md);
         border:1px solid var(--border); background:var(--surface-2);
         color:var(--muted); margin-bottom:6px; text-decoration:none; }
.gchip:hover .txt { text-decoration:underline; }
/* 220px 사이드바에서 한글이 낱글자로 쪼개지는 것을 막는다(실측: 「명부 대조 기 /
   록 없음」으로 끊겼다). `keep-all` 은 어절 안에서 안 끊고, `anywhere` 는 어절
   하나가 폭보다 길 때만(엔진 원문 fallback 의 `scripts/…py` 같은 긴 토큰) 끊게
   해 사이드바 밖으로 삐져나가는 것을 막는다 — 둘이 한 쌍이다. */
.gchip .txt { word-break:keep-all; overflow-wrap:anywhere; }
/* 「눌러서 어디론가 간다」를 쉬고 있을 때도 보이게 — hover 밑줄만으로는 마우스를
   올려본 사람만 알 수 있고, 이 칩의 존재 이유가 실무자를 고칠 수 있는 버튼으로
   데려가는 것이라 발견 자체가 기능이다. 장식이 아니라 안내라서 `::after`(DOM 밖)
   로 둔다 — 화면을 읽는 검사들이 이 글자를 내용으로 오해하지 않게. */
.gchip::after { content:"\203A"; margin-left:auto; padding-left:4px; opacity:.55;
                font-size:var(--fs-sm); line-height:1; }
.gchip .dot { width:8px; height:8px; border-radius:var(--r-full); flex:none;
              background:var(--faint); }
.gchip.ok   { border-color:var(--pos-border);  background:var(--pos-soft);  color:#166534; }
.gchip.ok .dot   { background:var(--pos); }
.gchip.bad  { border-color:var(--neg-border);  background:var(--neg-soft);  color:#991b1b; }
.gchip.bad .dot  { background:var(--neg); }
/* 🔴 최종 전체 리뷰 M5 — `unknown`(엔진 판정 자체가 실패) 상태에 규칙이 없어
   기본 회색으로 **떨어지고** 있었다. 회색을 그대로 쓰되 규칙을 명시해 「의도한
   회색」으로 만든다: /guide 의 4색 어휘(초록·주황·빨강·보라)는 전부 **무언가를
   주장하는** 색인데, 여기서 우리가 아는 것은 「모른다」뿐이라 넷 중 어느 것도
   쓰면 거짓이 된다(주황=주의는 「괜찮은데 곧 위험」이라는 주장이고, 빨강=차단은
   실제로 막혔다는 주장이다). 그래서 색이 아니라 **점선 테두리**로 불확실을
   말한다 — 색 어휘를 늘리지 않고 「확정된 회색」과도 구분된다. /guide 의 색 표
   각주가 이 회색을 「색 없음 = 아무것도 주장하지 않음」으로 함께 선언한다. */
.gchip.unknown { border-style:dashed; }
.col { flex:1; min-width:0; }
@media (max-width:900px) { .shell { display:block; }
  .side { position:static; height:auto; width:auto;
          border-right:0; border-bottom:1px solid var(--border); } }

/* 카드형 화면의 페이지 머리(`<main>` 앞 형제) */
header { padding: 20px 28px; background: var(--surface);
         border-bottom: 1px solid var(--border); }
header h1 { margin: 0; font-size: var(--fs-lg); }
header p { margin: 4px 0 0; color: var(--muted); font-size: var(--fs-sm); }

/* 맨몸 화면은 `<h1>` 이 `<main>` 안에 있다(브라우저 테스트가 그 구조를 문다) */
main.wrap h1 { font-size: var(--fs-xl); margin: 0 0 var(--sp-3); }
main.wrap h2 { font-size: var(--fs-base); color: var(--muted);
               margin: 28px 0 10px; font-weight: 600; }

/* 화면 머리 한 줄 목적문(G9, `{% block purpose %}`) — 카드형(header 있음)·
   맨몸(header 없음) 화면 둘 다에서 `page_header` 바로 다음, `main.wrap` 바로
   앞에 뜬다. `:empty` 는 G9 가 11/11 을 강제해 지금은 안전망일 뿐이다. */
.purpose { padding: 10px 28px 0; color: var(--muted); font-size: var(--fs-sm); }
.purpose:empty { display: none; }

footer { text-align: center; color: var(--muted); font-size: var(--fs-xs);
         padding: 20px; }

/* --- ③ 글자표 -----------------------------------------------------------
   🔴 키트 `table.t` 는 **숫자 대시보드용**이다(우측정렬·nowrap — 주간실적 DNA).
      우리 표는 한글 세트명·R2 URL·거절 사유가 들어가는 **글자 콘솔**이라 그대로
      얹으면 후퇴다. 그래서 우리 변형을 만든다.
   마크업을 안 고치려고 클래스가 아니라 **문맥 선택자**로 건다 — 지금 화면들이 맨
      `table`/`th`/`td` 에 규칙을 걸고 있어서 클래스를 요구하면 11개 화면의 마크업을
      전부 고쳐야 하고, 그건 이 슬라이스가 안 하기로 한 일이다.
   🔴 `overflow:hidden`(둥근 모서리용)을 **일부러 뺐다** — 그게 있으면 아래 ⑤①의
      「머리글 고정」이 동작하지 않는다. 모서리보다 머리글이 실무자에게 중요하다. */
main.wrap table { width: 100%; border-collapse: collapse; background: var(--surface);
                  border: 1px solid var(--border); font-size: var(--fs-sm);
                  font-variant-numeric: tabular-nums; margin: var(--sp-4) 0; }
main.wrap th, main.wrap td { text-align: left; padding: 9px 14px;
                             border-bottom: 1px solid var(--border);
                             vertical-align: top; overflow-wrap: anywhere; }
main.wrap th { background: var(--surface-2); color: var(--muted);
               font-weight: 600; font-size: var(--fs-xs); }
main.wrap tbody tr:last-child td { border-bottom: none; }

/* --- ④ 공용 부품 — **클래스 이름은 그대로**, 값만 토큰으로 --------------- */
.note, .sub { color: var(--muted); font-size: var(--fs-sm); }
.err  { color: var(--neg); white-space: pre-wrap; }
.warn { color: var(--warn); }
.ok   { color: var(--pos); }

/* 결과 목록의 성공/실패 (issuance·media·media_publish 의 `#results li`) */
li.ok   { color: var(--pos); }
li.fail { color: var(--neg); }

button, .btn { font-family: var(--font); font-size: var(--fs-sm); font-weight: 600;
               padding: 8px 16px; margin: 4px 8px 4px 0;
               border: 1px solid var(--border-2); border-radius: var(--r-md);
               background: var(--surface); color: var(--ink); cursor: pointer; }
button:hover:not(:disabled) { background: var(--surface-2); }
/* 🔴 **고른 칸은 hover 에도 강조색을 지킨다**(2026-08-10 눈 대조가 잡았다).
   키트의 `.pill.on` 은 «강조색 배경 + 흰 글씨»인데, 위 `button:hover` 가 배경만
   회색(`--surface-2`)으로 덮어 **흰 글씨가 흰 배경에 얹힌다** — 방금 고른 모드가
   마우스를 올려둔 동안 통째로 사라진다(담당자의 커서는 방금 누른 그 칸에 있다).
   `button:hover:not(:disabled)` 가 (0,2,1)로 `.pill.on`(0,2,0)을 이기기 때문이라,
   같은 무게 이상으로 되받는다. 색은 `--accent-ink`(같은 계열의 진한 색)다. */
.pill.on:hover:not(:disabled) { background: var(--accent-ink); }
/* 🔴 Task 3 리뷰 Important 3 — `.btn` 은 여태 `<button>` 에만 쓰였다(11개 템플릿
   전수 확인, 2026-08-04 — `today.html` 의 「지금 해야 할 일」 CTA 가 `<a class="btn">`
   로 쓴 첫 사례). `button`·`.btn` 어느 규칙도 `text-decoration` 을 안 건드리므로
   앵커 기본 밑줄이 그대로 샌다 — 버튼처럼 보여야 할 게 링크처럼 밑줄이 쳐졌다.
   앞으로 다른 화면이 `<a class="btn">` 를 써도 같은 사고가 안 나게 **클래스
   차원**(`a.btn`)에서 막는다 — `a.kpi`(:429)·`.pipe .stage`(:459)가 이미 같은
   이유로 각자 `text-decoration:none` 을 갖고 있는 것과 같은 관례다. */
a.btn { text-decoration: none; }

code { background: var(--surface-2); padding: 1px 5px;
       border-radius: var(--r-sm); font-size: var(--fs-xs); }

/* 상태 pill(`.action`·`.tag`) — 화면마다 뒤에 붙는 이름이 다르므로(§⑥) 여기선
   모양만 정하고 색은 각 화면 절에서 준다. */
.action, .tag, .badge { display: inline-block; padding: 2px 8px;
                        border-radius: var(--r-full); font-size: 11px;
                        font-weight: 600; }

/* 배너(`.banner-warn`·`.banner-info`) */
.banner, .banner-warn, .banner-info {
  border-radius: var(--r-md); padding: 10px 16px; margin: 0 0 var(--sp-4);
  font-size: var(--fs-sm); border: 1px solid var(--border); }
.banner strong, .banner-warn strong, .banner-info strong {
  display: block; margin-bottom: 4px; }
.banner ul, .banner-warn ul, .banner-info ul { margin: 4px 0 0; padding-left: 18px; }
.banner-warn { background: var(--warn-soft); border-color: var(--warn-border);
               color: #92400e; }
.banner-info { background: var(--info-soft); border-color: var(--info-border);
               color: var(--accent-ink); }
/* 🔴 ㉲-2 — 브리프 today.html 마크업이 `class="banner banner-neg tight"` 를
   쓰는데, 브리프 Step 5 CSS 블록엔 `.banner-neg` 규칙이 없었다(형제 두 변형
   `.banner-warn`/`.banner-info` 는 있는데 이것만 빠짐 — 실측: 없이 두면 배너가
   그냥 회색 테두리 상자로 렌더돼 "지금은 배포할 수 없습니다" 급한 배너가 안
   급해 보인다). 같은 패턴으로 채운다. */
.banner-neg  { background: var(--neg-soft);  border-color: var(--neg-border);
               color: #991b1b; }
/* 🔴 Task 3 리뷰 ruling #4(재윤) — 프로토타입은 이 배너가 **한 줄**이다(헤드라인+
   설명이 나란히, CTA 는 맨 오른쪽 고정). 처음 버전은 `.banner strong{display:block}`
   (위 배너 공용 규칙)를 그대로 물려받아 헤드라인이 줄을 하나 먹고, 버튼(`.go`,
   `margin-top:1rem`)이 본문 중간에 걸려 두 줄로 깨졌다 — 프로토타입보다 산만해
   보였다(재윤 실측 지적, "(c) 는 결함이지 네 번째 범주가 아니다"). 다른 배너
   변형(`.banner-warn`/`.banner-info`)은 그대로 두고 **`.banner-neg` 하나에만**
   좁혀서 한 줄로 편다 — 새 배너 변형을 만들지 않고 이 변형 자체를 고치는
   쪽이다. 마크업은 `.lead`(헤드라인+설명 묶음) + `.go`(버튼) 두 조각뿐이라
   `space-between` 하나로 좌우 배치가 끝난다. */
.banner-neg { display: flex; align-items: center; flex-wrap: wrap;
              justify-content: space-between; gap: 6px 16px; }
.banner-neg strong { display: inline; margin-bottom: 0; }
.banner-neg .go { margin-top: 0; flex: none; }
/* 🔴 `.banner-neg[hidden] { display: none; }` 이 여기 있었다 — 파일 맨 끝의 **전역
   `[hidden]` 차단**으로 대체했다(조각 4 T3 리뷰 M3). 좁은 규칙을 함께 두면 어느
   한쪽을 지워도 다른 쪽이 덮어 **뮤테이션이 아무것도 증명하지 못한다**(중복은 방어가
   아니라 사각지대다 — 이 태스크가 `showStep(1)` 에서 이미 한 판단과 같다). */

/* ── 목록 제어(㉰) — 검색칸·건수·전체 보기. 부품 `ck.listControl` 이 쓴다.
     ⚠️ 색·크기는 **토큰만** 쓴다(㉳ 가 세운 체계 — 값을 박으면 다크모드·전사 키트
        갱신에서 이 줄만 홀로 낡는다). `.note`/`.sub` 와 같은 잣대다. */
.list-controls { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap;
                 margin: .75rem 0; }
.list-controls input[type="search"] { flex: 1 1 16rem; min-width: 0; }
.list-controls .list-count { color: var(--muted); font-size: var(--fs-sm); }

/* --- ⑤ 사용성 (설계서 §5 — CSS 로만 되는 것) ---------------------------- */
/* ① 긴 표 머리글 고정 — 편성표를 100행 스크롤해도 열 이름이 안 사라진다.
      ⚠️ 조상에 `overflow:hidden` 이 있으면 동작하지 않는다(§③ 참고).
      ✅ 해소(㉱ Task 9, D11) — status.html 2개·notion_mirror.html 2개, 이 4표는
      머리글 `<tr><th>…` 를 `<thead>` 로, 본문을 `<tbody>` 로 감싸 sticky 가
      저절로 살아났다(마크업만 고쳤다 — 이 규칙 자체는 그대로).
      ⚠️ 그대로 두는 머리글 없는 표 3개(설계 D11 — 라벨-값 2~3행 쌍이라 머리글이
      무의미): media.html·media_publish.html 의 잠복(dormancy) 여부 표 2개
      (칸이 "Drive"/"R2"/"메타" 같은 라벨 + 값 한 쌍뿐) + notion_mirror.html 의
      소재별 상속 최종값 표(`p.effective` 루프 — 속성명이 매 칸 왼쪽에 이미
      라벨로 찍힌다). 이 셋은 이번 태스크의 파일 범위 밖(media.html·
      media_publish.html)이거나 의도적으로 안 건드린다. */
main.wrap thead th { position: sticky; top: 0; z-index: 1; }
/* ② 행 hover — 마우스 둔 행이 밝아져 눈이 행을 안 잃는다 */
main.wrap tbody tr:hover td { background: var(--surface-2); }
/* ③ 버튼 상태 — `opacity:.4` 만으로는 «눌렸는지» 를 모른다 */
button:disabled, .btn:disabled { opacity: 1; background: var(--surface-2);
                                 color: var(--faint); border-color: var(--border);
                                 cursor: not-allowed; }
/* ④ 화면 폭 통일 — 지금 960·1024·1040·1100·1152px 로 갈려 있다.
      키트 `.wrap` 의 1120px 을 그대로 쓴다(값은 키트가 정한다). */
/* ⑤ 오류·경고색 통일 — `#b00020`·`#b42318`·`var(--err)` 세 가지가 같은 뜻으로
      쓰이던 것을 `var(--neg)`/`var(--warn)` 하나로(위 ④절). */

/* =========================================================================
   ⑥ 화면별 잔여 규칙
   🔴 이상적으로는 없어야 한다. 지금 없애려면 마크업을 고쳐야 하고 그건 ㉳ 범위 밖이다
      (설계서 §3). **어느 화면 것인지 주석으로 남겨** ㉰·㉱ 가 그 화면을 만질 때
      같이 걷어내게 한다. ⚠️ 이 절의 **존재 자체는 부채가 아니다** — 화면별
      잔여가 있을 정당한 자리다. 부채는 그 안의 **죽은 셀렉터**뿐이다.

   ㉱ Task 9(D11) — 죽은 셀렉터 실측·정리. 지운 것: batch2.html 절의 옛 `data-action`
   속성값 선택자 우회(값 「신규생성」·「기존재사용」 각각 한 줄 + `#plan` 스코프 회색
   기본값 규칙) — `planActionCell` 이 공용 부품 `ck.actionCell()` 로 옮기며 클래스
   선택자(`action-` 접두 + 판정값, 아래 batch2.html 절 참고)로 대체했다. 그 외
   후보 7개(`cards`·`slots`·`notice`·
   `go`·`prop-page`·`banner`·`col-label`, 실측 grep 증거는 task-9-report.md)는
   전부 지금도 템플릿이 참조해 **살아 있다** — 삭제하지 않았다.
   지금 이 절에 남은 화면 블록(살아 있는 잔여): status.html·notion_mirror.html
   (카드+상태 태그) · notion_mirror.html(슬롯) · pause.html·perf.html(`.set`) ·
   deploy.html·deprecated_reorg.html(폐지 안내 상자) · issuance.html(판정
   pill·상속 아코디언·결과 목록) · media.html(잠복표 폭) · media.html·
   media_publish.html(배너 여백) · media_publish.html(잠복표 폭·배지색) ·
   batch2.html(이 슬라이스 최대 화면 — 동작 배지 포함) · _sync_button.html
   (네비 동기화 시각).
   🪦 조각5 T6(2026-08-10) — `/reorg` 화면(`reorg.html`)이 폐지되며 `.set`·`.ads`
   의 공용 소비처가 **하차+재편성**에서 **하차+성과 조치**(`perf.html`, 같은
   `.set`/`.ads` 마크업을 쓴다)로 옮겨갔다. 🔴 **`#msg` 는 옮겨가지 않았다** —
   `perf.html` 은 그 id 를 안 쓴다(자기 오류 메시지 자리는 `#perf-msg` 다).
   `#msg` 규칙은 여전히 `pause.html` **전용**이다(아래 그 절 주석 참고, 전체
   브랜치 리뷰가 이 자리의 오기를 잡았다). 그 화면 전용이던 `#reorg-form
   > label` 규칙은 소비처 자체가 없어져 지웠다(아래 옛 자리 참고).
   ========================================================================= */

/* --- status.html · notion_mirror.html (숫자 카드 + 상태 태그) ---
   🔴 `.card` 자체는 키트 컴포넌트에 이미 있다(`background`·`border`·`radius`·`shadow`,
   `cape-ui-components.css`) — 여기선 **덮어쓸 값만** 적는다. `.tag.stale` 은
   notion_mirror.html 에만 쓰이지만 이 절 제목대로 두 화면 공용 「상태 태그」
   묶음이라 여기 둔다. `min-width` 는 두 화면이 각각 130px·120px 로 갈려 있던 것을
   이관하며 130px 로 통일한다(재윤 승인 브리프 값 그대로 — Task 5). */
.cards { display: flex; gap: var(--sp-3); flex-wrap: wrap; }
.cards .card { min-width: 130px; padding: 14px 18px; margin-bottom: 0; }
.card .num   { font-size: var(--fs-2xl); font-weight: 700; }
.card .label { font-size: var(--fs-xs); color: var(--muted); margin-top: 2px; }
.card.warn .num { color: var(--warn); }
.tag.live, .tag.active, .tag.done { background: var(--pos-soft); color: var(--pos); }
.tag.manual, .tag.wip { background: var(--surface-2); color: var(--muted); }
.tag.stale { background: var(--warn-soft); color: #92400e; }

/* --- notion_mirror.html (슬롯 채움 표시) --- */
.slots { color: var(--muted); font-size: var(--fs-xs); }
.slots .filled { color: var(--pos); }
.slots .empty  { color: var(--warn); }

/* --- pause.html · perf.html (하차 정지 · 성과 조치 — 두 화면이 같은 `.set` 마크업)
   🪦 조각5 T6(2026-08-10) — `/reorg` 가 폐지되기 전에는 이 절이 「pause.html·
   reorg.html」이었다. `perf.html` 이 재편성의 「다른 세트로 이동」 액션을 흡수하며
   같은 `.set`/`.ads` 마크업을 그대로 쓰므로(`web/templates/perf.html`), 소비처가
   그쪽으로 옮겨갔을 뿐 규칙 자체는 안 바뀐다. --- */
.set { border: 1px solid var(--border-2); border-radius: var(--r-md);
       padding: .6rem .9rem; margin: .5rem 0; background: var(--surface); }
.ads { margin: .3rem 0 0 1.4rem; }
.set label { display: block; margin: .15rem 0; }
/* 🔴 2026-08-02 리뷰 Minor 1 — 이관 전 `.err` 는 굵었는데(`font-weight: bold`),
   그 굵기는 issuance·media·media_publish 의 `.err`(굵지 않음)까지 같이 굵히면
   시끄러워진다. 원래 굵었던 자리는 pause 의 오류 배너(`#msg`, 옛 reorg.html 도
   같은 id 를 썼다 — 그 화면은 조각5 T6 이 폐지했다) 뿐이었으므로 거기만 되살린다. */
#msg { font-weight: 700; }

/* --- deploy.html · deprecated_reorg.html (폐지 안내 — 묘비 화면의 「크게 빨간」
   안내 상자, 조각5 T6(2026-08-10)이 `/reorg` 를 이 모양으로 옮기며 재사용) --- */
/* `#991b1b`(진한 오류 잉크)에 해당하는 토큰이 키트에 없다 — 키트 자신도
   `.note-line.err` 에서 같은 값을 하드코딩한다(cape-ui-components.css:83).
   토큰이 생기면 그때 바꾼다. */
.notice { border: 3px solid var(--neg); background: var(--neg-soft); color: #991b1b;
          padding: 1.4rem 1.6rem; border-radius: var(--r-lg); }
.notice h1 { margin: 0 0 .6rem; font-size: var(--fs-xl); }
.notice p  { margin: .5rem 0; font-size: var(--fs-base); line-height: 1.5; }
.notice a  { color: #991b1b; font-weight: 700; }
.go { display: inline-block; margin-top: 1rem; padding: .6rem 1.1rem;
      background: var(--neg); color: #fff; border-radius: var(--r-md);
      text-decoration: none; font-weight: 700; }

/* --- issuance.html (판정 pill · 상속 속성 아코디언 · 결과 목록) ---
   🔴 `.action-*` 색은 issuance·media·media_publish 세 화면이 같은 이름을 공유해서
   여기 한 자리에 모았다(옛 세 파일 각각에 「값만 다른 같은 규칙」이 있었다) — 세
   화면의 옛 `<style>` 을 실제로 합집합한 목록이다(19종):
     녹색(성공): issue·adopt·fill·correct(issuance) + upload·register(media,
       media_publish 도 register 공유)
     주황(경고): drift·refresh(issuance) + changed·stale·not_final(media,
       media_publish 도 stale 공유)
     회색(중립): hold·noop·human·human-win·locked(issuance) + skip(media,
       media_publish 도 skip·locked 공유)
     빨강(거부): blocked(issuance) + denied(media, media_publish 도 denied 공유) */
.action-issue, .action-adopt, .action-fill, .action-correct,
.action-upload, .action-register { background: var(--pos-soft); color: var(--pos); }
.action-drift, .action-refresh, .action-changed, .action-stale,
.action-not_final { background: var(--warn-soft); color: #92400e; }
.action-hold, .action-noop, .action-human, .action-human-win,
.action-locked, .action-skip { background: var(--surface-2); color: var(--muted); }
.action-blocked, .action-denied { background: var(--neg-soft); color: var(--neg); }

details.prop-page { background: var(--surface); border: 1px solid var(--border);
                    border-radius: var(--r-lg); margin-bottom: 8px; padding: 4px 0; }
details.prop-page summary { cursor: pointer; padding: 9px 14px;
                            font-size: var(--fs-sm); color: var(--muted); }
main.wrap details.prop-page table { border: none; margin: 0; }

/* `#status`·`#results` 도 세 화면이 토씨까지 같은 규칙을 갖고 있었다(ID 선택자라
   화면마다 다시 적을 필요가 없다 — 여기 한 벌이면 세 화면 모두에 걸린다).
   🔴 ㉲-4(2026-08-05) — `/assets` 는 두 카드가 **한 문서**에 있어 등록 카드
   쪽만 id 를 `pub-` 접두로 개명했다(함정 ㉢, `_assets_publish.html` 머리
   주석). 이 규칙은 id **선택자**라 이름이 다르면 안 걸린다 — 개명하면서
   여기를 안 넓히면 등록 카드의 상태 문구·결과 목록만 스타일이 조용히
   빠진다(마크업은 멀쩡해 보여 눈에 안 띄는 종류의 결함). `#pub-status`·
   `#pub-results` 를 같은 규칙에 더해 둘 다 계속 같은 모양을 받게 한다. */
#status, #pub-status  { color: var(--muted); font-size: var(--fs-sm); margin: 8px 0;
           white-space: pre-wrap; }
#results, #pub-results { font-size: var(--fs-sm); margin: 6px 0 var(--sp-4); }
#results ul, #pub-results ul { margin: 0; padding-left: 18px; }

/* ── ㉲-3·4 접이식 부차 표. 내용을 지우지 않고 접기만 한다(재윤 확정 C안) —
   기능은 그대로 있고, 처음 화면에서 무엇이 먼저 보이나만 바뀐다. */
details.fold { border:1px solid var(--border); border-radius:var(--r-md);
  background:var(--surface); padding:10px 16px; margin:10px 0; }
details.fold > summary { cursor:pointer; font-weight:700; color:var(--ink-strong); }
details.fold[open] > summary { margin-bottom:10px; }

/* ── ㉲-3 리뷰(2026-08-05) — `/plans` 본문 맨 위 동기화 바(재윤 룰링: 프로토타입
   배치 그대로, 사이드바 버튼은 이 화면에서 억제). 마크업은 사이드바가 쓰던
   `_sync_button.html::sync_button_markup` **그대로**(id·스크립트 무변경) — 여기
   선택자는 그 매크로가 내는 자식들의 **시각 순서만** `order` 로 바꾼다(DOM 순서·
   `getElementById` 는 order 의 영향을 안 받는다). `.sync-meta`(④절)의
   `margin-left:.6rem` 은 사이드바(세로 스택) 전제라 여기서는 0으로 되돌린다 —
   `gap` 하나로 간격을 준다. */
.sync-bar > div { display:flex; align-items:center; flex-wrap:wrap;
  justify-content:space-between; gap:6px 16px; background:var(--surface);
  border:1px solid var(--border); border-radius:var(--r-md);
  padding:10px 16px; margin:0 0 var(--sp-4); }
.sync-bar #sync-when { order:1; margin-left:0; }
.sync-bar #sync-btn  { order:2; }
.sync-bar #sync-result { order:3; margin-left:0; }

/* --- media.html (잠복 여부 표 — 첫 칸 폭 고정) --- */
main.wrap table.dormancy td:first-child { width: 9rem; }

/* --- media.html · media_publish.html (배너 위 여백 — 인라인
   style="margin-top:12px" 를 클래스로 치환, ㉳ Task 6) --- */
.banner.tight { margin-top: 12px; }

/* --- media_publish.html (잠복 표 첫 칸 폭 — 인라인 style="width:9rem" 를
   클래스로 치환, ㉳ Task 6) --- */
main.wrap td.col-label { width: 9rem; }

/* --- media_publish.html (「수동(이 실행만)」 배지 색 — ④절 `.badge` 는 모양만
   정의하고(키트 컴포넌트도 마찬가지) 색은 화면 몫이다. 이관 전엔 `background:
   #eef3fb; color:#1f4b8f`(파랑)이었다.
   🔴 배경(`#eef3fb`→`var(--info-soft)`=`#eff6ff`)은 사실상 그대로지만, **글자색은
   파랑(`#1f4b8f`)에서 보라(`var(--accent-ink)`=`#5b21b6`)로 의도적으로 바뀐다** —
   ①절에서 확정한 이 프로젝트의 강조색(보라)을 따른 것이고, `.banner-info`(바로
   위 배너 규칙)가 이미 같은 치환을 하고 있어 관행과 일치한다. 「색을 그대로
   재현했다」가 아니다 — 다음에 헷갈리지 않도록 여기 적어둔다.
   🔴 Task 7 — `#products-table` 로 스코프를 좁혔다(이관 전엔 `.badge` 단독). 이
   규칙이 unscoped 였다면 batch2.html 이 합류하는 순간 `#manual-or-adv-badge`
   (뜻이 다른 「캠페인 예산관리방식」 배지)까지 보라로 물들었을 것이다 — 아래
   batch2 절 참고. */
#products-table .badge { background: var(--info-soft); color: var(--accent-ink);
                         margin-left: 6px; }

/* --- batch2.html (편성 2차 콘솔 — 이 슬라이스에서 가장 큰 화면) ---
   🔴 `<section>`·`.blocker`·`.blockers-box`·`.excluded`·`.partial`·`.warn-box`·
   `.precheck-row`·`.ad-outcome-*`·`.bucket`·`.member`·`#reconcile-card` 는 전부
   이 화면에만 있는 이름이다(11개 템플릿 전수 확인, Task 7) — unscoped 로 둬도
   새는 곳이 없다. */
/* ㉱ Task 3 — 워크플로 탭(전사 키트 `.seg`/`.pill`, cape-ui-components.css)의 여백만.
   모양(테두리·색)은 키트가 이미 정의하므로 이 화면은 위아래 간격만 더한다.
   🔴 조각 4 T4(2026-08-08) — 옛 `#workflow-tabs` 는 사라졌고 1단계의 모드 두 칸
      (`#dmode`)이 그 자리를 이어받았다. 규칙을 지우지 않고 **이름만 옮긴다** — 하는 일
      (키트 `.seg` 에 위아래 여백만 더한다)이 그대로이기 때문이다. 목업도 이 자리에
      `margin-bottom` 을 준다(1288). */
#dmode { margin: 1rem 0; }
main.wrap section { border-top: 2px solid var(--border); margin-top: 2.5rem;
                    padding-top: 1rem; }
#reconcile-card { border-top: 2px solid var(--border); margin-top: 1rem;
                  padding-top: 1rem; }
.blocker { color: var(--neg); font-weight: 700; margin: .3rem 0;
           white-space: pre-wrap; }   /* pre-wrap 이 없으면 "❌ 다시 실행하지
                                          마세요" 가 붉은 한 덩어리로 접힌다 */
.blockers-box:not(:empty) { border: 2px solid var(--neg); background: var(--neg-soft);
                            padding: .6rem 1rem; margin: 1rem 0; border-radius: var(--r-md); }
.excluded { background: var(--warn-soft); border: 1px solid var(--warn-border);
            padding: .6rem 1rem; margin: 1rem 0; border-radius: var(--r-md);
            font-size: var(--fs-sm); }
.partial  { border: 3px solid var(--neg); background: var(--neg-soft);
            padding: 1rem; margin: 1rem 0; border-radius: var(--r-md); }
.warn-box:not(:empty) { border: 1px solid var(--warn-border);
                        background: var(--warn-soft); padding: 8px 12px;
                        margin: 8px 0; border-radius: var(--r-md); }
.warn-box li { margin: 2px 0; }
.precheck-row   { background: var(--neg-soft); }   /* 재료 부족 = 광고에서 제외된다 */
.ad-outcome-fail { color: var(--neg); }
.ad-outcome-ok   { color: var(--pos); }
.ad-outcome-skip { color: var(--muted); }
.bucket  { width: 3rem; }
.member  { display: inline-block; margin-right: .8rem; white-space: nowrap; }
/* 🔴 Task 7 — 브리프 초안은 `main.wrap label { display: inline-block }` 이었다.
   그대로 두지 않았다: 특이성을 다시 계산하면 `main.wrap label`(id 0·class 1·
   type **2**)이 pause·reorg 의 `.set label`(id 0·class 1·type **1**)보다 더
   구체적이다 — class 개수가 같을 때는 type 선택자 수가 다음 기준이고 2>1이라
   `main.wrap label` 이 이긴다. unscoped 로 두면 이 규칙이 `.set label`의
   `display:block` 을 도로 `inline-block` 으로 덮어써 Task 4 가 고친 사고
   (스코프 없는 label 규칙이 `.set` 밖 라벨을 깬 것)와 같은 모양의 사고가 —
   이번엔 `.set` **안**의 라벨에서 — 재발한다. `#plan` 은 이 화면(batch2)에만
   있는 id 라 `:has()` 로 페이지 자체를 골라내면 다른 화면엔 이 규칙이 전혀
   안 걸려 특이성 다툼이 생길 자리가 없다. Playwright 로 실측(리포트 참고):
   이관 전후로 pause·reorg 의 `.set label` 은 `display: block` 그대로였다. */
body:has(#plan) label { display: inline-block; margin: .2rem .8rem .2rem 0; }
main.wrap pre { background: var(--surface-2); padding: 1rem; overflow-x: auto;
                font-size: var(--fs-xs); border-radius: var(--r-md); }

/* 편성 표 동작 배지 — ㉱ Task 9(D11) — `data-action` 속성 선택자 우회를 걷었다.
   그 우회의 사유(당시엔 `ck.actionCell()` 의 className 정규화가 ASCII 전용
   `[^\w-]`이라 한글 값이 「action-」 하나로 뭉개졌다)는 커밋 `bc76f1b` 가
   `\p{L}\p{N}` 로 고쳐 이미 사라졌다 — 이 화면만 우회를 그대로 남기고 있었다.
   이제 `planActionCell`(batch2.html)이 부품을 그대로 써서 `class="action
   action-신규생성"` 류를 그대로 내므로, 색은 다른 화면(issuance·media 등)과
   같은 자리(⑥절 issuance.html 절의 `.action-*` 목록)의 관례대로 클래스 선택자로 준다.
   ⚠️ `#plan` 스코프의 옛 기본 회색 배경 규칙(알려지지 않은 값 대비)도 함께
   지웠다 — issuance·media 등 `ck.actionCell` 을 이미 쓰는 다른 화면에도 그런
   기본값 규칙이 없다(이 화면만 갖는 특례를 없애 일관성을 맞춘다). */
.action-신규생성   { background: var(--pos-soft); color: var(--pos); }
.action-기존재사용 { background: var(--warn-soft); color: #92400e; }

/* `#manual-or-adv-badge`(캠페인의 Meta 예산관리방식 — 「수동 예산」/「Advantage+
   예산」 표시) — ⚠️ 위 media_publish 절의 `.badge`(「수동(이 실행만)」)와 낱말
   "수동"이 겹치지만 **같은 개념이 아니다**(Task 7 판단): 저건 "이번 실행에서
   계정을 손으로 오버라이드했다"는 실행 플래그, 이건 Meta 캠페인 자체가 이미
   가진 예산 속성이다. 그래서 색을 공유하지 않고 이 배지만 배경을 준다(모양은
   ④절 공용 pill 을 그대로 쓴다 — 이관 전엔 `border-radius:3px` 로 덜 둥글었지만
   그 모양 통일은 ④절이 다른 화면에도 이미 하고 있는 변화와 같은 종류라 그대로
   둔다). 이관 전 배경은 `#eef`(파랑빛 회색)였다 — 가장 가까운 토큰인
   `var(--info-soft)`(`#eff6ff`)를 쓴다. 글자색은 따로 정하지 않아 기본
   `--ink` 를 물려받는다(이관 전에도 색 지정이 없어 검정에 가까웠다) —
   media_publish 처럼 보라로 물들이지 않는다, 서로 다른 개념이 우연히 같은
   색으로 묶이면 다음에 헷갈린다. */
#manual-or-adv-badge { background: var(--info-soft); }

/* --- _sync_button.html (네비 안 동기화 시각 표시) --- */
/* 🔴 Task 6 — 220px 사이드바에서 한글이 낱글자로 쪼개지는 것을 막는다(실측: 「동기화
   기록 없음」이 「동기 / 화 기록 없음」으로 끊겼다 — `.globals` 블록에서 미러 3개가
   전부 미동기화일 때 재현). `.gchip .txt`(②절)가 이미 같은 220px 폭에서 같은 이유로
   갖고 있는 `word-break:keep-all; overflow-wrap:anywhere;` 쌍을 그대로 옮긴다 —
   `keep-all` 은 어절 안에서 안 끊고, `overflow-wrap:anywhere` 는 어절 하나가 폭보다
   길 때만(동기화 시각·상대시간이 길게 이어질 때) 끊게 해 사이드바 밖으로 삐져나가는
   것을 막는다. `/plans` 본문의 가로 동기화 바(㉲-3, ④절 `.sync-bar`)에서는 폭이
   넉넉해 이 규칙이 실질적으로 아무것도 끊지 않는다 — 두 문맥에 안전하게 같이 쓴다. */
.sync-meta { color: var(--muted); font-size: var(--fs-xs); margin-left: .6rem;
             word-break: keep-all; overflow-wrap: anywhere; }
.mirror-item { color: var(--muted); }
.mirror-item.stale { color: var(--neg); font-weight: 700; }  /* 낡음 경고 */

/* ── ㉲-2 「오늘」 화면 ─────────────────────────────────────────────
   `.kpis`·`.kpi`·`.k-value`·`.k-label` 은 **키트가 이미 준다**(cape-ui-components
   .css:54-59) — 다시 정의하지 않는다. 앱이 더하는 것은 「타일이 링크다」뿐. */
a.kpi { display:block; text-decoration:none; color:inherit; transition:border-color .15s; }
a.kpi:hover { border-color:var(--accent); }
a.kpi .k-value, a.kpi .k-label { display:block; }

/* 프로토타입은 「지금 해야 할 일」·「파이프라인 현황」·「최근 활동」 세 절을 각각
   흰 카드(테두리)로 감싼다 — `.card` 자체는 이미 키트에 있다(`cape-ui-components
   .css:63`, `status.html`·`notion_mirror.html` 이 이미 쓴다) — **재사용**한다
   (두 번째 카드 규칙을 만들지 않는다, §76 #4 와 같은 이유). `main.wrap h2` 는
   위(③절)에서 이미 `margin:28px 0 10px` 를 준다 — 카드 자체 패딩(`--sp-6`=24px)
   위에 그 28px 이 또 얹히면 카드 첫 줄이 프로토타입보다 훨씬 더 벌어진다. 카드의
   **첫 자식**일 때만 그 위 여백을 지운다(카드가 아닌 다른 곳의 h2 는 그대로). */
.card > h2:first-child { margin-top: 0; }

/*
   승인된 프로토타입 `docs/prototype/2026-08-04-console-redesign-proposal.html`
   의 `.todo`·`.pipe` 규칙을 그대로 옮긴 것이다. 값을 바꾸려면 프로토타입이
   아니라 여기가 원본이 된다(프로토타입은 사양이지 배포물이 아니다). */
.todo { display:flex; align-items:center; gap:12px; padding:13px 16px;
        border:1px solid var(--border); border-radius:var(--r-md);
        background:var(--surface); margin-bottom:8px; }
.todo .pri { flex:none; width:8px; height:8px; border-radius:var(--r-full); }
.todo .pri.p1 { background:var(--neg); }
.todo .pri.p2 { background:var(--warn); }
.todo .pri.p3 { background:var(--faint); }
.todo .grow { flex:1; }
.todo .why { color:var(--muted); font-size:var(--fs-xs); margin-top:2px; }

.pipe { display:flex; align-items:stretch; overflow-x:auto; padding:4px 0; }
.pipe .stage { flex:1; min-width:120px; background:var(--surface);
               border:1px solid var(--border); border-radius:var(--r-md);
               padding:12px 14px; text-decoration:none; color:inherit; }
.pipe .stage:hover { border-color:var(--accent); }
.pipe .stage .sv { display:block; font-size:var(--fs-2xl); font-weight:800;
                   color:var(--ink-strong); font-variant-numeric:tabular-nums; }
.pipe .stage .sl { display:block; font-size:var(--fs-xs); color:var(--muted); margin-top:2px; }
.pipe .stage.hot { border-color:var(--warn-border); background:var(--warn-soft); }
.pipe .stage.hot .sl { color:#92400e; font-weight:600; }
.pipe .arr { flex:none; display:flex; align-items:center; color:var(--faint);
             padding:0 6px; font-size:var(--fs-lg); }

/* ── `.two-cards`(㉲-4 가 만든 「소재 = 카드 둘」 격자)는 **지웠다**(조각 3
   Task 3, 2026-08-07). 유일한 사용처였던 `assets.html` 이 두 화면으로 갈리며
   사라졌다 — 죽은 셀렉터를 남기면 다음 사람이 "이건 어디서 쓰지" 를 매번
   되묻는다(㉱ Task 9 D11 의 잣대 그대로). 그때 얻은 교훈 자체는 유효하고 아래
   조각 3 절의 `.scroll`(가로 스크롤 상자)이 그 자리를 대신한다: **한글 열
   헤더가 든 표는 반쪽 너비에 안 들어간다** — 좁으면 줄바꿈이 아니라 스크롤. */

/* ══════════════ 조각 2(2026-08-07) — 막힘 상자 · 반복 문법 ② ══════════════
   목업 `2026-08-06-redesign-v3.html` 의 `.mk-blocked` 를 앱 이름으로 이식.
   `.mk-` 접두사는 **목업 전용**이라 앱에 들여오지 않는다(목업은 사양 문서이고,
   이 파일은 그 사양의 구현이다 — 이름까지 같으면 어느 쪽이 원본인지 흐려진다).

   ⚠️ 위쪽의 `.blocker` / `.blockers-box`(421·424행)와 **다른 것**이다:
     · `.blockers-box`  = `/batch2` 전용. **편성 계획 단위** blocker(예: "배치가 비어
                          있습니다")를 미리보기 응답이 JS 로 채운다.
     · `.blocked-box`   = 여기. **소재 단위** 막힘(노션에서 사람이 고쳐야 하는 것)을
                          서버가 렌더한다. 「오늘」·「기획」 두 화면이 공유.
   조각 4(편성·배포)가 앞의 것을 다시 그릴 때 둘을 합칠지 판단한다 — 지금 합치면
   성격이 다른 둘을 한 상자에 넣게 된다. */
.blocked-box { border: 1px solid var(--neg-border); background: var(--neg-soft);
               border-radius: var(--r-lg); padding: 12px 16px; margin: 0 0 var(--sp-4); }
.blocked-box h4 { margin: 0 0 6px; color: #991b1b; font-size: var(--fs-sm); }
.blocked-box ul { margin: 0; padding: 0; list-style: none; }
.blocked-box li { display: flex; gap: 10px; align-items: baseline; padding: 5px 0;
                  border-top: 1px dashed var(--neg-border); font-size: var(--fs-sm);
                  flex-wrap: wrap; }
.blocked-box li:first-child { border-top: 0; }
.blocked-box li .id { font-weight: 800; color: #991b1b; white-space: nowrap;
                      font-variant-numeric: tabular-nums; }
.blocked-box li .why { color: #7f1d1d; flex: 1; min-width: 180px; }
.blocked-box li .who { color: #b91c1c; font-size: var(--fs-xs); }
/* 엔진 원문(`detail`) — 사람 말(`why`)보다 약하게. `web/gate_status.py` 의
   text/detail 계약과 같은 위계다(짧은 사람 말이 먼저, 엔진 원문은 뒤·작게). */
.blocked-box li .detail { color: #b91c1c; font-size: var(--fs-xs); font-weight: 400; }
.blocked-box a { color: #991b1b; font-weight: 700; white-space: nowrap; }

/* ── 조각 2 — 「기획」 화면(목업 plans 뷰) ────────────────────────────────
   목업의 `.mk-form`·`.mk-prev`·`.mk-pg` 를 앱으로 이식. `.mk-` 접두사는 목업
   전용이지만 이 셋은 **목업이 정한 이름 그대로** 쓴다 — 사양 문서와 구현이 같은
   이름을 공유하면 눈 대조 때 「이게 그거다」를 바로 알 수 있고, 위 `.blocked-box`
   와 달리 앱에 같은 일을 하는 기존 이름이 없어 충돌 위험이 없다. */
.mk-form { display: grid; grid-template-columns: max-content 1fr; gap: 8px 14px;
           align-items: center; }
.mk-form label { font-weight: 700; font-size: var(--fs-sm); color: var(--ink); }
.mk-form select, .mk-form input { padding: 6px 10px; border: 1px solid var(--border-2);
           border-radius: var(--r-sm); font: inherit; max-width: 34rem; }
.mk-form input.narrow { max-width: 110px; }
/* 힌트는 라벨 칸을 비우고 값 칸에만 — grid 두 번째 열에 붙는다. */
.mk-form .hint { grid-column: 2; color: var(--muted); font-size: var(--fs-xs); margin-top: -4px; }
.mk-prev { margin-top: 14px; background: var(--surface-2); border: 1px solid var(--border);
           border-radius: var(--r-md); padding: 10px 14px; font-size: var(--fs-sm); }
.mk-prev .pl { font-weight: 700; margin-bottom: 4px; }
.mk-cap { color: var(--muted); font-size: var(--fs-xs); margin: 6px 0 0; }
.form-actions { margin-top: 12px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.mk-pg { display: flex; gap: 8px; justify-content: flex-end; margin-top: 10px; }
.mk-pg .btn[disabled] { opacity: .45; cursor: default; }

/* 목록 행 — 누르면 상속 속성 상세가 펼쳐진다(그래서 커서가 손가락이다). */
tr.plan { cursor: pointer; }
tr.plan.bad td { background: var(--neg-soft); }
tr.plan-detail > td { background: var(--surface-2); padding: 8px 12px; }
table.inherit { width: 100%; font-size: var(--fs-xs); }
table.inherit td { padding: 2px 8px; border: 0; }
/* 목록 가독성(눈 대조 2026-08-07) — 결과물 열이 길어 머리글이 세로로 쪼개졌다.
   뱃지·머리글은 줄바꿈하지 않고, 결과물 열만 좁게 감싼다. */
tr.plan td .badge, tr.plan th { white-space: nowrap; }
td.results { font-size: var(--fs-xs); max-width: 15rem; }
td.results .ok  { color: var(--pos); white-space: nowrap; }
td.results .err { color: var(--neg); white-space: nowrap; }

/* ══════════════ 조각 3(2026-08-07) — 「소재 보관」 화면(목업 store 뷰) ══════════
   목업 `2026-08-06-redesign-v3.html` 의 `.mk-sec`·`.mk-scroll`·`.mk-actionbar` 를
   **앱 이름**(`.list-sec`·`.scroll`·`.actionbar`)으로 이식한다. `.mk-` 접두사는
   목업 전용이라 그대로 들여오지 않는다(위 `.blocked-box` 와 같은 잣대 — 목업은
   사양 문서이고 이 파일은 그 구현이다. 이름까지 같으면 어느 쪽이 원본인지 흐려진다).
   ⚠️ 예외는 이미 이식된 `.mk-cap`·`.mk-form`·`.mk-prev`·`.mk-pg` 넷이다(조각 2 가
   「앱에 같은 일을 하는 기존 이름이 없다」는 이유로 목업 이름을 그대로 썼다) —
   `.mk-cap`(표 아래 작은 설명)은 이 화면도 그대로 쓴다. */

/* 리스트 머리 — 「① 올릴 수 있는 후보  ⑦  …한 번에 3소재까지」 한 줄. */
.list-sec { display: flex; align-items: center; gap: 9px; margin: 18px 0 8px;
            flex-wrap: wrap; }
.list-sec h3 { margin: 0; font-size: var(--fs-base); font-weight: 800;
               color: var(--ink-strong); }
.list-sec .cnt { font-size: var(--fs-xs); font-weight: 800; padding: 1px 9px;
                 border-radius: var(--r-full); background: var(--surface-2);
                 border: 1px solid var(--border); color: var(--muted); }
/* 「② 문제」의 건수만 빨강 — 0건이면 눈에 안 띄어야 하므로 색은 뱃지에만 준다
   (상자 전체를 빨갛게 하면 「늘 빨간 상자」가 되어 아무도 안 본다 — 막힘 상자와
   같은 규율, `web/blockers.py` 머리 주석). */
.list-sec.bad .cnt { background: var(--neg-soft); border-color: var(--neg-border);
                     color: var(--neg); }
.list-sec .rt { margin-left: auto; font-size: var(--fs-xs); color: var(--muted); }

/* 표를 감싸는 가로 스크롤 상자 — 한글 열 헤더가 든 표는 좁은 화면에서 글자 단위로
   줄바꿈된다(㉲-4 실측: `200115_003` → `20`/`011`/`5_0`/`03`). 줄바꿈 대신 스크롤. */
.scroll { overflow-x: auto; border: 1px solid var(--border);
          border-radius: var(--r-md); }
.scroll > table { margin: 0; }

/* 하단 액션바 — 목록이 길어도 [미리보기]·[승인 · 적용]이 늘 손에 닿아야 한다. */
.actionbar { position: sticky; bottom: 0; display: flex; align-items: center;
             gap: 10px; flex-wrap: wrap; background: var(--surface);
             border: 1px solid var(--border); border-radius: var(--r-md);
             padding: 10px 14px; margin-top: 10px; }
.actionbar .g { flex: 1; font-size: var(--fs-sm); }

/* 목록 공통 칸 표기 — `_plans_mirror.html`(조각 2)이 이미 쓰는 `.id`/`.dim` 과
   같은 뜻이다. 그쪽은 규칙 없이 이름만 달고 있었다(그래서 ID 열이 다른 열과
   똑같이 보였다) — 여기서 한 벌만 정의해 두 화면이 같이 쓴다. */
td.id { font-variant-numeric: tabular-nums; font-weight: 700;
        color: var(--ink-strong); white-space: nowrap; }
td.dim { color: var(--muted); }
/* ② 문제 표의 줄 — 목업(`<tr class="bad">`)대로 옅은 빨강. 표 id 로 스코프를 좁힌다:
   `tr.bad` 를 전역으로 두면 다른 화면(예: `_plans_mirror.html` 의 `tr.plan.bad`)의
   같은 이름과 특이도 싸움이 난다. ① 후보 표에는 색이 없다 — 정상이 기본이다. */
#problem-table tr.bad td { background: var(--neg-soft); }

/* ══════════════ 조각 3 Task 4(2026-08-07) — 「미디어 등록」 화면(목업 media 뷰) ══════
   목업의 `.mk-acct`(계정 상자) 를 **표 안의 머리띠 줄**로 옮긴 것이다 — 목업은 계정마다
   `<table>` 을 따로 두는데, 그러면 표 개수가 데이터에 달려 정적 `<thead>` 를 Jinja 에
   둘 수 없다(빈 상태에서 화면이 무엇인지 사람도 게이트도 못 본다, `media.html` 머리
   주석). `<thead>` 한 벌 + 계정마다 `<tbody>` 로 같은 그림을 만든다. */
#rows-table tr.ah td { background: var(--surface-2); border-top: 1px solid var(--border); }
#rows-table tr.ah .band { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
#rows-table tr.ah .band .nm { font-weight: 800; color: var(--ink-strong);
                              font-variant-numeric: tabular-nums; }
#rows-table tr.ah .band button { margin-left: auto; }
/* 계정 드롭다운 — 표 칸 안에서 글꼴·높이가 본문과 어긋나지 않게(목업 `.mk-tb select`). */
#rows-table td select, #products-table td select {
  font: inherit; padding: 4px 8px; border: 1px solid var(--border-2);
  border-radius: var(--r-sm); max-width: 22rem; }
/* 「상품 파생과 다름」 — 주황. 배지 기본형(`.badge`)에 색만 얹는다(색 어휘를 늘리지
   않는다: 경고색은 이미 `--warn-soft` 하나다). */
.badge-off { background: var(--warn-soft); color: #92400e; }
/* 「상품 파생과 다름」 경고 — **계정 묶음마다 그 계정 몫만**, 실행 버튼 바로 위에 붙는다
   [재윤 확정 2026-08-07]. 표 전체 위 한 줄이면 A 계정의 경고를 B 계정 버튼 누르는
   사람이 읽는다. 없으면 요소 자체를 안 만들므로 `:empty` 규칙이 필요 없다. */
#rows-table tr.ah .derive-warn { color: var(--warn); font-size: var(--fs-sm);
                                 font-weight: 700; margin-bottom: 6px; }

/* ══════════════ 조각 4 Task 3(2026-08-08) — 「편성 · 배포」 4단계(목업 deploy 뷰) ══
   목업 771-781 을 그대로 이식한다. `.mk-` 접두사를 유지하는 이유는 `.mk-form`·
   `.mk-prev`·`.mk-cap`·`.mk-pg`(조각 2)와 같다 — 사양 문서(목업)와 구현이 같은
   이름을 쓰면 눈 대조 때 「이게 그거다」를 바로 알 수 있고, 앱에 같은 일을 하는
   기존 이름이 없어 충돌 위험이 없다.
   🔴 여닫이는 **`on` 클래스 하나뿐**이다(`hidden` 속성과 섞지 말 것) — 아래
      `[hidden]{display:none !important}`(732행, T3 리뷰 M3) 때문에 지금은 `hidden` 이
      `!important` 로 순서·특이도와 무관하게 이겨, 섞으면 `.mk-sb.on{display:block}` 이
      무력화돼 「그 단계가 영영 안 열리는 화면」이 된다(리뷰 M2 — 실패 모양 정정,
      규칙은 그대로)(batch2.html 의 `showStep()` 주석 참고). */
.mk-steps { display: flex; gap: 0; margin-bottom: var(--sp-4); background: var(--surface);
            border: 1px solid var(--border); border-radius: var(--r-lg);
            overflow: hidden; box-shadow: var(--shadow-sm); }
.mk-steps .s { flex: 1; padding: 11px 14px; border-right: 1px solid var(--border);
               cursor: pointer; }
.mk-steps .s:last-child { border-right: 0; }
.mk-steps .s .sn { font-size: 11px; font-weight: 800; color: var(--faint); }
.mk-steps .s .st { font-size: var(--fs-base); font-weight: 800;
                   color: var(--ink-strong); margin-top: 1px; }
.mk-steps .s .sd { font-size: 11px; color: var(--muted); margin-top: 2px; }
.mk-steps .s.on { background: var(--accent-soft); }
.mk-steps .s.on .sn, .mk-steps .s.on .st { color: var(--accent-ink); }
.mk-steps .s.done .sn::after { content: " ✓"; color: var(--pos); }
.mk-sb { display: none; }
.mk-sb.on { display: block; }

/* ══════════════ `hidden` 은 «언제나» 이긴다 — 구조적 차단(조각 4 T3 리뷰 M3) ═══════
   🔴 **왜 있나.** 이 저장소는 요소를 숨길 때 거의 전부 `hidden` 속성을 쓴다(`el.hidden
      = true`). 그런데 브라우저 기본값 `[hidden]{display:none}` 은 **UA 스타일**이라,
      우리가 클래스에 `display:` 를 걸어 두면 **작성자 스타일이 이겨 `hidden` 이 무시된다.**
      이 저장소는 이 함정을 이미 **세 번** 밟았다:
        · 전사 키트 — `.note-line[hidden]{display:none;}`
          (cape-ui-components.css:86 「[hidden]가 display:flex를 이기지 못하던 버그 교정」)
        · 조각 4 T3 — `.banner-neg{display:flex}` 때문에 배포 게이트 배너가 `hidden` 인데도
          **항상 떠 있었다**(게이트가 열린 날에도 「배포가 잠겨 있습니다」라고 거짓말).
      한 태스크 안에서 같은 함정을 두 번 밟았으므로 **규율(다음 사람이 확인한다)이 아니라
      구조로** 막는다(ROADMAP §9 A32·A18·A11 「구조 > 규율」).
   🔴 **이미 깔린 다음 지뢰들:** `.mk-steps{display:flex}` · `.list-controls{display:flex}` ·
      `.cards`·`.todo`·`.pipe`·`.sync-bar > div`·`.mk-form`·`.mk-pg`… 누구든 이 요소에
      `hidden` 을 다는 날 똑같이 터졌을 것이다. 이 한 줄이 그 전부를 미리 닫는다.
   ⚠️ `!important` 를 쓰는 이유: 이기려는 상대가 **더 구체적인 선택자**(`.a .b`)일 수
      있어 특이도로는 못 이긴다. 실측으로 안전을 확인했다 — 이 저장소에 `style.display`
      를 직접 만지는 코드가 **0건**이고, 화면 7장 전수 스캔에서 「`hidden` 인데 보이는」
      요소도 0건이다(그 스캔은 아래 게이트로 승격했다).
   무는 게이트: tests/browser/test_screens_load.py::test_hidden_really_hides_on_every_screen */
[hidden] { display: none !important; }

/* ══════════════ 조각 4 Task 4(2026-08-08) — 1단계 「무엇을」(목업 1274-1354) ══════
   목업 788-792 의 `.mk-inline`/`.fi`(라벨 위·입력 아래로 가로 나열하는 한 줄 폼)를
   그대로 이식한다. `.mk-` 접두사를 유지하는 이유는 T3 의 `.mk-steps`·`.mk-sb` 와 같다
   — 사양 문서(목업)와 구현이 같은 이름을 쓰면 눈 대조가 쉽고, 앱에 같은 일을 하는
   기존 이름이 없다(`.mk-form` 은 라벨-입력 **2열 그리드**라 다른 물건이다).
   ⚠️ 인라인 `style=` 은 스타일 게이트(G2)가 막는다 — 목업의 `style="…"` 은 전부
      여기로 온다. */
.mk-inline { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end;
             margin-bottom: 6px; }
.mk-inline .fi { display: flex; flex-direction: column; gap: 4px; }
.mk-inline .fi label { font-size: var(--fs-xs); font-weight: 700; color: var(--muted); }
.mk-inline .fi input, .mk-inline .fi select {
  font: inherit; font-size: var(--fs-sm); padding: 7px 10px;
  border: 1px solid var(--border-2); border-radius: var(--r-md);
  background: var(--surface); }
/* 광고계정은 「표시명(ID) — 편성 가능 N건」이라 길다(목업 1280 도 `min-width:320px`). */
#source-account { min-width: 20rem; }
/* 후보 표의 체크박스 칸 — 목업 1304 의 `style="width:28px"`. */
#candidates th.pick { width: 28px; }

/* ══════════════ 조각 4 Task 6b(2026-08-08) — 3단계 「검토」 세트 카드(목업 1392-1423) ══
   목업 800-805 의 `.mk-set`/`.sh` 를 그대로 이식한다(`.mk-` 접두 유지 이유는 위
   `.mk-steps`·`.mk-inline` 과 같다 — 사양 문서와 구현이 같은 이름을 쓰면 눈 대조가 쉽다).
   ⚠️ 인라인 `style=` 은 스타일 게이트(G2)가 막는다 — 목업의 `style="border:0"` 은
      아래 `.mk-set .scroll` 로 온다. */
.mk-set { border: 1px solid var(--border-2); border-radius: var(--r-md);
          margin-bottom: 10px; overflow: hidden; }
.mk-set .sh { display: flex; align-items: center; gap: 9px; padding: 9px 13px;
              background: var(--surface-2); border-bottom: 1px solid var(--border);
              flex-wrap: wrap; }
.mk-set .sh .pk { font-size: var(--fs-xs); font-weight: 700; color: var(--muted);
                  white-space: nowrap; }
.mk-set .sh input[type=text] { font: inherit; font-size: var(--fs-sm);
                               padding: 5px 9px; border: 1px solid var(--border-2);
                               border-radius: var(--r-sm); flex: 1; min-width: 15rem;
                               font-variant-numeric: tabular-nums; }
/* 카드가 이미 테두리를 가졌다 — 안쪽 표까지 두르면 선이 겹친다(목업 1396 도 그래서
   `border:0` 이다). 가로 스크롤 기능은 그대로 둔다. */
.mk-set .scroll { border: 0; border-radius: 0; }
/* 소재별 문제 줄 — 그 소재 행 **바로 아래**에 붙는다(옛 `#plan` 표에서는 멤버 칸
   안이었다. 정보는 그대로, 자리만 바뀌었다). */
.mk-set tr.issue td { padding-top: 0; }
.mk-set tr.issue .blocker { margin: 0; }

/* ══════════════ 조각 5 Task 4(2026-08-10) — 「성과 조치」 화면(목업 perf 뷰) ══════════
   목업 1601-1689 를 이식한다. 표(`.mk-scroll`→`.scroll`)·목록 제어(`.list-controls`)·
   세트 행(`.set`/`.ads`)·액션바(`.actionbar`)는 조각 3·하차가 이미 이식해 둔 이름을
   그대로 재사용한다(재도출 금지) — 새로 필요한 건 아래 셋뿐이다.

   `.mk-cap`·`.mk-form` 은 **새로 만들지 않는다** — 조각 2 가 이미 같은 이름으로
   이식해 뒀다(위 596~610행 「예외는 이미 이식된 `.mk-cap`·`.mk-form`·`.mk-prev`·
   `.mk-pg` 넷이다」참고). `web/templates/perf.html` 은 그 두 클래스를 그대로 쓴다. */

/* 액션 패널(위너로 승격 · 다른 세트로 이동) — 액션바 버튼을 눌러야 열린다.
   🔴 **`display` 규칙을 일부러 안 둔다.** `.panel{display:none}` +
   `.panel:not([hidden]){display:block}` 처럼 클래스로 여닫으면, 이 파일 위쪽의
   전역 차단(`[hidden]{display:none!important}`, 조각 4 T3 리뷰 M3)과 상관없이
   **`hidden` 속성 하나만으로 이미 숨겨진다**(브라우저 기본 UA 스타일). 클래스에
   `display:`를 얹는 순간 그게 그 함정(`.mk-sb`·`.banner-neg` 가 이미 두 번 밟은
   것과 같은 모양)을 다시 파는 길이라 이 규칙엔 `display` 를 아예 적지 않는다.
   무는 게이트: tests/browser/test_screens_load.py::test_hidden_really_hides_on_every_screen */
.panel { margin-top: 16px; }

/* 액션 패널 안 카드의 색 구분 — 목업(1641·1671행)은 인라인 `style=` 으로 배경색을
   줬다(하드코딩 스케치). 스타일 게이트(G2)는 인라인 `style=` 을 막으므로 여기 이름
   붙은 수정자로 옮긴다: accent = 위너 승격(강조, `--accent-soft`) · muted = 다른
   세트로 이동(차분, `--surface-2`). */
.card.accent { background: var(--accent-soft); border-color: var(--info-border); }
.card.muted { background: var(--surface-2); }

/* ── 조각 5 Task 5(2026-08-10) — 위너 승격 패널 ───────────────────────────
   목업(1647-1656행)의 인라인 `color:var(--neg)`·`margin:` 을 G2(인라인 style 금지)에
   맞춰 클래스로 옮긴다. */
.req { color: var(--neg); }             /* 「사유 *」의 필수 표시 */
/* 「새로 만들기」 3칸(`#winner-new`)을 감싸는 안쪽 상자 — `.mk-form` 은 2열 그리드라
   그냥 `<div>` 로 감싸면 자식(label·select·input)이 그리드 칸 하나에 뭉친다.
   `display: contents` 는 이 래퍼 자체를 박스 트리에서 지워, 자식이 부모 그리드
   칸에 바로 이어서 배치되게 한다. `hidden` 이 걸리면 전역 차단(741행,
   `[hidden]{display:none!important}`)이 이 규칙을 무조건 이기므로 규약 ⑤를
   어기지 않는다(위 796-803행과 같은 이유). */
.subform { display: contents; }
/* 「이 승격이 나갈 곳」(`#winner-where`)은 `.mk-form` 안에 낀 **한 줄짜리** 안내문이라
   라벨·값 2열 자리를 하나씩 차지하면 뒤따르는 「사유」 라벨이 값 칸으로 밀린다 —
   grid-column 을 통째로 줘 자기 혼자 한 행을 쓰게 한다. */
.mk-form .mk-cap { grid-column: 1 / -1; }
