/* 지오영 디자인 토큰 + 리포트 레이아웃 (MIGRATION.md Phase 6-1).
 *
 * :root 토큰 블록은 app/gy_theme.py 에서 그대로 가져왔다 — 순수 CSS 변수라 이식된다.
 * 반면 gy_theme.py 의 나머지(레이아웃 규칙)는 [data-testid="stSidebarNav"] 처럼
 * Streamlit 내부 DOM 이름에 묶여 있어 옮길 수 없다. 그래서 색·타이포는 100% 재현,
 * 레이아웃은 여기서 새로 짠다.
 */

:root {
  /* 원시 스케일 v2.0 — 토큰 정의용(앱 코드 직접 사용 금지) */
  --navy-50:#F3F7FB; --navy-100:#E7EFF8; --navy-200:#C9D8EA; --navy-300:#9FB9D8;
  --navy-400:#7194C0; --navy-500:#4A73A7; --navy-600:#2C568F; --navy-700:#164883;
  --navy-800:#013B7A; --navy-900:#033163; --navy-950:#022040;
  --blue-50:#F3F8FC; --blue-400:#52B8FE; --blue-500:#1BA1FE; --blue-700:#0172C0; --blue-800:#01548E;
  --sky-500:#31A2DE;
  --green-50:#F5FAF5; --green-600:#3FB135; --green-800:#276E21;
  --yellow-50:#FCF9F3; --yellow-100:#F8F3E7; --yellow-600:#F8B300; --yellow-800:#8F6700;
  --magenta-50:#FCF3F6; --magenta-100:#F8E7ED; --magenta-600:#E50150; --magenta-700:#BC0142; --magenta-800:#8E0132;
  --gray-0:#FFFFFF; --gray-50:#F7F9FC; --gray-100:#EEF2F7; --gray-200:#E0E6EE; --gray-300:#C7D0DC;
  --gray-400:#9AA6B8; --gray-500:#6B7A92; --gray-600:#51607A; --gray-800:#2B364A; --gray-900:#1C2536;

  /* 시맨틱 — 라이트 */
  --gy-bg:var(--gray-50); --gy-surface:var(--gray-0); --gy-surface-sunken:var(--gray-100);
  --gy-border:var(--gray-200); --gy-border-strong:var(--gray-300);
  --gy-text:var(--gray-900); --gy-text-secondary:var(--gray-600); --gy-text-muted:var(--gray-500);
  --gy-primary:var(--navy-800); --gy-on-primary:#FFFFFF; --gy-primary-hover:var(--navy-700);
  --gy-primary-active:var(--navy-900); --gy-primary-subtle:var(--navy-50); --gy-on-primary-subtle:var(--navy-800);
  --gy-link:var(--blue-700); --gy-focus:var(--blue-500); --gy-focus-ring:0 0 0 3px rgba(27,161,254,.22);
  --gy-danger:var(--magenta-600); --gy-danger-text:var(--magenta-700); --gy-on-danger:#FFFFFF;
  --gy-accent:var(--sky-500);

  /* Nav 문맥(사이드바 전용) */
  --gy-nav-bg:var(--navy-950); --gy-nav-text:rgba(255,255,255,.62); --gy-nav-text-hover:rgba(255,255,255,.85);
  --gy-nav-text-active:#FFFFFF; --gy-nav-item-hover:rgba(255,255,255,.05); --gy-nav-item-active:rgba(255,255,255,.12);
  --gy-nav-section:rgba(255,255,255,.38); --gy-nav-border:rgba(255,255,255,.08);

  /* 형태 (v2: 카드10 / 모달14, 그림자 2겹) */
  --gy-radius-sm:4px; --gy-radius-md:6px; --gy-radius-lg:10px; --gy-radius-xl:14px; --gy-radius-full:9999px;
  --gy-shadow-sm:0 1px 2px rgba(16,22,31,.05);
  --gy-shadow-md:0 4px 12px rgba(16,22,31,.10),0 1px 2px rgba(16,22,31,.06);
  --gy-shadow-lg:0 12px 32px rgba(16,22,31,.16),0 2px 6px rgba(16,22,31,.08);

  --gy-font-sans:"Pretendard Variable",Pretendard,-apple-system,"Malgun Gothic",sans-serif;
  --gy-font-mono:ui-monospace,SFMono-Regular,Menlo,monospace;
}

/* ---- base ---- */
* { box-sizing: border-box; }
body {
  margin: 0; background: var(--gy-bg); color: var(--gy-text);
  font-family: var(--gy-font-sans); font-size: 14px; line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
a, a:visited { color: var(--gy-link); text-decoration: none; }
a:hover { text-decoration: underline; }
h1 { font-size: 24px; line-height: 32px; font-weight: 700; letter-spacing: -.02em; margin: 0; }
h2 { font-size: 20px; line-height: 28px; font-weight: 600; letter-spacing: -.01em; }

/* ---- 레이아웃: 사이드바 + 본문 ---- */
.gy-shell { display: flex; min-height: 100vh; }

.gy-nav {
  width: 220px; flex: 0 0 220px; background: var(--gy-nav-bg);
  display: flex; flex-direction: column; padding: 20px 0;
}
.gy-nav .brand {
  color: #fff; font-weight: 700; font-size: 15px; letter-spacing: -.01em;
  padding: 0 20px 18px; border-bottom: 1px solid var(--gy-nav-border); margin-bottom: 12px;
}
.gy-nav .brand small { display: block; font-weight: 400; font-size: 11px; color: var(--gy-nav-section); margin-top: 4px; }
.gy-nav .section {
  color: var(--gy-nav-section); font-size: 11px; font-weight: 600;
  letter-spacing: .05em; padding: 14px 20px 6px;
}
.gy-nav a {
  display: block; padding: 8px 20px; color: var(--gy-nav-text); font-size: 13px;
  text-decoration: none; transition: background 150ms ease-out, color 150ms ease-out;
}
.gy-nav a:hover { background: var(--gy-nav-item-hover); color: var(--gy-nav-text-hover); text-decoration: none; }
.gy-nav a.active { background: var(--gy-nav-item-active); color: var(--gy-nav-text-active); font-weight: 600; }
.gy-nav .spacer { flex: 1; }
.gy-nav .who { padding: 10px 20px 0; border-top: 1px solid var(--gy-nav-border); color: var(--gy-nav-section); font-size: 11px; }
/* 로그아웃은 POST 여야 해서(Django 5.2 LogoutView) <a> 가 아니라 <form><button> 이다.
   보이는 모습은 예전 링크 그대로 유지한다 — 버튼처럼 보이면 사이드바 균형이 깨진다. */
.gy-nav .who form { display: inline; }
.gy-nav .who button {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: inherit; font-size: inherit; color: var(--gy-link);
}
.gy-nav .who button:hover { text-decoration: underline; }
.gy-nav .who button:focus-visible { outline: none; box-shadow: var(--gy-focus-ring); border-radius: var(--gy-radius-sm); }

.gy-main { flex: 1; min-width: 0; padding: 24px 28px 48px; }

/* ---- 상단 바 ---- */
.gy-topbar { display: flex; align-items: flex-end; gap: 16px; flex-wrap: wrap; margin-bottom: 20px; }
.gy-topbar .grow { flex: 1; }
.gy-field label { display: block; font-size: 11px; font-weight: 600; color: var(--gy-text-secondary); margin-bottom: 4px; }
/* 체크박스·hidden 은 뺀다. 안 빼면 **체크박스가 텍스트 입력칸 옷을 입어** min-width
   150px 를 차지하고, 바로 옆 글자가 그만큼 밀려 어느 이름이 어느 칸인지 흐려진다
   (계정 추가 화면에서 담당자가 지적 — 2026-08-20). 같은 관용구를 이 파일이 이미 두 곳에서
   쓴다(`table.gy-edit input:not(...)` · `table.gy-emp input:not(...)`). */
.gy-field select, .gy-field .static,
.gy-field input:not([type="checkbox"]):not([type="hidden"]) {
  font-family: inherit; font-size: 13px; padding: 7px 10px; min-width: 150px;
  border: 1px solid var(--gy-border-strong); border-radius: var(--gy-radius-md);
  background: var(--gy-surface); color: var(--gy-text);
}
.gy-field select:focus, .gy-field input:focus {
  outline: none; border-color: var(--gy-focus); box-shadow: var(--gy-focus-ring);
}
.gy-field .static { display: inline-block; background: var(--gy-surface-sunken); color: var(--gy-text-secondary); }

/* ---- KPI 카드 ---- */
.gy-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 8px; }
.gy-kpi {
  background: var(--gy-surface); border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-lg); padding: 14px 16px; box-shadow: var(--gy-shadow-sm);
}
.gy-kpi .label { font-size: 11px; font-weight: 600; color: var(--gy-text-muted); letter-spacing: .02em; }
.gy-kpi .value { font-size: 24px; font-weight: 700; letter-spacing: -.02em; margin-top: 2px; color: var(--gy-primary); }
.gy-kpi .value.text { font-size: 15px; font-weight: 600; padding: 6px 0 4px; }
.gy-kpi .lines { margin-top: 6px; font-size: 12px; color: var(--gy-text-secondary); }
.gy-kpi .lines div + div { margin-top: 3px; color: var(--gy-text-muted); }

