/* @vendit/admin-ui: 정적 페이지용 배포본
 * 생성됨: scripts/build-static-css.mjs: 직접 고치지 마라.
 * 토큰 정본: admin-ui/theme.css (그 위 문서 정본: vcms-dashboards/DESIGN_TOKENS.md)
 * 토큰 99개 + 정적 셸.
 *
 * 쓰는 법:
 *   <link rel="stylesheet" href="https://vendit-ds.pages.dev/v1.css">
 *   <script src="https://vendit-ds.pages.dev/v1.js" defer></script>
 *   <header class="appbar" data-ds-appbar data-title="대시보드 이름"></header>
 */

:root{
  /* 반경 0. 예외 없다. 규격 2-6 */
  --radius: 0;

  /* 배지. 규격 3-2 의 의미 5를 옛 색이름에 얹은 것이다.
   * 중립과 성공이 같은 값인 것은 의도다. 운영 화면에서 정상이 다수 상태고
   * 다수를 칠하면 그게 배경이 되어 진짜 이상이 안 보인다.
   * 이름이 색(green/blue/orange)인 채로 값이 무채인 자리가 생긴다. 이름 정리는
   * N-2 의 일이고 지금 같이 바꾸면 값 변경과 이주가 한 커밋에 섞인다. */
  --color-badge-green-bg:   #f4f4f4;   /* 성공 = 중립. 색 없음 */
  --color-badge-green-text: #6f6f6f;   /* 4.56:1 */
  --color-badge-red-bg:     #da1e28;   /* 위험 */
  --color-badge-red-text:   #ffffff;   /* 5.00:1 */
  --color-badge-gray-bg:    #f4f4f4;   /* 중립. 기본값 */
  --color-badge-gray-text:  #6f6f6f;   /* 4.56:1 */
  --color-badge-blue-bg:    #f4f4f4;   /* 정보 = 무채 + 진한 글자 */
  --color-badge-blue-text:  #161616;   /* 16.45:1 */
  --color-badge-orange-bg:  #f1c21b;   /* 주의 */
  --color-badge-orange-text: #161616;  /* 10.74:1 */
  --color-badge-amber-bg:   #f1c21b;   /* 주의. orange 와 같다 */
  --color-badge-amber-text: #161616;
}

