/* SORI AI v2 디자인 시스템 — 단일 진실 소스(single source of truth).
 *
 * 색상/타이포/간격/radius를 전부 여기 변수로만 정의한다. 다른 CSS나 템플릿에 hex 값을
 * 직접 하드코딩하지 않는다 — 톤을 바꿀 일이 생기면 이 파일의 변수 값만 고치면 전체 화면에
 * 반영되어야 한다(검증 항목: --color-navy-900을 다른 색으로 바꿔서 실제로 사이드바 전체
 * 색이 바뀌는지 확인하는 것이 이 파일의 존재 이유).
 *
 * 값 자체는 지시서에서 시안으로 이미 확인받은 확정값이다. 실제 적용 중 대비/가독성 문제로
 * 미세조정이 필요하면 여기 값만 바꾸고 사유를 주석으로 남긴다 — 절대 개별 템플릿에
 * style="color:#..." 같은 식으로 덮어쓰지 않는다. */

:root {
  /* ── 색상 - 곤색(navy) ── */
  --color-navy-900: #16223D;   /* 사이드바 배경, 콘텐츠 영역 최상단 액센트 바 */
  --color-navy-700: #1E2A4A;   /* 버튼/헤더 텍스트, btn-primary 배경 */
  --color-navy-500: #3F5075;
  --color-navy-300: #A9B4CC;   /* 사이드바 비활성 아이콘/텍스트 */
  --color-navy-100: #C7CEE0;   /* 사이드바 비활성 라벨 */

  /* ── 색상 - 퍼플(accent) ── */
  --color-purple-700: #5B3FA0;
  --color-purple-600: #6D5BD0;  /* 액티브 상태, 버튼 액센트 */
  --color-purple-400: #8C7AD6;
  --color-purple-200: #AFA2E3;
  --color-purple-100: #EFEAFB;  /* 배지/카드 틴트 배경 */
  --color-purple-tint-bg: #F1EFFA; /* 헤더/탭바 영역 배경 */
  --color-purple-900: #3C2A6B;  /* 틴트 배경 위 텍스트 */

  /* ── 표면 ── */
  --surface-page: #F7F8FA;
  --surface-card: #FFFFFF;
  --surface-muted: #F1F2F5;

  /* ── 텍스트 ── */
  /* 순검정에 가까웠던 #1A1D29(대비율 16.78:1, 흰 배경 기준)를 같은 남색 계열 톤을
     유지하면서 살짝 밝힌 소프트 다크그레이로 교체(관리자 지시서 2026-07-23) — 대비율은
     14.51:1로 WCAG AAA(7:1) 기준을 여유 있게 넘겨 가독성 저하 없이 톤만 부드러워진다.
     이 변수 하나만 바뀌면 전체 화면 본문 텍스트에 일괄 반영된다(파일 상단 설계 원칙 참고).*/
  --text-primary: #262933;
  --text-secondary: #5B5F6E;
  --text-muted: #9296A3;

  /* ── 보더 ── */
  --border-default: #E4E5EA;
  --border-strong: #D0D2DA;

  /* ── 상태색(성공/실패/경고) — 디자인 시스템 밖 색이지만 배지/플래시 등에서 재사용되므로
     여기 같이 변수화한다. 채도를 낮춰 navy/purple 톤과 부딪히지 않게 맞췄다. ── */
  --color-success-bg: #E7F6EC;
  --color-success-text: #1F8A4C;
  --color-danger-bg: #FBEAEA;
  --color-danger-text: #C23B3B;
  --color-warning-bg: #FCF3DF;
  --color-warning-text: #97650F;

  /* ── 타이포그래피 ── */
  /* 전체적으로 글자가 작다는 피드백으로 기존 값의 1.5배로 상향(사용자 확정) — 이 6개
     변수만 고치면 전체 화면 톤이 그대로 따라가는 게 이 파일의 존재 이유(§ 상단 주석 참고).
     2026-08-18(윈도우 렌더링 차이 수정) — 예전엔 "Pretendard"가 실제로 로드하는
     @font-face/CDN 없이 "설치돼 있으면 쓴다"는 이름표만 있어서, 이 폰트가 없는 윈도우에서는
     자간·행높이가 다른 대체 폰트(맑은 고딕 등)로 조용히 떨어지며 레이아웃이 달라졌다(맥은
     -apple-system이 San Francisco로 정상 해석돼 증상이 안 보였을 뿐) — 이제 아래
     @font-face로 셀프 호스팅해 OS 무관하게 항상 같은 폰트가 로드된다. */
  --font-sans: "Pretendard Variable", "Pretendard", system-ui, "Malgun Gothic", sans-serif;
  --fs-caption: 18px;
  --fs-body-sm: 19.5px;
  --fs-body: 21px;
  --fs-subtitle: 24px;
  --fs-title: 30px;
  --fs-stat: 36px;
  --fw-regular: 400;
  --fw-medium: 500;
  --fw-bold: 700;

  /* ── 간격/radius ── */
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 12px;
  --space-lg: 16px;
  --space-xl: 24px;
  --radius-control: 8px;
  --radius-card: 12px;

  /* ── 레이아웃 상수 — 사이드바/헤더 크기도 매직넘버로 흩어지지 않게 변수화 ── */
  /* 176px는 폰트 확대 전 값 — "프로젝트 목록"/"프리셋 관리"처럼 6~7글자 메뉴 라벨이
     --fs-body-sm(19.5px)에서 아이콘+패딩을 뺀 가용폭(약 112px)을 넘어서면서 줄바꿈되는
     문제가 있어 208px로 넓혔다(계산: 라벨 최대폭 ~123px + 아이콘/여백/패딩 ~64px). */
  --sidebar-width: 208px;
  --accent-bar-height: 4px;
}

/* 가변 폰트(wght 45~930) 하나로 --fw-*(400/500/700)뿐 아니라 화면 곳곳의 인라인
   font-weight(600/800 등)까지 전부 커버한다 — 정적 굵기별 파일 여러 개를 두는 대신
   이 파일 하나만 자가 호스팅(app/static/fonts/, SIL OFL 1.1 라이선스,
   PRETENDARD_LICENSE.txt 동봉). 가변폰트는 Chrome 62+에서 지원돼 §최소 지원 기준
   (Chrome 100)에 안전하게 들어온다.
   2026-08-18(F-1, 폰트 서브셋) — 원본(2.0MB, 전체 유니코드 커버)을 한글/라틴 두 파일로
   쪼갰다: PretendardVariable.hangul.woff2(KS X 1001 완성형 2,350자, Unicode.org 공식
   매핑표(KSX1001.TXT)로 정확히 추출 — 임의 추정 아님, ~395KB) +
   PretendardVariable.latin.woff2(ASCII + 화면에서 실제 쓰는 기호 21종, 전수조사로 확인,
   ~42KB). `unicode-range`가 같은 font-family 두 @font-face를 나눠 브라우저가 실제로
   렌더링할 문자에 필요한 파일만 받게 한다 — 이 앱은 거의 모든 화면이 한글+영문숫자를
   섞어 쓰므로 실사용 시 사실상 둘 다 받게 되지만(합계 ~437KB), 순수 영문 페이지(로그인
   화면 등)에서는 라틴 파일만(~42KB) 받는 이득이 있다. 한글 unicode-range는 KS X 1001
   완성형 밖의 나머지 현대 한글 음절(U+AC00-D7A3 전체 중 8,822자)까지 넓게 잡아뒀다 —
   이 파일에 없는 음절이 요청되면 브라우저가 자동으로 다음 폰트(system-ui/맑은 고딕)로
   그 글자만 대체한다(표준 동작, 깨진 문자 아님) — 그래서 범위를 넓게 잡아도 안전하다.
   목표(400KB 이하)는 한글 파일 단독 기준 근소하게 초과(~395KB, gvar 가변폰트 데이터가
   가장 큰 비중) — 정직하게 보고, 억지로 더 줄이려 굵기 축을 자르면 인라인
   font-weight(600/800 등) 매칭이 깨지므로 이 선에서 유지. */
