/* @layer ui — 五个视图的样式：月、时间轴（周/日）、日程、年。
 *
 * 三条贯穿全部视图的做法（改之前先读）：
 *   1. 网格线是"发丝"，不是"栏杆"。日历天然需要很多条线，所以每一条都必须尽量轻：
 *      小时线用 --hairline，半小时线再降一级用 --decorative-faint，
 *      列与列之间同样是发丝线。真正分区的地方（表头下沿、全天区下沿）才用 --border。
 *   2. "今天"只有一种表达：--today 的实心药丸 + 一层几乎看不见的底色。
 *      不额外再用强调色，否则一屏里两种"重要"会互相抵消。
 *   3. 字阶承担层级：日期数字大且收紧字距，农历/节气等辅助信息小且放开字距。
 *      同一格里出现三种字号是刻意的，不是失误。
 */

/* ================= 月视图 ================= */
.month { display: flex; flex-direction: column; height: 100%; min-height: 0; }

/* 表头不做成一条灰色的"带"——留白就够分区了，底下一条发丝线收口 */
.month__head {
  flex: none;
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  border-bottom: 1px solid var(--border);
}
.month__head.has-weeknum { grid-template-columns: 34px repeat(7, 1fr); }
.month__weekday {
  padding: 10px 8px 9px;
  font-size: var(--fs-micro);
  /* 星期头是导航标签不是内容：560 + --tracking-caps 已经足够"排过版"，
     620 配 --text-faint 会重到像标题，但它并不是标题。 */
  font-weight: 560;
  letter-spacing: var(--tracking-caps);
  color: var(--text-faint);
  text-align: center;
  text-transform: uppercase;
}
.month__weeknum-head { border-right: 1px solid var(--hairline); }

.month__viewport {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  scrollbar-width: thin;
  scroll-padding-top: 34px;
}
.month__viewport::-webkit-scrollbar { width: 7px; }
.month__viewport::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--text) 16%, transparent); border-radius: var(--radius-pill); }
.month__body { display: flex; flex-direction: column; min-height: 0; }

.month__marker {
  position: sticky;
  top: 0;
  z-index: 6;
  flex: none;
  margin: 0;
  min-height: 34px;
  padding: 8px 12px 6px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-display);
  font-size: var(--fs-small);
  font-weight: 620;
  letter-spacing: var(--tracking-title);
}

.month__week {
  position: relative;
  flex: none;
  min-height: 112px;
  display: grid;
  grid-template-columns: 1fr;
  /* 4(格子上内边距) + 20(.month__num) + 13(.month__badge) − 1 = 36 → 横条泳道压在 35。
     藏掉徽标或压矮数字时，在同一条规则里把这个数改掉。 */
  --month-bars-top: 35px;
  border-bottom: 1px solid var(--hairline);
}
.month__week.has-weeknum { grid-template-columns: 34px 1fr; }
.month__week:last-child { border-bottom: none; }

.month__weeknum {
  display: flex;
  justify-content: center;
  padding-top: 7px;
  font-size: var(--fs-micro);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--tracking-caps);
  color: var(--text-faint);
  border-right: 1px solid var(--hairline);
}

.month__cells { display: grid; grid-template-columns: repeat(7, 1fr); min-height: 0; }

/* padding-top / .month__num 高度 / .month__badge 高度 与 .month__bars 的 top 是一组
   互相咬合的数字（4 + 20 + 13 - 1 = 36，横条泳道压在 35px）。动其中任何一个都要同时改另外几个。
   窄屏与横屏会改动其中的 .month__badge（藏掉）与 .month__num（压矮），所以这串数字
   不再写死在 .month__bars 的 top 上，而是收进 --month-bars-top —— 改高度的地方
   就在自己旁边把这个数一起改掉，不必跨半个文件去找。 */
.month__cell {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: 4px 5px 5px;
  border-right: 1px solid var(--hairline);
  overflow: hidden;
  /* 格子的宽度决定 chip 还能不能写下"时间 + 标题"，而窗口宽度不决定
     （侧栏在不在、周数列在不在、密度是哪档，都会改格子宽而不改窗口宽）。
     所以这个判断交给容器查询——与 .tgev.is-tiny 丢时间用的是同一套理由。 */
  container-type: inline-size;
  transition: background var(--dur-fast) var(--ease);
}
.month__cell:last-child { border-right: none; }
/* 周末浅浅垫一层，是日历的基本可读性，不是装饰。
   走 background-image 而不是 background-color：外月格要在它**下面**再垫一层地
   （见下一条），两层得能叠在一起。后面 .is-today / :hover 用的是 background 简写，
   会把这一层一并清掉——那正是想要的：那两种状态自己就说明了一切。 */
.month__cell:nth-child(6), .month__cell:nth-child(7) {
  background-image: linear-gradient(var(--weekend-tint), var(--weekend-tint));
}
/* 上下月的格子：降的是**格子的地**，不是格子里的字。
   （1）绝不整格 opacity —— .month__cell 自带 border-right: 1px solid var(--hairline)，
        整格 opacity 会把这条竖线一起淡掉，发丝线网格出现断档，
        在白色系统里这是最扎眼的一类不整齐。
   （2）也不能只淡内容。原来是给 .month__badge / .chip / .month__task / .month__more
        叠一层 opacity: 0.5：这些是**字**，而这套墨梯的第三级本来就贴着 AA 的地板
        （--text-faint 最差 4.62:1），再乘 0.5 实测掉到 2.00–2.67:1（十套色板×主题全中）。
        opacity 是压在令牌之上的第二次减淡，令牌怎么修都救不回来。
   层级交给面：内容面是 --surface，外月格退一级到 --bg —— 正是明度梯上相邻的一格
   （浅色 9/255，深色 8/255），一眼看得出"这不是本月"，而字一个都不淡。
   数字仍然多退一级色阶（--text-faint），两条线索叠加。
   （跨天横条 .bar 挂在 .month__bars 上，是 .month__cells 的兄弟节点、
     不是格子的后代，本来就不受格子的规则影响。） */