/* @theme → :root. Tailwind at-rule 은 브라우저가 못 읽는다. */
:root{
  /* ── 서체 (규격 2-2) ────────────────────────────────────────
   * 웹폰트 0개. 한글 서브셋 비용 0. Inter 와 Pretendard 를 쓰지 않는다. */
  --font-sans: "Helvetica Neue", Helvetica, -apple-system, "Apple SD Gothic Neo", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;

  /* ── 채움 (옛 이름 primary) ──────────────────────────────────
   * 스위스에 브랜드 색이 없다. 채움 버튼은 `--ink` 다(규격 2-1).
   * 옛 값 #7c3aed 와 #6d28d9 는 폐기다. 3레포에 아직 리터럴로 남아 있고
   * 그건 Track B Phase 4 의 이주 항목이다. */
  --color-primary:           #161616;
  --color-primary-hover:     #393939;
  --color-primary-light:     #f4f4f4;
  --color-primary-muted:     #e0e0e0;
  --color-primary-foreground: #ffffff;   /* 18.09:1 */
  --color-on-primary-light:  #161616;

  /* ── 바탕과 글자 (규격 2-1) ──────────────────────────────────
   * 카드가 바탕과 같은 색인 것은 의도다. 층을 그림자가 아니라 괘선으로 만든다. */
  --color-background:         #ffffff;   /* --bg */
  --color-foreground:         #161616;   /* --ink, 18.09:1 */
  --color-card:               #ffffff;
  --color-card-foreground:    #161616;
  --color-popover:            #ffffff;
  --color-popover-foreground: #161616;

  /* ── 보조면 (--bg-sub) ──────────────────────────────────────
   * 표 머리, 막대 홈, 중립 배지, 코드 블록, 호버 행. */
  --color-secondary:            #f4f4f4;
  --color-secondary-foreground: #161616;
  --color-muted:                #f4f4f4;
  --color-muted-foreground:     #6f6f6f;   /* --bg 위 5.02, --bg-sub 위 4.56 */
  --color-accent:               #f4f4f4;
  --color-accent-foreground:    #161616;

  /* ── 의미색 둘 (규격 3-2) ────────────────────────────────────
   * 성공에 색을 안 쓴다. 옛 #16a34a 는 흰 글자를 얹으면 3.29 로 어차피 미달이었다.
   * 위험 라이트는 Carbon Red 60 이다. 옛 #e2231a 는 --bg-sub 위 4.25 로 미달이라
   * Dean 판정으로 통으로 바꿨다(2026-09-17). */
  --color-destructive: #da1e28;
  --color-danger:      #da1e28;   /* 흰 바탕 5.00, --bg-sub 위 4.54 */
  --color-danger-hover: #b81922;
  --color-success:     #6f6f6f;   /* 무채. 중립과 같다 */
  /* 위험색 위 글자. 테마마다 다르다는 것이 요점이라 토큰이 필요하다.
     다크 #ff5c4d 에 흰 글자를 얹으면 3.04 로 미달이고, 생성기가 .btn-danger 에
     color:#fff 를 박아 둬서 팔레트를 바꾸는 순간 다크에서 읽히지 않는다. */
  --color-danger-foreground: #ffffff;   /* --on-danger. #da1e28 위 5.00 */

  /* ── 선 (규격 2-6) ──────────────────────────────────────────
   * 두께는 1px 하나다. line-strong 은 강조 구분선 하나(--rule-strong)라
   * 회색이 아니라 잉크다. 이게 층을 만드는 유일한 수단이다. */
  --color-border:        #e0e0e0;   /* --rule */
  --color-line:          #e0e0e0;
  --color-line-strong:   #161616;   /* --rule-strong */
  --color-line-grid:     #6f6f6f;
  --color-input:  #e0e0e0;
  --color-ring:   #161616;          /* 초점 링. 규격 6-3 */

  /* ── 확장 시맨틱 ───────────────────────────────────────────
   * `-light` 셋은 배너 채움이다. 규격 3-2 대로라면 위험 배너는 `--danger` 를
   * 통으로 채우고 글자가 `--on-danger` 여야 한다. 그런데 지금 소비자들이 이 위에
   * 진한 글자를 얹고 있어서 값만 바꾸면 빨강 위 진한 빨강이 되어 3.61 로 미달한다.
   * 그래서 여기서는 중립으로 두고, 위험 배너를 채움으로 올리는 것은 소비자 쪽
   * 글자색을 같이 고치는 Track B Phase 4 항목이다. 값만 바꾸는 이 커밋이
   * 대비를 깨뜨리지 않는 것이 먼저다.
   * 주의는 예외다. --warn 위 글자가 양쪽 테마 다 #161616 이고 10.74 라 지금 소비자가
   * 얹는 진한 글자와 그대로 맞는다. */
  --color-warning:           #f1c21b;   /* --warn. 양쪽 테마 같은 값 */
  --color-destructive-light: #f4f4f4;   /* 중립 채움. 위 문단 */
  --color-warning-light:     #f1c21b;
  --color-success-light:     #f4f4f4;
  --color-surface-hover:     #f4f4f4;
  --color-surface-disabled:  #f4f4f4;
  --color-line-subtle:       #f4f4f4;
  --color-scrim:             rgb(22 22 22 / 0.4);   /* --ink 40%. 규격 2-1 */
  /* 항상 어두운 플로팅 바. 테마 무관 동일값이라 .dark 가 재정의하지 않는다. */
  --color-inverse-surface:            #161616;
  --color-inverse-surface-foreground: #ffffff;
  --color-inverse-surface-muted:      #a8a8a8;
  --color-inverse-surface-line:       #393939;

  /* ── 데이터 인코딩 (규격 4-G) ────────────────────────────────
   * 히트맵은 회색 5단이다. 지금 점유율 4단, 알림 6단, 요금 상하 3벌이 따로 놀던 것을
   * 한 계단으로 모은다. 이상치만 --danger 를 쓴다.
   * 마감은 이상치가 아니라 상태라 계단의 맨 끝을 준다. */
  --color-occupancy-0: #f4f4f4;
  --color-occupancy-1: #e0e0e0;
  --color-occupancy-2: #a8a8a8;
  --color-occupancy-3: #6f6f6f;
  --color-closed:      #161616;

  /* 요금 변동. 규격 4-G 규칙 1 이 "계열을 색으로 가르지 않는다" 라서 상승과 하락을
   * 농도로 가른다. 방향은 색이 아니라 부호와 화살표가 말한다(규칙 4, 색만으로
   * 정보를 전달하지 않는다). color-mix 로 옅어지므로 글자는 양쪽 다 잉크다. */
  --color-rate-up:        #161616;
  --color-rate-up-text:   #161616;
  --color-rate-down:      #6f6f6f;
  --color-rate-down-text: #161616;

  /* ── 환경 ──────────────────────────────────────────────────
   * DEV 운영 종료(2026-05-17)로 둘이 같은 값이다. 보라를 잉크로 바꿨다. */
  --color-env-prod: #161616;
  --color-env-dev:  #161616;

  /* ── 12 아래 글자 ───────────────────────────────────────────
   * **규격 2-3 은 제일 작은 단이 12 다. 이 셋은 그 아래라 규격 위반이다.**
   * 값을 안 바꿨다. 9px 를 12px 로 올리면 vcms-admin 달력 셀과 KPI 카드의 줄바꿈이
   * 통째로 바뀌어서 팔레트 변경과 밀도 변경이 한 커밋에 섞인다. 별도 항목으로 둔다. */
  --text-nano:    0.5625rem;  /* 9px  */
  --text-micro:   0.625rem;   /* 10px */
  --text-caption: 0.6875rem;  /* 11px */

  /* ── 간격 ───────────────────────────────────────────────────
   * 2026-09-17 에 생성기에서 여기로 옮겼다. 규격 2-4 는 간격을 기반 열한 종류 중
   * 하나로 두고 "theme.css 의 @theme 블록에 선언한다" 고 적는데 실제로는 생성기가
   * 자기 :root 에 따로 들고 있었다. 그래서 쇼케이스가 토큰 수를 셀 때 "theme.css
   * 정본 + 정적 전용 간격" 이라고 둘로 나눠 말해야 했다. 한 사실에 출처가 둘이면
   * 언젠가 갈라진다. 오늘만 그 병을 네 번 봤다.
   *
   * 규격 2-4 는 8의 배수만 쓴다(8 16 24 32 48 80). 지금 값은 4px 그리드 그대로다.
   * 좁히는 것은 밀도가 눈에 띄게 바뀌는 변경이라 렌더를 보고 정한다. 실측은
   * --sp-1(4px) 참조가 셸에서 0회고, 걸림돌은 12px 와 20px 이며 합쳐 44곳이다. */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px;
  --sp-5:20px; --sp-6:24px; --sp-8:32px; --sp-10:40px; --sp-12:48px;

  /* 그림자는 0 이다(규격 2-6). Tailwind 의 shadow-* 유틸은 이 이름을 읽으므로 계단을
     통째로 0 으로 둬야 React 쪽도 평평해진다. sm 과 md 만 껐더니 shadow-xl 을 쓰는
     모달과 하단 바에 그림자가 남아 있었다(2026-09-18 실측). */
  --shadow-2xs: none;
  --shadow-xs: none;
  --shadow-sm: none;
  --shadow-md: none;
  --shadow-lg: none;
  --shadow-xl: none;
  --shadow-2xl: none;
  --inset-shadow-2xs: none;
  --inset-shadow-xs: none;
  --inset-shadow-sm: none;

  /* 반경 0 이라 pill 도 각지다. Tailwind 의 rounded-full 은 토큰이 아니라 리터럴
     9999px 이라 여기서 못 막는다. 부품 쪽에서 rounded-none 으로 쓴다. */

  /* ── 본문 최대 폭 ───────────────────────────────────────────
   * 이 주석이 근거로 적었던 "vcms-admin Layout 의 max-w-[1600px]" 은 사실이 아니다.
   * 2026-09-17 실측으로 vcms-admin 에 1600 이 0곳이고 17곳이 전부 Tailwind 표준
   * 클래스 max-w-screen-2xl, 즉 1536 이다. 1600 을 쓰는 곳은 vcms-backlog 4곳이다.
   * 값 자체는 Dean 결정 대기다. 후보와 비용은 crumbs/inventory/shell-max-decision.md. */
  --shell-max: 1600px;

  /* ── 반경 0 (규격 2-6) ──────────────────────────────────────
   * 네 단을 다 0 으로 둔다. 토큰을 지우면 소비자 빌드가 깨진다. */
  --radius-sm: 0;
  --radius-md: 0;
  --radius-lg: 0;
  --radius-xl: 0;

  /* ── 그림자 0 (규격 2-6) ────────────────────────────────────
   * 엘리베이션이 없다. 층은 괘선과 위치와 크기로만 만든다.
   * sticky 열과 머리는 그림자 대신 각 화면이 1px --rule 로 경계를 준다.
   * 토큰은 이름만 남기고 none 이다. */
  --shadow-sticky-col:    none;
  --shadow-sticky-header: none;

  /* ── z-index (값 보존, 스태킹 무변경) ─────────────────────── */
  --z-sticky:      30;
  --z-nudge:       50;
  --z-overlay:     60;
  --z-floating:    70;
  --z-modal:       80;
  --z-popover:     100;
  --z-popover-top: 101;
  --z-tooltip:     110;
  --z-toast:       120;
  --z-max:         9999;
}

