/* Claude 모바일 브릿지 -- 모바일 우선 스타일
   원칙:
   - 좌우 끝단 여백은 항상 같게 배분한다 (오른쪽 죽은 공간 금지)
   - 상태 강조에 테두리 두께를 쓰지 않는다. 두께가 바뀌면 소수점 테두리가
     기기 픽셀로 내림되어 안쪽 내용물이 미세하게 흔들린다. 색과 그림자만 쓴다
   - 회전 애니메이션을 쓰지 않는다. 진행 표시는 밝기 맥동으로 한다 */

:root {
  --bg: #16181d;
  --bg-2: #1d2027;
  --bg-3: #24272f;
  --fg: #e6e8ec;
  --fg-dim: #9aa1ad;
  --fg-faint: #6b7280;
  --line: #2e323b;
  --accent: #c96442;
  --accent-fg: #ffffff;
  /* 보낸 내용 줄의 바탕. 판의 어두운 색들과 같은 결에 두되 푸른 기를 남겨
     둔다 -- 그 기가 없으면 카드 바탕과 갈리지 않아 붙어 있는 줄이 안 보인다.
     판의 맨 아래 색과 섞어 뽑은 값이라 이 판 밖의 색이 아니다. */
  --user: #232c3b;
  --warn: #d19a3d;
  --error: #d9534f;
  --ok: #5aa469;
  /* 프리셋 표의 글자. 위 셋과 결이 다르다 -- 저것들은 무슨 일이 났는지를
     말하느라 눈에 걸려야 하고, 이것은 그 줄이 어느 묶음의 것인지만 말한다.
     그래서 뜻이 걸린 색상들을 피해 파란 쪽에 둔다 (이 판에서 파랑을 쓰는
     자리는 보낸 내용 줄의 바탕뿐이라 겹치지 않는다).
     밝은 판에서는 아래에서 어둡게 잡은 짝으로 덧쓴다. 색을 가르는 것은
     색상이고 밝기는 바탕에 맞춰 뒤집는다 -- 어두운 판의 값을 그대로 두면
     흰 칸에서 글자가 흐려 이름이 안 읽힌다. */
  --preset: #6f9bc4;
  --pad: 0.875rem; /* 좌우 공통 여백. 한 값으로만 쓴다 */
  /* 대화 칸 위 안여백. 곧 상단 머리와 그 아래 붙어 서는 줄의 틈이다.
     옆여백(--pad)과 갈라 두는 것은 재는 것이 달라서다 -- 옆은 화면 가장자리
     와의 여백이고 이것은 머리와의 틈이다. 눈으로 고르는 값이라 보이는 값을
     그대로 적는다. */
  --log-top: 10px;
  /* 줄 높이. 배수라 글자 크기가 작아지면 줄 상자도 함께 작아진다.
     시트 아래 버튼들은 글자 크기가 서로 다른데 높이는 같아야 해서, 거기서만
     이 값에 기준 글자 크기를 곱해 절대값으로 못박는다. 여기 한 자리에 두는
     것은 그 곱셈이 본문 줄 높이를 따라가야 하기 때문이다. */
  --line-h: 1.55;
  /* 목록 줄 한 벌. 프로젝트 / 세션 / 파일이 같은 상자를 쓴다.
     프로젝트와 세션 줄은 이름 한 줄에 딸린 값 한 줄이라 두 줄이고 파일 줄은
     이름 한 줄인데, 시트를 오갈 때 줄 키가 달라지면 같은 목록이 다른 것으로
     보인다. 그래서 속 높이를 두 줄로 못박아 셋을 같은 키로 세운다. */
  --row-pad: 0.6875rem;
  --row-sub: 0.7rem;
  --row-h: calc(var(--line-h) * (1rem + var(--row-sub)) + var(--row-pad) * 2);
  --radius: 0.75rem;
  /* 아이콘 버튼의 상자와 그 안 그림. 둘을 여기 한 자리에 둔다 --
     상자가 그림보다 크므로 그림은 상자 안쪽으로 그 차이의 절반만큼 들어가
     앉고, 끝에 세우는 버튼은 그만큼 당겨야 그림이 글자와 같은 선에 선다.
     숫자를 자리마다 다시 적으면 한쪽만 고쳐져 아이콘이 옆으로 튄다. */
  --iconbtn-box: 2.25rem;
  --iconbtn-glyph: 1.125rem;
  --iconbtn-pull: calc((var(--iconbtn-box) - var(--iconbtn-glyph)) / -2);
  /* 경로 줄 조각의 위아래 안여백. 손이 닿는 범위를 글자보다 넓히는 몫이라
     글자 자리에서 보면 그만큼 아래로 밀려 있다. 줄 위쪽 간격을 맞출 때 그
     값을 도로 당겨야 해서 여기 한 자리에 둔다. */
  --crumb-pad: 0.125rem;
  /* 경로 줄의 글자 크기. 그 줄을 그리는 자리와, 그 줄이 글자 위에 이고 있는
     빈 몫을 셈하는 자리 둘이 같이 읽는다. 두 곳에 숫자를 적으면 크기를 고칠
     때 한쪽만 고쳐져 자리가 어긋난다. */
  --crumb-size: 0.75rem;
  /* 글자 한 줄이 글자 위에 이고 있는 빈 몫. 줄 상자는 글자보다 크고 그 차이가
     위아래로 반씩 나뉘어 앉으므로, 상자 위에서 글자 위까지가 이만큼이다.
     바탕을 칠한 줄은 상자 끝이 보이지만 경로 줄은 글자만 보인다 -- 두 시트의
     간격을 눈으로 맞추려면 이 몫을 빼야 한다. */
  --crumb-lead: calc((var(--line-h) - 1) * var(--crumb-size) / 2);
  /* 경로 줄 글자에서 아래 목록까지 눈에 보이는 간격. 여백 값이 아니라 보이는
     값이다 -- 조각의 아래 안여백과 줄 상자가 글자 아래에 이고 있는 몫이 사이에
     끼므로, 실제로 줄 여백에 적는 것은 여기서 그 둘을 뺀 나머지다.
     보이는 값을 여기 적어 두는 까닭은 눈으로 고르는 값이 이것이어서다.
     여백 값을 적어 두면 고칠 때마다 두 몫을 머리로 더해야 한다. */
  --crumb-gap: 11px;
  /* 보낸 내용 줄 오른쪽의 접힘 / 펼침 세모.
     상자는 정사각형이고 그 안에 정삼각형이 앉는다. 상자를 정사각형으로 두는
     것은 오른쪽 세모와 아래 세모가 같은 자리를 쓰게 하려는 것이다 -- 모양에
     딱 맞춘 상자는 가로세로 비가 달라 갈아 끼울 때마다 옆이 밀린다.
     세모 자신은 상자의 열두 칸 중 열 칸이라 보이는 크기는 이 값의 열두 분의
     열이다. */
  /* 크기는 실측에서 나왔다. 이 자리에 쓰던 글자 세모의 획이 가로 6 세로 4 로
     상자 넓이가 24 였고, 거기서 조금만 키워 26 으로 둔다.
     세로가 아니라 넓이로 견주는 까닭 : 옛 글자 세모는 가로가 더 넓은 납작한
     꼴인데 정삼각형은 반대로 세로가 길다. 세로만 맞추면 훨씬 커 보인다.

     그리는 상자는 넓이에서 거꾸로 뽑는다.
     넓이 = 한 변 × 가리키는 쪽 = 한 변의 제곱 × 루트3의 절반
     한 변 = 루트(26 × 2 ÷ 루트3) = 5.479
     상자 = 한 변 × 12 ÷ 10 = 6.575 (세모가 상자의 열두 칸 중 열 칸이다)
     CSS 에 제곱근이 없어 뽑은 값을 적는다. 넓이를 고치면 이 줄도 다시 뽑는다.

     적는 값은 6.575 가 아니라 6.578125 다. 브라우저가 배치 값을 64분의 1
     눈금으로 끊어 6.575 는 6.5625 로 내려앉고 그러면 넓이가 25.9 가 된다.
     눈금에 딱 떨어지는 바로 위 값(64분의 421)을 적어 26.0 으로 세운다.

     카드 머리의 세모도 이 값을 쓴다. 같은 아이콘은 어느 자리에서나 같다. */
  --chev-box: 6.578125px;
  --chev-right: 0.625rem;
  /* 글자 앞에 서는 꺾쇠(>)의 그림 크기. 편집기 작업 장의 꺾쇠와 같은 12px 이다
     -- 같은 아이콘은 어느 자리에서나 같다. 글자 뒤의 세모(--chev-box)와는
     그림이 달라 값도 따로 둔다. 두 자리(도구 카드 머리 / 진행 상황 머리)가
     이 값 하나를 읽는다. */
  --caret-box: 12px;
  /* 세모 때문에 글자를 비워 두는 자리. 세모가 앉는 자리와 크기에 글자와 세모
     사이 틈을 더한 값이다. 이 값을 읽는 자리가 둘이라 여기 한 자리에 둔다 --
     글자를 밀어 두는 쪽과, 펼쳤을 때 막대를 세모 밖으로 빼는 쪽이다. */
  --chev-reserve: calc(var(--chev-right) + var(--chev-box) + 0.875rem);
  /* 보낸 내용 줄 첫 줄의 한가운데. 상자 위에서 이만큼 내려온 자리다.
     오른쪽 세모가 그 선에 맞춰 선다 -- 상자 한가운데에 맞추면 펼쳐서 여러
     줄이 됐을 때 세모가 글 한복판으로 내려간다. */
  --msg-line-mid: calc(0.625rem + var(--line-h) * 1rem / 2);
}

@media (prefers-color-scheme: light) {
  :root {
    --bg: #faf9f7;
    --bg-2: #ffffff;
    --bg-3: #f0eeea;
    --fg: #22242a;
    --fg-dim: #5c6270;
    --fg-faint: #8a8f9a;
    --line: #e0ddd7;
    --user: #dfe8f3;
    --preset: #43719b;
  }
}

* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

/* 손이 얹힌 자리에 브라우저가 그리는 테두리를 안 쓴다. 이 판은 손가락으로
   다루는 자리라 손이 어디 있는지는 눌림의 옅어짐이 말한다 (:active).
   한 줄로 건다 -- 자리마다 끄면 새 손잡이를 세울 때마다 그 줄을 빠뜨리고,
   빠뜨린 것은 그 자리에 손이 얹힐 때만 보인다. 재 보니 손을 받는 일곱
   갈래가 일곱 다 그 테두리를 세우고 있었다. */
:focus { outline: none; }
:focus-visible { outline: none; }

/* [주의] 평소 배경이 없는 컨트롤에는 눌렀을 때 배경을 켜지 않는다.
   그 순간 네모난 터치 영역이 통째로 드러나 눌린 곳보다 그 상자가 먼저 눈에
   들어온다. 머리 버튼(.ghost) / 위쪽 상태 표시(.statusbtn) / 아래 현황 줄
   (.statusbar-meta) / 현황 줄 아이콘(.iconbtn) / 예약 줄(.queue-item, .queue-del)
   이 그랬고 전부 걷어냈다.
   이미 제 배경이 있는 것(버튼 칸, 모달 버튼 등)은 filter 로 밝기만 바꾼다 --
   그건 영역을 드러내는 것이 아니라 있던 면의 색을 바꾸는 것이다. */

/* hidden 속성이 반드시 이기게 한다.
   브라우저 기본 스타일의 [hidden] { display: none } 은 우선순위가 가장 낮아서,
   아래에서 .gate / .app / .sheet / .working 에 display: flex 를 주는 순간
   클래스 선택자가 이겨 버린다. 그러면 hidden 을 켜도 화면에서 안 사라지고,
   시작하자마자 시트가 화면을 덮은 채 닫기가 먹지 않는다.
   display 를 쓰는 요소에 hidden 을 함께 쓴다면 이 규칙이 반드시 있어야 한다. */
[hidden] {
  display: none !important;
}

/* 칸 안에서 가로로 따로 굴러가는 자리.
   이름을 하나로 두는 까닭 : 시트를 미는 손짓이 이 이름을 보고 그 자리에서
   시작한 손을 안 가져간다 (app.js 의 시트 밀기). 자리마다 따로 적으면 새로
   가로 칸을 세울 때 그쪽을 같이 고쳐야 하고, 안 고치면 그 칸을 굴리는 손이
   시트를 넘겨 버린다.
   막대는 감춘다 -- 손가락으로 미는 자리라 막대가 설 필요가 없고, 서면 그
   높이만큼 칩이 눌린다.
   굴림이 끝까지 가도 바깥으로 안 넘긴다 (overscroll-behavior-x). 안 막으면
   끝에 닿은 뒤의 손짓이 뒤 칸으로 흘러 화면이 통째로 밀린다. */
.x-scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.x-scroll::-webkit-scrollbar {
  display: none;
}

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--fg);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Malgun Gothic", sans-serif;
  font-size: 15px;
  line-height: var(--line-h);
  /* 문서 자체는 굴러가지 않는다. 굴러가는 것은 대화 칸과 시트 같은 안쪽 칸이다.
     문서가 굴러갈 수 있는 판으로 남으면 iOS 26 홈 화면 앱이 그 맨 위에 유리
     흐림 띠를 얹는데, 그 띠가 시계 줄 아래 머리 바까지 내려와 이름과 연결
     상태를 번지게 했다 (버튜버는 판을 고정해 둬서 안 그랬다). */
  overflow: hidden;
  overscroll-behavior: none;
  /* 두 번 탭으로 확대되는 것을 막는다. 스크롤과 손가락 이동은 그대로 남는다.
     (none 으로 두면 스크롤까지 죽으므로 manipulation 이어야 한다) */
  touch-action: manipulation;
  /* 길게 눌렀을 때 뜨는 선택 / 미리보기도 앱 느낌을 깨뜨린다.
     입력칸과 대화 내용은 아래에서 다시 선택 가능하게 돌려 놓는다. */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  /* 길게 눌러 끌어내는 동작도 막는다. 선택 금지와 별개의 동작이라
     이것까지 꺼야 글자나 아이콘이 손가락에 딸려 오지 않는다. */
  -webkit-user-drag: none;
}

/* 사용자가 실제로 복사해야 하는 것만 선택을 살린다.
   대화 내용을 복사 못 하게 만들면 도구로서 못 쓴다.
   반대로 안내 문구 / 버튼 / 라벨까지 잡히면 길게 누를 때마다 선택이 떠서
   앱 느낌이 깨진다.

   살리는 것:
     .msg 대화 말풍선 (사람과 모델의 말)
     .card > pre 접힌 카드 안의 실제 내용 (생각 / 도구 결과)
     입력칸

   안 살리는 것 (기본값 그대로 금지):
     .note 세션 시작 같은 시스템 안내
     .card > summary 카드를 여닫는 라벨. 내용이 아니라 조작 장치다
     상단 바, 시트, 버튼 */
