/* Unitick · 面板的外框和共用小部件：左边一栏（助手、导航、课程目录）、手机顶上一条和底部标签、事项详情面板、底部「撤销」条、
   按钮、小标签、事项类型的颜色，最后一节是几页共用的行和卡片（rows.js）。归 M1 平台。样子照 docs/mockups/dashboard-v2.html；变量沿用 app.css，这里只补样式稿多出来的几个。
   各页自己的样式在 pages/<页>.css。起新类名前先在 app.css、landing.css、setup.css、todo.css 和这里搜一下（AGENTS.md 第 3 节）。 */
:root {
  /* 反馈 86（2026-10-27）：分栏和首页区块的淡色底 */
  --shell-bg: #eef1f6; --sb-bg: #e9eef5; --sb-line: #cdd8ea;
  --sb-selected: #d7e0ee; --sb-chip: #ebf3fc; --main-bg: #fbfbfb; --chat-bg: #e7eff1;
  --home-step-bg: #fbf6eb; --home-step-line: #dfcca6;
  --home-week-bg: #f2f7fd; --home-week-line: #bccee9; --home-week-day-line: #d2dbeb;
  --home-todo-bg: #f5fbf7; --home-todo-line: #c0d9cc;
  --home-ask-bg: #faf4e8; --home-ask-line: #dfcca6;
  --home-notice-bg: #f7f4fb; --home-notice-line: #d0c6e4;
  --summary-bg: #f4f8fd; --summary-line: #bccee9;
  --sel: #eeeef1;
  --green-soft: rgba(31, 157, 72, .1); --red-soft: rgba(217, 48, 37, .08);
  --yellow-ink: #b07800; --yellow-soft: rgba(201, 138, 0, .11); --yellow-line: rgba(201, 138, 0, .28);
  /* 事项几类：考试红、作业蓝、备注灰、考勤紫（还没有数据来源）；课表灰蓝；自己记的日程青。只用在小方块图标、小标签、日历格子里 */
  --t-exam: #d93025; --t-exam-bg: rgba(217, 48, 37, .1);
  --t-hw: #0a66d8; --t-hw-bg: rgba(10, 102, 216, .1);
  --t-note: #6e6e73; --t-note-bg: rgba(110, 110, 115, .11);
  --t-att: #7a4cc2; --t-att-bg: rgba(122, 76, 194, .1);
  --t-class: #5f6b7a; --t-class-bg: rgba(95, 107, 122, .1);
  --t-mine: #0f8b8d; --t-mine-bg: rgba(15, 139, 141, .11);
  /* 学生订阅的「我的日历」和滴答任务（周视图、日历页的图例）：和首页月历里「我的日历」一样用绿，偏黄一点，和青色的「自己记的日程」分得开 */
  --t-ext: #4b8a1f; --t-ext-bg: rgba(75, 138, 31, .15);
  --t-chg: #d66900; --t-chg-bg: rgba(214, 105, 0, .11);
  --snack: #1d1d1f; --on-dark: #ffffff; --on-dark-2: rgba(255, 255, 255, .14); --on-dark-link: #8cc4ff;
  --scrim: rgba(17, 17, 19, .06); --scrim-strong: rgba(17, 17, 19, .22);
  /* 左栏宽度（#479）：--sbw-open 是展开时的宽度（--sbw-user：拖过的宽度，side.js；没拖过 200），--sbw 是现在的宽度（收起时 60，手机上 0）。
     --sb-dur：左栏收起、展开的时长，系统设了「减少动态效果」时 1ms */
  --sbw-open: var(--sbw-user, 200px);
  --sbw: var(--sbw-open);
  --sb-dur: 300ms;
}
@media (prefers-reduced-motion: reduce) { :root { --sb-dur: 1ms; } }

.ic { width: 18px; height: 18px; flex: none; display: block; }

/* ---------------------------------------------------------------- 按钮：.btn 白底细边，.primary 近黑，.danger 红字；.ibtn 只有图标；.lnk 文字链接 */
button.btn, a.btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 32px; padding: 0 14px; border: 1px solid var(--line-2);
  border-radius: var(--radius-sm); background: var(--panel); color: var(--ink); font-size: 13.5px; font-weight: 500; line-height: 1; white-space: nowrap;
  text-decoration: none; cursor: pointer; transition: background-color .2s ease, border-color .2s ease, transform .3s var(--ease-spring); }
button.btn:hover, a.btn:hover { background: var(--soft); border-color: #c7c7cc; text-decoration: none; }
button.btn:active { transform: scale(var(--press)); transition-duration: .12s; }
button.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-dark); }
button.btn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
button.btn.sm { height: 28px; padding: 0 11px; font-size: 13px; border-radius: 8px; }
.btn .ic { width: 15px; height: 15px; }
button.btn.danger { color: var(--red); }
button.btn.danger:hover { background: var(--red-soft); border-color: rgba(217, 48, 37, .3); }
button.ibtn { display: grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 0; border-radius: 8px; background: none; color: var(--muted);
  transition: background-color .15s, color .15s; }
button.ibtn:hover { background: var(--soft-2); color: var(--ink); }
.ibtn .ic { width: 16px; height: 16px; }
button.lnk { display: inline-flex; align-items: center; gap: 4px; padding: 0; border: 0; background: none; color: var(--tint); font-size: 13px; font-weight: 400; }
button.lnk:hover { background: none; text-decoration: underline; text-underline-offset: .2em; }
.lnk .ic { width: 14px; height: 14px; }

/* ---------------------------------------------------------------- 小标签、事项类型的小方块 */
.tag { display: inline-flex; align-items: center; gap: 4px; flex: none; height: 20px; padding: 0 7px; border-radius: 999px; font-size: 11.5px; line-height: 1;
  color: var(--muted); background: var(--soft); border: 1px solid var(--line); white-space: nowrap; font-weight: 500; }
