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

/* Pretendard 를 우리 서버가 직접 낸다 — CDN 을 안 쓰는 이유는 센터 PC 가 사내망에서
   외부 CDN 을 막으면 그 PC 만 대체 글꼴로 보이기 때문이다. 글꼴이 PC 마다 다르면
   표 너비가 사람마다 달라지고, 그 차이는 「내 화면에서는 안 그런데요」로 돌아온다.

   🔑 url() 은 이 파일 기준 상대경로여야 한다 — 운영은 ManifestStaticFilesStorage 라
   collectstatic 이 이 참조를 따라가 해시 이름으로 바꾼다. 참조가 안 맞으면 수집이
   통째로 실패하고 전 화면이 500 이 된다(vendored sourcemap 때 겪은 그 사고).

   font-display:swap — 2MB 라 첫 방문에 늦게 올 수 있는데, swap 이면 글자가 먼저
   대체 글꼴로 보이고 나중에 바뀐다. 빈 화면을 기다리는 것보다 낫다. */
@font-face {
  font-family: "Pretendard Variable";
  font-weight: 45 920;               /* 가변 글꼴 — 400·600·700 을 한 파일이 낸다 */
  font-style: normal;
  font-display: swap;
  src: url("fonts/PretendardVariable.302ec55f5b43.woff2") format("woff2-variations");
}

: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);

  /* 상태색 — DESIGN.md §2.3. 이 셋이 없어서 화면 코드가 원시 스케일(--yellow-800 등)을
     직접 쓰고 있었다. 값은 규격서와 같으므로 색은 한 픽셀도 안 바뀐다. */
  --gy-info:var(--blue-700);      --gy-info-bg:var(--blue-50);      --gy-info-text:var(--blue-800);
  --gy-success:var(--green-600);  --gy-success-bg:var(--green-50);  --gy-success-text:var(--green-800);
  --gy-warning:var(--yellow-600); --gy-warning-bg:var(--yellow-50); --gy-warning-text:var(--yellow-800);
  --gy-danger-bg:var(--magenta-50);

  /* 규격서에 없어서 여기서 더한 셋 — 더한 이유를 남긴다(DESIGN.md §8: 필요하면 개정).
     ① -text-strong: 규격서가 danger 만 두 값을 말한다. §9.1 표는 --gy-danger-text 를
        magenta-700 으로, §6 배지 규칙은 「{색}-800」으로 정한다. 이 저장소는 배지·행
        강조에 -800, 안내문 글자에 -700 을 이미 **따로** 쓰고 있어 둘 다 필요하다.
        (info·success·warning 은 두 조항이 같은 -800 이라 하나면 된다.)
     ② -bg-strong: 표에서 「이 줄을 봐야 한다」를 한 단계 더 진하게 칠하는 자리.
        규격서의 -bg(50)는 배지·안내문용이라 행 강조에는 묻힌다. */
  --gy-danger-text-strong:var(--magenta-800);
  --gy-warning-bg-strong:var(--yellow-100);
  --gy-danger-bg-strong:var(--magenta-100);

  /* 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;
}
/* 활성 메뉴는 **pill** 이다(DESIGN.md §4) — 좌우 8px 을 띄워 벽에 안 붙인다.
   텍스트 시작점은 8+12=20px 로, 띠 방식이던 때(padding 20px)와 같은 자리다 —
   모양만 바뀌고 글자는 안 움직인다.

   켜는 값은 `nav_active`(web/report/context.py). 이 규칙은 그 값이 생기기 전까지
   **한 번도 켜진 적이 없었다** — 붙이는 쪽이 없어서 CSS 만 남아 있었다. */
.gy-nav a {
  display: block; margin: 0 8px; padding: 8px 12px;
  border-radius: var(--gy-radius-md);
  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); }

/* ---- 필터 탭 ---- */
/* 0 인 탭도 남긴다 — 「잔여 음수가 지금은 없다」는 것 자체가 정보이고, 센터를 바꿀
   때마다 탭이 사라지면 줄이 움직여 누르려던 것을 놓친다(툴바 버튼이 안 눌리던 일과
   같은 부류 — 2026-08-20). 0 은 색만 죽인다. */
.gy-tabs { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: -6px 0 16px; }
.gy-tabs a {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 6px 12px; border-radius: var(--gy-radius-full);
  border: 1px solid var(--gy-border); background: var(--gy-surface);
  color: var(--gy-text-secondary); font-size: 12px; font-weight: 600;
}
.gy-tabs a:hover { background: var(--gy-surface-sunken); color: var(--gy-text); text-decoration: none; }
.gy-tabs a:focus-visible { outline: none; box-shadow: var(--gy-focus-ring); }
.gy-tabs a.on { background: var(--gy-primary); border-color: var(--gy-primary); color: var(--gy-on-primary); }
.gy-tabs a.zero { color: var(--gy-text-muted); }
.gy-tabs a.zero.on { color: var(--gy-on-primary); }
/* 숫자는 `tabular-nums` 로 — 안 하면 33 과 8 의 폭이 달라 탭 줄이 눈에 들썩인다. */
.gy-tabs a .n { font-size: 11px; font-weight: 500; opacity: .72; font-variant-numeric: tabular-nums; }
.gy-tabs .hint { font-size: 11px; color: var(--gy-text-muted); margin-left: 2px; }

/* ---- 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; }
/* 단위(`/8` · `일` · `%`)는 숫자보다 작게 — 크기가 같으면 어느 쪽이 값인지 흐려진다. */
.gy-kpi .value .unit { font-size: 13px; font-weight: 600; color: var(--gy-text-muted); margin-left: 1px; }
/* 색은 **문제가 있을 때만** 든다. 0 인 칸까지 칠하면 평소에도 화면이 빨갛고, 그러면
   진짜 문제가 묻힌다(근태 도착 현황 화면 전체가 지키는 규약). */
.gy-kpi .value.bad { color: var(--gy-danger-text-strong); }
/* 「잴 수 없음」의 줄표. 숫자와 같은 굵기·색으로 두면 그 자체가 어떤 수치로 읽힌다. */
.gy-kpi .value .none { color: var(--gy-text-muted); font-weight: 400; }
.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(--gy-warning-bg-strong); color: var(--gy-warning-text); }
.gy-chip.danger { background: var(--gy-danger-bg-strong); color: var(--gy-danger-text-strong); }
.gy-chip.ok { background: var(--gy-success-bg); color: var(--gy-success-text); }
.gy-chip.leave { background: var(--gy-info-bg); color: var(--gy-info-text); }
/* 배경색이 아니라 띠·기호로 말하는 표시 — 칩도 색을 안 쓴다. 색 칩과 나란히 두면
   "이것도 배경색이구나" 로 읽혀, 실제 표에서 그 색을 찾다 못 찾는다. */
.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(--gy-danger-text-strong); margin-right: 3px; }

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

/* ---- 표 ---- */
.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(--gy-warning-bg); }
table.gy-table tbody tr:hover td.gy-unknown { background: var(--gy-warning-bg-strong); }
table.gy-table tbody tr:hover td { background: var(--gy-primary-subtle); }
table.gy-table tr.warn td { background: var(--gy-warning-bg-strong); color: var(--gy-warning-text); }
table.gy-table tr.danger td { background: var(--gy-danger-bg-strong); color: var(--gy-danger-text-strong); }
table.gy-table tr.warn:hover td { background: var(--gy-warning-bg); }
table.gy-table tr.danger:hover td { background: var(--gy-danger-bg); }
/* 직원 마스터에 없는 사람 — 배경이 아니라 **왼쪽 띠**로 표시한다. 이 사람들은 거의 늘
   '부분근무'(warn)로도 잡혀 있어서, 배경을 또 물들이면 둘 중 하나가 가려진다.
   축이 다른 신호이므로 표시도 축이 달라야 한다. */
table.gy-table tr.unknown td:first-child { border-left: 3px solid var(--gy-danger-text-strong); }

/* ---- 휴가 달력 — 날짜를 여러 개 골라 한 번에 등록한다 ---- */
.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(--gy-warning-bg); border-color: var(--gy-warning); }
.gy-cal .day .tag {
  display: block; font-size: 10px; color: var(--gy-warning-text); 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(--gy-danger-bg-strong); color: var(--gy-danger-text-strong); }
table.gy-table tr.leave td { background: var(--gy-info-bg); color: var(--gy-info-text); }
table.gy-table tr.holiday td { background: var(--gy-warning-bg-strong); color: var(--gy-warning-text); }
table.gy-table tr.absent:hover td { background: var(--gy-danger-bg); }
table.gy-table tr.leave:hover td { background: var(--gy-info-bg); }
table.gy-table tr.holiday:hover td { background: var(--gy-warning-bg); }
/* 소정 부족분이 난 날 · 사람이 손으로 고친 날 — 위 상태 색과 **축이 다르다**.
   배경을 또 칠하면 반차인데 부족도 난 날에서 하나가 가려지므로, 줄 왼쪽 띠와
   날짜 옆 연필로 말한다. `tr td.hi` 로 적는 이유는 상태 규칙(tr.leave td)과 특정도를
   맞추기 위해서다 — 짧게 쓰면 휴가·공휴일 줄에서 부족 칸 색이 덮인다. */
table.gy-table tr.short td:first-child { border-left: 3px solid var(--gy-danger-text-strong); }
table.gy-table tr.edited td:first-child::after { content: " ✎"; color: var(--gy-text-muted); }
/* 정산창이 들여온 줄 — 상태(결근·휴가·공휴일)와 **축이 다르다**. 배경색을 또 쓰면
   반차인데 이월이기도 한 날에서 하나가 가려지므로, 여기서도 색이 아니라 기호로 말한다
   (부족 띠·수동수정 연필과 같은 규약). 이 달의 기록이 아니라는 뜻이라 글자를 물린다. */
table.gy-table tr.carry td,
table.gy-table tr.assumed td { color: var(--gy-text-muted); }
table.gy-table tr.carry td:first-child::before { content: "↩ "; }
table.gy-table tr.assumed td:first-child::before { content: "≈ "; }
table.gy-table tr td.hi { background: var(--gy-danger-bg-strong); color: var(--gy-danger-text-strong); 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(--gy-success-bg); color: var(--gy-success-text); border: 1px solid var(--gy-success); }
.gy-flash.warn { background: var(--gy-warning-bg); color: var(--gy-warning-text); border: 1px solid var(--gy-warning); }
/* 설명 띠 — 경고가 아니라 «이 화면은 이렇게 읽는다»를 말하는 자리(기간 집계 탭).
   노랑(warn)을 쓰면 진짜 경고가 무디어진다: 한 화면에 노란 띠가 셋이면 담당자는
   셋 다 안 읽는다. 파랑은 이 디자인에서 이미 정보를 뜻한다(휴가 줄·휴가 칩과 같은
   --gy-info-bg/--gy-info-text) — 새 색을 고르지 않았다.

   테두리만 원시 스케일(--blue-400)로 남는다. 규격서의 알림 규칙은 배경(-50)과
   글자(-800)만 정하고 **테두리 단계는 안 정한다** — 없는 토큰을 지어내는 것보다
   여기 한 곳에 이유를 적고 원시값을 두는 편이 낫다(DESIGN.md §8). */
.gy-flash.info { background: var(--gy-info-bg); color: var(--gy-info-text); border: 1px solid var(--blue-400); }
/* 막힘·되돌리기 어려움 — 빨강. 위 info 주석이 말한 문제의 다른 쪽 끝이다: 노랑만
   쓰면 「파일을 못 읽었다」와 「이 사람은 209 정액이 붙는다」가 「폴더에 여러 개를
   올릴 수 있다」와 같은 무게로 보인다. 세 단(빨강·노랑·파랑)으로 눈이 먼저 갈 곳을
   정한다. 색만으로 말하지 않게 문구와 ⚠️ 가 함께 온다. */