.msg,
.card > pre,
textarea,
input,
code {
  -webkit-user-select: text;
  user-select: text;
  -webkit-touch-callout: default;
}

button {
  font: inherit;
  color: inherit;
  cursor: pointer;
  border: none;
  background: none;
}

/* ---------------------------------------------------------- 연결 화면 */

.gate {
  /* 화면에 고정하고 넘치면 이 안에서 굴린다. 문서는 안 굴러가므로
     (html / body 머리말) 높이만 늘리면 넘친 아래가 잘려 닿지 않는다. */
  position: fixed;
  inset: 0;
  display: flex;
  /* align-items: center 를 쓰면 안 된다.
     내용이 화면보다 길어지는 순간 위쪽이 잘려 나가고 스크롤로도 닿지 않는다
     (폰에서 키보드가 올라오면 바로 이 상황이 된다).
     대신 아래 .gate-box 의 margin: auto 로 가운데를 맞춘다 --
     공간이 남으면 가운데, 모자라면 위에서부터 자연스럽게 흐른다. */
  align-items: flex-start;
  justify-content: center;
  overflow-y: auto;
  padding: calc(env(safe-area-inset-top) + var(--pad)) var(--pad)
    max(env(safe-area-inset-bottom), var(--pad));
}
.gate-box {
  width: 100%;
  max-width: 26rem;
  margin: auto; /* 공간이 남을 때만 세로 가운데 정렬 */
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.25rem;
}
.gate-box h1 {
  margin: 0 0 0.5rem;
  font-size: 1.25rem;
}
.gate-desc {
  margin: 0 0 1rem;
  color: var(--fg-dim);
  font-size: 0.9rem;
}
/* QR / 연결 코드로 연결 -- 입력 화면의 첫 길이라 단추 꼴로 크게 둔다 */
.gate-pair {
  display: block;
  margin: 0 0 1rem;
  padding: 0.8rem;
  border-radius: 0.6rem;
  text-align: center;
  text-decoration: none;
  font-weight: 600;
}

/* 붙어 본 PC 목록. 한 줄에 PC 이름과 주소를 위아래로 둔다. 지금 붙은 PC 는
   테두리로 가른다 (app.js 의 renderHosts 가 current 를 단다). */
.host-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 0 0 1rem;
}
.host-item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.125rem;
  width: 100%;
  padding: 0.625rem 0.75rem;
  border: 1px solid var(--line, rgba(127, 127, 127, 0.3));
  border-radius: 0.5rem;
  background: transparent;
  color: inherit;
  text-align: left;
}
.host-item span {
  color: var(--fg-dim);
  font-size: 0.75rem;
  word-break: break-all;
}
.host-item.current {
  border-color: var(--accent, currentColor);
}

/* PC 에서 연결을 여는 법 안내. 폰에서 보는 화면이라 PC 쪽 자리를 밝혀 적는다.
   연결 단추 아래에 둔다 -- 위에 두면 단추가 화면 밖으로 밀린다 */
.gate-where {
  background: var(--bg-3);
  border-radius: 0.5rem;
  padding: 0.8125rem;
  margin-top: 0.875rem;
  font-size: 0.8125rem;
}
.gate-where > strong {
  display: block;
  margin-bottom: 0.5rem;
  color: var(--fg);
}
.gate-where ol {
  margin: 0;
  /* 번호가 상자 밖으로 나가지 않게. 좌우 여백을 같게 유지한다 */
  padding-left: 1.25rem;
  color: var(--fg-dim);
}
.gate-where li + li {
  margin-top: 0.5rem;
}
.gate-where b {
  color: var(--fg);
  font-weight: 600;
}
.gate-where code {
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  font-size: 0.78rem;
  background: var(--bg);
  padding: 0.05rem 0.3rem;
  border-radius: 0.25rem;
  word-break: break-all;
}
.gate-hint {
  margin: 0.625rem 0 0;
  color: var(--fg-faint);
  font-size: 0.75rem;
}
.gate-note {
  margin: 0.625rem 0 0;
  color: var(--fg-faint);
  font-size: 0.75rem;
  text-align: center;
}
.gate-box input {
  width: 100%;
  padding: 0.75rem;
  margin-bottom: 0.75rem;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  font-size: 1rem;
}
.gate-box input:focus {
  /* 두께 대신 안쪽 링으로 강조 */
  box-shadow: inset 0 0 0 2px var(--accent);
}
.gate-error {
  margin: 0.75rem 0 0;
  color: var(--error);
  font-size: 0.875rem;
  min-height: 1.2em;
}

.primary {
  width: 100%;
  padding: 0.75rem;
  background: var(--accent);
  color: var(--accent-fg);
  border-radius: 0.5rem;
  font-weight: 600;
}
.primary:active {
  filter: brightness(0.92);
}
.ghost {
  padding: 0.375rem 0.625rem;
  color: var(--fg-dim);
  border-radius: 0.5rem;
}

/* ---------------------------------------------------------- 레이아웃 */

/* 앱 판은 화면에 고정한다 (문서를 안 굴리는 까닭은 html / body 머리말). */
.app {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
}

.bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  /* 위아래 여백을 얇게 가져간다. 안에 든 버튼들이 스스로 높이를 갖고 있고,
     손가락이 닿는 범위는 ::after 로 따로 넓혀 두었으므로 여백을 줄여도
     누르기 어려워지지 않는다.
     맨 앞의 안전 영역(노치 / 상태 표시줄)은 건드리지 않는다 -- 그건 여백이
     아니라 화면이 가려지는 실제 높이다. 더하는 값만 줄인다. */
  padding: calc(env(safe-area-inset-top) + 0.25rem) var(--pad) 0.25rem;
  background: var(--bg-2);
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}
.bar > button {
  min-width: 0; /* 긴 프로젝트 이름이 오른쪽을 밀어내지 않게 */
}
#sessName {
  display: block;
  max-width: 45vw;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--fg);
  font-weight: 600;
}
.bar-right {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: 0 0 auto;
}
/* 상태 글자와 메뉴 버튼의 줄 높이를 하나로 묶는다.
   따로 두면 글꼴 크기가 다른 만큼 서로 위아래로 어긋나 보인다.
   그 높이를 본문 줄 하나로 잡는다 -- 세션 이름 버튼(.ghost) 안의 글자가 바로
   그 줄이라, 이렇게 두면 옆에 선 두 버튼이 같은 높이로 선다. 1.5rem 을
   박아 두었을 때는 0.75px 씩 낮았다. */
.status-text,
#btnMenu {
  display: inline-flex;
  align-items: center;
  height: calc(var(--line-h) * 1rem);
  line-height: 1;
}
.status-text {
  color: var(--fg-dim);
  font-size: 0.8rem;
  white-space: nowrap;
  /* 글자를 상자 한가운데로 올린다. 줄 상자를 가운데 맞춰도 한글 잉크는 그
     한가운데보다 0.5px 아래에 앉는다 -- 재 보면 상자 중심 21.0 에 잉크 중심
     21.5 였다. 메뉴 버튼이 가운뎃점을 올릴 때 쓴 것과 같은 수로,
     box-sizing 이 border-box 라 높이는 안 바뀌고 안의 것만 밀린다
     (아래 여백 1px → 0.5px 올라간다). */
  padding-bottom: 1px;
}

/* 메뉴 버튼: 보이는 크기와 누를 수 있는 범위를 따로 잡는다.
   보이는 크기를 44px 로 키우면 상단 바가 그만큼 두꺼워지므로,
   글자만 키우고 손가락이 닿는 범위는 아래 가상 요소로 넓힌다. */
#btnMenu {
  position: relative;
  justify-content: center;
  /* 아래 여백으로 글자를 위로 올린다.
     가운뎃점(U+00B7)은 소문자 높이 기준이라 줄 상자 한가운데보다 아래에
     찍힌다. 그래서 상자를 가운데 맞춰도 눈에는 낮아 보인다.
     box-sizing 이 border-box 라 높이는 그대로고 내용만 위로 밀린다
     (아래 여백 4px → 내용이 2px 올라간다). */
  padding: 0 0.375rem 0.25rem;
  font-size: 1.375rem;
  /* 눈에 띄지 않아야 한다. 늘 쓰는 버튼이 아니라 가끔 여는 메뉴다.
     크기로 누르기 쉽게 만들었으니 색까지 밝을 필요는 없다. */
  color: var(--fg-faint);
}
#btnMenu::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  /* 손가락 기준 최소 크기 44x44. 보이는 상자와 무관하게 가운데에 겹친다 */
  width: 2.75rem;
  height: 2.75rem;
  transform: translate(-50%, -50%);
}
/* 상태 표시 버튼 (점 + 글자). 누르면 서버 재시작 시트가 열린다.
   눌리는 것을 색으로만 알린다 -- 테두리 두께를 바꾸면 소수점 테두리가
   기기 픽셀로 내림되어 배율에 따라 안쪽 글자가 흔들린다 (Design.md 공통 원칙 절). */
.statusbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  /* 세션 이름 버튼(.ghost)과 같은 높이로 선다. 34.5px 를 값으로 안 박고
     그쪽이 쓰는 것과 같은 위아래 여백을 준다 -- .ghost 도 0.375rem 여백에
     1.5rem 줄이라 둘이 같은 자리에서 나온다. 값을 박으면 한쪽만 고쳤을 때
     조용히 어긋난다.
     안에 든 것(글자 1.5rem / 점 0.5rem)이 둘 다 크기가 박혀 있어 이 상자는
     상태가 바뀌어도 안 늘어난다. */
  padding: 0.375rem 0.25rem;
  margin: 0;
  border: 0;
  border-radius: 0.375rem;
  background: transparent;
  color: inherit;
  font: inherit;
}

.dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--fg-faint);
  flex: 0 0 auto;
}
.dot[data-state="idle"] {
  background: var(--ok);
}
.dot[data-state="working"] {
  background: var(--warn);
}
/* 다시 띄우는 중. 사고가 아니므로 빨강이 아니라 중립색으로 둔다 */
.dot[data-state="starting"] {
  background: var(--fg-faint);
}
.dot[data-state="offline"],
.dot[data-state="dead"] {
  background: var(--error);
}

/* ---------------------------------------------------------- 대화 로그 */

.log {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  /* 위는 옆여백과 따로 둔다. 옆과 아래는 화면 가장자리와의 여백이지만, 위는
     머리 아래에 붙어 서는 줄과의 틈이라 재는 것이 다르다.
     브라우저가 붙는 줄을 굴러가는 칸의 안여백만큼 안쪽에 세우므로 이 값이
     그대로 그 틈이 된다 -- 실측으로 확인했다. 그래서 눈으로 고르는 값을
     여기 그대로 적는다. */
  padding: var(--log-top) var(--pad) var(--pad);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* 로그 안의 항목은 절대 눌리지 않게 한다.
   .log 가 세로 flex 라 자식은 기본값(flex-shrink: 1)으로 줄어든다. 보통은
   min-height: auto 의 '내용 기준 최소 크기' 가 이를 막아 주는데, overflow 가
   visible 이 아닌 요소는 그 최소 크기가 0 으로 정해진다 (CSS flexbox 규격).
   .card 가 overflow: hidden 이므로 카드만 이 예외에 걸려서, 내용이 쌓여
   화면 높이를 넘는 순간부터 카드가 얇은 띠로 납작해졌다 (말풍선은 overflow 가
   visible 이라 멀쩡했다 -- 그래서 '생각 카드만 압축' 으로 보였다).
   로그는 스크롤로 넘겨 보는 곳이므로 자식은 제 높이를 그대로 지켜야 한다.
   .turn(대화 턴 상자)도 같은 세로 flex 라 같은 함정이 있다 -- 둘 다 적는다. */
.log > *,
.turn > * {
  flex: 0 0 auto;
}

/* 대화 턴 = 보낸 내용 1개 + 그에 딸린 응답 / 카드 / 안내.
   sticky 는 '자기 부모가 화면에 있는 동안' 만 붙어 있으므로, 턴으로 묶어야
   그 턴이 지나가면 붙어 있던 질문도 다음 질문에 밀려 올라간다.
   overflow 를 주지 않는다 (visible 이 아니면 안쪽 sticky 가 죽는다). */
.turn {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.msg {
  padding: 0.625rem 0.8125rem;
  border-radius: var(--radius);
  white-space: pre-wrap;
  word-break: break-word;
  overflow-wrap: anywhere;
}
/* 보낸 내용: 그 턴 맨 위에 붙어 있는 한 줄 머리말 (클로드 코드 식).
   말풍선을 오른쪽에 띄우지 않고 폭을 꽉 채운다 -- 붙어 있는 줄이 폭을 다 덮지
   않으면 그 아래를 지나가는 글자가 옆으로 비어져 보인다. */
.msg.user {
  position: sticky;
  top: 0;
  z-index: 1;
  /* 반투명 금지. 뒤로 지나가는 글자가 비쳐 읽기가 어려워진다 */
  background: var(--user);
  /* 오른쪽은 세모 자리 */
  padding-right: var(--chev-reserve);
}
/* 펼쳐도 붙이기를 풀지 않는다. 풀면 이 줄이 '실제 자리' 로 돌아가고, 화면이
   그 자리를 따라가느라 대화가 그 질문 위치로 끌려간다. 보던 자리에서 내용만
   보고 싶은 것이므로 붙은 채로 그 자리에서 펼친다.
   화면을 통째로 덮는 문제는 아래에서 높이를 제한해 막는다. */
/* 접힘(기본): 한 줄 + 말줄임. 펼침: 보낸 그대로 (줄바꿈 유지) */
.user-text {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.msg.user[data-open="1"] .user-text {
  white-space: pre-wrap;
  /* 붙어 있는 채로 펼치므로 높이를 제한한다. 넘치면 이 안에서만 스크롤하고
     대화 로그는 움직이지 않는다. 가로는 pre-wrap 이라 넘칠 일이 없다 */
  max-height: 40vh;
  overflow-x: hidden;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  /* 굴러가는 상자를 세모 자리까지 넓히고 그만큼 안여백으로 되돌린다.
     글자는 있던 자리에 그대로 서고 막대만 줄 오른쪽 끝으로 나간다 --
     안 넓히면 막대가 세모 안쪽에 그려져 글자 끝과 세모 사이를 덮는다.
     폰의 막대는 자리를 안 먹고 위에 덮여 그려지므로 상자를 넓히는 것 말고는
     비켜 세울 길이 없다. 목록들이 쓰는 방법과 같다 (.proj-list 무리).
     접었을 때는 안 넓힌다 -- 그때는 이 상자의 오른쪽 끝이 곧 말줄임이 찍히는
     자리라, 넓히면 잘린 글자가 세모 밑으로 들어간다. */
  margin-right: calc(var(--chev-reserve) * -1);
  padding-right: var(--chev-reserve);
}
.user-chev {
  position: absolute;
  /* 오른쪽 끝에서 한 눈금 안으로 들인다. 시트 옆여백만큼은 아니고, 붙어 있는
     줄이 화면 폭을 다 쓰므로 끝에 바짝 붙으면 화면 밖으로 나가는 것처럼 보인다 */
  right: var(--chev-right);
  /* 박스 높이가 아니라 첫 줄에 맞춘다 (펼쳐서 여러 줄이 돼도 자리가 그대로).
     첫 줄 한가운데에서 이 상자의 절반을 올린 자리다. 숫자로 안 적는 것은
     세모 크기를 고치면 이 값도 함께 따라가야 하기 때문이다. */
  top: calc(var(--msg-line-mid) - var(--chev-box) / 2);
  padding: 0;
  /* 그림 하나만 든다. 줄 상자가 아니라 그림이 이 상자의 크기를 정한다 --
     글자를 담던 때 쓰던 글자 크기와 줄 높이는 이제 정하는 것이 없다. */
  display: flex;
  color: var(--fg-faint);
  /* 부모(.msg)가 글자 선택을 열어 뒀다. 조작 장치라 여기선 다시 닫는다 */
  -webkit-user-select: none;
  user-select: none;
}
.user-chev svg {
  width: var(--chev-box);
  height: var(--chev-box);
  display: block;
}
/* 보이는 크기는 그대로 두고 손가락이 닿는 범위만 넓힌다 (상단 메뉴 버튼과 같은 방식) */
.user-chev::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 2.75rem;
  height: 2.75rem;
  transform: translate(-50%, -50%);
}
.msg.assistant {
  background: var(--bg-2);
  border: 1px solid var(--line);
}

.note {
  color: var(--fg-faint);
  font-size: 0.8rem;
  text-align: center;
  padding: 0.125rem 0;
  /* 줄바꿈이 든 안내는 그대로 보여 준다. 안 하면 여러 줄이 한 줄로 뭉친다 */
  white-space: pre-line;
}
/* 값을 나열하는 안내는 왼쪽 정렬 + 고정폭이라야 읽힌다.
   진단 출력을 대화창에 찍을 때 쓴다 (지금은 쓰는 곳이 없지만 규칙은 남겨 둔다 --
   같은 종류의 출력이 또 필요해질 때 형식을 다시 정하지 않기 위해서다). */
.note.diag {
  text-align: left;
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  color: var(--fg-dim);
  background: var(--bg-2);
  border-radius: 0.5rem;
  padding: 0.5rem 0.625rem;
  line-height: 1.5;
}
/* 지난 대화와 지금부터를 가르는 줄. 글자 양옆으로 선을 뻗는다 */
.note.dim {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--fg-faint);
}
.note.dim::before,
.note.dim::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--line);
}
.note.done {
  color: var(--ok);
}
.note.warn {
  color: var(--warn);
}
.note.error {
  color: var(--error);
}

