/* teacher.css — page-specific styles for teacher.html */

/* ── Auth overlay ────────────────────────────────── */
#teacher-auth-overlay {
  display: flex;
  flex-direction: column;   /* t-271：卡下面还有一行回岔口的链接 */
  position: fixed;
  inset: 0;
  background: var(--bg);
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 24px;
}
/* t-271 / S-029 §2：与 /join 的 .join-cross 同一身（flex 居中 / 44 / padding 0 12 / 13 / 600 / 无下划线 / margin-top 14），
   只有色值随这一屏的浅底取 --primary（压 --bg 7.34） */
.teacher-cross {
  display: flex; align-items: center; justify-content: center;
  min-height: 44px; min-width: 44px; padding: 0 12px; margin-top: 14px;
  font-size: 13px; font-weight: 600; color: var(--primary); text-decoration: none;
}
.teacher-cross:hover { color: var(--primary-dark); }
.teacher-auth-card {
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: 0 4px 24px rgba(0,0,0,0.10);
  padding: 36px 28px 32px;
  width: 100%; max-width: 360px;
}
.teacher-auth-brand {
  font-size: 24px; font-weight: 800; color: var(--primary);
  margin-bottom: 28px; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
}
.teacher-auth-brand span:last-child {
  font-size: 13px; font-weight: 400; color: var(--text-secondary);
}
#teacher-auth-overlay input {
  width: 100%; padding: 12px 14px;
  border: 1.5px solid var(--border); border-radius: 10px;
  font-size: 15px; color: var(--text); background: #fff;
  outline: none; margin-bottom: 10px; -webkit-appearance: none;
}
#teacher-auth-overlay input:focus { border-color: var(--primary); }
#t-login-btn {
  width: 100%; padding: 13px;
  background: var(--primary); color: #fff;
  /* UX-006 ② 点名的那一颗：全站唯一的方角主按钮（实测 10px） */
  border: none; border-radius: var(--radius-btn);
  font-size: 15px; font-weight: 600; cursor: pointer; margin-top: 2px;
}
#t-login-btn:hover { background: #22371F; }
#t-login-btn:disabled { opacity: 0.55; cursor: not-allowed; }
#t-login-error {
  margin-top: 10px; font-size: 13px; color: var(--danger);
  text-align: center; min-height: 18px;
}
.teacher-account {
  display: flex; align-items: center; gap: 10px; margin-left: auto;
}
.teacher-logout-btn {
  font-size: 12px; padding: 4px 12px;
  background: rgba(255,255,255,0.15); border: none;
  color: white; border-radius: 999px; cursor: pointer;
  min-height: 44px;   /* UX-004：22px */
}
/* 改密码是次要操作 —— 文字链，不与「登出」抢视觉份量 */
.teacher-pw-link {
  /* UX-004：22px。顶栏高度不能跟着长，所以用负外边距把版面收回去。 */
  display: inline-flex; align-items: center; min-height: 44px;
  font-size: 12px; padding: 0 8px; margin: -11px -6px;
  background: none; border: none; cursor: pointer;
  /* UIV-12 把页头的次级字换成画板那一枚 --on-dark-muted（压 --primary-deep 6.06）——
     design 04:37Z 的「同一张页头只许有一种次级字」照旧：校名和这颗一起换，不留两个白。 */
  color: var(--on-dark-muted); text-decoration: underline;
  text-underline-offset: 2px;
}
.teacher-pw-link:hover { color: #fff; }
.teacher-logout-btn:hover { background: rgba(255,255,255,0.25); }

/* ── Header ──────────────────────────────────────── */
.teacher-header {
  /* UIV-12：头部底色和另外四页一样用 --primary-deep（这一页原来浅一号，是全站唯一例外）；
     内边距也回到四页共用的 18 / 20 / 20。 */
  background: var(--primary-deep);
  color: white;
  padding: 18px 20px 20px;
  display: block;
}
/* 品牌降一级，页面标题升上来。承担「我在哪一屏」的是 .th-h1，
   品牌只是背景信息 —— 它每一屏都一样，说不出任何东西。 */
.th-strip { display: flex; align-items: center; gap: 8px; width: 100%; }
/* T174 B2 / BUG-034：写死的 #A8C6AD 压 --primary 是 4.36。design 04:37Z 判：用**这张页头
   自己的次级字那个白** —— 与 `.teacher-pw-link`（Change password）同一个 .72，5.10。
   **同一个角色用同一个值**：.75 能多出 0.28，但一张页头就有了两种「次级字」的白。
   **不用 --primary-light**（6.51）：几乎是白，和标题「Today」抢级 —— 品牌是背景信息，要比标题低一级。
   家长端 `.subtitle` 用的是几乎同色的 #A9C4AC，但它压的是更深的 #24402B，6.06，不用改。 */
/* UIV-12：画板 12.5 / 500 / #A9C4AC（= --on-dark-muted，家长端页头次级字同一个）。
   底色换成 --primary-deep 之后，这个值压它是 6.06（T174 B2 当初的 .72 是为了压浅一号的 --primary）。 */
.th-brand { font-size: 12.5px; font-weight: 500; color: var(--on-dark-muted); flex: 1; min-width: 0; }
.th-row { display: flex; align-items: baseline; gap: 10px; width: 100%; margin-top: 7px; }
.th-h1 {
  font-family: var(--font-display); font-size: 22px; font-weight: 800;   /* UIV-12：画板 22（原 26） */
  margin: 0; flex: 1; min-width: 0; letter-spacing: -0.2px;
}
.th-meta { font-size: 12.5px; font-weight: 700; color: #BFD6C4; white-space: nowrap; }
.role-pill {
  display: inline-block;
  padding: 3px 10px; border-radius: 999px;   /* UIV-12：画板是全圆角胶囊（原 6） */
  font-size: 11px; font-weight: 700;
  letter-spacing: 0.5px;
}
/* UIV-12：画板要的是叶绿底 #DCEBD9 + 深绿字（原来写死琥珀 #FFE7B5 / #7C4A03，全系列第三种互不相同的写死琥珀）。
   字用 --primary-deep（画板那一枚）而不是 --tag-success-fg —— 两者差一档，qa 按画板值逐值比。 */
.role-pill.head { background: var(--tag-success-bg); color: var(--primary-deep); }
.role-pill.assistant { background: #DDE9F8; color: #1E3A8A; }
.role-switch-btn {
  margin-top: 8px;
  font-size: 11px; color: rgba(255,255,255,0.7); width: 100%;
  cursor: pointer; text-decoration: underline;
  background: none; border: none; padding: 0; text-align: left;
}
.role-switch-btn:hover { color: white; }

/* RT-10411: assistant hides head-only elements entirely */
body.role-assistant .head-only { display: none !important; }
/* 助教看得见状态、改不了权限。**View 那一颗拿掉之后这里不再需要 :not()** ——
   留着一个指向已不存在的类的例外，下一个人会以为那一格还在。 */

/* ── Content ─────────────────────────────────────── */
/* 内边距归 .tab-page 管。两边都设的话是叠加，不是取大的那个。
   底部那一段留给悬浮导航 —— shared.css 的 .tab-page 已经算进 --nav-height。 */
.content { padding: 0; }
.tab-page { padding: 20px 20px calc(var(--nav-height) + 24px + env(safe-area-inset-bottom, 0px)); }
@supports (padding: env(safe-area-inset-left)) {
  .tab-page {
    padding-left: calc(20px + env(safe-area-inset-left));
    padding-right: calc(20px + env(safe-area-inset-right));
  }
}

/* ── Section headings ────────────────────────────── */
/* S-020 §1（UIV-12 §3①）：老师端小节标题换成画板那一种 —— Bricolage 16 / 700 / --text，不大写、不加字距。
   老师端 9 处一起变（这是体系不是逐页改）；家长端 shared.css 里的 .section-title 不动，两块画板本来就不一样。 */
.section-title {
  font-family: var(--font-display);
  font-size: 16px; font-weight: 700; text-transform: none;
  letter-spacing: normal; color: var(--text);
  margin: 24px 0 10px;
}
.section-title:first-child { margin-top: 0; }
.section-title-row {
  display: flex; align-items: center; justify-content: space-between;
  margin: 24px 0 12px;
}
.section-title-row .section-title { margin: 0; }
.digest-settings-toggle {
  /* T174 B1 / BUG-034：它站在 `.priority-section` 的 --primary-light 底上，不是页面底 ——
     --text-secondary 在那儿是 4.17。换 --primary：6.51；和旁边那行标题同一个绿，
     12px/400 比标题的 13px 低一级；绿字也顺带回答了 UX-004「它看不出能点」。
     --text（11.21）会比标题还重，不用。 */
  /* S-020 §1：浅绿条撤了，它压的是页底 —— 灰字 4.70 过线（T174 B1 当初换绿是为了压那条浅绿底）。命中区照旧。 */
  background: none; border: none; font-size: 12px; color: var(--text-secondary);
  cursor: pointer;
  /* UX-004：实测 14px —— 灰字、无下划线、无边框，**它甚至看不出能点**。
     内边距撑开 + 负外边距收回：看得见的没变，接得住手指的那一块变大（做法照 T066）。
     T174 B3：命中块底边离下面「📅 Calendar」只有 5px。**其余目标不许动**，所以只挪
     它自己的命中块：上多伸 3px、下少伸 3px（-14 / -8），高仍是 44；padding-top 6px
     把字按回原位（border-box：内容区 38 高、居中在 6+19=25，= 原来的 -11+22 同一行）。 */
  display: inline-flex; align-items: center;
  min-height: 44px; padding: 6px 8px 0; margin: -14px -8px -8px;
}

/* ── Card ────────────────────────────────────────── */
/* RT-10443: card hierarchy
   - base cards (次级) get a subtle outer-shadow + 1px ring so adjacent cards
     read as separate stacked tiles even with only 2px breathing room
   - .card + .card top-margin keeps the 2px+shadow separator between siblings
   - .priority cards (首屏重点: 待审批 / 跟踪摘要) get a stronger shadow,
     primary-coloured left rail, and tinted background for hierarchy
*/
.card { background: var(--card); border-radius: var(--radius); padding: 20px; box-shadow: var(--shadow); position: relative; }
.card + .card { margin-top: 2px; }
.card:not(.priority):not(.card-primary) {
  box-shadow: 0 1px 2px rgba(15,23,42,0.05), 0 0 0 1px rgba(15,23,42,0.04);
}
.card-primary {
  border-top: 2px solid var(--primary);
  background: #FBFAF3;
}
.card.priority {
  box-shadow: 0 3px 8px rgba(15,23,42,0.08), 0 0 0 1px rgba(15,23,42,0.05);
  border-left: 3px solid var(--primary);
  padding-left: 17px;
}
.card.priority .card-head-title,
.card.priority .ts-title { font-size: 16px; color: var(--primary); }
/* UIV-13 §3②：举报卡的「REPORTED TO YOU」照画板是中性小标签 —— 标签不抢色，有色的是徽章。
   只在这一张卡上限定掉上面那条（别的 priority 卡跟 S-020 走）。 */
#circle-reported-card .card-head-title {
  font-size: 11.5px; font-weight: 700; letter-spacing: 0.5px; color: var(--text-secondary);
}
.card.priority-pending { background: linear-gradient(180deg, #FFFBEB 0%, var(--card) 65%); }
.card.priority-tracking { background: linear-gradient(180deg, #F2F8F1 0%, var(--card) 65%); }

/* RT-10443: 今日摘要 section-title-row gets tinted accent treatment so the
   首屏重点 area opens with a clearly-marked banner even though the digest
   payload is hidden behind ⚙️ */
/* S-020 §1：浅绿底条撤掉 —— 画板没有这条；标题换成 16 深色 Bricolage 之后它自己就站出来了 */
.section-title-row.priority-section {
  margin: 24px 0 10px;
  padding: 0;
  background: none;
  border-left: 0;
  border-radius: 0;
}
/* S-020 §1：浅绿条撤了，这条也跟着撤 —— 标题跟 .section-title 本体走（16 / --text），不再单独设 */
.section-title-row.priority-section .section-title {
  margin: 0;
}

/* ── Sent banner ─────────────────────────────────── */
.sent-banner {
  display: none; background: #D1FAE5; color: #065F46;
  padding: 14px 16px; border-radius: var(--radius);
  font-size: 14px; font-weight: 600; text-align: center;
  margin-bottom: 16px; animation: slideDown 0.3s ease;
}
.sent-banner.show { display: block; }
@keyframes slideDown { from { opacity:0; transform:translateY(-10px); } to { opacity:1; transform:translateY(0); } }

/* ── Digest settings panel ───────────────────────── */
.digest-settings-panel { margin-bottom: 12px; }
.panel-label { font-size: 13px; font-weight: 600; margin-bottom: 10px; }
.panel-label.mt10 { margin-top: 12px; }
.radio-group { display: flex; flex-direction: column; gap: 8px; }
.radio-group label { display: flex; align-items: center; gap: 10px; font-size: 14px; cursor: pointer; }
.digest-time-sel { border: 1.5px solid var(--border); border-radius: 8px; padding: 8px 12px; font-size: 14px; color: var(--text); background: var(--bg); width: 100%; margin-top: 4px; }
.threshold-settings { margin-top: 14px; padding-top: 12px; border-top: 1px dashed var(--border); }
.threshold-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; font-size: 13px; flex-wrap: wrap; }
.threshold-row .swatch { width: 12px; height: 12px; border-radius: 3px; flex-shrink: 0; }
.threshold-row .swatch.red { background: var(--danger); }
.threshold-row .swatch.yellow { background: #F59E0B; }
.threshold-row label { color: var(--text); flex: 1; min-width: 110px; }
.threshold-row input[type="number"] { width: 64px; border: 1.5px solid var(--border); border-radius: 6px; padding: 4px 6px; font-size: 13px; text-align: right; }
.threshold-row .pct { color: var(--text-secondary); font-size: 12px; }
.threshold-hint { font-size: 11px; color: var(--text-secondary); margin-top: 4px; line-height: 1.5; }
.save-panel-btn { margin-top: 12px; width: 100%; padding: 10px; background: var(--primary); color: white; border: none; border-radius: 10px; font-size: 14px; font-weight: 600; cursor: pointer; font-family: inherit; }

/* ── Card head ───────────────────────────────────── */
.card-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.card-head-left { display: flex; align-items: center; gap: 8px; }
.card-head-title { font-size: 15px; font-weight: 700; }
.pending-badge { background: #FEF3C7; color: #92400E; font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 12px; }
/* UIV-13 §3①：举报数徽章照画板是红底白字的胶囊（原来和「普通待办」共用那枚写死的琥珀，
   「有人举报」的严重程度被拉平了）。只这一颗 —— .pending-badge 别处照旧。 */
#circle-reported-badge { background: var(--danger); color: #fff; font-size: 10.5px; border-radius: 999px; }
.card-head-meta { font-size: 11px; color: var(--text-secondary); }

/* ── 被举报的帖子 ────────────────────────────────
   老师看得到的全部 Circle 内容就是这一块 —— 其余照旧不可见。 */
.card.priority-circle { background: linear-gradient(180deg, #FBEEDD 0%, var(--card) 65%); }
.card-head-title .ch-icon { display: inline-flex; vertical-align: -3px; margin-right: 6px; color: var(--primary); }
.circle-reported-note {
  /* UIV-13：画板 13.5 / 1.55 / --text-body */
  font-size: 13.5px; line-height: 1.55; color: var(--text-body);
  margin: -4px 0 12px;
}
.circle-reported-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
/* UIV-13 §3①（design 21:03Z）：照画板 —— 白卡 + 4px 红左边 + 阴影，靠一条边线说「这条不一样」。
   原来整条是琥珀底 + 细描边（S-018 §3.2，UIV 之前定的，design 已在 S-018 头上注明被本表取代）：
   那是「还没处理的普通事项」同一个语气，跟「有人举报」不是一回事。 */
.circle-reported-item {
  padding: 12px; background: var(--card); border: 0; border-left: 4px solid var(--danger);
  border-radius: var(--radius-card); box-shadow: var(--shadow-md);
}
/* S-018 §3.1：处理完的那一条以前整条 opacity .6 —— 确认字「Taken down」2.22，而那是「你刚才那一下
   成了没有、成了什么」的唯一一句，出现一次、就那一眼（刷新就没了，接口只返回没处理的）。
   **退让用明确的颜色，不用 opacity**（design-spec §8）：琥珀底撤掉 = 「这条不再等你」；
   字退成次级 5.17；结果是一颗中性胶囊 + ✓（与 S-008 已签同一个样子）5.32。 */
/* S-018 §3.1：处理完那一条退成中性（不再等你）—— 白底不变，红左边退成中性描边色 */
.circle-reported-item.resolved { opacity: 1; background: var(--card); border-left-color: var(--border); }
.circle-reported-item.resolved .cr-title,
.circle-reported-item.resolved .cr-body,
.circle-reported-item.resolved .cr-count,
.circle-reported-item.resolved .cr-meta { color: var(--text-secondary); }
.circle-reported-item .cr-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.circle-reported-item .cr-title { font-size: 15px;   /* UIV-13：画板 15 */ font-weight: 700; color: var(--text); flex: 1; min-width: 0; }
.circle-reported-item .cr-count { font-size: 11px; font-weight: 700; color: var(--accent-deep); flex-shrink: 0; }
/* UIV-13：元信息照画板 12 / 700 / --danger（琥珀底没了，S-018 §3.2 那次为压琥珀挑的中性字随之作废） */
.circle-reported-item .cr-meta { font-size: 12px; font-weight: 700; color: var(--danger); margin-top: 2px; }
.circle-reported-item .cr-body { font-size: 13.5px; line-height: 1.5; color: var(--text-body); margin-top: 8px; white-space: pre-wrap; }
.circle-reported-item .cr-actions { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
/* S-018 §3.3：高 36 → 44。这一下是删别人的帖子 */
.circle-reported-item .cr-btn {
  flex: 1; min-height: 44px; padding: 9px 12px; border-radius: 999px; font-family: inherit;
  font-size: 13px; font-weight: 700; cursor: pointer;
  border: 1.5px solid var(--border); background: white; color: var(--text);
}
/* UIV-13：Keep 照画板是透明底、正文色（Remove 那颗已经对了，不动） */
.circle-reported-item .cr-btn.keep { background: transparent; color: var(--text-body); }
.circle-reported-item .cr-btn.remove { background: var(--danger); border-color: var(--danger); color: white; }
.circle-reported-item .cr-btn[disabled] { opacity: 0.5; cursor: default; }
.circle-reported-item .cr-result { font-size: 12.5px; font-weight: 700; color: var(--text-secondary); }
.circle-reported-item.resolved .cr-result {
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;
  background: var(--tag-neutral-bg); color: var(--tag-neutral-fg);
}
.circle-reported-item.resolved .cr-result::before { content: "\2713"; margin-right: 4px; }

/* ── Pending list ────────────────────────────────── */
.pending-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.pending-item {
  display: flex; flex-direction: column;
  padding: 10px 12px; background: #FFFBEB; border: 1px solid #FDE68A; border-radius: 10px; gap: 4px;
}
.pending-row-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.p-name { font-size: 14px; font-weight: 600; flex: 1; min-width: 0; }
.p-sub { font-size: 12px; color: var(--text-secondary); }
.pending-actions { display: flex; gap: 6px; flex-shrink: 0; }
.approve-btn { background: var(--success); color: white; border: none; border-radius: 8px; padding: 6px 11px; font-size: 13px; font-weight: 600; cursor: pointer; }
.deny-btn { background: #FEE2E2; color: #DC2626; border: none; border-radius: 8px; padding: 6px 11px; font-size: 13px; font-weight: 600; cursor: pointer; }

/* ── Tracking list ───────────────────────────────── */
.tracking-summary-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; flex-wrap: wrap; margin-bottom: 10px;
}
.ts-title { font-size: 15px; font-weight: 700; flex: 1 1 auto; min-width: 0; }
.sort-toggle {
  display: inline-flex; align-items: center; gap: 0;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: 6px; padding: 2px; flex-shrink: 0;
}
.sort-label {
  font-size: 10px; color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: 0.4px;
  padding: 0 6px 0 4px;
}
.sort-toggle button {
  background: transparent; border: none; border-radius: 4px;
  padding: 4px 8px; font-size: 11px; font-weight: 600;
  color: var(--text-secondary); cursor: pointer; line-height: 1.3;
  white-space: nowrap;
}
.sort-toggle button.active { background: var(--primary); color: white; }

.tracking-list { display: flex; flex-direction: column; gap: 6px; }
.tracking-item { display: flex; flex-direction: column; padding: 10px; background: var(--bg); border-radius: 8px; gap: 4px; position: relative; transition: border-left-color 0.2s; }
.ti-main {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "info  stats"
    "btn   stats";
  column-gap: 10px;
  row-gap: 4px;
  align-items: center;
}
.ti-info { grid-area: info; min-width: 0; }
.ti-info .ti-title {
  font-size: 13px; font-weight: 600; line-height: 1.3;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ti-info .ti-meta { font-size: 12px; color: var(--text-secondary); margin-top: 1px; }
.send-reminder-btn {
  grid-area: btn;
  display: inline-flex; align-items: center; gap: 4px;
  padding: 5px 9px; font-size: 11px; font-weight: 700;
  background: var(--primary); color: white;
  border: 1px solid var(--primary); border-radius: 6px;
  cursor: pointer; white-space: nowrap;
  justify-self: start;
}
.send-reminder-btn:hover { opacity: 0.9; }
.ti-main .stat-pair.compact {
  grid-area: stats;
  display: flex; flex-direction: column; gap: 2px;
  align-items: flex-end; flex-shrink: 0;
}
.ti-head-actions {
  display: flex; gap: 0;
  justify-content: flex-end;
  margin-top: 2px;
}
/* T126：帖子行上的动作槽。**行本身是一颗打开详情的大按钮**，
   所以这两颗必须坐在它外面、并且有自己的可点区域；不然点「撤回」会打开详情。
   顶栏那一对同理（.cs-action）。 */
/* T139 / T141：整批操作那一行。按钮沿用 .head-action-btn（44×44 已在那条规则里），
   这里只管排布 —— **不碰尺寸**，那正是我上一版在这个位置犯的错。 */
.invite-bulk { display: flex; align-items: center; gap: 4px; flex-wrap: wrap;
  margin: 0 0 6px -8px; }
.invite-picked { margin-left: auto; font-size: 11px; color: var(--text-secondary); }
/* T138：字数与段数。超一段就变色 —— 说的是代价，不是错误，所以不用 danger 色。 */
.invite-note-count { font-size: 11px; color: var(--text-secondary);
  text-align: right; margin-top: 2px; font-variant-numeric: tabular-nums; }
.invite-note-count.is-warn { color: var(--accent-deep); font-weight: 600; }
/* **不覆盖 .head-action-btn 的 44×44**。我第一版在这里写了 `min-height: 32px`，
   两件事一起犯了：① 32 本身就是 UX-004 修过的那个倒退；
   ② `.post-row-actions .head-action-btn {` 里含有 `test_frontend_touch_targets.py`
   锚定的那个串，于是它成了**第一个匹配**，守卫读的是我这条而不是真规则 ——
   **红是真的，但报出来的位置是「老师联系方式 Save」，跟我改的东西毫无关系。**
   收掉视觉臃肿靠负外边距（同 `.sd-back-btn`）：放大的是手指够得着的范围，不是版面。 */
.post-row-actions { display: flex; gap: 8px; padding: 0 0 4px; margin: -6px 0 0 -8px; }
.head-action-btn.danger { color: var(--danger, #A03528); }
.cs-topbar .cs-action {
  background: none; border: none; cursor: pointer; font-family: inherit;
  font-size: 13px; font-weight: 600; color: var(--primary);
  min-height: 44px; padding: 0 8px;      /* 够得着：44 高（同 .sd-back-btn 那一格）*/
}
.cs-topbar .cs-action.is-danger { color: var(--danger, #A03528); }
.cs-topbar .cs-title { flex: 1; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.head-action-btn {
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: none; padding: 6px 8px;
  /* **两个方向都要 44**。只写 min-height 的话，「Save」这种短词按钮
     实测是 45×41.2 —— 高的那一半修过了，宽的那一半从来没有。 */
  min-height: 44px; min-width: 44px;   /* UX-004 */
  font-size: 11px; font-weight: 500;
  color: var(--text-secondary); cursor: pointer;
  border-radius: 4px; letter-spacing: 0.1px;
}
.head-action-btn:hover { color: var(--text); text-decoration: underline; }

/* 进日历 / 批量导入的入口 chip（T010 / UC-09 AC7）。设计稿规格：带边框、≥13px、
   min-height 40。之前是 .head-action-btn（11px、无边框、次级灰）—— 这套 CSS 里最不起眼
   的控件，用户找不到。和 .cf-radios label 同一种 chip 语言。 */
.cal-entry-chip {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 44px; padding: 8px 14px; box-sizing: border-box;
  background: #fff; border: 1.5px solid var(--primary); border-radius: 10px;
  font-size: 13.5px; font-weight: 600; color: var(--primary);
  cursor: pointer; white-space: nowrap; font-family: inherit;
}
.cal-entry-chip:hover, .cal-entry-chip:focus-visible { background: var(--primary-light); }
/* 整行日历入口（T021 / UC-09 v0.11 AC7）：全宽、≥48px、主文案 + 副文案 + 箭头。
   白卡 + 叶绿描边，和设计稿 C 的列表项同一种存在感；chip 留给日历页里的 Bulk import。 */
/* UIV-12 v2（t-260）：画板这一行是两张并排的快捷卡 —— 画板内容宽 358（内边距 16），两张各 175、缝 8；
   #tab-today 内边距是 20，用 -4 的外边距撑回 358，不动整页内边距。 */
.quick-cards { display: flex; gap: 8px; margin: 8px -4px 4px; }
.quick-cards .cal-entry-row { flex: 1 1 0; margin: 0; }
.quick-cards .cal-entry-chevron { display: none; }   /* 画板的快捷卡没有箭头；半宽卡里它会把副文挤成两行 */
/* .cal-entry-row 本体 2026-09-14 起住 shared.css（S-028 §2：家长端也用它，搬走不复制） */
.head-action-btn + .head-action-btn::before {
  content: "·"; color: var(--border);
  margin-right: 6px; margin-left: -2px;
}
.head-action-btn.danger:hover { color: var(--danger); }

/* RT-10409: threshold colours */
.tracking-item.rate-low { border-left: 4px solid var(--danger) !important; padding-left: 6px !important; background: #FEF2F2 !important; }
.tracking-item.rate-warn { border-left: 4px solid #F59E0B !important; padding-left: 6px !important; background: #FFFBEB !important; }
.tracking-item.rate-low::before, .tracking-item.rate-warn::before {
  content: attr(data-rate-badge); position: absolute; top: -8px; left: 8px;
  font-size: 10px; font-weight: 700; padding: 2px 7px; border-radius: 8px; pointer-events: none; z-index: 1;
}
.tracking-item.rate-low::before { background: var(--danger); color: #fff; }
.tracking-item.rate-warn::before { background: #F59E0B; color: #fff; }

/* ── Stat pairs ──────────────────────────────────── */
.stat-pair { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; flex-shrink: 0; }
.stat-confirmed, .stat-pending { display: inline-flex; align-items: center; gap: 3px; padding: 4px 10px; border-radius: 8px; font-size: 13px; font-weight: 700; white-space: nowrap; }
.stat-confirmed { background: #D1FAE5; color: #065F46; }
.stat-pending { background: #F3F4F6; color: #6B7280; }
.stat-pair.compact .stat-confirmed, .stat-pair.compact .stat-pending { font-size: 11px; padding: 3px 8px; }

/* ── Compose form ────────────────────────────────── */
.form-group { margin-bottom: 16px; }
.form-group label { display: block; font-size: 13px; font-weight: 600; color: var(--text-secondary); margin-bottom: 6px; }
.form-group input, .form-group select, .form-group textarea {
  width: 100%; padding: 11px 14px; border: 1.5px solid var(--border); border-radius: 10px; font-size: 14px; font-family: inherit;
}
.form-group textarea { min-height: 100px; resize: vertical; }
/* UIV-14：占位字原来落到浏览器默认灰（#757575）—— 接上全站次级字 */
.form-group input::placeholder, .form-group textarea::placeholder { color: var(--text-secondary); opacity: 1; }
.required-mark { color: var(--danger); margin-left: 2px; font-weight: 700; }
.form-row { display: flex; gap: 12px; }
.form-row .form-group { flex: 1; }
.checkbox-row { display: flex; align-items: center; gap: 8px; font-size: 14px; margin-bottom: 16px; }
.checkbox-row input { width: 18px; height: 18px; accent-color: var(--primary); }
.required-counter { font-size: 13px; font-weight: 600; text-align: center; margin-bottom: 12px; padding: 8px 12px; border-radius: 8px; }
.required-counter.pending { background: #FEF2F2; color: var(--danger); }
.required-counter.complete { background: #D1FAE5; color: #065F46; }
/* UIV-14：主按钮照全站那一套 —— 胶囊（--radius-btn）· 上下 15 · 带同色系投影（原来是 16 圆角、扁平贴着页面） */
.send-btn { display: block; width: 100%; padding: 15px 14px; border: none; border-radius: var(--radius-btn); background: var(--primary); color: white; font-size: 16px; font-weight: 700; cursor: pointer; font-family: inherit; box-shadow: var(--shadow-primary); }
.send-btn:disabled { opacity: 0.55; cursor: not-allowed; }

/* Individual parent picker — shown when compose-to=individual (RT-10448) */
.parent-picker {
  background: #F8FAEF;
  border: 1px dashed #C9D5A8;
  border-radius: 12px;
  padding: 12px 14px 10px;
  margin-bottom: 16px;
}
.parent-picker-search {
  width: 100%; padding: 9px 12px;
  border: 1.5px solid var(--border); border-radius: 8px;
  font-size: 14px; font-family: inherit;
  background: #fff;
}
.parent-picker-meta {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 8px; font-size: 12px; color: var(--text-secondary);
}
.ppm-count { font-weight: 600; }
.ppm-clear {
  background: none; border: none; color: var(--primary);
  font-size: 12px; font-weight: 600; cursor: pointer; padding: 2px 6px;
}
.parent-picker-list {
  list-style: none; margin: 8px 0 0; padding: 0;
  max-height: 220px; overflow-y: auto;
  background: #fff; border: 1px solid var(--border); border-radius: 8px;
}
.parent-picker-list .pp-item { border-bottom: 1px solid #F1F2EE; }
.parent-picker-list .pp-item:last-child { border-bottom: none; }
.parent-picker-list label {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; cursor: pointer; font-size: 14px;
  margin: 0; font-weight: 400; color: var(--text);
}
.parent-picker-list .pp-cb { width: 16px; height: 16px; accent-color: var(--primary); margin: 0; }
.parent-picker-list .pp-name { font-weight: 500; }
.parent-picker-list .pp-sub  { color: var(--text-secondary); font-size: 13px; }
.parent-picker-list .pp-loading { padding: 12px; color: var(--text-secondary); font-size: 13px; text-align: center; }
/* Resource compose fields — shown only when type=resource */
.resource-fields {
  background: #F8FAEF;
  border: 1px dashed #C9D5A8;
  border-radius: 12px;
  padding: 14px 14px 4px;
  margin-bottom: 16px;
}
.resource-fields[hidden] { display: none; }
.resource-fields .form-group { margin-bottom: 12px; }
.resource-fields input[type="file"] {
  width: 100%; padding: 8px;
  background: #fff;
  border: 1.5px dashed var(--border);
  border-radius: 10px; font-size: 13px; cursor: pointer;
}
.resource-file-hint { font-size: 12px; color: var(--text-secondary); margin-top: 6px; }
.resource-file-hint.has-files { color: var(--primary); font-weight: 600; }

/* ── Tracking detail ─────────────────────────────── */
.reminder-mode-header {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  background: var(--primary-light); border: 1px solid var(--primary); color: var(--primary);
  border-radius: 10px; padding: 10px 12px; margin-bottom: 12px;
  font-size: 14px; font-weight: 700; animation: slideDown 0.25s ease;
}
.reminder-mode-header[hidden] { display: none !important; }
.rmh-exit { background: transparent; border: none; font-size: 12px; font-weight: 600; color: var(--primary); cursor: pointer; text-decoration: underline; }
.tracking-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
.tracking-header h3 { font-size: 16px; font-weight: 700; }
/* S-020 §2 / UIV-12：轨道照画板（--tag-neutral-bg · 6 高 · 胶囊）；填充分两态，界线是老师自己设的「低」阈值
   （JS 打 .is-chase / .is-steady 两个类，阈值读 loadConfirmRateThresholds()）。
   要追那一态的橙条压轨道 2.33 —— 画板值，PD-0072 v2 那把尺子（S-020 §2）。 */
.progress-bar { height: 6px; background: var(--tag-neutral-bg); border-radius: 999px; overflow: hidden; margin-bottom: 16px; }
.progress-fill { height: 100%; background: var(--primary); border-radius: 999px; transition: width 0.5s ease; }
.progress-fill.is-chase { background: var(--accent); }
.progress-fill.is-steady { background: var(--primary); }
.tracking-sent-info { font-size: 13px; color: var(--text-secondary); margin-bottom: 12px; }
.last-batch-info { font-size: 12px; color: var(--text-secondary); background: #F9FAFB; border: 1px solid var(--border); padding: 8px 12px; border-radius: 8px; margin-bottom: 10px; }
/* S-020 §2（v1.5 冻结）：催办在代码里有两处（今日页的 #reminder-btn、发帖层的 .pw-remind），同一个动作、同一个画板 ——
   合成一颗两态胶囊。写死的亮蓝 #2563EB（不在全站任何调色板里）退场。
   「要追」= 这一条的完成率低于老师设的 low：实心深绿白字；否则浅绿底深绿字。
   命中区 44 用内边距撑 + 负外边距收回（T066），看得见的还是画板那颗 8/13 的胶囊。 */
.remind-pill {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  border: none; border-radius: 999px;
  font-family: inherit; font-size: 12px; font-weight: 700;
  padding: 8px 13px; min-height: 44px; margin: -10px 0;
  background: var(--primary-light); color: var(--primary-dark);
  cursor: pointer;
}
.remind-pill.is-chase { background: var(--primary); color: #fff; }
.remind-pill.is-steady { background: var(--primary-light); color: var(--primary-dark); }
.reminder-btn { margin-top: 4px; }
.remind-pill:disabled { opacity: 0.5; cursor: not-allowed; background: var(--text-secondary); color: #fff; }

/* ── Parent list ─────────────────────────────────── */
.parent-list { list-style: none; padding: 0; margin: 0; }
.parent-item { display: flex; align-items: center; gap: 10px; padding: 10px 0; border-bottom: 1px solid var(--border); font-size: 14px; }
.parent-item:last-child { border-bottom: none; }
.status-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.status-dot.confirmed { background: var(--success); }
.status-dot.pending { background: var(--border); }
.parent-item .name { flex: 1; font-weight: 500; }
.parent-item .time { font-size: 12px; color: var(--text-secondary); }
.parent-item.pending-row { align-items: flex-start; }
.pending-meta { display: flex; flex-direction: column; gap: 3px; flex: 1; min-width: 0; }
.pending-meta .name { flex: none; }
.parent-item input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--primary); cursor: pointer; flex-shrink: 0; }
.push-meta-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.push-status { display: inline-flex; align-items: center; gap: 3px; padding: 2px 7px; border-radius: 10px; font-size: 11px; font-weight: 600; }
.push-status.delivered { background: #D1FAE5; color: #065F46; }
.push-status.undelivered { background: #FEE2E2; color: #991B1B; }
.push-time { font-size: 11px; color: var(--text-secondary); }
.parent-list.reminder-mode .parent-item:not(.pending-row) { display: none; }

/* ── Reminder history ────────────────────────────── */
.reminder-history { margin-top: 18px; border-top: 1px solid var(--border); padding-top: 14px; }
.reminder-history-toggle { display: flex; align-items: center; justify-content: space-between; font-size: 13px; color: var(--primary); cursor: pointer; padding: 4px 0; text-decoration: underline; font-weight: 600; }
.rh-toggle-text { display: inline-flex; align-items: center; gap: 6px; }
.rh-caret { transition: transform 0.15s; font-size: 12px; color: var(--text-secondary); }
.reminder-history.collapsed .rh-caret { transform: rotate(-90deg); }
.reminder-history.collapsed .reminder-history-list { display: none; }
.reminder-history-list { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.reminder-history-item { background: #F9FAFB; border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; animation: slideDown 0.25s ease; }
.reminder-history-item.first-send { background: #F0F7F1; border-color: #C8D8CA; }
.reminder-history-item.just-sent { background: #ECFDF5; border-color: #6EE7B7; }
.rh-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
.rh-method { display: inline-flex; align-items: center; gap: 3px; padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 700; }
.rh-method.push { background: #DBEAFE; color: #1E40AF; }
.rh-method.sms { background: #FEF3C7; color: #92400E; }
.rh-time { font-size: 12px; font-weight: 600; color: var(--text); }
.rh-tag { font-size: 10px; font-weight: 700; padding: 1px 6px; border-radius: 8px; background: var(--primary-light); color: var(--primary); }
.rh-meta { font-size: 12px; color: var(--text-secondary); margin-bottom: 4px; }
.rh-recipients { font-size: 12px; color: var(--text); line-height: 1.5; word-break: break-word; }

/* ── Photo permissions ───────────────────────────── */
.photo-perm-hint { font-size: 12px; color: #7A4F00; background: #FFF7E6; border: 1px solid #F4D9A8; padding: 8px 12px; border-radius: 8px; margin-bottom: 12px; line-height: 1.5; }
.photo-perm-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--border); }
.photo-perm-row:last-child { border-bottom: none; }
.album-meta { flex: 1; min-width: 0; }
.album-name { font-size: 14px; font-weight: 600; }
.album-sub { font-size: 12px; color: var(--text-secondary); margin-top: 2px; }
/* BUG-028：用户说「点了没反应」，ux 实测「点在正中心是有反应的」——
   **那不是按钮坏了，是按不中**：26px 高。这一组排在行右侧，长高会把整行撑开，
   所以用负外边距把行高收回去。 */

/* ── Permission denied toast ─────────────────────── */
.permission-toast {
  position: fixed; left: 50%; bottom: 30px; transform: translateX(-50%) translateY(20px);
  background: #1F2937; color: white; padding: 12px 18px; border-radius: 12px;
  font-size: 13px; font-weight: 600; box-shadow: 0 8px 24px rgba(0,0,0,0.25);
  opacity: 0; transition: opacity 0.25s, transform 0.25s; pointer-events: none; z-index: 999;
  max-width: 90%; text-align: center; line-height: 1.5;
}
.permission-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.pt-title { font-size: 14px; }
.pt-sub { font-size: 12px; font-weight: 500; opacity: 0.85; margin-top: 2px; }

/* ── Teacher Calendar (RT-10446) ─────────────────── */
.teacher-calendar { padding: 14px; }
#cal-empty-state .empty-icon { font-size: 40px; margin-bottom: 8px; }
#cal-empty-state h3 { font-size: 17px; font-weight: 700; margin-bottom: 6px; color: var(--text); }
#cal-empty-state p { font-size: 13px; color: var(--text-secondary); margin-bottom: 18px; line-height: 1.5; }
.cal-secondary-link { display: inline-block; margin-top: 10px; font-size: 13px; color: var(--primary); text-decoration: underline; padding: 6px 8px; min-height: 44px; }  /* UX-004 */

.cal-header-row { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
.cal-month-label { flex: 1; text-align: center; font-size: 15px; font-weight: 700; color: var(--text); }
.cal-nav-btn { background: var(--bg); border: 1px solid var(--border); border-radius: 8px; width: 36px; height: 36px; font-size: 18px; cursor: pointer; color: var(--text); }
.cal-nav-btn:hover { background: var(--primary-light); border-color: var(--primary); color: var(--primary); }
.cal-add-btn { background: var(--primary); color: #fff; border: none; border-radius: 8px; padding: 0 10px; height: 36px; font-size: 12px; font-weight: 700; cursor: pointer; flex-shrink: 0; }
.cal-add-btn:hover { opacity: 0.9; }
/* BUG-053（t-248 · 闸 1）：这一屏 8 个目标命中区 36 / 36×32 / 20 —— 翻月和 ⋮ 是最常点的两件，手指落在边缘就什么也不发生。
   看得见的形状与行高**逐像素不动**（H3），命中区靠伪元素扩（T066 / 4f31c3b 的做法）。
   撑大的那一块只往**没有相邻目标**的方向伸，相邻目标之间的间距一像素不变（design S-022 §4：横向间距要过闸 2）：
   ‹ 往右（月份标签那边）、› 往左、＋New 只上下、⋮ 只往右（外侧）。伪元素按内边距盒算，带 1px 边框的两颗要多扩 1。 */
.cal-nav-btn, .cal-add-btn, .cal-activity-item .ca-rsvp-badge { position: relative; }
#t-cal-prev::after { content: ''; position: absolute; top: -5px; bottom: -5px; left: 0; right: -10px; }    /* 34+10 = 44 */
#t-cal-next::after { content: ''; position: absolute; top: -5px; bottom: -5px; left: -10px; right: 0; }
.cal-add-btn::after { content: ''; position: absolute; top: -4px; bottom: -4px; left: 0; right: 0; }        /* 高 36 → 44 */
.cal-activity-item .ca-menu::after { content: ''; position: absolute; top: 50%; height: 44px; transform: translateY(-50%); left: 0; right: -12px; }   /* 宽 32 → 44；高固定 44、居中 */
/* RSVP 那颗按 H2 二选一：**保持可点**（它是进 RSVP 名单最短的路，⋮ 里那条留作备路），命中区 → 44；胶囊本身不变。
   qa run 141：胶囊和 ⋮ 的高都由**字体度量**定 —— 回退字下胶囊 20、Figtree 真加载后 19，「行盒 + 固定外扩」就差 1（43）。
   所以这两颗的竖向不再从行盒往外撑，而是伪元素**自己 44 高、居中**：字体怎么换，命中区都是 44。 */
.cal-activity-item .ca-rsvp-badge::after { content: ''; position: absolute; top: 50%; height: 44px; transform: translateY(-50%); left: 0; right: 0; }

.cal-weekdays, .cal-days { display: grid; grid-template-columns: repeat(7, 1fr); }
.cal-weekdays { font-size: 11px; color: var(--text-secondary); text-align: center; font-weight: 600; padding: 4px 0; }
.cal-weekdays div { padding: 4px 0; }
.cal-days { gap: 2px; }
.cal-day { aspect-ratio: 1/1; min-height: 38px; display: flex; align-items: center; justify-content: center; font-size: 13px; color: var(--text); border-radius: 8px; cursor: pointer; position: relative; user-select: none; }
/* 邻月格是空的（t-249 · S-022 §2），只剩形状 —— 原来这里给它染色的那条没有字可染，退场；cursor: default 由 shared.css 的同名规则给 */
.cal-day.today { background: var(--primary-light); font-weight: 700; color: var(--primary); }
.cal-day.has-event::after { content: ""; position: absolute; bottom: 5px; left: 50%; transform: translateX(-50%); width: 5px; height: 5px; border-radius: 50%; background: var(--primary); }
.cal-day.has-event.multi::after { background: linear-gradient(90deg, var(--primary) 50%, var(--accent) 50%); width: 9px; }
.cal-day.selected { background: var(--primary); color: #fff; }
.cal-day.selected.has-event::after { background: #fff; }
.cal-day:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }

.cal-day-tooltip { position: absolute; z-index: 999; background: #1F2937; color: #fff; padding: 10px 12px; border-radius: 10px; font-size: 12px; max-width: 240px; box-shadow: 0 8px 24px rgba(0,0,0,0.18); pointer-events: none; line-height: 1.5; }
.cal-day-tooltip[hidden] { display: none; }
.cal-day-tooltip .tt-date { font-weight: 700; margin-bottom: 4px; }
.cal-day-tooltip .tt-line { font-size: 11px; opacity: 0.9; }

.cal-list-toolbar { display: flex; align-items: center; gap: 8px; margin: 14px 0 8px; flex-wrap: wrap; }
.cal-search { flex: 1 1 160px; min-width: 0; padding: 8px 12px; border: 1.5px solid var(--border); border-radius: 8px; font-size: 13px; background: var(--bg); min-height: 44px; box-sizing: border-box; }   /* BUG-055（t-250）：33 → 44，同一行的按钮本来就 ≥44，工具条随之齐平 */
.cal-search:focus { outline: none; border-color: var(--primary); }
/* BUG-054（t-249 · S-022 §3）：占位字原来是浏览器默认色，压 --bg 只有 4.19；换 --text-secondary 压 --bg 4.70（压白 5.17），与 .form-group 的占位字同一 token */
.cal-search::placeholder { color: var(--text-secondary); opacity: 1; }
.cal-month-tag { display: inline-flex; align-items: center; gap: 4px; background: var(--primary-light); color: var(--primary); border-radius: 999px; padding: 4px 10px; font-size: 11px; font-weight: 700; }
.cal-clear-filter { background: none; border: none; font-size: 12px; color: var(--primary); cursor: pointer; text-decoration: underline; padding: 6px; min-height: 32px; }

.cal-activity-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.cal-activity-list .cal-month-divider { position: sticky; top: 0; z-index: 2; background: var(--card); padding: 8px 4px 4px; font-size: 11px; font-weight: 700; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.4px; border-bottom: 1px solid var(--border); }
.cal-activity-item { display: flex; align-items: center; gap: 10px; padding: 10px; background: var(--bg); border-radius: 10px; transition: background 0.15s; scroll-margin-top: 60px; }
.cal-activity-item.flash { animation: calRowFlash 1.2s ease; }
@keyframes calRowFlash { 0% { background: #FEF3C7; } 100% { background: var(--bg); } }
.cal-activity-item .ca-date { display: flex; flex-direction: column; align-items: center; min-width: 38px; flex-shrink: 0; }
.cal-activity-item .ca-day { font-size: 18px; font-weight: 700; color: var(--text); line-height: 1; }
.cal-activity-item .ca-mon { font-size: 10px; font-weight: 600; color: var(--text-secondary); text-transform: uppercase; }
.cal-activity-item .ca-body { flex: 1; min-width: 0; }
.cal-activity-item .ca-title { font-size: 14px; font-weight: 600; color: var(--text); margin-bottom: 2px; word-break: break-word; }
.cal-activity-item .ca-tags { display: flex; gap: 4px; flex-wrap: wrap; }
.cal-activity-item .ca-tag { font-size: 10px; font-weight: 600; padding: 2px 7px; border-radius: 999px; background: #FEF3C7; color: #92400E; }
.cal-activity-item .ca-tag.attend-required { background: #DBEAFE; color: #1E40AF; }
.cal-activity-item .ca-tag.attend-optional { background: #F3F4F6; color: #4B5563; }
.cal-activity-item .ca-tag.postponed { background: #FEE2E2; color: #991B1B; }
.cal-activity-item .ca-rsvp-badge { display: inline-flex; align-items: center; gap: 3px; padding: 3px 8px; border-radius: 999px; font-size: 11px; font-weight: 700; background: #ECFDF5; color: #065F46; cursor: pointer; border: none; flex-shrink: 0; }
.cal-activity-item .ca-rsvp-badge.warn { background: #FEF3C7; color: #92400E; }
.cal-activity-item .ca-menu { background: none; border: none; color: var(--text-secondary); font-size: 18px; cursor: pointer; padding: 6px 8px; min-width: 32px; min-height: 32px; border-radius: 6px; flex-shrink: 0; position: relative; }
.cal-activity-item .ca-menu:hover { background: var(--border); }
.cal-menu-pop { position: absolute; right: 0; top: 100%; background: var(--card); border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 8px 24px rgba(0,0,0,0.12); z-index: 5; min-width: 140px; padding: 4px; }
.cal-menu-pop button { display: block; width: 100%; text-align: left; background: none; border: none; padding: 8px 10px; font-size: 13px; color: var(--text); cursor: pointer; border-radius: 6px; }
.cal-menu-pop button:hover { background: var(--bg); }
.cal-menu-pop button.danger { color: var(--danger); }
/* Modals */
.cal-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.45); display: flex; align-items: flex-end; justify-content: center; z-index: 1100; padding: 16px; padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px)); animation: calFade 0.18s ease; }
.cal-modal[hidden] { display: none; }
@keyframes calFade { from { opacity: 0; } to { opacity: 1; } }
.cal-modal-card { background: var(--card); border-radius: 16px; padding: 18px; width: 100%; max-width: 460px; max-height: 90vh; overflow-y: auto; animation: calSlideUp 0.22s ease; }
.cal-modal-card.small { max-width: 360px; }
@keyframes calSlideUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
@media (min-width: 600px) { .cal-modal { align-items: center; } }
.cal-modal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.cal-modal-head h3 { font-size: 17px; font-weight: 700; color: var(--text); }
.cal-modal-close { background: none; border: none; font-size: 24px; color: var(--text-secondary); cursor: pointer; padding: 4px 10px; line-height: 1; min-height: 36px; }
.cal-form .form-group { margin-bottom: 12px; }
.cal-status-toggle { display: flex; gap: 12px; }
.cal-status-toggle label { display: inline-flex; align-items: center; gap: 6px; font-size: 14px; cursor: pointer; padding: 6px 0; }
.cal-modal-actions { display: flex; gap: 10px; margin-top: 16px; }
.cal-primary-btn, .cal-secondary-btn, .cal-danger-btn { flex: 1; padding: 12px; border: none; border-radius: 10px; font-size: 14px; font-weight: 700; cursor: pointer; font-family: inherit; }
.cal-primary-btn { background: var(--primary); color: #fff; }
.cal-primary-btn.full { width: 100%; flex: none; margin-top: 12px; }
.cal-primary-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.cal-secondary-btn { background: var(--bg); color: var(--text); border: 1px solid var(--border); }
.cal-danger-btn { background: var(--danger); color: #fff; }
/* 空态与警示上的出口（T051）。**一条警示如果不能回答「所以我该做什么」，
   它就只是一句让人不安的话** —— 所以这两块都带按钮，而不是只带一句提示。 */
#published-empty .pe-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
#published-empty .pe-actions button { flex: 1 1 auto; min-width: 140px; max-width: 220px; }

/* 说明文字自己占一段，按钮另起一行 —— 不然「…this page.」会和按钮挤在同一行，
   读起来像句子还没说完就被打断。 */
.review-no-contact > span { display: block; }
.review-no-contact .rnc-fix {
  /* 这块警示本身就是实心红底白字，按钮**不能也用红底** —— 那是红上加红，看不见。
     反过来：白底红字，在红底上是最亮的一块。 */
  display: inline-block; margin-top: 10px;
  background: #fff; color: var(--danger); border: none; border-radius: 8px;
  padding: 9px 14px; font-size: 13px; font-weight: 700; font-family: inherit; cursor: pointer;
  min-height: 44px;   /* UX-004：spec §4.1 链接/CTA ≥44。这一颗是那条警示唯一的出口 */
}

.post-sent-card { text-align: center; padding: 26px 20px 20px; }
.post-sent-check {
  width: 56px; height: 56px; margin: 0 auto 12px; border-radius: 50%;
  background: #D1FAE5; color: #065F46; font-size: 30px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}
.post-sent-card h3 { font-size: 19px; font-weight: 700; color: var(--text); margin-bottom: 6px; }
.post-sent-line { font-size: 14px; color: var(--text-secondary); line-height: 1.5; margin-bottom: 4px; }

.cal-delete-target { font-size: 14px; font-weight: 600; color: var(--text); margin-bottom: 8px; }
.cal-delete-hint { font-size: 12px; color: var(--text-secondary); background: #FEF3C7; padding: 8px 12px; border-radius: 8px; line-height: 1.5; }

.cal-rsvp-meta { font-size: 13px; color: var(--text-secondary); margin-bottom: 8px; }
.cal-rsvp-stats { display: flex; gap: 8px; margin-bottom: 14px; }
.cal-rsvp-section { margin-bottom: 14px; }
.cal-rsvp-section h4 { font-size: 13px; font-weight: 700; color: var(--text); margin-bottom: 6px; }
.cal-rsvp-list { list-style: none; margin: 0; padding: 0; max-height: 140px; overflow-y: auto; }
.cal-rsvp-list li { display: flex; justify-content: space-between; padding: 6px 0; font-size: 13px; border-bottom: 1px solid var(--border); }
.cal-rsvp-list li:last-child { border-bottom: none; }
.cal-rsvp-list .empty { color: var(--text-secondary); padding: 8px 0; font-size: 12px; text-align: center; }
.cal-rsvp-cooldown-hint { font-size: 12px; color: var(--text-secondary); text-align: center; margin-top: 8px; }

@media (max-width: 480px) {
  .cal-day { min-height: 34px; font-size: 12px; }
  .cal-list-toolbar { gap: 6px; }
  .cal-modal { padding: 0; }
  .cal-modal-card { border-radius: 16px 16px 0 0; max-height: 92vh; padding-bottom: calc(18px + env(safe-area-inset-bottom, 0px)); }
}

/* ── Project Ratings ───────────────────────────── */
.rating-projects-list { display: flex; flex-direction: column; gap: 2px; }
.rating-loading, .rating-error, .rating-empty { padding: 24px; text-align: center; color: var(--text-secondary); font-size: 14px; }
.rating-error { color: #DC2626; }
.rating-project-row {
  display: flex; justify-content: space-between; align-items: center;
  padding: 12px 14px; border-bottom: 1px solid var(--border);
  transition: background 0.15s;
}
.rating-project-row:last-child { border-bottom: none; }
.rating-project-row:hover { background: #F9FAFB; }
.rating-project-info { flex: 1; min-width: 0; }
.rating-project-name { font-weight: 600; font-size: 14px; color: var(--text); }   /* t-180：原来引用的变量未定义，整条声明曾无效 */
.rating-project-desc { font-size: 11px; color: var(--text-secondary); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rating-controls { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.rating-stars { display: flex; align-items: center; gap: 6px; }
.rating-star-display { font-size: 16px; letter-spacing: 1px; color: #F59E0B; }
.rating-numeric { font-size: 12px; font-weight: 600; color: var(--text-secondary); min-width: 24px; text-align: right; }
.rating-input-group { display: flex; align-items: center; gap: 4px; }
.rating-input {
  width: 52px; padding: 4px 6px; border: 1.5px solid var(--border);
  border-radius: 6px; font-size: 12px; text-align: center;
}
.rating-input:focus { outline: none; border-color: var(--primary); }
.rating-set-btn {
  padding: 4px 10px; border: none; border-radius: 6px;
  background: var(--primary); color: white; font-size: 12px; font-weight: 600;
  cursor: pointer; transition: background 0.15s;
}
.rating-set-btn:hover { background: var(--primary-dark); }
.rating-set-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.rating-set-btn.flash-success { background: #059669; }

/* ── Import Wizard (RT-10447) ────────────────────── */
.cal-modal-card.import-wizard { width: 100%; max-width: 720px; max-height: 90vh; display: flex; flex-direction: column; padding: 0; overflow: hidden; }
.cal-modal-card.import-wizard .cal-modal-head { padding: 14px 18px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
.import-step { padding: 16px 18px 18px; overflow-y: auto; flex: 1; }
.import-format-row { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 14px; margin-bottom: 10px; }
.import-format-row > label { font-size: 12px; font-weight: 600; color: var(--text-secondary); }
.import-format-toggle { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.import-format-toggle label { display: inline-flex; align-items: center; gap: 4px; padding: 5px 10px; border: 1.5px solid var(--border); border-radius: 999px; background: white; font-size: 12px; font-weight: 600; color: var(--text-secondary); cursor: pointer; transition: all 0.15s; }
.import-format-toggle label:has(input:checked) { background: var(--primary-light); border-color: var(--primary); color: var(--primary); }
.import-format-toggle input { display: none; }
.import-hint { font-size: 12px; color: var(--text-secondary); background: #FFF7E6; border: 1px solid #F4D9A8; padding: 8px 10px; border-radius: 8px; margin: 6px 0 12px; line-height: 1.55; }
.import-hint strong { color: #B45309; }
.import-paste-area { width: 100%; border: 1.5px solid var(--border); border-radius: 10px; padding: 12px; font-size: 13px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; line-height: 1.55; resize: vertical; min-height: 180px; background: white; color: var(--text); }
.import-paste-area:focus { outline: none; border-color: var(--primary); }
.import-paste-actions, .import-preview-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 14px; }

.import-summary { font-size: 13px; font-weight: 600; padding: 8px 12px; background: #F3F4F6; border-radius: 8px; margin-bottom: 10px; color: var(--text); position: sticky; top: 0; z-index: 2; }
.import-summary .iw-warn { color: #B45309; margin-left: 6px; font-weight: 700; }
.import-summary .iw-ok { color: #047857; margin-left: 6px; }
.import-preview-wrap { border: 1px solid var(--border); border-radius: 10px; overflow: auto; max-height: 50vh; }
.import-preview-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.import-preview-table thead th { position: sticky; top: 0; background: #F9FAFB; border-bottom: 1px solid var(--border); padding: 8px 10px; font-weight: 700; text-align: left; font-size: 12px; color: var(--text-secondary); z-index: 1; }
.import-preview-table .col-status { width: 48px; text-align: center; }
.import-preview-table .col-date { min-width: 104px; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* 日期格：完整年月日一行，区间终点和时间各自换行 —— 手机宽度下也放得下，年份看得见。 */
.import-preview-table .iw-range-end, .import-preview-table .iw-time { display: block; }
.import-preview-table .iw-arrow { color: #9CA3AF; }
.import-preview-table .iw-time { color: var(--text-secondary); font-weight: 600; }
.import-preview-table .col-tag { width: 90px; }
.import-preview-table tbody tr { border-bottom: 1px solid var(--border); cursor: pointer; }
.import-preview-table tbody tr:last-child { border-bottom: none; }
.import-preview-table tbody td { padding: 8px 10px; vertical-align: top; }
.import-preview-table tr.row-warn { background: #FFFBEB; }
.import-preview-table tr.row-error { background: #FEF2F2; }
.import-preview-table tr.row-ok:hover { background: #F9FAFB; }
.import-preview-table .iw-status-icon { font-size: 15px; }
.import-preview-table .iw-tag-pill { display: inline-block; background: #FCE7F3; color: #9D174D; border-radius: 999px; padding: 2px 8px; font-size: 11px; font-weight: 700; }
.import-preview-table tr.row-fix-open { background: #FFF7E6; }
.import-fix-row td { padding: 0 !important; background: #FFFBEB; }
.import-fix-panel { padding: 10px 14px 12px; border-top: 1px dashed #F4D9A8; }
.import-fix-panel .iw-fix-label { font-size: 12px; font-weight: 700; color: #92400E; margin-bottom: 4px; }
.import-fix-panel .iw-fix-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.import-fix-panel input { padding: 6px 10px; border: 1.5px solid var(--border); border-radius: 8px; font-size: 12px; min-width: 110px; flex: 1; }
.import-fix-panel input:focus { outline: none; border-color: var(--primary); }
.import-fix-panel .iw-fix-hint { font-size: 11px; color: #92400E; margin-top: 4px; line-height: 1.5; }

.import-unsaved-confirm { position: absolute; inset: 0; background: rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; z-index: 5; padding: 16px; }
.import-unsaved-confirm[hidden] { display: none; }
.iuc-card { background: white; border-radius: 12px; padding: 18px 20px; max-width: 360px; width: 100%; box-shadow: 0 12px 32px rgba(0,0,0,0.18); }
.iuc-card h4 { font-size: 15px; margin: 0 0 6px; color: var(--text); }
.iuc-card p { font-size: 13px; color: var(--text-secondary); margin: 0 0 14px; line-height: 1.55; }
.iuc-actions { display: flex; justify-content: flex-end; gap: 8px; }
.cal-danger-btn { padding: 8px 14px; border-radius: 8px; border: none; font-size: 13px; font-weight: 600; background: #DC2626; color: white; cursor: pointer; }
.cal-danger-btn:hover { background: #B91C1C; }
.cal-day-event-new { animation: iw-flash-new 1.6s ease-out 1; }
@keyframes iw-flash-new { 0%, 60% { background: #FEF3C7; } 100% { background: transparent; } }
.ca-new-badge {
  display: inline-block; background: #DC2626; color: white; font-size: 10px; font-weight: 800;
  padding: 1px 6px; border-radius: 999px; margin-right: 6px; vertical-align: middle;
  letter-spacing: 0.5px; line-height: 1.4;
}
.cal-activity-item.is-new { background: #FFFBEB; }
.cal-activity-item.is-new .ca-title { font-weight: 700; }


/* ── Teacher tools nav (RT-10446) ──────────────── */
.teacher-tools-nav {
  list-style: none; margin: 0 0 18px; padding: 0;
  display: grid; gap: 8px;
}
@media (min-width: 640px) {
  .teacher-tools-nav { grid-template-columns: 1fr 1fr; }
}
.teacher-tool-item {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 14px;
  background: var(--card); border-radius: var(--radius);
  text-decoration: none; color: var(--text);
  box-shadow: 0 1px 2px rgba(15,23,42,0.05), 0 0 0 1px rgba(15,23,42,0.04);
  transition: background 0.15s, transform 0.15s;
}
.teacher-tool-item:hover { background: var(--bg); }
.teacher-tool-item:active { transform: translateY(1px); }
.teacher-tool-item .tool-icon {
  font-size: 22px; flex-shrink: 0;
  width: 38px; height: 38px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--primary-light); border-radius: 10px;
}
.teacher-tool-item .tool-label {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; gap: 2px;
}
.teacher-tool-item .tool-title { font-size: 14px; font-weight: 700; color: var(--text); }
.teacher-tool-item .tool-sub { font-size: 11.5px; color: var(--text-secondary); }
.teacher-tool-item .tool-arrow {
  font-size: 18px; color: var(--text-secondary); flex-shrink: 0;
  margin-left: 4px;
}

/* 待审名单：空态与审批结果标签（列表改为 API 动态渲染后新增） */
.pending-item.pending-empty {
  justify-content: center;
  color: var(--text-secondary);
  font-size: 13px;
}
.pending-result { font-size: 12px; font-weight: 600; }
.pending-result.ok { color: var(--success); }
.pending-result.no { color: var(--danger); }
.pending-actions button[disabled] { opacity: 0.5; cursor: not-allowed; }

/* 追踪列表空态（列表改为 API 动态渲染后新增） */
.tracking-empty {
  padding: 16px;
  text-align: center;
  color: var(--text-secondary);
  font-size: 13px;
}

/* 追踪详情：接入真实数据后新增 */
.parent-item.parent-empty { justify-content: center; color: var(--text-secondary); font-size: 13px; }
.signed-tag {
  margin-left: 6px; padding: 1px 6px; border-radius: 6px;
  background: #FEF3C7; color: #92400E; font-size: 11px; font-weight: 600;
}
.detail-unmatched-note { margin-top: 8px; font-size: 12px; color: var(--text-secondary); }

/* 改密码弹窗 */
.pw-modal {
  display: none; position: fixed; inset: 0; z-index: 900;
  background: rgba(0,0,0,.45); align-items: center; justify-content: center; padding: 20px;
}
.pw-modal.show { display: flex; }
.pw-card {
  background: var(--card, #fff); border-radius: var(--radius, 12px);
  padding: 20px; width: 100%; max-width: 360px;
  box-shadow: 0 12px 32px rgba(0,0,0,.18);
}
.pw-card h3 { margin: 0 0 12px; font-size: 16px; }
.pw-card label {
  display: block; margin: 12px 0 4px;
  font-size: 12px; color: var(--text-secondary, #6B7280);
}
.pw-card input {
  width: 100%; box-sizing: border-box; padding: 10px 12px;
  border: 1px solid var(--border, #E5E7EB); border-radius: 8px; font-size: 14px;
}
.pw-hint { margin: 10px 0 0; font-size: 12px; color: var(--text-secondary, #6B7280); }
.pw-err {
  margin-top: 10px; padding: 8px 10px; border-radius: 8px;
  background: #FEF2F2; color: var(--danger, #DC4F4F); font-size: 13px;
}
.pw-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 16px; }
.pw-actions button {
  padding: 9px 16px; border-radius: 10px; font-size: 14px;
  font-weight: 600; cursor: pointer; border: 1px solid var(--border, #E5E7EB);
}
.pw-cancel { background: #fff; color: var(--text-secondary, #6B7280); }
.pw-save { background: var(--primary, #2D4A32); color: #fff; border-color: transparent; }
.pw-save[disabled] { opacity: .5; cursor: not-allowed; }

/* Compose: message importance hint */
.form-hint { font-size: 12px; color: var(--text-secondary); margin-top: 6px; line-height: 1.45; }
.form-hint.level-hint { color: var(--danger); }
.form-hint[hidden] { display: none; }

/* ── 已发布的 item ───────────────────────────────────────── */
.published-list { list-style: none; margin: 8px 0 0; padding: 0; }
.published-item {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 0; border-bottom: 1px solid var(--border);
}
.published-item:last-child { border-bottom: none; }
.published-item .pi-main { flex: 1; min-width: 0; }
.published-item .pi-title {
  font-size: 14px; font-weight: 600; display: flex; align-items: center; gap: 8px;
}
.published-item .pi-meta { font-size: 12px; color: var(--text-secondary); margin-top: 2px; }
.published-item .pi-updated {
  background: var(--tag-warning-bg); color: var(--tag-warning-fg);
  font-size: 11px; font-weight: 600; padding: 1px 8px; border-radius: 999px;
}

/* ── 谁还没回答 ─────────────────────────────────────────── */
.pi-track { margin-top: 8px; display: grid; gap: 4px; }
.pi-track[hidden] { display: none; }
.pt-row { font-size: 12px; color: var(--text-secondary); line-height: 1.5; }
.pt-row b { color: var(--text); font-size: 13px; }
/* 还没答的那一行是这个视图存在的理由，给它重量 */
.pt-row.pending { color: var(--danger); }
.pt-row.pending b { color: var(--danger); }
.pt-row.stale { color: var(--tag-warning-fg); }

/* ── 出席回执名单卡（PD-0084 ① · t-253 · S-023 §3）──
   --card · --radius-card 18 · --shadow-md · 16 内边距。两个合计各自一格（不合成「共 N 人」，她要排座位）；
   一户一行两行式：户名 15/600 + 右侧人数 13 nowrap（长户名截断让位）；第二行 12.5 次级色；
   答了不来：同重户名 + 「Can't come」600（区分靠那句话，不靠装饰）；没答：单独一组、户名次级色、「—」。 */
.attend-card { background: var(--card); border-radius: var(--radius-card); box-shadow: var(--shadow-md); padding: 16px; margin-top: 8px; }
.attend-card .at-totals { display: flex; gap: 10px; margin-bottom: 12px; }
.attend-card .at-tot { flex: 1; background: var(--bg); border-radius: var(--radius-note); padding: 10px 12px; }
.attend-card .at-tot b { display: block; font-family: var(--font-display); font-size: 22px; font-weight: 800; color: var(--primary-deep); line-height: 1.1; }
.attend-card .at-tot span { font-size: 12px; font-weight: 600; color: var(--text-secondary); }
.attend-card .at-fam { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; padding: 10px 0; border-bottom: 1px solid var(--hairline); }
.attend-card .at-fam:last-child { border-bottom: none; }
.attend-card .at-fam .who { min-width: 0; }
.attend-card .at-fam .n { font-size: 15px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.attend-card .at-fam .sub { font-size: 12.5px; font-weight: 400; color: var(--text-secondary); margin-top: 2px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.attend-card .at-fam .v { font-size: 13px; font-weight: 400; color: var(--text-secondary); white-space: nowrap; flex-shrink: 0; }
.attend-card .at-fam.no .v { font-weight: 600; }
.attend-card .at-fam.empty { justify-content: flex-end; }
/* 代填标签：中性，不琥珀 —— 琥珀在这套体系里是「要你动手」，而追不追是她的决定（S-023 §3） */
.attend-card .at-standin, .attend-card .at-stale-tag { font-size: 11.5px; font-weight: 700; letter-spacing: .3px; border-radius: 999px; padding: 3px 9px; background: var(--tag-neutral-bg); color: var(--tag-neutral-fg); }
/* 第四态（S-023 §3.1）：合计含旧版本的答案，这一行说出来；n = 0 时不渲 */
.attend-card .at-stale-note { font-size: 12px; font-weight: 400; color: var(--text-secondary); margin: -6px 0 10px; }
.attend-card .at-sub { font-size: 13px; font-weight: 700; color: var(--text); margin: 14px 0 2px; }
.attend-card .at-pending .at-fam .n { color: var(--text-secondary); }
/* RSVP 那一层本身就是一张卡：里面的名单卡不再套一层阴影与内边距 */
#cal-rsvp-attend .attend-card { box-shadow: none; padding: 0; margin-top: 4px; }
#cal-rsvp-attend { margin-bottom: 14px; }

/* ── 订货单（家长那份表单的转置）─────────────────────────── */
.os-head { font-size: 11px; color: var(--text-secondary); margin-bottom: 4px; }
.os-row {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px;
  font-size: 12.5px; padding: 4px 0; border-bottom: 1px dashed var(--border);
}
.os-row .os-label { font-weight: 700; min-width: 96px; }
.os-row .os-qty b { font-size: 14px; }
.os-row .os-sizes { color: var(--text-secondary); }
.os-row .os-no { color: var(--text-secondary); }
.os-row .os-pending { color: var(--danger); font-weight: 600; margin-left: auto; }

/* ── 举报处置队列 ───────────────────────────────────────── */
/* 处置须知放在最上面。真出事的那一刻，没人会先去翻文档。 */
.review-runbook {
  background: var(--tag-warning-bg); color: var(--tag-warning-fg);
  font-size: 12px; line-height: 1.6; padding: 10px 12px;
  border-radius: 10px; margin: 8px 0 12px;
}
.review-list { list-style: none; margin: 0; padding: 0; }
.review-item { padding: 10px 0; border-bottom: 1px solid var(--border); }
.review-item:last-child { border-bottom: none; }
.review-item .ri-head { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; }
.review-item .ri-reason { font-size: 13px; font-weight: 700; }
.review-item.safety .ri-reason { color: var(--danger); }
.review-item .ri-meta { font-size: 12px; color: var(--text-secondary); }
.review-item .ri-note { font-size: 13px; margin: 4px 0; }
.review-item .ri-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }

/* 收不到举报通知的横幅。这条比队列本身更要紧 —— 队列空着可能是没人举报，
   也可能是举报了但没人知道，两者看起来一模一样。 */
.review-no-contact {
  background: var(--danger); color: #fff;
  font-size: 13px; font-weight: 600; line-height: 1.5;
  padding: 10px 14px; border-radius: 12px; margin-bottom: 12px;
}
.review-no-contact[hidden] { display: none; }

/* ── 渠道自检 ───────────────────────────────────────────── */
.channel-list { list-style: none; margin: 8px 0; padding: 0; }
.channel-item {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-size: 13px; padding: 6px 0; border-bottom: 1px solid var(--border);
}
.channel-item:last-child { border-bottom: none; }
.channel-item .ch-name { font-weight: 700; min-width: 56px; }
.channel-item.ok    .ch-state { color: var(--success); }
/* log 后端「配好了但不发」是独立一档 —— 绿色会骗人 */
.channel-item.warn  .ch-state { color: var(--tag-warning-fg); }
.channel-item.bad   .ch-state { color: var(--danger); }
.channel-item .ch-floor {
  font-size: 11px; background: var(--tag-neutral-bg); color: var(--tag-neutral-fg);
  padding: 1px 8px; border-radius: 999px;
}
.channel-me { font-size: 12px; color: var(--text-secondary); margin-bottom: 8px; }
.channel-me.bad { color: var(--danger); font-weight: 600; }
.channel-result { font-size: 12.5px; margin-top: 8px; color: var(--text-secondary); }
.channel-result[hidden] { display: none; }

/* 老师自己填联系方式。只在收不到通知时出现 —— 告诉她「你收不到」却不给地方填，
   等于把问题原样还给她。 */
.channel-contact { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.channel-contact[hidden] { display: none; }
.tpush-label { display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--text); cursor: pointer; width: 100%; }
.tpush-label input { width: 18px; height: 18px; accent-color: var(--primary); }
.tpush-hint { width: 100%; font-size: 12.5px; color: var(--text-secondary); }
/* 帖子行标题旁「2 new replies」（2026-09-29）：和角标同一个颜色 */
.new-replies-tag { display: inline-block; margin-left: 8px; padding: 1px 8px; border-radius: 999px; font-size: 11.5px; font-weight: 700; color: var(--accent-deep); background: var(--accent-light); vertical-align: middle; white-space: nowrap; }
.channel-contact input {
  flex: 1; min-width: 150px; padding: 8px 10px;
  border: 1.5px solid var(--border); border-radius: 10px;
  font-size: 13px; font-family: inherit;
}
.channel-contact-err { flex-basis: 100%; font-size: 12px; color: var(--danger); }
.channel-contact-err[hidden] { display: none; }

/* ── 账目 ─────────────────────────────────────────── */
/* **报告的价值在 highlight，不在总数。**「本月收了多少」她已经知道；她不知道
   的是哪几户需要她本人去做点什么。所以那几档在视觉上要压过下面的总额。 */
.billing-totals { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 14px; }
/* S-030（t-272 / UX-012）：中间那句「Awaiting your confirmation」在 1/3 卡宽下折行，把它的数字往下推一整行标签高，
   三个 $ 数字不同顶。修法乙：格子排成一列、标签吃掉剩余高度 → 数字贴底；三格本来就等高（grid 拉伸），所以顶也必然齐，
   折成几行都成立。**不用 min-height / 行数假设** —— 那种修的是「这一次的折行数」，换句更长的标签或改一次字号就失效。 */
.b-tile { background: var(--bg); border-radius: 10px; padding: 10px 12px; display: flex; flex-direction: column; }
.b-label { font-size: 11px; font-weight: 700; color: var(--text-secondary); line-height: 1.3; flex: 1 1 auto; }
.b-value { font-size: 18px; font-weight: 800; margin-top: 4px; font-variant-numeric: tabular-nums; }
.b-tile.warn .b-value { color: var(--danger); }
.b-tile.ok .b-value { color: var(--success); }

.hl-block { margin-bottom: 14px; }
.hl-head {
  font-size: 13px; font-weight: 700; color: var(--text);
  padding-bottom: 6px; border-bottom: 1.5px solid var(--border); margin-bottom: 6px;
}
.hl-block ul { list-style: none; margin: 0; padding: 0; }
.hl-block li {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
  padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 13.5px;
}
.hl-block li:last-child { border-bottom: none; }
.hl-name { font-weight: 700; }
.hl-what { color: var(--text-secondary); flex: 1; min-width: 0; }
.hl-late { color: var(--danger); font-weight: 700; }
.hl-amt { font-weight: 700; font-variant-numeric: tabular-nums; }
/* BUG-052 E2：两颗是裁决键（收到了 / 没找到），**按错一颗就是把已付判成没付** —— 间距从 6 提到 8 */
.hl-acts { display: flex; gap: 8px; width: 100%; margin-top: 4px; }
.hl-ok, .hl-no {
  border-radius: 8px; border: 1.5px solid var(--border); background: var(--card);
  font-family: inherit; font-size: 12.5px; font-weight: 700;
  /* BUG-052：实测命中区 36.8 —— 这一屏第一次进闸 1 的量程（老师端此前没跑过）。
     这两颗是裁决「她说线下付过了」的那一下，按错就是把已付判成没付：撑到 44。 */
  padding: 11px 12px; min-height: 44px; cursor: pointer;
}
.hl-ok { border-color: var(--success); color: var(--success); }
.hl-no { color: var(--text-secondary); }
.hl-ok:active, .hl-no:active { opacity: 0.85; }

.charge-list { list-style: none; margin: 0 0 12px; padding: 0; }
.charge-item { padding: 10px 0; border-bottom: 1px solid var(--border); }
.charge-item:last-child { border-bottom: none; }
.ch-head { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; }
.ch-title { font-size: 14.5px; font-weight: 700; }
.ch-amt { font-weight: 700; font-variant-numeric: tabular-nums; }
.ch-kind {
  font-size: 11px; font-weight: 700; color: var(--text-secondary);
  background: var(--bg); border-radius: 999px; padding: 2px 8px;
}
.ch-sub { font-size: 12.5px; color: var(--text-secondary); margin-top: 3px; }
.ch-unpaid { list-style: none; margin: 6px 0 0; padding: 0; }
.ch-unpaid li { font-size: 12.5px; color: var(--text-secondary); padding: 2px 0; }

/* 建单时的「先不通知」 */
.cf-check { display: flex; align-items: center; gap: 8px; margin: 10px 0 2px;
            font-size: 13px; cursor: pointer; }
.cf-check input { width: 16px; height: 16px; }

/* **静音建的单要显眼。** 它不是一条状态说明，是一件还没做完的事 ——
   不显眼的话它会一直静着，直到某天以「逾期」的身份第一次出现在家长眼前。 */
.ch-unannounced {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-top: 8px; padding: 8px 10px; border-radius: 10px;
  background: #FFF7ED; border: 1px solid #FED7AA;
  font-size: 12.5px; color: #9A3412;
}
.ch-announce {
  /* BUG-052：同一屏上的第五颗，命中区 36.8 → 44 */
  flex-shrink: 0; border: none; border-radius: 999px; padding: 11px 14px;
  min-height: 44px; font-family: inherit; font-size: 12.5px; font-weight: 700;
  background: #C2410C; color: #fff; cursor: pointer;
}
.ch-announce:hover { background: #9A3412; }
.ch-announce:active { opacity: 0.85; }

.charge-form { margin-top: 12px; padding-top: 12px; border-top: 1.5px solid var(--border); }
.cf-label {
  display: block; font-size: 12.5px; font-weight: 700;
  color: var(--text-secondary); margin: 10px 0 5px;
}
.cf-input {
  width: 100%; box-sizing: border-box; padding: 10px 12px;
  border: 1.5px solid var(--border); border-radius: 10px;
  font-size: 15px; font-family: inherit;
  background: var(--card); color: var(--text); outline: none;
}
.cf-input:focus { border-color: var(--primary); }
.cf-money { display: flex; align-items: center; gap: 8px; }
.cf-money span { font-size: 18px; font-weight: 800; color: var(--text-secondary); }
.cf-radios { display: flex; gap: 8px; flex-wrap: wrap; }
.cf-radios label {
  display: flex; align-items: center; gap: 6px;
  background: var(--bg); border: 1.5px solid var(--border); border-radius: 10px;
  padding: 9px 12px; font-size: 13.5px; font-weight: 600; cursor: pointer; min-height: 44px;
  box-sizing: border-box;
}
.cf-radios label:has(input:checked) { border-color: var(--primary); background: var(--primary-light); }
/* 发出去之前就把「接下来会发生什么」说清楚 —— 催缴是会真发短信的 */
.cf-hint { font-size: 12.5px; line-height: 1.5; color: var(--text-secondary); margin: 12px 0 0; }
.cf-actions { display: flex; gap: 10px; margin-top: 12px; }
.cf-actions button { flex: 1; }
.cf-err { margin-top: 8px; font-size: 13px; color: var(--danger); font-weight: 600; }
.cf-err[hidden] { display: none; }

/* 发送失败的横幅不能长得像成功 */
.sent-banner.failed { background: var(--danger); color: #fff; }

/* ── Telegram 覆盖 ────────────────────────────────── */
.tg-coverage { margin: 10px 0; padding: 10px 12px; background: var(--bg); border-radius: 10px; }
.tg-coverage[hidden] { display: none; }
.tgc-head {
  display: flex; justify-content: space-between; gap: 10px;
  font-size: 12.5px; font-weight: 700; margin-bottom: 6px;
}
.tgc-count { color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.tgc-group { display: flex; gap: 8px; align-items: baseline; margin-top: 5px; font-size: 12px; }
.tgc-group[hidden] { display: none; }
.tgc-label { font-weight: 700; color: var(--text-secondary); flex-shrink: 0; }
/* 拉黑的不能长得像「还没连」—— 那一户以为自己还连着 */
.tgc-label.bad { color: var(--danger); }
.tgc-names { color: var(--text-secondary); line-height: 1.5; }

/* 「这条不能等」—— 只在必达档出现 */
.wake-now {
  display: flex; align-items: center; gap: 8px; margin-top: 8px;
  font-size: 13px; font-weight: 600; cursor: pointer;
}
.wake-now[hidden] { display: none; }

/* ── 出席回执：老师填的两问（PD-0084 ① · t-253 · S-023 §2）──
   attend 选中才出现；缩进 + 左侧竖线（settings 的 .sub-control 是那一页自己的，这里同一个样子自己写一份）。
   数字格与三颗单选都要 ≥44 的有效命中盒（S-022 §1）：单选 label 包 input、行高 44；数字格自己 44。 */
.attend-fields { margin-top: 12px; padding: 4px 0 4px 14px; border-left: 3px solid var(--primary-light); }
.attend-fields .attend-min { display: block; font-size: 13px; font-weight: 600; color: var(--text-secondary); margin-bottom: 6px; }
.attend-fields #attend-min { width: 96px; min-height: 44px; box-sizing: border-box; font-variant-numeric: tabular-nums; }
.attend-fields .attend-kids-label { font-size: 13px; font-weight: 600; color: var(--text-secondary); margin: 14px 0 2px; }
.attend-fields .attend-kids-opt { display: flex; align-items: center; gap: 10px; min-height: 44px; font-size: 14px; font-weight: 500; color: var(--text); cursor: pointer; }
.attend-fields .attend-kids-opt input { margin: 0; width: 18px; height: 18px; accent-color: var(--primary); flex-shrink: 0; }
.attend-fields #attend-kids-note { margin-top: 6px; min-height: 64px; }

/* webhook 指着谁 —— 「配好了」和「发得出去」之间还有这一格 */
.tg-webhook { margin-top: 10px; }
.tgw-state { font-size: 12.5px; line-height: 1.5; margin-bottom: 8px; }
.tgw-state.ok  { color: var(--success, #2E7D4F); }
.tgw-state.warn { color: #9A3412; }
.tgw-state.bad { color: var(--danger, #B42318); font-weight: 600; }

/* ── 接下来七天 ─────────────────────────────────── */
.upcoming-head { margin: 16px 0 12px; }
.upcoming-list { list-style: none; margin: 0; padding: 0; }
.upcoming-item {
  display: flex; align-items: baseline; gap: 10px;
  padding: 10px 0; border-top: 1px solid var(--hairline);
}
.upcoming-item:first-child { border-top: 0; }
.up-when {
  flex-shrink: 0; font-size: 11px; font-weight: 700; white-space: nowrap;
  padding: 3px 9px; border-radius: 999px;
  background: var(--tag-neutral-bg); color: var(--tag-neutral-fg);
}
.up-what { font-size: 14px; font-weight: 600; flex: 1; min-width: 0; }
.up-sub {
  display: block; font-size: 12px; font-weight: 400; color: var(--text-secondary);
}

/* 日历是 Today 底下的子页，不在底部导航里 —— 所以必须自己带一条回去的路。 */
.cal-back-btn {
  background: none; border: none; padding: 4px 0 12px;
  font-family: inherit; font-size: 14px; font-weight: 600;
  color: var(--primary); cursor: pointer; min-height: 44px;
}
/* ── 建单：给谁 ─────────────────────────────────────── */
/* 学生一行一个，勾上之后下面展开他的监护人。默认全勾、可逐个取消，至少留一位 */
.cf-students { margin-top: 8px; display: flex; flex-direction: column; gap: 6px; }
.cf-student {
  border: 1.5px solid var(--border); border-radius: 10px; padding: 8px 10px;
  background: var(--bg);
}
.cf-student:has(.cf-student-box:checked) { border-color: var(--primary); background: var(--card); }
.cf-student-head { display: flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 700; cursor: pointer; }
.cf-student-head input { width: 16px; height: 16px; }
.cf-student.is-orphan .cf-student-head { color: var(--text-secondary); cursor: default; }
.cf-orphan-note { font-size: 12px; color: var(--danger); font-weight: 600; margin: 4px 0 0 24px; }
.cf-guardians { display: flex; flex-direction: column; gap: 4px; margin: 6px 0 2px 24px; }
.cf-guardians[hidden] { display: none; }
.cf-guardian { display: flex; align-items: center; gap: 8px; font-size: 13px; cursor: pointer; }
.cf-guardian input { width: 15px; height: 15px; }
.cf-guardian .cf-rel { color: var(--text-secondary); font-size: 12px; }
/* ── 建单：确认页 ───────────────────────────────────── */
.cf-confirm { margin-top: 12px; padding-top: 12px; border-top: 1.5px solid var(--border); }
.cf-confirm-title { font-size: 15px; font-weight: 800; margin-bottom: 8px; }
.cf-summary { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 6px 12px; font-size: 13.5px; }
.cf-summary dt { color: var(--text-secondary); font-weight: 600; }
.cf-summary dd { margin: 0; }
.cf-summary .cf-sum-amt { font-weight: 800; font-size: 16px; }
.cf-summary ul { margin: 0; padding-left: 16px; }
.cf-summary li { margin: 2px 0; }
.cf-reach { font-weight: 700; color: var(--primary); }

/* 取消账单：与「Tell them now」同一位置，但它是破坏性的，用描边不用实心 */
.ch-actions { display: flex; justify-content: flex-end; margin-top: 8px; }
.ch-cancel {
  background: transparent; color: var(--danger); border: 1.5px solid var(--danger);
  border-radius: 999px; font-family: inherit; font-size: 12px; font-weight: 700;
  /* 实测命中区 26.8px。**破坏性动作太小是两头错**：要按的时候按不中，
     不想按的时候擦到。看得见的胶囊不变大，靠 padding 把命中区撑到 44。 */
  padding: 0 14px; min-height: 44px; display: inline-flex; align-items: center;
  cursor: pointer;
}
.ch-cancel:active { opacity: 0.8; }

/* ── 建单：名册里没有的学生在这里建 ─────────────────── */
.cf-add-student { display: grid; grid-template-columns: 1fr 1fr auto; gap: 6px; margin-top: 8px; }
.cf-add-student .cf-input { margin: 0; }
.cf-add-student button { white-space: nowrap; }
@media (max-width: 420px) { .cf-add-student { grid-template-columns: 1fr; } }
.cf-add-parent { display: flex; gap: 6px; margin: 6px 0 2px 24px; align-items: center; }
.cf-add-parent select { flex: 1; min-width: 0; font-size: 12.5px; padding: 6px 8px; }
/* 「一条都没送出去」不能和「送出去了」长一个样（T059）。
   它不是错误，是一个她必须看见的事实：记录写了，东西没出去。 */
#rh-delivered.rh-none { color: var(--danger); font-weight: 700; }

/* **这个类此前只在 `.cf-add-parent` 里被定过尺寸，本体一直没定义** ——
   于是名册那两颗（「Add student」「Add a teacher」）一直是浏览器默认按钮：
   量出来 #efefef 底、13.33px 字、20px 高。**它们不是风格不同，是漏了**
   （T052 / PM 广播 107-pm）。这里补上本体；`.cf-add-parent` 那条更具体，
   仍然覆盖尺寸，那处的紧凑排布不变。 */
/* 邀请入口（T073）。**三个数各占一行** —— 合成一句「已发送」的话，
   「一个都没发」和「全发了」在屏幕上长得一样。 */
.invite-box { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
.invite-hint { font-size: 12px; color: var(--text-secondary); line-height: 1.5; margin: 4px 0 10px; }
.invite-note { width: 100%; margin-bottom: 8px; font-family: inherit; }
.invite-note-err { font-size: 12px; color: var(--danger); margin-bottom: 8px; line-height: 1.45; }
.invite-note-err[hidden] { display: none; }
.invite-result { margin-top: 10px; font-size: 13px; line-height: 1.7; }
.invite-result[hidden] { display: none; }
/* 一条都没发出去时，那句话不能长得像成功 */
.invite-result.is-nothing { color: var(--danger); font-weight: 600; }

/* 注册状态直接写在名字这一行上 —— 老师问的是「还有谁没进来」 */
.inv-tag { margin-left: 6px; font-size: 11px; font-weight: 700; padding: 1px 6px; border-radius: 6px; }
.inv-tag.is-reg { background: #E7F0E4; color: #2C4428; }
.inv-tag.is-unreg { background: #FDECEA; color: #C24E42; }
.roster-head .invite-pick { margin-right: 8px; width: 16px; height: 16px; }
.roster-meta.is-warn-text { color: var(--danger); }

.btn-secondary {
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--bg); color: var(--text); border: 1px solid var(--border);
  /* 44 是 #105（T054 ④）定下的可点下限，这里照它来 */
  border-radius: 10px; padding: 0 16px; min-height: 44px;
  font-family: inherit; font-size: 13px; font-weight: 700; cursor: pointer;
}
.btn-secondary:hover { background: var(--card); }
.btn-secondary:active { transform: scale(0.98); }
.btn-secondary:disabled { opacity: 0.5; cursor: not-allowed; }
.cf-add-parent .btn-secondary { font-size: 12px; padding: 6px 10px; }
/* 「新家长」三格（T019）：选到 + New parent… 才出现；每个学生下面那份缩进对齐 select。 */
.cf-new-parent { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin: 6px 0 4px; }
.cf-new-parent .cf-input { margin: 0; }
.cf-new-parent .cf-np-email, .cf-new-parent .cf-hint { grid-column: 1 / -1; }
.cf-new-parent .cf-hint { margin: 0; }
.cf-new-parent[hidden] { display: none; }
.cf-new-parent-inline { margin-left: 24px; }
/* 静态表单里那份放在 grid 内：紧跟 select、整行铺开，按钮落到它下面。 */
.cf-add-student .cf-new-parent { grid-column: 1 / -1; }
@media (max-width: 420px) { .cf-new-parent { grid-template-columns: 1fr; } }

/* ── 名册 ───────────────────────────────────────────── */
.roster-sub { font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-secondary); margin: 14px 0 6px; }
.roster-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.roster-item { border: 1.5px solid var(--border); border-radius: 10px; padding: 8px 10px; background: var(--bg); }
.roster-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.roster-name { font-size: 14px; font-weight: 700; }
.roster-meta { font-size: 12px; color: var(--text-secondary); }
.roster-item.is-warn { border-color: var(--accent); }
.roster-item.is-warn .roster-meta { color: var(--accent-deep); font-weight: 600; }
.roster-guardians { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; }
.roster-guardian { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 13px; }
.roster-guardian .cf-rel { color: var(--text-secondary); font-size: 12px; margin-left: 6px; }
.roster-x {
  background: transparent; color: var(--text-secondary); border: 1.5px solid var(--border);
  border-radius: 999px; font-family: inherit; font-size: 11.5px; font-weight: 700; padding: 3px 9px; cursor: pointer;
}
.roster-x.is-danger { color: var(--danger); border-color: var(--danger); }
.roster-actions { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }

/* 渠道没配好：给老师的是一句话，变量名折在 Details 里给排障的人 */
.ch-details { margin-top: 4px; font-size: 11.5px; color: var(--text-secondary); }
.ch-details summary { cursor: pointer; font-weight: 600; }

/* ── 发帖前的确认页 ─────────────────────────────── */
.rv-section { margin-top: 12px; }
.rv-head { display: flex; justify-content: space-between; align-items: center; font-size: 12px; font-weight: 800;
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-secondary); margin-bottom: 4px; }
.rv-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.rv-item { border: 1.5px solid var(--border); border-radius: 10px; padding: 8px; background: var(--bg); }
.rv-item.is-warn { border-color: var(--accent); }
.rv-item .cf-input { margin: 0; }
.rv-row { display: flex; gap: 6px; align-items: center; margin-top: 6px; flex-wrap: wrap; }
.rv-row .rv-when { flex: 0 0 150px; }
.rv-row .rv-when-text { flex: 1 1 120px; }
.rv-row .rv-name { flex: 0 0 120px; }
.rv-row .rv-text { flex: 1 1 160px; }
.rv-check { display: flex; align-items: center; gap: 4px; font-size: 12px; white-space: nowrap; }
.rv-match { font-size: 12px; color: var(--primary); font-weight: 700; margin-top: 4px; }
.rv-match.is-warn { color: var(--accent-deep); }

/* **.lead-only 的兜底必须写在机制上，不能逐个类补。**
   `hidden` 是 UA 样式（display:none），任何作者写的 display 都会盖过它 ——
   .class-new 是 display:flex，于是设了 hidden 也照样显示，助教能看见「建班」。
   这个仓库里有四十来条 `.foo[hidden] { display:none }` 就是在逐个补这件事；
   但 .lead-only 会被挂到任意元素上，逐个补必然漏掉。所以在这里 !important。 */
.lead-only[hidden] { display: none !important; }

/* ── 教职工 ──────────────────────────────────────── */
.staff-list { list-style: none; margin: 0 0 12px; padding: 0; }
.staff-item {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 10px 0; border-bottom: 1px solid var(--hairline); font-size: 14px;
}
.staff-item:last-child { border-bottom: none; }
.staff-name { font-weight: 700; }
.staff-user { color: var(--text-secondary); font-size: 12.5px; }
/* 「收不到通知」不是一句说明，是一件没做完的事：安全举报的必达通知发给她，
   没号码那条告警就是哑的。所以它占一整行，不缩在角落里。 */
.staff-quiet {
  width: 100%; font-size: 12px; color: var(--tag-warning-fg);
  background: var(--tag-warning-bg); border-radius: 6px; padding: 4px 8px;
}
.staff-acts { display: flex; gap: 6px; width: 100%; margin-top: 4px; }
.staff-rank, .staff-remove {
  border-radius: 8px; border: 1.5px solid var(--border); background: var(--card);
  font-family: inherit; font-size: 12.5px; font-weight: 700;
  padding: 7px 12px; min-height: 36px; cursor: pointer;
}
.staff-remove { color: var(--danger); border-color: #E7C6C1; }
.staff-form { margin-top: 12px; padding-top: 12px; border-top: 1.5px solid var(--border); }

/* 一次性密码。**关掉就再也拿不到了**，所以它得压过这一屏上别的东西。 */
.staff-pw {
  margin-top: 12px; padding: 14px; border-radius: 12px;
  background: #FFF7ED; border: 1.5px solid #FED7AA;
}
.sp-head { font-size: 13px; font-weight: 700; color: #9A3412; }
.sp-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 22px; font-weight: 700; letter-spacing: 1px;
  margin: 8px 0; color: #7C2D12; user-select: all; word-break: break-all;
}
.sp-hint { font-size: 12px; color: #9A3412; line-height: 1.5; margin: 0 0 10px; }

/* ── 班级 ────────────────────────────────────────── */
.class-list { list-style: none; margin: 0 0 12px; padding: 0; }
.class-item { padding: 10px 0; border-bottom: 1px solid var(--hairline); }
.class-item:last-child { border-bottom: none; }
.class-item.is-archived { opacity: 0.6; }
.class-head { display: flex; align-items: center; gap: 8px; }
.class-name { font-size: 14.5px; font-weight: 700; flex: 1; min-width: 0; }
.class-archive {
  background: none; border: none; font-family: inherit; font-size: 12.5px;
  font-weight: 600; color: var(--text-secondary); cursor: pointer;
  padding: 6px 2px; min-height: 36px;
}
.class-who { display: block; font-size: 12.5px; color: var(--text-secondary); margin-top: 3px; }
/* 「这个班还没有老师」是要处理的事，不是一行空白 */
.class-who.none { color: var(--tag-warning-fg); font-weight: 600; }
.class-add {
  margin-top: 8px; width: 100%; padding: 9px 10px; border-radius: 8px;
  border: 1.5px solid var(--border); background: var(--card);
  font-family: inherit; font-size: 13px; color: var(--text-secondary); min-height: 44px;
}
.class-new { display: flex; gap: 8px; align-items: center; }
.class-new .cf-input { flex: 1; margin: 0; }
.cc-head {
  font-size: 12px; font-weight: 700; color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: 0.4px; margin: 14px 0 8px;
}

/* 未同意短信的家长（T025）：必达档发布前的名单。不是错误，是「这几户你得自己联系」。 */
/* S-003 §5：这一格今天有**六个写死的琥珀**，收敛到 token。
   替换前后都量过（D-004 ③：换了之后必须仍在门槛之上；低于替换前要写明换来什么）：
       标题/正文   #7C4A0A 6.93  →  var(--tag-warning-fg) #984D16  5.39 ✅
       名字那一行  #5B3A08 9.59  →  var(--text)          #22301F  12.16 ✅ **比今天更高**
   名字那一行**反而更清楚**是有理由的：它是她要照着打电话的东西。 */
.no-consent-box {
  margin: 10px 0 4px; padding: 10px 12px;
  background: var(--tag-warning-bg); border-left: 3px solid var(--accent-deep);
  border-radius: 8px; font-size: 12.5px; line-height: 1.55; color: var(--tag-warning-fg);
}
.no-consent-box[hidden] { display: none; }
.no-consent-title { font-weight: 700; margin-bottom: 4px; }
.no-consent-names {
  color: var(--text);
  /* **最后一个名字被吃掉，那户就没人打电话。** 所以这一行只许换行，
     不许横滚、不许省略号 —— 理由不是排版。 */
  overflow-wrap: anywhere;
}
/* 两块叠在同一个盒子里时要看得出是两件事 —— 上面那块是**她的待办**
   （还没选过怎么接收，可以再邀请一次），下面那块是**那户的决定**（别去催）。
   连成一段读，她会把两份名字当成同一种情况处理。（BUG-030） */
.no-consent-title.is-todo { margin-top: 0; }
/* 从「第二段起」推广到「第二、三段」（S-003 §2）：三段同构，一段都不许长得特别。 */
.no-consent-title.is-todo ~ .no-consent-title {
  margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border);
}
/* 名册与追踪里的小标记 */
.nc-tag {
  display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 999px;
  background: #FEF3C7; color: #92400E; font-size: 11px; font-weight: 700; white-space: nowrap;
}

/* 渠道那一段的范围说明（design S-002 §7）：它长在 Class 里只是因为
   老师只有这一个后台入口 —— 但它管的是整所学校。 */
.channel-scope-hint {
  font-size: 12px; line-height: 1.45; color: var(--text-secondary);
  margin: 2px 0 10px;
}

/* 相册那一屏现在是只读的（S-002 §6）：配置权归相册自己那一屏，
   老师在这里要的是**处置**，入口是举报队列（那里有留痕）。 */
.album-flags { font-size: 12px; color: var(--text-secondary); flex-shrink: 0; white-space: nowrap; }

/* ── S-002 / T092：一级五张小结卡 + 二级浮出层 ──────────────
   一级：**整卡可点 · 一行状态 · 零个输入控件**（D-001）。
   改之前这一页高 4398px（5.2 屏），而它没有一句话回答「班里有几个学生」。 */
.class-cards { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.class-card {
  display: block; width: 100%; text-align: left;
  background: var(--card); border: none; border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 14px 16px;
  font-family: inherit; cursor: pointer;
  min-height: 44px;                     /* UX-004 */
}
.class-card:active { background: var(--bg); }
.cc-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.cc-title { font-size: 15px; font-weight: 700; color: var(--text); }
.cc-chev { font-size: 20px; line-height: 1; color: var(--text-secondary); flex-shrink: 0; }
.cc-status { display: block; margin-top: 4px; font-size: 13px; color: var(--text-secondary); }
/* 需要她注意的那一行 —— 不是报错，是「这里还差点什么」 */
.cc-warn {
  display: inline-block; margin-top: 6px; padding: 2px 8px; border-radius: 999px;
  font-size: 12px; font-weight: 600;
  background: var(--tag-warning-bg, #FDF2D8); color: var(--tag-warning-fg, #7A5310);
}

/* 二级：共用一个壳，一次只开一个。返回键在顶栏左上 —— 和帖子详情同一个位置。 */
.class-sheet {
  position: fixed; inset: 0; z-index: 500;
  background: var(--bg); display: flex; flex-direction: column;
}
.class-sheet[hidden] { display: none; }
.cs-topbar {
  display: flex; align-items: center; gap: 6px;
  /* UIV-14：顶栏底色和全站另外几条一样用 --primary-deep（这条原来浅一号）；内边距照画板 12 / 16 */
  background: var(--primary-deep); color: #fff;
  padding: 12px 16px calc(12px + env(safe-area-inset-top, 0px));
  flex-shrink: 0;
}
.cs-back {
  background: none; border: none; color: #fff; cursor: pointer;
  font-size: 26px; line-height: 1;
  width: 44px; height: 44px;            /* UX-004：图标按钮 44×44 */
  display: flex; align-items: center; justify-content: center;
}
.cs-title { font-family: var(--font-display); font-size: 16px; font-weight: 700; min-width: 0; }   /* UIV-14：标题槽位显式用展示字体 */
.cs-body {
  flex: 1; overflow-y: auto;
  padding: 16px 16px calc(24px + env(safe-area-inset-bottom, 0px));
}
.cs-sub {
  font-size: 12px; font-weight: 700; letter-spacing: 0.4px; text-transform: uppercase;
  color: var(--text-secondary); margin: 4px 0 8px;
}
.cs-primary { width: 100%; margin-top: 14px; min-height: 48px; }
.cs-actions { display: flex; gap: 10px; margin-top: 16px; }
.cs-actions button { flex: 1; min-height: 44px; }
/* 破坏性动作单独一段，和上面隔开 —— 它不该长在扫视路径上（T084） */
/* **不满宽**：满宽 + 红框会让这一屏最突出的东西是「删掉」（T084，用户原话：
   「Remove 变成了红主引导，这是不对的」）。它仍然自成一段、仍然是红的 ——
   只是不再是版面上最大的那一块。实测改前 358×44，改后 226×44。 */
.cs-danger {
  margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--border);
  display: flex; justify-content: center;
}

/* 名册行：整行可点，进孩子详情 */
.roster-item.is-tappable { padding: 0; }
.roster-row {
  display: grid; grid-template-columns: 1fr auto; align-items: center;
  column-gap: 10px; row-gap: 2px;
  width: 100%; background: none; border: none; text-align: left;
  font-family: inherit; cursor: pointer; padding: 12px 14px; min-height: 44px;
}
.roster-row .roster-name { grid-column: 1; font-size: 15px; font-weight: 600; }
.roster-row .roster-meta { grid-column: 1; font-size: 13px; color: var(--text-secondary); }
.roster-row .cc-chev { grid-column: 2; grid-row: 1 / span 2; }

/* 「选已有的家长」是默认呈现的那一支，不是下拉里的一个选项 */
.ag-list { list-style: none; margin: 0 0 10px; padding: 0; }
.ag-opt {
  display: grid; grid-template-columns: auto 1fr; column-gap: 10px; align-items: center;
  padding: 10px 4px; min-height: 44px; border-bottom: 1px solid var(--border);
  cursor: pointer;
}
.ag-opt input { width: 20px; height: 20px; }
.ag-name { font-size: 15px; font-weight: 600; color: var(--text); }
.ag-of { grid-column: 2; font-size: 12px; color: var(--text-secondary); }

.child-guardians { list-style: none; margin: 0; padding: 0; }
.child-guardian {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 12px 0; border-bottom: 1px solid var(--border);
}
.cg-name { font-size: 15px; font-weight: 600; flex: 1; min-width: 0; }
.cg-phone { font-size: 13px; color: var(--text-secondary); }
.child-guardian .btn-danger { min-height: 44px; padding: 0 12px; }

/* ── S-001 / T093：Post 一级页面 ────────────────────────────
   小结 + 筛选**同一行**（上一版上下两行，把「已发过」那一组挤出了屏幕）。 */
.post-summary {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 4px 0 10px;
}
.ps-text { font-size: 13.5px; font-weight: 600; color: var(--text); min-width: 0; }
.post-group-title {
  font-size: 12px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-secondary); line-height: 30px;
}
.post-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.post-divider { height: 1px; background: var(--border); margin: 14px 0; }

/* 等待卡：标题整行宽（**行尾不放 action_type 胶囊** —— 那是重复信息穿着
   按钮的衣服坐在按钮的位置上，D-003），第二行左计数右催办。 */
.post-waiting {
  /* UIV-14：这一类主内容卡画板是 18（--radius-card），不是通用的 16 */
  background: var(--card); border-radius: var(--radius-card); box-shadow: var(--shadow);
  padding: 12px 14px; display: flex; flex-direction: column; gap: 8px;
}
.pw-open {
  display: block; width: 100%; text-align: left; background: none; border: none;
  font-family: inherit; cursor: pointer; padding: 0; min-height: 24px;
  position: relative;
}
/* 实测命中区 25px，而**这是进这条帖子的唯一入口**，她每天都在这儿点。
   用伪元素把命中区撑到 44，**不是加 padding** —— 这一页有一列卡片，
   每张长高 20px 会把下面的卡片推到导航栏底下（我第一版就是这么干的，
   ux 的尺子当场少数出一个目标：它没有变小，是被推到看不见的地方去了）。
   向上吃掉卡片自己的 12px 内边距、向下吃掉那 8px 间隙：24+12+8 = 44，
   **正好停在「Remind N families」那颗按钮之前**，不抢它的点击。
   这套手法这个仓库用过（T066 的 26px 小圆），ux 的尺子也认得它。 */
.pw-open::after {
  content: ''; position: absolute; left: 0; right: 0; top: -12px; bottom: -8px;
}
.pw-title { font-size: 14px; font-weight: 700; color: var(--text); }   /* UIV-14：画板 14 */
.pw-row { display: flex; align-items: center; gap: 10px; }
.pw-left { flex: 1; min-width: 0; }
/* **每个数字带着它的名字**：「9/15 signed」，不是「9/15」也不是「60%」 */
.pw-count { font-size: 12px; font-weight: 400; color: var(--text-secondary); }   /* UIV-14：画板 12 / 常规字重 */
.pw-bar {
  margin-top: 4px; height: 6px; border-radius: 999px;   /* UIV-14 / S-020 §2：画板 6 */
  background: var(--tag-neutral-bg, #EDF1EA); overflow: hidden;
}
/* S-020 §2：填充与「今日」页那条同一套两态（.is-chase 要追 = 橙 · .is-steady 还行 = 绿） */
.pw-bar span { display: block; height: 100%; background: var(--primary); border-radius: 999px; }
.pw-bar span.is-chase { background: var(--accent); }
.pw-bar span.is-steady { background: var(--primary); }
/* S-020 §2 v1.5：催办两处合成一颗 —— 这颗改用 .remind-pill（原来是 .btn-secondary 米底描边、10 圆角）。
   命中区 44 由 .remind-pill 自己带（内边距撑 + 负外边距收回）。 */
.pw-remind { flex-shrink: 0; }

/* 已发过：一行，没有进度条 */
.post-done { }
.pd-open {
  display: flex; align-items: center; gap: 10px; width: 100%;
  background: var(--card); border: none; border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 0 14px; min-height: 48px;
  font-family: inherit; cursor: pointer; text-align: left;
}
.pd-title { flex: 1; min-width: 0; font-size: 14.5px; font-weight: 600; color: var(--text); }
.pd-all { font-size: 12.5px; font-weight: 700; color: var(--success, #1F7A4D); flex-shrink: 0; }
.pd-date { font-size: 12.5px; color: var(--text-secondary); flex-shrink: 0; }

/* 折叠按钮：**规则写在按钮上** —— 一个不说明自己规则的折叠，
   和随机截断在屏幕上是同一个东西。 */
.post-more {
  width: 100%; min-height: 44px; margin-top: 10px;
  background: none; border: 1px dashed var(--border); border-radius: 10px;
  font-family: inherit; font-size: 12.5px; font-weight: 700; color: var(--text-secondary);
  cursor: pointer;
}
.post-more[hidden] { display: none; }

/* 一级页面上只留这颗圆形加号（用户原话）。和 Bills 那颗同一个形状 —— 那是基准。 */
/* FAB 是 fixed 的，会盖住最后一行 —— 给这一屏多留一个 FAB 的高度。
   （FAB 本体用 shared.css 的 `.fab`，不在这里新造。） */
#tab-post { padding-bottom: calc(var(--nav-height) + 96px + env(safe-area-inset-bottom, 0px)); }

/* `.btn-danger` 的本体**搬去了 shared.css**（design v2.1 §3 记的那一条：
   家长端不加载 teacher.css，一旦用到就重演「有类名、没本体」）。
   这里只留这一页自己的行为覆盖。 */
.btn-danger:hover { background: var(--tag-danger-bg, #FDECEC); }
.btn-danger:active { transform: scale(0.99); }

/* T080 ②：CSV 那一档的文件入口 */
.import-file-row { margin: 10px 0 0; }
.import-file-row[hidden] { display: none; }
.import-file-row input[type="file"] { font-family: inherit; font-size: 13px; }
.import-file-note {
  margin-top: 8px; font-size: 12.5px; color: var(--text-secondary);
}
.import-file-note[hidden] { display: none; }

/* ── 家长名册（S-002 §4.1 / T106）─────────────────────────────
   「注册了没有」是名册的一列，不是一颗按钮 —— 这一屏不请她做任何事。
   要发邀请去「Invite to the hub」那一屏。 */
.pr-reg, .pr-unreg {
  display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 999px;
  font-size: 11px; font-weight: 700; white-space: nowrap;
}
.pr-reg { background: var(--tag-success-bg); color: var(--tag-success-fg); }   /* t-215 · D-006 §3：Tailwind 绿 → token 绿（6.49 → 9.02） */
.pr-unreg { background: var(--tag-warning-bg, #FEF3C7); color: var(--tag-warning-fg, #92400E); }

/* .cs-sub 是**分段标签**，所以是全大写。带人名或整句话的那几处不能跟着大写 ——
   「GUARDIAN FOR MIA CHEN, RUBY CHEN」读起来像喊，而且人名全大写会被当成缩写。 */
.cs-sub.is-sentence {
  text-transform: none; letter-spacing: 0; font-size: 13px; font-weight: 600;
  color: var(--text);
}
/* 没号码的家长：这一句是**她收不到东西**的原因，不是一句装饰 */
.cs-sub.is-warn-text { color: var(--danger); text-transform: none; letter-spacing: 0; }

/* 家长详情里的解绑：和 .cs-danger 一样不长在扫视路径上，但它是逐个孩子的 */
.pd-unlink { margin-left: auto; padding: 0 12px; min-height: 44px; }

/* 附言下面那一行说明：链接由系统加。**在她写之前就说**，不是发送时才拒。 */
.invite-note-hint {
  font-size: 12px; line-height: 1.45; color: var(--text-secondary); margin: -4px 0 10px;
}
#pd-children .roster-head { align-items: center; }

/* 邀请那一屏的行：勾选框在最左，名字**紧挨着它**。
   `.roster-head` 默认是 space-between（给「名字 … 右侧状态」那种行用的），
   这一行有勾选框，于是名字被推到了右边缘 —— 截图上看像是排版坏了。 */
#invite-list .roster-head { justify-content: flex-start; gap: 8px; }

/* ── 说得出错的那个地方（T064）────────────────────────────────
   **不是 toast**：出错要她做点什么，而 toast 几秒后就没了 ——
   一句她还没读完就消失的话等于没说。成功照旧用 toast。
   固定在顶部，因为失败可能发生在任何一屏、任何滚动位置。 */
.teacher-problem {
  position: fixed; left: 12px; right: 12px;
  top: calc(12px + env(safe-area-inset-top, 0px));
  z-index: 9000;
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 12px 12px 14px;
  background: #FDECEA; color: #7A2A20;
  border: 1px solid #E4B4AC; border-left: 4px solid var(--danger, #B42318);
  border-radius: 12px; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
  font-size: 13.5px; line-height: 1.5;
}
.teacher-problem[hidden] { display: none; }
.tp-text { flex: 1; min-width: 0; font-weight: 600; }
.tp-close {
  flex: none; background: none; border: none; color: inherit;
  font-size: 20px; line-height: 1; cursor: pointer;
  min-width: 44px; min-height: 44px;      /* UX-004：它自己也是个可点目标 */
  margin: -12px -12px -12px 0;
}

/* ── T108：停用但仍可见（product 441 ②）───────────────────────
   一个档位，不是一个错误。姓名与孩子仍然读得出来，整行降到次级，
   一个中性标签。**不许**红色 / ⚠️ / 「已删除」—— 她不是一个问题，她是一个状态。 */
.roster-item.is-inactive {
  display: flex; align-items: center; gap: 8px;
}
.roster-item.is-inactive .roster-row {
  flex: 1; min-width: 0; opacity: 0.62;   /* 降次级：弱化，不是报警色 */
}
.inactive-tag {
  display: inline-block; margin-left: 8px; padding: 1px 8px;
  background: var(--tag-neutral-bg, #EEF0EE); color: var(--text-secondary);
  border-radius: 999px; font-size: 11px; font-weight: 700;
  letter-spacing: 0.2px; vertical-align: middle;
}
.roster-restore { flex: 0 0 auto; }

/* T113：顶上那一行 —— 没有它她会以为产品改了她的帖子 */
.pv-asfamilies {
  font-size: 12px; font-weight: 700; color: var(--text-secondary);
  background: var(--tag-neutral-bg, #EEF0EE); border-radius: 8px;
  padding: 6px 10px; margin-bottom: 10px;
}
.pv-track { margin-top: 4px; }
/* ── T109：成品预览（家长会收到的那份小结）─────────────────
   她按下发出之前看到的，就是家长会看到的东西。不带打钩 ——
   她不是在完成这些事，她是在看家长会收到什么。 */
.rv-preview { margin: 8px 0 4px; }
.rvp-summary { margin: 0 0 10px; font-size: 14px; line-height: 1.5; color: var(--text); }
.rvp-section { margin-top: 12px; }
.rvp-head {
  font-size: 11px; font-weight: 800; letter-spacing: 0.6px;
  text-transform: uppercase; color: var(--text-secondary); margin-bottom: 4px;
}
.rvp-list { list-style: none; margin: 0; padding: 0; }
.rvp-list li {
  padding: 7px 0; border-bottom: 1px solid var(--border);
  font-size: 13.5px; line-height: 1.45; color: var(--text);
}
.rvp-list li:last-child { border-bottom: none; }
.rvp-tag {
  display: inline-block; margin-left: 6px; padding: 1px 8px;
  background: var(--tag-neutral-bg, #EEF0EE); color: var(--text-secondary);
  border-radius: 999px; font-size: 11px; font-weight: 700;
}
.rvp-when { display: block; margin-top: 2px; font-size: 12px; color: var(--text-secondary); }

/* T111 / S-001 §6：发之前那一屏顶上的说明段 —— **常驻，不是可关掉的提示条**。
   她第二次发帖时同样需要它，而一次性提示只解释第一次。 */
.rv-explainer {
  margin: 4px 0 12px; padding: 10px 12px;
  background: var(--tag-neutral-bg, #EEF0EE); border-radius: 10px;
  font-size: 12.5px; line-height: 1.5; color: var(--text-secondary);
}
.rv-decisions { margin-bottom: 4px; }
/* 家长那一行的样子，画出来给她看 —— 示意，不可点（点了没反应比没有更糟） */
.rvp-fullhint {
  margin-top: 10px; padding: 7px 10px; text-align: center;
  border: 1.5px dashed var(--border); border-radius: 999px;
  font-size: 12px; font-weight: 700; color: var(--text-secondary);
}

/* T146：提取失败/太慢时那一块。
   **不是错误条，是一个选择**——所以它长得像卡里的一段话加两颗按钮，
   不是红底惊叹号。红色留给「东西没了」，这里东西都还在，只是摘要没做出来。
   失败态才加一道左边框区分：等待和失败不该长成同一个样子。 */
.extract-notice { margin-top: 12px; padding: 12px 14px; border-radius: var(--radius);
                  background: var(--bg); border: 1px solid var(--border); }
.extract-notice.is-failed { border-left: 3px solid var(--accent-deep); }
.en-title { font-weight: 700; font-size: 14px; color: var(--text); margin-bottom: 4px; }
.en-body  { font-size: 13px; line-height: 1.5; color: var(--text-secondary); }
.en-actions { display: flex; gap: 8px; margin-top: 12px; }
/* G30：这两颗是她此刻**仅有的**两条路，不许比别处的按钮小。 */
.en-actions .cal-primary-btn, .en-actions .cal-secondary-btn { min-height: 44px; }

/* ── 作业：已发的能改能删（2026-09-30）──────────────────── */
.hw-acts { display: flex; gap: 16px; margin-top: 6px; }
.hw-acts button {
  background: none; border: none; padding: 6px 0; min-height: 32px; cursor: pointer;
  font-size: 13px; font-weight: 700; color: var(--primary-deep);
}
.hw-acts button.is-danger { color: var(--danger); }
.hw-existing { margin: 4px 0 8px; }
.hw-thumbs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.hw-thumb { position: relative; width: 84px; height: 84px; border-radius: 10px; overflow: hidden; background: #1a1a1a; }
.hw-thumb img, .hw-thumb video { width: 100%; height: 100%; object-fit: cover; display: block; }
.hw-thumb-x {
  position: absolute; top: 4px; right: 4px; width: 28px; height: 28px; border-radius: 50%;
  border: 1px solid var(--border, rgba(0,0,0,0.15)); background: var(--card); color: var(--text);
  font-size: 13px; line-height: 1; cursor: pointer;
}
/* 点了 ✕：先变灰、划掉，存的时候才真删 —— 点错了再点一下就回来 */
.hw-thumb.is-removed img, .hw-thumb.is-removed video { opacity: 0.25; }
.hw-thumb.is-removed .hw-thumb-x { background: var(--danger); color: var(--card); border-color: var(--danger); }


/* 老师端二级纸从右边推进来（2026-10-01「都做吧」第 5 条）—— 和左边缘右划返回是一对 */
.class-sheet:not([hidden]) { animation: d7-push-in-t 0.3s cubic-bezier(.32,1,.31,1); }
@keyframes d7-push-in-t { from { transform: translateX(100%); } to { transform: none; } }
@media (hover: none) {
  .charge-item:active, .cc-row:active { filter: brightness(0.95); }
}