.gy-flash.danger { background: var(--gy-danger-bg); color: var(--gy-danger-text-strong); border: 1px solid var(--gy-danger); }
.gy-flash.danger strong { color: var(--gy-danger-text-strong); }

/* ---- 문서 (계산 규칙) ---- */
.gy-note {
  background: var(--gy-warning-bg); border: 1px solid var(--gy-warning-bg-strong);
  border-left: 3px solid var(--gy-warning); 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-flash.info 와 같은 토큰). 경고가
   아닌 글까지 노랑으로 두면 진짜 경고가 묻힌다. */
.gy-note.info {
  background: var(--gy-info-bg); border: 1px solid var(--blue-400);
  border-left: 3px solid var(--gy-info);
}

.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);
}
/* 흐름도·구조도 — `fenced_code` 가 낸 <pre><code>. 칸과 줄바꿈이 곧 그림이라
   `white-space` 를 건드리지 않고, 좁은 화면에서는 가로로 민다(줄바꿈되면 그림이
   깨진다 — 표를 접는 것과 같은 이유). */
.gy-doc pre {
  margin: 14px 0; padding: 12px 14px; overflow-x: auto;
  background: var(--gy-surface-sunken); border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-md); line-height: 1.5;
}
.gy-doc pre code {
  padding: 0; background: none; color: var(--gy-text);
  font-size: 12px; white-space: pre;
}
.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(--gy-info-bg); 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(--gy-warning); 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(--gy-warning-bg-strong); }
/* 체크박스가 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(--gy-danger-bg-strong); color: var(--gy-danger-text-strong); 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; }

/* ---- 휴가 관리 /leave/ — 2026-10-13 개편(기획·디자인 점검) ----
   이 화면 전용(lv-*). gy-cal·gy-kpis·gy-form 은 다른 화면 7~8곳이 같이 써서 건드리지 않는다.
   핵심: 달력이 남는 폭을 다 쓴다(예전 max-width 560px · 칸 80×46 · 1440 화면에서 마지막
   주가 저장 바에 가렸다). 카드 그림자는 DESIGN §5 대로 없다. */
.lv-summary {
  display: flex; align-items: center; gap: 10px 16px; flex-wrap: wrap;
  margin: 12px 0 6px; padding: 8px 12px; font-size: 13px;
  background: var(--gy-surface-sunken); border: 1px solid var(--gy-border);
  border-left: 3px solid var(--gy-border-strong); border-radius: var(--gy-radius-md);
  color: var(--gy-text-secondary);
}
.lv-summary.on { background: var(--gy-primary-subtle); border-left-color: var(--gy-primary); color: var(--gy-text); }
.lv-summary .who b { font-size: 15px; letter-spacing: -.01em; color: var(--gy-text); }
.lv-summary .sum { color: var(--gy-text-secondary); }
.lv-summary .off { font-size: 12px; }
.lv-summary .stats { margin-left: auto; display: flex; gap: 14px; font-size: 12px; color: var(--gy-text-secondary); }
.lv-summary .stat b { font-size: 15px; color: var(--gy-text); font-variant-numeric: tabular-nums; margin-left: 2px; }
.lv-kinds { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0 0 14px; font-size: 12px; }
.lv-chip { padding: 2px 8px; border-radius: var(--gy-radius-full); font-size: 12px; font-weight: 600;
  background: var(--gy-info-bg); color: var(--gy-info-text); }
.lv-chip.unpaid { background: var(--gy-danger-bg-strong); color: var(--gy-danger-text-strong); }
.lv-chip.mark { background: var(--gy-surface); color: var(--gy-text-secondary); border: 1px solid var(--gy-border-strong); }
.lv-pendlink { margin-left: auto; font-weight: 700; color: var(--gy-danger-text-strong); }

.lv-work { display: grid; grid-template-columns: minmax(0, 1fr) clamp(390px, 32%, 480px); gap: 20px; align-items: start; }
.lv-card { min-width: 0; background: var(--gy-surface); border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-lg); padding: 16px 20px 18px; }
.lv-h { margin: 0 0 12px; font-size: 16px; line-height: 24px; font-weight: 600; color: var(--gy-text); }
.lv-h small { margin-left: 6px; font-size: 12px; font-weight: 400; color: var(--gy-text-muted); }

