/* ================================================================
   学生请假系统 — Design System v2
   Color: warm paper + deep ink glass sidebar + teal/amber accents
   Type:  Space Grotesk (display) / Inter (body) / JetBrains Mono (data)
   Sig:   Aurora mesh gradient (登录页 + 侧边栏头部 — "深夜校园的窗")
   ================================================================ */

/* System fonts: no external request is required to render the interface. */

/* ===== Tokens ===== */
:root {
  --paper:   #f8f7f4;
  --surface: #ffffff;
  --ink:     #0c1226;
  --ink-2:   #161c33;
  --graphite:#595e72;
  --mist:    #9ca3af;
  --line:    #e9e7e2;
  --line-soft:#f0eee9;
  --teal:    #00897b;
  --teal-light:#e0f2f1;
  --teal-dim:#00695c;
  --amber:   #f59e0b;
  --amber-light:#fef3c7;
  --coral:   #ef4444;
  --coral-light:#fee2e2;
  --sky:     #3b82f6;
  --sky-light:#dbeafe;
  --radius:  14px;
  --radius-sm: 8px;
  --shadow-1: 0 1px 3px rgba(12,18,38,0.04), 0 1px 2px rgba(12,18,38,0.03);
  --shadow-2: 0 4px 24px rgba(12,18,38,0.06), 0 1px 4px rgba(12,18,38,0.03);
  --shadow-3: 0 12px 48px rgba(12,18,38,0.08), 0 4px 16px rgba(12,18,38,0.04);
  --sidebar-w: 246px;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --aurora-1: #00897b;
  --aurora-2: #f59e0b;
  --aurora-3: #3b82f6;
}

/* ===== Reset ===== */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  font-family: 'Inter', -apple-system, 'Microsoft YaHei', sans-serif;
  background: var(--paper);
  color: var(--graphite);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* ===== Typography ===== */
h1, h2, h3 {
  font-family: 'Space Grotesk', 'Inter', sans-serif;
  color: var(--ink);
  font-weight: 700;
  letter-spacing: -0.02em;
}
h1 { font-size: 28px; }
h2 { font-size: 20px; }
h3 { font-size: 16px; }

/* ===== Layout Shell ===== */
.layout {
  display: flex;
  min-height: 100vh;
}

/* ===== Aurora Keyframes ===== */
@keyframes aurora-shift {
  0%, 100% { transform: translate(0%, 0%) rotate(0deg) scale(1); }
  33%      { transform: translate(3%, -2%) rotate(8deg) scale(1.1); }
  66%      { transform: translate(-2%, 3%) rotate(-6deg) scale(0.95); }
}
@keyframes aurora-pulse {
  0%, 100% { opacity: 0.45; }
  50%      { opacity: 0.75; }
}

/* ===== Sidebar ===== */
.sidebar {
  width: var(--sidebar-w);
  background: var(--ink);
  color: #e2e8f0;
  display: flex;
  flex-direction: column;
  position: fixed;
  top: 0; left: 0;
  height: 100vh;
  z-index: 100;
  overflow: hidden;
  animation: sidebar-slide-in 0.6s var(--ease-out) both;
}
@keyframes sidebar-slide-in {
  from { transform: translateX(-100%); }
  to   { transform: translateX(0); }
}

/* aurora glow behind sidebar header */
.sidebar::before {
  content: '';
  position: absolute;
  top: -60%; left: -20%;
  width: 140%; height: 80%;
  background:
    radial-gradient(circle at 30% 50%, var(--aurora-1) 0%, transparent 50%),
    radial-gradient(circle at 70% 30%, var(--aurora-2) 0%, transparent 45%),
    radial-gradient(circle at 50% 70%, var(--aurora-3) 0%, transparent 40%);
  filter: blur(60px);
  opacity: 0.18;
  animation: aurora-shift 20s ease-in-out infinite;
  pointer-events: none;
}

.sidebar-header {
  padding: 22px 20px;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  position: relative;
  z-index: 1;
}
.sidebar-header h2 {
  font-size: 17px;
  font-weight: 700;
  color: #fff;
  letter-spacing: -0.01em;
  display: flex;
  align-items: center;
  gap: 8px;
}
.sidebar-header .aurora-dot {
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--amber);
  box-shadow: 0 0 12px var(--amber);
  animation: aurora-pulse 3s ease-in-out infinite;
}

.sidebar-nav {
  padding: 14px 12px;
  flex: 1;
  overflow-y: auto;
  position: relative;
  z-index: 1;
}
.sidebar-nav::-webkit-scrollbar { width: 4px; }
.sidebar-nav::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 4px; }

.nav-form { margin: 0; padding: 0; }
.inline-form { display: inline; margin: 0; padding: 0; }
.nav-button {
  width: 100%;
  background: transparent;
  border: 0;
  text-align: left;
  cursor: pointer;
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 14px;
  color: rgba(226,232,240,0.55);
  text-decoration: none;
  border-radius: var(--radius-sm);
  margin-bottom: 3px;
  font-size: 14px;
  font-weight: 500;
  transition: all 0.2s var(--ease);
  position: relative;
}
.nav-item::before {
  content: '';
  position: absolute;
  left: 0; top: 50%;
  transform: translateY(-50%) scaleY(0);
  width: 3px; height: 60%;
  background: var(--amber);
  border-radius: 0 3px 3px 0;
  transition: transform 0.3s var(--ease-out);
}
.nav-item:hover {
  background: rgba(255,255,255,0.05);
  color: #fff;
}
.nav-item:hover::before {
  transform: translateY(-50%) scaleY(1);
}
.nav-item.active {
  background: rgba(255,255,255,0.07);
  color: #fff;
}
.nav-item.active::before {
  transform: translateY(-50%) scaleY(1);
}

.sidebar-nav hr {
  border: none;
  border-top: 1px solid rgba(255,255,255,0.06);
  margin: 14px 0;
}

/* ===== Main Content ===== */
.main-content {
  margin-left: var(--sidebar-w);
  flex: 1;
  padding: 32px 40px;
  max-width: 100%;
  min-height: 100vh;
  animation: content-fade-up 0.5s var(--ease-out) 0.15s both;
}
@keyframes content-fade-up {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ===== Page Header ===== */
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 28px;
  gap: 16px;
}
.page-header h1 { margin: 0; }
.page-header > div { display: flex; gap: 8px; flex-wrap: wrap; }

.welcome {
  color: var(--graphite);
  margin-bottom: 24px;
  font-size: 15px;
}

