:root {
  --bg: #ffffff;
  --fg: #16181d;
  --muted: #6b7280;
  --line: #e5e7eb;
  --accent: #2563eb;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #101216;
    --fg: #e8eaed;
    --muted: #9aa1ac;
    --line: #262a31;
    --accent: #6ea8fe;
  }
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--fg);
  font-family: system-ui, -apple-system, "Segoe UI", "Malgun Gothic", sans-serif;
  line-height: 1.6;
}

.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.25rem 1.5rem;
  border-bottom: 1px solid var(--line);
}

.logo {
  display: inline-flex; /* a 의 line-height 때문에 이미지 아래 여백이 생기는 것을 막는다 */
  color: var(--fg);
  text-decoration: none;
}

/* width/height 속성으로 원본 비율을 알려두고(레이아웃 흔들림 방지),
   실제 크기는 높이 기준으로만 정한다. */
.logo img {
  display: block;
  width: auto;
  height: 2rem;
}

/* ─── 헤더 오른쪽 ──────────────────────────────────────────────────────── */
/* 햄버거 버튼과 그 왼쪽 자리(로그인 버튼 또는 닉네임)를 나란히 놓는다. */

.header-right {
  display: flex;
  align-items: center;
  gap: 0.625rem;
}

/* 로그인 버튼. 햄버거와 높이·모서리를 맞춰 한 벌로 보이게 한다. */
.header-action {
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  color: var(--fg);
  font-size: 0.9375rem;
  text-decoration: none;
  white-space: nowrap;
}

.header-action:hover {
  border-color: var(--muted);
}

.header-action:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ─── 계정 메뉴 ────────────────────────────────────────────────────────── */
/* 닉네임을 누르면 열린다. 사이트 메뉴(햄버거)와 같은 <details> 방식이지만,
   이쪽은 식빵이 아니라 평범한 드롭다운이다 — 화면에 식빵이 둘이면 서로 싸운다. */

.account-nav {
  position: relative;
}

/* 로그인 뒤의 닉네임 = 계정 메뉴를 여는 버튼. 햄버거와 높이를 맞춘다. */
.header-user {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  max-width: 10rem;
  padding: 0.4rem 0.7rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  color: var(--fg);
  font-size: 0.9375rem;
  font-weight: 600;
  cursor: pointer;
  list-style: none; /* summary 기본 삼각형 제거 */
}

.header-user::-webkit-details-marker {
  display: none;
}

.header-user::marker {
  content: "";
}

.header-user:hover {
  border-color: var(--muted);
}

.header-user:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.header-user-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.header-user-caret {
  flex-shrink: 0;
  width: 0.75rem;
  height: 0.75rem;
  color: var(--muted);
  transition: transform 0.15s ease;
}

.account-nav[open] .header-user-caret {
  transform: rotate(180deg);
}

.account-panel {
  position: absolute;
  top: calc(100% + 0.4rem);
  right: 0;
  z-index: 50;
  min-width: 9rem;
  padding: 0.3rem;
  border: 1px solid var(--line);
  border-radius: 0.625rem;
  background: var(--bg);
  box-shadow: 0 8px 20px rgb(0 0 0 / 0.12);
}

.account-panel ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.account-panel a {
  display: block;
  padding: 0.45rem 0.6rem;
  border-radius: 0.4rem;
  color: var(--fg);
  font-size: 0.9375rem;
  text-decoration: none;
  white-space: nowrap;
}

.account-panel a:hover {
  background: var(--line);
}

.account-panel a[aria-current="page"] {
  color: var(--accent);
  font-weight: 600;
}

.account-panel a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* ─── 햄버거 메뉴 ──────────────────────────────────────────────────────── */
/* <details>/<summary> 위에 얹은 스타일. 열림 상태는 [open] 으로만 구분한다. */

.nav-toggle {
  display: inline-flex;
  padding: 0.4rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  color: var(--fg);
  cursor: pointer;
  list-style: none; /* summary 기본 삼각형 제거 */
}

/* 삼각형 마커는 브라우저마다 다른 방식으로 그린다. 둘 다 지워야 한다. */
.nav-toggle::-webkit-details-marker {
  display: none;
}

.nav-toggle::marker {
  content: "";
}

.nav-toggle:hover {
  border-color: var(--muted);
}

.nav-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.nav-icon {
  display: block;
  width: 1.5rem;
  height: 1.5rem;
}

/* 햄버거 ↔ 닫기 아이콘 교체 */
.nav-icon-close,
.site-nav[open] .nav-icon-bars {
  display: none;
}

.site-nav[open] .nav-icon-close {
  display: block;
}

/* 메뉴판은 식빵 한 장이다. 배경·테두리·그림자를 CSS 로 그리지 않고
   .nav-bread SVG 가 전부 맡는다.
   헤더가 아니라 화면 오른쪽 아래 바닥에 세로로 길게 선다. 그래서 기준이
   헤더가 아닌 뷰포트이고, position 은 absolute 가 아니라 fixed 다. */
.nav-panel {
  /* 폭을 한 번만 정의하고 패딩 계산에 그대로 쓴다. 머리와 바닥의 높이가
     폭에 비례해 정해지기 때문이다(각각 viewBox 비율 43.5/100, 17.5/100). */
  --bread-width: min(16rem, 72vw);

  position: fixed;
  right: 1.5rem;
  bottom: 0;
  z-index: 50;
  display: flex;
  flex-direction: column;
  justify-content: flex-end; /* 글자는 머리를 피해 몸통 아래쪽에 모인다 */
  width: var(--bread-width);
  height: min(21rem, 78dvh);
  min-height: 17rem;
  /* 위아래는 머리·바닥 높이를 비켜 가고, 좌우는 윤곽선 안쪽으로 들어온다.
     패딩을 %로 주면 안 된다 — 자기 폭이 아니라 포함 블록 폭이 기준이다. */
  padding: calc(var(--bread-width) * 0.435) 2rem
    calc(var(--bread-width) * 0.175 + 0.4rem + env(safe-area-inset-bottom));
}

/* 식빵 세 조각을 세로로 쌓는다. 글자 뒤에 깔리는 배경이자 실루엣이다. */
.nav-bread {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: flex;
  flex-direction: column;
  filter: drop-shadow(0 6px 14px rgb(0 0 0 / 0.18));
}

/* 머리와 바닥: 폭에 비례해 높이가 정해지므로 모양이 절대 변하지 않는다.
   flex-shrink 를 막아 두는 것도 그래서다 — 화면이 낮아도 머리부터 눌리면 안 된다. */
.nav-bread .bread-head,
.nav-bread .bread-foot {
  display: block;
  flex-shrink: 0;
  width: 100%;
  height: auto;
  overflow: visible; /* viewBox 가장자리에 걸친 선이 잘리지 않게 */
}

/* 몸통: 남은 높이를 전부 차지한다. 여기만 늘어난다. */
.nav-bread .bread-body {
  display: block;
  flex: 1;
  width: 100%;
  min-height: 0;
  overflow: visible;
}

/* 빵은 테마를 따르지 않는다. 다크모드에서 속을 배경색(어두운 색)으로 두면
   빵이 배경에 묻혀 실루엣이 읽히지 않는다. 어느 테마에서도 흰 종이에 검은 선이다.
   식빵 커서(.bread-line)와 같은 판단이며 선 색도 같은 값을 쓴다. */
.bread-inside {
  fill: #fff;
}

.bread-outline {
  fill: #fff;
  stroke: #22252b;
  stroke-width: 3;
  stroke-linejoin: round;
  /* 몸통이 세로로 늘어나도 선 굵기는 그대로 3px 이다. 이게 없으면
     늘어난 만큼 선이 두꺼워져 머리·바닥과 굵기가 어긋난다. */
  vector-effect: non-scaling-stroke;
}

/* 열린 path 라 fill 은 시작점과 끝점을 이은 안쪽만 채우고, stroke 는 그
   가상의 선을 그리지 않는다. 조각끼리 맞닿는 자리에 가로선이 생기지 않는 이유다. */
.bread-crust {
  fill: none;
  stroke: #22252b;
  stroke-width: 2.4;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}

.nav-panel ul {
  position: relative; /* 식빵 위로 올라온다 */
  z-index: 1;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* 빵이 항상 흰색이므로 그 위의 글자도 테마를 따르지 않는다.
   var(--fg) 를 쓰면 다크모드에서 밝은 글자가 흰 빵 위에 얹혀 사라진다. */
.nav-panel a {
  display: block;
  padding: 0.45rem 0.7rem;
  border-radius: 0.5rem;
  color: #22252b;
  text-decoration: none;
  white-space: nowrap; /* 자리가 모자라도 글자가 세로로 쌓이지는 않게 */
}

.nav-panel a:hover {
  background: rgb(0 0 0 / 0.07);
}

/* "page" 는 그 화면 자체, "true" 는 그 메뉴 아래의 화면(근무 스케줄의 그룹·
   근무자 설정 등)이다. 둘 다 현재 위치로 똑같이 보인다. */
.nav-panel a[aria-current="page"],
.nav-panel a[aria-current="true"] {
  color: #1d4ed8;
  font-weight: 600;
}

.nav-panel a:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: 1px;
}

/* 대각선 도약 → 중력처럼 가속하는 낙하 → 눌림과 작은 반동.
   바닥을 회전·변형의 축으로 삼아 착지에 무게감을 준다. */
.site-nav[open] .nav-panel {
  transform-origin: 50% 100%;
  animation: nav-bread-pop 660ms both;
}