/* 「무엇을 넣는가」는 급여를 바꾸는 선택이다 — 달력 바로 위, 고르는 순간 뜻이 보인다 */
.lv-tool { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.lv-tool label { font-size: 13px; font-weight: 600; }
.lv-tool select { height: 36px; min-width: 160px; padding: 0 10px; font-size: 14px; font-weight: 600;
  border: 1px solid var(--gy-border-strong); border-radius: var(--gy-radius-md); background: var(--gy-surface); }
.lv-tool select:focus { outline: none; border-color: var(--gy-focus); box-shadow: var(--gy-focus-ring); }
.lv-note { font-size: 12px; color: var(--gy-text-secondary); }
.lv-note.unpaid { color: var(--gy-danger-text-strong); font-weight: 600; }
.lv-note.mark { color: var(--gy-text-muted); }

.lv-cal { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; }
.lv-cal .hd { height: 28px; line-height: 28px; text-align: center; font-size: 12px; font-weight: 600;
  color: var(--gy-text-secondary); }
.lv-cal .hd.we { color: var(--gy-text-muted); }
.lv-cal .day {
  position: relative; display: flex; flex-direction: column; gap: 3px; min-width: 0;
  min-height: clamp(64px, 5.2vw, 88px); padding: 6px 8px 8px; cursor: pointer; user-select: none;
  border: 1px solid var(--gy-border); border-radius: var(--gy-radius-md); background: var(--gy-surface);
}
.lv-cal .day.empty { border: 0; background: none; cursor: default; }
.lv-cal .day input { position: absolute; opacity: 0; pointer-events: none; }
.lv-cal .day .top { display: flex; align-items: center; gap: 4px; }
.lv-cal .day .n { font-size: 16px; font-weight: 600; color: var(--gy-text); font-variant-numeric: tabular-nums; }
.lv-cal .day .tick { display: none; font-size: 12px; font-weight: 700; }
.lv-cal .day .cnt { margin-left: auto; font-size: 12px; font-weight: 600; color: var(--gy-text-secondary); }
/* 공휴일 이름은 **줄을 바꿔서라도 다** — 한 줄 말줄임이라 「개천절 대체 …」처럼 잘렸다.
   낱말은 띄어쓰기에서 먼저 끊고(keep-all), 낱말 하나가 칸보다 길 때만 그 안에서 끊는다
   (1280 화면 칸 안 ≈56px 에 「부처님오신날」 ≈72px). */
.lv-cal .day .tag { font-size: 12px; line-height: 1.25; color: var(--gy-warning-text);
  white-space: normal; word-break: keep-all; overflow-wrap: anywhere; }
.lv-cal .day .mix { font-size: 12px; color: var(--gy-text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lv-cal .day .k { align-self: flex-start; max-width: 100%; padding: 1px 6px; border-radius: var(--gy-radius-full);
  font-size: 12px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  background: var(--gy-info-bg); color: var(--gy-info-text); }
.lv-cal .day .k.unpaid { background: var(--gy-danger-bg-strong); color: var(--gy-danger-text-strong); }
.lv-cal .day .k.mark { background: var(--gy-surface); color: var(--gy-text-secondary); border: 1px solid var(--gy-border-strong); }
.lv-cal .day .k.req { background: var(--gy-surface); color: var(--gy-danger-text-strong); border: 1px dashed var(--gy-danger); }
.lv-cal .day .k .short { display: none; }
/* 칸이 좁으면(1280 화면 ≈74px) 칩을 약칭으로 — 「신청 …」처럼 잘리면 무엇을 신청했는지 모른다 */
.lv-cal .day { container-type: inline-size; }
@container (max-width: 76px) {
  .lv-cal .day .k .full { display: none; }
  .lv-cal .day .k .short { display: inline; }
}
/* 상태 — 색만으로 말하지 않는다: 선택 ✓ · 등록 종류 글자 · 신청 점선+글자 · 공휴일 이름 */
.lv-cal .day.off { background: var(--gy-surface-sunken); }
.lv-cal .day.off .n { color: var(--gy-text-secondary); }
.lv-cal .day.hol { background: var(--gy-warning-bg); }
.lv-cal .day.hol::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: var(--gy-warning); border-radius: 0 0 var(--gy-radius-md) var(--gy-radius-md); }
.lv-cal .day.mine { box-shadow: inset 0 0 0 2px var(--gy-primary); }
.lv-cal .day.pend { border-style: dashed; border-color: var(--gy-danger); }
.lv-cal .day:hover { background: var(--gy-primary-subtle); border-color: var(--gy-primary); }
.lv-cal .day:has(input:checked) { background: var(--gy-primary); border-color: var(--gy-primary); }
.lv-cal .day:has(input:checked) .n,
.lv-cal .day:has(input:checked) .tick,
.lv-cal .day:has(input:checked) .tag,
.lv-cal .day:has(input:checked) .mix { color: var(--gy-on-primary); }
.lv-cal .day:has(input:checked) .tick { display: inline; }
.lv-cal .day.mine:has(input:checked) { box-shadow: inset 0 0 0 2px var(--gy-on-primary); }
/* 포커스 링이 「이미 등록」 안쪽 링을 덮지 않게 합성한다 */
.lv-cal .day:has(input:focus-visible) { outline: 2px solid var(--gy-focus); outline-offset: 2px; }
/* 대상자 전 — 흐리게(opacity .45 · 회색조) 하던 것을 없앤다: 정보는 또렷하게, 클릭만 막는다 */
.lv-cal.idle .day { cursor: default; }
.lv-cal.idle .day:hover { background: var(--gy-surface); border-color: var(--gy-border); }
.lv-cal.idle .day.off:hover { background: var(--gy-surface-sunken); }
.lv-cal.idle .day.hol:hover { background: var(--gy-warning-bg); }

.lv-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 12px; font-size: 12px; color: var(--gy-text-secondary); }
.lv-legend .lg::before { content: ""; display: inline-block; width: 12px; height: 12px; margin-right: 5px;
  vertical-align: -2px; border-radius: 3px; border: 1px solid var(--gy-border); background: var(--gy-surface); }
.lv-legend .lg:not(.sel):not(.mine):not(.req):not(.hol):not(.off):not(.nmreq):not(.nmunpaid):not(.nmpaid)::before { display: none; }
.lv-legend .lg.sel::before { background: var(--gy-primary); border-color: var(--gy-primary); }
.lv-legend .lg.mine::before { box-shadow: inset 0 0 0 2px var(--gy-primary); }
.lv-legend .lg.req::before { border: 1px dashed var(--gy-danger); }
.lv-legend .lg.hol::before { background: var(--gy-warning-bg); border-bottom: 3px solid var(--gy-warning); }
.lv-legend .lg.off::before { background: var(--gy-surface-sunken); }
.lv-hols { display: none; }
/* 대상자 전 칸의 이름 — 전원(2026-10-13 사용자 결정). 칩 배경을 빼고 **색 글자**로 촘촘히:
   1440 화면(칸 안 ≈79px)에서 한 줄에 두 명이 든다. 신청은 빨간 점선 밑줄(급여 미반영),
   등록은 종류 색(무급 빨강·유급 파랑·조퇴 회색). 색만으로 말하지 않게 종류는 마우스 말풍선에. */
.lv-cal .day .ppl { display: flex; flex-wrap: wrap; gap: 1px 6px; min-width: 0; }
.lv-cal .day .nm { font-size: 12px; line-height: 17px; font-weight: 700; white-space: nowrap;
  color: var(--gy-info-text); }
.lv-cal .day .nm.unpaid { color: var(--gy-danger-text-strong); }
/* 이름 = 대상자 지름길(2026-10-13) — 글자 색·점선은 위 규칙 그대로, 밑줄은 올렸을 때만. */
.lv-cal .day a.nm { text-decoration: none; cursor: pointer; }
.lv-cal .day a.nm:hover { text-decoration: underline; }
a.lv-who { color: inherit; text-decoration: none; }
a.lv-who:hover { color: var(--gy-primary); text-decoration: underline; }
.lv-cal .day .nm.mark { color: var(--gy-text-secondary); font-weight: 600; }
.lv-cal .day .nm.req { text-decoration: underline dashed var(--gy-danger); text-underline-offset: 3px;
  text-decoration-thickness: 1px; }
.lv-cal .day .k.req.dot { display: none; }
/* 임의등록(2026-10-13) — 본인 신청 전 실무자가 관리용으로 먼저 적어 둔 휴가. 급여는 휴가로
   그대로 세므로 색(유급·무급)은 안 바꾸고 회색 「임」 꼬리표만 단다 — 점선(확정 전 신청)과 겹쳐도 갈린다. */
.lv-provtag, .lv-cal .day .nm.prov::after { display: inline-block; margin-left: 2px; padding: 0 3px;
  font-size: 10px; line-height: 14px; font-weight: 600; font-style: normal; border-radius: 3px;
  background: var(--gy-surface-sunken); color: var(--gy-text-secondary); border: 1px solid var(--gy-border-strong); }
.lv-cal .day .nm.prov::after { content: "임"; }
.lv-prov { display: inline-flex; align-items: center; gap: 4px; font-size: 13px; font-weight: 600; }
.lv-prov small { font-weight: 400; color: var(--gy-text-secondary); }
.lv-prov input:disabled + span { opacity: .5; }
.lv-legend .lg.nmreq::before { height: 0; border: 0; border-bottom: 1px dashed var(--gy-danger); border-radius: 0; background: none; vertical-align: 3px; }
.lv-legend .lg.nmunpaid::before { background: var(--gy-danger-bg-strong); border-color: var(--gy-danger-bg-strong); }
.lv-legend .lg.nmpaid::before { background: var(--gy-info-bg); border-color: var(--gy-info-bg); }

.lv-listwrap { max-height: calc(100vh - 260px); }
/* 「제거」 칸이 가로로 밀려 반쯤 가려졌다(1440 화면 17px 넘침) — 급여를 바꾸는 단추라 늘 보여야 한다 */
.lv-listcard table.gy-edit select { min-width: 96px; }
.lv-listcard table.gy-edit td.nm { white-space: pre-wrap; }
.lv-listcard table.gy-edit th.rm, .lv-listcard table.gy-edit td.rm { width: 52px; }
.lv-listcard table.gy-edit td:first-child { white-space: nowrap; }

.lv-savebar { min-height: 56px; padding: 10px 16px; box-shadow: var(--gy-shadow-md); }
.lv-savebar .gy-btn { min-height: 36px; }

.lv-pend { margin-top: 24px; }
.lv-pend .lv-h { color: var(--gy-danger-text-strong); }
.lv-pend table.gy-table tbody td:first-child { box-shadow: inset 3px 0 0 var(--gy-danger); }
.lv-pend .ap-act { white-space: nowrap; }
.lv-pend .ap-act .gy-btn { min-height: 32px; padding: 0 12px; }
.lv-pend .ap-act .gy-btn + .gy-btn { margin-left: 6px; }
.lv-pend td .wd { margin-left: 6px; font-size: 12px; color: var(--gy-text-secondary); }
.lv-pend .hint { margin-top: 8px; font-size: 12px; color: var(--gy-text-secondary); }
.lv-badge { display: inline-block; padding: 1px 8px; border-radius: var(--gy-radius-full); font-size: 12px; font-weight: 600; }
.lv-badge.warn { background: var(--gy-warning-bg); color: var(--gy-warning-text); }
.lv-badge.info { background: var(--gy-info-bg); color: var(--gy-info-text); }

@media (max-width: 1200px) {
  .lv-work { grid-template-columns: minmax(0, 1fr); }
  .lv-listwrap { max-height: 60vh; }
}
/* 휴대폰 — 칸 ≥44px · 공휴일 이름은 칸 밖 한 줄로 · 종류는 약칭 */
@media (max-width: 760px) {
  /* 왼쪽 메뉴(220px)는 이 화면 범위 밖이라 그대로다(2026-10-13 사용자 결정) — 내용이 그 옆에서
     114px 로 짓눌려 달력 칸이 10px 가 됐다. 차라리 가로로 밀리더라도 칸 ≥44px 를 지킨다. */
  .lv { min-width: 360px; }
  .lv-summary .stats { margin-left: 0; }
  .lv-card { padding: 12px; }
  .lv-cal { gap: 4px; }
  .lv-cal .day { min-height: 48px; padding: 4px; gap: 2px; }
  .lv-cal .day .n { font-size: 14px; }
  .lv-cal .day .tag, .lv-cal .day .mix, .lv-cal .day .cnt, .lv-cal .day .ppl { display: none; }
  .lv-cal .day .k.req.dot { display: inline-block; }
  .lv-cal .day .k { padding: 0 4px; font-size: 11px; }
  .lv-cal .day .k .full { display: none; }
  .lv-cal .day .k .short { display: inline; }
  .lv-cal .day .k.req { font-size: 0; width: 8px; height: 8px; padding: 0; border-radius: 50%; background: var(--gy-danger); }
  .lv-hols { display: flex; flex-wrap: wrap; gap: 4px 10px; margin-top: 8px; font-size: 12px; color: var(--gy-warning-text); }
  .lv-savebar { flex-wrap: wrap; }
  .lv-savebar .gy-btn { min-height: 44px; }
}

/* ---- 직원 마스터 표 — 수백 행을 훑으며 고치는 표 ---- */
/* `table-layout: fixed` 가 이 표의 핵심이다. 자동 배분에 맡기면 열 너비가 **내용 따라**
   정해져 머리글과 아래 입력칸이 어긋나고, 필터를 바꿀 때마다 열이 움직인다. */
/* `min-width` 는 **메모 열이 보이는 유일한 이유**다 (2026-08-27 담당자 지적 「메모 란이
   안 보임」). 폭을 안 준 c-note 는 '남는 자리'를 먹는데, 고정폭 합(2082px)이 표가 앉을
   자리(1920 화면에서도 1642px)보다 커서 남는 자리가 아예 없었다 — 브라우저로 재면 메모
   열이 1280·1440·1728·1920 어느 화면에서도 정확히 0.0px 였다. 그래서 칸을 줄이는 것만
   으로는 안 된다. 아래에서 다섯 무리를 실측 최소치까지 줄여 256px 를 아끼고, 그 자리가
   실제로 생기도록 표 최소 폭을 2108px 로 못 박는다(고정폭 합 1826px + 메모 282px).
   표 폭은 2082→2108px 라 가로 스크롤은 사실상 그대로다. 화면이 2108px 보다 넓으면
   남는 자리도 전부 메모가 먹는다.

   센터·고용형태 열은 값이 두 가지 이상일 때만 나온다(services.employees_view 의
   show_center·show_kind). 그래서 고정폭 합은 1826px 이 최대가 아니다 — 고용형태까지
   나오면 1976px 이 되어 메모가 132px 로 줄어든다. 0 은 아니고, 그 화면에서도 메모는
   보인다. 아래 시험(tests/test_employees_ui.py)이 지키는 선은 200px 다. */
/* 2026-09-12: 사원번호 열(110px)이 붙으면서 2108 → 2218px. **열을 더하면 이 값도
   같이 올려야 한다** — 안 올리면 고정폭 합이 최소 폭을 넘어 남는 폭을 먹는 메모 열이
   0px 이 된다(담당자가 실제로 겪은 증상이고, 아래 c-note 주석이 그 이야기다). */
/* 2026-09-16: 계정 열(120px)이 붙으면서 2218 → 2338px. 위 규약 그대로다. */
table.gy-emp { table-layout: fixed; width: 100%; min-width: 2338px; }
/* 남는 폭은 **메모 열**(c-note)이 먹는다 — 폭을 안 준 열이 하나는 있어야 나머지가 고정폭을
   지키고, 그 자리는 가장 길게 쓰는 칸에 주는 것이 맞다. 예전엔 이름 열이 그 역할이라
   화면이 넓을수록 이름만 늘어났다. 그래서 c-note 에는 width 를 주지 않는다. */
/* 이름·센터는 실측으로 줄였다(2026-08-27, 전 150·170px). 407명 중 가장 긴 이름은
   4글자(`황보수진`)이고 13px 볼드에서 45.0px, 가장 긴 센터명 `인천(반품)센터` 는 75.7px
   다. 칸 좌우 패딩 20px 를 더한 65·96px 이 최소치이고, Pretendard 가 안 깔린 PC 의 대체
   글꼴 여유를 얹어 아래 값으로 둔다. 잘려도 `title` 이 전체를 보여 준다. */
/* 사원번호는 자릿수를 아직 모른다 — 담당자가 채우기 전이라 실측할 값이 없다. 시급 칸
   (110px)과 같은 폭으로 둔다: 13px 에서 숫자 10자리가 66px 이고 입력칸 패딩·칸 패딩을
   더하면 그 언저리다. 값이 들어온 뒤 더 긴 체계면 그때 실측해 고친다. */
table.gy-emp th.c-code,   table.gy-emp td.c-code   { width: 110px; }
table.gy-emp th.c-name,   table.gy-emp td.c-name   { width: 96px; }
table.gy-emp th.c-center, table.gy-emp td.c-center { width: 112px; }
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; }
/* 날짜 칸은 눈대중으로 못 잰다 — 크롬의 `input[type=date]` 는 내부 칸을 shadow DOM 으로
   그려서 잘려도 scrollWidth 가 안 늘어난다. 그래서 화면을 찍어 잉크 픽셀을 셌다: 칸
   128px 부터 연도 끝자리가 잘리기 시작했고(영어 표기 `08/26/2026`), 한국어 크롬이 쓰는
   `2026. 08. 26.` 가 10.8px 더 넓어 최소치는 139px 다. 대체 글꼴 여유를 얹어 152px
   (2026-08-27, 전 168px). 머리글 `근속 기산일 (근속수당)` 94.0px 도 한 줄에 들어간다. */
table.gy-emp th.c-date,   table.gy-emp td.c-date   { width: 152px; }
table.gy-emp th.c-wage,   table.gy-emp td.c-wage   { width: 110px; }
/* 시간 두 칸의 폭을 정하던 것은 값(`104.3`)이 아니라 머리글이었다 — `일소정시간
   (근무유형이 정함)` 이 117.9px 였다. 그 괄호를 머리글에서 떼어 `title` 로 옮기면서
   (2026-08-27, 담당자 요청) `월기준시간` 48.7px 가 폭을 정하게 됐다: 패딩 20px 를 더한
   69px 가 최소치다. 84px 로 둔다(전 108px). */
table.gy-emp th.c-hours,  table.gy-emp td.c-hours  { width: 84px; }
/* 기산일 두 칸은 `c-date` 를 쓴다 — 지오영입사일·퇴사일과 같은 날짜 칸이라 폭도 같다
   (2026-08-26, 일수 한 칸이 날짜 두 칸으로 갈렸다). 옛 `c-prev` 열은 함께 걷었다. */
