:root {
  /* 공식 Springfield Commonwealth Academy 팔레트(현행 사이트 근사). */
  --primary: #1e3a5f;
  --secondary: #2c5d8a;
  --gold: #c9a227;
  --bg: #f5f7fa;
  --surface: #ffffff;
  --surface-alt: #fafcfa;
  --text: #16202b;
  --text-muted: #4a5563;
  --border: #d3dae2;
  /* 상호작용 색은 primary(남색)로 통일 — 흰 배경에서 대비가 충분하다. gold 는 장식(테두리·
     강조)에만 아껴 쓴다: 흰 배경 위 focus 링으로 쓰면 대비가 부족해 접근성이 깨진다. */
  --accent: #1e3a5f;
  --accent-strong: #16324f;
  --focus: #1e3a5f;
  --danger: #a11221;
  --danger-bg: #fdecee;
  --success: #14603a;
  --success-bg: #e8f4ec;
  --info: #1e3a5f;
  --info-bg: #eaf0fb;
  --radius: 12px;
  --control-min: 44px;
  --max-width: 40rem;
}

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

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", "Apple SD Gothic Neo",
    "Malgun Gothic", "Noto Sans KR", sans-serif;
  font-size: 16px;
  line-height: 1.6;
  word-break: keep-all;
}

.page {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 1.25rem;
}

.site-header {
  padding: 0.5rem 0 0.85rem;
  margin: 0 0 1.25rem;
  /* gold 는 여기 얇은 강조 테두리로만 아껴 쓴다(브랜드 악센트). */
  border-bottom: 2px solid var(--gold);
}

/* 머리의 안쪽 줄. 배경·테두리는 바깥이 지고 **폭과 배치는 여기가** 진다 — 콘솔의 남색 띠는
   화면을 가로지르면서 그 안의 내용만 가운데로 모으기 때문이다(둘이 같은 요소면 둘 중 하나만
   얻는다). 좁은 페이지에서는 이 줄이 페이지 폭 그대로여서 아무 일도 하지 않는다. */
.site-header__in {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

/* 로고 크기의 **정본은 여기 한 벌**이다(W1-12). 예전에는 표면마다 값이 달라(기본 52 · 콘솔
   38 · 학생 콘솔 좁은 폭 30 · 포털 48/44) 화면을 옮길 때마다 머리글이 커졌다 작아졌고, 사람은
   그것을 다른 사이트로 넘어온 신호로 읽었다. 데스크톱 48px 는 학교명 두 줄과 같은 키이고,
   좁은 폭 44px 는 최소 터치 타깃과 같은 값이다 — 로고가 곧 홈 링크의 히트박스이기 때문에
   그 아래로는 내려갈 수 없다. 표면별 오버라이드를 다시 만들지 말 것. */
.site-logo {
  flex: 0 0 auto;
  width: 48px;
  height: 48px;
}

@media (max-width: 900px) {
  .site-logo {
    width: 44px;
    height: 44px;
  }
}

/* 로고와 학교명을 함께 감싸는 **홈으로 가는 문**(W1-01). 앵커는 자기 자식을 예전 배치 그대로
   세운다 — 부모의 gap 을 물려받으므로 화면마다 다른 간격이 그대로 유지되고, 마크업이 한 겹
   늘었다는 사실이 눈에 보이지 않는다. min-height 44px 는 손가락이 닿는 최소 크기다(W1-12 —
   로고가 44px 로 줄어드는 좁은 폭에서도 히트박스는 이 값이 지킨다).
   초점 링은 전역 :focus-visible 정본을 그대로 받는다(여기서 다시 그리지 않는다). */
.site-home {
  display: flex;
  align-items: center;
  gap: inherit;
  min-width: 0;
  min-height: 44px;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
}

.site-header__titles {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.site-name {
  margin: 0;
  font-size: 0.82rem;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: 0.01em;
  color: var(--secondary);
}

.site-header h1 {
  font-size: 1.35rem;
  margin: 0;
  color: var(--primary);
}

main {
  display: block;
}

h1,
h2 {
  line-height: 1.3;
}

p {
  color: var(--text-muted);
}

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.25rem;
  margin: 0 0 1rem;
}

.card > h2 {
  margin-top: 0;
  font-size: 1.15rem;
  color: var(--text);
}

.card > :last-child {
  margin-bottom: 0;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--control-min);
  min-width: var(--control-min);
  padding: 0.5rem 1.1rem;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  background: var(--accent);
  color: #ffffff;
  font: inherit;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}

.btn:hover {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
}

.btn--secondary {
  background: var(--surface);
  color: var(--accent-strong);
}

/*
 * 보조 버튼은 hover/focus 에서도 **읽혀야 한다** — 전 화면에서.
 *
 * 전역 .btn:hover 가 배경을 --accent-strong 으로 덮는데 .btn--secondary 의 글자색도 같은
 * --accent-strong 이다. 두 규칙이 겹치는 순간 어두운 바탕에 어두운 글자가 되어 버튼이 **빈 칸으로
 * 보인다**(계산 대비비 1.00). 배경을 되돌리지 않고 글자를 흰색으로 올린다 — 일차 버튼이 이미 그
 * 조합이므로 두 버튼이 같은 규칙으로 읽힌다.
 *
 * [:focus 도 함께 정하는 이유] 전역 규칙이 배경을 어둡게 하는 것은 hover 뿐이다. 그래서 글자만
 * 흰색으로 올리면 키보드로 짚은 버튼은 흰 바탕에 흰 글자가 되어 — hover 결함을 고친 자리에 똑같은
 * 결함이 마우스를 쓰지 않는 사람 쪽에만 남는다. 두 상태에 같은 짝(어두운 배경 + 흰 글자)을 준다.
 *
 * [왜 전역인가·K2] 앞 판의 고침은 data-screen **세 값**에 묶여 있었다(attendance ·
 * guardian-attendance · portal-login). 실 브라우저 전수 감사가 그 목록 밖에서 일곱 화면을 더
 * 찾아냈고(boundary B1 · student M-01), 그중에는 **이 제품에서 모든 사람이 반드시 지나는 단 하나의
 * 화면**인 포털 대시보드의 「로그아웃」이 있었다. 화면 이름 목록은 다음에 늘어나는 화면을 또
 * 빠뜨린다 — 화면을 세지 않고 규칙을 올린다. 세 화면에서 보이는 것은 한 픽셀도 달라지지 않는다
 * (같은 선언이 좁은 선택자에서 넓은 선택자로 옮겨 왔을 뿐이다).
 */
.btn--secondary:hover,
.btn--secondary:focus {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  color: #ffffff;
}

/*
 * 도움말 한 줄(.hint)은 지금까지 **규칙이 아예 없었다** — 라벨 아래의 형식·용도 안내가 본문과
 * 같은 크기로 서서 무엇이 안내이고 무엇이 내용인지 갈리지 않았다. 작게 만들되 읽을 수 있는 아래
 * 끝(14px)을 넘지 않는다: 이 줄이 실어 나르는 것은 「010-1234-5678」·「YYYY-MM」처럼 **정확히
 * 옮겨 적어야 하는 값**이다(applicant A-05·A-06).
 */
.hint {
  margin: 0.35rem 0 0;
  color: var(--text-muted);
  font-size: 0.875rem;
  line-height: 1.5;
}

.status {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.75rem 1rem;
  margin: 0 0 1rem;
}

.status--error {
  border-color: var(--danger);
  background: var(--danger-bg);
  color: var(--danger);
}

.status--success {
  border-color: var(--success);
  background: var(--success-bg);
  color: var(--success);
}

.status--status {
  border-color: var(--info);
  background: var(--info-bg);
  color: var(--info);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

/*
 * 네이티브 날짜·달 위젯의 초점은 **눈에 보여야 한다**.
 *
 * 전역 :focus-visible 은 그 둘에서 실제로 서지 않았다 — 초점 전후의 계산된 outline·box-shadow 를
 * 견주면 #att-date·#gatt-month·#satt-month 세 요소만 **아무 값도 변하지 않는다**(boundary
 * B11 실측·모바일이 더 넓다). focus-visible 은 브라우저의 휴리스틱이고 그 휴리스틱은 위젯마다
 * 다르다. 이 둘에서는 휴리스틱에 기대지 않고 :focus 로 직접 그린다 — 마우스로 연 고르개에도
 * 테두리가 서지만, 그것은 초점이 그 위젯에 있다는 **사실 그대로**다.
 */
input[type="month"]:focus,
input[type="date"]:focus {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

@media (min-width: 48rem) {
  .page {
    padding: 2rem 1.25rem;
  }
  .site-header h1 {
    font-size: 1.6rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ── 단계(fieldset)·폼 필드 ─────────────────────────────────────────────── */
.step {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem 1rem 1.25rem;
  margin: 0 0 1.25rem;
  min-width: 0; /* fieldset 이 그리드/플렉스에서 넘치지 않게 */
}

.step__legend {
  font-weight: 700;
  color: var(--primary);
  padding: 0 0.4rem;
}

.field {
  margin: 0 0 1rem;
}

.field__label {
  display: block;
  font-weight: 600;
  margin: 0 0 0.35rem;
  color: var(--text);
}

.field__control {
  display: block;
  width: 100%;
  min-height: var(--control-min);
  padding: 0.55rem 0.7rem;
  font: inherit;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

textarea.field__control {
  resize: vertical;
  line-height: 1.5;
}

.field__control:focus-visible {
  border-color: var(--focus);
}

.field__count {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.85rem;
  color: var(--text-muted);
}

/**
 * 고정 국가번호 + 번호 칸(S6). 접두어는 **읽기만 하는 표시**라 입력처럼 보이지 않아야 한다 —
 * 테두리도 배경도 칸과 다르게 두어, 지우거나 고칠 수 있는 자리가 아님을 모양이 먼저 말한다.
 */
.field__group {
  display: flex;
  align-items: stretch;
  gap: 0.4rem;
}

.field__prefix {
  display: flex;
  align-items: center;
  padding: 0 0.6rem;
  font: inherit;
  color: var(--text-muted);
  background: var(--surface-muted, transparent);
  border: 1px dashed var(--border);
  border-radius: 6px;
  white-space: nowrap;
}

/** 예비 수단 접기(S6). 펼치기 전에는 한 줄이고, 펼쳐도 주 입력보다 조용하다. */
.op-fallback {
  margin: 0.6rem 0 0;
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: 8px;
}

.op-fallback__summary {
  cursor: pointer;
  font-size: 0.92rem;
  color: var(--text-muted);
}

.field--check {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
}

.field__check {
  flex: 0 0 auto;
  width: 1.5rem;
  height: 1.5rem;
  margin: 0;
}

/* 체크칸 자체는 22×22px 였다(student L-04). 체크를 뒤집는 표적은 for 로 이어진 이 라벨이므로,
   줄 전체를 44px 로 세우면 손가락이 닿는다 — 체크칸의 그림 크기를 키우지 않고. */
.field__check-label {
  display: flex;
  align-items: center;
  min-height: 44px;
  color: var(--text);
}

/* ── 슬롯 목록(라디오) ──────────────────────────────────────────────────── */
.slots {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.6rem;
}

.slot {
  position: relative;
}

/* 라디오 자체는 접근성(포커스·키보드)을 위해 남기되, 라벨을 넓은 44px+ 타깃으로 만든다. */
.slot__radio {
  position: absolute;
  top: 50%;
  left: 0.85rem;
  transform: translateY(-50%);
  width: 1.25rem;
  height: 1.25rem;
  margin: 0;
}

.slot__label {
  display: flex;
  align-items: center;
  min-height: var(--control-min);
  padding: 0.6rem 0.9rem 0.6rem 2.6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  cursor: pointer;
}

/* 선택 상태는 색만으로 표시하지 않는다 — 굵은 테두리 + 체크 표식 + 굵은 글씨를 함께 준다. */
.slot__radio:checked + .slot__label {
  border-color: var(--accent);
  border-width: 2px;
  padding-left: calc(2.6rem - 1px);
  font-weight: 700;
  background: var(--info-bg);
}

.slot__radio:checked + .slot__label::after {
  content: "✓ 선택됨";
  margin-left: auto;
  padding-left: 0.75rem;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--accent-strong);
}

.slot__radio:focus-visible + .slot__label {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

.selected-slot {
  font-weight: 600;
  color: var(--text);
}

/* hidden 속성을 항상 존중한다(다른 display 규칙이 이겨 새어 보이지 않게). */
[hidden] {
  display: none !important;
}

@media (min-width: 48rem) {
  .slots {
    grid-template-columns: 1fr 1fr;
  }
}

/* ── 예약 현황(상태 타임라인·상세·조치) ─────────────────────────────────── */
.status-view {
  margin: 0;
}

/* 예약 상세(시각·신청 번호)를 정의 목록으로 — 라벨/값 쌍이 스크린리더에 의미 있게 읽힌다. */
.status-detail {
  margin: 1rem 0 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.4rem 0.9rem;
}

.status-detail dt {
  font-weight: 600;
  color: var(--text);
}

.status-detail dd {
  margin: 0;
  color: var(--text-muted);
  word-break: break-word;
}

/* 조치 영역(취소·새로고침) — 44px 타깃을 나란히, 좁은 화면에서 접힌다. */
.status-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 1.25rem 0 0;
}

.status-actions .btn {
  flex: 0 0 auto;
}

/* 취소 확인 문구. 색만으로 뜻을 전하지 않게 status 컴포넌트를 재사용한다. */
#cancel-status {
  margin-top: 1rem;
}

@media (prefers-contrast: more) {
  :root {
    --border: #1a1d21;
    --text-muted: #2b2f35;
  }
  .btn {
    border-width: 2px;
  }
  .slot__label {
    border-width: 2px;
  }
}

/* ── 콘솔 공용 셸의 탭 내비(S1) ─────────────────────────────────────────── */
/* 네 화면으로 가는 진짜 링크 넷 + 로그아웃. 현재 탭은 **색만으로** 말하지 않는다 — 굵은
   밑줄이 함께 서고, 그 사실의 정본은 마크업의 aria-current 다. 타깃은 다른 조작과 같은
   44px(--control-min) 이다. */
.op-tabs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.15rem;
  margin: 0 0 1.5rem;
  border-bottom: 1px solid var(--border);
}

.op-tabs__link {
  display: inline-flex;
  align-items: center;
  min-height: var(--control-min);
  padding: 0 0.85rem;
  color: var(--text-muted);
  font-weight: 600;
  text-decoration: none;
  /* 밑줄 자리는 평소에도 잡아 둔다 — 탭이 바뀔 때 글자가 위아래로 움직이지 않는다. */
  border-bottom: 3px solid transparent;
  margin-bottom: -1px;
}

.op-tabs__link:hover {
  color: var(--primary);
}

.op-tabs__link[aria-current="page"] {
  color: var(--primary);
  border-bottom-color: var(--primary);
}

/* 전역 규칙과 같은 3px 이되 **안쪽으로** 그린다 — 탭은 서로 붙어 있고 모바일에서는 화면
   가장자리에 닿으므로, 바깥으로 그린 링은 이웃 탭과 화면 밖에 잘려 절반만 보인다. */
.op-tabs__link:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: -3px;
}

/* 로그아웃은 화면 이동이 아니라 세션 종료다. 탭 줄의 반대쪽 끝에 세워 오조작을 줄인다. */
.op-tabs__logout {
  margin-left: auto;
}

/* 로그아웃 실패 문구는 탭을 밀어내지 않고 자기 줄을 차지한다. */
.op-tabs > [role="alert"] {
  flex: 1 0 100%;
  margin: 0.5rem 0;
}

/* 좁은 화면(≤900px·스케치와 같은 경계)에서는 탭이 화면 아래 고정 바로 내려간다. 운영자는
   한 손에 폰을 들고 복도에서 이 화면을 본다 — 화면을 오가는 손가락이 닿는 자리는 위가
   아니라 아래다. 칸은 이 화면에 실제로 선 탭 수만큼이므로 좁은 폭에서도 가로로 밀리지
   않는다(가로 스크롤 0). */
@media (max-width: 900px) {
  .op-tabs {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 50;
    display: grid;
    /* [칸 수는 다섯이 아니다] 이 바에 서는 탭 수는 화면마다 다르다 — host 게이트가 닿지 못하는
       화면의 탭을 접기 때문이다(§4.5): 관리자 센터 셋, counsel 콘솔 넷, attendance 콘솔·단일
       오리진 다섯. 칸을 다섯으로 못 박으면 셋인 화면에서는 오른쪽 5분의 2 가 **아무 일도
       일어나지 않는 빈 자리**로 남는다(엄지가 그 자리를 눌러도 화면이 바뀌지 않는다).
       그래서 칸 수는 문서가 스스로 말한다 — 마지막 앵커가 포털 문(로그아웃 자리·아래에서 자기
       줄을 쓴다)인 화면은 그 링크를 칸으로 세지 않는다.

       [왜 minmax(min-content, …) 인가] 라벨 길이는 서로 다르다(「달력」둘 vs 「포털로 돌아가기」
       일곱). 고르게 나누면 320px 에서 긴 라벨이 두 줄로 접히고 바가 두 배가 된다. 아래
       white-space: nowrap 과 함께, 각 칸은 **자기 글자가 한 줄로 설 만큼**을 먼저 갖고 남은
       폭을 1fr 로 고르게 나눠 갖는다.

       [기본이 다섯인 이유] 칸을 줄이는 것은 아래 :has() 규칙이다. :has() 를 모르는 옛
       WebKit(학교에 남아 있는 오래된 아이패드)에서는 이 기본값이 그대로 서고, 그 화면은
       오늘과 같은 그림이 된다 — 빈 칸은 남지만 넘치지는 않는다. 기본을 셋으로 두면 반대로
       그 기기에서 탭 다섯이 화면 밖으로 밀린다. */
    grid-template-columns: repeat(5, minmax(min-content, 1fr));
    gap: 0;
    margin: 0;
    background: var(--surface);
    border-top: 1px solid var(--border);
    border-bottom: 0;
    /* 아이폰 홈 인디케이터 아래로 탭이 내려가지 않게. */
    padding-bottom: env(safe-area-inset-bottom);
  }

  /* 다섯째 탭이 없는 화면(counsel 콘솔·넷)은 넷으로, 넷째도 없는 화면(관리자 센터·셋)은
     셋으로 좁힌다. 둘 다 맞는 화면에서는 뒤 규칙이 이긴다(같은 명시도).
     마지막 앵커가 포털 문(op-tabs__logout 자리·자기 줄을 쓴다)인 화면은 그 링크를 칸으로
     세지 않는다 — 그래서 관리자 센터의 앵커 넷이 칸 셋이 된다. */
  .op-tabs:not(:has(> a:nth-of-type(5):not(.op-tabs__logout))) {
    grid-template-columns: repeat(4, minmax(min-content, 1fr));
  }

  .op-tabs:not(:has(> a:nth-of-type(4):not(.op-tabs__logout))) {
    grid-template-columns: repeat(3, minmax(min-content, 1fr));
  }

  /* 밑줄은 바의 위쪽 모서리로 옮긴다 — 아래쪽은 화면 끝에 닿아 보이지 않는다. */
  .op-tabs__link {
    justify-content: center;
    min-height: 56px;
    padding: 0 0.2rem;
    font-size: 0.9rem;
    text-align: center;
    /* 한 낱말이 두 줄로 접히면 56px 바가 두 배가 된다. 접지 않는 대신 위 칸 규칙이 그 글자가
       설 폭을 먼저 준다. */
    white-space: nowrap;
    margin-bottom: 0;
    border-bottom: 0;
    border-top: 3px solid transparent;
  }

  .op-tabs__link[aria-current="page"] {
    border-top-color: var(--primary);
    background: var(--info-bg);
  }

  /* 로그아웃은 탭 **위**에 선다. 화면 맨 아래 모서리는 탭을 누르려는 엄지가 가장 자주
     닿는 자리이고, 그 자리에 세션을 끝내는 버튼을 두면 오조작 한 번이 로그아웃이 된다. */
  .op-tabs__logout {
    order: -1;
    grid-column: 1 / -1;
    justify-self: end;
    margin: 0.4rem 0.75rem;
  }

  .op-tabs > [role="alert"] {
    grid-column: 1 / -1;
    margin: 0 0.75rem 0.5rem;
  }

  /* 고정 바가 본문 끝을 가리지 않게 콘솔 화면만 아래 여백을 둔다 — 셸이 심은 화면 이름이
     여기서도 "이 문서에는 탭 바가 있다"는 사실의 근거가 된다(학생 화면에는 없다). */
  body[data-screen] main {
    padding-bottom: calc(9rem + env(safe-area-inset-bottom));
  }
}

/* ── 콘솔의 남색 상단 바와 넓은 캔버스(승인된 프로토타입) ─────────────────
   콘솔의 첫 인상은 **띠 하나**다: 왼쪽에 학교, 오른쪽에 화면 넷. 학생 화면과 같은 좁은
   가운데 칸을 쓰면 달력이 묻는 것(이레 × 열 시간)이 그 폭 안에서 잘린다 — 기능은 다
   있는데 토요일이 화면 밖에 있다. 그래서 이 캔버스는 화면을 쓴다.

   1600px 는 스케치가 정한 상한이다. 상한이 아예 없으면 초광폭 모니터에서 한 줄의 눈이
   1미터를 건너가고, 그때 사람은 목요일 칸을 보며 왼쪽 끝의 시각을 기억해야 한다. */
.page--console,
.page--student {
  max-width: none;
  /* 띠가 화면을 가로지르려면 페이지가 좌우 여백을 지면 안 된다. 여백은 안쪽 줄이 진다. */
  padding: 0 0 2rem;
}

.page--console .site-header,
.page--student .site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  padding: 0;
  margin: 0 0 1.25rem;
  background: var(--primary);
  /* 스케치와 같은 3px. 남색 위에서는 2px 이 거의 보이지 않는다. */
  border-bottom: 3px solid var(--gold);
}