@keyframes nav-bread-pop {
  0% {
    transform: translate(1rem, calc(100% + 3rem)) rotate(6deg) scale(0.98, 1.02);
    animation-timing-function: cubic-bezier(0.18, 0.65, 0.38, 1);
  }
  48% {
    transform: translate(-1rem, -3rem) rotate(-4deg) scale(1, 1);
    animation-timing-function: cubic-bezier(0.55, 0, 0.85, 0.45);
  }
  70% {
    transform: translate(0, 0) rotate(1.5deg) scale(1, 1);
    animation-timing-function: ease-out;
  }
  75% {
    transform: translate(0, 0) rotate(0.8deg) scale(1.03, 0.94);
    animation-timing-function: cubic-bezier(0.2, 0.65, 0.35, 1);
  }
  84% {
    transform: translate(-0.1rem, -0.25rem) rotate(-0.4deg) scale(0.998, 1.005);
    animation-timing-function: cubic-bezier(0.55, 0, 0.85, 0.45);
  }
  92% {
    transform: translate(0, 0) rotate(0.15deg) scale(1.005, 0.992);
    animation-timing-function: ease-out;
  }
  100% {
    transform: translate(0, 0) rotate(0deg) scale(1, 1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .site-nav[open] .nav-panel {
    animation: none;
  }
}

/* column 인 이유는 메시지 목록이 본문 위에 쌓여야 하기 때문이다.
   가운데 정렬은 그대로 유지된다. */
main {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.25rem;
  padding: 3rem 1.5rem;
}

/* The game shares the site background and header, with a wider content area. */
#app-main:has(> .pet-game) {
  padding: 0;
  justify-content: flex-start;
}
.pet-game { width: 100%; }

/* 운전 시뮬레이션은 헤더 아래 남은 화면을 전부 쓴다. 페이지가 스크롤되면 핸들을
   돌리다 화면이 끌려가므로 body 높이를 화면에 맞춰 고정하고 바닥글을 감춘다.
   안쪽 꾸밈은 driving/drive.css 가 맡는다. */
body:has(#app-main > .drive-sim) {
  height: 100dvh;
  min-height: 0;
  overflow: hidden;
  overscroll-behavior: none;
}
body:has(#app-main > .drive-sim) .site-footer { display: none; }
#app-main:has(> .drive-sim) {
  padding: 0;
  align-items: stretch;
  justify-content: flex-start;
  min-height: 0;
}
.drive-sim {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  background: #3b3e43;
}

/* ─── 운전 연습 입구 (driving/menu.html) ──────────────────────────────── */
/* 근무 스케줄 그룹 선택(.group-tile)과 같은 큰 타일. 휴대폰에서는 한 줄에 하나,
   넓으면 둘씩 놓되 시뮬레이션은 한 줄을 다 쓴다. 준비 중인 항목은 흐리게 두고
   누를 수 없다(aria-disabled). */
.drive-menu {
  width: min(100%, 34rem);
}
.drive-menu-heading {
  margin-bottom: 1.25rem;
  text-align: center;
}
.drive-menu-eyebrow {
  margin: 0 0 0.4rem;
  color: var(--accent);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.12em;
}
.drive-menu-heading h1 {
  margin: 0;
  font-size: clamp(1.5rem, 4vw, 2rem);
  letter-spacing: -0.045em;
}
.drive-menu-lead {
  margin: 0.35rem 0 0;
  color: var(--muted);
}
.drive-menu-list {
  display: grid;
  gap: 0.75rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 36rem) {
  .drive-menu-list { grid-template-columns: 1fr 1fr; }
  .drive-menu-main { grid-column: 1 / -1; }
}
.drive-menu-tile {
  display: flex;
  align-items: center;
  gap: 1rem;
  height: 100%;
  padding: 1.25rem;
  border: 1px solid var(--line);
  border-radius: 1.125rem;
  background: var(--bg);
  color: var(--fg);
  text-decoration: none;
}
a.drive-menu-tile:hover { border-color: var(--accent); }
a.drive-menu-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.drive-menu-tile[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
}
.drive-menu-icon {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 3rem;
  height: 3rem;
  border-radius: 0.875rem;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, var(--bg));
}
.drive-menu-icon svg { width: 1.75rem; height: 1.75rem; }
.drive-menu-tile[aria-disabled="true"] .drive-menu-icon {
  color: var(--muted);
  background: color-mix(in srgb, var(--muted) 10%, var(--bg));
}
.drive-menu-body { display: flex; flex: 1; flex-direction: column; min-width: 0; gap: 0.25rem; }
.drive-menu-body strong { font-size: 1.0625rem; }
.drive-menu-body > span { color: var(--muted); font-size: 0.8125rem; }
.drive-menu-action { color: var(--accent); font-size: 1.25rem; }

/* 화면 스크립트의 루트(data-module)를 걸어 두려고만 있는 껍데기. 상자를 만들지
   않으므로 안쪽 조각이 전처럼 main 의 자식으로 배치된다 (app-navigation.js). */
.page-module { display: contents; }

/* ─── 진행 표시줄 ──────────────────────────────────────────────────────── */
/* 전송 버튼을 누른 뒤 응답이 올 때까지 화면 맨 위에 흐르는 띠.
   레이아웃을 밀지 않도록 fixed 로 띄우고, 포인터 이벤트는 통과시킨다 —
   헤더 맨 윗줄을 덮고 있기 때문이다.

   진행률을 모르는 표시(indeterminate)다. 서버가 얼마나 걸릴지 알려주지 않으므로
   퍼센트를 지어내지 않고 좌우로 흐르기만 한다. */

.progress-bar {
  position: fixed;
  top: 0;
  right: 0;
  left: 0;
  z-index: 100; /* 헤더·메뉴(50) 위, 식빵 커서(9999) 아래 */
  height: 4px;
  overflow: hidden;
  pointer-events: none;
  /* 옅게 깔리는 트랙. 흐르는 조각이 화면 밖에 나가 있는 순간에도 띠가 거기
     있다는 것이 읽힌다 — 조각 하나만 두면 그 사이 아무것도 보이지 않는다. */
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  opacity: 0;
  transition: opacity 0.15s ease;
}

.progress-bar.is-active {
  opacity: 1;
}

/* 띠 안에서 움직이는 조각. 요소를 하나 더 두지 않으려고 가상 요소를 쓴다.
   양 끝이 투명으로 흐려지는 그라디언트라 딱딱한 막대가 아니라 빛줄기처럼
   지나가고, box-shadow 가 그 아래로 번지는 잔광을 만든다. */
.progress-bar::before {
  content: "";
  display: block;
  width: 45%;
  height: 100%;
  background: linear-gradient(
    90deg,
    transparent,
    var(--accent) 30%,
    color-mix(in srgb, var(--accent) 45%, white) 50%,
    var(--accent) 70%,
    transparent
  );
  box-shadow: 0 0 12px 1px color-mix(in srgb, var(--accent) 65%, transparent);
  transform: translateX(-100%); /* 대기 상태: 왼쪽 화면 밖 */
}

/* translateX 의 % 는 자기 폭 기준이다. 폭이 45% 이므로 222% ≈ 화면 폭 100%,
   즉 왼쪽 밖에서 출발해 오른쪽 밖으로 빠져나간다. */
.progress-bar.is-active::before {
  animation: progress-slide 0.9s ease-in-out infinite;
}

@keyframes progress-slide {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(222%);
  }
}

/* 움직임을 줄여달라고 한 사용자에게는 흐르지 않는 띠를 보여준다.
   애니메이션만 끄면 왼쪽 화면 밖에 멈춰 아무것도 안 보인다. */
@media (prefers-reduced-motion: reduce) {
  .progress-bar.is-active::before {
    width: 100%;
    background: var(--accent);
    animation: none;
    transform: none;
  }
}

/* ─── 알림 메시지 ──────────────────────────────────────────────────────── */
/* 모달(.modal) 안에 들어간다. 상자는 모달이 이미 그리므로 여기서는 글자만 다룬다. */

.messages {
  margin: 0 0 1.25rem;
  padding: 0;
  list-style: none;
}

.message {
  font-size: 0.9375rem;
  line-height: 1.5;
}

.message + .message {
  margin-top: 0.5rem;
}

.message-error {
  color: #b23c2c;
}

/* htmx 요청이 실패했을 때의 안내 (static/js/progress.js). 조각이 바뀌지 않으므로
   화면 위쪽에 따로 띄운다. */
#request-error {
  margin: 0;
  padding: 0.75rem 1rem;
  border: 1px solid currentColor;
  border-radius: 0.5rem;
  color: #b23c2c;
  font-size: 0.875rem;
  line-height: 1.5;
}

/* ─── 로그인·가입 화면 ─────────────────────────────────────────────────── */
/* Django 폼이 그리는 마크업에는 클래스가 거의 없다. body 의 auth-page 를 기준으로
   태그 선택자를 쓴다 — 다른 페이지에 번지지 않게 하려는 것이다. */

.auth-page main {
  justify-content: flex-start;
}

.auth-page main > h1,
.auth-card h1 {
  margin: 0;
  font-size: 1.5rem;
  letter-spacing: -0.02em;
}

.auth-page main > p,
.auth-card p {
  max-width: 26rem;
  margin: 0;
  text-align: center;
}

.auth-page form {
  display: flex;
  flex-direction: column;
  gap: 0.875rem;
  width: 100%;
  max-width: 22rem;
}

.auth-page form p {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  max-width: none;
  margin: 0;
  text-align: left;
}

/* 체크박스 줄('아이디 저장')만 가로로 눕힌다. 세로로 두면 라벨 아래에 작은
   네모가 덩그러니 떠 있는 모양이 된다.
   마크업 순서는 label → input 이라, row-reverse 로 체크박스를 앞에 놓는다. */
.auth-page form p:has(input[type="checkbox"]) {
  flex-direction: row-reverse;
  justify-content: flex-end;
  align-items: center;
  gap: 0.45rem;
}

.auth-page form p:has(input[type="checkbox"]) label {
  color: var(--fg);
  cursor: pointer;
}

.auth-page input[type="checkbox"] {
  width: 1rem;
  height: 1rem;
  margin: 0;
  accent-color: var(--fg);
  cursor: pointer;
}

.auth-page label {
  font-size: 0.875rem;
  color: var(--muted);
}