.gy-caption { color: var(--gy-text-muted); font-size: 12px; margin: 10px 0 18px; font-family: var(--gy-font-mono); }

/* ---- 탭 ---- */
.gy-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--gy-border-strong); margin-bottom: 18px; }
.gy-tabs a {
  padding: 9px 14px; font-size: 13px; font-weight: 600; color: var(--gy-text-muted);
  border-bottom: 2px solid transparent; margin-bottom: -1px; text-decoration: none;
  transition: color 150ms ease-out, border-color 150ms ease-out;
}
.gy-tabs a:hover { color: var(--gy-text-secondary); text-decoration: none; }
.gy-tabs a.active { color: var(--gy-primary); border-bottom-color: var(--gy-primary); }

/* ---- 개인별 상세: 계산 흐름 ---- */
.gy-who { margin: 4px 0 12px; }
.gy-who small { font-size: 13px; font-weight: 400; color: var(--gy-text-muted); letter-spacing: 0; }
.gy-flow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
.gy-flow .fc {
  display: inline-flex; align-items: baseline; gap: 8px;
  background: var(--gy-surface); border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-lg); padding: 9px 14px; box-shadow: var(--gy-shadow-sm);
  font-size: 12px; color: var(--gy-text-secondary);
}
.gy-flow .fc b {
  font-size: 18px; font-weight: 700; color: var(--gy-text);
  font-variant-numeric: tabular-nums; letter-spacing: -.01em;
}
.gy-flow .fc.hi { border-color: var(--gy-primary); background: var(--gy-primary-subtle); }
.gy-flow .fc.hi b { color: var(--gy-primary); }
.gy-flow .fa { color: var(--gy-text-muted); font-size: 14px; font-weight: 600; }

/* ---- 감사용 접힘 ---- */
.gy-details { margin-top: 18px; }
.gy-details > summary {
  cursor: pointer; font-size: 13px; font-weight: 600; color: var(--gy-text-secondary);
  padding: 9px 12px; background: var(--gy-surface); border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-md); list-style: none;
}
.gy-details > summary::-webkit-details-marker { display: none; }
.gy-details > summary:hover { color: var(--gy-primary); border-color: var(--gy-border-strong); }
.gy-details[open] > summary { margin-bottom: 10px; }

/* ---- 범례 + 도구 줄 ---- */
.gy-toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.gy-toolbar .gy-legend { margin-bottom: 0; }
.gy-toolbar .gy-btn { margin-left: auto; }

.gy-btn {
  display: inline-block; font-size: 12px; font-weight: 600; white-space: nowrap;
  font-family: inherit; border: 0; cursor: pointer;   /* <a> 와 <button> 을 같이 쓴다 */
  padding: 7px 14px; border-radius: var(--gy-radius-md); text-decoration: none;
  background: var(--gy-primary); color: var(--gy-on-primary);
  box-shadow: var(--gy-shadow-sm); transition: background 150ms ease-out;
}
.gy-btn:hover { background: var(--gy-primary-hover); color: var(--gy-on-primary); text-decoration: none; }
.gy-btn:active { background: var(--gy-primary-active); }
.gy-btn:focus-visible { outline: none; box-shadow: var(--gy-focus-ring); }

.gy-legend { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.gy-chip { font-size: 11px; font-weight: 600; padding: 4px 10px; border-radius: var(--gy-radius-full); }
.gy-chip.warn { background: var(--yellow-100); color: var(--yellow-800); }
.gy-chip.danger { background: var(--magenta-100); color: var(--magenta-800); }
.gy-chip.ok { background: var(--green-50); color: var(--green-800); }
.gy-chip.leave { background: var(--blue-50); color: var(--blue-800); }
/* 배경색이 아니라 띠·기호로 말하는 표시 — 칩도 색을 안 쓴다. 색 칩과 나란히 두면
   "이것도 배경색이구나" 로 읽혀, 실제 표에서 그 색을 찾다 못 찾는다. */
.gy-chip.mark { background: var(--gy-surface-sunken); color: var(--gy-text-muted); }
.gy-chip.mark .bar { display: inline-block; width: 3px; height: 10px; vertical-align: -1px;
                     background: var(--magenta-800); margin-right: 3px; }

/* 기준선 대비 증감 (근무조 비교) — 색이 곧 판단이라 방향을 색으로만 말한다.
   green=좋아짐(표와 더 맞음) / magenta=나빠짐. 규모는 숫자가 말한다. */
.gy-delta { display: block; font-size: 11px; font-weight: 600; margin-top: 2px; }
.gy-delta.up { color: var(--green-800); }
.gy-delta.down { color: var(--magenta-800); }

/* ---- 표 ---- */
.gy-tablewrap {
  background: var(--gy-surface); border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-lg); overflow: auto; max-height: 70vh; box-shadow: var(--gy-shadow-sm);
}
table.gy-table { border-collapse: separate; border-spacing: 0; width: 100%; font-size: 13px; }
table.gy-table th {
  /* z-index 2 — 합계 행(sticky, z-index 1)보다 위에 있어야 겹쳐도 머리글이 이긴다 */
  position: sticky; top: 0; z-index: 2; background: var(--gy-surface-sunken);
  color: var(--gy-text-secondary); font-size: 11px; font-weight: 600; letter-spacing: .02em;
  text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--gy-border-strong); white-space: nowrap;
}
table.gy-table th.num, table.gy-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
/* 정렬 가능한 머리글 — 누를 수 있다는 것이 보여야 한다. 화살표 자리를 늘 비워 두어
   (빈 span 이 자리를 차지) 정렬을 켜고 끌 때 열 너비가 흔들리지 않는다. */
table.gy-table th[data-sort-col] { cursor: pointer; user-select: none; }
table.gy-table th[data-sort-col]:hover { color: var(--gy-text); }
table.gy-table th[data-sort-col]:focus-visible { outline: 2px solid var(--gy-accent); outline-offset: -2px; }
table.gy-table th[data-sort-col].sorted { color: var(--gy-text); }
table.gy-table th .gy-sort-mark { display: inline-block; min-width: 1em; margin-left: 3px; font-size: 9px; }
table.gy-table td { padding: 7px 10px; border-bottom: 1px solid var(--gy-border); white-space: nowrap; }
table.gy-table td.idx { color: var(--gy-text-muted); text-align: right; font-size: 11px; }
/* 값이 '없다'가 아니라 '아직 모른다'는 표시 — 빈 입력칸은 확인이 끝난 것과 구별되지 않는다.
   KPI 의 '입사일 미확인 N명' 을 표에서 눈으로 찾을 수 있게 하는 것이 목적이라 칸을 물들인다. */
table.gy-table td.gy-unknown { background: var(--yellow-50); }
table.gy-table tbody tr:hover td.gy-unknown { background: var(--yellow-100); }
table.gy-table tbody tr:hover td { background: var(--gy-primary-subtle); }
table.gy-table tr.warn td { background: var(--yellow-100); color: var(--yellow-800); }
table.gy-table tr.danger td { background: var(--magenta-100); color: var(--magenta-800); }
table.gy-table tr.warn:hover td { background: var(--yellow-50); }
table.gy-table tr.danger:hover td { background: var(--magenta-50); }
/* 직원 마스터에 없는 사람 — 배경이 아니라 **왼쪽 띠**로 표시한다. 이 사람들은 거의 늘
   '부분근무'(warn)로도 잡혀 있어서, 배경을 또 물들이면 둘 중 하나가 가려진다.
   축이 다른 신호이므로 표시도 축이 달라야 한다. */
table.gy-table tr.unknown td:first-child { border-left: 3px solid var(--magenta-800); }

