:root {
  --blue: #2563eb;
  --blue-dark: #1d4ed8;
  --ink: #172033;
  --muted: #64748b;
  --line: #dbe3ee;
  --paper: #ffffff;
  --bg: #f5f7fb;
  --green: #0f9f6e;
  --amber: #d97706;
  --rose: #e11d48;
  --shadow: 0 14px 30px rgba(15, 23, 42, 0.08);
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: Inter, "PingFang SC", "Microsoft YaHei", Arial, sans-serif;
  letter-spacing: 0;
}

button,
input,
select,
textarea {
  font: inherit;
}

button {
  border: 0;
  background: var(--blue);
  color: #fff;
  border-radius: 8px;
  padding: 10px 14px;
  cursor: pointer;
  min-height: 40px;
}

button:hover {
  background: var(--blue-dark);
}

button.secondary {
  background: #e8eef8;
  color: #1e3a8a;
}

button.ghost {
  background: transparent;
  color: var(--blue);
  border: 1px solid var(--line);
}

button.warn {
  background: var(--amber);
}

button.danger {
  background: var(--rose);
}

button.ok {
  background: var(--green);
}

button:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

a {
  color: var(--blue);
}

input,
select,
textarea {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 10px 12px;
  background: #fff;
  color: var(--ink);
}

textarea {
  min-height: 92px;
  resize: vertical;
}

[hidden] {
  display: none !important;
}

.boot {
  padding: 40px;
  color: var(--muted);
}

.toast-stack {
  position: fixed;
  top: 18px;
  right: 18px;
  z-index: 1000;
  display: grid;
  gap: 10px;
  width: min(420px, calc(100vw - 36px));
  pointer-events: none;
}

.toast {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 12px;
  align-items: start;
  padding: 12px 12px 12px 14px;
  border: 1px solid var(--line);
  border-left: 4px solid var(--green);
  border-radius: 8px;
  background: #fff;
  box-shadow: var(--shadow);
  color: var(--ink);
  pointer-events: auto;
  animation: toast-in 160ms ease-out;
}

.toast.error {
  border-left-color: var(--rose);
}

.toast-close {
  width: 28px;
  height: 28px;
  min-height: 28px;
  padding: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  line-height: 1;
}

.toast-close:hover {
  background: #f1f5f9;
  color: var(--ink);
}

.toast-exit {
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 160ms ease, transform 160ms ease;
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.teacher-shell {
  display: grid;
  grid-template-columns: 230px 1fr;
  min-height: 100vh;
}

.sidebar {
  background: #ffffff;
  border-right: 1px solid var(--line);
  padding: 18px 14px;
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
}

/* Logout pinned to the bottom of the sidebar on desktop (margin-top:auto eats the slack
   between the nav and the bottom edge). On mobile it moves up into the brand row — see the
   responsive block. */
.sidebar-logout {
  margin-top: auto;
}

.brand {
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 8px 8px 18px;
  font-weight: 800;
}

.brand-mark {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  background: var(--blue);
  color: #fff;
  border-radius: 8px;
  overflow: hidden;
}

.brand-mark img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
}

.nav button {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  background: transparent;
  color: var(--ink);
  text-align: left;
  margin: 4px 0;
}

.nav button.active {
  background: #eaf1ff;
  color: #1d4ed8;
}

/* Fixed icon box so every nav label starts at the same x (the old single-char glyphs had
   different widths and shifted the text). flex:0 0 auto keeps the icon from shrinking when
   the nav scrolls horizontally on mobile. */
.nav button .nav-icon {
  flex: 0 0 20px;
  width: 20px;
  height: 20px;
}

.nav button.active .nav-icon {
  color: #1d4ed8;
}

.main {
  padding: 24px;
  max-width: 1360px;
  width: 100%;
  /* Grid items default to min-width:auto and refuse to shrink below their content's
     intrinsic width — a wide child then stretches the 1fr track and overflows the page.
     min-width:0 lets .main shrink to the viewport; wide children clip/scroll inside it. */
  min-width: 0;
}

.topbar,
.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
}

.title h1,
.section-head h2 {
  margin: 0;
}

.title p,
.section-head p,
.muted {
  color: var(--muted);
  margin: 4px 0 0;
}

.stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
  margin-bottom: 18px;
}

.stat,
.card,
.request-card,
.option-card,
.portal-panel {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: var(--shadow);
}

.stat {
  padding: 16px;
}

.stat strong {
  display: block;
  font-size: 28px;
}

.stat-sub {
  display: block;
  color: var(--muted);
  font-size: 13px;
  margin-top: 2px;
}

.stat-clear {
  box-shadow: var(--shadow), inset 3px 0 0 var(--green);
}

.stat-clear strong {
  color: var(--green);
}

.stat-attention {
  box-shadow: var(--shadow), inset 3px 0 0 var(--amber);
}

.stat-attention strong {
  color: var(--amber);
}

/* Dashboard stat tiles are <button>s (jump to the owning tab) — strip native button chrome
   so they look identical to the old divs, then add an affordance on hover. */
button.stat {
  font: inherit;
  color: inherit;
  text-align: left;
  width: 100%;
  cursor: pointer;
  transition: border-color 0.15s ease, transform 0.05s ease;
}

