/*
 * 이 화면에는 "괜찮다"는 뜻의 초록색이 없다.
 *
 * 2단계 벤치마크에서 TRUSTED 판정이 붙은 오답이 실제로 측정됐다 — 두 모델이 나란히
 * WHERE city = '서울'(실제 값은 'Seoul')을 써서 둘 다 0행을 냈고, 대조도 검증도
 * 통과했다. 3단계에서는 carrier = '한진택배'(실제 값 'Hanjin')로 더 깔끔하게
 * 재현됐다. 그래서 신뢰 배지는 등급을 색으로 말하지 않는다 — trust.trust 값은 이
 * 화면 어디에도 그려지지 않고, 세 등급 모두 똑같은 중립 카드로 나온다. 판정은
 * 오직 문장(headline/notes)으로만 전달된다.
 *
 * 경고(잘림, 실패)에만 색을 쓴다. 색이 있으면 주의하라는 뜻이고, 색이 없다고
 * 안심하라는 뜻은 아니다.
 */

/*
 * ── 디자인 토큰 ──────────────────────────────────────────────────────
 *
 * 이 도구는 문서가 아니라 계기판이다. 사람이 여기서 하는 일은 읽는 것이
 * 아니라 <b>확인하는 것</b> — 어떤 표가 있고, 몇 행이고, 무엇이 아직
 * 검증되지 않았는지. 그래서 시각 언어를 카탈로그 자신에게서 가져온다:
 * 식별자는 고정폭, 경계는 실선 한 겹, 숫자는 자릿수가 맞는 표기.
 *
 * 어두운 판이 기본이다. 이 화면은 SQL·스키마·계획을 들여다보는 자리이고
 * 그런 도구는 대부분 어둡다 — 밝은 판은 유지하되(프로젝터가 약한 방이
 * 있다) 고르는 것이지 기본이 아니다. prefers-color-scheme 을 보지 않고
 * 기본을 어둠으로 못박은 이유는, 데모에서 발표자의 OS 설정에 따라 화면이
 * 달라지면 안 되기 때문이다. 고를 수단은 머리줄의 토글이다.
 *
 * 색이 뜻하는 것은 이 파일 맨 위 주석 그대로다 — 경고에만 색을 쓴다.
 * --accent 는 파랑이고 "괜찮다"는 뜻이 <b>아니다</b>. 조작할 수 있는
 * 자리(링크·기본 버튼·포커스)를 가리킬 뿐이다.
 */
/*
 * ── 디자인 토큰 ──────────────────────────────────────────────────────
 *
 * 두 참조에서 왔다.
 *
 *   어두운 판  WhyWorks(D:\Workspace\WhyWorks) — void 위에 오로라가 흐르고
 *              그 위에 반투명 유리 패널이 뜬다. 깊이를 그림자가 아니라
 *              <b>값의 단계</b>로 만든다: 08080a → 0f1116 → 171a21 → 232833.
 *   밝은 판    whyzoo.com — 브랜드 블루 #285986, 파랑기가 도는 층진 그림자
 *              (0 8px 24px / 0 18px 42px), 그리고 아주 옅은 청사진 격자.
 *
 * 두 판이 <b>같은 파랑</b>을 쓴다. 참조한 WhyWorks 의 다크는 보라 액센트지만,
 * 그 색을 그대로 가져오면 이 도구가 WhyWorks 처럼 보인다. 가져온 것은 색이
 * 아니라 구조다 — void, 오로라, 유리, 값의 단계.
 *
 * 청사진 격자는 장식이 아니다. 이 도구가 다루는 것이 표와 관계의 격자이고,
 * whyzoo.com 이 이미 같은 무늬를 쓴다.
 *
 * 색이 뜻하는 것은 이 파일 맨 위 주석 그대로다 — 경고에만 색을 쓴다.
 * --accent 는 "괜찮다"는 뜻이 아니라 조작할 수 있는 자리를 가리킨다.
 */
:root[data-theme="dark"] {
  color-scheme: dark;

  /*
   * 값의 단계. 이 판의 깊이는 그림자가 아니라 <b>면의 밝기</b>가 만든다.
   *   0a0a0a  바닥. 여기가 전부의 밑판이다
   *   161616  떠 있는 면 — 옆줄, 머리줄, 불투명 카드
   *   서리유리 위 둘 사이를 반투명으로 메운다
   */
  --void: #0a0a0a;
  --ground: #0a0a0a;
  --bg: #0a0a0a;

  /*
   * 서리유리.
   *
   * 참조는 rgba(212,212,212,0.1) 인데 이 도구에서는 0.06 이다. 저쪽은
   * 카드가 화면에 서넛뿐인 소개 페이지이고, 여기는 카드 안에 표가 들어가
   * 화면 하나에 열 장이 겹친다 — 0.1 로 두면 겹친 자리가 회색으로 차오르고
   * 표의 줄이 안 보인다. 값은 낮췄지만 방식은 그대로다: 깊이를 그림자가
   * 아니라 <b>투명도</b>로 만든다.
   */
  --card: rgba(212, 212, 212, 0.075);
  --card-2: rgba(212, 212, 212, 0.14);
  --card-solid: #161616;

  /*
   * 옆줄·머리줄.
   *
   * 카드보다 불투명하되 <b>유리이기는 하다</b>. 0.92 로 두었더니 뒤의 노을이
   * 하나도 안 비쳐 이 두 면만 검은 판때기로 남았고, 화면 안에서 유리와
   * 판때기가 섞여 보였다(소유자 지적). 0.72 + 강한 블러면 색이 은은히
   * 배어 나오면서 글자는 그대로 읽힌다.
   */
  --panel: rgba(18, 18, 20, 0.72);

  /*
   * 실선 한 겹.
   *
   * 참조는 #e5e5e5 를 그대로 쓰라고 한다. 이 도구에서는 알파를 넣었다 —
   * 저쪽의 테두리는 화면에 열 줄쯤이고, 여기는 표 한 장에만 수백 줄이다.
   * 꽉 찬 #e5e5e5 로 격자를 그리면 자료가 아니라 <b>격자가</b> 읽힌다.
   * 가리키는 방향은 같다: 그림자를 쓰지 않고 밝은 실선 한 겹으로 면을 가른다.
   */
  --line: rgba(229, 229, 229, 0.26);
  --line-2: rgba(229, 229, 229, 0.13);

  /*
   * 글자 사다리. 순백을 본문에 쓰지 않는다 — 검정 위의 #ffffff 는 눈이
   * 부시고, 오래 읽는 화면에서 그 피로가 쌓인다(참조의 규칙이기도 하다).
   */
  --ink: #ededed;
  --ink-2: #c2c2c2;
  --dim: #9a9a9a;

  /*
   * 조작할 수 있는 자리.
   *
   * 이 판에는 브랜드 색이 없다. 그래서 <b>흰색이 곧 액션</b>이다 — 채워진
   * 흰 알약 하나가 이 판에서 유일하게 채워진 것이고, 그것이 눈을 끄는
   * 방식이다. 파랑을 다시 들이면 판의 무채색 규율이 그 자리에서 깨진다.
   *
   * 이 색이 "괜찮다"는 뜻이 아닌 것은 예전과 같다. 파일 맨 위 주석 참고.
   */
  --accent: #ffffff;
  --accent-ink: #161616;
  --accent-soft: rgba(255, 255, 255, 0.1);
  --focus: #ffffff;
  --focus-ring: rgba(255, 255, 255, 0.28);

  /*
   * 경고에는 색을 쓴다.
   *
   * 참조는 "색을 더 들이지 말라"고 하지만 그 규칙보다 앞서는 것이 이 파일
   * 맨 위의 규칙이다 — <b>경고에만 색을 쓴다</b>. 무채색 규율을 지키려고
   * 경고까지 회색으로 만들면, 잘렸다는 사실과 실패했다는 사실이 본문과
   * 같은 무게로 지나간다. 대신 채도를 낮춰 판에 얹는다.
   */
  --warn-ink: #e8c07d;
  --warn-bg: rgba(232, 192, 125, 0.08);
  --warn-line: rgba(232, 192, 125, 0.28);
  --bad-ink: #e89a9a;
  --bad-bg: rgba(232, 154, 154, 0.08);
  --bad-line: rgba(232, 154, 154, 0.3);

  /*
   * <b>그림자를 쓰지 않는다.</b> 이 판의 깊이는 투명도와 실선 한 겹이
   * 만든다 — 참조가 명시적으로 금지하는 것이고, 검정 위의 검정 그림자는
   * 어차피 거의 일하지 않았다.
   *
   * 토큰을 지우지 않고 none 으로 두는 이유: 이 값을 쓰는 규칙이 스무 곳
   * 넘고, 밝은 판은 여전히 그림자로 깊이를 만든다. 값 하나로 판이 갈린다.
   */
  /*
   * <b>안쪽 실선 한 겹</b>이 유리의 가장자리다.
   *
   * 참조가 그림자를 금지하면서 유일하게 남겨 둔 것이 이 값이다
   * (shadow-subtle: rgba(255,255,255,0.1) 0 0 0 1px inset). 바깥으로
   * 번지는 그림자가 아니라 <b>면의 테두리 안쪽에 빛이 걸린 것</b>이라,
   * 유리판을 위에서 봤을 때 모서리가 반짝이는 그 느낌을 만든다.
   *
   * 처음에는 none 으로 뒀다가 되돌렸다 — 그러면 카드가 배경에서 떠 있다는
   * 신호가 <b>하나도</b> 남지 않아 화면이 통째로 평평해졌다(실측, 소유자
   * 지적). 그림자를 안 쓴다는 것과 깊이를 안 준다는 것은 다른 말이다.
   */
  --shadow-1: rgba(255, 255, 255, 0.09) 0 0 0 1px inset;
  --shadow-2: rgba(255, 255, 255, 0.14) 0 0 0 1px inset;
  --shadow: var(--shadow-1);

  /*
   * 유리.
   *
   * 참조는 4px 를 말하지만 그것은 <b>배경이 그라디언트 한 장</b>일 때의
   * 값이다. 여기는 뒤에 표와 글자가 흐르므로 그만큼으로는 비쳐 읽힌다.
   * 채도를 올리는 것이 유리다움의 절반이다 — 뒤의 색이 유리를 통과하며
   * 살짝 진해지는 것이 실제 유리의 성질이고, 그것이 없으면 그냥
   * <b>반투명한 회색판</b>이 된다.
   */
  --glass: blur(18px) saturate(150%);

  /* 격자는 거의 지운다. 이 판의 바닥은 <매트한 검정>이다. */
  --grid-line: rgba(229, 229, 229, 0.022);
  --grid-size: 32px;

  /*
   * 단 하나의 색 — 저녁 보라. <b>채움에 쓰지 않는다.</b> 가로로 번지는
   * 그라디언트와 은은한 빛무리에만 나온다(참조의 규칙 그대로).
   */
  /*
   * 색이 나오는 자리는 <b>둘뿐</b>이고, 둘 다 배경이다.
   *
   * 처음 옮길 때 이 둘을 거의 지워 놓았더니 화면이 그냥 흑백이 됐다(소유자
   * 지적). 참조가 "2% 색"이라고 말한 것은 <b>색을 없애라</b>가 아니라
   * <b>색을 두 자리에만 몰아 쓰라</b>는 뜻이었다 — UI 는 무채색으로 두되,
   * 배경에는 앰버에서 코발트로 넘어가는 큰 그라디언트가 깔린다.
   */
  --violet: #6b62f2;

  /* 저녁 노을 — 따뜻한 쪽에서 찬 쪽으로. 참조의 hero horizon 이다. */
  --horizon-warm: rgba(238, 138, 62, 0.42);
  --horizon-cool: rgba(48, 96, 214, 0.48);

  /* 빛무리. 자리를 안 옮기고 밝기만 오르내리므로 알파가 곧 세기다. */
  --violet-glow: rgba(107, 98, 242, 0.26);

  /* 가로로 번지는 보라 띠. 채움이 아니라 <b>강조 선</b>으로만 쓴다. */
  --violet-wash: linear-gradient(90deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0) 40%,
      rgba(107, 98, 242, 0.565) 50%, rgba(0, 0, 0, 0) 60%, rgba(0, 0, 0, 0));

  --top-h: 58px;

  /* 본문 폭. 참조의 page max-width 그대로다. 화면마다 다르면 안 된다. */
  --page-max: 1200px;

  /*
   * <b>읽는 문장이 끊기는 폭.</b> 한 값이고, 여기 한 곳에만 있다.
   *
   * 예전에는 78ch 였다. ch 는 <b>「0」 글자의 너비</b>라 라틴 기준이고,
   * 한글 한 글자는 그 두 배쯤이다 — 그래서 78ch 가 실제로는 <b>한글 39자</b>
   * 에서 끊겼다. 폭이 1,080px 인 상자 안에서 605px 만 쓰고 접힌 것이다.
   * 소유자가 "여백이 충분한데 개행된다, 기준을 모르겠다"고 한 것이 이것이다.
   *
   * em 으로 적으면 한글 한 글자가 대략 1em 이라 <b>세는 단위와 보이는 것이
   * 같아진다</b> — 60em 은 한글 예순 자 남짓이다. 상자를 가득 채우지는
   * 않는다: 1,080px 을 꽉 채운 설명문은 눈이 다음 줄 첫 글자를 놓친다.
   */
  --measure: 60em;

  /*
   * 둥글기. 참조의 세 값을 그대로 쓴다 — 카드 24, 조작 10, 버튼은 알약.
   * 알약이 이 판의 <b>실루엣</b>이라 버튼에서만은 양보하지 않는다.
   */
  --radius-card: 24px;
  --radius-control: 10px;
  --radius-pill: 9999px;

  /*
   * 얼굴.
   *
   * 라틴은 DM Sans, 한글은 Noto Sans KR. 순서를 바꾸면 숫자까지 Noto 가
   * 내고 이 판의 성격이 사라진다 — 자세한 것은 fonts.css 의 주석에 있다.
   * 둘 다 저장소 안에 있다(폐쇄망).
   */
  --ui: "DM Sans", "Noto Sans KR", -apple-system, BlinkMacSystemFont,
        "Segoe UI", "Malgun Gothic", system-ui, sans-serif;
  --mono: "Cascadia Mono", "Cascadia Code", Consolas, "D2Coding",
          ui-monospace, SFMono-Regular, monospace;
}

/*
 * 알약이 이 판의 실루엣이다.
 *
 * 버튼·이동 링크·꼬리표가 전부 9999px 다. 참조가 "이 시스템을 정의하는
 * 모양"이라고 못박은 것이고, 실제로 이 하나만 바꿔도 화면의 인상이 바뀐다.
 *
 * 밝은 판에는 걸지 않는다 — 그쪽은 whyzoo.com 의 각진 카드 언어라서,
 * 알약만 옮겨 오면 두 판이 서로 다른 두 디자인의 조각을 섞어 입은 꼴이 된다.
 */
:root[data-theme="dark"] .btn,
:root[data-theme="dark"] .seg,
:root[data-theme="dark"] .nav-link {
  border-radius: var(--radius-pill);
}

/*
 * 굵게 하지 않는다.
 *
 * 참조의 서명이 <b>weight 500 으로 권위를 만드는 것</b>이다. 700 으로
 * 올리는 순간 이 판은 평범한 대시보드가 된다. 제목 자리만 눌러 둔다 —
 * 본문의 <strong> 은 그대로 두어야 강조가 강조로 남는다.
 */
:root[data-theme="dark"] h1,
:root[data-theme="dark"] h2,
:root[data-theme="dark"] h3,
:root[data-theme="dark"] .page-title,
:root[data-theme="dark"] .panel-title,
:root[data-theme="dark"] .side-title {
  font-weight: 500;
  letter-spacing: -0.02em;
}

/*
 * 밝은 판 — whyzoo.com.
 *
 * 어두운 판의 역할을 옮긴 것이지 색을 뒤집은 것이 아니다. 여기서 깊이는
 * 유리가 아니라 <b>층진 그림자</b>가 만든다. 그림자에 파랑기를 넣는 것이
 * 요점이다 — 회색 그림자는 종이에 때가 묻은 것처럼 보이고, 파랑기가 도는
 * 그림자는 면이 떠 있는 것처럼 보인다.
 */
