/* =====================================================================
   InnoLink — 컴포넌트

   접두사 규칙
     reb-*  RE:B(reb_design.md) 유래 — 차트 카드 · KPI · 표 · 탭 · PIN.
            구조와 클래스명을 원문 그대로 유지하고, 색 토큰만 innored 값으로 채운다.
     il-*   InnoLink 고유 — 버튼 · 인풋 · 배지 · 툴바 · 빈 상태 등.

   값은 전부 tokens.css. 이 파일에 hex 를 직접 쓰지 않는다.
   ===================================================================== */

/* ── 리셋 · 기본 ───────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  font-weight: var(--fw-body);
  line-height: var(--lh-body);
  color: var(--ink);
  background: var(--bg);
  /* 한글 가독성 — 단어 중간에서 줄바꿈되지 않게 한다 */
  word-break: keep-all;
  overflow-wrap: break-word;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, p, figure { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* 링크처럼 보이는 버튼 — 이동이 아니라 그 자리에서 무언가를 여는 동작.
   생김새는 <a> 와 같게 두되 요소는 <button> 이어야 한다.
   href 없는 <a> 는 키보드 포커스도 Enter 도 받지 못한다 (WCAG 2.1.1) */
.il-linklike {
  padding: 0; border: 0; background: none;
  font: inherit; color: var(--accent); cursor: pointer;
}
.il-linklike:hover { text-decoration: underline; }
img, svg { max-width: 100%; }

:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }
/* 잉크 바탕 위에서는 검은 링이 안 보인다 — 흰 링으로 뒤집는다 */
.il-sidebar :focus-visible, .il-lc--new :focus-visible { box-shadow: var(--focus-ring-on-ink); }

/* 숫자는 자릿수가 흔들리지 않게 고정폭으로 */
.il-num, .reb-kpi-value, .reb-table-cell--num, .il-metric { font-variant-numeric: tabular-nums; }

/* ── 타이포 유틸 ───────────────────────────────────────────────────── */
.il-display { font-size: var(--fs-display); font-weight: var(--fw-display); letter-spacing: var(--ls-display); line-height: var(--lh-tight); }
.il-title   { font-size: var(--fs-title);   font-weight: var(--fw-title);   letter-spacing: var(--ls-title);   line-height: var(--lh-tight); }
.il-heading { font-size: var(--fs-heading); font-weight: var(--fw-heading); letter-spacing: var(--ls-heading); line-height: var(--lh-tight); }
.il-lead    { font-size: var(--fs-lead);    font-weight: var(--fw-lead); }
.il-caption { font-size: var(--fs-caption); font-weight: var(--fw-caption); color: var(--text-secondary); }
.il-micro   { font-size: var(--fs-micro);   font-weight: var(--fw-micro); letter-spacing: var(--ls-micro);
              text-transform: uppercase; color: var(--text-muted); }

/* "INNORED" 워드마크 — 브랜드 레드를 쓸 수 있는 유일한 자리 */
.il-wordmark-innored { color: var(--brand-innored); font-weight: 800; }

/* ── 버튼 — innored_basic 5 variant · 2 size ───────────────────────── */
.il-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  height: 36px; padding: 0 var(--sp-4);
  border: 1px solid transparent; border-radius: var(--radius-sm);
  font-size: var(--fs-body); font-weight: 620; white-space: nowrap;
  cursor: pointer; transition: background var(--t-fast), border-color var(--t-fast);
}
.il-btn--sm { height: 28px; padding: 0 var(--sp-3); font-size: var(--fs-caption); }
.il-btn[disabled] { opacity: .45; cursor: not-allowed; }

.il-btn-primary   { background: var(--ink); color: var(--text-inverse); }
.il-btn-primary:hover:not([disabled])   { background: var(--ink-hover); }
.il-btn-secondary { background: var(--bg-elevated); color: var(--ink); border-color: var(--border-strong); }
.il-btn-secondary:hover:not([disabled]) { background: var(--bg-subtle); }
.il-btn-ghost     { background: transparent; color: var(--text-secondary); }
.il-btn-ghost:hover:not([disabled])     { background: var(--bg-subtle); color: var(--ink); }
.il-btn-accent    { background: var(--accent); color: var(--text-inverse); }
.il-btn-accent:hover:not([disabled])    { background: var(--accent-hover); }
.il-btn-danger    { background: var(--bg-elevated); color: var(--danger); border-color: var(--danger); }
.il-btn-danger:hover:not([disabled])    { background: var(--danger-tint); }

/* 툴바의 보조 동작에 ghost 를 쓰지 않는다 — 배경도 테두리도 없어 그냥 글자로 읽힌다.
   ghost 는 모달의 '취소'에서만 (코딩 가이드 §11) */

/* ── 인풋 ──────────────────────────────────────────────────────────── */
.il-field { display: flex; flex-direction: column; gap: var(--sp-2); }

/* 라벨 줄 — 라벨 + i 아이콘, 오른쪽 끝에 보조 동작(만들기·추가).
   버튼이 있는 칸만 줄이 높아지면 나란히 놓인 입력들이 어긋난다.
   그래서 높이를 라벨이 아니라 이 줄이 고정한다 (il-btn--sm = 28px).
   gap 을 주지 않는 이유 — .il-infotip__btn 이 이미 margin-left:4px 를
   갖고 있어서, 여기서 또 주면 i 아이콘만 라벨에서 멀어진다. */
.il-field__head { display: flex; align-items: center; min-height: 28px; }
.il-field__head > .il-field__action { margin-left: auto; }
/* 잠긴 <a> 버튼 (2026-08-26). <a> 에는 disabled 가 없어 aria-disabled 로 알리고
   href 를 뗀다. 그러면 Tab 에도 안 걸리고 눌러도 안 열린다 —
   `잠긴 것처럼 보이는데 눌리면 열리는` 상태가 가장 나쁘다.
   pointer-events 로만 막지 않는다. 그것은 마우스만 막는다 */
.il-btn.is-disabled {
  opacity: .45; cursor: not-allowed;
}

.il-label { font-size: var(--fs-caption); font-weight: 620; color: var(--text-secondary); }
.il-input, .il-select, .il-textarea {
  width: 100%; height: 36px; padding: 0 var(--sp-3);
  background: var(--bg-elevated); color: var(--ink);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  transition: border-color var(--t-fast);
}
.il-textarea { height: auto; min-height: 84px; padding: var(--sp-3); line-height: var(--lh-body); resize: vertical; }
.il-input::placeholder, .il-textarea::placeholder { color: var(--text-placeholder); }

/* 고르는 칸의 화살표 — 브라우저 기본을 끄고 우리 것을 그린다 (2026-09-04).
   기본 화살표는 OS 마다 다르고(리눅스 크롬은 두툼한 삼각형이다) 이 화면의
   아이콘 규칙과 어긋난다 — 아이콘은 stroke 만, 채우기 없음.
   색은 --text-muted(#6F6F6B)를 그대로 박았다. data URI 안에서는 var() 가
   안 풀린다 — 토큰이 바뀌면 여기도 같이 바꾼다.
   ★ 팝오버의 `.il-opt__in--sel` 이 같은 그림을 쓴다 (app.css). 둘이
     갈라지면 같은 자리에서 화살표가 두 모양이 된다 */
.il-select {
  cursor: pointer;
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  /* 화살표가 앉을 자리를 글자 쪽에서 비워 준다 */
  padding-right: var(--sp-8);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%236F6F6B' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--sp-3) center;
  background-size: 15px 15px;
}
/* 구형 Edge/IE 는 appearance 를 안 듣고 제 화살표를 하나 더 그린다 */
.il-select::-ms-expand { display: none; }
/* 포커스가 오면 화살표도 같이 또렷해진다 */
.il-select:focus, .il-select:focus-visible {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23111111' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
}
/* 글자 칸은 링을 겹치지 않는다 (2026-08-25). 테두리 한 줄이 옅은 회색에서
   잉크로 바뀌는 것으로 말한다 — 둘레 전체가 바뀌므로 신호는 충분하고,
   이미 테두리가 있는 칸에 링을 더하면 무슨 짓을 해도 두 줄로 보인다 */
.il-input:focus, .il-select:focus, .il-textarea:focus,
.il-input:focus-visible, .il-select:focus-visible, .il-textarea:focus-visible {
  border-color: var(--ink); box-shadow: none;
}
/* 표 칸 안에 서는 select (2026-09-03 — 링크 관리의 상태 칸).
   36px 은 행보다 커서 줄 높이를 밀어낸다. 버튼의 --sm 과 같은 28px 로 맞춘다 —
   같은 줄에 `통계` 버튼이 서므로 둘의 높이가 같아야 한다 */
.il-select--sm {
  height: 28px; padding: 0 var(--sp-2);
  font-size: var(--fs-caption);
}

/* ★ 손가락으로 쓰는 기기에서는 글자 칸이 **16px** 이다 (2026-09-11).
     iOS 사파리는 16px 보다 작은 칸에 포커스가 가면 **화면을 제멋대로
     확대한다.** 되돌려 주지도 않아서, 한 칸 고치고 나면 목록이 확대된 채로
     남고 그 뒤로는 가로로도 밀어야 한다.

     ── 왜 확대를 막지 않고 글자를 키우나 ──────────────────────────
     `maximum-scale=1` 을 뷰포트에 적으면 확대가 막히지만, 그것은 **사람이
     손가락으로 키우는 것까지** 막는다 (WCAG 1.4.4 — 200% 확대). 눈이 불편한
     사람에게서 확대를 빼앗는 대신, 우리가 16px 로 적는다.

     ── 왜 화면 전체에 거나 ────────────────────────────────────────
     같은 함정이 칸마다 있다. 목록의 팝오버에서 처음 드러났을 뿐이고,
     모달 · 필터 · 검색칸도 전부 14px 이었다. 한 자리만 고치면 다음 자리에서
     같은 신고가 다시 온다.

     ★ `pointer: coarse` 다 — 화면 폭이 아니라 **입력 수단**으로 가른다.
       좁은 창으로 줄인 데스크탑에서는 확대가 일어나지 않으므로 키울 이유가
       없고, 넓은 태블릿에서는 폭과 무관하게 일어난다.

     ── 클래스가 아니라 **요소**로 건다 (2026-09-11 2차) ────────────────
   처음에는 `.il-input · .il-select · .il-textarea` 만 적었다. 그런데 이 저장소의
   글자 칸이 그 셋만이 아니다 — 그룹 링크 꾸미기의 블록 제목 · 주소(.il-be__inp),
   그 안의 찾기 칸(.il-be__pickq), 주소 칸(.il-handle__input) 이 제 클래스로
   따로 크기를 정하고 있었다. 그래서 같은 신고가 화면을 옮겨 가며 또 왔다.

   이제 **글자를 받는 칸이면 무엇이든** 16px 이다.
   체크박스 · 라디오 · 단추는 글자를 받지 않으므로 뺀다.

   ★ 셋을 `:is()` 로 묶는 까닭은 **특정도**다. 따로 늘어놓으면 각자 (0,0,1)
     이라, 제 크기를 클래스로 적어 둔 칸(.il-select--sm · .il-opt__in 처럼
     (0,1,0))에 **진다** — 실제로 select 하나가 14px 로 남아 있었다.
     `:is(...)` 에 `:not([…])` 넷이 붙어 (0,4,1) 이 되므로 셋 다 이긴다.

   ★ 이 규칙은 **새 칸에도 저절로 걸린다.** 다음에 칸을 하나 더 만드는 사람이
     이 사연을 몰라도 된다 — 그것이 클래스로 적지 않은 이유다. */
@media (pointer: coarse) {
  :is(input, textarea, select):not([type="checkbox"]):not([type="radio"]):not([type="button"]):not([type="submit"]) {
    font-size: 16px;
  }
}

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

/* 단축 URL 조립 — [도메인 ▾] / [슬러그] */
.il-slug-group { display: flex; align-items: center; gap: var(--sp-2); }
.il-slug-group .il-select { width: auto; min-width: 120px; }
.il-slug-sep { color: var(--text-muted); }

/* ── 배지 — innored_basic 6 상태 ────────────────────────────────────
   상태 색은 배지 · 스트라이프 · 배경 tint 에만. 옆의 설명 문장은 항상 ink. */