@font-face {
  font-family: 'Pretendard Variable';
  src: url('../fonts/PretendardVariable.hangul.woff2') format('woff2-variations');
  font-weight: 45 930;
  font-style: normal;
  font-display: swap;
  unicode-range: U+AC00-D7A3;
}
@font-face {
  font-family: 'Pretendard Variable';
  src: url('../fonts/PretendardVariable.latin.woff2') format('woff2-variations');
  font-weight: 45 930;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+2010-2027, U+2190-2193, U+221D, U+2460-2473,
    U+2500-25FF, U+2700-27BF;
}

/* ── 리셋 ── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* 윈도우는 기본적으로 세로 스크롤바가 콘텐츠 폭을 차지해(오버레이 스크롤바가 기본인
   맥과 달리) 같은 화면이 두 OS에서 폭이 달라 보이는 원인이 된다 — 스크롤바 유무와
   무관하게 항상 자리를 예약해 폭 차이를 없앤다(2026-08-18 윈도우 렌더링 차이 수정 E). */
html { scrollbar-gutter: stable; }

body {
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  color: var(--text-primary);
  background: var(--surface-page);
}

/* 네이티브 <select> OS별 외형 통일(2026-08-18 윈도우 렌더링 차이 수정 C) — 커스텀
   드롭다운(그룹관리의 포털 컴포넌트 등)으로 당장 교체하기 부담스러운 화면이 아직 많아
   (제품군/옵션/카테고리 등 단순 단일선택 셀렉트 20여 곳), 최소 기준으로 appearance:none +
   공통 화살표 아이콘 + 높이/폰트/패딩 명시를 전 화면에 한 번에 적용한다(개별 화면마다
   따로 처리하지 않음, §0.4-1). data:URI 화살표라 외부 요청 없음. */
select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%235B5F6E' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 11px 7px;
  padding: 6px 30px 6px var(--space-md);
  height: 36px;
  font-size: var(--fs-body-sm);
  font-family: var(--font-sans);
  color: var(--text-primary);
  background-color: var(--surface-card);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  line-height: 1.3;
  cursor: pointer;
}
select::-ms-expand { display: none; }

a { color: inherit; }

/* ── 앱 셸: 사이드바 + 콘텐츠 ── */
.app-shell {
  display: flex;
  min-height: 100vh;
}

.sidebar {
  width: var(--sidebar-width);
  flex-shrink: 0;
  background: var(--color-navy-900);
  color: #fff;
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}

.sidebar-brand {
  padding: var(--space-lg) var(--space-md);
  font-weight: var(--fw-bold);
  font-size: var(--fs-subtitle);
  color: #fff;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  text-decoration: none;
  cursor: pointer;
}
.sidebar-brand:hover { opacity: 0.85; }
.sidebar-brand em { font-style: normal; color: var(--color-purple-400); }

.sidebar-section-label {
  color: var(--color-navy-300);
  font-size: var(--fs-caption);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: var(--space-md) var(--space-md) var(--space-xs);
}