/* ---- 휴가 달력 — 날짜를 여러 개 골라 한 번에 등록한다 ---- */
.gy-cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; max-width: 560px; }
.gy-cal .hd {
  text-align: center; font-size: 11px; font-weight: 600; color: var(--gy-text-muted); padding: 2px 0;
}
.gy-cal .day {
  position: relative; display: block; min-height: 46px; padding: 4px 6px; cursor: pointer;
  border: 1px solid var(--gy-border); border-radius: var(--gy-radius-md);
  background: var(--gy-surface); font-size: 12px; user-select: none;
}
.gy-cal .day.empty { border: 0; background: none; cursor: default; }
.gy-cal .day:hover { border-color: var(--gy-primary); }
.gy-cal .day input { position: absolute; opacity: 0; pointer-events: none; }
/* 고른 날은 한눈에 — 여러 개 고르는 화면이라 선택 표시가 약하면 실수한다 */
.gy-cal .day:has(input:checked) {
  background: var(--gy-primary); border-color: var(--gy-primary); color: var(--gy-on-primary);
}
.gy-cal .day:has(input:checked) .n { color: var(--gy-on-primary); }
.gy-cal .day .n { font-weight: 600; color: var(--gy-text-secondary); }
.gy-cal .day.off { background: var(--gy-surface-sunken); }
.gy-cal .day.hol { background: var(--yellow-50); border-color: var(--yellow-600); }
.gy-cal .day .tag {
  display: block; font-size: 10px; color: var(--yellow-800); line-height: 1.2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.gy-cal .day .cnt {
  position: absolute; top: 3px; right: 4px; font-size: 10px; font-weight: 700;
  color: var(--gy-on-primary); background: var(--gy-primary);
  border-radius: var(--gy-radius-full); padding: 0 5px;
}
.gy-cal .day:has(input:checked) .cnt { background: var(--gy-surface); color: var(--gy-primary); }

/* 비교표 합계 줄 — 스크롤해도 바닥에 붙어 있어야 열끼리 총합을 바로 견준다 */
table.gy-table tfoot td {
  position: sticky; bottom: 0; background: var(--gy-surface-sunken);
  font-weight: 700; border-top: 1px solid var(--gy-border-strong); border-bottom: 0;
}

/* 개인별 상세 일별 표 — 기존 화면의 `_hld` 와 같은 색 (결근/휴가/공휴일) */
table.gy-table tr.absent td { background: var(--magenta-100); color: var(--magenta-800); }
table.gy-table tr.leave td { background: var(--blue-50); color: var(--blue-800); }
table.gy-table tr.holiday td { background: var(--yellow-100); color: var(--yellow-800); }
table.gy-table tr.absent:hover td { background: var(--magenta-50); }
table.gy-table tr.leave:hover td { background: var(--blue-50); }
table.gy-table tr.holiday:hover td { background: var(--yellow-50); }
/* 소정 부족분이 난 날 · 사람이 손으로 고친 날 — 위 상태 색과 **축이 다르다**.
   배경을 또 칠하면 반차인데 부족도 난 날에서 하나가 가려지므로, 줄 왼쪽 띠와
   날짜 옆 연필로 말한다. `tr td.hi` 로 적는 이유는 상태 규칙(tr.leave td)과 특정도를
   맞추기 위해서다 — 짧게 쓰면 휴가·공휴일 줄에서 부족 칸 색이 덮인다. */
table.gy-table tr.short td:first-child { border-left: 3px solid var(--magenta-800); }
table.gy-table tr.edited td:first-child::after { content: " ✎"; color: var(--gy-text-muted); }
table.gy-table tr td.hi { background: var(--magenta-100); color: var(--magenta-800); font-weight: 600; }

/* 합계 행 — 맨 윗줄에 서서 스크롤과 무관하게 그 달 총량을 보여 준다.
   머리글 바로 밑에 붙어 있어야 하므로 sticky 위치는 th 높이만큼 내린다. */
table.gy-table tr.total td {
  position: sticky; top: 33px; z-index: 1;
  background: var(--gy-surface-sunken); font-weight: 700;
  border-bottom: 1px solid var(--gy-border-strong);
}
table.gy-table tr.total:hover td { background: var(--gy-surface-sunken); }

/* 보정을 펼치면 머리글이 **두 줄**이 된다(보정 층 구분 줄 + 열 이름 줄). sticky 위치가
   한 줄 기준으로 남아 있으면 둘째 머리글 줄이 첫 줄 위에 겹치고, 합계 줄은 머리글
   (z-index 2)에 덮여 사라진 것처럼 보인다 — 사용자가 본 어긋남이 이것이다. 한 줄 높이
   33px 만큼 각각 내린다. */
table.gy-table.adjusting thead tr:nth-child(2) th { top: 33px; }
table.gy-table.adjusting tr.total td { top: 66px; }

/* ---- 폼 (계산 기준) ---- */
.gy-sub { font-size: 15px; font-weight: 600; margin: 22px 0 10px; color: var(--gy-text-secondary); }
.gy-form {
  max-width: 720px; margin-top: 20px; background: var(--gy-surface);
  border: 1px solid var(--gy-border); border-radius: var(--gy-radius-lg);
  padding: 8px 22px 20px; box-shadow: var(--gy-shadow-sm);
}
.gy-row { display: flex; gap: 20px; padding: 14px 0; border-bottom: 1px solid var(--gy-border); }
.gy-row:last-of-type { border-bottom: 0; }
.gy-row > label { flex: 0 0 240px; font-size: 13px; font-weight: 600; padding-top: 7px; }
.gy-row .input { flex: 1; min-width: 0; }
.gy-row select, .gy-row input[type="number"] {
  font-family: inherit; font-size: 13px; padding: 7px 10px; min-width: 240px;
  border: 1px solid var(--gy-border-strong); border-radius: var(--gy-radius-md);
  background: var(--gy-surface); color: var(--gy-text);
}
.gy-row input[type="checkbox"] { width: 17px; height: 17px; margin-top: 6px; accent-color: var(--gy-primary); }
.gy-row select:focus, .gy-row input:focus { outline: none; border-color: var(--gy-focus); box-shadow: var(--gy-focus-ring); }
.gy-row .hint { font-size: 12px; color: var(--gy-text-muted); margin-top: 6px; }

.gy-actions { display: flex; align-items: center; gap: 10px; padding-top: 18px; }
.gy-btn.ghost {
  background: var(--gy-surface); color: var(--gy-primary);
  border: 1px solid var(--gy-border-strong); box-shadow: none;
}
.gy-btn.ghost:hover { background: var(--gy-primary-subtle); color: var(--gy-primary); }

/* 편집 표 (스케줄 마스터) — 읽기 표와 달리 셀 안에 입력이 들어간다 */
table.gy-edit { max-width: 560px; margin: 4px 0 0; }
table.gy-edit td { white-space: normal; vertical-align: middle; }
/* 타입을 하나씩 나열하면 새 입력 타입이 들어올 때마다 **조용히 스타일 밖으로 떨어진다** —
   실제로 `type="date"` 가 그렇게 빠져 브라우저 기본 폰트·높이로 그려졌고, 그 높이 차이가
   행 정렬을 무너뜨렸다. 그래서 "체크박스와 hidden 을 뺀 나머지 전부"로 뒤집어 적는다. */
table.gy-edit select,
table.gy-edit input:not([type="checkbox"]):not([type="hidden"]) {
  font-family: inherit; font-size: 13px; padding: 6px 9px;
  border: 1px solid var(--gy-border-strong); border-radius: var(--gy-radius-md);
  background: var(--gy-surface); color: var(--gy-text); min-width: 130px;
}
/* 날짜는 숫자가 세로로 줄 맞아야 훑어볼 수 있다. 브라우저 기본은 비례 폭이라 자릿수가 흔들린다. */
table.gy-edit input[type="date"] { font-variant-numeric: tabular-nums; }
table.gy-edit select:focus, table.gy-edit input:focus {
  outline: none; border-color: var(--gy-focus); box-shadow: var(--gy-focus-ring);
}
table.gy-edit input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--gy-danger); }
/* 줄무늬 + 호버는 **행을 잃지 않기 위한 장치**다. 수백 행에서 왼쪽 이름과 오른쪽 제거
   체크박스를 눈으로 이으려면 보조선이 있어야 한다. 예전엔 호버가 꺼져 있었다. */
table.gy-edit tbody tr:nth-child(even) td { background: var(--gy-bg); }
table.gy-edit tbody tr:hover td { background: var(--gy-primary-subtle); }
table.gy-edit tr.add td,
table.gy-edit tr.add:hover td { background: var(--gy-surface-sunken); }

.gy-flash { padding: 11px 16px; border-radius: var(--gy-radius-md); font-size: 13px; margin: 14px 0; }
.gy-flash.ok { background: var(--green-50); color: var(--green-800); border: 1px solid var(--green-600); }
.gy-flash.warn { background: var(--yellow-50); color: var(--yellow-800); border: 1px solid var(--yellow-600); }

/* ---- 문서 (계산 규칙) ---- */
.gy-note {
  background: var(--yellow-50); border: 1px solid var(--yellow-100);
  border-left: 3px solid var(--yellow-600); border-radius: var(--gy-radius-md);
  padding: 10px 14px; margin: 14px 0 24px; font-size: 13px; color: var(--gy-text-secondary);
}
.gy-note p { margin: 0; }