button.stat:hover {
  border-color: var(--blue);
}

button.stat:active {
  transform: translateY(1px);
}

/* Shared empty state for clean installs (roster / inbox / ledger). */
.empty-state {
  text-align: center;
  padding: 32px 16px;
}

.empty-state-title {
  margin: 0 0 6px;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}

.empty-state-hint {
  margin: 0 0 16px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted);
}

.empty-state-hint:last-child {
  margin-bottom: 0;
}

/* Schedule page nudge shown until the teacher has published any open slots. */
.schedule-hint {
  margin: 0 0 12px;
  padding: 10px 14px;
  border: 1px solid rgba(37, 99, 235, 0.25);
  background: rgba(37, 99, 235, 0.07);
  border-radius: 8px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink);
}

.grid-2 {
  display: grid;
  grid-template-columns: minmax(280px, 0.72fr) minmax(480px, 1.28fr);
  gap: 16px;
}

.grid-2-wide-left {
  display: grid;
  grid-template-columns: minmax(480px, 1.28fr) minmax(280px, 0.72fr);
  gap: 16px;
}

.card {
  padding: 16px;
}

.calendar-card {
  overflow: hidden;
}

.calendar-shell {
  min-height: 640px;
}

.parent-calendar {
  min-height: 260px;
}

.fc {
  color: var(--ink);
}

.fc button {
  width: auto;
}

.fc .fc-toolbar {
  gap: 12px;
  align-items: center;
}

.fc .fc-toolbar-title {
  font-size: 22px;
  font-weight: 800;
}

.fc .fc-button-primary {
  background: var(--blue);
  border-color: var(--blue);
  border-radius: 8px;
  box-shadow: none;
}

.fc .fc-button-primary:hover,
.fc .fc-button-primary:focus,
.fc .fc-button-primary:not(:disabled).fc-button-active {
  background: var(--blue-dark);
  border-color: var(--blue-dark);
}

.fc .fc-scrollgrid,
.fc .fc-theme-standard td,
.fc .fc-theme-standard th {
  border-color: var(--line);
}

.fc .fc-col-header-cell-cushion,
.fc .fc-daygrid-day-number {
  color: var(--ink);
  text-decoration: none;
}

.fc .lesson-event {
  border: 0;
  border-radius: 6px;
  padding: 1px 2px;
}

.fc .lesson-scheduled {
  background: var(--blue);
}

.fc .lesson-rescheduled {
  background: var(--green);
}

.fc .lesson-makeup_pending {
  background: var(--amber);
}

.fc .open-window-event {
  background: rgba(217, 119, 6, 0.18);
  border: 1px solid rgba(217, 119, 6, 0.24);
  color: #7c2d12;
  cursor: pointer;
}

.fc .open-window-event .fc-event-main {
  color: #7c2d12;
}

/* A slot freed up by a reschedule/makeup: same "open" family, tinted differently so the
   teacher can tell it apart from a window they declared themselves. */
.fc .released-slot-event {
  background: rgba(13, 148, 136, 0.16);
  border: 1px dashed rgba(13, 148, 136, 0.5);
  color: #0f766e;
}

.fc .released-slot-event .fc-event-main {
  color: #0f766e;
}

/* Closed-day background tints. A public holiday (red) or teacher vacation (slate) shades
   the whole day column so it's clear WHY no open slots appear there. Hover shows the name
   (set via eventDidMount). Kept subtle so existing lessons on that day stay readable. */
.fc .fc-bg-event.holiday-bg-event {
  background: rgba(220, 38, 38, 0.12);
  opacity: 1;
}

.fc .fc-bg-event.vacation-bg-event {
  background: rgba(100, 116, 139, 0.16);
  opacity: 1;
}

.timeoff-block {
  margin-top: 12px;
  padding-top: 14px;
  border-top: 1px solid var(--border, #e5e7eb);
}

.timeoff-title {
  margin: 0 0 2px;
  font-size: 15px;
}

.timeoff-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 10px;
  margin: 10px 0 14px;
}

.timeoff-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  color: #475569;
}

.timeoff-field input {
  width: auto;
}

.timeoff-label-field {
  flex: 1 1 140px;
}

.timeoff-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.timeoff-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 12px;
  background: rgba(100, 116, 139, 0.08);
  border-radius: 8px;
}

.timeoff-info {
  display: flex;
  flex-direction: column;
}

.timeoff-info small {
  color: #64748b;
}

.timeoff-remove {
  flex: none;
  width: auto;
  background: none;
  border: none;
  color: #dc2626;
  cursor: pointer;
  font-size: 13px;
  padding: 4px 6px;
}

.timeoff-remove:hover {
  text-decoration: underline;
}

.toggle-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 0 2px;
  cursor: pointer;
}

/* The global `input, select, textarea { width: 100% }` rule (form controls) also hits this
   checkbox, stretching it across the whole flex row so the label text got squeezed to zero
   width and collapsed into a one-char-per-line vertical column at the far edge. Reset it. */
.toggle-row > input {
  margin-top: 3px;
  flex: none;
  width: auto;
}

/* Let the text column take the row's remaining width; min-width:0 lets a flex item shrink
   below its min-content (spaceless Chinese = 1 char) so the block children wrap normally. */