/* 접히는 카드 (생각 / 도구 / 결과) */
.card {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.card > summary {
  padding: 0.5rem 0.75rem;
  cursor: pointer;
  color: var(--fg-dim);
  font-size: 0.8125rem;
  list-style: none;
}
.card > summary::-webkit-details-marker {
  display: none;
}
/* 세모와 글자를 나란히 두는 한 겹. summary 자신에 안 걸고 안에 한 겹을 두는
   것은, summary 의 display 를 건드리면 사파리 판에 따라 눌러도 안 펴지는
   자리가 있어서다.
   글자가 여러 줄이 되면 둘째 줄부터는 세모 자리를 비껴 들여쓰인 꼴로 선다 --
   글자 사이에 세모를 끼우면 접힌 줄이 그 밑으로 파고들어 어디부터가 글인지가
   안 보인다. */
.card-head {
  display: flex;
  align-items: flex-start;
  gap: 0.3125rem;
}
/* 접힘 / 펼침 세모. 글자가 아니라 그려서 세운다 (보낸 내용 줄의 세모와 같은 손).
   상자 높이를 글자 한 줄과 같게 두고 그 안에서 가운데에 맞춘다 -- 숫자로 밀어
   맞추면 글자 크기를 고칠 때 따로 놀고, 1em 은 이 줄의 글자 크기를 그대로 따른다. */
.card-mark {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  height: calc(var(--line-h) * 1em);
  color: var(--fg-faint);
}
.card-mark svg {
  width: var(--caret-box);
  height: var(--caret-box);
  display: block;
}
/* 두 모양을 다 세워 두고 지금 상태의 것만 보인다 (돌리지 않는다) */
.card-mark .mark-down {
  display: none;
}
.card[open] > summary .card-mark .mark-right {
  display: none;
}
.card[open] > summary .card-mark .mark-down {
  display: block;
}
/* 글자 칸. 접는 규칙은 세모가 아니라 이 칸이 든다 */
.card-label {
  min-width: 0;
  word-break: break-all;
}
.card > pre {
  margin: 0;
  padding: 0 0.75rem 0.75rem;
  max-height: 24rem;
  overflow: auto;
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  font-size: 0.75rem;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
  color: var(--fg-dim);
}
.card.tool > summary {
  color: #8ab4d8;
}
.card.tool-error > summary,
.card.denied > summary,
.card.error > summary {
  color: var(--error);
}
.card.denied {
  /* 두께가 아니라 색과 안쪽 링으로 강조 */
  box-shadow: inset 0 0 0 1px var(--error);
}

/* 권한 카드.
   칸의 겉모습(배경 / 테두리 / 모서리 / 링)을 .card-wrap 이 갖는다. 안쪽
   details 는 껍데기만 남긴다. 이렇게 해야 취소 / 확인 줄이 details 바깥에
   있으면서도 '칸 안' 으로 보인다 -- 안에 넣으면 접었을 때 통째로 숨는다.
   가르는 데 :has() 를 쓰지 않고 형제 선택자를 쓴다 (옛 사파리에서도 걸린다). */
.card-wrap {
  position: relative;
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  /* 안의 것이 둥근 모서리 밖으로 비어져 나오지 않게 잘라 낸다.
     hidden 이 아니라 clip 인 것이 요점이다 -- hidden 은 스크롤 컨테이너를
     만들어서 안쪽 sticky 가 그 통을 기준으로 삼게 되고, 그러면 붙는 바가
     칸 안에 갇혀 화면 아래에 붙어 있지 못한다. clip 은 자르기만 하고
     스크롤 통을 만들지 않으므로 sticky 는 바깥(.log)을 계속 기준으로 쓴다.
     지원하지 않는 브라우저는 이 줄을 무시한다 -- 그때는 지금까지와 같다. */
  overflow: clip;
}
.card-wrap.denied {
  /* 두께가 아니라 색과 안쪽 링으로 강조 */
  box-shadow: inset 0 0 0 1px var(--error);
}
/* 안쪽 details 는 겉모습을 갖지 않는다.
   overflow 를 visible 로 되돌리는 것이 핵심이다 -- hidden 인 조상이 하나라도
   있으면 아래 sticky 가 죽는다 (보낸 내용 줄에서 겪은 것과 같은 함정). */
.card-wrap > .card {
  background: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
}
.card-acts {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 0.375rem;
}
/* 접혔을 때: 그 줄 바로 오른쪽. 칸 높이가 summary 한 줄이라 거기 겹쳐 앉는다 */
.card:not([open]) + .card-acts {
  position: absolute;
  /* 위아래로 1px 들여 앉힌다. 줄이 짧을 때 버튼이 테두리와 강조 링에
     닿아 밖으로 삐져나온 것처럼 보이는 것을 막는다 */
  top: 1px;
  right: 0.5rem;
  bottom: 1px;
}
/* 라벨이 버튼 밑으로 파고들지 않게 자리를 비워 둔다.
   비울 폭은 그 카드에 선 버튼 수를 따른다 -- 지난 일을 알리는 카드는 둘이고,
   답을 기다리는 카드(.wait)는 '이번 턴은 계속 허용' 이 하나 더 서서 셋이다.
   한 값으로 두면 좁은 쪽이 남거나 넓은 쪽이 글자를 덮는다. */
.card.ask:not([open]) > summary {
  padding-right: 7rem;
}
.card.ask.wait:not([open]) > summary {
  padding-right: 15rem;
}
/* 답이 끝난 카드에는 누를 것이 없다. 비워 두던 자리를 도로 거둔다 --
   안 거두면 답한 뒤에도 머리 오른쪽이 통째로 비어 무언가 빠진 것처럼 보인다. */
.card.ask.done:not([open]) > summary {
  padding-right: 0.75rem;
}
/* 펼쳤을 때: 칸 안, 마지막 줄 다음 줄.
   칸 아래쪽에 붙여 둔다 -- 내용이 길어 스크롤을 다 내리지 않아도 버튼이
   칸 안에서 계속 보여야 한다 (보낸 내용 줄이 위에 붙어 있는 것과 같은 방식).
   배경을 깔아야 밑으로 지나가는 글이 비쳐 보이지 않는다. 좌우 / 아래 1px 을
   비워 두는 것은 강조 링을 덮지 않기 위해서다. */
.card[open] + .card-acts {
  position: sticky;
  /* 대화 칸 아래 끝에 붙인다. 브라우저는 붙는 줄을 굴러가는 칸의 안여백만큼
     안쪽에 세우므로, 0 으로 두면 그 안여백만큼(실측 13.13px) 떠서 아래 작업 중
     표시와 갈라진다. 그만큼 도로 당겨야 맞붙는다. */
  bottom: calc(var(--pad) * -1);
  /* 칸 안쪽으로 1px 들여 앉힌다 -- 테두리와 강조 링을 덮지 않게.
     곡률은 두 번 빼야 한다. 칸의 바깥 곡률에서 테두리 1px 을 빼면 안쪽
     곡선이고, 이 바는 거기서 또 1px 들어와 앉기 때문이다.
     한 번만 빼서(=안쪽 곡선 그대로) 두었더니 모서리가 그만큼 더 볼록해
     여전히 비어져 나왔다.
     위의 overflow: clip 이 최종 안전장치다 -- 계산이 어긋나도 칸 밖으로는
     못 나간다. 그래도 곡률을 맞춰 두는 것은 clip 을 모르는 브라우저에서
     그대로 드러나기 때문이다. */
  margin: 0 1px 1px;
  padding: 0.375rem 0.5rem;
  background: var(--bg-2);
  border-radius: 0 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px);
}
.card-act {
  padding: 0.3125rem 0.625rem;
  border-radius: 0.5rem;
  background: var(--bg-3);
  color: var(--fg-dim);
  font-size: 0.75rem;
}
.card-act:active {
  filter: brightness(1.15);
}
.card-act.yes {
  background: var(--accent);
  color: var(--accent-fg);
  font-weight: 600;
}
.card-act.yes:active {
  filter: brightness(0.92);
}
.card.dim > summary {
  color: var(--fg-faint);
}

/* ---------------------------------------------------------- 맨 아래로 */

/* 위로 올려 읽는 동안에만 선다. 입력줄 바로 위에 두는 것은 내릴지를 사람이
   정하고 그 손이 이미 입력칸 언저리에 가 있어서다. 맨 아래를 보고 있으면
   할 일이 없어 자리째 빠진다.
   편집기 AI 칸의 같은 손잡이를 옮긴 것이고 서는 규칙도 같다.

   자리를 안 먹고 대화 위에 겹쳐 뜬다. 한 줄을 차지하면 손잡이는 가운데
   하나뿐인데 그 줄이 폭을 다 먹어, 양옆의 아무것도 없는 자리가 통째로 띠가
   되어 옆의 글을 덮는다. 높이를 0 으로 두고 손잡이만 띄우면 가려지는 것이
   손잡이 너비뿐이다. */
.log-down {
  position: relative;
  height: 0;
  flex-shrink: 0;
}
.log-down-btn {
  /* 자리를 안 먹는 상자에서 띄우므로 가로 가운데는 스스로 잡는다 */
  position: absolute;
  left: 50%;
  bottom: 0.375rem;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.3125rem 0.75rem;
  border: 1px solid var(--line);
  /* 캡슐. 높이의 절반보다 크게 주면 좌우 끝이 반원으로 잘린다 */
  border-radius: 999px;
  /* 글 위에 뜨므로 바탕이 비면 안 된다 */
  background: var(--bg-3);
  color: var(--fg-dim);
  font-size: 0.75rem;
  white-space: nowrap;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
}
.log-down-btn:active {
  filter: brightness(1.15);
}
.log-down-btn svg {
  width: 0.75rem;
  height: 0.75rem;
  display: block;
}

/* ---------------------------------------------------------- 진행 표시 */

.working {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.375rem var(--pad);
  background: var(--bg-3);
  border-top: 1px solid var(--line);
  color: var(--fg-dim);
  font-size: 0.8rem;
  flex: 0 0 auto;
}
.working > span:nth-child(2) {
  flex: 1 1 auto;
}
/* 회전 대신 밝기 맥동 */
.pulse {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--accent);
  animation: pulse 1.4s ease-in-out infinite;
  flex: 0 0 auto;
}
@keyframes pulse {
  0%,
  100% {
    opacity: 0.3;
  }
  50% {
    opacity: 1;
  }
}
@media (prefers-reduced-motion: reduce) {
  .pulse {
    animation: none;
    opacity: 0.8;
  }
}

/* ---------------------------------------------------------- 입력창 */

.composer {
  display: flex;
  /* 입력칸과 보내기 버튼의 세로 중앙을 맞춘다.
     flex-end 로 아랫변을 맞추면, 높이가 다를 때 버튼이 위로 삐져나와
     짝이 안 맞아 보인다. 입력칸이 여러 줄로 늘어나도 버튼은 가운데에 선다. */
  align-items: center;
  gap: 0.5rem;
  /* 안전 영역 여백은 여기 두지 않는다. 아래 .statusbar 가 화면 맨 아래를
     차지하므로 그쪽이 책임진다. 양쪽에 다 넣으면 두 번 쌓인다. */
  /* 입력칸 자체가 한 줄이 안 잘리는 최소 높이를 이미 갖고 있으므로, 이 여백은
     순수하게 바깥 테두리와의 간격이다. 그래도 너무 얇으면 입력칸이 위아래
     경계선에 끼인 것처럼 보인다 -- 0.25rem 까지 줄였다가 되돌린 값이다.
     대화가 보이는 높이를 지키기 위해 0.5rem 까지는 가지 않는다. */
  padding: 0.375rem var(--pad);
  background: var(--bg-2);
  border-top: 1px solid var(--line);
  flex: 0 0 auto;
}
/* 입력칸을 감싸는 칸. 예약 아이콘을 입력칸 '안' 에 놓기 위한 기준점이다.
   입력칸에 직접 겹치려면 기준이 될 상대 위치 칸이 필요하다. */