.dark{
  --color-primary:            #f4f4f4;
  --color-primary-hover:      #e0e0e0;
  --color-primary-light:      #262626;
  --color-primary-muted:      #393939;
  --color-primary-foreground: #161616;   /* 16.45:1 */
  --color-on-primary-light:   #f4f4f4;

  --color-background:         #161616;
  --color-foreground:         #f4f4f4;   /* 16.45:1 */
  --color-card:               #161616;
  --color-card-foreground:    #f4f4f4;
  --color-popover:            #161616;
  --color-popover-foreground: #f4f4f4;

  --color-secondary:            #262626;
  --color-secondary-foreground: #f4f4f4;
  --color-muted:                #262626;
  --color-muted-foreground:     #a8a8a8;   /* --bg 위 7.61, --bg-sub 위 6.36 */
  --color-accent:               #262626;
  --color-accent-foreground:    #f4f4f4;

  --color-destructive:  #ff5c4d;
  --color-danger:       #ff5c4d;   /* --bg 위 5.93, --bg-sub 위 4.96 */
  --color-danger-hover: #ff8478;
  --color-danger-foreground: #161616;   /* #ff5c4d 위 5.93. 흰 글자면 3.04 로 미달 */
  --color-success:      #a8a8a8;

  --color-border:      #393939;
  --color-line:        #393939;
  --color-line-strong: #f4f4f4;
  --color-line-grid:   #a8a8a8;
  --color-input:       #393939;
  --color-ring:        #f4f4f4;

  /* 배지 다크. 주의는 양쪽 테마 같은 값이고 위 글자도 #161616 으로 같다.
   * 위험은 다크에서 글자가 뒤집힌다. 흰 글자를 얹으면 3.04 로 미달이다. */
  --color-badge-green-bg:  #262626;  --color-badge-green-text:  #a8a8a8;
  --color-badge-red-bg:    #ff5c4d;  --color-badge-red-text:    #161616;
  --color-badge-gray-bg:   #262626;  --color-badge-gray-text:   #a8a8a8;
  --color-badge-blue-bg:   #262626;  --color-badge-blue-text:   #f4f4f4;
  --color-badge-orange-bg: #f1c21b;  --color-badge-orange-text: #161616;
  --color-badge-amber-bg:  #f1c21b;  --color-badge-amber-text:  #161616;

  --color-warning:           #f1c21b;
  --color-destructive-light: #262626;
  --color-warning-light:     #f1c21b;
  --color-success-light:     #262626;
  --color-surface-hover:     #262626;
  --color-surface-disabled:  #262626;
  --color-line-subtle:       #262626;
  /* 규격 2-1 은 오버레이를 --ink 40% 라고 적는데 다크에서 --ink 는 밝은 색이다.
   * 그대로 쓰면 모달 뒤가 밝아진다. 백드롭은 어둡게 하는 것이 목적이라 검정을 쓴다. */
  --color-scrim:             rgb(0 0 0 / 0.6);

  /* 히트맵 다크. 계단의 반대편이다. */
  --color-occupancy-0: #262626;
  --color-occupancy-1: #393939;
  --color-occupancy-2: #6f6f6f;
  --color-occupancy-3: #a8a8a8;
  --color-closed:      #f4f4f4;

  --color-rate-up:        #f4f4f4;
  --color-rate-up-text:   #f4f4f4;
  --color-rate-down:      #a8a8a8;
  --color-rate-down-text: #f4f4f4;

  --shadow-sticky-col:    none;
  --shadow-sticky-header: none;
}

/* ── 간격 스케일 (정적 전용 추가) ───────────────────────────
 * Tailwind 를 안 쓰는 페이지엔 간격 기준이 없어서 매번 눈대중 px 가 박혔다.
 * 4px 그리드. 이 밖의 값은 쓰지 마라. */
/* 이 블록은 값을 만들지 않는다. 별칭만 둔다.
   값은 전부 theme.css 가 준다. 2026-09-17 까지 여기에 간격 9종과 서체 스택과 그림자를
   따로 들고 있었고, 그래서 theme.css 를 고쳐도 화면이 안 바뀌는 자리가 생겼다.
   --sans 의 스택에는 규격 2-2 가 금지한 Pretendard 가 들어 있었다.
   이름을 지우지 않는 이유는 이 이름을 참조하는 페이지가 남아 있어서다. */
:root{
  --sans:var(--font-sans);
  --mono:var(--font-mono);
}
.dark{
  /* 네이티브 스크롤바·폼 컨트롤도 다크로. 없으면 긴 팝오버에서 흰 스크롤바가 튄다. */
  color-scheme:dark;
}