.il-badge {
  display: inline-flex; align-items: center; gap: 4px;
  height: 20px; padding: 0 var(--sp-2);
  border-radius: var(--radius-full);
  font-size: var(--fs-micro); font-weight: 700; letter-spacing: .02em;
  background: var(--bg-subtle); color: var(--text-secondary);
}
.il-badge--on       { background: var(--accent-tint);  color: var(--accent-on-tint); }
.il-badge--ing      { background: var(--accent-tint);  color: var(--accent-on-tint); }
.il-badge--pass     { background: var(--success-tint); color: var(--success-on-tint); }
.il-badge--warn     { background: var(--warn-tint);    color: var(--warn-on-tint); }
.il-badge--fail     { background: var(--danger-tint);  color: var(--danger-on-tint); }
.il-badge--none     { background: var(--bg-subtle); color: var(--text-muted); }

/* 링크 상태 (feature-spec §6-4) */
.il-badge--active   { background: var(--success-tint); color: var(--success-on-tint); }
.il-badge--paused   { background: var(--bg-subtle); color: var(--text-muted); }
.il-badge--archived { background: var(--bg-subtle); color: var(--text-muted); }
.il-badge--expired  { background: var(--warn-tint);    color: var(--warn-on-tint); }

/* ── 카드 ──────────────────────────────────────────────────────────── */
.il-card {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--sp-5);
}
.il-card__head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); margin-bottom: var(--sp-4); }

/* ── 툴바 · 컨텍스트 액션 바 ────────────────────────────────────────
   평소에는 감춰 두고 행을 선택하면 나타난다 (bitly · vo.la 패턴) */
.il-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.il-toolbar__spacer { flex: 1 1 auto; }

/* ── 필터 바 — 클라이언트 · 프로젝트 · 기간 ──────────────────────────
   대시보드는 "무엇을 보고 있는가"가 화면 안에 있어야 한다.
   기준을 사이드바에 두면 본문과 눈이 멀어져 선택 상태를 놓친다. */
.il-filterbar {
  display: flex; flex-direction: column; gap: var(--sp-3);
  padding: var(--sp-4);
  background: var(--bg-elevated);
  border: 1px solid var(--border); border-radius: var(--radius-md);
}
.il-filterbar__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); }
/* 라벨과 컨트롤은 한 덩어리 — 줄바꿈될 때 라벨만 앞줄에 남으면 안 된다 */
.il-filterbar__field { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.il-filterbar__field .il-select { width: auto; min-width: 180px; }
.il-filterbar__label {
  flex: none; font-size: var(--fs-caption); font-weight: 620; color: var(--text-secondary);
}
.il-filterbar__spacer { flex: 1 1 auto; }
.il-filterbar__dash { flex: none; color: var(--text-muted); }
.il-filterbar__sum {
  padding-top: var(--sp-2); border-top: 1px solid var(--border);
  font-size: var(--fs-caption); color: var(--text-muted);
}
.il-filterbar__sum strong { color: var(--ink); font-weight: 700; }
/* 날짜 인풋 — 브라우저 기본 달력을 쓰지 않는다.
   표기(2026-07-21 (수) 등)가 OS 로케일에 끌려다니고, 달력 UI 도 손댈 수 없다.
   달력은 assets/vendor/ 의 라이브러리를 [data-datepicker] 에 붙인다 (vendor/README.md) */
.il-input--date { font-variant-numeric: tabular-nums; letter-spacing: .01em; }
/* 폭을 여기서 준다. .il-input 의 기본이 width:100% 라, 안 주면 날짜 칸이
   줄을 통째로 먹고 필터가 세 줄로 깨진다 — 링크 관리(.il-toolbar)에서 그랬다.
   여덟 글자짜리 칸이라 늘어날 이유가 없다 */
.il-filterbar .il-input--date,
.il-toolbar .il-input--date { width: 128px; }

/* 세그먼티드 — 배타 선택. 탭이 아니므로 role="tablist" 를 쓰지 않는다.
   상태는 aria-pressed 로 말한다 (색만으로 말하지 않는다)

   ★ inline-flex 다 (2026-09-04). flex 로 두면 **블록 자리에 홀로 놓였을 때**
     가로를 다 먹어서, 단추 둘 옆으로 회색 띠가 화면 끝까지 늘어난다.
     관리자 로그의 `감사 로그 · 오류` 가 그렇게 보였다.
     여태 안 드러난 것은 늘 필터 바(flex) 안에 있었기 때문이다 —
     flex 아이템은 inline-flex 를 flex 로 되돌리므로(블록화) 그쪽은 그대로다 */
.il-segment {
  display: inline-flex; flex-wrap: wrap; gap: 2px; padding: 2px;
  background: var(--bg-subtle); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.il-segment__btn {
  min-height: 28px; padding: 0 var(--sp-3);
  border: 0; border-radius: calc(var(--radius-sm) - 2px);
  background: transparent; color: var(--text-secondary);
  font: inherit; font-size: var(--fs-caption); font-weight: 620;
  white-space: nowrap; cursor: pointer;
}
.il-segment__btn:hover { background: var(--bg-elevated); color: var(--ink); }
.il-segment__btn[aria-pressed="true"] { background: var(--ink); color: var(--text-inverse); }

/* 텍스트 버튼 — 되돌리는 동작에만. 타깃 24px 확보 (WCAG 2.5.8) */
.il-textbtn {
  min-height: 24px; padding: 0 4px;
  border: 0; background: none; color: var(--text-secondary);
  font: inherit; font-size: var(--fs-caption);
  text-decoration: underline; text-underline-offset: 3px; cursor: pointer;
}
.il-textbtn:hover { color: var(--ink); }
.il-bulkbar {
  display: none; align-items: center; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  background: var(--bg-subtle); border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.il-bulkbar.is-open { display: flex; }
.il-bulkbar__count { font-weight: 700; }

/* ── 빈 상태 — 상태 + 다음에 할 일 ─────────────────────────────────── */
.il-empty { padding: var(--sp-12) var(--sp-6); text-align: center; color: var(--text-secondary); }
.il-empty__icon { color: var(--text-placeholder); margin-bottom: var(--sp-3); }
.il-empty__title { font-size: var(--fs-lead); font-weight: 700; color: var(--ink); margin-bottom: var(--sp-2); }
.il-empty__desc { margin-bottom: var(--sp-5); }

/* ── 토스트 — success / error / info 3종만 ─────────────────────────── */
.il-toast {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  background: var(--ink); color: var(--on-ink);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-lg);
}
.il-toast__mark { width: 6px; height: 6px; border-radius: var(--radius-full); background: var(--on-ink); }
.il-toast--success .il-toast__mark { background: var(--on-ink-success); }
.il-toast--error   .il-toast__mark { background: var(--on-ink-error); }

/* ── 모달 ──────────────────────────────────────────────────────────── */
.il-modal { position: fixed; inset: 0; z-index: 50; display: none; }

/* 열리면 가운데 정렬 판이 된다 (2026-09-01).
   앞서는 display:block 에 패널이 `margin: var(--sp-10) auto` 였다 —
   **가로만** 가운데였고 세로는 늘 천장에서 40px 이었다. 세로로 긴 화면일수록
   창이 위에 붙고 아래가 통째로 비어, 화면 한복판을 보고 있던 눈이
   위로 한 번 튀어야 했다.
   두 축을 같은 규칙 하나로 잡는다 — inset:0 판 안에서 place-items:center.

   여백은 패널의 margin 이 아니라 판의 padding 이 든다.
   판이 여백을 들고 있어야 아래 max-height:100% 가 "여백을 뺀 자리" 로
   맞아떨어진다. margin 으로 두면 높이 계산에서 그만큼이 빠지지 않아
   긴 모달이 화면 밖으로 넘친다. */
.il-modal.is-open { display: grid; place-items: center; padding: var(--sp-10) var(--sp-4); }

/* 덮개는 판의 padding box 를 덮는다 — 여백 밖까지, 사이드바까지 전부.
   모달이 떠 있는 동안 사이드바는 누를 것이 아니라 배경이다 */
.il-modal__scrim { position: absolute; inset: 0; background: var(--overlay); }
.il-modal__panel {
  position: relative; width: 100%; max-width: 640px;
  /* 100vh 를 직접 빼지 않는다 — 모바일에서 주소창이 접혔다 폈다 하면
     100vh 가 실제 보이는 높이보다 커져 창 밑동이 잘린다.
     100% 는 위 판의 안쪽 높이(= 화면 − 여백)라 그 함정이 없다 */
  max-height: 100%; overflow: auto;
  background: var(--bg-elevated); border-radius: var(--radius-xl); box-shadow: var(--shadow-xl);
}
/* ── 누른 자리에 서는 모달 (2026-09-08) ────────────────────────────
   공유처럼 **어디서 눌렀는지가 뜻을 갖는** 판이다. 화면 한복판으로 튀면
   눈이 거기까지 갔다가 다시 돌아와야 한다 — 누른 단추 바로 밑에 세운다.

   덮개 · 닫기 · Esc · 포커스는 모달의 것을 그대로 쓴다. 달라지는 것은
   자리뿐이라, 부품을 새로 만들지 않고 자리만 갈아 끼운다.

   ★ 덮개는 투명하다. 바깥을 눌러 닫는 길은 남기고(그 자리가 곧 덮개다)
     화면을 어둡게 덮지는 않는다 — 이 판은 화면을 가로막는 것이 아니라
     그 자리에서 잠깐 뜨는 것이다.
   ★ 자리(left · top)는 JS 가 넣는다 (pageedit.js 의 placeShare) */
.il-modal--pop.is-open { display: block; padding: 0; }
.il-modal--pop .il-modal__scrim { background: transparent; }
.il-modal--pop .il-modal__panel {
  /* 280px — 안에 든 것이 두 갈래뿐이라 340 은 넓었다 (2026-09-08).
     좁히면 누른 단추와의 거리도 같이 가까워진다: 판이 넓을수록 왼쪽
     끝이 멀어져 `이 단추가 연 것` 이라는 것이 약해진다 */
  position: fixed; width: 280px; max-width: calc(100vw - 24px);
  max-height: calc(100vh - 24px);
}

/* 링크 등록 폼처럼 3열 격자가 들어가는 모달. 640px 안에서는 칸이 뭉개진다 */
/* 저장하는 동안 창을 덮는다 (assets/js/ui.js 의 IL.busy).

   ★ 왜 창 위인가 (2026-09-03) — 저장이 곧바로 안 끝나는 자리가 있다.
     그때 아무 표시가 없으면 사람은 [생성] 을 한 번 더 누른다.
     버튼만 잠그면 왜 안 눌리는지가 안 보이고, 화면 밖에 토스트를 띄우면
     창을 보고 있던 눈이 그것을 놓친다. 덮개는 보고 있던 자리에 뜬다.

   panel 이 position:relative 이므로 inset:0 은 **구를 수 있는 전체 높이**를
   덮는다 — 내용이 길어 창이 굴러도 보이는 곳은 늘 덮여 있다.
   흰 반투명이다: 뒤가 비쳐 무엇을 저장하는 중인지 그대로 읽힌다 */
.il-modal__busy {
  position: absolute; inset: 0; z-index: 3;
  display: flex; align-items: center; justify-content: center;
  /* 패널 배경(--bg-elevated)이 흰색이라 같은 값에 알파만 준다 */
  background: rgba(255, 255, 255, .72);
  border-radius: inherit;
}
.il-modal__spin {
  width: 26px; height: 26px;
  border: 2.5px solid var(--border-strong); border-top-color: var(--ink);
  border-radius: 50%;
  animation: il-spin .6s linear infinite;
}
/* 움직임을 줄여 달라고 한 사람에게는 돌리지 않는다 — 덮개가 이미 말하고 있다 */
@media (prefers-reduced-motion: reduce) {
  .il-modal__spin { animation: none; }
}

.il-modal--wide .il-modal__panel { max-width: 1000px; }
/* 되묻기처럼 한 줄만 있는 모달. 640px 을 그대로 쓰면 글 한 줄이 허허벌판에 뜬다 */
.il-modal--narrow .il-modal__panel { max-width: 440px; }
/* ★ [hidden] 을 되살린다 (2026-09-03).

   브라우저 기본 규칙 [hidden]{display:none} 은 **저자 스타일시트에 진다.**
   특정도 문제가 아니라 출처(origin) 문제라, 어떤 셀렉터로 display 를 주든
   그 순간 hidden 이 조용히 안 먹는다.

   이 저장소는 그때마다 `.il-어쩌고[hidden] { display: none }` 한 줄씩
   덧대 왔다 — app.css 에만 여섯 군데다. 새로 display 를 주는 자리마다
   기억해야 하고, 잊으면 JS 는 감췄다고 믿는데 화면에는 그대로 남는다.
   여기서 한 번에 막는다.

   !important 를 쓴다. hidden 은 `보이지 않는다` 는 뜻이고 그것을 뒤집을
   이유가 있는 자리는 없다 — 있다면 hidden 이 아니라 다른 표시를 써야 한다. */
[hidden] { display: none !important; }

.il-modal__head, .il-modal__foot { padding: var(--sp-5); display: flex; align-items: center; gap: var(--sp-3); }
.il-modal__head { border-bottom: 1px solid var(--border); }
.il-modal__foot { border-top: 1px solid var(--border); justify-content: flex-end; }
.il-modal__body { padding: var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-4); }

/* 본문이 없는 알림 (IL.alert). 머리와 발만 남는데, 둘 다 구분선을 들고 있어
   그대로 두면 선 두 개가 맞붙어 빈 칸이 하나 있는 것처럼 보인다.
   할 말이 한 문장뿐이면 상자도 한 덩어리여야 한다 */
.il-modal--plain .il-modal__head { border-bottom: 0; padding-bottom: 0; }
.il-modal--plain .il-modal__foot { border-top: 0; padding-top: var(--sp-4); }

/* ── 아코디언 — 링크 등록 폼 3단 (bitly 패턴) ───────────────────────── */
.il-accordion { border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--bg-elevated); }
.il-accordion + .il-accordion { margin-top: var(--sp-3); }
.il-accordion__head {
  width: 100%; display: flex; align-items: center; justify-content: space-between;
  padding: var(--sp-4) var(--sp-5); background: none; border: 0; cursor: pointer; text-align: left;
  font-size: var(--fs-lead); font-weight: 700;
}
.il-accordion__body { padding: 0 var(--sp-5) var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-4); }
.il-accordion:not(.is-open) .il-accordion__body { display: none; }