.sidebar-nav { display: flex; flex-direction: column; gap: 2px; padding: 0 var(--space-sm); }
.sidebar-nav a {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-control);
  color: var(--color-navy-100);
  text-decoration: none;
  font-size: var(--fs-body-sm);
  transition: background 0.12s, color 0.12s;
}
.sidebar-nav a:hover { background: var(--color-navy-500); color: #fff; }
.sidebar-nav a.active { background: var(--color-purple-600); color: #fff; font-weight: var(--fw-medium); }
.sidebar-nav a i { width: 16px; text-align: center; color: var(--color-navy-300); }
.sidebar-nav a.active i, .sidebar-nav a:hover i { color: #fff; }

.sidebar-spacer { flex: 1; }

.sidebar-user {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-md);
  border-top: 1px solid rgba(255,255,255,0.08);
}
/* 아바타+이름+역할 클릭 시 "내 정보 수정"(auth.my_profile)으로 이동 — 로그아웃 아이콘은
   별도 링크로 남겨 오클릭을 방지한다. flex:1/min-width:0은 sidebar-user-name의
   text-overflow:ellipsis가 계속 동작하게 하기 위함(이전엔 이 역할을 감싸던 순수 div가 했음). */
.sidebar-user-link {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  flex: 1;
  min-width: 0;
  padding: 4px;
  margin: -4px;
  border-radius: var(--radius-control);
  text-decoration: none;
  transition: background 0.12s;
}
.sidebar-user-link:hover { background: rgba(255,255,255,0.08); }
.sidebar-user-avatar {
  /* fs-caption(18px) 이니셜이 28px 원 안에서 답답해 보여 34px로 같이 키움 — 대략 글자
     크기의 2배 지름을 유지하는 비율(가독성 기준). */
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--color-purple-600);
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  flex-shrink: 0;
}
.sidebar-user-name { font-size: var(--fs-body-sm); color: #fff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* --fs-* 변수는 아니지만(디자인 시스템 파일 안의 컴포넌트 상수), 옆 라벨들이 전부 1.5배
   커진 채로 11px만 그대로 두면 배지/역할표시가 눈에 띄게 작아 보여 같은 비율로 올림. */
.sidebar-user-role { font-size: 16px; color: var(--color-navy-300); }
.sidebar-user-logout {
  margin-left: auto; color: var(--color-navy-300); text-decoration: none; font-size: var(--fs-body-sm);
  flex-shrink: 0;
}
.sidebar-user-logout:hover { color: #fff; }
.sidebar-build-id {
  padding: 2px var(--space-md) var(--space-sm);
  font-size: 11px;
  line-height: 1.4;
  color: var(--color-navy-500);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── 콘텐츠 영역 ── */
.app-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background: var(--surface-page);
}

.content-accent-bar {
  height: var(--accent-bar-height);
  background: var(--color-navy-900);
  flex-shrink: 0;
}

/* .project-header는 .app-main(flex column)의 직계 flex 아이템이라, 배경은 전체 폭에 칠하되
   내용물의 좌우 정렬은 content-body와 맞춰야 한다(2026-07-24 버그 리포트: 넓은 화면에서
   브레드크럼이 그 아래 배너/탭바/본문보다 왼쪽으로 치우쳐 보임). .hero-banner-wrap/
   .hero-banner-inner와 동일한 이유로 max-width+margin:auto를 이 flex 아이템 자체에 걸지
   않고(크로스브라우저 이슈, 위 hero-banner-wrap 주석 참고) 그 안의 일반 블록 자식
   (.project-header-inner)으로 내려서 적용한다. */
.project-header {
  background: var(--color-purple-tint-bg);
  padding-top: var(--space-lg);
}
.project-header-inner {
  max-width: 1800px;
  margin: 0 auto;
  padding: 0 var(--space-xl);
}
.project-header-top {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin-bottom: var(--space-md);
}
.project-header-title {
  font-size: var(--fs-title);
  font-weight: var(--fw-bold);
  color: var(--color-purple-900);
}
/* 화살표+프로젝트명을 하나의 링크 영역으로 묶는다(2026-07-24 버그 리포트: 화살표만
   클릭되던 것을 텍스트까지 확장) — title의 색상은 자체 지정값(purple-900)을 유지하고,
   back 자체 텍스트/아이콘만 호버 시 purple-700으로 바뀐다. */
.project-header-back {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  color: var(--text-secondary);
  text-decoration: none;
  font-size: var(--fs-body-sm);
}
.project-header-back:hover { color: var(--color-purple-700); }

/* 탭바 — 세그먼트형 알약(pill) 스타일로 리뉴얼(관리자 지시서 2026-07-23, "더 세련되게").
   가운데 정렬 + 활성 탭은 포인트컬러로 꽉 채운 알약, 비활성은 투명 배경에 호버 시만 은은한
   틴트 — 예전의 "흰 상자 + 밑줄" 방식보다 접힌 세그먼트 컨트롤에 가까운 느낌을 준다. */
.tab-bar {
  display: flex;
  justify-content: center;
  gap: var(--space-xs);
  padding-bottom: var(--space-md);
  border-bottom: 1px solid var(--border-default);
  overflow-x: auto;
}
.tab-bar a {
  padding: var(--space-sm) var(--space-lg);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
  text-decoration: none;
  border-radius: 999px;
  white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}
.tab-bar a:hover { background: rgba(109, 91, 208, 0.1); color: var(--color-purple-700); }
.tab-bar a.active {
  background: var(--color-purple-600);
  color: #fff;
  box-shadow: 0 2px 10px rgba(109, 91, 208, 0.35);
}
.tab-bar a.active:hover { background: var(--color-purple-600); }
/* 메시지 매트릭스 채널 탭(2026-08-18)처럼 네비게이션이 아니라 JS로 우측만 갱신하는
   탭은 <a>가 아니라 <button>을 쓴다 — 동일한 시각 규칙을 그대로 적용. */
.tab-bar button.tab-bar-btn {
  padding: var(--space-sm) var(--space-lg);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
  background: transparent;
  border: none;
  cursor: pointer;
  border-radius: 999px;
  white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}
.tab-bar button.tab-bar-btn:hover { background: rgba(109, 91, 208, 0.1); color: var(--color-purple-700); }
.tab-bar button.tab-bar-btn.active {
  background: var(--color-purple-600);
  color: #fff;
  box-shadow: 0 2px 10px rgba(109, 91, 208, 0.35);
}

.content-body {
  padding: var(--space-xl);
  flex: 1;
  width: 100%;
  /* 표/카드가 많은 데이터 화면(전체현황 등)이 모니터 폭을 최대한 쓰도록 유동폭으로 두되,
     초대형 모니터에서 표 행이 과도하게 늘어지지 않게 넉넉한 상한만 둔다. */
  max-width: 1800px;
  margin: 0 auto;
}

/* 안내문/폼 위주 화면(표 없음)은 content-body가 넓어져도 줄 길이가 늘어지면 가독성이
   떨어지므로, base.html의 content_class 블록으로 옵트인해 별도로 좁힌다. */
.content-body.content-narrow {
  max-width: 760px;
}

/* 프로젝트 컨텍스트가 없는(목록성) 페이지용 축약 헤더 — 탭바 없이 accent bar 바로 아래
   콘텐츠가 시작되고, 각 페이지 자체의 <h2>가 헤더 역할을 겸한다. */
.page-header-compact {
  padding: var(--space-lg) var(--space-xl) 0;
}

/* ── 시그니처 요소 ─────────────────────────────────────────────────────────
   1) 액센트 세로바: 카드/헤더/테이블 등 콘텐츠 블록 왼쪽 끝에 반복 배치하는 5px 퍼플 바.
      ::before로 그려서 마크업 변경 없이 기존 .card/.project-header/.table-clean 등에
      클래스만 얹으면 된다. border-radius는 부모의 --radius-card를 그대로 물려받아 각진
      블록(헤더 등, radius 0)에도, 둥근 카드에도 자연스럽게 맞는다.
   2) 통계 카드(.stat-card): 좌상단 삼각형 탭 + 굵은 포인트컬러 숫자로 "숫자를 보여주는
      카드"(전체현황 통계, 요약 화면 지표 등)를 다른 카드와 구분되게 강조한다. */
.accent-bar { position: relative; }
/* .card 기본 padding(16px)에 바 두께(5px)만큼 왼쪽 여유를 더 줘서 본문 텍스트가 바에 바짝
   붙지 않게 한다 — 헤더/테이블 래퍼처럼 padding 구조가 다른 곳까지 일괄 적용하면 예상 못한
   자간이 생길 수 있어 .card 조합에만 한정한다. */
.card.accent-bar { padding-left: calc(var(--space-lg) + 5px); }
.accent-bar::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  width: 5px;
  height: 100%;
  background: var(--color-purple-600);
  border-radius: var(--radius-card) 0 0 var(--radius-card);
}

/* 대시보드 히어로 배너 — app/templates/_hero_banner.html이 쓰는 마크업 전용 스타일.
   base.html이 인증된 모든 화면 상단에 공통 include하므로(관리자 지시서 2026-07-23)
   랜딩 화면 전용일 때보다 더 압축된 2줄 레이아웃을 쓴다 — 1줄: SORI AI 라벨,
   2줄: 태그라인(왼쪽) + 날짜·인사말·프로젝트 수(오른쪽), .hero-banner-row가 그 2줄째를
   좌우로 나눈다. 로그인 페이지의 그라디언트(navy → purple)를 그대로 가져와 로그인 →
   대시보드 전환이 시각적으로 이어지게 한다. 하단 파형은 SORI(소리) 브랜드를 은은하게
   암시하는 장식용 오디오 웨이브폼 — 정보 전달용이 아니므로 aria-hidden. */
/* content-body와 동일한 가로 padding/max-width를 써서, 배너가 content-body 밖(탭바보다
   위)에 있어도 안에 있을 때와 좌우가 정확히 맞는다 — base.html 참고.

   2026-07-23 두 번째 사고: width:100%+max-width+margin:auto를 이 wrap 자체(app-main의
   column flex 아이템)에 직접 걸었더니, 이번엔 맥/윈도우(정확히는 브라우저 엔진별)로
   가로 폭 계산이 갈렸다 — "flex 아이템에 걸린 auto 마진이 cross-axis stretch를
   무효화하는지"를 width:100%가 얼마나 확실히 덮어쓰는지가 엔진마다 실측이 달라(스펙이
   이 조합에서 세세하게 완전히 못박혀 있지 않음), 크로스브라우저로 신뢰할 수 없는
   조합이라는 결론. 그래서 이번엔 아예 flex 아이템(.hero-banner-wrap) 자체에는 가로
   사이징 규칙을 하나도 안 두고(세로 padding만 남김 — 이건 flex 축과 무관해 안전),
   max-width+margin:auto는 그 밑의 평범한 블록 자식(.hero-banner-inner, flex 아이템이
   아님)으로 내려서 적용한다. "max-width + margin:0 auto인 일반 블록 요소 가운데 정렬"은
   CSS에서 가장 오래되고 모든 브라우저가 완전히 동일하게 구현하는 패턴이라 이 모호함
   자체가 원천적으로 사라진다. */
.hero-banner-wrap {
  padding-top: var(--space-lg);
}
.hero-banner-inner {
  max-width: 1800px;
  margin: 0 auto;
  padding: 0 var(--space-xl);
}
.hero-banner {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: linear-gradient(135deg, var(--color-navy-900), var(--color-purple-600));
  color: #fff;
  padding: var(--space-xl);
  margin-bottom: var(--space-lg);
}
.hero-banner-content { position: relative; z-index: 1; }
.hero-banner-eyebrow {
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  letter-spacing: 0.08em;
  color: var(--color-purple-200);
  margin-bottom: var(--space-xs);
}
.hero-banner-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-md);
}
.hero-banner-tagline {
  font-size: var(--fs-title);
  font-weight: var(--fw-bold);
}
.hero-banner-meta {
  font-size: var(--fs-body-sm);
  color: var(--color-navy-100);
  display: flex;
  gap: var(--space-sm);
  flex-wrap: wrap;
  align-items: center;
  white-space: nowrap;
}
.hero-banner-meta strong { color: #fff; }
.hero-banner-dot { opacity: 0.5; }
.hero-banner-wave {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: 34px;
  opacity: 0.16;
}
.hero-banner-wave rect { fill: #fff; }

.stat-card {
  position: relative;
  overflow: hidden;
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-card);
  padding: var(--space-lg);
}
.stat-card::before {
  /* 좌상단 삼각형 액센트 탭 — border trick(투명 변 2개 + 채워진 변 1개) */
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  border-style: solid;
  border-width: 20px 20px 0 0;
  border-color: var(--color-purple-600) transparent transparent transparent;
}
.stat-card .stat-card-label {
  font-size: var(--fs-caption);
  color: var(--text-secondary);
  margin-bottom: var(--space-xs);
}
.stat-card .stat-card-value {
  /* 24~26px 지정 범위 안의 기존 변수(--fs-subtitle=24px)를 재사용 — 새 매직넘버 안 만듦 */
  font-size: var(--fs-subtitle);
  font-weight: var(--fw-bold);
  color: var(--color-purple-600);
  line-height: 1.3;
}

/* ── 공통 컴포넌트 ── */
.card {
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-card);
  padding: var(--space-lg);
  margin-bottom: var(--space-lg);
}

