diff --git a/assets/css/app.css b/assets/css/app.css
index b7f43a7..d64ceb8 100644
--- a/assets/css/app.css
+++ b/assets/css/app.css
@@ -5400,10 +5400,33 @@ button.btn, a.btn, summary.btn, .seg-btn, .icon-btn, .rv-act-btn, .cs-btn, .bf-b
/* ---- Markdown 渲染出来的元素(列表页/预览共用同一套)---- */
.md-p { margin: 0 0 8px; }
.md-p:last-child { margin-bottom: 0; }
-.md-h { margin: 10px 0 6px; font-weight: 500; color: var(--ink); }
-h3.md-h { font-size: 16px; }
-h4.md-h { font-size: 15px; }
-h5.md-h, h6.md-h { font-size: 14px; }
+/* 🔴🔴 v85(用户 2026-10-24):「还有日记功能,markdown 代码都没解析,
+ `##` 后面的是标题,现在显示的还是和正文一样。」
+ ------------------------------------------------------------------
+ 渲染**一直是对的**(`## x` → `
`),问题全在**视觉**:
+ ① 旧规则 `h4.md-h { font-size: 15px }`,而阅读态正文
+ `.note-read-block { font-size: 15px }` —— **两者完全相同**;
+ ② 标题只有 `font-weight: 500`,而 .md-h 继承了正文的行高与颜色
+ ⇒ 用户看到的"标题"和"正文"逐像素一致,当然认为"没解析"。
+ ⇒ 三条一起改,缺一条都还是"看不出是标题":
+ ① **字号必须拉开 ≥2px 且各级之间也要分级**(h4 < h5 那种倒挂是旧 bug:
+ `##` 给的是 h4、`###` 给的是 h5,而旧表里 h4=15px、h5=14px 只差 1px);
+ ② **字重加到 600**(500 在中文无衬线里与常规几乎无差);
+ ③ **加上下边距**,让标题与正文之间出现明确的段落呼吸。
+ ⚠️ 为什么从 h4 起而不是 h1/h2:见 `Markdown::render()` 里的说明 ——
+ h1/h2 留给页面本身,笔记里的标题不许盖过页面标题。
+ 所以这里必须把 **h4/h5/h6 这三级**都调大,光调 h4 不够。 */
+.md-h {
+ margin: 16px 0 8px;
+ font-weight: 600;
+ color: var(--ink);
+ line-height: 1.45;
+}
+.md-h:first-child { margin-top: 0; }
+h3.md-h { font-size: 18px; }
+h4.md-h { font-size: 17px; }
+h5.md-h { font-size: 15.5px; }
+h6.md-h { font-size: 14.5px; color: var(--muted); }
.md-list { margin: 4px 0 8px; padding-left: 22px; }
.md-li { margin: 2px 0; }
/* 多级清单:子层用空心点,层级一眼可分(用户第 12 条"清单可以多级") */
diff --git a/templates/home/_card_agenda.php b/templates/home/_card_agenda.php
index 580919f..c44537f 100644
--- a/templates/home/_card_agenda.php
+++ b/templates/home/_card_agenda.php
@@ -22,18 +22,53 @@ foreach (($todayEvents ?? []) as $ev) {
}
}
-// ---- 掐头去尾:只画"第一个日程前一小时"到"最后一个日程后一小时" ----
+// ---- 掐头去尾:只画"第一个日程前一小时"到"最后一个日程结束后一小时" ----
$startH = 0;
$endH = 24;
if ($timed) {
- $mins = array_map(static function (array $e): int {
- // ⚠️ 变量名带 `ag` 前缀:这是**被 require 进来的片段**,裸赋值 $p 会撞上
- // 宿主模板里同名的 $p(流水线的"片段不许覆盖宿主变量"检查会拦下来)。
- $agHM = explode(':', (string)$e['time']);
- return ((int)($agHM[0] ?? 0)) * 60 + (int)($agHM[1] ?? 0);
- }, $timed);
- $startH = max(0, intdiv(min($mins), 60) - 1);
- $endH = min(24, intdiv(max($mins), 60) + 2);
+ /* 🔴🔴 v85(用户 2026-10-24):「今天的日程,有一个日程到下午五点半,
+ 首页日程刻度只有九点。」
+ 真根因:这里**只按 `time`(开始时间)算范围**,`end_time` 压根没参与
+ ⇒ 一条 08:30–17:30 的日程被当成"8 点半就结束了",
+ 于是 `endH = 8 + 2 = 10`,刻度只画到 09:00,
+ 色块高 540 分钟却只塞进 3 小时的格子里(被 `min(96, …)` 夹住,糊成一片)。
+
+ ⚠️ 判据必须是「**结束时间也参与**」,不能只是"把 +2 改成 +12":
+ 写死一个更大的偏移量,遇到 20:00 结束的日程照样截断,
+ 而且会把"只有一条 9 点日程"的卡片硬拉成 12 小时高(用户嫌空)。
+ ⚠️ `end_time` 可能是空(服务端没给)⇒ 那时按"开始 + 30 分钟"兜底,
+ 与下面算 `hPct` 时的兜底口径**必须一致**,否则刻度与色块又会错位。 */
+ $agStarts = [];
+ $agEnds = [];
+ foreach ($timed as $agEv) {
+ $agHM0 = explode(':', (string)$agEv['time']);
+ $agStart = ((int)($agHM0[0] ?? 0)) * 60 + (int)($agHM0[1] ?? 0);
+ $agStarts[] = $agStart;
+ $agEnd = 0;
+ $agET = (string)($agEv['end_time'] ?? '');
+ if ($agET !== '' && preg_match('/^(\d{1,2}):(\d{2})/', $agET, $agMM)) {
+ $agEnd = ((int)$agMM[1]) * 60 + ((int)$agMM[2]);
+ }
+ /* 跨天(结束 <= 开始)时按"到当天 24 点"算 —— 不能取 24:00 以上的值,
+ 否则 $endH 被夹到 24 之后刻度会凭空多出几行。 */
+ if ($agEnd <= $agStart) {
+ $agEnd = $agStart + 30;
+ }
+ if ($agEnd > 1440) {
+ $agEnd = 1440;
+ }
+ $agEnds[] = $agEnd;
+ }
+ /* ⚠️ 变量名带 `ag` 前缀:这是**被 require 进来的片段**,裸赋值会撞上
+ 宿主模板里的同名变量(流水线的"片段不许覆盖宿主变量"检查会拦下来)。 */
+ $startH = max(0, intdiv(min($agStarts), 60) - 1);
+ /* 🔴 用**结束时间**的上界(向上取整到小时)—— 这就是本条的真修法。
+ `+1` 之后又 `ceil`:17:30 → ceil(1050/60)=18,留半小时缓冲;再 +0(不额外留
+ 一小时),因为卡片本来就矮,"最后一个日程结束再画一整小时空行"太浪费。 */
+ $endH = min(24, (int)ceil(max($agEnds) / 60));
+ if ($endH <= intdiv(max($agStarts), 60)) {
+ $endH = min(24, intdiv(max($agStarts), 60) + 1);
+ }
/* 🔴 v28 用户第 1 条:「今天的日程,并没有定位到红线所在的位置」。
根因:**区间里没有"现在"**,于是红线不画、定位算出来是越界值。
⇒ 让区间永远包含当前时刻(用户要的正是"一眼看到现在处在哪儿")。