/* 만근·근속은 각각 체크박스 하나라 머리글(`만근수당`/`근속수당`)이 폭을 정한다.
   체크는 가운데로 — 왼쪽에 붙이면 옆 칸 숫자와 한 덩어리로 읽힌다. 한 열이 두 열로
   갈렸으므로(2026-08-24) 폭을 줄여 표 전체 폭을 지킨다. 머리글 `만근수당` 은 11px
   Pretendard 에서 39.0px 라 패딩 20px 를 더한 59px 가 최소치다. 그런데 한글이 전각(1em)
   인 대체 글꼴이면 4자 × 11px + 자간 0.9 ≈ 44.9px 로 늘고, 그 값은 Malgun Gothic 이 없는
   맥에서 잴 수 없어 **모델일 뿐 실측이 아니다**. 근무유형 열이 같은 이유로 담당자 화면
   에서만 잘린 적이 있으므로(위 주석) 여유를 두어 72px 로 둔다(2026-08-27, 전 88px). */
table.gy-emp th.c-flat,   table.gy-emp td.c-flat   { width: 72px; 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; }
/* 계정 열은 아이디(휴대폰 11자리)와 「발급」 단추 둘 중 하나를 담는다. 아이디가 더 넓다 —
   13px 고정폭 글꼴에서 11자가 약 86px 이고 칸 패딩 20px 를 더해 106px 가 최소치다.
   대체 글꼴 여유를 얹어 120px 로 둔다(근무유형·만근 열과 같은 이유의 여유다). */
table.gy-emp th.c-account, table.gy-emp td.c-account { width: 120px; }
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(--gy-warning); }

/* 저장 결과 — 개수가 아니라 실제로 바뀐 항목을 적는다 */
.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(--gy-danger-bg-strong); color: var(--gy-danger-text-strong); }
table.gy-changes-table tr.over td { background: var(--gy-warning-bg-strong); color: var(--gy-warning-text); }

.gy-btn.danger { background: var(--gy-danger); color: var(--gy-on-danger); }
.gy-btn.danger:hover { background: var(--gy-danger-text); 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(--gy-danger-bg); color: var(--gy-danger-text-strong); }
/* 파트 열 — 격자는 원래 조별로 묶여 서 있는데 어느 묶음인지 말해 주는 칸이 없었다
   (담당자 요청 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(--gy-warning-text);
  background: var(--gy-warning-bg-strong); border-radius: var(--gy-radius-sm); padding: 1px 4px; }
/* 퇴사일을 모르는 퇴직자 — 월중(노랑)보다 세게 표시한다. 월중은 "구간이 짧다"지만
   이쪽은 "그 달을 일했는지 아예 모른다"라 성격이 다르다. */
.gy-roster tbody th.who .gone { margin-left: 5px; font-size: 9px; color: var(--gy-danger-text-strong);
  background: var(--gy-danger-bg-strong); border-radius: var(--gy-radius-sm); padding: 1px 4px; }
/* 근무조가 확인되지 않은 줄 — 격자 밑그림이 곧 급여라 「확인된 값이 아니다」를
   드러낸다. 「퇴사일?」과 같은 세기다. 「파트」 칸에 얹지 않는 이유는 그 칸이 조 배지와
   같은 말을 해야 하기 때문이다(`services.roster_view` 주석). */
.gy-roster tbody th.who .gapbadge { margin-left: 5px; font-size: 9px; color: var(--gy-danger-text-strong);
  background: var(--gy-danger-bg-strong); border-radius: var(--gy-radius-sm); padding: 1px 4px; }
/* 아직 저장 전인 줄의 「파트」 칸. 조 이름을 미리 그리지 않는 이유는 저장 전에는
   배정이 아니기 때문이다 — 화면이 없는 사실을 말하면 안 된다(1단계 경고와 같은 원칙). */
.gy-roster tbody td.partcol.waiting { color: var(--gy-text-muted); font-style: italic; }
.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(--gy-danger-bg); }
.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(--gy-danger); }
/* 시각이 조정된 칸 — 멀리서도 보이도록 왼쪽에 띠를 세운다. */
.gy-roster td.cell.adj { box-shadow: inset 3px 0 0 var(--gy-warning); }
.gy-roster td.cell.adj.sel { box-shadow: inset 3px 0 0 var(--gy-warning), 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); }
/* 그 날 조가 실제와 달라 근무가 깎인 칸 — 급여가 실제로 줄어드는 자리다.
   `edited` 의 오른쪽 점과 겹치지 않게 **왼쪽 아래 삼각**으로 말한다. 배경을 안 칠하는
   것은 근무유형 색(kind-*)이 이미 배경을 쓰기 때문이다(한 칸이 두 색으로 말하면 안 된다). */
.gy-roster td.cell.cut::before { content: ""; position: absolute; left: 0; bottom: 0;
  border-width: 0 0 7px 7px; border-style: solid;
  border-color: transparent transparent var(--gy-danger) transparent; }
/* 확정 전 휴가 신청(신청·승인) — 장부가 아니라 표식이다. 급여에는 안 들어간다.
   `edited` 점(오른쪽 위)·`cut` 삼각(왼쪽 아래)·`adj` 띠(box-shadow)와 안 겹치게
   **점선 외곽선 + 왼쪽 위 글자**로 말한다. 담당자 요청 「미확정은 빨간색」(2026-10-12). */
.gy-roster td.cell.pend { outline: 2px dashed var(--gy-danger); outline-offset: -2px; }
/* 임의등록 꼬리표 — 오른쪽 위(확정 전 신청 표식은 왼쪽 위라 둘이 같이 서도 안 겹친다) */
.gy-roster td.cell .prov-k { position: absolute; top: 0; right: 0; z-index: 1;
  padding: 0 2px; border-radius: 0 0 0 3px; font-size: 9px; line-height: 11px;
  font-style: normal; font-weight: 700;
  background: var(--gy-surface-sunken); color: var(--gy-text-secondary); border: 1px solid var(--gy-border-strong); }
.gy-toolbar .prov-opt { cursor: pointer; display: inline-flex; align-items: center; gap: 4px; }
.gy-roster td.cell .pend-k { position: absolute; top: 0; left: 0; z-index: 1;
  padding: 0 2px; border-radius: 0 0 3px 0; font-size: 9px; line-height: 11px;
  font-style: normal; font-weight: 700;
  background: var(--gy-danger); color: var(--gy-on-danger); }
.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(--gy-primary-subtle) !important; color: var(--gy-primary); }
.kind-pt4   { background-color: var(--gy-info-bg) !important; color: var(--gy-info-text); }
.kind-pt3   { background-color: var(--gy-success-bg) !important; color: var(--gy-success-text); }
/* 일용직은 노랑 — 길이가 아니라 **계약의 종류**가 다르다. 파랑 계열(풀타임·단시간)과
   같은 줄에 서면 「짧은 단시간」으로 읽히는데, 이 사람에겐 소정근로시간 자체가 없다. */
.kind-daily { background-color: var(--gy-warning-bg) !important; color: var(--gy-warning-text); }
/* 야간조만 진하다. 소정 8시간이라 길이로는 풀타임과 구분되지 않아 색이 유일한 단서이고,
   실제로도 한 센터(스마트허브 21:00–06:00)뿐이라 진해도 화면이 어수선해지지 않는다.

   원시 스케일을 쓰는 것이 여기서는 맞다 — 이건 상태가 아니라 **자료를 가르는 색**이고,
   규격서가 원시 스케일 금지의 유일한 예외로 둔 자리다(DESIGN.md §2.6). navy-500 은
   그 예외 팔레트의 6번 색이기도 하다. */
.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; }

/* 급여계산·인별 집계 표의 근무유형 칸 (담당자 요청 2026-09-04). 색은 근무 격자·직원
   기본정보와 **같은 규약**(`.kind-*`)이라 세 화면이 같은 사람을 같은 색으로 부른다.
   칸 전체가 아니라 글자만 감싸는 칩인 이유: 이 표는 줄 배경으로 이미 다른 사실을
   말하고 있어(부분근무 노랑 · 결근 빨강), 칸을 통째로 칠하면 두 사실이 한 자리에서
   싸운다. `!important` 는 `.kind-*` 쪽이 갖고 있어 줄 색을 이긴다. */
.gy-wt { display: inline-block; padding: 2px 8px; border-radius: var(--gy-radius-full);
         font-size: 11px; font-weight: 600; white-space: nowrap; }

.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(--gy-danger-text); 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(--gy-warning-text); 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(--gy-warning-bg-strong); color: var(--gy-warning-text); }
.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(--gy-primary); }
.gy-roster tbody th.who .shift.kind-pt4   { color: var(--gy-info-text); }
.gy-roster tbody th.who .shift.kind-pt3   { color: var(--gy-success-text); }
.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); }

/* 급여계산 탭 — 사유가 붙은 칸(근속수당·만근수당). 점선 밑줄은 「이 금액에 이유가
   적혀 있다」는 신호이고, 이유 자체는 `title` 툴팁에 있다.
   **열을 안 늘리는 대신 이 신호가 필요하다** — 툴팁만 두면 사유가 있다는 사실 자체를
   아무도 모르고, 열을 늘리면 17열 표가 옆으로 밀린다. */
.gy-table td.why { text-decoration: underline dotted;
  text-decoration-color: var(--gy-text-muted); text-underline-offset: 3px; cursor: help; }

/* 그 사유를 **클릭**해서 고정해 보는 말풍선 (`gy_why.js`).
   `.gy-pop`(근무 편집)과 같은 토큰·같은 층(z-index 60)을 쓰지만 **클래스는 따로 둔다** —
   그쪽은 320px 고정 폭에 버튼 격자가 들어 있어, 물려받으면 편집 팝오버를 손볼 때 이
   말풍선이 같이 움직인다. */
.gy-whypop { position: absolute; z-index: 60; max-width: 340px;
  display: flex; align-items: flex-start; gap: 10px; padding: 10px 12px;
  background: var(--gy-surface); border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-lg); box-shadow: var(--gy-shadow-lg);
  font-size: 13px; line-height: 1.55; color: var(--gy-text); }
/* 🔴 `display: flex` 가 브라우저 기본 `[hidden] { display: none }` 을 **덮는다** — 이 한 줄이
   없으면 말풍선이 한 번 뜬 뒤 절대 닫히지 않는다(2026-08-28, Playwright 시험이 잡았다).
   `.gy-pop` 은 `display` 를 안 정해서 이 함정이 없었다. */
.gy-whypop[hidden] { display: none; }
.gy-whypop .body { flex: 1; }
.gy-whypop .close { flex: 0 0 auto; cursor: pointer; font: inherit; font-size: 12px;
  line-height: 1; padding: 3px 5px; color: var(--gy-text-muted);
  background: var(--gy-surface); border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-sm); }
.gy-whypop .close:hover { color: var(--gy-text); border-color: var(--gy-border-strong); }
/* 열린 칸을 세운다 — 말풍선만 뜨면 표가 길 때 어느 줄을 눌렀는지 잃는다. */
.gy-table td.why-open { outline: 2px solid var(--gy-accent); outline-offset: -2px; }

/* ---- 계정 (로그인 · 사용자 관리) ----
 *
 * 로그인·비밀번호 화면은 **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); }
/* 「비밀번호 보기」 눈 — 비밀번호 칸에서는 기기에 따라 한글 입력기가 조합을 끄고,
   본인은 자기가 무엇을 쳤는지 볼 수가 없다(2026-09-17).

   칸 **안**에 앉힌다. 칸을 감싼 relative 상자 위에 절대 배치하고, 입력 글자가 아이콘
   밑으로 흘러 들어가지 않도록 오른쪽 padding 을 아이콘 너비만큼 비워 둔다. */
.gy-auth-pw { position: relative; }
.gy-auth-form .gy-auth-pw input { padding-right: 38px; }
/* 위 `.gy-auth-form button` 이 가로 100%·40px 를 주므로 여기서 전부 되돌린다 —
   안 되돌리면 눈이 칸을 통째로 덮어 글자를 못 친다. */