.month__cell.is-outside { background-color: var(--bg); }
.month__cell.is-today { background: color-mix(in srgb, var(--today) 4%, transparent); }
.month__cell:hover { background: var(--surface-hover); }
.month__cell.is-outside .month__num-text { color: var(--text-faint); }

.month__num { display: flex; justify-content: center; height: 20px; flex: none; }
.month__num-text {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 21px;
  height: 21px;
  padding: 0 6px;
  border-radius: var(--radius-pill);
  font-size: var(--fs-small);
  font-weight: 520;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  color: var(--text-muted);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.month__cell.is-today .month__num-text {
  background: var(--today);
  /* --today 深色下变浅（#ff6f5e），近白前景压上去只有 2.7:1 */
  color: light-dark(hsl(var(--paper-h) 30% 99%), hsl(var(--paper-h) 35% 10%));
  font-weight: 650;
  box-shadow: 0 1px 4px color-mix(in srgb, var(--today) 42%, transparent);
}
/* 光标只画一圈内描边，不填色：填色会和"今天"抢注意力 */
.month__cell.is-cursor { box-shadow: inset 0 0 0 1px var(--accent-line); }
.month__cell.is-cursor.is-today { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--today) 45%, transparent); }

.month__badge {
  height: 13px;
  flex: none;
  margin-top: -1px;
  overflow: hidden;
  color: var(--text-faint);
  font-size: var(--fs-micro);
  line-height: 13px;
  letter-spacing: 0.02em;
  text-align: center;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.month__badge--holiday { color: var(--today); font-weight: 600; }
.month__badge--term { color: var(--text-muted); font-weight: 600; }
.month__badge--work { color: var(--text-faint); }

.month__lane-space { flex: none; }
.month__chips { display: flex; flex-direction: column; gap: 1px; min-height: 0; overflow: hidden; }
.month__more {
  font-size: var(--fs-micro);
  color: var(--text-faint);
  text-align: left;
  padding: 2px 6px;
  border-radius: var(--radius-xs);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.month__more:hover { background: var(--surface-hover); color: var(--text); }

.month__bars {
  position: absolute;
  top: var(--month-bars-top);
  left: 0;
  right: 0;
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  grid-auto-rows: var(--bar-h);
  gap: 1px 2px;
  padding: 0 2px;
  pointer-events: none;
}
.month__week.has-weeknum .month__bars { left: 34px; }
.month__bars > * { pointer-events: auto; }
.month__bars-more {
  position: absolute; right: 4px; top: 4px;
  padding: 1px 5px;
  border-radius: var(--radius-xs);
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  font-size: var(--fs-micro);
  font-variant-numeric: tabular-nums;
  color: var(--text-faint);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.month__bars-more:hover, .month__bars-more:focus-visible { color: var(--text); background: var(--surface-hover); }

/* ---- 当日清单（只在窄屏出现，见文件末尾的「窄屏」一节） ----
   宽屏它是 display:none：不占布局、不进无障碍树、不进 Tab 序。
   它常驻 DOM 而不是由 JS 按宽度决定要不要画，理由写在 month.js 的函数注释里。 */
.month__daylist { display: none; }

/* ================= 时间轴（周/日） ================= */
.tg { display: flex; flex-direction: column; height: 100%; min-height: 0; }

.tg__head { display: flex; }
.tg__gutter { flex: none; width: 56px; border-right: 1px solid var(--hairline); }
.tg__gutter--head { display: flex; align-items: center; justify-content: center; }
.tg__scale { display: inline-flex; align-items: center; gap: 2px; }
.tg__scale button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 21px;
  height: 21px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-xs);
  background: transparent;
  color: var(--text-muted);
  font-size: var(--fs-small);
  line-height: 1;
}
.tg__scale button:hover { color: var(--text); background: var(--surface-hover); }
.tg__scale button:active { background: var(--surface-sunken); }
.tg__gutter--allday {
  display: flex; align-items: center; justify-content: flex-end;
  padding: 0 8px;
  font-size: var(--fs-micro);
  letter-spacing: var(--tracking-caps);
  color: var(--text-faint);
}
.tg__days { flex: 1; display: grid; grid-template-columns: repeat(var(--cols), 1fr); }
.tg__dayhead {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 9px 0 10px;
  border: 0;
  border-right: 1px solid var(--hairline);
  background: transparent;
  color: inherit;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
.tg__dayhead:last-child { border-right: none; }
.tg__dayhead:hover { background: var(--surface-hover); }
.tg__dayname {
  font-size: var(--fs-micro);
  /* 与 .month__weekday 同一个角色（导航标签），字重必须一致 */
  font-weight: 560;
  letter-spacing: var(--tracking-caps);
  color: var(--text-faint);
  text-transform: uppercase;
}
.tg__daynum {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 28px; height: 28px;
  border-radius: var(--radius-pill);
  font-family: var(--font-display);
  font-size: var(--fs-title);
  font-weight: 550;
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--tracking-title);
  color: var(--text);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.tg__dayhead.is-today .tg__daynum {
  background: var(--today);
  color: light-dark(hsl(var(--paper-h) 30% 99%), hsl(var(--paper-h) 35% 10%));
  box-shadow: 0 1px 5px color-mix(in srgb, var(--today) 42%, transparent);
}

/* 全天区是另一种时间，值得一条真正的分隔线 */
.tg__allday {
  display: flex;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  background: color-mix(in srgb, var(--surface-2) 60%, transparent);
}
.tg__allday-grid {
  position: relative;
  flex: 1;
  display: grid;
  grid-template-columns: repeat(var(--cols), 1fr);
  min-height: calc(var(--lanes) * (var(--bar-h) + 2px) + 6px);
  max-height: 96px;
  overflow-y: auto;
}
.tg__allday-col { border-right: 1px solid var(--hairline); }
.tg__allday-col:last-child { border-right: none; }
.tg__allday-bars {
  position: absolute; inset: 3px 2px;
  display: grid;
  grid-template-columns: repeat(var(--cols), 1fr);
  grid-auto-rows: var(--bar-h);
  gap: 2px;
}

.tg__scroll { flex: 1; overflow-y: auto; overscroll-behavior: contain; }
.tg__body { display: flex; min-height: calc(var(--hour-h) * 24); }

.tg__gutter--body { position: relative; }
.tg__hour {
  height: var(--hour-h);
  position: relative;
  text-align: right;
  padding-right: 8px;
}
.tg__hour span {
  position: relative;
  top: -7px;
  font-size: var(--fs-micro);
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}
.tg__hour:first-child span { visibility: hidden; }

.tg__cols { flex: 1; display: grid; grid-template-columns: repeat(var(--cols), 1fr); }
.tg__col { position: relative; border-right: 1px solid var(--hairline); }
.tg__col:last-child { border-right: none; }
.tg__col.is-today { background: color-mix(in srgb, var(--today) 3.5%, transparent); }
/* 整点一条发丝线，半点再降一级——两者同粗会让一小时看起来像两小时 */
.tg__slot {
  height: var(--hour-h);
  border-bottom: 1px solid var(--hairline);
  background-image: linear-gradient(var(--decorative-faint) 1px, transparent 1px);
  background-size: 100% 50%;
  background-position: 0 50%;
  background-repeat: no-repeat;
  transition: background-color var(--dur-fast) var(--ease);
}
.tg__slot:hover { background-color: var(--surface-hover); }

.tg__now {
  position: absolute; left: 0; right: 0; height: 0; z-index: 5; pointer-events: none;
  border-top: 1.5px solid var(--today);
  /* 从左向右淡出，避免一整条硬线把视觉切断 */
  border-image: linear-gradient(90deg, var(--today) 0%, var(--today) 60%, color-mix(in srgb, var(--today) 25%, transparent) 100%) 1;
}
.tg__now-dot {
  position: absolute; left: -4px; top: -4.5px;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--today);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--today) 20%, transparent);
}

