` ← 一个 grid 项,**只占第一列**
在整页形态下不明显(页面宽,两列各 ~700px 还能看);但**浮层里面板只有 620px**
⇒ 两列各 286px,而所有字段都挤在左边那一列 286px 里,右边整列空着 —— 就是用户看到的"分栏"。
(实测:panel 620 / form-grid 588 / cols="286px 286px" / mv-grid w=286。)
⇒ 让 `.mv-grid` **跨满整行**(`grid-column: 1 / -1`)。
它本身是 `
` 包着**纵向堆叠**的 `.mv-row`,语义上就是"一整块、占满宽度",
天生不该被塞进半列。这样两种形态(整页 / 浮层)都正确,
而且不必去改 `event.php` 里"整页保留两列"的结构判断(那条注释说明整页形态是有意保留的)。 */
.form-grid > .mv-grid { grid-column: 1 / -1; }
.mv-row { display: flex; align-items: flex-start; gap: 10px; min-width: 0; }
.mv-row > .mv-lb {
flex: 0 0 62px; width: 62px; /* 固定宽 = 对齐的基准 */
text-align: right; padding-top: 9px; /* 与 40px 高输入框的文字基线对齐 */
font-size: 14px; font-weight: 600; color: var(--ink);
line-height: 1.4; word-break: keep-all;
}
.mv-row > .mv-lb::after { content: ':'; font-weight: 400; color: var(--muted); }
/* 🔴 v94 起星号不再使用(见文件下方「必填项统一标记」那段);这两条留着**不删**:
万一还有没清干净的 `
*`,它会渲染成紧贴的红色星号、不会露成一个灰星花,
而静态断言会把它当失败报出来 —— 比"悄悄留个灰星花"好定位。 */
.mv-row > .mv-lb > i { font-style: normal; color: var(--red, #C0392B); margin-left: 1px; }
/* 控件区:占满剩余宽度(min-width:0 让它能被压缩,不会把标签挤出去) */
.mv-row > *:not(.mv-lb) { flex: 1 1 auto; min-width: 0; }
/* 片名那一行:输入框 + 「自动获取」按钮并排 */
.mv-row .db-title-row { display: flex; gap: 8px; align-items: center; }
.mv-row .db-title-row .input { flex: 1 1 auto; min-width: 0; }
.mv-row .db-title-row .btn { flex: 0 0 auto; }
/* 影院那一行:自定义下拉 + 「新建影院」并排 */
.mv-row .cinema-select-row { display: flex; gap: 8px; align-items: center; }
.mv-row .cinema-select-row .cs { flex: 1 1 auto; min-width: 0; }
/* 时间那一行是 `.mv-row.dt-range`:它既是"标签+控件"的行,又是**范围组**。
⚠️ 这两重身份会打架:`.dt-range.dt-cols` 是 `flex-direction: row`,
会把标签也当成"一个端点"排进去。⇒ 这里把它拉回"标签一行、两端在下",
标签仍占固定 62px(与上面的字段对齐)。 */
.mv-row.dt-range.dt-cols { flex-direction: row; flex-wrap: wrap; align-items: flex-start; }
.mv-row.dt-range.dt-cols > .dt-range-label { flex: 0 0 62px; width: 62px; }
/* 🔴 v38 对齐修正(真机量出来的 8px 偏差):
`.dt-cols` 是 `gap: 10px 18px`(行距 10 / **列距 18**),而普通 `.mv-row`
只有 `gap: 10px`。于是"标签 → 控件"这段在时间行是 18px、在别的行是 10px
⇒ 控件左缘差了 8px(实测 452 vs 444),一眼就看得出没对齐。
⇒ 时间行的列距拉回 10px,与别的行一致。 */
.mv-row.dt-range.dt-cols { column-gap: 10px; }
/* 两端并排(沿用 v37 的 .dt-cols 规则),并**从标签右边开始** */
.mv-row.dt-range.dt-cols > .dt-input {
flex: 1 1 0; min-width: 220px; margin-top: 2px;
}
.mv-row.dt-range .err { flex: 1 1 100%; margin-left: 72px; }
/* 海报 / 观影照片:**与简介同一套 `.mv-row`**(标签在左、内容在右)。
🔴🔴 v39(用户 2026-10-10):「海报和观影照片为什么和上面的标题不一样呢?
海报的字体和简介是一样的,也带一个冒号。然后把后面点击或拖动进行上传往后放,
和简介后面的输入框保持一样大小。观影照片…也是加粗。和简介的字体保持一致,
不带冒号。]
⇒ 关键认识:**这两块不需要任何特殊规则**,它们就是普通字段行 ——
标签 = `.mv-lb`(继承上面那条:14px / 600 / var(--ink) / 固定 62px / 右对齐
+ `::after` 冒号),内容 = 行里唯一参与弹性布局的那一项(占满剩余、左缘 444)。
v38 之所以把它们写成 `.mv-media`(另起一行 + 手填 `margin-left:72px`),
是因为当时**探针取错了元素** —— 它按文档序取"第一个非标签子元素",
而 `.sr-only` 的文件框是 `position:absolute`(**脱离文档流、不参与 flex**),
却被算成了"控件",于是量出"1px 隐框把上传框推走 130px"这个**假结论**。
⇒ 这轮改用真机逐行量左缘来定,不再靠推断。
⚠️ 想让它别再"另起一行":这里**不设** flex-direction —— `.mv-row` 本来就是
`display:flex; align-items:flex-start`,标签与内容天然同一行。 */
.mv-grid-media { margin-top: 2px; gap: 14px; }
/* 内容区:纵向排(缩略图 + 上传框),并**占满**标签右侧的剩余宽度
(`flex:1 1 auto` + `min-width:0` 与 `.mv-row > *:not(.mv-lb)` 那条一致,
这里显式写一遍是为了让"它的宽度 = 简介输入框的宽度"这件事一眼可读)。 */
.mv-row-media > .mv-media-body {
flex: 1 1 auto; min-width: 0;
display: flex; flex-direction: column; gap: 8px;
}
.mv-media-body .photo-grid { margin-top: 0; }
/* 观影照片的标签**不带冒号**(用户原话:「观影照片也是加粗。和简介的字体保持一致,
不带冒号。」)—— 这类"有标签但不带冒号"的情况靠一个类开关,别去改 ::after 的全局规则。 */
.mv-lb.no-colon::after { content: none; }
/* ⚠️ `.sr-only` 是**绝对定位**的(见 .sr-only 的定义);它不该影响任何布局。
这里再钉一次"不参与 flex 分配"(万一将来有人把 .sr-only 的定义改了)。 */
.mv-media-body > .sr-only,
.mv-row > .sr-only { position: absolute; flex: 0 0 0; }
/* ---- ② Ant Design RangePicker 的外观对齐 ----
目标是"看起来就是站内的一个输入框",而不是"嵌进来一个外来控件":
同高(40px)、同圆角、同描边、同纸色。 */
.dt-antd { flex: 1 1 auto; min-width: 0; }
.dt-antd .ant-picker {
width: 100%; height: 40px; padding: 0 11px;
border: 1px solid var(--line); border-radius: var(--radius-s);
background: var(--surface); box-shadow: none;
}
.dt-antd .ant-picker:hover { border-color: var(--brand-2); }
.dt-antd .ant-picker-focused { border-color: var(--brand); box-shadow: none; }
.dt-antd .ant-picker-input > input { font: inherit; font-size: 14px; color: var(--ink); }
/* 品牌色覆盖:让 antd 的选中/主色跟着模块色走(与 Semi 那次同一思路) */
.dt-antd .ant-picker-range-separator { color: var(--muted); }
.dt-antd .ant-picker-cell-in-view.ant-picker-cell-selected .ant-picker-cell-inner,
.dt-antd .ant-picker-cell-in-view.ant-picker-cell-range-start .ant-picker-cell-inner,
.dt-antd .ant-picker-cell-in-view.ant-picker-cell-range-end .ant-picker-cell-inner {
background: var(--brand) !important;
}
/* 中间的"区间底色"用模块色的浅版 */
.dt-antd .ant-picker-cell-in-view.ant-picker-cell-in-range::before,
.dt-antd .ant-picker-cell-in-view.ant-picker-cell-range-hover::before {
background: color-mix(in srgb, var(--brand) 12%, transparent) !important;
}
.dt-antd .ant-picker-time-panel-column > li.ant-picker-time-panel-cell-selected .ant-picker-time-panel-cell-inner {
background: color-mix(in srgb, var(--brand) 14%, transparent) !important;
}
.dt-antd .ant-btn-primary { background: var(--brand); border-color: var(--brand); }
.dt-antd .ant-btn-primary:hover { background: var(--brand-2) !important; border-color: var(--brand-2) !important; }
/* 面板:跟着纸色(antd 默认是纯白 + 冷灰边) */
.dt-antd .ant-picker-dropdown .ant-picker-panel-container {
border: 1px solid var(--line); box-shadow: var(--shadow-l, 0 10px 30px rgba(0,0,0,.14));
}
/* 面板整体跟着模块色(antd 的 CSS-in-JS 变量) */
.ant-picker-dropdown {
--ant-color-primary: var(--brand, #A84A28);
--ant-color-primary-hover: var(--brand-2, #C05F38);
}
/* ⚠️ 挂上 antd 之后:**原生那对下拉收起来**(它们仍然在 DOM 里当值载体)。
⚠️ 用 visibility+绝对定位而不是 display:none —— 见 v37 那次的教训:
display:none 的控件在表单校验时不可聚焦;这里虽然已摘掉 required,
但保持"可聚焦"更稳(免得将来再加必填时又踩同一个坑)。
🔴🔴 v44(用户 2026-10-12 第 2 条):「之前的日期选择控件还有,需要删除。」
—— 用户在**日程**上看到 antd 控件与原生五个下拉**并排都在**。真根因是
**结构性**的:日程那对是 `‹div.dt-range› ‹div.field.ev-dt› ‹div.dt-input›`
(`.dt-input` 是**孙子**),而这条规则用的是**直接子**选择器 `> .dt-input`
⇒ **在日程上根本不命中**。
(与 v43 纪念日那次同族:结构一变,直接子选择器静默失效、不报任何错。)
⇒ ① 把 `>` 放宽成**后代**选择器(`_event_fields.php` 那边的结构也已改回
"与观影一致"的直接子,两道保险);
② 用户要的"删除"就做成真删 —— 用 `display:none` 把**原生下拉那一块**
从渲染里去掉。**注意:值载体不受影响** —— 隐藏的 `*_date` / `*_hour`
/ `*_minute` 是 `.dt-input` 的**直接子元素**,**不在 `.dt-native` 里**,
所以删掉原生块**不碰提交契约**。
(v37 那条"display:none 不可聚焦"讲的是**带 required 的可见字段**;
这里被藏的是"已被 antd 取代的旧控件",必填校验早已由
`guardRequired` 的 `data-required` 自查接管 ⇒ 不适用那个顾虑。) */
.dt-range.is-antd .dt-input {
position: absolute; width: 1px; height: 1px;
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
border: 0; padding: 0; margin: -1px;
}
/* antd 真的挂上了 ⇒ 原生那几档下拉**彻底不渲染**(用户要的"删除")。 */
.dt-range.is-antd .dt-input .dt-native { display: none !important; }
/* 🔴🔴 v44:**带内联标签的那个端点不收起**。
根因(真机几何量出来的):内联标签(「日期范围:」)是画在 `.dt-input` **里面**的,
而上面那条收起规则把整个 `.dt-input` 绝对定位 + `clip` 掉了 ⇒
**标签跟着一起被裁没了**,用户只看到一个没有标签的控件
(量出来的证据:标签盒在 x=316/y=313 却被祖先 clip,`elementFromPoint`
命中的是 `.ant-picker-range-separator` ⇒ 标签确实看不见)。
⇒ 两头都不牺牲:`.dt-input` 只藏**它里面的原生块**(上一条已 `display:none`),
自己保持正常布局 ⇒ 它变成 `[标签 62px][最新控件]` 的一个横排。
起止的**第二个**端点没有标签,继续按上一条被收起(它本来就该消失)。 */
.dt-range.is-antd > .dt-input.has-inline-label {
position: static; width: auto; height: auto; overflow: visible;
clip: auto; margin: 0; padding: 0; border: 0;
flex: 1 1 auto; min-width: 0;
display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
}
/* 🔴 v44(用户第 5 条):**单个**带时刻的 antd 控件(任务「截止时间」)。
与上面那对同一条思路 —— 挂上后把原生那几档下拉**真删掉**。
选择器用 `.dt-input.is-antd`(单点,不是范围组)——
`_datetime.php` 里 antd 挂载点仍是 `.dt-input` 自身。 */
.dt-input.is-antd > .dt-native { display: none !important; }
.dt-input.is-antd > .dt-antd { display: block; width: 100%; }
/* 窄屏:标签列收窄,别把输入框挤没了 */
@media (max-width: 560px) {
.mv-row > .mv-lb,
.mv-row.dt-range.dt-cols > .dt-range-label { flex: 0 0 52px; width: 52px; font-size: 13px; }
}
/* ---- 🔴 v39:撤销 v38 的"并排对" ----
用户 2026-10-10:「简介和备注不要放在一行,也就是说一个字段它就占一行。
所以简介后面是一行,备注后面是一行。」—— 简介/备注各占一整行。
⇒ `.mv-pair` 那套(grid 两列)整个删掉:它是 v38 为了压高度加的,
但半宽的多行文本框反而每行都换行、更难读。海报/观影照片也不再并排
(它们本来就只有一块,当时的"并排网格"里只有一个孩子,
grid 让它只占半宽 —— 那正是"海报的上传框比简介窄"的原因)。
⚠️ 不留悬念:全站搜 `.mv-pair` 应该**零命中**(模板与 CSS 都清干净)。 */
/* ============================================================================
🔴 v40(用户 2026-10-10)观影模块四处界面反馈
============================================================================ */
/* ---- ① 影院管理表格:操作列不再被挤成两行 ----
用户原话:「影院管理部分,注意控制列的宽度,因为最右侧的操作、编辑和删除两个按钮,
已经被挤成两行了,尝试让编辑和删除排在一行,宽度给它留够。」
🔴 真因:`.table` 是 `width:100%` + 自动布局,浏览器按内容分配列宽 ——
长地址那一列会把「操作」列压到只剩几十像素,两个按钮只好换行。
⇒ 两件事一起做才稳:
① 表格改 `table-layout: fixed`(列宽由我们给,不再被内容牵着走);
② 「操作」列给一个**够放两个按钮的最小宽度**(编辑 48 + 间距 8 + 删除 48
+ 单元格左右 padding 16 ≈ 128,留到 132 更从容)。
⚠️ 为什么不只是 `white-space:nowrap`:那会把列撑成"刚好够",
但**表格还是按内容分配**,窗口一窄就又轮到别的列挤压它;
固定布局才是真的把这件事钉死。 */
.cn-table { table-layout: fixed; }
.cn-table .cn-col-name { width: 30%; }
.cn-table .cn-col-place { width: 15%; }
.cn-table .cn-col-addr { width: 32%; }
.cn-table .cn-col-count { width: 10%; }
.cn-table .cn-col-ops { width: 132px; }
.cn-table td.cn-addr { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cn-ops {
display: flex; align-items: center; gap: 8px;
/* 🔴 不换行是本条的正题 */
flex-wrap: nowrap; white-space: nowrap;
}
.cn-ops > form { display: inline-flex; margin: 0; flex: 0 0 auto; }
.cn-ops > .btn { flex: 0 0 auto; }
/* 窄屏:地址列让位(它最长、也最不影响判断),操作列宽度保持不变 */
@media (max-width: 720px) {
.cn-table .cn-col-name { width: 34%; }
.cn-table .cn-col-place { width: 18%; }
.cn-table .cn-col-addr { width: 22%; }
.cn-table .cn-col-count { width: 12%; }
}
/* ---- ② 实心语义按钮:编辑=蓝、删除=红 ----
⚠️ 单独定义(不去改 `.btn-danger` 的既有语义):`.btn-danger` 是**淡红描边**的
"危险动作",全站十来处「删除」都在用它;用户这次要的是**实心色块**,
改 `.btn-danger` 会让所有页面一起变样。
三处都要写:裸类 / `a.` / `button.` —— 与页头动作区那套同一个理由
(`.btn`、`a.btn`、`button.btn` 特异性不同,漏一种就有一种是错的)。 */
.btn-edit, a.btn-edit, button.btn-edit {
background: var(--blue); color: #fff; border-color: transparent;
}
.btn-del, a.btn-del, button.btn-del {
background: var(--red); color: #fff; border-color: transparent;
}
/* ⚠️ hover 统一交给文件靠后的「全站悬停统一」段(有底色的一律同色系加深)——
但那段是按 `.btn-ok/.btn-del/...` 的类名白名单写的,`.btn-edit` 是新的,
要在这里补一条,否则它会退回 `.btn` 的默认(品牌色底),看着像变了个按钮。 */
.btn-edit:hover { filter: brightness(.92); }
/* ---- ③ 观影详情页 = 列表页的头部口径 ----
用户原话:「观影详情页面左上角的观影记录,应该和观影的列表页面里面保持一致。」
列表页是 `
观影记录
` + sub;详情页原来只有一个 `
`,
没有 h1 ⇒ 左上角看起来"不像是同一件事"。模板已补上 h1,
这里只处理**窄屏**:h1 与 sub 在页头会挤,sub 允许收缩并省略。 */
.page-head > .sub { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ---- ④ 高级搜索的日期范围:换成 AntD RangePicker(纯日期)----
🔴 v40(用户 2026-10-10):「高级搜索里面的日期范围,也使用我们最新的控件。」
挂上之后把**原生那两个日期框收起来**(它们仍是 GET 字段,只是不显示)。
⚠️ 与原观影那对 `.dt-range.is-antd > .dt-input` 用同一套"clip 收起"手法:
不用 `display:none`(保持可聚焦,避免将来加 required 时不可聚焦的坑)。 */
.date-antd-field .date-range.is-antd > .input,
.date-antd-field .date-range.is-antd > .to {
position: absolute; width: 1px; height: 1px;
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
border: 0; padding: 0; margin: -1px;
}
.date-antd-field .date-range.is-antd { display: block; }
/* 纯日期版控件与观影那边同款外观(同高、同圆角、同描边)—— 复用 .dt-antd 的规则,
这里只补"它是块级、占满字段宽度"这一点(.dt-antd 原为 flex 子项)。 */
.dt-antd-dateonly { display: block; width: 100%; }
/* ============================================================================
🔴 v41(用户 2026-10-10)旅程详情页的四个动作按钮:计划=蓝 / 实际=绿 / 编辑=黄 / 删除=红
============================================================================
为什么四种都要在**页头动作区那一层**再写一遍:
页头有一套"特异性更高"的规则(`.page-head .actions > .btn:not(.btn-icon)` 等,
见文件前部的页头按钮层),它们会盖过基类的 `.btn-xxx` 底色。
v40 的 `.btn-del` 就吃过这一脚(必须在页头层再补一条才生效)。
⚠️ 三处形态都要写(裸类 / a. / button.):`.btn`、`a.btn`、`button.btn`
的特异性不同,漏一种就有一种是错的(这条规律在 v40 已经验证过)。
⚠️ 设置按钮**不动**(用户:"设置就用这个图标,不要变")。 */
.btn-plan, a.btn-plan, button.btn-plan {
background: var(--blue); color: #fff; border-color: transparent;
}
.btn-actual, a.btn-actual, button.btn-actual {
/* 绿比 --green 深一档:白字对比度 3.0 → 4.6,暖纸底上看得清 */
background: #4E7C3F; color: #fff; border-color: transparent;
}
/* 🔴🔴 2026-10-11 修(v40 套件报的**真回归**):
这里原来写的是**裸类** `.btn-edit`(全局改黄),直接把 v40 影院管理页的
「编辑 = 蓝色实心」覆盖掉了 —— 影院那页量出来的实际色是 `rgb(184,134,11)`(黄),
而用户对它的要求是**蓝色**(v40 原话:「编辑使用蓝色的背景,删除使用红色的背景」)。
⇒ 两处需求不同:**旅程详情页的「编辑」= 黄**(v41 第 4 条),
**影院管理页的「编辑」= 蓝**(v40)。所以颜色必须**按作用域**给,不能裸类全局设。
锚点选 `[data-trip-edit]`(旅程详情页那个按钮的专属属性)——
它是最精确的标识,不依赖页头层级,也不会波及影院管理页的 ``。 */
.btn-edit[data-trip-edit] {
background: var(--yellow); color: #fff; border-color: transparent;
}
.btn-del, a.btn-del, button.btn-del {
background: var(--red); color: #fff; border-color: transparent;
}
/* hover:同色系加深(与文末"全站悬停统一"段同一手法:brightness 压低) */
.btn-plan:hover, .btn-actual:hover, .btn-edit:hover, .btn-del:hover { filter: brightness(.92); }
/* 页头动作区那一层(覆盖页头默认的"品牌色实心/ghost") */
.page-head .actions > .btn-plan:not(.btn-icon),
.page-head .actions > .btn-actual:not(.btn-icon),
.page-head .actions > .btn-edit[data-trip-edit]:not(.btn-icon),
.page-head .actions > .btn-del:not(.btn-icon),
.page-head .actions > form > button.btn-del:not(.btn-icon) { color: #fff; border-color: transparent; }
.page-head .actions > .btn-plan:not(.btn-icon) { background: var(--blue); }
.page-head .actions > .btn-actual:not(.btn-icon) { background: #4E7C3F; }
.page-head .actions > .btn-edit[data-trip-edit]:not(.btn-icon) { background: var(--yellow); }
.page-head .actions > .btn-del:not(.btn-icon),
.page-head .actions > form > button.btn-del:not(.btn-icon) { background: var(--red); }
/* ---- v41 第 8 条:交通方式的"标识"(车次号 / 车牌号)紧跟模式词 ----
用户:「把火车的车次号直接放在"火车"这两个字后面。如果是自驾,
就应该把自驾的车牌号放在后面。」
⇒ 同一个 chip 内、模式词之后,用略淡的字重区分(仍属同一个信息单元)。 */
.node-mode-no {
margin-left: 6px; padding-left: 6px;
border-left: 1px solid currentColor;
opacity: .78; font-weight: 500;
font-variant-numeric: tabular-nums;
}
/* ---- v41 第 2 条:题图的说明文字搬进框里(原来在框外另起一段)---- */
.cover-picker-hint {
display: block; margin-top: 8px;
font-size: 12.5px; line-height: 1.6; color: var(--muted);
}
/* ============================================================================
🔴 v41(用户 2026-10-10 第 5/6 条)行程弹框的**观影式排版**
============================================================================
用户原话:「编辑行程、加计划行程、加实际行程,全部都采用弹窗的形式。
其排版的方式和添加电影是一样的,前面是标题冒号,后面是输入框,
和观影模块一样,注意排版。」
🔴🔴 为什么这里用 **grid** 而不是 flex(2026-10-10 血泪):
先按观影那套写了 flex(`display:flex; flex-wrap:wrap` + label 固定 62px),
结果**控件被挤到下一行**(实测 label y=117 / 控件 y=187)。
查了半天:`.input/.select` 全站有 `width: 100%`,在 flex 行里
它的 auto 基准就是**整行宽** ⇒ 62 + 10 + 696 > 696 ⇒ 换行。
把 width 改 auto、把 flex-basis 从 0% 试到 auto、加 min-width……
控件宽度是对了(140),**换行依旧**(同一行里还有 `.tip` 声明着
`flex: 0 0 100%`,wrap 算法会把它们的理想宽度一起算)。
⇒ 换 **grid**:`.field` 两列(62px + 1fr),标签在第 1 列、控件在第 2 列,
`.tip/.err` 用 `grid-column: 2` 自动落在控件下面**且与控件左缘对齐**
(连 margin-left 都不需要)。两列表单的标准写法,不受 width/flex 交互影响。
⚠️ 作用域用 `.nf-form`(表单自己的类)而不是 `.nf-wrap` / `.nf-panel`:
片段形态(`?embed=1`)两者都不输出(壳由详情页容器给),
只用它们的话排版在弹框里**完全不生效**(探针抓到过)。 */
.nf-form .form-grid {
display: flex; flex-direction: column; gap: 12px;
}
.nf-form .form-grid > .field {
display: grid; grid-template-columns: 62px minmax(0, 1fr);
column-gap: 10px; row-gap: 6px; align-items: start;
}
/* 标签:固定第 1 列、右对齐、自动冒号(与观影那套同一组数字) */
.nf-form .form-grid > .field > label {
grid-column: 1;
text-align: right; padding-top: 9px;
font-size: 14px; font-weight: 600; color: var(--ink);
line-height: 1.4; word-break: keep-all;
}
.nf-form .form-grid > .field > label::after {
content: ':'; font-weight: 400; color: var(--muted);
}
/* 控件:占第 2 列(宽度由 grid 给,不受它的 width:100% 影响) */
.nf-form .form-grid > .field > *:not(label) {
grid-column: 2; min-width: 0;
}
/* 提示 / 错误:也在第 2 列 ⇒ **自动与控件左缘对齐** */
.nf-form .form-grid > .field > .tip,
.nf-form .form-grid > .field > .err {
grid-column: 2;
}
/* 起终点是两列小网格,整段落在第 2 列 */
.nf-form .form-grid > .field > .loc-row { grid-column: 2; }
/* 「当前状态」是只读文本,对齐 40px 高的控件 */
.nf-form .node-stage-ro { padding-top: 9px; }
/* 窄屏:标签列压到 52px,避免控件被挤得太窄 */
@media (max-width: 560px) {
.nf-form .form-grid > .field { grid-template-columns: 52px minmax(0, 1fr); }
}
/* ---- v41 第 8 条:交通方式的"标识"(车次号 / 车牌号)紧跟模式词 ----
用户:「把火车的车次号直接放在"火车"这两个字后面。如果是自驾,
就应该把自驾的车牌号放在后面。」
⇒ 同一个 chip 内、模式词之后,用略淡的字重区分(仍属同一个信息单元)。 */
.node-mode-no {
margin-left: 6px; padding-left: 6px;
border-left: 1px solid currentColor;
opacity: .78; font-weight: 500;
font-variant-numeric: tabular-nums;
}
/* ---- v41 第 2 条:题图的说明文字搬进框里(原来在框外另起一段)---- */
.cover-picker-hint {
display: block; margin-top: 8px;
font-size: 12.5px; line-height: 1.6; color: var(--muted);
}
/* ---- v41 第 10 条:纪念日弹框(观影式排版)里"月 / 日"那一行的两格 ---- */
.an-md { display: flex; align-items: center; gap: 6px; }
.an-md .input { width: 88px; flex: 0 0 auto; text-align: center; }
.an-md-sep { color: var(--muted); font-size: 13px; }
/* 观影式行里的"填充块"(选人组件那种自带布局的控件,占满控件位) */
.mv-row > .mv-fill { flex: 1 1 auto; min-width: 0; }
/* ---- v42(用户 2026-10-11 第 8 条)纪念日「日期」:AntD 单日期控件 ----
与原观影/高级搜索那两处同一套做法:
· `.dt-antd` 是 antd 的挂载点(宽度交给它);
· 挂上后(`.is-antd`)把**原生 `` 视觉收起** ——
它仍是值载体(antd 通过 change 写它),只是不再显示给用户。
⚠️ 用 clip 收起(不是 display:none):保留可聚焦、可被表单读到。
🔴🔴 v43(用户 2026-10-11 第 1 条):「日期那里多了一个控件,
只保留我们最新的控件就行,之前那种控件删除」——真根因有两层:
① 纪念日的原生框当初**没包在 `.dt-native` 里** ⇒ 这条选择器根本不命中
(模板已补上包装);
② **更要命的一层**:`.dt-input .dt-native { display: contents }`(第 619 行)
会让这个包装盒**自己不生成盒子**、子元素"提升"成 `.dt-input` 的直接布局项 ——
此时给它 `position:absolute; width:1px` **完全无效**(它没有盒子可定位),
原生框照样按 flex 项排出来 ⇒ 两个控件并排。
⇒ 修法:这里必须把 `display` **从 contents 改成 block**,定位才生效。
(只写 position/width/height 是不够的 —— 这是我第一版的错,探针当场抓到。) */
.dt-input.is-antd > .dt-native {
display: block; position: absolute; width: 1px; height: 1px;
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; padding: 0; margin: -1px;
}
.dt-input.is-antd > .dt-antd { display: block; width: 100%; }
.dt-input.dt-antd-single { display: block; width: 100%; }
/* 兜底:万一哪天原生框又变成 `.dt-input` 的**直接子元素**(不包 .dt-native 了),
这条按直接子元素也能把它收起来。 */
.dt-input.is-antd > input[type="date"] {
display: block; position: absolute; width: 1px; height: 1px;
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; padding: 0; margin: -1px;
}
/* ---- v42(用户第 6 条)简介为空时的那句可点占位 ----
与 `.trip-sum` 同一视觉位置,只是颜色淡一档(它是要点进去补的提示,不是内容)。 */
.trip-sum-empty { margin: 10px 0 0; font-size: 13px; color: var(--muted); }
.trip-sum-empty a { color: var(--brand); }
/* ============================================================================
🔴 v43(用户 2026-10-11 第 6 条)条目详情浮层
----------------------------------------------------------------------------
与 .cal-modal 同一形态(遮罩 + 居中面板 + 底栏),只是层级更高一点:
它可能从**另一个浮层**里打开(例如详情页弹框里点了条目),
所以要在 200 之上;但低于"看图"的灯箱(300)/预览层(320)。
⚠️ 与 .cn-modal(260)、.crop-overlay(260) 同层 —— 它们不会同时出现。
⚠️ 面板用 flex 列 + body 滚动,长内容(编辑表单)才有足够的可视区。 */
.fb-entry-modal {
position: fixed; inset: 0; z-index: 260;
display: flex; align-items: flex-start; justify-content: center;
padding: 24px 16px; overflow-y: auto;
background: rgba(44, 35, 29, .55); overscroll-behavior: contain;
}
.fb-entry-modal[hidden] { display: none; }
.fb-entry-panel { width: min(620px, 100%); max-height: calc(100vh - 64px); }
/* 🔴 v49(用户 2026-10-14 第 2 条):「点击详情…让模态框**变大**,显示更多信息」。
加宽到 860px(620 的 1.39 倍)—— 详情里可能有多行"标签:值"与附件清单,
宽一点才不至于每行都折行。窄屏仍占满。 */
.fb-entry-panel.is-wide { width: min(860px, 100%); }
/* 底栏右侧的按钮组(用户:「三个按钮,都放在下面的右侧」)。
`margin-left:auto` 把整组推到右边;左端那句提示(.cal-modal-tip)留在原地。
⚠️ 不用 `.cal-modal-foot .btn[type=submit]{margin-left:auto}` 那条(它只管提交键)。
⚠️ 组内允许换行:窄屏三颗按钮挤不下时要能折到第二行,而不是撑破底栏。 */
.fb-entry-acts {
margin-left: auto;
display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end;
}
/* 🔴🔴 v49:**必须显式声明 `[hidden]`**(与文件前部那条"凡靠 [hidden] 隐藏的都再写一次"
同一条规律)。原因:`.btn { display: inline-flex }` 是**作者样式**,
特异性与优先级都高于 `[hidden]` 的**浏览器默认** `display:none`
⇒ `hidden` 属性对 `.btn` **完全无效**。
症状:底栏四态切换时,前面那些按钮"藏不掉"、全都挤在一条上
(本轮实测:派生条目本该只有「详情」,却三颗都露着)。
⚠️ 用 `!important` 与文件前部 `.pick-row[hidden]` 那组的写法保持一致 ——
底栏是"四态互斥"的强语义,宁可压过一切。 */
.fb-entry-acts .btn[hidden] { display: none !important; }
/* 三颗的配色(用户:「详细用蓝色,编辑用黄色,删除用红色」)。
⚠️ **作用域类**(fb-a-*),不去改全局 `.btn-edit` / `.btn-danger` 的语义 ——
本项目踩过"裸类改色波及别页"的坑(v41 把影院管理页的蓝色编辑改成了黄,见上面那段)。
⚠️ 三处形态都写(裸类 / a. / button.):`.btn`、`a.btn`、`button.btn` 的特异性不同,
漏一种就有一种是错的(这条规律 v40 已验过)。 */
.fb-a-detail, a.fb-a-detail, button.fb-a-detail {
background: var(--blue); color: #fff; border-color: transparent;
}
.fb-a-edit, a.fb-a-edit, button.fb-a-edit {
background: var(--yellow); color: #fff; border-color: transparent;
}
.fb-a-del, a.fb-a-del, button.fb-a-del {
background: var(--red); color: #fff; border-color: transparent;
}
.fb-a-detail:hover, .fb-a-edit:hover, .fb-a-del:hover { filter: brightness(.92); }
/* 编辑态下正文里就是一张完整表单(内边距由它自己那份 CSS 给)——
这里只保证它不双份留白。 */
.fb-entry-panel .cal-modal-body > .mv-form-inner,
.fb-entry-panel .cal-modal-body > .nf-wrap { padding: 0; }
/* 详情展开块:`?embed=detail` 吐的只读 dl(带 .ev-detail-block)。
它自己那份 `.card-pad` 内边距在浮层里是多余的(面板已有 padding)。 */
.fb-entry-panel .ev-detail-block { padding: 0; }
@media (max-width: 640px) {
.fb-entry-panel, .fb-entry-panel.is-wide { width: 100%; }
}
/* ============================================================
v52(用户 2026-10-15 第 5 条):「是不是这个人?」确认条
------------------------------------------------------------
新增成员时若按 姓名/手机号/身份证号 在家里找到匹配的人,表单页顶部出现这一条。
⚠️ 它**在这张表单里面**(不是单独一页)—— 见 templates/people/form.php 的长注释:
单独一页得把整张表(含数组字段)原样回传,漏一类就静默丢数据。
⚠️ 视觉上要"明显但不像出错":它是个**问句**,不是错误 —— 所以用品牌淡底 + 左侧竖条,
而不是红色告警(红色会让人以为"提交失败了")。
============================================================ */
.person-confirm {
border-left: 3px solid var(--brand-2);
background: var(--brand-soft, #F3F7F7);
}
.person-hit {
display: flex; align-items: center; gap: 14px;
background: var(--surface); border: 1px solid var(--line);
border-radius: var(--radius-s); padding: 12px 14px;
}
.person-hit-av { flex: 0 0 auto; }
.person-hit-av img,
.person-hit-av .avatar-fallback {
width: 52px; height: 52px; border-radius: 12px; display: block;
object-fit: cover; background: var(--line-2);
}
.person-hit-av .avatar-fallback {
display: flex; align-items: center; justify-content: center;
font-size: 22px; color: var(--muted);
}
.person-hit-info { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.person-hit-ops { flex: 0 0 auto; }
/* ============================================================
v52(用户 2026-10-15 第 7 条):模块设置的**整合表**
------------------------------------------------------------
一行一个模块,四列 = 顺序 / 模块 / 导航 / 首页 / 颜色。
⚠️ 原来那是三张卡(首页模块 / 导航模块 / 主题色),同一串模块名出现两遍。
合成一张表后用户一次就能改完 —— 表格是这种"多列同构"最自然的形态。
⚠️ `min-width` + 外层 `overflow-x:auto`:窄屏(375px)五列放不下时
表格能横向滚动,而不是把页面撑破(本项目在名册上栽过溢出)。
============================================================ */
.mod-table-wrap { overflow-x: auto; }
.mod-table { width: 100%; border-collapse: collapse; min-width: 560px; }
.mod-table th, .mod-table td {
padding: 8px 10px; text-align: left; border-bottom: 1px solid var(--line-2);
font-size: 14px; vertical-align: middle;
}
.mod-table th { font-size: 12px; color: var(--muted); font-weight: 600; }
.mod-table .mod-th-on, .mod-table .mod-td-on { width: 62px; text-align: center; }
.mod-table .mod-th-color, .mod-table .mod-td-color { width: 72px; text-align: center; }
.mod-table .mod-th-order, .mod-table .mod-td-order { width: 108px; }
.mod-table .mod-td-order { white-space: nowrap; }
.mod-table .mod-td-name { display: flex; align-items: center; gap: 8px; border-bottom: 0; }
.mod-chk { display: inline-flex; cursor: pointer; }
.mod-chk input { accent-color: var(--brand); width: 16px; height: 16px; }
.mod-move { display: inline-flex; gap: 2px; margin-left: 4px; }
.mod-color-ro { width: 22px; height: 22px; border-radius: 5px; display: inline-block; }
.mod-table .mod-row.is-dragged { opacity: .5; }
.mod-table .mod-row.is-drop-before td { border-top: 2px solid var(--brand-2); }
.mod-table .color-picker { width: 34px; height: 24px; padding: 0; border: 1px solid var(--line); border-radius: 5px; background: #fff; cursor: pointer; }
/* ==========================================================================
🔴 v54(用户 2026-10-16 第 3/4/5 条):旅程 —— **列表 / 地图**两个视图
==========================================================================
用户原话:「在旅程这个模块,**可以在地图上显示旅程数据**」(移植 TripLog);
「在旅程的「新建旅程」后面也加一个「设置」按钮」;
「里面的设置让用户自己配置:**到底默认显示旅程列表,还是用地图显示**。」
两个视图用**标签页**切换(不是两页)—— 因为"看列表"与"看地图"是同一份数据的
两种看法,来回切不该刷新整页;也让"默认显示哪个"这个设置有意义
(设置决定**进来时**先显示哪一个)。
⚠️ 标签页样式沿用本仓既有的"胶囊按钮 + on 态"口径(与日历视图切换一致)。 */
/* ---- 视图切换条 ---- */
.trip-views {
display: flex; align-items: center; gap: 8px;
margin: 0 0 14px;
}
.trip-views .tv-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: 5px 14px; border-radius: 999px;
border: 1px solid var(--line); background: var(--surface);
color: var(--muted); font-size: 13.5px; cursor: pointer;
text-decoration: none; line-height: 1.5;
}
.trip-views .tv-btn:hover { color: var(--ink); border-color: var(--line-3, var(--line)); }
.trip-views .tv-btn.is-on {
background: var(--brand); border-color: var(--brand); color: #fff; font-weight: 600;
}
.trip-views .tv-btn svg { display: block; }
/* 右上角那颗「设为默认」小链接:与标签页同一行、靠右 */
.trip-views .tv-default {
margin-left: auto; font-size: 12.5px; color: var(--faint);
text-decoration: none; white-space: nowrap;
}
.trip-views .tv-default:hover { color: var(--brand); text-decoration: none; }
/* 「地图(0)」里那个小计数:还没位置时提示一下,别让人点进去看空地图 */
.trip-views .tv-dim { font-size: 11.5px; opacity: .8; margin-left: 2px; }
/* ---- 地图容器 ----
⚠️ **必须有固定高度**:高德地图在"高度为 0 的容器"里渲染出来是空白
(这是高德最常见的坑,`.trip-map` 的 height 就是为它写的)。
⚠️ 高度跟视口走(屏幕越大看得越远),但给下限免得笔记本上太扁。 */
.trip-map-wrap { margin-top: 2px; }
.trip-map {
width: 100%; height: clamp(380px, 62vh, 720px);
border: 1px solid var(--line); border-radius: var(--radius);
overflow: hidden; background: #EEF1F4; /* 瓦片到位前不闪白 */
}
.trip-map-fallback {
margin: 0; height: 100%; display: flex; align-items: center; justify-content: center;
color: var(--muted); font-size: 13.5px; padding: 0 24px; text-align: center;
}
.trip-map-hint { margin: 8px 0 0; }
/* ---- 地图上的标记:小卡片(左文右无图,比 TripLog 那版更轻)----
⚠️ 卡片是**绝对定位在画布中心**(JS 里 left/top = C+dx / C+dy):
画布固定 260×260,卡片偏移不影响标记锚点(AMap 按画布中心对齐坐标)。 */
.trip-pin-stage { position: relative; pointer-events: none; }
.trip-pin {
position: absolute; transform: translate(-50%, -50%);
pointer-events: auto; cursor: pointer;
}
.trip-pin-card {
display: flex; align-items: center; gap: 8px; /* 🔴 v63:题图在左、文字在右 */
background: var(--surface); border: 1px solid var(--line);
border-left: 3px solid var(--brand);
border-radius: 8px; padding: 6px 10px;
box-shadow: 0 2px 8px rgba(0,0,0,.10);
max-width: 220px;
}
/* 🔴 v63(用户 2026-10-19 第 3 条):旅程题图。
⚠️ `object-fit: cover` 必须有:题图原图长宽比五花八门,默认 `fill` 会把人拉扁。
⚠️ 有题图时卡片最多 220px(原来是 190px),文字那一块要 `min-width:0`
才能让 `.trip-pin-title` 的省略号真正生效(否则长标题把卡片撑破)。 */
.trip-pin-thumb {
flex: 0 0 auto; width: 40px; height: 40px;
border-radius: 5px; overflow: hidden; background: var(--line-2);
}
.trip-pin-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.trip-pin-body { display: block; min-width: 0; }
.trip-pin-title {
font-size: 13px; font-weight: 600; color: var(--ink);
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.trip-pin-sub { font-size: 11.5px; color: var(--muted); margin-top: 1px; }
.trip-pin:hover .trip-pin-card { border-color: var(--brand); box-shadow: 0 4px 14px rgba(0,0,0,.16); }
/* ⚠️ AMap 的标记包装层尺寸跟着 content(260×260)走、默认可命中,
会把旁边卡片的点击整层挡住。整层禁掉命中、只留卡片本身可点
(TripLog 也踩过这个,见其 `.amap-marker` 那条注释)。 */
#tripMapCanvas .amap-marker { pointer-events: none !important; }
#tripMapCanvas .amap-marker .trip-pin { pointer-events: auto !important; }
/* ---- 显示方式设置页的单选卡片(v54 第 5 条)----
原生 radio + label 包一层卡片外观;`.is-on` 由服务端渲染时给
—— 不依赖 JS 同步选中态(无 JS 时至少"当前项"是对的)。 */
.view-choice { display: flex; flex-direction: column; gap: 10px; max-width: 480px; }
.view-choice-item {
display: flex; align-items: flex-start; gap: 10px;
padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--radius-s);
cursor: pointer; background: var(--surface);
}
.view-choice-item:hover { border-color: var(--brand-2); }
.view-choice-item.is-on { border-color: var(--brand); background: var(--brand-soft, #f2f7f3); }
.view-choice-item input { margin-top: 3px; flex: 0 0 auto; }
.view-choice-item .vci-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.view-choice-item .vci-body b { font-size: 14px; }
/* ==================================================================== */
/* v62(用户 2026-10-18 第 2 条):分类图标 */
/* */
/* 分类徽标 = 图标 + 名字。图标是行内 SVG(AnnivIcon),颜色取 currentColor */
/* ⇒ 放在浅底徽标里就是灰字色,放在深色格子里自动变白,不需要第二套配色。 */
/* ==================================================================== */
.kind-badge {
display: inline-flex; align-items: center; gap: 4px;
vertical-align: middle;
max-width: 100%;
}
.kind-badge .ai { flex: 0 0 auto; opacity: .9; }
.kind-badge .kind-name {
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
max-width: 9em; /* 分类名最长 20 字,卡一行不撑破表格 */
}
.ann-ic { display: inline-flex; align-items: center; flex: 0 0 auto; color: var(--muted); }
.ann-ic .ai { display: block; }
/* ---- 分类设置页(days/settings)---- */
.kind-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.kind-row {
display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--radius-s);
background: var(--surface);
/* ⚠️ 行内每项都可能收缩(分类名输入框),flex 子项默认 min-width:auto 会顶破 */
min-width: 0;
}
.kind-row.is-ro { gap: 10px; }
.kind-move { display: inline-flex; gap: 2px; flex: 0 0 auto; }
.kind-prev {
flex: 0 0 auto; width: 30px; height: 30px; border-radius: 8px;
display: inline-flex; align-items: center; justify-content: center;
background: var(--line-2); color: var(--ink);
}
.kind-sel { flex: 0 0 auto; width: 190px; max-width: 46vw; }
.kind-name-in { flex: 1 1 140px; min-width: 0; }
.kind-name-ro { font-size: 14px; }
.kind-use { flex: 0 0 auto; font-size: 12px; color: var(--muted); min-width: 46px; text-align: right; }
/* 改过但没保存:保存按钮描一圈告警色 + 旁边一句小字(JS 加 .is-dirty / 去掉 hidden)
⚠️ 用 `.btn-warn` 同一个 #E8A33D(全站"待处理"的语义色),不要另发明一个黄。 */
.btn.is-dirty { box-shadow: 0 0 0 2px #E8A33D; }
.kind-dirty { color: #b57a19; margin-left: 10px; }
.kind-add {
display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
margin-top: 14px; padding: 10px; border-radius: var(--radius-s);
background: var(--line-2);
}
.kind-add .select { flex: 0 0 auto; width: 190px; max-width: 46vw; }
.kind-add .input { flex: 1 1 160px; min-width: 0; }
.kind-add-lb { font-size: 13px; color: var(--muted); }
.kind-icon-bank { display: none; }
/* ==================================================================== */
/* v62(用户 2026-10-18 第 3 条):首页卡片网格 */
/* */
/* 口径(与 app/HomeCards.php、templates/home_cards.php 的措辞一致): */
/* · 一排 8 格;宽 2/4/6/8 → 一排 4 张 / 2 张 / 一大一小 / 独占一排 */
/* · 高 2/4/6/8 行;一行 76px、间距 10px */
/* */
/* ⚠️ 为什么用 `grid-template-columns: repeat(8, minmax(0,1fr))` 而不是 */
/* flex + 百分比:8 格制下"宽 6"(一大一小)只有网格能自然表达; */
/* `minmax(0,1fr)` 里的 0 对应 flex 那条 `min-width:0` —— */
/* 少了它,卡片里的长标题会把整列顶宽(v54 那个坑的同源)。 */
/* ⚠️ 高度用 `grid-row: span N` 而不是给每张卡片算像素: */
/* 这样"高 4"的两张卡片并排时天然等高,不依赖内容多少。 */
/* ==================================================================== */
.hc-grid {
display: grid;
grid-template-columns: repeat(8, minmax(0, 1fr));
grid-auto-rows: 76px;
gap: 10px;
align-items: stretch;
}
.hc-card {
display: flex; flex-direction: column;
min-width: 0; min-height: 0;
margin: 0;
padding: 12px 14px;
overflow: hidden;
}
/* ⚠️ 必须单独写这一条,而且选择器要**比 `.card + .card` 更重**:
上面 `.hc-card { margin: 0 }` 只有 0,1,0,而全局相邻间距
`.card + .card { margin-top: 18px }` 是 0,2,0 —— 权重更高,会把
每一张**非首张**的卡片顶下去 18px(实测症状:高 2 期望 162 对得上,
高 4/高 8 恒少 18px,因为网格首个子元素不命中 `+ .card`)。
间距本来就由 grid 的 `gap` 统一给,这里把相邻 margin 彻底清掉。 */
.hc-grid > .card + .card { margin-top: 0; }
.hc-w2 { grid-column: span 2; }
.hc-w4 { grid-column: span 4; }
.hc-w6 { grid-column: span 6; }
.hc-w8 { grid-column: span 8; }
.hc-h2 { grid-row: span 2; }
.hc-h4 { grid-row: span 4; }
.hc-h6 { grid-row: span 6; }
.hc-h8 { grid-row: span 8; }
.hc-head {
display: flex; align-items: center; gap: 10px;
flex: 0 0 auto; min-width: 0; margin-bottom: 8px;
}
.hc-head .cinema-h2 { margin: 0; flex: 1 1 auto; min-width: 0; }
.hc-head .hd-more { flex: 0 0 auto; }
/* 卡片正文:**自己滚**,不撑卡片(用户给的高度就是最终高度)。 */
.hc-body {
flex: 1 1 auto; min-height: 0; min-width: 0;
overflow-y: auto; overscroll-behavior: contain;
scrollbar-width: none; -ms-overflow-style: none;
}
.hc-body::-webkit-scrollbar { width: 0; height: 0; }
/* 概述卡:左右两栏(问候/数字 | 天气),与原来的信息面板一致 */
.hc-body.home-brief { align-content: flex-start; }
/* 日程卡:内部还要再分"全天区 + 时间网格",所以它自己是一个纵向 flex,
滚动交给内层 `.hd-pane-body`(这样全天区才能钉在上面不动)。 */
.hc-body.hc-agenda { display: flex; flex-direction: column; overflow: hidden; }
/* 卡片里的 pane 不再有 440px 的固定高(那是 v24 双栏时代的值) */
.hc-card .hd-pane-body { flex: 1 1 auto; min-height: 0; }
.hc-card .ag-allday { max-height: 40%; }
/* ---- 纪念日卡片 ---- */
.hc-ann .ann-up-item { gap: 6px; padding: 7px 2px; }
.hc-ann .ann-days { min-width: 52px; }
.hc-ann .ann-title { font-size: 13.5px; }
/* ---- 观影卡片:海报 + 片名 ---- */
.hc-mv-list, .hc-bk-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.hc-mv, .hc-bk {
display: flex; align-items: center; gap: 10px; min-width: 0;
padding: 6px 2px; border-bottom: 1px solid var(--line-2);
}
.hc-mv:last-child, .hc-bk:last-child { border-bottom: 0; }
.hc-mv-poster, .hc-bk-cover {
flex: 0 0 auto; width: 34px; height: 46px; border-radius: 4px; overflow: hidden;
background: var(--line-2); display: inline-flex; align-items: center; justify-content: center;
}
.hc-bk-cover { width: 32px; height: 44px; }
.hc-mv-poster img, .hc-bk-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hc-mv-ph, .hc-bk-ph { font-size: 16px; opacity: .7; }
.hc-mv-main, .hc-bk-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.hc-mv-t, .hc-bk-t {
font-size: 13.5px; color: var(--ink); font-weight: 600;
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hc-bk-bar { display: block; height: 3px; border-radius: 2px; background: var(--line-2); overflow: hidden; }
.hc-bk-bar i { display: block; height: 100%; background: var(--brand); }
.hc-bk-st {
flex: 0 0 auto; font-size: 11px; padding: 1px 7px; border-radius: 999px;
background: var(--line-2); color: var(--muted);
}
.hc-bk-st.is-reading { background: #E7F0DA; color: #4A7226; }
.hc-bk-st.is-done { background: #E3EAF5; color: #3A5B8C; }
/* ---- 联系人卡片:头像 + 名字,按卡片宽度自动折行 ---- */
.hc-people { display: flex; flex-wrap: wrap; gap: 10px 12px; }
.hc-person {
display: flex; flex-direction: column; align-items: center; gap: 4px;
width: 56px; min-width: 0; text-decoration: none;
}
.hc-person-av {
width: 40px; height: 40px; border-radius: 50%; object-fit: cover;
display: inline-flex; align-items: center; justify-content: center;
font-size: 15px; font-weight: 600;
}
.hc-person-nm {
font-size: 11.5px; color: var(--muted); max-width: 100%;
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hc-person:hover .hc-person-nm { color: var(--ink); }
/* ---- 窄屏:8 格 → 4 格 → 1 列 ---- */
@media (max-width: 1180px) {
.hc-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.hc-w2 { grid-column: span 2; }
.hc-w4, .hc-w6, .hc-w8 { grid-column: span 4; }
.hc-h8 { grid-row: span 7; }
}
@media (max-width: 680px) {
.hc-grid { grid-template-columns: minmax(0, 1fr); grid-auto-rows: 64px; }
.hc-w2, .hc-w4, .hc-w6, .hc-w8 { grid-column: span 1; }
.hc-h6, .hc-h8 { grid-row: span 6; }
}
/* ---- 首页卡片布局设置页 ---- */
.hc-set-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.hc-set-row {
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius-s);
background: var(--surface); min-width: 0;
}
.hc-set-row.is-dragged { opacity: .5; }
.hc-set-row.is-drop-before { border-top: 2px solid var(--brand); }
.hc-set-order { display: inline-flex; align-items: center; gap: 2px; flex: 0 0 auto; }
.hc-set-on {
display: flex; align-items: center; gap: 8px;
flex: 1 1 220px; min-width: 0; cursor: pointer;
}
.hc-set-name { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.hc-set-name b { font-size: 14px; }
.hc-set-name .small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hc-set-dim { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.hc-set-dim .select { width: 66px; }
.hc-set-n { width: 64px; }
.hc-set-null { width: 0; }
/* ============================================================
v78(用户 2026-10-22):首页「近期任务」的就地面板
------------------------------------------------------------
用户原话:「网页端,首页点击一个任务后,除了展示信息,应该有相应的功能,
可以修改状态:待办、进行中,可以挪动分组,可以拖动上传附件,可以添加评论,
添加子任务等。其目地,是为了让用户不离开首页,就能对任务进行简单的处理。」
这一段只画**面板本体**(壳、遮罩、底栏都沿用 `.cal-modal*` / `.fb-entry-panel`)。
三块强调色都不新造:状态用既有的 `.kb-st`(三档颜色是它定义的唯一来源),
看板 chip 用看板自己的 `--bc`(与看板列头、卡片上的点同一个色)。
============================================================ */
.tk-panel { display: flex; flex-direction: column; gap: 14px; }
.tk-sec { display: flex; flex-direction: column; gap: 8px; }
/* 段落之间的那条细线:面板里段落多(状态/看板/信息/子任务/附件/讨论),
不划线会糊成一整块;用 line-2(比正文分隔线更淡)以免像个表单边框。 */
.tk-sec + .tk-sec { border-top: 1px solid var(--line-2); padding-top: 14px; }
.tk-sec-h {
display: flex; align-items: center; gap: 6px;
font-size: 13px; font-weight: 600; color: var(--ink);
}
.tk-sec-h .group-n { margin-left: 0; }
/* 「状态」「看板」这两段是"标签 + 一排可点的东西",横排。
⚠️ 用 flex-wrap:看板可能是七八个,窄屏要能折行而不是撑出面板。 */
.tk-sec-inline { flex-direction: row; align-items: center; gap: 10px; flex-wrap: wrap; }
/* 🔴 #74:面板里的「截止时间」AntD 控件。
`.dt-input` 是 `display:flex; flex-wrap:wrap`,放进 `.tk-sec-inline`
(也是 flex-wrap)里会被当成一个弹性项 —— 不给宽度约束时它会按内容撑,
在窄屏上可能把「截止」标签挤到上一行、或者控件自己溢出。
`flex: 1 1 auto` + `min-width: 0` 让它占满标签右侧的剩余宽度,
`max-width` 兜住极宽屏(AntD 的 picker 本身有固定宽度)。 */
.tk-sec-inline > .dt-input { flex: 1 1 auto; min-width: 0; max-width: 320px; }
/* 「还剩 3 天」那类小字:贴在控件右边,不与控件抢宽度 */
.tk-due-word { flex: 0 0 auto; font-size: 12.5px; color: var(--faint); white-space: nowrap; }
.tk-due-word.kb-late { color: var(--red); }
/* 🔴 #76:「更多信息」小节 —— 截止 / 负责人 / 分类 三行 + 保存。
用户 2026-11-05:「没有保存按钮…负责人和分类都没法修改。」
每行结构 = 固定宽标签 + 占满剩余宽度的控件,与 .mv-row 同一套观感
(但标签用 tk-attr-lb,因为面板里字号比整页表单小一号)。 */
.tk-attrs { display: flex; flex-direction: column; gap: 10px; }
.tk-attr-row {
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.tk-attr-lb {
flex: 0 0 46px; font-size: 12.5px; color: var(--faint); text-align: right;
}
/* 控件区占满标签右侧(min-width:0 让它能被压缩,不会把标签挤出去) */
.tk-attr-row > *:not(.tk-attr-lb) { flex: 1 1 auto; min-width: 0; }
/* AntD 的 picker 本身有固定宽度,别让它撑满整行 */
.tk-attr-row > .dt-input { max-width: 300px; }
.tk-attr-row > select.select { max-width: 300px; }
/* 保存按钮那一行:与上面的控件左缘对齐(= 46px 标签宽 + 10px gap) */
.tk-attr-actions {
display: flex; align-items: center; gap: 10px;
padding-left: 56px;
}
.tk-attr-tip { font-size: 12px; color: var(--green, #2E7D4F); }
/* #78:面板里的选人组件(负责人单选 / 协作人多选)。
组件本身按"整页宽"设计,塞进 .tk-attr-row 的弹性项里要收一下:
- 不加 min-width:0 的话,chips 一多会把标签挤到上一行;
- max-width 与其它控件对齐(300px),别让它撑满整行。 */
.tk-attr-row > .pp-root { flex: 1 1 auto; min-width: 0; max-width: 340px; }
.tk-attr-row > .pp-root .pp-chips { margin-bottom: 6px; }
/* 标签在多行控件(备注、协作人)上方对齐,而不是垂直居中 */
.tk-attr-row-top { align-items: flex-start; }
.tk-attr-row-top > .tk-attr-lb { padding-top: 8px; }
/* 只读态的协作人 chips */
.tk-share-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.tk-share-chip {
font-size: 12.5px; padding: 3px 10px; border-radius: 999px;
background: var(--surface-2, #F2F0EC); color: var(--ink);
}
/* #78:评论里的「添加附件」。
用原生 file input(视觉隐藏),点 label 触发 —— 与站内其它上传入口同一手法。 */
.cmt-attach-row {
display: flex; align-items: center; gap: 8px;
margin-top: 6px;
}
.cmt-attach-pick {
display: inline-flex; align-items: center; gap: 5px;
font-size: 12.5px; color: var(--muted); cursor: pointer;
padding: 4px 10px; border-radius: 999px;
border: 1px dashed var(--line); background: transparent;
}
.cmt-attach-pick:hover { color: var(--brand); border-color: var(--brand); }
.cmt-attach-name {
font-size: 12px; color: var(--faint);
max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* #79:评论附件区支持拖拽 —— 高亮用虚线框 + 浅主题底色,和上面那块
「任务附件」的 is-over 观感一致(同一个交互不该长两个样子)。 */
.cmt-attach-row {
padding: 6px 8px; border-radius: 8px;
border: 1px dashed transparent;
transition: background-color .12s, border-color .12s;
}
.cmt-attach-row.is-over {
border-color: var(--brand);
background: color-mix(in srgb, var(--brand) 8%, transparent);
}
.cmt-attach-row.is-over .cmt-attach-pick { color: var(--brand); border-color: var(--brand); }
.tk-sec-lb { flex: 0 0 34px; font-size: 12.5px; color: var(--faint); }
.tk-seg { display: flex; gap: 6px; flex-wrap: wrap; }
/* 面板里的状态徽标比卡片上的大一档(这里是要点的按钮,不是标签) */
.tk-seg .kb-st { padding: 3px 12px; font-size: 12.5px; }
.tk-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.tk-chip {
display: inline-flex; align-items: center; gap: 5px;
padding: 2px 10px; border-radius: 999px;
border: 1px solid var(--line); background: var(--surface);
font-size: 12px; color: var(--muted); cursor: pointer;
max-width: 100%;
}
.tk-chip:hover { border-color: var(--brand-2); color: var(--brand-ink); }
/* 选中的看板:用**它自己的颜色**描边 + 淡底 —— 与看板列头那颗点是同一个色,
用户不用读字就知道"这条现在在哪一摊事里"。 */
.tk-chip.is-on {
border-color: var(--bc, var(--brand));
background: color-mix(in srgb, var(--bc, var(--brand)) 12%, transparent);
color: var(--ink); font-weight: 600;
}
/* ⚠️ color-mix 在老浏览器上整条声明无效 → 底色会是透明的。
补一条纯 rgba 兜底(不指定色,统一淡灰),保证"选中"至少看得出来。 */
@supports not (background: color-mix(in srgb, red 10%, transparent)) {
.tk-chip.is-on { background: var(--line-2); }
}
.tk-chip.is-static { cursor: default; }
.tk-chip-dot {
width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto;
background: var(--bc, var(--faint));
}
/* 只读那几行(截止/负责人/分类/备注):面板里空间紧,行高比详情页收一点 */
.tk-kv .kv-row { padding: 6px 0; }
/* 加子任务那一行:输入框吃掉剩余宽度,按钮不缩 */
.tk-inline-form { display: flex; align-items: center; gap: 8px; }
.tk-inline-form .input { flex: 1 1 auto; min-width: 0; }
.tk-inline-form .btn { flex: 0 0 auto; }
/* 讨论里那条"删除":长得像链接(`.btn-inline` 负责去掉按钮的灰底,
`.link-quiet` 负责颜色),这里只把它推到本行最右。 */
.cmt-who .cmt-x { margin-left: auto; }
.tk-drop { margin-top: 4px; }
.tk-drop .upbox { min-height: 64px; }
@media (max-width: 640px) {
.tk-sec-lb { flex: 0 0 auto; width: 100%; }
}
/* v78:卡片窄到"宽 2"时,标题 +「全部任务」+「新建任务」三样挤不下 ——
允许折行,别把标题压成 0 宽(那会只剩两颗按钮,看不出这是哪张卡片)。 */
.hc-card.hc-w2 .hc-head { flex-wrap: wrap; }
/* ============================================================================
🔴🔴 v94(用户 2026-10-25):必填项统一标记 = **标签红色 + 加粗**,去掉红色星号
============================================================================
用户原话:「必填项现在的做法是在后面加一个红色的星,但这样排版有时候不好看,
对不齐。把所有必填项都改一下,改成红色加粗字体,就不用再考虑加红色星的问题了。
这样用户习惯之后,一看前面的标题,比如"日程名称"这种,只要是红色加粗的字体,
就知道这是必填项。我感觉这个比加红色的星更好一些。」
------------------------------------------------------------------
为什么用 `.is-req` 这个**显式标记**,而不是"看有没有 `required` 属性"自动推断
------------------------------------------------------------------
`required` 只出现在 `` 上,而标签是它的**兄弟或父级之前的节点** ——
CSS 没有"父选择器"能用它反推(`:has()` 也只在很新的内核里可用,本项目要照顾
旧 WebView)。而且有几个必填项**根本没有 `required` 属性**(比如影院名称、
板块名这些靠 JS 校验的),靠属性推断会漏掉。
⇒ 一律由模板显式加 `is-req` / `is-req` 放在 `` 上。
「哪些是必填」这个事实写在模板里(离 `required` 最近),
「红加粗长什么样」写在 CSS 里(一处改、全站生效)。
------------------------------------------------------------------
⚠️ 三条硬约束(都是本项目的实测坑)
------------------------------------------------------------------
① **特异性**:这里用 `.is-req`(0,1,0)去覆盖 `.mv-row > .mv-lb`
(0,2,0)和 `.form-grid > .field > label`(0,2,1)—— 单靠一条肯定压不住。
所以最后用 `!important` 打穿。这不是偷懒:颜色/字重是**语义标记**,
不该被"某个面板想调一下标签样式"顺手盖掉,否则同一个"必填"在不同页面长得不一样。
② **冒号必须跟着变红**:`.mv-lb::after` / `.dt-inline-lab::after` 把「:」画成
灰色 400 字重 —— 只改标签本体会出现"红字 + 灰冒号"的半截效果。这里一并覆盖。
③ **不能用 `color: inherit` 之类做减法**:`--red` 是动作红(删除按钮底色),
当文字色在暖纸底上偏浅,见 `:root` 里 `--req` 的说明。
------------------------------------------------------------------
覆盖的三种标签形态(本项目全部标签都落在这三种里,已逐个核过)
------------------------------------------------------------------
a) `