.gy-auth-form .gy-auth-eye {
  position: absolute; top: 0; right: 0; width: 36px; height: 36px;
  display: flex; align-items: center; justify-content: center;
  padding: 0; line-height: 0; border: 0; border-radius: var(--gy-radius-md);
  background: none; color: var(--gy-text-muted); cursor: pointer;
}
.gy-auth-form .gy-auth-eye:hover { background: none; color: var(--gy-text-secondary); }
.gy-auth-form .gy-auth-eye:focus-visible { outline: none; box-shadow: var(--gy-focus-ring); }
/* 지금 가려져 있으면 눈(누르면 보인다), 드러나 있으면 그어진 눈(누르면 가린다). */
.gy-auth-eye .eye-off { display: none; }
.gy-auth-eye.is-shown .eye-on { display: none; }
.gy-auth-eye.is-shown .eye-off { display: block; }
/* 인풋은 테두리 자체가 포커스를 표현한다 — 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(--gy-danger-bg); 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; }

/* 기산일 계산기 — 저장바 위에 접혀 있다. 표를 흉내 내지만 **입력 폼이 아니다**:
   name 이 없어 저장에 실려 가지 않고, 결과를 사람이 기산일 칸에 옮겨 적는다.
   접어 두는 이유: 재입사자만 쓰는 도구라 늘 펼쳐 두면 저장 버튼이 아래로 밀린다. */
details.gy-calc { margin: 12px 0; }
details.gy-calc > summary { cursor: pointer; padding: 6px 0; font-weight: 600; }
details.gy-calc > summary small { font-weight: 400; opacity: .75; }
.gy-calc-body { padding: 8px 0 4px; }
table.gy-calcrows { width: auto; min-width: 380px; table-layout: auto; }
table.gy-calcrows th.d, table.gy-calcrows td.d {
  text-align: right; font-variant-numeric: tabular-nums; min-width: 88px;
}
table.gy-calcrows input[type="date"] { min-width: 150px; }
/* 기산일 = 지오영입사일 − 합계. 표 아래 한 줄로 붙는다 — 합계를 보고 바로 날짜를
   읽어야 하고, 그 값을 위 기산일 칸에 손으로 옮긴다(계산에는 쓰이지 않는다). */
.gy-calc-base { display: flex; align-items: center; gap: 8px; margin: 8px 0 0; }
.gy-calc-base input[type="date"] { min-width: 150px; }
.gy-calc-base output { font-weight: 600; font-variant-numeric: tabular-nums; }
.gy-calc-note { margin: 6px 0 0; font-size: .92em; opacity: .85; line-height: 1.55; }

/* ---- 근태 마감 (담당자 요청 2026-08-29) ----
 *
 * 마감은 **경고가 아니라 상태**다. 노랑(warn)을 쓰면 진짜 경고와 섞이고(이 화면은
 * 이미 노란 띠가 많다 — 위 .gy-flash.info 주석의 그 문제), 빨강을 쓰면 「잘못됐다」로
 * 읽힌다. 마감은 정상 흐름의 끝이라 둘 다 아니다. 그래서 **회색 + 자물쇠**다:
 * 눈에 띄되 경보가 아닌 자리. 색만으로 말하지 않게 문구와 🔒 가 함께 온다.
 */
.gy-lock {
  padding: 12px 16px; margin: 14px 0; font-size: 13px; line-height: 20px;
  border: 1px solid var(--gy-border-strong); border-left: 3px solid var(--gy-text-secondary);
  border-radius: var(--gy-radius-md);
  background: var(--gy-surface-sunken); color: var(--gy-text-secondary);
}
.gy-lock strong { color: var(--gy-text); }
.gy-lock .when { font-family: var(--gy-font-mono); font-size: 12px; }

/* 마감·해제 이력 — 지우지 않고 쌓기만 하므로 길어질 수 있다. 접어 둔다. */
details.gy-lock-log { margin: 10px 0 18px; }
details.gy-lock-log > summary { cursor: pointer; font-size: 12px; color: var(--gy-text-muted); padding: 4px 0; }
.gy-lock-log ol { margin: 8px 0 0; padding-left: 20px; font-size: 12px; color: var(--gy-text-secondary); }
.gy-lock-log li { margin: 4px 0; }
.gy-lock-log .when { font-family: var(--gy-font-mono); }
.gy-lock-log .why { color: var(--gy-text); }

/* 현황 판 — 센터 × 최근 몇 달. 「어디가 아직 안 냈나 · 어디가 아직 마감 안 됐나」를
   드롭다운을 센터 수만큼 바꾸지 않고 한눈에 본다.
   칸은 **글자와 색을 함께** 쓴다 — 색맹·흑백 인쇄에서 색만으로는 못 읽는다. */
table.gy-board { width: auto; min-width: 360px; table-layout: auto; }
table.gy-board th.who { text-align: left; white-space: nowrap; }
table.gy-board td.cell { text-align: center; white-space: nowrap; font-size: 12px; }
table.gy-board td.cell.closed { background: var(--gy-surface-sunken); color: var(--gy-text-secondary); font-weight: 600; }
table.gy-board td.cell.ok { background: var(--gy-success-bg); color: var(--gy-success-text); }
table.gy-board td.cell.raw { background: var(--gy-warning-bg); color: var(--gy-warning-text); }
table.gy-board td.cell.none { color: var(--gy-text-muted); }
/* 지금 보고 있는 센터·달을 굵은 테두리로 짚어 준다 — 판이 8줄이면 내 줄을 놓친다. */
table.gy-board td.cell.here, table.gy-board th.who.here { box-shadow: inset 0 0 0 2px var(--gy-primary); }
.gy-board-legend { font-size: 12px; color: var(--gy-text-muted); margin: 6px 0 18px; }

/* 접히는 단계 (①② · 지우기) — 미리보기가 뜨면 앞의 두 칸을 접어 미리보기를 위로
   올린다. 미리보기는 이 화면에서 가장 중요한 글인데(그 안에 「209 정액이 붙습니다」
   경고가 있다) 페이지 맨 아래라, 올리고 나면 「화면이 그대로」로 보였다.
   접어도 요점은 summary 가 말한다 — 열어 보지 않고 상태를 안다. */
details.gy-step > summary { cursor: pointer; }
details.gy-step > summary::marker { color: var(--gy-text-muted); }
details.gy-step > summary small { font-weight: 400; font-size: 12px; color: var(--gy-text-muted); }
/* 지우기는 앞으로 가는 길이 아니다 — 맨 아래에서 한 단 더 물러나 있는다. */
details.gy-quiet > summary { color: var(--gy-text-muted); font-size: 13px; }

/* ==========================================================================
   챗봇 — 떠 있는 버튼과 대화 패널 (2026-08-31)

   메뉴가 아니라 화면 위에 떠 있다. 어느 화면에서 보고 있든 그 자리에서 물어볼 수
   있어야 하기 때문이다(사용자 결정).

   🔴 `[hidden]` 규칙을 **맨 아래**에 둔다. `.gy-chat-panel` 에 `display:flex` 를 주면
   `hidden` 의 기본 `display:none` 을 덮어써서 패널이 안 닫힌다 — 2026-08-28 사유
   말풍선이 정확히 그렇게 안 닫혔다(Playwright 가 잡았다).
   ========================================================================== */

.gy-chat-fab {
  position: fixed; right: 24px; bottom: 24px; z-index: 900;
  width: 52px; height: 52px; border-radius: var(--gy-radius-full);
  border: none; cursor: pointer;
  background: var(--gy-primary); color: var(--gy-on-primary);
  font-size: 22px; line-height: 1;
  box-shadow: var(--gy-shadow-lg);
}
.gy-chat-fab:hover { background: var(--gy-primary-hover); }
.gy-chat-fab:active { background: var(--gy-primary-active); }
.gy-chat-fab:focus-visible { outline: none; box-shadow: var(--gy-shadow-lg), var(--gy-focus-ring); }

.gy-chat-panel {
  position: fixed; right: 24px; bottom: 88px; z-index: 901;
  display: flex; flex-direction: column;
  width: 380px; max-width: calc(100vw - 32px);
  height: 540px; max-height: calc(100vh - 130px);
  background: var(--gy-surface); color: var(--gy-text);
  border: 1px solid var(--gy-border); border-radius: var(--gy-radius-lg);
  box-shadow: var(--gy-shadow-lg); overflow: hidden;
}
.gy-chat-panel > header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px; border-bottom: 1px solid var(--gy-border);
  background: var(--gy-surface-sunken);
}
.gy-chat-panel > header .close {
  border: none; background: none; cursor: pointer;
  color: var(--gy-text-muted); font-size: 15px; line-height: 1; padding: 4px 6px;
  border-radius: var(--gy-radius-sm);
}
.gy-chat-panel > header .close:hover { background: var(--gy-border); color: var(--gy-text); }

/* 개인정보 고지 — 쓰는 자리에 적는다. 눈에 띄되 대화를 가리지 않을 만큼만. */
.gy-chat-notice {
  margin: 0; padding: 8px 14px;
  font-size: 11px; line-height: 1.45; color: var(--gy-text-secondary);
  background: var(--gy-primary-subtle); border-bottom: 1px solid var(--gy-border);
}

.gy-chat-log { flex: 1; overflow-y: auto; padding: 14px; }
.gy-chat-log .msg { margin-bottom: 12px; max-width: 88%; }
.gy-chat-log .msg p {
  margin: 0; padding: 9px 12px; border-radius: var(--gy-radius-md);
  white-space: pre-wrap;      /* 마크다운을 안 그리므로 줄바꿈은 CSS 가 살린다 */
  word-break: break-word; font-size: 13px; line-height: 1.55;
}
.gy-chat-log .msg.bot p { background: var(--gy-surface-sunken); }
.gy-chat-log .msg.me { margin-left: auto; }
.gy-chat-log .msg.me p { background: var(--gy-primary); color: var(--gy-on-primary); }
.gy-chat-log .msg.err p { background: var(--gy-danger); color: var(--gy-on-danger); }
.gy-chat-log .msg.waiting p { color: var(--gy-text-muted); }
.gy-chat-log .msg .hint {
  background: none; padding: 4px 12px 0;
  font-size: 11px; color: var(--gy-text-muted);
}

.gy-chat-form {
  display: flex; gap: 8px; align-items: flex-end;
  padding: 10px 12px; border-top: 1px solid var(--gy-border);
  background: var(--gy-surface);
}
.gy-chat-form textarea {
  flex: 1; resize: none; font: inherit; font-size: 13px;
  padding: 8px 10px; border: 1px solid var(--gy-border-strong);
  border-radius: var(--gy-radius-md); background: var(--gy-surface); color: var(--gy-text);
}
.gy-chat-form textarea:focus { outline: none; border-color: var(--gy-focus); box-shadow: var(--gy-focus-ring); }
/* `.gy-btn` 은 링크용이라 `<button>` 에 붙이면 색이 안 온다 — 여기서 채워 준다
   (2026-08-29 「확정」과 「버리기」가 똑같이 회색이던 것과 같은 함정). */
.gy-chat-form button[type=submit] {
  border: none; cursor: pointer; white-space: nowrap;
  padding: 9px 14px; border-radius: var(--gy-radius-md);
  background: var(--gy-primary); color: var(--gy-on-primary); font-size: 13px;
}
.gy-chat-form button[type=submit]:hover:not(:disabled) { background: var(--gy-primary-hover); }
.gy-chat-form button[type=submit]:disabled { background: var(--gy-border-strong); cursor: default; }

/* 좁은 화면에서는 아래쪽 전체를 쓴다 — 380px 패널이 화면 밖으로 밀려나지 않게. */
@media (max-width: 560px) {
  .gy-chat-panel { right: 8px; left: 8px; width: auto; bottom: 80px; }
  .gy-chat-fab { right: 16px; bottom: 16px; }
}