/* ── 토글 스위치 ───────────────────────────────────────────────────── */
.il-toggle {
  position: relative; width: 38px; height: 22px; padding: 0;
  background: var(--border-strong); border: 0; border-radius: var(--radius-full);
  cursor: pointer; transition: background var(--t-fast);
}
.il-toggle[aria-checked="true"] { background: var(--accent); }
.il-toggle__knob {
  position: absolute; top: 3px; left: 3px; width: 16px; height: 16px;
  background: var(--text-inverse); border-radius: var(--radius-full); transition: transform var(--t-fast);
}
.il-toggle[aria-checked="true"] .il-toggle__knob { transform: translateX(16px); }

/* =====================================================================
   RE:B 유래 컴포넌트 — reb_design.md 구조 유지, 색만 innored
   ===================================================================== */

/* ── KPI 카드 ──────────────────────────────────────────────────────── */
.reb-kpi {
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: var(--sp-5);
}
.reb-kpi-head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: var(--sp-2) var(--sp-4); margin-bottom: var(--sp-3); }
/* 카드 제목. 안쪽 지표 라벨(.il-kpi-group__label)보다 커야 위계가 선다 */
.reb-kpi-label { font-size: var(--fs-lead); font-weight: 700; color: var(--ink); letter-spacing: -.01em; }
.reb-kpi-icon { color: var(--text-placeholder); }
.reb-kpi-value { font-size: var(--fs-title); font-weight: var(--fw-title); letter-spacing: var(--ls-title); line-height: 1.2; }
.reb-kpi-unit { font-size: var(--fs-body); font-weight: 620; color: var(--text-secondary); }
.reb-kpi-foot { margin-top: var(--sp-2); font-size: var(--fs-caption); color: var(--text-secondary); }
/* 증감은 색만으로 말하지 않는다 — 화살표 기호를 항상 함께 둔다 */
.reb-kpi-delta { font-weight: 700; }
.reb-kpi-delta--up   { color: var(--success); }
.reb-kpi-delta--down { color: var(--danger); }
.reb-kpi-delta--flat { color: var(--text-muted); }
.reb-kpi--primary { border-color: var(--border-strong); }
.reb-kpi--danger  { border-color: var(--danger-tint); }
.reb-kpi--sm { padding: var(--sp-4); }
.reb-kpi--sm .reb-kpi-value { font-size: var(--fs-heading); }

/* KPI 행 — 개수에 맞는 변형을 쓴다. 4열 그리드에 2장을 얹지 않는다 (가이드 §11) */
.il-kpi-row { display: grid; gap: var(--sp-4); }
.il-kpi-row-1 { grid-template-columns: minmax(0, 1fr); }
.il-kpi-row-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* 지표 개수가 다른 두 카드 — 넓은 쪽에 폭을 더 준다 */
.il-kpi-row-2a { grid-template-columns: 2fr 1fr; }
.il-kpi-row-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* 지표 3 : 1 : 2 개인 세 카드 */
.il-kpi-row-3a { grid-template-columns: 3fr 1.1fr 2fr; }
.il-kpi-row-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.il-kpi-row-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }

/* ── 차트 카드 ─────────────────────────────────────────────────────── */
.reb-chart-card {
  display: flex; flex-direction: column;   /* 차트가 남은 높이를 쓰게 한다 */
  background: var(--chart-surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: var(--sp-5);
}
.reb-chart-card-title { font-size: var(--fs-lead); font-weight: 700; }
/* 제목 바로 아래 차트가 오는 카드(부제·범례가 없는 경우) — 붙어 보이지 않게 띄운다 */
.reb-chart-card-title + .reb-chart { margin-top: var(--sp-4); }
/* 요일 × 시간 — 고르개가 선 머리 줄과 그림 사이에 한 칸 더 (2026-09-08).
   와이어프레임에서는 제목 바로 위 규칙(sp-4)이 걸렸다. 화면에 [전체 ·
   모바일 · PC] 가 붙으면서 제목을 머리 줄로 감쌌고, 그 순간 인접 형제가
   아니게 되어 머리 줄의 margin-bottom(sp-2)만 남았다 — 8px 이다.
   게다가 그 8px 은 글자를 재던 값이다. 글자 밑에는 눈에 안 보이는
   여백(line-height)이 딸려 있어 8px 이 8px 보다 넓게 보였는데, 테두리가
   있는 단추는 그것이 없어 칠해진 면이 곧바로 히트맵 첫 줄에 닿는다.

   머리 줄의 margin-bottom 을 키우지 않는다 — 그 값은 부제 · 범례가 이어지는
   카드에도 똑같이 걸려서, 붙어 있어야 할 줄까지 같이 벌어진다.
   :has 도 쓰지 않는다. 그림 쪽에 직접 걸면 히트맵이 있는 카드에서만
   알아서 걸린다 (linkstat.js · projstat.js 도 같은 열쇠로 이 판을 찾는다).
   합해서 sp-2 + sp-3 = 20px — 와이어프레임의 글자 제목이 내던 간격이다 */
.reb-chart-card-head + .reb-chart[data-chart="heatmap"] { margin-top: var(--sp-3); }
/* 제목과 차트 유형 전환을 한 줄에. 좁아지면 전환이 아래로 내려간다 */
.reb-chart-card-head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--sp-3); margin-bottom: var(--sp-2);
}
/* 고르는 것이 둘 이상인 머리 줄 (2026-09-07).
   space-between 이라 셋을 그냥 두면 가운데 것이 허공에 뜬다 — 무엇에 딸린
   단추인지가 자리로 안 읽힌다. `이 그림을 어떻게 볼까` 를 고르는 것끼리
   한 덩어리로 묶어 오른쪽에 세운다.
   좁아지면 둘이 아래로 함께 접힌다 (wrap) */
.reb-chart-card-tools {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2);
}
.il-segment__btn svg { flex: none; margin-right: 4px; vertical-align: -2px; }
/* ★ justify-content 를 같이 준다 (2026-09-11).
     이 줄이 단추를 flex 상자로 바꾼다(아이콘과 글자를 나란히 세우려고).
     그러면서 `text-align: center` 가 **아무 일도 안 하게** 됐다 — 가운데로
     모으는 것은 flex 쪽 속성이라, 안 주면 내용이 왼쪽에 붙는다.
     넓은 화면에서는 단추가 제 글자만큼만 넓어서 티가 안 났다. 좁은 화면은
     다르다: 거기서는 단추가 칸을 꽉 채우도록 늘어나므로(아래 520px 규칙),
     늘어난 만큼 글자가 왼쪽으로 쏠려 **가운데가 텅 빈 것처럼** 보였다.
     기간 프리셋을 격자로 세우면서 드러났고, 같은 일이 기기 고르개 ·
     분석 기준 · 로그 종류에서도 일어나고 있었다 */
.il-segment__btn { display: inline-flex; align-items: center; justify-content: center; }
/* 일자별 접속 — 링크 | QR 2단. QR 이 없는 선택이면 is-split 이 빠져 1단이 된다.
   링크 쪽이 막대 2개라 폭을 더 준다 */
/* 도넛 + 수평막대 — 도넛은 자기 크기만 쓰므로 칸을 좁게 준다 */
.il-grid-donut { grid-template-columns: 300px minmax(0, 1fr); }
/* 나란히 놓인 차트는 높이를 맞춘다. 하나는 도넛(고정 크기), 하나는 막대(행이 늘어남)라
   그냥 두면 서로 다른 높이로 끝난다. 막대는 이 높이를 행 수로 나눠 채운다 */
