/* ==========================================================================
   Tsutome — 로딩 / 빈 / 에러 3종 상태
   토큰은 design/tokens.css 에서 온다. 이 파일만 링크하면 색이 죽는다.

   왜 셋을 구분하나:
     로딩 = "기다려라"          → 최종 레이아웃 모양의 스켈레톤(스피너 아님)
     빈   = "데이터가 없다"      → 막다른 길이 아니라 다음 행동을 준다
     에러 = "실패했다"          → 원인 + 재시도. 이게 빈 상태와의 결정적 차이다.

   근태는 "지금 안 보이는 것"이 곧 분쟁거리라, 셋을 뭉뚱그리면 신뢰를 잃는다.
   실제로 punch.html 이 서버 연결 실패를 빈 상태로 그리고 있었고, 그래서 직원은
   화면을 껐다 켜는 것 말고 할 수 있는 게 없었다.
   ========================================================================== */

/* ---- 공통 ---- */
.state-loading, .state-empty, .state-error { line-height: 1.6; }

/* ---- 1. 로딩: 스켈레톤 ---- */
.skeleton {
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--line) 37%, var(--surface-2) 63%);
  background-size: 400% 100%;
  animation: sk 1.3s ease infinite;
  border-radius: 8px;
}
@keyframes sk { 0% { background-position: 100% 0 } 100% { background-position: -100% 0 } }
.sk-row { height: 44px; margin: var(--s2) 0; }
.state-loading { padding: var(--s2) 0; }

/* 스피너는 버튼 안처럼 레이아웃을 못 흉내내는 자리에서만 쓴다 */
.spinner {
  width: 20px; height: 20px; border: 2.5px solid var(--line);
  border-top-color: var(--brand); border-radius: 50%;
  animation: sp .7s linear infinite; display: inline-block; vertical-align: middle;
}
@keyframes sp { to { transform: rotate(360deg) } }

/* ---- 2. 빈 상태 ---- */
.state-empty, .empty {
  padding: var(--s7) var(--s5); text-align: center; color: var(--sub);
}
.state-empty .ic, .empty .ic { font-size: 34px; margin-bottom: var(--s2); }
.state-empty .t { font-size: var(--fs-lg); font-weight: var(--fw-bold); color: var(--ink); margin-bottom: 4px; }
.state-empty .h { font-size: var(--fs-sm); max-width: 34ch; margin: 0 auto; }
.state-empty .cta, .empty .cta { margin-top: var(--s4); }

/* ---- 3. 에러 상태 ---- */
.state-error {
  padding: var(--s5); text-align: center;
  background: var(--st-absent-bg); border: 1px solid var(--st-absent);
  border-radius: var(--r-md); color: var(--ink);
}
.state-error .ic { font-size: 28px; margin-bottom: 6px; }
.state-error .t { font-size: var(--fs-md); font-weight: var(--fw-bold); color: var(--st-absent); }
/* 원인 — 코드·거리·시각처럼 지원 문의 한 번에 해결될 정보를 담는다 */
.state-error .d { font-size: var(--fs-sm); color: var(--ink-2); margin-top: 5px; }
.state-error .d code {
  font-family: var(--font-num); font-size: var(--fs-xs);
  background: rgba(0, 0, 0, .06); padding: 1px 5px; border-radius: 4px;
}
.state-error .retry {
  margin-top: var(--s4); min-height: var(--tap-min); padding: 0 var(--s5);
  font: inherit; font-size: var(--fs-md); font-weight: var(--fw-bold);
  border: 1px solid var(--st-absent); border-radius: var(--r-sm);
  background: var(--surface); color: var(--st-absent); cursor: pointer;
}
.state-error .retry:hover { background: var(--surface-2); }

/* 인라인 축소판 — 목록 안에 끼워 넣는 좁은 에러 */
.state-error.inline { padding: var(--s3) var(--s4); text-align: left; }
/* 인라인이라고 버튼까지 작게 만들면 안 된다 — 키오스크에서 직원이 손가락으로 누른다.
   34px 로 뒀더니 실제로 25px 로 렌더됐다(패딩만으론 높이가 안 선다). */
.state-error.inline .retry { margin-top: var(--s2); min-height: var(--tap-min);
  padding: 0 var(--s4); font-size: var(--fs-sm); }

@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; }
  .spinner { animation-duration: 2s; }
}