.toggle-row > .toggle-text {
  flex: 1 1 0;
  min-width: 0;
}

/* Give the toggle label an explicit size/weight instead of inheriting the browser default
   (16px/bold-700). The old inherited value sat ABOVE nothing in the scale and out-weighted
   the 18px/600 section title, so on a phone the label read as "bigger than the heading".
   15px/600 keeps the hierarchy: section title (18/600) > label (15/600) > hint (13). */
.toggle-row .toggle-text strong {
  display: block;
  font-size: 15px;
  font-weight: 600;
}

.toggle-row .toggle-text small {
  display: block;
  margin-top: 2px;
  font-size: 13px;
  color: var(--muted, #64748b);
}

.parent-calendar .fc .fc-toolbar-title {
  font-size: 18px;
}

.parent-calendar .fc .fc-button-group {
  gap: 6px;
}

.parent-calendar .fc .fc-button-group > .fc-button {
  border-radius: 8px;
}

.parent-calendar .fc .fc-button-group > .fc-button:not(:first-child) {
  margin-left: 0;
}

.parent-calendar .fc .fc-button-primary {
  min-width: 72px;
  border: 1px solid var(--line);
  background: #fff;
  color: var(--blue);
}

.parent-calendar .fc .fc-button-primary:hover,
.parent-calendar .fc .fc-button-primary:focus {
  border-color: var(--blue);
  background: #eff6ff;
  color: var(--blue-dark);
}

.parent-calendar .fc .fc-list {
  border-color: var(--line);
  border-radius: 8px;
  overflow: hidden;
}

.parent-calendar .fc .fc-list-event {
  cursor: pointer;
}

.parent-calendar .fc .parent-lesson-event {
  background: transparent !important;
  color: var(--ink);
}

.parent-calendar .fc .fc-list-event.parent-lesson-event {
  background: transparent !important;
}

.parent-calendar .fc .fc-list-event td {
  background: #fff !important;
  color: var(--ink);
}

.parent-calendar .fc .fc-list-event .fc-list-event-dot {
  border-color: #93c5fd;
}

.parent-calendar .fc .fc-list-event .fc-list-event-time,
.parent-calendar .fc .fc-list-event .fc-list-event-title {
  color: var(--ink);
}

.parent-calendar .fc .fc-list-event:hover td {
  background: #f8fbff !important;
}

.parent-calendar .fc .lesson-selected td {
  background: #eff6ff !important;
}

.parent-calendar .fc .lesson-selected .fc-list-event-time {
  box-shadow: inset 3px 0 0 var(--blue);
}

.list {
  display: grid;
  gap: 10px;
}

.lesson-row {
  display: grid;
  grid-template-columns: 140px 80px 1fr auto;
  gap: 10px;
  align-items: center;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}

.lesson-row:last-child {
  border-bottom: 0;
}

.notification-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 12px;
  align-items: start;
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}

.notification-row:last-child {
  border-bottom: 0;
}

.notification-main {
  display: grid;
  gap: 3px;
  min-width: 0;
}

.notification-msg {
  color: var(--ink);
  font-size: 14px;
  line-height: 1.4;
}

.notification-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  justify-content: flex-end;
}

.notification-channel {
  color: var(--muted);
  font-size: 12px;
}

.ledger-row {
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  gap: 14px;
  align-items: center;
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}

.ledger-row:last-child {
  border-bottom: 0;
}

/* Ledger text had no explicit size, so it fell to the 16px default and looked oversized
   against the app's prevalent 13px secondary text — especially once each row stacks full
   width on mobile. Bring it onto the shared scale: name 15px, value 14px (the nested
   .field-label keeps its own 13px since it's not a direct child). */
.ledger-row > strong {
  font-size: 15px;
}

.ledger-row > span {
  font-size: 14px;
}

/* Generic label/value fields — reused across drawer, settings, ledger */
.field-grid {
  display: grid;
  gap: 12px;
}

.field {
  display: grid;
  grid-template-columns: 7em 1fr;
  gap: 12px;
  align-items: baseline;
}

.field-label {
  color: var(--muted);
  font-size: 13px;
}

.field-value.empty {
  color: var(--muted);
}

/* ---- Settings page: scoped type scale + editable rule rows ----
   Scoped under .settings-view so the tightened title/subtitle sizes don't change
   the shared .section-head used on every other page. */
.settings-view {
  display: grid;
  gap: 18px;
}

.settings-view .section-head h2 {
  font-size: 18px;
  font-weight: 600;
}

.settings-view .section-head p {
  font-size: 13px;
}

.setting-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 16px;
  padding: 12px 0;
  border-top: 1px solid var(--line);
}

.setting-row:first-child {
  border-top: 0;
  padding-top: 4px;
}

.setting-label {
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
}

.setting-hint {
  font-size: 13px;
  color: var(--muted);
  margin: 2px 0 0;
}

.setting-control {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--ink);
}

/* The global `input/select { width: 100% }` rule would blow these out across the row;
   constrain them to their content so label and control sit on one tidy line. */
.setting-control input[type="number"] {
  width: 5.5em;
  text-align: right;
  padding: 8px 10px;
  font-size: 14px;
}

.setting-control select {
  width: auto;
  padding: 8px 10px;
  font-size: 14px;
}

