/* 스토리 개발 Beta 전용 스타일.

   기존 `styles.css` 의 색·폰트 토큰을 재사용한다. 값을 다시 정하지 않는 이유:
   두 화면이 같은 제품으로 보여야 하고, 브랜드 색이 바뀔 때 한 곳만 고쳐야 한다.

   토큰이 없는 경우를 대비해 var() 의 두 번째 인자로 대체값을 둔다 — styles.css 가
   먼저 로드되지 않는 상황(캐시 실패)에서도 화면이 읽힌다.
*/

:root {
  --sd-ink: var(--ink, #2c2c33);
  --sd-dim: var(--muted, #6f6f78);
  --sd-line: var(--line, #d8d8de);
  --sd-accent: var(--accent, #5234c1);
  --sd-surface: var(--surface, #ffffff);
  --sd-bg: var(--bg, #f6f6f8);
  --sd-radius: 10px;
  --sd-gap: 16px;
  /* 아래 일곱은 규칙들이 정의 없이 대체값으로만 쓰던 이름이다(uiux-261029-c 4부, 2026-10-30). 밝은 값은 그 대체값 그대로라
     밝은 테마 모양은 바뀌지 않는다. 어두운 값은 아래 088 두 블록(바탕 판 --sd-panel 은 styles.css --ground 가 테마를 따른다). */
  --sd-panel: var(--ground, #ffffff);
  --sd-muted: #5b6472;
  --sd-border: #dde2ea;
  --sd-surface-muted: #f7f8fa;
  --sd-accent-border: #b7c8e8;
  --sd-accent-soft: #eef3fc;
  --sd-accent-text: #2b4b86;
  /* 위험 · 경고 색(uiux-261029-e 090 · 091). 규칙들이 색 리터럴(#c53030 · #b7791f · #fff5f5 · rgba(183, 121, 31, …))로 쓰던 자리를
     이 다섯 토큰이 대신한다 — 어두운 테마 값을 한 곳(아래 090 두 블록)에서 바꾸려고. 리터럴은 이 정의 안에만 남는다.
     - --sd-danger: 위험 글자 · 테두리 · 게이지 채움 · ① 오류 줄 · AI 칸 오류. 밝은 값은 리터럴 #c53030 그대로라 그 16곳은 모양이
       같다(090 의 #c0392b 를 쓰던 AI 칸 오류 글자만 4.99 → 5.01:1 로 아주 조금 진해진다). ① 줄은 앱 --danger(#d13a45) 대신 이것 —
       금색 연배경 위 4.12 → 4.72:1(설계자 결정: 밝은 테마 글자를 조금 진하게 해도 된다).
     - --sd-danger-soft: 위험 버튼 · 메뉴 항목 hover 바탕(밝은 값 그대로).
     - --sd-danger-fill: 흰 글자를 얹는 바탕(오류 토스트). 어두운 --sd-danger(밝은 빨강) 위 흰 글자는 2.7:1 이라 따로 두고 두 테마 같은 값.
     - --sd-warn: 경고 글자 · 테두리. 지금 #b7791f 는 밝은 바탕 위 3.3:1 이었다 — 같은 색상(35.5°) · 채도(71%)에서 명도만 42 → 33%
       (밝은 바탕 넷 5.0~5.5 · 금색 연배경 4.7:1). 앱 --pill-warn-ink(#855600)는 색상 · 채도가 달라 밝은 모양이 바뀌어 쓰지 않았다.
     - --sd-warn-soft: 경고 상자 바탕(밝은 값 그대로). */
  --sd-danger: #c53030;
  --sd-danger-soft: #fff5f5;
  --sd-danger-fill: #c53030;
  --sd-warn: #905f18;
  --sd-warn-soft: rgba(183, 121, 31, 0.06);
  /* 성공 색 · 상태 배지 바탕(uiux-261029-e 091b). 2차 회차 · 대본 화면과 옛 화면이 리터럴(#2e8b57 · #2f855a · #c0392b · #e08b2f ·
     #b5721a · 배지 바탕 셋)로 쓰던 자리를 위 위험 · 경고 토큰과 이 넷이 대신한다 — 어두운 값을 아래 090 두 블록 한 곳에서 바꾸려고.
     - --sd-success: 확정 · 맞음 글자 · 테두리 · 점 · 게이지 채움. 옛 #2f855a(밝은 바탕 위 4.2:1) · #2e8b57(3.9:1)을 하나로 —
       #2f855a 와 같은 색상(150°) · 채도(48%)에서 명도만 35 → 31%(5.1:1). 앱 --p1-ink(#1f6b3d)는 색상 · 채도가 달라 밝은 모양이
       바뀌어 쓰지 않았다(091 --sd-warn 과 같은 이유).
     - --sd-*-tint: 대본 상태 배지 바탕(밝은 값 = 지금 리터럴 그대로). 위 -soft(hover · 상자의 아주 옅은 바탕)보다 한 단계 진해
       -soft 로 옮기면 밝은 테마 배지가 거의 흰 알약이 된다. */
  --sd-success: #29754f;
  --sd-success-tint: #e7f4ec;
  --sd-danger-tint: #fbeae8;
  --sd-warn-tint: #fdf1e3;
}

/* 어두운 테마의 바탕 · hover 토큰(uiux-261029-c 3부, 2026-10-30 — 리뷰어 dev 점검).

   왜: `--bg` 는 styles.css 어디에도 없어 위 `--sd-bg` 는 **두 테마 모두** 대체값 #f6f6f8(밝은 회색)이었다. 어두운 테마에서
   글자는 흰색이라, 이 토큰을 쓰는 곳(문단 · ④ 4칸 · 버튼 · 속도 바 · 칩 hover, AI 비서 입력칸 알약, 질문 보기, 진행 띠 · 상자
   바탕 등 47곳)에서 흰 글자가 밝은 바탕 위에 놓여 읽히지 않았다(ed1343d 부터). 규칙 47개를 하나씩 고치지 않고 토큰 한 곳을 덮는다.

   값: styles.css 표면 단계 `--raised`(어두움 #1d1d1d — 카드 `--surface` #111 의 한 단계 위라 hover 가 보인다). 대비는 흰 글자
   16.9:1 · 흐린 글자(#b4b4b4) 8.1:1. `--nested`(#2a2a2a)는 금색 연배경(칩 hover · 고른 칸 #2a2117)과 거의 같아 쓰지 않았다.
   대체값은 밝은 테마와 같은 사슬이다 — `--raised` 가 없으면 styles.css 를 못 읽은 것이고, 그때는 글자도 밝은 테마 대체값이다.
   밝은 테마 값은 그대로다(덮어쓰기는 어두운 두 블록만 — OS 설정 · 검토용 data-theme). 구조 맵 창은 아래 자기 규칙
   (`.sd-map-page { --sd-bg: var(--ground) }`)이 더 가까워 그대로다. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --sd-bg: var(--raised, var(--bg, #f6f6f8)); }
}
:root[data-theme="dark"] { --sd-bg: var(--raised, var(--bg, #f6f6f8)); }

/* 어두운 테마의 남은 색 토큰(uiux-261029-c 4부, 2026-10-30 — 리뷰어 dev ci-78232cd8 점검).

   왜: 위 :root 의 --sd-muted · --sd-border · --sd-surface-muted · --sd-accent-* 는 규칙들이 정의 없이 밝은 대체값으로만 쓰던
   이름이라 어두운 테마에서도 밝은 색이었다. 확정 기록 표식(기획방향 카드 배지 · 문서 '기억 반영' 띠)은 밝은 파랑 바탕에 어두운 파랑
   글자라 어두운 화면에서 튀었고, 기록 관리 패널 · 대본 미리보기는 #fff 바탕에 흰 글자라 읽히지 않았다. --sd-muted 만 어두운
   글자로 바꾸면 정지 기록(바탕 --sd-surface-muted)이 밝은 바탕에 밝은 글자가 되므로 같은 묶음을 함께 정한다.

   값: 밝은 값에 맞는 styles.css 토큰의 어두운 값 — 흐린 글자 --muted · 테두리 --line · 한 단계 위 표면 --raised(087 --sd-bg 와
   같다) · 파랑 알약 --p2-bg / --p2-ink(밝은 테마 표식과 같은 파랑 계열, 글자 대비 7.3:1). 테두리는 밝은 테마처럼 바탕과 글자 사이의
   옅은 색(color-mix — 1부 AI 칸 바탕과 같은 지원 기준, 모르는 브라우저는 테두리만 빠진다). 대체값은 밝은 값이다(087 과 같은 이유 —
   styles.css 를 못 읽었으면 글자도 밝은 테마 대체값이다). 087 블록(--sd-bg 하나)과 나눠 둔 이유: 묶음이 달라 하나를 되돌릴 때 다른
   것을 건드리지 않는다. 바탕 판 --sd-panel 은 styles.css --ground 가 두 테마 값을 가져 여기에 다시 쓰지 않는다. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --sd-muted: var(--muted, #5b6472);
    --sd-border: var(--line, #dde2ea);
    --sd-surface-muted: var(--raised, #f7f8fa);
    --sd-accent-soft: var(--p2-bg, #eef3fc);
    --sd-accent-text: var(--p2-ink, #2b4b86);
    --sd-accent-border: color-mix(in srgb, var(--p2-ink, #2b4b86) 40%, var(--p2-bg, #eef3fc));
  }
}
:root[data-theme="dark"] {
  --sd-muted: var(--muted, #5b6472);
  --sd-border: var(--line, #dde2ea);
  --sd-surface-muted: var(--raised, #f7f8fa);
  --sd-accent-soft: var(--p2-bg, #eef3fc);
  --sd-accent-text: var(--p2-ink, #2b4b86);
  --sd-accent-border: color-mix(in srgb, var(--p2-ink, #2b4b86) 40%, var(--p2-bg, #eef3fc));
}

/* 어두운 테마의 오류 · 경고 색(uiux-261029-e, 2026-10-30 — 090 남은 이슈 2 · 091 색 리터럴).

   왜: --sd-danger 는 정의 없이 대체값 #c0392b 로만 쓰여, 어두운 말풍선 바탕(#111) 위 약 3.5:1 이었다(AI 비서 칸 오류 문장). 091 에서
   규칙의 색 리터럴(#c53030 17줄 · #b7791f 12줄 · hover #fff5f5 · 경고 연배경 — 대부분 전문가 보기 · 옛 화면)도 위 :root 토큰으로
   옮겼다. #c53030 은 어두운 #111 위 3.45:1 이었고 hover 의 #fff5f5 는 어두운 화면에서 흰 바탕으로 번쩍였다.
   값: styles.css 알약 토큰의 어두운 값 — 위험 글자 --pill-neg-ink(#f0707f — #111 위 6.6 · 판 #000 위 7.3 · 한 단계 위 표면 #1d1d1d 위
   5.9 · 금색 연배경 #2a2117 위 5.5) · 위험 연배경 --pill-neg-bg(#2c1a20 — 그 위 위험 글자 5.7) · 경고 글자 --pill-warn-ink(#e5a93b —
   8.1 이상) · 경고 연배경 --pill-warn-bg(#2b2414). 앱 --danger(#e5484d)는 --sd-bg 위 4.3 · 금색 연배경 위 4.0 이라 4.5 를 밑돌아 쓰지
   않는다(--danger 자체는 앱 전체 토큰이라 바꾸지 않는다). --sd-danger-fill(흰 글자 바탕)은 덮지 않는다 — 두 테마 같은 빨강 위 흰 글자.
   대체값은 밝은 값이다(087 O1 — styles.css 를 못 읽었으면 바탕도 밝은 테마 대체값이다). 087 · 088 블록과 나눈 이유: 묶음마다 따로
   되돌리고, 그 검사가 블록 모양을 그대로 읽는다. 091 은 같은 묶음(오류 · 경고 글자)이라 이 블록을 넓혔다.
   091b: 성공 색 · 상태 배지 바탕 넷도 같은 묶음(상태 색)이라 여기에 더했다 — 성공 글자 --p1-ink(#8fd1a8 — #111 위 10.7 · #1d1d1d 위
   9.5 · 성공 배지 바탕 위 8.4) · 성공 배지 바탕 --p1-bg(#1d2a22) · 위험 · 경고 배지 바탕은 위 -soft 와 같은 알약 바탕(#2c1a20 · #2b2414
   — 그 위 위험 글자 5.7 · 경고 글자 7.4). 예전 리터럴 배지 바탕(#fbeae8 · #fdf1e3 · #e7f4ec)은 어두운 화면에서 밝은 알약으로 튀었다. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --sd-danger: var(--pill-neg-ink, #c53030);
    --sd-danger-soft: var(--pill-neg-bg, #fff5f5);
    --sd-warn: var(--pill-warn-ink, #905f18);
    --sd-warn-soft: var(--pill-warn-bg, rgba(183, 121, 31, 0.06));
    --sd-success: var(--p1-ink, #29754f);
    --sd-success-tint: var(--p1-bg, #e7f4ec);
    --sd-danger-tint: var(--pill-neg-bg, #fbeae8);
    --sd-warn-tint: var(--pill-warn-bg, #fdf1e3);
  }
}
:root[data-theme="dark"] {
  --sd-danger: var(--pill-neg-ink, #c53030);
  --sd-danger-soft: var(--pill-neg-bg, #fff5f5);
  --sd-warn: var(--pill-warn-ink, #905f18);
  --sd-warn-soft: var(--pill-warn-bg, rgba(183, 121, 31, 0.06));
  --sd-success: var(--p1-ink, #29754f);
  --sd-success-tint: var(--p1-bg, #e7f4ec);
  --sd-danger-tint: var(--pill-neg-bg, #fbeae8);
  --sd-warn-tint: var(--pill-warn-bg, #fdf1e3);
}

.sd-body {
  margin: 0;
  background: var(--sd-bg);
  color: var(--sd-ink);
  font-family: "Pretendard Variable", Pretendard, -apple-system, system-ui, sans-serif;
  line-height: 1.6;
}

/* --------------------------------------------------------------------------
   D3 화려함(계획서 2026-10-26, 지시서 057) — 테마 전환 0.24s 크로스페이드

   스토리 개발 화면은 OS 설정(prefers-color-scheme)이나 검토용 [data-theme] 로 밝음↔어두움이
   바뀐다(별도 인앱 토글은 없다). 바뀌는 순간 색이 **툭** 끊기지 않고 0.24s 로 부드럽게 넘어가게,
   테마로 값이 바뀌는 속성(배경·글자·테두리)에만 전이를 둔다. transform·opacity 가 아니라 색
   전이라 '화면 튐'은 없다(위치가 안 바뀐다). 전이는 **값이 바뀔 때만** 재생되므로 첫 로드에는
   움직이지 않는다. reduced-motion 이면 아래에서 끈다(즉시 전환).

   범위를 테마 토큰을 쓰는 큰 면(본문 배경·카드·머리·진행 막대·도크)으로 좁혀, 작은 요소 수백 개가
   한꺼번에 전이하며 버벅이지 않게 한다. */
.sd-body,
.sd-page-project.is-v2 .cv2-head,
.sd-page-project.is-v2 .cv2-progress,
.sd-body .cv2-body-card,
.sd-body .cv2-card,
.sd-body .cv2-dock,
.sd-body .cv2-complete-card,
.sd-body .cv2-drawer {
  transition: background-color .24s ease, color .24s ease, border-color .24s ease;
}
@media (prefers-reduced-motion: reduce) {
  .sd-body,
  .sd-page-project.is-v2 .cv2-head,
  .sd-page-project.is-v2 .cv2-progress,
  .sd-body .cv2-body-card,
  .sd-body .cv2-card,
  .sd-body .cv2-dock,
  .sd-body .cv2-complete-card,
  .sd-body .cv2-drawer {
    transition: none;
  }
}

.sd-skip {
  position: absolute;
  left: -9999px;
}
.sd-skip:focus {
  left: 12px;
  top: 12px;
  z-index: 10;
  padding: 8px 12px;
  background: var(--sd-surface);
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
}

.sd-root { min-height: 100vh; }

/* 메인 화면 안에 붙는 자리 (index.html 의 #storyDevHost).

   `.sd-root` 와 달리 `min-height: 100vh` 를 주지 않는다 — 이 자리는 이미 상단바
   아래에 있어서 100vh 를 주면 화면 밖으로 넘친다.

   안쪽 `.sd-page` 의 여백과 최대폭을 지우는 이유: 그 값들은 **별 페이지**를 위한
   것이다. 메인 셸의 `.workspace` 가 이미 여백과 폭을 정하므로, 그대로 두면 여백이
   두 번 들어가고 다른 화면(프로젝트 목록 · 영상 라이브러리)과 좌우 정렬이 어긋난다. */
.story-dev-host { min-height: 50vh; }
.story-dev-host .sd-page { max-width: none; margin: 0; padding: 0; }
.sd-page { max-width: 1280px; margin: 0 auto; padding: 24px; }

/* --- 머리 --- */
.sd-page-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}
.sd-page-title { font-size: 22px; margin: 0; }
/* 머리 오른쪽 끝으로 밀어 두는 항목("기존 화면으로" 링크). flex-wrap 이 켜져 있어
   좁은 화면에서는 다음 줄로 떨어지고, 그때 margin-left:auto 는 무해하다. */
.sd-page-head-right { margin-left: auto; }
.sd-beta-tag {
  font-size: 12px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--sd-accent);
  color: #fff;
}

/* --- 진입 차단 화면 --- */
.sd-gate {
  max-width: 560px;
  margin: 15vh auto;
  padding: 32px;
  background: var(--sd-surface);
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  text-align: center;
}
.sd-gate-title { font-size: 20px; margin: 0 0 8px; }
.sd-gate-detail { color: var(--sd-dim); margin: 0 0 20px; }

/* --- 2단 레이아웃 --- */
.sd-layout {
  display: grid;
  grid-template-columns: 260px minmax(0, 1fr);
  gap: var(--sd-gap);
  align-items: start;
}
@media (max-width: 900px) {
  .sd-layout { grid-template-columns: 1fr; }
}

.sd-panel {
  background: var(--sd-surface);
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  padding: 16px;
  position: sticky;
  top: 16px;
}
.sd-panel-title { font-size: 13px; color: var(--sd-dim); margin: 0 0 12px; font-weight: 600; }

.sd-stage {
  background: var(--sd-surface);
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  padding: 24px;
  min-width: 0; /* grid 안에서 긴 텍스트가 칸을 밀지 않게 한다 */
}
.sd-stage-head { margin-bottom: 24px; }
.sd-stage-title { font-size: 20px; margin: 0 0 6px; }
.sd-stage-hint { color: var(--sd-dim); margin: 0; }
.sd-stage-meta { color: var(--sd-dim); font-size: 13px; margin: 8px 0 0; }

/* --- 스테퍼 --- */
.sd-steps { display: flex; flex-direction: column; gap: 4px; }
.sd-step {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  padding: 10px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: none;
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
}
.sd-step:hover { background: var(--sd-bg); }
.sd-step.is-active { border-color: var(--sd-accent); background: var(--sd-bg); }
.sd-step.is-locked { opacity: 0.5; }
.sd-step-no {
  flex: 0 0 22px;
  height: 22px;
  border-radius: 999px;
  background: var(--sd-line);
  color: var(--sd-ink);
  font-size: 12px;
  display: grid;
  place-items: center;
}
/* 배경 --sd-accent 는 앱 --accent(=반전색)라 어두운 테마에서 흰색이다 — 글자는 짝인 --invert-ink 로 둬야 두 테마 모두 보인다. */
.sd-step.is-active .sd-step-no { background: var(--sd-accent); color: var(--invert-ink, #fff); }
.sd-step-body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.sd-step-label { font-size: 14px; }
.sd-step-meta { display: flex; gap: 6px; flex-wrap: wrap; }
.sd-step-count { font-size: 11px; color: var(--sd-dim); }

/* --- 배지 --- */
.sd-badge {
  display: inline-block;
  font-size: 11px;
  padding: 1px 7px;
  border-radius: 999px;
  border: 1px solid var(--sd-line);
  color: var(--sd-dim);
  white-space: nowrap;
}
.sd-badge-confirmed { border-color: var(--sd-success); color: var(--sd-success); }
.sd-badge-stale { border-color: var(--sd-warn); color: var(--sd-warn); }
.sd-badge-needs_review { border-color: var(--sd-danger); color: var(--sd-danger); }
.sd-badge-draft { border-color: var(--sd-line); color: var(--sd-dim); }

/* --- 섹션 · 입력 --- */
.sd-section { margin-bottom: 28px; }
.sd-section-title { font-size: 15px; margin: 0 0 12px; }
.sd-row { display: flex; gap: 12px; flex-wrap: wrap; }
.sd-row > .sd-field { flex: 1 1 180px; }

.sd-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.sd-field-label { font-size: 12px; color: var(--sd-dim); font-weight: 600; }
.sd-field-hint { font-size: 12px; color: var(--sd-dim); margin: 4px 0 0; }

.sd-input {
  font: inherit;
  color: inherit;
  padding: 9px 11px;
  border: 1px solid var(--sd-line);
  border-radius: 8px;
  background: var(--sd-surface);
  width: 100%;
  box-sizing: border-box;
}
.sd-input:focus-visible { outline: 2px solid var(--sd-accent); outline-offset: 1px; }
.sd-textarea { resize: vertical; min-height: 54px; }

.sd-check { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }

/* --- 칩 --- */
.sd-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.sd-chips-tight { gap: 4px; }
.sd-chip {
  font: inherit;
  font-size: 13px;
  padding: 5px 11px;
  border-radius: 999px;
  border: 1px solid var(--sd-line);
  background: var(--sd-surface);
  color: var(--sd-dim);
  cursor: pointer;
}
.sd-chip-sm { font-size: 12px; padding: 3px 9px; }
.sd-chip.is-on { border-color: var(--sd-accent); color: var(--sd-accent); }
.sd-chip.is-warn { border-color: var(--sd-danger); color: var(--sd-danger); }

/* --- 버튼 --- */
.sd-button {
  font: inherit;
  font-size: 14px;
  padding: 9px 16px;
  border-radius: 8px;
  border: 1px solid var(--sd-line);
  background: var(--sd-surface);
  color: var(--sd-ink);
  cursor: pointer;
  text-decoration: none;
  display: inline-block;
}
.sd-button:hover { background: var(--sd-bg); }
.sd-button:disabled { opacity: 0.55; cursor: default; }
/* 100bj: AI 가 만드는 동안 잠긴 버튼(stream.js 실행 잠금 — aria-disabled + 까닭 말풍선)은 꺼진 버튼과 같게 보인다. 누름은 stream.js 가 막는다. */
[data-run-locked="1"]:not(textarea):not(input):not(select) { opacity: 0.55; cursor: default; }
.sd-button-primary {
  background: var(--sd-accent);
  border-color: var(--sd-accent);
  color: #fff;
}
.sd-button-primary:hover { filter: brightness(1.08); background: var(--sd-accent); }
.sd-button-ghost { border-color: transparent; color: var(--sd-accent); }

.sd-actions { border-top: 1px solid var(--sd-line); padding-top: 16px; }
.sd-actions-hint { font-size: 12px; color: var(--sd-dim); margin: 8px 0 0; }
.sd-toolbar { display: flex; gap: 12px; align-items: flex-start; margin-bottom: 16px; }
.sd-toolbar .sd-textarea { flex: 1; }
.sd-toolbar-wrap { flex-wrap: wrap; }
.sd-toolbar-wrap .sd-textarea { flex: 1 1 260px; min-width: 0; }
/* 기획안처럼 입력이 본문일 때. 칸을 폭 전체로 두고 버튼을 아래에 놓는다 —
   한 줄에 나란히 두면 라벨과 예시가 들어갈 자리가 없다. */
.sd-toolbar-stacked { flex-direction: column; align-items: stretch; }
.sd-toolbar-stacked .sd-field { margin-bottom: 0; }
.sd-toolbar-stacked .sd-button { align-self: flex-start; }
/* 세로로 쌓인 툴바 안에서 버튼 몇 개를 한 줄에 둔다. 버튼을 stacked 의 직접 자식으로
   두면 각각 한 줄을 차지해 "직접 쓴 안 추가" 가 저 아래로 내려간다. */
.sd-toolbar-row { display: flex; gap: 8px; flex-wrap: wrap; }

/* 범위가 다른 동작을 선으로 가른다 (tasks 2.10).
   S7 의 "대본 만들기 / 전체 다시" 는 **이 회차**이고 "모든 회차 대본 만들기" 는
   시리즈 전체다. 같은 줄에 두면 둘이 같은 범위처럼 보이고, 12부작에서 그 착각의
   값은 차감 36건이다. */
.sd-toolbar-divided {
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  border-top: 1px solid var(--sd-line);
  padding-top: 12px;
  margin-top: 4px;
}
.sd-toolbar-divided .sd-field-hint { margin: 0; }

/* --- 분량 예산 --- */
.sd-budget {
  border: 1px solid var(--sd-line);
  border-radius: 8px;
  padding: 14px;
  background: var(--sd-bg);
}
.sd-budget-title { font-size: 13px; margin: 0 0 10px; }
.sd-budget-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 10px;
}
.sd-metric { display: flex; flex-direction: column; }
.sd-metric-label { font-size: 11px; color: var(--sd-dim); }
.sd-metric-value { font-size: 16px; }

/* --- 유형 카드 --- */
.sd-type-cards { display: flex; gap: 12px; flex-wrap: wrap; }
.sd-type-card {
  flex: 1 1 240px;
  text-align: left;
  padding: 18px;
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  background: var(--sd-surface);
  cursor: pointer;
  font: inherit;
  color: inherit;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.sd-type-card:hover { border-color: var(--sd-accent); }
.sd-type-label { font-size: 15px; }
.sd-type-hint { font-size: 13px; color: var(--sd-dim); }

/* --- 프로젝트 목록 --- */
.sd-project-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.sd-project {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  background: var(--sd-surface);
  cursor: pointer;
  font: inherit;
  color: inherit;
  text-align: left;
}
.sd-project:hover { border-color: var(--sd-accent); }
.sd-project-title { flex: 1; }
.sd-project-meta { font-size: 12px; color: var(--sd-dim); }

/* --- 기획방향 카드 --- */
.sd-direction-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 12px;
}
.sd-direction {
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.sd-direction.is-selected { border-color: var(--sd-accent); box-shadow: inset 0 0 0 1px var(--sd-accent); }
.sd-direction-head { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.sd-direction-id { font-size: 12px; color: var(--sd-dim); font-weight: 700; }
.sd-axis {
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--sd-bg);
  color: var(--sd-accent);
  border: 1px solid var(--sd-line);
}
.sd-direction-parent { font-size: 11px; color: var(--sd-dim); }
.sd-direction-title { font-size: 15px; margin: 0; }
.sd-direction-logline { margin: 0; }
.sd-direction-details { display: flex; flex-direction: column; gap: 10px; }
.sd-direction-field p { margin: 2px 0 0; }
.sd-direction-field ul { margin: 2px 0 0; padding-left: 18px; }
.sd-direction-actions { display: flex; gap: 8px; margin-top: auto; padding-top: 8px; }

.sd-axis-summary { margin-top: 24px; }

/* --- 캐릭터 목록 (S3) ---

   **가로 행 목록이다.** 예전에는 300px 카드 그리드였는데, 12명이면 카드가 세 줄로
   쌓여 이름을 나란히 훑을 수 없었다. 이 화면에서 가장 자주 하는 일이 "누가 있었지" 를
   확인하는 것이라, 한 줄에 한 명씩 두고 이름을 같은 열에 세운다.

   잠금 테두리와 "저장 안 됨" 표시는 그대로 둔다. 그 둘이 이 화면의 핵심 상태다. */
.sd-character-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.sd-character {
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  padding: 10px 14px;
  min-width: 0;
}
/* 잠긴 인물은 재생성에서 지켜진다. 테두리로 그것을 계속 보이게 한다. */
.sd-character.is-locked { border-color: var(--sd-accent); box-shadow: inset 0 0 0 1px var(--sd-accent); }
/* 아직 저장하지 않은 잠금. 점선이라 "확정 아님" 이 한눈에 보인다. */
.sd-character.is-dirty { border-style: dashed; }

/* 한 인물 = 한 행. 이름까지는 고정 폭을 줘서 행마다 같은 열에 선다. */
.sd-character-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.sd-character-id { font-size: 12px; color: var(--sd-dim); font-weight: 700; }
/* 고정 폭은 **행 안에서만** 준다. 이 클래스는 분기 카드에서도 쓰이고(treatment.js),
   거기 id 는 `bp_synopsis_2` 처럼 길어서 34px 에 넣으면 잘린다. */
.sd-character-row > .sd-character-id { flex: 0 0 34px; }
.sd-character-name { font-size: 15px; margin: 0; flex: 0 0 auto; min-width: 96px; }
.sd-role-chip {
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--sd-bg);
  color: var(--sd-accent);
  border: 1px solid var(--sd-line);
  flex: 0 0 auto;
}
/* 나이·성별·직업. 남는 폭을 먹고, 좁아지면 여기가 먼저 줄어든다. */
.sd-character-line {
  margin: 0;
  color: var(--sd-dim);
  font-size: 13px;
  flex: 1 1 120px;
  min-width: 0;
}

.sd-character-details {
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* 행 아래에 펼쳐진다. 들여쓰기로 어느 인물의 것인지 보이게 한다. */
  margin: 10px 0 2px 44px;
  padding-left: 10px;
  border-left: 2px solid var(--sd-line);
}
.sd-character-field p { margin: 2px 0 0; }
.sd-character-field ul { margin: 2px 0 0; padding-left: 18px; }
/* 액션 4개(자세히·잠금·수정·삭제)는 행 오른쪽 끝에 모은다. */
.sd-character-actions {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-left: auto;
  flex: 0 0 auto;
}

/* 원작 매핑은 읽기 전용이다(서버가 계산한다). 편집 가능한 필드와 구별되게 눕혀 둔다. */
.sd-source-map { border-left: 2px solid var(--sd-line); padding-left: 10px; }

.sd-character-summary { margin-top: 24px; }

/* --- 부분 수정 패널 (한 명의 지정된 필드만) ---

   카드 안에서 열린다. 별 화면으로 빼지 않는 이유: 고칠 값이 바로 위에 보여야
   "무엇이 아쉬운지" 를 적을 수 있다. */
.sd-refine {
  border-top: 1px dashed var(--sd-line);
  margin-top: 8px;
  padding-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.sd-check-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: 4px 10px;
}

/* 필드마다 [라벨] [직접 입력 칸] [AI 가 다시] 세 칸.
   라벨을 고정폭으로 두어 칸이 세로로 정렬되게 한다 — 값이 긴 필드에서 입력 칸이
   들쭉날쭉하면 어느 줄이 어느 필드인지 읽기 어렵다. */
.sd-edit-grid { display: flex; flex-direction: column; gap: 6px; }
.sd-edit-row {
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
}
.sd-edit-row .sd-field-label { margin: 0; }
.sd-edit-row .sd-textarea { min-height: 40px; }
.sd-edit-row.is-disabled { opacity: 0.55; }
/* 잠금 안내는 세 칸 아래에 한 줄로 붙는다. */
.sd-edit-row > .sd-field-hint:last-child { grid-column: 2 / -1; margin: 0; }
@media (max-width: 520px) {
  .sd-edit-row { grid-template-columns: 1fr auto; }
  .sd-edit-row .sd-field-label { grid-column: 1 / -1; }
}
.sd-check-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  cursor: pointer;
}
/* 잠긴 인물의 이름·나이·성별·역할. 눌러 본 뒤 거부당하는 것보다 낫다. */
.sd-check-item.is-disabled { opacity: 0.5; cursor: default; }

/* --- 확정 바 ---

   단계 잠금 판정이 "바로 위 단계의 확정본" 이라, 이것이 없으면 다음 단계가 영원히
   열리지 않는다. 화면 아래에 두고 테두리로 눈에 띄게 한다. */
.sd-confirm-host { margin-top: 24px; }
.sd-confirm {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  border: 1px solid var(--sd-accent);
  border-radius: var(--sd-radius);
  padding: 14px 16px;
  background: var(--sd-bg);
}
/* 이미 확정된 뒤에는 강조하지 않는다. 할 일이 남아 있는 것처럼 보이면 안 된다. */
.sd-confirm.is-confirmed { border-color: var(--sd-line); }
.sd-confirm-body { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 200px; }
.sd-confirm-body .sd-field-hint { margin: 0; }
.sd-badge-confirmed { border-color: var(--sd-accent); color: var(--sd-accent); }

/* --- 회차 목록 (S5) ---

   **한 회차 = 한 줄.** 카드 그리드가 아니다.

   예전에는 `repeat(auto-fill, minmax(280px, 1fr))` 카드 그리드였고 카드 안의 내용이
   세로로 쌓여 있었다. 그래서 12부작을 읽으려면 카드마다 위아래로 눈을 움직이고 다시
   옆 칸으로 넘어가야 했다. 회차는 **순서대로 읽는** 것이므로 그 동선이 내용과 맞지
   않는다 — 흐름을 보려면 1화부터 12화까지 한 방향으로 내려가야 한다.

       [번호·상태]  [제목 / 한 줄 / 엔딩 훅 / 사건]  [분 · 확정 · 버튼]

   좁은 화면에서는 아래 미디어 쿼리가 다시 세로로 쌓는다.

   100bn: 회차 목록은 2차 틀의 문서 칸(AI 비서 칸 폭에 따라 줄어든다) 안에도 들어간다. 화면 폭 질의만으로는
   카드가 716px 인데 화면이 1089px 이라 쌓이지 않았다. 그래서 목록 자체를 크기 질의 컨테이너로 두고 목록 폭으로 쌓는다. */
.sd-episode-grid {
  display: flex;
  flex-direction: column;
  gap: 10px;
  container-type: inline-size;
  container-name: sd-episodes;
}
.sd-episode {
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  padding: 14px 16px;
  display: grid;
  /* 번호·상태 칸은 고정, 본문이 남은 폭을 전부, 오른쪽 조작 칸은 상한이 있는 폭.
     `minmax(0, 1fr)` 이어야 긴 한 줄 요약이 칸을 밀어내지 않는다.
     100bn: 옆 칸의 상한을 `auto` 로 두면 안 된다. 격자는 fr 칸보다 먼저 auto 칸을 내용 최대 폭까지 늘리므로,
     상세를 펼쳐 옆 칸 안내 글이 길어지면 옆 칸이 남는 폭을 다 가져가 본문이 0px 이 됐다(글자가 한 자씩 세로로). */
  grid-template-columns: 120px minmax(0, 1fr) minmax(200px, 280px);
  align-items: start;
  column-gap: 18px;
  row-gap: 8px;
  min-width: 0;
}
/* 상세가 있는 회차. 다음 단계로 넘어갈 수 있는 회차라는 뜻이다. */
.sd-episode.has-detail { border-color: var(--sd-accent); }
/* 확정된 회차. 대본을 만들 수 있는 상태이고, 이 화면에서 가장 중요한 신호다 —
   상세를 만들었는데 확정하지 않아 대본 단계가 열리지 않는 일이 실제로 있었다. */
.sd-episode.is-confirmed { border-left: 4px solid var(--sd-accent); }
/* 구조표가 바뀌어 상세가 옛 근거로 만들어진 상태. */
.sd-episode.is-stale { border-style: dashed; border-color: var(--sd-warn); }
.sd-episode.is-picked { box-shadow: inset 0 0 0 2px var(--sd-accent); }

/* 세 칸. 상세 패널만 전체 폭을 쓴다. */
.sd-episode-lead {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  min-width: 0;
}
.sd-episode-lead > * { max-width: 100%; }
.sd-episode-main { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.sd-episode-side {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  min-width: 0;
  overflow-wrap: anywhere;
}
/* 옆 칸 안의 줄(분 입력 + 긴 안내 · 확정 · 버튼 줄)이 칸 폭을 넘지 않고 줄바꿈한다. */
.sd-episode-side > * { max-width: 100%; min-width: 0; }
.sd-episode-side .sd-episode-minutes .sd-field-hint { min-width: 0; overflow-wrap: anywhere; }
.sd-episode-detail { grid-column: 1 / -1; }
/* 확정 상태와 버튼. 문구가 왜 확정이 필요한지 말한다. */
.sd-episode-confirm { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; }
.sd-episode-confirm .sd-field-hint { margin: 0; }

/* 좁은 화면에서는 세 칸을 다시 쌓는다. 가로 배치는 폭이 있을 때만 읽기 쉽다. */
@media (max-width: 1000px) {
  .sd-episode { grid-template-columns: minmax(0, 1fr); }
  .sd-episode-lead { flex-direction: row; align-items: center; flex-wrap: wrap; }
}
/* 100bn: 같은 쌓기를 목록 폭으로도 한다(2차 틀의 좁은 문서 칸). 760px 이면 본문 칸이 약 290px 남는다. */
@container sd-episodes (max-width: 760px) {
  .sd-episode { grid-template-columns: minmax(0, 1fr); }
  .sd-episode-lead { flex-direction: row; align-items: center; flex-wrap: wrap; }
}

/* 옛 이름. 다른 화면이 아직 쓸 수 있어 남겨 둔다. */
.sd-episode-head { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.sd-episode-title { font-size: 15px; margin: 0; }
.sd-episode-line { margin: 0; color: var(--sd-dim); font-size: 13px; }
/* 엔딩 훅은 이 단계의 핵심이다. 눈에 띄게 둔다. */
.sd-episode-hook {
  border-left: 2px solid var(--sd-accent);
  padding-left: 10px;
}
.sd-episode-hook p { margin: 2px 0 0; }
.sd-episode-events { margin: 0; padding-left: 18px; font-size: 13px; color: var(--sd-dim); }
.sd-episode-detail { border-top: 1px dashed var(--sd-line); padding-top: 10px; }
.sd-sequence-list { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 8px; }
.sd-sequence-list p { margin: 2px 0; }

/* 회차 상세 직접 편집. 시퀀스마다 fieldset 으로 묶어 어느 시퀀스의 칸인지 보이게 한다. */
.sd-detail-form { display: flex; flex-direction: column; gap: 10px; }
.sd-detail-form .sd-field { margin-bottom: 0; gap: 4px; }
.sd-seq-edit {
  border: 1px solid var(--sd-line);
  border-radius: 8px;
  padding: 10px 12px;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.sd-seq-edit legend { font-size: 12px; color: var(--sd-dim); font-weight: 600; padding: 0 4px; }
/* 순서 변경 버튼. 좁게 둬야 액션 줄이 넘치지 않는다(회차 카드에 버튼이 3개다). */
.sd-button-icon { padding: 9px 10px; line-height: 1; }

/* --- 잡 진행 (S5 회차 상세, N-04) ---

   회차 하나가 1~3분이라 퍼센트만 보여 주면 멈춘 것처럼 보인다. 그래서 회차별 칩을
   함께 둔다 — 어느 회차가 됐고 어느 회차가 실패했는지가 재시도 판단의 근거다. */
.sd-job {
  border: 1px solid var(--sd-line);
  border-radius: 8px;
  padding: 14px;
  background: var(--sd-bg);
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 16px;
}
.sd-job-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sd-job-head .sd-field-hint { margin: 0; }
.sd-progress {
  height: 6px;
  border-radius: 999px;
  background: var(--sd-line);
  overflow: hidden;
}
.sd-progress-fill { height: 100%; background: var(--sd-accent); transition: width 0.3s; }
.sd-unit.is-done { border-color: var(--sd-accent); color: var(--sd-accent); }
.sd-unit.is-failed { border-color: var(--sd-danger); color: var(--sd-danger); }
.sd-unit.is-queued { opacity: 0.6; }

/* --- 전체 흐름 갈림길 (S5 구조표와 같은 응답에서 온다) --- */
.sd-branch-host { margin-top: 24px; }
.sd-branch-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 12px;
}
.sd-branch {
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.sd-branch ul { margin: 0; padding-left: 18px; font-size: 13px; }
/* 앞선 선택과 모순되어 고를 수 없는 갈림길. 지우지 않고 남기는 이유는 "왜 못 고르나" 를
   보여 주기 위해서다(`branch_points.disable_conflicts`). */
.sd-branch.is-disabled { opacity: 0.55; }
.sd-branch-options { display: flex; flex-direction: column; gap: 8px; }
/* 라디오를 첫 줄에 맞춘다. 기본 정렬이면 여러 줄 요약에서 가운데로 내려간다. */
.sd-branch-option { display: grid; grid-template-columns: auto 1fr; gap: 8px; align-items: start; }
.sd-branch-option p { margin: 2px 0 0; font-size: 13px; }
.sd-branch-option .sd-field-hint { margin: 2px 0 0; }
.sd-check-item .sd-field-hint { margin: 0; }
.sd-checkbox { accent-color: var(--sd-accent); margin: 0; }
.sd-refine-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.sd-refine-actions .sd-field-hint { margin: 0; }

/* 삭제는 되돌리기 어렵다(버전 복원 경로가 이 화면에 없다). 색으로 구별한다. */
.sd-button-danger { border-color: var(--sd-danger); color: var(--sd-danger); }
.sd-button-danger:hover { background: var(--sd-danger-soft); }

/* 인원 수 입력. 툴바 안에서 폭을 차지하지 않게 한다. */
.sd-field-inline { flex-direction: column; gap: 4px; margin-bottom: 0; min-width: 132px; }
.sd-input-num { width: 92px; }
/* 나이 칸. 숫자만 받지 않는다 — "알 수 없음" 도 들어가야 하므로 조금 넓다. */
.sd-input-age { width: 140px; }

/* --- 시놉시스 문단 (S4) ---

   카드 그리드가 아니라 한 줄씩 쌓는다. 문단은 순서가 의미이고(발단→결말),
   나란히 놓으면 읽는 순서가 흐트러진다. */
.sd-para-list { display: flex; flex-direction: column; gap: 14px; margin-bottom: 28px; }
.sd-para {
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* 분기 적용으로 다시 써진 문단. 어디가 바뀌었는지 즉시 보여야 한다. */
.sd-para.is-rewritten { border-color: var(--sd-accent); }
.sd-para-head { display: flex; align-items: center; gap: 8px; }
.sd-para-no {
  font-size: 11px;
  font-weight: 700;
  color: var(--sd-dim);
  min-width: 16px;
}
.sd-para-tag {
  font-size: 12px;
  font-weight: 700;
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--sd-bg);
  border: 1px solid var(--sd-line);
}
.sd-para-count { font-size: 11px; color: var(--sd-dim); margin-left: auto; }
.sd-para-text { min-height: 110px; }
.sd-para-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* --- 분기점 카드 (S4) --- */
.sd-branch-list { display: flex; flex-direction: column; gap: 14px; }
.sd-branch {
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.sd-branch.is-applied { border-color: var(--sd-accent); }
/* 모순으로 비활성된 분기점. 지우지 않고 흐리게 남긴다 — 왜 못 누르는지 보여야 한다. */
.sd-branch.is-disabled { opacity: 0.55; }
.sd-branch-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sd-branch-scope {
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--sd-bg);
  color: var(--sd-accent);
  border: 1px solid var(--sd-line);
}
.sd-branch-question { font-size: 15px; margin: 0; }
.sd-branch-options {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 10px;
}
.sd-option {
  text-align: left;
  border: 1px solid var(--sd-line);
  border-radius: 8px;
  padding: 12px;
  background: var(--sd-surface);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 6px;
  font: inherit;
  color: inherit;
}
.sd-option:hover:not(:disabled) { border-color: var(--sd-accent); }
.sd-option:disabled { cursor: default; }
.sd-option.is-picked { border-color: var(--sd-accent); box-shadow: inset 0 0 0 1px var(--sd-accent); }
.sd-option:focus-visible { outline: 2px solid var(--sd-accent); outline-offset: 1px; }
.sd-option-head { display: flex; align-items: baseline; gap: 6px; }
.sd-option-label { font-size: 12px; font-weight: 700; color: var(--sd-accent); }
.sd-option-title { font-size: 13px; }
.sd-option-summary { margin: 0; font-size: 13px; color: var(--sd-dim); }
/* impact 가 카드의 존재 이유다. 요약보다 눈에 먼저 들어와야 한다. */
.sd-option-impact {
  margin: 0;
  font-size: 13px;
  border-top: 1px solid var(--sd-line);
  padding-top: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.sd-branch-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }

/* --- 레퍼런스 --- */
.sd-reference-list { display: flex; flex-direction: column; gap: 10px; }
.sd-reference {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 8px;
  align-items: center;
  padding: 10px;
  border: 1px solid var(--sd-line);
  border-radius: 8px;
}
.sd-reference > .sd-chips { grid-column: 1 / -1; }

/* --- 상태 · 알림 --- */
.sd-status { min-height: 24px; margin-bottom: 12px; }
.sd-status-line { display: flex; align-items: center; gap: 8px; margin: 0; }

.sd-spinner {
  width: 13px;
  height: 13px;
  border: 2px solid var(--sd-line);
  border-top-color: var(--sd-accent);
  border-radius: 50%;
  animation: sd-spin 0.9s linear infinite;
  flex: 0 0 auto;
}
@keyframes sd-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .sd-spinner { animation-duration: 3s; }
}

/* 타자기 커서(motion.js typewriter). 금색 막대 `▍`.
   깜빡임은 opacity 만 쓰고, 축소 모션이면 켜 둔 채로 멈춘다(움직임 없음). */
.sd-type-cursor {
  color: var(--cv2-gold, #b3803f);
  font-weight: 600;
  animation: sd-cursor-blink 1s steps(1, end) infinite;
}
@keyframes sd-cursor-blink { 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .sd-type-cursor { animation: none; opacity: 1; }
}

.sd-notice {
  border: 1px solid var(--sd-line);
  border-left-width: 3px;
  border-radius: 8px;
  padding: 12px 14px;
  margin: 0 0 16px;
  background: var(--sd-surface);
}
.sd-notice-warn { border-left-color: var(--sd-warn); }

.sd-notes {
  border: 1px solid var(--sd-danger);
  border-radius: 8px;
  padding: 12px 14px;
  margin-bottom: 16px;
}
.sd-notes-title { font-size: 13px; margin: 0 0 8px; color: var(--sd-danger); }
.sd-notes ul { margin: 0; padding-left: 18px; }
.sd-note-fix { color: var(--sd-dim); font-style: normal; }

/* 확인 사항. **경고가 아니다.**
   고쳐야 하는 메모는 빨간 테두리(.sd-notes)로 두고, 이쪽은 색을 빼서 "읽고 넘기면
   되는 것" 으로 보이게 한다. 단역 이름 안내처럼 회차마다 붙는 항목이 빨갛게 보이면
   사용자가 검수 표시 자체를 무시하게 된다. */
.sd-notes-info { margin-top: 10px; }
.sd-notes-info > .sd-notes-title {
  color: var(--sd-dim);
  cursor: pointer;
  margin: 0;
}
.sd-notes-info[open] > .sd-notes-title { margin-bottom: 8px; }
.sd-notes-info ul { color: var(--sd-dim); }

/* 고칠 것이 없고 확인 사항만 있으면 바깥 테두리도 빨갛지 않아야 한다.
   `:has()` 를 쓰는 이유: 그 판정을 JS 로 하면 클래스를 붙이는 코드가 한 곳 더 늘고,
   두 곳이 어긋나면 테두리와 내용이 다른 말을 한다. */
.sd-notes:not(:has(> ul)) {
  border-color: var(--sd-line);
}

.sd-empty { color: var(--sd-dim); padding: 20px 0; }

.sd-toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%) translateY(8px);
  padding: 11px 18px;
  border-radius: 999px;
  background: var(--sd-ink);
  color: #fff;
  font-size: 14px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s, transform 0.18s;
  z-index: 20;
  max-width: 90vw;
}
.sd-toast.is-visible { opacity: 1; transform: translateX(-50%) translateY(0); }
/* 오류 토스트는 흰 글자를 얹는 바탕이라 글자용 --sd-danger(어두운 테마는 밝은 빨강)가 아니라 바탕용 토큰이다(두 테마 같은 값 —
   흰 글자 5.5:1, uiux-261029-e 091). */
.sd-toast.is-error { background: var(--sd-danger-fill); }
/* 어두운 테마의 기본 토스트 · '처리 중' 알약(uiux-261029-e 091b).
   왜: 두 부품은 바탕이 --sd-ink(= --text, 어두운 테마 #fff)이고 글자가 #fff 고정이라 어두운 테마에서 흰 바탕에 흰 글자(1:1)였다.
   079 가 구조 맵 창만 `.sd-map-page` 범위로 고쳤던 규칙을 모든 화면 공통으로 넓힌다(값은 079 그대로 — 바탕 --nested #2a2a2a ·
   글자 --text #fff, 14.4:1). 맵 창의 같은 규칙은 지웠다 — 두 곳에 두면 더 구체적인 맵 창 규칙만 따로 어긋날 수 있다.
   오류 토스트는 빼고 위 --sd-danger-fill 바탕 · 흰 글자(두 테마 5.5:1)를 그대로 둔다. 성공 · 정보 알림은 따로 모양이 없다
   (dom.js toast() 는 "error" 만 나눈다 — 이 규칙이 그대로 걸린다). 알약의 기본 규칙은 아래 '처리 중' 절에 있다 — 이 규칙이 더
   구체적이라 순서와 상관없이 이긴다. 대체값도 079 와 같다: 글자 · 바탕을 한 규칙에서 함께 정해 styles.css 를 못 읽어도 어두운
   바탕 · 흰 글자 한 짝으로 남는다. 밝은 테마는 덮지 않는다(모양 그대로). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .sd-toast:not(.is-error),
  :root:not([data-theme="light"]) .sd-busy-pill {
    background: var(--nested, #2a2a2a);
    color: var(--text, #ffffff);
  }
}
:root[data-theme="dark"] .sd-toast:not(.is-error),
:root[data-theme="dark"] .sd-busy-pill {
  background: var(--nested, #2a2a2a);
  color: var(--text, #ffffff);
}

/* --------------------------------------------------------------------------
   버전 패널 · 비교 (versions.js, tasks 1.39~1.43)
   -------------------------------------------------------------------------- */

.sd-button-small { padding: 6px 10px; font-size: 13px; }

/* --- 회차별 분 · 목표 분량 (R-13, tasks 2.11) ---
   회차 카드 안의 한 줄. 입력 칸을 좁게 두는 이유: 값이 두세 자리이고, 넓으면
   제목과 한 줄 요약보다 눈에 먼저 들어온다. 이 칸은 대부분 비어 있다(틀의 분을 쓴다). */
.sd-episode-minutes {
  display: flex;
  gap: 10px;
  align-items: baseline;
  flex-wrap: wrap;
  margin: 8px 0 4px;
}
.sd-episode-minutes .sd-field { margin-bottom: 0; }
.sd-episode-minutes .sd-field-hint { flex: 1 1 240px; margin: 0; }
.sd-minutes-input { width: 84px; }

.sd-error-text { color: var(--sd-danger); margin: 0; }

.sd-versions-host { margin-top: 16px; }

.sd-versions {
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  background: var(--sd-surface);
}

/* 접힌 상태가 기본이다. 버전이 20개까지 쌓이므로 펼친 채로 두면 본문이 밀린다. */
.sd-versions-summary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  cursor: pointer;
  font-weight: 600;
  font-size: 14px;
}
.sd-versions-summary::-webkit-details-marker { color: var(--sd-dim); }
.sd-versions-summary .sd-field-hint { margin-left: auto; font-weight: 400; }
.sd-versions[open] .sd-versions-summary { border-bottom: 1px solid var(--sd-line); }

.sd-versions-body { padding: 12px 14px 14px; }

.sd-version-list {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.sd-version {
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  padding: 10px 12px;
}
/* 지금 보고 있는 버전. 목록에서 자기 위치를 잃지 않게 표시한다. */
.sd-version.is-current { border-color: var(--sd-accent); background: var(--sd-bg); }
.sd-version.is-confirmed { border-left: 3px solid var(--sd-success); }

.sd-version-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sd-version-no { font-size: 15px; }
.sd-version-meta { color: var(--sd-dim); font-size: 12px; }
.sd-version-summary { margin: 6px 0 0; font-size: 14px; }
/* 사람이 기억하는 것은 본문이 아니라 자기가 쓴 지시다. 눈에 띄게 둔다. */
.sd-version-instruction { margin: 6px 0 0; font-size: 13px; color: var(--sd-accent); }
.sd-version-actions { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }

.sd-diff-host { margin-top: 12px; border-top: 1px dashed var(--sd-line); padding-top: 10px; }
.sd-diff-list { list-style: none; margin: 8px 0 0; padding: 0; }
.sd-diff-row { padding: 8px 0; border-bottom: 1px solid var(--sd-line); }
.sd-diff-row:last-child { border-bottom: none; }
/* 같은 항목도 남긴다(서버 `_diff_rows`). 다른 것만 눈에 들어오게 색으로 가른다. */
.sd-diff-row.is-changed .sd-diff-label { color: var(--sd-accent); }
.sd-diff-label { font-size: 13px; }

.sd-diff-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 6px; }
.sd-diff-side { border: 1px solid var(--sd-line); border-radius: 8px; padding: 8px 10px; }
.sd-diff-side p { margin: 4px 0 0; font-size: 13px; white-space: pre-wrap; }
.sd-diff-left { background: #fffaf0; }
.sd-diff-right { background: #f0fff4; }
.sd-diff-tag { font-size: 11px; color: var(--sd-dim); }

@media (max-width: 720px) {
  /* 좁은 화면에서 두 칸을 나란히 두면 한 칸이 3~4단어로 줄어 읽을 수 없다. */
  .sd-diff-pair { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   변경 이력 (history.js)
   -------------------------------------------------------------------------- */

/* 이력은 스테퍼 없이 본문만 쓴다. 단계가 아니라 프로젝트 전체 축이다. */
.sd-stage-wide { width: 100%; }

.sd-history-search { display: flex; gap: 8px; margin: 16px 0; flex-wrap: wrap; }
.sd-history-search .sd-input { flex: 1 1 240px; }

.sd-history-list {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.sd-history-item {
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  padding: 10px 12px;
  background: var(--sd-surface);
}
/* 두 종류가 한 줄기에 섞인다(history.js 주석). 왼쪽 색선으로 종류를 구별한다. */
.sd-history-artifact { border-left: 3px solid var(--sd-line); }
.sd-history-decision { border-left: 3px solid var(--sd-accent); }

.sd-history-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sd-history-at { color: var(--sd-dim); font-size: 12px; font-variant-numeric: tabular-nums; }
.sd-history-what { font-size: 14px; }
.sd-history-meta { color: var(--sd-dim); font-size: 12px; }
.sd-history-summary { margin: 6px 0 0; font-size: 14px; }
.sd-history-instruction { margin: 6px 0 0; font-size: 13px; color: var(--sd-accent); }
.sd-history-item .sd-button { margin-top: 8px; }

.sd-badge-decision { border-color: var(--sd-accent); color: var(--sd-accent); }

/* --------------------------------------------------------------------------
   S6 마무리 (stages/finish.js, R-12)
   -------------------------------------------------------------------------- */

/* 두 옵션을 같은 무게로 둔다. 한쪽을 크게 만들면 "고르기" 로 읽히는데,
   R-12 는 둘이 배타가 아니라고 명시한다. */
.sd-finish-option {
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  padding: 16px;
  margin-top: 16px;
}

.sd-finish-parts { list-style: none; margin: 12px 0; padding: 0; }

.sd-finish-part {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 6px 0;
  border-bottom: 1px solid var(--sd-line);
  /* 빠진 절이 기본 모양이다. 준비된 것만 색을 준다 — 무엇이 빠졌는지가 이 목록의 정보다. */
  color: var(--sd-dim);
}
.sd-finish-part:last-child { border-bottom: none; }
.sd-finish-part.is-ready { color: var(--sd-ink); }

.sd-finish-mark {
  flex: 0 0 auto;
  font-size: 11px;
  padding: 2px 7px;
  border: 1px solid var(--sd-line);
  border-radius: 999px;
  color: var(--sd-dim);
}
.sd-finish-part.is-ready .sd-finish-mark { border-color: var(--sd-success); color: var(--sd-success); }

.sd-finish-detail { margin-left: auto; font-size: 13px; }

.sd-finish-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

/* 확정 뒤의 배지 + "다시 만들기". 확정이 끝이 아니라는 것을 나란히 보여 준다. */
.sd-confirm-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* --------------------------------------------------------------------------
   S7 대본 (stages/script.js, R-13)
   -------------------------------------------------------------------------- */

/* 회차 탭. 숏폼은 80부작이라 가로로 흐르게 두고 줄바꿈을 허용한다 —
   스크롤 컨테이너로 만들면 40화가 화면 밖에 숨고, 그것을 찾을 방법이 없다. */
.sd-ep-tabs {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: 20px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--sd-line);
}
.sd-ep-tab {
  font: inherit;
  font-size: 13px;
  padding: 5px 12px;
  border-radius: 999px;
  border: 1px solid var(--sd-line);
  background: var(--sd-surface);
  color: var(--sd-dim);
  cursor: pointer;
}
.sd-ep-tab:hover { background: var(--sd-bg); }
/* 대본이 있는 회차. 이 화면에서 가장 중요한 상태다. */
.sd-ep-tab.has-script { color: var(--sd-accent); border-color: var(--sd-accent); }
.sd-ep-tab.is-active {
  background: var(--sd-accent);
  border-color: var(--sd-accent);
  color: #fff;
}
.sd-ep-tab.is-stale { border-style: dashed; }
/* 트리트먼트가 확정되지 않아 아직 쓸 수 없는 회차. 누르면 이유를 보여 준다 —
   막지 않는 이유: 왜 못 쓰는지 읽을 수 있어야 한다. */
.sd-ep-tab.is-locked { opacity: 0.55; }

.sd-field-inline { margin-bottom: 0; max-width: 200px; }

/* --- 분량 게이지 ---
   대본은 "됐다/안 됐다" 가 아니라 얼마나 썼는가가 판정 기준이다. 허용 구간(±15%)을
   막대 안에 함께 그리는 이유: 퍼센트만 보여 주면 92% 가 통과인지 알 수 없다. */
.sd-gauge-host { margin-bottom: 20px; }
.sd-gauge { display: flex; flex-direction: column; gap: 4px; margin: 10px 0; }
.sd-gauge-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.sd-gauge-head strong { font-size: 13px; }
.sd-gauge-value { font-size: 13px; color: var(--sd-dim); }
.sd-gauge-value.is-ok { color: var(--sd-success); }
.sd-gauge-value.is-under { color: var(--sd-warn); }
.sd-gauge-value.is-over { color: var(--sd-danger); }
.sd-gauge-bar {
  position: relative;
  height: 8px;
  border-radius: 999px;
  background: var(--sd-bg);
  border: 1px solid var(--sd-line);
  overflow: hidden;
}
/* 허용 구간. 채움 막대 아래에 깔린다. 색은 성공 토큰의 18%(예전 rgba(47, 133, 90, 0.18) 과 같은 비율 — 091b, 어두운 테마에서도
   성공 색을 따른다). color-mix 를 모르는 브라우저는 허용 구간만 빠진다(채움 · 값 글자는 그대로 — .sd-busy-bar 와 같은 기준). */
.sd-gauge-band {
  position: absolute;
  top: 0;
  bottom: 0;
  background: color-mix(in srgb, var(--sd-success) 18%, transparent);
}
.sd-gauge-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  border-radius: 999px;
  background: var(--sd-dim);
  transition: width 0.3s;
}
.sd-gauge-fill.is-ok { background: var(--sd-success); }
.sd-gauge-fill.is-under { background: var(--sd-warn); }
.sd-gauge-fill.is-over { background: var(--sd-danger); }

/* --- 블록 카드 ---
   회차 카드(S5)와 달리 그리드가 아니라 한 열이다. 블록은 순서대로 읽는 것이고,
   본문을 펼치면 수만 자가 되므로 나란히 둘 이유가 없다. */
.sd-block-list { display: flex; flex-direction: column; gap: 12px; }
.sd-script-block {
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.sd-script-block.has-text { border-color: var(--sd-accent); }
.sd-script-block.is-picked { box-shadow: inset 0 0 0 2px var(--sd-accent); }
/* 회차 분이 줄어 예산 밖으로 밀린 블록. 다음 생성에서 버려진다. */
.sd-script-block.is-orphan { border-style: dashed; border-color: var(--sd-warn); }

/* 씬 목차. 번호가 왼쪽에 붙어야 블록 경계에서 이어지는 것이 보인다. */
.sd-scene-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 13px;
}
.sd-scene-list li { display: flex; gap: 8px; }
.sd-scene-list strong { flex: 0 0 56px; color: var(--sd-dim); font-weight: 600; }

/* 대본 본문. `pre` 로 그린다 — 줄바꿈과 들여쓰기가 형식의 일부다(부록 B).
   `white-space: pre-wrap` 이라 긴 지문도 가로 스크롤 없이 접힌다. */
.sd-script-text {
  margin: 0;
  padding: 14px;
  background: var(--sd-bg);
  border: 1px solid var(--sd-line);
  border-radius: 8px;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: "Pretendard Variable", Pretendard, -apple-system, system-ui, sans-serif;
  font-size: 14px;
  line-height: 1.75;
  max-height: 60vh;
  overflow-y: auto;
}
/* 편집 칸은 본문과 같은 줄 간격이어야 한다. 다르면 고치는 동안 분량 감각이 어긋난다. */
.sd-script-edit { line-height: 1.75; min-height: 320px; }

/* 블록 안에 붙는 검수 메모. 회차 하나가 블록 수만큼 저장되므로 정점의 메모만 보면
   어느 블록이 반려됐는지 알 수 없다. 그래서 블록 카드 안에도 둔다. */
.sd-notes-inline { margin-bottom: 0; padding: 10px 12px; }
.sd-notes-inline .sd-notes-title { font-size: 12px; }
.sd-notes-inline ul { font-size: 13px; }

/* --------------------------------------------------------------------------
   상위 변경됨 패널 (stepper.js, tasks 1.39)
   --------------------------------------------------------------------------
   전에는 문장 한 줄이었다. 그 문장은 무엇을 해야 하는지 말하지 않아서, 사용자가
   할 수 있는 두 가지(그대로 쓰기 / 다시 만들기)에 화면상 길이 없었다. */
.sd-stale { margin-bottom: 20px; }
.sd-stale-title { font-size: 14px; margin: 0 0 4px; }
.sd-stale-list { list-style: none; margin: 12px 0 0; padding: 0; }
.sd-stale-item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 0;
  border-top: 1px solid var(--sd-line);
}
.sd-stale-body { display: flex; flex-direction: column; gap: 2px; flex: 1 1 240px; min-width: 0; }
.sd-stale-body .sd-field-hint { margin: 0; }
/* 무엇 때문에 stale 이 됐는가. 이 줄이 판단의 근거다 — 없으면 "왜 확인해야 하나" 를
   알 수 없어 사용자가 그냥 다시 만든다(차감이 생긴다). */
.sd-stale-reason { font-size: 13px; color: var(--sd-warn); }
.sd-stale-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

/* --------------------------------------------------------------------------
   AI 코멘트 (comment.js, R-19)
   --------------------------------------------------------------------------
   왼쪽 패널의 스테퍼 아래. 단계 화면 6개에 각각 붙이면 같은 코드가 여섯 벌이 되고
   차감 안내가 조금씩 갈린다. */
.sd-comment-host { margin-top: 24px; border-top: 1px solid var(--sd-line); padding-top: 16px; }
.sd-comment-host .sd-panel-title { margin-bottom: 6px; }
.sd-comment-host .sd-input { margin-bottom: 8px; }
.sd-comment-host .sd-button { width: 100%; }
.sd-comment-body { margin-top: 12px; }
.sd-comment { display: flex; flex-direction: column; gap: 10px; font-size: 13px; }
.sd-comment-lines { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 4px; }
/* 가장 약한 지점은 **하나**다. 여러 개를 지적하면 무엇을 먼저 고칠지 알 수 없다 —
   프롬프트가 그것을 요구하고 화면도 하나만 그린다. */
.sd-comment-weak { border-left: 2px solid var(--sd-danger); padding-left: 10px; }
.sd-comment-weak p { margin: 2px 0 0; }
.sd-comment-fix { border-left: 2px solid var(--sd-accent); padding-left: 10px; }
.sd-comment-fix ol { margin: 2px 0 0; padding-left: 18px; }

/* --------------------------------------------------------------------------
   원작 업로드 · 요약 카드 (source.js, R-14)
   --------------------------------------------------------------------------
   틀(S1) 화면의 저장 버튼 뒤에 온다. 원작 카드는 S1 산출물이고, 원작 기반 프로젝트는
   그 카드를 확정해야 S2 가 열린다 — 그 순서가 화면에도 보여야 한다. */
.sd-source-host { border-top: 1px solid var(--sd-line); padding-top: 20px; }
.sd-source-upload {
  border: 1px solid var(--sd-line);
  border-radius: 8px;
  padding: 14px;
  background: var(--sd-bg);
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 16px;
}
.sd-source-upload .sd-field { margin-bottom: 0; }
.sd-source-upload .sd-button { align-self: flex-start; }

.sd-source-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
.sd-source {
  border: 1px solid var(--sd-line);
  border-radius: 8px;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}
.sd-source.is-ready { border-color: var(--sd-accent); }
.sd-source.is-failed { border-color: var(--sd-danger); border-style: dashed; }
/* 파싱 경고는 **실패가 아니다.** 인코딩·표·건너뛴 페이지를 알려 주는 것이라
   빨강이 아니라 주의색을 쓴다 — 빨강으로 두면 정상 업로드가 실패처럼 보인다. */
.sd-source-warn { font-size: 13px; color: var(--sd-warn); margin: 0; }

/* 읽은 본문 미리보기. **앞부분만**이다(N-11) — 전문을 내려보내면 브라우저 캐시와
   프록시 로그에 원작이 남는다. */
.sd-source-preview {
  margin: 8px 0 0;
  padding: 12px;
  background: var(--sd-bg);
  border: 1px solid var(--sd-line);
  border-radius: 8px;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: inherit;
  font-size: 13px;
  line-height: 1.7;
  max-height: 260px;
  overflow-y: auto;
}
.sd-source-card-host { margin-top: 8px; }

/* 프로젝트 한 줄: 열기 버튼(넓게) + 파생 버튼(오른쪽). 파생을 카드 안에 넣으면
   카드를 누르려다 파생이 눌린다 — 두 동작의 무게가 다르다. */
.sd-project-row { display: flex; align-items: center; gap: 8px; }
.sd-project-row .sd-project { flex: 1 1 auto; min-width: 0; }
.sd-project-row > .sd-button { flex: 0 0 auto; }

/* ==========================================================================
   사용자 확정 기록 (story memory)

   표식은 눈에 띄되 결과물을 가리지 않아야 한다. 기획방향 카드에서 축 배지 옆에
   붙으므로 같은 높이·같은 라운드를 쓰고 색만 다르다.
   ========================================================================== */

.sd-memory-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--sd-accent-border, #b7c8e8);
  background: var(--sd-accent-soft, #eef3fc);
  color: var(--sd-accent-text, #2b4b86);
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}

.sd-memory-note {
  margin: 0 0 12px;
  color: var(--sd-muted, #5b6472);
  font-size: 13px;
}

.sd-memory-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0 0 14px;
  padding: 10px 12px;
  border: 1px solid var(--sd-accent-border, #b7c8e8);
  border-radius: 10px;
  background: var(--sd-accent-soft, #eef3fc);
}

.sd-memory-banner-text {
  color: var(--sd-accent-text, #2b4b86);
  font-size: 13px;
}

.sd-memory-details {
  flex: 1 1 100%;
}

.sd-memory-details > summary {
  cursor: pointer;
  color: var(--sd-accent-text, #2b4b86);
  font-size: 12px;
}

.sd-memory-refs {
  margin: 8px 0 0;
  padding-left: 18px;
  color: var(--sd-muted, #5b6472);
  font-size: 12px;
  line-height: 1.6;
}

.sd-memory-ref-stage {
  font-weight: 600;
  color: var(--sd-accent-text, #2b4b86);
}

/* --- 기록 관리 패널 --------------------------------------------------------- */

/* 바탕은 판 토큰(--sd-panel — 밝음 #fff 그대로 · 어두움 --ground). 예전 #fff 글자는 어두운 테마에서 흰 바탕에 흰 글자였다(uiux-261029-c 4부). */
.sd-memory-wrap {
  margin-top: 12px;
  border: 1px solid var(--sd-border, #dde2ea);
  border-radius: 10px;
  background: var(--sd-panel, #fff);
}

.sd-memory-wrap > summary {
  padding: 10px 12px;
  cursor: pointer;
  font-weight: 600;
  font-size: 13px;
}

.sd-memory-slot {
  padding: 0 12px 12px;
}

.sd-memory-header h3 {
  margin: 0 0 4px;
  font-size: 14px;
}

.sd-memory-tabs {
  display: flex;
  gap: 6px;
  margin: 10px 0;
  flex-wrap: wrap;
}

.sd-memory-tab {
  padding: 4px 10px;
  border: 1px solid var(--sd-border, #dde2ea);
  border-radius: 999px;
  background: var(--sd-panel, #fff);
  cursor: pointer;
  font-size: 12px;
}

.sd-memory-tab.is-active {
  border-color: var(--sd-accent-border, #b7c8e8);
  background: var(--sd-accent-soft, #eef3fc);
  color: var(--sd-accent-text, #2b4b86);
  font-weight: 600;
}

.sd-memory-tools {
  margin-bottom: 8px;
}

.sd-memory-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 8px;
}

.sd-memory-item {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  justify-content: space-between;
  padding: 8px 10px;
  border: 1px solid var(--sd-border, #dde2ea);
  border-radius: 8px;
}

/* 정지된 기록은 목록에 남지만 생성에 쓰이지 않는다. 흐리게 하되 읽을 수는 있어야
   한다 — 무엇을 정지했는지 확인하려고 여는 화면이다. */
.sd-memory-item.is-paused {
  background: var(--sd-surface-muted, #f7f8fa);
  color: var(--sd-muted, #5b6472);
}

.sd-memory-item-summary {
  margin: 0 0 4px;
  font-size: 13px;
  line-height: 1.5;
}

.sd-memory-item-meta {
  margin: 0;
  color: var(--sd-muted, #5b6472);
  font-size: 11px;
}

.sd-memory-paused {
  display: inline-block;
  margin-top: 4px;
  font-size: 11px;
  color: var(--sd-muted, #5b6472);
}

.sd-memory-item-actions {
  display: flex;
  gap: 4px;
  flex-shrink: 0;
}

/* --- 맵 창 머리의 글씨 크기 버튼 (이매지너스 도크와 같은 클래스) --- */
.sd-dock-font { display: inline-flex; align-items: center; gap: 4px; flex: none; }
.sd-dock-font-value { min-width: 2ch; text-align: center; font-size: 0.85em; color: var(--sd-dim); }

/* --- 시나리오 구조 맵 창 (story-dev/map.html, story-map-design.md) ---
   화면 전체를 쓴다: 머리 | 왼쪽 보기 필터 · 가운데 캔버스(+회차 슬라이더) · 오른쪽 선택·점검.
   글씨는 `--sd-map-font`("가− / 가+", 기본 16px)에 비례한다. 캔버스 안 글씨는 캔버스가 그린다. */
.sd-map-body { font-size: var(--sd-map-font, 16px); }
.sd-map-root { display: flex; flex-direction: column; height: 100vh; padding: 12px 16px; box-sizing: border-box; gap: 10px; }
.sd-map-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: var(--sd-map-font, 16px); }
.sd-map-title { font-size: 1.25em; margin: 0; }
.sd-map-head-spacer { flex: 1 1 auto; }
.sd-map-notice { font-size: calc(var(--sd-map-font, 16px) * 0.88); color: var(--sd-dim); }
.sd-map-notice p { margin: 0; }
.sd-map-empty { flex: 1; display: grid; place-items: center; color: var(--sd-dim); font-size: var(--sd-map-font, 16px); }
.sd-map-body {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr) clamp(300px, calc(var(--sd-map-font, 16px) * 22), 460px);
  gap: var(--sd-gap);
}
.sd-map-filters, .sd-map-side {
  background: var(--sd-surface);
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  padding: 12px;
  overflow-y: auto;
  min-height: 0;
}
.sd-map-side { display: flex; flex-direction: column; gap: 14px; }
.sd-map-filters .sd-panel-title, .sd-map-side .sd-panel-title { font-size: 1em; color: var(--sd-ink); }
.sd-map-kind { display: flex; align-items: center; gap: 8px; margin: 6px 0; cursor: pointer; }
.sd-map-derived { margin-top: 14px; }
.sd-map-dot { width: 12px; height: 12px; border-radius: 50%; flex: none; display: inline-block; }
.sd-map-center { display: flex; flex-direction: column; min-width: 0; min-height: 0; gap: 8px; }
.sd-map-canvas {
  flex: 1 1 auto;
  min-height: 320px;
  border: 1px solid var(--sd-line);
  border-radius: var(--sd-radius);
  background: var(--sd-surface);
  overflow: hidden;
}
.sd-map-episode { display: flex; align-items: center; gap: 10px; }
.sd-map-episode input[type="range"] { flex: 1 1 auto; }
.sd-map-episode-label { min-width: 12ch; font-weight: 600; }
.sd-map-counts { margin: 0; }
.sd-map-node-head { display: flex; align-items: center; gap: 8px; }
.sd-map-claim { background: var(--sd-bg); border-radius: 8px; padding: 6px 8px; }
.sd-map-sources { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0; }
.sd-map-relations { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.sd-map-relations li { border-bottom: 1px solid var(--sd-line); padding-bottom: 6px; line-height: 1.5; }
.sd-map-relations li.is-derived { color: var(--sd-dim); }
.sd-map-snippet { margin: 8px 0 0; padding: 8px 10px; border-left: 3px solid var(--sd-accent); background: var(--sd-bg); }
.sd-map-snippet p { margin: 4px 0 0; line-height: 1.6; }
.sd-map-seed-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.sd-map-seed {
  display: grid; gap: 4px; width: 100%; text-align: left; cursor: pointer;
  border: 1px solid var(--sd-line); border-radius: 8px; padding: 8px 10px;
  background: var(--sd-surface); font: inherit; color: inherit;
}
.sd-map-seed:hover { border-color: var(--sd-accent); }
.sd-map-seed .sd-badge { justify-self: start; }
.sd-map-seed-desc { font-size: 0.9em; color: var(--sd-dim); line-height: 1.5; }
/* 좁은 화면: 세로로 쌓는다. 캔버스는 높이를 따로 준다(부모가 자라지 않는다). */
@media (max-width: 900px) {
  .sd-map-root { height: auto; }
  .sd-map-body { grid-template-columns: 1fr; }
  .sd-map-canvas { min-height: 60vh; }
}

/* --- 구조 맵: 구체 자리 저장 (설계 2026-09-29) --- */
.sd-map-layout-bar { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sd-map-layout-bar:empty { display: none; }
.sd-map-layout-note { font-size: 0.85em; color: var(--sd-dim); white-space: nowrap; }
.sd-map-layout-note.is-dirty { color: var(--sd-warn); font-weight: 600; }

/* --- 구조 맵 창: 두 테마 · 좁은 화면 (story-map-synopsis 2026-10-27, design D10) ---
   어두운 테마에서 맵 창이 안 읽혔다. 원인 셋: ① `--sd-bg` 가 어디에도 정의되지 않은 `--bg` 를 봐 늘 밝은
   회색(그 위에 흰 글자), ② 주 버튼 배경이 `--accent → --invert-bg`(어두움 #fff)인데 글자는 #fff,
   ③ 토스트 · 처리 중 알약 배경이 `--sd-ink`(어두움 #fff)인데 글자는 #fff.
   앱 전체의 `--sd-bg` 를 바꾸면 story-dev 의 모든 hover · 배경이 함께 바뀌므로 범위를 맵 창 body
   (`.sd-map-page`)로 좁힌다. 밝은 테마는 값이 같아 모양이 바뀌지 않는다(주 버튼 = 지금과 같은 반전색).
   ③ 은 모든 화면의 같은 결함이라 091b 부터 토스트 절의 공통 규칙(같은 값)이 고친다 — 여기 있던 맵 창 규칙은 지웠다.
   그래프 판(vendor 캔버스)은 스스로 밝은 판을 칠한다 — 어두운 테마에서도 밝은 판으로 읽힌다(고치지 않음). */
.sd-map-page .sd-button-primary {
  background: var(--invert-bg, var(--sd-accent));
  border-color: var(--invert-bg, var(--sd-accent));
  color: var(--invert-ink, #fff);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .sd-map-page { --sd-bg: var(--ground, #000000); }
}
:root[data-theme="dark"] .sd-map-page { --sd-bg: var(--ground, #000000); }
/* 좁은 화면: 머리의 배치 안내 글('옮긴 구체 n개 — 아직 저장하지 않았습니다')이 한 줄 고정이라 화면 밖으로
   넘쳤다. 줄바꿈을 허용하고 바깥 여백을 줄인다. */
@media (max-width: 560px) {
  .sd-map-root { padding: 10px; }
  .sd-map-layout-note { white-space: normal; }
}

/* --- 세계관 (world-ontology 2026-10-29) ----------------------------------------
   회차별 줄거리 2차 화면의 '세계관으로 다음 화 정하기' 칸(story-dev/world.js)과 맵 창의 처음/지금 전환 · 반영 이력 · 묻기.
   색은 기존 토큰(--sd-surface · --sd-line · --sd-ink · --sd-dim · --sd-accent · --cv2-gold*)만 쓴다 — 두 테마가 저절로
   맞는다(.cv2 · .sd-page-project.is-v2 가 어두운 테마의 금색을 덮는다). 고른 카드는 금색 테두리, 반영 미리보기는 금색
   옅은 바탕. 움직임은 테두리 색 바뀜 하나뿐이고 reduced-motion 에서 끈다. */
.sd-world {
  display: grid; gap: 12px;
  border: 1px solid var(--sd-line); border-radius: var(--sd-radius);
  background: var(--sd-surface); padding: 14px 16px; margin: 4px 0 12px;
}
.sd-world-head { display: flex; align-items: flex-start; gap: 10px; flex-wrap: wrap; }
.sd-world-head-text { flex: 1 1 260px; min-width: 0; }
.sd-world-title { margin: 0; font-size: 16px; font-weight: 600; color: var(--sd-ink); }
.sd-world-meta { margin: 2px 0 0; }
/* 칸 머리 '세계관 점검 n건' 한 줄과 맵 창으로 가는 안내(world-edit 100b). 안내 버튼은 글 줄 안에서 이어진다. */
.sd-world-checks { margin: 2px 0 0; }
.sd-world-edit-link { display: inline; }
.sd-world-map-link { margin-left: auto; align-self: center; }
.sd-world-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.sd-world-block { display: grid; gap: 8px; }
.sd-world-sub { margin: 0; }
.sd-world-notice {
  border: 1px solid var(--sd-line); border-left: 3px solid var(--cv2-gold, #b3803f);
  border-radius: 8px; padding: 10px 12px; display: grid; gap: 8px;
}
.sd-world-notice p { margin: 0; line-height: 1.6; }
.sd-world-preview {
  border: 1px solid var(--cv2-gold, #b3803f); border-radius: 10px;
  background: var(--cv2-gold-soft, #f6ede1); padding: 12px 14px; display: grid; gap: 8px; color: var(--sd-ink);
}
.sd-world-pending-title { margin: 0; font-weight: 600; }
.sd-world-counts { margin: 0; font-weight: 600; color: var(--cv2-gold-ink, #85592a); }
.sd-world-notes, .sd-world-notices { margin: 0; padding-left: 18px; display: grid; gap: 4px; line-height: 1.55; }
.sd-world-notices { color: var(--sd-dim); font-size: 0.92em; }
.sd-world-seeds {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 8px; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}
.sd-world-seed {
  display: grid; gap: 4px; width: 100%; height: 100%; text-align: left; cursor: pointer;
  border: 1px solid var(--sd-line); border-radius: 10px; padding: 10px 12px;
  background: var(--sd-surface); color: inherit; font: inherit;
  transition: border-color .15s ease;
}
.sd-world-seed:hover:not([disabled]) { border-color: var(--cv2-gold, #b3803f); }
.sd-world-seed[disabled] { cursor: default; }
.sd-world-seed.is-picked { border: 2px solid var(--cv2-gold, #b3803f); padding: 9px 11px; }
.sd-world-seed.is-fresh { border-style: dashed; border-color: var(--cv2-gold, #b3803f); }
.sd-world-seed-top { display: flex; flex-wrap: wrap; gap: 6px; }
.sd-world-seed-label { justify-self: start; }
.sd-world-fresh { background: var(--cv2-gold-soft, #f6ede1); color: var(--cv2-gold-ink, #85592a); }
.sd-world-seed-title { font-size: 15px; color: var(--sd-ink); }
.sd-world-seed-why { color: var(--cv2-gold-ink, #85592a); font-size: 0.92em; }
.sd-world-seed-desc, .sd-world-seed-focus { color: var(--sd-dim); font-size: 0.9em; line-height: 1.5; }
.sd-world-free { width: 100%; box-sizing: border-box; resize: vertical; min-height: 52px; }
.sd-world-dirs { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.sd-world-dir {
  display: grid; gap: 6px; align-content: start; text-align: left; cursor: pointer;
  border: 1px solid var(--sd-line); border-radius: 10px; padding: 12px;
  background: var(--sd-surface); color: inherit; font: inherit;
  transition: border-color .15s ease;
}
.sd-world-dir:hover:not([disabled]) { border-color: var(--cv2-gold, #b3803f); }
.sd-world-dir.is-chosen { border: 2px solid var(--cv2-gold, #b3803f); padding: 11px; background: var(--cv2-gold-soft, #f6ede1); }
.sd-world-dir-head { display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline; }
.sd-world-dir-tone { color: var(--sd-dim); font-size: 0.9em; }
.sd-world-dir-premise { line-height: 1.6; }
.sd-world-dir-beats, .sd-world-dir-outcome { margin: 0; padding-left: 18px; display: grid; gap: 3px; line-height: 1.5; font-size: 0.94em; }
.sd-world-dir-change { font-size: 0.9em; }
.sd-world-dir-change-label { font-weight: 600; color: var(--cv2-gold-ink, #85592a); }
.sd-world-chosen { margin: 0; font-weight: 600; }
.sd-world-rounds ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.sd-world-rounds li { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; justify-content: space-between; }
.sd-world-rounds li.is-off { color: var(--sd-dim); text-decoration: line-through; }
.ds-ep-world { margin: 4px 0 0; }
@media (max-width: 560px) {
  .sd-world { padding: 12px; }
  .sd-world-seeds, .sd-world-dirs { grid-template-columns: 1fr; }
  .sd-world-map-link { margin-left: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .sd-world-seed, .sd-world-dir { transition: none; }
}

/* 맵 창 — 처음/지금 전환 · 반영 이력 · 묻기 */
.sd-map-view { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.sd-map-relations li.is-added { color: var(--sd-ink); font-weight: 600; }
.sd-map-seed-why { font-size: 0.9em; color: var(--cv2-gold-ink, #85592a); }
.sd-map-history-host:empty, .sd-map-ask:empty { display: none; }
.sd-map-history { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.sd-map-history-item { display: grid; gap: 4px; border-bottom: 1px solid var(--sd-line); padding-bottom: 8px; }
.sd-map-history-item.is-off { color: var(--sd-dim); }
.sd-map-history-lines { margin: 0; padding-left: 18px; display: grid; gap: 2px; font-size: 0.92em; line-height: 1.5; }
.sd-map-ask { display: grid; gap: 8px; }
.sd-map-ask-input { width: 100%; box-sizing: border-box; resize: vertical; min-height: 52px; }
.sd-map-ask .sd-button { justify-self: start; }
.sd-map-ask-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.sd-map-ask-answer { margin: 4px 0 0; line-height: 1.6; white-space: pre-wrap; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .sd-map-page { --cv2-gold: #c8975e; --cv2-gold-ink: #e2b47c; --cv2-gold-soft: #2a2117; }
}
:root[data-theme="dark"] .sd-map-page { --cv2-gold: #c8975e; --cv2-gold-ink: #e2b47c; --cv2-gold-soft: #2a2117; }

/* --- 맵 창: 세계관 직접 고치기 · 점검 (world-edit 100b, map/map.js) ---
   항목 상세의 '끊기' · '관계 더하기' · '설명 고치기' 칸, 오른쪽 '세계관 고치기'(모아 둔 고칠 것 · 미리보기 · 반영), '세계관 점검' 목록,
   머리의 '새 항목 더하기' · '세계관 만들기'. 색은 기존 토큰만(--sd-* · --cv2-gold*). 맵 창 어두운 테마의 금색은 바로 위 .sd-map-page 블록이
   정하고, 밝은 테마는 맵 창에 금색 정의가 없어 위 맵 창 규칙(.sd-map-seed-why)과 같이 .cv2 밝은 값을 대체값으로 둔다 — 새 색 값은 없다.
   미리보기는 회차별 줄거리 칸의 반영 미리보기(.sd-world-preview)와 같은 금색 옅은 바탕이다(같은 '세계관에 반영' 뜻). 움직임은 점검 줄
   테두리 색 하나뿐이고 reduced-motion 에서 끈다. */
.sd-map-edit-head { display: inline-flex; align-items: center; gap: 6px; }
.sd-map-edit-head:empty, .sd-map-edit:empty, .sd-map-checks:empty { display: none; }
.sd-map-edit, .sd-map-checks { display: grid; gap: 8px; }
.sd-map-edit-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.sd-map-edit-detail { display: grid; gap: 8px; margin-top: 10px; }
.sd-map-relations .sd-map-edit-cut { margin-left: 6px; }
.sd-map-edit-cut.is-on { color: var(--sd-warn); border-color: var(--sd-warn); }
.sd-map-form {
  display: grid; gap: 8px;
  border: 1px solid var(--sd-line); border-radius: 8px; padding: 10px 12px; background: var(--sd-surface);
}
.sd-map-form-row { display: grid; grid-template-columns: 5.5em minmax(0, 1fr); gap: 8px; align-items: center; }
.sd-map-form-label { font-size: 0.9em; color: var(--sd-dim); }
.sd-map-form-row .sd-input { width: 100%; box-sizing: border-box; min-width: 0; }
.sd-map-form-row textarea.sd-input { resize: vertical; min-height: 52px; }
.sd-map-form-sub, .sd-map-edit-sub { margin: 4px 0 0; font-weight: 600; font-size: 0.92em; }
.sd-map-edit-count { margin: 0; font-weight: 600; color: var(--sd-warn); }
.sd-map-edit-list { margin: 0; padding-left: 20px; display: grid; gap: 6px; line-height: 1.5; }
.sd-map-edit-list li > span { margin-right: 6px; }
/* 미리보기에서 빠지는 이유를 그 고칠 것 줄 아래에(world-sync 100e2) — 아래 '빠지는 것' 목록과 같은 색, 작게. */
.sd-map-edit-why { margin: 2px 0 0; color: var(--sd-danger); font-size: 0.88em; line-height: 1.5; }
.sd-map-edit-preview-box {
  display: grid; gap: 6px;
  border: 1px solid var(--cv2-gold, #b3803f); border-radius: 10px;
  background: var(--cv2-gold-soft, #f6ede1); padding: 10px 12px; color: var(--sd-ink);
}
.sd-map-edit-preview-title { margin: 0; font-weight: 600; }
.sd-map-edit-counts { margin: 0; font-weight: 600; color: var(--cv2-gold-ink, #85592a); }
.sd-map-edit-notes, .sd-map-edit-skipped, .sd-map-edit-notices { margin: 0; padding-left: 18px; display: grid; gap: 4px; line-height: 1.55; }
.sd-map-edit-skipped { color: var(--sd-danger); }
.sd-map-edit-notices { color: var(--sd-dim); font-size: 0.92em; }
.sd-map-check-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.sd-map-check {
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 8px; align-items: start;
  width: 100%; text-align: left; cursor: pointer; font: inherit; color: inherit;
  border: 1px solid var(--sd-line); border-radius: 8px; padding: 6px 8px; background: var(--sd-surface);
  transition: border-color .15s ease;
}
.sd-map-check:hover { border-color: var(--sd-accent); }
.sd-map-check-level.is-error { color: var(--sd-danger); background: var(--sd-danger-tint); }
.sd-map-check-level.is-warn { color: var(--sd-warn); background: var(--sd-warn-tint); }
.sd-map-check-level.is-info { color: var(--sd-dim); background: var(--sd-bg); }
.sd-map-check-text { line-height: 1.5; }
@media (max-width: 560px) {
  .sd-map-form-row { grid-template-columns: 1fr; gap: 4px; }
  .sd-map-edit-head { flex-basis: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .sd-map-check { transition: none; }
}

/* --- 처리 중 표시 (api.js `beginBusy`, 설계 2026-09-29) ---
   요청이 0.7초를 넘기면 화면 맨 위 움직이는 막대 + 아래쪽 알약("처리 중 · N초").
   클릭을 막지 않는다(pointer-events: none) — 기다리는 동안 다른 칸을 읽을 수 있어야 한다. */
.sd-busy { position: fixed; inset: 0 0 auto 0; z-index: 1000; pointer-events: none; }
.sd-busy[hidden] { display: none; }
.sd-busy-bar {
  position: fixed; top: 0; left: 0; right: 0; height: 4px; overflow: hidden;
  background: color-mix(in srgb, var(--sd-accent, #5234c1) 18%, transparent);
}
.sd-busy-bar::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: -40%; width: 40%;
  background: var(--sd-accent, #5234c1);
  animation: sd-busy-slide 1.1s ease-in-out infinite;
}
.sd-busy-pill {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%);
  display: inline-flex; align-items: center; gap: 10px;
  max-width: calc(100vw - 32px); padding: 10px 18px; border-radius: 999px;
  background: var(--sd-ink, #2c2c33); color: #fff; font-size: 15px; font-weight: 600;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.25);
}
.sd-busy-spin {
  width: 16px; height: 16px; flex: none; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.35); border-top-color: #fff;
  animation: sd-busy-spin 0.8s linear infinite;
}
@keyframes sd-busy-slide { 0% { left: -40%; } 100% { left: 100%; } }
@keyframes sd-busy-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .sd-busy-bar::after { animation: none; left: 0; width: 100%; opacity: 0.6; }
  .sd-busy-spin { animation-duration: 2.4s; }
}

/* --- 캐릭터 우선 흐름 (키이스트 개편, 2026-09-29) ---
   S1 인물 탭과 편집기 · 준비 중 화면. (S0 메모 띠 .sd-origin-band 는 머리의 한 줄과 겹쳐 지웠다 — uiux-261029-a 항목 9.) */
.sd-origin-notice { margin-bottom: 16px; }

.sd-cast-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; align-items: center; }
.sd-cast-tab {
  display: inline-flex; align-items: center; gap: 6px; padding: 8px 12px;
  border: 1px solid var(--sd-line); border-radius: 999px; background: var(--sd-surface);
  cursor: pointer; font: inherit; color: var(--sd-ink);
}
.sd-cast-tab.is-active { border-color: var(--sd-accent); box-shadow: 0 0 0 1px var(--sd-accent) inset; }
.sd-cast-tab-name { font-weight: 600; white-space: nowrap; }
.sd-cast-tab-lead { font-size: 11px; color: var(--sd-accent); font-weight: 700; }
.sd-cast-editor { padding: 18px; }
.sd-cast-editor-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-bottom: 8px; }
.sd-cast-editor-head .sd-section-title { margin: 0; }
.sd-cast-group {
  margin: 18px 0 8px; font-size: 13px; color: var(--sd-dim); text-transform: none;
  border-top: 1px solid var(--sd-line); padding-top: 12px;
}
.sd-cast-lead { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; cursor: pointer; }
/* 인물 유형(주인공 · 조력자 · 적대자) 체크를 세로로 */
.sd-cast-role-types { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
/* 주인공 후보 패널 */
.sd-cast-leads-question { margin: 0 0 4px; }
.sd-cast-leads-list { margin: 10px 0; padding-left: 20px; display: grid; gap: 10px; }
.sd-cast-lead-card { padding: 10px 12px; border: 1px solid var(--sd-line); border-radius: 8px; background: var(--sd-bg, transparent); }
.sd-cast-lead-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.sd-cast-lead-summary { margin: 6px 0 4px; }
.sd-cast-leads-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
/* 칸 아래 'AI 추천 받기' */
.sd-assist-compact { margin: 4px 0 14px; padding: 0; border: 0; background: none; }
.sd-assist-compact .sd-assist-actions { margin: 0; }
.sd-cast-birth > .sd-field { flex: 0 1 110px; }
.sd-cast-birth > .sd-field:first-child { flex: 1 1 200px; }
.sd-cast-birth-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.sd-cast-species.sd-notice { margin: 12px 0 0; }
.sd-cast-species-question { margin: 0 0 10px; }
.sd-cast-species-answers { display: flex; gap: 8px; }
.sd-cast-species .sd-field { margin-bottom: 8px; }
.sd-saju-stale .sd-saju { opacity: 0.45; }
.sd-saju-stale-note { color: var(--sd-warn); }
.sd-cast-samples { margin: 0 0 8px; padding-left: 18px; }
.sd-cast-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 18px; }
.sd-cast-actions .sd-actions-hint { flex-basis: 100%; }
.sd-cast-removed { margin-top: 20px; }
.sd-cast-removed > summary { cursor: pointer; color: var(--sd-dim); }
.sd-cast-removed-item { margin: 10px 0; }

/* --- AI 비서 (assist.js) · 넣어 만들어보기 · 사주 · 내 취향 분석 (2026-09-29, 이름 통일 2026-10-22) --- */
.sd-assist {
  border: 1px solid var(--sd-line); border-radius: var(--sd-radius); padding: 12px 14px;
  margin: 12px 0 8px; background: color-mix(in srgb, var(--sd-accent) 4%, var(--sd-surface));
}
.sd-assist-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
.sd-assist-title { margin: 0; font-size: 14px; }
.sd-assist-actions { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 8px; }
.sd-assist-actions .sd-button { white-space: nowrap; }
.sd-assist-options { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.sd-assist-instruction { flex: 1 1 260px; }
/* 'AI 비서' 하나로 합치기(설계자 결정 2026-10-22): 화면에 AI 비서 입력줄(.sd-agent-composer)이나
   cast 2차 도크(.cv2-dock)가 있으면, 덧붙일 말 칸(.sd-assist-instruction)을 숨긴다. 글로 지시하는
   입력은 그 한 줄로 모은다. DOM 에는 남겨 두어, 입력줄이 문장을 이 칸에 넣고 task 버튼을 눌러 실행하는
   기존 경로(runAssist)가 그대로 동작한다. 입력줄이 없는 화면에서는 이 칸을 그대로 보인다(기능 유지). */
.sd-page-project.is-v2:has(.sd-agent-composer) .sd-assist-instruction,
.sd-page-project.is-v2:has(.cv2-dock) .sd-assist-instruction {
  display: none;
}
/* 전체 줄거리 · 회차별 줄거리 · 대본의 '수정지시' 입력칸(.sd-rewrite-input)도 같은 이유로 숨긴다
   (수정 5). 이 칸은 AI 비서 줄의 "○○ 다시 써줘"(action regenerate + instruction)가 값을 대신
   넣어 생성 요청에 쓰이므로(각 단계 onAction → instructionInput.value = note → run), 같은 일을
   하는 두 번째 입력이다(설계자 결정: AI 비서 하나). DOM 에는 남긴다 — 기존 코드가 값을 읽는다.
   AI 비서 줄(.sd-agent-composer)이 있을 때만 숨긴다(줄이 없으면 이 칸이 유일한 지시 입력이라 보인다). */
.sd-page-project.is-v2:has(.sd-agent-composer) .sd-rewrite-input {
  display: none;
}
.sd-assist-memory { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; cursor: pointer; }
.sd-assist-record { border-top: 1px dashed var(--sd-line); margin-top: 10px; padding-top: 10px; }
.sd-assist-record.is-fresh { border-top-style: solid; }
.sd-assist-record-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.sd-assist-comment { background: var(--sd-surface); border-left: 3px solid var(--sd-accent); padding: 8px 10px; margin: 8px 0; }
.sd-assist-items { margin: 8px 0 0; padding-left: 20px; display: flex; flex-direction: column; gap: 10px; }
.sd-assist-item-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.sd-assist-field { font-size: 12px; font-weight: 700; color: var(--sd-accent); }
.sd-assist-text { margin: 4px 0; white-space: pre-wrap; line-height: 1.6; }
.sd-assist-item.is-applied .sd-assist-text { opacity: 0.75; }
.sd-assist-history { margin-top: 10px; }
.sd-assist-history > summary { cursor: pointer; font-size: 13px; color: var(--sd-dim); }

.sd-saju { margin: 8px 0 4px; padding: 10px 12px; border: 1px solid var(--sd-line); border-radius: var(--sd-radius); }
.sd-saju-pillars { display: flex; flex-wrap: wrap; gap: 8px; }
.sd-saju-pillar {
  display: flex; flex-direction: column; align-items: center; gap: 2px; min-width: 76px;
  padding: 6px 8px; border-radius: 8px; background: var(--sd-bg);
}
.sd-saju-name { font-size: 11px; color: var(--sd-dim); }
.sd-saju-hanja { font-size: 13px; }
.sd-saju-line { margin: 8px 0 2px; font-size: 13px; }

.sd-taste { font-size: 13px; }
.sd-taste-total { margin: 4px 0 8px; font-weight: 600; }
.sd-taste-row { display: flex; gap: 8px; align-items: baseline; margin: 4px 0; }
.sd-taste-kind { flex: none; width: 52px; color: var(--sd-dim); font-size: 12px; }
.sd-taste-values { display: flex; flex-wrap: wrap; gap: 4px; }
.sd-taste-chip { border: 1px solid var(--sd-line); border-radius: 999px; padding: 1px 8px; font-size: 12px; }
.sd-taste-recent { margin: 8px 0; }
.sd-taste-recent > summary { cursor: pointer; color: var(--sd-dim); }
.sd-taste-recent ul, .sd-taste-list { padding-left: 18px; margin: 6px 0; }
.sd-taste-summary { border-top: 1px solid var(--sd-line); margin-top: 8px; padding-top: 8px; }
.sd-taste-summary-text { margin: 0 0 6px; line-height: 1.6; }
.sd-assist-lead { margin: 8px 0 4px; font-weight: 600; color: var(--sd-accent); }

/* S2 캐릭터 검증 · S3 스토리 설정 (키이스트 개편, 2026-09-30) */
.sd-verify-cast { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin: 8px 0 12px; }
.sd-verify-section, .sd-story-form { position: static; margin-bottom: 16px; }
.sd-verify-situation { border-top: 1px solid var(--sd-line); padding: 12px 0; }
.sd-verify-situation-head { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.sd-verify-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; margin-top: 10px; }
.sd-verify-col { border: 1px solid var(--sd-line); border-radius: 8px; padding: 8px 10px; }
.sd-verify-col h4 { margin: 0 0 6px; font-size: 14px; }
.sd-verify-col ul { margin: 0; padding-left: 18px; line-height: 1.6; }
.sd-verify-similar { margin-top: 10px; padding: 8px 10px; border-left: 3px solid var(--sd-warn); background: var(--sd-warn-soft); }
.sd-verify-similar p, .sd-verify-similar ul { margin: 6px 0 0; }
.sd-verify-progress { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 8px; }
.sd-verify-add { display: flex; gap: 8px; margin-top: 12px; }
.sd-verify-add .sd-input { flex: 1; min-width: 0; }
.sd-verify-blind-head { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.sd-verify-blind { padding-left: 0; list-style: none; margin: 10px 0; }
.sd-verify-blind-line { display: grid; grid-template-columns: 28px 1fr 160px auto; gap: 8px; align-items: center; padding: 6px 0; border-bottom: 1px solid var(--sd-line); }
.sd-verify-blind-line.is-match .sd-verify-blind-text { color: var(--sd-success); }
.sd-verify-blind-line.is-miss .sd-verify-blind-text { color: var(--sd-danger); }
.sd-verify-blind-no { color: var(--sd-dim); text-align: right; }
.sd-story-chip-group { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 4px 0; }
@media (max-width: 640px) {
  .sd-verify-blind-line { grid-template-columns: 24px 1fr; }
  .sd-verify-blind-line select, .sd-verify-blind-line .sd-field-hint { grid-column: 2; }
}

/* S4 목표 · 동기 · S5 수정 · 확정 (키이스트 개편, 2026-09-30) */
.sd-motive-story, .sd-motive-editor, .sd-final-section { position: static; margin-bottom: 16px; }
.sd-motive-story summary { cursor: pointer; font-weight: 600; }
.sd-motive-rows { display: grid; grid-template-columns: 120px 1fr; gap: 4px 12px; margin: 8px 0; }
.sd-motive-rows dt { color: var(--sd-dim); }
.sd-motive-rows dd { margin: 0; white-space: pre-wrap; }
.sd-motive-profile summary { cursor: pointer; color: var(--sd-dim); }
.sd-motive-link { display: grid; grid-template-columns: 24px 1fr auto; gap: 8px; align-items: start; margin: 8px 0; }
.sd-motive-link-body { display: flex; flex-direction: column; gap: 4px; }
.sd-motive-no { color: var(--sd-dim); text-align: right; padding-top: 8px; }
.sd-final-table { width: 100%; border-collapse: collapse; margin: 8px 0 12px; }
.sd-final-table th, .sd-final-table td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--sd-line); }
/* 100u: 다듬기 표 마지막 칸의 '확정' · '고치기' 두 버튼 사이 */
.sd-final-table td > .sd-button + .sd-button { margin-left: 6px; }
.sd-final-done { color: var(--sd-success); font-weight: 600; }
.sd-version-change { margin: 2px 0 0; }
.sd-assist-regen { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin: 4px 0 8px; }
.sd-assist-regen-pick { width: auto; }
@media (max-width: 640px) {
  .sd-motive-rows { grid-template-columns: 1fr; }
  .sd-final-table th:nth-child(3), .sd-final-table td:nth-child(3) { display: none; }
}

/* 사용자 맞춤 1안 (키이스트 개편, 2026-09-30) */
.sd-assist-personal { margin-top: 12px; padding: 10px 12px; border: 1px dashed var(--sd-accent); border-radius: 8px; }
.sd-assist-personal-lead { margin-top: 0; }
.sd-assist-item.is-personal { background: rgba(82, 52, 193, 0.04); }

/* 공유 · 팀 코멘트 · AI 코멘트 이력 · 이름 반영 (키이스트 개편, 2026-09-30) */
.sd-team-me { margin: 4px 0 8px; }
.sd-team-list { list-style: none; padding: 0; margin: 6px 0; }
.sd-team-list li { display: flex; gap: 8px; align-items: center; justify-content: space-between; padding: 4px 0; }
.sd-team-add { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 6px; }
.sd-team-add .sd-textarea { flex: 1 1 100%; }
.sd-team-note { border-top: 1px solid var(--sd-line); padding: 6px 0; }
.sd-team-note p { margin: 2px 0; white-space: pre-wrap; }
.sd-comment-history { margin-top: 10px; }
.sd-comment-past { border-top: 1px solid var(--sd-line); padding-top: 6px; margin-top: 6px; }
.sd-rename { border: 1px solid var(--sd-warn); background: var(--sd-warn-soft); border-radius: 8px; padding: 8px 12px; margin-bottom: 10px; }
.sd-rename p { margin: 0 0 6px; }


/* --- 혼합 UI: 다음 할 일 칩 + 대화 입력줄 (키이스트 UI 개선 B-3·B-4, 2026-10-21) ---
   기존 색 토큰·Pretendard 를 그대로 쓴다(이 파일 머리 주석). 기존 버튼·입력은 두고
   이 영역을 "얹는다"(D-K09). 모바일에서 깨지지 않게 flex-wrap 으로 흐른다. */

/* 칩 영역 고정 host(S6·S7). 잡 상태가 바뀌면 이 안만 다시 그린다 — 입력줄은 건드리지 않는다.
   비어 있으면(칩이 없거나 잡 진행 중) 아무것도 차지하지 않는다. */
.sd-agent-chips-host:empty { display: none; }

/* 다음 할 일 칩(본문 위) */
.sd-suggest {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px;
  margin: 0 0 14px;
}
.sd-suggest-label { font-size: 12px; font-weight: 700; color: var(--sd-dim); }
.sd-suggest-chips { gap: 6px; }
/* 차감되는 칩은 강조선으로 "누르면 한도가 준다" 를 미리 알린다(D-K16). */
.sd-suggest-chip { white-space: nowrap; }
.sd-suggest-chip.is-charged {
  border-color: var(--sd-accent);
  color: var(--sd-accent);
  font-weight: 600;
}
.sd-suggest-chip:hover:not(:disabled) { background: var(--sd-bg); }

/* 대화 입력줄(본문 아래) */
.sd-agent-composer { margin-top: 16px; }
.sd-composer {
  border-top: 1px solid var(--sd-line);
  padding-top: 12px;
}
/* 실행 중 한 줄 상태(끝나면 hidden 으로 사라진다 — 요구 4) */
.sd-composer-status {
  font-size: 13px; color: var(--sd-accent); font-weight: 600;
  margin-bottom: 8px;
}
.sd-composer-status[hidden] { display: none; }
.sd-composer-row {
  display: flex; gap: 8px; align-items: flex-end;
}
/* 입력줄 왼쪽 'AI 비서' 표시 — 이 줄이 AI 비서에게 말하는 곳임을 알린다(2026-10-22). */
.sd-composer-badge {
  flex: 0 0 auto; align-self: stretch; display: inline-flex; align-items: center;
  padding: 0 10px; border-radius: 10px; white-space: nowrap;
  font-size: 12px; font-weight: 700;
  color: var(--sd-accent);
  background: color-mix(in srgb, var(--sd-accent) 10%, var(--sd-surface));
}
.sd-composer-input {
  flex: 1 1 auto; min-width: 0;
  min-height: 42px; max-height: 160px;
  resize: none;
}
.sd-composer-send { flex: 0 0 auto; white-space: nowrap; align-self: stretch; }

@media (max-width: 640px) {
  /* 좁은 화면에서는 버튼을 아래로 내려 입력칸을 폭 전체로 쓴다. */
  .sd-composer-row { flex-direction: column; align-items: stretch; }
  .sd-composer-send { align-self: flex-end; }
  /* 좁은 화면에서 'AI 비서' 표시는 왼쪽 위로 붙인다. */
  .sd-composer-badge { align-self: flex-start; }
}


/* --- "더보기 ⋯" 메뉴 (more-menu.js, 키이스트 UI 개편 2026-10-21) ---

   화면에 보이는 버튼 수를 줄이려고 보조 동작을 이 메뉴로 모은다. cast.js(S4 캐릭터)와
   S4~S7 단계가 함께 쓴다. 기존 색 토큰·Pretendard 를 그대로 쓴다(이 파일 머리 주석).
   트리거는 평범한 sd-button 이라 버튼 색은 따로 주지 않는다 — 목록만 띄운다. */
.sd-more { position: relative; display: inline-block; }

/* 목록. 트리거 바로 아래에 띄운다. hidden 이면 아무것도 보이지 않는다. */
.sd-more-list {
  position: absolute;
  z-index: 40;
  top: calc(100% + 4px);
  left: 0;
  min-width: 200px;
  max-width: 300px;
  padding: 6px;
  background: var(--sd-surface, #ffffff);
  border: 1px solid var(--sd-line);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.sd-more-list[hidden] { display: none; }
/* 오른쪽 맞춤(align: "end"). 화면 오른쪽에 있는 트리거가 목록을 밖으로 밀어내지 않게. */
.sd-more-list-end { left: auto; right: 0; }

/* --------------------------------------------------------------------------
   D3 화려함(계획서 2026-10-26, 지시서 057) — 메뉴 열림 스프링 + 항목 40ms 간격 등장

   more-menu.js 는 목록을 `hidden` 속성으로 여닫는다(열면 hidden 제거). 그래서 열리는 순간
   (:not([hidden])) 애니메이션이 재생된다. 목록 전체는 위에서 스프링처럼 살짝 되튀며 펴지고
   (520/34 느낌을 overshoot 큐빅으로 근사 — 라이브러리 없이), 항목은 40ms 간격으로 하나씩
   내려온다. nth-child 로 지연을 줘 JS 를 건드리지 않는다(이 메뉴는 앱 전역이 쓴다 — CSS 로만).
   reduced-motion 이면 아래에서 끈다. */
.sd-more-list:not([hidden]) {
  transform-origin: top;
  animation: cv2-menu-open .34s cubic-bezier(0.22, 1.12, 0.36, 1);
}
@keyframes cv2-menu-open {
  from { transform: translateY(-6px) scaleY(0.96); opacity: 0; }
  to   { transform: translateY(0) scaleY(1);        opacity: 1; }
}
/* 항목 등장 — 40ms 간격(최대 8개까지 늘려 둔다. 그 뒤는 마지막 지연을 공유한다). */
.sd-more-list:not([hidden]) > .sd-more-item {
  animation: cv2-menu-item .26s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.sd-more-list:not([hidden]) > :nth-child(1) { animation-delay: .04s; }
.sd-more-list:not([hidden]) > :nth-child(2) { animation-delay: .08s; }
.sd-more-list:not([hidden]) > :nth-child(3) { animation-delay: .12s; }
.sd-more-list:not([hidden]) > :nth-child(4) { animation-delay: .16s; }
.sd-more-list:not([hidden]) > :nth-child(5) { animation-delay: .20s; }
.sd-more-list:not([hidden]) > :nth-child(6) { animation-delay: .24s; }
.sd-more-list:not([hidden]) > :nth-child(7) { animation-delay: .28s; }
.sd-more-list:not([hidden]) > :nth-child(n+8) { animation-delay: .32s; }
@keyframes cv2-menu-item {
  from { transform: translateY(-6px); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .sd-more-list:not([hidden]),
  .sd-more-list:not([hidden]) > .sd-more-item {
    animation: none;
  }
}

/* 항목. 목록 안에서는 한 줄짜리 글자 버튼이다 — 테두리 없이 행처럼 보이게. */
.sd-more-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 8px 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--sd-ink, inherit);
  font: inherit;
  cursor: pointer;
  white-space: normal;
}
.sd-more-item:hover:not(:disabled),
.sd-more-item:focus-visible { background: var(--sd-bg); outline: none; }
.sd-more-item:disabled { opacity: 0.5; cursor: default; }
/* 위험 동작(삭제 등)은 글자색으로 구분한다(sd-button-danger 와 같은 빨강). 구분선 아래에 모인다. */
.sd-more-item-danger { color: var(--sd-danger); }
.sd-more-item-danger:hover:not(:disabled),
.sd-more-item-danger:focus-visible { background: var(--sd-danger-soft); }

/* 위험 묶음 앞 구분선. */
.sd-more-sep { height: 1px; margin: 4px 2px; background: var(--sd-line); }

/* '…' 메뉴 안 "AI 속도" 세그먼트 규칙은 지웠다 — 속도는 단계 머리의 속도 바(.cv2-speed)로 옮겼다
   (speed-bar-admin-models 2026-10-29). 규칙을 남기면 다시 그리는 코드가 생겨 두 군데서 고치게 된다. */



/* ==========================================================================
   캐릭터 설정 2차 화면 (cast-v2.js, 키이스트 2차 개선안 2026-10-22)

   비IT 사용자용 "문서처럼 읽히는" 화면이다. 기존 `--sd-*` 토큰과 Pretendard 를 그대로
   쓰고(이 파일 머리 주석), 지시서의 금색 강조만 `--cv2-gold*` 로 이 범위 안에서 정의한다
   (플랫폼 styles.css 에 아직 --gold 토큰이 없어서 — guide-v1 2장 미적용). 두 테마는
   prefers-color-scheme 로 값을 바꾼다. 범위를 `.cv2` 로 좁혀 기존 화면에 새지 않게 한다.
   ========================================================================== */

.cv2 {
  /* 금색 강조(데이터 · 진행 표시에만). 밝음 기본값. */
  --cv2-gold: #b3803f;
  --cv2-gold-ink: #85592a;
  --cv2-gold-soft: #f6ede1;
  --cv2-radius: 16px;
  /* 하단 도크가 겹치지 않게 아래 여백을 둔다. 대화 기록이 펼쳐지면 도크가 커지므로
     doc-stage.syncDockHeight 가 --cv2-dock-h 로 실제 도크 높이를 담는다(없으면 96px). */
  padding-bottom: var(--cv2-dock-h, 96px);
}

/* is-v2 페이지 전체에 같은 금색 토큰을 둔다 — 공통 머리 · 진행 막대 · 하단 도크 · 주 버튼은
   `.cv2` 바깥(페이지 직속)에 있어도 같은 금색을 쓰기 때문이다(변수는 자손에게 상속된다). */
.sd-page-project.is-v2 {
  --cv2-gold: #b3803f;
  --cv2-gold-ink: #85592a;
  --cv2-gold-soft: #f6ede1;
  --cv2-radius: 16px;
}
/* cast 아닌 단계는 본문 아래에 fixed 도크가 겹친다. 본문 카드(.cv2-generic)가 .cv2 라서
   padding-bottom:96px 로 이미 도크 높이만큼 여백을 둔다(내용이 가려지지 않게). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .sd-page-project.is-v2 {
    --cv2-gold: #c8975e;
    --cv2-gold-ink: #e2b47c;
    --cv2-gold-soft: #2a2117;
  }
}
:root[data-theme="dark"] .sd-page-project.is-v2 {
  --cv2-gold: #c8975e;
  --cv2-gold-ink: #e2b47c;
  --cv2-gold-soft: #2a2117;
}

/* 2차 화면에서는 왼쪽 스테퍼를 숨긴다(진행 막대가 대신한다). workspace 가 이 클래스를 붙인다. */
.sd-panel-stepper.is-hidden { display: none; }

/* --------------------------------------------------------------------------
   2차 화면 배치 — 왼쪽 패널 · 기존 프로젝트 머리를 숨기고 본문을 폭 전체로 (지시서 1·2)

   workspace 가 **모든 단계**에 `.sd-page-project.is-v2` 를 붙인다(cast 2차는 `is-cast-v2` 도
   함께 붙는다). is-v2 면 공통 틀(머리 · 진행 막대 · 도크)이 옛 머리·스테퍼를 대신하므로 옛
   머리·왼쪽 패널을 숨긴다. 왼쪽 패널 DOM 은 남겨 두고 숨기기만 한다 — '…' 메뉴 서랍이 그
   노드를 옮겨 쓰기 때문이다(stage-shell.js openInDrawer). 전문가 보기(cast.js)도 is-v2 라
   공통 틀 안에 들어간다.
   -------------------------------------------------------------------------- */
/* 기존 프로젝트 머리(← 목록 · 제목 · 변경 이력)를 숨긴다 — 공통 머리와 겹친다.
   '← 목록'은 공통 머리 안의 .cv2-back 링크로, '변경 이력'은 '…' 메뉴로 대신한다. */
.sd-page-project.is-v2 > .sd-page-head { display: none; }
/* 본문 2단 레이아웃을 단으로 바꿔 본문이 폭을 다 쓰게 한다(cast 2차의 인물+질문 2열은 .cv2-body 가 만든다). */
.sd-page-project.is-v2 .sd-layout { grid-template-columns: minmax(0, 1fr); }
/* 왼쪽 패널 자체를 숨긴다(DOM 은 남아 서랍이 옮겨 쓴다). */
.sd-page-project.is-v2 .sd-panel-left { display: none; }

/* --- 오른쪽 서랍(…메뉴로 연 기존 왼쪽 패널) --- */
.cv2-drawer-overlay {
  position: fixed; inset: 0; z-index: 40;
  display: flex; justify-content: flex-end;
  background: rgba(20, 20, 24, 0.45);
  animation: cv2-overlay-in .2s ease;
}
.cv2-drawer {
  width: min(420px, 92vw); height: 100%;
  box-sizing: border-box;
  display: flex; flex-direction: column; gap: 12px;
  padding: 20px;
  background: var(--sd-surface); color: var(--sd-ink);
  border-left: 1px solid var(--sd-line);
  box-shadow: -12px 0 32px rgba(0, 0, 0, 0.18);
  overflow-y: auto;
  /* D3 화려함(계획서 2026-10-26, 지시서 057): 열림을 스프링 520/34 느낌으로. 라이브러리 없이 CSS 라
     스프링 방정식 대신 살짝 되튀는 overshoot 큐빅 베지어로 근사한다(049 motion.js 가 못 닿는 CSS 열림).
     오른쪽에서 밀려 들어오며 끝에서 아주 조금 넘쳤다 제자리. reduced-motion 이면 아래에서 끈다. */
  animation: cv2-drawer-in .42s cubic-bezier(0.22, 1.12, 0.36, 1);
}
.cv2-drawer-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding-bottom: 10px; border-bottom: 1px solid var(--sd-line);
}
.cv2-drawer-title { margin: 0; font-size: 16px; font-weight: 600; }
.cv2-drawer-close { font-size: 14px; }
/* 서랍 안에서는 옮겨 온 패널(`<details>`)을 펼친 채로 꽉 차게 보인다. */
.cv2-drawer-body .sd-memory-wrap { margin-top: 0; border: 0; }
.cv2-drawer-body .sd-memory-wrap > summary { display: none; }
.cv2-drawer-body .sd-memory-slot { padding: 0; }
/* D3 "항목 40ms 간격 등장": 서랍의 두 덩이(머리 · 본문)가 차례로 뜬다. 서랍이 옮겨 오는 내용은
   한 패널(details)이라 자식 수가 정해져 있지 않으므로, 머리와 본문 두 덩이에만 지연을 줘 과하지
   않게 한다(y 8→0 페이드). 서랍 열림 애니메이션과 같은 큐빅. */
.cv2-drawer-head { animation: cv2-drawer-item .3s cubic-bezier(0.22, 1, 0.36, 1) .06s both; }
.cv2-drawer-body { animation: cv2-drawer-item .3s cubic-bezier(0.22, 1, 0.36, 1) .1s both; }
@keyframes cv2-drawer-in {
  from { transform: translateX(24px); opacity: 0; }
  to   { transform: translateX(0);    opacity: 1; }
}
@keyframes cv2-drawer-item {
  from { transform: translateY(8px); opacity: 0; }
  to   { transform: translateY(0);   opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .cv2-drawer-overlay, .cv2-drawer,
  .cv2-drawer-head, .cv2-drawer-body { animation: none; }
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .cv2 {
    --cv2-gold: #c8975e;
    --cv2-gold-ink: #e2b47c;
    --cv2-gold-soft: #2a2117;
  }
}
:root[data-theme="dark"] .cv2 {
  --cv2-gold: #c8975e;
  --cv2-gold-ink: #e2b47c;
  --cv2-gold-soft: #2a2117;
}

/* --- 머리 --- */
/* D0(화면 튐 제거): 긴 화면에서 머리(제목·한 줄 이야기)와 진행 막대가 따라오게 sticky 로 붙인다.
   머리는 앱 상단바 바로 아래(top:var(--cv2-topbar)), 진행 막대는 그 바로 아래
   (top: 상단바 + 머리 높이)에 걸려 두 줄이 한 덩어리처럼 고정된다. 스크롤해도 본문만 그 아래로
   지나간다. 배경을 surface 로 채워 본문 글자가 비쳐 겹치지 않게 하고, 좌우로 조금 넘겨
   (margin/padding) 카드 모서리까지 덮는다.
   sticky 는 애니메이션이 없어 prefers-reduced-motion 과 무관하게 같은 결과다(모션 없음).

   ## 상단바 오프셋(--cv2-topbar) — 왜 넣었나 (리뷰어 실측 2026-10-26)
   2차 화면이 메인 앱 안(.content, 상단바 .topbar=sticky top:0 z20 높이 65px)에서 열리면,
   머리가 top:0 이라 상단바(z20) 밑에 **가려졌다**. 그래서 머리 top 을 상단바 높이만큼 내린다.
   상단바는 styles.css 에만 있고 CSS 변수(--topbar)가 없으므로 실측값을 변수로 둔다:
     - 메인 앱 안: 65px (좁은 화면 ≤820px 에서 상단바가 58px 로 줄어 58px).
     - story-dev 단독 페이지(body.sd-body, 상단바 없음): 0 — 아래 .sd-body 에서 0 으로 덮는다.
   값이 바뀌어도 머리·진행 막대·질문 카드가 모두 이 한 변수를 읽어 함께 따라간다. */
.cv2 {
  /* 상단바 높이(실측). 단독 페이지에선 .sd-body 가 0 으로 덮는다. */
  --cv2-topbar: 65px;
  /* 진행 막대가 걸릴 높이 = 머리 '실제 높이'. 071 후속(2026-10-26): stage-shell.js 의
     observeHeadHeight() 가 머리(.cv2-head)를 그린 뒤 ResizeObserver 로 실제 offsetHeight 를 재서
     이 변수(.cv2 루트)에 px 로 덮어쓴다. 메모지 띠 유무 · 한 줄 이야기 줄 수 · 스크롤 축소 · 창 크기에
     따라 머리 높이가 달라지므로, 고정값으로는 진행 막대가 머리 바로 밑에 붙지 못하고 본문 위에 떠서
     글자와 겹쳤다. 그래서 JS 측정값이 '정답'이고, 아래 156px 는 **초기값(대체값)** 일 뿐이다 —
     JS 가 측정하기 전(첫 프레임)이나 ResizeObserver 미지원 환경에서만 쓰인다. 초기값은 제목 + 한 줄
     이야기 한 줄 + 설정 메모지 띠(최대 2줄 ≈ 52px)를 더한 대략값이다. */
  --cv2-sticky-head: 156px;
}
/* story-dev 단독 페이지(body.sd-body)는 상단바가 없다 — 오프셋을 0 으로. 머리가 맨 위에 붙는다. */
.sd-body .cv2 { --cv2-topbar: 0px; }
/* 좁은 화면(≤820px): 메인 앱 상단바가 58px 로 줄어든다(styles.css). 단독 페이지(.sd-body)는 위에서
   0 으로 덮였으므로 영향 없다. */
@media (max-width: 820px) {
  .cv2 { --cv2-topbar: 58px; }
  .sd-body .cv2 { --cv2-topbar: 0px; }
}
/* 출발점(origin)은 머리에 한 줄 이야기 줄이 없어(본문 문단이 로그라인이라) 초기값이 더 낮다. 아래 124px
   도 **초기값(대체값)** 이다 — JS(observeHeadHeight)가 실제 높이를 재면 그 값으로 덮인다. 측정 전·미지원
   에서만 쓰이며, 제목 + 메모지 띠 대략값이다. */
.is-origin-v2 { --cv2-sticky-head: 124px; }
.cv2-head {
  margin-bottom: 16px;
  /* 상단바 바로 아래에 붙는다(top:var(--cv2-topbar)). 단독 페이지(.sd-body)는 변수가 0 이라 맨 위.
     z-index 12 로 본문·진행 막대보다 위, 앱 상단바(z20)보다 아래 — 상단바가 머리를 덮지 않게
     머리를 그 아래로 내렸으므로 겹치지 않는다. */
  position: sticky; top: var(--cv2-topbar, 0px); z-index: 12;
  /* 배경은 **불투명** surface 로 채운다(두 테마 모두 불투명: 밝음 #f5f5f4 · 어두움 #111111 — styles.css).
     스크롤 중 본문 글자(버튼)가 머리 뒤로 비치지 않게 하려면 불투명이어야 한다. 좌우는 아래 margin 음수로
     카드 모서리(.sd-stage padding:24px)까지 덮는다(071 과 같은 확장). */
  background: var(--sd-surface);
  /* 카드 안쪽 여백(.sd-stage padding:24px)을 덮어 본문이 머리 옆으로 비치지 않게 좌우로 넘긴다. */
  margin-left: -24px; margin-right: -24px;
  padding: 10px 24px 8px;
}
.cv2-head-top {
  display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap;
}
/* 머리 왼쪽: '← 목록' 링크 위에 제목. 링크는 작은 글씨로 제목 위에 둔다(그림 머리의 이동 경로). */
.cv2-head-left { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.cv2-back { font-size: 13px; align-self: flex-start; }
.cv2-title {
  font-size: 22px; font-weight: 600; margin: 0;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.cv2-step-pill {
  font-size: 13px; font-weight: 600;
  padding: 3px 10px; border-radius: 999px;
  background: var(--cv2-gold-soft); color: var(--cv2-gold-ink);
}
.cv2-head-right {
  margin-left: auto;
  display: flex; align-items: center; gap: 10px;
}
.cv2-save { font-size: 13px; min-width: 1px; }
.cv2-save.cv2-saved { color: var(--cv2-gold-ink); font-weight: 600; }
.cv2-save.cv2-saving { color: var(--sd-dim); }
.cv2-save.cv2-save-error { color: var(--sd-danger); font-weight: 600; }

/* --- 머리 AI 속도 바 · 기록 보기 (stage-shell.speedBar · recordsMenu, speed-bar-admin-models 2026-10-29) ---
   설계자 지시: "AI 속도는 바 형식으로 상단에 늘 보이게 · 기록 보기는 바 옆 같은 줄 오른쪽". 머리(.cv2-head)가
   sticky 라 스크롤해도 보인다. 색은 기존 토큰만(--sd-* · --cv2-gold*) — 두 테마가 토큰으로 바뀐다. 고른 칸은
   진행 막대와 같은 금색 계열이되 **옅은 금색 바탕 + 진한 금색 글자**로 둔다(흰 글자 + 금색 바탕은 어두운 테마에서
   대비가 낮다 — 밝음 #85592a/#f6ede1 · 어두움 #e2b47c/#2a2117 모두 4.5:1 이상). */
.cv2-speed { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.cv2-speed-label { font-size: 13px; font-weight: 600; color: var(--sd-dim); }
.cv2-speed-seg {
  display: inline-flex; gap: 2px; padding: 2px;
  border: 1px solid var(--sd-line); border-radius: 999px;
  background: var(--sd-surface);
}
.cv2-speed-btn {
  border: 1px solid transparent; border-radius: 999px;
  background: transparent; color: var(--sd-ink);
  font: inherit; font-size: 13px; line-height: 1.4;
  padding: 4px 12px; cursor: pointer; white-space: nowrap;
  transition: background-color .16s ease, color .16s ease, border-color .16s ease;
}
.cv2-speed-btn:hover { background: var(--sd-bg); }
.cv2-speed-btn:focus-visible { outline: 2px solid var(--cv2-gold-ink); outline-offset: 1px; }
/* 고른 칸 — is-on 과 aria-checked 둘 다로 건다(JS 가 두 속성을 같이 바꾼다 · 상태와 그림을 맞춘다). */
.cv2-speed-btn.is-on,
.cv2-speed-btn[aria-checked="true"] {
  background: var(--cv2-gold-soft); color: var(--cv2-gold-ink);
  border-color: var(--cv2-gold); font-weight: 700;
}
.cv2-records { white-space: nowrap; }
@media (prefers-reduced-motion: reduce) {
  .cv2-speed-btn { transition: none; }
}
/* 좁은 화면(≤720px): 오른쪽 묶음(저장 표시 · 속도 바 · 기록 보기 · 더보기)을 제목 아래 한 줄로 내린다.
   속도 바 자체는 줄바꿈하지 않는다(.cv2-speed nowrap) — 공간이 모자라면 메뉴 버튼만 다음 줄로 간다. */
@media (max-width: 720px) {
  .cv2-head-right { margin-left: 0; width: 100%; flex-wrap: wrap; gap: 8px; }
}
/* 아주 좁은 화면(≤480px): 'AI 속도' 글자를 숨기고 칸 여백 · 글자만 줄인다(바는 radiogroup 이름으로 읽힌다). */
@media (max-width: 480px) {
  .cv2-speed-label { display: none; }
  .cv2-speed-btn { padding: 4px 9px; font-size: 12px; }
}

/* 한 줄 이야기는 **한 줄로** 줄인다(요구 5) — 길어도 줄바꿈으로 머리 높이가 커지지 않게 한다.
   uiux-261029-a(항목 9 · 11): 넘치면 … 로 자르고, 줄 전체가 펼침 단추라 누르면(키보드 Enter · Space) 전문을 펼친다
   (▶ 가 90° 돈다 — 예전 '기획 출발점 메모' 상자의 펼침 방식). 확정한 로그라인이면 라벨이 '확정 로그라인'(금색 글자).
   색은 테마 토큰만(두 테마). 펼쳐 머리가 높아지면 observeHeadHeight 가 진행 막대를 그 아래로 내린다. */
.cv2-logline {
  margin: 8px 0 0; color: var(--sd-ink); font-size: 15px;
  min-width: 0; max-width: 100%;
}
.cv2-logline-label { color: var(--sd-dim); font-weight: 600; }
/* 글이 없을 때 — 안내 한 줄(단추 없음). */
.cv2-logline.is-empty { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv2-logline-toggle {
  display: flex; align-items: baseline; gap: 6px;
  width: 100%; min-width: 0; margin: 0; padding: 2px 6px;
  border: 0; border-radius: 8px; background: transparent; color: inherit;
  font: inherit; line-height: 1.5; text-align: left; cursor: pointer;
}
.cv2-logline-toggle:hover { background: var(--sd-bg); }
.cv2-logline-toggle:focus-visible { outline: 2px solid var(--cv2-gold-ink); outline-offset: 1px; }
.cv2-logline-caret {
  flex: 0 0 auto; display: inline-block; font-size: 11px; color: var(--sd-dim);
  transition: transform .16s ease;
}
.cv2-logline-toggle .cv2-logline-label { flex: 0 0 auto; white-space: nowrap; }
.cv2-logline-toggle .cv2-logline-text {
  flex: 1 1 auto; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cv2-logline.is-open .cv2-logline-caret { transform: rotate(90deg); }
.cv2-logline.is-open .cv2-logline-toggle .cv2-logline-text { white-space: normal; overflow: visible; overflow-wrap: anywhere; }
.cv2-logline.is-confirmed .cv2-logline-label { color: var(--cv2-gold-ink); }
@media (prefers-reduced-motion: reduce) {
  .cv2-logline-caret { transition: none; }
}

/* --- 설정 메모지 (A5) --- */
/* 한 줄 이야기(있으면) 위, 설정 메모지 띠 아래로 **세로로 쌓는다**(요구 1·2). 둘 다 전폭을 쓴다.
   띠는 머리(.cv2-head, 045 sticky) 안에 있어 스크롤해도 함께 따라온다. */
.cv2-head-brief {
  display: flex; flex-direction: column; gap: 8px; margin-top: 8px;
}
.cv2-head-brief .cv2-logline { margin: 0; }

/* 설정 메모지 띠 — 한 줄에 칩이 늘어선다. 은은한 금색 카드 + 둥근 모서리(요구 2).
   넓은 화면: 한 줄. 넘치면 flex-wrap 으로 줄바꿈해 최대 2줄까지 보이고(가로 스크롤 금지),
   2줄을 넘기면 숨긴다(max-height + overflow:hidden) — 머리 높이(045 --cv2-sticky-head)를 지켜
   본문을 덮지 않게 한다(요구 3). */
.cv2-head-memo {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 6px 2px;
  padding: 6px 12px; border-radius: 10px;
  background: color-mix(in srgb, var(--cv2-gold) 7%, var(--sd-surface));
  border: 1px solid var(--cv2-gold-soft);
  /* 두 줄(한 줄 ≈ 24px)까지만 보이고 넘치면 가린다 — 가로 스크롤 대신 세로 제한. */
  max-height: 52px; overflow: hidden;
}
/* 칩 하나 — 라벨 + 값. 항목 사이 얇은 세로 구분선은 칩 왼쪽 테두리로 낸다(첫 칩은 뺀다). */
.cv2-memo-item {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 2px 12px; min-width: 0; max-width: 100%;
  border-left: 1px solid var(--cv2-gold-soft);
}
.cv2-memo-item:first-child { border-left: 0; padding-left: 2px; }
/* 라벨 = 작은 회색 글자, 값 = 본문 색. */
.cv2-memo-key { color: var(--sd-dim); font-size: 12px; font-weight: 600; white-space: nowrap; }
.cv2-memo-val {
  font-size: 13px; color: var(--sd-ink); min-width: 0;
  /* 짧은 값은 그대로, 긴 값(.is-long)만 한 줄 말줄임으로 줄인다. */
  word-break: break-word;
}
/* 긴 값(주인공 단기 목표 등) — 한 줄 말줄임(…). 전체 문장은 data-help 말풍선으로 본다. */
.cv2-memo-val.is-long {
  display: inline-block; max-width: 22ch;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  vertical-align: bottom;
}
.cv2-memo-val.is-long:focus-visible { outline: 2px solid var(--cv2-gold-ink); outline-offset: 2px; border-radius: 4px; }
/* 이동이 되는 값 칩(button) — 생김새는 값 글자 그대로, 밑줄로 '누를 수 있음'을 알린다. */
.cv2-memo-link {
  border: 0; background: transparent; font: inherit; cursor: pointer;
  color: var(--cv2-gold-ink); text-decoration: underline; text-underline-offset: 2px;
  padding: 0;
}
.cv2-memo-link:hover { text-decoration-thickness: 2px; }
.cv2-memo-link:focus-visible { outline: 2px solid var(--cv2-gold-ink); outline-offset: 2px; border-radius: 4px; }

/* 좁은 화면(≤820px): 항목별로 줄을 쌓는다(한 줄에 하나). 세로 구분선은 끄고, 긴 값 말줄임 폭을 넓힌다.
   줄로 쌓이면 최대 2줄 제한을 풀어 전부 보이게 한다(좁은 화면에선 머리가 본문을 덮는 문제가 덜하다). */
@media (max-width: 820px) {
  .cv2-head-memo { flex-direction: column; align-items: stretch; max-height: none; }
  .cv2-memo-item { border-left: 0; padding: 2px 2px; }
  .cv2-memo-val.is-long { max-width: 100%; }
}

/* --- 진행 막대 8칸 --- */
/* 머리 바로 아래에 이어서 고정된다(sticky). 머리와 같은 배경으로 한 덩어리처럼 보이게 한다.
   top = 상단바(--cv2-topbar) + 머리 높이(--cv2-sticky-head) 라, 머리 바로 밑에 딱 붙는다.
   진행 막대가 '머리+막대 덩어리'의 맨 아래 줄이므로, 아래쪽에 옅은 경계선과 그림자를 줘서
   본문과 구분한다(머리에는 따로 주지 않는다 — 두 요소가 한 덩어리로 보이게). */
.cv2-progress {
  display: flex; gap: 6px; margin: 0 -24px 18px;
  overflow-x: auto; padding: 4px 24px 8px;
  position: sticky; top: calc(var(--cv2-topbar, 0px) + var(--cv2-sticky-head, 96px)); z-index: 11;
  background: var(--sd-surface);
  border-bottom: 1px solid var(--sd-line);
  box-shadow: 0 6px 12px -8px rgba(0, 0, 0, 0.18);
}
.cv2-step {
  flex: 1 1 0; min-width: 72px;
  border: 0; border-radius: 999px;
  padding: 7px 10px;
  font: inherit; font-size: 13px;
  cursor: pointer; white-space: nowrap;
  text-align: center;
}
/* 완료 = 회색 채움. */
.cv2-step.is-done { background: var(--sd-line); color: var(--sd-dim); }
.cv2-step.is-done:hover { filter: brightness(0.96); }
/* 현재 = 금색 + 굵게. */
.cv2-step.is-current {
  background: var(--cv2-gold); color: #fff; font-weight: 700;
}
/* 잠김 = 빈 트랙(점선 테두리만). */
.cv2-step.is-locked {
  background: transparent; color: var(--sd-dim);
  border: 1px dashed var(--sd-line);
}
/* A6(계획서 2026-10-26): 대본 "개발 중" 잠김 — 자물쇠 + "개발 중" 작은 배지. 잠김의 한 종류라
   .is-locked 와 함께 쓰인다. 색·테두리는 --sd 변수로 두 테마 모두 따라온다. 모션은 없다
   (정적 표시라 reduced-motion 에서도 따로 끌 것이 없다). */
.cv2-step.is-dev {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
}
.cv2-step .cv2-step-lock { font-size: 12px; line-height: 1; opacity: 0.85; }
.cv2-step .cv2-step-dev {
  font-size: 10px; font-weight: 700;
  padding: 1px 6px; border-radius: 999px;
  background: var(--sd-line); color: var(--sd-dim);
  white-space: nowrap;
}
.cv2-step:focus-visible { outline: 2px solid var(--sd-ink); outline-offset: 2px; }

/* --------------------------------------------------------------------------
   D3 화려함(계획서 2026-10-26, 지시서 057) — 진행 막대

   왜 CSS 로만 하나: 진행 막대는 두 자리(공통 틀 stage-shell · cast-v2)가 같은 .cv2-step 를
   그린다. CSS 로 두면 둘 다 한 번에 얻고 JS 를 건드리지 않는다(최우선: 기능 유지). transform·
   opacity·box-shadow 만 움직이고, 색은 --cv2-gold 토큰이라 두 테마가 저절로 따라온다.
   reduced-motion 이면 아래 블록에서 둘 다 끈다.

   (1) 완료 칸 금색 sweep — 칸이 '완료(is-done)'로 그려지는 순간 금색 빛이 왼쪽에서 한 번
       흘러 지나간다(0.8s, 1회). 칸은 매 draw 마다 새 노드로 생기므로 '단계를 막 끝내 완료로
       바뀐 칸'에서 등장과 함께 자연히 한 번 재생된다(무한 반복 아님). ::after 의 금색 그라데이션
       띠를 translateX 로 지나가게 한다 — 바탕색은 그대로 두고 그 위를 덮었다 사라진다. */
.cv2-step.is-done { position: relative; overflow: hidden; }
.cv2-step.is-done::after {
  content: ""; position: absolute; inset: 0;
  border-radius: inherit;
  background: linear-gradient(
    100deg,
    transparent 0%,
    color-mix(in srgb, var(--cv2-gold) 55%, transparent) 45%,
    color-mix(in srgb, var(--cv2-gold) 75%, transparent) 50%,
    color-mix(in srgb, var(--cv2-gold) 55%, transparent) 55%,
    transparent 100%
  );
  transform: translateX(-100%);
  animation: cv2-step-sweep .8s cubic-bezier(0.22, 1, 0.36, 1) 1 both;
  pointer-events: none;
}
@keyframes cv2-step-sweep {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}
/* (2) 현재 칸 금색 글로우 호흡 — 지금 있는 단계를 아주 약하게 숨 쉬듯 빛낸다(4s 반복).
   은은하게: box-shadow 금색 번짐을 0↔약하게 오간다. 색만 바뀌는 게 아니라 퍼짐 반경이
   살짝 커졌다 작아진다. 바탕(금색 채움)은 그대로다. */
.cv2-step.is-current {
  animation: cv2-step-glow 4s ease-in-out infinite;
}
@keyframes cv2-step-glow {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--cv2-gold) 0%, transparent); }
  50%      { box-shadow: 0 0 10px 1px color-mix(in srgb, var(--cv2-gold) 45%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  .cv2-step.is-done::after { animation: none; display: none; }
  .cv2-step.is-current { animation: none; }
}

/* --- 본문 2단(인물 카드 + 질문 카드) --- */
.cv2-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 440px;
  gap: var(--sd-gap);
  align-items: start;
}
@media (max-width: 1100px) {
  .cv2-body { grid-template-columns: 1fr; }
}
/* 100bn-fix1: 화면 폭만 보면 본문 폭이 화면보다 좁을 때(왼쪽 메뉴 · 여백) 1100px 넘는 화면에서도 본문 칸이 좁아진다
   (716px 이면 본문 칸 약 260px). .cv2-body 의 부모(단계 .cv2 — 8개 2차 단계 모두 바로 위)를 크기 질의 컨테이너로 두고
   900px 미만이면 한 칸으로 쌓는다. 900px 에서 본문 칸은 900 − 440 − 16 = 444px 이다.
   새 틀(.is-split — 그 자체가 fixed 틀)과 공통 본문 감싸개(.cv2-generic — .cv2-body 가 바로 아래 없다)에는 달지 않는다. */
.cv2:not(.is-split):not(.cv2-generic) { container: cv2-page / inline-size; }
@container cv2-page (max-width: 899.98px) {
  .cv2-body { grid-template-columns: minmax(0, 1fr); }
  .cv2-body .cv2-question { position: static; }
}
.cv2-main { min-width: 0; }

/* --- 시나리오 구조 맵 입구 줄 (전체 줄거리 · 회차별 줄거리 2차, story-map-synopsis 2026-10-27) ---
   본문 왼쪽 열 맨 위의 작은 줄: 버튼 + 한 줄 안내. 버튼이 꺼져 있을 때 이유를 늘 보이려고 안내 글을 옆에
   둔다(design D1). 색은 테마 토큰만 — 두 테마에서 같은 규칙으로 읽힌다. 좁으면 안내가 버튼 아래로 내려간다. */
.cv2-map-entry {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 12px;
  margin: 0 0 12px;
  min-width: 0;
}
.cv2-map-entry-btn { flex: 0 0 auto; white-space: nowrap; }
.cv2-map-entry-hint {
  flex: 1 1 220px;
  min-width: 0;
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--sd-dim);
}
@media (max-width: 560px) {
  .cv2-map-entry-hint { flex-basis: 100%; }
}

/* --- 인물 세그먼트 --- */
.cv2-people {
  display: flex; gap: 6px; margin-bottom: 12px;
  overflow-x: auto; padding-bottom: 2px;
}
.cv2-person {
  border: 1px solid var(--sd-line); border-radius: 999px;
  padding: 6px 14px; background: var(--sd-surface); color: var(--sd-ink);
  font: inherit; font-size: 14px; cursor: pointer; white-space: nowrap;
}
.cv2-person.is-active {
  background: var(--sd-ink); color: var(--sd-surface); border-color: var(--sd-ink);
  font-weight: 600;
}
.cv2-person:focus-visible { outline: 2px solid var(--sd-ink); outline-offset: 2px; }
.cv2-person-add {
  border: 0; background: transparent; color: var(--cv2-gold-ink);
  font: inherit; font-size: 14px; cursor: pointer; white-space: nowrap;
  padding: 6px 10px;
}
.cv2-person-add:hover { text-decoration: underline; }

/* --- 인물 카드 --- */
.cv2-card { border-radius: var(--cv2-radius); padding: 22px; }
/* 2차 본문 카드는 모양을 위해 `.sd-panel`(옛 왼쪽 패널 클래스)을 함께 달았는데, .sd-panel 의
   position:sticky; top:16px 까지 딸려 와 본문 카드가 뜻하지 않게 고정돼 본문 글자 위에 겹쳤다
   (리뷰어 실측 2026-10-26). 2차 본문 카드는 본문과 함께 흘러야 하므로 static 으로 되돌린다.
   두 클래스 선택자(.cv2-card.sd-panel)라 .sd-panel 한 클래스보다 우선한다 — 옛 전문가 보기의
   왼쪽 패널(.sd-panel-left)은 .cv2-card 가 없어 여기 걸리지 않고 sticky 를 그대로 유지한다. */
.cv2-card.sd-panel { position: static; top: auto; }
.cv2-identity {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding-bottom: 14px; border-bottom: 1px solid var(--sd-line); margin-bottom: 14px;
}
.cv2-identity-name { font-size: 18px; }
.cv2-identity-meta { color: var(--sd-dim); font-size: 14px; }
.cv2-identity .sd-button { margin-left: auto; }

/* --- 문단 4개 --- */
.cv2-paragraphs { display: flex; flex-direction: column; gap: 18px; }
.cv2-para {
  display: grid; grid-template-columns: 120px minmax(0, 1fr); gap: 14px;
  align-items: start;
}
@media (max-width: 560px) {
  .cv2-para { grid-template-columns: 1fr; gap: 6px; }
}
.cv2-para-head { display: flex; flex-direction: column; gap: 6px; }
.cv2-para-label { font-weight: 600; font-size: 15px; }
.cv2-badge { align-self: flex-start; }
/* AI 초안은 금색 알약(사람이 아직 손대지 않음). */
.cv2-badge-ai {
  border-color: var(--cv2-gold) !important;
  color: var(--cv2-gold-ink) !important;
  background: var(--cv2-gold-soft);
}
.cv2-para-body {
  font-size: 16px; line-height: 1.7;
  border: 1px solid transparent; border-radius: 10px;
  padding: 8px 10px; margin: -8px -10px;
  white-space: pre-wrap; word-break: break-word;
  min-height: 1.7em;
}
.cv2-para-body[contenteditable="true"]:hover { background: var(--sd-bg); }
.cv2-para-body[contenteditable="true"]:focus-visible {
  outline: none; border-color: var(--cv2-gold); background: var(--sd-surface);
}
/* 빈 문단 안내(placeholder 대용 — contenteditable 에 placeholder 가 없다). */
.cv2-para-body.is-empty::before {
  content: attr(data-placeholder);
  color: var(--sd-dim);
}
.cv2-speech-sample { margin: 8px 0 0; }

.cv2-card-hint {
  margin: 18px 0 0; padding-top: 14px; border-top: 1px solid var(--sd-line);
  color: var(--sd-dim); font-size: 14px;
}
.cv2-link {
  border: 0; background: transparent; color: var(--cv2-gold-ink);
  font: inherit; cursor: pointer; padding: 0; text-decoration: underline;
}

/* --------------------------------------------------------------------------
   공용 알약 버튼 (.ds-pill) — 글자만 보이던 '링크형 동작 요소'를 버튼으로 (설계자 피드백 2026-10-26)

   ## 왜 생겼나
   2차 화면의 '비서 추천 3안' · '직접 쓸게요' · '건너뛰기' · '○○에 맞춰 다시 쓰기' 같은 동작은
   밑줄 글자(.cv2-link)라 버튼인지 알기 어려웠다. 그래서 **흰 알약 배경 + 금색 글자 + 얇은 금색
   테두리**의 눈에 보이는 버튼으로 바꾼다. 화면 이동용 진짜 링크(← 목록 · 메모 칩)는 그대로 둔다.

   ## 한 종류로 통일
   모든 2차 단계가 같은 .ds-pill 한 클래스를 쓴다(doc-stage.paragraph extraLinks · questionCard
   '직접 쓸게요' · 각 단계 본문의 동작 링크). 모양·호버·포커스가 한곳에서 정의돼 어긋나지 않는다.
   기존 .cv2-link 와 함께 달아도(cv2-link ds-pill) ds-pill 이 밑줄·여백 0 을 덮어쓰도록 뒤에 둔다.

   ## 두 테마 · reduced-motion
   - 밝은 테마: 흰 바탕(--sd-surface) + 금색 글자(--cv2-gold-ink) + 얇은 금색 테두리(--cv2-gold).
   - 어두운 테마: 흰 알약이 너무 튀므로 --cv2-gold-soft(어두운 금색 바탕) + 금색 글자로 대비를 맞춘다.
   - 호버: 아주 옅은 금색 배경(--cv2-gold-soft). 포커스: 금색 포커스 링.
   - reduced-motion: transition 을 끈다(색 변화만 즉시).
   -------------------------------------------------------------------------- */
.ds-pill {
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--cv2-gold);
  background: var(--sd-surface);
  color: var(--cv2-gold-ink);
  font: inherit; font-size: 13px; font-weight: 600;
  padding: 5px 13px;
  border-radius: 999px;
  text-decoration: none;       /* .cv2-link 의 밑줄을 덮는다(함께 달 수 있으므로). */
  cursor: pointer;
  line-height: 1.4;
  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}
.ds-pill:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--cv2-gold-soft); }
.ds-pill:focus-visible {
  outline: 2px solid var(--cv2-gold-ink); outline-offset: 2px;
}
.ds-pill:disabled { opacity: 0.55; cursor: default; }
/* 100av: AI 비서 칸 '적용' 잠금(다른 '적용' · AI 가 일하는 중)은 disabled 가 아니라 aria-disabled 다 — 눌림이 화면에 닿아야
   "잠시 후 다시 눌러 주세요" 한 줄을 남길 수 있다(예전에는 클릭이 말없이 사라졌다). 모양은 disabled 와 같은 흐림. */
.ds-pill[aria-disabled="true"] { opacity: 0.55; cursor: default; }
/* 어두운 테마: 흰 알약 대신 어두운 금색 바탕 + 금색 글자로 대비를 맞춘다(--cv2-gold-soft 가
   테마 블록에서 어두운 값으로 바뀐다). 호버는 테두리를 밝혀 눌림을 알린다. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ds-pill {
    background: var(--cv2-gold-soft);
    border-color: var(--cv2-gold);
  }
  :root:not([data-theme="light"]) .ds-pill:hover:not(:disabled) {
    border-color: var(--cv2-gold-ink);
    filter: brightness(1.12);
  }
}
[data-theme="dark"] .ds-pill {
  background: var(--cv2-gold-soft);
  border-color: var(--cv2-gold);
}
[data-theme="dark"] .ds-pill:hover:not(:disabled) {
  border-color: var(--cv2-gold-ink);
  filter: brightness(1.12);
}
@media (prefers-reduced-motion: reduce) {
  .ds-pill { transition: none; }
}

/* 하단 도크 제안 칩 묶음 — 도크 입력줄 바로 위에 뜬다(doc-stage.setDockSuggestions).
   '현재 단계의 ○○을(를) 추천받을까요?' 알약을 가로로 늘어놓는다. 좁으면 줄바꿈. */
.ds-dock-suggest {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  width: 100%;
  margin-bottom: 8px;
}
.ds-dock-suggest-label { font-size: 12px; font-weight: 700; color: var(--sd-dim); }
.ds-dock-suggest-chip { white-space: nowrap; }

/* --------------------------------------------------------------------------
   하단 도크 대화 기록 — '안내형 채팅'(설계자 지시 2026-10-26, 요구 1·3·5)

   도크(.cv2-dock) 안에서 입력줄·제안 칩보다 **위**에 오는 전폭 영역이다(doc-stage.applyChat 가
   도크의 첫 자식으로 끼운다). 높이는 화면의 40% 이하, 넘치면 목록(.ds-chat-list) 안에서 스크롤.
   사용자 말풍선 오른쪽, 비서 말풍선 왼쪽(금색 점). 두 테마·reduced-motion 에서 모두 동작한다.
   -------------------------------------------------------------------------- */
.ds-chat {
  width: 100%;
  display: flex; flex-direction: column;
  border-bottom: 1px solid var(--sd-line);
  margin-bottom: 8px;
  padding-bottom: 8px;
}
.ds-chat.is-collapsed { border-bottom: none; padding-bottom: 0; margin-bottom: 4px; }

/* 머리 — 제목 + '대화 지우기' · '대화 숨기기/보기'. */
.ds-chat-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; padding: 2px 2px 6px;
}
.ds-chat-head-title { font-size: 12px; font-weight: 700; color: var(--sd-dim); }
.ds-chat-head-actions { display: flex; align-items: center; gap: 10px; }
.ds-chat-head-btn { font-size: 12px; }

/* 메시지 목록 — 넘치면 안에서 스크롤. 높이 화면 40% 이하(요구 1). */
.ds-chat-list {
  display: flex; flex-direction: column; gap: 10px;
  max-height: 40vh; overflow-y: auto;
  padding: 2px 2px 2px;
  scroll-behavior: smooth;
}
.ds-chat-empty { margin: 4px 2px; }

/* 말풍선 한 줄 — 사용자는 오른쪽 정렬, 비서는 왼쪽(금색 점). */
.ds-chat-msg { display: flex; align-items: flex-start; gap: 8px; max-width: 100%; }
.ds-chat-msg.is-user { justify-content: flex-end; }
.ds-chat-msg.is-assistant { justify-content: flex-start; }
.ds-chat-dot {
  flex: 0 0 auto; width: 8px; height: 8px; margin-top: 10px;
  border-radius: 50%; background: var(--cv2-gold);
}
.ds-chat-bubble {
  max-width: min(680px, 86%);
  border-radius: 14px; padding: 9px 13px;
  background: var(--sd-surface-2, var(--sd-surface));
  border: 1px solid var(--sd-line);
  font-size: 13px; line-height: 1.5;
}
.ds-chat-msg.is-user .ds-chat-bubble {
  background: var(--cv2-gold-soft);
  border-color: var(--cv2-gold);
  color: var(--sd-text);
}
.ds-chat-text { margin: 0; white-space: pre-wrap; }
.ds-chat-text.is-error { color: var(--sd-danger); }

/* 비서 말풍선 안의 버튼 목록·카드. */
.ds-chat-items { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.ds-chat-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.ds-chat-item, .ds-chat-action { font-size: 12px; }

/* 추천 결과 카드(3안 + 맞춤 1안) — 비서 말풍선 안에 세로로 쌓는다. */
.ds-chat-cards { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.ds-chat-card {
  border: 1px solid var(--sd-line); border-radius: 10px;
  padding: 8px 10px; background: var(--sd-surface);
  display: flex; flex-direction: column; gap: 5px;
}
.ds-chat-card-head { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ds-chat-card-title { font-size: 13px; }
.ds-chat-card-text { margin: 0; font-size: 13px; line-height: 1.5; white-space: pre-wrap; }
.ds-chat-card-meta { margin: 0; }
.ds-chat-card-apply { align-self: flex-start; font-size: 12px; margin-top: 2px; }

@media (prefers-reduced-motion: reduce) {
  .ds-chat-list { scroll-behavior: auto; }
}

.cv2-empty { padding: 40px 20px; text-align: center; }

/* --- 질문 카드(오른쪽, sticky) — 3/5 에서 채운다 --- */
.cv2-question {
  /* 머리·진행 막대가 sticky 로 위에 겹치므로(D0), 질문 카드는 그 아래(상단바+머리+막대 높이)에서
     따라오게 top 을 내린다. 안 그러면 질문 카드 머리가 고정 머리 덩어리에 가린다.
     상단바(--cv2-topbar) + 머리 높이(--cv2-sticky-head) + 진행 막대 높이(약 46px) 만큼 내린다. */
  position: sticky; top: calc(var(--cv2-topbar, 0px) + var(--cv2-sticky-head, 96px) + 46px);
  border-radius: var(--cv2-radius); padding: 22px;
}
@media (max-width: 1100px) { .cv2-question { position: static; } }
.cv2-question-title { font-weight: 600; font-size: 15px; margin: 0 0 8px; }

/* --- 하단 도크(fixed) — 4/5 에서 AI 비서 입력을 채운다 --- */
/* 메인 앱 사이드바 오른쪽에서 시작한다. 사이드바 폭은 styles.css 가 `.app-shell` 에 둔
   커스텀 속성 `--nav-w`(기본 242px, 접히면 `.nav-collapsed` 가 0px)다. 이 변수는 .app-shell
   자손에게 상속되므로(fixed 요소도 상속 사슬을 따른다) left 에 그대로 쓰면 사이드바 접힘에
   맞춰 도크가 넓어진다. story-dev 단독 페이지(.app-shell 밖)에서는 변수가 없어 대체값 0 이 쓰인다. */
.cv2-dock {
  position: fixed; left: var(--nav-w, 0px); right: 0; bottom: 0; z-index: 30;
  display: flex; align-items: center; flex-wrap: wrap; gap: 12px;
  padding: 12px 24px;
  background: var(--sd-surface);
  border-top: 1px solid var(--sd-line);
  box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.06);
}
.cv2-dock-say {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: 8px;
}
.cv2-dock-spark { color: var(--cv2-gold); }
.cv2-dock-confirm { flex: 0 0 auto; white-space: nowrap; }

/* 좁은 화면(≤820px)에서는 메인 사이드바가 서랍으로 숨고 `--nav-w` 가 그대로 242px 로 남으므로
   left 를 변수에 맡기면 도크가 빈 자리에서 시작한다. 이 폭에서는 좌우 16px 여백으로 고정한다
   (styles.css 의 모바일 사이드바 분기점 max-width:820px 과 맞춘다). */
@media (max-width: 820px) {
  .cv2-dock { left: 16px; right: 16px; }
}

@media (max-width: 560px) {
  .cv2-dock { flex-direction: column; align-items: stretch; gap: 8px; padding: 10px 14px; }
}

/* ==========================================================================
   cast 아닌 단계의 공통 틀 — 본문 카드 · 하단 도크 입력줄 (지시서 2·3·4)

   cast 2차는 .cv2-body 안에서 인물·질문 2열을 그리지만, 그 밖의 단계는 기존 본문
   (view.render 가 stageHost 에 그린 것)을 cv2 카드 모양 한 장으로 감싼다. workspace 가
   .cv2.cv2-generic > .cv2-body-card > .sd-stage 로 그린다. 색·모서리·여백만 cv2 토큰에 맞추고
   배치(그리드·순서)는 바꾸지 않는다 — 입력칸·표는 기존 .sd-* 규칙을 그대로 쓴다.
   ========================================================================== */

/* 본문 카드 — cv2 카드와 같은 배경·테두리·둥근 모서리·안쪽 여백. 폭 전체. */
.cv2-generic { min-width: 0; }
.cv2-body-card {
  background: var(--sd-surface);
  border: 1px solid var(--sd-line);
  border-radius: var(--cv2-radius);
  padding: 22px;
}
@media (max-width: 560px) { .cv2-body-card { padding: 16px; } }
/* 본문 글자 크기·행간은 cv2 문단과 맞춘다(16px / 1.7). 단 입력칸·표·버튼은 기존 크기를 지킨다. */
.cv2-body-card .sd-stage { font-size: 16px; line-height: 1.7; }
.cv2-body-card .sd-input,
.cv2-body-card .sd-textarea,
.cv2-body-card select,
.cv2-body-card table { font-size: 14px; line-height: 1.5; }
/* 안쪽 .sd-stage 는 자체 카드 꾸밈(배경·테두리·여백)을 끈다 — 카드 모양은 바깥 .cv2-body-card 가
   낸다(카드 안 카드가 되지 않게). 폭은 카드 안에서 다 쓴다. */
.cv2-body-card .sd-stage {
  background: transparent; border: 0; border-radius: 0; padding: 0;
  max-width: none; margin: 0;
}

/* --- cast 아닌 단계의 하단 도크 --- */
/* 왼쪽 입력 자리(.cv2-dock-slot)는 비어 있어도 도크 높이를 지키는 받침이다. 그 단계의 대화
   입력줄(.sd-agent-composer)은 아래 규칙으로 이 자리에 fixed 로 들어온다. */
.cv2-dock-slot { flex: 1 1 auto; min-width: 0; }
.cv2-dock-next { flex: 0 0 auto; white-space: nowrap; }

/* 단계 대화 입력줄을 도크 왼쪽에 겹쳐 둔다 — DOM 은 stageHost 안에 그대로 있고(재렌더에 살아남고),
   화면에서만 도크 자리로 끌어온다. cast 2차(.is-cast-v2)는 자체 도크를 쓰므로 제외한다.
   도크(.cv2-dock)와 같은 left/right/bottom 에 맞춰, 오른쪽 '다음 단계로 →' 버튼 자리는 비워 둔다. */
.sd-page-project.is-v2:not(.is-cast-v2) .sd-stage .sd-agent-composer {
  position: fixed;
  left: var(--nav-w, 0px);
  right: 0;
  bottom: 0;
  z-index: 31; /* .cv2-dock(30) 위에 둬서 입력이 눌린다. */
  box-sizing: border-box;
  /* 도크 안쪽 여백과 맞추고, 오른쪽은 '다음 단계로 →' 버튼 폭만큼 비운다. */
  padding: 12px 24px;
  padding-right: 200px;
  margin: 0;
}
/* 입력줄 안의 .sd-composer 는 도크 폭을 다 쓰되 한 줄로 눕는다. */
.sd-page-project.is-v2:not(.is-cast-v2) .sd-stage .sd-agent-composer .sd-composer { width: 100%; }

/* 마지막 단계는 '다음 단계로 →' 버튼이 없으므로 입력줄이 오른쪽 끝까지 쓴다.
   is-last-stage 는 workspace 가 페이지에 붙인다(단계 파일은 건드리지 않는다). */
.sd-page-project.is-v2.is-last-stage:not(.is-cast-v2) .sd-stage .sd-agent-composer {
  padding-right: 24px;
}

@media (max-width: 820px) {
  .sd-page-project.is-v2:not(.is-cast-v2) .sd-stage .sd-agent-composer { left: 16px; right: 16px; }
}
@media (max-width: 560px) {
  /* 좁은 화면에서는 도크가 세로로 쌓이므로, 입력줄 fixed 를 풀어 본문 흐름에 그대로 둔다
     (도크의 '다음 단계로 →' 버튼은 그 아래에 그대로 보인다). */
  .sd-page-project.is-v2:not(.is-cast-v2) .sd-stage .sd-agent-composer {
    position: static; padding: 0; z-index: auto;
  }
}

/* ==========================================================================
   공통 틀 단계(전체 줄거리 · 회차별 줄거리 · 대본)의 모양을 2차 단계와 똑같이 (수정 4, CSS 전용)

   synopsis · treatment · script 는 2차 전용 화면이 없어 공통 틀(stage-shell + 기존 본문)로
   그려진다. 2차 단계(cast-v2 · origin-v2 …)와 두 가지가 달라 보였다:
     (1) '기획 출발점 메모' 띠 위치 — 2차 단계는 머리 위, 공통 틀 단계는 머리와 진행 막대 사이.
     (2) 하단 도크 모양 — 2차 단계는 한 줄 알약(작은 'AI 비서' 표시 · 입력 · 오른쪽 주 버튼)인데,
         공통 틀 단계는 '보내기' 버튼이 보이고 알약 꼴이 아니었다.
   DOM · JS 는 건드리지 않고 CSS 로만 2차 단계와 같은 자리 · 같은 꼴로 맞춘다.
   ========================================================================== */

/* (1) '기획 출발점 메모' 띠를 머리 위로 — 2차 단계와 같은 자리.
   페이지를 세로 flex 로 두고(블록 쌓임과 같은 순서를 지킨다) 띠에만 order:-1 을 줘 맨 위로 올렸다.
   fixed 도크·숨긴 옛 머리(display:none)는 흐름 밖이라 영향 없다.
   uiux-261029-a(항목 9): 띠(.sd-origin-band)는 머리의 한 줄과 겹쳐 지웠다 — order 규칙도 지우고, 세로 flex 는
   지금 배치(머리 → 갱신 안내 → 진행 막대 → 본문)를 그대로 지키려고 남긴다. */
.sd-page-project.is-v2 {
  display: flex;
  flex-direction: column;
}

/* (2) 공통 틀 단계의 하단 도크를 2차 단계(cast-v2)와 같은 한 줄 알약으로.
   입력줄(.sd-agent-composer > .sd-composer)은 workspace 가 CSS 로 도크 자리에 fixed 로 끌어온
   것이다. 그 안을 알약 한 줄로 바꾸고 '보내기' 버튼은 숨긴다(Enter 로 보낸다). cast 2차는 자체
   도크를 쓰므로 :not(.is-cast-v2) 로 제외한다. */
.sd-page-project.is-v2:not(.is-cast-v2) .sd-stage .sd-agent-composer .sd-composer {
  /* 공통 틀 입력줄의 윗 구분선·여백을 없앤다(도크 자체가 윗 구분선을 갖는다). */
  border-top: 0;
  padding-top: 0;
}
.sd-page-project.is-v2:not(.is-cast-v2) .sd-stage .sd-agent-composer .sd-composer-row {
  /* 한 줄 알약 트랙 — cast 2차 .cv2-dock-say 와 같은 꼴(테두리 · 둥근 · 배경). */
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border: 1px solid var(--sd-line);
  border-radius: 999px;
  background: var(--sd-bg);
}
.sd-page-project.is-v2:not(.is-cast-v2) .sd-stage .sd-agent-composer .sd-composer-row:focus-within {
  border-color: var(--cv2-gold);
  background: var(--sd-surface);
}
/* 왼쪽 작은 'AI 비서' 표시 — cast 2차 ✦ 자리와 같은 왼쪽 라벨(기존 배지 모양은 알약 안에 맞게 낮춘다). */
.sd-page-project.is-v2:not(.is-cast-v2) .sd-stage .sd-agent-composer .sd-composer-badge {
  align-self: center;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
}
/* 입력칸 — 테두리·배경 없는 큰 글씨(알약이 테두리를 갖는다). cast 2차 .cv2-dock-input 과 같은 꼴. */
.sd-page-project.is-v2:not(.is-cast-v2) .sd-stage .sd-agent-composer .sd-composer-input {
  min-height: 28px;
  border: 0; background: transparent;
  font-size: 16px; line-height: 1.5;
  padding: 2px 0;
}
.sd-page-project.is-v2:not(.is-cast-v2) .sd-stage .sd-agent-composer .sd-composer-input:focus,
.sd-page-project.is-v2:not(.is-cast-v2) .sd-stage .sd-agent-composer .sd-composer-input:focus-visible {
  outline: none;
}
/* '보내기' 버튼은 숨긴다 — Enter 로 보낸다(composer.js 의 Enter 전송 그대로). DOM 에는 남긴다.
   좁은 화면(≤560px)에서는 Enter 가 어려우니 작은 ↵ 아이콘 버튼으로 되살린다(아래 미디어쿼리). */
.sd-page-project.is-v2:not(.is-cast-v2) .sd-stage .sd-agent-composer .sd-composer-send {
  /* 아이콘 폭만 남긴 작은 ↵ 버튼. 글자 '보내기' 는 숨기고 아이콘을 ::after 로 둔다. */
  align-self: center;
  flex: 0 0 auto;
  width: 34px; height: 30px; min-width: 0; padding: 0;
  overflow: hidden;
  border-radius: 999px;
  font-size: 0; /* '보내기' 글자를 숨긴다(data-help 는 그대로 읽힌다). */
  color: transparent;
}
.sd-page-project.is-v2:not(.is-cast-v2) .sd-stage .sd-agent-composer .sd-composer-send::after {
  content: "↵";
  font-size: 15px; color: #fff; line-height: 1;
}
/* 넓은 화면에서는 ↵ 버튼도 숨긴다(Enter 만으로 충분 — 2차 단계 도크와 같은 꼴). */
@media (min-width: 561px) {
  .sd-page-project.is-v2:not(.is-cast-v2) .sd-stage .sd-agent-composer .sd-composer-send {
    display: none;
  }
}
/* 좁은 화면: 도크가 세로로 쌓이고 입력줄 fixed 가 풀린다(위 @max-width:560px). 알약 한 줄은 유지하되
   ↵ 버튼을 보여 Enter 가 어려운 환경에서도 보낼 수 있게 한다. */
@media (max-width: 560px) {
  .sd-page-project.is-v2:not(.is-cast-v2) .sd-stage .sd-agent-composer .sd-composer-row {
    flex-direction: row; align-items: center;
  }
}

/* --- is-v2 공통 — 섹션 제목·버튼·입력칸 색·모서리만 cv2 토큰에 맞춘다(배치는 그대로) --- */
/* 주 버튼(확정·저장·다음)은 금색 강조로. 배경·글자색만 바꾸고 크기·여백은 sd-button 그대로. */
.sd-page-project.is-v2 .sd-button-primary {
  background: var(--cv2-gold);
  border-color: var(--cv2-gold);
  color: #fff;
}
.sd-page-project.is-v2 .sd-button-primary:hover { filter: brightness(0.96); }
/* 버튼·입력칸 모서리를 cv2 처럼 둥글게(색·배치는 유지). */
.sd-page-project.is-v2 .sd-button { border-radius: 10px; }
.sd-page-project.is-v2 .sd-input,
.sd-page-project.is-v2 .sd-textarea { border-radius: 10px; }
.sd-page-project.is-v2 .sd-input:focus-visible,
.sd-page-project.is-v2 .sd-textarea:focus-visible {
  outline: none; border-color: var(--cv2-gold);
}
/* 섹션 제목 — 금색 강조선 없이 글자만 또렷하게(배치·크기 유지). */
.sd-page-project.is-v2 .sd-stage-title { color: var(--sd-ink); }

/* ==========================================================================
   S1 2차 화면 — 질문 카드(3/5) · 자동 초안 · 공용 패널(2/5)

   질문 카드(오른쪽 sticky) 본문과 보기, 반영 <mark>, 글자 써지는 커서, 자동 초안 한도 안내,
   그리고 2/5 가 쓰는 접힘 패널(신원 · 성격 참고 · 말투 편집기)의 공통 레이아웃을 둔다.
   색은 직접 쓰지 않고 `--sd-*` · `--cv2-*` 토큰만 쓴다. 범위는 `.cv2` 안.
   ========================================================================== */

/* --- 질문 카드 상단(진행) --- */
.cv2-q-top {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-bottom: 14px;
}
.cv2-q-count { margin: 0; font-size: 14px; font-weight: 600; color: var(--sd-ink); }
.cv2-q-dots { display: flex; gap: 6px; }
.cv2-q-dot {
  width: 9px; height: 9px; border-radius: 999px;
  background: var(--sd-line);
}
.cv2-q-dot.is-done { background: var(--sd-dim); }
.cv2-q-dot.is-current { background: var(--cv2-gold); }

/* --- 질문 본문 --- */
.cv2-q-question { margin: 0 0 6px; font-size: 21px; font-weight: 600; line-height: 1.4; }
.cv2-q-why { margin: 0 0 14px; }

/* --- 보기 3개 --- */
.cv2-q-choices { display: flex; flex-direction: column; gap: 8px; }
.cv2-q-choice {
  display: flex; align-items: flex-start; gap: 12px;
  width: 100%; text-align: left;
  border: 1px solid var(--sd-line); border-radius: 12px;
  padding: 12px 14px; background: var(--sd-surface); color: var(--sd-ink);
  font: inherit; cursor: pointer;
  transition: border-color .12s ease, background .12s ease, transform .12s ease;
}
.cv2-q-choice:hover { border-color: var(--cv2-gold); background: color-mix(in srgb, var(--cv2-gold) 12%, transparent); } /* 다크 테마에서 --sd-bg(밝은색) 위 흰 글자가 묻혀 금색 옅은 배경으로 바꿈 (10-27) */
.cv2-q-choice:focus-visible { outline: 2px solid var(--sd-ink); outline-offset: 2px; }
.cv2-q-choice[aria-checked="true"] {
  border-color: var(--cv2-gold); background: var(--cv2-gold-soft);
}
.cv2-q-choice:disabled { opacity: .6; cursor: default; }
.cv2-q-num {
  flex: 0 0 auto;
  width: 24px; height: 24px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--cv2-gold-soft); color: var(--cv2-gold-ink);
  font-size: 13px; font-weight: 700;
}
.cv2-q-choice-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.cv2-q-choice-label { font-size: 16px; font-weight: 600; }
.cv2-q-choice-preview { font-size: 13px; color: var(--sd-dim); }

/* --- B3 적용 범위 선택(이 단락만 · 이후 전체 · 다음 N) --- */
.cv2-q-scope {
  margin-top: 10px; padding: 10px 12px;
  border: 1px solid var(--sd-line); border-radius: 10px;
  background: var(--sd-bg);
}
.cv2-q-scope-title { margin: 0 0 6px; }
.cv2-q-scope-opts { display: flex; flex-wrap: wrap; gap: 8px 16px; }
.cv2-q-scope-opt {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 14px; color: var(--sd-ink); cursor: pointer;
}
.cv2-q-scope-opt input:disabled { cursor: default; }
.cv2-q-scope-opt:has(input:disabled) { opacity: .6; cursor: default; }

.cv2-q-own {
  margin-top: 10px; display: inline-block; font-size: 14px;
}
.cv2-q-hint { margin: 12px 0 0; }

/* --- 질문 끝 · AI 의견 --- */
.cv2-q-done-body { margin: 6px 0 12px; font-size: 15px; }
.cv2-q-review-link { font-size: 14px; }

/* --- 078 추천 고르기 카드(.cv2-pick) · 다 만든 인물의 조용한 안내 카드(.cv2-quiet) ---
   질문 카드와 같은 틀(.cv2-question · .cv2-q-choice · .cv2-q-num)을 쓰고 다른 점만 둔다. 색은 테마 토큰만
   (두 테마), 새 전이 · 애니메이션은 없다(reduced-motion 영향 없음 — .cv2-q-choice 전이는 위에서 이미 끈다).
   scroll-margin: 좁은 화면에서 결과가 오면 카드로 스크롤하는데(revealPickCard) 고정 머리 · 도크에 가리지 않게. */
.cv2-pick,
.cv2-quiet {
  scroll-margin-top: calc(var(--cv2-topbar, 0px) + var(--cv2-sticky-head, 96px) + 56px);
  scroll-margin-bottom: var(--cv2-dock-h, 96px);
}
.cv2-pick .cv2-q-top { margin-bottom: 6px; }
.cv2-pick-title:focus { outline: none; }
.cv2-pick-title:focus-visible { outline: 2px solid var(--cv2-gold-ink); outline-offset: 2px; border-radius: 4px; }
.cv2-pick.is-running .cv2-pick-title { color: var(--cv2-gold-ink); }
.cv2-pick-note { margin: 0 0 10px; }
.cv2-pick-status { margin: 0 0 10px; color: var(--cv2-gold-ink); font-weight: 600; }
.cv2-pick-row { display: flex; flex-direction: column; gap: 4px; }
.cv2-pick .cv2-q-choice-label { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 6px; }
/* 미리보기는 전문이 DOM 에 있고(화면 낭독기는 다 읽는다) 화면에는 3줄만 보인다. '전체 보기'로 펼친다. */
.cv2-pick .cv2-pick-preview {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3;
  overflow: hidden;
  white-space: pre-wrap; word-break: break-word;
  font-size: 14px; line-height: 1.55; color: var(--sd-ink);
}
.cv2-pick .cv2-pick-row.is-expanded .cv2-pick-preview {
  display: block; -webkit-line-clamp: unset; line-clamp: none; overflow: visible;
}
.cv2-pick-why { font-size: 12px; color: var(--sd-dim); }
.cv2-pick-more { align-self: flex-end; font-size: 12px; padding: 3px 10px; }
.cv2-pick-choice.is-personal { border-style: dashed; }
.cv2-pick-row.is-picked .cv2-pick-choice { border-color: var(--cv2-gold); background: var(--cv2-gold-soft); }
.cv2-pick-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 12px; }
.cv2-pick-history { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--sd-line); }
.cv2-pick-history .sd-assist-compact { margin: 0; }
.cv2-quiet-text { margin: 0 0 12px; font-size: 15px; line-height: 1.6; color: var(--sd-ink); }
.cv2-quiet-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }

/* --- 반영 문장 <mark>(금색 밑줄 + 옅은 금색 배경) --- */
.cv2-mark {
  background: var(--cv2-gold-soft);
  border-bottom: 2px solid var(--cv2-gold);
  border-radius: 2px;
  padding: 0 1px;
  color: inherit;
}

/* --- 직접 쓸게요 강조 --- */
.cv2-para-body.cv2-para-focus {
  border-color: var(--cv2-gold);
  background: var(--cv2-gold-soft);
}

/* --- 글자 써지는 커서(자동 초안) --- */
.cv2-type-cursor {
  color: var(--cv2-gold);
  animation: cv2-blink 1s step-end infinite;
}
@keyframes cv2-blink { 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .cv2-type-cursor { animation: none; }
  .cv2-q-choice { transition: none; }
}

/* --- 자동 초안 한도 안내 --- */
.cv2-draft-notice-body { margin: 6px 0 4px; font-size: 16px; font-weight: 600; }
.cv2-draft-actions { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
.cv2-draft-actions .sd-button { flex: 1 1 auto; }
.cv2-draft-fail { margin: 6px 0 0; color: var(--sd-danger); }
/* 100av: 빈 칸 아래 "AI 비서 칸에 추천안이 와 있어요" — 실패가 아니라 안내라 기본 글자색(흐린 힌트)이고, 앞 구절은 그 카드로 가는 링크(.cv2-memo-link). */
.cv2-draft-wait { margin: 6px 0 0; }

/* --- 공용 접힘 패널(2/5: 신원 · 성격 참고 · 말투 편집기) --- */
.sd-panel-inset {
  margin-top: 14px; padding: 16px;
  border: 1px solid var(--sd-line); border-radius: 12px;
  background: var(--sd-bg);
}
.cv2-field-row { display: flex; gap: 12px; flex-wrap: wrap; }
.cv2-field-row > .cv2-field { flex: 1 1 160px; min-width: 0; }
.cv2-field { display: flex; flex-direction: column; gap: 4px; }
.cv2-field-group-label { margin: 14px 0 6px; font-weight: 600; font-size: 14px; }
.cv2-birth-row > .cv2-field { flex: 1 1 64px; }
.cv2-birth-actions { margin-top: 8px; }
.cv2-role-types { display: flex; gap: 14px; flex-wrap: wrap; margin: 12px 0; }
.cv2-role-type { display: flex; align-items: center; gap: 6px; font-size: 14px; }
.cv2-identity-actions { display: flex; align-items: center; gap: 10px; margin-top: 16px; }
.cv2-identity-toggle { margin-left: auto; color: var(--cv2-gold-ink); font-size: 14px; }
/* 100o: 이름이 빈 인물 카드 — 신원 줄이 접혀 있어도 이름 칸 자리가 보이게 하는 글자 안내(색은 금색 토큰만). */
.cv2-identity-unnamed { padding: 2px 10px; border-radius: 999px; background: var(--cv2-gold-soft); color: var(--cv2-gold-ink); font-size: 14px; font-weight: 600; }
/* 100o: 주변 인물 추천 띠에서 이미 넣은 후보 — 목록에 남기되 흐리게(버튼은 '넣었어요'로 꺼진다). */
.sd-cast-lead-card.is-placed { opacity: 0.6; }
.cv2-identity[role="button"] { cursor: pointer; }
.cv2-identity[role="button"]:focus-visible { outline: 2px solid var(--sd-ink); outline-offset: 2px; }
.cv2-saju-host { margin-top: 10px; }
.cv2-saju-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
/* (예전 '성격 참고' 패널 · 형용사 칩 규칙은 패널과 함께 지웠다 — uiux-261029-a 항목 6.) */
.cv2-para-ai { font-size: 13px; }
.cv2-para-rewrite { margin: 6px 0 0; }
.cv2-speech-para { cursor: pointer; }
.cv2-speech-para:focus-visible { outline: 2px solid var(--sd-ink); outline-offset: 2px; }
.cv2-speech-sample-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-top: 8px; }
/* 문단 그리드(120px + 1fr)의 셋째 칸으로 들어가 120px 열에 갇히면 '대사 확인' 알약이 한 글자씩 꺾였다
   (2026-10-26 운영). 두 열을 모두 쓰게 하고, 알약 버튼은 줄바꿈하지 않는다. */
.cv2-para > .cv2-speech-sample-row { grid-column: 1 / -1; }
/* 078 assist-pick-in-card(D): 제목 · 본문 다음 줄(다시 쓰기 버튼 줄 · 초안 다시 시도 · 말투 편집기 · 문단 아래
   추천 패널)도 같은 이유로 한 줄 전체를 쓴다. 다시 쓰기 줄이 120px 칸에 들어가고 오른쪽 칸의 패널 밑으로
   겹쳐 '전사에 맞춰 성격 다시 쓰기' 글자가 잘렸다(설계자 사진 10-27). 범위는 cast-v2 문단만이다 —
   doc-stage 문단(.ds-para)은 단계마다 자리 규칙이 따로 있어(.ds-assist-slot 등) 건드리지 않는다. */
.cv2-para:not(.ds-para) > .cv2-para-rewrite,
.cv2-para:not(.ds-para) > .cv2-draft-fail,
.cv2-para:not(.ds-para) > .cv2-draft-wait,
.cv2-para:not(.ds-para) > .cv2-speech-editor,
.cv2-para:not(.ds-para) > .sd-assist { grid-column: 1 / -1; }
.cv2-para:not(.ds-para) > .cv2-para-rewrite { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.ds-pill { white-space: nowrap; flex-shrink: 0; }
.cv2-speech-sample-label { color: var(--sd-dim); font-weight: 600; }
.cv2-rename-host:empty { display: none; }
.cv2-rename { margin-bottom: 10px; }
.cv2-leads { margin-bottom: 14px; }
.cv2-removed { margin-top: 16px; }


/* ==========================================================================
   S1 2차 화면 — 하단 도크 입력 · 되돌리기 알림 · 완료 카드 (5/5)

   4/5(AI 비서 입력 · 되돌리기 · 완료 카드)가 만든 DOM 에 아직 스타일이 없던 16개 클래스를
   채운다. 색은 직접 쓰지 않고 위에서 정의한 `--sd-*` · `--cv2-*` 토큰만 쓴다(두 테마가 그대로
   읽힌다 — prefers-color-scheme · [data-theme] 로 `--cv2-gold*` 가 바뀜). 범위는 `.cv2` 안.
   모션은 가이드 11장 값으로 keyframes/transition 을 쓰고, 맨 아래 prefers-reduced-motion 에서 끈다.
   ========================================================================== */

/* --- 하단 도크: AI 비서 입력(알약) · 실행 중 표시 --- */
/* 입력 묶음을 알약형 트랙으로 — 사이드바 오른쪽부터 화면 끝까지 꽉 차는 .cv2-dock 안에서
   입력칸이 넓은 알약으로 보인다. 입력칸 자체는 배경 투명 · 큰 글씨(16px). */
.cv2-dock-say {
  padding: 8px 16px;
  border: 1px solid var(--sd-line);
  border-radius: 999px;
  background: var(--sd-bg);
  /* D3 화려함(계획서 2026-10-26, 지시서 057): 포커스 때 알약이 살짝 넓어지고(scaleX) 금색 테두리가
     스프링처럼 붙는다. transform·box-shadow 만 바꾸고, 스프링 느낌은 overshoot 큐빅 베지어로 낸다
     (라이브러리 없이 — 049 motion.js 규칙과 같은 취지). reduced-motion 이면 아래에서 전이를 끈다. */
  transition:
    transform .34s cubic-bezier(0.34, 1.4, 0.64, 1),
    box-shadow .34s cubic-bezier(0.34, 1.4, 0.64, 1),
    border-color .18s ease,
    background-color .18s ease;
  transform-origin: left center;
}
.cv2-dock-say:focus-within {
  border-color: var(--cv2-gold);
  background: var(--sd-surface);
  /* 살짝 넓어짐 — 알약 트랙이 좌우로 1.5% 늘고 금색 번짐이 생긴다(은은하게). */
  transform: scaleX(1.015);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--cv2-gold) 22%, transparent);
}
.cv2-dock-input {
  flex: 1 1 auto; min-width: 0;
  border: 0; background: transparent; color: var(--sd-ink);
  font: inherit; font-size: 16px; line-height: 1.5;
  padding: 2px 0;
}
.cv2-dock-input::placeholder { color: var(--sd-dim); }
.cv2-dock-input:focus { outline: none; }
.cv2-dock-input:disabled { color: var(--sd-dim); cursor: default; }
/* '고치는 중…' — 입력칸 오른쪽의 작은 회색 글씨. 줄어들지 않게 고정 폭. */
.cv2-dock-working {
  flex: 0 0 auto; white-space: nowrap;
  font-size: 13px; color: var(--sd-dim);
}
/* D3 "보낼 때 'AI가 쓰는 중' 띠"(계획서 2026-10-26, 지시서 057):
   AI 비서로 보내면 각 단계가 .cv2-dock-working('고치는 중…')을 도크 입력 묶음에 붙인다(state.sayBusy).
   그 상태(:has(.cv2-dock-working))일 때 입력 묶음을 'AI가 쓰는 중' 띠처럼 보이게 한다 — 입력칸을
   페이드로 접고(0 높이로 눌러 "위로 접힘" 느낌), 금색 테두리에 금색 빛이 왼쪽→오른쪽으로 흐르는
   진행 띠(::before)를 깐다. JS·DOM 은 건드리지 않는다(이미 있는 .cv2-dock-working 노드를 CSS 로만
   '띠'로 바꾼다 — 기능 유지). 띠 글자는 단계가 넣은 '고치는 중…'을 그대로 쓴다(문구 교체 없음). */
.cv2-dock-say:has(.cv2-dock-working) {
  position: relative; overflow: hidden;
  border-color: var(--cv2-gold);
  background: var(--sd-surface);
}
.cv2-dock-say:has(.cv2-dock-working) .cv2-dock-input {
  /* 입력칸이 위로 접히는 느낌 — 세로로 눌러 사라지고, 띠(working)만 남는다. */
  max-height: 0; opacity: 0; padding: 0;
  transition: max-height .24s ease, opacity .18s ease, padding .24s ease;
  overflow: hidden;
}
.cv2-dock-say:has(.cv2-dock-working) .cv2-dock-working {
  flex: 1 1 auto;
  color: var(--cv2-gold-ink); font-weight: 600;
}
/* 금색 진행 띠 — 바닥에서 왼쪽→오른쪽으로 흐른다(생성이 도는 중이라는 신호). */
.cv2-dock-say:has(.cv2-dock-working)::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: linear-gradient(
    90deg,
    transparent, var(--cv2-gold), transparent
  );
  background-size: 50% 100%; background-repeat: no-repeat;
  animation: cv2-dock-stripe 1.1s linear infinite;
  pointer-events: none;
}
@keyframes cv2-dock-stripe {
  from { background-position: -50% 0; }
  to   { background-position: 150% 0; }
}
/* 좁은 화면: 도크가 세로로 쌓일 때(위 .cv2-dock @max-width:560px) 입력·실행중·버튼이
   겹치지 않게 입력 묶음을 한 줄 전체로, 확정 버튼은 그 아래로 자연히 줄바꿈된다. */
@media (max-width: 560px) {
  .cv2-dock-say { flex-wrap: wrap; }
  .cv2-dock-working { width: 100%; }
}
/* D3 도크 효과 reduced-motion 끄기 — 넓어짐·금색 번짐·접힘·진행 띠의 '움직임'만 끈다.
   결과(금색 테두리·띠 글자)는 남겨 상태를 알 수 있게 한다(요구: reduced-motion 동작). */
@media (prefers-reduced-motion: reduce) {
  .cv2-dock-say { transition: none; }
  .cv2-dock-say:focus-within { transform: none; box-shadow: none; }
  .cv2-dock-say:has(.cv2-dock-working) .cv2-dock-input { transition: none; }
  .cv2-dock-say:has(.cv2-dock-working)::before { animation: none; }
}

/* --- 되돌리기 알림: 도크 위 가운데, 알약형, 어두운 배경 + 흰 글씨 --- */
/* 호스트는 알림이 없을 때 자리를 차지하지 않게 비운다. 알림은 fixed 로 도크 바로 위 가운데. */
.cv2-notice-host:empty { display: none; }
.cv2-notice {
  position: fixed; left: 50%; bottom: 76px; z-index: 31;
  transform: translateX(-50%);
  display: inline-flex; align-items: center; gap: 12px;
  max-width: calc(100vw - 32px);
  padding: 9px 10px 9px 18px;
  border-radius: 999px;
  background: #23232a; color: #fff;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.28);
  font-size: 14px;
  /* 등장: y 24→0 (가이드 11장 스프링값을 CSS 에 근사). */
  animation: cv2-notice-rise .34s cubic-bezier(0.22, 1, 0.36, 1);
}
/* 4초 뒤 사라질 때(JS 가 is-leaving 을 붙인다): 0.2s 페이드 + 살짝 내려감. */
.cv2-notice.is-leaving {
  animation: cv2-notice-fade .22s ease-in forwards;
}
.cv2-notice-text { white-space: nowrap; }
/* [되돌리기] = 금색 텍스트 버튼(어두운 알약 위라 투명 배경). */
.cv2-notice-undo {
  border: 0 !important; background: transparent !important;
  color: var(--cv2-gold) !important; font-weight: 700;
  padding: 4px 10px !important; border-radius: 999px;
}
.cv2-notice-undo:hover { background: rgba(255, 255, 255, 0.1) !important; }
.cv2-notice-undo:focus-visible { outline: 2px solid var(--cv2-gold); outline-offset: 1px; }
@media (max-width: 560px) {
  /* 도크가 세로로 쌓여 더 높아지므로 알림을 조금 더 띄운다. */
  .cv2-notice { bottom: 108px; }
}

@keyframes cv2-notice-rise {
  from { transform: translate(-50%, 24px); opacity: 0; }
  to   { transform: translate(-50%, 0);   opacity: 1; }
}
@keyframes cv2-notice-fade {
  from { transform: translate(-50%, 0);   opacity: 1; }
  to   { transform: translate(-50%, 8px); opacity: 0; }
}

/* --- 완료 카드(모달): 반투명 오버레이 + 가운데 카드 --- */
.cv2-complete-host:empty { display: none; }
.cv2-complete-overlay {
  position: fixed; inset: 0; z-index: 40;
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  background: rgba(20, 20, 24, 0.55);
  animation: cv2-overlay-in .2s ease;
}
.cv2-complete-card {
  width: 100%; max-width: 520px;
  box-sizing: border-box;
  padding: 28px;
  border-radius: var(--cv2-radius);
  border: 1px solid var(--sd-line);
  background: var(--sd-surface); color: var(--sd-ink);
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.3);
  text-align: left;
  /* 등장: scale .94→1 + y 16→0 (가이드 11장). */
  animation: cv2-complete-in .32s cubic-bezier(0.22, 1, 0.36, 1);
}
/* `2단계 끝` 금색 알약(이미 .cv2-step-pill 스타일을 쓰되 아래 여백만 더한다). */
.cv2-complete-pill { display: inline-block; margin-bottom: 14px; }
.cv2-complete-title { margin: 0 0 8px; font-size: 24px; font-weight: 700; line-height: 1.3; }
.cv2-complete-guide { margin: 0 0 18px; font-size: 15px; line-height: 1.6; }
/* 요약 4줄 상자(옅은 바탕). 라벨 + 34자 요약. */
.cv2-complete-summary {
  display: flex; flex-direction: column; gap: 8px;
  padding: 16px 18px; margin: 0 0 22px;
  border-radius: 12px; background: var(--sd-bg);
}
.cv2-complete-summary-row { margin: 0; font-size: 15px; line-height: 1.55; }
.cv2-complete-summary-label { color: var(--cv2-gold-ink); font-weight: 700; }
/* 주 버튼 — 검정 바탕 · 금색 강조(완료 카드에서 가장 눈에 띄게). 폭 전체. */
.cv2-complete-primary {
  display: block; width: 100%;
  padding: 14px 18px;
  border: 0; border-radius: 999px;
  background: var(--sd-ink); color: var(--sd-surface);
  font: inherit; font-size: 16px; font-weight: 700;
  cursor: pointer;
}
.cv2-complete-primary:hover { filter: brightness(1.1); }
.cv2-complete-primary:focus-visible { outline: 2px solid var(--cv2-gold); outline-offset: 2px; }

@keyframes cv2-overlay-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes cv2-complete-in {
  from { transform: translateY(16px) scale(.94); opacity: 0; }
  to   { transform: translateY(0) scale(1);      opacity: 1; }
}

/* --------------------------------------------------------------------------
   D3 화려함(계획서 2026-10-26, 지시서 057) — 완료 카드 테두리 트레이스 + 금색 입자

   doc-stage.completeCard 가 reduced-motion 이 아닐 때만 장식 노드(.cv2-complete-trace ·
   .cv2-complete-sparks > .cv2-complete-spark 7개)를 넣는다. 둘 다 **1회**만 재생되고 멈춘다
   (animation-iteration-count:1, forwards/both). 완료 카드는 확정 때 한 번 뜨므로 반복되지 않는다.
   색은 --cv2-gold 토큰이라 두 테마가 저절로 따라온다. 전부 장식이라 pointer-events:none.
   -------------------------------------------------------------------------- */
/* 카드가 relative 여야 장식이 카드 기준으로 깔린다(기존 레이아웃엔 영향 없음). */
.cv2-complete-card { position: relative; }

/* (1) 테두리 금색 선 1회 트레이스 — 카드 테두리 두께의 금색 링을 conic-gradient 로 그리고,
   그 '보이는 각도'를 0°→360° 로 키워 한 바퀴 그려지는 것처럼 보이게 한다. 안쪽은 마스크로
   파내 테두리 선만 남긴다(채우지 않는다). 0.9초에 한 바퀴 돌고 멈춘 뒤 살짝 사라진다. */
.cv2-complete-trace {
  position: absolute; inset: -1px; border-radius: inherit;
  padding: 2px; pointer-events: none;
  background: conic-gradient(
    from -90deg,
    var(--cv2-gold) 0deg,
    var(--cv2-gold) var(--cv2-trace, 0deg),
    transparent var(--cv2-trace, 0deg),
    transparent 360deg
  );
  /* 안쪽을 파내 테두리 선만 남긴다(두 테마 모두 같은 합성). */
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  animation: cv2-trace-draw .9s ease-out .1s 1 both;
}
/* 각도 변수를 애니메이트하려면 @property 로 등록해야 보간된다(미지원 브라우저는 끝값으로 점프 —
   끊겨도 테두리가 완성돼 보인다). */
@property --cv2-trace {
  syntax: "<angle>"; inherits: false; initial-value: 0deg;
}
@keyframes cv2-trace-draw {
  0%   { --cv2-trace: 0deg;   opacity: 1; }
  80%  { --cv2-trace: 360deg; opacity: 1; }
  100% { --cv2-trace: 360deg; opacity: 0; }
}

/* (2) 금색 입자 6~8개(여기선 7개) — 카드 가운데 위에서 1초 동안 흩어지며 사라진다(1회).
   입자마다 --i(0~6)로 각도·거리를 달리해 부채처럼 퍼진다. 작은 금색 점이라 장식일 뿐이다. */
.cv2-complete-sparks {
  position: absolute; left: 50%; top: 18%;
  width: 0; height: 0; pointer-events: none;
}
.cv2-complete-spark {
  position: absolute; left: 0; top: 0;
  width: 6px; height: 6px; margin: -3px;
  border-radius: 50%;
  background: var(--cv2-gold);
  /* 각 입자의 흩어질 방향·거리(--i 로 7갈래). calc 로 각도를 만들어 translate 한다. */
  --ang: calc((var(--i) * 51deg) - 153deg);
  --dist: 64px;
  opacity: 0;
  animation: cv2-spark-burst 1s cubic-bezier(0.22, 1, 0.36, 1) .1s 1 both;
}
@keyframes cv2-spark-burst {
  0%   { transform: translate(0, 0) scale(0.4);   opacity: 0; }
  20%  { opacity: 1; }
  100% {
    transform:
      translate(calc(cos(var(--ang)) * var(--dist)), calc(sin(var(--ang)) * var(--dist)))
      scale(1);
    opacity: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  /* 혹시 장식 노드가 남아도(설정을 중간에 바꾼 경우) 움직이지 않게 한다 — JS 는 애초에 안 넣는다. */
  .cv2-complete-trace, .cv2-complete-spark { animation: none; display: none; }
}

/* --- 배지 전환 모션(가이드 11장: scale .7→1) --- */
/* AI 초안 / ✓ 확인함 배지가 다시 그려질 때 살짝 튀어나오게. */
.cv2-badge { animation: cv2-badge-pop .28s cubic-bezier(0.34, 1.56, 0.64, 1); }
@keyframes cv2-badge-pop {
  from { transform: scale(.7); opacity: .4; }
  to   { transform: scale(1);  opacity: 1; }
}

/* --- 질문 전환 모션(가이드 11장: 등장 x 40→0) --- */
/* 질문 영역(aria-live)이 다시 그려질 때 오른쪽에서 밀려 들어온다. */
.cv2-q-live { animation: cv2-question-in .28s cubic-bezier(0.22, 1, 0.36, 1); }
@keyframes cv2-question-in {
  from { transform: translateX(40px); opacity: 0; }
  to   { transform: translateX(0);    opacity: 1; }
}

/* --- 문단 갱신 <mark> 모션(가이드 11장: y 6→0 + 페이드) --- */
.cv2-mark { animation: cv2-mark-in .5s cubic-bezier(0.16, 1, 0.3, 1); }
@keyframes cv2-mark-in {
  from { transform: translateY(6px); opacity: 0; }
  to   { transform: translateY(0);   opacity: 1; }
}

/* --- 모션 끄기(가이드 11장 끝: prefers-reduced-motion) --- */
/* 위 reduced-motion 블록(글자 커서 · 보기 transition)에 더해, 5/5 에서 더한 애니메이션도 끈다. */
@media (prefers-reduced-motion: reduce) {
  .cv2-notice,
  .cv2-notice.is-leaving,
  .cv2-complete-overlay,
  .cv2-complete-card,
  .cv2-badge,
  .cv2-q-live,
  .cv2-mark {
    animation: none;
  }
  /* 알림은 가운데 고정 위치만 유지(이동 없이 결과만). */
  .cv2-notice { transform: translateX(-50%); }
}


/* ==========================================================================
   2차 공용 부품(doc-stage.js) — .ds-* (키이스트 2차 개선안 2026-10-22)

   doc-stage.js 의 부품은 cast-v2 와 같은 모양이 되도록 .cv2-* 클래스를 재사용한다. 여기 .ds-*
   는 cast-v2 에 없던 '일반화' 때문에 필요한 몇 가지만 더한다 — 문서 카드 머리(docCard)의 제목
   줄 배치와, 공용 도크 입력에 필요한 최소 보정. cast-v2 화면에는 .ds-* 가 붙지 않으므로 영향이
   없다(회귀 방지).
   ========================================================================== */

/* 문서 카드 머리 — 제목(강조) + 보조 설명이 한 줄에 읽히게. cast-v2 신원 줄(.cv2-identity)과 같은
   크기 감각을 쓴다(제목 18px · 보조 회색). 032~035 가 docCard 로 그린다. */
.ds-card .ds-doc-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin-bottom: 14px;
}

/* 문단 아래 작은 링크 묶음(extraLinks) — 링크가 여럿이면 가로로, 좁으면 줄바꿈. */
.ds-para-links {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
}


/* ==========================================================================
   출발점(origin) 2차 화면 — 범위 보정 (지시서 032, 2026-10-22)

   origin-v2.js 는 doc-stage 부품을 쓰는 첫 단계다. 대부분의 모양은 공용 `.cv2-*`(머리 · 진행 막대 ·
   도크 · 문단 · 질문 카드 · 되돌리기 · 완료 카드)를 그대로 재사용하므로 추가 스타일이 거의 없다.
   여기서는 (1) docCard 머리(doc-stage 가 .cv2-doc-head 로 그린다), (2) AI 안내 문단, (3) 펼친
   AI 추천 패널 자리의 여백만 보정한다. 범위는 `.is-origin-v2`(= origin 2차 화면)로 좁혀,
   cast-v2 · 다른 단계에 영향이 없게 한다(회귀 방지).

   '덧붙일 말' 칸 숨김은 이미 위쪽 규칙이 처리한다 — origin-v2 가 `.cv2-dock` 을 그리므로
   `.sd-page-project.is-v2:has(.cv2-dock) .sd-assist-instruction { display: none }` 에 걸린다.
   ========================================================================== */

/* 문서 카드 머리 — doc-stage.docCard 가 .cv2-doc-head(강조 제목 + 보조 설명)로 그린다.
   cast-v2 신원 줄(.cv2-identity)과 같은 크기 감각(제목 18px · 보조 회색). */
.is-origin-v2 .cv2-doc-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding-bottom: 14px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--sd-line);
}

/* AI 가 보는 범위 안내(문서 카드 맨 위) — 작은 회색 안내, 위아래 여백만. */
.is-origin-v2 .ds-origin-ai-note { margin: 0 0 16px; }

/* 로그라인 문단 아래 펼쳐지는 AI 추천 패널 자리 — 문단에서 살짝 들여 구분. */
.is-origin-v2 .ds-assist-slot { margin-top: 10px; grid-column: 1 / -1; }

/* A1: 장르 · 톤 · 타겟 '고르기' 칸 — 한 줄 이야기 위, 문서 카드 첫 줄. 아래 문단과 선으로 구분한다.
   칩 자체(.sd-chip · .sd-chips · .sd-story-chip-group)와 고르기 칸(.ds-choose)은 공용 스타일을 쓴다
   (story-v2 와 같은 모양). 여기서는 묶음 머리 여백과 아래 구분선만 origin 범위에서 보정한다. */
.is-origin-v2 .ds-origin-chips {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0 0 16px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--sd-line);
}
.is-origin-v2 .ds-origin-chips > .ds-choose-head { margin-bottom: 2px; }
.is-origin-v2 .ds-choose {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 6px 0 2px;
}
.is-origin-v2 .ds-choose-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 8px;
}

/* 전문가 보기(옛 origin.js)의 장르 · 톤 · 타겟 고르기 칸 — 기존 .sd-field 리듬 안에서 칩 묶음만 세로로 쌓는다. */
.ds-origin-chips .ds-choose { display: flex; flex-direction: column; gap: 8px; margin: 8px 0 0; }
.ds-origin-chips .ds-choose-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 8px; }



/* ==========================================================================
   목표·동기(motive) 2차 화면 — 범위 보정 (지시서 033, 2026-10-22)

   motive-v2.js 는 origin-v2 처럼 doc-stage 부품을 쓴다. 대부분의 모양은 공용 `.cv2-*`(머리 ·
   진행 막대 · 인물 세그먼트 · 문단 · 질문 카드 · 되돌리기 · 완료 카드)를 그대로 재사용한다.
   origin 과 다른 점은 (1) 인물이 여럿이라 세그먼트가 있고(= cast-v2 .cv2-people 재사용),
   (2) 도크에 '다음 할 일' 칩이 입력줄 위에 한 줄 더 있다는 것뿐이다. 그래서 여기서는 도크를
   세로 두 줄(칩 줄 + 입력 줄)로 눕히는 것과, 참고 묶음·초안 안내·행동 쌍 여백만 보정한다.
   범위는 `.is-motive-v2`(= motive 2차 화면)로 좁혀, cast-v2 · origin-v2 · 다른 단계에 영향이
   없게 한다(회귀 방지).

   '덧붙일 말' 칸 숨김은 이미 위쪽 규칙이 처리한다 — motive-v2 가 `.cv2-dock` 을 그리므로
   `.sd-page-project.is-v2:has(.cv2-dock) .sd-assist-instruction { display: none }` 에 걸린다.
   ========================================================================== */

/* 문서 카드 머리 — doc-stage.docCard 가 .cv2-doc-head(강조 제목 + 보조 설명)로 그린다
   (origin-v2 와 같은 보정). cast-v2 신원 줄과 같은 크기 감각(제목 18px · 보조 회색). */
.is-motive-v2 .cv2-doc-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding-bottom: 14px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--sd-line);
}

/* 참고 묶음(인물 카드 · 정해 둔 배경 설정) — 문단 사이 작은 details. 위아래 여백만. */
.is-motive-v2 .ds-ref { display: flex; flex-direction: column; gap: 8px; margin-bottom: 6px; }
.is-motive-v2 .ds-ref-block > summary { cursor: pointer; color: var(--sd-dim); font-size: 14px; }

/* 빈 인물 'AI 초안 받기' 안내 — 한 줄에 글 + 버튼. */
.is-motive-v2 .ds-draft-prompt {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  padding: 10px 0;
}

/* 단기목표 아래 펼쳐지는 AI 추천 패널 자리 — 문단에서 살짝 들여 구분(origin-v2 와 같다). */
.is-motive-v2 .ds-assist-slot { margin-top: 10px; grid-column: 1 / -1; }

/* 행동 → 동기 한 쌍 — '행동' 문단 + '이유' 문단을 한 묶음으로 묶어 사이 여백을 좁힌다. */
.is-motive-v2 .ds-motive-link-block {
  display: flex; flex-direction: column; gap: 10px;
  padding-top: 4px;
}

/* 도크 — 칩 줄(위) + 입력 줄(아래)로 눕힌다. 공용 .cv2-dock 은 가로 한 줄이라 세로로 바꾼다. */
.is-motive-v2 .cv2-dock-motive { flex-direction: column; align-items: stretch; gap: 8px; }
.is-motive-v2 .cv2-dock-row { display: flex; align-items: center; gap: 12px; }
/* 칩 줄은 왼쪽 정렬 · 위 여백 없음(도크 안이라 좁게). */
.is-motive-v2 .cv2-dock-motive .sd-suggest { margin: 0; }


/* ==========================================================================
   배경 설정(story) 2차 화면 — 범위 보정 (지시서 034, 2026-10-22)

   story-v2.js 는 origin-v2 · motive-v2 처럼 doc-stage 부품을 쓴다. 대부분의 모양은 공용 `.cv2-*`
   (머리 · 진행 막대 · 문단 · 질문 카드 · 되돌리기 · 완료 카드)를 그대로 재사용한다. story 만의
   다른 점은 (1) 글로 안 쓰고 목록에서 고르는 값이 많아 '고르기' 칸(.ds-choose: 플롯 select ·
   장르/톤/편성/타겟 칩 · 편성 분량 숫자)이 문단 사이에 들어가고, (2) 도크에 '다음 할 일' 칩이
   입력줄 위에 한 줄 더 있다는 것(motive-v2 와 같은 두 줄 도크)뿐이다. 그래서 여기서는 문서 카드
   머리 · 안내 문단 · '고르기' 칸 레이아웃 · 펼친 추천 패널 자리 · 도크 두 줄만 보정한다. 범위는
   `.is-story-v2`(= story 2차 화면)로 좁혀, cast-v2 · origin-v2 · motive-v2 · 다른 단계에 영향이
   없게 한다(회귀 방지).

   '덧붙일 말' 칸 숨김은 이미 위쪽 규칙이 처리한다 — story-v2 가 `.cv2-dock` 을 그리므로
   `.sd-page-project.is-v2:has(.cv2-dock) .sd-assist-instruction { display: none }` 에 걸린다.
   ========================================================================== */

/* 문서 카드 머리 — doc-stage.docCard 가 .cv2-doc-head(강조 제목 + 보조 설명)로 그린다
   (origin-v2 · motive-v2 와 같은 보정). */
.is-story-v2 .cv2-doc-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding-bottom: 14px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--sd-line);
}

/* AI 가 보는 범위 · 임시 목록 안내(문서 카드 맨 위) — 작은 회색 안내, 아래 여백만. */
.is-story-v2 .ds-story-note { margin: 0 0 12px; }

/* 고르기 칸 묶음 머리(장르·톤앤매너 / 편성 정보·타겟) — 문단과 구분되는 작은 구역 제목. */
.is-story-v2 .ds-story-section { margin: 18px 0 10px; }

/* '고르기' 칸 — 목록에서 고르는 값(플롯 · 장르 · 톤 · 편성 · 타겟 · 분량). 문단 사이에 들어가므로
   문단과 같은 세로 리듬을 쓰되, 왼쪽에 살짝 들여 '고르는 자리'임을 보인다. */
.is-story-v2 .ds-choose {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 10px 0 14px;
}

/* 고르기 칸 머리 — 라벨(강조) + 제한 안내 + '임시'/'확인함' 배지가 한 줄에. */
.is-story-v2 .ds-choose-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 8px;
}

/* 플롯 select 3개 · 편성 분량 숫자 칸 — 한 줄에 눕히되 좁으면 줄바꿈(.sd-row 기본값을 story 범위에서 그대로 쓴다). */
.is-story-v2 .ds-choose-row { gap: 12px; }
.is-story-v2 .ds-choose-field { min-width: 180px; }

/* 펼쳐지는 AI 추천 패널 자리 — 문단/칸에서 살짝 들여 구분(origin-v2 · motive-v2 와 같다). */
.is-story-v2 .ds-assist-slot { margin-top: 10px; grid-column: 1 / -1; }

/* 도크 — 칩 줄(위) + 입력 줄(아래)로 눕힌다(motive-v2 와 같은 두 줄 도크). 공용 .cv2-dock 은
   가로 한 줄이라 세로로 바꾼다. */
.is-story-v2 .cv2-dock-story { flex-direction: column; align-items: stretch; gap: 8px; }
.is-story-v2 .cv2-dock-row { display: flex; align-items: center; gap: 12px; }
/* 칩 줄은 왼쪽 정렬 · 위 여백 없음(도크 안이라 좁게). */
.is-story-v2 .cv2-dock-story .sd-suggest { margin: 0; }


/* ==========================================================================
   다듬기(finalize) 2차 화면 — 범위 보정 (지시서 035, 2026-10-22)

   finalize-v2.js 는 origin-v2 처럼 doc-stage 부품을 쓴다. 대부분의 모양은 공용 `.cv2-*`(머리 ·
   진행 막대 · 도크 · 문단 · 질문 카드 · 되돌리기 · 완료 카드)를 그대로 재사용한다. origin 과 다른
   점은 (1) 확정 한 줄 이야기 문단 아래 확정 버튼/배지 줄, (2) 문서 카드 안에 '인물 단위 확정'
   표 · '캐릭터 바이블' 받기 · 배경·점검 요약 같은 블록이 더 있고, (3) 도크에 '다음 할 일' 칩이
   입력줄 위에 한 줄 더 있다는 것뿐이다. 범위는 `.is-finalize-v2`(= finalize 2차 화면)로 좁혀,
   cast-v2 · origin-v2 · motive-v2 · story-v2 · 다른 단계에 영향이 없게 한다(회귀 방지).

   '덧붙일 말' 칸 숨김은 이미 위쪽 규칙이 처리한다 — finalize-v2 가 `.cv2-dock` 을 그리므로
   `.sd-page-project.is-v2:has(.cv2-dock) .sd-assist-instruction { display: none }` 에 걸린다.
   ========================================================================== */

/* 문서 카드 머리 — doc-stage.docCard 가 .cv2-doc-head(강조 제목 + 보조 설명)로 그린다
   (origin-v2 와 같은 보정). */
.is-finalize-v2 .cv2-doc-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding-bottom: 14px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--sd-line);
}

/* 로그라인 문단 아래 펼쳐지는 AI 추천 패널 자리 — 문단에서 살짝 들여 구분(origin-v2 와 같다). */
.is-finalize-v2 .ds-assist-slot { margin-top: 10px; grid-column: 1 / -1; }

/* 로그라인 확정 상태 줄(확정 버튼 · 확정 배지) — 문단 아래 한 줄에 버튼 + 배지. */
.is-finalize-v2 .ds-logline-status {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px 12px;
  margin-top: 6px;
}

/* 문서 카드 안 블록(인물 단위 확정 · 캐릭터 바이블) — 문단 사이 작은 구역. 위에 옅은 구분선. */
.is-finalize-v2 .ds-final-block {
  margin: 18px 0 10px;
  padding-top: 16px;
  border-top: 1px solid var(--sd-line);
}
/* 블록 제목 — 문단 라벨과 같은 크기 감각으로 굵게. */
.is-finalize-v2 .ds-block-label { display: block; margin: 0 0 8px; font-weight: 700; }

/* 참고 묶음(정해 둔 배경 설정 · 점검 결과) — 문단 사이 작은 details + 안내(origin · motive 와 같은 감각). */
.is-finalize-v2 .ds-ref { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.is-finalize-v2 .ds-ref-block > summary { cursor: pointer; color: var(--sd-dim); font-size: 14px; }
.is-finalize-v2 .ds-checks-note {
  display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px; margin: 6px 0 0;
}

/* 도크 — 칩 줄(위) + 입력 줄(아래)로 눕힌다(motive-v2 · story-v2 와 같은 두 줄 도크). */
.is-finalize-v2 .cv2-dock-finalize { flex-direction: column; align-items: stretch; gap: 8px; }
.is-finalize-v2 .cv2-dock-row { display: flex; align-items: center; gap: 12px; }
/* 칩 줄은 왼쪽 정렬 · 위 여백 없음(도크 안이라 좁게). */
.is-finalize-v2 .cv2-dock-finalize .sd-suggest { margin: 0; }



/* ==========================================================================
   전체 줄거리(synopsis) 2차 화면 — 범위 보정 (지시서 036, 2026-10-22)

   synopsis-v2.js 는 origin · story · finalize 2차처럼 doc-stage 부품을 쓴다. 대부분의 모양은
   공용 `.cv2-*`(머리 · 진행 막대 · 문단 · 질문 카드 · 되돌리기 · 완료 카드)와 story-v2 가 쓰는
   `.ds-choose-field`(문단 수 select) · finalize-v2 가 쓰는 `.ds-final-block`/`.ds-block-label`
   (검수 메모 블록)을 그대로 재사용한다. synopsis 만의 다른 점은 (1) 비어 있을 때 생성 카드
   (문단 수 고르기 + 큰 '만들기' 버튼), (2) 생성/적용 스트리밍 진행 표시 + 멈추기, (3) 문단마다
   '이 문단만 다시 쓰기' 줄과 글자 수 배지, (4) 도크에 '다음 할 일' 칩이 입력줄 위에 한 줄 더
   있다는 것(finalize-v2 와 같은 두 줄 도크)뿐이다. 범위는 `.is-synopsis-v2`(= synopsis 2차
   화면)로 좁혀, cast-v2 · 다른 단계에 영향이 없게 한다(회귀 방지).

   '덧붙일 말' 칸 숨김은 이미 위쪽 규칙이 처리한다 — synopsis-v2 가 `.cv2-dock` 을 그리므로
   `.sd-page-project.is-v2:has(.cv2-dock) .sd-assist-instruction { display: none }` 에 걸린다.
   ========================================================================== */

/* 문서 카드 머리 — doc-stage.docCard 가 .cv2-doc-head(강조 제목 + 보조 설명)로 그린다
   (origin-v2 · finalize-v2 와 같은 보정). */
.is-synopsis-v2 .cv2-doc-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding-bottom: 14px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--sd-line);
}

/* 안내 문단(문서 카드 맨 위) — 작은 회색 안내, 아래 여백만. */
.is-synopsis-v2 .ds-synopsis-note { margin: 0 0 14px; }

/* 비어 있을 때 생성 카드 — 문단 수 고르기 + 큰 '만들기' 버튼. 가운데로 모아 '여기서 시작'을 보인다. */
.is-synopsis-v2 .ds-synopsis-empty {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 10px 0;
}
/* 문단 수 select + '만들기' 버튼을 한 줄에(좁으면 줄바꿈). */
.is-synopsis-v2 .ds-synopsis-gen-row {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 4px;
}
/* 문단 수 select 묶음(story-v2 .ds-choose-field 재사용) — 최소 폭만 보장. */
.is-synopsis-v2 .ds-choose-field { min-width: 180px; }
/* '전체 줄거리 만들기' 큰 버튼 — 줄바꿈 금지. */
.is-synopsis-v2 .ds-synopsis-gen-button { flex: 0 0 auto; white-space: nowrap; }

/* 생성/적용 진행 표시 — 돌지 않으면 자리만(aria-live). 돌 때(is-running) 한 줄 + 멈추기. */
.is-synopsis-v2 .ds-synopsis-progress { margin: 0 0 6px; }
.is-synopsis-v2 .ds-synopsis-progress.is-running {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 14px;
  padding: 10px 12px;
  background: var(--cv2-gold-soft, var(--sd-bg));
  border: 1px solid var(--sd-line);
  border-radius: 10px;
}
.is-synopsis-v2 .ds-synopsis-progress .sd-status-line {
  display: flex; align-items: center; gap: 8px; margin: 0;
}
.is-synopsis-v2 .ds-synopsis-stop { flex: 0 0 auto; }
/* 빈 상태에서 보여 줄 때는 멈추기를 조금 크게(유일한 동작이므로). */
.is-synopsis-v2 .ds-synopsis-stop-big { align-self: flex-start; }

/* 문단 머리의 글자 수 배지 — 라벨 옆 작은 회색 숫자. */
.is-synopsis-v2 .ds-synopsis-count { color: var(--sd-dim); font-size: 13px; }

/* 문단 아래 동작 줄('이 문단만 다시 쓰기' + '…') — 공용 .ds-para-links(가로·줄바꿈) 위 여백만. */
.is-synopsis-v2 .ds-synopsis-para-actions { margin-top: 6px; }

/* 다시 쓴 문단 — 방금 스트리밍으로 갈아 끼운 문단을 잠깐 강조(배경 옅게). */
.is-synopsis-v2 .ds-para.is-rewritten .ds-para-body {
  background: var(--cv2-gold-soft, var(--sd-bg));
  border-radius: 10px;
}

/* --------------------------------------------------------------------------
   B1 기승전결 2단계(큰 틀 ↔ 하나씩) — 보기 전환 · 큰 틀 개요 · 단락 진행 막대
   (지시서 052, 2026-10-26). 색은 테마 변수만 쓰고, 모션은 reduced-motion 을 따른다.
   -------------------------------------------------------------------------- */

/* 큰 틀 ↔ 하나씩 보기 전환 — 두 버튼을 붙여 세그먼트처럼. 켜진 쪽만 금색 테두리. */
.is-synopsis-v2 .ds-synopsis-viewswitch {
  display: inline-flex;
  gap: 6px;
  margin: 2px 0 12px;
}
.is-synopsis-v2 .ds-synopsis-view-btn.is-on {
  border-color: var(--cv2-gold, var(--sd-accent));
  color: var(--cv2-gold, var(--sd-accent));
  font-weight: 600;
}

/* 큰 틀(개요) — 기·승·전·결 네 줄을 한눈에. 각 줄은 누르면 '하나씩'으로 들어간다. */
.is-synopsis-v2 .ds-synopsis-outline { margin: 0 0 6px; }
.is-synopsis-v2 .ds-synopsis-outline-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 8px;
}
.is-synopsis-v2 .ds-synopsis-outline-row {
  display: grid;
  grid-template-columns: 40px 1fr auto;
  align-items: start;
  gap: 10px;
  width: 100%;
  text-align: left;
  padding: 10px 12px;
  background: var(--sd-bg);
  border: 1px solid var(--sd-line);
  border-radius: 10px;
  cursor: pointer;
  color: inherit;
}
.is-synopsis-v2 .ds-synopsis-outline-row:hover:not([disabled]) {
  border-color: var(--cv2-gold, var(--sd-accent));
}
.is-synopsis-v2 .ds-synopsis-outline-row[disabled] { cursor: default; opacity: 0.7; }
.is-synopsis-v2 .ds-synopsis-outline-label {
  font-weight: 700;
  color: var(--cv2-gold, var(--sd-accent));
}
.is-synopsis-v2 .ds-synopsis-outline-body { line-height: 1.5; }
.is-synopsis-v2 .ds-synopsis-outline-body.is-empty { color: var(--sd-dim); }

/* 하나씩 — 기·승·전·결 사이 진행 막대(이전/다음 + 네 칸). */
.is-synopsis-v2 .ds-synopsis-stepper {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 2px 0 12px;
  flex-wrap: wrap;
}
.is-synopsis-v2 .ds-synopsis-step-dots { display: flex; gap: 6px; }
.is-synopsis-v2 .ds-synopsis-step-dot {
  min-width: 36px;
  padding: 4px 8px;
  border: 1px solid var(--sd-line);
  border-radius: 999px;
  background: var(--sd-bg);
  color: var(--sd-dim);
  cursor: pointer;
}
.is-synopsis-v2 .ds-synopsis-step-dot.is-filled { color: inherit; }
.is-synopsis-v2 .ds-synopsis-step-dot.is-current {
  border-color: var(--cv2-gold, var(--sd-accent));
  color: var(--cv2-gold, var(--sd-accent));
  font-weight: 700;
}
.is-synopsis-v2 .ds-synopsis-step-dot[disabled] { cursor: default; }

/* 검수 메모 블록(finalize-v2 .ds-final-block/.ds-block-label 재사용) — 목록 여백만. */
.is-synopsis-v2 .ds-synopsis-notes-list { margin: 0; padding-left: 18px; }
.is-synopsis-v2 .ds-synopsis-notes-list li { margin: 4px 0; }

/* 도크 — 칩 줄(위) + 입력 줄(아래)로 눕힌다(finalize-v2 · story-v2 와 같은 두 줄 도크). */
.is-synopsis-v2 .cv2-dock-synopsis { flex-direction: column; align-items: stretch; gap: 8px; }
.is-synopsis-v2 .cv2-dock-row { display: flex; align-items: center; gap: 12px; }
/* 칩 줄은 왼쪽 정렬 · 위 여백 없음(도크 안이라 좁게). */
.is-synopsis-v2 .cv2-dock-synopsis .sd-suggest { margin: 0; }

/* --------------------------------------------------------------------------
   uiux-261029-d(2026-10-30) — 전체 줄거리 큰 틀 요약
   (키이스트 UI/UX 개선 의견 261029 항목 12 — '큰 틀'에서 '하나씩'을 짧게 요약한 내용을 본다)

   - 줄(.ds-synopsis-outline-item)은 누르는 줄(버튼) + 그 아래 요약 상태 줄이다. 상태 줄의 '요약 다시 만들기'는 버튼이라 줄 버튼
     밖에 둔다(버튼 안에 버튼을 둘 수 없다). 줄 사이 간격은 목록(.ds-synopsis-outline-list gap)이 그대로 맡는다.
   - 요약 본문은 3줄에서 말줄임 — 옛 5문단처럼 한 단락에 두 문단이 접혀 두 요약을 이어도 줄 높이가 같다(요구 "2~3줄").
   - 요약이 없어 첫 문장들을 보일 때(.is-fallback)는 흐린 글자 — 요약과 앞부분을 한눈에 가른다.
   - 상태 줄은 본문 칸에 맞춰 왼쪽을 띄운다(라벨 칸 40px + 간격 10px + 줄 안 여백 12px).
   - 색은 테마 토큰만, 새 움직임 없음(두 테마 · reduced-motion 영향 없음). 088 의 줄 바탕 · 제목 색 규칙(UF6)은 그대로다.
   -------------------------------------------------------------------------- */
.is-synopsis-v2 .ds-synopsis-outline-item { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.is-synopsis-v2 .ds-synopsis-outline-body {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
  min-width: 0;
  overflow-wrap: anywhere;
}
.is-synopsis-v2 .ds-synopsis-outline-body.is-fallback { color: var(--sd-dim); }
.is-synopsis-v2 .ds-synopsis-summary-state {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
  margin: 0 0 0 62px;
  font-size: 13px;
  color: var(--sd-dim);
}
@media (max-width: 560px) {
  .is-synopsis-v2 .ds-synopsis-summary-state { margin-left: 12px; }
}

/* --------------------------------------------------------------------------
   synopsis-stepwise(2026-10-31) — 전체 줄거리 단락별 한 줄
   (작업 093 — "각 문단에 대해서 첫 문장을 먼저 한 줄로 만들고, 자세히 만들거나 한 줄이라도 확정하면 다음 단계로")

   - 단락 카드(.ds-step-card): 머리(이름 · 태그 · 상태 배지) → 한 줄 칸 → 본문(있을 때 — doc-stage 문단 그대로) → 버튼 줄. 잠긴
     카드는 흐린 테두리 · 흐린 글자이고 이유 한 줄(.ds-step-lock-note)을 보인다. 상태는 배지 글자로도 말한다(색만이 아니다).
   - 한 줄 칸 글자 수: 120자를 넘으면 경고 색, 200자를 넘으면 위험 색(저장하지 않는다 — 이유는 칸 아래 글).
   - '큰 틀' 줄은 089 줄 그대로에 상태 글자(.ds-step-mark) 한 칸을 더한다 — 단락별 줄거리에서만(.ds-step-outline 안).
   - 좁은 문서 칸(왼쪽 단 안쪽 600px 미만 — 088 컨테이너 질의와 같은 기준)과 560px 화면: 버튼 줄은 줄바꿈하고 주 버튼은 한 줄 전체,
     진행 막대 네 칸은 글자만 줄인다. 글자는 12px 아래로 내려가지 않는다.
   - 색은 테마 토큰만(대체값 없이 — 두 테마 값은 087~091b 가 정했다), 새 움직임 없음(reduced-motion 영향 없음).
   -------------------------------------------------------------------------- */
.is-synopsis-v2 .ds-step-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px 16px;
  border: 1px solid var(--sd-line);
  border-radius: 12px;
  background: var(--sd-surface);
  min-width: 0;
}
.is-synopsis-v2 .ds-step-card.is-locked { background: var(--sd-bg); color: var(--sd-dim); }
.is-synopsis-v2 .ds-step-head { flex-direction: row; align-items: center; flex-wrap: wrap; gap: 6px 10px; }
.is-synopsis-v2 .ds-step-tag { font-size: 13px; }
.is-synopsis-v2 .ds-step-badge.is-line-done { background: var(--sd-success-tint); }
.is-synopsis-v2 .ds-step-line { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.is-synopsis-v2 .ds-step-line-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.is-synopsis-v2 .ds-step-line-label { font-weight: 600; font-size: 14px; color: var(--sd-ink); }
.is-synopsis-v2 .ds-step-count { font-size: 12px; color: var(--sd-dim); white-space: nowrap; }
.is-synopsis-v2 .ds-step-count.is-over-soft { color: var(--sd-warn); font-weight: 600; }
.is-synopsis-v2 .ds-step-count.is-over-max { color: var(--sd-danger); font-weight: 700; }
.is-synopsis-v2 .ds-step-line-input {
  width: 100%;
  min-height: 3.2em;
  resize: vertical;
  font-size: 15px;
  line-height: 1.6;
  color: var(--sd-ink);
  background: var(--sd-surface);
  border: 1px solid var(--sd-line);
  border-radius: 10px;
  padding: 8px 10px;
  box-sizing: border-box;
}
.is-synopsis-v2 .ds-step-line-input:focus-visible { outline: 2px solid var(--cv2-gold); outline-offset: 1px; border-color: var(--cv2-gold); }
.is-synopsis-v2 .ds-step-line-text { margin: 0; font-size: 15px; line-height: 1.6; overflow-wrap: anywhere; }
.is-synopsis-v2 .ds-step-line-note:empty,
.is-synopsis-v2 .ds-step-save:empty { display: none; }
.is-synopsis-v2 .ds-step-line-note { margin: 0; color: var(--sd-danger); font-size: 13px; }
.is-synopsis-v2 .ds-step-save { font-size: 12px; color: var(--sd-dim); }
.is-synopsis-v2 .ds-step-lock-note { margin: 0; font-size: 14px; color: var(--sd-dim); }
.is-synopsis-v2 .ds-step-stale { margin: 0; font-size: 13px; color: var(--sd-warn); }
.is-synopsis-v2 .ds-step-denied { margin: 0; font-size: 13px; color: var(--sd-dim); }
.is-synopsis-v2 .ds-step-all-done {
  margin: 0;
  padding: 8px 10px;
  border: 1px solid var(--sd-success);
  border-radius: 10px;
  background: var(--sd-success-tint);
  color: var(--sd-success);
  font-size: 14px;
}
.is-synopsis-v2 .ds-step-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.is-synopsis-v2 .ds-step-progress { margin: 0 0 6px; }
.is-synopsis-v2 .ds-step-busy { margin: 0 0 8px; }
.is-synopsis-v2 .ds-step-dot.is-done,
.is-synopsis-v2 .ds-step-dot.is-lineDone { color: var(--sd-success); border-color: var(--sd-success); }
.is-synopsis-v2 .ds-step-dot.is-locked { color: var(--sd-dim); border-style: dashed; }
.is-synopsis-v2 .ds-step-dot.is-current { border-color: var(--cv2-gold); color: var(--sd-ink); background: var(--cv2-gold-soft); }
.is-synopsis-v2 .ds-step-outline .ds-synopsis-outline-row { grid-template-columns: 40px minmax(0, 1fr) auto auto; }
.is-synopsis-v2 .ds-step-mark { align-self: start; }
.is-synopsis-v2 .ds-step-mark.is-done,
.is-synopsis-v2 .ds-step-mark.is-line-done { border-color: var(--sd-success); color: var(--sd-success); }
.is-synopsis-v2 .ds-step-mark.is-writing { border-color: var(--cv2-gold); color: var(--sd-ink); }
.is-synopsis-v2 .ds-step-mark.is-locked,
.is-synopsis-v2 .ds-step-mark.is-empty { border-color: var(--sd-line); color: var(--sd-dim); }
.is-synopsis-v2 .ds-step-mode-row {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  margin-top: 4px;
  padding-top: 12px;
  border-top: 1px solid var(--sd-line);
}
.is-synopsis-v2 .ds-step-start { display: flex; flex-direction: column; gap: 10px; }
/* '단락별로 처음부터 쓰기' 확인 줄(093-⑤) — 버튼 자리에서 펼친다: 안내 한 줄 + '처음부터 쓰기'(주) · '취소'. 브라우저 확인 창 대신이다.
   금색 테두리 · 옅은 금색 바탕 · 본문 글자는 진행 막대 지금 칸(.ds-step-dot.is-current)과 같은 토큰 짝이라 두 테마 대비가 같다. 움직임 없음. */
.is-synopsis-v2 .ds-step-ask {
  padding: 12px;
  border: 1px solid var(--cv2-gold);
  border-radius: 10px;
  background: var(--cv2-gold-soft);
}
.is-synopsis-v2 .ds-step-ask-text { margin: 0; font-size: 14px; line-height: 1.6; color: var(--sd-ink); overflow-wrap: anywhere; }
.is-synopsis-v2 .ds-step-ask-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; align-self: stretch; }
@container cv2-doc (max-width: 599.98px) {
  .is-synopsis-v2 .ds-step-actions > .sd-button-primary { flex: 1 1 100%; }
  .is-synopsis-v2 .ds-step-ask-actions > .sd-button-primary { flex: 1 1 100%; }
  .is-synopsis-v2 .ds-step-dot { min-width: 0; padding: 4px 6px; font-size: 13px; }
  .is-synopsis-v2 .ds-step-outline .ds-synopsis-outline-row { grid-template-columns: 32px minmax(0, 1fr); }
  .is-synopsis-v2 .ds-step-outline .ds-synopsis-count,
  .is-synopsis-v2 .ds-step-outline .ds-step-mark { grid-column: 2; justify-self: start; }
}
@media (max-width: 560px) {
  .is-synopsis-v2 .ds-step-card { padding: 12px; }
  .is-synopsis-v2 .ds-step-actions > .sd-button-primary { flex: 1 1 100%; }
  .is-synopsis-v2 .ds-step-ask { padding: 10px; }
  .is-synopsis-v2 .ds-step-ask-actions > .sd-button-primary { flex: 1 1 100%; }
  .is-synopsis-v2 .ds-step-dot { min-width: 0; padding: 4px 6px; font-size: 13px; }
  .is-synopsis-v2 .ds-step-line-input { font-size: 14px; }
  .is-synopsis-v2 .ds-step-outline .ds-synopsis-outline-row { grid-template-columns: 32px minmax(0, 1fr); }
  .is-synopsis-v2 .ds-step-outline .ds-synopsis-count,
  .is-synopsis-v2 .ds-step-outline .ds-step-mark { grid-column: 2; justify-self: start; }
}


/* ==========================================================================
   회차별 줄거리(treatment) 2차 화면 — 범위 보정 (지시서 039, 1/2 + 2/2, 2026-10-22~23)

   treatment-v2.js 는 finalize · synopsis 2차처럼 doc-stage 부품을 쓴다. 대부분의 모양은
   공용 `.cv2-*`(머리 · 진행 막대 · 세그먼트 탭 · 문단 · 질문 카드 · 되돌리기 · 완료 카드)와
   finalize-v2 가 쓰는 `.ds-final-block`/`.ds-block-label`(블록)을 그대로 재사용한다. treatment
   만의 다른 점은 (1) 회차 가로 탭의 **상태 점**(없음 · 만드는 중 · 실패 · 초안 · 확정), (2) 머리
   아래 **진행 띠**(구조표 SSE · 회차 상세 잡), (3) 회차 문단 아래 보조(엔딩 훅 · 사건 · 확정 줄),
   (4) 두 줄 도크(칩 + 입력)뿐이다. 범위는 `.is-treatment-v2` 로 좁혀 다른 단계에 영향이 없게 한다.

   '덧붙일 말' 칸 숨김은 이미 위쪽 규칙이 처리한다 — treatment-v2 가 `.cv2-dock` 을 그리므로
   `.sd-page-project.is-v2:has(.cv2-dock) .sd-assist-instruction { display: none }` 에 걸린다.

   기존 1차 클래스 재사용: `.sd-job` · `.sd-progress` · `.sd-spinner` · `.sd-chips`(진행 띠 안),
   `.sd-episode-minutes` · `.sd-minutes-input`(회차 분 조정 — 039b). 재정의하지 않는다.
   ========================================================================== */

/* 문서 카드 머리 — doc-stage.docCard 가 .cv2-doc-head(강조 제목 + 보조 설명)로 그린다
   (finalize-v2 · synopsis-v2 와 같은 보정). */
.is-treatment-v2 .cv2-doc-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding-bottom: 14px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--sd-line);
}

/* 문서 카드 안 블록(회차 상세 만들기 · 검수 메모) — finalize-v2 .ds-final-block 과 같은 감각. */
.is-treatment-v2 .ds-final-block {
  margin: 18px 0 10px;
  padding-top: 16px;
  border-top: 1px solid var(--sd-line);
}
.is-treatment-v2 .ds-block-label { display: block; margin: 0 0 8px; font-weight: 700; }

/* 비어 있을 때 생성 카드 — 큰 '회차 구조표 만들기' 버튼. 가운데로 모아 '여기서 시작'을 보인다. */
.is-treatment-v2 .ds-empty-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 22px;
}
.is-treatment-v2 .ds-big-button { align-self: flex-start; white-space: nowrap; }

/* 시리즈 합계 한 줄 — 문단 사이 작은 회색 메타. */
.is-treatment-v2 .ds-series-line { margin: 0 0 4px; }

/* --- 회차 가로 탭(상태 점) — 세그먼트(.cv2-people/.cv2-person) 재사용 + 점 --- */

/* 회차 탭을 바꿀 때 scrollIntoView(block:start) 가 탭을 화면 맨 위로 보내는데, sticky 머리·진행
   막대가 그 위를 덮으므로(D0) 그만큼 아래로 멈추도록 scroll-margin-top 을 둔다(요구 4).
   덮는 높이 = 상단바(--cv2-topbar) + 머리(--cv2-sticky-head) + 진행 막대(약 52px). */
.ds-ep-tabs { scroll-margin-top: calc(var(--cv2-topbar, 0px) + var(--cv2-sticky-head, 96px) + 52px); }

/* 탭 안의 번호와 점을 한 줄에. */
.is-treatment-v2 .ds-ep-tab { display: inline-flex; align-items: center; gap: 6px; }

/* 고른 회차 탭 — 가장자리를 금색으로 살짝(선택 상태는 체크박스 대신 이 표시로). */
.is-treatment-v2 .ds-ep-tab.is-picked:not(.is-active) {
  border-color: var(--cv2-gold, var(--sd-ink));
  box-shadow: inset 0 0 0 1px var(--cv2-gold, var(--sd-ink));
}

/* 상태 점 — 없음(빈 원) · 만드는 중(금색, 깜빡) · 실패(빨강) · 초안(회색 채움) · 상위 변경됨(주황) ·
   확정(초록). 색은 기존 배지 색 감각과 맞춘다. */
.is-treatment-v2 .ds-ep-dot {
  width: 9px; height: 9px; border-radius: 999px;
  border: 1.5px solid var(--sd-dim);
  background: transparent;
  flex: 0 0 auto;
}
/* 활성 탭 안에서는 점 테두리를 밝게(배경이 어두워지므로). */
.is-treatment-v2 .cv2-person.is-active .ds-ep-dot { border-color: currentColor; }

.is-treatment-v2 .ds-ep-dot.is-none { background: transparent; }
.is-treatment-v2 .ds-ep-dot.is-draft { background: var(--sd-dim); border-color: var(--sd-dim); }
.is-treatment-v2 .ds-ep-dot.is-running {
  background: var(--cv2-gold, #d4a017);
  border-color: var(--cv2-gold, #d4a017);
  animation: ds-ep-pulse 1.1s ease-in-out infinite;
}
/* 실패 · 상위 변경됨 · 확정 점은 위험 · 경고 · 성공 토큰(uiux-261029-e 091b — 예전 리터럴 #c0392b · #e08b2f · #2e8b57 은 어두운
   테마를 따르지 않았다). 밝은 테마는 같은 색상에서 조금 진하다(밝은 바탕 위 5.0~5.1:1). */
.is-treatment-v2 .ds-ep-dot.is-failed { background: var(--sd-danger); border-color: var(--sd-danger); }
.is-treatment-v2 .ds-ep-dot.is-stale { background: var(--sd-warn); border-color: var(--sd-warn); }
.is-treatment-v2 .ds-ep-dot.is-confirmed { background: var(--sd-success); border-color: var(--sd-success); }

@keyframes ds-ep-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}
/* 모션 줄이기 존중 — 깜빡임을 끈다. */
@media (prefers-reduced-motion: reduce) {
  .is-treatment-v2 .ds-ep-dot.is-running { animation: none; }
}

/* --- 머리 아래 진행 띠(구조표 SSE · 회차 상세 잡) --- */

/* 돌지 않으면 자리만(aria-live). 내용이 있을 때만 상자처럼 보인다. */
.is-treatment-v2 .ds-band:empty { display: none; }
.is-treatment-v2 .ds-band {
  margin: 0 0 14px;
}
.is-treatment-v2 .ds-band-row {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px;
  background: var(--cv2-gold-soft, var(--sd-bg));
  border: 1px solid var(--sd-line);
  border-radius: 10px;
}
/* 잡 진행 띠(기존 .sd-job 재사용) — 상자 틀만 입힌다. */
.is-treatment-v2 .ds-band-job {
  padding: 12px 14px;
  background: var(--sd-bg);
  border: 1px solid var(--sd-line);
  border-radius: 10px;
}

/* --- 회차 문단 아래 보조(엔딩 훅 · 핵심 사건 · 확정 줄) --- */

.is-treatment-v2 .ds-ep-extra { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.is-treatment-v2 .ds-ep-hook { margin: 0; }
.is-treatment-v2 .ds-ep-events { margin: 0; padding-left: 18px; color: var(--sd-dim); font-size: 14px; }
.is-treatment-v2 .ds-ep-events li { margin: 2px 0; }
/* 확정 줄 — 가로로 안내 + 링크(공용 .ds-para-links 위에 간격만). */
.is-treatment-v2 .ds-ep-confirm { margin: 2px 0 0; display: flex; align-items: center; flex-wrap: wrap; gap: 4px 12px; }

/* 100y: 카드 머리 바로 아래 '지금 할 다음 일' 줄 — 버튼 하나 + 잠긴 까닭(또는 모두 확정 안내). 회차 탭 바로 아래라 스크롤 없이 보인다.
   색은 더하지 않는다(공용 .sd-button-primary · .sd-field-hint 그대로 — 두 테마). */
.is-treatment-v2 .ds-ep-next { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 12px; margin: 0 0 12px; }
.is-treatment-v2 .ds-ep-next .sd-field-hint { margin: 0; }
.is-treatment-v2 .ds-ep-next-btn { flex: 0 0 auto; white-space: nowrap; }
/* 100aa: 전체 탭 확정 뒤 '아래 세계관 칸 확인' 안내 — 버튼 줄 아래 한 줄을 다 쓴다(색 없음 · 공용 .cv2-link). */
.is-treatment-v2 .ds-ep-next-world { flex: 1 0 100%; }

/* --- 회차 상세 문서 카드 (039b, 2/2) --- */
.is-treatment-v2 .ds-ep-detail-card { padding: 22px; }
.is-treatment-v2 .ds-ep-detail-head {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 10px;
  padding-bottom: 12px; margin-bottom: 12px;
  border-bottom: 1px solid var(--sd-line);
}
.is-treatment-v2 .ds-ep-detail-head .cv2-identity-meta { color: var(--sd-dim); font-size: 14px; }
/* 순서 '…' 메뉴를 머리 오른쪽 끝으로 민다. */
.is-treatment-v2 .ds-ep-detail-head .cv2-more { margin-left: auto; }

/* 이 회차 분(분량) 조정 칸 — 카드 위 작은 칸(#22). 한 줄에 라벨 + 입력 + (바뀌면) 저장.
   기존 1차 .sd-episode-minutes/.sd-minutes-input 감각을 2차 카드 폭에 맞춰 좁게 둔다. */
.is-treatment-v2 .ds-ep-minutes {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px 12px;
  margin: 0 0 14px; padding: 10px 12px;
  background: var(--sd-bg); border: 1px solid var(--sd-line); border-radius: 10px;
}
.is-treatment-v2 .ds-ep-minutes-field { display: inline-flex; align-items: center; gap: 8px; margin: 0; }
.is-treatment-v2 .ds-ep-minutes-field .sd-field-label { font-weight: 700; white-space: nowrap; }
.is-treatment-v2 .ds-ep-minutes-input { width: 84px; }
.is-treatment-v2 .ds-ep-minutes-hint { flex: 1 1 220px; min-width: 180px; color: var(--sd-dim); }
.is-treatment-v2 .ds-ep-minutes-save { flex: 0 0 auto; }

/* 상세 문서 — 문단 묶음(docCard 가 .cv2-paragraphs 로 담는다). 공용 .cv2-para 재사용. */
.is-treatment-v2 .ds-ep-doc { display: flex; flex-direction: column; gap: 10px; }
/* uiux-261029-a(항목 13): 회차 상세 · 시퀀스 글 칸을 넓힌다. 회차 탭에서는 본문을 1단으로 두고(질문 카드는 글 아래 —
   1100px 이하 배치와 같다), 상세 문단은 120px 제목 열 없이 제목(라벨 · 배지)을 글 위 한 줄에 둬 글 칸이 카드 가로를
   다 쓰게 한다. 색은 바꾸지 않는다(두 테마 그대로). 560px 이하는 공용 규칙이 이미 1단이다. 전체 탭은 2단 그대로. */
.cv2-body.is-single { grid-template-columns: minmax(0, 1fr); }
.cv2-body.is-single > .cv2-question { position: static; }
.is-treatment-v2 .ds-ep-doc .cv2-para { grid-template-columns: minmax(0, 1fr); gap: 6px; }
.is-treatment-v2 .ds-ep-doc .cv2-para-head { flex-direction: row; align-items: center; flex-wrap: wrap; gap: 6px 10px; }
/* 상세 안내 한 줄 — 문단 위 작은 회색 메타. */
.is-treatment-v2 .ds-ep-doc-note { margin: 0 0 6px; }
/* 시퀀스 소제목(시퀀스 n · 등장 인물) — 문단 묶음을 가르는 굵은 라벨. */
.is-treatment-v2 .ds-seq-legend {
  display: block; margin: 12px 0 2px; font-weight: 700;
  padding-top: 10px; border-top: 1px dashed var(--sd-line);
}
/* 첫 시퀀스 소제목은 위 점선을 생략(안내 바로 아래라 과하다). */
.is-treatment-v2 .ds-ep-doc-note + .ds-seq-legend { border-top: 0; padding-top: 0; margin-top: 0; }
/* 상세 카드 아래 줄(확정/풀기 + 되돌리기) — 공용 .ds-para-links(가로·줄바꿈) 위 간격만. */
.is-treatment-v2 .ds-ep-detail-footer {
  margin: 14px 0 0; padding-top: 12px; border-top: 1px solid var(--sd-line);
  display: flex; align-items: center; flex-wrap: wrap; gap: 4px 12px;
}

/* --- 도크 — 칩 줄(위) + 입력 줄(아래)로 눕힌다(finalize-v2 와 같은 두 줄 도크). --- */
.is-treatment-v2 .cv2-dock-treatment { flex-direction: column; align-items: stretch; gap: 8px; }
.is-treatment-v2 .cv2-dock-row { display: flex; align-items: center; gap: 12px; }
.is-treatment-v2 .cv2-dock-treatment .sd-suggest { margin: 0; }

/* ==========================================================================
   대본(S7) 2차 화면 — script-v2.js (지시서 040, 1/2)

   treatment-v2 와 같은 틀(회차 가로 탭 상태 점 · 머리 아래 진행 띠 · 두 줄 도크)을 쓴다. script
   만의 다른 점은 전체 탭이 **회차별 대본 상태 목록**(본문을 싣지 않는다)이라는 것뿐이다. 그래서
   treatment-v2 규칙을 `.is-script-v2` 로 다시 쓰고(범위 분리), 상태 목록용 `.ds-ep-status*` 와
   잠김 점(`is-locked`)만 더한다. 범위를 `.is-script-v2` 로 좁혀 다른 단계에 영향이 없게 한다.

   '덧붙일 말' 칸 숨김은 이미 위쪽 공통 규칙이 처리한다(.cv2-dock 이 있으면 .sd-assist-instruction
   숨김). 기존 1차 클래스 재사용: .sd-job · .sd-progress · .sd-spinner · .sd-chips(진행 띠 · 블록
   상태 칩). 재정의하지 않는다.
   ========================================================================== */

/* 문서 카드 머리 — doc-stage.docCard 가 .cv2-doc-head 로 그린다(treatment-v2 와 같은 보정). */
.is-script-v2 .cv2-doc-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding-bottom: 14px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--sd-line);
}

/* 문서 카드 안 블록(대본 만들기 등) — finalize-v2 .ds-final-block 과 같은 감각. */
.is-script-v2 .ds-final-block {
  margin: 18px 0 10px;
  padding-top: 16px;
  border-top: 1px solid var(--sd-line);
}
.is-script-v2 .ds-block-label { display: block; margin: 0 0 8px; font-weight: 700; }

/* 비어 있을 때 안내 카드 — 큰 '회차별 줄거리로 가기' 버튼. */
.is-script-v2 .ds-empty-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 22px;
}
.is-script-v2 .ds-big-button { align-self: flex-start; white-space: nowrap; }

/* 예산 요약 한 줄 — 문단 사이 작은 회색 메타. */
.is-script-v2 .ds-series-line { margin: 0 0 4px; }

/* --- 회차 가로 탭(상태 점) — 세그먼트(.cv2-people/.cv2-person) 재사용 + 점 --- */
.is-script-v2 .ds-ep-tab { display: inline-flex; align-items: center; gap: 6px; }

/* 상태 점 — 없음 · 잠김(트리트먼트 미확정) · 만드는 중 · 실패 · 초안 · 상위 변경됨 · 확정.
   treatment-v2 와 같은 색 감각. 잠김(is-locked)만 대본에 더한다(빈 원 + 점선). */
.is-script-v2 .ds-ep-dot {
  width: 9px; height: 9px; border-radius: 999px;
  border: 1.5px solid var(--sd-dim);
  background: transparent;
  flex: 0 0 auto;
}
.is-script-v2 .cv2-person.is-active .ds-ep-dot { border-color: currentColor; }
.is-script-v2 .ds-ep-dot.is-none { background: transparent; }
.is-script-v2 .ds-ep-dot.is-locked { background: transparent; border-style: dotted; opacity: 0.6; }
.is-script-v2 .ds-ep-dot.is-draft { background: var(--sd-dim); border-color: var(--sd-dim); }
.is-script-v2 .ds-ep-dot.is-running {
  background: var(--cv2-gold, #d4a017);
  border-color: var(--cv2-gold, #d4a017);
  animation: ds-ep-pulse 1.1s ease-in-out infinite;
}
/* 색은 treatment-v2 점과 같은 토큰(091b). */
.is-script-v2 .ds-ep-dot.is-failed { background: var(--sd-danger); border-color: var(--sd-danger); }
.is-script-v2 .ds-ep-dot.is-stale { background: var(--sd-warn); border-color: var(--sd-warn); }
.is-script-v2 .ds-ep-dot.is-confirmed { background: var(--sd-success); border-color: var(--sd-success); }
/* 모션 줄이기 존중 — 깜빡임을 끈다(treatment-v2 와 같은 keyframes ds-ep-pulse 재사용). */
@media (prefers-reduced-motion: reduce) {
  .is-script-v2 .ds-ep-dot.is-running { animation: none; }
}

/* --- 머리 아래 진행 띠(대본 잡) --- */
.is-script-v2 .ds-band:empty { display: none; }
.is-script-v2 .ds-band { margin: 0 0 14px; }
.is-script-v2 .ds-band-job {
  padding: 12px 14px;
  background: var(--sd-bg);
  border: 1px solid var(--sd-line);
  border-radius: 10px;
}

/* --- 전체 탭 = 회차별 대본 상태 목록 --- */
/* 회차 한 줄 — 상태 배지 + 제목(한 줄에), 그 아래 한 줄 요약 + 링크. */
.is-script-v2 .ds-ep-status { margin: 14px 0 10px; }
.is-script-v2 .ds-ep-status-head {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px;
}
.is-script-v2 .ds-ep-status-badge { flex: 0 0 auto; }
.is-script-v2 .ds-ep-status-title { font-weight: 700; }
.is-script-v2 .ds-ep-status-line { margin: 4px 0 0; }
.is-script-v2 .ds-ep-status-links {
  margin: 6px 0 0; display: flex; align-items: center; flex-wrap: wrap; gap: 4px 12px;
}
/* 상태 배지 색 — 점과 같은 감각. 기존 .sd-badge 위에 상태별 색만 입힌다. */
.is-script-v2 .ds-ep-status-badge.is-none { background: var(--sd-bg); color: var(--sd-dim); }
.is-script-v2 .ds-ep-status-badge.is-locked { background: var(--sd-bg); color: var(--sd-dim); }
.is-script-v2 .ds-ep-status-badge.is-draft { background: var(--sd-bg); color: var(--sd-ink); }
.is-script-v2 .ds-ep-status-badge.is-running { background: var(--cv2-gold-soft, var(--sd-bg)); color: var(--sd-ink); }
/* 실패 · 상위 변경됨 · 확정 배지: 글자는 점과 같은 토큰, 바탕은 배지 바탕 토큰(091b — 바탕 리터럴도 함께 옮긴다. 글자만 옮기면
   어두운 테마에서 밝은 분홍 · 연두 바탕 위 밝은 글자가 된다). 세 테마 4.5:1 이상. */
.is-script-v2 .ds-ep-status-badge.is-failed { background: var(--sd-danger-tint); color: var(--sd-danger); }
.is-script-v2 .ds-ep-status-badge.is-stale { background: var(--sd-warn-tint); color: var(--sd-warn); }
.is-script-v2 .ds-ep-status-badge.is-confirmed { background: var(--sd-success-tint); color: var(--sd-success); }

/* --- 회차 탭 = 그 회차 대본 조각(요구 5, 2번 전까지 간추림) --- */
.is-script-v2 .ds-ep-detail-card { padding: 22px; }
.is-script-v2 .ds-ep-detail-head {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 10px;
  padding-bottom: 12px; margin-bottom: 12px;
  border-bottom: 1px solid var(--sd-line);
}
.is-script-v2 .ds-ep-detail-head .cv2-identity-meta { color: var(--sd-dim); font-size: 14px; }
.is-script-v2 .ds-ep-doc { display: flex; flex-direction: column; gap: 10px; }
.is-script-v2 .ds-ep-doc-note { margin: 0 0 6px; }
/* 블록 상태 칩 줄(있음/없음 · 자수). 기존 .sd-chips/.sd-chip 재사용 — 간격만. */
.is-script-v2 .ds-ep-block-chips { margin: 2px 0; }
.is-script-v2 .ds-ep-detail-actions { margin: 8px 0 0; display: flex; flex-wrap: wrap; gap: 8px; }
/* 확정/풀기 + 되돌리기 줄 — 공용 .ds-para-links 위 간격만. */
.is-script-v2 .ds-ep-confirm {
  margin: 14px 0 0; padding-top: 12px; border-top: 1px solid var(--sd-line);
  display: flex; align-items: center; flex-wrap: wrap; gap: 4px 12px;
}

/* --- 도크 — 칩 줄(위) + 입력 줄(아래)로 눕힌다(treatment-v2 와 같은 두 줄 도크). --- */
.is-script-v2 .cv2-dock-script { flex-direction: column; align-items: stretch; gap: 8px; }
.is-script-v2 .cv2-dock-row { display: flex; align-items: center; gap: 12px; }
.is-script-v2 .cv2-dock-script .sd-suggest { margin: 0; }


/* ==========================================================================
   대본(S7) 2차 — 회차 블록 문서 카드 (지시서 040, 2/2 · 040b)

   회차 탭을 누르면 그 회차 대본을 **블록 문서 카드**로 보여 준다(요구 1). 블록마다 씬 목차(S# 굵게)
   + 본문 미리보기(씬·인물명 볼드) + '직접 고치기'(contenteditable). 예산 밖(orphan) 블록 안내
   (요구 2), 버전 비교·복원 접는 패널(#17), 오른쪽 이 회차 요약 카드(요구 4)를 더한다. 범위를
   `.is-script-v2` 로 좁혀 다른 단계에 영향이 없게 한다. 기존 재사용: .sd-badge · .ds-para-links ·
   .cv2-para(본문 편집) · .sd-notes-info(검수 메모) · .cv2-question(요약 카드 틀).
   ========================================================================== */

/* 블록 문서 하나 — 문단 사이를 점선으로 가른다(treatment-v2 ds-seq-legend 감각). */
.is-script-v2 .ds-block-doc {
  padding-top: 14px;
  margin-top: 4px;
  border-top: 1px dashed var(--sd-line);
}
/* 첫 블록은 위 점선 생략(안내 바로 아래라 과하다). */
.is-script-v2 .ds-ep-doc-note + .ds-block-doc,
.is-script-v2 .ds-ep-sum-line + .ds-block-doc {
  border-top: 0; padding-top: 0; margin-top: 0;
}
/* 예산 밖 블록은 흐리게(지워지지 않지만 다음 생성에서 빠진다는 신호). */
.is-script-v2 .ds-block-doc.is-orphan { opacity: 0.82; }

/* 블록 소제목 — '블록 n' 굵게 + 배지 + 자수 메타 한 줄. */
.is-script-v2 .ds-block-legend {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px;
  margin: 0 0 6px; font-weight: 700;
}
.is-script-v2 .ds-block-legend .ds-block-meta { font-weight: 400; color: var(--sd-dim); }
.is-script-v2 .ds-block-orphan-badge { font-weight: 400; }
.is-script-v2 .ds-block-orphan-note { margin: 0 0 6px; color: var(--sd-warn); }
.is-script-v2 .ds-block-cast { margin: 2px 0 6px; }

/* 씬 목차 — S#n 굵게(요구 1). 기존 1차 .sd-scene-list 와 같은 감각을 2차 카드에 맞춰 둔다. */
.is-script-v2 .ds-scene-list {
  margin: 4px 0 8px; padding: 10px 12px; list-style: none;
  background: var(--sd-bg); border: 1px solid var(--sd-line); border-radius: 10px;
  display: flex; flex-direction: column; gap: 3px;
}
.is-script-v2 .ds-scene-item { display: flex; gap: 6px; font-size: 14px; }
.is-script-v2 .ds-scene-no { flex: 0 0 auto; color: var(--sd-ink); }
.is-script-v2 .ds-scene-head { color: var(--sd-dim); }

/* 본문 미리보기 — 씬 헤더·인물명 볼드(요구 1). 한 줄 = 한 p(scriptLineNode). */
.is-script-v2 .ds-block-body { margin: 2px 0 2px; }
.is-script-v2 .ds-script-preview {
  padding: 12px 14px; border: 1px solid var(--sd-line); border-radius: 10px;
  background: var(--cv2-paper, var(--sd-panel, #fff));
  max-height: 460px; overflow: auto;
}
.is-script-v2 .ds-script-line { margin: 0 0 4px; line-height: 1.6; white-space: pre-wrap; }
.is-script-v2 .ds-script-line.is-blank { height: 8px; margin: 0; }
/* 씬 헤더 줄 — 굵게 + 위 간격(장면 구분). */
.is-script-v2 .ds-script-line.is-scene { margin-top: 12px; }
.is-script-v2 .ds-script-line.is-scene:first-child { margin-top: 0; }
.is-script-v2 .ds-script-line.is-scene strong { font-size: 15px; }
/* 인물 대사 — 이름만 굵게. */
.is-script-v2 .ds-script-name { font-weight: 700; }
.is-script-v2 .ds-script-line.is-dir { color: var(--sd-ink); }
.is-script-v2 .ds-block-links { margin: 6px 0 0; }

/* 직접 고치기(편집 중) — 공용 .cv2-para 본문에 코드 느낌 글꼴·높이만 더한다. */
.is-script-v2 .ds-block-body.is-editing .ds-para-body {
  min-height: 220px;
  font-family: var(--sd-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 14px; line-height: 1.6; white-space: pre-wrap;
}

/* 블록 검수 메모 — 기존 .sd-notes-info 재사용(간격만). */
.is-script-v2 .ds-block-notes { margin: 4px 0 8px; }

/* 버전 비교·복원 접는 패널(#17) — 카드 아래. summary 는 링크처럼. */
.is-script-v2 .ds-block-versions { margin: 14px 0 0; padding-top: 12px; border-top: 1px solid var(--sd-line); }
.is-script-v2 .ds-block-versions-summary { cursor: pointer; color: var(--cv2-link, var(--sd-ink)); font-weight: 600; }

/* 회차 전체 자수·씬 요약 줄 — 허용 구간 색(기존 게이지 색 감각 — 091b 부터 게이지와 같은 토큰). */
.is-script-v2 .ds-ep-sum-line { margin: 0 0 8px; }
.is-script-v2 .ds-ep-sum-chars.is-ok { color: var(--sd-success); font-weight: 600; }
.is-script-v2 .ds-ep-sum-chars.is-under { color: var(--sd-warn); font-weight: 600; }
.is-script-v2 .ds-ep-sum-chars.is-over { color: var(--sd-danger); font-weight: 600; }

/* 오른쪽 이 회차 요약 카드(요구 4) — 질문 카드 틀(.cv2-question) 재사용, 요약 줄만 더한다. */
.is-script-v2 .ds-ep-sum-card .ds-ep-sum-row { margin: 6px 0; }
.is-script-v2 .ds-ep-sum-card .cv2-q-why { margin-top: 12px; }

/* 이 회차 대본 설정(#25) — 분 조정 + 분량 검수 한 줄. treatment-v2 ds-ep-minutes 감각. */
.is-script-v2 .ds-ep-options { margin: 14px 0 6px; }
.is-script-v2 .ds-ep-opt-row {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 18px; margin: 2px 0 6px;
}
.is-script-v2 .ds-ep-opt-field { display: inline-flex; align-items: center; gap: 8px; margin: 0; }
.is-script-v2 .ds-ep-opt-field .sd-field-label { font-weight: 700; white-space: nowrap; }
.is-script-v2 .ds-ep-opt-minutes { width: 84px; }



/* ==========================================================================
   고객 확정안 고르기 부품(taxonomy-261026, taxonomy-ui.js) — 출발점 · 스토리 설정 공용 `.tx-*`

   색은 기존 토큰(--sd-* · --cv2-gold*)만 쓴다 — 두 테마(밝음 · 어두움)는 토큰이 바꾼다. 오류 빨강은 예전에 여기서만
   테마별 --tx-danger(#b42318 · 어두움 #f29b8f)를 따로 두었다 — 그때는 story-dev 에 두 테마 위험 토큰이 없었다. 091b 부터 같은 2차
   화면의 ① 생년월일 오류 줄 · AI 비서 칸 오류와 같은 --sd-danger 하나다(세 테마 4.5:1 이상 — 밝은 테마 대비는 6.0 → 5.0:1 로
   조금 내려간다, WORKLOG 091b). 전이 · 애니메이션은 두지 않는다(reduced-motion 과 무관하게
   같은 화면). 범위를 2차 화면(.cv2)으로 좁혀 전문가 보기 · 다른 화면에 새지 않게 한다.
   ========================================================================== */

/* 구역(① 장르 · ② 소재·무대 · ③ 기본 톤) — 문단과 같은 세로 리듬, 구역 사이 선. */
.cv2 .tx-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0 0 16px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--sd-line);
}
.cv2 .tx-section-title { margin: 4px 0 2px; }
.cv2 .tx-choose { display: flex; flex-direction: column; gap: 8px; margin: 6px 0 2px; }
.cv2 .tx-choose .ds-choose-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 8px; }
.cv2 .tx-choose-help { margin: 0; }
.cv2 .tx-required { border-color: var(--cv2-gold); color: var(--cv2-gold-ink); }
.cv2 .tx-error { margin: 0; color: var(--sd-danger); font-weight: 600; font-size: 14px; }

/* 고른 장르의 핵심 질문 목록 — 작은 글자(A1.4). */
.cv2 .tx-genre-questions {
  list-style: none;
  margin: 2px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  color: var(--sd-dim);
}
.cv2 .tx-genre-questions .sd-badge { font-size: 12px; }
.cv2 .tx-central { border-color: var(--cv2-gold); color: var(--cv2-gold-ink); background: var(--cv2-gold-soft); }

/* 슬라이더 — 제목 + n/5 + 뜻 / range / 양 끝 문구 / 정하는 것(고객 그림 1). */
.cv2 .tx-slider { display: flex; flex-direction: column; gap: 4px; margin: 6px 0 4px; }
.cv2 .tx-slider-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px; }
.cv2 .tx-slider-value { font-weight: 700; color: var(--cv2-gold-ink); font-variant-numeric: tabular-nums; }
.cv2 .tx-slider-input { width: 100%; max-width: 420px; accent-color: var(--cv2-gold); }
.cv2 .tx-slider-input:focus-visible { outline: 2px solid var(--cv2-gold); outline-offset: 3px; }
.cv2 .tx-slider-ends {
  display: flex;
  justify-content: space-between;
  max-width: 420px;
  font-size: 12px;
  color: var(--sd-dim);
}
.cv2 .tx-slider-what,
.cv2 .tx-slider-help { margin: 0; }

/* 출발점 요약(스토리 설정 위, 읽기 전용) — 금색 옅은 바탕으로 '고치는 곳이 아님'을 구분한다. */
.cv2 .tx-summary {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0 0 14px;
  padding: 12px 14px;
  border: 1px solid var(--sd-line);
  border-radius: 12px;
  background: var(--cv2-gold-soft);
}
.cv2 .tx-summary-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px; }
.cv2 .tx-summary-rows {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 2px 12px;
  margin: 0;
  font-size: 14px;
}
.cv2 .tx-summary-rows dt { color: var(--sd-dim); }
.cv2 .tx-summary-rows dd { margin: 0; color: var(--sd-ink); }

/* 플롯 — 자주 쓰는 조합 칩 · 고른 칸 아래 목표 문장. */
.cv2 .tx-pairs { display: flex; flex-direction: column; gap: 6px; margin: 2px 0 4px; }
.cv2 .tx-plot-goal { display: block; margin-top: 2px; }

/* 엔딩 방향 설정(고객 그림 2) — 두 축 fieldset · 라디오 · 결과 줄. */
.cv2 .tx-ending { display: flex; flex-direction: column; gap: 8px; margin: 10px 0 14px; }
.cv2 .tx-ending-axes { display: flex; flex-wrap: wrap; gap: 12px 28px; }
.cv2 .tx-ending-axis { margin: 0; padding: 0; border: 0; min-width: 220px; }
.cv2 .tx-ending-axis legend { padding: 0; margin-bottom: 2px; }
.cv2 .tx-radio-row { display: flex; gap: 16px; margin-top: 4px; }
.cv2 .tx-radio { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.cv2 .tx-radio input { accent-color: var(--cv2-gold); }
.cv2 .tx-radio input:focus-visible { outline: 2px solid var(--cv2-gold); outline-offset: 2px; }
.cv2 .tx-ending-result { margin: 0; color: var(--sd-dim); }
.cv2 .tx-ending-result.is-set { color: var(--cv2-gold-ink); font-weight: 700; }

/* 세부 톤 묶음 · 유머 방식 숨김 안내. */
.cv2 .tx-detail { gap: 10px; }
.cv2 .tx-humor-off { margin: 4px 0; }

/* 옮김 안내 · 새로 고침 안내 · 이전 값 상자. */
.cv2 .tx-notice {
  margin: 0 0 14px;
  padding: 12px 14px;
  border: 1px solid var(--cv2-gold);
  border-radius: 12px;
  background: var(--sd-surface);
  color: var(--sd-ink);
}
.cv2 .tx-notice-title { margin: 0 0 6px; font-weight: 700; }
.cv2 .tx-notice-list { margin: 0 0 6px; padding-left: 18px; font-size: 14px; }
.cv2 .tx-notice-close { margin-top: 4px; }
.cv2 .tx-legacy { margin: 6px 0 0; font-size: 14px; }
.cv2 .tx-legacy > summary { cursor: pointer; color: var(--sd-dim); }
.cv2 .tx-legacy > summary:focus-visible { outline: 2px solid var(--cv2-gold); outline-offset: 2px; }
.cv2 .tx-legacy-list { margin: 6px 0 0; padding-left: 18px; color: var(--sd-dim); }


/* --------------------------------------------------------------------------
   uiux-261029-c(2026-10-29) — 오른쪽 AI 비서 칸 하나 · 채팅 · 좌우 독립 스크롤
   (키이스트 UI/UX 개선 의견 261029 항목 3 · 4 · 5 — stage-shell.splitFrame · doc-stage.chatPanel)

   - 틀(.cv2.is-split)은 상단바 아래 · 사이드바 오른쪽부터 화면 끝까지 고정한다(design D2 — 흐름 안에 두면 앱 여백만큼 페이지가
     스크롤돼 입력칸이 화면 밖에 걸린다). 머리 · 진행 막대는 위에 서 있고, 그 아래 왼쪽 문서 · 오른쪽 대화가 각자 스크롤한다
     (overscroll-behavior: contain — 한쪽 끝에 닿아도 다른 쪽 · 페이지로 넘어가지 않게).
   - 고정 틀은 쌓임 맥락을 만든다(z 14 — 상단바 20 · 사이드바 30 · 서랍 · 완료 카드 40 아래). 머리 메뉴가 그 안에서 본문 위로
     펼쳐지게 머리를 한 층 올린다.
   - 1024px 미만: 칸은 아래 서랍(기본 닫힘), 오른쪽 아래 'AI 비서' 버튼 · 배경막. 넓은 화면 접힘: 오른쪽 끝 세로 탭.
   - 색은 테마 토큰만(두 테마 — --sd-* · --cv2-gold* 가 어두운 테마 값으로 바뀐다). 움직임(서랍 · 생각 중 점 · 알림)은
     reduced-motion 이면 끈다.
   -------------------------------------------------------------------------- */
.cv2.is-split {
  position: fixed;
  top: var(--cv2-topbar, 0px);
  left: var(--nav-w, 0px);
  right: 0;
  bottom: 0;
  z-index: 14;
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 0;
  background: var(--sd-surface);
  color: var(--sd-ink);
}
/* 좁은 화면(≤820px)은 메인 사이드바가 서랍이 된다 — 틀이 화면 왼쪽 끝부터(옛 도크와 같은 분기점). */
@media (max-width: 820px) {
  .cv2.is-split { left: 0; }
}
/* 머리 · 진행 막대: sticky · 카드 여백 덮기(음수 여백)를 풀고 틀 위쪽에 서 있게. 머리 메뉴가 아래 단 위로 펼쳐지게 한 층 위. */
.cv2.is-split > .cv2-head {
  position: relative;
  top: auto;
  z-index: 3;
  flex: 0 0 auto;
  margin: 0;
  padding: 10px 20px 6px;
}
.cv2.is-split > .cv2-progress {
  position: relative;
  top: auto;
  z-index: 2;
  flex: 0 0 auto;
  margin: 0;
  padding: 4px 20px 8px;
}

/* 2단 — 왼쪽 문서 | 폭 경계 | 오른쪽 칸. 폭은 --cv2-ai-w(stage-shell 이 저장값으로 넣는다), 문서가 너무 좁아지지 않게 48% 까지. */
.cv2-split {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
}
.cv2-split.is-resizing { cursor: col-resize; user-select: none; }
.cv2-split-main {
  flex: 1 1 auto;
  min-width: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
}
.cv2-split-doc { flex: 1 0 auto; min-width: 0; padding: 16px 20px 24px; }
.cv2-split-doc > .sd-stale-host:not(:empty) { margin-bottom: 14px; }
/* 왼쪽 단 아래 줄 — 되돌리기 알림 · '확정하고 다음 단계로'. 문서와 함께 스크롤되지 않고 그 단 아래에 늘 보인다(design D3). */
.cv2-split-foot {
  position: sticky;
  bottom: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 8px 12px;
  padding: 10px 20px;
  background: var(--sd-surface);
  border-top: 1px solid var(--sd-line);
}
.cv2-split-foot .cv2-dock-confirm { flex: 0 0 auto; white-space: nowrap; }
/* 되돌리기 알림은 옛 도크 위 가운데(fixed)가 아니라 이 줄 왼쪽에 선다. */
.cv2-split-foot .cv2-notice {
  position: static;
  transform: none;
  margin-right: auto;
  max-width: 100%;
  animation: cv2-split-notice-in .24s ease-out;
}
.cv2-split-foot .cv2-notice.is-leaving { animation: cv2-split-notice-out .2s ease-in forwards; }
.cv2-split-foot .cv2-notice-text { white-space: normal; }
@keyframes cv2-split-notice-in {
  from { transform: translateY(8px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
@keyframes cv2-split-notice-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* 폭 경계 — 8px 손잡이(가운데 1px 선). 끌기 · 키보드(← → Home End). */
.cv2-split-handle {
  position: relative;
  flex: 0 0 8px;
  cursor: col-resize;
  touch-action: none;
  background: transparent;
}
.cv2-split-handle::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 3px;
  width: 1px;
  background: var(--sd-line);
}
.cv2-split-handle:hover::before,
.cv2-split.is-resizing .cv2-split-handle::before { left: 2px; width: 3px; background: var(--cv2-gold); }
.cv2-split-handle:focus-visible { outline: 2px solid var(--cv2-gold-ink); outline-offset: -2px; }

/* 오른쪽 AI 비서 칸. */
.cv2-split > .cv2-ai {
  flex: 0 0 clamp(300px, var(--cv2-ai-w, 400px), 48%);
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  background: color-mix(in srgb, var(--cv2-gold) 4%, var(--sd-surface));
}
.cv2-ai [hidden] { display: none; }
.cv2-ai-head {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 12px 16px 10px;
  border-bottom: 1px solid var(--sd-line);
}
.cv2-ai-head-text { flex: 1 1 auto; min-width: 0; }
.cv2-ai-title { margin: 0; font-size: 16px; font-weight: 700; }
.cv2-ai-sub { margin: 2px 0 0; font-size: 12px; line-height: 1.5; color: var(--sd-dim); }
.cv2-ai-hide { flex: 0 0 auto; }
.cv2-ai-body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.cv2-ai-tools { flex: 0 0 auto; display: flex; justify-content: flex-end; padding: 6px 16px 0; }
.cv2-ai-clear { font-size: 12px; }
.cv2-ai-scroll {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 8px 16px 14px;
}
.cv2-ai-log { display: flex; flex-direction: column; gap: 10px; }
.cv2-ai-empty { margin: 6px 0; }
/* 말풍선 — 아래 도크 대화(.ds-chat-*)와 같은 모양, 칸 폭에 맞춰 넓게. */
.cv2-ai .ds-chat-bubble { max-width: 92%; font-size: 14px; }
.cv2-ai .ds-chat-msg.is-question .ds-chat-bubble { background: var(--sd-surface); border-color: var(--cv2-gold-soft); }
.cv2-ai .ds-chat-cards { gap: 8px; }
.cv2-ai .ds-chat-card { gap: 6px; padding: 10px 12px; }
.cv2-ai .ds-chat-card-text { font-size: 14px; line-height: 1.6; }
.cv2-ai-card.is-applied { border-color: var(--cv2-gold); }
.cv2-ai-card-no {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--cv2-gold-soft);
  color: var(--cv2-gold-ink);
  font-size: 12px;
  font-weight: 700;
}
.cv2-ai-card-apply { align-self: flex-start; }
/* 지금 질문 — 예전 오른쪽 질문 카드를 말풍선 안에 담는다(카드 테두리 · sticky · 큰 글씨 · 등장 모션을 끈다). */
.cv2-ai-live .ds-chat-msg { align-items: flex-start; }
.cv2-ai .cv2-ai-live-bubble { flex: 1 1 auto; max-width: 100%; background: var(--sd-surface); border-color: var(--cv2-gold-soft); }
.cv2-ai-live .cv2-question,
.cv2-ai-live .cv2-question.sd-panel {
  position: static;
  top: auto;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}
.cv2-ai-live .cv2-question + .cv2-question { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--sd-line); }
.cv2-ai-live .cv2-q-live { animation: none; }
.cv2-ai-live .cv2-q-question { font-size: 17px; }
.cv2-ai-live .cv2-q-choice { padding: 10px 12px; }
.cv2-ai-live .cv2-q-choice-label { font-size: 15px; }
.cv2-ai-live .sd-cast-leads { margin: 0 0 10px; }
/* 생각 중 — 점이 숨 쉬듯 깜박이고, 스트림 글자는 끝부분만 흐리게. */
.cv2-ai-thinking .ds-chat-bubble { border-style: dashed; border-color: var(--cv2-gold); }
.cv2-ai-thinking-head { margin: 0 0 2px; font-size: 13px; font-weight: 700; color: var(--cv2-gold-ink); }
.cv2-ai-thinking-secs { font-weight: 400; color: var(--sd-dim); }
.cv2-ai-thinking-note { margin: 4px 0 0; }
.cv2-ai-stream {
  margin: 6px 0 0;
  max-height: 9em;
  overflow: hidden;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 13px;
  line-height: 1.5;
  color: var(--sd-dim);
}
.cv2-ai-thinking-dot { animation: cv2-ai-pulse 1.2s ease-in-out infinite; }
@keyframes cv2-ai-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .25; }
}
/* 칸이 '생각 중'을 보이는 동안 화면 아래 '처리 중입니다 · n초' 알약(api.js beginBusy)은 숨긴다 — 같은 것을 두 번 말하지 않게.
   화면 맨 위 얇은 진행 막대는 남는다(다른 요청도 함께 센다). */
body:has(.cv2-ai-body.is-thinking) .sd-busy-pill { display: none; }

/* 아래 — 다음 행동 칩 + 입력칸(여러 줄 · Enter 보내기 · Shift+Enter 줄바꿈). */
.cv2-ai-foot {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px 12px 12px;
  border-top: 1px solid var(--sd-line);
  background: var(--sd-surface);
}
.cv2-ai-chips { max-height: 30vh; overflow-y: auto; overscroll-behavior: contain; }
.cv2-ai-chips .ds-dock-suggest { margin: 0; gap: 6px; }
.cv2-ai-chips .ds-pill { white-space: normal; flex-shrink: 1; text-align: left; }
.cv2-ai-composer {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  padding: 6px 6px 6px 12px;
  border: 1px solid var(--sd-line);
  border-radius: 14px;
  background: var(--sd-bg);
}
.cv2-ai-composer:focus-within {
  border-color: var(--cv2-gold);
  background: var(--sd-surface);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--cv2-gold) 22%, transparent);
}
.cv2-ai-input {
  flex: 1 1 auto;
  min-width: 0;
  max-height: 160px;
  overflow-y: auto;
  resize: none;
  border: 0;
  background: transparent;
  color: var(--sd-ink);
  font: inherit;
  font-size: 15px;
  line-height: 1.5;
  padding: 4px 0;
}
.cv2-ai-input::placeholder { color: var(--sd-dim); }
.cv2-ai-input:focus { outline: none; }
.cv2-ai-input:disabled { cursor: default; }
.cv2-ai-send { flex: 0 0 auto; }

/* 'AI 비서' 펼치기 — 기본은 숨김. 넓은 화면에서 접혔을 때 오른쪽 끝 세로 탭. */
.cv2-ai-reopen { display: none; }
.cv2-ai-scrim { display: none; }
.cv2-split.is-ai-collapsed > .cv2-ai,
.cv2-split.is-ai-collapsed > .cv2-split-handle { display: none; }
.cv2-split.is-ai-collapsed > .cv2-ai-reopen {
  display: flex;
  flex: 0 0 40px;
  align-items: center;
  justify-content: center;
  writing-mode: vertical-rl;
  padding: 14px 0;
  border: 0;
  border-left: 1px solid var(--sd-line);
  background: color-mix(in srgb, var(--cv2-gold) 8%, var(--sd-surface));
  color: var(--cv2-gold-ink);
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: .08em;
  cursor: pointer;
}
.cv2-ai-reopen:focus-visible { outline: 2px solid var(--cv2-gold-ink); outline-offset: -3px; }

/* 1024px 미만 — 칸은 아래 서랍(기본 닫힘). 닫힌 서랍은 visibility 로 숨겨 Tab 이 들어가지 않는다. */
@media (max-width: 1023.98px) {
  .cv2-split-handle { display: none; }
  .cv2-split > .cv2-ai {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 6;
    height: min(80%, 680px);
    border-top: 1px solid var(--sd-line);
    border-radius: 16px 16px 0 0;
    background: var(--sd-surface);
    box-shadow: 0 -10px 28px color-mix(in srgb, var(--sd-ink) 18%, transparent);
    transform: translateY(100%);
    visibility: hidden;
    transition: transform .26s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s linear .26s;
  }
  .cv2-split.is-ai-open > .cv2-ai {
    transform: none;
    visibility: visible;
    transition: transform .26s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .cv2-split:not(.is-ai-open) > .cv2-ai-reopen {
    display: inline-flex;
    position: absolute;
    right: 16px;
    bottom: 76px;
    z-index: 4;
    align-items: center;
    padding: 10px 18px;
    border: 1px solid var(--cv2-gold);
    border-radius: 999px;
    background: var(--sd-surface);
    color: var(--cv2-gold-ink);
    font: inherit;
    font-size: 14px;
    font-weight: 700;
    cursor: pointer;
    box-shadow: 0 6px 18px color-mix(in srgb, var(--sd-ink) 18%, transparent);
  }
  .cv2-split > .cv2-ai-scrim {
    display: block;
    position: absolute;
    inset: 0;
    z-index: 5;
    background: color-mix(in srgb, var(--sd-ink) 32%, transparent);
    opacity: 0;
    pointer-events: none;
    transition: opacity .2s ease;
  }
  .cv2-split.is-ai-open > .cv2-ai-scrim { opacity: 1; pointer-events: auto; }
}
@media (max-width: 560px) {
  .cv2-split-doc { padding: 12px 14px 20px; }
  .cv2-split-foot { padding: 8px 14px; }
  .cv2.is-split > .cv2-head { padding: 8px 14px 6px; }
  .cv2.is-split > .cv2-progress { padding: 4px 14px 8px; }
}
@media (prefers-reduced-motion: reduce) {
  .cv2-split > .cv2-ai,
  .cv2-split.is-ai-open > .cv2-ai,
  .cv2-split > .cv2-ai-scrim { transition: none; }
  .cv2-ai-thinking-dot,
  .cv2-split-foot .cv2-notice,
  .cv2-split-foot .cv2-notice.is-leaving { animation: none; }
}

/* --------------------------------------------------------------------------
   uiux-261029-c 2부(2026-10-29) — 손잡이 신호 · 모든 단계 새 틀
   (1부 이슈 5: 1024px 미만에서 AI 가 일을 시작해도 서랍을 저절로 올리지 않는다 — stage-shell.noteAiPanel)

   - 'AI 비서' 손잡이(좁은 화면 오른쪽 아래 알약 · 넓은 화면 접힘 세로 탭) 안에 신호 자리(.cv2-ai-reopen-signal)를 둔다.
     칸 본문이 '생각 중'이면 .is-ai-thinking — 점이 숨 쉬듯 깜박이고 '생각 중…', 닫힌 동안 새 답이 왔으면 .is-ai-unread — 금색 점과
     '새 답'. 사용자가 열면 둘 다 꺼진다. 글자는 aria-hidden 이고 버튼 이름(aria-label)으로 화면 낭독기에 전한다.
   - 색은 테마 토큰만(두 테마). 움직임(점 깜박임)은 reduced-motion 이면 끈다.
   -------------------------------------------------------------------------- */
.cv2-ai-reopen { gap: 8px; }
.cv2-ai-reopen-signal:empty { display: none; }
.cv2-ai-reopen-signal {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--cv2-gold-ink);
}
.cv2-ai-reopen-signal::before {
  content: "";
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: var(--cv2-gold);
}
.cv2-split.is-ai-thinking > .cv2-ai-reopen .cv2-ai-reopen-signal::before { animation: cv2-ai-pulse 1.2s ease-in-out infinite; }
.cv2-split.is-ai-unread > .cv2-ai-reopen { border-color: var(--cv2-gold); }
@media (prefers-reduced-motion: reduce) {
  .cv2-split.is-ai-thinking > .cv2-ai-reopen .cv2-ai-reopen-signal::before { animation: none; }
}

/* --------------------------------------------------------------------------
   uiux-261029-c 3부(2026-10-30) — 노트북 높이에서 대화 기록을 넓게
   (리뷰어 dev 1366×768 점검: 대화 기록이 약 200px — 여러 줄로 쌓인 칩 · 2줄 입력칸 · 칸 머리 설명 · 본문 위 '대화 지우기' 줄이
   자리를 썼다. 목표: 화면 높이 768px 에서 대화 기록 384px(절반) 이상 — 어림은 design 10.3)

   - 다음 행동 칩은 한 줄이다. 넘치면 가로로 스크롤한다 — 접기를 더 두지 않은 이유: doc-stage 칩 줄에 '더 보기 ⋯'(펼치기)가
     이미 있어 접기를 하나 더 두면 펼치기가 두 단계가 된다. 마우스 세로 휠은 stage-shell.onAiPanelWheel 이 가로로 돌리고, 키보드
     포커스(Tab · ← →)는 브라우저가 칩을 보이는 자리로 민다(scroll-padding 으로 끝 칩도 여백을 두고). 끝에서 잘린 칩과 얇은
     스크롤 막대가 '옆에 더 있다'를 보인다. 칩 줄이 넘친 것을 자르므로(overflow) 포커스 테두리는 안쪽으로 그린다.
   - 입력칸은 한 줄로 시작한다(stage-shell.fitAiPanelBody 가 rows=1 — 늘어남 · 160px 상한 · 보낸 뒤 줄어듦은 doc-stage.growInput
     그대로). CSS 높이로만 줄이면 첫 글자에서 growInput 이 rows 높이로 되돌린다.
   - '대화 지우기'는 칸 머리(stage-shell 의 대리 버튼)로 옮기고 본문 위 줄은 숨긴다. 기록이 없어 본문 버튼이 숨으면(doc-stage
     paintChatTools) 머리 버튼도 숨는다(:has — 상태를 따로 들지 않아 어긋나지 않는다. :has 를 모르는 브라우저에서는 늘 보이고,
     누르면 '지울 대화 기록이 없어요.' 안내 — 1부 body:has 알약 규칙과 같은 기준).
   - 화면 높이 820px 이하(1366×768 · 1280×800 노트북 — 1080p 데스크톱은 빠진다)에서만 칸 머리 설명을 숨기고 단계 머리 · 진행
     막대 · 칸 아래의 여백을 줄인다. 칸 이름은 제목('AI 비서' — aria-labelledby)이 그대로 전한다. 한 줄 이야기 · 설정 메모지 ·
     진행 칸 · 머리 버튼은 숨기지 않는다. 가로 여백(560px 이하 규칙)은 건드리지 않게 위 · 아래 여백만 바꾼다.
   - 색은 테마 토큰만, 새 움직임 없음(두 테마 · reduced-motion 영향 없음).
   -------------------------------------------------------------------------- */
.cv2-ai-chips {
  max-height: none;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
  scroll-padding-inline: 12px;
}
.cv2-ai-chips .ds-dock-suggest {
  flex-wrap: nowrap;
  width: max-content;
  min-width: 100%;
  padding: 2px 0;
}
.cv2-ai-chips .ds-dock-suggest-label,
.cv2-ai-chips .ds-pill {
  flex: 0 0 auto;
  white-space: nowrap;
}
.cv2-ai-chips .ds-pill { padding: 4px 12px; }
.cv2-ai-chips .ds-pill:focus-visible { outline-offset: -2px; }
/* 본문 위 '대화 지우기' 줄 — 칸 머리 버튼이 대신한다(본문 버튼은 그 머리 버튼이 누르는 대상이라 DOM 에는 남는다). */
.cv2-ai .cv2-ai-body > .cv2-ai-tools { display: none; }
.cv2-ai-head-clear { flex: 0 0 auto; }
.cv2-ai:has(.cv2-ai-body .cv2-ai-clear[hidden]) .cv2-ai-head-clear { display: none; }
@media (max-height: 820px) {
  .cv2-ai-sub { display: none; }
  .cv2-ai-head { align-items: center; padding-top: 6px; padding-bottom: 5px; }
  .cv2-ai-foot { gap: 6px; padding-top: 6px; padding-bottom: 8px; }
  .cv2-ai-composer { padding-top: 4px; padding-bottom: 4px; padding-right: 4px; }
  .cv2.is-split > .cv2-head { padding-top: 6px; padding-bottom: 4px; }
  .cv2.is-split > .cv2-head .cv2-head-left { flex-direction: row; flex-wrap: wrap; align-items: baseline; gap: 2px 12px; }
  .cv2.is-split > .cv2-head .cv2-back { align-self: baseline; }
  .cv2.is-split > .cv2-head .cv2-title { font-size: 19px; }
  .cv2.is-split > .cv2-head .cv2-head-brief { margin-top: 4px; gap: 4px; }
  .cv2.is-split > .cv2-progress { padding-top: 2px; padding-bottom: 6px; }
  .cv2.is-split > .cv2-progress .cv2-step { padding-top: 5px; padding-bottom: 5px; }
}

/* --------------------------------------------------------------------------
   uiux-261029-c 4부(2026-10-30) — 회차 구조표 보조 칸 · 좁은 왼쪽 단 · 긴 대화 기록
   (리뷰어 dev ci-78232cd8 1366×768 점검 · 설계자 3차 후속 2 지시서)

   - 회차 구조표 문단(treatment-v2 episodeParagraph)은 doc-stage 문단(.cv2-para 격자 120px | 나머지)에 보조 칸(.ds-ep-extra — 엔딩 훅 ·
     핵심 사건 · 상위 변경됨 · 확정 줄)을 셋째 자식으로 덧붙인다. 그래서 둘째 줄 첫 칸(120px 라벨 칸)에 들어가 글이 약 100px 폭으로
     세로로 흘렀다(083 항목 13 은 회차 상세만 1단으로 바꿨다). 보조 칸은 문단 한 줄 전체(1 / -1)를 쓴다 — 본문 칸(2 / -1)이 아닌 이유:
     560px 이하 1단 격자에서 2 / -1 은 보이지 않는 둘째 열을 만든다. 이 파일의 다른 '문단 다음 줄'(대사 확인 줄 · 078 다시 쓰기 줄)과
     같은 규칙이다. 옛 틀(아래 도크)도 같은 자리라 함께 고쳐진다.
   - 새 틀의 왼쪽 단 폭은 화면 폭이 아니라 AI 비서 칸 폭(끌기 · 접기)으로 정해져 화면 폭 질의로는 알 수 없다. 그래서 문서 칸
     (.cv2-split-doc)을 크기 질의 컨테이너로 두고, 안쪽 600px 미만(좌우 여백 20px 씩 — 왼쪽 단 약 640px 미만)에서 회차 문단을
     1단(라벨 · 배지 한 줄이 위, 본문 아래 — 083 회차 상세와 같은 모양)으로 둔다. 문서 칸 폭은 틀(세로 flex 늘이기)이 정해 inline-size
     묶기가 배치를 바꾸지 않는다. 스크롤 칸(.cv2-split-main)이 아니라 그 안 문서 칸에 두어 아래 줄(sticky)을 묶음 밖에 둔다.
     컨테이너 질의를 모르는 브라우저는 2단 그대로(보조 칸은 한 줄 전체).
   - 긴 대화: AI 비서 칸은 처음 최근 30개만 그리고(doc-stage CHAT_RECENT), 맨 위 '이전 대화 n개 더 보기'가 앞 기록을 앞 기록 묶음
     (.cv2-ai-earlier — 기록 role=log 밖이라 화면 낭독기가 새 말로 읽지 않는다)에 끼운다. 묶음은 비면 자리를 차지하지 않고, 누른 뒤
     포커스 자리라 키보드 포커스 테두리를 보인다.
   - 색은 테마 토큰만, 새 움직임 없음(두 테마 · reduced-motion 영향 없음).
   -------------------------------------------------------------------------- */
.is-treatment-v2 .cv2-para > .ds-ep-extra { grid-column: 1 / -1; }
.cv2-split-doc { container: cv2-doc / inline-size; }
@container cv2-doc (max-width: 599.98px) {
  .is-treatment-v2 .cv2-para { grid-template-columns: minmax(0, 1fr); gap: 6px; }
  .is-treatment-v2 .cv2-para-head { flex-direction: row; align-items: center; flex-wrap: wrap; gap: 6px 10px; }
}
.cv2-ai-more { align-self: center; flex: 0 0 auto; }
.cv2-ai-earlier { display: flex; flex-direction: column; gap: 10px; }
.cv2-ai-earlier:empty { display: none; }
.cv2-ai-earlier:focus { outline: none; }
.cv2-ai-earlier:focus-visible { outline: 2px solid var(--cv2-gold-ink); outline-offset: 2px; border-radius: 10px; }


/* --------------------------------------------------------------------------
   protagonist-saju-flow(2026-10-27) — 주인공 설계 순서
   ① 사주 · MBTI 블록(.cv2-basis) · 문단 단계 번호(.cv2-para-step) · '앞 단계가 바뀌었어요'(.cv2-para-stale) ·
   ① 안내 카드(.cv2-basis-guide). 색은 테마 토큰만(두 테마 — --cv2-gold* 는 어두운 테마 값이 따로 있다),
   새 전이 · 애니메이션은 없다(reduced-motion 영향 없음).
   -------------------------------------------------------------------------- */
.cv2-basis {
  margin: 0 0 18px; padding: 16px;
  border: 1px solid var(--cv2-gold); border-radius: 12px;
  background: var(--cv2-gold-soft);
}
.cv2-basis-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.cv2-basis-title { margin: 0; font-weight: 700; font-size: 16px; }
.cv2-basis-sub { color: var(--sd-dim); font-size: 13px; }
/* 생년월일(넓게) · MBTI(좁게) 두 열. 좁은 화면은 한 열. */
.cv2-basis-grid { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 16px; }
@media (max-width: 720px) {
  .cv2-basis-grid { grid-template-columns: 1fr; gap: 6px; }
}
.cv2-basis-col > .cv2-field-group-label:first-child { margin-top: 0; }
.cv2-basis-need { color: var(--cv2-gold-ink); font-size: 12px; font-weight: 600; }
/* ① 생년월일 오류 줄 — AI 비서 칸 오류 글자와 같은 오류 토큰(uiux-261029-e). 090 은 어두운 테마에서만 이 토큰이었고(앱 --danger
   #e5484d 가 금색 연배경 위 4.0:1), 091 에서 밝은 테마도 이 토큰이다 — 앱 --danger(#d13a45)는 금색 연배경(#f6ede1) 위 4.12:1 이었다
   (설계자 결정: 밝은 테마 글자를 조금 진하게 해도 된다 → #c53030 4.72:1, 어두움 #f0707f 5.52:1). 테마별 규칙이 필요 없어 지웠다. */
.cv2-basis-error { margin: 6px 0 0; color: var(--sd-danger); font-size: 13px; }
.cv2-basis-error:empty { display: none; }
.cv2-basis-mbti { display: flex; align-items: flex-end; gap: 8px; flex-wrap: wrap; }
.cv2-basis-mbti > .cv2-field { flex: 1 1 120px; min-width: 0; }
.cv2-basis-ai { white-space: nowrap; }
.cv2-basis-mbti-easy { margin: 6px 0 0; }
.cv2-basis-saju-row { margin-top: 14px; display: flex; flex-direction: column; gap: 4px; }
.cv2-basis-saju-line { margin: 0; font-size: 14px; line-height: 1.6; color: var(--sd-ink); }
.cv2-basis-detail { margin-top: 10px; }
.cv2-basis-detail > summary { cursor: pointer; color: var(--cv2-gold-ink); font-size: 13px; font-weight: 600; }
.cv2-basis-detail > summary:focus-visible { outline: 2px solid var(--cv2-gold-ink); outline-offset: 2px; border-radius: 4px; }
.cv2-basis-detail[open] > summary { margin-bottom: 8px; }
.cv2-basis-manual { margin-top: 10px; }
.cv2-basis-species { margin-top: 10px; }
.cv2-basis-actions { margin-top: 12px; display: flex; gap: 8px; flex-wrap: wrap; }
.cv2-identity-basis-note { margin: 12px 0 0; }

/* 문단 제목 앞 단계 번호(② ③ ④). 번호는 aria-hidden — 화면 낭독기는 이름만 읽는다. */
.cv2-para-title { display: inline-flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.cv2-basis-head > .cv2-para-step,
.cv2-para-step { color: var(--cv2-gold-ink); font-weight: 700; font-size: 15px; }

/* '앞 단계가 바뀌었어요 — 다시 추천받기'. 문단 격자(120px + 1fr)의 한 줄 전체를 쓴다(078 D 와 같은 이유). 낡지 않았으면
   hidden 자리만 남는다 — display:none 이라 격자 칸을 차지하지 않는다. */
.cv2-para:not(.ds-para) > .cv2-para-stale { grid-column: 1 / -1; }
.cv2-para-stale {
  margin: 6px 0 0; display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  font-size: 13px; color: var(--cv2-gold-ink);
}
.cv2-para-stale[hidden] { display: none; }

/* ① 안내 카드(새 주인공 — 옆 패널). 질문 카드와 같은 틀(.cv2-question)을 쓴다. */
.cv2-basis-guide { scroll-margin-top: calc(var(--cv2-topbar, 0px) + var(--cv2-sticky-head, 96px) + 56px); }
.cv2-basis-guide-lead { margin: 6px 0 8px; }
.cv2-basis-guide-steps {
  margin: 0 0 10px; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: 6px; font-size: 14px;
}
.cv2-pick-hint { color: var(--cv2-gold-ink); }

/* --------------------------------------------------------------------------
   support-saju-flow(2026-10-27) — 주변 인물도 같은 순서
   주변 인물만의 '인물 유형' 줄(.cv2-role-para)은 문단 격자와 같은 줄(.cv2-para — 120px 제목 + 본문)이다. 예전 신원 패널 안의
   라디오 여백(위아래 12px)을 문단 줄 높이에 맞춘다. 색 · 전이 없음(두 테마 · reduced-motion 영향 없음).
   -------------------------------------------------------------------------- */
.cv2-role-para .cv2-role-types { margin: 0 0 4px; }
.cv2-role-body { min-width: 0; }
.cv2-role-body > .sd-field-hint { margin: 0; }

/* --------------------------------------------------------------------------
   onto-cascade(2026-10-31) — 뒤 단계 반영 진행 칩 · 확인 카드 · 끝 카드 · 바뀐 곳 목록
   자리는 하나다: 새 틀은 AI 비서 칸 머리 바로 아래(stage-shell.splitFrame 이 옮긴다), 옛 틀은 단계 갱신 안내 위(workspace).
   30분 넘게 걸리는 일이라 대화 기록에 섞지 않고 늘 같은 자리에 둔다(design 5.1). 색은 기존 토큰만 — 두 테마는 토큰이 바꾼다.
   움직임은 막대 폭 전환 하나라 reduced-motion 에서 끈다. 560px 아래는 버튼을 한 줄에 하나씩 꽉 차게(손가락으로 누르기 쉽게).
   이 블록은 uiux-261029-b 블록 **앞**에 둔다 — 그 검사(UB6)가 자기 표식부터 파일 끝까지를 '전이 없음'으로 본다.
   -------------------------------------------------------------------------- */
.sd-cascade[hidden] { display: none; }
.sd-cascade {
  margin: 0 0 12px; padding: 10px 12px;
  border: 1px solid var(--sd-line); border-left: 3px solid var(--cv2-gold); border-radius: 10px;
  background: var(--sd-surface); color: var(--sd-ink); font-size: 14px;
}
/* 새 틀 칸 안: 칸 머리와 같은 좌우 여백. 목록이 길어도 대화 기록 자리를 다 먹지 않게 칩 높이를 줄인다(안에서 스크롤). */
.cv2-ai > .sd-cascade-host { flex: 0 0 auto; }
.cv2-ai > .sd-cascade-host > .sd-cascade { margin: 10px 16px 0; max-height: 45vh; overflow-y: auto; }
.sd-cascade-line { margin: 0; font-weight: 600; line-height: 1.5; overflow-wrap: anywhere; }
.sd-cascade-sub { margin: 4px 0 0; font-size: 13px; color: var(--sd-dim); }
.sd-cascade-track { margin: 8px 0 0; height: 6px; border-radius: 999px; background: var(--sd-line); overflow: hidden; }
.sd-cascade-fill { height: 100%; width: 0; background: var(--cv2-gold); transition: width 0.4s ease; }
.sd-cascade-notes { margin: 6px 0 0; padding-left: 18px; font-size: 13px; color: var(--sd-dim); }
.sd-cascade-actions { margin: 8px 0 0; display: flex; flex-wrap: wrap; gap: 6px; }
.sd-cascade-list { margin: 8px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 4px; }
.sd-cascade-item { font-size: 13px; line-height: 1.5; color: var(--sd-ink); overflow-wrap: anywhere; }
.sd-cascade-item.is-skipped { color: var(--sd-dim); }
.sd-cascade-item.is-failed { color: var(--sd-danger); }
.sd-cascade-link {
  padding: 2px 0; border: 0; background: none; color: var(--sd-ink);
  font: inherit; text-align: left; text-decoration: underline; cursor: pointer;
}
.sd-cascade-link:hover { text-decoration-thickness: 2px; }
.sd-cascade-link:focus-visible { outline: 2px solid var(--cv2-gold); outline-offset: 2px; }
/* 단계 갱신 안내의 '자동 반영 중인 곳 n건' 한 줄(stepper.drawStalePanel — 버튼 없음). */
.sd-stale-running { margin: 0 0 6px; font-size: 14px; color: var(--sd-dim); }
.sd-stale > .sd-stale-running:only-child { margin: 0; }
/* 칸이 접히거나 서랍이 닫혔을 때 손잡이의 '반영 중'(stage-shell.paintSplit) — '새 답'과 같은 테두리. */
.cv2-split.is-ai-cascading > .cv2-ai-reopen { border-color: var(--cv2-gold); }
/* 바뀐 곳 목록의 '그래도 다시 쓰기'(설계 11장 Q5) — 건너뛴 줄 글 뒤에 작은 버튼. */
.sd-cascade-item.is-skipped > .sd-button { margin-left: 6px; padding: 2px 10px; font-size: 13px; }
/* 바뀐 곳 표시(④ · R50 · design 5.4): 반영한 곳에 금색 테두리 + 작은 '반영됨' 글자. 표시는 정보라 글자로 둔다(화면 낭독기가
   읽는다). 움직임이 없다(reduced-motion 영향 없음). 테두리는 box-shadow — 문단 격자 · 탭의 크기를 바꾸지 않는다. */
.is-cascade-marked { box-shadow: 0 0 0 2px var(--cv2-gold); border-radius: 10px; }
.sd-cascade-mark {
  display: inline-block; margin-left: 6px; padding: 0 6px;
  border: 1px solid var(--cv2-gold); border-radius: 999px; background: var(--cv2-gold-soft);
  color: var(--sd-ink); font-size: 12px; font-weight: 600; line-height: 1.6; vertical-align: middle; white-space: nowrap;
}
/* 회차 탭 이름 옆 점 — 글자는 남기되(낭독기가 '반영됨'을 읽는다) 화면에는 점만 보인다. */
.sd-cascade-mark.is-dot {
  width: 8px; height: 8px; margin-left: 4px; padding: 0; border: 0; border-radius: 999px;
  background: var(--cv2-gold); font-size: 0; line-height: 0; overflow: hidden;
}
/* '표시 지우기' 줄 — 표시한 곳 바로 위(문서 카드 안). */
.sd-cascade-mark-note {
  margin: 0 0 8px; display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  font-size: 13px; color: var(--sd-dim);
}
.sd-cascade-mark-note > .sd-cascade-mark { margin-left: 0; }
/* 반영된 곳을 고쳐 저장하면(inline-confirm 093-⑥ · design D8) 그 자리 표시를 노드째 빼지 않고 감춘다 — 배지 · '표시 지우기' 줄을
   빼면 그 아래 쓰던 글이 위로 밀린다. visibility 라 자리는 그대로이고 화면 낭독기 · Tab 에서는 빠진다. 다음 그리기에서 걷힌다. */
.sd-cascade-mark.is-spent,
.sd-cascade-mark-note.is-spent { visibility: hidden; }
@media (prefers-reduced-motion: reduce) {
  .sd-cascade-fill { transition: none; }
}
@media (max-width: 560px) {
  .sd-cascade { padding: 10px; }
  .cv2-ai > .sd-cascade-host > .sd-cascade { margin: 8px 12px 0; }
  .sd-cascade-actions > .sd-button { flex: 1 1 100%; }
}


/* --------------------------------------------------------------------------
   inline-confirm(2026-11-01) — 화면 안 확인 · 입력 창(ask.js)
   브라우저 기본 창(window.confirm · prompt) 대신 쓰는 대화상자 하나. 바탕(.sd-ask-overlay)이 화면을 덮고 가운데 창(.sd-ask)이 뜬다.
   색은 토큰만이라 두 테마는 토큰이 바꾼다(바탕 --scrim · 그림자 --float-shadow 는 styles.css 의 두 테마 값). 주 버튼 글자는
   --invert-ink — 바탕 --sd-accent 가 앱 반전색이라 어두운 테마에서 흰색이다(진행 막대 지금 칸과 같은 짝). 위험한 일의 확인 버튼은
   기존 .sd-button-danger 그대로. 창은 body 끝에 붙어 .sd-page-project.is-v2 의 금색 주 버튼 규칙이 닿지 않는다.
   z-index 250 — 서랍 · 완료 카드(40) · 앱 모달(200) 위, 버튼 설명 말풍선(help-tip) 아래. 등장 움직임은 reduced-motion 에서 끈다.
   560px 아래는 버튼을 한 줄에 하나씩 꽉 차게. 이 블록은 uiux-261029-b 블록 앞에 둔다(그 검사 UB6 이 자기 표식부터 끝까지를 본다).
   -------------------------------------------------------------------------- */
.sd-ask-overlay {
  position: fixed; inset: 0; z-index: 250;
  display: flex; align-items: center; justify-content: center;
  padding: 24px; box-sizing: border-box;
  background: var(--scrim);
  animation: sd-ask-fade .16s ease;
}
.sd-ask {
  width: min(440px, 100%); max-height: calc(100vh - 48px); overflow-y: auto; box-sizing: border-box;
  padding: 22px 22px 18px;
  border: 1px solid var(--sd-line); border-radius: 14px;
  background: var(--sd-surface); color: var(--sd-ink);
  box-shadow: var(--float-shadow);
  animation: sd-ask-in .2s cubic-bezier(0.22, 1, 0.36, 1);
}
.sd-ask:focus { outline: none; }
.sd-ask-title { margin: 0; font-size: 16px; font-weight: 700; line-height: 1.5; overflow-wrap: anywhere; }
.sd-ask-detail { margin: 8px 0 0; font-size: 14px; line-height: 1.6; white-space: pre-line; overflow-wrap: anywhere; }
.sd-ask-input { margin-top: 14px; }
.sd-ask-actions { margin-top: 20px; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
.sd-ask-actions > .sd-button-primary { color: var(--invert-ink); }
.sd-ask-actions > .sd-button:focus-visible { outline: 2px solid var(--sd-accent); outline-offset: 2px; }
/* 데이터 가져오기 화면(migrate.js)의 확인 줄 — 그 화면은 모듈을 import 하지 않아(옛 캐시와 섞이지 않게) 이 창 대신 버튼 자리에
   안내 한 줄과 두 버튼을 펼친다(design D10). 색은 기존 .sd-notice · .sd-button 그대로. */
.sd-migrate-ask { margin: 12px 0 0; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.sd-migrate-ask > .sd-notice { flex: 1 1 100%; margin: 0; }
@keyframes sd-ask-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes sd-ask-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .sd-ask-overlay, .sd-ask { animation: none; }
}
@media (max-width: 560px) {
  .sd-ask-overlay { padding: 16px; }
  .sd-ask { padding: 18px 16px 14px; }
  .sd-ask-actions > .sd-button { flex: 1 1 100%; }
  .sd-migrate-ask > .sd-button { flex: 1 1 100%; }
}


/* --------------------------------------------------------------------------
   uiux-261029-b(2026-10-29) — ④ 현재 성격과 특징 4칸
   성격적 강점 · 성격적 약점(나열 — 2열) · 행동 특성 및 습관 · 상황별 반응 패턴(문장 — 한 줄 전체). 칸 본문은 문단 본문
   (.cv2-para-body)과 같은 편집 모양을 쓰되, 문단 격자용 음수 여백을 칸 안에서는 쓰지 않는다(칸 테두리 밖으로 넘친다).
   옛 인물의 한 칸 보기(.is-legacy)는 지금 문단 그대로 + 아래 나누기 제안 줄. 옆 고르기 카드의 4칸 미리보기(.cv2-pick-cells)는
   3줄로 접지 않는다(4칸 모두가 고를 근거). 색은 테마 토큰만(두 테마), 새 전이 · 애니메이션 없음(reduced-motion 영향 없음).
   -------------------------------------------------------------------------- */
.cv2-pcells { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 14px; min-width: 0; }
.cv2-pcells.is-legacy { display: flex; flex-direction: column; gap: 10px; }
@media (max-width: 720px) {
  .cv2-pcells { grid-template-columns: minmax(0, 1fr); }
}
.cv2-pcell { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.cv2-pcell.is-wide { grid-column: 1 / -1; }
.cv2-pcell-label { font-size: 13px; font-weight: 600; color: var(--sd-dim); }
.cv2-pcells .cv2-para-body.cv2-pcell-body {
  margin: 0; padding: 6px 10px; min-height: 1.7em;
  border-color: var(--sd-line); background: var(--sd-surface);
}
/* 위 규칙이 문단 공용 hover · focus 규칙과 특이도가 같고 뒤에 있어 그 둘을 덮는다 — 칸에서도 같은 표시가 나게 다시 둔다. */
.cv2-pcells .cv2-para-body.cv2-pcell-body[contenteditable="true"]:hover { background: var(--sd-bg); }
.cv2-pcells .cv2-para-body.cv2-pcell-body[contenteditable="true"]:focus-visible {
  outline: none; border-color: var(--cv2-gold); background: var(--sd-surface);
}
.cv2-pcells-legacy { display: flex; flex-direction: column; gap: 6px; }
.cv2-pcells-legacy-note { margin: 0; }
.cv2-pcells-legacy-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.cv2-pcells-source {
  grid-column: 1 / -1; margin: 2px 0 0; padding: 10px 12px;
  border: 1px dashed var(--sd-line); border-radius: 10px; background: var(--sd-bg);
}
.cv2-pcells-source-title { margin: 0 0 4px; font-size: 13px; font-weight: 600; color: var(--sd-dim); }
.cv2-pcells-source-text { margin: 0 0 6px; font-size: 14px; line-height: 1.6; color: var(--sd-ink); white-space: pre-wrap; word-break: break-word; }
.cv2-pick .cv2-pick-preview.cv2-pick-cells {
  display: flex; flex-direction: column; gap: 2px;
  -webkit-line-clamp: unset; line-clamp: none; overflow: visible;
}
.cv2-pick-cell { display: block; }
.cv2-pick-cell-label { font-weight: 600; color: var(--sd-dim); margin-right: 6px; }
.cv2-pick-cell-label::after { content: ":"; }


/* 100af(고객 요청 261105 2 · 3 · 4번): AI 비서 칸의 추천 상자(예전 질문창 자리) · 대화 제안 카드 '적용' · 갈림길 제안 카드.
   추천 상자 칩은 글이 길어 줄을 바꿀 수 있게 한다(.ds-pill 기본은 한 줄). 색은 기존 변수만 쓴다. */
.ds-suggest-box { display: flex; flex-direction: column; gap: 10px; }
.ds-suggest-group { display: flex; flex-direction: column; gap: 6px; }
.ds-suggest-head { margin: 0; font-size: 12px; font-weight: 700; color: var(--sd-dim); }
.ds-suggest-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.ds-pill.ds-suggest-chip { white-space: normal; flex-shrink: 1; text-align: left; justify-content: flex-start; }
.ds-suggest-hint { margin: 0; }
.cv2-ai-prop-apply.is-applied { border-style: dashed; }
.ds-branch-card { display: flex; flex-direction: column; gap: 8px; }
.ds-branch-kicker { margin: 0; font-size: 12px; font-weight: 700; color: var(--sd-dim); }
.ds-branch-choices { display: flex; flex-direction: column; gap: 8px; }
.ds-branch-choice { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; padding: 8px 10px; border: 1px solid var(--sd-line); border-radius: 10px; }
.ds-branch-choice-body { flex: 1 1 auto; min-width: 0; }
.ds-branch-choice-label { margin: 0; font-weight: 600; }
.ds-branch-choice-preview { margin: 4px 0 0; }
.ds-branch-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