.gy-doc {
  display: block; max-width: 860px;
  background: var(--gy-surface); border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-lg); padding: 28px 32px; box-shadow: var(--gy-shadow-sm);
}
.gy-doc h3 {
  font-size: 18px; font-weight: 700; letter-spacing: -.01em;
  margin: 32px 0 12px; padding-bottom: 8px; border-bottom: 1px solid var(--gy-border);
  color: var(--gy-primary);
}
.gy-doc h3:first-child { margin-top: 0; }
.gy-doc h4 { font-size: 15px; font-weight: 600; margin: 24px 0 8px; }
.gy-doc h5 { font-size: 14px; font-weight: 600; margin: 20px 0 8px; color: var(--gy-text-secondary); }
.gy-doc p { margin: 10px 0; }
.gy-doc ul, .gy-doc ol { margin: 10px 0; padding-left: 22px; }
.gy-doc li { margin: 5px 0; }
.gy-doc code {
  font-family: var(--gy-font-mono); font-size: .9em; padding: 1px 5px;
  background: var(--gy-surface-sunken); border-radius: var(--gy-radius-sm); color: var(--gy-primary);
}
.gy-doc hr { border: 0; border-top: 1px solid var(--gy-border); margin: 28px 0; }
.gy-doc blockquote {
  margin: 14px 0; padding: 10px 16px; border-left: 3px solid var(--gy-accent);
  background: var(--blue-50); border-radius: 0 var(--gy-radius-md) var(--gy-radius-md) 0;
  color: var(--gy-text-secondary);
}
.gy-doc blockquote p { margin: 6px 0; }
.gy-doc table {
  border-collapse: collapse; width: 100%; margin: 14px 0; font-size: 13px;
  display: block; overflow-x: auto;
}
.gy-doc th {
  background: var(--gy-surface-sunken); color: var(--gy-text-secondary);
  font-size: 11px; font-weight: 600; letter-spacing: .02em; text-align: left;
  padding: 9px 12px; border: 1px solid var(--gy-border);
}
.gy-doc td { padding: 8px 12px; border: 1px solid var(--gy-border); vertical-align: top; }
.gy-doc tbody tr:nth-child(even) td { background: var(--gy-bg); }

.gy-empty {
  background: var(--gy-surface); border: 1px dashed var(--gy-border-strong);
  border-radius: var(--gy-radius-lg); padding: 32px; text-align: center; color: var(--gy-text-muted);
}
/* 표 안에 들어갈 때 — td 셀렉터가 특이도로 이겨서 padding 이 죽으므로 div 로 감싸 쓴다 */
table.gy-table td > .gy-empty { margin: 8px 0; white-space: normal; }

/* ══ 휴가 관리 ══════════════════════════════════════════════════════════════
   담당자가 매달 쓰는 화면. 휴가 한 건이 빠지면 그 사람은 하루 8시간 + 그 주 주휴
   8시간까지 결근으로 깎이고, 이 파일은 git 이력이 없어 지운 것을 되살릴 수 없다.
   그래서 여기 규칙은 대부분 **실수를 눈에 보이게 만드는** 쪽에 쓰인다. */

/* 제목 아래 여백을 안내문에 의존하지 않는다 — 안내문을 지우면 제목이 필터에 달라붙었다 */
h1 small { font-size: 14px; font-weight: 600; letter-spacing: 0; color: var(--gy-text-secondary); margin-left: 8px; }
.gy-main > h1 { margin-bottom: 16px; }

/* 대상자는 범위 필터(센터·월)가 아니라 작업 대상이라 한 급 위로 보여야 한다 */
.gy-field.subject select { min-width: 200px; font-weight: 600; }
.gy-field.subject.on select {
  border-color: var(--gy-primary); background: var(--gy-primary-subtle); color: var(--gy-on-primary-subtle);
}

/* 지운 안내문 자리 — 산문 대신 '지금 누구를 작업 중인가'가 들어온다 */
.gy-subject {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 12px 0 18px; padding: 10px 14px; font-size: 13px;
  background: var(--gy-primary-subtle); border: 1px solid var(--navy-200);
  border-left: 3px solid var(--gy-primary); border-radius: var(--gy-radius-md);
}
.gy-subject b { font-size: 15px; letter-spacing: -.01em; }
.gy-subject .sum { color: var(--gy-text-secondary); }
.gy-subject .off { margin-left: auto; font-size: 12px; }
.gy-subject.none {
  background: var(--gy-surface-sunken); border-color: var(--gy-border);
  border-left-color: var(--gy-border-strong); color: var(--gy-text-secondary);
}

/* 달력이 라벨 240px 에 눌려 셀이 56px 였다 — 라벨을 위로 올려 선언한 폭을 실제로 쓴다 */
.gy-row.wide { display: block; }
.gy-row.wide > label { display: block; flex: none; padding: 0 0 8px; }
.gy-row.wide .input { width: 100%; }

/* 대상자를 안 고르면 달력은 아무 의미가 없다. 예전엔 여기서 고른 날짜가 조용히 버려졌다 */
.gy-cal.idle { opacity: .45; pointer-events: none; filter: grayscale(1); }

/* 공휴일 표시를 배경이 아니라 **아래 띠**로도 남긴다 — 선택(네이비)에 덮이지 않게 */
.gy-cal .day.hol::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: var(--yellow-600); border-radius: 0 0 var(--gy-radius-md) var(--gy-radius-md);
}
/* 선택하면 .n 만 흰색이 되어 공휴일 이름이 네이비 위 노랑으로 죽었다 */
.gy-cal .day:has(input:checked) .tag { color: var(--gy-on-primary); opacity: .85; }

/* 이 사람이 이미 등록된 날 — 채움이 아니라 안쪽 링이라 선택 표시와 겹쳐도 둘 다 읽힌다 */
.gy-cal .day.mine { box-shadow: inset 0 0 0 2px var(--gy-primary); }
.gy-cal .day.mine:has(input:checked) { box-shadow: inset 0 0 0 2px var(--gy-on-primary); }
.gy-cal .day .cnt.mine { background: var(--gy-surface); color: var(--gy-primary); }
.gy-cal .day.mine:has(input:checked) .cnt.mine { background: var(--gy-on-primary); color: var(--gy-primary); }

/* 여러 날을 고르는 화면이라 호버가 테두리 1px 색만 바뀌면 약하다 */
.gy-cal .day:hover { border-color: var(--gy-primary); background: var(--gy-primary-subtle); }
.gy-cal .day.hol:hover { background: var(--yellow-100); }
/* 체크박스가 opacity:0 이라 키보드로는 아무 표시 없이 지나갔다 */
.gy-cal .day:has(input:focus-visible) { border-color: var(--gy-focus); box-shadow: var(--gy-focus-ring); }

/* 추가 폼과 목록을 **나란히** 둔다. 위아래로 쌓으면 달력에서 날짜를 찍은 뒤 목록을 보려고
   수백 행을 지나야 하고, 그 사이 무엇을 골랐는지 화면에서 사라진다. */
.gy-leave { display: grid; grid-template-columns: minmax(0, 620px) minmax(0, 1fr); gap: 20px; align-items: start; }
.gy-leave > .gy-form { max-width: none; margin-top: 0; }
.gy-leave > .gy-list { min-width: 0; }
.gy-leave > .gy-list > .gy-sub { margin-top: 6px; }
/* 왼쪽이 짧고 오른쪽이 길다 — 목록만 따로 스크롤해야 둘이 함께 보인다.
   스크롤 조상이 생기므로 sticky th 는 이 상자 기준으로 붙는다(의도한 동작). */
.gy-listwrap { max-height: calc(100vh - 220px); }
@media (max-width: 1200px) {
  .gy-leave { grid-template-columns: 1fr; }
  .gy-listwrap { max-height: 60vh; }
}

table.gy-edit.gy-table { max-width: none; width: 100%; }
table.gy-edit td { padding: 5px 10px; }
table.gy-edit td .wd { color: var(--gy-text-secondary); margin-left: 6px; font-size: 12px; }
/* 공백 개수만 다른 이름(`김려원 (야간)` 5칸/6칸)이 HTML 접힘 때문에 똑같이 보였다 */
table.gy-edit td.nm { white-space: pre; }
table.gy-edit td.rm, table.gy-edit th.rm { padding: 0; text-align: center; width: 64px; }
table.gy-edit td.rm label {
  display: flex; align-items: center; justify-content: center; min-height: 32px; cursor: pointer;
}
/* 고르는 즉시 행이 통째로 붉어진다 — 저장 버튼까지 가는 동안 계속 보이게 */
table.gy-edit tr:has(input[name="drop"]:checked) td {
  background: var(--magenta-100); color: var(--magenta-800); text-decoration: line-through;
}
table.gy-edit tr:has(input[name="drop"]:checked) td:first-child { box-shadow: inset 3px 0 0 var(--gy-danger); }

/* 저장은 손이 닿는 곳에 — 수백 행 아래의 버튼은 사실상 없는 버튼이다 */
.gy-savebar {
  position: sticky; bottom: 0; z-index: 3;
  display: flex; align-items: center; gap: 12px; margin-top: 16px;
  padding: 12px 16px; background: var(--gy-surface);
  border-top: 1px solid var(--gy-border-strong); box-shadow: 0 -4px 12px rgba(16, 22, 31, .10);
}
.gy-savebar .pending { font-size: 12px; color: var(--gy-text-secondary); }
.gy-savebar .pending.drop { color: var(--gy-danger-text); font-weight: 600; }