:root[data-theme="light"] {
  color-scheme: light;

  /*
   * 밝은 판 — <b>부드러운 흰 카드가 떠 있는</b> 판.
   *
   * 소유자가 보여준 그림(Stripe 홈)의 방식이다. 앞서 시도한 유리
   * (반투명 + 블러)와 <b>다른 방법으로</b> 깊이를 만든다:
   *
   *   유리    뒤가 비친다. 깊이는 <투명도>가 만든다
   *   이 판   불투명한 흰 면. 깊이는 <b>크고 부드러운 그림자</b>가 만든다
   *
   * 그림자가 두 겹인 것이 요점이다 — 가까운 한 겹이 면의 가장자리를 앉히고,
   * 멀고 크게 번지는 한 겹이 <b>공중에 떠 있음</b>을 만든다. 한 겹만 쓰면
   * 종이가 바닥에 붙어 있는 것처럼 보인다.
   *
   * 흔히 "뉴모피즘"이라 부르는 것(같은 색 바닥에 안팎 그림자를 넣어 도드라져
   * 보이게 하는 것)은 <b>입력 칸</b>에만 쓴다 — 눌러 넣는 자리라 파여
   * 보이는 것이 맞고, 넓은 면에 쓰면 대비가 무너져 글자가 안 읽힌다.
   */

  --void: #eef2f7;
  --ground: #f2f6fa;
  --bg: #f6f9fc;

  /* 카드는 <b>불투명한 흰색</b>이다. 유리가 아니라 종이다. */
  --card: #ffffff;
  --card-2: #f6f9fc;
  --card-solid: #ffffff;
  --panel: #ffffff;

  /*
   * 실선은 <b>거의 없다</b>. 면을 가르는 일을 그림자가 하므로 테두리는
   * 표 안쪽처럼 꼭 필요한 자리에만 아주 옅게 남는다.
   */
  --line: rgba(60, 66, 87, 0.12);
  --line-2: rgba(60, 66, 87, 0.06);

  /* 남색 잉크. 순검정은 흰 면 위에서 딱딱하다. */
  --ink: #0a2540;
  --ink-2: #3c4257;
  --dim: #6b7c93;

  --accent: #635bff;
  --accent-ink: #ffffff;
  --accent-soft: rgba(99, 91, 255, 0.1);
  --focus: #635bff;
  --focus-ring: rgba(99, 91, 255, 0.25);

  --warn-ink: #7a4b00;
  --warn-bg: #fff6e5;
  --warn-line: #f5d9a3;
  --bad-ink: #8c2020;
  --bad-bg: #ffeff0;
  --bad-line: #f3c2c5;

  /*
   * 두 겹 그림자.
   *
   * 가까운 겹은 짧고 진하게(가장자리), 먼 겹은 길고 옅게(떠 있음).
   * 색에 남색기를 넣는 것은 그대로다 — 회색 그림자는 종이에 때가 묻은
   * 것처럼 보인다.
   */
  --shadow-1: 0 2px 5px -1px rgba(50, 50, 93, 0.09),
              0 12px 28px -8px rgba(50, 50, 93, 0.13);
  --shadow-2: 0 4px 8px -2px rgba(50, 50, 93, 0.1),
              0 28px 56px -16px rgba(50, 50, 93, 0.18);
  --shadow: var(--shadow-1);

  /* 유리가 아니다. 블러를 끈다 — 켜 두면 흰 면 위에서 하는 일이 없다. */
  --glass: none;

  --grid-line: rgba(60, 66, 87, 0.035);
  --grid-size: 28px;

  /*
   * 배경의 <b>메시 그라디언트</b>. 그림의 그 띠다.
   *
   * 어두운 판의 노을보다 <b>훨씬 진하다</b>(0.52 -> 0.9 대) — 저쪽은 유리를
   * 통해 배어 나오면 되지만, 여기서는 카드가 불투명해서 배경이 카드 <b>바깥
   * 에서만</b> 보인다. 옅으면 그냥 흰 화면이 된다.
   *
   * 대신 카드가 덮지 않는 자리에 글자가 놓이므로, 진한 색은 화면 <b>가장
   * 자리</b>로 몰아 둔다(아래 body::before/after 의 좌표).
   */
  --horizon-warm: rgba(255, 94, 128, 0.62);
  --horizon-cool: rgba(64, 156, 255, 0.55);
  --violet-glow: rgba(124, 77, 255, 0.42);

  --radius-card: 14px;
  --radius-control: 9px;
}

/*
 * 입력 칸만 <b>파여</b> 보이게 한다. 이것이 흔히 말하는 뉴모피즘의 자리다 —
 * 값을 눌러 넣는 곳이라 안으로 들어간 모양이 뜻과 맞는다.
 */
:root[data-theme="light"] input[type="text"],
:root[data-theme="light"] input[type="password"],
:root[data-theme="light"] input[type="search"],
:root[data-theme="light"] input[type="number"],
:root[data-theme="light"] textarea,
:root[data-theme="light"] select {
  background: #f6f9fc;
  border-color: rgba(60, 66, 87, 0.1);
  box-shadow: inset 0 2px 4px rgba(50, 50, 93, 0.07),
              inset 0 1px 1px rgba(50, 50, 93, 0.04);
}

:root[data-theme="light"] input:focus,
:root[data-theme="light"] textarea:focus,
:root[data-theme="light"] select:focus {
  background: #ffffff;
  box-shadow: 0 0 0 3px var(--focus-ring),
              inset 0 1px 2px rgba(50, 50, 93, 0.05);
}

/*
 * 누를 것은 반대로 <b>도드라진다</b>. 파인 입력 칸과 나란히 놓였을 때
 * 그 차이가 "여기는 넣는 곳, 저기는 누르는 곳"을 말한다.
 */
:root[data-theme="light"] .btn {
  box-shadow: 0 1px 2px rgba(50, 50, 93, 0.1), 0 1px 1px rgba(0, 0, 0, 0.05);
}
:root[data-theme="light"] .btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 3px 7px rgba(50, 50, 93, 0.14), 0 2px 3px rgba(0, 0, 0, 0.06);
}
:root[data-theme="light"] .btn:active {
  transform: translateY(0);
  box-shadow: inset 0 2px 4px rgba(50, 50, 93, 0.12);
}



/*
 * ── 세 번째 판: 네오 브루탈리즘 ─────────────────────────────────────
 *
 * 앞의 두 판과 <b>정반대의 방법</b>으로 깊이를 만든다.
 *
 *   어두운 판·밝은 판   반투명 + 블러 + 은은한 실선 (있는 듯 없는 듯)
 *   이 판              불투명 + <b>두꺼운 검정 테두리</b> + 번지지 않는 그림자
 *
 * 그림자에 blur 가 0 인 것이 이 양식의 전부다 — 면이 <b>종이처럼 잘려</b>
 * 바로 뒤에 또 다른 종이가 어긋나 놓인 모양이다. 부드럽게 번지면 그 순간
 * 그냥 평범한 카드가 된다.
 *
 * <h2>이 저장소의 규칙과 부딪히는 자리</h2>
 *
 * 이 파일 맨 위가 <b>경고에만 색을 쓴다</b>고 못박고 있다. 네오 브루탈리즘은
 * 원색을 넓게 쓰는 것이 정체성이라 그 규칙과 정면으로 부딪힌다.
 *
 * 타협하지 않고 <b>규칙을 지켰다</b>. 색은 조작할 수 있는 자리(버튼·링크)와
 * 경고에만 나오고, 면은 크림색과 흰색이다. 그래서 이 판은 "알록달록한
 * 브루탈리즘"이 아니라 <b>종이와 잉크의 브루탈리즘</b>이 된다 — 판정 배지에
 * 색을 입히는 순간 두 벤치마크에서 잡은 TRUSTED-오답 문제가 되돌아온다.
 *
 * 배경의 노을도 끈다. 그라디언트는 이 양식에 없는 것이고, 대신 원래 있던
 * 청사진 격자를 <b>진하게</b> 살린다 — 모눈종이 위의 종이 카드.
 */
/*
 * <b>기본 판이다.</b> 속성이 하나도 안 적혀 있을 때 이것이 쓰인다
 * (소유자 결정 2026-08-31 — 첫 접속에서 뜨는 판).
 *
 * 그래서 토큰이 :root 에 <b>직접</b> 붙는다. 기본값을 JS 로 정하면
 * (「저장된 것이 없으면 brutal 을 적는다」) 스크립트가 막힌 브라우저에서
 * 다른 판이 뜨고, 무엇보다 <b>CSS 만 읽는 사람이 기본을 알 수 없다</b>.
 */
:root,
:root[data-theme="brutal"] {
  color-scheme: light;

  /* 크림색 종이. 순백은 이 양식에서 너무 매끈하다. */
  --void: #f2ece0;
  --ground: #f5efe4;
  --bg: #faf5ea;
  --card: #ffffff;
  --card-2: #f4eee2;
  --card-solid: #ffffff;
  --panel: #fffdf8;

  /* 잉크. 테두리는 전부 이 색이고, 두께는 아래 규칙이 정한다. */
  --line: #14120f;
  --line-2: #cfc6b4;

  --ink: #14120f;
  --ink-2: #3a352c;
  --dim: #6b6254;

  /*
   * 조작할 수 있는 자리에만 색.
   *
   * 노랑을 고른 이유: 이 양식의 원형(브루탈리스트 웹)이 쓰는 색이면서,
   * 이 화면에서 <b>초록이 아닌</b> 색이다. 초록은 "괜찮다"로 읽히고
   * 이 도구는 그 말을 하지 않는다(파일 맨 위 주석).
   */
  --accent: #ffd93d;
  --accent-ink: #14120f;
  --accent-soft: #fff3c4;
  --focus: #14120f;
  --focus-ring: rgba(20, 18, 15, 0.35);

  --warn-ink: #14120f;
  --warn-bg: #ffd08a;
  --warn-line: #14120f;
  --bad-ink: #14120f;
  --bad-bg: #ff9d9d;
  --bad-line: #14120f;

  /*
   * <b>번지지 않는</b> 그림자. blur 0, 퍼짐 0, 그냥 어긋난 검정 사각형이다.
   * 이 한 줄이 이 판의 서명이다.
   */
  --shadow-1: 4px 4px 0 0 #14120f;
  --shadow-2: 7px 7px 0 0 #14120f;
  --shadow: var(--shadow-1);

  /* 유리가 아니다. 블러를 끈다 — 켜 두면 두꺼운 테두리 안이 흐릿해진다. */
  --glass: none;

  /* 모눈종이. 앞의 두 판보다 <b>진하게</b> — 여기서는 무늬가 재료다. */
  --grid-line: rgba(20, 18, 15, 0.09);
  --grid-size: 26px;

  /* 배경 얼룩은 없다. 그라디언트는 이 양식에 없는 것이다. */
  --horizon-warm: transparent;
  --horizon-cool: transparent;
  --violet-glow: transparent;

  /* 각지게. 완전한 직각은 딱딱해서, 아주 조금만 굴린다. */
  --radius-card: 6px;
  --radius-control: 5px;
}

/*
 * 테두리를 <b>두껍게</b>.
 *
 * 토큰으로는 색만 정해지고 두께는 규칙에 박혀 있다(1px solid var(--line)).
 * 그래서 굵히는 자리를 여기서 짚는다 — <b>바깥 틀만</b>이다. 표의 칸마다
 * 3px 검정을 두르면 자료가 아니라 격자가 읽힌다(어두운 판에서 이미 한 번
 * 겪은 것과 같은 이유).
 */
:root:not([data-theme="dark"]):not([data-theme="light"]) .panel,
:root:not([data-theme="dark"]):not([data-theme="light"]) .side,
:root:not([data-theme="dark"]):not([data-theme="light"]) .top,
:root:not([data-theme="dark"]):not([data-theme="light"]) .btn,
:root:not([data-theme="dark"]):not([data-theme="light"]) .seg,
:root:not([data-theme="dark"]):not([data-theme="light"]) input,
:root:not([data-theme="dark"]):not([data-theme="light"]) textarea,
:root:not([data-theme="dark"]):not([data-theme="light"]) select,
:root:not([data-theme="dark"]):not([data-theme="light"]) .composer,
:root:not([data-theme="dark"]):not([data-theme="light"]) .sheet-grid-wrap,
:root:not([data-theme="dark"]):not([data-theme="light"]) .sheet-table-wrap {
  border-width: 2.5px;
  border-style: solid;
  border-color: var(--line);
}

/* 상자와 누를 것에는 어긋난 그림자를 얹는다. */
:root:not([data-theme="dark"]):not([data-theme="light"]) .panel,
:root:not([data-theme="dark"]):not([data-theme="light"]) .btn,
:root:not([data-theme="dark"]):not([data-theme="light"]) .kpi {
  box-shadow: var(--shadow-1);
}

/*
 * 누르면 <b>그림자 쪽으로 내려앉는다</b>. 이 양식에서 버튼이 눌리는 방식이고,
 * 색이 아니라 위치로 상태를 말하므로 이 도구의 색 규칙과도 어긋나지 않는다.
 */
:root:not([data-theme="dark"]):not([data-theme="light"]) .btn:active {
  transform: translate(4px, 4px);
  box-shadow: 0 0 0 0 var(--line);
}

:root:not([data-theme="dark"]):not([data-theme="light"]) .btn:hover {
  transform: translate(-1px, -1px);
  box-shadow: 5px 5px 0 0 var(--line);
}

/*
 * 굵게 쓴다. 앞의 두 판은 "굵게 하지 않는 것"이 서명이었는데 여기는
 * 반대다 — 목소리가 큰 것이 이 양식이다.
 */
:root:not([data-theme="dark"]):not([data-theme="light"]) h1,
:root:not([data-theme="dark"]):not([data-theme="light"]) h2,
:root:not([data-theme="dark"]):not([data-theme="light"]) h3,
:root:not([data-theme="dark"]):not([data-theme="light"]) .page-title,
:root:not([data-theme="dark"]):not([data-theme="light"]) .side-title,
:root:not([data-theme="dark"]):not([data-theme="light"]) .hero-title {
  font-weight: 800;
  letter-spacing: -0.03em;
}

/* 카드 이름표는 대문자처럼 또렷하게 — 한글이라 자간으로만 표를 낸다. */
:root:not([data-theme="dark"]):not([data-theme="light"]) .panel-title {
  color: var(--ink);
  font-weight: 700;
  letter-spacing: 0.02em;
}

/* 기본 버튼은 노랑 종이 한 장. 이 판에서 <b>유일하게 채워진</b> 색이다. */
:root:not([data-theme="dark"]):not([data-theme="light"]) .btn-primary {
  background: var(--accent);
  color: var(--accent-ink);
}

/* 배경 얼룩을 아예 그리지 않는다. 색이 transparent 라 안 보이지만,
   레이어와 애니메이션까지 지워야 저사양 기계에서 헛일을 안 한다. */
:root:not([data-theme="dark"]):not([data-theme="light"]) body::before,
:root:not([data-theme="dark"]):not([data-theme="light"]) body::after,
:root:not([data-theme="dark"]):not([data-theme="light"]) html::before {
  display: none;
}

/*
 * 알갱이가 그려지는 판. 화면 전체를 덮되 <b>아무것도 가로막지 않는다</b>.
 *
 * z-index 가 머리줄(20)보다 위인 이유: 버튼이 머리줄에도 있고, 거기서 터진
 * 알갱이가 머리줄 뒤로 숨으면 반쪽만 보인다. 대신 pointer-events 가 none 이라
 * 클릭은 그대로 아래로 지나간다.
 */
.particle-layer {
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
}

/*
 * 잠긴 입력칸.
 *
 * readonly 는 <b>브라우저가 아무 표시도 해 주지 않는다</b>. 그래서 추론이
 * 도는 동안 사용자가 그 칸에 계속 타이핑하다 "왜 안 써지지"로 읽는다 —
 * 못 쓴다는 것을 화면이 말해야 한다.
 *
 * 회색으로 죽이지는 않는다. 방금 무엇을 물었는지가 그대로 읽혀야 하고,
 * 그것이 disabled 대신 readonly 를 고른 이유이기도 하다(app.js 주석).
 * 바탕을 한 단계 눌러 앉히고 커서를 바꾸는 것으로 충분하다.
 */
input[readonly],
textarea[readonly],
/*
 * 두 질문칸은 <b>이름으로</b> 한 번 더 짚는다.
 *
 * 이 파일에서 그 둘은 id 로 칠해져 있고(#question, #ask-question), id 는
 * 속성 선택자보다 세다 — 위 두 줄만으로는 잠긴 티가 안 났다(실측: 잠겼는데
 * 배경이 흰색 그대로였다). 잠금이 안 보이면 사용자는 계속 타이핑하다
 * "왜 안 써지지"로 읽는다.
 */
#question[readonly],
#ask-question[readonly] {
  background: var(--card-2);
  cursor: default;
}

* { box-sizing: border-box; }

/*
 * hidden 속성이 실제로 숨긴다.
 *
 * 브라우저 기본 스타일시트의 [hidden] { display: none } 은 <b>작성자</b>
 * 스타일시트의 어떤 규칙에도 진다. 그래서 .toolbar { display: flex } 한 줄이
 * hidden 을 무력화하고 있었다 — 아직 아무것도 묻지 않은 화면에 빈 툴바가
 * 그려진다. 이 규칙 하나로 그 부류 전체를 막는다.
 */
[hidden] { display: none !important; }

/*
 * 라틴 글꼴을 앞에, 한글 글꼴을 뒤에 둔다. 이전 판은 "Malgun Gothic" 이
 * 맨 앞이라 영문·숫자까지 맑은 고딕이 그렸고, 그 글자체가 화면 전체를
 * 낡아 보이게 만들고 있었다 — 이 도구의 화면은 절반이 식별자와 숫자다.
 * 한글은 뒤쪽 맑은 고딕이 그대로 받는다.
 */
/*
 * 바탕은 세 겹이다. 아래에서부터:
 *
 *   1. html   — void 한 색. 불투명하다.
 *   2. body::before / ::after — 오로라. 아주 느리게 떠다니는 빛 덩어리 둘.
 *   3. body   — 청사진 격자. 이 도구가 다루는 것이 격자라서 쓰는 무늬다.
 *
 * WhyWorks 가 실측으로 얻은 두 가지를 그대로 지킨다: filter: blur() 를 쓰지
 * 않는다(화면 크기 오프스크린 버퍼를 만들어 저사양 GPU 에서 레이어가 통째로
 * 사라지는 일이 있다 — radial-gradient 자체의 감쇠로 충분하다), 그리고 색을
 * 매 프레임 다시 칠하지 않고 transform 만 움직인다.
 */