/* 나란히 놓인 두 차트를 가르는 선 — 다른 축의 다른 값이라는 신호 */
.il-split-col { border-left: 1px solid var(--border); padding-left: var(--sp-5); }
.il-chart-h { height: 220px; display: flex; align-items: center; }
.il-chart-h > svg { margin-inline: auto; }
.il-chartrow { display: grid; gap: var(--sp-4); grid-template-columns: minmax(0, 1fr); }
.il-chartrow.is-split { grid-template-columns: 2fr 1fr; }
.reb-chart-card-subtitle { margin-top: 2px; font-size: var(--fs-caption); color: var(--text-muted); }
.reb-chart-card-legend { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); margin: var(--sp-3) 0; }
.reb-legend-item { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-caption); color: var(--text-secondary); }
.reb-legend-dot { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.reb-chart { position: relative; width: 100%; }
/* 카드 안에서 차트가 남은 높이를 다 쓴다 — 옆 카드와 높이가 맞춰질 때
   아래쪽이 빈 채로 남지 않게 한다 */
.reb-chart-card > .reb-chart { flex: 1 1 auto; min-height: 0; }
/* width:100% 로 늘리면 viewBox 가 확대되어 축 라벨까지 같이 커진다.
   chart.js 가 컨테이너 실제 폭으로 viewBox 를 잡으므로 여기서는 늘리지 않는다. */
.reb-chart svg { display: block; max-width: 100%; height: auto; }

/* 값이 없을 때 차트 자리에 놓는 한 줄 (chart.js 의 blank()).
   높이를 주는 이유 — 글자 한 줄만 두면 카드가 납작해져서, 기간을 바꿔
   값이 생기는 순간 아래 내용이 통째로 밀린다. 자리를 미리 잡아 둔다 */
.il-chart-blank {
  display: flex; align-items: center; justify-content: center;
  min-height: 120px; padding: var(--sp-6) var(--sp-4);
  font-size: var(--fs-caption); color: var(--text-muted);
  text-align: center;
  /* ★ 자리를 다 차지해야 가운데로 간다 (2026-09-07).
     여기 안쪽은 원래도 가운데 정렬이었는데, 담는 상자가 flex 인 자리에서는
     (.il-chart-h — 분석 탭의 다섯 칸이 그렇다) 이 <p> 가 **flex 아이템**이라
     제 글자만큼만 넓어져 왼쪽에 붙어 섰다. 안에서 가운데를 잡아 봐야 그
     좁은 상자 안에서의 가운데다.
     block 인 자리(카드 안 차트)에서는 이미 폭을 다 쓰므로 이 줄이 아무것도
     바꾸지 않는다 — 한 줄로 두 자리가 같아진다 */
  flex: 1 1 auto;
}
.reb-chart-card > .reb-chart > .il-chart-blank { min-height: 200px; }

/* 불러오는 중 (2026-09-07). `없다` 와 같은 자리에 서지만 다른 말이다 —
   빈 자리와 높이를 맞춰서, 차트가 도착해도 아래가 안 밀린다 */
.il-chart-load { gap: var(--sp-2); }
.il-chart-load__spin {
  width: 14px; height: 14px; flex: none;
  border: 2px solid var(--border-strong); border-top-color: var(--ink);
  border-radius: 50%;
  animation: il-spin .6s linear infinite;
}
/* 움직임을 줄여 달라고 한 사람에게는 돌리지 않는다 — 글자가 이미 말하고 있다 */
@media (prefers-reduced-motion: reduce) {
  .il-chart-load__spin { animation: none; }
}

/* 차트 내부 SVG 공통 — 그리드와 축은 후퇴시킨다 */
.il-grid-line { stroke: var(--chart-grid); stroke-width: 1; }
.il-axis-line { stroke: var(--chart-axis); stroke-width: 1; }
.il-axis-text { fill: var(--chart-label); font-size: 11px; }
.il-series-label { fill: var(--ink); font-size: 11px; font-weight: 620; }
/* 수평 막대의 항목 이름 — 축 눈금과 달리 본문처럼 읽는다 */
.il-cat-label { fill: var(--ink); font-size: var(--fs-caption); font-weight: 620; }
/* 마크: 선 2px, 막대 끝 4px 라운드, 채움 사이 2px 간격 */
.il-line { fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.il-bar { rx: 4; }
/* r 은 차트가 간격에 맞춰 인라인으로 정한다. CSS 로 못 박으면 인라인 값을 덮어버린다 */
.il-dot { stroke: var(--chart-surface); stroke-width: 2; }
.il-dot:not([r]) { r: 4; }
/* 점이 촘촘할 때는 링을 뺀다 — 링이 선을 끊어 점선처럼 보인다 */
.il-dot--flat { stroke: none; }

/* 막대 위에 얹는 값 */
.il-bar-top {
  font-size: 10px; font-weight: 700; fill: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
/* 막대 안에 얹는 값 — 채움 위에 올라가므로 반전색을 쓴다 */
.il-bar-value {
  font-size: 10px; font-weight: 700; fill: var(--text-inverse);
  font-variant-numeric: tabular-nums;
}

/* 마우스 오버 — 세로 안내선 + 값 목록 */
.il-guide { stroke: var(--border-strong); stroke-width: 1; stroke-dasharray: 3 3; }
/* 도넛에서 다른 조각을 흐리게 — 지금 보고 있는 것이 어느 조각인지 말해 준다 */
.il-s1.is-dim, .il-s2.is-dim, .il-s3.is-dim,
.il-s4.is-dim, .il-s5.is-dim, .il-s6.is-dim, .il-s-other.is-dim { opacity: .3; }

/* 히트맵에서 마우스가 올라간 칸 — 색을 바꾸지 않고 테두리만 준다 (값이 왜곡되면 안 된다) */
.il-heat-focus { fill: none; stroke: var(--ink); stroke-width: 2; pointer-events: none; }
.il-charttip {
  position: absolute; z-index: 5; pointer-events: none;
  min-width: 148px; padding: var(--sp-3);
  background: var(--bg-elevated);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  font-size: var(--fs-caption);
}
.il-charttip__date { margin-bottom: var(--sp-2); color: var(--text-muted); font-weight: 620; }
.il-charttip__row { display: flex; align-items: center; gap: var(--sp-2); line-height: 1.9; }
.il-charttip__row.is-sub { padding-left: var(--sp-3); }
.il-charttip__k { flex: 1 1 auto; color: var(--text-secondary); }
.il-charttip__v { flex: none; font-variant-numeric: tabular-nums; }

/* 시리즈 색 — 순서대로만 배정한다 */
.il-s1 { color: var(--chart-1); } .il-s2 { color: var(--chart-2); } .il-s3 { color: var(--chart-3); }
.il-s4 { color: var(--chart-4); } .il-s5 { color: var(--chart-5); } .il-s6 { color: var(--chart-6); }
.il-s-other { color: var(--chart-other); }
.il-s-ink { color: var(--ink); }
.il-fill { fill: currentColor; }
.il-stroke { stroke: currentColor; }

/* 히트맵 — 0 값은 램프를 쓰지 않고 비운다 */
.il-heat-0 { fill: var(--chart-empty); }
.il-heat-1 { fill: var(--seq-100); } .il-heat-2 { fill: var(--seq-200); }
.il-heat-3 { fill: var(--seq-300); } .il-heat-4 { fill: var(--seq-400); }
.il-heat-5 { fill: var(--seq-500); } .il-heat-6 { fill: var(--seq-600); }

/* ── 데이터 표 ─────────────────────────────────────────────────────── */
.reb-table-wrap {
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius-lg); overflow: hidden;
}
.reb-table-toolbar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--border);
}
.reb-table-toolbar-title { font-size: var(--fs-lead); font-weight: 700; }
.reb-table-toolbar-meta { margin-left: var(--sp-2); font-size: var(--fs-caption); color: var(--text-muted); }
/* 표는 자기 컨테이너 안에서만 가로 스크롤한다. body 는 절대 스크롤되지 않는다 */
.reb-table-scroll { overflow-x: auto; }
.reb-table { width: 100%; border-collapse: collapse; min-width: 720px; }
.reb-table th, .reb-table td {
  padding: var(--sp-3) var(--sp-4); text-align: left;
  vertical-align: middle;          /* 두 줄짜리 셀과 숫자 셀의 높이를 맞춘다 */
  border-bottom: 1px solid var(--border);
}
.reb-table-cell--center { text-align: center; }
/* 열 폭을 균등하게 나누고 가운데 정렬하는 표 — 실시간 피드처럼 한 화면에 다 들어가는 것 */
.reb-table--center th, .reb-table--center td { text-align: center; }
/* ★ th 만 적혀 있던 것을 td 까지 넓혔다 (2026-09-04). 위의
     `.reb-table th, .reb-table td { text-align: left }` 가 (0,2,0) 이라
     클래스 하나짜리(0,1,0)로는 <td> 에서 지고, 머리글만 가운데로 갔다 —
     .reb-table-cell--num 과 똑같은 자리다 (아래 참조) */
.reb-table th.reb-table-cell--center,
.reb-table td.reb-table-cell--center { text-align: center; }
.reb-table th { font-size: var(--fs-caption); font-weight: 620; color: var(--text-secondary); white-space: nowrap; }
.reb-table tbody tr:last-child td { border-bottom: 0; }
.reb-table tbody tr:hover { background: var(--bg-subtle); }
/* compact 는 카드·좁은 칸 안에 들어간다. 720px 바닥을 그대로 쓰면 열이 잘린다 */
.reb-table--compact { min-width: 0; }
.reb-table--compact th, .reb-table--compact td { padding: var(--sp-2) var(--sp-3); }
/* roomy — compact 의 min-width:0 은 쓰되 여백은 기본 표만큼 되돌린다.
   카드 폭을 다 쓰는 표용이다. compact 의 좁은 여백은 il-grid-2 안에 두 개를
   나란히 넣을 때(campaigns.html) 필요한 것이지, 단독 카드에서는 답답하다.
   좌우 끝 열은 툴바 여백(--sp-5)에 맞춘다 — 그래야 표 제목과 첫 열이 한 줄에 선다 */
.reb-table--roomy th, .reb-table--roomy td { padding: var(--sp-3) var(--sp-4); }
/* 끝열 여백은 툴바가 여백을 대는 reb-table-wrap 안에서만 준다.
   il-card 처럼 컨테이너가 이미 여백을 가진 곳에 주면 이중으로 들어가
   표 제목보다 첫 열이 더 안쪽으로 밀린다 */
.reb-table-wrap .reb-table--roomy th:first-child,
.reb-table-wrap .reb-table--roomy td:first-child { padding-left: var(--sp-5); }
.reb-table-wrap .reb-table--roomy th:last-child,
.reb-table-wrap .reb-table--roomy td:last-child  { padding-right: var(--sp-5); }
/* 숫자 칸 — 오른쪽 정렬.

   ★ 셀렉터에 th · td 를 반드시 붙인다 (2026-09-04). 위의
       .reb-table th, .reb-table td { text-align: left }
     이 (0,2,0) 이라, 클래스 하나짜리(0,1,0)로 적으면 **진다.**
     그렇게 적혀 있어서 이 클래스는 여태 <td> 에서 한 번도 듣지 않았다 —
     머리글만 맞고 숫자는 왼쪽에 붙어 있었다.
     빈 표만 보던 동안에는 드러나지 않았다 (프로젝트 통계에 값이 들어가면서
     보였다). Tabulator 의 빈 목록 안내 · 로딩 상자에서 겪은 것과 같은 자리다. */
.reb-table th.reb-table-cell--num,
.reb-table td.reb-table-cell--num { text-align: right; }
/* .reb-table 밖에서 쓰는 경우를 위해 홑으로도 남겨 둔다 */
.reb-table-cell--num { text-align: right; }
.reb-table-cell--mono { font-family: var(--font-mono); font-size: var(--fs-caption); }
.reb-table-cell--actions { width: 1%; white-space: nowrap; }
/* Tabulator 셀 안의 액션 묶음. 위 --actions 는 <td> 전용(width:1%)이라 여기 못 쓴다 */
.il-cell-actions { display: inline-flex; gap: var(--sp-2); white-space: nowrap; }

/* ── 탭 ────────────────────────────────────────────────────────────── */
/* 분석 탭 줄 — 탭 묶음과, 그 오른쪽에 서는 고르개(기기 · 방문/클릭).

   ★ 고르개를 `.reb-tabs` **바깥**으로 뺐다 (2026-09-11).
     안에 두면 탭 묶음의 가로 스크롤 상자 안에 갇힌다. 넓은 화면에서는
     자리가 남아 티가 안 났는데, 폰에서는 —
       · 탭 다섯이 이미 넘쳐서 고르개가 **화면 밖**으로 밀린다. 오른쪽으로
         250px 쯤 밀어야 나오니 있는 줄도 모른다.
       · 게다가 남은 폭으로 쥐어짜여, 단추 셋이 세로로 쌓여 **줄 높이가
         48px → 110px 로 뛰었다.** 탭을 옮길 때마다 탭 줄이 들썩였다
         (고르개는 `디바이스 · OS` 탭에서만 숨는다 — chart.js).
     밖으로 빼면 탭 줄은 높이가 고정되고, 좁은 화면에서는 고르개가 제 줄을
     갖는다 (app.css 의 520px 규칙).

   테두리 한 줄은 이 바깥 상자가 그린다 — 안쪽 탭 묶음이 그리면 고르개가
   아래로 내려간 폰에서 선이 탭과 고르개 사이를 가로지른다 */
.reb-tabsbar { display: flex; align-items: stretch; border-bottom: 1px solid var(--border); }
.reb-tabsbar > .reb-tabs { flex: 1 1 auto; min-width: 0; border-bottom: 0; }
.reb-tabs { display: flex; gap: var(--sp-1); border-bottom: 1px solid var(--border); overflow-x: auto; }
.reb-tab {
  padding: var(--sp-3) var(--sp-4); background: none; border: 0; border-bottom: 2px solid transparent;
  color: var(--text-secondary); font-weight: 620; white-space: nowrap; cursor: pointer;
}
.reb-tab:hover { color: var(--ink); }
.reb-tab[aria-selected="true"], .reb-tab.is-active { color: var(--ink); border-bottom-color: var(--ink); }
.reb-tab-count {
  margin-left: 6px; padding: 0 6px; border-radius: var(--radius-full);
  background: var(--bg-subtle); font-size: var(--fs-micro); font-weight: 700;
}
.reb-tabs--pill { border-bottom: 0; gap: var(--sp-1); padding: 3px; background: var(--bg-subtle); border-radius: var(--radius-sm); }
.reb-tabs--pill .reb-tab { border: 0; border-radius: 6px; padding: var(--sp-2) var(--sp-3); }
.reb-tabs--pill .reb-tab.is-active { background: var(--bg-elevated); box-shadow: var(--shadow-xs); }