/* ---- 직원 마스터 표 — 수백 행을 훑으며 고치는 표 ---- */
/* `table-layout: fixed` 가 이 표의 핵심이다. 자동 배분에 맡기면 열 너비가 **내용 따라**
   정해져 머리글과 아래 입력칸이 어긋나고, 필터를 바꿀 때마다 열이 움직인다. */
table.gy-emp { table-layout: fixed; width: 100%; }
/* 남는 폭은 **메모 열**(c-note)이 먹는다 — 폭을 안 준 열이 하나는 있어야 나머지가 고정폭을
   지키고, 그 자리는 가장 길게 쓰는 칸에 주는 것이 맞다. 예전엔 이름 열이 그 역할이라
   화면이 넓을수록 이름만 늘어났다. 그래서 c-note 에는 width 를 주지 않는다. */
table.gy-emp th.c-name,   table.gy-emp td.c-name   { width: 150px; }
table.gy-emp th.c-center, table.gy-emp td.c-center { width: 170px; }
table.gy-emp th.c-status, table.gy-emp td.c-status { width: 100px; }
/* 근무유형은 상태(`재직`·`퇴직` 두 글자)와 폭을 나눠 쓸 수 없다 — `단시간(3)`·`단시간(4)`
   가 들어가야 하고, 100px 에서는 `단시간(` 까지만 보여 3인지 4인지 구별이 안 됐다
   (담당자 지적 2026-08-20). 실측으로 폭을 잡았다: 13px Pretendard 에서 글자 49.6px +
   select 좌우 패딩 18px + 펼침 화살표 약 20px + 칸 패딩 20px ≈ 108px. 128px 로 둔 것은
   Pretendard 가 안 깔린 PC 의 대체 글꼴이 더 넓기 때문이다 — 그 여유가 없으면 담당자
   화면에서만 다시 잘린다. 남는 폭은 여전히 c-note 가 먹는다. */
table.gy-emp th.c-worktype, table.gy-emp td.c-worktype { width: 128px; }
table.gy-emp th.c-kind,   table.gy-emp td.c-kind   { width: 150px; }
table.gy-emp th.c-date,   table.gy-emp td.c-date   { width: 168px; }
table.gy-emp th.c-wage,   table.gy-emp td.c-wage   { width: 110px; }
table.gy-emp th.c-hours,  table.gy-emp td.c-hours  { width: 108px; }
/* 정액수당은 체크박스 하나라 머리글(`정액수당 (만근·근속)`)이 폭을 정한다.
   체크는 가운데로 — 왼쪽에 붙이면 옆 칸 숫자와 한 덩어리로 읽힌다. */
table.gy-emp th.c-flat,   table.gy-emp td.c-flat   { width: 104px; text-align: center; }
/* 연락처. 휴대폰은 `010-1234-5678`(13자) 이 잘리지 않을 만큼만, 이메일은 흔한 길이
   (`hong.gildong@geo-young.com`)가 들어갈 만큼 준다. 둘 다 고정폭이라 남는 폭은 계속
   c-note 가 먹는다 — 위 규약이 그대로 유지된다. */
table.gy-emp th.c-phone,  table.gy-emp td.c-phone  { width: 150px; }
table.gy-emp th.c-email,  table.gy-emp td.c-email  { width: 210px; }
/* 금액·시간은 자릿수가 세로로 맞아야 큰 값·작은 값이 한눈에 갈린다. */
table.gy-emp td.c-wage input,
table.gy-emp td.c-hours input { text-align: right; font-variant-numeric: tabular-nums; }
/* 읽기표의 nowrap 을 gy-edit 가 normal 로 덮어써 긴 센터명이 줄바꿈되고 있었다.
   한 행이 두 줄이 되는 순간 옆 행과 높이가 달라져 표 전체가 어긋나 보인다. */
table.gy-emp td {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  padding: 4px 10px; vertical-align: middle;   /* 앞선 규칙에 기대지 않고 여기서 못 박는다 */
}
/* 이름 칸만 위쪽에 붙던 원인: 휴가 화면이 넣은 `td.nm { white-space: pre }` 가 여기까지
   걸리는데, 이 칸 안에는 숨은 입력이 **줄 바꿔** 들어 있어 빈 줄 두 개가 실제로 생겼다.
   그 규칙은 공백 개수만 다른 이름(`김려원  (야간)`)을 구분하려고 있는 것이라 버리지 않고,
   **이름 글자를 감싼 span 에만** 건다 — 칸 바깥의 줄바꿈은 이제 영향이 없다. */
table.gy-emp td.nm { white-space: nowrap; }
table.gy-emp td.nm > span { white-space: pre; }
/* 컨트롤 높이를 한 값으로 못 박는다 — select 와 input 의 기본 높이가 달라서 생기던 흔들림.
   `:not()` 두 번을 그대로 되받는 이유는 우선순위다. 위 gy-edit 규칙이 `min-width: 130px`
   를 걸어 두는데, 선택자가 그쪽이 더 강하면 100px 짜리 상태 열에서 칸이 넘쳐 다시 어긋난다. */
table.gy-emp select,
table.gy-emp input:not([type="checkbox"]):not([type="hidden"]) {
  width: 100%; min-width: 0; height: 32px; box-sizing: border-box;
}
table.gy-emp td.nm { font-weight: 600; }
/* 미확인 칸은 배경으로 말한다(td.gy-unknown). 안쪽 입력까지 흰색으로 남으면 표시가 죽는다. */
table.gy-emp td.gy-unknown input { background: transparent; border-color: var(--yellow-600); }

/* 저장 결과 — 개수가 아니라 실제로 바뀐 항목을 적는다 */
.gy-changes { margin: 8px 0 0; padding-left: 18px; font-size: 12px; }
.gy-changes li { margin: 2px 0; }
.gy-changes li b { display: inline-block; min-width: 56px; }
.gy-changes .was { color: var(--gy-text-muted); }

/* 확인 화면의 변경 목록 — 자체 스크롤 없이 페이지와 함께 흐른다(항목이 몇 개뿐이다) */
.gy-tablewrap.flow { max-height: none; overflow: visible; }

table.gy-changes-table td.op { font-weight: 600; }
table.gy-changes-table td.nm { white-space: pre; }
table.gy-changes-table tr.drop td { background: var(--magenta-100); color: var(--magenta-800); }
table.gy-changes-table tr.over td { background: var(--yellow-100); color: var(--yellow-800); }

.gy-btn.danger { background: var(--gy-danger); color: var(--gy-on-danger); }
.gy-btn.danger:hover { background: var(--magenta-700); color: var(--gy-on-danger); }

/* ── 근무표 격자 (/roster/) ────────────────────────────────────────────────
   사람 × 날짜라 칸이 수천 개다. 한 칸이 26px 을 넘으면 스마트허브(120명 × 31일)가
   한 화면에 안 들어온다. 그래서 격자는 값만 담고 설명은 범례가 한 번만 한다.

   색은 코드 이름이 아니라 **번호**로 받는다(services.roster_view 가 정한다) —
   한글·숫자로 시작하는 코드명을 CSS 식별자로 쓰지 않기 위해서다. */
.gy-roster-scroll { overflow: auto; max-height: 70vh; border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-md); background: var(--gy-surface); }
.gy-roster { border-collapse: separate; border-spacing: 0;
  font-family: var(--gy-font-mono); font-size: 11px; }
.gy-roster th, .gy-roster td { border-right: 1px solid var(--gy-border);
  border-bottom: 1px solid var(--gy-border); }
.gy-roster thead th { position: sticky; top: 0; z-index: 3; background: var(--gy-surface);
  color: var(--gy-text-muted); font-weight: 600; min-width: 40px; padding: 4px 0 5px;
  text-align: center; font-variant-numeric: tabular-nums; cursor: pointer; user-select: none; }
.gy-roster thead th span { display: block; font-size: 9px; color: var(--gy-text-muted); font-weight: 400; }
.gy-roster thead th.wk { background: var(--gy-surface-sunken); }
.gy-roster thead th.hol { background: var(--magenta-50); color: var(--magenta-800); }
/* 파트 열 — 격자는 원래 조별로 묶여 서 있는데 어느 묶음인지 말해 주는 칸이 없었다
   (담당자 요청 2026-08-20). 이름 열보다 **왼쪽**에 고정하므로 left 값이 한 벌로 움직인다:
   파트가 0, 이름이 파트 폭(92px)이다. 둘 중 하나만 고치면 가로 스크롤에서 겹친다.
   92px 근거는 실측이다 — 가장 긴 값 `단시간 3시간` 이 들어가는 폭. */