.composer-field {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
}
.composer textarea {
  flex: 1 1 auto;
  min-width: 0;
  /* 한 줄이 안 잘리는 최소 높이를 지킨다.
     box-sizing: border-box 라 height 안에 안쪽 여백과 테두리가 모두 들어간다.
       글자 한 줄 1.4em(약 22px) + 안쪽 여백 위아래 14px + 테두리 2px = 약 39px
     이보다 작게 잡으면 글자 아랫부분이 잘린다 (2.5rem=40px 로 뒀다가 잘렸다).
     내용이 길어지면 app.js 의 growInput 이 늘린다. */
  min-height: 2.5rem;
  height: 2.5rem;
  max-height: 8.25rem;
  padding: 0.4375rem 0.75rem;
  /* 오른쪽 안여백은 예약 아이콘 자리다. 이게 없으면 글이 아이콘 밑으로 흘러
     들어가 가려진다.
     눈에 보이는 여백은 버튼이 아니라 아이콘(1.125rem) 기준으로 재야 한다 --
     버튼 배경이 입력칸과 같은 색이라 조각은 안 보이고, 버튼 속 좌우 여백
     0.25rem 이 숨은 채로 왼쪽을 더 벌린다.
       아이콘 왼쪽 0.25rem + 아이콘 1.125rem + 버튼 속 0.25rem + 띄움 0.25rem
     왼쪽을 오른쪽(0.5rem)보다 좁게 잡은 것은 눈으로 맞춘 값이다 -- 글자는
     제 상자보다 안쪽에서 끝나므로(한글 좌우 side bearing) 수치를 같게 두면
     왼쪽이 더 벌어져 보인다. 한글 한 글자가 더 들어갈 자리이기도 하다.
     버튼 크기나 오른쪽 띄움을 바꾸면 이 값도 같이 본다.
     반드시 위의 padding 축약보다 뒤에 있어야 한다 -- 앞에 두면 축약이 이
     값을 0.75rem 로 되돌려 지운다 (실제로 그렇게 가려져 있었다). */
  padding-right: 1.875rem;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-family: inherit;
  font-size: 1rem; /* 16px 미만이면 iOS 가 화면을 확대한다 */
  line-height: 1.4;
  resize: none;
  /* 줄 끝에서 마침표를 치면 앞 글자가 같이 아래로 끌려 내려가는 것을 막는다.
     원인은 행두 금칙 -- . , ) 」 같은 부호는 줄 첫머리에 올 수 없어서, 부호가
     넘어갈 상황이면 앞 글자를 같이 내려보낸다. 읽기용 글에서는 옳은 처리지만
     입력칸에서는 치는 도중에 글자가 튀어 보인다.
     anywhere 는 모든 글자 사이에 줄바꿈 자리를 만들어 그 금지 규칙을 무시한다.
     word-break: break-all 로는 안 된다 -- 그쪽은 '단어 안에서 쪼갤 수 있다' 일
     뿐이라 부호를 줄 첫머리에 두지 않으려는 규칙은 그대로 남는다. */
  line-break: anywhere;
}
.composer textarea:focus {
  box-shadow: inset 0 0 0 2px var(--accent);
}
.composer .send {
  width: auto;
  flex: 0 0 auto;
  /* 입력칸이 한 줄일 때의 높이와 같게 맞춘다. 그래야 둘이 한 쌍으로 보인다.
     입력칸이 여러 줄로 늘어나도 버튼은 이 높이 그대로 가운데에 선다. */
  height: 2.5rem;
  min-height: 2.5rem;
  padding: 0 1rem;
}
/* ---------------------------------------------------------- 첨부 미리보기 */

/* 입력칸 바로 위. 붙인 사진이 없으면 hidden 이라 자리를 먹지 않는다. */
/* ---------------------------------------------------------- 갈래 고르기 */

/* 답 안에서 고르라고 내민 갈래 한 줄. 편집기 브릿지 창의 같은 자리와 같은
   짜임이다 -- 앞에 표 한 칸, 그 옆에 글.

   블록으로 세운다. 답 칸이 글자를 그대로 그리는 자리라(pre-wrap) 인라인으로
   두면 앞뒤 줄바꿈 글자에 끌려가 줄 가운데 박힌다.

   글자 자리를 answer 칸의 글과 맞추지 않는다 -- 이것은 줄글이 아니라 누르는
   자리라, 눌리는 것이라는 표시가 먼저 읽혀야 한다. */
.ai-opt {
  display: flex;
  /* 표는 그 바 높이의 한가운데 선다. 줄이 여럿으로 감겨도 마찬가지다 --
     표가 첫 줄에 붙으면 감긴 바에서만 위쪽으로 쏠려, 같은 목록의 줄들이
     서로 다른 자리에서 시작하는 것처럼 보인다. */
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  margin: 0.25rem 0;
  padding: 0.5rem 0.625rem;
  border-radius: 0.5rem;
  background: var(--bg-3);
  box-shadow: inset 0 0 0 1px var(--line);
  text-align: left;
  /* 답 칸이 pre-wrap 이라 그대로 두면 이 안의 글도 줄바꿈 글자를 그린다 */
  white-space: pre-wrap;
  word-break: break-word;
  overflow-wrap: anywhere;
}
.ai-opt:active { filter: brightness(1.15); }
/* 고른 줄. 테두리 두께를 안 바꾼다 -- 소수점 테두리가 기기 픽셀로 내림되어
   내용물이 미세하게 흔들린다. 안쪽 링과 표의 색으로만 가른다. */
.ai-opt.on {
  box-shadow: inset 0 0 0 1px var(--accent);
}
/* 앞에 서는 표 한 칸. 가나다 한 글자이거나 번호다. */
.ai-opt-mark {
  flex: 0 0 auto;
  min-width: 1.375rem;
  padding: 0 0.25rem;
  border-radius: 0.375rem;
  background: var(--bg);
  color: var(--fg-dim);
  font-size: 0.8125rem;
  font-weight: 600;
  text-align: center;
  white-space: nowrap;
}
.ai-opt.on .ai-opt-mark {
  background: var(--accent);
  color: var(--accent-fg);
}
.ai-opt-text {
  flex: 1;
  min-width: 0;
}

/* 갈래 묶음 아래의 직접 적는 칸. 갈래 바와 같은 상자를 쓴다 -- 그래야 그
   묶음의 마지막 갈래로 읽히고 글도 같은 자리에서 시작한다. */
.ai-opt-own {
  display: flex;
  /* 표와 붙임 손잡이는 칸 높이의 한가운데 선다. 칸이 여러 줄로 자라도
     마찬가지다 -- 갈래 바와 같은 값이라, 묶음의 마지막 줄로 읽힌다. */
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  margin: 0.25rem 0 0;
  padding: 0.5rem 0.625rem;
  border-radius: 0.5rem;
  background: var(--bg-3);
  box-shadow: inset 0 0 0 1px var(--line);
}
.ai-opt-own-text {
  flex: 1;
  min-width: 0;
  background: transparent;
  color: var(--fg);
  border: none;
  resize: none;
  padding: 0;
  /* 폰에서 글자가 16px 아래면 자판이 뜰 때 화면을 확대한다 */
  font-size: 1rem;
  line-height: calc(var(--line-h) * 1rem);
  /* 키우는 셈이 높이를 정하므로 스스로 굴러가지 않게 둔다. 안 막으면 자라기
     전 한 줄 안에서 글이 굴러가 아랫줄이 안 보인다. */
  overflow: hidden;
}
.ai-opt-own-text::placeholder { color: var(--fg-faint); }
.ai-opt-own-go {
  flex: 0 0 auto;
  padding: 0.25rem 0.5rem;
  border-radius: 0.375rem;
  background: var(--bg);
  color: var(--fg-dim);
  font-size: 0.8125rem;
}
.ai-opt-own-go:active { filter: brightness(1.15); }

/* 고른 갈래가 지시문으로 붙는 줄. 사진 줄과 같은 자리에 같은 꼴로 선다. */
.pastedbar {
  display: flex;
  gap: 0.375rem;
  align-items: center;
  flex: 0 0 auto;
  padding: 0.5rem var(--pad) 0;
  background: var(--bg-2);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.pasted-chip {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  flex: 0 0 auto;
  max-width: 60%;
  padding: 0.3125rem 0.5rem;
  border-radius: 0.5rem;
  background: var(--bg-3);
  box-shadow: inset 0 0 0 1px var(--accent);
  font-size: 0.8125rem;
}
.pasted-chip:active { filter: brightness(1.15); }
/* 한 줄로 줄여 세운다. 붙인 것이 무엇인지만 알면 되고, 전문은 답 안의 그 줄이
   들고 있다 -- 칩이 길어지면 줄이 밀려 입력칸이 화면 밖으로 나간다. */
.pasted-text {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.pasted-x {
  flex: 0 0 auto;
  color: var(--fg-faint);
}

.attachbar {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  flex: 0 0 auto;
  padding: 0.5rem var(--pad) 0;
  background: var(--bg-2);
  /* 장수가 상한(4장)이라 넘칠 일은 없지만, 좁은 폰에서 밀리지 않게 둔다 */
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.attach-item {
  position: relative;
  flex: 0 0 auto;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 0.5rem;
  overflow: hidden;
  background: var(--bg-3);
}
.attach-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.attach-size {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 0.0625rem 0.25rem;
  font-size: 0.625rem;
  color: var(--fg);
  background: rgba(0, 0, 0, 0.55);
  text-align: center;
}
/* 지우기. 손가락으로 누를 수 있게 아이콘보다 넉넉히 잡는다 */
.attach-del {
  position: absolute;
  top: 0;
  right: 0;
  width: 1.375rem;
  height: 1.375rem;
  padding: 0;
  border: 0;
  border-radius: 0 0 0 0.5rem;
  background: rgba(0, 0, 0, 0.6);
  color: var(--fg);
  font-size: 0.875rem;
  line-height: 1;
}
.attach-del:active {
  background: rgba(0, 0, 0, 0.85);
}

/* 보낸 턴에 붙는 '사진 N장' 표시. 글 없이 사진만 보낸 턴이
   빈 줄로 보이지 않게 한다 */
.user-attach {
  margin-left: 0.375rem;
  padding: 0.0625rem 0.375rem;
  border-radius: 0.625rem;
  font-size: 0.75rem;
  color: var(--fg-dim);
  background: var(--bg-3);
}

/* ---------------------------------------------------------- PC 파일 고르기 */

.files-path {
  margin: 0 0 0.5rem;
  font-size: var(--crumb-size);
  color: var(--fg-dim);
  /* 경로는 길다. 줄바꿈을 허용해 시트 밖으로 삐져나가지 않게 한다 */
  word-break: break-all;
}
/* 조각으로 나눈 경로 줄. 조각을 눌러 그 겹으로 간다.
   조각 안에서는 글자를 안 쪼갠다 -- break-all 이 걸리면 폴더 이름 하나가
   두 줄로 갈려 어디까지가 한 조각인지가 안 보인다. 줄은 조각 사이에서
   접힌다 (flex-wrap). */
.crumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.125rem 0.25rem;
  word-break: normal;
  /* 머리 아래 간격을 옆 시트와 눈으로 맞춘다.
     옆 시트에서 머리 다음에 보이는 것은 바탕을 칠한 줄이라 상자 위 끝이 곧
     보이는 자리다. 이 시트에서 보이는 것은 글자뿐이고, 글자는 상자 위에서
     두 몫만큼 내려와 앉는다 -- 조각에 준 위 안여백과 줄 상자가 글자 위에
     이고 있는 빈 몫이다. 둘을 도로 당겨야 두 시트의 간격이 같아 보인다.
     하나만 당기면 이 시트만 머리 아래가 벌어져 보인다. */
  margin-top: calc(var(--crumb-pad) * -1 - var(--crumb-lead));
  /* 아래 간격은 --crumb-gap 이 정한다. 그 값은 글자에서 목록까지 보이는
     값이라, 그 사이에 끼는 두 몫(조각의 아래 안여백과 줄 상자가 글자 아래에
     인 몫)을 빼서 여백에 적는다. 붙으면 어디까지가 지금 자리를 말하는 줄이고
     어디부터가 목록인지가 한눈에 안 갈리고, 넉넉히 띄우면 이 줄이 목록에서
     떨어져 나와 따로 선 안내판처럼 보인다. */
  margin-bottom: calc(var(--crumb-gap) - var(--crumb-pad) - var(--crumb-lead));
}
.crumb-step,
.crumb-here {
  padding: var(--crumb-pad) 0.25rem;
  border-radius: 0.25rem;
  font-size: inherit;
  /* 조각 하나가 시트보다 길 수 있다. 그때만 그 조각 안에서 자른다 */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.crumb-step {
  background: transparent;
  color: var(--fg-dim);
  text-decoration: underline;
  text-underline-offset: 0.15rem;
}
.crumb-step:active {
  color: var(--fg);
}
/* 지금 서 있는 자리. 갈 곳이 아니라 밑줄을 안 긋고 글자만 진하게 둔다 */
.crumb-here {
  color: var(--fg);
  font-weight: 600;
}
.crumb-sep {
  color: var(--fg-faint);
  flex-shrink: 0;
}
.file-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 55dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.file-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  text-align: left;
  padding: var(--row-pad) 0.75rem;
  min-height: var(--row-h);
  border: 0;
  border-radius: 0.5rem;
  background: var(--bg-3);
  color: var(--fg);
  margin-bottom: 0.375rem;
}
.file-row:active {
  filter: brightness(1.15);
}
/* 프로젝트 폴더 고르기에서는 누르는 즉시 밝아지지 않는다.
   여기서는 폴더를 여러 칸 타고 들어가느라 줄을 자주 누르는데, 누를 때마다
   번쩍이면 목록이 어수선하다 (프로젝트 목록과 같은 이유).
   PC 파일 고르기(#files)는 그대로 둔다 -- 거기는 한 번 골라 끝난다. */
#browse .file-row:active {
  filter: none;
}
.file-icon {
  flex: 0 0 auto;
  width: 1rem;
  color: var(--fg-dim);
}
.file-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}
.file-size {
  flex: 0 0 auto;
  font-size: 0.75rem;
  color: var(--fg-dim);
}

/* ---------------------------------------------------------- 세션 현황 줄 */