html {
  background: var(--void);
}

body {
  margin: 0;
  padding: 0;
  background-color: transparent;
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: var(--grid-size) var(--grid-size);
  color: var(--ink);
  font-family: var(--ui);

  /*
   * 본문 — <b>모던 고딕</b>.
   *
   * 한글 고딕은 라틴 산세리프보다 글자가 <b>네모에 꽉 찬다</b>. 같은 크기와
   * 같은 줄간격을 주면 한글 쪽이 훨씬 빽빽해 보이고, 오래 읽으면 그 빽빽함이
   * 피로가 된다. 그래서 셋을 함께 손본다:
   *
   *   크기      15 -> 15.5px. 반 픽셀이 우스워 보이지만 한글에서는
   *             15 와 16 사이의 차이가 크다 — 16 은 이 밀도의 도구에서
   *             한 화면에 들어가는 줄 수를 눈에 띄게 줄인다.
   *   줄간격    1.6 -> 1.72. 한글 본문의 표준 자리다. 받침이 있는 줄과
   *             없는 줄이 섞이면 1.6 에서는 줄들이 서로 붙어 보인다.
   *   자간      -0.003em. 아주 조금 당긴다. Noto Sans KR 은 기본 자간이
   *             넉넉한 편이라, 이 정도만 당겨도 낱말 덩어리가 또렷해진다.
   *             더 당기면 받침이 붙어 읽기가 나빠진다.
   *
   * <b>줄이 어디서 넘어가는가</b>는 여기서 정한다.
   *
   * 브라우저 기본(word-break: normal)은 한글을 <b>글자 단위로</b> 끊는다 —
   * 「설정 · 유지관리」가 「유 / 지관리」로 갈린다. 그래서 여백이 남았는데도
   * 개행된 것처럼 보이고, 실제로 그 화면을 보고 「개행 기준을 모르겠다」는
   * 말이 나왔다(2026-08-31). 기준이 없었던 것이 아니라 <낱말을 안 보는>
   * 기준이었다.
   *
   *   word-break: keep-all      어절 안에서는 끊지 않는다. 한글의 규칙이다
   *   overflow-wrap: anywhere   그래도 한 줄에 안 들어가는 것은 끊는다
   *
   * 둘은 짝이다. keep-all 만 두면 categories_2025q4 같은 긴 이름이 상자를
   * 뚫고 나가고, anywhere 만 두면 브라우저 기본과 다를 것이 없다.
   */
  font-size: 15.5px;
  line-height: 1.72;
  letter-spacing: -0.003em;
  word-break: keep-all;
  overflow-wrap: anywhere;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* 숫자가 표에서 자릿수를 맞춰 선다. 계기판의 기본 예의다. */
  font-variant-numeric: tabular-nums;
}

/*
 * 제목은 <b>본문보다 더 당긴다</b>.
 *
 * 고딕 제목이 커질수록 글자 사이가 벌어져 보이는 것은 착시가 아니라
 * 실제로 그렇다 — 자간은 비율이 아니라 절대값으로 붙기 때문이다. 크기에
 * 따라 조금씩 더 당겨야 덩어리로 읽힌다.
 */
h1, h2, h3, .page-title, .hero-title {
  letter-spacing: -0.021em;
  line-height: 1.34;
  word-break: keep-all;
}

body::before,
body::after {
  content: "";
  position: fixed;
  /* 돌리고 키워도 가장자리가 비지 않도록 화면보다 크게 */
  inset: -35%;
  z-index: -1;
  pointer-events: none;
  will-change: transform;
}

/*
 * <b>따뜻한 쪽</b>. 노을의 한쪽 끝이다.
 *
 * 예전에는 따뜻한 색과 찬 색이 <b>같은 레이어에</b> 있었다. 그러면 층을
 * 움직여도 둘이 같이 움직여서 <b>서로의 관계가 한 번도 바뀌지 않는다</b> —
 * 화면 어느 점에서 봐도 색이 그대로라 "움직이지 않는다"로 읽혔다(소유자
 * 지적: "전혀 모르겠어"). 큰 얼룩을 4% 옮기는 것으로는 아무 일도 안 난다.
 *
 * 그래서 <b>갈라 놓았다</b>. 둘이 서로 다른 속도로 서로 다른 방향으로 돌면
 * 경계가 실제로 쓸려 다니고, 그때 비로소 화면에 시간이 흐르는 것이 보인다.
 */
body::before {
  background:
    radial-gradient(ellipse 78% 62% at 12% 10%, var(--horizon-warm), transparent 62%);
  animation: aurora1 22s ease-in-out infinite alternate;
}

/*
 * 밝은 판에서는 메시를 화면 <b>가장자리</b>로 몰아 둔다.
 *
 * 카드가 불투명해서 배경이 보이는 자리가 카드 바깥뿐인데, 그 바깥에 제목과
 * 안내문도 놓인다. 진한 색을 가운데까지 끌고 오면 그 글자들이 색 위에 앉아
 * 읽기 나빠진다 — 색은 모서리에서 번지고 가운데는 흰 종이로 둔다.
 */
:root[data-theme="light"] body::before {
  background:
    radial-gradient(ellipse 52% 44% at 4% 2%, var(--horizon-warm), transparent 66%);
}

:root[data-theme="light"] body::after {
  background:
    radial-gradient(ellipse 54% 46% at 97% 96%, var(--horizon-cool), transparent 66%);
}

:root[data-theme="light"] html::before {
  background:
    radial-gradient(circle at 96% 6%, var(--violet-glow), transparent 42%);
}

/* <b>찬 쪽</b>. 반대 방향으로, 다른 주기로 돈다 — 둘이 절대 맞물리지 않는다. */
body::after {
  background:
    radial-gradient(ellipse 82% 66% at 88% 86%, var(--horizon-cool), transparent 64%);
  animation: aurora2 31s ease-in-out infinite alternate;
}

/*
 * 저녁 보라 빛무리. <b>세 번째 층</b>이라 html 에 붙인다 — body 의
 * 가짜 요소 둘은 노을이 쓴다.
 *
 * 이것만 자리를 옮기지 않고 <b>밝기만</b> 오르내린다. 얼룩 셋이 전부
 * 떠다니면 배경이 소란스러워지는데, 이 도구는 표를 읽는 자리다.
 */
html::before {
  content: "";
  position: fixed;
  inset: -20%;
  z-index: -1;
  pointer-events: none;
  will-change: opacity;
  background:
    radial-gradient(circle at 70% 16%, var(--violet-glow), transparent 46%);
  animation: violet-pulse 17s ease-in-out infinite alternate;
}

/*
 * 움직임을 <b>보이게</b> 키웠다.
 *
 * 예전 값은 ±4%·34초였다. 화면을 덮는 큰 얼룩에서 4% 는 어느 한 점의 색이
 * 거의 안 바뀐다는 뜻이고, 34초에 걸치면 더 그렇다 — 소유자가 "전혀
 * 모르겠어"라고 한 것이 그 값이다. 지금은 ±20% 를 22초·31초에 걸쳐 간다.
 *
 * 둘의 주기가 서로 나누어떨어지지 않는 것이 중요하다(22 와 31). 맞물리면
 * 두 얼룩이 같은 자리로 돌아오는 순간이 생기고, 그때 화면이 "멈췄다"로
 * 읽힌다.
 *
 * 색을 매 프레임 다시 칠하지 않고 transform 만 움직인다(WhyWorks 실측).
 * filter: blur() 도 쓰지 않는다 — 화면 크기 오프스크린 버퍼를 만들어
 * 저사양 GPU 에서 레이어가 통째로 사라지는 일이 있었다.
 */
@keyframes aurora1 {
  0%   { transform: translate3d(-18%, -12%, 0) scale(1.00) rotate(-6deg); }
  50%  { transform: translate3d(16%, 10%, 0) scale(1.30) rotate(9deg); }
  100% { transform: translate3d(-6%, 20%, 0) scale(1.10) rotate(-3deg); }
}

/* 빛무리는 자리를 안 옮기고 <b>밝기만</b> 오르내린다. 얼룩 셋이 전부
   떠다니면 배경이 소란스러워지는데, 이 도구는 표를 읽는 자리다. */
@keyframes violet-pulse {
  0%   { opacity: 0.45; }
  100% { opacity: 1; }
}

/* 찬 쪽은 <b>반대로</b> 간다. 같은 방향이면 둘이 나란히 흘러 관계가 안 바뀐다. */
@keyframes aurora2 {
  0%   { transform: translate3d(20%, 14%, 0) scale(1.18) rotate(5deg); }
  50%  { transform: translate3d(-16%, -12%, 0) scale(1.00) rotate(-10deg); }
  100% { transform: translate3d(10%, -20%, 0) scale(1.26) rotate(7deg); }
}

/* 움직임을 원하지 않는 사람에게는 멈춘 빛만 남는다. 색과 깊이는 그대로다.
   !important 를 붙이는 이유는 미디어 질의가 특정성을 올려 주지 않기 때문이다 —
   나중에 더 구체적인 선택자로 오로라를 다시 움직이게 하는 규칙이 생기면
   이 규칙이 조용히 진다. WaitingStateIT 가 이 파일의 모든 animation 규칙에
   대해 같은 것을 확인한다. */
@media (prefers-reduced-motion: reduce) {
  body::before, body::after, html::before { animation: none !important; }
}

/* 숫자는 어디서나 자릿수가 맞아야 한다. 표에서만 켜면 카드의 요약 숫자가
   흔들리고, 그 흔들림이 "대충 만든 화면"으로 읽힌다. */
.num, td.num, .facts-table td, .spec-facts dd, .standing-sub, .meta, .progress {
  font-variant-numeric: tabular-nums;
}

code, kbd, pre, .mono { font-family: var(--mono); }

/*
 * 머리줄은 화면에 하나뿐인 "고정된 바닥"이다. 스크롤해도 남아 있어야
 * 어느 화면에 있는지, 어디로 갈 수 있는지가 늘 보인다 — 정의서처럼 긴
 * 화면에서 열네 번째 표를 보다가 메뉴로 가려고 맨 위까지 올라가야 하는
 * 것이 지금까지의 모양이었다.
 */
/*
 * ── 앱 셸 ────────────────────────────────────────────────────────────
 *
 * 화면 셋이 서로 다른 골격을 갖고 있었다. 검색만 좌측 패널이 있었고, 어떤
 * 화면은 문서 전체가 스크롤되고 어떤 화면은 안 됐다. 그래서 화면을 넘길
 * 때마다 뼈대가 바뀌었다.
 *
 * 지금은 모든 화면이 같은 셸이다:
 *
 *   머리줄   고정. 브랜드(고정 문구) · GNB · 유틸리티.
 *   좌측     이 화면이 무엇인지, 어디로 뛸 수 있는지. 모든 화면에 있다.
 *   본문     <b>스크롤되는 유일한 자리</b>.
 *
 * 스크롤 주체를 하나로 못박는 것이 요점이다. 문서가 스크롤되면 고정 머리줄이
 * 본문 위에 겹쳐 앉고, 그 겹침이 화면마다 다르게 보인다.
 */
.shell {
  display: grid;
  grid-template-columns: 260px minmax(0, 1fr);
  /* 머리줄 높이를 빼고 나머지 전부. 화면 단위(dvh)를 쓰는 이유는 모바일
     브라우저의 주소창이 접힐 때 vh 가 거짓말을 하기 때문이다. */
  height: calc(100dvh - var(--top-h));
}

/* 긴 메뉴나 버튼이 그리드 열의 최소 폭을 밀어 화면 밖으로 내보내지 않게 한다. */
.shell > * { min-width: 0; }

.side {
  overflow-y: auto;
  padding: 1.1rem 1rem;
  border-right: 1px solid var(--line);
  background: var(--panel);
  backdrop-filter: var(--glass);
  -webkit-backdrop-filter: var(--glass);
}

/*
 * 본문의 자리는 <b>화면마다 같아야 한다</b>.
 *
 * 재 보니 어긋나 있었다(1600px 폭, 소유자 지적 "탭을 옮길 때마다 흔들린다"):
 *
 *   검색            내용 323..1523  (폭 1200)
 *   튜닝·엑셀·ERD   내용 358..1487  (폭 1129)   <- 35px 오른쪽, 71px 좁음
 *
 * 같은 1200 인데 <b>재는 곳이 달랐다</b>. 검색은 .thread > * 에 걸어 내용
 * 폭이 1200 이고, 나머지는 main 자체가 1200 이라 거기서 좌우 여백(56)과
 * 스크롤 자리(15)를 뺀 1129 가 내용 폭이 된다.
 *
 * 그래서 두 곳이 <b>같은 방식으로</b> 재게 맞춘다 — 바깥 칸은 폭을 다 쓰고,
 * 그 안의 자식이 1200 으로 가운데 선다. 검색 화면(.thread)이 원래 그렇게
 * 하고 있었으므로, 나머지를 그쪽에 맞추는 것이다.
 *
 * 이것이 미관 문제가 아닌 이유: 탭을 옮기는 사람은 <b>같은 자리에 같은 것이
 * 있다</b>는 것을 몸으로 기억하고 움직인다. 35px 이 매번 튀면 눈이 매번
 * 다시 찾는다.
 */
.shell > main {
  overflow-y: auto;
  /* 본문만 스크롤한다. 문서는 스크롤하지 않는다. */
  scrollbar-gutter: stable;
  max-width: none;
  margin: 0;
}

/*
 * 대화 화면은 <b>뺀다</b>. 그쪽은 본문이 세로 flex 로 나뉘어 있고
 * (.thread 가 스크롤, .composer 가 바닥에 붙는다) 폭을 묶는 자리가 이미
 * 안쪽(.thread > *)에 있다.
 *
 * 이 예외 없이 걸었더니 .thread 자체가 1200 으로 묶이면서 flex 안에서
 * 983px 로 쪼그라들었고, 결과적으로 검색 화면만 <b>더 좁아졌다</b>(실측
 * 459..1386). 고치려던 것과 반대 방향으로 어긋난 것이다.
 */
.shell > main:not(.conversation) > * {
  max-width: var(--page-max);
  margin-left: auto;
  margin-right: auto;
}

/*
 * 밝은 판의 본문 바닥.
 *
 * 본문이 body 의 <격자 무늬> 위에 바로 놓여 있었다. 카드 안의 글은 흰
 * 바탕을 깔고 있어 멀쩡한데, 카드 <밖>의 글 — 제목·안내문·목록 — 은 그
 * 무늬와 겹쳐 읽기가 나빴다. 옆줄(.side)은 이미 반투명 흰 판을 깔고 있어서
 * 그 차이가 한 화면 안에서 보였다.
 *
 * 그래서 같은 판을 본문에도 깐다. 완전 불투명이 아니라 반투명인 이유는,
 * 이 판의 깊이가 <바닥이 살짝 비치는 것>에서 오기 때문이다 — 흰색으로
 * 덮어 버리면 오로라도 격자도 사라져 화면이 납작해진다.
 *
 * 어두운 판은 건드리지 않는다. 거기서는 글자가 밝고 바닥이 어두워 무늬가
 * 읽기를 방해하지 않는다.
 */
/*
 * 밝은 판의 본문 바닥도 <b>유리 한 장</b>이다.
 *
 * 카드 밖의 글(제목·안내문·목록)이 노을 위에 바로 놓이면 읽기 나쁘다.
 * 그렇다고 흰색으로 덮으면 배경이 사라져 유리가 유리가 아니게 된다.
 * 그래서 아주 옅은 유리를 한 장 깔고, 그 위에 카드 유리가 또 한 장 얹힌다 —
 * 두 겹이 겹치는 자리가 밝은 글래스모피즘의 깊이다.
 */
:root[data-theme="light"] .shell > main {
  /*
   * 본문 바닥은 <b>비워 둔다</b>. 예전에는 여기에 유리를 한 장 깔았는데,
   * 지금은 카드가 불투명한 흰 종이라 바닥까지 하얗게 덮으면 배경의 메시가
   * 통째로 사라진다 — 그림의 그 색이 보이는 자리가 바로 카드 <b>바깥</b>이다.
   */
  background: transparent;
}

/* 옆줄은 흰 종이 한 장. 본문과 같은 재질이고, 오른쪽 그림자로 갈린다. */
:root[data-theme="light"] .side {
  background: #ffffff;
  border-right: 0;
  box-shadow: 1px 0 0 rgba(60, 66, 87, 0.08), 6px 0 22px -12px rgba(50, 50, 93, 0.16);
}

/* 머리줄도 종이다. 스크롤이 그 아래로 지나가는 것을 그림자가 말한다. */
:root[data-theme="light"] .top {
  background: #ffffff;
  border-bottom: 0;
  box-shadow: 0 1px 0 rgba(60, 66, 87, 0.08), 0 6px 22px -14px rgba(50, 50, 93, 0.2);
}

.side-title { margin: 0 0 0.35rem; font-size: 1rem; font-weight: 600; }