.page--console .site-header__in,
.page--student .site-header__in {
  max-width: 100rem;
  margin: 0 auto;
  padding: 0.6rem 1.25rem;
  gap: 0.65rem;
}

/* 남색 위에서는 두 줄의 무게가 뒤집힌다 — 큰 글자는 학교 이름이고, 제품 라벨은 그 아래
   한 줄의 부제다. 학생 화면(흰 배경)에서는 지금까지대로다. */
.page--console .site-name,
.page--student .site-name {
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: #ffffff;
}

.page--console .site-header h1,
.page--student .site-header h1 {
  font-size: 0.78rem;
  font-weight: 400;
  line-height: 1.35;
  color: #c8d6e8;
}

.page--console main,
.page--student main {
  max-width: 100rem;
  margin: 0 auto;
  padding: 0 1.25rem;
}

/* 넓은 화면에서만 탭이 띠 안에 선다. 좁은 화면에서 이 줄은 화면 아래 고정 바이므로
   (위 @media max-width:900px) 여기의 색·모양이 닿으면 흰 바 위에 흰 글자가 남는다. */
@media (min-width: 901px) {
  .page--console .site-header .op-tabs {
    margin: 0 0 0 auto;
    gap: 0.25rem;
    border-bottom: 0;
  }

  .page--console .site-header .op-tabs__link {
    padding: 0 1rem;
    border-bottom: 0;
    margin-bottom: 0;
    border-radius: 999px;
    color: #dce6f2;
  }

  .page--console .site-header .op-tabs__link:hover {
    background: rgba(255, 255, 255, 0.12);
    color: #ffffff;
  }

  /* 현재 화면. 흰 알약은 남색 띠에서 가장 먼 표현이고, 그 사실의 정본은 여전히
     마크업의 aria-current 다(색만으로 말하지 않는다). */
  .page--console .site-header .op-tabs__link[aria-current='page'] {
    background: #ffffff;
    color: var(--primary);
    border-bottom-color: transparent;
  }

  .page--console .site-header .op-tabs__link:focus-visible {
    outline-color: var(--gold);
  }

  /* 세션을 끝내는 버튼은 띠의 오른쪽 끝, 탭과 한 칸 떨어져 선다(오조작 감소·전과 같은 근거). */
  .page--console .site-header .op-tabs__logout {
    margin-left: 0.75rem;
    background: transparent;
    border-color: #3d5b80;
    color: #dce6f2;
  }

  .page--console .site-header .op-tabs__logout:hover {
    background: rgba(255, 255, 255, 0.12);
    border-color: #6b88ad;
  }

  .page--console .site-header > .site-header__in > .op-tabs > [role='alert'] {
    flex: 1 0 100%;
    margin: 0.4rem 0 0;
    color: #ffffff;
  }
}

/* ── 학생 화면의 상단 내비(Stage B1) ─────────────────────────────────────
   운영자의 탭 넷과 **자리는 같고 모양은 다르다**: 학생이 오갈 곳은 둘뿐이므로 좁은 화면에서
   화면 아래 고정 바로 내려갈 이유가 없다(고정 바는 그 자체로 본문 한 줄을 가린다). 그래서
   이 줄은 어느 폭에서도 띠 안에 남고, 자리가 모자라면 줄바꿈으로 아래에 한 줄을 더 쓴다. */
.s-tabs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem;
  margin: 0 0 0 auto;
}

.s-tabs__link {
  display: inline-flex;
  align-items: center;
  min-height: var(--control-min);
  padding: 0 1rem;
  border-radius: 999px;
  color: #dce6f2;
  font-weight: 600;
  text-decoration: none;
}

.s-tabs__link:hover {
  background: rgba(255, 255, 255, 0.12);
  color: #ffffff;
}

/* 지금 있는 화면. 흰 알약은 남색 띠에서 가장 먼 표현이고, 그 사실의 정본은 여전히
   마크업의 aria-current 다(색만으로 말하지 않는다·콘솔과 같은 규약). */
.s-tabs__link[aria-current="page"] {
  background: #ffffff;
  color: var(--primary);
}

.s-tabs__link:focus-visible {
  outline: 3px solid var(--gold);
  outline-offset: -3px;
}

.s-tabs__logout {
  margin-left: 0.5rem;
  background: transparent;
  border-color: #3d5b80;
  color: #dce6f2;
}

/* 좁은 화면에서 이 줄은 브랜드 아래 **한 줄**로 내려간다. 같은 줄에 두면 학교 정식명이 세
   줄로 접히고 내비가 세로로 쌓여, 띠 하나가 폰 화면의 절반을 먹는다(실측). 화면 아래 고정
   바로 내리지 않는 이유는 그것이 곧 본문 한 줄을 늘 가리는 덮개이기 때문이다. */
@media (max-width: 900px) {
  .page--student .site-header__in {
    flex-wrap: wrap;
    padding: 0.45rem 0.9rem 0.5rem;
    gap: 0.5rem;
  }

  .page--student .site-name {
    font-size: 0.82rem;
  }

  .page--student .site-header h1 {
    font-size: 0.72rem;
  }

  .s-tabs {
    flex: 1 0 100%;
    margin: 0;
    gap: 0.2rem;
  }

  .s-tabs__link {
    min-height: 2.25rem;
    padding: 0 0.7rem;
    font-size: 0.9rem;
  }

  /* 좁은 폭에서만 36px 였다(boundary B7·두 폭 비교표의 유일한 반응형 결함). 44px 로 올린다 —
     이 줄에서 되돌릴 수 없는 결과를 내는 유일한 버튼이다. */
  .s-tabs__logout {
    min-height: 2.75rem;
    margin-left: auto;
    padding: 0 0.7rem;
    font-size: 0.9rem;
  }
}

.s-tabs__logout:hover {
  background: rgba(255, 255, 255, 0.12);
  border-color: #6b88ad;
}

/* 로그아웃 실패 문구는 탭을 밀어내지 않고 자기 줄을 차지한다(콘솔과 같은 규약). */
.s-tabs > [role="alert"] {
  flex: 1 0 100%;
  margin: 0.4rem 0 0;
  color: #ffffff;
}

/* ── 학생 예약 화면 — 달력이 먼저, 신청은 그 옆(Stage B1) ─────────────────
   [왜 두 판인가] 이 화면이 묻는 것은 둘이다 — 언제로 할까, 그리고 무엇을 적을까. 앞의 답을
   고르는 자리(주간 달력)와 뒤를 적는 자리(신청 폼)가 위아래로 길게 늘어서면, 시간을 고른
   사람은 자기가 고른 것이 무엇인지 보이지 않는 채로 화면 몇 개를 내려가야 한다. 그래서 넓은
   화면에서는 나란히 서고 오른쪽 판은 따라 내려온다(sticky·문서 흐름 안·덮개 0).
   좁은 화면에서는 위아래이되, 고른 순간 그 자리로 화면이 옮겨간다(스크립트가 한다). */
.book {
  display: grid;
  gap: 1rem;
  align-items: start;
  margin: 0 0 1.5rem;
}

@media (min-width: 901px) {
  .book {
    grid-template-columns: minmax(0, 1fr) 23rem;
  }
}

.book__pane {
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.book__panel {
  min-width: 0;
  padding: 1rem 1.1rem 1.2rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  /* 남색 띠가 sticky 이므로, 이 판으로 화면을 옮길 때 제목이 그 아래로 숨지 않게 한다. */
  scroll-margin-top: 5.5rem;
}

@media (min-width: 901px) {
  .book__panel {
    position: sticky;
    top: 5.5rem;
  }
}

.book__title {
  margin: 0 0 0.5rem;
  font-size: 1.15rem;
  letter-spacing: -0.01em;
  /* 화면을 이 제목으로 옮길 때 sticky 남색 띠가 그것을 덮지 않게 한다. 스크롤 목표는
     판이 아니라 **제목**이므로 여백도 제목이 져야 한다. 값은 좁은 화면의 띠(내비가 한 줄
     더 서는 쪽)를 기준으로 잡는다 — 덮인 제목은 옮겨 온 것이 아니다. 로고가 한 벌 값
     (44px·W1-12)으로 올라가며 그 띠가 114px 가 되었으므로 7rem(112px)으로는 2px 이 덮였다. */
  scroll-margin-top: 7.5rem;
}

/* 유형 선택과 상태 문구는 판의 머리 안쪽에 산다(격자와 같은 테두리 안). */
.book__head {
  padding: 0.2rem 0.9rem 0.7rem;
}

.book__head .field {
  margin: 0;
}

.book__grid {
  padding: 0 0.9rem 0.9rem;
  border: 0;
  margin: 0;
  min-width: 0;
}

/* 이레는 일곱 등분이다. 최소 폭을 두지 않으므로 **가로 스크롤이 생기지 않는다** — 표를
   옆으로 미는 화면은 한 손으로 쓸 수 없다(운영자 달력의 스트립과 같은 잣대). */
.wgrid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 0.35rem;
  min-width: 0;
}

/* 날짜 머리는 **진짜 버튼**이다 — 좁은 화면에서 이 줄이 곧 날짜 선택기이고, 키보드와
   스크린리더에서 닿을 수 있어야 그 선택이 존재한다. */
.wday {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  min-height: var(--control-min);
  padding: 0.35rem 0.1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: inherit;
  font: inherit;
  line-height: 1.25;
  cursor: pointer;
}

.wday--today {
  background: var(--info-bg);
}

/* 고른 날. 안쪽 테두리라 칸의 크기를 밀지 않는다(달력 격자와 같은 규약). */
.wday--sel {
  box-shadow: inset 0 0 0 2px var(--accent);
}

/* 그 날의 빈 시간 수. 점 하나로는 "있다"만 말할 수 있고 "몇 개"는 말할 수 없다. */
.wday__n {
  min-width: 1.15rem;
  padding: 0 0.2rem;
  border-radius: 999px;
  background: var(--accent);
  color: #ffffff;
  font-size: 0.7rem;
  line-height: 1.15rem;
  text-align: center;
}

.wday__n--none {
  background: transparent;
  color: var(--text-muted);
}

.wbody {
  display: grid;
  gap: 0.3rem;
  align-content: start;
  min-width: 0;
}

/* 그 날에 남은 시간이 하나도 없을 때의 한 줄. 빈 칸은 "없다"와 "아직 못 읽었다"를
   구별해 주지 않는다. */
.wbody__none {
  margin: 0;
  padding: 0.4rem 0.2rem;
  font-size: 0.8rem;
  color: var(--text-muted);
  text-align: center;
}

/* 시간 한 칸. 라디오는 접근성(포커스·화살표 이동)을 위해 남기되 눈에는 라벨만 보인다. */
.tslot {
  position: relative;
  min-width: 0;
}

.tslot__radio {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
}

.tslot__label {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--control-min);
  padding: 0.3rem 0.2rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}

/* 고른 시간은 색만으로 말하지 않는다 — 굵은 테두리 + 굵은 글씨 + 채움을 함께 준다. */
.tslot__radio:checked + .tslot__label {
  border-color: var(--accent);
  border-width: 2px;
  padding: calc(0.3rem - 1px) calc(0.2rem - 1px);
  background: var(--accent);
  color: #ffffff;
  font-weight: 700;
}

.tslot__radio:focus-visible + .tslot__label {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

/* 아직 고르지 않았을 때의 요약 자리. 비어 보이지 않게 안내 한 줄이 늘 서 있다. */
.book__summary {
  margin: 0 0 0.8rem;
  padding: 0.6rem 0.7rem;
  border-radius: var(--radius);
  background: var(--info-bg);
  font-weight: 600;
}

.book__summary--empty {
  background: var(--bg);
  color: var(--text-muted);
  font-weight: 400;
}

/* 좁은 화면: 이레는 머리 한 줄이 말하고, 시간은 **고른 날 하나**가 말한다. 일곱 날의 시간을
   한꺼번에 세로로 쌓으면 신청 폼이 화면 몇 개 아래로 밀려난다(그것이 이 단계의 출발점이다). */
@media (max-width: 900px) {
  .wgrid > .wbody {
    grid-column: 1 / -1;
  }

  .wgrid > .wbody:not(.wbody--sel) {
    display: none;
  }

  .wbody {
    grid-template-columns: repeat(auto-fill, minmax(5.5rem, 1fr));
    margin-top: 0.4rem;
  }
}

/* 한 건을 읽는 화면(예약 현황)은 넓은 캔버스 안에서도 읽기 좋은 폭에 머문다 — 달력이
   아닌 화면까지 1600px 를 쓰면 한 줄의 눈이 화면을 가로지른다. */
.page--student main > .card {
  max-width: 48rem;
}

/* ── 내 상담(자리만 있는 구역·Stage B1) ─────────────────────────────────── */
.my {
  margin: 0 0 2rem;
  padding: 1rem 1.1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  /* 제목과 같은 띠 아래로 옮겨 온다(.book__title 과 같은 값·같은 이유). */
  scroll-margin-top: 7.5rem;
}

.my h2 {
  margin: 0 0 0.4rem;
  font-size: 1.15rem;
}

.my p {
  margin: 0;
  color: var(--text-muted);
}

/* ── 상태 뱃지 — 목록과 상세가 같은 한 마디를 같은 색으로 낸다(Stage B2) ────
   [색만으로 말하지 않는다] 뱃지 안의 글자가 곧 상태다(승인 대기·확정·반려…). 색은 그 글자를
   거드는 것이지 대신하는 것이 아니다 — 색각 이상·흑백 인쇄·고대비 모드에서도 뜻이 남는다.
   다섯 색조는 STATUS_META 의 tone 과 일대일이고, 그 매핑의 정본은 CSS 가 아니라 그 표다. */
.s-badge {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  padding: 0.2rem 0.65rem;
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: 0.85rem;
  font-weight: 700;
  line-height: 1.5;
  white-space: nowrap;
}

/* 남이 결정할 차례. 기다림은 경고가 아니므로 차분한 남색 계열이다. */
.s-badge--pending {
  background: var(--info-bg);
  border-color: #b9cbea;
  color: var(--info);
}

/* 시스템이 옮기는 중. 테두리를 점선으로 두어 "아직 멈추지 않았다"를 모양으로도 말한다. */
.s-badge--busy {
  background: var(--info-bg);
  border-style: dashed;
  border-color: #7f9dc9;
  color: var(--info);
}

/* 사람이 손대야 한다. 금색은 이 서비스에서 가장 아껴 쓰는 색이고, 그 자리가 여기다. */
.s-badge--attention {
  background: #fdf6e0;
  border-color: var(--gold);
  color: #6b5410;
}

/* 원하던 결과가 났다. */
.s-badge--done {
  background: var(--success-bg);
  border-color: #9ac6ae;
  color: var(--success);
}

/* 더 진행되지 않고 끝났다. 반려·만료·취소는 오류가 아니므로 붉게 겁주지 않는다. */
.s-badge--closed {
  background: #eef1f5;
  border-color: var(--border);
  color: var(--text-muted);
}

/* 현황 상세에서 뱃지는 문장 위에 한 줄로 선다. */
.status-view > .s-badge {
  margin: 0 0 0.6rem;
}

/* 자동 확인의 진행 문구. 조용한 자리이므로 조용한 모양이다 — 눈에 띄면 5초마다 깜박이는
   것이 화면의 주인공이 된다. 비어 있을 때 자리를 차지하지 않도록 :empty 를 접는다. */
.poll-progress {
  margin: 0.6rem 0 0;
  color: var(--text-muted);
  font-size: 0.88rem;
}

.poll-progress:empty {
  display: none;
}

/* ── 내 상담 목록(Stage B2) ──────────────────────────────────────────────
   [줄 전체가 링크인 이유] 이 화면의 유일한 조작은 "이 상담을 열어 본다"이다. 링크를 유형
   글자에만 걸면 손가락은 그 몇 글자를 겨냥해야 하고, 폰에서 그것은 44px 규약을 어기는
   과녁이 된다. 줄이 곧 과녁이면 겨냥할 것이 없다. */
.s-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 0.5rem;
}