.auth-page input[type="email"],
.auth-page input[type="password"],
.auth-page input[type="text"] {
  width: 100%;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: var(--bg);
  color: var(--fg);
  font: inherit;
}

.auth-page input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.auth-page button[type="submit"] {
  padding: 0.6rem 1.25rem;
  border: 2px solid var(--fg);
  border-radius: 999px;
  background: var(--fg);
  color: var(--bg);
  font: inherit;
  cursor: pointer;
}

.auth-page button[type="submit"]:hover {
  opacity: 0.85;
}

/* 전송 중. progress.js 가 버튼을 잠근 상태다 — 같은 폼이 두 번 날아가지 않게.
   :hover 뒤에 두어야 마우스를 올려도 잠긴 모양이 유지된다.

   화면 맨 위의 띠만으로는 약하다. 버튼을 누른 사람의 시선은 그 버튼에 있지
   화면 꼭대기에 있지 않기 때문이다. 그래서 누른 자리에서 바로 도는 것을
   하나 더 붙인다. 버튼은 폼 폭 전체를 차지하므로 글자 옆에 붙어도 크기가
   변하지 않는다. */
.auth-page button[type="submit"]:disabled {
  opacity: 0.72;
  cursor: progress;
}

.auth-page button[type="submit"]:disabled::after {
  content: "";
  display: inline-block;
  width: 0.85em;
  height: 0.85em;
  margin-left: 0.5em;
  vertical-align: -0.12em;
  border: 2px solid currentColor;
  border-right-color: transparent; /* 한 쪽을 비워야 도는 것이 보인다 */
  border-radius: 50%;
  animation: progress-spin 0.7s linear infinite;
}

@keyframes progress-spin {
  to {
    transform: rotate(360deg);
  }
}

/* 도는 것을 멈추면 그냥 고리가 된다. 그것만으로도 평소와 다른 상태임은 읽힌다. */
@media (prefers-reduced-motion: reduce) {
  .auth-page button[type="submit"]:disabled::after {
    animation: none;
  }
}

/* 폼 오류(비밀번호 불일치 등). Django 는 ul.errorlist 로 그린다. */
.auth-page .errorlist {
  margin: 0;
  padding-left: 1.1rem;
  color: #b23c2c;
  font-size: 0.875rem;
}

.auth-page .helptext {
  color: var(--muted);
  font-size: 0.8125rem;
  line-height: 1.5;
}

/* 비밀번호 규칙은 Django 가 클래스 없는 <ul><li> 로 만들어 준다.
   감싼 .helptext 가 색·크기를 물려주고, 여기서 여백만 잡는다. */
.auth-page .helptext ul {
  margin: 0.25rem 0 0;
  padding-left: 1.1rem;
}

.auth-page .helptext li + li {
  margin-top: 0.15rem;
}

.auth-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  text-align: center;
}

/* ─── 모달 ─────────────────────────────────────────────────────────────── */
/* <dialog> 를 쓴다. 여닫기와 포커스 처리를 브라우저가 이미 안다.
   open 속성만 있을 때도 같은 위치와 배경으로 표시해 showModal() 전후의
   화면이 튀지 않게 한다. */

/* 알림이 들어앉는 자리. htmx 가 이 id 를 과녁 삼아 알림을 갈아 끼운다
   (templates/_messages.html). 알림이 없을 때는 빈 상자만 남는데, main 이
   flex 라 그대로 두면 gap 만큼 빈 줄이 생긴다 — 상자를 없애고 자식만
   부모의 흐름에 참여시킨다. */
#messages {
  display: contents;
}

.modal {
  position: fixed;
  inset: 0;
  margin: auto;
  max-width: 22rem;
  padding: 1.5rem;
  border: 1px solid var(--line);
  border-radius: 0.75rem;
  background: var(--bg);
  color: var(--fg);
  box-shadow: 0 10px 30px rgb(0 0 0 / 0.18);
}

/* 초기 HTML 이 스크립트보다 먼저 그려져도 모달과 같은 배경을 유지한다.
   승격 후에는 ::backdrop 만 사용해 배경이 두 번 어두워지지 않게 한다. */
.modal[open]:not(:modal) {
  z-index: 100;
  box-shadow: 0 10px 30px rgb(0 0 0 / 0.18), 0 0 0 100vmax rgb(0 0 0 / 0.4);
}

.modal form {
  display: flex;
  justify-content: flex-end;
}

.modal button {
  padding: 0.45rem 1.1rem;
  border: 2px solid var(--fg);
  border-radius: 999px;
  background: var(--fg);
  color: var(--bg);
  font: inherit;
  cursor: pointer;
}

.modal::backdrop {
  background: rgb(0 0 0 / 0.4);
}

.hero {
  max-width: 34rem;
  text-align: center;
}

.hero h1 {
  margin: 0 0 0.5rem;
  font-size: clamp(2.5rem, 8vw, 4rem);
  letter-spacing: -0.04em;
}

/* 텍스트 대신 이미지를 넣은 제목. h1 의 큰 font-size 가 줄 높이로 남지 않도록
   line-height 를 0 으로 접는다. */
.hero-mark {
  line-height: 0;
}

/* 세로로 긴 이미지라 폭이 아니라 높이가 먼저 걸린다.
   max-width / max-height 를 함께 두면 브라우저가 비율을 지키면서
   둘 다 만족하는 크기로 줄인다. */
.hero-mark img {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: min(60vh, 26rem);
}

.lead {
  margin: 0 0 0.75rem;
  font-size: 1.125rem;
}

.note {
  margin: 0;
  color: var(--muted);
  font-size: 0.9375rem;
}

/* ─── 현재 시각 ────────────────────────────────────────────────────────── */

.clock {
  margin: 1.75rem 0 0;
}

.clock time {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.1rem;
}

.clock-date {
  color: var(--muted);
  font-size: 0.9375rem;
}

/* 1초마다 숫자가 바뀐다. 글꼴 기본값은 숫자마다 폭이 달라서 그때마다 좌우로
   흔들리므로 tabular-nums(모든 숫자 같은 폭)로 고정한다. */
.clock-time {
  font-size: clamp(2rem, 7vw, 2.75rem);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  line-height: 1.15;
}

.site-footer {
  padding: 1.25rem 1.5rem;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.875rem;
}

.site-footer p {
  margin: 0;
}

/* ─── 마우스를 따라다니는 식빵 ─────────────────────────────────────────── */
/* 위치는 JS 가 transform 으로만 갱신한다 (top/left 를 건드리면 레이아웃이 다시 계산된다) */

.bread-cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9999;
  width: 31px;
  height: 56px;
  pointer-events: none; /* 아래쪽 링크·버튼 클릭을 가로채지 않는다 */
  opacity: 0;
  transition: opacity 0.18s ease;
  will-change: transform;
  transform-origin: 50% 95%; /* 기울일 때의 축은 머리가 아니라 발밑 */
  filter: drop-shadow(0 2px 3px rgb(0 0 0 / 0.22));
}

.bread-cursor.is-visible {
  opacity: 1;
}

/* 모달이 열려 있는 동안에는 감춘다. <dialog> 는 top layer 에 그려져서 z-index 로는
   그 위로 올라갈 수 없다 — 커서가 어두운 배경 뒤에 반쯤 걸쳐 보이느니 없는 편이 낫다. */
body:has(dialog.modal[open]) .bread-cursor,
body:has(dialog.view-range-dialog[open]) .bread-cursor {
  opacity: 0;
}

.bread-cursor svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* 흰 몸통 + 어두운 외곽선. 밝은 배경에서는 선이, 어두운 배경에서는 몸통이
   보이므로 다크모드에서도 색을 바꿀 필요가 없다. */