/* 옆줄의 설명글. 본문보다 작지만 줄간격은 <더> 준다 — 작은 글씨가
   빽빽하면 읽히지 않고 그냥 회색 덩어리가 된다. */
.side-note { margin: 0 0 0.95rem; font-size: 0.84rem; color: var(--dim); line-height: 1.62; }

.side-links { display: flex; flex-direction: column; gap: 0.1rem; margin-bottom: 1rem; }

.side-links a {
  display: block;
  padding: 0.35rem 0.55rem;
  border-radius: var(--radius-control);
  color: var(--ink-2);
  text-decoration: none;
  font-size: 0.86rem;
  border: 1px solid transparent;
}

.side-links a:hover { background: var(--card-2); color: var(--ink); }
.side-links a.here { background: var(--card); border-color: var(--line); color: var(--ink); font-weight: 600; }
.side-links a:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }

/* 정의서의 표 목차. 열네 줄이라 한 줄에 두 이름을 겹쳐 담는다. */
.side-toc a { display: grid; gap: 0; line-height: 1.3; padding: 0.3rem 0.55rem; }
.side-toc-eng { font-family: var(--mono); font-size: 0.7rem; color: var(--dim); }

.side-facts {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.15rem 0.5rem;
  margin: 0 0 1rem;
  font-size: 0.82rem;
}
.side-facts dt { color: var(--dim); }
.side-facts dd { margin: 0; font-variant-numeric: tabular-nums; }

.side-hint {
  margin: 0;
  font-size: 0.78rem;
  color: var(--dim);
  line-height: 1.55;
  padding-top: 0.8rem;
  border-top: 1px solid var(--line-2);
}

/* 본문의 첫 줄. 화면 제목은 머리줄이 아니라 여기 있다 — 머리줄의 제목은
   길이가 화면마다 달라 GNB 를 밀었다. */
/*
 * 화면 이름. 굵기로 크게 만들지 않는다 — 크기와 자간으로 만든다.
 *
 * 아래 여백이 0.9 -> 1.35rem 인 이유: 제목과 첫 상자가 붙어 있으면 제목이
 * 그 상자의 것처럼 읽힌다. 제목은 <b>화면 전체</b>의 이름이다.
 */
.page-title {
  margin: 0 0 1.35rem;
  font-size: 1.6rem;
  font-weight: 600;
}

@media (max-width: 62rem) {
  .shell { grid-template-columns: 1fr; height: auto; }
  .side { border-right: 0; border-bottom: 1px solid var(--line); }
  .shell > main { overflow: visible; }
}

.top {
  height: var(--top-h);
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.7rem 1.5rem;
  /* 머리줄도 유리다. 아래로 흘러가는 내용이 비쳐야 "떠 있는 줄"로 읽힌다.
     다만 글자가 빽빽하므로 카드보다 불투명하게(--panel). */
  background: var(--panel);
  backdrop-filter: var(--glass);
  -webkit-backdrop-filter: var(--glass);
  border-bottom: 1px solid var(--line);
}

/*
 * 제품 로고. 모든 화면이 fragments/nav.html 의 같은 조각을 쓴다.
 * 화면 제목을 여기에 섞지 않아 GNB의 시작점이 화면마다 움직이지 않는다.
 */
.brand {
  flex: none;
  display: block;
  width: 9rem;
  height: 2.35rem;
  text-decoration: none;
}

.brand:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; border-radius: 4px; }

.brand-logo { display: block; width: 100%; height: 100%; object-fit: contain; object-position: left center; }
.brand-logo-dark { display: none; }
:root[data-theme="dark"] .brand-logo-light { display: none; }
:root[data-theme="dark"] .brand-logo-dark { display: block; }

.period {
  font-size: 0.85rem;
  color: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.1rem 0.7rem;
  background: var(--bg);
}

/*
 * 화면들을 잇는 줄. 조각 하나(templates/fragments/nav.html)가 모든 화면에
 * 그대로 들어가므로 모양도 한 벌이다.
 */
/*
 * 이동 메뉴.
 *
 * <b>overflow 를 걸지 않는다.</b> 예전에는 좁은 화면을 위해 overflow-x: auto
 * 였는데, 그러면 이 안에서 펼친 접힘 메뉴가 <b>통째로 잘린다</b>(실측 —
 * 「데이터 문서화」 목록이 실 한 줄로만 보였다). overflow 는 한 축만 자를
 * 수 없어서, 가로를 자르는 순간 세로로 넘치는 것도 함께 잘린다.
 *
 * 대신 줄이 <b>짧아졌다</b>. 2026-08-31 에 일곱 개가 넷 + 접힘 하나로
 * 줄었고(소유자 결정), 그 폭이면 좁은 화면에서도 아래 미디어 질의가
 * 줄바꿈으로 받아낸다.
 */
.nav {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem;
  align-items: center;
}

/*
 * 메뉴는 밑줄 친 링크가 아니라 <b>탭</b>이다. 링크는 "여기를 누르면 다른
 * 문서로 간다"고 말하고, 탭은 "같은 도구의 다른 화면"이라고 말한다 —
 * 다섯 화면은 후자다.
 *
 * 현재 위치는 여전히 색이 아니라 <b>바탕과 굵기</b>로 표시한다. 이
 * 화면에서 색은 "주의"라는 뜻이고 현재 위치는 주의할 일이 아니라는 규칙은
 * 그대로다.
 */
.nav-link {
  font-size: 0.85rem;
  color: var(--ink-2);
  text-decoration: none;
  white-space: nowrap;
  padding: 0.35rem 0.7rem;
  border-radius: var(--radius-control);
  border: 1px solid transparent;
  transition: background-color 0.12s ease, color 0.12s ease;
}

.nav-link:hover { background: var(--card-2); color: var(--ink); }

/*
 * ── 접히는 메뉴 ──────────────────────────────────────────────────
 *
 * 여닫개는 <b>줄 위에서 자리를 차지하고</b>, 펼쳐진 목록은 그 아래로
 * 떠서 <b>줄의 너비를 건드리지 않는다</b>. 이것이 이 파일이 오래 지켜 온
 * 것과 이어진다 — 머리줄에는 길이가 변하는 것을 두지 않는다(GNB 가
 * 화면마다 223px 씩 움직였던 일).
 */
.nav-menu { position: relative; flex: none; }

/* 기본 삼각형을 지운다. 여닫개가 다른 메뉴와 같은 모양이어야 한다. */
.nav-menu > summary { list-style: none; cursor: pointer; }
.nav-menu > summary::-webkit-details-marker { display: none; }
.nav-menu > summary::marker { content: ""; }

/* 접힘/펼침을 <b>글자로</b> 말한다. 아이콘 하나를 더 들이지 않는다. */
.nav-menu > summary::after { content: " ▾"; opacity: 0.55; }
.nav-menu[open] > summary::after { content: " ▴"; opacity: 0.9; }

.nav-drop {
  position: absolute;
  top: calc(100% + 0.4rem);
  /* 오른쪽 끝에 붙은 여닫개(설정)라 목록도 오른쪽으로 맞춘다. left 로 두면
     화면 밖으로 넘친다. */
  right: 0;
  z-index: 40;
  min-width: 11rem;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: 0.35rem;
  background: var(--card-solid);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-2);
}

.nav-drop a {
  font-size: 0.85rem;
  color: var(--ink-2);
  text-decoration: none;
  white-space: nowrap;
  padding: 0.4rem 0.6rem;
  border-radius: var(--radius-control);
}

/* 줄 <가운데>에 있는 여닫개(데이터 문서화)는 왼쪽으로 편다 — 오른쪽으로
   맞추면 목록이 여닫개보다 왼쪽에서 시작해 무엇에 딸린 것인지 흐려진다. */
.nav-drop-left { right: auto; left: 0; }

.nav-drop a:hover { background: var(--card-2); color: var(--ink); }
.nav-drop a.nav-here { background: var(--accent-soft); color: var(--ink); font-weight: 600; }


.nav-link:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; box-shadow: 0 0 0 4px var(--focus-ring); }

.nav-here {
  background: var(--card);
  border-color: var(--line);
  color: var(--ink);
  font-weight: 600;
}

/* 밝기 토글. 오른쪽 끝, 메뉴 다음이다 — 화면을 고르는 일보다 덜 자주
   쓰는 조작이므로 이동 메뉴보다 뒤에 온다. */
/* 머리줄 오른쪽 구역. 화면마다 내용이 달라도 GNB 를 밀지 않는다 —
   GNB 는 브랜드 바로 뒤에 고정돼 있고, 이 구역만 남는 자리를 갖는다. */
.top-utils {
  margin-left: auto;
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  min-width: 0;
}

/* 좁은 화면에서는 머리글을 세 줄로 나눠 메뉴와 계정 도구가 겹치지 않게 한다.
   메뉴는 한 줄 가로 스크롤로 유지해 항목명이 글자 단위로 접히지 않게 한다. */
@media (max-width: 62rem) {
  .top {
    height: auto;
    position: sticky;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 0.45rem 0.7rem;
    padding: 0.6rem 0.8rem;
  }

  .brand { grid-column: 1; grid-row: 1; align-self: center; }

  .nav {
    grid-column: 1 / -1;
    grid-row: 2;
    width: 100%;
    padding-bottom: 0.15rem;
  }

  .top-utils {
    grid-column: 2;
    grid-row: 1;
    justify-self: end;
    margin-left: 0;
    gap: 0.35rem;
  }

  .who-role { display: none; }
  .who-chip { max-width: 8rem; }
  main { padding: 1rem 1rem 3rem; }
  .thread { padding: 1.25rem 1rem 2rem; }
  .hero { padding-top: 1.25rem; }
}

@media (max-width: 30rem) {
  .top-utils .period { display: none; }
  .who-chip { max-width: 6rem; }
  .who-out .btn { padding-inline: 0.45rem; }
}

.theme-toggle {
  flex: none;
  width: 30px;
  height: 30px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--card);
  color: var(--ink-2);
  cursor: pointer;
  font-size: 0.9rem;
  line-height: 1;
}

.theme-toggle:hover { color: var(--ink); border-color: var(--dim); }
.theme-toggle:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; box-shadow: 0 0 0 4px var(--focus-ring); }

/* 로그인한 사용자와 역할을 표시하는 계정 영역. */
.who { font-size: 0.82rem; color: var(--dim); display: flex; align-items: center; gap: 0.4rem; }

/*
 * 1080px 이었다. 정의서의 컬럼 표는 아홉 칸이라 그 폭에서 순번 칸이
 * 40px 까지 눌렸고(실측), 설명이 네 줄로 접혔다. 관리 도구의 표는 읽는
 * 글이 아니라 훑는 격자다 — 폭을 주는 편이 맞다. 본문 글줄이 너무 길어지는
 * 문제는 .standing-sub / .caveat 이 자기 폭을 따로 잡아 막는다.
 */
/*
 * 본문의 <b>한 가지</b> 폭과 여백.
 *
 * 화면마다 달랐다(소유자 지적). 검색 화면만 .conversation 이 max-width 를
 * 풀고 .thread > * 가 62rem 로 다시 묶어서, 탭을 옮길 때마다 본문의 좌우가
 * 튀었다. 1200px 은 참조의 page max-width 다.
 *
 * 폭이 한 값이어야 하는 이유는 미관이 아니다 — 탭을 옮기는 사람은 <같은
 * 자리에 같은 것이 있다>는 것을 몸으로 기억하고 움직인다. 그 자리가 매번
 * 바뀌면 매번 다시 찾아야 한다.
 */
main { max-width: var(--page-max); margin: 0 auto; padding: 1.5rem 1.75rem 4rem; }

/*
 * 읽는 문장은 <b>전부 같은 폭</b>에서 끊긴다.
 *
 * 목록을 늘려 둔 이유: 예전에는 여기 없는 문단이 상자 폭을 가득 쓰고, 여기
 * 있는 문단은 39자에서 접혔다. 나란히 놓이면 <b>규칙이 없어 보인다</b> —
 * 실제로 규칙이 둘이었다.
 *
 * 새 문단 클래스를 만들면 여기에 넣는다. 폭 값을 직접 적지 않는다.
 */
.standing-line, .standing-sub, .caveat, .lede, .split-lead, .split-next,
.shape-lead, .scale-note, .sessions-note,
.sheet-note, .side-note, .req-lead, .tuning-lead,
.headline, .notes, .conn-note, .spec-note { max-width: var(--measure); }

/* ──────────────────────────────────────────────────────────── 버튼 체계
 *
 * 이전에는 버튼이 저마다 자기 자리에서 자기 모양을 갖고 있었다: 검색의
 * #run 은 채워진 파랑, ERD 의 일곱 버튼은 클래스 없는 맨 <button>,
 * 정의서의 내려받기는 또 다른 .btn-download. 같은 무게의 조작이 화면마다
 * 다르게 생겼고, 다른 무게의 조작(보기 바꾸기 vs 파일 만들기)이 같게
 * 생겼다.
 *
 * 네 가지로 정리한다. 고르는 기준은 <b>되돌릴 수 있는가</b>와
 * <b>이 화면의 목적인가</b>다.
 *
 *   .btn-primary   한 화면에 하나. 그 화면이 존재하는 이유인 조작.
 *                  (질문 보내기)
 *   .btn-secondary 자주 쓰지만 주인공은 아닌 것. 바깥으로 무언가를
 *                  만들어 내는 것도 여기 — 파일 내려받기.
 *   .btn-ghost     보조. 눌러도 아무것도 만들어지지 않고 화면만 바뀐다.
 *   .seg           서로 배타적인 상태를 고르는 묶음(표/차트, 확대/축소).
 */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  font: inherit;
  font-size: 0.86rem;
  font-weight: 500;
  line-height: 1;
  padding: 0.5rem 0.85rem;
  border-radius: var(--radius-control);
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--ink);
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}

.btn:hover { border-color: var(--dim); background: var(--card-2); }
.btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; box-shadow: 0 0 0 4px var(--focus-ring); }
.btn[disabled] { opacity: 0.45; cursor: not-allowed; }

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}
.btn-primary:hover { background: var(--focus); border-color: var(--focus); }

.btn-secondary { background: var(--card); color: var(--ink); }

.btn-ghost { background: transparent; border-color: transparent; color: var(--ink-2); }
.btn-ghost:hover { background: var(--card-2); border-color: var(--line); color: var(--ink); }

.btn-sm { font-size: 0.8rem; padding: 0.35rem 0.6rem; }

/* 파일을 만들어 내는 버튼에는 아래로 향하는 표식을 붙인다. 글자만으로도
   읽히지만, 훑을 때 "이건 무언가를 내려받는 것"이 모양으로 먼저 온다. */
.btn-download::before {
  content: "↓";
  font-size: 0.95em;
  opacity: 0.75;
}

/* 조작 묶음. 한 줄에 여러 버튼이 놓일 때 간격과 정렬을 여기서 한 번만
   정한다 — 화면마다 gap 을 다시 적으면 화면마다 달라진다. */
.btn-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.45rem;
}

.btn-row-end { margin-left: auto; }

/* 묶음 앞의 이름표("내려받기", "보기"). 무엇을 고르는 묶음인지 말한다. */
.btn-row-label {
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: var(--dim);
  margin-right: 0.15rem;
}

/*
 * 상시 문장. 네 화면이 전부 이 파일을 부르므로 정의는 여기 한 곳이다.
 *
 * 앞선 판은 requests.css 와 schemadoc.css 가 같은 이름을 서로 다른 값으로
 * 정의했다 — /requests 에서는 1.05rem/700, /erd 와 /data-guide 에서는
 * 0.95rem/400. 클래스 이름은 하나인데 화면마다 다른 모양이었고, 어느 쪽이
 * 의도인지 파일만 봐서는 알 수 없었다.
 *
 * 무거운 쪽(1.05rem/700)으로 통일한다. "상시 문장"은 정의상 그 화면에서
 * 가장 먼저 읽혀야 하는 줄이다 — /requests 의 "아직 어느 것도 반영되지
 * 않았습니다"가 작아지면 그 화면의 요점이 사라진다. 도식 화면들의 안내
 * 줄이 조금 굵어지는 쪽이 그 반대보다 낫다.
 */
.standing-line {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.5;
  color: var(--ink);
}

.standing-sub {
  margin: 0.55rem 0 0;
  font-size: 0.9rem;
  color: var(--ink-2);
  line-height: 1.6;
}

.standing-sub strong { color: var(--ink); font-weight: 700; }

section, details.panel { margin-bottom: 1rem; }

.panel {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  padding: 1.05rem 1.2rem;
  box-shadow: var(--shadow-1);
  /* 유리. 어두운 판에서는 뒤의 오로라가 비치고, 밝은 판에서는 카드가
     불투명해 아무 일도 하지 않는다 — 값이 하나라 규칙도 하나다. */
  backdrop-filter: var(--glass);
  -webkit-backdrop-filter: var(--glass);
}

/* 화면의 첫 카드는 한 단계 더 떠 있다. 어디부터 읽어야 하는지를 그림자가
   먼저 말한다. */
main > section.panel:first-of-type { box-shadow: var(--shadow-2); }

/* 카드 제목은 이름표다 — 읽는 제목이 아니라 "이 상자가 무엇인지" 하는
   표식이라, 작고 넓게 벌린 글자로 둔다. 계기판의 라벨과 같은 자리다. */