.s-list__link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  min-height: var(--control-min);
  padding: 0.7rem 0.9rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  text-decoration: none;
}

.s-list__link:hover {
  border-color: var(--secondary);
  background: var(--surface-alt);
}

.s-list__link:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

/* 유형과 시각은 한 덩어리로 왼쪽에 선다. min-width:0 이 없으면 긴 유형 이름이 격자를
   밀어내 뱃지를 화면 밖으로 보낸다(가로 넘침의 흔한 자리). */
.s-list__main {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.s-list__type {
  font-weight: 700;
  overflow-wrap: anywhere;
}

.s-list__when {
  color: var(--text-muted);
  font-size: 0.92rem;
  overflow-wrap: anywhere;
}

/* 신청이 없을 때. 없다고 말하는 문장과 신청하러 가는 문이 한 자리에 함께 선다. */
.s-empty {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.8rem;
}

/* 좁은 화면에서는 뱃지가 줄 오른쪽 끝에 남되, 시각이 두 줄로 접혀도 줄이 무너지지 않는다. */
@media (max-width: 480px) {
  .s-list__link {
    padding: 0.65rem 0.75rem;
  }

  .s-list__when {
    font-size: 0.85rem;
  }
}

/* ── 운영자 콘솔(로그인·대기함·결재)·U5 ─────────────────────────────────── */
/* 로그인·홈 공통. 학생 화면과 같은 카드·버튼·상태 컴포넌트를 재사용하고, 대기함 카드와
   인라인 step-up 폼만 여기서 더한다(색만으로 뜻을 전하지 않는다·44px 타깃 유지). */
.op-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 0 0 1rem;
}

.op-inbox-list {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.9rem;
}

.op-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem 1rem 1.15rem;
  background: var(--surface);
}

.op-card__meta {
  margin: 0 0 0.75rem;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.35rem 0.9rem;
}

.op-card__meta dt {
  font-weight: 600;
  color: var(--text);
}

.op-card__meta dd {
  margin: 0;
  color: var(--text-muted);
  word-break: break-word;
}

.op-card__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
}

.op-card__actions .btn {
  flex: 0 0 auto;
}

/* 거절 버튼은 색만이 아니라 테두리·라벨(“거절”)로 수락과 구별한다. */
.btn--danger {
  background: var(--surface);
  color: var(--danger);
  border-color: var(--danger);
  border-width: 2px;
}

.btn--danger:hover {
  background: var(--danger-bg);
  border-color: var(--danger);
}

/* 인라인 step-up(비밀번호 재입력) — 결재를 확정하기 전 재인증. 초기엔 hidden. */
.op-stepup {
  margin: 0.9rem 0 0;
  padding: 0.9rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-alt);
}

.op-stepup__legend {
  font-weight: 700;
  color: var(--primary);
  padding: 0 0.4rem;
}

.op-stepup .field:last-of-type {
  margin-bottom: 0.9rem;
}

.op-card__result {
  margin: 0.9rem 0 0;
}

@media (min-width: 48rem) {
  .op-inbox-list {
    grid-template-columns: 1fr 1fr;
  }
}

/* ── 콘솔 내비게이션·초대 발급·연동 상태(U6) ────────────────────────────── */
/* 내비게이션은 문서 내 앵커 링크다. 버튼과 같은 44px 타깃을 쓰되 밑줄은 걷는다
   (버튼 모양이 이미 링크임을 말한다). 좁은 화면에서는 .op-nav 의 wrap 이 접는다. */
.op-nav__link {
  text-decoration: none;
}

/* 초대·연동 영역의 버튼 줄. 형태는 대기함의 .op-card__actions 와 같지만 **클래스가 다르다** —
   "op-card" 는 결재 카드 마크업의 표식이고, 홈 SSR 셸에는 그 표식이 하나도 없어야 하기
   때문이다(U5 계약: SSR 에 신청 항목 마크업 0). 같은 모양이라고 같은 이름을 쓰면 그 계약이
   조용히 깨진다. */
.op-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
}

.op-actions .btn {
  flex: 0 0 auto;
}

/* 초대 폼의 역할 라디오는 학생 화면의 .slots 규칙을 그대로 쓴다(44px·선택 표식 포함). */
.op-invite-form .slots {
  margin-top: 0.25rem;
}

/* 발급 결과 — 폼과 시각적으로 끊어 "이것은 방금 만들어진 것"이 분명하게 보이도록. */
.op-invite-result {
  margin: 1.25rem 0 0;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}

/* 링크는 읽기 전용이다. 배경을 달리해 편집 가능한 칸과 구별하고, 긴 토큰이 잘리지 않게
   등폭 글꼴 + 줄바꿈을 준다(모바일에서 화면 밖으로 밀려나지 않는다). */
.op-invite-result input[readonly] {
  background: var(--surface-alt);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.95rem;
  word-break: break-all;
}

/* 전달 경고 — 색만으로 말하지 않는다(문구가 정본이고, 테두리는 보조다). */
.op-warning {
  margin: 0.75rem 0;
  padding: 0.75rem 1rem;
  border: 2px solid var(--gold);
  border-radius: var(--radius);
  /* 아주 옅은 크림색. gold 테두리와 짝이며 본문 대비를 해치지 않는다. */
  background: #fffbe9;
  color: var(--text);
  font-weight: 600;
}

/* ── 명부 표와 오프보딩 시트(S5) ───────────────────────────────────────── */

/*
 * 명부는 행과 열이 있는 자료다. 좁은 화면에서 가로로 넘칠 수 있으므로 표 자체를 줄이지
 * 않고 **감싸는 자리**가 스크롤을 갖는다 — 글자를 줄이면 그 화면에서 되돌릴 수 없는
 * 버튼을 누르는 사람이 잘못된 줄을 읽는다.
 */
.op-accounts-table {
  width: 100%;
  margin-top: 0.75rem;
  border-collapse: collapse;
  display: block;
  overflow-x: auto;
}
.op-accounts-table th,
.op-accounts-table td {
  padding: 0.6rem 0.5rem;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
  white-space: nowrap;
}
.op-accounts-table th {
  font-size: 0.9rem;
  color: var(--text-muted);
}
/* 연락처 구분은 값의 일부가 아니라 그 값에 붙는 꼬리표다(색이 아니라 글자로 말한다). */
.op-accounts-role {
  color: var(--text-muted);
  font-size: 0.85rem;
}

/*
 * 오프보딩 시트. **문서 흐름 안의 영역**이지 팝업이 아니다 — 팝업은 좁은 화면에서 내용이
 * 잘리고, 이 시트가 담는 것은 저장 전에 반드시 읽혀야 하는 문장들이다(결정 D-7).
 */
.op-sheet {
  margin-top: 0.75rem;
  padding: 1rem;
  border: 2px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-alt);
}
.op-sheet__title {
  margin: 0 0 0.5rem;
  font-size: 1.05rem;
}
/* 예정 상담 목록. 건수만으로는 사람이 캘린더에서 무엇을 찾아야 할지 모른다. */
.op-sheet__bookings {
  margin: 0.5rem 0;
  padding-left: 1.2rem;
}
/*
 * 잠긴 조치의 이유. 버튼만 회색으로 두면 사람은 "고장났나"를 먼저 의심한다 — 왜 잠겼는지가
 * 버튼 옆에 글자로 있어야 다음에 무엇을 할지 알 수 있다.
 */
.op-sheet__locked {
  margin: 0.35rem 0 0;
  color: var(--text);
  font-weight: 600;
}

#invite-copy-status,
#invite-error {
  margin: 0.6rem 0 0;
}

#calendar-result {
  margin: 0.9rem 0 0;
}

#recheck-stepup {
  margin-top: 0.9rem;
}

/* ── 주간 달력 격자(S3) ──────────────────────────────────────────────────
   격자는 표다 — 열이 날짜이고 행이 시각이므로 그 축은 마크업이 이미 말한다. 여기서
   더하는 것은 그 표가 **한 화면 안에서** 읽히게 하는 것뿐이다.

   좁은 화면에서는 여덟 열이 접히는 대신 가로로 밀린다. 접으면 "같은 시각의 이레"라는
   이 화면의 유일한 사실이 사라지고, 그것은 다른 화면이지 좁아진 달력이 아니다. */
.wk {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.wk__grid {
  width: 100%;
  min-width: 44rem;
  border-collapse: collapse;
  table-layout: fixed;
}

.wk__grid th,
.wk__grid td {
  border: 1px solid var(--border);
  padding: 0;
  vertical-align: top;
}

.wk__corner {
  width: 3.5rem;
}

/* 시간 눈금. 칸의 위 모서리에 붙어 그 행이 어느 시각에서 시작하는지 말한다. */
.wk__hour {
  width: 3.5rem;
  padding: 0.25rem 0.4rem 0;
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--text-muted);
  text-align: right;
  white-space: nowrap;
}

.wk__cell {
  height: 3rem;
  padding: 2px;
}

/* 운영시간 밖. 사선 음영은 "닫혔다"를 색이 아니라 **무늬**로 말한다 — 옅은 회색 배경
   하나였다면 고대비 모드나 흑백 인쇄에서 열린 칸과 구별되지 않는다. */
.off {
  background-image: repeating-linear-gradient(
    -45deg,
    #e7ebf1,
    #e7ebf1 5px,
    #f6f8fb 5px,
    #f6f8fb 10px
  );
}

/* 날짜 머리칸 — 요일·날짜·그날의 운영시간. */
.dayhead {
  padding: 0.4rem 0.25rem;
  text-align: center;
  font-weight: 400;
}

.dayhead--today {
  background: var(--info-bg);
}

/* 머리글을 고르는 버튼(S4-6). 칸 전체를 덮되 생김새는 그대로 둔다 — 버튼인 이유는 모양이
   아니라 키보드와 스크린리더에서 **닿을 수 있는 자리**여야 하기 때문이다(월간 칸과 같다). */
.dayhead__pick {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.dow {
  display: block;
  font-size: 0.75rem;
  color: var(--text-muted);
}

.dnumrow {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 4px;
}

.dnum {
  font-size: 1.05rem;
  font-weight: 700;
}

/* 한국 달력의 색. 공휴일·일요일은 빨강, 토요일은 파랑이다. 이 색은 "쉬는 날"만 말하고
   운영 여부(휴무 빗금·휴무 태그)는 건드리지 않는다 — 빨간 날이라고 자동으로 닫히지
   않으며 그것은 운영자가 정한다. 색만으로 말하지 않으므로 공휴일 이름이 옆에 함께 선다. */
.dnum--sun,
.dnum--holi {
  color: var(--danger);
}

.dnum--sat {
  color: var(--secondary);
}

.holiname {
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--danger);
  line-height: 1.2;
  min-width: 0;
  word-break: keep-all;
}

.tag {
  display: block;
  margin-top: 2px;
  font-size: 0.7rem;
  color: var(--text-muted);
  white-space: nowrap;
}

.tag--closed {
  color: var(--danger);
  font-weight: 700;
}

/* 일정 칩 — 상태는 선·색·글자 셋으로 함께 말한다(색만으로 말하지 않는다). */
.ev {
  border-radius: 8px;
  padding: 3px 5px;
  margin-bottom: 2px;
  font-size: 0.75rem;
  line-height: 1.25;
  overflow: hidden;
}

.ev b {
  display: block;
  font-weight: 700;
  /* 시각 구간("11:00–12:00")은 한 덩어리다. 좁은 열에서 "11:00–"과 "12:00"으로 접히면 한 시각이
     두 줄로 읽힌다. 줄바꿈을 막고, 열이 좁은 자리(폰·태블릿·좁은 데스크톱)에서는 글자를 줄여
     담는다(아래 반응형 층) — 자르지 않는다(잘림은 어느 시각인지를 숨긴다). */
  white-space: nowrap;
}

.ev small {
  display: block;
  font-size: 0.7rem;
}

/* 대기 = 점선. 아직 정해지지 않은 자리라는 것을 선의 성질이 말한다. */
.ev--pending {
  border: 2px dashed var(--secondary);
  background: #eaf1f9;
  color: var(--primary);
}

/* 확정 = 실선 네이비 + 골드 좌측 띠. 대기와 가장 멀리 떨어진 표현이다. */
.ev--confirmed {
  border: 2px solid var(--primary);
  border-left: 5px solid var(--gold);
  background: var(--primary);
  color: #ffffff;
}

/* 이어지는 칸의 칩. 위 모서리를 펴서 "앞 칸에서 이어져 왔다"를 모양으로도 말한다 — 글자
   ("이어짐")가 그 사실을 이미 말하므로 이것은 거드는 표현이고, 색을 쓰지 않는다. */
.ev--cont {
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

/* 전이·실패 = 점선 호박색. 대기(점선 파랑)와도 확정(실선 네이비)과도 다른 세 번째 표현이다.
   이 자리는 "정해졌다"도 "기다린다"도 아니라 **사람이 봐야 한다**를 말한다 — 색은 그 뜻을
   거들 뿐이고 실제 사유는 칩의 글자(취소 처리 중·캘린더 반영 실패…)가 말한다. */
.ev--attention {
  border: 2px dotted #8a6414;
  background: #fdf3d8;
  color: #4a3708;
}

/* 여유 = 옅은 테두리 + 흐린 글자. 자리는 상담과 똑같이 막혀 있지만 그 시간에 만날 사람은
   없다 — 그래서 상담(대기·확정)보다 약하게, 그러나 빈 칸과는 분명히 다르게 그린다. 사선을
   쓰지 않는 것은 바쁨(회색 사선)과 갈리기 위해서다. 이름("앞 여유"·"뒤 여유")은 칩의 글자가
   말하므로 색은 여기서도 거들 뿐이다. */
.ev--buffer {
  border: 1px solid #c3cad4;
  background: #f2f4f7;
  color: var(--text-muted);
}

/* ── 격자 위의 줄과 월간 격자(S3-14) ────────────────────────────────────
   월간은 **여섯 주 마흔두 칸으로 고정**된다. 달의 모양대로 그리면 달을 넘길 때마다 높이가
   튀고 아래 있던 것들이 따라 움직인다 — 같은 화면이 달마다 다른 화면이 된다. */
/* 판의 머리 한 줄. 왼쪽이 지금 창의 이름이고 오른쪽이 그 창을 바꾸는 자리들이다(스케치의
   .calhead). 이름이 왼쪽 끝을 잡는 이유는 눈이 먼저 닿는 자리가 거기이고, 이 화면에서
   가장 먼저 알아야 하는 것이 "지금 어디를 보고 있는가"이기 때문이다. */
.calbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
  padding: 0.7rem 0.9rem;
  border-bottom: 1px solid var(--border);
}

/* 지금 열려 있는 창의 이름. 월간에는 달 이름이 격자 안 어디에도 없으므로, 이 한 줄이
   "지금 어디를 보고 있는가"를 말하는 유일한 자리다. */
.calbar__label {
  flex: 1 1 8rem;
  /* 창 이름은 **한 덩어리**다 — "8월"과 "1일" 사이에서 접히면 한 낱말이 두 줄이 된다. 그래서
     줄바꿈을 막고(nowrap), 자리가 모자라면 이름을 줄이는 대신 **바꾸는 자리들(seg·nav)을 다음
     줄로 밀어낸다**(min-width:max-content). 이름은 눈이 가장 먼저 닿는 자리이므로 온전히 선다. */
  min-width: max-content;
  white-space: nowrap;
  margin: 0;
  font-size: 1.15rem;
  letter-spacing: -0.01em;
  text-align: left;
}

.calbar__nav {
  padding: 0.4rem 0.9rem;
}

