/* styles.css — PRD §7 스타일 방향
   카테고리 색상 3종 + 우선순위 accent 1종 + 중립 배경. 그 외에 색을 늘리지 않는다. */

/* 테마는 `color-scheme` 하나로 갈린다. 값마다 light-dark()를 쓰므로
   팔레트를 두 벌 적어둘 필요가 없다. 고르기 전에는 OS 설정을 따른다. */
:root {
  color-scheme: light dark;

  --bg: light-dark(#f6f6f7, #131316);
  --surface: light-dark(#ffffff, #1c1c20);
  --border: light-dark(#e4e4e7, #2e2e34);
  /* **입력칸 테두리는 따로 둔다.** 위 `--border`는 카드와 구분선을 위한 값이라
     배경과 1.2:1 남짓이다. 칸 안에 아직 아무것도 적히지 않았을 때 그 선이
     "여기에 쓸 수 있다"고 말하는 유일한 표시인데, 그 대비로는 보이지 않는다.
     글자가 아닌 그림·조작 요소에 요구되는 3:1을 네 배경(라이트 카드·바탕,
     다크 카드·바탕) 모두에서 넘긴다 — 3.38 / 3.13 / 3.17 / 3.46.
     테두리가 말하지 않아도 알아볼 수 있는 것(글자가 적힌 버튼, 카드, 구분선)은
     그대로 `--border`를 쓴다. 여기까지 올리면 화면이 온통 선으로 덮인다. */
  --field-border: light-dark(#8b8b92, #6a6a73);
  --text: light-dark(#18181b, #ececee);
  /* 이 색을 쓰는 자리가 전부 11~15px이라 큰 글자 예외에 들지 않고, 완료 처리한 할 일은
     제목 전체가 이 색이라 가장 많이 읽히는 글이 가장 흐렸다. 양쪽 다 WCAG AA(4.5:1)에
     못 미쳐 밝기만 옮겼다 — 라이트는 #8b8b93에서 내리고 다크는 #86868d에서 올렸다.

     평면 배경만 재면 다크는 통과한 것처럼 보인다(#86868d가 --bg 위 5.13, --surface 위 4.70).
     하지만 이 색은 틴트 위에도 얹힌다. 태그 알약(.tag, --text 7%)에서 3.92,
     완료한 할 일 제목의 hover(--todo-title:hover, --text 6%)에서 4.04로 떨어졌다.
     다크는 --surface 위 여유가 0.2밖에 없어 어떤 틴트를 올려도 기준 아래로 내려간다.
     틴트를 낮추는 건 답이 아니다 — 다크에서 태그 알약이 배경에 묻혀 사라진다.
     #919198이면 7% 4.53, 6% 4.66, --surface 5.43, --bg 5.92로 네 자리가 함께 통과한다. */
  --text-dim: light-dark(#6b6b73, #919198);
  /* 라이트 쪽은 원래 #2f6feb였는데, 같은 색을 14% 틴트 위에 얹는 .remaining 배지가
     3.55로 미달했다. 색조는 그대로 두고 OKLCh 밝기만 0.573에서 0.489로 내려
     틴트 위에서도 4.93 아래로 내려가지 않는다. 다크는 5.02가 최저라 그대로 둔다. */
  --focus: light-dark(#1554ce, #6c9bff);
  /* 토마토는 알아보는 게 목적이라 팔레트 밖 색을 하나 쓴다 (F-22) */
  --tomato: light-dark(#d6392b, #ff6f5e);

  --radius: 12px;
  --gap: 10px;
  /* 하위 들여쓰기는 따로 정하지 않는다. 연결선 바로 오른쪽에서 시작하도록 유도한다 —
     숫자를 둘로 나눠 적으면 좁은 화면에서 한쪽만 고쳐 선이 하위 위로 올라탄다. */
  --indent: calc(var(--handle) + var(--gap) + var(--marker) + var(--gap) + 4px);
  --marker: 16px;
  --handle: 12px;
  --row-pad: 12px;
  --row-pad-y: 10px;
  /* 글자 치수를 여기 두는 이유는 **행 높이가 여기서 나오기 때문**이다.
     body에만 적어두면 줄 높이를 고쳤을 때 연결선이 따라오지 않는다. */
  --font-size: 15px;
  --line-height: 1.5;
  /* 할 일 한 줄의 높이. 연결선이 마지막 하위의 체크박스 한가운데서 멈추는
     거리가 여기서 나온다 — 숫자를 따로 적어두면 행 패딩을 고쳤을 때 선만
     제자리에 남아 마지막 하위를 지나쳐 내려간다.
     = 제목 줄 + 제목 위아래 패딩(2px씩) + 행 위아래 패딩 + 구분선 */
  --row-h: calc(
    var(--font-size) * var(--line-height) + 4px + var(--row-pad-y) * 2 + 1px
  );

  /* 연결선은 상위 체크박스의 **왼쪽 테두리**에 맞춘다. 중심에 그으면 선이 네모 한가운데를
     지나 반으로 갈라 보이고, 그만큼 오른쪽이라 하위의 우선순위 숫자를 뚫고 지나갔다.
     테두리에 세우면 "이 아래는 저 체크박스에 딸린 것"이 선 하나로 읽힌다.
     = 행 좌패딩 + 손잡이 + 간격 + 마커 + 간격 */
  --line-x: calc(
    var(--row-pad) + var(--handle) + var(--gap) + var(--marker) + var(--gap)
  );

  /* 카테고리 탭 한 줄의 높이. **탭이 이 값에서 크기를 얻고**, 그 옆의 톱니·정렬이
     첫 줄 가운데에 서는 거리도 여기서 나온다. 탭에 패딩으로 적고 도구에 숫자를 따로
     적어두면, 탭 패딩을 고치는 순간 도구만 어긋난다. */
  --tab-h: 34px;
  /* 톱니·정렬·태그 칩의 높이. 탭보다 낮아서 첫 줄 안에서 가운데로 내려앉아야 한다. */
  --tool-h: 28px;
}

:root[data-theme="light"] {
  color-scheme: light;
}

:root[data-theme="dark"] {
  color-scheme: dark;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 32px 16px 96px;
  background: var(--bg);
  color: var(--text);
  font: var(--font-size)/var(--line-height) system-ui, -apple-system,
        "Apple SD Gothic Neo", "Malgun Gothic", sans-serif;
}

#app {
  max-width: 640px;
  margin: 0 auto;
}

/* ── 배너 — 무엇이 잘못됐고 어떻게 되는지만 말한다 ──── */

.banner {
  margin-bottom: 16px;
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--focus);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: 13px;
}

.banner[hidden] {
  display: none;
}

.banner-line {
  margin: 0;
}

.banner-line + .banner-line {
  margin-top: 6px;
}

/* ── 헤더 + 진행률 ──────────────────────────────────── */

/* 첫 줄은 앱 자체에 대한 것 — 제목, 테마, 도움말. */
.header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.header-meta {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* 둘째 줄은 일에 관한 것 — 남은 할 일과 뽀모도로. 오른쪽 끝에 붙여 위와 맞춘다.
   묶음 자체를 정렬해야 배지가 숨어도 나머지가 왼쪽으로 딸려 오지 않는다. */
.header-tools {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 10px;
}

/* 이 줄은 오른쪽 정렬이다. 왼쪽 끝에 세우려고 이 버튼이 남은 자리를 밀어낸다.
   남은 할 일 배지 쪽에 여백을 걸면 그 배지가 숨을 때(0개) 나머지가 딸려 온다. */
.login-button {
  flex: none;
  margin-right: auto;
  height: 26px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: none;
  color: var(--text-dim);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}

.login-button:hover {
  border-color: var(--text-dim);
  color: var(--text);
}

.header-title {
  margin: 0;
  white-space: nowrap;
  font-size: 20px;
  font-weight: 650;
  letter-spacing: -0.01em;
}

.remaining {
  flex: none;
  padding: 3px 10px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--focus) 14%, transparent);
  color: var(--focus);
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.remaining[hidden] {
  display: none;
}

/* ── 테마 토글 ──────────────────────────────────────── */

.switch {
  flex: none;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  line-height: 0;
}

.switch-track {
  position: relative;
  display: inline-block;
  width: 48px;
  height: 26px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  transition: background 0.15s ease, border-color 0.15s ease;
}

/* 켜진 상태는 곧 다크 모드다. 밤하늘처럼 어둡게 두고,
   달이 올라앉은 흰 손잡이가 대비로 드러나게 한다. */
.switch.is-on .switch-track {
  background: #262b3c;
  border-color: #383e54;
}

/* 손잡이는 아이콘 아래에 깔린다. 켜져 있는 쪽 아이콘이 그 위에 올라온다. */
.switch-knob {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: light-dark(#fff, #ececee);
  box-shadow: 0 1px 2px rgb(0 0 0 / 25%);
  transition: transform 0.18s ease;
}

.switch.is-on .switch-knob {
  transform: translateX(22px);
}

/* 해와 달은 늘 보인다. 지금 켜진 쪽이 손잡이 위에서 진하게 나온다. */
.switch-icon {
  position: absolute;
  top: 6px;
  width: 12px;
  height: 12px;
  transition: color 0.15s ease;
}

.switch-sun {
  left: 6px;
  color: light-dark(#c8801a, #8e6a2e);
}

/* 꺼진 쪽 아이콘은 트랙 위에 그대로 얹힌다. 흐리게 두더라도 **어느 쪽이 무엇인지는
   보여야 한다** — 달이 안 보이면 이 스위치가 다크 모드를 켜는 것인 줄 알 길이 없다.
   라이트 3.28:1 / 다크 3.69:1로, 글자가 아닌 그림에 요구되는 3:1을 넘긴다. */
.switch-moon {
  right: 6px;
  color: light-dark(#8a8e97, #69749a);
}

.switch.is-on .switch-sun {
  color: #767c92;
  opacity: 1;
}

.switch.is-on .switch-moon {
  color: light-dark(#3a4668, #2c3550);
  opacity: 1;
}

.help-button {
  flex: none;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: none;
  color: var(--text-dim);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}

.help-button:hover {
  border-color: var(--text-dim);
  color: var(--text);
}

/* ── 도움말 ─────────────────────────────────────────── */

.dialog-help {
  width: min(460px, calc(100vw - 32px));
}

.help-body {
  max-height: min(60vh, 520px);
  overflow-y: auto;
  margin-bottom: 16px;
}

/* 스크롤 영역은 키보드로 포커스된다. 테두리를 안쪽에 붙여 대화상자를 넘지 않게 한다. */
.help-body:focus-visible {
  outline-offset: -2px;
}

/* **높이 상한을 둔 통 안에서는 포커스 테두리도 안쪽에 붙인다.**
   `overflow`가 `visible`이 아니면 패딩 상자에서 잘리는데, 기본 테두리는 바깥 2~4px에
   그려진다. 첫 탭은 (0,0)에서 시작하므로 `Alt+1`로 옮겨가면 위·왼쪽 변이 통째로
   잘려 나가고, 아래 줄의 탭은 스크롤되면서 잘리는 변만 바뀐다.
   `.help-body`에서 이미 같은 문제를 이렇게 풀었다. */
.tabs :focus-visible,
.cat-list :focus-visible,
.tag-bar :focus-visible {
  outline-offset: -2px;
}

.help-section + .help-section {
  margin-top: 18px;
}

.help-heading {
  margin: 0 0 8px;
  color: var(--text-dim);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.03em;
}

.help-list {
  margin: 0;
  display: grid;
  grid-template-columns: minmax(88px, auto) 1fr;
  gap: 6px 14px;
  font-size: 13px;
}

.help-list dt {
  color: var(--text);
  font-weight: 600;
  white-space: nowrap;
}

.help-list dd {
  margin: 0;
  color: var(--text-dim);
}

.help-list code,
.help-list kbd {
  padding: 1px 5px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--text) 10%, transparent);
  color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
}

.help-list strong {
  color: var(--text);
}

/* ── 뽀모도로 ───────────────────────────────────────── */

.pomo-button {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 26px;
  padding: 0 8px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--text-dim);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}

/* 토마토는 늘 빨갛다. 상태는 글자 쪽이 알린다. */
.pomo-button-icon {
  flex: none;
  color: var(--tomato);
}

.pomo-button:hover {
  background: color-mix(in srgb, var(--text) 8%, transparent);
  color: var(--text);
}

.pomo-button[aria-expanded="true"] {
  color: var(--text);
}

/* 돌아가는 중에는 패널을 닫아둬도 글자가 알려준다 */
.pomo-button.is-running {
  background: color-mix(in srgb, var(--focus) 12%, transparent);
  color: var(--focus);
}

/* 휴식 중에는 패널과 같은 색으로 맞춘다.
   라이트 쪽은 원래 #0f8a63이었는데 12~14% 틴트 위에서 3.47~3.63으로 AA에 못 미쳤다.
   파란 --focus와 같은 요령으로 색조는 두고 OKLCh 밝기만 0.562에서 0.47로 내렸다.
   다크는 6.85가 최저라 그대로 둔다. */
.pomo-button.is-rest {
  --focus: light-dark(#006c4c, #57d3a4);
}

.pomo {
  margin-top: 12px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.pomo[hidden] {
  display: none;
}

.pomo.is-running {
  border-color: color-mix(in srgb, var(--focus) 45%, var(--border));
}

/* 휴식은 다른 상태다. 색조를 돌려 한눈에 갈라 보이게 한다. 값은 .pomo-button.is-rest와 같다. */
.pomo.is-rest {
  --focus: light-dark(#006c4c, #57d3a4);
}

.pomo-main,
.pomo-lengths {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.pomo-lengths {
  margin-top: 8px;
}

.pomo-clock {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--text-dim);
}

.pomo-time {
  color: var(--text);
  font-size: 19px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}

.pomo.is-running .pomo-time {
  color: var(--focus);
}

.pomo-phase {
  padding: 2px 8px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--focus) 14%, transparent);
  color: var(--focus);
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}

.pomo-phase[hidden] {
  display: none;
}

.pomo-preset,
.pomo-apply,
.pomo-run,
.pomo-reset {
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: none;
  color: var(--text-dim);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

.pomo-preset:hover,
.pomo-apply:hover,
.pomo-reset:hover {
  border-color: var(--text-dim);
  color: var(--text);
}

/* 지금 고른 것 하나만 켠다. 직접 입력한 길이일 때는 `설정`이 그 자리를 맡는다. */
.pomo-preset.is-active,
.pomo-apply.is-active {
  border-color: var(--focus);
  color: var(--focus);
  font-weight: 600;
}

.pomo-cycle {
  font-weight: 600;
}

.pomo-custom {
  display: flex;
  gap: 4px;
}

.pomo-input {
  width: 66px;
  padding: 5px 8px;
  border: 1px solid var(--field-border);
  border-radius: 7px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 13px;
}

.pomo-input::placeholder {
  color: var(--text-dim);
}

.pomo-controls {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
}

.pomo-run {
  border-color: var(--focus);
  color: var(--focus);
  font-weight: 600;
}

.pomo-run[hidden] {
  display: none;
}

/* 다음 구간을 잇는 버튼. 여기서 멈춰 선 채로 기다리는 중이라 눌러야 할 것이
   이것 하나뿐이다. 테두리만이 아니라 바탕까지 채워 그 사실을 말한다. */
.pomo-next {
  border-color: transparent;
  background: var(--focus);
  color: var(--surface);
}

.pomo-next:hover {
  border-color: transparent;
  background: color-mix(in srgb, var(--focus) 84%, var(--text));
  color: var(--surface);
}

/* 끝나서 기다리는 중임을 테두리로도 알린다. 돌아가는 중(.is-running)과 같은 굵기지만
   색이 다르다 — 시계가 00:00에 멈춰 있는 것만으로는 다 읽은 것인지 알기 어렵다. */
.pomo.is-waiting {
  border-color: color-mix(in srgb, var(--focus) 60%, var(--border));
}

.pomo.is-waiting .pomo-time {
  color: var(--focus);
}

.pomo-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 7px;
  background: none;
  color: var(--text-dim);
  cursor: pointer;
}

.pomo-icon:hover {
  background: color-mix(in srgb, var(--text) 8%, transparent);
  color: var(--text);
}

/* inline-flex는 hidden보다 세다. 이 줄이 없으면 지원하지 않는 브라우저에서도
   `창으로 빼기`가 그대로 보이고, 눌러도 아무 일이 없다. */
.pomo-icon[hidden] {
  display: none;
}

.pomo-icon.is-active {
  color: var(--focus);
}

/* 글자가 함께 들어가는 아이콘 버튼. 정사각형 크기를 풀고 좌우 여백만 준다.
   설정(톱니)은 그대로 아이콘만 쓰므로 .pomo-icon 쪽은 건드리지 않는다. */
.pomo-icon-wide {
  width: auto;
  gap: 4px;
  padding: 0 8px;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}

/* 펼치기 화살표는 열리면 뒤집는다 */
#pomo-expand.is-active svg {
  transform: rotate(180deg);
}

/* ── 펼친 시계 ──────────────────────────────────────── */

.pomo-dial {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}

.pomo-dial[hidden] {
  display: none;
}

.pomo-dial-face {
  position: relative;
  width: 168px;
  height: 168px;
}

/* **직계 자식만** 잡는다. 이 안에는 눈금 말고도 빼놓은 창의 버튼 아이콘이 들어
   있어서(`.pomo-dial-label > .pip-action > svg`), `>`를 빼면 그 아이콘까지 버튼을
   가장자리까지 채운다. 지금은 뒤에 오는 `.pip-action svg`가 가리고 있을 뿐이라,
   두 규칙의 순서가 바뀌면 되살아난다. */
.pomo-dial-face > svg {
  width: 100%;
  height: 100%;
}

.pomo-dial-track {
  stroke: color-mix(in srgb, var(--text) 12%, transparent);
}

/* 흐른 만큼 채워진다. 남은 시간이 아니라 지나간 시간을 그린다.
   전환 시간은 갱신 주기와 같은 1초여야 한다. 짧게 주면 그만큼만 움직이고
   나머지 시간은 멈춰 있어, 이어서 흐르지 않고 초마다 뚝뚝 끊겨 보인다. */
.pomo-dial-fill {
  stroke: var(--focus);
  transition: stroke-dashoffset 1s linear;
}

.pomo-dial-label {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  pointer-events: none;
}

.pomo-dial-time {
  font-size: 30px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}

/* 시계를 펼치면 **같은 숫자가 머리줄과 원 안에 함께 뜬다.** 머리줄만 물들고
   원 안이 그대로면 같은 값인데 다른 상태처럼 읽힌다 — 눈이 가는 쪽은 큰 숫자다.
   `.pomo-time`과 같은 자리에서 같이 물든다. 빼놓은 창은 두 모드가 `.pip-time`
   하나를 함께 쓰므로 이미 어긋나지 않는다. */
.pomo.is-running .pomo-dial-time,
.pomo.is-waiting .pomo-dial-time {
  color: var(--focus);
}

.pomo-dial-phase {
  color: var(--text-dim);
  font-size: 12px;
}

.pomo-dots {
  display: flex;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.pomo-dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border: 1px solid var(--border);
  border-radius: 50%;
  color: var(--text-dim);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.pomo-dot.is-done {
  border-color: color-mix(in srgb, var(--focus) 45%, transparent);
  color: var(--focus);
}

.pomo-dot.is-now {
  border-color: var(--focus);
  background: var(--focus);
  color: light-dark(#fff, #10131a);
  font-weight: 700;
}

/* ── 뽀모도로 설정 ──────────────────────────────────── */

.pomo-settings {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}

.pomo-settings[hidden] {
  display: none;
}

.pomo-set-head,
.pomo-set-row {
  display: grid;
  grid-template-columns: 56px 1fr 1fr;
  align-items: center;
  gap: 8px;
}

.pomo-set-head {
  margin-bottom: 6px;
  color: var(--text-dim);
  font-size: 12px;
  font-weight: 600;
}

.pomo-set-row + .pomo-set-row {
  margin-top: 6px;
}

.pomo-set-index {
  color: var(--text-dim);
  font-size: 13px;
}

.pomo-set-input {
  width: 100%;
  padding: 5px 8px;
  border: 1px solid var(--field-border);
  border-radius: 7px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 13px;
}

.pomo-set-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 10px;
}

.pomo-set-note {
  color: var(--text-dim);
  font-size: 12px;
}

/* 미니 타이머 불투명도 — 회차 표 아래에 한 줄로 둔다 */
.pomo-set-veil {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}

.pomo-set-veil-label {
  flex: none;
  color: var(--text-dim);
  font-size: 12px;
  font-weight: 600;
}

.pomo-veil {
  flex: 1 1 auto;
  min-width: 0;
  accent-color: var(--focus);
}

/* 손잡이를 끄는 동안 숫자가 흔들리지 않게 자리를 고정한다. `100%`가 가장 넓다. */
.pomo-veil-value {
  flex: none;
  min-width: 4ch;
  color: var(--text-dim);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* ── 미니 타이머 — 패널을 닫아도 남는다 ─────────────── */

/* 반투명하게 두되 **글자까지 흐리지 않는다.** 바탕만 반투명으로 깔고 글자는
   불투명한 색을 그대로 쓴다. opacity로 통째로 흐리면 대비가 그만큼 떨어지고,
   얼마나 떨어졌는지는 계산해보기 전에는 보이지 않는다.

   82%면 뒤에 오는 것이 --bg든 --surface든 섞인 결과가 원래 --surface에서
   거의 벗어나지 않는다 (라이트 #fdfdfd, 다크 #1a1a1e). 두 테마 모두에서
   --text와 --text-dim의 대비가 평면 --surface 위와 같은 자리에 남는다. */
.pomo-mini {
  /* 바탕이 얼마나 진한가. app.js가 저장된 값으로 갈아 끼운다 (Store.getMiniOpacity).
     여기 적힌 82%는 그 전에 한 번 칠해지는 값이라 기본값과 같아야 한다.
     **바탕과 테두리에만 건다** — 글자까지 흐리게 하면 대비가 그만큼 떨어지고,
     얼마나 떨어졌는지는 계산해보기 전에는 보이지 않는다. */
  --mini-veil: 82%;
  /* 상태마다 테두리 색만 갈아 끼우고, 옅게 만드는 일은 아래 한 줄이 도맡는다.
     상태별로 따로 옅게 하면 새 상태를 더할 때 그 한 줄을 빠뜨린다. */
  --mini-line: var(--border);

  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 8px 7px 12px;
  border: 1px solid color-mix(in srgb, var(--mini-line) var(--mini-veil), transparent);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--surface) var(--mini-veil), transparent);
  backdrop-filter: blur(8px);
  box-shadow: 0 6px 24px color-mix(in srgb, rgb(0 0 0 / 18%) var(--mini-veil), transparent);
}

.pomo-mini[hidden] {
  display: none;
}

/* 불투명도를 조절하는 동안 미리 보여주는 상태. 걸린 판이 없는데 시계가 떠 있으면
   무언가 돌아가는 줄 알기 때문에, 지금 무엇을 보고 있는지 한 줄로 말한다. */
.pomo-mini.is-preview .pomo-mini-time::after {
  content: ' 미리보기';
  font-size: 11px;
  font-weight: 600;
  color: var(--text-dim);
}

.pomo-mini.is-running,
.pomo-mini.is-waiting {
  --mini-line: color-mix(in srgb, var(--focus) 55%, var(--border));
}

/* 휴식은 다른 상태다. 값은 .pomo.is-rest와 같다. */
.pomo-mini.is-rest {
  --focus: light-dark(#006c4c, #57d3a4);
}

.pomo-mini-main {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  cursor: pointer;
}

.pomo-mini-time {
  color: var(--text);
  font-size: 17px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}

.pomo-mini.is-running .pomo-mini-time,
.pomo-mini.is-waiting .pomo-mini-time {
  color: var(--focus);
}

.pomo-mini-phase {
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
}

.pomo-mini-next {
  flex: none;
  padding: 5px 10px;
  border: 1px solid transparent;
  border-radius: 7px;
  background: var(--focus);
  color: var(--surface);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}

.pomo-mini-next:hover {
  background: color-mix(in srgb, var(--focus) 84%, var(--text));
}

.pomo-mini-next[hidden] {
  display: none;
}

.pomo-mini-close {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--text-dim);
  cursor: pointer;
}

.pomo-mini-close:hover {
  background: color-mix(in srgb, var(--text) 8%, transparent);
  color: var(--text);
}

/* ── 빼놓은 창 (Document Picture-in-Picture) ─────────── */

/* 이 규칙은 별도 창의 문서에 걸린다. 그 창은 같은 styles.css를 물려받지만
   `body`의 여백과 640px 폭은 거기서 쓸모가 없다 — 창 전체가 시계 하나다.

   **가로는 늘 남고 세로가 비싸다.** 브라우저가 정하는 최소 폭(240px)을 내릴 방법이
   없어서, 세로를 줄이면 가로에 빈 자리가 생긴다. 좁아질수록 세로로 쌓던 것을
   가로로 눕혀 그 자리를 쓴다. */
body.pip {
  --pip-dial: min(176px, calc(100vh - 24px), calc(100vw - 24px));

  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
  padding: 0;
  /* 브라우저가 내주는 크기는 우리가 정하지 못한다. 아주 좁게 왔을 때 스크롤 막대가
     생기면, 손바닥만 한 창의 남은 자리마저 그것이 가져간다. */
  overflow: hidden;
}

.pip-box {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 10px;
}

/* 눈금은 패널의 펼친 시계와 같은 클래스를 쓴다 (.pomo-dial-face · -track · -fill · -label).
   색도 굵기도 1초 전환도 거기서 그대로 온다. 여기서는 창에 맞는 크기만 다시 잡는다.

   **요청한 크기가 그대로 나오지 않는다.** 적는 높이는 바깥 크기라 브라우저가 자기
   머리띠를 그 안에서 빼가고, 최소 폭을 따로 두며, 마지막 크기를 기억하고, 사용자가
   직접 끌어 바꾼다. 숫자로 못박으면 그때 시계가 창 밖으로 잘린다. */
body.pip .pomo-dial-face {
  width: var(--pip-dial);
  height: var(--pip-dial);
}

/* 원 안의 것들은 전부 눈금에서 크기를 얻는다. 따로 적어두면 창이 줄어들 때
   숫자만 남아 원 밖으로 넘친다. */
body.pip .pip-time {
  font-size: clamp(13px, calc(var(--pip-dial) / 4.6), 36px);
}

body.pip .pip-phase {
  font-size: clamp(9px, calc(var(--pip-dial) / 13), 12px);
}

body.pip .pip-action {
  width: clamp(22px, calc(var(--pip-dial) / 4.2), 34px);
  height: clamp(22px, calc(var(--pip-dial) / 4.2), 34px);
  margin-top: 2px;
  /* 원 안의 글자 묶음은 `pointer-events: none`이다 (패널에서는 눈금 위에 얹힌
     장식일 뿐이라 그렇다). 여기서는 그 안에 **누를 것**이 들어가므로 되살린다.
     이 줄이 없으면 버튼이 보이기만 하고 눌리지 않는다. */
  pointer-events: auto;
}

/* ── 낮은 창 — 세로로 쌓던 것을 가로로 눕힌다 ─────────
   구간 이름을 접고, 버튼을 **원 밖 오른쪽**으로 뺀다. 세로가 모자란 것이지
   가로가 모자란 것이 아니므로, 원은 그만큼 더 클 수 있다. */
@media (max-height: 130px) {
  body.pip {
    /* 오른쪽으로 뺀 버튼 자리(48)만큼 원이 쓸 가로가 줄어든다 */
    --pip-dial: min(176px, calc(100vh - 24px), calc(100vw - 76px));
  }

  body.pip .pip-phase {
    display: none;
  }

  /* 흐름에서 빼내 원 옆에 세운다. 상자는 그만큼 오른쪽 여백을 비워 자리를 만든다. */
  .pip-box {
    padding-right: 48px;
  }

  body.pip .pip-action {
    position: absolute;
    left: calc(100% + 10px);
    top: 50%;
    margin-top: 0;
    transform: translateY(-50%);
  }
}

/* ── 숫자만 보기 ─────────────────────────────────────
   눈금을 접고 **한 줄로 눕힌다.** 세로로 쌓으면 그만큼 창이 높아야 하는데,
   가로는 최소 폭 때문에 어차피 남는다. 어느 크기에서 보느냐가 아니라
   **무엇을 보고 싶으냐**로 갈리므로 창 높이가 아니라 고른 값이 정한다. */
body.pip.is-text .pomo-dial-face {
  width: auto;
  height: auto;
}

/* **직계 자식만** 잡는다. `>`를 빼면 이 안의 버튼 아이콘까지 함께 사라져,
   테두리만 남은 빈 동그라미가 된다 — 실제로 그렇게 만들어 놓고 한참 못 봤다. */
body.pip.is-text .pomo-dial-face > svg {
  display: none;
}

body.pip.is-text .pomo-dial-label {
  position: static;
  flex-direction: row;
  align-items: center;
  gap: 8px;
}

/* 오른쪽 위 보기 방식 버튼과 부딪히지 않게 그쪽 여백을 비워둔다 */
body.pip.is-text .pip-box {
  padding: 10px 30px 10px 10px;
}

/* 148px은 손으로 센 값이었고, 그 셈이 가장 긴 구간 이름(`긴 휴식 시작`)을 빠뜨렸다.
   폭 240px — 브라우저가 강제하는 최소이자 처음 열릴 때의 폭 — 에서 대기 상태가 되면
   시계 앞자리와 유일한 조작 버튼이 함께 잘렸다. 넘친 것을 `overflow: hidden`이
   잘라내 스크롤조차 안 된다.

   그래서 **줄이 모자라면 접히게** 둔다. 잘리는 것보다 두 줄이 낫고, 창 높이는
   사용자가 늘릴 수 있지만 잘린 글자는 되찾을 방법이 없다.
   구간 이름은 줄바꿈을 허용해 그 줄이 가장 먼저 양보하게 한다. */
body.pip.is-text .pomo-dial-label {
  flex-wrap: wrap;
  justify-content: center;
}

body.pip.is-text .pip-time {
  font-size: clamp(15px, min(calc(100vh - 30px), calc((100vw - 148px) / 2.6)), 44px);
}

body.pip.is-text .pip-phase {
  display: inline;
  font-size: clamp(10px, calc(100vh / 9), 12px);
  /* 다른 자리에서는 한 줄로 두지만 여기서는 접히는 쪽이 낫다 */
  white-space: normal;
  text-align: center;
}

body.pip.is-text .pip-action {
  position: static;
  transform: none;
  margin-top: 0;
  width: clamp(22px, calc(100vh / 4), 32px);
  height: clamp(22px, calc(100vh / 4), 32px);
}

.pip-phase {
  color: var(--text-dim);
  font-weight: 600;
  white-space: nowrap;
}

.pip-time {
  color: var(--text);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  line-height: 1.1;
}

body.pip.is-running .pip-time,
body.pip.is-waiting .pip-time {
  color: var(--focus);
}

/* 값은 .pomo.is-rest와 같다. */
body.pip.is-rest {
  --focus: light-dark(#006c4c, #57d3a4);
}

/* 누를 것. 글자를 빼고 모양만 남겼다 — 글자 버튼 한 줄이 차지하던 높이가
   그대로 가리는 넓이였다. 읽히는 이름은 aria-label이 들고 있다.

   **바탕을 옅게 깔아 존재를 만든다.** 테두리만 두면 그 안의 그림이 창 바탕과 같은
   어둠 위에 얇게 얹혀, 멀리서는 빈 동그라미로만 보인다. */
.pip-action {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  /* 테두리는 **옅히지 않는다.** 55%로 두었더니 창 바탕과 2.46까지 내려가, 경계에
     요구되는 3:1에 못 미쳤다. 패널의 `시작` 버튼과 같은 굵기·같은 색으로 둔다. */
  border: 1px solid var(--focus);
  border-radius: 50%;
  background: color-mix(in srgb, var(--focus) 16%, transparent);
  color: var(--focus);
  cursor: pointer;
}

/* 그림이 칸을 꽉 채워야 한다. 작게 그리면 버튼 지름의 3분의 1도 차지하지 못해
   테두리만 보인다. 아이콘 좌표도 24칸을 거의 다 쓰도록 그려져 있다 (app.js). */
.pip-action svg {
  width: 78%;
  height: 78%;
}

.pip-action:hover {
  background: color-mix(in srgb, var(--focus) 28%, transparent);
}

/* 기다리는 중에는 누를 것이 이것 하나뿐이다. 바탕까지 채워 그 사실을 말한다. */
body.pip.is-waiting .pip-action {
  border-color: transparent;
  background: var(--focus);
  color: var(--surface);
}

/* 보기 방식 버튼. 모서리에 세워 흐름에서 빼낸다 — 줄로 두면 그 줄만큼 창이 커진다.
   흐리게 두되 hover로만 드러내지는 않는다. 여기 이런 버튼이 있다는 것을
   알 방법이 창 안에 달리 없다. */
.pip-mode {
  position: absolute;
  top: 3px;
  right: 3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--text-dim);
  cursor: pointer;
  /* 창 모서리에 붙어 있어 밖으로 그리면 잘린다. 안쪽에 붙인다. */
  outline-offset: -1px;
}

.pip-mode:hover {
  background: color-mix(in srgb, var(--text) 10%, transparent);
  color: var(--text);
}

/* ── 요약 줄 ────────────────────────────────────────── */

.summary-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 18px;
  min-height: 22px;
}

.stats-text {
  margin: 0;
  flex: none;
  color: var(--text-dim);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.clear-completed {
  flex: none;
  padding: 3px 9px;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: none;
  color: var(--text-dim);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}

.clear-completed:hover {
  color: var(--text);
  border-color: var(--text-dim);
}

.clear-completed[hidden] {
  display: none;
}

.progress-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 14px 0 8px;
}

/* 빈 쪽에도 잉크를 남긴다. 10%로는 배경에 묻혀 채워진 만큼만 보이고 **전체가
   어디까지인지**가 안 보였다. 3:1까지 올리면 4px 막대가 통째로 찬 것처럼 보여
   오히려 잘못 읽히므로, 자리를 알아볼 만큼만 올린다 — 정확한 수는 바로 옆
   `.stats-text`가 글로 말한다. */
.progress {
  flex: 1;
  height: 4px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--text) 22%, transparent);
  overflow: hidden;
}

.progress-fill {
  width: 0;
  height: 100%;
  border-radius: 999px;
  background: var(--focus);
  transition: width 0.15s ease;
}

.progress-percent {
  flex: none;
  min-width: 34px;
  color: var(--text-dim);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* ── 입력 ───────────────────────────────────────────── */

.add-form {
  display: flex;
  gap: 8px;
}

.add-input,
.add-category,
.add-priority,
.add-button {
  height: 40px;
  padding: 0 12px;
  border: 1px solid var(--field-border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font: inherit;
}

.add-input {
  flex: 1;
  min-width: 0;
}

.add-input::placeholder {
  color: var(--text-dim);
}

.add-category,
.add-priority {
  flex: none;
  cursor: pointer;
}

.add-priority {
  padding: 0 8px;
}

.add-button {
  flex: none;
  padding: 0 16px;
  font-weight: 600;
  cursor: pointer;
}

.add-button:hover {
  border-color: var(--text-dim);
}

/* 접두어 규칙은 발견되지 않으면 없는 기능이다. 상시 노출한다. */
.add-hint {
  margin: 6px 2px 0;
  color: var(--text-dim);
  font-size: 12px;
}

/* ── 검색 ───────────────────────────────────────────── */

.search-row {
  position: relative;
  margin-top: 10px;
}

.search-input {
  width: 100%;
  height: 36px;
  padding: 0 34px 0 12px;
  border: 1px solid var(--field-border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 14px;
}

.search-input::placeholder {
  color: var(--text-dim);
}

.search-clear {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--text-dim);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}

.search-clear:hover {
  background: color-mix(in srgb, var(--text) 8%, transparent);
  color: var(--text);
}

.search-clear[hidden] {
  display: none;
}

/* ── 필터 ───────────────────────────────────────────── */

/* 탭과 태그 바를 한 줄에 둔다. 태그가 많으면 아래로 흘러내린다. */
.filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
  margin: 18px 0 12px;
}

/*
  탭이 여러 줄이 되어도 **톱니와 정렬은 첫 줄에 남는다.**

  가운데 정렬이면 줄이 늘어날 때마다 도구가 블록 한복판으로 내려가, 탭 사이에 끼어
  있는 것처럼 보인다. 카테고리 8개에 폰 폭이면 벌써 39px 아래로 떠 있었다.
  위에 붙인 뒤, 탭 한 줄과 도구의 높이 차이만큼만 내려 첫 줄 가운데에 세운다.
*/
.tabs-row {
  display: flex;
  align-items: flex-start;
  gap: 4px;
  min-width: 0;
}

.tabs-row > .gear,
.tabs-row > .sort-select {
  margin-top: calc((var(--tab-h) - var(--tool-h)) / 2);
}

/*
  카테고리는 64개까지 만들 수 있다(도움말에도 그렇게 적혀 있다). 상한이 없으면
  그만큼 줄이 늘어 **할 일 목록을 화면 밖으로 밀어낸다** — 43개에서 탭바가 열 줄
  323px가 되고, 목록이 화면 아래 끝에서야 시작했다. 세 줄까지만 자리를 내주고
  나머지는 그 안에서 굴린다. 줄 간격을 변수로 두는 것은 상한을 여기서 유도하기
  위해서다. 따로 적으면 간격을 고칠 때 상한이 세 줄에서 어긋난다.
*/
.tabs {
  --tab-gap: 2px;

  display: flex;
  flex-wrap: wrap;
  gap: var(--tab-gap);
  min-width: 0;
  max-height: calc(var(--tab-h) * 3 + var(--tab-gap) * 2);
  overflow-y: auto;
}

/* ── 카테고리 관리 ──────────────────────────────────── */

.gear {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tool-h);
  height: var(--tool-h);
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--text-dim);
  cursor: pointer;
}

.gear:hover {
  background: color-mix(in srgb, var(--text) 8%, transparent);
  color: var(--text);
}

.gear[aria-expanded="true"] {
  background: var(--surface);
  color: var(--text);
}

.cat-panel {
  margin-bottom: 12px;
  padding: 10px 12px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.cat-panel[hidden] {
  display: none;
}

/*
  카테고리 목록에도 상한을 둔다. 43개를 만들어보니 패널이 1920px로 펼쳐져 페이지가
  3672px가 됐고, 정작 **`새 카테고리 이름` 입력칸이 그 끝에 밀려** 하나 더 만들려면
  화면 두 번 반을 굴려야 닿았다. 만드는 자리는 언제나 목록 바로 아래에 있어야 한다.

  줄 높이가 이름 길이에 따라 달라져 "몇 줄"로는 잡을 수 없다. 화면 절반까지만 쓴다.
*/
.cat-list {
  list-style: none;
  margin: 0 0 10px;
  padding: 0;
  max-height: min(50vh, 340px);
  overflow-y: auto;
}

.cat-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 7px 2px;
}

.cat-row + .cat-row {
  border-top: 1px solid var(--border);
}

.cat-dot {
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--cat);
}

/* 눌러서 이름을 고친다. 할 일 제목과 같은 생김새·같은 반응으로 맞춘다. */
.cat-name {
  padding: 2px 4px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: inherit;
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  text-align: left;
  cursor: text;
  overflow-wrap: anywhere;
}

.cat-name:hover {
  background: color-mix(in srgb, var(--text) 6%, transparent);
}

.cat-rename {
  min-width: 0;
  flex: 1;
  height: 28px;
  padding: 0 6px;
  border: 1px solid var(--focus);
  border-radius: 7px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 14px;
}

.cat-count {
  flex: 1;
  color: var(--text-dim);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.cat-remove,
.cat-confirm-yes,
.cat-confirm-no {
  flex: none;
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: none;
  color: var(--text-dim);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

.cat-remove:hover:not(:disabled),
.cat-confirm-no:hover {
  color: var(--text);
}

.cat-remove:disabled {
  opacity: 0.4;
  cursor: default;
}

.cat-confirm {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

.cat-confirm-text {
  color: var(--text-dim);
  font-size: 13px;
}

.cat-confirm-yes {
  border-color: var(--focus);
  color: var(--focus);
  font-weight: 600;
}

.cat-move {
  height: 28px;
  padding: 0 6px;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

.cat-add {
  display: flex;
  gap: 6px;
}

.cat-input {
  flex: 1;
  min-width: 0;
  height: 32px;
  padding: 0 10px;
  border: 1px solid var(--field-border);
  border-radius: 8px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 14px;
}

.cat-input::placeholder {
  color: var(--text-dim);
}

.cat-add-button {
  flex: none;
  padding: 0 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}

.cat-add-button:hover {
  border-color: var(--text-dim);
}

/* 무엇이 잘못됐고 어떻게 하면 되는지만 말한다 */
.cat-error {
  margin: 8px 2px 0;
  color: var(--text);
  font-size: 13px;
}

.cat-error[hidden] {
  display: none;
}

/* 세로 크기를 패딩이 아니라 `--tab-h`에서 얻는다. 옆의 도구가 그 값으로 자리를
   잡으므로, 여기서만 패딩으로 키우면 도구가 첫 줄 가운데에서 벗어난다.
   고른 탭이 굵은 글씨가 되어도 높이가 흔들리지 않는 효과도 함께 온다. */
.tab {
  display: inline-flex;
  align-items: center;
  min-height: var(--tab-h);
  padding: 0 12px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: none;
  color: var(--text-dim);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}

.tab:hover {
  color: var(--text);
}

.tab.is-active {
  border-color: var(--border);
  background: var(--surface);
  color: var(--text);
  font-weight: 600;
}

.sort-select {
  flex: none;
  height: var(--tool-h);
  max-width: 120px;
  padding: 0 4px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: none;
  color: var(--text-dim);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

.sort-select:hover {
  border-color: var(--border);
  color: var(--text);
}

/* 태그도 상한이 없으면 끝없이 늘어난다 — 항목마다 5개까지 붙으니 목록이 커질수록
   함께 커진다. 카테고리 탭과 같이 세 줄까지만 내주고 그 안에서 굴린다. */
.tag-bar {
  --tag-gap: 6px;

  display: flex;
  flex-wrap: wrap;
  gap: var(--tag-gap);
  max-height: calc(var(--tool-h) * 3 + var(--tag-gap) * 2);
  overflow-y: auto;
}

.tag-bar[hidden] {
  display: none;
}

/* 칸 높이를 `--tool-h`에서 얻는다. 위의 세 줄 상한이 이 값으로 계산되므로,
   여기서만 패딩으로 키우면 상한이 세 줄에서 어긋난다. */
.tag-chip {
  display: inline-flex;
  align-items: center;
  min-height: var(--tool-h);
  gap: 5px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text-dim);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

.tag-chip:hover {
  color: var(--text);
}

.tag-chip.is-active {
  border-color: var(--focus);
  color: var(--focus);
}

/* opacity로 흐리게 하지 않는다. 0.7을 겹치면 칩 색이 라이트 2.21·다크 2.99까지 떨어져
   양쪽 테마 모두 WCAG AA에 못 미쳤다. 칩 글자와 같은 색을 쓰고 크기로만 구분한다. */
.tag-count {
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

/* ── 목록 ───────────────────────────────────────────── */

/* 항목마다 카드를 띄우지 않는다. 목록 전체가 하나의 면이고,
   행 사이는 1px 실선으로만 나눈다. */
.todo-list {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.todo-list:empty {
  display: none;
}

.todo-list,
.todo-children {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* 계층은 마크업으로 표현한다 — <ul> 안의 <ul>. 들여쓰기는 그 위에 얹는 것뿐이다. */
.todo-children {
  position: relative;
}

/* 상위 체크박스 중심에서 내려오는 1px 연결선.
   마지막 하위의 체크박스 중심(행 높이의 절반)에서 멈춘다. */
.todo-children::before {
  content: "";
  position: absolute;
  left: var(--line-x);
  top: 0;
  bottom: calc(var(--row-h) / 2);
  width: 1px;
  background: var(--border);
}

.todo {
  display: flex;
  align-items: center;
  gap: var(--gap);
  padding: var(--row-pad-y) var(--row-pad);
  border-top: 1px solid var(--border);
}

/* 첫 행 위에는 선을 긋지 않는다 — 카드 테두리가 이미 있다. */
.todo-list > .todo-node:first-child > .todo {
  border-top: 0;
}

/* 들여쓰기는 행 안쪽 패딩으로 준다. 구분선이 카드 폭을 온전히 가로지른다. */
.todo-child {
  padding-left: calc(var(--row-pad) + var(--indent));
}

/* ── 끌기 손잡이 ────────────────────────────────────── */

/* 직접 순서 모드가 아니어도 자리는 지킨다. 모드를 바꿀 때 왼쪽이 흔들리면 안 된다. */
.todo-handle {
  flex: none;
  width: var(--handle);
  height: 22px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-dim);
  font-size: 13px;
  line-height: 22px;
  text-align: center;
}

.todo-handle.is-draggable {
  cursor: grab;
}

.todo-handle.is-draggable:active {
  cursor: grabbing;
}

.todo-node.is-dragging > .todo {
  opacity: 0.4;
}

/* 놓일 자리를 선으로 알린다 */
.todo-node.is-drop-before > .todo {
  box-shadow: inset 0 2px 0 var(--focus);
}

.todo-node.is-drop-after > .todo {
  box-shadow: inset 0 -2px 0 var(--focus);
}

/* ── 우선순위 마커 ──────────────────────────────────── */

/* 숫자를 그대로 보여준다. 사용자가 0~3으로 고르는 값이므로 그 값이 곧 표시다 —
   색이나 굵기만으로 네 단계를 구분시키면 읽을 수 없다. (PRD §9) */
.todo-priority {
  position: relative;
  flex: none;
  width: var(--marker);
  height: 22px;
  /* 왼쪽 여백을 주면 text-align: center가 그 안쪽의 가운데를 잡아 숫자가 오른쪽으로
     밀린다(16px 칸에서 왼쪽 6.8px · 오른쪽 1.8px였다). 0에만 서는 accent 바는
     흐름 밖(absolute)이라 자리를 비워둘 필요가 없다. */
  padding: 0;
  border: 0;
  border-radius: 3px;
  background: none;
  color: var(--text-dim);
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 22px;
  text-align: center;
  cursor: pointer;
}

/* 마커 자리에 그대로 들어앉는다. 칸보다 넓지만, 열려 있는 동안만이고
   그 줄만 잠깐 밀린다. 줄 높이가 바뀌면 목록 전체가 출렁이므로 높이는 맞춘다. */
.todo-priority-select {
  flex: none;
  height: 22px;
  padding: 0 2px;
  border: 1px solid var(--focus);
  border-radius: 4px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* 0에만 세로 accent 바를 세운다. 한눈에 걸리는 건 이 하나로 충분하다. */
.todo-priority::before {
  content: "";
  position: absolute;
  /* 칸 밖으로 뺀다. 안에 두면 가운데로 옮긴 숫자와 1px 남기고 붙는다.
     왼쪽은 손잡이와의 간격이라 비어 있다. */
  left: -5px;
  top: 0;
  bottom: 0;
  width: 3px;
  border-radius: 2px;
  background: transparent;
}

.todo-priority.is-p0 {
  color: var(--focus);
}

.todo-priority.is-p0::before {
  background: var(--focus);
}

/* 0은 굵게, 아래로 갈수록 명도를 낮춘다. 취소선은 완료 전용이다. */
.priority-0 .todo-title {
  font-weight: 700;
}

.priority-2 .todo-title,
.priority-3 .todo-title {
  color: var(--text-dim);
}

/* 3의 숫자를 더 흐리게 하던 `opacity: 0.6`을 걷어냈다. 2.42:1이었고 문맥 행과
   겹치면 0.33이 되어 1.57:1까지 내려갔는데, 그 흐림을 읽히는 선(4.5:1) 안으로
   끌어올리면 남는 차이가 눈에 보이지 않을 만큼 작다.

   애초에 기댈 신호가 아니었다 — 이 파일 맨 위에 적어둔 대로 **숫자가 곧 표시**이고
   (`.todo-priority`), 제목도 이미 2·3에서 `--text-dim`으로 내려간다. 하나를 더
   흐리게 해서 얻는 것보다 읽히지 않게 되는 쪽이 크다. */

.todo-check {
  flex: none;
  width: 18px;
  height: 18px;
  margin: 0;
  cursor: pointer;
  accent-color: var(--focus);
}

/* 제목은 버튼이다 — 클릭으로 편집에 들어가고, 키보드로도 닿아야 한다. */
.todo-title {
  flex: 1;
  min-width: 0;
  padding: 2px 4px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: inherit;
  font: inherit;
  font-weight: 500;
  text-align: left;
  cursor: text;
  overflow-wrap: anywhere;
}

.todo-title:hover {
  background: color-mix(in srgb, var(--text) 6%, transparent);
}

/* 하위는 글자 크기를 줄이지 않는다. 굵기만 낮춘다. (PRD §7) */
.todo-child .todo-title {
  font-weight: 400;
}

.todo.is-done .todo-title {
  color: var(--text-dim);
  text-decoration: line-through;
}

.todo-title-static {
  cursor: default;
}

.todo-title-static:hover {
  background: none;
}

.todo-edit {
  flex: 1;
  min-width: 0;
  height: 28px;
  padding: 0 6px;
  border: 1px solid var(--focus);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
}

/* ── 태그 배지 — 색은 쓰지 않는다. 색은 카테고리 전용이다. ── */

.tags {
  display: inline-flex;
  align-items: center;
  flex: none;
  gap: 4px;
}

.tag {
  display: inline-flex;
  align-items: center;
  border-radius: 999px;
  background: color-mix(in srgb, var(--text) 7%, transparent);
}

/* 3개를 넘으면 접고, 항목에 포커스하면 전부 펼친다 (PRD §7) */
.tag:nth-of-type(n + 4) {
  display: none;
}

.todo:focus-within .tag:nth-of-type(n + 4) {
  display: inline-flex;
}

.todo:focus-within .tag-more {
  display: none;
}

.tag-filter {
  padding: 2px 7px;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--text-dim); /* 카테고리 배지보다 약한 대비 */
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}

.tag-filter:hover {
  color: var(--text);
}

.tag-remove {
  width: 16px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-dim);
  font-size: 12px;
  line-height: 1;
  overflow: hidden;
  cursor: pointer;
}

.tag-more {
  color: var(--text-dim);
  font-size: 11px;
}

.tag-more-narrow {
  display: none;
}

/* ── 카테고리 배지 — 색만으로 구분하지 않고 라벨을 함께 쓴다 (F-08) ── */

/* --cat-hue는 app.js가 항목마다 넣어준다. 채도·명도는 테마마다 여기서 한 번만 정한다.

   밝기는 hsl이 아니라 OKLCh로 못박는다. hsl은 명도를 40%로 똑같이 줘도 색조마다
   휘도가 크게 달라, 파랑 배지는 라이트에서 대비 5.97이 나오는데 초록·노랑 배지는
   2.3까지 떨어졌다. 색조를 5도 간격으로 전부 훑으면 라이트에서 절반이 4.5:1에 못 미쳤다.
   OKLCh의 L은 색조와 무관하게 균일해 한 값으로 전 색조가 함께 통과한다.

   색조는 여전히 hsl에서 물려받는다 — 상대 색상 문법(oklch(from ...) 안의 c·h)이
   원본 색의 채도와 색조를 그대로 넘겨주기 때문이다. hsl의 각도와 OKLCh의 각도는
   같은 숫자라도 다른 색이라, --cat-hue를 oklch()에 바로 넣으면 업무(221)가 청록이
   되고 공부(262)가 파랑이 되어 두 카테고리가 서로 붙어버린다.
   데이터에는 여전히 hue 하나만 남는다. */
.badge,
.cat-dot {
  --cat-hue: 0;
  --cat: light-dark(
    oklch(from hsl(var(--cat-hue) 68% 40%) 0.45 c h),
    oklch(from hsl(var(--cat-hue) 85% 74%) 0.76 c h)
  );
}

.badge {
  flex: none;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--cat);
  /* span일 때도 button일 때도 같은 생김새여야 한다. 문맥 행에서는 span이라
     누를 수 없고, 그 밖에는 눌러서 소속을 바꾼다. */
  border: 0;
  font-family: inherit;
  /* 바탕은 --cat에서 바로 뽑는다. 같은 색을 두 번 적어두면 한쪽만 고쳤을 때
     글자와 바탕의 색조가 어긋나고, 어긋났다는 사실이 화면에 잘 드러나지 않는다. */
  background: light-dark(
    color-mix(in srgb, var(--cat) 12%, transparent),
    color-mix(in srgb, var(--cat) 18%, transparent)
  );
}

/* 누를 수 있다는 것은 커서로 알린다. 색을 더 늘리지 않는다. */
button.badge {
  cursor: pointer;
}

button.badge:hover {
  outline: 1px solid var(--cat);
  outline-offset: 1px;
}

/* 배지 자리에 그대로 들어앉는다. 줄 높이가 바뀌면 목록 전체가 출렁인다. */
.badge-select {
  flex: none;
  max-width: 140px;
  height: 22px;
  padding: 0 4px;
  border: 1px solid var(--focus);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
}

/* ── 문맥 행 — 보여주기만 한다 ──────────────────────── */

/* **`opacity`로 흐리게 하지 않는다.** 겹치면 대비가 그만큼 떨어지고, 얼마나
   떨어졌는지는 계산해보기 전에는 보이지 않는다 — 0.55에서 흐린 글자가 2.22:1,
   카테고리 배지가 2.32:1까지 내려가 있었다. 같은 이유로 `.tag-count`에서도
   한 번 걷어냈다. 색을 직접 고르고, 배경으로 "지금 자리"임을 말한다. */
.todo.is-context {
  background: color-mix(in srgb, var(--text) 3%, transparent);
}

.todo.is-context .todo-title {
  color: var(--text-dim);
  font-weight: 500;
}

.todo.is-context .todo-priority {
  cursor: default;
}

/* ── 하위 추가 ──────────────────────────────────────── */

/* opacity로 숨긴다. display:none이면 키보드로 닿을 수 없다. */
.todo-add-child {
  flex: none;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--text-dim);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}

.todo-add-child:hover {
  background: color-mix(in srgb, var(--text) 8%, transparent);
  color: var(--text);
}

/* 세로 여백만 준다. **`padding` 한 벌로 적으면 `.todo-child`의 들여쓰기가 함께
   지워진다** — 특이도가 같고 이쪽이 뒤라 이긴다. 실제로 형제 하위 행보다 52px
   왼쪽에 서 있었고, 연결선이 그 입력칸 위를 가로질렀다.
   가로 여백은 `.todo`(좌우 `--row-pad`)와 `.todo-child`(들여쓰기)에서 온다. */
.todo-draft {
  padding-top: 6px;
  padding-bottom: 6px;
}

.todo-draft-input {
  flex: 1;
  min-width: 0;
  height: 30px;
  padding: 0 8px;
  border: 1px solid var(--focus);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
}

.todo-draft-input::placeholder {
  color: var(--text-dim);
}

/* 넣는 버튼과 닫는 버튼. **hover로 숨기지 않는다** — 이 줄은 지금 무엇을 해야 하는지
   알려주는 자리라, 터치 화면에서 안 보이면 마우스 없이는 빠져나갈 길이 사라진다. */
.todo-draft-add,
.todo-draft-close {
  flex: none;
  height: 30px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text-dim);
  font: inherit;
  cursor: pointer;
}

.todo-draft-add {
  padding: 0 12px;
  font-size: 13px;
  font-weight: 600;
  color: var(--focus);
  border-color: var(--focus);
}

.todo-draft-close {
  width: 30px;
  padding: 0;
  font-size: 16px;
  line-height: 1;
}

.todo-draft-add:hover,
.todo-draft-close:hover {
  background: color-mix(in srgb, var(--text) 6%, transparent);
}

.todo-draft-close:hover {
  color: var(--text);
}

/* ── 삭제 ───────────────────────────────────────────── */

.todo-delete {
  flex: none;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--text-dim);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
}

/* `⋯`는 `×`와 같은 자리에 같은 크기로 선다. 둘을 따로 적으면 한쪽만 고쳐 어긋난다. */
.todo-detail {
  flex: none;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--text-dim);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
}

.todo-delete:hover,
.todo-detail:hover {
  background: color-mix(in srgb, var(--text) 8%, transparent);
  color: var(--text);
}

/* 스치듯 드러나는 것들은 **마우스가 있는 화면에서만** 숨긴다.
   터치 화면에는 hover가 없어, 숨기면 영영 못 찾는다. */
@media (hover: hover) and (pointer: fine) {
  .todo-delete,
  .todo-detail,
  .todo-add-child,
  .todo-handle.is-draggable {
    opacity: 0;
  }

  .tag-remove {
    width: 0;
    opacity: 0;
  }

  .todo:hover .todo-delete,
  .todo:focus-within .todo-delete,
  .todo:hover .todo-detail,
  .todo:focus-within .todo-detail,
  .todo:hover .todo-add-child,
  .todo:focus-within .todo-add-child,
  .todo:hover .todo-handle.is-draggable,
  .todo:focus-within .todo-handle.is-draggable {
    opacity: 1;
  }

  .tag:hover .tag-remove,
  .tag:focus-within .tag-remove {
    width: 16px;
    opacity: 1;
  }
}

/* ── 마감 (F-24) ────────────────────────────────────── */

/* 카테고리 배지 옆에 서지만 **색을 늘리지 않는다.** 마감은 중립색으로 두고,
   지난 것만 우선순위 0이 쓰는 accent를 빌려 온다 — 이미 "급하다"를 뜻하는 색이다. */
.todo-due {
  flex: none;
  padding: 2px 7px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--text) 7%, transparent);
  color: var(--text-dim);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* 색만으로 알리지 않는다 — 굵기를 함께 올리고, 읽어주는 쪽에는 app.js가
   "마감 지남"을 aria-label로 넣는다. 네 배경(라이트·다크 × 카드·바탕)에서
   5.01 / 5.40 / 5.09 / 5.66이다. 머리줄의 `.remaining`이 이미 같은 방식
   (accent 색 + accent 틴트)을 쓰고 있어 새로 들이는 것이 없다. */
.todo-due.is-overdue {
  background: color-mix(in srgb, var(--focus) 13%, transparent);
  color: var(--focus);
  font-weight: 600;
}

/* 반복 배지. 마감 바로 뒤에 서서 그 날짜가 어떻게 움직이는지를 말한다.
   **색을 늘리지 않는다** — 화살표도 글자도 `currentColor` 하나다. */
.todo-repeat {
  flex: none;
  padding: 2px 7px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--text) 7%, transparent);
  color: var(--text-dim);
  font-size: 12px;
  white-space: nowrap;
}