/*
 * 카드 이름표. 읽는 제목이 아니라 "이 상자가 무엇인지" 하는 표식이다.
 *
 * 한글에서 자간을 벌리는 것은 <b>라틴과 다르게</b> 위험하다 — 0.09em 은
 * 낱자가 흩어져 「데 이 터 질 문」처럼 읽힌다. 0.045em 까지만 벌리고,
 * 대신 크기를 조금 올려(0.72 -> 0.78) 이름표다움을 유지한다.
 */
.panel-title {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.045em;
  color: var(--dim);
  text-transform: none;
  margin: 0 0 0.75rem;
}

/* ---------------------------------------------------------------- 질문 입력 */

.composer form { display: flex; gap: 0.5rem; }

#question {
  flex: 1;
  min-width: 0;
  padding: 0.7rem 0.9rem;
  font: inherit;
  /* 색을 적어 둔다. 없으면 브라우저 기본 잉크(color-scheme 에 따라 순백 또는
     순흑)가 쓰이고, 그 색만 화면의 다른 글자와 미세하게 어긋난다. */
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--card);
}

#question::placeholder { color: var(--dim); }

#question:focus { outline: 2px solid var(--focus); outline-offset: -1px; border-color: var(--focus); }

/* 이 화면이 존재하는 이유인 조작 하나. .btn-primary 와 같은 색을 쓰되
   입력칸과 높이를 맞추기 위해 자기 여백을 갖는다. */
#run {
  flex: none;
  padding: 0.7rem 1.5rem;
  font: inherit;
  font-weight: 600;
  color: var(--accent-ink);
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--radius-control);
  cursor: pointer;
  /*
   * white-space: nowrap 을 여기 두지 않는다.
   *
   * 두 가지 이유다. 첫째, 본문이 word-break: keep-all 이라 「보내기」는
   * 어차피 어절 안에서 끊기지 않는다 — 하는 일이 없는 선언이다.
   *
   * 둘째가 진짜다. 이 선택자는 <아이디>라, 스타일시트를 읽어 white-space 를
   * 판정하는 테스트(SchemaDocControllerIT)가 <읽지 못한다>. 그 테스트는
   * 「내가 못 보는 자리에서 이 속성이 정해지고 있지는 않은가」를 묻고,
   * 여기 두는 순간 그 물음의 답이 「있다」가 된다. 실제로 붉어졌다.
   */
  transition: background-color 0.12s ease, border-color 0.12s ease;
}

#run:hover { background: var(--focus); border-color: var(--focus); }
#run:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; box-shadow: 0 0 0 4px var(--focus-ring); }
#run[disabled] { opacity: 0.5; cursor: progress; }

/* "최근 질문" 칩은 없어졌다. 그 자리를 왼쪽 대화방 목록이 대신한다 —
   칩은 새로고침하면 사라졌지만 대화방은 남는다. */

.progress {
  margin: 0.7rem 0 0;
  font-size: 0.88rem;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}

.progress .step { color: var(--dim); }
.progress .step.done { color: var(--ink); }

/* ---------------------------------------------------------------- 신뢰 배지 */

.trust { border-left: 4px solid var(--ink-2); }
.trust.pending { border-left-color: var(--line); }

.trust .headline {
  margin: 0 0 0.5rem;
  font-size: 1.02rem;
  font-weight: 600;
  line-height: 1.5;
}

.trust .headline.muted { font-weight: 500; color: var(--dim); }

/* ------------------------------------------------------- 생각중 (대기 중의 장면)

   왜 이것이 여기 있나.

   소유자가 원한 것은 "생각중..." 과 입체 애니메이션이다. 그런데 이 화면의
   제1규칙은 <b>대기 상태가 끝난 것처럼 보이면 안 된다</b>는 것이고, 아무 말도
   하지 않는 회전판은 멈추는 순간 "다 됐다"로 읽힌다. 그래서 이 절이 지키는 것 다섯.

   1) 이 장면은 카드에서 가장 큰 글자가 아니다. "생각중..." 은 0.95rem 의 제목이고
      "아직 아무것도 검증되지 않았습니다" 문장은 1.02rem/600 이다. 크기가 뒤집히면
      WaitingStateIT 가 붉어진다.
   2) 움직임은 오직 .waiting 아래에서만 일어난다. 기다림이 끝나면 app.js 가 그
      클래스를 떼고, 그 순간 이 파일의 모든 animation 이 걸리지 않는다. 움직임이
      없다는 것 자체가 "지금 아무것도 돌고 있지 않다"는 사실이다.
   3) 어디까지 갔는지를 말한다. data-stage="generating" 은 두 판이 각자 줄을 쓰는
      모습이고, "verifying" 은 쓰기가 멈추고 두 판이 서로 뒤집히는 모습이다.
   4) 배치를 밀지 않는다. 칸은 88×88 로 고정이고 안의 것들은 절대 위치 + transform
      으로만 움직인다 — transform 은 배치를 다시 계산하게 만들지 않는다.
   5) 색으로 말하지 않는다. 여기 쓰인 색은 전부 이미 있던 글자·테두리 회색이다.
      새 색상값이 하나도 없으므로 초록 검사에 새로 노출되는 표면도 없다. */

.thinking { display: flex; align-items: center; gap: 1.1rem; }

.thinking-say { min-width: 0; }

/* 제목이지 판정이 아니다. 아래 문장(1.02rem/600)보다 작게 유지한다. */
.thinking-title {
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0;
  color: var(--ink-2);
  margin: 0 0 0.35rem;
}

.thinking-scene {
  position: relative;
  flex: none;
  width: 88px;
  height: 88px;
  perspective: 260px;
}

.thinking-world {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transform: rotateX(-16deg);
}

/* 두 판이 도는 궤도. */
.thinking-orbit {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
}

/* 모델이 쓰고 있는 질의 한 장. 뒷면도 보인다 — 뒤집히는 것이 요점이다. */
.thinking-plate {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 30px;
  height: 40px;
  margin: -20px 0 0 -15px;
  border: 1px solid var(--ink-2);
  border-radius: 3px;
  background: var(--card);
  transform-style: preserve-3d;
  backface-visibility: visible;
}

.thinking-plate-a { transform: translateX(-22px) translateZ(16px) rotateY(14deg); }
.thinking-plate-b { transform: translateX(22px) translateZ(-16px) rotateY(-14deg); }

/* 판 위의 줄. 질의가 한 줄씩 적히는 자리. */
.thinking-plate > span {
  position: absolute;
  left: 5px;
  height: 2px;
  border-radius: 1px;
  background: var(--line);
  transform-origin: left center;
}

.thinking-plate > span:nth-child(1) { top: 8px; width: 20px; }
.thinking-plate > span:nth-child(2) { top: 15px; width: 14px; }
.thinking-plate > span:nth-child(3) { top: 22px; width: 18px; }

/* 가운데의 질문. 여섯 면을 가진 실제 상자다. */
.thinking-core {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 18px;
  height: 18px;
  margin: -9px 0 0 -9px;
  transform-style: preserve-3d;
}

.thinking-core > span {
  position: absolute;
  inset: 0;
  border: 1px solid var(--dim);
  background: var(--bg);
  opacity: 0.9;
}

.thinking-core > span:nth-child(1) { transform: translateZ(9px); }
.thinking-core > span:nth-child(2) { transform: rotateY(180deg) translateZ(9px); }
.thinking-core > span:nth-child(3) { transform: rotateY(90deg) translateZ(9px); }
.thinking-core > span:nth-child(4) { transform: rotateY(-90deg) translateZ(9px); }
.thinking-core > span:nth-child(5) { transform: rotateX(90deg) translateZ(9px); }
.thinking-core > span:nth-child(6) { transform: rotateX(-90deg) translateZ(9px); }

@keyframes thinking-orbit-spin {
  from { transform: rotateY(0deg); }
  to { transform: rotateY(360deg); }
}

@keyframes thinking-core-tumble {
  from { transform: rotateX(0deg) rotateY(0deg); }
  to { transform: rotateX(360deg) rotateY(-360deg); }
}

/* 줄이 한 줄씩 길어졌다 줄어든다 — 쓰는 중이라는 뜻이다. width 가 아니라
   scaleX 로 한다: transform 은 배치를 다시 계산하지 않는다. */
@keyframes thinking-write {
  0%, 100% { transform: scaleX(0.2); }
  50% { transform: scaleX(1); }
}

/* 검토 — 한 판이 통째로 뒤집혀 뒷면을 보인다. 자리는 그대로다(앞의
   translate 를 그대로 안고 rotateY 만 돈다). */
@keyframes thinking-review-a {
  0%, 100% { transform: translateX(-22px) translateZ(16px) rotateY(14deg); }
  50% { transform: translateX(-22px) translateZ(16px) rotateY(194deg); }
}

@keyframes thinking-review-b {
  0%, 100% { transform: translateX(22px) translateZ(-16px) rotateY(-14deg); }
  50% { transform: translateX(22px) translateZ(-16px) rotateY(166deg); }
}

/* ── 움직임은 .waiting 안에서만 일어난다 ── */

.waiting .thinking-orbit { animation: thinking-orbit-spin 7s linear infinite; }
.waiting .thinking-core { animation: thinking-core-tumble 9s linear infinite; }

.waiting[data-stage="generating"] .thinking-plate > span {
  animation: thinking-write 2.4s ease-in-out infinite;
}

.waiting[data-stage="generating"] .thinking-plate > span:nth-child(2) {
  animation-delay: 0.35s;
}

.waiting[data-stage="generating"] .thinking-plate > span:nth-child(3) {
  animation-delay: 0.7s;
}

.waiting[data-stage="generating"] .thinking-plate-b > span { animation-delay: 0.5s; }

/* 검토 단계. 궤도가 빨라지고, 쓰기가 멈추고, 두 판이 서로 뒤집힌다. */
.waiting[data-stage="verifying"] .thinking-orbit { animation-duration: 4.2s; }

.waiting[data-stage="verifying"] .thinking-plate-a {
  animation: thinking-review-a 4.2s ease-in-out infinite;
}

.waiting[data-stage="verifying"] .thinking-plate-b {
  animation: thinking-review-b 4.2s ease-in-out infinite;
  animation-delay: 2.1s;
}

/* 기다림이 결과 없이 끝났을 때. 판이 눕고 장면이 흐려진다 — 색이 아니라
   자세와 정지로 말한다. 칸 크기는 그대로라 배치가 움직이지 않는다. */
.trust.pending:not(.waiting) .thinking-scene { opacity: 0.5; }

.trust.pending:not(.waiting) .thinking-plate-a {
  transform: translateX(-22px) translateZ(16px) rotateX(84deg);
}

.trust.pending:not(.waiting) .thinking-plate-b {
  transform: translateX(22px) translateZ(-16px) rotateX(84deg);
}

/*
 * 움직임을 원하지 않는 사람에게.
 *
 * 장면은 그대로 있되 한 프레임에서 멈춘다 — 입체는 남고 회전만 사라진다.
 * 그래도 이 화면이 살아 있다는 것은 여전히 말해진다: 카드 <b>바깥</b>의
 * .turn-progress 가 200ms 마다 초를 다시 적기 때문이다. 그것은 움직임이 아니라
 * 글자라서 여기서 끄지 않는다.
 *
 * 여기 이름이 빠진 채로 새 animation 이 붙으면 WaitingStateIT 가 붉어진다.
 *
 * !important 가 필요하다. 위 규칙들은 .waiting[data-stage="…"] 를 앞에 달고 있어
 * 특정도가 더 높다 — 미디어 질의는 특정도를 올려 주지 않으므로, 그것 없이는 이
 * 블록이 <b>조용히 지고</b> 움직임을 원하지 않는 사람에게 그대로 돌아간다.
 */
@media (prefers-reduced-motion: reduce) {
  .thinking-orbit,
  .thinking-core,
  .thinking-plate,
  .thinking-plate-a,
  .thinking-plate-b,
  .thinking-plate > span {
    animation: none !important;
  }
}

.notes { margin: 0 0 0.6rem; padding-left: 1.1rem; color: var(--ink-2); }
.notes li { margin: 0.15rem 0; }

.facts {
  margin: 0.6rem 0 0;
  padding: 0.6rem 0 0;
  border-top: 1px solid var(--line);
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.2rem 0.9rem;
  font-size: 0.88rem;
}

.facts dt { color: var(--dim); }
.facts dd { margin: 0; color: var(--ink-2); }

/*
 * Task 9(화면 검토): 이 문단은 화면에서 "이 숫자를 얼마나 믿어야 하는가"를 말하는
 * 유일한 자리인데, 패널에서 가장 흐리고 가장 작은 글자였다(0.82rem, --dim).
 * 판정 문장은 1.02rem/600으로 크게 나가고, 그 판정이 정답 보증이 아니라는 사실은
 * 그 아래 가장 옅은 회색으로 붙는다 — 무게가 정반대로 실려 있었다. 경고색은
 * 여전히 쓰지 않는다(색이 있으면 주의, 없으면 안심이라는 규칙을 깨지 않기 위해).
 * 크기와 명도만 본문 수준으로 올린다.
 */
.caveat { margin: 0.7rem 0 0; font-size: 0.87rem; color: var(--ink-2); line-height: 1.6; }
.caveat strong { color: var(--ink); font-weight: 700; }
.caveat code {
  font-family: Consolas, "D2Coding", monospace;
  font-size: 0.95em;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0 0.25rem;
}

/* ---------------------------------------------------------------- 결과 툴바 */

.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.5rem 0.2rem;
}

.views { display: flex; gap: 0.35rem; }

.seg {
  font: inherit;
  font-size: 0.88rem;
  padding: 0.3rem 0.9rem;
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--ink-2);
  border-radius: var(--radius-control);
  cursor: pointer;
}

.seg.on { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.seg[disabled] { opacity: 0.4; cursor: not-allowed; }

.meta { display: flex; flex-wrap: wrap; gap: 0.9rem; font-size: 0.86rem; color: var(--ink-2); }
.meta .dim, .dim { color: var(--dim); }

.warn {
  color: var(--warn-ink);
  background: var(--warn-bg);
  border: 1px solid var(--warn-line);
  border-radius: var(--radius-control);
  padding: 0 0.5rem;
}

/* ---------------------------------------------------------------- 그리드 */

.result { padding: 0; overflow: hidden; }

.grid { overflow: auto; max-height: 26rem; }

table { border-collapse: collapse; width: 100%; font-size: 0.9rem; }

th, td {
  border-bottom: 1px solid var(--line);
  padding: 0.4rem 0.75rem;
  text-align: left;
  white-space: nowrap;
}

th {
  position: sticky;
  top: 0;
  background: var(--bg);
  font-weight: 600;
  font-size: 0.82rem;
  color: var(--ink-2);
}

td.num { text-align: right; font-variant-numeric: tabular-nums; }
td.null { color: var(--dim); font-style: italic; }

.no-rows { padding: 1.5rem; color: var(--dim); }

.kpis { display: flex; flex-wrap: wrap; gap: 1rem; padding: 1.15rem; }

.kpi {
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  padding: 0.7rem 1.2rem;
  min-width: 9rem;
}

.kpi .k { font-size: 0.8rem; color: var(--dim); }
.kpi .v { font-size: 1.7rem; font-weight: 700; font-variant-numeric: tabular-nums; }

.chart { height: 22rem; padding: 0.5rem; }

/* ---------------------------------------------------------------- 변경 요청 */

/*
 * 이 패널에도 "괜찮다"는 뜻의 초록색이 없다. 요청이 접수돼도 데이터는 그대로이기
 * 때문에, 접수 카드는 안심시키는 색을 쓰지 않고 중립 카드로 나온다. 거부·실패에만
 * 경고색이 붙는다 — 화면 전체의 규칙과 같다.
 *
 * 누를 수 있는 셀은 점선 밑줄과 커서로만 표시한다. 색으로 표시하면 "이 값이
 * 특별하다"는 다른 뜻으로 읽힌다.
 */

td.askable { cursor: pointer; text-decoration: underline dotted var(--dim); text-underline-offset: 3px; }
td.askable:hover { background: var(--bg); }
td.askable:focus { outline: 2px solid var(--accent); outline-offset: -2px; }
td.asking { background: var(--bg); box-shadow: inset 0 0 0 2px var(--ink-2); }

.request-blocked-head, .request-lead {
  margin: 0;
  font-size: 0.95rem;
  color: var(--ink);
  line-height: 1.55;
}

.request-lead strong { font-weight: 700; }

/* 사유 문장이 다음에 할 일까지 담게 되면서 길어졌다(최종 리뷰 F1) — 줄
   간격을 다른 안내 문단과 맞춘다. */
.request-reason {
  margin: 0.45rem 0 0;
  padding: 0.5rem 0.75rem;
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--ink-2);
  background: var(--bg);
  border: 1px solid var(--line);
  border-left: 3px solid var(--ink-2);
  border-radius: var(--radius-control);
}

.request-columns { margin: 0.45rem 0 0; font-size: 0.87rem; color: var(--ink-2); line-height: 1.55; }

.request-form { margin-top: 0.9rem; border-top: 1px solid var(--line); padding-top: 0.8rem; }

.request-form-title { font-size: 0.9rem; margin: 0.2rem 0 0.5rem; color: var(--ink); font-weight: 700; }