.seg {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.seg__btn {
  min-height: var(--control-min);
  padding: 0.4rem 1rem;
  border: 0;
  background: var(--surface);
  color: var(--accent-strong);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

/* 눌린 쪽. 상태(aria-pressed)를 그대로 선택자로 쓴다 — 보이는 것과 말해지는 것이 같은
   한 사실에서 나오면 둘이 어긋날 자리가 없다. */
.seg__btn[aria-pressed='true'] {
  background: var(--accent);
  color: #ffffff;
}

/* ── 반응형 층(화면 폭에 맞춘 자리·글자) ─────────────────────────────────────
   같은 달력을 폭마다 다르게 **줄이지 않고 맞춘다**. 세 자리에서 글자가 접혀 한 낱말·한 시각이
   두 줄이 되던 것을 폭별로 바로잡는다: 폰(≤600)은 창 이름을 제 줄에 세우고, 좁은 열의 칩 시각은
   폰·태블릿(≤800)과 좁은 데스크톱(901–1199)에서 글자를 줄여 한 줄에 담는다. 넓은 데스크톱
   (≥1200)과 태블릿 세로(≈889)의 승인된 크기는 건드리지 않는다. */

/* [폰] 창 이름은 바꾸는 자리들과 한 줄을 다투지 않고 **제 줄 전체**를 가진다. 그래도 넘칠 만큼
   좁은 폰에서는 글자를 조금 줄여(clamp) 자르지 않고 한 줄에 담는다. */
@media (max-width: 600px) {
  .calbar__label {
    flex-basis: 100%;
    font-size: clamp(0.95rem, 4.2vw, 1.15rem);
  }
}

/* [폰·좁은 태블릿] 좁은 시간 격자(≈96px 열)에서 "11:00–12:00"이 한 줄에 들도록 칩 시각·상태를
   compact 로 줄인다. 접근성 이름(칩의 글자 전체)은 그대로다 — 줄인 것은 크기지 내용이 아니다.
   태블릿 세로(801–900·≈889)는 열이 넓어 승인된 0.75rem 을 지킨다. */
@media (max-width: 800px) {
  .tg .ev b {
    font-size: 0.7rem;
  }
  .tg .ev small {
    font-size: 0.66rem;
  }
  .tg .ev {
    padding: 2px 3px;
  }
}

/* [좁은 데스크톱] 상세 페인(22.5rem)이 격자를 좁혀 1024 폭의 넓은 표는 한 열이 ≈80px 로 준다.
   그 폭에서 "11:00–12:00"이 접히지 않도록 표의 칩 시각을 줄인다. 1200 이상에서는 열이 넓어
   승인된 크기를 그대로 둔다. */
@media (min-width: 901px) and (max-width: 1199px) {
  .wk .ev b {
    font-size: 0.64rem;
  }
  .wk .ev small {
    font-size: 0.62rem;
  }
  .wk__cell {
    padding: 1px;
  }
}

/* ── 좁은 화면의 주간: 진짜 시간 격자(S4-11 재작성) ──────────────────────
   왼쪽 시각축 + 이레 일곱 열 + 시각 자리에 비례해 앉는 칩. 넓은 화면 표와 **같은 사실**(같은
   시각의 이레)을 폰에서도 지킨다. 다만 이레×시각을 폰 폭에 다 그릴 수 없으므로, 접는 대신
   **상자 안에서** 가로로 민다(문서 전체는 밀리지 않는다). 왼쪽 시각축과 위쪽 날짜 머리는 그
   밀림·세로 스크롤에도 제자리에 붙는다(sticky) — "몇 시의 무슨 요일"을 스크롤 어디에서도 잃지
   않는다. 태블릿 세로(≈889px)에서는 일곱 열이 다 들어와 밀 것이 없다. */
.tgw {
  min-width: 0;
}

.tg {
  /* 한 시간의 높이. 손가락이 닿는 칸이자, 시각선·칩 자리의 비례 기준이다. */
  --tg-row-h: 3rem;
  --tg-axis-w: 3.25rem;
  /* 한 날 열의 최소 폭. 이보다 좁아지면 상자 안에서 가로로 민다. */
  --tg-col-w: 6rem;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  max-height: 68vh;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.tg__inner {
  display: grid;
  grid-template-columns: var(--tg-axis-w) repeat(7, minmax(var(--tg-col-w), 1fr));
  min-width: calc(var(--tg-axis-w) + 7 * var(--tg-col-w));
}

/* 날짜 머리 — 위로 붙는다(세로로 밀어도 요일을 잃지 않는다). */
.tg__head {
  position: sticky;
  top: 0;
  z-index: 3;
  padding: 0.3rem 0.15rem;
  background: var(--surface);
  border-left: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  text-align: center;
}

/* 모서리는 가로·세로 둘 다 붙으므로 가장 위에 온다. */
.tg__corner {
  position: sticky;
  top: 0;
  left: 0;
  z-index: 4;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.tg__head .tag {
  white-space: normal;
  font-size: 0.62rem;
  line-height: 1.15;
}

/* 종일 줄 — 날짜 머리 바로 아래. 자정에 붙은 일정은 시각 격자가 아니라 여기 선다. */
.tg__axhead,
.tg__allday {
  min-height: 1.4rem;
  border-left: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.tg__axhead {
  position: sticky;
  left: 0;
  z-index: 1;
  padding: 0.2rem 0.3rem;
  background: var(--surface);
  border-left: 0;
  font-size: 0.7rem;
  color: var(--text-muted);
  text-align: right;
}

/* 종일 일정이 걸친 칸 — 바쁨과 같은 회색 사선. 이름은 sr-only 로 글자에 싣는다(PII 0). */
.tg__allday--on {
  background-image: repeating-linear-gradient(
    45deg,
    #c9ced6,
    #c9ced6 3px,
    #dfe3e9 3px,
    #dfe3e9 6px
  );
}

/* 왼쪽 시각축 — 왼쪽에 붙는다(가로로 밀어도 몇 시인지 잃지 않는다). */
.tg__axis {
  position: sticky;
  left: 0;
  z-index: 1;
  height: calc(var(--tg-rows) * var(--tg-row-h));
  background: var(--surface);
}

.tg__hour {
  position: absolute;
  right: 0.3rem;
  transform: translateY(-0.5em);
  font-size: 0.7rem;
  color: var(--text-muted);
  white-space: nowrap;
}

/* 한 날의 시간 열 — 칩이 시각 자리에 비례해 앉는다. 시각선은 배경 눈금으로 긋는다. */
.tg__col {
  position: relative;
  height: calc(var(--tg-rows) * var(--tg-row-h));
  border-left: 1px solid var(--border);
  background-image: repeating-linear-gradient(
    to bottom,
    var(--border) 0,
    var(--border) 1px,
    transparent 1px,
    transparent var(--tg-row-h)
  );
}

/* 운영시간 밖 음영. 넓은 화면 .off 와 같은 사선 무늬를 시각 자리에 겹쳐 덮는다. */
.tg__off {
  position: absolute;
  left: 0;
  right: 0;
}

/* 칩 자리 — top/height/left/width 는 스크립트가 시각·겹침에서 계산해 CSSOM 으로 얹는다.
   문자열 조립이 아니므로 응답의 값이 마크업이 되지 않는다. */
.tg__ev {
  position: absolute;
  box-sizing: border-box;
  padding: 0 1px;
  overflow: hidden;
}

.tg__ev--btn {
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.tg__ev > .ev {
  height: 100%;
  margin: 0;
  box-sizing: border-box;
}

/* 좁은 시간 격자는 상태와 짧은 시각을 한 줄로만 말한다. 긴 전이 이름도 상태부터 보이고,
   남는 꼬리만 말줄임 된다. 온전한 문장은 aria-label/title 에 있다. */
.tg__ev > .ev > b {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tg__ev--btn[aria-pressed='true'] > .ev {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* 격자 아래 그날 요약(고른 날의 공휴일 이름·운영시간)과 운영시간 여는 자리. */
.tg__daybar {
  margin-top: 0.6rem;
}

.agenda__day {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  margin: 0 0 0.2rem;
  font-size: 1rem;
  font-weight: 700;
}

.agenda__hours {
  margin: 0 0 0.6rem;
  font-size: 0.85rem;
  color: var(--text-muted);
}

.agenda__hours--closed {
  font-weight: 600;
}

/* ── 폰(≤600)의 주간: 고른 하루 격자 + 이레 선택 띠(Phase Z·승인된 옵션 1) ──────────
   일곱 열을 상자 안에서 미는 대신, 위에 이레 선택 띠를 세우고 아래에 고른 하루만 시각 격자로
   그린다. 두 열(시각축 + 하루)뿐이므로 폰 폭 안에 온전히 들어와 가로로 밀 것이 없다. */

/* 이레 선택 띠 — 일곱 칸이 균등하게 한 줄에 선다(가로 스크롤 없이). */
.daysel {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 0.25rem;
  margin: 0 0 0.6rem;
}

.daysel__day {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.daysel__day .dayhead__pick {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  width: 100%;
  padding: 0.35rem 0.1rem;
  border: 0;
  border-radius: var(--radius);
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

.daysel__day .dow {
  font-size: 0.66rem;
  color: var(--text-muted);
}

.daysel__day .dnum {
  font-size: 0.95rem;
  font-weight: 700;
}

.daysel__day.dayhead--today {
  border-color: var(--primary);
}

/* 고른 날은 **선명한 남색 테두리**로 말한다(색·눌림 둘 다). 날짜 숫자의 색(일·토·공휴일)은
   그대로 두어 그 사실이 고름에 가려지지 않게 한다. */
.daysel__day.dayhead--sel {
  border-color: var(--primary);
  box-shadow: inset 0 0 0 2px var(--primary);
}

.daysel__day.dayhead--sel .dow {
  color: var(--primary);
  font-weight: 700;
}

/* 폰의 하루 격자 — 시각축 + 한 날 열(전폭). 두 열뿐이라 상자 안에서도 밀 것이 없다. */
.tg--day {
  min-width: 0;
}

.tg--day .tg__inner {
  grid-template-columns: var(--tg-axis-w) minmax(0, 1fr);
  min-width: 0;
}

/* ── 좁은 화면의 압축 월간(S4-12) ───────────────────────────────────────
   점은 종류를, 숫자는 개수를 말한다. 빈 원과 꽉 찬 원은 **채움**으로 갈리므로 색을 못 보는
   화면에서도 구별된다. */
.mdots {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  margin: 2px 0 0;
}

.dot {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  border: 1.5px solid var(--primary);
}

.dot--pending {
  background: transparent;
}

.dot--confirmed {
  background: var(--primary);
}

.dot--attention {
  border-color: #8a6414;
  background: #fdf3d8;
}

.dot--busy {
  border-color: #b8bfc9;
  background: #c9ced6;
}

/* ── 바텀시트(S4-12) ────────────────────────────────────────────────────
   폰에서 상세를 늘 펴 두면 그 자리가 화면의 절반을 먹는다. 그래서 필요할 때 아래에서
   올라오고, 하단 내비(56px + 안전영역) 위에 앉으며, 화면 높이의 절반을 넘지 않는다. */
.cal__detail--sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(56px + env(safe-area-inset-bottom));
  z-index: 60;
  max-height: 60vh;
  overflow-y: auto;
  border-radius: var(--radius) var(--radius) 0 0;
  background: var(--surface);
  box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.18);
}

.cal__sheetclose {
  position: absolute;
  top: 0.6rem;
  right: 0.8rem;
}

/* 격자와 상세는 넓은 화면에서 나란히, 좁은 화면에서는 위아래로 선다. 접히는 것은 자리이지
   내용이 아니다 — 어느 쪽에서도 같은 두 가지(격자·고른 날)를 말한다.

   넓은 화면에서 **격자(flex)로 붙이지 않는 이유**: flex-wrap 은 폭이 모자라면 상세를 조용히
   아래로 내린다. 그 조용함이 지금까지의 문제였다 — 자리 배치가 어긋나도 아무 시험도 실패하지
   않았다. 두 열짜리 격자는 모자라면 격자를 좁힐 뿐 자리를 바꾸지 않는다. */
.cal {
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
}

@media (min-width: 901px) {
  .cal {
    display: grid;
    /* 스케치와 같은 두 열: 남는 폭은 전부 격자에게, 상세는 360px 고정. */
    grid-template-columns: minmax(0, 1fr) 22.5rem;
    gap: 1rem;
    align-items: start;
  }
}

/* 격자가 사는 판(스케치의 왼쪽 .panel). 머리 한 줄 · 범례 한 줄 · 격자가 **한 테두리
   안에** 든다 — 셋이 각자 떠 있으면 어느 범례가 어느 격자의 것인지 화면이 말하지 않는다. */
.cal__pane {
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

/* 상태 어휘를 격자 **바로 위**에 적어 둔다. 점선·실선·회색 사선·빗금은 이 화면이 만든
   약속이고, 약속은 그것을 쓰는 자리 옆에 있어야 한다 — 다른 화면에 적힌 범례는 아무도
   찾아가지 않는다. 읽는 순서가 곧 놓인 순서다(창 이름 → 어휘 → 격자). */
.cal__legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  margin: 0;
  padding: 0.55rem 0.9rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.78rem;
  color: var(--text-muted);
}

.cal__legendkey {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

/* 견본은 칩과 **같은 선·같은 색**을 쓴다(.ev--* 를 그대로 얹는다) — 범례가 자기 색을 따로
   가지면 언젠가 칩만 바뀌고 범례는 옛 약속을 계속 말한다. */
.cal__legendswatch {
  width: 18px;
  height: 12px;
  border-radius: 3px;
  padding: 0;
  margin: 0;
  flex: 0 0 auto;
}

/* 판 아래의 한 줄. 이 화면에서 무엇을 누를 수 있는지 말한다(스케치의 #calHint). */
.cal__hint {
  margin: 0;
  padding: 0.6rem 0.9rem;
  border-top: 1px solid var(--border);
  font-size: 0.78rem;
  color: var(--text-muted);
}

.cal__pane > .wk,
.cal__pane > .mn,
.cal__pane > .tgw {
  min-width: 0;
}

/* 상세는 **모달이 아니라 늘 자리를 차지하는 페인**이다(S4-6). 모달이면 그 자리는 무언가를
   누른 뒤에만 존재하고, 누르지 않은 화면은 "여기서 무엇을 할 수 있는가"를 말하지 않는다.
   좁은 화면에서는 격자 아래로 내려가고(같은 두 가지를 위아래로), 넓은 화면에서는 스케치가
   정한 360px 를 그대로 차지한다 — 줄지도 늘지도 않는 폭이라 격자의 폭이 그만큼 정해진다. */
.cal__detail {
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.75rem 1rem;
}

.cal__detailtitle {
  margin: 0;
  font-size: 1rem;
}

/* 페인 안의 한 묶음(그 날의 상담 / 그날의 운영시간). 제목은 페인 안에서 h4 다. */
.cal__sect {
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--border);
}

.cal__secttitle {
  margin: 0 0 0.5rem;
  font-size: 0.9rem;
  color: var(--text-muted);
}

.cal__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.cal__list > li + li {
  margin-top: 0.35rem;
}

/* 그 날의 상담 한 줄. **진짜 버튼**이다 — 클릭만 받는 줄은 키보드에도 스크린리더에도 없는
   자리가 되고, 그러면 결재는 마우스를 쓰는 사람에게만 존재한다. */
.daybtn {
  display: block;
  width: 100%;
  min-height: var(--control-min);
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.daybtn[aria-pressed='true'] {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}

.cal__req {
  margin-top: 0.6rem;
}

/* 그 날 유지되는 확정 건수. 경고 문구 바로 아래에 붙어 일반론을 그 날의 사실로 좁힌다. */
.cal__keep {
  margin: 0 0 0.6rem;
  font-size: 0.85rem;
  font-weight: 600;
}

/* 시간 두 칸(S4-9). 고른 처분이 '시간 변경' 일 때만 선다 — 고르지 않은 처분의 칸은 아예
   존재하지 않는다(휴무에 남은 값이 함께 실려 서버가 요청 전체를 거부하는 사고를 막는다). */
.cal__hourtime {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

.cal__hourtime > .field {
  flex: 1 1 0;
  min-width: 0;
}

.cal__req dl {
  margin: 0;
}

.cal__req dt {
  margin-top: 0.4rem;
  font-size: 0.75rem;
  color: var(--text-muted);
}

.cal__req dd {
  margin: 0;
  font-weight: 600;
  word-break: break-all;
}

.mn {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.mn__grid {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}

/* 최소 폭은 **넓은 화면에만** 있다. 넓은 화면의 칸은 일정 칩 세 개까지를 실으므로 그만한
   폭이 없으면 칩이 뭉개지고, 그때는 옆으로 미는 편이 낫다. 좁은 화면의 칸은 점과 숫자만
   싣도록 이미 압축되어 있으므로(S4-12) 그 폭이 필요하지 않다 — 여기에 44rem 을 그대로 두면
   폰에서 한 달을 보려고 표를 좌우로 밀어야 하고, 그것은 압축이 아니라 축소 실패다. */
@media (min-width: 901px) {
  .mn__grid {
    min-width: 44rem;
  }
}

.mn__grid th,
.mn__grid td {
  border: 1px solid var(--border);
  padding: 0;
  vertical-align: top;
}

.mhead {
  padding: 0.3rem;
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--text-muted);
}

.mhead--sun {
  color: var(--danger);
}

.mhead--sat {
  color: var(--secondary);
}

.mcell {
  height: 6rem;
  padding: 3px;
}

.mcell .dnumrow {
  justify-content: space-between;
}

.mcell--today {
  background: var(--info-bg);
}

/* 고른 날 하나. 안쪽 테두리라 칸의 크기를 밀지 않는다 — 고를 때마다 격자가 흔들리면
   그 흔들림이 곧 "무언가 잘못 눌렀다"로 읽힌다. 오늘 표시(배경)와는 다른 축이다. */
.mcell--sel,
.dayhead--sel {
  box-shadow: inset 0 0 0 2px var(--accent);
}

/* 날짜를 고르는 버튼. 칸 전체를 덮되 생김새는 그대로 둔다 — 여기서 버튼인 이유는 모양이
   아니라 키보드와 스크린리더에서 **닿을 수 있는 자리**여야 하기 때문이다. */
.mcell__pick {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

/* 앞뒤 달의 날. 지우지 않는 것은 높이를 고정하기 위해서이고, 흐리게 하는 것은 이 달의 날이
   아니기 때문이다. 색 자체는 그대로 둔다 — 일요일은 지난 달에서도 일요일이다. */
.mcell--out {
  background: #f7f8fa;
}

.mcell--out .dnum,
.mcell--out .ev {
  opacity: 0.55;
}

/* 그 날의 셈과 넘친 건수. 잘림 자체는 나쁘지 않다 — 잘렸다는 사실을 말하지 않는 것이 나쁘다. */
.mcount,
.mmore {
  margin: 2px 0;
  font-size: 0.7rem;
  color: var(--text-muted);
}

/* 바쁨 = 회색 사선. **누를 수 없다** — 이 화면이 남의 일정에 할 수 있는 일이 없다. */
.ev--busy {
  border: 1px solid #b8bfc9;
  background-image: repeating-linear-gradient(
    45deg,
    #c9ced6,
    #c9ced6 3px,
    #dfe3e9 3px,
    #dfe3e9 6px
  );
  color: #2b3440;
  pointer-events: none;
}

/* ── 정식 진입점: 학생·보호자 로그인(GET /·Phase Z) ──────────────────────── */
/* 주 로그인 버튼과 가입 신청 버튼은 카드 폭을 정직하게 쓴다 — 좁은 폰에서 손가락이 겨눌
   면적을 최대로 두고, 두 조치가 카드 안에서 같은 무게로 선다(입력칸은 .field__control 이
   이미 100%). */
body[data-screen="student-login"] #login-submit,
body[data-screen="student-login"] #login-signup-link {
  width: 100%;
}
/* "처음 이용하시나요?" — 가입 버튼을 여는 한 줄. 주 버튼과 보조 버튼 사이의 숨. */
body[data-screen="student-login"] .auth-lead {
  margin: 1.5rem 0 0.5rem;
  text-align: center;
  color: var(--text-muted);
}
/* 운영자 문은 조용한 링크 한 줄이되 **누를 수 있는 크기**(44px)는 지킨다 — 눈에 덜 띄는
   것과 손이 닿지 않는 것은 다르다. 가입 버튼보다 아래에 선다. */
body[data-screen="student-login"] .auth-op {
  margin: 1rem 0 0;
  text-align: center;
}
body[data-screen="student-login"] .auth-op__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--control-min);
  padding: 0 0.75rem;
  color: var(--text-muted);
  font-size: 0.9rem;
}

/* ── 공개 자가가입(/s/signup·공개 가입 0023) ─────────────────────────────── */
body[data-screen="signup"] .field__input {
  display: block;
  width: 100%;
  min-height: 48px;
  padding: 0.65rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 16px;
}
body[data-screen="signup"] .field__input:focus-visible {
  border-color: var(--primary);
}
body[data-screen="signup"] #signup-submit,
body[data-screen="signup"] .signup-pending .btn {
  width: 100%;
  min-height: 52px;
  justify-content: center;
}
.signup-roles {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  background: #edf1f6;
  border-radius: 12px;
  padding: 4px;
  margin: 6px 0 18px;
}
.signup-role {
  border: 0;
  background: transparent;
  border-radius: 9px;
  min-height: 44px;
  color: #576476;
  font-weight: 800;
  font-size: 15px;
  cursor: pointer;
}
.signup-role--active {
  background: var(--primary);
  color: #fff;
}
/* 승인 뒤에 무엇이 열리는지 말하는 줄. 13px 은 이 문장을 읽지 않고 지나치게 만드는 크기였다 —
   이 화면에서 유일하게 **약속**을 담은 문단이므로 읽을 수 있는 아래 끝(14px)을 지킨다. */
.signup-notice {
  background: #f0f5fb;
  border-left: 4px solid var(--primary);
  border-radius: 9px;
  padding: 12px 14px;
  margin: 6px 0 16px;
  color: #445268;
  font-size: 0.875rem;
  line-height: 1.55;
}
/*
 * 개인정보 수집·이용 동의. 두 가지를 고친다(student L-03·applicant A-07 실측).
 *
 *  - 글자 13px → 14px. 동의는 **읽고 하는 것**이고, 읽히지 않는 크기의 동의문은 동의의 형식만
 *    갖춘다.
 *  - 표적 12×12px(감싼 라벨 모바일 39px·데스크톱 19.5px) → 줄 전체가 44px. 이 요소가 곧
 *    label 이므로 줄 어디를 눌러도 체크가 뒤집힌다.
 */
.signup-consent {
  display: flex;
  gap: 9px;
  align-items: center;
  min-height: 44px;
  font-size: 0.875rem;
  color: #4e5a6a;
  line-height: 1.5;
  margin-bottom: 16px;
}
.signup-consent input {
  flex: 0 0 auto;
  width: 1.5rem;
  height: 1.5rem;
}
.signup-login {
  text-align: center;
  margin: 14px 0 0;
  color: #647184;
  font-size: 0.875rem;
}
.signup-pending {
  text-align: center;
  padding: 18px 4px;
}
.signup-pending__mark {
  width: 58px;
  height: 58px;
  border-radius: 50%;
  background: #e6eef8;
  color: var(--primary);
  display: grid;
  place-items: center;
  font-size: 26px;
  margin: 0 auto 16px;
}

/* ── 자가가입 상태 페이지(/s/pending·/s/rejected) ────────────────────────── */
.reg-status__mark {
  width: 58px;
  height: 58px;
  border-radius: 50%;
  background: #e6eef8;
  color: var(--primary);
  display: grid;
  place-items: center;
  font-size: 26px;
  margin: 4px auto 14px;
}
.reg-status__mark--rejected {
  background: #fbe9ec;
  color: #9f2134;
}
.reg-status__title {
  text-align: center;
  margin: 0 0 8px;
}
.reg-status__lead {
  text-align: center;
  color: var(--muted, #637083);
  line-height: 1.6;
  margin: 0 0 20px;
}
.reg-status__actions {
  display: grid;
  gap: 10px;
}

/* ── 운영자 가입 승인(/op/approvals) ─────────────────────────────────────── */
.reg-titlebar {
  display: flex;
  justify-content: space-between;
  align-items: end;
  gap: 12px;
  margin-bottom: 16px;
}
.reg-titlebar h1 {
  margin: 0;
}
.reg-subtitle {
  margin: 6px 0 0;
  color: #657184;
  font-size: 13px;
}
.reg-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  margin-bottom: 16px;
}
.reg-stat {
  background: #fff;
  border: 1px solid var(--line, #d5dde7);
  border-radius: 13px;
  padding: 14px 16px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.reg-stat__label {
  font-size: 13px;
  color: #657184;
  font-weight: 700;
}
.reg-stat__value {
  font-size: 21px;
}
.reg-stat--pending .reg-stat__value {
  color: var(--gold);
}
.reg-layout {
  display: grid;
  grid-template-columns: 380px 1fr;
  gap: 16px;
  align-items: start;
}
.reg-panel {
  background: #fff;
  border: 1px solid var(--line, #d5dde7);
  border-radius: 16px;
  overflow: hidden;
}
/**
 * 신청 종류 토글(0031). 필터와 **다른 축**이므로 다른 줄·다른 모양이다 — 필터는 알약이고
 * 종류는 밑줄 탭이다. 같은 모양으로 두면 여섯 알약이 한 무리로 읽혀 지금 무엇을 보고 있는지
 * 알 수 없게 된다.
 */
.reg-kinds {
  display: flex;
  gap: 4px;
  padding: 12px 14px 0;
  overflow-x: auto;
}
.reg-kind {
  /* 손가락은 44px 다(boundary B7·두 폭 모두 36px 였다). 관리자 센터는 결재 대기 목록을 훑는
     화면이라 실제로 모바일에서 많이 쓰이고, 이 줄은 그 화면에서 가장 자주 눌리는 자리다. */
  min-height: 44px;
  border: 0;
  border-bottom: 3px solid transparent;
  background: transparent;
  padding: 0 10px 6px;
  color: #596678;
  font-weight: 750;
  white-space: nowrap;
  cursor: pointer;
}
.reg-kind--active {
  color: var(--primary);
  border-bottom-color: var(--primary);
}
.reg-filters {
  display: flex;
  gap: 7px;
  padding: 14px;
  border-bottom: 1px solid var(--line, #d5dde7);
  overflow-x: auto;
}
.reg-filter {
  /* 같은 이유로 44px(boundary B7). 알약 모양은 그대로이므로 보이는 것은 세로 8px 뿐이다. */
  min-height: 44px;
  border: 1px solid #c8d0dc;
  background: #fff;
  border-radius: 22px;
  padding: 0 13px;
  color: #596678;
  font-weight: 750;
  white-space: nowrap;
  cursor: pointer;
}
.reg-filter--active {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
}
.reg-list {
  padding: 8px;
  display: flex;
  flex-direction: column;
}
.reg-person {
  width: 100%;
  text-align: left;
  border: 0;
  background: #fff;
  border-radius: 11px;
  padding: 14px;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 7px;
  align-items: center;
  cursor: pointer;
  color: var(--ink, #142235);
}
.reg-person + .reg-person {
  border-top: 1px solid #edf0f4;
}
.reg-person--active {
  background: #edf3fa;
  box-shadow: inset 4px 0 var(--primary);
}
.reg-person strong {
  font-size: 15px;
  display: block;
}
.reg-person small {
  color: #657184;
  font-size: 11px;
}
.reg-badge {
  align-self: start;
  border-radius: 15px;
  padding: 4px 8px;
  font-size: 10px;
  font-weight: 800;
  background: #fff4cf;
  color: #7a5a00;
}
.reg-detail__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 18px 20px;
  border-bottom: 1px solid var(--line, #d5dde7);
}
.reg-detail__head h2 {
  margin: 0;
  font-size: 20px;
}
.reg-detail__back {
  display: none;
}
#reg-detail-body,
#reg-detail-empty,
#reg-decision-result {
  padding: 18px 20px;
}
.reg-type {
  display: inline-block;
  background: #e8eff8;
  color: var(--primary);
  border-radius: 17px;
  padding: 6px 10px;
  font-size: 12px;
  font-weight: 800;
  margin-bottom: 14px;
}
.reg-info {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin-bottom: 18px;
}
.reg-item {
  background: #f7f8fa;
  border: 1px solid #e2e7ed;
  border-radius: 11px;
  padding: 12px;
}
.reg-item span {
  display: block;
  color: #657184;
  font-size: 11px;
  font-weight: 700;
  margin-bottom: 6px;
}
.reg-item strong {
  font-size: 15px;
}
.reg-reauth {
  display: grid;
  gap: 6px;
  margin-bottom: 14px;
  font-size: 13px;
  font-weight: 700;
  color: #445268;
}
.reg-reauth input {
  min-height: 44px;
  border: 1px solid #bdc7d4;
  border-radius: 10px;
  padding: 0 12px;
  font: inherit;
}
.reg-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
.reg-reject,
.reg-approve {
  min-height: 45px;
  border-radius: 10px;
  padding: 0 21px;
  font-weight: 800;
  cursor: pointer;
}
.reg-reject {
  border: 1px solid #9f2134;
  background: #fff;
  color: #9f2134;
}
.reg-approve {
  border: 1px solid var(--primary);
  background: var(--primary);
  color: #fff;
}

@media (max-width: 600px) {
  .reg-stats {
    gap: 7px;
  }
  .reg-stat {
    display: block;
    padding: 10px;
  }
  .reg-stat__value {
    display: block;
    margin-top: 4px;
  }
  .reg-layout {
    display: block;
  }
  .reg-panel--detail {
    display: none;
  }
  .reg-panel--detail.reg-panel--open {
    display: block;
    position: fixed;
    inset: 0;
    /* 콘솔 헤더가 sticky z-index:40 이므로 전체화면 상세는 그 위에 서야 목록 버튼이 눌린다. */
    z-index: 60;
    border: 0;
    border-radius: 0;
    overflow-y: auto;
  }
  .reg-detail__back {
    display: inline-flex;
  }
  .reg-info {
    grid-template-columns: 1fr;
  }
  .reg-actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }
}

/* ── 관리자 센터: 학생 디렉터리·가족 연결 ─────────────────────────────── */
/**
 * 표식(chip)은 **한 줄 안에서** 상태를 말한다. 색만으로 구별하지 않는다 — 색각 이상에서
 * 세 표식이 한 덩어리로 읽히면 "미연결"과 "검토 필요"가 같은 것이 된다. 그래서 글자를 함께
 * 싣고 테두리 모양을 다르게 둔다.
 */
.idn-chip {
  font-style: normal;
  font-size: 11px;
  font-weight: 800;
  padding: 2px 7px;
  border-radius: 999px;
  white-space: nowrap;
  border: 1px solid var(--line, #d5dde7);
  color: #596678;
  background: #fff;
}
.idn-chip--warn {
  color: #8a5a00;
  border-color: #e3c07a;
  background: #fdf6e7;
}
.idn-chip--dup {
  color: #6b3fa0;
  border-style: dashed;
  border-color: #b79fdd;
  background: #f7f3fd;
}
.idn-chip--review {
  color: #9f2134;
  border-color: #e2a3ad;
  background: #fdf1f3;
}
.idn-search {
  display: block;
  padding: 10px 14px 0;
}
.idn-search__label {
  display: block;
  font-size: 12px;
  font-weight: 800;
  color: #657184;
  margin-bottom: 4px;
}
.idn-search input {
  width: 100%;
  min-height: 40px;
  border: 1px solid var(--line, #d5dde7);
  border-radius: 10px;
  padding: 0 10px;
  font: inherit;
}
.idn-pick {
  margin-top: 14px;
}
.idn-pick h3,
.idn-history h3 {
  font-size: 14px;
  margin: 0 0 6px;
}
/* 선택 칸은 손가락으로 눌린다 — 44px 아래로 내려가지 않는다(모바일 명부와 같은 잣대). */
.idn-option {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 0 4px;
  border-bottom: 1px solid var(--line, #eef2f7);
  cursor: pointer;
}
.idn-option input {
  width: 20px;
  height: 20px;
}
.idn-history {
  margin-top: 16px;
}
.idn-history__list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.idn-history__list li {
  padding: 8px 0;
  border-bottom: 1px solid var(--line, #eef2f7);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.idn-history__list small {
  color: #657184;
}

/* ── 관리자 센터: 계정 발급의 자녀 고르개 ───────────────────────────────────

   [왜 규칙이 필요했나] 이 고르개의 각 줄은 label.acc-learner 로 스크립트가 만든다
   (ADMIN_ACCOUNTS_JS 의 loadLearners). 클래스는 붙었지만 대응 규칙이 없어 체크칸이
   브라우저 기본 12px 로 서고 줄 높이도 없었다 — 아이 50명이 12px 간격으로 늘어섰다
   (T28 §3.2 · 1440·390 동일). 표적을 세우는 자리는 여기 한 곳이다.

   [무엇을 키우는가] 키우는 것은 **줄**이다. 명부의 선택 칸(.idn-option)과 같은 잣대로
   줄 전체를 44px 로 세우고 체크칸은 20px 로 눈에 보이게만 둔다 — 라벨이 체크칸을 품고
   있으므로 줄 어디를 눌러도 선택이 뒤집힌다(선택 의미는 그대로다).

   [50명의 밀도] 44px × 50 = 2200px 는 발급 폼을 통째로 밀어낸다. 그래서 고르개가 자기
   안에서 구른다 — 폼의 다른 칸(아이디·발급 단추)이 언제나 한 화면 안에 남는다.

   [좁은 화면] 가로로는 아무 것도 넘치지 않는다: 줄은 세로 축으로만 자라고, 긴 이름은
   끊어서 다음 줄로 간다(고르개 자체가 가로 스크롤을 만들지 않는다). */
.acc-learner-picker {
  max-height: 264px;
  overflow-y: auto;
  border: 1px solid var(--line, #d5dde7);
  border-radius: 10px;
  padding: 0 8px;
}
.acc-learner {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 4px;
  border-bottom: 1px solid var(--line, #eef2f7);
  cursor: pointer;
  overflow-wrap: anywhere;
}
.acc-learner:last-child {
  border-bottom: 0;
}
.acc-learner input {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  margin: 0;
}
/* 초점은 체크칸 하나가 아니라 **줄 위에** 선다 — 20px 짜리 링만으로는 50줄 가운데 어디에
   있는지 읽히지 않는다. 전역 :focus-visible 의 색을 그대로 받는다(색을 새로 만들지 않는다). */
.acc-learner:focus-within {
  outline: 3px solid var(--focus);
  outline-offset: -3px;
  border-radius: 8px;
}

/* ── 보호자 달력(T6·6주 42칸) ─────────────────────────────────────────────

   [왜 42칸을 CSS 가 세지 않는가] 칸의 개수도 첫 칸의 요일도 서버가 정한다(domain month-grid).
   여기서 하는 일은 그 42개를 **일곱 열로 흘리는 것**뿐이다 — 열 수가 코드 두 곳에서 정해지면
   언젠가 한쪽만 바뀌고 격자가 어긋난다.

   [좁은 화면이 기준이다] 320px 에서 일곱 열이면 한 칸의 폭이 40px 남짓이다. 그래서 칸 안의
   글자는 날짜 하나와 한 글자짜리 칩뿐이고, 그보다 긴 것(공휴일 이름)은 넓은 화면에서만 선다.
   가로 스크롤은 어느 폭에서도 만들지 않는다 — minmax(0, 1fr) 이 그 계약이다.

   [칸은 44px 이상이다] 손가락이 닿는 자리이므로 --control-min 을 그대로 쓴다. */
.cal-layout {
  display: grid;
  gap: 16px;
  margin-bottom: 16px;
}
/* [왜 .cal 이 아니라 .calmonth 인가 · K30 S2]

   상담 달력(위쪽 .cal__pane·.cal__legend 무리)이 이미 .cal 을 자기 블록 이름으로 쓴다. 그
   이름은 그쪽에서 **두 열 배치**를 뜻한다: @media (min-width: 901px) 아래의
   .cal { display: grid; grid-template-columns: minmax(0, 1fr) 22.5rem }. 출결 달력이 같은
   이름을 쓰는 동안, 901px 이상에서 이 격자의 두 아이(요일 머리글 · 42칸)가 그 두 열에 하나씩
   배정됐다 — 요일은 왼쪽 1fr 에, 날짜는 오른쪽 22.5rem 에. 요일과 날짜가 수백 px 어긋난 채로
   서 있었고, 어느 단위 시험도 그것을 볼 수 없었다: 두 규칙이 만나는 자리가 브라우저의
   캐스케이드이기 때문이다.

   그래서 이 달력은 자기 이름을 갖는다. 이름이 갈리면 규칙도 갈린다 — 다음 사람이 상담 달력의
   .cal 에 무엇을 더해도 이 격자는 움직이지 않는다. 실 WebKit 이 네 폭에서 그 정렬을 잰다
   (e2e/tests/guardian-attendance-viewports.spec.ts). */
.calmonth {
  min-width: 0;
}
.calmonth__weekdays,
.calmonth__grid {
  display: grid;
  /* 0 을 최소로 두어야 좁은 화면에서 칸이 스스로 넓어져 가로 스크롤을 만들지 않는다. */
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 2px;
}
.calmonth__weekdays {
  margin-bottom: 4px;
}
.calmonth__weekday {
  padding: 4px 0;
  text-align: center;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
}
/* 일요일은 빨강·토요일은 파랑 — 한국 달력의 규약이다(공휴일은 아래에서 요일을 이긴다). */
.calmonth__weekday--sun {
  color: var(--danger);
}
.calmonth__weekday--sat {
  color: var(--secondary);
}
.calmonth__cell {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  min-height: var(--control-min);
  min-width: 0;
  padding: 4px 3px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  text-align: left;
  overflow: hidden;
  cursor: pointer;
}
.calmonth__cell:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 1px;
}
/* 앞뒤 달의 여백 칸 — 지우지 않고 흐리게 둔다(지우면 요일 열이 어긋난다·month-grid 헤더). */
.calmonth__cell--outside {
  background: var(--bg);
  color: var(--text-muted);
  opacity: 0.65;
}
.calmonth__cell--sun .calmonth__num,
.calmonth__cell--holiday .calmonth__num {
  color: var(--danger);
}
.calmonth__cell--sat .calmonth__num {
  color: var(--secondary);
}
.calmonth__cell--today {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.calmonth__cell--open {
  background: var(--info-bg);
  border-color: var(--accent);
}
.calmonth__num {
  font-size: 0.85rem;
  font-weight: 700;
  line-height: 1.2;
}
/* 공휴일 이름은 좁은 화면에서 칸을 밀어내므로 넓은 화면에서만 선다(색과 낭독은 두 폭 모두 산다). */
.calmonth__holiday {
  display: none;
  font-size: 0.68rem;
  line-height: 1.2;
  color: var(--danger);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
/* 칸 안의 **수업 줄**(학생 본인 달력 · 보호자 달력 · T6·K30). 개수(「수업 4개」)도 대표 상태
   하나도 아니고 「수업명 · 출결상태」 를 한 줄씩 **직접** 적는다 — 개수로 접으면 달력을 열어
   놓고도 무슨 수업이 어떻게 됐는지는 날짜를 하나하나 눌러 봐야 한다.

   [무엇이 잘리는가] 여기서만 잘린다. 한 줄은 nowrap + ellipsis 로 칸 폭에 맞춰 접히고,
   그 수업의 전체 이름은 칸의 aria-label 과 날짜 상세에 그대로 있다.

   [몇 줄이 보이는가] 390px 는 **첫 줄 하나**, 721px 이상은 **둘**. 셋째 줄부터는 어느 폭에서도
   접힌다(--rest). 그 경계를 CSS 한 자리에만 두는 이유: JS 가 matchMedia 로 같은 경계를 또
   들면 회전·창 크기 변경마다 다시 그려야 하고, 두 숫자가 언젠가 갈라진다. */
.calmonth__classes {
  display: flex;
  flex-direction: column;
  gap: 1px;
  width: 100%;
  min-width: 0;
}
.calmonth__class {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.66rem;
  line-height: 1.35;
  color: var(--text);
}
/* 390px — 둘째 줄부터 접힌다(첫 줄만 직접 보인다). */
.calmonth__class--n1 {
  display: none;
}
/* 셋째 줄부터는 어느 폭에서도 접힌다 — +N개 가 그 수를 말한다. */
.calmonth__class--rest {
  display: none;
}
/* 접힌 수업의 수. 색이 아니라 **글자**로 말한다(+2개). */
.calmonth__more {
  display: block;
  font-size: 0.62rem;
  font-weight: 700;
  line-height: 1.4;
  color: var(--text-muted);
}
/* 좁은 화면에서는 total-1 표식이, 넓은 화면에서는 total-2 표식이 선다(아래 @media). */
.calmonth__more--wide {
  display: none;
}
/* 오늘의 날짜 숫자 — 테두리(색)에 **밑줄**(모양)을 더한다. 색만으로 말하지 않는다. */
.calmonth__num--today {
  text-decoration: underline;
  text-underline-offset: 2px;
}
/* 고른 칸 — 배경(색)에 **안쪽 두께 2px**(모양)을 더한다. 색만으로 말하지 않는다. */
.calmonth__cell--selected {
  box-shadow: inset 0 0 0 2px var(--accent);
}

/* 721px 이상 — 칸이 두 줄을 담을 만큼 높아진다(위 .calmonth__cell 의 min-height 5.5rem). */
@media (min-width: 721px) {
  .calmonth__class--n1 {
    display: block;
  }
  .calmonth__class {
    font-size: 0.72rem;
  }
  .calmonth__more {
    font-size: 0.68rem;
  }
  .calmonth__more--narrow {
    display: none;
  }
  .calmonth__more--wide {
    display: block;
  }
}

/* 하루 상세. 좁은 화면에서는 화면을 덮고(아래 @media), 넓은 화면에서는 달력 옆에 선다. */
.cal-detail {
  padding: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.cal-detail:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}
.cal-detail__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}
.cal-detail__heading {
  margin: 0;
  font-size: 1.05rem;
}
.cal-detail__holiday {
  margin: 0 0 8px;
  color: var(--danger);
  font-size: 0.9rem;
}
.cal-detail__list {
  display: grid;
  gap: 8px;
}
.cal-detail__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 10px 0;
  border-top: 1px solid var(--border);
}
.cal-detail__row:first-child {
  border-top: 0;
}
.cal-detail__what {
  flex: 1 1 10rem;
  min-width: 0;
  font-weight: 600;
}
.cal-detail__fact {
  color: var(--text-muted);
  font-size: 0.9rem;
}
.cal-detail__row .btn {
  min-height: var(--control-min);
}

/* 달 이동. 두 버튼과 그 사이의 달 이름 — 44px 를 넘기고 좁은 화면에서도 한 줄이다. */
.cal-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.cal-nav__btn {
  min-height: var(--control-min);
  min-width: var(--control-min);
}
.cal-nav__label {
  flex: 1 1 auto;
  text-align: center;
  font-size: 1.05rem;
}

/* 좁은 화면 — 고른 날의 상세가 화면을 덮는다. 달력 위에 겹쳐 두지 않는 이유는 그때 사람이
   무엇을 닫아야 하는지 알 수 없기 때문이다(닫는 버튼은 언제나 위에 있다). */
@media (max-width: 720px) {
  .cal-detail {
    position: fixed;
    inset: 0;
    z-index: 60;
    border-radius: 0;
    overflow-y: auto;
    padding: 16px 16px calc(24px + env(safe-area-inset-bottom, 0px));
    -webkit-overflow-scrolling: touch;
  }
  .cal-detail__bar {
    position: sticky;
    top: 0;
    padding: 4px 0 8px;
    background: var(--surface);
  }
}

/* 넓은 화면 — 달력과 상세가 나란히 선다. 상세는 스크롤을 따라다녀 어느 줄에서도 닫을 수 있다. */
@media (min-width: 721px) {
  .cal-layout {
    grid-template-columns: minmax(0, 1fr) minmax(18rem, 24rem);
    align-items: start;
  }
  .cal-layout__side {
    position: sticky;
    top: 5rem;
    display: grid;
    gap: 12px;
  }
  .calmonth__cell {
    min-height: 5.5rem;
    padding: 6px;
  }
  .calmonth__num {
    font-size: 0.95rem;
  }
  .calmonth__holiday {
    display: block;
  }
}

/* ── 출결 화면(§5·Task 9) ─────────────────────────────────────────────── */
.att-titlebar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}
.att-subtitle {
  margin: 4px 0 0;
  color: var(--text-muted);
  font-size: 0.95rem;
}
.att-controls {
  display: grid;
  gap: 10px;
  margin-bottom: 16px;
}
.att-controls__row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.att-field__label {
  min-width: 3rem;
  font-weight: 600;
}
.att-field__input {
  min-height: var(--control-min);
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 1rem;
  max-width: 100%;
}
/* 수업 시간 칩(0041) — 칩 자체는 .btn 이라 이미 44px 다. 이 줄은 **접히기만** 한다:
   좁은 화면에서 타임이 셋 넷이어도 가로로 밀지 않고 다음 줄로 내려간다. */
.att-times {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  min-width: 0;
}
.att-times__status {
  margin: 0;
  color: var(--text-muted);
}
.att-stats {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 8px;
  margin-bottom: 16px;
}
.att-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 10px 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.att-stat__label {
  font-size: 0.8rem;
  color: var(--text-muted);
}
.att-stat__value {
  font-size: 1.4rem;
}
.att-roster {
  display: grid;
  gap: 8px;
  /* 하단 고정 작업바에 명단이 가리지 않도록 여유를 둔다. */
  padding-bottom: calc(88px + env(safe-area-inset-bottom, 0px));
}
.att-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.att-card__name {
  font-weight: 600;
}
.att-card__marks {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
}
.att-mark {
  min-height: var(--control-min);
  min-width: var(--control-min);
  padding: 8px 4px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font-size: 0.95rem;
  cursor: pointer;
}
.att-mark--on {
  background: var(--primary);
  color: #fff;
  border-color: var(--primary);
  font-weight: 700;
}
.att-mark:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}
.att-card__state {
  font-size: 0.85rem;
  color: var(--text-muted);
}
.att-manage__row,
.att-manage__add {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 0;
  flex-wrap: wrap;
}
.att-manage__name {
  flex: 1 1 auto;
  min-width: 8rem;
}
.att-manage__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.att-manage__erase {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  flex-basis: 100%;
}
.att-existing {
  margin-top: 1rem;
  border-top: 1px solid var(--border);
  padding-top: 0.75rem;
}
.att-existing__head {
  font-weight: 600;
  margin: 0 0 0.5rem;
}
.att-existing__row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 6px 0;
}
.att-existing__name {
  flex: 1 1 auto;
  min-width: 8rem;
}
.att-existing__empty {
  color: var(--text-muted);
  font-size: 0.9rem;
}
.att-history-panel {
  margin-top: 1rem;
}
/*
 * R2 패널(반 운영 일정·교직원 배정·사유 심사 함·내 수업). 네 패널이 같은 골격을 쓴다 — 각기
 * 다른 클래스를 두면 한 곳만 고쳐지고 네 화면이 서로 다르게 보이게 된다.
 */
.att-panel {
  margin-top: 1rem;
  padding: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.att-panel__heading {
  margin: 0 0 8px;
  font-size: 1.1rem;
}
.att-panel__subheading {
  margin: 16px 0 8px;
  font-size: 0.95rem;
  color: var(--text-muted);
}
.att-panel__list {
  display: grid;
  gap: 4px;
  margin-bottom: 8px;
}
/*
 * 한 줄 안에서 사실(글자)과 조작(버튼)이 나란히 선다. flex + gap 없이 두면 버튼이 글자 위에
 * 겹쳐 앉는다 — 실제로 그렇게 났고, 스크린샷을 볼 때만 드러났다.
 */
.att-panel__row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 8px 0;
  border-top: 1px solid var(--border);
}
/* 사실 칸이 남는 폭을 먹고 버튼을 오른쪽으로 민다. */
.att-panel__row-facts {
  flex: 1 1 12rem;
}
/* 다만 그 줄이 실은 것이 **조작이 아니라 사실**이면(마감 안내) 밀지 않는다 — 넓은 화면에서
   행 끝으로 밀려난 안내는 어느 날에 대한 말인지 눈으로 잇기 어렵다(guardian F9). */
.att-panel__row--note .att-panel__row-facts {
  flex: 0 1 auto;
}
/* 계정·출결 행 uuid — 좁은 화면에서 줄을 밀지 않게 줄여 싣고 넘치면 자른다. */
.att-panel__id {
  flex: 1 1 12rem;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.8rem;
  color: var(--text-muted);
}
.att-panel__note {
  color: var(--text-muted);
  font-size: 0.85rem;
}
.att-history__row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 8px 0;
  border-top: 1px solid var(--border);
}
.att-history__date {
  flex: 1 1 auto;
  font-weight: 600;
}
.att-history__counts {
  color: var(--text-muted);
  font-size: 0.85rem;
}
.att-actionbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px;
  padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  background: var(--surface);
  border-top: 1px solid var(--border);
  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06);
  z-index: 10;
}
.att-actionbar__note {
  font-weight: 600;
}
.att-actionbar__buttons {
  display: flex;
  gap: 8px;
}
@media (max-width: 900px) {
  /* 좁은 화면에서는 콘솔 탭 바가 화면 맨 아래(약 9rem)에 고정된다(op-tabs·z-50). 그 위에
     두 번째 고정 바를 겹치면 탭 바가 마감·전체 출석 탭을 가로채고 화면 절반을 바가 먹는다.
     그래서 작업바는 **흐름 안(static)**으로 두어 명단 끝에 놓이고, 콘솔이 이미 확보한
     main 하단 여백(9rem)이 탭 바로부터 이 바를 띄운다. */
  .att-actionbar {
    position: static;
    box-shadow: none;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    margin-top: 0.75rem;
    flex-wrap: wrap;
    padding-bottom: 12px;
  }
  .att-roster {
    padding-bottom: 0;
  }
}
@media (max-width: 520px) {
  .att-stats {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* ── 출결 관리자 콘솔(T2) ──────────────────────────── ATT-ADMIN:START ──
   자기 이름(.att-admin 및 그 자손)으로만 서는 자족 블록이다 — 선생님·보호자 문서에는 이 클래스가
   하나도 없으므로 저 화면들의 픽셀은 이 블록이 있든 없든 같다.

   색은 앱 팔레트 그대로 쓴다: 남색(--primary)이 자리를, 금색(--gold)이 **지금 있는 자리**만
   가리킨다(장식으로 넓게 칠하지 않는다 — 흰 배경 위 금색은 대비가 모자라 글자를 싣지 않는다).
   조작면의 최소 높이는 숫자를 다시 적지 않고 --control-min(44px)을 참조한다. */
.att-admin {
  display: grid;
  grid-template-columns: 15rem minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
}
.att-admin__body {
  min-width: 0;
}
/* 내비는 남색 판 하나다. 세로 목록이 기본이고 좁은 폭에서 가로 줄로 접힌다. */
.att-nav {
  display: grid;
  gap: 4px;
  padding: 10px;
  background: var(--primary);
  border-radius: var(--radius);
  position: sticky;
  top: 1rem;
}
.att-nav__item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: var(--control-min);
  padding: 10px 12px;
  border: 1px solid transparent;
  border-radius: 10px;
  background: transparent;
  color: #eef3f9;
  font: inherit;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.att-nav__item:hover,
.att-nav__item:focus-visible {
  background: rgba(255, 255, 255, 0.14);
  color: #ffffff;
}
/* 현재 자리는 색이 아니라 상태다 — aria-pressed 가 곧 선택자다(화면과 보조기술이 같은 말을 한다). */
.att-nav__item[aria-pressed='true'] {
  background: var(--surface);
  color: var(--primary);
  border-color: var(--gold);
}

/* 현황판으로 가는 앵커(K38). 같은 줄에 서는 여섯 버튼과 **한 자도 다르지 않게** 보여야 한다 —
   자리표가 아니라 목적지의 종류가 다를 뿐이므로, 모양이 다르면 사람은 그것을 다른 등급의
   조작으로 읽는다. 최소 높이는 여섯과 같은 토큰에서 온다(손가락이 닿는 크기는 한 값이다). */
.att-nav__link {
  display: flex;
  align-items: center;
  min-height: var(--control-min);
  text-decoration: none;
}
.att-nav__label {
  flex: 1 1 auto;
  min-width: 0;
}
.att-nav__badge {
  flex: 0 0 auto;
  min-width: 1.5rem;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--danger);
  color: #ffffff;
  font-size: 0.78rem;
  font-weight: 700;
  text-align: center;
}
.att-view {
  display: block;
  min-width: 0;
}
/* 아직 할 말이 없는 상태 줄은 자리를 차지하지 않는다 — 빈 파란 띠가 서 있으면 사람은 그것을
   읽으려 하고, 읽을 것이 없다는 사실을 화면이 두 번 말하게 된다. */
.att-admin .status:empty,
.att-wizard .status:empty {
  display: none;
}
/* 콘솔 안의 입력·선택은 버튼과 같은 최소 높이를 갖는다 — 폰에서 손가락이 닿는 크기(44px)는
   버튼만의 계약이 아니다(상태 필터 select 가 23px 로 서던 자리·실 WebKit 계측). 마법사도 같은
   규칙을 쓴다(체크박스는 이 클래스를 쓰지 않으므로 그대로 작다). */
.att-admin .att-field__input,
.att-wizard .att-field__input {
  min-height: var(--control-min);
}
/*
 * WebKit 의 native select(menulist)는 padding·min-height 를 무시한다 — iPhone 실측에서 상태
 * 필터가 23px 로 섰다(44px 계약 위반). appearance 를 끄고 우리가 그린다. 화살표는 **그라디언트
 * 두 장**으로 만든다: 이 앱의 CSP 는 img-src 'self' 라 data: URI 아이콘을 실을 수 없다.
 * 범위는 관리자 콘솔·마법사뿐이다(다른 화면의 select 는 이 pass 의 범위가 아니다).
 */
.att-admin select.att-field__input,
.att-wizard select.att-field__input {
  -webkit-appearance: none;
  appearance: none;
  background-color: var(--surface);
  background-image: linear-gradient(45deg, transparent 50%, var(--text-muted) 50%),
    linear-gradient(135deg, var(--text-muted) 50%, transparent 50%);
  background-position: calc(100% - 17px) calc(50% - 2px), calc(100% - 12px) calc(50% - 2px);
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: 2rem;
}
.att-view__head {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 1rem;
}
.att-view__title {
  margin: 0;
  font-size: 1.25rem;
  color: var(--primary);
}
.att-view__lead {
  margin: 0.15rem 0 0;
  color: var(--text-muted);
  font-size: 0.9rem;
}
.att-view__head .btn {
  margin-left: auto;
}
/* 왼쪽 목록 + 오른쪽 상세. 좁아지면 한 줄로 접힌다(가로 스크롤 0). */
.att-split {
  display: grid;
  grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
  gap: 1rem;
  align-items: start;
}
.att-split__side,
.att-split__main {
  min-width: 0;
}
.att-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 0.75rem;
}
.att-toolbar .att-field__input {
  flex: 1 1 8rem;
  min-width: 0;
}
.att-class-list {
  display: grid;
  gap: 8px;
}
.att-class-card {
  display: grid;
  gap: 2px;
  width: 100%;
  min-height: var(--control-min);
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.att-class-card:hover,
.att-class-card:focus-visible {
  border-color: var(--primary);
}
.att-class-card[aria-pressed='true'] {
  border-color: var(--primary);
  border-left: 4px solid var(--gold);
  background: var(--info-bg);
}
.att-class-card__name {
  font-weight: 700;
}
.att-class-card__meta {
  color: var(--text-muted);
  font-size: 0.82rem;
}
.att-detail__hero {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  flex-wrap: wrap;
  padding-bottom: 0.5rem;
}
.att-detail__hero > div {
  flex: 1 1 12rem;
  min-width: 0;
}
.att-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 0.85rem;
}
.att-tab {
  min-height: var(--control-min);
  padding: 8px 12px;
  border: 0;
  border-bottom: 3px solid transparent;
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.att-tab:hover,
.att-tab:focus-visible {
  color: var(--primary);
}
.att-tab[aria-selected='true'] {
  color: var(--primary);
  border-bottom-color: var(--gold);
}
.att-tabpage {
  display: block;
  min-width: 0;
}
.att-info-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 10px;
  margin-bottom: 0.85rem;
}
.att-info {
  display: grid;
  gap: 2px;
}
.att-info__label {
  color: var(--text-muted);
  font-size: 0.8rem;
}
.att-info__value {
  color: var(--text);
}
/* 사람 한 줄(이름 + 사실 + 행동). 이름이 남는 폭을 먹고 버튼이 오른쪽으로 밀린다. */
.att-person {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 10px 0;
  border-top: 1px solid var(--border);
}
.att-person__body {
  flex: 1 1 12rem;
  min-width: 0;
}
.att-person__name {
  display: block;
  font-weight: 600;
}
.att-person__meta {
  display: block;
  color: var(--text-muted);
  font-size: 0.82rem;
}
.att-person__warn {
  display: block;
  color: var(--danger);
  font-size: 0.82rem;
}
.att-chip {
  display: inline-block;
  padding: 2px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text-muted);
  font-size: 0.78rem;
  font-weight: 700;
  white-space: nowrap;
}
.att-chip--ok {
  border-color: var(--success);
  background: var(--success-bg);
  color: var(--success);
}
.att-chip--warn {
  border-color: var(--danger);
  background: var(--danger-bg);
  color: var(--danger);
}
/* ── 수업 만들기 마법사 ───────────────────────────────────────────────── */
.att-wizard {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: rgba(22, 32, 43, 0.55);
}
.att-wizard[hidden] {
  display: none;
}
.att-wizard__dialog {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 46rem;
  max-height: 92vh;
  overflow: auto;
  padding: 1.1rem;
  background: var(--surface);
  border-radius: var(--radius);
  border-top: 4px solid var(--gold);
}
.att-wizard__head {
  display: flex;
  align-items: center;
  gap: 10px;
}
.att-wizard__title {
  flex: 1 1 auto;
  margin: 0;
  font-size: 1.15rem;
  color: var(--primary);
}
.att-wizard__steps {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0.85rem 0;
  padding: 0;
  list-style: none;
}
.att-wizard__step {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text-muted);
  font-size: 0.82rem;
  font-weight: 600;
}
.att-wizard__step[aria-current='step'] {
  border-color: var(--primary);
  background: var(--info-bg);
  color: var(--primary);
}
.att-wizard__step-no {
  display: grid;
  place-items: center;
  width: 1.35rem;
  height: 1.35rem;
  border-radius: 50%;
  background: var(--primary);
  color: #ffffff;
  font-size: 0.75rem;
}
.att-wizard__body {
  flex: 1 1 auto;
  min-height: 0;
}
.att-wizard__page {
  display: block;
}
.att-wizard__page[hidden] {
  display: none;
}
.att-wizard__heading {
  margin: 0 0 0.2rem;
  font-size: 1rem;
  color: var(--primary);
}
.att-wizard__lead {
  margin: 0 0 0.75rem;
  color: var(--text-muted);
  font-size: 0.86rem;
}
.att-wizard__learners {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: 6px;
  max-height: 16rem;
  overflow: auto;
}
.att-wizard__learner {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: var(--control-min);
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: 10px;
}
.att-wizard__summary {
  margin-top: 0.85rem;
  padding: 12px;
  border: 1px solid var(--border);
  border-left: 4px solid var(--gold);
  border-radius: 10px;
  background: var(--bg);
}
.att-wizard__foot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 0.85rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border);
}
.att-wizard__foot .status {
  flex: 1 1 10rem;
  margin: 0;
}
.att-wizard__buttons {
  display: flex;
  gap: 8px;
  margin-left: auto;
}
/* 시간표 한 줄(요일·시작·끝·삭제) — 좁은 폭에서 칸이 줄바꿈된다. */
.att-time-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 0;
  border-top: 1px solid var(--border);
}
.att-time-row .att-field__input {
  flex: 1 1 6rem;
  min-width: 0;
}
@media (max-width: 900px) {
  /* 폰에서는 내비가 위로 올라와 가로로 눕고, 분할이 한 줄로 접힌다(가로 스크롤 0). */
  .att-admin {
    grid-template-columns: minmax(0, 1fr);
  }
  .att-nav {
    position: static;
    grid-auto-flow: column;
    grid-auto-columns: minmax(7.5rem, 1fr);
    overflow-x: auto;
  }
  .att-split {
    grid-template-columns: minmax(0, 1fr);
  }
  .att-wizard {
    padding: 0;
  }
  .att-wizard__dialog {
    max-width: none;
    max-height: 100vh;
    height: 100%;
    border-radius: 0;
  }
}
/* ─────────────────────────────────────────────────────── ATT-ADMIN:END ── */

/* ── 통합 포털(로그인·서비스 런처) ───────────────────────── PORTAL:START ──
   자기 이름(.page--portal)으로만 서는 자족 블록이다 — 콘솔·학생 규칙을 건드리지 않는다.
   남색 띠 + 금색 얇은 강조만 쓰고 그라디언트·글래스는 없다. 깊이는 낮은 ring shadow 한 단계만
   써서 학교 운영 화면다운 명확한 계층을 만든다. */
.page--portal {
  max-width: none;
  padding: 0 0 2.5rem;
  min-height: 100vh;
  background: #f3f5f8;
}

.page--portal .site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  padding: 0;
  margin: 0 0 1.5rem;
  background: var(--primary);
  border-bottom: 3px solid var(--gold);
}

.page--portal .site-header__in {
  max-width: 68rem;
  margin: 0 auto;
  padding: 0.6rem 1.25rem;
  gap: 0.65rem;
}

.page--portal .site-name {
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: #ffffff;
}

.page--portal .site-header h1 {
  font-size: 0.78rem;
  font-weight: 400;
  line-height: 1.35;
  color: #c8d6e8;
}

.page--portal main {
  max-width: 68rem;
  margin: 0 auto;
  padding: 0 1.25rem;
}

/* ── 로그인: 데스크톱 두 구역(브랜드·폼), 모바일 한 줄 스택 ────────────────── */
.portal-login {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(22rem, 0.85fr);
  gap: clamp(2.5rem, 7vw, 6.5rem);
  align-items: center;
  min-height: calc(100vh - 7rem);
  padding: 2.5rem 0 5rem;
}

.portal-login__brand {
  max-width: 34rem;
  padding: 0;
}

.portal-login__eyebrow {
  margin: 0 0 0.85rem;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--secondary);
}

.portal-login__headline {
  max-width: 11em;
  margin: 0;
  color: var(--primary);
  font-size: clamp(2.15rem, 5vw, 3.55rem);
  line-height: 1.08;
  letter-spacing: -0.045em;
}

.portal-login__lede {
  max-width: 31rem;
  margin: 1.35rem 0 0;
  font-size: 1.05rem;
  line-height: 1.75;
  color: var(--text-muted);
}

.portal-login__panel {
  min-width: 0;
  width: 100%;
  max-width: 28rem;
  justify-self: end;
}

.portal-login__panel .card {
  padding: clamp(1.5rem, 4vw, 2rem);
  border-color: transparent;
  border-radius: 16px;
  box-shadow: 0 0 0 1px rgba(30, 58, 95, 0.08), 0 12px 32px rgba(22, 32, 43, 0.08);
}

.portal-login__panel .card > h2 {
  margin-bottom: 0.4rem;
  font-size: 1.35rem;
}

.portal-login__panel .op-login-form {
  margin-top: 1.4rem;
}

.portal-login__panel .btn {
  width: 100%;
}

/* 가입 문을 여는 한 줄. 주 버튼(로그인)과 보조 버튼(회원가입) 사이의 숨이며, 그 둘의 차례가
   곧 "먼저 할 일"의 차례다(학생 로그인 화면과 같은 결). */
.portal-login__signup-lead {
  margin: 1.35rem 0 0.5rem;
  text-align: center;
  color: var(--text-muted);
}

/* ── 서비스 런처: 역할은 신원, 카드는 실제 행동으로 분리한다 ─────────────── */
.portal-dashboard {
  padding: 1.5rem 0 2rem;
}

.portal-dashboard__eyebrow {
  margin: 0 0 0.35rem;
  color: var(--secondary);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.1em;
}

.portal-dashboard__title {
  margin: 0;
  color: var(--primary);
  font-size: clamp(1.75rem, 4vw, 2.45rem);
  line-height: 1.2;
  letter-spacing: -0.035em;
}

.portal-dashboard__description {
  max-width: 44rem;
  margin: 1.25rem 0 0;
}

.portal-identity {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin: 1.75rem 0 0;
  padding: 0.9rem 1rem;
  background: var(--surface);
  border: 1px solid rgba(30, 58, 95, 0.12);
  border-radius: 10px;
}

.portal-identity__label {
  flex: 0 0 auto;
  margin: 0;
  color: var(--text);
  font-size: 0.85rem;
  font-weight: 700;
}

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

.portal-badge {
  display: inline-flex;
  align-items: center;
  padding: 0.25rem 0.75rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--primary);
  background: var(--info-bg);
  border: 1px solid var(--border);
  border-radius: 999px;
}

.portal-services {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1.1rem;
  margin: 1rem 0 1.75rem;
}

.portal-service {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  min-height: 15rem;
  padding: 1.4rem;
  background: var(--surface);
  border: 1px solid rgba(30, 58, 95, 0.12);
  border-radius: 14px;
  box-shadow: 0 4px 14px rgba(22, 32, 43, 0.05);
  transition: border-color 140ms ease, box-shadow 140ms ease, transform 140ms ease;
}

.portal-service:hover {
  border-color: rgba(30, 58, 95, 0.38);
  box-shadow: 0 8px 22px rgba(22, 32, 43, 0.08);
  transform: translateY(-2px);
}

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

.portal-service h3 {
  margin: 0;
  font-size: 1.1rem;
  color: var(--primary);
}

.portal-service__description {
  margin: 0;
  flex: 1 1 auto;
}

.portal-service__action {
  width: 100%;
  margin-top: 0.4rem;
}

.portal-status {
  margin: 0.5rem 0 1.5rem;
}

/*
 * 관리자 센터 입구. 서비스 카드와 **다른 줄·다른 모양**이다 — 카드는 "다른 오리진으로
 * 넘어간다"이고 이것은 "이 포털 안의 관리자 화면으로 들어간다"다. 같은 격자에 끼워 넣으면
 * 세 번째 서비스처럼 읽히고, 그러면 사람은 여기서도 handoff 가 도는 줄 안다.
 */
.portal-admin-entry {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.9rem;
  margin: 1.5rem 0 0;
  padding: 1.1rem 1.25rem;
  border: 1px solid var(--border);
  border-radius: 14px;
}

.portal-admin-entry__text {
  flex: 1 1 18rem;
  min-width: 0;
}

.portal-admin-entry__text h3 {
  margin: 0 0 0.25rem;
  font-size: 1.05rem;
}

.portal-admin-entry__text p {
  margin: 0;
  font-size: 0.9rem;
}

.portal-logout {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
}

@media (max-width: 780px) {
  .page--portal .site-header__in {
    flex-wrap: wrap;
    padding: 0.45rem 0.9rem 0.5rem;
  }

  .portal-login {
    grid-template-columns: 1fr;
    gap: 1.25rem;
    padding: 1rem 0 2rem;
  }

  .portal-login__brand {
    padding: 1rem 0 0;
  }

  /* 좁은 화면에서는 설명과 버튼이 한 줄에 서지 못한다 — 버튼을 폭 전체로 내린다. */
  .portal-admin-entry {
    align-items: stretch;
  }

  .portal-admin-entry .btn {
    width: 100%;
  }

  .portal-login__headline {
    font-size: 2.15rem;
  }

  .portal-login__panel {
    max-width: none;
    justify-self: stretch;
  }

  .portal-identity {
    align-items: flex-start;
    flex-direction: column;
    gap: 0.55rem;
  }

  .portal-services {
    grid-template-columns: 1fr;
  }
}

/* ── AVAILABILITY:START ─ 상담 시간 달력(§7.1·R4·T5) ───────────────────── */

/* 격자와 판. 넓은 화면에서는 판이 격자 **옆**에 서고, 좁은 화면에서는 화면을 채운다 —
   조작마다 다른 자리에 뜨면 사람은 매번 그것을 찾아야 한다. */
.avail__split {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}

@media (min-width: 901px) {
  .avail__split {
    grid-template-columns: minmax(0, 1fr) minmax(20rem, 26rem);
  }

  /* 달을 넘기며 여러 날짜를 손보는 동안 판이 화면 밖으로 밀려나지 않는다. */
  .avail__side {
    position: sticky;
    top: 1rem;
  }
}

.avail__monthbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin: 0 0 0.75rem;
}

.avail__month {
  margin: 0;
  font-size: 1.15rem;
  text-align: center;
  flex: 1 1 auto;
}

.avail__grid {
  display: grid;
  gap: 2px;
}

.avail__row {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 2px;
}

.avail__dow {
  text-align: center;
  font-size: 0.8rem;
  color: var(--text-muted);
  padding: 0.25rem 0;
}

.avail__cell {
  display: block;
  min-width: 0;
}

/* 한 날짜의 표적. 최소 44px 는 좁은 화면에서도 지켜진다(글자만 작아진다). */
.avail__day {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  width: 100%;
  min-height: var(--control-min);
  padding: 0.35rem 0.3rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 0.75rem;
  line-height: 1.3;
  text-align: left;
  cursor: pointer;
  overflow-wrap: anywhere;
}

.avail__day:hover:not(:disabled) {
  border-color: var(--accent);
}

.avail__day:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

/* 지난 날짜·창 밖은 읽을 수는 있고 고를 수는 없다. 회색만으로 말하지 않고
   aria-label 이 "편집할 수 없음"을 함께 읽는다. */
.avail__day:disabled {
  background: var(--bg);
  color: var(--text-muted);
  cursor: default;
}

.avail__day[data-today="true"] {
  border-color: var(--accent);
  border-width: 2px;
}

.avail__day[aria-pressed="true"] {
  background: var(--info-bg);
  border-color: var(--accent);
  border-width: 2px;
}

.avail__num {
  font-weight: 700;
  font-size: 0.9rem;
}

/* 처분은 **글자로** 말한다. 색은 보조일 뿐이고 흑백·고대비에서도 같은 문장이 읽힌다. */
.avail__mode {
  font-size: 0.7rem;
  color: var(--text-muted);
}

.avail__mode[data-mode="CLOSED"] {
  color: var(--danger);
  font-weight: 600;
}

.avail__mode[data-mode="CUSTOM"] {
  color: var(--secondary);
  font-weight: 600;
}

.avail__hours {
  font-size: 0.68rem;
  color: var(--text-muted);
}

.avail__booked {
  font-size: 0.68rem;
  font-weight: 600;
  color: var(--primary);
}

.avail__legend {
  margin: 0.75rem 0 0;
  font-size: 0.85rem;
  color: var(--text-muted);
}

.avail__actions {
  margin-top: 1rem;
}

/* 네 판과 확인은 같은 상자다 — 어느 판인지는 **제목**이 말한다. */
.avail-panel,
.avail-preview {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem;
}

.avail-panel:focus-visible,
.avail-preview:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

.avail-panel__title {
  margin: 0 0 0.5rem;
  font-size: 1.05rem;
}

.avail-panel__meta {
  margin: 0 0 0.75rem;
  font-size: 0.9rem;
  color: var(--text-muted);
}

.avail-panel__times {
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
}

.avail-weekly {
  list-style: none;
  margin: 0 0 1rem;
  padding: 0;
  display: grid;
  gap: 0.85rem;
}

.avail-weekly__row {
  border-top: 1px solid var(--border);
  padding-top: 0.6rem;
}

.avail-weekly__toggle {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: var(--control-min);
  font-weight: 600;
}

.avail-weekly__check {
  width: 1.35rem;
  height: 1.35rem;
}

.avail-preview__summary {
  margin: 0 0 0.75rem;
  font-weight: 600;
}

.avail-preview__days {
  list-style: none;
  margin: 0 0 1rem;
  padding: 0;
  display: grid;
  gap: 0.35rem;
  max-height: 40vh;
  overflow-y: auto;
}

.avail-preview__day {
  font-size: 0.9rem;
  color: var(--text-muted);
}

/* 설정 화면에서 달력으로 가는 문. 버튼과 설명이 한 줄에 서지 못하면 아래로 접힌다. */
.op-settings-entry {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.85rem;
  margin: 0 0 0.85rem;
}

.op-settings-entry__hint {
  font-size: 0.9rem;
  color: var(--text-muted);
}

@media (max-width: 900px) {
  /* 좁은 화면의 판은 화면을 **채운다**. 격자 아래에 이어 붙이면 사람이 스크롤로
     그것을 찾아야 하고, 저장 전에 읽어야 하는 문장들이 화면 밖에 남는다. */
  .avail-panel:not([hidden]),
  .avail-preview:not([hidden]) {
    position: fixed;
    inset: 0;
    z-index: 40;
    height: 100dvh;
    max-height: 100dvh;
    overflow-y: auto;
    border: 0;
    border-radius: 0;
    padding: 1rem 1rem 5rem;
  }

  .avail-preview__days {
    max-height: none;
  }
}

@media (max-width: 600px) {
  /* 320px 폭에서도 일곱 열이 그대로 선다 — 글자만 작아지고 시각은 판에서 읽는다. */
  .avail__day {
    padding: 0.3rem 0.15rem;
    font-size: 0.65rem;
  }

  .avail__hours {
    display: none;
  }

  .avail__num {
    font-size: 0.8rem;
  }

  .avail__mode,
  .avail__booked {
    font-size: 0.6rem;
  }

  .avail__actions .btn {
    width: 100%;
  }
}
/* ── AVAILABILITY:END ─────────────────────────────────────────────────── */
/* ─────────────────────────────────────────────────────── PORTAL:END ── */
/* ── 출결 현황판(K38·DASHBOARD:BEGIN) ─────────────────────────────────────
   시각 정본은 sketches/attendance-dashboard-visual-analytics/(D안)이다. 여기 있는 것은 그
   프로토타입의 **자리 배치**뿐이고 색·타이포·초점은 전부 이 파일 위쪽의 운영 토큰에서 온다
   (프로토타입의 값은 한 줄도 옮기지 않는다).

   [가로로 밀지 않는다] 좁은 화면에서 가로 스크롤이 허용된 자리는 **반×날짜 격자 하나**다
   (.dash-mx). 22개 열을 390px 에 접으면 칸의 글자가 사라지고, 글자가 사라지면 이 화면의 규율
   하나(색이 아니라 글자가 상태를 말한다)가 깨진다. 나머지는 전부 감싸거나 줄인다. */

.dash-asof {
  margin: 0 0 0.5rem;
  font-size: 0.82rem;
  color: var(--text-muted);
}

.dash-status {
  margin: 0 0 0.75rem;
}

/* ── 기간 막대 ─────────────────────────────────────────────────────────── */
.dash-range {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.75rem 0.9rem;
  margin: 0 0 1rem;
}

.dash-range__sum {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
}

.dash-range__sum p {
  margin: 0;
  font-weight: 600;
  flex: 1 1 14rem;
  min-width: 0;
}

.dash-range__toggle {
  min-height: var(--control-min);
  padding: 0 0.9rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--accent);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.dash-range__body {
  margin: 0.75rem 0 0;
  border: 0;
  padding: 0;
}

.dash-range__body legend {
  padding: 0;
  font-weight: 600;
  font-size: 0.9rem;
}

.dash-range__row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.6rem 0.75rem;
  margin: 0.5rem 0 0;
}