.bread-line {
  fill: #fffdf8;
  stroke: #22252b;
  stroke-width: 2.2;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.bread-detail {
  fill: none;
  stroke: #22252b;
  stroke-width: 1.6;
  stroke-linecap: round;
}

.bread-stitch {
  fill: none;
  stroke: #22252b;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-dasharray: 1.6 2.8;
}

/* 걷기: 두 다리를 엇갈리게 흔들고, 몸통은 그 두 배 빠르게 들썩인다.
   회전축은 bbox 중심이 아니라 골반 좌표로 직접 지정한다 — 신발이 한쪽으로
   튀어나와 있어 bbox 중심을 쓰면 축이 어긋난다. */
.bread-leg {
  transform-box: view-box;
  transform-origin: 19.1px 44px;
}

/* 두 다리에 '같은' 애니메이션을 건다. 오른쪽 다리는 미러 그룹 안에 있어
   회전 방향까지 뒤집히므로, 이것만으로 시각적으로 정확히 반대 위상이 된다.
   여기에 reverse 를 더하면 오히려 두 다리가 나란히 흔들린다. */
.is-walking .bread-leg {
  animation: bread-step 0.42s ease-in-out infinite;
}

.is-walking .bread-body {
  animation: bread-bob 0.21s ease-in-out infinite;
}

@keyframes bread-step {
  0%,
  100% {
    transform: rotate(11deg);
  }
  50% {
    transform: rotate(-11deg);
  }
}

@keyframes bread-bob {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-0.8px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .bread-leg,
  .bread-body {
    animation: none;
  }
}

/* ═══ 근무 스케줄 ════════════════════════════════════════════════════════
   /schedule/ 두 화면(스케줄 관리 · 근무자 설정)이 함께 쓴다.
   body.schedule-page 아래에만 걸어 다른 페이지로 번지지 않게 한다. */

/* 배경색은 사이트 기본값(--bg)을 그대로 쓴다. 이 화면만 다른 바탕을 깔면
   헤더·푸터와 색이 갈려 다른 사이트처럼 보인다. 카드는 색이 아니라
   테두리와 그림자로 구분한다 — .modal, .account-panel 과 같은 방식이다. */
.schedule-page {
  --shadow: 0 1px 3px rgb(16 24 40 / 0.06), 0 6px 18px rgb(16 24 40 / 0.06);
  --track-height: 48rem; /* 24시간 × 2rem */
}

@media (prefers-color-scheme: dark) {
  .schedule-page {
    --shadow: 0 1px 3px rgb(0 0 0 / 0.5);
  }
}

/* 다른 화면은 내용을 가운데 놓지만 여기는 위에서부터 쌓인다. */
.schedule-page main {
  align-items: stretch;
  justify-content: flex-start;
  gap: 0.875rem;
  padding: 1.25rem 0.875rem 3rem;
}

/* htmx 가 통째로 갈아 끼우는 조각. main 의 자식이 하나로 묶이므로
   화면 안의 세로 간격은 여기서 다시 잡는다. */
.board {
  display: flex;
  flex-direction: column;
  gap: 0.875rem;
  width: 100%;
  /* 브라우저의 스크롤 앵커링을 끈다.
     이 상자가 통째로 바뀔 때(outerHTML) 새 내용이 들어온 뒤 옛 내용이 빠지는
     잠깐 동안 문서 높이가 두 배가 된다. 그때 앵커링이 기준을 잃고 보정을
     하는 바람에, 날짜만 옮겼는데 화면이 맨 아래로 내려가 버린다.
     보던 자리를 지키는 일은 schedule.js 가 직접 한다(7번). */
  overflow-anchor: none;
}

.screen-title {
  max-width: 42rem;
  width: 100%;
  margin: 0 auto 0.25rem;
  font-size: 1.375rem;
  letter-spacing: -0.02em;
  text-align: center;
}

/* 돌아가는 길. 이 화면에서 나가는 유일한 출구라 흐린 글자 링크로 두면
   찾지 못한다 — 화면의 다른 버튼과 같은 모양(.btn .btn-quiet)을 쓴다.
   여기서는 그 버튼을 카드와 같은 폭 안에 왼쪽으로 붙이는 일만 한다. */
.back-link {
  display: flex;
  max-width: 42rem;
  width: 100%;
  margin: 0 auto;
}

/* 뒤로 가는 화살표라 글자보다 앞에 온다. .btn-icon 기본값보다 조금 크게
   잡아야 글자와 눈높이가 맞는다. */
.back-link .btn-icon {
  width: 1.125rem;
  height: 1.125rem;
  margin-left: -0.15rem;
}

/* 스크린리더에만 남기는 글자. 시각적으로는 없지만 display:none 과 달리
   읽히기는 한다 — 표의 제목처럼 눈으로는 열 머리만 봐도 되는 것에 쓴다. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ─── 카드 ─────────────────────────────────────────────────────────────── */

.schedule-page .card {
  width: 100%;
  max-width: 42rem;
  margin: 0 auto;
  padding: 1rem;
  border: 1px solid var(--line);
  border-radius: 1rem;
  background: var(--bg);
  box-shadow: var(--shadow);
}

.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.875rem;
}

.card-title {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0 0 0.75rem;
  font-size: 1.0625rem;
}

.card-head .card-title {
  margin-bottom: 0;
}

.card-icon {
  width: 1.375rem;
  height: 1.375rem;
  flex-shrink: 0;
}

.empty {
  margin: 0;
  color: var(--muted);
  font-size: 0.875rem;
}

/* ─── 버튼 ─────────────────────────────────────────────────────────────── */
/* 링크(<a>)와 버튼(<button>)이 같은 모양을 써야 해서 둘 다 받는다. */

.schedule-page .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  padding: 0.55rem 1rem;
  border: 1px solid transparent;
  border-radius: 0.625rem;
  font: inherit;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}

.schedule-page .btn-sm {
  padding: 0.4rem 0.7rem;
  font-size: 0.875rem;
}

.schedule-page .btn-icon {
  width: 1rem;
  height: 1rem;
  flex-shrink: 0;
}

.schedule-page .btn-primary {
  background: #2563eb;
  color: #ffffff;
}

.schedule-page .btn-primary:hover {
  background: #1d4ed8;
}

.schedule-page .btn-quiet {
  border-color: var(--line);
  background: var(--bg);
  color: var(--fg);
  font-weight: 500;
}

.schedule-page .btn-quiet:hover {
  border-color: var(--muted);
}