.setting-suffix {
  color: var(--muted);
  font-size: 14px;
}

/* Clickable entity rows (e.g. family list) */
.entity-list {
  display: grid;
  gap: 8px;
}

/* A family row is a wrapper: a big clickable area that opens the drawer, plus a small share
   button on the right. The wrapper carries the border; the two children fill it. */
.entity-row {
  display: flex;
  align-items: stretch;
  width: 100%;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #fff;
  overflow: hidden;
}

.entity-row:hover {
  border-color: #bcd0ea;
}

.entity-row-open {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 12px;
  align-items: center;
  text-align: left;
  padding: 12px 14px;
  border: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}

.entity-row-open:hover {
  background: #f8fbff;
}

.entity-row-share {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  padding: 0 14px;
  border: 0;
  border-left: 1px solid var(--line);
  background: transparent;
  color: var(--muted);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}

.entity-row-share:hover {
  background: #f8fbff;
  color: var(--blue);
}

/* portal-share carries a top divider to separate it from the form in the drawer; in the share
   sheet and the success screen it leads, so drop the stray top line/padding there. */
.share-sheet .portal-share,
.form-success .portal-share {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

.success-share-intro {
  margin-bottom: 6px;
}

.entity-row-main {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.entity-row-main strong {
  font-weight: 600;
}

.entity-row-sub {
  color: var(--muted);
  font-size: 13px;
}

.entity-row-chevron {
  color: #94a3b8;
  font-size: 18px;
  line-height: 1;
}

.entity-row-contacts {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  font-size: 13px;
}

.contact-chip {
  color: var(--ink);
}

.contact-role {
  color: var(--muted);
  margin-right: 4px;
}

/* Parent access-link list (dashboard) */
/* Parent-entry share block in the family drawer: web link + Telegram deep link, each with a
   one-tap copy button. */
.portal-share {
  margin-top: 10px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
  display: grid;
  gap: 10px;
}

.portal-share-head {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}

.portal-share-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 10px;
  align-items: center;
}

.portal-share-info {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.portal-share-label {
  font-size: 12px;
  color: var(--muted);
}

.portal-share-info a {
  font-size: 13px;
  word-break: break-all;
}

.portal-share-hint {
  margin: 2px 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}

.portal-share-reset {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--line);
}

.portal-share-reset button {
  color: var(--rose);
  border-color: var(--rose);
  flex: 0 0 auto;
}

.portal-share-reset-hint {
  flex: 1 1 140px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}

.link-button {
  background: transparent;
  color: var(--blue);
  border: 0;
  padding: 10px 0 0;
  min-height: 0;
  font-size: 14px;
  cursor: pointer;
}

.link-button:hover {
  background: transparent;
  color: var(--blue-dark);
  text-decoration: underline;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  border-radius: 999px;
  background: #eef2ff;
  color: #3730a3;
  font-size: 12px;
  white-space: nowrap;
}

.pill.green {
  background: #dcfce7;
  color: #166534;
}

.pill.amber {
  background: #fef3c7;
  color: #92400e;
}

.pill.rose {
  background: #ffe4e6;
  color: #9f1239;
}

.request-card,
.option-card {
  padding: 14px;
}

.request-list {
  display: grid;
  gap: 8px;
}

/* Inbox drill-in: the "← 收件箱" back button only exists on mobile (shown via the media
   query below). On wide screens the two panes sit side by side, so it stays hidden. */
.back-to-inbox {
  display: none;
  width: auto;
  align-self: flex-start;
  margin-bottom: 12px;
  background: transparent;
  color: var(--blue);
  border: 1px solid var(--line);
}

.request-card {
  box-shadow: none;
  cursor: pointer;
  display: grid;
  gap: 6px;
}

.request-card.selected {
  border-color: var(--blue);
  background: #f8fbff;
  box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.12);
}

.request-card.request-card-highlight {
  animation: requestPulse 1.2s ease-in-out 2;
}

@keyframes requestPulse {
  0%,
  100% {
    box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.12);
  }
  50% {
    box-shadow: 0 0 0 5px rgba(37, 99, 235, 0.22);
  }
}

.request-line {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.request-card span:not(.pill) {
  color: var(--muted);
  font-size: 13px;
}

.request-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 8px 0;
}

.assistant-thread {
  display: grid;
  gap: 14px;
}

.bubble {
  display: grid;
  gap: 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 14px;
}

.bubble h3 {
  margin: 0;
  font-size: 20px;
  line-height: 1.25;
}

.bubble p {
  margin: 0;
  line-height: 1.55;
}