.gy-roster .partcol { position: sticky; left: 0; z-index: 4; background: var(--gy-surface);
  width: 92px; min-width: 92px; max-width: 92px; text-align: left; padding: 4px 8px;
  font-family: var(--gy-font-sans); font-size: 11px; color: var(--gy-text-secondary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gy-roster thead th.partcol { z-index: 5; color: var(--gy-text-muted); text-align: left; }
.gy-roster th.who { position: sticky; left: 92px; z-index: 4; background: var(--gy-surface);
  min-width: 108px; text-align: left; padding: 4px 10px; font-family: var(--gy-font-sans);
  border-right: 1px solid var(--gy-border-strong); }
.gy-roster thead th.who { z-index: 5; }
.gy-roster tbody th.who { font-weight: 500; color: var(--gy-text); cursor: pointer; white-space: nowrap; }
.gy-roster tbody th.who .part { margin-left: 5px; font-size: 9px; color: var(--yellow-800);
  background: var(--yellow-100); border-radius: var(--gy-radius-sm); padding: 1px 4px; }
/* 퇴사일을 모르는 퇴직자 — 월중(노랑)보다 세게 표시한다. 월중은 "구간이 짧다"지만
   이쪽은 "그 달을 일했는지 아예 모른다"라 성격이 다르다. */
.gy-roster tbody th.who .gone { margin-left: 5px; font-size: 9px; color: var(--magenta-800);
  background: var(--magenta-100); border-radius: var(--gy-radius-sm); padding: 1px 4px; }
.gy-roster tbody tr:hover th.who { background: var(--gy-primary-subtle); }
.gy-roster td.cell { height: 24px; padding: 0; text-align: center; cursor: pointer;
  position: relative; background: var(--gy-surface); }
.gy-roster td.cell span { display: block; line-height: 24px; }
.gy-roster td.cell.wk { background: var(--gy-surface-sunken); }
.gy-roster td.cell.hol { background: var(--magenta-50); }
.gy-roster td.cell.sel { box-shadow: inset 0 0 0 2px var(--gy-focus); }
/* 기본값과 다른 칸 — 미리 채워 두는 화면이라 "무엇을 손댔나"가 안 보이면 못 믿는다. */
.gy-roster td.cell.edited { font-weight: 700; }
.gy-roster td.cell.edited::after { content: ""; position: absolute; top: 2px; right: 2px;
  width: 4px; height: 4px; border-radius: 50%; background: var(--magenta-600); }
/* 시각이 조정된 칸 — 멀리서도 보이도록 왼쪽에 띠를 세운다. */
.gy-roster td.cell.adj { box-shadow: inset 3px 0 0 var(--yellow-600); }
.gy-roster td.cell.adj.sel { box-shadow: inset 3px 0 0 var(--yellow-600), inset 0 0 0 2px var(--gy-focus); }
/* 재직 기간 밖 — 월중 입·퇴사자의 빈 구간. 실수로 채워 넣는 것을 막는다. */
.gy-roster td.cell.locked { cursor: not-allowed; color: var(--gy-text-muted);
  background: repeating-linear-gradient(45deg, var(--gy-surface-sunken),
    var(--gy-surface-sunken) 3px, var(--gy-surface) 3px, var(--gy-surface) 6px); }
.gy-roster td.sum, .gy-roster th.sum { text-align: right; padding: 0 8px; min-width: 44px;
  color: var(--gy-text-secondary); font-variant-numeric: tabular-nums; background: var(--gy-surface); }

/* ---- 근무유형 색 (담당자 요청 2026-08-20) ----
   풀타임 / 단시간(4시간) / 단시간(3시간) / 야간조 넷을 가른다. 판별은 서버가 한다
   (`services.work_kind`) — 야간조는 자정을 넘는 조이고, 나머지는 소정시간으로 갈린다.

   예전에는 조가 정의된 순서대로 색이 붙어(`i % 6`) 뜻이 없었고, 코드가 7개인
   스마트허브에서는 한 바퀴 돌아 단시간 3시간과 풀타임이 같은 색이 됐다.

   격자 칸·범례·팝오버 버튼·이름 열 조 배지가 **모두 이 색을 쓴다** — 한 화면에서
   같은 사실을 두 색으로 말하면 담당자가 어느 쪽을 믿어야 할지 모른다. */
/* **background-color 로 좁힌 것이 중요하다.** 단축 `background:` 은 background-image 까지
   초기화해서, !important 와 함께 쓰면 십자 하이라이트(background-image 로 얹는다)를
   지워 버린다. 색은 색만, 얹는 것은 얹는 것만 건드린다. */
.kind-full  { background-color: var(--navy-50) !important; color: var(--navy-800); }
.kind-pt4   { background-color: var(--blue-50) !important; color: var(--blue-800); }
.kind-pt3   { background-color: var(--green-50) !important; color: var(--green-800); }
/* 야간조만 진하다. 소정 8시간이라 길이로는 풀타임과 구분되지 않아 색이 유일한 단서이고,
   실제로도 한 센터(스마트허브 21:00–06:00)뿐이라 진해도 화면이 어수선해지지 않는다. */
.kind-night { background-color: var(--navy-500) !important; color: #fff; }
.kind-off   { color: var(--gy-text-muted); }
/* 직원 마스터의 근무유형 칸(select). 격자와 같은 색을 써서 두 화면이 같은 말을 한다.
   야간조는 여기 없다 — 그것은 근무유형이 아니라 근무조라 이 칸으로 표현되지 않는다. */
table.gy-emp select.wt { font-weight: 600; }

.gy-legend { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0; }
.gy-legend .lg { display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  border: 1px solid var(--gy-border); border-radius: var(--gy-radius-md);
  background: var(--gy-surface); padding: 3px 9px 3px 4px; font-size: 12px; color: var(--gy-text-secondary); }
.gy-legend .lg:hover { border-color: var(--gy-border-strong); }
.gy-legend .lg[aria-pressed="true"] { border-color: var(--gy-primary); background: var(--gy-primary-subtle); }
.gy-legend .k { font-family: var(--gy-font-mono); font-weight: 700; font-size: 11px;
  min-width: 30px; height: 20px; display: grid; place-items: center; border-radius: var(--gy-radius-sm); }
.gy-legend .t { font-family: var(--gy-font-mono); font-size: 11px; color: var(--gy-text-muted); }

/* 편집 팝오버 — 「한 시간 땡긴다」가 두 뜻이라 문장으로 묻는다. */
.gy-pop { position: absolute; z-index: 60; width: 320px; padding: 13px;
  background: var(--gy-surface); border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-lg); box-shadow: var(--gy-shadow-lg);
  /* 화면보다 길어지면 창 안에서 스크롤한다 — 뒤집기(JS)로도 안 들어가는 좁은
     화면에서 「확인」 버튼이 잘려 나가는 것을 막는 마지막 안전망이다. */
  max-height: calc(100vh - 24px); overflow-y: auto; }
.gy-pop .head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 9px; }
.gy-pop .head span { font-family: var(--gy-font-mono); font-size: 12px; color: var(--gy-text-muted); }
.gy-pop .lab { margin: 0 0 6px; font-size: 10px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--gy-text-muted); }
/* 근무조는 센터에 따라 11개까지 된다. 5열이면 3줄이 되어 팝오버가 격자를 크게
   가리므로 6열로 좁힌다 — 코드는 길어야 4글자(0600)라 폭이 남는다. */
.gy-pop .codes { display: grid; grid-template-columns: repeat(6, 1fr); gap: 4px; margin-bottom: 10px; }
.gy-pop .codes button { font-family: var(--gy-font-mono); font-size: 11px; font-weight: 700;
  padding: 6px 0; cursor: pointer; border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-sm); }
.gy-pop .codes button[aria-pressed="true"] { border-color: var(--gy-primary); box-shadow: inset 0 0 0 1px var(--gy-primary); }
.gy-pop .presets { display: grid; gap: 4px; margin-bottom: 11px; }
.gy-pop .presets button { font: inherit; font-size: 12px; text-align: left; padding: 6px 9px;
  cursor: pointer; border: 1px solid var(--gy-border); border-radius: var(--gy-radius-md);
  background: var(--gy-surface); color: var(--gy-text-secondary);
  display: flex; justify-content: space-between; gap: 8px; align-items: center; }
.gy-pop .presets button:hover { border-color: var(--gy-primary); background: var(--gy-primary-subtle); }
.gy-pop .presets span { font-family: var(--gy-font-mono); font-size: 10px; color: var(--gy-text-muted); }
.gy-pop .step { display: grid; grid-template-columns: 1fr auto auto auto; gap: 6px;
  align-items: center; margin-bottom: 5px; font-size: 12px; }
.gy-pop .step button { font-family: var(--gy-font-mono); width: 26px; height: 24px; cursor: pointer;
  border: 1px solid var(--gy-border); border-radius: var(--gy-radius-sm); background: var(--gy-surface); }