.schedule-page .btn-danger {
  border-color: color-mix(in srgb, #dc2626 45%, transparent);
  background: color-mix(in srgb, #dc2626 10%, var(--bg));
  color: #dc2626;
}

.schedule-page .btn-danger:hover {
  background: color-mix(in srgb, #dc2626 18%, var(--bg));
}

.schedule-page .btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.schedule-page .btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* ─── 근무자 색 ─────────────────────────────────────────────────────────
   근무자마다 고르는 색은 스무 가지로 고정돼 있다(schedules/models.py 의
   WorkerColor). 값이 그대로 클래스 이름이 되므로 임의의 색이 스타일로
   흘러들 수 없다. **이름과 개수는 그쪽과 반드시 같아야 한다** — 한쪽에만
   있는 항목은 색 없는 점으로 그려진다.

   각 클래스는 --worker-color 하나만 정한다. 점·막대·테두리는 그 색을 배경과
   섞어 쓰므로(color-mix), 다크 모드에서도 따로 정의할 필요가 없다 —
   --bg 가 어두워지면 섞인 결과도 함께 어두워진다.

   밝기를 비슷하게 맞춘 값들이다. 하나만 유난히 어두우면 22% 로 옅게 깔리는
   막대에서 그 근무자만 튄다. */

/* 처음 여덟 — 서로 가장 멀리 떨어진 색 */
.wc-blue {
  --worker-color: #3b82f6;
}
.wc-green {
  --worker-color: #22c55e;
}
.wc-red {
  --worker-color: #ef4444;
}
.wc-purple {
  --worker-color: #a855f7;
}
.wc-amber {
  --worker-color: #f59e0b;
}
.wc-teal {
  --worker-color: #14b8a6;
}
.wc-orange {
  --worker-color: #f97316;
}
.wc-pink {
  --worker-color: #ec4899;
}

/* 그다음 열둘 — 앞의 색들 사이를 메운다 */
.wc-indigo {
  --worker-color: #6366f1;
}
.wc-lime {
  --worker-color: #84cc16;
}
.wc-cyan {
  --worker-color: #06b6d4;
}
.wc-rose {
  --worker-color: #f43f5e;
}
.wc-violet {
  --worker-color: #8b5cf6;
}
.wc-emerald {
  --worker-color: #10b981;
}
.wc-yellow {
  --worker-color: #eab308;
}
.wc-fuchsia {
  --worker-color: #d946ef;
}
.wc-sky {
  --worker-color: #0ea5e9;
}
.wc-brown {
  --worker-color: #b45309;
}
.wc-slate {
  --worker-color: #64748b;
}
.wc-maroon {
  --worker-color: #be123c;
}

.worker-dot {
  flex-shrink: 0;
  width: 0.875rem;
  height: 0.875rem;
  border-radius: 50%;
  background: var(--worker-color, var(--muted));
}

/* ─── 근무자 목록 (스케줄 화면 상단) ────────────────────────────────────
   알약 모양 칩을 줄바꿈으로 늘어놓는다. 근무자가 늘어도 아래로만 자라고
   가로로 넘치지 않는다.

   **칩 하나가 곧 시간표 필터다.** 누르면 그 근무자가 표에서 빠진다. 체크박스를
   두지 않으므로 켜짐/꺼짐은 오로지 배경색으로 읽힌다 — 그래서 `.is-off` 의
   대비를 충분히 준다. */

.worker-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* <li> 는 자리만 잡는다. 칩 자체는 그 안의 <a>(또는 <span>)다. */
.worker-chips > li {
  display: flex;
  min-width: 0;
  max-width: 100%;
}

.worker-chip {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--line);
  border-radius: 0.75rem;
  background: var(--bg);
  font-size: 0.9375rem;
  font-weight: 600;
}

.worker-chip-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 칩은 대부분 <a> 다 (마지막 한 명만 <span> — 끌 수 없으므로). 링크로 보이게
   두지 않는다. 이 칩의 뜻은 '누르면 이동'이 아니라 '지금 켜져 있다' 이다. */
a.worker-chip {
  color: inherit;
  text-decoration: none;
}

a.worker-chip:hover {
  border-color: var(--worker-color, var(--accent));
}

a.worker-chip:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* 시간표에서 빼 둔 근무자. 목록에서 사라지지는 않는다 — 다시 누르면 돌아온다.
   배경을 회색으로 깔고 점과 글자도 함께 죽여, 켜진 칩 옆에서 한눈에 갈린다. */
.worker-chip.is-off {
  background: color-mix(in srgb, var(--muted) 18%, var(--bg));
  border-color: transparent;
  color: var(--muted);
}

.worker-chip.is-off .worker-dot {
  background: var(--muted);
  opacity: 0.5;
}

/* ─── 날짜 이동 ────────────────────────────────────────────────────────── */

.date-nav {
  display: flex;
  align-items: stretch;
  gap: 0.5rem;
  margin-bottom: 0.875rem;
}

.date-step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 2.75rem;
  border: 1px solid var(--line);
  border-radius: 0.75rem;
  background: var(--bg);
  color: var(--fg);
  text-decoration: none;
}

.date-step:hover {
  border-color: var(--muted);
}

.date-step:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.date-step svg {
  width: 1.25rem;
  height: 1.25rem;
}

.date-form {
  display: flex;
  flex: 1;
  gap: 0.5rem;
  min-width: 0;
}

/* 보이는 글자는 한국어 날짜이고, 그 위에 투명한 <input type="date"> 를
   겹쳐 둔다. 누르면 기기의 날짜 선택기가 그대로 열린다 — 직접 만든
   달력보다 각 기기에 익숙한 것이 낫다. */
.date-field {
  position: relative;
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  min-width: 0;
  padding: 0.55rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: 0.75rem;
  background: var(--bg);
  cursor: pointer;
}

.date-field:hover {
  border-color: var(--muted);
}

/* 겹쳐 둔 칸은 보이지 않지만 포커스는 받는다. 키보드로 왔을 때 어디에
   있는지 보이도록 테두리를 바깥 상자가 대신 그린다. */
.date-field:has(.date-input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.date-icon {
  flex-shrink: 0;
  width: 1.125rem;
  height: 1.125rem;
  color: var(--muted);
}

.date-text {
  overflow: hidden;
  font-size: 0.9375rem;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.date-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  opacity: 0; /* 보이지 않지만 눌리고, 포커스도 받는다 */
  cursor: pointer;
}

/* schedule.js 가 붙으면 고르는 즉시 전송되므로 이 버튼은 할 일이 없다.
   스크립트가 없으면 그대로 남아 있어야 한다 — 그래서 기본이 '보임'이다. */
.date-form.has-auto-submit .date-go {
  display: none;
}

/* ─── 일정 등록 폼 ─────────────────────────────────────────────────────── */

/* 첫 줄은 [근무자명] [일정 추가], 둘째 줄은 [출근 시간] [퇴근 시간] 이다.
   두 열을 같은 폭으로 두어야 둘째 줄의 두 칸이 나란히 보인다. */
.shift-form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.625rem 0.75rem;
  align-items: end;
}

.shift-form .form-errors {
  grid-column: 1 / -1;
}

.field-worker {
  grid-column: 1;
}

.field-submit {
  grid-column: 2;
  height: 2.75rem;
}

/* 비고는 적을 때만 적는 칸이라 두 칸을 가로질러 한 줄로 둔다. */
.field-note {
  grid-column: 1 / -1;
}

/* 시 · 분 두 개의 select 를 나란히 놓는다. 폼 필드 하나(start_minutes)가
   화면에서만 둘로 나뉜 것이라, 바깥의 .field 는 여전히 칸 하나로 다룬다.

   둘의 폭을 같게 둔다. 고를 것의 개수는 다르지만(24 대 12) 그것은 목록을
   펼쳤을 때의 이야기이고, 닫혀 있는 칸에는 두 자리 숫자 하나가 놓일 뿐이다 —
   폭이 다르면 같은 종류의 칸 둘이 다른 것처럼 읽힌다. */
.time-select {
  display: flex;
  gap: 0.35rem;
  min-width: 0;
}

.time-select select {
  /* flex-basis 를 0 으로 두어야 내용(선택지 폭)과 무관하게 정확히 반씩 나눈다.
     flex: 1 (기본 basis: 0%) 이 그 일을 한다. */
  flex: 1;
  min-width: 0;
  /* 화살표까지 넣기에 자리가 빠듯하므로 좌우 여백을 줄인다. */
  padding: 0 0.4rem;
}

.schedule-page .field {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-width: 0;
}

.schedule-page .field > label {
  color: var(--muted);
  font-size: 0.8125rem;
}

.schedule-page select,
.schedule-page input[type="text"] {
  width: 100%;
  min-width: 0;
  height: 2.75rem;
  padding: 0 0.7rem;
  border: 1px solid var(--line);
  border-radius: 0.625rem;
  background: var(--bg);
  color: var(--fg);
  font: inherit;
}

.schedule-page select:focus-visible,
.schedule-page input[type="text"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.schedule-page .errorlist {
  margin: 0;
  padding-left: 1.1rem;
  color: #b23c2c;
  font-size: 0.8125rem;
}

.schedule-page .form-errors {
  margin-bottom: 0.25rem;
}

/* ─── 스케줄 표 ────────────────────────────────────────────────────────── */

.schedule-card {
  padding: 0.75rem 0.5rem 0.75rem 0;
  overflow: hidden; /* 가로 스크롤이 카드 모서리를 넘어가지 않게 */
}

/* 표는 전체 높이로 펼치고, 근무자가 많을 때만 가로로 이동한다. */
.schedule-scroll {
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
}

.schedule-scroll:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.schedule-grid {
  display: flex;
  align-items: stretch;
  min-width: max-content;
  /* 가로 스크롤바가 놓일 자리. 위의 overflow-y: hidden 이 바닥을 잘라내므로,
     잘리는 것이 24:00 눈금이나 막대 끝이 아니라 이 여백이 되게 한다.
     윈도우의 고전 스크롤바가 17px 안팎이라 그보다 넉넉히 준다. 스크롤바가
     없는 환경(겹쳐 그리는 방식)에서는 그냥 아래 여백으로 보인다. */
  padding-bottom: 1.25rem;
}

.col-head {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  height: 2.5rem;
  padding: 0 0.4rem;
  font-size: 0.8125rem;
  font-weight: 700;
}

.col-head-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 시간 열. sticky 로 왼쪽에 고정하고, 뒤로 지나가는 막대가 비치지 않도록
   배경을 카드와 같은 색으로 채운다. */
.time-column {
  position: sticky;
  left: 0;
  z-index: 2;
  flex-shrink: 0;
  width: 3.5rem;
  background: var(--bg);
}

.time-marks {
  /* 24칸 + 높이 0인 마지막 칸. 마지막 칸 덕분에 24:00 눈금이 바닥 선에 정확히
     놓인다 (칸 안에 두면 한 시간만큼 위로 밀린다). */
  display: grid;
  grid-template-rows: repeat(var(--view-hours, 24), 1fr) 0;
  height: var(--track-height);
  margin-top: var(--track-gap, 0.5rem);
}

.time-mark {
  position: relative;
}

/* 눈금 글자는 칸의 '시작선'에 걸터앉는다. 칸 안에 놓으면 선과 한 시간 어긋난다. */
.time-mark span {
  position: absolute;
  top: 0;
  right: 0.5rem;
  color: var(--muted);
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
  transform: translateY(-50%);
}

/* 열 사이 점선은 열 전체가 아니라 머리 칸과 표(.worker-track)에 따로 긋는다.
   둘 사이의 틈(--track-gap)에는 선이 없어야 머리 칸과 표가 갈라져 보인다. */
.worker-column {
  flex: 1 0 6.25rem;
  min-width: 6.25rem;
}

.worker-column > .col-head,
.worker-column > .worker-track {
  border-left: 1px dashed var(--line);
}

/* 근무 막대가 놓이는 바탕. 한 시간마다 옅은 가로선을 긋는다 — 이미지가 아니라
   반복 그라디언트라 높이가 바뀌어도 선 수가 그대로다.

   표시 시간대를 좁히면 범위 밖으로 걸친 막대가 생긴다. overflow: hidden 이
   그 부분을 잘라 낸다.

   margin-top(--track-gap)은 머리 칸과 표 사이의 틈이다. 첫 눈금 글자는 선에
   가운데를 맞추느라 절반이 선 위로 올라가는데, 틈이 없으면 그 절반이 머리
   칸(sticky, 불투명) 밑에 깔려 잘린다. 시간 열(.time-marks)과 같은 값을 써야
   눈금과 가로선이 어긋나지 않는다. */
.worker-track {
  position: relative;
  height: var(--track-height);
  margin-top: var(--track-gap, 0.5rem);
  overflow: hidden;
  background-image: repeating-linear-gradient(
    to bottom,
    var(--line) 0 1px,
    transparent 1px calc(100% / var(--view-hours, 24))
  );
}

/* 막대. --start/--end 는 자정으로부터 흐른 분이며, 일정마다 다르므로
   템플릿의 nonce <style> 블록이 내려보낸다 (templates/schedules/board.html).

   아래 기본값은 그 블록이 없을 때를 위한 것이다. 하루 전체 길이가 되므로
   '뭔가 잘못됐다'가 눈에 보인다 — 막대가 사라져 일정이 없는 것처럼 보이는
   것보다 낫다. */
.shift-bar {
  --start: 0;
  --end: 1440;

  position: absolute;
  right: 0.25rem;
  left: 0.25rem;
  /* 표시 시간대(--view-start, --view-hours: 시 단위)를 기준으로 놓는다.
     기본값은 하루 전체(0시부터 24시간)다. */
  top: calc((var(--start) - var(--view-start, 0) * 60) / (var(--view-hours, 24) * 60) * 100%);
  height: calc((var(--end) - var(--start)) / (var(--view-hours, 24) * 60) * 100%);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.1rem;
  overflow: hidden; /* 30분짜리 막대에는 글자가 다 들어가지 않는다 */
  padding: 0.25rem;
  border: 1px solid color-mix(in srgb, var(--worker-color) 45%, var(--bg));
  border-radius: 0.5rem;
  background: color-mix(in srgb, var(--worker-color) 22%, var(--bg));
  color: var(--fg);
  font-size: 0.75rem;
  line-height: 1.35;
  text-align: center;
  text-decoration: none;
}

/* 표시 시간대 밖의 막대 (schedule.js 10번). display: flex 가 hidden 속성을 이기므로 따로 적는다. */
.shift-bar[hidden] {
  display: none;
}

.shift-bar:hover {
  background: color-mix(in srgb, var(--worker-color) 32%, var(--bg));
}

.shift-bar:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
}

.shift-name {
  font-weight: 700;
}

.shift-time {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* 막대 안의 비고. 자리가 남을 때만 보인다 — 짧은 막대에서는 .shift-bar 의
   overflow: hidden 이 잘라낸다. 전부 보려면 눌러서 상세 시트를 연다.

   근무자 이름·시각보다 작고 흐리게 둔다. 있으면 좋고 없어도 그만인 정보라,
   시간표를 훑을 때 시선을 먼저 가져가면 안 된다. */
.shift-note {
  margin-top: 0.15rem;
  padding-top: 0.15rem;
  border-top: 1px solid color-mix(in srgb, var(--worker-color) 35%, transparent);
  color: var(--muted);
  font-size: 0.6875rem;
  line-height: 1.3;
  /* 긴 메모가 막대를 옆으로 밀지 않게 한다. */
  overflow-wrap: anywhere;
}

/* 출근·퇴근 시각을 잇는 세로 획. 디자인의 `06:00 │ 14:00` 을 세로로 세운 것이다. */
.shift-sep {
  width: 1px;
  height: 0.5rem;
  background: color-mix(in srgb, var(--worker-color) 70%, var(--bg));
}

/* ─── Bottom Sheet ─────────────────────────────────────────────────────
   막대를 누르면 아래에서 올라오는 상세 패널. 여는 것도 닫는 것도 그냥
   링크(주소)라 스크립트 없이 동작하고, 뒤로 가기로도 닫힌다.

   base.html 의 알림 모달(.modal)과는 다른 클래스다. modal.js 가 그쪽만
   showModal() 로 승격하므로 이 시트는 건드리지 않는다. */

.sheet-scrim {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: block;
  background: rgb(0 0 0 / 0.4);
}

.schedule-page .sheet {
  position: fixed;
  inset: auto 0 0;
  z-index: 61;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  width: 100%;
  max-width: 30rem;
  max-height: 85vh;
  margin: 0 auto;
  padding: 1.25rem 1.25rem calc(1.25rem + env(safe-area-inset-bottom));
  overflow-y: auto;
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: 1.25rem 1.25rem 0 0;
  background: var(--bg);
  color: var(--fg);
  box-shadow: 0 -8px 30px rgb(0 0 0 / 0.2);
}

.sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}

.sheet-title {
  margin: 0;
  font-size: 1.0625rem;
}

.sheet-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 0.5rem;
  color: var(--muted);
}