/* ===== Alerts ===== */
.alert {
  padding: 14px 20px;
  border-radius: var(--radius-sm);
  margin-bottom: 20px;
  font-size: 14px;
  font-weight: 500;
  display: flex;
  align-items: center;
  gap: 8px;
  animation: alert-slide-in 0.4s var(--ease-out) both;
}
@keyframes alert-slide-in {
  from { opacity: 0; transform: translateX(-20px); }
  to   { opacity: 1; transform: translateX(0); }
}
.alert-success { background: var(--teal-light); color: var(--teal-dim); border: 1px solid #b2dfdb; }
.alert-danger  { background: var(--coral-light); color: #b91c1c; border: 1px solid #fecaca; }
.alert-warning { background: var(--amber-light); color: #92400e; border: 1px solid #fde68a; }
.alert-info    { background: var(--sky-light); color: #1e40af; border: 1px solid #bfdbfe; }

/* ===== 磁盘用量告警 ===== */
.disk-alert {
  padding: 14px 20px;
  border-radius: var(--radius-sm);
  margin-bottom: 20px;
  font-size: 14px;
  font-weight: 500;
  background: var(--teal-light);
  color: var(--teal-dim);
  border: 1px solid #b2dfdb;
  animation: alert-slide-in 0.4s var(--ease-out) both;
}
.disk-alert span { display: block; font-size: 13px; font-weight: 400; margin-top: 4px; }
.disk-alert.disk-warn { background: var(--amber-light); color: #92400e; border-color: #fde68a; }
.disk-alert.disk-over { background: var(--coral-light); color: #b91c1c; border-color: #fecaca; }

/* ===== Stats Grid ===== */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 20px;
  margin: 24px 0;
}
.stat-card {
  background: var(--surface);
  padding: 28px 24px;
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  text-align: center;
  transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease);
  position: relative;
  overflow: hidden;
  opacity: 0;
  animation: card-stagger-in 0.5s var(--ease-out) both;
}
.stat-card:nth-child(1) { animation-delay: 0.2s; }
.stat-card:nth-child(2) { animation-delay: 0.3s; }
.stat-card:nth-child(3) { animation-delay: 0.4s; }
.stat-card:nth-child(4) { animation-delay: 0.5s; }
@keyframes card-stagger-in {
  from { opacity: 0; transform: translateY(20px) scale(0.96); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.stat-card::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, transparent 60%, rgba(245,158,11,0.03) 100%);
  pointer-events: none;
}
.stat-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-3);
}
.stat-number {
  font-family: 'Space Grotesk', sans-serif;
  font-size: 40px;
  font-weight: 700;
  color: var(--teal);
  line-height: 1.1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
.stat-label {
  margin-top: 10px;
  color: var(--graphite);
  font-size: 13px;
  font-weight: 500;
}

/* ===== Quick Actions ===== */
.quick-actions { margin-top: 32px; }
.quick-actions h2 { margin-bottom: 18px; }
.action-buttons { display: flex; gap: 10px; flex-wrap: wrap; }

/* ===== Buttons ===== */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 10px 22px;
  border-radius: var(--radius-sm);
  text-decoration: none;
  font-size: 14px;
  font-weight: 600;
  border: none;
  cursor: pointer;
  transition: all 0.25s var(--ease);
  position: relative;
  overflow: hidden;
}
.btn::before {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(255,255,255,0.15);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.4s var(--ease-out);
  pointer-events: none;
}
.btn:hover::before { transform: scaleX(1); }

.btn-primary { background: var(--teal); color: #fff; box-shadow: 0 2px 8px rgba(0,137,123,0.25); }
.btn-primary:hover { background: var(--teal-dim); box-shadow: 0 4px 16px rgba(0,137,123,0.35); transform: translateY(-1px); }
.btn-success { background: var(--teal); color: #fff; }
.btn-success:hover { background: var(--teal-dim); transform: translateY(-1px); }
.btn-danger { background: var(--coral); color: #fff; }
.btn-danger:hover { background: #dc2626; transform: translateY(-1px); }
.btn-info { background: var(--sky); color: #fff; }
.btn-info:hover { background: #2563eb; transform: translateY(-1px); }
.btn-warning { background: var(--amber); color: #fff; }
.btn-warning:hover { background: #d97706; transform: translateY(-1px); }
.btn-cancel { background: var(--line-soft); color: var(--graphite); }
.btn-cancel:hover { background: var(--line); }

.btn-sm { padding: 6px 14px; font-size: 12px; border-radius: 6px; }
.btn-full { width: 100%; padding: 15px; font-size: 16px; }

/* ===== Tables ===== */
.table-container {
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  overflow: hidden;
  animation: card-stagger-in 0.5s var(--ease-out) 0.2s both;
}
table { width: 100%; border-collapse: collapse; }
th, td {
  padding: 14px 20px;
  text-align: left;
  font-size: 14px;
}
th {
  background: #fafaf8;
  font-family: 'Inter', sans-serif;
  font-weight: 600;
  color: var(--graphite);
  border-bottom: 2px solid var(--line);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
td {
  border-bottom: 1px solid var(--line-soft);
  color: var(--ink);
}
td:first-child { font-family: 'JetBrains Mono', monospace; font-size: 13px; }
tr { transition: background 0.15s var(--ease); }
tr:hover td { background: #fafcfb; }
tr:last-child td { border-bottom: none; }
.action-cell { display: flex; gap: 6px; white-space: nowrap; }

/* ===== Badges ===== */
.badge {
  display: inline-block;
  padding: 4px 14px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
.badge-pending  { background: var(--amber-light); color: #92400e; }
.badge-approved { background: var(--teal-light); color: var(--teal-dim); }
.badge-rejected { background: var(--coral-light); color: #b91c1c; }
.badge-returned { background: var(--sky-light); color: #1e40af; }
.badge-high     { background: var(--coral-light); color: #b91c1c; }
.badge-warning  { background: var(--amber-light); color: #92400e; }
.badge-danger   { background: var(--coral-light); color: #b91c1c; }

/* ===== Forms ===== */
.form-card {
  background: var(--surface);
  padding: 32px;
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  max-width: 680px;
  animation: card-stagger-in 0.5s var(--ease-out) 0.2s both;
}
.form-group { margin-bottom: 22px; }
.form-group label {
  display: block;
  margin-bottom: 8px;
  font-weight: 600;
  font-size: 14px;
  color: var(--ink);
}
.form-group input,
.form-group select,
.form-group textarea {
  width: 100%;
  padding: 11px 16px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-family: 'Inter', sans-serif;
  background: var(--surface);
  color: var(--ink);
  transition: border-color 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  border-color: var(--teal);
  box-shadow: 0 0 0 4px rgba(0,137,123,0.1);
  outline: none;
}
.form-group textarea { min-height: 110px; resize: vertical; }
.form-row { display: flex; gap: 20px; }
.form-row .form-group { flex: 1; }
.form-actions { display: flex; gap: 10px; margin-top: 16px; }

/* ===== Profile Card ===== */
.profile-card {
  background: var(--surface);
  padding: 32px;
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  max-width: 480px;
}

/* ===== Info Table ===== */
.info-table { width: auto; }
.info-table td { padding: 12px 32px 12px 0; border: none; }
.info-table td:first-child {
  font-family: 'Inter', sans-serif;
  font-weight: 600;
  color: var(--graphite);
  width: 100px;
  font-size: 14px;
}

/* ===== Detail Section ===== */
.detail-section {
  margin-bottom: 20px;
  padding: 20px;
  background: #fafaf8;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-soft);
}
.detail-section h3 {
  margin: 0 0 14px 0;
  font-size: 15px;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 6px;
}
.detail-section p {
  margin: 6px 0;
  font-size: 14px;
  color: var(--graphite);
}
.detail-section strong { color: var(--ink); font-weight: 600; }

/* ===== Utility ===== */
.empty-row {
  text-align: center;
  color: var(--mist);
  padding: 48px;
  font-family: 'Inter', sans-serif;
}
.notice-list {
  margin: 10px 0;
  padding-left: 22px;
  color: var(--graphite);
  line-height: 2;
  font-size: 14px;
}
.file-input {
  padding: 14px;
  border: 2px dashed var(--line);
  width: 100%;
  border-radius: var(--radius-sm);
  font-size: 14px;
  color: var(--graphite);
  transition: border-color 0.25s var(--ease);
  cursor: pointer;
}
.file-input:hover { border-color: var(--teal); }

/* ===== Login Page ===== */
.login-page {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  background: var(--ink);
  position: relative;
  overflow: hidden;
}
.login-page::before {
  content: '';
  position: absolute;
  top: -40%; left: -20%;
  width: 160%; height: 140%;
  background:
    radial-gradient(circle at 25% 40%, var(--aurora-1) 0%, transparent 45%),
    radial-gradient(circle at 75% 20%, var(--aurora-2) 0%, transparent 40%),
    radial-gradient(circle at 50% 80%, var(--aurora-3) 0%, transparent 45%),
    radial-gradient(circle at 85% 65%, var(--teal-dim) 0%, transparent 35%);
  filter: blur(80px);
  opacity: 0.35;
  animation: aurora-shift 18s ease-in-out infinite, aurora-pulse 8s ease-in-out infinite;
}
.login-page::after {
  content: '';
  position: absolute;
  top: -20%; right: -10%;
  width: 100%; height: 100%;
  background:
    radial-gradient(circle at 60% 50%, var(--amber) 0%, transparent 30%),
    radial-gradient(circle at 30% 30%, var(--teal) 0%, transparent 35%);
  filter: blur(120px);
  opacity: 0.12;
  animation: aurora-shift 25s ease-in-out infinite reverse;
}
.login-card {
  background: rgba(255,255,255,0.97);
  backdrop-filter: blur(20px);
  padding: 48px 40px;
  border-radius: 20px;
  box-shadow: 0 24px 80px rgba(0,0,0,0.25), 0 8px 32px rgba(0,0,0,0.1);
  width: 400px;
  position: relative;
  z-index: 1;
  animation: login-card-rise 0.7s var(--ease-out) 0.2s both;
}
@keyframes login-card-rise {
  from { opacity: 0; transform: translateY(40px) scale(0.95); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.login-card h1 {
  text-align: center;
  margin-bottom: 6px;
  font-size: 26px;
  color: var(--ink);
}
.login-subtitle {
  text-align: center;
  color: var(--graphite);
  margin-bottom: 32px;
  font-size: 14px;
}
.login-hint {
  text-align: center;
  color: var(--mist);
  margin-top: 24px;
  font-size: 12px;
  font-family: 'JetBrains Mono', monospace;
}
.login-form .form-group { margin-bottom: 20px; }
.login-form label {
  display: block;
  margin-bottom: 8px;
  font-weight: 600;
  font-size: 14px;
  color: var(--ink);
}
.login-form input {
  width: 100%;
  padding: 13px 16px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: 15px;
  font-family: 'JetBrains Mono', monospace;
  background: var(--paper);
  color: var(--ink);
  transition: border-color 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.login-form input:focus {
  border-color: var(--teal);
  box-shadow: 0 0 0 4px rgba(0,137,123,0.1);
  outline: none;
}

/* ===== Responsive ===== */
@media (max-width: 768px) {
  :root { --sidebar-w: 60px; }
  .sidebar-header h2 { font-size: 0; }
  .nav-item { font-size: 0; gap: 0; justify-content: center; padding: 14px 8px; }
  .nav-item::before { width: 3px; height: 70%; }
  .main-content { padding: 20px 16px; }
  .stats-grid { grid-template-columns: 1fr 1fr; }
  .form-row { flex-direction: column; gap: 0; }
  .page-header { flex-direction: column; align-items: flex-start; }
  .login-card { width: 92vw; padding: 36px 24px; }
}

@media (max-width: 480px) {
  .stats-grid { grid-template-columns: 1fr; }
}

/* ===== Reduced Motion ===== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .sidebar::before, .login-page::before, .login-page::after { display: none; }
}
/* ================================================================
   移动端增强（学生端手机网页）
   ================================================================ */
/* 请假审批页工具条（搜索 + 过滤） */
.leave-toolbar {
  display: flex; flex-wrap: wrap; gap: 16px; align-items: center; justify-content: space-between;
  margin-bottom: 16px; padding: 12px 16px; background: var(--surface); border: 1px solid var(--line);
  border-radius: 12px; box-shadow: var(--shadow-1);
}
.leave-search { display: flex; align-items: center; gap: 8px; flex: 1 1 260px; }
.leave-search input { flex: 1; min-height: 38px; padding: 8px 12px; border: 1px solid var(--line); border-radius: 8px; }
.leave-filter { display: flex; align-items: center; gap: 8px; }
.chip {
  display: inline-flex; align-items: center; padding: 6px 14px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--paper); color: var(--ink);
  text-decoration: none; font-size: 13px; font-weight: 600; transition: all 0.2s var(--ease);
}
.chip:hover { border-color: var(--teal); color: var(--teal); }
.chip-active { background: var(--teal); color: #fff; border-color: var(--teal); }

/* 学生主页：个人信息卡片 + 常驻动画（始终在动） */
.profile-card {
  position: relative; overflow: hidden;
  background: linear-gradient(135deg, var(--ink) 0%, #1e293b 100%);
  color: #fff; border-radius: 18px; padding: 24px; margin-bottom: 20px; box-shadow: var(--shadow-2);
}
.profile-card::after {
  content: ''; position: absolute; top: -50%; right: -20%; width: 60%; height: 200%;
  background: radial-gradient(circle, var(--amber) 0%, transparent 60%);
  opacity: 0.25; filter: blur(40px);
  animation: profile-glow 6s ease-in-out infinite;
  pointer-events: none;
}
@keyframes profile-glow {
  0%, 100% { transform: translateY(0) scale(1); opacity: 0.25; }
  50%      { transform: translateY(20px) scale(1.15); opacity: 0.45; }
}
.profile-card .pc-name { font-family: 'Space Grotesk', sans-serif; font-size: 26px; font-weight: 700; position: relative; z-index: 1; }
.profile-card .pc-pulse {
  display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: var(--teal);
  margin-right: 8px; box-shadow: 0 0 12px var(--teal); animation: pc-ping 1.6s ease-in-out infinite;
  position: relative; top: -3px;
}
@keyframes pc-ping {
  0%, 100% { transform: scale(1); opacity: 1; }
  50%      { transform: scale(1.5); opacity: 0.5; }
}
.profile-card .pc-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 14px; margin-top: 16px; position: relative; z-index: 1; }
.profile-card .pc-item .k { font-size: 12px; color: rgba(226,232,240,0.6); }
.profile-card .pc-item .v { font-size: 16px; font-weight: 600; margin-top: 2px; }
.profile-card .pc-shimmer {
  position: absolute; left: 0; bottom: 0; height: 3px; width: 30%;
  background: linear-gradient(90deg, transparent, var(--amber), transparent);
  animation: pc-shimmer 3s linear infinite;
}
@keyframes pc-shimmer {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(400%); }
}
/* ===== 请假提交：等待动画 / 成功过渡 / 文件预览 ===== */
.submit-overlay {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(12,18,38,0.45); backdrop-filter: blur(4px);
  display: none; align-items: center; justify-content: center;
}
.submit-overlay.is-active { display: flex; }
.submit-overlay__box {
  background: var(--surface); border-radius: 18px; padding: 32px 40px;
  box-shadow: var(--shadow-3); text-align: center; max-width: 90vw;
  animation: card-stagger-in 0.4s var(--ease-out) both;
}
.submit-spinner {
  width: 46px; height: 46px; margin: 0 auto 16px;
  border: 4px solid var(--teal-light); border-top-color: var(--teal);
  border-radius: 50%;
  animation: submit-spin 0.8s linear infinite;
}
@keyframes submit-spin { to { transform: rotate(360deg); } }
.submit-overlay__text { font-size: 17px; font-weight: 700; color: var(--ink); }
.submit-overlay__sub { margin-top: 6px; font-size: 13px; color: var(--mist); }

/* 成功过渡页 */
.submit-success { display: none; text-align: center; padding: 48px 20px; max-width: 460px; margin: 40px auto; }
.submit-success.is-active { display: block; animation: content-fade-up 0.5s var(--ease-out) both; }
.success-check { width: 80px; height: 80px; margin: 0 auto 18px; }
.success-check svg { width: 80px; height: 80px; }
.success-check circle {
  stroke: var(--teal); stroke-width: 3; fill: var(--teal-light);
  stroke-dasharray: 151; stroke-dashoffset: 151;
  animation: check-circle 0.6s var(--ease-out) forwards;
}
@keyframes check-circle { to { stroke-dashoffset: 0; } }
.success-check path {
  stroke: var(--teal-dim); stroke-width: 4; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 48; stroke-dashoffset: 48;
  animation: check-mark 0.4s var(--ease-out) 0.5s forwards;
}
@keyframes check-mark { to { stroke-dashoffset: 0; } }
.submit-success h2 { font-size: 24px; color: var(--ink); margin: 8px 0; }
.submit-success__sub { color: var(--mist); font-size: 14px; margin-bottom: 20px; }

/* 附件预览 */
.file-preview { margin-top: 10px; }
.file-preview img { max-width: 160px; max-height: 160px; border-radius: 10px; border: 1px solid var(--line); box-shadow: var(--shadow-1); }
.muted { color: var(--mist); font-size: 12px; }

/* 附件缩略图 */
.thumb-link img { width: 48px; height: 48px; object-fit: cover; border-radius: 6px; border: 1px solid var(--line); display: block; }
.thumb-lg { width: 100%; max-width: 320px; height: auto; border-radius: 8px; border: 1px solid var(--line); }

/* 销假提示框 */
.callout { background: var(--amber-light); border-left: 4px solid var(--amber); padding: 14px 16px; border-radius: 8px; margin: 16px 0; color: #92400e; }
.callout b { color: #78350f; }

/* 移动端顶栏（默认隐藏，<=768px 显示） */
.mobile-topbar { display: none; }
.sidebar-overlay { display: none; }

@media (max-width: 768px) {
  /* 顶栏 */
  .mobile-topbar {
    display: flex; align-items: center; gap: 12px;
    position: sticky; top: 0; z-index: 100;
    background: var(--surface); border-bottom: 1px solid var(--line);
    padding: 10px 16px; box-shadow: var(--shadow-1);
  }
  .mobile-menu-btn {
    border: 1px solid var(--line); background: var(--paper);
    width: 40px; height: 40px; border-radius: 10px; font-size: 18px; cursor: pointer;
    color: var(--ink);
  }
  .mobile-title { font-family: 'Space Grotesk', sans-serif; font-weight: 700; color: var(--ink); }

 /* 侧边栏改为抽屉 */
 :root { --sidebar-w: 260px; }
 .sidebar {
   position: fixed; top: 0; left: 0; bottom: 0; z-index: 200;
   transform: translateX(-100%); transition: transform 0.3s var(--ease-out);
   box-shadow: var(--shadow-3);
   animation: none;
 }
 .sidebar.open { transform: translateX(0); }
  .sidebar-header h2 { font-size: 18px; }
  .nav-item { font-size: 14px; }
 .main-content { margin-left: 0; padding: 16px; padding-top: 20px; }
 /* .mobile-topbar 是 sticky 且在文档流内占位，.main-content 不再额外补 padding，勿加回 */

 /* 遮罩 */
  .sidebar-overlay {
    display: block; position: fixed; inset: 0; z-index: 150;
    background: rgba(12,18,38,0.4); opacity: 0; pointer-events: none;
    transition: opacity 0.3s var(--ease-out);
  }
  .sidebar-overlay.show { opacity: 1; pointer-events: auto; }

  /* 响应式表格：行变卡片 */
  .rwd-table thead { display: none; }
  .rwd-table, .rwd-table tbody, .rwd-table tr, .rwd-table td { display: block; width: 100%; }
  .rwd-table tr {
    margin-bottom: 12px; border: 1px solid var(--line); border-radius: 12px;
    background: var(--surface); padding: 6px 12px; box-shadow: var(--shadow-1);
  }
  .rwd-table td { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 8px 4px; border: none; border-bottom: 1px solid var(--line-soft); }
  .rwd-table td:last-child { border-bottom: none; }
  .rwd-table td::before { content: attr(data-label); color: var(--mist); font-size: 12px; font-weight: 600; flex-shrink: 0; }
  .rwd-table td[data-label="原因"], .rwd-table td[data-label="时间"] { flex-direction: column; align-items: flex-start; }
  .rwd-table td[data-label="操作"] { flex-direction: row; justify-content: flex-end; }
  .rwd-table .action-cell { white-space: nowrap; }

  /* 表单与卡片 */
  .form-card { padding: 16px; }
  .form-row { flex-direction: column; gap: 0; }
  .form-actions { flex-direction: column-reverse; }
  .form-actions .btn { width: 100%; margin-top: 8px; }
  .quick-actions .action-buttons { flex-wrap: wrap; }
  .quick-actions .action-buttons .btn { flex: 1 1 calc(50% - 8px); }

  /* 文件选择按钮加大点击区 */
  input[type="file"] { width: 100%; min-height: 48px; padding: 10px; }
  h1 { font-size: 22px; }
}

@media (max-width: 480px) {
  .stats-grid { grid-template-columns: 1fr; }
  .page-header { flex-direction: column; align-items: stretch; gap: 10px; }
  .page-header .btn { width: 100%; }
}

/* ================================================================
   请假列表 — 每个学生一条独立卡片
   ================================================================ */
.leave-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
  gap: 16px;
  align-items: start;
}

.leave-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 12px;
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  padding: 18px 20px 14px;
  overflow: hidden;
  transition: box-shadow .22s var(--ease), transform .22s var(--ease), border-color .22s var(--ease);
  animation: card-stagger-in .45s var(--ease-out) both;
}
.leave-card::before {
  content: "";
  position: absolute;
  left: 0; top: 16px; bottom: 16px;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--line);
}
.leave-card--pending::before  { background: var(--amber); }
.leave-card--approved::before { background: var(--teal); }
.leave-card--rejected::before { background: var(--coral); }
.leave-card--returned::before { background: var(--sky); }

.leave-card:hover {
  box-shadow: var(--shadow-2);
  transform: translateY(-2px);
  border-color: var(--line);
}

/* 顶部：头像 + 姓名学号 + 状态 */
.leave-card-top { display: flex; align-items: center; gap: 12px; }
.leave-avatar {
  flex: 0 0 auto;
  width: 44px; height: 44px;
  border-radius: 50%;
  display: grid; place-items: center;
  font-family: 'Space Grotesk', 'Inter', sans-serif;
  font-size: 18px; font-weight: 700; color: #fff;
  background: linear-gradient(135deg, var(--teal) 0%, #0d9488 55%, var(--sky) 100%);
  box-shadow: 0 2px 8px rgba(0,137,123,.22);
  user-select: none;
}
.leave-ident { flex: 1 1 auto; min-width: 0; }
.leave-name {
  font-family: 'Space Grotesk', 'Inter', sans-serif;
  font-weight: 600; font-size: 15px; color: var(--ink); line-height: 1.3;
}
.leave-sub {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11.5px; color: var(--mist); margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* 字段两列 */
.leave-meta {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 8px 12px;
  margin: 0; font-size: 13px;
}
.leave-meta dt { color: var(--mist); font-size: 12px; white-space: nowrap; }
.leave-meta dd { color: var(--ink); margin: 0; line-height: 1.5; }
.leave-time { font-family: 'JetBrains Mono', monospace; font-size: 12.5px; }
.leave-arrow { color: var(--mist); margin: 0 4px; }
.leave-dur { display: block; color: var(--mist); font-size: 11.5px; margin-top: 2px; }

/* 事由 / 审批意见 */
.leave-reason {
  margin: 0; padding: 10px 12px;
  background: var(--paper);
  border-radius: var(--radius-sm);
  font-size: 13px; color: var(--graphite); line-height: 1.55;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.leave-comment { margin: 0; font-size: 12.5px; color: var(--graphite); line-height: 1.55; }
.leave-comment-label {
  display: inline-block; margin-right: 6px; padding: 1px 7px;
  border-radius: 10px; background: var(--teal-light); color: var(--teal-dim);
  font-size: 11px; font-weight: 600;
}

/* 附件缩略图 */
.leave-thumbs { display: flex; gap: 10px; }
.leave-thumb {
  position: relative; display: block;
  border-radius: 10px; overflow: hidden;
  border: 1px solid var(--line); line-height: 0;
}
.leave-thumb img {
  display: block; width: 64px; height: 64px; object-fit: cover;
  transition: transform .25s var(--ease);
}
.leave-thumb:hover img { transform: scale(1.06); }
.leave-thumb-tag {
  position: absolute; left: 0; right: 0; bottom: 0;
  font-size: 10px; line-height: 1.6; text-align: center; color: #fff;
  background: rgba(12,18,38,.55);
}

/* 底部：提交时间 + 操作 */
.leave-card-foot {
  margin-top: auto;
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding-top: 12px;
  border-top: 1px dashed var(--line);
}
.leave-created {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px; color: var(--mist); white-space: nowrap;
}
.leave-actions { display: flex; gap: 8px; }

.leave-empty {
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  padding: 48px 20px;
  text-align: center;
  color: var(--mist);
  font-size: 14px;
}

@media (max-width: 900px) {
  .leave-cards { grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); gap: 12px; }
}
@media (max-width: 768px) {
  .leave-cards { grid-template-columns: 1fr; }
  .leave-card { padding: 16px; gap: 10px; }
  .leave-avatar { width: 40px; height: 40px; font-size: 16px; }
  .leave-thumb img { width: 58px; height: 58px; }
}

/* ================================================================
   请假证件照 + 请假详情页（仅追加，不改动上方任何既有选择器）
   ================================================================ */

/* ---------- 列表卡片：学生证件照（295:413 ≈ 5:7） ---------- */
.leave-portrait {
  position: relative;
  flex: 0 0 auto;
  width: 56px;
  height: 78px;
  aspect-ratio: 295 / 413;
  border-radius: 10px;
  overflow: hidden;
  display: grid;
  place-items: center;
  background: linear-gradient(135deg, var(--teal) 0%, #0d9488 55%, var(--sky) 100%);
  box-shadow: 0 2px 8px rgba(0, 137, 123, .22);
  user-select: none;
}
.leave-portrait-fb {
  font-family: 'Space Grotesk', 'Inter', sans-serif;
  font-size: 20px;
  font-weight: 700;
  line-height: 1;
  color: #fff;
}
.leave-portrait-img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---------- 列表卡片：证明照/销假照的加载失败占位 ---------- */
.leave-thumbs .leave-thumb-fallback {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: grid;
  place-items: center;
  background: #f1f0ed;
  font-size: 0;
}
.leave-thumbs .leave-thumb img {
  position: relative;
  z-index: 1;
}
.leave-thumbs .leave-thumb-tag {
  z-index: 2;
}

/* ---------- 详情页：页头 ---------- */
.leave-detail-header {
  margin-bottom: 18px;
}
.leave-detail-title {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
.leave-detail-title h1 {
  margin: 0;
  font-size: 24px;
}
.leave-detail-back {
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
  font-size: 18px;
  line-height: 1;
  display: grid;
  place-items: center;
  box-shadow: var(--shadow-1);
  transition: border-color .2s var(--ease), color .2s var(--ease);
}
.leave-detail-back:hover {
  border-color: var(--teal);
  color: var(--teal);
}

/* ---------- 详情页：大卡容器 ---------- */
.leave-detail-card {
  max-width: 960px;
  padding: 22px 24px 20px;
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  overflow-wrap: anywhere;
}

/* ---------- 详情页：学生信息区（证件照 / 学号姓名班级 / 证明照） ---------- */
.leave-detail-student {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) minmax(150px, 210px);
  gap: 18px 22px;
  align-items: center;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--line-soft);
}
.leave-detail-portrait {
  position: relative;
  width: 96px;
  height: 134px;
  aspect-ratio: 295 / 413;
  flex: 0 0 auto;
  border-radius: 10px;
  overflow: hidden;
  display: grid;
  place-items: center;
  background: linear-gradient(135deg, var(--teal) 0%, #0d9488 55%, var(--sky) 100%);
  box-shadow: var(--shadow-1);
  user-select: none;
}
.leave-detail-portrait-fb {
  font-family: 'Space Grotesk', 'Inter', sans-serif;
  font-size: 30px;
  font-weight: 700;
  line-height: 1;
  color: #fff;
}
.leave-detail-portrait img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.leave-detail-ident {
  display: grid;
  gap: 8px;
  margin: 0;
  min-width: 0;
}
.leave-detail-ident-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
}
.leave-detail-ident dt {
  flex: 0 0 40px;
  color: var(--graphite);
  font-size: 12px;
}
.leave-detail-ident dd {
  margin: 0;
  min-width: 0;
  color: var(--ink);
  font-size: 14px;
  font-weight: 600;
  overflow-wrap: anywhere;
}
.leave-detail-ident-name {
  font-family: 'Space Grotesk', 'Inter', sans-serif;
  font-size: 18px;
}
.leave-detail-proof {
  position: relative;
  justify-self: end;
  width: 100%;
  max-width: 200px;
}
.leave-detail-proof-link {
  position: relative;
  display: block;
  aspect-ratio: 3 / 4;
  max-height: 260px;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--line);
  background: #f3f4f6;
  line-height: 0;
}
.leave-detail-proof-fallback {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: grid;
  place-items: center;
  background: #f1f0ed;
  color: var(--mist);
  font-size: 12px;
  line-height: 1.4;
}
.leave-detail-proof-link img {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #f3f4f6;
}
.leave-detail-proof-link:hover {
  border-color: var(--teal);
}
.leave-detail-proof-hint {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  padding: 4px 0;
  text-align: center;
  font-size: 11px;
  line-height: 1.6;
  color: #fff;
  background: rgba(12, 18, 38, .55);
}
.leave-detail-proof-empty {
  display: grid;
  place-items: center;
  width: 100%;
  aspect-ratio: 3 / 4;
  max-height: 260px;
  border: 1px dashed var(--line);
  border-radius: 10px;
  background: #f3f4f6;
  color: var(--graphite);
  font-size: 13px;
}

/* ---------- 详情页：分区标题 + 两列字段 ---------- */
.leave-detail-section-title {
  margin: 20px 0 12px;
  font-family: 'Space Grotesk', 'Inter', sans-serif;
  font-size: 15px;
  font-weight: 700;
  color: var(--ink);
}
.leave-detail-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  margin: 0;
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
  border-radius: 10px;
  overflow: hidden;
}
.leave-detail-field {
  display: flex;
  gap: 12px;
  min-width: 0;
  padding: 11px 14px;
  background: var(--surface);
}
.leave-detail-field dt {
  flex: 0 0 72px;
  padding-top: 2px;
  color: var(--graphite);
  font-size: 12px;
}
.leave-detail-field dd {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  color: var(--ink);
  font-size: 13.5px;
  overflow-wrap: anywhere;
}
.leave-detail-field--full {
  grid-column: 1 / -1;
}
.leave-detail-mono {
  font-family: 'JetBrains Mono', monospace;
  font-size: 12.5px;
  white-space: nowrap;
}
.leave-detail-photo-link {
  color: var(--teal);
  font-weight: 600;
  text-decoration: none;
}
.leave-detail-photo-link:hover {
  text-decoration: underline;
}
.leave-detail-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
.leave-detail-pill::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}
.leave-detail-pill--done {
  background: var(--teal-light);
  color: var(--teal-dim);
}
.leave-detail-pill--todo {
  background: var(--amber-light);
  color: #92400e;
}

/* ---------- 详情页：审核流程时间轴（纯 CSS 圆徽标 + 连线） ---------- */
.leave-detail-timeline {
  position: relative;
  margin: 0;
  padding: 4px 0 0 30px;
  list-style: none;
}
.leave-detail-timeline::before {
  content: "";
  position: absolute;
  left: 9px;
  top: 14px;
  bottom: 14px;
  width: 2px;
  background: var(--line);
}
.leave-detail-node {
  position: relative;
  padding: 0 0 22px 0;
}
.leave-detail-node:last-child {
  padding-bottom: 0;
}
.leave-detail-node::before {
  content: attr(data-mark);
  position: absolute;
  left: -30px;
  top: 0;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  color: #fff;
  background: var(--mist);
  box-shadow: 0 0 0 4px var(--surface);
}
.leave-detail-node--apply::before {
  background: var(--teal);
}
.leave-detail-node--review::before {
  background: var(--sky);
}
.leave-detail-node--waiting::before {
  background: var(--sky);
  color: #fff;
}
.leave-detail-node--return::before {
  background: var(--amber);
}
.leave-detail-node-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.leave-detail-node-role {
  color: var(--ink);
  font-size: 13px;
  font-weight: 600;
}
.leave-detail-node-time {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11.5px;
  color: var(--graphite);
}
.leave-detail-node-body {
  margin: 4px 0 0;
  color: var(--graphite);
  font-size: 13px;
  line-height: 1.55;
  overflow-wrap: anywhere;
}
.leave-detail-node-status {
  font-size: 12px;
  font-weight: 600;
}
.leave-detail-node-status--ok {
  color: var(--teal-dim);
}
.leave-detail-node-status--no {
  color: #b91c1c;
}
.leave-detail-node-status--wait {
  color: #92400e;
}

/* ---------- 详情页：底部操作 ---------- */
.leave-detail-actions {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px dashed var(--line);
}
.leave-detail-actions .form-group {
  margin-bottom: 14px;
}
.leave-detail-actions .form-actions {
  margin-top: 0;
  flex-wrap: wrap;
}
.leave-detail-return {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px dashed var(--line);
}

/* ---------- 详情页：原生 :target 放大层（无 JS） ---------- */
.leave-detail-lightbox {
  display: none;
}
.leave-detail-lightbox:target {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(12, 18, 38, .82);
}
.leave-detail-lightbox-backdrop {
  position: absolute;
  inset: 0;
}
.leave-detail-lightbox img {
  position: relative;
  z-index: 1;
  width: auto;
  height: auto;
  max-width: min(92vw, 720px);
  max-height: 86vh;
  object-fit: contain;
  border-radius: 8px;
  background: #fff;
  box-shadow: var(--shadow-3);
}
.leave-detail-lightbox-close {
  position: absolute;
  top: 14px;
  right: 16px;
  z-index: 2;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #fff;
  background: rgba(255, 255, 255, .15);
  font-size: 24px;
  line-height: 1;
  text-decoration: none;
}
.leave-detail-lightbox-close:hover {
  background: rgba(255, 255, 255, .3);
}

/* ---------- 响应式 ---------- */
@media (max-width: 900px) {
  .leave-detail-student {
    grid-template-columns: auto minmax(0, 1fr);
  }
  .leave-detail-proof {
    grid-column: 1 / -1;
    justify-self: start;
    max-width: 220px;
  }
}
@media (max-width: 768px) {
  .leave-portrait {
    width: 48px;
    height: 67px;
  }
  .leave-portrait-fb {
    font-size: 17px;
  }
  .leave-detail-card {
    padding: 16px;
  }
  .leave-detail-title h1 {
    font-size: 21px;
  }
  .leave-detail-fields {
    grid-template-columns: 1fr;
  }
  .leave-detail-field dt {
    flex-basis: 64px;
  }
  .leave-detail-timeline {
    padding-left: 26px;
  }
  .leave-detail-node::before {
    left: -26px;
  }
  .leave-detail-timeline::before {
    left: 9px;
  }
}
@media (max-width: 400px) {
  .leave-detail-portrait {
    width: 84px;
    height: 118px;
  }
  .leave-detail-student {
    gap: 14px;
  }
  .leave-detail-proof {
    max-width: 100%;
  }
  .leave-detail-field {
    flex-direction: column;
    gap: 2px;
  }
  .leave-detail-field dt {
    flex-basis: auto;
  }
}

/* ≤480px：时间区间改为“起始一行 / → 结束一行”，避免箭头孤立行尾 */
@media (max-width: 480px) {
  .leave-meta dd .leave-time { display: inline; }
  .leave-meta dd .leave-time:first-of-type { display: block; }
  .leave-meta dd .leave-arrow { display: inline; margin: 0 6px; }
  .leave-meta dd .leave-dur { display: block; margin-top: 3px; }
}

/* ============================================================
   第三轮追加：列表卡片「查看详情」入口（只读详情页 /leave/detail/<id>）
   只新增选择器，不改动上方任何已有规则。
   ============================================================ */
.leave-foot-links {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 8px;
  margin-left: auto;
}

.leave-detail-link {
  font-size: 12px;
  font-weight: 600;
  color: var(--teal);
  text-decoration: none;
  white-space: nowrap;
}

.leave-detail-link:hover {
  color: var(--teal-dim);
  text-decoration: underline;
}


/* ============================================================
   第四轮追加：审查清单修复（P0/P1）
   只新增选择器，不改动上方任何既有规则；需要覆盖旧规则时用更高特异性。
   ============================================================ */

/* 10) .page-header > div (0,1,1) 压住了 .leave-detail-title (0,1,0)，这里用 (0,2,0) 修正 gap */
.page-header > .leave-detail-title {
  gap: 12px;
}

/* 1) 详情页「销假自拍」缩略图：复用 .leave-detail-proof-link 结构，只收窄尺寸 */
.leave-detail-proof-link--thumb {
  width: 104px;
  max-width: 104px;
  max-height: 140px;
  cursor: zoom-in;
}
.leave-detail-proof-link--thumb .leave-detail-proof-hint {
  font-size: 10px;
  padding: 3px 0;
}

/* 13) 未知 status 的中性徽标兜底（不覆盖已有的 badge-pending/approved/rejected/returned） */
.badge:not(.badge-pending):not(.badge-approved):not(.badge-rejected):not(.badge-returned) {
  background: #eef0f3;
  color: var(--graphite);
}

/* 11) 769–900px 两列字段过窄会让时间戳中间断行：该区间提前收为单列 */
@media (max-width: 900px) {
  .leave-detail-fields {
    grid-template-columns: 1fr;
  }
}

/* ============================================================
   第 8 轮追加：无证明照时不渲染右上大图区；销假自拍收为小缩略图
   只新增选择器，不改动上方规则。
   ============================================================ */

/* 1) 没有 attachment 时右上大图区整体不渲染，左列证件照改顶部对齐 */
.leave-detail-student--no-proof .leave-detail-portrait {
  align-self: start;
}

/* 2) 字段区「销假自拍」小缩略图：104×139（3:4），维持可点放大 */
.leave-detail-field dd > .leave-detail-proof-link--thumb {
  width: 104px;
  height: 139px;
  max-width: 104px;
  max-height: none;
  flex: 0 0 auto;
}
.leave-detail-field dd > .leave-detail-proof-link--thumb img {
  object-fit: cover;
}


/* ============================================================
   第 9 轮追加：销假缩略图放大提示、移动端时间区间换行
   ============================================================ */

/* 8) ≤480px：箭头与结束时间绑定为一行，不再让 → 单独成行 */
@media (max-width: 480px) {
  .leave-meta dd .leave-range-end {
    display: block;
    white-space: nowrap;
  }
  .leave-meta dd .leave-range-end .leave-time {
    display: inline;
  }
  /* 箭头不再右缩进，与上一行起始时间左对齐 */
  .leave-meta dd .leave-range-end .leave-arrow {
    margin-left: 0;
  }
}

/* 销假登记页：起止时间整体不拆行，只在「~」处换行 */
.leave-return-time {
  white-space: nowrap;
}


/* ============================================================
   第 10 轮追加：「学生管理」列表页卡片网格 + 搜索/班级筛选
   来源：/home/.tmp/mock/student_cards_mock.html 的 <style> 第 9-249 行
   （已剔除评审稿专用的 .mock-* 规则）
   ============================================================ */

/* ---------- 工具条：搜索 + 班级 chips ---------- */
.student-toolbar {
  display: flex; flex-wrap: wrap; gap: 14px 16px;
  align-items: center; justify-content: space-between;
  padding: 12px 16px; margin-bottom: 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: var(--shadow-1);
}
.student-search { display: flex; align-items: center; gap: 8px; flex: 1 1 300px; min-width: 0; }
.student-search input {
  flex: 1 1 auto; min-width: 0; min-height: 38px;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
  color: var(--ink);
  font-size: 13.5px;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease), background .2s var(--ease);
}
.student-search input::placeholder { color: var(--mist); }
.student-search input:focus {
  outline: none; background: var(--surface);
  border-color: var(--teal);
  box-shadow: 0 0 0 3px rgba(0,137,123,.12);
}
.student-filters { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.student-filters .filter-label { font-size: 12px; color: var(--mist); }

/* 结果计数：工具条下一行小字 */
.student-count {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
  margin: 0 2px 16px;
  font-size: 12.5px; color: var(--graphite);
}
.student-count strong { color: var(--ink); font-weight: 600; font-size: 13px; }
.student-count .count-hint { color: var(--mist); font-size: 12px; }

/* ---------- 卡片网格 ---------- */
.student-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 16px;
  align-items: start;
}

.student-card {
  position: relative;
  display: flex; flex-direction: column;
  gap: 12px;
  padding: 16px 16px 14px;
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  /* 仅用于圆角裁剪卡片内的子元素（证件照/色条），不是用来掩盖溢出的。
     注意：它会让页面级 scrollWidth 恒等于 clientWidth，响应式破版检测必须
     直接量元素 rect（见 F1 回归脚本），不能只看页面横向滚动条。 */
  overflow: hidden;
  transition: box-shadow .22s var(--ease), transform .22s var(--ease), border-color .22s var(--ease);
  animation: card-stagger-in .45s var(--ease-out) both;
}
/* 左侧 3px 性别色条，和请假卡片的左侧色条同一套语言 */
.student-card::before {
  content: ""; position: absolute;
  left: 0; top: 16px; bottom: 16px; width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--line);
}
.student-card--female::before { background: var(--teal); }
.student-card--male::before   { background: var(--sky); }
.student-card:hover {
  box-shadow: var(--shadow-2);
  transform: translateY(-2px);
  border-color: var(--line);
}

/* 顶部：证件照 + 姓名/学号/班级 */
.student-card-head { display: flex; align-items: flex-start; gap: 14px; }

/* 证件照 88×123（≈5:7），沿用 .leave-portrait 的圆角 + 阴影语言 */
.student-portrait {
  width: 88px; height: 123px;
  border-radius: 10px;
  box-shadow: 0 2px 10px rgba(12,18,38,.10);
  border: 1px solid rgba(12,18,38,.05);
}
/* 无照片：灰底首字占位（避免破图） */
.student-portrait--fb {
  background: linear-gradient(150deg, #f3f3f0 0%, #e7e7e3 100%);
  box-shadow: inset 0 0 0 1px rgba(12,18,38,.03);
}
.student-portrait .leave-portrait-fb { font-size: 34px; color: #82878f; letter-spacing: 0; }
.student-portrait--fb .leave-portrait-fb { text-shadow: 0 1px 0 rgba(255,255,255,.8); }
.student-portrait .leave-portrait-img { border-radius: 9px; }

/* 右列与证件照同高：姓名/学号靠上，班级 chip 贴底，避免右侧出现大片空白 */
.student-ident {
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-direction: column; align-items: flex-start;
  justify-content: center;
  min-height: 123px;
  padding: 2px 0;
}
.student-name-row { display: flex; align-items: center; gap: 8px; min-width: 0; width: 100%; }
.student-name {
  font-family: 'Space Grotesk', 'Inter', sans-serif;
  font-size: 18px; font-weight: 700; line-height: 1.25;
  color: var(--ink); text-decoration: none;
  letter-spacing: -0.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.student-name:hover { color: var(--teal); }
.gender-pill {
  flex: 0 0 auto; margin-left: auto;
  display: inline-flex; align-items: center;
  padding: 1px 8px; border-radius: 999px;
  font-size: 11px; font-weight: 600; line-height: 1.7;
}
.gender-pill--f { background: var(--teal-light); color: var(--teal-dim); }
.gender-pill--m { background: var(--sky-light); color: #1e40af; }

.student-no {
  display: inline-block; margin-top: 4px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 12.5px; color: var(--graphite);
  text-decoration: none; letter-spacing: .01em;
}
.student-no:hover { color: var(--teal); text-decoration: underline; }

.student-class {
  display: inline-flex; align-items: center; gap: 4px;
  max-width: 100%; margin-top: 8px;
  padding: 2px 9px 2px 7px;
  border-radius: 999px;
  background: var(--teal-light);
  color: var(--teal-dim);
  font-size: 11.5px; font-weight: 600;
  text-decoration: none;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: background .2s var(--ease);
}
.student-class:hover { background: #c9e9e5; }

/* 两列小字段：宿舍 / 电话 */
.student-meta {
  display: grid;
  grid-template-columns: auto 1fr auto 1fr;
  gap: 4px 8px;
  margin: 0;
  padding-top: 11px;
  border-top: 1px solid var(--line-soft);
  font-size: 13px;
  align-items: baseline;
}
.student-meta dt { color: var(--mist); font-size: 12px; white-space: nowrap; }
.student-meta dd {
  margin: 0; color: var(--ink); font-size: 13px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  min-width: 0;
}
.student-meta dd.mono { font-family: 'JetBrains Mono', monospace; font-size: 12.5px; }

/* 底部操作行 */
.student-actions {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-top: auto; padding-top: 12px;
  border-top: 1px dashed var(--line);
}
.student-actions .btn { flex: 0 0 auto; line-height: 1.5; }
.student-actions form { display: inline; margin: 0; padding: 0; }

/* 空状态 */
.student-empty {
  background: var(--paper);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  box-shadow: none;
  padding: 44px 20px;
  text-align: center;
  color: var(--graphite);
}
.student-empty-icon {
  display: grid; place-items: center;
  width: 54px; height: 54px; margin: 0 auto 14px;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--line);
  font-size: 24px; line-height: 1;
}
.student-empty-title { font-size: 15px; font-weight: 600; color: var(--ink); margin: 0 0 6px; }
.student-empty-sub { font-size: 12.5px; color: var(--mist); margin: 0 0 16px; }
.student-empty-link {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 18px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--teal); font-size: 13px; font-weight: 600;
  text-decoration: none;
}
.student-empty-link:hover { border-color: var(--teal); background: var(--teal-light); }

/* ---------- 响应式 ---------- */
@media (max-width: 900px) {
  .student-cards { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
}
@media (max-width: 768px) {
  .student-cards { grid-template-columns: 1fr; }
  .student-toolbar { padding: 12px; gap: 12px; }
  .student-search { flex: 1 1 100%; }
  .student-filters { flex: 1 1 100%; }
  .student-count { margin-bottom: 14px; }
  .student-count .count-hint { display: none; }
}
@media (max-width: 420px) {
  .student-meta { grid-template-columns: 46px 1fr; gap: 6px 8px; }
  .student-portrait { width: 78px; height: 109px; }
  .student-ident { min-height: 109px; }
  .student-name { font-size: 17px; }
  /* 手机端：2×2 排布，把点击目标从 ~52px 宽放大到 ~159px，并抬到 36px 高 */
  .student-actions .btn { flex: 1 1 calc(50% - 3px); justify-content: center; min-height: 36px; }
}

/* ---------- 本轮新增工具类（供搜索页 JS 使用） ---------- */
.is-hidden { display: none !important; }

/* 搜索关键词高亮：只加底色，不加 padding，避免文本抖动 */
mark.student-hit {
  background: #fff3b0;
  color: inherit;
  padding: 0;
  border-radius: 2px;
  box-shadow: 0 1px 0 rgba(12,18,38,.06);
}

/* 筛选失败提示（aria-live 区域） */
.student-error {
  flex: 1 1 100%;
  color: #b45309;
  font-size: 12.5px;
}

/* ---------- 420px 操作按钮 2×2 补齐 ----------
   视觉稿的 2×2 规则只作用于直接子按钮；本页模板把「重置密码/删除」包在
   <form class="inline-form"> 里（保留原有 POST + CSRF 语义），form 成为 flex item
   后按钮不再等宽。这里仅追加规则让 form 也参与 2×2，不改动任何既有选择器。 */
@media (max-width: 420px) {
  .student-actions form {
    flex: 1 1 calc(50% - 3px);
    display: flex;
    margin: 0;
    padding: 0;
  }
  .student-actions form .btn {
    flex: 1 1 100%;
    justify-content: center;
    min-height: 36px;
  }
}

/* ---------- F10：320px 小屏网格下限 ----------
   基础规则是 minmax(300px, 1fr)，320px 视口下内容宽 288px < 300px，
   会把单列硬撑到 300px 造成横向溢出。这里在小屏把下限放宽到 0。 */
@media (max-width: 360px) {
  .student-cards { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- 截断提示（配合后端 truncated 契约 M3） ---------- */
.student-truncated {
  flex: 1 1 100%;
  color: #b45309;
  font-size: 12.5px;
}

/* 选中 chip 除了 class 还带 aria-current，这里不改视觉，仅保证键盘焦点可见 */
.student-filters .chip:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 2px;
}

/* ================================================================
   证件照定位修复（本轮）
   根因：.student-portrait 只声明了尺寸/圆角/阴影，没有 position:relative，
   于是内部 .leave-portrait-img（position:absolute; inset:0）的包含块
   不是证件照框，而是最近的已定位祖先 .student-card（position:relative），
   照片被拉伸到整张卡片的 padding box 上并盖住姓名/学号。
   同时 .student-portrait 也没有 display:grid; place-items:center
   （那是 .leave-portrait 的规则，而卡片用的是 .student-portrait），
   无照片时的首字占位实际贴在左上角，并未居中。
   下面只做追加覆盖，不改动 1741-1746 的既有声明，避免影响其它页面。
   ================================================================ */
.student-portrait {
  position: relative;
  flex: 0 0 auto;
  overflow: hidden;
  display: grid;
  place-items: center;
}
/* 绝对定位的 <img> 在 grid 容器里不再参与排布，圆角由容器的 overflow 负责裁剪 */

/* ---------- 学生详情页：证件照 + 姓名/学号/班级 ---------- */
.student-detail-head {
  display: flex;
  align-items: center;
  gap: 20px;
  margin-bottom: 20px;
}
.student-detail-portrait {
  position: relative;
  flex: 0 0 auto;
  width: 96px;
  height: 134px;
  border-radius: 12px;
  overflow: hidden;
  display: grid;
  place-items: center;
  box-shadow: 0 2px 10px rgba(12,18,38,.10);
  border: 1px solid rgba(12,18,38,.05);
}
.student-detail-portrait--fb { background: linear-gradient(150deg,#f3f3f0 0%,#e7e7e3 100%); }
.student-detail-initial {
  font-family: 'Space Grotesk','Inter',sans-serif;
  font-size: 36px;
  font-weight: 700;
  color: #82878f;
  line-height: 1;
}
.student-detail-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.student-detail-meta { flex: 1 1 auto; min-width: 0; }
.student-detail-meta h1 { margin: 0 0 6px; overflow-wrap: anywhere; }
.student-detail-sub {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 13px;
  color: var(--graphite);
}
.student-detail-class {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--teal-light);
  color: var(--teal-dim);
  font-size: 12px;
  font-weight: 600;
}
@media (max-width: 480px) {
  .student-detail-head { gap: 14px; }
  .student-detail-portrait { width: 76px; height: 106px; }
  .student-detail-initial { font-size: 28px; }
  .student-detail-meta h1 { font-size: 22px; }
}

/* ================================================================
   个人信息页「我的档案」+ 仪表盘个人卡证件照（本轮）
   -----------------------------------------------------------------
   与 student-detail-* 同源（96×134 / 295:413），但使用独立的
   .profile-photo-* / .pc-portrait* 命名，避免与列表页 .leave-portrait*
   或详情页 .student-detail-* 的既有规则互相覆盖。
   关键约束：容器必须自带 position:relative + overflow:hidden +
   display:grid + place-items:center + flex:0 0 auto，内部绝对定位的
   <img> 才以容器为包含块；否则会逃逸到最近的已定位祖先
   （.profile-card 是 position:relative）并铺满整张卡片。
   ================================================================ */

/* ---------- 个人信息页：证件照 + 宿舍号 + 只读档案 ---------- */
.profile-photo-card { max-width: 680px; }
.profile-photo-locktip { margin-top: 0; }

.profile-photo-row {
  display: flex;
  align-items: flex-start;
  gap: 20px;
  flex-wrap: wrap;
}

.profile-photo-frame {
  position: relative;
  overflow: hidden;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 96px;
  height: 134px;
  aspect-ratio: 295 / 413;
  border-radius: 12px;
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: 0 2px 10px rgba(12,18,38,.10);
}
.profile-photo-frame--fb {
  background: linear-gradient(150deg,#f3f3f0 0%,#e7e7e3 100%);
}
.profile-photo-initial {
  font-family: 'Space Grotesk','Inter',sans-serif;
  font-size: 36px;
  font-weight: 700;
  line-height: 1;
  color: #82878f;
}
.profile-photo-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.profile-photo-actions {
  flex: 1 1 220px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}
.profile-photo-form {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  width: 100%;
}
.profile-photo-file {
  width: 100%;
  max-width: 320px;
  font-size: 13px;
  color: var(--graphite);
}
.profile-photo-delete { display: inline-flex; }
.profile-photo-hint { margin: 2px 0 0; line-height: 1.6; }

.profile-dorm-form {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  flex-wrap: wrap;
}
.profile-dorm-form .form-group { margin-bottom: 0; flex: 1 1 200px; min-width: 0; }
.profile-dorm-form .btn { flex: 0 0 auto; }
.profile-photo-card .detail-section > .muted { margin-top: 10px; }

/* ---------- 仪表盘个人卡：左证件照 + 右文字 ---------- */
.profile-card .pc-body {
  display: flex;
  align-items: center;
  gap: 16px;
  position: relative;
  z-index: 1;
}
.profile-card .pc-main { flex: 1 1 auto; min-width: 0; }

.profile-card .pc-portrait {
  position: relative;
  overflow: hidden;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 72px;
  height: 101px;
  aspect-ratio: 295 / 413;
  border-radius: 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: 0 2px 8px rgba(12,18,38,.18);
}
.profile-card .pc-portrait.pc-portrait--fb {
  background: linear-gradient(150deg,#f3f3f0 0%,#e7e7e3 100%);
}
.profile-card .pc-initial {
  font-family: 'Space Grotesk','Inter',sans-serif;
  font-size: 24px;
  font-weight: 700;
  line-height: 1;
  color: #82878f;
}
.profile-card .pc-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

@media (max-width: 768px) {
  .profile-photo-row { gap: 16px; }
}

@media (max-width: 480px) {
  .profile-card .pc-portrait { width: 60px; height: 84px; border-radius: 8px; }
  .profile-card .pc-initial { font-size: 20px; }
  .profile-photo-row { gap: 14px; }
  .profile-photo-frame { width: 84px; height: 118px; }
  .profile-photo-initial { font-size: 30px; }
  .profile-dorm-form { flex-direction: column; align-items: stretch; }
  .profile-dorm-form .btn { width: 100%; }
  .profile-photo-file { max-width: 100%; }
}

@media (max-width: 360px) {
  .profile-card .pc-portrait { width: 54px; height: 76px; }
  .profile-photo-frame { width: 76px; height: 106px; }
}

/* ---------- 证件照 file input：可见标签 + 可见焦点环（可访问性） ---------- */
.profile-photo-file-label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}
.profile-photo-file:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }

/* ================================================================
   小视口（视口高 <= 760px）首屏可及性
   -----------------------------------------------------------------
   强制改密学生首次登录落在 /profile：「修改密码」小节是唯一出口，
   但 1280x720 下「更新密码」按钮 bottom 原本 871.5 > 720，需要下滚
   才能看到。这里只压缩纵向留白（卡片内边距 / 小节 padding / 字段
   margin / 提示条留白），不隐藏任何内容、不改模板结构、不缩小证件照
   （.profile-photo-frame 仍为 96x134）。1280x900、1920x1080、
   1024x768 均高于该断点，版式不受影响。
   ================================================================ */
/* 断点修正：上面标题里的「<= 760px」是历史值，实际断点已改为 max-height: 820px，
   以下方 @media 为准（这里只补说明，不动上方注释行，保证本文件 diff 零删除）。 */
@media (max-height: 820px) {
  .main-content { padding-top: 12px; }
  .main-content > .alert { padding: 8px 14px; margin-bottom: 10px; }
  .form-card { padding: 14px 16px; }
  .form-card .detail-section { margin-bottom: 10px; padding: 10px 12px; }
  .form-card .detail-section h3 { margin-bottom: 8px; }
  .form-card .form-group { margin-bottom: 8px; }
  .form-card .callout { margin: 6px 0; padding: 8px 12px; }
  .form-card .profile-photo-locktip { margin: 0 0 6px; }
}

/* ============================================================
   分页控件（M4：替换原来的「仅显示前 300 条」硬截断）
   ------------------------------------------------------------
   只新增 .student-pager* 前缀，不改动上方任何既有规则。
   布局：flex + flex-wrap，窄屏时页码整体换到第二行、可横向滚动兜底，
   保证 390px 下 clientWidth == scrollWidth（无横向溢出）。
   ============================================================ */
.student-pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px 16px;
  margin-top: 18px;
  padding: 12px 14px;
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: 10px;
  /* 兜底：页码再多也不会把 page 撑宽，内部容器自己滚动 */
  max-width: 100%;
  overflow: hidden;
}

.student-pager-info {
  color: var(--mist);
  font-size: 12.5px;
  white-space: nowrap;
}
.student-pager-info strong { color: var(--ink); font-weight: 600; }

.student-pager-btns {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  /* 页码多时允许自身滚动，绝不把父容器撑出横向滚动条 */
  min-width: 0;
  max-width: 100%;
}

.student-pager-btn,
.student-pager-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  height: 34px;
  padding: 0 10px;
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink);
  font-size: 13px;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}

.student-pager-btn:hover,
.student-pager-num:hover {
  border-color: var(--teal);
  color: var(--teal);
}

.student-pager-num.is-current {
  background: var(--teal);
  border-color: var(--teal);
  color: #fff;
  font-weight: 600;
  cursor: default;
}

.student-pager-btn.is-disabled {
  color: var(--mist);
  border-color: var(--line-soft);
  background: transparent;
  cursor: not-allowed;
  opacity: .6;
}

.student-pager-gap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 34px;
  color: var(--mist);
  font-size: 13px;
  user-select: none;
}

.student-pager-btn:focus-visible,
.student-pager-num:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 2px;
}

/* 窄屏：信息行与按钮组分两行，按钮可换行，避免 390px 横向溢出 */
@media (max-width: 480px) {
  .student-pager {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
    padding: 10px 12px;
  }
  .student-pager-info { text-align: center; white-space: normal; }
  .student-pager-btns { justify-content: center; }
  .student-pager-btn,
  .student-pager-num { min-width: 30px; height: 32px; padding: 0 8px; font-size: 12.5px; }
  .student-pager-gap { height: 32px; }
}

/* ================================================================
   级联删除预览弹窗（static/cascade_delete.js）
   —— 仅在本节内定义 .cascade-* 类，不改动任何既有规则。
   ================================================================ */
.cascade-dialog {
  width: min(520px, calc(100vw - 32px));
  max-width: calc(100vw - 32px);
  max-height: min(80vh, 640px);
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-2);
  overflow: hidden;
}

.cascade-dialog[open] {
  display: flex;
  flex-direction: column;
}

/* 遮罩：半透明深色，点击遮罩由 JS 判定 event.target === dialog 关闭 */
.cascade-dialog::backdrop {
  background: rgba(12, 18, 38, 0.45);
  backdrop-filter: blur(2px);
}

.cascade-dialog-head {
  padding: 20px 22px 14px;
  border-bottom: 1px solid var(--line-soft);
  flex: 0 0 auto;
}

.cascade-dialog-title {
  margin: 0;
  font-family: 'Space Grotesk', 'Inter', sans-serif;
  font-size: 18px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--ink);
}

.cascade-dialog-target {
  margin: 6px 0 0;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--graphite);
  word-break: break-word;
  overflow-wrap: anywhere;
}

.cascade-dialog-body {
  padding: 16px 22px 6px;
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.cascade-dialog-lead {
  margin: 0 0 10px;
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--ink);
}

.cascade-dialog-list {
  margin: 0;
  padding: 0 0 0 20px;
  list-style: disc outside;
}

.cascade-dialog-list li {
  margin: 0 0 6px;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--graphite);
  word-break: break-word;
  overflow-wrap: anywhere;
}

.cascade-dialog-list li::marker { color: var(--teal); }

.cascade-dialog-empty {
  margin: 0 0 10px;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--graphite);
}

/* 阻断项：错误语义，与「将被删除」的清单在视觉上必须区分开 */
.cascade-dialog-blockers {
  margin: 12px 0 4px;
  padding: 10px 12px 10px 28px;
  list-style: disc outside;
  background: var(--coral-light);
  border: 1px solid #fecaca;
  border-radius: var(--radius-sm);
}

.cascade-dialog-blockers li {
  margin: 0 0 4px;
  font-size: 13px;
  line-height: 1.5;
  color: #b91c1c;
  font-weight: 600;
  word-break: break-word;
  overflow-wrap: anywhere;
}

.cascade-dialog-blockers li:last-child { margin-bottom: 0; }
.cascade-dialog-blockers li::marker { color: var(--coral); }

.cascade-dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 14px 22px 18px;
  border-top: 1px solid var(--line-soft);
  flex: 0 0 auto;
}

.cascade-dialog-actions .btn {
  min-height: 40px;
  padding: 9px 18px;
  font-size: 13.5px;
}

/* 禁用态：can_force=false（存在 blockers）时按钮灰掉且不可点 */
.cascade-dialog-confirm:disabled,
.cascade-dialog-confirm[disabled] {
  background: var(--line);
  color: var(--mist);
  box-shadow: none;
  cursor: not-allowed;
  transform: none;
}

.cascade-dialog-confirm:disabled::before,
.cascade-dialog-confirm[disabled]::before { display: none; }

.cascade-dialog-actions .btn:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 2px;
}

/* 弹窗打开时锁背景滚动（JS 在 html 上挂 .cascade-dialog-open） */
html.cascade-dialog-open,
html.cascade-dialog-open body {
  overflow: hidden;
}

/* 窄屏（含 390px）：面板贴边留 12px，按钮纵向堆叠且占满宽度，绝无横向溢出 */
@media (max-width: 480px) {
  .cascade-dialog {
    width: calc(100vw - 24px);
    max-width: calc(100vw - 24px);
    max-height: 82vh;
  }
  .cascade-dialog-head { padding: 16px 16px 12px; }
  .cascade-dialog-title { font-size: 16.5px; }
  .cascade-dialog-body { padding: 14px 16px 4px; }
  .cascade-dialog-actions {
    flex-direction: column-reverse;
    align-items: stretch;
    gap: 8px;
    padding: 12px 16px 16px;
  }
  .cascade-dialog-actions .btn {
    width: 100%;
    justify-content: center;
    min-height: 44px;
  }
}

/* 弹窗居中修复：全局 reset 的 `*{margin:0}` 会覆盖 UA 对 <dialog> 的
   `margin:auto`，导致模态框贴左上角；这里显式恢复居中。 */
.cascade-dialog {
  margin: auto;
  position: fixed;
  inset: 0;
}

/* ================================================================
   级联删除弹窗 —— 审查修复补充（2026-10 前端审查 S5/M1/M7/L2/L3）
   —— 本节仍为**纯追加**：不改动上方任何既有行。
   ================================================================ */

/* L2 说明：`.cascade-dialog` 在文件里出现两次（2324 行与 2498 行）。
   前者是尺寸/配色/圆角等主样式；后者只补 `margin:auto; position:fixed; inset:0`
   —— 因为全局 reset 的 `*{margin:0}` 会盖掉 UA 给 <dialog> 的 `margin:auto`，
   少了它模态框会贴左上角。两处必须同时存在，这里保留原样并加注说明。 */

/* L3：阻断项的 role="alert" 已移到容器 .cascade-dialog-blocker-box 上
   （直接加在 <ul> 会覆盖列表角色，读屏不再按列表播报）。
   容器只负责语义与间距，视觉继续沿用原有 .cascade-dialog-blockers。 */
.cascade-dialog-blocker-box {
  margin: 12px 0 4px;
}

.cascade-dialog-blocker-box[hidden] {
  display: none;
}

.cascade-dialog-blocker-box .cascade-dialog-blockers {
  margin: 0;
}

/* M1：预览请求在途时按钮 disabled + aria-busy="true"。
   不加新动画（审查明确要求不引入 spinner），只用既有视觉语言把按钮「压灰」。 */
.cascade-dialog-actions .btn[aria-busy="true"],
form[data-cascade-preview] button[aria-busy="true"] {
  cursor: progress;
}

.btn[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
  filter: saturate(0.6);
}

/* M7：没有 showModal() 的极老浏览器 —— dialog[open] 是非模态面板，
   ::backdrop 不生效、也不会有焦点陷阱。降级实现走 window.confirm
   （见 cascade_delete.js 的 fallbackConfirm），此面板只会被瞬时打开；
   这里仍给它一个自查可见的遮罩外观，避免真被复用时「看起来像飘着的卡片」。 */
.cascade-dialog[open]:not(:modal) {
  z-index: 1200;
  box-shadow: 0 24px 64px rgba(12, 18, 38, 0.45);
}

.cascade-dialog[open]:not(:modal)::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background: rgba(12, 18, 38, 0.45);
}


/* ============================================================
   第 11 轮追加：班长审批中间环节（列表入口 / 详情时间轴「班」节点 /
   班长审批台 / 班长审批表单）
   只追加选择器，不改动上方任何既有规则；复用既有 token，
   未引入新的颜色字面量（--sky / --teal / --amber / --coral / --radius / --shadow-*）。
   ============================================================ */

/* ---------- 列表页状态徽标：monitor_approved = indigo（与 pending 的橙、已销假的蓝明确区分） ---------- */
/* 注意：badge 的通用兜底规则
   .badge:not(.badge-pending):not(.badge-approved):not(.badge-rejected):not(.badge-returned)
   特异性是 (0,5,0)，会盖掉同层级的 .badge-monitor_approved (0,1,0)，
   这里用 .badge.badge-monitor_approved (0,2,0) 仍不够，必须叠加 :not() 把它抬到 (0,5,0)+1。
   更稳的写法是直接抬到 (0,6,0)——用两个类选择器 + 四个 :not()。
   缺陷 M1：原色值与 .badge-returned 完全相同（#dbeafe / #1e40af），「待老师审批」与「已销假」撞色，
   这里改用 indigo（#e0e7ff / #3730a3），与琥珀/teal/红/蓝五色互不相同。 */
.badge.badge-monitor_approved:not(.badge-pending):not(.badge-approved):not(.badge-rejected):not(.badge-returned) {
  background: #e0e7ff;
  color: #3730a3;
}

/* ---------- 列表卡片：老师端「班长已通过」提示行 ---------- */
.leave-monitor-note {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  background: var(--sky-light);
  color: #1e40af;
  font-size: 12.5px;
  line-height: 1.5;
  overflow-wrap: anywhere;
}

/* ---------- 学生端：班长审批台入口卡 ---------- */
.monitor-entry {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 16px;
  padding: 14px 18px;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: var(--shadow-1);
  text-decoration: none;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease), transform .2s var(--ease);
}
.monitor-entry:hover {
  border-color: var(--sky);
  box-shadow: var(--shadow-2);
  transform: translateY(-1px);
}
.monitor-entry-icon {
  flex: 0 0 auto;
  width: 42px;
  height: 42px;
  border-radius: 12px;
  display: grid;
  place-items: center;
  font-size: 20px;
  line-height: 1;
  background: var(--sky-light);
}
.monitor-entry-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.monitor-entry-title {
  font-family: 'Space Grotesk', 'Inter', sans-serif;
  font-weight: 700;
  font-size: 15px;
  color: var(--ink);
  line-height: 1.35;
  overflow-wrap: anywhere;
}
.monitor-entry-sub {
  font-size: 12px;
  color: var(--mist);
}
.monitor-entry-arrow {
  flex: 0 0 auto;
  color: var(--sky);
  font-size: 16px;
  font-weight: 700;
}

/* ---------- 详情页时间轴：「班」节点（插在「申」与「审」之间） ---------- */
.leave-detail-node--monitor::before {
  background: var(--sky);
}
/* 尚未处理：用 amber 表示「卡在这一步」，与已处理的 sky 蓝区分 */
.leave-detail-node--monitor-waiting::before {
  background: var(--amber);
}

/* ---------- 详情页：等待班长审核的提示（老师端 pending） ---------- */
.leave-detail-wait-note {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: 999px;
  background: var(--amber-light);
  color: #92400e;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.5;
}

/* ---------- 详情页：班长审批表单 ---------- */
.monitor-review-form {
  padding: 16px 18px;
  margin-top: 22px;
  border: 1px solid var(--sky-light);
  border-radius: var(--radius);
  background: var(--paper);
}
.monitor-review-form .leave-detail-section-title {
  margin-top: 0;
}
.monitor-review-hint {
  margin: 0 0 12px;
  font-size: 12.5px;
  color: var(--graphite);
  line-height: 1.6;
}
.monitor-review-form .form-actions {
  flex-wrap: wrap;
}

/* ---------- 班长审批台：说明条 ---------- */
.monitor-banner {
  margin-bottom: 16px;
  padding: 14px 16px;
  border-radius: var(--radius);
  border: 1px solid var(--sky-light);
  background: var(--sky-light);
}
.monitor-banner-title {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-family: 'Space Grotesk', 'Inter', sans-serif;
  font-weight: 700;
  font-size: 14px;
  color: var(--ink);
}
.monitor-banner-count {
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--surface);
  color: var(--sky);
  font-size: 12px;
  font-weight: 600;
}
.monitor-banner-note {
  margin: 6px 0 0;
  font-size: 12.5px;
  color: var(--graphite);
  line-height: 1.6;
}

/* ---------- 班长审批台：卡片底部审批动作 ---------- */
.monitor-card-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 10px;
  padding-top: 12px;
  border-top: 1px dashed var(--line);
}
.monitor-action-form {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
/* 驳回表单：理由输入独占一行，避免在窄卡里被挤成一条缝 */
.monitor-action-form--reject {
  flex: 1 1 200px;
  align-items: stretch;
  flex-direction: column;
  gap: 6px;
}
.monitor-reject-label {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--graphite);
}
.monitor-action-form textarea {
  width: 100%;
  min-height: 54px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font-family: inherit;
  font-size: 12.5px;
  line-height: 1.5;
  resize: vertical;
}
.monitor-action-form textarea:focus {
  outline: none;
  border-color: var(--sky);
  box-shadow: 0 0 0 3px var(--sky-light);
}
.monitor-action-form--reject .btn {
  align-self: flex-start;
}

/* ---------- 窄屏：审批台卡片内的按钮不溢出 ---------- */
@media (max-width: 480px) {
  .monitor-entry {
    padding: 12px 14px;
    gap: 10px;
  }
  .monitor-card-actions {
    flex-direction: column;
  }
  .monitor-action-form {
    width: 100%;
  }
  .monitor-action-form--reject {
    flex: 1 1 auto;
    width: 100%;
  }
}

/* ============================================================
   第 12 轮追加：缺陷 S2 —— 历史老单（班长制度上线前）的「班」节点
   命中 monitor_skipped 时用中性灰「无需审核」，既不是 pending 的琥珀，
   也不是已通过的 teal，避免与「卡在这一步」混淆。只新增选择器。
   ============================================================ */
.leave-detail-node-status--skip {
  color: var(--graphite);
}

/* ================================================================
   宿舍数据看板（modules/dorm_stats）
   —— 本段为「只追加」新增：全部选择器以 .dormstats- 前缀命名，
      不覆盖任何既有规则；颜色沿用 :root 里的 token。
   ================================================================ */
.dormstats-shell {
  display: flex;
  flex-direction: column;
  gap: 18px;
  min-width: 0;
}

/* ---------- 工具条：维度 Tab + 月份选择 ---------- */
.dormstats-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
}
.dormstats-tabs {
  display: flex;
  gap: 8px;
  min-width: 0;
}
.dormstats-tabs .chip {
  cursor: pointer;
  font-family: inherit;
  min-width: 64px;
  justify-content: center;
}
.dormstats-month-picker {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
}
.dormstats-month-picker[hidden] { display: none; }
.dormstats-month-picker input {
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 13px;
  color: var(--ink);
  background: var(--paper);
}
.dormstats-month-picker input:focus {
  outline: none;
  border-color: var(--teal);
  box-shadow: 0 0 0 3px var(--teal-light);
}

/* ---------- 概览数字 ---------- */
.dormstats-summary {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
}
.dormstats-metric {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 16px 18px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  min-width: 0;
}
.dormstats-metric-label {
  font-size: 12px;
  color: var(--graphite);
  font-weight: 600;
}
.dormstats-metric-value {
  font-family: 'JetBrains Mono', monospace;
  font-size: 26px;
  font-weight: 600;
  line-height: 1.15;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.dormstats-metric-unit {
  font-size: 11px;
  color: var(--mist);
}
.dormstats-metric--accent .dormstats-metric-value { color: var(--teal); }

/* ---------- 图表卡片 ---------- */
.dormstats-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  padding: 18px 20px 20px;
  min-width: 0;
}
.dormstats-card[hidden] { display: none; }
.dormstats-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 14px;
}
.dormstats-card-title {
  margin: 0;
  font-family: 'Space Grotesk', sans-serif;
  font-size: 16px;
  font-weight: 700;
  color: var(--ink);
}
.dormstats-legend-line {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--graphite);
}
.dormstats-dot {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  margin-left: 8px;
}
.dormstats-dot--rate { background: var(--teal); }
.dormstats-dot--count { background: var(--sky); }

/* 图表容器：固定高度 + 相对定位，Chart.js 的 responsive 才能撑满。 */
.dormstats-chart-wrap {
  position: relative;
  width: 100%;
  height: 340px;
  min-width: 0;
}
.dormstats-chart-wrap canvas {
  display: block;
  width: 100% !important;
  height: 100% !important;
}
.dormstats-svg-host {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.dormstats-svg-host[hidden] { display: none; }
.dormstats-svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---------- 空状态 ---------- */
.dormstats-empty {
  padding: 36px 20px;
  text-align: center;
  color: var(--graphite);
  font-size: 14px;
  background: var(--paper);
  border: 1px dashed var(--line);
  border-radius: var(--radius-sm);
}
.dormstats-empty[hidden] { display: none; }
.dormstats-empty .muted { display: block; margin-top: 6px; }

/* ---------- 排名表 ---------- */
.dormstats-table-wrap {
  overflow-x: auto;
  box-shadow: none;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  animation: none;
}
.dormstats-table {
  width: 100%;
  min-width: 620px;
  border-collapse: collapse;
}
.dormstats-table th,
.dormstats-table td {
  padding: 10px 14px;
  font-size: 13px;
  white-space: nowrap;
}
.dormstats-table th {
  position: sticky;
  top: 0;
  background: #fafaf8;
  z-index: 1;
}
.dormstats-table td:first-child { font-size: 13px; }
.dormstats-rank-no { font-weight: 600; color: var(--graphite); }
.dormstats-dorm-name { font-weight: 600; }
.dormstats-chief { color: var(--graphite); }
/* 提交率 < 60% 的行整行标红（文字色，不用背景，避免与 hover 打架）。 */
.dormstats-row-low td { color: var(--coral); }
.dormstats-row-low .dormstats-chief { color: var(--coral); }
.dormstats-table-empty {
  text-align: center;
  color: var(--mist);
  padding: 28px 12px !important;
  white-space: normal;
}
.dormstats-rank-foot { margin: 12px 0 0; }

/* ---------- 加载失败提示 ---------- */
.dormstats-alert {
  padding: 12px 16px;
  border-radius: var(--radius-sm);
  background: var(--amber-light);
  color: #92400e;
  font-size: 13px;
  border: 1px solid #fcd34d;
}
.dormstats-alert[hidden] { display: none; }

/* ---------- 平板 / 窄屏 ---------- */
@media (max-width: 768px) {
  .dormstats-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dormstats-chart-wrap { height: 260px; }
  .dormstats-card { padding: 14px 14px 16px; }
  .dormstats-metric { padding: 12px 14px; }
  .dormstats-metric-value { font-size: 22px; }
  /* Tab 可以横滑，不换行挤压 */
  .dormstats-toolbar { align-items: stretch; }
  .dormstats-tabs {
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    flex: 1 1 100%;
  }
  .dormstats-tabs::-webkit-scrollbar { display: none; }
}

@media (max-width: 420px) {
  .dormstats-tabs .chip { min-width: 56px; padding: 6px 12px; }
  .dormstats-metric-value { font-size: 20px; }
  .dormstats-card-title { font-size: 15px; }
  .dormstats-legend-line { font-size: 11px; }
}

/* 尊重「减少动态效果」偏好：本模块自绘的动画全部关掉。 */
@media (prefers-reduced-motion: reduce) {
  .dormstats-table-wrap { animation: none; }
  .dormstats-card { animation: none; }
}

/* ============================================================
   第 12 轮追加：宿舍三张表在手机上可横滑（只追加，不改上方规则）

   .table-container 是 overflow:hidden，表格 min-content 宽约 770px，
   390px 手机上右侧「操作」列会被直接裁掉且无法横滑。这里在表格外层再套
   一层 .table-scroll 做横向滚动，页面本身保持不横滑（body overflow-x:hidden）。
   ============================================================ */
.table-scroll { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-scroll > table { min-width: 720px; }
/* 成员表只有 4 列，不需要 720px；用修饰类单独降低门槛。 */
.table-scroll--narrow > table { min-width: 420px; }

/* dorm_list 空状态与 dorm_submit 相机卡片：此前只在模板里用了类名，CSS 未定义。 */
.empty-state { padding: 32px; text-align: center; color: var(--graphite); border: 1px dashed var(--line); border-radius: 12px; }
.camera-card { max-width: 640px; margin: 0 auto; }

/* 上面的 .empty-state 与看板空状态同特异性且更靠后，这里把看板原有观感原样补回。 */
.dormstats-empty { padding: 36px 20px; font-size: 14px; background: var(--paper); border-radius: var(--radius-sm); }

/* SVG 兜底图：容器高度 auto，靠 viewBox 比例等比缩放，避免固定 340px 把图压扁。 */
.dormstats-chart-wrap--svg { height: auto; }
/* canvas 的 hidden 属性会被上面 `.dormstats-chart-wrap canvas{display:block}`
   盖掉，不显式隐藏它就会按 canvas 固有 2:1 比例把容器撑高、与 SVG 高度对不上。 */
.dormstats-chart-wrap--svg canvas[hidden] { display: none; }
/* 兜底时把绝对定位的 SVG 容器放回文档流，容器高度就等于 SVG 的等比高度。 */
.dormstats-chart-wrap--svg .dormstats-svg-host {
  position: static;
  inset: auto;
  width: 100%;
  height: auto;
}
.dormstats-svg { width: 100%; height: auto; }

/* 版本号页脚：登录页是 100vh 整屏，固定定位避免多出一屏滚动条 */
.version-footer { position: fixed; left: 0; right: 0; bottom: 0; text-align: center; }

/* ================================================================
   首次登录强制改密弹窗（m01622）
   —— 服务端渲染 `<dialog open>`，禁用 JS 也能看见并提交；
      JS 只做增强（showModal 升格 / 拦 Esc / 焦点）。
      骨架复用 .cascade-dialog（2324 行起），这里只补遮罩层与加宽标题。
   ================================================================ */

/* 无 JS 时的遮罩：<dialog open> 未进入 top layer，::backdrop 不会绘制，
   所以由这层 fixed 元素兜住「盖屏 + 吃点击 + 居中」。 */
.force-password-layer {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  overflow-y: auto;
  background: rgba(12, 18, 38, 0.45);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}

/* D4：既有 .cascade-dialog[open]:not(:modal)::before（2551 行，fixed + rgba .45）
   也会给非模态 <dialog> 铺一层暗色，和上面这层的 rgba .45 叠成 α≈0.70（无 JS 时
   明显比有 JS 暗）。选择器多加一个 .force-password-layer 前缀，特异性 0,3,1 >
   0,2,1 且位置更靠后，只关掉本弹窗里的那层，既有 .cascade-dialog 不受影响。 */
.force-password-layer .cascade-dialog[open]:not(:modal)::before { content: none; }

/* .main-content 带 content-fade-up 入场动画（animation-fill-mode: both 会保留
   transform），而 transform 祖先会给 position:fixed 造出新的包含块，
   结果遮罩只盖住内容区、盖不住侧边栏。改密页不需要入场动画，直接停掉。
   不支持 :has() 的浏览器退化为「只盖内容区」，弹窗本身仍然居中可用。 */
.main-content:has(> .force-password-layer) { animation: none; }
/* 兜底：强制态下 base.html 已经不渲染 main 里那份 flash（D7），这里再藏一次是为了
   将来若有模板在强制态往 main 里写 .alert，也不会和弹窗里那份重复出现。 */
.main-content:has(> .force-password-layer) > .alert { display: none; }

/* D9：无 JS 时背景页面也不跟着滚。真实层级是
   body > div.layout > main.main-content > div.force-password-layer
   （已用 test_client 渲染 + html.parser 建树核对），所以 :has() 参数里要带 .layout；
   写成 > .main-content 匹配不到。不支持 :has() 的浏览器只是保留滚动，弹窗仍可提交。 */
body:has(> .layout > .main-content > .force-password-layer) { overflow: hidden; }

.force-password-dialog {
  /* 回到文档流：盖屏与居中交给 .force-password-layer；
     固定定位那套（给 showModal 用的）见文件末尾的 --modal 规则。 */
  position: static;
  margin: auto;
  width: min(560px, 100%);
  max-width: 100%;
  max-height: calc(100vh - 32px);
  max-height: calc(100dvh - 32px);
}

.force-password-dialog .cascade-dialog-head {
  border-bottom: 2px solid var(--teal-light);
  background: linear-gradient(180deg, var(--teal-light), var(--surface));
}

.force-password-dialog .cascade-dialog-title {
  font-size: 20px;
  line-height: 1.35;
  color: var(--ink);
}

.force-password-dialog .cascade-dialog-body {
  padding-bottom: 18px;
  /* 列向 flex 子项默认 min-height:auto，矮屏（横屏手机）里会把提交按钮顶出框外；
     显式允许收缩，中间区才真的滚动而不是被 .cascade-dialog 的 overflow:hidden 裁掉。 */
  min-height: 0;
  overscroll-behavior: contain;
}
.force-password-dialog .cascade-dialog-lead { font-size: 14px; }
.force-password-dialog .alert { margin: 0 0 12px; }

.force-password-rules { margin: 0 0 16px; }
/* 圆点颜色沿用 2402 行的 .cascade-dialog-list li::marker，此处不再重复声明。 */

.force-password-form .form-group { margin-bottom: 14px; }
.force-password-form .form-group label {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}
.force-password-form .muted { display: block; margin-top: 6px; color: var(--mist); }
.force-password-form .btn {
  width: 100%;
  justify-content: center;
  min-height: 44px;
}

/* JS 升格成模态后：真正的遮罩是 .cascade-dialog::backdrop，
   本层必须让位（透明 + 不拦事件），否则会出现「双层变暗」。 */
.force-password-layer--modal {
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  pointer-events: none;
  overflow: visible;
}

/* top layer 里的 <dialog> 依赖 position:fixed; inset:0; margin:auto 居中，
   上面为了无 JS 布局把它们改成了 static/auto，升格后要还原。 */
.force-password-dialog--modal {
  position: fixed;
  inset: 0;
  margin: auto;
  width: min(560px, calc(100vw - 32px));
  max-width: calc(100vw - 32px);
  pointer-events: auto;
}

/* 移动端：贴边 12px、标题降一档；面板自身限高、内部 .cascade-dialog-body 滚动，
   任何高度都不会把提交按钮挤出屏幕（flex + margin:auto 在溢出时自动退化为起点对齐）。 */
@media (max-width: 640px) {
  .force-password-layer { padding: 12px; }
  .force-password-dialog {
    width: 100%;
    max-width: 100%;
    max-height: calc(100vh - 24px);
    max-height: calc(100dvh - 24px);
  }
  .force-password-dialog .cascade-dialog-title { font-size: 17px; }
  .force-password-dialog .cascade-dialog-body { padding: 14px 16px 16px; }
  .force-password-dialog .cascade-dialog-lead { font-size: 13.5px; }
}

/* ================================================================
   强制改密弹窗里的「退出登录」出口（复审 P2）
   —— 弹窗本身仍然没有关闭按钮/Esc/点遮罩退出（那是需求）；
      这里补的是 app.py 白名单已放行 logout 所对应的那条可见出口。
      纯追加，不改上面 .force-password-layer 的 z-index 与既有规则。
   ================================================================ */
.force-password-exit {
  /* 与改密表单视觉分离：一条虚线分隔 + 上方留白，读起来像「脚注」而不是第二个主操作 */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px dashed var(--line-soft);
}

/* 次要操作：文本按钮，不用填充色，避免和主提交按钮抢注意力；
   高 40px 起（触摸目标），左右内边距给足，键盘焦点用 :focus-visible 显式描边。 */
.force-password-exit-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: 8px 12px;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--graphite);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  cursor: pointer;
  transition: background 0.2s var(--ease), color 0.2s var(--ease);
}

.force-password-exit-btn:hover {
  background: var(--line-soft);
  color: var(--ink);
}

.force-password-exit-btn:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 2px;
}

.force-password-exit-hint {
  margin: 0;
  padding: 0 12px 2px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--mist);
}

/* 窄屏：出口独占整行、按钮拉满宽度并抬高到 44px，
   与上方的「更新密码并继续」纵向分开，不会挤成同一行。 */
@media (max-width: 640px) {
  .force-password-exit { margin-top: 12px; padding-top: 10px; }
  .force-password-exit-btn {
    width: 100%;
    min-height: 44px;
    text-align: center;
  }
  .force-password-exit-hint { padding: 0; }
}

/* ================================================================
   请假统计：未销假人数/条数 + 待审批条数
   （仅在文件末尾追加，不改动上面任何既有规则）
   复用 .chip / .chip-active 的既有视觉，这里只补「数字更醒目」与
   学生端/班长台两处轻量差异，避免再造一套按钮样式。
   ================================================================ */
/* 教师/管理员审批页：统计自成一行（工具条是 flex-wrap，100% 宽度即换行），
   左边是可点击筛选的「未销假」chip，右边是「待审批」纯文本计数。 */
.leave-stats {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  flex: 1 1 100%;
}
.leave-stat-chip { gap: 4px; }
.leave-stat-num {
  font-variant-numeric: tabular-nums;
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
}
/* 选中态：chip 背景变成 teal 时数字必须跟着变白，否则深色数字压在高亮底上看不清 */
.leave-stat-chip.chip-active .leave-stat-num { color: #fff; }
.leave-stat-item {
  font-size: 13px;
  font-weight: 600;
  color: var(--graphite);
  line-height: 1.5;
}
.leave-stat-item .leave-stat-num { color: var(--ink); }
.leave-stat-hint {
  font-size: 12px;
  color: var(--mist);
}

/* 学生端「我的未销假」：独立小卡片，不抢「班长审批台」入口的视觉权重 */
.leave-stats--student {
  flex: initial;
  justify-content: flex-start;
  margin-bottom: 16px;
  padding: 10px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-1);
}
.leave-stats--student .leave-stat-num {
  font-size: 15px;
  color: var(--teal-dim);
}

/* 班长审批台说明条：本班未销假与「待审核」并排，用 amber 区分「待销假」语义 */
.monitor-banner-count--warn {
  background: var(--amber-light);
  color: #92400e;
}

/* 窄屏：统计行改为纵向堆叠，数字与文案不挤在同一行 */
@media (max-width: 640px) {
  .leave-stats { gap: 6px; }
  .leave-stats--student { padding: 10px 12px; }
}

/* ================================================================
   1.5 热更新第 3 项 — 总览页
   ① 统计卡片整卡可点（仅老师/管理员视图使用 <a class="stat-card">）
   ② 快速操作区替换为「今日宿舍打卡」卡片
   全部为**追加**规则，不改动上面既有的 .stat-card / .stats-grid /
   .quick-actions / .action-buttons 规则，学生视图沿用原样式不受影响。
   ================================================================ */

/* ① 可点击统计卡片：<a> 去掉链接默认样式，hover 给出「可点」提示 */
a.stat-card,
.stat-card--link {
  display: block;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
}
a.stat-card:hover {
  /* 复用既有 .stat-card:hover 的上浮 + 阴影，这里只叠一圈浅青提示色 */
  box-shadow: var(--shadow-3), 0 0 0 2px var(--teal-light);
}
a.stat-card:hover .stat-label { color: var(--teal-dim); }
a.stat-card:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 3px;
}
a.stat-card .stat-label::after {
  /* 纯装饰箭头，但 ::after 的内容默认会被读屏朗读成「›」（a11y 缺陷）。
     第一行是旧浏览器回退：不支持 alt-text 语法时整条声明被丢弃，箭头照旧显示；
     第二行 content: '›' / '' 用 CSS 内容替换语法给出空替代文本，读屏直接跳过。 */
  content: '›';
  content: '›' / '';
  display: inline-block;
  margin-left: 6px;
  font-weight: 700;
  color: var(--mist);
  transition: transform 0.25s var(--ease), color 0.25s var(--ease);
}
a.stat-card:hover .stat-label::after {
  color: var(--teal);
  transform: translateX(3px);
}

/* ② 今日宿舍打卡卡片 */
.dorm-today {
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  padding: 22px 24px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.dorm-today-headline {
  font-size: 20px;
  font-weight: 700;
  color: var(--ink);
  letter-spacing: -0.01em;
}
.dorm-today-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.dorm-today-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 6px 14px;
  border-radius: 999px;
  background: var(--amber-light);
  color: #92400e;
  font-size: 13px;
  font-weight: 500;
  line-height: 1.4;
}
.dorm-today-pill strong {
  font-family: 'Space Grotesk', sans-serif;
  font-size: 15px;
  font-weight: 700;
}
.dorm-today-pill--rate {
  background: var(--teal-light);
  color: var(--teal-dim);
}
/* 应交为 0（老师无宿舍）：— 用中性灰，避免看起来像「0% 很差」 */
.dorm-today-pill--rate.is-na {
  background: var(--line-soft);
  color: var(--graphite);
}
.dorm-today-missing {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--graphite);
}
.dorm-today-missing--ok { color: var(--teal-dim); font-weight: 500; }
.dorm-today-missing-k { font-weight: 600; color: var(--ink); }
.dorm-missing-chip {
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--coral-light);
  color: #b91c1c;
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.dorm-missing-more { font-size: 12px; color: var(--mist); }
.dorm-today .action-buttons { margin-top: 2px; }