.request-target {
  margin: 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.2rem 0.9rem;
  font-size: 0.88rem;
}

.request-target dt { color: var(--dim); }
.request-target dd { margin: 0; color: var(--ink-2); }

.request-input {
  margin-top: 0.7rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.88rem;
}

.request-input input[type="text"] {
  font: inherit;
  padding: 0.35rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--card);
  min-width: 16rem;
}

.request-input input[type="text"][disabled] { background: var(--bg); color: var(--dim); }

.request-null { display: flex; align-items: center; gap: 0.3rem; color: var(--ink-2); }

.request-null-note, .request-sentence-note {
  margin: 0.45rem 0 0;
  font-size: 0.85rem;
  color: var(--ink-2);
  line-height: 1.55;
}

.request-null-note code {
  font-family: Consolas, "D2Coding", monospace;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0 0.25rem;
}

.request-buttons { margin-top: 0.8rem; display: flex; gap: 0.5rem; }

.request-buttons button {
  font: inherit;
  font-size: 0.9rem;
  padding: 0.4rem 1.1rem;
  color: var(--accent-ink);
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--radius-control);
  cursor: pointer;
}

.request-buttons button.alt { color: var(--ink-2); background: var(--card); border-color: var(--line); }
.request-buttons button[disabled] { opacity: 0.5; cursor: progress; }

.request-result {
  margin-top: 0.9rem;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--ink-2);
  border-radius: var(--radius-control);
  background: var(--bg);
}

.request-result-warn {
  border-color: var(--warn-line);
  border-left-color: var(--warn-ink);
  background: var(--warn-bg);
}

.request-result-head { margin: 0; font-size: 0.98rem; font-weight: 700; color: var(--ink); line-height: 1.5; }
.request-result-body { margin: 0.45rem 0 0; font-size: 0.93rem; color: var(--ink); line-height: 1.55; }
.request-result-note { margin: 0.45rem 0 0; font-size: 0.86rem; color: var(--ink-2); line-height: 1.55; }
.request-result-link { margin: 0.5rem 0 0; font-size: 0.88rem; }
.request-result-link a { color: var(--accent); }

/* ---------------------------------------------------------------- 빈 결과 힌트 */

.hint { border-left: 4px solid var(--warn-line); background: var(--warn-bg); }
.hint .panel-title { color: var(--warn-ink); }
.hint code { background: var(--card); border: 1px solid var(--warn-line); border-radius: 4px; padding: 0 0.3rem; }

.chips { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-top: 0.5rem; }

.chips span {
  font-size: 0.85rem;
  background: var(--card);
  border: 1px solid var(--warn-line);
  border-radius: 999px;
  padding: 0.05rem 0.65rem;
}

/*
 * Task 9(화면 검토): 힌트는 "왜 비었는지"까지만 말하고 멈춰 있었다. 이 화면의
 * 사용자는 SQL을 고칠 수 없으므로, 실제로 할 수 있는 유일한 행동(값을 그대로
 * 옮겨 다시 묻기)을 문장으로 적어 준다.
 */
.hint-next { margin: 0.7rem 0 0; font-size: 0.88rem; color: var(--warn-ink); line-height: 1.6; }
.hint-next em { font-style: normal; text-decoration: underline; }

/* ---------------------------------------------------------------- 질의 도식 */

/*
 * Task 9(화면 검토): 도식은 사용자가 SQL 없이 볼 수 있는 유일한 "질의의 모습"이라
 * 무의식적으로 검증 도구처럼 읽힌다. 실측한 화면에서 마지막 단계가 14행인데 표는
 * 0행이었고, 필터가 아무것도 못 찾은 단계는 1행으로 적혀 있었다(계획의 추정치는
 * 0으로 내려가지 않는다). 그래서 도식 위에 이 도식이 무엇이 아닌지부터 적는다.
 */
.shape-lead { margin: 0 0 0.7rem; font-size: 0.88rem; color: var(--ink-2); line-height: 1.55; }
.shape-lead strong { color: var(--ink); font-weight: 700; }

.stages { list-style: none; margin: 0; padding: 0; }

.stages li {
  display: grid;
  grid-template-columns: minmax(8rem, 14rem) 1fr minmax(7rem, max-content);
  align-items: center;
  gap: 0.75rem;
  padding: 0.28rem 0;
}

.stages .lbl { font-size: 0.9rem; }

.stages .track {
  height: 0.85rem;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 3px;
  overflow: hidden;
}

.stages .fill { height: 100%; background: var(--ink-2); }

.stages .fill.approx {
  background: repeating-linear-gradient(
    45deg, var(--ink-2), var(--ink-2) 4px, var(--line) 4px, var(--line) 8px);
}

.stages .track.unknown {
  background: repeating-linear-gradient(
    45deg, var(--card), var(--card) 5px, var(--line) 5px, var(--line) 10px);
}

.stages .rows { font-size: 0.86rem; text-align: right; font-variant-numeric: tabular-nums; }
.stages .rows.unknown { color: var(--dim); }

.stages .arrow { color: var(--line); font-size: 0.8rem; }

.tag-approx, .tag-unknown {
  font-size: 0.72rem;
  border-radius: 4px;
  padding: 0 0.3rem;
  margin-left: 0.3rem;
  border: 1px solid var(--line);
  color: var(--ink-2);
  background: var(--bg);
  white-space: nowrap;
}

/*
 * fix round 3 / C: 로그 눈금 안내는 바로 아래의 상시 주의문과 같은 옷을 입고 있었다
 * (같은 class, 13.12px, 같은 회색, 굵기 400). 흐린 회색 문단 두 개가 연달아 있으면
 * 그중 하나만 "이번 도식에서는 막대 길이의 뜻이 달라졌다"는 신호라는 걸 알아볼 수가
 * 없다. 그래서 이 문단만 더 진하게, 조금 더 크게, 왼쪽 강조선이 있는 상자로 뺀다.
 * 경고가 아니라 읽는 법 안내이므로 경고색은 쓰지 않는다.
 */
.scale-note {
  margin: 0.8rem 0 0;
  padding: 0.55rem 0.8rem;
  font-size: 0.88rem;
  line-height: 1.55;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line);
  border-left: 3px solid var(--ink-2);
  border-radius: var(--radius-control);
}

.scale-note-lead {
  display: block;
  font-weight: 700;
  font-size: 0.84rem;
  letter-spacing: 0.02em;
  color: var(--ink);
  margin-bottom: 0.2rem;
}

.scale-note strong { font-weight: 700; }

.joins { margin: 0.6rem 0 0; padding-left: 1.1rem; font-size: 0.86rem; color: var(--ink-2); }

/* ---------------------------------------------------------------- SQL 패널 */

details.sql summary { cursor: pointer; font-size: 0.9rem; color: var(--ink-2); font-weight: 600; }

.sql-body { margin-top: 0.8rem; }

.sql-block { margin-bottom: 1rem; }

.sql-block h4 { font-size: 0.82rem; margin: 0 0 0.35rem; color: var(--dim); font-weight: 700; }

pre {
  margin: 0;
  padding: 0.7rem 0.85rem;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  overflow-x: auto;
  font-family: Consolas, "D2Coding", monospace;
  font-size: 0.82rem;
  line-height: 1.55;
  white-space: pre-wrap;
  word-break: break-word;
}

.sql-block ul { margin: 0; padding-left: 1.1rem; font-size: 0.88rem; color: var(--ink-2); }
.sql-block li { margin: 0.2rem 0; }
.sql-block li .who { font-weight: 600; color: var(--ink); }

/* ---------------------------------------------------------------- 실패 */

.failure { border-left: 4px solid var(--bad-line); background: var(--bad-bg); }
.failure .failure-title { font-size: 0.95rem; margin: 0 0 0.4rem; color: var(--bad-ink); }
.failure p { margin: 0 0 0.5rem; }

/* ================================================================
 * 대화형 화면 (/) — 명세 14장
 *
 * 왼쪽 대화방 목록, 오른쪽 대화. 색은 여기서도 아무 판정을 말하지 않는다.
 * 이 절에는 초록이 없고, 두 모델이 갈린 카드에도 경고색을 쓰지 않는다 —
 * "갈렸다"는 사실은 문장과 두 표가 나란히 놓인 배치 자체가 말한다.
 * ================================================================ */

/* 검색 화면도 이제 공용 셸(.shell)을 쓴다. 예전에는 이 화면만 .workspace 라는
   자기 골격을 갖고 있었고, 그래서 좌측 패널이 여기에만 있었다. */
.chat-body { padding-bottom: 0; }

/* ---------------------------------------------------------------- 대화방 목록 */

/* 대화방 목록은 .side 의 변형이다. 바탕·테두리·스크롤은 .side 가 이미 정했고,
   여기서는 이 목록에만 필요한 세로 배치만 더한다. */
.sessions {
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.sessions-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }

.sessions-title {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--dim);
}

/* 생김새는 .btn .btn-secondary .btn-sm 가 한다. 여기 남는 것은 이 버튼에만
   있는 사실 하나 — 눌리면 서버를 기다리므로 커서가 진행 중을 말한다. */
.new-session[disabled] { cursor: progress; }

.session-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.2rem; }

.session-list li { display: flex; align-items: stretch; gap: 0.2rem; }

.session-open {
  flex: 1;
  min-width: 0;
  text-align: left;
  font: inherit;
  font-size: 0.86rem;
  color: var(--ink-2);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  padding: 0.35rem 0.5rem;
  cursor: pointer;
  line-height: 1.35;
}

.session-open:hover { background: var(--bg); }

/* 열려 있는 방. 색이 아니라 배경과 굵기로 표시한다. */
.session-open.here { background: var(--bg); border-color: var(--line); color: var(--ink); font-weight: 700; }

.session-name { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.session-sub { display: block; font-size: 0.76rem; color: var(--dim); font-weight: 400; }

.session-delete {
  font: inherit;
  font-size: 0.8rem;
  color: var(--dim);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  padding: 0 0.4rem;
  cursor: pointer;
}

.session-delete:hover { color: var(--ink); border-color: var(--line); background: var(--bg); }

.sessions-empty, .sessions-note {
  margin: 0;
  font-size: 0.78rem;
  color: var(--dim);
  line-height: 1.55;
}

.sessions-note { margin-top: auto; padding-top: 0.6rem; border-top: 1px solid var(--line); }
.sessions-note strong { color: var(--ink-2); font-weight: 700; }

/* ---------------------------------------------------------------- 대화 본문 */

.conversation {
  min-height: 0;
  max-width: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.thread { flex: 1; min-height: 0; overflow-y: auto; padding: 1.5rem 1.75rem 2rem; }

/* 대화 줄기도 다른 화면의 본문과 <같은 폭>이다. 위 주석 참고. */
.thread > * { max-width: var(--page-max); margin-left: auto; margin-right: auto; }

/*
 * 첫 화면. 입력창이 여기(가운데) 있다가, 첫 질문을 던지는 순간 app.js 가
 * #composer 를 대화 아래로 옮긴다. 그래서 "내려갔다"는 것은 클래스 토글이
 * 아니라 부모가 실제로 바뀌는 일이고, 화면을 못 보는 자리에서도
 * parentElement 로 확인할 수 있다.
 */
.hero { padding: 2.5rem 0 1rem; }

.hero-title { margin: 0 0 0.7rem; font-size: 1.85rem; font-weight: 600; }

.hero-lead { margin: 0 0 1.4rem; font-size: 0.95rem; color: var(--ink-2); line-height: 1.65; }
.hero-lead strong { color: var(--ink); font-weight: 700; }

.hero-composer-slot { margin-bottom: 1.6rem; }

.examples { border-top: 1px solid var(--line); padding-top: 1.1rem; }

.examples-title { margin: 0 0 0.6rem; font-size: 0.8rem; font-weight: 700; letter-spacing: 0.04em; color: var(--dim); }

.example-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  gap: 0.4rem;
}

.example {
  width: 100%;
  text-align: left;
  font: inherit;
  font-size: 0.9rem;
  color: var(--ink-2);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  padding: 0.55rem 0.8rem;
  cursor: pointer;
  line-height: 1.45;
}

.example:hover { border-color: var(--ink-2); color: var(--ink); }

.examples-note { margin: 0.8rem 0 0; font-size: 0.85rem; color: var(--ink-2); line-height: 1.6; }
.examples-note strong { color: var(--ink); font-weight: 700; }

/* ---------------------------------------------------------------- 입력창 */

/*
 * 첫 결과 뒤 입력창은 .conversation 의 마지막 자식이 되고, 그때 이 규칙이
 * 걸린다(.hero-composer-slot 안에 있는 동안에는 걸리지 않는다). 대화가
 * 그 위로 쌓인다.
 */
.conversation > .composer {
  flex: none;
  padding: 0.8rem 1.5rem 1rem;
  background: var(--card);
  border-top: 1px solid var(--line);
}

.conversation > .composer form { max-width: 62rem; margin: 0 auto; }
.conversation > .composer .composer-status { max-width: 62rem; margin: 0.5rem auto 0; }

.composer-status { margin: 0.5rem 0 0; font-size: 0.86rem; color: var(--ink-2); line-height: 1.55; }

.composer-status.composer-warn {
  color: var(--warn-ink);
  background: var(--warn-bg);
  border: 1px solid var(--warn-line);
  border-radius: var(--radius-control);
  padding: 0.4rem 0.7rem;
}

/* ---------------------------------------------------------------- 턴 */

.turns { display: flex; flex-direction: column; gap: 1.6rem; }

.turn { border-top: 1px solid var(--line); padding-top: 1.1rem; }
.turn:first-child { border-top: none; padding-top: 0; }

.asked { display: flex; align-items: baseline; gap: 0.7rem; margin-bottom: 0.8rem; }

.asked .q {
  margin: 0;
  font-size: 1.08rem;
  font-weight: 700;
  line-height: 1.5;
  color: var(--ink);
}

.asked-at { font-size: 0.78rem; white-space: nowrap; }

.answer { display: flex; flex-direction: column; }
.answer > * { margin-bottom: 0.8rem; }
.answer > *:last-child { margin-bottom: 0; }

.turn-progress { margin: 0.5rem 0 0; }

/* ---------------------------------------------------------------- 갈린 두 답 */

/*
 * 경고색을 쓰지 않는다. 이 카드가 뜬다는 것 자체가 "확인이 필요하다"는
 * 뜻이지만, 색을 붙이면 반대로 색이 없는 카드가 "안심"으로 읽힌다 —
 * 이 화면 전체가 피하는 바로 그 읽기다. 무게는 배치와 문장이 싣는다.
 */
.split { border-left: 4px solid var(--ink-2); }

.split-lead { margin: 0 0 0.9rem; font-size: 0.95rem; color: var(--ink); line-height: 1.6; }
.split-lead strong { font-weight: 700; }

.split-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }

.split-col {
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  padding: 0.7rem 0.8rem;
  background: var(--bg);
}

.split-head {
  margin: 0 0 0.15rem;
  font-size: 0.88rem;
  font-weight: 700;
  color: var(--ink);
  line-height: 1.4;
  word-break: break-word;
}

.split-count { margin: 0 0 0.5rem; font-size: 0.8rem; }

.split-grid {
  overflow: auto;
  max-height: 18rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
}

.split-grid table { font-size: 0.85rem; }
.split-grid th { background: var(--bg); }

.split-sql { margin-top: 0.5rem; }
.split-sql summary { cursor: pointer; font-size: 0.82rem; color: var(--ink-2); }
.split-sql pre { margin-top: 0.4rem; background: var(--card); }

.split-next { margin: 0.9rem 0 0; font-size: 0.9rem; color: var(--ink-2); line-height: 1.6; }
.split-next strong { color: var(--ink); font-weight: 700; }
.split-next em { font-style: normal; text-decoration: underline; }

/* ---------------------------------------------------------------- 접히는 패널 */

details.panel > summary {
  cursor: pointer;
  font-size: 0.9rem;
  color: var(--ink-2);
  font-weight: 600;
  line-height: 1.5;
}

details.panel[open] > summary { margin-bottom: 0.6rem; }

.shape-body { padding-top: 0.2rem; }

.request-body { padding-top: 0.2rem; }

.request-new-label { display: flex; align-items: center; gap: 0.4rem; color: var(--ink-2); }

.chart-blocked { padding: 1.1rem; font-size: 0.88rem; line-height: 1.6; }

/* ---------------------------------------------------------------- 좁은 화면 */