*{box-sizing:border-box}
html{
  -webkit-text-size-adjust:100%;
  scrollbar-gutter:stable;   /* 탭 전환으로 스크롤바가 생겼다 사라지며 가로로 튀는 것 방지 */
}
body{
  margin:0; background:var(--color-background); color:var(--color-foreground);
  font:14px/1.43 var(--sans); -webkit-font-smoothing:antialiased;
}
code,pre,.ds-mono{font-family:var(--mono)}
.ds-num{font-variant-numeric:tabular-nums; letter-spacing:-.01em}
a{color:var(--color-primary); text-decoration:none}
h1,h2,h3{margin:0; text-wrap:balance}
button{font:inherit; color:inherit; cursor:pointer}
:focus-visible{outline:2px solid var(--color-ring); outline-offset:2px; border-radius:var(--radius-sm)}

/* 마지막 안전망이다. body 에는 넣지 않는다. body 가 스크롤 컨테이너가 되면서
   표 머리의 position:sticky 가 죽는다. 그리고 이건 증상만 가리는 수단이라
   measure-mobile 의 단언은 scrollWidth 를 보므로 이걸로 통과하지 않는다.
   React 앱에는 안 나간다. 거기선 진짜 넘침을 가려 버린다(vcms-dashboards 의
   min-w-[1740px] 표). */
html{overflow-x:hidden}


/* 움직임을 줄이라고 설정한 사람에게는 줄인다. 규격 6-6 이고 12레포 전부에 있어야 한다.
   BASE 에 두면 정적 4장만 받는다. 셸에 두면 React 소비자도 shell.css 로 같이 받는다.
   이건 부품이 아니라 접근성 바닥이라 여기가 맞는 자리다. */
@media (prefers-reduced-motion:reduce){*{animation:none!important; transition:none!important}}

/* ── 앱바: 모든 대시보드 공통. 로고 클릭 = Access 런처 ── */
.ds-appbar{
  position:sticky; top:0; z-index:var(--z-sticky,30);
  background:var(--color-background);
  border-bottom:1px solid transparent;
  transition:border-color .2s;
}
.ds-appbar.is-scrolled{border-bottom-color:var(--color-line)}
/* 줄 높이 48 은 규격 2-5 다. 예전엔 높이가 없고 위아래 패딩 16 에 내용 32 라 64 였고
   그건 폭과 무관해서 데스크톱에서도 64 였다. 높이를 박고 패딩을 8 로 줄인다. */
.ds-appbar-in{
  max-width:var(--shell-max); margin:0 auto;
  display:flex; align-items:center; gap:var(--sp-4);
  height:48px; padding:0 var(--sp-6); box-sizing:border-box;
}
/* 브랜드가 안 줄어들면 제목이 긴 페이지에서 앱바가 넘친다. 지금 넘치는 페이지가
   없지만 조건부 위험이라 여기서 닫는다. 줄바꿈은 안 준다. flex-wrap 을 주면 폰에서
   앱바가 두 줄이 되어 높이 48 이 깨진다. */
.ds-appbar .ds-brand{display:flex; align-items:center; gap:9px; min-height:40px;
  flex-shrink:1; min-width:0; color:var(--color-foreground)}
.ds-appbar .ds-logo{flex-shrink:0; display:block}
.ds-appbar h1{
  font-size:16px; font-weight:700; letter-spacing:.03em; white-space:nowrap;
  display:inline-flex; gap:5px; color:var(--color-foreground);
  min-width:0; overflow:hidden; text-overflow:ellipsis;
}
/* 강조는 색이 아니라 농도로 낸다. --color-primary 가 잉크가 되면서 제목과 같은 색이
   되어 구분이 사라졌다. 무채 안에서 가르는 것이 규격 3-2 의 방식이다.
   굵기도 800 에서 700 으로 내렸다. 규격 2-3 이 쓰는 굵기는 300/400/600/700 넷이다. */
.ds-appbar h1 .ds-mark,
.ds-appbar h1 .ds-devmark{color:var(--color-muted-foreground); font-weight:400}   /* devmark = 구 이름 */
.ds-appbar .ds-spacer{flex:1 1 auto}

/* 셸은 페이지의 전역 요소 규칙에 안 깨져야 한다. 두 번 깨졌다.
   vendit-workflow 의 h1{max-width:12ch} 가 제목을 "VENDIT WOR" 로 잘랐고,
   Keyword Radar 어드민의 svg{width:100%} 가 26px 로고를 늘려 h1 을 0 으로 밀었다.
   둘 다 페이지가 자기 본문을 위해 쓴 규칙이고 앱바를 노린 것이 아니다. 페이지마다
   쫓아다니며 막는 대신 셸이 자기 것을 여기서 다시 못 박는다. */
.ds-appbar h1{max-width:none; text-transform:none}
.ds-appbar .ds-logo{width:26px; height:26px; max-width:none; flex-shrink:0}
.ds-subbar{
  max-width:var(--shell-max); margin:0 auto;
  padding:0 var(--sp-6) var(--sp-2);
  display:flex; align-items:center; gap:var(--sp-2); flex-wrap:wrap;
}
.ds-subbar .ds-stamp{font-size:12px; color:var(--color-muted-foreground); font-variant-numeric:tabular-nums}
.ds-wrap{max-width:var(--shell-max); margin:0 auto; padding:var(--sp-6) var(--sp-6) 72px}

/* 다시 불러오기 버튼 (subbar) */
.ds-refresh-btn{
  display:inline-flex; align-items:center; justify-content:center;
  /* 규격 2-5 의 컨트롤 40. coarse 에서는 아래 블록이 48 로 올린다.
     반경은 0 이라 여기에 리터럴을 두지 않는다(규격 2-6). */
  width:40px; height:40px; padding:0; border:0; background:none;
  color:var(--color-muted-foreground);
  transition:background .12s, color .12s;
}
.ds-refresh-btn:hover{color:var(--color-foreground); background:var(--color-secondary)}
.ds-refresh-btn svg{width:14px; height:14px}
.ds-refresh-btn:disabled{cursor:default}
.ds-refresh-btn.is-spinning svg{animation:dsspin .8s linear infinite}
@keyframes dsspin{to{transform:rotate(360deg)}}
.ds-refresh-status{font-size:12px; color:var(--color-muted-foreground)}
.ds-refresh-status.ds-ok{color:var(--color-success)}
.ds-refresh-status.ds-err{color:var(--color-danger)}

