/* ==========================================================================
   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); }

/* ---- 4. 染み dye: **네가 누른 바로 그것**이 기다리는 중 ----

   앞의 셋과 다른 자리를 맡는다.
     스켈레톤 = "화면이 아직 없다"
     스피너   = "어딘가에서 뭔가 돌고 있다"
     染み     = "방금 누른 이 타일이 응답을 기다린다"

   왜 필요했나: punch.html 은 이름을 누르면 얼굴 등록 여부를 확인하러 서버에 갔다
   오는데, 그 사이 화면에 **아무 일도 일어나지 않았다**. 지점 전화기의 네트워크에서
   그 침묵은 0.1초일 수도 1초일 수도 있고, 직원은 안 눌렸다고 생각해 다시 누른다.

   왜 물드는 모양인가: 藍染 은 천을 위에서 덮는 게 아니라 **밑에서 빨려 올라간다**.
   그래서 transform-origin 은 언제나 bottom 이다. 이 방향이 이 시스템의 지문이다.

   규칙
   - **기다림이 시작되는 순간에만** 쓴다. 호버·성공·강조에 쓰면 싸구려가 된다.
   - 색을 덮지 않고 **씻는다**(--dye-alpha 기본 .14). 불투명하게 칠하면 그 위의
     글자 대비가 무너진다 — 물들었다고 이름이 안 읽히면 아무 의미가 없다.
   - 방향(콘솔=藍, 키오스크=琥珀)마다 --dye 를 덮어쓴다. 모션은 하나, 방언은 둘. */
.dyeing { position: relative; overflow: hidden; }
/* 물감은 글자 **뒤로** 간다. 자식을 올려 두지 않으면 ::after 가 이름을 덮는다. */
.dyeing > * { position: relative; z-index: 1; }
.dyeing::after {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: var(--dye); opacity: var(--dye-alpha, .14);
  transform-origin: bottom; transform: scaleY(0);
  animation: dye-rise var(--dur-dye) var(--ease-dye) forwards;
}
@keyframes dye-rise { from { transform: scaleY(0) } to { transform: scaleY(1) } }

/* ---- 染み 의 정적 형제: 담금 띠 ----
   같은 몸짓, 다른 데이터. 染み 는 **기다림**을 말하고 이건 **누적**을 말한다.
   목록 행의 왼쪽 모서리가 이 기간에 채운 만큼 차오른다 — 역시 아래에서 위로.

   자격(BRAND.md §3-3): 변화의 방향을 보여 준다. 기간 탭을 바꾸면 띠가 자라거나 줄고,
   그 움직임이 "이 기간에는 덜 채웠다"를 숫자를 읽기 전에 알려 준다.

   규칙 전문과 계산식은 BRAND.md §2. 여기서 지켜야 할 두 가지만 —
   ① transform-origin 은 bottom, ② 띠 옆에 언제나 숫자(.phrs)가 같이 나간다. */
.pdip {
  position: absolute; left: 9px; top: 11px; bottom: 11px; width: 4px;
  border-radius: 2px; background: var(--dip-track); overflow: hidden;
}
.pdip i {
  position: absolute; left: 0; right: 0; top: 0; bottom: 0;
  background: var(--dip-4); transform-origin: bottom;
  transition: transform 260ms var(--ease-dye);
}
.pdip[data-dip="0"] i { background: var(--dip-0) }
.pdip[data-dip="1"] i { background: var(--dip-1) }
.pdip[data-dip="2"] i { background: var(--dip-2) }
.pdip[data-dip="3"] i { background: var(--dip-3) }
/* 색만으로 정보를 전달하지 않는다(BRAND.md §4) — 띠는 훑기용, 이 숫자가 사실이다. */
.phrs {
  flex: 0 0 auto; font-size: var(--fs-sm); color: var(--sub);
  font-variant-numeric: tabular-nums slashed-zero;
}
/* 분모가 근무표가 아니라 **가정**일 때. 점선 밑줄은 "이 값은 추정이다"라는
   오래된 관례고, 색을 하나 더 쓰지 않아도 되는 유일한 방법이다.
   띠도 같이 흐려 둔다 — 확신의 정도가 다르면 겉모습도 달라야 한다. */
.phrs.est { border-bottom: 1px dotted var(--line-strong); }
.pdip[data-basis="assumed"] { opacity: .55; }

@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; }
  .spinner { animation-duration: 2s; }
  /* 물드는 과정만 뺀다. 물든 상태 자체는 남긴다 — 이건 장식이 아니라
     "눌렸다"는 유일한 신호라서, 없애면 그 사람은 다시 누르게 된다. */
  .dyeing::after { animation: none; transform: scaleY(1); }
  .pdip i { transition: none; }
}