.tgev {
  position: absolute;
  padding: 4px 7px;
  border-radius: var(--radius-xs);
  border-left: 2.5px solid var(--chip-color);
  /* 事件块用同色系的浅渐变，比纯色块更有材质感 */
  background: linear-gradient(160deg,
    color-mix(in srgb, var(--chip-color) 20%, var(--surface)) 0%,
    color-mix(in srgb, var(--chip-color) 11%, var(--surface)) 100%);
  color: var(--text);
  font-size: var(--fs-small);
  line-height: 1.4;
  overflow: hidden;
  cursor: pointer;
  z-index: 2;
  transition: box-shadow var(--dur-fast) var(--ease), filter var(--dur-fast) var(--ease), transform var(--dur-press) var(--ease);
}
.tgev:hover { box-shadow: var(--shadow-md); z-index: 4; filter: brightness(1.03) saturate(1.05); }
.tgev:active { transform: scale(0.99); }
.tgev.is-selected { box-shadow: 0 0 0 2px var(--accent); z-index: 6; }
/* 「办完了」在三个面必须是同一种表达：删除线 + 退一级墨，不用 opacity
   （见 components.css 的 .chip.is-done）。opacity: 0.5 把字和它自己的底一起往
   页面色拉，实测只剩 2.90:1（浅色）/ 3.76（深色）——淡掉的同时也淡掉了这条信息本身。
   这里退到 --text-muted 而不是待办面的 --text-faint：.tgev 的底是带色的，
   第三级墨在上面本来就不过 AA（3.85:1），见上一条。 */
.tgev.is-done { color: var(--text-muted); }
.tgev.is-done .tgev__title { text-decoration: line-through; }
.tgev.is-dragging { opacity: 0.85; box-shadow: var(--shadow-lg); z-index: 30; cursor: grabbing; }
.tgev__time {
  font-size: var(--fs-micro);
  color: var(--text-muted);
  margin-right: 5px;
  font-variant-numeric: tabular-nums;
}
.tgev__title { font-weight: 550; letter-spacing: var(--tracking-body); }
/* 地点走第二级墨而不是第三级：.tgev 的底是"分类色 20% + 纸"，比纸暗一截，
   第三级墨压上去实测最差 3.85:1（浅色）/ 4.17（深色，十个分类色 × 五套色板取最差）。
   同一块底上 .tgev__time 本来就是 --text-muted（5.46 / 6.35），两行归一级。 */