@media (max-width: 900px) {
  .sessions { max-height: 12rem; }
  .split-cols { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------------ 실행 전 확인 */

/*
 * 이 카드에 초록이 없는 것은 다른 카드와 같은 이유다(StylesheetHygieneIT).
 * 여기서는 이유가 하나 더 있다 — "실행" 버튼을 눈에 띄게 칠하면 그 색이
 * 곧 권유가 된다. 확인과 그만두기는 같은 크기로, 같은 무게로 놓는다.
 *
 * 왼쪽 띠는 --bad-line 이다. 경고가 아니라 <b>되돌릴 수 없음</b>의 표시이고,
 * 이 카드는 그 성질을 가진 유일한 카드다.
 */
.mutation {
  border-left: 4px solid var(--bad-line);
}
.mutation-headline {
  margin: 0 0 0.4rem;
  font-size: 1.02rem;
  font-weight: 700;
  color: var(--ink);
  text-transform: none;
  letter-spacing: 0;
}
.mutation-comparison {
  margin: 0 0 0.9rem;
  font-size: 0.9rem;
  color: var(--ink-2);
  line-height: 1.6;
}
.mutation-sentence {
  margin: 0;
  padding: 0.7rem 0.85rem;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 4px;
  font-size: 1.02rem;
  font-weight: 700;
  color: var(--ink);
  line-height: 1.55;
}
.mutation-warnings {
  margin: 0.7rem 0 0;
  padding: 0.6rem 0.85rem 0.6rem 1.9rem;
  background: var(--warn-bg);
  border: 1px solid var(--warn-line);
  border-radius: 4px;
  color: var(--warn-ink);
  font-size: 0.9rem;
  line-height: 1.6;
}
.mutation-warnings li { margin: 0.2rem 0; }
.mutation-sql { margin-top: 0.7rem; font-size: 0.86rem; color: var(--ink-2); }
.mutation-sql summary { cursor: pointer; color: var(--dim); }
.mutation-sql pre {
  margin: 0.5rem 0 0;
  padding: 0.7rem 0.8rem;
  overflow-x: auto;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 4px;
  color: var(--ink);
  white-space: pre-wrap;
  word-break: break-word;
}
.mutation-other {
  margin-top: 1rem;
  padding-top: 0.9rem;
  border-top: 1px dashed var(--line);
}
.mutation-other-title {
  margin: 0 0 0.4rem;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--ink-2);
}
.mutation-other-sentence { margin: 0; font-size: 0.94rem; color: var(--ink-2); line-height: 1.55; }
.mutation-decide { margin-top: 1rem; padding-top: 0.9rem; border-top: 1px solid var(--line); }
.mutation-actor-note { margin: 0 0 0.7rem; font-size: 0.85rem; color: var(--dim); line-height: 1.6; }
.mutation-actor-note strong { color: var(--ink-2); font-weight: 700; }
.mutation-buttons { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.mutation-buttons button {
  padding: 0.55rem 1rem;
  font: inherit;
  font-size: 0.92rem;
  cursor: pointer;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--ink-2);
  border-radius: 4px;
}
.mutation-buttons button.alt { color: var(--ink-2); border-color: var(--line); }
.mutation-buttons button[disabled] { opacity: 0.5; cursor: progress; }
.mutation-decide-note { margin: 0.6rem 0 0; font-size: 0.85rem; color: var(--bad-ink); }
.mutation-decide-note strong { color: var(--ink-2); }

/* 구조 변경일 때만 열리는 사유 칸. 실행 버튼 위에 둔다 — 아래에 두면
   버튼을 먼저 누르고 나서 못 채운 칸을 찾게 된다. */
.mutation-reason { margin: 0 0 0.9rem; }
.mutation-reason label {
  display: block;
  margin-bottom: 0.35rem;
  font-size: 0.88rem;
  font-weight: 700;
  color: var(--ink-2);
}
.mutation-reason-text {
  width: 100%;
  padding: 0.5rem 0.6rem;
  font: inherit;
  font-size: 0.92rem;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 4px;
  resize: vertical;
  box-sizing: border-box;
}
.mutation-reason-note { margin: 0.35rem 0 0; font-size: 0.83rem; color: var(--dim); line-height: 1.6; }
.mutation-result {
  margin-top: 1rem;
  padding: 0.8rem 0.9rem;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 4px;
}
.mutation-result-head { margin: 0; font-size: 0.98rem; font-weight: 700; color: var(--ink); }
.mutation-result-body { margin: 0.35rem 0 0; font-size: 0.9rem; color: var(--ink-2); line-height: 1.6; }

/* ─────────────────────────────────── 층을 나눈 경고문
   답을 받은 순간 마주치던 327자·358자짜리 글 덩어리를 나눈 결과다. 결론은
   늘 보이고, 왜 그런지는 접힌다. 글을 줄인 것이 아니라 먼저 읽힐 것을
   고른 것이다 — 한 글자도 지우지 않았다. */

.caveat-lead { margin-top: 0.6rem; }

.caveat-more { margin-top: 0.45rem; }

.caveat-more > summary {
  cursor: pointer;
  font-size: 0.82rem;
  color: var(--ink-2);
  width: fit-content;
  padding: 0.1rem 0.35rem;
  margin-left: -0.35rem;
  border-radius: 5px;
}

.caveat-more > summary:hover { color: var(--ink); background: var(--card-2); }
.caveat-more > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; box-shadow: 0 0 0 4px var(--focus-ring); }
.caveat-more > .caveat { margin-top: 0.35rem; }

/* ─────────────────────────────────────────────── 모델 A/B 선택
   두 답을 나란히 놓기만 하던 자리에 "어느 쪽이 나은가"를 묻는 줄을 더한다.
   고른 것은 기록으로만 남는다 — 화면의 답은 바뀌지 않는다. 색으로 어느 쪽을
   권하지 않는 것이 요점이라, 두 버튼은 같은 모양이다. */

.ab {
  margin-top: 0.9rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--line-2);
}

.ab-ask { margin: 0 0 0.5rem; font-size: 0.92rem; font-weight: 600; }

.ab-buttons { margin-bottom: 0; }

.ab-done {
  margin: 0.5rem 0 0;
  font-size: 0.85rem;
  color: var(--ink-2);
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--card-2);
  width: fit-content;
}

/* ───────────────────────────────────────── 화면별 대화창
   업무정의서·ERD·정의서 아래에 붙는다. 본문의 끝이라는 것이 보이도록
   카드가 아니라 한 겹 아래로 가라앉은 면으로 둔다. */

.screenchat {
  margin-top: 1.5rem;
  padding: 1.05rem 1.2rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  background: var(--card-2);
}

.screenchat-lead {
  margin: 0 0 0.8rem;
  font-size: 0.85rem;
  color: var(--ink-2);
  line-height: 1.6;
  max-width: var(--measure);
}

.screenchat-form { display: flex; gap: 0.5rem; }

.screenchat-input {
  flex: 1;
  min-width: 0;
  padding: 0.6rem 0.8rem;
  font: inherit;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
}

.screenchat-input::placeholder { color: var(--dim); }
.screenchat-input:focus {
  outline: 2px solid var(--focus);
  outline-offset: -1px;
  border-color: var(--focus);
}

.screenchat-send { flex: none; padding: 0.6rem 1.2rem; }

.screenchat-status {
  margin: 0.7rem 0 0;
  font-size: 0.86rem;
  color: var(--ink-2);
}

/* 오간 말이 쌓이는 자리. 예전에는 답 한 벌만 놓였고 새 질문이 그것을
   덮어썼다 — 그래서 되물을 때마다 처음부터 다시 시작하는 것처럼 보였다. */
.screenchat-thread {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  margin: 0.9rem 0;
}

/* 대화 한 마디. 왼쪽 선 하나로 묶는다 — 상자를 겹겹이 두르면 두 답의
   상자와 경쟁해서 어느 것이 무엇의 경계인지 흐려진다. */
.screenchat-turn {
  padding-left: 0.9rem;
  border-left: 2px solid var(--line);
}

/* 사람이 한 물음. 답보다 먼저 읽히도록 진하게 두되, 크기는 키우지 않는다 —
   실이 길어졌을 때 물음만 도드라지면 답이 부속처럼 보인다. */
.screenchat-q {
  margin: 0 0 0.6rem;
  font-size: 0.92rem;
  font-weight: 600;
  line-height: 1.6;
  color: var(--ink);
}

.screenchat-q::before {
  content: '묻기 ';
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--dim);
}

/* 두 답은 같은 크기로 나란히 놓인다. 한쪽을 크게 두면 화면이 그쪽을
   권하는 셈이 되는데, 어느 쪽이 나은지는 화면이 모른다. */
.screenchat-cols {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.7rem;
}

.screenchat-col {
  padding: 0.8rem 0.9rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
}

.screenchat-model {
  margin: 0 0 0.5rem;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
}

/* 모델의 답은 줄바꿈을 지킨다. textContent 로 넣으므로 마크업은 없다. */
.screenchat-text {
  font-size: 0.9rem;
  line-height: 1.65;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

@media (max-width: 62rem) {
  .screenchat-cols { grid-template-columns: 1fr; }
}

/* ───────────────────────────────── 선행 판정에서 멈춘 답
   결과가 없다는 것을 말하는 카드다. 색으로 경고하지 않는다 — 질문이 잘못된
   것이 아니라 이 데이터로는 답할 수 없다는 사실일 뿐이다. */

.preflight-headline {
  margin: 0 0 0.7rem;
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.5;
  max-width: var(--measure);
}

.preflight-options { list-style: none; margin: 0 0 0.7rem; padding: 0; }

.preflight-form { display: flex; gap: 0.5rem; margin-bottom: 0.7rem; max-width: 46rem; }

.preflight-input {
  flex: 1;
  min-width: 0;
  padding: 0.5rem 0.7rem;
  font: inherit;
  font-size: 0.9rem;
  color: var(--ink);
  background: var(--card-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
}
.preflight-input::placeholder { color: var(--dim); }
.preflight-input:focus { outline: 2px solid var(--focus); outline-offset: -1px; }

.preflight-note { margin: 0; font-size: 0.82rem; line-height: 1.6; max-width: var(--measure); }

.preflight-raw { margin-top: 0.4rem; }

/* ───────────────────────── 갈린 답 — 두 칸이 각각 완전한 답
   표·차트·CSV 를 각자 갖는다. 두 칸은 같은 크기다 — 한쪽을 크게 두면
   화면이 그쪽을 권하는 셈이 되는데, 어느 쪽이 맞는지 화면은 모른다. */

.split-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin: 0.2rem 0 0.5rem;
}

.split-bar .views { display: flex; gap: 0.25rem; }
.split-bar .seg { font-size: 0.76rem; padding: 0.2rem 0.5rem; }

.split-chart { width: 100%; height: 240px; }

.split-nochart { margin: 0.6rem 0; font-size: 0.82rem; line-height: 1.55; }

/* ───────────────────────── DB 접속정보 관리
   이 화면에만 폼다운 폼이 있다. 다른 화면들은 질문 한 줄을 받거나 이미
   만들어진 것을 보여줄 뿐이라 입력 요소의 규칙이 필요 없었다.

   색은 전부 위에서 정의한 토큰으로만 쓴다 — 밝은 판 규칙이 :root[data-theme]
   한 곳에 있고, 여기서 리터럴을 쓰면 한쪽 판에서만 읽히는 줄이 생긴다. */

.conn-flash {
  margin: 0 0 0.9rem;
  padding: 0.6rem 0.8rem;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--card);
  font-size: 0.88rem;
  line-height: 1.55;
}

.conn-flash-ok { border-color: var(--accent); background: var(--accent-soft); color: var(--ink); }
.conn-flash-bad { border-color: var(--bad-line); background: var(--bad-bg); color: var(--bad-ink); }

.conn-probe-head { margin: 0 0 0.5rem; font-weight: 600; }
.conn-probe-ok { color: var(--accent); }
.conn-probe-bad { color: var(--bad-ink); }

/* 활성 지정 — 종류마다 한 줄. 라벨·선택·버튼이 한 줄에 서고, 좁은 화면에서
   접힌다. */
.conn-active { display: flex; flex-direction: column; gap: 0.6rem; margin: 0.8rem 0; }

.conn-active-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
}

.conn-active-row > label {
  flex: 0 0 12rem;
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--ink);
}

.conn-active-row select { flex: 1 1 22rem; min-width: 0; }

.conn-who {
  display: block;
  font-weight: 400;
  font-size: 0.76rem;
  color: var(--dim);
}

/* 폼. 좁은 칸(포트, 대기 시간)은 격자에서 한 칸만 차지한다. */
.conn-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 0.9rem 1.1rem;
  margin: 0.4rem 0 1rem;
}

.conn-field { display: flex; flex-direction: column; gap: 0.3rem; margin: 0; }
.conn-field-narrow { max-width: 11rem; }
.conn-field-check { margin: 0 0 1rem; }

.conn-field > label { font-size: 0.82rem; font-weight: 600; color: var(--ink-2); }

.conn-field-check label {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.84rem;
  font-weight: 500;
  color: var(--ink-2);
}

.conn-opt { font-weight: 400; color: var(--dim); }

.conn-hint { font-size: 0.75rem; line-height: 1.5; color: var(--dim); }

.conn-form input[type="text"],
.conn-form input[type="password"],
.conn-form input[type="number"],
.conn-active-row select,
.conn-form select {
  width: 100%;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--bg);
  color: var(--ink);
  font: inherit;
  font-size: 0.88rem;
}

.conn-form input:focus-visible,
.conn-form select:focus-visible,
.conn-active-row select:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
  box-shadow: 0 0 0 4px var(--focus-ring);
}

/* 읽기 전용 칸은 눌러도 되지 않는다는 것이 보여야 한다. 식별자가 그렇다 —
   저장 뒤에 바꾸면 활성 지정이 가리키는 곳이 사라진다. */
.conn-form input[readonly] { background: var(--card-2); color: var(--dim); }

.conn-inline { display: inline; }
.conn-actions { white-space: nowrap; }
.conn-actions .btn { margin-right: 0.2rem; }

.conn-mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.82rem;
}

.conn-path { margin: 0 0 0.6rem; color: var(--ink-2); word-break: break-all; }

.conn-id {
  display: inline-block;
  margin-left: 0.35rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.74rem;
  color: var(--dim);
}

/* 지금 쓰이고 있는 한 벌임을 표에서 한눈에. */
.conn-badge {
  display: inline-block;
  padding: 0.1rem 0.4rem;
  border: 1px solid var(--accent);
  border-radius: 999px;
  background: var(--accent-soft);
  font-size: 0.72rem;
  color: var(--ink);
}

/* ───────────────────────── 로그인·가입 — 문 앞의 화면
   GNB 가 없다. 아직 아무 화면도 열 수 없는 사람에게 여섯 개의 메뉴를
   보여 주면, 누를 때마다 여기로 되돌아온다. */

.gate-body { display: flex; min-height: 100vh; }

.gate {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  padding: 2rem 1rem;
}

.gate-card {
  width: 100%;
  max-width: 26rem;
  padding: 2rem;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--panel);
  box-shadow: var(--shadow, none);
}

.gate-brand {
  display: block;
  width: 13rem;
  height: 3.25rem;
  margin: 0 0 0.7rem;
}

.gate-brand:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; border-radius: 4px; }

.gate-title { margin: 0 0 1.2rem; font-size: 1.5rem; color: var(--ink); }

.gate-form { display: flex; flex-direction: column; gap: 0.9rem; }
.gate-submit { width: 100%; justify-content: center; margin-top: 0.3rem; }

.gate-alt { margin: 1.1rem 0 0.4rem; font-size: 0.86rem; color: var(--ink-2); }
.gate-alt a { color: var(--accent); }

.gate-note {
  margin: 0;
  padding-top: 0.8rem;
  border-top: 1px solid var(--line-2);
  font-size: 0.78rem;
  line-height: 1.6;
  color: var(--dim);
}

/* ───────────────────────── 누구로 들어와 있는가
   머리줄 오른쪽 끝, 밝기 토글 앞. 모든 화면에서 같은 자리다. */

.who-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  padding: 0.2rem 0.55rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  white-space: nowrap;
}

/* 이름 칸이 <내 정보>로 가는 길이 됐다. 누를 수 있는 것은 눌러 보이게 한다 —
   다만 크기는 그대로다. 머리줄에서 길이가 변하는 것은 두지 않는다. */
a.who-chip { text-decoration: none; }
a.who-chip:hover { border-color: var(--ink-2); }
a.who-chip:focus-visible { outline: 2px solid var(--ink-2); outline-offset: 2px; }

.who-name { font-size: 0.82rem; color: var(--ink); }
.who-role { font-size: 0.72rem; color: var(--dim); }
.who-out { display: inline; }

/* ───────────────────────── 회원 관리 */

.member-assign {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
}

.member-assign select {
  padding: 0.3rem 0.45rem;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--bg);
  color: var(--ink);
  font: inherit;
  font-size: 0.8rem;
}

.member-assign select:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}

.member-warn {
  margin: 0.35rem 0 0;
  font-size: 0.76rem;
  line-height: 1.55;
  color: var(--warn-ink);
}

.member-warn a { color: var(--accent); }
.member-table td { vertical-align: top; }

/* ───────────────────────── 스켈레톤 — 기다리는 자리
   답이 <어디에 얼마만큼> 들어올지 미리 보여 준다. 상태 문구 한 줄만 두면
   화면은 그동안 빈 채로 있고, 사람은 무엇을 기다리는지 알 수 없다.

   띠의 길이를 서로 다르게 두는 것이 요점이다. 같은 길이가 나란히 있으면
   표처럼 보이고, 그러면 사람은 그것을 <내용>으로 읽으려 한다. */

.skel {
  display: block;
  height: 0.72rem;
  margin: 0.42rem 0;
  border-radius: 999px;
  /* 색은 토큰에서 온다. 리터럴을 쓰면 밝은 판에서 검은 띠가 뛴다. */
  background: linear-gradient(90deg,
    var(--card-2) 0%, var(--line) 45%, var(--card-2) 90%);
  background-size: 220% 100%;
  animation: skel-sweep 1.35s linear infinite;
}