.dash-field {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
  flex: 1 1 9.5rem;
}

.dash-field label {
  font-size: 0.82rem;
  color: var(--text-muted);
}

.dash-field input {
  min-height: var(--control-min);
  padding: 0 0.6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  min-width: 0;
  width: 100%;
}

.dash-presets {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.6rem 0 0;
}

.dash-chip {
  min-height: var(--control-min);
  padding: 0 0.8rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
}

.dash-chip[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: #ffffff;
  font-weight: 600;
}

.dash-range__note {
  margin: 0.6rem 0 0;
  font-size: 0.82rem;
  color: var(--text-muted);
}

/* ── 세 자리(탭) ───────────────────────────────────────────────────────── */
.dash-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin: 0 0 1rem;
  border-bottom: 1px solid var(--border);
}

.dash-tab {
  min-height: var(--control-min);
  padding: 0 0.9rem;
  border: 1px solid transparent;
  border-bottom: 0;
  border-radius: var(--radius) var(--radius) 0 0;
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.dash-tab[aria-selected="true"] {
  background: var(--surface);
  border-color: var(--border);
  color: var(--accent);
  box-shadow: inset 0 3px 0 var(--accent);
}

.dash-panel[aria-busy="true"] {
  opacity: 0.6;
}

/* ── 카드 ──────────────────────────────────────────────────────────────── */
.dash-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.9rem 1rem 1rem;
  margin: 0 0 1rem;
  min-width: 0;
}