/* 🔴 반드시 맨 마지막 — 위의 `display:flex` 를 이겨야 패널이 닫힌다. */
.gy-chat-panel[hidden] { display: none; }

/* ── 공지사항 — 게시판(목록 · 글 · 쓰기) (2026-09-02 사용자 결정) ────────────────
   한 화면에 「쓰기 폼 + 모든 글 전문」이 있던 것을 셋으로 갈랐다. 본문은 에디터(Quill)가
   만든 HTML 이라, 글 화면은 Quill 없이 그 HTML 을 그린다 — 정렬·들여쓰기·크기 클래스
   (`ql-*`)를 여기서 다시 정의하는 이유다(살균기 `notice_html.py` 가 남기는 것과 같은 셋). */

/* 목록 */
.gy-notice-toolbar { justify-content: space-between; }
.gy-notice-toolbar .hint { font-size: 12px; color: var(--gy-text-muted); }
.gy-notice-toolbar .hint .gy-chip { padding: 2px 7px; font-size: 10px; }
table.gy-notice-list th:first-child, table.gy-notice-list td.idx { width: 56px; }
table.gy-notice-list td.t { white-space: normal; width: 100%; }
table.gy-notice-list td.t a { color: var(--gy-text); font-weight: 600; }
table.gy-notice-list td.t a:hover { color: var(--gy-link); }
table.gy-notice-list td.t .att { margin-left: 8px; font-size: 12px; color: var(--gy-text-muted); }
table.gy-notice-list td.t .gy-chip { margin-left: 8px; padding: 2px 7px; font-size: 10px; vertical-align: 1px; }
table.gy-notice-list td.d { color: var(--gy-text-secondary); font-variant-numeric: tabular-nums; }
table.gy-notice-list td.d .edited { color: var(--gy-text-muted); }

/* 돌아가기 줄 — 제목 위 */
.gy-notice-nav { margin: -4px 0 12px; font-size: 13px; }

/* 글 한 편 — .gy-doc 카드 위에 머리글·본문·첨부·도구를 얹는다 */
.gy-notice-view header { border-bottom: 1px solid var(--gy-border); padding-bottom: 14px; margin-bottom: 20px; }
.gy-notice-view header h1 { font-size: 22px; line-height: 30px; }
.gy-notice-view .meta { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 8px; font-size: 12px; color: var(--gy-text-muted); }
.gy-notice-view .body { line-height: 1.75; font-size: 14px; word-break: break-word; overflow-wrap: anywhere; }
/* Quill 은 빈 줄도 <p><br></p> 로 낸다 — 단락 간격을 넓히면 빈 줄 하나가 두 줄로 보인다 */
.gy-notice-view .body p { margin: 0 0 4px; }
.gy-notice-view .body h1 { font-size: 22px; line-height: 30px; margin: 22px 0 8px; }
.gy-notice-view .body h2 { font-size: 19px; line-height: 27px; margin: 20px 0 8px; }
.gy-notice-view .body h3 { font-size: 16px; margin: 18px 0 6px; padding: 0; border: 0; color: var(--gy-text); }
.gy-notice-view .body h1:first-child, .gy-notice-view .body h2:first-child, .gy-notice-view .body h3:first-child { margin-top: 0; }
.gy-notice-view .body img { max-width: 100%; height: auto; border-radius: var(--gy-radius-sm); }
.gy-notice-view .body ul, .gy-notice-view .body ol { margin: 6px 0; }
.gy-notice-view .body .ql-align-center { text-align: center; }
.gy-notice-view .body .ql-align-right { text-align: right; }
.gy-notice-view .body .ql-align-justify { text-align: justify; }
.gy-notice-view .body .ql-size-small { font-size: .8em; }
.gy-notice-view .body .ql-size-large { font-size: 1.4em; }
.gy-notice-view .body .ql-size-huge { font-size: 2em; }
.gy-notice-view .body .ql-indent-1 { margin-left: 3em; }
.gy-notice-view .body .ql-indent-2 { margin-left: 6em; }
.gy-notice-view .body .ql-indent-3 { margin-left: 9em; }
.gy-notice-view .body .ql-indent-4 { margin-left: 12em; }
.gy-notice-view .body .ql-indent-5 { margin-left: 15em; }
.gy-notice-view .body .ql-indent-6 { margin-left: 18em; }
.gy-notice-view .body .ql-indent-7 { margin-left: 21em; }
.gy-notice-view .body .ql-indent-8 { margin-left: 24em; }
.gy-notice-view .nobody { color: var(--gy-text-muted); margin: 0; }
.gy-notice-view .tools { display: flex; gap: 8px; align-items: center; margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--gy-border); }
.gy-notice-view .tools form { margin: 0; }

/* 첨부 상자 — 글 화면·고치기 화면 공용 */
.gy-attach {
  margin-top: 24px; background: var(--gy-surface-sunken); border: 1px solid var(--gy-border);
  border-radius: var(--gy-radius-md); padding: 12px 16px;
}
.gy-attach h2 { font-size: 13px; font-weight: 600; margin: 0 0 6px; color: var(--gy-text-secondary); }
.gy-attach ul { list-style: none; margin: 0; padding: 0; }
.gy-attach li { display: flex; align-items: center; gap: 8px; padding: 7px 0; border-top: 1px solid var(--gy-border); font-size: 13px; }
.gy-attach li:first-child { border-top: 0; }
.gy-attach .ext {
  display: inline-block; min-width: 40px; text-align: center; margin-right: 6px;
  font-size: 10px; font-weight: 700; letter-spacing: .03em; padding: 2px 5px;
  border-radius: var(--gy-radius-sm); background: var(--gy-primary-subtle); color: var(--gy-on-primary-subtle);
}
.gy-attach .size { color: var(--gy-text-muted); font-size: 12px; }
.gy-attach form { margin: 0 0 0 auto; }
.gy-attach-edit { max-width: 960px; background: var(--gy-surface); }
.gy-btn.small { padding: 4px 10px; font-size: 11px; }

/* 이웃 글 */
.gy-notice-neighbors { max-width: 860px; margin-top: 14px; display: grid; gap: 6px; }
.gy-notice-neighbors a {
  display: flex; gap: 10px; padding: 9px 14px; font-size: 13px; color: var(--gy-text);
  background: var(--gy-surface); border: 1px solid var(--gy-border); border-radius: var(--gy-radius-md);
}
.gy-notice-neighbors a:hover { background: var(--gy-primary-subtle); text-decoration: none; }
.gy-notice-neighbors .k { flex: 0 0 68px; color: var(--gy-text-muted); font-size: 12px; }

/* 쓰기 — Quill snow 테마를 우리 토큰에 맞춘다 */
.gy-notice-form { max-width: 960px; }
.gy-notice-form .gy-row.wide > label { display: block; margin-bottom: 8px; padding-top: 0; }
.gy-notice-form .gy-editor-wrap { min-width: 0; }
.gy-notice-form input[type="text"] {
  width: 100%; font-family: inherit; font-size: 15px; padding: 9px 12px;
  border: 1px solid var(--gy-border-strong); border-radius: var(--gy-radius-md);
  background: var(--gy-surface); color: var(--gy-text);
}
.gy-notice-form input[type="text"]:focus { outline: none; border-color: var(--gy-focus); box-shadow: var(--gy-focus-ring); }
.gy-notice-form .ql-toolbar.ql-snow {
  border: 1px solid var(--gy-border-strong); border-bottom: 0;
  border-radius: var(--gy-radius-md) var(--gy-radius-md) 0 0;
  background: var(--gy-surface-sunken); font-family: var(--gy-font-sans);
  position: sticky; top: 0; z-index: 2;   /* 긴 글에서 아래로 내려가도 도구 줄이 따라온다 */
}
.gy-notice-form .ql-container.ql-snow {
  border: 1px solid var(--gy-border-strong); border-radius: 0 0 var(--gy-radius-md) var(--gy-radius-md);
  background: var(--gy-surface); font-family: var(--gy-font-sans); font-size: 14px;
}
.gy-notice-form .ql-editor { min-height: 380px; line-height: 1.75; }
.gy-notice-form .ql-editor.ql-blank::before { color: var(--gy-text-muted); font-style: normal; }
.gy-notice-form .ql-editor img { max-width: 100%; height: auto; }
.gy-notice-form .ql-snow .ql-picker { font-family: inherit; }
/* 도구 줄 글자를 한글로 — snow 테마는 이 글자들을 CSS content 로 그린다 */
.gy-notice-form .ql-snow .ql-picker.ql-header .ql-picker-label::before,
.gy-notice-form .ql-snow .ql-picker.ql-header .ql-picker-item::before { content: "본문"; }
.gy-notice-form .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
.gy-notice-form .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { content: "제목 1"; }
.gy-notice-form .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.gy-notice-form .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { content: "제목 2"; }
.gy-notice-form .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.gy-notice-form .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { content: "제목 3"; }
.gy-notice-form .ql-snow .ql-picker.ql-size .ql-picker-label::before,
.gy-notice-form .ql-snow .ql-picker.ql-size .ql-picker-item::before { content: "보통"; }
.gy-notice-form .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
.gy-notice-form .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before { content: "작게"; }
.gy-notice-form .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
.gy-notice-form .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before { content: "크게"; }
.gy-notice-form .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
.gy-notice-form .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before { content: "아주 크게"; }
.gy-notice-form .ql-snow .ql-tooltip::before { content: "링크:"; }
.gy-notice-form .ql-snow .ql-tooltip[data-mode="link"]::before { content: "주소 입력:"; }
.gy-notice-form .ql-snow .ql-tooltip a.ql-action::after { content: "고치기"; }
.gy-notice-form .ql-snow .ql-tooltip.ql-editing a.ql-action::after { content: "확인"; }
.gy-notice-form .ql-snow .ql-tooltip a.ql-remove::before { content: "지우기"; }
.gy-notice-form .ql-snow .ql-tooltip { z-index: 3; }

/* ── 마스터에 없어 계산에서 뺀 이름 상자 (2026-09-03 사용자 결정) — 대시보드 모든 탭 위 ── */
.gy-excluded table { margin: 10px 0 6px; border-collapse: collapse; font-size: 12px; background: var(--gy-surface); }
.gy-excluded th, .gy-excluded td { padding: 5px 10px; border: 1px solid var(--gy-border); text-align: left; white-space: nowrap; }
.gy-excluded th { background: var(--gy-surface-sunken); color: var(--gy-text-secondary); font-size: 11px; font-weight: 600; }
.gy-excluded td.num { text-align: right; font-variant-numeric: tabular-nums; }
.gy-excluded .how { margin-top: 6px; font-size: 12px; color: var(--gy-danger-text-strong); }
.gy-excluded .ignored { margin-top: 8px; font-size: 12px; color: var(--gy-text-secondary); }
.gy-ignored { margin: 0 0 14px; font-family: var(--gy-font-sans); }

/* ==========================================================================
   근태 도착 현황 — 센터 × 일자 격자 (`report/intake.html`)

   스케줄 마스터와 같은 격자 모양이라 담당자가 새로 배울 것이 없다(사용자 결정).
   31칸이 가로로 서므로 좁은 화면에서는 **표만** 옆으로 밀린다 — 페이지 전체가
   흔들리면 왼쪽 메뉴까지 따라 움직여 읽기가 어렵다.

   🔴 `table-layout` 을 fixed 로 두지 않는다. 폭을 안 준 열이 있으면 고정폭 합이
   넘칠 때 그 열이 0px 이 되어 통째로 사라진다(이 프로젝트에서 실제로 겪었다).
   ========================================================================== */