.bubble-kicker {
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.bubble-request {
  background: #f8fafc;
  border-color: #cbd5e1;
}

.bubble-agent {
  background: #f8fbff;
  border-color: #93c5fd;
  box-shadow: 0 10px 24px rgba(37, 99, 235, 0.08);
}

.bubble-draft {
  background: #fffdf7;
  border-color: #f7c96b;
}

.bubble-draft.empty,
.bubble-draft.compact {
  background: #f8fafc;
  border-color: var(--line);
  box-shadow: none;
}

.bubble-result {
  background: #ecfdf5;
  border-color: #bbf7d0;
}

.bubble-result h3 {
  color: #166534;
}

.process-log {
  border-top: 1px solid #bbf7d0;
  padding-top: 10px;
}

.process-log summary {
  cursor: pointer;
  font-weight: 800;
  color: #166534;
}

.process-log ol {
  margin: 8px 0 0;
  padding-left: 20px;
  color: #334155;
  line-height: 1.6;
}

.reply-draft h3 {
  color: var(--blue);
  font-size: 22px;
}

.reply-draft .draft-box {
  margin-top: 2px;
}

.thread-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.option-card pre,
.draft-box {
  white-space: pre-wrap;
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 12px;
  color: #243044;
  overflow: auto;
}

.other-options {
  border-top: 1px solid #dbeafe;
  padding-top: 8px;
}

.other-options summary {
  cursor: pointer;
  color: var(--blue);
  font-weight: 700;
}

.other-option-list {
  display: grid;
  gap: 10px;
  margin-top: 10px;
}

.option-collapsed {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 12px;
  align-items: start;
  padding: 10px 0;
  border-top: 1px solid #e2e8f0;
}

.option-collapsed:first-child {
  border-top: 0;
}

.option-collapsed strong,
.option-collapsed span {
  display: block;
}

.option-collapsed span {
  color: var(--muted);
  font-size: 13px;
  line-height: 1.45;
  margin-top: 3px;
}

.move-diagram {
  display: flex;
  gap: 10px;
  align-items: stretch;
  background: #ffffff;
  border: 1px solid #dbeafe;
  border-radius: 8px;
  padding: 10px;
}

.swap-person,
.result-card {
  flex: 1;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 10px;
  background: #f8fafc;
  display: grid;
  gap: 6px;
  min-width: 0;
}

.swap-person.counterpart,
.result-card.counterpart {
  border-color: #93c5fd;
  background: #eff6ff;
}

.swap-person span,
.result-card span {
  color: var(--muted);
  font-size: 12px;
  line-height: 1.35;
}

.swap-mark {
  align-self: center;
  color: var(--blue);
  font-weight: 800;
  white-space: nowrap;
}

.stepper {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px;
}

.stepper span {
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--muted);
  font-size: 12px;
  padding: 5px 8px;
  text-align: center;
}

.stepper span.done {
  background: #dcfce7;
  border-color: #bbf7d0;
  color: #166534;
}

.next-question {
  display: grid;
  gap: 8px;
  padding: 10px;
  border-radius: 8px;
  background: #fff7ed;
}

.actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.login {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 24px;
}

.login form {
  width: min(420px, 100%);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 24px;
  box-shadow: var(--shadow);
}

.login h1 {
  margin-top: 0;
}

.login .notice {
  background: #ecfdf5;
  border: 1px solid #a7f3d0;
  color: #065f46;
  border-radius: 6px;
  padding: 8px 10px;
  margin-bottom: 12px;
  font-size: 13px;
}

.password-form {
  display: grid;
  gap: 4px;
  max-width: 420px;
}

.portal {
  min-height: 100vh;
  background: #f8fafc;
}

.portal-header {
  background: #ffffff;
  border-bottom: 1px solid var(--line);
  padding: 18px;
}

.portal-header-inner,
.portal-main {
  max-width: 720px;
  margin: 0 auto;
}

.portal-main {
  padding: 18px;
  display: grid;
  gap: 14px;
}

.portal-panel {
  padding: 16px;
  box-shadow: none;
}

.portal-panel h2 {
  margin: 0 0 10px;
  font-size: 20px;
}

.form-grid {
  display: grid;
  gap: 12px;
}

.parent-exact-fields,
.parent-window-fields {
  display: grid;
  gap: 12px;
}

.parent-exact-fields[hidden],
.parent-window-fields[hidden] {
  display: none !important;
}

.parent-action-choice {
  margin: 2px 0;
}

.window-options {
  display: grid;
  gap: 10px;
}

.window-option {
  display: grid;
  grid-template-columns: minmax(138px, 1fr) minmax(190px, 1.25fr);
  gap: 8px 10px;
  align-items: end;
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #f8fafc;
}

.window-option-head {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-height: 28px;
}

.window-option-head strong {
  font-size: 13px;
}

.window-remove {
  min-height: 28px;
  padding: 3px 8px;
  font-size: 12px;
}

.window-date-field,
.window-time-pair label {
  display: grid;
  gap: 4px;
}

.window-date-field span,
.window-time-pair span {
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
}

.window-time-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

.add-window-option {
  justify-self: start;
}

.request-slot-summary {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 10px;
  margin-top: 4px;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.35;
}

.request-slot-summary div {
  display: grid;
  gap: 3px;
  min-width: 0;
}

.request-slot-summary strong {
  color: var(--ink);
  font-size: 13px;
}

.availability-active {
  align-items: center;
  display: flex !important;
  gap: 8px !important;
  min-height: 40px;
}

.availability-active input {
  width: auto;
}

.drawer-backdrop {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  justify-content: flex-end;
  background: rgba(15, 23, 42, 0.38);
}

.drawer-panel {
  width: min(620px, calc(100vw - 72px));
  height: 100vh;
  background: var(--paper);
  border-left: 1px solid var(--line);
  box-shadow: -18px 0 36px rgba(15, 23, 42, 0.14);
  display: grid;
  grid-template-rows: auto 1fr auto;
}