.sheet-close:hover {
  background: var(--line);
  color: var(--fg);
}

.sheet-close svg {
  width: 1.125rem;
  height: 1.125rem;
}

.sheet-date {
  margin: 0;
  color: var(--muted);
  font-size: 0.875rem;
  font-variant-numeric: tabular-nums;
}

.sheet-warning {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.5;
}

.sheet-form {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.sheet-times {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
}

.sheet-delete {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
}

/* ─── 근무자 설정 화면 ─────────────────────────────────────────────────── */

/* 상단 줄 — 새 근무자 칸 + `추가`.
   전체 변경사항 저장은 목록 아래 별도 영역에 둔다. */
.worker-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.875rem;
  padding-bottom: 0.875rem;
  border-bottom: 1px solid var(--line);
}

.worker-new {
  display: flex;
  flex: 1 1 14rem;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

/* 추가 칸에는 이름과 색상만 보인다.
   순서 손잡이·삭제 칸은 **그려 두되 가린다** — `추가` 를 누르면 schedule.js 가 이
   속을 통째로 목록의 줄로 옮기는데, 그때 칸이 이미 들어 있어야 옮긴 줄이 다른
   줄과 똑같이 동작한다 (templates/schedules/_worker_row.html 참고). */
.worker-new .row-grip,
.worker-new .row-delete {
  display: none;
}

.worker-new .field:first-of-type {
  flex: 1 1 7rem;
}

.worker-new select {
  width: auto;
}

.worker-new .errorlist {
  flex-basis: 100%;
}

.worker-toolbar .btn {
  height: 2.75rem;
}

.worker-form-actions {
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line);
}

.schedule-page .worker-form-actions .btn {
  width: 100%;
  min-height: 3.5rem;
  padding: 0.875rem 1.5rem;
  font-size: 1rem;
  font-weight: 700;
}

.worker-save-status {
  margin: 0 0 0.625rem;
  color: var(--muted);
  font-size: 0.875rem;
  line-height: 1.5;
}

.worker-form-actions.has-unsaved .worker-save-status {
  color: var(--accent);
  font-weight: 600;
}

.schedule-page .worker-form-actions {
  position: fixed;
  inset: auto 0 0;
  z-index: 40;
  margin: 0;
  padding: 0.875rem 1rem calc(0.875rem + env(safe-area-inset-bottom));
  background: var(--bg);
  box-shadow: 0 -4px 16px rgb(0 0 0 / 0.08);
}

.group-management:not([open]) .worker-form-actions { display: none; }

.schedule-page main:has(#workers, .group-management[open]) {
  padding-bottom: calc(var(--worker-save-height, 9rem) + 1.5rem);
}

html:has(.schedule-page #workers, .schedule-page .group-management[open]) {
  scroll-padding-bottom: calc(var(--worker-save-height, 9rem) + 1.5rem);
}

.worker-rows {
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.worker-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  padding: 0.625rem;
  border: 1px solid var(--line);
  border-radius: 0.75rem;
  background: var(--bg);
}

.worker-row .form-errors {
  flex-basis: 100%;
}

/* 이름 칸이 남는 자리를 먹고, 색상 선택은 제 폭만 차지한다. */
.worker-row .field:first-of-type {
  flex: 1 1 7rem;
}

.worker-row select {
  width: auto;
}

/* 맨 아래 새 근무자 줄. 이미 있는 근무자와 섞이지 않게 한 칸 떼고 점선으로 둔다. */
.worker-row.is-new {
  margin-top: 0.25rem;
  border-style: dashed;
}

.worker-row.is-new .worker-dot {
  opacity: 0.4;
}

/* ─── 끌어서 순서 바꾸기 ──────────────────────────────────────────────────

   손잡이는 줄의 **맨 왼쪽**이고, 줄에서 끌리는 곳은 여기뿐이다. 줄 전체를
   잡히게 하면 이름 칸을 고치려고 누른 것과 옮기려고 누른 것을 구분할 수 없다.

   `touch-action: none` 은 이 작은 칸에만 건다. 브라우저에게 '여기서 시작한
   세로 제스처는 스크롤이 아니다'라고 알리는 것이라, 줄의 나머지를 쓸면
   목록은 평소대로 스크롤된다.

   스크립트가 줄을 옮길 수 있을 때만 내보낸다(.has-reorder) — 끌어도 아무 일도
   없는 손잡이를 보여주지 않기 위해서다. */
.row-grip {
  display: none;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2.5rem; /* 손가락이 닿을 넓이 */
  padding: 0;
  border: 0;
  border-radius: 0.5rem;
  background: none;
  color: var(--muted);
  cursor: grab;
  touch-action: none;
}

.worker-rows.has-reorder .row-grip {
  display: inline-flex;
}

.row-grip:hover {
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  color: var(--fg);
}

.schedule-page .row-grip:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.grip-icon {
  width: 0.625rem;
  height: 1rem;
  fill: currentColor;
  pointer-events: none; /* 끌기의 대상은 언제나 손잡이 자신이다 */
}

/* 끌고 있는 줄. 손가락을 따라가는 거리는 스크립트가 --drag-offset 으로 준다. */
.worker-row.is-dragging {
  position: relative;
  z-index: 2;
  transform: translateY(var(--drag-offset, 0));
  border-color: var(--accent);
  box-shadow: 0 0.5rem 1.25rem rgb(0 0 0 / 0.18);
  cursor: grabbing;
}

.worker-row.is-dragging .row-grip {
  color: var(--accent);
  cursor: grabbing;
}

/* 끄는 동안에는 글자가 딸려 선택되지 않게 한다. */
.worker-rows.is-reordering {
  user-select: none;
}

/* 삭제 — 겉은 버튼, 속은 체크박스다. <label> 이 그 체크박스를 감싸고 있어서
   스크립트가 없어도 눌리고, 실제로 지우는 것은 언제나 `저장` 이다.
   (마크업은 templates/schedules/_worker_row.html 참고) */
.row-delete {
  position: relative; /* 안쪽 체크박스의 .visually-hidden 기준점 */
}

/* 스크립트가 있으면 누른 줄은 곧바로 감춰진다. 클래스가 아니라 hidden 속성을
   쓰는데, .worker-row 의 display:flex 가 브라우저 기본 [hidden] 규칙을 이기므로
   여기서 다시 눌러 준다. */
.worker-row[hidden] {
  display: none;
}

/* 스크립트가 없을 때의 모습. 줄은 남아 있고 '지워질 예정'으로만 읽힌다. */
.worker-row:has(.row-delete input:checked) {
  opacity: 0.55;
}

.worker-row:has(.row-delete input:checked) .field input,
.worker-row:has(.row-delete input:checked) .field select {
  text-decoration: line-through;
}

.schedule-page .helptext {
  margin: 0.75rem 0 0;
  color: var(--muted);
  font-size: 0.8125rem;
  line-height: 1.5;
}

/* 그룹 목록에 붙는 '근무자 N명'. 이름만 늘어놓으면 어느 그룹이 실제로 쓰이고
   있는지 알 수 없다. */
.group-count {
  margin-left: auto;
  color: var(--muted);
  font-size: 0.8125rem;
  white-space: nowrap;
}

.check {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  color: var(--muted);
  font-size: 0.875rem;
  white-space: nowrap;
  cursor: pointer;
}

.check input[type="checkbox"] {
  width: 1rem;
  height: 1rem;
  margin: 0;
  accent-color: var(--accent);
  cursor: pointer;
}