.gy-pop .step b { font-family: var(--gy-font-mono); min-width: 48px; text-align: center; }
.gy-pop .out { border-top: 1px solid var(--gy-border); margin-top: 10px; padding-top: 9px;
  font-family: var(--gy-font-mono); font-size: 12px; color: var(--gy-text-secondary); }
.gy-pop .out .ot { color: var(--magenta-700); font-style: normal; font-weight: 700; }
.gy-pop .foot { display: flex; gap: 6px; margin-top: 11px; }
.gy-pop .foot .gy-btn { flex: 1; text-align: center; }
/* 닫기(✕) — 여러 칸을 고른 채 창을 열면 "적용하지 않고 빠져나오는 길"이 눈에 보여야
   한다. Esc 만으로 두면 그 길이 있는 줄 모르는 담당자가 아무 코드나 눌러 확정한다. */
.gy-pop .head { justify-content: space-between; }
.gy-pop .head .x { margin-left: auto; width: 22px; height: 22px; line-height: 1; cursor: pointer;
  border: 1px solid transparent; border-radius: var(--gy-radius-sm);
  background: none; color: var(--gy-text-muted); font-size: 12px; }
.gy-pop .head .x:hover { border-color: var(--gy-border); color: var(--gy-text); }
/* 고른 칸의 근무조가 서로 다를 때. 첫 칸 값을 대표로 보여주면 전체가 그 값인 줄
   오해하므로, 숫자 대신 문장으로 말한다. */
.gy-pop .out .mix { color: var(--yellow-800); font-style: normal; font-weight: 700; }
/* 시각이 칸마다 다를 때의 한 줄 설명. ± 가 "무엇을 기준으로" 움직이는지 말해 주지
   않으면 담당자가 절대값이 박히는 줄 알고 누르기를 피한다. */
.gy-pop .hintline { margin: 2px 0 0; font-size: 11px; color: var(--gy-text-muted); }
.gy-pop .step b { font-size: 12px; }

/* ---- 격자 편집 보조 표시 (선택 수 · 붓) ---- */
/* 선택 칩은 「수정 N칸」과 다른 사실이라 색도 다르다 — 파란색은 "지금 고른 것",
   노란색 계열 붓 배지는 "누르면 바로 칠해지는 상태"다. */
.gy-chip.sel { background: var(--gy-primary-subtle); color: var(--gy-primary); }
.gy-chip.brush { background: var(--yellow-100); color: var(--yellow-800); }
.gy-chip.brush span { font-weight: 500; opacity: .75; margin-left: 4px; }
/* 붓이 켜져 있으면 커서가 먼저 말해 준다. 격자 밖에서는 평소 커서를 유지해야
   저장·되돌리기 버튼을 누르기 어색하지 않다. */
body.gy-brushing .gy-roster td.cell { cursor: cell; }
/* 여러 칸을 고르는 일이 흔해졌으니(이름·날짜 클릭) 선택은 테두리만으로 부족하다 —
   22칸이 한꺼번에 켜질 때 배경까지 옅게 깔려야 범위가 한눈에 잡힌다. */
.gy-roster td.cell.sel { background-color: var(--gy-primary-subtle) !important; }
/* 십자 하이라이트가 쓰는 얹는 색. **background-image** 로 얹는 이유: background-color 는
   근무유형 색(.kind-*)이 이미 쓰고 있어, 덮으면 "이 칸이 단시간인가"를 잃는다.
   두 겹이라 색은 살고 십자는 보인다. 야간조(진한 남색) 위에서도 읽히도록 옅게 준다. */
:root { --gy-hl: rgba(1, 59, 122, .07); }
/* 십자 규칙 — 예전에는 JS 가 칸을 지날 때마다 스타일시트를 통째로 새로 썼다. 그러면
   브라우저가 그 규칙에 걸릴 수 있는 문서 전체를 다시 따져, 큰 센터에서 마우스가
   밀린다(실측: 스마트허브 137행 12.9ms/칸 → 클래스 방식 7.0ms, roster.js 주석 참조).
   지금은 JS 가 행 <tr> 과 그 열 칸에만 `.hl` 을 걸고 색은 여기 고정 규칙이 맡는다.
   선택자 자릿수(#rosterTable …)를 예전 그대로 둔 것은 의도다 — .locked 줄무늬를
   덮던 우선순위까지 같아야 겉모습이 한 픽셀도 안 바뀐다. */
#rosterTable tbody tr.hl > td.cell,
#rosterTable td.cell.hl { background-image: linear-gradient(var(--gy-hl), var(--gy-hl)); }
#rosterTable tbody tr.hl > th.who,
#rosterTable tbody tr.hl > td.partcol,
#rosterTable thead th.hl { background: var(--gy-primary-subtle);
  color: var(--gy-primary); font-weight: 700; }
.gy-roster td.cell { background-repeat: no-repeat; }
.gy-roster tbody th.who:hover { background: var(--gy-primary-subtle); }
.gy-roster thead th[data-day]:hover { background: var(--gy-primary-subtle); cursor: pointer; }
/* 보조 안내 — 색·굵기 범례처럼 "알아 두면 좋은" 줄은 본 안내보다 한 단 낮춘다. */
.gy-note.muted { color: var(--gy-text-muted); font-size: 12px; }
/* 툴바 오른쪽 버튼 묶음 — 자리를 고정한다. 개별 버튼에 걸린 margin-left:auto 는
   여럿일 때 남는 폭을 나눠 가져, 옆 칩이 숨었다 나타날 때마다 버튼이 움직인다. */
.gy-toolbar .acts { margin-left: auto; display: flex; gap: 8px; align-items: center; }
.gy-toolbar .acts .gy-btn { margin-left: 0; }
.gy-btn:disabled { opacity: .45; cursor: not-allowed; }

/* 접이식 — 자주 안 고치는 층(기본값 규칙)을 격자 아래로 내려 두되 닫아 둔다. */
.gy-fold { margin-top: 28px; border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-md); background: var(--gy-surface); }
.gy-fold > summary { cursor: pointer; padding: 11px 14px; font-weight: 600;
  color: var(--gy-text-secondary); list-style: none; user-select: none; }
.gy-fold > summary::-webkit-details-marker { display: none; }
.gy-fold > summary::before { content: "▸ "; color: var(--gy-text-muted); }
.gy-fold[open] > summary::before { content: "▾ "; }
.gy-fold > summary:hover { color: var(--gy-text); background: var(--gy-surface-sunken); }
.gy-fold > summary:focus-visible { outline: none; box-shadow: var(--gy-focus-ring); }
.gy-fold > *:not(summary) { padding: 0 14px; }
.gy-fold > *:last-child { padding-bottom: 14px; }
/* 이름 옆 조 배지 — 칸과 같은 근무유형 색을 입어 "이 사람이 어떤 근무인가"가
   이름 줄에서 바로 읽힌다. 회색 글씨였을 때는 조 이름만 있고 뜻이 없었다. */
.gy-roster tbody th.who .shift { float: right; font-family: var(--gy-font-mono);
  font-size: 9.5px; padding: 1px 5px; margin-top: 1px; border-radius: var(--gy-radius-sm);
  color: var(--gy-text-muted); }
.gy-roster tbody th.who .shift.kind-full  { color: var(--navy-800); }
.gy-roster tbody th.who .shift.kind-pt4   { color: var(--blue-800); }
.gy-roster tbody th.who .shift.kind-pt3   { color: var(--green-800); }
.gy-roster tbody th.who .shift.kind-night { color: #fff; }

/* 금액 탭 — 아직 계산하지 않는 수당 칸. 0원과 눈으로 구별되어야 한다:
   0 은 "그 항목이 없다", 이 칸은 "아직 안 만들었다"로 뜻이 다르다. */
.gy-table td.pending { color: var(--gy-text-muted); background: var(--gy-surface-sunken); }

/* 급여계산 탭 — 시간 칸. 금액과 색을 달리해 「시간 → 금액」 짝이 눈에 들어오게 한다.
   같은 줄에 숫자가 15개라 구분이 없으면 어느 것이 원이고 어느 것이 시간인지 안 보인다. */
.gy-table th.hour, .gy-table td.hour { color: var(--gy-text-secondary);
  background: color-mix(in srgb, var(--gy-surface-sunken) 55%, transparent); }

/* ---- 계정 (로그인 · 사용자 관리) ----
 *
 * 로그인·비밀번호 화면은 **eQuality 시스템의 중앙 카드형 로그인을 그대로 옮긴 것**이다
 * (equality/apps/web/src/pages/login/, 2026-08-14 사용자 지시). 두 제품이 같은 --gy-*
 * 토큰을 쓰고 있어 값을 바꿀 것이 없었고, 브랜드 글자만 지오영으로 갈았다.
 *
 * 원본이 React CSS Module 이라 클래스 이름만 이 파일의 규약(gy-*)에 맞췄다.
 * 규격은 그쪽 주석에 적힌 DESIGN.md v2.0 §4·§5·§6 을 그대로 따른다:
 *   카드 padding 32 / radius-lg / hairline 경계 · 그림자 없음
 *   폼 필드 세로 간격 20 · 레이블-인풋 6 · 인풋 높이 36 · 로그인 버튼 40
 *
 * 한 곳만 바꿨다: 원본의 `min-height: 100%` 는 html·body 에 height 100% 가
 * 걸려 있어야 도는데 이 제품 body 에는 그게 없다. 100vh 로 둔다.
 */
.gy-auth { min-height: 100vh; display: grid; place-items: center; padding: 24px; background: var(--gy-bg); }
.gy-auth-stack { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 400px; }

/* 카드는 떠 있는 요소(드롭다운·모달)가 아니라 같은 층의 표면이라 그림자를 두르지 않는다. */
.gy-auth-card {
  padding: 32px; border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-lg); background: var(--gy-surface);
}
.gy-auth-brand { margin-bottom: 24px; text-align: center; }
.gy-auth-brand h1 { font-size: 24px; line-height: 32px; font-weight: 700;
  letter-spacing: -.02em; color: var(--gy-text); }