/* ── 프로필 메뉴: 아이덴티티 / 화면모드 / 로그아웃 ── */
.ds-pm{position:relative; flex-shrink:0}
.ds-pm-btn{
  /* 둥근 아바타는 스위스와 안 맞는다. 정사각이다 */
  width:40px; height:40px; border:0;
  background:var(--color-primary-muted); color:var(--color-on-primary-light);
  font-weight:600; font-size:12px;
  display:inline-flex; align-items:center; justify-content:center; transition:filter .12s;
}
.ds-pm-btn:hover{filter:brightness(.94)}
.ds-pm-menu{
  display:none; position:absolute; right:0; top:calc(100% + 8px); width:256px;
  background:var(--color-card); border:1px solid var(--color-line);
  border-radius:var(--radius-lg);
  overflow:hidden; z-index:var(--z-popover,100);
}
.ds-pm.is-open .ds-pm-menu{display:block}
.ds-pm-id{display:flex; align-items:center; gap:var(--sp-3); padding:14px var(--sp-4)}
.ds-pm-av{
  /* 반경 0. 9999px 은 토큰이 아니라 리터럴이라 --radius 를 0 으로 내려도 안 따라왔다. */
  width:36px; height:36px;
  background:var(--color-primary-muted); color:var(--color-on-primary-light);
  font-weight:600; font-size:14px;
  display:inline-flex; align-items:center; justify-content:center; flex-shrink:0;
}
.ds-pm-who{min-width:0; flex:1}
/* 이름 옆에 권한 배지가 올 수 있다. 블록인 채로 배지를 넣으면 배지가 아래로 떨어지고
   ellipsis 도 안 먹는다. 이름만 줄이고 배지는 줄지 않게 가른다. */
.ds-pm-nm{display:flex; align-items:center; gap:var(--sp-2); min-width:0;
  font-size:14px; font-weight:600; color:var(--color-foreground)}