.tgev__loc {
  display: block;
  font-size: var(--fs-micro);
  color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tgev.is-tiny { display: flex; align-items: center; gap: 5px; padding-top: 0; padding-bottom: 0; }
.tgev.is-tiny .tgev__loc { display: none; }
.tgev__resize { position: absolute; left: 0; right: 0; bottom: 0; height: 7px; cursor: ns-resize; }
.tgev__resize:hover { background: color-mix(in srgb, var(--chip-color) 40%, transparent); }
.tgev--ghost { background: var(--accent-soft); border-left-color: var(--accent); opacity: 0.9; pointer-events: none; }

.is-dragging { cursor: grabbing; user-select: none; }

/* ================= 日程视图 =================
   这是全应用字阶差最大的一屏：日期是"标题"，事件是"正文"。
   所以日期用 display 字体、收紧字距；事件行不画盒子，只留一枚方形色标。 */
.agenda { height: 100%; overflow-y: auto; padding: 4px 0 48px; }
.agenda--empty { display: flex; align-items: center; justify-content: center; color: var(--text-faint); }

.agenda__day {
  display: flex;
  gap: 18px;
  padding: 14px 24px 16px;
  border-bottom: 1px solid var(--hairline);
}
.agenda__day.is-today { background: color-mix(in srgb, var(--today) 3.5%, transparent); }

.agenda__head { flex: none; width: 104px; display: flex; align-items: baseline; gap: 9px; }
.agenda__date {
  font-family: var(--font-display);
  font-size: var(--fs-display);
  font-weight: 600;
  line-height: 1;
  letter-spacing: var(--tracking-hero);
  font-variant-numeric: tabular-nums;
}
.agenda__day.is-today .agenda__date { color: var(--today); }
.agenda__meta { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.agenda__weekday { font-size: var(--fs-small); color: var(--text-muted); }
.agenda__month { font-size: var(--fs-micro); color: var(--text-faint); letter-spacing: 0.02em; }
/* 只有"今天"配得上红色；"明天""昨天"是中性信息 */
.agenda__relative { font-size: var(--fs-micro); color: var(--text-muted); font-weight: 600; letter-spacing: 0.02em; }
.agenda__day.is-today .agenda__relative { color: var(--today); }

.agenda__list { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.agenda__item {
  display: flex; align-items: baseline; gap: 10px;
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  min-width: 0;
  transition: background var(--dur-fast) var(--ease), transform var(--dur-press) var(--ease);
}
.agenda__item::before {
  content: '';
  flex: none;
  align-self: center;
  width: 7px; height: 7px;
  border-radius: 2px;
  background: var(--chip-color);
}
.agenda__item:active { transform: scale(0.995); }
.agenda__item:hover { background: var(--surface-hover); }
.agenda__item.is-selected { background: var(--accent-soft); box-shadow: inset 0 0 0 1.5px var(--accent-line); }
.agenda__item.is-done::before { opacity: 0.4; }
.agenda__item.is-done .agenda__title { text-decoration: line-through; color: var(--text-faint); }
.agenda__time {
  flex: none;
  width: 100px;
  font-size: var(--fs-small);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.agenda__title {
  font-size: var(--fs-body);
  font-weight: 500;
  letter-spacing: var(--tracking-body);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.agenda__loc, .agenda__tag, .agenda__badge { font-size: var(--fs-small); color: var(--text-faint); }
.agenda__tag { color: var(--text-muted); }

@media (max-width: 640px) {
  .agenda__day { flex-direction: column; gap: 8px; padding: 12px 14px 14px; }
  .agenda__head { width: auto; }
}

/* ================= 年视图 =================
   十二个月同时在场，任何一点多余的装饰都会乘以 12。
   所以这里只有一件事在用颜色：当天的忙碌程度。 */
.year {
  height: 100%;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(212px, 1fr));
  gap: 24px 20px;
  padding: 22px;
}
.year__month { display: flex; flex-direction: column; gap: 7px; }
.year__title {
  align-self: flex-start;
  padding: 2px 7px;
  margin-left: -7px;
  border: 0;
  border-radius: var(--radius-xs);
  background: transparent;
  color: var(--text);
  font-size: var(--fs-body);
  font-weight: 600;
  letter-spacing: var(--tracking-title);
  text-align: left;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
.year__title:hover { background: var(--surface-hover); }
.year__weekdays, .year__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.year__weekdays span {
  text-align: center;
  padding-bottom: 2px;
  font-size: var(--fs-micro);
  letter-spacing: var(--tracking-caps);
  color: var(--text-faint);
}
.year__day {
  aspect-ratio: 1;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-tiny);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  border-radius: var(--radius-xs);
  color: var(--text-muted);
  /* 忙碌程度是这一屏唯一用颜色编码的东西，梯度必须够浅：12 个月同时在场，任何一点浓度都会被乘以 12。
     8.5 的斜率还有第二个问题：最深那一档（level 4 = 34% 强调色）把压在上面的日期数字
     实测压到 4.37:1（深色 / 松绿）、4.50（浅色 / 墨），贴着或跌破 AA ——
     底是用来读数字的，不是用来盖住数字的。收到 7（最深 28%）之后最差 5.02 / 5.12，
     四档的差别照样看得出来。 */
  background: color-mix(in srgb, var(--accent) calc(var(--level) * 7%), transparent);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
/* 与 .minical__day.is-outside、.month__cell.is-outside 同一条：外月只靠**色阶**分级，
   不叠 opacity。本月是 --text-muted、外月是 --text-faint，已经差一级；
   再乘 0.4 实测只有 1.82:1（浅色，全色板最差），比"退到后面去"低了整整一个数量级。
   忙碌底也一并撤掉：一是它本来就是**重复计数**——同一个日期在年视图里出现两次
   （本月一次、相邻月的补位一次），浓度被画了两遍；二是最深那一档（level 4 = 34% 强调色）
   把第三级墨压到 3.41:1，而第三级墨是外月唯一的标记。撤掉之后回到 6.13:1。
   这一条必须排在 :hover / .is-cursor / .is-today **之前**：它们同为 (0,2,0)，
   排在后面就会把悬停与光标的底一起吃掉——外月的格子照样能点，反馈不能少。 */
.year__day.is-outside { color: var(--text-faint); background: none; }
.year__day:hover { background: var(--surface-hover); color: var(--text); }
.year__day.is-cursor { box-shadow: inset 0 0 0 1px var(--accent-line); color: var(--text); font-weight: 620; }
.year__day.is-today {
  background: var(--today);
  color: light-dark(hsl(var(--paper-h) 30% 99%), hsl(var(--paper-h) 35% 10%));
  font-weight: 650;
  box-shadow: 0 1px 4px color-mix(in srgb, var(--today) 40%, transparent);
}

/* ================================================================
   窄屏
   ================================================================

   一组量出来的数字，下面每一个阈值都从这里推：

     窗口 390px（iPhone 14 Pro）时，侧栏已经是抽屉、主区拿到整个宽度，
     所以 **格子宽 = 窗口宽 ÷ 7 = 55.7px**，格子的内容盒 = 55.7 − 10(内边距) − 1(竖线) = 44.7px。
     一枚 chip 正好铺满这个内容盒。它要装的东西各自多宽（同机实测）：

       固定开销  内边距 12 + 色点 6 + 间距 5                = 23px
       时间      "09:30" 11px tabular                      = 31.3px（+ 一个 5px 间距）
       标题      12px 中文，每字                            = 12.0px
       ↻ 角标   11px（+ 一个 5px 间距）                     = 14.1px

     于是：`09:30站会↻` 需要 67px，而它只有 45px —— 实测 scrollWidth 67 / clientWidth 45。
     .chip 自己没有 overflow:hidden，被裁的是外面的 .month__cell，
     所以溢出的时间是被**从中间切开**的：屏幕上读到的是「14:0(」「19:0(」，半个字形。
     待办同理，标题被省略成「把…」「预…」。这不是难看，这是"看起来像坏了"。

   三个阈值：

     ① 容器 ≤ 80px（格子 ≤ 91；无侧栏时窗口 ≤ 637，带侧栏时窗口 ≤ 885）—— 丢掉时间。
        实测过界点：窗口 890（带侧栏）容器 80.7，时间还在；870 容器 78，时间收起。
        1280 / 1440 的容器是 136 / 159，桌面端一个像素都不受影响。
        23 + 31.3 + 5 = 59.3 是"有时间"的地板，再要两个中文字就得 83.3。
        换句话说，容器不到 80 时，留着时间就意味着标题只剩一个字。
        标题是这条日程唯一没法从别处读出来的东西（几点，点开就有），所以先丢时间。
        与 .tgev.is-tiny 丢时间是同一条理由、同一种做法（容器查询，不是窗口查询）——
        决定装不装得下的是**格子有多宽**，而侧栏在不在、周数列在不在、密度是哪档
        都会改格子宽却不改窗口宽。

     ② 容器 ≤ 66px（窗口 ≤ 539）—— 丢掉 ↻ 角标。它值 14.1px，正好是一个多中文字。
        重复与否在编辑器里一目了然，标题不是。

     ③ 窗口 ≤ 500px 或 高度 ≤ 500px —— 换掉整个表达：格子只剩标记（下一节）。
        500px 是"标题连三个字都放不下"的位置：容器 = 500/7 − 11 = 60.4，
        而三个字要 23 + 36 = 59 —— 再窄一格就掉到两个字，也就是「把…」。
   ================================================================ */

/* ---------------- ①② 格子窄到一定程度，chip 先脱衣服 ---------------- */

@container (max-width: 80px) {
  .month__chips .chip__time { display: none; }
}
@container (max-width: 66px) {
  .month__chips .chip__badge { display: none; }
}

/* ================================================================
   ③ 标记模式：格子退化成"这天有什么形状的东西"，细节交给下面的当日清单
   ================================================================
   参照 iOS 日历的月视图分工：上面是地图，下面是这一天到底有什么。
   一格 56px 装不下"时间 + 标题"是**结构**问题，调字号解决不了——
   12px 的中文再小就不是中文了，而 44.7px 无论如何只够三个半字。

   **形状语言一个字都没改。** 标记就是原来那三枚标记本身，只是把它们周围的字去掉了：

     日程（定时）  6×6 实心方点     = .chip__dot 原样，只是 .chip 自己变成了那颗点
     日程（跨天）  4px 实心色条     = .bar 原样，横跨它覆盖的那几天，只是不写标题
     待办          7×7 空心方框     = .month__task-mark 原样
     回访          7×7 缺口环       = .month__task-mark.is-followup 原样

   三个工作面靠这三种形状互相认得出对方，所以宁可丢字也不能丢形状。

   **触摸目标也是在这里解决的，而且是靠"取消"而不是靠"放大"。**
   原来格子里的 .month__task 是 45×21 的按钮——点不中，还会把本该落在格子上的
   那一下抢走（shell.js 的 [data-date] 委托遇到 [data-occ] / [data-more] 会让路）。
   标记模式下所有标记 pointer-events:none，**格子自己**成为那唯一一个目标：
   390×844 下它是 55.7×59.4，远超 44×44（实测在标记正中心做 elementFromPoint，
   命中的是 .month__cell）。点它 → 光标改变 → 整块重画 → 下面的清单换成这天。
   清单里的行才是 44px 高的真目标。键盘不受影响：标记仍在 Tab 序里，
   焦点环特意画到标记之外（见下），键盘用户拿到的精度比手指高，这是对的。 */

/* 第二支 (max-height: 500px) 不带宽度上限，是**故意**的，别顺手加上去。
   它确实会把桌面上被拖矮的窗口（1440×470、DevTools 停在底部、上下分屏）
   一起卷进来，于是 205px 宽的格子里只有 6px 的圆点，看着不像给这块屏幕做的。
   但另一条路更差，实测过：给这一支加 (max-width: 1024px) 之后，1440×470 下
   农历行回来、数字回到 20px，一格 54.8px 高被"上内边距 4 + 数字 20 + 农历 13
   + 泳道 21 + 下内边距 5"吃干净，.month__chips 分到 **0px** ——
   六个有内容的格子一条日程都不显示（这正是横屏那条要修的病）。
   矮视口下"格子里放得下一行字"本来就不成立，圆点是这里唯一还能说真话的表达。 */
@media (max-width: 500px), (max-height: 500px) {
  /* 农历那一行整行撤掉：42 个格子每格一行「十四/十五/十六」是纹理不是信息，
     而它值 13px × 6 行 = 78px —— 那正好是当日清单能不能存在的差额。
     节气与农历的全称留给当日清单的标题行，那里一行字是读得动的。 */
  .month__badge { display: none; }

  /* 但"放不放假"不能跟着一起没：它改变这天要做什么，不是注解。
     不另画一枚角点——一枚灰点和下面那排彩色标记同大小同形状，
     读者要先分辨"这颗点是什么点"，等于用一个新符号去省一行字，不划算。
     让**数字自己**说：休 = 红字，班 = 加重的深字。纸质中式日历一直是这么排的，
     成本是零像素。（节气不给记号：它是季节注解，不改动今天做什么。）

     :not(.is-today) 不是可选项 —— .month__cell.is-today .month__num-text
     与这条同为 (0,3,0)，本文件在后，不排除就会把"今天"红药丸上的白字
     也刷成红色，红底红字。 */
  .month__cell:not(.is-today):has(.month__badge--holiday) .month__num-text {
    color: var(--today);
    font-weight: 650;
  }
  .month__cell:not(.is-today):has(.month__badge--work) .month__num-text {
    color: var(--text);
    font-weight: 680;
  }

  /* 4 + 20 − 1 = 23。徽标行没了，横条泳道跟着上移。 */
  .month__week {
    --month-bars-top: 23px;
    --bar-h: 6px;
    /* 格子里塞得满满当当的一列：4(上内边距) + 20(数字) + 2(标记行上边距)
       + 12(标记行) + 5(下内边距) = 43，再加 .month__week 自己的 1px 下边线 = 44。
       少算任何一项，矮屏上标记行就会被格子的 overflow:hidden 切掉——
       320×568 实测过一次：写 41 时七个格子的标记行各溢出 3px。 */
    min-height: 44px;
  }

  /* 跨天条：留住"横过去"这件事（一颗点表达不了"周一到周五"），只去掉字。 */
  .month__bars { gap: 2px; }
  .month__bars > * { pointer-events: none; }
  .month__bars .bar {
    padding: 0;
    border-radius: 2px;
    box-shadow: none;
  }
  .month__bars .bar__title,
  .month__bars .bar__badge { display: none; }

  /* 标记行：一行，居中，放不下的**整颗**换到第二行然后被裁掉——
     绝不出现半颗点（半个字形正是这次要修的那个毛病，缩小了重演一遍更糟）。

     所以列距与行距必须分开写。两者都是 4px 时，第一行的行高是 7px（待办标记最高），
     第二行从 7 + 4 = 11px 开始，而盒子只有 12px —— 于是第二行**露出 1px**：
     屏幕上是一排小横杠，正是要躲开的那个毛病换了个方向重演一遍。
     实测 390×844 有 2 枚、320×568 有 4 枚待办标记只剩 1–2px 的一横。
     行距取 12px（等于盒子高度）后，第二行最早从 6 + 12 = 18px 开始，
     无论第一行是 6 还是 7 高，都整行落在盒子外。 */
  .month__chips {
    flex: none;
    flex-direction: row;
    flex-wrap: wrap;
    align-content: flex-start;
    align-items: center;
    justify-content: center;
    column-gap: 4px;
    row-gap: 12px;
    height: 12px;
    margin-top: 2px;
    pointer-events: none;
  }
  .month__chips .chip {
    flex: none;
    width: 6px;
    height: 6px;
    padding: 0;
    gap: 0;
    border-radius: 2px;
    background: var(--chip-color);
  }
  .month__chips .chip__dot,
  .month__chips .chip__time,
  .month__chips .chip__title,
  .month__chips .chip__badge { display: none; }
  .month__chips .chip.is-selected { box-shadow: 0 0 0 1.5px var(--accent); }

  .month__chips .month__task {
    flex: none;
    width: auto;
    height: auto;
    padding: 0;
    gap: 0;
    background: none;
    pointer-events: none;
  }
  .month__chips .month__task-title { display: none; }
  .month__chips .month__task-mark { width: 7px; height: 7px; }

  /* 键盘仍然能走到每一枚标记，所以焦点环要画在标记**外面**才看得见。 */
  .month__chips .chip:focus-visible,
  .month__chips .month__task:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
  }

  /* "还有 N 个"在这里没有落脚点（一行 11px 的字比整行标记还宽）。
     数目由格子的 aria-label 与下面的清单负责，两者都已经是全的。 */
  .month__more,
  .month__bars-more { display: none; }
}

/* ---------------- 当日清单 ----------------

   出现条件是"标记模式 **且** 竖着还有地方"：
   横屏（844×390）走标记模式但没有清单——实测那里一行格子只有 42.1px 高，
   6 行格子加一条清单，两样都得残。横屏的出路是切换条上的「日 / 日程」，
   那两枚标签在这里已经是 44px 的目标。 */

/* 门槛 600 不是拍的：清单最少要装得下自己的标题行(42) + 一行 44 + 10 内边距 = 96，
   而网格的地板是 6 × 44 + 表头 36.5 = 300.5。两者相加 396.5 就是 .month 的最小高度，
   而 .month = 视口高 − 104(两行顶栏) − 64(底部栏让出的位置) − 44(切换条) = 视口 − 212。
   于是视口高 ≥ 608.5 才放得下。取 600 留一点余量给不同的安全区。
   568 高的机器（SE 一代）与横屏因此只有标记网格没有清单——那里 6 行格子
   本来就吃掉整块高度，硬塞一条清单只会两样都残。 */
@media (max-width: 500px) and (min-height: 600px) {
  .month__daylist {
    display: flex;
    flex-direction: column;
    /* 与网格一起分剩余空间：网格的 flex 基准是 6 行 × min-height 44 = 264，
       清单的基准是自己的内容高。两边同权重伸展，于是空日子网格大、忙日子清单大，
       而 6 行格子永远不会被压到 44 以下（min-height 撑着，行高始终相等）。
       390×844 实测：空日子 网格 361 / 清单 234；把清单塞到 8 条以上时
       清单封顶在 46%（290.7）、网格 304.8（每行 51）、清单内部滚动、页面不横不竖溢出。 */
    flex: 1 1 auto;
    min-height: 0;
    max-height: 46%;
    /* 与 .tg__allday 同一种做法：这是另一种时间，值得一条真正的分隔线。
       底仍然是纸，只是退半级——它是网格的注解，不是又一块内容。 */
    border-top: 1px solid var(--border);
    background: color-mix(in srgb, var(--surface-2) 60%, transparent);
  }
  .month__body { flex: none; }

  .month__daylist-head {
    flex: none;
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 11px 15px 7px;
  }
  .month__daylist-date {
    font-family: var(--font-display);
    font-size: var(--fs-lead);
    font-weight: 620;
    letter-spacing: var(--tracking-title);
    color: var(--text);
  }
  /* 网格里只剩数字颜色的节假日，在这里恢复成一行全称：「10月1日 星期四 国庆」。
     三种成色要显式写出来——.month__badge--holiday / --term 与这条同为 (0,1,0)，
     本文件在后，只写一条 --text-faint 会把国庆的红一并抹掉（实测抹掉过）。 */
  .month__daylist-badge {
    font-size: var(--fs-micro);
    letter-spacing: 0.02em;
    color: var(--text-faint);
  }
  .month__daylist-badge.month__badge--holiday { color: var(--today); font-weight: 600; }
  .month__daylist-badge.month__badge--term { color: var(--text-muted); font-weight: 600; }

  .month__daylist-items {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
    /* --safe-b 而不是裸 env()：layout.css 在定义处已经把软键盘吃掉的高度扣掉了，
       裸 env() 拿不到那一步，键盘弹起时这条列表底下会多留一段 34px 够不着的空。
       在桌面与"键盘收起"两种情况下，两种写法逐字节等价。 */
    padding: 0 9px calc(10px + var(--safe-b));
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  /* 一行 = 一个 44px 的触摸目标（HIG 下限）。日程与待办同一个盒子，
     区别仍然只在那枚标记上。 */
  .month__daylist .chip,
  .month__daylist .month__task {
    flex: none;
    width: 100%;
    height: 44px;
    padding: 0 10px;
    gap: 10px;
    border-radius: var(--radius-sm);
    font-size: var(--fs-body);
  }
  .month__daylist .chip__time {
    /* "14:00–15:30" 与「全天」不等宽，不钉住的话标题列会参差。 */
    min-width: 76px;
    font-size: var(--fs-tiny);
  }
  .month__daylist .chip__title,
  .month__daylist .month__task-title { font-weight: 500; }
  .month__daylist .chip:active,
  .month__daylist .month__task:active { background: var(--surface-hover); }

  /* 全天/跨天的实心色块在 44px 高的行里是一整条颜料，太重。
     清单里"是不是全天"由时间列的「全天」二字说清楚了，
     所以这里把它退回与定时日程同一种行，只是那枚实心方点要补回来
     —— renderChip 对全天不画点（网格里它是整条 .bar，不需要）。 */
  .month__daylist .chip.is-allday {
    background: none;
    color: var(--text);
  }
  .month__daylist .chip.is-allday::before {
    content: '';
    flex: none;
    width: 6px;
    height: 6px;
    border-radius: 2px;
    background: var(--chip-color);
  }
  .month__daylist .chip.is-allday .chip__time { color: var(--text-muted); }
  .month__daylist .chip.is-allday .chip__badge { color: var(--text-faint); }

  .month__daylist-empty {
    padding: 2px 15px 16px;
    color: var(--text-faint);
    font-size: var(--fs-small);
  }
}

/* ---------------- 横屏：高度才是稀缺的那一维 ----------------
   844×390 实测：一行格子 42.1px 高，而 4 + 数字 20 + 农历 13 + 5 = 42 —— 
   数字和农历两行就把格子吃光了，.month__chips 分到 1.1px，
   四条 chip 全部 clipped:true。也就是说横屏下月视图**一条日程都不显示**。
   上面的标记模式已经撤掉农历行并把标记压到 12px（4+20+12+5 = 41 ≤ 42.1），
   这里再把数字收一档，给标记留出富余。 */

/* 条件必须与上面标记模式那一支保持一致（都是裸的 max-height: 500px）：
   这里的 --month-bars-top 是按"农历行已经没了"算出来的，两条一旦错开，
   跨天条就会压在农历上。 */
@media (max-height: 500px) {
  .month__num { height: 18px; }
  /* 只收盒子，不收字号：字阶归 calendar-shell.css 的 .calshell .month__num-text 管，
     那条是 (0,2,0)，这里写 font-size 是一条死声明（844×390 实测仍是 13.5px）。
     13.5 的字在 19px 的药丸里本来就装得下，所以要的只是盒子矮一档。 */
  .month__num-text { min-width: 19px; height: 19px; }
  /* 数字收到 18：4 + 18 + 2 + 12 + 5 = 41，加下边线 = 42。 */
  .month__week { --month-bars-top: 21px; min-height: 42px; }
  .month__weekday { padding: 6px 8px 5px; }
}

/* ================================================================
   窄屏：周 / 日
   ================================================================
   有人问过要不要在窄屏把「周」降级成三天，或者干脆把人推去日视图。**两条都不做。**

   降成三天是撒谎：range/step/title 三个函数都写着七天，顶栏标题会照旧写
   「8月24–30」，而画面上只有三列——用户看到的和读到的对不上，比挤更糟。
   何况周视图存在的理由就是"一周长什么形状"，砍成三天等于把它变成另一个视图。
   自动跳去日视图更糟：视图是用户按 2 或点标签选的，选完却给另一个，
   那是软件在替人改主意。

   做的是另外两件事：
     1. 让块**优雅地脱字**，而不是把字裁成半个字形——和月视图 chip 同一条原则，
        也和 .tgev.is-tiny 早就在做的事同一条路（容器查询，块自己决定）。
        320px 下一列只有 39.7px，块的内容盒 = 39.7 − 10(内边距) − 2.5(左边框) = 27.2，
        两个中文字要 24 —— 所以 34px 以下丢时间与地点，22px 以下连标题也丢掉，
        剩一条纯色块。纯色块不是"没内容"：它精确说出了几点到几点、有多长、
        和谁重叠，那正是时间轴唯一能说而列表说不了的事。点它就是编辑器。
     2. 顶上那一行日期本来就是 [data-goto] 的按钮，窄屏下它 47×52，
        比 44×44 还大——"看形状用周，看细节点那天"是这里的分工，
        跟月视图"点格子看下面的清单"是同一句话。

   左侧刻度栏 56px 在 320px 下是整宽的 17.5%。"07:00" 11px tabular 实测 31.3px，
   收到 46 / 42 仍装得下（padding-right 一起收），七列各自多拿到 1.4 / 2.0px。 */

@media (max-width: 640px) {
  .tg__gutter { width: 46px; }
  .tg__hour { padding-right: 6px; }
  .tg__gutter--allday { padding: 0 6px; }

  .tgev {
    padding: 3px 5px;
    /* 只在窄屏让每个块成为容器：桌面端的 .tgev 一个字都不动。
       （.is-tiny 的容器是 calendar-shell.css 一直就有的，两者不冲突。） */
    container-type: inline-size;
  }
  @container (max-width: 34px) {
    .tgev__time,
    .tgev__loc { display: none; }
  }
  @container (max-width: 22px) {
    .tgev__title { display: none; }
  }
}

@media (max-width: 400px) {
  .tg__gutter { width: 42px; }
  .tg__hour { padding-right: 5px; }
}

/* 横屏：可滚的时间轴不缺高度，缺高度的是它上面那两条。
   全天区最多让出 96px 是给桌面定的，390px 高的屏上那是四分之一屏。 */
@media (max-height: 500px) {
  .tg__dayhead { padding: 5px 0 6px; }
  .tg__allday-grid { max-height: 58px; }
}

/* ================================================================
   窄屏：年
   ================================================================
   .year 的 auto-fill minmax(212px) 在 390px 下只排得下一列：
   十二个月摞成一条 4000px 的长卷，翻到十月要滚四屏——
   而年视图存在的唯一理由是"十二个月同时在场"。一列的年视图不是年视图，
   是十二个月视图排队。

   两列：390px 下每月 (390 − 24 − 12) ÷ 2 = 177，格子 177/7 ≈ 24.3px，
   12px 的两位数宽 13.4px，装得下。整年的高度从 ~4000 收到 ~1200，
   一屏能看四个月，翻一次半屏到年底。
   320px 下每月 143、格子 19.6px —— 数字降到 11px（11.5px 宽）保住余量。 */

@media (max-width: 640px) {
  .year {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 18px 12px;
    padding: 16px 12px calc(20px + var(--safe-b));   /* 走 --safe-b，理由见 .month__daylist-items */
  }
  .year__month { gap: 5px; }
  .year__weekdays, .year__grid { gap: 1px; }
}

/* 月名是这一屏的**主要**去处（点它 → 那个月的月视图），所以它该是个够得着的目标。
   与切换条、日程行同一个条件（宽窄 或 横过来的手机）。
   日格子够不着是这一屏的既定代价：7 × 44 = 308px 就意味着一列，
   而一列的年视图不成立（见上）。日格子是次要通道，点偏了顶多落到隔壁一天，
   退回来一步的事；月名点偏了是翻错月份。 */
@media (max-width: 640px), (max-height: 500px) {
  .year__title { min-height: 44px; padding: 6px 9px; }
}

@media (max-width: 360px) {
  .year { gap: 16px 10px; padding-left: 10px; padding-right: 10px; }
  .year__day { font-size: var(--fs-micro); }
}

/* ================================================================
   窄屏：日程
   ================================================================
   这一屏本来就最适合窄屏（640 以下把日期头竖过来那条规则早就在了）。
   剩下的是手指：一行 .agenda__item 只有 6+6 内边距 + 一行字 ≈ 28px 高，
   而 HIG 的下限是 44。时间列的 100px 也是给桌面定的，
   "14:00–15:30" 12px tabular 实测 62px，窄屏收到 78 起（12 小时制会自己撑开）。 */

/* (max-height: 500px) 同样是横过来的手机：844×390 下按宽度写的规则一条都不命中，
   一行会退回 32.3px。理由与 calendar-shell.css 里那条一模一样。 */
@media (max-width: 640px), (max-height: 500px) {
  .agenda__item {
    min-height: 44px;
    padding: 6px 10px;
    gap: 9px;
  }
  .agenda__time { width: auto; min-width: 78px; }
  .agenda__list { gap: 1px; }
}

/* 320 下"标题 + 地点 + 标签"三样并排，标题会被挤到一两个字。
   标签是筛选用的把手，不是这一行的内容，先让它退场。 */
@media (max-width: 400px) {
  .agenda__tag { display: none; }
  .agenda__day { padding: 12px 10px 14px; }
}