.metric-card {
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-card);
  padding: var(--space-lg);
}
.metric-card .metric-label {
  font-size: var(--fs-caption);
  color: var(--text-secondary);
  margin-bottom: var(--space-xs);
}
.metric-card .metric-value {
  font-size: var(--fs-stat);
  font-weight: var(--fw-medium);
  color: var(--text-primary);
}

h2 { font-size: var(--fs-title); font-weight: var(--fw-bold); margin-bottom: var(--space-lg); color: var(--text-primary); }
h3 { font-size: var(--fs-subtitle); font-weight: var(--fw-medium); margin: var(--space-lg) 0 var(--space-sm); color: var(--text-primary); }

/* min-width(100%가 아니라 min-width)로 둬서 컬럼이 많은 표는 필요한 만큼 더 넓어질 수
   있게 하고, 그 초과분은 부모 .card의 overflow-x: auto로 스크롤 처리한다 — width:100%로
   고정하면 좁은 창에서 셀 텍스트가 강제로 줄바꿈되며 눌리는 문제(완전삭제 버튼 등이 세로로
   꺾이는 현상)가 있었다. */
table { min-width: 100%; border-collapse: collapse; font-size: var(--fs-body-sm); }
th, td { text-align: left; padding: var(--space-sm) var(--space-md); border-bottom: 1px solid var(--border-default); }
th { color: var(--text-secondary); font-weight: var(--fw-medium); font-size: var(--fs-caption); white-space: nowrap; }

/* 표가 카드 폭보다 넓어지면(컬럼 많은 표를 좁은 창에서 볼 때) 셀 텍스트를 짓누르는 대신
   가로 스크롤을 띄운다. 2026-08-18(윈도우 렌더링 차이 수정 A/B) — 원래
   `.card:has(table){overflow-x:auto}`였는데, :has()는 Chrome 105 미만에서 셀렉터
   자체가 무효 처리되며 이 규칙 하나만 조용히 무시되는 게 아니라(브라우저별 파싱 차이로
   인한 렌더링 불일치의 원인 클래스) — 클래스 기반으로 교체하고, 그 클래스는 base.html의
   전역 스크립트(app/static/js/compat.js)가 `.card` 안에 `<table>`이 있으면 페이지 로드
   시 자동으로 붙인다(화면마다 따로 처리 안 함). */
.card.has-table-scroll { overflow-x: auto; }

.table-clean { border: 1px solid var(--border-default); border-radius: var(--radius-card); overflow: hidden; }
.table-clean table { margin: 0; }
.table-clean th { background: var(--surface-muted); }
.table-clean tr:last-child td { border-bottom: none; }