.skel-lines { min-height: 4.6rem; }
.skel-lines .skel:first-child { margin-top: 0; }

@keyframes skel-sweep {
  from { background-position: 120% 0; }
  to   { background-position: -80% 0; }
}

/* 움직임을 줄여 달라고 한 사람에게는 뛰지 않는 띠를 준다. 자리를 알려
   주는 일은 그대로 하되, 흔들리지 않는다.

   !important 인 이유: 미디어 질의는 특정도를 올려 주지 않는다. 나중에
   누군가 .screenchat-col .skel 처럼 더 좁은 규칙으로 애니메이션을 붙이면
   이 블록이 조용히 진다. WaitingStateIT 가 그 규칙을 지킨다. */
@media (prefers-reduced-motion: reduce) {
  .skel {
    animation: none !important;
    background: var(--card-2);
  }
}

/* 표가 앉을 자리. 머리줄 한 줄과 본문 몇 줄로 표의 모양만 흉내 낸다 —
   글줄 띠만 쌓으면 문단이 올 자리로 읽히고, 실제로 오는 것은 표다. */
.skel-grid { display: flex; flex-direction: column; gap: 0.1rem; }
.skel-grid .skel { height: 0.95rem; margin: 0.3rem 0; }
.skel-grid .skel-head { height: 1.15rem; margin-bottom: 0.75rem; opacity: 0.75; }

/* 본문 줄은 조금씩 짧아진다. 같은 길이가 반복되면 그것 자체가 내용처럼
   보이고, 사람이 읽으려 든다. */
.skel-grid .skel:nth-child(2) { width: 100%; }
.skel-grid .skel:nth-child(3) { width: 96%; }
.skel-grid .skel:nth-child(4) { width: 99%; }
.skel-grid .skel:nth-child(5) { width: 93%; }
.skel-grid .skel:nth-child(6) { width: 97%; }

/* ───────────────────────── 로그인의 「아이디 저장」 */
.gate-remember { display: flex; flex-direction: column; gap: 0.3rem; }
.gate-remember-label {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-weight: 400;
  cursor: pointer;
}
.gate-remember-label input { width: auto; margin: 0; }
.gate-remember-note { font-size: 0.82rem; color: var(--dim); line-height: 1.6; }

/* ───────────────────────── 내 정보
   못 바꾸는 칸은 입력칸으로 그리지 않는다. 비활성 입력칸은 "지금은 못 바꾼다"로
   읽히지만, 여기서는 "여기서 바꾸는 곳이 아니다"가 맞는 말이다. */
.me-facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.45rem 1.2rem;
  margin: 0;
}
.me-facts dt { font-size: 0.85rem; color: var(--dim); }
.me-facts dd { margin: 0; font-size: 0.92rem; color: var(--ink); }
.me-note { margin: 0.9rem 0 0; font-size: 0.85rem; color: var(--dim); line-height: 1.7; }
.me-note code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.85em;
}
.me-form { max-width: 26rem; }

/* ───────────────────────── 튜닝 포인트
   측정값과 모델의 의견을 <다르게> 그린다. 같은 무게로 그리면 "인덱스를
   만드세요"가 "지워진 행이 3만 개입니다"와 같이 읽힌다. */

/* 번호를 끈다. 측정된 것에 번호를 매기면 사람이 그 번호로 이야기하는데,
   목록이 종류마다 잘려 있어서 「7번」이 어제의 7번과 다른 것을 가리킨다.
   사람이 셀 번호는 <영역>의 것뿐이고 그것은 서버가 준 글자다. */
.tuning-list { list-style: none; }

/* ── 영역 상자.
   안내 문장 · 측정된 것 · 두 모델의 가이드가 <한 상자에> 있다. 측정과 모델의
   말을 다른 카드로 갈라 두면 사람이 아래 카드만 읽고 그것을 측정값으로 읽는다.
   같은 상자에 두되 <생김새로> 가른다 — 잰 것은 목록이고, 모델의 말은 왼쪽에
   굵은 선이 붙은 좌우 비교다(검색 화면의 .split 과 같은 모양). */
.tuning-area { scroll-margin-top: 4.5rem; }
.tuning-area-lead {
  margin: 0 0 0.5rem;
  max-width: var(--measure);
  font-size: 0.92rem;
  color: var(--ink-2);
  line-height: 1.7;
}
.tuning-area-lead b { color: var(--ink); font-weight: 700; }
.tuning-area-focus {
  margin: 0 0 1rem;
  max-width: var(--measure);
  font-size: 0.85rem;
  color: var(--dim);
  line-height: 1.65;
}
.tuning-area-none {
  margin: 0 0 1rem;
  max-width: var(--measure);
  font-size: 0.9rem;
  color: var(--dim);
  line-height: 1.7;
}
.tuning-ask { margin-top: 1rem; }
.tuning-guide { margin-top: 1rem; padding-left: 0.9rem; }

/* ── 왼쪽 바로가기.
   개수를 오른쪽 끝에 붙여 <같은 자리에> 세운다. 이름 뒤에 이어 붙이면 이름
   길이에 따라 숫자가 들쭉날쭉해서, 어느 영역이 많은지 훑어볼 수가 없다. */
.area-nav { display: flex; flex-direction: column; gap: 0.15rem; margin: 0.2rem 0 1.1rem; }
.area-link {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.4rem 0.5rem;
  border-radius: var(--radius-control);
  border-left: 3px solid transparent;
  color: var(--ink-2);
  text-decoration: none;
  font-size: 0.88rem;
  line-height: 1.45;
}
.area-link:hover { background: var(--bg); color: var(--ink); }
/* 지금 보고 있는 자리. 색이 아니라 <선과 굵기>로 말한다 — 이 저장소는 색을
   경고에만 쓴다. */
.area-link.here {
  border-left-color: var(--ink);
  background: var(--bg);
  color: var(--ink);
  font-weight: 700;
}
.area-link-name { min-width: 0; }
.area-link-count {
  flex: none;
  font-size: 0.8rem;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
}
/* 0 개를 흐리게 둔다. 지우지는 않는다 — 「봤는데 없었다」도 사실이다. */
.area-link-none { opacity: 0.55; }

.tuning-metric {
  margin: 0.3rem 0 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.85rem;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.tuning-detail { margin: 0.35rem 0 0; font-size: 0.9rem; color: var(--ink-2); line-height: 1.6; }

/* 고치는 방향이 분명하지 않은 항목. 색이 아니라 왼쪽 선과 <말>로 구별한다 —
   이 저장소는 판정을 색으로 말하지 않는다. */
.tuning-judgement { border-left: 3px solid var(--line); }
.tuning-caution { margin: 0.35rem 0 0; font-size: 0.85rem; color: var(--dim); }

.tuning-remedy-label { margin: 0.7rem 0 0; font-size: 0.85rem; color: var(--dim); }

/* 두 답을 담는 상자와 머리는 .split-col · .split-head 를 쓴다 — 검색 화면과
   같은 것이다. 여기 있던 세로로 쌓는 규칙(.tuning-opinions)은 지웠다:
   두 벌이면 두 화면의 비교 카드가 서서히 달라진다. 아래 둘은 <글의 생김새>라
   좌우 비교에서도 그대로 쓴다. */
.tuning-opinion-text {
  margin: 0;
  font-size: 0.94rem;
  line-height: 1.75;
  color: var(--ink);
  white-space: pre-wrap;
}
.tuning-opinion-error { margin: 0; font-size: 0.9rem; color: var(--bad-ink); line-height: 1.6; }

/* ─────────────────────────────── 내 엑셀
 *
 * 이 화면이 시각적으로 해야 하는 일 하나: <확신하지 못한 것>을 확신한 것과
 * 다르게 보이게 하는 것. 엑셀에는 표의 정의가 없어 머리글을 짐작하는데,
 * 짐작이 틀린 채로 나란히 놓이면 사람이 확인할 이유를 못 느낀다.
 *
 * 색은 이미 있는 토큰만 쓴다. --warn 계열이 "확인해 달라"이고 --bad 계열이
 * "실패했다"다. 여기서 새 색을 만들면 같은 뜻이 화면마다 다른 색이 된다.
 */
.sheet-upload {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.sheet-chosen { font-size: 0.9rem; color: var(--dim); }
.sheet-status {
  margin: 0.75rem 0 0;
  font-size: 0.9rem;
  color: var(--ink-2);
}
.sheet-status.bad { color: var(--bad-ink); }

.sheet-file {
  margin: 0 0 0.75rem;
  font-size: 0.9rem;
  color: var(--dim);
}

.sheet-shape {
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 0.9rem 1rem;
  margin-bottom: 0.75rem;
}
/* 짐작을 확신하지 못한 표. 사람이 확인해야 하는 자리다. */
.sheet-shape-unsure { border-color: var(--warn-line); background: var(--warn-bg); }
/* 아예 표가 되지 못한 시트. 조용히 빠지지 않도록 눈에 걸리게 둔다. */
.sheet-shape-skipped { border-color: var(--bad-line); background: var(--bad-bg); }

.sheet-shape-title { margin: 0 0 0.35rem; font-size: 1rem; color: var(--ink); }
.sheet-shape-meta {
  margin: 0 0 0.5rem;
  font-size: 0.85rem;
  color: var(--ink-2);
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
}
.sheet-dot { color: var(--dim); }
.sheet-conf { color: var(--ink-2); }
.sheet-conf-low { color: var(--warn-ink); font-weight: 600; }
.sheet-note { margin: 0 0 0.6rem; font-size: 0.85rem; line-height: 1.65; color: var(--ink-2); }
/* ── 올린 자료의 격자 ────────────────────────────────────────────────
   파일 하나가 카드 하나다. 합쳐서 마지막 것만 보여주면 방금 올린 것이
   앞엣것을 지운 것처럼 보인다. */
.sheet-grids { display: flex; flex-direction: column; gap: 1rem; }
.sheet-grid-head { margin-bottom: 0.35rem; }
.sheet-grid-unsure { border-color: var(--warn-line); }
.sheet-grid-skipped { border-color: var(--bad-line); }

/* 격자는 <자기 안에서만> 넘친다. 본문 전체가 옆으로 밀리면 안 된다.
   세로도 묶어 둔다 — 그 높이가 곧 "내리면 더 읽는다"의 자리다. */
.sheet-grid-wrap {
  max-height: 22rem;
  overflow: auto;
  border: 1px solid var(--line);
  border-radius: 6px;
}
/* 머리줄은 내려도 붙어 있어야 한다. 스무 줄쯤 내리면 어느 칸이 무엇인지
   알 수 없게 되는데, 그때 위로 되돌아가 확인하게 만들 이유가 없다. */
.sheet-grid-wrap thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--card);
}
.sheet-grid-foot { margin: 0.4rem 0 0; font-size: 0.8rem; color: var(--dim); }
.sheet-grid-foot.bad { color: var(--bad-ink); }
.sheet-columns { margin: 0 0 0.6rem; }
.sheet-columns summary { font-size: 0.85rem; color: var(--ink-2); cursor: pointer; }

/* ── 읽는 동안의 뼈대 ──────────────────────────────────────────────
   빈 화면에서 기다리면 사람은 <아무 일도 안 일어났다>고 읽고 다시 누른다.
   240MB CSV 가 10초 넘게 걸린다(실측). */
.sheet-skel { display: flex; flex-direction: column; gap: 0.5rem; padding: 0.5rem 0; }
.sheet-skel-row { display: flex; gap: 0.5rem; }
.sheet-skel-cell {
  flex: 1;
  height: 1.1rem;
  border-radius: 4px;
  background: linear-gradient(90deg, var(--line) 25%, var(--card) 50%, var(--line) 75%);
  background-size: 200% 100%;
  animation: sheet-skel-slide 1.4s ease-in-out infinite;
}
@keyframes sheet-skel-slide {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}
/* 움직임을 줄여 달라고 한 사람에게는 움직이지 않는다. 자리를 잡아 두는
   일은 애니메이션 없이도 그대로 된다. */
/* !important 가 필요하다. 미디어 질의는 특정도를 올려 주지 않아서, 위의
   .sheet-skel-cell 규칙과 같은 무게로 붙으면 <나중에 온 쪽>이 이기는 순서
   싸움이 된다. WaitingStateIT 가 이 규칙을 못박는다. */
@media (prefers-reduced-motion: reduce) {
  .sheet-skel-cell { animation: none !important; background: var(--line); }
}

/* 옆줄의 업로드 파일 목록. 이름을 누르면 그 자료로 간다. */
.side-subtitle {
  margin: 1.2rem 0 0.5rem;
  font-size: 0.9rem;
  color: var(--ink);
}
.sheet-list .sheet-row-main {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: inherit;
  font: inherit;
}

/* 못 실은 줄이 있다. 「이렇게 읽었습니다」 안에서 이것만은 눈에 걸려야 한다 —
   조용히 지나가면 사람은 자기 자료가 다 들어온 줄 알고 합계를 낸다. */
.sheet-note-warn {
  color: var(--warn-ink);
  font-weight: 600;
  border-left: 3px solid var(--warn-line);
  padding-left: 0.6rem;
}

/* 컬럼이 많으면 가로로만 넘친다. 본문 전체가 옆으로 밀리면 안 된다. */
.sheet-table-wrap { overflow-x: auto; }
.sheet-table { border-collapse: collapse; font-size: 0.85rem; min-width: 24rem; }
.sheet-table th,
.sheet-table td {
  border-bottom: 1px solid var(--line-2);
  padding: 0.3rem 0.7rem 0.3rem 0;
  text-align: left;
  vertical-align: top;
}
.sheet-table th { color: var(--dim); font-weight: 500; }
.sheet-letter { color: var(--dim); font-variant-numeric: tabular-nums; }
/* 우리가 지어낸 것 · 확인 못 한 것에 붙는 꼬리표. */
.sheet-tag {
  margin-left: 0.4rem;
  font-size: 0.75rem;
  color: var(--warn-ink);
  white-space: nowrap;
}

.sheet-list { display: flex; flex-direction: column; gap: 0.5rem; }
.sheet-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  border: 1px solid var(--line-2);
  border-radius: 10px;
  padding: 0.6rem 0.85rem;
}
.sheet-row-main { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.sheet-row-table { font-size: 0.95rem; color: var(--ink); }
.sheet-row-sub {
  font-size: 0.8rem;
  color: var(--dim);
  display: flex;
  gap: 0.35rem;
  flex-wrap: wrap;
}
.sheet-row-actions { display: flex; gap: 0.4rem; flex-wrap: wrap; }

.sheet-ask { display: flex; gap: 0.6rem; align-items: center; }
.sheet-ask-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  color: var(--ink);
  font: inherit;
}
/* 돌린 문장. 이 화면에는 신뢰 배지가 없고 이것이 그 자리를 대신한다. */
.sheet-sql {
  margin: 0.9rem 0 0.4rem;
  padding: 0.6rem 0.8rem;
  border: 1px solid var(--line-2);
  border-radius: 8px;
  background: var(--panel);
  color: var(--ink-2);
  font-size: 0.8rem;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-x: auto;
}
.sheet-null { color: var(--dim); }

/* 답의 그림 자리. 검색 화면의 .chart 와 같은 높이를 준다. */
.sheet-chart { height: 320px; margin: 0.9rem 0 0.4rem; }

/* ─────────────────────────────── 확인 카드
 *
 * 업무 DB 의 확인 카드와 같은 이유로 있고, 훨씬 가볍다 — 결재도 승인자도
 * 없고 「N칸이 바뀝니다」와 목록뿐이다. 다만 <눈에 걸려야> 한다: 답과
 * 같은 무게로 그리면 사람이 읽지 않고 누른다. --warn 계열이 이 저장소에서
 * 「확인해 달라」는 뜻이다.
 */
.sheet-confirm {
  margin-top: 0.9rem;
  border: 1px solid var(--warn-line);
  background: var(--warn-bg);
  border-radius: 10px;
  padding: 0.9rem 1rem;
}
.sheet-confirm-title { margin: 0 0 0.35rem; font-size: 1rem; color: var(--ink); }
.sheet-confirm-lead {
  margin: 0 0 0.6rem;
  font-size: 0.9rem;
  line-height: 1.65;
  color: var(--warn-ink);
}
.sheet-confirm-actions {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.8rem;
  flex-wrap: wrap;
}
/* 바뀔 값. 지금 값과 나란히 있으므로 <어느 쪽이 새것인지>가 보여야 한다. */
.sheet-after { color: var(--ink); font-weight: 600; }

/* 얼마나 썼나. 목록 바로 위 — 지울 것을 고르는 자리와 같은 자리여야 한다. */
.sheet-usage { margin-bottom: 0.8rem; }
.sheet-usage-bar {
  height: 4px;
  border-radius: 2px;
  background: var(--line-2);
  overflow: hidden;
}
.sheet-usage-bar > span {
  display: block;
  height: 100%;
  background: var(--accent);
}
/* 거의 다 찼다는 것은 색으로도 보여야 한다. 숫자만으로는 안 읽힌다. */
.sheet-usage-bar > span.sheet-usage-full { background: var(--warn-ink); }
.sheet-usage-text { margin: 0.4rem 0 0; font-size: 0.8rem; color: var(--dim); }