/* 입력칸 아래 한 줄. 늘 보이되 눈에 띄지 않아야 한다.
   composer 가 안전 영역 여백을 이미 갖고 있으므로 여기서는 다시 넣지 않는다 --
   이 줄이 composer 아래에 붙어 화면 맨 아래를 차지한다. */
.statusbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  width: 100%;
  flex: 0 0 auto;
  /* 위아래 여백을 반씩 나눠 갖는다 -- 아이콘과 문구가 줄 한가운데 서야 한다.
     아래쪽에만 안전 영역을 주면 그만큼 내용이 위로 쏠려 보인다. 줄 전체 높이는
     그대로 두고 (위 + 아래 합이 예전과 같다) 반으로 갈라 위아래에 똑같이 준다.
     안전 영역은 홈 바가 있는 폰의 약 34px 이다. 그대로 두면 아래가 허전해
     1.5rem 을 덜어낸다. 덜어낸 만큼 내용이 홈 바 쪽으로 내려가므로 무한정
     줄일 수는 없다 -- 홈 바 자체에 글자가 닿기 시작한다.
     안전 영역이 없는 기기(안드로이드 / 데스크탑)는 뒤의 최소값을 쓴다.
     첫 줄은 calc 을 못 읽는 브라우저용 대칭 기본값이다. */
  --sb-gap: max(calc(env(safe-area-inset-bottom, 0px) - 1.5rem), 0.25rem);
  padding: 0.1875rem var(--pad);
  padding-top: calc((0.125rem + var(--sb-gap)) / 2);
  padding-bottom: calc((0.125rem + var(--sb-gap)) / 2);
  background: var(--bg-2);
  border-top: 1px solid var(--line);
  color: var(--fg-faint);
  font-size: 0.7rem;
  text-align: left;
}

/* 왼쪽 아이콘 줄 */
.statusbar-icons {
  display: flex;
  align-items: center;
  gap: 0.125rem;
  flex: 0 0 auto;
}
.iconbtn {
  /* 터치 범위를 아이콘보다 넉넉히 잡는다. 화면 맨 아래이고 키보드 바로 위라
     손가락이 빗나가기 쉽다 */
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--iconbtn-box);
  height: var(--iconbtn-box);
  padding: 0;
  border: 0;
  border-radius: 0.5rem;
  background: transparent;
  color: var(--fg-dim);
  -webkit-appearance: none;
  appearance: none;
}
.iconbtn svg {
  width: var(--iconbtn-glyph);
  height: var(--iconbtn-glyph);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* 눌림 강조는 색으로만 한다. 배경을 켜면 그 순간 버튼의 네모난 터치 영역이
   드러나는데, 평소 배경이 없는 자리라 그게 눈에 거슬린다 (아래 [주의] 참조).
   테두리 두께도 건드리지 않는다 -- 픽셀이 내리면 안쪽 내용이 흔들린다 */
.iconbtn:active {
  color: var(--fg);
}
/* 사용량 고리. 회전시키지 않는다 -- 진행은 채워지는 길이로만 나타낸다 */
.ring-track {
  stroke: var(--line);
}
.ring-fill {
  stroke: var(--fg-dim);
  transition: stroke-dasharray 0.3s ease;
}
.ring-fill.warn {
  stroke: var(--warn);
}
.ring-fill.over {
  stroke: var(--error);
}

/* 오른쪽 두 줄. 위가 권한 모드, 아래가 턴 / 토큰 / 한도.
   누르면 설정 시트가 열린다 (예전에 줄 전체가 하던 역할) */
.statusbar-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.0625rem;
  flex: 1 1 auto;
  min-width: 0;
  padding: 0.125rem 0;
  border: 0;
  border-radius: 0.5rem;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: right;
}
.statusbar-perm {
  color: var(--fg-dim);
  line-height: 1.3;
}
.statusbar-usage {
  color: var(--fg-faint);
  font-size: 0.65rem;
  line-height: 1.3;
  /* 길어지면 줄바꿈 대신 잘라낸다. 두 줄이 세 줄이 되면 이 줄의 높이가
     바뀌어 그 위 대화 영역까지 흔들린다 */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.set-label {
  margin: 0.875rem 0 0.375rem;
  color: var(--fg);
  font-size: 0.8125rem;
  font-weight: 600;
}
.set-label:first-of-type {
  margin-top: 0.25rem;
}
.set-hint {
  margin: 0.5rem 0 0;
  color: var(--fg-faint);
  font-size: 0.7rem;
  line-height: 1.5;
}
/* 안내문 안에서 '지금 상태' 만 눈에 띄게 한다. 설명은 늘 같지만 이 값은
   그때그때 다르고, 예상과 다르면 사용자가 알아채야 하는 부분이다. */
.set-note {
  color: var(--fg-dim);
}
/* 재시작 안내문만 줄 바꿈을 그대로 살린다 -- '지금 상황' 과 '무엇이 남는가' 를
   다른 줄로 나눠 읽게 하려는 것이다.
   .set-hint 전체에 걸면 안 된다: 설정 시트의 안내문은 HTML 에 여러 줄로 적혀
   있어서, 그 줄 바꿈이 그대로 화면에 나타난다 */
#restartHint {
  white-space: pre-line;
}
.set-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.8125rem;
}
.set-table td {
  padding: 0.3125rem 0;
  border-bottom: 1px solid var(--bg-3);
}
.set-table td:first-child {
  color: var(--fg-dim);
}
.set-table td:last-child {
  text-align: right;
  color: var(--fg);
  font-variant-numeric: tabular-nums;
}
/* 고른 항목 표시. 테두리 두께가 아니라 안쪽 링으로 (Design.md 공통 원칙 절) */
.menu-list button.chosen {
  box-shadow: inset 0 0 0 1px var(--accent);
  color: var(--fg);
}
/* 설명이 여러 줄로 감긴다. 이름과 붙어 보이지 않게 간격과 줄높이를 준다 */
.perm-desc {
  display: block;
  color: var(--fg-faint);
  font-size: 0.7rem;
  line-height: 1.55;
  margin-top: 0.25rem;
  /* 목록 버튼이 한 줄짜리 기준이라 위아래가 답답하다 */
  white-space: normal;
}
/* 설명이 붙는 항목은 한 줄짜리 기준 여백으로는 답답하다 */
.menu-list button.perm-item {
  padding-top: 0.625rem;
  padding-bottom: 0.625rem;
}
.perm-name {
  display: block;
  color: var(--fg);
  font-weight: 600;
}

/* ---------------------------------------------------------- 시트 */

.sheet {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: flex-end;
  z-index: 10;
}
.sheet-inner {
  width: 100%;
  max-height: 80dvh;
  overflow-y: auto;
  background: var(--bg-2);
  border-top-left-radius: 1rem;
  border-top-right-radius: 1rem;
  padding: var(--pad) var(--pad) max(env(safe-area-inset-bottom), var(--pad));
}
.sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.5rem;
  /* 모든 시트의 제목이 같은 높이에서 시작하게 한다.
     프로젝트 시트에는 머리에 버튼(수정 / 삭제)이 앉아 머리가 그만큼 두껍다.
     그것만 두꺼우면 시트마다 제목 위 여백이 달라 보이므로, 버튼이 없는
     시트도 같은 높이를 잡아 둔다 (버튼이 있는 쪽 기준).
     세션 / 프로젝트 시트 머리에는 서로를 여는 아이콘이 앉는다. 값을 그
     버튼 상자에 맞춘다 -- 버튼을 상자에 맞춰 줄이면 손가락이 닿는 범위가
     그만큼 좁아지고, 그 둘만 두꺼우면 시트마다 제목 위 여백이 또 갈린다. */
  min-height: var(--iconbtn-box);
}
/* 머리 끝에 서는 아이콘은 그 상자만큼 당긴다.
   상자가 그림보다 커서 그냥 두면 그림이 상자 안쪽으로 들어가 앉고, 그만큼
   오른쪽 여백이 왼쪽 제목 여백보다 넓어 보인다. 당기면 그림의 오른쪽 끝이
   제목 글자의 왼쪽 끝과 같은 선에 선다.
   당기는 것은 상자이고 손가락이 닿는 범위는 안 줄인다 -- 크기를 줄여 맞추면
   여백은 맞아도 누르기가 그만큼 어려워진다. */
.sheet-head .iconbtn {
  margin-right: var(--iconbtn-pull);
}
/* 세션 시트의 머리는 장을 갈아 끼우는 자리다. 만질 수 있다는 것을 글로
   안 알리고 손 모양이 그 일을 한다.
   상자를 안 건드린다 -- 장이 갈리면 옆에 뱃지가 서고 걷히는데 여기에 여백이나
   테를 두면 그 상자가 함께 자라, 머리 높이가 장마다 달라진다.
   뱃지 자체의 옷은 여기서 안 적는다. 프로젝트 줄의 임시 표와 같은 상자를
   그대로 쓰므로(.sess-tag) 여기 다시 적으면 같은 값이 세 벌이 된다. */