/* ── PIN 입력 — 공유 리포트 ────────────────────────────────────────── */
.reb-pin { display: flex; gap: var(--sp-2); }
.reb-pin-cell {
  width: 44px; height: 52px; text-align: center;
  font-size: var(--fs-title); font-weight: 700; font-variant-numeric: tabular-nums;
  background: var(--bg-elevated); border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
}
.reb-pin-cell:focus { border-color: var(--ink); outline: none; }
.reb-pin--filled .reb-pin-cell { border-color: var(--ink); }
.reb-pin--error .reb-pin-cell { border-color: var(--danger); }
.reb-pin--sm .reb-pin-cell { width: 34px; height: 40px; font-size: var(--fs-lead); }

/* ── 숫자 스테퍼 ───────────────────────────────────────────────────── */
.reb-stepper { display: inline-flex; align-items: center; gap: var(--sp-1); }
.reb-stepper-btn {
  width: 32px; height: 32px; background: var(--bg-elevated);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm); cursor: pointer;
}
.reb-stepper-input { width: 64px; height: 32px; text-align: center; font-variant-numeric: tabular-nums;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--bg-elevated); }
.reb-stepper-suffix { font-size: var(--fs-caption); color: var(--text-secondary); }

/* ── 툴팁 ──────────────────────────────────────────────────────────── */
.reb-tooltip { position: relative; display: inline-flex; }
.reb-tooltip-content {
  position: absolute; bottom: calc(100% + 6px); left: 50%; transform: translateX(-50%);
  padding: 6px var(--sp-2); background: var(--ink); color: var(--on-ink);
  border-radius: 6px; font-size: var(--fs-caption); white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity var(--t-fast);
  z-index: 20;
}
.reb-tooltip:hover .reb-tooltip-content, .reb-tooltip:focus-within .reb-tooltip-content { opacity: 1; }
.reb-tooltip--multiline .reb-tooltip-content { white-space: normal; max-width: 240px; }
.reb-tooltip--bottom .reb-tooltip-content { bottom: auto; top: calc(100% + 6px); }
@media (hover: none) { .reb-tooltip-content { display: none; } }  /* 모바일에서는 표시하지 않는다 */

/* =====================================================================
   Phase 3 추가 — 셸·표·배너에서 쓰는 조각
   (파일 뒤쪽에 더하므로 위쪽 @media 를 덮지 않는지 확인함: 아래 선택자는
    app.css 의 미디어쿼리와 겹치지 않는다 — 코딩 가이드 §11)
   ===================================================================== */

/* 스크린리더 전용 — 라벨은 지우지 않고 감춘다 */
.il-sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* 배너 — 동기화 실패처럼 화면 전체에 알려야 하는 상태 */
/* 왼쪽 3px 강조 막대를 두지 않는다 — 테두리는 사방 1px 로 두고
   성격은 배경과 테두리 색으로만 구분한다 */
.il-banner {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) 14px; margin-bottom: var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md); background: var(--bg-elevated);
  font-size: var(--fs-caption);
}
.il-banner--warn   { border-color: var(--warn);   background: var(--warn-tint); }
.il-banner--danger { border-color: var(--danger); background: var(--danger-tint); }
.il-banner--info   { border-color: var(--accent); background: var(--accent-tint); }
/* 경고가 아니라 그냥 알아 둘 것. 주의색을 쓰지 않는다 —
   남발하면 정작 위험한 안내가 묻힌다. 글자는 본문색 그대로 둔다 */
.il-banner--quiet {
  border-color: var(--border-strong); background: var(--bg-subtle); color: var(--ink);
}

/* ── QR 받기 레이어 (2026-09-07) ──────────────────────────────────
   `내려받기` 를 누르면 무엇으로 받을지 묻는 작은 메뉴다 (assets/js/ui.js).

   ★ 여기 55 는 **창 밖에서 열렸을 때의 값**이다. 토스트(60)보다 아래로
     둔다 — `복사했습니다` 가 메뉴에 가리면 안 된다.
     모달 안에서 열리면 이 값을 안 쓴다 (2026-09-08). 창은 스타일시트의
     50 으로 뜨지 않는다 — openModal 이 열 때마다 z-index 를 다시 적어서
     (50 + 깊이 × 10) 하나만 떠 있어도 60 이다. 그래서 ui.js 가 그 창의
     값을 읽어 한 칸 위에 세운다. 여기 숫자를 키워 쫓아가지 않는다 —
     창이 겹쳐 열리면 70 · 80 … 으로 계속 올라간다.
   ★ position:fixed 다. body 에 붙여 놓고 JS 가 누른 자리 옆에 세운다 —
     카드 안에 두면 .reb-table-wrap 같은 overflow:hidden 상자에서 잘린다. */
/* ★ 폭을 못 박지 않는다 (2026-09-07). 처음에는 208px 를 깔아 두었는데,
   그것은 `인쇄용 · 크기를 키워도 안 깨집니다` 라는 설명 줄이 있을 때의
   값이었다. 그 줄을 걷어내고도 폭이 남아 있어서 글자 오른쪽이 훤히 비었다 —
   세 줄짜리 메뉴가 제 내용보다 두 배 넓게 서 있었다.
   이제 글자가 폭을 정한다 (아래 nowrap 이 줄바꿈을 막는다) */
.il-qrmenu {
  position: fixed; z-index: 55; padding: var(--sp-1);
  display: flex; flex-direction: column;
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius-md); box-shadow: var(--shadow-lg);
}
.il-qrmenu button {
  display: flex; align-items: center; gap: var(--sp-2); width: 100%;
  padding: var(--sp-2) var(--sp-3); border: 0; border-radius: var(--radius-sm);
  background: none; color: var(--ink); font: inherit; font-size: var(--fs-body);
  text-align: left; white-space: nowrap; cursor: pointer;
}
/* 아이콘은 줄어들지 않는다 — 좁아질 때 글자가 아니라 그림이 찌그러지면
   무엇인지 알아볼 수 없게 된다 */
.il-qrmenu button svg { flex: none; color: var(--text-secondary); }
.il-qrmenu button:hover svg { color: var(--ink); }
.il-qrmenu button:hover { background: var(--bg-subtle); }
/* 포커스 테두리를 안쪽에 그린다 — 바깥이면 레이어 모서리에 잘린다 */
.il-qrmenu button:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }

/* 토스트 스택 */
.il-toast-host {
  position: fixed; left: 50%; bottom: var(--sp-6); transform: translateX(-50%);
  z-index: 60; display: flex; flex-direction: column; gap: var(--sp-2);
  align-items: center; pointer-events: none;
}

/* 정렬 가능한 헤더 */
.reb-table th.is-sortable { cursor: pointer; user-select: none; }
.reb-table th.is-sortable:hover { color: var(--ink); }
.reb-table th.is-sortable::after { content: ''; display: inline-block; width: 0; }
.reb-table th[aria-sort="descending"]::after { content: ' ▾'; }
.reb-table th[aria-sort="ascending"]::after  { content: ' ▴'; }
.reb-table tbody tr.is-selected { background: var(--accent-tint); }
.reb-table td[data-rownum] { color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* 체크박스 · 라디오 — 타깃 크기 확보 (WCAG 2.2 최소 24px) */
.reb-table input[type="checkbox"],
.il-check { width: 16px; height: 16px; margin: 4px; accent-color: var(--accent); }

/* 탐색 셀렉트 라벨 래퍼 */

/* 정의 목록 — 상세 화면의 메타 표기 */
/* 라벨(작은 글씨)과 값(본문 크기)의 높이를 맞춘다.
   baseline 정렬은 값 안에 배지(20px 상자)나 아이콘 버튼이 섞이면 다시 어긋난다
   — 상자의 baseline 은 그 안 글자에서 나오기 때문이다.
   행 단위로 가운데를 맞추면 무엇이 들어와도 한 줄로 보인다. */
.il-dl {
  display: grid; grid-template-columns: max-content 1fr;
  align-items: center;
  gap: var(--sp-3) var(--sp-4); margin: 0;
}
.il-dl dt { color: var(--text-secondary); font-size: var(--fs-caption); line-height: var(--lh-body); }
.il-dl dd {
  margin: 0; min-width: 0; line-height: var(--lh-body);
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
}

/* 코드/토큰 표기 */
.il-code {
  font-family: var(--font-mono); font-size: var(--fs-caption);
  background: var(--bg-subtle); padding: 1px 6px; border-radius: 6px;
}

/* 표 칸 전체가 코드일 때. .il-code 를 <td> 에 걸면 칸 배경이 통째로 회색이 되어
   열 하나가 띠처럼 보인다 (channels.html 에서 실제로 그랬다).
   같은 등폭 글꼴을 쓰되 배경은 주지 않는다 */
.il-mono { font-family: var(--font-mono); font-size: var(--fs-caption); }

/* 스텝 인디케이터 — QR 2단계, 벌크 3단계 */
.il-steps { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-5); }
.il-step { display: flex; align-items: center; gap: var(--sp-2); color: var(--text-muted); font-size: var(--fs-caption); }
.il-step__no {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: var(--radius-full);
  border: 1px solid var(--border-strong); font-weight: 700;
}
.il-step.is-active { color: var(--ink); font-weight: 620; }
.il-step.is-active .il-step__no { background: var(--ink); color: var(--text-inverse); border-color: var(--ink); }
.il-step__sep { flex: 0 0 24px; height: 1px; background: var(--border); }

/* QR 미리보기 자리 */
/* 색 견본 — QR 전경색처럼 정해진 몇 개 중 고르는 자리.
   색상값은 .il-s1~6 · .il-s-ink 가 토큰에서 가져온다 (여기에 hex 를 쓰지 않는다) */
.il-swatches { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.il-swatch {
  width: 28px; height: 28px; padding: 0; cursor: pointer;
  border: 2px solid transparent; border-radius: var(--radius-full);
  background: currentColor;
  /* 흰 배경에서 밝은 색 견본의 경계가 사라지지 않게 */
  box-shadow: inset 0 0 0 1px var(--border);
}
.il-swatch[aria-pressed="true"] { border-color: var(--ink); }
.il-swatch:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.il-qr-preview {
  display: grid; place-items: center;
  aspect-ratio: 1; background: var(--bg-subtle);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  color: var(--text-placeholder); font-size: var(--fs-caption);
}
/* 코드가 그려진 상태 — 바탕은 흰색 고정 (--qr-bg 주석 참고) */
.il-qr-preview:has(svg) { background: var(--qr-bg); }
/* ★ 코드는 잉크다 (2026-08-26 28차).
   빈 자리의 안내 글자 색(--text-placeholder)을 svg 가 그대로 물려받아
   회색으로 그려지고 있었다 — QR 이 옅으면 꺼져 있는 것처럼 보이고,
   실제로도 대비가 낮으면 잘 안 읽힌다 */
.il-qr-preview svg {
  width: 100%; height: auto; display: block;
  border-radius: var(--radius-sm); color: var(--ink);
}
/* 확인용 크기 (2026-08-26 27차). 모달 폭을 다 쓰게 두면 인쇄용 원본처럼 보이는데,
   여기 있는 것은 스캔이 되는지 눈으로 보는 것뿐이다 — 쓸 파일은 내려받는다 */
.il-qr-preview--sm { width: 240px; max-width: 100%; margin-inline: auto; }
.il-qr-bg { fill: var(--qr-bg); }

/* 드롭존 */
.il-dropzone {
  padding: var(--sp-10) var(--sp-5); text-align: center;
  border: 1.5px dashed var(--border-strong); border-radius: var(--radius-md);
  background: var(--bg-elevated); color: var(--text-secondary);
}

/* 와이어프레임 주석 — 팀 리뷰용 메모. 실제 화면에는 없다 */
.il-note {
  border-left: 3px solid var(--accent); background: var(--accent-tint);
  padding: var(--sp-3) var(--sp-4); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: var(--fs-caption);
}
.il-note strong { font-weight: 700; }

/* 날짜·수치 셀은 줄바꿈하지 않는다 — 'YYYY-MM-' / 'DD' 로 갈라지면 읽기 어렵다 */
.reb-table td.il-caption[data-value],
.reb-table .reb-table-cell--num { white-space: nowrap; }

/* =====================================================================
   정보 툴팁 (il-infotip) — 짧은 reb-tooltip 으로는 담기 어려운 설명용

   reb-tooltip 은 한 줄~240px 짜리다. 여러 문단·코드 블록이 들어가는
   개념 설명은 이 컴포넌트를 쓴다.
   hover · 키보드 포커스 · 클릭(터치) 세 경로 모두로 열린다 —
   hover 만 쓰면 모바일에서 아예 볼 수 없다.
   ===================================================================== */
.il-infotip { position: relative; display: inline-flex; vertical-align: middle; }

/* 라벨 옆에 붙는 인라인 아이콘. 테두리 없이 아이콘만 보이되,
   클릭 영역은 24px 를 유지한다 (WCAG 2.5.8) */
.il-infotip__btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0; margin: -4px 0 -4px 4px;
  background: none; border: 0; border-radius: var(--radius-full);
  color: var(--text-placeholder); cursor: pointer;
  transition: color var(--t-fast), background var(--t-fast);
}
.il-infotip__btn:hover,
.il-infotip__btn[aria-expanded="true"] { color: var(--ink); background: var(--bg-subtle); }

