/* 오디오 드라마 스튜디오 — 전용 스타일. 허브/상세 카드는 기존 ep-* 클래스를 재사용하고,
   에디터(라이브러리·트랙·클립·믹스다운)와 AI 생성 모달만 여기서 정의(다크 글래스, glass.css 토큰 상속). */

/* ── 에디터 레이아웃 — ep-hub 와 동일한 페이지 여백 리듬, 폭은 넓게(편집 도구라 공간이 많이 필요) ── */
.audio-editor { --audio-head-w: 200px; width: 100%; max-width: 1920px; margin: 0 auto; height: 100dvh; overflow: hidden; box-sizing: border-box; padding: var(--sp-xxl) var(--sp-lg) var(--sp-section); gap: var(--sp-md); }
.audio-editor-bar { display: flex; align-items: center; gap: var(--sp-sm); flex-wrap: wrap; }
.audio-editor-actions { margin-left: auto; display: flex; gap: var(--sp-xs); align-items: center; flex-wrap: wrap; }
.audio-actions-sep { width: 1px; height: 18px; background: var(--hairline); margin: 0 4px; }
.audio-editor-time { font-size: var(--fs-caption); color: var(--ink-48); font-variant-numeric: tabular-nums; min-width: 44px; text-align: center; }
.audio-editor-body { display: flex; gap: var(--sp-lg); align-items: stretch; flex: 1 1 auto; min-height: 0; }
/* 고정 폭 flex 슬롯 — audioAssetLibraryPanel 이 자식(.audio-lib)에 className 을 다시 쓰더라도 이 클래스가 폭을 계속 강제(과거 라이브러리 폭이 타임라인 크기에 따라 흔들리던 버그 재발방지).
   높이는 부모(.audio-editor-body)가 공급하는 고정 잔대(100dvh 기준)를 align-items:stretch 로 그대로 받는다 — 타임라인(.audio-editor-main) 콘텐츠 길이와 무관하게 항상 화면을 거의 채운다(km-layout 패턴 미러). */
.audio-editor-lib { flex: 0 0 340px; width: 340px; max-width: 340px; display: flex; flex-direction: column; min-height: 0; }
.audio-editor-main { flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; flex-direction: column; gap: var(--sp-lg); overflow-y: auto; }
.audio-section { display: flex; flex-direction: column; gap: var(--sp-xs); }
.audio-section-head { display: flex; align-items: baseline; gap: var(--sp-sm); flex-wrap: wrap; }
.audio-section-title { margin: 0; font-size: var(--fs-caption); font-weight: 700; color: var(--ink-80); }
.audio-section-hint { font-size: 11px; color: var(--ink-48); }

/* ── 라이브러리 패널 — 고정 폭 슬롯(.audio-editor-lib) 안을 세로로 꽉 채우고, 목록만 내부 스크롤 ──
   .audio-editor-lib 자체는 폭만 고정하는 레이아웃 슬롯이고, 실제 패널 스타일은 자식(.audio-lib)에서만 정의된다. */
.audio-lib { flex: 1 1 auto; min-height: 0; width: 100%; display: flex; flex-direction: column; border: 1px solid var(--hairline); border-radius: var(--r-md); padding: var(--sp-md); background: rgba(255,255,255,0.02); }
.audio-lib-title { font-size: var(--fs-body); margin: 0 0 var(--sp-sm); color: var(--ink); }
.audio-lib-actions { display: flex; gap: var(--sp-xs); margin-bottom: var(--sp-sm); }
/* 오디오 편집기 버튼은 사각(--r-sm) 계열 — 알약형 .btn 기본을 덮는다.
 * ⚠️ .btn.X 로 특이도 2 필요: button.css 가 audioDrama.css 보다 뒤에 로드된다(index.html). */
.btn.audio-lib-btn { border-radius: var(--r-sm); border-color: var(--hairline); background: rgba(255,255,255,0.04); color: var(--ink); }
.btn.audio-lib-btn:hover:not(:disabled) { background: rgba(255,255,255,0.08); }
/* ── 범위 탭(회차·기획안·전체) — 340px 안을 셋이 균등 분할. 라벨이 짧아야 건수 배지까지 들어간다. ── */
.audio-lib-tabs { display: flex; gap: 2px; margin-bottom: var(--sp-xs); padding: 2px; border: 1px solid var(--hairline); border-radius: var(--r-sm); background: rgba(0,0,0,0.18); }
.btn.audio-lib-tab { flex: 1 1 0; min-width: 0; height: 26px; padding: 0 4px; gap: 4px; font-size: 11px; border-radius: var(--r-xs); border-color: transparent; background: transparent; color: var(--ink-48); }
.btn.audio-lib-tab:hover:not(:disabled) { background: rgba(255,255,255,0.05); color: var(--ink); }
.btn.audio-lib-tab.is-on { background: rgba(255,255,255,0.10); color: var(--ink); }
/* ⚠️ 선택된 탭의 hover 는 **따로** 적어야 한다. 위 :hover 규칙(클래스 4개)이 .is-on(3개)을 이겨서,
 * 그냥 두면 선택된 탭에 마우스를 올리는 순간 배경이 0.10 → 0.05 로 오히려 흐려진다(선택 표시 상실). */
.btn.audio-lib-tab.is-on:hover:not(:disabled) { background: rgba(255,255,255,0.14); color: var(--ink); }
/* 건수 배지 — 모르는 범위는 [hidden] 로 아예 안 그린다(0 으로 메우면 거짓말이 된다). */
.audio-lib-tab-ct { font-size: 10px; color: var(--ink-48); font-variant-numeric: tabular-nums; }
.audio-lib-tab-ct[hidden] { display: none; }
.btn.audio-lib-tab.is-on .audio-lib-tab-ct { color: var(--ink-80); }

/* ── 검색 + 종류 칩 — 칩은 반드시 wrap. 4개가 한 줄에 안 들어가는 폭에서도 가로 스크롤이 생기면 안 된다. ── */
.audio-lib-ctl { display: flex; flex-direction: column; gap: var(--sp-xs); margin-bottom: var(--sp-xs); }
.audio-lib-search { width: 100%; box-sizing: border-box; height: 28px; padding: 0 8px; border: 1px solid var(--hairline); border-radius: var(--r-sm); background: rgba(0,0,0,0.18); color: var(--ink); font-family: var(--font-sans); font-size: var(--fs-caption); }
.audio-lib-search::placeholder { color: var(--ink-48); }
.audio-lib-search:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-ring); }
/* 사파리 검색 취소 버튼이 다크 배경에서 검은 점으로 보인다 — planHub 와 같은 처방. */
.audio-lib-search::-webkit-search-cancel-button { filter: invert(0.7); cursor: pointer; }
.audio-lib-chips { display: flex; flex-wrap: wrap; gap: 4px; }
/* 채널 색은 --audio-chan 하나로 점·켜진 배경을 동시에 물들인다.
 * ⚠️ .btn.audio-lib-chip.is-on(클래스 3개)은 button.css 의 .btn--chip.is-on(2개)을 이기지만,
 *    **hover 는 별개다** — button.css 의 .btn--chip.is-on:hover:not(:disabled) 는 클래스 4개라 3개짜리를 이긴다.
 *    그래서 켜진 칩에 마우스를 올리면 채널색이 --accent-focus 로 덮여 어느 채널이 켜졌는지 사라진다.
 *    hover 판을 클래스 5개로 따로 못박아 둔다(이 주석의 선대 판본은 hover 를 빼먹어 사실과 달랐다). */