.btn, .btn-primary, .btn-secondary {
  display: inline-block;
  border: none;
  border-radius: var(--radius-control);
  padding: 7px var(--space-md);
  font-size: var(--fs-body-sm);
  font-family: var(--font-sans);
  cursor: pointer;
  text-decoration: none;
  transition: opacity 0.12s, background 0.12s;
  /* 한글은 글자 사이 어디서나 줄바꿈이 허용되는 언어라, 이게 없으면 좁은 표 컬럼 안에서
     "완전삭제" 같은 버튼 라벨이 한 글자씩 세로로 쌓여 보이는 문제가 있었다. */
  white-space: nowrap;
}
.btn, .btn-primary { background: var(--color-navy-700); color: #fff; }
.btn:hover, .btn-primary:hover { opacity: 0.9; }
.btn.secondary, .btn-secondary { background: transparent; color: var(--text-secondary); border: 1px solid var(--border-strong); }
.btn.secondary:hover, .btn-secondary:hover { background: var(--surface-muted); }
.btn.danger { background: var(--color-danger-text); color: #fff; }
.btn.accent { background: var(--color-purple-600); color: #fff; }

.impersonation-banner {
  position: sticky;
  top: 0;
  z-index: 500;
  background: var(--color-warning-bg);
  color: var(--color-warning-text);
  padding: var(--space-sm) var(--space-lg);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-md);
  font-size: var(--fs-body-sm);
  border-bottom: 1px solid var(--color-warning-text);
}
.impersonation-banner .btn.secondary { padding: 5px 10px; border-color: var(--color-warning-text); color: var(--color-warning-text); }
.impersonation-banner .btn.secondary:hover { background: rgba(151, 101, 15, 0.1); }

.sentiment-btn.active { background: var(--color-purple-600); color: #fff; border-color: var(--color-purple-600); }

input, select, textarea {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  padding: 6px var(--space-md);
  font-size: var(--fs-body-sm);
  font-family: var(--font-sans);
  color: var(--text-primary);
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--color-purple-600);
  box-shadow: 0 0 0 3px var(--color-purple-100);
}

/* 체크박스는 브라우저 기본 크기(~13px)가 클릭하기 작다는 피드백으로 전체 화면 공통
   1.5배 확대(관리자 지시서 2026-07-22) — 여기 한 곳만 고치면 유저 목록/전체현황 체크박스부터
   즉시모듈 필터, 로그인 화면까지 전부 반영된다. transform:scale() 대신 width/height를 직접
   지정한 이유: transform은 페인트 단계에서만 확대돼 실제 레이아웃 박스 크기가 안 늘어나서
   플렉스/표 안 옆 요소와 겹치는 문제가 있다.
   (login.html의 .remember-label 안 체크박스도 이 규칙을 그대로 타도록 자체 width/height는
   제거해뒀다 — 페이지마다 따로 크기를 안 둬야 여기 값 하나로 전체가 같이 바뀐다.) */
input[type="checkbox"] {
  width: 20px;
  height: 20px;
  cursor: pointer;
}

label { font-size: var(--fs-caption); color: var(--text-secondary); display: block; margin-bottom: var(--space-xs); }
.form-row { margin-bottom: var(--space-md); }

.badge {
  display: inline-block;
  padding: 2px var(--space-sm);
  border-radius: 999px;
  /* sidebar-user-role과 같은 이유로 11px→16px(1.5배 비율 유지) — 배지는 status/타입
     표시로 화면 전체에 반복 노출되므로 본문 글자만 커지고 이것만 작게 남으면 불균형함. */
  font-size: 16px;
  background: var(--surface-muted);
  color: var(--text-secondary);
  white-space: nowrap;
}
.badge-accent, .badge.badge-accent {
  background: var(--color-purple-100);
  color: var(--color-purple-900);
}
.badge.status-success { background: var(--color-success-bg); color: var(--color-success-text); }
.badge.status-failed { background: var(--color-danger-bg); color: var(--color-danger-text); }
.badge.status-running, .badge.status-pending, .badge.status-waiting_external { background: var(--color-warning-bg); color: var(--color-warning-text); }
/* 2026-07-24 대용량 배치 처리 안정화 지시서 2번 — 관리자 처리 현황판의 일시정지 상태.
   running/pending/waiting_external과 같은 warning 팔레트를 재사용하고 텍스트 라벨로 구분한다. */
.badge.status-paused { background: var(--color-warning-bg); color: var(--color-warning-text); }
/* A-2(2026-08-16) — 오류 건수 배지는 0건이어도 항상 보이되, 1건이라도 있으면 색과
   크기로 즉시 눈에 띄어야 한다(juno 지시) — 기본 배지보다 한 단계 큰 폰트/패딩만
   더한다(새 색 없음, 기존 status-failed 팔레트 그대로). */
.badge.badge-emphasis { font-size: var(--fs-body); font-weight: var(--fw-bold); padding: 4px 12px; }

/* AI 사용 단계 배지(2026-08-18, VOC 잡 실행 화면 6탭 재구성 — juno 지시 "AI 사용 단계에
   배지, 크고 명확하게") — badge-accent(보라)+badge-emphasis(굵은 폰트/패딩)를 조합만 한
   범용 클래스. 새 색 없음. 리뷰 화면(AI 분석 탭 등)에도 그대로 적용 가능하도록 .badge에
   종속시키지 않고 독립 클래스로 둔다 — 아이콘+텍스트를 함께 넣는 용도라 .badge의
   white-space:nowrap/inline-block 정도만 공유하면 충분. */
.badge-ai {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border-radius: 999px;
  background: var(--color-purple-100);
  color: var(--color-purple-900);
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  white-space: nowrap;
}

/* ── 세그먼트 필터 바(C-3, 2026-08-15) ──────────────────────────────────────────
   구매트리거/키워드/연관맵/적응곡선/페르소나 상세 5개 화면이 공유하는 필터 바
   (app/templates/insights/_segment_filter_bar.html). 여러 값을 동시에 골라야 해서
   <select multiple>(리스트박스) 대신 "버튼 + 펼침 체크박스 목록" 패턴을 쓴다 — 닫힌
   상태는 pill 한 줄, 열면 카드 형태 목록. 색은 전부 기존 보라 토큰 재사용(badge-accent와
   같은 틴트 팔레트) — 새 색 임의 추가 안 함. */
.segment-filter-bar { padding: var(--space-md) var(--space-lg); }
.segment-filter-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-md);
}
.segment-filter-dropdowns { display: flex; flex-wrap: wrap; gap: var(--space-sm); }

.seg-dropdown { position: relative; }
.seg-dropdown-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px var(--space-md);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--surface-card);
  color: var(--text-primary);
  font-size: var(--fs-body-sm);
  font-family: var(--font-sans);
  cursor: pointer;
  white-space: nowrap;
}
.seg-dropdown-btn:hover { background: var(--surface-muted); }
.seg-dropdown-btn:focus-visible {
  outline: none;
  border-color: var(--color-purple-600);
  box-shadow: 0 0 0 3px var(--color-purple-100);
}
.seg-dropdown-label { color: var(--text-secondary); }
.seg-dropdown-caret { color: var(--text-muted); font-size: 12px; }
/* 값이 선택된 드롭다운은 보라 틴트로 강조 — 목록을 안 열어도 "조건이 걸려 있다"가
   한눈에 보이게(badge-accent와 동일 팔레트: 틴트 배경 + 진한 보라 텍스트). */
.seg-dropdown.active .seg-dropdown-btn {
  background: var(--color-purple-100);
  border-color: var(--color-purple-200);
  color: var(--color-purple-900);
}
.seg-dropdown.active .seg-dropdown-label,
.seg-dropdown.active .seg-dropdown-caret { color: var(--color-purple-700); }

.seg-dropdown-panel {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 40;
  min-width: 200px;
  max-height: 280px;
  overflow-y: auto;
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-card);
  box-shadow: 0 8px 24px rgba(38, 41, 51, 0.14);
  padding: var(--space-sm);
}
.seg-dropdown-panel[hidden] { display: none; }
.seg-option {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-control);
  font-size: var(--fs-body-sm);
  color: var(--text-primary);
  cursor: pointer;
  margin-bottom: 0;
}
.seg-option:hover { background: var(--surface-muted); }
.seg-option input[type="checkbox"] { margin: 0; flex-shrink: 0; }