.tag.y { color: var(--yellow-ink); background: var(--yellow-soft); border-color: var(--yellow-line); }
.tag.o { color: var(--orange); background: var(--t-chg-bg); border-color: rgba(214, 105, 0, .25); }
.tag.g { color: var(--green); background: var(--green-soft); border-color: rgba(31, 157, 72, .25); }
.tag.b { color: var(--tint); background: var(--tint-soft); border-color: rgba(10, 102, 216, .22); }
.tag.r { color: var(--red); background: var(--red-soft); border-color: rgba(217, 48, 37, .22); }
.tag[hidden] { display: none; }
/* 状态小标签（ui.js 的 statusTag）：前面一个跟着文字颜色的小圆点 */
.tag.u-st::before { content: ""; flex: none; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.tag.u-st:not(.g):not(.y):not(.r)::before { background: var(--faint); }
.tico { display: grid; place-items: center; flex: none; width: 20px; height: 20px; border-radius: 6px; }
.tico .ic { width: 13px; height: 13px; stroke-width: 2; }
.tico.t-exam { background: var(--t-exam-bg); color: var(--t-exam); }
.tico.t-hw { background: var(--t-hw-bg); color: var(--t-hw); }
.tico.t-note { background: var(--t-note-bg); color: var(--t-note); }
.tico.t-att { background: var(--t-att-bg); color: var(--t-att); }
/* 课程的小方块：课程名的第一个字 */
.cmono { display: grid; place-items: center; flex: none; width: 20px; height: 20px; border-radius: 6px; background: var(--soft-2); color: var(--text);
  font-size: 11px; font-weight: 600; line-height: 1; }
.cmono .ic { width: 13px; height: 13px; color: var(--muted); }
.cmono.ok { background: var(--green-soft); color: var(--green); }
.cmono.ok .ic { color: var(--green); width: 12px; height: 12px; stroke-width: 2.2; }

/* ---------------------------------------------------------------- 外框：左边一栏粘在屏幕上，右边一页随整页滚动 */
.shell { display: flex; min-height: 100vh; background: var(--shell-bg); }
.sb { position: sticky; top: 0; align-self: flex-start; flex: none; width: var(--sbw); height: 100vh; height: 100dvh; display: flex; flex-direction: column;
  --side: var(--sb-bg); background: var(--side); border-right: 1px solid var(--sb-line); }
.sb-top { flex: none; padding: 16px 12px 6px; padding-left: max(12px, env(safe-area-inset-left, 0px)); }
.who { display: flex; align-items: center; gap: 10px; padding: 8px; margin-bottom: 6px; border-radius: var(--radius-sm); color: inherit; text-decoration: none;
  transition: background-color .2s ease; }
.who:hover { background: var(--soft); text-decoration: none; }
.who.on { background: var(--accent-soft); }
.who-chev { width: 16px; height: 16px; margin-left: auto; color: var(--faint); }
.av { position: relative; display: grid; place-items: center; flex: none; width: 30px; height: 30px; border-radius: 50%; background: var(--soft-2);
  color: var(--ink); font-size: 13px; font-weight: 600; }
.av::after { content: ""; position: absolute; right: -1px; bottom: -1px; width: 9px; height: 9px; border-radius: 50%; background: var(--green);
  box-shadow: 0 0 0 2px var(--side); }
.av.av-blue::after { background: var(--tint); } .av.av-yellow::after { background: var(--yellow); } .av.av-red::after { background: var(--red); }
/* 助手的头像（avatar.js）：默认头像的小插画或上传的图片铺满圆；状态小圆点（::after）照旧叠在右下角 */
.av.av-pic { background: none; }
.av-pic > svg, .av-pic > img { display: block; width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
/* 选头像的一块（avatar.js 的 avatarPicker：账户页卡片、第一次设置的第一步） */
.avp-avs { display: flex; flex-wrap: wrap; gap: 12px; }
button.avp-av { width: 48px; height: 48px; padding: 0; border: 0; border-radius: 50%; background: none; box-shadow: 0 0 0 1px var(--line); }
button.avp-av:where(:hover:not(:disabled)) { background: none; box-shadow: 0 0 0 1px var(--line), 0 0 0 4px var(--soft-2); }
button.avp-av[aria-checked="true"] { box-shadow: 0 0 0 2px var(--panel), 0 0 0 4px var(--ink); }
button.avp-av > svg, button.avp-av > img { display: block; width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
.avp-up { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 12px; margin-top: 14px; }
.avp-up small { color: var(--muted); font-size: 12px; }
/* 裁剪：左边 256×256 的框（就是存下的大小），右边缩放和按钮；窄屏上下排 */
.avp-cropper { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 16px 24px; }
.avp-crop { position: relative; width: 256px; height: 256px; flex: none; overflow: hidden; border-radius: var(--radius-sm); background: var(--soft);
  cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none; }
.avp-crop:active { cursor: grabbing; }
.avp-crop-img { position: absolute; left: 0; top: 0; max-width: none; transform-origin: 0 0; pointer-events: none; }
.avp-crop-ring { position: absolute; inset: 0; border-radius: 50%; border: 1.5px dashed rgba(255, 255, 255, .95); box-shadow: 0 0 0 999px rgba(255, 255, 255, .55); pointer-events: none; }
.avp-crop-side { display: flex; flex-direction: column; gap: 12px; flex: 1; min-width: 200px; }
.avp-crop-side p { margin: 0; }
.avp-zoom-l { display: flex; align-items: center; gap: 10px; }
.avp-zoom-l small { color: var(--muted); font-size: 13px; flex: none; }
.avp-zoom { flex: 1; max-width: 220px; accent-color: var(--ink); }
.avp-msg:empty { display: none; }
.avp-msg { margin: 8px 0 0; }
.who b { display: block; color: var(--ink); font-size: 15px; font-weight: 600; line-height: 1.25; }
.who small { display: block; color: var(--muted); font-size: 12px; line-height: 1.3; }
.nav { position: relative; display: flex; align-items: center; gap: 12px; height: 36px; padding: 0 10px; border-radius: var(--radius-sm); color: var(--muted);
  font-size: 14px; text-decoration: none; transition: background-color .2s ease, color .2s ease, transform .3s var(--ease-spring); }
.nav:hover { background: var(--soft); color: var(--ink); text-decoration: none; }
.nav:active { transform: scale(var(--press)); transition-duration: .12s; }
.nav.on { background: var(--accent-soft); color: var(--ink); font-weight: 600; }
.nav.on .ic { color: var(--tint); }
.nav:focus-visible { outline-offset: -2px; }
.nav .badge, .tab .badge { margin-left: auto; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px; background: var(--ink); color: var(--on-dark);
  font-size: 11px; font-weight: 600; line-height: 20px; text-align: center; font-variant-numeric: tabular-nums; }
/* 课程目录：能上下滚，但不显示滚动条（滚动条一冒出来会把整栏往左挤一下）；横向一律不滚。收起、展开时也不改滚动方式，免得滚过的位置跳回顶上。
   顶上不留内边距：吸顶的「课程 · 第 N 周」只贴到内边距下沿，留了的话滚上去的课程会从那道缝里露出来（2026-10-21） */
.sb-mid { flex: 1; min-height: 0; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; padding: 0 10px 16px; scrollbar-width: none; }
.sb-mid::-webkit-scrollbar { display: none; }
.view { min-width: 0; }

/* 拖动改宽度的把手（ui.js 的 resizer）：平时看不见，移上去光标变左右拖、出一条细蓝线；拖的时候不选中文字、两栏不放宽度过渡 */
.u-rsz { position: absolute; top: 0; bottom: 0; z-index: 6; width: 10px; cursor: col-resize; touch-action: none; }
.u-rsz-right { right: -5px; }
.u-rsz-left { left: 0; }
.u-rsz::after { content: ""; position: absolute; top: 0; bottom: 0; left: 4px; width: 2px; border-radius: 1px; background: var(--tint); opacity: 0;
  transition: opacity .15s; }
.u-rsz-left::after { left: 0; }
.u-rsz:hover::after, .u-rsz.is-on::after, .u-rsz:focus-visible::after { opacity: 1; transition-delay: .08s; }
.u-rsz:focus-visible { outline: none; }
html.u-resizing, html.u-resizing * { cursor: col-resize !important; user-select: none !important; -webkit-user-select: none !important; }
html.u-resizing .sb, html.u-resizing .chat-host > .chat { transition: none !important; }
.sb-folded .sb > .u-rsz { display: none; }

/* ---------------------------------------------------------------- 左栏（#479，docs/mockups/course-nav/README.md 第 2.1 节）
   所有图标钉在同一列：中心离左栏左边 30 像素，正好是收起后 60 宽的正中，所以收起时导航图标、头像都不用动。
   收起（宽屏，html.sb-folded）：栏宽 --sbw 过渡到 60；文字先淡出（展开时栏宽先变、文字稍后淡入）；课程的三角收拢、小方块左移到正中；
   展开着的课下面那些事收到 0 高、下面的课平滑上移；「课程 · 第 N 周」收成一条细线；「助手动态」的数字移到图标右上角。手机上整栏都不在 */
.sb { transition: width var(--sb-dur) var(--ease-sheet); }
.sb-top { padding: 22px 10px 6px; }
.sb .who { height: 48px; gap: 10px; margin-bottom: 2px; padding: 0 0 0 4px; overflow: hidden; white-space: nowrap; }
.sb .who .av { width: 32px; height: 32px; }
.sb .who > div { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.sb .nav { padding: 0 0 0 11px; overflow: hidden; white-space: nowrap; }
.sb .nav .badge { position: absolute; top: 8px; right: 8px; margin: 0; pointer-events: none; transform-origin: top right;
  transition: top var(--sb-dur) var(--ease-sheet), right var(--sb-dur) var(--ease-sheet), transform var(--sb-dur) var(--ease-sheet); }
/* 会淡出的文字：导航名、助手名字、课程栏标题、课程简称和件数 */
.sb .nav > span:not(.badge):not(.fb-dot), .sb .who > div, .sb .lbl { transition: opacity calc(var(--sb-dur) * .55) ease calc(var(--sb-dur) * .35); }
.sb-foot { flex: none; padding: 8px 10px 17px; }
button.sb-fold { width: 100%; border: 0; background: none; font: inherit; text-align: left; cursor: pointer; }
.fold-ic { transition: transform var(--sb-dur) var(--ease-sheet); }
/* 收起时浮出的名字：整页一个，side.js 放到那一行右边（x = 窄条右边 + 10） */
.sb-tip { position: fixed; z-index: 80; padding: 5px 10px; border-radius: 8px; background: var(--ink); color: var(--on-dark); font-size: 12.5px; font-weight: 500;
  line-height: 1.4; white-space: nowrap; pointer-events: none; transform: translateY(-50%); animation: sb-tip .15s ease-out; }
@keyframes sb-tip { from { opacity: 0; transform: translate(-4px, -50%); } }
@media (min-width: 760px) {
  :root.sb-folded { --sbw: 60px; }
  .sb-folded .sb .nav > span:not(.badge):not(.fb-dot), .sb-folded .sb .who > div, .sb-folded .sb .lbl { opacity: 0; pointer-events: none;
    transition: opacity calc(var(--sb-dur) * .3) ease 0s; }
  .sb-folded .sb .nav .badge { top: 1px; right: 0; transform: scale(.8); }
  .sb-folded .fold-ic { transform: scaleX(-1); }
}

/* 意见反馈（feedback.js，docs/specs/feedback.md）：侧边栏导航最下面（「北大资讯」下面）一项，字加粗；红点是开发团队有新回复。点开的小窗口 */
button.sb-fbnav { width: 100%; border: 0; background: none; font: inherit; font-weight: 700; color: var(--ink); text-align: left; cursor: pointer; }
button.sb-fbnav .ic { color: var(--ink); }
.fb-dot { flex: none; display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--red); vertical-align: middle; }
.sb-fbnav .fb-dot { margin-left: auto; }
@media (min-width: 760px) { .sb-folded .sb-fbnav .fb-dot { position: absolute; top: 6px; right: 12px; } }
.mtop .who .fb-dot { margin-left: auto; }
.mtop .who .fb-dot + .who-chev { margin-left: 6px; }
.fb-box { max-width: 520px; }
.fb-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.fb-head h3 { margin: 0; }
.fb-seg button { display: inline-flex; align-items: center; gap: 6px; }
.fb-box p.small { margin: 0; }
.fb-text { display: block; width: 100%; min-height: 120px; margin: 4px 0 0; resize: vertical; }
.fb-err { margin: 8px 0 0; }
.fb-credit { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin: 10px 0 0; font-size: 14px; cursor: pointer; }
.fb-credit input { margin: 0; }
.fb-nick { margin: 8px 0 0 22px; }
.fb-nick[hidden] { display: none; }
.fb-btns { justify-content: flex-end; margin-top: 18px; gap: 8px; }
.fb-mine { max-height: min(60vh, 520px); overflow-y: auto; margin: 0 -4px; padding: 0 4px; }
.fb-item { padding: 12px 0; border-top: 1px solid var(--line); }
.fb-item:first-child { border-top: 0; padding-top: 4px; }
.fb-meta { display: flex; align-items: center; gap: 6px; }
.fb-newtag { color: var(--red); font-weight: 600; }
.fb-said { margin-top: 4px; }
.fb-reply { margin-top: 8px; padding: 8px 12px; border-radius: var(--radius-sm); background: var(--soft); }
.fb-wait { margin-top: 6px; }
/* 附图：写的时候一排小图（每张右上角 ×）、加图按钮；看的时候一排小图，点开 .fb-zoom 看大图（开发者平台也用） */
.fb-pics { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 10px 0 0; }
.fb-pic { position: relative; width: 64px; height: 64px; border-radius: var(--radius-sm); overflow: hidden; border: 1px solid var(--line); background: var(--soft); }
.fb-pic img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fb-pic.fb-done { opacity: .6; }
.fb-pic-x { position: absolute; top: 2px; right: 2px; width: 22px; height: 22px; min-height: 0; padding: 0; display: grid; place-items: center;
  border: 0; border-radius: 50%; background: rgba(0, 0, 0, .6); color: #fff; }
.fb-pic-x svg { width: 14px; height: 14px; }
.fb-add { height: 64px; padding: 0 12px; border-style: dashed; }
.fb-pics-n { flex-basis: 100%; }
.fb-thumbs { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.fb-thumb { width: 72px; height: 72px; min-height: 0; padding: 0; border: 1px solid var(--line); border-radius: var(--radius-sm); overflow: hidden; background: var(--soft); cursor: zoom-in; }
.fb-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fb-zoom { position: fixed; inset: 0; z-index: 1000; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  padding: 16px; background: rgba(0, 0, 0, .82); cursor: zoom-out; }
.fb-zoom img { max-width: 100%; max-height: calc(100% - 40px); object-fit: contain; border-radius: 4px; background: #fff; }
.fb-zoom-bar { display: flex; gap: 16px; color: #ddd; font-size: 13px; }
.fb-zoom-bar a { color: #fff; }
/* 同学自己的昵称（nickname.js，docs/specs/nickname.md）：左上角的红点、提醒窗口、输入框下面的红色小字 */
.nk-dot { flex: none; display: inline-block; width: 8px; height: 8px; margin-left: auto; border-radius: 50%; background: var(--red); vertical-align: middle; }
.who { position: relative; }
@media (min-width: 760px) { .sb-folded .who > .nk-dot { display: block; position: absolute; top: 4px; right: 50%; margin-right: -19px; box-shadow: 0 0 0 2px var(--side); } }
.mtop .who .nk-dot + .who-chev { margin-left: 6px; }
.nk-box { max-width: 440px; }
.nk-box h3 { margin: 0 0 8px; }
.nk-box .nk-p { margin: 0 0 16px; color: var(--muted); font-size: 14px; line-height: 1.7; }
input.su-input.nk-in { display: block; width: 100%; }
.nk-hint { margin: 6px 0 0; color: var(--red); font-size: 12px; line-height: 1.5; }
.nk-err { margin: 8px 0 0; }
.nk-btns { justify-content: flex-end; margin-top: 18px; gap: 8px; }
/* 邮箱登录的小窗口（emaillogin.js）：登录邮箱一行、两个密码框、验证码框 */
.el-f { display: block; margin-top: 12px; }
.el-l { display: block; margin-bottom: 6px; color: var(--muted); font-size: 13px; }
input.su-input.el-in { display: block; width: 100%; }
.el-mail { margin: 0 0 4px; }
.el-mail b { font-weight: 500; overflow-wrap: anywhere; }
.el-note { margin: 10px 0 0; }

/* ---------------------------------------------------------------- 宽屏的底板和纸
   外框灰蓝、左栏淡蓝、中间近白、对话淡青（反馈 86）；保留中间圆角和留缝，左栏右侧加分界线。
   首页宽屏（1360 以上）纸是 .chat-host-main，别的页纸是整个 .main。 */
@media (min-width: 760px) {
  .sb { --soft: #ebebef; --soft-2: #e2e2e7; }
  .sb-top, .sb-folded .sb-top { padding-top: 22px; }   /* 头像和对话栏顶上、首页问好同一高度 */
  .sb-foot, .sb-folded .sb-foot { padding-bottom: 17px; border-top-color: transparent; }
  .sb-mid { border-top-color: transparent; }
  .main { align-self: flex-start; min-height: calc(100vh - 16px); min-height: calc(100dvh - 16px); margin: 8px 8px 8px 0; border-radius: 16px; background: var(--panel);
    box-shadow: 0 0 0 1px rgba(0, 0, 0, .05), 0 1px 3px rgba(0, 0, 0, .04); }
  .main .ibx { height: calc(100vh - 16px); height: calc(100dvh - 16px); }   /* 助手动态整屏高的两栏：放进纸里少 16px */
}
@media (min-width: 1360px) {
  .main:has(> .view > .chat-host) { margin: 0; min-height: 0; border-radius: 0; background: none; box-shadow: none; }
  .chat-host > .chat-host-main { align-self: stretch; min-height: calc(100vh - 16px); min-height: calc(100dvh - 16px); margin: 8px 0; border-radius: 16px;
    background: var(--main-bg); box-shadow: 0 0 0 1px rgba(0, 0, 0, .05), 0 1px 3px rgba(0, 0, 0, .04); }
  .chat-host > .chat-host-main > .page.hm-page { padding-top: 12px; }   /* 问好和左右两栏顶上的头像同一高度 */
  .chat-host:has(> .chat.is-folded) > .chat-host-main { margin-right: 8px; }   /* 对话收起了：纸右边也留一道缝 */
}

/* ---------------------------------------------------------------- 课程目录（#479 的样子，尺寸见 docs/mockups/course-nav/README.md 第 2.1 节）
   一门课一行：三角（20 宽，左外边距 2）、小方块（24，左外边距 2）、简称、件数。三角尖在 x=22，下面那些事的细竖线就画在这里；
   事项的类型小方块和课程的小方块左边对齐（x=34）。收起时三角宽 20→0、小方块外边距 2→8，小方块从 x=34 平滑移到 x=18（中心 30） */
.ct-h { position: sticky; top: 0; z-index: 3; display: flex; align-items: center; gap: 6px; height: 40px; padding: 6px 0 0 11px; overflow: hidden; white-space: nowrap;
  background: var(--side); transition: height var(--sb-dur) var(--ease-sheet), padding var(--sb-dur) var(--ease-sheet); }
.ct-title { color: var(--muted); font-size: 12px; font-weight: 600; letter-spacing: .04em; text-decoration: none; }
.ct-sub { color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
.ct-rule { height: 1px; margin: 0 16px 2px; background: var(--line-2); opacity: 0; transition: opacity calc(var(--sb-dur) * .4) ease; }
/* 课程栏标题右边的「立即刷新教学网」（side.js）：靠右，刷新时图标转、按钮变灰 */
button.ct-sync { flex: none; margin-left: auto; margin-right: 2px; width: 26px; height: 26px; }
button.ct-sync .ic { width: 15px; height: 15px; }
button.ct-sync:disabled { opacity: .55; cursor: progress; }
.ct-sync.is-spin .ic { animation: u-spin .9s linear infinite; }
.ct-empty { margin: 16px 10px; padding: 24px 12px; border: 1px dashed var(--line-2); border-radius: 12px; color: var(--muted); font-size: 13px; text-align: center; }
.cn { margin-top: 1px; }
.ct .cn + .cn { margin-top: 6px; }
/* 拖着一门课排先后（#831，dragsort.js）：浮起来、盖在别的课上面；拖着时全页是「抓着」的手、不选字 */
.ct .cn.srt-drag { position: relative; z-index: 3; border-radius: 10px; background: var(--side); box-shadow: var(--shadow); }
.ct .cn.srt-drag > .cn-row { --row-bg: var(--soft); }
/* 能拖的课程那一行：按住别让 iPad（横屏用的是这套左栏）弹链接预览、选字，不然它发 pointercancel，拖不了也出不了菜单。
   收成窄条时的小方块也在这个链接里。改名框不在这几样里，照常能选字 */
.ct .cn[data-key] > .cn-row > :is(.cn-open, .cn-arrow, .cn-more) { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent; }
html.srt-on, html.srt-on * { cursor: grabbing !important; user-select: none !important; -webkit-user-select: none !important; }
.cn-row { --row-bg: var(--side); position: relative; display: flex; align-items: center; height: 40px; border-radius: 10px; overflow: hidden; white-space: nowrap;
  color: var(--ink); background: var(--row-bg); transition: background-color .15s; }
.cn-row:hover { --row-bg: var(--soft); }
.cn.is-open > .cn-row { --row-bg: var(--soft); }
.cn-row.on, .cn.is-open > .cn-row.on { --row-bg: #e8e8ea; }
.cn-row.on .cmono { background: var(--tint-soft); color: var(--tint); }
button.cn-arrow { display: grid; place-items: center; flex: none; width: 20px; height: 26px; margin: 0 0 0 2px; padding: 0; border: 0; border-radius: 6px; background: none;
  color: var(--faint); overflow: hidden; transition: width var(--sb-dur) var(--ease-sheet), margin var(--sb-dur) var(--ease-sheet), opacity calc(var(--sb-dur) * .4) ease; }
button.cn-arrow:hover { background: var(--soft-2); color: var(--ink); }
.cn-arrow:focus-visible { outline-offset: -2px; }
.cn-open { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; height: 100%; padding: 0; border-radius: 10px; color: inherit; text-decoration: none; overflow: hidden; }
.cn-open:hover { text-decoration: none; }
.cn-open:focus-visible { outline-offset: -2px; }
.chev { width: 16px; height: 16px; color: var(--faint); stroke-width: 2; transition: transform .24s var(--ease-out); }
.cn.is-open > .cn-row .chev { transform: rotate(90deg); }
.ct .cn-row .cmono { position: relative; width: 24px; height: 24px; margin-left: 2px; border-radius: 7px; font-size: 12px;
  transition: margin var(--sb-dur) var(--ease-sheet), background-color .15s, color .15s; }
/* 收起后才显示的红点：这门课有过期或 24 小时内到期的事 */
.hot-dot { position: absolute; top: -3px; right: -3px; width: 8px; height: 8px; border-radius: 50%; background: var(--red); box-shadow: 0 0 0 2px var(--side);
  opacity: 0; transition: opacity calc(var(--sb-dur) * .4) ease; }
.cn-name { flex: 1; min-width: 0; color: var(--ink); font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cnt { flex: none; min-width: 20px; height: 18px; margin-right: 8px; padding: 0 6px; border-radius: 9px; background: var(--soft-2); color: var(--muted);
  font-size: 11px; font-weight: 600; line-height: 18px; text-align: center; font-variant-numeric: tabular-nums; }
.cnt.hot { background: var(--red); color: var(--on-dark); }
.cnt.zero { background: none; color: var(--muted); font-weight: 400; }
/* 课程那一行悬停：件数左边空着的地方出一个「···」（菜单开着时保持按下的样子），左边一段渐变盖住名字；收起时、触屏上没有 */
button.cn-more { position: absolute; top: 50%; right: 34px; z-index: 2; display: none; place-items: center; width: 26px; height: 26px; margin-top: -13px; padding: 0;
  border: 0; border-radius: 7px; background: var(--row-bg); color: var(--muted); }
button.cn-more::before { content: ""; position: absolute; right: 100%; top: 0; bottom: 0; width: 14px; background: linear-gradient(to right, transparent, var(--row-bg));
  pointer-events: none; }
button.cn-more .ic { width: 16px; height: 16px; }
.cn-row:hover button.cn-more, button.cn-more:focus-visible, button.cn-more.is-on { display: grid; }
button.cn-more:hover, button.cn-more.is-on { background: var(--soft-2); color: var(--ink); }
button.cn-more:disabled { display: none; }
@media (hover: none) { button.cn-more { display: none; } }
/* 重命名：原处变成输入框 */
.cn-edit { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; height: 100%; }
input.cn-in { flex: 1; min-width: 0; height: 28px; margin: 0 6px 0 0; padding: 0 7px; border: 1px solid var(--tint); border-radius: 7px; background: var(--panel);
  color: var(--ink); font: inherit; font-size: 14px; font-weight: 600; outline: none; box-shadow: 0 0 0 3px var(--tint-soft); }
input.cn-in:disabled { opacity: .6; }
/* 展开后的事：高度用 grid 的 0fr↔1fr 过渡。最里面一层（.kids）宽度钉成展开时的宽度（栏宽 − 两边 10 − 左边 12），
   左栏变窄时被外面裁掉、文字不重新折行，不然这一块先变高再收，下面的课会上下跳（样式稿 README 第 5 节第 3 条） */
.cn-kids { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--sb-dur) var(--ease-sheet); }
.cn.is-open > .cn-kids { grid-template-rows: 1fr; }
.cn-kids > div { min-height: 0; overflow: hidden; }
.ct .kids { position: relative; margin-left: 12px; padding: 2px 0 4px 4px; transition: opacity calc(var(--sb-dur) * .5) ease calc(var(--sb-dur) * .3); }
.sb .ct .kids { width: calc(var(--sbw-open) - 32px); }
.ct .kids::before { content: ""; position: absolute; left: -.5px; top: 4px; bottom: 8px; width: 1px; background: var(--line-2); }
.ct .cn-kids .it { padding: 5px 8px; }
.ct .cn-kids .it .tico { width: 16px; height: 16px; border-radius: 5px; margin-top: 1px; }
.ct .cn-kids .it .tico .ic { width: 11px; height: 11px; }
/* 最底下的「其他」（side.js 的 offBox）：停止跟踪的课，默认收着；样子和上面的课一样，课名灰一点，右边一个「恢复」 */
.ct .ct-off { margin-top: 14px; }
button.ct-off-h { border: 0; background: none; font: inherit; text-align: left; cursor: pointer; }
button.ct-off-h:hover, button.ct-off-h:active { background: none; transform: none; }
.ct-off-h .cn-name { color: var(--muted); }
.ct-off-h .cmono .ic { width: 14px; height: 14px; }
.ct .kids.offk { margin-left: 26px; padding-left: 8px; }
.ct .kids.offk::before { display: none; }
.ct-off-c { display: flex; align-items: center; gap: 6px; height: 34px; padding-right: 4px; border-radius: 8px; white-space: nowrap; }
.ct-off-c:hover { background: var(--soft); }
.ct-off-c.on { background: var(--accent-soft); }
.ct-off-c .cn-open { padding-left: 4px; }
.ct-off-c .cn-name { color: var(--muted); font-size: 13px; font-weight: 500; }
button.cn-back { flex: none; height: 24px; padding: 0 9px; font-size: 12px; }
/* 收着时「恢复」的边框会从缩进那几像素里漏出来：收完再藏 */
.ct-off:not(.is-open) > .cn-kids > div { visibility: hidden; transition: visibility 0s var(--sb-dur); }
/* 左栏收起时（宽屏）：只剩小方块，展开着的课也收拢；手机「课程」页上的那一棵不受影响 */
@media (min-width: 760px) {
  .sb-folded .sb .ct-h { height: 14px; padding-top: 0; }
  .sb-folded .sb .ct-rule { opacity: 1; transition-delay: calc(var(--sb-dur) * .5); }
  .sb-folded .sb button.cn-arrow { width: 0; margin-left: 0; opacity: 0; pointer-events: none; }
  .sb-folded .sb .cn-row .cmono { margin-left: 8px; }
  .sb-folded .sb .hot-dot { opacity: 1; transition-delay: calc(var(--sb-dur) * .5); }
  .sb-folded .sb .cn.is-open > .cn-row:not(.on):not(:hover) { --row-bg: var(--side); }
  .sb-folded .sb .cn.is-open > .cn-kids { grid-template-rows: 0fr; }
  .sb-folded .sb .kids { opacity: 0; transition-delay: 0s; }
  .sb-folded .sb button.cn-more { display: none; }
}

/* 一件事：左边类型小方块，中间标题和时间，右边倒计时或标签；悬停时右边换成「完成」「删除」 */
.it { --row-bg: var(--side); position: relative; display: flex; align-items: flex-start; gap: 8px; padding: 6px 8px 6px 30px; border-radius: 8px;
  background: var(--row-bg); transition: background-color .15s; }
.it:hover { --row-bg: var(--soft); }
.it.is-sel, .it.is-menu { --row-bg: var(--sel); }
.sb .it.is-sel, .sb .it.is-menu { --row-bg: #e2e2e8; }
.it .tico { margin-top: 1px; }
button.it-b { flex: 1; min-width: 0; display: block; padding: 0; border: 0; border-radius: 4px; background: none; color: inherit; font-weight: 400; text-align: left; line-height: inherit; }
button.it-b:hover, button.it-b:active { background: none; transform: none; }
.it-b:focus-visible { outline-offset: 4px; }
/* 左栏里一件事只两行（#479）：短标题、时间，都是一行、放不下用省略号；侧栏显示里开了地点、来源的接在时间后面，说明再一行 */
.it-t { display: block; overflow: hidden; color: var(--text); font-size: 13px; line-height: 18px; white-space: nowrap; text-overflow: ellipsis; transition: color .3s ease; }
.it-t .new { display: inline-block; width: 6px; height: 6px; margin: 0 6px 1px 0; border-radius: 50%; background: var(--tint); vertical-align: middle; }
.it-m { display: block; overflow: hidden; color: var(--muted); font-size: 11.5px; line-height: 16px; white-space: nowrap; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
.it-m .mp { white-space: nowrap; }
.it-m .sep { margin: 0 5px; color: var(--faint); }
.it-n { display: block; color: var(--faint); font-size: 11.5px; line-height: 16px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.it.is-soon .it-m .when, .it.is-late .it-m .when { color: var(--red); }
.it-side { flex: none; display: flex; align-items: center; height: 20px; margin-top: 1px; color: var(--faint); font-size: 12px; font-variant-numeric: tabular-nums; }
.it-side:empty { display: none; }
.it-side .tag { height: 18px; padding: 0 6px; font-size: 11px; }
.it-acts { position: absolute; top: 50%; right: 3px; transform: translateY(-50%); display: flex; gap: 2px; padding-left: 18px; opacity: 0; pointer-events: none;
  background: linear-gradient(to right, transparent, var(--row-bg) 16px); transition: opacity .15s; }
.it:hover .it-acts, .it:focus-within .it-acts { opacity: 1; pointer-events: auto; }
.it-acts .ibtn { width: 24px; height: 24px; background: var(--row-bg); }
.it-acts .ibtn .ic { width: 15px; height: 15px; }
.it-acts .ibtn:hover { background: var(--soft-2); }
.it-acts .ibtn.ok:hover { color: var(--green); }
.it-acts .ibtn.del:hover { color: var(--red); }
@media (hover: none) { .it-acts { display: none; } }
.it.is-going .tico { background: var(--green); color: var(--on-dark); transition: background-color .2s; }
.it.is-going .it-t { color: var(--muted); text-decoration: line-through; text-decoration-color: var(--faint); }
.it.is-going .it-acts { opacity: 0; }

/* ---------------------------------------------------------------- 详情：宽屏从右边滑出（左边一栏照样能点），手机从底部升起 */
.layer { position: fixed; top: 0; right: 0; bottom: 0; left: var(--sbw); z-index: 50; pointer-events: none; }
.layer .scrim { position: absolute; inset: 0; background: var(--scrim); opacity: 0; transition: opacity .3s var(--ease-out); }
.sheet { position: absolute; top: 0; right: 0; bottom: 0; display: flex; flex-direction: column; width: min(440px, 100%); background: var(--panel);
  border-left: 1px solid var(--line); box-shadow: var(--shadow-3); transform: translateX(104%); transition: transform .48s var(--ease-out); }
.layer.open { pointer-events: auto; }
.layer.open .scrim { opacity: 1; }
.layer.open .sheet { transform: none; }
.sheet-h { flex: none; display: flex; align-items: center; gap: 8px; min-height: 52px; padding: 6px 10px 6px 20px; border-bottom: 1px solid var(--line); }
.sheet-h .crumb { flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; color: var(--muted); font-size: 13px; white-space: nowrap; overflow: hidden; }
.sheet-h .crumb b { color: var(--ink); font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.grab { display: none; }
.sheet-b { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 22px 24px calc(32px + env(safe-area-inset-bottom, 0px)); }
.sheet-b > * + * { margin-top: 20px; }
.d-tags { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.d-type { display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 9px 0 4px; border-radius: 7px; font-size: 12.5px; font-weight: 600; }
.d-type .tico { width: 18px; height: 18px; background: none; color: inherit; }
.d-type.t-exam { background: var(--t-exam-bg); color: var(--t-exam); } .d-type.t-hw { background: var(--t-hw-bg); color: var(--t-hw); }
.d-type.t-note { background: var(--t-note-bg); color: var(--ink); } .d-type.t-att { background: var(--t-att-bg); color: var(--t-att); }
.sheet-b h2.d-title { margin: 10px 0 0; font-size: 21px; font-weight: 650; line-height: 1.35; letter-spacing: -.008em; }
.d-title.done { color: var(--muted); text-decoration: line-through; text-decoration-color: var(--faint); }
.d-ask { padding: 14px 16px; border-radius: 12px; background: var(--yellow-soft); border: 1px solid var(--yellow-line); }
.d-ask b { display: block; color: var(--ink); font-weight: 600; }
.d-ask .row { display: flex; gap: 8px; margin-top: 12px; }
.kvs { display: grid; gap: 10px; }
.kv { display: grid; grid-template-columns: 56px minmax(0, 1fr); gap: 12px; font-size: 14px; line-height: 1.55; }
.kv .k { color: var(--muted); }
.kv .v { color: var(--text); min-width: 0; overflow-wrap: anywhere; }
.kv .v small { display: block; color: var(--muted); font-size: 12.5px; }
.kv .v button.lnk.d-fix-time { font-size: 12.5px; margin-top: 2px; }   /* 只知道时段的考试：「改时间」（#941） */
.kv .v small.red { color: var(--red); }
.kv .v:has(> .d-plan-in) { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }   /* 打算哪天做（#735）：日子、清掉、「拖了 N 天」一行 */
.d-plan-in { height: 28px; padding: 0 8px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); color: var(--text); font: inherit; font-size: 13.5px; }
button.ibtn.d-plan-x { width: 24px; height: 24px; }
.d-block h3, .sheet-b .td-block h3 { margin: 0 0 8px; color: var(--muted); font-size: 12.5px; font-weight: 600; letter-spacing: .02em; }
.d-sum { margin: 0; color: var(--text); font-size: 14px; line-height: 1.7; white-space: pre-wrap; }
.d-change { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 13.5px; }
.d-change s { color: var(--faint); }
.d-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding-top: 18px; border-top: 1px solid var(--line); }
.d-acts .lnk { margin-left: auto; }
.d-acts .lnk + .lnk { margin-left: 0; }
/* 标签（#776 第 8 节）：一个个小胶囊，带 ×，最后一个「＋」 */
.d-tag-list { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.d-tag { display: inline-flex; align-items: center; gap: 2px; height: 24px; padding: 0 2px 0 9px; border: 1px solid var(--line); border-radius: 999px; font-size: 12.5px; }
button.d-tag-x { display: inline-grid; place-items: center; width: 20px; height: 20px; padding: 0; border: 0; border-radius: 50%; background: none; color: var(--faint); }
button.d-tag-x:hover { color: var(--ink); background: var(--soft); }
.d-tag-x .ic { width: 12px; height: 12px; }
button.ibtn.d-tag-add { width: 24px; height: 24px; }
button.lnk.d-reset { margin-top: 6px; }

/* 底部一条：「已完成… 撤销」 */
.snack { position: fixed; left: 50%; bottom: calc(28px + env(safe-area-inset-bottom, 0px)); z-index: 70; display: flex; align-items: center; gap: 14px;
  max-width: calc(100vw - 32px); padding: 10px 10px 10px 18px; border-radius: 12px; background: var(--snack); color: var(--on-dark); font-size: 14px;
  box-shadow: 0 8px 30px rgba(0, 0, 0, .2); transform: translateX(-50%); animation: snack .32s var(--ease-out); }
.snack span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.snack-wide { width: min(440px, calc(100vw - 32px)); }   /* 靠 left: 50% 摆的，不给宽度只能占半屏 */
.snack-wide .snack-2 { flex: 1; }
.snack .snack-2 { display: grid; white-space: normal; }   /* 两行：上面一句（太长截掉），下面一行小字（照样换行） */
.snack-2 b { overflow: hidden; font-weight: inherit; text-overflow: ellipsis; white-space: nowrap; }
.snack-2 small { color: var(--on-dark); font-size: 12px; opacity: .72; }
.snack button { flex: none; padding: 5px 12px; border: 0; border-radius: var(--radius-xs); background: var(--on-dark-2); color: var(--on-dark-link); font-weight: 600; }
.snack button:hover { background: rgba(255, 255, 255, .22); }
@keyframes snack { from { opacity: 0; transform: translate(-50%, 10px); } }

/* 右键菜单（menu.js，#479）：出在点的位置，下面铺一层透明的遮罩，点它、在它上面右键都关菜单。悬停和键盘选中蓝底白字，「删除」红字 */
.u-menu-veil { position: fixed; inset: 0; z-index: 90; }
.u-menu { position: fixed; z-index: 91; min-width: 176px; padding: 5px; border-radius: 10px; background: rgba(255, 255, 255, .97);
  -webkit-backdrop-filter: saturate(180%) blur(20px); backdrop-filter: saturate(180%) blur(20px);
  box-shadow: 0 0 0 .5px rgba(0, 0, 0, .14), 0 10px 32px -6px rgba(0, 0, 0, .22), 0 2px 6px rgba(0, 0, 0, .06); transform-origin: top left; animation: u-menu .12s ease-out; }
.u-menu:focus { outline: none; }
@keyframes u-menu { from { opacity: 0; transform: scale(.98); } }
button.u-menu-i { display: flex; align-items: center; width: 100%; height: 28px; margin: 0; padding: 0 10px; border: 0; border-radius: 6px; background: none;
  color: var(--ink); font: inherit; font-size: 13.5px; font-weight: 400; text-align: left; white-space: nowrap; cursor: default; }
button.u-menu-i:hover, button.u-menu-i:focus-visible { background: var(--tint); color: var(--on-dark); outline: none; }
button.u-menu-i.danger { color: var(--red); }
button.u-menu-i.danger:hover, button.u-menu-i.danger:focus-visible { background: var(--red); color: var(--on-dark); }
.u-menu-sep { height: 1px; margin: 4px 10px; background: var(--line); }
@media (prefers-reduced-motion: reduce) { .u-menu, .sb-tip { animation: none; } }

/* ---------------------------------------------------------------- 手机：左边一栏换成顶上一条（助手）和底部五个标签，课程目录是单独一页 */
.mtop, .tabbar { display: none; }
@media (max-width: 759.98px) {
  :root { --sbw: 0px; }
  .shell { display: block; min-height: 0; }
  .sb { display: none; }
  .main { padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px)); }
  .mtop { position: sticky; top: 0; z-index: 20; display: flex; align-items: center; height: 52px;
    padding-left: max(16px, env(safe-area-inset-left, 0px)); padding-right: max(10px, env(safe-area-inset-right, 0px));
    background: var(--glass); -webkit-backdrop-filter: var(--blur); backdrop-filter: var(--blur); border-bottom: 1px solid var(--line); }
  .mtop .who { flex: 1; margin: 0 0 0 -8px; padding: 4px 8px; }
  .mtop .av { width: 28px; height: 28px; }
  .view .page { padding-top: 20px; }
  .tabbar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 40; display: flex;
    padding: 4px max(16px, env(safe-area-inset-right, 0px)) calc(4px + env(safe-area-inset-bottom, 0px)) max(16px, env(safe-area-inset-left, 0px));
    background: var(--glass); -webkit-backdrop-filter: var(--blur); backdrop-filter: var(--blur); border-top: 1px solid var(--line); }
  .tab { position: relative; flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 1px; padding: 5px 0 3px;
    color: var(--muted); font-size: 11px; text-decoration: none; }
  .tab:hover { text-decoration: none; }
  .tab .ic { width: 22px; height: 22px; }
  .tab.on { color: var(--tint); font-weight: 500; }
  .tab .badge { position: absolute; top: 1px; left: calc(50% + 5px); min-width: 17px; height: 17px; margin: 0; padding: 0 4px; font-size: 10px; line-height: 17px; }
  #app:has(.shell) ~ #toast { bottom: calc(76px + env(safe-area-inset-bottom, 0px)); }
  .layer { left: 0; z-index: 50; }
  .layer .scrim { background: var(--scrim-strong); }
  .sheet { top: auto; left: 0; width: auto; max-height: 88%; border-left: 0; border-radius: var(--radius-lg) var(--radius-lg) 0 0; transform: translateY(104%);
    transition-timing-function: var(--ease-sheet); }
  .grab { display: block; flex: none; width: 40px; height: 5px; margin: 8px auto 0; border-radius: 3px; background: var(--line-2); }
  .sheet-b { padding-left: 18px; padding-right: 18px; }
  .snack { bottom: calc(76px + env(safe-area-inset-bottom, 0px)); }
  .it-acts { display: none; }
  html.dt-lock, html.dt-lock body { overflow: hidden; }
}
@media (prefers-reduced-motion: reduce) {
  .sheet, .layer .scrim, .cn-kids, .chev { transition-duration: .01ms; }
  .snack { animation-duration: .16s; }
}

/* ---------------------------------------------------------------- 各页共用的页头、返回、占位 */
.page.wide { max-width: 1120px; }
.page-h { display: grid; gap: 6px; }
.page-h h1 { margin: 0; }
.page-h p { margin: 0; color: var(--muted); }
.back { display: inline-flex; align-items: center; gap: 2px; color: var(--tint); font-size: 14px; text-decoration: none; }
.back.co-back { display: none; }
.ph-empty { padding: 40px 16px; border: 1px dashed var(--line-2); border-radius: var(--radius); color: var(--muted); text-align: center; }
@media (max-width: 759.98px) {
  .back.co-back { display: inline-flex; }
  .page > .co-back + * { margin-top: 10px; }
}
.ct-err { margin: 2px 4px 10px 6px; padding: 8px 10px; border-radius: 8px; background: var(--red-soft); color: var(--red); font-size: 12.5px; line-height: 1.5; }
.view:focus, .sb:focus { outline: none; }   /* 关掉详情后焦点退回整块时（detail.js），不画焦点框 */

/* ---------------------------------------------------------------- 共用的行和卡片（rows.js）：样子照样式稿的 .box、.sh、taskRow、examTile、upRow。
   类名的前缀：区块 sec-、一行待办 tk-、考试卡片 ex-、紧凑的一行 up-（todo.css 的旧清单占着 .td-*，别混用） */
/* 白底卡片区块和区块小标题：标题 + 浅色的件数 + 右边一个链接 */
.sec-box { border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); box-shadow: var(--shadow-1); padding: 18px 20px; }
.sec-box > p { margin: 0; }
.sec-h { display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px; color: var(--ink); font-size: 15px; font-weight: 600; line-height: 1.6; }
.sec-h small, .sec-tog small { color: var(--faint); font-size: 13px; font-weight: 400; font-variant-numeric: tabular-nums; }
.sec-h > .sec-end { display: inline-flex; align-items: center; gap: 4px; margin-left: auto; }   /* 右边的链接（或按钮）单独一层 */
a.sec-lnk { color: var(--tint); font-size: 13px; font-weight: 400; text-decoration: none; }
a.sec-lnk:hover { text-decoration: underline; text-underline-offset: .2em; }
/* 可折叠的一块：整行标题可点，左边绿色的勾、件数，右边箭头；展开时箭头转下来 */
button.sec-tog { display: flex; align-items: center; gap: 8px; width: 100%; padding: 0; border: 0; border-radius: 6px; background: none; color: var(--ink);
  font-size: 15px; font-weight: 600; line-height: 1.6; text-align: left; }
button.sec-tog:hover, button.sec-tog:active { background: none; transform: none; }
.sec-tog > .ic:first-child:not(.chev) { width: 16px; height: 16px; color: var(--green); }
.sec-tog > .chev { margin-left: auto; }
.sec-fold.is-open > .sec-tog { margin-bottom: 6px; }
.sec-fold.is-open > .sec-tog > .chev { transform: rotate(90deg); }

/* 一行待办：左边圆圈（点了完成，已完成的是实心的），中间标题和信息，右边类型小方块；悬停出删除（手机上一直在） */
.tk-sec + .tk-sec { margin-top: 22px; }
.tk-sec-h { display: flex; align-items: center; gap: 8px; margin-bottom: 2px; color: var(--muted); font-size: 13px; font-weight: 600; letter-spacing: .02em; }
.tk-sec-h small { color: var(--faint); font-weight: 400; font-variant-numeric: tabular-nums; }
.tk-sec.is-late .tk-sec-h { color: var(--red); }
.tk-row { --row-bg: var(--panel); position: relative; display: flex; align-items: flex-start; gap: 12px; padding: 11px 4px; border-top: 1px solid var(--line);
  background: var(--row-bg); }
.tk-row:first-child, :is(.sec-h, .tk-sec-h, .sec-tog) + .tk-row { border-top: 0; }
button.tk-ck { position: relative; flex: none; width: 22px; height: 22px; margin-top: 1px; padding: 0; border: 1.6px solid #b8b8be; border-radius: 50%;
  background: var(--panel); transition: background-color .18s ease, border-color .18s ease; }
.tk-ck::after { content: ""; position: absolute; left: 6.5px; top: 3px; width: 5px; height: 10px; border: solid var(--on-dark); border-width: 0 2px 2px 0;
  transform: rotate(45deg) scale(0); transition: transform .18s ease; }
button.tk-ck:hover { border-color: var(--ink); background: var(--soft); }
button.tk-ck:active { transform: none; }
button.tk-ck.late { border-color: var(--red); }
button.tk-ck.q { border-color: var(--yellow-ink); }
button.tk-ck.on, button.tk-ck.on:hover { border-color: var(--ink); background: var(--ink); }
.tk-ck.on::after { transform: rotate(45deg) scale(1); }
button.tk-b { flex: 1; min-width: 0; display: block; padding: 0; border: 0; border-radius: 4px; background: none; color: inherit; font-weight: 400;
  line-height: inherit; text-align: left; }
button.tk-b:hover, button.tk-b:active { background: none; transform: none; }
.tk-t { display: block; color: var(--ink); font-size: 14.5px; line-height: 1.45; overflow-wrap: anywhere; transition: color .3s ease; }
.tk-b:hover .tk-t { text-decoration: underline; text-underline-offset: .2em; text-decoration-color: var(--line-2); }
.tk-m { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 0; margin-top: 1px; color: var(--muted); font-size: 12.5px; line-height: 1.5; }
.tk-m > span + span::before { content: "·"; margin: 0 6px; color: var(--faint); }
.tk-m .red { color: var(--red); }
.tk-m > span:has(> .tag) { margin-left: 8px; }   /* 小标签前面不加「·」，空一点就够 */
.tk-m > span + span:has(> .tag)::before { content: none; }
.tk-m > span:first-child:has(> .tag) { margin-left: 0; }
.tk-row > .tico { margin-top: 2px; }
.tk-row.is-going .tk-t { color: var(--muted); text-decoration: line-through; text-decoration-color: var(--faint); }
.tk-row.is-done .tk-t { color: var(--muted); }
.tk-row.is-done > .tico { opacity: .45; }
.tk-row.is-sel { --row-bg: var(--sel); margin: 0 -10px; padding-left: 14px; padding-right: 14px; border-radius: 10px; border-top-color: transparent; }
.tk-row.is-sel + .tk-row { border-top-color: transparent; }
button.tk-del { flex: none; align-self: center; opacity: 0; transition: opacity .15s, background-color .15s, color .15s; }
.tk-row:hover .tk-del, .tk-del:focus-visible { opacity: 1; }
button.tk-del:hover { color: var(--red); }
.tk-row.is-going .tk-del { opacity: 0; }
.tk-sg-acts { flex: none; align-self: center; display: flex; gap: 6px; }   /* 等你确认的「要」「不要」（#776） */
.tk-row.is-folding { overflow: hidden; }   /* 收起时裁掉超出的内容，不压到下面几行上 */
.tk-row.is-folded { display: none; }       /* 收起以后列表又重画了：直接是收起的样子 */

/* 考试倒计时卡片：大数字「N 天后」，下面标题和日期、时间、地点；右上角类型小方块，等你确认的旁边带「待确认」 */
.ex-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(176px, 1fr)); gap: 12px; margin-top: 10px; }
.ex-grid:first-child { margin-top: 0; }
button.ex-tile { position: relative; display: grid; gap: 2px; align-content: start; min-width: 0; padding: 14px 16px 15px; border: 0; border-radius: var(--radius);
  background: var(--soft); color: inherit; font-weight: 400; line-height: 1.6; text-align: left;
  transition: background-color .2s, transform .3s var(--ease-spring); }
button.ex-tile:hover { background: var(--soft-2); }
button.ex-tile:active { background: var(--soft-2); transform: scale(.985); }
.ex-n { display: flex; align-items: baseline; gap: 4px; color: var(--ink); font-size: 32px; font-weight: 650; line-height: 1.1; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums; }
.ex-n small { color: var(--muted); font-size: 13px; font-weight: 500; letter-spacing: 0; }
.ex-t { margin-top: 8px; color: var(--ink); font-size: 14px; font-weight: 600; line-height: 1.4; overflow-wrap: anywhere; }
.ex-m { color: var(--muted); font-size: 12px; line-height: 1.5; }
.ex-tile > .tico { position: absolute; top: 14px; right: 14px; }
.ex-tile > .tag { position: absolute; top: 14px; right: 40px; }
.ex-tile.has-tag > .ex-n { flex-wrap: wrap; padding-right: 80px; }   /* 右上角有「待确认」：大数字（三位数）别伸到标签底下 */

/* 紧凑的一行：类型小方块、标题、课程和地点，右边时间和快到期时的剩余时间；按天分组时每天一个小标题 */
.up-day { display: flex; align-items: baseline; gap: 6px; margin: 14px 0 2px; color: var(--muted); font-size: 12px; font-weight: 600; }
.up-day:first-child { margin-top: 4px; }
.up-day span { color: var(--faint); font-weight: 400; }
button.up-row { display: flex; align-items: center; gap: 10px; width: calc(100% + 16px); margin: 0 -8px; padding: 7px 8px; border: 0; border-radius: 9px;
  background: none; color: inherit; font-weight: 400; line-height: inherit; text-align: left; transition: background-color .15s; }
button.up-row:hover, button.up-row:active { background: var(--soft); transform: none; }
.up-b { flex: 1; min-width: 0; }
.up-t { display: block; color: var(--ink); font-size: 14px; line-height: 1.4; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.up-c { display: block; color: var(--muted); font-size: 12px; line-height: 1.45; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.up-c:empty { display: none; }
.up-w { flex: none; text-align: right; font-size: 12.5px; color: var(--text); font-variant-numeric: tabular-nums; line-height: 1.4; }
.up-w small { display: block; color: var(--faint); font-size: 11.5px; }
.up-w.is-soon, .up-w.is-soon small, .up-w.is-late, .up-w.is-late small { color: var(--red); }

/* 说明文字（ui.js 的 lead、points、more；docs/specs/web-design.md「信息精简」）：顶上一句浅灰小字；分点一行一项、前面小圆点；
   「了解更多」默认折叠，蓝色小字加箭头，展开后里面是浅灰小字 */
.u-lead { margin: 0; color: var(--muted); font-size: 13.5px; line-height: 1.55; }
ul.u-pts { display: grid; gap: 4px; margin: 0; padding: 0; list-style: none; color: var(--text); font-size: 13.5px; line-height: 1.55; }
.u-pts > li { position: relative; padding-left: 14px; }
.u-pts > li::before { content: ""; position: absolute; left: 2px; top: .68em; width: 5px; height: 5px; border-radius: 50%; background: var(--faint); }
details.u-more > summary { display: inline-flex; align-items: center; gap: 4px; color: var(--tint); font-size: 13px; cursor: pointer; list-style: none; }
details.u-more > summary::-webkit-details-marker { display: none; }
details.u-more > summary::after { content: ""; width: 6px; height: 6px; margin-left: 2px; border: solid currentColor; border-width: 0 1.5px 1.5px 0;
  transform: translateY(-2px) rotate(45deg); transition: transform .2s var(--ease-out); }
details.u-more[open] > summary::after { transform: translateY(1px) rotate(225deg); }
* + details.u-more { margin-top: 6px; }
.u-more-b { margin-top: 8px; color: var(--muted); font-size: 13px; line-height: 1.6; }
.u-more-b > * { margin: 0; }
.u-more-b > * + * { margin-top: 6px; }

/* 手机：删除一直露着；考试卡片两列，「待确认」放到卡片最下面（地点那一行下面） */
@media (hover: none), (max-width: 759.98px) { button.tk-del { opacity: 1; } }
@media (max-width: 759.98px) {
  .ex-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .ex-n { font-size: 28px; }
  .ex-tile > .tag { position: static; justify-self: start; margin-top: 6px; }
  .ex-tile.has-tag > .ex-n { padding-right: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .tk-ck, .tk-ck::after, .tk-t, .sec-tog > .chev, button.ex-tile { transition-duration: .01ms; }
}

/* 左栏选中与课程角标；只覆盖左栏，其他页共用的小方块不变 */
.sb :is(.who.on, .nav.on) { background: var(--sb-selected); }
.sb .cn-row.on, .sb .cn.is-open > .cn-row.on { --row-bg: var(--sb-selected); }
.sb .it.is-sel, .sb .it.is-menu { --row-bg: var(--sb-selected); }
.sb .cmono, .sb .cnt:not(.hot) { background: var(--sb-chip); color: var(--text); }