.il-infotip__panel {
  position: absolute; z-index: 40; top: calc(100% + 8px); right: 0;
  width: 380px; max-width: min(380px, calc(100vw - 32px));
  /* 닫혀 있을 때는 높이를 접는다. visibility:hidden 은 안 보일 뿐 자리는
     그대로 차지해서, 모달처럼 overflow:auto 인 상자 안에서는 이 패널 때문에
     세로 스크롤바가 생긴다. 폭은 그대로 두어야 펼칠 방향을 잴 수 있다 */
  max-height: 0; overflow: hidden;
  padding: 0 var(--sp-4);
  background: var(--bg-elevated); color: var(--ink);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  font-size: var(--fs-caption); line-height: 1.65; font-weight: 400;
  text-align: left; white-space: normal;
  /* ★ 화면 안으로 밀어 넣는 거리는 **`translate` 쪽**에 붙는다 (2026-09-15).
     이 패널은 아이콘에 붙어 좌우 중 한쪽으로 펴지는데(아래 여는 방향),
     폰에서는 **양쪽 다 모자란** 경우가 있다 — 380px 짜리가 390px 화면에
     붙을 자리가 없다. 그때 ui.js 의 placeTip 이 `panel.style.translate` 에
     px 를 넣어 통째로 민다.

     ★ transform 에 넣으면 안 된다. 여기 transition 이 transform 을 물고
       있어서, 그 값을 JS 로 바꾸면 **화면에 반영되지 않는다** — 커스텀
       속성(var)으로 넣어도 같다(등록하지 않은 속성은 전이를 못 만든다).
       한 번 그렇게 짰다가 `--tip-x` 만 바뀌고 패널은 제자리에 있었다.
       독립 `translate` 속성은 이 transition 목록에 없어서 바로 먹는다.
       둘 다 이동이라 결과는 그냥 더해진다 */
  opacity: 0; visibility: hidden; transform: translateY(-4px);
  transition: opacity var(--t-fast), transform var(--t-fast), visibility var(--t-fast);
}
.il-infotip:hover .il-infotip__panel,
.il-infotip:focus-within .il-infotip__panel,
.il-infotip.is-open .il-infotip__panel {
  opacity: 1; visibility: visible; transform: translateY(0);
  max-height: 70vh; overflow: auto; padding: var(--sp-4);
}

/* 툴팁 제목은 문서 헤딩이 아니다 — h4 를 쓰면 그룹 링크 헤딩 위계(h1→h4)가 끊긴다 */
.il-infotip__title { display: block; font-size: var(--fs-body); font-weight: 700; margin-bottom: var(--sp-2); }
.il-infotip__panel p { margin-bottom: var(--sp-3); }
.il-infotip__panel p:last-child { margin-bottom: 0; }
.il-infotip__panel pre {
  margin: 0 0 var(--sp-3); padding: var(--sp-3);
  background: var(--bg-subtle); border-radius: var(--radius-sm);
  font-family: var(--font-mono); font-size: 11px; line-height: 1.7;
  white-space: pre; overflow-x: auto;
}

/* 여는 방향 — 기본은 오른쪽 정렬(왼쪽으로 펼침) · 아래로 펼침.
   상자(화면 또는 모달) 밖으로 나가면 JS 가 반대쪽 클래스를 붙인다. */
.il-infotip--start .il-infotip__panel { left: 0; right: auto; }
.il-infotip--up    .il-infotip__panel { top: auto; bottom: calc(100% + 8px); }

/* 한 카드 안에 관련 지표를 같은 비중으로 나란히 둔다.
   지표를 카드로 잘게 쪼개면 서로의 관계가 안 보이고,
   한쪽만 크게 하면 나머지가 곁다리로 읽힌다.

   카드는 항상 세 줄로 쌓는다 — 제목 / 지표 라벨 / 숫자.
   지표가 하나뿐인 카드도 라벨 줄을 비워서 자리를 남긴다.
   그래야 아래 subgrid 로 카드 간 숫자 높이를 맞출 수 있다. */
.il-kpi-cols { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-4); }
.il-kpi-cols--1 { grid-template-columns: minmax(0, 1fr); }
.il-kpi-cols--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.il-kpi-cols--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* 라벨과 숫자를 제 칸 가운데에 둔다 (2026-08-25).
   이 칸들은 카드 하나가 화면 폭을 다 쓰는 자리에 있어서 하나가 400~700px 다.
   왼쪽에 붙여 두면 그 넓은 칸에서 글자가 왼쪽으로 흘러내린 것처럼 보였다.
   라벨은 inline-flex 라 text-align 이 안 먹는다 — justify-content 로 맞춘다 */
.il-kpi-label-cell {
  display: inline-flex; align-items: center; justify-content: center; min-width: 0;
  font-size: var(--fs-caption); font-weight: 620; color: var(--text-secondary);
}
.il-kpi-value-cell {
  font-size: var(--fs-title); font-weight: var(--fw-title); letter-spacing: var(--ls-title);
  line-height: 1.2; font-variant-numeric: tabular-nums; min-width: 0; text-align: center;
}
.il-kpi-value-cell .reb-kpi-unit { font-size: var(--fs-body); }

/* 카드마다 안쪽 줄 수가 달라 숫자 높이가 어긋난다.
   subgrid 로 부모의 행을 그대로 물려받아 제목 · 라벨 · 숫자 줄을 카드 간에 맞춘다. */
@supports (grid-template-rows: subgrid) {
  .il-kpi-row--aligned { grid-template-rows: auto auto auto; row-gap: var(--sp-3); }
  .il-kpi-row--aligned > .reb-kpi {
    display: grid; grid-template-rows: subgrid; grid-row: span 3;
    align-content: start; row-gap: var(--sp-2);
  }
  .il-kpi-row--aligned > .reb-kpi > .reb-kpi-head { margin-bottom: 0; }
}

/* 머리 줄에 고르개가 서면 아래로 한 칸 더 준다 (2026-09-07).
   subgrid 의 row-gap 은 8px 인데, 그 값은 머리 줄이 **글자 하나**였을 때
   정한 것이다. 글자 밑에는 눈에 보이지 않는 여백(line-height)이 붙어
   있어서 8px 이 8px 보다 넓게 보였는데, 테두리가 있는 단추는 그 여백이
   없어 칠해진 면이 곧바로 아랫줄 라벨과 맞닿는다.

   row-gap 을 키우지 않는 까닭 — 그 값은 라벨과 숫자 사이에도 똑같이
   걸린다. 붙어 있어야 할 두 줄(라벨·숫자)까지 같이 벌어진다.

   :has 를 쓰지 않는다. 단추에 직접 걸면 단추가 있는 곳에서만 알아서
   걸리고, 없는 카드는 규칙을 만나지도 않는다.

   sp-2 였다 (같은 날 한 번 더 키웠다). row-gap 8 + margin 8 = 16px 인데
   아직 좁았다 — 위가 칠해진 면이라 그 밑의 흰 자리가 실제 치수보다
   좁아 보인다. 16 을 더해 24px 로 둔다 */
.reb-kpi-head > .il-segment { margin-bottom: var(--sp-4); }

/* ── vendor 테마 매핑 — Air Datepicker ──────────────────────────────
   assets/vendor/air-datepicker/ 를 넣으면 이 변수들이 적용된다.
   라이브러리가 없으면 아무 효과도 없다 (선언만 남는다).
   셀렉터를 덮어쓰지 않고 라이브러리가 노출한 --adp-* 만 쓴다 — 버전을 올려도 안 깨진다.
   변수명은 air-datepicker 3.6.0 의 air-datepicker.css 에서 확인한 것. */
.air-datepicker {
  --adp-font-family: var(--font-sans);
  --adp-font-size: var(--fs-caption);
  --adp-border-radius: var(--radius-sm);
  --adp-background-color: var(--bg-elevated);
  --adp-border-color: var(--border);
  --adp-border-color-inner: var(--border);
  --adp-color: var(--ink);
  --adp-color-secondary: var(--text-muted);
  --adp-accent-color: var(--accent);
  --adp-color-current-date: var(--accent);
  --adp-color-other-month: var(--text-placeholder);
  --adp-color-disabled: var(--text-placeholder);
  --adp-day-name-color: var(--text-secondary);
  --adp-cell-border-radius: var(--radius-sm);
  --adp-cell-background-color-hover: var(--bg-subtle);
  --adp-cell-background-color-selected: var(--ink);
  --adp-cell-background-color-selected-hover: var(--ink-hover);
  --adp-cell-background-color-in-range: var(--accent-tint);
  --adp-cell-background-color-in-range-hover: var(--accent-tint);
  --adp-btn-color: var(--ink);
  --adp-btn-color-hover: var(--ink);
  --adp-btn-background-color-hover: var(--bg-subtle);
  --adp-btn-border-radius: var(--radius-sm);
}

/* ── 상위 링크 표 ────────────────────────────────────────────────────
   목록의 링크를 파란 밑줄 글자로 두면 "가서 보라"는 신호만 남고
   무엇인지가 안 읽힌다. 제목은 본문색으로, 단축 URL 은 보조로 둔다. */