.segment-filter-summary {
  flex: 1 1 220px;
  min-width: 180px;
  color: var(--text-secondary);
}
.segment-filter-summary:empty { display: none; }
.segment-filter-summary .seg-summary-condition {
  font-size: var(--fs-caption);
  color: var(--text-muted);
  margin-bottom: 2px;
}
.segment-filter-summary .seg-summary-count {
  font-size: var(--fs-subtitle);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.segment-filter-summary .seg-summary-pct {
  font-size: var(--fs-caption);
  color: var(--text-muted);
  font-weight: var(--fw-regular);
  margin-left: 4px;
}

.segment-filter-reset { margin-left: auto; }
.segment-filter-reset[disabled] { opacity: 0.4; cursor: default; pointer-events: none; }

.flash {
  background: var(--color-danger-bg);
  border: 1px solid var(--color-danger-text);
  border-radius: var(--radius-control);
  padding: 10px var(--space-md);
  font-size: var(--fs-body-sm);
  color: var(--color-danger-text);
  margin-bottom: var(--space-lg);
}
.flash.success { background: var(--color-success-bg); border-color: var(--color-success-text); color: var(--color-success-text); }

/* 임시 비밀번호 발급/초기화 직후 1회성으로 보여주는 자격증명 박스 — 일반 success 배너와
   달리 "다음 페이지로 넘어가면 다시 볼 수 없다"는 점이 중요해서 별도로 눈에 띄게 하고
   값을 그대로 복사할 수 있는 버튼을 둔다. */
.credential-flash {
  background: var(--color-success-bg);
  border: 1px solid var(--color-success-text);
  border-radius: var(--radius-control);
  padding: var(--space-md);
  font-size: var(--fs-body-sm);
  color: var(--color-success-text);
  margin-bottom: var(--space-lg);
}
.credential-value-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
}
.credential-value {
  background: var(--surface-card);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  padding: 6px var(--space-md);
  font-family: monospace;
  font-size: var(--fs-body);
  color: var(--text-primary);
  letter-spacing: 0.02em;
}

/* 체크박스 드롭다운(다중선택) — 유저 목록의 "소속 그룹"/그룹관리의 "프로젝트"처럼
   다중선택은 유지해야 하지만 <select multiple>은 옵션이 몇 개만 있어도 80px+ 높이를
   강제로 차지해서 한 줄짜리 표 레이아웃과 안 맞았다. 패널 안 체크박스들이 전부 같은
   name으로 제출되므로 백엔드(request.form.getlist)는 <select multiple>과 동일하게
   받는다 — admin.py 쪽 변경 없음.
   2026-08-18(드롭다운 잘림 수정) — 원래 네이티브 <details>/<summary>로 열고닫기를
   처리하고 패널은 트리거 기준 position:absolute였는데, 이 표는
   `.card:has(table){overflow-x:auto}`(위 규칙, 컬럼 많은 표의 가로 스크롤 처리용) 안에
   있어 CSS 스펙상 overflow-x를 auto로 주면 overflow-y도 자동으로 clip 대상이 되는
   부작용으로 패널이 카드 경계에서 잘렸다. 부모 overflow를 visible로 바꾸면 표 가로
   스크롤 자체가 깨지므로(요청사항 — 금지), 대신 패널을 열 때 document.body로
   포털링하고 position:fixed + 트리거 좌표 기준으로 직접 배치한다
   (app/static/js/dropdown_portal.js). <details>는 포털링과 상성이 나빠(네이티브 open
   상태가 DOM 위치에 묶임) <div>+<button>으로 교체했다. */
.group-dropdown { position: relative; display: inline-block; }
.group-dropdown-summary {
  list-style: none;
  cursor: pointer;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  padding: 6px var(--space-md);
  font-size: var(--fs-body-sm);
  font-family: var(--font-sans);
  color: var(--text-primary);
  background: var(--surface-card);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.group-dropdown-summary::after { content: '▾'; font-size: 11px; color: var(--text-muted); }
.group-dropdown-summary.active { border-color: var(--color-purple-600); }
/* 열렸을 때 document.body의 직계 자식으로 포털링되므로 트리거와의 위치 관계는 전부 JS가
   top/left를 인라인 style로 계산해서 지정한다(position:fixed) — 여기서는 그 외의
   고정 규격(크기 상한/그림자/스크롤 방식)만 정의. z-index는 고정 요약바(450)보다 위,
   확인 모달(1000)보다 아래. */
.group-dropdown-panel {
  display: none;
  flex-direction: column;
  position: fixed;
  z-index: 700;
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-control);
  box-shadow: 0 8px 24px rgba(0,0,0,0.18);
  padding: var(--space-sm);
  min-width: 180px;
  max-height: 320px;
}
.group-dropdown-panel.dp-open { display: flex; }
/* 검색창은 스크롤 영역 밖에 고정 — 목록만 내부 스크롤(max-height 320px 안에서
   flex:1 + min-height:0으로 실제 스크롤 발생, 검색창은 flex-shrink:0으로 항상 보임). */
.group-dropdown-search {
  flex-shrink: 0;
  width: 100%;
  box-sizing: border-box;
  margin-bottom: 6px;
  padding: 5px 8px;
  font-size: var(--fs-body-sm);
  font-family: var(--font-sans);
  border: 1px solid var(--border-default);
  border-radius: 4px;
}
.group-dropdown-list { overflow-y: auto; flex: 1; min-height: 0; }
.group-dropdown-panel label {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px;
  border-radius: 4px;
  font-size: var(--fs-body-sm);
  color: var(--text-primary);
  cursor: pointer;
  white-space: nowrap;
}
.group-dropdown-panel label:hover { background: var(--surface-muted); }
.group-dropdown-empty { color: var(--text-muted); font-size: var(--fs-caption); padding: 4px 6px; white-space: nowrap; }
.group-dropdown-panel-wide { min-width: 240px; }

.muted { color: var(--text-muted); font-size: var(--fs-caption); }

/* 공통 확인 모달 — 삭제/비활성화 등 파괴적 액션 확인에 브라우저 기본 confirm() 대신 쓴다
   (base.html에 마크업+JS 있음, window.confirmModal). 카드 컴포넌트와 같은 radius/표면색을
   쓰고, 위쪽에 퍼플 포인트 바를 둬 사이드바 브랜드 컬러와 시각적으로 연결한다. */
.modal-overlay {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: rgba(22, 34, 61, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: var(--space-lg);
}
.modal-overlay[hidden] { display: none; }
.modal-card {
  background: var(--surface-card);
  border-radius: var(--radius-card);
  border-top: 4px solid var(--color-purple-600);
  padding: var(--space-xl);
  max-width: 480px;
  width: 100%;
  box-shadow: 0 12px 40px rgba(22, 34, 61, 0.3);
}
.modal-body {
  font-size: var(--fs-body);
  color: var(--text-primary);
  line-height: 1.5;
  white-space: pre-line;
  margin-bottom: var(--space-lg);
}
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
}