.gy-scroll-x { overflow-x: auto; }

/* 🔴 폭은 **옆 표와 같아야 한다**(2026-09-15 사용자 지적). `width:auto` 는 내용만큼만
   차지해 격자가 화면 중간에서 끝나는데, 바로 아래 센터별 상태 표는 `.gy-table` 의
   `width:100%` 라 오른쪽 끝까지 간다 — 같은 화면의 두 표가 다른 자리에서 끝났다.
   `table-layout` 은 여전히 auto 다(위 주석의 0px 사고). 31칸이 화면보다 넓으면
   `width:100%` 는 최소값으로만 작동하고 감싼 카드가 옆으로 민다. */
table.gy-days { width: 100%; table-layout: auto; }
table.gy-days th.who { text-align: left; white-space: nowrap; padding-right: 10px; }
table.gy-days thead th { font-size: 11px; font-weight: 500; text-align: center; padding: 4px 3px; }
table.gy-days thead th.off { color: var(--gy-text-muted); }
/* 머리글 둘째 줄 = 요일 글자. 주말을 흐리게 칠하는 것만으로는 색맹·흑백 인쇄에서
   「어느 칸이 일요일인가」를 못 읽는다 — 칸 기호와 같은 규약이다. */
table.gy-days thead th small { display: block; font-size: 10px; font-weight: 400; opacity: .8; }
table.gy-days td.cell { text-align: center; padding: 3px; font-size: 13px; line-height: 1.2; }

/* 센터 이름은 **왼쪽에 붙어 따라온다.** 31칸을 옆으로 밀면 어느 줄인지 사라진다.
   `.gy-table th` 가 모든 th 를 `top:0` 로 붙이는데(머리글용), 본문의 센터 칸까지
   위로 붙으면 세로로 스크롤할 때 이름 줄이 머리글 자리에 겹친다 — 본문 것은 `top`
   을 풀고 `left` 로 바꾼다. 바탕색은 `.gy-table th` 가 이미 불투명하게 준다. */
table.gy-days tbody th.who { position: sticky; left: 0; top: auto; z-index: 1; }
table.gy-days thead th.who { left: 0; z-index: 3; }

/* 칸은 **글자와 색을 함께** 쓴다 — 색만으로는 색맹·흑백 인쇄에서 못 읽는다. */
table.gy-days td.cell.auto { color: var(--gy-success-text); background: var(--gy-success-bg); }
table.gy-days td.cell.manual { color: var(--gy-text-secondary); background: var(--gy-surface-sunken); }
table.gy-days td.cell.missing { color: var(--gy-danger-text-strong); background: var(--gy-danger-bg); font-weight: 700; }
table.gy-days td.cell.off, table.gy-days td.cell.future { color: var(--gy-text-muted); }
/* 「도착」 열에 **왼쪽 선**을 긋는다. 표가 본문 폭을 다 쓰면서 남는 폭이 이 열로 가는데,
   선이 없으면 마지막 날짜와 숫자 사이의 빈 띠가 「여기서 표가 끝났다」로 읽혀 도착 열이
   딴 표처럼 떠 보인다(2026-09-15 화면 검토). 선이 있으면 같은 빈 띠가 그 열의 여백이 된다. */
table.gy-days td.sum, table.gy-days th.sum { text-align: right; white-space: nowrap; font-size: 12px;
                                             padding-left: 10px; border-left: 1px solid var(--gy-border); }
table.gy-days td.sum.bad { color: var(--gy-danger-text-strong); font-weight: 700; }

/* ---- 칸을 눌러 펼치기 (2026-09-16) ----
   격자는 「무엇이 왔나」까지만 답한다. 다음 물음은 늘 「그 안에 누가 있나」이고, 그
   답을 보려고 화면을 떠나면 ✕ 가 여러 개일 때 매번 돌아와야 한다.

   칸 안의 버튼은 **닿는 자리**만 맡는다 — 색과 기호는 위 `td.cell.*` 규칙이 이미
   정했으므로, 버튼이 자기 배경·테두리를 들고 오면 그 색을 덮어 버린다. */
table.gy-days td.cell button {
  display: block; width: 100%; padding: 0; border: 0; background: none;
  font: inherit; color: inherit; cursor: pointer; border-radius: 3px;
}
table.gy-days td.cell button:hover { background: var(--gy-primary-subtle); }
table.gy-days td.cell button:focus-visible { outline: 2px solid var(--gy-focus); outline-offset: -2px; }
/* 열려 있는 칸 — 펼친 표가 길면 어느 칸을 눌렀는지 잊는다. */
table.gy-days td.cell button[aria-expanded="true"] {
  box-shadow: inset 0 0 0 2px var(--gy-primary); font-weight: 700;
}

/* 펼친 조각이 앉는 줄. 격자 줄들 사이에 끼므로 **위아래 여백**으로 갈라 준다 —
   안 그러면 어느 센터의 것인지 눈으로 못 잇는다. */
tr.gy-daypanel-row > td { padding: 0 0 14px; background: var(--gy-surface); }

.gy-daypanel {
  border: 1px solid var(--gy-border); border-top: 2px solid var(--gy-primary);
  border-radius: var(--gy-radius); padding: 12px 14px; background: var(--gy-surface);
}
.gy-daypanel.loading, .gy-daypanel.error { color: var(--gy-text-muted); font-size: 12px; }
.gy-daypanel.error { color: var(--gy-danger-text-strong); }
.gy-daypanel .head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.gy-daypanel .head .grow { flex: 1; }
.gy-daypanel .head .who { font-size: 12px; color: var(--gy-text-secondary); }
.gy-daypanel .gy-legend.tally { margin: 0 0 10px; }
/* 표가 길어도 격자가 화면 밖으로 밀려나지 않게 — 펼친 표 안에서만 구른다.

   🔴 **`max-height` 와 `overflow` 는 반드시 같이 적는다.** 높이만 깎고 `overflow` 를
   딴 규칙에 맡기면, 그 규칙이 `visible` 을 주는 순간 표가 380px 상자 밖으로 **그려진
   채** 남는다 — 잘리지도 구르지도 않고 아래에 있는 저장 줄·안내문·다음 센터 줄 위에
   겹쳐 찍힌다(2026-09-17 담당자 지적, 실측 917px 내용 / 378px 상자).

   실제로 그랬다: 조각 template 이 `.gy-tablewrap flow`(= `max-height:none; overflow:
   visible`)를 달고 있었는데, 선택자 가중치가 같아 **뒤에 오는 이 규칙이 높이만 이기고**
   `overflow: visible` 은 그대로 남았다. 한쪽만 이기는 조합이 가장 나쁘다. */
.gy-daypanel .gy-tablewrap { max-height: 380px; overflow: auto; }

/* 🔴 표는 **내용 폭만** 쓴다. `.gy-table` 의 `width:100%` 를 그대로 두면 열이 화면
   끝까지 벌어져, 이름과 그 사람의 출근 시각이 손가락 두 뼘 떨어진다(2026-09-16 화면
   검토). 격자와 오른쪽 선을 맞출 이유도 없다 — 격자 안에 끼워 넣은 조각이라 왼쪽에
   모여 있는 편이 같은 줄로 읽힌다. */
.gy-daypanel table.gy-dayrows { width: auto; min-width: 520px; max-width: 100%; }
.gy-daypanel table.gy-dayrows th.who, .gy-daypanel table.gy-dayrows td.t { padding-right: 16px; }
/* 판정은 마지막 열이라 남는 폭을 먹어도 된다 — 대신 시각 열에서 떼어 둔다. */
.gy-daypanel table.gy-dayrows td.verdict { padding-left: 16px; }

/* 시각 칸은 **자릿수를 맞춘다** — 08:55 과 익 06:00 이 섞이면 눈이 자꾸 되돌아온다. */
table.gy-dayrows td.t, table.gy-dayrows th.t {
  text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums;
  font-family: var(--gy-font-mono);
}
table.gy-dayrows thead tr.sub th { font-size: 10px; font-weight: 400; color: var(--gy-text-muted); }
table.gy-dayrows th.who { text-align: left; white-space: nowrap; }
table.gy-dayrows td.verdict { font-size: 12px; color: var(--gy-text-secondary); }
table.gy-dayrows td.verdict small { color: var(--gy-text-muted); }
/* 도장이 하나뿐인 줄 — 출근·퇴근 두 칸에 걸쳐 그대로 적는다(어느 쪽인지 모른다). */
table.gy-dayrows td.lone { text-align: center; color: var(--gy-warning-text); }
table.gy-dayrows td.lone small { font-weight: 400; opacity: .85; }
/* 어긋난 줄만 눈에 띄게. 「태그만」·「확정분만」은 오류가 아니라 명단 차이라 칠하지
   않는다 — 다 칠하면 진짜 어긋남이 그 속에 묻힌다(격자 전체가 지키는 규약). */
table.gy-dayrows tr.v-differ td.verdict { color: var(--gy-danger-text-strong); font-weight: 600; }

/* ---- 고치기 (2걸음) ----
   입력칸이 붙으면 표가 넓어지므로 폭 하한을 올린다. */
.gy-daypanel table.gy-dayrows.editing { min-width: 760px; }
table.gy-dayrows td.fix { padding: 2px 4px; }
table.gy-dayrows td.fix input {
  width: 62px; padding: 3px 5px; text-align: right;
  font-family: var(--gy-font-mono); font-size: 12px; font-variant-numeric: tabular-nums;
  border: 1px solid var(--gy-border); border-radius: 4px; background: var(--gy-surface);
}
table.gy-dayrows td.fix input:focus { outline: 2px solid var(--gy-focus); outline-offset: -1px; }
/* 확정분에 줄이 없어 못 고치는 사람 — 칸을 안 그리되 **왜 없는지**가 보이게 흐리게 둔다. */
table.gy-dayrows td.nofix { text-align: center; color: var(--gy-text-muted); }
/* 이미 보정이 걸린 줄 — 무엇을 덮고 있는지 멀리서도 보여야 한다(격자 `.adj` 와 같은 규약). */
table.gy-dayrows tr.has-fix > th.who { box-shadow: inset 3px 0 0 var(--gy-warning); }
table.gy-dayrows tr.has-fix td.fix input { background: var(--gy-warning-bg-strong); }
/* 지금 손대고 있는 줄 */
table.gy-dayrows tr.dirty td.fix input { border-color: var(--gy-primary); }
table.gy-dayrows td.verdict .willbe {
  display: inline-block; margin-left: 8px; padding: 1px 7px; border-radius: var(--gy-radius-full);
  background: var(--gy-primary-subtle); color: var(--gy-primary);
  font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums;
}

/* 🔴 저장 줄은 **왼쪽에 모은다.** 오른쪽 끝으로 밀면 떠 있는 챗봇 단추(화면 오른쪽
   아래 고정)에 저장 버튼이 가린다 — 2026-09-16 화면 검토에서 실제로 겹쳤다. */
/* 🔴 저장 줄은 **왼쪽에 모은다.** 오른쪽 끝으로 밀면 떠 있는 챗봇 단추(화면 오른쪽
   아래 고정)에 저장 버튼이 가린다 — 2026-09-16 화면 검토에서 실제로 겹쳤다. */
.gy-daypanel-save { display: flex; align-items: center; gap: 8px; margin: 10px 0 4px; flex-wrap: wrap; }
.gy-daypanel-save label { font-size: 12px; color: var(--gy-text-secondary); }
.gy-daypanel-save label small { color: var(--gy-text-muted); }
.gy-daypanel-save input[name="why"] {
  flex: 0 1 280px; padding: 5px 8px; font-size: 12px;
  border: 1px solid var(--gy-border); border-radius: 4px; background: var(--gy-surface);
}
.gy-daypanel-save .changed { font-size: 12px; font-weight: 600; color: var(--gy-primary); }
/* 저장 결과 — 조용히 돌아오면 저장된 줄 안다. */
.gy-daypanel-said { margin: 0 0 8px; font-size: 12px; }
.gy-daypanel-said.ok { color: var(--gy-success-text); }
.gy-daypanel-said.warn { color: var(--gy-warning-text); }
.gy-board-legend.locked { color: var(--gy-text-muted); }