.il-toplink { display: flex; flex-direction: column; gap: 2px; color: inherit; text-decoration: none; }
.il-toplink__title {
  font-weight: 620; color: var(--ink); line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.il-toplink__short { font-size: var(--fs-caption); color: var(--text-muted); font-variant-numeric: tabular-nums; }
.il-toplink:hover .il-toplink__title { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }

/* 순위 — 1~3 위만 강조한다. 전부 강조하면 아무것도 강조되지 않는다 */
.il-rank {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: var(--radius-full);
  background: var(--bg-subtle); color: var(--text-muted);
  font-size: var(--fs-caption); font-weight: 700; font-variant-numeric: tabular-nums;
}
.il-rank--1 { background: var(--ink); color: var(--text-inverse); }
.il-rank--2, .il-rank--3 { background: var(--accent-tint); color: var(--accent-on-tint); }

/* =====================================================================
   Tabulator 6.5.2 — innored 토큰에 맞추기

   air-datepicker 와 달리 Tabulator 는 CSS 커스텀 프로퍼티를 노출하지 않는다
   (SCSS 변수를 빌드 시점에 굽는다). 그래서 셀렉터를 덮어쓴다 —
   버전을 올릴 때 이 블록을 다시 확인해야 한다.
   값은 전부 토큰에서 가져온다. 여기에 hex 를 쓰지 않는다.
   ===================================================================== */
.tabulator {
  background: var(--bg-elevated);
  border: 0;
  font-size: var(--fs-body);
  font-family: var(--font-sans);
}
.tabulator .tabulator-header {
  background: var(--bg-subtle);
  border-bottom: 1px solid var(--border);
  color: var(--text-secondary);
  font-weight: 620;
  font-size: var(--fs-caption);
}
.tabulator .tabulator-header .tabulator-col {
  background: transparent;
  border-right: 0;
}
.tabulator .tabulator-header .tabulator-col .tabulator-col-content { padding: var(--sp-3) var(--sp-4); }
.tabulator .tabulator-header .tabulator-col.tabulator-sortable:hover { background: var(--bg-subtle); }

/* 머리글은 가운데 정렬 — 제목과 정렬 화살표가 한 묶음으로 움직인다.
   hozAlign 은 듣지 않는다 (php-coding-guide §5-4-1) */
.tabulator .tabulator-header .tabulator-col .tabulator-col-title-holder { justify-content: center; }
.tabulator .tabulator-header .tabulator-col .tabulator-col-title { text-align: center; }

/* ★ 표 속에서 세로로 스크롤하지 않는다 (2026-09-08).
   쪽 넘김이 있어서 표는 늘 제 높이대로 서고, 스크롤은 페이지가 한다.
   그런데 감사 로그의 줄을 펼치면 표가 **잠깐** 제 상자보다 길어지고, 그
   찰나에 상자 안에 세로 스크롤바가 선다 — Tabulator 가 하필 그 순간의
   폭으로 칸을 재서, 스크롤바가 사라진 뒤에도 줄이 15px 좁은 채로 남았다
   (펼친 줄 오른쪽의 흰 띠가 그것이다. 실측 1082 vs 1097).
   세로를 아예 끄면 그 찰나가 없다. 가로(auto)는 둔다 — 좁은 창에서
   칸이 최소폭에 걸리면 가로 스크롤은 실제로 필요하다 */
.tabulator .tabulator-tableholder { overflow-y: hidden; }

.tabulator .tabulator-row { background: var(--bg-elevated); border-bottom: 1px solid var(--border); }
.tabulator .tabulator-row.tabulator-row-even { background: var(--bg-elevated); }
.tabulator .tabulator-row:hover { background: var(--bg-subtle); }
/* 세로 중앙은 CSS 로 맞춘다. Tabulator 의 vertAlign 을 주면 셀이 flex-row 가 되어
   제목·단축URL·목적지 세 줄이 옆으로 붙는다 (php-coding-guide §5-4) */
.tabulator .tabulator-row .tabulator-cell {
  padding: var(--sp-3) var(--sp-4);
  border-right: 0;
  color: var(--ink);
  /* flex 가 아니라 inline-flex — Tabulator 셀은 inline-block 이라
     블록 flex 로 바꾸면 셀마다 한 줄을 차지해 행이 세로로 무너진다 */
  display: inline-flex; flex-direction: column; justify-content: center;
  /* 인라인 박스가 baseline 에 앉으면서 폭이 밀리지 않게 못박는다 */
  box-sizing: border-box; vertical-align: top;
}
/* 셀을 column flex 로 만들었으므로 가로 정렬도 align-items 로 옮긴다 */
.tabulator .tabulator-row .tabulator-cell.il-col-center { align-items: center; text-align: center; }
.tabulator .tabulator-row .tabulator-cell.il-col-num    { align-items: flex-end; text-align: right; }
/* 버튼 두 개가 들어가는 칸은 좌우 여백을 줄인다 — 안 그러면 버튼이 칸 밖으로 나간다 */
.tabulator .tabulator-row .tabulator-cell.il-col-actions { padding-left: var(--sp-2); padding-right: var(--sp-2); }

/* ★ 그런데 **줄의 마지막 칸**은 예외다 (2026-09-15 · 사용자 지적).
   좁힌 여백이 표 한가운데(UTM 칸 같은 곳)에서는 맞다 — 양옆에 다른 칸이
   있어 그 사이가 좁아질 뿐이다. 마지막 칸은 그 오른쪽이 **표의 끝**이라,
   좁히면 단추가 표 모서리에 붙는다.

   왼쪽 첫 칸(`번호`)은 16px 를 그대로 들고 있어서, 한 줄 안에서 왼쪽은
   여유롭고 오른쪽만 끼어 보였다 — 표가 오른쪽으로 잘린 것처럼 읽힌다.
   머리글(.tabulator-col-content)도 16px 라, 여기만 8px 였다.

   ★ 되돌리면 칸이 16px 더 필요하다. 그 폭은 액션 칸의 width 로 같이
     준다 (adminlinks.js · admingroups.js) — 안 주면 단추가 칸 밖으로
     나가고, 원래 이 규칙이 막으려던 것이 그것이다.

   ★ `:last-child` 가 아니라 **`:last-of-type`** 이다. Tabulator 는 셀마다
     뒤에 `<span class="tabulator-col-resize-handle">` 을 하나씩 끼워
     넣는다 — 그래서 **마지막 칸도 :last-child 가 아니다**(마지막 자식은
     늘 그 span 이다). 셀은 div 이고 손잡이는 span 이라, 타입으로 세면
     마지막 셀이 잡힌다. 여기서 한 번 틀렸다: `:last-child` 로 적었더니
     규칙이 조용히 아무 데도 안 걸렸다 */
.tabulator .tabulator-row .tabulator-cell.il-col-actions:last-of-type {
  padding-left: var(--sp-4); padding-right: var(--sp-4);
}

/* =====================================================================
   로그 줄 — 접었다 폈다 (관리자 로그의 감사 로그 탭, 2026-09-04)

   왜 표가 아닌가
     감사 로그 한 줄이 답하는 것은 `무엇이 어떻게 바뀌었나` 인데, 그 `어떻게`
     가 칸에 안 들어간다 — 바뀐 칸 이름과 **이전 값**이 줄마다 개수가 다르다.
     칸을 늘려 담으면 대부분의 줄에서 그 칸이 비고, 한 줄로 뭉치면 잘린다.
     그래서 접힌 줄은 `언제 · 누가 · 무엇을` 한 줄로만 말하고, 자세한 것은
     펼쳐서 본다.

   Tabulator 안에 그린다 (칸 하나짜리 표). 쪽 넘김 · 불러오는 중 · 실패 상자를
   다시 만들지 않으려는 것이다 — 그 셋은 오류 탭과 같아야 한다.
   ===================================================================== */
.il-logrow { display: flex; align-items: center; gap: var(--sp-3); width: 100%; }
/* 켜져 있음을 색 하나로 말하지 않는다 — 옆의 동작 이름이 같은 것을 글자로 말한다 */
.il-logrow__dot {
  flex: none; width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent);
}
.il-logrow__at { flex: none; font-family: var(--font-mono); font-size: var(--fs-caption); color: var(--text-secondary); }
/* 동작 이름은 코드값이라 고정폭으로 — 훑을 때 세로로 정렬돼 읽힌다 */
/* 동작 이름 — 한글이 들어오면서 모노 글꼴을 뗐다 (2026-09-08).
   모노는 코드를 읽으라는 표시인데, 이제 여기 오는 것은 `그룹 링크 고치기`
   같은 말이다. 코드는 펼친 속에 남아 있고 그쪽이 모노다 */
.il-logrow__action {
  flex: none; font-size: var(--fs-caption); font-weight: 620;
  white-space: nowrap;
  padding: 2px var(--sp-2); border-radius: var(--radius-sm);
  background: var(--accent-tint); color: var(--accent-on-tint);
}
/* 대상은 남는 폭을 갖고, 넘치면 자른다. 전문은 펼쳐서 본다 */
.il-logrow__target { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.il-logrow__who { flex: none; color: var(--text-secondary); font-size: var(--fs-caption); }
.il-logrow__chev { flex: none; color: var(--text-muted); transition: transform var(--t-fast); }
.il-logitem.is-open .il-logrow__chev { transform: rotate(180deg); }

/* 펼치면 **줄 전체**가 한 덩이가 된다 (2026-09-08 3차).
   앞서는 펼친 속에만 바탕을 깔았는데, 그 상자의 오른쪽 끝이 머리 줄의
   꺾쇠보다 바깥이라 `머리 줄은 여기까지, 속은 저기까지` 로 두 겹으로
   보였다 — 그 어긋난 자리가 오른쪽의 흰 여백이다.
   머리와 속이 같은 상자를 쓰면 어긋날 자리가 없다 */
/* ★ 바탕은 **줄이 든다** — 펼친 속이 아니라 (2026-09-08 5차).
     펼친 속 상자에 바탕을 깔면 그 상자가 칸 안여백만큼 안으로 들어가
     좌우에 흰 띠가 남는다. 마우스를 얹었을 때만 멀쩡해 보였던 것은
     :hover 바탕이 줄 끝까지 차서 그 띠를 덮고 있었기 때문이다 —
     마우스를 빼면 도로 띠가 났다.
     그 :hover 와 **같은 자리에 같은 색**을 깐다. 펼친 줄은 마우스가
     있든 없든 얹었을 때의 그 모습으로 서 있는다.
     음수 바깥여백으로 넓히지 않는다: 칸이 넓어지면 표가 제 상자보다
     넓어져 펼치는 순간 줄이 왼쪽으로 뛴다 (4차에서 그랬다).

   ★ 줄을 :has 로 잡지 않는다 (6차). `.tabulator-row:has(.il-logitem.is-open)`
     은 파이어폭스에서만 먹었다 — reformat() 이 칸 속을 통째로 갈아 끼우는데,
     그때 조상의 :has 를 다시 재는 일을 크롬과 사파리가 건너뛴다.
     표시는 adminaudit.js 의 markOpen() 이 줄에 직접 붙인다 */
.tabulator .tabulator-row.il-rowopen { background: var(--bg-subtle); }

.il-logitem.is-open { padding: var(--sp-2) 0; }

/* 펼친 속 — 이름·값 쌍이 **흐른다** (2026-09-08).

   ★ 격자로 두었더니 마지막 줄 오른쪽이 늘 비었다. 값이 `16:05:40` 이나
     IP 처럼 짧은데 줄은 화면 폭을 다 쓰고, 칸 수는 홀수라 어느 배치로도
     한 칸이 남는다 — 그 빈칸이 `덜 그려진 칸` 으로 보였다.
     흐르게 두면 줄이 차는 데까지 차고 남은 데서 끝난다. 같은 빈자리가
     빈칸이 아니라 **문장 끝의 여백**으로 읽힌다.

   ★ 바탕은 줄 전체가 든다 (위 .il-logitem.is-open). 여기서 따로 깔면
     머리 줄과 속이 두 겹으로 보인다 — 오른쪽 흰 여백이 그것이었다. */
.il-logrow__detail {
  margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: 1px solid var(--border);
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-6);
  font-size: var(--fs-caption);
}
/* 한 쌍은 붙어 다닌다 — 이름과 값이 다른 줄로 갈라지면 짝을 잃는다 */
.il-logrow__pair { display: flex; align-items: baseline; gap: var(--sp-2); min-width: 0; }
.il-logrow__pair dt { flex: none; color: var(--text-muted); }
.il-logrow__pair dd { margin: 0; color: var(--ink); min-width: 0; word-break: break-all; }
.il-logrow__pair dd.il-mono { font-family: var(--font-mono); }
/* 긴 값(주소 · 경로 · 유입처 · UA)은 제 줄을 통째로 쓴다 —
   흐르는 줄에 끼면 두 글자씩 끊겨 내려간다 (adminaudit.js 의 dt) */
.il-logrow__pair.is-wide { flex: 1 0 100%; }