.dash-card > h2 {
  margin: 0 0 0.15rem;
  font-size: 1.02rem;
}

.dash-figcap,
.dash-note,
.dash-caveat {
  margin: 0.5rem 0 0;
  font-size: 0.82rem;
  color: var(--text-muted);
}

.dash-caveat {
  color: var(--text);
  background: var(--info-bg);
  border-radius: var(--radius);
  padding: 0.5rem 0.7rem;
}

.dash-empty {
  margin: 0.5rem 0 0;
  padding: 0.9rem;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  color: var(--text-muted);
  text-align: center;
}

/* ── 지표 여섯 ─────────────────────────────────────────────────────────── */
.dash-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  gap: 0.6rem;
  margin: 0 0 1rem;
}

.dash-kpi {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 4px solid var(--border);
  border-radius: var(--radius);
  padding: 0.6rem 0.75rem;
  min-width: 0;
}

.dash-kpi--alert {
  border-left-color: var(--danger);
}

.dash-kpi--warn {
  border-left-color: var(--gold);
}

.dash-kpi__value {
  font-size: 1.5rem;
  line-height: 1.2;
}

.dash-kpi__label {
  font-size: 0.85rem;
  font-weight: 600;
}

.dash-kpi__note {
  font-size: 0.74rem;
  color: var(--text-muted);
}