.drawer-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 24px 28px 18px;
  border-bottom: 1px solid var(--line);
}

.drawer-header h2 {
  margin: 0;
  font-size: 28px;
  line-height: 1.15;
}

.drawer-body {
  overflow: auto;
  padding: 22px 28px 96px;
}

.drawer-section {
  display: grid;
  gap: 14px;
  padding-bottom: 22px;
}

.drawer-section + .drawer-section {
  border-top: 1px solid #eef2f7;
  padding-top: 20px;
}

.drawer-section h3 {
  margin: 0;
  font-size: 18px;
  line-height: 1.3;
}

.icon-button {
  width: 36px;
  height: 36px;
  min-height: 36px;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  padding: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  border: 1px solid transparent;
  font-size: 24px;
  line-height: 1;
}

.icon-button:hover {
  background: #eef2f7;
  color: var(--ink);
}

.availability-editor-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
  align-items: end;
}

.availability-editor-grid label {
  display: grid;
  gap: 6px;
}

.availability-editor-grid label span {
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
}

.availability-wide-field {
  grid-column: 1 / -1;
}

.choice-list {
  display: grid;
  gap: 12px;
}

.choice-item {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 12px;
  align-items: start;
  color: var(--ink);
}

.choice-item input {
  width: 20px;
  height: 20px;
  margin-top: 2px;
  accent-color: var(--blue);
}

.choice-item span {
  display: grid;
  gap: 3px;
}

.choice-item strong {
  font-weight: 500;
}

.choice-item small {
  color: var(--muted);
  line-height: 1.4;
}

.choice-item.disabled {
  color: var(--muted);
}

.recurrence-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
  align-items: end;
  margin-top: 2px;
}

.recurrence-fields label {
  display: grid;
  gap: 6px;
}

.recurrence-fields label span {
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
}

.drawer-footer {
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  gap: 10px;
  align-items: center;
  padding: 16px 28px;
  border-top: 1px solid var(--line);
  background: var(--paper);
}

.status-line {
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}

.status-line:last-child {
  border-bottom: 0;
}

.error {
  background: #fff1f2;
  color: #9f1239;
  border: 1px solid #fecdd3;
  border-radius: 8px;
  padding: 10px;
  margin: 10px 0;
}

.success {
  background: #ecfdf5;
  color: #166534;
  border: 1px solid #bbf7d0;
  border-radius: 8px;
  padding: 10px;
  margin: 10px 0;
}

@media (max-width: 920px) {
  .teacher-shell {
    grid-template-columns: 1fr;
  }

  /* Inbox drill-in: show the list OR the detail, never both stacked. */
  .requests-pane.has-selection .request-list-section {
    display: none;
  }

  .requests-pane:not(.has-selection) #request-detail {
    display: none;
  }

  .requests-pane.has-selection .back-to-inbox {
    display: inline-flex;
  }

  .sidebar {
    position: static;
    height: auto;
    border-right: 0;
    border-bottom: 1px solid var(--line);
    /* Grid item: without min-width:0 it refuses to shrink below the nav's intrinsic width
       (~700px for the 6 buttons), stretching the whole shell past the phone and overflowing
       every page. min-width:0 lets it shrink to the viewport so the nav scrolls internally. */
    min-width: 0;
    /* Phone layout: brand + 退出 share the first row, the nav wraps to a full-width second
       row. order pins the sequence (DOM order is brand → nav → logout), flex-wrap lets the
       nav drop below. */
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
  }

  .brand {
    order: 1;
    padding-bottom: 8px;
  }

  .sidebar-logout {
    order: 2;
    /* Override the desktop bottom-pin and push 退出 to the right edge of the brand row. */
    margin-top: 0;
    margin-left: auto;
  }

  .nav {
    order: 3;
    flex-basis: 100%;
    display: flex;
    overflow-x: auto;
    gap: 6px;
    /* Required for overflow-x:auto to actually engage: a flex container defaults to
       min-width:auto (= content width), so the scroll never kicked in and the buttons'
       combined width was forced onto the parent instead. */
    min-width: 0;
  }

  .nav button {
    min-width: max-content;
  }

  .stats,
  .grid-2,
  .grid-2-wide-left {
    grid-template-columns: 1fr;
  }

  .main {
    padding: 16px;
  }
}