.sheet-head-swap {
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.sheet-head-swap:active { opacity: 0.7; }

.proj-list,
.sess-list,
.conv-list,
.menu-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
/* 대화 목록은 길어질 수 있다. 시트가 화면을 넘지 않게 여기서만 스크롤한다 */
/* 목록만 굴러가고 시트 맨 아래 띠는 늘 보인다.
   목록에 상한을 안 두면 시트 전체가 굴러가고, 그러면 길게 훑는 동안 아래
   버튼이 화면 밖으로 밀려 내려간다. */
.conv-list,
.proj-list,
.sess-list {
  max-height: 55dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
/* 굴러가는 목록은 시트 옆여백까지 넓히고 그만큼 안여백으로 되돌린다.
   줄은 있던 자리에 그대로 서고 스크롤 막대만 시트 바깥쪽 끝으로 나간다.
   폰의 막대는 자리를 안 먹고 위에 덮여 그려지므로, 상자를 안 넓히면 그것이
   줄 위에 얹혀 오른쪽 끝 글자를 가린다. */
.conv-list,
.proj-list,
.sess-list,
.file-list {
  margin-right: calc(var(--pad) * -1);
  padding-right: var(--pad);
}
.proj-list button,
.sess-list button,
.conv-list button,
.menu-list button {
  display: block;
  width: 100%;
  text-align: left;
  padding: var(--row-pad) 0.75rem;
  min-height: var(--row-h);
  border-radius: 0.5rem;
  background: var(--bg-3);
  margin-bottom: 0.375rem;
}
.conv-list button:active,
.menu-list button:active {
  filter: brightness(1.15);
}
/* 프로젝트 줄만 누르는 즉시 깜박이지 않는다.
   여기서는 한 번 두드리는 것이 '고르기' 라 훑는 동안 손이 자주 닿는데,
   닿을 때마다 번쩍이면 목록이 어수선해진다. 눈에 보이는 표시는 길게 눌러
   잡혔을 때만 준다 (아래 .proj-row[data-dragging], 예약 줄과 같은 규칙). */
.proj-list li,
.sess-list li {
  /* 수정 / 삭제 버튼이 이 줄 안에 겹쳐 앉는다 */
  position: relative;
  /* 세로 넘기기는 브라우저에 맡기고, 잡힌 뒤의 끌기는 우리가 막는다.
     예약 줄과 같은 값이다 */
  touch-action: pan-y;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
/* 놓고 나서 새 순서를 받는 사이. 전환을 꺼 두어 '한 칸 튀었다가 제자리로
   미끄러지는' 두 번 움직임을 없앤다 (app.js 의 projDragEnd 가 켜고 끈다).

   :not() 을 함께 쓰는 이유는 오직 우선순위 때문이다.
   아래 전환 규칙이 .proj-list li:not([data-dragging="1"]) 라 무게가 같고,
   같으면 나중에 쓰인 쪽이 이긴다. 그래서 이 규칙만으로는 전환이 안 꺼졌다 --
   놓는 순간 비켜 서 있던 줄들이 제 transform 을 0.2s 에 걸쳐 풀어서,
   한 칸 위로 튀었다가 도로 내려오는 출렁임이 보였다.
   (예약 목록 쪽은 .queue-list ... .queue-row 라 무게가 이미 더 무겁다.) */
.proj-list[data-settling="1"] li,
.sess-list[data-settling="1"] li,
.proj-list[data-settling="1"] li:not([data-dragging="1"]),
.sess-list[data-settling="1"] li:not([data-dragging="1"]) {
  transition: none;
}
/* 끌고 있는 줄. 다른 줄 위로 떠 보이게 한다 (예약 줄과 같은 값) */
.proj-list li[data-dragging="1"],
.sess-list li[data-dragging="1"] {
  position: relative;
  z-index: 2;
  cursor: grabbing;
  opacity: 0.92;
  box-shadow: 0 0.5rem 1.25rem rgba(0, 0, 0, 0.35);
}
/* 비켜 주는 줄이 부드럽게 밀려나게 한다. 끄는 줄은 뺀다 --
   손가락을 한 박자도 늦지 않게 그대로 따라와야 한다 */
.proj-list li:not([data-dragging="1"]),
.sess-list li:not([data-dragging="1"]) {
  transition: transform 0.2s cubic-bezier(0.2, 0, 0.2, 1);
}
/* 프로젝트 줄은 이제 막지 않는다 (폴더가 없어도 고쳐 쓸 수 있어야 한다).
   흐림은 .proj-item.missing 이 맡는다. 이 규칙은 다른 목록을 위해 남긴다. */
.conv-list button:disabled,
.menu-list button:disabled {
  opacity: 0.4;
}
/* 메뉴 안의 켜고 끄는 줄. 다른 메뉴 항목과 같은 칸 모양을 유지하되
   오른쪽에 토글을 얹는다. 버튼이 아니라 토글인 이유는 '지금 어느 상태인가'
   가 누르기 전에 보여야 하기 때문이다. */
.menu-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  padding: 0.6875rem 0.75rem;
  border-radius: 0.5rem;
  background: var(--bg-3);
  margin-bottom: 0.375rem;
  box-sizing: border-box;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.menu-toggle:active {
  filter: brightness(1.15);
}
.menu-toggle-name {
  color: var(--fg);
}
/* 토글 자체는 노트 앱과 같은 규격이다 (51x31, 손잡이 27, 이동 20).
   손에 익은 크기가 앱마다 다르면 같은 동작인지 알아보기 어렵다. */
.toggle-switch {
  position: relative;
  display: inline-block;
  flex: 0 0 auto;
  width: 3.1875rem;
  height: 1.9375rem;
}
.toggle-switch input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.toggle-slider {
  position: absolute;
  inset: 0;
  border-radius: 1.9375rem;
  background: var(--line);
  transition: background 0.25s ease;
}
.toggle-slider::before {
  content: "";
  position: absolute;
  left: 0.125rem;
  bottom: 0.125rem;
  width: 1.6875rem;
  height: 1.6875rem;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.2);
  transition: transform 0.25s ease;
}
.toggle-switch input:checked + .toggle-slider {
  background: #34c759;
}
.toggle-switch input:checked + .toggle-slider::before {
  transform: translateX(1.25rem);
}
/* 켜는 중에는 손을 못 대게 한다. 권한 창이 떠 있는 동안 또 누르면
   등록이 두 번 일어난다 */
.toggle-switch input:disabled + .toggle-slider {
  opacity: 0.5;
}
/* 고른 줄과 지금 열려 있는 프로젝트는 같은 표시를 쓴다.
   둘은 동시에 뜨지 않는다 -- 고른 줄이 생기면 활성 표시를 감추므로
   (projectRow), 화면에서는 이 테두리 하나가 줄 사이를 옮겨 다니는 것으로
   보인다. 고를 때 표시를 새로 만들면 그게 호버처럼 읽힌다. */
.proj-list button.current,
.sess-list button.current,
.proj-list button.selected,
.sess-list button.selected {
  box-shadow: inset 0 0 0 1px var(--accent);
}
.proj-name {
  display: block;
  font-weight: 600;
}
.proj-cwd {
  display: block;
  color: var(--fg-faint);
  font-size: var(--row-sub);
  word-break: break-all;
}
/* 두 번 두드려 연 줄에 붙는 버튼들.
   줄 아래가 아니라 줄 '안' 오른쪽에 겹쳐 앉는다 -- 아래에 한 줄을 더 만들면
   그만큼 목록이 밀리고, 손도 한 칸 더 내려가야 한다.

   위아래를 줄 상자에 그대로 맞춘다. 줄 사이 간격은 이 상자 안에 없다 --
   줄 버튼의 아래 여백은 감싼 항목 밖으로 빠져나가 항목 자신의 여백이 되므로
   (여백 합침), 항목의 상자는 이미 버튼 상자와 같은 자리에서 끝난다.
   그만큼 또 빼면 상자가 줄보다 짧아지고, 가운데 맞춘 버튼이 그 절반만큼
   위로 올라가 줄 위쪽을 넘는다. */
.proj-acts {
  position: absolute;
  top: 0;
  right: 0.5rem;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: 0.375rem;
}
/* 버튼이 앉는 만큼 글이 들어올 자리를 비운다.
   안 비우면 이름과 경로가 버튼 밑으로 들어가 읽히지 않는다. */
.proj-item.with-acts {
  padding-right: 6.5rem;
}
/* 임시 프로젝트는 '저장' 이 하나 더 붙는다 */
.proj-item.with-acts-wide {
  padding-right: 9.5rem;
}
/* 폴더가 없거나 경로가 바뀐 줄. 고를 수는 있다 (고쳐야 할 것이 이것들이다).
   누를 수 없게 막지 않고 흐리게만 둔다 -- 막으면 이름도 못 바꾸고 못 지운다.

   흐림은 글자에만 건다. 줄 자체(button)에 걸면 테두리까지 같이 흐려져,
   골랐을 때 표시가 다른 줄을 골랐을 때와 달라 보인다. 고르는 표시는
   어느 줄에서나 같아야 한다. */
.proj-item.missing .proj-name,
.proj-item.missing .proj-cwd {
  opacity: 0.45;
}
/* 이번 판에만 있는 프로젝트. 이름 옆에 작게 붙는다 */
.proj-tag {
  margin-left: 0.375rem;
  padding: 0.0625rem 0.3125rem;
  border-radius: 0.25rem;
  background: var(--bg-2);
  color: var(--fg-faint);
  font-size: 0.65rem;
  font-weight: 400;
  vertical-align: middle;
}
/* 세션 줄. 편집기 탭과 같은 것을 보여 준다 -- 상태 점 / 이름 / 예약 개수.
   목록 한 벌(.proj-list 규칙들)을 함께 쓰므로 여기엔 이 줄만의 것만 적는다. */
.sess-top {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  min-width: 0;
}
.sess-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
}
/* 그 세션의 상태 점. 편집기 탭의 점과 같은 규칙이다 -- 쉬는 중과 도는 중이
   같은 초록이고 가르는 것은 깜빡임이다. 색으로 가르면 옆 점과 견주어야 보이고
   움직임으로 가르면 그 점 하나만 봐도 보인다. */
.sess-dot {
  width: 0.375rem;
  height: 0.375rem;
  border-radius: 50%;
  background: var(--fg-faint);
  flex-shrink: 0;
}
.sess-dot.on {
  background: var(--ok);
}
.sess-dot.busy {
  background: var(--ok);
  animation: sess-blink 1.1s steps(2, start) infinite;
}
.sess-dot.off {
  background: var(--error);
}
@keyframes sess-blink {
  50% {
    opacity: 0.25;
  }
}
/* 예약이 쌓인 세션. 편집기 탭이 적는 것과 같은 꼴이다 */
.sess-q {
  flex-shrink: 0;
  font-size: 0.68rem;
  color: var(--accent);
}
/* 프로젝트 이름과 턴 수와 마지막 활동. 가운뎃점으로 잇는다.
   경로와 달리 끊어 읽을 것이 아니라 한 줄로 훑는 값이라, 넘치면 자른다
   (.proj-cwd 는 break-all 로 접는다 -- 거기서는 경로 전체가 뜻이다). */
.sess-meta {
  display: block;
  color: var(--fg-faint);
  font-size: var(--row-sub);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 세션 줄에 붙는 작은 표 한 벌의 상자 (.proj-tag 와 같은 크기). 아래 갈래
   들이 이 상자를 그대로 쓰고 칠만 덧쓴다.
   여기 적은 칠은 어두운 칸에 흐린 글자다. 지금 이것을 그대로 입는 갈래는
   없고 아래 셋이 다 덧쓴다. 그래도 남겨 두는 것은 색을 못 받은 표가 바탕에
   묻히지 않게 하려는 것이다. */
.sess-tag {
  margin-left: 0.375rem;
  padding: 0.0625rem 0.3125rem;
  border-radius: 0.25rem;
  background: var(--bg-2);
  color: var(--fg-faint);
  font-size: 0.65rem;
  font-weight: 400;
  vertical-align: middle;
}

/* 편집기가 들고 있다가 놓은 자리. 위 상자를 그대로 쓰고 칠만 덧쓴다 --
   상자를 여기서 새로 지으면 뒤에 그 줄에 표가 하나 더 설 때 둘이 다른
   갈래의 표시로 읽힌다.
   오류색을 안 쓴다. 고장이 아니라 한쪽이 손을 놓았다는 사실이고, 그 대화는
   여기서 그대로 돈다. 다만 위 표보다는 눈에 걸려야 한다 -- 이 표를 못 보고
   지나가면 PC 에서 지운 것이 안 지워진 것으로 읽힌다. */
/* 시트 머리의 호스트 장 표시도 같은 칠을 읽는다 (사람이 골랐다). 값을 다시
   적으면 두 벌이 되어 한쪽만 고쳐진다.
   뜻이 다른 둘이 한 칠을 쓰는 자리다. 색으로는 안 갈리지만 서는 자리가
   갈린다 -- 저것은 목록 줄 안에 서고 이것은 시트 머리에 서서, 한 화면에
   나란히 설 일이 없다. 뒤에 이 칠을 쓰는 셋째가 목록 줄에 서게 되면
   그때는 갈라야 한다. */
.sess-tag.is-let-go,
.sess-tag.is-host {
  background: color-mix(in srgb, var(--warn) 14%, transparent);
  color: var(--warn);
}
/* 세션 줄의 글 두 줄을 묶은 칸. 오른쪽 뱃지와 나란히 선다.
   남는 자리를 다 먹고 안에서 눌린다 -- 이름이 긴 줄에서 깎이는 것은 이름
   이어야 하고, 그러려면 이 칸이 제 속만큼 버티지 않아야 한다
   (min-width:0). */
/* 목록 줄 한 벌이 block 으로 못박아 두어(.sess-list button) 이름을 둘 겹쳐
   무게를 올린다. 한 겹으로 적으면 그 규칙이 이겨 이 줄만 안 바뀐다. */
.sess-list .sess-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.sess-text {
  flex: 1;
  min-width: 0;
}
/* 그 줄이 선 프리셋. 위 상자를 그대로 쓰고 글자 색만 바꾼다 -- 칸까지
   칠하는 갈래는 무슨 일이 났는지를 말하느라 눈에 걸려야 하는데, 이 표는
   그 줄이 어디에 앉았는지만 말하므로 그 무게로 서면 목록이 온통 그 색이
   된다.
   서는 자리도 다르다 -- 놓음 표는 이름 바로 뒤에 붙어 그 줄의 상태를
   말하고, 이것은 줄 끝에서 그 줄이 어느 묶음의 것인지를 말한다. 목록이
   프리셋 차례로 서므로 같은 이름이 세로로 이어져, 끝에 세우면 그 줄들이
   한 단으로 읽힌다.
   세로는 윗줄이 아니라 줄 높이의 가운데다. 줄이 두 단이라 윗줄에 맞추면
   이 뱃지만 위로 떠 보인다.
   안 눌린다 (flex-shrink:0). 눌리게 두면 이름이 긴 줄에서 이 글자부터
   깎여, 정작 묶음을 가리키는 값이 먼저 사라진다. 눌리는 것은 이름 칸이다.
   거르고 있든 아니든 늘 선다. 거른 목록에서는 같은 글자가 줄 수만큼
   되풀이되지만, 판마다 줄이 달라 보이지 않는 것이 값이 더 크다. */
.sess-tag.is-preset {
  margin-left: 0;
  flex-shrink: 0;
  color: var(--preset);
}
/* 줄 안에 겹쳐 앉은 수정 / 삭제만큼 글이 들어올 자리를 비운다.
   프로젝트 줄과 붙는 버튼 수가 같으므로 값도 같게 둔다 -- 한쪽만 달리 두면
   같은 시트에서 위아래 줄의 글이 다른 선에서 끊긴다. */
.sess-item.with-acts {
  padding-right: 6.5rem;
}
/* 시트 아래에 한 줄로 서는 버튼들의 줄 상자.
   글자 크기가 서로 다르다 (＋ PC 폴더에서 추가는 작다). 줄 높이가 배수면
   그만큼 상자도 작아져 두 버튼의 키가 갈리므로, 여기서만 절대값으로 못박아
   글자 크기와 무관하게 같은 키로 세운다. */
/* 시트 맨 아래에 한 줄로 서는 버튼. 세 시트가 같은 옷을 입는다 --
   세션 선택 / 세션 추가 / 프로젝트 추가가 같은 자리에 같은 키로 선다.
   한 규칙에 묶어 두는 까닭은 값을 따로 적으면 시트를 오갈 때 버튼 키가
   달라져 아래가 한 번씩 튀기 때문이다.
   목록의 줄과 달리 색을 준다 -- 줄들 사이에 섞이지 않고 무언가를 세우는
   자리로 보여야 한다. 반대쪽 시트로 건너가는 일은 머리의 아이콘과 미는
   손짓이 맡으므로 여기 안 선다. */
.sheet-go,
.browse-add {
  width: 100%;
  padding: var(--row-pad) 0.75rem;
  /* 줄 상자를 절대값으로 못박는다. 줄 높이가 배수면 글자 크기를 따라
     상자도 줄어들어, 어느 한 버튼의 글자를 건드리는 순간 셋의 키가 갈린다. */
  line-height: calc(var(--line-h) * 1rem);
  border-radius: 0.5rem;
  background: var(--accent);
  color: var(--accent-fg);
  font-weight: 600;
}
.sheet-go:active,
.browse-add:active {
  filter: brightness(0.92);
}
/* 세울 세션이 설 프리셋을 고르는 줄. 세우는 버튼 바로 위에 선다.
   드롭다운을 안 쓰고 칩을 늘어놓는다 : 이 화면에는 드롭다운 부품이 없고,
   고를 것이 여덟까지라 한 줄에 다 서면 무엇이 있는지가 여는 걸음 없이
   보인다. 넘치면 옆으로 굴러간다 -- 줄을 접으면 시트 아래가 프리셋 수만큼
   자라 그만큼 목록이 짧아진다. */
.preset-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
  min-width: 0;
}
.preset-pick {
  display: flex;
  gap: 0.375rem;
  min-width: 0;
}
/* 칩 하나가 바 하나다. 고른 것만 칠하고 상자는 어느 상태에서나 같다 --
   두께나 여백으로 가르면 고르는 순간 줄 전체가 옆으로 밀린다. */
.preset-chip {
  flex-shrink: 0;
  width: auto;
  min-height: 0;
  margin-bottom: 0;
  padding: 0.25rem 0.625rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: transparent;
  color: var(--fg-dim);
  font-size: 0.8rem;
  font-weight: 400;
  white-space: nowrap;
}
.preset-chip.on {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent);
  font-weight: 600;
}
.preset-chip:active {
  opacity: 0.8;
}
/* 칩을 길게 눌러 끌어 차례를 바꿀 때. 값은 줄 목록 쪽과 같게 둔다 -- 쓰는
   사람에게는 한 손짓이라, 밀려나는 결이 다르면 같은 앱에서 두 결을 만난다.
   비켜 주는 칩만 부드럽게 밀리고 잡힌 칩은 뺀다. 잡힌 것은 손가락을 한 박자도
   늦지 않게 그대로 따라와야 한다. */
.preset-pick .preset-chip:not([data-dragging="1"]) {
  transition: transform 0.2s cubic-bezier(0.2, 0, 0.2, 1);
}
/* 놓고 나서 새 차례를 받는 사이. 전환을 꺼 두어 한 칸 튀었다가 제자리로
   미끄러지는 두 번 움직임을 없앤다 (app.js 의 rowDragEnd 가 켜고 끈다).
   :not() 을 함께 적는 까닭은 위 규칙과 무게를 맞추기 위해서다 -- 무게가 같으면
   나중에 쓰인 쪽이 이기므로, 이 규칙이 아래에 서야 전환이 실제로 꺼진다
   (줄 목록 쪽과 같은 까닭). */