/* ── 잡 실행 화면 재설계(2026-08-16, juno 지시) — 세로 흐름 레이아웃 공용 컴포넌트.
   4개 탭(AI 분석/필드 정리/페르소나 확정/메시지매트릭스)이 전부 이 클래스들을 공유한다.
   새 색은 안 만듦 — 기존 퍼플/텍스트/서피스 토큰만 재사용. 본문보다 작은 글자를 안 쓰는
   원칙에 따라 이 섹션 안에서는 --fs-caption(18px)을 최소 크기로 둔다. */

/* 폼/카드 위주 화면(표 없음)용 중간 폭 — content-narrow(760px)보단 넓게, 데이터 표
   화면의 content-body 기본값(1800px)보단 좁게. 선택 카드 3개가 나란히 놓일 여유를 준다. */
.content-body.content-medium { max-width: 1100px; }

.process-section { margin-bottom: var(--space-xl); }
.process-section-title {
  font-size: var(--fs-subtitle);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  margin-bottom: var(--space-xs);
}
.process-section-desc {
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
  margin-bottom: var(--space-lg);
}

/* 진행 상태 — 상단 얇은 요약 줄. <details>로 접고 펼침(완료 상태면 서버가 open 속성을
   안 넣어 기본 접힘, 진행 중/확인 필요 상태면 open을 넣어 기본 펼침 — juno 지시
   "완료면 접어서 한 줄, 진행 중일 때만 펼쳐서 상세"). */
.progress-strip {
  border: 1px solid var(--border-default);
  border-radius: var(--radius-card);
  background: var(--surface-card);
  margin-bottom: var(--space-xl);
  overflow: hidden;
}
.progress-strip summary {
  list-style: none;
  cursor: pointer;
  padding: var(--space-md) var(--space-lg);
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-lg);
  font-size: var(--fs-body);
}
.progress-strip summary > strong { flex-shrink: 0; }
.progress-strip summary::-webkit-details-marker { display: none; }
.progress-strip summary::after { content: '자세히 ▾'; margin-left: auto; color: var(--text-muted); font-size: var(--fs-caption); flex-shrink: 0; }
.progress-strip[open] summary::after { content: '접기 ▴'; }
.progress-strip-bar-track {
  flex: 1 1 140px;
  max-width: 260px;
  height: 8px;
  border-radius: 999px;
  background: var(--surface-muted);
  overflow: hidden;
}
.progress-strip-bar-fill { height: 100%; background: var(--color-purple-600); }
.progress-strip-body {
  padding: var(--space-lg);
  border-top: 1px solid var(--border-default);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
.progress-strip-body > * + * { margin-top: var(--space-sm); }

/* 처리 방식 선택 카드(A-6, "바로 처리/모아서 처리/나눠서 처리") — 라디오를 카드 전체에
   깔아 라벨 전체가 클릭되게 하고, 선택되면 테두리+배경으로 강조한다. */
.choice-card-group {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-lg);
}
/* 뷰포트 기준 브레이크포인트(1000px) — 사이드바(208px 고정)를 빼면 콘텐츠 영역이
   실제로 760px 안팎이 되는 지점에 맞췄다(사이드바 자체는 반응형 접힘이 없어 이 화면
   재설계 범위 밖 — app 전체 셸의 별도 과제로 남겨둠). */
@media (max-width: 1000px) {
  .choice-card-group { grid-template-columns: 1fr; }
}
.choice-card {
  position: relative;
  display: block;
  border: 2px solid var(--border-default);
  border-radius: var(--radius-card);
  padding: var(--space-lg);
  padding-top: calc(var(--space-lg) + 4px);
  cursor: pointer;
  background: var(--surface-card);
  transition: border-color 0.12s, background 0.12s;
}
.choice-card:hover { border-color: var(--color-purple-400); }
.choice-card input[type="radio"] {
  position: absolute;
  top: var(--space-lg);
  right: var(--space-lg);
  width: 20px;
  height: 20px;
  margin: 0;
}
/* 2026-08-18(윈도우 렌더링 차이 수정 A/B) — 원래 `.choice-card:has(input:checked)`도
   같이 걸려 있었다. :has()가 무효 처리되는 브라우저에서는 콤마로 묶인 이 선택자 목록
   전체가 통째로 무효가 돼(단일 :is()/:where()가 아니라 일반 콤마 목록이라 하나만 골라
   무시하는 게 아니라 규칙 자체가 사라짐) `.choice-card.selected`까지 함께 죽는다 — 카드
   배경/테두리가 전부 사라지고 글자만 남는 증상이 여기서 발생했다. .selected 클래스
   하나만 남기고, 체크 상태와의 동기화는 app/static/js/compat.js가 change 이벤트로
   전담한다(:has() 완전 제거). */
.choice-card.selected {
  border-color: var(--color-purple-600);
  background: var(--color-purple-100);
}
.choice-card-badge {
  position: absolute;
  top: -12px;
  left: var(--space-lg);
  background: var(--color-purple-600);
  color: #fff;
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  padding: 2px var(--space-md);
  border-radius: 999px;
}
.choice-card-title { font-size: var(--fs-body); font-weight: var(--fw-bold); color: var(--text-primary); margin-bottom: var(--space-md); padding-right: 28px; }
/* 2026-08-16(juno 지시) — 처리 방식을 고르는 판단 근거는 시간이지 금액이 아니다.
   금액을 카드에서 아예 뺐으므로 이 자리(가장 큰 글자)는 이제 소요 시간이 차지한다
   (클래스 이름은 이전 라운드의 "가격" 용도 흔적이지만 마크업이 이 이름 하나만
   참조하므로 재작성 범위를 최소화하기 위해 그대로 둠 — 실제 렌더 내용만 시간으로 교체). */
.choice-card-price { font-size: var(--fs-stat); font-weight: var(--fw-bold); color: var(--color-purple-600); line-height: 1.2; margin: var(--space-xs) 0 var(--space-md); }
.choice-card-note { font-size: var(--fs-caption); color: var(--text-secondary); }
.choice-card-expand {
  display: none;
  margin-top: var(--space-md);
  padding-top: var(--space-md);
  border-top: 1px solid var(--border-default);
}
.choice-card.selected .choice-card-expand { display: block; }

/* 화면의 주 동작 버튼 — 크고 눈에 띄게(.btn.accent의 퍼플 배경 위에 크기만 키움) */
.btn-cta {
  font-size: var(--fs-subtitle);
  font-weight: var(--fw-bold);
  padding: var(--space-md) var(--space-xl);
  width: 100%;
  border-radius: var(--radius-card);
}

/* 라디오/체크박스 한 줄 선택지 — 라벨 전체가 클릭 영역, 넉넉한 패딩(juno 지시). */
.option-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-control);
  cursor: pointer;
  font-size: var(--fs-body);
  color: var(--text-primary);
}
.option-row:hover { background: var(--surface-muted); }
.option-row input[type="radio"], .option-row input[type="checkbox"] { flex-shrink: 0; }

/* 숫자 입력칸 — "100000"(6자리)이 안 잘리는 폭. 본문 크기 이상 글자. */
.input-number-wide {
  font-size: var(--fs-body);
  width: 130px;
  padding: 8px var(--space-md);
}