@media (max-width: 620px) {
  /* Mobile readability nudge for ①: the app's secondary text is mostly fixed at 13px, which
     reads cramped on a phone. Bump the content-carrying 13px tiers to 14px on small screens
     only (desktop unchanged). Sizes are explicit px (not rem) across the app, so this has to
     be done per-class rather than via a single base bump. */
  .setting-hint,
  .entity-row-sub,
  .entity-row-contacts,
  .stat-sub {
    font-size: 14px;
  }

  .toast-stack {
    top: auto;
    right: 12px;
    bottom: 12px;
    width: calc(100vw - 24px);
  }

  .toast-exit {
    transform: translateY(6px);
  }

  .section-head {
    align-items: flex-start;
    flex-direction: column;
  }

  /* Keep 退出 compact in the brand row: the mobile `button{width:100%}` rule below would
     otherwise blow it up to full width and break the row. */
  .sidebar-logout {
    width: auto;
  }

  .lesson-row,
  .ledger-row,
  .option-collapsed {
    grid-template-columns: 1fr;
  }

  .window-option {
    grid-template-columns: 1fr;
  }

  .window-time-pair {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }

  .request-slot-summary {
    grid-template-columns: 1fr;
  }

  .move-diagram {
    flex-direction: column;
  }

  .swap-mark {
    align-self: flex-start;
  }

  .stepper {
    grid-template-columns: 1fr 1fr;
  }

  button {
    width: 100%;
  }

  .fc button {
    width: auto;
  }

  /* The mobile `button { width: 100% }` rule above also hits the two buttons inside an
     .entity-row. The share (🔗) button is flex:0 0 auto (never shrinks), so at 100% width it
     hogs the whole row and crushes the .entity-row-open name column to one char per line.
     Let flex sizing drive both buttons instead. */
  .entity-row-open,
  .entity-row-share {
    width: auto;
  }

  .fc .fc-toolbar {
    align-items: flex-start;
    flex-direction: column;
  }

  .availability-editor-grid {
    grid-template-columns: 1fr;
  }

  /* ③: on mobile, present the side drawer as a bottom sheet — the platform-standard place
     for a contextual panel on a phone, instead of a right-anchored full-height drawer. */
  .drawer-backdrop {
    align-items: flex-end;
  }

  .drawer-panel {
    width: 100%;
    height: auto;
    max-height: 88vh;
    border-left: 0;
    border-radius: 16px 16px 0 0;
  }

  .drawer-header,
  .drawer-body,
  .drawer-footer {
    padding-left: 18px;
    padding-right: 18px;
  }

  .drawer-footer {
    grid-template-columns: 1fr 1fr;
  }

  .drawer-footer .danger,
  .drawer-footer span {
    grid-column: 1 / -1;
  }

  .recurrence-fields {
    grid-template-columns: 1fr;
  }

  .icon-button,
  .fc button {
    width: auto;
  }
}

/* --- Roster entry & management (backlog #2) ---------------------------------- */
button.small {
  min-height: 32px;
  padding: 6px 12px;
  font-size: 14px;
}

.section-head-inline {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.section-head-inline h3 {
  margin: 0;
  font-size: 18px;
}

/* Editable contact form inside the family drawer + dialog field stacks */
.roster-form,
.student-screen,
.roster-dialog .drawer-body,
.slot-grid {
  display: grid;
  gap: 12px;
}

.roster-form label,
.student-screen > label,
.slot-grid label,
.roster-field {
  display: grid;
  gap: 5px;
}

.roster-form label > span,
.student-screen > label > span,
.slot-grid label > span,
.roster-field > span {
  font-size: 13px;
  color: var(--muted);
}

.roster-form-actions {
  justify-self: start;
}

.family-link-row {
  margin: 4px 0 0;
}

.slot-grid {
  grid-template-columns: 1fr 1fr;
}

/* Student blocks: a header (name + rate + edit/退课) over a list of fixed weekly times */
.student-row-list {
  display: grid;
  gap: 12px;
}

.student-block {
  display: grid;
  gap: 10px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #fff;
}

.student-block-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.student-row-inactive {
  opacity: 0.6;
}

.student-row-main {
  display: grid;
  gap: 3px;
  min-width: 0;
}

.student-row-main strong {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.student-row-meta {
  font-size: 13px;
  color: var(--muted);
}

.student-row-actions {
  display: flex;
  gap: 6px;
  flex-shrink: 0;
}

.slot-row-list {
  display: grid;
  gap: 6px;
  padding-left: 4px;
  border-left: 2px solid #eef2f7;
}

.slot-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 4px 0 4px 10px;
}

.slot-row-text {
  font-size: 14px;
}

.slot-row-actions {
  display: flex;
  gap: 6px;
  flex-shrink: 0;
}

.small-muted {
  font-size: 13px;
  margin: 2px 0 2px 10px;
}

.slot-add-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 2px;
}

.slot-hint {
  font-size: 12px;
  margin: 8px 0 0;
}

.rate-field {
  display: grid;
  gap: 5px;
  margin-top: 12px;
}

.rate-field > span {
  font-size: 13px;
  color: var(--muted);
}

/* Input with a trailing unit (e.g. rate · SGD) */
.input-suffix-wrap {
  display: flex;
  align-items: stretch;
}