/* ── 대화상자의 반복 칸 ─────────────────────────────── */

.detail-repeat {
  display: block;
  height: 36px;
  margin-top: 6px;
  padding: 0 10px;
  border: 1px solid var(--field-border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 14px;
}

/* 마감이 없으면 고를 수 없다. 흐리게만 두지 않고 왜 막혔는지를 아래 문구가 말한다 —
   `opacity`로 덮으면 그 문구까지 같이 흐려져 읽히는 선 아래로 내려간다. */
.detail-repeat:disabled {
  border-color: var(--border);
  background: color-mix(in srgb, var(--text) 4%, transparent);
  color: var(--text-dim);
  cursor: not-allowed;
}

.detail-field[hidden] {
  display: none;
}

/* ── 추가 폼의 자세히 버튼 ──────────────────────────── */

/* **줄을 늘리지 않는다.** 여기에 날짜 칸을 펼치게 두었더니 `.add-form`이 좁은
   화면에서만 줄바꿈을 허용하는 탓에 제목 칸이 330px에서 26px로 찌그러졌다.
   행의 `⋯`와 같은 대화상자를 열어 입력 칸을 한 벌만 둔다. */
.add-detail {
  flex: none;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--field-border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text-dim);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
}

.add-detail:hover {
  color: var(--text);
}