/* ── 처리 대기열 ───────────────────────────────────────────────────────── */
.dash-queue-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin: 0.5rem 0;
}

.dash-qgroup {
  margin: 0.8rem 0 0.35rem;
  font-size: 0.85rem;
  color: var(--text-muted);
}

.dash-qitem {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-height: var(--control-min);
  padding: 0.5rem 0.7rem;
  margin: 0 0 0.35rem;
  border: 1px solid var(--border);
  border-left: 5px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  text-decoration: none;
}

.dash-qitem--overdue {
  border-left-color: var(--danger);
}

.dash-qitem--unmarked {
  border-left-color: var(--gold);
}

.dash-qitem--excuse {
  border-left-color: var(--secondary);
}

.dash-qitem--scheduled {
  border-left-style: dotted;
}

.dash-qitem__body {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1 1 auto;
}

.dash-qitem__t {
  font-weight: 600;
  overflow-wrap: anywhere;
}

.dash-qitem__d {
  font-size: 0.82rem;
  color: var(--text-muted);
  overflow-wrap: anywhere;
}

.dash-qitem__age {
  flex: 0 0 auto;
  font-size: 0.8rem;
  color: var(--text-muted);
}

/* ── 그림 ──────────────────────────────────────────────────────────────── */
.dash-figure {
  margin: 0.6rem 0 0;
  max-width: 100%;
}