.preset-pick[data-settling="1"] .preset-chip,
.preset-pick[data-settling="1"] .preset-chip:not([data-dragging="1"]) {
  transition: none;
}
/* 끌고 있는 칩. 다른 칩 위로 떠 보이게 한다 (줄 목록과 같은 값) */
.preset-chip[data-dragging="1"] {
  position: relative;
  z-index: 2;
  cursor: grabbing;
  opacity: 0.92;
  box-shadow: 0 0.5rem 1.25rem rgba(0, 0, 0, 0.35);
}
/* 줄 안에 겹쳐 앉는 버튼들 (수정 / 삭제 / 저장, 이름 고치기 상자의 것까지).
   .proj-list button 은 목록 항목 기준(한 줄 가득)이라 여기서 되돌린다 --
   안 되돌리면 작은 버튼들이 저마다 한 줄을 차지한다.

   되돌릴 것에 min-height 가 들어간다. 목록 줄은 두 줄짜리 키를 최소로 깔아
   두는데(--row-h), 그것이 안 지워지면 이 작은 버튼들도 줄 한 칸만 한 키로
   서서 겹쳐 앉을 자리보다 커진다. 크면 위아래로 비어져 나와 줄 테두리를
   넘는다. 상자를 건드리는 값은 하나라도 남기면 안 된다. */
.proj-list .card-act,
.sess-list .card-act {
  display: inline-flex;
  align-items: center;
  width: auto;
  min-height: 0;
  margin-bottom: 0;
  padding: 0.3125rem 0.625rem;
}
/* 확인은 이 버튼이 아니라 모달이 받는다 (.modal). 그래서 눌러 둔 상태가
   따로 없고, 글자도 '삭제' 하나로 고정이다. */
.card-act.del {
  color: var(--error);
}
/* 이름 고치기. 줄 안에서 바로 고친다 */
.proj-edit {
  padding: 0.5rem;
  border-radius: 0.5rem;
  background: var(--bg-3);
  margin-bottom: 0.375rem;
}
.proj-edit-text {
  width: 100%;
  padding: 0.5rem 0.625rem;
  border: 0;
  border-radius: 0.375rem;
  background: var(--bg-2);
  color: var(--fg);
  font: inherit;
  font-size: 0.875rem;
}
.proj-edit-acts {
  display: flex;
  justify-content: flex-end;
  gap: 0.375rem;
  margin-top: 0.5rem;
}

/* ---------------------------------------------------------- 확인 모달 */

/* 시트보다 위에 뜬다. 시트를 열어 둔 채로 묻는 자리라 아래에 깔리면 안 된다.
   가운데에 두는 것은 시트와 구분하기 위해서다 -- 아래에서 올라오면 또 하나의
   시트로 읽혀 밀어 닫으려 든다. */
.modal {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--pad);
  background: rgba(0, 0, 0, 0.6);
}
.modal-box {
  width: 100%;
  max-width: 22rem;
  padding: 1rem;
  border-radius: var(--radius);
  background: var(--bg-2);
  box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.45);
}
.modal-title {
  display: block;
  font-size: 1rem;
}
.modal-body {
  margin: 0.5rem 0 0;
  color: var(--fg-dim);
  font-size: 0.8125rem;
  line-height: 1.6;
  /* 이름과 경로를 줄로 나눠 보여 준다. 한 줄로 이으면 무엇을 지우는지가
     긴 경로에 묻힌다 */
  white-space: pre-line;
  word-break: break-all;
}
.modal-acts {
  display: flex;
  gap: 0.5rem;
  margin-top: 1rem;
}
/* 취소와 확인을 같은 폭으로 둔다. 되돌릴 수 없는 쪽만 크면 그쪽으로 손이 간다 */
.modal-btn {
  flex: 1 1 0;
  padding: 0.6875rem 0.75rem;
  border-radius: 0.5rem;
  background: var(--bg-3);
  color: var(--fg);
  font-size: 0.875rem;
}
.modal-btn:active {
  filter: brightness(1.15);
}
.modal-btn.danger {
  background: var(--error);
  color: var(--accent-fg);
  font-weight: 600;
}
.modal-btn.danger:active {
  filter: brightness(0.92);
}
/* 허용 쪽. 되돌릴 수 없는 쪽(danger)과 색을 갈라 둔다 -- 여기서 무거운 쪽은
   삭제가 아니라 '실행해도 좋다' 이고, 그 둘이 같은 붉은색이면 손이 헷갈린다. */
.modal-btn.yes {
  background: var(--accent);
  color: var(--accent-fg);
  font-weight: 600;
}
.modal-btn.yes:active {
  filter: brightness(0.92);
}

/* 권한 확인 창의 속.
   어느 대화의 물음인가 → 무슨 도구인가 → 무엇에 대고 하려는가 → 왜 묻나
   차례로 선다. 맨 위가 대화 이름인 까닭은 그것이 이 창에서 유일하게
   '지금 보고 있는 화면과 다를 수 있는' 값이라서다. */
.perm-where {
  margin: 0.5rem 0 0;
  color: var(--fg-dim);
  font-size: 0.8125rem;
  word-break: break-all;
}
.perm-tool {
  margin: 0.25rem 0 0;
  font-size: 0.9375rem;
  font-weight: 600;
  word-break: break-all;
}
/* 무엇에 대고 하려는가. 명령과 경로가 들어와 길다 -- 접지 않고 다 보이되
   키에 상한을 두고 그 안에서 굴린다. 잘라 버리면 정작 판단에 필요한 끝부분
   (어느 폴더인가)이 안 보인다. */
.perm-input {
  margin: 0.5rem 0 0;
  max-height: 9rem;
  overflow: auto;
  padding: 0.5rem;
  border-radius: 0.375rem;
  background: var(--bg-3);
  color: var(--fg);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.75rem;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-all;
}
.perm-why {
  margin: 0.5rem 0 0;
  color: var(--fg-dim);
  font-size: 0.75rem;
  line-height: 1.5;
  word-break: break-all;
}
/* 뒤에 더 기다리는 것이 있다는 줄. 답하고 나면 창이 곧바로 다음 것으로
   갈아 끼워지므로, 그 사실을 미리 알려야 갑자기 또 뜬 것으로 안 읽힌다. */
.perm-more {
  margin: 0.5rem 0 0;
  color: var(--warn);
  font-size: 0.75rem;
}
/* 셋이 한 줄에 서면 가운데 것이 글자가 길어 좁아진다. 세로로 세운다 --
   허용과 거부가 같은 폭이어야 한쪽으로 손이 쏠리지 않는다. */
.perm-acts {
  flex-direction: column;
}

/* ---------------------------------------------------------- 파일 */

/* 뿌리 목록에서만 붙는 전체 경로. 이름만으로는 구분이 안 된다 */
.browse-sub {
  display: block;
  color: var(--fg-faint);
  font-size: 0.7rem;
  word-break: break-all;
}
/* 목록 아래 고정으로 붙는 고르기 줄 */
/* 목록만 굴러가는 시트 (파일 / 프로젝트 / 세션 / 진행).
   시트 전체가 굴러가면 머리와 아래 띠가 목록을 따라 밀려 화면 밖으로 나간다.
   머리와 띠는 자리에 두고 가운데 목록이 남는 높이를 다 쓰게 세운다.
   목록에 min-height 0 을 주는 것은 flex 항목이 기본으로 제 내용보다 안
   줄어들기 때문이다 -- 안 주면 목록이 안 줄고 시트가 통째로 넘친다. */
.sheet-cols .sheet-inner {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.sheet-cols .sheet-head,
.sheet-cols .sheet-foot,
.sheet-cols .sheet-empty,
.sheet-cols .files-path {
  flex: 0 0 auto;
}
.sheet-cols .proj-list,
.sheet-cols .sess-list,
.sheet-cols .file-list,
.sheet-cols .plan-list {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}

/* ── 진행 상황 ──────────────────────────────────────────────────────────
   대화가 답 끝에 세운 계획 표를 그대로 옮겨 세우는 자리.
   목록의 다른 시트들과 달리 누르는 줄이 하나도 없다 -- 읽기만 하는 겹이라
   줄을 버튼으로 세우지 않고 글 상자로 둔다. 굴러가는 자리는 여기 하나이고
   옆여백을 넓혔다 되돌리는 규칙은 다른 목록과 같다 (막대가 글자를 가린다). */
.plan-list {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  margin-right: calc(var(--pad) * -1);
  padding-right: var(--pad);
}
/* 일 하나가 한 덩이다. 일 이름이 같으면 같은 덩이에 쌓이고 다르면 새 덩이가
   서므로, 덩이끼리는 틈으로 가른다 (선을 그으면 걸음 사이 선과 섞인다). */
.plan-work {
  margin-bottom: 0.75rem;
  padding: var(--row-pad) 0.75rem;
  border-radius: 0.5rem;
  background: var(--bg-3);
}
.plan-work:last-child {
  margin-bottom: 0;
}
/* 일의 머리. 줄 하나가 통째로 접고 펴는 손잡이다. 접혀도 이 줄은 남아
   이름과 몇까지 왔는지를 말한다. 버튼의 옷은 벗기고 덩이의 글 줄로 둔다. */
.plan-head {
  display: flex;
  align-items: flex-start;
  gap: 0.375rem;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
/* 접힘 / 펼침 세모. 도구 카드 머리의 세모와 같은 손이고, 상자 높이를 이름
   한 줄과 같게 두어 그 안에서 가운데에 앉힌다 */
.plan-fold {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  height: calc(var(--line-h) * 0.875rem);
  color: var(--fg-faint);
}
.plan-fold svg {
  width: var(--caret-box);
  height: var(--caret-box);
  display: block;
}
.plan-name {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--fg);
  font-size: 0.875rem;
  font-weight: 600;
  line-height: var(--line-h);
}
/* 몇까지 왔는가. 이름이 길어 접혀도 이 칸은 안 눌리고 첫 줄에 남는다 */
.plan-count {
  flex-shrink: 0;
  color: var(--fg-dim);
  font-size: 0.75rem;
  line-height: calc(var(--line-h) * 0.875rem);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* 진행 막대. 펼친 일에만 선다 -- 접힌 일은 머리의 수가 그 몫을 든다.
   다 된 걸음의 칠과 같은 색을 읽는다. */
.plan-bar {
  height: 0.1875rem;
  margin-top: 0.375rem;
  border-radius: 0.125rem;
  background: var(--line);
  overflow: hidden;
}
.plan-bar-fill {
  display: block;
  height: 100%;
  background: var(--ok);
}
/* 무엇을 하려는 일인가. 막대 바로 아래 붙여 한 벌로 읽히게 둔다 */
.plan-intent {
  margin-top: 0.375rem;
  color: var(--fg-dim);
  font-size: 0.75rem;
  line-height: 1.5;
}
.plan-steps {
  margin-top: 0.5rem;
}
/* 걸음 한 줄. 표시와 글이 나란히 서고 글은 남는 폭을 다 쓴다.
   겹은 왼쪽 안여백으로만 준다 -- 상자를 통째로 밀면 접힌 둘째 줄이 표시
   아래로 들어가 겹이 몇인지가 안 보인다. */
.plan-step {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  padding: 0.25rem 0;
}
.plan-step-body {
  flex: 1 1 auto;
  min-width: 0;
}
.plan-step-name {
  color: var(--fg);
  font-size: 0.8125rem;
  line-height: 1.5;
}
/* 그 걸음의 세부. 이름만으로는 무엇을 하는 걸음인지 모자랄 때 읽는 자리다 */
.plan-step-note {
  margin-top: 0.0625rem;
  color: var(--fg-faint);
  font-size: 0.7rem;
  line-height: 1.45;
}
/* 상태 표시. 세 갈래를 색과 채움으로 가른다 -- 글자로 적으면 줄마다
   '미완 / 하는 중 / 완료' 가 앞에 서서 정작 걸음 이름이 뒤로 밀린다. */
.plan-mark {
  flex: 0 0 auto;
  width: 0.5rem;
  height: 0.5rem;
  margin-top: 0.375rem;
  border-radius: 50%;
  border: 1.5px solid var(--fg-faint);
}
.plan-mark[data-state="doing"] {
  border-color: var(--accent);
  background: var(--accent);
  animation: plan-pulse 1.4s ease-in-out infinite;
}
.plan-mark[data-state="done"] {
  border-color: var(--ok);
  background: var(--ok);
}
/* 도는 걸음만 숨을 쉰다. 작업 중 표시와 같은 결로 둔다 -- 돌아가는 것을
   회전이 아니라 밝기로 알리는 것이 이 화면의 규칙이다 */
@keyframes plan-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}
/* 다 끝난 걸음은 글자를 죽인다. 목록이 길어지면 지금 볼 것은 도는 걸음과
   아직 안 한 걸음이라, 끝난 것이 같은 무게로 서면 그 둘이 묻힌다 */
.plan-step[data-state="done"] .plan-step-name {
  color: var(--fg-dim);
}

/* 시트 맨 아래 띠. 위 목록은 굴러가고 이 띠는 늘 보인다.
   목록과 사이에 선을 하나 그어 어디까지가 굴러가는 자리인지를 알린다 --
   선이 없으면 목록이 그냥 끊긴 것으로 보인다.
   세 시트가 같은 띠를 쓴다. */
.sheet-foot {
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--line);
}
/* 프로젝트 추가의 옷은 위 .sheet-go 와 한 규칙에 묶여 있다. 여기 같은
   이름으로 한 벌을 더 두면 뒤엣것이 이겨 그 묶음이 이 버튼에서만 풀린다 --
   실제로 글자가 12.19px 로 서 있었다 (다른 둘은 15px, 실측).
   두 벌을 없애고 한 규칙으로 되돌렸다. 여기 남기는 것은 그 규칙에 없는
   갈래 하나뿐이다. 재는 자리는 '시트 아래 버튼 셋이 한 벌인지 실측' 이다. */

/* 이미 목록에 있는 폴더. 더 넣을 것이 없다는 것을 색으로 말한다 --
   감추면 버튼이 있던 자리가 비어 아래 띠의 높이가 흔들린다. */
.browse-add[data-mode="have"] {
  background: var(--bg-3);
  color: var(--fg-dim);
  font-weight: 400;
}
/* 폴더를 옮겨 가며 훑는 동안. 목록을 비우지 않고 살짝 흐리게만 한다 --
   비우면 시트가 확 줄었다 다시 늘어 창이 닫혔다 뜬 것처럼 보인다 */