/* Mobile-first schedule workspace. All colors follow the site's light/dark palette. */
.schedule-page main {
  background: color-mix(in srgb, var(--accent) 3%, var(--bg));
  padding: 1.25rem 1rem 3rem;
}
.schedule-page .board {
  max-width: 64rem;
  margin-inline: auto;
  gap: 1rem;
}
.schedule-page .card,
.schedule-page .back-link {
  max-width: none;
}
.schedule-page .card {
  box-shadow: 0 2px 8px rgb(0 0 0 / 0.025);
  border-radius: 1.125rem;
}
.schedule-heading { padding: 0.25rem 0 0.5rem; }
.schedule-page .screen-title {
  max-width: none;
  margin: 0;
  text-align: center;
  font-size: clamp(1.5rem, 4vw, 2rem);
  letter-spacing: -0.045em;
  overflow-wrap: anywhere;
}
.schedule-heading-with-back {
  display: grid;
  grid-template-columns: 2.75rem minmax(0, 1fr) 2.75rem;
  align-items: center;
  column-gap: 0.5rem;
  row-gap: 0.5rem;
}
.schedule-heading-with-back .screen-title { grid-column: 2; grid-row: 1; }
.schedule-heading-with-back .schedule-back { grid-column: 1; grid-row: 1; }
.schedule-heading-description {
  grid-column: 1 / -1;
  grid-row: 2;
  min-width: 0;
  text-align: center;
}
.schedule-heading-description .schedule-eyebrow { overflow-wrap: anywhere; }
.schedule-eyebrow {
  margin: 0 0 0.4rem;
  color: var(--accent);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
}
.schedule-back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  min-height: 2.75rem;
  align-self: start;
  border-radius: 50%;
  color: var(--fg);
  text-decoration: none;
}
.schedule-page .schedule-back .btn-icon { width: 1.5rem; height: 1.5rem; }
.schedule-back:hover { background: color-mix(in srgb, var(--accent) 8%, var(--bg)); }
.schedule-back:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.section-description {
  margin: 0.35rem 0 0;
  color: var(--muted);
  font-size: 0.875rem;
  line-height: 1.6;
}
.schedule-page .btn { min-height: 2.75rem; }
.schedule-page select,
.schedule-page input[type="text"] { font-size: 1rem; min-height: 3rem; }
.schedule-page .btn-primary { box-shadow: 0 3px 8px rgb(37 99 235 / 0.14); }
.schedule-toolbar {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--bg);
  padding: 0.625rem;
  border: 1px solid var(--line);
  border-radius: 1rem;
}
.schedule-toolbar .date-nav { margin: 0; gap: 0.25rem; }
.schedule-toolbar .date-step { border: 0; width: 2.75rem; min-height: 3rem; }
.schedule-toolbar .date-field { border: 0; padding-inline: 0; }
.schedule-toolbar .date-icon { display: none; }
.schedule-toolbar .date-text { font-size: 0.875rem; letter-spacing: -0.04em; }
.schedule-toolbar .btn { padding-inline: 0.625rem; }
.compose-card > summary,
.group-management > summary {
  min-height: 2.75rem;
  align-content: center;
  cursor: pointer;
  font-weight: 700;
  color: var(--fg);
}
.compose-card > summary { color: var(--accent); }
.compose-card > summary span { float: right; font-size: 0.8125rem; font-weight: 500; }
.compose-card[open] > summary,
.group-management[open] > summary { border-bottom: 1px solid var(--line); padding-bottom: 0.75rem; }
.compose-card .section-description,
.group-management .section-description { margin-bottom: 1rem; }
.group-management .worker-toolbar,
.group-management .worker-row { flex-wrap: nowrap; }
.group-management .worker-new,
.group-management .worker-row .field:first-of-type {
  flex: 1 1 0;
}
.group-management .worker-toolbar > .btn,
.group-management .worker-row > .group-count,
.group-management .worker-row > .row-delete { flex-shrink: 0; }
.compose-card .field-worker,
.compose-card .field-submit { grid-column: 1 / -1; }
.compose-card .field-submit { margin-top: 0.5rem; min-height: 3rem; }
.compose-card .shift-form { gap: 1rem 0.75rem; }
.count-badge {
  flex-shrink: 0;
  padding: 0.3rem 0.625rem;
  border-radius: 2rem;
  background: color-mix(in srgb, var(--accent) 8%, var(--bg));
  color: var(--accent);
  font-size: 0.75rem;
  font-weight: 600;
}
.schedule-page .schedule-card { padding: 0; }
.schedule-card > .card-head { margin: 0; padding: 1rem; }
.schedule-card .card-title { font-size: 1rem; }
.schedule-card .section-description { font-size: 0.75rem; }
.schedule-card > .empty,
.schedule-notice { padding: 1rem; }
.schedule-notice { margin: 0; color: var(--muted); font-size: 0.875rem; border-top: 1px solid var(--line); }
/* 내려받기 버튼 줄(엑셀 · PNG). 표 바로 위 오른쪽에 둔다. */
.schedule-export {
  display: flex;
  justify-content: flex-end;
  padding: 0.5rem 1rem;
}
.schedule-export { gap: 0.5rem; }
.schedule-export-png[hidden] { display: none; }
.schedule-export-png,
.schedule-export-xlsx { display: inline-flex; align-items: center; gap: 0.35rem; }
/* 테두리 색으로 둘을 구분한다 — 엑셀은 녹색, PNG 는 붉은색.
   .schedule-page .btn-quiet(:hover) 보다 구체적이어야 덮어쓴다. */
.schedule-export .schedule-export-xlsx { --export-color: #107B10; }
.schedule-export .schedule-export-png { --export-color: #ff0008; }
.schedule-export .schedule-export-xlsx,
.schedule-export .schedule-export-png,
.schedule-export .schedule-export-xlsx:hover,
.schedule-export .schedule-export-png:hover {
  border-color: var(--export-color);
}
.schedule-export .schedule-export-xlsx:hover,
.schedule-export .schedule-export-png:hover {
  background: color-mix(in srgb, var(--export-color) 8%, var(--bg));
}
.schedule-scroll {
  border-top: 1px solid var(--line);
  scrollbar-width: thin;
}
/* 표시 시간대. 기본은 하루 전체이고, 바꾸면 schedule.js(10번)가 이 요소에
   직접 값을 넣는다. 한 시간의 높이는 그대로 두고 표 높이만 줄어든다. */
.schedule-grid {
  --view-start: 0;
  --view-end: 24;
  --view-hours: 24;
  --hour-height: 2.5rem;
  --track-height: calc(var(--view-hours) * var(--hour-height));
}
.schedule-grid .col-head {
  position: sticky;
  top: 0;
  z-index: 3;
  height: 3rem;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.schedule-grid .time-column { z-index: 4; }
.schedule-grid .worker-column { min-width: 6rem; flex-basis: 6rem; }
.roster-card { position: relative; }
.roster-manage { position: absolute; top: 1rem; right: 1rem; }
.roster-details > summary {
  min-height: 2.75rem;
  padding-right: 7rem;
  align-content: center;
  cursor: pointer;
  font-size: 1rem;
  font-weight: 700;
}
.roster-count { color: var(--muted); font-size: 0.8125rem; white-space: nowrap; }
.roster-details[open] > summary { margin-bottom: 0.875rem; }
/* 칩은 눌리는 자리다. 손가락에 맞는 높이를 지킨다. */
.roster-card .worker-chip {
  max-width: 100%;
  min-height: 2.25rem;
  padding: 0.375rem 0.75rem;
  font-size: 0.8125rem;
  border-radius: 2rem;
}
.roster-card .card-title { font-size: 1rem; }
.roster-hint { margin: 0.75rem 0 0; color: var(--muted); font-size: 0.75rem; }
.group-gallery { display: grid; gap: 0.75rem; }
.group-tile {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.25rem;
  border: 1px solid var(--line);
  border-radius: 1.125rem;
  background: var(--bg);
  color: var(--fg);
  text-decoration: none;
}
.group-tile:hover { border-color: var(--accent); }
.group-tile:focus-visible,
.schedule-page summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.group-tile-icon {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 3rem;
  height: 3rem;
  border-radius: 0.875rem;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, var(--bg));
  font-size: 1.75rem;
}
.group-tile-body { display: flex; flex: 1; flex-direction: column; min-width: 0; gap: 0.25rem; }
.group-tile-body strong { overflow-wrap: anywhere; font-size: 1.0625rem; }
.group-tile-body > span { color: var(--muted); font-size: 0.8125rem; }
.group-tile-action { color: var(--accent); font-size: 1.25rem; }
.schedule-page .worker-toolbar {
  padding: 0.875rem;
  border-radius: 0.875rem;
  background: color-mix(in srgb, var(--accent) 4%, var(--bg));
}
.schedule-page .worker-row { padding: 0.875rem; gap: 0.75rem; }

/* 근무자 이름은 남는 너비를 채우고, 모바일에서는 편집 항목을 두 줄로 배치한다. */
#workers .worker-toolbar,
#workers .worker-new,
#workers .worker-row { flex-wrap: nowrap; }
#workers .worker-new,
#workers .worker-new .worker-name-field,
#workers .worker-row .worker-name-field { flex: 1 1 0; }
#workers .worker-toolbar > .btn,
#workers .worker-new .worker-color-field,
#workers .worker-row .worker-color-field,
#workers .worker-row .row-delete { flex-shrink: 0; }

@media (max-width: 47.999rem) {
  /* 내려받기 버튼은 좁은 화면에서 줄 전체를 반씩 나눠 가진다 (누르기 쉽게).
     PNG 가 감춰져 있으면(스크립트 없음) 엑셀 하나가 줄을 다 쓴다. */
  .schedule-export .schedule-export-xlsx,
  .schedule-export .schedule-export-png {
    flex: 1 1 0;
    min-width: 0;
    justify-content: center;
  }

  #workers .worker-toolbar {
    display: grid;
    grid-template-columns: 0.875rem minmax(0, 1fr) auto;
    grid-template-areas:
      "name name name"
      "dot color add";
    gap: 0.75rem;
  }

  #workers .worker-new { display: contents; }
  #workers .worker-new .worker-dot { grid-area: dot; }
  #workers .worker-new .worker-name-field { grid-area: name; }
  #workers .worker-new .worker-color-field { grid-area: color; }
  #workers .worker-new .worker-color-field select { width: 100%; }
  #workers .worker-new > .form-errors { grid-column: 1 / -1; }
  #workers .worker-toolbar > .btn { grid-area: add; min-height: 2.75rem; }

  #workers .worker-row:not([hidden]) {
    display: grid;
    grid-template-columns: 0.875rem minmax(0, 1fr) auto;
    grid-template-areas:
      "name name name"
      "dot color delete";
  }

  #workers .has-reorder .worker-row {
    grid-template-columns: 2.2rem 0.875rem minmax(0, 1fr) auto;
    grid-template-areas:
      "grip name name name"
      "grip dot color delete";
  }

  #workers .worker-row .row-grip {
    grid-area: grip;
    align-self: stretch;
    width: 100%;
    height: auto;
    min-height: 2.75rem;
    background: color-mix(in srgb, var(--muted) 4%, var(--bg));
  }

  #workers .worker-row .row-grip .grip-icon {
    width: 0.5rem;
    height: 0.8rem;
  }

  #workers .worker-row .row-grip:hover,
  #workers .worker-row .row-grip:focus-visible {
    background: color-mix(in srgb, var(--muted) 8%, var(--bg));
  }

  #workers .worker-row .worker-dot { grid-area: dot; }
  #workers .worker-new .worker-dot,
  #workers .worker-row .worker-dot {
    align-self: center;
    justify-self: center;
    width: 0.875rem;
    height: 0.875rem;
    border-radius: 50%;
  }
  #workers .worker-row .worker-name-field { grid-area: name; }
  #workers .worker-row .worker-color-field { grid-area: color; }
  #workers .worker-row .worker-color-field select { width: 100%; }
  #workers .worker-row .row-delete { grid-area: delete; min-height: 2.75rem; }
  #workers .worker-row > .form-errors { grid-column: 1 / -1; }
}