@media (max-width: 640px) {
  .dorm-today { padding: 18px 16px; }
  .dorm-today-headline { font-size: 17px; }
}

/* ----------------------------------------------------------------
   1.5 热更新第 1 项（P3）— 宿舍名 chip 溢出
   宿舍名是自由文本（如「91栋901」「南区 12 号楼 3 单元 401」），超长时上面
   那条 .dorm-missing-chip（既有规则，未改动）只有 padding/圆角，字会顶出胶囊、
   在窄屏上横向溢出卡片。这里追加一条同选择器规则补上收敛约束：
   max-width:100% 让 chip 不超过容器宽，overflow-wrap:anywhere 允许中文/
   无空格长串在任意字符处折行。追加在同选择器规则之后，层叠顺序天然生效。
   ---------------------------------------------------------------- */
.dorm-missing-chip {
  max-width: 100%;
  overflow-wrap: anywhere;
}

/* ==================================================================
   v1.6 · Campus workspace / Harmony-inspired, not an ArkUI implementation.
   Existing module geometry is retained; this final layer owns visual tokens.
   ================================================================== */
:root {
  --paper: #f3f5f9; --surface: #fff; --ink: #182338; --ink-2: #26364d;
  --graphite: #59677b; --mist: #68768a; --line: #dfe5ee; --line-soft: #edf0f5;
  --teal: #2363d9; --teal-dim: #184baa; --teal-light: #eaf1ff;
  --sky: #2363d9; --sky-light: #eaf1ff; --coral: #c73542;
  --radius: 22px; --radius-sm: 12px; --sidebar-w: 248px;
  --shadow-1: 0 2px 8px rgba(30,48,78,.035);
  --shadow-2: 0 6px 24px rgba(30,48,78,.045);
  --shadow-3: 0 12px 32px rgba(30,48,78,.08);
  --ease: cubic-bezier(.2,0,.2,1); --ease-out: cubic-bezier(0,0,.2,1);
  --motion-fast: 120ms; --motion-normal: 200ms; --motion-panel: 260ms;
  --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
}
html { scroll-behavior: auto; }
body, button, input, select, textarea, h1, h2, h3, .stat-number,
.login-hint, .login-form input, .profile-card .pc-name, .mobile-title,
th, td:first-child, .dorm-today-pill strong { font-family: var(--font-ui); }
body { color: var(--graphite); }
h1 { font-size: 28px; line-height: 1.4; letter-spacing: -.035em; }
h2 { font-size: 19px; }
a, button, input, select, textarea { -webkit-tap-highlight-color: rgba(35,99,217,.12); }
a:focus, button:focus, input:focus, select:focus, textarea:focus, summary:focus {
  outline: 3px solid #2363d9; outline-offset: 3px;
}
.skip-link { position: fixed; top: -100px; left: 16px; z-index: 10000; padding: 12px 20px; border-radius: 12px; background: #fff; color: #184baa; }
.skip-link:focus { top: 12px; }
.sidebar, .main-content, .alert, .disk-alert, .stat-card, .table-container,
.form-card, .login-card, .profile-card { animation: none; opacity: 1; }
.sidebar::before, .login-page::before, .login-page::after, .profile-card::after,
.pc-shimmer, .pc-pulse, .stat-card::after, .btn::before { display: none; animation: none; }
.sidebar { background: #f9faff; color: var(--ink); border-right: 1px solid var(--line); box-shadow: none; }
.sidebar-header { padding: 30px 20px 22px; border-bottom: 0; }
.sidebar-header h2 { color: var(--ink); font-size: 16px; gap: 10px; }
.brand-mark, .login-brand { display: inline-flex; justify-content: center; align-items: center; background: #2363d9; color: #fff; border-radius: 13px; width: 36px; height: 36px; font-size: 18px; font-weight: 700; flex-shrink: 0; }
.sidebar-caption { font-size: 11px; color: var(--graphite); margin-top: 10px; letter-spacing: .1em; }
.sidebar-nav { padding: 8px 14px 24px; }
.nav-item { min-height: 46px; padding: 11px 14px; border-radius: 14px; color: #536278; margin-bottom: 6px; font-size: 14px; transition: background-color 150ms var(--ease), color 150ms var(--ease); }
.nav-item::before { display: none; }
.nav-item:hover { background: #edf1f8; color: var(--ink); }
.nav-item.active { background: #e5eeff; color: #184baa; font-weight: 700; }
.sidebar-nav hr { border-color: var(--line); margin: 18px 6px; }
.sidebar-close { display: none; }
.main-content { min-width: 0; padding: 40px 36px; }
.main-content > footer { margin-top: 40px; font-size: 12px; color: var(--graphite); }
.page-header { margin-bottom: 24px; }
.page-eyebrow, .login-eyebrow { display: block; color: #496b9d; font-size: 11px; font-weight: 700; letter-spacing: .16em; margin-bottom: 8px; }
.welcome { margin-top: 8px; }
.stats-grid { gap: 16px; margin: 26px 0; }
.stat-card { border: 1px solid #e7ecf4; text-align: left; padding: 25px; transition: transform 200ms var(--ease); }
.stat-number { font-size: 38px; color: var(--ink); font-weight: 650; }
.stat-label { font-size: 13px; margin-top: 14px; color: var(--graphite); }
.stat-card:hover, a.stat-card:hover { transform: none; box-shadow: var(--shadow-2); }
.stat-card--link .stat-label { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.quick-actions { margin-top: 28px; }
.dorm-today, .form-card, .table-container { border: 1px solid #e7ecf4; }
.dorm-today { padding: 26px; gap: 18px; }
.btn { min-height: 44px; justify-content: center; border-radius: 12px; transition: transform 120ms var(--ease), background-color 150ms var(--ease), color 150ms var(--ease); }
.btn-primary, .btn-primary:hover { box-shadow: none; }
.btn-info { background: #eaf1ff; color: #184baa; }
.btn-info:hover { background: #dce8ff; }
.btn-warning { color: #563500; }
.btn-sm { min-height: 36px; padding: 7px 13px; font-size: 12px; }
.btn:hover { transform: none; }
.btn:active, .mobile-menu-btn:active, .chip:active { transform: scale(.98); }
button:disabled, .btn[aria-disabled="true"] { opacity: .6; cursor: not-allowed; }
.chip { min-height: 38px; transition: background-color 150ms var(--ease), color 150ms var(--ease); }
.chip-active:hover { color: #fff; }
.table-container { box-shadow: var(--shadow-1); }
th { background: #f8faff; font-size: 12px; letter-spacing: .02em; border-bottom: 1px solid var(--line); }
th, td { padding: 16px 18px; }
tr:hover td { background: #f8faff; }
.form-group input, .form-group select, .form-group textarea, .login-form input { min-height: 46px; background: #f8faff; border: 1px solid #d8e0ec; font-size: 16px; transition: border-color 150ms var(--ease); }
.form-group input[type="checkbox"], .form-group input[type="radio"] { min-height: auto; }
.form-group input:focus, .form-group textarea:focus, .form-group select:focus, .login-form input:focus { box-shadow: 0 0 0 3px #eaf1ff; border-color: #2363d9; }
.detail-section, .leave-toolbar { background: #f8faff; border-color: var(--line); }
.empty-row { color: var(--graphite); padding: 44px 20px; }
.alert, .disk-alert { line-height: 1.7; border-radius: 14px; }
.profile-card { background: #fff; color: var(--ink); border: 1px solid #e7ecf4; }
.profile-card .pc-name { color: var(--ink); font-size: 24px; }
.profile-card .pc-item .k { color: var(--graphite); }
.profile-card .pc-item .v { color: var(--ink); }
.login-page { background: radial-gradient(ellipse at 15% 18%, #e1ecff, transparent 60%), #f3f5f9; min-height: 100vh; padding: 64px 32px; gap: 8vw; overflow: visible; }
.login-intro { max-width: 440px; }
.login-intro h2 { font-size: 44px; line-height: 1.45; letter-spacing: -.04em; margin: 24px 0; }
.login-intro > p { font-size: 16px; line-height: 1.9; }
.login-feature-list { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 36px; }
.login-feature-list span { padding: 8px 14px; background: #fff; border: 1px solid #e4eaf4; border-radius: 100px; color: #496384; font-size: 13px; }
.login-card { background: #fff; backdrop-filter: none; width: 420px; max-width: 100%; border: 1px solid #e5ebf4; border-radius: 28px; padding: 36px; box-shadow: 0 16px 60px rgba(41,67,111,.08); }
.login-brand { width: 48px; height: 48px; font-size: 24px; border-radius: 17px; margin-bottom: 20px; }
.login-card h1, .login-subtitle { text-align: left; }
.login-card h1 { font-size: 28px; }
.login-subtitle { line-height: 1.7; margin-top: 8px; margin-bottom: 24px; }
.login-hint { margin-top: 12px; text-align: left; color: var(--graphite); font-size: 12px; line-height: 1.7; }
.version-footer { color: var(--graphite); background: transparent; }
@media (hover: hover) and (pointer: fine) {
  a.stat-card:hover { transform: translateY(-2px); border-color: #c9d9f7; }
}
@media (prefers-reduced-motion: no-preference) {
  .login-card { animation: workspace-enter 240ms var(--ease-out); }
  .stats-grid .stat-card { animation: workspace-enter 220ms var(--ease-out); }
  .stats-grid .stat-card:nth-child(2) { animation-delay: 20ms; }
  .stats-grid .stat-card:nth-child(3) { animation-delay: 40ms; }
  .stats-grid .stat-card:nth-child(4) { animation-delay: 60ms; }
}
@keyframes workspace-enter { from { opacity: .75; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (max-width: 1000px) {
  .login-page { gap: 32px; }
  .login-intro h2 { font-size: 34px; }
  .main-content { padding: 28px 24px; }
}
@media (max-width: 768px) {
  .mobile-topbar { padding: 10px 16px; background: #f9faff; border-color: var(--line); box-shadow: none; }
  .mobile-menu-btn { width: 44px; height: 44px; border-radius: 14px; display: none; }
  .nav-enhanced .mobile-menu-btn { display: block; }
  .mobile-title { font-size: 15px; }
  /* Safe baseline: navigation remains in document flow when JS is unavailable. */
  .layout { display: block; }
  .sidebar { position: relative; width: 100%; height: auto; transform: none; border-right: 0; border-bottom: 1px solid var(--line); box-shadow: none; }
  .sidebar-header { padding: 20px; }
  .sidebar-nav { display: flex; flex-wrap: wrap; gap: 6px; }
  .sidebar-nav hr { width: 100%; margin: 6px; }
  .nav-item { font-size: 14px; justify-content: flex-start; padding: 11px 14px; gap: 10px; margin: 0; }
  .nav-enhanced .sidebar { position: fixed; top: 0; left: 0; bottom: 0; width: 288px; max-width: 88vw; height: 100vh; height: 100dvh; transform: translateX(-105%); visibility: hidden; transition: transform 260ms var(--ease); z-index: 200; border-radius: 0 24px 24px 0; }
  /* Visibility changes immediately in both directions: entry can receive focus,
     and closed navigation cannot remain tabbable during a visual exit delay.
     Animate entry/transform only; no hidden-but-interactive exit window. */
  .nav-enhanced .sidebar.open { transform: none; visibility: visible; transition-delay: 0s; }
  .nav-enhanced .sidebar-nav { display: block; overflow-y: auto; }
  .nav-enhanced .nav-item { margin-bottom: 5px; }
  .nav-enhanced .sidebar-header { padding: 26px 18px 18px; }
  .nav-enhanced .sidebar-header h2 { font-size: 14px; padding-right: 28px; }
  .nav-enhanced .sidebar-close { display: block; position: absolute; top: 20px; right: 10px; width: 40px; height: 44px; border: 0; background: transparent; color: var(--ink); font-size: 26px; cursor: pointer; }
  .sidebar-overlay { display: none; background: rgba(24,35,56,.3); transition: opacity 160ms var(--ease); }
  .nav-enhanced .sidebar-overlay { display: block; }
  .drawer-open { overflow: hidden; }
  .main-content { margin-left: 0; padding: 24px 16px; min-height: 70vh; }
  h1 { font-size: 25px; }
  .stats-grid { grid-template-columns: repeat(2,minmax(0,1fr)); gap: 12px; }
  .stat-card { padding: 20px 16px; border-radius: 18px; }
  .stat-number { font-size: 32px; }
  .stat-label { font-size: 12px; }
  .form-card { padding: 22px 18px; }
  .dorm-today { padding: 22px 18px; }
  .btn-sm { min-height: 44px; }
  .rwd-table tr { border-radius: 16px; padding: 8px 12px; }
  .rwd-table td::before { color: var(--graphite); }
  .login-page { flex-direction: column; gap: 24px; padding: 32px 20px 60px; }
  .login-intro { width: 100%; max-width: 420px; }
  .login-intro h2 { font-size: 28px; margin: 12px 0; }
  .login-intro h2 br { display: none; }
  .login-intro > p { font-size: 13px; }
  .login-feature-list { display: none; }
  .login-card { padding: 28px 24px; border-radius: 24px; width: 420px; }
}
@media (max-width: 360px) { .stats-grid { grid-template-columns: 1fr; } .login-page { padding-left: 12px; padding-right: 12px; } }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; animation-delay: 0s !important; transition-duration: 0s !important; transition-delay: 0s !important; scroll-behavior: auto !important; }
  .stat-card, .btn, .chip { transform: none !important; }
}