/* 센터별 상태의 「연결」 칸 — 격자와 **같은 규약**으로 글자와 색을 함께 쓴다. 이 열에
   색이 없으면 자동이 멎은 한 줄이 나머지 일곱과 똑같이 보인다(2026-09-15 화면 검토에서
   드러났다 — 템플릿은 `class="link {{ r.link }}"` 를 붙이고 있었는데 CSS 가 없었다).
   「열쇠 없음」은 문제가 아니라 **아직 안 붙인 상태**라 흐리게만 둔다 — 빨갛게 칠하면
   자동을 안 쓰는 센터가 매일 경고로 뜬다. */
table.gy-table td.link.stale, table.gy-table td.link.never { color: var(--gy-warning-text); font-weight: 600; }
table.gy-table td.link.revoked { color: var(--gy-danger-text-strong); font-weight: 600; }
table.gy-table td.link.none { color: var(--gy-text-muted); }

/* 세콤 설정 칸 — 연결 칸과 같은 규약이다(글자 + 색). **경고는 둘뿐**이고 나머지는
   조용하다: `ok` 는 본문색 그대로, `info`(되돌림·모르는 값)는 한 단 낮게, `none`
   (이번엔 점검 전 · 아직 안 붙임)은 흐리게. 경고가 둘뿐이라야 그 둘이 눈에 띈다. */
/* 세콤 설정 · 며칠 빈 뒤 되받기 — 뜻은 다르지만 읽는 법(경고/알림/할 말 없음)은 같다.
   🔴 클래스를 한 벌로 합치지 않는다 — 화면 시험이 `td.setting` 으로 그 칸만 골라
   읽는다(`test_intake_ui`). 같은 이름을 쓰면 두 칸의 값이 섞여 들어온다. */
table.gy-table td.setting.warn,
table.gy-table td.backfill.warn { color: var(--gy-warning-text); font-weight: 600; }
table.gy-table td.setting.info,
table.gy-table td.backfill.info { color: var(--gy-text-secondary); }
table.gy-table td.setting.none,
table.gy-table td.backfill.none { color: var(--gy-text-muted); }

/* 요약 — 정상일 때는 **얇고 조용하게**. 매일 빨간 화면은 아무도 안 읽는다. */
.gy-intake-sum { font-size: 13px; padding: 8px 12px; border-radius: 6px; margin: 0 0 14px; }
.gy-intake-sum.ok { background: var(--gy-success-bg); color: var(--gy-success-text); }
.gy-intake-sum.warn { background: var(--gy-warning-bg); color: var(--gy-warning-text); }
.gy-board-legend b.miss { color: var(--gy-danger-text-strong); }

/* 내용 카드 — 표가 아닌 덩어리(받기 폼 · 할 일)를 `.gy-tablewrap` · `.gy-kpi` 와 같은
   옷으로 세운다. 한 화면 안에서 어떤 덩어리는 카드고 어떤 덩어리는 맨바닥이면, 그
   차이가 뜻이 있는 줄 알고 읽게 된다. */
.gy-panel {
  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-topbar` 를 그대로
   쓰지 않는 이유: 저쪽은 화면 맨 위 필터라 아래 여백(20px)을 갖고 있는데, 이쪽은
   카드 **안**이라 그 여백이 카드 바닥에 빈 띠로 남는다. */
.gy-getbar { display: flex; align-items: flex-end; gap: 14px; flex-wrap: wrap; }
/* 두 날짜 사이의 `~`. 라벨이 없는 칸이라 글자 높이만큼 내려 입력칸과 밑선을 맞춘다. */
.gy-getbar .dash { color: var(--gy-text-muted); font-size: 14px; padding-bottom: 8px; }

/* ---- ＋ 기록 없는 사람 넣기 (3걸음) ----
   도장이 아예 없는 사람의 하루를 만드는 자리다. **접어 둔다** — 흔한 일이 아니고,
   펼쳐 두면 고치는 표보다 먼저 눈에 들어 「새로 만드는 것」이 기본처럼 보인다. */
.gy-daypanel-add { margin: 10px 0 4px; font-size: 12px; }
.gy-daypanel-add > summary { cursor: pointer; color: var(--gy-text-secondary); padding: 4px 0; }
.gy-daypanel-add > summary small { color: var(--gy-text-muted); }
.gy-daypanel-add .row { display: flex; gap: 6px; align-items: center; margin: 8px 0 6px; flex-wrap: wrap; }
.gy-daypanel-add select, .gy-daypanel-add input {
  padding: 5px 8px; font-size: 12px; border: 1px solid var(--gy-border);
  border-radius: 4px; background: var(--gy-surface);
}
.gy-daypanel-add input { width: 110px; font-family: var(--gy-font-mono); }
.gy-daypanel-add input:disabled { background: var(--gy-surface-sunken); color: var(--gy-text-muted); }
.gy-daypanel-add select { min-width: 150px; }
/* 경고가 아니라 「이 자리는 이런 뜻이다」라 흐리게 둔다 — 화면 전체가 지키는 규약. */
.gy-daypanel-add .why { margin: 4px 0 0; color: var(--gy-text-muted); line-height: 1.6; }

/* ---- 사용자 관리 (2026-09-19 처음부터 다시 설계) ----
   목록: 센터별 권한 매트릭스(사내) · 줄 단추(계약직) · 필터 줄 · 요약 칩.
   추가/수정: 권한 표 하나(센터 × 조회/승인) + 라디오 + 붙는 저장 줄.
   매트릭스는 열이 13개라 1280px 에서 표가 상자를 넘치지 않게 센터 칸의 좌우 여백을 줄인다
   (`tests/test_accounts_ui.py` 가 넘침을 잰다). */
.gy-pagehead { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
.gy-filters { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.gy-filters .gy-field select { min-width: 140px; }
.gy-chips { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 0 0 12px; }
.gy-chips .gy-chip { background: var(--gy-surface); border: 1px solid var(--gy-border); color: var(--gy-text-secondary); text-decoration: none; }
.gy-chips .gy-chip b { font-variant-numeric: tabular-nums; margin-left: 2px; }
.gy-chips .gy-chip:hover { background: var(--gy-surface-sunken); color: var(--gy-text); text-decoration: none; }
.gy-chips .gy-chip.on { background: var(--gy-primary); border-color: var(--gy-primary); color: var(--gy-on-primary); }
.gy-chips .hint { font-size: 11px; color: var(--gy-text-muted); margin-left: 4px; }
.gy-btn.sm { padding: 4px 9px; font-size: 11px; box-shadow: none; }
.gy-legend-line { font-size: 11px; color: var(--gy-text-muted); margin: 8px 2px 0; }

table.gy-matrix th.c, table.gy-matrix td.c { text-align: center; padding-left: 4px; padding-right: 4px; }
table.gy-matrix td.who { max-width: 220px; overflow: hidden; text-overflow: ellipsis; }
table.gy-matrix td.who a { color: var(--gy-text); font-weight: 600; }
table.gy-matrix td.c.has { background: var(--gy-primary-subtle); }
table.gy-matrix td.all { color: var(--gy-text-muted); font-size: 12px; background: var(--gy-surface-sunken); }
table.gy-matrix .dot { color: var(--gy-border-strong); }
table.gy-matrix .m { font-size: 12px; }
table.gy-matrix .m.a { color: var(--gy-success-text); font-weight: 700; }
table.gy-matrix .m.k { color: var(--gy-danger-text-strong); }
table.gy-matrix td.when, table.gy-people td.when { color: var(--gy-text-muted); font-variant-numeric: tabular-nums; }
table.gy-matrix tr.off td, table.gy-people tr.off td { color: var(--gy-text-muted); }
table.gy-people td.act form { display: inline-block; margin-right: 4px; }

.gy-perm-form { max-width: 760px; }
.gy-sect {
  background: var(--gy-surface); border: 1px solid var(--gy-border); border-radius: var(--gy-radius-lg);
  padding: 16px 20px; margin-bottom: 12px; box-shadow: var(--gy-shadow-sm);
}
.gy-sect h2 { font-size: 13px; font-weight: 700; margin: 0 0 10px; color: var(--gy-text-secondary); }
.gy-sect h2 small { font-weight: 500; color: var(--gy-text-muted); margin-left: 6px; }
.gy-sect .hint { font-size: 12px; color: var(--gy-text-muted); margin: 8px 0 0; line-height: 1.5; }
.gy-sect .static { font-size: 14px; font-weight: 600; }
.gy-sect.dim { opacity: .45; }
.gy-radio, .gy-check { display: flex; align-items: flex-start; gap: 8px; padding: 6px 0; font-size: 13px; cursor: pointer; }
.gy-radio input, .gy-check input { width: 16px; height: 16px; margin: 2px 0 0; flex: none; accent-color: var(--gy-primary); }
.gy-radio span, .gy-check span { color: var(--gy-text-muted); font-size: 12px; }
table.gy-perm { border-collapse: collapse; font-size: 13px; }
table.gy-perm th, table.gy-perm td { padding: 7px 14px; border-bottom: 1px solid var(--gy-border); text-align: left; white-space: nowrap; }
table.gy-perm th { font-size: 11px; color: var(--gy-text-secondary); }
table.gy-perm th.c, table.gy-perm td.c { text-align: center; width: 110px; }
table.gy-perm th.c label { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
table.gy-perm input[type="checkbox"] { width: 16px; height: 16px; margin: 0; vertical-align: middle; accent-color: var(--gy-primary); }
.gy-perm-form .gy-savebar { border: 1px solid var(--gy-border); border-radius: var(--gy-radius-lg); }
table.gy-matrix td.who small { display: block; font-weight: 400; font-size: 11px; color: var(--gy-text-muted); }
.gy-sect .gy-field.name { margin-top: 12px; }
.gy-sect .gy-field.name label small { font-weight: 400; color: var(--gy-text-muted); }

/* 근태 「확인 필요」 상자 — 원본이 무슨 말을 했는데 우리가 못 알아들은 자리.
   `.gy-flash.warn` 옷을 그대로 입고 안쪽 목록만 얇게 눕힌다. 새 색을 만들지 않는 이유:
   이 화면에 이미 노란 경고 규약이 있고(② 한 줄 요약), 색이 하나 늘면 담당자가 둘의
   차이를 뜻으로 읽는다. */
.gy-attention .gy-att-center { margin-top: 10px; font-size: 13px; }
.gy-attention .gy-att-list { margin: 4px 0 0; padding-left: 18px; }
/* 줄바꿈 없이 한 줄씩 — 날짜·이름·표기가 세로로 맞아야 눈으로 훑을 수 있다. */
.gy-attention .gy-att-list li { line-height: 1.7; }
.gy-attention .gy-att-how { margin-top: 12px; padding-top: 10px;
  border-top: 1px solid var(--yellow-200, rgba(0,0,0,.08)); font-size: 13px; }

/* 확정 화면의 「휴가 DB 에도 넣기」 칸 — 근태 파일과 휴가 기록 둘을 건드리는 자리라
   단추 바로 위에 붙여 둔다. 체크칸은 `form=` 으로 확정 폼에 붙어 있다(폼 안에 넣으면
   `.gy-actions` 의 가로 배치가 깨진다). */
.gy-leave-carry label { display: block; cursor: pointer; }
.gy-leave-carry .was { margin-top: 6px; line-height: 1.6; }