.ds-pm-nm > :first-child{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.ds-pm-nm > :not(:first-child){flex-shrink:0}
.ds-pm-em{font-size:12px; color:var(--color-muted-foreground); overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.ds-pm-div{height:1px; background:var(--color-line)}
.ds-pm-row{
  display:flex; align-items:center; justify-content:space-between; gap:var(--sp-2);
  width:100%; padding:10px var(--sp-4); background:none; border:0; font:inherit;
  font-size:14px; color:var(--color-foreground); text-align:left; text-decoration:none;
}
.ds-pm-row:hover{background:var(--color-secondary)}
.ds-pm-rl{display:inline-flex; align-items:center; gap:9px}
.ds-pm-rv{font-size:12px; font-weight:600; color:var(--color-muted-foreground)}
.ds-pm-ic{width:16px; height:16px; color:var(--color-muted-foreground); flex-shrink:0}
.ds-pm-logout{color:var(--color-danger)}
.ds-pm-logout .ds-pm-ic{color:var(--color-danger)}
.ds-pm-logout:hover{background:var(--color-destructive-light)}


/* ── 포인터와 폭: 셸 몫 ──────────────────────────────────────────
   부품 쪽 같은 블록은 PARTS 에 따로 있다. 한 블록에 섞어 두면 React 앱이
   shell.css 만 물었을 때 앱바 버튼이 손가락 크기로 안 올라간다. */
@media (pointer:coarse){
  /* 규격 2-5 는 앱바 안 아이콘 버튼을 32 에서 48 로 키우라고 적는다. 가짜로 넓히지
     않는다. ::after 로 히트 영역만 넓히면 손가락은 닿지만 측정기가 못 본다.
     안 보이는 것을 통과로 찍으면 그때부터 게이트가 거짓말을 한다.
     앱바가 정확히 48 이라 48 짜리 버튼이 여백 없이 들어간다. */
  .ds-pm-btn{width:48px; height:48px}
  .ds-refresh-btn{width:48px; height:48px}
  .ds-appbar .ds-brand{min-height:48px}
}
/* 호버로만 드러나는 것을 금지한다(규격 3-6 규칙 2). 터치에서는 :hover 가 탭 뒤에
   들러붙어서 눌린 것처럼 남는다. */
@media (hover:none){
  .ds-refresh-btn:hover, .ds-pm-btn:hover{background:inherit; color:inherit; filter:none}
}
@media (max-width:639px){
  .ds-appbar-in{padding:0 var(--sp-4)}
  .ds-subbar{padding:0 var(--sp-4) var(--sp-2)}
  .ds-wrap{padding:var(--sp-4) var(--sp-4) 72px}
  /* 폰에서 프로필 메뉴가 화면 밖으로 나가지 않게. 256 고정이면 320px 화면에서
     좌우 여백 16 을 빼고도 들어가지만 그보다 좁은 기기가 남아 있다. */
  .ds-pm-menu{width:min(256px, calc(100vw - var(--sp-8)))}
}

/* ── 프리미티브 (admin-ui/src/shared/ui 대응) ── */
/* card: 정본은 React card.tsx.
 *   cva base "@container rounded-md"      → border-radius:var(--radius-md)
 *   defaultVariants.padding = "md" = p-5  → padding:20px = var(--sp-5)
 * radar/pulse 는 radius-lg + 20px 를 쓰지만 반지름은 React 가 명시하므로 React 가 이긴다.
 * 패딩은 예전 정적 값(--sp-4=16px)이 React 와 어긋나 있었다 → --sp-5(20px)로 맞춤. */
.ds-card{
  background:var(--color-card); border:1px solid var(--color-line);
  border-radius:var(--radius-md);
  padding:var(--sp-5);
}
.ds-card-pad-lg{padding:var(--sp-5) var(--sp-6)}      /* 정적 전용: 넓은 섹션 카드 */

/* ── 배지 ─────────────────────────────────────────────────────
 * 모디파이어 이름은 React badge.tsx 의 시맨틱 이름이 정본이다:
 *   default | primary | success | warning | danger | info
 * 색 이름(.ds-badge-green/-red/-blue/-amber)은 폐기예정 별칭으로 남겨둔다 -
 * 마이그레이션 중간에 페이지가 깨지지 않게 하기 위한 것이고, 새 마크업엔 쓰지 마라.
 *
 * base 값 출처: React badge.tsx = "inline-flex items-center font-medium rounded-sm"
 *   + size md("px-2 py-0.5 text-xs") → font-weight:500 / padding:2px 8px / font-size:12px.
 * border 는 React 가 침묵 → 3개 정적 페이지 중 2개(radar·pulse)가 이미 쓰는
 * 1px transparent 를 채택. 도메인 클래스가 보더를 켜도 크기가 안 튀게 하는 값이다. */
.ds-badge{
  display:inline-flex; align-items:center; font-weight:500;
  border:1px solid transparent;
  border-radius:var(--radius-sm); padding:2px var(--sp-2); font-size:12px;
  background:var(--color-badge-gray-bg); color:var(--color-badge-gray-text);
}
.ds-badge-sm{padding:2px 6px; font-size:var(--text-micro,10px)}   /* React size="sm" */
.ds-badge-default{background:var(--color-badge-gray-bg); color:var(--color-badge-gray-text)}
.ds-badge-success,
.ds-badge-green{background:var(--color-badge-green-bg); color:var(--color-badge-green-text)}
.ds-badge-danger,
.ds-badge-red{background:var(--color-badge-red-bg); color:var(--color-badge-red-text)}
.ds-badge-info,
.ds-badge-blue{background:var(--color-badge-blue-bg); color:var(--color-badge-blue-text)}
.ds-badge-warning,
.ds-badge-amber{background:var(--color-badge-amber-bg); color:var(--color-badge-amber-text)}
/* orange 는 React 에 대응 시맨틱이 없다(개선요청 등 radar 도메인 색). 색 이름 그대로 유지. */
.ds-badge-orange{background:var(--color-badge-orange-bg); color:var(--color-badge-orange-text)}
/* React 는 text-primary 지만 정적은 --color-on-primary-light 를 쓴다: 다크에서
 * primary-muted(#4c1d95) 위 primary(#7c3aed) = 보라 위 보라로 대비가 무너진다.
 * (segmented-control.tsx 가 같은 이유로 다크에서 색을 뒤집는다.) 값 유지 = 의도된 분기. */
.ds-badge-primary{background:var(--color-primary-muted); color:var(--color-on-primary-light)}

/* ⚠️ 맨 .ds-btn 은 "채운 프라이머리"(보라 배경 + 흰 글씨)다. React button.tsx 의
 * defaultVariants.variant = "primary" 와 같은 뜻.
 * radar/pulse 는 과거에 맨 .ds-btn 을 정반대 뜻(중립 아웃라인)으로 썼다. 같은 이름,
 * 반대 의미. 중립 버튼이 필요하면 반드시 class="btn btn-secondary" 를 써라.
 * 로컬 .ds-btn 정의를 지우면서 btn-secondary 를 안 붙이면 모든 버튼이 보라로 변한다. */
.ds-btn{
  display:inline-flex; align-items:center; justify-content:center; gap:6px;
  /* 타겟은 두 변 다다. 높이만 잡아 두면 "삭제" 처럼 짧은 라벨이 38 폭으로 남아
     coarse 바닥 48 을 못 넘는다. .ds-tab 에 같은 것을 고치고도 여기는 놓쳤다. */
  height:40px; min-width:40px; padding:0 var(--sp-3); font-size:14px; font-weight:600;
  border-radius:var(--radius-md); border:1px solid transparent;
  background:var(--color-primary); color:var(--color-primary-foreground);
  transition:background .12s;
}
.ds-btn:hover{background:var(--color-primary-hover)}
.ds-btn-sm{height:32px; min-width:32px; padding:0 var(--sp-3); font-size:12px}
/* 중립 버튼. React button.tsx variant="secondary"
 * ("bg-card text-foreground border border-border hover:bg-secondary") 와 1:1.
 * radar/pulse 가 지금까지 맨 .ds-btn 으로 부르던 그 버튼이다.
 * .ds-btn-outline 은 같은 규칙의 폐기예정 별칭: React 이름은 secondary 다. */
.ds-btn-secondary,
.ds-btn-outline{
  background:var(--color-card); color:var(--color-foreground);
  border-color:var(--color-border);
}
.ds-btn-secondary:hover,
.ds-btn-outline:hover{background:var(--color-secondary)}
.ds-btn-ghost{background:none; color:var(--color-muted-foreground); border-color:transparent}
.ds-btn-ghost:hover{background:var(--color-secondary); color:var(--color-foreground)}
.ds-btn-danger{background:var(--color-danger); color:var(--color-danger-foreground)}
.ds-btn-danger:hover{background:var(--color-danger-hover)}

.ds-icon-btn{
  display:inline-flex; align-items:center; justify-content:center;
  width:32px; height:32px; padding:0; border-radius:var(--radius-md);
  border:1px solid var(--color-line); background:var(--color-secondary);
  color:var(--color-muted-foreground); transition:background .12s,color .12s;
}
.ds-icon-btn:hover{color:var(--color-foreground)}

.ds-input{
  background:var(--color-background); color:var(--color-foreground);
  border:1px solid var(--color-input); border-radius:var(--radius-md);
  height:40px; padding:0 var(--sp-3); font-size:14px;
}
.ds-input::placeholder{color:var(--color-muted-foreground)}

/* ── 데이터 테이블 ── */
.ds-table{border-collapse:collapse; width:100%; font-size:14px}
.ds-table th{
  text-align:left; font-size:12px; font-weight:600;
  color:var(--color-muted-foreground);
  height:32px; padding:0 var(--sp-3); border-bottom:1px solid var(--color-line);
  white-space:nowrap;
}
.ds-table td{height:40px; padding:10px var(--sp-3); border-bottom:1px solid var(--color-line-subtle); vertical-align:top}
.ds-table tr:last-child td{border-bottom:0}
.ds-table td.ds-n{text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap}
.ds-tablewrap{overflow-x:auto}

/* ── KPI 타일 ── */
.ds-kpi-grid{display:grid; gap:var(--sp-3); grid-template-columns:repeat(auto-fit,minmax(170px,1fr))}
.ds-kpi{
  background:var(--color-card); border:1px solid var(--color-line);
  border-top:3px solid var(--color-line); border-radius:var(--radius-md);
  padding:var(--sp-3) var(--sp-4);
  display:flex; flex-direction:column; gap:2px;
}
.ds-kpi-label{
  font-size:12px; font-weight:600; color:var(--color-muted-foreground);
}
.ds-kpi-value{font-size:40px; font-weight:300; line-height:1.1; font-variant-numeric:tabular-nums; letter-spacing:-.02em}
.ds-kpi-value .ds-u{font-size:14px; font-weight:600; color:var(--color-muted-foreground); margin-left:2px}
.ds-kpi-note{font-size:12px; color:var(--color-muted-foreground); margin-top:var(--sp-1)}
.ds-kpi.ds-t-primary{border-top-color:var(--color-primary)}
.ds-kpi.ds-t-danger{border-top-color:var(--color-danger)}
.ds-kpi.ds-t-warning{border-top-color:var(--color-warning)}
.ds-kpi.ds-t-success{border-top-color:var(--color-success)}

/* ── 탭 / 세그먼트 ───────────────────────────────────────────
 * 이 프리미티브의 요점은 "역할 분리"다. 둘 다 탭처럼 보이지만 쓰는 자리가 다르다.
 *
 *   .ds-tabs / .ds-tab     밑줄 탭: 화면 최상위 뷰 전환.
 *                          "이 페이지에서 무엇을 보는가"(업장/추세, 개요/상세).
 *   .ds-seg  / .ds-seg-b   알약 트랙 세그먼트 컨트롤: 카드/모달 안 좁은 2~3지 토글.
 *                          "같은 것을 어떻게 보는가"(목록/카드, 일간/주간).
 *
 *   좁은 토글에 밑줄 탭을 쓰지 마라. 뷰 전환에 알약을 쓰지 마라. 그 순간 다시 갈라진다.
 *
 * 숫자 정본: 밑줄 = radar 의 .vtabs, 알약 = React segmented-control.tsx 의 pill variant.
 * 선택 상태 훅은 [aria-selected="true"] 와 .is-active 를 둘 다 받는다 (페이지마다 다름). */
.ds-tabs{display:flex; align-items:flex-end; flex-wrap:wrap; gap:0 var(--sp-5)}
/* 하단 레일이 필요한 페이지만 함께 붙인다: class="ds-tabs ds-tabs-rail" */
.ds-tabs-rail{border-bottom:1px solid var(--color-line)}
.ds-tabs-rail .ds-tab{margin-bottom:-1px}   /* 2px 밑줄이 레일 위로 올라타게 */
.ds-tab{
  /* 폭도 40 이다. 높이만 주면 "추세" 같은 두 글자 탭이 24x40 으로 남는다.
     타겟은 넓이지 높이가 아니다(규격 6-5). */
  position:relative; height:40px; min-width:40px; padding:0; border:0; background:none;
  font:inherit; font-size:14px; font-weight:700;
  color:var(--color-muted-foreground);
  display:inline-flex; align-items:baseline; gap:6px;
  transition:color .12s;
}
.ds-tab:hover{color:var(--color-foreground)}
.ds-tab[aria-selected="true"],
.ds-tab.is-active{color:var(--color-foreground)}
.ds-tab[aria-selected="true"]::after,
.ds-tab.is-active::after{
  content:""; position:absolute; left:0; right:0; bottom:0;
  height:2px; border-radius:2px; background:var(--color-primary);
}
/* 탭 이름 뒤 건수 꼬리표. 두 페이지가 각각 <em> 과 <span class="cnt"> 로 쓰고 있어 둘 다 받는다. */
.ds-tab em{font-style:normal; font-weight:600; font-size:12px; font-variant-numeric:tabular-nums}
.ds-tab .ds-cnt{font-weight:600; font-size:12px; color:var(--color-muted-foreground); font-variant-numeric:tabular-nums}

/* 알약 세그먼트: React segmented-control.tsx pill variant 그대로.
 *   트랙  = "h-9 inline-flex items-center bg-accent rounded-md p-0.5 font-medium select-none"
 *           (p-0.5 = 0.125rem = 2px. 트랙 안쪽 여백이 이 2px 뿐이다)
 *   활성  = "bg-card text-foreground shadow-sm dark:bg-line-strong"
 *           다크에선 그림자가 안 보이므로 트랙(slate-700)보다 한 단계 밝은
 *           line-strong 으로 띄운다. 그 파일이 문서화한 처리를 그대로 옮김.
 *   비활성= "text-muted-foreground hover:text-foreground" */
/* 안쪽 여백 2 를 걷었다. 그 2px 때문에 버튼 자체 높이가 40 이 아니라 36 이었고
   coarse 에서도 48 이 아니라 44 였다. 컨트롤 높이는 맞는데 누르는 것은 바닥 미달인
   상태다. 반경이 0 이라 활성 칸이 테두리에 붙는 것이 맞는 모양이기도 하다.
   소비자 중 처음으로 ccusage 가 이 부품을 쓰면서 드러났다(2026-09-18). */
.ds-seg{
  display:inline-flex; align-items:center; height:40px; padding:0;
  background:var(--color-accent); border-radius:var(--radius-md);
  font-size:14px; font-weight:600; user-select:none;
}
.ds-seg-sm{height:32px; font-size:12px}      /* React size="sm" (h-7 text-xs) */
.ds-seg-full{width:100%}
.ds-seg-full .ds-seg-b{flex:1}
.ds-seg-b{
  height:100%; padding:0 var(--sp-3); border:0; background:none; font:inherit;
  border-radius:var(--radius-md); color:var(--color-muted-foreground);
  display:inline-flex; align-items:center; justify-content:center; white-space:nowrap;
  transition:background .12s, color .12s;
}
.ds-seg-sm .ds-seg-b{padding:0 var(--sp-2)}
.ds-seg-b:hover{color:var(--color-foreground)}
/* 활성 조각은 트랙보다 한 단 떨어져 보인다. 그림자가 아니라 면의 단차로 만든다.
   라이트는 트랙(--color-accent #f4f4f4) 위에 --color-card(흰색),
   다크는 트랙(#262626) 위에 --color-border(#393939) 다.
   예전엔 다크에서 --color-line-strong 을 채움으로 썼는데 그 토큰은 선 색이다.
   규격이 그 값을 잉크(#f4f4f4)로 정하자 흰 바탕에 흰 글자가 되어 글자가 사라졌다.
   이름이 line 인 토큰을 채움으로 쓰면 값이 바뀌는 날 이렇게 된다. */
.ds-seg-b[aria-pressed="true"],
.ds-seg-b[aria-selected="true"],
.ds-seg-b.is-active{
  background:var(--color-card); color:var(--color-foreground);
}
.dark .ds-seg-b[aria-pressed="true"],
.dark .ds-seg-b[aria-selected="true"],
.dark .ds-seg-b.is-active{background:var(--color-border)}

.ds-section-head{display:flex; align-items:baseline; gap:var(--sp-3); margin:0 0 var(--sp-3)}
.ds-section-head h2{font-size:16px; font-weight:600}
.ds-section-head .ds-hint{font-size:12px; color:var(--color-muted-foreground)}

/* ── 포인터 (규격 3-6) ─────────────────────────────────────────
   주 입력장치가 손가락이면 누르는 것의 타겟이 48 이다. 시각 크기는 그대로 두고
   타겟만 키운다. any-pointer 를 쓰면 터치 달린 노트북이 전부 48 이 되어 데스크톱
   밀도가 무너진다. 그래서 pointer 하나만 본다.
   트랙패드 달린 아이패드가 fine 으로 잡혀 40 이 나오는 것은 감수한다. */
@media (pointer:coarse){
  .ds-icon-btn{width:48px; height:48px}
  .ds-btn{height:48px; min-width:48px}
  /* 손가락에서는 작은 버튼도 48 이다. 밀도 축과 포인터 축이 만나면 포인터가 이긴다 -
     "작게" 는 화면이 빽빽하라고 있는 것이고 손가락 크기는 화면 사정을 봐주지 않는다.
     그래서 coarse 에서 sm 과 기본이 같아진다. 그게 맞는 값이다. */
  .ds-btn-sm{height:48px; min-width:48px}
  /* 폭도 준다. 옵션이 아직 안 채워진 select 는 46px 로 서는데, 그건 데이터가 없는
     순간에도 손가락이 닿아야 하는 자리다. */
  .ds-input{height:48px; min-width:48px}
  .ds-table td, .ds-table th{padding-top:12px; padding-bottom:12px}
  /* 탭과 세그먼트도 누르는 것이다. 2026-09-17 에 이 블록을 처음 쓸 때 빼먹었고
     네 소비자를 Pixel 5 로 재니 .ds-tab 이 32 인 채로 잡혔다. 규칙을 적을 때가 아니라
     실제로 재 볼 때 드러났다. */
  /* 타겟은 두 변 다다. 높이만 48 로 올렸더니 "추세" 같은 짧은 탭이 24x48 로 남았다.
     폭도 48 을 준다. 탭이 조금 넓어지고 그게 맞는 값이다. */
  .ds-tab{height:48px; min-width:48px; justify-content:center}
  .ds-seg{height:48px}
  .ds-seg-sm{height:48px}
  .ds-seg-b{min-width:48px}
}

@media (hover:none){
  .ds-btn:hover, .ds-btn-secondary:hover, .ds-btn-outline:hover, .ds-btn-ghost:hover,
  .ds-icon-btn:hover, .ds-seg-b:hover{background:inherit; color:inherit; filter:none}
}

/* ── 폰 (규격 2-10 의 좁음, ~639) ──────────────────────────────
   입력 글자를 16 으로 올린다. iOS Safari 는 16 아래인 입력에 초점이 가면 화면을
   확대한다. 사람이 아무것도 안 했는데 레이아웃이 튄다. maximum-scale=1 로 막는
   방법은 손가락으로 키우는 것까지 막아서 안 쓴다(규격 6-7). */
@media (max-width:639px){
  .ds-input, input, select, textarea{font-size:16px}
  .ds-kpi-grid{grid-template-columns:minmax(0,1fr)}
  /* 규격 4 의 탭은 좁을 때 가로로 스크롤한다. 줄바꿈을 두면 탭 여섯 개가 두 줄로 접히고
     첫 줄 활성 탭의 밑줄이 둘째 줄 위에 떠서 무엇이 골라졌는지가 안 읽힌다.
     스크롤바는 안 숨긴다. 숨기면 더 있다는 것을 알 길이 없다. */
  .ds-tabs{flex-wrap:nowrap; overflow-x:auto; min-width:0}
  .ds-tab{flex:none}
  /* 규격 5-1 은 폰에서 표를 스크롤로 낸다. 래퍼에 overflow 만 주고 표에 최소폭을 안 주면
     표가 래퍼 폭에 맞춰 찌그러지고, 한국어는 음절 사이에서 줄이 바뀌니 넘치지도 않는다.
     "불만" 이 두 줄이 되고 넘침은 0 이라 모든 단언이 통과한다. max-content 면 칸이
     내용만큼 벌어지고 그때부터 래퍼가 실제로 스크롤한다. */
  .ds-tablewrap > .ds-table{min-width:max-content}
}
/* html{overflow-x:hidden} 은 BASE 가 갖는다. 여기 사본을 두면 같은 사실에 출처가
   둘이라 한쪽을 고치는 날 조용히 갈라진다(2026-09-19 diff 에서 발견). */