@media (max-width: 520px) {
  /* 좁은 화면에서는 쌍마다 제 줄을 쓴다 — 두 쌍이 한 줄에 끼면 값이
     두 글자씩 끊긴다 */
  .il-logrow__detail { gap: var(--sp-2); }
  .il-logrow__pair { flex: 1 0 100%; }

  /* ★ 접힌 줄도 세 줄로 눕힌다 (2026-09-11).

     한 줄에 다섯이 선다 — 점 · 동작 · 시각 · 대상 · 사람 · 화살표.
     재 보면 **396px 이 필요한데 폰에서 쓸 수 있는 폭은 310px** 이다.
     줄일 수 있는 것은 `대상` 하나뿐이라(나머지는 flex:none) 그것이 0 까지
     쪼그라든 뒤에도 86px 이 모자라, **사람 이름과 화살표가 칸 밖으로
     잘려 나갔다.** 사진의 그 줄이다. 접는 화살표가 안 보이니 **펼 수 있다는
     사실 자체가 사라진다** — 잘린 것 중 가장 나쁜 쪽이었다.

     flex 로 접지 않고 격자로 자리를 못 박는다. 흘려 보내면 줄이 차는 대로
     끊겨서, 어떤 줄은 화살표가 시각 옆에 끼고 어떤 줄은 안 낀다 —
     열 줄이 제각각으로 접힌다.

         ●  [동작]                              ⌄
            2026-09-14 10:59:47      여경일 (빅터)
            대상 이름…

     시각과 사람을 한 줄에 두는 까닭 — 그 둘이 `언제 누가` 라 같이 읽힌다.
     대상은 길어서 제 줄을 통째로 쓴다 (넘치면 … 로 자른다).

     ★ 오류 표도 같은 뼈대다. 거기서는 동작 자리에 `.il-badge` 가 서고
       사람 자리에 `3회` 가 선다 — 그래서 둘 다 적는다 */
  .il-logrow {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "dot act  chev"
      ".   at   who"
      ".   tgt  tgt";
    column-gap: var(--sp-2);
    row-gap: 2px;
    align-items: center;
  }
  .il-logrow__dot { grid-area: dot; }
  .il-logrow__action,
  .il-logrow > .il-badge { grid-area: act; justify-self: start; }
  .il-logrow__chev { grid-area: chev; justify-self: end; }
  .il-logrow__at { grid-area: at; justify-self: start; }
  .il-logrow__who { grid-area: who; justify-self: end; }
  .il-logrow__target { grid-area: tgt; }
}

/* 칸이 하나뿐인 표 — 셀이 줄 전체다. 가운데 정렬 규칙이 걸리지 않게 왼쪽으로 */
.tabulator .tabulator-row .tabulator-cell.il-logcell {
  align-items: stretch; text-align: left; white-space: normal;
  padding: var(--sp-3) var(--sp-4);
}
.tabulator .tabulator-row .tabulator-cell.il-logcell .il-logitem { width: 100%; cursor: pointer; }

/* 푸터 — 그룹 링크 넘김 */
.tabulator .tabulator-footer {
  background: var(--bg-subtle);
  border-top: 1px solid var(--border);
  color: var(--text-secondary);
  font-size: var(--fs-caption);
}
.tabulator .tabulator-footer .tabulator-page {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font-size: var(--fs-caption);
  padding: 4px var(--sp-3);
}
.tabulator .tabulator-footer .tabulator-page:not(.disabled):hover { background: var(--bg-subtle); color: var(--ink); }
.tabulator .tabulator-footer .tabulator-page.active { background: var(--ink); border-color: var(--ink); color: var(--bg-elevated); }
.tabulator .tabulator-footer .tabulator-page.disabled { opacity: .4; }
.tabulator .tabulator-footer .tabulator-page-size {
  background: var(--bg-elevated); color: var(--ink);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 3px var(--sp-2);
}
.tabulator .tabulator-footer .tabulator-paginator label { color: var(--text-muted); }

/* 빈 목록 — 줄이 하나도 없을 때

   ★ 셀렉터에 .tabulator-tableholder 를 반드시 넣는다 (2026-09-02).
     Tabulator 의 기본 규칙이
       .tabulator .tabulator-tableholder .tabulator-placeholder .tabulator-placeholder-contents
     라 특정도가 (0,4,0) 이다. 여기서 그것을 빼면 (0,3,0) 이 되어 **진다** —
     이 블록은 여태 그렇게 적혀 있었고, 그래서 한 줄도 듣지 않았다.
     빈 표를 볼 일이 없어서(와이어프레임은 늘 목업이 차 있었다) 아무도 몰랐다.
     실제 개발에 들어가 빈 DB 를 보니 20px · 700 · #ccc 가 그대로 떴다.

   본문 크기로 내린다. 이 문구는 **읽는 글**이다 — 표가 비었다는 사실을
   사람에게 말한다.
   그래서 색도 --text-placeholder(3.24:1 · UI 전용)가 아니라
   --text-muted(4.70:1 · AA 본문)를 쓴다. 옅으면 안내가 아니라 고장으로 읽힌다.

   위아래 여백을 넉넉히 준다. 머리글에 붙어 있으면 표가 잘린 것처럼 보인다 */
.tabulator .tabulator-tableholder .tabulator-placeholder {
  background: var(--bg-elevated);
}
.tabulator .tabulator-tableholder .tabulator-placeholder .tabulator-placeholder-contents {
  color: var(--text-muted);
  font-size: var(--fs-body);
  font-weight: var(--fw-body);
  line-height: var(--lh-body);
  /* 안내가 화면 끝까지 늘어지지 않게. 문구가 길어지는 날을 위한 자리다 */
  max-width: 46ch;
  padding: var(--sp-10) var(--sp-6);
  white-space: normal;
}

/* 불러오는 중 · 실패 — 서버에서 쪽을 받아 오는 동안 표 위에 뜬다 (2026-09-04)

   Tabulator 의 기본값이 이 화면과 너무 멀었다. 새로고침할 때마다 이것이 떴다:

     · 표 위를 rgba(0,0,0,.4) 로 **검게** 덮는다
     · 그 위에 4px #333 테두리를 두른 흰 상자, 16px · 700
     · 문구는 영어로 `Loading`

   덮는 까닭 자체는 옳다 — 그 아래 값이 지금 것이 아니라는 신호다. 다만
   이 화면에서 그 신호는 **검은 막이 아니라 옅게 뜨는 것**이어야 한다.
   쪽을 넘길 때 앞 쪽 줄들이 비쳐 보이는 편이, 화면이 한 번 꺼졌다 켜지는
   것보다 무엇을 기다리는지 분명하다.

   색은 토큰에서 만든다 (여기에 hex 를 쓰지 않는다). color-mix 로 카드색을
   옅게 태워 쓴다 — app.css 가 이미 쓰는 방식이다. */
.tabulator .tabulator-alert {
  background: color-mix(in srgb, var(--bg-elevated) 72%, transparent);
}
.tabulator .tabulator-alert .tabulator-alert-msg {
  background: var(--bg-elevated);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  font-size: var(--fs-caption);
  font-weight: var(--fw-caption);
  padding: var(--sp-2) var(--sp-4);
}

/* ★ 테두리와 글자색은 **상태 클래스까지 붙여야** 듣는다 (0,4,0).
     Tabulator 의 기본이
       .tabulator .tabulator-alert .tabulator-alert-msg.tabulator-alert-state-msg
     라, 위 블록(0,3,0)에 border 를 적으면 4px #333 이 그대로 이긴다.
     빈 표 안내(.tabulator-placeholder)에서 한 번 겪은 것과 같은 자리다 */
.tabulator .tabulator-alert .tabulator-alert-msg.tabulator-alert-state-msg {
  border: 1px solid var(--border);
  color: var(--text-muted);
}
/* 실패는 색으로 말한다. 글자만 바뀌면 기다리는 중과 구분이 안 된다 */
.tabulator .tabulator-alert .tabulator-alert-msg.tabulator-alert-state-error {
  background: var(--danger-tint);
  border: 1px solid var(--danger);
  color: var(--danger-on-tint);
}

/* 첫 화면에서 표가 납작해지지 않게. 줄이 하나도 없는 채로 뜨면 머리글과
   푸터 사이가 30px 밖에 안 되고, 안내 상자가 그 틈에 끼어 카드 밖으로
   비어져 나온다. 줄이 도착하면 이 값은 저절로 의미를 잃는다 (min- 이다) */
.tabulator:has(.tabulator-alert) .tabulator-tableholder { min-height: 168px; }

/* 기다리는 동안 `없습니다` 를 감춘다.
   빈 채로 세워 두고 데이터를 나중에 받는 표(프로젝트 관리)는 첫 순간에
   빈 표 안내가 먼저 그려진다. 덮개가 옅어서 그 글씨가 비쳐 보이는데,
   **아직 묻지도 않았는데 없다고 답한 것**이라 그대로 읽히면 곤란하다.
   줄은 비쳐도 된다(쪽을 넘길 때 앞 쪽이 보이는 편이 낫다) — 안내만 감춘다 */
.tabulator:has(.tabulator-alert) .tabulator-placeholder { visibility: hidden; }

/* =====================================================================
   찾기가 붙은 고르는 칸 (assets/js/pickselect.js)

   <select> 를 없애지 않는다 — 감춰만 두고 값은 거기 있다. JS 가 죽거나
   늦게 오면 원래 select 가 그대로 보이고, 고르는 일 자체는 된다.
   ===================================================================== */
.il-pick { position: relative; }

/* JS 가 붙기 전에는 원래 select 가 보인다. 붙고 나면 자리만 남긴다 —
   display:none 이 아니다: 라벨(for=)과 폼 값이 살아 있어야 한다 */
.il-pick__native {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.il-pick__btn {
  width: 100%; text-align: left;
  font: inherit; color: var(--ink);
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  /* 오른쪽은 화살표 자리만큼 넓다 (--sp-8 = 32px) */
  padding: var(--sp-3) var(--sp-8) var(--sp-3) var(--sp-4);
  cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 아직 안 고른 칸은 옅다 — 채워진 칸과 눈으로 갈려야 한다 */
.il-pick__btn.is-empty { color: var(--text-muted); }
.il-pick__btn:hover { border-color: var(--ink); }

/* 화살표. 브라우저 것을 쓰지 않는다 (원래 select 는 감춰져 있다) */
.il-pick__btn::after {
  content: ''; position: absolute; right: var(--sp-4); top: 50%;
  width: 8px; height: 8px; margin-top: -6px;
  border-right: 1.6px solid var(--text-muted);
  border-bottom: 1.6px solid var(--text-muted);
  transform: rotate(45deg); pointer-events: none;
}

/* 아래에 자리가 없으면 위로 뒤집는다 (pickselect.js 가 is-up 을 붙인다).
   레이어(inlineedit.js) 안에서 열 때가 특히 그렇다 — 레이어는 fixed 라
   화면 밖으로 나간 목록이 스크롤로도 안 따라온다. 못 고르는 칸이 된다 */
.il-pick__pop.is-up { top: auto; bottom: calc(100% + 4px); }
.il-pick__pop {
  position: absolute; z-index: 30; left: 0; right: 0; top: calc(100% + 4px);
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  padding: var(--sp-2);
  display: flex; flex-direction: column; gap: var(--sp-2);
}
.il-pick__q { flex: none; width: 100%; }

/* 여덟 줄쯤에서 구른다. 창을 넘기면 아래가 잘려 고를 수 없다 */
.il-pick__list { max-height: 240px; overflow: auto; display: flex; flex-direction: column; }

/* ★ flex: none 이 빠지면 안 된다 (2026-09-03).
     이 상자는 세로 flex 이고 높이가 240px 로 묶여 있다. flex 항목은 기본이
     flex-shrink:1 이라, 줄이 상자보다 많으면 **줄마다 제 높이보다 납작해진다** —
     글자가 서로 겹쳐 읽을 수 없게 되고, 스크롤은 생기지도 않는다.

     프로젝트 고르는 칸은 서너 개뿐이라 이 버그가 안 보였다. 참여자 칸에
     사내 인원 아흔둘이 들어오면서 드러났다: 목록이 길어질수록 나빠지는
     종류라, 검사가 없으면 다음에도 같은 자리에서 같은 방식으로 돌아온다 */
.il-pick__opt {
  flex: none;
  text-align: left; font: inherit; color: var(--ink);
  background: transparent; border: 0; border-radius: var(--radius-sm);
  padding: var(--sp-2) var(--sp-3); cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 마우스와 키보드가 같은 표시를 쓴다 — 둘이 다르면 어디를 짚고 있는지
   섞인다 (마우스를 올린 채 화살표를 누르는 일이 흔하다) */
.il-pick__opt:hover, .il-pick__opt.is-at { background: var(--bg-subtle); }
.il-pick__opt[aria-selected="true"] { font-weight: 620; }

.il-pick__none {
  margin: 0; padding: var(--sp-4) var(--sp-3);
  color: var(--text-muted); font-size: var(--fs-caption); text-align: center;
}