.sheet[data-loading="1"] .file-list {
  opacity: 0.5;
}
/* 지난 대화 한 건 */
.conv-title {
  display: block;
  font-weight: 600;
  /* 제목은 한 줄로 자른다. 목록에서 훑을 수 있어야 한다 */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.conv-meta {
  display: block;
  color: var(--fg-dim);
  font-size: 0.7rem;
  margin-top: 0.125rem;
}
.conv-last {
  display: block;
  color: var(--fg-faint);
  font-size: 0.7rem;
  margin-top: 0.125rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 대화 끝부분 미리보기. 제목(첫 발화)만으로는 어떤 대화인지 구분이 안 된다 */
.conv-tail {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  margin-top: 0.3125rem;
}
/* 한 발화 = 한 줄. 여러 줄을 허용하면 목록이 대화창처럼 되어 훑기가 어렵다 */
.conv-tail-line {
  display: block;
  color: var(--fg-faint);
  font-size: 0.7rem;
  line-height: 1.4;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 모델이 한 말을 조금 더 밝게. 누가 말했는지를 색으로도 구분한다 */
.conv-tail-line.ai {
  color: var(--fg-dim);
}
.conv-tail-line b {
  margin-right: 0.375rem;
  color: var(--fg-faint);
  font-weight: 600;
}
.sheet-empty {
  margin: 0.5rem 0;
  padding: 0.75rem;
  background: var(--bg-3);
  border-radius: 0.5rem;
  color: var(--fg-dim);
  font-size: 0.8125rem;
}

/* 목록 자체에 대한 알림. 줄 파일 한 장을 못 읽어 이 목록이 짧을 수 있다는
   말이라, 어느 한 줄이 아니라 목록 맨 위에 한 번 선다.
   빈 안내(.sheet-empty)와 같은 상자를 쓰고 칠만 다르다 -- 같은 자리에 서고
   둘 중 하나만 뜨므로, 상자가 달라지면 그 자체가 무슨 일이 난 것으로 읽힌다.
   오류색을 안 쓴다. 고장이 아니라 지금은 다 못 보여 준다는 사실이고, 다음에
   읽히면 저절로 사라진다. */
.sheet-note {
  margin: 0.5rem 0;
  padding: 0.75rem;
  background: color-mix(in srgb, var(--warn) 12%, transparent);
  border-left: 2px solid color-mix(in srgb, var(--warn) 55%, transparent);
  border-radius: 0 0.5rem 0.5rem 0;
  color: var(--warn);
  font-size: 0.8125rem;
  line-height: 1.45;
}

.menu-list .danger {
  color: var(--error);
}
/* 되돌릴 수 없는 버튼은 안내문에 붙여 두지 않는다.
   읽고 나서 누르도록 한 칸 떨어뜨리고 선으로 끊는다 (오탭 방지).
   상태 강조가 아니라 고정 구분선이므로 두께가 변하지 않는다 (Design.md 공통 원칙 절) */
.menu-list.restart-act {
  margin-top: 1.25rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--bg-3);
}
/* 목록 버튼은 다음 항목과 벌어지라고 아래 여백을 갖는데, 이건 마지막 항목이라
   그 여백이 시트 바닥에 그대로 남는다 */
.menu-list.restart-act button {
  margin-bottom: 0;
}
/* 서버 재시작 버튼의 상자와 글자 자리를 시트 아래 버튼(.sheet-go)에 맞춘다.
   둘 다 목록 사이에 섞이는 줄이 아니라 그 시트에서 무언가를 세우는 자리라,
   키와 글자 자리가 갈리면 같은 일을 하는 버튼이 시트마다 다른 것으로 보인다.

   색은 안 건드린다 -- 되돌릴 수 없는 동작이라는 표시가 이 버튼에서는 색에
   있고, .sheet-go 의 강조색을 입히면 그 표시가 사라진다.

   메뉴 줄에서 물려받는 값 둘을 되돌린다.
   min-height 는 이름 한 줄에 값 한 줄이 딸린 목록 줄 기준(--row-h)이라,
   한 줄짜리인 이 버튼에 걸리면 그만큼 키가 커진다. 대신 .sheet-go 와 같은
   절대 줄 높이를 준다 -- 글자 크기를 따라 상자가 움직이면 두 시트의 버튼 키가
   다시 갈린다.
   왼쪽 정렬은 이름이 죽 늘어서는 목록의 규칙이라 세우는 자리에는 안 맞는다. */
#btnDoRestart {
  min-height: 0;
  line-height: calc(var(--line-h) * 1rem);
  text-align: center;
}
/* 바닥 여백을 안전 영역에서 덜어낸다.
   시트가 아래에서 올라오는 형태라, 여백이 줄어든 만큼 내용이 그대로 내려간다.
   전부 덜어내지는 않는다 -- 홈 인디케이터 위에 버튼이 걸리면 누르기 어렵다.
   덜어내는 방식은 하단 현황 줄(--sb-gap)과 같다.
   시트마다 켜는 방식이다. 전부에 걸면 목록이 긴 시트(지난 대화 / 파일)까지
   바닥이 얕아져 마지막 항목이 인디케이터에 가까워진다 */
#restart .sheet-inner,
#menu .sheet-inner {
  padding-bottom: max(calc(env(safe-area-inset-bottom, 0px) - 1rem), 0.5rem);
}
.menu-info {
  margin: 0.75rem 0 0;
  color: var(--fg-faint);
  font-size: 0.7rem;
  word-break: break-all;
}

/* ---------------------------------------------------------- 지시 예약 */

/* 입력칸 안 오른쪽에 겹친다. 아이콘 줄의 버튼과 같은 모양을 쓰되, 밀린 개수를
   배지로 얹는다 -- 시트를 열지 않아도 몇 개가 밀려 있는지 보여야 한다.
   아래쪽에 붙이는 이유: 입력칸이 여러 줄로 늘어나도 아이콘이 따라 내려와
   보내기 버튼과 같은 줄에 남는다 */
.queuebtn {
  position: absolute;
  right: 0.3125rem;
  /* 세로 가운데. 입력칸이 여러 줄로 늘어나도 가운데에 선다 (아래에 깔리지 않게).
     transform: translateY(-50%) 대신 위아래 0 + margin auto 로 맞춘다 --
     transform 은 소수 픽셀에 걸리면 아이콘이 흐려지고, 여기 높이는 줄 수에
     따라 소수로 떨어진다 (한 줄 1.4em = 22.4px). */
  top: 0;
  bottom: 0;
  margin-top: auto;
  margin-bottom: auto;
  /* 입력칸 위에 얹히는 조각이라 너무 크면 글 쓰는 자리를 눌러 보인다.
     아이콘(1.125rem)은 그대로 두고 둘레 여백만 줄였다. 이 값 아래로는
     내리지 않는다 -- 화면 맨 아래 키보드 바로 위라 손가락이 빗나간다. */
  width: 1.625rem;
  height: 1.625rem;
  flex-shrink: 0;
  /* 입력칸 배경 위에 떠 있으므로 제 배경을 갖는다. 글이 뒤로 지나가도
     아이콘이 읽힌다 */
  background: var(--bg);
  border-radius: 0.375rem;
}
.queue-badge {
  position: absolute;
  top: -0.25rem;
  right: -0.25rem;
  min-width: 1rem;
  padding: 0 0.1875rem;
  border-radius: 0.5rem;
  background: var(--accent);
  color: var(--bg);
  font-size: 0.625rem;
  font-weight: 700;
  line-height: 1rem;
  text-align: center;
}
.queue-auto {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 0.6875rem 0.75rem;
  border-radius: 0.5rem;
  background: var(--bg-3);
  margin-bottom: 0.625rem;
}
.queue-auto-name {
  color: var(--fg);
}
.queue-auto-state {
  color: var(--fg-faint);
  font-size: 0.75rem;
}
/* 켜져 있을 때만 색으로 알린다. 꺼짐이 기본값처럼 보이면 안 된다 */
.queue-auto[data-on="1"] .queue-auto-state {
  color: var(--accent);
}
.queue-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 45dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
/* 한 줄이 칸 하나다. 배경과 모서리는 이 줄이 갖고, 본문과 지우기는 그 안에서
   자리만 나눈다 -- 칸 두 개로 갈라 보이지 않게 gap 을 두지 않는다.
   overflow:hidden 은 안쪽 버튼의 눌림 배경이 둥근 모서리를 네모로 덮지 않게 */
.queue-row {
  display: flex;
  align-items: stretch;
  /* 지우기(×)가 이 안에 겹쳐 앉는 기준이 된다 */
  position: relative;
  margin-bottom: 0.375rem;
  border-radius: 0.5rem;
  background: var(--bg-3);
  overflow: hidden;
}
/* 길게 눌러 잡는 방식이라 touch-action 을 none 으로 두지 않는다.
   none 이면 목록을 넘길 수 없다. 넘기기는 브라우저에 맡기고, 잡힌 뒤에만
   app.js 가 touchmove 를 막는다 (가만히 누르는 동안은 넘기기가 시작되지
   않으므로 그 사이에 잡아채면 둘이 부딪히지 않는다).
   길게 누를 때 글자가 선택되거나 확대 메뉴가 뜨지 않게 막는다. */
.queue-row {
  touch-action: pan-y;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  /* 탭할 때 기기 기본 강조가 덧씌워지지 않게 한다. 우리 눌림 표시만 쓴다 */
  -webkit-tap-highlight-color: transparent;
}
/* 놓고 나서 새 순서를 받는 사이. 전환을 꺼 두어 '제자리로 돌아갔다가 다시
   튀는' 두 번 움직임을 없앤다 (app.js 의 queueDragEnd 가 켜고 끈다) */
.queue-list[data-settling="1"] .queue-row {
  transition: none;
}
/* 끌고 있는 줄. 다른 줄 위로 떠 보이게 한다.
   살짝 흐리게 + 그림자를 키워 '들려 있다' 는 것이 눈에 읽히게 한다. */
.queue-row[data-dragging="1"] {
  position: relative;
  z-index: 2;
  cursor: grabbing;
  opacity: 0.92;
  box-shadow: 0 0.5rem 1.25rem rgba(0, 0, 0, 0.35);
}
/* 비켜 주는 줄이 부드럽게 밀려나게 한다.
   0.2s -- 짧으면 툭툭 끊겨 보이고 길면 손가락보다 늦게 따라온다.
   끝이 느려지는 곡선이라 비켜서기 시작은 빠르고 멈춤은 부드럽다.
   끄는 줄은 뺀다 -- 손가락을 한 박자도 늦지 않게 그대로 따라와야 한다.
   놓는 순간의 잔상(제자리로 미끄러져 돌아갔다가 새 순서로 다시 튀는 것)은
   위의 data-settling 이 전환을 꺼서 막는다. 그래서 여기에 전환을 둬도
   그 문제는 돌아오지 않는다. */
.queue-row:not([data-dragging="1"]) {
  transition: transform 0.2s cubic-bezier(0.2, 0, 0.2, 1);
}
/* 본문 버튼이 남는 폭을 다 갖고, 지우기는 제 폭만 갖는다.
   min-width:0 이 없으면 긴 글이 flex 항목을 밀어 지우기 버튼이 밖으로 나간다 */
/* 줄 전체를 차지한다. 지우기(×)는 이 위에 겹쳐 앉으므로 flex 로 자리를
   나누지 않는다 -- 나누면 눌림 배경이 본문과 × 두 조각으로 갈려 보인다. */
.queue-item {
  flex: 1;
  min-width: 0;
  text-align: left;
  padding: 0.6875rem 0.75rem;
  /* × 가 앉을 자리를 비워 둔다. 반드시 위의 padding 축약보다 뒤에 있어야
     한다 -- 앞에 두면 축약이 이 값을 되돌려 지운다 (입력칸에서 겪은 함정) */
  padding-right: 2.75rem;
  background: none;
}
.queue-text {
  display: block;
  color: var(--fg);
  font-size: 0.8125rem;
  line-height: 1.45;
  /* 두 줄까지만 보여 준다. 긴 지시가 목록을 통째로 밀어내지 않게 */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.queue-run {
  display: block;
  margin-top: 0.25rem;
  color: var(--fg-faint);
  font-size: 0.6875rem;
}
/* 지금 도는 항목. 색으로만 구분한다 -- 테두리 두께를 바꾸면 글자가 흔들린다 */
.queue-row[data-running="1"] {
  box-shadow: inset 0 0 0 1px var(--accent);
}
.queue-row[data-running="1"] .queue-run {
  color: var(--accent);
}
/* 중단해 세워 둔 항목. 자동 진행이 건너뛰므로 흐르는 것들과 구분해 보인다.
   두께가 아니라 색으로만 알린다 */
.queue-row[data-held="1"] .queue-text {
  color: var(--fg-dim);
}
.queue-row[data-held="1"] .queue-run {
  color: var(--error);
}
/* 줄 오른쪽 끝에 겹쳐 앉는다. 자리를 나눠 갖지 않고 본문 위에 얹히므로,
   본문을 누르면 그 배경이 이 밑으로 그대로 이어져 줄 전체가 한 덩어리로
   눌린다 (제 배경은 눌렸을 때만 켠다). */
.queue-del {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 2.75rem;
  background: none;
  color: var(--fg-faint);
  font-size: 1rem;
}
/* 줄 안에서 바로 고치기. 두 번 탭하면 이 모양으로 바뀐다. */
.queue-edit {
  width: 100%;
  padding: 0.5rem;
}
.queue-edit-text {
  width: 100%;
  box-sizing: border-box;
  min-height: 4.5rem;
  padding: 0.5rem;
  border: 1px solid var(--line);
  border-radius: 0.375rem;
  background: var(--bg);
  color: var(--fg);
  font-family: inherit;
  font-size: 1rem; /* 16px 미만이면 iOS 가 화면을 확대한다 */
  line-height: 1.4;
  resize: none;
}
.queue-edit-text:focus {
  box-shadow: inset 0 0 0 2px var(--accent);
}
.queue-edit-acts {
  display: flex;
  justify-content: flex-end;
  gap: 0.375rem;
  margin-top: 0.375rem;
}
/* 고치는 중인 줄은 끌리지 않아야 한다. 글자를 고르는 손짓이 끌기로 잡히면
   커서를 옮기려다 순서가 바뀐다. */
.queue-row[data-editing="1"] {
  touch-action: auto;
  -webkit-user-select: text;
  user-select: text;
}

/* 예약 전체 삭제. 목록(ul)의 형제라 width:100% 면 항목 줄과 폭이 같아진다.
   되돌릴 수 없는 동작이라 누르기 전부터 색으로 알린다. */
.queue-clear {
  width: 100%;
  margin-top: 0.375rem;
  padding: 0.6875rem 0.75rem;
  border-radius: 0.5rem;
  background: var(--bg-3);
  color: var(--error);
  font-size: 0.8125rem;
}
.queue-clear:active {
  background: var(--line);
}
/* 지울 것이 없을 때. 숨기지 않고 흐리게 둔다 -- 목록은 보이는데 버튼만
   사라지면 고장으로 읽힌다. 라벨이 이유를 말한다. */
.queue-clear:disabled {
  opacity: 0.45;
  color: var(--fg-faint);
  cursor: default;
}
/* 한 번 눌러 확인을 기다리는 중. 여기서 한 번 더 눌러야 지워진다 */
.queue-clear[data-armed="1"] {
  background: var(--error);
  color: var(--accent-fg);
  font-weight: 600;
}