.gy-auth-brand p { margin: 4px 0 0; font-size: 13px; line-height: 20px; color: var(--gy-text-secondary); }
.gy-auth-org { margin: 0; text-align: center; font-size: 11px; font-weight: 600;
  letter-spacing: .05em; color: var(--gy-text-muted); }

.gy-auth-form { display: flex; flex-direction: column; gap: 20px; }
.gy-auth-form .field { display: flex; flex-direction: column; gap: 6px; }
.gy-auth-form label { font-size: 12px; font-weight: 600; line-height: 18px; color: var(--gy-text-secondary); }
.gy-auth-form .required { margin-left: 2px; color: var(--gy-danger); }
.gy-auth-form input {
  width: 100%; height: 36px; padding: 0 12px; font-family: inherit; font-size: 14px;
  border: 1px solid var(--gy-border-strong); border-radius: var(--gy-radius-md);
  background: var(--gy-surface); color: var(--gy-text);
  transition: border-color 150ms ease-out, box-shadow 150ms ease-out;
}
.gy-auth-form input::placeholder { color: var(--gy-text-muted); }
/* 인풋은 테두리 자체가 포커스를 표현한다 — outline 을 겹치면 테두리가 두 겹으로 보인다. */
.gy-auth-form input:focus-visible { outline: none; border-color: var(--gy-focus); box-shadow: var(--gy-focus-ring); }
/* 로그인 버튼은 이 화면의 유일한 primary 라 가로를 꽉 채운다. 링크(<a>)를 버튼으로
   쓰는 자리도 있어(초기화 완료 → 로그인) 같은 규칙을 함께 건다 — <a> 는 높이를
   line-height 로 잡아야 글자가 가운데 선다. */
.gy-auth-form button, .gy-auth-form a.gy-btn {
  width: 100%; height: 40px; font-size: 14px; padding: 0;
  text-align: center; line-height: 40px;
  /* 원본 버튼에는 그림자가 없다 — 이 화면에서 떠 있는 것은 아무것도 없다.
     포커스 링은 .gy-btn:focus-visible 이 특이도로 이겨 그대로 살아 있다. */
  box-shadow: none;
}

/* 오류 알림 — 좌측 3px 띠 + 옅은 배경 + 아이콘. 색만으로 말하지 않는다(문구가 함께 온다).
   이 제품 토큰에는 --gy-danger-bg 가 없어 원시 스케일 magenta-50 을 쓴다. */
.gy-auth-alert {
  display: flex; align-items: flex-start; gap: 8px; margin: 0; padding: 12px 16px;
  border-left: 3px solid var(--gy-danger); border-radius: var(--gy-radius-xl);
  background: var(--magenta-50); color: var(--gy-danger-text); font-size: 13px; line-height: 20px;
}
/* 16px 아이콘을 20px 줄 상자 가운데에 맞춘다((20-16)/2). align-items:center 를 쓰면
   문구가 두 줄로 넘어갈 때 아이콘이 블록 중앙으로 내려가 첫 줄과 어긋난다. */
.gy-auth-alert svg { flex-shrink: 0; margin-top: 2px; }

.gy-auth-note { margin: 0; text-align: center; font-size: 12px; line-height: 18px; color: var(--gy-text-muted); }

@media (prefers-reduced-motion: reduce) {
  .gy-auth-form input { transition: none; }
}

/* 센터 체크박스 묶음 — 7개가 한 줄로 흐르면 어디까지가 한 항목인지 안 보인다. */
/* 여러 개 고르는 체크박스 묶음(계정 추가의 센터). **격자로 세운다** — flex 로 흘리면
   이름 길이가 제각각이라(`김포병원` vs `인천(반품)센터`) 줄마다 칸이 어긋나 눈이 세로로
   못 훑는다. 칸 폭을 고정하면 위아래가 맞아 8개를 한눈에 센다.
   라벨은 `inline-flex` 로 **체크박스와 이름을 한 덩이**로 묶는다 — 둘 사이(6px)가 항목
   사이(격자 칸)보다 좁아야 어느 이름이 어느 칸인지 눈이 바로 잇는다. */
.gy-checks { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 8px 12px; }
.gy-checks label { display: inline-flex; align-items: center; gap: 6px; margin-bottom: 0;
  font-size: 13px; font-weight: 500; color: var(--gy-text); white-space: nowrap;
  cursor: pointer; }
.gy-checks input[type="checkbox"] { width: 16px; height: 16px; margin: 0; flex: none;
  accent-color: var(--gy-primary); }

/* 초기화 링크 — 통째로 복사해야 하므로 줄바꿈을 허용하고 골라잡기 쉽게 둔다. */
.gy-linkbox {
  margin-top: 8px; padding: 9px 11px; background: var(--gy-surface); border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-md); font-family: var(--gy-font-mono); font-size: 12px;
  word-break: break-all; user-select: all;
}

/* ══ 확인 안내 (/checklist/) ═════════════════════════════════════════════════
   담당자에게 묻는 요점 목록. 예전에는 항목마다 상태를 고르는 폼이었는데 아무도
   안 눌러(37개 중 눌린 것 1개) 읽는 화면으로 바꿨다(2026-08-14).
   요점과 예시의 **무게를 다르게** 둔다 — 같은 크기로 두면 예시가 요점처럼 읽힌다. */
.gy-points { list-style: none; margin: 18px 0 0; padding: 0; counter-reset: gy-point; }
/* 항목 사이를 한 줄만큼 띄운다(2026-08-14 사용자 지시) — 붙여 두면 어디까지가 한
   항목인지 안 보인다. 요점이 두 줄로 넘어가는 항목이 있어 더 그렇다. */
.gy-points > li {
  position: relative; padding: 14px 16px 14px 38px; margin-bottom: 20px;
  counter-increment: gy-point;
  background: var(--gy-surface); border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-md);
}
/* 번호는 ::before 로 — <ol> 기본 마커는 여러 줄일 때 첫 줄에만 붙어 들쭉날쭉하다.
   불릿(•)이 아니라 번호인 이유(2026-08-18 사용자 지시): 항목이 열 개를 넘어가면
   "몇 번째 것" 으로 주고받게 되는데, 불릿으로는 그 말을 할 수가 없다.
   두 자리 수(10·11·12)가 들어가도록 왼쪽 여백을 30px → 38px 로 넓혔다. */
.gy-points > li::before {
  content: counter(gy-point) "."; position: absolute; left: 12px; top: 13px;
  color: var(--gy-text-muted); font-weight: 700; font-variant-numeric: tabular-nums;
}
.gy-points .point { font-size: 14px; line-height: 1.55; }
.gy-points .example {
  margin-top: 7px; padding-top: 7px; border-top: 1px dashed var(--gy-border);
  font-family: var(--gy-font-mono); font-size: 12px; line-height: 1.6;
  color: var(--gy-text-muted);
}
/* 센터 딱지 — 요점 앞에 붙어 "어느 센터 이야기인가"를 먼저 말한다. 본문보다 옅게
   두어 읽는 흐름을 끊지 않는다(2026-08-18). ASCII 표를 넣었다가 화면에서 어그러져
   걷어낸 자리이기도 하다 — 예시는 평범한 문장으로 쓴다. */
.gy-points .point .center { color: var(--gy-primary); font-weight: 600; }
/* 근거 링크 — 예시보다 한 단 더 낮은 층이라 더 작고 옅게. 여러 개가 한 줄에 붙으므로
   줄바꿈을 허용한다(법령 제목이 길다). */
.gy-points .sources { margin-top: 6px; font-size: 11px; line-height: 1.6; color: var(--gy-text-muted); }
.gy-points .sources a { color: var(--gy-primary); text-decoration: underline; }