.dash-chart {
  display: block;
  width: 100%;
  height: auto;
  max-width: 100%;
}

.dash-gridline {
  stroke: var(--border);
  stroke-width: 1;
}

.dash-axis {
  stroke: var(--text-muted);
  stroke-width: 1;
}

.dash-refline {
  stroke: var(--text-muted);
  stroke-width: 1;
  stroke-dasharray: 3 3;
}

.dash-avgline {
  stroke: var(--secondary);
  stroke-width: 1.5;
  stroke-dasharray: 6 4;
}

.dash-lin {
  stroke: var(--primary);
  stroke-width: 2.5;
  fill: none;
}

.dash-lin--open {
  stroke-dasharray: 5 4;
}

.dash-dot {
  fill: var(--primary);
}

.dash-dot--open {
  fill: var(--surface);
  stroke: var(--primary);
  stroke-width: 2;
}

.dash-axlabel {
  fill: var(--text-muted);
  font-size: 11px;
}

.dash-vlabel {
  fill: var(--text);
  font-size: 11px;
  font-weight: 600;
}

.dash-trk {
  fill: var(--bg);
}

.dash-s-p {
  fill: #2c5d8a;
}

.dash-s-l {
  fill: #8a6a10;
}

.dash-s-e {
  fill: #4a5563;
}

.dash-s-a {
  fill: #a11221;
}

.dash-s-x {
  fill: #14603a;
}

.dash-bar {
  height: 16px;
  border-radius: 3px;
}

/* ── 반 × 날짜 격자 ────────────────────────────────────────────────────── */
.dash-granbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  margin: 0.5rem 0;
}

.dash-granbar__label {
  font-size: 0.82rem;
  color: var(--text-muted);
  margin-right: 0.2rem;
}

/* 이 화면에서 가로로 미는 것이 허락된 **유일한** 자리. */
.dash-mx {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  max-width: 100%;
}

.dash-mx table {
  border-collapse: collapse;
  font-size: 0.78rem;
}

.dash-mx th,
.dash-mx td {
  padding: 2px;
  text-align: center;
  white-space: nowrap;
}

.dash-mx thead th {
  position: sticky;
  top: 0;
  background: var(--surface-alt);
  color: var(--text-muted);
  font-weight: 600;
  border-bottom: 1px solid var(--border);
}

.dash-mx__c1 {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--surface);
  text-align: left !important;
  min-width: 8rem;
  max-width: 12rem;
  white-space: normal !important;
  border-right: 1px solid var(--border);
}

.dash-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  width: 100%;
  min-width: 44px;
  min-height: 44px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 0.72rem;
  cursor: pointer;
}

.dash-cell u {
  text-decoration: none;
  font-weight: 700;
}

.dash-cell s {
  text-decoration: none;
  font-size: 0.64rem;
  color: var(--text-muted);
}

.dash-cell--ok {
  background: var(--success-bg);
  border-left: 4px solid var(--success);
}

.dash-cell--low {
  background: var(--surface);
  border: 2px dotted var(--danger);
}

.dash-cell--um {
  background: var(--surface);
  border: 2px dashed var(--gold);
}

.dash-cell--od {
  background: var(--danger-bg);
  border-left: 5px solid var(--danger);
}

.dash-cell--cx {
  background: var(--bg);
  color: var(--text-muted);
}

.dash-cell--sched {
  background: var(--info-bg);
  border-style: dotted;
}

.dash-cell--none,
.dash-cell--closed {
  border: 0;
  background: transparent;
  color: var(--text-muted);
  cursor: default;
  min-height: 44px;
}

.dash-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.9rem;
  margin: 0.6rem 0 0;
  font-size: 0.78rem;
  color: var(--text-muted);
}

.dash-legend span {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}

.dash-l {
  display: inline-block;
  width: 14px;
  height: 14px;
  border-radius: 3px;
  border: 1px solid var(--border);
}

.dash-l--od {
  background: var(--danger-bg);
  border-left: 4px solid var(--danger);
}

.dash-l--um {
  border: 2px dashed var(--gold);
}

.dash-l--low {
  border: 2px dotted var(--danger);
}

.dash-l--ok {
  background: var(--success-bg);
  border-left: 4px solid var(--success);
}

.dash-l--cx {
  background: var(--bg);
}

.dash-l--sched {
  background: var(--info-bg);
  border-style: dotted;
}

.dash-l--none {
  background: transparent;
}

/* ── 상태 구성 ─────────────────────────────────────────────────────────── */
.dash-comprow {
  display: grid;
  grid-template-columns: minmax(6rem, 12rem) 1fr minmax(8rem, 14rem);
  align-items: center;
  gap: 0.6rem;
  padding: 0.45rem 0;
  border-bottom: 1px solid var(--border);
  min-width: 0;
}

.dash-comprow__nm {
  font-weight: 600;
  overflow-wrap: anywhere;
  min-width: 0;
}

.dash-comprow__bar {
  min-width: 0;
}

.dash-comprow__fig {
  font-size: 0.78rem;
  color: var(--text-muted);
  text-align: right;
}

.dash-comprow__fig b {
  color: var(--text);
}

/* ── 학생 작은 배수 ────────────────────────────────────────────────────── */
.dash-smbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.5rem 0;
}

.dash-smbar input[type="search"] {
  flex: 1 1 12rem;
  min-width: 0;
  min-height: var(--control-min);
  padding: 0 0.6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font: inherit;
}

.dash-smgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
  gap: 0.6rem;
  margin: 0.6rem 0 0;
}

.dash-smcard {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-height: var(--control-min);
  padding: 0.6rem;
  border: 1px solid var(--border);
  border-left: 4px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
  min-width: 0;
}

.dash-smcard--bad {
  border-left-color: var(--danger);
}

.dash-smcard--warn {
  border-left-color: var(--gold);
}

.dash-smcard__hd {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  min-width: 0;
}

.dash-smcard__hd b {
  overflow-wrap: anywhere;
  min-width: 0;
}

.dash-smcard__fg {
  margin-left: auto;
  font-size: 0.8rem;
  color: var(--text-muted);
  white-space: nowrap;
}

.dash-smcard__sub {
  font-size: 0.75rem;
  color: var(--text-muted);
  overflow-wrap: anywhere;
}

/* ── 표(그림 밑에 같은 값) ─────────────────────────────────────────────── */
.dash-tbl {
  margin: 0.6rem 0 0;
}

.dash-tbl summary {
  min-height: var(--control-min);
  display: flex;
  align-items: center;
  cursor: pointer;
  font-size: 0.85rem;
  color: var(--accent);
  font-weight: 600;
}

.dash-scroller {
  overflow-x: auto;
  max-width: 100%;
}

.dash-scroller table {
  border-collapse: collapse;
  width: 100%;
  font-size: 0.82rem;
}

.dash-scroller th,
.dash-scroller td {
  padding: 0.35rem 0.5rem;
  border-bottom: 1px solid var(--border);
  text-align: left;
  white-space: nowrap;
}

.dash-num {
  text-align: right !important;
  font-variant-numeric: tabular-nums;
}

/* ── 서랍 ──────────────────────────────────────────────────────────────── */
.dash-scrim {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(22, 32, 43, 0.45);
}

.dash-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 61;
  width: min(28rem, 100vw);
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-left: 1px solid var(--border);
  box-shadow: -8px 0 24px rgba(22, 32, 43, 0.18);
}

.dash-drawer__h {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0.9rem;
  border-bottom: 1px solid var(--border);
}

.dash-drawer__h h2 {
  margin: 0;
  font-size: 1rem;
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}

.dash-drawer__b {
  overflow-y: auto;
  padding: 0.9rem;
}

.dash-dl {
  margin: 0;
}

.dash-dl > div {
  display: flex;
  gap: 0.6rem;
  padding: 0.3rem 0;
  border-bottom: 1px solid var(--border);
}

.dash-dl dt {
  flex: 0 0 6rem;
  color: var(--text-muted);
  font-size: 0.85rem;
}

.dash-dl dd {
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

.dash-lbl {
  margin: 0.9rem 0 0.3rem;
  font-weight: 600;
  font-size: 0.9rem;
}

.dash-golink {
  display: inline-flex;
  align-items: center;
  min-height: var(--control-min);
  margin: 0.8rem 0 0;
}

.dash-foot {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 1.25rem 0 0;
}

/* 오늘 자리의 세 덩어리는 세로 한 줄이다 — 좁은 폭에서 순서를 바꿀 수 있게 흐름을 만든다. */
#dash-panel-today {
  display: flex;
  flex-direction: column;
}

@media (max-width: 40rem) {
  /*
     [좁은 화면에서는 할 일이 먼저다] 이 화면 앞에 앉은 사람은 밀린 것을 비우는 사람이고, 390px
     에서 지표 여섯 장을 세로로 쌓으면 그 사람의 첫 실행 항목이 1,000px 아래로 내려간다(실측).
     넓은 화면에서는 지표가 한 줄이라 그런 일이 없으므로 순서를 바꾸지 않는다 — 좁은 폭에서만
     대기열이 위로 올라오고, 지표는 두 칸 격자로 접혀 바로 아래에 선다. 빠지는 사실은 없다. */
  #dash-queue {
    order: -1;
  }

  .dash-kpis {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.4rem;
  }

  .dash-kpi {
    padding: 0.45rem 0.55rem;
  }

  .dash-kpi__value {
    font-size: 1.25rem;
  }

  /* 좁은 폭에서 머리글의 설명과 기준 시각은 **작아지되 사라지지 않는다** — 정의와 as-of 는
     이 화면의 정직함 그 자체이므로 접거나 지우지 않고, 글자 크기만 줄여 첫 실행 항목을
     화면 안으로 올린다. */
  .dash-head .dash-note,
  .dash-asof {
    font-size: 0.76rem;
  }

  .dash-comprow {
    grid-template-columns: 1fr;
    gap: 0.25rem;
  }

  .dash-comprow__fig {
    text-align: left;
  }

  .dash-drawer {
    width: 100vw;
  }
}
/* ── DASHBOARD:END ────────────────────────────────────────────────────── */