.schedule-page .check { min-height: 2.75rem; }
.schedule-page .row-delete:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.schedule-page .helptext { line-height: 1.8; }
@media (min-width: 48rem) {
  .schedule-page main { padding: 2rem 2rem 4rem; }
  .group-gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .schedule-toolbar .date-nav { max-width: 30rem; margin-inline: auto; }
  .schedule-toolbar .date-icon { display: block; }
  .schedule-toolbar .date-text { font-size: 1rem; }
  .compose-card .shift-form { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 23rem) {
  .schedule-page main { padding-inline: 0.625rem; }
  .schedule-toolbar { padding-inline: 0.25rem; }
  .schedule-toolbar .date-step { width: 2.75rem; }
  .schedule-toolbar .date-text { font-size: 0.8125rem; }
}

/* ─── 표시 시간대 ──────────────────────────────────────────────────────── */
/* 시간 열 머리의 톱니바퀴와 그것이 여는 모달 (board.html, schedule.js 10번). */

.view-range-open {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}

.view-range-open[hidden] {
  display: none;
}

.view-range-open:hover {
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  color: var(--fg);
}

.view-range-open:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.view-range-open svg {
  width: 1.25rem;
  height: 1.25rem;
}

.view-range-dialog {
  width: calc(100% - 2rem);
  max-width: 22rem;
  padding: 1.25rem;
  border: 1px solid var(--line);
  border-radius: 0.75rem;
  background: var(--bg);
  color: var(--fg);
  box-shadow: 0 10px 30px rgb(0 0 0 / 0.18);
}

.view-range-dialog::backdrop {
  background: rgb(0 0 0 / 0.4);
}

.view-range-form {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.view-range-title {
  margin: 0;
  font-size: 1.0625rem;
}

.view-range-form .section-description {
  margin: 0;
}

.view-range-fields {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
}

.view-range-error {
  margin: 0;
}

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

.view-range-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.5rem;
}

.view-range-actions .btn-quiet:first-child {
  margin-right: auto;
}

/* ─── 보기 전환 탭 (일간 · 주간 · 월간) ──────────────────────────────────
   세 화면이 같은 것을 쓴다 (templates/schedules/_view_tabs.html).
   평범한 링크 셋을 한 줄의 분할 버튼처럼 보이게 한다. 지금 화면은
   aria-current 로 표시되고, 모양도 그 속성을 따른다 — 클래스를 따로 두면
   둘이 어긋날 자리가 생긴다. */

.view-tabs {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.25rem;
  padding: 0.25rem;
  border: 1px solid var(--line);
  border-radius: 0.875rem;
  background: color-mix(in srgb, var(--muted) 8%, var(--bg));
}

.view-tab {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 2.5rem;
  border-radius: 0.625rem;
  color: var(--muted);
  font-size: 0.9375rem;
  font-weight: 600;
  text-decoration: none;
}

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

.view-tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.view-tab[aria-current="page"] {
  background: var(--bg);
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.08);
  color: var(--accent);
}

/* ─── 주간 보기 ──────────────────────────────────────────────────────────
   하루가 한 덩어리다. 머리(날짜)를 누르면 그 날의 일간 화면, 일정 줄을
   누르면 그 일정의 상세 시트가 열린 일간 화면으로 간다. */

.week-days {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
}

.week-day {
  padding-block: 0.625rem;
  border-top: 1px solid var(--line);
}

.week-day:first-child {
  border-top: 0;
  padding-top: 0;
}

.week-day-head {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.25rem 0.375rem;
  border-radius: 0.5rem;
  color: var(--fg);
  text-decoration: none;
}

.week-day-head:hover {
  background: color-mix(in srgb, var(--accent) 6%, var(--bg));
}

.week-day-head:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.week-day-date {
  min-width: 1.75rem;
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: -0.03em;
}

.week-day-name {
  color: var(--muted);
  font-size: 0.875rem;
}

.week-day.is-sunday .week-day-date,
.week-day.is-sunday .week-day-name {
  color: #dc2626;
}

.week-day.is-saturday .week-day-date,
.week-day.is-saturday .week-day-name {
  color: #2563eb;
}

.week-today-badge {
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  background: var(--accent);
  color: var(--bg);
  font-size: 0.6875rem;
  font-weight: 700;
}

.week-day-count {
  margin-left: auto;
  color: var(--muted);
  font-size: 0.8125rem;
}

.week-day.is-today .week-day-head {
  background: color-mix(in srgb, var(--accent) 8%, var(--bg));
}

.week-shifts {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  margin: 0.375rem 0 0;
  padding: 0 0 0 2.25rem;
  list-style: none;
}

.week-shift {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 0.5rem;
  row-gap: 0.15rem;
  min-height: 2.75rem;
  padding: 0.45rem 0.7rem;
  border: 1px solid color-mix(in srgb, var(--worker-color) 45%, var(--bg));
  border-radius: 0.625rem;
  background: color-mix(in srgb, var(--worker-color) 14%, var(--bg));
  color: var(--fg);
  font-size: 0.9375rem;
  text-decoration: none;
}

.week-shift:hover {
  border-color: var(--worker-color);
}

.week-shift:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.week-shift-name {
  min-width: 0;
  overflow: hidden;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.week-shift-time {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
}

/* 비고는 아래 줄로 내려 전체 폭을 쓴다. */
.week-shift-note {
  flex-basis: 100%;
  padding-left: 1.375rem;
  color: var(--muted);
  font-size: 0.8125rem;
  overflow-wrap: anywhere;
}

.week-empty {
  margin: 0.25rem 0 0;
  padding-left: 2.625rem;
  color: var(--muted);
  font-size: 0.8125rem;
}

/* ─── 월간 보기 (달력) ───────────────────────────────────────────────────
   일곱 칸 격자. 칸 하나가 링크이고, 칸 안의 근무자는 색 띠로 적는다.
   좁은 폭에서는 이름만, 넓어지면 출근 시각까지 보인다. */

.month-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  border-top: 1px solid var(--line);
  border-left: 1px solid var(--line);
}

.month-weekday {
  padding: 0.375rem 0;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 700;
  text-align: center;
}

.month-day {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
  min-height: 5.25rem;
  padding: 0.25rem 0.1875rem 0.375rem;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  color: var(--fg);
  text-decoration: none;
}

.month-day:hover {
  background: color-mix(in srgb, var(--accent) 6%, var(--bg));
}

.month-day:focus-visible {
  position: relative;
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.month-day-number {
  align-self: flex-start;
  min-width: 1.5rem;
  padding: 0.05rem 0.25rem;
  border-radius: 999px;
  font-size: 0.8125rem;
  font-weight: 600;
  text-align: center;
}

.is-sunday.month-weekday,
.month-day.is-sunday .month-day-number {
  color: #dc2626;
}

.is-saturday.month-weekday,
.month-day.is-saturday .month-day-number {
  color: #2563eb;
}

.month-day.is-today .month-day-number {
  background: var(--accent);
  color: var(--bg);
}

/* 앞뒤 달의 날짜. 칸은 남겨 두되 이번 달이 아님이 드러나게 흐린다. */
.month-day.is-outside {
  background: color-mix(in srgb, var(--muted) 6%, var(--bg));
}

.month-day.is-outside > * {
  opacity: 0.5;
}

.month-entry {
  display: flex;
  gap: 0.25rem;
  min-width: 0;
  padding: 0.05rem 0.25rem;
  border-left: 3px solid var(--worker-color);
  border-radius: 0.25rem;
  background: color-mix(in srgb, var(--worker-color) 16%, var(--bg));
  font-size: 0.6875rem;
  line-height: 1.4;
}

.month-entry-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 휴대폰 폭에서는 칸이 손가락 하나 너비라 시각까지 넣으면 이름이 잘린다. */
.month-entry-time {
  display: none;
  margin-left: auto;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.month-more {
  padding-left: 0.25rem;
  color: var(--muted);
  font-size: 0.6875rem;
  font-weight: 600;
}

@media (min-width: 48rem) {
  .month-day {
    min-height: 6.5rem;
    padding-inline: 0.3125rem;
  }

  .month-entry {
    font-size: 0.75rem;
  }

  .month-entry-time {
    display: inline;
  }
}

@media (prefers-color-scheme: dark) {
  .week-day.is-sunday .week-day-date,
  .week-day.is-sunday .week-day-name,
  .is-sunday.month-weekday,
  .month-day.is-sunday .month-day-number {
    color: #f87171;
  }

  .week-day.is-saturday .week-day-date,
  .week-day.is-saturday .week-day-name,
  .is-saturday.month-weekday,
  .month-day.is-saturday .month-day-number {
    color: #6ea8fe;
  }
}

/* 오늘 표시는 일요일·토요일 색보다 우선한다. */
.month-day.is-today.is-sunday .month-day-number,
.month-day.is-today.is-saturday .month-day-number {
  color: var(--bg);
}