/* 걸어둔 것이 있으면 버튼에 남긴다. 안 그러면 정했는지를 열어봐야만 안다.
   색만으로 알리지 않으려고 app.js가 aria-label에 날짜를 함께 넣는다. */
.add-detail.is-set {
  border-color: var(--focus);
  color: var(--focus);
}

.detail-label {
  flex: none;
  color: var(--text-dim);
  font-size: 13px;
  font-weight: 600;
}

.detail-due-field,
.detail-due-clock {
  height: 36px;
  padding: 0 10px;
  border: 1px solid var(--field-border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 14px;
}

.detail-due-field {
  flex: 1 1 auto;
  min-width: 0;
}

.detail-due-clock {
  flex: none;
}

.detail-due-reset {
  flex: none;
  height: 36px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: none;
  color: var(--text-dim);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

.detail-due-reset:hover {
  color: var(--text);
}

/* ── 자세히 대화상자 ────────────────────────────────── */

.dialog-detail {
  width: min(420px, calc(100vw - 32px));
}

/* 무엇을 고치는 중인지. 길면 줄바꿈해서 다 보여준다 — 잘라내면 비슷한 제목을
   가진 둘 중 어느 쪽인지 알 수 없다. */
.detail-subject {
  margin: 0 0 16px;
  color: var(--text);
  font-size: 15px;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.detail-field {
  margin-bottom: 18px;
}

.detail-due {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
}

.detail-note {
  margin: 6px 0 0;
  color: var(--text-dim);
  font-size: 12px;
}

/* 읽기만 하는 사실들. 값이 오른쪽에 붙어 두 줄이 나란히 읽힌다. */
.detail-facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 16px;
  margin: 0 0 20px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  font-size: 13px;
}

.detail-facts dt {
  color: var(--text-dim);
}

.detail-facts dd {
  margin: 0;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* ── 토스트 ─────────────────────────────────────────── */

.toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 14px;
  max-width: calc(100vw - 32px);
  padding: 10px 12px 10px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: 0 6px 24px rgb(0 0 0 / 18%);
}

.toast[hidden] {
  display: none;
}

/* 미니 타이머가 떠 있는 동안에는 토스트를 그 위로 비켜준다.
   토스트는 화면 폭에 따라 오른쪽 끝까지 자라므로 두 자리가 실제로 겹친다.
   겹치면 방금 생긴 `휴식 시작` 버튼이 그 사실을 알리는 알림에 가려,
   알리려던 그 동작을 누를 수 없게 된다 — 알림은 5초 뒤에 사라지지만
   그 5초가 하필 눌러야 하는 순간이다. */
body.has-mini .toast {
  bottom: 84px;
}

.toast-undo {
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: none;
  color: var(--focus);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

/* ── 빈 상태 ────────────────────────────────────────── */

.list-message {
  margin: 20px 2px 0;
  color: var(--text-dim);
  font-size: 14px;
}

.list-message[hidden] {
  display: none;
}

/* ── 데이터 — 가끔 쓰는 동작이라 아래에 둔다 ────────── */

.footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  margin-top: 18px;
}

.mirror-statuses {
  flex: 1 1 0;
  min-width: 0;
  display: grid;
  gap: 6px;
}

.mirror-status-row {
  display: flex;
  min-width: 0;
  align-items: center;
  gap: 6px;
}

.file-status {
  flex: 1 1 0;
  min-width: 0;
  margin-right: auto;
  color: var(--text-dim);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.file-retry {
  flex: none;
}

.file-conflict-resolve,
.markdown-conflict-resolve {
  flex: none;
}

.file-retry[hidden],
.file-conflict-resolve[hidden],
.markdown-conflict-resolve[hidden] {
  display: none;
}

.footer-button {
  flex: none;
  padding: 5px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: none;
  color: var(--text-dim);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

.footer-button:hover {
  border-color: var(--text-dim);
  color: var(--text);
}

.footer-button:disabled {
  cursor: default;
  opacity: 0.6;
}

/* ── 확인 다이얼로그 ────────────────────────────────── */

.dialog {
  margin: auto;
  width: min(360px, calc(100vw - 32px));
  padding: 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
}

.dialog::backdrop {
  background: rgb(0 0 0 / 45%);
}

/* ── 로그인 화면 ────────────────────────────────────── */

.dialog-login {
  width: min(340px, calc(100vw - 32px));
}

/* 무엇이 되고 무엇이 안 되는지를 맨 위에서 말한다. 아래 칸들이 잠겨 있는 이유다. */
.login-note {
  margin: 0 0 16px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--focus);
  border-radius: var(--radius);
  color: var(--text-dim);
  font-size: 13px;
  line-height: 1.5;
}

.login-field + .login-field {
  margin-top: 10px;
}

.login-label {
  display: block;
  margin-bottom: 4px;
  color: var(--text-dim);
  font-size: 12px;
  font-weight: 600;
}

.login-input,
.login-submit {
  width: 100%;
  height: 38px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font: inherit;
  font-size: 14px;
}

.login-input {
  padding: 0 10px;
  background: var(--surface);
  color: var(--text);
}

.login-submit {
  margin-top: 16px;
  border-color: var(--focus);
  background: none;
  color: var(--focus);
  font-weight: 600;
  cursor: pointer;
}

/* 잠긴 칸은 잠겨 보여야 한다. 눌러도 되는 것처럼 두면 몇 번을 더 누르게 된다. */
.login-input:disabled,
.login-submit:disabled {
  background: color-mix(in srgb, var(--text) 5%, transparent);
  border-color: var(--border);
  color: var(--text-dim);
  cursor: not-allowed;
}

.login-hint {
  margin: 14px 0 16px;
  color: var(--text-dim);
  font-size: 12px;
  line-height: 1.5;
}

/* ── 문의하기 ───────────────────────────────────────── */

.dialog-contact {
  width: min(400px, calc(100vw - 32px));
}

.contact-field + .contact-field {
  margin-top: 10px;
}

.contact-label {
  display: block;
  margin-bottom: 4px;
  color: var(--text-dim);
  font-size: 12px;
  font-weight: 600;
}

.contact-input,
.contact-textarea {
  display: block;
  width: 100%;
  border: 1px solid var(--field-border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 14px;
}

.contact-input {
  height: 38px;
  padding: 0 10px;
}

.contact-textarea {
  padding: 8px 10px;
  line-height: 1.5;
  /* 세로로만 늘린다. 가로로 끌면 상자 밖으로 나간다. */
  resize: vertical;
}

.contact-input::placeholder,
.contact-textarea::placeholder {
  color: var(--text-dim);
}

/* 칸에 딸린 안내. `.contact-hint`와 달리 칸 바로 아래에 붙으므로 여백을 줄인다. */
.contact-note {
  margin: 6px 0 0;
  color: var(--text-dim);
  font-size: 12px;
  line-height: 1.5;
}

/* 카테고리 오류와 같은 모양이다. 색을 하나 더 들이지 않고 `--text`로 적고
   크기로 구분한다 — 색은 카테고리·우선순위·토마토까지다. */
.contact-error {
  margin: 8px 0 0;
  color: var(--text);
  font-size: 13px;
}

.contact-error[hidden] {
  display: none;
}

.contact-hint {
  margin: 12px 0 16px;
  color: var(--text-dim);
  font-size: 12px;
  line-height: 1.5;
}

/* 주소는 눈으로 옮겨 적는 자리다. 붙은 글자가 갈라지지 않게 폭을 고정한다. */
.contact-address {
  color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  overflow-wrap: anywhere;
}

.dialog-title {
  margin: 0 0 8px;
  font-size: 15px;
  font-weight: 650;
}

.dialog-text {
  margin: 0 0 16px;
  color: var(--text-dim);
  font-size: 14px;
}

.dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

.dialog-button {
  padding: 7px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}

.dialog-button:hover {
  border-color: var(--text-dim);
}

/* 이 상자에서 눌러야 할 것 하나를 켠다. `is-danger`와 같은 모양이다 —
   되돌릴 수 없다는 뜻과 이것을 누르라는 뜻이 갈라져야 할 자리가 아직 없다.
   `is-primary`는 마크업에만 있고 규칙이 없어, Markdown 가져오기 상자에서
   `가져오기`가 `취소`와 똑같이 보이고 있었다. */
.dialog-button.is-danger,
.dialog-button.is-primary {
  border-color: var(--focus);
  color: var(--focus);
  font-weight: 600;
}

/* ── 포커스 ─────────────────────────────────────────── */

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* ── 좁은 화면 (360px까지) ──────────────────────────── */

@media (max-width: 480px) {
  :root {
    /* --indent는 정하지 않는다. --gap이 줄면 알아서 함께 줄어든다. */
    --gap: 8px;
    --row-pad: 10px;
  }

  .dialog-actions {
    flex-wrap: wrap;
  }

  .markdown-import-actions {
    flex-wrap: wrap;
  }

  .conflict-actions {
    flex-direction: column;
    align-items: stretch;
  }

  .help-list {
    grid-template-columns: 1fr;
    gap: 2px;
  }

  .pomo-controls {
    margin-left: 0;
  }

  /* 헤더 한 줄에 제목·배지·뽀모도로·토글·도움말이 다 들어가야 한다 */
  .header-row {
    gap: 6px;
  }

  .header-meta,
  .header-tools {
    gap: 6px;
  }

  .remaining {
    padding: 3px 7px;
  }

  .pomo-button {
    padding: 0 5px;
    gap: 4px;
  }

  .pomo-custom {
    flex: 1 1 auto;
  }

  .help-list dd {
    margin-bottom: 8px;
  }

  body {
    padding: 20px 12px 88px;
  }

  /* 한 줄에 셋을 욱여넣지 않는다 — 입력창이 먼저 한 줄을 차지한다 */
  .add-form {
    flex-wrap: wrap;
  }

  .add-input {
    flex: 1 1 100%;
  }

  .add-category {
    flex: 1 1 auto;
  }

  /* 좁은 화면에서는 제목이 한 줄을 차지하고 나머지가 둘째 줄에 선다.
     자세히 버튼도 그 줄의 식구다 — 넓히지 않고 자리만 지킨다. */
  .add-detail {
    flex: none;
  }

  .tabs {
    flex-wrap: wrap;
  }

  /* 좁은 화면에서는 태그를 1개만 보여준다 */
  .tag:nth-of-type(n + 2) {
    display: none;
  }

  .todo:focus-within .tag:nth-of-type(n + 2) {
    display: inline-flex;
  }

  .tag-more-wide {
    display: none;
  }

  .tag-more-narrow {
    display: inline;
  }

  .todo:focus-within .tag-more-narrow {
    display: none;
  }
}

/* 모션을 줄여달라고 한 사람에게는 줄인다.

   값마다 찾아 끄지 않고 전부 한 번에 끈다. 새로 붙인 전환을 여기 적는 걸 잊으면
   그 하나만 계속 움직이는데, 잊었다는 사실은 그 설정을 켠 사람만 알게 된다.
   그래서 이 파일에서 `!important`는 여기 한 곳뿐이다 — 어느 규칙보다 뒤에 서야
   구조적으로 빠짐이 없다.

   0으로 두지 않고 아주 짧게 남기는 이유는, 전환이 끝났을 때 도는 코드가
   `transition: none`에서는 영영 불리지 않기 때문이다. 지금은 그런 코드가 없지만
   나중에 붙어도 멈추지 않는다. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}