/* 페르소나 확정 "정의" 컬럼(2026-08-17, juno 지시) — 항목 하나에 한 줄, 라벨(작고 회색)과
   값(진하게)을 나눈다. 값이 없으면 "—"를 라벨과 동일한 무채색으로 눌러 "값이 있는 항목이
   먼저 눈에 들어오게" 한다. */
.persona-def-item {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
  line-height: 1.5;
}
.persona-def-item + .persona-def-item { margin-top: 2px; }
.persona-def-label {
  flex-shrink: 0;
  min-width: 84px;
  font-size: var(--fs-caption);
  color: var(--text-muted);
}
.persona-def-value {
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  color: var(--text-primary);
}
.persona-def-value.persona-def-empty {
  font-weight: var(--fw-regular);
  color: var(--text-muted);
}

/* ── 스크롤 고정 요약바(2026-08-18, 공용 컴포넌트 / 2·3차 보완) — 조건 영역이 있는
   화면 공통. 원래 조건 영역을 통째로 고정하지 않고, 그 영역이 화면 밖으로 나가는 순간에만
   압축된 한 줄 요약바가 나타난다(IntersectionObserver로 감지,
   app/static/js/sticky_condition_bar.js). 사이드바(--sidebar-width) 영역은 피해서 그
   오른쪽부터 덮는다. z-index는 임퍼스네이션 배너(500)보다 낮고 세그먼트 드롭다운(40)/
   그룹 드롭다운(20)보다 높게 — 서로 겹칠 일이 없는 구성이지만 방어적으로 순서를 지킨다.
   2차 보완(높이 44→64px, 칩을 "선택된 것만"에서 "화면이 가진 조건 축 전부, 항상"으로
   변경) 이후 3차 보완(2026-08-18) — "글씨가 너무 작다" 지적으로 컴포넌트 전용 절대값
   폰트(15/13/20px)를 폐기하고 화면 나머지 부분과 동일한 --fs-* 토큰을 그대로 쓴다
   (본문보다 작은 글자를 안 쓰는 이 파일 전체의 원칙을 이 컴포넌트에도 동일 적용). */
.sticky-condition-bar {
  position: fixed;
  left: var(--sidebar-width);
  right: 0;
  z-index: 450;
  height: 64px;
  background: var(--surface-card);
  border-bottom: 1px solid var(--border-default);
  box-shadow: 0 4px 14px rgba(22, 34, 61, 0.14);
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity 150ms ease, transform 150ms ease;
  pointer-events: none;
}
.sticky-condition-bar.visible { opacity: 1; transform: translateY(0); pointer-events: auto; }
.sticky-condition-inner {
  height: 100%;
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: 0 var(--space-xl);
  max-width: 1800px;
  margin: 0 auto;
}
.sticky-condition-page {
  flex-shrink: 0;
  font-weight: var(--fw-bold);
  font-size: var(--fs-body-sm);
  color: var(--text-primary);
  white-space: nowrap;
}
.sticky-condition-chips {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 6px;
  overflow: hidden;
  min-width: 0;
}
.sticky-chip {
  flex-shrink: 0;
  border-radius: 999px;
  padding: 4px 14px;
  font-size: var(--fs-caption);
  white-space: nowrap;
}
/* 선택된 축(값이 있음)은 진하게, 전체(미선택)인 축은 연하게 — juno 지시 그대로. */
.sticky-chip-selected { background: var(--color-purple-100); color: var(--color-purple-700); font-weight: var(--fw-medium); }
.sticky-chip-unselected { background: var(--surface-muted); color: var(--text-muted); }
.sticky-chip[data-clickable="true"] { cursor: pointer; }
.sticky-chip[data-clickable="true"]:hover { background: var(--color-purple-400); color: #fff; }
.sticky-chip-more { background: var(--surface-muted); color: var(--text-secondary); font-weight: var(--fw-medium); }
.sticky-condition-count {
  flex-shrink: 0;
  font-size: var(--fs-subtitle);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  white-space: nowrap;
}
.sticky-condition-top {
  flex-shrink: 0;
  background: transparent;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  color: var(--text-secondary);
  padding: 4px 14px;
  font-size: var(--fs-caption);
  cursor: pointer;
  white-space: nowrap;
}
.sticky-condition-top:hover { background: var(--surface-muted); }

/* ── 페르소나 목록 카드(2026-08-15, 페르소나 화면) — 원래 insights/personas.html
   안에 인라인 <style>로만 있었으나, 2026-08-18 메시지 매트릭스 화면이 동일한 좌측
   목록 UI를 그대로 재사용하게 되면서 공용 컴포넌트로 옮겼다(§5 실패패턴4 — 같은
   구조를 두 화면이 쓰게 됐을 때 인라인으로 복제하지 않고 여기로 승격). */
.persona-list-item {
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-card);
  padding: var(--space-md) var(--space-lg);
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.persona-list-item:hover { border-color: var(--color-purple-400); }
.persona-list-item.active { background: var(--color-purple-600); border-color: var(--color-purple-600); }
.persona-list-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.persona-list-name { font-size: var(--fs-body-sm); font-weight: var(--fw-medium); color: var(--text-primary); }
.persona-list-count { font-size: var(--fs-caption); color: var(--text-secondary); white-space: nowrap; }
.persona-list-quote {
  margin-top: var(--space-xs);
  font-size: var(--fs-caption); color: var(--text-secondary); line-height: 1.5;
  border-left: 3px solid var(--color-purple-100);
  padding-left: var(--space-sm);
}
.persona-list-item.active .persona-list-name,
.persona-list-item.active .persona-list-count,
.persona-list-item.active .persona-list-quote { color: #fff; }
.persona-list-item.active .persona-list-quote { border-left-color: rgba(255,255,255,0.5); }
.persona-list-item.active .badge { background: rgba(255,255,255,0.25); color: #fff; }
.persona-member-item {
  background: var(--surface-muted);
  border-radius: var(--radius-control);
  padding: var(--space-sm) var(--space-md);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
  line-height: 1.6;
}

/* ── 메시지 매트릭스 화면 재구성(2026-08-18) — 페르소나 화면과 동일한 좌우 3분할 구조.
   좌측 페르소나 목록은 위 .persona-list-item을 그대로 재사용, 우측 채널탭+카피카드만
   신규. */
.mm-copy-card {
  background: var(--surface-muted);
  border-radius: var(--radius-control);
  padding: var(--space-md);
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-md);
}
.mm-copy-text {
  font-size: var(--fs-body-sm);
  color: var(--text-primary);
  line-height: 1.6;
  flex: 1;
}
.mm-copy-btn {
  flex-shrink: 0;
  background: transparent;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  color: var(--text-secondary);
  padding: 4px 10px;
  font-size: var(--fs-caption);
  cursor: pointer;
  white-space: nowrap;
}
.mm-copy-btn:hover { background: var(--surface-card); }
.mm-copy-btn.copied { background: var(--color-purple-600); color: #fff; border-color: var(--color-purple-600); }