.btn.audio-lib-chip { border-radius: var(--r-pill); padding: 0 8px; gap: 5px; font-size: 11px; }
.btn.audio-lib-chip::before { content: ''; flex: 0 0 auto; width: 6px; height: 6px; border-radius: 50%; background: var(--audio-chan); }
.btn.audio-lib-chip.is-on { background: var(--audio-chan); border-color: var(--audio-chan); color: #0d0d12; }
.btn.audio-lib-chip.is-on:hover:not(:disabled) { background: var(--audio-chan); border-color: var(--audio-chan); color: #0d0d12; filter: brightness(1.08); }
.btn.audio-lib-chip.is-on::before { background: rgba(0,0,0,0.45); }
.audio-lib-chip--voice { --audio-chan: #4C9AE8; }
.audio-lib-chip--amb   { --audio-chan: #4FB89A; }
.audio-lib-chip--sfx   { --audio-chan: #E0913C; }
.audio-lib-chip--music { --audio-chan: #A97BD6; }

/* 범위 안내 줄(읽기전용·200개 절단) — 목록 바로 위 한 줄. 줄 개수를 늘리지 않는다. */
.audio-lib-hint { font-size: 10px; line-height: 1.45; color: var(--ink-48); margin-bottom: var(--sp-xs); }
.audio-lib-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 6px; }
.audio-lib-empty, .audio-tracks-empty { color: var(--ink-48); font-size: var(--fs-caption); padding: var(--sp-sm); }
.audio-lib-loading { padding: var(--sp-sm); color: var(--ink-48); }
/* 행은 이제 이름 외에 설명·메타까지 실을 수 있어 높이가 제각각이다 — 가운데 정렬하면 휴지통 아이콘이 본문 중간에 떠서 위로 붙인다. */
.audio-asset { display: flex; align-items: flex-start; gap: var(--sp-xs); padding: 8px; border: 1px solid var(--hairline); border-radius: var(--r-sm); background: rgba(255,255,255,0.03); cursor: grab; }
/* 전체 탭 항목은 드래그할 수 없다 — grab 커서를 남겨 두면 끌어보고 안 되는 것을 버그로 오해한다. */
.audio-asset--static { cursor: default; }
.audio-asset.is-dragging { opacity: 0.5; }
.audio-asset-main { flex: 1 1 auto; min-width: 0; }
.audio-asset-name { font-size: var(--fs-caption); color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.audio-asset-meta { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 2px; }
.audio-asset-dur { font-size: 11px; color: var(--ink-48); }
/* 설명 — 2줄에서 자른다. 공백 없는 긴 토큰이 340px 를 밀어내지 않게 overflow-wrap 까지 같이 건다. */
.audio-asset-desc { margin-top: 2px; font-size: 11px; line-height: 1.4; color: var(--ink-48); overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.audio-asset-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 3px; }
.audio-asset-mtag { min-width: 0; max-width: 100%; padding: 0 5px; border: 1px solid var(--hairline); border-radius: var(--r-xs); font-size: 10px; line-height: 1.5; color: var(--ink-48); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.audio-asset-mtag--plan { color: var(--ink-80); border-color: var(--divider-soft); }
.btn.audio-asset-import { flex: 0 0 auto; height: 24px; min-width: 0; padding: 0 8px; font-size: 10px; border-radius: var(--r-xs); }
.btn.audio-asset-del { width: 24px; min-width: 24px; height: 24px; border: none; border-radius: var(--r-xs); }
/* 라이브러리 미리듣기 버튼 — 타임라인에 넣기 전 원본을 귀로 확인. 재생 중이면 accent 로 상태를 드러낸다. */
.audio-asset-preview { flex: 0 0 auto; width: 22px; height: 22px; display: flex; align-items: center; justify-content: center; padding: 0; border: 1px solid var(--hairline); border-radius: 50%; background: rgba(255,255,255,0.05); color: var(--ink-80); cursor: pointer; font-size: 9px; line-height: 1; }
.audio-asset-preview:hover { border-color: var(--accent); color: var(--accent); }
.audio-asset-preview.is-playing { border-color: var(--accent); background: var(--accent); color: #0d0d12; }
.audio-src-badge { font-size: 10px; padding: 1px 6px; border-radius: var(--r-pill); border: 1px solid var(--hairline); color: var(--ink-80); }
.audio-src-badge--upload { background: rgba(120,120,140,0.18); }
.audio-src-badge--tts { background: rgba(90,140,220,0.20); }
.audio-src-badge--sfx { background: rgba(220,160,80,0.20); }
.audio-src-badge--music { background: rgba(150,110,210,0.20); }

/* ── 타임라인(눈금자 + 트랙 + 재생헤드) — 가로 스크롤 컨테이너 1개 공유, 트랙 헤더는 좌측 sticky 고정 ── */
.audio-tracks { display: flex; flex-direction: column; gap: var(--sp-sm); }
.audio-tracks-scroll { position: relative; overflow-x: auto; border: 1px solid var(--hairline); border-radius: var(--r-sm); background: rgba(0,0,0,0.12); }
.audio-ruler-row, .audio-track { display: flex; align-items: stretch; min-width: max-content; }
.audio-ruler-row { border-bottom: 1px solid var(--hairline); }
.audio-track + .audio-track { border-top: 1px solid var(--hairline); }
/* ⚠️ 재생헤드(z6)·갭밴드(z5)는 스크롤 콘텐츠를 따라 움직이는 일반 absolute 라, sticky 로 화면에
   고정된 이 헤더보다 z-index 가 낮으면 재생헤드가 헤더 뒤로 스크롤돼 들어올 때 헤더를 뚫고 그려진다
   (재생 위치보다 뒤 구간으로 스크롤하면 항상 재현 — 브라우저 무관, 2026-08-03). 헤더가 항상 위에 있어야
   스크롤된 재생헤드/갭밴드가 고정 헤더 칼럼 안에서는 가려진다. */
.audio-ruler-head, .audio-track-head { flex: 0 0 var(--audio-head-w); position: sticky; left: 0; z-index: 8; background: var(--canvas, #16161c); border-right: 1px solid var(--hairline); }
/* 눈금자는 드래그 스크럽 영역 — 잡을 수 있다는 것을 커서로 알린다(라벨 텍스트 선택은 JS 가 막는다). */
.audio-ruler { position: relative; height: 26px; cursor: ew-resize; user-select: none; }
.audio-tracks-scroll.is-scrubbing { cursor: ew-resize; }
.audio-ruler-tick { position: absolute; bottom: 0; width: 1px; height: 6px; background: rgba(255,255,255,0.2); }
.audio-ruler-label { position: absolute; top: 6px; padding-left: 4px; font-size: 10px; color: var(--ink-48); font-variant-numeric: tabular-nums; }
.audio-track-head { display: flex; flex-direction: column; gap: 8px; padding: 10px; }
.audio-track-name { background: rgba(0,0,0,0.2); border: 1px solid var(--hairline); border-radius: var(--r-xs); color: var(--ink); padding: 4px 6px; font-size: var(--fs-caption); }
.audio-track-ctrls { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.btn.audio-track-mute {
  height: 20px; min-width: 0; padding: 0 6px; gap: 4px;
  font-size: 10px; border-radius: var(--r-xs); border-color: var(--hairline); color: var(--ink-80);
}
.btn.audio-track-mute .btn__icons { width: 11px; height: 11px; }
.btn.audio-track-mute.is-on { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
/* 페이더는 헤더 폭을 전부 쓰는 전용 줄 — 버튼들과 같은 줄에 두면 72px 로 쪼그라들어
   손잡이 이동거리가 56px 밖에 안 된다(audioTrackLane 이 .audio-fader 를 head 에 직접 넣는다). */
.audio-track-vol { width: 100%; }
.audio-track-vol-db { font-size: 10px; color: var(--ink-48); font-variant-numeric: tabular-nums; min-width: 44px; }
.btn.audio-track-del { height: 20px; min-width: 0; padding: 0 4px; gap: 4px; font-size: 10px; border: none; }
.btn.audio-track-del .btn__icons { width: 11px; height: 11px; }
.audio-track-strip { position: relative; flex: 0 0 auto; min-height: 72px; background: rgba(255,255,255,0.02); }
.audio-track-strip.is-dragover { outline: 2px dashed var(--accent); outline-offset: -2px; }
/* 재생헤드 — --audio-playhead-x 는 JS 가 설정(트랙 헤더 폭 기준 오프셋은 CSS 변수로 해결해 JS 가 헤더폭을 몰라도 됨). */
.audio-playhead { position: absolute; top: 0; bottom: 0; left: calc(var(--audio-head-w) + var(--audio-playhead-x, 0px)); width: 2px; background: #ff5a5a; pointer-events: none; z-index: 6; }
/* 갭(클립 사이 빈 공간) 하이라이트 — 재생헤드와 같은 좌표 규약(헤더 폭 오프셋은 CSS 가 더한다).
   ⚠️ 리플 삭제가 모든 트랙을 함께 당기므로 띠도 컨테이너 전체 높이(top:0/bottom:0)를 덮는다 — 한 트랙만
   칠하면 "이 트랙만 당겨진다"고 오해한다. 클릭을 먹지 않도록 pointer-events 는 끈다. */
.audio-gap-band { position: absolute; top: 0; bottom: 0; left: calc(var(--audio-head-w) + var(--audio-gap-x, 0px)); width: var(--audio-gap-w, 0px); pointer-events: none; z-index: 5; }
.audio-gap-band--hover { background: rgba(255,255,255,0.05); border-left: 1px dashed var(--ink-48); border-right: 1px dashed var(--ink-48); }
.audio-gap-band--sel { background: rgba(122,162,247,0.18); border-left: 1px solid var(--accent); border-right: 1px solid var(--accent); }

/* ── 페이더(트랙 볼륨 · 마스터 게인 공용, audioFaderControl 산출물) ──
   ⚠️ 네이티브 range 는 손잡이를 자기 반지름만큼 트랙 안쪽에 물려, 값이 0/최대여도 손잡이가 양끝에
      닿지 않는다("볼륨 0인데 끝이 아님", 2026-07-28). 믹스다운 스크럽바(.podcast-player-*)와 같이
      트랙·채움·손잡이를 직접 그리고 range 는 투명하게 덮어 입력만 받는다 — 손잡이가 left:p% +
      translateX(-50%) 라 p=0% 에서 중심이 트랙 왼쪽 끝에 정확히 온다. */
.audio-fader { position: relative; height: 18px; display: flex; align-items: center; width: 100%; }
.audio-fader-bg { position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%); height: 5px; border-radius: var(--r-pill); background: rgba(255,255,255,0.12); overflow: hidden; }
.audio-fader-fill { position: absolute; top: 0; left: 0; height: 100%; width: 0%; background: var(--accent); }
/* 원음(0dB) 표식 — 테이퍼라 원음 지점이 눈으로 안 보인다(선형이면 정중앙이라 찾기 쉬웠다). */
.audio-fader-unity { position: absolute; top: 50%; left: var(--audio-fader-unity, 75%); width: 1px; height: 11px; transform: translate(-50%, -50%); background: rgba(255,255,255,0.4); pointer-events: none; }
.audio-fader-handle { position: absolute; top: 50%; left: 0%; width: 13px; height: 13px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 8px rgba(0,0,0,0.55); transform: translate(-50%, -50%); pointer-events: none; }
.audio-fader-input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; -webkit-appearance: none; appearance: none; background: transparent; }
.audio-fader-input:disabled { cursor: default; }
.audio-fader-input::-webkit-slider-runnable-track { background: transparent; }
.audio-fader-input::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; background: transparent; border: none; }
.audio-fader-input::-moz-range-track { background: transparent; }
.audio-fader-input::-moz-range-thumb { width: 16px; height: 16px; background: transparent; border: none; }

/* ── 마스터 출력 dB 미터(우측 고정 패널, 프리미어 프로 미러) — 재생 중 실시간 L/R 피크 ── */
.audio-master-meter { flex: 0 0 112px; width: 112px; display: flex; flex-direction: column; align-items: center; gap: 6px; padding: var(--sp-sm) 4px; }
/* 마스터 게인 페이더 — 게이지 최상단(audioMasterMeter.js 가 valueEl 보다 먼저 넣는다).
   패널을 86→112px 로 넓혀 페이더 이동거리를 확보했다(72→96px). 매핑은 트랙 페이더와 같은 테이퍼. */
.audio-master-meter-gain { display: flex; flex-direction: column; align-items: center; gap: 3px; }
.audio-master-meter-gain-label { font-size: 9px; color: var(--ink-48); letter-spacing: 0.02em; }
.audio-master-meter-gain-input { width: 96px; }
.audio-master-meter-gain-db { font-size: 9px; color: var(--ink-48); font-variant-numeric: tabular-nums; }
.audio-master-meter-value { font-size: 10px; color: var(--ink-80); font-variant-numeric: tabular-nums; white-space: nowrap; }
.audio-master-meter-body { position: relative; flex: 1 1 auto; min-height: 0; display: flex; gap: 6px; align-self: stretch; justify-content: center; }
.audio-master-meter-scale { position: relative; width: 24px; align-self: stretch; }
.audio-master-meter-tick { position: absolute; right: 0; left: 0; text-align: right; transform: translateY(50%); font-size: 8px; color: var(--ink-48); font-variant-numeric: tabular-nums; }
.audio-master-meter-bars { display: flex; gap: 4px; align-self: stretch; }
/* 라벨을 track 바로 아래 같은 열에 두어 바와 구조적으로 정렬(폭이 다른 두 flex row가 독립적으로 중앙정렬되며 어긋나던 문제 수정). */
.audio-meter-col { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.audio-meter-col .audio-meter-track { flex: 1 1 auto; min-height: 0; }
.audio-meter-track { position: relative; width: 12px; border-radius: 2px; overflow: hidden; background: linear-gradient(to top, #4caf6e 0%, #4caf6e 68%, #e0c94e 68%, #e0c94e 88%, #e2493d 88%, #e2493d 100%); }
.audio-meter-mask { position: absolute; top: 0; left: 0; right: 0; height: 100%; background: rgba(12,12,16,0.94); }
.audio-meter-peak { position: absolute; left: 0; right: 0; height: 2px; background: #f2f2f5; box-shadow: 0 0 4px rgba(255,255,255,0.7); }
.audio-master-meter-ch { font-size: 8px; color: var(--ink-48); width: 12px; text-align: center; }

/* ── 클립 — 파형 캔버스 + 트림 핸들 + 선택 강조 ── */
.audio-clip { position: absolute; top: 8px; bottom: 8px; background: rgba(90,140,220,0.35); border: 1px solid rgba(120,170,240,0.6); border-radius: var(--r-xs); cursor: grab; overflow: visible; min-width: 28px; }
.audio-clip.is-selected { outline: 2px solid var(--accent); outline-offset: 1px; }
.audio-clip--missing { background: rgba(220,80,80,0.3); border-color: rgba(240,120,120,0.7); }
/* 드래그 중 클립 — 다른 클립 위로 떠서 트랙을 갈아탈 수 있게. pointer-events 는 JS 가 끈다(elementFromPoint 가 밑의 트랙을 봐야 함). */
.audio-clip.is-dragging { z-index: 7; opacity: 0.85; cursor: grabbing; box-shadow: 0 4px 14px rgba(0,0,0,0.45); }
/* 드롭 프리뷰 고스트 — 놓기 전 '여기 생긴다'를 파형까지 미리 보여준다. 드래그 타깃이 되면 안 되므로 pointer-events:none. */
.audio-clip--ghost { pointer-events: none; z-index: 5; opacity: 0.5; border-style: dashed; border-color: var(--accent); background: rgba(90,140,220,0.2); }
.audio-clip-label { position: relative; z-index: 2; display: block; max-width: 100%; box-sizing: border-box; font-size: 10px; color: var(--ink); padding: 3px 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; background: rgba(0,0,0,0.25); }
.audio-clip-wave { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; pointer-events: none; border-radius: inherit; }
/* 트림 핸들 — 히트영역 14px(클립 경계를 살짝 넘어 잡기 쉽게), 시각은 중앙 얇은 바 1개로 절제. */
.audio-clip-handle { position: absolute; top: 0; bottom: 0; width: 14px; z-index: 4; cursor: ew-resize; display: flex; align-items: center; justify-content: center; }
.audio-clip-handle--l { left: -4px; }
.audio-clip-handle--r { right: -4px; }
.audio-clip-handle-bar { width: 3px; height: 60%; border-radius: 2px; background: rgba(255,255,255,0.4); transition: background var(--t-fast) var(--ease); }
.audio-clip-handle:hover .audio-clip-handle-bar { background: var(--accent); }
/* 드래그(이동/트림) 중 실시간 피드백 툴팁 — 커서를 따라다니는 작은 시간/길이 표시(프리미어·캡컷 미러). */
.audio-clip-drag-tooltip { position: fixed; z-index: 90; padding: 3px 7px; border-radius: var(--r-xs); background: var(--canvas, #16161c); border: 1px solid var(--hairline); color: var(--ink); font-size: 11px; font-variant-numeric: tabular-nums; pointer-events: none; white-space: nowrap; }

/* ── 게인 라인(프리미어 미러) — 겉보기 2px 줄, 히트영역 11px. 세로 위치 = 게인(중앙이 원음 1.00×) ── */
.audio-clip-gain-hit { position: absolute; left: 0; right: 0; height: 11px; margin-top: -5.5px; z-index: 3; cursor: ns-resize; display: flex; align-items: center; }
.audio-clip-gain-line { width: 100%; height: 2px; background: rgba(255,220,120,0.75); }
.audio-clip-gain-hit:hover .audio-clip-gain-line, .audio-clip-gain-hit.is-active .audio-clip-gain-line { background: #ffd67a; box-shadow: 0 0 6px rgba(255,214,122,0.6); }
/* 값 라벨은 드래그 중(is-active)에만 — 항상 띄우면 파형과 라벨이 뒤엉킨다. */
.audio-clip-gain-label { position: absolute; right: 3px; top: -15px; padding: 1px 4px; border-radius: var(--r-xs); background: rgba(0,0,0,0.72); color: #ffd67a; font-size: 9px; font-variant-numeric: tabular-nums; white-space: nowrap; opacity: 0; transition: opacity var(--t-fast) var(--ease); pointer-events: none; }
.audio-clip-gain-hit.is-active .audio-clip-gain-label { opacity: 1; }

/* ── 게인 키프레임(시간대별 볼륨 자동화) — 곡선은 SVG polyline, 점은 div.
   ⚠️ 래퍼는 클립 전체를 덮으므로 pointer-events: none 이어야 한다. 점·곡선만 이벤트를 받는다 —
      래퍼가 받으면 클립 이동 드래그가 전부 막힌다(게인 편집이 이동을 잡아먹는다). ── */
.audio-clip-gain-kf { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
.audio-clip-gain-kf-svg { display: block; width: 100%; height: 100%; overflow: visible; }
.audio-clip-gain-kf-line { stroke: #ffd67a; stroke-width: 2; vector-effect: non-scaling-stroke; pointer-events: none; }
/* 히트 스트로크는 투명하지만 12px 로 굵다 — 얇은 곡선은 더블클릭으로 맞히기 어렵다(게인 라인 히트영역과 동일 취지). */
.audio-clip-gain-kf-hitline { stroke: transparent; stroke-width: 12; vector-effect: non-scaling-stroke; pointer-events: stroke; cursor: copy; }
.audio-clip-gain-kf-dot { position: absolute; width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; border-radius: 50%; background: #ffd67a; border: 1px solid rgba(0,0,0,0.55); box-shadow: 0 0 5px rgba(255,214,122,0.55); pointer-events: auto; cursor: ns-resize; transition: transform var(--t-fast) var(--ease); }
.audio-clip-gain-kf-dot:hover { transform: scale(1.3); }
.audio-clip-gain-kf.is-active .audio-clip-gain-label { opacity: 1; }

/* ── 영역 드래그 선택 사각형 — fixed(가로 스크롤 컨테이너에 잘리지 않게 body 자식) ── */
.audio-marquee { position: fixed; z-index: 85; border: 1px solid var(--accent); background: rgba(122,162,247,0.14); pointer-events: none; border-radius: 2px; }

/* 호버 툴팁(.hovertip)은 오디오 전용이 아니라 버튼 프리미티브 전체가 쓴다 → components.css 로 옮겼다. */

/* ── 클립 단위 뮤트 ── */
/* 뮤트된 클립은 믹스다운·프리뷰에서 빠진다 → 눈으로도 빠져 보여야 한다(흐리게 + 배지).
   배지는 **우하단** — 좌상단 라벨·우상단 EQ 배지와 자리를 다투지 않는다.
   ⚠️ is-dragging(0.85) 보다 뒤에 둔다 — 같은 특이도라 나중 규칙이 이긴다: 끌고 있는 동안에도 뮤트는 흐리게 보인다. */
.audio-clip.is-muted { opacity: 0.5; }
.audio-clip-mute-badge { position: absolute; bottom: 2px; right: 3px; z-index: 2; padding: 0 4px; border-radius: var(--r-xs); background: rgba(0,0,0,0.72); border: 1px solid rgba(255,255,255,0.32); color: var(--ink-80); font-size: 8px; font-weight: 700; letter-spacing: 0.04em; pointer-events: none; }

/* ── 클립 우클릭 컨텍스트 메뉴 ── */
/* 백드롭이 바깥 클릭·스크롤·우클릭을 받아 닫는다(document 리스너 없음) → 투명하되 이벤트는 받아야 한다.
   z-index 는 클립 상세 팝오버(.audio-clip-editor, 80)·마퀴(85)·드래그 툴팁(90) 위. */
.audio-ctx-backdrop { position: fixed; inset: 0; z-index: 94; }
/* 폭은 JS 의 MENU_W 와 같아야 한다(우측 이탈 클램프 계산이 이 값을 쓴다). */
.audio-ctx-menu { position: fixed; z-index: 95; width: 200px; display: flex; flex-direction: column; gap: 1px; padding: 5px; background: var(--canvas, #16161c); border: 1px solid var(--hairline); border-radius: var(--r-sm); box-shadow: 0 10px 28px rgba(0,0,0,0.55); outline: none; }
/* 항목은 btn() 프리미티브 — 메뉴 줄로 보이게 좌측 정렬 + 알약 대신 사각형으로 되돌린다.
   ⚠️ 반드시 `.btn.` 을 앞에 붙인다 — button.css 가 audioDrama.css **뒤에** 로드되므로 한 클래스 선택자는
      `.btn` 의 justify-content:center·padding·border-radius 에 그대로 진다(이 repo 의 다른 오버라이드도 전부 이 형태다). */
.btn.audio-ctx-item { justify-content: flex-start; height: 28px; gap: 8px; padding: 0 8px; border-radius: var(--r-xs); font-size: 11px; color: var(--ink-80); }
.btn.audio-ctx-item .btn__icons { width: 13px; height: 13px; }
.btn.audio-ctx-item:hover:not(:disabled) { background: rgba(255,255,255,0.08); color: var(--ink); }
/* 삭제는 위에 구분선을 둬 손이 미끄러지는 것을 막고, 색으로도 갈라 둔다.
   ⚠️ 색을 여기서 다시 준다 — `.btn--danger`(한 클래스)는 위 `.btn.audio-ctx-item`(두 클래스)의 color 에
      특이도로 져서, 다시 쓰지 않으면 삭제 줄이 다른 항목과 똑같은 회색으로 보인다. */
.btn.audio-ctx-item--danger { margin-top: 4px; border-top: 1px solid var(--hairline); border-radius: 0 0 var(--r-xs) var(--r-xs); color: var(--danger, #d86a6a); }
.btn.audio-ctx-item--danger:hover:not(:disabled) { background: rgba(216,106,106,0.14); color: var(--danger, #d86a6a); }

/* ── 클립 EQ ── */
/* EQ 가 걸린 클립 표식 — 우상단 작은 칩. 파형·라벨과 겹치지 않게 z-index 는 게인 레이어(3) 아래. */
.audio-clip-eq-badge { position: absolute; top: 2px; right: 3px; z-index: 2; padding: 0 4px; border-radius: var(--r-xs); background: rgba(0,0,0,0.6); border: 1px solid rgba(255,214,122,0.5); color: #ffd67a; font-size: 8px; font-weight: 700; letter-spacing: 0.04em; pointer-events: none; }

/* EQ 드로어 — ep-drawer 골격 재사용. 밴드 줄이 7열이라 폭을 조금 넓힌다(더 좁으면 슬라이더가 잡히지 않는다). */
.audio-eq-drawer.ep-drawer { width: min(620px, 94vw); }
.audio-eq-body { gap: var(--sp-md); }
/* 응답곡선 캔버스 — 높이는 CSS 가 정하고 JS 가 dpr 로 backing store 를 맞춘다(clientWidth/Height 를 읽는다).
   ⚠️ touch-action: none — 점을 끌 때 브라우저가 그 제스처를 스크롤로 가로채면 드래그가 통째로 안 먹는다.
   커서는 JS 가 점 위/밖에 따라 바꾼다(게인 없는 타입은 가로 전용이라 ew-resize). */
.audio-eq-curve { display: block; width: 100%; height: 184px; border: 1px solid var(--hairline); border-radius: var(--r-sm); background: rgba(0,0,0,0.28); touch-action: none; }
.audio-eq-hint { font-size: var(--fs-caption); color: var(--ink-48); line-height: 1.5; }
/* EQ 전체에 걸리는 컨트롤 한 줄 — 프리셋 · 바이패스 · 출력 게인. */
.audio-eq-global { display: grid; grid-template-columns: minmax(0, 1.1fr) auto minmax(0, 1fr); align-items: end; gap: 8px; }
.audio-eq-preset { width: 100%; min-width: 0; }
.audio-eq-list { display: flex; flex-direction: column; gap: 6px; }
.audio-eq-empty { padding: var(--sp-md); border: 1px dashed var(--hairline); border-radius: var(--r-sm); color: var(--ink-48); font-size: var(--fs-caption); text-align: center; }
.audio-eq-band { display: grid; grid-template-columns: 18px 88px 1fr 1fr 1fr 26px 26px; align-items: center; gap: 7px; padding: 8px; border: 1px solid var(--hairline); border-radius: var(--r-sm); background: rgba(255,255,255,0.02); }
/* 선택된 밴드 — 곡선 위 점의 흰 테두리와 짝이다(어느 점이 이 줄인지 양방향으로 읽힌다). */
.audio-eq-band.is-selected { border-color: rgba(255,214,122,0.55); background: rgba(255,214,122,0.07); }
/* 우회된 밴드 — 지운 것이 아니라 지금 안 걸린 것이므로 흐리게만 둔다(값은 그대로 보인다). */
.audio-eq-band.is-off { opacity: 0.55; }
/* 번호 색은 JS 가 타입 색으로 덮는다(곡선 위 점과 같은 색). */
.audio-eq-band-idx { color: var(--ink-48); font-size: 10px; font-weight: 700; font-variant-numeric: tabular-nums; text-align: center; }
.audio-eq-select { background: rgba(0,0,0,0.25); border: 1px solid var(--hairline); border-radius: var(--r-xs); color: var(--ink); padding: 3px 4px; font-size: 11px; }
.audio-eq-field { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.audio-eq-field-label { color: var(--ink-48); font-size: 9px; }
.audio-eq-field--empty { color: var(--ink-48); font-size: 9px; justify-content: center; }
.audio-eq-range { width: 100%; min-width: 0; accent-color: #ffd67a; }
.audio-eq-num { width: 100%; min-width: 0; background: rgba(0,0,0,0.25); border: 1px solid var(--hairline); border-radius: var(--r-xs); color: var(--ink); padding: 2px 4px; font-size: 10px; font-variant-numeric: tabular-nums; }
.btn.audio-eq-band-off,
.btn.audio-eq-band-del { width: 24px; height: 24px; padding: 0; border-color: var(--hairline); color: var(--ink-48); }
.btn.audio-eq-band-off.is-on { color: #ffd67a; border-color: rgba(255,214,122,0.5); }
.audio-eq-add { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
/* 좁은 화면에서는 한 줄 그리드가 무너진다 → 라벨/컨트롤을 아래로 흘린다. */
@media (max-width: 640px) {
  .audio-eq-band { grid-template-columns: 18px 1fr 1fr 26px; }
  .audio-eq-global { grid-template-columns: 1fr; align-items: stretch; }
}

/* 클립 인라인 편집 팝오버 — 가로 스크롤 컨테이너에 잘리지 않게 fixed(토글 시 JS 가 좌표 계산) */
.audio-clip-editor { position: fixed; z-index: 80; width: 240px; display: flex; flex-direction: column; gap: 4px; padding: 10px; background: var(--canvas, #16161c); border: 1px solid var(--hairline); border-radius: var(--r-sm); box-shadow: 0 8px 24px rgba(0,0,0,0.5); }
.audio-clip-field { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.audio-clip-field-label { font-size: 11px; color: var(--ink-80); }
.audio-clip-num { width: 90px; background: rgba(0,0,0,0.25); border: 1px solid var(--hairline); border-radius: var(--r-xs); color: var(--ink); padding: 3px 5px; font-size: 11px; }
.audio-clip-durnote { font-size: 10px; color: var(--ink-48); }
.audio-clip-editor-row { display: flex; gap: 6px; margin-top: 4px; }
.btn.audio-clip-mini-btn {
  flex: 1 1 0; min-width: 0; height: 24px; padding: 0 4px; gap: 4px;
  font-size: 11px; border-radius: var(--r-xs); border-color: var(--hairline); color: var(--ink-80);
}
.btn.audio-clip-mini-btn .btn__icons { width: 12px; height: 12px; }
.btn.audio-clip-mini-btn:hover:not(:disabled) { background: rgba(255,255,255,0.08); }
.btn.audio-clip-mini-btn--danger { color: var(--danger, #d86a6a); border-color: rgba(216,106,106,0.4); }
.btn.audio-add-track { align-self: flex-start; }

/* ── 트랜스포트 · 믹스다운 ── */
.btn.audio-transport-btn {
  border-radius: var(--r-sm); border-color: var(--hairline);
  background: rgba(255,255,255,0.04); color: var(--ink);
  min-width: 0; padding: 0 10px; /* 툴바에 8개가 늘어서므로 min-width 를 풀어 폭을 아낀다 */
}
.btn.audio-transport-btn:hover:not(:disabled) { background: rgba(255,255,255,0.08); }
/* 되돌리기/다시실행 — 스택이 비면 비활성(눌러도 무반응이라 고장으로 오해되는 것을 막는다). */
.btn.audio-transport-btn:disabled:hover { background: rgba(255,255,255,0.04); }

/* ── 팟캐스트 완성 플레이어(믹스다운 "done") — audioMixdownPlayer.js 전용, 프리미어 프로급 다크 카드 ── */
.podcast-player { position: relative; width: 100%; max-width: 560px; display: flex; flex-direction: column; gap: var(--sp-md); padding: var(--sp-lg); border-radius: var(--r-lg); background: linear-gradient(145deg, var(--parchment) 0%, var(--canvas) 100%); border: 1px solid rgba(255,255,255,0.08); box-shadow: var(--shadow-product), inset 0 1px 0 rgba(255,255,255,0.08); }
.podcast-player-head { display: flex; align-items: center; gap: var(--sp-sm); min-width: 0; }
.podcast-player-meta { min-width: 0; }
.podcast-player-title { font-size: var(--fs-body); font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.podcast-player-sub { font-size: var(--fs-caption); color: var(--ink-80); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.podcast-player-scrub { display: flex; flex-direction: column; gap: 6px; }
.podcast-player-track { position: relative; height: 20px; display: flex; align-items: center; }
.podcast-player-track-bg { position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%); height: 6px; border-radius: var(--r-pill); background: rgba(255,255,255,0.1); overflow: hidden; }
.podcast-player-fill { position: absolute; top: 0; left: 0; height: 100%; width: 0%; border-radius: var(--r-pill); background: var(--accent); }
.podcast-player-handle { position: absolute; top: 50%; left: 0%; width: 14px; height: 14px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 10px rgba(0,0,0,0.5); transform: translate(-50%, -50%); pointer-events: none; }
.podcast-player-seek { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; -webkit-appearance: none; appearance: none; background: transparent; }
.podcast-player-seek::-webkit-slider-runnable-track { background: transparent; }
.podcast-player-seek::-webkit-slider-thumb { -webkit-appearance: none; width: 20px; height: 20px; background: transparent; border: none; }
.podcast-player-seek::-moz-range-track { background: transparent; }
.podcast-player-seek::-moz-range-thumb { width: 20px; height: 20px; background: transparent; border: none; }
.podcast-player-time { display: flex; justify-content: space-between; font-size: var(--fs-fine); color: var(--ink-48); font-variant-numeric: tabular-nums; }
.podcast-player-ctrls { display: flex; align-items: center; gap: var(--sp-sm); }
.podcast-player-vol { display: flex; align-items: center; gap: 4px; margin-right: auto; }
/* ⚠️ all:unset 을 뺐다 — 프리미티브(.btn)를 쓰는 지금은 그것이 형태를 통째로 지운다. 치수만 덮는다. */
.btn.podcast-player-vol-btn { width: 28px; min-width: 28px; height: 28px; border-radius: var(--r-sm); }
.podcast-player-vol-slider { width: 0; opacity: 0; margin: 0; height: 4px; -webkit-appearance: none; appearance: none; background: transparent; transition: width var(--t-fast) var(--ease), opacity var(--t-fast) var(--ease); }
.podcast-player-vol:hover .podcast-player-vol-slider, .podcast-player-vol:focus-within .podcast-player-vol-slider { width: 120px; opacity: 1; margin-left: 4px; }
.podcast-player-vol-slider::-webkit-slider-runnable-track { height: 4px; border-radius: var(--r-pill); background: rgba(255,255,255,0.15); }
.podcast-player-vol-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 10px; height: 10px; margin-top: -3px; border-radius: 50%; background: var(--accent); }
.podcast-player-vol-slider::-moz-range-track { height: 4px; border-radius: var(--r-pill); background: rgba(255,255,255,0.15); }
.podcast-player-vol-slider::-moz-range-thumb { width: 10px; height: 10px; border: none; border-radius: 50%; background: var(--accent); }
.btn.podcast-player-skip, .btn.podcast-player-download {
  width: 36px; min-width: 36px; height: 36px; padding: 0; gap: 1px;
  border-radius: 50%; border: none;
  font-size: 9px; font-weight: 600; color: var(--ink-80); background: rgba(255,255,255,0.05);
}
/* 스킵은 아이콘 위 + 숫자 아래(36px 안에 나란히는 안 들어간다) */
.btn.podcast-player-skip { flex-direction: column; }
.btn.podcast-player-skip .btn__icons { width: 13px; height: 13px; }
.btn.podcast-player-skip:hover, .btn.podcast-player-download:hover { background: rgba(255,255,255,0.1); color: var(--ink); }
/* (수동 일시정지 막대는 morph 아이콘으로 대체돼 제거됨) */
.btn.podcast-player-play {
  width: 52px; min-width: 52px; height: 52px; border-radius: 50%; border: none;
  background: var(--accent); color: var(--on-accent); box-shadow: 0 8px 16px rgba(0,0,0,0.3);
}
.btn.podcast-player-play .btn__icons { width: 20px; height: 20px; }
.btn.podcast-player-play:hover:not(:disabled) { background: var(--accent-focus); }
.btn.podcast-player-play:active:not(:disabled) { transform: scale(0.92); }
.btn.podcast-player-speed {
  margin-left: auto; min-width: 0; padding: 0 10px; border: none; border-radius: var(--r-sm);
  background: rgba(255,255,255,0.05); color: var(--ink-80);
  font-size: var(--fs-fine); font-variant-numeric: tabular-nums;
}
.btn.podcast-player-speed:hover { background: rgba(255,255,255,0.1); color: var(--ink); }

/* ── 믹스다운 완성 팝업 모달 — audioMixdownModal.js 전용, .podcast-player 카드를 배경+닫기로만 감싸다(이중 카드화 회피) ── */
.podcast-modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1001; width: min(560px, 94vw); display: flex; flex-direction: column; gap: var(--sp-xs); }
.podcast-modal-close-row { display: flex; justify-content: flex-end; }
/* 배경 블러 — fb-overlay/safetyModal 패턴 미러(다른 모달과 동일한 흐림처리). */
.podcast-modal-backdrop { position: fixed; inset: 0; z-index: 1000; background: rgba(0,0,0,0.55); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); }

/* ── AI 생성 모달 ── */
.audio-gen-modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1001; width: min(480px, 92vw); max-height: 88vh; overflow-y: auto; background: var(--canvas, #16161c); border: 1px solid var(--hairline); border-radius: var(--r-md); padding: var(--sp-md); box-shadow: 0 20px 60px rgba(0,0,0,0.6); }
.audio-gen-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--sp-sm); }
.audio-gen-title { font-size: var(--fs-body); margin: 0; color: var(--ink); }
.audio-gen-tabs { display: flex; gap: 4px; margin-bottom: var(--sp-sm); }
.btn.audio-gen-tab {
  flex: 1 1 0; min-width: 0; font-size: var(--fs-caption);
  border-radius: var(--r-sm); border-color: var(--hairline); color: var(--ink-80);
}
.btn.audio-gen-tab.is-active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.audio-gen-form { display: flex; flex-direction: column; gap: 6px; }
.audio-gen-label { font-size: var(--fs-caption); color: var(--ink-80); margin-top: 4px; }
.audio-gen-select, .audio-gen-num { background: rgba(0,0,0,0.25); border: 1px solid var(--hairline); border-radius: var(--r-xs); color: var(--ink); padding: 6px 8px; font-size: var(--fs-caption); }
.audio-gen-text { min-height: 88px; resize: vertical; }
.audio-gen-foot { display: flex; justify-content: flex-end; margin-top: var(--sp-md); }

@media (max-width: 860px) {
  .audio-editor { height: auto; overflow: visible; padding: var(--sp-lg) var(--sp-md) var(--sp-xxl); }
  .audio-editor-body { flex-direction: column; min-height: 0; }
  .audio-editor-lib { flex-basis: auto; width: auto; max-width: none; }
  .audio-editor-main { overflow-y: visible; }
  .audio-lib-list { max-height: 40vh; }
  .audio-master-meter { display: none; }
}

/* ── 대본 에디터(WS3) ─────────────────────────────────────────────────────────
   진입이 빈 타임라인에서 대본으로 바뀌는 화면. 타임라인 에디터(.audio-editor)와 달리 세로 스크롤 문서라
   100dvh 로 가두지 않는다 — 68블록을 위아래로 훑는 것이 이 화면의 주 동작이다.
   ⚠️ .btn.X 로 특이도 2 필요: button.css 가 audioDrama.css 보다 뒤에 로드된다(index.html). */
.script-editor { --script-warn: #e2b341; width: 100%; max-width: 1100px; margin: 0 auto; box-sizing: border-box; padding: var(--sp-xxl) var(--sp-lg) var(--sp-section); display: flex; flex-direction: column; gap: var(--sp-md); }
.script-head { display: flex; flex-direction: column; gap: var(--sp-sm); position: sticky; top: 0; z-index: 3; padding-bottom: var(--sp-xs); background: linear-gradient(to bottom, var(--canvas) 76%, transparent); }
.script-bar { display: flex; align-items: center; gap: var(--sp-sm); flex-wrap: wrap; }
.script-title { margin: 0; font-size: 1.3rem; color: var(--ink); }
.script-ver, .script-status { font-size: var(--fs-caption); color: var(--ink-48); font-variant-numeric: tabular-nums; }
.script-status { padding: 2px 8px; border: 1px solid var(--hairline); border-radius: 999px; color: var(--ink-80); }
.btn.script-save, .btn.script-approve { margin-left: auto; }
.btn.script-approve { margin-left: 0; }
.script-fail-msg { margin-bottom: var(--sp-sm); }

/* 언어 토글 + 미동기 요약 — 승인이 잠기는 이유를 승인 버튼을 누르기 전에 보여 준다(AC-27). */
.script-lang { display: flex; align-items: center; gap: var(--sp-sm); flex-wrap: wrap; }
.script-lang-group { display: flex; gap: 2px; padding: 2px; border: 1px solid var(--hairline); border-radius: var(--r-sm); background: rgba(0,0,0,0.18); }
.btn.script-lang-btn { height: 26px; min-width: 44px; padding: 0 10px; border-radius: var(--r-xs); border-color: transparent; background: transparent; color: var(--ink-48); font-size: 11px; }
.btn.script-lang-btn:hover:not(:disabled) { background: rgba(255,255,255,0.05); color: var(--ink); }
.btn.script-lang-btn.is-on { background: rgba(255,255,255,0.10); color: var(--ink); }
.btn.script-lang-btn.is-on:hover:not(:disabled) { background: rgba(255,255,255,0.14); color: var(--ink); }
.script-lang-unsynced { font-size: var(--fs-caption); color: var(--script-warn); }
.script-lang-synced { font-size: var(--fs-caption); color: var(--ink-48); }

.script-tabs { display: flex; gap: var(--sp-xs); }
.btn.script-tab { height: 28px; border-radius: var(--r-sm); }

/* 오디오 바이블 — 접히는 5줄. 여기 한 줄이 그 회차 효과음·음악 전부의 프롬프트 프리픽스가 된다. */
.script-bible { border: 1px solid var(--hairline); border-radius: var(--r-md); padding: var(--sp-sm) var(--sp-md); background: rgba(255,255,255,0.02); }
.script-bible-sum { cursor: pointer; font-size: var(--fs-caption); font-weight: 700; color: var(--ink-80); }
.script-bible-note { font-size: 11px; color: var(--ink-48); margin: var(--sp-xs) 0 var(--sp-sm); }
.script-bible-row { display: flex; align-items: center; gap: var(--sp-sm); margin-bottom: 6px; }
.script-bible-label { flex: 0 0 72px; font-size: 11px; color: var(--ink-48); }
.script-bible-input { flex: 1 1 auto; min-width: 0; height: 28px; padding: 0 8px; border: 1px solid var(--hairline); border-radius: var(--r-sm); background: rgba(0,0,0,0.22); color: var(--ink); font-size: var(--fs-caption); }

/* 보이스 캐스팅 — 접히는 화자별 목소리 목록(바이블과 같은 패널 문법). */
.script-cast { border: 1px solid var(--hairline); border-radius: var(--r-md); padding: var(--sp-sm) var(--sp-md); background: rgba(255,255,255,0.02); margin-top: var(--sp-sm); }
.script-cast-sum { cursor: pointer; font-size: var(--fs-caption); font-weight: 700; color: var(--ink-80); }
.script-cast-note { font-size: 11px; color: var(--ink-48); margin: var(--sp-xs) 0 var(--sp-sm); }
.script-cast-row { display: flex; align-items: center; gap: var(--sp-sm); margin-bottom: 6px; }
.script-cast-name { flex: 0 0 120px; font-size: var(--fs-caption); color: var(--ink-80); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.script-cast-narr { font-weight: 700; }
.script-cast-select { flex: 1 1 auto; min-width: 0; height: 28px; padding: 0 8px; border: 1px solid var(--hairline); border-radius: var(--r-sm); background: rgba(0,0,0,0.22); color: var(--ink); font-size: var(--fs-caption); }

/* 씬 — 베드와 타임스탬프의 단위. 재정렬이 이 경계를 넘지 않는다. */
.script-scene { display: flex; flex-direction: column; gap: 6px; margin-bottom: var(--sp-lg); }
.script-scene-head { display: flex; align-items: baseline; gap: var(--sp-sm); flex-wrap: wrap; padding-bottom: 4px; border-bottom: 1px solid var(--hairline); }
.script-scene-n { font-size: var(--fs-caption); font-weight: 700; color: var(--ink); }
.script-scene-meta { font-size: 11px; color: var(--ink-48); }
.script-scene-badge { font-size: 10px; padding: 1px 6px; border-radius: 999px; background: rgba(226,179,65,0.16); color: var(--script-warn); }
.script-scene-empty { font-size: 11px; color: var(--ink-48); padding: var(--sp-sm) 0; }
.btn.script-scene-add { align-self: flex-start; }

/* 종류별 색 구분(AC-7) — 왼쪽 띠 하나로 가른다. 대사·해설·독백은 TTS, 효과음·앰비언스는
   sound-generation, 음악은 music 으로 **다른 벤더 엔드포인트**에 간다 — 색이 곧 청구 경로다. */
.script-card { position: relative; display: flex; flex-direction: column; gap: 6px; padding: var(--sp-sm) var(--sp-md); border: 1px solid var(--hairline); border-left: 3px solid var(--ink-48); border-radius: var(--r-sm); background: rgba(255,255,255,0.02); }
.script-card--narr { border-left-color: #7aa2f7; }
.script-card--dial { border-left-color: #9ece6a; }
.script-card--mono { border-left-color: #bb9af7; }
.script-card--amb  { border-left-color: #56b6c2; }
.script-card--sfx  { border-left-color: #e0af68; }
.script-card--music{ border-left-color: #f7768e; }
.script-card--unsynced { background: rgba(226,179,65,0.06); }
.script-card-head { display: flex; align-items: center; gap: var(--sp-xs); flex-wrap: wrap; }
.script-card-kind { font-size: 10px; font-weight: 700; color: var(--ink-80); }
.script-card-id { font-size: 10px; color: var(--ink-48); font-variant-numeric: tabular-nums; }
.script-card-time { font-size: 10px; color: var(--ink-48); font-variant-numeric: tabular-nums; }
.script-card-badge { font-size: 10px; padding: 1px 6px; border-radius: 999px; background: rgba(226,179,65,0.16); color: var(--script-warn); }
.script-card-speaker { width: 120px; height: 24px; padding: 0 6px; border: 1px solid var(--hairline); border-radius: var(--r-xs); background: rgba(0,0,0,0.22); color: var(--ink); font-size: 11px; }
.script-card-direction { height: 24px; padding: 0 6px; border: 1px dashed var(--hairline); border-radius: var(--r-xs); background: transparent; color: var(--ink-80); font-size: 11px; font-style: italic; }
.script-card-text { width: 100%; box-sizing: border-box; min-height: 30px; resize: none; overflow: hidden; padding: 6px 8px; border: 1px solid var(--hairline); border-radius: var(--r-sm); background: rgba(0,0,0,0.22); color: var(--ink); font: inherit; font-size: var(--fs-body); line-height: 1.5; }
.script-card-actions { display: flex; gap: 2px; flex-wrap: wrap; }
.script-add-menu { display: flex; gap: 2px; flex-wrap: wrap; padding: 4px; border: 1px solid var(--hairline); border-radius: var(--r-sm); background: rgba(0,0,0,0.35); }

/* [원문] 탭 — textarea 다(contenteditable 이 아니다). 캐럿·IME·되돌리기를 브라우저 기본 동작으로 얻는다. */
.script-raw { display: flex; flex-direction: column; gap: var(--sp-xs); }
.script-raw-bar { display: flex; align-items: center; gap: var(--sp-sm); }
.script-raw-status { flex: 1 1 auto; font-size: var(--fs-caption); color: var(--ink-48); }
.script-raw-status--ok { color: #9ece6a; }
.script-raw-status--warn { color: var(--script-warn); }
.script-raw-status--err { color: #f7768e; }
.script-raw-text { width: 100%; box-sizing: border-box; min-height: 60vh; resize: vertical; padding: var(--sp-md); border: 1px solid var(--hairline); border-radius: var(--r-md); background: rgba(0,0,0,0.28); color: var(--ink); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; line-height: 1.6; tab-size: 2; }
.script-raw-errors { display: flex; flex-direction: column; gap: 2px; max-height: 240px; overflow-y: auto; }
.script-raw-error { display: flex; gap: var(--sp-sm); align-items: baseline; text-align: left; padding: 4px 8px; border: 0; border-radius: var(--r-xs); background: rgba(247,118,142,0.08); color: var(--ink); font-size: 11px; cursor: pointer; }
.script-raw-error:hover { background: rgba(247,118,142,0.16); }
.script-raw-error-line { flex: 0 0 48px; color: #f7768e; font-variant-numeric: tabular-nums; }
.script-raw-error-hint { color: var(--ink-48); }
.script-raw-error-more { font-size: 11px; color: var(--ink-48); padding: 4px 8px; }

/* ── 잡 진행 화면(WS4) ────────────────────────────────────────────────────────
   승인 후 사용자가 갇히지 않게 하는 화면. 진행률·실패·재시도·타임라인 문. 진행바는 허브 카드의
   .ep-card-prog-bar/-fill 을 그대로 재사용한다(같은 의미의 막대를 두 벌 만들지 않는다). */
.script-job { display: flex; flex-direction: column; gap: var(--sp-sm); max-width: 720px; padding: var(--sp-lg); border: 1px solid var(--hairline); border-radius: var(--r-md); background: rgba(255,255,255,0.02); }
.script-job-label { font-size: var(--fs-caption); color: var(--ink-80); font-variant-numeric: tabular-nums; }

/* 진행률 헤드 — 큰 수가 주인공이고 단위·퍼센트가 그 옆에 앉는다(baseline 정렬). */
.script-job-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-md); flex-wrap: wrap; }
.script-job-count { display: flex; align-items: baseline; gap: var(--sp-xs); flex-wrap: wrap; }
.script-job-num { font-size: var(--fs-display-md); font-weight: 600; letter-spacing: var(--ls-display-md); color: var(--ink); font-variant-numeric: tabular-nums; }
.script-job-unit { font-size: var(--fs-caption); color: var(--ink-48); }
.script-job-pct { font-size: var(--fs-tagline); font-weight: 600; color: var(--accent); font-variant-numeric: tabular-nums; }
.script-job-failed { font-size: 11px; color: #f7768e; border: 1px solid currentColor; border-radius: 999px; padding: 1px 8px; }
.script-job-timing { font-size: var(--fs-fine); color: var(--ink-48); font-variant-numeric: tabular-nums; }

/* 인디터미네이트 스윕 — 첫 정산 전까지 막대가 0% 에 붙어 있어 화면이 정지 화면과 구별되지 않던 구간을 메운다.
   부모(.ep-card-prog-bar)가 overflow:hidden 이라 밖으로 나간 부분은 잘린다.
   ⚠️ 확정 채움과 **다르게 생겨야 한다** — 같은 단색이면 정지한 순간 '38% 진행'으로 읽힌다(실측). 양끝이
   투명한 그라데이션이라 "여기까지 왔다"는 경계선이 없다. */
.ep-card-prog-fill--live { width: 38%; background: linear-gradient(90deg, transparent, var(--accent), transparent); animation: script-job-sweep 1.5s ease-in-out infinite; }
@keyframes script-job-sweep { from { transform: translateX(-100%); } to { transform: translateX(365%); } }

.script-job-note { font-size: 11px; color: var(--ink-48); }
.script-job-note--warn { color: var(--script-warn); }
.script-job-actions { display: flex; gap: var(--sp-sm); flex-wrap: wrap; margin-top: var(--sp-xs); }
/* 그리드 — flex-wrap 은 마지막 줄이 들쭉날쭉해 42개가 흩어진 점처럼 보였다. 열을 맞추면 '남은 양'이 눈에 잡힌다. */
.script-job-blocks { display: grid; grid-template-columns: repeat(auto-fill, minmax(44px, 1fr)); gap: 4px; max-width: 720px; }
.script-job-chip { display: flex; align-items: center; justify-content: center; min-height: 22px; font-size: 10px; padding: 2px 6px; border: 1px solid var(--hairline); border-radius: 999px; color: var(--ink-48); font-variant-numeric: tabular-nums; }
.script-job-chip--running { border-color: #7aa2f7; background: rgba(122,162,247,0.12); color: #7aa2f7; animation: script-job-pulse 1.2s ease-in-out infinite; }
@keyframes script-job-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }
.script-job-chip--done { border-color: transparent; background: rgba(158,206,106,0.16); color: #9ece6a; }
.script-job-chip--failed { border-color: #f7768e; background: rgba(247,118,142,0.10); color: #f7768e; }
.script-job-more { font-size: 10px; color: var(--ink-48); align-self: center; }

/* 움직임을 줄여 달라고 한 사용자에게는 애니메이션 대신 정적 표현을 준다 — 정보는 그대로 남긴다. */
@media (prefers-reduced-motion: reduce) {
  .ep-card-prog-fill--live { width: 100%; opacity: 0.28; animation: none; }
  .script-job-chip--running { animation: none; }
}

/* 생성 중 편집 경고 — 배지 하나로 대가를 말한다(B-5). 편집을 막지는 않는다. */
.script-gen-warn { font-size: 11.5px; color: var(--warn, #d8a657); border: 1px solid currentColor;
  border-radius: 999px; padding: 2px 9px; white-space: nowrap; opacity: .9; }