.input-suffix-wrap input {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

.input-suffix {
  display: inline-flex;
  align-items: center;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-left: 0;
  border-radius: 0 8px 8px 0;
  background: #f8fafc;
  color: var(--muted);
  font-size: 13px;
  white-space: nowrap;
}

/* Phone: country-code select + national number */
.phone-field {
  display: grid;
  gap: 5px;
}

.phone-field > span {
  font-size: 13px;
  color: var(--muted);
}

.phone-input {
  display: flex;
  gap: 8px;
}

.phone-input select {
  width: auto;
  flex: 0 0 auto;
}

.phone-input input {
  flex: 1;
  min-width: 0;
}

/* Inline student lifecycle status picker */
.status-select {
  width: auto;
  min-height: 32px;
  padding: 4px 26px 4px 8px;
  font-size: 13px;
}

/* Roster list: sort control + collapsed 已离开 section */
.section-head-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

.sort-control {
  display: flex;
  align-items: center;
  gap: 6px;
}

.sort-label {
  font-size: 13px;
  color: var(--muted);
  white-space: nowrap;
}

.sort-select {
  width: auto;
}

/* Filter-as-you-type box above the 学生资料 list. Full-width row of its own so it stays
   usable on mobile instead of fighting the sort + 添加学生 controls for space. */
.list-search {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
}

.list-search-input {
  flex: 1;
}

.list-search-count {
  font-size: 13px;
  color: var(--muted);
  white-space: nowrap;
}

.list-search-empty {
  padding: 6px 2px;
}

.entity-row-archived {
  opacity: 0.65;
}

.archived-section {
  margin-top: 14px;
  border-top: 1px solid var(--line);
  padding-top: 10px;
}

.archived-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: auto;
  padding: 6px 2px;
  background: transparent;
  color: var(--muted);
  border: 0;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}

.archived-toggle:hover {
  background: transparent;
  color: var(--ink);
}

.archived-caret {
  font-size: 12px;
}

/* When there are no 非在读 students, show a static muted "非在读（0）" instead of hiding
   the section entirely — keeps the category visible and clear. Matches .archived-toggle's
   look but isn't clickable (nothing to expand). */
.archived-empty {
  display: inline-block;
  padding: 6px 2px;
  color: var(--muted);
  font-size: 14px;
  font-weight: 600;
}

.archived-list {
  margin-top: 8px;
}

/* Body-mounted overlays (add student, edit student, change slot) */
/* Body-mounted roster overlays are compact, centered, content-height modals (NOT the
   full-height side drawer). Override .drawer-panel's height:100vh so the few fields don't
   get stretched to fill the viewport. */
.roster-overlay-backdrop {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  overflow: auto;
  background: rgba(15, 23, 42, 0.38);
}

.roster-dialog,
.student-form {
  height: auto;
  max-height: calc(100vh - 40px);
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: 0 24px 60px rgba(15, 23, 42, 0.24);
}

.roster-dialog {
  width: min(460px, calc(100vw - 32px));
}

.student-form {
  width: min(560px, calc(100vw - 32px));
  grid-template-rows: auto auto 1fr auto;
}

/* Compact modal bodies: scroll within max-height, drop the tall drawer padding. */
.roster-dialog .drawer-body,
.student-form .drawer-body {
  align-content: start;
  min-height: 0;
  overflow: auto;
  padding: 20px 24px;
}

.form-progress {
  display: flex;
  gap: 10px;
  padding: 12px 24px;
  border-bottom: 1px solid var(--line);
  background: #f8fafc;
}

.step-dot {
  font-size: 13px;
  color: var(--muted);
  font-weight: 600;
}

.step-dot.active {
  color: var(--blue);
}

.student-form .drawer-footer,
.roster-dialog .drawer-footer {
  display: flex;
  gap: 10px;
  align-items: center;
}

.footer-spacer {
  flex: 1;
}

.form-success {
  display: grid;
  gap: 8px;
  text-align: center;
  padding: 24px 8px;
}

.success-title {
  font-size: 22px;
  font-weight: 700;
  color: var(--green, #059669);
  margin: 0;
}

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

  .student-row {
    flex-direction: column;
    align-items: stretch;
  }

  .student-row-actions {
    justify-content: flex-end;
  }
}

/* PDPA collection notice in the parent portal footer */
.portal-footer {
  margin-top: 8px;
  padding: 4px 2px 24px;
}
.privacy-notice {
  font-size: 13px;
  color: var(--muted, #6b7280);
}
.privacy-notice > summary {
  cursor: pointer;
  user-select: none;
  padding: 6px 0;
}
.privacy-notice .privacy-body {
  padding: 4px 2px 0;
  line-height: 1.6;
}
.privacy-notice .privacy-body p {
  margin: 6px 0;
}

/* 乐理小班 (small-group classes) */
.group-class-row {
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #fff;
  padding: 12px 14px;
  display: grid;
  gap: 8px;
}
.group-class-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.group-class-head .footer-spacer {
  flex: 1;
}
.group-class-members {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.group-member-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg, #f6f7f9);
  font-size: 13px;
  /* Keep each chip whole: don't let the flex-wrap row shrink a chip below its content,
     which was wrapping Chinese names one character per line (林安/安·40). */
  white-space: nowrap;
  flex-shrink: 0;
}
.group-member-chip .chip-x {
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  color: var(--muted, #888);
  padding: 0 2px;
}
.group-member-chip .chip-x:hover {
  color: var(--rose, #c0392b);
}
.group-member-pick {
  display: grid;
  gap: 6px;
  /* min-height keeps the picker from collapsing to a sliver inside the height-constrained
     dialog body on mobile; max-height keeps it from pushing the rest of the form off-screen
     (it scrolls internally past that). */
  min-height: 150px;
  max-height: 240px;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 8px;
  margin: 8px 0;
}
.group-member-row {
  display: grid;
  grid-template-columns: auto 1fr 90px;
  align-items: center;
  gap: 10px;
}
.group-member-row .group-member-name {
  display: flex;
  flex-direction: column;
}
.group-member-row .group-member-rate {
  width: 90px;
}
